Compare commits

..

1 Commits

Author SHA1 Message Date
Jonathan Bennett
9f5be559c9 update build-* to also output binaries to *-update.bin/uf2 2025-10-05 13:52:57 -05:00
172 changed files with 2060 additions and 1641 deletions

View File

@@ -19,8 +19,6 @@ jobs:
pio-build: pio-build:
name: build-${{ inputs.platform }} name: build-${{ inputs.platform }}
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
outputs:
artifact-id: ${{ steps.upload.outputs.artifact-id }}
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
with: with:
@@ -57,7 +55,6 @@ jobs:
- name: Store binaries as an artifact - name: Store binaries as an artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
id: upload
with: with:
name: firmware-${{ inputs.platform }}-${{ inputs.pio_env }}-${{ inputs.version }}.zip name: firmware-${{ inputs.platform }}-${{ inputs.pio_env }}-${{ inputs.version }}.zip
overwrite: true overwrite: true

View File

@@ -3,7 +3,6 @@ name: Build One Arch
on: on:
workflow_dispatch: workflow_dispatch:
inputs: inputs:
# trunk-ignore(checkov/CKV_GHA_7)
arch: arch:
type: choice type: choice
options: options:
@@ -17,13 +16,10 @@ on:
- stm32 - stm32
- native - native
permissions: read-all
env:
INPUT_ARCH: ${{ github.event.inputs.arch }}
jobs: jobs:
setup: setup:
strategy:
fail-fast: false
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
@@ -35,11 +31,23 @@ jobs:
- name: Generate matrix - name: Generate matrix
id: jsonStep id: jsonStep
run: | run: |
TARGETS=$(./bin/generate_ci_matrix.py $INPUT_ARCH --level extra) if [[ "$GITHUB_HEAD_REF" == "" ]]; then
echo "Name: $GITHUB_REF_NAME Base: $GITHUB_BASE_REF Ref: $GITHUB_REF" TARGETS=$(./bin/generate_ci_matrix.py ${{inputs.arch}} extra)
echo "selected_arch=$TARGETS" >> $GITHUB_OUTPUT else
TARGETS=$(./bin/generate_ci_matrix.py ${{inputs.arch}} pr)
fi
echo "Name: $GITHUB_REF_NAME Base: $GITHUB_BASE_REF Ref: $GITHUB_REF Targets: $TARGETS"
echo "${{inputs.arch}}=$(jq -cn --argjson environments "$TARGETS" '{board: $environments}')" >> $GITHUB_OUTPUT
outputs: outputs:
selected_arch: ${{ steps.jsonStep.outputs.selected_arch }} 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 }}
rp2350: ${{ steps.jsonStep.outputs.rp2350 }}
stm32: ${{ steps.jsonStep.outputs.stm32 }}
check: ${{ steps.jsonStep.outputs.check }}
version: version:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -56,18 +64,101 @@ jobs:
long: ${{ steps.version.outputs.long }} long: ${{ steps.version.outputs.long }}
deb: ${{ steps.version.outputs.deb }} deb: ${{ steps.version.outputs.deb }}
build: build-esp32:
if: ${{ github.event_name != 'workflow_dispatch' }} if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'esp32'}}
needs: [setup, version] needs: [setup, version]
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix: ${{ fromJson(needs.setup.outputs.esp32) }}
build: ${{ fromJson(needs.setup.outputs.selected_arch) }}
uses: ./.github/workflows/build_firmware.yml uses: ./.github/workflows/build_firmware.yml
with: with:
version: ${{ needs.version.outputs.long }} version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.build.board }} pio_env: ${{ matrix.board }}
platform: ${{ matrix.build.arch }} platform: esp32
build-esp32s3:
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'esp32s3'}}
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.esp32s3) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: esp32s3
build-esp32c3:
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'esp32c3'}}
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.esp32c3) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: esp32c3
build-esp32c6:
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'esp32c6'}}
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.esp32c6) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: esp32c6
build-nrf52840:
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'nrf52840'}}
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.nrf52840) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: nrf52840
build-rp2040:
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'rp2040'}}
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.rp2040) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: rp2040
build-rp2350:
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'rp2350'}}
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.rp2350) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: rp2350
build-stm32:
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'stm32' }}
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.stm32) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: stm32
build-debian-src: build-debian-src:
if: ${{ github.repository == 'meshtastic/firmware' && github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }} if: ${{ github.repository == 'meshtastic/firmware' && github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
@@ -88,6 +179,62 @@ jobs:
if: ${{ !contains(github.ref_name, 'event/') && github.event_name != 'workflow_dispatch' || !contains(github.ref_name, 'event/') && inputs.arch == 'native' }} if: ${{ !contains(github.ref_name, 'event/') && github.event_name != 'workflow_dispatch' || !contains(github.ref_name, 'event/') && inputs.arch == 'native' }}
uses: ./.github/workflows/test_native.yml uses: ./.github/workflows/test_native.yml
docker-deb-amd64:
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
docker-deb-amd64-tft:
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
pio_env: native-tft
docker-alp-amd64:
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
uses: ./.github/workflows/docker_build.yml
with:
distro: alpine
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
docker-alp-amd64-tft:
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
uses: ./.github/workflows/docker_build.yml
with:
distro: alpine
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
pio_env: native-tft
docker-deb-arm64:
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/arm64
runs-on: ubuntu-24.04-arm
push: false
docker-deb-armv7:
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/arm/v7
runs-on: ubuntu-24.04-arm
push: false
gather-artifacts: gather-artifacts:
permissions: permissions:
contents: write contents: write
@@ -105,7 +252,18 @@ jobs:
- rp2350 - rp2350
- stm32 - stm32
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [version, build] needs:
[
version,
build-esp32,
build-esp32s3,
build-esp32c3,
build-esp32c6,
build-nrf52840,
build-rp2040,
build-rp2350,
build-stm32,
]
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v5 uses: actions/checkout@v5
@@ -174,3 +332,169 @@ jobs:
name: firmware-${{inputs.arch}}-${{ needs.version.outputs.long }} name: firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}
description: "Download firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip. This artifact will be available for 90 days from creation" description: "Download firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip. This artifact will be available for 90 days from creation"
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
release-artifacts:
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' }}
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
needs:
- version
- gather-artifacts
- build-debian-src
- package-pio-deps-native-tft
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: 3.x
- name: Create release
uses: softprops/action-gh-release@v2
id: create_release
with:
draft: true
prerelease: true
name: Meshtastic Firmware ${{ needs.version.outputs.long }} Alpha
tag_name: v${{ needs.version.outputs.long }}
body: |
Autogenerated by github action, developer should edit as required before publishing...
- name: Download source deb
uses: actions/download-artifact@v5
with:
pattern: firmware-debian-${{ needs.version.outputs.deb }}~UNRELEASED-src
merge-multiple: true
path: ./output/debian-src
- name: Download `native-tft` pio deps
uses: actions/download-artifact@v5
with:
pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }}
merge-multiple: true
path: ./output/pio-deps-native-tft
- name: Zip Linux sources
working-directory: output
run: |
zip -j -9 -r ./meshtasticd-${{ needs.version.outputs.deb }}-src.zip ./debian-src
zip -9 -r ./platformio-deps-native-tft-${{ needs.version.outputs.long }}.zip ./pio-deps-native-tft
# For diagnostics
- name: Display structure of downloaded files
run: ls -lR
- name: Add Linux sources to GtiHub Release
# Only run when targeting master branch with workflow_dispatch
if: ${{ github.ref_name == 'master' }}
run: |
gh release upload v${{ needs.version.outputs.long }} ./output/meshtasticd-${{ needs.version.outputs.deb }}-src.zip
gh release upload v${{ needs.version.outputs.long }} ./output/platformio-deps-native-tft-${{ needs.version.outputs.long }}.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
release-firmware:
strategy:
fail-fast: false
matrix:
arch:
- esp32
- esp32s3
- esp32c3
- esp32c6
- nrf52840
- rp2040
- rp2350
- stm32
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' }}
needs: [release-artifacts, version]
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: 3.x
- uses: actions/download-artifact@v5
with:
pattern: firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}
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-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip ./output
- uses: actions/download-artifact@v5
with:
name: debug-elfs-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip
merge-multiple: true
path: ./elfs
- name: Zip debug elfs
run: zip -j -9 -r ./debug-elfs-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip ./elfs
# For diagnostics
- name: Display structure of downloaded files
run: ls -lR
- name: Add bins and debug elfs to GitHub Release
# Only run when targeting master branch with workflow_dispatch
if: ${{ github.ref_name == 'master' }}
run: |
gh release upload v${{ needs.version.outputs.long }} ./firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip
gh release upload v${{ needs.version.outputs.long }} ./debug-elfs-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
publish-firmware:
runs-on: ubuntu-24.04
if: ${{ github.event_name == 'workflow_dispatch' }}
needs: [release-firmware, version]
env:
targets: |-
esp32,esp32s3,esp32c3,esp32c6,nrf52840,rp2040,rp2350,stm32
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: 3.x
- uses: actions/download-artifact@v5
with:
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
merge-multiple: true
path: ./publish
- name: Publish firmware to meshtastic.github.io
uses: peaceiris/actions-gh-pages@v4
env:
# On event/* branches, use the event name as the destination prefix
DEST_PREFIX: ${{ contains(github.ref_name, 'event/') && format('{0}/', github.ref_name) || '' }}
with:
deploy_key: ${{ secrets.DIST_PAGES_DEPLOY_KEY }}
external_repository: meshtastic/meshtastic.github.io
publish_branch: master
publish_dir: ./publish
destination_dir: ${{ env.DEST_PREFIX }}firmware-${{ needs.version.outputs.long }}
keep_files: true
user_name: github-actions[bot]
user_email: github-actions[bot]@users.noreply.github.com
commit_message: ${{ needs.version.outputs.long }}
enable_jekyll: true

View File

@@ -3,7 +3,6 @@ name: Build One Target
on: on:
workflow_dispatch: workflow_dispatch:
inputs: inputs:
# trunk-ignore(checkov/CKV_GHA_7)
arch: arch:
type: choice type: choice
options: options:
@@ -20,13 +19,11 @@ on:
type: string type: string
required: false required: false
description: Choose the target board, e.g. nrf52_promicro_diy_tcxo. If blank, will find available targets. description: Choose the target board, e.g. nrf52_promicro_diy_tcxo. If blank, will find available targets.
# find-target: # find-target:
# type: boolean # type: boolean
# default: true # default: true
# description: 'Find the available targets' # description: 'Find the available targets'
permissions: read-all
jobs: jobs:
find-targets: find-targets:
if: ${{ inputs.target == '' }} if: ${{ inputs.target == '' }}
@@ -54,13 +51,13 @@ jobs:
- name: Generate matrix - name: Generate matrix
id: jsonStep id: jsonStep
run: | run: |
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}} --level extra) TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}} extra)
echo "Name: $GITHUB_REF_NAME" >> $GITHUB_STEP_SUMMARY echo "Name: $GITHUB_REF_NAME" >> $GITHUB_STEP_SUMMARY
echo "Base: $GITHUB_BASE_REF" >> $GITHUB_STEP_SUMMARY echo "Base: $GITHUB_BASE_REF" >> $GITHUB_STEP_SUMMARY
echo "Arch: ${{matrix.arch}}" >> $GITHUB_STEP_SUMMARY echo "Arch: ${{matrix.arch}}" >> $GITHUB_STEP_SUMMARY
echo "Ref: $GITHUB_REF" >> $GITHUB_STEP_SUMMARY echo "Ref: $GITHUB_REF" >> $GITHUB_STEP_SUMMARY
echo "Targets:" >> $GITHUB_STEP_SUMMARY echo "Targets:" >> $GITHUB_STEP_SUMMARY
echo $TARGETS >> $GITHUB_STEP_SUMMARY echo $TARGETS | sed 's/[][]//g; s/", "/\n- /g; s/"//g; s/^/- /' >> $GITHUB_STEP_SUMMARY
version: version:
if: ${{ inputs.target != '' }} if: ${{ inputs.target != '' }}
@@ -78,9 +75,11 @@ jobs:
long: ${{ steps.version.outputs.long }} long: ${{ steps.version.outputs.long }}
deb: ${{ steps.version.outputs.deb }} deb: ${{ steps.version.outputs.deb }}
build: build-arch:
if: ${{ inputs.target != '' && inputs.arch != 'native' }} if: ${{ inputs.target != '' && inputs.arch != 'native' }}
needs: [version] needs: [version]
strategy:
fail-fast: false
uses: ./.github/workflows/build_firmware.yml uses: ./.github/workflows/build_firmware.yml
with: with:
version: ${{ needs.version.outputs.long }} version: ${{ needs.version.outputs.long }}
@@ -106,12 +105,70 @@ jobs:
if: ${{ !contains(github.ref_name, 'event/') && github.event_name != 'workflow_dispatch' || !contains(github.ref_name, 'event/') && inputs.arch == 'native' && inputs.target != '' }} if: ${{ !contains(github.ref_name, 'event/') && github.event_name != 'workflow_dispatch' || !contains(github.ref_name, 'event/') && inputs.arch == 'native' && inputs.target != '' }}
uses: ./.github/workflows/test_native.yml uses: ./.github/workflows/test_native.yml
docker-deb-amd64:
if: ${{ inputs.target != '' && inputs.arch == 'native' }}
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
docker-deb-amd64-tft:
if: ${{ inputs.target != '' && inputs.arch == 'native' }}
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
pio_env: native-tft
docker-alp-amd64:
if: ${{ inputs.target != '' && inputs.arch == 'native' }}
uses: ./.github/workflows/docker_build.yml
with:
distro: alpine
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
docker-alp-amd64-tft:
if: ${{ inputs.target != '' && inputs.arch == 'native' }}
uses: ./.github/workflows/docker_build.yml
with:
distro: alpine
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
pio_env: native-tft
docker-deb-arm64:
if: ${{ inputs.target != '' && inputs.arch == 'native' }}
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/arm64
runs-on: ubuntu-24.04-arm
push: false
docker-deb-armv7:
if: ${{ inputs.target != '' && inputs.arch == 'native' }}
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/arm/v7
runs-on: ubuntu-24.04-arm
push: false
gather-artifacts: gather-artifacts:
permissions: permissions:
contents: write contents: write
pull-requests: write pull-requests: write
strategy:
fail-fast: false
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [version, build] needs: [version, build-arch]
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v5 uses: actions/checkout@v5
@@ -180,3 +237,159 @@ jobs:
name: firmware-${{inputs.target}}-${{ needs.version.outputs.long }} name: firmware-${{inputs.target}}-${{ needs.version.outputs.long }}
description: "Download firmware-${{inputs.target}}-${{ needs.version.outputs.long }}.zip. This artifact will be available for 90 days from creation" description: "Download firmware-${{inputs.target}}-${{ needs.version.outputs.long }}.zip. This artifact will be available for 90 days from creation"
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
release-artifacts:
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' && inputs.target != ''}}
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
needs:
- version
- gather-artifacts
- build-debian-src
- package-pio-deps-native-tft
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: 3.x
- name: Create release
uses: softprops/action-gh-release@v2
id: create_release
with:
draft: true
prerelease: true
name: Meshtastic Firmware ${{ needs.version.outputs.long }} Alpha
tag_name: v${{ needs.version.outputs.long }}
body: |
Autogenerated by github action, developer should edit as required before publishing...
- name: Download source deb
uses: actions/download-artifact@v5
with:
pattern: firmware-debian-${{ needs.version.outputs.deb }}~UNRELEASED-src
merge-multiple: true
path: ./output/debian-src
- name: Download `native-tft` pio deps
uses: actions/download-artifact@v5
with:
pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }}
merge-multiple: true
path: ./output/pio-deps-native-tft
- name: Zip Linux sources
working-directory: output
run: |
zip -j -9 -r ./meshtasticd-${{ needs.version.outputs.deb }}-src.zip ./debian-src
zip -9 -r ./platformio-deps-native-tft-${{ needs.version.outputs.long }}.zip ./pio-deps-native-tft
# For diagnostics
- name: Display structure of downloaded files
run: ls -lR
- name: Add Linux sources to GtiHub Release
# Only run when targeting master branch with workflow_dispatch
if: ${{ github.ref_name == 'master' }}
run: |
gh release upload v${{ needs.version.outputs.long }} ./output/meshtasticd-${{ needs.version.outputs.deb }}-src.zip
gh release upload v${{ needs.version.outputs.long }} ./output/platformio-deps-native-tft-${{ needs.version.outputs.long }}.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
release-firmware:
strategy:
fail-fast: false
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' && inputs.target != ''}}
needs: [release-artifacts, version]
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: 3.x
- uses: actions/download-artifact@v5
with:
pattern: firmware-*-${{ needs.version.outputs.long }}
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-${{inputs.target}}-${{ needs.version.outputs.long }}.zip ./output
- uses: actions/download-artifact@v5
with:
pattern: debug-elfs-*-${{ needs.version.outputs.long }}.zip
merge-multiple: true
path: ./elfs
- name: Zip debug elfs
run: zip -j -9 -r ./debug-elfs-${{inputs.target}}-${{ needs.version.outputs.long }}.zip ./elfs
# For diagnostics
- name: Display structure of downloaded files
run: ls -lR
- name: Add bins and debug elfs to GitHub Release
# Only run when targeting master branch with workflow_dispatch
if: ${{ github.ref_name == 'master' }}
run: |
gh release upload v${{ needs.version.outputs.long }} ./firmware-${{inputs.target}}-${{ needs.version.outputs.long }}.zip
gh release upload v${{ needs.version.outputs.long }} ./debug-elfs-${{inputs.target}}-${{ needs.version.outputs.long }}.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
publish-firmware:
runs-on: ubuntu-24.04
if: ${{ github.event_name == 'workflow_dispatch' && github.repository == 'meshtastic/firmware' && inputs.target != '' }}
needs: [release-firmware, version]
env:
targets: |-
esp32,esp32s3,esp32c3,esp32c6,nrf52840,rp2040,rp2350,stm32
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: 3.x
- uses: actions/download-artifact@v5
with:
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
merge-multiple: true
path: ./publish
- name: Publish firmware to meshtastic.github.io
uses: peaceiris/actions-gh-pages@v4
env:
# On event/* branches, use the event name as the destination prefix
DEST_PREFIX: ${{ contains(github.ref_name, 'event/') && format('{0}/', github.ref_name) || '' }}
with:
deploy_key: ${{ secrets.DIST_PAGES_DEPLOY_KEY }}
external_repository: meshtastic/meshtastic.github.io
publish_branch: master
publish_dir: ./publish
destination_dir: ${{ env.DEST_PREFIX }}firmware-${{ needs.version.outputs.long }}
keep_files: true
user_name: github-actions[bot]
user_email: github-actions[bot]@users.noreply.github.com
commit_message: ${{ needs.version.outputs.long }}
enable_jekyll: true

View File

