diff --git a/.github/actions/build-variant/action.yml b/.github/actions/build-variant/action.yml new file mode 100644 index 000000000..f9410eb03 --- /dev/null +++ b/.github/actions/build-variant/action.yml @@ -0,0 +1,90 @@ +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" + +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.board }}-${{ steps.version.outputs.version }}.zip + overwrite: true + path: | + ${{ inputs.artifact-paths }} 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/workflows/build_esp32.yml b/.github/workflows/build_esp32.yml index 4cbb4c7a4..041191d34 100644 --- a/.github/workflows/build_esp32.yml +++ b/.github/workflows/build_esp32.yml @@ -12,52 +12,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 - 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 diff --git a/.github/workflows/build_esp32_c3.yml b/.github/workflows/build_esp32_c3.yml index 07727d711..ddc2e2859 100644 --- a/.github/workflows/build_esp32_c3.yml +++ b/.github/workflows/build_esp32_c3.yml @@ -14,50 +14,21 @@ 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 diff --git a/.github/workflows/build_esp32_s3.yml b/.github/workflows/build_esp32_s3.yml index 10773833e..29857ef17 100644 --- a/.github/workflows/build_esp32_s3.yml +++ b/.github/workflows/build_esp32_s3.yml @@ -12,50 +12,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-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 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..606cb8a3e 100644 --- a/.github/workflows/build_nrf52.yml +++ b/.github/workflows/build_nrf52.yml @@ -12,23 +12,15 @@ 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 diff --git a/.github/workflows/build_rpi2040.yml b/.github/workflows/build_rpi2040.yml index 6e258fe2a..b0508877d 100644 --- a/.github/workflows/build_rpi2040.yml +++ b/.github/workflows/build_rpi2040.yml @@ -12,22 +12,14 @@ 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 diff --git a/.github/workflows/build_stm32.yml b/.github/workflows/build_stm32.yml index d13c52c8a..6858812e1 100644 --- a/.github/workflows/build_stm32.yml +++ b/.github/workflows/build_stm32.yml @@ -12,22 +12,14 @@ 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 Raspberry Pi 2040 + 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 diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index 9b97dcb2e..ca9f1d37f 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -51,6 +51,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 @@ -124,6 +125,7 @@ jobs: after-checks: runs-on: ubuntu-latest + if: ${{ github.event_name != 'workflow_dispatch' }} needs: [check] steps: - name: Checkout code @@ -231,7 +233,7 @@ jobs: release-artifacts: runs-on: ubuntu-latest if: ${{ github.event_name == 'workflow_dispatch' }} - needs: [gather-artifacts, after-checks] + needs: [gather-artifacts] steps: - name: Checkout uses: actions/checkout@v4 @@ -251,12 +253,6 @@ jobs: 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 @@ -314,6 +310,12 @@ jobs: asset_name: debug-elfs-${{ steps.version.outputs.version }}.zip asset_content_type: application/zip + - uses: actions/download-artifact@v4 + with: + pattern: meshtasticd_${{ steps.version.outputs.version }}_*.deb + merge-multiple: true + path: ./output + - name: Add raspbian aarch64 .deb uses: actions/upload-release-asset@v1 env: @@ -349,7 +351,35 @@ jobs: 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 + + - name: Checkout meshtastic/meshtastic.github.io + uses: actions/checkout@v4 + with: + repository: meshtastic/meshtastic.github.io + token: ${{ secrets.ARTIFACTS_TOKEN }} + path: meshtastic.github.io + + - name: Display structure of downloaded files + run: ls -R + + - name: Extract firmware.zip + run: | + unzip ./firmware-${{ steps.version.outputs.version }}.zip -d meshtastic.github.io/firmware-${{ steps.version.outputs.version }} + + - name: Display structure of downloaded files + run: ls -R + + - name: Commit and push changes + run: | + cd meshtastic.github.io + find . -type f -name 'meshtasticd_*' -exec rm -f {} + + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git add . + git commit -m "Add firmware version ${{ steps.version.outputs.version }}" + git push 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 2575cbf01..9013f1c74 100644 --- a/.github/workflows/sec_sast_semgrep_pull.yml +++ b/.github/workflows/sec_sast_semgrep_pull.yml @@ -6,7 +6,7 @@ jobs: semgrep-diff: 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..925b91215 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..7ce767370 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: @@ -26,8 +26,9 @@ jobs: ./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/arch/esp32/esp32.ini b/arch/esp32/esp32.ini index 0dd6cbc1d..13360be78 100644 --- a/arch/esp32/esp32.ini +++ b/arch/esp32/esp32.ini @@ -5,7 +5,7 @@ custom_esp32_kind = esp32 platform = platformio/espressif32@6.7.0 build_src_filter = - ${arduino_base.build_src_filter} - - - - - + ${arduino_base.build_src_filter} - - - - - upload_speed = 921600 debug_init_break = tbreak setup diff --git a/arch/nrf52/nrf52.ini b/arch/nrf52/nrf52.ini index 503da2aab..04880d540 100644 --- a/arch/nrf52/nrf52.ini +++ b/arch/nrf52/nrf52.ini @@ -16,7 +16,7 @@ build_flags = -DLFS_NO_ASSERT ; Disable LFS assertions , see https://github.com/meshtastic/firmware/pull/3818 build_src_filter = - ${arduino_base.build_src_filter} - - - - - - - - - - + ${arduino_base.build_src_filter} - - - - - - - - - - lib_deps= ${arduino_base.lib_deps} diff --git a/arch/portduino/portduino.ini b/arch/portduino/portduino.ini index 482b1f9c5..30cc190d2 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 = - - - - - + - - - + diff --git a/arch/rp2040/rp2040.ini b/arch/rp2xx0/rp2040.ini similarity index 97% rename from arch/rp2040/rp2040.ini rename to arch/rp2xx0/rp2040.ini index dd3a4d7ff..d3f27a676 100644 --- a/arch/rp2040/rp2040.ini +++ b/arch/rp2xx0/rp2040.ini @@ -8,7 +8,7 @@ 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/rp2xx0/rp2350.ini b/arch/rp2xx0/rp2350.ini new file mode 100644 index 000000000..96ed0cb21 --- /dev/null +++ b/arch/rp2xx0/rp2350.ini @@ -0,0 +1,23 @@ +; Common settings for rp2040 Processor based targets +[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#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/rp2xx0 + -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/stm32/stm32.ini b/arch/stm32/stm32.ini index 050dbf7f0..4be290015 100644 --- a/arch/stm32/stm32.ini +++ b/arch/stm32/stm32.ini @@ -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 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/protobufs b/protobufs index 5709c0a05..9651aa59e 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 5709c0a05eaefccbc9cb8ed3917adbf5fd134197 +Subproject commit 9651aa59eaa3b54d801b9c251efcfe842790db32 diff --git a/src/Power.cpp b/src/Power.cpp index 61a6c987d..b3a67abd5 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 @@ -244,7 +246,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; diff --git a/src/SerialConsole.cpp b/src/SerialConsole.cpp index b911e15da..15366d222 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" @@ -47,7 +49,7 @@ SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port), con #if defined(ARCH_NRF52) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(ARCH_RP2040) time_t timeout = millis(); while (!Port) { - if ((millis() - timeout) < 5000) { + if (Throttle::isWithinTimespanMs(timeout, FIVE_SECONDS_MS)) { delay(100); } else { break; @@ -72,8 +74,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 +121,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/configuration.h b/src/configuration.h index 349bd2870..0b470eef3 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -210,6 +210,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 diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index f09eb3b95..c0e70503b 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 @@ -334,11 +334,10 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) // Check register 0x0F for 0x3300 response to ID LIS3DH chip. registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0F), 2); - if (registerValue == 0x3300) { + if (registerValue == 0x3300 || registerValue == 0x3333) { // RAK4631 WisBlock has LIS3DH register at 0x3333 type = LIS3DH; LOG_INFO("LIS3DH accelerometer found\n"); } - break; } case SHT31_4x_ADDR: @@ -423,7 +422,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; @@ -435,4 +434,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 01fa65816..285f3a2fd 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -6,6 +6,7 @@ #include "NodeDB.h" #include "PowerMon.h" #include "RTC.h" +#include "Throttle.h" #include "main.h" // pmu_found #include "sleep.h" @@ -206,7 +207,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(); @@ -275,7 +276,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"); @@ -332,7 +333,7 @@ int GPS::getACK(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t uint32_t startTime = millis(); uint16_t needRead; - while (millis() - startTime < waitMillis) { + while (Throttle::isWithinTimespanMs(startTime, waitMillis)) { if (_serial_gps->available()) { int c = _serial_gps->read(); switch (ubxFrameCounter) { @@ -525,268 +526,95 @@ bool GPS::setup() delay(250); _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, "Unable to disable text info messages.\n", 500); + SEND_UBX_PACKET(0x06, 0x39, _message_JAM_6_7, "Unable to enable interference resistance.\n", 500); + SEND_UBX_PACKET(0x06, 0x23, _message_NAVX5, "Unable to configure NAVX5 settings.\n", 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, "Unable to set GPS update rate.\n", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_GLL, "Unable to disable NMEA GLL.\n", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_GSA, "Unable to Enable NMEA GSA.\n", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_GSV, "Unable to disable NMEA GSV.\n", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_VTG, "Unable to disable NMEA VTG.\n", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_RMC, "Unable to enable NMEA RMC.\n", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_GGA, "Unable to enable NMEA GGA.\n", 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, "Unable to enable powersaving ECO mode for Neo-6.\n", 500); + SEND_UBX_PACKET(0x06, 0x3B, _message_CFG_PM2, "Unable to enable powersaving details for GPS.\n", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_AID, "Unable to disable UBX-AID.\n", 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 (gnssModel == GNSS_MODEL_UBLOX7 || gnssModel == GNSS_MODEL_UBLOX8 || gnssModel == 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("Unable to 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, "Unable to disable text info messages.\n", 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, "Unable to enable interference resistance.\n", 500); + + clearBuffer(); + SEND_UBX_PACKET(0x06, 0x23, _message_NAVX5_8, "Unable to configure NAVX5_8 settings.\n", 500); + } else { // 6,7,9 + SEND_UBX_PACKET(0x06, 0x39, _message_JAM_6_7, "Unable to enable interference resistance.\n", 500); + SEND_UBX_PACKET(0x06, 0x23, _message_NAVX5, "Unable to configure NAVX5 settings.\n", 500); + } + // Turn off unwanted NMEA messages, set update rate + SEND_UBX_PACKET(0x06, 0x08, _message_1HZ, "Unable to set GPS update rate.\n", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_GLL, "Unable to disable NMEA GLL.\n", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_GSA, "Unable to Enable NMEA GSA.\n", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_GSV, "Unable to disable NMEA GSV.\n", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_VTG, "Unable to disable NMEA VTG.\n", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_RMC, "Unable to enable NMEA RMC.\n", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_GGA, "Unable to enable NMEA GGA.\n", 500); + + if (uBloxProtocolVersion >= 18) { + clearBuffer(); + SEND_UBX_PACKET(0x06, 0x86, _message_PMS, "Unable to enable powersaving for GPS.\n", 500); + SEND_UBX_PACKET(0x06, 0x3B, _message_CFG_PM2, "Unable to enable powersaving details for GPS.\n", 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, "Unable to enable NMEA 4.10.\n", 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, "Unable to enable powersaving mode for GPS.\n", 500); + SEND_UBX_PACKET(0x06, 0x3B, _message_CFG_PM2, "Unable to enable powersaving details for GPS.\n", 500); } + msglen = makeUBXPacket(0x06, 0x09, sizeof(_message_SAVE), _message_SAVE); _serial_gps->write(UBXscratch, msglen); if (getACK(0x06, 0x09, 2000) != GNSS_RESPONSE_OK) { @@ -794,6 +622,55 @@ 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, "Unable to disable NMEA messages in M10 RAM.\n", 300); + delay(750); + clearBuffer(); + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_DISABLE_NMEA_BBR, "Unable to disable NMEA messages in M10 BBR.\n", 300); + delay(750); + clearBuffer(); + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_DISABLE_TXT_INFO_RAM, + "Unable to disable Info messages for M10 GPS RAM.\n", 300); + delay(750); + // Next disable Info txt messages in BBR layer + clearBuffer(); + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_DISABLE_TXT_INFO_BBR, + "Unable to disable Info messages for M10 GPS BBR.\n", 300); + delay(750); + // Do M10 configuration for Power Management. + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_PM_RAM, "Unable to enable powersaving for M10 GPS RAM.\n", 300); + delay(750); + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_PM_BBR, "Unable to enable powersaving for M10 GPS BBR.\n", 300); + delay(750); + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_ITFM_RAM, "Unable to enable Jamming detection M10 GPS RAM.\n", 300); + delay(750); + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_ITFM_BBR, "Unable to enable Jamming detection M10 GPS BBR.\n", 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, "Unable to disable SBAS M10 GPS RAM.\n", 300); + delay(750); // will cause a receiver restart so wait a bit + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_DISABLE_SBAS_BBR, "Unable to disable SBAS M10 GPS BBR.\n", 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, "Unable to enable messages for M10 GPS BBR.\n", 300); + delay(750); + // Next enable wanted NMEA messages in RAM layer + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_ENABLE_NMEA_RAM, "Unable to enable messages for M10 GPS RAM.\n", 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; } @@ -949,7 +826,8 @@ void GPS::setPowerPMU(bool on) void GPS::setPowerUBLOX(bool on, uint32_t sleepMs) { // Abort: if not UBLOX hardware - if (gnssModel != GNSS_MODEL_UBLOX) + if (!(gnssModel == GNSS_MODEL_UBLOX6 || gnssModel == GNSS_MODEL_UBLOX7 || gnssModel == GNSS_MODEL_UBLOX8 || + gnssModel == GNSS_MODEL_UBLOX9 || gnssModel == GNSS_MODEL_UBLOX10)) return; // If waking @@ -972,7 +850,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); @@ -1031,7 +909,9 @@ void GPS::down() // 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 (gnssModel == GNSS_MODEL_UBLOX6 || gnssModel == GNSS_MODEL_UBLOX7 || gnssModel == GNSS_MODEL_UBLOX8 || + gnssModel == GNSS_MODEL_UBLOX9 || gnssModel == GNSS_MODEL_UBLOX10) softsleepSupported = true; #ifdef PIN_GPS_STANDBY // L76B, L76K and clones have a standby pin softsleepSupported = true; @@ -1106,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) && + (gnssModel == GNSS_MODEL_UBLOX6 || gnssModel == GNSS_MODEL_UBLOX7 || gnssModel == GNSS_MODEL_UBLOX8 || + gnssModel == GNSS_MODEL_UBLOX9 || gnssModel == 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"); @@ -1335,9 +1217,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; @@ -1352,9 +1234,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() @@ -1528,16 +1421,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 { @@ -1581,16 +1473,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 { @@ -1820,4 +1711,4 @@ void GPS::toggleGpsMode() enable(); } } -#endif // Exclude GPS +#endif // Exclude GPS \ No newline at end of file diff --git a/src/gps/GPS.h b/src/gps/GPS.h index 3423edb68..317d80544 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -26,7 +26,11 @@ 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, @@ -130,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[]; @@ -310,4 +315,4 @@ class GPS : private concurrency::OSThread }; extern GPS *gps; -#endif // Exclude GPS +#endif // Exclude GPS \ No newline at end of file diff --git a/src/gps/RTC.cpp b/src/gps/RTC.cpp index 728284242..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(); @@ -58,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(); @@ -127,7 +128,7 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate) } else if (q == RTCQualityGPS) { shouldSet = true; LOG_DEBUG("Reapplying GPS time: %ld secs\n", printableEpoch); - } else if (q == RTCQualityNTP && (now - lastSetMsec) > (12 * 60 * 60 * 1000UL)) { + } 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); @@ -150,7 +151,7 @@ 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(); @@ -164,7 +165,7 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate) 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(); diff --git a/src/gps/ubx.h b/src/gps/ubx.h index 0852c331d..c73a7efb3 100644 --- a/src/gps/ubx.h +++ b/src/gps/ubx.h @@ -1,3 +1,10 @@ +#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(#ERRMSG); \ + } + // Power Management uint8_t GPS::_message_PMREQ[] PROGMEM = { @@ -316,6 +323,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 +349,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 ae09ee408..19b20e8dc 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 @@ -117,6 +118,7 @@ static bool heartbeat = false; #define SCREEN_HEIGHT display->getHeight() #include "graphics/ScreenFonts.h" +#include #define getStringCenteredX(s) ((SCREEN_WIDTH - display->getStringWidth(s)) / 2) @@ -1670,6 +1672,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(); @@ -1726,6 +1733,8 @@ void Screen::setup() #if defined(ST7701_CS) || defined(ST7735_CS) || defined(ILI9341_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 @@ -1942,7 +1951,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 @@ -2435,8 +2444,8 @@ 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 (!Throttle::isWithinTimespanMs(storeForwardModule->lastHeartbeat, + (storeForwardModule->heartbeatInterval * 1200))) { // no heartbeat, overlap a bit #if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || defined(ST7789_CS) || \ defined(USE_ST7789) || defined(HX8357_CS)) && \ !defined(DISPLAY_FORCE_SMALL_FONTS) 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/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 7b7a2f3ec..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; } diff --git a/src/input/cardKbI2cImpl.cpp b/src/input/cardKbI2cImpl.cpp index 8aaebcb45..f1df6b137 100644 --- a/src/input/cardKbI2cImpl.cpp +++ b/src/input/cardKbI2cImpl.cpp @@ -16,7 +16,7 @@ void CardKbI2cImpl::init() 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); diff --git a/src/input/kbI2cBase.cpp b/src/input/kbI2cBase.cpp index 2692fc80d..1d8154bcf 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) { diff --git a/src/main.cpp b/src/main.cpp index e24ba68b3..6f0099cb1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,16 +11,16 @@ #include "airtime.h" #include "buzz.h" -#include "error.h" -#include "power.h" -// #include "debug.h" #include "FSCommon.h" #include "Led.h" #include "RTC.h" #include "SPILock.h" +#include "Throttle.h" #include "concurrency/OSThread.h" #include "concurrency/Periodic.h" #include "detect/ScanI2C.h" +#include "error.h" +#include "power.h" #if !MESHTASTIC_EXCLUDE_I2C #include "detect/ScanI2CTwoWire.h" @@ -38,7 +38,6 @@ #include "target_specific.h" #include #include -// #include #ifdef ARCH_ESP32 #if !MESHTASTIC_EXCLUDE_WEBSERVER @@ -360,6 +359,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) @@ -427,6 +428,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) @@ -621,7 +624,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 && (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)) + LOG_DEBUG("Tracker/Sensor: Skipping start melody\n"); + else + playStartMelody(); // fixed screen override? if (config.display.oled != meshtastic_Config_DisplayConfig_OledType_OLED_AUTO) @@ -1101,10 +1110,6 @@ void loop() { runASAP = false; - // axpDebugOutput.loop(); - - // heap_caps_check_integrity_all(true); // FIXME - disable this expensive check - #ifdef ARCH_ESP32 esp32Loop(); #endif @@ -1113,33 +1118,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/mesh/LR11x0Interface.cpp b/src/mesh/LR11x0Interface.cpp index c0742f241..e237c354f 100644 --- a/src/mesh/LR11x0Interface.cpp +++ b/src/mesh/LR11x0Interface.cpp @@ -1,7 +1,9 @@ #include "LR11x0Interface.h" +#include "Throttle.h" #include "configuration.h" #include "error.h" #include "mesh/NodeDB.h" + #ifdef ARCH_PORTDUINO #include "PortduinoGlue.h" #endif @@ -275,15 +277,15 @@ template bool LR11x0Interface::isActivelyReceiving() 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)) { + activeReceiveStart = millis(); + } else if (!Throttle::isWithinTimespanMs(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) { + } 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"); diff --git a/src/mesh/MeshService.h b/src/mesh/MeshService.h index ea1c4e345..e78c2be2c 100644 --- a/src/mesh/MeshService.h +++ b/src/mesh/MeshService.h @@ -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); diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index dca639070..2820cafd4 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -657,9 +657,10 @@ void NodeDB::pickNewNodeNum() 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, 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); + 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); 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 121687c49..103572990 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -25,6 +25,7 @@ #if !MESHTASTIC_EXCLUDE_MQTT #include "mqtt/MQTT.h" #endif +#include "Throttle.h" #include PhoneAPI::PhoneAPI() @@ -61,9 +62,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); @@ -71,8 +74,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; } } @@ -404,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); @@ -443,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) { @@ -474,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 */ @@ -508,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; @@ -551,7 +570,6 @@ void PhoneAPI::sendNotification(meshtastic_LogRecord_Level level, uint32_t reply cn->time = getValidTime(RTCQualityFromNet); strncpy(cn->message, message, sizeof(cn->message)); service->sendClientNotification(cn); - delete cn; } /** @@ -561,12 +579,12 @@ bool PhoneAPI::handleToRadioPacket(meshtastic_MeshPacket &p) { printPacket("PACKET FROM PHONE", &p); if (p.decoded.portnum == meshtastic_PortNum_TRACEROUTE_APP && lastPortNumToRadio[p.decoded.portnum] && - (millis() - lastPortNumToRadio[p.decoded.portnum]) < (THIRTY_SECONDS_MS)) { + 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"); return false; } else if (p.decoded.portnum == meshtastic_PortNum_POSITION_APP && lastPortNumToRadio[p.decoded.portnum] && - (millis() - lastPortNumToRadio[p.decoded.portnum]) < (FIVE_SECONDS_MS)) { + Throttle::isWithinTimespanMs(lastPortNumToRadio[p.decoded.portnum], FIVE_SECONDS_MS)) { LOG_WARN("Rate limiting portnum %d\n", p.decoded.portnum); sendNotification(meshtastic_LogRecord_Level_WARNING, p.id, "Position can only be sent once every 5 seconds"); return false; diff --git a/src/mesh/PhoneAPI.h b/src/mesh/PhoneAPI.h index 1e09b9535..cf6f55416 100644 --- a/src/mesh/PhoneAPI.h +++ b/src/mesh/PhoneAPI.h @@ -147,11 +147,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(); @@ -159,6 +154,8 @@ class PhoneAPI void releaseMqttClientProxyPhonePacket(); + void releaseClientNotification(); + /// begin a new connection void handleStartConfig(); diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 7b6b4f5fa..6fca67188 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -151,7 +151,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() { @@ -326,7 +326,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() diff --git a/src/mesh/RadioInterface.h b/src/mesh/RadioInterface.h index f1016e3d8..129861441 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 @@ -89,7 +91,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]; + uint8_t radiobuf[MAX_LORA_PAYLOAD_LEN + 1]; /** * Enqueue a received packet for the registered receiver diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp index f299ebff2..6cdb3b99e 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. diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 3cf30519b..a993ee7c0 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]; +static uint8_t ScratchEncrypted[MAX_LORA_PAYLOAD_LEN + 1]; /** * Constructor @@ -330,13 +330,13 @@ bool perhapsDecode(meshtastic_MeshPacket *p) // 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) { + 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; @@ -475,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); @@ -499,7 +499,7 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) 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) { @@ -508,7 +508,7 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) 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; diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index 6d23206bd..2c6096062 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -6,6 +6,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 @@ -319,15 +321,15 @@ template bool SX126xInterface::isActivelyReceiving() 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)) { + activeReceiveStart = millis(); + } else if (!Throttle::isWithinTimespanMs(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) { + } 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"); @@ -359,4 +361,4 @@ template bool SX126xInterface::sleep() #endif return true; -} +} \ No newline at end of file diff --git a/src/mesh/SX128xInterface.cpp b/src/mesh/SX128xInterface.cpp index 9ff9ac2d7..270356e26 100644 --- a/src/mesh/SX128xInterface.cpp +++ b/src/mesh/SX128xInterface.cpp @@ -1,4 +1,5 @@ #include "SX128xInterface.h" +#include "Throttle.h" #include "configuration.h" #include "error.h" #include "mesh/NodeDB.h" @@ -294,15 +295,15 @@ template bool SX128xInterface::isActivelyReceiving() // 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)) { + activeReceiveStart = millis(); + } else if (!Throttle::isWithinTimespanMs(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) { + } 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"); diff --git a/src/mesh/StreamAPI.cpp b/src/mesh/StreamAPI.cpp index 9f59aa971..c3d85ed33 100644 --- a/src/mesh/StreamAPI.cpp +++ b/src/mesh/StreamAPI.cpp @@ -1,6 +1,7 @@ #include "StreamAPI.h" #include "PowerFSM.h" #include "RTC.h" +#include "Throttle.h" #include "configuration.h" #define START1 0x94 @@ -20,10 +21,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 +71,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/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 921dfa55b..9ba69c612 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -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,13 @@ 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, /* ------------------------------------------------------------------------------------------------------------------------------------------ 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. ------------------------------------------------------------------------------------------------------------------------------------------ */ diff --git a/src/mesh/wifi/WiFiAPClient.cpp b/src/mesh/wifi/WiFiAPClient.cpp index 07b03222e..d1169dc3b 100644 --- a/src/mesh/wifi/WiFiAPClient.cpp +++ b/src/mesh/wifi/WiFiAPClient.cpp @@ -23,6 +23,7 @@ static void WiFiEvent(WiFiEvent_t event); #endif #ifndef DISABLE_NTP +#include "Throttle.h" #include #endif @@ -142,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"); @@ -420,4 +421,4 @@ uint8_t getWifiDisconnectReason() { return wifiDisconnectReason; } -#endif +#endif \ No newline at end of file diff --git a/src/meshUtils.cpp b/src/meshUtils.cpp index c6f2c69b4..4819f6ed7 100644 --- a/src/meshUtils.cpp +++ b/src/meshUtils.cpp @@ -60,10 +60,17 @@ 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"); + char *messageBuffer; + int labelSize = strlen(label); + if (labelSize < 100 && numbytes < 64) { + 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) diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index 87a3e8927..a1b9c4dc0 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -27,6 +27,7 @@ #endif #include "graphics/ScreenFonts.h" +#include // Remove Canned message screen if no action is taken for some milliseconds #define INACTIVATE_AFTER_MS 20000 @@ -422,7 +423,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; diff --git a/src/modules/DetectionSensorModule.cpp b/src/modules/DetectionSensorModule.cpp index 20d91a381..670fd3208 100644 --- a/src/modules/DetectionSensorModule.cpp +++ b/src/modules/DetectionSensorModule.cpp @@ -5,6 +5,7 @@ #include "PowerFSM.h" #include "configuration.h" #include "main.h" +#include DetectionSensorModule *detectionSensorModule; #define GPIO_POLLING_INTERVAL 100 @@ -49,7 +50,8 @@ 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) && + if (!Throttle::isWithinTimespanMs(lastSentToMesh, + Default::getConfiguredOrDefaultMs(moduleConfig.detection_sensor.minimum_broadcast_secs)) && hasDetectionEvent()) { sendDetectionMessage(); return DELAYED_INTERVAL; @@ -58,8 +60,9 @@ int32_t DetectionSensorModule::runOnce() // 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)) { + !Throttle::isWithinTimespanMs(lastSentToMesh, + Default::getConfiguredOrDefaultMs(moduleConfig.detection_sensor.state_broadcast_secs, + default_telemetry_broadcast_interval_secs))) { sendCurrentStateMessage(); return DELAYED_INTERVAL; } diff --git a/src/modules/NeighborInfoModule.cpp b/src/modules/NeighborInfoModule.cpp index 218fb8801..55ed46b5e 100644 --- a/src/modules/NeighborInfoModule.cpp +++ b/src/modules/NeighborInfoModule.cpp @@ -3,6 +3,7 @@ #include "MeshService.h" #include "NodeDB.h" #include "RTC.h" +#include NeighborInfoModule *neighborInfoModule; @@ -83,11 +84,11 @@ uint32_t NeighborInfoModule::collectNeighborInfo(meshtastic_NeighborInfo *neighb */ void NeighborInfoModule::cleanUpNeighbors() { - uint32_t now = getTime(); 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 - if ((now - it->last_rx_time > it->node_broadcast_interval_secs * 2) && (it->node_id != my_node_id)) { + if (!Throttle::isWithinTimespanMs(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( neighbors.erase(std::next(it).base())); // Erase the element and update the iterator diff --git a/src/modules/NodeInfoModule.cpp b/src/modules/NodeInfoModule.cpp index cb047a4dc..41f008fb0 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; @@ -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 cb6a58b2e..4ba09385d 100644 --- a/src/modules/PositionModule.cpp +++ b/src/modules/PositionModule.cpp @@ -145,7 +145,7 @@ 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 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..c98f15d40 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 diff --git a/src/modules/RemoteHardwareModule.cpp b/src/modules/RemoteHardwareModule.cpp index 0242b59bc..f6b8b2e90 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 @@ -118,11 +119,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) { + if (!Throttle::isWithinTimespanMs(lastWatchMsec, WATCH_INTERVAL_MSEC)) { uint64_t curVal = digitalReads(watchGpios); - lastWatchMsec = now; + lastWatchMsec = millis(); if (curVal != previousWatch) { previousWatch = curVal; diff --git a/src/modules/SerialModule.cpp b/src/modules/SerialModule.cpp index f0ba64f65..a4dbb072f 100644 --- a/src/modules/SerialModule.cpp +++ b/src/modules/SerialModule.cpp @@ -7,6 +7,7 @@ #include "Router.h" #include "configuration.h" #include +#include /* SerialModule @@ -97,8 +98,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() @@ -182,13 +182,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); @@ -500,7 +500,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/Telemetry/AirQualityTelemetry.cpp b/src/modules/Telemetry/AirQualityTelemetry.cpp index 56d308cfa..0b6be1b7e 100644 --- a/src/modules/Telemetry/AirQualityTelemetry.cpp +++ b/src/modules/Telemetry/AirQualityTelemetry.cpp @@ -12,6 +12,7 @@ #include "Router.h" #include "detect/ScanI2CTwoWire.h" #include "main.h" +#include int32_t AirQualityTelemetryModule::runOnce() { @@ -60,15 +61,14 @@ int32_t AirQualityTelemetryModule::runOnce() if (!moduleConfig.telemetry.air_quality_enabled) 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/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index 31cb2f838..f94f7956b 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -64,6 +64,7 @@ T1000xSensor t1000xSensor; #define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true #include "graphics/ScreenFonts.h" +#include int32_t EnvironmentTelemetryModule::runOnce() { @@ -155,21 +156,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); diff --git a/src/modules/Telemetry/PowerTelemetry.cpp b/src/modules/Telemetry/PowerTelemetry.cpp index 318acf456..a493042a0 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() { @@ -69,20 +70,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); 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/esp32/StoreForwardModule.cpp b/src/modules/esp32/StoreForwardModule.cpp index c696d342a..51ec2a942 100644 --- a/src/modules/esp32/StoreForwardModule.cpp +++ b/src/modules/esp32/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" @@ -29,6 +30,9 @@ StoreForwardModule *storeForwardModule; +uint32_t lastHeartbeat = 0; +uint32_t heartbeatInterval = 60; // Default to 60 seconds, adjust as needed + int32_t StoreForwardModule::runOnce() { #ifdef ARCH_ESP32 @@ -42,7 +46,7 @@ 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"); @@ -127,7 +131,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)) { diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 1b16690e2..ee7af488d 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; @@ -158,7 +159,22 @@ 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 (p->from == 0 || p->from == nodeDB->getNodeNum()) { + 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; } @@ -598,7 +614,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)) { diff --git a/src/nimble/NimbleBluetooth.cpp b/src/nimble/NimbleBluetooth.cpp index d959553a4..03fa80415 100644 --- a/src/nimble/NimbleBluetooth.cpp +++ b/src/nimble/NimbleBluetooth.cpp @@ -124,7 +124,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/architecture.h b/src/platform/esp32/architecture.h index 93630aa8a..90c4c392d 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) diff --git a/src/platform/nrf52/NRF52Bluetooth.cpp b/src/platform/nrf52/NRF52Bluetooth.cpp index 2e9dbc1f8..971019558 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) { 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/rp2040/main-rp2040.cpp b/src/platform/rp2xx0/main-rp2xx0.cpp similarity index 100% rename from src/platform/rp2040/main-rp2040.cpp rename to src/platform/rp2xx0/main-rp2xx0.cpp diff --git a/src/sleep.cpp b/src/sleep.cpp index 27e81ce54..f32d24caa 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" @@ -28,6 +29,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 +170,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 +274,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 +311,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); } diff --git a/variants/diy/platformio.ini b/variants/diy/platformio.ini index 2a55f7a79..f3c22b7a8 100644 --- a/variants/diy/platformio.ini +++ b/variants/diy/platformio.ini @@ -69,4 +69,22 @@ build_flags = ${nrf52840_base.build_flags} 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 e0d8ca0cc..1b06c7f5e 100644 --- a/variants/heltec_mesh_node_t114/platformio.ini +++ b/variants/heltec_mesh_node_t114/platformio.ini @@ -14,4 +14,4 @@ build_src_filter = ${nrf52_base.build_src_filter} +<../variants/heltec_mesh_node 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.h b/variants/heltec_mesh_node_t114/variant.h index 454e66931..2cea3ef2f 100644 --- a/variants/heltec_mesh_node_t114/variant.h +++ b/variants/heltec_mesh_node_t114/variant.h @@ -92,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) 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/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/version.properties b/version.properties index 69c478482..8c8e94bec 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 5 -build = 1 +build = 3