diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index e45fd5d93..d83d052b0 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -13,16 +13,13 @@ }, "customizations": { "vscode": { - "extensions": [ - "ms-vscode.cpptools", - "platformio.platformio-ide", - ] + "extensions": ["ms-vscode.cpptools", "platformio.platformio-ide"] } }, // Use 'forwardPorts' to make a list of ports inside the container available locally. - "forwardPorts": [ 4403 ], + "forwardPorts": [4403], // Run commands to prepare the container for use - "postCreateCommand": ".devcontainer/setup.sh", + "postCreateCommand": ".devcontainer/setup.sh" } diff --git a/.devcontainer/setup.sh b/.devcontainer/setup.sh index 866a4a417..0b2665f84 100755 --- a/.devcontainer/setup.sh +++ b/.devcontainer/setup.sh @@ -1,3 +1,3 @@ #!/usr/bin/env sh -git submodule update --init \ No newline at end of file +git submodule update --init diff --git a/.github/ISSUE_TEMPLATE/Bug Report.yml b/.github/ISSUE_TEMPLATE/Bug Report.yml index b5ca0db40..f2d2f6507 100644 --- a/.github/ISSUE_TEMPLATE/Bug Report.yml +++ b/.github/ISSUE_TEMPLATE/Bug Report.yml @@ -49,10 +49,24 @@ body: - Heltec V3 - Heltec Wireless Paper - Heltec Wireless Tracker + - Heltec Mesh Node T114 + - Heltec Vision Master E213 + - Heltec Vision Master E290 + - Heltec Vision Master T190 + - Nano G1 + - Nano G1 Explorer + - Nano G2 Ultra - Raspberry Pi Pico (W) - Relay v1 - Relay v2 - Seeed Wio Tracker 1110 + - Seeed Card Tracker T1000-E + - Station G1 + - Station G2 + - unPhone + - CanaryOne + - Chatter + - Linux Native - DIY - Other validations: diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml index b027a36cc..b50ccac26 100644 --- a/.github/ISSUE_TEMPLATE/feature.yml +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -18,6 +18,7 @@ body: - ESP32 - RP2040 - Linux Native + - Cross-Platform - other validations: required: true diff --git a/.github/actions/build-variant/action.yml b/.github/actions/build-variant/action.yml new file mode 100644 index 000000000..80d2a56bb --- /dev/null +++ b/.github/actions/build-variant/action.yml @@ -0,0 +1,94 @@ +name: Setup Build Variant Composite Action +description: Variant build actions for Meshtastic Platform IO steps + +inputs: + board: + description: The board to build for + required: true + github_token: + description: GitHub token + required: true + build-script-path: + description: Path to the build script + required: true + remove-debug-flags: + description: A space separated list of files to remove debug flags from + required: false + default: "" + ota-firmware-source: + description: The OTA firmware file to pull + required: false + default: "" + ota-firmware-target: + description: The target path to store the OTA firmware file + required: false + default: "" + artifact-paths: + description: A newline separated list of paths to store as artifacts + required: false + default: "" + include-web-ui: + description: Include the web UI in the build + required: false + default: "false" + arch: + description: Processor arch name + required: true + default: "esp32" + +runs: + using: composite + steps: + - name: Build base + id: base + uses: ./.github/actions/setup-base + + - name: Pull web ui + if: inputs.include-web-ui == 'true' + uses: dsaltares/fetch-gh-release-asset@master + with: + repo: meshtastic/web + file: build.tar + target: build.tar + token: ${{ inputs.github_token }} + + - name: Unpack web ui + if: inputs.include-web-ui == 'true' + shell: bash + run: | + tar -xf build.tar -C data/static + rm build.tar + + - name: Remove debug flags for release + shell: bash + if: inputs.remove-debug-flags != '' + run: | + for INI_FILE in ${{ inputs.remove-debug-flags }}; do + sed -i '/DDEBUG_HEAP/d' ${INI_FILE} + done + + - name: Build ${{ inputs.board }} + shell: bash + run: ${{ inputs.build-script-path }} ${{ inputs.board }} + + - name: Pull OTA Firmware + if: inputs.ota-firmware-source != '' && inputs.ota-firmware-target != '' + uses: dsaltares/fetch-gh-release-asset@master + with: + repo: meshtastic/firmware-ota + file: ${{ inputs.ota-firmware-source }} + target: ${{ inputs.ota-firmware-target }} + token: ${{ inputs.github_token }} + + - name: Get release version string + shell: bash + run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT + id: version + + - name: Store binaries as an artifact + uses: actions/upload-artifact@v4 + with: + name: firmware-${{ inputs.arch }}-${{ inputs.board }}-${{ steps.version.outputs.version }}.zip + overwrite: true + path: | + ${{ inputs.artifact-paths }} diff --git a/.github/actions/setup-base/action.yml b/.github/actions/setup-base/action.yml index 929c1df38..c0f6c4e66 100644 --- a/.github/actions/setup-base/action.yml +++ b/.github/actions/setup-base/action.yml @@ -11,6 +11,11 @@ runs: ref: ${{github.event.pull_request.head.ref}} repository: ${{github.event.pull_request.head.repo.full_name}} + - name: Uncomment build epoch + shell: bash + run: | + sed -i 's/#-DBUILD_EPOCH=$UNIX_TIME/-DBUILD_EPOCH=$UNIX_TIME/' platformio.ini + - name: Install dependencies shell: bash run: | diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..616c16ce2 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,26 @@ +version: 2 +updates: + - package-ecosystem: docker + directory: devcontainer + schedule: + interval: daily + time: "05:00" # trunk-ignore(yamllint/quoted-strings): required by dependabot syntax check + timezone: US/Pacific + - package-ecosystem: docker + directory: / + schedule: + interval: daily + time: "05:00" # trunk-ignore(yamllint/quoted-strings): required by dependabot syntax check + timezone: US/Pacific + - package-ecosystem: gitsubmodule + directory: / + schedule: + interval: daily + time: "05:00" # trunk-ignore(yamllint/quoted-strings): required by dependabot syntax check + timezone: US/Pacific + - package-ecosystem: github-actions + directory: /.github/workflows + schedule: + interval: daily + time: "05:00" # trunk-ignore(yamllint/quoted-strings): required by dependabot syntax check + timezone: US/Pacific diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 512dea311..6ccb4a105 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,9 +1,9 @@ +### ❌ (Please delete all these tips and replace them with your text) ❌ + ## Thank you for sending in a pull request, here's some tips to get started! -(Please delete all these tips and replace with your text) - - Before starting on some new big chunk of code, it it is optional but highly recommended to open an issue first - to say "hey, I think this idea X should be implemented and I'm starting work on it. My general plan is Y, any feedback + to say "Hey, I think this idea X should be implemented and I'm starting work on it. My general plan is Y, any feedback is appreciated." This will allow other devs to potentially save you time by not accidentially duplicating work etc... - Please do not check in files that don't have real changes - Please do not reformat lines that you didn't have to change the code on @@ -12,3 +12,4 @@ - If your PR fixes a bug, mention "fixes #bugnum" somewhere in your pull request description. - If your other co-developers have comments on your PR please tweak as needed. - Please also enable "Allow edits by maintainers". +- If your PR gets accepted you can request a "Contributor" role in the Meshtastic Discord diff --git a/.github/workflows/build_esp32.yml b/.github/workflows/build_esp32.yml index 4cbb4c7a4..7d069e3db 100644 --- a/.github/workflows/build_esp32.yml +++ b/.github/workflows/build_esp32.yml @@ -12,52 +12,23 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Build base - id: base - uses: ./.github/actions/setup-base - - - name: Pull web ui - uses: dsaltares/fetch-gh-release-asset@master - with: - repo: meshtastic/web - file: build.tar - target: build.tar - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Unpack web ui - run: | - tar -xf build.tar -C data/static - rm build.tar - - - name: Remove debug flags for release - if: ${{ github.event_name == 'workflow_dispatch' }} - run: | - sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32.ini - sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32s2.ini - sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32s3.ini - sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32c3.ini - name: Build ESP32 - run: bin/build-esp32.sh ${{ inputs.board }} - - - name: Pull OTA Firmware - uses: dsaltares/fetch-gh-release-asset@master + id: build + uses: ./.github/actions/build-variant with: - repo: meshtastic/firmware-ota - file: firmware.bin - target: release/bleota.bin - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Get release version string - shell: bash - run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT - id: version - - - name: Store binaries as an artifact - uses: actions/upload-artifact@v4 - with: - name: firmware-${{ inputs.board }}-${{ steps.version.outputs.version }}.zip - overwrite: true - path: | + github_token: ${{ secrets.GITHUB_TOKEN }} + board: ${{ inputs.board }} + remove-debug-flags: >- + ./arch/esp32/esp32.ini + ./arch/esp32/esp32s2.ini + ./arch/esp32/esp32s3.ini + ./arch/esp32/esp32c3.ini + build-script-path: bin/build-esp32.sh + ota-firmware-source: firmware.bin + ota-firmware-target: release/bleota.bin + artifact-paths: | release/*.bin release/*.elf + include-web-ui: true + arch: esp32 diff --git a/.github/workflows/build_esp32_c3.yml b/.github/workflows/build_esp32_c3.yml index 07727d711..5234dbe81 100644 --- a/.github/workflows/build_esp32_c3.yml +++ b/.github/workflows/build_esp32_c3.yml @@ -14,50 +14,22 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Build base - id: base - uses: ./.github/actions/setup-base - - name: Pull web ui - uses: dsaltares/fetch-gh-release-asset@master + - name: Build ESP32-C3 + id: build + uses: ./.github/actions/build-variant with: - repo: meshtastic/web - file: build.tar - target: build.tar - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Unpack web ui - run: | - tar -xf build.tar -C data/static - rm build.tar - - name: Remove debug flags for release - if: ${{ github.event_name == 'workflow_dispatch' }} - run: | - sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32.ini - sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32s2.ini - sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32s3.ini - sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32c3.ini - - name: Build ESP32 - run: bin/build-esp32.sh ${{ inputs.board }} - - - name: Pull OTA Firmware - uses: dsaltares/fetch-gh-release-asset@master - with: - repo: meshtastic/firmware-ota - file: firmware-c3.bin - target: release/bleota-c3.bin - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Get release version string - shell: bash - run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT - id: version - - - name: Store binaries as an artifact - uses: actions/upload-artifact@v4 - with: - name: firmware-${{ inputs.board }}-${{ steps.version.outputs.version }}.zip - overwrite: true - path: | + github_token: ${{ secrets.GITHUB_TOKEN }} + board: ${{ inputs.board }} + remove-debug-flags: >- + ./arch/esp32/esp32.ini + ./arch/esp32/esp32s2.ini + ./arch/esp32/esp32s3.ini + ./arch/esp32/esp32c3.ini + build-script-path: bin/build-esp32.sh + ota-firmware-source: firmware-c3.bin + ota-firmware-target: release/bleota-c3.bin + artifact-paths: | release/*.bin release/*.elf + arch: esp32c3 diff --git a/.github/workflows/build_esp32_c6.yml b/.github/workflows/build_esp32_c6.yml new file mode 100644 index 000000000..66f2764a6 --- /dev/null +++ b/.github/workflows/build_esp32_c6.yml @@ -0,0 +1,36 @@ +name: Build ESP32-C6 + +on: + workflow_call: + inputs: + board: + required: true + type: string + +permissions: read-all + +jobs: + build-esp32-c6: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Build ESP32-C6 + id: build + uses: ./.github/actions/build-variant + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + board: ${{ inputs.board }} + remove-debug-flags: >- + ./arch/esp32/esp32.ini + ./arch/esp32/esp32s2.ini + ./arch/esp32/esp32s3.ini + ./arch/esp32/esp32c3.ini + ./arch/esp32/esp32c6.ini + build-script-path: bin/build-esp32.sh + ota-firmware-source: firmware-c3.bin + ota-firmware-target: release/bleota-c3.bin + artifact-paths: | + release/*.bin + release/*.elf + arch: esp32c6 diff --git a/.github/workflows/build_esp32_s3.yml b/.github/workflows/build_esp32_s3.yml index 10773833e..554b37cef 100644 --- a/.github/workflows/build_esp32_s3.yml +++ b/.github/workflows/build_esp32_s3.yml @@ -12,50 +12,23 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Build base - id: base - uses: ./.github/actions/setup-base - - name: Pull web ui - uses: dsaltares/fetch-gh-release-asset@master + - name: Build ESP32-S3 + id: build + uses: ./.github/actions/build-variant with: - repo: meshtastic/web - file: build.tar - target: build.tar - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Unpack web ui - run: | - tar -xf build.tar -C data/static - rm build.tar - - name: Remove debug flags for release - if: ${{ github.event_name == 'workflow_dispatch' }} - run: | - sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32.ini - sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32s2.ini - sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32s3.ini - sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32c3.ini - - name: Build ESP32 - run: bin/build-esp32.sh ${{ inputs.board }} - - - name: Pull OTA Firmware - uses: dsaltares/fetch-gh-release-asset@master - with: - repo: meshtastic/firmware-ota - file: firmware-s3.bin - target: release/bleota-s3.bin - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Get release version string - shell: bash - run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT - id: version - - - name: Store binaries as an artifact - uses: actions/upload-artifact@v4 - with: - name: firmware-${{ inputs.board }}-${{ steps.version.outputs.version }}.zip - overwrite: true - path: | + github_token: ${{ secrets.GITHUB_TOKEN }} + board: ${{ inputs.board }} + remove-debug-flags: >- + ./arch/esp32/esp32.ini + ./arch/esp32/esp32s2.ini + ./arch/esp32/esp32s3.ini + ./arch/esp32/esp32c3.ini + build-script-path: bin/build-esp32.sh + ota-firmware-source: firmware-s3.bin + ota-firmware-target: release/bleota-s3.bin + artifact-paths: | release/*.bin release/*.elf + include-web-ui: true + arch: esp32s3 diff --git a/.github/workflows/build_native.yml b/.github/workflows/build_native.yml index 51bef0c13..1fb44a717 100644 --- a/.github/workflows/build_native.yml +++ b/.github/workflows/build_native.yml @@ -67,7 +67,7 @@ jobs: - name: Docker build and push tagged versions if: ${{ github.event_name == 'workflow_dispatch' }} continue-on-error: true # FIXME: Failing docker login auth - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./Dockerfile @@ -77,7 +77,7 @@ jobs: - name: Docker build and push if: ${{ github.ref == 'refs/heads/master' && github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }} continue-on-error: true # FIXME: Failing docker login auth - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./Dockerfile diff --git a/.github/workflows/build_nrf52.yml b/.github/workflows/build_nrf52.yml index ac509a096..ce26838f2 100644 --- a/.github/workflows/build_nrf52.yml +++ b/.github/workflows/build_nrf52.yml @@ -12,24 +12,17 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Build base - id: base - uses: ./.github/actions/setup-base - name: Build NRF52 - run: bin/build-nrf52.sh ${{ inputs.board }} - - - name: Get release version string - run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT - id: version - - - name: Store binaries as an artifact - uses: actions/upload-artifact@v4 + id: build + uses: ./.github/actions/build-variant with: - name: firmware-${{ inputs.board }}-${{ steps.version.outputs.version }}.zip - overwrite: true - path: | + github_token: ${{ secrets.GITHUB_TOKEN }} + board: ${{ inputs.board }} + build-script-path: bin/build-nrf52.sh + artifact-paths: | release/*.hex release/*.uf2 release/*.elf release/*.zip + arch: nrf52840 diff --git a/.github/workflows/build_rpi2040.yml b/.github/workflows/build_rpi2040.yml index 6e258fe2a..492a1f010 100644 --- a/.github/workflows/build_rpi2040.yml +++ b/.github/workflows/build_rpi2040.yml @@ -12,22 +12,15 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Build base - id: base - uses: ./.github/actions/setup-base - name: Build Raspberry Pi 2040 - run: ./bin/build-rpi2040.sh ${{ inputs.board }} - - - name: Get release version string - run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT - id: version - - - name: Store binaries as an artifact - uses: actions/upload-artifact@v4 + id: build + uses: ./.github/actions/build-variant with: - name: firmware-${{ inputs.board }}-${{ steps.version.outputs.version }}.zip - overwrite: true - path: | + github_token: ${{ secrets.GITHUB_TOKEN }} + board: ${{ inputs.board }} + build-script-path: bin/build-rpi2040.sh + artifact-paths: | release/*.uf2 release/*.elf + arch: rp2040 diff --git a/.github/workflows/build_stm32.yml b/.github/workflows/build_stm32.yml index d13c52c8a..b463bab71 100644 --- a/.github/workflows/build_stm32.yml +++ b/.github/workflows/build_stm32.yml @@ -12,22 +12,16 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Build base - id: base - uses: ./.github/actions/setup-base - - name: Build STM32 - run: bin/build-stm32.sh ${{ inputs.board }} - - - name: Get release version string - run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT - id: version - - - name: Store binaries as an artifact - uses: actions/upload-artifact@v4 + - name: Build STM32WL + id: build + uses: ./.github/actions/build-variant with: - name: firmware-${{ inputs.board }}-${{ steps.version.outputs.version }}.zip - overwrite: true - path: | + github_token: ${{ secrets.GITHUB_TOKEN }} + board: ${{ inputs.board }} + build-script-path: bin/build-stm32.sh + artifact-paths: | release/*.hex release/*.bin + release/*.elf + arch: stm32 diff --git a/.github/workflows/generate-userprefs.yml b/.github/workflows/generate-userprefs.yml new file mode 100644 index 000000000..10dd1ff7d --- /dev/null +++ b/.github/workflows/generate-userprefs.yml @@ -0,0 +1,35 @@ +name: Generate UsersPrefs JSON manifest + +on: + push: + paths: + - userPrefs.h + branches: + - master + +jobs: + generate-userprefs: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Clang + run: sudo apt-get install -y clang + + - name: Install trunk + run: curl https://get.trunk.io -fsSL | bash + + - name: Generate userPrefs.jsom + run: python3 ./bin/build-userprefs-json.py + + - name: Trunk format json + run: trunk format userPrefs.json + + - name: Commit userPrefs.json + run: | + git config --global user.email "actions@github.com" + git config --global user.name "GitHub Actions" + git add userPrefs.json + git commit -m "Update userPrefs.json" + git push diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index 36125f72f..555d4d092 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -1,7 +1,7 @@ name: CI -#concurrency: -# group: ${{ github.ref }} -# cancel-in-progress: ${{ github.ref != 'refs/heads/master' }} +concurrency: + group: ci-${{ github.head_ref || github.run_id }} + cancel-in-progress: true on: # # Triggers the workflow on push but only for the master branch push: @@ -24,7 +24,7 @@ jobs: strategy: fail-fast: false matrix: - arch: [esp32, esp32s3, esp32c3, nrf52840, rp2040, stm32, check] + arch: [esp32, esp32s3, esp32c3, esp32c6, nrf52840, rp2040, stm32, check] runs-on: ubuntu-latest steps: - id: checkout @@ -32,13 +32,18 @@ jobs: name: Checkout base - id: jsonStep run: | - TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}}) - echo "$TARGETS" + if [[ "${{ github.head_ref }}" == "" ]]; then + TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}}) + else + TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}} quick) + fi + echo "Name: ${{ github.ref_name }} Base: ${{ github.base_ref }} Head: ${{ github.head_ref }} Ref: ${{ github.ref }} Targets: $TARGETS" echo "${{matrix.arch}}=$(jq -cn --argjson environments "$TARGETS" '{board: $environments}')" >> $GITHUB_OUTPUT outputs: esp32: ${{ steps.jsonStep.outputs.esp32 }} esp32s3: ${{ steps.jsonStep.outputs.esp32s3 }} esp32c3: ${{ steps.jsonStep.outputs.esp32c3 }} + esp32c6: ${{ steps.jsonStep.outputs.esp32c6 }} nrf52840: ${{ steps.jsonStep.outputs.nrf52840 }} rp2040: ${{ steps.jsonStep.outputs.rp2040 }} stm32: ${{ steps.jsonStep.outputs.stm32 }} @@ -51,6 +56,7 @@ jobs: matrix: ${{ fromJson(needs.setup.outputs.check) }} runs-on: ubuntu-latest + if: ${{ github.event_name != 'workflow_dispatch' }} steps: - uses: actions/checkout@v4 - name: Build base @@ -86,6 +92,15 @@ jobs: with: board: ${{ matrix.board }} + build-esp32-c6: + needs: setup + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.setup.outputs.esp32c6) }} + uses: ./.github/workflows/build_esp32_c6.yml + with: + board: ${{ matrix.board }} + build-nrf52: needs: setup strategy: @@ -119,11 +134,12 @@ jobs: package-raspbian-armv7l: uses: ./.github/workflows/package_raspbian_armv7l.yml - package-native: - uses: ./.github/workflows/package_amd64.yml + # package-native: + # uses: ./.github/workflows/package_amd64.yml after-checks: runs-on: ubuntu-latest + if: ${{ github.event_name != 'workflow_dispatch' }} needs: [check] steps: - name: Checkout code @@ -136,18 +152,20 @@ jobs: permissions: contents: write pull-requests: write + strategy: + fail-fast: false + matrix: + arch: [esp32, esp32s3, esp32c3, esp32c6, nrf52840, rp2040, stm32] runs-on: ubuntu-latest needs: [ build-esp32, build-esp32-s3, build-esp32-c3, + build-esp32-c6, build-nrf52, build-rpi2040, build-stm32, - package-raspbian, - package-raspbian-armv7l, - package-native, ] steps: - name: Checkout code @@ -159,6 +177,7 @@ jobs: - uses: actions/download-artifact@v4 with: path: ./ + pattern: firmware-${{matrix.arch}}-* merge-multiple: true - name: Display structure of downloaded files @@ -169,12 +188,12 @@ jobs: id: version - name: Move files up - run: mv -b -t ./ ./release/meshtasticd_linux_* ./bin/config-dist.yaml ./bin/device-*.sh ./bin/device-*.bat + run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat - name: Repackage in single firmware zip uses: actions/upload-artifact@v4 with: - name: firmware-${{ steps.version.outputs.version }} + name: firmware-${{matrix.arch}}-${{ steps.version.outputs.version }} overwrite: true path: | ./firmware-*.bin @@ -183,16 +202,14 @@ jobs: ./firmware-*-ota.zip ./device-*.sh ./device-*.bat - ./meshtasticd_linux_* - ./config-dist.yaml ./littlefs-*.bin ./bleota*bin ./Meshtastic_nRF52_factory_erase*.uf2 - retention-days: 90 + retention-days: 30 - uses: actions/download-artifact@v4 with: - name: firmware-${{ steps.version.outputs.version }} + name: firmware-${{matrix.arch}}-${{ steps.version.outputs.version }} merge-multiple: true path: ./output @@ -206,32 +223,34 @@ jobs: chmod +x ./output/device-update.sh - name: Zip firmware - run: zip -j -9 -r ./firmware-${{ steps.version.outputs.version }}.zip ./output + run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip ./output - name: Repackage in single elfs zip uses: actions/upload-artifact@v4 with: - name: debug-elfs-${{ steps.version.outputs.version }}.zip + name: debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip overwrite: true path: ./*.elf retention-days: 30 - - name: Create request artifacts - continue-on-error: true # FIXME: Why are we getting 502, but things still work? - if: ${{ github.event_name == 'pull_request_target' || github.event_name == 'pull_request' }} - uses: gavv/pull-request-artifacts@v2.1.0 + - uses: scruplelesswizard/comment-artifact@main + if: ${{ github.event_name == 'pull_request' }} with: - commit: ${{ (github.event.pull_request_target || github.event.pull_request).head.sha }} - repo-token: ${{ secrets.GITHUB_TOKEN }} - artifacts-token: ${{ secrets.ARTIFACTS_TOKEN }} - artifacts-repo: meshtastic/artifacts - artifacts-branch: device - artifacts: ./firmware-${{ steps.version.outputs.version }}.zip + name: firmware-${{matrix.arch}}-${{ steps.version.outputs.version }} + description: "Download firmware-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip. This artifact will be available for 90 days from creation" + github-token: ${{ secrets.GITHUB_TOKEN }} release-artifacts: runs-on: ubuntu-latest if: ${{ github.event_name == 'workflow_dispatch' }} - needs: [gather-artifacts, after-checks] + outputs: + upload_url: ${{ steps.create_release.outputs.upload_url }} + needs: [ + gather-artifacts, + package-raspbian, + package-raspbian-armv7l, + # package-native, + ] steps: - name: Checkout uses: actions/checkout@v4 @@ -245,42 +264,6 @@ jobs: run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT id: version - - uses: actions/download-artifact@v4 - with: - name: firmware-${{ steps.version.outputs.version }} - merge-multiple: true - path: ./output - - - uses: actions/download-artifact@v4 - with: - pattern: meshtasticd_${{ steps.version.outputs.version }}_*.deb - merge-multiple: true - path: ./output - - - name: Display structure of downloaded files - run: ls -R - - - name: Device scripts permissions - run: | - chmod +x ./output/device-install.sh - chmod +x ./output/device-update.sh - - - name: Zip firmware - run: zip -j -9 -r ./firmware-${{ steps.version.outputs.version }}.zip ./output -x *.deb - - - uses: actions/download-artifact@v4 - with: - name: debug-elfs-${{ steps.version.outputs.version }}.zip - merge-multiple: true - path: ./elfs - - - name: Zip Elfs - run: zip -j -9 -r ./debug-elfs-${{ steps.version.outputs.version }}.zip ./elfs - - # For diagnostics - - name: Show artifacts - run: ls -lR - - name: Create release uses: actions/create-release@v1 id: create_release @@ -294,25 +277,16 @@ jobs: env: GITHUB_TOKEN: ${{ github.token }} - - name: Add bins to release - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ github.token }} + - name: Download deb files + uses: actions/download-artifact@v4 with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./firmware-${{ steps.version.outputs.version }}.zip - asset_name: firmware-${{ steps.version.outputs.version }}.zip - asset_content_type: application/zip + pattern: meshtasticd_${{ steps.version.outputs.version }}_*.deb + merge-multiple: true + path: ./output - - name: Add debug elfs to release - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ github.token }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./debug-elfs-${{ steps.version.outputs.version }}.zip - asset_name: debug-elfs-${{ steps.version.outputs.version }}.zip - asset_content_type: application/zip + # For diagnostics + - name: Display structure of downloaded files + run: ls -lR - name: Add raspbian aarch64 .deb uses: actions/upload-release-asset@v1 @@ -334,22 +308,94 @@ jobs: asset_name: meshtasticd_${{ steps.version.outputs.version }}_armhf.deb asset_content_type: application/vnd.debian.binary-package - - name: Add raspbian amd64 .deb - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ github.token }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./output/meshtasticd_${{ steps.version.outputs.version }}_amd64.deb - asset_name: meshtasticd_${{ steps.version.outputs.version }}_amd64.deb - asset_content_type: application/vnd.debian.binary-package + # - name: Add raspbian amd64 .deb + # uses: actions/upload-release-asset@v1 + # env: + # GITHUB_TOKEN: ${{ github.token }} + # with: + # upload_url: ${{ steps.create_release.outputs.upload_url }} + # asset_path: ./output/meshtasticd_${{ steps.version.outputs.version }}_amd64.deb + # asset_name: meshtasticd_${{ steps.version.outputs.version }}_amd64.deb + # asset_content_type: application/vnd.debian.binary-package - name: Bump version.properties run: >- bin/bump_version.py - name: Create version.properties pull request - uses: peter-evans/create-pull-request@v6 + uses: peter-evans/create-pull-request@v7 with: + title: Bump version.properties add-paths: | version.properties + + release-firmware: + strategy: + fail-fast: false + matrix: + arch: [esp32, esp32s3, esp32c3, esp32c6, nrf52840, rp2040, stm32] + runs-on: ubuntu-latest + if: ${{ github.event_name == 'workflow_dispatch' }} + needs: [release-artifacts] + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: 3.x + + - name: Get release version string + run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT + id: version + + - uses: actions/download-artifact@v4 + with: + pattern: firmware-${{matrix.arch}}-${{ steps.version.outputs.version }} + merge-multiple: true + path: ./output + + - name: Display structure of downloaded files + run: ls -lR + + - name: Device scripts permissions + run: | + chmod +x ./output/device-install.sh + chmod +x ./output/device-update.sh + + - name: Zip firmware + run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip ./output + + - uses: actions/download-artifact@v4 + with: + name: debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip + merge-multiple: true + path: ./elfs + + - name: Zip firmware + run: zip -j -9 -r ./debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip ./elfs + + # For diagnostics + - name: Display structure of downloaded files + run: ls -lR + + - name: Add bins to release + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ github.token }} + with: + upload_url: ${{needs.release-artifacts.outputs.upload_url}} + asset_path: ./firmware-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip + asset_name: firmware-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip + asset_content_type: application/zip + + - name: Add debug elfs to release + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ github.token }} + with: + upload_url: ${{needs.release-artifacts.outputs.upload_url}} + asset_path: ./debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip + asset_name: debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip + asset_content_type: application/zip diff --git a/.github/workflows/package_amd64.yml b/.github/workflows/package_amd64.yml index ae7bf3242..0b5093f24 100644 --- a/.github/workflows/package_amd64.yml +++ b/.github/workflows/package_amd64.yml @@ -13,7 +13,7 @@ jobs: uses: ./.github/workflows/build_native.yml package-native: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: build-native steps: - name: Checkout code diff --git a/.github/workflows/package_raspbian.yml b/.github/workflows/package_raspbian.yml index 5471332c5..bcbda53e2 100644 --- a/.github/workflows/package_raspbian.yml +++ b/.github/workflows/package_raspbian.yml @@ -13,7 +13,7 @@ jobs: uses: ./.github/workflows/build_raspbian.yml package-raspbian: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: build-raspbian steps: - name: Checkout code diff --git a/.github/workflows/package_raspbian_armv7l.yml b/.github/workflows/package_raspbian_armv7l.yml index 5b9c9aa71..1308fe925 100644 --- a/.github/workflows/package_raspbian_armv7l.yml +++ b/.github/workflows/package_raspbian_armv7l.yml @@ -13,7 +13,7 @@ jobs: uses: ./.github/workflows/build_raspbian_armv7l.yml package-raspbian_armv7l: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: build-raspbian_armv7l steps: - name: Checkout code diff --git a/.github/workflows/sec_sast_semgrep_cron.yml b/.github/workflows/sec_sast_semgrep_cron.yml index 2a0361f5e..54bbbe6d2 100644 --- a/.github/workflows/sec_sast_semgrep_cron.yml +++ b/.github/workflows/sec_sast_semgrep_cron.yml @@ -12,7 +12,7 @@ jobs: semgrep-full: runs-on: ubuntu-latest container: - image: returntocorp/semgrep + image: semgrep/semgrep steps: # step 1 diff --git a/.github/workflows/sec_sast_semgrep_pull.yml b/.github/workflows/sec_sast_semgrep_pull.yml index b6c288494..9013f1c74 100644 --- a/.github/workflows/sec_sast_semgrep_pull.yml +++ b/.github/workflows/sec_sast_semgrep_pull.yml @@ -4,9 +4,9 @@ on: pull_request jobs: semgrep-diff: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 container: - image: returntocorp/semgrep + image: semgrep/semgrep steps: # step 1 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f58b38ac9..241598fd0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -57,35 +57,50 @@ jobs: reporter: java-junit hardware-tests: - runs-on: ubuntu-latest + runs-on: test-runner steps: - name: Checkout code uses: actions/checkout@v4 + # - uses: actions/setup-python@v5 + # with: + # python-version: '3.10' + + # pipx install "setuptools<72" - name: Upgrade python tools shell: bash run: | - python -m pip install --upgrade pip - pip install -U --no-build-isolation --no-cache-dir "setuptools<72" - pip install -U platformio adafruit-nrfutil --no-build-isolation - pip install -U poetry --no-build-isolation - pip install -U meshtastic --pre --no-build-isolation + pipx install adafruit-nrfutil + pipx install poetry + pipx install meshtastic --pip-args=--pre + + - name: Install PlatformIO from script + shell: bash + run: | + curl -fsSL -o get-platformio.py https://raw.githubusercontent.com/platformio/platformio-core-installer/master/get-platformio.py + python3 get-platformio.py - name: Upgrade platformio shell: bash run: | + export PATH=$PATH:$HOME/.local/bin pio upgrade + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 18 + - name: Setup pnpm - uses: pnpm/action-setup@v2 + uses: pnpm/action-setup@v4 with: version: latest - - name: Install Dependencies - run: pnpm install - - - name: Setup devices - run: pnpm run setup - - - name: Execute end to end tests on connected hardware - run: pnpm run test + - name: Install dependencies, setup devices and run + shell: bash + run: | + git submodule update --init --recursive + cd meshtestic/ + pnpm install + pnpm run setup + pnpm run test diff --git a/.github/workflows/update_protobufs.yml b/.github/workflows/update_protobufs.yml index 4402a280e..f1c92b860 100644 --- a/.github/workflows/update_protobufs.yml +++ b/.github/workflows/update_protobufs.yml @@ -1,4 +1,4 @@ -name: "Update protobufs and regenerate classes" +name: Update protobufs and regenerate classes on: workflow_dispatch jobs: @@ -17,17 +17,18 @@ jobs: - name: Download nanopb run: | - wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.8-linux-x86.tar.gz - tar xvzf nanopb-0.4.8-linux-x86.tar.gz - mv nanopb-0.4.8-linux-x86 nanopb-0.4.8 + wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.9-linux-x86.tar.gz + tar xvzf nanopb-0.4.9-linux-x86.tar.gz + mv nanopb-0.4.9-linux-x86 nanopb-0.4.9 - name: Re-generate protocol buffers run: | ./bin/regen-protos.sh - name: Create pull request - uses: peter-evans/create-pull-request@v6 + uses: peter-evans/create-pull-request@v7 with: + title: Update protobufs and classes add-paths: | protobufs src/mesh diff --git a/.gitignore b/.gitignore index 0f2202f8d..28f9a24cc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,4 @@ .pio -main/configuration.h -main/credentials.h # ignore vscode IDE settings files .vscode/* @@ -32,4 +30,4 @@ release/ .vscode/extensions.json /compile_commands.json src/mesh/raspihttp/certificate.pem -src/mesh/raspihttp/private_key.pem \ No newline at end of file +src/mesh/raspihttp/private_key.pem diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index b20a1ec95..ea4045a16 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -1,34 +1,34 @@ version: 0.1 cli: - version: 1.22.3 + version: 1.22.6 plugins: sources: - id: trunk - ref: v1.6.2 + ref: v1.6.3 uri: https://github.com/trunk-io/plugins lint: enabled: - - trufflehog@3.81.9 + - trufflehog@3.82.6 - yamllint@1.35.1 - - bandit@1.7.9 - - checkov@3.2.238 + - bandit@1.7.10 + - checkov@3.2.256 - terrascan@1.19.1 - - trivy@0.54.1 + - trivy@0.55.2 #- trufflehog@3.63.2-rc0 - taplo@0.9.3 - - ruff@0.6.2 + - ruff@0.6.8 - isort@5.13.2 - - markdownlint@0.41.0 + - markdownlint@0.42.0 - oxipng@9.1.2 - svgo@3.3.2 - - actionlint@1.7.1 + - actionlint@1.7.3 - flake8@7.1.1 - hadolint@2.12.0 - shfmt@3.6.0 - shellcheck@0.10.0 - black@24.8.0 - git-diff-check - - gitleaks@8.18.4 + - gitleaks@8.20.0 - clang-format@16.0.3 - prettier@3.3.3 ignore: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..f579a7fe0 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,47 @@ +# Contributing to Meshtastic Firmware + +We're excited that you're interested in contributing to the Meshtastic firmware! This document provides a high-level overview of how you can get involved. + +## Important First Steps + +Before you begin, please: + +1. **Read our documentation**: Our [official documentation](https://meshtastic.org/docs/) is a crucial resource. It contains essential information about the project. + +2. **Check out the firmware build guide**: For specific instructions on setting up your development environment and building the firmware, refer to our [Firmware Build Guide](https://meshtastic.org/docs/development/firmware/build/). + +3. Read our [Code of Conduct](https://meshtastic.org/docs/legal/conduct/) + +4. Join our [Discord community](https://discord.com/invite/ktMAKGBnBs) to connect with developers and other contributors to get help. + +## Getting Help and Discussing Ideas + +We encourage open communication and discussion before diving into code changes: + +1. **Use GitHub Discussions**: For new ideas, questions, or to discuss potential changes, start a conversation in our [GitHub Discussions](https://github.com/meshtastic/firmware/discussions) first. This helps us collaborate and avoid duplicate work. + +2. **Join our Discord**: For real-time chat and quick questions, join our [Discord server](https://discord.com/invite/ktMAKGBnBs). It's a great place to get help and connect with other developers and the community. + +3. **Reporting Issues**: If you've identified a bug, please use our bug report template when creating a new issue in the [issue tracker](https://github.com/meshtastic/firmware/issues). Ensure you've searched existing issues to avoid duplicates. + +## Making Contributions + +> [!IMPORTANT] +> Before making any contributions, you must sign our Contributor License Agreement (CLA). You can do this by visiting https://cla-assistant.io/meshtastic/firmware. Be sure to use the GitHub account you will use to submit your contributions when signing. + +1. Fork the repository +2. Create a new branch for your feature or bug fix +3. Make your changes +4. Test your changes thoroughly +5. Create a pull request with a clear description, using the provided template, of your changes. Be sure to enable "Allow edits from maintainers". + +## Coding Standards + +To ensure consistent code formatting across the project: + +1. Install the [Trunk](https://marketplace.visualstudio.com/items?itemName=Trunk.io) extension for Visual Studio Code. +2. Before submitting your changes, run `trunk fmt` to automatically format your code according to our standards. + +Adhering to these formatting guidelines helps maintain code consistency and makes the review process smoother. + +Thank you for contributing to Meshtastic! diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..fb4d9005a --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,12 @@ +# Security Policy + +## Supported Versions + +| Firmware Version | Supported | +| ---------------- | ------------------ | +| 2.5.x | :white_check_mark: | +| <= 2.4.x | :x: | + +## Reporting a Vulnerability + +We support the private reporting of potential security vulnerabilities. Please go to the Security tab to file a report with a description of the potential vulnerability and reproduction scripts (preferred) or steps, and our developers will review. diff --git a/arch/esp32/esp32.ini b/arch/esp32/esp32.ini index 2c4d3d595..d8641fe9b 100644 --- a/arch/esp32/esp32.ini +++ b/arch/esp32/esp32.ini @@ -2,13 +2,13 @@ [esp32_base] extends = arduino_base custom_esp32_kind = esp32 -platform = platformio/espressif32@6.7.0 +platform = platformio/espressif32@6.9.0 build_src_filter = ${arduino_base.build_src_filter} - - - - + - - - - @@ -52,7 +52,7 @@ lib_deps = https://github.com/meshtastic/esp32_https_server.git#23665b3adc080a311dcbb586ed5941b5f94d6ea2 h2zero/NimBLE-Arduino@^1.4.2 https://github.com/dbSuS/libpax.git#7bcd3fcab75037505be9b122ab2b24cc5176b587 - https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6 + lewisxhe/XPowersLib@^0.2.6 https://github.com/meshtastic/ESP32_Codec2.git#633326c78ac251c059ab3a8c430fcdf25b41672f rweather/Crypto@^0.4.0 diff --git a/arch/esp32/esp32c6.ini b/arch/esp32/esp32c6.ini new file mode 100644 index 000000000..53d7f92ec --- /dev/null +++ b/arch/esp32/esp32c6.ini @@ -0,0 +1,40 @@ +[esp32c6_base] +extends = esp32_base +platform = https://github.com/Jason2866/platform-espressif32.git#22faa566df8c789000f8136cd8d0aca49617af55 +build_flags = + ${arduino_base.build_flags} + -Wall + -Wextra + -Isrc/platform/esp32 + -std=c++11 + -DESP_OPENSSL_SUPPRESS_LEGACY_WARNING + -DSERIAL_BUFFER_SIZE=4096 + -DLIBPAX_ARDUINO + -DLIBPAX_WIFI + -DLIBPAX_BLE + -DMESHTASTIC_EXCLUDE_WEBSERVER + ;-DDEBUG_HEAP + ; TEMP + -DHAS_BLUETOOTH=0 + -DMESHTASTIC_EXCLUDE_PAXCOUNTER + -DMESHTASTIC_EXCLUDE_BLUETOOTH + +lib_deps = + ${arduino_base.lib_deps} + ${networking_base.lib_deps} + ${environmental_base.lib_deps} + lewisxhe/XPowersLib@^0.2.6 + https://github.com/meshtastic/ESP32_Codec2.git#633326c78ac251c059ab3a8c430fcdf25b41672f + rweather/Crypto@^0.4.0 + +build_src_filter = + ${esp32_base.build_src_filter} - + +monitor_speed = 460800 +monitor_filters = esp32_c3_exception_decoder + +lib_ignore = + NonBlockingRTTTL + NimBLE-Arduino + libpax + \ No newline at end of file diff --git a/arch/nrf52/nrf52.ini b/arch/nrf52/nrf52.ini index baeb17abd..4ceb6a0ff 100644 --- a/arch/nrf52/nrf52.ini +++ b/arch/nrf52/nrf52.ini @@ -24,7 +24,7 @@ build_src_filter = - - - - - + - - - - diff --git a/arch/portduino/portduino.ini b/arch/portduino/portduino.ini index eb6d0a56d..76de3e8d3 100644 --- a/arch/portduino/portduino.ini +++ b/arch/portduino/portduino.ini @@ -1,6 +1,6 @@ ; The Portduino based sim environment on top of any host OS, all hardware will be simulated [portduino_base] -platform = https://github.com/meshtastic/platform-native.git#ad8112adf82ce1f5b917092cf32be07a077801a0 +platform = https://github.com/meshtastic/platform-native.git#6b3796d697481c8f6e3f4aa5c111bd9979f29e64 framework = arduino build_src_filter = @@ -9,7 +9,7 @@ build_src_filter = - - - - - + - - - - @@ -25,7 +25,7 @@ lib_deps = ${env.lib_deps} ${networking_base.lib_deps} rweather/Crypto@^0.4.0 - https://github.com/lovyan03/LovyanGFX.git#5a39989aa2c9492572255b22f033843ec8900233 + lovyan03/LovyanGFX@^1.1.16 build_flags = ${arduino_base.build_flags} @@ -35,4 +35,4 @@ build_flags = -DPORTDUINO_LINUX_HARDWARE -lbluetooth -lgpiod - -lyaml-cpp + -lyaml-cpp \ No newline at end of file diff --git a/arch/rp2xx0/rp2040.ini b/arch/rp2xx0/rp2040.ini new file mode 100644 index 000000000..c004a3bec --- /dev/null +++ b/arch/rp2xx0/rp2040.ini @@ -0,0 +1,25 @@ +; Common settings for rp2040 Processor based targets +[rp2040_base] +platform = https://github.com/maxgerhardt/platform-raspberrypi.git#v1.2.0-gcc12 +extends = arduino_base +platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#4.0.3 + +board_build.core = earlephilhower +board_build.filesystem_size = 0.5m +build_flags = + ${arduino_base.build_flags} -Wno-unused-variable -Wcast-align + -Isrc/platform/rp2xx0 + -Isrc/platform/rp2xx0/hardware_rosc/include + -Isrc/platform/rp2xx0/pico_sleep/include + -D__PLAT_RP2040__ +# -D _POSIX_THREADS +build_src_filter = + ${arduino_base.build_src_filter} - - - - - - - - - + +lib_ignore = + BluetoothOTA + +lib_deps = + ${arduino_base.lib_deps} + ${environmental_base.lib_deps} + rweather/Crypto \ No newline at end of file diff --git a/arch/rp2040/rp2040.ini b/arch/rp2xx0/rp2350.ini similarity index 82% rename from arch/rp2040/rp2040.ini rename to arch/rp2xx0/rp2350.ini index 372934758..0579dc15e 100644 --- a/arch/rp2040/rp2040.ini +++ b/arch/rp2xx0/rp2350.ini @@ -1,14 +1,14 @@ ; Common settings for rp2040 Processor based targets -[rp2040_base] -platform = https://github.com/maxgerhardt/platform-raspberrypi.git#60d6ae81fcc73c34b1493ca9e261695e471bc0c2 +[rp2350_base] +platform = https://github.com/maxgerhardt/platform-raspberrypi.git#9e55f6db5c56b9867c69fe473f388beea4546672 extends = arduino_base -platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#3.7.2 +platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#a6ab6e1f95bc1428d667d55ea7173c0744acc03c ; 4.0.2+ board_build.core = earlephilhower board_build.filesystem_size = 0.5m build_flags = ${arduino_base.build_flags} -Wno-unused-variable - -Isrc/platform/rp2040 + -Isrc/platform/rp2xx0 -D__PLAT_RP2040__ # -D _POSIX_THREADS build_src_filter = diff --git a/arch/stm32/stm32.ini b/arch/stm32/stm32.ini index 80bd0017c..c1b783083 100644 --- a/arch/stm32/stm32.ini +++ b/arch/stm32/stm32.ini @@ -1,7 +1,7 @@ [stm32_base] extends = arduino_base platform = ststm32 -platform_packages = platformio/framework-arduinoststm32@https://github.com/stm32duino/Arduino_Core_STM32.git#361a7fdb67e2a7104e99b4f42a802469eef8b129 +platform_packages = platformio/framework-arduinoststm32@https://github.com/stm32duino/Arduino_Core_STM32.git#ea74156acd823b6d14739f389e6cdc648f8ee36e build_type = release @@ -22,7 +22,7 @@ build_flags = -fdata-sections build_src_filter = - ${arduino_base.build_src_filter} - - - - - - - - - - - - - - - + ${arduino_base.build_src_filter} - - - - - - - - - - - - - - - board_upload.offset_address = 0x08000000 upload_protocol = stlink @@ -33,5 +33,5 @@ lib_deps = https://github.com/caveman99/Crypto.git#f61ae26a53f7a2d0ba5511625b8bf8eff3a35d5e lib_ignore = - https://github.com/mathertel/OneButton@~2.6.1 + mathertel/OneButton@~2.6.1 Wire \ No newline at end of file diff --git a/bin/build-userprefs-json.py b/bin/build-userprefs-json.py new file mode 100644 index 000000000..58f460bcf --- /dev/null +++ b/bin/build-userprefs-json.py @@ -0,0 +1,48 @@ +import json +import subprocess +import re + +def get_macros_from_header(header_file): + # Run clang to preprocess the header file and capture the output + result = subprocess.run(['clang', '-E', '-dM', header_file], capture_output=True, text=True) + if result.returncode != 0: + raise RuntimeError(f"Clang preprocessing failed: {result.stderr}") + + # Extract macros from the output + macros = {} + macro_pattern = re.compile(r'#define\s+(\w+)\s+(.*)') + for line in result.stdout.splitlines(): + match = macro_pattern.match(line) + if match and 'USERPREFS_' in line and '_USERPREFS_' not in line: + macros[match.group(1)] = match.group(2) + + return macros + +def write_macros_to_json(macros, output_file): + with open(output_file, 'w') as f: + json.dump(macros, f, indent=4) + +def main(): + header_file = 'userPrefs.h' + output_file = 'userPrefs.json' + # Uncomment all macros in the header file + with open(header_file, 'r') as file: + lines = file.readlines() + + uncommented_lines = [] + for line in lines: + stripped_line = line.strip().replace('/*', '').replace('*/', '') + if stripped_line.startswith('//') and 'USERPREFS_' in stripped_line: + # Replace "//" + stripped_line = stripped_line.replace('//', '') + uncommented_lines.append(stripped_line + '\n') + + with open(header_file, 'w') as file: + for line in uncommented_lines: + file.write(line) + macros = get_macros_from_header(header_file) + write_macros_to_json(macros, output_file) + print(f"Macros have been written to {output_file}") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/bin/config-dist.yaml b/bin/config-dist.yaml index 5590ed3b0..3a470b7bb 100644 --- a/bin/config-dist.yaml +++ b/bin/config-dist.yaml @@ -9,6 +9,7 @@ Lora: # IRQ: 16 # Busy: 20 # Reset: 18 +# SX126X_ANT_SW: 6 # Module: sx1262 # Waveshare SX1302 LISTEN ONLY AT THIS TIME! # CS: 7 @@ -153,4 +154,4 @@ Webserver: General: MaxNodes: 200 - MaxMessageQueue: 100 + MaxMessageQueue: 100 \ No newline at end of file diff --git a/bin/generate_ci_matrix.py b/bin/generate_ci_matrix.py index 46398dd59..4d8759ecc 100755 --- a/bin/generate_ci_matrix.py +++ b/bin/generate_ci_matrix.py @@ -6,6 +6,7 @@ import configparser import json import os import sys +import random rootdir = "variants/" @@ -39,5 +40,7 @@ for subdir, dirs, files in os.walk(rootdir): "check" in options ): outlist.append(section) - -print(json.dumps(outlist)) +if ("quick" in options) & (len(outlist) > 3): + print(json.dumps(random.sample(outlist, 3))) +else: + print(json.dumps(outlist)) diff --git a/bin/regen-protos.bat b/bin/regen-protos.bat index f28ef0025..7fa8f333d 100644 --- a/bin/regen-protos.bat +++ b/bin/regen-protos.bat @@ -1 +1 @@ -cd protobufs && ..\nanopb-0.4.8\generator-bin\protoc.exe --experimental_allow_proto3_optional "--nanopb_out=-S.cpp -v:..\src\mesh\generated" -I=..\protobufs\ ..\protobufs\meshtastic\*.proto +cd protobufs && ..\nanopb-0.4.9\generator-bin\protoc.exe --experimental_allow_proto3_optional "--nanopb_out=-S.cpp -v:..\src\mesh\generated" -I=..\protobufs\ ..\protobufs\meshtastic\*.proto diff --git a/bin/regen-protos.sh b/bin/regen-protos.sh index 2e60784e3..12546bfdc 100755 --- a/bin/regen-protos.sh +++ b/bin/regen-protos.sh @@ -2,10 +2,10 @@ set -e -echo "This script requires https://jpa.kapsi.fi/nanopb/download/ version 0.4.8 to be located in the" +echo "This script requires https://jpa.kapsi.fi/nanopb/download/ version 0.4.9 to be located in the" echo "firmware root directory if the following step fails, you should download the correct" -echo "prebuilt binaries for your computer into nanopb-0.4.8" +echo "prebuilt binaries for your computer into nanopb-0.4.9" # the nanopb tool seems to require that the .options file be in the current directory! cd protobufs -../nanopb-0.4.8/generator-bin/protoc --experimental_allow_proto3_optional "--nanopb_out=-S.cpp -v:../src/mesh/generated/" -I=../protobufs meshtastic/*.proto +../nanopb-0.4.9/generator-bin/protoc --experimental_allow_proto3_optional "--nanopb_out=-S.cpp -v:../src/mesh/generated/" -I=../protobufs meshtastic/*.proto diff --git a/boards/heltec_mesh_node_t114.json b/boards/heltec_mesh_node_t114.json index 5c97d8c75..2bd306eb9 100644 --- a/boards/heltec_mesh_node_t114.json +++ b/boards/heltec_mesh_node_t114.json @@ -5,7 +5,7 @@ }, "core": "nRF5", "cpu": "cortex-m4", - "extra_flags": "-DARDUINO_NRF52840_PCA10056 -DNRF52840_XXAA", + "extra_flags": "-DHELTEC_T114 -DNRF52840_XXAA", "f_cpu": "64000000L", "hwids": [ ["0x239A", "0x4405"], diff --git a/boards/lora-relay-v1.json b/boards/lora-relay-v1.json deleted file mode 100644 index b390b8404..000000000 --- a/boards/lora-relay-v1.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "build": { - "arduino": { - "ldscript": "nrf52840_s140_v6.ld" - }, - "core": "nRF5", - "cpu": "cortex-m4", - "extra_flags": "-DARDUINO_NRF52840_LORA_RELAY_V1 -DNRF52840_XXAA", - "f_cpu": "64000000L", - "hwids": [["0x239A", "0x4404"]], - "usb_product": "LORA_RELAY", - "mcu": "nrf52840", - "variant": "lora_relay_v1", - "variants_dir": "variants", - "bsp": { - "name": "adafruit" - }, - "softdevice": { - "sd_flags": "-DS140", - "sd_name": "s140", - "sd_version": "6.1.1", - "sd_fwid": "0x00B6" - }, - "bootloader": { - "settings_addr": "0xFF000" - } - }, - "connectivity": ["bluetooth"], - "debug": { - "jlink_device": "nRF52840_xxAA", - "onboard_tools": ["jlink"], - "svd_path": "nrf52840.svd", - "openocd_target": "nrf52840-mdk-rs" - }, - "frameworks": ["arduino"], - "name": "Meshtastic Lora Relay V1 (Adafruit BSP)", - "upload": { - "maximum_ram_size": 248832, - "maximum_size": 815104, - "require_upload_port": true, - "speed": 115200, - "protocol": "jlink", - "protocols": ["jlink", "nrfjprog", "stlink"] - }, - "url": "https://github.com/BigCorvus/SX1262-LoRa-BLE-Relay", - "vendor": "BigCorvus" -} diff --git a/boards/lora-relay-v2.json b/boards/lora-relay-v2.json deleted file mode 100644 index 52b775e58..000000000 --- a/boards/lora-relay-v2.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "build": { - "arduino": { - "ldscript": "nrf52840_s140_v6.ld" - }, - "core": "nRF5", - "cpu": "cortex-m4", - "extra_flags": "-DARDUINO_NRF52840_LORA_RELAY_V2 -DNRF52840_XXAA", - "f_cpu": "64000000L", - "hwids": [["0x239A", "0x4406"]], - "usb_product": "LORA_RELAY", - "mcu": "nrf52840", - "variant": "lora_relay_v2", - "variants_dir": "variants", - "bsp": { - "name": "adafruit" - }, - "softdevice": { - "sd_flags": "-DS140", - "sd_name": "s140", - "sd_version": "6.1.1", - "sd_fwid": "0x00B6" - }, - "bootloader": { - "settings_addr": "0xFF000" - } - }, - "connectivity": ["bluetooth"], - "debug": { - "jlink_device": "nRF52840_xxAA", - "onboard_tools": ["jlink"], - "svd_path": "nrf52840.svd", - "openocd_target": "nrf52840-mdk-rs" - }, - "frameworks": ["arduino"], - "name": "Meshtastic Lora Relay V1 (Adafruit BSP)", - "upload": { - "maximum_ram_size": 248832, - "maximum_size": 815104, - "require_upload_port": true, - "speed": 115200, - "protocol": "jlink", - "protocols": ["jlink", "nrfjprog", "stlink"] - }, - "url": "https://github.com/BigCorvus/SX1262-LoRa-BLE-Relay", - "vendor": "BigCorvus" -} diff --git a/boards/lora_isp4520.json b/boards/lora_isp4520.json deleted file mode 100644 index 8125aa666..000000000 --- a/boards/lora_isp4520.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "build": { - "arduino": { - "ldscript": "nrf52832_s132_v6.ld" - }, - "core": "nRF5", - "cpu": "cortex-m4", - "extra_flags": "-DNRF52832_XXAA -DNRF52", - "f_cpu": "64000000L", - "mcu": "nrf52832", - "variant": "lora_isp4520", - "bsp": { - "name": "adafruit" - }, - "softdevice": { - "sd_flags": "-DS132", - "sd_name": "s132", - "sd_version": "6.1.1", - "sd_fwid": "0x00B7" - } - }, - "connectivity": ["bluetooth"], - "debug": { - "jlink_device": "nRF52832_xxAA", - "svd_path": "nrf52.svd", - "openocd_target": "nrf52840-mdk-rs" - }, - "frameworks": ["arduino"], - "name": "lora ISP4520", - "upload": { - "maximum_ram_size": 65536, - "maximum_size": 524288, - "require_upload_port": true, - "speed": 115200, - "protocol": "nrfutil", - "protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"] - }, - "url": "", - "vendor": "PsiSoft" -} diff --git a/boards/me25ls01-4y10td.json b/boards/me25ls01-4y10td.json index 46c526a7c..9e1d63265 100644 --- a/boards/me25ls01-4y10td.json +++ b/boards/me25ls01-4y10td.json @@ -5,7 +5,7 @@ }, "core": "nRF5", "cpu": "cortex-m4", - "extra_flags": "-DARDUINO_WIO_WM1110 -DNRF52840_XXAA", + "extra_flags": "-DME25LS01_4Y10TD -DNRF52840_XXAA", "f_cpu": "64000000L", "hwids": [ ["0x239A", "0x8029"], diff --git a/boards/ms24sf1.json b/boards/ms24sf1.json index 4e28507da..8356e3012 100644 --- a/boards/ms24sf1.json +++ b/boards/ms24sf1.json @@ -5,7 +5,7 @@ }, "core": "nRF5", "cpu": "cortex-m4", - "extra_flags": "-DARDUINO_WIO_WM1110 -DNRF52840_XXAA", + "extra_flags": "-DMS24SF1 -DNRF52840_XXAA", "f_cpu": "64000000L", "hwids": [ ["0x239A", "0x8029"], diff --git a/boards/nordic_pca10059.json b/boards/nordic_pca10059.json index b99e3c763..6540817a2 100644 --- a/boards/nordic_pca10059.json +++ b/boards/nordic_pca10059.json @@ -5,7 +5,7 @@ }, "core": "nRF5", "cpu": "cortex-m4", - "extra_flags": "-DARDUINO_NRF52840_FEATHER -DNRF52840_XXAA", + "extra_flags": "-DNORDIC_PCA10059 -DNRF52840_XXAA", "f_cpu": "64000000L", "hwids": [ ["0x239A", "0x8029"], diff --git a/boards/nrf52840_dk_modified.json b/boards/nrf52840_dk_modified.json deleted file mode 100644 index 2932cb4b9..000000000 --- a/boards/nrf52840_dk_modified.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "build": { - "arduino": { - "ldscript": "nrf52840_s113_v7.ld" - }, - "core": "nRF5", - "cpu": "cortex-m4", - "extra_flags": "-DARDUINO_NRF52840_PCA10056 -DNRF52840_XXAA", - "f_cpu": "64000000L", - "hwids": [["0x239A", "0x4404"]], - "usb_product": "nrf52840dk", - "mcu": "nrf52840", - "variant": "pca10056-rc-clock", - "variants_dir": "variants", - "bsp": { - "name": "adafruit" - }, - "softdevice": { - "sd_flags": "-DS140", - "sd_name": "s140", - "sd_version": "6.1.1", - "sd_fwid": "0x00B6" - }, - "bootloader": { - "settings_addr": "0xFF000" - } - }, - "connectivity": ["bluetooth"], - "debug": { - "jlink_device": "nRF52840_xxAA", - "onboard_tools": ["jlink"], - "svd_path": "nrf52840.svd", - "openocd_target": "nrf52840-mdk-rs" - }, - "frameworks": ["arduino"], - "name": "A modified NRF52840-DK devboard (Adafruit BSP)", - "upload": { - "maximum_ram_size": 248832, - "maximum_size": 815104, - "require_upload_port": true, - "speed": 115200, - "protocol": "jlink", - "protocols": ["jlink", "nrfjprog", "stlink"] - }, - "url": "https://meshtastic.org/", - "vendor": "Nordic Semi" -} diff --git a/boards/ppr.json b/boards/ppr.json deleted file mode 100644 index 15e3025c0..000000000 --- a/boards/ppr.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "build": { - "arduino": { - "ldscript": "nrf52840_s140_v6.ld" - }, - "core": "nRF5", - "cpu": "cortex-m4", - "extra_flags": "-DARDUINO_NRF52840_PPR -DNRF52840_XXAA", - "f_cpu": "64000000L", - "hwids": [["0x239A", "0x4403"]], - "usb_product": "PPR", - "mcu": "nrf52840", - "variant": "ppr", - "variants_dir": "variants", - "bsp": { - "name": "adafruit" - }, - "softdevice": { - "sd_flags": "-DS140", - "sd_name": "s140", - "sd_version": "6.1.1", - "sd_fwid": "0x00B6" - }, - "bootloader": { - "settings_addr": "0xFF000" - } - }, - "connectivity": ["bluetooth"], - "debug": { - "jlink_device": "nRF52840_xxAA", - "onboard_tools": ["jlink"], - "svd_path": "nrf52840.svd", - "openocd_target": "nrf52840-mdk-rs" - }, - "frameworks": ["arduino"], - "name": "Meshtastic PPR (Adafruit BSP)", - "upload": { - "maximum_ram_size": 248832, - "maximum_size": 815104, - "require_upload_port": true, - "speed": 115200, - "protocol": "jlink", - "protocols": ["jlink", "nrfjprog", "stlink"] - }, - "url": "https://meshtastic.org/", - "vendor": "Othernet" -} diff --git a/boards/ppr1.json b/boards/ppr1.json deleted file mode 100644 index 35bf7d1e4..000000000 --- a/boards/ppr1.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "build": { - "arduino": { - "ldscript": "nrf52833_s113_v7.ld" - }, - "core": "nRF5", - "cpu": "cortex-m4", - "extra_flags": "-DARDUINO_NRF52833_PPR -DNRF52833_XXAA", - "f_cpu": "64000000L", - "hwids": [["0x239A", "0x4406"]], - "usb_product": "PPR", - "mcu": "nrf52833", - "variant": "ppr", - "variants_dir": "variants", - "bsp": { - "name": "adafruit" - }, - "softdevice": { - "sd_flags": "-DS113", - "sd_name": "s113", - "sd_version": "7.2.0", - "sd_fwid": "0x00b6" - }, - "bootloader": { - "settings_addr": "0xFF000" - } - }, - "connectivity": ["bluetooth"], - "debug": { - "jlink_device": "nRF52833_xxAA", - "onboard_tools": ["jlink"], - "svd_path": "nrf52833.svd", - "openocd_target": "nrf52840-mdk-rs" - }, - "frameworks": ["arduino"], - "name": "Meshtastic PPR1 (Adafruit BSP)", - "upload": { - "maximum_ram_size": 248832, - "maximum_size": 815104, - "require_upload_port": true, - "speed": 115200, - "protocol": "jlink", - "protocols": ["jlink", "nrfjprog", "stlink"] - }, - "url": "https://meshtastic.org/", - "vendor": "Othernet" -} diff --git a/boards/seeed-sensecap-indicator.json b/boards/seeed-sensecap-indicator.json new file mode 100644 index 000000000..3fc57126f --- /dev/null +++ b/boards/seeed-sensecap-indicator.json @@ -0,0 +1,42 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32s3_out.ld", + "partitions": "default_8MB.csv", + "memory_type": "qio_opi" + }, + "core": "esp32", + "extra_flags": [ + "-DBOARD_HAS_PSRAM", + "-DARDUINO_USB_CDC_ON_BOOT=0", + "-DARDUINO_USB_MODE=1", + "-DARDUINO_RUNNING_CORE=1", + "-DARDUINO_EVENT_RUNNING_CORE=1" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "hwids": [["0x1A86", "0x7523"]], + "mcu": "esp32s3", + "variant": "esp32s3r8" + }, + "connectivity": ["wifi", "bluetooth", "lora"], + "debug": { + "default_tool": "esp-builtin", + "onboard_tools": ["esp-builtin"], + "openocd_target": "esp32s3.cfg" + }, + "frameworks": ["arduino"], + "name": "Seeed Studio SenseCAP Indicator", + "upload": { + "flash_size": "8MB", + "maximum_ram_size": 327680, + "maximum_size": 8388608, + "require_upload_port": true, + "use_1200bps_touch": true, + "wait_for_upload_port": true, + "speed": 921600 + }, + "url": "https://www.seeedstudio.com/Indicator-for-Meshtastic.html", + "vendor": "Seeed Studio" +} diff --git a/boards/seeed-xiao-s3.json b/boards/seeed-xiao-s3.json new file mode 100644 index 000000000..0b7b432a0 --- /dev/null +++ b/boards/seeed-xiao-s3.json @@ -0,0 +1,41 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32s3_out.ld", + "memory_type": "qio_opi" + }, + "core": "esp32", + "extra_flags": [ + "-DBOARD_HAS_PSRAM", + "-DARDUINO_USB_CDC_ON_BOOT=1", + "-DARDUINO_USB_MODE=0", + "-DARDUINO_RUNNING_CORE=1", + "-DARDUINO_EVENT_RUNNING_CORE=0" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "hwids": [["0x2886", "0x0059"]], + "mcu": "esp32s3", + "variant": "seeed-xiao-s3" + }, + "connectivity": ["wifi", "bluetooth", "lora"], + "debug": { + "default_tool": "esp-builtin", + "onboard_tools": ["esp-builtin"], + "openocd_target": "esp32s3.cfg" + }, + "frameworks": ["arduino", "espidf"], + "name": "seeed-xiao-s3", + "upload": { + "flash_size": "8MB", + "maximum_ram_size": 8388608, + "maximum_size": 8388608, + "use_1200bps_touch": true, + "wait_for_upload_port": true, + "require_upload_port": true, + "speed": 921600 + }, + "url": "https://www.seeedstudio.com/XIAO-ESP32S3-Sense-p-5639.html", + "vendor": "Seeed Studio" +} diff --git a/images/compass.png b/images/compass.png index 8639dde52..c4e5b589b 100644 Binary files a/images/compass.png and b/images/compass.png differ diff --git a/images/face-24px.svg b/images/face-24px.svg index 293468664..08e8f5ffc 100644 --- a/images/face-24px.svg +++ b/images/face-24px.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/face.png b/images/face.png index 036e29f73..eb6b91e13 100644 Binary files a/images/face.png and b/images/face.png differ diff --git a/images/location_searching-24px.svg b/images/location_searching-24px.svg index 7f72a31bc..abef0ef78 100644 --- a/images/location_searching-24px.svg +++ b/images/location_searching-24px.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/pin.png b/images/pin.png index 112a7ce8e..3c91a726c 100644 Binary files a/images/pin.png and b/images/pin.png differ diff --git a/images/room-24px.svg b/images/room-24px.svg index 79a4807e7..48e55ab80 100644 --- a/images/room-24px.svg +++ b/images/room-24px.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/textsms-24px.svg b/images/textsms-24px.svg index 4455f047e..84c4fdcc1 100644 --- a/images/textsms-24px.svg +++ b/images/textsms-24px.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/meshtestic b/meshtestic index 31ee3d90c..dcac7e567 160000 --- a/meshtestic +++ b/meshtestic @@ -1 +1 @@ -Subproject commit 31ee3d90c8bef61e835c3271be2c7cda8c4a5cc2 +Subproject commit dcac7e5673005f4d8a2b1f0f6e06877b689d7519 diff --git a/platformio.ini b/platformio.ini index 87eb0afb7..e437b572e 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,11 +17,9 @@ default_envs = tbeam ;default_envs = tlora-v2-1-1_6 ;default_envs = tlora-v2-1-1_6-tcxo ;default_envs = tlora-t3s3-v1 -;default_envs = lora-relay-v1 # nrf board ;default_envs = t-echo ;default_envs = canaryone -;default_envs = nrf52840dk-geeksville -;default_envs = native # lora-relay-v1 # nrf52840dk-geeksville # linux # or if you'd like to change the default to something like lora-relay-v1 put that here +;default_envs = native ;default_envs = nano-g1 ;default_envs = pca10059_diy_eink ;default_envs = meshtastic-diy-v1 @@ -29,6 +27,7 @@ default_envs = tbeam ;default_envs = meshtastic-dr-dev ;default_envs = m5stack-coreink ;default_envs = rak4631 +;default_envs = rak4631_eth_gw ;default_envs = rak2560 ;default_envs = rak10701 ;default_envs = wio-e5 @@ -60,41 +59,42 @@ build_flags = -Wno-missing-field-initializers -DUSE_THREAD_NAMES -DTINYGPS_OPTION_NO_CUSTOM_FIELDS -DPB_ENABLE_MALLOC=1 - -DRADIOLIB_EXCLUDE_CC1101 - -DRADIOLIB_EXCLUDE_NRF24 - -DRADIOLIB_EXCLUDE_RF69 - -DRADIOLIB_EXCLUDE_SX1231 - -DRADIOLIB_EXCLUDE_SX1233 - -DRADIOLIB_EXCLUDE_SI443X - -DRADIOLIB_EXCLUDE_RFM2X - -DRADIOLIB_EXCLUDE_AFSK - -DRADIOLIB_EXCLUDE_BELL - -DRADIOLIB_EXCLUDE_HELLSCHREIBER - -DRADIOLIB_EXCLUDE_MORSE - -DRADIOLIB_EXCLUDE_RTTY - -DRADIOLIB_EXCLUDE_SSTV - -DRADIOLIB_EXCLUDE_AX25 - -DRADIOLIB_EXCLUDE_DIRECT_RECEIVE - -DRADIOLIB_EXCLUDE_BELL - -DRADIOLIB_EXCLUDE_PAGER - -DRADIOLIB_EXCLUDE_FSK4 - -DRADIOLIB_EXCLUDE_APRS - -DRADIOLIB_EXCLUDE_LORAWAN + -DRADIOLIB_EXCLUDE_CC1101=1 + -DRADIOLIB_EXCLUDE_NRF24=1 + -DRADIOLIB_EXCLUDE_RF69=1 + -DRADIOLIB_EXCLUDE_SX1231=1 + -DRADIOLIB_EXCLUDE_SX1233=1 + -DRADIOLIB_EXCLUDE_SI443X=1 + -DRADIOLIB_EXCLUDE_RFM2X=1 + -DRADIOLIB_EXCLUDE_AFSK=1 + -DRADIOLIB_EXCLUDE_BELL=1 + -DRADIOLIB_EXCLUDE_HELLSCHREIBER=1 + -DRADIOLIB_EXCLUDE_MORSE=1 + -DRADIOLIB_EXCLUDE_RTTY=1 + -DRADIOLIB_EXCLUDE_SSTV=1 + -DRADIOLIB_EXCLUDE_AX25=1 + -DRADIOLIB_EXCLUDE_DIRECT_RECEIVE=1 + -DRADIOLIB_EXCLUDE_BELL=1 + -DRADIOLIB_EXCLUDE_PAGER=1 + -DRADIOLIB_EXCLUDE_FSK4=1 + -DRADIOLIB_EXCLUDE_APRS=1 + -DRADIOLIB_EXCLUDE_LORAWAN=1 -DMESHTASTIC_EXCLUDE_DROPZONE=1 + -DMESHTASTIC_EXCLUDE_REMOTEHARDWARE=1 + #-DBUILD_EPOCH=$UNIX_TIME ;-D OLED_PL monitor_speed = 115200 monitor_filters = direct lib_deps = -; jgromes/RadioLib@~6.6.0 - https://github.com/jgromes/RadioLib.git#3115fc2d6700a9aee05888791ac930a910f2628f + jgromes/RadioLib@~7.0.2 https://github.com/meshtastic/esp8266-oled-ssd1306.git#e16cee124fe26490cb14880c679321ad8ac89c95 ; ESP8266_SSD1306 - https://github.com/mathertel/OneButton@~2.6.1 ; OneButton library for non-blocking button debounce + mathertel/OneButton@~2.6.1 ; OneButton library for non-blocking button debounce https://github.com/meshtastic/arduino-fsm.git#7db3702bf0cfe97b783d6c72595e3f38e0b19159 https://github.com/meshtastic/TinyGPSPlus.git#71a82db35f3b973440044c476d4bcdc673b104f4 https://github.com/meshtastic/ArduinoThread.git#1ae8778c85d0a2a729f989e0b1e7d7c4dc84eef0 - nanopb/Nanopb@^0.4.8 + nanopb/Nanopb@^0.4.9 erriez/ErriezCRC32@^1.0.1 ; Used for the code analysis in PIO Home / Inspect @@ -127,7 +127,7 @@ lib_deps = ; (not included in native / portduino) [environmental_base] lib_deps = - adafruit/Adafruit BusIO@^1.15.0 + adafruit/Adafruit BusIO@^1.16.1 adafruit/Adafruit Unified Sensor@^1.1.11 adafruit/Adafruit BMP280 Library@^2.6.8 adafruit/Adafruit BMP085 Library@^1.2.4 @@ -136,28 +136,31 @@ lib_deps = adafruit/Adafruit MCP9808 Library@^2.0.0 adafruit/Adafruit INA260 Library@^1.5.0 adafruit/Adafruit INA219@^1.2.0 + adafruit/Adafruit MAX1704X@^1.0.3 adafruit/Adafruit SHTC3 Library@^1.0.0 adafruit/Adafruit LPS2X@^2.0.4 adafruit/Adafruit SHT31 Library@^2.2.2 - adafruit/Adafruit PM25 AQI Sensor@^1.0.6 + adafruit/Adafruit PM25 AQI Sensor@^1.1.1 adafruit/Adafruit MPU6050@^2.2.4 - adafruit/Adafruit LIS3DH@^1.2.4 + adafruit/Adafruit LIS3DH@^1.3.0 adafruit/Adafruit AHTX0@^2.0.5 adafruit/Adafruit LSM6DS@^4.7.2 adafruit/Adafruit VEML7700 Library@^2.1.6 adafruit/Adafruit SHT4x Library@^1.0.4 adafruit/Adafruit TSL2591 Library@^1.4.5 sparkfun/SparkFun Qwiic Scale NAU7802 Arduino Library@^1.0.5 + sparkfun/SparkFun 9DoF IMU Breakout - ICM 20948 - Arduino Library@^1.2.13 ClosedCube OPT3001@^1.1.2 emotibit/EmotiBit MLX90632@^1.0.8 dfrobot/DFRobot_RTU@^1.0.3 - + sparkfun/SparkFun MAX3010x Pulse and Proximity Sensor Library@^1.1.2 + adafruit/Adafruit MLX90614 Library@^2.1.5 https://github.com/boschsensortec/Bosch-BSEC2-Library#v1.7.2502 boschsensortec/BME68x Sensor Library@^1.1.40407 - https://github.com/KodinLanewave/INA3221@^1.0.0 - lewisxhe/SensorLib@^0.2.0 + https://github.com/KodinLanewave/INA3221@^1.0.1 + lewisxhe/SensorLib@0.2.0 mprograms/QMC5883LCompass@^1.2.0 - - https://github.com/meshtastic/DFRobot_LarkWeatherStation#dee914270dc7cb3e43fbf034edd85a63a16a12ee \ No newline at end of file + https://github.com/meshtastic/DFRobot_LarkWeatherStation#4de3a9cadef0f6a5220a8a906cf9775b02b0040d + https://github.com/gjelsoe/STK8xxx-Accelerometer.git#v0.1.1 diff --git a/protobufs b/protobufs index 005d7231e..c9ae7fd47 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 005d7231e59fdbadd74065230132b9ee176f2c87 +Subproject commit c9ae7fd478bffe5f954b30de6cb140821fe9ff52 diff --git a/src/AccelerometerThread.h b/src/AccelerometerThread.h deleted file mode 100644 index c2910007e..000000000 --- a/src/AccelerometerThread.h +++ /dev/null @@ -1,297 +0,0 @@ -#pragma once -#include "configuration.h" - -#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR - -#include "PowerFSM.h" -#include "concurrency/OSThread.h" -#include "main.h" -#include "power.h" - -#include -#include -#include -#include -#include -#include -#ifdef RAK_4631 -#include "Fusion/Fusion.h" -#include "graphics/Screen.h" -#include "graphics/ScreenFonts.h" -#include -#endif - -#define ACCELEROMETER_CHECK_INTERVAL_MS 100 -#define ACCELEROMETER_CLICK_THRESHOLD 40 - -static inline int readRegister(uint8_t address, uint8_t reg, uint8_t *data, uint8_t len) -{ - Wire.beginTransmission(address); - Wire.write(reg); - Wire.endTransmission(); - Wire.requestFrom((uint8_t)address, (uint8_t)len); - uint8_t i = 0; - while (Wire.available()) { - data[i++] = Wire.read(); - } - return 0; // Pass -} - -static inline int writeRegister(uint8_t address, uint8_t reg, uint8_t *data, uint8_t len) -{ - Wire.beginTransmission(address); - Wire.write(reg); - Wire.write(data, len); - return (0 != Wire.endTransmission()); -} - -class AccelerometerThread : public concurrency::OSThread -{ - public: - explicit AccelerometerThread(ScanI2C::DeviceType type) : OSThread("AccelerometerThread") - { - if (accelerometer_found.port == ScanI2C::I2CPort::NO_I2C) { - LOG_DEBUG("AccelerometerThread disabling due to no sensors found\n"); - disable(); - return; - } - acceleremoter_type = type; -#ifndef RAK_4631 - if (!config.display.wake_on_tap_or_motion && !config.device.double_tap_as_button_press) { - LOG_DEBUG("AccelerometerThread disabling due to no interested configurations\n"); - disable(); - return; - } -#endif - init(); - } - - void start() - { - init(); - setIntervalFromNow(0); - }; - - protected: - int32_t runOnce() override - { - canSleep = true; // Assume we should not keep the board awake - - if (acceleremoter_type == ScanI2C::DeviceType::MPU6050 && mpu.getMotionInterruptStatus()) { - wakeScreen(); - } else if (acceleremoter_type == ScanI2C::DeviceType::LIS3DH && lis.getClick() > 0) { - uint8_t click = lis.getClick(); - if (!config.device.double_tap_as_button_press) { - wakeScreen(); - } - - if (config.device.double_tap_as_button_press && (click & 0x20)) { - buttonPress(); - return 500; - } - } else if (acceleremoter_type == ScanI2C::DeviceType::BMA423 && bmaSensor.readIrqStatus() != DEV_WIRE_NONE) { - if (bmaSensor.isTilt() || bmaSensor.isDoubleTap()) { - wakeScreen(); - return 500; - } -#ifdef RAK_4631 - } else if (acceleremoter_type == ScanI2C::DeviceType::BMX160) { - sBmx160SensorData_t magAccel; - sBmx160SensorData_t gAccel; - - /* Get a new sensor event */ - bmx160.getAllData(&magAccel, NULL, &gAccel); - - // expirimental calibrate routine. Limited to between 10 and 30 seconds after boot - if (millis() > 12 * 1000 && millis() < 30 * 1000) { - if (!showingScreen) { - showingScreen = true; - screen->startAlert((FrameCallback)drawFrameCalibration); - } - if (magAccel.x > highestX) - highestX = magAccel.x; - if (magAccel.x < lowestX) - lowestX = magAccel.x; - if (magAccel.y > highestY) - highestY = magAccel.y; - if (magAccel.y < lowestY) - lowestY = magAccel.y; - if (magAccel.z > highestZ) - highestZ = magAccel.z; - if (magAccel.z < lowestZ) - lowestZ = magAccel.z; - } else if (showingScreen && millis() >= 30 * 1000) { - showingScreen = false; - screen->endAlert(); - } - - int highestRealX = highestX - (highestX + lowestX) / 2; - - magAccel.x -= (highestX + lowestX) / 2; - magAccel.y -= (highestY + lowestY) / 2; - magAccel.z -= (highestZ + lowestZ) / 2; - FusionVector ga, ma; - ga.axis.x = -gAccel.x; // default location for the BMX160 is on the rear of the board - ga.axis.y = -gAccel.y; - ga.axis.z = gAccel.z; - ma.axis.x = -magAccel.x; - ma.axis.y = -magAccel.y; - ma.axis.z = magAccel.z * 3; - - // If we're set to one of the inverted positions - if (config.display.compass_orientation > meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_270) { - ma = FusionAxesSwap(ma, FusionAxesAlignmentNXNYPZ); - ga = FusionAxesSwap(ga, FusionAxesAlignmentNXNYPZ); - } - - float heading = FusionCompassCalculateHeading(FusionConventionNed, ga, ma); - - switch (config.display.compass_orientation) { - case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_0_INVERTED: - case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_0: - break; - case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_90: - case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_90_INVERTED: - heading += 90; - break; - case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_180: - case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_180_INVERTED: - heading += 180; - break; - case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_270: - case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_270_INVERTED: - heading += 270; - break; - } - - screen->setHeading(heading); - -#endif - } else if (acceleremoter_type == ScanI2C::DeviceType::LSM6DS3 && lsm.shake()) { - wakeScreen(); - return 500; - } - - return ACCELEROMETER_CHECK_INTERVAL_MS; - } - - private: - void init() - { - LOG_DEBUG("AccelerometerThread initializing\n"); - - if (acceleremoter_type == ScanI2C::DeviceType::MPU6050 && mpu.begin(accelerometer_found.address)) { - LOG_DEBUG("MPU6050 initializing\n"); - // setup motion detection - mpu.setHighPassFilter(MPU6050_HIGHPASS_0_63_HZ); - mpu.setMotionDetectionThreshold(1); - mpu.setMotionDetectionDuration(20); - mpu.setInterruptPinLatch(true); // Keep it latched. Will turn off when reinitialized. - mpu.setInterruptPinPolarity(true); - } else if (acceleremoter_type == ScanI2C::DeviceType::LIS3DH && lis.begin(accelerometer_found.address)) { - LOG_DEBUG("LIS3DH initializing\n"); - lis.setRange(LIS3DH_RANGE_2_G); - // Adjust threshold, higher numbers are less sensitive - lis.setClick(config.device.double_tap_as_button_press ? 2 : 1, ACCELEROMETER_CLICK_THRESHOLD); - } else if (acceleremoter_type == ScanI2C::DeviceType::BMA423 && - bmaSensor.begin(accelerometer_found.address, &readRegister, &writeRegister)) { - LOG_DEBUG("BMA423 initializing\n"); - bmaSensor.configAccelerometer(bmaSensor.RANGE_2G, bmaSensor.ODR_100HZ, bmaSensor.BW_NORMAL_AVG4, - bmaSensor.PERF_CONTINUOUS_MODE); - bmaSensor.enableAccelerometer(); - bmaSensor.configInterrupt(BMA4_LEVEL_TRIGGER, BMA4_ACTIVE_HIGH, BMA4_PUSH_PULL, BMA4_OUTPUT_ENABLE, - BMA4_INPUT_DISABLE); - -#ifdef BMA423_INT - pinMode(BMA4XX_INT, INPUT); - attachInterrupt( - BMA4XX_INT, - [] { - // Set interrupt to set irq value to true - BMA_IRQ = true; - }, - RISING); // Select the interrupt mode according to the actual circuit -#endif - -#ifdef T_WATCH_S3 - // Need to raise the wrist function, need to set the correct axis - bmaSensor.setReampAxes(bmaSensor.REMAP_TOP_LAYER_RIGHT_CORNER); -#else - bmaSensor.setReampAxes(bmaSensor.REMAP_BOTTOM_LAYER_BOTTOM_LEFT_CORNER); -#endif - // bmaSensor.enableFeature(bmaSensor.FEATURE_STEP_CNTR, true); - bmaSensor.enableFeature(bmaSensor.FEATURE_TILT, true); - bmaSensor.enableFeature(bmaSensor.FEATURE_WAKEUP, true); - // bmaSensor.resetPedometer(); - - // Turn on feature interrupt - bmaSensor.enablePedometerIRQ(); - bmaSensor.enableTiltIRQ(); - // It corresponds to isDoubleClick interrupt - bmaSensor.enableWakeupIRQ(); -#ifdef RAK_4631 - } else if (acceleremoter_type == ScanI2C::DeviceType::BMX160 && bmx160.begin()) { - bmx160.ODR_Config(BMX160_ACCEL_ODR_100HZ, BMX160_GYRO_ODR_100HZ); // set output data rate - -#endif - } else if (acceleremoter_type == ScanI2C::DeviceType::LSM6DS3 && lsm.begin_I2C(accelerometer_found.address)) { - LOG_DEBUG("LSM6DS3 initializing\n"); - // Default threshold of 2G, less sensitive options are 4, 8 or 16G - lsm.setAccelRange(LSM6DS_ACCEL_RANGE_2_G); -#ifndef LSM6DS3_WAKE_THRESH -#define LSM6DS3_WAKE_THRESH 20 -#endif - lsm.enableWakeup(config.display.wake_on_tap_or_motion, 1, LSM6DS3_WAKE_THRESH); - // Duration is number of occurances needed to trigger, higher threshold is less sensitive - } - } - void wakeScreen() - { - if (powerFSM.getState() == &stateDARK) { - LOG_INFO("Tap or motion detected. Turning on screen\n"); - powerFSM.trigger(EVENT_INPUT); - } - } - - void buttonPress() - { - LOG_DEBUG("Double-tap detected. Firing button press\n"); - powerFSM.trigger(EVENT_PRESS); - } - - ScanI2C::DeviceType acceleremoter_type; - Adafruit_MPU6050 mpu; - Adafruit_LIS3DH lis; - Adafruit_LSM6DS3TRC lsm; - SensorBMA423 bmaSensor; - bool BMA_IRQ = false; -#ifdef RAK_4631 - bool showingScreen = false; - RAK_BMX160 bmx160; - float highestX = 0, lowestX = 0, highestY = 0, lowestY = 0, highestZ = 0, lowestZ = 0; - - static void drawFrameCalibration(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) - { - int x_offset = display->width() / 2; - int y_offset = display->height() <= 80 ? 0 : 32; - display->setTextAlignment(TEXT_ALIGN_LEFT); - display->setFont(FONT_MEDIUM); - display->drawString(x, y, "Calibrating\nCompass"); - int16_t compassX = 0, compassY = 0; - uint16_t compassDiam = graphics::Screen::getCompassDiam(display->getWidth(), display->getHeight()); - - // coordinates for the center of the compass/circle - if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) { - compassX = x + display->getWidth() - compassDiam / 2 - 5; - compassY = y + display->getHeight() / 2; - } else { - compassX = x + display->getWidth() - compassDiam / 2 - 5; - compassY = y + FONT_HEIGHT_SMALL + (display->getHeight() - FONT_HEIGHT_SMALL) / 2; - } - display->drawCircle(compassX, compassY, compassDiam / 2); - screen->drawCompassNorth(display, compassX, compassY, screen->getHeading() * PI / 180); - } -#endif -}; - -#endif \ No newline at end of file diff --git a/src/ButtonThread.cpp b/src/ButtonThread.cpp index 5351fa049..9e6ef55f5 100644 --- a/src/ButtonThread.cpp +++ b/src/ButtonThread.cpp @@ -124,6 +124,11 @@ int32_t ButtonThread::runOnce() switch (btnEvent) { case BUTTON_EVENT_PRESSED: { LOG_BUTTON("press!\n"); + // If a nag notification is running, stop it and prevent other actions + if (moduleConfig.external_notification.enabled && (externalNotificationModule->nagCycleCutoff != UINT32_MAX)) { + externalNotificationModule->stopNow(); + return 50; + } #ifdef BUTTON_PIN if (((config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN) != moduleConfig.canned_message.inputbroker_pin_press) || diff --git a/src/DebugConfiguration.cpp b/src/DebugConfiguration.cpp index 23b140daf..1c081ae29 100644 --- a/src/DebugConfiguration.cpp +++ b/src/DebugConfiguration.cpp @@ -97,12 +97,14 @@ Syslog &Syslog::logMask(uint8_t priMask) void Syslog::enable() { + this->_client->begin(this->_port); this->_enabled = true; } void Syslog::disable() { this->_enabled = false; + this->_client->stop(); } bool Syslog::isEnabled() @@ -193,4 +195,4 @@ inline bool Syslog::_sendLog(uint16_t pri, const char *appName, const char *mess return true; } -#endif \ No newline at end of file +#endif diff --git a/src/FSCommon.cpp b/src/FSCommon.cpp index 0f78f3e8b..bfe04fa49 100644 --- a/src/FSCommon.cpp +++ b/src/FSCommon.cpp @@ -375,8 +375,8 @@ void setupSDCard() } uint64_t cardSize = SD.cardSize() / (1024 * 1024); - LOG_DEBUG("SD Card Size: %lluMB\n", cardSize); - LOG_DEBUG("Total space: %llu MB\n", SD.totalBytes() / (1024 * 1024)); - LOG_DEBUG("Used space: %llu MB\n", SD.usedBytes() / (1024 * 1024)); + LOG_DEBUG("SD Card Size: %lu MB\n", (uint32_t)cardSize); + LOG_DEBUG("Total space: %lu MB\n", (uint32_t)(SD.totalBytes() / (1024 * 1024))); + LOG_DEBUG("Used space: %lu MB\n", (uint32_t)(SD.usedBytes() / (1024 * 1024))); #endif } \ No newline at end of file diff --git a/src/Power.cpp b/src/Power.cpp index 61a6c987d..2fe28633a 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -13,6 +13,7 @@ #include "power.h" #include "NodeDB.h" #include "PowerFSM.h" +#include "Throttle.h" #include "buzz/buzz.h" #include "configuration.h" #include "main.h" @@ -30,6 +31,7 @@ #if HAS_WIFI #include #endif + #endif #ifndef DELAY_FOREVER @@ -75,6 +77,15 @@ INA219Sensor ina219Sensor; INA3221Sensor ina3221Sensor; #endif +#if !MESHTASTIC_EXCLUDE_I2C && !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) +#include "modules/Telemetry/Sensor/MAX17048Sensor.h" +#include +extern std::pair nodeTelemetrySensorsMap[_meshtastic_TelemetrySensorType_MAX + 1]; +#if HAS_TELEMETRY && (!MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR || !MESHTASTIC_EXCLUDE_POWER_TELEMETRY) +MAX17048Sensor max17048Sensor; +#endif +#endif + #if HAS_RAKPROT && !defined(ARCH_PORTDUINO) RAK9154Sensor rak9154Sensor; #endif @@ -136,6 +147,8 @@ using namespace meshtastic; */ static HasBatteryLevel *batteryLevel; // Default to NULL for no battery level sensor +#ifdef BATTERY_PIN + static void adcEnable() { #ifdef ADC_CTRL // enable adc voltage divider when we need to read @@ -160,11 +173,14 @@ static void adcDisable() #endif } +#endif + /** * A simple battery level sensor that assumes the battery voltage is attached via a voltage-divider to an analog input */ class AnalogBatteryLevel : public HasBatteryLevel { + public: /** * Battery state of charge, from 0 to 100 or -1 for unknown */ @@ -244,7 +260,7 @@ class AnalogBatteryLevel : public HasBatteryLevel config.power.adc_multiplier_override > 0 ? config.power.adc_multiplier_override : ADC_MULTIPLIER; // Do not call analogRead() often. const uint32_t min_read_interval = 5000; - if (millis() - last_read_time_ms > min_read_interval) { + if (!Throttle::isWithinTimespanMs(last_read_time_ms, min_read_interval)) { last_read_time_ms = millis(); uint32_t raw = 0; @@ -551,7 +567,12 @@ bool Power::analogInit() */ bool Power::setup() { - bool found = axpChipInit() || analogInit(); + // initialise one power sensor (only) + bool found = axpChipInit(); + if (!found) + found = lipoInit(); + if (!found) + found = analogInit(); #ifdef NRF_APM found = true; @@ -567,7 +588,7 @@ void Power::shutdown() { LOG_INFO("Shutting down\n"); -#if defined(ARCH_NRF52) || defined(ARCH_ESP32) +#if defined(ARCH_NRF52) || defined(ARCH_ESP32) || defined(ARCH_RP2040) #ifdef PIN_LED1 ledOff(PIN_LED1); #endif @@ -950,7 +971,6 @@ bool Power::axpChipInit() PMU->enableVbusVoltageMeasure(); PMU->enableBattVoltageMeasure(); - LOG_DEBUG("=======================================================================\n"); if (PMU->isChannelAvailable(XPOWERS_DCDC1)) { LOG_DEBUG("DC1 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_DCDC1) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_DCDC1)); @@ -999,7 +1019,6 @@ bool Power::axpChipInit() LOG_DEBUG("BLDO2: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_BLDO2) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_BLDO2)); } - LOG_DEBUG("=======================================================================\n"); // We can safely ignore this approach for most (or all) boards because MCU turned off // earlier than battery discharged to 2.6V. @@ -1042,4 +1061,82 @@ bool Power::axpChipInit() #else return false; #endif -} \ No newline at end of file +} + +#if !MESHTASTIC_EXCLUDE_I2C && !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) + +/** + * Wrapper class for an I2C MAX17048 Lipo battery sensor. + */ +class LipoBatteryLevel : public HasBatteryLevel +{ + private: + MAX17048Singleton *max17048 = nullptr; + + public: + /** + * Init the I2C MAX17048 Lipo battery level sensor + */ + bool runOnce() + { + if (max17048 == nullptr) { + max17048 = MAX17048Singleton::GetInstance(); + } + + // try to start if the sensor has been detected + if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_MAX17048].first != 0) { + return max17048->runOnce(nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_MAX17048].second); + } + return false; + } + + /** + * Battery state of charge, from 0 to 100 or -1 for unknown + */ + virtual int getBatteryPercent() override { return max17048->getBusBatteryPercent(); } + + /** + * The raw voltage of the battery in millivolts, or NAN if unknown + */ + virtual uint16_t getBattVoltage() override { return max17048->getBusVoltageMv(); } + + /** + * return true if there is a battery installed in this unit + */ + virtual bool isBatteryConnect() override { return max17048->isBatteryConnected(); } + + /** + * return true if there is an external power source detected + */ + virtual bool isVbusIn() override { return max17048->isExternallyPowered(); } + + /** + * return true if the battery is currently charging + */ + virtual bool isCharging() override { return max17048->isBatteryCharging(); } +}; + +LipoBatteryLevel lipoLevel; + +/** + * Init the Lipo battery level sensor + */ +bool Power::lipoInit() +{ + bool result = lipoLevel.runOnce(); + LOG_DEBUG("Power::lipoInit lipo sensor is %s\n", result ? "ready" : "not ready yet"); + if (!result) + return false; + batteryLevel = &lipoLevel; + return true; +} + +#else +/** + * The Lipo battery level sensor is unavailable - default to AnalogBatteryLevel + */ +bool Power::lipoInit() +{ + return false; +} +#endif \ No newline at end of file diff --git a/src/SafeFile.h b/src/SafeFile.h index 7088074cd..61361d312 100644 --- a/src/SafeFile.h +++ b/src/SafeFile.h @@ -24,7 +24,7 @@ class SafeFile : public Print { public: - SafeFile(char const *filepath, bool fullAtomic = false); + explicit SafeFile(char const *filepath, bool fullAtomic = false); virtual size_t write(uint8_t); virtual size_t write(const uint8_t *buffer, size_t size); diff --git a/src/SerialConsole.cpp b/src/SerialConsole.cpp index b911e15da..fe6ccdefe 100644 --- a/src/SerialConsole.cpp +++ b/src/SerialConsole.cpp @@ -1,6 +1,8 @@ #include "SerialConsole.h" +#include "Default.h" #include "NodeDB.h" #include "PowerFSM.h" +#include "Throttle.h" #include "configuration.h" #include "time.h" @@ -44,10 +46,11 @@ SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port), con Port.setRX(SERIAL2_RX); #endif Port.begin(SERIAL_BAUD); -#if defined(ARCH_NRF52) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(ARCH_RP2040) +#if defined(ARCH_NRF52) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(ARCH_RP2040) || \ + defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) time_t timeout = millis(); while (!Port) { - if ((millis() - timeout) < 5000) { + if (Throttle::isWithinTimespanMs(timeout, FIVE_SECONDS_MS)) { delay(100); } else { break; @@ -72,8 +75,7 @@ void SerialConsole::flush() // For the serial port we can't really detect if any client is on the other side, so instead just look for recent messages bool SerialConsole::checkIsConnected() { - uint32_t now = millis(); - return (now - lastContactMsec) < SERIAL_CONNECTION_TIMEOUT; + return Throttle::isWithinTimespanMs(lastContactMsec, SERIAL_CONNECTION_TIMEOUT); } /** @@ -120,4 +122,4 @@ void SerialConsole::log_to_serial(const char *logLevel, const char *format, va_l emitLogRecord(ll, thread ? thread->ThreadName.c_str() : "", format, arg); } else RedirectablePrint::log_to_serial(logLevel, format, arg); -} +} \ No newline at end of file diff --git a/src/airtime.cpp b/src/airtime.cpp index 2702ee2bf..fcda05468 100644 --- a/src/airtime.cpp +++ b/src/airtime.cpp @@ -13,17 +13,17 @@ void AirTime::logAirtime(reportTypes reportType, uint32_t airtime_ms) { if (reportType == TX_LOG) { - LOG_DEBUG("AirTime - Packet transmitted : %ums\n", airtime_ms); + LOG_DEBUG("Packet transmitted : %ums\n", airtime_ms); this->airtimes.periodTX[0] = this->airtimes.periodTX[0] + airtime_ms; air_period_tx[0] = air_period_tx[0] + airtime_ms; this->utilizationTX[this->getPeriodUtilHour()] = this->utilizationTX[this->getPeriodUtilHour()] + airtime_ms; } else if (reportType == RX_LOG) { - LOG_DEBUG("AirTime - Packet received : %ums\n", airtime_ms); + LOG_DEBUG("Packet received : %ums\n", airtime_ms); this->airtimes.periodRX[0] = this->airtimes.periodRX[0] + airtime_ms; air_period_rx[0] = air_period_rx[0] + airtime_ms; } else if (reportType == RX_ALL_LOG) { - LOG_DEBUG("AirTime - Packet received (noise?) : %ums\n", airtime_ms); + LOG_DEBUG("Packet received (noise?) : %ums\n", airtime_ms); this->airtimes.periodRX_ALL[0] = this->airtimes.periodRX_ALL[0] + airtime_ms; } diff --git a/src/configuration.h b/src/configuration.h index 047dbd727..729d6b046 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -69,6 +69,9 @@ along with this program. If not, see . #ifndef RTC_DATA_ATTR #define RTC_DATA_ATTR #endif +#ifndef EXT_RAM_BSS_ATTR +#define EXT_RAM_BSS_ATTR EXT_RAM_ATTR +#endif // ----------------------------------------------------------------------------- // Regulatory overrides @@ -122,9 +125,11 @@ along with this program. If not, see . #define INA_ADDR_ALTERNATE 0x41 #define INA_ADDR_WAVESHARE_UPS 0x43 #define INA3221_ADDR 0x42 +#define MAX1704X_ADDR 0x36 #define QMC6310_ADDR 0x1C #define QMI8658_ADDR 0x6B -#define QMC5883L_ADDR 0x1E +#define QMC5883L_ADDR 0x0D +#define HMC5883L_ADDR 0x1E #define SHTC3_ADDR 0x70 #define LPS22HB_ADDR 0x5C #define LPS22HB_ADDR_ALT 0x5D @@ -139,15 +144,20 @@ along with this program. If not, see . #define MLX90632_ADDR 0x3A #define DFROBOT_LARK_ADDR 0x42 #define NAU7802_ADDR 0x2A +#define MAX30102_ADDR 0x57 +#define MLX90614_ADDR_DEF 0x5A // ----------------------------------------------------------------------------- // ACCELEROMETER // ----------------------------------------------------------------------------- #define MPU6050_ADDR 0x68 +#define STK8BXX_ADR 0x18 #define LIS3DH_ADR 0x18 #define BMA423_ADDR 0x19 #define LSM6DS3_ADDR 0x6A #define BMX160_ADDR 0x69 +#define ICM20948_ADDR 0x69 +#define ICM20948_ADDR_ALT 0x68 // ----------------------------------------------------------------------------- // LED @@ -162,6 +172,7 @@ along with this program. If not, see . // ----------------------------------------------------------------------------- // IO Expander // ----------------------------------------------------------------------------- +#define TCA9535_ADDR 0x20 #define TCA9555_ADDR 0x26 // ----------------------------------------------------------------------------- @@ -171,6 +182,11 @@ along with this program. If not, see . #define GPS_THREAD_INTERVAL 200 #endif +// ----------------------------------------------------------------------------- +// Touchscreen +// ----------------------------------------------------------------------------- +#define FT6336U_ADDR 0x48 + // convert 24-bit color to 16-bit (56K) #define COLOR565(r, g, b) (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3)) @@ -202,6 +218,16 @@ along with this program. If not, see . #define MINIMUM_SAFE_FREE_HEAP 1500 #endif +#ifndef WIRE_INTERFACES_COUNT +// Officially an NRF52 macro +// Repurposed cross-platform to identify devices using Wire1 +#if defined(I2C_SDA1) || defined(PIN_WIRE1_SDA) +#define WIRE_INTERFACES_COUNT 2 +#elif HAS_WIRE +#define WIRE_INTERFACES_COUNT 1 +#endif +#endif + /* Step #3: mop up with disabled values for HAS_ options not handled by the above two */ #ifndef HAS_WIFI @@ -324,4 +350,4 @@ along with this program. If not, see . #endif #include "DebugConfiguration.h" -#include "RF95Configuration.h" \ No newline at end of file +#include "RF95Configuration.h" diff --git a/src/detect/LoRaRadioType.h b/src/detect/LoRaRadioType.h index 3975153b5..a059a3668 100644 --- a/src/detect/LoRaRadioType.h +++ b/src/detect/LoRaRadioType.h @@ -10,7 +10,8 @@ enum LoRaRadioType { LLCC68_RADIO, SX1280_RADIO, LR1110_RADIO, - LR1120_RADIO + LR1120_RADIO, + LR1121_RADIO }; extern LoRaRadioType radioType; \ No newline at end of file diff --git a/src/detect/ScanI2C.cpp b/src/detect/ScanI2C.cpp index 73bdf973b..a9d70edaa 100644 --- a/src/detect/ScanI2C.cpp +++ b/src/detect/ScanI2C.cpp @@ -37,8 +37,8 @@ ScanI2C::FoundDevice ScanI2C::firstKeyboard() const ScanI2C::FoundDevice ScanI2C::firstAccelerometer() const { - ScanI2C::DeviceType types[] = {MPU6050, LIS3DH, BMA423, LSM6DS3, BMX160}; - return firstOfOrNONE(5, types); + ScanI2C::DeviceType types[] = {MPU6050, LIS3DH, BMA423, LSM6DS3, BMX160, STK8BAXX, ICM20948}; + return firstOfOrNONE(7, types); } ScanI2C::FoundDevice ScanI2C::find(ScanI2C::DeviceType) const diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h index 0a5b360de..920af06c7 100644 --- a/src/detect/ScanI2C.h +++ b/src/detect/ScanI2C.h @@ -28,6 +28,7 @@ class ScanI2C INA260, INA219, INA3221, + MAX17048, MCP9808, SHT31, SHT4X, @@ -36,12 +37,14 @@ class ScanI2C QMC6310, QMI8658, QMC5883L, + HMC5883L, PMSA0031, MPU6050, LIS3DH, BMA423, BQ24295, LSM6DS3, + TCA9535, TCA9555, VEML7700, RCWL9620, @@ -49,10 +52,15 @@ class ScanI2C TSL2591, OPT3001, MLX90632, + MLX90614, AHT10, BMX160, DFROBOT_LARK, - NAU7802 + NAU7802, + FT6336U, + STK8BAXX, + ICM20948, + MAX30102 } DeviceType; // typedef uint8_t DeviceAddress; @@ -63,8 +71,9 @@ class ScanI2C } I2CPort; typedef struct DeviceAddress { - I2CPort port; - uint8_t address; + // set default values for ADDRESS_NONE + I2CPort port = I2CPort::NO_I2C; + uint8_t address = 0; explicit DeviceAddress(I2CPort port, uint8_t address); DeviceAddress(); diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index 326eb4165..0fec2d63f 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -162,13 +162,13 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) Melopero_RV3028 rtc; #endif -#ifdef I2C_SDA1 +#if WIRE_INTERFACES_COUNT == 2 if (port == I2CPort::WIRE1) { i2cBus = &Wire1; } else { #endif i2cBus = &Wire; -#ifdef I2C_SDA1 +#if WIRE_INTERFACES_COUNT == 2 } #endif @@ -313,17 +313,33 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) } break; case MCP9808_ADDR: - registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x07), 2); - if (registerValue == 0x0400) { - type = MCP9808; - LOG_INFO("MCP9808 sensor found\n"); - } else { - type = LIS3DH; - LOG_INFO("LIS3DH accelerometer found\n"); + // We need to check for STK8BAXX first, since register 0x07 is new data flag for the z-axis and can produce some + // weird result. and register 0x00 doesn't seems to be colliding with MCP9808 and LIS3DH chips. + { + // Check register 0x00 for 0x8700 response to ID STK8BA53 chip. + registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 2); + if (registerValue == 0x8700) { + type = STK8BAXX; + LOG_INFO("STK8BAXX accelerometer found\n"); + break; + } + + // Check register 0x07 for 0x0400 response to ID MCP9808 chip. + registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x07), 2); + if (registerValue == 0x0400) { + type = MCP9808; + LOG_INFO("MCP9808 sensor found\n"); + break; + } + + // Check register 0x0F for 0x3300 response to ID LIS3DH chip. + registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0F), 2); + if (registerValue == 0x3300 || registerValue == 0x3333) { // RAK4631 WisBlock has LIS3DH register at 0x3333 + type = LIS3DH; + LOG_INFO("LIS3DH accelerometer found\n"); + } + break; } - - break; - case SHT31_4x_ADDR: registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x89), 2); if (registerValue == 0x11a2 || registerValue == 0x11da || registerValue == 0xe9c) { @@ -340,7 +356,18 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) break; SCAN_SIMPLE_CASE(SHTC3_ADDR, SHTC3, "SHTC3 sensor found\n") - SCAN_SIMPLE_CASE(RCWL9620_ADDR, RCWL9620, "RCWL9620 sensor found\n") + case RCWL9620_ADDR: + // get MAX30102 PARTID + registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xFF), 1); + if (registerValue == 0x15) { + type = MAX30102; + LOG_INFO("MAX30102 Health sensor found\n"); + break; + } else { + type = RCWL9620; + LOG_INFO("RCWL9620 sensor found\n"); + } + break; case LPS22HB_ADDR_ALT: SCAN_SIMPLE_CASE(LPS22HB_ADDR, LPS22HB, "LPS22HB sensor found\n") @@ -365,18 +392,38 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) break; SCAN_SIMPLE_CASE(QMC5883L_ADDR, QMC5883L, "QMC5883L Highrate 3-Axis magnetic sensor found\n") - + SCAN_SIMPLE_CASE(HMC5883L_ADDR, HMC5883L, "HMC5883L 3-Axis digital compass found\n") SCAN_SIMPLE_CASE(PMSA0031_ADDR, PMSA0031, "PMSA0031 air quality sensor found\n") - SCAN_SIMPLE_CASE(MPU6050_ADDR, MPU6050, "MPU6050 accelerometer found\n"); - SCAN_SIMPLE_CASE(BMX160_ADDR, BMX160, "BMX160 accelerometer found\n"); SCAN_SIMPLE_CASE(BMA423_ADDR, BMA423, "BMA423 accelerometer found\n"); SCAN_SIMPLE_CASE(LSM6DS3_ADDR, LSM6DS3, "LSM6DS3 accelerometer found at address 0x%x\n", (uint8_t)addr.address); + SCAN_SIMPLE_CASE(TCA9535_ADDR, TCA9535, "TCA9535 I2C expander found\n"); SCAN_SIMPLE_CASE(TCA9555_ADDR, TCA9555, "TCA9555 I2C expander found\n"); SCAN_SIMPLE_CASE(VEML7700_ADDR, VEML7700, "VEML7700 light sensor found\n"); SCAN_SIMPLE_CASE(TSL25911_ADDR, TSL2591, "TSL2591 light sensor found\n"); SCAN_SIMPLE_CASE(OPT3001_ADDR, OPT3001, "OPT3001 light sensor found\n"); SCAN_SIMPLE_CASE(MLX90632_ADDR, MLX90632, "MLX90632 IR temp sensor found\n"); SCAN_SIMPLE_CASE(NAU7802_ADDR, NAU7802, "NAU7802 based scale found\n"); + SCAN_SIMPLE_CASE(FT6336U_ADDR, FT6336U, "FT6336U touchscreen found\n"); + SCAN_SIMPLE_CASE(MAX1704X_ADDR, MAX17048, "MAX17048 lipo fuel gauge found\n"); + SCAN_SIMPLE_CASE(MLX90614_ADDR_DEF, MLX90614, "MLX90614 IR temp sensor found\n"); + + case ICM20948_ADDR: // same as BMX160_ADDR + case ICM20948_ADDR_ALT: // same as MPU6050_ADDR + registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1); + if (registerValue == 0xEA) { + type = ICM20948; + LOG_INFO("ICM20948 9-dof motion processor found\n"); + break; + } else if (addr.address == BMX160_ADDR) { + type = BMX160; + LOG_INFO("BMX160 accelerometer found\n"); + break; + } else { + type = MPU6050; + LOG_INFO("MPU6050 accelerometer found\n"); + break; + } + break; default: LOG_INFO("Device found at address 0x%x was not able to be enumerated\n", addr.address); @@ -403,7 +450,7 @@ TwoWire *ScanI2CTwoWire::fetchI2CBus(ScanI2C::DeviceAddress address) const if (address.port == ScanI2C::I2CPort::WIRE) { return &Wire; } else { -#ifdef I2C_SDA1 +#if WIRE_INTERFACES_COUNT == 2 return &Wire1; #else return &Wire; @@ -415,4 +462,4 @@ size_t ScanI2CTwoWire::countDevices() const { return foundDevices.size(); } -#endif +#endif \ No newline at end of file diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 066c12f5b..7296610ad 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -6,6 +6,8 @@ #include "NodeDB.h" #include "PowerMon.h" #include "RTC.h" +#include "Throttle.h" +#include "meshUtils.h" #include "main.h" // pmu_found #include "sleep.h" @@ -26,6 +28,8 @@ #if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) HardwareSerial *GPS::_serial_gps = &Serial1; +#elif defined(ARCH_RP2040) +SerialUART *GPS::_serial_gps = &Serial1; #else HardwareSerial *GPS::_serial_gps = NULL; #endif @@ -58,7 +62,8 @@ const char *getGPSPowerStateString(GPSPowerState state) case GPS_OFF: return "OFF"; default: - assert(false); // Unhandled enum value.. + assert(false); // Unhandled enum value.. + return "FALSE"; // to make new ESP-IDF happy } } @@ -86,9 +91,9 @@ void GPS::CASChecksum(uint8_t *message, size_t length) // Iterate over the payload as a series of uint32_t's and // accumulate the cksum - uint32_t const *payload = (uint32_t *)(message + 6); for (size_t i = 0; i < (length - 10) / 4; i++) { - uint32_t pl = payload[i]; + uint32_t pl = 0; + memcpy(&pl, (message + 6) + (i * sizeof(uint32_t)), sizeof(uint32_t)); // avoid pointer dereference cksum += pl; } @@ -166,18 +171,21 @@ GPS_RESPONSE GPS::getACK(const char *message, uint32_t waitMillis) b = _serial_gps->read(); #ifdef GPS_DEBUG - LOG_DEBUG("%02X", (char *)buffer); + LOG_DEBUG("%c", (b >= 32 && b <= 126) ? b : '.'); #endif buffer[bytesRead] = b; bytesRead++; if ((bytesRead == 767) || (b == '\r')) { if (strnstr((char *)buffer, message, bytesRead) != nullptr) { #ifdef GPS_DEBUG - LOG_DEBUG("\r"); + LOG_DEBUG("\r\nFound: %s\r\n", message); // Log the found message #endif return GNSS_RESPONSE_OK; } else { bytesRead = 0; +#ifdef GPS_DEBUG + LOG_DEBUG("\r\n"); +#endif } } } @@ -201,7 +209,7 @@ GPS_RESPONSE GPS::getACKCas(uint8_t class_id, uint8_t msg_id, uint32_t waitMilli // ACK-NACK| 0xBA | 0xCE | 0x04 | 0x00 | 0x05 | 0x00 | 0xXX | 0xXX | 0x00 | 0x00 | 0xXX | 0xXX | 0xXX | 0xXX | // ACK-ACK | 0xBA | 0xCE | 0x04 | 0x00 | 0x05 | 0x01 | 0xXX | 0xXX | 0x00 | 0x00 | 0xXX | 0xXX | 0xXX | 0xXX | - while (millis() - startTime < waitMillis) { + while (Throttle::isWithinTimespanMs(startTime, waitMillis)) { if (_serial_gps->available()) { buffer[bufferPos++] = _serial_gps->read(); @@ -270,7 +278,7 @@ GPS_RESPONSE GPS::getACK(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis) buf[9] += buf[8]; } - while (millis() - startTime < waitMillis) { + while (Throttle::isWithinTimespanMs(startTime, waitMillis)) { if (ack > 9) { #ifdef GPS_DEBUG LOG_DEBUG("\n"); @@ -325,9 +333,9 @@ int GPS::getACK(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t { uint16_t ubxFrameCounter = 0; uint32_t startTime = millis(); - uint16_t needRead; + uint16_t needRead = 0; - while (millis() - startTime < waitMillis) { + while (Throttle::isWithinTimespanMs(startTime, waitMillis)) { if (_serial_gps->available()) { int c = _serial_gps->read(); switch (ubxFrameCounter) { @@ -398,9 +406,9 @@ int GPS::getACK(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t bool GPS::setup() { - int msglen = 0; if (!didSerialInit) { + int msglen = 0; if (tx_gpio && gnssModel == GNSS_MODEL_UNKNOWN) { // if GPS_BAUDRATE is specified in variant (i.e. not 9600), skip to the specified rate. @@ -505,283 +513,110 @@ bool GPS::setup() delay(250); _serial_gps->write("$CFGMSG,6,1,0\r\n"); delay(250); - } else if (gnssModel == GNSS_MODEL_AG3335) { + } else if (IS_ONE_OF(gnssModel, GNSS_MODEL_AG3335, GNSS_MODEL_AG3352)) { - _serial_gps->write("$PAIR066,1,0,1,0,0,1*3B"); // Enable GPS+GALILEO+NAVIC + _serial_gps->write("$PAIR066,1,0,1,0,0,1*3B\r\n"); // Enable GPS+GALILEO+NAVIC // Configure NMEA (sentences will output once per fix) - _serial_gps->write("$PAIR062,0,0*3F"); // GGA ON - _serial_gps->write("$PAIR062,1,0*3F"); // GLL OFF - _serial_gps->write("$PAIR062,2,1*3D"); // GSA ON - _serial_gps->write("$PAIR062,3,0*3D"); // GSV OFF - _serial_gps->write("$PAIR062,4,0*3B"); // RMC ON - _serial_gps->write("$PAIR062,5,0*3B"); // VTG OFF - _serial_gps->write("$PAIR062,6,1*39"); // ZDA ON + _serial_gps->write("$PAIR062,0,1*3F\r\n"); // GGA ON + _serial_gps->write("$PAIR062,1,0*3F\r\n"); // GLL OFF + _serial_gps->write("$PAIR062,2,0*3C\r\n"); // GSA OFF + _serial_gps->write("$PAIR062,3,0*3D\r\n"); // GSV OFF + _serial_gps->write("$PAIR062,4,1*3B\r\n"); // RMC ON + _serial_gps->write("$PAIR062,5,0*3B\r\n"); // VTG OFF + _serial_gps->write("$PAIR062,6,0*38\r\n"); // ZDA ON delay(250); - _serial_gps->write("$PAIR513*3D"); // save configuration + _serial_gps->write("$PAIR513*3D\r\n"); // save configuration + } else if (gnssModel == GNSS_MODEL_UBLOX6) { + clearBuffer(); + SEND_UBX_PACKET(0x06, 0x02, _message_DISABLE_TXT_INFO, "disable text info messages", 500); + SEND_UBX_PACKET(0x06, 0x39, _message_JAM_6_7, "enable interference resistance", 500); + SEND_UBX_PACKET(0x06, 0x23, _message_NAVX5, "configure NAVX5 settings", 500); - } else if (gnssModel == GNSS_MODEL_UBLOX) { - // Configure GNSS system to GPS+SBAS+GLONASS (Module may restart after this command) - // We need set it because by default it is GPS only, and we want to use GLONASS too - // Also we need SBAS for better accuracy and extra features - // ToDo: Dynamic configure GNSS systems depending of LoRa region + // Turn off unwanted NMEA messages, set update rate + SEND_UBX_PACKET(0x06, 0x08, _message_1HZ, "set GPS update rate", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_GLL, "disable NMEA GLL", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_GSA, "enable NMEA GSA", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_GSV, "disable NMEA GSV", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_VTG, "disable NMEA VTG", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_RMC, "enable NMEA RMC", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_GGA, "enable NMEA GGA", 500); - if (strncmp(info.hwVersion, "000A0000", 8) != 0) { - if (strncmp(info.hwVersion, "00040007", 8) != 0) { - // The original ublox Neo-6 is GPS only and doesn't support the UBX-CFG-GNSS message - // Max7 seems to only support GPS *or* GLONASS - // Neo-7 is supposed to support GPS *and* GLONASS but NAKs the CFG-GNSS command to do it - // So treat all the u-blox 7 series as GPS only - // M8 can support 3 constallations at once so turn on GPS, GLONASS and Galileo (or BeiDou) + clearBuffer(); + SEND_UBX_PACKET(0x06, 0x11, _message_CFG_RXM_ECO, "enable powersaving ECO mode for Neo-6", 500); + SEND_UBX_PACKET(0x06, 0x3B, _message_CFG_PM2, "enable powersaving details for GPS", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_AID, "disable UBX-AID", 500); - if (strncmp(info.hwVersion, "00070000", 8) == 0) { - LOG_DEBUG("Setting GPS+SBAS\n"); - msglen = makeUBXPacket(0x06, 0x3e, sizeof(_message_GNSS_7), _message_GNSS_7); - _serial_gps->write(UBXscratch, msglen); - } else { - msglen = makeUBXPacket(0x06, 0x3e, sizeof(_message_GNSS_8), _message_GNSS_8); - _serial_gps->write(UBXscratch, msglen); - } + msglen = makeUBXPacket(0x06, 0x09, sizeof(_message_SAVE), _message_SAVE); + _serial_gps->write(UBXscratch, msglen); + if (getACK(0x06, 0x09, 2000) != GNSS_RESPONSE_OK) { + LOG_WARN("Unable to save GNSS module configuration.\n"); + } else { + LOG_INFO("GNSS module configuration saved!\n"); + } + } else if (IS_ONE_OF(gnssModel, GNSS_MODEL_UBLOX7, GNSS_MODEL_UBLOX8, GNSS_MODEL_UBLOX9)) { + if (gnssModel == GNSS_MODEL_UBLOX7) { + LOG_DEBUG("Setting GPS+SBAS\n"); + msglen = makeUBXPacket(0x06, 0x3e, sizeof(_message_GNSS_7), _message_GNSS_7); + _serial_gps->write(UBXscratch, msglen); + } else { // 8,9 + msglen = makeUBXPacket(0x06, 0x3e, sizeof(_message_GNSS_8), _message_GNSS_8); + _serial_gps->write(UBXscratch, msglen); + } - if (getACK(0x06, 0x3e, 800) == GNSS_RESPONSE_NAK) { - // It's not critical if the module doesn't acknowledge this configuration. - LOG_INFO("Unable to reconfigure GNSS - defaults maintained. Is this module GPS-only?\n"); - } else { - if (strncmp(info.hwVersion, "00070000", 8) == 0) { - LOG_INFO("GNSS configured for GPS+SBAS. Pause for 0.75s before sending next command.\n"); - } else { - LOG_INFO( - "GNSS configured for GPS+SBAS+GLONASS+Galileo. Pause for 0.75s before sending next command.\n"); - } - // Documentation say, we need wait atleast 0.5s after reconfiguration of GNSS module, before sending next - // commands for the M8 it tends to be more... 1 sec should be enough ;>) - delay(1000); - } + if (getACK(0x06, 0x3e, 800) == GNSS_RESPONSE_NAK) { + // It's not critical if the module doesn't acknowledge this configuration. + LOG_INFO("reconfigure GNSS - defaults maintained. Is this module GPS-only?\n"); + } else { + if (gnssModel == GNSS_MODEL_UBLOX7) { + LOG_INFO("GNSS configured for GPS+SBAS.\n"); + } else { // 8,9 + LOG_INFO("GNSS configured for GPS+SBAS+GLONASS+Galileo.\n"); } - // Disable Text Info messages - msglen = makeUBXPacket(0x06, 0x02, sizeof(_message_DISABLE_TXT_INFO), _message_DISABLE_TXT_INFO); + // Documentation say, we need wait atleast 0.5s after reconfiguration of GNSS module, before sending next + // commands for the M8 it tends to be more... 1 sec should be enough ;>) + delay(1000); + } + + // Disable Text Info messages //6,7,8,9 + clearBuffer(); + SEND_UBX_PACKET(0x06, 0x02, _message_DISABLE_TXT_INFO, "disable text info messages", 500); + + if (gnssModel == GNSS_MODEL_UBLOX8) { // 8 clearBuffer(); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x02, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to disable text info messages.\n"); - } - // ToDo add M10 tests for below - if (strncmp(info.hwVersion, "00080000", 8) == 0) { - msglen = makeUBXPacket(0x06, 0x39, sizeof(_message_JAM_8), _message_JAM_8); + SEND_UBX_PACKET(0x06, 0x39, _message_JAM_8, "enable interference resistance", 500); + + clearBuffer(); + SEND_UBX_PACKET(0x06, 0x23, _message_NAVX5_8, "configure NAVX5_8 settings", 500); + } else { // 6,7,9 + SEND_UBX_PACKET(0x06, 0x39, _message_JAM_6_7, "enable interference resistance", 500); + SEND_UBX_PACKET(0x06, 0x23, _message_NAVX5, "configure NAVX5 settings", 500); + } + // Turn off unwanted NMEA messages, set update rate + SEND_UBX_PACKET(0x06, 0x08, _message_1HZ, "set GPS update rate", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_GLL, "disable NMEA GLL", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_GSA, "enable NMEA GSA", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_GSV, "disable NMEA GSV", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_VTG, "disable NMEA VTG", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_RMC, "enable NMEA RMC", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_GGA, "enable NMEA GGA", 500); + + if (uBloxProtocolVersion >= 18) { + clearBuffer(); + SEND_UBX_PACKET(0x06, 0x86, _message_PMS, "enable powersaving for GPS", 500); + SEND_UBX_PACKET(0x06, 0x3B, _message_CFG_PM2, "enable powersaving details for GPS", 500); + + // For M8 we want to enable NMEA vserion 4.10 so we can see the additional sats. + if (gnssModel == GNSS_MODEL_UBLOX8) { clearBuffer(); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x39, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable interference resistance.\n"); - } - - msglen = makeUBXPacket(0x06, 0x23, sizeof(_message_NAVX5_8), _message_NAVX5_8); - clearBuffer(); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x23, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to configure NAVX5_8 settings.\n"); - } - } else { - msglen = makeUBXPacket(0x06, 0x39, sizeof(_message_JAM_6_7), _message_JAM_6_7); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x39, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable interference resistance.\n"); - } - - msglen = makeUBXPacket(0x06, 0x23, sizeof(_message_NAVX5), _message_NAVX5); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x23, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to configure NAVX5 settings.\n"); - } - } - // Turn off unwanted NMEA messages, set update rate - - msglen = makeUBXPacket(0x06, 0x08, sizeof(_message_1HZ), _message_1HZ); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x08, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to set GPS update rate.\n"); - } - - msglen = makeUBXPacket(0x06, 0x01, sizeof(_message_GLL), _message_GLL); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x01, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to disable NMEA GLL.\n"); - } - - msglen = makeUBXPacket(0x06, 0x01, sizeof(_message_GSA), _message_GSA); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x01, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to Enable NMEA GSA.\n"); - } - - msglen = makeUBXPacket(0x06, 0x01, sizeof(_message_GSV), _message_GSV); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x01, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to disable NMEA GSV.\n"); - } - - msglen = makeUBXPacket(0x06, 0x01, sizeof(_message_VTG), _message_VTG); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x01, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to disable NMEA VTG.\n"); - } - - msglen = makeUBXPacket(0x06, 0x01, sizeof(_message_RMC), _message_RMC); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x01, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable NMEA RMC.\n"); - } - - msglen = makeUBXPacket(0x06, 0x01, sizeof(_message_GGA), _message_GGA); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x01, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable NMEA GGA.\n"); - } - - if (uBloxProtocolVersion >= 18) { - msglen = makeUBXPacket(0x06, 0x86, sizeof(_message_PMS), _message_PMS); - clearBuffer(); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x86, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable powersaving for GPS.\n"); - } - msglen = makeUBXPacket(0x06, 0x3B, sizeof(_message_CFG_PM2), _message_CFG_PM2); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x3B, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable powersaving details for GPS.\n"); - } - // For M8 we want to enable NMEA vserion 4.10 so we can see the additional sats. - if (strncmp(info.hwVersion, "00080000", 8) == 0) { - msglen = makeUBXPacket(0x06, 0x17, sizeof(_message_NMEA), _message_NMEA); - clearBuffer(); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x17, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable NMEA 4.10.\n"); - } - } - } else { - if (strncmp(info.hwVersion, "00040007", 8) == 0) { // This PSM mode is only for Neo-6 - msglen = makeUBXPacket(0x06, 0x11, 0x2, _message_CFG_RXM_ECO); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x11, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable powersaving ECO mode for Neo-6.\n"); - } - msglen = makeUBXPacket(0x06, 0x3B, sizeof(_message_CFG_PM2), _message_CFG_PM2); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x3B, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable powersaving details for GPS.\n"); - } - msglen = makeUBXPacket(0x06, 0x01, sizeof(_message_AID), _message_AID); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x01, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to disable UBX-AID.\n"); - } - } else { - msglen = makeUBXPacket(0x06, 0x11, 0x2, _message_CFG_RXM_PSM); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x11, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable powersaving mode for GPS.\n"); - } - - msglen = makeUBXPacket(0x06, 0x3B, sizeof(_message_CFG_PM2), _message_CFG_PM2); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x3B, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable powersaving details for GPS.\n"); - } - } + SEND_UBX_PACKET(0x06, 0x17, _message_NMEA, "enable NMEA 4.10", 500); } } else { - // LOG_INFO("u-blox M10 hardware found.\n"); - delay(1000); - // First disable all NMEA messages in RAM layer - msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_DISABLE_NMEA_RAM), _message_VALSET_DISABLE_NMEA_RAM); - clearBuffer(); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to disable NMEA messages for M10 GPS RAM.\n"); - } - delay(250); - // Next disable unwanted NMEA messages in BBR layer - msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_DISABLE_NMEA_BBR), _message_VALSET_DISABLE_NMEA_BBR); - clearBuffer(); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to disable NMEA messages for M10 GPS BBR.\n"); - } - delay(250); - // Disable Info txt messages in RAM layer - msglen = - makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_DISABLE_TXT_INFO_RAM), _message_VALSET_DISABLE_TXT_INFO_RAM); - clearBuffer(); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to disable Info messages for M10 GPS RAM.\n"); - } - delay(250); - // Next disable Info txt messages in BBR layer - msglen = - makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_DISABLE_TXT_INFO_BBR), _message_VALSET_DISABLE_TXT_INFO_BBR); - clearBuffer(); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to disable Info messages for M10 GPS BBR.\n"); - } - // Do M10 configuration for Power Management. - - msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_PM_RAM), _message_VALSET_PM_RAM); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable powersaving for M10 GPS RAM.\n"); - } - msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_PM_BBR), _message_VALSET_PM_BBR); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable powersaving for M10 GPS BBR.\n"); - } - - delay(250); - msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_ITFM_RAM), _message_VALSET_ITFM_RAM); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable Jamming detection M10 GPS RAM.\n"); - } - msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_ITFM_BBR), _message_VALSET_ITFM_BBR); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable Jamming detection M10 GPS BBR.\n"); - } - - // Here is where the init commands should go to do further M10 initialization. - delay(250); - msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_DISABLE_SBAS_RAM), _message_VALSET_DISABLE_SBAS_RAM); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to disable SBAS M10 GPS RAM.\n"); - } - delay(750); // will cause a receiver restart so wait a bit - msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_DISABLE_SBAS_BBR), _message_VALSET_DISABLE_SBAS_BBR); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to disable SBAS M10 GPS BBR.\n"); - } - delay(750); // will cause a receiver restart so wait a bit - // Done with initialization, Now enable wanted NMEA messages in BBR layer so they will survive a periodic sleep. - msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_ENABLE_NMEA_BBR), _message_VALSET_ENABLE_NMEA_BBR); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable messages for M10 GPS BBR.\n"); - } - delay(250); - // Next enable wanted NMEA messages in RAM layer - msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_ENABLE_NMEA_RAM), _message_VALSET_ENABLE_NMEA_RAM); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable messages for M10 GPS RAM.\n"); - } - // As the M10 has no flash, the best we can do to preserve the config is to set it in RAM and BBR. - // BBR will survive a restart, and power off for a while, but modules with small backup - // batteries or super caps will not retain the config for a long power off time. + SEND_UBX_PACKET(0x06, 0x11, _message_CFG_RXM_PSM, "enable powersaving mode for GPS", 500); + SEND_UBX_PACKET(0x06, 0x3B, _message_CFG_PM2, "enable powersaving details for GPS", 500); } + msglen = makeUBXPacket(0x06, 0x09, sizeof(_message_SAVE), _message_SAVE); _serial_gps->write(UBXscratch, msglen); if (getACK(0x06, 0x09, 2000) != GNSS_RESPONSE_OK) { @@ -789,6 +624,53 @@ bool GPS::setup() } else { LOG_INFO("GNSS module configuration saved!\n"); } + } else if (gnssModel == GNSS_MODEL_UBLOX10) { + delay(1000); + clearBuffer(); + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_DISABLE_NMEA_RAM, "disable NMEA messages in M10 RAM", 300); + delay(750); + clearBuffer(); + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_DISABLE_NMEA_BBR, "disable NMEA messages in M10 BBR", 300); + delay(750); + clearBuffer(); + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_DISABLE_TXT_INFO_RAM, "disable Info messages for M10 GPS RAM", 300); + delay(750); + // Next disable Info txt messages in BBR layer + clearBuffer(); + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_DISABLE_TXT_INFO_BBR, "disable Info messages for M10 GPS BBR", 300); + delay(750); + // Do M10 configuration for Power Management. + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_PM_RAM, "enable powersaving for M10 GPS RAM", 300); + delay(750); + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_PM_BBR, "enable powersaving for M10 GPS BBR", 300); + delay(750); + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_ITFM_RAM, "enable Jamming detection M10 GPS RAM", 300); + delay(750); + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_ITFM_BBR, "enable Jamming detection M10 GPS BBR", 300); + delay(750); + // Here is where the init commands should go to do further M10 initialization. + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_DISABLE_SBAS_RAM, "disable SBAS M10 GPS RAM", 300); + delay(750); // will cause a receiver restart so wait a bit + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_DISABLE_SBAS_BBR, "disable SBAS M10 GPS BBR", 300); + delay(750); // will cause a receiver restart so wait a bit + + // Done with initialization, Now enable wanted NMEA messages in BBR layer so they will survive a periodic sleep. + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_ENABLE_NMEA_BBR, "enable messages for M10 GPS BBR", 300); + delay(750); + // Next enable wanted NMEA messages in RAM layer + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_ENABLE_NMEA_RAM, "enable messages for M10 GPS RAM", 500); + delay(750); + + // As the M10 has no flash, the best we can do to preserve the config is to set it in RAM and BBR. + // BBR will survive a restart, and power off for a while, but modules with small backup + // batteries or super caps will not retain the config for a long power off time. + msglen = makeUBXPacket(0x06, 0x09, sizeof(_message_SAVE_10), _message_SAVE_10); + _serial_gps->write(UBXscratch, msglen); + if (getACK(0x06, 0x09, 2000) != GNSS_RESPONSE_OK) { + LOG_WARN("Unable to save GNSS module configuration.\n"); + } else { + LOG_INFO("GNSS module configuration saved!\n"); + } } didSerialInit = true; } @@ -944,7 +826,7 @@ void GPS::setPowerPMU(bool on) void GPS::setPowerUBLOX(bool on, uint32_t sleepMs) { // Abort: if not UBLOX hardware - if (gnssModel != GNSS_MODEL_UBLOX) + if (!IS_ONE_OF(gnssModel, GNSS_MODEL_UBLOX6, GNSS_MODEL_UBLOX7, GNSS_MODEL_UBLOX8, GNSS_MODEL_UBLOX9, GNSS_MODEL_UBLOX10)) return; // If waking @@ -967,7 +849,7 @@ void GPS::setPowerUBLOX(bool on, uint32_t sleepMs) } // Determine hardware version - if (strncmp(info.hwVersion, "000A0000", 8) != 0) { + if (gnssModel == GNSS_MODEL_UBLOX10) { // Encode the sleep time in millis into the packet for (int i = 0; i < 4; i++) gps->_message_PMREQ[0 + i] = sleepMs >> (i * 8); @@ -1019,33 +901,36 @@ void GPS::down() LOG_DEBUG("%us until next search\n", sleepTime / 1000); // If update interval less than 10 seconds, no attempt to sleep - if (updateInterval <= 10 * 1000UL) + if (updateInterval <= 10 * 1000UL || sleepTime == 0) setPowerState(GPS_IDLE); else { // Check whether the GPS hardware is capable of GPS_SOFTSLEEP // If not, fallback to GPS_HARDSLEEP instead bool softsleepSupported = false; - if (gnssModel == GNSS_MODEL_UBLOX) // U-blox is supported via PMREQ + // U-blox is supported via PMREQ + if (IS_ONE_OF(gnssModel, GNSS_MODEL_UBLOX6, GNSS_MODEL_UBLOX7, GNSS_MODEL_UBLOX8, GNSS_MODEL_UBLOX9, GNSS_MODEL_UBLOX10)) softsleepSupported = true; #ifdef PIN_GPS_STANDBY // L76B, L76K and clones have a standby pin softsleepSupported = true; #endif - // How long does gps_update_interval need to be, for GPS_HARDSLEEP to become more efficient than GPS_SOFTSLEEP? - // Heuristic equation. A compromise manually fitted to power observations from U-blox NEO-6M and M10050 - // https://www.desmos.com/calculator/6gvjghoumr - // This is not particularly accurate, but probably an impromevement over a single, fixed threshold - uint32_t hardsleepThreshold = (2750 * pow(predictedSearchDuration / 1000, 1.22)); - LOG_DEBUG("gps_update_interval >= %us needed to justify hardsleep\n", hardsleepThreshold / 1000); - - // If update interval too short: softsleep (if supported by hardware) - if (softsleepSupported && updateInterval < hardsleepThreshold) - setPowerState(GPS_SOFTSLEEP, sleepTime); + if (softsleepSupported) { + // How long does gps_update_interval need to be, for GPS_HARDSLEEP to become more efficient than GPS_SOFTSLEEP? + // Heuristic equation. A compromise manually fitted to power observations from U-blox NEO-6M and M10050 + // https://www.desmos.com/calculator/6gvjghoumr + // This is not particularly accurate, but probably an impromevement over a single, fixed threshold + uint32_t hardsleepThreshold = (2750 * pow(predictedSearchDuration / 1000, 1.22)); + LOG_DEBUG("gps_update_interval >= %us needed to justify hardsleep\n", hardsleepThreshold / 1000); + // If update interval too short: softsleep (if supported by hardware) + if (updateInterval < hardsleepThreshold) { + setPowerState(GPS_SOFTSLEEP, sleepTime); + return; + } + } // If update interval long enough (or softsleep unsupported): hardsleep instead - else - setPowerState(GPS_HARDSLEEP, sleepTime); + setPowerState(GPS_HARDSLEEP, sleepTime); } } @@ -1101,7 +986,9 @@ int32_t GPS::runOnce() // if we have received valid NMEA claim we are connected setConnected(); } else { - if ((config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) && (gnssModel == GNSS_MODEL_UBLOX)) { + if ((config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) && + IS_ONE_OF(gnssModel, GNSS_MODEL_UBLOX6, GNSS_MODEL_UBLOX7, GNSS_MODEL_UBLOX8, GNSS_MODEL_UBLOX9, + GNSS_MODEL_UBLOX10)) { // reset the GPS on next bootup if (devicestate.did_gps_reset && scheduling.elapsedSearchMs() > 60 * 1000UL && !hasFlow()) { LOG_DEBUG("GPS is not communicating, trying factory reset on next bootup.\n"); @@ -1184,25 +1071,34 @@ int GPS::prepareDeepSleep(void *unused) return 0; } +const char *PROBE_MESSAGE = "Trying %s (%s)...\n"; +const char *DETECTED_MESSAGE = "%s detected, using %s Module\n"; + +#define PROBE_SIMPLE(CHIP, TOWRITE, RESPONSE, DRIVER, TIMEOUT, ...) \ + LOG_DEBUG(PROBE_MESSAGE, TOWRITE, CHIP); \ + clearBuffer(); \ + _serial_gps->write(TOWRITE "\r\n"); \ + if (getACK(RESPONSE, TIMEOUT) == GNSS_RESPONSE_OK) { \ + LOG_INFO(DETECTED_MESSAGE, CHIP, #DRIVER); \ + return DRIVER; \ + } + GnssModel_t GPS::probe(int serialSpeed) { -#if defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) || defined(ARCH_RP2040) || defined(ARCH_STM32WL) || defined(ARCH_APOLLO3) +#if defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) || defined(ARCH_STM32WL) || defined(ARCH_APOLLO3) _serial_gps->end(); _serial_gps->begin(serialSpeed); +#elif defined(ARCH_RP2040) + _serial_gps->end(); + _serial_gps->setFIFOSize(256); + _serial_gps->begin(serialSpeed); #else if (_serial_gps->baudRate() != serialSpeed) { LOG_DEBUG("Setting Baud to %i\n", serialSpeed); _serial_gps->updateBaudRate(serialSpeed); } #endif -#ifdef GNSS_AIROHA - return GNSS_MODEL_AG3335; -#endif -#ifdef GPS_DEBUG - for (int i = 0; i < 20; i++) { - getACK("$GP", 200); - } -#endif + memset(&info, 0, sizeof(struct uBloxGnssModelInfo)); uint8_t buffer[768] = {0}; delay(100); @@ -1211,70 +1107,29 @@ GnssModel_t GPS::probe(int serialSpeed) _serial_gps->write("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n"); delay(20); - // get version information from Unicore UFirebirdII Series - // Works for: UC6580, UM620, UM621, UM670A, UM680A, or UM681A - _serial_gps->write("$PDTINFO\r\n"); - delay(750); - if (getACK("UC6580", 500) == GNSS_RESPONSE_OK) { - LOG_INFO("UC6580 detected, using UC6580 Module\n"); - return GNSS_MODEL_UC6580; - } - - clearBuffer(); - _serial_gps->write("$PDTINFO\r\n"); - delay(750); - if (getACK("UM600", 500) == GNSS_RESPONSE_OK) { - LOG_INFO("UM600 detected, using UC6580 Module\n"); - return GNSS_MODEL_UC6580; - } - - // Get version information for ATGM336H - clearBuffer(); - _serial_gps->write("$PCAS06,1*1A\r\n"); - if (getACK("$GPTXT,01,01,02,HW=ATGM336H", 500) == GNSS_RESPONSE_OK) { - LOG_INFO("ATGM336H GNSS init succeeded, using ATGM336H Module\n"); - return GNSS_MODEL_ATGM336H; - } - + // Unicore UFirebirdII Series: UC6580, UM620, UM621, UM670A, UM680A, or UM681A + PROBE_SIMPLE("UC6580", "$PDTINFO", "UC6580", GNSS_MODEL_UC6580, 500); + PROBE_SIMPLE("UM600", "$PDTINFO", "UM600", GNSS_MODEL_UC6580, 500); + PROBE_SIMPLE("ATGM336H", "$PCAS06,1*1A", "$GPTXT,01,01,02,HW=ATGM336H", GNSS_MODEL_ATGM336H, 500); /* ATGM332D series (-11(GPS), -21(BDS), -31(GPS+BDS), -51(GPS+GLONASS), -71-0(GPS+BDS+GLONASS)) based on AT6558 */ - clearBuffer(); - _serial_gps->write("$PCAS06,1*1A\r\n"); - if (getACK("$GPTXT,01,01,02,HW=ATGM332D", 500) == GNSS_RESPONSE_OK) { - LOG_INFO("ATGM332D detected, using ATGM336H Module\n"); - return GNSS_MODEL_ATGM336H; - } + PROBE_SIMPLE("ATGM332D", "$PCAS06,1*1A", "$GPTXT,01,01,02,HW=ATGM332D", GNSS_MODEL_ATGM336H, 500); /* Airoha (Mediatek) AG3335A/M/S, A3352Q, Quectel L89 2.0, SimCom SIM65M */ - clearBuffer(); - _serial_gps->write("PAIR020*38\r\n"); - if (getACK("$PAIR020,AG3335", 500) == GNSS_RESPONSE_OK) { - LOG_INFO("Aioha AG3335 detected, using AG3335 Module\n"); - return GNSS_MODEL_AG3335; - } - // Get version information for Airoha AG3335 - clearBuffer(); - _serial_gps->write("$PMTK605*31\r\n"); + _serial_gps->write("$PAIR062,2,0*3C\r\n"); // GSA OFF to reduce volume + _serial_gps->write("$PAIR062,3,0*3D\r\n"); // GSV OFF to reduce volume + _serial_gps->write("$PAIR513*3D\r\n"); // save configuration + PROBE_SIMPLE("AG3335", "$PAIR021*39", "$PAIR021,AG3335", GNSS_MODEL_AG3335, 500); + PROBE_SIMPLE("AG3352", "$PAIR021*39", "$PAIR021,AG3352", GNSS_MODEL_AG3352, 500); + PROBE_SIMPLE("LC86", "$PQTMVERNO*58", "$PQTMVERNO,LC86", GNSS_MODEL_AG3352, 500); - // Get version information - clearBuffer(); - _serial_gps->write("$PCAS06,0*1B\r\n"); - if (getACK("$GPTXT,01,01,02,SW=", 500) == GNSS_RESPONSE_OK) { - LOG_INFO("L76K GNSS init succeeded, using L76K GNSS Module\n"); - return GNSS_MODEL_MTK; - } + PROBE_SIMPLE("L76K", "$PCAS06,0*1B", "$GPTXT,01,01,02,SW=", GNSS_MODEL_MTK, 500); // Close all NMEA sentences, valid for L76B MTK platform (Waveshare Pico GPS) _serial_gps->write("$PMTK514,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*2E\r\n"); delay(20); - // Get version information - clearBuffer(); - _serial_gps->write("$PMTK605*31\r\n"); - if (getACK("Quectel-L76B", 500) == GNSS_RESPONSE_OK) { - LOG_INFO("L76B GNSS init succeeded, using L76B GNSS Module\n"); - return GNSS_MODEL_MTK_L76B; - } + PROBE_SIMPLE("L76B", "$PMTK605*31", "Quectel-L76B", GNSS_MODEL_MTK_L76B, 500); uint8_t cfg_rate[] = {0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x00, 0x00}; UBXChecksum(cfg_rate, sizeof(cfg_rate)); @@ -1301,9 +1156,13 @@ GnssModel_t GPS::probe(int serialSpeed) _serial_gps->write(_message_prt, sizeof(_message_prt)); delay(500); serialSpeed = 9600; -#if defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) || defined(ARCH_RP2040) || defined(ARCH_STM32WL) || defined(ARCH_APOLLO3) +#if defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) || defined(ARCH_STM32WL) || defined(ARCH_APOLLO3) _serial_gps->end(); _serial_gps->begin(serialSpeed); +#elif defined(ARCH_RP2040) + _serial_gps->end(); + _serial_gps->setFIFOSize(256); + _serial_gps->begin(serialSpeed); #else _serial_gps->updateBaudRate(serialSpeed); #endif @@ -1361,9 +1220,9 @@ GnssModel_t GPS::probe(int serialSpeed) strncpy((char *)buffer, &(info.extension[i][4]), sizeof(buffer)); // LOG_DEBUG("GetModel:%s\n", (char *)buffer); if (strlen((char *)buffer)) { - LOG_INFO("UBlox GNSS probe succeeded, using UBlox %s GNSS Module\n", (char *)buffer); + LOG_INFO("%s detected, using GNSS_MODEL_UBLOX\n", (char *)buffer); } else { - LOG_INFO("UBlox GNSS probe succeeded, using UBlox GNSS Module\n"); + LOG_INFO("Generic Ublox detected, using GNSS_MODEL_UBLOX\n"); } } else if (!strncmp(info.extension[i], "PROTVER", 7)) { char *ptr = nullptr; @@ -1378,9 +1237,20 @@ GnssModel_t GPS::probe(int serialSpeed) } } } + if (strncmp(info.hwVersion, "00040007", 8) == 0) { + return GNSS_MODEL_UBLOX6; + } else if (strncmp(info.hwVersion, "00070000", 8) == 0) { + return GNSS_MODEL_UBLOX7; + } else if (strncmp(info.hwVersion, "00080000", 8) == 0) { + return GNSS_MODEL_UBLOX8; + } else if (strncmp(info.hwVersion, "00190000", 8) == 0) { + return GNSS_MODEL_UBLOX9; + } else if (strncmp(info.hwVersion, "000A0000", 8) == 0) { + return GNSS_MODEL_UBLOX10; + } } - return GNSS_MODEL_UBLOX; + return GNSS_MODEL_UNKNOWN; } GPS *GPS::createGps() @@ -1464,6 +1334,9 @@ GPS *GPS::createGps() LOG_DEBUG("Using GPIO%d for GPS RX\n", new_gps->rx_gpio); LOG_DEBUG("Using GPIO%d for GPS TX\n", new_gps->tx_gpio); _serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, new_gps->rx_gpio, new_gps->tx_gpio); +#elif defined(ARCH_RP2040) + _serial_gps->setFIFOSize(256); + _serial_gps->begin(GPS_BAUDRATE); #else _serial_gps->begin(GPS_BAUDRATE); #endif @@ -1495,21 +1368,21 @@ bool GPS::factoryReset() 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1C, 0xA2}; _serial_gps->write(_message_reset1, sizeof(_message_reset1)); if (getACK(0x05, 0x01, 10000)) { - LOG_INFO("Get ack success!\n"); + LOG_INFO(ACK_SUCCESS_MESSAGE); } delay(100); byte _message_reset2[] = {0xB5, 0x62, 0x06, 0x09, 0x0D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1B, 0xA1}; _serial_gps->write(_message_reset2, sizeof(_message_reset2)); if (getACK(0x05, 0x01, 10000)) { - LOG_INFO("Get ack success!\n"); + LOG_INFO(ACK_SUCCESS_MESSAGE); } delay(100); byte _message_reset3[] = {0xB5, 0x62, 0x06, 0x09, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x03, 0x1D, 0xB3}; _serial_gps->write(_message_reset3, sizeof(_message_reset3)); if (getACK(0x05, 0x01, 10000)) { - LOG_INFO("Get ack success!\n"); + LOG_INFO(ACK_SUCCESS_MESSAGE); } // Reset device ram to COLDSTART state // byte _message_CFG_RST_COLDSTART[] = {0xB5, 0x62, 0x06, 0x04, 0x04, 0x00, 0xFF, 0xB9, 0x00, 0x00, 0xC6, 0x8B}; @@ -1551,16 +1424,15 @@ bool GPS::lookForTime() #ifdef GNSS_AIROHA uint8_t fix = reader.fixQuality(); - uint32_t now = millis(); if (fix > 0) { if (lastFixStartMsec > 0) { - if ((now - lastFixStartMsec) < GPS_FIX_HOLD_TIME) { + if (Throttle::isWithinTimespanMs(lastFixStartMsec, GPS_FIX_HOLD_TIME)) { return false; } else { clearBuffer(); } } else { - lastFixStartMsec = now; + lastFixStartMsec = millis(); return false; } } else { @@ -1575,7 +1447,7 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s (midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z). */ struct tm t; - t.tm_sec = ti.second(); + t.tm_sec = ti.second() + round(ti.age() / 1000); t.tm_min = ti.minute(); t.tm_hour = ti.hour(); t.tm_mday = d.day(); @@ -1583,8 +1455,8 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s t.tm_year = d.year() - 1900; t.tm_isdst = false; if (t.tm_mon > -1) { - LOG_DEBUG("NMEA GPS time %02d-%02d-%02d %02d:%02d:%02d\n", d.year(), d.month(), t.tm_mday, t.tm_hour, t.tm_min, - t.tm_sec); + LOG_DEBUG("NMEA GPS time %02d-%02d-%02d %02d:%02d:%02d age %d\n", d.year(), d.month(), t.tm_mday, t.tm_hour, t.tm_min, + t.tm_sec, ti.age()); perhapsSetRTC(RTCQualityGPS, t); return true; } else @@ -1604,16 +1476,15 @@ bool GPS::lookForLocation() #ifdef GNSS_AIROHA if ((config.position.gps_update_interval * 1000) >= (GPS_FIX_HOLD_TIME * 2)) { uint8_t fix = reader.fixQuality(); - uint32_t now = millis(); if (fix > 0) { if (lastFixStartMsec > 0) { - if ((now - lastFixStartMsec) < GPS_FIX_HOLD_TIME) { + if (Throttle::isWithinTimespanMs(lastFixStartMsec, GPS_FIX_HOLD_TIME)) { return false; } else { clearBuffer(); } } else { - lastFixStartMsec = now; + lastFixStartMsec = millis(); return false; } } else { diff --git a/src/gps/GPS.h b/src/gps/GPS.h index c0e9fb8b6..6222881bc 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -26,11 +26,16 @@ struct uBloxGnssModelInfo { typedef enum { GNSS_MODEL_ATGM336H, GNSS_MODEL_MTK, - GNSS_MODEL_UBLOX, + GNSS_MODEL_UBLOX6, + GNSS_MODEL_UBLOX7, + GNSS_MODEL_UBLOX8, + GNSS_MODEL_UBLOX9, + GNSS_MODEL_UBLOX10, GNSS_MODEL_UC6580, GNSS_MODEL_UNKNOWN, GNSS_MODEL_MTK_L76B, - GNSS_MODEL_AG3335 + GNSS_MODEL_AG3335, + GNSS_MODEL_AG3352 } GnssModel_t; typedef enum { @@ -101,7 +106,7 @@ class GPS : private concurrency::OSThread public: /** If !NULL we will use this serial port to construct our GPS */ -#if defined(RPI_PICO_WAVESHARE) +#if defined(ARCH_RP2040) static SerialUART *_serial_gps; #else static HardwareSerial *_serial_gps; @@ -129,6 +134,7 @@ class GPS : private concurrency::OSThread static const uint8_t _message_GGA[]; static const uint8_t _message_PMS[]; static const uint8_t _message_SAVE[]; + static const uint8_t _message_SAVE_10[]; // VALSET Commands for M10 static const uint8_t _message_VALSET_PM[]; @@ -150,6 +156,8 @@ class GPS : private concurrency::OSThread static const uint8_t _message_CAS_CFG_NAVX_CONF[]; static const uint8_t _message_CAS_CFG_RATE_1HZ[]; + const char *ACK_SUCCESS_MESSAGE = "Get ack success!\n"; + meshtastic_Position p = meshtastic_Position_init_default; /** This is normally bound to config.position.gps_en_gpio but some rare boards (like heltec tracker) need more advanced @@ -297,7 +305,6 @@ class GPS : private concurrency::OSThread virtual int32_t runOnce() override; // Get GNSS model - String getNMEA(); GnssModel_t probe(int serialSpeed); // delay counter to allow more sats before fixed position stops GPS thread diff --git a/src/gps/NMEAWPL.cpp b/src/gps/NMEAWPL.cpp index 71943b76c..f528c4607 100644 --- a/src/gps/NMEAWPL.cpp +++ b/src/gps/NMEAWPL.cpp @@ -75,10 +75,13 @@ uint32_t printWPL(char *buf, size_t bufsz, const meshtastic_Position &pos, const uint32_t printGGA(char *buf, size_t bufsz, const meshtastic_Position &pos) { GeoCoord geoCoord(pos.latitude_i, pos.longitude_i, pos.altitude); - tm *t = gmtime((time_t *)&pos.timestamp); + time_t timestamp = pos.timestamp; + + tm *t = gmtime(×tamp); if (getRTCQuality() > 0) { // use the device clock if we got time from somewhere. If not, use the GPS timestamp. uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice); - t = gmtime((time_t *)&rtc_sec); + timestamp = rtc_sec; + t = gmtime(×tamp); } uint32_t len = snprintf( diff --git a/src/gps/RTC.cpp b/src/gps/RTC.cpp index 070038672..d9ac56b74 100644 --- a/src/gps/RTC.cpp +++ b/src/gps/RTC.cpp @@ -2,6 +2,7 @@ #include "configuration.h" #include "detect/ScanI2C.h" #include "main.h" +#include #include #include @@ -29,7 +30,7 @@ void readFromRTC() if (rtc_found.address == RV3028_RTC) { uint32_t now = millis(); Melopero_RV3028 rtc; -#ifdef I2C_SDA1 +#if WIRE_INTERFACES_COUNT == 2 rtc.initI2C(rtc_found.port == ScanI2C::I2CPort::WIRE1 ? Wire1 : Wire); #else rtc.initI2C(); @@ -43,7 +44,10 @@ void readFromRTC() t.tm_sec = rtc.getSecond(); tv.tv_sec = gm_mktime(&t); tv.tv_usec = 0; - LOG_DEBUG("Read RTC time from RV3028 as %ld\n", tv.tv_sec); + + uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms + LOG_DEBUG("Read RTC time from RV3028 getTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)\n", t.tm_year + 1900, t.tm_mon + 1, + t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch); timeStartMsec = now; zeroOffsetSecs = tv.tv_sec; if (currentQuality == RTCQualityNone) { @@ -55,7 +59,7 @@ void readFromRTC() uint32_t now = millis(); PCF8563_Class rtc; -#ifdef I2C_SDA1 +#if WIRE_INTERFACES_COUNT == 2 rtc.begin(rtc_found.port == ScanI2C::I2CPort::WIRE1 ? Wire1 : Wire); #else rtc.begin(); @@ -71,7 +75,10 @@ void readFromRTC() t.tm_sec = tc.second; tv.tv_sec = gm_mktime(&t); tv.tv_usec = 0; - LOG_DEBUG("Read RTC time from PCF8563 as %ld\n", tv.tv_sec); + + uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms + LOG_DEBUG("Read RTC time from PCF8563 getDateTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)\n", t.tm_year + 1900, + t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch); timeStartMsec = now; zeroOffsetSecs = tv.tv_sec; if (currentQuality == RTCQualityNone) { @@ -81,7 +88,8 @@ void readFromRTC() #else if (!gettimeofday(&tv, NULL)) { uint32_t now = millis(); - LOG_DEBUG("Read RTC time as %ld\n", tv.tv_sec); + uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms + LOG_DEBUG("Read RTC time as %ld\n", printableEpoch); timeStartMsec = now; zeroOffsetSecs = tv.tv_sec; } @@ -101,6 +109,13 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate) { static uint32_t lastSetMsec = 0; uint32_t now = millis(); + uint32_t printableEpoch = tv->tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms +#ifdef BUILD_EPOCH + if (tv->tv_sec < BUILD_EPOCH) { + LOG_WARN("Ignoring time (%ld) before build epoch (%ld)!\n", printableEpoch, BUILD_EPOCH); + return false; + } +#endif bool shouldSet; if (forceUpdate) { @@ -110,10 +125,13 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate) } else if (q > currentQuality) { shouldSet = true; LOG_DEBUG("Upgrading time to quality %s\n", RtcName(q)); - } else if (q >= RTCQualityNTP && (now - lastSetMsec) > (12 * 60 * 60 * 1000UL)) { - // Every 12 hrs we will slam in a new GPS or Phone GPS / NTP time, to correct for local RTC clock drift + } else if (q == RTCQualityGPS) { shouldSet = true; - LOG_DEBUG("Reapplying external time to correct clock drift %ld secs\n", tv->tv_sec); + LOG_DEBUG("Reapplying GPS time: %ld secs\n", printableEpoch); + } else if (q == RTCQualityNTP && !Throttle::isWithinTimespanMs(lastSetMsec, (12 * 60 * 60 * 1000UL))) { + // Every 12 hrs we will slam in a new NTP or Phone GPS / NTP time, to correct for local RTC clock drift + shouldSet = true; + LOG_DEBUG("Reapplying external time to correct clock drift %ld secs\n", printableEpoch); } else { shouldSet = false; LOG_DEBUG("Current RTC quality: %s. Ignoring time of RTC quality of %s\n", RtcName(currentQuality), RtcName(q)); @@ -133,29 +151,29 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate) #ifdef RV3028_RTC if (rtc_found.address == RV3028_RTC) { Melopero_RV3028 rtc; -#ifdef I2C_SDA1 +#if WIRE_INTERFACES_COUNT == 2 rtc.initI2C(rtc_found.port == ScanI2C::I2CPort::WIRE1 ? Wire1 : Wire); #else rtc.initI2C(); #endif tm *t = gmtime(&tv->tv_sec); rtc.setTime(t->tm_year + 1900, t->tm_mon + 1, t->tm_wday, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); - LOG_DEBUG("RV3028_RTC setTime %02d-%02d-%02d %02d:%02d:%02d %ld\n", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, - t->tm_hour, t->tm_min, t->tm_sec, tv->tv_sec); + LOG_DEBUG("RV3028_RTC setTime %02d-%02d-%02d %02d:%02d:%02d (%ld)\n", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, + t->tm_hour, t->tm_min, t->tm_sec, printableEpoch); } #elif defined(PCF8563_RTC) if (rtc_found.address == PCF8563_RTC) { PCF8563_Class rtc; -#ifdef I2C_SDA1 +#if WIRE_INTERFACES_COUNT == 2 rtc.begin(rtc_found.port == ScanI2C::I2CPort::WIRE1 ? Wire1 : Wire); #else rtc.begin(); #endif tm *t = gmtime(&tv->tv_sec); rtc.setDateTime(t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); - LOG_DEBUG("PCF8563_RTC setDateTime %02d-%02d-%02d %02d:%02d:%02d %ld\n", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, - t->tm_hour, t->tm_min, t->tm_sec, tv->tv_sec); + LOG_DEBUG("PCF8563_RTC setDateTime %02d-%02d-%02d %02d:%02d:%02d (%ld)\n", t->tm_year + 1900, t->tm_mon + 1, + t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, printableEpoch); } #elif defined(ARCH_ESP32) settimeofday(tv, NULL); @@ -272,4 +290,4 @@ time_t gm_mktime(struct tm *tm) #else return mktime(tm); #endif -} \ No newline at end of file +} diff --git a/src/gps/ubx.h b/src/gps/ubx.h index 0852c331d..b137d3349 100644 --- a/src/gps/ubx.h +++ b/src/gps/ubx.h @@ -1,3 +1,12 @@ +const char *failMessage = "Unable to %s\n"; + +#define SEND_UBX_PACKET(TYPE, ID, DATA, ERRMSG, TIMEOUT) \ + msglen = makeUBXPacket(TYPE, ID, sizeof(DATA), DATA); \ + _serial_gps->write(UBXscratch, msglen); \ + if (getACK(TYPE, ID, TIMEOUT) != GNSS_RESPONSE_OK) { \ + LOG_WARN(failMessage, #ERRMSG); \ + } + // Power Management uint8_t GPS::_message_PMREQ[] PROGMEM = { @@ -316,6 +325,13 @@ const uint8_t GPS::_message_SAVE[] = { 0x17 // deviceMask: BBR, Flash, EEPROM, and SPI Flash }; +const uint8_t GPS::_message_SAVE_10[] = { + 0x00, 0x00, 0x00, 0x00, // clearMask: no sections cleared + 0xFF, 0xFF, 0x00, 0x00, // saveMask: save all sections + 0x00, 0x00, 0x00, 0x00, // loadMask: no sections loaded + 0x01 // deviceMask: only save to BBR +}; + // As the M10 has no flash, the best we can do to preserve the config is to set it in RAM and BBR. // BBR will survive a restart, and power off for a while, but modules with small backup // batteries or super caps will not retain the config for a long power off time. @@ -335,36 +351,36 @@ const uint8_t GPS::_message_SAVE[] = { // has details on low-power modes /* -CFG-PM2 has been replaced by many CFG-PM commands -CFG-PMS has been removed - -CFG-PM-OPERATEMODE E1 (0 | 1 | 2) -> 1 (PSMOO), because sporadic position updates are required instead of continous tracking <10s -(PSMCT) CFG-PM-POSUPDATEPERIOD U4 -> 0ms, no self-timed wakup because receiver power mode is controlled via "software standby -mode" by legacy UBX-RXM-PMREQ request CFG-PM-ACQPERIOD U4 -> 0ms, because receiver power mode is controlled via "software standby -mode" by legacy UBX-RXM-PMREQ request CFG-PM-ONTIME U4 -> 0ms, optional I guess CFG-PM-EXTINTBACKUP L -> 1, force receiver into -BACKUP mode when EXTINT (should be connected to GPS_EN_PIN) pin is "low" - -This is required because the receiver never enters low power mode if microcontroller is in deep-sleep. -Maybe the changing UART_RX levels trigger a wakeup but even with UBX-RXM-PMREQ[12] = 0x00 (all external wakeup sources disabled) -the receivcer remains in aquisition state -> potentially a bug - -Workaround: Control the EXTINT pin by the GPS_EN_PIN signal - -As mentioned in the M10 operational issues down below, power save won't allow the use of BDS B1C. -CFG-SIGNAL-BDS_B1C_ENA L -> 0 +OPERATEMODE E1 2 (0 | 1 | 2) +POSUPDATEPERIOD U4 5 +ACQPERIOD U4 10 +GRIDOFFSET U4 0 +ONTIME U2 1 +MINACQTIME U1 0 +MAXACQTIME U1 0 +DONOTENTEROFF L 1 +WAITTIMEFIX L 1 +UPDATEEPH L 1 +EXTINTWAKE L 0 no ext ints +EXTINTBACKUP L 0 no ext ints +EXTINTINACTIVE L 0 no ext ints +EXTINTACTIVITY U4 0 no ext ints +LIMITPEAKCURRENT L 1 // Ram layer config message: -// 01 01 00 00 01 00 D0 20 01 02 00 D0 40 00 00 00 00 03 00 D0 40 00 00 00 00 05 00 D0 30 00 00 0D 00 D0 10 01 +// b5 62 06 8a 26 00 00 01 00 00 01 00 d0 20 02 02 00 d0 40 05 00 00 00 05 00 d0 30 01 00 08 00 d0 10 01 09 00 d0 10 01 10 00 d0 +// 10 01 8b de // BBR layer config message: -// 01 02 00 00 01 00 D0 20 01 02 00 D0 40 00 00 00 00 03 00 D0 40 00 00 00 00 05 00 D0 30 00 00 0D 00 D0 10 01 +// b5 62 06 8a 26 00 00 02 00 00 01 00 d0 20 02 02 00 d0 40 05 00 00 00 05 00 d0 30 01 00 08 00 d0 10 01 09 00 d0 10 01 10 00 d0 +// 10 01 8c 03 */ -const uint8_t GPS::_message_VALSET_PM_RAM[] = {0x01, 0x01, 0x00, 0x00, 0x0F, 0x00, 0x31, 0x10, 0x00, 0x01, 0x00, 0xD0, 0x20, 0x01, - 0x02, 0x00, 0xD0, 0x40, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0xD0, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x05, 0x00, 0xD0, 0x30, 0x00, 0x00, 0x0D, 0x00, 0xD0, 0x10, 0x01}; -const uint8_t GPS::_message_VALSET_PM_BBR[] = {0x01, 0x02, 0x00, 0x00, 0x0F, 0x00, 0x31, 0x10, 0x00, 0x01, 0x00, 0xD0, 0x20, 0x01, - 0x02, 0x00, 0xD0, 0x40, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0xD0, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x05, 0x00, 0xD0, 0x30, 0x00, 0x00, 0x0D, 0x00, 0xD0, 0x10, 0x01}; +const uint8_t GPS::_message_VALSET_PM_RAM[] = {0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0xd0, 0x20, 0x02, 0x02, 0x00, 0xd0, 0x40, + 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0xd0, 0x30, 0x01, 0x00, 0x08, 0x00, 0xd0, + 0x10, 0x01, 0x09, 0x00, 0xd0, 0x10, 0x01, 0x10, 0x00, 0xd0, 0x10, 0x01}; +const uint8_t GPS::_message_VALSET_PM_BBR[] = {0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0xd0, 0x20, 0x02, 0x02, 0x00, 0xd0, 0x40, + 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0xd0, 0x30, 0x01, 0x00, 0x08, 0x00, 0xd0, + 0x10, 0x01, 0x09, 0x00, 0xd0, 0x10, 0x01, 0x10, 0x00, 0xd0, 0x10, 0x01}; /* CFG-ITFM replaced by 5 valset messages which can be combined into one for RAM and one for BBR diff --git a/src/graphics/EInkDynamicDisplay.cpp b/src/graphics/EInkDynamicDisplay.cpp index c31941a60..ca994b2c9 100644 --- a/src/graphics/EInkDynamicDisplay.cpp +++ b/src/graphics/EInkDynamicDisplay.cpp @@ -1,3 +1,4 @@ +#include "Throttle.h" #include "configuration.h" #if defined(USE_EINK) && defined(USE_EINK_DYNAMICDISPLAY) @@ -231,15 +232,13 @@ void EInkDynamicDisplay::checkForPromotion() // Is it too soon for another frame of this type? void EInkDynamicDisplay::checkRateLimiting() { - uint32_t now = millis(); - // Sanity check: millis() overflow - just let the update run.. - if (previousRunMs > now) + if (previousRunMs > millis()) return; // Skip update: too soon for BACKGROUND if (frameFlags == BACKGROUND) { - if (now - previousRunMs < EINK_LIMIT_RATE_BACKGROUND_SEC * 1000) { + if (Throttle::isWithinTimespanMs(previousRunMs, EINK_LIMIT_RATE_BACKGROUND_SEC * 1000)) { refresh = SKIPPED; reason = EXCEEDED_RATELIMIT_FULL; return; @@ -252,7 +251,7 @@ void EInkDynamicDisplay::checkRateLimiting() // Skip update: too soon for RESPONSIVE if (frameFlags & RESPONSIVE) { - if (now - previousRunMs < EINK_LIMIT_RATE_RESPONSIVE_SEC * 1000) { + if (Throttle::isWithinTimespanMs(previousRunMs, EINK_LIMIT_RATE_RESPONSIVE_SEC * 1000)) { refresh = SKIPPED; reason = EXCEEDED_RATELIMIT_FAST; LOG_DEBUG("refresh=SKIPPED, reason=EXCEEDED_RATELIMIT_FAST, frameFlags=0x%x\n", frameFlags); diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 04fe73e44..ad42fa979 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -22,6 +22,7 @@ along with this program. If not, see . #include "Screen.h" #include "../userPrefs.h" #include "PowerMon.h" +#include "Throttle.h" #include "configuration.h" #if HAS_SCREEN #include @@ -47,6 +48,7 @@ along with this program. If not, see . #include "modules/AdminModule.h" #include "modules/ExternalNotificationModule.h" #include "modules/TextMessageModule.h" +#include "modules/WaypointModule.h" #include "sleep.h" #include "target_specific.h" @@ -56,10 +58,11 @@ along with this program. If not, see . #ifdef ARCH_ESP32 #include "esp_task_wdt.h" -#include "modules/esp32/StoreForwardModule.h" +#include "modules/StoreForwardModule.h" #endif #if ARCH_PORTDUINO +#include "modules/StoreForwardModule.h" #include "platform/portduino/PortduinoGlue.h" #endif @@ -117,6 +120,7 @@ static bool heartbeat = false; #define SCREEN_HEIGHT display->getHeight() #include "graphics/ScreenFonts.h" +#include #define getStringCenteredX(s) ((SCREEN_WIDTH - display->getStringWidth(s)) / 2) @@ -159,8 +163,8 @@ static void drawIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDispl display->setFont(FONT_MEDIUM); display->setTextAlignment(TEXT_ALIGN_LEFT); -#ifdef SPLASH_TITLE_USERPREFS - const char *title = SPLASH_TITLE_USERPREFS; +#ifdef USERPREFS_SPLASH_TITLE + const char *title = USERPREFS_SPLASH_TITLE; #else const char *title = "meshtastic.org"; #endif @@ -1004,55 +1008,55 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state display->setColor(WHITE); #ifndef EXCLUDE_EMOJI - if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"\U0001F44D") == 0) { + if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\U0001F44D") == 0) { display->drawXbm(x + (SCREEN_WIDTH - thumbs_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - thumbs_height) / 2 + 2 + 5, thumbs_width, thumbs_height, thumbup); - } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"\U0001F44E") == 0) { + } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\U0001F44E") == 0) { display->drawXbm(x + (SCREEN_WIDTH - thumbs_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - thumbs_height) / 2 + 2 + 5, thumbs_width, thumbs_height, thumbdown); - } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"❓") == 0) { + } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "❓") == 0) { display->drawXbm(x + (SCREEN_WIDTH - question_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - question_height) / 2 + 2 + 5, question_width, question_height, question); - } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"‼️") == 0) { + } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "‼️") == 0) { display->drawXbm(x + (SCREEN_WIDTH - bang_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - bang_height) / 2 + 2 + 5, bang_width, bang_height, bang); - } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"\U0001F4A9") == 0) { + } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\U0001F4A9") == 0) { display->drawXbm(x + (SCREEN_WIDTH - poo_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - poo_height) / 2 + 2 + 5, poo_width, poo_height, poo); } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\xf0\x9f\xa4\xa3") == 0) { display->drawXbm(x + (SCREEN_WIDTH - haha_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - haha_height) / 2 + 2 + 5, haha_width, haha_height, haha); - } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"\U0001F44B") == 0) { + } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\U0001F44B") == 0) { display->drawXbm(x + (SCREEN_WIDTH - wave_icon_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - wave_icon_height) / 2 + 2 + 5, wave_icon_width, wave_icon_height, wave_icon); - } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"\U0001F920") == 0) { + } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\U0001F920") == 0) { display->drawXbm(x + (SCREEN_WIDTH - cowboy_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - cowboy_height) / 2 + 2 + 5, cowboy_width, cowboy_height, cowboy); - } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"\U0001F42D") == 0) { + } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\U0001F42D") == 0) { display->drawXbm(x + (SCREEN_WIDTH - deadmau5_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - deadmau5_height) / 2 + 2 + 5, deadmau5_width, deadmau5_height, deadmau5); - } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"\xE2\x98\x80\xEF\xB8\x8F") == 0) { + } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\xE2\x98\x80\xEF\xB8\x8F") == 0) { display->drawXbm(x + (SCREEN_WIDTH - sun_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - sun_height) / 2 + 2 + 5, sun_width, sun_height, sun); - } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"\u2614") == 0) { + } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\u2614") == 0) { display->drawXbm(x + (SCREEN_WIDTH - rain_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - rain_height) / 2 + 2 + 10, rain_width, rain_height, rain); - } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"☁️") == 0) { + } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "☁️") == 0) { display->drawXbm(x + (SCREEN_WIDTH - cloud_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - cloud_height) / 2 + 2 + 5, cloud_width, cloud_height, cloud); - } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"🌫️") == 0) { + } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "🌫️") == 0) { display->drawXbm(x + (SCREEN_WIDTH - fog_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - fog_height) / 2 + 2 + 5, fog_width, fog_height, fog); - } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"\xf0\x9f\x98\x88") == 0) { + } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\xf0\x9f\x98\x88") == 0) { display->drawXbm(x + (SCREEN_WIDTH - devil_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - devil_height) / 2 + 2 + 5, devil_width, devil_height, devil); - } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"♥️") == 0) { + } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "♥️") == 0) { display->drawXbm(x + (SCREEN_WIDTH - heart_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - heart_height) / 2 + 2 + 5, heart_width, heart_height, heart); } else { @@ -1093,8 +1097,8 @@ static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, const NodeStat { char usersString[20]; snprintf(usersString, sizeof(usersString), "%d/%d", nodeStatus->getNumOnline(), nodeStatus->getNumTotal()); -#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \ - defined(HX8357_CS)) && \ +#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \ + defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS)) && \ !defined(DISPLAY_FORCE_SMALL_FONTS) display->drawFastImage(x, y + 3, 8, 8, imgUser); #else @@ -1285,8 +1289,8 @@ static int8_t prevFrame = -1; // Draw the arrow pointing to a node's location void Screen::drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t compassY, uint16_t compassDiam, float headingRadian) { - Point tip(0.0f, 0.5f), tail(0.0f, -0.5f); // pointing up initially - float arrowOffsetX = 0.2f, arrowOffsetY = 0.2f; + Point tip(0.0f, 0.5f), tail(0.0f, -0.35f); // pointing up initially + float arrowOffsetX = 0.14f, arrowOffsetY = 1.0f; Point leftArrow(tip.x - arrowOffsetX, tip.y - arrowOffsetY), rightArrow(tip.x + arrowOffsetX, tip.y - arrowOffsetY); Point *arrowPoints[] = {&tip, &tail, &leftArrow, &rightArrow}; @@ -1296,9 +1300,19 @@ void Screen::drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t com arrowPoints[i]->scale(compassDiam * 0.6); arrowPoints[i]->translate(compassX, compassY); } + /* Old arrow display->drawLine(tip.x, tip.y, tail.x, tail.y); display->drawLine(leftArrow.x, leftArrow.y, tip.x, tip.y); display->drawLine(rightArrow.x, rightArrow.y, tip.x, tip.y); + display->drawLine(leftArrow.x, leftArrow.y, tail.x, tail.y); + display->drawLine(rightArrow.x, rightArrow.y, tail.x, tail.y); + */ +#ifdef USE_EINK + display->drawTriangle(tip.x, tip.y, rightArrow.x, rightArrow.y, tail.x, tail.y); +#else + display->fillTriangle(tip.x, tip.y, rightArrow.x, rightArrow.y, tail.x, tail.y); +#endif + display->drawTriangle(tip.x, tip.y, leftArrow.x, leftArrow.y, tail.x, tail.y); } // Get a string representation of the time passed since something happened @@ -1336,22 +1350,27 @@ void Screen::drawCompassNorth(OLEDDisplay *display, int16_t compassX, int16_t co // If north is supposed to be at the top of the compass we want rotation to be +0 if (config.display.compass_north_top) myHeading = -0; - - Point N1(-0.04f, 0.65f), N2(0.04f, 0.65f); + /* N sign points currently not deleted*/ + Point N1(-0.04f, 0.65f), N2(0.04f, 0.65f); // N sign points (N1-N4) Point N3(-0.04f, 0.55f), N4(0.04f, 0.55f); - Point *rosePoints[] = {&N1, &N2, &N3, &N4}; + Point NC1(0.00f, 0.50f); // north circle center point + Point *rosePoints[] = {&N1, &N2, &N3, &N4, &NC1}; uint16_t compassDiam = Screen::getCompassDiam(SCREEN_WIDTH, SCREEN_HEIGHT); - for (int i = 0; i < 4; i++) { + for (int i = 0; i < 5; i++) { // North on compass will be negative of heading rosePoints[i]->rotate(-myHeading); rosePoints[i]->scale(compassDiam); rosePoints[i]->translate(compassX, compassY); } + + /* changed the N sign to a small circle on the compass circle. display->drawLine(N1.x, N1.y, N3.x, N3.y); display->drawLine(N2.x, N2.y, N4.x, N4.y); display->drawLine(N1.x, N1.y, N4.x, N4.y); + */ + display->drawCircle(NC1.x, NC1.y, 4); // North sign circle, 4px radius is sufficient for all displays. } uint16_t Screen::getCompassDiam(uint32_t displayWidth, uint32_t displayHeight) @@ -1515,7 +1534,8 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O #elif defined(USE_SSD1306) dispdev = new SSD1306Wire(address.address, -1, -1, geometry, (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE); -#elif defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ST7789_CS) || defined(RAK14014) || defined(HX8357_CS) +#elif defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7789_CS) || \ + defined(RAK14014) || defined(HX8357_CS) dispdev = new TFTDisplay(address.address, -1, -1, geometry, (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE); #elif defined(USE_EINK) && !defined(USE_EINK_DYNAMICDISPLAY) @@ -1654,6 +1674,11 @@ void Screen::setup() static_cast(dispdev)->setSubtype(7); #endif +#if defined(USE_ST7789) && defined(TFT_MESH) + // Heltec T114 and T190: honor a custom text color, if defined in variant.h + static_cast(dispdev)->setRGB(TFT_MESH); +#endif + // Initialising the UI will init the display too. ui->init(); @@ -1707,8 +1732,11 @@ void Screen::setup() // Standard behaviour is to FLIP the screen (needed on T-Beam). If this config item is set, unflip it, and thereby logically // flip it. If you have a headache now, you're welcome. if (!config.display.flip_screen) { -#if defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ST7789_CS) || defined(RAK14014) || defined(HX8357_CS) +#if defined(ST7701_CS) || defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || \ + defined(ST7789_CS) || defined(RAK14014) || defined(HX8357_CS) static_cast(dispdev)->flipScreenVertically(); +#elif defined(USE_ST7789) + static_cast(dispdev)->flipScreenVertically(); #else dispdev->flipScreenVertically(); #endif @@ -1766,6 +1794,11 @@ void Screen::forceDisplay(bool forceUiUpdate) #ifdef USE_EINK // If requested, make sure queued commands are run, and UI has rendered a new frame if (forceUiUpdate) { + // Force a display refresh, in addition to the UI update + // Changing the GPS status bar icon apparently doesn't register as a change in image + // (False negative of the image hashing algorithm used to skip identical frames) + EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); + // No delay between UI frame rendering setFastFramerate(); @@ -1851,13 +1884,7 @@ int32_t Screen::runOnce() handleSetOn(false); break; case Cmd::ON_PRESS: - // If a nag notification is running, stop it - if (moduleConfig.external_notification.enabled && (externalNotificationModule->nagCycleCutoff != UINT32_MAX)) { - externalNotificationModule->stopNow(); - } else { - // Don't advance the screen if we just wanted to switch off the nag notification - handleOnPress(); - } + handleOnPress(); break; case Cmd::SHOW_PREV_FRAME: handleShowPrevFrame(); @@ -1920,7 +1947,7 @@ int32_t Screen::runOnce() if (showingNormalScreen) { // standard screen loop handling here if (config.display.auto_screen_carousel_secs > 0 && - (millis() - lastScreenTransition) > (config.display.auto_screen_carousel_secs * 1000)) { + !Throttle::isWithinTimespanMs(lastScreenTransition, config.display.auto_screen_carousel_secs * 1000)) { // If an E-Ink display struggles with fast refresh, force carousel to use full refresh instead // Carousel is potentially a major source of E-Ink display wear @@ -2081,8 +2108,13 @@ void Screen::setFrames(FrameFocus focus) // Check if the module being drawn has requested focus // We will honor this request later, if setFrames was triggered by a UIFrameEvent MeshModule *m = *i; - if (m->isRequestingFocus()) + if (m->isRequestingFocus()) { fsi.positions.focusedModule = numframes; + } + + // Identify the position of specific modules, if we need to know this later + if (m == waypointModule) + fsi.positions.waypoint = numframes; numframes++; } @@ -2101,8 +2133,8 @@ void Screen::setFrames(FrameFocus focus) #endif // If we have a text message - show it next, unless it's a phone message and we aren't using any special modules - fsi.positions.textMessage = numframes; if (devicestate.has_rx_text_message && shouldDrawMessage(&devicestate.rx_text_message)) { + fsi.positions.textMessage = numframes; normalFrames[numframes++] = drawTextMessageFrame; } @@ -2164,7 +2196,7 @@ void Screen::setFrames(FrameFocus focus) case FOCUS_PRESERVE: // If we can identify which type of frame "originalPosition" was, can move directly to it in the new frameset - FramesetInfo &oldFsi = this->framesetInfo; + const FramesetInfo &oldFsi = this->framesetInfo; if (originalPosition == oldFsi.positions.log) ui->switchToFrame(fsi.positions.log); else if (originalPosition == oldFsi.positions.settings) @@ -2204,6 +2236,31 @@ void Screen::setFrameImmediateDraw(FrameCallback *drawFrames) setFastFramerate(); } +// Dismisses the currently displayed screen frame, if possible +// Relevant for text message, waypoint, others in future? +// Triggered with a CardKB keycombo +void Screen::dismissCurrentFrame() +{ + uint8_t currentFrame = ui->getUiState()->currentFrame; + bool dismissed = false; + + if (currentFrame == framesetInfo.positions.textMessage && devicestate.has_rx_text_message) { + LOG_INFO("Dismissing Text Message\n"); + devicestate.has_rx_text_message = false; + dismissed = true; + } + + else if (currentFrame == framesetInfo.positions.waypoint && devicestate.has_rx_waypoint) { + LOG_DEBUG("Dismissing Waypoint\n"); + devicestate.has_rx_waypoint = false; + dismissed = true; + } + + // If we did make changes to dismiss, we now need to regenerate the frameset + if (dismissed) + setFrames(); +} + void Screen::handleStartFirmwareUpdateScreen() { LOG_DEBUG("showing firmware screen\n"); @@ -2413,10 +2470,10 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 // Draw our hardware ID to assist with bluetooth pairing. Either prefix with Info or S&F Logo if (moduleConfig.store_forward.enabled) { #ifdef ARCH_ESP32 - if (millis() - storeForwardModule->lastHeartbeat > - (storeForwardModule->heartbeatInterval * 1200)) { // no heartbeat, overlap a bit -#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \ - defined(HX8357_CS)) && \ + if (!Throttle::isWithinTimespanMs(storeForwardModule->lastHeartbeat, + (storeForwardModule->heartbeatInterval * 1200))) { // no heartbeat, overlap a bit +#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \ + defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || ARCH_PORTDUINO) && \ !defined(DISPLAY_FORCE_SMALL_FONTS) display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8, imgQuestionL1); @@ -2427,8 +2484,8 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 imgQuestion); #endif } else { -#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \ - defined(HX8357_CS)) && \ +#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \ + defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS)) && \ !defined(DISPLAY_FORCE_SMALL_FONTS) display->drawFastImage(x + SCREEN_WIDTH - 18 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 16, 8, imgSFL1); @@ -2442,8 +2499,8 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 #endif } else { // TODO: Raspberry Pi supports more than just the one screen size -#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \ - defined(HX8357_CS) || ARCH_PORTDUINO) && \ +#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \ + defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || ARCH_PORTDUINO) && \ !defined(DISPLAY_FORCE_SMALL_FONTS) display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8, imgInfoL1); @@ -2716,12 +2773,23 @@ int Screen::handleInputEvent(const InputEvent *event) } #endif - if (showingNormalScreen && moduleFrames.size() == 0) { - // LOG_DEBUG("Screen::handleInputEvent from %s\n", event->source); - if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) { - showPrevFrame(); - } else if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT)) { - showNextFrame(); + // Use left or right input from a keyboard to move between frames, + // so long as a mesh module isn't using these events for some other purpose + if (showingNormalScreen) { + + // Ask any MeshModules if they're handling keyboard input right now + bool inputIntercepted = false; + for (MeshModule *module : moduleFrames) { + if (module->interceptingKeyboardInput()) + inputIntercepted = true; + } + + // If no modules are using the input, move between frames + if (!inputIntercepted) { + if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) + showPrevFrame(); + else if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT)) + showNextFrame(); } } diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index 021b11bda..b2e6e905b 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -454,6 +454,9 @@ class Screen : public concurrency::OSThread void setWelcomeFrames(); + // Dismiss the currently focussed frame, if possible (e.g. text message, waypoint) + void dismissCurrentFrame(); + #ifdef USE_EINK /// Draw an image to remain on E-Ink display after screen off void setScreensaverFrames(FrameCallback einkScreensaver = NULL); @@ -503,11 +506,14 @@ class Screen : public concurrency::OSThread void handleStartFirmwareUpdateScreen(); // Info collected by setFrames method. - // Index location of specific frames. Used to apply the FrameFocus parameter of setFrames + // Index location of specific frames. + // - Used to apply the FrameFocus parameter of setFrames + // - Used to dismiss the currently shown frame (txt; waypoint) by CardKB combo struct FramesetInfo { struct FramePositions { uint8_t fault = 0; uint8_t textMessage = 0; + uint8_t waypoint = 0; uint8_t focusedModule = 0; uint8_t log = 0; uint8_t settings = 0; diff --git a/src/graphics/ScreenFonts.h b/src/graphics/ScreenFonts.h index 267f6e0a8..c9ce961fa 100644 --- a/src/graphics/ScreenFonts.h +++ b/src/graphics/ScreenFonts.h @@ -12,8 +12,8 @@ #include "graphics/fonts/OLEDDisplayFontsUA.h" #endif -#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \ - defined(HX8357_CS)) && \ +#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \ + defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS)) && \ !defined(DISPLAY_FORCE_SMALL_FONTS) // The screen is bigger so use bigger fonts #define FONT_SMALL ArialMT_Plain_16 // Height: 19 @@ -41,4 +41,4 @@ #define FONT_HEIGHT_SMALL _fontHeight(FONT_SMALL) #define FONT_HEIGHT_MEDIUM _fontHeight(FONT_MEDIUM) -#define FONT_HEIGHT_LARGE _fontHeight(FONT_LARGE) \ No newline at end of file +#define FONT_HEIGHT_LARGE _fontHeight(FONT_LARGE) diff --git a/src/graphics/TFTDisplay.cpp b/src/graphics/TFTDisplay.cpp index 2849dd9a9..0c32a7c32 100644 --- a/src/graphics/TFTDisplay.cpp +++ b/src/graphics/TFTDisplay.cpp @@ -244,9 +244,9 @@ class LGFX : public lgfx::LGFX_Device static LGFX *tft = nullptr; -#elif defined(ILI9341_DRIVER) +#elif defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) -#include // Graphics and font library for ILI9341 driver chip +#include // Graphics and font library for ILI9341/ILI9342 driver chip #if defined(ILI9341_BACKLIGHT_EN) && !defined(TFT_BL) #define TFT_BL ILI9341_BACKLIGHT_EN @@ -254,7 +254,11 @@ static LGFX *tft = nullptr; class LGFX : public lgfx::LGFX_Device { +#if defined(ILI9341_DRIVER) lgfx::Panel_ILI9341 _panel_instance; +#elif defined(ILI9342_DRIVER) + lgfx::Panel_ILI9342 _panel_instance; +#endif lgfx::Bus_SPI _bus_instance; lgfx::Light_PWM _light_instance; @@ -265,7 +269,11 @@ class LGFX : public lgfx::LGFX_Device auto cfg = _bus_instance.config(); // configure SPI +#if defined(ILI9341_DRIVER) cfg.spi_host = ILI9341_SPI_HOST; // ESP32-S2,S3,C3 : SPI2_HOST or SPI3_HOST / ESP32 : VSPI_HOST or HSPI_HOST +#elif defined(ILI9342_DRIVER) + cfg.spi_host = ILI9342_SPI_HOST; // ESP32-S2,S3,C3 : SPI2_HOST or SPI3_HOST / ESP32 : VSPI_HOST or HSPI_HOST +#endif cfg.spi_mode = 0; cfg.freq_write = SPI_FREQUENCY; // SPI clock for transmission (up to 80MHz, rounded to the value obtained by dividing // 80MHz by an integer) @@ -336,7 +344,7 @@ class LGFX : public lgfx::LGFX_Device static LGFX *tft = nullptr; #elif defined(ST7735_CS) -#include // Graphics and font library for ILI9341 driver chip +#include // Graphics and font library for ILI9342 driver chip static TFT_eSPI *tft = nullptr; // Invoke library, pins defined in User_Setup.h #elif ARCH_PORTDUINO && HAS_SCREEN != 0 @@ -360,6 +368,8 @@ class LGFX : public lgfx::LGFX_Device _panel_instance = new lgfx::Panel_ST7735S; else if (settingsMap[displayPanel] == ili9341) _panel_instance = new lgfx::Panel_ILI9341; + else if (settingsMap[displayPanel] == ili9342) + _panel_instance = new lgfx::Panel_ILI9342; auto buscfg = _bus_instance.config(); buscfg.spi_mode = 0; buscfg.spi_host = settingsMap[displayspidev]; @@ -500,10 +510,126 @@ class LGFX : public lgfx::LGFX_Device static LGFX *tft = nullptr; +#elif defined(ST7701_CS) +#include // Graphics and font library for ST7701 driver chip +#include +#include + +class LGFX : public lgfx::LGFX_Device +{ + lgfx::Panel_ST7701 _panel_instance; + lgfx::Bus_RGB _bus_instance; + lgfx::Light_PWM _light_instance; + lgfx::Touch_FT5x06 _touch_instance; + + public: + LGFX(void) + { + { + auto cfg = _panel_instance.config(); + cfg.memory_width = 800; + cfg.memory_height = 480; + cfg.panel_width = TFT_WIDTH; + cfg.panel_height = TFT_HEIGHT; + cfg.offset_x = TFT_OFFSET_X; + cfg.offset_y = TFT_OFFSET_Y; + _panel_instance.config(cfg); + } + + { + auto cfg = _panel_instance.config_detail(); + cfg.pin_cs = ST7701_CS; + cfg.pin_sclk = ST7701_SCK; + cfg.pin_mosi = ST7701_SDA; + // cfg.use_psram = 1; + _panel_instance.config_detail(cfg); + } + + { + auto cfg = _bus_instance.config(); + cfg.panel = &_panel_instance; +#ifdef SENSECAP_INDICATOR + cfg.pin_d0 = GPIO_NUM_15; // B0 + cfg.pin_d1 = GPIO_NUM_14; // B1 + cfg.pin_d2 = GPIO_NUM_13; // B2 + cfg.pin_d3 = GPIO_NUM_12; // B3 + cfg.pin_d4 = GPIO_NUM_11; // B4 + + cfg.pin_d5 = GPIO_NUM_10; // G0 + cfg.pin_d6 = GPIO_NUM_9; // G1 + cfg.pin_d7 = GPIO_NUM_8; // G2 + cfg.pin_d8 = GPIO_NUM_7; // G3 + cfg.pin_d9 = GPIO_NUM_6; // G4 + cfg.pin_d10 = GPIO_NUM_5; // G5 + + cfg.pin_d11 = GPIO_NUM_4; // R0 + cfg.pin_d12 = GPIO_NUM_3; // R1 + cfg.pin_d13 = GPIO_NUM_2; // R2 + cfg.pin_d14 = GPIO_NUM_1; // R3 + cfg.pin_d15 = GPIO_NUM_0; // R4 + + cfg.pin_henable = GPIO_NUM_18; + cfg.pin_vsync = GPIO_NUM_17; + cfg.pin_hsync = GPIO_NUM_16; + cfg.pin_pclk = GPIO_NUM_21; + cfg.freq_write = 12000000; + + cfg.hsync_polarity = 0; + cfg.hsync_front_porch = 10; + cfg.hsync_pulse_width = 8; + cfg.hsync_back_porch = 50; + + cfg.vsync_polarity = 0; + cfg.vsync_front_porch = 10; + cfg.vsync_pulse_width = 8; + cfg.vsync_back_porch = 20; + + cfg.pclk_active_neg = 0; + cfg.de_idle_high = 1; + cfg.pclk_idle_high = 0; +#endif + _bus_instance.config(cfg); + } + _panel_instance.setBus(&_bus_instance); + + { + auto cfg = _light_instance.config(); + cfg.pin_bl = ST7701_BL; + _light_instance.config(cfg); + } + _panel_instance.light(&_light_instance); + + { + auto cfg = _touch_instance.config(); + cfg.pin_cs = -1; + cfg.x_min = 0; + cfg.x_max = 479; + cfg.y_min = 0; + cfg.y_max = 479; + cfg.pin_int = -1; // don't use SCREEN_TOUCH_INT; + cfg.pin_rst = SCREEN_TOUCH_RST; + cfg.bus_shared = true; + cfg.offset_rotation = TFT_OFFSET_ROTATION; + + cfg.i2c_port = TOUCH_I2C_PORT; + cfg.i2c_addr = TOUCH_SLAVE_ADDRESS; + cfg.pin_sda = I2C_SDA; + cfg.pin_scl = I2C_SCL; + cfg.freq = 400000; + _touch_instance.config(cfg); + _panel_instance.setTouch(&_touch_instance); + } + + setPanel(&_panel_instance); + } +}; + +static LGFX *tft = nullptr; + #endif -#if defined(ST7735_CS) || defined(ST7789_CS) || defined(ILI9341_DRIVER) || defined(RAK14014) || defined(HX8357_CS) || \ - (ARCH_PORTDUINO && HAS_SCREEN != 0) +#if defined(ST7701_CS) || defined(ST7735_CS) || defined(ST7789_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || \ + defined(RAK14014) || defined(HX8357_CS) || (ARCH_PORTDUINO && HAS_SCREEN != 0) #include "SPILock.h" #include "TFTDisplay.h" #include @@ -709,7 +835,6 @@ bool TFTDisplay::connect() #ifdef UNPHONE unphone.backlight(true); // using unPhone library - LOG_INFO("Power to TFT Backlight\n"); #endif tft->init(); @@ -725,7 +850,7 @@ bool TFTDisplay::connect() attachInterrupt(digitalPinToInterrupt(SCREEN_TOUCH_INT), rak14014_tpIntHandle, FALLING); #elif defined(T_DECK) || defined(PICOMPUTER_S3) || defined(CHATTER_2) tft->setRotation(1); // T-Deck has the TFT in landscape -#elif defined(T_WATCH_S3) +#elif defined(T_WATCH_S3) || defined(SENSECAP_INDICATOR) tft->setRotation(2); // T-Watch S3 left-handed orientation #else tft->setRotation(3); // Orient horizontal and wide underneath the silkscreen name label @@ -735,4 +860,4 @@ bool TFTDisplay::connect() return true; } -#endif \ No newline at end of file +#endif diff --git a/src/graphics/images.h b/src/graphics/images.h index ab3767a89..2b0854a33 100644 --- a/src/graphics/images.h +++ b/src/graphics/images.h @@ -20,8 +20,8 @@ const uint8_t bluetoothConnectedIcon[36] PROGMEM = {0xfe, 0x01, 0xff, 0x03, 0x03 0xfe, 0x31, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0xf0, 0x3f, 0xe0, 0x1f}; #endif -#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \ - defined(HX8357_CS) || ARCH_PORTDUINO) && \ +#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \ + defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || ARCH_PORTDUINO) && \ !defined(DISPLAY_FORCE_SMALL_FONTS) const uint8_t imgQuestionL1[] PROGMEM = {0xff, 0x01, 0x01, 0x32, 0x7b, 0x49, 0x49, 0x6f, 0x26, 0x01, 0x01, 0xff}; const uint8_t imgQuestionL2[] PROGMEM = {0x0f, 0x08, 0x08, 0x08, 0x06, 0x0f, 0x0f, 0x06, 0x08, 0x08, 0x08, 0x0f}; diff --git a/src/graphics/img/icon.xbm b/src/graphics/img/icon.xbm index f90cf4946..4e78ae8a1 100644 --- a/src/graphics/img/icon.xbm +++ b/src/graphics/img/icon.xbm @@ -1,4 +1,4 @@ -#ifndef HAS_USERPREFS_SPLASH +#ifndef USERPREFS_HAS_SPLASH #define icon_width 50 #define icon_height 28 static uint8_t icon_bits[] = { diff --git a/src/input/ExpressLRSFiveWay.cpp b/src/input/ExpressLRSFiveWay.cpp index c444800ba..af4433dae 100644 --- a/src/input/ExpressLRSFiveWay.cpp +++ b/src/input/ExpressLRSFiveWay.cpp @@ -1,5 +1,5 @@ - #include "ExpressLRSFiveWay.h" +#include "Throttle.h" #ifdef INPUTBROKER_EXPRESSLRSFIVEWAY_TYPE @@ -76,11 +76,10 @@ void ExpressLRSFiveWay::update(int *keyValue, bool *keyLongPressed) *keyValue = NO_PRESS; int newKey = readKey(); - uint32_t now = millis(); if (keyInProcess == NO_PRESS) { // New key down if (newKey != NO_PRESS) { - keyDownStart = now; + keyDownStart = millis(); // DBGLN("down=%u", newKey); } } else { @@ -88,7 +87,7 @@ void ExpressLRSFiveWay::update(int *keyValue, bool *keyLongPressed) if (newKey == NO_PRESS) { // DBGLN("up=%u", keyInProcess); if (!isLongPressed) { - if ((now - keyDownStart) > KEY_DEBOUNCE_MS) { + if (!Throttle::isWithinTimespanMs(keyDownStart, KEY_DEBOUNCE_MS)) { *keyValue = keyInProcess; *keyLongPressed = false; } @@ -101,7 +100,7 @@ void ExpressLRSFiveWay::update(int *keyValue, bool *keyLongPressed) } // else still pressing, waiting for long if not already signaled else if (!isLongPressed) { - if ((now - keyDownStart) > KEY_LONG_PRESS_MS) { + if (!Throttle::isWithinTimespanMs(keyDownStart, KEY_LONG_PRESS_MS)) { *keyValue = keyInProcess; *keyLongPressed = true; isLongPressed = true; diff --git a/src/input/InputBroker.h b/src/input/InputBroker.h index 57c25af4b..db7524bb0 100644 --- a/src/input/InputBroker.h +++ b/src/input/InputBroker.h @@ -4,6 +4,22 @@ #define ANYKEY 0xFF #define MATRIXKEY 0xFE +#define INPUT_BROKER_MSG_BRIGHTNESS_UP 0x11 +#define INPUT_BROKER_MSG_BRIGHTNESS_DOWN 0x12 +#define INPUT_BROKER_MSG_REBOOT 0x90 +#define INPUT_BROKER_MSG_SHUTDOWN 0x9b +#define INPUT_BROKER_MSG_GPS_TOGGLE 0x9e +#define INPUT_BROKER_MSG_MUTE_TOGGLE 0xac +#define INPUT_BROKER_MSG_SEND_PING 0xaf +#define INPUT_BROKER_MSG_DISMISS_FRAME 0x8b +#define INPUT_BROKER_MSG_LEFT 0xb4 +#define INPUT_BROKER_MSG_UP 0xb5 +#define INPUT_BROKER_MSG_DOWN 0xb6 +#define INPUT_BROKER_MSG_RIGHT 0xb7 +#define INPUT_BROKER_MSG_FN_SYMBOL_ON 0xf1 +#define INPUT_BROKER_MSG_FN_SYMBOL_OFF 0xf2 +#define INPUT_BROKER_MSG_BLUETOOTH_TOGGLE 0xAA + typedef struct _InputEvent { const char *source; char inputEvent; diff --git a/src/input/LinuxInput.cpp b/src/input/LinuxInput.cpp index 6194195ed..57a87b0ef 100644 --- a/src/input/LinuxInput.cpp +++ b/src/input/LinuxInput.cpp @@ -147,11 +147,11 @@ int32_t LinuxInput::runOnce() case KEY_LEFT: // Left e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT; break; - e.kbchar = 0xb4; + e.kbchar = INPUT_BROKER_MSG_LEFT; case KEY_RIGHT: // Right e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT; break; - e.kbchar = 0xb7; + e.kbchar = INPUT_BROKER_MSG_RIGHT; case KEY_ENTER: // Enter e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT; break; diff --git a/src/input/ScanAndSelect.cpp b/src/input/ScanAndSelect.cpp index d693d768c..65ca7e332 100644 --- a/src/input/ScanAndSelect.cpp +++ b/src/input/ScanAndSelect.cpp @@ -6,6 +6,7 @@ #include "ScanAndSelect.h" #include "modules/CannedMessageModule.h" +#include // Config static const char name[] = "scanAndSelect"; // should match "allow input source" string @@ -75,7 +76,7 @@ int32_t ScanAndSelectInput::runOnce() else { // Duration enough for long press // Long press not yet fired (prevent repeat firing while held) - if (!longPressFired && now - downSinceMs > durationLongMs) { + if (!longPressFired && Throttle::isWithinTimespanMs(downSinceMs, durationLongMs)) { longPressFired = true; longPress(); } @@ -91,7 +92,7 @@ int32_t ScanAndSelectInput::runOnce() // Long press event didn't already fire if (held && !longPressFired) { // Duration enough for short press - if (now - downSinceMs > durationShortMs) { + if (!Throttle::isWithinTimespanMs(downSinceMs, durationShortMs)) { shortPress(); } } diff --git a/src/input/SerialKeyboard.cpp b/src/input/SerialKeyboard.cpp index fa3eb2528..4827e8995 100644 --- a/src/input/SerialKeyboard.cpp +++ b/src/input/SerialKeyboard.cpp @@ -1,5 +1,6 @@ #include "SerialKeyboard.h" #include "configuration.h" +#include #ifdef INPUTBROKER_SERIAL_TYPE #define CANNED_MESSAGE_MODULE_ENABLE 1 // in case it's not set in the variant file @@ -73,7 +74,7 @@ int32_t SerialKeyboard::runOnce() // Serial.print ("X"); // Serial.println (shiftRegister2, BIN); - if (millis() - lastPressTime > 500) { + if (!Throttle::isWithinTimespanMs(lastPressTime, 500)) { quickPress = 0; } @@ -87,7 +88,7 @@ int32_t SerialKeyboard::runOnce() e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP; } else if (!(shiftRegister2 & (1 << 2))) { e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT; - e.kbchar = 0xb7; + e.kbchar = INPUT_BROKER_MSG_RIGHT; } else if (!(shiftRegister2 & (1 << 1))) { e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT; } else if (!(shiftRegister2 & (1 << 0))) { diff --git a/src/input/cardKbI2cImpl.cpp b/src/input/cardKbI2cImpl.cpp index 1bff49475..f1df6b137 100644 --- a/src/input/cardKbI2cImpl.cpp +++ b/src/input/cardKbI2cImpl.cpp @@ -9,14 +9,14 @@ CardKbI2cImpl::CardKbI2cImpl() : KbI2cBase("cardKB") {} void CardKbI2cImpl::init() { -#ifndef ARCH_PORTDUINO +#if !MESHTASTIC_EXCLUDE_I2C && !defined(ARCH_PORTDUINO) if (cardkb_found.address == 0x00) { LOG_DEBUG("Rescanning for I2C keyboard\n"); uint8_t i2caddr_scan[] = {CARDKB_ADDR, TDECK_KB_ADDR, BBQ10_KB_ADDR}; uint8_t i2caddr_asize = 3; auto i2cScanner = std::unique_ptr(new ScanI2CTwoWire()); -#if defined(I2C_SDA1) +#if WIRE_INTERFACES_COUNT == 2 i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1, i2caddr_scan, i2caddr_asize); #endif i2cScanner->scanPort(ScanI2C::I2CPort::WIRE, i2caddr_scan, i2caddr_asize); @@ -57,4 +57,4 @@ void CardKbI2cImpl::init() } #endif inputBroker->registerSource(this); -} +} \ No newline at end of file diff --git a/src/input/kbI2cBase.cpp b/src/input/kbI2cBase.cpp index 024b16b9e..8b201cd22 100644 --- a/src/input/kbI2cBase.cpp +++ b/src/input/kbI2cBase.cpp @@ -33,7 +33,7 @@ int32_t KbI2cBase::runOnce() if (!i2cBus) { switch (cardkb_found.port) { case ScanI2C::WIRE1: -#ifdef I2C_SDA1 +#if WIRE_INTERFACES_COUNT == 2 LOG_DEBUG("Using I2C Bus 1 (the second one)\n"); i2cBus = &Wire1; if (cardkb_found.address == BBQ10_KB_ADDR) { @@ -94,7 +94,7 @@ int32_t KbI2cBase::runOnce() case 'e': // sym e if (is_sym) { e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP; - e.kbchar = 0xb5; + e.kbchar = INPUT_BROKER_MSG_UP; is_sym = false; // reset sym state after second keypress } else { e.inputEvent = ANYKEY; @@ -104,7 +104,7 @@ int32_t KbI2cBase::runOnce() case 'x': // sym x if (is_sym) { e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN; - e.kbchar = 0xb6; + e.kbchar = INPUT_BROKER_MSG_DOWN; is_sym = false; // reset sym state after second keypress } else { e.inputEvent = ANYKEY; @@ -134,8 +134,8 @@ int32_t KbI2cBase::runOnce() case 0x13: // Code scanner says the SYM key is 0x13 is_sym = !is_sym; e.inputEvent = ANYKEY; - e.kbchar = - is_sym ? 0xf1 : 0xf2; // send 0xf1 to tell CannedMessages to display that the modifier key is active + e.kbchar = is_sym ? INPUT_BROKER_MSG_FN_SYMBOL_ON // send 0xf1 to tell CannedMessages to display that + : INPUT_BROKER_MSG_FN_SYMBOL_OFF; // the modifier key is active break; case 0x0a: // apparently Enter on Q10 is a line feed instead of carriage return e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT; @@ -214,7 +214,7 @@ int32_t KbI2cBase::runOnce() if (is_sym) { is_sym = false; e.inputEvent = ANYKEY; - e.kbchar = 0xac; // mute notifications + e.kbchar = INPUT_BROKER_MSG_MUTE_TOGGLE; // mute notifications } else { e.inputEvent = ANYKEY; e.kbchar = c; @@ -224,7 +224,7 @@ int32_t KbI2cBase::runOnce() if (is_sym) { is_sym = false; e.inputEvent = ANYKEY; - e.kbchar = 0x11; // Increase Brightness code + e.kbchar = INPUT_BROKER_MSG_BRIGHTNESS_UP; // Increase Brightness code } else { e.inputEvent = ANYKEY; e.kbchar = c; @@ -234,7 +234,7 @@ int32_t KbI2cBase::runOnce() if (is_sym) { is_sym = false; e.inputEvent = ANYKEY; - e.kbchar = 0x12; // Decrease Brightness code + e.kbchar = INPUT_BROKER_MSG_BRIGHTNESS_DOWN; // Decrease Brightness code } else { e.inputEvent = ANYKEY; e.kbchar = c; @@ -244,7 +244,7 @@ int32_t KbI2cBase::runOnce() if (is_sym) { is_sym = false; e.inputEvent = ANYKEY; - e.kbchar = 0xaf; // (fn + space) + e.kbchar = INPUT_BROKER_MSG_SEND_PING; // (fn + space) } else { e.inputEvent = ANYKEY; e.kbchar = c; @@ -254,7 +254,7 @@ int32_t KbI2cBase::runOnce() if (is_sym) { is_sym = false; e.inputEvent = ANYKEY; - e.kbchar = 0x9e; + e.kbchar = INPUT_BROKER_MSG_GPS_TOGGLE; } else { e.inputEvent = ANYKEY; e.kbchar = c; @@ -269,32 +269,35 @@ int32_t KbI2cBase::runOnce() break; case 0xb5: // Up e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP; - e.kbchar = 0xb5; + e.kbchar = INPUT_BROKER_MSG_UP; break; case 0xb6: // Down e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN; - e.kbchar = 0xb6; + e.kbchar = INPUT_BROKER_MSG_DOWN; break; case 0xb4: // Left e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT; - e.kbchar = 0xb4; + e.kbchar = INPUT_BROKER_MSG_LEFT; break; case 0xb7: // Right e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT; - e.kbchar = 0xb7; + e.kbchar = INPUT_BROKER_MSG_RIGHT; break; case 0xc: // Modifier key: 0xc is alt+c (Other options could be: 0xea = shift+mic button or 0x4 shift+$(speaker)) // toggle moddifiers button. is_sym = !is_sym; e.inputEvent = ANYKEY; - e.kbchar = is_sym ? 0xf1 : 0xf2; // send 0xf1 to tell CannedMessages to display that the modifier key is active + e.kbchar = is_sym ? INPUT_BROKER_MSG_FN_SYMBOL_ON // send 0xf1 to tell CannedMessages to display that the + : INPUT_BROKER_MSG_FN_SYMBOL_OFF; // modifier key is active break; - case 0x90: // fn+r + case 0x90: // fn+r INPUT_BROKER_MSG_REBOOT case 0x91: // fn+t - case 0x9b: // fn+s - case 0xac: // fn+m - case 0x9e: // fn+g - case 0xaf: // fn+space + case 0x9b: // fn+s INPUT_BROKER_MSG_SHUTDOWN + case 0xac: // fn+m INPUT_BROKER_MSG_MUTE_TOGGLE + case 0x9e: // fn+g INPUT_BROKER_MSG_GPS_TOGGLE + case 0xaf: // fn+space INPUT_BROKER_MSG_SEND_PING + case 0x8b: // fn+del INPUT_BROKEN_MSG_DISMISS_FRAME + case 0xAA: // fn+b INPUT_BROKER_MSG_BLUETOOTH_TOGGLE // just pass those unmodified e.inputEvent = ANYKEY; e.kbchar = c; diff --git a/src/main.cpp b/src/main.cpp index c8345eaa1..a792f2e47 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,3 +1,4 @@ +#include "../userPrefs.h" #include "configuration.h" #if !MESHTASTIC_EXCLUDE_GPS #include "GPS.h" @@ -11,16 +12,19 @@ #include "airtime.h" #include "buzz.h" -#include "error.h" -#include "power.h" -// #include "debug.h" #include "FSCommon.h" #include "Led.h" #include "SPILock.h" +#include "Throttle.h" #include "concurrency/OSThread.h" #include "concurrency/Periodic.h" #include "detect/ScanI2C.h" +<<<<<<< HEAD #include "gps/RTC.h" +======= +#include "error.h" +#include "power.h" +>>>>>>> master #if !MESHTASTIC_EXCLUDE_I2C #include "detect/ScanI2CTwoWire.h" @@ -33,13 +37,13 @@ #include "graphics/Screen.h" #include "main.h" #include "mesh/generated/meshtastic/config.pb.h" +#include "meshUtils.h" #include "modules/Modules.h" #include "shutdown.h" #include "sleep.h" #include "target_specific.h" #include #include -// #include #ifdef ARCH_ESP32 #if !MESHTASTIC_EXCLUDE_WEBSERVER @@ -73,6 +77,7 @@ NRF52Bluetooth *nrf52Bluetooth = nullptr; #include "LLCC68Interface.h" #include "LR1110Interface.h" #include "LR1120Interface.h" +#include "LR1121Interface.h" #include "RF95Interface.h" #include "SX1262Interface.h" #include "SX1268Interface.h" @@ -103,8 +108,13 @@ NRF52Bluetooth *nrf52Bluetooth = nullptr; #include "AmbientLightingThread.h" #include "PowerFSMThread.h" +<<<<<<< HEAD #if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !defined(ARCH_APOLLO3) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR #include "AccelerometerThread.h" +======= +#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C +#include "motion/AccelerometerThread.h" +>>>>>>> master AccelerometerThread *accelerometerThread = nullptr; #endif @@ -119,6 +129,8 @@ float tcxoVoltage = SX126X_DIO3_TCXO_VOLTAGE; // if TCXO is optional, put this h using namespace concurrency; +volatile static const char slipstreamTZString[] = USERPREFS_TZ_STRING; + // We always create a screen object, but we only init it if we find the hardware graphics::Screen *screen = nullptr; @@ -323,15 +335,19 @@ void setup() #ifdef BUTTON_PIN #ifdef ARCH_ESP32 - // If the button is connected to GPIO 12, don't enable the ability to use - // meshtasticAdmin on the device. - pinMode(config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN, INPUT); - +#if ESP_ARDUINO_VERSION_MAJOR >= 3 +#ifdef BUTTON_NEED_PULLUP + pinMode(config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN, INPUT_PULLUP); +#else + pinMode(config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN, INPUT); // default to BUTTON_PIN +#endif +#else + pinMode(config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN, INPUT); // default to BUTTON_PIN #ifdef BUTTON_NEED_PULLUP gpio_pullup_en((gpio_num_t)(config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN)); delay(10); #endif - +#endif #endif #endif @@ -361,6 +377,8 @@ void setup() Wire1.begin(); #elif defined(I2C_SDA1) && !defined(ARCH_RP2040) Wire1.begin(I2C_SDA1, I2C_SCL1); +#elif WIRE_INTERFACES_COUNT == 2 + Wire1.begin(); #endif #if defined(I2C_SDA) && defined(ARCH_RP2040) @@ -391,7 +409,7 @@ void setup() #endif #ifdef AQ_SET_PIN - // RAK-12039 set pin for Air quality sensor + // RAK-12039 set pin for Air quality sensor. Detectable on I2C after ~3 seconds, so we need to rescan later pinMode(AQ_SET_PIN, OUTPUT); digitalWrite(AQ_SET_PIN, HIGH); #endif @@ -428,6 +446,8 @@ void setup() #elif defined(I2C_SDA1) && !defined(ARCH_RP2040) Wire1.begin(I2C_SDA1, I2C_SCL1); i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1); +#elif defined(NRF52840_XXAA) && (WIRE_INTERFACES_COUNT == 2) + i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1); #endif #if defined(I2C_SDA) && defined(ARCH_RP2040) @@ -553,6 +573,7 @@ void setup() SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::INA260, meshtastic_TelemetrySensorType_INA260) SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::INA219, meshtastic_TelemetrySensorType_INA219) SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::INA3221, meshtastic_TelemetrySensorType_INA3221) + SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MAX17048, meshtastic_TelemetrySensorType_MAX17048) SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MCP9808, meshtastic_TelemetrySensorType_MCP9808) SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MCP9808, meshtastic_TelemetrySensorType_MCP9808) SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::SHT31, meshtastic_TelemetrySensorType_SHT31) @@ -561,15 +582,19 @@ void setup() SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::QMC6310, meshtastic_TelemetrySensorType_QMC6310) SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::QMI8658, meshtastic_TelemetrySensorType_QMI8658) SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::QMC5883L, meshtastic_TelemetrySensorType_QMC5883L) + SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::HMC5883L, meshtastic_TelemetrySensorType_QMC5883L) SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::PMSA0031, meshtastic_TelemetrySensorType_PMSA003I) SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::RCWL9620, meshtastic_TelemetrySensorType_RCWL9620) SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::VEML7700, meshtastic_TelemetrySensorType_VEML7700) SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::TSL2591, meshtastic_TelemetrySensorType_TSL25911FN) SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::OPT3001, meshtastic_TelemetrySensorType_OPT3001) SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MLX90632, meshtastic_TelemetrySensorType_MLX90632) + SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MLX90614, meshtastic_TelemetrySensorType_MLX90614) SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::SHT4X, meshtastic_TelemetrySensorType_SHT4X) SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::AHT10, meshtastic_TelemetrySensorType_AHT10) SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::DFROBOT_LARK, meshtastic_TelemetrySensorType_DFROBOT_LARK) + SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::ICM20948, meshtastic_TelemetrySensorType_ICM20948) + SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MAX30102, meshtastic_TelemetrySensorType_MAX30102) i2cScanner.reset(); #endif @@ -587,6 +612,9 @@ void setup() // Hello printInfo(); +#ifdef BUILD_EPOCH + LOG_INFO("Build timestamp: %ld\n", BUILD_EPOCH); +#endif #ifdef ARCH_ESP32 esp32Setup(); @@ -618,7 +646,13 @@ void setup() buttonThread = new ButtonThread(); #endif - playStartMelody(); + // only play start melody when role is not tracker or sensor + if (config.power.is_power_saving == true && + IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_TRACKER, + meshtastic_Config_DeviceConfig_Role_TAK_TRACKER, meshtastic_Config_DeviceConfig_Role_SENSOR)) + LOG_DEBUG("Tracker/Sensor: Skipping start melody\n"); + else + playStartMelody(); // fixed screen override? if (config.display.oled != meshtastic_Config_DisplayConfig_OledType_OLED_AUTO) @@ -634,10 +668,8 @@ void setup() #endif #if !MESHTASTIC_EXCLUDE_I2C -#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !defined(ARCH_APOLLO3) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR +#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !defined(ARCH_APOLLO3) if (acc_info.type != ScanI2C::DeviceType::NONE) { - config.display.wake_on_tap_or_motion = true; - moduleConfig.external_notification.enabled = true; accelerometerThread = new AccelerometerThread(acc_info.type); } #endif @@ -688,10 +720,17 @@ void setup() // setup TZ prior to time actions. #if !MESHTASTIC_EXCLUDE_TZ - if (*config.device.tzdef) { + LOG_DEBUG("Using compiled/slipstreamed %s\n", slipstreamTZString); // important, removing this clobbers our magic string + if (*config.device.tzdef && config.device.tzdef[0] != 0) { + LOG_DEBUG("Saved TZ: %s \n", config.device.tzdef); setenv("TZ", config.device.tzdef, 1); } else { - setenv("TZ", "GMT0", 1); + if (strncmp((const char *)slipstreamTZString, "tzpl", 4) == 0) { + setenv("TZ", "GMT0", 1); + } else { + setenv("TZ", (const char *)slipstreamTZString, 1); + strcpy(config.device.tzdef, (const char *)slipstreamTZString); + } } tzset(); LOG_DEBUG("Set Timezone to %s\n", getenv("TZ")); @@ -748,8 +787,8 @@ void setup() #if !MESHTASTIC_EXCLUDE_I2C // Don't call screen setup until after nodedb is setup (because we need // the current region name) -#if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7789_CS) || defined(HX8357_CS) || \ - defined(USE_ST7789) +#if defined(ST7701_CS) || defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || \ + defined(ST7789_CS) || defined(HX8357_CS) || defined(USE_ST7789) screen->setup(); #elif defined(ARCH_PORTDUINO) if (screen_found.port != ScanI2C::I2CPort::NO_I2C || settingsMap[displayPanel]) { @@ -763,12 +802,6 @@ void setup() screen->print("Started...\n"); -#ifdef SX126X_ANT_SW - // make analog PA vs not PA switch on SX126x eval board work properly - pinMode(SX126X_ANT_SW, OUTPUT); - digitalWrite(SX126X_ANT_SW, 1); -#endif - #ifdef PIN_PWR_DELAY_MS // This may be required to give the peripherals time to power up. delay(PIN_PWR_DELAY_MS); @@ -873,8 +906,8 @@ void setup() } #endif -#if defined(RF95_IRQ) - if (!rIf) { +#if defined(RF95_IRQ) && RADIOLIB_EXCLUDE_SX127X != 1 + if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) { rIf = new RF95Interface(RadioLibHAL, LORA_CS, RF95_IRQ, RF95_RESET, RF95_DIO1); if (!rIf->init()) { LOG_WARN("Failed to find RF95 radio\n"); @@ -887,8 +920,8 @@ void setup() } #endif -#if defined(USE_SX1262) && !defined(ARCH_PORTDUINO) && !defined(TCXO_OPTIONAL) - if (!rIf) { +#if defined(USE_SX1262) && !defined(ARCH_PORTDUINO) && !defined(TCXO_OPTIONAL) && RADIOLIB_EXCLUDE_SX126X != 1 + if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) { rIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY); if (!rIf->init()) { LOG_WARN("Failed to find SX1262 radio\n"); @@ -902,7 +935,7 @@ void setup() #endif #if defined(USE_SX1262) && !defined(ARCH_PORTDUINO) && defined(TCXO_OPTIONAL) - if (!rIf) { + if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) { // Try using the specified TCXO voltage rIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY); if (!rIf->init()) { @@ -918,7 +951,7 @@ void setup() } } - if (!rIf) { + if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) { // If specified TCXO voltage fails, attempt to use DIO3 as a reference instea rIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY); if (!rIf->init()) { @@ -936,7 +969,7 @@ void setup() #endif #if defined(USE_SX1268) - if (!rIf) { + if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) { rIf = new SX1268Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY); if (!rIf->init()) { LOG_WARN("Failed to find SX1268 radio\n"); @@ -950,7 +983,7 @@ void setup() #endif #if defined(USE_LLCC68) - if (!rIf) { + if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) { rIf = new LLCC68Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY); if (!rIf->init()) { LOG_WARN("Failed to find LLCC68 radio\n"); @@ -963,33 +996,49 @@ void setup() } #endif -#if defined(USE_LR1110) - if (!rIf) { - rIf = new LR1110Interface(RadioLibHAL, LR1110_SPI_NSS_PIN, LR1110_IRQ_PIN, LR1110_NRESER_PIN, LR1110_BUSY_PIN); +#if defined(USE_LR1110) && RADIOLIB_EXCLUDE_LR11X0 != 1 + if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) { + rIf = new LR1110Interface(RadioLibHAL, LR1110_SPI_NSS_PIN, LR1110_IRQ_PIN, LR1110_NRESET_PIN, LR1110_BUSY_PIN); if (!rIf->init()) { LOG_WARN("Failed to find LR1110 radio\n"); delete rIf; rIf = NULL; } else { LOG_INFO("LR1110 Radio init succeeded, using LR1110 radio\n"); + radioType = LR1110_RADIO; } } #endif -#if defined(USE_LR1120) +#if defined(USE_LR1120) && RADIOLIB_EXCLUDE_LR11X0 != 1 if (!rIf) { - rIf = new LR1120Interface(RadioLibHAL, LR1120_SPI_NSS_PIN, LR1120_IRQ_PIN, LR1120_NRESER_PIN, LR1120_BUSY_PIN); + rIf = new LR1120Interface(RadioLibHAL, LR1120_SPI_NSS_PIN, LR1120_IRQ_PIN, LR1120_NRESET_PIN, LR1120_BUSY_PIN); if (!rIf->init()) { LOG_WARN("Failed to find LR1120 radio\n"); delete rIf; rIf = NULL; } else { LOG_INFO("LR1120 Radio init succeeded, using LR1120 radio\n"); + radioType = LR1120_RADIO; } } #endif -#if defined(USE_SX1280) +#if defined(USE_LR1121) && RADIOLIB_EXCLUDE_LR11X0 != 1 + if (!rIf) { + rIf = new LR1121Interface(RadioLibHAL, LR1121_SPI_NSS_PIN, LR1121_IRQ_PIN, LR1121_NRESET_PIN, LR1121_BUSY_PIN); + if (!rIf->init()) { + LOG_WARN("Failed to find LR1121 radio\n"); + delete rIf; + rIf = NULL; + } else { + LOG_INFO("LR1121 Radio init succeeded, using LR1121 radio\n"); + radioType = LR1121_RADIO; + } + } +#endif + +#if defined(USE_SX1280) && RADIOLIB_EXCLUDE_SX128X != 1 if (!rIf) { rIf = new SX1280Interface(RadioLibHAL, SX128X_CS, SX128X_DIO1, SX128X_RESET, SX128X_BUSY); if (!rIf->init()) { @@ -1004,7 +1053,6 @@ void setup() #endif // check if the radio chip matches the selected region - if ((config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_LORA_24) && (!rIf->wideLora())) { LOG_WARN("Radio chip does not support 2.4GHz LoRa. Reverting to unset.\n"); config.lora.region = meshtastic_Config_LoRaConfig_RegionCode_UNSET; @@ -1096,6 +1144,9 @@ extern meshtastic_DeviceMetadata getDeviceMetadata() deviceMetadata.position_flags = config.position.position_flags; deviceMetadata.hw_model = HW_VENDOR; deviceMetadata.hasRemoteHardware = moduleConfig.remote_hardware.enabled; +#if !(MESHTASTIC_EXCLUDE_PKI) + deviceMetadata.hasPKC = true; +#endif return deviceMetadata; } #ifndef PIO_UNIT_TESTING @@ -1103,10 +1154,6 @@ void loop() { runASAP = false; - // axpDebugOutput.loop(); - - // heap_caps_check_integrity_all(true); // FIXME - disable this expensive check - #ifdef ARCH_ESP32 esp32Loop(); #endif @@ -1115,33 +1162,21 @@ void loop() #endif powerCommandsCheck(); - // For debugging - // if (rIf) ((RadioLibInterface *)rIf)->isActivelyReceiving(); - #ifdef DEBUG_STACK static uint32_t lastPrint = 0; - if (millis() - lastPrint > 10 * 1000L) { + if (!Throttle::isWithinTimespanMs(lastPrint, 10 * 1000L)) { lastPrint = millis(); meshtastic::printThreadInfo("main"); } #endif - // TODO: This should go into a thread handled by FreeRTOS. - // handleWebResponse(); - service->loop(); long delayMsec = mainController.runOrDelay(); - /* if (mainController.nextThread && delayMsec) - LOG_DEBUG("Next %s in %ld\n", mainController.nextThread->ThreadName.c_str(), - mainController.nextThread->tillRun(millis())); */ - // We want to sleep as long as possible here - because it saves power if (!runASAP && loopCanSleep()) { - // if(delayMsec > 100) LOG_DEBUG("sleeping %ld\n", delayMsec); mainDelay.delay(delayMsec); } - // if (didWake) LOG_DEBUG("wake!\n"); } #endif \ No newline at end of file diff --git a/src/main.h b/src/main.h index 896015b68..d3b6ade47 100644 --- a/src/main.h +++ b/src/main.h @@ -56,8 +56,8 @@ extern AudioThread *audioThread; // Global Screen singleton. extern graphics::Screen *screen; -#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR -#include "AccelerometerThread.h" +#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C +#include "motion/AccelerometerThread.h" extern AccelerometerThread *accelerometerThread; #endif @@ -86,4 +86,4 @@ void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop(), rp2040Setup(), clearB meshtastic_DeviceMetadata getDeviceMetadata(); // We default to 4MHz SPI, SPI mode 0 -extern SPISettings spiSettings; +extern SPISettings spiSettings; \ No newline at end of file diff --git a/src/memGet.cpp b/src/memGet.cpp index e982ef7ee..ef1102f1e 100644 --- a/src/memGet.cpp +++ b/src/memGet.cpp @@ -57,6 +57,8 @@ uint32_t MemGet::getFreePsram() { #ifdef ARCH_ESP32 return ESP.getFreePsram(); +#elif defined(ARCH_PORTDUINO) + return 4194252; #else return 0; #endif @@ -71,6 +73,8 @@ uint32_t MemGet::getPsramSize() { #ifdef ARCH_ESP32 return ESP.getPsramSize(); +#elif defined(ARCH_PORTDUINO) + return 4194252; #else return 0; #endif diff --git a/src/mesh/Channels.cpp b/src/mesh/Channels.cpp index bba7571d2..bb30e501d 100644 --- a/src/mesh/Channels.cpp +++ b/src/mesh/Channels.cpp @@ -13,10 +13,6 @@ #include "mqtt/MQTT.h" #endif -/// 16 bytes of random PSK for our _public_ default channel that all devices power up on (AES128) -static const uint8_t defaultpsk[] = {0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59, - 0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0x01}; - Channels channels; const char *Channels::adminChannel = "admin"; @@ -80,6 +76,23 @@ meshtastic_Channel &Channels::fixupChannel(ChannelIndex chIndex) return ch; } +void Channels::initDefaultLoraConfig() +{ + meshtastic_Config_LoRaConfig &loraConfig = config.lora; + + loraConfig.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST; // Default to Long Range & Fast + loraConfig.use_preset = true; + loraConfig.tx_power = 0; // default + loraConfig.channel_num = 0; + +#ifdef USERPREFS_LORACONFIG_MODEM_PRESET + loraConfig.modem_preset = USERPREFS_LORACONFIG_MODEM_PRESET; +#endif +#ifdef USERPREFS_LORACONFIG_CHANNEL_NUM + loraConfig.channel_num = USERPREFS_LORACONFIG_CHANNEL_NUM; +#endif +} + /** * Write a default channel to the specified channel index */ @@ -87,12 +100,7 @@ void Channels::initDefaultChannel(ChannelIndex chIndex) { meshtastic_Channel &ch = getByIndex(chIndex); meshtastic_ChannelSettings &channelSettings = ch.settings; - meshtastic_Config_LoRaConfig &loraConfig = config.lora; - loraConfig.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST; // Default to Long Range & Fast - loraConfig.use_preset = true; - loraConfig.tx_power = 0; // default - loraConfig.channel_num = 0; uint8_t defaultpskIndex = 1; channelSettings.psk.bytes[0] = defaultpskIndex; channelSettings.psk.size = 1; @@ -101,29 +109,53 @@ void Channels::initDefaultChannel(ChannelIndex chIndex) channelSettings.has_module_settings = true; ch.has_settings = true; - ch.role = meshtastic_Channel_Role_PRIMARY; + ch.role = chIndex == 0 ? meshtastic_Channel_Role_PRIMARY : meshtastic_Channel_Role_SECONDARY; -#ifdef LORACONFIG_MODEM_PRESET_USERPREFS - loraConfig.modem_preset = LORACONFIG_MODEM_PRESET_USERPREFS; -#endif -#ifdef LORACONFIG_CHANNEL_NUM_USERPREFS - loraConfig.channel_num = LORACONFIG_CHANNEL_NUM_USERPREFS; -#endif - - // Install custom defaults. Will eventually support setting multiple default channels - if (chIndex == 0) { -#ifdef CHANNEL_0_PSK_USERPREFS - static const uint8_t defaultpsk[] = CHANNEL_0_PSK_USERPREFS; - memcpy(channelSettings.psk.bytes, defaultpsk, sizeof(defaultpsk)); - channelSettings.psk.size = sizeof(defaultpsk); + switch (chIndex) { + case 0: +#ifdef USERPREFS_CHANNEL_0_PSK + static const uint8_t defaultpsk0[] = USERPREFS_CHANNEL_0_PSK; + memcpy(channelSettings.psk.bytes, defaultpsk0, sizeof(defaultpsk0)); + channelSettings.psk.size = sizeof(defaultpsk0); #endif -#ifdef CHANNEL_0_NAME_USERPREFS - strcpy(channelSettings.name, CHANNEL_0_NAME_USERPREFS); +#ifdef USERPREFS_CHANNEL_0_NAME + strcpy(channelSettings.name, USERPREFS_CHANNEL_0_NAME); #endif -#ifdef CHANNEL_0_PRECISION_USERPREFS - channelSettings.module_settings.position_precision = CHANNEL_0_PRECISION_USERPREFS; +#ifdef USERPREFS_CHANNEL_0_PRECISION + channelSettings.module_settings.position_precision = USERPREFS_CHANNEL_0_PRECISION; #endif + break; + case 1: +#ifdef USERPREFS_CHANNEL_1_PSK + static const uint8_t defaultpsk1[] = USERPREFS_CHANNEL_1_PSK; + memcpy(channelSettings.psk.bytes, defaultpsk1, sizeof(defaultpsk1)); + channelSettings.psk.size = sizeof(defaultpsk1); + +#endif +#ifdef USERPREFS_CHANNEL_1_NAME + strcpy(channelSettings.name, USERPREFS_CHANNEL_1_NAME); +#endif +#ifdef USERPREFS_CHANNEL_1_PRECISION + channelSettings.module_settings.position_precision = USERPREFS_CHANNEL_1_PRECISION; +#endif + break; + case 2: +#ifdef USERPREFS_CHANNEL_2_PSK + static const uint8_t defaultpsk2[] = USERPREFS_CHANNEL_2_PSK; + memcpy(channelSettings.psk.bytes, defaultpsk2, sizeof(defaultpsk2)); + channelSettings.psk.size = sizeof(defaultpsk2); + +#endif +#ifdef USERPREFS_CHANNEL_2_NAME + strcpy(channelSettings.name, USERPREFS_CHANNEL_2_NAME); +#endif +#ifdef USERPREFS_CHANNEL_2_PRECISION + channelSettings.module_settings.position_precision = USERPREFS_CHANNEL_2_PRECISION; +#endif + break; + default: + break; } } @@ -213,7 +245,15 @@ void Channels::initDefaults() channelFile.channels_count = MAX_NUM_CHANNELS; for (int i = 0; i < channelFile.channels_count; i++) fixupChannel(i); + initDefaultLoraConfig(); + +#ifdef USERPREFS_CHANNELS_TO_WRITE + for (int i = 0; i < USERPREFS_CHANNELS_TO_WRITE; i++) { + initDefaultChannel(i); + } +#else initDefaultChannel(0); +#endif } void Channels::onConfigChanged() @@ -277,7 +317,7 @@ void Channels::setChannel(const meshtastic_Channel &c) bool Channels::anyMqttEnabled() { -#if EVENT_MODE +#if USERPREFS_EVENT_MODE // Don't publish messages on the public MQTT broker if we are in event mode if (strcmp(moduleConfig.mqtt.address, default_mqtt_address) == 0) { return false; @@ -363,4 +403,4 @@ bool Channels::decryptForHash(ChannelIndex chIndex, ChannelHash channelHash) int16_t Channels::setActiveByIndex(ChannelIndex channelIndex) { return setCrypto(channelIndex); -} +} \ No newline at end of file diff --git a/src/mesh/Channels.h b/src/mesh/Channels.h index 4f87cb309..b0c9b3d07 100644 --- a/src/mesh/Channels.h +++ b/src/mesh/Channels.h @@ -117,7 +117,12 @@ class Channels meshtastic_Channel &fixupChannel(ChannelIndex chIndex); /** - * Write a default channel to the specified channel index + * Writes the default lora config + */ + void initDefaultLoraConfig(); + + /** + * Write default channels defined in UserPrefs */ void initDefaultChannel(ChannelIndex chIndex); @@ -129,4 +134,12 @@ class Channels }; /// Singleton channel table -extern Channels channels; \ No newline at end of file +extern Channels channels; + +/// 16 bytes of random PSK for our _public_ default channel that all devices power up on (AES128) +static const uint8_t defaultpsk[] = {0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59, + 0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0x01}; + +static const uint8_t eventpsk[] = {0x38, 0x4b, 0xbc, 0xc0, 0x1d, 0xc0, 0x22, 0xd1, 0x81, 0xbf, 0x36, + 0xb8, 0x61, 0x21, 0xe1, 0xfb, 0x96, 0xb7, 0x2e, 0x55, 0xbf, 0x74, + 0x22, 0x7e, 0x9d, 0x6a, 0xfb, 0x48, 0xd6, 0x4c, 0xb1, 0xa1}; \ No newline at end of file diff --git a/src/mesh/CryptoEngine.cpp b/src/mesh/CryptoEngine.cpp index a5322a65a..a875eb8b2 100644 --- a/src/mesh/CryptoEngine.cpp +++ b/src/mesh/CryptoEngine.cpp @@ -66,12 +66,11 @@ bool CryptoEngine::encryptCurve25519(uint32_t toNode, uint32_t fromNode, uint64_ uint8_t *bytesOut) { uint8_t *auth; - uint32_t *extraNonce; long extraNonceTmp = random(); auth = bytesOut + numBytes; - extraNonce = (uint32_t *)(auth + 8); - *extraNonce = extraNonceTmp; - LOG_INFO("Random nonce value: %d\n", *extraNonce); + memcpy((uint8_t *)(auth + 8), &extraNonceTmp, + sizeof(uint32_t)); // do not use dereference on potential non aligned pointers : *extraNonce = extraNonceTmp; + LOG_INFO("Random nonce value: %d\n", extraNonceTmp); meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(toNode); if (node->num < 1 || node->user.public_key.size == 0) { LOG_DEBUG("Node %d or their public_key not found\n", toNode); @@ -80,14 +79,15 @@ bool CryptoEngine::encryptCurve25519(uint32_t toNode, uint32_t fromNode, uint64_ if (!crypto->setDHKey(toNode)) { return false; } - initNonce(fromNode, packetNum, *extraNonce); + initNonce(fromNode, packetNum, extraNonceTmp); // Calculate the shared secret with the destination node and encrypt printBytes("Attempting encrypt using nonce: ", nonce, 13); - printBytes("Attempting encrypt using shared_key: ", shared_key, 32); + printBytes("Attempting encrypt using shared_key starting with: ", shared_key, 8); aes_ccm_ae(shared_key, 32, nonce, 8, bytes, numBytes, nullptr, 0, bytesOut, auth); // this can write up to 15 bytes longer than numbytes past bytesOut - *extraNonce = extraNonceTmp; + memcpy((uint8_t *)(auth + 8), &extraNonceTmp, + sizeof(uint32_t)); // do not use dereference on potential non aligned pointers : *extraNonce = extraNonceTmp; return true; } @@ -99,11 +99,13 @@ bool CryptoEngine::encryptCurve25519(uint32_t toNode, uint32_t fromNode, uint64_ */ bool CryptoEngine::decryptCurve25519(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes, uint8_t *bytesOut) { - uint8_t *auth; // set to last 8 bytes of text? - uint32_t *extraNonce; + uint8_t *auth; // set to last 8 bytes of text? + uint32_t extraNonce; // pointer was not really used auth = bytes + numBytes - 12; - extraNonce = (uint32_t *)(auth + 8); - LOG_INFO("Random nonce value: %d\n", *extraNonce); + memcpy(&extraNonce, auth + 8, + sizeof(uint32_t)); // do not use dereference on potential non aligned pointers : (uint32_t *)(auth + 8); +#ifndef PIO_UNIT_TESTING + LOG_INFO("Random nonce value: %d\n", extraNonce); meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(fromNode); if (node == nullptr || node->num < 1 || node->user.public_key.size == 0) { @@ -115,9 +117,10 @@ bool CryptoEngine::decryptCurve25519(uint32_t fromNode, uint64_t packetNum, size if (!crypto->setDHKey(fromNode)) { return false; } - initNonce(fromNode, packetNum, *extraNonce); +#endif + initNonce(fromNode, packetNum, extraNonce); printBytes("Attempting decrypt using nonce: ", nonce, 13); - printBytes("Attempting decrypt using shared_key: ", shared_key, 32); + printBytes("Attempting decrypt using shared_key starting with: ", shared_key, 8); return aes_ccm_ad(shared_key, 32, nonce, 8, bytes, numBytes - 12, nullptr, 0, auth, bytesOut); } @@ -137,11 +140,12 @@ bool CryptoEngine::setDHKey(uint32_t nodeNum) LOG_DEBUG("Node %d or their public_key not found\n", nodeNum); return false; } - + printBytes("Generating DH with remote pubkey: ", node->user.public_key.bytes, 32); + printBytes("And local pubkey: ", config.security.public_key.bytes, 32); if (!setDHPublicKey(node->user.public_key.bytes)) return false; - printBytes("DH Output: ", shared_key, 32); + // printBytes("DH Output: ", shared_key, 32); /** * D.J. Bernstein reccomends hashing the shared key. We want to do this because there are @@ -166,12 +170,12 @@ bool CryptoEngine::setDHKey(uint32_t nodeNum) void CryptoEngine::hash(uint8_t *bytes, size_t numBytes) { SHA256 hash; - size_t posn, len; + size_t posn; uint8_t size = numBytes; uint8_t inc = 16; hash.reset(); for (posn = 0; posn < size; posn += inc) { - len = size - posn; + size_t len = size - posn; if (len > inc) len = inc; hash.update(bytes + posn, len); diff --git a/src/mesh/Default.cpp b/src/mesh/Default.cpp index ac7441394..0bedcfc91 100644 --- a/src/mesh/Default.cpp +++ b/src/mesh/Default.cpp @@ -45,7 +45,7 @@ uint32_t Default::getConfiguredOrDefaultMsScaled(uint32_t configured, uint32_t d uint8_t Default::getConfiguredOrDefaultHopLimit(uint8_t configured) { -#if EVENT_MODE +#if USERPREFS_EVENT_MODE return (configured > HOP_RELIABLE) ? HOP_RELIABLE : config.lora.hop_limit; #else return (configured >= HOP_MAX) ? HOP_MAX : config.lora.hop_limit; diff --git a/src/mesh/Default.h b/src/mesh/Default.h index bafa60898..5641b5d25 100644 --- a/src/mesh/Default.h +++ b/src/mesh/Default.h @@ -3,6 +3,8 @@ #include #define ONE_DAY 24 * 60 * 60 #define ONE_MINUTE_MS 60 * 1000 +#define THIRTY_SECONDS_MS 30 * 1000 +#define FIVE_SECONDS_MS 5 * 1000 #define default_gps_update_interval IF_ROUTER(ONE_DAY, 2 * 60) #define default_telemetry_broadcast_interval_secs IF_ROUTER(ONE_DAY / 2, 30 * 60) diff --git a/src/mesh/FloodingRouter.cpp b/src/mesh/FloodingRouter.cpp index fbe56159c..23f6b6f92 100644 --- a/src/mesh/FloodingRouter.cpp +++ b/src/mesh/FloodingRouter.cpp @@ -21,11 +21,13 @@ ErrorCode FloodingRouter::send(meshtastic_MeshPacket *p) bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p) { if (wasSeenRecently(p)) { // Note: this will also add a recent packet record - printPacket("Ignoring incoming msg we've already seen", p); + printPacket("Ignoring dupe incoming msg", p); + rxDupe++; if (config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER && config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER) { // cancel rebroadcast of this message *if* there was already one, unless we're a router/repeater! - Router::cancelSending(p->from, p->id); + if (Router::cancelSending(p->from, p->id)) + txRelayCanceled++; } return true; } @@ -36,18 +38,18 @@ bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p) void FloodingRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtastic_Routing *c) { bool isAckorReply = (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) && (p->decoded.request_id != 0); - if (isAckorReply && p->to != getNodeNum() && p->to != NODENUM_BROADCAST) { + if (isAckorReply && !isToUs(p) && p->to != NODENUM_BROADCAST) { // do not flood direct message that is ACKed or replied to - LOG_DEBUG("Receiving an ACK or reply not for me, but don't need to rebroadcast this direct message anymore.\n"); + LOG_DEBUG("Rxd an ACK/reply not for me, cancel rebroadcast.\n"); Router::cancelSending(p->to, p->decoded.request_id); // cancel rebroadcast for this DM } - if ((p->to != getNodeNum()) && (p->hop_limit > 0) && (getFrom(p) != getNodeNum())) { + if (!isToUs(p) && (p->hop_limit > 0) && !isFromUs(p)) { if (p->id != 0) { if (config.device.role != meshtastic_Config_DeviceConfig_Role_CLIENT_MUTE) { meshtastic_MeshPacket *tosend = packetPool.allocCopy(*p); // keep a copy because we will be sending it tosend->hop_limit--; // bump down the hop count -#if EVENT_MODE +#if USERPREFS_EVENT_MODE if (tosend->hop_limit > 2) { // if we are "correcting" the hop_limit, "correct" the hop_start by the same amount to preserve hops away. tosend->hop_start -= (tosend->hop_limit - 2); @@ -55,7 +57,7 @@ void FloodingRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas } #endif - LOG_INFO("Rebroadcasting received floodmsg to neighbors\n"); + LOG_INFO("Rebroadcasting received floodmsg\n"); // Note: we are careful to resend using the original senders node id // We are careful not to call our hooked version of send() - because we don't want to check this again Router::send(tosend); @@ -63,7 +65,7 @@ void FloodingRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas LOG_DEBUG("Not rebroadcasting. Role = Role_ClientMute\n"); } } else { - LOG_DEBUG("Ignoring a simple (0 id) broadcast\n"); + LOG_DEBUG("Ignoring 0 id broadcast\n"); } } // handle the packet as normal diff --git a/src/mesh/InterfacesTemplates.cpp b/src/mesh/InterfacesTemplates.cpp index f2cac8028..2720e8525 100644 --- a/src/mesh/InterfacesTemplates.cpp +++ b/src/mesh/InterfacesTemplates.cpp @@ -8,12 +8,19 @@ #include "api/ServerAPI.h" // We need this declaration for proper linking in derived classes +#if RADIOLIB_EXCLUDE_SX126X != 1 template class SX126xInterface; template class SX126xInterface; template class SX126xInterface; +#endif +#if RADIOLIB_EXCLUDE_SX128X != 1 template class SX128xInterface; +#endif +#if RADIOLIB_EXCLUDE_LR11X0 != 1 template class LR11x0Interface; template class LR11x0Interface; +template class LR11x0Interface; +#endif #ifdef ARCH_STM32WL template class SX126xInterface; #endif diff --git a/src/mesh/LLCC68Interface.cpp b/src/mesh/LLCC68Interface.cpp index 8109765a6..d92ea5450 100644 --- a/src/mesh/LLCC68Interface.cpp +++ b/src/mesh/LLCC68Interface.cpp @@ -1,3 +1,4 @@ +#if RADIOLIB_EXCLUDE_SX126X != 1 #include "LLCC68Interface.h" #include "configuration.h" #include "error.h" @@ -6,4 +7,5 @@ LLCC68Interface::LLCC68Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, R RADIOLIB_PIN_TYPE busy) : SX126xInterface(hal, cs, irq, rst, busy) { -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/mesh/LLCC68Interface.h b/src/mesh/LLCC68Interface.h index 7e0fa1439..1cd23e92b 100644 --- a/src/mesh/LLCC68Interface.h +++ b/src/mesh/LLCC68Interface.h @@ -1,5 +1,5 @@ #pragma once - +#if RADIOLIB_EXCLUDE_SX126X != 1 #include "SX126xInterface.h" /** @@ -15,4 +15,5 @@ class LLCC68Interface : public SX126xInterface public: LLCC68Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy); -}; \ No newline at end of file +}; +#endif \ No newline at end of file diff --git a/src/mesh/LR1110Interface.cpp b/src/mesh/LR1110Interface.cpp index c000bd838..5dbd3ff38 100644 --- a/src/mesh/LR1110Interface.cpp +++ b/src/mesh/LR1110Interface.cpp @@ -1,3 +1,5 @@ +#if RADIOLIB_EXCLUDE_LR11X0 != 1 + #include "LR1110Interface.h" #include "configuration.h" #include "error.h" @@ -6,4 +8,5 @@ LR1110Interface::LR1110Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, R RADIOLIB_PIN_TYPE busy) : LR11x0Interface(hal, cs, irq, rst, busy) { -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/mesh/LR1110Interface.h b/src/mesh/LR1110Interface.h index 79e7c36ca..2a2e6e861 100644 --- a/src/mesh/LR1110Interface.h +++ b/src/mesh/LR1110Interface.h @@ -1,5 +1,5 @@ #pragma once - +#if RADIOLIB_EXCLUDE_LR11X0 != 1 #include "LR11x0Interface.h" /** @@ -10,4 +10,5 @@ class LR1110Interface : public LR11x0Interface public: LR1110Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy); -}; \ No newline at end of file +}; +#endif \ No newline at end of file diff --git a/src/mesh/LR1120Interface.cpp b/src/mesh/LR1120Interface.cpp index 94f3568f7..a17ac87ef 100644 --- a/src/mesh/LR1120Interface.cpp +++ b/src/mesh/LR1120Interface.cpp @@ -1,3 +1,5 @@ +#if RADIOLIB_EXCLUDE_LR11X0 != 1 + #include "LR1120Interface.h" #include "configuration.h" #include "error.h" @@ -6,4 +8,10 @@ LR1120Interface::LR1120Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, R RADIOLIB_PIN_TYPE busy) : LR11x0Interface(hal, cs, irq, rst, busy) { -} \ No newline at end of file +} + +bool LR1120Interface::wideLora() +{ + return true; +} +#endif \ No newline at end of file diff --git a/src/mesh/LR1120Interface.h b/src/mesh/LR1120Interface.h index fc59293ec..d81a480a9 100644 --- a/src/mesh/LR1120Interface.h +++ b/src/mesh/LR1120Interface.h @@ -1,7 +1,7 @@ #pragma once +#if RADIOLIB_EXCLUDE_LR11X0 != 1 #include "LR11x0Interface.h" - /** * Our adapter for LR1120 wideband radios */ @@ -10,4 +10,6 @@ class LR1120Interface : public LR11x0Interface public: LR1120Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy); -}; \ No newline at end of file + bool wideLora() override; +}; +#endif \ No newline at end of file diff --git a/src/mesh/LR1121Interface.cpp b/src/mesh/LR1121Interface.cpp new file mode 100644 index 000000000..29bd07d08 --- /dev/null +++ b/src/mesh/LR1121Interface.cpp @@ -0,0 +1,16 @@ +#if RADIOLIB_EXCLUDE_LR11X0 != 1 +#include "LR1121Interface.h" +#include "configuration.h" +#include "error.h" + +LR1121Interface::LR1121Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, + RADIOLIB_PIN_TYPE busy) + : LR11x0Interface(hal, cs, irq, rst, busy) +{ +} + +bool LR1121Interface::wideLora() +{ + return true; +} +#endif \ No newline at end of file diff --git a/src/mesh/LR1121Interface.h b/src/mesh/LR1121Interface.h new file mode 100644 index 000000000..ebc5b59a9 --- /dev/null +++ b/src/mesh/LR1121Interface.h @@ -0,0 +1,16 @@ +#pragma once +#if RADIOLIB_EXCLUDE_LR11X0 != 1 + +#include "LR11x0Interface.h" + +/** + * Our adapter for LR1121 wideband radios + */ +class LR1121Interface : public LR11x0Interface +{ + public: + LR1121Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, + RADIOLIB_PIN_TYPE busy); + bool wideLora() override; +}; +#endif \ No newline at end of file diff --git a/src/mesh/LR11x0Interface.cpp b/src/mesh/LR11x0Interface.cpp index 8f0dc062e..6641634c4 100644 --- a/src/mesh/LR11x0Interface.cpp +++ b/src/mesh/LR11x0Interface.cpp @@ -1,15 +1,33 @@ +#if RADIOLIB_EXCLUDE_LR11X0 != 1 #include "LR11x0Interface.h" +#include "Throttle.h" #include "configuration.h" #include "error.h" #include "mesh/NodeDB.h" +#ifdef LR11X0_DIO_AS_RF_SWITCH +#include "rfswitch.h" +#else +static const uint32_t rfswitch_dio_pins[] = {RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC}; +static const Module::RfSwitchMode_t rfswitch_table[] = { + {LR11x0::MODE_STBY, {}}, {LR11x0::MODE_RX, {}}, {LR11x0::MODE_TX, {}}, {LR11x0::MODE_TX_HP, {}}, + {LR11x0::MODE_TX_HF, {}}, {LR11x0::MODE_GNSS, {}}, {LR11x0::MODE_WIFI, {}}, END_OF_MODE_TABLE, +}; +#endif + #ifdef ARCH_PORTDUINO #include "PortduinoGlue.h" #endif // Particular boards might define a different max power based on what their hardware can do, default to max power output if not // specified (may be dangerous if using external PA and LR11x0 power config forgotten) -#ifndef LR11X0_MAX_POWER -#define LR11X0_MAX_POWER 22 +#ifndef LR1110_MAX_POWER +#define LR1110_MAX_POWER 22 +#endif + +// the 2.4G part maxes at 13dBm + +#ifndef LR1120_MAX_POWER +#define LR1120_MAX_POWER 13 #endif template @@ -45,65 +63,28 @@ template bool LR11x0Interface::init() RadioLibInterface::init(); - if (power > LR11X0_MAX_POWER) // Clamp power to maximum defined level - power = LR11X0_MAX_POWER; + if (power > LR1110_MAX_POWER) // Clamp power to maximum defined level + power = LR1110_MAX_POWER; + + if ((power > LR1120_MAX_POWER) && + (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) // clamp again if wide freq range + power = LR1120_MAX_POWER; limitPower(); -#ifdef TRACKER_T1000_E // Tracker T1000E uses DIO5, DIO6, DIO7, DIO8 for RF switching - - static const uint32_t rfswitch_dio_pins[] = {RADIOLIB_LR11X0_DIO5, RADIOLIB_LR11X0_DIO6, RADIOLIB_LR11X0_DIO7, - RADIOLIB_LR11X0_DIO8, RADIOLIB_NC}; - - static const Module::RfSwitchMode_t rfswitch_table[] = { - // mode DIO5 DIO6 DIO7 DIO8 - {LR11x0::MODE_STBY, {LOW, LOW, LOW, LOW}}, {LR11x0::MODE_RX, {HIGH, LOW, LOW, HIGH}}, - {LR11x0::MODE_TX, {HIGH, HIGH, LOW, HIGH}}, {LR11x0::MODE_TX_HP, {LOW, HIGH, LOW, HIGH}}, - {LR11x0::MODE_TX_HF, {LOW, LOW, LOW, LOW}}, {LR11x0::MODE_GNSS, {LOW, LOW, HIGH, LOW}}, - {LR11x0::MODE_WIFI, {LOW, LOW, LOW, LOW}}, END_OF_MODE_TABLE, - }; - -#else - - // set RF switch configuration for Wio WM1110 - // Wio WM1110 uses DIO5 and DIO6 for RF switching - - static const uint32_t rfswitch_dio_pins[] = {RADIOLIB_LR11X0_DIO5, RADIOLIB_LR11X0_DIO6, RADIOLIB_NC, RADIOLIB_NC, - RADIOLIB_NC}; - - static const Module::RfSwitchMode_t rfswitch_table[] = { - // mode DIO5 DIO6 - {LR11x0::MODE_STBY, {LOW, LOW}}, {LR11x0::MODE_RX, {HIGH, LOW}}, - {LR11x0::MODE_TX, {HIGH, HIGH}}, {LR11x0::MODE_TX_HP, {LOW, HIGH}}, - {LR11x0::MODE_TX_HF, {LOW, LOW}}, {LR11x0::MODE_GNSS, {LOW, LOW}}, - {LR11x0::MODE_WIFI, {LOW, LOW}}, END_OF_MODE_TABLE, - }; - -#endif - -// We need to do this before begin() call -#ifdef LR11X0_DIO_AS_RF_SWITCH - LOG_DEBUG("Setting DIO RF switch\n"); - bool dioAsRfSwitch = true; -#elif defined(ARCH_PORTDUINO) - bool dioAsRfSwitch = false; - if (settingsMap[dio2_as_rf_switch]) { - LOG_DEBUG("Setting DIO RF switch\n"); - dioAsRfSwitch = true; - } -#else - bool dioAsRfSwitch = false; -#endif - - if (dioAsRfSwitch) - lora.setRfSwitchTable(rfswitch_dio_pins, rfswitch_table); - int res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage); // \todo Display actual typename of the adapter, not just `LR11x0` LOG_INFO("LR11x0 init result %d\n", res); if (res == RADIOLIB_ERR_CHIP_NOT_FOUND) return false; + LR11x0VersionInfo_t version; + res = lora.getVersionInfo(&version); + if (res == RADIOLIB_ERR_NONE) + LOG_DEBUG("LR11x0 Device %d, HW %d, FW %d.%d, WiFi %d.%d, GNSS %d.%d\n", version.device, version.hardware, + version.fwMajor, version.fwMinor, version.fwMajorWiFi, version.fwMinorWiFi, version.fwGNSS, + version.almanacGNSS); + LOG_INFO("Frequency set to %f\n", getFreq()); LOG_INFO("Bandwidth set to %f\n", bw); LOG_INFO("Power output set to %d\n", power); @@ -114,6 +95,23 @@ template bool LR11x0Interface::init() // FIXME: May want to set depending on a definition, currently all LR1110 variant files use the DC-DC regulator option if (res == RADIOLIB_ERR_NONE) res = lora.setRegulatorDCDC(); + +#ifdef LR11X0_DIO_AS_RF_SWITCH + bool dioAsRfSwitch = true; +#elif defined(ARCH_PORTDUINO) + bool dioAsRfSwitch = false; + if (settingsMap[dio2_as_rf_switch]) { + dioAsRfSwitch = true; + } +#else + bool dioAsRfSwitch = false; +#endif + + if (dioAsRfSwitch) { + lora.setRfSwitchTable(rfswitch_dio_pins, rfswitch_table); + LOG_DEBUG("Setting DIO RF switch\n", res); + } + if (res == RADIOLIB_ERR_NONE) { if (config.lora.sx126x_rx_boosted_gain) { // the name is unfortunate but historically accurate res = lora.setRxBoostedGainMode(true); @@ -165,8 +163,10 @@ template bool LR11x0Interface::reconfigure() if (err != RADIOLIB_ERR_NONE) RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); - if (power > LR11X0_MAX_POWER) // This chip has lower power limits than some - power = LR11X0_MAX_POWER; + if (power > LR1110_MAX_POWER) // This chip has lower power limits than some + power = LR1110_MAX_POWER; + if ((power > LR1120_MAX_POWER) && (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) // 2.4G power limit + power = LR1120_MAX_POWER; err = lora.setOutputPower(power); assert(err == RADIOLIB_ERR_NONE); @@ -263,29 +263,8 @@ template bool LR11x0Interface::isActivelyReceiving() { // The IRQ status will be cleared when we start our read operation. Check if we've started a header, but haven't yet // received and handled the interrupt for reading the packet/handling errors. - - uint16_t irq = lora.getIrqStatus(); - bool detected = (irq & (RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID | RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED)); - // Handle false detections - if (detected) { - uint32_t now = millis(); - if (!activeReceiveStart) { - activeReceiveStart = now; - } else if ((now - activeReceiveStart > 2 * preambleTimeMsec) && !(irq & RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID)) { - // The HEADER_VALID flag should be set by now if it was really a packet, so ignore PREAMBLE_DETECTED flag - activeReceiveStart = 0; - LOG_DEBUG("Ignore false preamble detection.\n"); - return false; - } else if (now - activeReceiveStart > maxPacketTimeMsec) { - // We should have gotten an RX_DONE IRQ by now if it was really a packet, so ignore HEADER_VALID flag - activeReceiveStart = 0; - LOG_DEBUG("Ignore false header detection.\n"); - return false; - } - } - - // if (detected) LOG_DEBUG("rx detected\n"); - return detected; + return receiveDetected(lora.getIrqStatus(), RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID, + RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED); } template bool LR11x0Interface::sleep() @@ -306,4 +285,5 @@ template bool LR11x0Interface::sleep() #endif return true; -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/mesh/LR11x0Interface.h b/src/mesh/LR11x0Interface.h index 9272f43f0..4829ddc1d 100644 --- a/src/mesh/LR11x0Interface.h +++ b/src/mesh/LR11x0Interface.h @@ -1,5 +1,5 @@ #pragma once - +#if RADIOLIB_EXCLUDE_LR11X0 != 1 #include "RadioLibInterface.h" /** @@ -65,7 +65,5 @@ template class LR11x0Interface : public RadioLibInterface virtual void addReceiveMetadata(meshtastic_MeshPacket *mp) override; virtual void setStandby() override; - - private: - uint32_t activeReceiveStart = 0; }; +#endif \ No newline at end of file diff --git a/src/mesh/MeshModule.cpp b/src/mesh/MeshModule.cpp index 3b137d4bd..c60404c98 100644 --- a/src/mesh/MeshModule.cpp +++ b/src/mesh/MeshModule.cpp @@ -86,7 +86,7 @@ void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src) // Was this message directed to us specifically? Will be false if we are sniffing someone elses packets auto ourNodeNum = nodeDB->getNodeNum(); - bool toUs = mp.to == NODENUM_BROADCAST || mp.to == ourNodeNum; + bool toUs = mp.to == NODENUM_BROADCAST || isToUs(&mp); for (auto i = modules->begin(); i != modules->end(); ++i) { auto &pi = **i; @@ -141,8 +141,7 @@ void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src) // because currently when the phone sends things, it sends things using the local node ID as the from address. A // better solution (FIXME) would be to let phones have their own distinct addresses and we 'route' to them like // any other node. - if (isDecoded && mp.decoded.want_response && toUs && (getFrom(&mp) != ourNodeNum || mp.to == ourNodeNum) && - !currentReply) { + if (isDecoded && mp.decoded.want_response && toUs && (!isFromUs(&mp) || isToUs(&mp)) && !currentReply) { pi.sendResponse(mp); ignoreRequest = ignoreRequest || pi.ignoreRequest; // If at least one module asks it, we may ignore a request LOG_INFO("Asked module '%s' to send a response\n", pi.name); @@ -241,7 +240,7 @@ std::vector MeshModule::GetMeshModulesWithUIFrames() for (auto i = modules->begin(); i != modules->end(); ++i) { auto &pi = **i; if (pi.wantUIFrame()) { - LOG_DEBUG("Module wants a UI Frame\n"); + LOG_DEBUG("%s wants a UI Frame\n", pi.name); modulesWithUIFrames.push_back(&pi); } } @@ -256,7 +255,7 @@ void MeshModule::observeUIEvents(Observer *observer) auto &pi = **i; Observable *observable = pi.getUIFrameObservable(); if (observable != NULL) { - LOG_DEBUG("Module wants a UI Frame\n"); + LOG_DEBUG("%s wants a UI Frame\n", pi.name); observer->observe(observable); } } @@ -297,4 +296,4 @@ bool MeshModule::isRequestingFocus() } else return false; } -#endif \ No newline at end of file +#endif diff --git a/src/mesh/MeshModule.h b/src/mesh/MeshModule.h index c341b301a..7929ba972 100644 --- a/src/mesh/MeshModule.h +++ b/src/mesh/MeshModule.h @@ -79,7 +79,8 @@ class MeshModule meshtastic_AdminMessage *response); #if HAS_SCREEN virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { return; } - virtual bool isRequestingFocus(); // Checked by screen, when regenerating frameset + virtual bool isRequestingFocus(); // Checked by screen, when regenerating frameset + virtual bool interceptingKeyboardInput() { return false; } // Can screen use keyboard for nav, or is module handling input? #endif protected: const char *name; diff --git a/src/mesh/MeshPacketQueue.cpp b/src/mesh/MeshPacketQueue.cpp index 6581b1ce4..99ef41c1e 100644 --- a/src/mesh/MeshPacketQueue.cpp +++ b/src/mesh/MeshPacketQueue.cpp @@ -1,4 +1,5 @@ #include "MeshPacketQueue.h" +#include "NodeDB.h" #include "configuration.h" #include @@ -16,13 +17,9 @@ bool CompareMeshPacketFunc(const meshtastic_MeshPacket *p1, const meshtastic_Mes { assert(p1 && p2); auto p1p = getPriority(p1), p2p = getPriority(p2); - // If priorities differ, use that - // for equal priorities, order by id (older packets have higher priority - this will briefly be wrong when IDs roll over but - // no big deal) - return (p1p != p2p) - ? (p1p < p2p) // prefer bigger priorities - : ((p1->id & ID_COUNTER_MASK) >= (p2->id & ID_COUNTER_MASK)); // Mask to counter portion, prefer smaller packet ids + // for equal priorities, prefer packets already on mesh. + return (p1p != p2p) ? (p1p > p2p) : (!isFromUs(p1) && isFromUs(p2)); } MeshPacketQueue::MeshPacketQueue(size_t _maxLen) : maxLen(_maxLen) {} @@ -69,8 +66,9 @@ bool MeshPacketQueue::enqueue(meshtastic_MeshPacket *p) return replaceLowerPriorityPacket(p); } - queue.push_back(p); - std::push_heap(queue.begin(), queue.end(), &CompareMeshPacketFunc); + // Find the correct position using upper_bound to maintain a stable order + auto it = std::upper_bound(queue.begin(), queue.end(), p, CompareMeshPacketFunc); + queue.insert(it, p); // Insert packet at the found position return true; } @@ -81,9 +79,7 @@ meshtastic_MeshPacket *MeshPacketQueue::dequeue() } auto *p = queue.front(); - std::pop_heap(queue.begin(), queue.end(), &CompareMeshPacketFunc); - queue.pop_back(); - + queue.erase(queue.begin()); // Remove the highest-priority packet return p; } @@ -104,7 +100,6 @@ meshtastic_MeshPacket *MeshPacketQueue::remove(NodeNum from, PacketId id) auto p = (*it); if (getFrom(p) == from && p->id == id) { queue.erase(it); - std::make_heap(queue.begin(), queue.end(), &CompareMeshPacketFunc); return p; } } @@ -115,28 +110,21 @@ meshtastic_MeshPacket *MeshPacketQueue::remove(NodeNum from, PacketId id) /** Attempt to find and remove a packet from this queue. Returns the packet which was removed from the queue */ bool MeshPacketQueue::replaceLowerPriorityPacket(meshtastic_MeshPacket *p) { - std::sort_heap(queue.begin(), queue.end(), &CompareMeshPacketFunc); // sort ascending based on priority (0 -> 127) - // find first packet which does not compare less (in priority) than parameter packet - auto low = std::lower_bound(queue.begin(), queue.end(), p, &CompareMeshPacketFunc); - - if (low == queue.begin()) { // if already at start, there are no packets with lower priority - return false; + if (queue.empty()) { + return false; // No packets to replace + } + // Check if the packet at the back has a lower priority than the new packet + auto &backPacket = queue.back(); + if (backPacket->priority < p->priority) { + // Remove the back packet + packetPool.release(backPacket); + queue.pop_back(); + // Insert the new packet in the correct order + enqueue(p); + return true; } - if (low == queue.end()) { - // all priorities in the vector are smaller than the incoming packet. Replace the lowest priority (first) element - low = queue.begin(); - } else { - // 'low' iterator points to first packet which does not compare less than parameter - --low; // iterate to lower priority packet - } - - if (getPriority(p) > getPriority(*low)) { - packetPool.release(*low); // deallocate and drop the packet we're replacing - *low = p; // replace low-pri packet at this position with incoming packet with higher priority - } - - std::make_heap(queue.begin(), queue.end(), &CompareMeshPacketFunc); - return true; + // If the back packet's priority is not lower, no replacement occurs + return false; } \ No newline at end of file diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 2c502d899..7b0bc0c5b 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -80,12 +80,11 @@ int MeshService::handleFromRadio(const meshtastic_MeshPacket *mp) nodeDB->updateFrom(*mp); // update our DB state based off sniffing every RX packet from the radio if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag && mp->decoded.portnum == meshtastic_PortNum_TELEMETRY_APP && mp->decoded.request_id > 0) { - LOG_DEBUG( - "Received telemetry response. Skip sending our NodeInfo because this potentially a Repeater which will ignore our " - "request for its NodeInfo.\n"); + LOG_DEBUG("Received telemetry response. Skip sending our NodeInfo.\n"); // because this potentially a Repeater which will + // ignore our request for its NodeInfo } else if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag && !nodeDB->getMeshNode(mp->from)->has_user && nodeInfoModule) { - LOG_INFO("Heard a node on channel %d we don't know, sending NodeInfo and asking for a response.\n", mp->channel); + LOG_INFO("Heard new node on channel %d, sending NodeInfo and asking for a response.\n", mp->channel); if (airTime->isTxAllowedChannelUtil(true)) { nodeInfoModule->sendOurNodeInfo(mp->from, true, mp->channel); } else { @@ -223,7 +222,7 @@ ErrorCode MeshService::sendQueueStatusToPhone(const meshtastic_QueueStatus &qs, copied->mesh_packet_id = mesh_packet_id; if (toPhoneQueueStatusQueue.numFree() == 0) { - LOG_DEBUG("NOTE: tophone queue status queue is full, discarding oldest\n"); + LOG_INFO("tophone queue status queue is full, discarding oldest\n"); meshtastic_QueueStatus *d = toPhoneQueueStatusQueue.dequeuePtr(0); if (d) releaseQueueStatusToPool(d); @@ -317,7 +316,7 @@ void MeshService::sendToPhone(meshtastic_MeshPacket *p) void MeshService::sendMqttMessageToClientProxy(meshtastic_MqttClientProxyMessage *m) { - LOG_DEBUG("Sending mqtt message on topic '%s' to client for proxying to server\n", m->topic); + LOG_DEBUG("Sending mqtt message on topic '%s' to client for proxy\n", m->topic); if (toPhoneMqttProxyQueue.numFree() == 0) { LOG_WARN("MqttClientProxyMessagePool queue is full, discarding oldest\n"); meshtastic_MqttClientProxyMessage *d = toPhoneMqttProxyQueue.dequeuePtr(0); @@ -408,3 +407,15 @@ bool MeshService::isToPhoneQueueEmpty() { return toPhoneQueue.isEmpty(); } + +uint32_t MeshService::GetTimeSinceMeshPacket(const meshtastic_MeshPacket *mp) +{ + uint32_t now = getTime(); + + uint32_t last_seen = mp->rx_time; + int delta = (int)(now - last_seen); + if (delta < 0) // our clock must be slightly off still - not set from GPS yet + delta = 0; + + return delta; +} diff --git a/src/mesh/MeshService.h b/src/mesh/MeshService.h index ea1c4e345..1ccca4e6d 100644 --- a/src/mesh/MeshService.h +++ b/src/mesh/MeshService.h @@ -13,9 +13,9 @@ #if defined(ARCH_PORTDUINO) && !HAS_RADIO #include "../platform/portduino/SimRadio.h" #endif -#ifdef ARCH_ESP32 +#if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) #if !MESHTASTIC_EXCLUDE_STOREFORWARD -#include "modules/esp32/StoreForwardModule.h" +#include "modules/StoreForwardModule.h" #endif #endif @@ -92,6 +92,9 @@ class MeshService /// Return the next MqttClientProxyMessage packet destined to the phone. meshtastic_MqttClientProxyMessage *getMqttClientProxyMessageForPhone() { return toPhoneMqttProxyQueue.dequeuePtr(0); } + /// Return the next ClientNotification packet destined to the phone. + meshtastic_ClientNotification *getClientNotificationForPhone() { return toPhoneClientNotificationQueue.dequeuePtr(0); } + // search the queue for a request id and return the matching nodenum NodeNum getNodenumFromRequestId(uint32_t request_id); @@ -148,6 +151,8 @@ class MeshService ErrorCode sendQueueStatusToPhone(const meshtastic_QueueStatus &qs, ErrorCode res, uint32_t mesh_packet_id); + uint32_t GetTimeSinceMeshPacket(const meshtastic_MeshPacket *mp); + private: #if HAS_GPS /// Called when our gps position has changed - updates nodedb and sends Location message out into the mesh @@ -160,4 +165,4 @@ class MeshService friend class RoutingModule; }; -extern MeshService *service; \ No newline at end of file +extern MeshService *service; diff --git a/src/mesh/MeshTypes.h b/src/mesh/MeshTypes.h index 90cfd5897..27d100fbe 100644 --- a/src/mesh/MeshTypes.h +++ b/src/mesh/MeshTypes.h @@ -50,5 +50,11 @@ extern Allocator &packetPool; */ NodeNum getFrom(const meshtastic_MeshPacket *p); +// Returns true if the packet originated from the local node +bool isFromUs(const meshtastic_MeshPacket *p); + +// Returns true if the packet is destined to us +bool isToUs(const meshtastic_MeshPacket *p); + /* Some clients might not properly set priority, therefore we fix it here. */ void fixPriority(meshtastic_MeshPacket *p); \ No newline at end of file diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 71bb8b1fe..b7ab61c74 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -32,12 +32,13 @@ #if HAS_WIFI #include "mesh/wifi/WiFiAPClient.h" #endif -#include "modules/esp32/StoreForwardModule.h" +#include "modules/StoreForwardModule.h" #include #include #endif #ifdef ARCH_PORTDUINO +#include "modules/StoreForwardModule.h" #include "platform/portduino/PortduinoGlue.h" #endif @@ -49,7 +50,7 @@ NodeDB *nodeDB = nullptr; // we have plenty of ram so statically alloc this tempbuf (for now) -EXT_RAM_ATTR meshtastic_DeviceState devicestate; +EXT_RAM_BSS_ATTR meshtastic_DeviceState devicestate; meshtastic_MyNodeInfo &myNodeInfo = devicestate.my_node; meshtastic_LocalConfig config; meshtastic_LocalModuleConfig moduleConfig; @@ -121,6 +122,8 @@ NodeDB::NodeDB() owner.hw_model = HW_VENDOR; // Ensure user (nodeinfo) role is set to whatever we're configured to owner.role = config.device.role; + // Ensure macaddr is set to our macaddr as it will be copied in our info below + memcpy(owner.macaddr, ourMacAddr, sizeof(owner.macaddr)); // Include our owner in the node db under our nodenum meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getNodeNum()); @@ -129,39 +132,31 @@ NodeDB::NodeDB() config.security.serial_enabled = config.device.serial_enabled; config.security.is_managed = config.device.is_managed; } -#if !(MESHTASTIC_EXCLUDE_PKI) + +#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN || MESHTASTIC_EXCLUDE_PKI) + bool keygenSuccess = false; + if (config.security.private_key.size == 32) { + if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) { + keygenSuccess = true; + } + } else { + LOG_INFO("Generating new PKI keys\n"); + crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes); + keygenSuccess = true; + } + if (keygenSuccess) { + config.security.public_key.size = 32; + config.security.private_key.size = 32; + owner.public_key.size = 32; + memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32); + } +#elif !(MESHTASTIC_EXCLUDE_PKI) // Calculate Curve25519 public and private keys - printBytes("Old Pubkey", config.security.public_key.bytes, 32); if (config.security.private_key.size == 32 && config.security.public_key.size == 32) { - LOG_INFO("Using saved PKI keys\n"); owner.public_key.size = config.security.public_key.size; memcpy(owner.public_key.bytes, config.security.public_key.bytes, config.security.public_key.size); crypto->setDHPrivateKey(config.security.private_key.bytes); - } else { -#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) - bool keygenSuccess = false; - if (config.security.private_key.size == 32) { - LOG_INFO("Calculating PKI Public Key\n"); - if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) { - keygenSuccess = true; - } - } else { - LOG_INFO("Generating new PKI keys\n"); - crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes); - keygenSuccess = true; - } - if (keygenSuccess) { - config.security.public_key.size = 32; - config.security.private_key.size = 32; - printBytes("New Pubkey", config.security.public_key.bytes, 32); - owner.public_key.size = 32; - memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32); - } -#else - LOG_INFO("No PKI keys set, and generation disabled!\n"); -#endif } - #endif info->user = TypeConversions::ConvertToUserLite(owner); @@ -201,6 +196,18 @@ NodeNum getFrom(const meshtastic_MeshPacket *p) return (p->from == 0) ? nodeDB->getNodeNum() : p->from; } +// Returns true if the packet originated from the local node +bool isFromUs(const meshtastic_MeshPacket *p) +{ + return p->from == 0 || p->from == nodeDB->getNodeNum(); +} + +// Returns true if the packet is destined to us +bool isToUs(const meshtastic_MeshPacket *p) +{ + return p->to == nodeDB->getNodeNum(); +} + bool NodeDB::resetRadioConfig(bool factory_reset) { bool didFactoryReset = false; @@ -243,7 +250,7 @@ bool NodeDB::factoryReset(bool eraseBleBonds) #endif // second, install default state (this will deal with the duplicate mac address issue) installDefaultDeviceState(); - installDefaultConfig(); + installDefaultConfig(!eraseBleBonds); // Also preserve the private key if we're not erasing BLE bonds installDefaultModuleConfig(); installDefaultChannels(); // third, write everything to disk @@ -266,8 +273,13 @@ bool NodeDB::factoryReset(bool eraseBleBonds) return true; } -void NodeDB::installDefaultConfig() +void NodeDB::installDefaultConfig(bool preserveKey = false) { + uint8_t private_key_temp[32]; + bool shouldPreserveKey = preserveKey && config.has_security && config.security.private_key.size > 0; + if (shouldPreserveKey) { + memcpy(private_key_temp, config.security.private_key.bytes, config.security.private_key.size); + } LOG_INFO("Installing default LocalConfig\n"); memset(&config, 0, sizeof(meshtastic_LocalConfig)); config.version = DEVICESTATE_CUR_VER; @@ -285,30 +297,36 @@ void NodeDB::installDefaultConfig() config.lora.tx_enabled = true; // FIXME: maybe false in the future, and setting region to enable it. (unset region forces it off) config.lora.override_duty_cycle = false; -#ifdef CONFIG_LORA_REGION_USERPREFS - config.lora.region = CONFIG_LORA_REGION_USERPREFS; + config.lora.config_ok_to_mqtt = false; +#ifdef USERPREFS_CONFIG_LORA_REGION + config.lora.region = USERPREFS_CONFIG_LORA_REGION; #else config.lora.region = meshtastic_Config_LoRaConfig_RegionCode_UNSET; #endif -#ifdef LORACONFIG_MODEM_PRESET_USERPREFS - config.lora.modem_preset = LORACONFIG_MODEM_PRESET_USERPREFS; +#ifdef USERPREFS_LORACONFIG_MODEM_PRESET + config.lora.modem_preset = USERPREFS_LORACONFIG_MODEM_PRESET; #else config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST; #endif config.lora.hop_limit = HOP_RELIABLE; -#ifdef CONFIG_LORA_IGNORE_MQTT_USERPREFS - config.lora.ignore_mqtt = CONFIG_LORA_IGNORE_MQTT_USERPREFS; +#ifdef USERPREFS_CONFIG_LORA_IGNORE_MQTT + config.lora.ignore_mqtt = USERPREFS_CONFIG_LORA_IGNORE_MQTT; #else config.lora.ignore_mqtt = false; #endif -#ifdef ADMIN_KEY_USERPREFS - memcpy(config.security.admin_key[0].bytes, admin_key_userprefs, 32); +#ifdef USERPREFS_USE_ADMIN_KEY + memcpy(config.security.admin_key[0].bytes, USERPREFS_ADMIN_KEY, 32); config.security.admin_key[0].size = 32; -#else - config.security.admin_key[0].size = 0; + config.security.admin_key_count = 1; #endif + if (shouldPreserveKey) { + config.security.private_key.size = 32; + memcpy(config.security.private_key.bytes, private_key_temp, config.security.private_key.size); + printBytes("Restored key", config.security.private_key.bytes, config.security.private_key.size); + } else { + config.security.private_key.size = 0; + } config.security.public_key.size = 0; - config.security.private_key.size = 0; #ifdef PIN_GPS_EN config.position.gps_en_gpio = PIN_GPS_EN; #endif @@ -317,7 +335,9 @@ void NodeDB::installDefaultConfig() #else config.device.disable_triple_click = true; #endif -#if !HAS_GPS || defined(T_DECK) || defined(TLORA_T3S3_EPAPER) +#if defined(USERPREFS_CONFIG_GPS_MODE) + config.position.gps_mode = USERPREFS_CONFIG_GPS_MODE; +#elif !HAS_GPS || defined(T_DECK) || defined(TLORA_T3S3_EPAPER) config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT; #elif !defined(GPS_RX_PIN) if (config.position.rx_gpio == 0) @@ -339,8 +359,8 @@ void NodeDB::installDefaultConfig() // FIXME: Default to bluetooth capability of platform as default config.bluetooth.enabled = true; config.bluetooth.fixed_pin = defaultBLEPin; -#if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7789_CS) || defined(HX8357_CS) || \ - defined(USE_ST7789) +#if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7789_CS) || \ + defined(HX8357_CS) || defined(USE_ST7789) bool hasScreen = true; #elif ARCH_PORTDUINO bool hasScreen = false; @@ -362,6 +382,9 @@ void NodeDB::installDefaultConfig() #ifdef DISPLAY_FLIP_SCREEN config.display.flip_screen = true; #endif +#ifdef RAK4630 + config.display.wake_on_tap_or_motion = true; +#endif #ifdef T_WATCH_S3 config.display.screen_on_secs = 30; config.display.wake_on_tap_or_motion = true; @@ -455,7 +478,7 @@ void NodeDB::installDefaultModuleConfig() moduleConfig.has_detection_sensor = true; moduleConfig.detection_sensor.enabled = false; - moduleConfig.detection_sensor.detection_triggered_high = true; + moduleConfig.detection_sensor.detection_trigger_type = meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_LOGIC_HIGH; moduleConfig.detection_sensor.minimum_broadcast_secs = 45; moduleConfig.has_ambient_lighting = true; @@ -510,6 +533,7 @@ void NodeDB::installRoleDefaults(meshtastic_Config_DeviceConfig_Role role) moduleConfig.telemetry.device_update_interval = UINT32_MAX; moduleConfig.telemetry.environment_update_interval = UINT32_MAX; moduleConfig.telemetry.air_quality_interval = UINT32_MAX; + moduleConfig.telemetry.health_update_interval = UINT32_MAX; } } @@ -520,6 +544,7 @@ void NodeDB::initModuleConfigIntervals() moduleConfig.telemetry.environment_update_interval = 0; moduleConfig.telemetry.air_quality_interval = 0; moduleConfig.telemetry.power_update_interval = 0; + moduleConfig.telemetry.health_update_interval = 0; moduleConfig.neighbor_info.update_interval = 0; moduleConfig.paxcounter.paxcounter_update_interval = 0; } @@ -602,18 +627,20 @@ void NodeDB::installDefaultDeviceState() // devicestate.node_db_lite_count = 0; devicestate.version = DEVICESTATE_CUR_VER; devicestate.receive_queue_count = 0; // Not yet implemented FIXME + devicestate.has_rx_waypoint = false; + devicestate.has_rx_text_message = false; generatePacketId(); // FIXME - ugly way to init current_packet_id; // Set default owner name pickNewNodeNum(); // based on macaddr now -#ifdef CONFIG_OWNER_LONG_NAME_USERPREFS - snprintf(owner.long_name, sizeof(owner.long_name), CONFIG_OWNER_LONG_NAME_USERPREFS); +#ifdef USERPREFS_CONFIG_OWNER_LONG_NAME + snprintf(owner.long_name, sizeof(owner.long_name), USERPREFS_CONFIG_OWNER_LONG_NAME); #else snprintf(owner.long_name, sizeof(owner.long_name), "Meshtastic %02x%02x", ourMacAddr[4], ourMacAddr[5]); #endif -#ifdef CONFIG_OWNER_SHORT_NAME_USERPREFS - snprintf(owner.short_name, sizeof(owner.short_name), CONFIG_OWNER_SHORT_NAME_USERPREFS); +#ifdef USERPREFS_CONFIG_OWNER_SHORT_NAME + snprintf(owner.short_name, sizeof(owner.short_name), USERPREFS_CONFIG_OWNER_SHORT_NAME); #else snprintf(owner.short_name, sizeof(owner.short_name), "%02x%02x", ourMacAddr[4], ourMacAddr[5]); #endif @@ -637,10 +664,13 @@ void NodeDB::pickNewNodeNum() } meshtastic_NodeInfoLite *found; - while ((nodeNum == NODENUM_BROADCAST || nodeNum < NUM_RESERVED) || - ((found = getMeshNode(nodeNum)) && memcmp(found->user.macaddr, ourMacAddr, sizeof(ourMacAddr)) != 0)) { + while (((found = getMeshNode(nodeNum)) && memcmp(found->user.macaddr, ourMacAddr, sizeof(ourMacAddr)) != 0) || + (nodeNum == NODENUM_BROADCAST || nodeNum < NUM_RESERVED)) { NodeNum candidate = random(NUM_RESERVED, LONG_MAX); // try a new random choice - LOG_WARN("NOTE! Our desired nodenum 0x%x is invalid or in use, so trying for 0x%x\n", nodeNum, candidate); + if (found) + LOG_WARN("NOTE! Our desired nodenum 0x%x is invalid or in use, by MAC ending in 0x%02x%02x vs our 0x%02x%02x, so " + "trying for 0x%x\n", + nodeNum, found->user.macaddr[4], found->user.macaddr[5], ourMacAddr[4], ourMacAddr[5], candidate); nodeNum = candidate; } LOG_DEBUG("Using nodenum 0x%x \n", nodeNum); @@ -706,7 +736,7 @@ void NodeDB::loadFromDisk() //} else { if (devicestate.version < DEVICESTATE_MIN_VER) { LOG_WARN("Devicestate %d is old, discarding\n", devicestate.version); - factoryReset(); + installDefaultDeviceState(); } else { LOG_INFO("Loaded saved devicestate version %d, with nodecount: %d\n", devicestate.version, devicestate.node_db_lite.size()); @@ -722,7 +752,7 @@ void NodeDB::loadFromDisk() } else { if (config.version < DEVICESTATE_MIN_VER) { LOG_WARN("config %d is old, discarding\n", config.version); - installDefaultConfig(); + installDefaultConfig(true); } else { LOG_INFO("Loaded saved config version %d\n", config.version); } @@ -1035,7 +1065,7 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde if (p.public_key.size > 0) { printBytes("Incoming Pubkey: ", p.public_key.bytes, 32); if (info->user.public_key.size > 0) { // if we have a key for this user already, don't overwrite with a new one - LOG_INFO("Public Key set for node, not updateing!\n"); + LOG_INFO("Public Key set for node, not updating!\n"); // we copy the key into the incoming packet, to prevent overwrite memcpy(p.public_key.bytes, info->user.public_key.bytes, 32); } else { @@ -1066,7 +1096,7 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde // We just changed something about the user, store our DB Throttle::execute( &lastNodeDbSave, ONE_MINUTE_MS, []() { nodeDB->saveToDisk(SEGMENT_DEVICESTATE); }, - []() { LOG_DEBUG("Deferring NodeDB saveToDisk for now, since we saved less than a minute ago\n"); }); + []() { LOG_DEBUG("Deferring NodeDB saveToDisk for now\n"); }); // since we saved less than a minute ago } return changed; @@ -1093,8 +1123,10 @@ void NodeDB::updateFrom(const meshtastic_MeshPacket &mp) info->via_mqtt = mp.via_mqtt; // Store if we received this packet via MQTT // If hopStart was set and there wasn't someone messing with the limit in the middle, add hopsAway - if (mp.hop_start != 0 && mp.hop_limit <= mp.hop_start) + if (mp.hop_start != 0 && mp.hop_limit <= mp.hop_start) { + info->has_hops_away = true; info->hops_away = mp.hop_start - mp.hop_limit; + } } } @@ -1190,4 +1222,4 @@ void recordCriticalError(meshtastic_CriticalErrorCode code, uint32_t address, co LOG_ERROR("A critical failure occurred, portduino is exiting..."); exit(2); #endif -} \ No newline at end of file +} diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index a71f3e134..1be61759a 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -2,11 +2,13 @@ #include "Observer.h" #include +#include #include #include #include "MeshTypes.h" #include "NodeStatus.h" +#include "configuration.h" #include "mesh-pb-constants.h" #include "mesh/generated/meshtastic/mesh.pb.h" // For CriticalErrorCode @@ -182,7 +184,8 @@ class NodeDB void cleanupMeshDB(); /// Reinit device state from scratch (not loading from disk) - void installDefaultDeviceState(), installDefaultChannels(), installDefaultConfig(), installDefaultModuleConfig(); + void installDefaultDeviceState(), installDefaultChannels(), installDefaultConfig(bool preserveKey), + installDefaultModuleConfig(); /// write to flash /// @return true if the save was successful diff --git a/src/mesh/PacketHistory.cpp b/src/mesh/PacketHistory.cpp index 26a73a3fe..ed1c3c59c 100644 --- a/src/mesh/PacketHistory.cpp +++ b/src/mesh/PacketHistory.cpp @@ -5,6 +5,7 @@ #ifdef ARCH_PORTDUINO #include "platform/portduino/PortduinoGlue.h" #endif +#include "Throttle.h" PacketHistory::PacketHistory() { @@ -22,18 +23,17 @@ bool PacketHistory::wasSeenRecently(const meshtastic_MeshPacket *p, bool withUpd return false; // Not a floodable message ID, so we don't care } - uint32_t now = millis(); - PacketRecord r; r.id = p->id; r.sender = getFrom(p); - r.rxTimeMsec = now; + r.rxTimeMsec = millis(); auto found = recentPackets.find(r); bool seenRecently = (found != recentPackets.end()); // found not equal to .end() means packet was seen recently - if (seenRecently && (now - found->rxTimeMsec) >= FLOOD_EXPIRE_TIME) { // Check whether found packet has already expired - recentPackets.erase(found); // Erase and pretend packet has not been seen recently + if (seenRecently && + !Throttle::isWithinTimespanMs(found->rxTimeMsec, FLOOD_EXPIRE_TIME)) { // Check whether found packet has already expired + recentPackets.erase(found); // Erase and pretend packet has not been seen recently found = recentPackets.end(); seenRecently = false; } @@ -64,12 +64,10 @@ bool PacketHistory::wasSeenRecently(const meshtastic_MeshPacket *p, bool withUpd */ void PacketHistory::clearExpiredRecentPackets() { - uint32_t now = millis(); - LOG_DEBUG("recentPackets size=%ld\n", recentPackets.size()); for (auto it = recentPackets.begin(); it != recentPackets.end();) { - if ((now - it->rxTimeMsec) >= FLOOD_EXPIRE_TIME) { + if (!Throttle::isWithinTimespanMs(it->rxTimeMsec, FLOOD_EXPIRE_TIME)) { it = recentPackets.erase(it); // erase returns iterator pointing to element immediately following the one erased } else { ++it; diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index 30af9d7b0..ad4a1f33d 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -8,6 +8,7 @@ #include "FSCommon.h" #include "MeshService.h" #include "NodeDB.h" +#include "PacketHistory.h" #include "PhoneAPI.h" #include "PowerFSM.h" #include "RadioInterface.h" @@ -25,10 +26,13 @@ #if !MESHTASTIC_EXCLUDE_MQTT #include "mqtt/MQTT.h" #endif +#include "Throttle.h" +#include PhoneAPI::PhoneAPI() { lastContactMsec = millis(); + std::fill(std::begin(recentToRadioPacketIds), std::end(recentToRadioPacketIds), 0); } PhoneAPI::~PhoneAPI() @@ -60,9 +64,11 @@ void PhoneAPI::handleStartConfig() void PhoneAPI::close() { + LOG_INFO("PhoneAPI::close()\n"); + if (state != STATE_SEND_NOTHING) { state = STATE_SEND_NOTHING; - + resetReadIndex(); unobserve(&service->fromNumChanged); #ifdef FSCom unobserve(&xModem.packetReady); @@ -70,8 +76,17 @@ void PhoneAPI::close() releasePhonePacket(); // Don't leak phone packets on shutdown releaseQueueStatusPhonePacket(); releaseMqttClientProxyPhonePacket(); - + releaseClientNotification(); onConnectionChanged(false); + fromRadioScratch = {}; + toRadioScratch = {}; + nodeInfoForPhone = {}; + packetForPhone = NULL; + filesManifest.clear(); + fromRadioNum = 0; + config_nonce = 0; + config_state = 0; + pauseBluetoothLogging = false; } } @@ -96,8 +111,6 @@ bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength) powerFSM.trigger(EVENT_CONTACT_FROM_PHONE); // As long as the phone keeps talking to us, don't let the radio go to sleep lastContactMsec = millis(); - // return (lastContactMsec != 0) && - memset(&toRadioScratch, 0, sizeof(toRadioScratch)); if (pb_decode_from_bytes(buf, bufLength, &meshtastic_ToRadio_msg, &toRadioScratch)) { switch (toRadioScratch.which_payload_variant) { @@ -194,7 +207,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) auto us = nodeDB->readNextMeshNode(readIndex); if (us) { nodeInfoForPhone = TypeConversions::ConvertToNodeInfo(us); - nodeInfoForPhone.hops_away = 0; + nodeInfoForPhone.has_hops_away = false; nodeInfoForPhone.is_favorite = true; fromRadioScratch.which_payload_variant = meshtastic_FromRadio_node_info_tag; fromRadioScratch.node_info = nodeInfoForPhone; @@ -403,6 +416,10 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) fromRadioScratch.which_payload_variant = meshtastic_FromRadio_xmodemPacket_tag; fromRadioScratch.xmodemPacket = xmodemPacketForPhone; xmodemPacketForPhone = meshtastic_XModem_init_zero; + } else if (clientNotification) { + fromRadioScratch.which_payload_variant = meshtastic_FromRadio_clientNotification_tag; + fromRadioScratch.clientNotification = *clientNotification; + releaseClientNotification(); } else if (packetForPhone) { printPacket("phone downloaded packet", packetForPhone); @@ -442,13 +459,6 @@ void PhoneAPI::sendConfigComplete() pauseBluetoothLogging = false; } -void PhoneAPI::handleDisconnect() -{ - filesManifest.clear(); - pauseBluetoothLogging = false; - LOG_INFO("PhoneAPI disconnect\n"); -} - void PhoneAPI::releasePhonePacket() { if (packetForPhone) { @@ -473,6 +483,14 @@ void PhoneAPI::releaseMqttClientProxyPhonePacket() } } +void PhoneAPI::releaseClientNotification() +{ + if (clientNotification) { + service->releaseClientNotificationToPool(clientNotification); + clientNotification = NULL; + } +} + /** * Return true if we have data available to send to the phone */ @@ -507,7 +525,9 @@ bool PhoneAPI::available() queueStatusPacketForPhone = service->getQueueStatusForPhone(); if (!mqttClientProxyMessageForPhone) mqttClientProxyMessageForPhone = service->getMqttClientProxyMessageForPhone(); - bool hasPacket = !!queueStatusPacketForPhone || !!mqttClientProxyMessageForPhone; + if (!clientNotification) + clientNotification = service->getClientNotificationForPhone(); + bool hasPacket = !!queueStatusPacketForPhone || !!mqttClientProxyMessageForPhone || !!clientNotification; if (hasPacket) return true; @@ -541,14 +561,64 @@ bool PhoneAPI::available() return false; } +void PhoneAPI::sendNotification(meshtastic_LogRecord_Level level, uint32_t replyId, const char *message) +{ + meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed(); + cn->has_reply_id = true; + cn->reply_id = replyId; + cn->level = meshtastic_LogRecord_Level_WARNING; + cn->time = getValidTime(RTCQualityFromNet); + strncpy(cn->message, message, sizeof(cn->message)); + service->sendClientNotification(cn); +} + +bool PhoneAPI::wasSeenRecently(uint32_t id) +{ + for (int i = 0; i < 20; i++) { + if (recentToRadioPacketIds[i] == id) { + return true; + } + if (recentToRadioPacketIds[i] == 0) { + recentToRadioPacketIds[i] = id; + return false; + } + } + // If the array is full, shift all elements to the left and add the new id at the end + memmove(recentToRadioPacketIds, recentToRadioPacketIds + 1, (19) * sizeof(uint32_t)); + recentToRadioPacketIds[19] = id; + return false; +} + /** * Handle a packet that the phone wants us to send. It is our responsibility to free the packet to the pool */ bool PhoneAPI::handleToRadioPacket(meshtastic_MeshPacket &p) { printPacket("PACKET FROM PHONE", &p); - service->handleToRadio(p); + if (p.id > 0 && wasSeenRecently(p.id)) { + LOG_DEBUG("Ignoring packet from phone, already seen recently\n"); + return false; + } + + if (p.decoded.portnum == meshtastic_PortNum_TRACEROUTE_APP && lastPortNumToRadio[p.decoded.portnum] && + Throttle::isWithinTimespanMs(lastPortNumToRadio[p.decoded.portnum], THIRTY_SECONDS_MS)) { + LOG_WARN("Rate limiting portnum %d\n", p.decoded.portnum); + sendNotification(meshtastic_LogRecord_Level_WARNING, p.id, "TraceRoute can only be sent once every 30 seconds"); + meshtastic_QueueStatus qs = router->getQueueStatus(); + service->sendQueueStatusToPhone(qs, 0, p.id); + return false; + } else if (p.decoded.portnum == meshtastic_PortNum_POSITION_APP && lastPortNumToRadio[p.decoded.portnum] && + Throttle::isWithinTimespanMs(lastPortNumToRadio[p.decoded.portnum], FIVE_SECONDS_MS)) { + LOG_WARN("Rate limiting portnum %d\n", p.decoded.portnum); + meshtastic_QueueStatus qs = router->getQueueStatus(); + service->sendQueueStatusToPhone(qs, 0, p.id); + // FIXME: Figure out why this continues to happen + // sendNotification(meshtastic_LogRecord_Level_WARNING, p.id, "Position can only be sent once every 5 seconds"); + return false; + } + lastPortNumToRadio[p.decoded.portnum] = millis(); + service->handleToRadio(p); return true; } diff --git a/src/mesh/PhoneAPI.h b/src/mesh/PhoneAPI.h index 5feb1c4bf..3247fee5c 100644 --- a/src/mesh/PhoneAPI.h +++ b/src/mesh/PhoneAPI.h @@ -2,8 +2,10 @@ #include "Observer.h" #include "mesh-pb-constants.h" +#include "meshtastic/portnums.pb.h" #include #include +#include #include // Make sure that we never let our packets grow too large for one BLE packet @@ -48,6 +50,10 @@ class PhoneAPI uint8_t config_state = 0; + // Hashmap of timestamps for last time we received a packet on the API per portnum + std::unordered_map lastPortNumToRadio; + uint32_t recentToRadioPacketIds[20]; // Last 20 ToRadio MeshPacket IDs we have seen + /** * Each packet sent to the phone has an incrementing count */ @@ -99,6 +105,11 @@ class PhoneAPI */ virtual bool handleToRadio(const uint8_t *buf, size_t len); + /** + * Send a (client)notification to the phone + */ + virtual void sendNotification(meshtastic_LogRecord_Level level, uint32_t replyId, const char *message); + /** * Get the next packet we want to send to the phone * @@ -137,11 +148,6 @@ class PhoneAPI */ virtual void onNowHasData(uint32_t fromRadioNum) {} - /** - * Subclasses can use this to find out when a client drops the link - */ - virtual void handleDisconnect(); - private: void releasePhonePacket(); @@ -149,9 +155,13 @@ class PhoneAPI void releaseMqttClientProxyPhonePacket(); + void releaseClientNotification(); + /// begin a new connection void handleStartConfig(); + bool wasSeenRecently(uint32_t packetId); + /** * Handle a packet that the phone wants us to send. We can write to it but can not keep a reference to it * @return true true if a packet was queued for sending diff --git a/src/mesh/ProtobufModule.h b/src/mesh/ProtobufModule.h index 3b438ebeb..e4d4e5c09 100644 --- a/src/mesh/ProtobufModule.h +++ b/src/mesh/ProtobufModule.h @@ -108,7 +108,7 @@ template class ProtobufModule : protected SinglePortModule T *decoded = NULL; if (mp.which_payload_variant == meshtastic_MeshPacket_decoded_tag && mp.decoded.portnum == ourPortNum) { memset(&scratch, 0, sizeof(scratch)); - auto &p = mp.decoded; + const meshtastic_Data &p = mp.decoded; if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, fields, &scratch)) { decoded = &scratch; } else { diff --git a/src/mesh/RF95Interface.cpp b/src/mesh/RF95Interface.cpp index 25df3258f..581fd9034 100644 --- a/src/mesh/RF95Interface.cpp +++ b/src/mesh/RF95Interface.cpp @@ -1,3 +1,4 @@ +#if RADIOLIB_EXCLUDE_SX127X != 1 #include "RF95Interface.h" #include "MeshRadio.h" // kinda yucky, but we need to know which region we are in #include "RadioLibRF95.h" @@ -219,17 +220,17 @@ bool RF95Interface::reconfigure() err = lora->setSyncWord(syncWord); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR("Radiolib error %d when attempting RF95 setSyncWord!\n", err); + LOG_ERROR("RF95 setSyncWord %s%d\n", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); err = lora->setCurrentLimit(currentLimit); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR("Radiolib error %d when attempting RF95 setCurrentLimit!\n", err); + LOG_ERROR("RF95 setCurrentLimit %s%d\n", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); err = lora->setPreambleLength(preambleLength); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR("Radiolib error %d when attempting RF95 setPreambleLength!\n", err); + LOG_ERROR(" RF95 setPreambleLength %s%d\n", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); err = lora->setFrequency(getFreq()); @@ -265,7 +266,7 @@ void RF95Interface::setStandby() { int err = lora->standby(); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR("Radiolib error %d when attempting RF95 standby!\n", err); + LOG_ERROR("RF95 standby %s%d\n", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); isReceiving = false; // If we were receiving, not any more @@ -289,7 +290,7 @@ void RF95Interface::startReceive() setStandby(); int err = lora->startReceive(); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR("Radiolib error %d when attempting RF95 startReceive!\n", err); + LOG_ERROR("RF95 startReceive %s%d\n", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); isReceiving = true; @@ -311,7 +312,7 @@ bool RF95Interface::isChannelActive() return true; } if (result != RADIOLIB_CHANNEL_FREE) - LOG_ERROR("Radiolib error %d when attempting RF95 isChannelActive!\n", result); + LOG_ERROR("RF95 isChannelActive %s%d\n", radioLibErr, result); assert(result != RADIOLIB_ERR_WRONG_MODEM); // LOG_DEBUG("Channel is free!\n"); @@ -336,3 +337,4 @@ bool RF95Interface::sleep() return true; } +#endif \ No newline at end of file diff --git a/src/mesh/RF95Interface.h b/src/mesh/RF95Interface.h index a50cf93a2..327e57900 100644 --- a/src/mesh/RF95Interface.h +++ b/src/mesh/RF95Interface.h @@ -1,5 +1,5 @@ #pragma once - +#if RADIOLIB_EXCLUDE_SX127X != 1 #include "MeshRadio.h" // kinda yucky, but we need to know which region we are in #include "RadioLibInterface.h" #include "RadioLibRF95.h" @@ -69,3 +69,4 @@ class RF95Interface : public RadioLibInterface /** Some boards require GPIO control of tx vs rx paths */ void setTransmitEnable(bool txon); }; +#endif \ No newline at end of file diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index eacd49644..7501852f2 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -136,6 +136,17 @@ const RegionInfo regions[] = { */ RDEF(SG_923, 917.0f, 925.0f, 100, 0, 20, true, false, false), + /* + Philippines + 433 - 434.7 MHz <10 mW erp, NTC approved device required + 868 - 869.4 MHz <25 mW erp, NTC approved device required + 915 - 918 MHz <250 mW EIRP, no external antennna allowed + https://github.com/meshtastic/firmware/issues/4948#issuecomment-2394926135 + */ + + RDEF(PH_433, 433.0f, 434.7f, 100, 0, 10, true, false, false), RDEF(PH_868, 868.0f, 869.4f, 100, 0, 14, true, false, false), + RDEF(PH_915, 915.0f, 918.0f, 100, 0, 24, true, false, false), + /* 2.4 GHZ WLAN Band equivalent. Only for SX128x chips. */ @@ -151,7 +162,7 @@ const RegionInfo regions[] = { const RegionInfo *myRegion; bool RadioInterface::uses_default_frequency_slot = true; -static uint8_t bytes[MAX_RHPACKETLEN]; +static uint8_t bytes[MAX_LORA_PAYLOAD_LEN + 1]; void initRegion() { @@ -202,8 +213,6 @@ uint32_t RadioInterface::getPacketTime(uint32_t pl) uint32_t msecs = tPacket * 1000; - LOG_DEBUG("(bw=%d, sf=%d, cr=4/%d) packet symLen=%d ms, payloadSize=%u, time %d ms\n", (int)bw, sf, cr, (int)(tSym * 1000), - pl, msecs); return msecs; } @@ -326,7 +335,7 @@ void printPacket(const char *prefix, const meshtastic_MeshPacket *p) RadioInterface::RadioInterface() { - assert(sizeof(PacketHeader) == 16); // make sure the compiler did what we expected + assert(sizeof(PacketHeader) == MESHTASTIC_HEADER_LENGTH); // make sure the compiler did what we expected } bool RadioInterface::reconfigure() @@ -420,7 +429,7 @@ void RadioInterface::applyModemConfig() switch (loraConfig.modem_preset) { case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO: - bw = (myRegion->wideLora) ? 812.5 : 500; + bw = (myRegion->wideLora) ? 1625.0 : 500; cr = 5; sf = 7; break; @@ -550,11 +559,11 @@ void RadioInterface::applyModemConfig() LOG_INFO("Radio freq=%.3f, config.lora.frequency_offset=%.3f\n", freq, loraConfig.frequency_offset); LOG_INFO("Set radio: region=%s, name=%s, config=%u, ch=%d, power=%d\n", myRegion->name, channelName, loraConfig.modem_preset, channel_num, power); - LOG_INFO("Radio myRegion->freqStart -> myRegion->freqEnd: %f -> %f (%f MHz)\n", myRegion->freqStart, myRegion->freqEnd, + LOG_INFO("myRegion->freqStart -> myRegion->freqEnd: %f -> %f (%f MHz)\n", myRegion->freqStart, myRegion->freqEnd, myRegion->freqEnd - myRegion->freqStart); - LOG_INFO("Radio myRegion->numChannels: %d x %.3fkHz\n", numChannels, bw); - LOG_INFO("Radio channel_num: %d\n", channel_num + 1); - LOG_INFO("Radio frequency: %f\n", getFreq()); + LOG_INFO("numChannels: %d x %.3fkHz\n", numChannels, bw); + LOG_INFO("channel_num: %d\n", channel_num + 1); + LOG_INFO("frequency: %f\n", getFreq()); LOG_INFO("Slot time: %u msec\n", slotTimeMsec); } @@ -595,25 +604,24 @@ size_t RadioInterface::beginSending(meshtastic_MeshPacket *p) lastTxStart = millis(); - PacketHeader *h = (PacketHeader *)radiobuf; - - h->from = p->from; - h->to = p->to; - h->id = p->id; - h->channel = p->channel; - h->next_hop = 0; // *** For future use *** - h->relay_node = 0; // *** For future use *** + radioBuffer.header.from = p->from; + radioBuffer.header.to = p->to; + radioBuffer.header.id = p->id; + radioBuffer.header.channel = p->channel; + radioBuffer.header.next_hop = 0; // *** For future use *** + radioBuffer.header.relay_node = 0; // *** For future use *** if (p->hop_limit > HOP_MAX) { LOG_WARN("hop limit %d is too high, setting to %d\n", p->hop_limit, HOP_RELIABLE); p->hop_limit = HOP_RELIABLE; } - h->flags = p->hop_limit | (p->want_ack ? PACKET_FLAGS_WANT_ACK_MASK : 0) | (p->via_mqtt ? PACKET_FLAGS_VIA_MQTT_MASK : 0); - h->flags |= (p->hop_start << PACKET_FLAGS_HOP_START_SHIFT) & PACKET_FLAGS_HOP_START_MASK; + radioBuffer.header.flags = + p->hop_limit | (p->want_ack ? PACKET_FLAGS_WANT_ACK_MASK : 0) | (p->via_mqtt ? PACKET_FLAGS_VIA_MQTT_MASK : 0); + radioBuffer.header.flags |= (p->hop_start << PACKET_FLAGS_HOP_START_SHIFT) & PACKET_FLAGS_HOP_START_MASK; // if the sender nodenum is zero, that means uninitialized - assert(h->from); + assert(radioBuffer.header.from); - memcpy(radiobuf + sizeof(PacketHeader), p->encrypted.bytes, p->encrypted.size); + memcpy(radioBuffer.payload, p->encrypted.bytes, p->encrypted.size); sendingPacket = p; return p->encrypted.size + sizeof(PacketHeader); diff --git a/src/mesh/RadioInterface.h b/src/mesh/RadioInterface.h index f1016e3d8..89a4c7087 100644 --- a/src/mesh/RadioInterface.h +++ b/src/mesh/RadioInterface.h @@ -9,7 +9,9 @@ #define MAX_TX_QUEUE 16 // max number of packets which can be waiting for transmission -#define MAX_RHPACKETLEN 256 +#define MAX_LORA_PAYLOAD_LEN 255 // max length of 255 per Semtech's datasheets on SX12xx +#define MESHTASTIC_HEADER_LENGTH 16 +#define MESHTASTIC_PKC_OVERHEAD 12 #define PACKET_FLAGS_HOP_LIMIT_MASK 0x07 #define PACKET_FLAGS_WANT_ACK_MASK 0x08 @@ -43,6 +45,20 @@ typedef struct { uint8_t relay_node; } PacketHeader; +/** + * This structure represent the structured buffer : a PacketHeader then the payload. The whole is + * MAX_LORA_PAYLOAD_LEN + 1 length + * It makes the use of its data easier, and avoids manipulating pointers (and potential non aligned accesses) + */ +typedef struct { + /** The header, as defined just before */ + PacketHeader header; + + /** The payload, of maximum length minus the header, aligned just to be sure */ + uint8_t payload[MAX_LORA_PAYLOAD_LEN + 1 - sizeof(PacketHeader)] __attribute__((__aligned__)); + +} RadioBuffer; + /** * Basic operations all radio chipsets must implement. * @@ -89,8 +105,7 @@ class RadioInterface /** * A temporary buffer used for sending/receiving packets, sized to hold the biggest buffer we might need * */ - uint8_t radiobuf[MAX_RHPACKETLEN]; - + RadioBuffer radioBuffer __attribute__((__aligned__)); /** * Enqueue a received packet for the registered receiver */ diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp index f299ebff2..818826018 100644 --- a/src/mesh/RadioLibInterface.cpp +++ b/src/mesh/RadioLibInterface.cpp @@ -3,6 +3,7 @@ #include "NodeDB.h" #include "PowerMon.h" #include "SPILock.h" +#include "Throttle.h" #include "configuration.h" #include "error.h" #include "main.h" @@ -41,7 +42,7 @@ void LockingArduinoHal::spiTransfer(uint8_t *out, size_t len, uint8_t *in) uint32_t start = millis(); while (digitalRead(busy)) { - if (millis() - start >= 2000) { + if (!Throttle::isWithinTimespanMs(start, 2000)) { LOG_ERROR("GPIO mid-transfer timeout, is it connected?"); return; } @@ -114,7 +115,7 @@ bool RadioLibInterface::canSendImmediately() } // If we've been trying to send the same packet more than one minute and we haven't gotten a // TX IRQ from the radio, the radio is probably broken. - if (busyTx && (millis() - lastTxStart > 60000)) { + if (busyTx && !Throttle::isWithinTimespanMs(lastTxStart, 60000)) { LOG_ERROR("Hardware Failure! busyTx for more than 60s\n"); RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_TRANSMIT_FAILED); // reboot in 5 seconds when this condition occurs. @@ -128,6 +129,28 @@ bool RadioLibInterface::canSendImmediately() return true; } +bool RadioLibInterface::receiveDetected(uint16_t irq, ulong syncWordHeaderValidFlag, ulong preambleDetectedFlag) +{ + bool detected = (irq & (syncWordHeaderValidFlag | preambleDetectedFlag)); + // Handle false detections + if (detected) { + if (!activeReceiveStart) { + activeReceiveStart = millis(); + } else if (!Throttle::isWithinTimespanMs(activeReceiveStart, 2 * preambleTimeMsec) && !(irq & syncWordHeaderValidFlag)) { + // The HEADER_VALID flag should be set by now if it was really a packet, so ignore PREAMBLE_DETECTED flag + activeReceiveStart = 0; + LOG_DEBUG("Ignore false preamble detection.\n"); + return false; + } else if (!Throttle::isWithinTimespanMs(activeReceiveStart, maxPacketTimeMsec)) { + // We should have gotten an RX_DONE IRQ by now if it was really a packet, so ignore HEADER_VALID flag + activeReceiveStart = 0; + LOG_DEBUG("Ignore false header detection.\n"); + return false; + } + } + return detected; +} + /// Send a packet (possibly by enquing in a private fifo). This routine will /// later free() the packet to pool. This routine is not allowed to stall because it is called from /// bluetooth comms code. If the txmit queue is empty it might return an error @@ -144,7 +167,7 @@ ErrorCode RadioLibInterface::send(meshtastic_MeshPacket *p) } } else { - LOG_WARN("send - lora tx disable because RegionCode_Unset\n"); + LOG_WARN("send - lora tx disabled because RegionCode_Unset\n"); packetPool.release(p); return ERRNO_DISABLED; } @@ -163,7 +186,7 @@ ErrorCode RadioLibInterface::send(meshtastic_MeshPacket *p) #ifndef LORA_DISABLE_SENDING printPacket("enqueuing for send", p); - LOG_DEBUG("txGood=%d,rxGood=%d,rxBad=%d\n", txGood, rxGood, rxBad); + LOG_DEBUG("txGood=%d,txRelay=%d,rxGood=%d,rxBad=%d\n", txGood, txRelay, rxGood, rxBad); ErrorCode res = txQueue.enqueue(p) ? ERRNO_OK : ERRNO_UNKNOWN; if (res != ERRNO_OK) { // we weren't able to queue it, so we must drop it to prevent leaks @@ -330,6 +353,8 @@ void RadioLibInterface::completeSending() if (p) { txGood++; + if (!isFromUs(p)) + txRelay++; printPacket("Completed sending", p); // We are done sending that packet, release it @@ -356,7 +381,15 @@ void RadioLibInterface::handleReceiveInterrupt() xmitMsec = getPacketTime(length); - int state = iface->readData(radiobuf, length); +#ifndef DISABLE_WELCOME_UNSET + if (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET) { + LOG_WARN("recv - lora rx disabled because RegionCode_Unset\n"); + airTime->logAirtime(RX_ALL_LOG, xmitMsec); + return; + } +#endif + + int state = iface->readData((uint8_t *)&radioBuffer, length); if (state != RADIOLIB_ERR_NONE) { LOG_ERROR("ignoring received packet due to error=%d\n", state); rxBad++; @@ -366,7 +399,6 @@ void RadioLibInterface::handleReceiveInterrupt() } else { // Skip the 4 headers that are at the beginning of the rxBuf int32_t payloadLen = length - sizeof(PacketHeader); - const uint8_t *payload = radiobuf + sizeof(PacketHeader); // check for short packets if (payloadLen < 0) { @@ -374,10 +406,9 @@ void RadioLibInterface::handleReceiveInterrupt() rxBad++; airTime->logAirtime(RX_ALL_LOG, xmitMsec); } else { - const PacketHeader *h = (PacketHeader *)radiobuf; rxGood++; // altered packet with "from == 0" can do Remote Node Administration without permission - if (h->from == 0) { + if (radioBuffer.header.from == 0) { LOG_WARN("ignoring received packet without sender\n"); return; } @@ -387,22 +418,22 @@ void RadioLibInterface::handleReceiveInterrupt() // nodes. meshtastic_MeshPacket *mp = packetPool.allocZeroed(); - mp->from = h->from; - mp->to = h->to; - mp->id = h->id; - mp->channel = h->channel; + mp->from = radioBuffer.header.from; + mp->to = radioBuffer.header.to; + mp->id = radioBuffer.header.id; + mp->channel = radioBuffer.header.channel; assert(HOP_MAX <= PACKET_FLAGS_HOP_LIMIT_MASK); // If hopmax changes, carefully check this code - mp->hop_limit = h->flags & PACKET_FLAGS_HOP_LIMIT_MASK; - mp->hop_start = (h->flags & PACKET_FLAGS_HOP_START_MASK) >> PACKET_FLAGS_HOP_START_SHIFT; - mp->want_ack = !!(h->flags & PACKET_FLAGS_WANT_ACK_MASK); - mp->via_mqtt = !!(h->flags & PACKET_FLAGS_VIA_MQTT_MASK); + mp->hop_limit = radioBuffer.header.flags & PACKET_FLAGS_HOP_LIMIT_MASK; + mp->hop_start = (radioBuffer.header.flags & PACKET_FLAGS_HOP_START_MASK) >> PACKET_FLAGS_HOP_START_SHIFT; + mp->want_ack = !!(radioBuffer.header.flags & PACKET_FLAGS_WANT_ACK_MASK); + mp->via_mqtt = !!(radioBuffer.header.flags & PACKET_FLAGS_VIA_MQTT_MASK); addReceiveMetadata(mp); mp->which_payload_variant = meshtastic_MeshPacket_encrypted_tag; // Mark that the payload is still encrypted at this point assert(((uint32_t)payloadLen) <= sizeof(mp->encrypted.bytes)); - memcpy(mp->encrypted.bytes, payload, payloadLen); + memcpy(mp->encrypted.bytes, radioBuffer.payload, payloadLen); mp->encrypted.size = payloadLen; printPacket("Lora RX", mp); @@ -437,14 +468,14 @@ void RadioLibInterface::startSend(meshtastic_MeshPacket *txp) { printPacket("Starting low level send", txp); if (disabled || !config.lora.tx_enabled) { - LOG_WARN("startSend is dropping tx packet because we are disabled\n"); + LOG_WARN("Drop Tx packet because LoRa Tx disabled\n"); packetPool.release(txp); } else { configHardwareForSend(); // must be after setStandby size_t numbytes = beginSending(txp); - int res = iface->startTransmit(radiobuf, numbytes); + int res = iface->startTransmit((uint8_t *)&radioBuffer, numbytes); if (res != RADIOLIB_ERR_NONE) { LOG_ERROR("startTransmit failed, error=%d\n", res); RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_RADIO_SPI_BUG); diff --git a/src/mesh/RadioLibInterface.h b/src/mesh/RadioLibInterface.h index edcbb394f..673f53a32 100644 --- a/src/mesh/RadioLibInterface.h +++ b/src/mesh/RadioLibInterface.h @@ -107,7 +107,7 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified /** * Debugging counts */ - uint32_t rxBad = 0, rxGood = 0, txGood = 0; + uint32_t rxBad = 0, rxGood = 0, txGood = 0, txRelay = 0; public: RadioLibInterface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, @@ -167,6 +167,10 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified meshtastic_QueueStatus getQueueStatus(); protected: + uint32_t activeReceiveStart = 0; + + bool receiveDetected(uint16_t irq, ulong syncWordHeaderValidFlag, ulong preambleDetectedFlag); + /** Do any hardware setup needed on entry into send configuration for the radio. * Subclasses can customize, but must also call this base method */ virtual void configHardwareForSend(); @@ -192,4 +196,6 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified * Subclasses must override, implement and then call into this base class implementation */ virtual void setStandby(); + + const char *radioLibErr = "RadioLib err=\n"; }; \ No newline at end of file diff --git a/src/mesh/RadioLibRF95.cpp b/src/mesh/RadioLibRF95.cpp index a202d4f4d..fe9bbdc93 100644 --- a/src/mesh/RadioLibRF95.cpp +++ b/src/mesh/RadioLibRF95.cpp @@ -1,3 +1,4 @@ +#if RADIOLIB_EXCLUDE_SX127X != 1 #include "RadioLibRF95.h" #include "configuration.h" @@ -81,4 +82,5 @@ uint8_t RadioLibRF95::readReg(uint8_t addr) { Module *mod = this->getMod(); return mod->SPIreadRegister(addr); -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/mesh/RadioLibRF95.h b/src/mesh/RadioLibRF95.h index 3bdb794f2..916a33234 100644 --- a/src/mesh/RadioLibRF95.h +++ b/src/mesh/RadioLibRF95.h @@ -1,4 +1,5 @@ #pragma once +#if RADIOLIB_EXCLUDE_SX127X != 1 #include /*! @@ -69,3 +70,4 @@ class RadioLibRF95 : public SX1278 // use the previous value float currentLimit = 100; }; +#endif \ No newline at end of file diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index 1f2c01473..fa05e7973 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -78,7 +78,7 @@ bool ReliableRouter::shouldFilterReceived(const meshtastic_MeshPacket *p) * Resending real ACKs is omitted, as you might receive a packet multiple times due to flooding and * flooding this ACK back to the original sender already adds redundancy. */ bool isRepeated = p->hop_start == 0 ? (p->hop_limit == HOP_RELIABLE) : (p->hop_start == p->hop_limit); - if (wasSeenRecently(p, false) && isRepeated && !MeshModule::currentReply && p->to != nodeDB->getNodeNum()) { + if (wasSeenRecently(p, false) && isRepeated && !MeshModule::currentReply && !isToUs(p)) { LOG_DEBUG("Resending implicit ack for a repeated floodmsg\n"); meshtastic_MeshPacket *tosend = packetPool.allocCopy(*p); tosend->hop_limit--; // bump down the hop count @@ -102,17 +102,15 @@ bool ReliableRouter::shouldFilterReceived(const meshtastic_MeshPacket *p) */ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtastic_Routing *c) { - NodeNum ourNode = getNodeNum(); - - if (p->to == ourNode) { // ignore ack/nak/want_ack packets that are not address to us (we only handle 0 hop reliability) + if (isToUs(p)) { // ignore ack/nak/want_ack packets that are not address to us (we only handle 0 hop reliability) if (p->want_ack) { if (MeshModule::currentReply) { - LOG_DEBUG("Some other module has replied to this message, no need for a 2nd ack\n"); + LOG_DEBUG("Another module replied to this message, no need for 2nd ack\n"); } else if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) { sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel, p->hop_start, p->hop_limit); } else if (p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag && p->channel == 0 && (nodeDB->getMeshNode(p->from) == nullptr || nodeDB->getMeshNode(p->from)->user.public_key.size == 0)) { - LOG_INFO("This looks like it might be a PKI packet from an unknown node, so send PKI_UNKNOWN_PUBKEY\n"); + LOG_INFO("PKI packet from unknown node, send PKI_UNKNOWN_PUBKEY\n"); sendAckNak(meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY, getFrom(p), p->id, channels.getPrimaryIndex(), p->hop_start, p->hop_limit); } else { @@ -124,7 +122,7 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag && c && c->error_reason == meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY) { if (owner.public_key.size == 32) { - LOG_INFO("This seems like a remote PKI decrypt failure, so send a NodeInfo"); + LOG_INFO("PKI decrypt failure, send a NodeInfo"); nodeInfoModule->sendOurNodeInfo(p->from, false, p->channel, true); } } @@ -136,11 +134,10 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas // We intentionally don't check wasSeenRecently, because it is harmless to delete non existent retransmission records if (ackId || nakId) { + LOG_DEBUG("Received a %s for 0x%x, stopping retransmissions\n", ackId ? "ACK" : "NAK", ackId); if (ackId) { - LOG_DEBUG("Received an ack for 0x%x, stopping retransmissions\n", ackId); stopRetransmission(p->to, ackId); } else { - LOG_DEBUG("Received a nak for 0x%x, stopping retransmissions\n", nakId); stopRetransmission(p->to, nakId); } } diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 8d27c5040..d6655d940 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -36,8 +36,8 @@ static MemoryDynamic staticPool; Allocator &packetPool = staticPool; -static uint8_t bytes[MAX_RHPACKETLEN]; -static uint8_t ScratchEncrypted[MAX_RHPACKETLEN]; +static uint8_t bytes[MAX_LORA_PAYLOAD_LEN + 1] __attribute__((__aligned__)); +static uint8_t ScratchEncrypted[MAX_LORA_PAYLOAD_LEN + 1] __attribute__((__aligned__)); /** * Constructor @@ -169,7 +169,7 @@ ErrorCode Router::sendLocal(meshtastic_MeshPacket *p, RxSource src) LOG_ERROR("Packet received with to: of 0!\n"); } // No need to deliver externally if the destination is the local node - if (p->to == nodeDB->getNodeNum()) { + if (isToUs(p)) { printPacket("Enqueued local", p); enqueueReceivedMessage(p); return ERRNO_OK; @@ -185,9 +185,12 @@ ErrorCode Router::sendLocal(meshtastic_MeshPacket *p, RxSource src) handleReceived(p, src); } - if (!p->channel) { // don't override if a channel was requested - p->channel = nodeDB->getMeshNodeChannel(p->to); - LOG_DEBUG("localSend to channel %d\n", p->channel); + if (!p->channel && !p->pki_encrypted) { // don't override if a channel was requested + meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(p->to); + if (node && node->user.public_key.size == 0) { + p->channel = node->channel; + LOG_DEBUG("localSend to channel %d\n", p->channel); + } } return send(p); @@ -201,7 +204,7 @@ ErrorCode Router::sendLocal(meshtastic_MeshPacket *p, RxSource src) */ ErrorCode Router::send(meshtastic_MeshPacket *p) { - if (p->to == nodeDB->getNodeNum()) { + if (isToUs(p)) { LOG_ERROR("BUG! send() called with packet destined for local node!\n"); packetPool.release(p); return meshtastic_Routing_Error_BAD_REQUEST; @@ -223,7 +226,7 @@ ErrorCode Router::send(meshtastic_MeshPacket *p) service->sendClientNotification(cn); #endif meshtastic_Routing_Error err = meshtastic_Routing_Error_DUTY_CYCLE_LIMIT; - if (getFrom(p) == nodeDB->getNodeNum()) { // only send NAK to API, not to the mesh + if (isFromUs(p)) { // only send NAK to API, not to the mesh abortSendAndNak(err, p); } else { packetPool.release(p); @@ -245,7 +248,7 @@ ErrorCode Router::send(meshtastic_MeshPacket *p) p->from = getFrom(p); // If we are the original transmitter, set the hop limit with which we start - if (p->from == getNodeNum()) + if (isFromUs(p)) p->hop_start = p->hop_limit; // If the packet hasn't yet been encrypted, do so now (it might already be encrypted if we are just forwarding it) @@ -270,7 +273,7 @@ ErrorCode Router::send(meshtastic_MeshPacket *p) } #if !MESHTASTIC_EXCLUDE_MQTT // Only publish to MQTT if we're the original transmitter of the packet - if (moduleConfig.mqtt.enabled && p->from == nodeDB->getNodeNum() && mqtt) { + if (moduleConfig.mqtt.enabled && isFromUs(p) && mqtt) { mqtt->onSend(*p, *p_decoded, chIndex); } #endif @@ -325,15 +328,15 @@ bool perhapsDecode(meshtastic_MeshPacket *p) memcpy(ScratchEncrypted, p->encrypted.bytes, rawSize); #if !(MESHTASTIC_EXCLUDE_PKI) // Attempt PKI decryption first - if (p->channel == 0 && p->to == nodeDB->getNodeNum() && p->to > 0 && p->to != NODENUM_BROADCAST && - nodeDB->getMeshNode(p->from) != nullptr && nodeDB->getMeshNode(p->from)->user.public_key.size > 0 && - nodeDB->getMeshNode(p->to)->user.public_key.size > 0 && rawSize > 12) { + if (p->channel == 0 && isToUs(p) && p->to > 0 && p->to != NODENUM_BROADCAST && nodeDB->getMeshNode(p->from) != nullptr && + nodeDB->getMeshNode(p->from)->user.public_key.size > 0 && nodeDB->getMeshNode(p->to)->user.public_key.size > 0 && + rawSize > MESHTASTIC_PKC_OVERHEAD) { LOG_DEBUG("Attempting PKI decryption\n"); if (crypto->decryptCurve25519(p->from, p->id, rawSize, ScratchEncrypted, bytes)) { LOG_INFO("PKI Decryption worked!\n"); memset(&p->decoded, 0, sizeof(p->decoded)); - rawSize -= 12; + rawSize -= MESHTASTIC_PKC_OVERHEAD; if (pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &p->decoded) && p->decoded.portnum != meshtastic_PortNum_UNKNOWN_APP) { decrypted = true; @@ -344,8 +347,11 @@ bool perhapsDecode(meshtastic_MeshPacket *p) // memcpy(bytes, ScratchEncrypted, rawSize); // TODO: Rename the bytes buffers // chIndex = 8; } else { + LOG_ERROR("PKC Decrypted, but pb_decode failed!\n"); return false; } + } else { + LOG_WARN("PKC decrypt attempted but failed!\n"); } } #endif @@ -378,6 +384,8 @@ bool perhapsDecode(meshtastic_MeshPacket *p) // parsing was successful p->which_payload_variant = meshtastic_MeshPacket_decoded_tag; // change type to decoded p->channel = chIndex; // change to store the index instead of the hash + if (p->decoded.has_bitfield) + p->decoded.want_response |= p->decoded.bitfield & BITFIELD_WANT_RESPONSE_MASK; /* Not actually ever used. // Decompress if needed. jm @@ -424,6 +432,12 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) // If the packet is not yet encrypted, do so now if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) { + if (isFromUs(p)) { + p->decoded.has_bitfield = true; + p->decoded.bitfield |= (config.lora.config_ok_to_mqtt << BITFIELD_OK_TO_MQTT_SHIFT); + p->decoded.bitfield |= (p->decoded.want_response << BITFIELD_WANT_RESPONSE_SHIFT); + } + size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_Data_msg, &p->decoded); /* Not actually used, so save the cycles @@ -461,7 +475,7 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) } } */ - if (numbytes > MAX_RHPACKETLEN) + if (numbytes + MESHTASTIC_HEADER_LENGTH > MAX_LORA_PAYLOAD_LEN) return meshtastic_Routing_Error_TOO_LARGE; // printBytes("plaintext", bytes, numbytes); @@ -470,21 +484,31 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) #if !(MESHTASTIC_EXCLUDE_PKI) meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(p->to); - if (!owner.is_licensed && config.security.private_key.size == 32 && p->to != NODENUM_BROADCAST && node != nullptr && - node->user.public_key.size > 0 && p->decoded.portnum != meshtastic_PortNum_TRACEROUTE_APP && - p->decoded.portnum != meshtastic_PortNum_NODEINFO_APP && p->decoded.portnum != meshtastic_PortNum_ROUTING_APP && - p->decoded.portnum != meshtastic_PortNum_POSITION_APP) { + // We may want to retool things so we can send a PKC packet when the client specifies a key and nodenum, even if the node + // is not in the local nodedb + if ( + // Don't use PKC with Ham mode + !owner.is_licensed && + // Don't use PKC if it's not explicitly requested and a non-primary channel is requested + !(p->pki_encrypted != true && p->channel > 0) && + // Check for valid keys and single node destination + config.security.private_key.size == 32 && p->to != NODENUM_BROADCAST && node != nullptr && + // Check for a known public key for the destination + (node->user.public_key.size == 32) && + // Some portnums either make no sense to send with PKC + p->decoded.portnum != meshtastic_PortNum_TRACEROUTE_APP && p->decoded.portnum != meshtastic_PortNum_NODEINFO_APP && + p->decoded.portnum != meshtastic_PortNum_ROUTING_APP && p->decoded.portnum != meshtastic_PortNum_POSITION_APP) { LOG_DEBUG("Using PKI!\n"); - if (numbytes + 12 > MAX_RHPACKETLEN) + if (numbytes + MESHTASTIC_HEADER_LENGTH + MESHTASTIC_PKC_OVERHEAD > MAX_LORA_PAYLOAD_LEN) return meshtastic_Routing_Error_TOO_LARGE; if (p->pki_encrypted && !memfll(p->public_key.bytes, 0, 32) && memcmp(p->public_key.bytes, node->user.public_key.bytes, 32) != 0) { - LOG_WARN("Client public key for client differs from requested! Requested 0x%02x, but stored key begins 0x%02x\n", - *p->public_key.bytes, *node->user.public_key.bytes); + LOG_WARN("Client public key differs from requested: 0x%02x, stored key begins 0x%02x\n", *p->public_key.bytes, + *node->user.public_key.bytes); return meshtastic_Routing_Error_PKI_FAILED; } crypto->encryptCurve25519(p->to, getFrom(p), p->id, numbytes, bytes, ScratchEncrypted); - numbytes += 12; + numbytes += MESHTASTIC_PKC_OVERHEAD; memcpy(p->encrypted.bytes, ScratchEncrypted, numbytes); p->channel = 0; p->pki_encrypted = true; @@ -566,7 +590,7 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src) skipHandle = true; } -#if EVENT_MODE +#if USERPREFS_EVENT_MODE if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag && (p->decoded.portnum == meshtastic_PortNum_ATAK_FORWARDER || p->decoded.portnum == meshtastic_PortNum_ATAK_PLUGIN || p->decoded.portnum == meshtastic_PortNum_PAXCOUNTER_APP || p->decoded.portnum == meshtastic_PortNum_IP_TUNNEL_APP || @@ -589,7 +613,7 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src) #if !MESHTASTIC_EXCLUDE_MQTT // After potentially altering it, publish received message to MQTT if we're not the original transmitter of the packet - if (decoded && moduleConfig.mqtt.enabled && getFrom(p) != nodeDB->getNodeNum() && mqtt) + if (decoded && moduleConfig.mqtt.enabled && !isFromUs(p) && mqtt) mqtt->onSend(*p_encrypted, *p, p->channel); #endif } diff --git a/src/mesh/Router.h b/src/mesh/Router.h index 98486745b..8ebc1a3e5 100644 --- a/src/mesh/Router.h +++ b/src/mesh/Router.h @@ -82,6 +82,10 @@ class Router : protected concurrency::OSThread */ virtual ErrorCode send(meshtastic_MeshPacket *p); + /* Statistics for the amount of duplicate received packets and the amount of times we cancel a relay because someone did it + before us */ + uint32_t rxDupe = 0, txRelayCanceled = 0; + protected: friend class RoutingModule; @@ -148,3 +152,8 @@ extern Router *router; /// Generate a unique packet id // FIXME, move this someplace better PacketId generatePacketId(); + +#define BITFIELD_WANT_RESPONSE_SHIFT 1 +#define BITFIELD_OK_TO_MQTT_SHIFT 0 +#define BITFIELD_WANT_RESPONSE_MASK (1 << BITFIELD_WANT_RESPONSE_SHIFT) +#define BITFIELD_OK_TO_MQTT_MASK (1 << BITFIELD_OK_TO_MQTT_SHIFT) \ No newline at end of file diff --git a/src/mesh/SX1262Interface.cpp b/src/mesh/SX1262Interface.cpp index e96e72b71..4c0dea00b 100644 --- a/src/mesh/SX1262Interface.cpp +++ b/src/mesh/SX1262Interface.cpp @@ -1,3 +1,4 @@ +#if RADIOLIB_EXCLUDE_SX126X != 1 #include "SX1262Interface.h" #include "configuration.h" #include "error.h" @@ -6,4 +7,5 @@ SX1262Interface::SX1262Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, R RADIOLIB_PIN_TYPE busy) : SX126xInterface(hal, cs, irq, rst, busy) { -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/mesh/SX1262Interface.h b/src/mesh/SX1262Interface.h index 31a12ae90..6e4616c8b 100644 --- a/src/mesh/SX1262Interface.h +++ b/src/mesh/SX1262Interface.h @@ -1,3 +1,4 @@ +#if RADIOLIB_EXCLUDE_SX126X != 1 #pragma once #include "SX126xInterface.h" @@ -10,4 +11,5 @@ class SX1262Interface : public SX126xInterface public: SX1262Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy); -}; \ No newline at end of file +}; +#endif \ No newline at end of file diff --git a/src/mesh/SX1268Interface.cpp b/src/mesh/SX1268Interface.cpp index ea299fce5..fe6e9af89 100644 --- a/src/mesh/SX1268Interface.cpp +++ b/src/mesh/SX1268Interface.cpp @@ -1,3 +1,4 @@ +#if RADIOLIB_EXCLUDE_SX126X != 1 #include "SX1268Interface.h" #include "configuration.h" #include "error.h" @@ -15,4 +16,5 @@ float SX1268Interface::getFreq() return 433.125f; else return savedFreq; -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/mesh/SX1268Interface.h b/src/mesh/SX1268Interface.h index c8bcf20a7..cc6dd3534 100644 --- a/src/mesh/SX1268Interface.h +++ b/src/mesh/SX1268Interface.h @@ -1,4 +1,5 @@ #pragma once +#if RADIOLIB_EXCLUDE_SX126X != 1 #include "SX126xInterface.h" @@ -13,3 +14,4 @@ class SX1268Interface : public SX126xInterface SX1268Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy); }; +#endif \ No newline at end of file diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index fcb3e4edb..924cdfa9f 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -1,3 +1,4 @@ +#if RADIOLIB_EXCLUDE_SX126X != 1 #include "SX126xInterface.h" #include "configuration.h" #include "error.h" @@ -6,6 +7,8 @@ #include "PortduinoGlue.h" #endif +#include "Throttle.h" + // Particular boards might define a different max power based on what their hardware can do, default to max power output if not // specified (may be dangerous if using external PA and SX126x power config forgotten) #ifndef SX126X_MAX_POWER @@ -25,15 +28,35 @@ SX126xInterface::SX126xInterface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs /// \return true if initialisation succeeded. template bool SX126xInterface::init() { -#ifdef SX126X_POWER_EN - pinMode(SX126X_POWER_EN, OUTPUT); + +// Typically, the RF switch on SX126x boards is controlled by two signals, which are negations of each other (switched RFIO +// paths). The negation is usually performed in hardware, or (suboptimal design) TXEN and RXEN are the two inputs to this style of +// RF switch. On some boards, there is no hardware negation between CTRL and ¬CTRL, but CTRL is internally connected to DIO2, and +// DIO2's switching is done by the SX126X itself, so the MCU can't control ¬CTRL at exactly the same time. One solution would be +// to set ¬CTRL as SX126X_TXEN or SX126X_RXEN, but they may already be used for another purpose, such as controlling another +// PA/LNA. Keeping ¬CTRL high seems to work, as long CTRL=1, ¬CTRL=1 has the opposite and stable RF path effect as CTRL=0 and +// ¬CTRL=1, this depends on the RF switch, but it seems this usually works. Better hardware design, which is done most the time, +// means this workaround is not necessary. +#ifdef SX126X_ANT_SW // Perhaps add RADIOLIB_NC check, and beforehand define as such if it is undefined, but it is not commonly + // used and not part of the 'default' set of pin definitions. + digitalWrite(SX126X_ANT_SW, HIGH); + pinMode(SX126X_ANT_SW, OUTPUT); +#endif + +#ifdef SX126X_POWER_EN // Perhaps add RADIOLIB_NC check, and beforehand define as such if it is undefined, but it is not commonly + // used and not part of the 'default' set of pin definitions. digitalWrite(SX126X_POWER_EN, HIGH); + pinMode(SX126X_POWER_EN, OUTPUT); #endif #if ARCH_PORTDUINO float tcxoVoltage = 0; if (settingsMap[dio3_tcxo_voltage]) tcxoVoltage = 1.8; + if (settingsMap[sx126x_ant_sw] != RADIOLIB_NC) { + digitalWrite(settingsMap[sx126x_ant_sw], HIGH); + pinMode(settingsMap[sx126x_ant_sw], OUTPUT); + } // FIXME: correct logic to default to not using TCXO if no voltage is specified for SX126X_DIO3_TCXO_VOLTAGE #elif !defined(SX126X_DIO3_TCXO_VOLTAGE) float tcxoVoltage = @@ -81,21 +104,19 @@ template bool SX126xInterface::init() LOG_DEBUG("Current limit set to %f\n", currentLimit); LOG_DEBUG("Current limit set result %d\n", res); -#ifdef SX126X_DIO2_AS_RF_SWITCH - LOG_DEBUG("Setting DIO2 as RF switch\n"); - bool dio2AsRfSwitch = true; -#elif defined(ARCH_PORTDUINO) - bool dio2AsRfSwitch = false; - if (settingsMap[dio2_as_rf_switch]) { - LOG_DEBUG("Setting DIO2 as RF switch\n"); - dio2AsRfSwitch = true; - } -#else - LOG_DEBUG("Setting DIO2 as not RF switch\n"); - bool dio2AsRfSwitch = false; -#endif if (res == RADIOLIB_ERR_NONE) { +#ifdef SX126X_DIO2_AS_RF_SWITCH + bool dio2AsRfSwitch = true; +#elif defined(ARCH_PORTDUINO) + bool dio2AsRfSwitch = false; + if (settingsMap[dio2_as_rf_switch]) { + dio2AsRfSwitch = true; + } +#else + bool dio2AsRfSwitch = false; +#endif res = lora.setDio2AsRfSwitch(dio2AsRfSwitch); + LOG_DEBUG("Set DIO2 as %sRF switch, result: %d\n", dio2AsRfSwitch ? "" : "not ", res); } // If a pin isn't defined, we set it to RADIOLIB_NC, it is safe to always do external RF switching with RADIOLIB_NC as it has @@ -182,17 +203,17 @@ template bool SX126xInterface::reconfigure() err = lora.setSyncWord(syncWord); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR("Radiolib error %d when attempting SX126X setSyncWord!\n", err); + LOG_ERROR("SX126X setSyncWord %s%d\n", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); err = lora.setCurrentLimit(currentLimit); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR("Radiolib error %d when attempting SX126X setCurrentLimit!\n", err); + LOG_ERROR("SX126X setCurrentLimit %s%d\n", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); err = lora.setPreambleLength(preambleLength); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR("Radiolib error %d when attempting SX126X setPreambleLength!\n", err); + LOG_ERROR("SX126X setPreambleLength %s%d\n", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); err = lora.setFrequency(getFreq()); @@ -204,7 +225,7 @@ template bool SX126xInterface::reconfigure() err = lora.setOutputPower(power); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR("Radiolib error %d when attempting SX126X setOutputPower!\n", err); + LOG_ERROR("SX126X setOutputPower %s%d\n", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); startReceive(); // restart receiving @@ -224,7 +245,7 @@ template void SX126xInterface::setStandby() int err = lora.standby(); if (err != RADIOLIB_ERR_NONE) - LOG_DEBUG("SX126x standby failed with error %d\n", err); + LOG_DEBUG("SX126x standby %s%d\n", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); isReceiving = false; // If we were receiving, not any more @@ -266,7 +287,7 @@ template void SX126xInterface::startReceive() // Furthermore, we need the PREAMBLE_DETECTED and HEADER_VALID IRQ flag to detect whether we are actively receiving int err = lora.startReceiveDutyCycleAuto(preambleLength, 8, RADIOLIB_IRQ_RX_DEFAULT_FLAGS | RADIOLIB_IRQ_PREAMBLE_DETECTED); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR("Radiolib error %d when attempting SX126X startReceiveDutyCycleAuto!\n", err); + LOG_ERROR("SX126X startReceiveDutyCycleAuto %s%d\n", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); RadioLibInterface::startReceive(); @@ -287,7 +308,7 @@ template bool SX126xInterface::isChannelActive() if (result == RADIOLIB_LORA_DETECTED) return true; if (result != RADIOLIB_CHANNEL_FREE) - LOG_ERROR("Radiolib error %d when attempting SX126X scanChannel!\n", result); + LOG_ERROR("SX126X scanChannel %s%d\n", radioLibErr, result); assert(result != RADIOLIB_ERR_WRONG_MODEM); return false; @@ -298,37 +319,15 @@ template bool SX126xInterface::isActivelyReceiving() { // The IRQ status will be cleared when we start our read operation. Check if we've started a header, but haven't yet // received and handled the interrupt for reading the packet/handling errors. - - uint16_t irq = lora.getIrqFlags(); - bool detected = (irq & (RADIOLIB_SX126X_IRQ_HEADER_VALID | RADIOLIB_SX126X_IRQ_PREAMBLE_DETECTED)); - // Handle false detections - if (detected) { - uint32_t now = millis(); - if (!activeReceiveStart) { - activeReceiveStart = now; - } else if ((now - activeReceiveStart > 2 * preambleTimeMsec) && !(irq & RADIOLIB_SX126X_IRQ_HEADER_VALID)) { - // The HEADER_VALID flag should be set by now if it was really a packet, so ignore PREAMBLE_DETECTED flag - activeReceiveStart = 0; - LOG_DEBUG("Ignore false preamble detection.\n"); - return false; - } else if (now - activeReceiveStart > maxPacketTimeMsec) { - // We should have gotten an RX_DONE IRQ by now if it was really a packet, so ignore HEADER_VALID flag - activeReceiveStart = 0; - LOG_DEBUG("Ignore false header detection.\n"); - return false; - } - } - - // if (detected) LOG_DEBUG("rx detected\n"); - return detected; + return receiveDetected(lora.getIrqFlags(), RADIOLIB_SX126X_IRQ_HEADER_VALID, RADIOLIB_SX126X_IRQ_PREAMBLE_DETECTED); } template bool SX126xInterface::sleep() { // Not keeping config is busted - next time nrf52 board boots lora sending fails tcxo related? - see datasheet // \todo Display actual typename of the adapter, not just `SX126x` - LOG_DEBUG("SX126x entering sleep mode (FIXME, don't keep config)\n"); - setStandby(); // Stop any pending operations + LOG_DEBUG("SX126x entering sleep mode\n"); // (FIXME, don't keep config) + setStandby(); // Stop any pending operations // turn off TCXO if it was powered // FIXME - this isn't correct @@ -344,3 +343,4 @@ template bool SX126xInterface::sleep() return true; } +#endif \ No newline at end of file diff --git a/src/mesh/SX126xInterface.h b/src/mesh/SX126xInterface.h index b392cd3e4..45b39a68a 100644 --- a/src/mesh/SX126xInterface.h +++ b/src/mesh/SX126xInterface.h @@ -1,4 +1,5 @@ #pragma once +#if RADIOLIB_EXCLUDE_SX126X != 1 #include "RadioLibInterface.h" @@ -67,7 +68,5 @@ template class SX126xInterface : public RadioLibInterface virtual void addReceiveMetadata(meshtastic_MeshPacket *mp) override; virtual void setStandby() override; - - private: - uint32_t activeReceiveStart = 0; }; +#endif \ No newline at end of file diff --git a/src/mesh/SX1280Interface.cpp b/src/mesh/SX1280Interface.cpp index 3287f141f..9e0d42122 100644 --- a/src/mesh/SX1280Interface.cpp +++ b/src/mesh/SX1280Interface.cpp @@ -1,3 +1,4 @@ +#if RADIOLIB_EXCLUDE_SX128X != 1 #include "SX1280Interface.h" #include "configuration.h" #include "error.h" @@ -7,3 +8,4 @@ SX1280Interface::SX1280Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, R : SX128xInterface(hal, cs, irq, rst, busy) { } +#endif \ No newline at end of file diff --git a/src/mesh/SX1280Interface.h b/src/mesh/SX1280Interface.h index 8f2c4ec2e..534dd8084 100644 --- a/src/mesh/SX1280Interface.h +++ b/src/mesh/SX1280Interface.h @@ -1,5 +1,5 @@ #pragma once - +#if RADIOLIB_EXCLUDE_SX128X != 1 #include "SX128xInterface.h" /** @@ -12,3 +12,4 @@ class SX1280Interface : public SX128xInterface SX1280Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy); }; +#endif diff --git a/src/mesh/SX128xInterface.cpp b/src/mesh/SX128xInterface.cpp index 9ff9ac2d7..d379f26e6 100644 --- a/src/mesh/SX128xInterface.cpp +++ b/src/mesh/SX128xInterface.cpp @@ -1,4 +1,6 @@ +#if RADIOLIB_EXCLUDE_SX128X != 1 #include "SX128xInterface.h" +#include "Throttle.h" #include "configuration.h" #include "error.h" #include "mesh/NodeDB.h" @@ -127,12 +129,12 @@ template bool SX128xInterface::reconfigure() err = lora.setSyncWord(syncWord); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR("Radiolib error %d when attempting SX128X setSyncWord!\n", err); + LOG_ERROR("SX128X setSyncWord %s%d\n", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); err = lora.setPreambleLength(preambleLength); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR("Radiolib error %d when attempting SX128X setPreambleLength!\n", err); + LOG_ERROR("SX128X setPreambleLength %s%d\n", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); err = lora.setFrequency(getFreq()); @@ -144,7 +146,7 @@ template bool SX128xInterface::reconfigure() err = lora.setOutputPower(power); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR("Radiolib error %d when attempting SX128X setOutputPower!\n", err); + LOG_ERROR("SX128X setOutputPower %s%d\n", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); startReceive(); // restart receiving @@ -169,7 +171,7 @@ template void SX128xInterface::setStandby() int err = lora.standby(); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR("SX128x standby failed with error %d\n", err); + LOG_ERROR("SX128x standby %s%d\n", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); #if ARCH_PORTDUINO if (settingsMap[rxen] != RADIOLIB_NC) { @@ -259,7 +261,7 @@ template void SX128xInterface::startReceive() int err = lora.startReceive(RADIOLIB_SX128X_RX_TIMEOUT_INF, RADIOLIB_IRQ_RX_DEFAULT_FLAGS | RADIOLIB_IRQ_PREAMBLE_DETECTED); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR("Radiolib error %d when attempting SX128X startReceive!\n", err); + LOG_ERROR("SX128X startReceive %s%d\n", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); RadioLibInterface::startReceive(); @@ -280,7 +282,7 @@ template bool SX128xInterface::isChannelActive() if (result == RADIOLIB_LORA_DETECTED) return true; if (result != RADIOLIB_CHANNEL_FREE) - LOG_ERROR("Radiolib error %d when attempting SX128X scanChannel!\n", result); + LOG_ERROR("SX128X scanChannel %s%d\n", radioLibErr, result); assert(result != RADIOLIB_ERR_WRONG_MODEM); return false; @@ -289,36 +291,15 @@ template bool SX128xInterface::isChannelActive() /** Could we send right now (i.e. either not actively receiving or transmitting)? */ template bool SX128xInterface::isActivelyReceiving() { - uint16_t irq = lora.getIrqStatus(); - bool detected = (irq & (RADIOLIB_SX128X_IRQ_HEADER_VALID | RADIOLIB_SX128X_IRQ_PREAMBLE_DETECTED)); - - // Handle false detections - if (detected) { - uint32_t now = millis(); - if (!activeReceiveStart) { - activeReceiveStart = now; - } else if ((now - activeReceiveStart > 2 * preambleTimeMsec) && !(irq & RADIOLIB_SX128X_IRQ_HEADER_VALID)) { - // The HEADER_VALID flag should be set by now if it was really a packet, so ignore PREAMBLE_DETECTED flag - activeReceiveStart = 0; - LOG_DEBUG("Ignore false preamble detection.\n"); - return false; - } else if (now - activeReceiveStart > maxPacketTimeMsec) { - // We should have gotten an RX_DONE IRQ by now if it was really a packet, so ignore HEADER_VALID flag - activeReceiveStart = 0; - LOG_DEBUG("Ignore false header detection.\n"); - return false; - } - } - - return detected; + return receiveDetected(lora.getIrqStatus(), RADIOLIB_SX128X_IRQ_HEADER_VALID, RADIOLIB_SX128X_IRQ_PREAMBLE_DETECTED); } template bool SX128xInterface::sleep() { // Not keeping config is busted - next time nrf52 board boots lora sending fails tcxo related? - see datasheet // \todo Display actual typename of the adapter, not just `SX128x` - LOG_DEBUG("SX128x entering sleep mode (FIXME, don't keep config)\n"); - setStandby(); // Stop any pending operations + LOG_DEBUG("SX128x entering sleep mode\n"); // (FIXME, don't keep config) + setStandby(); // Stop any pending operations // turn off TCXO if it was powered // FIXME - this isn't correct @@ -333,4 +314,5 @@ template bool SX128xInterface::sleep() #endif return true; -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/mesh/SX128xInterface.h b/src/mesh/SX128xInterface.h index f7fd35b25..bba31dab4 100644 --- a/src/mesh/SX128xInterface.h +++ b/src/mesh/SX128xInterface.h @@ -67,7 +67,4 @@ template class SX128xInterface : public RadioLibInterface virtual void addReceiveMetadata(meshtastic_MeshPacket *mp) override; virtual void setStandby() override; - - private: - uint32_t activeReceiveStart = 0; }; diff --git a/src/mesh/StreamAPI.cpp b/src/mesh/StreamAPI.cpp index e10638c03..a230de099 100644 --- a/src/mesh/StreamAPI.cpp +++ b/src/mesh/StreamAPI.cpp @@ -1,6 +1,6 @@ #include "StreamAPI.h" #include "PowerFSM.h" -#include "configuration.h" +#include "Throttle.h" #include "gps/RTC.h" #define START1 0x94 @@ -20,10 +20,9 @@ int32_t StreamAPI::runOncePart() */ int32_t StreamAPI::readStream() { - uint32_t now = millis(); if (!stream->available()) { // Nothing available this time, if the computer has talked to us recently, poll often, otherwise let CPU sleep a long time - bool recentRx = (now - lastRxMsec) < 2000; + bool recentRx = Throttle::isWithinTimespanMs(lastRxMsec, 2000); return recentRx ? 5 : 250; } else { while (stream->available()) { // Currently we never want to block @@ -71,7 +70,7 @@ int32_t StreamAPI::readStream() } // we had bytes available this time, so assume we might have them next time also - lastRxMsec = now; + lastRxMsec = millis(); return 0; } } diff --git a/src/mesh/Throttle.cpp b/src/mesh/Throttle.cpp index d8f23f9dc..f278cc843 100644 --- a/src/mesh/Throttle.cpp +++ b/src/mesh/Throttle.cpp @@ -24,4 +24,12 @@ bool Throttle::execute(uint32_t *lastExecutionMs, uint32_t minumumIntervalMs, vo onDefer(); } return false; +} + +/// @brief Check if the last execution time is within the interval +/// @param lastExecutionMs The last execution time in milliseconds +/// @param timeSpanMs The interval in milliseconds of the timespan +bool Throttle::isWithinTimespanMs(uint32_t lastExecutionMs, uint32_t timeSpanMs) +{ + return (millis() - lastExecutionMs) < timeSpanMs; } \ No newline at end of file diff --git a/src/mesh/Throttle.h b/src/mesh/Throttle.h index 8115595a4..8b4bb5d30 100644 --- a/src/mesh/Throttle.h +++ b/src/mesh/Throttle.h @@ -6,4 +6,5 @@ class Throttle { public: static bool execute(uint32_t *lastExecutionMs, uint32_t minumumIntervalMs, void (*func)(void), void (*onDefer)(void) = NULL); + static bool isWithinTimespanMs(uint32_t lastExecutionMs, uint32_t intervalMs); }; \ No newline at end of file diff --git a/src/mesh/TypeConversions.cpp b/src/mesh/TypeConversions.cpp index 513728ca5..550f87021 100644 --- a/src/mesh/TypeConversions.cpp +++ b/src/mesh/TypeConversions.cpp @@ -11,9 +11,13 @@ meshtastic_NodeInfo TypeConversions::ConvertToNodeInfo(const meshtastic_NodeInfo info.last_heard = lite->last_heard; info.channel = lite->channel; info.via_mqtt = lite->via_mqtt; - info.hops_away = lite->hops_away; info.is_favorite = lite->is_favorite; + if (lite->has_hops_away) { + info.has_hops_away = true; + info.hops_away = lite->hops_away; + } + if (lite->has_position) { info.has_position = true; if (lite->position.latitude_i != 0) @@ -78,7 +82,7 @@ meshtastic_UserLite TypeConversions::ConvertToUserLite(meshtastic_User user) lite.hw_model = user.hw_model; lite.role = user.role; lite.is_licensed = user.is_licensed; - memccpy(lite.macaddr, user.macaddr, sizeof(user.macaddr), sizeof(lite.macaddr)); + memcpy(lite.macaddr, user.macaddr, sizeof(lite.macaddr)); memcpy(lite.public_key.bytes, user.public_key.bytes, sizeof(lite.public_key.bytes)); lite.public_key.size = user.public_key.size; return lite; @@ -94,7 +98,7 @@ meshtastic_User TypeConversions::ConvertToUser(uint32_t nodeNum, meshtastic_User user.hw_model = lite.hw_model; user.role = lite.role; user.is_licensed = lite.is_licensed; - memccpy(user.macaddr, lite.macaddr, sizeof(lite.macaddr), sizeof(user.macaddr)); + memcpy(user.macaddr, lite.macaddr, sizeof(user.macaddr)); memcpy(user.public_key.bytes, lite.public_key.bytes, sizeof(user.public_key.bytes)); user.public_key.size = lite.public_key.size; diff --git a/src/mesh/TypedQueue.h b/src/mesh/TypedQueue.h index c96edae8e..f7d016f10 100644 --- a/src/mesh/TypedQueue.h +++ b/src/mesh/TypedQueue.h @@ -14,7 +14,7 @@ */ template class TypedQueue { - static_assert(std::is_pod::value, "T must be pod"); + static_assert(std::is_standard_layout::value, "T must be standard layout"); QueueHandle_t h; concurrency::OSThread *reader = NULL; diff --git a/src/mesh/aes-ccm.cpp b/src/mesh/aes-ccm.cpp index cd18ae6c5..b9af14fdb 100644 --- a/src/mesh/aes-ccm.cpp +++ b/src/mesh/aes-ccm.cpp @@ -95,7 +95,7 @@ static void aes_ccm_encr(size_t L, const uint8_t *in, size_t len, uint8_t *out, *out++ ^= *in++; } } -static void aes_ccm_encr_auth(size_t M, uint8_t *x, uint8_t *a, uint8_t *auth) +static void aes_ccm_encr_auth(size_t M, const uint8_t *x, uint8_t *a, uint8_t *auth) { size_t i; uint8_t tmp[AES_BLOCK_SIZE]; diff --git a/src/mesh/api/ServerAPI.cpp b/src/mesh/api/ServerAPI.cpp index 140567ad2..42217428d 100644 --- a/src/mesh/api/ServerAPI.cpp +++ b/src/mesh/api/ServerAPI.cpp @@ -5,7 +5,7 @@ template ServerAPI::ServerAPI(T &_client) : StreamAPI(&client), concurrency::OSThread("ServerAPI"), client(_client) { - LOG_INFO("Incoming wifi connection\n"); + LOG_INFO("Incoming API connection\n"); } template ServerAPI::~ServerAPI() @@ -45,10 +45,28 @@ template void APIServerPort::init() template int32_t APIServerPort::runOnce() { +#ifdef ARCH_ESP32 +#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0) + auto client = U::accept(); +#else auto client = U::available(); +#endif +#else + auto client = U::available(); +#endif if (client) { // Close any previous connection (see FIXME in header file) if (openAPI) { +#if RAK_4631 + // RAK13800 Ethernet requests periodically take more time + // This backoff addresses most cases keeping max wait < 1s + // Reconnections are delayed by full wait time + if (waitTime < 400) { + waitTime *= 2; + LOG_INFO("Previous TCP connection still open, trying again in %dms\n", waitTime); + return waitTime; + } +#endif LOG_INFO("Force closing previous TCP connection\n"); delete openAPI; } @@ -56,5 +74,8 @@ template int32_t APIServerPort::runOnce() openAPI = new T(client); } +#if RAK_4631 + waitTime = 100; +#endif return 100; // only check occasionally for incoming connections } diff --git a/src/mesh/api/ServerAPI.h b/src/mesh/api/ServerAPI.h index dd2a767c9..5b84fddd7 100644 --- a/src/mesh/api/ServerAPI.h +++ b/src/mesh/api/ServerAPI.h @@ -31,7 +31,7 @@ template class ServerAPI : public StreamAPI, private concurrency::OSTh }; /** - * Listens for incoming connections and does accepts and creates instances of WiFiServerAPI as needed + * Listens for incoming connections and does accepts and creates instances of ServerAPI as needed */ template class APIServerPort : public U, private concurrency::OSThread { @@ -41,6 +41,10 @@ template class APIServerPort : public U, private concurrency: * delegate to the worker. Once coroutines are implemented we can relax this restriction. */ T *openAPI = NULL; +#if RAK_4631 + // Track wait time for RAK13800 Ethernet requests + int32_t waitTime = 100; +#endif public: explicit APIServerPort(int port); diff --git a/src/mesh/api/ethServerAPI.h b/src/mesh/api/ethServerAPI.h index 59673a684..6f214c75a 100644 --- a/src/mesh/api/ethServerAPI.h +++ b/src/mesh/api/ethServerAPI.h @@ -14,7 +14,7 @@ class ethServerAPI : public ServerAPI }; /** - * Listens for incoming connections and does accepts and creates instances of WiFiServerAPI as needed + * Listens for incoming connections and does accepts and creates instances of EthernetServerAPI as needed */ class ethServerPort : public APIServerPort { diff --git a/src/mesh/compression/unishox2.cpp b/src/mesh/compression/unishox2.cpp index 4e239d489..9fc012a76 100644 --- a/src/mesh/compression/unishox2.cpp +++ b/src/mesh/compression/unishox2.cpp @@ -339,7 +339,7 @@ int32_t readUTF8(const char *in, int len, int l, int *utf8len) /// This is also used for Unicode strings \n /// This is a crude implementation that is not optimized. Assuming only short strings \n /// are encoded, this is not much of an issue. -int matchOccurance(const char *in, int len, int l, char *out, int olen, int *ol, uint8_t *state, const uint8_t usx_hcodes[], +int matchOccurance(const char *in, int len, int l, char *out, int olen, int *ol, const uint8_t *state, const uint8_t usx_hcodes[], const uint8_t usx_hcode_lens[]) { int j, k; @@ -383,7 +383,7 @@ int matchOccurance(const char *in, int len, int l, char *out, int olen, int *ol, /// This is also used for Unicode strings \n /// This is a crude implementation that is not optimized. Assuming only short strings \n /// are encoded, this is not much of an issue. -int matchLine(const char *in, int len, int l, char *out, int olen, int *ol, struct us_lnk_lst *prev_lines, uint8_t *state, +int matchLine(const char *in, int len, int l, char *out, int olen, int *ol, struct us_lnk_lst *prev_lines, const uint8_t *state, const uint8_t usx_hcodes[], const uint8_t usx_hcode_lens[]) { int last_ol = *ol; diff --git a/src/mesh/eth/ethClient.cpp b/src/mesh/eth/ethClient.cpp index 9f3bb8ab7..1c97f3bed 100644 --- a/src/mesh/eth/ethClient.cpp +++ b/src/mesh/eth/ethClient.cpp @@ -38,7 +38,7 @@ static int32_t reconnectETH() Ethernet.maintain(); if (!ethStartupComplete) { // Start web server - LOG_INFO("... Starting network services\n"); + LOG_INFO("Starting Ethernet network services\n"); #ifndef DISABLE_NTP LOG_INFO("Starting NTP time client\n"); @@ -131,7 +131,8 @@ bool initEthernet() status = Ethernet.begin(mac); } else if (config.network.address_mode == meshtastic_Config_NetworkConfig_AddressMode_STATIC) { LOG_INFO("starting Ethernet Static\n"); - Ethernet.begin(mac, config.network.ipv4_config.ip, config.network.ipv4_config.dns, config.network.ipv4_config.subnet); + Ethernet.begin(mac, config.network.ipv4_config.ip, config.network.ipv4_config.dns, config.network.ipv4_config.gateway, + config.network.ipv4_config.subnet); status = 1; } else { LOG_INFO("Ethernet Disabled\n"); @@ -186,4 +187,4 @@ bool isEthernetAvailable() } } -#endif \ No newline at end of file +#endif diff --git a/src/mesh/generated/meshtastic/admin.pb.cpp b/src/mesh/generated/meshtastic/admin.pb.cpp index 339960302..8b3fd3d1b 100644 --- a/src/mesh/generated/meshtastic/admin.pb.cpp +++ b/src/mesh/generated/meshtastic/admin.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #include "meshtastic/admin.pb.h" #if PB_PROTO_HEADER_VERSION != 40 @@ -18,3 +18,5 @@ PB_BIND(meshtastic_NodeRemoteHardwarePinsResponse, meshtastic_NodeRemoteHardware + + diff --git a/src/mesh/generated/meshtastic/admin.pb.h b/src/mesh/generated/meshtastic/admin.pb.h index c1ff7ebd4..bf81269b4 100644 --- a/src/mesh/generated/meshtastic/admin.pb.h +++ b/src/mesh/generated/meshtastic/admin.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #ifndef PB_MESHTASTIC_MESHTASTIC_ADMIN_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_ADMIN_PB_H_INCLUDED diff --git a/src/mesh/generated/meshtastic/apponly.pb.cpp b/src/mesh/generated/meshtastic/apponly.pb.cpp index 44b0ea3cc..64d43b7ee 100644 --- a/src/mesh/generated/meshtastic/apponly.pb.cpp +++ b/src/mesh/generated/meshtastic/apponly.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #include "meshtastic/apponly.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/apponly.pb.h b/src/mesh/generated/meshtastic/apponly.pb.h index f5bacea52..dc08d9ff3 100644 --- a/src/mesh/generated/meshtastic/apponly.pb.h +++ b/src/mesh/generated/meshtastic/apponly.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #ifndef PB_MESHTASTIC_MESHTASTIC_APPONLY_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_APPONLY_PB_H_INCLUDED @@ -55,7 +55,7 @@ extern const pb_msgdesc_t meshtastic_ChannelSet_msg; /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_APPONLY_PB_H_MAX_SIZE meshtastic_ChannelSet_size -#define meshtastic_ChannelSet_size 676 +#define meshtastic_ChannelSet_size 679 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/meshtastic/atak.pb.cpp b/src/mesh/generated/meshtastic/atak.pb.cpp index 491336bcf..6dbc69fb4 100644 --- a/src/mesh/generated/meshtastic/atak.pb.cpp +++ b/src/mesh/generated/meshtastic/atak.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #include "meshtastic/atak.pb.h" #if PB_PROTO_HEADER_VERSION != 40 @@ -27,3 +27,5 @@ PB_BIND(meshtastic_PLI, meshtastic_PLI, AUTO) + + diff --git a/src/mesh/generated/meshtastic/atak.pb.h b/src/mesh/generated/meshtastic/atak.pb.h index 5fd18f963..15a86788b 100644 --- a/src/mesh/generated/meshtastic/atak.pb.h +++ b/src/mesh/generated/meshtastic/atak.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #ifndef PB_MESHTASTIC_MESHTASTIC_ATAK_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_ATAK_PB_H_INCLUDED @@ -120,6 +120,7 @@ typedef struct _meshtastic_PLI { uint16_t course; } meshtastic_PLI; +typedef PB_BYTES_ARRAY_T(220) meshtastic_TAKPacket_detail_t; /* Packets for the official ATAK Plugin */ typedef struct _meshtastic_TAKPacket { /* Are the payloads strings compressed for LoRA transport? */ @@ -139,6 +140,9 @@ typedef struct _meshtastic_TAKPacket { meshtastic_PLI pli; /* ATAK GeoChat message */ meshtastic_GeoChat chat; + /* Generic CoT detail XML + May be compressed / truncated by the sender (EUD) */ + meshtastic_TAKPacket_detail_t detail; } payload_variant; } meshtastic_TAKPacket; @@ -199,6 +203,7 @@ extern "C" { #define meshtastic_TAKPacket_status_tag 4 #define meshtastic_TAKPacket_pli_tag 5 #define meshtastic_TAKPacket_chat_tag 6 +#define meshtastic_TAKPacket_detail_tag 7 /* Struct field encoding specification for nanopb */ #define meshtastic_TAKPacket_FIELDLIST(X, a) \ @@ -207,7 +212,8 @@ X(a, STATIC, OPTIONAL, MESSAGE, contact, 2) \ X(a, STATIC, OPTIONAL, MESSAGE, group, 3) \ X(a, STATIC, OPTIONAL, MESSAGE, status, 4) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,pli,payload_variant.pli), 5) \ -X(a, STATIC, ONEOF, MESSAGE, (payload_variant,chat,payload_variant.chat), 6) +X(a, STATIC, ONEOF, MESSAGE, (payload_variant,chat,payload_variant.chat), 6) \ +X(a, STATIC, ONEOF, BYTES, (payload_variant,detail,payload_variant.detail), 7) #define meshtastic_TAKPacket_CALLBACK NULL #define meshtastic_TAKPacket_DEFAULT NULL #define meshtastic_TAKPacket_contact_MSGTYPE meshtastic_Contact diff --git a/src/mesh/generated/meshtastic/cannedmessages.pb.cpp b/src/mesh/generated/meshtastic/cannedmessages.pb.cpp index 71e659be2..9f51e9634 100644 --- a/src/mesh/generated/meshtastic/cannedmessages.pb.cpp +++ b/src/mesh/generated/meshtastic/cannedmessages.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #include "meshtastic/cannedmessages.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/cannedmessages.pb.h b/src/mesh/generated/meshtastic/cannedmessages.pb.h index c3f9a8b9b..06d14b98f 100644 --- a/src/mesh/generated/meshtastic/cannedmessages.pb.h +++ b/src/mesh/generated/meshtastic/cannedmessages.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #ifndef PB_MESHTASTIC_MESHTASTIC_CANNEDMESSAGES_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_CANNEDMESSAGES_PB_H_INCLUDED diff --git a/src/mesh/generated/meshtastic/channel.pb.cpp b/src/mesh/generated/meshtastic/channel.pb.cpp index fe76d8140..52f923b13 100644 --- a/src/mesh/generated/meshtastic/channel.pb.cpp +++ b/src/mesh/generated/meshtastic/channel.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #include "meshtastic/channel.pb.h" #if PB_PROTO_HEADER_VERSION != 40 @@ -17,3 +17,4 @@ PB_BIND(meshtastic_Channel, meshtastic_Channel, AUTO) + diff --git a/src/mesh/generated/meshtastic/channel.pb.h b/src/mesh/generated/meshtastic/channel.pb.h index d9c7d4ffa..3d617ae39 100644 --- a/src/mesh/generated/meshtastic/channel.pb.h +++ b/src/mesh/generated/meshtastic/channel.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #ifndef PB_MESHTASTIC_MESHTASTIC_CHANNEL_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_CHANNEL_PB_H_INCLUDED diff --git a/src/mesh/generated/meshtastic/clientonly.pb.cpp b/src/mesh/generated/meshtastic/clientonly.pb.cpp index 44c6f95ce..d99af8cf5 100644 --- a/src/mesh/generated/meshtastic/clientonly.pb.cpp +++ b/src/mesh/generated/meshtastic/clientonly.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #include "meshtastic/clientonly.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/clientonly.pb.h b/src/mesh/generated/meshtastic/clientonly.pb.h index dc323292a..bf32d7875 100644 --- a/src/mesh/generated/meshtastic/clientonly.pb.h +++ b/src/mesh/generated/meshtastic/clientonly.pb.h @@ -1,10 +1,11 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #ifndef PB_MESHTASTIC_MESHTASTIC_CLIENTONLY_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_CLIENTONLY_PB_H_INCLUDED #include #include "meshtastic/localonly.pb.h" +#include "meshtastic/mesh.pb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. @@ -28,6 +29,15 @@ typedef struct _meshtastic_DeviceProfile { /* The ModuleConfig of the node */ bool has_module_config; meshtastic_LocalModuleConfig module_config; + /* Fixed position data */ + bool has_fixed_position; + meshtastic_Position fixed_position; + /* Ringtone for ExternalNotification */ + bool has_ringtone; + char ringtone[231]; + /* Predefined messages for CannedMessage */ + bool has_canned_messages; + char canned_messages[201]; } meshtastic_DeviceProfile; @@ -36,8 +46,8 @@ extern "C" { #endif /* Initializer values for message structs */ -#define meshtastic_DeviceProfile_init_default {false, "", false, "", {{NULL}, NULL}, false, meshtastic_LocalConfig_init_default, false, meshtastic_LocalModuleConfig_init_default} -#define meshtastic_DeviceProfile_init_zero {false, "", false, "", {{NULL}, NULL}, false, meshtastic_LocalConfig_init_zero, false, meshtastic_LocalModuleConfig_init_zero} +#define meshtastic_DeviceProfile_init_default {false, "", false, "", {{NULL}, NULL}, false, meshtastic_LocalConfig_init_default, false, meshtastic_LocalModuleConfig_init_default, false, meshtastic_Position_init_default, false, "", false, ""} +#define meshtastic_DeviceProfile_init_zero {false, "", false, "", {{NULL}, NULL}, false, meshtastic_LocalConfig_init_zero, false, meshtastic_LocalModuleConfig_init_zero, false, meshtastic_Position_init_zero, false, "", false, ""} /* Field tags (for use in manual encoding/decoding) */ #define meshtastic_DeviceProfile_long_name_tag 1 @@ -45,6 +55,9 @@ extern "C" { #define meshtastic_DeviceProfile_channel_url_tag 3 #define meshtastic_DeviceProfile_config_tag 4 #define meshtastic_DeviceProfile_module_config_tag 5 +#define meshtastic_DeviceProfile_fixed_position_tag 6 +#define meshtastic_DeviceProfile_ringtone_tag 7 +#define meshtastic_DeviceProfile_canned_messages_tag 8 /* Struct field encoding specification for nanopb */ #define meshtastic_DeviceProfile_FIELDLIST(X, a) \ @@ -52,11 +65,15 @@ X(a, STATIC, OPTIONAL, STRING, long_name, 1) \ X(a, STATIC, OPTIONAL, STRING, short_name, 2) \ X(a, CALLBACK, OPTIONAL, STRING, channel_url, 3) \ X(a, STATIC, OPTIONAL, MESSAGE, config, 4) \ -X(a, STATIC, OPTIONAL, MESSAGE, module_config, 5) +X(a, STATIC, OPTIONAL, MESSAGE, module_config, 5) \ +X(a, STATIC, OPTIONAL, MESSAGE, fixed_position, 6) \ +X(a, STATIC, OPTIONAL, STRING, ringtone, 7) \ +X(a, STATIC, OPTIONAL, STRING, canned_messages, 8) #define meshtastic_DeviceProfile_CALLBACK pb_default_field_callback #define meshtastic_DeviceProfile_DEFAULT NULL #define meshtastic_DeviceProfile_config_MSGTYPE meshtastic_LocalConfig #define meshtastic_DeviceProfile_module_config_MSGTYPE meshtastic_LocalModuleConfig +#define meshtastic_DeviceProfile_fixed_position_MSGTYPE meshtastic_Position extern const pb_msgdesc_t meshtastic_DeviceProfile_msg; diff --git a/src/mesh/generated/meshtastic/config.pb.cpp b/src/mesh/generated/meshtastic/config.pb.cpp index 92c3313bd..23f4d542b 100644 --- a/src/mesh/generated/meshtastic/config.pb.cpp +++ b/src/mesh/generated/meshtastic/config.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #include "meshtastic/config.pb.h" #if PB_PROTO_HEADER_VERSION != 40 @@ -46,6 +46,19 @@ PB_BIND(meshtastic_Config_SessionkeyConfig, meshtastic_Config_SessionkeyConfig, + + + + + + + + + + + + + diff --git a/src/mesh/generated/meshtastic/config.pb.h b/src/mesh/generated/meshtastic/config.pb.h index eb03ddc58..988f852ff 100644 --- a/src/mesh/generated/meshtastic/config.pb.h +++ b/src/mesh/generated/meshtastic/config.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #ifndef PB_MESHTASTIC_MESHTASTIC_CONFIG_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_CONFIG_PB_H_INCLUDED @@ -238,7 +238,13 @@ typedef enum _meshtastic_Config_LoRaConfig_RegionCode { /* Malaysia 919mhz */ meshtastic_Config_LoRaConfig_RegionCode_MY_919 = 17, /* Singapore 923mhz */ - meshtastic_Config_LoRaConfig_RegionCode_SG_923 = 18 + meshtastic_Config_LoRaConfig_RegionCode_SG_923 = 18, + /* Philippines 433mhz */ + meshtastic_Config_LoRaConfig_RegionCode_PH_433 = 19, + /* Philippines 868mhz */ + meshtastic_Config_LoRaConfig_RegionCode_PH_868 = 20, + /* Philippines 915mhz */ + meshtastic_Config_LoRaConfig_RegionCode_PH_915 = 21 } meshtastic_Config_LoRaConfig_RegionCode; /* Standard predefined channel settings @@ -510,6 +516,8 @@ typedef struct _meshtastic_Config_LoRaConfig { uint32_t ignore_incoming[3]; /* If true, the device will not process any packets received via LoRa that passed via MQTT anywhere on the path towards it. */ bool ignore_mqtt; + /* Sets the ok_to_mqtt bit on outgoing packets */ + bool config_ok_to_mqtt; } meshtastic_Config_LoRaConfig; typedef struct _meshtastic_Config_BluetoothConfig { @@ -533,7 +541,7 @@ typedef struct _meshtastic_Config_SecurityConfig { meshtastic_Config_SecurityConfig_private_key_t private_key; /* The public key authorized to send admin messages to this node. */ pb_size_t admin_key_count; - meshtastic_Config_SecurityConfig_admin_key_t admin_key[1]; + meshtastic_Config_SecurityConfig_admin_key_t admin_key[3]; /* If true, device is considered to be "managed" by a mesh administrator via admin messages Device is managed by a mesh administrator. */ bool is_managed; @@ -613,8 +621,8 @@ extern "C" { #define _meshtastic_Config_DisplayConfig_CompassOrientation_ARRAYSIZE ((meshtastic_Config_DisplayConfig_CompassOrientation)(meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_270_INVERTED+1)) #define _meshtastic_Config_LoRaConfig_RegionCode_MIN meshtastic_Config_LoRaConfig_RegionCode_UNSET -#define _meshtastic_Config_LoRaConfig_RegionCode_MAX meshtastic_Config_LoRaConfig_RegionCode_SG_923 -#define _meshtastic_Config_LoRaConfig_RegionCode_ARRAYSIZE ((meshtastic_Config_LoRaConfig_RegionCode)(meshtastic_Config_LoRaConfig_RegionCode_SG_923+1)) +#define _meshtastic_Config_LoRaConfig_RegionCode_MAX meshtastic_Config_LoRaConfig_RegionCode_PH_915 +#define _meshtastic_Config_LoRaConfig_RegionCode_ARRAYSIZE ((meshtastic_Config_LoRaConfig_RegionCode)(meshtastic_Config_LoRaConfig_RegionCode_PH_915+1)) #define _meshtastic_Config_LoRaConfig_ModemPreset_MIN meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST #define _meshtastic_Config_LoRaConfig_ModemPreset_MAX meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO @@ -656,9 +664,9 @@ extern "C" { #define meshtastic_Config_NetworkConfig_init_default {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_default, ""} #define meshtastic_Config_NetworkConfig_IpV4Config_init_default {0, 0, 0, 0} #define meshtastic_Config_DisplayConfig_init_default {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN} -#define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0} +#define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0} #define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0} -#define meshtastic_Config_SecurityConfig_init_default {{0, {0}}, {0, {0}}, 0, {{0, {0}}}, 0, 0, 0, 0} +#define meshtastic_Config_SecurityConfig_init_default {{0, {0}}, {0, {0}}, 0, {{0, {0}}, {0, {0}}, {0, {0}}}, 0, 0, 0, 0} #define meshtastic_Config_SessionkeyConfig_init_default {0} #define meshtastic_Config_init_zero {0, {meshtastic_Config_DeviceConfig_init_zero}} #define meshtastic_Config_DeviceConfig_init_zero {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0} @@ -667,9 +675,9 @@ extern "C" { #define meshtastic_Config_NetworkConfig_init_zero {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_zero, ""} #define meshtastic_Config_NetworkConfig_IpV4Config_init_zero {0, 0, 0, 0} #define meshtastic_Config_DisplayConfig_init_zero {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN} -#define meshtastic_Config_LoRaConfig_init_zero {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0} +#define meshtastic_Config_LoRaConfig_init_zero {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0} #define meshtastic_Config_BluetoothConfig_init_zero {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0} -#define meshtastic_Config_SecurityConfig_init_zero {{0, {0}}, {0, {0}}, 0, {{0, {0}}}, 0, 0, 0, 0} +#define meshtastic_Config_SecurityConfig_init_zero {{0, {0}}, {0, {0}}, 0, {{0, {0}}, {0, {0}}, {0, {0}}}, 0, 0, 0, 0} #define meshtastic_Config_SessionkeyConfig_init_zero {0} /* Field tags (for use in manual encoding/decoding) */ @@ -746,6 +754,7 @@ extern "C" { #define meshtastic_Config_LoRaConfig_pa_fan_disabled_tag 15 #define meshtastic_Config_LoRaConfig_ignore_incoming_tag 103 #define meshtastic_Config_LoRaConfig_ignore_mqtt_tag 104 +#define meshtastic_Config_LoRaConfig_config_ok_to_mqtt_tag 105 #define meshtastic_Config_BluetoothConfig_enabled_tag 1 #define meshtastic_Config_BluetoothConfig_mode_tag 2 #define meshtastic_Config_BluetoothConfig_fixed_pin_tag 3 @@ -887,7 +896,8 @@ X(a, STATIC, SINGULAR, BOOL, sx126x_rx_boosted_gain, 13) \ X(a, STATIC, SINGULAR, FLOAT, override_frequency, 14) \ X(a, STATIC, SINGULAR, BOOL, pa_fan_disabled, 15) \ X(a, STATIC, REPEATED, UINT32, ignore_incoming, 103) \ -X(a, STATIC, SINGULAR, BOOL, ignore_mqtt, 104) +X(a, STATIC, SINGULAR, BOOL, ignore_mqtt, 104) \ +X(a, STATIC, SINGULAR, BOOL, config_ok_to_mqtt, 105) #define meshtastic_Config_LoRaConfig_CALLBACK NULL #define meshtastic_Config_LoRaConfig_DEFAULT NULL @@ -944,12 +954,12 @@ extern const pb_msgdesc_t meshtastic_Config_SessionkeyConfig_msg; #define meshtastic_Config_BluetoothConfig_size 10 #define meshtastic_Config_DeviceConfig_size 98 #define meshtastic_Config_DisplayConfig_size 30 -#define meshtastic_Config_LoRaConfig_size 82 +#define meshtastic_Config_LoRaConfig_size 85 #define meshtastic_Config_NetworkConfig_IpV4Config_size 20 #define meshtastic_Config_NetworkConfig_size 196 #define meshtastic_Config_PositionConfig_size 62 #define meshtastic_Config_PowerConfig_size 52 -#define meshtastic_Config_SecurityConfig_size 111 +#define meshtastic_Config_SecurityConfig_size 178 #define meshtastic_Config_SessionkeyConfig_size 0 #define meshtastic_Config_size 199 diff --git a/src/mesh/generated/meshtastic/connection_status.pb.cpp b/src/mesh/generated/meshtastic/connection_status.pb.cpp index fc5a364dd..d1495bb83 100644 --- a/src/mesh/generated/meshtastic/connection_status.pb.cpp +++ b/src/mesh/generated/meshtastic/connection_status.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #include "meshtastic/connection_status.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/connection_status.pb.h b/src/mesh/generated/meshtastic/connection_status.pb.h index 1c618e4d4..c433e370b 100644 --- a/src/mesh/generated/meshtastic/connection_status.pb.h +++ b/src/mesh/generated/meshtastic/connection_status.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #ifndef PB_MESHTASTIC_MESHTASTIC_CONNECTION_STATUS_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_CONNECTION_STATUS_PB_H_INCLUDED diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.cpp b/src/mesh/generated/meshtastic/deviceonly.pb.cpp index 2747ac9d9..135634762 100644 --- a/src/mesh/generated/meshtastic/deviceonly.pb.cpp +++ b/src/mesh/generated/meshtastic/deviceonly.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #include "meshtastic/deviceonly.pb.h" #if PB_PROTO_HEADER_VERSION != 40 @@ -26,3 +26,4 @@ PB_BIND(meshtastic_OEMStore, meshtastic_OEMStore, 2) + diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.h b/src/mesh/generated/meshtastic/deviceonly.pb.h index 692402210..2aa8fda8e 100644 --- a/src/mesh/generated/meshtastic/deviceonly.pb.h +++ b/src/mesh/generated/meshtastic/deviceonly.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #ifndef PB_MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_INCLUDED @@ -94,6 +94,7 @@ typedef struct _meshtastic_NodeInfoLite { /* True if we witnessed the node over MQTT instead of LoRA transport */ bool via_mqtt; /* Number of hops away from us this node is (0 if adjacent) */ + bool has_hops_away; uint8_t hops_away; /* True if node is in our favorites list Persists between NodeDB internal clean ups */ @@ -202,13 +203,13 @@ extern "C" { /* Initializer values for message structs */ #define meshtastic_PositionLite_init_default {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN} #define meshtastic_UserLite_init_default {{0}, "", "", _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}} -#define meshtastic_NodeInfoLite_init_default {0, false, meshtastic_UserLite_init_default, false, meshtastic_PositionLite_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, 0, 0} +#define meshtastic_NodeInfoLite_init_default {0, false, meshtastic_UserLite_init_default, false, meshtastic_PositionLite_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, false, 0, 0} #define meshtastic_DeviceState_init_default {false, meshtastic_MyNodeInfo_init_default, false, meshtastic_User_init_default, 0, {meshtastic_MeshPacket_init_default}, false, meshtastic_MeshPacket_init_default, 0, 0, 0, false, meshtastic_MeshPacket_init_default, 0, {meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default}, {0}} #define meshtastic_ChannelFile_init_default {0, {meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default}, 0} #define meshtastic_OEMStore_init_default {0, 0, {0, {0}}, _meshtastic_ScreenFonts_MIN, "", {0, {0}}, false, meshtastic_LocalConfig_init_default, false, meshtastic_LocalModuleConfig_init_default} #define meshtastic_PositionLite_init_zero {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN} #define meshtastic_UserLite_init_zero {{0}, "", "", _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}} -#define meshtastic_NodeInfoLite_init_zero {0, false, meshtastic_UserLite_init_zero, false, meshtastic_PositionLite_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, 0, 0} +#define meshtastic_NodeInfoLite_init_zero {0, false, meshtastic_UserLite_init_zero, false, meshtastic_PositionLite_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, false, 0, 0} #define meshtastic_DeviceState_init_zero {false, meshtastic_MyNodeInfo_init_zero, false, meshtastic_User_init_zero, 0, {meshtastic_MeshPacket_init_zero}, false, meshtastic_MeshPacket_init_zero, 0, 0, 0, false, meshtastic_MeshPacket_init_zero, 0, {meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero}, {0}} #define meshtastic_ChannelFile_init_zero {0, {meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero}, 0} #define meshtastic_OEMStore_init_zero {0, 0, {0, {0}}, _meshtastic_ScreenFonts_MIN, "", {0, {0}}, false, meshtastic_LocalConfig_init_zero, false, meshtastic_LocalModuleConfig_init_zero} @@ -287,7 +288,7 @@ X(a, STATIC, SINGULAR, FIXED32, last_heard, 5) \ X(a, STATIC, OPTIONAL, MESSAGE, device_metrics, 6) \ X(a, STATIC, SINGULAR, UINT32, channel, 7) \ X(a, STATIC, SINGULAR, BOOL, via_mqtt, 8) \ -X(a, STATIC, SINGULAR, UINT32, hops_away, 9) \ +X(a, STATIC, OPTIONAL, UINT32, hops_away, 9) \ X(a, STATIC, SINGULAR, BOOL, is_favorite, 10) #define meshtastic_NodeInfoLite_CALLBACK NULL #define meshtastic_NodeInfoLite_DEFAULT NULL @@ -358,7 +359,7 @@ extern const pb_msgdesc_t meshtastic_OEMStore_msg; #define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_OEMStore_size #define meshtastic_ChannelFile_size 718 #define meshtastic_NodeInfoLite_size 183 -#define meshtastic_OEMStore_size 3497 +#define meshtastic_OEMStore_size 3578 #define meshtastic_PositionLite_size 28 #define meshtastic_UserLite_size 96 diff --git a/src/mesh/generated/meshtastic/localonly.pb.cpp b/src/mesh/generated/meshtastic/localonly.pb.cpp index 9bc98fb85..0a752a5a8 100644 --- a/src/mesh/generated/meshtastic/localonly.pb.cpp +++ b/src/mesh/generated/meshtastic/localonly.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #include "meshtastic/localonly.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/localonly.pb.h b/src/mesh/generated/meshtastic/localonly.pb.h index 91a23dc4f..6409aef74 100644 --- a/src/mesh/generated/meshtastic/localonly.pb.h +++ b/src/mesh/generated/meshtastic/localonly.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #ifndef PB_MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_INCLUDED @@ -186,9 +186,9 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg; #define meshtastic_LocalModuleConfig_fields &meshtastic_LocalModuleConfig_msg /* Maximum encoded size of messages (where known) */ -#define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalModuleConfig_size -#define meshtastic_LocalConfig_size 664 -#define meshtastic_LocalModuleConfig_size 687 +#define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalConfig_size +#define meshtastic_LocalConfig_size 735 +#define meshtastic_LocalModuleConfig_size 697 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/meshtastic/mesh.pb.cpp b/src/mesh/generated/meshtastic/mesh.pb.cpp index 8c8b9ded7..a0c1e2e73 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.cpp +++ b/src/mesh/generated/meshtastic/mesh.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #include "meshtastic/mesh.pb.h" #if PB_PROTO_HEADER_VERSION != 40 @@ -90,4 +90,13 @@ PB_BIND(meshtastic_ChunkedPayloadResponse, meshtastic_ChunkedPayloadResponse, AU + + + + + + + + + diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 9d7ff74a1..fb154e9d5 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #ifndef PB_MESHTASTIC_MESHTASTIC_MESH_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_MESH_PB_H_INCLUDED @@ -71,6 +71,8 @@ typedef enum _meshtastic_HardwareModel { meshtastic_HardwareModel_RAK2560 = 22, /* Heltec HRU-3601: https://heltec.org/project/hru-3601/ */ meshtastic_HardwareModel_HELTEC_HRU_3601 = 23, + /* Heltec Wireless Bridge */ + meshtastic_HardwareModel_HELTEC_WIRELESS_BRIDGE = 24, /* B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station */ meshtastic_HardwareModel_STATION_G1 = 25, /* RAK11310 (RP2040 + SX1262) */ @@ -107,7 +109,7 @@ typedef enum _meshtastic_HardwareModel { meshtastic_HardwareModel_NRF52840_PCA10059 = 40, /* Custom Disaster Radio esp32 v3 device https://github.com/sudomesh/disaster-radio/tree/master/hardware/board_esp32_v3 */ meshtastic_HardwareModel_DR_DEV = 41, - /* M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, Paper) https://m5stack.com/ */ + /* M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, CoreS3, Paper) https://m5stack.com/ */ meshtastic_HardwareModel_M5STACK = 42, /* New Heltec LoRA32 with ESP32-S3 CPU */ meshtastic_HardwareModel_HELTEC_V3 = 43, @@ -196,9 +198,19 @@ typedef enum _meshtastic_HardwareModel { https://www.adafruit.com/product/938 ^^^ short A0 to switch to I2C address 0x3C */ meshtastic_HardwareModel_RP2040_FEATHER_RFM95 = 76, - /* M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, Paper) https://m5stack.com/ */ + /* M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, CoreS3, Paper) https://m5stack.com/ */ meshtastic_HardwareModel_M5STACK_COREBASIC = 77, meshtastic_HardwareModel_M5STACK_CORE2 = 78, + /* Pico2 with Waveshare Hat, same as Pico */ + meshtastic_HardwareModel_RPI_PICO2 = 79, + /* M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, CoreS3, Paper) https://m5stack.com/ */ + meshtastic_HardwareModel_M5STACK_CORES3 = 80, + /* Seeed XIAO S3 DK */ + meshtastic_HardwareModel_SEEED_XIAO_S3 = 81, + /* Nordic nRF52840+Semtech SX1262 LoRa BLE Combo Module. nRF52840+SX1262 MS24SF1 */ + meshtastic_HardwareModel_MS24SF1 = 82, + /* Lilygo TLora-C6 with the new ESP32-C6 MCU */ + meshtastic_HardwareModel_TLORA_C6 = 83, /* ------------------------------------------------------------------------------------------------------------------------------------------ Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. ------------------------------------------------------------------------------------------------------------------------------------------ */ @@ -316,7 +328,11 @@ typedef enum _meshtastic_Routing_Error { /* The client specified a PKI transport, but the node was unable to send the packet using PKI (and did not send the message at all) */ meshtastic_Routing_Error_PKI_FAILED = 34, /* The receiving node does not have a Public Key to decode with */ - meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY = 35 + meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY = 35, + /* Admin packet otherwise checks out, but uses a bogus or expired session key */ + meshtastic_Routing_Error_ADMIN_BAD_SESSION_KEY = 36, + /* Admin packet sent using PKC, but not from a public key on the admin key list */ + meshtastic_Routing_Error_ADMIN_PUBLIC_KEY_UNAUTHORIZED = 37 } meshtastic_Routing_Error; /* The priority of this message for sending. @@ -580,6 +596,9 @@ typedef struct _meshtastic_Data { /* Defaults to false. If true, then what is in the payload should be treated as an emoji like giving a message a heart or poop emoji. */ uint32_t emoji; + /* Bitfield for extra flags. First use is to indicate that user approves the packet being uploaded to MQTT. */ + bool has_bitfield; + uint8_t bitfield; } meshtastic_Data; /* Waypoint message, used to share arbitrary locations across the mesh */ @@ -738,6 +757,7 @@ typedef struct _meshtastic_NodeInfo { /* True if we witnessed the node over MQTT instead of LoRA transport */ bool via_mqtt; /* Number of hops away from us this node is (0 if adjacent) */ + bool has_hops_away; uint8_t hops_away; /* True if node is in our favorites list Persists between NodeDB internal clean ups */ @@ -868,6 +888,8 @@ typedef struct _meshtastic_DeviceMetadata { meshtastic_HardwareModel hw_model; /* Has Remote Hardware enabled */ bool hasRemoteHardware; + /* Has PKC capabilities */ + bool hasPKC; } meshtastic_DeviceMetadata; /* Packets from the radio to the phone will appear on the fromRadio characteristic. @@ -1023,8 +1045,8 @@ extern "C" { #define _meshtastic_Position_AltSource_ARRAYSIZE ((meshtastic_Position_AltSource)(meshtastic_Position_AltSource_ALT_BAROMETRIC+1)) #define _meshtastic_Routing_Error_MIN meshtastic_Routing_Error_NONE -#define _meshtastic_Routing_Error_MAX meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY -#define _meshtastic_Routing_Error_ARRAYSIZE ((meshtastic_Routing_Error)(meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY+1)) +#define _meshtastic_Routing_Error_MAX meshtastic_Routing_Error_ADMIN_PUBLIC_KEY_UNAUTHORIZED +#define _meshtastic_Routing_Error_ARRAYSIZE ((meshtastic_Routing_Error)(meshtastic_Routing_Error_ADMIN_PUBLIC_KEY_UNAUTHORIZED+1)) #define _meshtastic_MeshPacket_Priority_MIN meshtastic_MeshPacket_Priority_UNSET #define _meshtastic_MeshPacket_Priority_MAX meshtastic_MeshPacket_Priority_MAX @@ -1082,11 +1104,11 @@ extern "C" { #define meshtastic_User_init_default {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}} #define meshtastic_RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}} #define meshtastic_Routing_init_default {0, {meshtastic_RouteDiscovery_init_default}} -#define meshtastic_Data_init_default {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0} +#define meshtastic_Data_init_default {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0, false, 0} #define meshtastic_Waypoint_init_default {0, false, 0, false, 0, 0, 0, "", "", 0} #define meshtastic_MqttClientProxyMessage_init_default {"", 0, {{0, {0}}}, 0} #define meshtastic_MeshPacket_init_default {0, 0, 0, 0, {meshtastic_Data_init_default}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0} -#define meshtastic_NodeInfo_init_default {0, false, meshtastic_User_init_default, false, meshtastic_Position_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, 0, 0} +#define meshtastic_NodeInfo_init_default {0, false, meshtastic_User_init_default, false, meshtastic_Position_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, false, 0, 0} #define meshtastic_MyNodeInfo_init_default {0, 0, 0} #define meshtastic_LogRecord_init_default {"", 0, "", _meshtastic_LogRecord_Level_MIN} #define meshtastic_QueueStatus_init_default {0, 0, 0, 0} @@ -1097,7 +1119,7 @@ extern "C" { #define meshtastic_Compressed_init_default {_meshtastic_PortNum_MIN, {0, {0}}} #define meshtastic_NeighborInfo_init_default {0, 0, 0, 0, {meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default}} #define meshtastic_Neighbor_init_default {0, 0, 0, 0} -#define meshtastic_DeviceMetadata_init_default {"", 0, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_Role_MIN, 0, _meshtastic_HardwareModel_MIN, 0} +#define meshtastic_DeviceMetadata_init_default {"", 0, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_Role_MIN, 0, _meshtastic_HardwareModel_MIN, 0, 0} #define meshtastic_Heartbeat_init_default {0} #define meshtastic_NodeRemoteHardwarePin_init_default {0, false, meshtastic_RemoteHardwarePin_init_default} #define meshtastic_ChunkedPayload_init_default {0, 0, 0, {0, {0}}} @@ -1107,11 +1129,11 @@ extern "C" { #define meshtastic_User_init_zero {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}} #define meshtastic_RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}} #define meshtastic_Routing_init_zero {0, {meshtastic_RouteDiscovery_init_zero}} -#define meshtastic_Data_init_zero {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0} +#define meshtastic_Data_init_zero {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0, false, 0} #define meshtastic_Waypoint_init_zero {0, false, 0, false, 0, 0, 0, "", "", 0} #define meshtastic_MqttClientProxyMessage_init_zero {"", 0, {{0, {0}}}, 0} #define meshtastic_MeshPacket_init_zero {0, 0, 0, 0, {meshtastic_Data_init_zero}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0} -#define meshtastic_NodeInfo_init_zero {0, false, meshtastic_User_init_zero, false, meshtastic_Position_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, 0, 0} +#define meshtastic_NodeInfo_init_zero {0, false, meshtastic_User_init_zero, false, meshtastic_Position_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, false, 0, 0} #define meshtastic_MyNodeInfo_init_zero {0, 0, 0} #define meshtastic_LogRecord_init_zero {"", 0, "", _meshtastic_LogRecord_Level_MIN} #define meshtastic_QueueStatus_init_zero {0, 0, 0, 0} @@ -1122,7 +1144,7 @@ extern "C" { #define meshtastic_Compressed_init_zero {_meshtastic_PortNum_MIN, {0, {0}}} #define meshtastic_NeighborInfo_init_zero {0, 0, 0, 0, {meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero}} #define meshtastic_Neighbor_init_zero {0, 0, 0, 0} -#define meshtastic_DeviceMetadata_init_zero {"", 0, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_Role_MIN, 0, _meshtastic_HardwareModel_MIN, 0} +#define meshtastic_DeviceMetadata_init_zero {"", 0, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_Role_MIN, 0, _meshtastic_HardwareModel_MIN, 0, 0} #define meshtastic_Heartbeat_init_zero {0} #define meshtastic_NodeRemoteHardwarePin_init_zero {0, false, meshtastic_RemoteHardwarePin_init_zero} #define meshtastic_ChunkedPayload_init_zero {0, 0, 0, {0, {0}}} @@ -1176,6 +1198,7 @@ extern "C" { #define meshtastic_Data_request_id_tag 6 #define meshtastic_Data_reply_id_tag 7 #define meshtastic_Data_emoji_tag 8 +#define meshtastic_Data_bitfield_tag 9 #define meshtastic_Waypoint_id_tag 1 #define meshtastic_Waypoint_latitude_i_tag 2 #define meshtastic_Waypoint_longitude_i_tag 3 @@ -1252,6 +1275,7 @@ extern "C" { #define meshtastic_DeviceMetadata_position_flags_tag 8 #define meshtastic_DeviceMetadata_hw_model_tag 9 #define meshtastic_DeviceMetadata_hasRemoteHardware_tag 10 +#define meshtastic_DeviceMetadata_hasPKC_tag 11 #define meshtastic_FromRadio_id_tag 1 #define meshtastic_FromRadio_packet_tag 2 #define meshtastic_FromRadio_my_info_tag 3 @@ -1351,7 +1375,8 @@ X(a, STATIC, SINGULAR, FIXED32, dest, 4) \ X(a, STATIC, SINGULAR, FIXED32, source, 5) \ X(a, STATIC, SINGULAR, FIXED32, request_id, 6) \ X(a, STATIC, SINGULAR, FIXED32, reply_id, 7) \ -X(a, STATIC, SINGULAR, FIXED32, emoji, 8) +X(a, STATIC, SINGULAR, FIXED32, emoji, 8) \ +X(a, STATIC, OPTIONAL, UINT32, bitfield, 9) #define meshtastic_Data_CALLBACK NULL #define meshtastic_Data_DEFAULT NULL @@ -1406,7 +1431,7 @@ X(a, STATIC, SINGULAR, FIXED32, last_heard, 5) \ X(a, STATIC, OPTIONAL, MESSAGE, device_metrics, 6) \ X(a, STATIC, SINGULAR, UINT32, channel, 7) \ X(a, STATIC, SINGULAR, BOOL, via_mqtt, 8) \ -X(a, STATIC, SINGULAR, UINT32, hops_away, 9) \ +X(a, STATIC, OPTIONAL, UINT32, hops_away, 9) \ X(a, STATIC, SINGULAR, BOOL, is_favorite, 10) #define meshtastic_NodeInfo_CALLBACK NULL #define meshtastic_NodeInfo_DEFAULT NULL @@ -1531,7 +1556,8 @@ X(a, STATIC, SINGULAR, BOOL, hasEthernet, 6) \ X(a, STATIC, SINGULAR, UENUM, role, 7) \ X(a, STATIC, SINGULAR, UINT32, position_flags, 8) \ X(a, STATIC, SINGULAR, UENUM, hw_model, 9) \ -X(a, STATIC, SINGULAR, BOOL, hasRemoteHardware, 10) +X(a, STATIC, SINGULAR, BOOL, hasRemoteHardware, 10) \ +X(a, STATIC, SINGULAR, BOOL, hasPKC, 11) #define meshtastic_DeviceMetadata_CALLBACK NULL #define meshtastic_DeviceMetadata_DEFAULT NULL @@ -1629,13 +1655,13 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg; #define meshtastic_ChunkedPayload_size 245 #define meshtastic_ClientNotification_size 415 #define meshtastic_Compressed_size 243 -#define meshtastic_Data_size 270 -#define meshtastic_DeviceMetadata_size 46 +#define meshtastic_Data_size 273 +#define meshtastic_DeviceMetadata_size 48 #define meshtastic_FileInfo_size 236 #define meshtastic_FromRadio_size 510 #define meshtastic_Heartbeat_size 0 #define meshtastic_LogRecord_size 426 -#define meshtastic_MeshPacket_size 364 +#define meshtastic_MeshPacket_size 367 #define meshtastic_MqttClientProxyMessage_size 501 #define meshtastic_MyNodeInfo_size 18 #define meshtastic_NeighborInfo_size 258 diff --git a/src/mesh/generated/meshtastic/module_config.pb.cpp b/src/mesh/generated/meshtastic/module_config.pb.cpp index 88a771d5b..c40041eab 100644 --- a/src/mesh/generated/meshtastic/module_config.pb.cpp +++ b/src/mesh/generated/meshtastic/module_config.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #include "meshtastic/module_config.pb.h" #if PB_PROTO_HEADER_VERSION != 40 @@ -60,3 +60,10 @@ PB_BIND(meshtastic_RemoteHardwarePin, meshtastic_RemoteHardwarePin, AUTO) + + + + + + + diff --git a/src/mesh/generated/meshtastic/module_config.pb.h b/src/mesh/generated/meshtastic/module_config.pb.h index 2e1985660..32d5ded23 100644 --- a/src/mesh/generated/meshtastic/module_config.pb.h +++ b/src/mesh/generated/meshtastic/module_config.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #ifndef PB_MESHTASTIC_MESHTASTIC_MODULE_CONFIG_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_MODULE_CONFIG_PB_H_INCLUDED @@ -19,6 +19,23 @@ typedef enum _meshtastic_RemoteHardwarePinType { meshtastic_RemoteHardwarePinType_DIGITAL_WRITE = 2 } meshtastic_RemoteHardwarePinType; +typedef enum _meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType { + /* Event is triggered if pin is low */ + meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_LOGIC_LOW = 0, + /* Event is triggered if pin is high */ + meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_LOGIC_HIGH = 1, + /* Event is triggered when pin goes high to low */ + meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_FALLING_EDGE = 2, + /* Event is triggered when pin goes low to high */ + meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_RISING_EDGE = 3, + /* Event is triggered on every pin state change, low is considered to be + "active" */ + meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_EITHER_EDGE_ACTIVE_LOW = 4, + /* Event is triggered on every pin state change, high is considered to be + "active" */ + meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_EITHER_EDGE_ACTIVE_HIGH = 5 +} meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType; + /* Baudrate for codec2 voice */ typedef enum _meshtastic_ModuleConfig_AudioConfig_Audio_Baud { meshtastic_ModuleConfig_AudioConfig_Audio_Baud_CODEC2_DEFAULT = 0, @@ -144,11 +161,13 @@ typedef struct _meshtastic_ModuleConfig_NeighborInfoConfig { typedef struct _meshtastic_ModuleConfig_DetectionSensorConfig { /* Whether the Module is enabled */ bool enabled; - /* Interval in seconds of how often we can send a message to the mesh when a state change is detected */ + /* Interval in seconds of how often we can send a message to the mesh when a + trigger event is detected */ uint32_t minimum_broadcast_secs; - /* Interval in seconds of how often we should send a message to the mesh with the current state regardless of changes - When set to 0, only state changes will be broadcasted - Works as a sort of status heartbeat for peace of mind */ + /* Interval in seconds of how often we should send a message to the mesh + with the current state regardless of trigger events When set to 0, only + trigger events will be broadcasted Works as a sort of status heartbeat + for peace of mind */ uint32_t state_broadcast_secs; /* Send ASCII bell with alert message Useful for triggering ext. notification on bell */ @@ -159,9 +178,8 @@ typedef struct _meshtastic_ModuleConfig_DetectionSensorConfig { char name[20]; /* GPIO pin to monitor for state changes */ uint8_t monitor_pin; - /* Whether or not the GPIO pin state detection is triggered on HIGH (1) - Otherwise LOW (0) */ - bool detection_triggered_high; + /* The type of trigger event to be used */ + meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType detection_trigger_type; /* Whether or not use INPUT_PULLUP mode for GPIO pin Only applicable if the board uses pull-up resistors on the pin */ bool use_pullup; @@ -309,15 +327,21 @@ typedef struct _meshtastic_ModuleConfig_TelemetryConfig { /* Interval in seconds of how often we should try to send our air quality metrics to the mesh */ uint32_t air_quality_interval; - /* Interval in seconds of how often we should try to send our - air quality metrics to the mesh */ + /* Enable/disable Power metrics */ bool power_measurement_enabled; /* Interval in seconds of how often we should try to send our - air quality metrics to the mesh */ + power metrics to the mesh */ uint32_t power_update_interval; - /* Interval in seconds of how often we should try to send our - air quality metrics to the mesh */ + /* Enable/Disable the power measurement module on-device display */ bool power_screen_enabled; + /* Preferences for the (Health) Telemetry Module + Enable/Disable the telemetry measurement module measurement collection */ + bool health_measurement_enabled; + /* Interval in seconds of how often we should try to send our + health metrics to the mesh */ + uint32_t health_update_interval; + /* Enable/Disable the health telemetry module on-device display */ + bool health_screen_enabled; } meshtastic_ModuleConfig_TelemetryConfig; /* TODO: REPLACE */ @@ -427,6 +451,10 @@ extern "C" { #define _meshtastic_RemoteHardwarePinType_MAX meshtastic_RemoteHardwarePinType_DIGITAL_WRITE #define _meshtastic_RemoteHardwarePinType_ARRAYSIZE ((meshtastic_RemoteHardwarePinType)(meshtastic_RemoteHardwarePinType_DIGITAL_WRITE+1)) +#define _meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_MIN meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_LOGIC_LOW +#define _meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_MAX meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_EITHER_EDGE_ACTIVE_HIGH +#define _meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_ARRAYSIZE ((meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType)(meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_EITHER_EDGE_ACTIVE_HIGH+1)) + #define _meshtastic_ModuleConfig_AudioConfig_Audio_Baud_MIN meshtastic_ModuleConfig_AudioConfig_Audio_Baud_CODEC2_DEFAULT #define _meshtastic_ModuleConfig_AudioConfig_Audio_Baud_MAX meshtastic_ModuleConfig_AudioConfig_Audio_Baud_CODEC2_700B #define _meshtastic_ModuleConfig_AudioConfig_Audio_Baud_ARRAYSIZE ((meshtastic_ModuleConfig_AudioConfig_Audio_Baud)(meshtastic_ModuleConfig_AudioConfig_Audio_Baud_CODEC2_700B+1)) @@ -448,6 +476,7 @@ extern "C" { +#define meshtastic_ModuleConfig_DetectionSensorConfig_detection_trigger_type_ENUMTYPE meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType #define meshtastic_ModuleConfig_AudioConfig_bitrate_ENUMTYPE meshtastic_ModuleConfig_AudioConfig_Audio_Baud @@ -473,14 +502,14 @@ extern "C" { #define meshtastic_ModuleConfig_MapReportSettings_init_default {0, 0} #define meshtastic_ModuleConfig_RemoteHardwareConfig_init_default {0, 0, 0, {meshtastic_RemoteHardwarePin_init_default, meshtastic_RemoteHardwarePin_init_default, meshtastic_RemoteHardwarePin_init_default, meshtastic_RemoteHardwarePin_init_default}} #define meshtastic_ModuleConfig_NeighborInfoConfig_init_default {0, 0} -#define meshtastic_ModuleConfig_DetectionSensorConfig_init_default {0, 0, 0, 0, "", 0, 0, 0} +#define meshtastic_ModuleConfig_DetectionSensorConfig_init_default {0, 0, 0, 0, "", 0, _meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_MIN, 0} #define meshtastic_ModuleConfig_AudioConfig_init_default {0, 0, _meshtastic_ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0} #define meshtastic_ModuleConfig_PaxcounterConfig_init_default {0, 0, 0, 0} #define meshtastic_ModuleConfig_SerialConfig_init_default {0, 0, 0, 0, _meshtastic_ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MIN, 0} #define meshtastic_ModuleConfig_ExternalNotificationConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_ModuleConfig_StoreForwardConfig_init_default {0, 0, 0, 0, 0, 0} #define meshtastic_ModuleConfig_RangeTestConfig_init_default {0, 0, 0} -#define meshtastic_ModuleConfig_TelemetryConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +#define meshtastic_ModuleConfig_TelemetryConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_ModuleConfig_CannedMessageConfig_init_default {0, 0, 0, 0, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, 0, 0, "", 0} #define meshtastic_ModuleConfig_AmbientLightingConfig_init_default {0, 0, 0, 0, 0} #define meshtastic_RemoteHardwarePin_init_default {0, "", _meshtastic_RemoteHardwarePinType_MIN} @@ -489,14 +518,14 @@ extern "C" { #define meshtastic_ModuleConfig_MapReportSettings_init_zero {0, 0} #define meshtastic_ModuleConfig_RemoteHardwareConfig_init_zero {0, 0, 0, {meshtastic_RemoteHardwarePin_init_zero, meshtastic_RemoteHardwarePin_init_zero, meshtastic_RemoteHardwarePin_init_zero, meshtastic_RemoteHardwarePin_init_zero}} #define meshtastic_ModuleConfig_NeighborInfoConfig_init_zero {0, 0} -#define meshtastic_ModuleConfig_DetectionSensorConfig_init_zero {0, 0, 0, 0, "", 0, 0, 0} +#define meshtastic_ModuleConfig_DetectionSensorConfig_init_zero {0, 0, 0, 0, "", 0, _meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_MIN, 0} #define meshtastic_ModuleConfig_AudioConfig_init_zero {0, 0, _meshtastic_ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0} #define meshtastic_ModuleConfig_PaxcounterConfig_init_zero {0, 0, 0, 0} #define meshtastic_ModuleConfig_SerialConfig_init_zero {0, 0, 0, 0, _meshtastic_ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MIN, 0} #define meshtastic_ModuleConfig_ExternalNotificationConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_ModuleConfig_StoreForwardConfig_init_zero {0, 0, 0, 0, 0, 0} #define meshtastic_ModuleConfig_RangeTestConfig_init_zero {0, 0, 0} -#define meshtastic_ModuleConfig_TelemetryConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +#define meshtastic_ModuleConfig_TelemetryConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_ModuleConfig_CannedMessageConfig_init_zero {0, 0, 0, 0, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, 0, 0, "", 0} #define meshtastic_ModuleConfig_AmbientLightingConfig_init_zero {0, 0, 0, 0, 0} #define meshtastic_RemoteHardwarePin_init_zero {0, "", _meshtastic_RemoteHardwarePinType_MIN} @@ -523,7 +552,7 @@ extern "C" { #define meshtastic_ModuleConfig_DetectionSensorConfig_send_bell_tag 4 #define meshtastic_ModuleConfig_DetectionSensorConfig_name_tag 5 #define meshtastic_ModuleConfig_DetectionSensorConfig_monitor_pin_tag 6 -#define meshtastic_ModuleConfig_DetectionSensorConfig_detection_triggered_high_tag 7 +#define meshtastic_ModuleConfig_DetectionSensorConfig_detection_trigger_type_tag 7 #define meshtastic_ModuleConfig_DetectionSensorConfig_use_pullup_tag 8 #define meshtastic_ModuleConfig_AudioConfig_codec2_enabled_tag 1 #define meshtastic_ModuleConfig_AudioConfig_ptt_pin_tag 2 @@ -578,6 +607,9 @@ extern "C" { #define meshtastic_ModuleConfig_TelemetryConfig_power_measurement_enabled_tag 8 #define meshtastic_ModuleConfig_TelemetryConfig_power_update_interval_tag 9 #define meshtastic_ModuleConfig_TelemetryConfig_power_screen_enabled_tag 10 +#define meshtastic_ModuleConfig_TelemetryConfig_health_measurement_enabled_tag 11 +#define meshtastic_ModuleConfig_TelemetryConfig_health_update_interval_tag 12 +#define meshtastic_ModuleConfig_TelemetryConfig_health_screen_enabled_tag 13 #define meshtastic_ModuleConfig_CannedMessageConfig_rotary1_enabled_tag 1 #define meshtastic_ModuleConfig_CannedMessageConfig_inputbroker_pin_a_tag 2 #define meshtastic_ModuleConfig_CannedMessageConfig_inputbroker_pin_b_tag 3 @@ -688,7 +720,7 @@ X(a, STATIC, SINGULAR, UINT32, state_broadcast_secs, 3) \ X(a, STATIC, SINGULAR, BOOL, send_bell, 4) \ X(a, STATIC, SINGULAR, STRING, name, 5) \ X(a, STATIC, SINGULAR, UINT32, monitor_pin, 6) \ -X(a, STATIC, SINGULAR, BOOL, detection_triggered_high, 7) \ +X(a, STATIC, SINGULAR, UENUM, detection_trigger_type, 7) \ X(a, STATIC, SINGULAR, BOOL, use_pullup, 8) #define meshtastic_ModuleConfig_DetectionSensorConfig_CALLBACK NULL #define meshtastic_ModuleConfig_DetectionSensorConfig_DEFAULT NULL @@ -770,7 +802,10 @@ X(a, STATIC, SINGULAR, BOOL, air_quality_enabled, 6) \ X(a, STATIC, SINGULAR, UINT32, air_quality_interval, 7) \ X(a, STATIC, SINGULAR, BOOL, power_measurement_enabled, 8) \ X(a, STATIC, SINGULAR, UINT32, power_update_interval, 9) \ -X(a, STATIC, SINGULAR, BOOL, power_screen_enabled, 10) +X(a, STATIC, SINGULAR, BOOL, power_screen_enabled, 10) \ +X(a, STATIC, SINGULAR, BOOL, health_measurement_enabled, 11) \ +X(a, STATIC, SINGULAR, UINT32, health_update_interval, 12) \ +X(a, STATIC, SINGULAR, BOOL, health_screen_enabled, 13) #define meshtastic_ModuleConfig_TelemetryConfig_CALLBACK NULL #define meshtastic_ModuleConfig_TelemetryConfig_DEFAULT NULL @@ -855,7 +890,7 @@ extern const pb_msgdesc_t meshtastic_RemoteHardwarePin_msg; #define meshtastic_ModuleConfig_RemoteHardwareConfig_size 96 #define meshtastic_ModuleConfig_SerialConfig_size 28 #define meshtastic_ModuleConfig_StoreForwardConfig_size 24 -#define meshtastic_ModuleConfig_TelemetryConfig_size 36 +#define meshtastic_ModuleConfig_TelemetryConfig_size 46 #define meshtastic_ModuleConfig_size 257 #define meshtastic_RemoteHardwarePin_size 21 diff --git a/src/mesh/generated/meshtastic/mqtt.pb.cpp b/src/mesh/generated/meshtastic/mqtt.pb.cpp index f00dd823b..74536cb79 100644 --- a/src/mesh/generated/meshtastic/mqtt.pb.cpp +++ b/src/mesh/generated/meshtastic/mqtt.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #include "meshtastic/mqtt.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/mqtt.pb.h b/src/mesh/generated/meshtastic/mqtt.pb.h index 8ec9f98c3..4d1027374 100644 --- a/src/mesh/generated/meshtastic/mqtt.pb.h +++ b/src/mesh/generated/meshtastic/mqtt.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #ifndef PB_MESHTASTIC_MESHTASTIC_MQTT_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_MQTT_PB_H_INCLUDED diff --git a/src/mesh/generated/meshtastic/paxcount.pb.cpp b/src/mesh/generated/meshtastic/paxcount.pb.cpp index 67f07a31b..403288147 100644 --- a/src/mesh/generated/meshtastic/paxcount.pb.cpp +++ b/src/mesh/generated/meshtastic/paxcount.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #include "meshtastic/paxcount.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/paxcount.pb.h b/src/mesh/generated/meshtastic/paxcount.pb.h index 09377d833..b6b51fdd5 100644 --- a/src/mesh/generated/meshtastic/paxcount.pb.h +++ b/src/mesh/generated/meshtastic/paxcount.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #ifndef PB_MESHTASTIC_MESHTASTIC_PAXCOUNT_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_PAXCOUNT_PB_H_INCLUDED diff --git a/src/mesh/generated/meshtastic/portnums.pb.cpp b/src/mesh/generated/meshtastic/portnums.pb.cpp index 8f32c0851..8fca9af79 100644 --- a/src/mesh/generated/meshtastic/portnums.pb.cpp +++ b/src/mesh/generated/meshtastic/portnums.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #include "meshtastic/portnums.pb.h" #if PB_PROTO_HEADER_VERSION != 40 @@ -8,3 +8,4 @@ + diff --git a/src/mesh/generated/meshtastic/portnums.pb.h b/src/mesh/generated/meshtastic/portnums.pb.h index b9e537ddf..df6cf32c2 100644 --- a/src/mesh/generated/meshtastic/portnums.pb.h +++ b/src/mesh/generated/meshtastic/portnums.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #ifndef PB_MESHTASTIC_MESHTASTIC_PORTNUMS_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_PORTNUMS_PB_H_INCLUDED diff --git a/src/mesh/generated/meshtastic/powermon.pb.cpp b/src/mesh/generated/meshtastic/powermon.pb.cpp index ce41ea021..6a9b7551a 100644 --- a/src/mesh/generated/meshtastic/powermon.pb.cpp +++ b/src/mesh/generated/meshtastic/powermon.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #include "meshtastic/powermon.pb.h" #if PB_PROTO_HEADER_VERSION != 40 @@ -15,3 +15,5 @@ PB_BIND(meshtastic_PowerStressMessage, meshtastic_PowerStressMessage, AUTO) + + diff --git a/src/mesh/generated/meshtastic/powermon.pb.h b/src/mesh/generated/meshtastic/powermon.pb.h index 7de0618e9..5add85b85 100644 --- a/src/mesh/generated/meshtastic/powermon.pb.h +++ b/src/mesh/generated/meshtastic/powermon.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #ifndef PB_MESHTASTIC_MESHTASTIC_POWERMON_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_POWERMON_PB_H_INCLUDED diff --git a/src/mesh/generated/meshtastic/remote_hardware.pb.cpp b/src/mesh/generated/meshtastic/remote_hardware.pb.cpp index 4a23698b2..239950e7e 100644 --- a/src/mesh/generated/meshtastic/remote_hardware.pb.cpp +++ b/src/mesh/generated/meshtastic/remote_hardware.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #include "meshtastic/remote_hardware.pb.h" #if PB_PROTO_HEADER_VERSION != 40 @@ -11,3 +11,4 @@ PB_BIND(meshtastic_HardwareMessage, meshtastic_HardwareMessage, AUTO) + diff --git a/src/mesh/generated/meshtastic/remote_hardware.pb.h b/src/mesh/generated/meshtastic/remote_hardware.pb.h index 936034b62..ade250e93 100644 --- a/src/mesh/generated/meshtastic/remote_hardware.pb.h +++ b/src/mesh/generated/meshtastic/remote_hardware.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #ifndef PB_MESHTASTIC_MESHTASTIC_REMOTE_HARDWARE_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_REMOTE_HARDWARE_PB_H_INCLUDED diff --git a/src/mesh/generated/meshtastic/rtttl.pb.cpp b/src/mesh/generated/meshtastic/rtttl.pb.cpp index 8367fdbce..61ad8b73f 100644 --- a/src/mesh/generated/meshtastic/rtttl.pb.cpp +++ b/src/mesh/generated/meshtastic/rtttl.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #include "meshtastic/rtttl.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/meshtastic/rtttl.pb.h b/src/mesh/generated/meshtastic/rtttl.pb.h index 452b0cf4b..2b7e35f11 100644 --- a/src/mesh/generated/meshtastic/rtttl.pb.h +++ b/src/mesh/generated/meshtastic/rtttl.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #ifndef PB_MESHTASTIC_MESHTASTIC_RTTTL_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_RTTTL_PB_H_INCLUDED diff --git a/src/mesh/generated/meshtastic/storeforward.pb.cpp b/src/mesh/generated/meshtastic/storeforward.pb.cpp index 5b3fadd9a..71a232bf6 100644 --- a/src/mesh/generated/meshtastic/storeforward.pb.cpp +++ b/src/mesh/generated/meshtastic/storeforward.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #include "meshtastic/storeforward.pb.h" #if PB_PROTO_HEADER_VERSION != 40 @@ -20,3 +20,4 @@ PB_BIND(meshtastic_StoreAndForward_Heartbeat, meshtastic_StoreAndForward_Heartbe + diff --git a/src/mesh/generated/meshtastic/storeforward.pb.h b/src/mesh/generated/meshtastic/storeforward.pb.h index 311596c7f..71f2fcad5 100644 --- a/src/mesh/generated/meshtastic/storeforward.pb.h +++ b/src/mesh/generated/meshtastic/storeforward.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #ifndef PB_MESHTASTIC_MESHTASTIC_STOREFORWARD_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_STOREFORWARD_PB_H_INCLUDED diff --git a/src/mesh/generated/meshtastic/telemetry.pb.cpp b/src/mesh/generated/meshtastic/telemetry.pb.cpp index 90859c98e..f6d39da6e 100644 --- a/src/mesh/generated/meshtastic/telemetry.pb.cpp +++ b/src/mesh/generated/meshtastic/telemetry.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #include "meshtastic/telemetry.pb.h" #if PB_PROTO_HEADER_VERSION != 40 @@ -21,6 +21,9 @@ PB_BIND(meshtastic_AirQualityMetrics, meshtastic_AirQualityMetrics, AUTO) PB_BIND(meshtastic_LocalStats, meshtastic_LocalStats, AUTO) +PB_BIND(meshtastic_HealthMetrics, meshtastic_HealthMetrics, AUTO) + + PB_BIND(meshtastic_Telemetry, meshtastic_Telemetry, AUTO) @@ -29,3 +32,4 @@ PB_BIND(meshtastic_Nau7802Config, meshtastic_Nau7802Config, AUTO) + diff --git a/src/mesh/generated/meshtastic/telemetry.pb.h b/src/mesh/generated/meshtastic/telemetry.pb.h index cedc2867e..a33988129 100644 --- a/src/mesh/generated/meshtastic/telemetry.pb.h +++ b/src/mesh/generated/meshtastic/telemetry.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #ifndef PB_MESHTASTIC_MESHTASTIC_TELEMETRY_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_TELEMETRY_PB_H_INCLUDED @@ -71,7 +71,11 @@ typedef enum _meshtastic_TelemetrySensorType { /* MAX17048 1S lipo battery sensor (voltage, state of charge, time to go) */ meshtastic_TelemetrySensorType_MAX17048 = 28, /* Custom I2C sensor implementation based on https://github.com/meshtastic/i2c-sensor */ - meshtastic_TelemetrySensorType_CUSTOM_SENSOR = 29 + meshtastic_TelemetrySensorType_CUSTOM_SENSOR = 29, + /* MAX30102 Pulse Oximeter and Heart-Rate Sensor */ + meshtastic_TelemetrySensorType_MAX30102 = 30, + /* MLX90614 non-contact IR temperature sensor. */ + meshtastic_TelemetrySensorType_MLX90614 = 31 } meshtastic_TelemetrySensorType; /* Struct definitions */ @@ -223,7 +227,7 @@ typedef struct _meshtastic_LocalStats { float air_util_tx; /* Number of packets sent */ uint32_t num_packets_tx; - /* Number of packets received good */ + /* Number of packets received (both good and bad) */ uint32_t num_packets_rx; /* Number of packets received that are malformed or violate the protocol */ uint32_t num_packets_rx_bad; @@ -231,8 +235,29 @@ typedef struct _meshtastic_LocalStats { uint16_t num_online_nodes; /* Number of nodes total */ uint16_t num_total_nodes; + /* Number of received packets that were duplicates (due to multiple nodes relaying). + If this number is high, there are nodes in the mesh relaying packets when it's unnecessary, for example due to the ROUTER/REPEATER role. */ + uint32_t num_rx_dupe; + /* Number of packets we transmitted that were a relay for others (not originating from ourselves). */ + uint32_t num_tx_relay; + /* Number of times we canceled a packet to be relayed, because someone else did it before us. + This will always be zero for ROUTERs/REPEATERs. If this number is high, some other node(s) is/are relaying faster than you. */ + uint32_t num_tx_relay_canceled; } meshtastic_LocalStats; +/* Health telemetry metrics */ +typedef struct _meshtastic_HealthMetrics { + /* Heart rate (beats per minute) */ + bool has_heart_bpm; + uint8_t heart_bpm; + /* SpO2 (blood oxygen saturation) level */ + bool has_spO2; + uint8_t spO2; + /* Body temperature in degrees Celsius */ + bool has_temperature; + float temperature; +} meshtastic_HealthMetrics; + /* Types of Measurements the telemetry module is equipped to handle */ typedef struct _meshtastic_Telemetry { /* Seconds since 1970 - or 0 for unknown/unset */ @@ -249,6 +274,8 @@ typedef struct _meshtastic_Telemetry { meshtastic_PowerMetrics power_metrics; /* Local device mesh statistics */ meshtastic_LocalStats local_stats; + /* Health telemetry metrics */ + meshtastic_HealthMetrics health_metrics; } variant; } meshtastic_Telemetry; @@ -267,8 +294,9 @@ extern "C" { /* Helper constants for enums */ #define _meshtastic_TelemetrySensorType_MIN meshtastic_TelemetrySensorType_SENSOR_UNSET -#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_CUSTOM_SENSOR -#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_CUSTOM_SENSOR+1)) +#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_MLX90614 +#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_MLX90614+1)) + @@ -283,14 +311,16 @@ extern "C" { #define meshtastic_EnvironmentMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_PowerMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_AirQualityMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} -#define meshtastic_LocalStats_init_default {0, 0, 0, 0, 0, 0, 0, 0} +#define meshtastic_LocalStats_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +#define meshtastic_HealthMetrics_init_default {false, 0, false, 0, false, 0} #define meshtastic_Telemetry_init_default {0, 0, {meshtastic_DeviceMetrics_init_default}} #define meshtastic_Nau7802Config_init_default {0, 0} #define meshtastic_DeviceMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_EnvironmentMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_PowerMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_AirQualityMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} -#define meshtastic_LocalStats_init_zero {0, 0, 0, 0, 0, 0, 0, 0} +#define meshtastic_LocalStats_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +#define meshtastic_HealthMetrics_init_zero {false, 0, false, 0, false, 0} #define meshtastic_Telemetry_init_zero {0, 0, {meshtastic_DeviceMetrics_init_zero}} #define meshtastic_Nau7802Config_init_zero {0, 0} @@ -343,12 +373,19 @@ extern "C" { #define meshtastic_LocalStats_num_packets_rx_bad_tag 6 #define meshtastic_LocalStats_num_online_nodes_tag 7 #define meshtastic_LocalStats_num_total_nodes_tag 8 +#define meshtastic_LocalStats_num_rx_dupe_tag 9 +#define meshtastic_LocalStats_num_tx_relay_tag 10 +#define meshtastic_LocalStats_num_tx_relay_canceled_tag 11 +#define meshtastic_HealthMetrics_heart_bpm_tag 1 +#define meshtastic_HealthMetrics_spO2_tag 2 +#define meshtastic_HealthMetrics_temperature_tag 3 #define meshtastic_Telemetry_time_tag 1 #define meshtastic_Telemetry_device_metrics_tag 2 #define meshtastic_Telemetry_environment_metrics_tag 3 #define meshtastic_Telemetry_air_quality_metrics_tag 4 #define meshtastic_Telemetry_power_metrics_tag 5 #define meshtastic_Telemetry_local_stats_tag 6 +#define meshtastic_Telemetry_health_metrics_tag 7 #define meshtastic_Nau7802Config_zeroOffset_tag 1 #define meshtastic_Nau7802Config_calibrationFactor_tag 2 @@ -417,17 +454,28 @@ X(a, STATIC, SINGULAR, UINT32, num_packets_tx, 4) \ X(a, STATIC, SINGULAR, UINT32, num_packets_rx, 5) \ X(a, STATIC, SINGULAR, UINT32, num_packets_rx_bad, 6) \ X(a, STATIC, SINGULAR, UINT32, num_online_nodes, 7) \ -X(a, STATIC, SINGULAR, UINT32, num_total_nodes, 8) +X(a, STATIC, SINGULAR, UINT32, num_total_nodes, 8) \ +X(a, STATIC, SINGULAR, UINT32, num_rx_dupe, 9) \ +X(a, STATIC, SINGULAR, UINT32, num_tx_relay, 10) \ +X(a, STATIC, SINGULAR, UINT32, num_tx_relay_canceled, 11) #define meshtastic_LocalStats_CALLBACK NULL #define meshtastic_LocalStats_DEFAULT NULL +#define meshtastic_HealthMetrics_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, UINT32, heart_bpm, 1) \ +X(a, STATIC, OPTIONAL, UINT32, spO2, 2) \ +X(a, STATIC, OPTIONAL, FLOAT, temperature, 3) +#define meshtastic_HealthMetrics_CALLBACK NULL +#define meshtastic_HealthMetrics_DEFAULT NULL + #define meshtastic_Telemetry_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, FIXED32, time, 1) \ X(a, STATIC, ONEOF, MESSAGE, (variant,device_metrics,variant.device_metrics), 2) \ X(a, STATIC, ONEOF, MESSAGE, (variant,environment_metrics,variant.environment_metrics), 3) \ X(a, STATIC, ONEOF, MESSAGE, (variant,air_quality_metrics,variant.air_quality_metrics), 4) \ X(a, STATIC, ONEOF, MESSAGE, (variant,power_metrics,variant.power_metrics), 5) \ -X(a, STATIC, ONEOF, MESSAGE, (variant,local_stats,variant.local_stats), 6) +X(a, STATIC, ONEOF, MESSAGE, (variant,local_stats,variant.local_stats), 6) \ +X(a, STATIC, ONEOF, MESSAGE, (variant,health_metrics,variant.health_metrics), 7) #define meshtastic_Telemetry_CALLBACK NULL #define meshtastic_Telemetry_DEFAULT NULL #define meshtastic_Telemetry_variant_device_metrics_MSGTYPE meshtastic_DeviceMetrics @@ -435,6 +483,7 @@ X(a, STATIC, ONEOF, MESSAGE, (variant,local_stats,variant.local_stats), #define meshtastic_Telemetry_variant_air_quality_metrics_MSGTYPE meshtastic_AirQualityMetrics #define meshtastic_Telemetry_variant_power_metrics_MSGTYPE meshtastic_PowerMetrics #define meshtastic_Telemetry_variant_local_stats_MSGTYPE meshtastic_LocalStats +#define meshtastic_Telemetry_variant_health_metrics_MSGTYPE meshtastic_HealthMetrics #define meshtastic_Nau7802Config_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, INT32, zeroOffset, 1) \ @@ -447,6 +496,7 @@ extern const pb_msgdesc_t meshtastic_EnvironmentMetrics_msg; extern const pb_msgdesc_t meshtastic_PowerMetrics_msg; extern const pb_msgdesc_t meshtastic_AirQualityMetrics_msg; extern const pb_msgdesc_t meshtastic_LocalStats_msg; +extern const pb_msgdesc_t meshtastic_HealthMetrics_msg; extern const pb_msgdesc_t meshtastic_Telemetry_msg; extern const pb_msgdesc_t meshtastic_Nau7802Config_msg; @@ -456,6 +506,7 @@ extern const pb_msgdesc_t meshtastic_Nau7802Config_msg; #define meshtastic_PowerMetrics_fields &meshtastic_PowerMetrics_msg #define meshtastic_AirQualityMetrics_fields &meshtastic_AirQualityMetrics_msg #define meshtastic_LocalStats_fields &meshtastic_LocalStats_msg +#define meshtastic_HealthMetrics_fields &meshtastic_HealthMetrics_msg #define meshtastic_Telemetry_fields &meshtastic_Telemetry_msg #define meshtastic_Nau7802Config_fields &meshtastic_Nau7802Config_msg @@ -464,7 +515,8 @@ extern const pb_msgdesc_t meshtastic_Nau7802Config_msg; #define meshtastic_AirQualityMetrics_size 72 #define meshtastic_DeviceMetrics_size 27 #define meshtastic_EnvironmentMetrics_size 85 -#define meshtastic_LocalStats_size 42 +#define meshtastic_HealthMetrics_size 11 +#define meshtastic_LocalStats_size 60 #define meshtastic_Nau7802Config_size 16 #define meshtastic_PowerMetrics_size 30 #define meshtastic_Telemetry_size 92 diff --git a/src/mesh/generated/meshtastic/xmodem.pb.cpp b/src/mesh/generated/meshtastic/xmodem.pb.cpp index 8e5cde457..3960ccdaa 100644 --- a/src/mesh/generated/meshtastic/xmodem.pb.cpp +++ b/src/mesh/generated/meshtastic/xmodem.pb.cpp @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #include "meshtastic/xmodem.pb.h" #if PB_PROTO_HEADER_VERSION != 40 @@ -11,3 +11,4 @@ PB_BIND(meshtastic_XModem, meshtastic_XModem, AUTO) + diff --git a/src/mesh/generated/meshtastic/xmodem.pb.h b/src/mesh/generated/meshtastic/xmodem.pb.h index 67bd0869f..76edc0df6 100644 --- a/src/mesh/generated/meshtastic/xmodem.pb.h +++ b/src/mesh/generated/meshtastic/xmodem.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ #ifndef PB_MESHTASTIC_MESHTASTIC_XMODEM_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_XMODEM_PB_H_INCLUDED diff --git a/src/mesh/http/ContentHandler.cpp b/src/mesh/http/ContentHandler.cpp index 772b3e821..c5ea86429 100644 --- a/src/mesh/http/ContentHandler.cpp +++ b/src/mesh/http/ContentHandler.cpp @@ -776,7 +776,7 @@ void handleRestart(HTTPRequest *req, HTTPResponse *res) res->println("

Meshtastic

\n"); res->println("Restarting"); - LOG_DEBUG("***** Restarted on HTTP(s) Request *****\n"); + LOG_DEBUG("Restarted on HTTP(s) Request\n"); webServerThread->requestRestart = (millis() / 1000) + 5; } diff --git a/src/mesh/wifi/WiFiAPClient.cpp b/src/mesh/wifi/WiFiAPClient.cpp index e733d1801..c835878d3 100644 --- a/src/mesh/wifi/WiFiAPClient.cpp +++ b/src/mesh/wifi/WiFiAPClient.cpp @@ -15,16 +15,15 @@ #include #ifdef ARCH_ESP32 #if !MESHTASTIC_EXCLUDE_WEBSERVER -#if !MESHTASTIC_EXCLUDE_WEBSERVER #include "mesh/http/WebServer.h" #endif -#endif #include #include static void WiFiEvent(WiFiEvent_t event); #endif #ifndef DISABLE_NTP +#include "Throttle.h" #include #endif @@ -58,7 +57,7 @@ static void onNetworkConnected() { if (!APStartupComplete) { // Start web server - LOG_INFO("Starting network services\n"); + LOG_INFO("Starting WiFi network services\n"); #ifdef ARCH_ESP32 // start mdns @@ -144,7 +143,7 @@ static int32_t reconnectWiFi() } #ifndef DISABLE_NTP - if (WiFi.isConnected() && (((millis() - lastrun_ntp) > 43200000) || (lastrun_ntp == 0))) { // every 12 hours + if (WiFi.isConnected() && (!Throttle::isWithinTimespanMs(lastrun_ntp, 43200000) || (lastrun_ntp == 0))) { // every 12 hours LOG_DEBUG("Updating NTP time from %s\n", config.network.ntp_server); if (timeClient.update()) { LOG_DEBUG("NTP Request Success - Setting RTCQualityNTP if needed\n"); @@ -311,7 +310,12 @@ static void WiFiEvent(WiFiEvent_t event) onNetworkConnected(); break; case ARDUINO_EVENT_WIFI_STA_GOT_IP6: +#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0) + LOG_INFO("Obtained Local IP6 address: %s\n", WiFi.linkLocalIPv6().toString().c_str()); + LOG_INFO("Obtained GlobalIP6 address: %s\n", WiFi.globalIPv6().toString().c_str()); +#else LOG_INFO("Obtained IP6 address: %s\n", WiFi.localIPv6().toString().c_str()); +#endif break; case ARDUINO_EVENT_WIFI_STA_LOST_IP: LOG_INFO("Lost IP address and IP address is reset to 0\n"); diff --git a/src/meshUtils.cpp b/src/meshUtils.cpp index c6f2c69b4..e98a6f1ce 100644 --- a/src/meshUtils.cpp +++ b/src/meshUtils.cpp @@ -60,10 +60,16 @@ char *strnstr(const char *s, const char *find, size_t slen) void printBytes(const char *label, const uint8_t *p, size_t numbytes) { - LOG_DEBUG("%s: ", label); - for (size_t i = 0; i < numbytes; i++) - LOG_DEBUG("%02x ", p[i]); - LOG_DEBUG("\n"); + int labelSize = strlen(label); + if (labelSize < 100 && numbytes < 64) { + char *messageBuffer = new char[labelSize + (numbytes * 3) + 2]; + strncpy(messageBuffer, label, labelSize); + for (size_t i = 0; i < numbytes; i++) + snprintf(messageBuffer + labelSize + i * 3, 4, " %02x", p[i]); + strcpy(messageBuffer + labelSize + numbytes * 3, "\n"); + LOG_DEBUG(messageBuffer); + delete[] messageBuffer; + } } bool memfll(const uint8_t *mem, uint8_t find, size_t numbytes) @@ -73,4 +79,19 @@ bool memfll(const uint8_t *mem, uint8_t find, size_t numbytes) return false; } return true; +} + +bool isOneOf(int item, int count, ...) +{ + va_list args; + va_start(args, count); + bool found = false; + for (int i = 0; i < count; ++i) { + if (item == va_arg(args, int)) { + found = true; + break; + } + } + va_end(args); + return found; } \ No newline at end of file diff --git a/src/meshUtils.h b/src/meshUtils.h index ce063cb6a..aff3976f4 100644 --- a/src/meshUtils.h +++ b/src/meshUtils.h @@ -1,5 +1,8 @@ #pragma once #include "DebugConfiguration.h" +#include +#include +#include #include /// C++ v17+ clamp function, limits a given value to a range defined by lo and hi @@ -17,4 +20,8 @@ char *strnstr(const char *s, const char *find, size_t slen); void printBytes(const char *label, const uint8_t *p, size_t numbytes); // is the memory region filled with a single character? -bool memfll(const uint8_t *mem, uint8_t find, size_t numbytes); \ No newline at end of file +bool memfll(const uint8_t *mem, uint8_t find, size_t numbytes); + +bool isOneOf(int item, int count, ...); + +#define IS_ONE_OF(item, ...) isOneOf(item, sizeof((int[]){__VA_ARGS__}) / sizeof(int), __VA_ARGS__) diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index a88079aab..90e3b43c6 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -34,8 +34,8 @@ #include "modules/PositionModule.h" #endif -#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR -#include "AccelerometerThread.h" +#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C +#include "motion/AccelerometerThread.h" #endif AdminModule *adminModule; @@ -66,19 +66,45 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta // if handled == false, then let others look at this message also if they want bool handled = false; assert(r); - bool fromOthers = mp.from != 0 && mp.from != nodeDB->getNodeNum(); + bool fromOthers = !isFromUs(&mp); if (mp.which_payload_variant != meshtastic_MeshPacket_decoded_tag) { return handled; } meshtastic_Channel *ch = &channels.getByIndex(mp.channel); // Could tighten this up further by tracking the last public_key we went an AdminMessage request to // and only allowing responses from that remote. - if (!((mp.from == 0 && !config.security.is_managed) || messageIsResponse(r) || - (strcasecmp(ch->settings.name, Channels::adminChannel) == 0 && config.security.admin_channel_enabled) || - (mp.pki_encrypted && memcmp(mp.public_key.bytes, config.security.admin_key[0].bytes, 32) == 0))) { - LOG_INFO("Ignoring admin payload %i\n", r->which_payload_variant); + if (messageIsResponse(r)) { + LOG_DEBUG("Allowing admin response message\n"); + } else if (mp.from == 0) { + if (config.security.is_managed) { + LOG_INFO("Ignoring local admin payload because is_managed.\n"); + return handled; + } + } else if (strcasecmp(ch->settings.name, Channels::adminChannel) == 0) { + if (!config.security.admin_channel_enabled) { + LOG_INFO("Ignoring admin channel, as legacy admin is disabled.\n"); + myReply = allocErrorResponse(meshtastic_Routing_Error_NOT_AUTHORIZED, &mp); + return handled; + } + } else if (mp.pki_encrypted) { + if ((config.security.admin_key[0].size == 32 && + memcmp(mp.public_key.bytes, config.security.admin_key[0].bytes, 32) == 0) || + (config.security.admin_key[1].size == 32 && + memcmp(mp.public_key.bytes, config.security.admin_key[1].bytes, 32) == 0) || + (config.security.admin_key[2].size == 32 && + memcmp(mp.public_key.bytes, config.security.admin_key[2].bytes, 32) == 0)) { + LOG_INFO("PKC admin payload with authorized sender key.\n"); + } else { + myReply = allocErrorResponse(meshtastic_Routing_Error_ADMIN_PUBLIC_KEY_UNAUTHORIZED, &mp); + LOG_INFO("Received PKC admin payload, but the sender public key does not match the admin authorized key!\n"); + return handled; + } + } else { + LOG_INFO("Ignoring unauthorized admin payload %i\n", r->which_payload_variant); + myReply = allocErrorResponse(meshtastic_Routing_Error_NOT_AUTHORIZED, &mp); return handled; } + LOG_INFO("Handling admin payload %i\n", r->which_payload_variant); // all of the get and set messages, including those for other modules, flow through here first. @@ -86,6 +112,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta if (mp.from != 0 && !messageIsRequest(r) && !messageIsResponse(r)) { if (!checkPassKey(r)) { LOG_WARN("Admin message without session_key!\n"); + myReply = allocErrorResponse(meshtastic_Routing_Error_ADMIN_BAD_SESSION_KEY, &mp); return handled; } } @@ -186,18 +213,22 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta break; } case meshtastic_AdminMessage_factory_reset_config_tag: { + disableBluetooth(); LOG_INFO("Initiating factory config reset\n"); nodeDB->factoryReset(); + LOG_INFO("Factory config reset finished, rebooting soon.\n"); reboot(DEFAULT_REBOOT_SECONDS); break; } case meshtastic_AdminMessage_factory_reset_device_tag: { + disableBluetooth(); LOG_INFO("Initiating full factory reset\n"); nodeDB->factoryReset(true); reboot(DEFAULT_REBOOT_SECONDS); break; } case meshtastic_AdminMessage_nodedb_reset_tag: { + disableBluetooth(); LOG_INFO("Initiating node-db reset\n"); nodeDB->resetNodes(); reboot(DEFAULT_REBOOT_SECONDS); @@ -209,6 +240,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta break; } case meshtastic_AdminMessage_commit_edit_settings_tag: { + disableBluetooth(); LOG_INFO("Committing transaction for edited settings\n"); hasOpenEditTransaction = false; saveChanges(SEGMENT_CONFIG | SEGMENT_MODULECONFIG | SEGMENT_DEVICESTATE | SEGMENT_CHANNELS); @@ -252,34 +284,26 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta break; } case meshtastic_AdminMessage_set_fixed_position_tag: { - if (fromOthers) { - LOG_INFO("Ignoring set_fixed_position command from another node.\n"); - } else { - LOG_INFO("Client is receiving a set_fixed_position command.\n"); - meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); - node->has_position = true; - node->position = TypeConversions::ConvertToPositionLite(r->set_fixed_position); - nodeDB->setLocalPosition(r->set_fixed_position); - config.position.fixed_position = true; - saveChanges(SEGMENT_DEVICESTATE | SEGMENT_CONFIG, false); + LOG_INFO("Client is receiving a set_fixed_position command.\n"); + meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); + node->has_position = true; + node->position = TypeConversions::ConvertToPositionLite(r->set_fixed_position); + nodeDB->setLocalPosition(r->set_fixed_position); + config.position.fixed_position = true; + saveChanges(SEGMENT_DEVICESTATE | SEGMENT_CONFIG, false); #if !MESHTASTIC_EXCLUDE_GPS - if (gps != nullptr) - gps->enable(); - // Send our new fixed position to the mesh for good measure - positionModule->sendOurPosition(); + if (gps != nullptr) + gps->enable(); + // Send our new fixed position to the mesh for good measure + positionModule->sendOurPosition(); #endif - } break; } case meshtastic_AdminMessage_remove_fixed_position_tag: { - if (fromOthers) { - LOG_INFO("Ignoring remove_fixed_position command from another node.\n"); - } else { - LOG_INFO("Client is receiving a remove_fixed_position command.\n"); - nodeDB->clearLocalPosition(); - config.position.fixed_position = false; - saveChanges(SEGMENT_DEVICESTATE | SEGMENT_CONFIG, false); - } + LOG_INFO("Client is receiving a remove_fixed_position command.\n"); + nodeDB->clearLocalPosition(); + config.position.fixed_position = false; + saveChanges(SEGMENT_DEVICESTATE | SEGMENT_CONFIG, false); break; } case meshtastic_AdminMessage_set_time_only_tag: { @@ -406,7 +430,10 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) LOG_INFO("Setting config: Device\n"); config.has_device = true; #if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR - if (config.device.double_tap_as_button_press == false && c.payload_variant.device.double_tap_as_button_press == true) { + if (config.device.double_tap_as_button_press == false && c.payload_variant.device.double_tap_as_button_press == true && + accelerometerThread->enabled == false) { + config.device.double_tap_as_button_press = c.payload_variant.device.double_tap_as_button_press; + accelerometerThread->enabled = true; accelerometerThread->start(); } #endif @@ -440,7 +467,7 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) requiresReboot = true; } } -#if EVENT_MODE +#if USERPREFS_EVENT_MODE // If we're in event mode, nobody is a Router or Repeater if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER || config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) { @@ -484,7 +511,10 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) requiresReboot = false; } #if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR - if (config.display.wake_on_tap_or_motion == false && c.payload_variant.display.wake_on_tap_or_motion == true) { + if (config.display.wake_on_tap_or_motion == false && c.payload_variant.display.wake_on_tap_or_motion == true && + accelerometerThread->enabled == false) { + config.display.wake_on_tap_or_motion = c.payload_variant.display.wake_on_tap_or_motion; + accelerometerThread->enabled = true; accelerometerThread->start(); } #endif @@ -557,12 +587,17 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) break; } + if (requiresReboot && !hasOpenEditTransaction) { + disableBluetooth(); + } saveChanges(changes, requiresReboot); } void AdminModule::handleSetModuleConfig(const meshtastic_ModuleConfig &c) { + if (!hasOpenEditTransaction) + disableBluetooth(); switch (c.which_payload_variant) { case meshtastic_ModuleConfig_mqtt_tag: LOG_INFO("Setting module config: MQTT\n"); @@ -634,7 +669,6 @@ void AdminModule::handleSetModuleConfig(const meshtastic_ModuleConfig &c) moduleConfig.paxcounter = c.payload_variant.paxcounter; break; } - saveChanges(SEGMENT_MODULECONFIG); } @@ -937,7 +971,7 @@ void AdminModule::saveChanges(int saveWhat, bool shouldReboot) } else { LOG_INFO("Delaying save of changes to disk until the open transaction is committed\n"); } - if (shouldReboot) { + if (shouldReboot && !hasOpenEditTransaction) { reboot(DEFAULT_REBOOT_SECONDS); } } @@ -997,7 +1031,7 @@ bool AdminModule::checkPassKey(meshtastic_AdminMessage *res) memcmp(res->session_passkey.bytes, session_passkey, 8) == 0); } -bool AdminModule::messageIsResponse(meshtastic_AdminMessage *r) +bool AdminModule::messageIsResponse(const meshtastic_AdminMessage *r) { if (r->which_payload_variant == meshtastic_AdminMessage_get_channel_response_tag || r->which_payload_variant == meshtastic_AdminMessage_get_owner_response_tag || @@ -1014,7 +1048,7 @@ bool AdminModule::messageIsResponse(meshtastic_AdminMessage *r) return false; } -bool AdminModule::messageIsRequest(meshtastic_AdminMessage *r) +bool AdminModule::messageIsRequest(const meshtastic_AdminMessage *r) { if (r->which_payload_variant == meshtastic_AdminMessage_get_channel_request_tag || r->which_payload_variant == meshtastic_AdminMessage_get_owner_request_tag || @@ -1029,3 +1063,16 @@ bool AdminModule::messageIsRequest(meshtastic_AdminMessage *r) else return false; } + +void disableBluetooth() +{ +#if HAS_BLUETOOTH +#ifdef ARCH_ESP32 + if (nimbleBluetooth) + nimbleBluetooth->deinit(); +#elif defined(ARCH_NRF52) + if (nrf52Bluetooth) + nrf52Bluetooth->shutdown(); +#endif +#endif +} \ No newline at end of file diff --git a/src/modules/AdminModule.h b/src/modules/AdminModule.h index 61c54d1b1..d6c0a7343 100644 --- a/src/modules/AdminModule.h +++ b/src/modules/AdminModule.h @@ -55,8 +55,10 @@ class AdminModule : public ProtobufModule, public Obser void setPassKey(meshtastic_AdminMessage *res); bool checkPassKey(meshtastic_AdminMessage *res); - bool messageIsResponse(meshtastic_AdminMessage *r); - bool messageIsRequest(meshtastic_AdminMessage *r); + bool messageIsResponse(const meshtastic_AdminMessage *r); + bool messageIsRequest(const meshtastic_AdminMessage *r); }; -extern AdminModule *adminModule; \ No newline at end of file +extern AdminModule *adminModule; + +void disableBluetooth(); \ No newline at end of file diff --git a/src/modules/AtakPluginModule.cpp b/src/modules/AtakPluginModule.cpp index 437a341db..72d069619 100644 --- a/src/modules/AtakPluginModule.cpp +++ b/src/modules/AtakPluginModule.cpp @@ -52,6 +52,10 @@ meshtastic_TAKPacket AtakPluginModule::cloneTAKPacketData(meshtastic_TAKPacket * } else if (t->which_payload_variant == meshtastic_TAKPacket_chat_tag) { clone.which_payload_variant = meshtastic_TAKPacket_chat_tag; clone.payload_variant.chat = {0}; + } else if (t->which_payload_variant == meshtastic_TAKPacket_detail_tag) { + clone.which_payload_variant = meshtastic_TAKPacket_detail_tag; + clone.payload_variant.detail.size = t->payload_variant.detail.size; + memcpy(clone.payload_variant.detail.bytes, t->payload_variant.detail.bytes, t->payload_variant.detail.size); } return clone; diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index 4df5a03fc..ed0dce25f 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -12,6 +12,7 @@ #include "detect/ScanI2C.h" #include "input/ScanAndSelect.h" #include "mesh/generated/meshtastic/cannedmessages.pb.h" +#include "modules/AdminModule.h" #include "main.h" // for cardkb_found #include "modules/ExternalNotificationModule.h" // for buzzer control @@ -27,6 +28,7 @@ #endif #include "graphics/ScreenFonts.h" +#include // Remove Canned message screen if no action is taken for some milliseconds #define INACTIVATE_AFTER_MS 20000 @@ -77,16 +79,16 @@ int CannedMessageModule::splitConfiguredMessages() int messageIndex = 0; int i = 0; - String messages = cannedMessageModuleConfig.messages; + String canned_messages = cannedMessageModuleConfig.messages; #if defined(T_WATCH_S3) || defined(RAK14014) - String separator = messages.length() ? "|" : ""; + String separator = canned_messages.length() ? "|" : ""; - messages = "[---- Free Text ----]" + separator + messages; + canned_messages = "[---- Free Text ----]" + separator + canned_messages; #endif // collect all the message parts - strncpy(this->messageStore, messages.c_str(), sizeof(this->messageStore)); + strncpy(this->messageStore, canned_messages.c_str(), sizeof(this->messageStore)); // The first message points to the beginning of the store. this->messages[messageIndex++] = this->messageStore; @@ -190,17 +192,17 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event) #if defined(T_WATCH_S3) || defined(RAK14014) if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) { - this->payload = 0xb4; + this->payload = INPUT_BROKER_MSG_LEFT; } else if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT)) { - this->payload = 0xb7; + this->payload = INPUT_BROKER_MSG_RIGHT; } #else // tweak for left/right events generated via trackball/touch with empty kbchar if (!event->kbchar) { if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) { - this->payload = 0xb4; + this->payload = INPUT_BROKER_MSG_LEFT; } else if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT)) { - this->payload = 0xb7; + this->payload = INPUT_BROKER_MSG_RIGHT; } } else { // pass the pressed key @@ -222,26 +224,26 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event) // Run modifier key code below, (doesnt inturrupt typing or reset to start screen page) switch (event->kbchar) { - case 0x11: // make screen brighter + case INPUT_BROKER_MSG_BRIGHTNESS_UP: // make screen brighter if (screen) screen->increaseBrightness(); LOG_DEBUG("increasing Screen Brightness\n"); break; - case 0x12: // make screen dimmer + case INPUT_BROKER_MSG_BRIGHTNESS_DOWN: // make screen dimmer if (screen) screen->decreaseBrightness(); LOG_DEBUG("Decreasing Screen Brightness\n"); break; - case 0xf1: // draw modifier (function) symbal + case INPUT_BROKER_MSG_FN_SYMBOL_ON: // draw modifier (function) symbal if (screen) screen->setFunctionSymbal("Fn"); break; - case 0xf2: // remove modifier (function) symbal + case INPUT_BROKER_MSG_FN_SYMBOL_OFF: // remove modifier (function) symbal if (screen) screen->removeFunctionSymbal("Fn"); break; // mute (switch off/toggle) external notifications on fn+m - case 0xac: + case INPUT_BROKER_MSG_MUTE_TOGGLE: if (moduleConfig.external_notification.enabled == true) { if (externalNotificationModule->getMute()) { externalNotificationModule->setMute(false); @@ -257,7 +259,7 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event) } } break; - case 0x9e: // toggle GPS like triple press does + case INPUT_BROKER_MSG_GPS_TOGGLE: // toggle GPS like triple press does #if !MESHTASTIC_EXCLUDE_GPS if (gps != nullptr) { gps->toggleGpsMode(); @@ -267,7 +269,22 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event) showTemporaryMessage("GPS Toggled"); #endif break; - case 0xaf: // fn+space send network ping like double press does + case INPUT_BROKER_MSG_BLUETOOTH_TOGGLE: // toggle Bluetooth on/off + if (config.bluetooth.enabled == true) { + config.bluetooth.enabled = false; + LOG_INFO("User toggled Bluetooth"); + nodeDB->saveToDisk(); + disableBluetooth(); + showTemporaryMessage("Bluetooth OFF"); + } else if (config.bluetooth.enabled == false) { + config.bluetooth.enabled = true; + LOG_INFO("User toggled Bluetooth"); + nodeDB->saveToDisk(); + rebootAtMsec = millis() + 2000; + showTemporaryMessage("Bluetooth ON\nReboot"); + } + break; + case INPUT_BROKER_MSG_SEND_PING: // fn+space send network ping like double press does service->refreshLocalMeshNode(); if (service->trySendPosition(NODENUM_BROADCAST, true)) { showTemporaryMessage("Position \nUpdate Sent"); @@ -275,6 +292,13 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event) showTemporaryMessage("Node Info \nUpdate Sent"); } break; + case INPUT_BROKER_MSG_DISMISS_FRAME: // fn+del: dismiss screen frames like text or waypoint + // Avoid opening the canned message screen frame + // We're only handling the keypress here by convention, this has nothing to do with canned messages + this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; + // Attempt to close whatever frame is currently shown on display + screen->dismissCurrentFrame(); + return 0; default: // pass the pressed key // LOG_DEBUG("Canned message ANYKEY (%x)\n", event->kbchar); @@ -283,7 +307,7 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event) validEvent = true; break; } - if (screen && (event->kbchar != 0xf1)) { + if (screen && (event->kbchar != INPUT_BROKER_MSG_FN_SYMBOL_ON)) { screen->removeFunctionSymbal("Fn"); // remove modifier (function) symbal } } @@ -422,7 +446,7 @@ int32_t CannedMessageModule::runOnce() this->notifyObservers(&e); } else if (((this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT)) && - ((millis() - this->lastTouchMillis) > INACTIVATE_AFTER_MS)) { + !Throttle::isWithinTimespanMs(this->lastTouchMillis, INACTIVATE_AFTER_MS)) { // Reset module e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen this->currentMessageIndex = -1; @@ -505,7 +529,7 @@ int32_t CannedMessageModule::runOnce() } } else if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT || this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) { switch (this->payload) { - case 0xb4: // left + case INPUT_BROKER_MSG_LEFT: if (this->destSelect == CANNED_MESSAGE_DESTINATION_TYPE_NODE) { size_t numMeshNodes = nodeDB->getNumMeshNodes(); if (this->dest == NODENUM_BROADCAST) { @@ -540,7 +564,7 @@ int32_t CannedMessageModule::runOnce() } } break; - case 0xb7: // right + case INPUT_BROKER_MSG_RIGHT: if (this->destSelect == CANNED_MESSAGE_DESTINATION_TYPE_NODE) { size_t numMeshNodes = nodeDB->getNumMeshNodes(); if (this->dest == NODENUM_BROADCAST) { @@ -602,19 +626,19 @@ int32_t CannedMessageModule::runOnce() this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NODE; } break; - case 0xb4: // left - case 0xb7: // right + case INPUT_BROKER_MSG_LEFT: + case INPUT_BROKER_MSG_RIGHT: // already handled above break; // handle fn+s for shutdown - case 0x9b: + case INPUT_BROKER_MSG_SHUTDOWN: if (screen) screen->startAlert("Shutting down..."); shutdownAtMsec = millis() + DEFAULT_SHUTDOWN_SECONDS * 1000; runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; break; // and fn+r for reboot - case 0x90: + case INPUT_BROKER_MSG_REBOOT: if (screen) screen->startAlert("Rebooting..."); rebootAtMsec = millis() + DEFAULT_REBOOT_SECONDS * 1000; @@ -934,6 +958,19 @@ void CannedMessageModule::drawEnterIcon(OLEDDisplay *display, int x, int y, floa #endif +// Indicate to screen class that module is handling keyboard input specially (at certain times) +// This prevents the left & right keys being used for nav. between screen frames during text entry. +bool CannedMessageModule::interceptingKeyboardInput() +{ + switch (runState) { + case CANNED_MESSAGE_RUN_STATE_DISABLED: + case CANNED_MESSAGE_RUN_STATE_INACTIVE: + return false; + default: + return true; + } +} + void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { char buffer[50]; @@ -977,7 +1014,7 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st int16_t rssiY = 130; // If dislay is *slighly* too small for the original consants, squish up a bit - if (display->getHeight() < rssiY) { + if (display->getHeight() < rssiY + FONT_HEIGHT_SMALL) { snrY = display->getHeight() - ((1.5) * FONT_HEIGHT_SMALL); rssiY = display->getHeight() - ((2.5) * FONT_HEIGHT_SMALL); } @@ -1124,7 +1161,6 @@ void CannedMessageModule::loadProtoForModule() installDefaultCannedMessageModuleConfig(); } } - /** * @brief Save the module config to file. * diff --git a/src/modules/CannedMessageModule.h b/src/modules/CannedMessageModule.h index 9e6af8890..e9dc2bda0 100644 --- a/src/modules/CannedMessageModule.h +++ b/src/modules/CannedMessageModule.h @@ -114,6 +114,7 @@ class CannedMessageModule : public SinglePortModule, public ObservableshouldDraw(); } virtual Observable *getUIFrameObservable() override { return this; } virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) override; + virtual bool interceptingKeyboardInput() override; virtual AdminMessageHandleResult handleAdminMessageForModule(const meshtastic_MeshPacket &mp, meshtastic_AdminMessage *request, meshtastic_AdminMessage *response) override; @@ -223,4 +224,4 @@ class CannedMessageModule : public SinglePortModule, public Observable DetectionSensorModule *detectionSensorModule; #define GPIO_POLLING_INTERVAL 100 #define DELAYED_INTERVAL 1000 +typedef enum { + DetectionSensorVerdictDetected, + DetectionSensorVerdictSendState, + DetectionSensorVerdictNoop, +} DetectionSensorTriggerVerdict; + +typedef DetectionSensorTriggerVerdict (*DetectionSensorTriggerHandler)(bool prev, bool current); + +static DetectionSensorTriggerVerdict detection_trigger_logic_level(bool prev, bool current) +{ + return current ? DetectionSensorVerdictDetected : DetectionSensorVerdictNoop; +} + +static DetectionSensorTriggerVerdict detection_trigger_single_edge(bool prev, bool current) +{ + return (!prev && current) ? DetectionSensorVerdictDetected : DetectionSensorVerdictNoop; +} + +static DetectionSensorTriggerVerdict detection_trigger_either_edge(bool prev, bool current) +{ + if (prev == current) { + return DetectionSensorVerdictNoop; + } + return current ? DetectionSensorVerdictDetected : DetectionSensorVerdictSendState; +} + +const static DetectionSensorTriggerHandler handlers[_meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_MAX + 1] = { + [meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_LOGIC_LOW] = detection_trigger_logic_level, + [meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_LOGIC_HIGH] = detection_trigger_logic_level, + [meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_FALLING_EDGE] = detection_trigger_single_edge, + [meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_RISING_EDGE] = detection_trigger_single_edge, + [meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_EITHER_EDGE_ACTIVE_LOW] = detection_trigger_either_edge, + [meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_EITHER_EDGE_ACTIVE_HIGH] = detection_trigger_either_edge, +}; + int32_t DetectionSensorModule::runOnce() { /* @@ -21,7 +57,8 @@ int32_t DetectionSensorModule::runOnce() // moduleConfig.detection_sensor.monitor_pin = 21; // WisBlock RAK12013 Radar IO6 // moduleConfig.detection_sensor.minimum_broadcast_secs = 30; // moduleConfig.detection_sensor.state_broadcast_secs = 120; - // moduleConfig.detection_sensor.detection_triggered_high = true; + // moduleConfig.detection_sensor.detection_trigger_type = + // meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_LOGIC_HIGH; // strcpy(moduleConfig.detection_sensor.name, "Motion"); if (moduleConfig.detection_sensor.enabled == false) @@ -49,18 +86,31 @@ int32_t DetectionSensorModule::runOnce() // LOG_DEBUG("Detection Sensor Module: Current pin state: %i\n", digitalRead(moduleConfig.detection_sensor.monitor_pin)); - if ((millis() - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.detection_sensor.minimum_broadcast_secs) && - hasDetectionEvent()) { - sendDetectionMessage(); - return DELAYED_INTERVAL; + if (!Throttle::isWithinTimespanMs(lastSentToMesh, + Default::getConfiguredOrDefaultMs(moduleConfig.detection_sensor.minimum_broadcast_secs))) { + bool isDetected = hasDetectionEvent(); + DetectionSensorTriggerVerdict verdict = + handlers[moduleConfig.detection_sensor.detection_trigger_type](wasDetected, isDetected); + wasDetected = isDetected; + switch (verdict) { + case DetectionSensorVerdictDetected: + sendDetectionMessage(); + return DELAYED_INTERVAL; + case DetectionSensorVerdictSendState: + sendCurrentStateMessage(isDetected); + return DELAYED_INTERVAL; + case DetectionSensorVerdictNoop: + break; + } } // Even if we haven't detected an event, broadcast our current state to the mesh on the scheduled interval as a sort // of heartbeat. We only do this if the minimum broadcast interval is greater than zero, otherwise we'll only broadcast state // change detections. - else if (moduleConfig.detection_sensor.state_broadcast_secs > 0 && - (millis() - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.detection_sensor.state_broadcast_secs, - default_telemetry_broadcast_interval_secs)) { - sendCurrentStateMessage(); + if (moduleConfig.detection_sensor.state_broadcast_secs > 0 && + !Throttle::isWithinTimespanMs(lastSentToMesh, + Default::getConfiguredOrDefaultMs(moduleConfig.detection_sensor.state_broadcast_secs, + default_telemetry_broadcast_interval_secs))) { + sendCurrentStateMessage(hasDetectionEvent()); return DELAYED_INTERVAL; } return GPIO_POLLING_INTERVAL; @@ -86,10 +136,10 @@ void DetectionSensorModule::sendDetectionMessage() delete[] message; } -void DetectionSensorModule::sendCurrentStateMessage() +void DetectionSensorModule::sendCurrentStateMessage(bool state) { char *message = new char[40]; - sprintf(message, "%s state: %i", moduleConfig.detection_sensor.name, hasDetectionEvent()); + sprintf(message, "%s state: %i", moduleConfig.detection_sensor.name, state); meshtastic_MeshPacket *p = allocDataPacket(); p->want_ack = false; @@ -105,5 +155,5 @@ bool DetectionSensorModule::hasDetectionEvent() { bool currentState = digitalRead(moduleConfig.detection_sensor.monitor_pin); // LOG_DEBUG("Detection Sensor Module: Current state: %i\n", currentState); - return moduleConfig.detection_sensor.detection_triggered_high ? currentState : !currentState; + return (moduleConfig.detection_sensor.detection_trigger_type & 1) ? currentState : !currentState; } \ No newline at end of file diff --git a/src/modules/DetectionSensorModule.h b/src/modules/DetectionSensorModule.h index ed6cddda5..b960c8744 100644 --- a/src/modules/DetectionSensorModule.h +++ b/src/modules/DetectionSensorModule.h @@ -15,8 +15,9 @@ class DetectionSensorModule : public SinglePortModule, private concurrency::OSTh private: bool firstTime = true; uint32_t lastSentToMesh = 0; + bool wasDetected = false; void sendDetectionMessage(); - void sendCurrentStateMessage(); + void sendCurrentStateMessage(bool state); bool hasDetectionEvent(); }; diff --git a/src/modules/ExternalNotificationModule.cpp b/src/modules/ExternalNotificationModule.cpp index 652db04d3..8abc386ec 100644 --- a/src/modules/ExternalNotificationModule.cpp +++ b/src/modules/ExternalNotificationModule.cpp @@ -428,7 +428,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP drv.setWaveform(2, 0); drv.go(); #endif - if (getFrom(&mp) != nodeDB->getNodeNum()) { + if (!isFromUs(&mp)) { // Check if the message contains a bell character. Don't do this loop for every pin, just once. auto &p = mp.decoded; bool containsBell = false; @@ -593,4 +593,4 @@ void ExternalNotificationModule::handleSetRingtone(const char *from_msg) if (changed) { nodeDB->saveProto(rtttlConfigFile, meshtastic_RTTTLConfig_size, &meshtastic_RTTTLConfig_msg, &rtttlConfig); } -} +} \ No newline at end of file diff --git a/src/modules/ExternalNotificationModule.h b/src/modules/ExternalNotificationModule.h index 000ef10f6..e86585c2f 100644 --- a/src/modules/ExternalNotificationModule.h +++ b/src/modules/ExternalNotificationModule.h @@ -3,7 +3,7 @@ #include "SinglePortModule.h" #include "concurrency/OSThread.h" #include "configuration.h" -#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !defined(ARCH_APOLLO3) +#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !defined(ARCH_APOLLO3) && !defined(CONFIG_IDF_TARGET_ESP32C6) #include #else // Noop class for portduino. diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index eca0e8ac9..ad3f0ace4 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -47,6 +47,9 @@ #endif #if ARCH_PORTDUINO #include "input/LinuxInputImpl.h" +#if !MESHTASTIC_EXCLUDE_STOREFORWARD +#include "modules/StoreForwardModule.h" +#endif #endif #if HAS_TELEMETRY #include "modules/Telemetry/DeviceTelemetry.h" @@ -55,6 +58,7 @@ #include "main.h" #include "modules/Telemetry/AirQualityTelemetry.h" #include "modules/Telemetry/EnvironmentTelemetry.h" +#include "modules/Telemetry/HealthTelemetry.h" #endif #if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_POWER_TELEMETRY #include "modules/Telemetry/PowerTelemetry.h" @@ -67,7 +71,7 @@ #include "modules/esp32/PaxcounterModule.h" #endif #if !MESHTASTIC_EXCLUDE_STOREFORWARD -#include "modules/esp32/StoreForwardModule.h" +#include "modules/StoreForwardModule.h" #endif #endif #if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040) @@ -191,6 +195,10 @@ void setupModules() if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].first > 0) { new AirQualityTelemetryModule(); } + if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_MAX30102].first > 0 || + nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_MLX90614].first > 0) { + new HealthTelemetryModule(); + } #endif #if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_POWER_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR new PowerTelemetryModule(); @@ -206,13 +214,15 @@ void setupModules() #if defined(USE_SX1280) && !MESHTASTIC_EXCLUDE_AUDIO audioModule = new AudioModule(); #endif -#if !MESHTASTIC_EXCLUDE_STOREFORWARD - storeForwardModule = new StoreForwardModule(); -#endif #if !MESHTASTIC_EXCLUDE_PAXCOUNTER paxcounterModule = new PaxcounterModule(); #endif #endif +#if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) +#if !MESHTASTIC_EXCLUDE_STOREFORWARD + storeForwardModule = new StoreForwardModule(); +#endif +#endif #if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040) #if !MESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION externalNotificationModule = new ExternalNotificationModule(); diff --git a/src/modules/NeighborInfoModule.cpp b/src/modules/NeighborInfoModule.cpp index 2af953b4e..163d3a897 100644 --- a/src/modules/NeighborInfoModule.cpp +++ b/src/modules/NeighborInfoModule.cpp @@ -3,6 +3,7 @@ #include "MeshService.h" #include "NodeDB.h" #include "gps/RTC.h" +#include NeighborInfoModule *neighborInfoModule; @@ -61,7 +62,8 @@ uint32_t NeighborInfoModule::collectNeighborInfo(meshtastic_NeighborInfo *neighb NodeNum my_node_id = nodeDB->getNodeNum(); neighborInfo->node_id = my_node_id; neighborInfo->last_sent_by_id = my_node_id; - neighborInfo->node_broadcast_interval_secs = moduleConfig.neighbor_info.update_interval; + neighborInfo->node_broadcast_interval_secs = + Default::getConfiguredOrDefault(moduleConfig.neighbor_info.update_interval, default_telemetry_broadcast_interval_secs); cleanUpNeighbors(); @@ -87,6 +89,7 @@ void NeighborInfoModule::cleanUpNeighbors() NodeNum my_node_id = nodeDB->getNodeNum(); for (auto it = neighbors.rbegin(); it != neighbors.rend();) { // We will remove a neighbor if we haven't heard from them in twice the broadcast interval + // cannot use isWithinTimespanMs() as it->last_rx_time is seconds since 1970 if ((now - it->last_rx_time > it->node_broadcast_interval_secs * 2) && (it->node_id != my_node_id)) { LOG_DEBUG("Removing neighbor with node ID 0x%x\n", it->node_id); it = std::vector::reverse_iterator( diff --git a/src/modules/NodeInfoModule.cpp b/src/modules/NodeInfoModule.cpp index cb047a4dc..61ec375cc 100644 --- a/src/modules/NodeInfoModule.cpp +++ b/src/modules/NodeInfoModule.cpp @@ -6,6 +6,7 @@ #include "Router.h" #include "configuration.h" #include "main.h" +#include NodeInfoModule *nodeInfoModule; @@ -25,7 +26,7 @@ bool NodeInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes } // if user has changed while packet was not for us, inform phone - if (hasChanged && !wasBroadcast && mp.to != nodeDB->getNodeNum()) + if (hasChanged && !wasBroadcast && !isToUs(&mp)) service->sendToPhone(packetPool.allocCopy(mp)); // LOG_DEBUG("did handleReceived\n"); @@ -67,13 +68,12 @@ meshtastic_MeshPacket *NodeInfoModule::allocReply() LOG_DEBUG("Skip sending NodeInfo due to > 40 percent channel util.\n"); return NULL; } - uint32_t now = millis(); // If we sent our NodeInfo less than 5 min. ago, don't send it again as it may be still underway. - if (!shorterTimeout && lastSentToMesh && (now - lastSentToMesh) < (5 * 60 * 1000)) { + if (!shorterTimeout && lastSentToMesh && Throttle::isWithinTimespanMs(lastSentToMesh, 5 * 60 * 1000)) { LOG_DEBUG("Skip sending NodeInfo since we just sent it less than 5 minutes ago.\n"); ignoreRequest = true; // Mark it as ignored for MeshModule return NULL; - } else if (shorterTimeout && lastSentToMesh && (now - lastSentToMesh) < (60 * 1000)) { + } else if (shorterTimeout && lastSentToMesh && Throttle::isWithinTimespanMs(lastSentToMesh, 60 * 1000)) { LOG_DEBUG("Skip sending actively requested NodeInfo since we just sent it less than 60 seconds ago.\n"); ignoreRequest = true; // Mark it as ignored for MeshModule return NULL; @@ -82,7 +82,7 @@ meshtastic_MeshPacket *NodeInfoModule::allocReply() meshtastic_User &u = owner; LOG_INFO("sending owner %s/%s/%s\n", u.id, u.long_name, u.short_name); - lastSentToMesh = now; + lastSentToMesh = millis(); return allocDataProtobuf(u); } } diff --git a/src/modules/PositionModule.cpp b/src/modules/PositionModule.cpp index 450fcf7f8..a2693a814 100644 --- a/src/modules/PositionModule.cpp +++ b/src/modules/PositionModule.cpp @@ -12,6 +12,7 @@ #include "gps/RTC.h" #include "main.h" #include "mesh/compression/unishox2.h" +#include "meshUtils.h" #include "meshtastic/atak.pb.h" #include "sleep.h" #include "target_specific.h" @@ -53,7 +54,7 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes // FIXME this can in fact happen with packets sent from EUD (src=RX_SRC_USER) // to set fixed location, EUD-GPS location or just the time (see also issue #900) bool isLocal = false; - if (nodeDB->getNodeNum() == getFrom(&mp)) { + if (isFromUs(&mp)) { isLocal = true; if (config.position.fixed_position) { LOG_DEBUG("Ignore incoming position update from myself except for time, because position.fixed_position is true\n"); @@ -109,7 +110,7 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes void PositionModule::alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtastic_Position *p) { // Phone position packets need to be truncated to the channel precision - if (nodeDB->getNodeNum() == getFrom(&mp) && (precision < 32 && precision > 0)) { + if (isFromUs(&mp) && (precision < 32 && precision > 0)) { LOG_DEBUG("Truncating phone position to channel precision %i\n", precision); p->latitude_i = p->latitude_i & (UINT32_MAX << (32 - precision)); p->longitude_i = p->longitude_i & (UINT32_MAX << (32 - precision)); @@ -145,8 +146,12 @@ void PositionModule::trySetRtc(meshtastic_Position p, bool isLocal, bool forceUp bool PositionModule::hasQualityTimesource() { bool setFromPhoneOrNtpToday = - lastSetFromPhoneNtpOrGps == 0 ? false : (millis() - lastSetFromPhoneNtpOrGps) <= (SEC_PER_DAY * 1000UL); + lastSetFromPhoneNtpOrGps == 0 ? false : Throttle::isWithinTimespanMs(lastSetFromPhoneNtpOrGps, SEC_PER_DAY * 1000UL); +#if MESHTASTIC_EXCLUDE_GPS + bool hasGpsOrRtc = (rtc_found.address != ScanI2C::ADDRESS_NONE.address); +#else bool hasGpsOrRtc = (gps && gps->isConnected()) || (rtc_found.address != ScanI2C::ADDRESS_NONE.address); +#endif return hasGpsOrRtc || setFromPhoneOrNtpToday; } @@ -269,7 +274,7 @@ meshtastic_MeshPacket *PositionModule::allocAtakPli() meshtastic_TAKPacket takPacket = {.is_compressed = true, .has_contact = true, - .contact = {0}, + .contact = meshtastic_Contact_init_default, .has_group = true, .group = {meshtastic_MemberRole_TeamMember, meshtastic_Team_Cyan}, .has_status = true, @@ -278,13 +283,13 @@ meshtastic_MeshPacket *PositionModule::allocAtakPli() .battery = powerStatus->getBatteryChargePercent(), }, .which_payload_variant = meshtastic_TAKPacket_pli_tag, - {.pli = { - .latitude_i = localPosition.latitude_i, - .longitude_i = localPosition.longitude_i, - .altitude = localPosition.altitude_hae, - .speed = localPosition.ground_speed, - .course = static_cast(localPosition.ground_track), - }}}; + .payload_variant = {.pli = { + .latitude_i = localPosition.latitude_i, + .longitude_i = localPosition.longitude_i, + .altitude = localPosition.altitude_hae, + .speed = localPosition.ground_speed, + .course = static_cast(localPosition.ground_track), + }}}; auto length = unishox2_compress_lines(owner.long_name, strlen(owner.long_name), takPacket.contact.device_callsign, sizeof(takPacket.contact.device_callsign) - 1, USX_PSET_DFLT, NULL); @@ -343,8 +348,8 @@ void PositionModule::sendOurPosition(NodeNum dest, bool wantReplies, uint8_t cha service->sendToMesh(p, RX_SRC_LOCAL, true); - if ((config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER || - config.device.role == meshtastic_Config_DeviceConfig_Role_TAK_TRACKER) && + if (IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_TRACKER, + meshtastic_Config_DeviceConfig_Role_TAK_TRACKER) && config.power.is_power_saving) { LOG_DEBUG("Starting next execution in 5 seconds and then going to sleep.\n"); sleepOnNextExecution = true; diff --git a/src/modules/PositionModule.h b/src/modules/PositionModule.h index c4ef66501..41b86b795 100644 --- a/src/modules/PositionModule.h +++ b/src/modules/PositionModule.h @@ -63,7 +63,7 @@ class PositionModule : public ProtobufModule, private concu bool hasQualityTimesource(); const uint32_t minimumTimeThreshold = - Default::getConfiguredOrDefaultMsScaled(config.position.broadcast_smart_minimum_interval_secs, 30, numOnlineNodes); + Default::getConfiguredOrDefaultMs(config.position.broadcast_smart_minimum_interval_secs, 30); }; struct SmartPosition { diff --git a/src/modules/PowerStressModule.cpp b/src/modules/PowerStressModule.cpp index 4c9f0df88..48159ba54 100644 --- a/src/modules/PowerStressModule.cpp +++ b/src/modules/PowerStressModule.cpp @@ -9,6 +9,7 @@ #include "main.h" #include "sleep.h" #include "target_specific.h" +#include extern void printInfo(); @@ -114,7 +115,7 @@ int32_t PowerStressModule::runOnce() break; case meshtastic_PowerStressMessage_Opcode_CPU_FULLON: { uint32_t start_msec = millis(); - while ((millis() - start_msec) < (uint32_t)sleep_msec) + while (Throttle::isWithinTimespanMs(start_msec, sleep_msec)) ; // Don't let CPU idle at all sleep_msec = 0; // we already slept break; diff --git a/src/modules/RangeTestModule.cpp b/src/modules/RangeTestModule.cpp index 8154a661e..e78b4e68d 100644 --- a/src/modules/RangeTestModule.cpp +++ b/src/modules/RangeTestModule.cpp @@ -19,6 +19,7 @@ #include "configuration.h" #include "gps/GeoCoord.h" #include +#include RangeTestModule *rangeTestModule; RangeTestModuleRadio *rangeTestModuleRadio; @@ -79,7 +80,7 @@ int32_t RangeTestModule::runOnce() } // If we have been running for more than 8 hours, turn module back off - if (millis() - started > 28800000) { + if (!Throttle::isWithinTimespanMs(started, 28800000)) { LOG_INFO("Range Test Module - Disabling after 8 hours\n"); return disable(); } else { @@ -114,7 +115,7 @@ void RangeTestModuleRadio::sendPayload(NodeNum dest, bool wantReplies) packetSequence++; - static char heartbeatString[MAX_RHPACKETLEN]; + static char heartbeatString[MAX_LORA_PAYLOAD_LEN + 1]; snprintf(heartbeatString, sizeof(heartbeatString), "seq %u", packetSequence); p->decoded.payload.size = strlen(heartbeatString); // You must specify how many bytes are in the reply @@ -138,7 +139,7 @@ ProcessMessage RangeTestModuleRadio::handleReceived(const meshtastic_MeshPacket LOG_INFO.getNodeNum(), mp.from, mp.to, mp.id, p.payload.size, p.payload.bytes); */ - if (getFrom(&mp) != nodeDB->getNodeNum()) { + if (!isFromUs(&mp)) { if (moduleConfig.range_test.save) { appendFile(mp); diff --git a/src/modules/RemoteHardwareModule.cpp b/src/modules/RemoteHardwareModule.cpp index 6910005a8..43612e450 100644 --- a/src/modules/RemoteHardwareModule.cpp +++ b/src/modules/RemoteHardwareModule.cpp @@ -5,6 +5,7 @@ #include "Router.h" #include "configuration.h" #include "main.h" +#include #define NUM_GPIOS 64 @@ -14,26 +15,44 @@ // a max of one change per 30 seconds #define WATCH_INTERVAL_MSEC (30 * 1000) +// Tests for access to read from or write to a specified GPIO pin +static bool pinAccessAllowed(uint64_t mask, uint8_t pin) +{ + // If undefined pin access is allowed, don't check the pin and just return true + if (moduleConfig.remote_hardware.allow_undefined_pin_access) { + return true; + } + + // Test to see if the pin is in the list of allowed pins and return true if found + if (mask & (1ULL << pin)) { + return true; + } + + return false; +} + /// Set pin modes for every set bit in a mask -static void pinModes(uint64_t mask, uint8_t mode) +static void pinModes(uint64_t mask, uint8_t mode, uint64_t maskAvailable) { for (uint64_t i = 0; i < NUM_GPIOS; i++) { if (mask & (1ULL << i)) { - pinMode(i, mode); + if (pinAccessAllowed(maskAvailable, i)) { + pinMode(i, mode); + } } } } /// Read all the pins mentioned in a mask -static uint64_t digitalReads(uint64_t mask) +static uint64_t digitalReads(uint64_t mask, uint64_t maskAvailable) { uint64_t res = 0; - pinModes(mask, INPUT_PULLUP); + pinModes(mask, INPUT_PULLUP, maskAvailable); for (uint64_t i = 0; i < NUM_GPIOS; i++) { uint64_t m = 1ULL << i; - if (mask & m) { + if (mask & m && pinAccessAllowed(maskAvailable, i)) { if (digitalRead(i)) { res |= m; } @@ -47,6 +66,13 @@ RemoteHardwareModule::RemoteHardwareModule() : ProtobufModule("remotehardware", meshtastic_PortNum_REMOTE_HARDWARE_APP, &meshtastic_HardwareMessage_msg), concurrency::OSThread("RemoteHardwareModule") { + // restrict to the gpio channel for rx + boundChannel = Channels::gpioChannel; + + // Pull available pin allowlist from config and build a bitmask out of it for fast comparisons later + for (uint8_t i = 0; i < 4; i++) { + availablePins += 1ULL << moduleConfig.remote_hardware.available_pins[i].gpio_pin; + } } bool RemoteHardwareModule::handleReceivedProtobuf(const meshtastic_MeshPacket &req, meshtastic_HardwareMessage *pptr) @@ -60,10 +86,10 @@ bool RemoteHardwareModule::handleReceivedProtobuf(const meshtastic_MeshPacket &r // Print notification to LCD screen screen->print("Write GPIOs\n"); - pinModes(p.gpio_mask, OUTPUT); + pinModes(p.gpio_mask, OUTPUT, availablePins); for (uint8_t i = 0; i < NUM_GPIOS; i++) { uint64_t mask = 1ULL << i; - if (p.gpio_mask & mask) { + if (p.gpio_mask & mask && pinAccessAllowed(availablePins, i)) { digitalWrite(i, (p.gpio_value & mask) ? 1 : 0); } } @@ -76,7 +102,7 @@ bool RemoteHardwareModule::handleReceivedProtobuf(const meshtastic_MeshPacket &r if (screen) screen->print("Read GPIOs\n"); - uint64_t res = digitalReads(p.gpio_mask); + uint64_t res = digitalReads(p.gpio_mask, availablePins); // Send the reply meshtastic_HardwareMessage r = meshtastic_HardwareMessage_init_default; @@ -116,11 +142,10 @@ bool RemoteHardwareModule::handleReceivedProtobuf(const meshtastic_MeshPacket &r int32_t RemoteHardwareModule::runOnce() { if (moduleConfig.remote_hardware.enabled && watchGpios) { - uint32_t now = millis(); - if (now - lastWatchMsec >= WATCH_INTERVAL_MSEC) { - uint64_t curVal = digitalReads(watchGpios); - lastWatchMsec = now; + if (!Throttle::isWithinTimespanMs(lastWatchMsec, WATCH_INTERVAL_MSEC)) { + uint64_t curVal = digitalReads(watchGpios, availablePins); + lastWatchMsec = millis(); if (curVal != previousWatch) { previousWatch = curVal; diff --git a/src/modules/RemoteHardwareModule.h b/src/modules/RemoteHardwareModule.h index dd39f5b69..4dc31d405 100644 --- a/src/modules/RemoteHardwareModule.h +++ b/src/modules/RemoteHardwareModule.h @@ -17,6 +17,9 @@ class RemoteHardwareModule : public ProtobufModule, /// The timestamp of our last watch event (we throttle watches to 1 change every 30 seconds) uint32_t lastWatchMsec = 0; + /// A bitmask of GPIOs that are exposed to the mesh if undefined access is not enabled + uint64_t availablePins = 0; + public: /** Constructor * name is for debugging output diff --git a/src/modules/RoutingModule.cpp b/src/modules/RoutingModule.cpp index b7be4abc9..3b7be1ab7 100644 --- a/src/modules/RoutingModule.cpp +++ b/src/modules/RoutingModule.cpp @@ -28,7 +28,7 @@ bool RoutingModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mesh // FIXME - move this to a non promsicious PhoneAPI module? // Note: we are careful not to send back packets that started with the phone back to the phone - if ((mp.to == NODENUM_BROADCAST || mp.to == nodeDB->getNodeNum()) && (mp.from != 0)) { + if ((mp.to == NODENUM_BROADCAST || isToUs(&mp)) && (mp.from != 0)) { printPacket("Delivering rx packet", &mp); service->handleFromRadio(&mp); } diff --git a/src/modules/SerialModule.cpp b/src/modules/SerialModule.cpp index f0ba64f65..d40b59345 100644 --- a/src/modules/SerialModule.cpp +++ b/src/modules/SerialModule.cpp @@ -7,6 +7,7 @@ #include "Router.h" #include "configuration.h" #include +#include /* SerialModule @@ -62,6 +63,9 @@ SerialModuleRadio *serialModuleRadio; #if defined(TTGO_T_ECHO) || defined(CANARYONE) SerialModule::SerialModule() : StreamAPI(&Serial), concurrency::OSThread("SerialModule") {} static Print *serialPrint = &Serial; +#elif defined(CONFIG_IDF_TARGET_ESP32C6) +SerialModule::SerialModule() : StreamAPI(&Serial1), concurrency::OSThread("SerialModule") {} +static Print *serialPrint = &Serial1; #else SerialModule::SerialModule() : StreamAPI(&Serial2), concurrency::OSThread("SerialModule") {} static Print *serialPrint = &Serial2; @@ -97,8 +101,7 @@ SerialModuleRadio::SerialModuleRadio() : MeshModule("SerialModuleRadio") */ bool SerialModule::checkIsConnected() { - uint32_t now = millis(); - return (now - lastContactMsec) < SERIAL_CONNECTION_TIMEOUT; + return Throttle::isWithinTimespanMs(lastContactMsec, SERIAL_CONNECTION_TIMEOUT); } int32_t SerialModule::runOnce() @@ -137,7 +140,16 @@ int32_t SerialModule::runOnce() // Give it a chance to flush out 💩 delay(10); } -#ifdef ARCH_ESP32 +#if defined(CONFIG_IDF_TARGET_ESP32C6) + if (moduleConfig.serial.rxd && moduleConfig.serial.txd) { + Serial1.setRxBufferSize(RX_BUFFER); + Serial1.begin(baud, SERIAL_8N1, moduleConfig.serial.rxd, moduleConfig.serial.txd); + } else { + Serial.begin(baud); + Serial.setTimeout(moduleConfig.serial.timeout > 0 ? moduleConfig.serial.timeout : TIMEOUT); + } + +#elif defined(ARCH_ESP32) if (moduleConfig.serial.rxd && moduleConfig.serial.txd) { Serial2.setRxBufferSize(RX_BUFFER); @@ -182,13 +194,13 @@ int32_t SerialModule::runOnce() return runOncePart(); } else if ((moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_NMEA) && HAS_GPS) { // in NMEA mode send out GGA every 2 seconds, Don't read from Port - if (millis() - lastNmeaTime > 2000) { + if (!Throttle::isWithinTimespanMs(lastNmeaTime, 2000)) { lastNmeaTime = millis(); printGGA(outbuf, sizeof(outbuf), localPosition); serialPrint->printf("%s", outbuf); } } else if ((moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_CALTOPO) && HAS_GPS) { - if (millis() - lastNmeaTime > 10000) { + if (!Throttle::isWithinTimespanMs(lastNmeaTime, 10000)) { lastNmeaTime = millis(); uint32_t readIndex = 0; const meshtastic_NodeInfoLite *tempNodeInfo = nodeDB->readNextMeshNode(readIndex); @@ -205,8 +217,13 @@ int32_t SerialModule::runOnce() processWXSerial(); } else { +#if defined(CONFIG_IDF_TARGET_ESP32C6) + while (Serial1.available()) { + serialPayloadSize = Serial1.readBytes(serialBytes, meshtastic_Constants_DATA_PAYLOAD_LEN); +#else while (Serial2.available()) { serialPayloadSize = Serial2.readBytes(serialBytes, meshtastic_Constants_DATA_PAYLOAD_LEN); +#endif serialModuleRadio->sendPayload(); } } @@ -293,7 +310,7 @@ ProcessMessage SerialModuleRadio::handleReceived(const meshtastic_MeshPacket &mp // LOG_DEBUG("Received text msg self=0x%0x, from=0x%0x, to=0x%0x, id=%d, msg=%.*s\n", // nodeDB->getNodeNum(), mp.from, mp.to, mp.id, p.payload.size, p.payload.bytes); - if (getFrom(&mp) == nodeDB->getNodeNum()) { + if (!isFromUs(&mp)) { /* * If moduleConfig.serial.echo is true, then echo the packets that are sent out @@ -392,7 +409,7 @@ uint32_t SerialModule::getBaudRate() */ void SerialModule::processWXSerial() { -#if !defined(TTGO_T_ECHO) && !defined(CANARYONE) +#if !defined(TTGO_T_ECHO) && !defined(CANARYONE) && !defined(CONFIG_IDF_TARGET_ESP32C6) static unsigned int lastAveraged = 0; static unsigned int averageIntervalMillis = 300000; // 5 minutes hard coded. static double dir_sum_sin = 0; @@ -500,7 +517,7 @@ void SerialModule::processWXSerial() LOG_INFO("WS85 : %i %.1fg%.1f %.1fv %.1fv\n", atoi(windDir), strtof(windVel, nullptr), strtof(windGust, nullptr), batVoltageF, capVoltageF); } - if (gotwind && millis() - lastAveraged > averageIntervalMillis) { + if (gotwind && !Throttle::isWithinTimespanMs(lastAveraged, averageIntervalMillis)) { // calulate averages and send to the mesh float velAvg = 1.0 * velSum / velCount; diff --git a/src/modules/esp32/StoreForwardModule.cpp b/src/modules/StoreForwardModule.cpp similarity index 89% rename from src/modules/esp32/StoreForwardModule.cpp rename to src/modules/StoreForwardModule.cpp index c696d342a..e0092839f 100644 --- a/src/modules/esp32/StoreForwardModule.cpp +++ b/src/modules/StoreForwardModule.cpp @@ -17,6 +17,7 @@ #include "NodeDB.h" #include "RTC.h" #include "Router.h" +#include "Throttle.h" #include "airtime.h" #include "configuration.h" #include "memGet.h" @@ -31,7 +32,7 @@ StoreForwardModule *storeForwardModule; int32_t StoreForwardModule::runOnce() { -#ifdef ARCH_ESP32 +#if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) if (moduleConfig.store_forward.enabled && is_server) { // Send out the message queue. if (this->busy) { @@ -42,10 +43,10 @@ int32_t StoreForwardModule::runOnce() this->busy = false; } } - } else if (this->heartbeat && (millis() - lastHeartbeat > (heartbeatInterval * 1000)) && + } else if (this->heartbeat && (!Throttle::isWithinTimespanMs(lastHeartbeat, heartbeatInterval * 1000)) && airTime->isTxAllowedChannelUtil(true)) { lastHeartbeat = millis(); - LOG_INFO("*** Sending heartbeat\n"); + LOG_INFO("Sending heartbeat\n"); meshtastic_StoreAndForward sf = meshtastic_StoreAndForward_init_zero; sf.rr = meshtastic_StoreAndForward_RequestResponse_ROUTER_HEARTBEAT; sf.which_variant = meshtastic_StoreAndForward_heartbeat_tag; @@ -69,8 +70,8 @@ void StoreForwardModule::populatePSRAM() https://learn.upesy.com/en/programmation/psram.html#psram-tab */ - LOG_DEBUG("*** Before PSRAM initialization: heap %d/%d PSRAM %d/%d\n", memGet.getFreeHeap(), memGet.getHeapSize(), - memGet.getFreePsram(), memGet.getPsramSize()); + LOG_DEBUG("Before PSRAM init: heap %d/%d PSRAM %d/%d\n", memGet.getFreeHeap(), memGet.getHeapSize(), memGet.getFreePsram(), + memGet.getPsramSize()); /* Use a maximum of 2/3 the available PSRAM unless otherwise specified. Note: This needs to be done after every thing that would use PSRAM @@ -78,12 +79,16 @@ void StoreForwardModule::populatePSRAM() uint32_t numberOfPackets = (this->records ? this->records : (((memGet.getFreePsram() / 3) * 2) / sizeof(PacketHistoryStruct))); this->records = numberOfPackets; - +#if defined(ARCH_ESP32) this->packetHistory = static_cast(ps_calloc(numberOfPackets, sizeof(PacketHistoryStruct))); +#elif defined(ARCH_PORTDUINO) + this->packetHistory = static_cast(calloc(numberOfPackets, sizeof(PacketHistoryStruct))); - LOG_DEBUG("*** After PSRAM initialization: heap %d/%d PSRAM %d/%d\n", memGet.getFreeHeap(), memGet.getHeapSize(), - memGet.getFreePsram(), memGet.getPsramSize()); - LOG_DEBUG("*** numberOfPackets for packetHistory - %u\n", numberOfPackets); +#endif + + LOG_DEBUG("After PSRAM init: heap %d/%d PSRAM %d/%d\n", memGet.getFreeHeap(), memGet.getHeapSize(), memGet.getFreePsram(), + memGet.getPsramSize()); + LOG_DEBUG("numberOfPackets for packetHistory - %u\n", numberOfPackets); } /** @@ -100,11 +105,11 @@ void StoreForwardModule::historySend(uint32_t secAgo, uint32_t to) queueSize = this->historyReturnMax; if (queueSize) { - LOG_INFO("*** S&F - Sending %u message(s)\n", queueSize); + LOG_INFO("S&F - Sending %u message(s)\n", queueSize); this->busy = true; // runOnce() will pickup the next steps once busy = true. this->busyTo = to; } else { - LOG_INFO("*** S&F - No history to send\n"); + LOG_INFO("S&F - No history\n"); } meshtastic_StoreAndForward sf = meshtastic_StoreAndForward_init_zero; sf.rr = meshtastic_StoreAndForward_RequestResponse_ROUTER_HISTORY; @@ -127,7 +132,7 @@ uint32_t StoreForwardModule::getNumAvailablePackets(NodeNum dest, uint32_t last_ { uint32_t count = 0; if (lastRequest.find(dest) == lastRequest.end()) { - lastRequest[dest] = 0; + lastRequest.emplace(dest, 0); } for (uint32_t i = lastRequest[dest]; i < this->packetHistoryTotalCount; i++) { if (this->packetHistory[i].time && (this->packetHistory[i].time > last_time)) { @@ -182,7 +187,7 @@ void StoreForwardModule::historyAdd(const meshtastic_MeshPacket &mp) const auto &p = mp.decoded; if (this->packetHistoryTotalCount == this->records) { - LOG_WARN("*** S&F - PSRAM Full. Starting overwrite now.\n"); + LOG_WARN("S&F - PSRAM Full. Starting overwrite.\n"); this->packetHistoryTotalCount = 0; for (auto &i : lastRequest) { i.second = 0; // Clear the last request index for each client device @@ -210,7 +215,7 @@ bool StoreForwardModule::sendPayload(NodeNum dest, uint32_t last_time) { meshtastic_MeshPacket *p = preparePayload(dest, last_time); if (p) { - LOG_INFO("*** Sending S&F Payload\n"); + LOG_INFO("Sending S&F Payload\n"); service->sendToMesh(p); this->requestCount++; return true; @@ -326,9 +331,9 @@ void StoreForwardModule::sendErrorTextMessage(NodeNum dest, bool want_response) pr->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP; const char *str; if (this->busy) { - str = "** S&F - Busy. Try again shortly."; + str = "S&F - Busy. Try again shortly."; } else { - str = "** S&F - Not available on this channel."; + str = "S&F not permitted on the public channel."; } LOG_WARN("%s\n", str); memcpy(pr->decoded.payload.bytes, str, strlen(str)); @@ -360,7 +365,7 @@ void StoreForwardModule::statsSend(uint32_t to) sf.variant.stats.return_max = this->historyReturnMax; sf.variant.stats.return_window = this->historyReturnWindow; - LOG_DEBUG("*** Sending S&F Stats\n"); + LOG_DEBUG("Sending S&F Stats\n"); storeForwardModule->sendMessage(to, sf); } @@ -372,14 +377,13 @@ void StoreForwardModule::statsSend(uint32_t to) */ ProcessMessage StoreForwardModule::handleReceived(const meshtastic_MeshPacket &mp) { -#ifdef ARCH_ESP32 +#if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) if (moduleConfig.store_forward.enabled) { if ((mp.decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP) && is_server) { auto &p = mp.decoded; - if (mp.to == nodeDB->getNodeNum() && (p.payload.bytes[0] == 'S') && (p.payload.bytes[1] == 'F') && - (p.payload.bytes[2] == 0x00)) { - LOG_DEBUG("*** Legacy Request to send\n"); + if (isToUs(&mp) && (p.payload.bytes[0] == 'S') && (p.payload.bytes[1] == 'F') && (p.payload.bytes[2] == 0x00)) { + LOG_DEBUG("Legacy Request to send\n"); // Send the last 60 minutes of messages. if (this->busy || channels.isDefaultChannel(mp.channel)) { @@ -389,9 +393,9 @@ ProcessMessage StoreForwardModule::handleReceived(const meshtastic_MeshPacket &m } } else { storeForwardModule->historyAdd(mp); - LOG_INFO("*** S&F stored. Message history contains %u records now.\n", this->packetHistoryTotalCount); + LOG_INFO("S&F stored. Message history contains %u records now.\n", this->packetHistoryTotalCount); } - } else if (getFrom(&mp) != nodeDB->getNodeNum() && mp.decoded.portnum == meshtastic_PortNum_STORE_FORWARD_APP) { + } else if (!isFromUs(&mp) && mp.decoded.portnum == meshtastic_PortNum_STORE_FORWARD_APP) { auto &p = mp.decoded; meshtastic_StoreAndForward scratch; meshtastic_StoreAndForward *decoded = NULL; @@ -435,7 +439,7 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, if (is_server) { // stop sending stuff, the client wants to abort or has another error if ((this->busy) && (this->busyTo == getFrom(&mp))) { - LOG_ERROR("*** Client in ERROR or ABORT requested\n"); + LOG_ERROR("Client in ERROR or ABORT requested\n"); this->requestCount = 0; this->busy = false; } @@ -445,7 +449,7 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, case meshtastic_StoreAndForward_RequestResponse_CLIENT_HISTORY: if (is_server) { requests_history++; - LOG_INFO("*** Client Request to send HISTORY\n"); + LOG_INFO("Client Request to send HISTORY\n"); // Send the last 60 minutes of messages. if (this->busy || channels.isDefaultChannel(mp.channel)) { sendErrorTextMessage(getFrom(&mp), mp.decoded.want_response); @@ -462,7 +466,6 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, case meshtastic_StoreAndForward_RequestResponse_CLIENT_PING: if (is_server) { - LOG_INFO("*** StoreAndForward_RequestResponse_CLIENT_PING\n"); // respond with a ROUTER PONG storeForwardModule->sendMessage(getFrom(&mp), meshtastic_StoreAndForward_RequestResponse_ROUTER_PONG); } @@ -470,17 +473,16 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, case meshtastic_StoreAndForward_RequestResponse_CLIENT_PONG: if (is_server) { - LOG_INFO("*** StoreAndForward_RequestResponse_CLIENT_PONG\n"); // NodeDB is already updated } break; case meshtastic_StoreAndForward_RequestResponse_CLIENT_STATS: if (is_server) { - LOG_INFO("*** Client Request to send STATS\n"); + LOG_INFO("Client Request to send STATS\n"); if (this->busy) { storeForwardModule->sendMessage(getFrom(&mp), meshtastic_StoreAndForward_RequestResponse_ROUTER_BUSY); - LOG_INFO("*** S&F - Busy. Try again shortly.\n"); + LOG_INFO("S&F - Busy. Try again shortly.\n"); } else { storeForwardModule->statsSend(getFrom(&mp)); } @@ -490,7 +492,7 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, case meshtastic_StoreAndForward_RequestResponse_ROUTER_ERROR: case meshtastic_StoreAndForward_RequestResponse_ROUTER_BUSY: if (is_client) { - LOG_DEBUG("*** StoreAndForward_RequestResponse_ROUTER_BUSY\n"); + LOG_DEBUG("StoreAndForward_RequestResponse_ROUTER_BUSY\n"); // retry in messages_saved * packetTimeMax ms retry_delay = millis() + getNumAvailablePackets(this->busyTo, this->last_time) * packetTimeMax * (meshtastic_StoreAndForward_RequestResponse_ROUTER_ERROR ? 2 : 1); @@ -506,13 +508,12 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, heartbeatInterval = p->variant.heartbeat.period; } lastHeartbeat = millis(); - LOG_INFO("*** StoreAndForward Heartbeat received\n"); + LOG_INFO("StoreAndForward Heartbeat received\n"); } break; case meshtastic_StoreAndForward_RequestResponse_ROUTER_PING: if (is_client) { - LOG_DEBUG("*** StoreAndForward_RequestResponse_ROUTER_PING\n"); // respond with a CLIENT PONG storeForwardModule->sendMessage(getFrom(&mp), meshtastic_StoreAndForward_RequestResponse_CLIENT_PONG); } @@ -520,7 +521,7 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, case meshtastic_StoreAndForward_RequestResponse_ROUTER_STATS: if (is_client) { - LOG_DEBUG("*** Router Response STATS\n"); + LOG_DEBUG("Router Response STATS\n"); // These fields only have informational purpose on a client. Fill them to consume later. if (p->which_variant == meshtastic_StoreAndForward_stats_tag) { this->records = p->variant.stats.messages_max; @@ -538,7 +539,7 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, // These fields only have informational purpose on a client. Fill them to consume later. if (p->which_variant == meshtastic_StoreAndForward_history_tag) { this->historyReturnWindow = p->variant.history.window / 60000; - LOG_INFO("*** Router Response HISTORY - Sending %d messages from last %d minutes\n", + LOG_INFO("Router Response HISTORY - Sending %d messages from last %d minutes\n", p->variant.history.history_messages, this->historyReturnWindow); } } @@ -555,7 +556,7 @@ StoreForwardModule::StoreForwardModule() ProtobufModule("StoreForward", meshtastic_PortNum_STORE_FORWARD_APP, &meshtastic_StoreAndForward_msg) { -#ifdef ARCH_ESP32 +#if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) isPromiscuous = true; // Brown chicken brown cow @@ -572,7 +573,7 @@ StoreForwardModule::StoreForwardModule() // Router if ((config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER || moduleConfig.store_forward.is_server)) { - LOG_INFO("*** Initializing Store & Forward Module in Server mode\n"); + LOG_INFO("Initializing Store & Forward Module in Server mode\n"); if (memGet.getPsramSize() > 0) { if (memGet.getFreePsram() >= 1024 * 1024) { @@ -600,18 +601,17 @@ StoreForwardModule::StoreForwardModule() this->populatePSRAM(); is_server = true; } else { - LOG_INFO("*** Device has less than 1M of PSRAM free.\n"); - LOG_INFO("*** Store & Forward Module - disabling server.\n"); + LOG_INFO(".\n"); + LOG_INFO("S&F: not enough PSRAM free, disabling.\n"); } } else { - LOG_INFO("*** Device doesn't have PSRAM.\n"); - LOG_INFO("*** Store & Forward Module - disabling server.\n"); + LOG_INFO("S&F: device doesn't have PSRAM, disabling.\n"); } // Client } else { is_client = true; - LOG_INFO("*** Initializing Store & Forward Module in Client mode\n"); + LOG_INFO("Initializing Store & Forward Module in Client mode\n"); } } else { disable(); diff --git a/src/modules/esp32/StoreForwardModule.h b/src/modules/StoreForwardModule.h similarity index 100% rename from src/modules/esp32/StoreForwardModule.h rename to src/modules/StoreForwardModule.h diff --git a/src/modules/Telemetry/AirQualityTelemetry.cpp b/src/modules/Telemetry/AirQualityTelemetry.cpp index d07296710..0b6be1b7e 100644 --- a/src/modules/Telemetry/AirQualityTelemetry.cpp +++ b/src/modules/Telemetry/AirQualityTelemetry.cpp @@ -10,11 +10,12 @@ #include "PowerFSM.h" #include "RTC.h" #include "Router.h" +#include "detect/ScanI2CTwoWire.h" #include "main.h" +#include int32_t AirQualityTelemetryModule::runOnce() { - int32_t result = INT32_MAX; /* Uncomment the preferences below if you want to use the module without having to configure it from the PythonAPI or WebUI. @@ -29,31 +30,45 @@ int32_t AirQualityTelemetryModule::runOnce() if (firstTime) { // This is the first time the OSThread library has called this function, so do some setup - firstTime = 0; + firstTime = false; if (moduleConfig.telemetry.air_quality_enabled) { LOG_INFO("Air quality Telemetry: Initializing\n"); if (!aqi.begin_I2C()) { - LOG_WARN("Could not establish i2c connection to AQI sensor\n"); + LOG_WARN("Could not establish i2c connection to AQI sensor. Rescanning...\n"); + // rescan for late arriving sensors. AQI Module starts about 10 seconds into the boot so this is plenty. + uint8_t i2caddr_scan[] = {PMSA0031_ADDR}; + uint8_t i2caddr_asize = 1; + auto i2cScanner = std::unique_ptr(new ScanI2CTwoWire()); +#if defined(I2C_SDA1) + i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1, i2caddr_scan, i2caddr_asize); +#endif + i2cScanner->scanPort(ScanI2C::I2CPort::WIRE, i2caddr_scan, i2caddr_asize); + auto found = i2cScanner->find(ScanI2C::DeviceType::PMSA0031); + if (found.type != ScanI2C::DeviceType::NONE) { + nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].first = found.address.address; + nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].second = + i2cScanner->fetchI2CBus(found.address); + return 1000; + } return disable(); } return 1000; } - return result; + return disable(); } else { // if we somehow got to a second run of this module with measurement disabled, then just wait forever if (!moduleConfig.telemetry.air_quality_enabled) - return result; + return disable(); - uint32_t now = millis(); if (((lastSentToMesh == 0) || - ((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMsScaled(moduleConfig.telemetry.air_quality_interval, - default_telemetry_broadcast_interval_secs, - numOnlineNodes))) && + !Throttle::isWithinTimespanMs(lastSentToMesh, Default::getConfiguredOrDefaultMsScaled( + moduleConfig.telemetry.air_quality_interval, + default_telemetry_broadcast_interval_secs, numOnlineNodes))) && airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) && airTime->isTxAllowedAirUtil()) { sendTelemetry(); - lastSentToMesh = now; + lastSentToMesh = millis(); } else if (service->isToPhoneQueueEmpty()) { // Just send to phone when it's not our time to send to mesh yet // Only send while queue is empty (phone assumed connected) diff --git a/src/modules/Telemetry/AirQualityTelemetry.h b/src/modules/Telemetry/AirQualityTelemetry.h index 23df6ac58..fb8edd07e 100644 --- a/src/modules/Telemetry/AirQualityTelemetry.h +++ b/src/modules/Telemetry/AirQualityTelemetry.h @@ -44,7 +44,7 @@ class AirQualityTelemetryModule : private concurrency::OSThread, public Protobuf private: Adafruit_PM25AQI aqi; PM25_AQI_Data data = {0}; - bool firstTime = 1; + bool firstTime = true; meshtastic_MeshPacket *lastMeasurementPacket; uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute uint32_t lastSentToMesh = 0; diff --git a/src/modules/Telemetry/DeviceTelemetry.cpp b/src/modules/Telemetry/DeviceTelemetry.cpp index f22685d43..eb3f67e96 100644 --- a/src/modules/Telemetry/DeviceTelemetry.cpp +++ b/src/modules/Telemetry/DeviceTelemetry.cpp @@ -11,14 +11,15 @@ #include "main.h" #include #include +#include #define MAGIC_USB_BATTERY_LEVEL 101 int32_t DeviceTelemetryModule::runOnce() { refreshUptime(); - bool isImpoliteRole = config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR || - config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER; + bool isImpoliteRole = + IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_SENSOR, meshtastic_Config_DeviceConfig_Role_ROUTER); if (((lastSentToMesh == 0) || ((uptimeLastMs - lastSentToMesh) >= Default::getConfiguredOrDefaultMsScaled(moduleConfig.telemetry.device_update_interval, @@ -100,8 +101,9 @@ meshtastic_Telemetry DeviceTelemetryModule::getDeviceTelemetry() #if ARCH_PORTDUINO t.variant.device_metrics.battery_level = MAGIC_USB_BATTERY_LEVEL; #else - t.variant.device_metrics.battery_level = - powerStatus->getIsCharging() ? MAGIC_USB_BATTERY_LEVEL : powerStatus->getBatteryChargePercent(); + t.variant.device_metrics.battery_level = (!powerStatus->getHasBattery() || powerStatus->getIsCharging()) + ? MAGIC_USB_BATTERY_LEVEL + : powerStatus->getBatteryChargePercent(); #endif t.variant.device_metrics.channel_utilization = airTime->channelUtilizationPercent(); t.variant.device_metrics.voltage = powerStatus->getBatteryVoltageMv() / 1000.0; @@ -123,8 +125,13 @@ void DeviceTelemetryModule::sendLocalStatsToPhone() telemetry.variant.local_stats.num_total_nodes = nodeDB->getNumMeshNodes(); if (RadioLibInterface::instance) { telemetry.variant.local_stats.num_packets_tx = RadioLibInterface::instance->txGood; - telemetry.variant.local_stats.num_packets_rx = RadioLibInterface::instance->rxGood; + telemetry.variant.local_stats.num_packets_rx = RadioLibInterface::instance->rxGood + RadioLibInterface::instance->rxBad; telemetry.variant.local_stats.num_packets_rx_bad = RadioLibInterface::instance->rxBad; + telemetry.variant.local_stats.num_tx_relay = RadioLibInterface::instance->txRelay; + } + if (router) { + telemetry.variant.local_stats.num_rx_dupe = router->rxDupe; + telemetry.variant.local_stats.num_tx_relay_canceled = router->txRelayCanceled; } LOG_INFO( diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index 31cb2f838..1ccdedeb7 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -10,6 +10,7 @@ #include "PowerFSM.h" #include "RTC.h" #include "Router.h" +#include "UnitConversions.h" #include "main.h" #include "power.h" #include "sleep.h" @@ -64,6 +65,7 @@ T1000xSensor t1000xSensor; #define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true #include "graphics/ScreenFonts.h" +#include int32_t EnvironmentTelemetryModule::runOnce() { @@ -82,7 +84,7 @@ int32_t EnvironmentTelemetryModule::runOnce() */ // moduleConfig.telemetry.environment_measurement_enabled = 1; - // moduleConfig.telemetry.environment_screen_enabled = 1; + // moduleConfig.telemetry.environment_screen_enabled = 1; // moduleConfig.telemetry.environment_update_interval = 15; if (!(moduleConfig.telemetry.environment_measurement_enabled || moduleConfig.telemetry.environment_screen_enabled)) { @@ -143,6 +145,8 @@ int32_t EnvironmentTelemetryModule::runOnce() result = mlx90632Sensor.runOnce(); if (nau7802Sensor.hasSensor()) result = nau7802Sensor.runOnce(); + if (max17048Sensor.hasSensor()) + result = max17048Sensor.runOnce(); #endif } return result; @@ -155,21 +159,20 @@ int32_t EnvironmentTelemetryModule::runOnce() result = bme680Sensor.runTrigger(); } - uint32_t now = millis(); if (((lastSentToMesh == 0) || - ((now - lastSentToMesh) >= - Default::getConfiguredOrDefaultMsScaled(moduleConfig.telemetry.environment_update_interval, - default_telemetry_broadcast_interval_secs, numOnlineNodes))) && + !Throttle::isWithinTimespanMs(lastSentToMesh, Default::getConfiguredOrDefaultMsScaled( + moduleConfig.telemetry.environment_update_interval, + default_telemetry_broadcast_interval_secs, numOnlineNodes))) && airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) && airTime->isTxAllowedAirUtil()) { sendTelemetry(); - lastSentToMesh = now; - } else if (((lastSentToPhone == 0) || ((now - lastSentToPhone) >= sendToPhoneIntervalMs)) && + lastSentToMesh = millis(); + } else if (((lastSentToPhone == 0) || !Throttle::isWithinTimespanMs(lastSentToPhone, sendToPhoneIntervalMs)) && (service->isToPhoneQueueEmpty())) { // Just send to phone when it's not our time to send to mesh yet // Only send while queue is empty (phone assumed connected) sendTelemetry(NODENUM_BROADCAST, true); - lastSentToPhone = now; + lastSentToPhone = millis(); } } return min(sendToPhoneIntervalMs, result); @@ -180,23 +183,6 @@ bool EnvironmentTelemetryModule::wantUIFrame() return moduleConfig.telemetry.environment_screen_enabled; } -float EnvironmentTelemetryModule::CelsiusToFahrenheit(float c) -{ - return (c * 9) / 5 + 32; -} - -uint32_t GetTimeSinceMeshPacket(const meshtastic_MeshPacket *mp) -{ - uint32_t now = getTime(); - - uint32_t last_seen = mp->rx_time; - int delta = (int)(now - last_seen); - if (delta < 0) // our clock must be slightly off still - not set from GPS yet - delta = 0; - - return delta; -} - void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { display->setTextAlignment(TEXT_ALIGN_LEFT); @@ -211,10 +197,10 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt // Decode the last measurement packet meshtastic_Telemetry lastMeasurement; - uint32_t agoSecs = GetTimeSinceMeshPacket(lastMeasurementPacket); + uint32_t agoSecs = service->GetTimeSinceMeshPacket(lastMeasurementPacket); const char *lastSender = getSenderShortName(*lastMeasurementPacket); - auto &p = lastMeasurementPacket->decoded; + const meshtastic_Data &p = lastMeasurementPacket->decoded; if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &lastMeasurement)) { display->drawString(x, y, "Measurement Error"); LOG_ERROR("Unable to decode last packet"); @@ -226,7 +212,8 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt String last_temp = String(lastMeasurement.variant.environment_metrics.temperature, 0) + "°C"; if (moduleConfig.telemetry.environment_display_fahrenheit) { - last_temp = String(CelsiusToFahrenheit(lastMeasurement.variant.environment_metrics.temperature), 0) + "°F"; + last_temp = + String(UnitConversions::CelsiusToFahrenheit(lastMeasurement.variant.environment_metrics.temperature), 0) + "°F"; } // Continue with the remaining details @@ -397,6 +384,10 @@ bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m m->variant.environment_metrics.relative_humidity = m_ahtx.variant.environment_metrics.relative_humidity; } } + if (max17048Sensor.hasSensor()) { + valid = valid && max17048Sensor.getMetrics(m); + hasSensor = true; + } #endif return valid && hasSensor; @@ -587,6 +578,11 @@ AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule if (result != AdminMessageHandleResult::NOT_HANDLED) return result; } + if (max17048Sensor.hasSensor()) { + result = max17048Sensor.handleAdminMessage(mp, request, response); + if (result != AdminMessageHandleResult::NOT_HANDLED) + return result; + } return result; } diff --git a/src/modules/Telemetry/EnvironmentTelemetry.h b/src/modules/Telemetry/EnvironmentTelemetry.h index 59d272e78..e680d8bbd 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.h +++ b/src/modules/Telemetry/EnvironmentTelemetry.h @@ -52,7 +52,6 @@ class EnvironmentTelemetryModule : private concurrency::OSThread, public Protobu meshtastic_AdminMessage *response) override; private: - float CelsiusToFahrenheit(float c); bool firstTime = 1; meshtastic_MeshPacket *lastMeasurementPacket; uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute diff --git a/src/modules/Telemetry/HealthTelemetry.cpp b/src/modules/Telemetry/HealthTelemetry.cpp new file mode 100644 index 000000000..bcf9d9d57 --- /dev/null +++ b/src/modules/Telemetry/HealthTelemetry.cpp @@ -0,0 +1,249 @@ +#include "configuration.h" + +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO) + +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "Default.h" +#include "HealthTelemetry.h" +#include "MeshService.h" +#include "NodeDB.h" +#include "PowerFSM.h" +#include "RTC.h" +#include "Router.h" +#include "UnitConversions.h" +#include "main.h" +#include "power.h" +#include "sleep.h" +#include "target_specific.h" +#include +#include + +// Sensors +#include "Sensor/MAX30102Sensor.h" +#include "Sensor/MLX90614Sensor.h" + +MAX30102Sensor max30102Sensor; +MLX90614Sensor mlx90614Sensor; + +#define FAILED_STATE_SENSOR_READ_MULTIPLIER 10 +#define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true + +#if (HAS_SCREEN) +#include "graphics/ScreenFonts.h" +#endif +#include + +int32_t HealthTelemetryModule::runOnce() +{ + if (sleepOnNextExecution == true) { + sleepOnNextExecution = false; + uint32_t nightyNightMs = Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.health_update_interval, + default_telemetry_broadcast_interval_secs); + LOG_DEBUG("Sleeping for %ims, then awaking to send metrics again.\n", nightyNightMs); + doDeepSleep(nightyNightMs, true); + } + + uint32_t result = UINT32_MAX; + + if (!(moduleConfig.telemetry.health_measurement_enabled || moduleConfig.telemetry.health_screen_enabled)) { + // If this module is not enabled, and the user doesn't want the display screen don't waste any OSThread time on it + return disable(); + } + + if (firstTime) { + // This is the first time the OSThread library has called this function, so do some setup + firstTime = false; + + if (moduleConfig.telemetry.health_measurement_enabled) { + LOG_INFO("Health Telemetry: Initializing\n"); + // Initialize sensors + if (mlx90614Sensor.hasSensor()) + result = mlx90614Sensor.runOnce(); + if (max30102Sensor.hasSensor()) + result = max30102Sensor.runOnce(); + } + return result; + } else { + // if we somehow got to a second run of this module with measurement disabled, then just wait forever + if (!moduleConfig.telemetry.health_measurement_enabled) { + return disable(); + } + + if (((lastSentToMesh == 0) || + !Throttle::isWithinTimespanMs(lastSentToMesh, Default::getConfiguredOrDefaultMsScaled( + moduleConfig.telemetry.health_update_interval, + default_telemetry_broadcast_interval_secs, numOnlineNodes))) && + airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) && + airTime->isTxAllowedAirUtil()) { + sendTelemetry(); + lastSentToMesh = millis(); + } else if (((lastSentToPhone == 0) || !Throttle::isWithinTimespanMs(lastSentToPhone, sendToPhoneIntervalMs)) && + (service->isToPhoneQueueEmpty())) { + // Just send to phone when it's not our time to send to mesh yet + // Only send while queue is empty (phone assumed connected) + sendTelemetry(NODENUM_BROADCAST, true); + lastSentToPhone = millis(); + } + } + return min(sendToPhoneIntervalMs, result); +} + +bool HealthTelemetryModule::wantUIFrame() +{ + return moduleConfig.telemetry.health_screen_enabled; +} + +void HealthTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) +{ + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(FONT_SMALL); + + if (lastMeasurementPacket == nullptr) { + // If there's no valid packet, display "Health" + display->drawString(x, y, "Health"); + display->drawString(x, y += _fontHeight(FONT_SMALL), "No measurement"); + return; + } + + // Decode the last measurement packet + meshtastic_Telemetry lastMeasurement; + uint32_t agoSecs = service->GetTimeSinceMeshPacket(lastMeasurementPacket); + const char *lastSender = getSenderShortName(*lastMeasurementPacket); + + const meshtastic_Data &p = lastMeasurementPacket->decoded; + if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &lastMeasurement)) { + display->drawString(x, y, "Measurement Error"); + LOG_ERROR("Unable to decode last packet"); + return; + } + + // Display "Health From: ..." on its own + display->drawString(x, y, "Health From: " + String(lastSender) + "(" + String(agoSecs) + "s)"); + + String last_temp = String(lastMeasurement.variant.health_metrics.temperature, 0) + "°C"; + if (moduleConfig.telemetry.environment_display_fahrenheit) { + last_temp = String(UnitConversions::CelsiusToFahrenheit(lastMeasurement.variant.health_metrics.temperature), 0) + "°F"; + } + + // Continue with the remaining details + display->drawString(x, y += _fontHeight(FONT_SMALL), "Temp: " + last_temp); + if (lastMeasurement.variant.health_metrics.has_heart_bpm) { + display->drawString(x, y += _fontHeight(FONT_SMALL), + "Heart Rate: " + String(lastMeasurement.variant.health_metrics.heart_bpm, 0) + " bpm"); + } + if (lastMeasurement.variant.health_metrics.has_spO2) { + display->drawString(x, y += _fontHeight(FONT_SMALL), + "spO2: " + String(lastMeasurement.variant.health_metrics.spO2, 0) + " %"); + } +} + +bool HealthTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *t) +{ + if (t->which_variant == meshtastic_Telemetry_health_metrics_tag) { +#ifdef DEBUG_PORT + const char *sender = getSenderShortName(mp); + + LOG_INFO("(Received from %s): temperature=%f, heart_bpm=%d, spO2=%d,\n", sender, t->variant.health_metrics.temperature, + t->variant.health_metrics.heart_bpm, t->variant.health_metrics.spO2); + +#endif + // release previous packet before occupying a new spot + if (lastMeasurementPacket != nullptr) + packetPool.release(lastMeasurementPacket); + + lastMeasurementPacket = packetPool.allocCopy(mp); + } + + return false; // Let others look at this message also if they want +} + +bool HealthTelemetryModule::getHealthTelemetry(meshtastic_Telemetry *m) +{ + bool valid = true; + bool hasSensor = false; + m->time = getTime(); + m->which_variant = meshtastic_Telemetry_health_metrics_tag; + m->variant.health_metrics = meshtastic_HealthMetrics_init_zero; + + if (max30102Sensor.hasSensor()) { + valid = valid && max30102Sensor.getMetrics(m); + hasSensor = true; + } + if (mlx90614Sensor.hasSensor()) { + valid = valid && mlx90614Sensor.getMetrics(m); + hasSensor = true; + } + + return valid && hasSensor; +} + +meshtastic_MeshPacket *HealthTelemetryModule::allocReply() +{ + if (currentRequest) { + auto req = *currentRequest; + const auto &p = req.decoded; + meshtastic_Telemetry scratch; + meshtastic_Telemetry *decoded = NULL; + memset(&scratch, 0, sizeof(scratch)); + if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &scratch)) { + decoded = &scratch; + } else { + LOG_ERROR("Error decoding HealthTelemetry module!\n"); + return NULL; + } + // Check for a request for health metrics + if (decoded->which_variant == meshtastic_Telemetry_health_metrics_tag) { + meshtastic_Telemetry m = meshtastic_Telemetry_init_zero; + if (getHealthTelemetry(&m)) { + LOG_INFO("Health telemetry replying to request\n"); + return allocDataProtobuf(m); + } else { + return NULL; + } + } + } + return NULL; +} + +bool HealthTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) +{ + meshtastic_Telemetry m = meshtastic_Telemetry_init_zero; + m.which_variant = meshtastic_Telemetry_health_metrics_tag; + m.time = getTime(); + if (getHealthTelemetry(&m)) { + LOG_INFO("(Sending): temperature=%f, heart_bpm=%d, spO2=%d\n", m.variant.health_metrics.temperature, + m.variant.health_metrics.heart_bpm, m.variant.health_metrics.spO2); + + sensor_read_error_count = 0; + + meshtastic_MeshPacket *p = allocDataProtobuf(m); + p->to = dest; + p->decoded.want_response = false; + if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR) + p->priority = meshtastic_MeshPacket_Priority_RELIABLE; + else + p->priority = meshtastic_MeshPacket_Priority_BACKGROUND; + // release previous packet before occupying a new spot + if (lastMeasurementPacket != nullptr) + packetPool.release(lastMeasurementPacket); + + lastMeasurementPacket = packetPool.allocCopy(*p); + if (phoneOnly) { + LOG_INFO("Sending packet to phone\n"); + service->sendToPhone(p); + } else { + LOG_INFO("Sending packet to mesh\n"); + service->sendToMesh(p, RX_SRC_LOCAL, true); + + if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR && config.power.is_power_saving) { + LOG_DEBUG("Starting next execution in 5 seconds and then going to sleep.\n"); + sleepOnNextExecution = true; + setIntervalFromNow(5000); + } + } + return true; + } + return false; +} + +#endif diff --git a/src/modules/Telemetry/HealthTelemetry.h b/src/modules/Telemetry/HealthTelemetry.h new file mode 100644 index 000000000..4ad0da838 --- /dev/null +++ b/src/modules/Telemetry/HealthTelemetry.h @@ -0,0 +1,60 @@ +#include "configuration.h" + +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO) + +#pragma once +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "NodeDB.h" +#include "ProtobufModule.h" +#include +#include + +class HealthTelemetryModule : private concurrency::OSThread, public ProtobufModule +{ + CallbackObserver nodeStatusObserver = + CallbackObserver(this, &HealthTelemetryModule::handleStatusUpdate); + + public: + HealthTelemetryModule() + : concurrency::OSThread("HealthTelemetryModule"), + ProtobufModule("HealthTelemetry", meshtastic_PortNum_TELEMETRY_APP, &meshtastic_Telemetry_msg) + { + lastMeasurementPacket = nullptr; + nodeStatusObserver.observe(&nodeStatus->onNewStatus); + setIntervalFromNow(10 * 1000); + } + +#if !HAS_SCREEN + void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); +#else + virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) override; +#endif + + virtual bool wantUIFrame() override; + + protected: + /** Called to handle a particular incoming message + @return true if you've guaranteed you've handled this message and no other handlers should be considered for it + */ + virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override; + virtual int32_t runOnce() override; + /** Called to get current Health telemetry data + @return true if it contains valid data + */ + bool getHealthTelemetry(meshtastic_Telemetry *m); + virtual meshtastic_MeshPacket *allocReply() override; + /** + * Send our Telemetry into the mesh + */ + bool sendTelemetry(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false); + + private: + bool firstTime = 1; + meshtastic_MeshPacket *lastMeasurementPacket; + uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute + uint32_t lastSentToMesh = 0; + uint32_t lastSentToPhone = 0; + uint32_t sensor_read_error_count = 0; +}; + +#endif diff --git a/src/modules/Telemetry/PowerTelemetry.cpp b/src/modules/Telemetry/PowerTelemetry.cpp index 318acf456..be3048998 100644 --- a/src/modules/Telemetry/PowerTelemetry.cpp +++ b/src/modules/Telemetry/PowerTelemetry.cpp @@ -19,6 +19,7 @@ #define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true #include "graphics/ScreenFonts.h" +#include int32_t PowerTelemetryModule::runOnce() { @@ -59,6 +60,8 @@ int32_t PowerTelemetryModule::runOnce() result = ina260Sensor.runOnce(); if (ina3221Sensor.hasSensor() && !ina3221Sensor.isInitialized()) result = ina3221Sensor.runOnce(); + if (max17048Sensor.hasSensor() && !max17048Sensor.isInitialized()) + result = max17048Sensor.runOnce(); } return result; #else @@ -69,20 +72,19 @@ int32_t PowerTelemetryModule::runOnce() if (!moduleConfig.telemetry.power_measurement_enabled) return disable(); - uint32_t now = millis(); if (((lastSentToMesh == 0) || - ((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMsScaled(moduleConfig.telemetry.power_update_interval, - default_telemetry_broadcast_interval_secs, - numOnlineNodes))) && + !Throttle::isWithinTimespanMs(lastSentToMesh, Default::getConfiguredOrDefaultMsScaled( + moduleConfig.telemetry.power_update_interval, + default_telemetry_broadcast_interval_secs, numOnlineNodes))) && airTime->isTxAllowedAirUtil()) { sendTelemetry(); - lastSentToMesh = now; - } else if (((lastSentToPhone == 0) || ((now - lastSentToPhone) >= sendToPhoneIntervalMs)) && + lastSentToMesh = millis(); + } else if (((lastSentToPhone == 0) || !Throttle::isWithinTimespanMs(lastSentToPhone, sendToPhoneIntervalMs)) && (service->isToPhoneQueueEmpty())) { // Just send to phone when it's not our time to send to mesh yet // Only send while queue is empty (phone assumed connected) sendTelemetry(NODENUM_BROADCAST, true); - lastSentToPhone = now; + lastSentToPhone = millis(); } } return min(sendToPhoneIntervalMs, result); @@ -92,18 +94,6 @@ bool PowerTelemetryModule::wantUIFrame() return moduleConfig.telemetry.power_screen_enabled; } -uint32_t GetTimeyWimeySinceMeshPacket(const meshtastic_MeshPacket *mp) -{ - uint32_t now = getTime(); - - uint32_t last_seen = mp->rx_time; - int delta = (int)(now - last_seen); - if (delta < 0) // our clock must be slightly off still - not set from GPS yet - delta = 0; - - return delta; -} - void PowerTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { display->setTextAlignment(TEXT_ALIGN_LEFT); @@ -117,10 +107,10 @@ void PowerTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *s meshtastic_Telemetry lastMeasurement; - uint32_t agoSecs = GetTimeyWimeySinceMeshPacket(lastMeasurementPacket); + uint32_t agoSecs = service->GetTimeSinceMeshPacket(lastMeasurementPacket); const char *lastSender = getSenderShortName(*lastMeasurementPacket); - auto &p = lastMeasurementPacket->decoded; + const meshtastic_Data &p = lastMeasurementPacket->decoded; if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &lastMeasurement)) { display->setFont(FONT_SMALL); display->drawString(x, y += _fontHeight(FONT_MEDIUM), "Measurement Error"); @@ -128,19 +118,23 @@ void PowerTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *s return; } + // Display current and voltage based on ...power_metrics.has_[channel/voltage/current]... flags display->setFont(FONT_SMALL); - String last_temp = String(lastMeasurement.variant.environment_metrics.temperature, 0) + "°C"; display->drawString(x, y += _fontHeight(FONT_MEDIUM) - 2, "From: " + String(lastSender) + "(" + String(agoSecs) + "s)"); - if (lastMeasurement.variant.power_metrics.ch1_voltage != 0) { + if (lastMeasurement.variant.power_metrics.has_ch1_voltage || lastMeasurement.variant.power_metrics.has_ch1_current) { display->drawString(x, y += _fontHeight(FONT_SMALL), - "Ch 1 Volt/Cur: " + String(lastMeasurement.variant.power_metrics.ch1_voltage, 0) + "V / " + - String(lastMeasurement.variant.power_metrics.ch1_current, 0) + "mA"); + "Ch1 Volt: " + String(lastMeasurement.variant.power_metrics.ch1_voltage, 2) + + "V / Curr: " + String(lastMeasurement.variant.power_metrics.ch1_current, 0) + "mA"); + } + if (lastMeasurement.variant.power_metrics.has_ch2_voltage || lastMeasurement.variant.power_metrics.has_ch2_current) { display->drawString(x, y += _fontHeight(FONT_SMALL), - "Ch 2 Volt/Cur: " + String(lastMeasurement.variant.power_metrics.ch2_voltage, 0) + "V / " + - String(lastMeasurement.variant.power_metrics.ch2_current, 0) + "mA"); + "Ch2 Volt: " + String(lastMeasurement.variant.power_metrics.ch2_voltage, 2) + + "V / Curr: " + String(lastMeasurement.variant.power_metrics.ch2_current, 0) + "mA"); + } + if (lastMeasurement.variant.power_metrics.has_ch3_voltage || lastMeasurement.variant.power_metrics.has_ch3_current) { display->drawString(x, y += _fontHeight(FONT_SMALL), - "Ch 3 Volt/Cur: " + String(lastMeasurement.variant.power_metrics.ch3_voltage, 0) + "V / " + - String(lastMeasurement.variant.power_metrics.ch3_current, 0) + "mA"); + "Ch3 Volt: " + String(lastMeasurement.variant.power_metrics.ch3_voltage, 2) + + "V / Curr: " + String(lastMeasurement.variant.power_metrics.ch3_current, 0) + "mA"); } } @@ -150,8 +144,8 @@ bool PowerTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &m #ifdef DEBUG_PORT const char *sender = getSenderShortName(mp); - LOG_INFO("(Received from %s): ch1_voltage=%f, ch1_current=%f, ch2_voltage=%f, ch2_current=%f, " - "ch3_voltage=%f, ch3_current=%f\n", + LOG_INFO("(Received from %s): ch1_voltage=%.1f, ch1_current=%.1f, ch2_voltage=%.1f, ch2_current=%.1f, " + "ch3_voltage=%.1f, ch3_current=%.1f\n", sender, t->variant.power_metrics.ch1_voltage, t->variant.power_metrics.ch1_current, t->variant.power_metrics.ch2_voltage, t->variant.power_metrics.ch2_current, t->variant.power_metrics.ch3_voltage, t->variant.power_metrics.ch3_current); @@ -172,12 +166,7 @@ bool PowerTelemetryModule::getPowerTelemetry(meshtastic_Telemetry *m) m->time = getTime(); m->which_variant = meshtastic_Telemetry_power_metrics_tag; - m->variant.power_metrics.ch1_voltage = 0; - m->variant.power_metrics.ch1_current = 0; - m->variant.power_metrics.ch2_voltage = 0; - m->variant.power_metrics.ch2_current = 0; - m->variant.power_metrics.ch3_voltage = 0; - m->variant.power_metrics.ch3_current = 0; + m->variant.power_metrics = meshtastic_PowerMetrics_init_zero; #if HAS_TELEMETRY && !defined(ARCH_PORTDUINO) if (ina219Sensor.hasSensor()) valid = ina219Sensor.getMetrics(m); @@ -185,6 +174,8 @@ bool PowerTelemetryModule::getPowerTelemetry(meshtastic_Telemetry *m) valid = ina260Sensor.getMetrics(m); if (ina3221Sensor.hasSensor()) valid = ina3221Sensor.getMetrics(m); + if (max17048Sensor.hasSensor()) + valid = max17048Sensor.getMetrics(m); #endif return valid; @@ -252,7 +243,7 @@ bool PowerTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) service->sendToMesh(p, RX_SRC_LOCAL, true); if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR && config.power.is_power_saving) { - LOG_DEBUG("Starting next execution in 5 seconds and then going to sleep.\n"); + LOG_DEBUG("Starting next execution in 5s then going to sleep.\n"); sleepOnNextExecution = true; setIntervalFromNow(5000); } @@ -262,4 +253,4 @@ bool PowerTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) return false; } -#endif \ No newline at end of file +#endif diff --git a/src/modules/Telemetry/Sensor/MAX17048Sensor.cpp b/src/modules/Telemetry/Sensor/MAX17048Sensor.cpp new file mode 100644 index 000000000..96dd5ae80 --- /dev/null +++ b/src/modules/Telemetry/Sensor/MAX17048Sensor.cpp @@ -0,0 +1,176 @@ +#include "MAX17048Sensor.h" + +#if !MESHTASTIC_EXCLUDE_I2C && !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) + +MAX17048Singleton *MAX17048Singleton::GetInstance() +{ + if (pinstance == nullptr) { + pinstance = new MAX17048Singleton(); + } + return pinstance; +} + +MAX17048Singleton::MAX17048Singleton() {} + +MAX17048Singleton::~MAX17048Singleton() {} + +MAX17048Singleton *MAX17048Singleton::pinstance{nullptr}; + +bool MAX17048Singleton::runOnce(TwoWire *theWire) +{ + initialized = begin(theWire); + LOG_DEBUG("%s::runOnce %s\n", sensorStr, initialized ? "began ok" : "begin failed"); + return initialized; +} + +bool MAX17048Singleton::isBatteryCharging() +{ + float volts = cellVoltage(); + if (isnan(volts)) { + LOG_DEBUG("%s::isBatteryCharging is not connected\n", sensorStr); + return 0; + } + + MAX17048ChargeSample sample; + sample.chargeRate = chargeRate(); // charge/discharge rate in percent/hr + sample.cellPercent = cellPercent(); // state of charge in percent 0 to 100 + chargeSamples.push(sample); // save a sample into a fifo buffer + + // Keep the fifo buffer trimmed + while (chargeSamples.size() > MAX17048_CHARGING_SAMPLES) + chargeSamples.pop(); + + // Based on the past n samples, is the lipo charging, discharging or idle + if (chargeSamples.front().chargeRate > MAX17048_CHARGING_MINIMUM_RATE && + chargeSamples.back().chargeRate > MAX17048_CHARGING_MINIMUM_RATE) { + if (chargeSamples.front().cellPercent > chargeSamples.back().cellPercent) + chargeState = MAX17048ChargeState::EXPORT; + else if (chargeSamples.front().cellPercent < chargeSamples.back().cellPercent) + chargeState = MAX17048ChargeState::IMPORT; + else + chargeState = MAX17048ChargeState::IDLE; + } else { + chargeState = MAX17048ChargeState::IDLE; + } + + LOG_DEBUG("%s::isBatteryCharging %s volts: %.3f soc: %.3f rate: %.3f\n", sensorStr, chargeLabels[chargeState], volts, + sample.cellPercent, sample.chargeRate); + return chargeState == MAX17048ChargeState::IMPORT; +} + +uint16_t MAX17048Singleton::getBusVoltageMv() +{ + float volts = cellVoltage(); + if (isnan(volts)) { + LOG_DEBUG("%s::getBusVoltageMv is not connected\n", sensorStr); + return 0; + } + LOG_DEBUG("%s::getBusVoltageMv %.3fmV\n", sensorStr, volts); + return (uint16_t)(volts * 1000.0f); +} + +uint8_t MAX17048Singleton::getBusBatteryPercent() +{ + float soc = cellPercent(); + LOG_DEBUG("%s::getBusBatteryPercent %.1f%%\n", sensorStr, soc); + return clamp(static_cast(round(soc)), static_cast(0), static_cast(100)); +} + +uint16_t MAX17048Singleton::getTimeToGoSecs() +{ + float rate = chargeRate(); // charge/discharge rate in percent/hr + float soc = cellPercent(); // state of charge in percent 0 to 100 + soc = clamp(soc, 0.0f, 100.0f); // clamp soc between 0 and 100% + float ttg = ((100.0f - soc) / rate) * 3600.0f; // calculate seconds to charge/discharge + LOG_DEBUG("%s::getTimeToGoSecs %.0f seconds\n", sensorStr, ttg); + return (uint16_t)ttg; +} + +bool MAX17048Singleton::isBatteryConnected() +{ + float volts = cellVoltage(); + if (isnan(volts)) { + LOG_DEBUG("%s::isBatteryConnected is not connected\n", sensorStr); + return false; + } + + // if a valid voltage is returned, then battery must be connected + return true; +} + +bool MAX17048Singleton::isExternallyPowered() +{ + float volts = cellVoltage(); + if (isnan(volts)) { + // if the battery is not connected then there must be external power + LOG_DEBUG("%s::isExternallyPowered battery is\n", sensorStr); + return true; + } + // if the bus voltage is over MAX17048_BUS_POWER_VOLTS, then the external power + // is assumed to be connected + LOG_DEBUG("%s::isExternallyPowered %s connected\n", sensorStr, volts >= MAX17048_BUS_POWER_VOLTS ? "is" : "is not"); + return volts >= MAX17048_BUS_POWER_VOLTS; +} + +#if (HAS_TELEMETRY && (!MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR || !MESHTASTIC_EXCLUDE_POWER_TELEMETRY)) + +MAX17048Sensor::MAX17048Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_MAX17048, "MAX17048") {} + +int32_t MAX17048Sensor::runOnce() +{ + if (isInitialized()) { + LOG_INFO("Init sensor: %s is already initialised\n", sensorName); + return true; + } + + LOG_INFO("Init sensor: %s\n", sensorName); + if (!hasSensor()) { + return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; + } + + // Get a singleton instance and initialise the max17048 + if (max17048 == nullptr) { + max17048 = MAX17048Singleton::GetInstance(); + } + status = max17048->runOnce(nodeTelemetrySensorsMap[sensorType].second); + return initI2CSensor(); +} + +void MAX17048Sensor::setup() {} + +bool MAX17048Sensor::getMetrics(meshtastic_Telemetry *measurement) +{ + LOG_DEBUG("MAX17048Sensor::getMetrics id: %i\n", measurement->which_variant); + + float volts = max17048->cellVoltage(); + if (isnan(volts)) { + LOG_DEBUG("MAX17048Sensor::getMetrics battery is not connected\n"); + return false; + } + + float rate = max17048->chargeRate(); // charge/discharge rate in percent/hr + float soc = max17048->cellPercent(); // state of charge in percent 0 to 100 + soc = clamp(soc, 0.0f, 100.0f); // clamp soc between 0 and 100% + float ttg = (100.0f - soc) / rate; // calculate hours to charge/discharge + + LOG_DEBUG("MAX17048Sensor::getMetrics volts: %.3fV soc: %.1f%% ttg: %.1f hours\n", volts, soc, ttg); + if ((int)measurement->which_variant == meshtastic_Telemetry_power_metrics_tag) { + measurement->variant.power_metrics.has_ch1_voltage = true; + measurement->variant.power_metrics.ch1_voltage = volts; + } else if ((int)measurement->which_variant == meshtastic_Telemetry_device_metrics_tag) { + measurement->variant.device_metrics.has_battery_level = true; + measurement->variant.device_metrics.has_voltage = true; + measurement->variant.device_metrics.battery_level = static_cast(round(soc)); + measurement->variant.device_metrics.voltage = volts; + } + return true; +} + +uint16_t MAX17048Sensor::getBusVoltageMv() +{ + return max17048->getBusVoltageMv(); +}; + +#endif + +#endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/MAX17048Sensor.h b/src/modules/Telemetry/Sensor/MAX17048Sensor.h new file mode 100644 index 000000000..bd109cbb1 --- /dev/null +++ b/src/modules/Telemetry/Sensor/MAX17048Sensor.h @@ -0,0 +1,111 @@ +#pragma once + +#ifndef MAX17048_SENSOR_H +#define MAX17048_SENSOR_H + +#include "configuration.h" + +#if !MESHTASTIC_EXCLUDE_I2C && !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) + +// Samples to store in a buffer to determine if the battery is charging or discharging +#define MAX17048_CHARGING_SAMPLES 3 + +// Threshold to determine if the battery is on charge, in percent/hour +#define MAX17048_CHARGING_MINIMUM_RATE 1.0f + +// Threshold to determine if the board has bus power +#define MAX17048_BUS_POWER_VOLTS 4.195f + +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "TelemetrySensor.h" +#include "VoltageSensor.h" + +#include "meshUtils.h" +#include +#include + +struct MAX17048ChargeSample { + float cellPercent; + float chargeRate; +}; + +enum MAX17048ChargeState { IDLE, EXPORT, IMPORT }; + +// Singleton wrapper for the Adafruit_MAX17048 class +class MAX17048Singleton : public Adafruit_MAX17048 +{ + private: + static MAX17048Singleton *pinstance; + bool initialized = false; + std::queue chargeSamples; + MAX17048ChargeState chargeState = IDLE; + const String chargeLabels[3] = {F("idle"), F("export"), F("import")}; + const char *sensorStr = "MAX17048Sensor"; + + protected: + MAX17048Singleton(); + ~MAX17048Singleton(); + + public: + // Create a singleton instance (not thread safe) + static MAX17048Singleton *GetInstance(); + + // Singletons should not be cloneable. + MAX17048Singleton(MAX17048Singleton &other) = delete; + + // Singletons should not be assignable. + void operator=(const MAX17048Singleton &) = delete; + + // Initialise the sensor (not thread safe) + virtual bool runOnce(TwoWire *theWire = &Wire); + + // Get the current bus voltage + uint16_t getBusVoltageMv(); + + // Get the state of charge in percent 0 to 100 + uint8_t getBusBatteryPercent(); + + // Calculate the seconds to charge/discharge + uint16_t getTimeToGoSecs(); + + // Returns true if the battery sensor has started + inline virtual bool isInitialised() { return initialized; }; + + // Returns true if the battery is currently on charge (not thread safe) + bool isBatteryCharging(); + + // Returns true if a battery is actually connected + bool isBatteryConnected(); + + // Returns true if there is bus or external power connected + bool isExternallyPowered(); +}; + +#if (HAS_TELEMETRY && (!MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR || !MESHTASTIC_EXCLUDE_POWER_TELEMETRY)) + +class MAX17048Sensor : public TelemetrySensor, VoltageSensor +{ + private: + MAX17048Singleton *max17048 = nullptr; + + protected: + virtual void setup() override; + + public: + MAX17048Sensor(); + + // Initialise the sensor + virtual int32_t runOnce() override; + + // Get the current bus voltage and state of charge + virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + + // Get the current bus voltage + virtual uint16_t getBusVoltageMv() override; +}; + +#endif + +#endif + +#endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/MAX30102Sensor.cpp b/src/modules/Telemetry/Sensor/MAX30102Sensor.cpp new file mode 100644 index 000000000..b3b20e5f2 --- /dev/null +++ b/src/modules/Telemetry/Sensor/MAX30102Sensor.cpp @@ -0,0 +1,83 @@ +#include "configuration.h" + +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO) + +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "MAX30102Sensor.h" +#include "TelemetrySensor.h" +#include + +MAX30102Sensor::MAX30102Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_MAX30102, "MAX30102") {} + +int32_t MAX30102Sensor::runOnce() +{ + LOG_INFO("Init sensor: %s\n", sensorName); + if (!hasSensor()) { + return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; + } + + if (max30102.begin(*nodeTelemetrySensorsMap[sensorType].second, _speed, nodeTelemetrySensorsMap[sensorType].first) == + true) // MAX30102 init + { + byte brightness = 60; // 0=Off to 255=50mA + byte sampleAverage = 4; // 1, 2, 4, 8, 16, 32 + byte leds = 2; // 1 = Red only, 2 = Red + IR + byte sampleRate = 100; // 50, 100, 200, 400, 800, 1000, 1600, 3200 + int pulseWidth = 411; // 69, 118, 215, 411 + int adcRange = 4096; // 2048, 4096, 8192, 16384 + + max30102.enableDIETEMPRDY(); // Enable the temperature ready interrupt + max30102.setup(brightness, sampleAverage, leds, sampleRate, pulseWidth, adcRange); + LOG_DEBUG("MAX30102 Init Succeed\n"); + status = true; + } else { + LOG_ERROR("MAX30102 Init Failed\n"); + status = false; + } + return initI2CSensor(); +} + +void MAX30102Sensor::setup() {} + +bool MAX30102Sensor::getMetrics(meshtastic_Telemetry *measurement) +{ + uint32_t ir_buff[MAX30102_BUFFER_LEN]; + uint32_t red_buff[MAX30102_BUFFER_LEN]; + int32_t spo2; + int8_t spo2_valid; + int32_t heart_rate; + int8_t heart_rate_valid; + float temp = max30102.readTemperature(); + + measurement->variant.environment_metrics.temperature = temp; + measurement->variant.environment_metrics.has_temperature = true; + measurement->variant.health_metrics.temperature = temp; + measurement->variant.health_metrics.has_temperature = true; + for (byte i = 0; i < MAX30102_BUFFER_LEN; i++) { + while (max30102.available() == false) + max30102.check(); + + red_buff[i] = max30102.getRed(); + ir_buff[i] = max30102.getIR(); + max30102.nextSample(); + } + + maxim_heart_rate_and_oxygen_saturation(ir_buff, MAX30102_BUFFER_LEN, red_buff, &spo2, &spo2_valid, &heart_rate, + &heart_rate_valid); + LOG_DEBUG("heart_rate=%d(%d), sp02=%d(%d)", heart_rate, heart_rate_valid, spo2, spo2_valid); + if (heart_rate_valid) { + measurement->variant.health_metrics.has_heart_bpm = true; + measurement->variant.health_metrics.heart_bpm = heart_rate; + } else { + measurement->variant.health_metrics.has_heart_bpm = false; + } + if (spo2_valid) { + measurement->variant.health_metrics.has_spO2 = true; + measurement->variant.health_metrics.spO2 = spo2; + } else { + measurement->variant.health_metrics.has_spO2 = true; + } + return true; +} + +#endif diff --git a/src/modules/Telemetry/Sensor/MAX30102Sensor.h b/src/modules/Telemetry/Sensor/MAX30102Sensor.h new file mode 100644 index 000000000..426d9d365 --- /dev/null +++ b/src/modules/Telemetry/Sensor/MAX30102Sensor.h @@ -0,0 +1,26 @@ +#include "configuration.h" + +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO) + +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "TelemetrySensor.h" +#include + +#define MAX30102_BUFFER_LEN 100 + +class MAX30102Sensor : public TelemetrySensor +{ + private: + MAX30105 max30102 = MAX30105(); + uint32_t _speed = 200000UL; + + protected: + virtual void setup() override; + + public: + MAX30102Sensor(); + virtual int32_t runOnce() override; + virtual bool getMetrics(meshtastic_Telemetry *measurement) override; +}; + +#endif diff --git a/src/modules/Telemetry/Sensor/MLX90614Sensor.cpp b/src/modules/Telemetry/Sensor/MLX90614Sensor.cpp new file mode 100644 index 000000000..92c22bf21 --- /dev/null +++ b/src/modules/Telemetry/Sensor/MLX90614Sensor.cpp @@ -0,0 +1,44 @@ +#include "configuration.h" + +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO) + +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "MLX90614Sensor.h" +#include "TelemetrySensor.h" +MLX90614Sensor::MLX90614Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_MLX90614, "MLX90614") {} + +int32_t MLX90614Sensor::runOnce() +{ + LOG_INFO("Init sensor: %s\n", sensorName); + if (!hasSensor()) { + return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; + } + + if (mlx.begin(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second) == true) // MLX90614 init + { + LOG_DEBUG("MLX90614 emissivity: %f", mlx.readEmissivity()); + if (fabs(MLX90614_EMISSIVITY - mlx.readEmissivity()) > 0.001) { + mlx.writeEmissivity(MLX90614_EMISSIVITY); + LOG_INFO("MLX90614 emissivity updated. In case of weird data, power cycle."); + } + LOG_DEBUG("MLX90614 Init Succeed\n"); + status = true; + } else { + LOG_ERROR("MLX90614 Init Failed\n"); + status = false; + } + return initI2CSensor(); +} + +void MLX90614Sensor::setup() {} + +bool MLX90614Sensor::getMetrics(meshtastic_Telemetry *measurement) +{ + measurement->variant.environment_metrics.temperature = mlx.readAmbientTempC(); + measurement->variant.environment_metrics.has_temperature = true; + measurement->variant.health_metrics.temperature = mlx.readObjectTempC(); + measurement->variant.health_metrics.has_temperature = true; + return true; +} + +#endif diff --git a/src/modules/Telemetry/Sensor/MLX90614Sensor.h b/src/modules/Telemetry/Sensor/MLX90614Sensor.h new file mode 100644 index 000000000..00f63449e --- /dev/null +++ b/src/modules/Telemetry/Sensor/MLX90614Sensor.h @@ -0,0 +1,24 @@ +#include "configuration.h" + +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO) +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "TelemetrySensor.h" +#include + +#define MLX90614_EMISSIVITY 0.98 // human skin + +class MLX90614Sensor : public TelemetrySensor +{ + private: + Adafruit_MLX90614 mlx = Adafruit_MLX90614(); + + protected: + virtual void setup() override; + + public: + MLX90614Sensor(); + virtual int32_t runOnce() override; + virtual bool getMetrics(meshtastic_Telemetry *measurement) override; +}; + +#endif diff --git a/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp b/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp index d7dcbd09f..59f310a24 100644 --- a/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp +++ b/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp @@ -7,6 +7,7 @@ #include "NAU7802Sensor.h" #include "SafeFile.h" #include "TelemetrySensor.h" +#include #include #include @@ -40,7 +41,7 @@ bool NAU7802Sensor::getMetrics(meshtastic_Telemetry *measurement) uint32_t start = millis(); while (!nau7802.available()) { delay(100); - if (millis() - start > 1000) { + if (!Throttle::isWithinTimespanMs(start, 1000)) { nau7802.powerDown(); return false; } diff --git a/src/modules/Telemetry/Sensor/T1000xSensor.cpp b/src/modules/Telemetry/Sensor/T1000xSensor.cpp index 4079d8ae3..4772aeb9e 100644 --- a/src/modules/Telemetry/Sensor/T1000xSensor.cpp +++ b/src/modules/Telemetry/Sensor/T1000xSensor.cpp @@ -95,7 +95,7 @@ float T1000xSensor::getTemp() Vout = ntc_vot; Rt = (HEATER_NTC_RP * vcc_vot) / Vout - HEATER_NTC_RP; - for (u8i = 0; u8i < 136; u8i++) { + for (u8i = 0; u8i < 135; u8i++) { if (Rt >= ntc_res2[u8i]) { break; } diff --git a/src/modules/TraceRouteModule.cpp b/src/modules/TraceRouteModule.cpp index dd3d0b4f9..955098c28 100644 --- a/src/modules/TraceRouteModule.cpp +++ b/src/modules/TraceRouteModule.cpp @@ -11,13 +11,13 @@ bool TraceRouteModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, m void TraceRouteModule::alterReceivedProtobuf(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r) { - auto &incoming = p.decoded; + const meshtastic_Data &incoming = p.decoded; // Insert unknown hops if necessary insertUnknownHops(p, r, !incoming.request_id); - // Append ID and SNR. For the last hop (p.to == nodeDB->getNodeNum()), we only need to append the SNR - appendMyIDandSNR(r, p.rx_snr, !incoming.request_id, p.to == nodeDB->getNodeNum()); + // Append ID and SNR. If the last hop is to us, we only need to append the SNR + appendMyIDandSNR(r, p.rx_snr, !incoming.request_id, isToUs(&p)); if (!incoming.request_id) printRoute(r, p.from, p.to, true); else @@ -53,7 +53,7 @@ void TraceRouteModule::insertUnknownHops(meshtastic_MeshPacket &p, meshtastic_Ro uint8_t hopsTaken = p.hop_start - p.hop_limit; int8_t diff = hopsTaken - *route_count; for (uint8_t i = 0; i < diff; i++) { - if (*route_count < sizeof(*route) / sizeof(route[0])) { + if (*route_count < ROUTE_SIZE) { route[*route_count] = NODENUM_BROADCAST; // This will represent an unknown hop *route_count += 1; } @@ -61,7 +61,7 @@ void TraceRouteModule::insertUnknownHops(meshtastic_MeshPacket &p, meshtastic_Ro // Add unknown SNR values if necessary diff = *route_count - *snr_count; for (uint8_t i = 0; i < diff; i++) { - if (*snr_count < sizeof(*snr_list) / sizeof(snr_list[0])) { + if (*snr_count < ROUTE_SIZE) { snr_list[*snr_count] = INT8_MIN; // This will represent an unknown SNR *snr_count += 1; } @@ -89,7 +89,7 @@ void TraceRouteModule::appendMyIDandSNR(meshtastic_RouteDiscovery *updated, floa snr_list = updated->snr_back; } - if (*snr_count < sizeof(*snr_list) / sizeof(snr_list[0])) { + if (*snr_count < ROUTE_SIZE) { snr_list[*snr_count] = (int8_t)(snr * 4); // Convert SNR to 1 byte *snr_count += 1; } @@ -97,11 +97,11 @@ void TraceRouteModule::appendMyIDandSNR(meshtastic_RouteDiscovery *updated, floa return; // Length of route array can normally not be exceeded due to the max. hop_limit of 7 - if (*route_count < sizeof(*route) / sizeof(route[0])) { + if (*route_count < ROUTE_SIZE) { route[*route_count] = myNodeInfo.my_node_num; *route_count += 1; } else { - LOG_WARN("Route exceeded maximum hop limit, are you bridging networks?\n"); + LOG_WARN("Route exceeded maximum hop limit!\n"); // Are you bridging networks? } } diff --git a/src/modules/TraceRouteModule.h b/src/modules/TraceRouteModule.h index fe69300de..afe2b3871 100644 --- a/src/modules/TraceRouteModule.h +++ b/src/modules/TraceRouteModule.h @@ -1,6 +1,8 @@ #pragma once #include "ProtobufModule.h" +#define ROUTE_SIZE sizeof(((meshtastic_RouteDiscovery *)0)->route) / sizeof(((meshtastic_RouteDiscovery *)0)->route[0]) + /** * A module that traces the route to a certain destination node */ diff --git a/src/modules/WaypointModule.cpp b/src/modules/WaypointModule.cpp index e1974db73..a4611e780 100644 --- a/src/modules/WaypointModule.cpp +++ b/src/modules/WaypointModule.cpp @@ -89,7 +89,7 @@ void WaypointModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL); // Decode the waypoint - meshtastic_MeshPacket &mp = devicestate.rx_waypoint; + const meshtastic_MeshPacket &mp = devicestate.rx_waypoint; meshtastic_Waypoint wp; memset(&wp, 0, sizeof(wp)); if (!pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, &meshtastic_Waypoint_msg, &wp)) { @@ -171,16 +171,16 @@ void WaypointModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, strncpy(distStr, "? km", sizeof(distStr)); } + // Draw compass circle + display->drawCircle(compassX, compassY, compassDiam / 2); + // Undo color-inversion, if set prior to drawing header // Unsure of expected behavior? For now: copy drawNodeInfo if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) { display->setColor(BLACK); } - // Draw compass circle - display->drawCircle(compassX, compassY, compassDiam / 2); - // Must be after distStr is populated screen->drawColumns(display, x, y, fields); } -#endif \ No newline at end of file +#endif diff --git a/src/modules/esp32/AudioModule.cpp b/src/modules/esp32/AudioModule.cpp index 8a29f9a2e..89e4b4e25 100644 --- a/src/modules/esp32/AudioModule.cpp +++ b/src/modules/esp32/AudioModule.cpp @@ -273,7 +273,7 @@ ProcessMessage AudioModule::handleReceived(const meshtastic_MeshPacket &mp) { if ((moduleConfig.audio.codec2_enabled) && (myRegion->audioPermitted)) { auto &p = mp.decoded; - if (getFrom(&mp) != nodeDB->getNodeNum()) { + if (!isFromUs(&mp)) { memcpy(rx_encode_frame, p.payload.bytes, p.payload.size); radio_state = RadioState::rx; rx_encode_frame_index = p.payload.size; diff --git a/src/motion/AccelerometerThread.h b/src/motion/AccelerometerThread.h new file mode 100755 index 000000000..f9cbb49a1 --- /dev/null +++ b/src/motion/AccelerometerThread.h @@ -0,0 +1,149 @@ +#pragma once +#ifndef _ACCELEROMETER_H_ +#define _ACCELEROMETER_H_ + +#include "configuration.h" + +#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C + +#include "../concurrency/OSThread.h" +#include "BMA423Sensor.h" +#include "BMX160Sensor.h" +#include "ICM20948Sensor.h" +#include "LIS3DHSensor.h" +#include "LSM6DS3Sensor.h" +#include "MPU6050Sensor.h" +#include "MotionSensor.h" +#include "STK8XXXSensor.h" + +extern ScanI2C::DeviceAddress accelerometer_found; + +class AccelerometerThread : public concurrency::OSThread +{ + private: + MotionSensor *sensor = nullptr; + bool isInitialised = false; + + public: + explicit AccelerometerThread(ScanI2C::FoundDevice foundDevice) : OSThread("AccelerometerThread") + { + device = foundDevice; + init(); + } + + explicit AccelerometerThread(ScanI2C::DeviceType type) : AccelerometerThread(ScanI2C::FoundDevice{type, accelerometer_found}) + { + } + + void start() + { + init(); + setIntervalFromNow(0); + }; + + protected: + int32_t runOnce() override + { + // Assume we should not keep the board awake + canSleep = true; + + if (isInitialised) + return sensor->runOnce(); + + return MOTION_SENSOR_CHECK_INTERVAL_MS; + } + + private: + ScanI2C::FoundDevice device; + + void init() + { + if (isInitialised) + return; + + if (device.address.port == ScanI2C::I2CPort::NO_I2C || device.address.address == 0 || device.type == ScanI2C::NONE) { + LOG_DEBUG("AccelerometerThread disabling due to no sensors found\n"); + disable(); + return; + } + +#ifndef RAK_4631 + if (!config.display.wake_on_tap_or_motion && !config.device.double_tap_as_button_press) { + LOG_DEBUG("AccelerometerThread disabling due to no interested configurations\n"); + disable(); + return; + } +#endif + + switch (device.type) { + case ScanI2C::DeviceType::BMA423: + sensor = new BMA423Sensor(device); + break; + case ScanI2C::DeviceType::MPU6050: + sensor = new MPU6050Sensor(device); + break; + case ScanI2C::DeviceType::BMX160: + sensor = new BMX160Sensor(device); + break; + case ScanI2C::DeviceType::LIS3DH: + sensor = new LIS3DHSensor(device); + break; + case ScanI2C::DeviceType::LSM6DS3: + sensor = new LSM6DS3Sensor(device); + break; + case ScanI2C::DeviceType::STK8BAXX: + sensor = new STK8XXXSensor(device); + break; + case ScanI2C::DeviceType::ICM20948: + sensor = new ICM20948Sensor(device); + break; + default: + disable(); + return; + } + + isInitialised = sensor->init(); + if (!isInitialised) { + clean(); + } + LOG_DEBUG("AccelerometerThread::init %s\n", isInitialised ? "ok" : "failed"); + } + + // Copy constructor (not implemented / included to avoid cppcheck warnings) + AccelerometerThread(const AccelerometerThread &other) : OSThread::OSThread("AccelerometerThread") { this->copy(other); } + + // Destructor (included to avoid cppcheck warnings) + virtual ~AccelerometerThread() { clean(); } + + // Copy assignment (not implemented / included to avoid cppcheck warnings) + AccelerometerThread &operator=(const AccelerometerThread &other) + { + this->copy(other); + return *this; + } + + // Take a very shallow copy (does not copy OSThread state nor the sensor object) + // If for some reason this is ever used, make sure to call init() after any copy + void copy(const AccelerometerThread &other) + { + if (this != &other) { + clean(); + this->device = ScanI2C::FoundDevice(other.device.type, + ScanI2C::DeviceAddress(other.device.address.port, other.device.address.address)); + } + } + + // Cleanup resources + void clean() + { + isInitialised = false; + if (sensor != nullptr) { + delete sensor; + sensor = nullptr; + } + } +}; + +#endif + +#endif \ No newline at end of file diff --git a/src/motion/BMA423Sensor.cpp b/src/motion/BMA423Sensor.cpp new file mode 100755 index 000000000..ec07b7c40 --- /dev/null +++ b/src/motion/BMA423Sensor.cpp @@ -0,0 +1,62 @@ +#include "BMA423Sensor.h" + +#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C + +using namespace MotionSensorI2C; + +BMA423Sensor::BMA423Sensor(ScanI2C::FoundDevice foundDevice) : MotionSensor::MotionSensor(foundDevice) {} + +bool BMA423Sensor::init() +{ + if (sensor.begin(deviceAddress(), &MotionSensorI2C::readRegister, &MotionSensorI2C::writeRegister)) { + sensor.configAccelerometer(sensor.RANGE_2G, sensor.ODR_100HZ, sensor.BW_NORMAL_AVG4, sensor.PERF_CONTINUOUS_MODE); + sensor.enableAccelerometer(); + sensor.configInterrupt(BMA4_LEVEL_TRIGGER, BMA4_ACTIVE_HIGH, BMA4_PUSH_PULL, BMA4_OUTPUT_ENABLE, BMA4_INPUT_DISABLE); + +#ifdef BMA423_INT + pinMode(BMA4XX_INT, INPUT); + attachInterrupt( + BMA4XX_INT, + [] { + // Set interrupt to set irq value to true + BMA_IRQ = true; + }, + RISING); // Select the interrupt mode according to the actual circuit +#endif + +#ifdef T_WATCH_S3 + // Need to raise the wrist function, need to set the correct axis + sensor.setReampAxes(sensor.REMAP_TOP_LAYER_RIGHT_CORNER); +#else + sensor.setReampAxes(sensor.REMAP_BOTTOM_LAYER_BOTTOM_LEFT_CORNER); +#endif + // sensor.enableFeature(sensor.FEATURE_STEP_CNTR, true); + sensor.enableFeature(sensor.FEATURE_TILT, true); + sensor.enableFeature(sensor.FEATURE_WAKEUP, true); + // sensor.resetPedometer(); + + // Turn on feature interrupt + sensor.enablePedometerIRQ(); + sensor.enableTiltIRQ(); + + // It corresponds to isDoubleClick interrupt + sensor.enableWakeupIRQ(); + LOG_DEBUG("BMA423Sensor::init ok\n"); + return true; + } + LOG_DEBUG("BMA423Sensor::init failed\n"); + return false; +} + +int32_t BMA423Sensor::runOnce() +{ + if (sensor.readIrqStatus() != DEV_WIRE_NONE) { + if (sensor.isTilt() || sensor.isDoubleTap()) { + wakeScreen(); + return 500; + } + } + return MOTION_SENSOR_CHECK_INTERVAL_MS; +} + +#endif \ No newline at end of file diff --git a/src/motion/BMA423Sensor.h b/src/motion/BMA423Sensor.h new file mode 100755 index 000000000..0bc0ddf8c --- /dev/null +++ b/src/motion/BMA423Sensor.h @@ -0,0 +1,26 @@ +#pragma once +#ifndef _BMA423_SENSOR_H_ +#define _BMA423_SENSOR_H_ + +#include "MotionSensor.h" + +#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C + +#include +#include + +class BMA423Sensor : public MotionSensor +{ + private: + SensorBMA423 sensor; + volatile bool BMA_IRQ = false; + + public: + explicit BMA423Sensor(ScanI2C::FoundDevice foundDevice); + virtual bool init() override; + virtual int32_t runOnce() override; +}; + +#endif + +#endif \ No newline at end of file diff --git a/src/motion/BMX160Sensor.cpp b/src/motion/BMX160Sensor.cpp new file mode 100755 index 000000000..3f68a4a25 --- /dev/null +++ b/src/motion/BMX160Sensor.cpp @@ -0,0 +1,104 @@ +#include "BMX160Sensor.h" + +#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C + +BMX160Sensor::BMX160Sensor(ScanI2C::FoundDevice foundDevice) : MotionSensor::MotionSensor(foundDevice) {} + +#ifdef RAK_4631 +#if !defined(MESHTASTIC_EXCLUDE_SCREEN) + +// screen is defined in main.cpp +extern graphics::Screen *screen; +#endif + +bool BMX160Sensor::init() +{ + if (sensor.begin()) { + // set output data rate + sensor.ODR_Config(BMX160_ACCEL_ODR_100HZ, BMX160_GYRO_ODR_100HZ); + LOG_DEBUG("BMX160Sensor::init ok\n"); + return true; + } + LOG_DEBUG("BMX160Sensor::init failed\n"); + return false; +} + +int32_t BMX160Sensor::runOnce() +{ + sBmx160SensorData_t magAccel; + sBmx160SensorData_t gAccel; + + /* Get a new sensor event */ + sensor.getAllData(&magAccel, NULL, &gAccel); + +#if !defined(MESHTASTIC_EXCLUDE_SCREEN) + // experimental calibrate routine. Limited to between 10 and 30 seconds after boot + if (millis() > 12 * 1000 && millis() < 30 * 1000) { + if (!showingScreen) { + showingScreen = true; + screen->startAlert((FrameCallback)drawFrameCalibration); + } + if (magAccel.x > highestX) + highestX = magAccel.x; + if (magAccel.x < lowestX) + lowestX = magAccel.x; + if (magAccel.y > highestY) + highestY = magAccel.y; + if (magAccel.y < lowestY) + lowestY = magAccel.y; + if (magAccel.z > highestZ) + highestZ = magAccel.z; + if (magAccel.z < lowestZ) + lowestZ = magAccel.z; + } else if (showingScreen && millis() >= 30 * 1000) { + showingScreen = false; + screen->endAlert(); + } + + int highestRealX = highestX - (highestX + lowestX) / 2; + + magAccel.x -= (highestX + lowestX) / 2; + magAccel.y -= (highestY + lowestY) / 2; + magAccel.z -= (highestZ + lowestZ) / 2; + FusionVector ga, ma; + ga.axis.x = -gAccel.x; // default location for the BMX160 is on the rear of the board + ga.axis.y = -gAccel.y; + ga.axis.z = gAccel.z; + ma.axis.x = -magAccel.x; + ma.axis.y = -magAccel.y; + ma.axis.z = magAccel.z * 3; + + // If we're set to one of the inverted positions + if (config.display.compass_orientation > meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_270) { + ma = FusionAxesSwap(ma, FusionAxesAlignmentNXNYPZ); + ga = FusionAxesSwap(ga, FusionAxesAlignmentNXNYPZ); + } + + float heading = FusionCompassCalculateHeading(FusionConventionNed, ga, ma); + + switch (config.display.compass_orientation) { + case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_0_INVERTED: + case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_0: + break; + case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_90: + case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_90_INVERTED: + heading += 90; + break; + case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_180: + case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_180_INVERTED: + heading += 180; + break; + case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_270: + case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_270_INVERTED: + heading += 270; + break; + } + screen->setHeading(heading); +#endif + + return MOTION_SENSOR_CHECK_INTERVAL_MS; +} + +#endif + +#endif \ No newline at end of file diff --git a/src/motion/BMX160Sensor.h b/src/motion/BMX160Sensor.h new file mode 100755 index 000000000..26f477271 --- /dev/null +++ b/src/motion/BMX160Sensor.h @@ -0,0 +1,41 @@ +#pragma once + +#ifndef _BMX160_SENSOR_H_ +#define _BMX160_SENSOR_H_ + +#include "MotionSensor.h" + +#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C + +#ifdef RAK_4631 + +#include "Fusion/Fusion.h" +#include + +class BMX160Sensor : public MotionSensor +{ + private: + RAK_BMX160 sensor; + bool showingScreen = false; + float highestX = 0, lowestX = 0, highestY = 0, lowestY = 0, highestZ = 0, lowestZ = 0; + + public: + explicit BMX160Sensor(ScanI2C::FoundDevice foundDevice); + virtual bool init() override; + virtual int32_t runOnce() override; +}; + +#else + +// Stub +class BMX160Sensor : public MotionSensor +{ + public: + explicit BMX160Sensor(ScanI2C::FoundDevice foundDevice); +}; + +#endif + +#endif + +#endif \ No newline at end of file diff --git a/src/motion/ICM20948Sensor.cpp b/src/motion/ICM20948Sensor.cpp new file mode 100755 index 000000000..d16968e06 --- /dev/null +++ b/src/motion/ICM20948Sensor.cpp @@ -0,0 +1,186 @@ +#include "ICM20948Sensor.h" + +#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C + +// Flag when an interrupt has been detected +volatile static bool ICM20948_IRQ = false; + +// Interrupt service routine +void ICM20948SetInterrupt() +{ + ICM20948_IRQ = true; +} + +ICM20948Sensor::ICM20948Sensor(ScanI2C::FoundDevice foundDevice) : MotionSensor::MotionSensor(foundDevice) {} + +bool ICM20948Sensor::init() +{ + // Initialise the sensor + sensor = ICM20948Singleton::GetInstance(); + if (!sensor->init(device)) + return false; + + // Enable simple Wake on Motion + return sensor->setWakeOnMotion(); +} + +#ifdef ICM_20948_INT_PIN + +int32_t ICM20948Sensor::runOnce() +{ + // Wake on motion using hardware interrupts - this is the most efficient way to check for motion + if (ICM20948_IRQ) { + ICM20948_IRQ = false; + sensor->clearInterrupts(); + wakeScreen(); + } + return MOTION_SENSOR_CHECK_INTERVAL_MS; +} + +#else + +int32_t ICM20948Sensor::runOnce() +{ + // Wake on motion using polling - this is not as efficient as using hardware interrupt pin (see above) + auto status = sensor->setBank(0); + if (sensor->status != ICM_20948_Stat_Ok) { + LOG_DEBUG("ICM20948Sensor::isWakeOnMotion failed to set bank - %s\n", sensor->statusString()); + return MOTION_SENSOR_CHECK_INTERVAL_MS; + } + + ICM_20948_INT_STATUS_t int_stat; + status = sensor->read(AGB0_REG_INT_STATUS, (uint8_t *)&int_stat, sizeof(ICM_20948_INT_STATUS_t)); + if (status != ICM_20948_Stat_Ok) { + LOG_DEBUG("ICM20948Sensor::isWakeOnMotion failed to read interrupts - %s\n", sensor->statusString()); + return MOTION_SENSOR_CHECK_INTERVAL_MS; + } + + if (int_stat.WOM_INT != 0) { + // Wake up! + wakeScreen(); + } + return MOTION_SENSOR_CHECK_INTERVAL_MS; +} + +#endif + +// ---------------------------------------------------------------------- +// ICM20948Singleton +// ---------------------------------------------------------------------- + +// Get a singleton wrapper for an Sparkfun ICM_20948_I2C +ICM20948Singleton *ICM20948Singleton::GetInstance() +{ + if (pinstance == nullptr) { + pinstance = new ICM20948Singleton(); + } + return pinstance; +} + +ICM20948Singleton::ICM20948Singleton() {} + +ICM20948Singleton::~ICM20948Singleton() {} + +ICM20948Singleton *ICM20948Singleton::pinstance{nullptr}; + +// Initialise the ICM20948 Sensor +bool ICM20948Singleton::init(ScanI2C::FoundDevice device) +{ +#ifdef ICM_20948_DEBUG + // Set ICM_20948_DEBUG to enable helpful debug messages on Serial + enableDebugging(); +#endif + +// startup +#ifdef Wire1 + ICM_20948_Status_e status = + begin(device.address.port == ScanI2C::I2CPort::WIRE1 ? Wire1 : Wire, device.address.address == ICM20948_ADDR ? 1 : 0); +#else + ICM_20948_Status_e status = begin(Wire, device.address.address == ICM20948_ADDR ? 1 : 0); +#endif + if (status != ICM_20948_Stat_Ok) { + LOG_DEBUG("ICM20948Sensor::init begin - %s\n", statusString()); + return false; + } + + // SW reset to make sure the device starts in a known state + if (swReset() != ICM_20948_Stat_Ok) { + LOG_DEBUG("ICM20948Sensor::init reset - %s\n", statusString()); + return false; + } + delay(200); + + // Now wake the sensor up + if (sleep(false) != ICM_20948_Stat_Ok) { + LOG_DEBUG("ICM20948Sensor::init wake - %s\n", statusString()); + return false; + } + + if (lowPower(false) != ICM_20948_Stat_Ok) { + LOG_DEBUG("ICM20948Sensor::init high power - %s\n", statusString()); + return false; + } + +#ifdef ICM_20948_INT_PIN + + // Active low + cfgIntActiveLow(true); + LOG_DEBUG("ICM20948Sensor::init set cfgIntActiveLow - %s\n", statusString()); + + // Push-pull + cfgIntOpenDrain(false); + LOG_DEBUG("ICM20948Sensor::init set cfgIntOpenDrain - %s\n", statusString()); + + // If enabled, *ANY* read will clear the INT_STATUS register. + cfgIntAnyReadToClear(true); + LOG_DEBUG("ICM20948Sensor::init set cfgIntAnyReadToClear - %s\n", statusString()); + + // Latch the interrupt until cleared + cfgIntLatch(true); + LOG_DEBUG("ICM20948Sensor::init set cfgIntLatch - %s\n", statusString()); + + // Set up an interrupt pin with an internal pullup for active low + pinMode(ICM_20948_INT_PIN, INPUT_PULLUP); + + // Set up an interrupt service routine + attachInterrupt(ICM_20948_INT_PIN, ICM20948SetInterrupt, FALLING); + +#endif + return true; +} + +#ifdef ICM_20948_DMP_IS_ENABLED + +// Stub +bool ICM20948Sensor::initDMP() +{ + return false; +} + +#endif + +bool ICM20948Singleton::setWakeOnMotion() +{ + // Set WoM threshold in milli G's + auto status = WOMThreshold(ICM_20948_WOM_THRESHOLD); + if (status != ICM_20948_Stat_Ok) + return false; + + // Enable WoM Logic mode 1 = Compare the current sample with the previous sample + status = WOMLogic(true, 1); + LOG_DEBUG("ICM20948Sensor::init set WOMLogic - %s\n", statusString()); + if (status != ICM_20948_Stat_Ok) + return false; + + // Enable interrupts on WakeOnMotion + status = intEnableWOM(true); + LOG_DEBUG("ICM20948Sensor::init set intEnableWOM - %s\n", statusString()); + return status == ICM_20948_Stat_Ok; + + // Clear any current interrupts + ICM20948_IRQ = false; + clearInterrupts(); + return true; +} + +#endif \ No newline at end of file diff --git a/src/motion/ICM20948Sensor.h b/src/motion/ICM20948Sensor.h new file mode 100755 index 000000000..d5e246c8d --- /dev/null +++ b/src/motion/ICM20948Sensor.h @@ -0,0 +1,96 @@ +#pragma once +#ifndef _ICM_20948_SENSOR_H_ +#define _ICM_20948_SENSOR_H_ + +#include "MotionSensor.h" + +#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C + +#include + +// Set the default gyro scale - dps250, dps500, dps1000, dps2000 +#ifndef ICM_20948_MPU_GYRO_SCALE +#define ICM_20948_MPU_GYRO_SCALE dps250 +#endif + +// Set the default accelerometer scale - gpm2, gpm4, gpm8, gpm16 +#ifndef ICM_20948_MPU_ACCEL_SCALE +#define ICM_20948_MPU_ACCEL_SCALE gpm2 +#endif + +// Define a threshold for Wake on Motion Sensing (0mg to 1020mg) +#ifndef ICM_20948_WOM_THRESHOLD +#define ICM_20948_WOM_THRESHOLD 16U +#endif + +// Define a pin in variant.h to use interrupts to read the ICM-20948 +#ifndef ICM_20948_WOM_THRESHOLD +#define ICM_20948_INT_PIN 255 +#endif + +// Uncomment this line to enable helpful debug messages on Serial +// #define ICM_20948_DEBUG 1 + +// Uncomment this line to enable the onboard digital motion processor (to be added in a future PR) +// #define ICM_20948_DMP_IS_ENABLED 1 + +// Check for a mandatory compiler flag to use the DMP (to be added in a future PR) +#ifdef ICM_20948_DMP_IS_ENABLED +#ifndef ICM_20948_USE_DMP +#error To use the digital motion processor, please either set the compiler flag ICM_20948_USE_DMP or uncomment line 29 (#define ICM_20948_USE_DMP) in ICM_20948_C.h +#endif +#endif + +// The I2C address of the Accelerometer (if found) from main.cpp +extern ScanI2C::DeviceAddress accelerometer_found; + +// Singleton wrapper for the Sparkfun ICM_20948_I2C class +class ICM20948Singleton : public ICM_20948_I2C +{ + private: + static ICM20948Singleton *pinstance; + + protected: + ICM20948Singleton(); + ~ICM20948Singleton(); + + public: + // Create a singleton instance (not thread safe) + static ICM20948Singleton *GetInstance(); + + // Singletons should not be cloneable. + ICM20948Singleton(ICM20948Singleton &other) = delete; + + // Singletons should not be assignable. + void operator=(const ICM20948Singleton &) = delete; + + // Initialise the motion sensor singleton for normal operation + bool init(ScanI2C::FoundDevice device); + + // Enable Wake on Motion interrupts (sensor must be initialised first) + bool setWakeOnMotion(); + +#ifdef ICM_20948_DMP_IS_ENABLED + // Initialise the motion sensor singleton for digital motion processing + bool initDMP(); +#endif +}; + +class ICM20948Sensor : public MotionSensor +{ + private: + ICM20948Singleton *sensor = nullptr; + + public: + explicit ICM20948Sensor(ScanI2C::FoundDevice foundDevice); + + // Initialise the motion sensor + virtual bool init() override; + + // Called each time our sensor gets a chance to run + virtual int32_t runOnce() override; +}; + +#endif + +#endif \ No newline at end of file diff --git a/src/motion/LIS3DHSensor.cpp b/src/motion/LIS3DHSensor.cpp new file mode 100755 index 000000000..e2df60b1c --- /dev/null +++ b/src/motion/LIS3DHSensor.cpp @@ -0,0 +1,36 @@ +#include "LIS3DHSensor.h" + +#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C + +LIS3DHSensor::LIS3DHSensor(ScanI2C::FoundDevice foundDevice) : MotionSensor::MotionSensor(foundDevice) {} + +bool LIS3DHSensor::init() +{ + if (sensor.begin(deviceAddress())) { + sensor.setRange(LIS3DH_RANGE_2_G); + // Adjust threshold, higher numbers are less sensitive + sensor.setClick(config.device.double_tap_as_button_press ? 2 : 1, MOTION_SENSOR_CHECK_INTERVAL_MS); + LOG_DEBUG("LIS3DHSensor::init ok\n"); + return true; + } + LOG_DEBUG("LIS3DHSensor::init failed\n"); + return false; +} + +int32_t LIS3DHSensor::runOnce() +{ + if (sensor.getClick() > 0) { + uint8_t click = sensor.getClick(); + if (!config.device.double_tap_as_button_press) { + wakeScreen(); + } + + if (config.device.double_tap_as_button_press && (click & 0x20)) { + buttonPress(); + return 500; + } + } + return MOTION_SENSOR_CHECK_INTERVAL_MS; +} + +#endif \ No newline at end of file diff --git a/src/motion/LIS3DHSensor.h b/src/motion/LIS3DHSensor.h new file mode 100755 index 000000000..603d195a8 --- /dev/null +++ b/src/motion/LIS3DHSensor.h @@ -0,0 +1,24 @@ +#pragma once +#ifndef _LIS3DH_SENSOR_H_ +#define _LIS3DH_SENSOR_H_ + +#include "MotionSensor.h" + +#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C + +#include + +class LIS3DHSensor : public MotionSensor +{ + private: + Adafruit_LIS3DH sensor; + + public: + explicit LIS3DHSensor(ScanI2C::FoundDevice foundDevice); + virtual bool init() override; + virtual int32_t runOnce() override; +}; + +#endif + +#endif \ No newline at end of file diff --git a/src/motion/LSM6DS3Sensor.cpp b/src/motion/LSM6DS3Sensor.cpp new file mode 100755 index 000000000..64ef9a23b --- /dev/null +++ b/src/motion/LSM6DS3Sensor.cpp @@ -0,0 +1,33 @@ +#include "LSM6DS3Sensor.h" + +#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C + +LSM6DS3Sensor::LSM6DS3Sensor(ScanI2C::FoundDevice foundDevice) : MotionSensor::MotionSensor(foundDevice) {} + +bool LSM6DS3Sensor::init() +{ + if (sensor.begin_I2C(deviceAddress())) { + + // Default threshold of 2G, less sensitive options are 4, 8 or 16G + sensor.setAccelRange(LSM6DS_ACCEL_RANGE_2_G); + + // Duration is number of occurances needed to trigger, higher threshold is less sensitive + sensor.enableWakeup(config.display.wake_on_tap_or_motion, 1, LSM6DS3_WAKE_THRESH); + + LOG_DEBUG("LSM6DS3Sensor::init ok\n"); + return true; + } + LOG_DEBUG("LSM6DS3Sensor::init failed\n"); + return false; +} + +int32_t LSM6DS3Sensor::runOnce() +{ + if (sensor.shake()) { + wakeScreen(); + return 500; + } + return MOTION_SENSOR_CHECK_INTERVAL_MS; +} + +#endif \ No newline at end of file diff --git a/src/motion/LSM6DS3Sensor.h b/src/motion/LSM6DS3Sensor.h new file mode 100755 index 000000000..77069ef3c --- /dev/null +++ b/src/motion/LSM6DS3Sensor.h @@ -0,0 +1,28 @@ +#pragma once +#ifndef _LSM6DS3_SENSOR_H_ +#define _LSM6DS3_SENSOR_H_ + +#include "MotionSensor.h" + +#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C + +#ifndef LSM6DS3_WAKE_THRESH +#define LSM6DS3_WAKE_THRESH 20 +#endif + +#include + +class LSM6DS3Sensor : public MotionSensor +{ + private: + Adafruit_LSM6DS3TRC sensor; + + public: + explicit LSM6DS3Sensor(ScanI2C::FoundDevice foundDevice); + virtual bool init() override; + virtual int32_t runOnce() override; +}; + +#endif + +#endif \ No newline at end of file diff --git a/src/motion/MPU6050Sensor.cpp b/src/motion/MPU6050Sensor.cpp new file mode 100755 index 000000000..77aaca46d --- /dev/null +++ b/src/motion/MPU6050Sensor.cpp @@ -0,0 +1,31 @@ +#include "MPU6050Sensor.h" + +#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C + +MPU6050Sensor::MPU6050Sensor(ScanI2C::FoundDevice foundDevice) : MotionSensor::MotionSensor(foundDevice) {} + +bool MPU6050Sensor::init() +{ + if (sensor.begin(deviceAddress())) { + // setup motion detection + sensor.setHighPassFilter(MPU6050_HIGHPASS_0_63_HZ); + sensor.setMotionDetectionThreshold(1); + sensor.setMotionDetectionDuration(20); + sensor.setInterruptPinLatch(true); // Keep it latched. Will turn off when reinitialized. + sensor.setInterruptPinPolarity(true); + LOG_DEBUG("MPU6050Sensor::init ok\n"); + return true; + } + LOG_DEBUG("MPU6050Sensor::init failed\n"); + return false; +} + +int32_t MPU6050Sensor::runOnce() +{ + if (sensor.getMotionInterruptStatus()) { + wakeScreen(); + } + return MOTION_SENSOR_CHECK_INTERVAL_MS; +} + +#endif \ No newline at end of file diff --git a/src/motion/MPU6050Sensor.h b/src/motion/MPU6050Sensor.h new file mode 100755 index 000000000..2e6eafecd --- /dev/null +++ b/src/motion/MPU6050Sensor.h @@ -0,0 +1,24 @@ +#pragma once +#ifndef _MPU6050_SENSOR_H_ +#define _MPU6050_SENSOR_H_ + +#include "MotionSensor.h" + +#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C + +#include + +class MPU6050Sensor : public MotionSensor +{ + private: + Adafruit_MPU6050 sensor; + + public: + explicit MPU6050Sensor(ScanI2C::FoundDevice foundDevice); + virtual bool init() override; + virtual int32_t runOnce() override; +}; + +#endif + +#endif \ No newline at end of file diff --git a/src/motion/MotionSensor.cpp b/src/motion/MotionSensor.cpp new file mode 100755 index 000000000..f1c0ba85e --- /dev/null +++ b/src/motion/MotionSensor.cpp @@ -0,0 +1,79 @@ +#include "MotionSensor.h" + +#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C + +// screen is defined in main.cpp +extern graphics::Screen *screen; + +MotionSensor::MotionSensor(ScanI2C::FoundDevice foundDevice) +{ + device.address.address = foundDevice.address.address; + device.address.port = foundDevice.address.port; + device.type = foundDevice.type; + LOG_DEBUG("MotionSensor::MotionSensor port: %s address: 0x%x type: %d\n", + devicePort() == ScanI2C::I2CPort::WIRE1 ? "Wire1" : "Wire", (uint8_t)deviceAddress(), deviceType()); +} + +ScanI2C::DeviceType MotionSensor::deviceType() +{ + return device.type; +} + +uint8_t MotionSensor::deviceAddress() +{ + return device.address.address; +} + +ScanI2C::I2CPort MotionSensor::devicePort() +{ + return device.address.port; +} + +#if defined(RAK_4631) & !MESHTASTIC_EXCLUDE_SCREEN +void MotionSensor::drawFrameCalibration(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) +{ + // int x_offset = display->width() / 2; + // int y_offset = display->height() <= 80 ? 0 : 32; + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(FONT_MEDIUM); + display->drawString(x, y, "Calibrating\nCompass"); + int16_t compassX = 0, compassY = 0; + uint16_t compassDiam = graphics::Screen::getCompassDiam(display->getWidth(), display->getHeight()); + + // coordinates for the center of the compass/circle + if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) { + compassX = x + display->getWidth() - compassDiam / 2 - 5; + compassY = y + display->getHeight() / 2; + } else { + compassX = x + display->getWidth() - compassDiam / 2 - 5; + compassY = y + FONT_HEIGHT_SMALL + (display->getHeight() - FONT_HEIGHT_SMALL) / 2; + } + display->drawCircle(compassX, compassY, compassDiam / 2); + screen->drawCompassNorth(display, compassX, compassY, screen->getHeading() * PI / 180); +} +#endif + +#if !MESHTASTIC_EXCLUDE_POWER_FSM +void MotionSensor::wakeScreen() +{ + if (powerFSM.getState() == &stateDARK) { + LOG_DEBUG("MotionSensor::wakeScreen detected\n"); + powerFSM.trigger(EVENT_INPUT); + } +} + +void MotionSensor::buttonPress() +{ + LOG_DEBUG("MotionSensor::buttonPress detected\n"); + powerFSM.trigger(EVENT_PRESS); +} + +#else + +void MotionSensor::wakeScreen() {} + +void MotionSensor::buttonPress() {} + +#endif + +#endif \ No newline at end of file diff --git a/src/motion/MotionSensor.h b/src/motion/MotionSensor.h new file mode 100755 index 000000000..4da23cc70 --- /dev/null +++ b/src/motion/MotionSensor.h @@ -0,0 +1,85 @@ +#pragma once +#ifndef _MOTION_SENSOR_H_ +#define _MOTION_SENSOR_H_ + +#define MOTION_SENSOR_CHECK_INTERVAL_MS 100 +#define MOTION_SENSOR_CLICK_THRESHOLD 40 + +#include "../configuration.h" + +#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C + +#include "../PowerFSM.h" +#include "../detect/ScanI2C.h" +#include "../graphics/Screen.h" +#include "../graphics/ScreenFonts.h" +#include "../power.h" + +// Base class for motion processing +class MotionSensor +{ + public: + explicit MotionSensor(ScanI2C::FoundDevice foundDevice); + virtual ~MotionSensor(){}; + + // Get the device type + ScanI2C::DeviceType deviceType(); + + // Get the device address + uint8_t deviceAddress(); + + // Get the device port + ScanI2C::I2CPort devicePort(); + + // Initialise the motion sensor + inline virtual bool init() { return false; }; + + // The method that will be called each time our sensor gets a chance to run + // Returns the desired period for next invocation (or RUN_SAME for no change) + // Refer to /src/concurrency/OSThread.h for more information + inline virtual int32_t runOnce() { return MOTION_SENSOR_CHECK_INTERVAL_MS; }; + + protected: + // Turn on the screen when a tap or motion is detected + virtual void wakeScreen(); + + // Register a button press when a double-tap is detected + virtual void buttonPress(); + +#if defined(RAK_4631) & !MESHTASTIC_EXCLUDE_SCREEN + // draw an OLED frame (currently only used by the RAK4631 BMX160 sensor) + static void drawFrameCalibration(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); +#endif + + ScanI2C::FoundDevice device; +}; + +namespace MotionSensorI2C +{ + +static inline int readRegister(uint8_t address, uint8_t reg, uint8_t *data, uint8_t len) +{ + Wire.beginTransmission(address); + Wire.write(reg); + Wire.endTransmission(); + Wire.requestFrom((uint8_t)address, (uint8_t)len); + uint8_t i = 0; + while (Wire.available()) { + data[i++] = Wire.read(); + } + return 0; // Pass +} + +static inline int writeRegister(uint8_t address, uint8_t reg, uint8_t *data, uint8_t len) +{ + Wire.beginTransmission(address); + Wire.write(reg); + Wire.write(data, len); + return (0 != Wire.endTransmission()); +} + +} // namespace MotionSensorI2C + +#endif + +#endif \ No newline at end of file diff --git a/src/motion/STK8XXXSensor.cpp b/src/motion/STK8XXXSensor.cpp new file mode 100755 index 000000000..d4d69ef99 --- /dev/null +++ b/src/motion/STK8XXXSensor.cpp @@ -0,0 +1,40 @@ +#include "STK8XXXSensor.h" + +#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C + +STK8XXXSensor::STK8XXXSensor(ScanI2C::FoundDevice foundDevice) : MotionSensor::MotionSensor(foundDevice) {} + +#ifdef STK8XXX_INT + +volatile static bool STK_IRQ; + +bool STK8XXXSensor::init() +{ + if (sensor.STK8xxx_Initialization(STK8xxx_VAL_RANGE_2G)) { + STK_IRQ = false; + sensor.STK8xxx_Anymotion_init(); + pinMode(STK8XXX_INT, INPUT_PULLUP); + attachInterrupt( + digitalPinToInterrupt(STK8XXX_INT), [] { STK_IRQ = true; }, RISING); + + LOG_DEBUG("STK8XXXSensor::init ok\n"); + return true; + } + LOG_DEBUG("STK8XXXSensor::init failed\n"); + return false; +} + +int32_t STK8XXXSensor::runOnce() +{ + if (STK_IRQ) { + STK_IRQ = false; + if (config.display.wake_on_tap_or_motion) { + wakeScreen(); + } + } + return MOTION_SENSOR_CHECK_INTERVAL_MS; +} + +#endif + +#endif \ No newline at end of file diff --git a/src/motion/STK8XXXSensor.h b/src/motion/STK8XXXSensor.h new file mode 100755 index 000000000..190b916b4 --- /dev/null +++ b/src/motion/STK8XXXSensor.h @@ -0,0 +1,37 @@ +#pragma once +#ifndef _STK8XXX_SENSOR_H_ +#define _STK8XXX_SENSOR_H_ + +#include "MotionSensor.h" + +#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C + +#ifdef STK8XXX_INT + +#include + +class STK8XXXSensor : public MotionSensor +{ + private: + STK8xxx sensor; + + public: + explicit STK8XXXSensor(ScanI2C::FoundDevice foundDevice); + virtual bool init() override; + virtual int32_t runOnce() override; +}; + +#else + +// Stub +class STK8XXXSensor : public MotionSensor +{ + public: + explicit STK8XXXSensor(ScanI2C::FoundDevice foundDevice); +}; + +#endif + +#endif + +#endif \ No newline at end of file diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 797fc7dc3..c0399af38 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -21,6 +21,7 @@ #include "Default.h" #include "serialization/JSON.h" #include "serialization/MeshPacketSerializer.h" +#include #include const int reconnectMax = 5; @@ -31,6 +32,9 @@ static MemoryDynamic staticMqttPool; Allocator &mqttPool = staticMqttPool; +// FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets +static uint8_t bytes[meshtastic_MqttClientProxyMessage_size + 30]; // 12 for channel name and 16 for nodeid + void MQTT::mqttCallback(char *topic, byte *payload, unsigned int length) { mqtt->onReceive(topic, payload, length); @@ -146,7 +150,7 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length) // Generate an implicit ACK towards ourselves (handled and processed only locally!) for this message. // We do this because packets are not rebroadcasted back into MQTT anymore and we assume that at least one node // receives it when we get our own packet back. Then we'll stop our retransmissions. - if (e.packet && getFrom(e.packet) == nodeDB->getNodeNum()) + if (e.packet && isFromUs(e.packet)) routingModule->sendAckNak(meshtastic_Routing_Error_NONE, getFrom(e.packet), e.packet->id, ch.index); else LOG_INFO("Ignoring downlink message we originally sent.\n"); @@ -158,18 +162,33 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length) meshtastic_MeshPacket *p = packetPool.allocCopy(*e.packet); p->via_mqtt = true; // Mark that the packet was received via MQTT + if (isFromUs(p)) { + LOG_INFO("Ignoring downlink message we originally sent.\n"); + packetPool.release(p); + return; + } if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) { + if (moduleConfig.mqtt.encryption_enabled) { + LOG_INFO("Ignoring decoded message on MQTT, encryption is enabled.\n"); + packetPool.release(p); + return; + } + if (p->decoded.portnum == meshtastic_PortNum_ADMIN_APP) { + LOG_INFO("Ignoring decoded admin packet.\n"); + packetPool.release(p); + return; + } p->channel = ch.index; } // PKI messages get accepted even if we can't decrypt if (router && p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag && strcmp(e.channel_id, "PKI") == 0) { - meshtastic_NodeInfoLite *tx = nodeDB->getMeshNode(getFrom(p)); - meshtastic_NodeInfoLite *rx = nodeDB->getMeshNode(p->to); + const meshtastic_NodeInfoLite *tx = nodeDB->getMeshNode(getFrom(p)); + const meshtastic_NodeInfoLite *rx = nodeDB->getMeshNode(p->to); // Only accept PKI messages to us, or if we have both the sender and receiver in our nodeDB, as then it's // likely they discovered each other via a channel we have downlink enabled for - if (p->to == nodeDB->getNodeNum() || (tx && tx->has_user && rx && rx->has_user)) + if (isToUs(p) || (tx && tx->has_user && rx && rx->has_user)) router->enqueueReceivedMessage(p); } else if (router && perhapsDecode(p)) // ignore messages if we don't have the channel key router->enqueueReceivedMessage(p); @@ -310,6 +329,7 @@ void MQTT::reconnect() mqttPassword = moduleConfig.mqtt.password; } #if HAS_WIFI && !defined(ARCH_PORTDUINO) +#if !defined(CONFIG_IDF_TARGET_ESP32C6) && !defined(RPI_PICO) if (moduleConfig.mqtt.tls_enabled) { // change default for encrypted to 8883 try { @@ -325,6 +345,9 @@ void MQTT::reconnect() LOG_INFO("Using non-TLS-encrypted session\n"); pubSub.setClient(mqttClient); } +#else + pubSub.setClient(mqttClient); +#endif #elif HAS_NETWORKING pubSub.setClient(mqttClient); #endif @@ -379,13 +402,14 @@ void MQTT::sendSubscriptions() std::string topic = cryptTopic + channels.getGlobalId(i) + "/+"; LOG_INFO("Subscribing to %s\n", topic.c_str()); pubSub.subscribe(topic.c_str(), 1); // FIXME, is QOS 1 right? -#ifndef ARCH_NRF52 // JSON is not supported on nRF52, see issue #2804 +#if !defined(ARCH_NRF52) || \ + defined(NRF52_USE_JSON) // JSON is not supported on nRF52, see issue #2804 ### Fixed by using ArduinoJSON ### if (moduleConfig.mqtt.json_enabled == true) { std::string topicDecoded = jsonTopic + channels.getGlobalId(i) + "/+"; LOG_INFO("Subscribing to %s\n", topicDecoded.c_str()); pubSub.subscribe(topicDecoded.c_str(), 1); // FIXME, is QOS 1 right? } -#endif // ARCH_NRF52 +#endif // ARCH_NRF52 NRF52_USE_JSON } } #if !MESHTASTIC_EXCLUDE_PKI @@ -466,9 +490,7 @@ void MQTT::publishQueuedMessages() { if (!mqttQueue.isEmpty()) { LOG_DEBUG("Publishing enqueued MQTT message\n"); - // FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets meshtastic_ServiceEnvelope *env = mqttQueue.dequeuePtr(0); - static uint8_t bytes[meshtastic_MeshPacket_size + 64]; size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, env); std::string topic; if (env->packet->pki_encrypted) { @@ -480,7 +502,8 @@ void MQTT::publishQueuedMessages() publish(topic.c_str(), bytes, numBytes, false); -#ifndef ARCH_NRF52 // JSON is not supported on nRF52, see issue #2804 +#if !defined(ARCH_NRF52) || \ + defined(NRF52_USE_JSON) // JSON is not supported on nRF52, see issue #2804 ### Fixed by using ArduinoJson ### if (moduleConfig.mqtt.json_enabled) { // handle json topic auto jsonString = MeshPacketSerializer::JsonSerialize(env->packet); @@ -496,7 +519,7 @@ void MQTT::publishQueuedMessages() publish(topicJson.c_str(), jsonString.c_str(), false); } } -#endif // ARCH_NRF52 +#endif // ARCH_NRF52 NRF52_USE_JSON mqttPool.release(env); } } @@ -514,19 +537,29 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp, const meshtastic_MeshPacket & return; // no channels have an uplink enabled auto &ch = channels.getByIndex(chIndex); - if (mp_decoded.which_payload_variant != meshtastic_MeshPacket_decoded_tag) { - LOG_CRIT("MQTT::onSend(): mp_decoded isn't actually decoded\n"); - return; - } + if (!mp.pki_encrypted) { + if (mp_decoded.which_payload_variant != meshtastic_MeshPacket_decoded_tag) { + LOG_CRIT("MQTT::onSend(): mp_decoded isn't actually decoded\n"); + return; + } - if (strcmp(moduleConfig.mqtt.address, default_mqtt_address) == 0 && - (mp_decoded.decoded.portnum == meshtastic_PortNum_RANGE_TEST_APP || - mp_decoded.decoded.portnum == meshtastic_PortNum_DETECTION_SENSOR_APP)) { - LOG_DEBUG("MQTT onSend - Ignoring range test or detection sensor message on public mqtt\n"); - return; - } + // check for the lowest bit of the data bitfield set false, and the use of one of the default keys. + if (!isFromUs(&mp_decoded) && mp_decoded.decoded.has_bitfield && + !(mp_decoded.decoded.bitfield & BITFIELD_OK_TO_MQTT_MASK) && + (ch.settings.psk.size < 2 || (ch.settings.psk.size == 16 && memcmp(ch.settings.psk.bytes, defaultpsk, 16)) || + (ch.settings.psk.size == 32 && memcmp(ch.settings.psk.bytes, eventpsk, 32)))) { + LOG_INFO("MQTT onSend - Not forwarding packet due to DontMqttMeBro flag\n"); + return; + } - if (ch.settings.uplink_enabled || mp.pki_encrypted) { + if (strcmp(moduleConfig.mqtt.address, default_mqtt_address) == 0 && + (mp_decoded.decoded.portnum == meshtastic_PortNum_RANGE_TEST_APP || + mp_decoded.decoded.portnum == meshtastic_PortNum_DETECTION_SENSOR_APP)) { + LOG_DEBUG("MQTT onSend - Ignoring range test or detection sensor message on public mqtt\n"); + return; + } + } + if (mp.pki_encrypted || ch.settings.uplink_enabled) { const char *channelId = mp.pki_encrypted ? "PKI" : channels.getGlobalId(chIndex); meshtastic_ServiceEnvelope *env = mqttPool.allocZeroed(); @@ -537,21 +570,21 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp, const meshtastic_MeshPacket & if (moduleConfig.mqtt.encryption_enabled) { env->packet = (meshtastic_MeshPacket *)∓ LOG_DEBUG("encrypted message\n"); - } else { + } else if (mp_decoded.which_payload_variant == + meshtastic_MeshPacket_decoded_tag) { // Don't upload a still-encrypted PKI packet env->packet = (meshtastic_MeshPacket *)&mp_decoded; LOG_DEBUG("portnum %i message\n", env->packet->decoded.portnum); } if (moduleConfig.mqtt.proxy_to_client_enabled || this->isConnectedDirectly()) { - // FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets - static uint8_t bytes[meshtastic_MeshPacket_size + 64]; size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, env); std::string topic = cryptTopic + channelId + "/" + owner.id; LOG_DEBUG("MQTT Publish %s, %u bytes\n", topic.c_str(), numBytes); publish(topic.c_str(), bytes, numBytes, false); -#ifndef ARCH_NRF52 // JSON is not supported on nRF52, see issue #2804 +#if !defined(ARCH_NRF52) || \ + defined(NRF52_USE_JSON) // JSON is not supported on nRF52, see issue #2804 ### Fixed by using ArduinoJson ### if (moduleConfig.mqtt.json_enabled) { // handle json topic auto jsonString = MeshPacketSerializer::JsonSerialize((meshtastic_MeshPacket *)&mp_decoded); @@ -562,7 +595,7 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp, const meshtastic_MeshPacket & publish(topicJson.c_str(), jsonString.c_str(), false); } } -#endif // ARCH_NRF52 +#endif // ARCH_NRF52 NRF52_USE_JSON } else { LOG_INFO("MQTT not connected, queueing packet\n"); if (mqttQueue.numFree() == 0) { @@ -584,7 +617,7 @@ void MQTT::perhapsReportToMap() if (!moduleConfig.mqtt.map_reporting_enabled || !(moduleConfig.mqtt.proxy_to_client_enabled || isConnectedDirectly())) return; - if (millis() - last_report_to_map < map_publish_interval_msecs) { + if (Throttle::isWithinTimespanMs(last_report_to_map, map_publish_interval_msecs)) { return; } else { if (map_position_precision == 0 || (localPosition.latitude_i == 0 && localPosition.longitude_i == 0)) { @@ -639,8 +672,6 @@ void MQTT::perhapsReportToMap() &meshtastic_MapReport_msg, &mapReport); se->packet = mp; - // FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets - static uint8_t bytes[meshtastic_MeshPacket_size + 64]; size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, se); LOG_INFO("MQTT Publish map report to %s\n", mapTopic.c_str()); @@ -664,4 +695,4 @@ bool MQTT::isValidJsonEnvelope(JSONObject &json) (json["from"]->AsNumber() == nodeDB->getNodeNum()) && // only accept message if the "from" is us (json.find("type") != json.end()) && json["type"]->IsString() && // should specify a type (json.find("payload") != json.end()); // should have a payload -} +} \ No newline at end of file diff --git a/src/mqtt/MQTT.h b/src/mqtt/MQTT.h index ba0987783..c827e12ca 100644 --- a/src/mqtt/MQTT.h +++ b/src/mqtt/MQTT.h @@ -9,9 +9,11 @@ #if HAS_WIFI #include #if !defined(ARCH_PORTDUINO) +#if defined(ESP_ARDUINO_VERSION_MAJOR) && ESP_ARDUINO_VERSION_MAJOR < 3 #include #endif #endif +#endif #if HAS_ETHERNET #include #endif @@ -33,9 +35,11 @@ class MQTT : private concurrency::OSThread #if HAS_WIFI WiFiClient mqttClient; #if !defined(ARCH_PORTDUINO) +#if defined(ESP_ARDUINO_VERSION_MAJOR) && ESP_ARDUINO_VERSION_MAJOR < 3 WiFiClientSecure wifiSecureClient; #endif #endif +#endif #if HAS_ETHERNET EthernetClient mqttClient; #endif diff --git a/src/nimble/NimbleBluetooth.cpp b/src/nimble/NimbleBluetooth.cpp index d959553a4..eedfe1a71 100644 --- a/src/nimble/NimbleBluetooth.cpp +++ b/src/nimble/NimbleBluetooth.cpp @@ -44,6 +44,9 @@ static BluetoothPhoneAPI *bluetoothPhoneAPI; * Subclasses can use this as a hook to provide custom notifications for their transport (i.e. bluetooth notifies) */ +// Last ToRadio value received from the phone +static uint8_t lastToRadio[MAX_TO_FROM_RADIO_SIZE]; + class NimbleBluetoothToRadioCallback : public NimBLECharacteristicCallbacks { virtual void onWrite(NimBLECharacteristic *pCharacteristic) @@ -51,7 +54,13 @@ class NimbleBluetoothToRadioCallback : public NimBLECharacteristicCallbacks LOG_INFO("To Radio onwrite\n"); auto val = pCharacteristic->getValue(); - bluetoothPhoneAPI->handleToRadio(val.data(), val.length()); + if (memcmp(lastToRadio, val.data(), val.length()) != 0) { + LOG_DEBUG("New ToRadio packet\n"); + memcpy(lastToRadio, val.data(), val.length()); + bluetoothPhoneAPI->handleToRadio(val.data(), val.length()); + } else { + LOG_DEBUG("Dropping duplicate ToRadio packet we just saw\n"); + } } }; @@ -124,7 +133,14 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks } } - virtual void onDisconnect(NimBLEServer *pServer, ble_gap_conn_desc *desc) { LOG_INFO("BLE disconnect\n"); } + virtual void onDisconnect(NimBLEServer *pServer, ble_gap_conn_desc *desc) + { + LOG_INFO("BLE disconnect\n"); + + if (bluetoothPhoneAPI) { + bluetoothPhoneAPI->close(); + } + } }; static NimbleBluetoothToRadioCallback *toRadioCallbacks; diff --git a/src/platform/esp32/CallbackCharacteristic.h b/src/platform/esp32/CallbackCharacteristic.h deleted file mode 100644 index cd3bc6f51..000000000 --- a/src/platform/esp32/CallbackCharacteristic.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include "BLECharacteristic.h" -#include "PowerFSM.h" // FIXME - someday I want to make this OTA thing a separate lb at at that point it can't touch this - -/** - * A characteristic with a set of overridable callbacks - */ -class CallbackCharacteristic : public BLECharacteristic, public BLECharacteristicCallbacks -{ - public: - CallbackCharacteristic(const char *uuid, uint32_t btprops) : BLECharacteristic(uuid, btprops) { setCallbacks(this); } -}; diff --git a/src/platform/esp32/architecture.h b/src/platform/esp32/architecture.h index f86b342ce..01f416629 100644 --- a/src/platform/esp32/architecture.h +++ b/src/platform/esp32/architecture.h @@ -76,6 +76,8 @@ #ifdef HELTEC_V2_1 #define HW_VENDOR meshtastic_HardwareModel_HELTEC_V2_1 #endif +#elif defined(HELTEC_WIRELESS_BRIDGE) +#define HW_VENDOR meshtastic_HardwareModel_HELTEC_WIRELESS_BRIDGE #elif defined(ARDUINO_HELTEC_WIFI_LORA_32) #define HW_VENDOR meshtastic_HardwareModel_HELTEC_V1 #elif defined(TLORA_V1) @@ -88,6 +90,8 @@ #define HW_VENDOR meshtastic_HardwareModel_TLORA_V2_1_1P6 #elif defined(TLORA_V2_1_18) #define HW_VENDOR meshtastic_HardwareModel_TLORA_V2_1_1P8 +#elif defined(TLORA_C6) +#define HW_VENDOR meshtastic_HardwareModel_TLORA_C6 #elif defined(T_DECK) #define HW_VENDOR meshtastic_HardwareModel_T_DECK #elif defined(T_WATCH_S3) @@ -166,6 +170,10 @@ #define HW_VENDOR meshtastic_HardwareModel_HELTEC_VISION_MASTER_E290 #elif defined(HELTEC_MESH_NODE_T114) #define HW_VENDOR meshtastic_HardwareModel_HELTEC_MESH_NODE_T114 +#elif defined(SENSECAP_INDICATOR) +#define HW_VENDOR meshtastic_HardwareModel_SENSECAP_INDICATOR +#elif defined(SEEED_XIAO_S3) +#define HW_VENDOR meshtastic_HardwareModel_SEEED_XIAO_S3 #endif // ----------------------------------------------------------------------------- diff --git a/src/platform/esp32/main-esp32.cpp b/src/platform/esp32/main-esp32.cpp index 1b997500a..f16accab6 100644 --- a/src/platform/esp32/main-esp32.cpp +++ b/src/platform/esp32/main-esp32.cpp @@ -13,6 +13,7 @@ #include "mesh/wifi/WiFiAPClient.h" #endif +#include "esp_mac.h" #include "meshUtils.h" #include "sleep.h" #include "soc/rtc.h" @@ -120,17 +121,24 @@ void esp32Setup() uint32_t rebootCounter = preferences.getUInt("rebootCounter", 0); rebootCounter++; preferences.putUInt("rebootCounter", rebootCounter); + // store firmware version and hwrevision for access from OTA firmware + String fwrev = preferences.getString("firmwareVersion", ""); + if (fwrev.compareTo(optstr(APP_VERSION)) != 0) + preferences.putString("firmwareVersion", optstr(APP_VERSION)); + uint8_t hwven = preferences.getUInt("hwVendor", 0); + if (hwven != HW_VENDOR) + preferences.putUInt("hwVendor", HW_VENDOR); preferences.end(); LOG_DEBUG("Number of Device Reboots: %d\n", rebootCounter); #if !MESHTASTIC_EXCLUDE_BLUETOOTH String BLEOTA = BleOta::getOtaAppVersion(); if (BLEOTA.isEmpty()) { - LOG_DEBUG("No OTA firmware available\n"); + LOG_INFO("No OTA firmware available\n"); } else { - LOG_DEBUG("OTA firmware version %s\n", BLEOTA.c_str()); + LOG_INFO("OTA firmware version %s\n", BLEOTA.c_str()); } #else - LOG_DEBUG("No OTA firmware available\n"); + LOG_INFO("No OTA firmware available\n"); #endif // enableModemSleep(); @@ -140,9 +148,16 @@ void esp32Setup() // #define APP_WATCHDOG_SECS 45 #define APP_WATCHDOG_SECS 90 +#ifdef CONFIG_IDF_TARGET_ESP32C6 + esp_task_wdt_config_t *wdt_config = (esp_task_wdt_config_t *)malloc(sizeof(esp_task_wdt_config_t)); + wdt_config->timeout_ms = APP_WATCHDOG_SECS * 1000; + wdt_config->trigger_panic = true; + res = esp_task_wdt_init(wdt_config); + assert(res == ESP_OK); +#else res = esp_task_wdt_init(APP_WATCHDOG_SECS, true); assert(res == ESP_OK); - +#endif res = esp_task_wdt_add(NULL); assert(res == ESP_OK); @@ -216,7 +231,7 @@ void cpuDeepSleep(uint32_t msecToWake) // to detect wake and in normal operation the external part drives them hard. #ifdef BUTTON_PIN // Only GPIOs which are have RTC functionality can be used in this bit map: 0,2,4,12-15,25-27,32-39. -#if SOC_RTCIO_HOLD_SUPPORTED +#if SOC_RTCIO_HOLD_SUPPORTED && SOC_PM_SUPPORT_EXT_WAKEUP uint64_t gpioMask = (1ULL << (config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN)); #endif diff --git a/src/platform/extra_variants/heltec_wireless_tracker/variant.cpp b/src/platform/extra_variants/heltec_wireless_tracker/variant.cpp index 84264ef58..0a19a9c3b 100644 --- a/src/platform/extra_variants/heltec_wireless_tracker/variant.cpp +++ b/src/platform/extra_variants/heltec_wireless_tracker/variant.cpp @@ -10,10 +10,14 @@ void lateInitVariant() { // LOG_DEBUG("Heltec tracker initVariant\n"); -#ifdef VEXT_ENABLE - GpioPin *hwEnable = new GpioHwPin(VEXT_ENABLE); - GpioVirtPin *virtGpsEnable = gps ? gps->enablePin : new GpioVirtPin(); +#ifndef MESHTASTIC_EXCLUDE_GPS + GpioVirtPin *virtGpsEnable = gps ? gps->enablePin : new GpioVirtPin(); +#else + GpioVirtPin *virtGpsEnable = new GpioVirtPin(); +#endif + +#ifndef MESHTASTIC_EXCLUDE_SCREEN // On this board we are actually using the backlightEnable signal to already be controlling a physical enable to the // display controller. But we'd _ALSO_ like to have that signal drive a virtual GPIO. So nest it as needed. GpioVirtPin *virtScreenEnable = new GpioVirtPin(); @@ -25,8 +29,11 @@ void lateInitVariant() // Assume screen is initially powered splitter->set(true); } +#endif +#if defined(VEXT_ENABLE) && (!defined(MESHTASTIC_EXCLUDE_GPS) || !defined(MESHTASTIC_EXCLUDE_SCREEN)) // If either the GPS or the screen is on, turn on the external power regulator + GpioPin *hwEnable = new GpioHwPin(VEXT_ENABLE); new GpioBinaryTransformer(virtGpsEnable, virtScreenEnable, hwEnable, GpioBinaryTransformer::Or); #endif } diff --git a/src/platform/nrf52/BLEDfuScure.cpp b/src/platform/nrf52/BLEDfuScure.cpp index 8f7a327c4..82cb8905a 100644 --- a/src/platform/nrf52/BLEDfuScure.cpp +++ b/src/platform/nrf52/BLEDfuScure.cpp @@ -48,11 +48,10 @@ extern "C" void bootloader_util_app_start(uint32_t start_addr); static uint16_t crc16(const uint8_t *data_p, uint8_t length) { - uint8_t x; uint16_t crc = 0xFFFF; while (length--) { - x = crc >> 8 ^ *data_p++; + uint8_t x = crc >> 8 ^ *data_p++; x ^= x >> 4; crc = (crc << 8) ^ ((uint16_t)(x << 12)) ^ ((uint16_t)(x << 5)) ^ ((uint16_t)x); } diff --git a/src/platform/nrf52/JLINK_MONITOR.c b/src/platform/nrf52/JLINK_MONITOR.c deleted file mode 100644 index 160264905..000000000 --- a/src/platform/nrf52/JLINK_MONITOR.c +++ /dev/null @@ -1,124 +0,0 @@ -/********************************************************************* -* SEGGER Microcontroller GmbH & Co. KG * -* The Embedded Experts * -********************************************************************** -* * -* (c) 1995 - 2015 SEGGER Microcontroller GmbH & Co. KG * -* * -* www.segger.com Support: support@segger.com * -* * -********************************************************************** - ----------------------------------------------------------------------- -File : JLINK_MONITOR.c -Purpose : Implementation of debug monitor for J-Link monitor mode debug on Cortex-M devices. --------- END-OF-HEADER --------------------------------------------- -*/ - -#include "JLINK_MONITOR.h" - -/********************************************************************* - * - * Configuration - * - ********************************************************************** - */ - -/********************************************************************* - * - * Defines - * - ********************************************************************** - */ - -/********************************************************************* - * - * Types - * - ********************************************************************** - */ - -/********************************************************************* - * - * Static data - * - ********************************************************************** - */ - -volatile int MAIN_MonCnt; // Incremented in JLINK_MONITOR_OnPoll() while CPU is in debug mode - -/********************************************************************* - * - * Local functions - * - ********************************************************************** - */ - -/********************************************************************* - * - * Global functions - * - ********************************************************************** - */ - -/********************************************************************* - * - * JLINK_MONITOR_OnExit() - * - * Function description - * Called from DebugMon_Handler(), once per debug exit. - * May perform some target specific operations to be done on debug mode exit. - * - * Notes - * (1) Must not keep the CPU busy for more than 100 ms - */ -void JLINK_MONITOR_OnExit(void) -{ - // - // Add custom code here - // - // BSP_ClrLED(0); -} - -/********************************************************************* - * - * JLINK_MONITOR_OnEnter() - * - * Function description - * Called from DebugMon_Handler(), once per debug entry. - * May perform some target specific operations to be done on debug mode entry - * - * Notes - * (1) Must not keep the CPU busy for more than 100 ms - */ -void JLINK_MONITOR_OnEnter(void) -{ - // - // Add custom code here - // - // BSP_SetLED(0); - // BSP_ClrLED(1); -} - -/********************************************************************* - * - * JLINK_MONITOR_OnPoll() - * - * Function description - * Called periodically from DebugMon_Handler(), to perform some actions that need to be performed periodically during debug - * mode. - * - * Notes - * (1) Must not keep the CPU busy for more than 100 ms - */ -void JLINK_MONITOR_OnPoll(void) -{ - // - // Add custom code here - // - MAIN_MonCnt++; - // BSP_ToggleLED(0); - // _Delay(500000); -} - -/****** End Of File *************************************************/ diff --git a/src/platform/nrf52/JLINK_MONITOR.h b/src/platform/nrf52/JLINK_MONITOR.h deleted file mode 100644 index 87cf332fe..000000000 --- a/src/platform/nrf52/JLINK_MONITOR.h +++ /dev/null @@ -1,27 +0,0 @@ -/********************************************************************* -* SEGGER Microcontroller GmbH & Co. KG * -* The Embedded Experts * -********************************************************************** -* * -* (c) 1995 - 2015 SEGGER Microcontroller GmbH & Co. KG * -* * -* www.segger.com Support: support@segger.com * -* * -********************************************************************** - ----------------------------------------------------------------------- -File : JLINK_MONITOR.h -Purpose : Header file of debug monitor for J-Link monitor mode debug on Cortex-M devices. --------- END-OF-HEADER --------------------------------------------- -*/ - -#ifndef JLINK_MONITOR_H -#define JLINK_MONITOR_H - -void JLINK_MONITOR_OnExit(void); -void JLINK_MONITOR_OnEnter(void); -void JLINK_MONITOR_OnPoll(void); - -#endif - -/****** End Of File *************************************************/ diff --git a/src/platform/nrf52/JLINK_MONITOR_ISR_SES.S b/src/platform/nrf52/JLINK_MONITOR_ISR_SES.S deleted file mode 100644 index cda4b1a50..000000000 --- a/src/platform/nrf52/JLINK_MONITOR_ISR_SES.S +++ /dev/null @@ -1,888 +0,0 @@ -/********************************************************************* -* SEGGER Microcontroller GmbH & Co. KG * -* The Embedded Experts * -********************************************************************** -* * -* (c) 1995 - 2015 SEGGER Microcontroller GmbH & Co. KG * -* * -* www.segger.com Support: support@segger.com * -* * -********************************************************************** - ----------------------------------------------------------------------- -File : JLINK_MONITOR_ISR_SES.s -Purpose : Implementation of debug monitor for J-Link monitor mode - debug on Cortex-M devices, supporting SES compiler. --------- END-OF-HEADER --------------------------------------------- -*/ - - .name JLINK_MONITOR_ISR - .syntax unified - - .extern JLINK_MONITOR_OnEnter - .extern JLINK_MONITOR_OnExit - .extern JLINK_MONITOR_OnPoll - - .global DebugMon_Handler - -/********************************************************************* -* -* Defines, configurable -* -********************************************************************** -*/ - -#define _MON_VERSION 100 // V x.yy - -/********************************************************************* -* -* Defines, fixed -* -********************************************************************** -*/ - -#define _APP_SP_OFF_R0 0x00 -#define _APP_SP_OFF_R1 0x04 -#define _APP_SP_OFF_R2 0x08 -#define _APP_SP_OFF_R3 0x0C -#define _APP_SP_OFF_R12 0x10 -#define _APP_SP_OFF_R14_LR 0x14 -#define _APP_SP_OFF_PC 0x18 -#define _APP_SP_OFF_XPSR 0x1C -#define _APP_SP_OFF_S0 0x20 -#define _APP_SP_OFF_S1 0x24 -#define _APP_SP_OFF_S2 0x28 -#define _APP_SP_OFF_S3 0x2C -#define _APP_SP_OFF_S4 0x30 -#define _APP_SP_OFF_S5 0x34 -#define _APP_SP_OFF_S6 0x38 -#define _APP_SP_OFF_S7 0x3C -#define _APP_SP_OFF_S8 0x40 -#define _APP_SP_OFF_S9 0x44 -#define _APP_SP_OFF_S10 0x48 -#define _APP_SP_OFF_S11 0x4C -#define _APP_SP_OFF_S12 0x50 -#define _APP_SP_OFF_S13 0x54 -#define _APP_SP_OFF_S14 0x58 -#define _APP_SP_OFF_S15 0x5C -#define _APP_SP_OFF_FPSCR 0x60 - -#define _NUM_BYTES_BASIC_STACKFRAME 32 -#define _NUM_BYTES_EXTENDED_STACKFRAME 72 - -#define _SYSTEM_DCRDR_OFF 0x00 -#define _SYSTEM_DEMCR_OFF 0x04 - -#define _SYSTEM_DHCSR 0xE000EDF0 // Debug Halting Control and Status Register (DHCSR) -#define _SYSTEM_DCRSR 0xE000EDF4 // Debug Core Register Selector Register (DCRSR) -#define _SYSTEM_DCRDR 0xE000EDF8 // Debug Core Register Data Register (DCRDR) -#define _SYSTEM_DEMCR 0xE000EDFC // Debug Exception and Monitor Control Register (DEMCR) - -#define _SYSTEM_FPCCR 0xE000EF34 // Floating-Point Context Control Register (FPCCR) -#define _SYSTEM_FPCAR 0xE000EF38 // Floating-Point Context Address Register (FPCAR) -#define _SYSTEM_FPDSCR 0xE000EF3C // Floating-Point Default Status Control Register (FPDSCR) -#define _SYSTEM_MVFR0 0xE000EF40 // Media and FP Feature Register 0 (MVFR0) -#define _SYSTEM_MVFR1 0xE000EF44 // Media and FP Feature Register 1 (MVFR1) - -/* -* Defines for determining if the current debug config supports FPU registers -* For some compilers like IAR EWARM when disabling the FPU in the compiler settings an error is thrown when -*/ -#ifdef __FPU_PRESENT - #if __FPU_PRESENT - #define _HAS_FPU_REGS 1 - #else - #define _HAS_FPU_REGS 0 - #endif -#else - #define _HAS_FPU_REGS 0 -#endif - -/********************************************************************* -* -* Signature of monitor -* -* Function description -* Needed for targets where also a boot ROM is present that possibly specifies a vector table with a valid debug monitor exception entry -*/ - .section .text, "ax" - - // - // JLINKMONHANDLER - // - .byte 0x4A - .byte 0x4C - .byte 0x49 - .byte 0x4E - .byte 0x4B - .byte 0x4D - .byte 0x4F - .byte 0x4E - .byte 0x48 - .byte 0x41 - .byte 0x4E - .byte 0x44 - .byte 0x4C - .byte 0x45 - .byte 0x52 - .byte 0x00 // Align to 8-bytes - -/********************************************************************* -* -* DebugMon_Handler() -* -* Function description -* Debug monitor handler. CPU enters this handler in case a "halt" request is made from the debugger. -* This handler is also responsible for handling commands that are sent by the debugger. -* -* Notes -* This is actually the ISR for the debug interrupt (exception no. 12) -*/ - .thumb_func - -DebugMon_Handler: - /* - General procedure: - DCRDR is used as communication register - DEMCR[19] is used as ready flag - For the command J-Link sends to the monitor: DCRDR[7:0] == Cmd, DCRDR[31:8] == ParamData - - 1) Monitor sets DEMCR[19] whenever it is ready to receive new commands/data - DEMCR[19] is initially set on debug monitor entry - 2) J-Link will clear once it has placed conmmand/data in DCRDR for J-Link - 3) Monitor will wait for DEMCR[19] to be cleared - 4) Monitor will process command (May cause additional data transfers etc., depends on command - 5) No restart-CPU command? => Back to 2), Otherwise => 6) - 6) Monitor will clear DEMCR[19] 19 to indicate that it is no longer ready - */ - PUSH {LR} - BL JLINK_MONITOR_OnEnter - POP {LR} - LDR.N R3,_AddrDCRDR // 0xe000edf8 == _SYSTEM_DCRDR - B.N _IndicateMonReady -_WaitProbeReadIndicateMonRdy: // while(_SYSTEM_DEMCR & (1uL << 19)); => Wait until J-Link has read item - LDR R0,[R3, #+_SYSTEM_DEMCR_OFF] // _SYSTEM_DEMCR - LSLS R0,R0,#+12 - BMI.N _WaitProbeReadIndicateMonRdy -_IndicateMonReady: - LDR R0,[R3, #+_SYSTEM_DEMCR_OFF] // _SYSTEM_DEMCR |= (1uL << 19); => Set MON_REQ bit, so J-Link knows monitor is ready to receive commands - ORR R0,R0,#0x80000 - STR R0,[R3, #+_SYSTEM_DEMCR_OFF] - /* - During command loop: - R0 = Tmp - R1 = Tmp - R2 = Tmp - R3 = &_SYSTEM_DCRDR (allows also access to DEMCR with offset) - R12 = Tmp - - Outside command loop R0-R3 and R12 may be overwritten by MONITOR_OnPoll() - */ -_WaitForJLinkCmd: // do { - PUSH {LR} - BL JLINK_MONITOR_OnPoll - POP {LR} - LDR.N R3,_AddrDCRDR // 0xe000edf8 == _SYSTEM_DCRDR - LDR R0,[R3, #+_SYSTEM_DEMCR_OFF] - LSRS R0,R0,#+20 // DEMCR[19] -> Carry Clear? => J-Link has placed command for us - BCS _WaitForJLinkCmd - /* - Perform command - Command is placed by J-Link in DCRDR[7:0] and additional parameter data is stored in DCRDR[31:8] - J-Link clears DEMCR[19] to indicate that it placed a command/data or read data - Monitor sets DEMCR[19] to indicate that it placed data or read data / is ready for a new command - Setting DEMCR[19] indicates "monitor ready for new command / data" and also indicates: "data has been placed in DCRDR by monitor, for J-Link" - Therefore it is responsibility of the commands to respond to the commands accordingly - - Commands for debug monitor - Commands must not exceed 0xFF (255) as we only defined 8-bits for command-part. Higher 24-bits are parameter info for current command - - Protocol for different commands: - J-Link: Cmd -> DCRDR, DEMCR[19] -> 0 => Cmd placed by probe - */ - LDR R0,[R3, #+_SYSTEM_DCRDR_OFF] // ParamInfo = _SYSTEM_DCRDR - LSRS R1,R0,#+8 // ParamInfo >>= 8 - LSLS R0,R0,#+24 - LSRS R0,R0,#+24 // Cmd = ParamInfo & 0xFF - // - // switch (Cmd) - // - CMP R0,#+0 - BEQ.N _HandleGetMonVersion // case _MON_CMD_GET_MONITOR_VERSION - CMP R0,#+2 - BEQ.N _HandleReadReg // case _MON_CMD_READ_REG - BCC.N _HandleRestartCPU // case _MON_CMD_RESTART_CPU - CMP R0,#+3 - BEQ.N _HandleWriteReg_Veneer // case _MON_CMD_WRITE_REG - B.N _IndicateMonReady // default : while (1); - /* - Return - _MON_CMD_RESTART_CPU - CPU: DEMCR[19] -> 0 => Monitor no longer ready - */ -_HandleRestartCPU: - LDR R0,[R3, #+_SYSTEM_DEMCR_OFF] // _SYSTEM_DEMCR &= ~(1uL << 19); => Clear MON_REQ to indicate that monitor is no longer active - BIC R0,R0,#0x80000 - STR R0,[R3, #+_SYSTEM_DEMCR_OFF] - PUSH {LR} - BL JLINK_MONITOR_OnExit - POP {PC} - // - // Place data section here to not get in trouble with load-offsets - // - .section .text, "ax", %progbits - .align 2 -_AddrDCRDR: - .long 0xE000EDF8 -_AddrCPACR: - .long 0xE000ED88 - - .section .text, "ax" - .thumb_func - -;/********************************************************************* -;* -;* _HandleGetMonVersion -;* -;*/ -_HandleGetMonVersion: - /* - _MON_CMD_GET_MONITOR_VERSION - CPU: Data -> DCRDR, DEMCR[19] -> 1 => Data ready - J-Link: DCRDR -> Read, DEMCR[19] -> 0 => Data read - CPU: DEMCR[19] -> 1 => Mon ready - */ - MOVS R0,#+_MON_VERSION - STR R0,[R3, #+_SYSTEM_DCRDR_OFF] // _SYSTEM_DCRDR = x - LDR R0,[R3, #+_SYSTEM_DEMCR_OFF] // _SYSTEM_DEMCR |= (1uL << 19); => Set MON_REQ bit, so J-Link knows monitor is ready to receive commands - ORR R0,R0,#0x80000 - STR R0,[R3, #+_SYSTEM_DEMCR_OFF] // Indicate data ready - B _WaitProbeReadIndicateMonRdy - -/********************************************************************* -* -* _HandleReadReg -* -*/ -_HandleWriteReg_Veneer: - B.N _HandleWriteReg -_HandleReadReg: - /* - _MON_CMD_READ_REG - CPU: Data -> DCRDR, DEMCR[19] -> 1 => Data ready - J-Link: DCRDR -> Read, DEMCR[19] -> 0 => Data read - CPU: DEMCR[19] -> 1 => Mon ready - - - Register indexes - 0-15: R0-R15 (13 == R13 reserved => is banked ... Has to be read as PSP / MSP. Decision has to be done by J-Link DLL side!) - 16: XPSR - 17: MSP - 18: PSP - 19: CFBP CONTROL/FAULTMASK/BASEPRI/PRIMASK (packed into 4 bytes of word. CONTROL = CFBP[31:24], FAULTMASK = CFBP[16:23], BASEPRI = CFBP[15:8], PRIMASK = CFBP[7:0] - 20: FPSCR - 21-52: FPS0-FPS31 - - - Register usage when entering this "subroutine": - R0 Cmd - R1 ParamInfo - R2 --- - R3 = &_SYSTEM_DCRDR (allows also access to DEMCR with offset) - R12 --- - - Table B1-9 EXC_RETURN definition of exception return behavior, with FP extension - LR Return to Return SP Frame type - --------------------------------------------------------- - 0xFFFFFFE1 Handler mode. MSP Extended - 0xFFFFFFE9 Thread mode MSP Extended - 0xFFFFFFED Thread mode PSP Extended - 0xFFFFFFF1 Handler mode. MSP Basic - 0xFFFFFFF9 Thread mode MSP Basic - 0xFFFFFFFD Thread mode PSP Basic - - So LR[2] == 1 => Return stack == PSP else MSP - - R0-R3, R12, PC, xPSR can be read from application stackpointer - Other regs can be read directly - */ - LSRS R2,LR,#+3 // Shift LR[2] into carry => Carry clear means that CPU was running on MSP - ITE CS - MRSCS R2,PSP - MRSCC R2,MSP - CMP R1,#+4 // if (RegIndex < 4) { (R0-R3) - BCS _HandleReadRegR4 - LDR R0,[R2, R1, LSL #+2] // v = [SP + Rx * 4] (R0-R3) - B.N _HandleReadRegDone -_HandleReadRegR4: - CMP R1,#+5 // if (RegIndex < 5) { (R4) - BCS _HandleReadRegR5 - MOV R0,R4 - B.N _HandleReadRegDone -_HandleReadRegR5: - CMP R1,#+6 // if (RegIndex < 6) { (R5) - BCS _HandleReadRegR6 - MOV R0,R5 - B.N _HandleReadRegDone -_HandleReadRegR6: - CMP R1,#+7 // if (RegIndex < 7) { (R6) - BCS _HandleReadRegR7 - MOV R0,R6 - B.N _HandleReadRegDone -_HandleReadRegR7: - CMP R1,#+8 // if (RegIndex < 8) { (R7) - BCS _HandleReadRegR8 - MOV R0,R7 - B.N _HandleReadRegDone -_HandleReadRegR8: - CMP R1,#+9 // if (RegIndex < 9) { (R8) - BCS _HandleReadRegR9 - MOV R0,R8 - B.N _HandleReadRegDone -_HandleReadRegR9: - CMP R1,#+10 // if (RegIndex < 10) { (R9) - BCS _HandleReadRegR10 - MOV R0,R9 - B.N _HandleReadRegDone -_HandleReadRegR10: - CMP R1,#+11 // if (RegIndex < 11) { (R10) - BCS _HandleReadRegR11 - MOV R0,R10 - B.N _HandleReadRegDone -_HandleReadRegR11: - CMP R1,#+12 // if (RegIndex < 12) { (R11) - BCS _HandleReadRegR12 - MOV R0,R11 - B.N _HandleReadRegDone -_HandleReadRegR12: - CMP R1,#+14 // if (RegIndex < 14) { (R12) - BCS _HandleReadRegR14 - LDR R0,[R2, #+_APP_SP_OFF_R12] - B.N _HandleReadRegDone -_HandleReadRegR14: - CMP R1,#+15 // if (RegIndex < 15) { (R14 / LR) - BCS _HandleReadRegR15 - LDR R0,[R2, #+_APP_SP_OFF_R14_LR] - B.N _HandleReadRegDone -_HandleReadRegR15: - CMP R1,#+16 // if (RegIndex < 16) { (R15 / PC) - BCS _HandleReadRegXPSR - LDR R0,[R2, #+_APP_SP_OFF_PC] - B.N _HandleReadRegDone -_HandleReadRegXPSR: - CMP R1,#+17 // if (RegIndex < 17) { (xPSR) - BCS _HandleReadRegMSP - LDR R0,[R2, #+_APP_SP_OFF_XPSR] - B.N _HandleReadRegDone -_HandleReadRegMSP: - /* - Stackpointer is tricky because we need to get some info about the SP used in the user app, first - - Handle reading R0-R3 which can be read right from application stackpointer - - Table B1-9 EXC_RETURN definition of exception return behavior, with FP extension - LR Return to Return SP Frame type - --------------------------------------------------------- - 0xFFFFFFE1 Handler mode. MSP Extended - 0xFFFFFFE9 Thread mode MSP Extended - 0xFFFFFFED Thread mode PSP Extended - 0xFFFFFFF1 Handler mode. MSP Basic - 0xFFFFFFF9 Thread mode MSP Basic - 0xFFFFFFFD Thread mode PSP Basic - - So LR[2] == 1 => Return stack == PSP else MSP - Per architecture definition: Inside monitor (exception) SP = MSP - - Stack pointer handling is complicated because it is different what is pushed on the stack before entering the monitor ISR... - Cortex-M: 8 regs - Cortex-M + forced-stack-alignment: 8 regs + 1 dummy-word if stack was not 8-byte aligned - Cortex-M + FPU: 8 regs + 17 FPU regs + 1 dummy-word + 1-dummy word if stack was not 8-byte aligned - Cortex-M + FPU + lazy mode: 8 regs + 17 dummy-words + 1 dummy-word + 1-dummy word if stack was not 8-byte aligned - */ - CMP R1,#+18 // if (RegIndex < 18) { (MSP) - BCS _HandleReadRegPSP - MRS R0,MSP - LSRS R1,LR,#+3 // LR[2] -> Carry == 0 => CPU was running on MSP => Needs correction - BCS _HandleReadRegDone_Veneer // CPU was running on PSP? => No correction necessary -_HandleSPCorrection: - LSRS R1,LR,#+5 // LR[4] -> Carry == 0 => extended stack frame has been allocated. See ARM DDI0403D, B1.5.7 Stack alignment on exception entry - ITE CS - ADDCS R0,R0,#+_NUM_BYTES_BASIC_STACKFRAME - ADDCC R0,R0,#+_NUM_BYTES_EXTENDED_STACKFRAME - LDR R1,[R2, #+_APP_SP_OFF_XPSR] // Get xPSR from application stack (R2 has been set to app stack on beginning of _HandleReadReg) - LSRS R1,R1,#+5 // xPSR[9] -> Carry == 1 => Stack has been force-aligned before pushing regs. See ARM DDI0403D, B1.5.7 Stack alignment on exception entry - IT CS - ADDCS R0,R0,#+4 - B _HandleReadRegDone -_HandleReadRegPSP: // RegIndex == 18 - CMP R1,#+19 // if (RegIndex < 19) { - BCS _HandleReadRegCFBP - MRS R0,PSP // PSP is not touched by monitor - LSRS R1,LR,#+3 // LR[2] -> Carry == 1 => CPU was running on PSP => Needs correction - BCC _HandleReadRegDone_Veneer // CPU was running on MSP? => No correction of PSP necessary - B _HandleSPCorrection -_HandleReadRegCFBP: - /* - CFBP is a register that can only be read via debug probe and is a merger of the following regs: - CONTROL/FAULTMASK/BASEPRI/PRIMASK (packed into 4 bytes of word. CONTROL = CFBP[31:24], FAULTMASK = CFBP[16:23], BASEPRI = CFBP[15:8], PRIMASK = CFBP[7:0] - To keep J-Link side the same for monitor and halt mode, we also return CFBP in monitor mode - */ - CMP R1,#+20 // if (RegIndex < 20) { (CFBP) - BCS _HandleReadRegFPU - MOVS R0,#+0 - MRS R2,PRIMASK - ORRS R0,R2 // Merge PRIMASK into CFBP[7:0] - MRS R2,BASEPRI - LSLS R2,R2,#+8 // Merge BASEPRI into CFBP[15:8] - ORRS R0,R2 - MRS R2,FAULTMASK - LSLS R2,R2,#+16 // Merge FAULTMASK into CFBP[23:16] - ORRS R0,R2 - MRS R2,CONTROL - LSRS R1,LR,#3 // LR[2] -> Carry. CONTROL.SPSEL is saved to LR[2] on exception entry => ARM DDI0403D, B1.5.6 Exception entry behavior - IT CS // As J-Link sees value of CONTROL at application time, we need reconstruct original value of CONTROL - ORRCS R2,R2,#+2 // CONTROL.SPSEL (CONTROL[1]) == 0 inside monitor - LSRS R1,LR,#+5 // LR[4] == NOT(CONTROL.FPCA) -> Carry - ITE CS // Merge original value of FPCA (CONTROL[2]) into read data - BICCS R2,R2,#+0x04 // Remember LR contains NOT(CONTROL) - ORRCC R2,R2,#+0x04 - LSLS R2,R2,#+24 - ORRS R0,R2 - B.N _HandleReadRegDone -_HandleReadRegFPU: -#if _HAS_FPU_REGS - CMP R1,#+53 // if (RegIndex < 53) { (20 (FPSCR), 21-52 FPS0-FPS31) - BCS _HandleReadRegDone_Veneer - /* - Read Coprocessor Access Control Register (CPACR) to check if CP10 and CP11 are enabled - If not, access to floating point is not possible - CPACR[21:20] == CP10 enable. 0b01 = Privileged access only. 0b11 = Full access. Other = reserved - CPACR[23:22] == CP11 enable. 0b01 = Privileged access only. 0b11 = Full access. Other = reserved - */ - LDR R0,_AddrCPACR - LDR R0,[R0] - LSLS R0,R0,#+8 - LSRS R0,R0,#+28 - CMP R0,#+0xF - BEQ _HandleReadRegFPU_Allowed - CMP R0,#+0x5 - BNE _HandleReadRegDone_Veneer -_HandleReadRegFPU_Allowed: - CMP R1,#+21 // if (RegIndex < 21) (20 == FPSCR) - BCS _HandleReadRegFPS0_FPS31 - LSRS R0,LR,#+5 // CONTROL[2] == FPCA => NOT(FPCA) saved to LR[4]. LR[4] == 0 => Extended stack frame, so FPU regs possibly on stack - BCS _HandleReadFPSCRLazyMode // Remember: NOT(FPCA) is stored to LR. == 0 means: Extended stack frame - LDR R0,=_SYSTEM_FPCCR - LDR R0,[R0] - LSLS R0,R0,#+2 // FPCCR[30] -> Carry == 1 indicates if lazy mode is active, so space on stack is reserved but FPU registers are not saved on stack - BCS _HandleReadFPSCRLazyMode - LDR R0,[R2, #+_APP_SP_OFF_FPSCR] - B _HandleReadRegDone -_HandleReadFPSCRLazyMode: - VMRS R0,FPSCR - B _HandleReadRegDone -_HandleReadRegFPS0_FPS31: // RegIndex == 21-52 - LSRS R0,LR,#+5 // CONTROL[2] == FPCA => NOT(FPCA) saved to LR[4]. LR[4] == 0 => Extended stack frame, so FPU regs possibly on stack - BCS _HandleReadFPS0_FPS31LazyMode // Remember: NOT(FPCA) is stored to LR. == 0 means: Extended stack frame - LDR R0,=_SYSTEM_FPCCR - LDR R0,[R0] - LSLS R0,R0,#+2 // FPCCR[30] -> Carry == 1 indicates if lazy mode is active, so space on stack is reserved but FPU registers are not saved on stack - BCS _HandleReadFPS0_FPS31LazyMode - SUBS R1,#+21 // Convert absolute reg index into rel. one - LSLS R1,R1,#+2 // RegIndex to position on stack - ADDS R1,#+_APP_SP_OFF_S0 - LDR R0,[R2, R1] -_HandleReadRegDone_Veneer: - B _HandleReadRegDone -_HandleReadFPS0_FPS31LazyMode: - SUBS R1,#+20 // convert abs. RegIndex into rel. one - MOVS R0,#+6 - MULS R1,R0,R1 - LDR R0,=_HandleReadRegUnknown - SUB R0,R0,R1 // _HandleReadRegUnknown - 6 * ((RegIndex - 21) + 1) - ORR R0,R0,#1 // Thumb bit needs to be set in DestAddr - BX R0 - // - // Table for reading FPS0-FPS31 - // - VMOV R0,S31 // v = FPSx - B _HandleReadRegDone - VMOV R0,S30 - B _HandleReadRegDone - VMOV R0,S29 - B _HandleReadRegDone - VMOV R0,S28 - B _HandleReadRegDone - VMOV R0,S27 - B _HandleReadRegDone - VMOV R0,S26 - B _HandleReadRegDone - VMOV R0,S25 - B _HandleReadRegDone - VMOV R0,S24 - B _HandleReadRegDone - VMOV R0,S23 - B _HandleReadRegDone - VMOV R0,S22 - B _HandleReadRegDone - VMOV R0,S21 - B _HandleReadRegDone - VMOV R0,S20 - B _HandleReadRegDone - VMOV R0,S19 - B _HandleReadRegDone - VMOV R0,S18 - B _HandleReadRegDone - VMOV R0,S17 - B _HandleReadRegDone - VMOV R0,S16 - B _HandleReadRegDone - VMOV R0,S15 - B _HandleReadRegDone - VMOV R0,S14 - B _HandleReadRegDone - VMOV R0,S13 - B _HandleReadRegDone - VMOV R0,S12 - B _HandleReadRegDone - VMOV R0,S11 - B _HandleReadRegDone - VMOV R0,S10 - B _HandleReadRegDone - VMOV R0,S9 - B _HandleReadRegDone - VMOV R0,S8 - B _HandleReadRegDone - VMOV R0,S7 - B _HandleReadRegDone - VMOV R0,S6 - B _HandleReadRegDone - VMOV R0,S5 - B _HandleReadRegDone - VMOV R0,S4 - B _HandleReadRegDone - VMOV R0,S3 - B _HandleReadRegDone - VMOV R0,S2 - B _HandleReadRegDone - VMOV R0,S1 - B _HandleReadRegDone - VMOV R0,S0 - B _HandleReadRegDone -#else - B _HandleReadRegUnknown -_HandleReadRegDone_Veneer: - B _HandleReadRegDone -#endif -_HandleReadRegUnknown: - MOVS R0,#+0 // v = 0 - B.N _HandleReadRegDone -_HandleReadRegDone: - - // Send register content to J-Link and wait until J-Link has read the data - - STR R0,[R3, #+_SYSTEM_DCRDR_OFF] // DCRDR = v; - LDR R0,[R3, #+_SYSTEM_DEMCR_OFF] // _SYSTEM_DEMCR |= (1uL << 19); => Set MON_REQ bit, so J-Link knows monitor is ready to receive commands - ORR R0,R0,#0x80000 - STR R0,[R3, #+_SYSTEM_DEMCR_OFF] // Indicate data ready - B _WaitProbeReadIndicateMonRdy - - // Data section for register addresses - -_HandleWriteReg: - /* - _MON_CMD_WRITE_REG - CPU: DEMCR[19] -> 1 => Mon ready - J-Link: Data -> DCRDR, DEMCR[19] -> 0 => Data placed by probe - CPU: DCRDR -> Read, Process command, DEMCR[19] -> 1 => Data read & mon ready - - Register indexes - 0-15: R0-R15 (13 == R13 reserved => is banked ... Has to be read as PSP / MSP. Decision has to be done by J-Link DLL side!) - 16: XPSR - 17: MSP - 18: PSP - 19: CFBP CONTROL/FAULTMASK/BASEPRI/PRIMASK (packed into 4 bytes of word. CONTROL = CFBP[31:24], FAULTMASK = CFBP[16:23], BASEPRI = CFBP[15:8], PRIMASK = CFBP[7:0] - 20: FPSCR - 21-52: FPS0-FPS31 - - - Register usage when entering this "subroutine": - R0 Cmd - R1 ParamInfo - R2 --- - R3 = &_SYSTEM_DCRDR (allows also access to DEMCR with offset) - R12 --- - - Table B1-9 EXC_RETURN definition of exception return behavior, with FP extension - LR Return to Return SP Frame type - --------------------------------------------------------- - 0xFFFFFFE1 Handler mode. MSP Extended - 0xFFFFFFE9 Thread mode MSP Extended - 0xFFFFFFED Thread mode PSP Extended - 0xFFFFFFF1 Handler mode. MSP Basic - 0xFFFFFFF9 Thread mode MSP Basic - 0xFFFFFFFD Thread mode PSP Basic - - So LR[2] == 1 => Return stack == PSP else MSP - - R0-R3, R12, PC, xPSR can be written via application stackpointer - Other regs can be written directly - - - Read register data from J-Link into R0 - */ - LDR R0,[R3, #+_SYSTEM_DEMCR_OFF] // _SYSTEM_DEMCR |= (1uL << 19); => Monitor is ready to receive register data - ORR R0,R0,#0x80000 - STR R0,[R3, #+_SYSTEM_DEMCR_OFF] -_HandleWRegWaitUntilDataRecv: - LDR R0,[R3, #+_SYSTEM_DEMCR_OFF] - LSLS R0,R0,#+12 - BMI.N _HandleWRegWaitUntilDataRecv // DEMCR[19] == 0 => J-Link has placed new data for us - LDR R0,[R3, #+_SYSTEM_DCRDR_OFF] // Get register data - // - // Determine application SP - // - LSRS R2,LR,#+3 // Shift LR[2] into carry => Carry clear means that CPU was running on MSP - ITE CS - MRSCS R2,PSP - MRSCC R2,MSP - CMP R1,#+4 // if (RegIndex < 4) { (R0-R3) - BCS _HandleWriteRegR4 - STR R0,[R2, R1, LSL #+2] // v = [SP + Rx * 4] (R0-R3) - B.N _HandleWriteRegDone -_HandleWriteRegR4: - CMP R1,#+5 // if (RegIndex < 5) { (R4) - BCS _HandleWriteRegR5 - MOV R4,R0 - B.N _HandleWriteRegDone -_HandleWriteRegR5: - CMP R1,#+6 // if (RegIndex < 6) { (R5) - BCS _HandleWriteRegR6 - MOV R5,R0 - B.N _HandleWriteRegDone -_HandleWriteRegR6: - CMP R1,#+7 // if (RegIndex < 7) { (R6) - BCS _HandleWriteRegR7 - MOV R6,R0 - B.N _HandleWriteRegDone -_HandleWriteRegR7: - CMP R1,#+8 // if (RegIndex < 8) { (R7) - BCS _HandleWriteRegR8 - MOV R7,R0 - B.N _HandleWriteRegDone -_HandleWriteRegR8: - CMP R1,#+9 // if (RegIndex < 9) { (R8) - BCS _HandleWriteRegR9 - MOV R8,R0 - B.N _HandleWriteRegDone -_HandleWriteRegR9: - CMP R1,#+10 // if (RegIndex < 10) { (R9) - BCS _HandleWriteRegR10 - MOV R9,R0 - B.N _HandleWriteRegDone -_HandleWriteRegR10: - CMP R1,#+11 // if (RegIndex < 11) { (R10) - BCS _HandleWriteRegR11 - MOV R10,R0 - B.N _HandleWriteRegDone -_HandleWriteRegR11: - CMP R1,#+12 // if (RegIndex < 12) { (R11) - BCS _HandleWriteRegR12 - MOV R11,R0 - B.N _HandleWriteRegDone -_HandleWriteRegR12: - CMP R1,#+14 // if (RegIndex < 14) { (R12) - BCS _HandleWriteRegR14 - STR R0,[R2, #+_APP_SP_OFF_R12] - B.N _HandleWriteRegDone -_HandleWriteRegR14: - CMP R1,#+15 // if (RegIndex < 15) { (R14 / LR) - BCS _HandleWriteRegR15 - STR R0,[R2, #+_APP_SP_OFF_R14_LR] - B.N _HandleWriteRegDone -_HandleWriteRegR15: - CMP R1,#+16 // if (RegIndex < 16) { (R15 / PC) - BCS _HandleWriteRegXPSR - STR R0,[R2, #+_APP_SP_OFF_PC] - B.N _HandleWriteRegDone -_HandleWriteRegXPSR: - CMP R1,#+17 // if (RegIndex < 17) { (xPSR) - BCS _HandleWriteRegMSP - STR R0,[R2, #+_APP_SP_OFF_XPSR] - B.N _HandleWriteRegDone -_HandleWriteRegMSP: - // - // For now, SP cannot be modified because it is needed to jump back from monitor mode - // - CMP R1,#+18 // if (RegIndex < 18) { (MSP) - BCS _HandleWriteRegPSP - B.N _HandleWriteRegDone -_HandleWriteRegPSP: // RegIndex == 18 - CMP R1,#+19 // if (RegIndex < 19) { - BCS _HandleWriteRegCFBP - B.N _HandleWriteRegDone -_HandleWriteRegCFBP: - /* - CFBP is a register that can only be read via debug probe and is a merger of the following regs: - CONTROL/FAULTMASK/BASEPRI/PRIMASK (packed into 4 bytes of word. CONTROL = CFBP[31:24], FAULTMASK = CFBP[16:23], BASEPRI = CFBP[15:8], PRIMASK = CFBP[7:0] - To keep J-Link side the same for monitor and halt mode, we also return CFBP in monitor mode - */ - CMP R1,#+20 // if (RegIndex < 20) { (CFBP) - BCS _HandleWriteRegFPU - LSLS R1,R0,#+24 - LSRS R1,R1,#+24 // Extract CFBP[7:0] => PRIMASK - MSR PRIMASK,R1 - LSLS R1,R0,#+16 - LSRS R1,R1,#+24 // Extract CFBP[15:8] => BASEPRI - MSR BASEPRI,R1 - LSLS R1,R0,#+8 // Extract CFBP[23:16] => FAULTMASK - LSRS R1,R1,#+24 - MSR FAULTMASK,R1 - LSRS R1,R0,#+24 // Extract CFBP[31:24] => CONTROL - LSRS R0,R1,#2 // Current CONTROL[1] -> Carry - ITE CS // Update saved CONTROL.SPSEL (CONTROL[1]). CONTROL.SPSEL is saved to LR[2] on exception entry => ARM DDI0403D, B1.5.6 Exception entry behavior - ORRCS LR,LR,#+4 - BICCC LR,LR,#+4 - BIC R1,R1,#+2 // CONTROL.SPSEL (CONTROL[1]) == 0 inside monitor. Otherwise behavior is UNPREDICTABLE - LSRS R0,R1,#+3 // New CONTROL.FPCA (CONTROL[2]) -> Carry - ITE CS // CONTROL[2] == FPCA => NOT(FPCA) saved to LR[4]. LR[4] == 0 => Extended stack frame, so FPU regs possibly on stack - BICCS LR,LR,#+0x10 // Remember: NOT(FPCA) is stored to LR. == 0 means: Extended stack frame - ORRCC LR,LR,#+0x10 - MRS R0,CONTROL - LSRS R0,R0,#+3 // CONTROL[2] -> Carry - ITE CS // Preserve original value of current CONTROL[2] - ORRCS R1,R1,#+0x04 - BICCC R1,R1,#+0x04 - MSR CONTROL,R1 - ISB // Necessary after writing to CONTROL, see ARM DDI0403D, B1.4.4 The special-purpose CONTROL register - B.N _HandleWriteRegDone -_HandleWriteRegFPU: -#if _HAS_FPU_REGS - CMP R1,#+53 // if (RegIndex < 53) { (20 (FPSCR), 21-52 FPS0-FPS31) - BCS _HandleWriteRegDone_Veneer - /* - Read Coprocessor Access Control Register (CPACR) to check if CP10 and CP11 are enabled - If not, access to floating point is not possible - CPACR[21:20] == CP10 enable. 0b01 = Privileged access only. 0b11 = Full access. Other = reserved - CPACR[23:22] == CP11 enable. 0b01 = Privileged access only. 0b11 = Full access. Other = reserved - */ - MOV R12,R0 // Save register data - LDR R0,_AddrCPACR - LDR R0,[R0] - LSLS R0,R0,#+8 - LSRS R0,R0,#+28 - CMP R0,#+0xF - BEQ _HandleWriteRegFPU_Allowed - CMP R0,#+0x5 - BNE _HandleWriteRegDone_Veneer -_HandleWriteRegFPU_Allowed: - CMP R1,#+21 // if (RegIndex < 21) (20 == FPSCR) - BCS _HandleWriteRegFPS0_FPS31 - LSRS R0,LR,#+5 // CONTROL[2] == FPCA => NOT(FPCA) saved to LR[4]. LR[4] == 0 => Extended stack frame, so FPU regs possibly on stack - BCS _HandleWriteFPSCRLazyMode // Remember: NOT(FPCA) is stored to LR. == 0 means: Extended stack frame - LDR R0,=_SYSTEM_FPCCR - LDR R0,[R0] - LSLS R0,R0,#+2 // FPCCR[30] -> Carry == 1 indicates if lazy mode is active, so space on stack is reserved but FPU registers are not saved on stack - BCS _HandleWriteFPSCRLazyMode - STR R12,[R2, #+_APP_SP_OFF_FPSCR] - B _HandleWriteRegDone -_HandleWriteFPSCRLazyMode: - VMSR FPSCR,R12 - B _HandleWriteRegDone -_HandleWriteRegFPS0_FPS31: // RegIndex == 21-52 - LDR R0,=_SYSTEM_FPCCR - LDR R0,[R0] - LSLS R0,R0,#+2 // FPCCR[30] -> Carry == 1 indicates if lazy mode is active, so space on stack is reserved but FPU registers are not saved on stack - BCS _HandleWriteFPS0_FPS31LazyMode - LSRS R0,LR,#+5 // CONTROL[2] == FPCA => NOT(FPCA) saved to LR[4]. LR[4] == 0 => Extended stack frame, so FPU regs possibly on stack - BCS _HandleWriteFPS0_FPS31LazyMode // Remember: NOT(FPCA) is stored to LR. == 0 means: Extended stack frame - SUBS R1,#+21 // Convert absolute reg index into rel. one - LSLS R1,R1,#+2 // RegIndex to position on stack - ADDS R1,#+_APP_SP_OFF_S0 - STR R12,[R2, R1] -_HandleWriteRegDone_Veneer: - B _HandleWriteRegDone -_HandleWriteFPS0_FPS31LazyMode: - SUBS R1,#+20 // Convert abs. RegIndex into rel. one - MOVS R0,#+6 - MULS R1,R0,R1 - LDR R0,=_HandleReadRegUnknown - SUB R0,R0,R1 // _HandleReadRegUnknown - 6 * ((RegIndex - 21) + 1) - ORR R0,R0,#1 // Thumb bit needs to be set in DestAddr - BX R0 - // - // Table for reading FPS0-FPS31 - // - VMOV S31,R12 // v = FPSx - B _HandleWriteRegDone - VMOV S30,R12 - B _HandleWriteRegDone - VMOV S29,R12 - B _HandleWriteRegDone - VMOV S28,R12 - B _HandleWriteRegDone - VMOV S27,R12 - B _HandleWriteRegDone - VMOV S26,R12 - B _HandleWriteRegDone - VMOV S25,R12 - B _HandleWriteRegDone - VMOV S24,R12 - B _HandleWriteRegDone - VMOV S23,R12 - B _HandleWriteRegDone - VMOV S22,R12 - B _HandleWriteRegDone - VMOV S21,R12 - B _HandleWriteRegDone - VMOV S20,R12 - B _HandleWriteRegDone - VMOV S19,R12 - B _HandleWriteRegDone - VMOV S18,R12 - B _HandleWriteRegDone - VMOV S17,R12 - B _HandleWriteRegDone - VMOV S16,R12 - B _HandleWriteRegDone - VMOV S15,R12 - B _HandleWriteRegDone - VMOV S14,R12 - B _HandleWriteRegDone - VMOV S13,R12 - B _HandleWriteRegDone - VMOV S12,R12 - B _HandleWriteRegDone - VMOV S11,R12 - B _HandleWriteRegDone - VMOV S10,R12 - B _HandleWriteRegDone - VMOV S9,R12 - B _HandleWriteRegDone - VMOV S8,R12 - B _HandleWriteRegDone - VMOV S7,R12 - B _HandleWriteRegDone - VMOV S6,R12 - B _HandleWriteRegDone - VMOV S5,R12 - B _HandleWriteRegDone - VMOV S4,R12 - B _HandleWriteRegDone - VMOV S3,R12 - B _HandleWriteRegDone - VMOV S2,R12 - B _HandleWriteRegDone - VMOV S1,R12 - B _HandleWriteRegDone - VMOV S0,R12 - B _HandleWriteRegDone -#else - B _HandleWriteRegUnknown -#endif -_HandleWriteRegUnknown: - B.N _HandleWriteRegDone -_HandleWriteRegDone: - B _IndicateMonReady // Indicate that monitor has read data, processed command and is ready for a new one - .end -/****** End Of File *************************************************/ diff --git a/src/platform/nrf52/NRF52Bluetooth.cpp b/src/platform/nrf52/NRF52Bluetooth.cpp index 1405ea4f3..63d9331ad 100644 --- a/src/platform/nrf52/NRF52Bluetooth.cpp +++ b/src/platform/nrf52/NRF52Bluetooth.cpp @@ -55,7 +55,6 @@ static BluetoothPhoneAPI *bluetoothPhoneAPI; void onConnect(uint16_t conn_handle) { - // Get the reference to current connection BLEConnection *connection = Bluefruit.Connection(conn_handle); connectionHandle = conn_handle; @@ -70,8 +69,10 @@ void onConnect(uint16_t conn_handle) */ void onDisconnect(uint16_t conn_handle, uint8_t reason) { - // FIXME - we currently assume only one active connection LOG_INFO("BLE Disconnected, reason = 0x%x\n", reason); + if (bluetoothPhoneAPI) { + bluetoothPhoneAPI->close(); + } } void onCccd(uint16_t conn_hdl, BLECharacteristic *chr, uint16_t cccd_value) { @@ -140,10 +141,19 @@ void onFromRadioAuthorize(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_e } authorizeRead(conn_hdl); } +// Last ToRadio value received from the phone +static uint8_t lastToRadio[MAX_TO_FROM_RADIO_SIZE]; + void onToRadioWrite(uint16_t conn_hdl, BLECharacteristic *chr, uint8_t *data, uint16_t len) { LOG_INFO("toRadioWriteCb data %p, len %u\n", data, len); - bluetoothPhoneAPI->handleToRadio(data, len); + if (memcmp(lastToRadio, data, len) != 0) { + LOG_DEBUG("New ToRadio packet\n"); + memcpy(lastToRadio, data, len); + bluetoothPhoneAPI->handleToRadio(data, len); + } else { + LOG_DEBUG("Dropping duplicate ToRadio packet we just saw\n"); + } } void setupMeshService(void) @@ -310,6 +320,7 @@ bool NRF52Bluetooth::onPairingPasskey(uint16_t conn_handle, uint8_t const passke { LOG_INFO("BLE pairing process started with passkey %.3s %.3s\n", passkey, passkey + 3); powerFSM.trigger(EVENT_BLUETOOTH_PAIR); +#if !defined(MESHTASTIC_EXCLUDE_SCREEN) screen->startAlert([](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void { char btPIN[16] = "888888"; snprintf(btPIN, sizeof(btPIN), "%06u", configuredPasskey); @@ -335,6 +346,7 @@ bool NRF52Bluetooth::onPairingPasskey(uint16_t conn_handle, uint8_t const passke y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_LARGE - 6 : y_offset + FONT_HEIGHT_LARGE + 5; display->drawString(x_offset + x, y_offset + y, deviceName); }); +#endif if (match_request) { uint32_t start_time = millis(); while (millis() < start_time + 30000) { diff --git a/src/platform/nrf52/architecture.h b/src/platform/nrf52/architecture.h index 5a04dd6a7..b2b7b5a20 100644 --- a/src/platform/nrf52/architecture.h +++ b/src/platform/nrf52/architecture.h @@ -71,6 +71,8 @@ #define HW_VENDOR meshtastic_HardwareModel_MS24SF1 #elif defined(PRIVATE_HW) || defined(FEATHER_DIY) #define HW_VENDOR meshtastic_HardwareModel_PRIVATE_HW +#elif defined(HELTEC_T114) +#define HW_VENDOR meshtastic_HardwareModel_HELTEC_MESH_NODE_T114 #else #define HW_VENDOR meshtastic_HardwareModel_NRF52_UNKNOWN #endif diff --git a/src/platform/nrf52/main-nrf52.cpp b/src/platform/nrf52/main-nrf52.cpp index b28640e65..f9329d875 100644 --- a/src/platform/nrf52/main-nrf52.cpp +++ b/src/platform/nrf52/main-nrf52.cpp @@ -12,6 +12,7 @@ #include "PowerMon.h" #include "error.h" #include "main.h" +#include "meshUtils.h" #ifdef BQ25703A_ADDR #include "BQ25713.h" @@ -157,6 +158,7 @@ void nrf52Loop() #ifdef USE_SEMIHOSTING #include +#include /** * Note: this variable is in BSS and therfore false by default. But the gdbinit @@ -261,14 +263,18 @@ void cpuDeepSleep(uint32_t msecToWake) // Sleepy trackers or sensors can low power "sleep" // Don't enter this if we're sleeping portMAX_DELAY, since that's a shutdown event if (msecToWake != portMAX_DELAY && - (config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER || - config.device.role == meshtastic_Config_DeviceConfig_Role_TAK_TRACKER || - config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR) && - config.power.is_power_saving == true) { + (IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_TRACKER, + meshtastic_Config_DeviceConfig_Role_TAK_TRACKER, meshtastic_Config_DeviceConfig_Role_SENSOR) && + config.power.is_power_saving == true)) { sd_power_mode_set(NRF_POWER_MODE_LOWPWR); delay(msecToWake); NVIC_SystemReset(); } else { + // Resume on user button press + // https://github.com/lyusupov/SoftRF/blob/81c519ca75693b696752235d559e881f2e0511ee/software/firmware/source/SoftRF/src/platform/nRF52.cpp#L1738 + constexpr uint32_t DFU_MAGIC_SKIP = 0x6d; + sd_power_gpregret_set(0, DFU_MAGIC_SKIP); // Equivalent NRF_POWER->GPREGRET = DFU_MAGIC_SKIP + // FIXME, use system off mode with ram retention for key state? // FIXME, use non-init RAM per // https://devzone.nordicsemi.com/f/nordic-q-a/48919/ram-retention-settings-with-softdevice-enabled diff --git a/src/platform/nrf52/pgmspace.h b/src/platform/nrf52/pgmspace.h deleted file mode 100644 index 5ad8035be..000000000 --- a/src/platform/nrf52/pgmspace.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -// dummy file to keep old arduino code happy -#define PROGMEM -#define pgm_read_byte(addr) (*((unsigned const char *)addr)) \ No newline at end of file diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index dc143c661..e56016a5b 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -80,6 +80,7 @@ void portduinoSetup() irq, busy, reset, + sx126x_ant_sw, txen, rxen, displayDC, @@ -180,6 +181,7 @@ void portduinoSetup() settingsMap[reset] = yamlConfig["Lora"]["Reset"].as(RADIOLIB_NC); settingsMap[txen] = yamlConfig["Lora"]["TXen"].as(RADIOLIB_NC); settingsMap[rxen] = yamlConfig["Lora"]["RXen"].as(RADIOLIB_NC); + settingsMap[sx126x_ant_sw] = yamlConfig["Lora"]["SX126X_ANT_SW"].as(RADIOLIB_NC); settingsMap[gpiochip] = yamlConfig["Lora"]["gpiochip"].as(0); settingsMap[ch341Quirk] = yamlConfig["Lora"]["ch341_quirk"].as(false); settingsMap[spiSpeed] = yamlConfig["Lora"]["spiSpeed"].as(2000000); @@ -221,6 +223,8 @@ void portduinoSetup() settingsMap[displayPanel] = st7796; else if (yamlConfig["Display"]["Panel"].as("") == "ILI9341") settingsMap[displayPanel] = ili9341; + else if (yamlConfig["Display"]["Panel"].as("") == "ILI9342") + settingsMap[displayPanel] = ili9342; else if (yamlConfig["Display"]["Panel"].as("") == "ILI9488") settingsMap[displayPanel] = ili9488; else if (yamlConfig["Display"]["Panel"].as("") == "HX8357D") @@ -305,6 +309,8 @@ void portduinoSetup() gpioInit(max_GPIO + 1); // Done here so we can inform Portduino how many GPIOs we need. // Need to bind all the configured GPIO pins so they're not simulated + // TODO: Can we do this in the for loop above? + // TODO: If one of these fails, we should log and terminate if (settingsMap.count(cs) > 0 && settingsMap[cs] != RADIOLIB_NC) { if (initGPIOPin(settingsMap[cs], gpioChipName) != ERRNO_OK) { settingsMap[cs] = RADIOLIB_NC; @@ -325,6 +331,11 @@ void portduinoSetup() settingsMap[reset] = RADIOLIB_NC; } } + if (settingsMap.count(sx126x_ant_sw) > 0 && settingsMap[sx126x_ant_sw] != RADIOLIB_NC) { + if (initGPIOPin(settingsMap[sx126x_ant_sw], gpioChipName) != ERRNO_OK) { + settingsMap[sx126x_ant_sw] = RADIOLIB_NC; + } + } if (settingsMap.count(user) > 0 && settingsMap[user] != RADIOLIB_NC) { if (initGPIOPin(settingsMap[user], gpioChipName) != ERRNO_OK) { settingsMap[user] = RADIOLIB_NC; @@ -391,4 +402,4 @@ int initGPIOPin(int pinNum, const std::string gpioChipName) #else return ERRNO_OK; #endif -} +} \ No newline at end of file diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h index 3fee1db40..8ee96717e 100644 --- a/src/platform/portduino/PortduinoGlue.h +++ b/src/platform/portduino/PortduinoGlue.h @@ -8,6 +8,7 @@ enum configNames { irq, busy, reset, + sx126x_ant_sw, txen, rxen, dio2_as_rf_switch, @@ -56,11 +57,11 @@ enum configNames { maxnodes, ascii_logs }; -enum { no_screen, x11, st7789, st7735, st7735s, st7796, ili9341, ili9488, hx8357d }; +enum { no_screen, x11, st7789, st7735, st7735s, st7796, ili9341, ili9342, ili9488, hx8357d }; enum { no_touchscreen, xpt2046, stmpe610, gt911, ft5x06 }; enum { level_error, level_warn, level_info, level_debug, level_trace }; extern std::map settingsMap; extern std::map settingsStrings; extern std::ofstream traceFile; -int initGPIOPin(int pinNum, std::string gpioChipname); +int initGPIOPin(int pinNum, std::string gpioChipname); \ No newline at end of file diff --git a/src/platform/rp2040/architecture.h b/src/platform/rp2xx0/architecture.h similarity index 90% rename from src/platform/rp2040/architecture.h rename to src/platform/rp2xx0/architecture.h index 3f75735d3..8c7dfc0cd 100644 --- a/src/platform/rp2040/architecture.h +++ b/src/platform/rp2xx0/architecture.h @@ -23,6 +23,8 @@ #if defined(RPI_PICO) #define HW_VENDOR meshtastic_HardwareModel_RPI_PICO +#elif defined(RPI_PICO2) +#define HW_VENDOR meshtastic_HardwareModel_RPI_PICO2 #elif defined(RAK11310) #define HW_VENDOR meshtastic_HardwareModel_RAK11310 #elif defined(SENSELORA_RP2040) diff --git a/src/platform/rp2xx0/hardware_rosc/include/hardware/rosc.h b/src/platform/rp2xx0/hardware_rosc/include/hardware/rosc.h new file mode 100644 index 000000000..e1e014f33 --- /dev/null +++ b/src/platform/rp2xx0/hardware_rosc/include/hardware/rosc.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _HARDWARE_ROSC_H_ +#define _HARDWARE_ROSC_H_ + +#include "hardware/structs/rosc.h" +#include "pico.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file rosc.h + * \defgroup hardware_rosc hardware_rosc + * + * Ring Oscillator (ROSC) API + * + * A Ring Oscillator is an on-chip oscillator that requires no external crystal. Instead, the output is generated from a series of + * inverters that are chained together to create a feedback loop. RP2040 boots from the ring oscillator initially, meaning the + * first stages of the bootrom, including booting from SPI flash, will be clocked by the ring oscillator. If your design has a + * crystal oscillator, you’ll likely want to switch to this as your reference clock as soon as possible, because the frequency is + * more accurate than the ring oscillator. + */ + +/*! \brief Set frequency of the Ring Oscillator + * \ingroup hardware_rosc + * + * \param code The drive strengths. See the RP2040 datasheet for information on this value. + */ +void rosc_set_freq(uint32_t code); + +/*! \brief Set range of the Ring Oscillator + * \ingroup hardware_rosc + * + * Frequency range. Frequencies will vary with Process, Voltage & Temperature (PVT). + * Clock output will not glitch when changing the range up one step at a time. + * + * \param range 0x01 Low, 0x02 Medium, 0x03 High, 0x04 Too High. + */ +void rosc_set_range(uint range); + +/*! \brief Disable the Ring Oscillator + * \ingroup hardware_rosc + * + */ +void rosc_disable(void); + +/*! \brief Put Ring Oscillator in to dormant mode. + * \ingroup hardware_rosc + * + * The ROSC supports a dormant mode,which stops oscillation until woken up up by an asynchronous interrupt. + * This can either come from the RTC, being clocked by an external clock, or a GPIO pin going high or low. + * If no IRQ is configured before going into dormant mode the ROSC will never restart. + * + * PLLs should be stopped before selecting dormant mode. + */ +void rosc_set_dormant(void); + +// FIXME: Add doxygen + +uint32_t next_rosc_code(uint32_t code); + +uint rosc_find_freq(uint32_t low_mhz, uint32_t high_mhz); + +void rosc_set_div(uint32_t div); + +inline static void rosc_clear_bad_write(void) +{ + hw_clear_bits(&rosc_hw->status, ROSC_STATUS_BADWRITE_BITS); +} + +inline static bool rosc_write_okay(void) +{ + return !(rosc_hw->status & ROSC_STATUS_BADWRITE_BITS); +} + +inline static void rosc_write(io_rw_32 *addr, uint32_t value) +{ + rosc_clear_bad_write(); + assert(rosc_write_okay()); + *addr = value; + assert(rosc_write_okay()); +}; + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/platform/rp2xx0/hardware_rosc/rosc.c b/src/platform/rp2xx0/hardware_rosc/rosc.c new file mode 100644 index 000000000..f79929f8d --- /dev/null +++ b/src/platform/rp2xx0/hardware_rosc/rosc.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "pico.h" + +// For MHZ definitions etc +#include "hardware/clocks.h" +#include "hardware/rosc.h" + +// Given a ROSC delay stage code, return the next-numerically-higher code. +// Top result bit is set when called on maximum ROSC code. +uint32_t next_rosc_code(uint32_t code) +{ + return ((code | 0x08888888u) + 1u) & 0xf7777777u; +} + +uint rosc_find_freq(uint32_t low_mhz, uint32_t high_mhz) +{ + // TODO: This could be a lot better + rosc_set_div(1); + for (uint32_t code = 0; code <= 0x77777777u; code = next_rosc_code(code)) { + rosc_set_freq(code); + uint rosc_mhz = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_ROSC_CLKSRC) / 1000; + if ((rosc_mhz >= low_mhz) && (rosc_mhz <= high_mhz)) { + return rosc_mhz; + } + } + return 0; +} + +void rosc_set_div(uint32_t div) +{ + assert(div <= 31 && div >= 1); + rosc_write(&rosc_hw->div, ROSC_DIV_VALUE_PASS + div); +} + +void rosc_set_freq(uint32_t code) +{ + rosc_write(&rosc_hw->freqa, (ROSC_FREQA_PASSWD_VALUE_PASS << ROSC_FREQA_PASSWD_LSB) | (code & 0xffffu)); + rosc_write(&rosc_hw->freqb, (ROSC_FREQA_PASSWD_VALUE_PASS << ROSC_FREQA_PASSWD_LSB) | (code >> 16u)); +} + +void rosc_set_range(uint range) +{ + // Range should use enumvals from the headers and thus have the password correct + rosc_write(&rosc_hw->ctrl, (ROSC_CTRL_ENABLE_VALUE_ENABLE << ROSC_CTRL_ENABLE_LSB) | range); +} + +void rosc_disable(void) +{ + uint32_t tmp = rosc_hw->ctrl; + tmp &= (~ROSC_CTRL_ENABLE_BITS); + tmp |= (ROSC_CTRL_ENABLE_VALUE_DISABLE << ROSC_CTRL_ENABLE_LSB); + rosc_write(&rosc_hw->ctrl, tmp); + // Wait for stable to go away + while (rosc_hw->status & ROSC_STATUS_STABLE_BITS) + ; +} + +void rosc_set_dormant(void) +{ + // WARNING: This stops the rosc until woken up by an irq + rosc_write(&rosc_hw->dormant, ROSC_DORMANT_VALUE_DORMANT); + // Wait for it to become stable once woken up + while (!(rosc_hw->status & ROSC_STATUS_STABLE_BITS)) + ; +} \ No newline at end of file diff --git a/src/platform/rp2040/main-rp2040.cpp b/src/platform/rp2xx0/main-rp2xx0.cpp similarity index 71% rename from src/platform/rp2040/main-rp2040.cpp rename to src/platform/rp2xx0/main-rp2xx0.cpp index 6306f34c1..67bf1eb08 100644 --- a/src/platform/rp2040/main-rp2040.cpp +++ b/src/platform/rp2xx0/main-rp2xx0.cpp @@ -2,22 +2,71 @@ #include "hardware/xosc.h" #include #include +#include #include #include -#include void setBluetoothEnable(bool enable) { // not needed } +static bool awake; + +static void sleep_callback(void) +{ + awake = true; +} + +void epoch_to_datetime(time_t epoch, datetime_t *dt) +{ + struct tm *tm_info; + + tm_info = gmtime(&epoch); + dt->year = tm_info->tm_year; + dt->month = tm_info->tm_mon + 1; + dt->day = tm_info->tm_mday; + dt->dotw = tm_info->tm_wday; + dt->hour = tm_info->tm_hour; + dt->min = tm_info->tm_min; + dt->sec = tm_info->tm_sec; +} + +void debug_date(datetime_t t) +{ + LOG_DEBUG("%d %d %d %d %d %d %d\n", t.year, t.month, t.day, t.hour, t.min, t.sec, t.dotw); + uart_default_tx_wait_blocking(); +} + void cpuDeepSleep(uint32_t msecs) { - /* Disable both PLL to avoid power dissipation */ - pll_deinit(pll_sys); - pll_deinit(pll_usb); + + time_t seconds = (time_t)(msecs / 1000); + datetime_t t_init, t_alarm; + + awake = false; + // Start the RTC + rtc_init(); + epoch_to_datetime(0, &t_init); + rtc_set_datetime(&t_init); + epoch_to_datetime(seconds, &t_alarm); + // debug_date(t_init); + // debug_date(t_alarm); + uart_default_tx_wait_blocking(); + sleep_run_from_dormant_source(DORMANT_SOURCE_ROSC); + sleep_goto_sleep_until(&t_alarm, &sleep_callback); + + // Make sure we don't wake + while (!awake) { + delay(1); + } + + /* For now, I don't know how to revert this state + We just reboot in order to get back operational */ + rp2040.reboot(); + /* Set RP2040 in dormant mode. Will not wake up. */ - xosc_dormant(); + // xosc_dormant(); } void updateBatteryLevel(uint8_t level) diff --git a/src/platform/rp2xx0/pico_sleep/include/pico/sleep.h b/src/platform/rp2xx0/pico_sleep/include/pico/sleep.h new file mode 100644 index 000000000..17dff2468 --- /dev/null +++ b/src/platform/rp2xx0/pico_sleep/include/pico/sleep.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _PICO_SLEEP_H_ +#define _PICO_SLEEP_H_ + +#include "hardware/rtc.h" +#include "pico.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file sleep.h + * \defgroup hardware_sleep hardware_sleep + * + * Lower Power Sleep API + * + * The difference between sleep and dormant is that ALL clocks are stopped in dormant mode, + * until the source (either xosc or rosc) is started again by an external event. + * In sleep mode some clocks can be left running controlled by the SLEEP_EN registers in the clocks + * block. For example you could keep clk_rtc running. Some destinations (proc0 and proc1 wakeup logic) + * can't be stopped in sleep mode otherwise there wouldn't be enough logic to wake up again. + * + * \subsection sleep_example Example + * \addtogroup hardware_sleep + * \include hello_sleep.c + + */ +typedef enum { DORMANT_SOURCE_NONE, DORMANT_SOURCE_XOSC, DORMANT_SOURCE_ROSC } dormant_source_t; + +/*! \brief Set all clock sources to the the dormant clock source to prepare for sleep. + * \ingroup hardware_sleep + * + * \param dormant_source The dormant clock source to use + */ +void sleep_run_from_dormant_source(dormant_source_t dormant_source); + +/*! \brief Set the dormant clock source to be the crystal oscillator + * \ingroup hardware_sleep + */ +static inline void sleep_run_from_xosc(void) +{ + sleep_run_from_dormant_source(DORMANT_SOURCE_XOSC); +} + +/*! \brief Set the dormant clock source to be the ring oscillator + * \ingroup hardware_sleep + */ +static inline void sleep_run_from_rosc(void) +{ + sleep_run_from_dormant_source(DORMANT_SOURCE_ROSC); +} + +/*! \brief Send system to sleep until the specified time + * \ingroup hardware_sleep + * + * One of the sleep_run_* functions must be called prior to this call + * + * \param t The time to wake up + * \param callback Function to call on wakeup. + */ +void sleep_goto_sleep_until(datetime_t *t, rtc_callback_t callback); + +/*! \brief Send system to sleep until the specified GPIO changes + * \ingroup hardware_sleep + * + * One of the sleep_run_* functions must be called prior to this call + * + * \param gpio_pin The pin to provide the wake up + * \param edge true for leading edge, false for trailing edge + * \param high true for active high, false for active low + */ +void sleep_goto_dormant_until_pin(uint gpio_pin, bool edge, bool high); + +/*! \brief Send system to sleep until a leading high edge is detected on GPIO + * \ingroup hardware_sleep + * + * One of the sleep_run_* functions must be called prior to this call + * + * \param gpio_pin The pin to provide the wake up + */ +static inline void sleep_goto_dormant_until_edge_high(uint gpio_pin) +{ + sleep_goto_dormant_until_pin(gpio_pin, true, true); +} + +/*! \brief Send system to sleep until a high level is detected on GPIO + * \ingroup hardware_sleep + * + * One of the sleep_run_* functions must be called prior to this call + * + * \param gpio_pin The pin to provide the wake up + */ +static inline void sleep_goto_dormant_until_level_high(uint gpio_pin) +{ + sleep_goto_dormant_until_pin(gpio_pin, false, true); +} + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/platform/rp2xx0/pico_sleep/sleep.c b/src/platform/rp2xx0/pico_sleep/sleep.c new file mode 100644 index 000000000..65096be85 --- /dev/null +++ b/src/platform/rp2xx0/pico_sleep/sleep.c @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "pico.h" + +#include "pico/sleep.h" +#include "pico/stdlib.h" + +#include "hardware/clocks.h" +#include "hardware/pll.h" +#include "hardware/regs/io_bank0.h" +#include "hardware/rosc.h" +#include "hardware/rtc.h" +#include "hardware/xosc.h" +// For __wfi +#include "hardware/sync.h" +// For scb_hw so we can enable deep sleep +#include "hardware/structs/scb.h" +// when using old SDK this macro is not defined +#ifndef XOSC_HZ +#define XOSC_HZ 12000000u +#endif +// The difference between sleep and dormant is that ALL clocks are stopped in dormant mode, +// until the source (either xosc or rosc) is started again by an external event. +// In sleep mode some clocks can be left running controlled by the SLEEP_EN registers in the clocks +// block. For example you could keep clk_rtc running. Some destinations (proc0 and proc1 wakeup logic) +// can't be stopped in sleep mode otherwise there wouldn't be enough logic to wake up again. + +// TODO: Optionally, memories can also be powered down. + +static dormant_source_t _dormant_source; + +bool dormant_source_valid(dormant_source_t dormant_source) +{ + return (dormant_source == DORMANT_SOURCE_XOSC) || (dormant_source == DORMANT_SOURCE_ROSC); +} + +// In order to go into dormant mode we need to be running from a stoppable clock source: +// either the xosc or rosc with no PLLs running. This means we disable the USB and ADC clocks +// and all PLLs +void sleep_run_from_dormant_source(dormant_source_t dormant_source) +{ + assert(dormant_source_valid(dormant_source)); + _dormant_source = dormant_source; + + // FIXME: Just defining average rosc freq here. + uint src_hz = (dormant_source == DORMANT_SOURCE_XOSC) ? XOSC_HZ : 6.5 * MHZ; + uint clk_ref_src = (dormant_source == DORMANT_SOURCE_XOSC) ? CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC + : CLOCKS_CLK_REF_CTRL_SRC_VALUE_ROSC_CLKSRC_PH; + + // CLK_REF = XOSC or ROSC + clock_configure(clk_ref, clk_ref_src, + 0, // No aux mux + src_hz, src_hz); + + // CLK SYS = CLK_REF + clock_configure(clk_sys, CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLK_REF, + 0, // Using glitchless mux + src_hz, src_hz); + + // CLK USB = 0MHz + clock_stop(clk_usb); + + // CLK ADC = 0MHz + clock_stop(clk_adc); + + // CLK RTC = ideally XOSC (12MHz) / 256 = 46875Hz but could be rosc + uint clk_rtc_src = (dormant_source == DORMANT_SOURCE_XOSC) ? CLOCKS_CLK_RTC_CTRL_AUXSRC_VALUE_XOSC_CLKSRC + : CLOCKS_CLK_RTC_CTRL_AUXSRC_VALUE_ROSC_CLKSRC_PH; + + clock_configure(clk_rtc, + 0, // No GLMUX + clk_rtc_src, src_hz, 46875); + + // CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select and enable + clock_configure(clk_peri, 0, CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS, src_hz, src_hz); + + pll_deinit(pll_sys); + pll_deinit(pll_usb); + + // Assuming both xosc and rosc are running at the moment + if (dormant_source == DORMANT_SOURCE_XOSC) { + // Can disable rosc + rosc_disable(); + } else { + // Can disable xosc + xosc_disable(); + } + + // Reconfigure uart with new clocks + /* This dones not work with our current core */ + // setup_default_uart(); +} + +// Go to sleep until woken up by the RTC +void sleep_goto_sleep_until(datetime_t *t, rtc_callback_t callback) +{ + // We should have already called the sleep_run_from_dormant_source function + assert(dormant_source_valid(_dormant_source)); + + // Turn off all clocks when in sleep mode except for RTC + clocks_hw->sleep_en0 = CLOCKS_SLEEP_EN0_CLK_RTC_RTC_BITS; + clocks_hw->sleep_en1 = 0x0; + + rtc_set_alarm(t, callback); + + uint save = scb_hw->scr; + // Enable deep sleep at the proc + scb_hw->scr = save | M0PLUS_SCR_SLEEPDEEP_BITS; + + // Go to sleep + __wfi(); +} + +static void _go_dormant(void) +{ + assert(dormant_source_valid(_dormant_source)); + + if (_dormant_source == DORMANT_SOURCE_XOSC) { + xosc_dormant(); + } else { + rosc_set_dormant(); + } +} + +void sleep_goto_dormant_until_pin(uint gpio_pin, bool edge, bool high) +{ + bool low = !high; + bool level = !edge; + + // Configure the appropriate IRQ at IO bank 0 + assert(gpio_pin < NUM_BANK0_GPIOS); + + uint32_t event = 0; + + if (level && low) + event = IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_LEVEL_LOW_BITS; + if (level && high) + event = IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_LEVEL_HIGH_BITS; + if (edge && high) + event = IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_EDGE_HIGH_BITS; + if (edge && low) + event = IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_EDGE_LOW_BITS; + + gpio_set_dormant_irq_enabled(gpio_pin, event, true); + + _go_dormant(); + // Execution stops here until woken up + + // Clear the irq so we can go back to dormant mode again if we want + gpio_acknowledge_irq(gpio_pin, event); +} \ No newline at end of file diff --git a/src/power.h b/src/power.h index a4307ee07..63335104b 100644 --- a/src/power.h +++ b/src/power.h @@ -5,6 +5,7 @@ #include "configuration.h" #ifdef ARCH_ESP32 +// "legacy adc calibration driver is deprecated, please migrate to use esp_adc/adc_cali.h and esp_adc/adc_cali_scheme.h #include #include #endif @@ -48,6 +49,11 @@ extern INA219Sensor ina219Sensor; extern INA3221Sensor ina3221Sensor; #endif +#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) +#include "modules/Telemetry/Sensor/MAX17048Sensor.h" +extern MAX17048Sensor max17048Sensor; +#endif + #if HAS_RAKPROT && !defined(ARCH_PORTDUINO) #include "../variants/rak2560/RAK9154Sensor.h" extern RAK9154Sensor rak9154Sensor; @@ -82,6 +88,8 @@ class Power : private concurrency::OSThread bool axpChipInit(); /// Setup a simple ADC input based battery sensor bool analogInit(); + /// Setup a Lipo battery level sensor + bool lipoInit(); private: // open circuit voltage lookup table diff --git a/src/serialization/JSON.cpp b/src/serialization/JSON.cpp index e98bf47b9..42e615108 100644 --- a/src/serialization/JSON.cpp +++ b/src/serialization/JSON.cpp @@ -184,7 +184,7 @@ bool JSON::ExtractString(const char **data, std::string &str) // End of the string? else if (next_char == '"') { (*data)++; - str.reserve(); // Remove unused capacity + str.shrink_to_fit(); // Remove unused capacity return true; } diff --git a/src/serialization/JSONValue.cpp b/src/serialization/JSONValue.cpp index 51e0c1a3b..b2e9575bf 100644 --- a/src/serialization/JSONValue.cpp +++ b/src/serialization/JSONValue.cpp @@ -37,14 +37,14 @@ #define FREE_ARRAY(x) \ { \ JSONArray::iterator iter; \ - for (iter = x.begin(); iter != x.end(); iter++) { \ + for (iter = x.begin(); iter != x.end(); ++iter) { \ delete *iter; \ } \ } #define FREE_OBJECT(x) \ { \ JSONObject::iterator iter; \ - for (iter = x.begin(); iter != x.end(); iter++) { \ + for (iter = x.begin(); iter != x.end(); ++iter) { \ delete (*iter).second; \ } \ } @@ -430,7 +430,7 @@ JSONValue::JSONValue(const JSONValue &m_source) JSONArray source_array = *m_source.array_value; JSONArray::iterator iter; array_value = new JSONArray(); - for (iter = source_array.begin(); iter != source_array.end(); iter++) + for (iter = source_array.begin(); iter != source_array.end(); ++iter) array_value->push_back(new JSONValue(**iter)); break; } @@ -439,7 +439,7 @@ JSONValue::JSONValue(const JSONValue &m_source) JSONObject source_object = *m_source.object_value; object_value = new JSONObject(); JSONObject::iterator iter; - for (iter = source_object.begin(); iter != source_object.end(); iter++) { + for (iter = source_object.begin(); iter != source_object.end(); ++iter) { std::string name = (*iter).first; (*object_value)[name] = new JSONValue(*((*iter).second)); } @@ -462,12 +462,12 @@ JSONValue::~JSONValue() { if (type == JSONType_Array) { JSONArray::iterator iter; - for (iter = array_value->begin(); iter != array_value->end(); iter++) + for (iter = array_value->begin(); iter != array_value->end(); ++iter) delete *iter; delete array_value; } else if (type == JSONType_Object) { JSONObject::iterator iter; - for (iter = object_value->begin(); iter != object_value->end(); iter++) { + for (iter = object_value->begin(); iter != object_value->end(); ++iter) { delete (*iter).second; } delete object_value; @@ -722,7 +722,7 @@ std::vector JSONValue::ObjectKeys() const while (iter != object_value->end()) { keys.push_back(iter->first); - iter++; + ++iter; } } @@ -865,7 +865,7 @@ std::string JSONValue::StringifyString(const std::string &str) str_out += chr; } - iter++; + ++iter; } str_out += "\""; diff --git a/src/serialization/JSONValue.h b/src/serialization/JSONValue.h index 0380d324b..16d53e89f 100644 --- a/src/serialization/JSONValue.h +++ b/src/serialization/JSONValue.h @@ -40,15 +40,15 @@ class JSONValue public: JSONValue(/*NULL*/); - JSONValue(const char *m_char_value); - JSONValue(const std::string &m_string_value); - JSONValue(bool m_bool_value); - JSONValue(double m_number_value); - JSONValue(int m_integer_value); - JSONValue(unsigned int m_integer_value); - JSONValue(const JSONArray &m_array_value); - JSONValue(const JSONObject &m_object_value); - JSONValue(const JSONValue &m_source); + explicit JSONValue(const char *m_char_value); + explicit JSONValue(const std::string &m_string_value); + explicit JSONValue(bool m_bool_value); + explicit JSONValue(double m_number_value); + explicit JSONValue(int m_integer_value); + explicit JSONValue(unsigned int m_integer_value); + explicit JSONValue(const JSONArray &m_array_value); + explicit JSONValue(const JSONObject &m_object_value); + explicit JSONValue(const JSONValue &m_source); ~JSONValue(); bool IsNull() const; diff --git a/src/serialization/MeshPacketSerializer.cpp b/src/serialization/MeshPacketSerializer.cpp index e00dde024..ebcfaecab 100644 --- a/src/serialization/MeshPacketSerializer.cpp +++ b/src/serialization/MeshPacketSerializer.cpp @@ -1,3 +1,4 @@ +#ifndef NRF52_USE_JSON #include "MeshPacketSerializer.h" #include "JSON.h" #include "NodeDB.h" @@ -93,7 +94,7 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, } jsonObj["payload"] = new JSONValue(msgPayload); } else if (shouldLog) { - LOG_ERROR("Error decoding protobuf for telemetry message!\n"); + LOG_ERROR(errStr, msgType.c_str()); } break; } @@ -111,7 +112,7 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, msgPayload["role"] = new JSONValue((int)decoded->role); jsonObj["payload"] = new JSONValue(msgPayload); } else if (shouldLog) { - LOG_ERROR("Error decoding protobuf for nodeinfo message!\n"); + LOG_ERROR(errStr, msgType.c_str()); } break; } @@ -156,12 +157,12 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, } jsonObj["payload"] = new JSONValue(msgPayload); } else if (shouldLog) { - LOG_ERROR("Error decoding protobuf for position message!\n"); + LOG_ERROR(errStr, msgType.c_str()); } break; } case meshtastic_PortNum_WAYPOINT_APP: { - msgType = "position"; + msgType = "waypoint"; meshtastic_Waypoint scratch; meshtastic_Waypoint *decoded = NULL; memset(&scratch, 0, sizeof(scratch)); @@ -176,7 +177,7 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, msgPayload["longitude_i"] = new JSONValue((int)decoded->longitude_i); jsonObj["payload"] = new JSONValue(msgPayload); } else if (shouldLog) { - LOG_ERROR("Error decoding protobuf for position message!\n"); + LOG_ERROR(errStr, msgType.c_str()); } break; } @@ -202,7 +203,7 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, msgPayload["neighbors"] = new JSONValue(neighbors); jsonObj["payload"] = new JSONValue(msgPayload); } else if (shouldLog) { - LOG_ERROR("Error decoding protobuf for neighborinfo message!\n"); + LOG_ERROR(errStr, msgType.c_str()); } break; } @@ -234,7 +235,7 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, msgPayload["route"] = new JSONValue(route); jsonObj["payload"] = new JSONValue(msgPayload); } else if (shouldLog) { - LOG_ERROR("Error decoding protobuf for traceroute message!\n"); + LOG_ERROR(errStr, msgType.c_str()); } } break; @@ -261,7 +262,7 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, msgPayload["uptime"] = new JSONValue((unsigned int)decoded->uptime); jsonObj["payload"] = new JSONValue(msgPayload); } else if (shouldLog) { - LOG_ERROR("Error decoding protobuf for Paxcount message!\n"); + LOG_ERROR(errStr, msgType.c_str()); } break; } @@ -284,7 +285,7 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, jsonObj["payload"] = new JSONValue(msgPayload); } } else if (shouldLog) { - LOG_ERROR("Error decoding protobuf for RemoteHardware message!\n"); + LOG_ERROR(errStr, "RemoteHardware"); } break; } @@ -353,4 +354,5 @@ std::string MeshPacketSerializer::JsonSerializeEncrypted(const meshtastic_MeshPa delete value; return jsonStr; -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/serialization/MeshPacketSerializer.h b/src/serialization/MeshPacketSerializer.h index 03860ab35..989f30fc0 100644 --- a/src/serialization/MeshPacketSerializer.h +++ b/src/serialization/MeshPacketSerializer.h @@ -2,6 +2,7 @@ #include static const char hexChars[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; +static const char *errStr = "Error decoding protobuf for %s message!\n"; class MeshPacketSerializer { diff --git a/src/serialization/MeshPacketSerializer_nRF52.cpp b/src/serialization/MeshPacketSerializer_nRF52.cpp new file mode 100644 index 000000000..cd3aa1630 --- /dev/null +++ b/src/serialization/MeshPacketSerializer_nRF52.cpp @@ -0,0 +1,353 @@ +#ifdef NRF52_USE_JSON +#warning 'Using nRF52 Serializer' + +#include "ArduinoJson.h" +#include "MeshPacketSerializer.h" +#include "NodeDB.h" +#include "mesh/generated/meshtastic/mqtt.pb.h" +#include "mesh/generated/meshtastic/remote_hardware.pb.h" +#include "mesh/generated/meshtastic/telemetry.pb.h" +#include "modules/RoutingModule.h" +#include +#include + +StaticJsonDocument<1024> jsonObj; +StaticJsonDocument<1024> arrayObj; + +std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, bool shouldLog) +{ + // the created jsonObj is immutable after creation, so + // we need to do the heavy lifting before assembling it. + std::string msgType; + jsonObj.clear(); + arrayObj.clear(); + + if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag) { + switch (mp->decoded.portnum) { + case meshtastic_PortNum_TEXT_MESSAGE_APP: { + msgType = "text"; + // convert bytes to string + if (shouldLog) + LOG_DEBUG("got text message of size %u\n", mp->decoded.payload.size); + + char payloadStr[(mp->decoded.payload.size) + 1]; + memcpy(payloadStr, mp->decoded.payload.bytes, mp->decoded.payload.size); + payloadStr[mp->decoded.payload.size] = 0; // null terminated string + // check if this is a JSON payload + StaticJsonDocument<512> text_doc; + DeserializationError error = deserializeJson(text_doc, payloadStr); + if (error) { + // if it isn't, then we need to create a json object + // with the string as the value + if (shouldLog) + LOG_INFO("text message payload is of type plaintext\n"); + jsonObj["payload"]["text"] = payloadStr; + } else { + // if it is, then we can just use the json object + if (shouldLog) + LOG_INFO("text message payload is of type json\n"); + jsonObj["payload"] = text_doc; + } + break; + } + case meshtastic_PortNum_TELEMETRY_APP: { + msgType = "telemetry"; + meshtastic_Telemetry scratch; + meshtastic_Telemetry *decoded = NULL; + memset(&scratch, 0, sizeof(scratch)); + if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Telemetry_msg, &scratch)) { + decoded = &scratch; + if (decoded->which_variant == meshtastic_Telemetry_device_metrics_tag) { + jsonObj["payload"]["battery_level"] = (unsigned int)decoded->variant.device_metrics.battery_level; + jsonObj["payload"]["voltage"] = decoded->variant.device_metrics.voltage; + jsonObj["payload"]["channel_utilization"] = decoded->variant.device_metrics.channel_utilization; + jsonObj["payload"]["air_util_tx"] = decoded->variant.device_metrics.air_util_tx; + jsonObj["payload"]["uptime_seconds"] = (unsigned int)decoded->variant.device_metrics.uptime_seconds; + } else if (decoded->which_variant == meshtastic_Telemetry_environment_metrics_tag) { + jsonObj["payload"]["temperature"] = decoded->variant.environment_metrics.temperature; + jsonObj["payload"]["relative_humidity"] = decoded->variant.environment_metrics.relative_humidity; + jsonObj["payload"]["barometric_pressure"] = decoded->variant.environment_metrics.barometric_pressure; + jsonObj["payload"]["gas_resistance"] = decoded->variant.environment_metrics.gas_resistance; + jsonObj["payload"]["voltage"] = decoded->variant.environment_metrics.voltage; + jsonObj["payload"]["current"] = decoded->variant.environment_metrics.current; + jsonObj["payload"]["lux"] = decoded->variant.environment_metrics.lux; + jsonObj["payload"]["white_lux"] = decoded->variant.environment_metrics.white_lux; + jsonObj["payload"]["iaq"] = (uint)decoded->variant.environment_metrics.iaq; + jsonObj["payload"]["wind_speed"] = decoded->variant.environment_metrics.wind_speed; + jsonObj["payload"]["wind_direction"] = (uint)decoded->variant.environment_metrics.wind_direction; + jsonObj["payload"]["wind_gust"] = decoded->variant.environment_metrics.wind_gust; + jsonObj["payload"]["wind_lull"] = decoded->variant.environment_metrics.wind_lull; + } else if (decoded->which_variant == meshtastic_Telemetry_air_quality_metrics_tag) { + jsonObj["payload"]["pm10"] = (unsigned int)decoded->variant.air_quality_metrics.pm10_standard; + jsonObj["payload"]["pm25"] = (unsigned int)decoded->variant.air_quality_metrics.pm25_standard; + jsonObj["payload"]["pm100"] = (unsigned int)decoded->variant.air_quality_metrics.pm100_standard; + jsonObj["payload"]["pm10_e"] = (unsigned int)decoded->variant.air_quality_metrics.pm10_environmental; + jsonObj["payload"]["pm25_e"] = (unsigned int)decoded->variant.air_quality_metrics.pm25_environmental; + jsonObj["payload"]["pm100_e"] = (unsigned int)decoded->variant.air_quality_metrics.pm100_environmental; + } else if (decoded->which_variant == meshtastic_Telemetry_power_metrics_tag) { + jsonObj["payload"]["voltage_ch1"] = decoded->variant.power_metrics.ch1_voltage; + jsonObj["payload"]["current_ch1"] = decoded->variant.power_metrics.ch1_current; + jsonObj["payload"]["voltage_ch2"] = decoded->variant.power_metrics.ch2_voltage; + jsonObj["payload"]["current_ch2"] = decoded->variant.power_metrics.ch2_current; + jsonObj["payload"]["voltage_ch3"] = decoded->variant.power_metrics.ch3_voltage; + jsonObj["payload"]["current_ch3"] = decoded->variant.power_metrics.ch3_current; + } + } else if (shouldLog) { + LOG_ERROR("Error decoding protobuf for telemetry message!\n"); + return ""; + } + break; + } + case meshtastic_PortNum_NODEINFO_APP: { + msgType = "nodeinfo"; + meshtastic_User scratch; + meshtastic_User *decoded = NULL; + memset(&scratch, 0, sizeof(scratch)); + if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_User_msg, &scratch)) { + decoded = &scratch; + jsonObj["payload"]["id"] = decoded->id; + jsonObj["payload"]["longname"] = decoded->long_name; + jsonObj["payload"]["shortname"] = decoded->short_name; + jsonObj["payload"]["hardware"] = decoded->hw_model; + jsonObj["payload"]["role"] = (int)decoded->role; + } else if (shouldLog) { + LOG_ERROR("Error decoding protobuf for nodeinfo message!\n"); + return ""; + } + break; + } + case meshtastic_PortNum_POSITION_APP: { + msgType = "position"; + meshtastic_Position scratch; + meshtastic_Position *decoded = NULL; + memset(&scratch, 0, sizeof(scratch)); + if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Position_msg, &scratch)) { + decoded = &scratch; + if ((int)decoded->time) { + jsonObj["payload"]["time"] = (unsigned int)decoded->time; + } + if ((int)decoded->timestamp) { + jsonObj["payload"]["timestamp"] = (unsigned int)decoded->timestamp; + } + jsonObj["payload"]["latitude_i"] = (int)decoded->latitude_i; + jsonObj["payload"]["longitude_i"] = (int)decoded->longitude_i; + if ((int)decoded->altitude) { + jsonObj["payload"]["altitude"] = (int)decoded->altitude; + } + if ((int)decoded->ground_speed) { + jsonObj["payload"]["ground_speed"] = (unsigned int)decoded->ground_speed; + } + if (int(decoded->ground_track)) { + jsonObj["payload"]["ground_track"] = (unsigned int)decoded->ground_track; + } + if (int(decoded->sats_in_view)) { + jsonObj["payload"]["sats_in_view"] = (unsigned int)decoded->sats_in_view; + } + if ((int)decoded->PDOP) { + jsonObj["payload"]["PDOP"] = (int)decoded->PDOP; + } + if ((int)decoded->HDOP) { + jsonObj["payload"]["HDOP"] = (int)decoded->HDOP; + } + if ((int)decoded->VDOP) { + jsonObj["payload"]["VDOP"] = (int)decoded->VDOP; + } + if ((int)decoded->precision_bits) { + jsonObj["payload"]["precision_bits"] = (int)decoded->precision_bits; + } + } else if (shouldLog) { + LOG_ERROR("Error decoding protobuf for position message!\n"); + return ""; + } + break; + } + case meshtastic_PortNum_WAYPOINT_APP: { + msgType = "position"; + meshtastic_Waypoint scratch; + meshtastic_Waypoint *decoded = NULL; + memset(&scratch, 0, sizeof(scratch)); + if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Waypoint_msg, &scratch)) { + decoded = &scratch; + jsonObj["payload"]["id"] = (unsigned int)decoded->id; + jsonObj["payload"]["name"] = decoded->name; + jsonObj["payload"]["description"] = decoded->description; + jsonObj["payload"]["expire"] = (unsigned int)decoded->expire; + jsonObj["payload"]["locked_to"] = (unsigned int)decoded->locked_to; + jsonObj["payload"]["latitude_i"] = (int)decoded->latitude_i; + jsonObj["payload"]["longitude_i"] = (int)decoded->longitude_i; + } else if (shouldLog) { + LOG_ERROR("Error decoding protobuf for position message!\n"); + return ""; + } + break; + } + case meshtastic_PortNum_NEIGHBORINFO_APP: { + msgType = "neighborinfo"; + meshtastic_NeighborInfo scratch; + meshtastic_NeighborInfo *decoded = NULL; + memset(&scratch, 0, sizeof(scratch)); + if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_NeighborInfo_msg, + &scratch)) { + decoded = &scratch; + jsonObj["payload"]["node_id"] = (unsigned int)decoded->node_id; + jsonObj["payload"]["node_broadcast_interval_secs"] = (unsigned int)decoded->node_broadcast_interval_secs; + jsonObj["payload"]["last_sent_by_id"] = (unsigned int)decoded->last_sent_by_id; + jsonObj["payload"]["neighbors_count"] = decoded->neighbors_count; + + JsonObject neighbors_obj = arrayObj.to(); + JsonArray neighbors = neighbors_obj.createNestedArray("neighbors"); + JsonObject neighbors_0 = neighbors.createNestedObject(); + + for (uint8_t i = 0; i < decoded->neighbors_count; i++) { + neighbors_0["node_id"] = (unsigned int)decoded->neighbors[i].node_id; + neighbors_0["snr"] = (int)decoded->neighbors[i].snr; + neighbors[i + 1] = neighbors_0; + neighbors_0.clear(); + } + neighbors.remove(0); + jsonObj["payload"]["neighbors"] = neighbors; + } else if (shouldLog) { + LOG_ERROR("Error decoding protobuf for neighborinfo message!\n"); + return ""; + } + break; + } + case meshtastic_PortNum_TRACEROUTE_APP: { + if (mp->decoded.request_id) { // Only report the traceroute response + msgType = "traceroute"; + meshtastic_RouteDiscovery scratch; + meshtastic_RouteDiscovery *decoded = NULL; + memset(&scratch, 0, sizeof(scratch)); + if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_RouteDiscovery_msg, + &scratch)) { + decoded = &scratch; + JsonArray route = arrayObj.createNestedArray("route"); + + auto addToRoute = [](JsonArray *route, NodeNum num) { + char long_name[40] = "Unknown"; + meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(num); + bool name_known = node ? node->has_user : false; + if (name_known) + memcpy(long_name, node->user.long_name, sizeof(long_name)); + route->add(long_name); + }; + + addToRoute(&route, mp->to); // route.add(mp->to); + for (uint8_t i = 0; i < decoded->route_count; i++) { + addToRoute(&route, decoded->route[i]); // route.add(decoded->route[i]); + } + addToRoute(&route, + mp->from); // route.add(mp->from); // Ended at the original destination (source of response) + + jsonObj["payload"]["route"] = route; + } else if (shouldLog) { + LOG_ERROR("Error decoding protobuf for traceroute message!\n"); + return ""; + } + } else { + LOG_WARN("Traceroute response not reported"); + return ""; + } + break; + } + case meshtastic_PortNum_DETECTION_SENSOR_APP: { + msgType = "detection"; + char payloadStr[(mp->decoded.payload.size) + 1]; + memcpy(payloadStr, mp->decoded.payload.bytes, mp->decoded.payload.size); + payloadStr[mp->decoded.payload.size] = 0; // null terminated string + jsonObj["payload"]["text"] = payloadStr; + break; + } + case meshtastic_PortNum_REMOTE_HARDWARE_APP: { + meshtastic_HardwareMessage scratch; + meshtastic_HardwareMessage *decoded = NULL; + memset(&scratch, 0, sizeof(scratch)); + if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_HardwareMessage_msg, + &scratch)) { + decoded = &scratch; + if (decoded->type == meshtastic_HardwareMessage_Type_GPIOS_CHANGED) { + msgType = "gpios_changed"; + jsonObj["payload"]["gpio_value"] = (unsigned int)decoded->gpio_value; + } else if (decoded->type == meshtastic_HardwareMessage_Type_READ_GPIOS_REPLY) { + msgType = "gpios_read_reply"; + jsonObj["payload"]["gpio_value"] = (unsigned int)decoded->gpio_value; + jsonObj["payload"]["gpio_mask"] = (unsigned int)decoded->gpio_mask; + } + } else if (shouldLog) { + LOG_ERROR("Error decoding protobuf for RemoteHardware message!\n"); + return ""; + } + break; + } + // add more packet types here if needed + default: + LOG_WARN("Unsupported packet type %d\n", mp->decoded.portnum); + return ""; + break; + } + } else if (shouldLog) { + LOG_WARN("Couldn't convert encrypted payload of MeshPacket to JSON\n"); + return ""; + } + + jsonObj["id"] = (unsigned int)mp->id; + jsonObj["timestamp"] = (unsigned int)mp->rx_time; + jsonObj["to"] = (unsigned int)mp->to; + jsonObj["from"] = (unsigned int)mp->from; + jsonObj["channel"] = (unsigned int)mp->channel; + jsonObj["type"] = msgType.c_str(); + jsonObj["sender"] = owner.id; + if (mp->rx_rssi != 0) + jsonObj["rssi"] = (int)mp->rx_rssi; + if (mp->rx_snr != 0) + jsonObj["snr"] = (float)mp->rx_snr; + if (mp->hop_start != 0 && mp->hop_limit <= mp->hop_start) { + jsonObj["hops_away"] = (unsigned int)(mp->hop_start - mp->hop_limit); + jsonObj["hop_start"] = (unsigned int)(mp->hop_start); + } + + // serialize and write it to the stream + + // Serial.printf("serialized json message: \r\n"); + // serializeJson(jsonObj, Serial); + // Serial.println(""); + + std::string jsonStr = ""; + serializeJson(jsonObj, jsonStr); + + if (shouldLog) + LOG_INFO("serialized json message: %s\n", jsonStr.c_str()); + + return jsonStr; +} + +std::string MeshPacketSerializer::JsonSerializeEncrypted(const meshtastic_MeshPacket *mp) +{ + jsonObj.clear(); + jsonObj["id"] = (unsigned int)mp->id; + jsonObj["time_ms"] = (double)millis(); + jsonObj["timestamp"] = (unsigned int)mp->rx_time; + jsonObj["to"] = (unsigned int)mp->to; + jsonObj["from"] = (unsigned int)mp->from; + jsonObj["channel"] = (unsigned int)mp->channel; + jsonObj["want_ack"] = mp->want_ack; + + if (mp->rx_rssi != 0) + jsonObj["rssi"] = (int)mp->rx_rssi; + if (mp->rx_snr != 0) + jsonObj["snr"] = (float)mp->rx_snr; + if (mp->hop_start != 0 && mp->hop_limit <= mp->hop_start) { + jsonObj["hops_away"] = (unsigned int)(mp->hop_start - mp->hop_limit); + jsonObj["hop_start"] = (unsigned int)(mp->hop_start); + } + jsonObj["size"] = (unsigned int)mp->encrypted.size; + auto encryptedStr = bytesToHex(mp->encrypted.bytes, mp->encrypted.size); + jsonObj["bytes"] = encryptedStr.c_str(); + + // serialize and write it to the stream + std::string jsonStr = ""; + serializeJson(jsonObj, jsonStr); + + return jsonStr; +} +#endif \ No newline at end of file diff --git a/src/shutdown.h b/src/shutdown.h index 3f191eea8..481e7778d 100644 --- a/src/shutdown.h +++ b/src/shutdown.h @@ -44,7 +44,7 @@ void powerCommandsCheck() if (shutdownAtMsec && millis() > shutdownAtMsec) { LOG_INFO("Shutting down from admin command\n"); -#if defined(ARCH_NRF52) || defined(ARCH_ESP32) +#if defined(ARCH_NRF52) || defined(ARCH_ESP32) || defined(ARCH_RP2040) playShutdownMelody(); power->shutdown(); #elif defined(ARCH_PORTDUINO) diff --git a/src/sleep.cpp b/src/sleep.cpp index 27e81ce54..60f83f537 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -5,6 +5,7 @@ #endif #include "ButtonThread.h" +#include "Default.h" #include "Led.h" #include "MeshRadio.h" #include "MeshService.h" @@ -17,6 +18,7 @@ #include "target_specific.h" #ifdef ARCH_ESP32 +// "esp_pm_config_esp32_t is deprecated, please include esp_pm.h and use esp_pm_config_t instead" #include "esp32/pm.h" #include "esp_pm.h" #if HAS_WIFI @@ -28,6 +30,7 @@ esp_sleep_source_t wakeCause; // the reason we booted this time #endif +#include "Throttle.h" #ifndef INCLUDE_vTaskSuspend #define INCLUDE_vTaskSuspend 0 @@ -168,7 +171,8 @@ static void waitEnterSleep(bool skipPreflight = false) while (!doPreflightSleep()) { delay(100); // Kinda yucky - wait until radio says say we can shutdown (finished in process sends/receives) - if (millis() - now > 30 * 1000) { // If we wait too long just report an error and go to sleep + if (!Throttle::isWithinTimespanMs(now, + THIRTY_SECONDS_MS)) { // If we wait too long just report an error and go to sleep RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_SLEEP_ENTER_WAIT); assert(0); // FIXME - for now we just restart, need to fix bug #167 break; @@ -271,13 +275,6 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false) digitalWrite(LORA_CS, HIGH); gpio_hold_en((gpio_num_t)LORA_CS); } - -#if defined(I2C_SDA) - Wire.end(); - pinMode(I2C_SDA, ANALOG); - pinMode(I2C_SCL, ANALOG); -#endif - #endif #ifdef HAS_PMU @@ -315,6 +312,14 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false) } #endif +#if defined(ARCH_ESP32) && defined(I2C_SDA) + // Added by https://github.com/meshtastic/firmware/pull/4418 + // Possibly to support Heltec Capsule Sensor? + Wire.end(); + pinMode(I2C_SDA, ANALOG); + pinMode(I2C_SCL, ANALOG); +#endif + console->flush(); cpuDeepSleep(msecToWake); } @@ -446,12 +451,17 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r */ void enableModemSleep() { +#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0) + static esp_pm_config_t esp32_config; // filled with zeros because bss +#else static esp_pm_config_esp32_t esp32_config; // filled with zeros because bss - +#endif #if CONFIG_IDF_TARGET_ESP32S3 esp32_config.max_freq_mhz = CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ; #elif CONFIG_IDF_TARGET_ESP32S2 esp32_config.max_freq_mhz = CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ; +#elif CONFIG_IDF_TARGET_ESP32C6 + esp32_config.max_freq_mhz = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ; #elif CONFIG_IDF_TARGET_ESP32C3 esp32_config.max_freq_mhz = CONFIG_ESP32C3_DEFAULT_CPU_FREQ_MHZ; #else diff --git a/test/test_crypto/test_main.cpp b/test/test_crypto/test_main.cpp index 129c88283..cbe366048 100644 --- a/test/test_crypto/test_main.cpp +++ b/test/test_crypto/test_main.cpp @@ -98,7 +98,44 @@ void test_DH25519(void) HexToBytes(private_key, "18630f93598637c35da623a74559cf944374a559114c7937811041fc8605564a"); crypto->setDHPrivateKey(private_key); TEST_ASSERT(!crypto->setDHPublicKey(public_key)); // Weak public key results in 0 shared key + + HexToBytes(public_key, "f7e13a1a067d2f4e1061bf9936fde5be6b0c2494a8f809cbac7f290ef719e91c"); + HexToBytes(private_key, "10300724f3bea134eb1575245ef26ff9b8ccd59849cd98ce1a59002fe1d5986c"); + HexToBytes(expected_shared, "24becd5dfed9e9289ba2e15b82b0d54f8e9aacb72f5e4248c58d8d74b451ce76"); + crypto->setDHPrivateKey(private_key); + TEST_ASSERT(crypto->setDHPublicKey(public_key)); + crypto->hash(crypto->shared_key, 32); + TEST_ASSERT_EQUAL_MEMORY(expected_shared, crypto->shared_key, 32); } + +void test_PKC_Decrypt(void) +{ + uint8_t private_key[32]; + uint8_t public_key[32]; + uint8_t expected_shared[32]; + uint8_t expected_decrypted[32]; + uint8_t radioBytes[128] __attribute__((__aligned__)); + uint8_t decrypted[128] __attribute__((__aligned__)); + uint8_t expected_nonce[16]; + + uint32_t fromNode; + HexToBytes(public_key, "db18fc50eea47f00251cb784819a3cf5fc361882597f589f0d7ff820e8064457"); + HexToBytes(private_key, "a00330633e63522f8a4d81ec6d9d1e6617f6c8ffd3a4c698229537d44e522277"); + HexToBytes(expected_shared, "777b1545c9d6f9a2"); + HexToBytes(expected_decrypted, "08011204746573744800"); + HexToBytes(radioBytes, "8c646d7a2909000062d6b2136b00000040df24abfcc30a17a3d9046726099e796a1c036a792b"); + HexToBytes(expected_nonce, "62d6b213036a792b2909000000"); + fromNode = 0x0929; + crypto->setDHPrivateKey(private_key); + TEST_ASSERT(crypto->setDHPublicKey(public_key)); + crypto->hash(crypto->shared_key, 32); + crypto->decryptCurve25519(fromNode, 0x13b2d662, 22, radioBytes + 16, decrypted); + TEST_ASSERT_EQUAL_MEMORY(expected_shared, crypto->shared_key, 8); + TEST_ASSERT_EQUAL_MEMORY(expected_nonce, crypto->nonce, 13); + + TEST_ASSERT_EQUAL_MEMORY(expected_decrypted, decrypted, 10); +} + void test_AES_CTR(void) { uint8_t expected[32]; @@ -129,6 +166,7 @@ void setup() { // NOTE!!! Wait for >2 secs // if board doesn't support software reset via Serial.DTR/RTS + delay(10); delay(2000); UNITY_BEGIN(); // IMPORTANT LINE! @@ -136,6 +174,7 @@ void setup() RUN_TEST(test_ECB_AES256); RUN_TEST(test_DH25519); RUN_TEST(test_AES_CTR); + RUN_TEST(test_PKC_Decrypt); } void loop() diff --git a/userPrefs.h b/userPrefs.h index 3b32bf79b..ed622bcfb 100644 --- a/userPrefs.h +++ b/userPrefs.h @@ -1,31 +1,57 @@ #ifndef _USERPREFS_ #define _USERPREFS_ +// Slipstream values: + +#define USERPREFS_TZ_STRING "tzplaceholder " + // Uncomment and modify to set device defaults -// #define EVENT_MODE 1 +// #define USERPREFS_EVENT_MODE 1 -// #define CONFIG_LORA_REGION_USERPREFS meshtastic_Config_LoRaConfig_RegionCode_US -// #define LORACONFIG_MODEM_PRESET_USERPREFS meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST -// #define LORACONFIG_CHANNEL_NUM_USERPREFS 31 -// #define CONFIG_LORA_IGNORE_MQTT_USERPREFS true +// #define USERPREFS_CONFIG_LORA_REGION meshtastic_Config_LoRaConfig_RegionCode_US +// #define USERPREFS_LORACONFIG_MODEM_PRESET meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST +// #define USERPREFS_LORACONFIG_CHANNEL_NUM 31 +// #define USERPREFS_CONFIG_LORA_IGNORE_MQTT true + +// #define USERPREFS_CONFIG_GPS_MODE meshtastic_Config_PositionConfig_GpsMode_ENABLED + +// #define USERPREFS_CHANNELS_TO_WRITE 3 /* -#define CHANNEL_0_PSK_USERPREFS \ +#define USERPREFS_CHANNEL_0_PSK \ { \ 0x38, 0x4b, 0xbc, 0xc0, 0x1d, 0xc0, 0x22, 0xd1, 0x81, 0xbf, 0x36, 0xb8, 0x61, 0x21, 0xe1, 0xfb, 0x96, 0xb7, 0x2e, 0x55, \ 0xbf, 0x74, 0x22, 0x7e, 0x9d, 0x6a, 0xfb, 0x48, 0xd6, 0x4c, 0xb1, 0xa1 \ } */ -// #define CHANNEL_0_NAME_USERPREFS "DEFCONnect" -// #define CHANNEL_0_PRECISION_USERPREFS 14 +// #define USERPREFS_CHANNEL_0_NAME "DEFCONnect" +// #define USERPREFS_CHANNEL_0_PRECISION 14 +/* +#define USERPREFS_CHANNEL_1_PSK \ + { \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 \ + } +*/ +// #define USERPREFS_CHANNEL_1_NAME "REPLACEME" +// #define USERPREFS_CHANNEL_1_PRECISION 14 +/* +#define USERPREFS_CHANNEL_2_PSK \ + { \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 \ + } +*/ +// #define USERPREFS_CHANNEL_2_NAME "REPLACEME" +// #define USERPREFS_CHANNEL_2_PRECISION 14 -// #define CONFIG_OWNER_LONG_NAME_USERPREFS "My Long Name" -// #define CONFIG_OWNER_SHORT_NAME_USERPREFS "MLN" +// #define USERPREFS_CONFIG_OWNER_LONG_NAME "My Long Name" +// #define USERPREFS_CONFIG_OWNER_SHORT_NAME "MLN" -// #define SPLASH_TITLE_USERPREFS "DEFCONtastic" +// #define USERPREFS_SPLASH_TITLE "DEFCONtastic" // #define icon_width 34 // #define icon_height 29 -// #define HAS_USERPREFS_SPLASH +// #define USERPREFS_HAS_SPLASH /* static unsigned char icon_bits[] = { 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, @@ -37,8 +63,8 @@ static unsigned char icon_bits[] = { 0x00, 0x00, 0x1C, 0x00, 0x70, 0x00, 0x00, 0x1C, 0x00, 0x70, 0x00, 0x00, 0x1C, 0x00, 0x70, 0x00, 0x00, 0x1C, 0x00}; */ /* -#define ADMIN_KEY_USERPREFS 1 -static unsigned char admin_key_userprefs[] = {0xcd, 0xc0, 0xb4, 0x3c, 0x53, 0x24, 0xdf, 0x13, 0xca, 0x5a, 0xa6, +#define USERPREFS_USE_ADMIN_KEY 1 +static unsigned char USERPREFS_ADMIN_KEY[] = {0xcd, 0xc0, 0xb4, 0x3c, 0x53, 0x24, 0xdf, 0x13, 0xca, 0x5a, 0xa6, 0x0c, 0x0d, 0xec, 0x85, 0x5a, 0x4c, 0xf6, 0x1a, 0x96, 0x04, 0x1a, 0x3e, 0xfc, 0xbb, 0x8e, 0x33, 0x71, 0xe5, 0xfc, 0xff, 0x3c}; */ diff --git a/userPrefs.json b/userPrefs.json new file mode 100644 index 000000000..bc62602be --- /dev/null +++ b/userPrefs.json @@ -0,0 +1,16 @@ +{ + "USERPREFS_CHANNEL_0_NAME": "\"DEFCONnect\"", + "USERPREFS_CHANNEL_0_PRECISION": "14", + "USERPREFS_CHANNEL_0_PSK": "{ 0x38, 0x4b, 0xbc, 0xc0, 0x1d, 0xc0, 0x22, 0xd1, 0x81, 0xbf, 0x36, 0xb8, 0x61, 0x21, 0xe1, 0xfb, 0x96, 0xb7, 0x2e, 0x55, 0xbf, 0x74, 0x22, 0x7e, 0x9d, 0x6a, 0xfb, 0x48, 0xd6, 0x4c, 0xb1, 0xa1 }", + "USERPREFS_CONFIG_LORA_IGNORE_MQTT": "true", + "USERPREFS_CONFIG_LORA_REGION": "meshtastic_Config_LoRaConfig_RegionCode_US", + "USERPREFS_CONFIG_OWNER_LONG_NAME": "\"My Long Name\"", + "USERPREFS_CONFIG_OWNER_SHORT_NAME": "\"MLN\"", + "USERPREFS_EVENT_MODE": "1", + "USERPREFS_HAS_SPLASH": "", + "USERPREFS_LORACONFIG_CHANNEL_NUM": "31", + "USERPREFS_LORACONFIG_MODEM_PRESET": "meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST", + "USERPREFS_SPLASH_TITLE": "\"DEFCONtastic\"", + "USERPREFS_TZ_STRING": "\"tzplaceholder \"", + "USERPREFS_USE_ADMIN_KEY": "1" +} diff --git a/variants/Dongle_nRF52840-pca10059-v1/platformio.ini b/variants/Dongle_nRF52840-pca10059-v1/platformio.ini index c87f83d39..a98656e86 100644 --- a/variants/Dongle_nRF52840-pca10059-v1/platformio.ini +++ b/variants/Dongle_nRF52840-pca10059-v1/platformio.ini @@ -1,4 +1,5 @@ [env:pca10059_diy_eink] +board_level = extra extends = nrf52840_base board = nordic_pca10059 build_flags = ${nrf52840_base.build_flags} -Ivariants/Dongle_nRF52840-pca10059-v1 -D NORDIC_PCA10059 diff --git a/variants/ME25LS01-4Y10TD/platformio.ini b/variants/ME25LS01-4Y10TD/platformio.ini index 985919d2d..479a4e79c 100644 --- a/variants/ME25LS01-4Y10TD/platformio.ini +++ b/variants/ME25LS01-4Y10TD/platformio.ini @@ -3,7 +3,7 @@ extends = nrf52840_base board = me25ls01-4y10td board_level = extra ; platform = https://github.com/maxgerhardt/platform-nordicnrf52#cac6fcf943a41accd2aeb4f3659ae297a73f422e -build_flags = ${nrf52840_base.build_flags} -Ivariants/ME25LS01-4Y10TD -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -DME25LS01_4Y10TD ;-DRADIOLIB_GODMODE +build_flags = ${nrf52840_base.build_flags} -Ivariants/ME25LS01-4Y10TD -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -DME25LS01_4Y10TD -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld @@ -12,4 +12,4 @@ lib_deps = ${nrf52840_base.lib_deps} ; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) upload_protocol = nrfutil -upload_port = /dev/ttyACM1 +upload_port = /dev/ttyACM1 \ No newline at end of file diff --git a/variants/ME25LS01-4Y10TD/rfswitch.h b/variants/ME25LS01-4Y10TD/rfswitch.h new file mode 100644 index 000000000..cda6364f5 --- /dev/null +++ b/variants/ME25LS01-4Y10TD/rfswitch.h @@ -0,0 +1,15 @@ +#include "RadioLib.h" +#include "nrf.h" + +// set RF switch configuration for Wio WM1110 +// Wio WM1110 uses DIO5 and DIO6 for RF switching + +static const uint32_t rfswitch_dio_pins[] = {RADIOLIB_LR11X0_DIO5, RADIOLIB_LR11X0_DIO6, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC}; + +static const Module::RfSwitchMode_t rfswitch_table[] = { + // mode DIO5 DIO6 + {LR11x0::MODE_STBY, {LOW, LOW}}, {LR11x0::MODE_RX, {HIGH, LOW}}, + {LR11x0::MODE_TX, {HIGH, HIGH}}, {LR11x0::MODE_TX_HP, {LOW, HIGH}}, + {LR11x0::MODE_TX_HF, {LOW, LOW}}, {LR11x0::MODE_GNSS, {LOW, LOW}}, + {LR11x0::MODE_WIFI, {LOW, LOW}}, END_OF_MODE_TABLE, +}; diff --git a/variants/ME25LS01-4Y10TD/variant.h b/variants/ME25LS01-4Y10TD/variant.h index 715de8ee6..e772069da 100644 --- a/variants/ME25LS01-4Y10TD/variant.h +++ b/variants/ME25LS01-4Y10TD/variant.h @@ -94,7 +94,7 @@ extern "C" { #define USE_LR1110 #define LR1110_IRQ_PIN LORA_DIO1 -#define LR1110_NRESER_PIN LORA_RESET +#define LR1110_NRESET_PIN LORA_RESET #define LR1110_BUSY_PIN LORA_DIO2 #define LR1110_SPI_NSS_PIN LORA_CS #define LR1110_SPI_SCK_PIN LORA_SCK @@ -103,7 +103,6 @@ extern "C" { #define LR11X0_DIO3_TCXO_VOLTAGE 1.6 #define LR11X0_DIO_AS_RF_SWITCH -#define LR11X0_DIO_RF_SWITCH_CONFIG 0x0f, 0x0, 0x09, 0x0B, 0x0A, 0x0, 0x4, 0x0 #define HAS_GPS 0 diff --git a/variants/ME25LS01-4Y10TD_e-ink/platformio.ini b/variants/ME25LS01-4Y10TD_e-ink/platformio.ini index 4e837abd8..f2e3a49e3 100644 --- a/variants/ME25LS01-4Y10TD_e-ink/platformio.ini +++ b/variants/ME25LS01-4Y10TD_e-ink/platformio.ini @@ -3,7 +3,7 @@ extends = nrf52840_base board = me25ls01-4y10td board_level = extra ; platform = https://github.com/maxgerhardt/platform-nordicnrf52#cac6fcf943a41accd2aeb4f3659ae297a73f422e -build_flags = ${nrf52840_base.build_flags} -Ivariants/ME25LS01-4Y10TD_e-ink -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -DME25LS01_4Y10TD ;-DRADIOLIB_GODMODE +build_flags = ${nrf52840_base.build_flags} -Ivariants/ME25LS01-4Y10TD_e-ink -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -DME25LS01_4Y10TD -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. -DEINK_DISPLAY_MODEL=GxEPD2_420_GDEY042T81 @@ -16,4 +16,4 @@ lib_deps = zinggjm/GxEPD2@^1.5.8 ; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) upload_protocol = nrfutil -upload_port = /dev/ttyACM1 +upload_port = /dev/ttyACM1 \ No newline at end of file diff --git a/variants/ME25LS01-4Y10TD_e-ink/rfswitch.h b/variants/ME25LS01-4Y10TD_e-ink/rfswitch.h new file mode 100644 index 000000000..cda6364f5 --- /dev/null +++ b/variants/ME25LS01-4Y10TD_e-ink/rfswitch.h @@ -0,0 +1,15 @@ +#include "RadioLib.h" +#include "nrf.h" + +// set RF switch configuration for Wio WM1110 +// Wio WM1110 uses DIO5 and DIO6 for RF switching + +static const uint32_t rfswitch_dio_pins[] = {RADIOLIB_LR11X0_DIO5, RADIOLIB_LR11X0_DIO6, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC}; + +static const Module::RfSwitchMode_t rfswitch_table[] = { + // mode DIO5 DIO6 + {LR11x0::MODE_STBY, {LOW, LOW}}, {LR11x0::MODE_RX, {HIGH, LOW}}, + {LR11x0::MODE_TX, {HIGH, HIGH}}, {LR11x0::MODE_TX_HP, {LOW, HIGH}}, + {LR11x0::MODE_TX_HF, {LOW, LOW}}, {LR11x0::MODE_GNSS, {LOW, LOW}}, + {LR11x0::MODE_WIFI, {LOW, LOW}}, END_OF_MODE_TABLE, +}; diff --git a/variants/ME25LS01-4Y10TD_e-ink/variant.h b/variants/ME25LS01-4Y10TD_e-ink/variant.h index 60996d468..797394ce6 100644 --- a/variants/ME25LS01-4Y10TD_e-ink/variant.h +++ b/variants/ME25LS01-4Y10TD_e-ink/variant.h @@ -97,7 +97,7 @@ static const uint8_t MISO = PIN_SPI_MISO; static const uint8_t SCK = PIN_SPI_SCK; // EPD SPI -#define PIN_SPI1_MISO (-1) // Not Used for EPD +#define PIN_SPI1_MISO (32 + 2) // Not Used for EPD but needs to be defined #define PIN_SPI1_MOSI (0 + 10) // EPD_MOSI P0.10 #define PIN_SPI1_SCK (0 + 9) // EPD_SCLK P0.09 @@ -117,7 +117,7 @@ static const uint8_t SCK = PIN_SPI_SCK; #define USE_LR1110 #define LR1110_IRQ_PIN LORA_DIO1 -#define LR1110_NRESER_PIN LORA_RESET +#define LR1110_NRESET_PIN LORA_RESET #define LR1110_BUSY_PIN LORA_DIO2 #define LR1110_SPI_NSS_PIN LORA_CS #define LR1110_SPI_SCK_PIN LORA_SCK @@ -126,7 +126,6 @@ static const uint8_t SCK = PIN_SPI_SCK; #define LR11X0_DIO3_TCXO_VOLTAGE 1.6 #define LR11X0_DIO_AS_RF_SWITCH -#define LR11X0_DIO_RF_SWITCH_CONFIG 0x0f, 0x0, 0x09, 0x0B, 0x0A, 0x0, 0x4, 0x0 #define HAS_GPS 0 @@ -159,4 +158,4 @@ static const uint8_t SCK = PIN_SPI_SCK; * Arduino objects - C++ only *----------------------------------------------------------------------------*/ -#endif // _VARIANT_ME25LS01_4Y10TD__ +#endif // _VARIANT_ME25LS01_4Y10TD__ \ No newline at end of file diff --git a/variants/MakePython_nRF52840_eink/variant.h b/variants/MakePython_nRF52840_eink/variant.h index 16f803dd2..00c8dc199 100644 --- a/variants/MakePython_nRF52840_eink/variant.h +++ b/variants/MakePython_nRF52840_eink/variant.h @@ -25,8 +25,6 @@ extern "C" { #define NUM_ANALOG_INPUTS (6) #define NUM_ANALOG_OUTPUTS (0) -#define RADIOLIB_GODMODE - // LEDs #define PIN_LED1 (32 + 10) // LED P1.15 #define PIN_LED2 (-1) // diff --git a/variants/MakePython_nRF52840_oled/variant.h b/variants/MakePython_nRF52840_oled/variant.h index a2a5fa80a..28d941171 100644 --- a/variants/MakePython_nRF52840_oled/variant.h +++ b/variants/MakePython_nRF52840_oled/variant.h @@ -25,8 +25,6 @@ extern "C" { #define NUM_ANALOG_INPUTS (6) #define NUM_ANALOG_OUTPUTS (0) -#define RADIOLIB_GODMODE - // LEDs #define PIN_LED1 (32 + 10) // LED P1.15 #define PIN_LED2 (-1) // diff --git a/variants/chatter2/variant.h b/variants/chatter2/variant.h index b7f946970..5c27e2fb5 100644 --- a/variants/chatter2/variant.h +++ b/variants/chatter2/variant.h @@ -66,6 +66,7 @@ #define SCREEN_ROTATE #define SCREEN_TRANSITION_FRAMERATE 5 // fps #define DISPLAY_FORCE_SMALL_FONTS +#define TFT_BACKLIGHT_ON LOW // Battery @@ -121,4 +122,4 @@ // cannot serve any extra function even if requested to LORA_DIO3 value is never used in src (as we are not using RF95), so no // need to define, and DIO3_AS_TCXO_AT_1V8 is set so it cannot serve any extra function even if requested to (from 13.3.2.1 // DioxMask in SX1262 datasheet: Note that if DIO2 or DIO3 are used to control the RF Switch or the TCXO, the IRQ will not be -// generated even if it is mapped to the pins.) \ No newline at end of file +// generated even if it is mapped to the pins.) diff --git a/variants/diy/platformio.ini b/variants/diy/platformio.ini index adc10de44..f3c22b7a8 100644 --- a/variants/diy/platformio.ini +++ b/variants/diy/platformio.ini @@ -7,7 +7,6 @@ build_flags = ${esp32_base.build_flags} -D DIY_V1 -D EBYTE_E22 - -D OLED_RU -I variants/diy/v1 ; Meshtastic DIY v1.1 new schematic based on ESP32-WROOM-32 & SX1262/SX1268 modules @@ -19,7 +18,6 @@ build_flags = ${esp32_base.build_flags} -D DIY_V1 -D EBYTE_E22 - -D OLED_RU -I variants/diy/v1_1 ; Port to Disaster Radio's ESP32-v3 Dev Board @@ -52,7 +50,6 @@ board_level = extra build_flags = ${nrf52840_base.build_flags} -I variants/diy/nrf52_promicro_diy_xtal -D NRF52_PROMICRO_DIY - -D OLED_RU -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" build_src_filter = ${nrf52_base.build_src_filter} +<../variants/diy/nrf52_promicro_diy_xtal> lib_deps = @@ -68,9 +65,26 @@ board_level = extra build_flags = ${nrf52840_base.build_flags} -I variants/diy/nrf52_promicro_diy_tcxo -D NRF52_PROMICRO_DIY - -D OLED_RU -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" build_src_filter = ${nrf52_base.build_src_filter} +<../variants/diy/nrf52_promicro_diy_tcxo> lib_deps = ${nrf52840_base.lib_deps} -debug_tool = jlink \ No newline at end of file +debug_tool = jlink + +; NanoVHF T-Energy-S3 + E22(0)-xxxM - DIY +[env:t-energy-s3_e22] +extends = esp32s3_base +board = esp32-s3-devkitc-1 +board_level = extra +board_upload.flash_size = 16MB ;Specify the FLASH capacity as 16MB +board_build.arduino.memory_type = qio_opi ;Enable internal PSRAM +build_unflags = + ${esp32s3_base.build_unflags} + -D ARDUINO_USB_MODE=1 +build_flags = + ${esp32s3_base.build_flags} + -D EBYTE_ESP32_S3 + -D BOARD_HAS_PSRAM + -D ARDUINO_USB_MODE=0 + -D ARDUINO_USB_CDC_ON_BOOT=1 + -I variants/diy/t-energy-s3_e22 diff --git a/variants/diy/t-energy-s3_e22/variant.h b/variants/diy/t-energy-s3_e22/variant.h new file mode 100644 index 000000000..6933d7715 --- /dev/null +++ b/variants/diy/t-energy-s3_e22/variant.h @@ -0,0 +1,46 @@ +// NanoVHF T-Energy-S3 + E22(0)-xxxM - DIY +// https://github.com/NanoVHF/Meshtastic-DIY/tree/main/PCB/ESP-32-devkit_EBYTE-E22/Mesh-v1.06-TTGO-T18 + +// Battery +#define BATTERY_PIN 3 +#define ADC_MULTIPLIER 2.0 +#define ADC_CHANNEL ADC1_GPIO3_CHANNEL + +// Button on NanoVHF PCB +#define BUTTON_PIN 39 + +// I2C via connectors on NanoVHF PCB +#define I2C_SCL 2 +#define I2C_SDA 42 + +// Screen (disabled) +#define HAS_SCREEN 0 // Assume no screen present by default to prevent crash... + +// GPS via T-Energy-S3 onboard connector +#define HAS_GPS 1 +#define GPS_TX_PIN 43 +#define GPS_RX_PIN 44 + +// LoRa +#define USE_SX1262 // E22-900M30S, E22-900M22S, and E22-900MM22S (not E220!) use SX1262 +#define USE_SX1268 // E22-400M30S, E22-400M33S, E22-400M22S, and E22-400MM22S use SX1268 + +#define SX126X_MAX_POWER 22 // SX126xInterface.cpp defaults to 22 if not defined, but here we define it for good practice +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 // E22 series TCXO reference voltage is 1.8V + +#define SX126X_CS 5 // EBYTE module's NSS pin // FIXME: rename to SX126X_SS +#define SX126X_SCK 6 // EBYTE module's SCK pin +#define SX126X_MOSI 13 // EBYTE module's MOSI pin +#define SX126X_MISO 4 // EBYTE module's MISO pin +#define SX126X_RESET 1 // EBYTE module's NRST pin +#define SX126X_BUSY 48 // EBYTE module's BUSY pin +#define SX126X_DIO1 47 // EBYTE module's DIO1 pin + +#define SX126X_TXEN 10 // Schematic connects EBYTE module's TXEN pin to MCU +#define SX126X_RXEN 12 // Schematic connects EBYTE module's RXEN pin to MCU + +#define LORA_CS SX126X_CS // Compatibility with variant file configuration structure +#define LORA_SCK SX126X_SCK // Compatibility with variant file configuration structure +#define LORA_MOSI SX126X_MOSI // Compatibility with variant file configuration structure +#define LORA_MISO SX126X_MISO // Compatibility with variant file configuration structure +#define LORA_DIO1 SX126X_DIO1 // Compatibility with variant file configuration structure diff --git a/variants/heltec_mesh_node_t114/platformio.ini b/variants/heltec_mesh_node_t114/platformio.ini index 1009ecce5..1b06c7f5e 100644 --- a/variants/heltec_mesh_node_t114/platformio.ini +++ b/variants/heltec_mesh_node_t114/platformio.ini @@ -8,9 +8,10 @@ debug_tool = jlink build_flags = ${nrf52840_base.build_flags} -Ivariants/heltec_mesh_node_t114 -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" -DGPS_POWER_TOGGLE + -DHELTEC_T114 build_src_filter = ${nrf52_base.build_src_filter} +<../variants/heltec_mesh_node_t114> lib_deps = ${nrf52840_base.lib_deps} lewisxhe/PCF8563_Library@^1.0.1 - https://github.com/meshtastic/st7789#7181320e7ed05c7fb5fd2d32f14723bce6088b7b \ No newline at end of file + https://github.com/meshtastic/st7789#bd33ea58ddfe4a5e4a66d53300ccbd38d66ac21f \ No newline at end of file diff --git a/variants/heltec_mesh_node_t114/variant.cpp b/variants/heltec_mesh_node_t114/variant.cpp index cae079b74..85c9f4a72 100644 --- a/variants/heltec_mesh_node_t114/variant.cpp +++ b/variants/heltec_mesh_node_t114/variant.cpp @@ -32,13 +32,7 @@ const uint32_t g_ADigitalPinMap[] = { void initVariant() { - // LED1 & LED2 + // LED1 pinMode(PIN_LED1, OUTPUT); ledOff(PIN_LED1); - - pinMode(PIN_LED2, OUTPUT); - ledOff(PIN_LED2); - - pinMode(PIN_LED3, OUTPUT); - ledOff(PIN_LED3); } diff --git a/variants/heltec_mesh_node_t114/variant.h b/variants/heltec_mesh_node_t114/variant.h index e8c305990..426085a26 100644 --- a/variants/heltec_mesh_node_t114/variant.h +++ b/variants/heltec_mesh_node_t114/variant.h @@ -67,20 +67,17 @@ extern "C" { #define NUM_ANALOG_OUTPUTS (0) // LEDs -#define PIN_LED1 (32 + 3) // 13 red (confirmed on 1.0 board) -// Unused(by firmware) LEDs: -#define PIN_LED2 (1 + 1) // 14 blue -#define PIN_LED3 (1 + 11) // 15 green - -#define LED_RED PIN_LED3 -#define LED_BLUE PIN_LED1 -#define LED_GREEN PIN_LED2 - -#define LED_BUILTIN LED_BLUE -#define LED_CONN PIN_GREEN - +#define PIN_LED1 (32 + 3) // green (confirmed on 1.0 board) +#define LED_BLUE PIN_LED1 // fake for bluefruit library +#define LED_GREEN PIN_LED1 +#define LED_BUILTIN LED_GREEN #define LED_STATE_ON 0 // State when LED is lit +#define HAS_NEOPIXEL // Enable the use of neopixels +#define NEOPIXEL_COUNT 2 // How many neopixels are connected +#define NEOPIXEL_DATA 14 // gpio pin used to send data to the neopixels +#define NEOPIXEL_TYPE (NEO_GRB + NEO_KHZ800) // type of neopixels in use + /* * Buttons */ @@ -95,13 +92,22 @@ No longer populated on PCB #define PIN_SERIAL2_TX (0 + 10) // #define PIN_SERIAL2_EN (0 + 17) -/** - Wire Interfaces - */ -#define WIRE_INTERFACES_COUNT 1 +/* + * I2C + */ -#define PIN_WIRE_SDA (26) -#define PIN_WIRE_SCL (27) +#define WIRE_INTERFACES_COUNT 2 + +// I2C bus 0 +// Routed to footprint for PCF8563TS RTC +// Not populated on T114 V1, maybe in future? +#define PIN_WIRE_SDA (0 + 26) // P0.26 +#define PIN_WIRE_SCL (0 + 27) // P0.27 + +// I2C bus 1 +// Available on header pins, for general use +#define PIN_WIRE1_SDA (0 + 16) // P0.16 +#define PIN_WIRE1_SCL (0 + 13) // P0.13 // QSPI Pins #define PIN_QSPI_SCK (32 + 14) @@ -148,8 +154,11 @@ No longer populated on PCB // #define PIN_GPS_RESET (32 + 6) // An output to reset L76K GPS. As per datasheet, low for > 100ms will reset the L76K #define GPS_RESET_MODE LOW -#define PIN_GPS_EN (21) -#define GPS_EN_ACTIVE HIGH +// #define PIN_GPS_EN (21) +#define VEXT_ENABLE (0 + 21) +#define PERIPHERAL_WARMUP_MS 1000 // Make sure I2C QuickLink has stable power before continuing +#define VEXT_ON_VALUE HIGH +// #define GPS_EN_ACTIVE HIGH #define PIN_GPS_STANDBY (32 + 2) // An output to wake GPS, low means allow sleep, high means force wake #define PIN_GPS_PPS (32 + 4) // Seems to be missing on this new board @@ -206,4 +215,4 @@ No longer populated on PCB * Arduino objects - C++ only *----------------------------------------------------------------------------*/ -#endif \ No newline at end of file +#endif diff --git a/variants/heltec_vision_master_t190/platformio.ini b/variants/heltec_vision_master_t190/platformio.ini index fd0001439..0c504d62b 100644 --- a/variants/heltec_vision_master_t190/platformio.ini +++ b/variants/heltec_vision_master_t190/platformio.ini @@ -9,5 +9,5 @@ build_flags = lib_deps = ${esp32s3_base.lib_deps} lewisxhe/PCF8563_Library@^1.0.1 - https://github.com/meshtastic/st7789#7181320e7ed05c7fb5fd2d32f14723bce6088b7b + https://github.com/meshtastic/st7789#bd33ea58ddfe4a5e4a66d53300ccbd38d66ac21f upload_speed = 921600 \ No newline at end of file diff --git a/variants/heltec_wireless_bridge/platformio.ini b/variants/heltec_wireless_bridge/platformio.ini new file mode 100644 index 000000000..45c3aba74 --- /dev/null +++ b/variants/heltec_wireless_bridge/platformio.ini @@ -0,0 +1,6 @@ +[env:heltec-wireless-bridge] +;build_type = debug ; to make it possible to step through our jtag debugger +extends = esp32_base +board = heltec_wifi_lora_32 +build_flags = + ${esp32_base.build_flags} -D HELTEC_WIRELESS_BRIDGE -I variants/heltec_wireless_bridge \ No newline at end of file diff --git a/variants/heltec_wireless_bridge/variant.h b/variants/heltec_wireless_bridge/variant.h new file mode 100644 index 000000000..7c4f41660 --- /dev/null +++ b/variants/heltec_wireless_bridge/variant.h @@ -0,0 +1,29 @@ +// the default ESP32 Pin of 15 is the Oled SCL, set to 36 and 37 and works fine. +// Tested on Neo6m module. +#undef GPS_RX_PIN +#undef GPS_TX_PIN +#define GPS_RX_PIN 36 +#define GPS_TX_PIN 33 + +#ifndef USE_JTAG // gpio15 is TDO for JTAG, so no I2C on this board while doing jtag +#define I2C_SDA 4 // I2C pins for this board +#define I2C_SCL 15 +#endif + +#define LED_PIN 25 // If defined we will blink this LED +#define BUTTON_PIN 0 // If defined, this will be used for user button presses + +#define USE_RF95 +#define LORA_DIO0 26 // a No connect on the SX1262 module +#ifndef USE_JTAG +#define LORA_RESET 14 +#endif +#define LORA_DIO1 35 +#define LORA_DIO2 34 // Not really used + +// ratio of voltage divider = 3.20 (R1=100k, R2=220k) +#define ADC_MULTIPLIER 3.2 + +#define BATTERY_PIN 13 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage +#define ADC_CHANNEL ADC2_GPIO13_CHANNEL +#define BAT_MEASURE_ADC_UNIT 2 \ No newline at end of file diff --git a/variants/lora_isp4520/platformio.ini b/variants/lora_isp4520/platformio.ini deleted file mode 100644 index 9d6563515..000000000 --- a/variants/lora_isp4520/platformio.ini +++ /dev/null @@ -1,18 +0,0 @@ -[env:lora_isp4520] -extends = nrf52_base -board = lora_isp4520 -board_level = extra - -# add our variants files to the include and src paths -build_flags = ${nrf52_base.build_flags} -Ivariants/lora_isp4520 - -# No screen and GPS on the board. We still need RTC.cpp for the RTC clock. -build_src_filter = ${nrf52_base.build_src_filter} +<../variants/lora_isp4520> - - + + -lib_ignore = ${nrf52_base.lib_ignore} - ESP8266_SSD1306 - SparkFun Ublox Arduino Library - AXP202X_Library - TinyGPSPlus - -upload_protocol = jlink -monitor_port = /dev/ttyUSB0 \ No newline at end of file diff --git a/variants/lora_isp4520/variant.cpp b/variants/lora_isp4520/variant.cpp deleted file mode 100644 index 41b31384a..000000000 --- a/variants/lora_isp4520/variant.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - Copyright (c) 2014-2015 Arduino LLC. All right reserved. - Copyright (c) 2016 Sandeep Mistry All right reserved. - Copyright (c) 2018, Adafruit Industries (adafruit.com) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#include "variant.h" -#include "nrf.h" -#include "wiring_constants.h" -#include "wiring_digital.h" - -const uint32_t g_ADigitalPinMap[] = { - 25, // D0 SPI_MISO - 24, // D1 SPI_NSS - 23, // D2 SPI_SCK - 4, // D3 VBAT - 11, // D4 DIO1 - 27, // D5 BUSY - 19, // D6 NRESET - 12, // D7 BUTTON2 - 22, // D8 BUTTON3 - 26, // D9 SPI_MOSI - 31, // D10 UART_RX - 2, // D11 UART_TX - 10, // D12 LED1 GREEN - 17, // D13 LED2 RED - 9, // D14 BUZZER - 7, // D15 BUTTON1 -}; - -#include -void initVariant() -{ - for (int i : {PIN_LED1, PIN_LED2}) { - pinMode(i, OUTPUT); - ledOff(i); - } -} diff --git a/variants/lora_isp4520/variant.h b/variants/lora_isp4520/variant.h deleted file mode 100644 index 30b8fc169..000000000 --- a/variants/lora_isp4520/variant.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - Copyright (c) 2014-2015 Arduino LLC. All right reserved. - Copyright (c) 2016 Sandeep Mistry All right reserved. - Copyright (c) 2018, Adafruit Industries (adafruit.com) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef _VARIANT_LORA_ISP4520_ -#define _VARIANT_LORA_ISP4520_ - -#define USE_SEGGER -/*---------------------------------------------------------------------------- - * Headers - *----------------------------------------------------------------------------*/ - -#include "WVariant.h" - -#define USE_LFXO - -// #define USE_SEGGER - -// Number of pins defined in PinDescription array -#define PINS_COUNT (16) -#define NUM_DIGITAL_PINS (16) -#define NUM_ANALOG_INPUTS (1) -#define NUM_ANALOG_OUTPUTS (1) - -/* - * SPI Interfaces - */ -#define SPI_INTERFACES_COUNT 1 - -// These are in arduino pin numbers, -// translation in g_ADigitalPinMap in variants.cpp -#define PIN_SPI_MISO (0) -#define PIN_SPI_MOSI (9) -#define PIN_SPI_SCK (2) - -/* - * Wire Interfaces (I2C) - */ -#define WIRE_INTERFACES_COUNT 0 - -// GPIOs the SX1262 is connected -#define USE_SX1262 -#define SX126X_CS 1 // aka SPI_NSS -#define SX126X_DIO1 (4) -#define SX126X_BUSY (5) -#define SX126X_RESET (6) - -/* - * Serial interfaces - */ -#define PIN_SERIAL_RX (10) -#define PIN_SERIAL_TX (11) -// LEDs -#define PIN_LED1 (12) -#define PIN_LED2 (13) -#define PIN_BUZZER (14) - -#define LED_BUILTIN PIN_LED1 -#define LED_CONN PIN_LED2 - -#define LED_RED PIN_LED1 -#define LED_BLUE PIN_LED2 - -#define LED_STATE_ON 1 // State when LED is litted - -/* - * Buttons - */ -#define PIN_BUTTON1 (15) -#define PIN_BUTTON2 (7) -#define PIN_BUTTON3 (8) - -// ADC pin and voltage divider -#define BATTERY_PIN 3 -#define ADC_MULTIPLIER 1.436 - -#define SX126X_DIO2_AS_RF_SWITCH -#define SX126X_DIO3_TCXO_VOLTAGE 1.8 // Not really an E22 but this board clones using DIO3 for tcxo control - -#endif diff --git a/variants/lora_relay_v1/platformio.ini b/variants/lora_relay_v1/platformio.ini deleted file mode 100644 index 435d256c5..000000000 --- a/variants/lora_relay_v1/platformio.ini +++ /dev/null @@ -1,24 +0,0 @@ -; The https://github.com/BigCorvus/SX1262-LoRa-BLE-Relay board by @BigCorvus -[env:lora-relay-v1] -extends = nrf52840_base -board = lora-relay-v1 -board_level = extra -# add our variants files to the include and src paths -# define build flags for the TFT_eSPI library -build_flags = ${nrf52840_base.build_flags} -Ivariants/lora_relay_v1 - -DUSER_SETUP_LOADED - -DTFT_WIDTH=80 - -DTFT_HEIGHT=160 - -DST7735_GREENTAB160x80 - -DST7735_DRIVER - -DTFT_CS=ST7735_CS - -DTFT_DC=ST7735_RS - -DTFT_RST=ST7735_RESET - -DSPI_FREQUENCY=27000000 - -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" -build_src_filter = ${nrf52_base.build_src_filter} +<../variants/lora_relay_v1> -lib_deps = - ${nrf52840_base.lib_deps} - sparkfun/SparkFun BQ27441 LiPo Fuel Gauge Arduino Library@^1.1.0 - bodmer/TFT_eSPI@^2.4.76 - adafruit/Adafruit NeoPixel @ ^1.12.0 \ No newline at end of file diff --git a/variants/lora_relay_v1/variant.cpp b/variants/lora_relay_v1/variant.cpp deleted file mode 100644 index 891c8bb29..000000000 --- a/variants/lora_relay_v1/variant.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - Copyright (c) 2014-2015 Arduino LLC. All right reserved. - Copyright (c) 2016 Sandeep Mistry All right reserved. - Copyright (c) 2018, Adafruit Industries (adafruit.com) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#include "variant.h" -#include "nrf.h" -#include "wiring_constants.h" -#include "wiring_digital.h" - -const uint32_t g_ADigitalPinMap[] = { - // D0 .. D13 - 25, // D0 is P0.25 (UART TX) - 24, // D1 is P0.24 (UART RX - 10, // D2 is P0.10 (NFC2) - 47, // D3 is P1.15 (LED1) - 42, // D4 is P1.10 (LED2) - 40, // D5 is P1.08 - 7, // D6 is P0.07 - 34, // D7 is P1.02 (Button) - 16, // D8 is P0.16 (NeoPixel) - 26, // D9 is P0.26 D_RS (IPS data/command control) - 27, // D10 is P0.27 - 6, // D11 is P0.06 D_RES (IPS display reset) - 8, // D12 is P0.08 D_CS (IPS display chip select) - 41, // D13 is P1.09 BLT (IPS display backlight) - 4, // D14 is P0.04 SX1262 RXEN - 5, // D15 is P0.05 BOOST_EN (5V buck converter enable for the the radio power) - - // D14 .. D21 (aka A0 .. A7) - 30, // D16 is P0.30 (A0) - 28, // D17 is P0.28 (A1) - 2, // D18 is P0.02 (A2) - 3, // D19 is P0.03 (A3) - 29, // D20 is P0.29 (A4, Battery) - 31, // D21 is P0.31 (A5, ARef) - - // D22 .. D23 (aka I2C pins) - 12, // D22 is P0.12 (SDA) - 11, // D23 is P0.11 (SCL) - - // D24 .. D26 (aka SPI pins) - 15, // D24 is P0.15 (SPI MISO) - 13, // D25 is P0.13 (SPI MOSI) - 14, // D26 is P0.14 (SPI SCK ) - - // QSPI pins (not exposed via any header / test point) - // 19, // P0.19 (QSPI CLK) - // 20, // P0.20 (QSPI CS) - // 17, // P0.17 (QSPI Data 0) - // 22, // P0.22 (QSPI Data 1) - // 23, // P0.23 (QSPI Data 2) - // 21, // P0.21 (QSPI Data 3) - - // The remaining NFC pin - 9, // D27 P0.09 (NFC1, exposed only via test point on bottom of board) - - // The following pins were never listed as they were considered unusable - // 0, // P0.00 is XL1 (attached to 32.768kHz crystal) Never expose as GPIOs - // 1, // P0.01 is XL2 (attached to 32.768kHz crystal) - 18, // D28 P0.18 is RESET (attached to switch) - // 32, // P1.00 is SWO (attached to debug header) - - // D29-D43 - 27, // D29 P0.27 E22-SX1262 DIO1 - 28, // D30 P0.28 E22-SX1262 DIO2 - 30, // D31 P0.30 E22-SX1262 TXEN - 35, // D32 P1.03 E22-SX1262 NSS - 32 + 8, // D33 P1.08 E22-SX1262 BUSY - 32 + 12, // D34 P1.12 E22-SX1262 RESET - 32 + 1, // P1.01 BTN_UP - 32 + 2, // P1.02 SWITCH - 32 + 14, // D37 P1.14 is not connected per schematic - 36, // P1.04 is not connected per schematic - 37, // P1.05 is not connected per schematic - 38, // P1.06 is not connected per schematic - 39, // P1.07 is not connected per schematic - 43, // P1.11 is not connected per schematic - 45, // P1.13 is not connected per schematic -}; - -void initVariant() -{ - // LED1 & LED2 - pinMode(PIN_LED1, OUTPUT); - ledOff(PIN_LED1); - - pinMode(PIN_LED2, OUTPUT); - ledOff(PIN_LED2); -} diff --git a/variants/lora_relay_v1/variant.h b/variants/lora_relay_v1/variant.h deleted file mode 100644 index 6efd711c6..000000000 --- a/variants/lora_relay_v1/variant.h +++ /dev/null @@ -1,161 +0,0 @@ -/* - Copyright (c) 2014-2015 Arduino LLC. All right reserved. - Copyright (c) 2016 Sandeep Mistry All right reserved. - Copyright (c) 2018, Adafruit Industries (adafruit.com) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef _VARIANT_LORA_RELAY_V1_ -#define _VARIANT_LORA_RELAY_V1_ - -/** Master clock frequency */ -#define VARIANT_MCK (64000000ul) - -#define USE_LFXO // Board uses 32khz crystal for LF -// define USE_LFRC // Board uses RC for LF - -/*---------------------------------------------------------------------------- - * Headers - *----------------------------------------------------------------------------*/ - -#include "WVariant.h" - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - -// Number of pins defined in PinDescription array -#define PINS_COUNT (43) -#define NUM_DIGITAL_PINS (43) -#define NUM_ANALOG_INPUTS (6) // A6 is used for battery, A7 is analog reference -#define NUM_ANALOG_OUTPUTS (0) - -// LEDs -#define PIN_LED1 (3) -#define PIN_LED2 (4) -// #define PIN_NEOPIXEL (8) -#define HAS_NEOPIXEL // Enable the use of neopixels -#define NEOPIXEL_COUNT 1 // How many neopixels are connected -#define NEOPIXEL_DATA 8 // gpio pin used to send data to the neopixels -#define NEOPIXEL_TYPE (NEO_GRB + NEO_KHZ800) // type of neopixels in use - -#define LED_BUILTIN PIN_LED1 -#define LED_CONN PIN_LED2 - -#define LED_RED PIN_LED1 -#define LED_BLUE PIN_LED2 - -#define LED_STATE_ON 1 // State when LED is litted - -/* - * Buttons - */ -#define PIN_BUTTON1 (7) - -/* - * Analog pins - */ -#define PIN_A0 (16) -#define PIN_A1 (17) -#define PIN_A2 (18) -#define PIN_A3 (19) -#define PIN_A4 (20) -#define PIN_A5 (21) - -static const uint8_t A0 = PIN_A0; -static const uint8_t A1 = PIN_A1; -static const uint8_t A2 = PIN_A2; -static const uint8_t A3 = PIN_A3; -static const uint8_t A4 = PIN_A4; -static const uint8_t A5 = PIN_A5; -#define ADC_RESOLUTION 14 - -// Other pins -#define PIN_AREF PIN_A5 -#define PIN_VBAT PIN_A4 -#define BATTERY_PIN PIN_VBAT -#define PIN_NFC1 (33) -#define PIN_NFC2 (2) -#define PIN_PIEZO (37) -static const uint8_t AREF = PIN_AREF; - -/* - * Serial interfaces - */ -#define PIN_SERIAL1_RX (1) -#define PIN_SERIAL1_TX (0) - -/* - * SPI Interfaces - */ -#define SPI_INTERFACES_COUNT 1 - -#define PIN_SPI_MISO (24) -#define PIN_SPI_MOSI (25) -#define PIN_SPI_SCK (26) - -static const uint8_t SS = (5); -static const uint8_t MOSI = PIN_SPI_MOSI; -static const uint8_t MISO = PIN_SPI_MISO; -static const uint8_t SCK = PIN_SPI_SCK; - -/* - * Wire Interfaces - */ -#define WIRE_INTERFACES_COUNT 1 - -#define PIN_WIRE_SDA (22) -#define PIN_WIRE_SCL (23) - -// I2C device addresses -#define I2C_ADDR_BQ27441 0x55 // Battery gauge - -// SX1262 declaration -#define USE_SX1262 - -// CUSTOM GPIOs the SX1262 -#define SX126X_CS (32) - -// If you would prefer to get console debug output over the JTAG ICE connection rather than the CDC-ACM USB serial device, just -// define this. #define USE_SEGGER - -#define SX126X_DIO1 (29) -#define SX1262_DIO2 (30) -#define SX126X_BUSY (33) // Supposed to be P0.18 but because of reworks, now on P0.31 (18) -#define SX126X_RESET (34) -// #define SX126X_ANT_SW (32 + 10) -#define SX126X_RXEN (14) -#define SX126X_TXEN (31) -#define SX126X_POWER_EN \ - (15) // FIXME, see warning hre https://github.com/BigCorvus/SX1262-LoRa-BLE-Relay/blob/master/LORA_RELAY_NRF52840.ino -// Indicates this SX1262 is inside of an ebyte E22 module and special config should be done for that -#define SX126X_DIO3_TCXO_VOLTAGE 1.8 - -#define ST7735_RESET (11) // Output -#define ST7735_CS (12) -#define TFT_BL (13) -#define ST7735_RS (9) - -// #define LORA_DISABLE_SENDING // The board can brownout during lora TX if you don't have a battery connected. Disable sending -// to allow USB power only based debugging - -#ifdef __cplusplus -} -#endif - -/*---------------------------------------------------------------------------- - * Arduino objects - C++ only - *----------------------------------------------------------------------------*/ - -#endif \ No newline at end of file diff --git a/variants/lora_relay_v2/platformio.ini b/variants/lora_relay_v2/platformio.ini deleted file mode 100644 index 3598466d5..000000000 --- a/variants/lora_relay_v2/platformio.ini +++ /dev/null @@ -1,26 +0,0 @@ -; The https://github.com/BigCorvus/LoRa-BLE-Relay-v2 board by @BigCorvus -[env:lora-relay-v2] -extends = nrf52840_base -board = lora-relay-v2 -board_level = extra -# add our variants files to the include and src paths -# define build flags for the TFT_eSPI library -build_flags = ${nrf52840_base.build_flags} -Ivariants/lora_relay_v2 - -DUSER_SETUP_LOADED - -DTFT_WIDTH=80 - -DTFT_HEIGHT=160 - -DST7735_GREENTAB160x80 - -DST7735_DRIVER - -DTFT_CS=ST7735_CS - -DTFT_DC=ST7735_RS - -DTFT_RST=ST7735_RESET - -DSPI_FREQUENCY=27000000 - -DTFT_WR=ST7735_SDA - -DTFT_SCLK=ST7735_SCK - -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" -build_src_filter = ${nrf52_base.build_src_filter} +<../variants/lora_relay_v2> -lib_deps = - ${nrf52840_base.lib_deps} - sparkfun/SparkFun BQ27441 LiPo Fuel Gauge Arduino Library@^1.1.0 - bodmer/TFT_eSPI@^2.4.76 - adafruit/Adafruit NeoPixel @ ^1.12.0 \ No newline at end of file diff --git a/variants/lora_relay_v2/variant.cpp b/variants/lora_relay_v2/variant.cpp deleted file mode 100644 index 23d648873..000000000 --- a/variants/lora_relay_v2/variant.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - Copyright (c) 2014-2015 Arduino LLC. All right reserved. - Copyright (c) 2016 Sandeep Mistry All right reserved. - Copyright (c) 2018, Adafruit Industries (adafruit.com) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#include "variant.h" -#include "nrf.h" -#include "wiring_constants.h" -#include "wiring_digital.h" - -const uint32_t g_ADigitalPinMap[] = { - // D0 .. D13 - 25, // D0 is P0.25 (UART TX) - 24, // D1 is P0.24 (UART RX - 10, // D2 is P0.10 (NFC2) - 47, // D3 is P1.15 (LED1) - (32 + 10), // D4 is P1.10 (LED2) - 40, // D5 is P1.08 - 7, // D6 is P0.07 - 34, // D7 is P1.02 (Switch) - 16, // D8 is P0.16 (NeoPixel) - 26, // D9 is P0.26 D_RS (IPS data/command control) - 27, // D10 is P0.27 - 6, // D11 is P0.06 D_RES (IPS display reset) - 8, // D12 is P0.08 D_CS (IPS display chip select) - 41, // D13 is P0.23 BLT (IPS display backlight) - 4, // D14 is P0.04 SX1262 RXEN - 5, // D15 is P0.05 BOOST_EN (5V buck converter enable for the the radio power) - - // D14 .. D21 (aka A0 .. A7) - 30, // D16 is P0.30 (A0) - 28, // D17 is P0.28 (A1) - 2, // D18 is P0.02 (A2) - 3, // D19 is P0.03 (A3) - 29, // D20 is P0.29 (A4, Battery) - 31, // D21 is P0.31 (A5, ARef) - - // D22 .. D23 (aka I2C pins) - 12, // D22 is P0.12 (SDA) - 11, // D23 is P0.11 (SCL) - - // D24 .. D26 (aka SPI pins) - 15, // D24 is P0.15 (SPI MISO) - 13, // D25 is P0.13 (SPI MOSI) - 14, // D26 is P0.14 (SPI SCK ) - - // QSPI pins (not exposed via any header / test point) - // 19, // P0.19 (QSPI CLK) - // 20, // P0.20 (QSPI CS) - // 17, // P0.17 (QSPI Data 0) - // 22, // P0.22 (QSPI Data 1) - // 23, // P0.23 (QSPI Data 2) - // 21, // P0.21 (QSPI Data 3) - - // The remaining NFC pin - 9, // D27 P0.09 (NFC1, exposed only via test point on bottom of board) - - // The following pins were never listed as they were considered unusable - // 0, // P0.00 is XL1 (attached to 32.768kHz crystal) Never expose as GPIOs - // 1, // P0.01 is XL2 (attached to 32.768kHz crystal) - 18, // D28 P0.18 is RESET (attached to switch) - // 32, // P1.00 is SWO (attached to debug header) - - // D29-D43 - 32 + 12, // D29 P0.27 E22-SX1262 DIO1 - 28, // D30 P0.28 E22-SX1262 DIO2 - 30, // D31 P0.30 E22-SX1262 TXEN - 35, // D32 P1.03 E22-SX1262 NSS - 32 + 8, // D33 P1.08 E22-SX1262 BUSY - 27, // D34 P0.27 E22-SX1262 RESET - 32 + 1, // D35 P1.01 BTN_UP - 32, // D36 P1.0 GPS power - 21, // D37 P0.21 disp_clk - 36, // P1.04 BTN_OK - 37, // D39 P0.19 disp_SDA - 38, // D40 P1.06 BUZZER - 39, // P1.07 is not connected per schematic - 43, // P1.11 is not connected per schematic - 45, // P1.13 is not connected per schematic -}; - -void initVariant() -{ - // LED1 & LED2 - pinMode(PIN_LED1, OUTPUT); - ledOff(PIN_LED1); - - pinMode(PIN_LED2, OUTPUT); - ledOff(PIN_LED2); -} diff --git a/variants/lora_relay_v2/variant.h b/variants/lora_relay_v2/variant.h deleted file mode 100644 index f18f81034..000000000 --- a/variants/lora_relay_v2/variant.h +++ /dev/null @@ -1,188 +0,0 @@ -/* - Copyright (c) 2014-2015 Arduino LLC. All right reserved. - Copyright (c) 2016 Sandeep Mistry All right reserved. - Copyright (c) 2018, Adafruit Industries (adafruit.com) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef _VARIANT_LORA_RELAY_V1_ -#define _VARIANT_LORA_RELAY_V1_ - -/** Master clock frequency */ -#define VARIANT_MCK (64000000ul) - -#define USE_LFXO // Board uses 32khz crystal for LF -// define USE_LFRC // Board uses RC for LF - -/* -kevinh todo - -ok leds -ok buttons -ok gps power -ok gps signal -ok? lcd -ok buzzer -serial flash -ok lora (inc boost en) - -mention dat1 and dat2 on sd card -use hardware spi controller for lcd - not bitbang - -*/ - -/*---------------------------------------------------------------------------- - * Headers - *----------------------------------------------------------------------------*/ - -#include "WVariant.h" - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - -// Number of pins defined in PinDescription array -#define PINS_COUNT (43) -#define NUM_DIGITAL_PINS (43) -#define NUM_ANALOG_INPUTS (6) // A6 is used for battery, A7 is analog reference -#define NUM_ANALOG_OUTPUTS (0) - -// LEDs -#define PIN_LED1 (3) -#define PIN_LED2 (4) -// #define PIN_NEOPIXEL (8) -#define HAS_NEOPIXEL // Enable the use of neopixels -#define NEOPIXEL_COUNT 1 // How many neopixels are connected -#define NEOPIXEL_DATA 8 // gpio pin used to send data to the neopixels -#define NEOPIXEL_TYPE (NEO_GRB + NEO_KHZ800) // type of neopixels in use - -#define PIN_BUZZER (40) - -#define LED_BUILTIN PIN_LED1 -#define LED_CONN PIN_LED2 - -#define LED_RED PIN_LED1 -#define LED_BLUE PIN_LED2 - -#define LED_STATE_ON 1 // State when LED is litted - -/* - * Buttons - */ -#define PIN_BUTTON1 (7) -#define PIN_BUTTON2 (35) -#define PIN_BUTTON3 (37) - -/* - * Analog pins - */ -#define PIN_A0 (16) -#define PIN_A1 (17) -#define PIN_A2 (18) -#define PIN_A3 (19) -#define PIN_A4 (20) -#define PIN_A5 (21) - -static const uint8_t A0 = PIN_A0; -static const uint8_t A1 = PIN_A1; -static const uint8_t A2 = PIN_A2; -static const uint8_t A3 = PIN_A3; -static const uint8_t A4 = PIN_A4; -static const uint8_t A5 = PIN_A5; -#define ADC_RESOLUTION 14 - -// Other pins -#define PIN_AREF PIN_A5 -#define PIN_VBAT PIN_A4 -#define BATTERY_PIN PIN_VBAT -#define PIN_NFC1 (33) -#define PIN_NFC2 (2) -#define PIN_PIEZO (37) -static const uint8_t AREF = PIN_AREF; - -/* - * Serial interfaces - */ -#define PIN_SERIAL1_RX (1) -#define PIN_SERIAL1_TX (0) - -/* - * SPI Interfaces - */ -#define SPI_INTERFACES_COUNT 1 - -#define PIN_SPI_MISO (24) -#define PIN_SPI_MOSI (25) -#define PIN_SPI_SCK (26) - -static const uint8_t SS = (5); -static const uint8_t MOSI = PIN_SPI_MOSI; -static const uint8_t MISO = PIN_SPI_MISO; -static const uint8_t SCK = PIN_SPI_SCK; - -/* - * Wire Interfaces - */ -#define WIRE_INTERFACES_COUNT 1 - -#define PIN_WIRE_SDA (22) -#define PIN_WIRE_SCL (23) - -// I2C device addresses -#define I2C_ADDR_BQ27441 0x55 // Battery gauge - -// SX1262 declaration -#define USE_SX1262 - -// CUSTOM GPIOs the SX1262 -#define SX126X_CS (32) - -// If you would prefer to get console debug output over the JTAG ICE connection rather than the CDC-ACM USB serial device, just -// define this. #define USE_SEGGER - -#define SX126X_DIO1 (29) -#define SX1262_DIO2 (30) -#define SX126X_BUSY (33) // Supposed to be P0.18 but because of reworks, now on P0.31 (18) -#define SX126X_RESET (34) -// #define SX126X_ANT_SW (32 + 10) -#define SX126X_RXEN (14) -#define SX126X_TXEN (31) -#define SX126X_POWER_EN \ - (15) // FIXME, see warning hre https://github.com/BigCorvus/SX1262-LoRa-BLE-Relay/blob/master/LORA_RELAY_NRF52840.ino -// Indicates this SX1262 is inside of an ebyte E22 module and special config should be done for that -#define SX126X_DIO3_TCXO_VOLTAGE 1.8 - -// ST7565 SPI -#define ST7735_RESET (11) // Output -#define ST7735_CS (12) -#define TFT_BL (13) -#define ST7735_RS (9) -#define ST7735_SDA (39) // actually spi MOSI -#define ST7735_SCK (37) // actually spi clk - -#define PIN_GPS_EN 36 // Just kill GPS power when we want it to sleep? FIXME -#define GPS_EN_ACTIVE 0 // GPS Power output is active low - -// #define LORA_DISABLE_SENDING // The board can brownout during lora TX if you don't have a battery connected. Disable sending -// to allow USB power only based debugging - -#ifdef __cplusplus -} -#endif - -/*---------------------------------------------------------------------------- - * Arduino objects - C++ only - *----------------------------------------------------------------------------*/ - -#endif \ No newline at end of file diff --git a/variants/monteops_hw1/platformio.ini b/variants/monteops_hw1/platformio.ini index 551419abc..4b42dce39 100644 --- a/variants/monteops_hw1/platformio.ini +++ b/variants/monteops_hw1/platformio.ini @@ -1,5 +1,6 @@ ; MonteOps M.Node/M.Backbone/M.Eagle hardware based on hardware variant #1 (RAK4630 based) [env:monteops_hw1] +board_level = extra extends = nrf52840_base board = wiscore_rak4631 build_flags = ${nrf52840_base.build_flags} -Ivariants/monteops_hw1 -D MONTEOPS_HW1 diff --git a/variants/pca10056-rc-clock/platformio.ini b/variants/pca10056-rc-clock/platformio.ini deleted file mode 100644 index f8cff4d73..000000000 --- a/variants/pca10056-rc-clock/platformio.ini +++ /dev/null @@ -1,9 +0,0 @@ -; The NRF52840-dk development board, but @geeksville's board - which has a busted oscilliator -[env:nrf52840dk-geeksville] -board_level = extra -extends = nrf52840_base -board = nrf52840_dk_modified -# add our variants files to the include and src paths -build_flags = ${nrf52_base.build_flags} -Ivariants/pca10056-rc-clock - -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" -build_src_filter = ${nrf52_base.build_src_filter} +<../variants/pca10056-rc-clock> \ No newline at end of file diff --git a/variants/pca10056-rc-clock/variant.h b/variants/pca10056-rc-clock/variant.h deleted file mode 100644 index 032e1de2b..000000000 --- a/variants/pca10056-rc-clock/variant.h +++ /dev/null @@ -1,162 +0,0 @@ -/* - Copyright (c) 2014-2015 Arduino LLC. All right reserved. - Copyright (c) 2016 Sandeep Mistry All right reserved. - Copyright (c) 2018, Adafruit Industries (adafruit.com) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef _VARIANT_PCA10056_ -#define _VARIANT_PCA10056_ - -/** Master clock frequency */ -#define VARIANT_MCK (64000000ul) - -// This file is the same as the standard pac10056 variant, except that @geeksville broke the xtal on his devboard so -// he has to use a RC clock. - -// #define USE_LFXO // Board uses 32khz crystal for LF -#define USE_LFRC // Board uses RC for LF - -/*---------------------------------------------------------------------------- - * Headers - *----------------------------------------------------------------------------*/ - -#include "WVariant.h" - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - -// Number of pins defined in PinDescription array -#define PINS_COUNT (48) -#define NUM_DIGITAL_PINS (48) -#define NUM_ANALOG_INPUTS (6) -#define NUM_ANALOG_OUTPUTS (0) - -// LEDs -#define PIN_LED1 (13) -#define PIN_LED2 (14) - -#define LED_BUILTIN PIN_LED1 -#define LED_CONN PIN_LED2 - -#define LED_RED PIN_LED1 -#define LED_BLUE PIN_LED2 - -#define LED_STATE_ON 0 // State when LED is litted - -/* - * Buttons - */ -#define PIN_BUTTON1 11 -#define PIN_BUTTON2 12 -#define PIN_BUTTON3 24 -#define PIN_BUTTON4 25 - -/* - * Analog pins - */ -#define PIN_A0 (3) -#define PIN_A1 (4) -#define PIN_A2 (28) -#define PIN_A3 (29) -#define PIN_A4 (30) -#define PIN_A5 (31) -#define PIN_A6 (0xff) -#define PIN_A7 (0xff) - -static const uint8_t A0 = PIN_A0; -static const uint8_t A1 = PIN_A1; -static const uint8_t A2 = PIN_A2; -static const uint8_t A3 = PIN_A3; -static const uint8_t A4 = PIN_A4; -static const uint8_t A5 = PIN_A5; -static const uint8_t A6 = PIN_A6; -static const uint8_t A7 = PIN_A7; -#define ADC_RESOLUTION 14 - -// Other pins -#define PIN_AREF (2) -#define PIN_NFC1 (9) -#define PIN_NFC2 (10) - -static const uint8_t AREF = PIN_AREF; - -/* - * Serial interfaces - */ - -// Arduino Header D0, D1 -#define PIN_SERIAL1_RX (33) // P1.01 -#define PIN_SERIAL1_TX (34) // P1.02 - -// Connected to Jlink CDC -#define PIN_SERIAL2_RX (8) -#define PIN_SERIAL2_TX (6) - -/* - * SPI Interfaces - */ -#define SPI_INTERFACES_COUNT 1 - -#define PIN_SPI_MISO (46) -#define PIN_SPI_MOSI (45) -#define PIN_SPI_SCK (47) - -static const uint8_t SS = 44; -static const uint8_t MOSI = PIN_SPI_MOSI; -static const uint8_t MISO = PIN_SPI_MISO; -static const uint8_t SCK = PIN_SPI_SCK; - -/* - * Wire Interfaces - */ -#define WIRE_INTERFACES_COUNT 1 - -#define PIN_WIRE_SDA (26) -#define PIN_WIRE_SCL (27) - -// QSPI Pins -#define PIN_QSPI_SCK 19 -#define PIN_QSPI_CS 17 -#define PIN_QSPI_IO0 20 -#define PIN_QSPI_IO1 21 -#define PIN_QSPI_IO2 22 -#define PIN_QSPI_IO3 23 - -// On-board QSPI Flash -#define EXTERNAL_FLASH_DEVICES MX25R6435F -#define EXTERNAL_FLASH_USE_QSPI - -// CUSTOM GPIOs the SX1262MB2CAS shield when installed on the NRF52840-DK development board -#define USE_SX1262 -#define SX126X_CS (32 + 8) // P1.08 -#define SX126X_DIO1 (32 + 6) // P1.06 -#define SX126X_BUSY (32 + 4) // P1.04 -#define SX126X_RESET (0 + 3) // P0.03 -#define SX126X_ANT_SW (32 + 10) // P1.10 -#define SX126X_DIO2_AS_RF_SWITCH - -// To debug via the segger JLINK console rather than the CDC-ACM serial device -// #define USE_SEGGER - -#ifdef __cplusplus -} -#endif - -/*---------------------------------------------------------------------------- - * Arduino objects - C++ only - *----------------------------------------------------------------------------*/ - -#endif diff --git a/variants/portduino/variant.h b/variants/portduino/variant.h index 70d7cbd52..b7b39d6e8 100644 --- a/variants/portduino/variant.h +++ b/variants/portduino/variant.h @@ -2,5 +2,4 @@ #define CANNED_MESSAGE_MODULE_ENABLE 1 #define HAS_GPS 1 #define MAX_RX_TOPHONE settingsMap[maxtophone] -#define MAX_NUM_NODES settingsMap[maxnodes] -#define RADIOLIB_GODMODE 1 +#define MAX_NUM_NODES settingsMap[maxnodes] \ No newline at end of file diff --git a/variants/ppr/platformio.ini b/variants/ppr/platformio.ini deleted file mode 100644 index 22273ce8e..000000000 --- a/variants/ppr/platformio.ini +++ /dev/null @@ -1,8 +0,0 @@ -; The PPR board -[env:ppr] -extends = nrf52_base -board = ppr -board_level = extra -lib_deps = - ${arduino_base.lib_deps} - industruino/UC1701@^1.1.0 \ No newline at end of file diff --git a/variants/ppr/variant.cpp b/variants/ppr/variant.cpp deleted file mode 100644 index f5f219e9b..000000000 --- a/variants/ppr/variant.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - Copyright (c) 2014-2015 Arduino LLC. All right reserved. - Copyright (c) 2016 Sandeep Mistry All right reserved. - Copyright (c) 2018, Adafruit Industries (adafruit.com) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#include "variant.h" -#include "nrf.h" -#include "wiring_constants.h" -#include "wiring_digital.h" - -const uint32_t g_ADigitalPinMap[] = { - // P0 - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0xff, 12, 13, 0xff, 15, 0xff, 17, 18, 0xff, 20, 0xff, 22, 0xff, 24, 0xff, 26, 0xff, 28, 29, - 30, 31, - - // P1 - 32, 0xff, 34, 0xff, 36, 0xff, 38, 0xff, 0xff, 41, 42, 43, 0xff, 45}; - -void initVariant() -{ - // LED1 & LED2 - pinMode(PIN_LED1, OUTPUT); - ledOff(PIN_LED1); - - pinMode(PIN_LED2, OUTPUT); - ledOff(PIN_LED2); -} diff --git a/variants/ppr/variant.h b/variants/ppr/variant.h deleted file mode 100644 index 4c6cc015c..000000000 --- a/variants/ppr/variant.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - Copyright (c) 2014-2015 Arduino LLC. All right reserved. - Copyright (c) 2016 Sandeep Mistry All right reserved. - Copyright (c) 2018, Adafruit Industries (adafruit.com) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#pragma once - -/** Master clock frequency */ -#define VARIANT_MCK (64000000ul) - -// This board does not have a 32khz crystal -// #define USE_LFXO // Board uses 32khz crystal for LF -#define USE_LFRC // Board uses RC for LF - -/*---------------------------------------------------------------------------- - * Headers - *----------------------------------------------------------------------------*/ - -#include "WVariant.h" - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - -// Number of pins defined in PinDescription array -#define PINS_COUNT (46) -#define NUM_DIGITAL_PINS (46) -#define NUM_ANALOG_INPUTS (0) -#define NUM_ANALOG_OUTPUTS (0) - -// LEDs -#define PIN_LED1 (0) -#define PIN_LED2 (1) - -#define LED_BUILTIN PIN_LED1 -#define LED_CONN PIN_LED2 - -#define LED_RED PIN_LED1 -#define LED_GREEN PIN_LED2 - -// FIXME, bluefruit automatically blinks this led while connected. call AdafruitBluefruit::autoConnLed to change this. -#define LED_BLUE LED_GREEN - -#define LED_STATE_ON 1 // State when LED is litted - -/* - * Buttons - */ -#define PIN_BUTTON1 4 // center -#define PIN_BUTTON2 2 -#define PIN_BUTTON3 3 -#define PIN_BUTTON4 5 -#define PIN_BUTTON5 6 - -/* - * Analog pins - */ -#define PIN_A0 (0xff) -#define PIN_A1 (0xff) -#define PIN_A2 (0xff) -#define PIN_A3 (0xff) -#define PIN_A4 (0xff) -#define PIN_A5 (0xff) -#define PIN_A6 (0xff) -#define PIN_A7 (0xff) - -static const uint8_t A0 = PIN_A0; -static const uint8_t A1 = PIN_A1; -static const uint8_t A2 = PIN_A2; -static const uint8_t A3 = PIN_A3; -static const uint8_t A4 = PIN_A4; -static const uint8_t A5 = PIN_A5; -static const uint8_t A6 = PIN_A6; -static const uint8_t A7 = PIN_A7; -#define ADC_RESOLUTION 14 - -// Other pins -#define PIN_AREF (0xff) -// #define PIN_NFC1 (9) -// #define PIN_NFC2 (10) - -static const uint8_t AREF = PIN_AREF; - -/* - * Serial interfaces - */ - -// GPS is on Serial1 -#define PIN_SERIAL1_RX (8) -#define PIN_SERIAL1_TX (9) - -// Connected to Jlink CDC -// #define PIN_SERIAL2_RX (8) -// #define PIN_SERIAL2_TX (6) - -/* - * SPI Interfaces - */ -#define SPI_INTERFACES_COUNT 1 - -#define PIN_SPI_MISO (15) -#define PIN_SPI_MOSI (13) -#define PIN_SPI_SCK (12) - -// static const uint8_t SS = 44; -static const uint8_t MOSI = PIN_SPI_MOSI; -static const uint8_t MISO = PIN_SPI_MISO; -static const uint8_t SCK = PIN_SPI_SCK; - -/* - * Wire Interfaces - */ -#define WIRE_INTERFACES_COUNT 1 - -#define PIN_WIRE_SDA (32 + 2) -#define PIN_WIRE_SCL (32) - -// CUSTOM GPIOs the SX1262 -#define USE_SX1262 -#define SX126X_CS (10) -#define SX126X_DIO1 (20) -#define SX1262_DIO2 (26) -#define SX126X_BUSY (31) // Supposed to be P0.18 but because of reworks, now on P0.31 (18) -#define SX126X_RESET (17) -// #define SX126X_ANT_SW (32 + 10) -#define SX126X_RXEN (22) -#define SX126X_TXEN (24) -// Indicates this SX1262 is inside of an ebyte E22 module and special config should be done for that -#define SX126X_DIO3_TCXO_VOLTAGE 1.8 - -// ERC12864-10 LCD -#define ERC12864_CS (32 + 4) -#define ERC12864_RESET (32 + 6) -#define ERC12864_CD (32 + 9) - -// L80 GPS -#define L80_PPS (28) -#define L80_RESET (29) - -#ifdef __cplusplus -} -#endif - -/*---------------------------------------------------------------------------- - * Arduino objects - C++ only - *----------------------------------------------------------------------------*/ diff --git a/variants/ppr1/platformio.ini b/variants/ppr1/platformio.ini deleted file mode 100644 index f6c2a5e0b..000000000 --- a/variants/ppr1/platformio.ini +++ /dev/null @@ -1,9 +0,0 @@ -; The PPR board -[env:ppr1] -extends = nrf52_base -board = ppr1 -board_level = extra -build_flags = ${nrf52_base.build_flags} -Ivariants/ppr1 -build_src_filter = ${nrf52_base.build_src_filter} +<../variants/ppr1> -lib_deps = - ${arduino_base.lib_deps} \ No newline at end of file diff --git a/variants/ppr1/variant.cpp b/variants/ppr1/variant.cpp deleted file mode 100644 index acc3e344a..000000000 --- a/variants/ppr1/variant.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - Copyright (c) 2014-2015 Arduino LLC. All right reserved. - Copyright (c) 2016 Sandeep Mistry All right reserved. - Copyright (c) 2018, Adafruit Industries (adafruit.com) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#include "variant.h" -#include "nrf.h" -#include "wiring_constants.h" -#include "wiring_digital.h" - -const uint32_t g_ADigitalPinMap[] = { - // P0 - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - - // P1 - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45}; - -void initVariant() -{ - // LED1 & LED2 - pinMode(PIN_LED1, OUTPUT); - ledOff(PIN_LED1); - - pinMode(PIN_LED2, OUTPUT); - ledOff(PIN_LED2); -} diff --git a/variants/ppr1/variant.h b/variants/ppr1/variant.h deleted file mode 100644 index ba3a25c2a..000000000 --- a/variants/ppr1/variant.h +++ /dev/null @@ -1,179 +0,0 @@ -/* - Copyright (c) 2014-2015 Arduino LLC. All right reserved. - Copyright (c) 2016 Sandeep Mistry All right reserved. - Copyright (c) 2018, Adafruit Industries (adafruit.com) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#pragma once - -/** Master clock frequency */ -#define VARIANT_MCK (64000000ul) - -// This board does have a 32khz crystal -#define USE_LFXO // Board uses 32khz crystal for LF -// #define USE_LFRC // Board uses RC for LF - -/*---------------------------------------------------------------------------- - * Headers - *----------------------------------------------------------------------------*/ - -#include "WVariant.h" - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - -// Number of pins defined in PinDescription array -#define PINS_COUNT (46) -#define NUM_DIGITAL_PINS (46) -#define NUM_ANALOG_INPUTS (0) -#define NUM_ANALOG_OUTPUTS (0) - -// LEDs -#define PIN_LED1 (25) -#define PIN_LED2 (11) - -#define LED_BUILTIN PIN_LED1 -#define LED_CONN PIN_LED2 - -#define LED_RED PIN_LED1 -#define LED_GREEN PIN_LED2 - -// FIXME, bluefruit automatically blinks this led while connected. call AdafruitBluefruit::autoConnLed to change this. -#define LED_BLUE LED_GREEN - -#define LED_STATE_ON 1 // State when LED is litted - -/* - * Buttons - */ -#define PIN_BUTTON1 4 // up -#define PIN_BUTTON2 2 // left -#define PIN_BUTTON3 3 // center -#define PIN_BUTTON4 5 // right -#define PIN_BUTTON5 6 // down - -/* - * Analog pins - */ -#define PIN_A0 (0xff) -#define PIN_A1 (0xff) -#define PIN_A2 (0xff) -#define PIN_A3 (0xff) -#define PIN_A4 (0xff) -#define PIN_A5 (0xff) -#define PIN_A6 (0xff) -#define PIN_A7 (0xff) - -static const uint8_t A0 = PIN_A0; -static const uint8_t A1 = PIN_A1; -static const uint8_t A2 = PIN_A2; -static const uint8_t A3 = PIN_A3; -static const uint8_t A4 = PIN_A4; -static const uint8_t A5 = PIN_A5; -static const uint8_t A6 = PIN_A6; -static const uint8_t A7 = PIN_A7; -#define ADC_RESOLUTION 14 - -// Other pins -#define PIN_AREF (0xff) -// #define PIN_NFC1 (9) -// #define PIN_NFC2 (10) - -static const uint8_t AREF = PIN_AREF; - -/* - * Serial interfaces - */ - -// GPS is on Serial1 -#define PIN_SERIAL1_RX (8) -#define PIN_SERIAL1_TX (9) - -// We intentionally leave this undefined so we don't even try to make a Ublox driver -// #define GPS_TX_PIN PIN_SERIAL1_TX -// #define GPS_RX_PIN PIN_SERIAL1_RX - -#define PIN_GPS_RESET 29 // active high -#define PIN_GPS_PPS 28 -// #define PIN_GPS_WAKE 20 // CELL_CTRL in schematic? based on their example code -#define PIN_GPS_EN 7 // GPS_EN active high - -// #define PIN_VUSB_EN 21 - -// LCD - -#define PIN_LCD_RESET 23 // active low, pulse low for 20ms at boot -#define USE_ST7567 - -/// Charge controller I2C address -#define BQ25703A_ADDR 0x6b - -// Define if screen should be mirrored left to right -#define SCREEN_MIRROR - -// LCD screens are slow, so slowdown the wipe so it looks better -#define SCREEN_TRANSITION_FRAMERATE 10 // fps - -/* - * SPI Interfaces - */ -#define SPI_INTERFACES_COUNT 1 - -#define PIN_SPI_MISO (15) -#define PIN_SPI_MOSI (13) -#define PIN_SPI_SCK (12) - -// static const uint8_t SS = 44; -static const uint8_t MOSI = PIN_SPI_MOSI; -static const uint8_t MISO = PIN_SPI_MISO; -static const uint8_t SCK = PIN_SPI_SCK; - -/* - * Wire Interfaces - */ -#define WIRE_INTERFACES_COUNT 1 - -#define PIN_WIRE_SDA (32 + 2) -#define PIN_WIRE_SCL (32) - -// CUSTOM GPIOs the SX1262 -#define USE_SX1262 -#define SX126X_CS (0 + 10) // FIXME - we really should define LORA_CS instead -#define SX126X_DIO1 (0 + 20) -#define SX1262_DIO2 (0 + 26) -#define SX126X_BUSY (0 + 19) -#define SX126X_RESET (0 + 17) -#define SX126X_TXEN (0 + 24) -#define SX126X_RXEN (0 + 22) -// Not really an E22 but this board clones using DIO3 for tcxo control -#define SX126X_DIO3_TCXO_VOLTAGE 1.8 - -// FIXME, to prevent burning out parts I've set the power level super low, because I don't have -// an antenna wired up -#define SX126X_MAX_POWER 1 - -#define LORA_DISABLE_SENDING // Define this to disable transmission for testing (power testing etc...) - -// To debug via the segger JLINK console rather than the CDC-ACM serial device -// #define USE_SEGGER - -#ifdef __cplusplus -} -#endif - -/*---------------------------------------------------------------------------- - * Arduino objects - C++ only - *----------------------------------------------------------------------------*/ diff --git a/variants/radiomaster_900_bandit/variant.h b/variants/radiomaster_900_bandit/variant.h index 0499970f5..0c7417cac 100644 --- a/variants/radiomaster_900_bandit/variant.h +++ b/variants/radiomaster_900_bandit/variant.h @@ -11,12 +11,17 @@ /* I2C SDA and SCL. - 0x18 - STK8XXX Accelerometer, Not supported yet. + 0x18 - STK8XXX Accelerometer 0x3C - SH1115 Display Driver */ #define I2C_SDA 14 #define I2C_SCL 12 +/* + I2C STK8XXX Accelerometer Interrupt PIN to ESP32 Pin 6 - SENSOR_CAPP (GPIO37) +*/ +#define STK8XXX_INT 37 + /* No GPS - but free pins are available. */ diff --git a/variants/rak11200/variant.h b/variants/rak11200/variant.h index 3cd601254..01edb8b73 100644 --- a/variants/rak11200/variant.h +++ b/variants/rak11200/variant.h @@ -70,11 +70,12 @@ static const uint8_t SCK = 33; #define LORA_CS SS #define USE_SX1262 +#define SX126X_ANT_SW WB_IO3 #define SX126X_CS SS // NSS for SX126X #define SX126X_DIO1 LORA_DIO1 #define SX126X_BUSY LORA_DIO2 #define SX126X_RESET LORA_RESET -#define SX126X_POWER_EN WB_IO3 +#define SX126X_POWER_EN WB_IO2 // DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3 #define SX126X_DIO2_AS_RF_SWITCH #define SX126X_DIO3_TCXO_VOLTAGE 1.8 diff --git a/variants/rak11310/platformio.ini b/variants/rak11310/platformio.ini index e1bd2b1b0..c7b3504fe 100644 --- a/variants/rak11310/platformio.ini +++ b/variants/rak11310/platformio.ini @@ -2,6 +2,8 @@ extends = rp2040_base board = wiscore_rak11300 upload_protocol = picotool +# keep an old SDK to use less memory. +platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#3.7.2 # add our variants files to the include and src paths build_flags = ${rp2040_base.build_flags} diff --git a/variants/rak11310/variant.h b/variants/rak11310/variant.h index f9dcbd91a..54e403ee7 100644 --- a/variants/rak11310/variant.h +++ b/variants/rak11310/variant.h @@ -6,6 +6,7 @@ #define LED_CONN PIN_LED2 #define LED_PIN LED_BUILTIN +#define ledOff(pin) pinMode(pin, INPUT) #define BUTTON_PIN 9 #define BUTTON_NEED_PULLUP diff --git a/variants/rak3172/platformio.ini b/variants/rak3172/platformio.ini index d1bd55e83..9e617e01e 100644 --- a/variants/rak3172/platformio.ini +++ b/variants/rak3172/platformio.ini @@ -28,4 +28,7 @@ build_flags = -DHAL_TIM_MODULE_DISABLED -DHAL_WWDG_MODULE_DISABLED -DHAL_EXTI_MODULE_DISABLED + -DRADIOLIB_EXCLUDE_SX128X=1 + -DRADIOLIB_EXCLUDE_SX127X=1 + -DRADIOLIB_EXCLUDE_LR11X0=1 upload_port = stlink \ No newline at end of file diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini index 8f1006eca..ced93df66 100644 --- a/variants/rak4631/platformio.ini +++ b/variants/rak4631/platformio.ini @@ -9,6 +9,9 @@ build_flags = ${nrf52840_base.build_flags} -Ivariants/rak4631 -D RAK_4631 -DEINK_DISPLAY_MODEL=GxEPD2_213_BN -DEINK_WIDTH=250 -DEINK_HEIGHT=122 + -DRADIOLIB_EXCLUDE_SX128X=1 + -DRADIOLIB_EXCLUDE_SX127X=1 + -DRADIOLIB_EXCLUDE_LR11X0=1 build_src_filter = ${nrf52_base.build_src_filter} +<../variants/rak4631> + + + lib_deps = ${nrf52840_base.lib_deps} @@ -16,7 +19,7 @@ lib_deps = melopero/Melopero RV3028@^1.1.0 https://github.com/RAKWireless/RAK13800-W5100S.git#1.0.2 rakwireless/RAKwireless NCP5623 RGB LED library@^1.0.2 - https://github.com/meshtastic/RAK12034-BMX160.git#4821355fb10390ba8557dc43ca29a023bcfbb9d9 + https://github.com/RAKWireless/RAK12034-BMX160.git#dcead07ffa267d3c906e9ca4a1330ab989e957e2 ; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) ; Note: as of 6/2013 the serial/bootloader based programming takes approximately 30 seconds diff --git a/variants/rak4631_epaper/platformio.ini b/variants/rak4631_epaper/platformio.ini index 08342dcf7..2479f09c8 100644 --- a/variants/rak4631_epaper/platformio.ini +++ b/variants/rak4631_epaper/platformio.ini @@ -7,6 +7,9 @@ build_flags = ${nrf52840_base.build_flags} -Ivariants/rak4631_epaper -D RAK_4631 -DEINK_DISPLAY_MODEL=GxEPD2_213_BN -DEINK_WIDTH=250 -DEINK_HEIGHT=122 + -DRADIOLIB_EXCLUDE_SX128X=1 + -DRADIOLIB_EXCLUDE_SX127X=1 + -DRADIOLIB_EXCLUDE_LR11X0=1 build_src_filter = ${nrf52_base.build_src_filter} +<../variants/rak4631_epaper> lib_deps = ${nrf52840_base.lib_deps} diff --git a/variants/rak4631_epaper_onrxtx/platformio.ini b/variants/rak4631_epaper_onrxtx/platformio.ini index f7035a1b1..8c1b8eee8 100644 --- a/variants/rak4631_epaper_onrxtx/platformio.ini +++ b/variants/rak4631_epaper_onrxtx/platformio.ini @@ -9,6 +9,9 @@ build_flags = ${nrf52840_base.build_flags} -Ivariants/rak4631_epaper -D RAK_4631 -D EINK_DISPLAY_MODEL=GxEPD2_213_BN -D EINK_WIDTH=250 -D EINK_HEIGHT=122 + -D RADIOLIB_EXCLUDE_SX128X=1 + -D RADIOLIB_EXCLUDE_SX127X=1 + -D RADIOLIB_EXCLUDE_LR11X0=1 build_src_filter = ${nrf52_base.build_src_filter} +<../variants/rak4631_epaper_onrxtx> lib_deps = ${nrf52840_base.lib_deps} diff --git a/variants/rak4631_eth_gw/platformio.ini b/variants/rak4631_eth_gw/platformio.ini new file mode 100644 index 000000000..62b7e737d --- /dev/null +++ b/variants/rak4631_eth_gw/platformio.ini @@ -0,0 +1,66 @@ +; The very slick RAK wireless RAK 4631 / 4630 board - Unified firmware for 5005/19003, with or without OLED RAK 1921 +[env:rak4631_eth_gw] +extends = nrf52840_base +board = wiscore_rak4631 +board_check = true +build_flags = ${nrf52840_base.build_flags} -Ivariants/rak4631_eth_gw -D RAK_4631 + -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" + -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. + -DEINK_DISPLAY_MODEL=GxEPD2_213_BN + -DEINK_WIDTH=250 + -DEINK_HEIGHT=122 + -DNRF52_USE_JSON=1 + -DMESHTASTIC_EXCLUDE_GPS=1 + -DMESHTASTIC_EXCLUDE_WIFI=1 + -DMESHTASTIC_EXCLUDE_SCREEN=1 +; -DMESHTASTIC_EXCLUDE_PKI=1 + -DMESHTASTIC_EXCLUDE_POWER_FSM=1 + -DMESHTASTIC_EXCLUDE_POWERMON=1 +; -DMESHTASTIC_EXCLUDE_TZ=1 + -DMESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION=1 + -DMESHTASTIC_EXCLUDE_PAXCOUNTER=1 + -DMESHTASTIC_EXCLUDE_REMOTEHARDWARE=1 + -DMESHTASTIC_EXCLUDE_STOREFORWARD=1 + -DMESHTASTIC_EXCLUDE_CANNEDMESSAGES=1 + -DMESHTASTIC_EXCLUDE_WAYPOINT=1 +build_src_filter = ${nrf52_base.build_src_filter} +<../variants/rak4631_eth_gw> + + + +lib_deps = + ${nrf52840_base.lib_deps} + ${networking_base.lib_deps} + melopero/Melopero RV3028@^1.1.0 + https://github.com/RAKWireless/RAK13800-W5100S.git#1.0.2 + rakwireless/RAKwireless NCP5623 RGB LED library@^1.0.2 + https://github.com/meshtastic/RAK12034-BMX160.git#4821355fb10390ba8557dc43ca29a023bcfbb9d9 + bblanchon/ArduinoJson @ 6.21.4 +; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) +; Note: as of 6/2013 the serial/bootloader based programming takes approximately 30 seconds +;upload_protocol = jlink + +; Allows programming and debug via the RAK NanoDAP as the default debugger tool for the RAK4631 (it is only $10!) +; programming time is about the same as the bootloader version. +; For information on this see the meshtastic developers documentation for "Development on the NRF52" +[env:rak4631_eth_gw_dbg] +extends = env:rak4631 +board_level = extra + +; if the builtin version of openocd has a buggy version of semihosting, so use the external version +; platform_packages = platformio/tool-openocd@^3.1200.0 + +build_flags = + ${env:rak4631_eth_gw.build_flags} + -D USE_SEMIHOSTING + +lib_deps = + ${env:rak4631_eth_gw.lib_deps} + https://github.com/geeksville/Armduino-Semihosting.git#35b538fdf208c3530c1434cd099a08e486672ee4 + +; NOTE: the pyocd support for semihosting is buggy. So I switched to using the builtin platformio support for the stlink adapter which worked much better. +; However the built in openocd version in platformio has buggy support for TCP to semihosting. +; +; So I'm now trying the external openocd - but the openocd scripts for nrf52.cfg assume you are using a DAP adapter not an STLINK adapter. +; In theory I could change those scripts. But for now I'm trying going back to a DAP adapter but with the external openocd. + +upload_protocol = stlink +; eventually use platformio/tool-pyocd@^2.3600.0 instad +;upload_protocol = custom +;upload_command = pyocd flash -t nrf52840 $UPLOADERFLAGS $SOURCE \ No newline at end of file diff --git a/variants/pca10056-rc-clock/variant.cpp b/variants/rak4631_eth_gw/variant.cpp similarity index 93% rename from variants/pca10056-rc-clock/variant.cpp rename to variants/rak4631_eth_gw/variant.cpp index a1882a33f..e84b60b3b 100644 --- a/variants/pca10056-rc-clock/variant.cpp +++ b/variants/rak4631_eth_gw/variant.cpp @@ -38,5 +38,8 @@ void initVariant() pinMode(PIN_LED2, OUTPUT); ledOff(PIN_LED2); - ; + + // 3V3 Power Rail + pinMode(PIN_3V3_EN, OUTPUT); + digitalWrite(PIN_3V3_EN, HIGH); } diff --git a/variants/rak4631_eth_gw/variant.h b/variants/rak4631_eth_gw/variant.h new file mode 100644 index 000000000..bc5541336 --- /dev/null +++ b/variants/rak4631_eth_gw/variant.h @@ -0,0 +1,273 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Sandeep Mistry All right reserved. + Copyright (c) 2018, Adafruit Industries (adafruit.com) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _VARIANT_RAK4630_ +#define _VARIANT_RAK4630_ + +#define RAK4630 + +/** Master clock frequency */ +#define VARIANT_MCK (64000000ul) + +#define USE_LFXO // Board uses 32khz crystal for LF +// define USE_LFRC // Board uses RC for LF + +/*---------------------------------------------------------------------------- + * Headers + *----------------------------------------------------------------------------*/ + +#include "WVariant.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// Number of pins defined in PinDescription array +#define PINS_COUNT (48) +#define NUM_DIGITAL_PINS (48) +#define NUM_ANALOG_INPUTS (6) +#define NUM_ANALOG_OUTPUTS (0) + +// LEDs +#define PIN_LED1 (35) +#define PIN_LED2 (36) + +#define LED_BUILTIN PIN_LED1 +#define LED_CONN PIN_LED2 + +#define LED_GREEN PIN_LED1 +#define LED_BLUE PIN_LED2 + +#define LED_STATE_ON 1 // State when LED is litted + +/* + * Buttons + */ + +#define PIN_BUTTON1 9 // Pin for button on E-ink button module or IO expansion +#define BUTTON_NEED_PULLUP +#define PIN_BUTTON2 12 +#define PIN_BUTTON3 24 +#define PIN_BUTTON4 25 + +/* + * Analog pins + */ +#define PIN_A0 (5) +#define PIN_A1 (31) +#define PIN_A2 (28) +#define PIN_A3 (29) +#define PIN_A4 (30) +#define PIN_A5 (31) +#define PIN_A6 (0xff) +#define PIN_A7 (0xff) + +static const uint8_t A0 = PIN_A0; +static const uint8_t A1 = PIN_A1; +static const uint8_t A2 = PIN_A2; +static const uint8_t A3 = PIN_A3; +static const uint8_t A4 = PIN_A4; +static const uint8_t A5 = PIN_A5; +static const uint8_t A6 = PIN_A6; +static const uint8_t A7 = PIN_A7; +#define ADC_RESOLUTION 14 + +// Other pins +#define PIN_AREF (2) +#define PIN_NFC1 (9) +#define PIN_NFC2 (10) + +static const uint8_t AREF = PIN_AREF; + +/* + * Serial interfaces + */ +#define PIN_SERIAL1_RX (15) +#define PIN_SERIAL1_TX (16) + +// Connected to Jlink CDC +#define PIN_SERIAL2_RX (8) +#define PIN_SERIAL2_TX (6) + +/* + * SPI Interfaces + */ +#define SPI_INTERFACES_COUNT 2 + +#define PIN_SPI_MISO (45) +#define PIN_SPI_MOSI (44) +#define PIN_SPI_SCK (43) + +#define PIN_SPI1_MISO (29) // (0 + 29) +#define PIN_SPI1_MOSI (30) // (0 + 30) +#define PIN_SPI1_SCK (3) // (0 + 3) + +static const uint8_t SS = 42; +static const uint8_t MOSI = PIN_SPI_MOSI; +static const uint8_t MISO = PIN_SPI_MISO; +static const uint8_t SCK = PIN_SPI_SCK; + +/* + * eink display pins + */ + +#define PIN_EINK_CS (0 + 26) +#define PIN_EINK_BUSY (0 + 4) +#define PIN_EINK_DC (0 + 17) +#define PIN_EINK_RES (-1) +#define PIN_EINK_SCLK (0 + 3) +#define PIN_EINK_MOSI (0 + 30) // also called SDI + +// #define USE_EINK + +// RAKRGB +#define HAS_NCP5623 + +/* + * Wire Interfaces + */ +#define WIRE_INTERFACES_COUNT 1 + +#define PIN_WIRE_SDA (13) +#define PIN_WIRE_SCL (14) + +// QSPI Pins +#define PIN_QSPI_SCK 3 +#define PIN_QSPI_CS 26 +#define PIN_QSPI_IO0 30 +#define PIN_QSPI_IO1 29 +#define PIN_QSPI_IO2 28 +#define PIN_QSPI_IO3 2 + +// On-board QSPI Flash +#define EXTERNAL_FLASH_DEVICES IS25LP080D +#define EXTERNAL_FLASH_USE_QSPI + +/* @note RAK5005-O GPIO mapping to RAK4631 GPIO ports + RAK5005-O <-> nRF52840 + IO1 <-> P0.17 (Arduino GPIO number 17) + IO2 <-> P1.02 (Arduino GPIO number 34) + IO3 <-> P0.21 (Arduino GPIO number 21) + IO4 <-> P0.04 (Arduino GPIO number 4) + IO5 <-> P0.09 (Arduino GPIO number 9) + IO6 <-> P0.10 (Arduino GPIO number 10) + IO7 <-> P0.28 (Arduino GPIO number 28) + SW1 <-> P0.01 (Arduino GPIO number 1) + A0 <-> P0.04/AIN2 (Arduino Analog A2 + A1 <-> P0.31/AIN7 (Arduino Analog A7 + SPI_CS <-> P0.26 (Arduino GPIO number 26) + */ + +// RAK4630 LoRa module + +/* Setup of the SX1262 LoRa module ( https://docs.rakwireless.com/Product-Categories/WisBlock/RAK4631/Datasheet/ ) + +P1.10 NSS SPI NSS (Arduino GPIO number 42) +P1.11 SCK SPI CLK (Arduino GPIO number 43) +P1.12 MOSI SPI MOSI (Arduino GPIO number 44) +P1.13 MISO SPI MISO (Arduino GPIO number 45) +P1.14 BUSY BUSY signal (Arduino GPIO number 46) +P1.15 DIO1 DIO1 event interrupt (Arduino GPIO number 47) +P1.06 NRESET NRESET manual reset of the SX1262 (Arduino GPIO number 38) + +Important for successful SX1262 initialization: + +* Setup DIO2 to control the antenna switch +* Setup DIO3 to control the TCXO power supply +* Setup the SX1262 to use it's DCDC regulator and not the LDO +* RAK4630 schematics show GPIO P1.07 connected to the antenna switch, but it should not be initialized, as DIO2 will do the +control of the antenna switch + +SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG + +*/ + +#define DETECTION_SENSOR_EN 4 + +#define USE_SX1262 +#define SX126X_CS (42) +#define SX126X_DIO1 (47) +#define SX126X_BUSY (46) +#define SX126X_RESET (38) +// #define SX126X_TXEN (39) +// #define SX126X_RXEN (37) +#define SX126X_POWER_EN (37) +// DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3 +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 + +// Testing USB detection +#define NRF_APM + +// enables 3.3V periphery like GPS or IO Module +// Do not toggle this for GPS power savings +#define PIN_3V3_EN (34) + +// RAK1910 GPS module +// If using the wisblock GPS module and pluged into Port A on WisBlock base +// IO1 is hooked to PPS (pin 12 on header) = gpio 17 +// IO2 is hooked to GPS RESET = gpio 34, but it can not be used to this because IO2 is ALSO used to control 3V3_S power (1 is on). +// Therefore must be 1 to keep peripherals powered +// Power is on the controllable 3V3_S rail +// #define PIN_GPS_RESET (34) +// #define PIN_GPS_EN PIN_3V3_EN +#define PIN_GPS_PPS (17) // Pulse per second input from the GPS + +#define GPS_RX_PIN PIN_SERIAL1_RX +#define GPS_TX_PIN PIN_SERIAL1_TX + +// Define pin to enable GPS toggle (set GPIO to LOW) via user button triple press + +// RAK12002 RTC Module +#define RV3028_RTC (uint8_t)0b1010010 + +// RAK18001 Buzzer in Slot C +// #define PIN_BUZZER 21 // IO3 is PWM2 +// NEW: set this via protobuf instead! + +// Battery +// The battery sense is hooked to pin A0 (5) +#define BATTERY_PIN PIN_A0 +// and has 12 bit resolution +#define BATTERY_SENSE_RESOLUTION_BITS 12 +#define BATTERY_SENSE_RESOLUTION 4096.0 +#undef AREF_VOLTAGE +#define AREF_VOLTAGE 3.0 +#define VBAT_AR_INTERNAL AR_INTERNAL_3_0 +#define ADC_MULTIPLIER 1.73 + +#define HAS_RTC 1 + +#define HAS_ETHERNET 1 + +#define RAK_4631 1 + +#define PIN_ETHERNET_RESET 21 +#define PIN_ETHERNET_SS PIN_EINK_CS +#define ETH_SPI_PORT SPI1 +#define AQ_SET_PIN 10 + +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------- + * Arduino objects - C++ only + *----------------------------------------------------------------------------*/ + +#endif \ No newline at end of file diff --git a/variants/rp2040-lora/variant.h b/variants/rp2040-lora/variant.h index d0bbc0ec3..f1826605f 100644 --- a/variants/rp2040-lora/variant.h +++ b/variants/rp2040-lora/variant.h @@ -23,6 +23,7 @@ // ratio of voltage divider = 3.0 (R17=200k, R18=100k) // #define ADC_MULTIPLIER 3.1 // 3.0 + a bit for being optimistic +#define HAS_CPU_SHUTDOWN 1 #define USE_SX1262 #undef LORA_SCK diff --git a/variants/rpipico2/platformio.ini b/variants/rpipico2/platformio.ini new file mode 100644 index 000000000..a63414418 --- /dev/null +++ b/variants/rpipico2/platformio.ini @@ -0,0 +1,16 @@ +[env:pico2] +extends = rp2350_base +board = rpipico2 +upload_protocol = picotool + +# add our variants files to the include and src paths +build_flags = ${rp2350_base.build_flags} + -DRPI_PICO2 + -Ivariants/rpipico2 + -DDEBUG_RP2040_PORT=Serial + -DHW_SPI1_DEVICE + -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m0plus" +lib_deps = + ${rp2350_base.lib_deps} +debug_build_flags = ${rp2350_base.build_flags} +debug_tool = cmsis-dap ; for e.g. Picotool \ No newline at end of file diff --git a/variants/rpipico2/variant.h b/variants/rpipico2/variant.h new file mode 100644 index 000000000..7efaeaf7a --- /dev/null +++ b/variants/rpipico2/variant.h @@ -0,0 +1,50 @@ +// #define RADIOLIB_CUSTOM_ARDUINO 1 +// #define RADIOLIB_TONE_UNSUPPORTED 1 +// #define RADIOLIB_SOFTWARE_SERIAL_UNSUPPORTED 1 + +#define ARDUINO_ARCH_AVR + +// default I2C pins: +// SDA = 4 +// SCL = 5 + +// Recommended pins for SerialModule: +// txd = 8 +// rxd = 9 + +#define EXT_NOTIFY_OUT 22 +#define BUTTON_PIN 17 + +#define LED_PIN PIN_LED + +#define BATTERY_PIN 26 +// ratio of voltage divider = 3.0 (R17=200k, R18=100k) +#define ADC_MULTIPLIER 3.1 // 3.0 + a bit for being optimistic +#define BATTERY_SENSE_RESOLUTION_BITS ADC_RESOLUTION + +#define USE_SX1262 + +#undef LORA_SCK +#undef LORA_MISO +#undef LORA_MOSI +#undef LORA_CS + +#define LORA_SCK 10 +#define LORA_MISO 12 +#define LORA_MOSI 11 +#define LORA_CS 3 + +#define LORA_DIO0 RADIOLIB_NC +#define LORA_RESET 15 +#define LORA_DIO1 20 +#define LORA_DIO2 2 +#define LORA_DIO3 RADIOLIB_NC + +#ifdef USE_SX1262 +#define SX126X_CS LORA_CS +#define SX126X_DIO1 LORA_DIO1 +#define SX126X_BUSY LORA_DIO2 +#define SX126X_RESET LORA_RESET +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 +#endif diff --git a/variants/seeed-sensecap-indicator/pins_arduino.h b/variants/seeed-sensecap-indicator/pins_arduino.h new file mode 100644 index 000000000..300f0e0f5 --- /dev/null +++ b/variants/seeed-sensecap-indicator/pins_arduino.h @@ -0,0 +1,56 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +// static const uint8_t LED_BUILTIN = -1; + +// static const uint8_t TX = 43; +// static const uint8_t RX = 44; + +static const uint8_t SDA = 39; +static const uint8_t SCL = 40; + +// Default SPI will be mapped to Radio +static const uint8_t SS = -1; +static const uint8_t MOSI = 48; +static const uint8_t MISO = 47; +static const uint8_t SCK = 41; + +static const uint8_t A0 = 1; +static const uint8_t A1 = 2; +static const uint8_t A2 = 3; +static const uint8_t A3 = 4; +static const uint8_t A4 = 5; +static const uint8_t A5 = 6; +static const uint8_t A6 = 7; +static const uint8_t A7 = 8; +static const uint8_t A8 = 9; +static const uint8_t A9 = 10; +static const uint8_t A10 = 11; +static const uint8_t A11 = 12; +static const uint8_t A12 = 13; +static const uint8_t A13 = 14; +static const uint8_t A14 = 15; +static const uint8_t A15 = 16; +static const uint8_t A16 = 17; +static const uint8_t A17 = 18; +static const uint8_t A18 = 19; +static const uint8_t A19 = 20; + +static const uint8_t T1 = 1; +static const uint8_t T2 = 2; +static const uint8_t T3 = 3; +static const uint8_t T4 = 4; +static const uint8_t T5 = 5; +static const uint8_t T6 = 6; +static const uint8_t T7 = 7; +static const uint8_t T8 = 8; +static const uint8_t T9 = 9; +static const uint8_t T10 = 10; +static const uint8_t T11 = 11; +static const uint8_t T12 = 12; +static const uint8_t T13 = 13; +static const uint8_t T14 = 14; + +#endif /* Pins_Arduino_h */ \ No newline at end of file diff --git a/variants/seeed-sensecap-indicator/platformio.ini b/variants/seeed-sensecap-indicator/platformio.ini new file mode 100644 index 000000000..e6bb2145e --- /dev/null +++ b/variants/seeed-sensecap-indicator/platformio.ini @@ -0,0 +1,28 @@ +; Seeed Studio SenseCAP Indicator +[env:seeed-sensecap-indicator] +extends = esp32s3_base +platform_packages = + platformio/framework-arduinoespressif32 @ https://github.com/mverch67/arduino-esp32.git#add_tca9535 ; based on 2.0.16 + +board = seeed-sensecap-indicator +board_check = true +upload_protocol = esptool + +build_flags = ${esp32_base.build_flags} + -Ivariants/seeed-sensecap-indicator + -DSENSECAP_INDICATOR + -DCONFIG_ARDUHAL_LOG_COLORS + -DRADIOLIB_DEBUG_SPI=0 + -DRADIOLIB_DEBUG_PROTOCOL=0 + -DRADIOLIB_DEBUG_BASIC=0 + -DRADIOLIB_VERBOSE_ASSERT=0 + -DRADIOLIB_SPI_PARANOID=0 + -DIO_EXPANDER=0x40 + -DIO_EXPANDER_IRQ=42 + ;-DIO_EXPANDER_DEBUG + -DUSE_ARDUINO_HAL_GPIO + +lib_deps = ${esp32s3_base.lib_deps} + https://github.com/mverch67/LovyanGFX#develop + earlephilhower/ESP8266Audio@^1.9.7 + earlephilhower/ESP8266SAM@^1.0.1 \ No newline at end of file diff --git a/variants/seeed-sensecap-indicator/variant.h b/variants/seeed-sensecap-indicator/variant.h new file mode 100644 index 000000000..d7ed329eb --- /dev/null +++ b/variants/seeed-sensecap-indicator/variant.h @@ -0,0 +1,64 @@ +#define I2C_SDA 39 +#define I2C_SCL 40 + +#define BUTTON_PIN 38 +// #define BUTTON_NEED_PULLUP + +// #define BATTERY_PIN 27 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage +// #define ADC_CHANNEL ADC1_GPIO27_CHANNEL +// #define ADC_MULTIPLIER 2 + +// ST7701 TFT LCD +#define ST7701_CS (4 | IO_EXPANDER) +#define ST7701_RS -1 // DC +#define ST7701_SDA 48 // MOSI +#define ST7701_SCK 41 +#define ST7701_RESET (5 | IO_EXPANDER) +#define ST7701_MISO 47 +#define ST7701_BUSY -1 +#define ST7701_BL 45 +#define ST7701_SPI_HOST SPI2_HOST +#define ST7701_BACKLIGHT_EN 45 +#define SPI_FREQUENCY 20000000 +#define SPI_READ_FREQUENCY 16000000 +#define TFT_HEIGHT 480 +#define TFT_WIDTH 480 +#define TFT_OFFSET_X 0 +#define TFT_OFFSET_Y 0 +#define TFT_OFFSET_ROTATION 0 +#define TFT_BL 45 +#define SCREEN_ROTATE +#define SCREEN_TRANSITION_FRAMERATE 5 // fps + +#define HAS_TOUCHSCREEN 1 +#define SCREEN_TOUCH_INT (6 | IO_EXPANDER) +#define SCREEN_TOUCH_RST (7 | IO_EXPANDER) +#define TOUCH_I2C_PORT 0 +#define TOUCH_SLAVE_ADDRESS 0x48 + +// Buzzer +#define PIN_BUZZER 19 + +#define HAS_GPS 0 +#undef GPS_RX_PIN +#undef GPS_TX_PIN + +#define USE_SX1262 +#define USE_SX1268 + +#define LORA_SCK 41 +#define LORA_MISO 47 +#define LORA_MOSI 48 +#define LORA_CS (0 | IO_EXPANDER) + +#define LORA_DIO0 -1 // a no connect on the SX1262 module +#define LORA_RESET (1 | IO_EXPANDER) +#define LORA_DIO1 (3 | IO_EXPANDER) // SX1262 IRQ +#define LORA_DIO2 (2 | IO_EXPANDER) // SX1262 BUSY +#define LORA_DIO3 + +#define SX126X_CS LORA_CS +#define SX126X_DIO1 LORA_DIO1 +#define SX126X_BUSY LORA_DIO2 +#define SX126X_RESET LORA_RESET +#define SX126X_DIO2_AS_RF_SWITCH diff --git a/variants/seeed_xiao_s3/pins_arduino.h b/variants/seeed_xiao_s3/pins_arduino.h new file mode 100644 index 000000000..52e96eaeb --- /dev/null +++ b/variants/seeed_xiao_s3/pins_arduino.h @@ -0,0 +1,21 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +#define USB_VID 0x2886 +#define USB_PID 0x0059 + +// GPIO48 Reference: https://github.com/espressif/arduino-esp32/pull/8600 + +// The default Wire will be mapped to Screen and Sensors +static const uint8_t SDA = 47; +static const uint8_t SCL = 48; + +// Default SPI will be mapped to Radio +static const uint8_t MISO = 8; +static const uint8_t SCK = 7; +static const uint8_t MOSI = 9; +static const uint8_t SS = 41; + +#endif /* Pins_Arduino_h */ diff --git a/variants/seeed_xiao_s3/platformio.ini b/variants/seeed_xiao_s3/platformio.ini new file mode 100644 index 000000000..3d10d7136 --- /dev/null +++ b/variants/seeed_xiao_s3/platformio.ini @@ -0,0 +1,17 @@ +[env:seeed-xiao-s3] +extends = esp32s3_base +board = seeed-xiao-s3 +board_check = true +board_build.mcu = esp32s3 +upload_protocol = esptool +upload_speed = 921600 +lib_deps = + ${esp32s3_base.lib_deps} +build_unflags = + ${esp32s3_base.build_unflags} + -DARDUINO_USB_MODE=1 +build_flags = + ${esp32s3_base.build_flags} -DSEEED_XIAO_S3 -I variants/seeed_xiao_s3 + -DBOARD_HAS_PSRAM + + -DARDUINO_USB_MODE=0 \ No newline at end of file diff --git a/variants/seeed_xiao_s3/variant.h b/variants/seeed_xiao_s3/variant.h new file mode 100644 index 000000000..ab886d354 --- /dev/null +++ b/variants/seeed_xiao_s3/variant.h @@ -0,0 +1,84 @@ +/* + ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄ +▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░▌ +▐░█▀▀▀▀▀▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀ ▐░█▀▀▀▀▀▀▀█░▌ +▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌ +▐░█▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄▄▄ ▐░▌ ▐░▌ +▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌ ▐░▌ + ▀▀▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀ ▐░▌ ▐░▌ + ▐░▌▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌ + ▄▄▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄█░▌ +▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░▌ + ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀ + + ▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ + ▐░▌ ▐░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌ + ▐░▌ ▐░▌ ▀▀▀▀█░█▀▀▀▀ ▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀█░▌ ▐░█▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀█░▌ + ▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌ ▐░▌ + ▐░▐░▌ ▐░▌ ▐░█▄▄▄▄▄▄▄█░▌▐░▌ ▐░▌ ▐░█▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄█░▌ + ▐░▌ ▐░▌ ▐░░░░░░░░░░░▌▐░▌ ▐░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌ + ▐░▌░▌ ▐░▌ ▐░█▀▀▀▀▀▀▀█░▌▐░▌ ▐░▌ ▀▀▀▀▀▀▀▀▀█░▌ ▀▀▀▀▀▀▀▀▀█░▌ + ▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌ ▐░▌ + ▐░▌ ▐░▌ ▄▄▄▄█░█▄▄▄▄ ▐░▌ ▐░▌▐░█▄▄▄▄▄▄▄█░▌ ▄▄▄▄▄▄▄▄▄█░▌ ▄▄▄▄▄▄▄▄▄█░▌ + ▐░▌ ▐░▌▐░░░░░░░░░░░▌▐░▌ ▐░▌▐░░░░░░░░░░░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌ + ▀ ▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ +*/ + +/* +Board Information: https://www.seeedstudio.com/XIAO-ESP32S3-Sense-p-5639.html +Expansion Board Infomation : https://www.seeedstudio.com/Seeeduino-XIAO-Expansion-board-p-4746.html +L76K GPS Module Information : https://www.seeedstudio.com/L76K-GNSS-Module-for-Seeed-Studio-XIAO-p-5864.html +*/ + +#define LED_PIN 48 +#define LED_STATE_ON 1 // State when LED is lit + +#define BUTTON_PIN 21 // This is the Program Button +#define BUTTON_NEED_PULLUP + +/*Warning: + https://www.seeedstudio.com/L76K-GNSS-Module-for-Seeed-Studio-XIAO-p-5864.html + L76K Expansion Board can not directly used, L76K Reset Pin needs to override or physically remove it, + otherwise it will conflict with the SPI pins +*/ +// #define GPS_L76K +#ifdef GPS_L76K +#define GPS_RX_PIN 44 +#define GPS_TX_PIN 43 +#define HAS_GPS 1 +#define GPS_BAUDRATE 9600 +#define GPS_THREAD_INTERVAL 50 +#define PIN_SERIAL1_RX PIN_GPS_TX +#define PIN_SERIAL1_TX PIN_GPS_RX +#define PIN_GPS_STANDBY 1 +#endif + +// XIAO S3 Expansion board has 1.3 inch OLED Screen +#define USCREEN_SSD1306 + +#define I2C_SDA 5 +#define I2C_SCL 6 + +// XIAO S3 LORA module +#define USE_SX1262 + +#define LORA_MISO 8 +#define LORA_SCK 7 +#define LORA_MOSI 9 +#define LORA_CS 41 + +#define LORA_RESET 42 +#define LORA_DIO1 39 + +#define LORA_DIO2 38 + +#ifdef USE_SX1262 +#define SX126X_CS LORA_CS +#define SX126X_DIO1 LORA_DIO1 +#define SX126X_BUSY 40 +#define SX126X_RESET LORA_RESET + +// DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3 +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 +#endif \ No newline at end of file diff --git a/variants/senselora_rp2040/platformio.ini b/variants/senselora_rp2040/platformio.ini index c719bd261..852ecbbb9 100644 --- a/variants/senselora_rp2040/platformio.ini +++ b/variants/senselora_rp2040/platformio.ini @@ -1,4 +1,5 @@ [env:senselora_rp2040] +board_level = extra extends = rp2040_base board = rpipico upload_protocol = picotool diff --git a/variants/tbeam_v07/platformio.ini b/variants/tbeam_v07/platformio.ini index 105d65912..0cba92400 100644 --- a/variants/tbeam_v07/platformio.ini +++ b/variants/tbeam_v07/platformio.ini @@ -1,5 +1,6 @@ ; The original TBEAM board without the AXP power chip and a few other changes [env:tbeam0_7] +board_level = extra extends = esp32_base board = ttgo-t-beam build_flags = diff --git a/variants/tlora_c6/platformio.ini b/variants/tlora_c6/platformio.ini new file mode 100644 index 000000000..d042cd78b --- /dev/null +++ b/variants/tlora_c6/platformio.ini @@ -0,0 +1,10 @@ +[env:tlora-c6] +extends = esp32c6_base +board = esp32-c6-devkitm-1 +build_flags = + ${esp32c6_base.build_flags} + -D TLORA_C6 + -I variants/tlora_c6 + -DARDUINO_USB_CDC_ON_BOOT=1 + -DARDUINO_USB_MODE=1 + -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/esp32c3" \ No newline at end of file diff --git a/variants/tlora_c6/variant.h b/variants/tlora_c6/variant.h new file mode 100644 index 000000000..55635fe13 --- /dev/null +++ b/variants/tlora_c6/variant.h @@ -0,0 +1,21 @@ +#define I2C_SDA 8 // I2C pins for this board +#define I2C_SCL 9 + +#define LED_PIN 7 // If defined we will blink this LED +#define LED_STATE_ON 0 // State when LED is lit + +#define USE_SX1262 +#define LORA_SCK 6 +#define LORA_MISO 1 +#define LORA_MOSI 0 +#define LORA_CS 18 +#define LORA_RESET 21 +#define SX126X_CS LORA_CS +#define SX126X_DIO1 23 +#define SX126X_DIO2 20 +#define SX126X_BUSY 22 +#define SX126X_RESET LORA_RESET +#define SX126X_RXEN 15 +#define SX126X_TXEN 14 +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 diff --git a/variants/tlora_t3s3_v1/rfswitch.h b/variants/tlora_t3s3_v1/rfswitch.h new file mode 100644 index 000000000..19080cec6 --- /dev/null +++ b/variants/tlora_t3s3_v1/rfswitch.h @@ -0,0 +1,11 @@ +#include "RadioLib.h" + +static const uint32_t rfswitch_dio_pins[] = {RADIOLIB_LR11X0_DIO5, RADIOLIB_LR11X0_DIO6, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC}; + +static const Module::RfSwitchMode_t rfswitch_table[] = { + // mode DIO5 DIO6 + {LR11x0::MODE_STBY, {LOW, LOW}}, {LR11x0::MODE_RX, {HIGH, LOW}}, + {LR11x0::MODE_TX, {LOW, HIGH}}, {LR11x0::MODE_TX_HP, {LOW, HIGH}}, + {LR11x0::MODE_TX_HF, {LOW, LOW}}, {LR11x0::MODE_GNSS, {LOW, LOW}}, + {LR11x0::MODE_WIFI, {LOW, LOW}}, END_OF_MODE_TABLE, +}; \ No newline at end of file diff --git a/variants/tlora_t3s3_v1/variant.h b/variants/tlora_t3s3_v1/variant.h index 8a1af1ec2..babe44a58 100644 --- a/variants/tlora_t3s3_v1/variant.h +++ b/variants/tlora_t3s3_v1/variant.h @@ -24,6 +24,7 @@ #define USE_RF95 // RFM95/SX127x #define USE_SX1262 #define USE_SX1280 +#define USE_LR1121 #define LORA_SCK 5 #define LORA_MISO 3 @@ -60,3 +61,19 @@ #define SX128X_TXEN 10 #define SX128X_MAX_POWER 3 #endif + +// LR1121 +#ifdef USE_LR1121 +#define LR1121_IRQ_PIN 36 +#define LR1121_NRESET_PIN LORA_RESET +#define LR1121_BUSY_PIN LORA_DIO2 +#define LR1121_SPI_NSS_PIN LORA_CS +#define LR1121_SPI_SCK_PIN LORA_SCK +#define LR1121_SPI_MOSI_PIN LORA_MOSI +#define LR1121_SPI_MISO_PIN LORA_MISO +#define LR11X0_DIO3_TCXO_VOLTAGE 3.0 +#define LR11X0_DIO_AS_RF_SWITCH +#endif + +#define HAS_SDCARD // Have SPI interface SD card slot +#define SDCARD_USE_SPI1 \ No newline at end of file diff --git a/variants/tracker-t1000-e/rfswitch.h b/variants/tracker-t1000-e/rfswitch.h new file mode 100644 index 000000000..e229d77cf --- /dev/null +++ b/variants/tracker-t1000-e/rfswitch.h @@ -0,0 +1,13 @@ +#include "RadioLib.h" +#include "nrf.h" + +static const uint32_t rfswitch_dio_pins[Module::RFSWITCH_MAX_PINS] = {RADIOLIB_LR11X0_DIO5, RADIOLIB_LR11X0_DIO6, + RADIOLIB_LR11X0_DIO7, RADIOLIB_LR11X0_DIO8, RADIOLIB_NC}; + +static const Module::RfSwitchMode_t rfswitch_table[] = { + // mode DIO5 DIO6 DIO7 DIO8 + {LR11x0::MODE_STBY, {LOW, LOW, LOW, LOW}}, {LR11x0::MODE_RX, {HIGH, LOW, LOW, HIGH}}, + {LR11x0::MODE_TX, {HIGH, HIGH, LOW, HIGH}}, {LR11x0::MODE_TX_HP, {LOW, HIGH, LOW, HIGH}}, + {LR11x0::MODE_TX_HF, {LOW, LOW, LOW, LOW}}, {LR11x0::MODE_GNSS, {LOW, LOW, HIGH, LOW}}, + {LR11x0::MODE_WIFI, {LOW, LOW, LOW, LOW}}, END_OF_MODE_TABLE, +}; \ No newline at end of file diff --git a/variants/tracker-t1000-e/variant.h b/variants/tracker-t1000-e/variant.h index 470edd08c..b8eb59b31 100644 --- a/variants/tracker-t1000-e/variant.h +++ b/variants/tracker-t1000-e/variant.h @@ -93,7 +93,7 @@ extern "C" { #define USE_LR1110 #define LR1110_IRQ_PIN LORA_DIO1 -#define LR1110_NRESER_PIN LORA_RESET +#define LR1110_NRESET_PIN LORA_RESET #define LR1110_BUSY_PIN LORA_DIO2 #define LR1110_SPI_NSS_PIN LORA_CS #define LR1110_SPI_SCK_PIN LORA_SCK diff --git a/variants/wio-e5/platformio.ini b/variants/wio-e5/platformio.ini index 51591d569..29c4a0a37 100644 --- a/variants/wio-e5/platformio.ini +++ b/variants/wio-e5/platformio.ini @@ -28,6 +28,9 @@ build_flags = -DHAL_TIM_MODULE_DISABLED -DHAL_WWDG_MODULE_DISABLED -DHAL_EXTI_MODULE_DISABLED + -DRADIOLIB_EXCLUDE_SX128X=1 + -DRADIOLIB_EXCLUDE_SX127X=1 + -DRADIOLIB_EXCLUDE_LR11X0=1 ; -D PIO_FRAMEWORK_ARDUINO_NANOLIB_FLOAT_PRINTF upload_port = stlink \ No newline at end of file diff --git a/variants/wio-sdk-wm1110/rfswitch.h b/variants/wio-sdk-wm1110/rfswitch.h new file mode 100644 index 000000000..cda6364f5 --- /dev/null +++ b/variants/wio-sdk-wm1110/rfswitch.h @@ -0,0 +1,15 @@ +#include "RadioLib.h" +#include "nrf.h" + +// set RF switch configuration for Wio WM1110 +// Wio WM1110 uses DIO5 and DIO6 for RF switching + +static const uint32_t rfswitch_dio_pins[] = {RADIOLIB_LR11X0_DIO5, RADIOLIB_LR11X0_DIO6, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC}; + +static const Module::RfSwitchMode_t rfswitch_table[] = { + // mode DIO5 DIO6 + {LR11x0::MODE_STBY, {LOW, LOW}}, {LR11x0::MODE_RX, {HIGH, LOW}}, + {LR11x0::MODE_TX, {HIGH, HIGH}}, {LR11x0::MODE_TX_HP, {LOW, HIGH}}, + {LR11x0::MODE_TX_HF, {LOW, LOW}}, {LR11x0::MODE_GNSS, {LOW, LOW}}, + {LR11x0::MODE_WIFI, {LOW, LOW}}, END_OF_MODE_TABLE, +}; diff --git a/variants/wio-sdk-wm1110/variant.h b/variants/wio-sdk-wm1110/variant.h index 8f66b1f8c..b6e5c79df 100644 --- a/variants/wio-sdk-wm1110/variant.h +++ b/variants/wio-sdk-wm1110/variant.h @@ -95,7 +95,7 @@ extern "C" { #define USE_LR1110 #define LR1110_IRQ_PIN LORA_DIO1 -#define LR1110_NRESER_PIN LORA_RESET +#define LR1110_NRESET_PIN LORA_RESET #define LR1110_BUSY_PIN LORA_DIO2 #define LR1110_SPI_NSS_PIN LORA_CS #define LR1110_SPI_SCK_PIN LORA_SCK diff --git a/variants/wio-t1000-s/rfswitch.h b/variants/wio-t1000-s/rfswitch.h new file mode 100644 index 000000000..e229d77cf --- /dev/null +++ b/variants/wio-t1000-s/rfswitch.h @@ -0,0 +1,13 @@ +#include "RadioLib.h" +#include "nrf.h" + +static const uint32_t rfswitch_dio_pins[Module::RFSWITCH_MAX_PINS] = {RADIOLIB_LR11X0_DIO5, RADIOLIB_LR11X0_DIO6, + RADIOLIB_LR11X0_DIO7, RADIOLIB_LR11X0_DIO8, RADIOLIB_NC}; + +static const Module::RfSwitchMode_t rfswitch_table[] = { + // mode DIO5 DIO6 DIO7 DIO8 + {LR11x0::MODE_STBY, {LOW, LOW, LOW, LOW}}, {LR11x0::MODE_RX, {HIGH, LOW, LOW, HIGH}}, + {LR11x0::MODE_TX, {HIGH, HIGH, LOW, HIGH}}, {LR11x0::MODE_TX_HP, {LOW, HIGH, LOW, HIGH}}, + {LR11x0::MODE_TX_HF, {LOW, LOW, LOW, LOW}}, {LR11x0::MODE_GNSS, {LOW, LOW, HIGH, LOW}}, + {LR11x0::MODE_WIFI, {LOW, LOW, LOW, LOW}}, END_OF_MODE_TABLE, +}; \ No newline at end of file diff --git a/variants/wio-t1000-s/variant.h b/variants/wio-t1000-s/variant.h index fa6ea4abc..eb6a34d6c 100644 --- a/variants/wio-t1000-s/variant.h +++ b/variants/wio-t1000-s/variant.h @@ -94,7 +94,7 @@ extern "C" { #define USE_LR1110 #define LR1110_IRQ_PIN LORA_DIO1 -#define LR1110_NRESER_PIN LORA_RESET +#define LR1110_NRESET_PIN LORA_RESET #define LR1110_BUSY_PIN LORA_DIO2 #define LR1110_SPI_NSS_PIN LORA_CS #define LR1110_SPI_SCK_PIN LORA_SCK @@ -103,7 +103,6 @@ extern "C" { #define LR11X0_DIO3_TCXO_VOLTAGE 1.6 #define LR11X0_DIO_AS_RF_SWITCH -#define LR11X0_DIO_RF_SWITCH_CONFIG 0x0f, 0x0, 0x09, 0x0B, 0x0A, 0x0, 0x4, 0x0 #define HAS_GPS 1 #define GNSS_AIROHA @@ -158,4 +157,4 @@ extern "C" { * Arduino objects - C++ only *----------------------------------------------------------------------------*/ -#endif // _VARIANT_WIO_SDK_WM1110_ +#endif // _VARIANT_WIO_SDK_WM1110_ \ No newline at end of file diff --git a/variants/wio-tracker-wm1110/rfswitch.h b/variants/wio-tracker-wm1110/rfswitch.h new file mode 100644 index 000000000..cda6364f5 --- /dev/null +++ b/variants/wio-tracker-wm1110/rfswitch.h @@ -0,0 +1,15 @@ +#include "RadioLib.h" +#include "nrf.h" + +// set RF switch configuration for Wio WM1110 +// Wio WM1110 uses DIO5 and DIO6 for RF switching + +static const uint32_t rfswitch_dio_pins[] = {RADIOLIB_LR11X0_DIO5, RADIOLIB_LR11X0_DIO6, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC}; + +static const Module::RfSwitchMode_t rfswitch_table[] = { + // mode DIO5 DIO6 + {LR11x0::MODE_STBY, {LOW, LOW}}, {LR11x0::MODE_RX, {HIGH, LOW}}, + {LR11x0::MODE_TX, {HIGH, HIGH}}, {LR11x0::MODE_TX_HP, {LOW, HIGH}}, + {LR11x0::MODE_TX_HF, {LOW, LOW}}, {LR11x0::MODE_GNSS, {LOW, LOW}}, + {LR11x0::MODE_WIFI, {LOW, LOW}}, END_OF_MODE_TABLE, +}; diff --git a/variants/wio-tracker-wm1110/variant.h b/variants/wio-tracker-wm1110/variant.h index de7da9911..32e84485d 100644 --- a/variants/wio-tracker-wm1110/variant.h +++ b/variants/wio-tracker-wm1110/variant.h @@ -91,7 +91,7 @@ extern "C" { #define USE_LR1110 #define LR1110_IRQ_PIN LORA_DIO1 -#define LR1110_NRESER_PIN LORA_RESET +#define LR1110_NRESET_PIN LORA_RESET #define LR1110_BUSY_PIN LORA_DIO2 #define LR1110_SPI_NSS_PIN LORA_CS #define LR1110_SPI_SCK_PIN LORA_SCK diff --git a/version.properties b/version.properties index 95d3d2538..df7ba70c7 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 5 -build = 0 +build = 6