@@ -27,11 +27,19 @@ on:
jobs: jobs:
setup: setup:
if: github.repository == 'meshtastic/firmware'
strategy: strategy:
fail-fast: true fail-fast: false
matrix: matrix:
arch: arch:
- all - esp32
- esp32s3
- esp32c3
- esp32c6
- nrf52840
- rp2040
- rp2350
- stm32
- check - check
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
@@ -41,22 +49,33 @@ jobs:
python-version: 3.x python-version: 3.x
cache: pip cache: pip
- run: pip install -U platformio - run: pip install -U platformio
- name: Uncomment build epoch
shell: bash
run: |
sed -i 's/#-DBUILD_EPOCH=$UNIX_TIME/-DBUILD_EPOCH=$UNIX_TIME/' platformio.ini
- name: Generate matrix - name: Generate matrix
id: jsonStep id: jsonStep
run: | run: |
if [[ "$GITHUB_HEAD_REF" == "" ]]; then if [[ "$GITHUB_HEAD_REF" == "" ]]; then
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}}) TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}})
else else
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}} --level pr) TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}} pr)
fi fi
echo "Name: $GITHUB_REF_NAME Base: $GITHUB_BASE_REF Ref: $GITHUB_REF" echo "Name: $GITHUB_REF_NAME Base: $GITHUB_BASE_REF Ref: $GITHUB_REF Targets: $TARGETS"
echo "${{matrix.arch}}=$TARGETS" >> $GITHUB_OUTPUT echo "${{matrix.arch}}=$(jq -cn --argjson environments "$TARGETS" '{board: $environments}')" >> $GITHUB_OUTPUT
echo "$TARGETS" >> $GITHUB_STEP_SUMMARY
outputs: outputs:
all: ${{ steps.jsonStep.outputs.all }} 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 }}
rp2350: ${{ steps.jsonStep.outputs.rp2350 }}
stm32: ${{ steps.jsonStep.outputs.stm32 }}
check: ${{ steps.jsonStep.outputs.check }} check: ${{ steps.jsonStep.outputs.check }}
version: version:
if: github.repository == 'meshtastic/firmware'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
@@ -75,8 +94,7 @@ jobs:
needs: setup needs: setup
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix: ${{ fromJson(needs.setup.outputs.check) }}
check: ${{ fromJson(needs.setup.outputs.check) }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name != 'workflow_dispatch' && github.repository == 'meshtastic/firmware' }} if: ${{ github.event_name != 'workflow_dispatch' && github.repository == 'meshtastic/firmware' }}
@@ -85,20 +103,96 @@ jobs:
- name: Build base - name: Build base
id: base id: base
uses: ./.github/actions/setup-base uses: ./.github/actions/setup-base
- name: Check ${{ matrix.check.board }} - name: Check ${{ matrix.board }}
run: bin/check-all.sh ${{ matrix.check.board }} run: bin/check-all.sh ${{ matrix.board }}
build: build-esp32:
needs: [setup, version] needs: [setup, version]
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix: ${{ fromJson(needs.setup.outputs.esp32) }}
build: ${{ fromJson(needs.setup.outputs.all) }}
uses: ./.github/workflows/build_firmware.yml uses: ./.github/workflows/build_firmware.yml
with: with:
version: ${{ needs.version.outputs.long }} version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.build.board }} pio_env: ${{ matrix.board }}
platform: ${{ matrix.build.platform }} platform: esp32
build-esp32s3:
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.esp32s3) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: esp32s3
build-esp32c3:
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.esp32c3) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: esp32c3
build-esp32c6:
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.esp32c6) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: esp32c6
build-nrf52840:
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.nrf52840) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: nrf52840
build-rp2040:
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.rp2040) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: rp2040
build-rp2350:
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.rp2350) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: rp2350
build-stm32:
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.stm32) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: stm32
build-debian-src: build-debian-src:
if: github.repository == 'meshtastic/firmware' if: github.repository == 'meshtastic/firmware'
@@ -109,7 +203,7 @@ jobs:
secrets: inherit secrets: inherit
package-pio-deps-native-tft: package-pio-deps-native-tft:
if: ${{ github.repository == 'meshtastic/firmware' && github.event_name == 'workflow_dispatch' }} if: ${{ github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/package_pio_deps.yml uses: ./.github/workflows/package_pio_deps.yml
with: with:
pio_env: native-tft pio_env: native-tft
@@ -119,26 +213,60 @@ jobs:
if: ${{ !contains(github.ref_name, 'event/') && github.repository == 'meshtastic/firmware' }} if: ${{ !contains(github.ref_name, 'event/') && github.repository == 'meshtastic/firmware' }}
uses: ./.github/workflows/test_native.yml uses: ./.github/workflows/test_native.yml
docker: docker-deb-amd64:
strategy: if: github.repository == 'meshtastic/firmware'
fail-fast: false
matrix:
distro: [debian, alpine]
platform: [linux/amd64, linux/arm64, linux/arm/v7]
pio_env: [native, native-tft]
exclude:
- distro: alpine
platform: linux/arm/v7
- pio_env: native-tft
platform: linux/arm64
- pio_env: native-tft
platform: linux/arm/v7
uses: ./.github/workflows/docker_build.yml uses: ./.github/workflows/docker_build.yml
with: with:
distro: ${{ matrix.distro }} distro: debian
platform: ${{ matrix.platform }} platform: linux/amd64
runs-on: ${{ contains(matrix.platform, 'arm') && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }} runs-on: ubuntu-24.04
pio_env: ${{ matrix.pio_env }} push: false
docker-deb-amd64-tft:
if: github.repository == 'meshtastic/firmware'
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
pio_env: native-tft
docker-alp-amd64:
if: github.repository == 'meshtastic/firmware'
uses: ./.github/workflows/docker_build.yml
with:
distro: alpine
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
docker-alp-amd64-tft:
if: github.repository == 'meshtastic/firmware'
uses: ./.github/workflows/docker_build.yml
with:
distro: alpine
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
pio_env: native-tft
docker-deb-arm64:
if: github.repository == 'meshtastic/firmware'
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/arm64
runs-on: ubuntu-24.04-arm
push: false
docker-deb-armv7:
if: github.repository == 'meshtastic/firmware'
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/arm/v7
runs-on: ubuntu-24.04-arm
push: false push: false
gather-artifacts: gather-artifacts:
@@ -160,7 +288,18 @@ jobs:
- rp2350 - rp2350
- stm32 - stm32
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [version, build] needs:
[
version,
build-esp32,
build-esp32s3,
build-esp32c3,
build-esp32c6,
build-nrf52840,
build-rp2040,
build-rp2350,
build-stm32,
]
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v5 uses: actions/checkout@v5

View File

@@ -7,13 +7,23 @@ on:
# Merge group is a special trigger that is used to trigger the workflow when a merge group is created. # Merge group is a special trigger that is used to trigger the workflow when a merge group is created.
merge_group: merge_group:
env:
FAIL_FAST_PER_ARCH: true
jobs: jobs:
setup: setup:
strategy: strategy:
fail-fast: true fail-fast: true
matrix: matrix:
arch: arch:
- all - esp32
- esp32s3
- esp32c3
- esp32c6
- nrf52840
- rp2040
- rp2350
- stm32
- check - check
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
@@ -29,12 +39,19 @@ jobs:
if [[ "$GITHUB_HEAD_REF" == "" ]]; then if [[ "$GITHUB_HEAD_REF" == "" ]]; then
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}}) TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}})
else else
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}} --level pr) TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}} pr)
fi fi
echo "Name: $GITHUB_REF_NAME Base: $GITHUB_BASE_REF Ref: $GITHUB_REF" echo "Name: $GITHUB_REF_NAME Base: $GITHUB_BASE_REF Ref: $GITHUB_REF Targets: $TARGETS"
echo "${{matrix.arch}}=$TARGETS" >> $GITHUB_OUTPUT echo "${{matrix.arch}}=$(jq -cn --argjson environments "$TARGETS" '{board: $environments}')" >> $GITHUB_OUTPUT
outputs: outputs:
all: ${{ steps.jsonStep.outputs.all }} 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 }}
rp2350: ${{ steps.jsonStep.outputs.rp2350 }}
stm32: ${{ steps.jsonStep.outputs.stm32 }}
check: ${{ steps.jsonStep.outputs.check }} check: ${{ steps.jsonStep.outputs.check }}
version: version:
@@ -56,8 +73,7 @@ jobs:
needs: setup needs: setup
strategy: strategy:
fail-fast: true fail-fast: true
matrix: matrix: ${{ fromJson(needs.setup.outputs.check) }}
check: ${{ fromJson(needs.setup.outputs.check) }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name != 'workflow_dispatch' }} if: ${{ github.event_name != 'workflow_dispatch' }}
@@ -66,19 +82,96 @@ jobs:
- name: Build base - name: Build base
id: base id: base
uses: ./.github/actions/setup-base uses: ./.github/actions/setup-base
- name: Check ${{ matrix.check.board }} - name: Check ${{ matrix.board }}
run: bin/check-all.sh ${{ matrix.check.board }} run: bin/check-all.sh ${{ matrix.board }}
build: build-esp32:
needs: [setup, version] needs: [setup, version]
strategy: strategy:
matrix: fail-fast: ${{ vars.FAIL_FAST_PER_ARCH == true }}
build: ${{ fromJson(needs.setup.outputs.all) }} matrix: ${{ fromJson(needs.setup.outputs.esp32) }}
uses: ./.github/workflows/build_firmware.yml uses: ./.github/workflows/build_firmware.yml
with: with:
version: ${{ needs.version.outputs.long }} version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.build.board }} pio_env: ${{ matrix.board }}
platform: ${{ matrix.build.platform }} platform: esp32
build-esp32s3:
needs: [setup, version]
strategy:
fail-fast: ${{ vars.FAIL_FAST_PER_ARCH == true }}
matrix: ${{ fromJson(needs.setup.outputs.esp32s3) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: esp32s3
build-esp32c3:
needs: [setup, version]
strategy:
fail-fast: ${{ vars.FAIL_FAST_PER_ARCH == true }}
matrix: ${{ fromJson(needs.setup.outputs.esp32c3) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: esp32c3
build-esp32c6:
needs: [setup, version]
strategy:
fail-fast: ${{ vars.FAIL_FAST_PER_ARCH == true }}
matrix: ${{ fromJson(needs.setup.outputs.esp32c6) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: esp32c6
build-nrf52840:
needs: [setup, version]
strategy:
fail-fast: ${{ vars.FAIL_FAST_PER_ARCH == true }}
matrix: ${{ fromJson(needs.setup.outputs.nrf52840) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: nrf52840
build-rp2040:
needs: [setup, version]
strategy:
fail-fast: ${{ vars.FAIL_FAST_PER_ARCH == true }}
matrix: ${{ fromJson(needs.setup.outputs.rp2040) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: rp2040
build-rp2350:
needs: [setup, version]
strategy:
fail-fast: ${{ vars.FAIL_FAST_PER_ARCH == true }}
matrix: ${{ fromJson(needs.setup.outputs.rp2350) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: rp2350
build-stm32:
needs: [setup, version]
strategy:
fail-fast: ${{ vars.FAIL_FAST_PER_ARCH == true }}
matrix: ${{ fromJson(needs.setup.outputs.stm32) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: stm32
build-debian-src: build-debian-src:
if: github.repository == 'meshtastic/firmware' if: github.repository == 'meshtastic/firmware'
@@ -99,26 +192,54 @@ jobs:
if: ${{ !contains(github.ref_name, 'event/') }} if: ${{ !contains(github.ref_name, 'event/') }}
uses: ./.github/workflows/test_native.yml uses: ./.github/workflows/test_native.yml
docker: docker-deb-amd64:
strategy:
fail-fast: false
matrix:
distro: [debian, alpine]
platform: [linux/amd64, linux/arm64, linux/arm/v7]
pio_env: [native, native-tft]
exclude:
- distro: alpine
platform: linux/arm/v7
- pio_env: native-tft
platform: linux/arm64
- pio_env: native-tft
platform: linux/arm/v7
uses: ./.github/workflows/docker_build.yml uses: ./.github/workflows/docker_build.yml
with: with:
distro: ${{ matrix.distro }} distro: debian
platform: ${{ matrix.platform }} platform: linux/amd64
runs-on: ${{ contains(matrix.platform, 'arm') && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }} runs-on: ubuntu-24.04
pio_env: ${{ matrix.pio_env }} push: false
docker-deb-amd64-tft:
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
pio_env: native-tft
docker-alp-amd64:
uses: ./.github/workflows/docker_build.yml
with:
distro: alpine
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
docker-alp-amd64-tft:
uses: ./.github/workflows/docker_build.yml
with:
distro: alpine
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
pio_env: native-tft
docker-deb-arm64:
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/arm64
runs-on: ubuntu-24.04-arm
push: false
docker-deb-armv7:
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/arm/v7
runs-on: ubuntu-24.04-arm
push: false push: false
gather-artifacts: gather-artifacts:
@@ -139,7 +260,18 @@ jobs:
- rp2350 - rp2350
- stm32 - stm32
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [version, build] needs:
[
version,
build-esp32,
build-esp32s3,
build-esp32c3,
build-esp32c6,
build-nrf52840,
build-rp2040,
build-rp2350,
build-stm32,
]
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v5 uses: actions/checkout@v5

View File

@@ -41,7 +41,7 @@ jobs:
# step 4 # step 4
- name: publish code scanning alerts - name: publish code scanning alerts
uses: github/codeql-action/upload-sarif@v4 uses: github/codeql-action/upload-sarif@v3
with: with:
sarif_file: report.sarif sarif_file: report.sarif
category: semgrep category: semgrep

View File

@@ -40,7 +40,7 @@ jobs:
- name: Integration test - name: Integration test
run: | run: |
.pio/build/coverage/program -s & .pio/build/coverage/program &
PID=$! PID=$!
timeout 20 bash -c "until ls -al /proc/$PID/fd | grep socket; do sleep 1; done" timeout 20 bash -c "until ls -al /proc/$PID/fd | grep socket; do sleep 1; done"
echo "Simulator started, launching python test..." echo "Simulator started, launching python test..."

View File

@@ -4,19 +4,19 @@ cli:
plugins: plugins:
sources: sources:
- id: trunk - id: trunk
ref: v1.7.3 ref: v1.7.2
uri: https://github.com/trunk-io/plugins uri: https://github.com/trunk-io/plugins
lint: lint:
enabled: enabled:
- checkov@3.2.477 - checkov@3.2.473
- renovate@41.144.1 - renovate@41.132.5
- prettier@3.6.2 - prettier@3.6.2
- trufflehog@3.90.8 - trufflehog@3.90.8
- yamllint@1.37.1 - yamllint@1.37.1
- bandit@1.8.6 - bandit@1.8.6
- trivy@0.67.1 - trivy@0.67.0
- taplo@0.10.0 - taplo@0.10.0
- ruff@0.14.0 - ruff@0.13.2
- isort@6.1.0 - isort@6.1.0
- markdownlint@0.45.0 - markdownlint@0.45.0
- oxipng@9.1.5 - oxipng@9.1.5

View File

@@ -3,7 +3,7 @@
# trunk-ignore-all(hadolint/DL3008): Do not pin apt package versions # trunk-ignore-all(hadolint/DL3008): Do not pin apt package versions
# trunk-ignore-all(hadolint/DL3013): Do not pin pip package versions # trunk-ignore-all(hadolint/DL3013): Do not pin pip package versions
FROM python:3.14-slim-trixie AS builder FROM python:3.13-slim-trixie AS builder
ARG PIO_ENV=native ARG PIO_ENV=native
ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_FRONTEND=noninteractive
ENV TZ=Etc/UTC ENV TZ=Etc/UTC

View File

@@ -3,7 +3,7 @@
# trunk-ignore-all(hadolint/DL3018): Do not pin apk package versions # trunk-ignore-all(hadolint/DL3018): Do not pin apk package versions
# trunk-ignore-all(hadolint/DL3013): Do not pin pip package versions # trunk-ignore-all(hadolint/DL3013): Do not pin pip package versions
FROM python:3.14-alpine3.22 AS builder FROM python:3.13-alpine3.22 AS builder
ARG PIO_ENV=native ARG PIO_ENV=native
ENV PIP_ROOT_USER_ACTION=ignore ENV PIP_ROOT_USER_ACTION=ignore

View File

@@ -31,7 +31,6 @@ build_flags =
-DMYNEWT_VAL_BLE_HS_LOG_LVL=LOG_LEVEL_CRITICAL -DMYNEWT_VAL_BLE_HS_LOG_LVL=LOG_LEVEL_CRITICAL
-DAXP_DEBUG_PORT=Serial -DAXP_DEBUG_PORT=Serial
-DCONFIG_BT_NIMBLE_ENABLED -DCONFIG_BT_NIMBLE_ENABLED
-DCONFIG_BT_NIMBLE_MAX_BONDS=6 # default is 3
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=2 -DCONFIG_NIMBLE_CPP_LOG_LEVEL=2
-DCONFIG_BT_NIMBLE_MAX_CCCDS=20 -DCONFIG_BT_NIMBLE_MAX_CCCDS=20
-DCONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=8192 -DCONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=8192
@@ -57,7 +56,7 @@ lib_deps =
# renovate: datasource=git-refs depName=libpax packageName=https://github.com/dbinfrago/libpax gitBranch=master # renovate: datasource=git-refs depName=libpax packageName=https://github.com/dbinfrago/libpax gitBranch=master
https://github.com/dbinfrago/libpax/archive/3cdc0371c375676a97967547f4065607d4c53fd1.zip https://github.com/dbinfrago/libpax/archive/3cdc0371c375676a97967547f4065607d4c53fd1.zip
# renovate: datasource=github-tags depName=XPowersLib packageName=lewisxhe/XPowersLib # renovate: datasource=github-tags depName=XPowersLib packageName=lewisxhe/XPowersLib
https://github.com/lewisxhe/XPowersLib/archive/v0.3.1.zip https://github.com/lewisxhe/XPowersLib/archive/v0.3.0.zip
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master # renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto # renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto

View File

@@ -28,7 +28,7 @@ lib_deps =
${environmental_extra.lib_deps} ${environmental_extra.lib_deps}
${radiolib_base.lib_deps} ${radiolib_base.lib_deps}
# renovate: datasource=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib # renovate: datasource=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib
lewisxhe/XPowersLib@0.3.1 lewisxhe/XPowersLib@0.3.0
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master # renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto # renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto

View File

@@ -47,6 +47,8 @@ else
cp bin/*.uf2 $OUTDIR cp bin/*.uf2 $OUTDIR
fi fi
cp $OUTDIR/$basename.uf2 $OUTDIR/$basename-update.uf2
if (echo $1 | grep -q "rak4631"); then if (echo $1 | grep -q "rak4631"); then
echo "Copying hex file" echo "Copying hex file"
cp .pio/build/$1/firmware.hex $OUTDIR/$basename.hex cp .pio/build/$1/firmware.hex $OUTDIR/$basename.hex

View File

@@ -29,5 +29,7 @@ echo "Copying uf2 file"
SRCBIN=.pio/build/$1/firmware.uf2 SRCBIN=.pio/build/$1/firmware.uf2
cp $SRCBIN $OUTDIR/$basename.uf2 cp $SRCBIN $OUTDIR/$basename.uf2
cp $OUTDIR/$basename.uf2 $OUTDIR/$basename-update.uf2
cp bin/device-install.* $OUTDIR cp bin/device-install.* $OUTDIR
cp bin/device-update.* $OUTDIR cp bin/device-update.* $OUTDIR

View File

@@ -27,3 +27,5 @@ cp $SRCELF $OUTDIR/$basename.elf
SRCBIN=.pio/build/$1/firmware.bin SRCBIN=.pio/build/$1/firmware.bin
cp $SRCBIN $OUTDIR/$basename.bin cp $SRCBIN $OUTDIR/$basename.bin
cp $OUTDIR/$basename.bin $OUTDIR/$basename-update.bin

View File

@@ -1,32 +1,28 @@
#!/usr/bin/env python3 #!/usr/bin/env python
"""Generate the CI matrix.""" """Generate the CI matrix."""
import argparse
import json import json
import sys
import random
import re import re
from platformio.project.config import ProjectConfig from platformio.project.config import ProjectConfig
parser = argparse.ArgumentParser(description="Generate the CI matrix") options = sys.argv[1:]
parser.add_argument("platform", help="Platform to build for")
parser.add_argument(
"--level",
choices=["extra", "pr"],
nargs="*",
default=[],
help="Board level to build for (omit for full release boards)",
)
args = parser.parse_args()
outlist = [] outlist = []
if len(options) < 1:
print(json.dumps(outlist))
exit(1)
cfg = ProjectConfig.get_instance() cfg = ProjectConfig.get_instance()
pio_envs = cfg.envs() pio_envs = cfg.envs()
# Gather all PlatformIO environments for filtering later # Gather all PlatformIO environments for filtering later
all_envs = [] all_envs = []
for pio_env in pio_envs: for pio_env in pio_envs:
env_build_flags = cfg.get(f"env:{pio_env}", "build_flags") env_build_flags = cfg.get(f"env:{pio_env}", 'build_flags')
env_platform = None env_platform = None
for flag in env_build_flags: for flag in env_build_flags:
# Extract the platform from the build flags # Extract the platform from the build flags
@@ -41,35 +37,36 @@ for pio_env in pio_envs:
exit(1) exit(1)
# Store env details as a dictionary, and add to 'all_envs' list # Store env details as a dictionary, and add to 'all_envs' list
env = { env = {
"ci": {"board": pio_env, "platform": env_platform}, 'name': pio_env,
"board_level": cfg.get(f"env:{pio_env}", "board_level", default=None), 'platform': env_platform,
"board_check": bool(cfg.get(f"env:{pio_env}", "board_check", default=False)), 'board_level': cfg.get(f"env:{pio_env}", 'board_level', default=None),
'board_check': bool(cfg.get(f"env:{pio_env}", 'board_check', default=False))
} }
all_envs.append(env) all_envs.append(env)
# Filter outputs based on options # Filter outputs based on options
# Check is mutually exclusive with other options (except 'pr') # Check is mutually exclusive with other options (except 'pr')
if "check" in args.platform: if "check" in options:
for env in all_envs: for env in all_envs:
if env["board_check"]: if env['board_check']:
if "pr" in args.level: if "pr" in options:
if env["board_level"] == "pr": if env['board_level'] == 'pr':
outlist.append(env["ci"]) outlist.append(env['name'])
else: else:
outlist.append(env["ci"]) outlist.append(env['name'])
# Filter (non-check) builds by platform # Filter (non-check) builds by platform
else: else:
for env in all_envs: for env in all_envs:
if args.platform == env["ci"]["platform"] or args.platform == "all": if options[0] == env['platform']:
# Always include board_level = 'pr' # Always include board_level = 'pr'
if env["board_level"] == "pr": if env['board_level'] == 'pr':
outlist.append(env["ci"]) outlist.append(env['name'])
# Include board_level = 'extra' when requested # Include board_level = 'extra' when requested
elif "extra" in args.level and env["board_level"] == "extra": elif "extra" in options and env['board_level'] == "extra":
outlist.append(env["ci"]) outlist.append(env['name'])
# If no board level is specified, include in release builds (not PR) # If no board level is specified, include in release builds (not PR)
elif "pr" not in args.level and not env["board_level"]: elif "pr" not in options and not env['board_level']:
outlist.append(env["ci"]) outlist.append(env['name'])
# Return as a JSON list # Return as a JSON list
print(json.dumps(outlist)) print(json.dumps(outlist))

View File

@@ -1,116 +0,0 @@
#!/bin/bash
# Script to cancel all running GitHub Actions workflows
# Requires GitHub CLI (gh) to be installed and authenticated
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Function to print colored output
print_status() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Check if gh CLI is installed
if ! command -v gh &> /dev/null; then
print_error "GitHub CLI (gh) is not installed. Please install it first:"
echo " brew install gh"
echo " Or visit: https://cli.github.com/"
exit 1
fi
# Check if authenticated
if ! gh auth status &> /dev/null; then
print_error "GitHub CLI is not authenticated. Please run:"
echo " gh auth login"
exit 1
fi
# Get repository info
REPO=$(gh repo view --json owner,name -q '.owner.login + "/" + .name')
if [[ -z "$REPO" ]]; then
print_error "Could not determine repository. Make sure you're in a GitHub repository."
exit 1
fi
print_status "Working with repository: $REPO"
# Get all active workflows (both queued and in-progress)
print_status "Fetching active workflows (queued and in-progress)..."
QUEUED_WORKFLOWS=$(gh run list --status queued --json databaseId,displayTitle,headBranch,status --limit 100)
IN_PROGRESS_WORKFLOWS=$(gh run list --status in_progress --json databaseId,displayTitle,headBranch,status --limit 100)
# Combine both lists
ALL_WORKFLOWS=$(echo "$QUEUED_WORKFLOWS $IN_PROGRESS_WORKFLOWS" | jq -s 'add | unique_by(.databaseId)')
if [[ "$ALL_WORKFLOWS" == "[]" ]]; then
print_status "No active workflows found."
exit 0
fi
# Parse and display active workflows
echo
print_warning "Found active workflows:"
echo "$ALL_WORKFLOWS" | jq -r '.[] | " - \(.displayTitle) (Branch: \(.headBranch), Status: \(.status), ID: \(.databaseId))"'
echo
read -p "Do you want to cancel ALL these workflows? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
print_status "Cancelled by user."
exit 0
fi
# Cancel each workflow
print_status "Cancelling workflows..."
CANCELLED_COUNT=0
FAILED_COUNT=0
while IFS= read -r WORKFLOW_ID; do
if [[ -n "$WORKFLOW_ID" ]]; then
print_status "Cancelling workflow ID: $WORKFLOW_ID"
if gh run cancel "$WORKFLOW_ID" 2>/dev/null; then
((CANCELLED_COUNT++))
else
print_error "Failed to cancel workflow ID: $WORKFLOW_ID"
((FAILED_COUNT++))
fi
fi
done < <(echo "$ALL_WORKFLOWS" | jq -r '.[].databaseId')
echo
print_status "Summary:"
echo " - Cancelled: $CANCELLED_COUNT workflows"
if [[ $FAILED_COUNT -gt 0 ]]; then
echo " - Failed: $FAILED_COUNT workflows"
fi
print_status "Done!"
# Optional: Show remaining active workflows
echo
print_status "Checking for any remaining active workflows..."
REMAINING_QUEUED=$(gh run list --status queued --json databaseId --limit 10)
REMAINING_IN_PROGRESS=$(gh run list --status in_progress --json databaseId --limit 10)
REMAINING_ALL=$(echo "$REMAINING_QUEUED $REMAINING_IN_PROGRESS" | jq -s 'add | unique_by(.databaseId)')
if [[ "$REMAINING_ALL" == "[]" ]]; then
print_status "All workflows successfully cancelled."
else
REMAINING_COUNT=$(echo "$REMAINING_ALL" | jq '. | length')
print_warning "Still $REMAINING_COUNT workflows active (may take a moment to update status)"
fi

View File

@@ -87,9 +87,6 @@
</screenshots> </screenshots>
<releases> <releases>
<release version="2.7.13" date="2025-10-11">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.13</url>
</release>
<release version="2.7.12" date="2025-10-01"> <release version="2.7.12" date="2025-10-01">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.12</url> <url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.12</url>
</release> </release>

View File

@@ -1 +1 @@
2.6.6 2.6.4

6
debian/changelog vendored
View File

@@ -1,9 +1,3 @@
meshtasticd (2.7.13.0) unstable; urgency=medium
* Version 2.7.13
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Sat, 11 Oct 2025 15:27:28 +0000
meshtasticd (2.7.12.0) unstable; urgency=medium meshtasticd (2.7.12.0) unstable; urgency=medium
[ Austin Lane ] [ Austin Lane ]

View File

@@ -70,7 +70,7 @@ lib_deps =
# renovate: datasource=git-refs depName=meshtastic-TinyGPSPlus packageName=https://github.com/meshtastic/TinyGPSPlus gitBranch=master # renovate: datasource=git-refs depName=meshtastic-TinyGPSPlus packageName=https://github.com/meshtastic/TinyGPSPlus gitBranch=master
https://github.com/meshtastic/TinyGPSPlus/archive/71a82db35f3b973440044c476d4bcdc673b104f4.zip https://github.com/meshtastic/TinyGPSPlus/archive/71a82db35f3b973440044c476d4bcdc673b104f4.zip
# renovate: datasource=git-refs depName=meshtastic-ArduinoThread packageName=https://github.com/meshtastic/ArduinoThread gitBranch=master # renovate: datasource=git-refs depName=meshtastic-ArduinoThread packageName=https://github.com/meshtastic/ArduinoThread gitBranch=master
https://github.com/meshtastic/ArduinoThread/archive/b841b0415721f1341ea41cccfb4adccfaf951567.zip https://github.com/meshtastic/ArduinoThread/archive/7c3ee9e1951551b949763b1f5280f8db1fa4068d.zip
# renovate: datasource=custom.pio depName=Nanopb packageName=nanopb/library/Nanopb # renovate: datasource=custom.pio depName=Nanopb packageName=nanopb/library/Nanopb
nanopb/Nanopb@0.4.91 nanopb/Nanopb@0.4.91
# renovate: datasource=custom.pio depName=ErriezCRC32 packageName=erriez/library/ErriezCRC32 # renovate: datasource=custom.pio depName=ErriezCRC32 packageName=erriez/library/ErriezCRC32
@@ -120,7 +120,7 @@ lib_deps =
[device-ui_base] [device-ui_base]
lib_deps = lib_deps =
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master # renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
https://github.com/meshtastic/device-ui/archive/19b7855e9a1d9deff37391659ca7194e4ef57c43.zip https://github.com/meshtastic/device-ui/archive/505ffadaa7a931df5dc8153229b719a07bbb028c.zip
; Common libs for environmental measurements in telemetry module ; Common libs for environmental measurements in telemetry module
[environmental_base] [environmental_base]

View File

@@ -562,7 +562,6 @@ class AnalogBatteryLevel : public HasBatteryLevel
config.power.device_battery_ina_address) { config.power.device_battery_ina_address) {
if (!ina226Sensor.isInitialized()) if (!ina226Sensor.isInitialized())
return ina226Sensor.runOnce() > 0; return ina226Sensor.runOnce() > 0;
return ina226Sensor.isRunning();
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260].first == } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260].first ==
config.power.device_battery_ina_address) { config.power.device_battery_ina_address) {
if (!ina260Sensor.isInitialized()) if (!ina260Sensor.isInitialized())
@@ -692,16 +691,6 @@ bool Power::setup()
#ifdef NRF_APM #ifdef NRF_APM
found = true; found = true;
#endif #endif
#ifdef EXT_PWR_DETECT
attachInterrupt(
EXT_PWR_DETECT,
[]() {
power->setIntervalFromNow(0);
runASAP = true;
BaseType_t higherWake = 0;
},
CHANGE);
#endif
enabled = found; enabled = found;
low_voltage_counter = 0; low_voltage_counter = 0;

View File

@@ -33,9 +33,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "pcf8563.h" #include "pcf8563.h"
#endif #endif
/* Offer chance for variant-specific defines */
#include "variant.h"
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Version // Version
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@@ -263,6 +260,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// convert 24-bit color to 16-bit (56K) // convert 24-bit color to 16-bit (56K)
#define COLOR565(r, g, b) (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3)) #define COLOR565(r, g, b) (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3))
/* Step #1: offer chance for variant-specific defines */
#include "variant.h"
#if defined(VEXT_ENABLE) && !defined(VEXT_ON_VALUE) #if defined(VEXT_ENABLE) && !defined(VEXT_ON_VALUE)
// Older variant.h files might not be defining this value, so stay with the old default // Older variant.h files might not be defining this value, so stay with the old default
#define VEXT_ON_VALUE LOW #define VEXT_ON_VALUE LOW

View File

@@ -1,16 +0,0 @@
#include "ScanI2CConsumer.h"
#include <forward_list>
static std::forward_list<ScanI2CConsumer *> ScanI2CConsumers;
ScanI2CConsumer::ScanI2CConsumer()
{
ScanI2CConsumers.push_front(this);
}
void ScanI2CCompleted(ScanI2C *i2cScanner)
{
for (ScanI2CConsumer *consumer : ScanI2CConsumers) {
consumer->i2cScanFinished(i2cScanner);
}
}

View File

@@ -1,13 +0,0 @@
#pragma once
#include "ScanI2C.h"
#include <stddef.h>
class ScanI2CConsumer
{
public:
ScanI2CConsumer();
virtual void i2cScanFinished(ScanI2C *i2cScanner) = 0;
};
void ScanI2CCompleted(ScanI2C *i2cScanner);

View File

@@ -378,8 +378,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
case SHT31_4x_ADDR: // same as OPT3001_ADDR_ALT case SHT31_4x_ADDR: // same as OPT3001_ADDR_ALT
case SHT31_4x_ADDR_ALT: // same as OPT3001_ADDR case SHT31_4x_ADDR_ALT: // same as OPT3001_ADDR
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x89), 2); registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x89), 2);
if (registerValue == 0x11a2 || registerValue == 0x11da || registerValue == 0x11f3 || registerValue == 0xe9c || if (registerValue == 0x11a2 || registerValue == 0x11da || registerValue == 0xe9c || registerValue == 0xc8d) {
registerValue == 0xc8d) {
type = SHT4X; type = SHT4X;
logFoundDevice("SHT4X", (uint8_t)addr.address); logFoundDevice("SHT4X", (uint8_t)addr.address);
} else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2) == 0x5449) { } else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2) == 0x5449) {
@@ -581,7 +580,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port)
scanPort(port, nullptr, 0); scanPort(port, nullptr, 0);
} }
TwoWire *ScanI2CTwoWire::fetchI2CBus(ScanI2C::DeviceAddress address) TwoWire *ScanI2CTwoWire::fetchI2CBus(ScanI2C::DeviceAddress address) const
{ {
if (address.port == ScanI2C::I2CPort::WIRE) { if (address.port == ScanI2C::I2CPort::WIRE) {
return &Wire; return &Wire;

View File

@@ -23,12 +23,12 @@ class ScanI2CTwoWire : public ScanI2C
ScanI2C::FoundDevice find(ScanI2C::DeviceType) const override; ScanI2C::FoundDevice find(ScanI2C::DeviceType) const override;
TwoWire *fetchI2CBus(ScanI2C::DeviceAddress) const;
bool exists(ScanI2C::DeviceType) const override; bool exists(ScanI2C::DeviceType) const override;
size_t countDevices() const override; size_t countDevices() const override;
static TwoWire *fetchI2CBus(ScanI2C::DeviceAddress);
protected: protected:
FoundDevice firstOfOrNONE(size_t, DeviceType[]) const override; FoundDevice firstOfOrNONE(size_t, DeviceType[]) const override;

View File

@@ -494,10 +494,22 @@ bool GPS::setup()
if (!didSerialInit) { if (!didSerialInit) {
int msglen = 0; int msglen = 0;
if (tx_gpio && gnssModel == GNSS_MODEL_UNKNOWN) { if (tx_gpio && gnssModel == GNSS_MODEL_UNKNOWN) {
#ifdef TRACKER_T1000_E
// add power up/down strategy, improve ag3335 detection success
digitalWrite(PIN_GPS_EN, LOW);
delay(500);
digitalWrite(GPS_VRTC_EN, LOW);
delay(1000);
digitalWrite(GPS_VRTC_EN, HIGH);
delay(500);
digitalWrite(PIN_GPS_EN, HIGH);
delay(1000);
#endif
if (probeTries < GPS_PROBETRIES) { if (probeTries < GPS_PROBETRIES) {
LOG_DEBUG("Probe for GPS at %d", serialSpeeds[speedSelect]);
gnssModel = probe(serialSpeeds[speedSelect]); gnssModel = probe(serialSpeeds[speedSelect]);
if (gnssModel == GNSS_MODEL_UNKNOWN) { if (gnssModel == GNSS_MODEL_UNKNOWN) {
if (currentStep == 0 && ++speedSelect == array_count(serialSpeeds)) { if (++speedSelect == array_count(serialSpeeds)) {
speedSelect = 0; speedSelect = 0;
++probeTries; ++probeTries;
} }
@@ -506,9 +518,10 @@ bool GPS::setup()
// Rare Serial Speeds // Rare Serial Speeds
#ifndef CONFIG_IDF_TARGET_ESP32C6 #ifndef CONFIG_IDF_TARGET_ESP32C6
if (probeTries == GPS_PROBETRIES) { if (probeTries == GPS_PROBETRIES) {
LOG_DEBUG("Probe for GPS at %d", rareSerialSpeeds[speedSelect]);
gnssModel = probe(rareSerialSpeeds[speedSelect]); gnssModel = probe(rareSerialSpeeds[speedSelect]);
if (gnssModel == GNSS_MODEL_UNKNOWN) { if (gnssModel == GNSS_MODEL_UNKNOWN) {
if (currentStep == 0 && ++speedSelect == array_count(rareSerialSpeeds)) { if (++speedSelect == array_count(rareSerialSpeeds)) {
LOG_WARN("Give up on GPS probe and set to %d", GPS_BAUDRATE); LOG_WARN("Give up on GPS probe and set to %d", GPS_BAUDRATE);
return true; return true;
} }
@@ -1020,7 +1033,7 @@ void GPS::down()
LOG_DEBUG("%us until next search", sleepTime / 1000); LOG_DEBUG("%us until next search", sleepTime / 1000);
// If update interval less than 10 seconds, no attempt to sleep // If update interval less than 10 seconds, no attempt to sleep
if (updateInterval <= GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS || sleepTime == 0) if (updateInterval <= 10 * 1000UL || sleepTime == 0)
setPowerState(GPS_IDLE); setPowerState(GPS_IDLE);
else { else {
@@ -1081,7 +1094,7 @@ int32_t GPS::runOnce()
return disable(); return disable();
} }
if (!setup()) if (!setup())
return currentDelay; // Setup failed, re-run in two seconds return 2000; // Setup failed, re-run in two seconds
// We have now loaded our saved preferences from flash // We have now loaded our saved preferences from flash
if (config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED) { if (config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
@@ -1091,29 +1104,6 @@ int32_t GPS::runOnce()
publishUpdate(); publishUpdate();
} }
// ======================== GPS_ACTIVE state ========================
// In GPS_ACTIVE state, GPS is powered on and we're receiving NMEA messages.
// We use the following logic to determine when to update the local position
// or time by running GPS::publishUpdate.
// Note: Local position update is asynchronous to position broadcast. We
// generally run this state every gps_update_interval seconds, and in most cases
// gps_update_interval is faster than the position broadcast interval so there's a
// fresh position ready when the device wants to broadcast one on the mesh.
//
// 1. Got a time for the first time --> set the time, don't publish.
// 2. Got a lock for the first time
// --> If gps_update_interval is <= 10s --> publishUpdate
// --> Otherwise, hold for MIN(gps_update_interval - GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS, 20s)
// 3. Got a lock after turning back on
// --> If gps_update_interval is <= 10s --> publishUpdate
// --> Otherwise, hold for MIN(gps_update_interval - GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS, 20s)
// 4. Hold has expired
// --> If we have a time and a location --> publishUpdate
// --> down()
// 5. Search time has expired
// --> If we have a time and a location --> publishUpdate
// --> If we had a location before but don't now --> publishUpdate
// --> down()
if (whileActive()) { if (whileActive()) {
// if we have received valid NMEA claim we are connected // if we have received valid NMEA claim we are connected
setConnected(); setConnected();
@@ -1123,81 +1113,55 @@ int32_t GPS::runOnce()
if (!config.position.fixed_position && powerState != GPS_ACTIVE && scheduling.isUpdateDue()) if (!config.position.fixed_position && powerState != GPS_ACTIVE && scheduling.isUpdateDue())
up(); up();
// quality of the previous fix. We set it to 0 when we go down, so it's a way // If we've already set time from the GPS, no need to ask the GPS
// to check if we're getting a lock after being GPS_OFF. bool gotTime = (getRTCQuality() >= RTCQualityGPS);
if (!gotTime && lookForTime()) { // Note: we count on this && short-circuiting and not resetting the RTC time
gotTime = true;
shouldPublish = true;
}
uint8_t prev_fixQual = fixQual; uint8_t prev_fixQual = fixQual;
bool gotLoc = lookForLocation();
if (gotLoc && !hasValidLocation) { // declare that we have location ASAP
LOG_DEBUG("hasValidLocation RISING EDGE");
hasValidLocation = true;
shouldPublish = true;
// Hold for 20secs after getting a lock to download ephemeris etc
fixHoldEnds = millis() + 20000;
}
if (powerState == GPS_ACTIVE) { if (gotLoc && prev_fixQual == 0) { // just got a lock after turning back on.
// if gps_update_interval is <=10s, GPS never goes off, so we treat that differently fixHoldEnds = millis() + 20000;
uint32_t updateInterval = Default::getConfiguredOrDefaultMs(config.position.gps_update_interval); shouldPublish = true; // Publish immediately, since next publish is at end of hold
}
// 1. Got a time for the first time bool tooLong = scheduling.searchedTooLong();
bool gotTime = (getRTCQuality() >= RTCQualityGPS); if (tooLong)
if (!gotTime && lookForTime()) { // Note: we count on this && short-circuiting and not resetting the RTC time LOG_WARN("Couldn't publish a valid location: didn't get a GPS lock in time");
gotTime = true;
}
// 2. Got a lock for the first time, or 3. Got a lock after turning back on // Once we get a location we no longer desperately want an update
bool gotLoc = lookForLocation(); if ((gotLoc && gotTime) || tooLong) {
if (gotLoc) {
#ifdef GPS_DEBUG
if (!hasValidLocation) { // declare that we have location ASAP
LOG_DEBUG("hasValidLocation RISING EDGE");
}
#endif
if (updateInterval <= GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS) {
hasValidLocation = true;
shouldPublish = true;
} else if (!hasValidLocation || prev_fixQual == 0 || (fixHoldEnds + GPS_THREAD_INTERVAL) < millis()) {
hasValidLocation = true;
// Hold for up to 20secs after getting a lock to download ephemeris etc
uint32_t holdTime = updateInterval - GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS;
if (holdTime > GPS_FIX_HOLD_MAX_MS)
holdTime = GPS_FIX_HOLD_MAX_MS;
fixHoldEnds = millis() + holdTime;
#ifdef GPS_DEBUG
LOG_DEBUG("Holding for %ums after lock", holdTime);
#endif
}
}
bool tooLong = scheduling.searchedTooLong();
if (tooLong && !gotLoc) { if (tooLong && !gotLoc) {
LOG_WARN("Couldn't publish a valid location: didn't get a GPS lock in time");
// we didn't get a location during this ack window, therefore declare loss of lock // we didn't get a location during this ack window, therefore declare loss of lock
if (hasValidLocation) { if (hasValidLocation) {
p = meshtastic_Position_init_default;
hasValidLocation = false;
shouldPublish = true;
#ifdef GPS_DEBUG
LOG_DEBUG("hasValidLocation FALLING EDGE"); LOG_DEBUG("hasValidLocation FALLING EDGE");
#endif
} }
p = meshtastic_Position_init_default;
hasValidLocation = false;
} }
if (millis() > fixHoldEnds) {
// Hold has expired , Search time has expired, we got a time only, or we never needed to hold. shouldPublish = true; // publish our update at the end of the lock hold
bool holdExpired = (fixHoldEnds != 0 && millis() > fixHoldEnds); publishUpdate();
if (shouldPublish || tooLong || holdExpired) { down();
if (gotTime && hasValidLocation) {
shouldPublish = true;
}
if (shouldPublish) {
fixHoldEnds = 0;
publishUpdate();
}
// There's a chance we just got a time, so keep going to see if we can get a location too
if (tooLong || holdExpired) {
down();
}
#ifdef GPS_DEBUG #ifdef GPS_DEBUG
} else if (fixHoldEnds != 0) { } else {
LOG_DEBUG("Holding for GPS data download: %d ms (numSats=%d)", fixHoldEnds - millis(), p.sats_in_view); LOG_DEBUG("Holding for GPS data download: %d ms (numSats=%d)", fixHoldEnds - millis(), p.sats_in_view);
#endif #endif
} }
} }
// ===================== end GPS_ACTIVE state ========================
// If state has changed do a publish
publishUpdate();
if (config.position.fixed_position == true && hasValidLocation) if (config.position.fixed_position == true && hasValidLocation)
return disable(); // This should trigger when we have a fixed position, and get that first position return disable(); // This should trigger when we have a fixed position, and get that first position
@@ -1254,197 +1218,163 @@ static const char *DETECTED_MESSAGE = "%s detected";
GnssModel_t GPS::probe(int serialSpeed) GnssModel_t GPS::probe(int serialSpeed)
{ {
uint8_t buffer[768] = {0};
switch (currentStep) {
case 0: {
#if defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) || defined(ARCH_STM32WL) #if defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) || defined(ARCH_STM32WL)
_serial_gps->end(); _serial_gps->end();
_serial_gps->begin(serialSpeed); _serial_gps->begin(serialSpeed);
#elif defined(ARCH_RP2040) #elif defined(ARCH_RP2040)
_serial_gps->end(); _serial_gps->end();
_serial_gps->setFIFOSize(256); _serial_gps->setFIFOSize(256);
_serial_gps->begin(serialSpeed); _serial_gps->begin(serialSpeed);
#else #else
if (_serial_gps->baudRate() != serialSpeed) { if (_serial_gps->baudRate() != serialSpeed) {
LOG_DEBUG("Set GPS Baud to %i", serialSpeed); LOG_DEBUG("Set Baud to %i", serialSpeed);
_serial_gps->updateBaudRate(serialSpeed); _serial_gps->updateBaudRate(serialSpeed);
} }
#endif #endif
memset(&ublox_info, 0, sizeof(ublox_info)); memset(&ublox_info, 0, sizeof(ublox_info));
delay(100); uint8_t buffer[768] = {0};
delay(100);
// Close all NMEA sentences, valid for L76K, ATGM336H (and likely other AT6558 devices) // Close all NMEA sentences, valid for L76K, ATGM336H (and likely other AT6558 devices)
_serial_gps->write("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n"); _serial_gps->write("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n");
delay(20); delay(20);
// Close NMEA sequences on Ublox // Close NMEA sequences on Ublox
_serial_gps->write("$PUBX,40,GLL,0,0,0,0,0,0*5C\r\n"); _serial_gps->write("$PUBX,40,GLL,0,0,0,0,0,0*5C\r\n");
_serial_gps->write("$PUBX,40,GSV,0,0,0,0,0,0*59\r\n"); _serial_gps->write("$PUBX,40,GSV,0,0,0,0,0,0*59\r\n");
_serial_gps->write("$PUBX,40,VTG,0,0,0,0,0,0*5E\r\n"); _serial_gps->write("$PUBX,40,VTG,0,0,0,0,0,0*5E\r\n");
delay(20); delay(20);
// Close NMEA sequences on CM121 // Close NMEA sequences on CM121
_serial_gps->write("$CFGMSG,0,1,0,1*1B\r\n"); _serial_gps->write("$CFGMSG,0,1,0,1*1B\r\n");
_serial_gps->write("$CFGMSG,0,2,0,1*18\r\n"); _serial_gps->write("$CFGMSG,0,2,0,1*18\r\n");
_serial_gps->write("$CFGMSG,0,3,0,1*19\r\n"); _serial_gps->write("$CFGMSG,0,3,0,1*19\r\n");
currentDelay = 20; delay(20);
currentStep = 1;
return GNSS_MODEL_UNKNOWN;
}
case 1: {
// Unicore UFirebirdII Series: UC6580, UM620, UM621, UM670A, UM680A, or UM681A,or CM121 // Unicore UFirebirdII Series: UC6580, UM620, UM621, UM670A, UM680A, or UM681A,or CM121
std::vector<ChipInfo> unicore = { std::vector<ChipInfo> unicore = {
{"UC6580", "UC6580", GNSS_MODEL_UC6580}, {"UM600", "UM600", GNSS_MODEL_UC6580}, {"CM121", "CM121", GNSS_MODEL_CM121}}; {"UC6580", "UC6580", GNSS_MODEL_UC6580}, {"UM600", "UM600", GNSS_MODEL_UC6580}, {"CM121", "CM121", GNSS_MODEL_CM121}};
PROBE_FAMILY("Unicore Family", "$PDTINFO", unicore, 500); PROBE_FAMILY("Unicore Family", "$PDTINFO", unicore, 500);
currentDelay = 20;
currentStep = 2;
return GNSS_MODEL_UNKNOWN;
}
case 2: {
std::vector<ChipInfo> atgm = {
{"ATGM336H", "$GPTXT,01,01,02,HW=ATGM336H", GNSS_MODEL_ATGM336H},
/* ATGM332D series (-11(GPS), -21(BDS), -31(GPS+BDS), -51(GPS+GLONASS), -71-0(GPS+BDS+GLONASS)) based on AT6558 */
{"ATGM332D", "$GPTXT,01,01,02,HW=ATGM332D", GNSS_MODEL_ATGM336H}};
PROBE_FAMILY("ATGM33xx Family", "$PCAS06,1*1A", atgm, 500);
currentDelay = 20;
currentStep = 3;
return GNSS_MODEL_UNKNOWN;
}
case 3: {
/* Airoha (Mediatek) AG3335A/M/S, A3352Q, Quectel L89 2.0, SimCom SIM65M */
_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
std::vector<ChipInfo> airoha = {{"AG3335", "$PAIR021,AG3335", GNSS_MODEL_AG3335},
{"AG3352", "$PAIR021,AG3352", GNSS_MODEL_AG3352},
{"RYS3520", "$PAIR021,REYAX_RYS3520_V2", GNSS_MODEL_AG3352}};
PROBE_FAMILY("Airoha Family", "$PAIR021*39", airoha, 1000);
currentDelay = 20;
currentStep = 4;
return GNSS_MODEL_UNKNOWN;
}
case 4: {
PROBE_SIMPLE("LC86", "$PQTMVERNO*58", "$PQTMVERNO,LC86", GNSS_MODEL_AG3352, 500);
PROBE_SIMPLE("L76K", "$PCAS06,0*1B", "$GPTXT,01,01,02,SW=", GNSS_MODEL_MTK, 500);
currentDelay = 20;
currentStep = 5;
return GNSS_MODEL_UNKNOWN;
}
case 5: {
// Close all NMEA sentences, valid for MTK3333 and MTK3339 platforms std::vector<ChipInfo> atgm = {
_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"); {"ATGM336H", "$GPTXT,01,01,02,HW=ATGM336H", GNSS_MODEL_ATGM336H},
delay(20); /* ATGM332D series (-11(GPS), -21(BDS), -31(GPS+BDS), -51(GPS+GLONASS), -71-0(GPS+BDS+GLONASS)) based on AT6558 */
std::vector<ChipInfo> mtk = {{"L76B", "Quectel-L76B", GNSS_MODEL_MTK_L76B}, {"PA1010D", "1010D", GNSS_MODEL_MTK_PA1010D}, {"ATGM332D", "$GPTXT,01,01,02,HW=ATGM332D", GNSS_MODEL_ATGM336H}};
{"PA1616S", "1616S", GNSS_MODEL_MTK_PA1616S}, {"LS20031", "MC-1513", GNSS_MODEL_MTK_L76B}, PROBE_FAMILY("ATGM33xx Family", "$PCAS06,1*1A", atgm, 500);
{"L96", "Quectel-L96", GNSS_MODEL_MTK_L76B}, {"L80-R", "_3337_", GNSS_MODEL_MTK_L76B},
{"L80", "_3339_", GNSS_MODEL_MTK_L76B}};
PROBE_FAMILY("MTK Family", "$PMTK605*31", mtk, 500); /* Airoha (Mediatek) AG3335A/M/S, A3352Q, Quectel L89 2.0, SimCom SIM65M */
currentDelay = 20; _serial_gps->write("$PAIR062,2,0*3C\r\n"); // GSA OFF to reduce volume
currentStep = 6; _serial_gps->write("$PAIR062,3,0*3D\r\n"); // GSV OFF to reduce volume
_serial_gps->write("$PAIR513*3D\r\n"); // save configuration
std::vector<ChipInfo> airoha = {{"AG3335", "$PAIR021,AG3335", GNSS_MODEL_AG3335},
{"AG3352", "$PAIR021,AG3352", GNSS_MODEL_AG3352},
{"RYS3520", "$PAIR021,REYAX_RYS3520_V2", GNSS_MODEL_AG3352}};
PROBE_FAMILY("Airoha Family", "$PAIR021*39", airoha, 1000);
PROBE_SIMPLE("LC86", "$PQTMVERNO*58", "$PQTMVERNO,LC86", GNSS_MODEL_AG3352, 500);
PROBE_SIMPLE("L76K", "$PCAS06,0*1B", "$GPTXT,01,01,02,SW=", GNSS_MODEL_MTK, 500);
// Close all NMEA sentences, valid for MTK3333 and MTK3339 platforms
_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);
std::vector<ChipInfo> mtk = {{"L76B", "Quectel-L76B", GNSS_MODEL_MTK_L76B}, {"PA1010D", "1010D", GNSS_MODEL_MTK_PA1010D},
{"PA1616S", "1616S", GNSS_MODEL_MTK_PA1616S}, {"LS20031", "MC-1513", GNSS_MODEL_MTK_L76B},
{"L96", "Quectel-L96", GNSS_MODEL_MTK_L76B}, {"L80-R", "_3337_", GNSS_MODEL_MTK_L76B},
{"L80", "_3339_", GNSS_MODEL_MTK_L76B}};
PROBE_FAMILY("MTK Family", "$PMTK605*31", mtk, 500);
uint8_t cfg_rate[] = {0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x00, 0x00};
UBXChecksum(cfg_rate, sizeof(cfg_rate));
clearBuffer();
_serial_gps->write(cfg_rate, sizeof(cfg_rate));
// Check that the returned response class and message ID are correct
GPS_RESPONSE response = getACK(0x06, 0x08, 750);
if (response == GNSS_RESPONSE_NONE) {
LOG_WARN("No GNSS Module (baudrate %d)", serialSpeed);
return GNSS_MODEL_UNKNOWN; return GNSS_MODEL_UNKNOWN;
} else if (response == GNSS_RESPONSE_FRAME_ERRORS) {
LOG_INFO("UBlox Frame Errors (baudrate %d)", serialSpeed);
} }
case 6: {
uint8_t cfg_rate[] = {0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x00, 0x00}; memset(buffer, 0, sizeof(buffer));
UBXChecksum(cfg_rate, sizeof(cfg_rate)); uint8_t _message_MONVER[8] = {
clearBuffer(); 0xB5, 0x62, // Sync message for UBX protocol
_serial_gps->write(cfg_rate, sizeof(cfg_rate)); 0x0A, 0x04, // Message class and ID (UBX-MON-VER)
// Check that the returned response class and message ID are correct 0x00, 0x00, // Length of payload (we're asking for an answer, so no payload)
GPS_RESPONSE response = getACK(0x06, 0x08, 750); 0x00, 0x00 // Checksum
if (response == GNSS_RESPONSE_NONE) { };
LOG_WARN("No GNSS Module (baudrate %d)", serialSpeed); // Get Ublox gnss module hardware and software info
currentDelay = 2000; UBXChecksum(_message_MONVER, sizeof(_message_MONVER));
currentStep = 0; clearBuffer();
return GNSS_MODEL_UNKNOWN; _serial_gps->write(_message_MONVER, sizeof(_message_MONVER));
} else if (response == GNSS_RESPONSE_FRAME_ERRORS) {
LOG_INFO("UBlox Frame Errors (baudrate %d)", serialSpeed); uint16_t len = getACK(buffer, sizeof(buffer), 0x0A, 0x04, 1200);
if (len) {
uint16_t position = 0;
for (int i = 0; i < 30; i++) {
ublox_info.swVersion[i] = buffer[position];
position++;
}
for (int i = 0; i < 10; i++) {
ublox_info.hwVersion[i] = buffer[position];
position++;
}
while (len >= position + 30) {
for (int i = 0; i < 30; i++) {
ublox_info.extension[ublox_info.extensionNo][i] = buffer[position];
position++;
}
ublox_info.extensionNo++;
if (ublox_info.extensionNo > 9)
break;
}
LOG_DEBUG("Module Info : ");
LOG_DEBUG("Soft version: %s", ublox_info.swVersion);
LOG_DEBUG("Hard version: %s", ublox_info.hwVersion);
LOG_DEBUG("Extensions:%d", ublox_info.extensionNo);
for (int i = 0; i < ublox_info.extensionNo; i++) {
LOG_DEBUG(" %s", ublox_info.extension[i]);
} }
memset(buffer, 0, sizeof(buffer)); memset(buffer, 0, sizeof(buffer));
uint8_t _message_MONVER[8] = {
0xB5, 0x62, // Sync message for UBX protocol
0x0A, 0x04, // Message class and ID (UBX-MON-VER)
0x00, 0x00, // Length of payload (we're asking for an answer, so no payload)
0x00, 0x00 // Checksum
};
// Get Ublox gnss module hardware and software info
UBXChecksum(_message_MONVER, sizeof(_message_MONVER));
clearBuffer();
_serial_gps->write(_message_MONVER, sizeof(_message_MONVER));
uint16_t len = getACK(buffer, sizeof(buffer), 0x0A, 0x04, 1200); // tips: extensionNo field is 0 on some 6M GNSS modules
if (len) { for (int i = 0; i < ublox_info.extensionNo; ++i) {
uint16_t position = 0; if (!strncmp(ublox_info.extension[i], "MOD=", 4)) {
for (int i = 0; i < 30; i++) { strncpy((char *)buffer, &(ublox_info.extension[i][4]), sizeof(buffer));
ublox_info.swVersion[i] = buffer[position]; } else if (!strncmp(ublox_info.extension[i], "PROTVER", 7)) {
position++; char *ptr = nullptr;
} memset(buffer, 0, sizeof(buffer));
for (int i = 0; i < 10; i++) { strncpy((char *)buffer, &(ublox_info.extension[i][8]), sizeof(buffer));
ublox_info.hwVersion[i] = buffer[position]; LOG_DEBUG("Protocol Version:%s", (char *)buffer);
position++; if (strlen((char *)buffer)) {
} ublox_info.protocol_version = strtoul((char *)buffer, &ptr, 10);
LOG_DEBUG("ProtVer=%d", ublox_info.protocol_version);
while (len >= position + 30) { } else {
for (int i = 0; i < 30; i++) { ublox_info.protocol_version = 0;
ublox_info.extension[ublox_info.extensionNo][i] = buffer[position];
position++;
} }
ublox_info.extensionNo++;
if (ublox_info.extensionNo > 9)
break;
}
LOG_DEBUG("Module Info : ");
LOG_DEBUG("Soft version: %s", ublox_info.swVersion);
LOG_DEBUG("Hard version: %s", ublox_info.hwVersion);
LOG_DEBUG("Extensions:%d", ublox_info.extensionNo);
for (int i = 0; i < ublox_info.extensionNo; i++) {
LOG_DEBUG(" %s", ublox_info.extension[i]);
}
memset(buffer, 0, sizeof(buffer));
// tips: extensionNo field is 0 on some 6M GNSS modules
for (int i = 0; i < ublox_info.extensionNo; ++i) {
if (!strncmp(ublox_info.extension[i], "MOD=", 4)) {
strncpy((char *)buffer, &(ublox_info.extension[i][4]), sizeof(buffer));
} else if (!strncmp(ublox_info.extension[i], "PROTVER", 7)) {
char *ptr = nullptr;
memset(buffer, 0, sizeof(buffer));
strncpy((char *)buffer, &(ublox_info.extension[i][8]), sizeof(buffer));
LOG_DEBUG("Protocol Version:%s", (char *)buffer);
if (strlen((char *)buffer)) {
ublox_info.protocol_version = strtoul((char *)buffer, &ptr, 10);
LOG_DEBUG("ProtVer=%d", ublox_info.protocol_version);
} else {
ublox_info.protocol_version = 0;
}
}
}
if (strncmp(ublox_info.hwVersion, "00040007", 8) == 0) {
LOG_INFO(DETECTED_MESSAGE, "U-blox 6", "6");
return GNSS_MODEL_UBLOX6;
} else if (strncmp(ublox_info.hwVersion, "00070000", 8) == 0) {
LOG_INFO(DETECTED_MESSAGE, "U-blox 7", "7");
return GNSS_MODEL_UBLOX7;
} else if (strncmp(ublox_info.hwVersion, "00080000", 8) == 0) {
LOG_INFO(DETECTED_MESSAGE, "U-blox 8", "8");
return GNSS_MODEL_UBLOX8;
} else if (strncmp(ublox_info.hwVersion, "00190000", 8) == 0) {
LOG_INFO(DETECTED_MESSAGE, "U-blox 9", "9");
return GNSS_MODEL_UBLOX9;
} else if (strncmp(ublox_info.hwVersion, "000A0000", 8) == 0) {
LOG_INFO(DETECTED_MESSAGE, "U-blox 10", "10");
return GNSS_MODEL_UBLOX10;
} }
} }
} if (strncmp(ublox_info.hwVersion, "00040007", 8) == 0) {
LOG_INFO(DETECTED_MESSAGE, "U-blox 6", "6");
return GNSS_MODEL_UBLOX6;
} else if (strncmp(ublox_info.hwVersion, "00070000", 8) == 0) {
LOG_INFO(DETECTED_MESSAGE, "U-blox 7", "7");
return GNSS_MODEL_UBLOX7;
} else if (strncmp(ublox_info.hwVersion, "00080000", 8) == 0) {
LOG_INFO(DETECTED_MESSAGE, "U-blox 8", "8");
return GNSS_MODEL_UBLOX8;
} else if (strncmp(ublox_info.hwVersion, "00190000", 8) == 0) {
LOG_INFO(DETECTED_MESSAGE, "U-blox 9", "9");
return GNSS_MODEL_UBLOX9;
} else if (strncmp(ublox_info.hwVersion, "000A0000", 8) == 0) {
LOG_INFO(DETECTED_MESSAGE, "U-blox 10", "10");
return GNSS_MODEL_UBLOX10;
}
} }
LOG_WARN("No GNSS Module (baudrate %d)", serialSpeed); LOG_WARN("No GNSS Module (baudrate %d)", serialSpeed);
currentDelay = 2000;
currentStep = 0;
return GNSS_MODEL_UNKNOWN; return GNSS_MODEL_UNKNOWN;
} }

View File

@@ -16,9 +16,6 @@
#define GPS_EN_ACTIVE 1 #define GPS_EN_ACTIVE 1
#endif #endif
static constexpr uint32_t GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS = 10 * 1000UL;
static constexpr uint32_t GPS_FIX_HOLD_MAX_MS = 20000;
typedef enum { typedef enum {
GNSS_MODEL_ATGM336H, GNSS_MODEL_ATGM336H,
GNSS_MODEL_MTK, GNSS_MODEL_MTK,
@@ -154,8 +151,6 @@ class GPS : private concurrency::OSThread
TinyGPSPlus reader; TinyGPSPlus reader;
uint8_t fixQual = 0; // fix quality from GPGGA uint8_t fixQual = 0; // fix quality from GPGGA
uint32_t lastChecksumFailCount = 0; uint32_t lastChecksumFailCount = 0;
uint8_t currentStep = 0;
int32_t currentDelay = 2000;
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS #ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
// (20210908) TinyGps++ can only read the GPGSA "FIX TYPE" field // (20210908) TinyGps++ can only read the GPGSA "FIX TYPE" field
@@ -178,6 +173,8 @@ class GPS : private concurrency::OSThread
*/ */
bool hasValidLocation = false; // default to false, until we complete our first read bool hasValidLocation = false; // default to false, until we complete our first read
bool isInPowersave = false;
bool shouldPublish = false; // If we've changed GPS state, this will force a publish the next loop() bool shouldPublish = false; // If we've changed GPS state, this will force a publish the next loop()
bool hasGPS = false; // Do we have a GPS we are talking to bool hasGPS = false; // Do we have a GPS we are talking to

View File

@@ -1428,9 +1428,6 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
} }
nodeDB->updateGUI = false; nodeDB->updateGUI = false;
break; break;
case STATUS_TYPE_POWER:
forceDisplay(true);
break;
} }
return 0; return 0;
@@ -1490,7 +1487,7 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
strcpy(banner, "Alert Received"); strcpy(banner, "Alert Received");
} }
screen->showSimpleBanner(banner, 3000); screen->showSimpleBanner(banner, 3000);
} else if (!channel.settings.has_module_settings || !channel.settings.module_settings.is_muted) { } else if (!channel.settings.mute) {
if (longName && longName[0]) { if (longName && longName[0]) {
#if defined(M5STACK_UNITC6L) #if defined(M5STACK_UNITC6L)
strcpy(banner, "New Message"); strcpy(banner, "New Message");
@@ -1506,7 +1503,7 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
screen->showSimpleBanner(banner, 1500); screen->showSimpleBanner(banner, 1500);
if (config.device.buzzer_mode != meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY || if (config.device.buzzer_mode != meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY ||
(isAlert && moduleConfig.external_notification.alert_bell_buzzer) || (isAlert && moduleConfig.external_notification.alert_bell_buzzer) ||
(!isBroadcast(packet->to) && isToUs(packet))) { (!isBroadcast(packet->to) && isToUs(p))) {
// Beep if not in DIRECT_MSG_ONLY mode or if in DIRECT_MSG_ONLY mode and either // Beep if not in DIRECT_MSG_ONLY mode or if in DIRECT_MSG_ONLY mode and either
// - packet contains an alert and alert bell buzzer is enabled // - packet contains an alert and alert bell buzzer is enabled
// - packet is a non-broadcast that is addressed to this node // - packet is a non-broadcast that is addressed to this node

View File

@@ -762,31 +762,6 @@ void menuHandler::nodeListMenu()
screen->showOverlayBanner(bannerOptions); screen->showOverlayBanner(bannerOptions);
} }
void menuHandler::nodeNameLengthMenu()
{
enum OptionsNumbers { Back, Long, Short };
static const char *optionsArray[] = {"Back", "Long", "Short"};
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Node Name Length";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 3;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Long) {
// Set names to long
LOG_INFO("Setting names to long");
config.display.use_long_node_name = true;
} else if (selected == Short) {
// Set names to short
LOG_INFO("Setting names to short");
config.display.use_long_node_name = false;
} else if (selected == Back) {
menuQueue = screen_options_menu;
screen->runNow();
}
};
screen->showOverlayBanner(bannerOptions);
}
void menuHandler::resetNodeDBMenu() void menuHandler::resetNodeDBMenu()
{ {
static const char *optionsArray[] = {"Back", "Confirm"}; static const char *optionsArray[] = {"Back", "Confirm"};
@@ -1329,16 +1304,11 @@ void menuHandler::screenOptionsMenu()
hasSupportBrightness = false; hasSupportBrightness = false;
#endif #endif
enum optionsNumbers { Back, NodeNameLength, Brightness, ScreenColor }; enum optionsNumbers { Back, Brightness, ScreenColor };
static const char *optionsArray[5] = {"Back"}; static const char *optionsArray[4] = {"Back"};
static int optionsEnumArray[5] = {Back}; static int optionsEnumArray[4] = {Back};
int options = 1; int options = 1;
#if defined(T_DECK) || defined(T_LORA_PAGER)
optionsArray[options] = "Show Long/Short Name";
optionsEnumArray[options++] = NodeNameLength;
#endif
// Only show brightness for B&W displays // Only show brightness for B&W displays
if (hasSupportBrightness) { if (hasSupportBrightness) {
optionsArray[options] = "Brightness"; optionsArray[options] = "Brightness";
@@ -1363,9 +1333,6 @@ void menuHandler::screenOptionsMenu()
} else if (selected == ScreenColor) { } else if (selected == ScreenColor) {
menuHandler::menuQueue = menuHandler::tftcolormenupicker; menuHandler::menuQueue = menuHandler::tftcolormenupicker;
screen->runNow(); screen->runNow();
} else if (selected == NodeNameLength) {
menuHandler::menuQueue = menuHandler::node_name_length_menu;
screen->runNow();
} else { } else {
menuQueue = system_base_menu; menuQueue = system_base_menu;
screen->runNow(); screen->runNow();
@@ -1464,8 +1431,6 @@ void menuHandler::FrameToggles_menu()
lora, lora,
clock, clock,
show_favorites, show_favorites,
show_telemetry,
show_power,
enumEnd enumEnd
}; };
static const char *optionsArray[enumEnd] = {"Finish"}; static const char *optionsArray[enumEnd] = {"Finish"};
@@ -1504,12 +1469,6 @@ void menuHandler::FrameToggles_menu()
optionsArray[options] = screen->isFrameHidden("show_favorites") ? "Show Favorites" : "Hide Favorites"; optionsArray[options] = screen->isFrameHidden("show_favorites") ? "Show Favorites" : "Hide Favorites";
optionsEnumArray[options++] = show_favorites; optionsEnumArray[options++] = show_favorites;
optionsArray[options] = moduleConfig.telemetry.environment_screen_enabled ? "Hide Telemetry" : "Show Telemetry";
optionsEnumArray[options++] = show_telemetry;
optionsArray[options] = moduleConfig.telemetry.power_screen_enabled ? "Hide Power" : "Show Power";
optionsEnumArray[options++] = show_power;
BannerOverlayOptions bannerOptions; BannerOverlayOptions bannerOptions;
bannerOptions.message = "Show/Hide Frames"; bannerOptions.message = "Show/Hide Frames";
bannerOptions.optionsArrayPtr = optionsArray; bannerOptions.optionsArrayPtr = optionsArray;
@@ -1564,14 +1523,6 @@ void menuHandler::FrameToggles_menu()
screen->toggleFrameVisibility("show_favorites"); screen->toggleFrameVisibility("show_favorites");
menuHandler::menuQueue = menuHandler::FrameToggles; menuHandler::menuQueue = menuHandler::FrameToggles;
screen->runNow(); screen->runNow();
} else if (selected == show_telemetry) {
moduleConfig.telemetry.environment_screen_enabled = !moduleConfig.telemetry.environment_screen_enabled;
menuHandler::menuQueue = menuHandler::FrameToggles;
screen->runNow();
} else if (selected == show_power) {
moduleConfig.telemetry.power_screen_enabled = !moduleConfig.telemetry.power_screen_enabled;
menuHandler::menuQueue = menuHandler::FrameToggles;
screen->runNow();
} }
}; };
screen->showOverlayBanner(bannerOptions); screen->showOverlayBanner(bannerOptions);
@@ -1643,9 +1594,6 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
case brightness_picker: case brightness_picker:
BrightnessPickerMenu(); BrightnessPickerMenu();
break; break;
case node_name_length_menu:
nodeNameLengthMenu();
break;
case reboot_menu: case reboot_menu:
rebootMenu(); rebootMenu();
break; break;

View File

@@ -43,7 +43,6 @@ class menuHandler
key_verification_final_prompt, key_verification_final_prompt,
trace_route_menu, trace_route_menu,
throttle_message, throttle_message,
node_name_length_menu,
FrameToggles FrameToggles
}; };
static screenMenus menuQueue; static screenMenus menuQueue;
@@ -86,7 +85,6 @@ class menuHandler
static void notificationsMenu(); static void notificationsMenu();
static void screenOptionsMenu(); static void screenOptionsMenu();
static void powerMenu(); static void powerMenu();
static void nodeNameLengthMenu();
static void FrameToggles_menu(); static void FrameToggles_menu();
static void textMessageMenu(); static void textMessageMenu();

View File

@@ -55,32 +55,26 @@ static int scrollIndex = 0;
const char *getSafeNodeName(meshtastic_NodeInfoLite *node) const char *getSafeNodeName(meshtastic_NodeInfoLite *node)
{ {
const char *name = NULL;
static char nodeName[16] = "?"; static char nodeName[16] = "?";
if (config.display.use_long_node_name == true) { if (node->has_user && strlen(node->user.short_name) > 0) {
if (node->has_user && strlen(node->user.long_name) > 0) { bool valid = true;
name = node->user.long_name; const char *name = node->user.short_name;
for (size_t i = 0; i < strlen(name); i++) {
uint8_t c = (uint8_t)name[i];
if (c < 32 || c > 126) {
valid = false;
break;
}
}
if (valid) {
strncpy(nodeName, name, sizeof(nodeName) - 1);
nodeName[sizeof(nodeName) - 1] = '\0';
} else { } else {
snprintf(nodeName, sizeof(nodeName), "(%04X)", (uint16_t)(node->num & 0xFFFF)); snprintf(nodeName, sizeof(nodeName), "(%04X)", (uint16_t)(node->num & 0xFFFF));
} }
} else {
if (node->has_user && strlen(node->user.short_name) > 0) {
name = node->user.short_name;
} else {
snprintf(nodeName, sizeof(nodeName), "(%04X)", (uint16_t)(node->num & 0xFFFF));
}
}
// Use sanitizeString() function and copy directly into nodeName
std::string sanitized_name = sanitizeString(name ? name : "");
if (!sanitized_name.empty()) {
strncpy(nodeName, sanitized_name.c_str(), sizeof(nodeName) - 1);
nodeName[sizeof(nodeName) - 1] = '\0';
} else { } else {
snprintf(nodeName, sizeof(nodeName), "(%04X)", (uint16_t)(node->num & 0xFFFF)); snprintf(nodeName, sizeof(nodeName), "(%04X)", (uint16_t)(node->num & 0xFFFF));
} }
return nodeName; return nodeName;
} }

View File

@@ -23,7 +23,6 @@
#include "power.h" #include "power.h"
#if !MESHTASTIC_EXCLUDE_I2C #if !MESHTASTIC_EXCLUDE_I2C
#include "detect/ScanI2CConsumer.h"
#include "detect/ScanI2CTwoWire.h" #include "detect/ScanI2CTwoWire.h"
#include <Wire.h> #include <Wire.h>
#endif #endif
@@ -719,21 +718,46 @@ void setup()
LOG_DEBUG("acc_info = %i", acc_info.type); LOG_DEBUG("acc_info = %i", acc_info.type);
#endif #endif
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::BME_680, meshtastic_TelemetrySensorType_BME680);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::BME_280, meshtastic_TelemetrySensorType_BME280);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::BMP_280, meshtastic_TelemetrySensorType_BMP280);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::BMP_3XX, meshtastic_TelemetrySensorType_BMP3XX);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::BMP_085, meshtastic_TelemetrySensorType_BMP085);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA260, meshtastic_TelemetrySensorType_INA260); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA260, meshtastic_TelemetrySensorType_INA260);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA226, meshtastic_TelemetrySensorType_INA226); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA226, meshtastic_TelemetrySensorType_INA226);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA219, meshtastic_TelemetrySensorType_INA219); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA219, meshtastic_TelemetrySensorType_INA219);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA3221, meshtastic_TelemetrySensorType_INA3221); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA3221, meshtastic_TelemetrySensorType_INA3221);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MAX17048, meshtastic_TelemetrySensorType_MAX17048); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MAX17048, meshtastic_TelemetrySensorType_MAX17048);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MCP9808, meshtastic_TelemetrySensorType_MCP9808);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::SHT31, meshtastic_TelemetrySensorType_SHT31);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::SHTC3, meshtastic_TelemetrySensorType_SHTC3);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::LPS22HB, meshtastic_TelemetrySensorType_LPS22);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::QMC6310, meshtastic_TelemetrySensorType_QMC6310); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::QMC6310, meshtastic_TelemetrySensorType_QMC6310);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::QMI8658, meshtastic_TelemetrySensorType_QMI8658); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::QMI8658, meshtastic_TelemetrySensorType_QMI8658);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::QMC5883L, meshtastic_TelemetrySensorType_QMC5883L); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::QMC5883L, meshtastic_TelemetrySensorType_QMC5883L);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::HMC5883L, meshtastic_TelemetrySensorType_QMC5883L); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::HMC5883L, meshtastic_TelemetrySensorType_QMC5883L);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::PMSA0031, meshtastic_TelemetrySensorType_PMSA003I); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::PMSA0031, meshtastic_TelemetrySensorType_PMSA003I);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::RCWL9620, meshtastic_TelemetrySensorType_RCWL9620);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::VEML7700, meshtastic_TelemetrySensorType_VEML7700);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::TSL2591, meshtastic_TelemetrySensorType_TSL25911FN);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::OPT3001, meshtastic_TelemetrySensorType_OPT3001);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MLX90632, meshtastic_TelemetrySensorType_MLX90632);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MLX90614, meshtastic_TelemetrySensorType_MLX90614); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MLX90614, meshtastic_TelemetrySensorType_MLX90614);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::SHT4X, meshtastic_TelemetrySensorType_SHT4X);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::AHT10, meshtastic_TelemetrySensorType_AHT10);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::DFROBOT_LARK, meshtastic_TelemetrySensorType_DFROBOT_LARK);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::ICM20948, meshtastic_TelemetrySensorType_ICM20948); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::ICM20948, meshtastic_TelemetrySensorType_ICM20948);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MAX30102, meshtastic_TelemetrySensorType_MAX30102); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MAX30102, meshtastic_TelemetrySensorType_MAX30102);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::CGRADSENS, meshtastic_TelemetrySensorType_RADSENS);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::DFROBOT_RAIN, meshtastic_TelemetrySensorType_DFROBOT_RAIN);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::LTR390UV, meshtastic_TelemetrySensorType_LTR390UV);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::DPS310, meshtastic_TelemetrySensorType_DPS310);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::RAK12035, meshtastic_TelemetrySensorType_RAK12035);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::PCT2075, meshtastic_TelemetrySensorType_PCT2075);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::SCD4X, meshtastic_TelemetrySensorType_SCD4X); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::SCD4X, meshtastic_TelemetrySensorType_SCD4X);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::TSL2561, meshtastic_TelemetrySensorType_TSL2561);
i2cScanner.reset();
#endif #endif
#ifdef HAS_SDCARD #ifdef HAS_SDCARD
@@ -940,12 +964,6 @@ void setup()
// Now that the mesh service is created, create any modules // Now that the mesh service is created, create any modules
setupModules(); setupModules();
#if !MESHTASTIC_EXCLUDE_I2C
// Inform modules about I2C devices
ScanI2CCompleted(i2cScanner.get());
i2cScanner.reset();
#endif
// warn the user about a low entropy key // warn the user about a low entropy key
if (nodeDB->keyIsLowEntropy && !nodeDB->hasWarned) { if (nodeDB->keyIsLowEntropy && !nodeDB->hasWarned) {
LOG_WARN(LOW_ENTROPY_WARNING); LOG_WARN(LOW_ENTROPY_WARNING);

View File

@@ -218,7 +218,6 @@ template <typename T> void LR11x0Interface<T>::addReceiveMetadata(meshtastic_Mes
// LOG_DEBUG("PacketStatus %x", lora.getPacketStatus()); // LOG_DEBUG("PacketStatus %x", lora.getPacketStatus());
mp->rx_snr = lora.getSNR(); mp->rx_snr = lora.getSNR();
mp->rx_rssi = lround(lora.getRSSI()); mp->rx_rssi = lround(lora.getRSSI());
LOG_DEBUG("Corrected frequency offset: %f", lora.getFrequencyError());
} }
/** We override to turn on transmitter power as needed. /** We override to turn on transmitter power as needed.

View File

@@ -1874,13 +1874,6 @@ uint8_t NodeDB::getMeshNodeChannel(NodeNum n)
return info->channel; return info->channel;
} }
std::string NodeDB::getNodeId() const
{
char nodeId[16];
snprintf(nodeId, sizeof(nodeId), "!%08x", myNodeInfo.my_node_num);
return std::string(nodeId);
}
/// Find a node in our DB, return null for missing /// Find a node in our DB, return null for missing
/// NOTE: This function might be called from an ISR /// NOTE: This function might be called from an ISR
meshtastic_NodeInfoLite *NodeDB::getMeshNode(NodeNum n) meshtastic_NodeInfoLite *NodeDB::getMeshNode(NodeNum n)

View File

@@ -5,7 +5,6 @@
#include <algorithm> #include <algorithm>
#include <assert.h> #include <assert.h>
#include <pb_encode.h> #include <pb_encode.h>
#include <string>
#include <vector> #include <vector>
#include "MeshTypes.h" #include "MeshTypes.h"
@@ -204,9 +203,6 @@ class NodeDB
/// @return our node number /// @return our node number
NodeNum getNodeNum() { return myNodeInfo.my_node_num; } NodeNum getNodeNum() { return myNodeInfo.my_node_num; }
/// @return our node ID as a string in the format "!xxxxxxxx"
std::string getNodeId() const;
// @return last byte of a NodeNum, 0xFF if it ended at 0x00 // @return last byte of a NodeNum, 0xFF if it ended at 0x00
uint8_t getLastByteOfNodeNum(NodeNum num) { return (uint8_t)((num & 0xFF) ? (num & 0xFF) : 0xFF); } uint8_t getLastByteOfNodeNum(NodeNum num) { return (uint8_t)((num & 0xFF) ? (num & 0xFF) : 0xFF); }

View File

@@ -1,253 +0,0 @@
#include "PacketCache.h"
#include "Router.h"
PacketCache packetCache{};
/**
* Allocate a new cache entry and copy the packet header and payload into it
*/
PacketCacheEntry *PacketCache::cache(const meshtastic_MeshPacket *p, bool preserveMetadata)
{
size_t payload_size =
(p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag) ? p->encrypted.size : p->decoded.payload.size;
PacketCacheEntry *e = (PacketCacheEntry *)malloc(sizeof(PacketCacheEntry) + payload_size +
(preserveMetadata ? sizeof(PacketCacheMetadata) : 0));
if (!e) {
LOG_ERROR("Unable to allocate memory for packet cache entry");
return NULL;
}
*e = {};
e->header.from = p->from;
e->header.to = p->to;
e->header.id = p->id;
e->header.channel = p->channel;
e->header.next_hop = p->next_hop;
e->header.relay_node = p->relay_node;
e->header.flags = (p->hop_limit & PACKET_FLAGS_HOP_LIMIT_MASK) | (p->want_ack ? PACKET_FLAGS_WANT_ACK_MASK : 0) |
(p->via_mqtt ? PACKET_FLAGS_VIA_MQTT_MASK : 0) |
((p->hop_start << PACKET_FLAGS_HOP_START_SHIFT) & PACKET_FLAGS_HOP_START_MASK);
PacketCacheMetadata m{};
if (preserveMetadata) {
e->has_metadata = true;
m.rx_rssi = (uint8_t)(p->rx_rssi + 200);
m.rx_snr = (uint8_t)((p->rx_snr + 30.0f) / 0.25f);
m.rx_time = p->rx_time;
m.transport_mechanism = p->transport_mechanism;
m.priority = p->priority;
}
if (p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag) {
e->encrypted = true;
e->payload_len = p->encrypted.size;
memcpy(((unsigned char *)e) + sizeof(PacketCacheEntry), p->encrypted.bytes, p->encrypted.size);
} else if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
e->encrypted = false;
if (preserveMetadata) {
m.portnum = p->decoded.portnum;
m.want_response = p->decoded.want_response;
m.emoji = p->decoded.emoji;
m.bitfield = p->decoded.bitfield;
if (p->decoded.reply_id)
m.reply_id = p->decoded.reply_id;
else if (p->decoded.request_id)
m.request_id = p->decoded.request_id;
}
e->payload_len = p->decoded.payload.size;
memcpy(((unsigned char *)e) + sizeof(PacketCacheEntry), p->decoded.payload.bytes, p->decoded.payload.size);
} else {
LOG_ERROR("Unable to cache packet with unknown payload type %d", p->which_payload_variant);
free(e);
return NULL;
}
if (preserveMetadata)
memcpy(((unsigned char *)e) + sizeof(PacketCacheEntry) + e->payload_len, &m, sizeof(m));
size += sizeof(PacketCacheEntry) + e->payload_len + (e->has_metadata ? sizeof(PacketCacheMetadata) : 0);
insert(e);
return e;
};
/**
* Dump a list of packets into the provided buffer
*/
void PacketCache::dump(void *dest, const PacketCacheEntry **entries, size_t num_entries)
{
unsigned char *pos = (unsigned char *)dest;
for (size_t i = 0; i < num_entries; i++) {
size_t entry_len =
sizeof(PacketCacheEntry) + entries[i]->payload_len + (entries[i]->has_metadata ? sizeof(PacketCacheMetadata) : 0);
memcpy(pos, entries[i], entry_len);
pos += entry_len;
}
}
/**
* Calculate the length of buffer needed to dump the specified entries
*/
size_t PacketCache::dumpSize(const PacketCacheEntry **entries, size_t num_entries)
{
size_t total_size = 0;
for (size_t i = 0; i < num_entries; i++) {
total_size += sizeof(PacketCacheEntry) + entries[i]->payload_len;
if (entries[i]->has_metadata)
total_size += sizeof(PacketCacheMetadata);
}
return total_size;
}
/**
* Find a packet in the cache
*/
PacketCacheEntry *PacketCache::find(NodeNum from, PacketId id)
{
uint16_t h = PACKET_HASH(from, id);
PacketCacheEntry *e = buckets[PACKET_CACHE_BUCKET(h)];
while (e) {
if (e->header.from == from && e->header.id == id)
return e;
e = e->next;
}
return NULL;
}
/**
* Find a packet in the cache by its hash
*/
PacketCacheEntry *PacketCache::find(PacketHash h)
{
PacketCacheEntry *e = buckets[PACKET_CACHE_BUCKET(h)];
while (e) {
if (PACKET_HASH(e->header.from, e->header.id) == h)
return e;
e = e->next;
}
return NULL;
}
/**
* Load a list of packets from the provided buffer
*/
bool PacketCache::load(void *src, PacketCacheEntry **entries, size_t num_entries)
{
memset(entries, 0, sizeof(PacketCacheEntry *) * num_entries);
unsigned char *pos = (unsigned char *)src;
for (size_t i = 0; i < num_entries; i++) {
PacketCacheEntry e{};
memcpy(&e, pos, sizeof(PacketCacheEntry));
size_t entry_len = sizeof(PacketCacheEntry) + e.payload_len + (e.has_metadata ? sizeof(PacketCacheMetadata) : 0);
entries[i] = (PacketCacheEntry *)malloc(entry_len);
size += entry_len;
if (!entries[i]) {
LOG_ERROR("Unable to allocate memory for packet cache entry");
for (size_t j = 0; j < i; j++) {
size -= sizeof(PacketCacheEntry) + entries[j]->payload_len +
(entries[j]->has_metadata ? sizeof(PacketCacheMetadata) : 0);
free(entries[j]);
entries[j] = NULL;
}
return false;
}
memcpy(entries[i], pos, entry_len);
pos += entry_len;
}
for (size_t i = 0; i < num_entries; i++)
insert(entries[i]);
return true;
}
/**
* Copy the cached packet into the provided MeshPacket structure
*/
void PacketCache::rehydrate(const PacketCacheEntry *e, meshtastic_MeshPacket *p)
{
if (!e || !p)
return;
*p = {};
p->from = e->header.from;
p->to = e->header.to;
p->id = e->header.id;
p->channel = e->header.channel;
p->next_hop = e->header.next_hop;
p->relay_node = e->header.relay_node;
p->hop_limit = e->header.flags & PACKET_FLAGS_HOP_LIMIT_MASK;
p->want_ack = !!(e->header.flags & PACKET_FLAGS_WANT_ACK_MASK);
p->via_mqtt = !!(e->header.flags & PACKET_FLAGS_VIA_MQTT_MASK);
p->hop_start = (e->header.flags & PACKET_FLAGS_HOP_START_MASK) >> PACKET_FLAGS_HOP_START_SHIFT;
p->which_payload_variant = e->encrypted ? meshtastic_MeshPacket_encrypted_tag : meshtastic_MeshPacket_decoded_tag;
unsigned char *payload = ((unsigned char *)e) + sizeof(PacketCacheEntry);
PacketCacheMetadata m{};
if (e->has_metadata) {
memcpy(&m, (payload + e->payload_len), sizeof(m));
p->rx_rssi = ((int)m.rx_rssi) - 200;
p->rx_snr = ((float)m.rx_snr * 0.25f) - 30.0f;
p->rx_time = m.rx_time;
p->transport_mechanism = (meshtastic_MeshPacket_TransportMechanism)m.transport_mechanism;
p->priority = (meshtastic_MeshPacket_Priority)m.priority;
}
if (e->encrypted) {
memcpy(p->encrypted.bytes, payload, e->payload_len);
p->encrypted.size = e->payload_len;
} else {
memcpy(p->decoded.payload.bytes, payload, e->payload_len);
p->decoded.payload.size = e->payload_len;
if (e->has_metadata) {
// Decrypted-only metadata
p->decoded.portnum = (meshtastic_PortNum)m.portnum;
p->decoded.want_response = m.want_response;
p->decoded.emoji = m.emoji;
p->decoded.bitfield = m.bitfield;
if (m.reply_id)
p->decoded.reply_id = m.reply_id;
else if (m.request_id)
p->decoded.request_id = m.request_id;
}
}
}
/**
* Release a cache entry
*/
void PacketCache::release(PacketCacheEntry *e)
{
if (!e)
return;
remove(e);
size -= sizeof(PacketCacheEntry) + e->payload_len + (e->has_metadata ? sizeof(PacketCacheMetadata) : 0);
free(e);
}
/**
* Insert a new entry into the hash table
*/
void PacketCache::insert(PacketCacheEntry *e)
{
assert(e);
PacketHash h = PACKET_HASH(e->header.from, e->header.id);
PacketCacheEntry **target = &buckets[PACKET_CACHE_BUCKET(h)];
e->next = *target;
*target = e;
num_entries++;
}
/**
* Remove an entry from the hash table
*/
void PacketCache::remove(PacketCacheEntry *e)
{
assert(e);
PacketHash h = PACKET_HASH(e->header.from, e->header.id);
PacketCacheEntry **target = &buckets[PACKET_CACHE_BUCKET(h)];
while (*target) {
if (*target == e) {
*target = e->next;
e->next = NULL;
num_entries--;
break;
} else {
target = &(*target)->next;
}
}
}

View File

@@ -1,75 +0,0 @@
#pragma once
#include "RadioInterface.h"
#define PACKET_HASH(a, b) ((((a ^ b) >> 16) ^ (a ^ b)) & 0xFFFF) // 16 bit fold of packet (from, id) tuple
typedef uint16_t PacketHash;
#define PACKET_CACHE_BUCKETS 64 // Number of hash table buckets
#define PACKET_CACHE_BUCKET(h) (((h >> 12) ^ (h >> 6) ^ h) & 0x3F) // Fold hash down to 6-bit bucket index
typedef struct PacketCacheEntry {
PacketCacheEntry *next;
PacketHeader header;
uint16_t payload_len = 0;
union {
uint16_t bitfield;
struct {
uint8_t encrypted : 1; // Payload is encrypted
uint8_t has_metadata : 1; // Payload includes PacketCacheMetadata
uint8_t : 6; // Reserved for future use
uint16_t : 8; // Reserved for future use
};
};
} PacketCacheEntry;
typedef struct PacketCacheMetadata {
PacketCacheMetadata() : _bitfield(0), reply_id(0), _bitfield2(0) {}
union {
uint32_t _bitfield;
struct {
uint16_t portnum : 9; // meshtastic_MeshPacket::decoded::portnum
uint16_t want_response : 1; // meshtastic_MeshPacket::decoded::want_response
uint16_t emoji : 1; // meshtastic_MeshPacket::decoded::emoji
uint16_t bitfield : 5; // meshtastic_MeshPacket::decoded::bitfield (truncated)
uint8_t rx_rssi : 8; // meshtastic_MeshPacket::rx_rssi (map via actual RSSI + 200)
uint8_t rx_snr : 8; // meshtastic_MeshPacket::rx_snr (map via (p->rx_snr + 30.0f) / 0.25f)
};
};
union {
uint32_t reply_id; // meshtastic_MeshPacket::decoded.reply_id
uint32_t request_id; // meshtastic_MeshPacket::decoded.request_id
};
uint32_t rx_time = 0; // meshtastic_MeshPacket::rx_time
uint8_t transport_mechanism = 0; // meshtastic_MeshPacket::transport_mechanism
struct {
uint8_t _bitfield2;
union {
uint8_t priority : 7; // meshtastic_MeshPacket::priority
uint8_t reserved : 1; // Reserved for future use
};
};
} PacketCacheMetadata;
class PacketCache
{
public:
PacketCacheEntry *cache(const meshtastic_MeshPacket *p, bool preserveMetadata);
static void dump(void *dest, const PacketCacheEntry **entries, size_t num_entries);
size_t dumpSize(const PacketCacheEntry **entries, size_t num_entries);
PacketCacheEntry *find(NodeNum from, PacketId id);
PacketCacheEntry *find(PacketHash h);
bool load(void *src, PacketCacheEntry **entries, size_t num_entries);
size_t getNumEntries() { return num_entries; }
size_t getSize() { return size; }
void rehydrate(const PacketCacheEntry *e, meshtastic_MeshPacket *p);
void release(PacketCacheEntry *e);
private:
PacketCacheEntry *buckets[PACKET_CACHE_BUCKETS]{};
size_t num_entries = 0;
size_t size = 0;
void insert(PacketCacheEntry *e);
void remove(PacketCacheEntry *e);
};
extern PacketCache packetCache;

View File

@@ -72,7 +72,6 @@ void PhoneAPI::handleStartConfig()
LOG_INFO("Start API client config"); LOG_INFO("Start API client config");
nodeInfoForPhone.num = 0; // Don't keep returning old nodeinfos nodeInfoForPhone.num = 0; // Don't keep returning old nodeinfos
nodeInfoQueue.clear();
resetReadIndex(); resetReadIndex();
} }
@@ -95,7 +94,6 @@ void PhoneAPI::close()
fromRadioScratch = {}; fromRadioScratch = {};
toRadioScratch = {}; toRadioScratch = {};
nodeInfoForPhone = {}; nodeInfoForPhone = {};
nodeInfoQueue.clear();
packetForPhone = NULL; packetForPhone = NULL;
filesManifest.clear(); filesManifest.clear();
fromRadioNum = 0; fromRadioNum = 0;
@@ -434,24 +432,15 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
case STATE_SEND_OTHER_NODEINFOS: { case STATE_SEND_OTHER_NODEINFOS: {
LOG_DEBUG("Send known nodes"); LOG_DEBUG("Send known nodes");
if (nodeInfoForPhone.num == 0 && !nodeInfoQueue.empty()) {
// Serve the next cached node without re-reading from the DB iterator.
nodeInfoForPhone = nodeInfoQueue.front();
nodeInfoQueue.pop_front();
}
if (nodeInfoForPhone.num != 0) { if (nodeInfoForPhone.num != 0) {
// Just in case we stored a different user.id in the past, but should never happen going forward LOG_INFO("nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s", nodeInfoForPhone.num, nodeInfoForPhone.last_heard,
sprintf(nodeInfoForPhone.user.id, "!%08x", nodeInfoForPhone.num); nodeInfoForPhone.user.id, nodeInfoForPhone.user.long_name);
LOG_DEBUG("nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s", nodeInfoForPhone.num, nodeInfoForPhone.last_heard,
nodeInfoForPhone.user.id, nodeInfoForPhone.user.long_name);
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_node_info_tag; fromRadioScratch.which_payload_variant = meshtastic_FromRadio_node_info_tag;
fromRadioScratch.node_info = nodeInfoForPhone; fromRadioScratch.node_info = nodeInfoForPhone;
nodeInfoForPhone = {}; // Stay in current state until done sending nodeinfos
prefetchNodeInfos(); nodeInfoForPhone.num = 0; // We just consumed a nodeinfo, will need a new one next time
} else { } else {
LOG_DEBUG("Done sending nodeinfo"); LOG_DEBUG("Done sending nodeinfo");
nodeInfoQueue.clear();
state = STATE_SEND_FILEMANIFEST; state = STATE_SEND_FILEMANIFEST;
// Go ahead and send that ID right now // Go ahead and send that ID right now
return getFromRadio(buf); return getFromRadio(buf);
@@ -555,30 +544,6 @@ void PhoneAPI::releaseQueueStatusPhonePacket()
} }
} }
void PhoneAPI::prefetchNodeInfos()
{
bool added = false;
// Keep the queue topped up so BLE reads stay responsive even if DB fetches take a moment.
while (nodeInfoQueue.size() < kNodePrefetchDepth) {
auto nextNode = nodeDB->readNextMeshNode(readIndex);
if (!nextNode)
break;
auto info = TypeConversions::ConvertToNodeInfo(nextNode);
bool isUs = info.num == nodeDB->getNodeNum();
info.hops_away = isUs ? 0 : info.hops_away;
info.last_heard = isUs ? getValidTime(RTCQualityFromNet) : info.last_heard;
info.snr = isUs ? 0 : info.snr;
info.via_mqtt = isUs ? false : info.via_mqtt;
info.is_favorite = info.is_favorite || isUs;
nodeInfoQueue.push_back(info);
added = true;
}
if (added)
onNowHasData(0);
}
void PhoneAPI::releaseMqttClientProxyPhonePacket() void PhoneAPI::releaseMqttClientProxyPhonePacket()
{ {
if (mqttClientProxyMessageForPhone) { if (mqttClientProxyMessageForPhone) {
@@ -615,8 +580,19 @@ bool PhoneAPI::available()
return true; return true;
case STATE_SEND_OTHER_NODEINFOS: case STATE_SEND_OTHER_NODEINFOS:
if (nodeInfoQueue.empty()) if (nodeInfoForPhone.num == 0) {
prefetchNodeInfos(); auto nextNode = nodeDB->readNextMeshNode(readIndex);
if (nextNode) {
nodeInfoForPhone = TypeConversions::ConvertToNodeInfo(nextNode);
bool isUs = nodeInfoForPhone.num == nodeDB->getNodeNum();
nodeInfoForPhone.hops_away = isUs ? 0 : nodeInfoForPhone.hops_away;
nodeInfoForPhone.last_heard = isUs ? getValidTime(RTCQualityFromNet) : nodeInfoForPhone.last_heard;
nodeInfoForPhone.snr = isUs ? 0 : nodeInfoForPhone.snr;
nodeInfoForPhone.via_mqtt = isUs ? false : nodeInfoForPhone.via_mqtt;
nodeInfoForPhone.is_favorite = nodeInfoForPhone.is_favorite || isUs; // Our node is always a favorite
onNowHasData(0);
}
}
return true; // Always say we have something, because we might need to advance our state machine return true; // Always say we have something, because we might need to advance our state machine
case STATE_SEND_PACKETS: { case STATE_SEND_PACKETS: {
if (!queueStatusPacketForPhone) if (!queueStatusPacketForPhone)
@@ -756,7 +732,7 @@ int PhoneAPI::onNotify(uint32_t newValue)
LOG_INFO("Tell client we have new packets %u", newValue); LOG_INFO("Tell client we have new packets %u", newValue);
onNowHasData(newValue); onNowHasData(newValue);
} else { } else {
LOG_DEBUG("Client not yet interested in packets (state=%d)", state); LOG_DEBUG("(Client not yet interested in packets)");
} }
return timeout ? -1 : 0; // If we timed out, MeshService should stop iterating through observers as we just removed one return timeout ? -1 : 0; // If we timed out, MeshService should stop iterating through observers as we just removed one

View File

@@ -3,7 +3,6 @@
#include "Observer.h" #include "Observer.h"
#include "mesh-pb-constants.h" #include "mesh-pb-constants.h"
#include "meshtastic/portnums.pb.h" #include "meshtastic/portnums.pb.h"
#include <deque>
#include <iterator> #include <iterator>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
@@ -80,10 +79,6 @@ class PhoneAPI
/// We temporarily keep the nodeInfo here between the call to available and getFromRadio /// We temporarily keep the nodeInfo here between the call to available and getFromRadio
meshtastic_NodeInfo nodeInfoForPhone = meshtastic_NodeInfo_init_default; meshtastic_NodeInfo nodeInfoForPhone = meshtastic_NodeInfo_init_default;
// Prefetched node info entries ready for immediate transmission to the phone.
std::deque<meshtastic_NodeInfo> nodeInfoQueue;
// Tunable size of the node info cache so we can keep BLE reads non-blocking.
static constexpr size_t kNodePrefetchDepth = 4;
meshtastic_ToRadio toRadioScratch = { meshtastic_ToRadio toRadioScratch = {
0}; // this is a static scratch object, any data must be copied elsewhere before returning 0}; // this is a static scratch object, any data must be copied elsewhere before returning
@@ -163,8 +158,6 @@ class PhoneAPI
void releaseQueueStatusPhonePacket(); void releaseQueueStatusPhonePacket();
void prefetchNodeInfos();
void releaseMqttClientProxyPhonePacket(); void releaseMqttClientProxyPhonePacket();
void releaseClientNotification(); void releaseClientNotification();

View File

@@ -647,24 +647,23 @@ void RadioInterface::limitPower(int8_t loraMaxPower)
} }
#ifndef NUM_PA_POINTS #ifndef NUM_PA_POINTS
if (TX_GAIN_LORA > 0 && !devicestate.owner.is_licensed) { if (TX_GAIN_LORA > 0) {
LOG_INFO("Requested Tx power: %d dBm; Device LoRa Tx gain: %d dB", power, TX_GAIN_LORA); LOG_INFO("Requested Tx power: %d dBm; Device LoRa Tx gain: %d dB", power, TX_GAIN_LORA);
power -= TX_GAIN_LORA; power -= TX_GAIN_LORA;
} }
#else #else
if (!devicestate.owner.is_licensed) { // we have an array of PA gain values. Find the highest power setting that works.
// we have an array of PA gain values. Find the highest power setting that works. const uint16_t tx_gain[NUM_PA_POINTS] = {TX_GAIN_LORA};
const uint16_t tx_gain[NUM_PA_POINTS] = {TX_GAIN_LORA}; for (int radio_dbm = 0; radio_dbm < NUM_PA_POINTS; radio_dbm++) {
for (int radio_dbm = 0; radio_dbm < NUM_PA_POINTS; radio_dbm++) { if (((radio_dbm + tx_gain[radio_dbm]) > power) ||
if (((radio_dbm + tx_gain[radio_dbm]) > power) || ((radio_dbm == (NUM_PA_POINTS - 1)) && ((radio_dbm + tx_gain[radio_dbm]) <= power))) {
((radio_dbm == (NUM_PA_POINTS - 1)) && ((radio_dbm + tx_gain[radio_dbm]) <= power))) { // we've exceeded the power limit, or hit the max we can do
// we've exceeded the power limit, or hit the max we can do LOG_INFO("Requested Tx power: %d dBm; Device LoRa Tx gain: %d dB", power, tx_gain[radio_dbm]);
LOG_INFO("Requested Tx power: %d dBm; Device LoRa Tx gain: %d dB", power, tx_gain[radio_dbm]); power -= tx_gain[radio_dbm];
power -= tx_gain[radio_dbm]; break;
break;
}
} }
} }
#endif #endif
if (power > loraMaxPower) // Clamp power to maximum defined level if (power > loraMaxPower) // Clamp power to maximum defined level
power = loraMaxPower; power = loraMaxPower;

View File

@@ -266,7 +266,6 @@ template <typename T> void SX126xInterface<T>::addReceiveMetadata(meshtastic_Mes
// LOG_DEBUG("PacketStatus %x", lora.getPacketStatus()); // LOG_DEBUG("PacketStatus %x", lora.getPacketStatus());
mp->rx_snr = lora.getSNR(); mp->rx_snr = lora.getSNR();
mp->rx_rssi = lround(lora.getRSSI()); mp->rx_rssi = lround(lora.getRSSI());
LOG_DEBUG("Corrected frequency offset: %f", lora.getFrequencyError());
} }
/** We override to turn on transmitter power as needed. /** We override to turn on transmitter power as needed.

View File

@@ -204,7 +204,6 @@ template <typename T> void SX128xInterface<T>::addReceiveMetadata(meshtastic_Mes
// LOG_DEBUG("PacketStatus %x", lora.getPacketStatus()); // LOG_DEBUG("PacketStatus %x", lora.getPacketStatus());
mp->rx_snr = lora.getSNR(); mp->rx_snr = lora.getSNR();
mp->rx_rssi = lround(lora.getRSSI()); mp->rx_rssi = lround(lora.getRSSI());
LOG_DEBUG("Corrected frequency offset: %f", lora.getFrequencyError());
} }
/** We override to turn on transmitter power as needed. /** We override to turn on transmitter power as needed.

View File

@@ -55,7 +55,7 @@ extern const pb_msgdesc_t meshtastic_ChannelSet_msg;
/* Maximum encoded size of messages (where known) */ /* Maximum encoded size of messages (where known) */
#define MESHTASTIC_MESHTASTIC_APPONLY_PB_H_MAX_SIZE meshtastic_ChannelSet_size #define MESHTASTIC_MESHTASTIC_APPONLY_PB_H_MAX_SIZE meshtastic_ChannelSet_size
#define meshtastic_ChannelSet_size 679 #define meshtastic_ChannelSet_size 695
#ifdef __cplusplus #ifdef __cplusplus
} /* extern "C" */ } /* extern "C" */

View File

@@ -34,9 +34,9 @@ typedef enum _meshtastic_Channel_Role {
typedef struct _meshtastic_ModuleSettings { typedef struct _meshtastic_ModuleSettings {
/* Bits of precision for the location sent in position packets. */ /* Bits of precision for the location sent in position packets. */
uint32_t position_precision; uint32_t position_precision;
/* Controls whether or not the client / device should mute the current channel /* Controls whether or not the phone / clients should mute the current channel
Useful for noisy public channels you don't necessarily want to disable */ Useful for noisy public channels you don't necessarily want to disable */
bool is_muted; bool is_client_muted;
} meshtastic_ModuleSettings; } meshtastic_ModuleSettings;
typedef PB_BYTES_ARRAY_T(32) meshtastic_ChannelSettings_psk_t; typedef PB_BYTES_ARRAY_T(32) meshtastic_ChannelSettings_psk_t;
@@ -97,6 +97,8 @@ typedef struct _meshtastic_ChannelSettings {
/* Per-channel module settings. */ /* Per-channel module settings. */
bool has_module_settings; bool has_module_settings;
meshtastic_ModuleSettings module_settings; meshtastic_ModuleSettings module_settings;
/* Whether or not we should receive notifactions / alerts through this channel */
bool mute;
} meshtastic_ChannelSettings; } meshtastic_ChannelSettings;
/* A pair of a channel number, mode and the (sharable) settings for that channel */ /* A pair of a channel number, mode and the (sharable) settings for that channel */
@@ -128,16 +130,16 @@ extern "C" {
/* Initializer values for message structs */ /* Initializer values for message structs */
#define meshtastic_ChannelSettings_init_default {0, {0, {0}}, "", 0, 0, 0, false, meshtastic_ModuleSettings_init_default} #define meshtastic_ChannelSettings_init_default {0, {0, {0}}, "", 0, 0, 0, false, meshtastic_ModuleSettings_init_default, 0}
#define meshtastic_ModuleSettings_init_default {0, 0} #define meshtastic_ModuleSettings_init_default {0, 0}
#define meshtastic_Channel_init_default {0, false, meshtastic_ChannelSettings_init_default, _meshtastic_Channel_Role_MIN} #define meshtastic_Channel_init_default {0, false, meshtastic_ChannelSettings_init_default, _meshtastic_Channel_Role_MIN}
#define meshtastic_ChannelSettings_init_zero {0, {0, {0}}, "", 0, 0, 0, false, meshtastic_ModuleSettings_init_zero} #define meshtastic_ChannelSettings_init_zero {0, {0, {0}}, "", 0, 0, 0, false, meshtastic_ModuleSettings_init_zero, 0}
#define meshtastic_ModuleSettings_init_zero {0, 0} #define meshtastic_ModuleSettings_init_zero {0, 0}
#define meshtastic_Channel_init_zero {0, false, meshtastic_ChannelSettings_init_zero, _meshtastic_Channel_Role_MIN} #define meshtastic_Channel_init_zero {0, false, meshtastic_ChannelSettings_init_zero, _meshtastic_Channel_Role_MIN}
/* Field tags (for use in manual encoding/decoding) */ /* Field tags (for use in manual encoding/decoding) */
#define meshtastic_ModuleSettings_position_precision_tag 1 #define meshtastic_ModuleSettings_position_precision_tag 1
#define meshtastic_ModuleSettings_is_muted_tag 2 #define meshtastic_ModuleSettings_is_client_muted_tag 2
#define meshtastic_ChannelSettings_channel_num_tag 1 #define meshtastic_ChannelSettings_channel_num_tag 1
#define meshtastic_ChannelSettings_psk_tag 2 #define meshtastic_ChannelSettings_psk_tag 2
#define meshtastic_ChannelSettings_name_tag 3 #define meshtastic_ChannelSettings_name_tag 3
@@ -145,6 +147,7 @@ extern "C" {
#define meshtastic_ChannelSettings_uplink_enabled_tag 5 #define meshtastic_ChannelSettings_uplink_enabled_tag 5
#define meshtastic_ChannelSettings_downlink_enabled_tag 6 #define meshtastic_ChannelSettings_downlink_enabled_tag 6
#define meshtastic_ChannelSettings_module_settings_tag 7 #define meshtastic_ChannelSettings_module_settings_tag 7
#define meshtastic_ChannelSettings_mute_tag 8
#define meshtastic_Channel_index_tag 1 #define meshtastic_Channel_index_tag 1
#define meshtastic_Channel_settings_tag 2 #define meshtastic_Channel_settings_tag 2
#define meshtastic_Channel_role_tag 3 #define meshtastic_Channel_role_tag 3
@@ -157,14 +160,15 @@ X(a, STATIC, SINGULAR, STRING, name, 3) \
X(a, STATIC, SINGULAR, FIXED32, id, 4) \ X(a, STATIC, SINGULAR, FIXED32, id, 4) \
X(a, STATIC, SINGULAR, BOOL, uplink_enabled, 5) \ X(a, STATIC, SINGULAR, BOOL, uplink_enabled, 5) \
X(a, STATIC, SINGULAR, BOOL, downlink_enabled, 6) \ X(a, STATIC, SINGULAR, BOOL, downlink_enabled, 6) \
X(a, STATIC, OPTIONAL, MESSAGE, module_settings, 7) X(a, STATIC, OPTIONAL, MESSAGE, module_settings, 7) \
X(a, STATIC, SINGULAR, BOOL, mute, 8)
#define meshtastic_ChannelSettings_CALLBACK NULL #define meshtastic_ChannelSettings_CALLBACK NULL
#define meshtastic_ChannelSettings_DEFAULT NULL #define meshtastic_ChannelSettings_DEFAULT NULL
#define meshtastic_ChannelSettings_module_settings_MSGTYPE meshtastic_ModuleSettings #define meshtastic_ChannelSettings_module_settings_MSGTYPE meshtastic_ModuleSettings
#define meshtastic_ModuleSettings_FIELDLIST(X, a) \ #define meshtastic_ModuleSettings_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, position_precision, 1) \ X(a, STATIC, SINGULAR, UINT32, position_precision, 1) \
X(a, STATIC, SINGULAR, BOOL, is_muted, 2) X(a, STATIC, SINGULAR, BOOL, is_client_muted, 2)
#define meshtastic_ModuleSettings_CALLBACK NULL #define meshtastic_ModuleSettings_CALLBACK NULL
#define meshtastic_ModuleSettings_DEFAULT NULL #define meshtastic_ModuleSettings_DEFAULT NULL
@@ -187,8 +191,8 @@ extern const pb_msgdesc_t meshtastic_Channel_msg;
/* Maximum encoded size of messages (where known) */ /* Maximum encoded size of messages (where known) */
#define MESHTASTIC_MESHTASTIC_CHANNEL_PB_H_MAX_SIZE meshtastic_Channel_size #define MESHTASTIC_MESHTASTIC_CHANNEL_PB_H_MAX_SIZE meshtastic_Channel_size
#define meshtastic_ChannelSettings_size 72 #define meshtastic_ChannelSettings_size 74
#define meshtastic_Channel_size 87 #define meshtastic_Channel_size 89
#define meshtastic_ModuleSettings_size 8 #define meshtastic_ModuleSettings_size 8
#ifdef __cplusplus #ifdef __cplusplus

View File

@@ -360,8 +360,8 @@ extern const pb_msgdesc_t meshtastic_BackupPreferences_msg;
/* Maximum encoded size of messages (where known) */ /* Maximum encoded size of messages (where known) */
/* meshtastic_NodeDatabase_size depends on runtime parameters */ /* meshtastic_NodeDatabase_size depends on runtime parameters */
#define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_BackupPreferences_size #define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_BackupPreferences_size
#define meshtastic_BackupPreferences_size 2277 #define meshtastic_BackupPreferences_size 2293
#define meshtastic_ChannelFile_size 718 #define meshtastic_ChannelFile_size 734
#define meshtastic_DeviceState_size 1737 #define meshtastic_DeviceState_size 1737
#define meshtastic_NodeInfoLite_size 196 #define meshtastic_NodeInfoLite_size 196
#define meshtastic_PositionLite_size 28 #define meshtastic_PositionLite_size 28

View File

@@ -94,13 +94,11 @@ static void onNetworkConnected()
// ESPmDNS (ESP32) and SimpleMDNS (RP2040) have slightly different APIs for adding TXT records // ESPmDNS (ESP32) and SimpleMDNS (RP2040) have slightly different APIs for adding TXT records
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
MDNS.addServiceTxt("meshtastic", "tcp", "shortname", String(owner.short_name)); MDNS.addServiceTxt("meshtastic", "tcp", "shortname", String(owner.short_name));
MDNS.addServiceTxt("meshtastic", "tcp", "id", String(nodeDB->getNodeId().c_str())); MDNS.addServiceTxt("meshtastic", "tcp", "id", String(owner.id));
MDNS.addServiceTxt("meshtastic", "tcp", "pio_env", optstr(APP_ENV));
// ESP32 prints obtained IP address in WiFiEvent // ESP32 prints obtained IP address in WiFiEvent
#elif defined(ARCH_RP2040) #elif defined(ARCH_RP2040)
MDNS.addServiceTxt("meshtastic", "shortname", owner.short_name); MDNS.addServiceTxt("meshtastic", "shortname", owner.short_name);
MDNS.addServiceTxt("meshtastic", "id", nodeDB->getNodeId().c_str()); MDNS.addServiceTxt("meshtastic", "id", owner.id);
MDNS.addServiceTxt("meshtastic", "pio_env", optstr(APP_ENV));
LOG_INFO("Obtained IP address: %s", WiFi.localIP().toString().c_str()); LOG_INFO("Obtained IP address: %s", WiFi.localIP().toString().c_str());
#endif #endif
} }

View File

@@ -510,8 +510,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
} }
} }
if (moduleConfig.external_notification.alert_message && if (moduleConfig.external_notification.alert_message && !ch.settings.mute) {
(!ch.settings.has_module_settings || !ch.settings.module_settings.is_muted)) {
LOG_INFO("externalNotificationModule - Notification Module"); LOG_INFO("externalNotificationModule - Notification Module");
isNagging = true; isNagging = true;
setExternalState(0, true); setExternalState(0, true);
@@ -522,8 +521,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
} }
} }
if (moduleConfig.external_notification.alert_message_vibra && if (moduleConfig.external_notification.alert_message_vibra && !ch.settings.mute) {
(!ch.settings.has_module_settings || !ch.settings.module_settings.is_muted)) {
LOG_INFO("externalNotificationModule - Notification Module (Vibra)"); LOG_INFO("externalNotificationModule - Notification Module (Vibra)");
isNagging = true; isNagging = true;
setExternalState(1, true); setExternalState(1, true);
@@ -534,8 +532,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
} }
} }
if (moduleConfig.external_notification.alert_message_buzzer && if (moduleConfig.external_notification.alert_message_buzzer && !ch.settings.mute) {
(!ch.settings.has_module_settings || !ch.settings.module_settings.is_muted)) {
LOG_INFO("externalNotificationModule - Notification Module (Buzzer)"); LOG_INFO("externalNotificationModule - Notification Module (Buzzer)");
if (config.device.buzzer_mode != meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY || if (config.device.buzzer_mode != meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY ||
(!isBroadcast(mp.to) && isToUs(&mp))) { (!isBroadcast(mp.to) && isToUs(&mp))) {

View File

@@ -113,12 +113,8 @@ meshtastic_MeshPacket *NodeInfoModule::allocReply()
u.public_key.size = 0; u.public_key.size = 0;
} }
// FIXME: Clear the user.id field since it should be derived from node number on the receiving end // Clear the user.id field since it should be derived from node number on the receiving end
// u.id[0] = '\0'; u.id[0] = '\0';
// Ensure our user.id is derived correctly
strcpy(u.id, nodeDB->getNodeId().c_str());
LOG_INFO("Send owner %s/%s/%s", u.id, u.long_name, u.short_name); LOG_INFO("Send owner %s/%s/%s", u.id, u.long_name, u.short_name);
lastSentToMesh = millis(); lastSentToMesh = millis();
return allocDataProtobuf(u); return allocDataProtobuf(u);

View File

@@ -67,7 +67,7 @@ SerialModuleRadio *serialModuleRadio;
defined(ELECROW_ThinkNode_M5) || defined(HELTEC_MESH_SOLAR) || defined(T_ECHO_LITE) defined(ELECROW_ThinkNode_M5) || defined(HELTEC_MESH_SOLAR) || defined(T_ECHO_LITE)
SerialModule::SerialModule() : StreamAPI(&Serial), concurrency::OSThread("Serial") {} SerialModule::SerialModule() : StreamAPI(&Serial), concurrency::OSThread("Serial") {}
static Print *serialPrint = &Serial; static Print *serialPrint = &Serial;
#elif defined(CONFIG_IDF_TARGET_ESP32C6) || defined(RAK3172) || defined(EBYTE_E77_MBL) #elif defined(CONFIG_IDF_TARGET_ESP32C6) || defined(RAK3172)
SerialModule::SerialModule() : StreamAPI(&Serial1), concurrency::OSThread("Serial") {} SerialModule::SerialModule() : StreamAPI(&Serial1), concurrency::OSThread("Serial") {}
static Print *serialPrint = &Serial1; static Print *serialPrint = &Serial1;
#else #else

View File

@@ -26,8 +26,7 @@ int32_t DeviceTelemetryModule::runOnce()
Default::getConfiguredOrDefaultMsScaled(moduleConfig.telemetry.device_update_interval, Default::getConfiguredOrDefaultMsScaled(moduleConfig.telemetry.device_update_interval,
default_telemetry_broadcast_interval_secs, numOnlineNodes))) && default_telemetry_broadcast_interval_secs, numOnlineNodes))) &&
airTime->isTxAllowedChannelUtil(!isImpoliteRole) && airTime->isTxAllowedAirUtil() && airTime->isTxAllowedChannelUtil(!isImpoliteRole) && airTime->isTxAllowedAirUtil() &&
config.device.role != meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN && config.device.role != meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN) {
moduleConfig.telemetry.device_telemetry_enabled) {
sendTelemetry(); sendTelemetry();
lastSentToMesh = uptimeLastMs; lastSentToMesh = uptimeLastMs;
} else if (service->isToPhoneQueueEmpty()) { } else if (service->isToPhoneQueueEmpty()) {

View File

@@ -35,103 +35,175 @@ extern void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const c
} }
#if __has_include(<Adafruit_AHTX0.h>) #if __has_include(<Adafruit_AHTX0.h>)
#include "Sensor/AHT10.h" #include "Sensor/AHT10.h"
AHT10Sensor aht10Sensor;
#else
NullSensor aht10Sensor;
#endif #endif
#if __has_include(<Adafruit_BME280.h>) #if __has_include(<Adafruit_BME280.h>)
#include "Sensor/BME280Sensor.h" #include "Sensor/BME280Sensor.h"
BME280Sensor bme280Sensor;
#else
NullSensor bmp280Sensor;
#endif #endif
#if __has_include(<Adafruit_BMP085.h>) #if __has_include(<Adafruit_BMP085.h>)
#include "Sensor/BMP085Sensor.h" #include "Sensor/BMP085Sensor.h"
BMP085Sensor bmp085Sensor;
#else
NullSensor bmp085Sensor;
#endif #endif
#if __has_include(<Adafruit_BMP280.h>) #if __has_include(<Adafruit_BMP280.h>)
#include "Sensor/BMP280Sensor.h" #include "Sensor/BMP280Sensor.h"
BMP280Sensor bmp280Sensor;
#else
NullSensor bme280Sensor;
#endif #endif
#if __has_include(<Adafruit_LTR390.h>) #if __has_include(<Adafruit_LTR390.h>)
#include "Sensor/LTR390UVSensor.h" #include "Sensor/LTR390UVSensor.h"
LTR390UVSensor ltr390uvSensor;
#else
NullSensor ltr390uvSensor;
#endif #endif
#if __has_include(<bsec2.h>) #if __has_include(<bsec2.h>)
#include "Sensor/BME680Sensor.h" #include "Sensor/BME680Sensor.h"
BME680Sensor bme680Sensor;
#else
NullSensor bme680Sensor;
#endif #endif
#if __has_include(<Adafruit_DPS310.h>) #if __has_include(<Adafruit_DPS310.h>)
#include "Sensor/DPS310Sensor.h" #include "Sensor/DPS310Sensor.h"
DPS310Sensor dps310Sensor;
#else
NullSensor dps310Sensor;
#endif #endif
#if __has_include(<Adafruit_MCP9808.h>) #if __has_include(<Adafruit_MCP9808.h>)
#include "Sensor/MCP9808Sensor.h" #include "Sensor/MCP9808Sensor.h"
MCP9808Sensor mcp9808Sensor;
#else
NullSensor mcp9808Sensor;
#endif #endif
#if __has_include(<Adafruit_SHT31.h>) #if __has_include(<Adafruit_SHT31.h>)
#include "Sensor/SHT31Sensor.h" #include "Sensor/SHT31Sensor.h"
SHT31Sensor sht31Sensor;
#else
NullSensor sht31Sensor;
#endif #endif
#if __has_include(<Adafruit_LPS2X.h>) #if __has_include(<Adafruit_LPS2X.h>)
#include "Sensor/LPS22HBSensor.h" #include "Sensor/LPS22HBSensor.h"
LPS22HBSensor lps22hbSensor;
#else
NullSensor lps22hbSensor;
#endif #endif
#if __has_include(<Adafruit_SHTC3.h>) #if __has_include(<Adafruit_SHTC3.h>)
#include "Sensor/SHTC3Sensor.h" #include "Sensor/SHTC3Sensor.h"
SHTC3Sensor shtc3Sensor;
#else
NullSensor shtc3Sensor;
#endif #endif
#if __has_include("RAK12035_SoilMoisture.h") && defined(RAK_4631) && RAK_4631 == 1 #if __has_include("RAK12035_SoilMoisture.h") && defined(RAK_4631) && RAK_4631 == 1
#include "Sensor/RAK12035Sensor.h" #include "Sensor/RAK12035Sensor.h"
RAK12035Sensor rak12035Sensor;
#else
NullSensor rak12035Sensor;
#endif #endif
#if __has_include(<Adafruit_VEML7700.h>) #if __has_include(<Adafruit_VEML7700.h>)
#include "Sensor/VEML7700Sensor.h" #include "Sensor/VEML7700Sensor.h"
VEML7700Sensor veml7700Sensor;
#else
NullSensor veml7700Sensor;
#endif #endif
#if __has_include(<Adafruit_TSL2591.h>) #if __has_include(<Adafruit_TSL2591.h>)
#include "Sensor/TSL2591Sensor.h" #include "Sensor/TSL2591Sensor.h"
TSL2591Sensor tsl2591Sensor;
#else
NullSensor tsl2591Sensor;
#endif #endif
#if __has_include(<ClosedCube_OPT3001.h>) #if __has_include(<ClosedCube_OPT3001.h>)
#include "Sensor/OPT3001Sensor.h" #include "Sensor/OPT3001Sensor.h"
OPT3001Sensor opt3001Sensor;
#else
NullSensor opt3001Sensor;
#endif #endif
#if __has_include(<Adafruit_SHT4x.h>) #if __has_include(<Adafruit_SHT4x.h>)
#include "Sensor/SHT4XSensor.h" #include "Sensor/SHT4XSensor.h"
SHT4XSensor sht4xSensor;
#else
NullSensor sht4xSensor;
#endif #endif
#if __has_include(<SparkFun_MLX90632_Arduino_Library.h>) #if __has_include(<SparkFun_MLX90632_Arduino_Library.h>)
#include "Sensor/MLX90632Sensor.h" #include "Sensor/MLX90632Sensor.h"
MLX90632Sensor mlx90632Sensor;
#else
NullSensor mlx90632Sensor;
#endif #endif
#if __has_include(<DFRobot_LarkWeatherStation.h>) #if __has_include(<DFRobot_LarkWeatherStation.h>)
#include "Sensor/DFRobotLarkSensor.h" #include "Sensor/DFRobotLarkSensor.h"
DFRobotLarkSensor dfRobotLarkSensor;
#else
NullSensor dfRobotLarkSensor;
#endif #endif
#if __has_include(<DFRobot_RainfallSensor.h>) #if __has_include(<DFRobot_RainfallSensor.h>)
#include "Sensor/DFRobotGravitySensor.h" #include "Sensor/DFRobotGravitySensor.h"
DFRobotGravitySensor dfRobotGravitySensor;
#else
NullSensor dfRobotGravitySensor;
#endif #endif
#if __has_include(<SparkFun_Qwiic_Scale_NAU7802_Arduino_Library.h>) #if __has_include(<SparkFun_Qwiic_Scale_NAU7802_Arduino_Library.h>)
#include "Sensor/NAU7802Sensor.h" #include "Sensor/NAU7802Sensor.h"
NAU7802Sensor nau7802Sensor;
#else
NullSensor nau7802Sensor;
#endif #endif
#if __has_include(<Adafruit_BMP3XX.h>) #if __has_include(<Adafruit_BMP3XX.h>)
#include "Sensor/BMP3XXSensor.h" #include "Sensor/BMP3XXSensor.h"
BMP3XXSensor bmp3xxSensor;
#else
NullSensor bmp3xxSensor;
#endif #endif
#if __has_include(<Adafruit_PCT2075.h>) #if __has_include(<Adafruit_PCT2075.h>)
#include "Sensor/PCT2075Sensor.h" #include "Sensor/PCT2075Sensor.h"
PCT2075Sensor pct2075Sensor;
#else
NullSensor pct2075Sensor;
#endif #endif
RCWL9620Sensor rcwl9620Sensor;
CGRadSensSensor cgRadSens;
#endif #endif
#ifdef T1000X_SENSOR_EN #ifdef T1000X_SENSOR_EN
#include "Sensor/T1000xSensor.h" #include "Sensor/T1000xSensor.h"
T1000xSensor t1000xSensor;
#endif #endif
#ifdef SENSECAP_INDICATOR #ifdef SENSECAP_INDICATOR
#include "Sensor/IndicatorSensor.h" #include "Sensor/IndicatorSensor.h"
IndicatorSensor indicatorSensor;
#endif #endif
#if __has_include(<Adafruit_TSL2561_U.h>) #if __has_include(<Adafruit_TSL2561_U.h>)
#include "Sensor/TSL2561Sensor.h" #include "Sensor/TSL2561Sensor.h"
TSL2561Sensor tsl2561Sensor;
#else
NullSensor tsl2561Sensor;
#endif #endif
#define FAILED_STATE_SENSOR_READ_MULTIPLIER 10 #define FAILED_STATE_SENSOR_READ_MULTIPLIER 10
@@ -140,132 +212,6 @@ extern void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const c
#include "graphics/ScreenFonts.h" #include "graphics/ScreenFonts.h"
#include <Throttle.h> #include <Throttle.h>
#include <forward_list>
static std::forward_list<TelemetrySensor *> sensors;
template <typename T> void addSensor(ScanI2C *i2cScanner, ScanI2C::DeviceType type)
{
ScanI2C::FoundDevice dev = i2cScanner->find(type);
if (dev.type != ScanI2C::DeviceType::NONE || type == ScanI2C::DeviceType::NONE) {
TelemetrySensor *sensor = new T();
#if WIRE_INTERFACES_COUNT > 1
TwoWire *bus = ScanI2CTwoWire::fetchI2CBus(dev.address);
if (dev.address.port != ScanI2C::I2CPort::WIRE1 && sensor->onlyWire1()) {
// This sensor only works on Wire (Wire1 is not supported)
delete sensor;
return;
}
#else
TwoWire *bus = &Wire;
#endif
if (sensor->initDevice(bus, &dev)) {
sensors.push_front(sensor);
return;
}
// destroy sensor
delete sensor;
}
}
void EnvironmentTelemetryModule::i2cScanFinished(ScanI2C *i2cScanner)
{
if (!moduleConfig.telemetry.environment_measurement_enabled && !ENVIRONMENTAL_TELEMETRY_MODULE_ENABLE) {
return;
}
LOG_INFO("Environment Telemetry adding I2C devices...");
// order by priority of metrics/values (low top, high bottom)
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
#ifdef T1000X_SENSOR_EN
// Not a real I2C device
addSensor<T1000xSensor>(i2cScanner, ScanI2C::DeviceType::NONE);
#else
#ifdef SENSECAP_INDICATOR
// Not a real I2C device, uses UART
addSensor<IndicatorSensor>(i2cScanner, ScanI2C::DeviceType::NONE);
#endif
addSensor<RCWL9620Sensor>(i2cScanner, ScanI2C::DeviceType::RCWL9620);
addSensor<CGRadSensSensor>(i2cScanner, ScanI2C::DeviceType::CGRADSENS);
#endif
#endif
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR_EXTERNAL
#if __has_include(<DFRobot_LarkWeatherStation.h>)
addSensor<DFRobotLarkSensor>(i2cScanner, ScanI2C::DeviceType::DFROBOT_LARK);
#endif
#if __has_include(<DFRobot_RainfallSensor.h>)
addSensor<DFRobotGravitySensor>(i2cScanner, ScanI2C::DeviceType::DFROBOT_RAIN);
#endif
#if __has_include(<Adafruit_AHTX0.h>)
addSensor<AHT10Sensor>(i2cScanner, ScanI2C::DeviceType::AHT10);
#endif
#if __has_include(<Adafruit_BMP085.h>)
addSensor<BMP085Sensor>(i2cScanner, ScanI2C::DeviceType::BMP_085);
#endif
#if __has_include(<Adafruit_BME280.h>)
addSensor<BME280Sensor>(i2cScanner, ScanI2C::DeviceType::BME_280);
#endif
#if __has_include(<Adafruit_LTR390.h>)
addSensor<LTR390UVSensor>(i2cScanner, ScanI2C::DeviceType::LTR390UV);
#endif
#if __has_include(<bsec2.h>)
addSensor<BME680Sensor>(i2cScanner, ScanI2C::DeviceType::BME_680);
#endif
#if __has_include(<Adafruit_BMP280.h>)
addSensor<BMP280Sensor>(i2cScanner, ScanI2C::DeviceType::BMP_280);
#endif
#if __has_include(<Adafruit_DPS310.h>)
addSensor<DPS310Sensor>(i2cScanner, ScanI2C::DeviceType::DPS310);
#endif
#if __has_include(<Adafruit_MCP9808.h>)
addSensor<MCP9808Sensor>(i2cScanner, ScanI2C::DeviceType::MCP9808);
#endif
#if __has_include(<Adafruit_SHT31.h>)
addSensor<SHT31Sensor>(i2cScanner, ScanI2C::DeviceType::SHT31);
#endif
#if __has_include(<Adafruit_LPS2X.h>)
addSensor<LPS22HBSensor>(i2cScanner, ScanI2C::DeviceType::LPS22HB);
#endif
#if __has_include(<Adafruit_SHTC3.h>)
addSensor<SHTC3Sensor>(i2cScanner, ScanI2C::DeviceType::SHTC3);
#endif
#if __has_include("RAK12035_SoilMoisture.h") && defined(RAK_4631) && RAK_4631 == 1
addSensor<RAK12035Sensor>(i2cScanner, ScanI2C::DeviceType::RAK12035);
#endif
#if __has_include(<Adafruit_VEML7700.h>)
addSensor<VEML7700Sensor>(i2cScanner, ScanI2C::DeviceType::VEML7700);
#endif
#if __has_include(<Adafruit_TSL2591.h>)
addSensor<TSL2591Sensor>(i2cScanner, ScanI2C::DeviceType::TSL2591);
#endif
#if __has_include(<ClosedCube_OPT3001.h>)
addSensor<OPT3001Sensor>(i2cScanner, ScanI2C::DeviceType::OPT3001);
#endif
#if __has_include(<Adafruit_SHT4x.h>)
addSensor<SHT4XSensor>(i2cScanner, ScanI2C::DeviceType::SHT4X);
#endif
#if __has_include(<SparkFun_MLX90632_Arduino_Library.h>)
addSensor<MLX90632Sensor>(i2cScanner, ScanI2C::DeviceType::MLX90632);
#endif
#if __has_include(<Adafruit_BMP3XX.h>)
addSensor<BMP3XXSensor>(i2cScanner, ScanI2C::DeviceType::BMP_3XX);
#endif
#if __has_include(<Adafruit_PCT2075.h>)
addSensor<PCT2075Sensor>(i2cScanner, ScanI2C::DeviceType::PCT2075);
#endif
#if __has_include(<Adafruit_TSL2561_U.h>)
addSensor<TSL2561Sensor>(i2cScanner, ScanI2C::DeviceType::TSL2561);
#endif
#if __has_include(<SparkFun_Qwiic_Scale_NAU7802_Arduino_Library.h>)
addSensor<NAU7802Sensor>(i2cScanner, ScanI2C::DeviceType::NAU7802);
#endif
#endif
}
int32_t EnvironmentTelemetryModule::runOnce() int32_t EnvironmentTelemetryModule::runOnce()
{ {
if (sleepOnNextExecution == true) { if (sleepOnNextExecution == true) {
@@ -298,27 +244,81 @@ int32_t EnvironmentTelemetryModule::runOnce()
if (moduleConfig.telemetry.environment_measurement_enabled || ENVIRONMENTAL_TELEMETRY_MODULE_ENABLE) { if (moduleConfig.telemetry.environment_measurement_enabled || ENVIRONMENTAL_TELEMETRY_MODULE_ENABLE) {
LOG_INFO("Environment Telemetry: init"); LOG_INFO("Environment Telemetry: init");
#ifdef SENSECAP_INDICATOR
// check if we have at least one sensor result = indicatorSensor.runOnce();
if (!sensors.empty()) { #endif
result = DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
#ifdef T1000X_SENSOR_EN #ifdef T1000X_SENSOR_EN
result = t1000xSensor.runOnce();
#elif !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR_EXTERNAL #elif !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR_EXTERNAL
if (dfRobotLarkSensor.hasSensor())
result = dfRobotLarkSensor.runOnce();
if (dfRobotGravitySensor.hasSensor())
result = dfRobotGravitySensor.runOnce();
if (bmp085Sensor.hasSensor())
result = bmp085Sensor.runOnce();
#if __has_include(<Adafruit_BME280.h>)
if (bmp280Sensor.hasSensor())
result = bmp280Sensor.runOnce();
#endif
if (bme280Sensor.hasSensor())
result = bme280Sensor.runOnce();
if (ltr390uvSensor.hasSensor())
result = ltr390uvSensor.runOnce();
if (bmp3xxSensor.hasSensor())
result = bmp3xxSensor.runOnce();
if (bme680Sensor.hasSensor())
result = bme680Sensor.runOnce();
if (dps310Sensor.hasSensor())
result = dps310Sensor.runOnce();
if (mcp9808Sensor.hasSensor())
result = mcp9808Sensor.runOnce();
if (shtc3Sensor.hasSensor())
result = shtc3Sensor.runOnce();
if (lps22hbSensor.hasSensor())
result = lps22hbSensor.runOnce();
if (sht31Sensor.hasSensor())
result = sht31Sensor.runOnce();
if (sht4xSensor.hasSensor())
result = sht4xSensor.runOnce();
if (ina219Sensor.hasSensor()) if (ina219Sensor.hasSensor())
result = ina219Sensor.runOnce(); result = ina219Sensor.runOnce();
if (ina260Sensor.hasSensor()) if (ina260Sensor.hasSensor())
result = ina260Sensor.runOnce(); result = ina260Sensor.runOnce();
if (ina3221Sensor.hasSensor()) if (ina3221Sensor.hasSensor())
result = ina3221Sensor.runOnce(); result = ina3221Sensor.runOnce();
if (veml7700Sensor.hasSensor())
result = veml7700Sensor.runOnce();
if (tsl2591Sensor.hasSensor())
result = tsl2591Sensor.runOnce();
if (opt3001Sensor.hasSensor())
result = opt3001Sensor.runOnce();
if (rcwl9620Sensor.hasSensor())
result = rcwl9620Sensor.runOnce();
if (aht10Sensor.hasSensor())
result = aht10Sensor.runOnce();
if (mlx90632Sensor.hasSensor())
result = mlx90632Sensor.runOnce();
if (nau7802Sensor.hasSensor())
result = nau7802Sensor.runOnce();
if (max17048Sensor.hasSensor()) if (max17048Sensor.hasSensor())
result = max17048Sensor.runOnce(); result = max17048Sensor.runOnce();
if (cgRadSens.hasSensor())
result = cgRadSens.runOnce();
if (tsl2561Sensor.hasSensor())
result = tsl2561Sensor.runOnce();
if (pct2075Sensor.hasSensor())
result = pct2075Sensor.runOnce();
// this only works on the wismesh hub with the solar option. This is not an I2C sensor, so we don't need the // this only works on the wismesh hub with the solar option. This is not an I2C sensor, so we don't need the
// sensormap here. // sensormap here.
#ifdef HAS_RAKPROT #ifdef HAS_RAKPROT
result = rak9154Sensor.runOnce(); result = rak9154Sensor.runOnce();
#endif #endif
#if __has_include("RAK12035_SoilMoisture.h") && defined(RAK_4631) && RAK_4631 == 1
if (rak12035Sensor.hasSensor()) {
result = rak12035Sensor.runOnce();
}
#endif
#endif #endif
} }
// it's possible to have this module enabled, only for displaying values on the screen. // it's possible to have this module enabled, only for displaying values on the screen.
@@ -328,13 +328,11 @@ int32_t EnvironmentTelemetryModule::runOnce()
// if we somehow got to a second run of this module with measurement disabled, then just wait forever // if we somehow got to a second run of this module with measurement disabled, then just wait forever
if (!moduleConfig.telemetry.environment_measurement_enabled && !ENVIRONMENTAL_TELEMETRY_MODULE_ENABLE) { if (!moduleConfig.telemetry.environment_measurement_enabled && !ENVIRONMENTAL_TELEMETRY_MODULE_ENABLE) {
return disable(); return disable();
} } else {
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR_EXTERNAL
for (TelemetrySensor *sensor : sensors) { if (bme680Sensor.hasSensor())
uint32_t delay = sensor->runOnce(); result = bme680Sensor.runTrigger();
if (delay < result) { #endif
result = delay;
}
} }
if (((lastSentToMesh == 0) || if (((lastSentToMesh == 0) ||
@@ -552,12 +550,72 @@ bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m
m->which_variant = meshtastic_Telemetry_environment_metrics_tag; m->which_variant = meshtastic_Telemetry_environment_metrics_tag;
m->variant.environment_metrics = meshtastic_EnvironmentMetrics_init_zero; m->variant.environment_metrics = meshtastic_EnvironmentMetrics_init_zero;
for (TelemetrySensor *sensor : sensors) { #ifdef SENSECAP_INDICATOR
valid = valid && sensor->getMetrics(m); valid = valid && indicatorSensor.getMetrics(m);
hasSensor = true;
#endif
#ifdef T1000X_SENSOR_EN // add by WayenWeng
valid = valid && t1000xSensor.getMetrics(m);
hasSensor = true;
#else
if (dfRobotLarkSensor.hasSensor()) {
valid = valid && dfRobotLarkSensor.getMetrics(m);
hasSensor = true;
}
if (dfRobotGravitySensor.hasSensor()) {
valid = valid && dfRobotGravitySensor.getMetrics(m);
hasSensor = true;
}
if (sht31Sensor.hasSensor()) {
valid = valid && sht31Sensor.getMetrics(m);
hasSensor = true;
}
if (sht4xSensor.hasSensor()) {
valid = valid && sht4xSensor.getMetrics(m);
hasSensor = true;
}
if (lps22hbSensor.hasSensor()) {
valid = valid && lps22hbSensor.getMetrics(m);
hasSensor = true;
}
if (shtc3Sensor.hasSensor()) {
valid = valid && shtc3Sensor.getMetrics(m);
hasSensor = true;
}
if (bmp085Sensor.hasSensor()) {
valid = valid && bmp085Sensor.getMetrics(m);
hasSensor = true;
}
#if __has_include(<Adafruit_BME280.h>)
if (bmp280Sensor.hasSensor()) {
valid = valid && bmp280Sensor.getMetrics(m);
hasSensor = true;
}
#endif
if (bme280Sensor.hasSensor()) {
valid = valid && bme280Sensor.getMetrics(m);
hasSensor = true;
}
if (ltr390uvSensor.hasSensor()) {
valid = valid && ltr390uvSensor.getMetrics(m);
hasSensor = true;
}
if (bmp3xxSensor.hasSensor()) {
valid = valid && bmp3xxSensor.getMetrics(m);
hasSensor = true;
}
if (bme680Sensor.hasSensor()) {
valid = valid && bme680Sensor.getMetrics(m);
hasSensor = true;
}
if (dps310Sensor.hasSensor()) {
valid = valid && dps310Sensor.getMetrics(m);
hasSensor = true;
}
if (mcp9808Sensor.hasSensor()) {
valid = valid && mcp9808Sensor.getMetrics(m);
hasSensor = true; hasSensor = true;
} }
#ifndef T1000X_SENSOR_EN
if (ina219Sensor.hasSensor()) { if (ina219Sensor.hasSensor()) {
valid = valid && ina219Sensor.getMetrics(m); valid = valid && ina219Sensor.getMetrics(m);
hasSensor = true; hasSensor = true;
@@ -570,14 +628,78 @@ bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m
valid = valid && ina3221Sensor.getMetrics(m); valid = valid && ina3221Sensor.getMetrics(m);
hasSensor = true; hasSensor = true;
} }
if (veml7700Sensor.hasSensor()) {
valid = valid && veml7700Sensor.getMetrics(m);
hasSensor = true;
}
if (tsl2591Sensor.hasSensor()) {
valid = valid && tsl2591Sensor.getMetrics(m);
hasSensor = true;
}
if (opt3001Sensor.hasSensor()) {
valid = valid && opt3001Sensor.getMetrics(m);
hasSensor = true;
}
if (mlx90632Sensor.hasSensor()) {
valid = valid && mlx90632Sensor.getMetrics(m);
hasSensor = true;
}
if (rcwl9620Sensor.hasSensor()) {
valid = valid && rcwl9620Sensor.getMetrics(m);
hasSensor = true;
}
if (nau7802Sensor.hasSensor()) {
valid = valid && nau7802Sensor.getMetrics(m);
hasSensor = true;
}
if (tsl2561Sensor.hasSensor()) {
valid = valid && tsl2561Sensor.getMetrics(m);
hasSensor = true;
}
if (aht10Sensor.hasSensor()) {
if (!bmp280Sensor.hasSensor() && !bmp3xxSensor.hasSensor()) {
valid = valid && aht10Sensor.getMetrics(m);
hasSensor = true;
} else if (bmp280Sensor.hasSensor()) {
// prefer bmp280 temp if both sensors are present, fetch only humidity
meshtastic_Telemetry m_ahtx = meshtastic_Telemetry_init_zero;
LOG_INFO("AHTX0+BMP280 module detected: using temp from BMP280 and humy from AHTX0");
aht10Sensor.getMetrics(&m_ahtx);
m->variant.environment_metrics.relative_humidity = m_ahtx.variant.environment_metrics.relative_humidity;
m->variant.environment_metrics.has_relative_humidity = m_ahtx.variant.environment_metrics.has_relative_humidity;
} else {
// prefer bmp3xx temp if both sensors are present, fetch only humidity
meshtastic_Telemetry m_ahtx = meshtastic_Telemetry_init_zero;
LOG_INFO("AHTX0+BMP3XX module detected: using temp from BMP3XX and humy from AHTX0");
aht10Sensor.getMetrics(&m_ahtx);
m->variant.environment_metrics.relative_humidity = m_ahtx.variant.environment_metrics.relative_humidity;
m->variant.environment_metrics.has_relative_humidity = m_ahtx.variant.environment_metrics.has_relative_humidity;
}
}
if (max17048Sensor.hasSensor()) { if (max17048Sensor.hasSensor()) {
valid = valid && max17048Sensor.getMetrics(m); valid = valid && max17048Sensor.getMetrics(m);
hasSensor = true; hasSensor = true;
} }
#endif if (cgRadSens.hasSensor()) {
valid = valid && cgRadSens.getMetrics(m);
hasSensor = true;
}
if (pct2075Sensor.hasSensor()) {
valid = valid && pct2075Sensor.getMetrics(m);
hasSensor = true;
}
#ifdef HAS_RAKPROT #ifdef HAS_RAKPROT
valid = valid && rak9154Sensor.getMetrics(m); valid = valid && rak9154Sensor.getMetrics(m);
hasSensor = true; hasSensor = true;
#endif
#if __has_include("RAK12035_SoilMoisture.h") && defined(RAK_4631) && \
RAK_4631 == \
1 // Not really needed, but may as well just skip at a lower level it if no library or not a RAK_4631
if (rak12035Sensor.hasSensor()) {
valid = valid && rak12035Sensor.getMetrics(m);
hasSensor = true;
}
#endif
#endif #endif
return valid && hasSensor; return valid && hasSensor;
} }
@@ -615,8 +737,11 @@ bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero; meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
m.which_variant = meshtastic_Telemetry_environment_metrics_tag; m.which_variant = meshtastic_Telemetry_environment_metrics_tag;
m.time = getTime(); m.time = getTime();
#ifdef T1000X_SENSOR_EN
if (t1000xSensor.getMetrics(&m)) {
#else
if (getEnvironmentTelemetry(&m)) { if (getEnvironmentTelemetry(&m)) {
#endif
LOG_INFO("Send: barometric_pressure=%f, current=%f, gas_resistance=%f, relative_humidity=%f, temperature=%f", LOG_INFO("Send: barometric_pressure=%f, current=%f, gas_resistance=%f, relative_humidity=%f, temperature=%f",
m.variant.environment_metrics.barometric_pressure, m.variant.environment_metrics.current, m.variant.environment_metrics.barometric_pressure, m.variant.environment_metrics.current,
m.variant.environment_metrics.gas_resistance, m.variant.environment_metrics.relative_humidity, m.variant.environment_metrics.gas_resistance, m.variant.environment_metrics.relative_humidity,
@@ -678,13 +803,71 @@ AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule
{ {
AdminMessageHandleResult result = AdminMessageHandleResult::NOT_HANDLED; AdminMessageHandleResult result = AdminMessageHandleResult::NOT_HANDLED;
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR_EXTERNAL #if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR_EXTERNAL
if (dfRobotLarkSensor.hasSensor()) {
for (TelemetrySensor *sensor : sensors) { result = dfRobotLarkSensor.handleAdminMessage(mp, request, response);
result = sensor->handleAdminMessage(mp, request, response); if (result != AdminMessageHandleResult::NOT_HANDLED)
return result;
}
if (dfRobotGravitySensor.hasSensor()) {
result = dfRobotGravitySensor.handleAdminMessage(mp, request, response);
if (result != AdminMessageHandleResult::NOT_HANDLED)
return result;
}
if (sht31Sensor.hasSensor()) {
result = sht31Sensor.handleAdminMessage(mp, request, response);
if (result != AdminMessageHandleResult::NOT_HANDLED)
return result;
}
if (lps22hbSensor.hasSensor()) {
result = lps22hbSensor.handleAdminMessage(mp, request, response);
if (result != AdminMessageHandleResult::NOT_HANDLED)
return result;
}
if (shtc3Sensor.hasSensor()) {
result = shtc3Sensor.handleAdminMessage(mp, request, response);
if (result != AdminMessageHandleResult::NOT_HANDLED)
return result;
}
if (bmp085Sensor.hasSensor()) {
result = bmp085Sensor.handleAdminMessage(mp, request, response);
if (result != AdminMessageHandleResult::NOT_HANDLED)
return result;
}
if (bmp280Sensor.hasSensor()) {
result = bmp280Sensor.handleAdminMessage(mp, request, response);
if (result != AdminMessageHandleResult::NOT_HANDLED)
return result;
}
if (bme280Sensor.hasSensor()) {
result = bme280Sensor.handleAdminMessage(mp, request, response);
if (result != AdminMessageHandleResult::NOT_HANDLED)
return result;
}
if (ltr390uvSensor.hasSensor()) {
result = ltr390uvSensor.handleAdminMessage(mp, request, response);
if (result != AdminMessageHandleResult::NOT_HANDLED)
return result;
}
if (bmp3xxSensor.hasSensor()) {
result = bmp3xxSensor.handleAdminMessage(mp, request, response);
if (result != AdminMessageHandleResult::NOT_HANDLED)
return result;
}
if (bme680Sensor.hasSensor()) {
result = bme680Sensor.handleAdminMessage(mp, request, response);
if (result != AdminMessageHandleResult::NOT_HANDLED)
return result;
}
if (dps310Sensor.hasSensor()) {
result = dps310Sensor.handleAdminMessage(mp, request, response);
if (result != AdminMessageHandleResult::NOT_HANDLED)
return result;
}
if (mcp9808Sensor.hasSensor()) {
result = mcp9808Sensor.handleAdminMessage(mp, request, response);
if (result != AdminMessageHandleResult::NOT_HANDLED) if (result != AdminMessageHandleResult::NOT_HANDLED)
return result; return result;
} }
if (ina219Sensor.hasSensor()) { if (ina219Sensor.hasSensor()) {
result = ina219Sensor.handleAdminMessage(mp, request, response); result = ina219Sensor.handleAdminMessage(mp, request, response);
if (result != AdminMessageHandleResult::NOT_HANDLED) if (result != AdminMessageHandleResult::NOT_HANDLED)
@@ -700,11 +883,60 @@ AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule
if (result != AdminMessageHandleResult::NOT_HANDLED) if (result != AdminMessageHandleResult::NOT_HANDLED)
return result; return result;
} }
if (veml7700Sensor.hasSensor()) {
result = veml7700Sensor.handleAdminMessage(mp, request, response);
if (result != AdminMessageHandleResult::NOT_HANDLED)
return result;
}
if (tsl2591Sensor.hasSensor()) {
result = tsl2591Sensor.handleAdminMessage(mp, request, response);
if (result != AdminMessageHandleResult::NOT_HANDLED)
return result;
}
if (opt3001Sensor.hasSensor()) {
result = opt3001Sensor.handleAdminMessage(mp, request, response);
if (result != AdminMessageHandleResult::NOT_HANDLED)
return result;
}
if (mlx90632Sensor.hasSensor()) {
result = mlx90632Sensor.handleAdminMessage(mp, request, response);
if (result != AdminMessageHandleResult::NOT_HANDLED)
return result;
}
if (rcwl9620Sensor.hasSensor()) {
result = rcwl9620Sensor.handleAdminMessage(mp, request, response);
if (result != AdminMessageHandleResult::NOT_HANDLED)
return result;
}
if (nau7802Sensor.hasSensor()) {
result = nau7802Sensor.handleAdminMessage(mp, request, response);
if (result != AdminMessageHandleResult::NOT_HANDLED)
return result;
}
if (aht10Sensor.hasSensor()) {
result = aht10Sensor.handleAdminMessage(mp, request, response);
if (result != AdminMessageHandleResult::NOT_HANDLED)
return result;
}
if (max17048Sensor.hasSensor()) { if (max17048Sensor.hasSensor()) {
result = max17048Sensor.handleAdminMessage(mp, request, response); result = max17048Sensor.handleAdminMessage(mp, request, response);
if (result != AdminMessageHandleResult::NOT_HANDLED) if (result != AdminMessageHandleResult::NOT_HANDLED)
return result; return result;
} }
if (cgRadSens.hasSensor()) {
result = cgRadSens.handleAdminMessage(mp, request, response);
if (result != AdminMessageHandleResult::NOT_HANDLED)
return result;
}
#if __has_include("RAK12035_SoilMoisture.h") && defined(RAK_4631) && \
RAK_4631 == \
1 // Not really needed, but may as well just skip it at a lower level if no library or not a RAK_4631
if (rak12035Sensor.hasSensor()) {
result = rak12035Sensor.handleAdminMessage(mp, request, response);
if (result != AdminMessageHandleResult::NOT_HANDLED)
return result;
}
#endif
#endif #endif
return result; return result;
} }

View File

@@ -11,13 +11,10 @@
#include "../mesh/generated/meshtastic/telemetry.pb.h" #include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "ProtobufModule.h" #include "ProtobufModule.h"
#include "detect/ScanI2CConsumer.h"
#include <OLEDDisplay.h> #include <OLEDDisplay.h>
#include <OLEDDisplayUi.h> #include <OLEDDisplayUi.h>
class EnvironmentTelemetryModule : private concurrency::OSThread, class EnvironmentTelemetryModule : private concurrency::OSThread, public ProtobufModule<meshtastic_Telemetry>
public ScanI2CConsumer,
public ProtobufModule<meshtastic_Telemetry>
{ {
CallbackObserver<EnvironmentTelemetryModule, const meshtastic::Status *> nodeStatusObserver = CallbackObserver<EnvironmentTelemetryModule, const meshtastic::Status *> nodeStatusObserver =
CallbackObserver<EnvironmentTelemetryModule, const meshtastic::Status *>(this, CallbackObserver<EnvironmentTelemetryModule, const meshtastic::Status *>(this,
@@ -25,7 +22,7 @@ class EnvironmentTelemetryModule : private concurrency::OSThread,
public: public:
EnvironmentTelemetryModule() EnvironmentTelemetryModule()
: concurrency::OSThread("EnvironmentTelemetry"), ScanI2CConsumer(), : concurrency::OSThread("EnvironmentTelemetry"),
ProtobufModule("EnvironmentTelemetry", meshtastic_PortNum_TELEMETRY_APP, &meshtastic_Telemetry_msg) ProtobufModule("EnvironmentTelemetry", meshtastic_PortNum_TELEMETRY_APP, &meshtastic_Telemetry_msg)
{ {
lastMeasurementPacket = nullptr; lastMeasurementPacket = nullptr;
@@ -59,8 +56,6 @@ class EnvironmentTelemetryModule : private concurrency::OSThread,
meshtastic_AdminMessage *request, meshtastic_AdminMessage *request,
meshtastic_AdminMessage *response) override; meshtastic_AdminMessage *response) override;
void i2cScanFinished(ScanI2C *i2cScanner);
private: private:
bool firstTime = 1; bool firstTime = 1;
meshtastic_MeshPacket *lastMeasurementPacket; meshtastic_MeshPacket *lastMeasurementPacket;

View File

@@ -15,16 +15,20 @@
AHT10Sensor::AHT10Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_AHT10, "AHT10") {} AHT10Sensor::AHT10Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_AHT10, "AHT10") {}
bool AHT10Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) int32_t AHT10Sensor::runOnce()
{ {
LOG_INFO("Init sensor: %s", sensorName); LOG_INFO("Init sensor: %s", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
aht10 = Adafruit_AHTX0(); aht10 = Adafruit_AHTX0();
status = aht10.begin(bus, 0, dev->address.address); status = aht10.begin(nodeTelemetrySensorsMap[sensorType].second, 0, nodeTelemetrySensorsMap[sensorType].first);
initI2CSensor(); return initI2CSensor();
return status;
} }
void AHT10Sensor::setup() {}
bool AHT10Sensor::getMetrics(meshtastic_Telemetry *measurement) bool AHT10Sensor::getMetrics(meshtastic_Telemetry *measurement)
{ {
LOG_DEBUG("AHT10 getMetrics"); LOG_DEBUG("AHT10 getMetrics");
@@ -32,16 +36,11 @@ bool AHT10Sensor::getMetrics(meshtastic_Telemetry *measurement)
sensors_event_t humidity, temp; sensors_event_t humidity, temp;
aht10.getEvent(&humidity, &temp); aht10.getEvent(&humidity, &temp);
// prefer other sensors like bmp280, bmp3xx measurement->variant.environment_metrics.has_temperature = true;
if (!measurement->variant.environment_metrics.has_temperature) { measurement->variant.environment_metrics.has_relative_humidity = true;
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.temperature = temp.temperature;
}
if (!measurement->variant.environment_metrics.has_relative_humidity) { measurement->variant.environment_metrics.temperature = temp.temperature;
measurement->variant.environment_metrics.has_relative_humidity = true; measurement->variant.environment_metrics.relative_humidity = humidity.relative_humidity;
measurement->variant.environment_metrics.relative_humidity = humidity.relative_humidity;
}
return true; return true;
} }

View File

@@ -15,10 +15,13 @@ class AHT10Sensor : public TelemetrySensor
private: private:
Adafruit_AHTX0 aht10; Adafruit_AHTX0 aht10;
protected:
virtual void setup() override;
public: public:
AHT10Sensor(); AHT10Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
}; };
#endif #endif

View File

@@ -10,13 +10,13 @@
BME280Sensor::BME280Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BME280, "BME280") {} BME280Sensor::BME280Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BME280, "BME280") {}
bool BME280Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) int32_t BME280Sensor::runOnce()
{ {
LOG_INFO("Init sensor: %s", sensorName); LOG_INFO("Init sensor: %s", sensorName);
status = bme280.begin(dev->address.address, bus); if (!hasSensor()) {
if (!status) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
return status;
} }
status = bme280.begin(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second);
bme280.setSampling(Adafruit_BME280::MODE_FORCED, bme280.setSampling(Adafruit_BME280::MODE_FORCED,
Adafruit_BME280::SAMPLING_X1, // Temp. oversampling Adafruit_BME280::SAMPLING_X1, // Temp. oversampling
@@ -24,10 +24,11 @@ bool BME280Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
Adafruit_BME280::SAMPLING_X1, // Humidity oversampling Adafruit_BME280::SAMPLING_X1, // Humidity oversampling
Adafruit_BME280::FILTER_OFF, Adafruit_BME280::STANDBY_MS_1000); Adafruit_BME280::FILTER_OFF, Adafruit_BME280::STANDBY_MS_1000);
initI2CSensor(); return initI2CSensor();
return status;
} }
void BME280Sensor::setup() {}
bool BME280Sensor::getMetrics(meshtastic_Telemetry *measurement) bool BME280Sensor::getMetrics(meshtastic_Telemetry *measurement)
{ {
measurement->variant.environment_metrics.has_temperature = true; measurement->variant.environment_metrics.has_temperature = true;

View File

@@ -11,10 +11,13 @@ class BME280Sensor : public TelemetrySensor
private: private:
Adafruit_BME280 bme280; Adafruit_BME280 bme280;
protected:
virtual void setup() override;
public: public:
BME280Sensor(); BME280Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
}; };
#endif #endif

View File

@@ -10,7 +10,7 @@
BME680Sensor::BME680Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BME680, "BME680") {} BME680Sensor::BME680Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BME680, "BME680") {}
int32_t BME680Sensor::runOnce() int32_t BME680Sensor::runTrigger()
{ {
if (!bme680.run()) { if (!bme680.run()) {
checkStatus("runTrigger"); checkStatus("runTrigger");
@@ -18,10 +18,13 @@ int32_t BME680Sensor::runOnce()
return 35; return 35;
} }
bool BME680Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) int32_t BME680Sensor::runOnce()
{ {
status = 0;
if (!bme680.begin(dev->address.address, *bus)) if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
if (!bme680.begin(nodeTelemetrySensorsMap[sensorType].first, *nodeTelemetrySensorsMap[sensorType].second))
checkStatus("begin"); checkStatus("begin");
if (bme680.status == BSEC_OK) { if (bme680.status == BSEC_OK) {
@@ -37,15 +40,17 @@ bool BME680Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
} }
LOG_INFO("Init sensor: %s with the BSEC Library version %d.%d.%d.%d ", sensorName, bme680.version.major, LOG_INFO("Init sensor: %s with the BSEC Library version %d.%d.%d.%d ", sensorName, bme680.version.major,
bme680.version.minor, bme680.version.major_bugfix, bme680.version.minor_bugfix); bme680.version.minor, bme680.version.major_bugfix, bme680.version.minor_bugfix);
} else {
status = 0;
} }
if (status == 0) if (status == 0)
LOG_DEBUG("BME680Sensor::runOnce: bme680.status %d", bme680.status); LOG_DEBUG("BME680Sensor::runOnce: bme680.status %d", bme680.status);
initI2CSensor(); return initI2CSensor();
return status;
} }
void BME680Sensor::setup() {}
bool BME680Sensor::getMetrics(meshtastic_Telemetry *measurement) bool BME680Sensor::getMetrics(meshtastic_Telemetry *measurement)
{ {
if (bme680.getData(BSEC_OUTPUT_RAW_PRESSURE).signal == 0) if (bme680.getData(BSEC_OUTPUT_RAW_PRESSURE).signal == 0)

View File

@@ -18,6 +18,7 @@ class BME680Sensor : public TelemetrySensor
Bsec2 bme680; Bsec2 bme680;
protected: protected:
virtual void setup() override;
const char *bsecConfigFileName = "/prefs/bsec.dat"; const char *bsecConfigFileName = "/prefs/bsec.dat";
uint8_t bsecState[BSEC_MAX_STATE_BLOB_SIZE] = {0}; uint8_t bsecState[BSEC_MAX_STATE_BLOB_SIZE] = {0};
uint8_t accuracy = 0; uint8_t accuracy = 0;
@@ -37,9 +38,9 @@ class BME680Sensor : public TelemetrySensor
public: public:
BME680Sensor(); BME680Sensor();
int32_t runTrigger();
virtual int32_t runOnce() override; virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
}; };
#endif #endif

View File

@@ -10,17 +10,20 @@
BMP085Sensor::BMP085Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BMP085, "BMP085") {} BMP085Sensor::BMP085Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BMP085, "BMP085") {}
bool BMP085Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) int32_t BMP085Sensor::runOnce()
{ {
LOG_INFO("Init sensor: %s", sensorName); LOG_INFO("Init sensor: %s", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
bmp085 = Adafruit_BMP085(); bmp085 = Adafruit_BMP085();
status = bmp085.begin(dev->address.address, bus); status = bmp085.begin(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second);
initI2CSensor(); return initI2CSensor();
return status;
} }
void BMP085Sensor::setup() {}
bool BMP085Sensor::getMetrics(meshtastic_Telemetry *measurement) bool BMP085Sensor::getMetrics(meshtastic_Telemetry *measurement)
{ {
measurement->variant.environment_metrics.has_temperature = true; measurement->variant.environment_metrics.has_temperature = true;

View File

@@ -11,10 +11,13 @@ class BMP085Sensor : public TelemetrySensor
private: private:
Adafruit_BMP085 bmp085; Adafruit_BMP085 bmp085;
protected:
virtual void setup() override;
public: public:
BMP085Sensor(); BMP085Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
}; };
#endif #endif

View File

@@ -10,25 +10,25 @@
BMP280Sensor::BMP280Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BMP280, "BMP280") {} BMP280Sensor::BMP280Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BMP280, "BMP280") {}
bool BMP280Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) int32_t BMP280Sensor::runOnce()
{ {
LOG_INFO("Init sensor: %s", sensorName); LOG_INFO("Init sensor: %s", sensorName);
if (!hasSensor()) {
bmp280 = Adafruit_BMP280(bus); return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
status = bmp280.begin(dev->address.address);
if (!status) {
return status;
} }
bmp280 = Adafruit_BMP280(nodeTelemetrySensorsMap[sensorType].second);
status = bmp280.begin(nodeTelemetrySensorsMap[sensorType].first);
bmp280.setSampling(Adafruit_BMP280::MODE_FORCED, bmp280.setSampling(Adafruit_BMP280::MODE_FORCED,
Adafruit_BMP280::SAMPLING_X1, // Temp. oversampling Adafruit_BMP280::SAMPLING_X1, // Temp. oversampling
Adafruit_BMP280::SAMPLING_X1, // Pressure oversampling Adafruit_BMP280::SAMPLING_X1, // Pressure oversampling
Adafruit_BMP280::FILTER_OFF, Adafruit_BMP280::STANDBY_MS_1000); Adafruit_BMP280::FILTER_OFF, Adafruit_BMP280::STANDBY_MS_1000);
initI2CSensor(); return initI2CSensor();
return status;
} }
void BMP280Sensor::setup() {}
bool BMP280Sensor::getMetrics(meshtastic_Telemetry *measurement) bool BMP280Sensor::getMetrics(meshtastic_Telemetry *measurement)
{ {
measurement->variant.environment_metrics.has_temperature = true; measurement->variant.environment_metrics.has_temperature = true;

View File

@@ -11,10 +11,13 @@ class BMP280Sensor : public TelemetrySensor
private: private:
Adafruit_BMP280 bmp280; Adafruit_BMP280 bmp280;
protected:
virtual void setup() override;
public: public:
BMP280Sensor(); BMP280Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
}; };
#endif #endif

View File

@@ -6,18 +6,20 @@
BMP3XXSensor::BMP3XXSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BMP3XX, "BMP3XX") {} BMP3XXSensor::BMP3XXSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BMP3XX, "BMP3XX") {}
bool BMP3XXSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) void BMP3XXSensor::setup() {}
int32_t BMP3XXSensor::runOnce()
{ {
LOG_INFO("Init sensor: %s", sensorName); LOG_INFO("Init sensor: %s", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
// Get a singleton instance and initialise the bmp3xx // Get a singleton instance and initialise the bmp3xx
if (bmp3xx == nullptr) { if (bmp3xx == nullptr) {
bmp3xx = BMP3XXSingleton::GetInstance(); bmp3xx = BMP3XXSingleton::GetInstance();
} }
status = bmp3xx->begin_I2C(dev->address.address, bus); status = bmp3xx->begin_I2C(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second);
if (!status) {
return status;
}
// set up oversampling and filter initialization // set up oversampling and filter initialization
bmp3xx->setTemperatureOversampling(BMP3_OVERSAMPLING_4X); bmp3xx->setTemperatureOversampling(BMP3_OVERSAMPLING_4X);
@@ -29,8 +31,7 @@ bool BMP3XXSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
bmp3xx->performReading(); bmp3xx->performReading();
} }
initI2CSensor(); return initI2CSensor();
return status;
} }
bool BMP3XXSensor::getMetrics(meshtastic_Telemetry *measurement) bool BMP3XXSensor::getMetrics(meshtastic_Telemetry *measurement)

View File

@@ -43,11 +43,12 @@ class BMP3XXSensor : public TelemetrySensor
{ {
protected: protected:
BMP3XXSingleton *bmp3xx = nullptr; BMP3XXSingleton *bmp3xx = nullptr;
virtual void setup() override;
public: public:
BMP3XXSensor(); BMP3XXSensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
}; };
#endif #endif

View File

@@ -14,16 +14,22 @@
CGRadSensSensor::CGRadSensSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_RADSENS, "RadSens") {} CGRadSensSensor::CGRadSensSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_RADSENS, "RadSens") {}
bool CGRadSensSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) int32_t CGRadSensSensor::runOnce()
{ {
// Initialize the sensor following the same pattern as RCWL9620Sensor // Initialize the sensor following the same pattern as RCWL9620Sensor
LOG_INFO("Init sensor: %s", sensorName); LOG_INFO("Init sensor: %s", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
status = true; status = true;
begin(bus, dev->address.address); begin(nodeTelemetrySensorsMap[sensorType].second, nodeTelemetrySensorsMap[sensorType].first);
initI2CSensor();
return status; return initI2CSensor();
} }
void CGRadSensSensor::setup() {}
void CGRadSensSensor::begin(TwoWire *wire, uint8_t addr) void CGRadSensSensor::begin(TwoWire *wire, uint8_t addr)
{ {
// Store the Wire and address to the sensor following the same pattern as RCWL9620Sensor // Store the Wire and address to the sensor following the same pattern as RCWL9620Sensor

View File

@@ -17,13 +17,14 @@ class CGRadSensSensor : public TelemetrySensor
TwoWire *_wire = &Wire; TwoWire *_wire = &Wire;
protected: protected:
virtual void setup() override;
void begin(TwoWire *wire = &Wire, uint8_t addr = 0x66); void begin(TwoWire *wire = &Wire, uint8_t addr = 0x66);
float getStaticRadiation(); float getStaticRadiation();
public: public:
CGRadSensSensor(); CGRadSensSensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
}; };
#endif #endif

View File

@@ -10,39 +10,31 @@
DFRobotGravitySensor::DFRobotGravitySensor() : TelemetrySensor(meshtastic_TelemetrySensorType_DFROBOT_RAIN, "DFROBOT_RAIN") {} DFRobotGravitySensor::DFRobotGravitySensor() : TelemetrySensor(meshtastic_TelemetrySensorType_DFROBOT_RAIN, "DFROBOT_RAIN") {}
DFRobotGravitySensor::~DFRobotGravitySensor() int32_t DFRobotGravitySensor::runOnce()
{
if (gravity) {
delete gravity;
gravity = nullptr;
}
}
bool DFRobotGravitySensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{ {
LOG_INFO("Init sensor: %s", sensorName); LOG_INFO("Init sensor: %s", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
gravity = new DFRobot_RainfallSensor_I2C(bus); gravity = DFRobot_RainfallSensor_I2C(nodeTelemetrySensorsMap[sensorType].second);
status = gravity->begin(); status = gravity.begin();
LOG_DEBUG("%s VID: %x, PID: %x, Version: %s", sensorName, gravity->vid, gravity->pid, gravity->getFirmwareVersion().c_str()); return initI2CSensor();
}
initI2CSensor(); void DFRobotGravitySensor::setup()
return status; {
LOG_DEBUG("%s VID: %x, PID: %x, Version: %s", sensorName, gravity.vid, gravity.pid, gravity.getFirmwareVersion().c_str());
} }
bool DFRobotGravitySensor::getMetrics(meshtastic_Telemetry *measurement) bool DFRobotGravitySensor::getMetrics(meshtastic_Telemetry *measurement)
{ {
if (!gravity) {
LOG_ERROR("DFRobotGravitySensor not initialized");
return false;
}
measurement->variant.environment_metrics.has_rainfall_1h = true; measurement->variant.environment_metrics.has_rainfall_1h = true;
measurement->variant.environment_metrics.has_rainfall_24h = true; measurement->variant.environment_metrics.has_rainfall_24h = true;
measurement->variant.environment_metrics.rainfall_1h = gravity->getRainfall(1); measurement->variant.environment_metrics.rainfall_1h = gravity.getRainfall(1);
measurement->variant.environment_metrics.rainfall_24h = gravity->getRainfall(24); measurement->variant.environment_metrics.rainfall_24h = gravity.getRainfall(24);
LOG_INFO("Rain 1h: %f mm", measurement->variant.environment_metrics.rainfall_1h); LOG_INFO("Rain 1h: %f mm", measurement->variant.environment_metrics.rainfall_1h);
LOG_INFO("Rain 24h: %f mm", measurement->variant.environment_metrics.rainfall_24h); LOG_INFO("Rain 24h: %f mm", measurement->variant.environment_metrics.rainfall_24h);

View File

@@ -14,13 +14,15 @@
class DFRobotGravitySensor : public TelemetrySensor class DFRobotGravitySensor : public TelemetrySensor
{ {
private: private:
DFRobot_RainfallSensor_I2C *gravity = nullptr; DFRobot_RainfallSensor_I2C gravity = DFRobot_RainfallSensor_I2C(nodeTelemetrySensorsMap[sensorType].second);
protected:
virtual void setup() override;
public: public:
DFRobotGravitySensor(); DFRobotGravitySensor();
~DFRobotGravitySensor(); virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
}; };
#endif #endif

View File

@@ -11,10 +11,14 @@
DFRobotLarkSensor::DFRobotLarkSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_DFROBOT_LARK, "DFROBOT_LARK") {} DFRobotLarkSensor::DFRobotLarkSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_DFROBOT_LARK, "DFROBOT_LARK") {}
bool DFRobotLarkSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) int32_t DFRobotLarkSensor::runOnce()
{ {
LOG_INFO("Init sensor: %s", sensorName); LOG_INFO("Init sensor: %s", sensorName);
lark = DFRobot_LarkWeatherStation_I2C(dev->address.address, bus); if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
lark = DFRobot_LarkWeatherStation_I2C(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second);
if (lark.begin() == 0) // DFRobotLarkSensor init if (lark.begin() == 0) // DFRobotLarkSensor init
{ {
@@ -24,10 +28,11 @@ bool DFRobotLarkSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
LOG_ERROR("DFRobotLarkSensor Init Failed"); LOG_ERROR("DFRobotLarkSensor Init Failed");
status = false; status = false;
} }
initI2CSensor(); return initI2CSensor();
return status;
} }
void DFRobotLarkSensor::setup() {}
bool DFRobotLarkSensor::getMetrics(meshtastic_Telemetry *measurement) bool DFRobotLarkSensor::getMetrics(meshtastic_Telemetry *measurement)
{ {
measurement->variant.environment_metrics.has_temperature = true; measurement->variant.environment_metrics.has_temperature = true;

View File

@@ -16,10 +16,13 @@ class DFRobotLarkSensor : public TelemetrySensor
private: private:
DFRobot_LarkWeatherStation_I2C lark = DFRobot_LarkWeatherStation_I2C(); DFRobot_LarkWeatherStation_I2C lark = DFRobot_LarkWeatherStation_I2C();
protected:
virtual void setup() override;
public: public:
DFRobotLarkSensor(); DFRobotLarkSensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
}; };
#endif #endif

View File

@@ -9,22 +9,23 @@
DPS310Sensor::DPS310Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_DPS310, "DPS310") {} DPS310Sensor::DPS310Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_DPS310, "DPS310") {}
bool DPS310Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) int32_t DPS310Sensor::runOnce()
{ {
LOG_INFO("Init sensor: %s", sensorName); LOG_INFO("Init sensor: %s", sensorName);
status = dps310.begin_I2C(dev->address.address, bus); if (!hasSensor()) {
if (!status) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
return status;
} }
status = dps310.begin_I2C(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second);
dps310.configurePressure(DPS310_1HZ, DPS310_4SAMPLES); dps310.configurePressure(DPS310_1HZ, DPS310_4SAMPLES);
dps310.configureTemperature(DPS310_1HZ, DPS310_4SAMPLES); dps310.configureTemperature(DPS310_1HZ, DPS310_4SAMPLES);
dps310.setMode(DPS310_CONT_PRESTEMP); dps310.setMode(DPS310_CONT_PRESTEMP);
initI2CSensor(); return initI2CSensor();
return status;
} }
void DPS310Sensor::setup() {}
bool DPS310Sensor::getMetrics(meshtastic_Telemetry *measurement) bool DPS310Sensor::getMetrics(meshtastic_Telemetry *measurement)
{ {
sensors_event_t temp, press; sensors_event_t temp, press;

View File

@@ -11,10 +11,13 @@ class DPS310Sensor : public TelemetrySensor
private: private:
Adafruit_DPS310 dps310; Adafruit_DPS310 dps310;
protected:
virtual void setup() override;
public: public:
DPS310Sensor(); DPS310Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
}; };
#endif #endif

View File

@@ -61,11 +61,11 @@ static int cmd_send(uint8_t cmd, const char *p_data, uint8_t len)
return -1; return -1;
} }
bool IndicatorSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) int32_t IndicatorSensor::runOnce()
{ {
LOG_INFO("%s: init", sensorName); LOG_INFO("%s: init", sensorName);
setup(); setup();
return true; return 2 * DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; // give it some time to start up
} }
void IndicatorSensor::setup() void IndicatorSensor::setup()

View File

@@ -7,13 +7,13 @@
class IndicatorSensor : public TelemetrySensor class IndicatorSensor : public TelemetrySensor
{ {
protected:
virtual void setup() override;
public: public:
IndicatorSensor(); IndicatorSensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
private:
void setup();
}; };
#endif #endif

View File

@@ -10,17 +10,19 @@
LPS22HBSensor::LPS22HBSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_LPS22, "LPS22HB") {} LPS22HBSensor::LPS22HBSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_LPS22, "LPS22HB") {}
bool LPS22HBSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) int32_t LPS22HBSensor::runOnce()
{ {
LOG_INFO("Init sensor: %s", sensorName); LOG_INFO("Init sensor: %s", sensorName);
status = lps22hb.begin_I2C(dev->address.address, bus); if (!hasSensor()) {
if (!status) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
return status;
} }
lps22hb.setDataRate(LPS22_RATE_10_HZ); status = lps22hb.begin_I2C(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second);
return initI2CSensor();
}
initI2CSensor(); void LPS22HBSensor::setup()
return status; {
lps22hb.setDataRate(LPS22_RATE_10_HZ);
} }
bool LPS22HBSensor::getMetrics(meshtastic_Telemetry *measurement) bool LPS22HBSensor::getMetrics(meshtastic_Telemetry *measurement)

View File

@@ -12,10 +12,13 @@ class LPS22HBSensor : public TelemetrySensor
private: private:
Adafruit_LPS22 lps22hb; Adafruit_LPS22 lps22hb;
protected:
virtual void setup() override;
public: public:
LPS22HBSensor(); LPS22HBSensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
}; };
#endif #endif

View File

@@ -9,23 +9,23 @@
LTR390UVSensor::LTR390UVSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_LTR390UV, "LTR390UV") {} LTR390UVSensor::LTR390UVSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_LTR390UV, "LTR390UV") {}
bool LTR390UVSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) int32_t LTR390UVSensor::runOnce()
{ {
LOG_INFO("Init sensor: %s", sensorName); LOG_INFO("Init sensor: %s", sensorName);
if (!hasSensor()) {
status = ltr390uv.begin(bus); return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
if (!status) {
return status;
} }
status = ltr390uv.begin(nodeTelemetrySensorsMap[sensorType].second);
ltr390uv.setMode(LTR390_MODE_UVS); ltr390uv.setMode(LTR390_MODE_UVS);
ltr390uv.setGain(LTR390_GAIN_18); // Datasheet default ltr390uv.setGain(LTR390_GAIN_18); // Datasheet default
ltr390uv.setResolution(LTR390_RESOLUTION_20BIT); // Datasheet default ltr390uv.setResolution(LTR390_RESOLUTION_20BIT); // Datasheet default
initI2CSensor(); return initI2CSensor();
return status;
} }
void LTR390UVSensor::setup() {}
bool LTR390UVSensor::getMetrics(meshtastic_Telemetry *measurement) bool LTR390UVSensor::getMetrics(meshtastic_Telemetry *measurement)
{ {
LOG_DEBUG("LTR390UV getMetrics"); LOG_DEBUG("LTR390UV getMetrics");

View File

@@ -13,10 +13,13 @@ class LTR390UVSensor : public TelemetrySensor
float lastLuxReading = 0; float lastLuxReading = 0;
float lastUVReading = 0; float lastUVReading = 0;
protected:
virtual void setup() override;
public: public:
LTR390UVSensor(); LTR390UVSensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
}; };
#endif #endif

View File

@@ -9,17 +9,19 @@
MCP9808Sensor::MCP9808Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_MCP9808, "MCP9808") {} MCP9808Sensor::MCP9808Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_MCP9808, "MCP9808") {}
bool MCP9808Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) int32_t MCP9808Sensor::runOnce()
{ {
LOG_INFO("Init sensor: %s", sensorName); LOG_INFO("Init sensor: %s", sensorName);
status = mcp9808.begin(dev->address.address, bus); if (!hasSensor()) {
if (!status) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
return status;
} }
mcp9808.setResolution(2); status = mcp9808.begin(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second);
return initI2CSensor();
}
initI2CSensor(); void MCP9808Sensor::setup()
return status; {
mcp9808.setResolution(2);
} }
bool MCP9808Sensor::getMetrics(meshtastic_Telemetry *measurement) bool MCP9808Sensor::getMetrics(meshtastic_Telemetry *measurement)

View File

@@ -11,10 +11,13 @@ class MCP9808Sensor : public TelemetrySensor
private: private:
Adafruit_MCP9808 mcp9808; Adafruit_MCP9808 mcp9808;
protected:
virtual void setup() override;
public: public:
MCP9808Sensor(); MCP9808Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
}; };
#endif #endif

View File

@@ -8,12 +8,16 @@
MLX90632Sensor::MLX90632Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_MLX90632, "MLX90632") {} MLX90632Sensor::MLX90632Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_MLX90632, "MLX90632") {}
bool MLX90632Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) int32_t MLX90632Sensor::runOnce()
{ {
LOG_INFO("Init sensor: %s", sensorName); LOG_INFO("Init sensor: %s", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
MLX90632::status returnError; MLX90632::status returnError;
if (mlx.begin(dev->address.address, *bus, returnError) == true) // MLX90632 init if (mlx.begin(nodeTelemetrySensorsMap[sensorType].first, *nodeTelemetrySensorsMap[sensorType].second, returnError) ==
true) // MLX90632 init
{ {
LOG_DEBUG("MLX90632 Init Succeed"); LOG_DEBUG("MLX90632 Init Succeed");
status = true; status = true;
@@ -21,10 +25,11 @@ bool MLX90632Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
LOG_ERROR("MLX90632 Init Failed"); LOG_ERROR("MLX90632 Init Failed");
status = false; status = false;
} }
initI2CSensor(); return initI2CSensor();
return status;
} }
void MLX90632Sensor::setup() {}
bool MLX90632Sensor::getMetrics(meshtastic_Telemetry *measurement) bool MLX90632Sensor::getMetrics(meshtastic_Telemetry *measurement)
{ {
measurement->variant.environment_metrics.has_temperature = true; measurement->variant.environment_metrics.has_temperature = true;

View File

@@ -11,10 +11,13 @@ class MLX90632Sensor : public TelemetrySensor
private: private:
MLX90632 mlx = MLX90632(); MLX90632 mlx = MLX90632();
protected:
virtual void setup() override;
public: public:
MLX90632Sensor(); MLX90632Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
}; };
#endif #endif

View File

@@ -16,23 +16,24 @@ meshtastic_Nau7802Config nau7802config = meshtastic_Nau7802Config_init_zero;
NAU7802Sensor::NAU7802Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_NAU7802, "NAU7802") {} NAU7802Sensor::NAU7802Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_NAU7802, "NAU7802") {}
bool NAU7802Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) int32_t NAU7802Sensor::runOnce()
{ {
LOG_INFO("Init sensor: %s", sensorName); LOG_INFO("Init sensor: %s", sensorName);
status = nau7802.begin(*bus); if (!hasSensor()) {
if (!status) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
return status;
} }
status = nau7802.begin(*nodeTelemetrySensorsMap[sensorType].second);
nau7802.setSampleRate(NAU7802_SPS_320); nau7802.setSampleRate(NAU7802_SPS_320);
if (!loadCalibrationData()) { if (!loadCalibrationData()) {
LOG_ERROR("Failed to load calibration data"); LOG_ERROR("Failed to load calibration data");
} }
nau7802.calibrateAFE(); nau7802.calibrateAFE();
LOG_INFO("Offset: %d, Calibration factor: %.2f", nau7802.getZeroOffset(), nau7802.getCalibrationFactor()); LOG_INFO("Offset: %d, Calibration factor: %.2f", nau7802.getZeroOffset(), nau7802.getCalibrationFactor());
initI2CSensor(); return initI2CSensor();
return status;
} }
void NAU7802Sensor::setup() {}
bool NAU7802Sensor::getMetrics(meshtastic_Telemetry *measurement) bool NAU7802Sensor::getMetrics(meshtastic_Telemetry *measurement)
{ {
LOG_DEBUG("NAU7802 getMetrics"); LOG_DEBUG("NAU7802 getMetrics");

View File

@@ -13,14 +13,15 @@ class NAU7802Sensor : public TelemetrySensor
NAU7802 nau7802; NAU7802 nau7802;
protected: protected:
virtual void setup() override;
const char *nau7802ConfigFileName = "/prefs/nau7802.dat"; const char *nau7802ConfigFileName = "/prefs/nau7802.dat";
bool saveCalibrationData(); bool saveCalibrationData();
bool loadCalibrationData(); bool loadCalibrationData();
public: public:
NAU7802Sensor(); NAU7802Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
void tare(); void tare();
void calibrate(float weight); void calibrate(float weight);
AdminMessageHandleResult handleAdminMessage(const meshtastic_MeshPacket &mp, meshtastic_AdminMessage *request, AdminMessageHandleResult handleAdminMessage(const meshtastic_MeshPacket &mp, meshtastic_AdminMessage *request,

View File

@@ -9,15 +9,20 @@
OPT3001Sensor::OPT3001Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_OPT3001, "OPT3001") {} OPT3001Sensor::OPT3001Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_OPT3001, "OPT3001") {}
bool OPT3001Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) int32_t OPT3001Sensor::runOnce()
{ {
LOG_INFO("Init sensor: %s", sensorName); LOG_INFO("Init sensor: %s", sensorName);
auto errorCode = opt3001.begin(dev->address.address); if (!hasSensor()) {
status = errorCode == NO_ERROR; return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
if (!status) {
return status;
} }
auto errorCode = opt3001.begin(nodeTelemetrySensorsMap[sensorType].first);
status = errorCode == NO_ERROR;
return initI2CSensor();
}
void OPT3001Sensor::setup()
{
OPT3001_Config newConfig; OPT3001_Config newConfig;
newConfig.RangeNumber = 0b1100; newConfig.RangeNumber = 0b1100;
@@ -29,10 +34,6 @@ bool OPT3001Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
if (errorConfig != NO_ERROR) { if (errorConfig != NO_ERROR) {
LOG_ERROR("OPT3001 configuration error #%d", errorConfig); LOG_ERROR("OPT3001 configuration error #%d", errorConfig);
} }
status = errorConfig == NO_ERROR;
initI2CSensor();
return status;
} }
bool OPT3001Sensor::getMetrics(meshtastic_Telemetry *measurement) bool OPT3001Sensor::getMetrics(meshtastic_Telemetry *measurement)

View File

@@ -12,13 +12,13 @@ class OPT3001Sensor : public TelemetrySensor
private: private:
ClosedCube_OPT3001 opt3001; ClosedCube_OPT3001 opt3001;
protected:
virtual void setup() override;
public: public:
OPT3001Sensor(); OPT3001Sensor();
#if WIRE_INTERFACES_COUNT > 1 virtual int32_t runOnce() override;
virtual bool onlyWire1() { return true; }
#endif
virtual bool getMetrics(meshtastic_Telemetry *measurement) override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
}; };
#endif #endif

View File

@@ -9,18 +9,24 @@
PCT2075Sensor::PCT2075Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_PCT2075, "PCT2075") {} PCT2075Sensor::PCT2075Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_PCT2075, "PCT2075") {}
bool PCT2075Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) int32_t PCT2075Sensor::runOnce()
{ {
LOG_INFO("Init sensor: %s", sensorName); LOG_INFO("Init sensor: %s", sensorName);
status = pct2075.begin(dev->address.address, bus); if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
initI2CSensor(); status = pct2075.begin(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second);
return status;
return initI2CSensor();
} }
void PCT2075Sensor::setup() {}
bool PCT2075Sensor::getMetrics(meshtastic_Telemetry *measurement) bool PCT2075Sensor::getMetrics(meshtastic_Telemetry *measurement)
{ {
measurement->variant.environment_metrics.has_temperature = true; measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.temperature = pct2075.getTemperature(); measurement->variant.environment_metrics.temperature = pct2075.getTemperature();
return true; return true;

View File

@@ -12,10 +12,13 @@ class PCT2075Sensor : public TelemetrySensor
private: private:
Adafruit_PCT2075 pct2075; Adafruit_PCT2075 pct2075;
protected:
virtual void setup() override;
public: public:
PCT2075Sensor(); PCT2075Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
}; };
#endif #endif

View File

@@ -6,12 +6,16 @@
RAK12035Sensor::RAK12035Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_RAK12035, "RAK12035") {} RAK12035Sensor::RAK12035Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_RAK12035, "RAK12035") {}
bool RAK12035Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) int32_t RAK12035Sensor::runOnce()
{ {
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
// TODO:: check for up to 2 additional sensors and start them if present. // TODO:: check for up to 2 additional sensors and start them if present.
sensor.set_sensor_addr(RAK120351_ADDR); sensor.set_sensor_addr(RAK120351_ADDR);
delay(100); delay(100);
sensor.begin(dev->address.address); sensor.begin(nodeTelemetrySensorsMap[sensorType].first);
// Get sensor firmware version // Get sensor firmware version
uint8_t data = 0; uint8_t data = 0;
@@ -27,13 +31,8 @@ bool RAK12035Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
LOG_ERROR("RAK12035Sensor Init Failed"); LOG_ERROR("RAK12035Sensor Init Failed");
status = false; status = false;
} }
if (!status) {
return status;
}
setup();
initI2CSensor(); return initI2CSensor();
return status;
} }
void RAK12035Sensor::setup() void RAK12035Sensor::setup()

View File

@@ -16,14 +16,13 @@ class RAK12035Sensor : public TelemetrySensor
{ {
private: private:
RAK12035 sensor; RAK12035 sensor;
void setup();
protected:
virtual void setup() override;
public: public:
RAK12035Sensor(); RAK12035Sensor();
#if WIRE_INTERFACES_COUNT > 1 virtual int32_t runOnce() override;
virtual bool onlyWire1() { return true; }
#endif
virtual bool getMetrics(meshtastic_Telemetry *measurement) override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
}; };
#endif #endif

View File

@@ -8,15 +8,19 @@
RCWL9620Sensor::RCWL9620Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_RCWL9620, "RCWL9620") {} RCWL9620Sensor::RCWL9620Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_RCWL9620, "RCWL9620") {}
bool RCWL9620Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) int32_t RCWL9620Sensor::runOnce()
{ {
LOG_INFO("Init sensor: %s", sensorName); LOG_INFO("Init sensor: %s", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
status = 1; status = 1;
begin(bus, dev->address.address); begin(nodeTelemetrySensorsMap[sensorType].second, nodeTelemetrySensorsMap[sensorType].first);
initI2CSensor(); return initI2CSensor();
return status;
} }
void RCWL9620Sensor::setup() {}
bool RCWL9620Sensor::getMetrics(meshtastic_Telemetry *measurement) bool RCWL9620Sensor::getMetrics(meshtastic_Telemetry *measurement)
{ {
measurement->variant.environment_metrics.has_distance = true; measurement->variant.environment_metrics.has_distance = true;

View File

@@ -16,13 +16,14 @@ class RCWL9620Sensor : public TelemetrySensor
uint32_t _speed = 200000UL; uint32_t _speed = 200000UL;
protected: protected:
virtual void setup() override;
void begin(TwoWire *wire = &Wire, uint8_t addr = 0x57, uint8_t sda = -1, uint8_t scl = -1, uint32_t speed = 200000UL); void begin(TwoWire *wire = &Wire, uint8_t addr = 0x57, uint8_t sda = -1, uint8_t scl = -1, uint32_t speed = 200000UL);
float getDistance(); float getDistance();
public: public:
RCWL9620Sensor(); RCWL9620Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
}; };
#endif #endif

View File

@@ -9,13 +9,20 @@
SHT31Sensor::SHT31Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SHT31, "SHT31") {} SHT31Sensor::SHT31Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SHT31, "SHT31") {}
bool SHT31Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) int32_t SHT31Sensor::runOnce()
{ {
LOG_INFO("Init sensor: %s", sensorName); LOG_INFO("Init sensor: %s", sensorName);
sht31 = Adafruit_SHT31(bus); if (!hasSensor()) {
status = sht31.begin(dev->address.address); return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
initI2CSensor(); }
return status; sht31 = Adafruit_SHT31(nodeTelemetrySensorsMap[sensorType].second);
status = sht31.begin(nodeTelemetrySensorsMap[sensorType].first);
return initI2CSensor();
}
void SHT31Sensor::setup()
{
// Set up oversampling and filter initialization
} }
bool SHT31Sensor::getMetrics(meshtastic_Telemetry *measurement) bool SHT31Sensor::getMetrics(meshtastic_Telemetry *measurement)

View File

@@ -11,10 +11,13 @@ class SHT31Sensor : public TelemetrySensor
private: private:
Adafruit_SHT31 sht31; Adafruit_SHT31 sht31;
protected:
virtual void setup() override;
public: public:
SHT31Sensor(); SHT31Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
}; };
#endif #endif

View File

@@ -9,16 +9,16 @@
SHT4XSensor::SHT4XSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SHT4X, "SHT4X") {} SHT4XSensor::SHT4XSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SHT4X, "SHT4X") {}
bool SHT4XSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) int32_t SHT4XSensor::runOnce()
{ {
LOG_INFO("Init sensor: %s", sensorName); LOG_INFO("Init sensor: %s", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
uint32_t serialNumber = 0; uint32_t serialNumber = 0;
status = sht4x.begin(bus); sht4x.begin(nodeTelemetrySensorsMap[sensorType].second);
if (!status) {
return status;
}
serialNumber = sht4x.readSerial(); serialNumber = sht4x.readSerial();
if (serialNumber != 0) { if (serialNumber != 0) {
@@ -29,8 +29,12 @@ bool SHT4XSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
status = 0; status = 0;
} }
initI2CSensor(); return initI2CSensor();
return status; }
void SHT4XSensor::setup()
{
// Set up oversampling and filter initialization
} }
bool SHT4XSensor::getMetrics(meshtastic_Telemetry *measurement) bool SHT4XSensor::getMetrics(meshtastic_Telemetry *measurement)

View File

@@ -11,10 +11,13 @@ class SHT4XSensor : public TelemetrySensor
private: private:
Adafruit_SHT4x sht4x = Adafruit_SHT4x(); Adafruit_SHT4x sht4x = Adafruit_SHT4x();
protected:
virtual void setup() override;
public: public:
SHT4XSensor(); SHT4XSensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
}; };
#endif #endif

View File

@@ -9,13 +9,19 @@
SHTC3Sensor::SHTC3Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SHTC3, "SHTC3") {} SHTC3Sensor::SHTC3Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SHTC3, "SHTC3") {}
bool SHTC3Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) int32_t SHTC3Sensor::runOnce()
{ {
LOG_INFO("Init sensor: %s", sensorName); LOG_INFO("Init sensor: %s", sensorName);
status = shtc3.begin(bus); if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
status = shtc3.begin(nodeTelemetrySensorsMap[sensorType].second);
return initI2CSensor();
}
initI2CSensor(); void SHTC3Sensor::setup()
return status; {
// Set up oversampling and filter initialization
} }
bool SHTC3Sensor::getMetrics(meshtastic_Telemetry *measurement) bool SHTC3Sensor::getMetrics(meshtastic_Telemetry *measurement)

Some files were not shown because too many files have changed in this diff Show More