diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 30af24bd2..a2c56fad9 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,7 +1,7 @@ # trunk-ignore-all(terrascan/AC_DOCKER_0002): Known terrascan issue # trunk-ignore-all(hadolint/DL3008): Do not pin apt package versions # trunk-ignore-all(hadolint/DL3013): Do not pin pip package versions -FROM mcr.microsoft.com/devcontainers/cpp:1-debian-12 +FROM mcr.microsoft.com/devcontainers/cpp:2-debian-12 USER root diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index bf1c50982..bc660170c 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -8,7 +8,7 @@ "features": { "ghcr.io/devcontainers/features/python:1": { "installTools": true, - "version": "latest" + "version": "3.13" } }, "customizations": { diff --git a/.github/workflows/build_one_arch.yml b/.github/workflows/build_one_arch.yml index 6d9134941..9c57f8b7d 100644 --- a/.github/workflows/build_one_arch.yml +++ b/.github/workflows/build_one_arch.yml @@ -88,62 +88,6 @@ jobs: if: ${{ !contains(github.ref_name, 'event/') && github.event_name != 'workflow_dispatch' || !contains(github.ref_name, 'event/') && inputs.arch == 'native' }} uses: ./.github/workflows/test_native.yml - docker-deb-amd64: - if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }} - uses: ./.github/workflows/docker_build.yml - with: - distro: debian - platform: linux/amd64 - runs-on: ubuntu-24.04 - push: false - - docker-deb-amd64-tft: - if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }} - uses: ./.github/workflows/docker_build.yml - with: - distro: debian - platform: linux/amd64 - runs-on: ubuntu-24.04 - push: false - pio_env: native-tft - - docker-alp-amd64: - if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }} - uses: ./.github/workflows/docker_build.yml - with: - distro: alpine - platform: linux/amd64 - runs-on: ubuntu-24.04 - push: false - - docker-alp-amd64-tft: - if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }} - uses: ./.github/workflows/docker_build.yml - with: - distro: alpine - platform: linux/amd64 - runs-on: ubuntu-24.04 - push: false - pio_env: native-tft - - docker-deb-arm64: - if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }} - uses: ./.github/workflows/docker_build.yml - with: - distro: debian - platform: linux/arm64 - runs-on: ubuntu-24.04-arm - push: false - - docker-deb-armv7: - if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }} - uses: ./.github/workflows/docker_build.yml - with: - distro: debian - platform: linux/arm/v7 - runs-on: ubuntu-24.04-arm - push: false - gather-artifacts: permissions: contents: write diff --git a/.github/workflows/build_one_target.yml b/.github/workflows/build_one_target.yml index ba1d5080f..15b3fdba9 100644 --- a/.github/workflows/build_one_target.yml +++ b/.github/workflows/build_one_target.yml @@ -106,62 +106,6 @@ jobs: if: ${{ !contains(github.ref_name, 'event/') && github.event_name != 'workflow_dispatch' || !contains(github.ref_name, 'event/') && inputs.arch == 'native' && inputs.target != '' }} uses: ./.github/workflows/test_native.yml - docker-deb-amd64: - if: ${{ inputs.target != '' && inputs.arch == 'native' }} - uses: ./.github/workflows/docker_build.yml - with: - distro: debian - platform: linux/amd64 - runs-on: ubuntu-24.04 - push: false - - docker-deb-amd64-tft: - if: ${{ inputs.target != '' && inputs.arch == 'native' }} - uses: ./.github/workflows/docker_build.yml - with: - distro: debian - platform: linux/amd64 - runs-on: ubuntu-24.04 - push: false - pio_env: native-tft - - docker-alp-amd64: - if: ${{ inputs.target != '' && inputs.arch == 'native' }} - uses: ./.github/workflows/docker_build.yml - with: - distro: alpine - platform: linux/amd64 - runs-on: ubuntu-24.04 - push: false - - docker-alp-amd64-tft: - if: ${{ inputs.target != '' && inputs.arch == 'native' }} - uses: ./.github/workflows/docker_build.yml - with: - distro: alpine - platform: linux/amd64 - runs-on: ubuntu-24.04 - push: false - pio_env: native-tft - - docker-deb-arm64: - if: ${{ inputs.target != '' && inputs.arch == 'native' }} - uses: ./.github/workflows/docker_build.yml - with: - distro: debian - platform: linux/arm64 - runs-on: ubuntu-24.04-arm - push: false - - docker-deb-armv7: - if: ${{ inputs.target != '' && inputs.arch == 'native' }} - uses: ./.github/workflows/docker_build.yml - with: - distro: debian - platform: linux/arm/v7 - runs-on: ubuntu-24.04-arm - push: false - gather-artifacts: permissions: contents: write diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index 812990eca..02a4c23b8 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -27,7 +27,6 @@ on: jobs: setup: - if: github.repository == 'meshtastic/firmware' strategy: fail-fast: true matrix: @@ -42,10 +41,6 @@ jobs: python-version: 3.x cache: pip - run: pip install -U platformio - - name: Uncomment build epoch - shell: bash - run: | - sed -i 's/#-DBUILD_EPOCH=$UNIX_TIME/-DBUILD_EPOCH=$UNIX_TIME/' platformio.ini - name: Generate matrix id: jsonStep run: | @@ -62,7 +57,6 @@ jobs: check: ${{ steps.jsonStep.outputs.check }} version: - if: github.repository == 'meshtastic/firmware' runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 @@ -125,60 +119,26 @@ jobs: if: ${{ !contains(github.ref_name, 'event/') && github.repository == 'meshtastic/firmware' }} uses: ./.github/workflows/test_native.yml - docker-deb-amd64: - if: github.repository == 'meshtastic/firmware' + docker: + strategy: + fail-fast: false + matrix: + distro: [debian, alpine] + platform: [linux/amd64, linux/arm64, linux/arm/v7] + pio_env: [native, native-tft] + exclude: + - distro: alpine + platform: linux/arm/v7 + - pio_env: native-tft + platform: linux/arm64 + - pio_env: native-tft + platform: linux/arm/v7 uses: ./.github/workflows/docker_build.yml with: - distro: debian - platform: linux/amd64 - runs-on: ubuntu-24.04 - push: false - - docker-deb-amd64-tft: - if: github.repository == 'meshtastic/firmware' - uses: ./.github/workflows/docker_build.yml - with: - distro: debian - platform: linux/amd64 - runs-on: ubuntu-24.04 - push: false - pio_env: native-tft - - docker-alp-amd64: - if: github.repository == 'meshtastic/firmware' - uses: ./.github/workflows/docker_build.yml - with: - distro: alpine - platform: linux/amd64 - runs-on: ubuntu-24.04 - push: false - - docker-alp-amd64-tft: - if: github.repository == 'meshtastic/firmware' - uses: ./.github/workflows/docker_build.yml - with: - distro: alpine - platform: linux/amd64 - runs-on: ubuntu-24.04 - push: false - pio_env: native-tft - - docker-deb-arm64: - if: github.repository == 'meshtastic/firmware' - uses: ./.github/workflows/docker_build.yml - with: - distro: debian - platform: linux/arm64 - runs-on: ubuntu-24.04-arm - push: false - - docker-deb-armv7: - if: github.repository == 'meshtastic/firmware' - uses: ./.github/workflows/docker_build.yml - with: - distro: debian - platform: linux/arm/v7 - runs-on: ubuntu-24.04-arm + distro: ${{ matrix.distro }} + platform: ${{ matrix.platform }} + runs-on: ${{ contains(matrix.platform, 'arm') && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }} + pio_env: ${{ matrix.pio_env }} push: false gather-artifacts: diff --git a/.github/workflows/merge_queue.yml b/.github/workflows/merge_queue.yml index 79e8b0803..e8c3d3450 100644 --- a/.github/workflows/merge_queue.yml +++ b/.github/workflows/merge_queue.yml @@ -99,54 +99,26 @@ jobs: if: ${{ !contains(github.ref_name, 'event/') }} uses: ./.github/workflows/test_native.yml - docker-deb-amd64: + docker: + strategy: + fail-fast: false + matrix: + distro: [debian, alpine] + platform: [linux/amd64, linux/arm64, linux/arm/v7] + pio_env: [native, native-tft] + exclude: + - distro: alpine + platform: linux/arm/v7 + - pio_env: native-tft + platform: linux/arm64 + - pio_env: native-tft + platform: linux/arm/v7 uses: ./.github/workflows/docker_build.yml with: - distro: debian - platform: linux/amd64 - runs-on: ubuntu-24.04 - push: false - - docker-deb-amd64-tft: - uses: ./.github/workflows/docker_build.yml - with: - distro: debian - platform: linux/amd64 - runs-on: ubuntu-24.04 - push: false - pio_env: native-tft - - docker-alp-amd64: - uses: ./.github/workflows/docker_build.yml - with: - distro: alpine - platform: linux/amd64 - runs-on: ubuntu-24.04 - push: false - - docker-alp-amd64-tft: - uses: ./.github/workflows/docker_build.yml - with: - distro: alpine - platform: linux/amd64 - runs-on: ubuntu-24.04 - push: false - pio_env: native-tft - - docker-deb-arm64: - uses: ./.github/workflows/docker_build.yml - with: - distro: debian - platform: linux/arm64 - runs-on: ubuntu-24.04-arm - push: false - - docker-deb-armv7: - uses: ./.github/workflows/docker_build.yml - with: - distro: debian - platform: linux/arm/v7 - runs-on: ubuntu-24.04-arm + distro: ${{ matrix.distro }} + platform: ${{ matrix.platform }} + runs-on: ${{ contains(matrix.platform, 'arm') && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }} + pio_env: ${{ matrix.pio_env }} push: false gather-artifacts: diff --git a/.github/workflows/sec_sast_semgrep_cron.yml b/.github/workflows/sec_sast_semgrep_cron.yml index 96c993cba..2059fde2c 100644 --- a/.github/workflows/sec_sast_semgrep_cron.yml +++ b/.github/workflows/sec_sast_semgrep_cron.yml @@ -41,7 +41,7 @@ jobs: # step 4 - name: publish code scanning alerts - uses: github/codeql-action/upload-sarif@v3 + uses: github/codeql-action/upload-sarif@v4 with: sarif_file: report.sarif category: semgrep diff --git a/.github/workflows/stale_bot.yml b/.github/workflows/stale_bot.yml index a80619e90..11ba59386 100644 --- a/.github/workflows/stale_bot.yml +++ b/.github/workflows/stale_bot.yml @@ -20,5 +20,7 @@ jobs: uses: actions/stale@v10.1.0 with: days-before-stale: 45 - exempt-issue-labels: pinned,3.0 - exempt-pr-labels: pinned,3.0 + stale-issue-message: This issue has not had any comment or update in the last month. If it is still relevant, please post update comments. If no comments are made, this issue will be closed automagically in 7 days. + close-issue-message: This issue has not had any comment since the last notice. It has been closed automatically. If this is incorrect, or the issue becomes relevant again, please request that it is reopened. + exempt-issue-labels: pinned,3.0,triaged,backlog + exempt-pr-labels: pinned,3.0,triaged,backlog diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 942659348..7852fc31f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -47,7 +47,7 @@ jobs: pio upgrade - name: Setup Node - uses: actions/setup-node@v5 + uses: actions/setup-node@v6 with: node-version: 22 diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index 8c981850d..5bec39ae2 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -4,24 +4,24 @@ cli: plugins: sources: - id: trunk - ref: v1.7.2 + ref: v1.7.3 uri: https://github.com/trunk-io/plugins lint: enabled: - - checkov@3.2.473 - - renovate@41.132.5 + - checkov@3.2.483 + - renovate@41.148.2 - prettier@3.6.2 - trufflehog@3.90.8 - yamllint@1.37.1 - bandit@1.8.6 - - trivy@0.67.0 + - trivy@0.67.2 - taplo@0.10.0 - - ruff@0.13.3 - - isort@6.1.0 + - ruff@0.14.0 + - isort@7.0.0 - markdownlint@0.45.0 - oxipng@9.1.5 - svgo@4.0.0 - - actionlint@1.7.7 + - actionlint@1.7.8 - flake8@7.3.0 - hadolint@2.14.0 - shfmt@3.6.0 diff --git a/Dockerfile b/Dockerfile index b1e151ac7..111dd69fc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ # trunk-ignore-all(hadolint/DL3008): Do not pin apt package versions # trunk-ignore-all(hadolint/DL3013): Do not pin pip package versions -FROM python:3.13-slim-trixie AS builder +FROM python:3.14-slim-trixie AS builder ARG PIO_ENV=native ENV DEBIAN_FRONTEND=noninteractive ENV TZ=Etc/UTC diff --git a/alpine.Dockerfile b/alpine.Dockerfile index 670736241..0469874e4 100644 --- a/alpine.Dockerfile +++ b/alpine.Dockerfile @@ -3,7 +3,7 @@ # trunk-ignore-all(hadolint/DL3018): Do not pin apk package versions # trunk-ignore-all(hadolint/DL3013): Do not pin pip package versions -FROM python:3.13-alpine3.22 AS builder +FROM python:3.14-alpine3.22 AS builder ARG PIO_ENV=native ENV PIP_ROOT_USER_ACTION=ignore diff --git a/arch/esp32/esp32.ini b/arch/esp32/esp32.ini index d2c933461..810c9780e 100644 --- a/arch/esp32/esp32.ini +++ b/arch/esp32/esp32.ini @@ -31,6 +31,7 @@ build_flags = -DMYNEWT_VAL_BLE_HS_LOG_LVL=LOG_LEVEL_CRITICAL -DAXP_DEBUG_PORT=Serial -DCONFIG_BT_NIMBLE_ENABLED + -DCONFIG_BT_NIMBLE_MAX_BONDS=6 # default is 3 -DCONFIG_NIMBLE_CPP_LOG_LEVEL=2 -DCONFIG_BT_NIMBLE_MAX_CCCDS=20 -DCONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=8192 @@ -56,7 +57,7 @@ lib_deps = # renovate: datasource=git-refs depName=libpax packageName=https://github.com/dbinfrago/libpax gitBranch=master https://github.com/dbinfrago/libpax/archive/3cdc0371c375676a97967547f4065607d4c53fd1.zip # renovate: datasource=github-tags depName=XPowersLib packageName=lewisxhe/XPowersLib - https://github.com/lewisxhe/XPowersLib/archive/v0.3.0.zip + https://github.com/lewisxhe/XPowersLib/archive/v0.3.1.zip # renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip # renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto diff --git a/arch/esp32/esp32c6.ini b/arch/esp32/esp32c6.ini index 1afb9b547..7b06f4cd8 100644 --- a/arch/esp32/esp32c6.ini +++ b/arch/esp32/esp32c6.ini @@ -28,7 +28,7 @@ lib_deps = ${environmental_extra.lib_deps} ${radiolib_base.lib_deps} # renovate: datasource=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib - lewisxhe/XPowersLib@0.3.0 + lewisxhe/XPowersLib@0.3.1 # renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip # renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto diff --git a/bin/kill-github-actions.sh b/bin/kill-github-actions.sh new file mode 100755 index 000000000..f71047c5e --- /dev/null +++ b/bin/kill-github-actions.sh @@ -0,0 +1,116 @@ +#!/bin/bash + +# Script to cancel all running GitHub Actions workflows +# Requires GitHub CLI (gh) to be installed and authenticated + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Function to print colored output +print_status() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +print_warning() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +print_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Check if gh CLI is installed +if ! command -v gh &> /dev/null; then + print_error "GitHub CLI (gh) is not installed. Please install it first:" + echo " brew install gh" + echo " Or visit: https://cli.github.com/" + exit 1 +fi + +# Check if authenticated +if ! gh auth status &> /dev/null; then + print_error "GitHub CLI is not authenticated. Please run:" + echo " gh auth login" + exit 1 +fi + +# Get repository info +REPO=$(gh repo view --json owner,name -q '.owner.login + "/" + .name') +if [[ -z "$REPO" ]]; then + print_error "Could not determine repository. Make sure you're in a GitHub repository." + exit 1 +fi + +print_status "Working with repository: $REPO" + +# Get all active workflows (both queued and in-progress) +print_status "Fetching active workflows (queued and in-progress)..." +QUEUED_WORKFLOWS=$(gh run list --status queued --json databaseId,displayTitle,headBranch,status --limit 100) +IN_PROGRESS_WORKFLOWS=$(gh run list --status in_progress --json databaseId,displayTitle,headBranch,status --limit 100) + +# Combine both lists +ALL_WORKFLOWS=$(echo "$QUEUED_WORKFLOWS $IN_PROGRESS_WORKFLOWS" | jq -s 'add | unique_by(.databaseId)') + +if [[ "$ALL_WORKFLOWS" == "[]" ]]; then + print_status "No active workflows found." + exit 0 +fi + +# Parse and display active workflows +echo +print_warning "Found active workflows:" +echo "$ALL_WORKFLOWS" | jq -r '.[] | " - \(.displayTitle) (Branch: \(.headBranch), Status: \(.status), ID: \(.databaseId))"' + +echo +read -p "Do you want to cancel ALL these workflows? (y/N): " -n 1 -r +echo + +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + print_status "Cancelled by user." + exit 0 +fi + +# Cancel each workflow +print_status "Cancelling workflows..." +CANCELLED_COUNT=0 +FAILED_COUNT=0 + +while IFS= read -r WORKFLOW_ID; do + if [[ -n "$WORKFLOW_ID" ]]; then + print_status "Cancelling workflow ID: $WORKFLOW_ID" + if gh run cancel "$WORKFLOW_ID" 2>/dev/null; then + ((CANCELLED_COUNT++)) + else + print_error "Failed to cancel workflow ID: $WORKFLOW_ID" + ((FAILED_COUNT++)) + fi + fi +done < <(echo "$ALL_WORKFLOWS" | jq -r '.[].databaseId') + +echo +print_status "Summary:" +echo " - Cancelled: $CANCELLED_COUNT workflows" +if [[ $FAILED_COUNT -gt 0 ]]; then + echo " - Failed: $FAILED_COUNT workflows" +fi + +print_status "Done!" + +# Optional: Show remaining active workflows +echo +print_status "Checking for any remaining active workflows..." +REMAINING_QUEUED=$(gh run list --status queued --json databaseId --limit 10) +REMAINING_IN_PROGRESS=$(gh run list --status in_progress --json databaseId --limit 10) +REMAINING_ALL=$(echo "$REMAINING_QUEUED $REMAINING_IN_PROGRESS" | jq -s 'add | unique_by(.databaseId)') + +if [[ "$REMAINING_ALL" == "[]" ]]; then + print_status "All workflows successfully cancelled." +else + REMAINING_COUNT=$(echo "$REMAINING_ALL" | jq '. | length') + print_warning "Still $REMAINING_COUNT workflows active (may take a moment to update status)" +fi \ No newline at end of file diff --git a/bin/org.meshtastic.meshtasticd.metainfo.xml b/bin/org.meshtastic.meshtasticd.metainfo.xml index 3505f1940..6fc5e8597 100644 --- a/bin/org.meshtastic.meshtasticd.metainfo.xml +++ b/bin/org.meshtastic.meshtasticd.metainfo.xml @@ -87,6 +87,9 @@ + + https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.13 + https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.12 diff --git a/bin/web.version b/bin/web.version index 952f449f1..ba5c9fca6 100644 --- a/bin/web.version +++ b/bin/web.version @@ -1 +1 @@ -2.6.6 \ No newline at end of file +2.6.7 \ No newline at end of file diff --git a/debian/changelog b/debian/changelog index 8bd053b25..e124f8929 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +meshtasticd (2.7.13.0) unstable; urgency=medium + + * Version 2.7.13 + + -- GitHub Actions Sat, 11 Oct 2025 15:27:28 +0000 + meshtasticd (2.7.12.0) unstable; urgency=medium [ Austin Lane ] diff --git a/platformio.ini b/platformio.ini index 345e2e832..983ae780e 100644 --- a/platformio.ini +++ b/platformio.ini @@ -120,7 +120,7 @@ lib_deps = [device-ui_base] lib_deps = # renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master - https://github.com/meshtastic/device-ui/archive/d8929cd2bd2825da0e706660b5465bec12f6998f.zip + https://github.com/meshtastic/device-ui/archive/af27b81b871b795eb44883e74d10d26c88d37eea.zip ; Common libs for environmental measurements in telemetry module [environmental_base] @@ -164,7 +164,7 @@ lib_deps = # renovate: datasource=custom.pio depName=QMC5883L Compass packageName=mprograms/library/QMC5883LCompass mprograms/QMC5883LCompass@1.2.3 # renovate: datasource=custom.pio depName=DFRobot_RTU packageName=dfrobot/library/DFRobot_RTU - dfrobot/DFRobot_RTU@1.0.3 + dfrobot/DFRobot_RTU@1.0.6 # renovate: datasource=git-refs depName=DFRobot_RainfallSensor packageName=https://github.com/DFRobot/DFRobot_RainfallSensor gitBranch=master https://github.com/DFRobot/DFRobot_RainfallSensor/archive/38fea5e02b40a5430be6dab39a99a6f6347d667e.zip # renovate: datasource=custom.pio depName=INA226 packageName=robtillaart/library/INA226 diff --git a/protobufs b/protobufs index a1b8c3d17..bf149bbdc 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit a1b8c3d171445b2eebfd4b5bd1e4876f3bbed605 +Subproject commit bf149bbdcce45ba7cd8643db7cb25e5c8815072b diff --git a/src/Power.cpp b/src/Power.cpp index 9c556fa78..1c9efb907 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -562,6 +562,7 @@ class AnalogBatteryLevel : public HasBatteryLevel config.power.device_battery_ina_address) { if (!ina226Sensor.isInitialized()) return ina226Sensor.runOnce() > 0; + return ina226Sensor.isRunning(); } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260].first == config.power.device_battery_ina_address) { if (!ina260Sensor.isInitialized()) @@ -691,6 +692,16 @@ bool Power::setup() #ifdef NRF_APM found = true; #endif +#ifdef EXT_PWR_DETECT + attachInterrupt( + EXT_PWR_DETECT, + []() { + power->setIntervalFromNow(0); + runASAP = true; + BaseType_t higherWake = 0; + }, + CHANGE); +#endif enabled = found; low_voltage_counter = 0; diff --git a/src/configuration.h b/src/configuration.h index e67f0b8e5..baf24a636 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -33,6 +33,9 @@ along with this program. If not, see . #include "pcf8563.h" #endif +/* Offer chance for variant-specific defines */ +#include "variant.h" + // ----------------------------------------------------------------------------- // Version // ----------------------------------------------------------------------------- @@ -123,6 +126,11 @@ along with this program. If not, see . #define TX_GAIN_LORA 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 9, 9, 8, 7 #endif +#ifdef RAK13302 +#define NUM_PA_POINTS 22 +#define TX_GAIN_LORA 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8 +#endif + // Default system gain to 0 if not defined #ifndef TX_GAIN_LORA #define TX_GAIN_LORA 0 @@ -260,9 +268,6 @@ along with this program. If not, see . // convert 24-bit color to 16-bit (56K) #define COLOR565(r, g, b) (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3)) -/* Step #1: offer chance for variant-specific defines */ -#include "variant.h" - #if defined(VEXT_ENABLE) && !defined(VEXT_ON_VALUE) // Older variant.h files might not be defining this value, so stay with the old default #define VEXT_ON_VALUE LOW diff --git a/src/detect/ScanI2CConsumer.cpp b/src/detect/ScanI2CConsumer.cpp new file mode 100644 index 000000000..a70fa5398 --- /dev/null +++ b/src/detect/ScanI2CConsumer.cpp @@ -0,0 +1,16 @@ +#include "ScanI2CConsumer.h" +#include + +static std::forward_list ScanI2CConsumers; + +ScanI2CConsumer::ScanI2CConsumer() +{ + ScanI2CConsumers.push_front(this); +} + +void ScanI2CCompleted(ScanI2C *i2cScanner) +{ + for (ScanI2CConsumer *consumer : ScanI2CConsumers) { + consumer->i2cScanFinished(i2cScanner); + } +} \ No newline at end of file diff --git a/src/detect/ScanI2CConsumer.h b/src/detect/ScanI2CConsumer.h new file mode 100644 index 000000000..fd97f7edc --- /dev/null +++ b/src/detect/ScanI2CConsumer.h @@ -0,0 +1,13 @@ +#pragma once + +#include "ScanI2C.h" +#include + +class ScanI2CConsumer +{ + public: + ScanI2CConsumer(); + virtual void i2cScanFinished(ScanI2C *i2cScanner) = 0; +}; + +void ScanI2CCompleted(ScanI2C *i2cScanner); \ No newline at end of file diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index ffef698b0..4570882e1 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -378,7 +378,8 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) case SHT31_4x_ADDR: // same as OPT3001_ADDR_ALT case SHT31_4x_ADDR_ALT: // same as OPT3001_ADDR registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x89), 2); - if (registerValue == 0x11a2 || registerValue == 0x11da || registerValue == 0x11f3 || registerValue == 0xe9c || registerValue == 0xc8d) { + if (registerValue == 0x11a2 || registerValue == 0x11da || registerValue == 0x11f3 || registerValue == 0xe9c || + registerValue == 0xc8d) { type = SHT4X; logFoundDevice("SHT4X", (uint8_t)addr.address); } else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2) == 0x5449) { @@ -580,7 +581,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port) scanPort(port, nullptr, 0); } -TwoWire *ScanI2CTwoWire::fetchI2CBus(ScanI2C::DeviceAddress address) const +TwoWire *ScanI2CTwoWire::fetchI2CBus(ScanI2C::DeviceAddress address) { if (address.port == ScanI2C::I2CPort::WIRE) { return &Wire; diff --git a/src/detect/ScanI2CTwoWire.h b/src/detect/ScanI2CTwoWire.h index 6988091ad..c5b791920 100644 --- a/src/detect/ScanI2CTwoWire.h +++ b/src/detect/ScanI2CTwoWire.h @@ -23,12 +23,12 @@ class ScanI2CTwoWire : public ScanI2C ScanI2C::FoundDevice find(ScanI2C::DeviceType) const override; - TwoWire *fetchI2CBus(ScanI2C::DeviceAddress) const; - bool exists(ScanI2C::DeviceType) const override; size_t countDevices() const override; + static TwoWire *fetchI2CBus(ScanI2C::DeviceAddress); + protected: FoundDevice firstOfOrNONE(size_t, DeviceType[]) const override; diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 95a9bae9c..70a11aa56 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -494,17 +494,6 @@ bool GPS::setup() if (!didSerialInit) { int msglen = 0; if (tx_gpio && gnssModel == GNSS_MODEL_UNKNOWN) { -#ifdef TRACKER_T1000_E - // add power up/down strategy, improve ag3335 detection success - digitalWrite(PIN_GPS_EN, LOW); - delay(500); - digitalWrite(GPS_VRTC_EN, LOW); - delay(1000); - digitalWrite(GPS_VRTC_EN, HIGH); - delay(500); - digitalWrite(PIN_GPS_EN, HIGH); - delay(1000); -#endif if (probeTries < GPS_PROBETRIES) { gnssModel = probe(serialSpeeds[speedSelect]); if (gnssModel == GNSS_MODEL_UNKNOWN) { diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index a3f4038b0..9787cda13 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -1428,6 +1428,9 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg) } nodeDB->updateGUI = false; break; + case STATUS_TYPE_POWER: + forceDisplay(true); + break; } return 0; @@ -1487,7 +1490,7 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet) strcpy(banner, "Alert Received"); } screen->showSimpleBanner(banner, 3000); - } else if (!channel.settings.mute) { + } else if (!channel.settings.has_module_settings || !channel.settings.module_settings.is_muted) { if (longName && longName[0]) { #if defined(M5STACK_UNITC6L) strcpy(banner, "New Message"); diff --git a/src/graphics/SharedUIDisplay.cpp b/src/graphics/SharedUIDisplay.cpp index dcaa5d69b..8e1299f51 100644 --- a/src/graphics/SharedUIDisplay.cpp +++ b/src/graphics/SharedUIDisplay.cpp @@ -1,6 +1,8 @@ -#include "graphics/SharedUIDisplay.h" +#include "configuration.h" +#if HAS_SCREEN #include "RTC.h" #include "graphics/ScreenFonts.h" +#include "graphics/SharedUIDisplay.h" #include "graphics/draw/UIRenderer.h" #include "main.h" #include "meshtastic/config.pb.h" @@ -423,3 +425,4 @@ std::string sanitizeString(const std::string &input) } } // namespace graphics +#endif \ No newline at end of file diff --git a/src/graphics/VirtualKeyboard.cpp b/src/graphics/VirtualKeyboard.cpp index 84d5551cb..8062a0338 100644 --- a/src/graphics/VirtualKeyboard.cpp +++ b/src/graphics/VirtualKeyboard.cpp @@ -1,5 +1,6 @@ -#include "VirtualKeyboard.h" #include "configuration.h" +#if HAS_SCREEN +#include "VirtualKeyboard.h" #include "graphics/Screen.h" #include "graphics/ScreenFonts.h" #include "graphics/SharedUIDisplay.h" @@ -736,3 +737,4 @@ bool VirtualKeyboard::isTimedOut() const } } // namespace graphics +#endif \ No newline at end of file diff --git a/src/graphics/draw/CompassRenderer.cpp b/src/graphics/draw/CompassRenderer.cpp index 0e5a1d727..629949ffd 100644 --- a/src/graphics/draw/CompassRenderer.cpp +++ b/src/graphics/draw/CompassRenderer.cpp @@ -1,3 +1,5 @@ +#include "configuration.h" +#if HAS_SCREEN #include "CompassRenderer.h" #include "NodeDB.h" #include "UIRenderer.h" @@ -135,3 +137,4 @@ uint16_t getCompassDiam(uint32_t displayWidth, uint32_t displayHeight) } // namespace CompassRenderer } // namespace graphics +#endif \ No newline at end of file diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp index c93e34545..701062e08 100644 --- a/src/graphics/draw/MenuHandler.cpp +++ b/src/graphics/draw/MenuHandler.cpp @@ -762,6 +762,31 @@ void menuHandler::nodeListMenu() screen->showOverlayBanner(bannerOptions); } +void menuHandler::nodeNameLengthMenu() +{ + enum OptionsNumbers { Back, Long, Short }; + static const char *optionsArray[] = {"Back", "Long", "Short"}; + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Node Name Length"; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsCount = 3; + bannerOptions.bannerCallback = [](int selected) -> void { + if (selected == Long) { + // Set names to long + LOG_INFO("Setting names to long"); + config.display.use_long_node_name = true; + } else if (selected == Short) { + // Set names to short + LOG_INFO("Setting names to short"); + config.display.use_long_node_name = false; + } else if (selected == Back) { + menuQueue = screen_options_menu; + screen->runNow(); + } + }; + screen->showOverlayBanner(bannerOptions); +} + void menuHandler::resetNodeDBMenu() { static const char *optionsArray[] = {"Back", "Confirm"}; @@ -1304,11 +1329,16 @@ void menuHandler::screenOptionsMenu() hasSupportBrightness = false; #endif - enum optionsNumbers { Back, Brightness, ScreenColor }; - static const char *optionsArray[4] = {"Back"}; - static int optionsEnumArray[4] = {Back}; + enum optionsNumbers { Back, NodeNameLength, Brightness, ScreenColor }; + static const char *optionsArray[5] = {"Back"}; + static int optionsEnumArray[5] = {Back}; int options = 1; +#if defined(T_DECK) || defined(T_LORA_PAGER) + optionsArray[options] = "Show Long/Short Name"; + optionsEnumArray[options++] = NodeNameLength; +#endif + // Only show brightness for B&W displays if (hasSupportBrightness) { optionsArray[options] = "Brightness"; @@ -1333,6 +1363,9 @@ void menuHandler::screenOptionsMenu() } else if (selected == ScreenColor) { menuHandler::menuQueue = menuHandler::tftcolormenupicker; screen->runNow(); + } else if (selected == NodeNameLength) { + menuHandler::menuQueue = menuHandler::node_name_length_menu; + screen->runNow(); } else { menuQueue = system_base_menu; screen->runNow(); @@ -1431,6 +1464,8 @@ void menuHandler::FrameToggles_menu() lora, clock, show_favorites, + show_telemetry, + show_power, enumEnd }; static const char *optionsArray[enumEnd] = {"Finish"}; @@ -1469,6 +1504,12 @@ void menuHandler::FrameToggles_menu() optionsArray[options] = screen->isFrameHidden("show_favorites") ? "Show Favorites" : "Hide Favorites"; optionsEnumArray[options++] = show_favorites; + optionsArray[options] = moduleConfig.telemetry.environment_screen_enabled ? "Hide Telemetry" : "Show Telemetry"; + optionsEnumArray[options++] = show_telemetry; + + optionsArray[options] = moduleConfig.telemetry.power_screen_enabled ? "Hide Power" : "Show Power"; + optionsEnumArray[options++] = show_power; + BannerOverlayOptions bannerOptions; bannerOptions.message = "Show/Hide Frames"; bannerOptions.optionsArrayPtr = optionsArray; @@ -1523,6 +1564,14 @@ void menuHandler::FrameToggles_menu() screen->toggleFrameVisibility("show_favorites"); menuHandler::menuQueue = menuHandler::FrameToggles; screen->runNow(); + } else if (selected == show_telemetry) { + moduleConfig.telemetry.environment_screen_enabled = !moduleConfig.telemetry.environment_screen_enabled; + menuHandler::menuQueue = menuHandler::FrameToggles; + screen->runNow(); + } else if (selected == show_power) { + moduleConfig.telemetry.power_screen_enabled = !moduleConfig.telemetry.power_screen_enabled; + menuHandler::menuQueue = menuHandler::FrameToggles; + screen->runNow(); } }; screen->showOverlayBanner(bannerOptions); @@ -1594,6 +1643,9 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display) case brightness_picker: BrightnessPickerMenu(); break; + case node_name_length_menu: + nodeNameLengthMenu(); + break; case reboot_menu: rebootMenu(); break; diff --git a/src/graphics/draw/MenuHandler.h b/src/graphics/draw/MenuHandler.h index 47dbb2543..1f7bbac8c 100644 --- a/src/graphics/draw/MenuHandler.h +++ b/src/graphics/draw/MenuHandler.h @@ -43,6 +43,7 @@ class menuHandler key_verification_final_prompt, trace_route_menu, throttle_message, + node_name_length_menu, FrameToggles }; static screenMenus menuQueue; @@ -85,6 +86,7 @@ class menuHandler static void notificationsMenu(); static void screenOptionsMenu(); static void powerMenu(); + static void nodeNameLengthMenu(); static void FrameToggles_menu(); static void textMessageMenu(); diff --git a/src/graphics/draw/NodeListRenderer.cpp b/src/graphics/draw/NodeListRenderer.cpp index 7d6a38dd3..07577db8c 100644 --- a/src/graphics/draw/NodeListRenderer.cpp +++ b/src/graphics/draw/NodeListRenderer.cpp @@ -55,26 +55,32 @@ static int scrollIndex = 0; const char *getSafeNodeName(meshtastic_NodeInfoLite *node) { + const char *name = NULL; static char nodeName[16] = "?"; - if (node->has_user && strlen(node->user.short_name) > 0) { - bool valid = true; - const char *name = node->user.short_name; - for (size_t i = 0; i < strlen(name); i++) { - uint8_t c = (uint8_t)name[i]; - if (c < 32 || c > 126) { - valid = false; - break; - } - } - if (valid) { - strncpy(nodeName, name, sizeof(nodeName) - 1); - nodeName[sizeof(nodeName) - 1] = '\0'; + if (config.display.use_long_node_name == true) { + if (node->has_user && strlen(node->user.long_name) > 0) { + name = node->user.long_name; } else { snprintf(nodeName, sizeof(nodeName), "(%04X)", (uint16_t)(node->num & 0xFFFF)); } + } else { + if (node->has_user && strlen(node->user.short_name) > 0) { + name = node->user.short_name; + } else { + snprintf(nodeName, sizeof(nodeName), "(%04X)", (uint16_t)(node->num & 0xFFFF)); + } + } + + // Use sanitizeString() function and copy directly into nodeName + std::string sanitized_name = sanitizeString(name ? name : ""); + + if (!sanitized_name.empty()) { + strncpy(nodeName, sanitized_name.c_str(), sizeof(nodeName) - 1); + nodeName[sizeof(nodeName) - 1] = '\0'; } else { snprintf(nodeName, sizeof(nodeName), "(%04X)", (uint16_t)(node->num & 0xFFFF)); } + return nodeName; } diff --git a/src/graphics/draw/UIRenderer.cpp b/src/graphics/draw/UIRenderer.cpp index 9b90e96a0..6fd093ca3 100644 --- a/src/graphics/draw/UIRenderer.cpp +++ b/src/graphics/draw/UIRenderer.cpp @@ -564,6 +564,7 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta display->setTextAlignment(TEXT_ALIGN_LEFT); display->setFont(FONT_SMALL); int line = 1; + meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); // === Header === #if defined(M5STACK_UNITC6L) @@ -741,7 +742,6 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta int yOffset = (isHighResolution) ? 0 : 5; std::string longNameStr; - meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); if (ourNode && ourNode->has_user && strlen(ourNode->user.long_name) > 0) { longNameStr = sanitizeString(ourNode->user.long_name); } @@ -1001,24 +1001,7 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU const char *displayLine = ""; // Initialize to empty string by default meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); - bool usePhoneGPS = (ourNode && nodeDB->hasValidPosition(ourNode) && - config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED); - - if (usePhoneGPS) { - // Phone-provided GPS is active - displayLine = "Phone GPS"; - int yOffset = (isHighResolution) ? 3 : 1; - if (isHighResolution) { - NodeListRenderer::drawScaledXBitmap16x16(x, getTextPositions(display)[line] + yOffset - 5, imgSatellite_width, - imgSatellite_height, imgSatellite, display); - } else { - display->drawXbm(x + 1, getTextPositions(display)[line] + yOffset, imgSatellite_width, imgSatellite_height, - imgSatellite); - } - int xOffset = (isHighResolution) ? 6 : 0; - display->drawString(x + 11 + xOffset, getTextPositions(display)[line++], displayLine); - } else if (config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED) { - // GPS disabled / not present + if (config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED) { if (config.position.fixed_position) { displayLine = "Fixed GPS"; } else { @@ -1109,9 +1092,7 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU // === Final Row: Altitude === char altitudeLine[32] = {0}; - int32_t alt = (strcmp(displayLine, "Phone GPS") == 0 && ourNode && nodeDB->hasValidPosition(ourNode)) - ? ourNode->position.altitude - : geoCoord.getAltitude(); + int32_t alt = geoCoord.getAltitude(); if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) { snprintf(altitudeLine, sizeof(altitudeLine), "Alt: %.0fft", alt * METERS_TO_FEET); } else { diff --git a/src/graphics/emotes.cpp b/src/graphics/emotes.cpp index e1a105d20..a0227d365 100644 --- a/src/graphics/emotes.cpp +++ b/src/graphics/emotes.cpp @@ -1,3 +1,5 @@ +#include "configuration.h" +#if HAS_SCREEN #include "emotes.h" namespace graphics @@ -275,3 +277,4 @@ const unsigned char bell_icon[] PROGMEM = { #endif } // namespace graphics +#endif \ No newline at end of file diff --git a/src/graphics/niche/InkHUD/Applets/Bases/Map/MapApplet.cpp b/src/graphics/niche/InkHUD/Applets/Bases/Map/MapApplet.cpp index db0805f4e..818c68070 100644 --- a/src/graphics/niche/InkHUD/Applets/Bases/Map/MapApplet.cpp +++ b/src/graphics/niche/InkHUD/Applets/Bases/Map/MapApplet.cpp @@ -13,45 +13,147 @@ void InkHUD::MapApplet::onRender() return; } + // Helper: draw rounded rectangle centered at x,y + auto fillRoundedRect = [&](int16_t cx, int16_t cy, int16_t w, int16_t h, int16_t r, uint16_t color) { + int16_t x = cx - (w / 2); + int16_t y = cy - (h / 2); + + // center rects + fillRect(x + r, y, w - 2 * r, h, color); + fillRect(x, y + r, r, h - 2 * r, color); + fillRect(x + w - r, y + r, r, h - 2 * r, color); + + // corners + fillCircle(x + r, y + r, r, color); + fillCircle(x + w - r - 1, y + r, r, color); + fillCircle(x + r, y + h - r - 1, r, color); + fillCircle(x + w - r - 1, y + h - r - 1, r, color); + }; + // Find center of map - // - latitude and longitude - // - will be placed at X(0.5), Y(0.5) getMapCenter(&latCenter, &lngCenter); - - // Calculate North+East distance of each node to map center - // - which nodes to use controlled by virtual shouldDrawNode method calculateAllMarkers(); - - // Set the region shown on the map - // - default: fit all nodes, plus padding - // - maybe overriden by derived applet - // - getMapSize *sets* passed parameters (C-style) getMapSize(&widthMeters, &heightMeters); - - // Set the metersToPx conversion value calculateMapScale(); - // Special marker for own node - meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); - if (ourNode && nodeDB->hasValidPosition(ourNode)) - drawLabeledMarker(ourNode); - - // Draw all markers + // Draw all markers first for (Marker m : markers) { int16_t x = X(0.5) + (m.eastMeters * metersToPx); int16_t y = Y(0.5) - (m.northMeters * metersToPx); - // Cross Size - constexpr uint16_t csMin = 5; - constexpr uint16_t csMax = 12; + // Add white halo outline first + constexpr int outlinePad = 1; + int boxSize = 11; + int radius = 2; // rounded corner radius - // Too many hops away - if (m.hasHopsAway && m.hopsAway > config.lora.hop_limit) // Too many mops - printAt(x, y, "!", CENTER, MIDDLE); - else if (!m.hasHopsAway) // Unknown hops - drawCross(x, y, csMin); - else // The fewer hops, the larger the cross - drawCross(x, y, map(m.hopsAway, 0, config.lora.hop_limit, csMax, csMin)); + // White halo background + fillRoundedRect(x, y, boxSize + (outlinePad * 2), boxSize + (outlinePad * 2), radius + 1, WHITE); + + // Draw inner box + fillRoundedRect(x, y, boxSize, boxSize, radius, BLACK); + + // Text inside + setFont(fontSmall); + setTextColor(WHITE); + + // Draw actual marker on top + if (m.hasHopsAway && m.hopsAway > config.lora.hop_limit) { + printAt(x + 1, y + 1, "X", CENTER, MIDDLE); + } else if (!m.hasHopsAway) { + printAt(x + 1, y + 1, "?", CENTER, MIDDLE); + } else { + char hopStr[4]; + snprintf(hopStr, sizeof(hopStr), "%d", m.hopsAway); + printAt(x, y + 1, hopStr, CENTER, MIDDLE); + } + + // Restore default font and color + setFont(fontSmall); + setTextColor(BLACK); + } + + // Dual map scale bars + int16_t horizPx = width() * 0.25f; + int16_t vertPx = height() * 0.25f; + float horizMeters = horizPx / metersToPx; + float vertMeters = vertPx / metersToPx; + + auto formatDistance = [&](float meters, char *out, size_t len) { + if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) { + float feet = meters * 3.28084f; + if (feet < 528) + snprintf(out, len, "%.0f ft", feet); + else { + float miles = feet / 5280.0f; + snprintf(out, len, miles < 10 ? "%.1f mi" : "%.0f mi", miles); + } + } else { + if (meters >= 1000) + snprintf(out, len, "%.1f km", meters / 1000.0f); + else + snprintf(out, len, "%.0f m", meters); + } + }; + + // Horizontal scale bar + int16_t horizBarY = height() - 2; + int16_t horizBarX = 1; + drawLine(horizBarX, horizBarY, horizBarX + horizPx, horizBarY, BLACK); + drawLine(horizBarX, horizBarY - 3, horizBarX, horizBarY + 3, BLACK); + drawLine(horizBarX + horizPx, horizBarY - 3, horizBarX + horizPx, horizBarY + 3, BLACK); + + char horizLabel[32]; + formatDistance(horizMeters, horizLabel, sizeof(horizLabel)); + int16_t horizLabelW = getTextWidth(horizLabel); + int16_t horizLabelH = getFont().lineHeight(); + int16_t horizLabelX = horizBarX + horizPx + 4; + int16_t horizLabelY = horizBarY - horizLabelH + 1; + fillRect(horizLabelX - 2, horizLabelY - 1, horizLabelW + 4, horizLabelH + 2, WHITE); + printAt(horizLabelX, horizBarY, horizLabel, LEFT, BOTTOM); + + // Vertical scale bar + int16_t vertBarX = 1; + int16_t vertBarBottom = horizBarY; + int16_t vertBarTop = vertBarBottom - vertPx; + drawLine(vertBarX, vertBarBottom, vertBarX, vertBarTop, BLACK); + drawLine(vertBarX - 3, vertBarBottom, vertBarX + 3, vertBarBottom, BLACK); + drawLine(vertBarX - 3, vertBarTop, vertBarX + 3, vertBarTop, BLACK); + + char vertTopLabel[32]; + formatDistance(vertMeters, vertTopLabel, sizeof(vertTopLabel)); + int16_t topLabelY = vertBarTop - getFont().lineHeight() - 2; + int16_t topLabelW = getTextWidth(vertTopLabel); + int16_t topLabelH = getFont().lineHeight(); + fillRect(vertBarX - 2, topLabelY - 1, topLabelW + 6, topLabelH + 2, WHITE); + printAt(vertBarX + (topLabelW / 2) + 1, topLabelY + (topLabelH / 2), vertTopLabel, CENTER, MIDDLE); + + char vertBottomLabel[32]; + formatDistance(vertMeters, vertBottomLabel, sizeof(vertBottomLabel)); + int16_t bottomLabelY = vertBarBottom + 4; + int16_t bottomLabelW = getTextWidth(vertBottomLabel); + int16_t bottomLabelH = getFont().lineHeight(); + fillRect(vertBarX - 2, bottomLabelY - 1, bottomLabelW + 6, bottomLabelH + 2, WHITE); + printAt(vertBarX + (bottomLabelW / 2) + 1, bottomLabelY + (bottomLabelH / 2), vertBottomLabel, CENTER, MIDDLE); + + // Draw our node LAST with full white fill + outline + meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); + if (ourNode && nodeDB->hasValidPosition(ourNode)) { + Marker self = calculateMarker(ourNode->position.latitude_i * 1e-7, ourNode->position.longitude_i * 1e-7, false, 0); + + int16_t centerX = X(0.5) + (self.eastMeters * metersToPx); + int16_t centerY = Y(0.5) - (self.northMeters * metersToPx); + + // White fill background + halo + fillCircle(centerX, centerY, 8, WHITE); // big white base + drawCircle(centerX, centerY, 8, WHITE); // crisp edge + + // Black bullseye on top + drawCircle(centerX, centerY, 6, BLACK); + fillCircle(centerX, centerY, 2, BLACK); + + // Crosshairs + drawLine(centerX - 8, centerY, centerX + 8, centerY, BLACK); + drawLine(centerX, centerY - 8, centerX, centerY + 8, BLACK); } } @@ -63,110 +165,123 @@ void InkHUD::MapApplet::onRender() void InkHUD::MapApplet::getMapCenter(float *lat, float *lng) { - // Find mean lat long coords - // ============================ - // - assigning X, Y and Z values to position on Earth's surface in 3D space, relative to center of planet - // - averages the x, y and z coords - // - uses tan to find angles for lat / long degrees - // - longitude: triangle formed by x and y (on plane of the equator) - // - latitude: triangle formed by z (north south), - // and the line along plane of equator which stretches from earth's axis to where point xyz intersects planet's surface + // If we have a valid position for our own node, use that as the anchor + meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); + if (ourNode && nodeDB->hasValidPosition(ourNode)) { + *lat = ourNode->position.latitude_i * 1e-7; + *lng = ourNode->position.longitude_i * 1e-7; + } else { + // Find mean lat long coords + // ============================ + // - assigning X, Y and Z values to position on Earth's surface in 3D space, relative to center of planet + // - averages the x, y and z coords + // - uses tan to find angles for lat / long degrees + // - longitude: triangle formed by x and y (on plane of the equator) + // - latitude: triangle formed by z (north south), + // and the line along plane of equator which stretches from earth's axis to where point xyz intersects planet's + // surface - // Working totals, averaged after nodeDB processed - uint32_t positionCount = 0; - float xAvg = 0; - float yAvg = 0; - float zAvg = 0; + // Working totals, averaged after nodeDB processed + uint32_t positionCount = 0; + float xAvg = 0; + float yAvg = 0; + float zAvg = 0; - // For each node in db - for (uint32_t i = 0; i < nodeDB->getNumMeshNodes(); i++) { - meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i); + // For each node in db + for (uint32_t i = 0; i < nodeDB->getNumMeshNodes(); i++) { + meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i); - // Skip if no position - if (!nodeDB->hasValidPosition(node)) - continue; + // Skip if no position + if (!nodeDB->hasValidPosition(node)) + continue; - // Skip if derived applet doesn't want to show this node on the map - if (!shouldDrawNode(node)) - continue; + // Skip if derived applet doesn't want to show this node on the map + if (!shouldDrawNode(node)) + continue; - // Latitude and Longitude of node, in radians - float latRad = node->position.latitude_i * (1e-7) * DEG_TO_RAD; - float lngRad = node->position.longitude_i * (1e-7) * DEG_TO_RAD; + // Latitude and Longitude of node, in radians + float latRad = node->position.latitude_i * (1e-7) * DEG_TO_RAD; + float lngRad = node->position.longitude_i * (1e-7) * DEG_TO_RAD; - // Convert to cartesian points, with center of earth at 0, 0, 0 - // Exact distance from center is irrelevant, as we're only interested in the vector - float x = cos(latRad) * cos(lngRad); - float y = cos(latRad) * sin(lngRad); - float z = sin(latRad); + // Convert to cartesian points, with center of earth at 0, 0, 0 + // Exact distance from center is irrelevant, as we're only interested in the vector + float x = cos(latRad) * cos(lngRad); + float y = cos(latRad) * sin(lngRad); + float z = sin(latRad); - // To find mean values shortly - xAvg += x; - yAvg += y; - zAvg += z; - positionCount++; + // To find mean values shortly + xAvg += x; + yAvg += y; + zAvg += z; + positionCount++; + } + + // All NodeDB processed, find mean values + xAvg /= positionCount; + yAvg /= positionCount; + zAvg /= positionCount; + + // Longitude from cartesian coords + // (Angle from 3D coords describing a point of globe's surface) + /* + UK + /-------\ + (Top View) /- -\ + /- (You) -\ + /- . -\ + /- . X -\ + Asia - ... - USA + \- Y -/ + \- -/ + \- -/ + \- -/ + \- -----/ + Pacific + + */ + + *lng = atan2(yAvg, xAvg) * RAD_TO_DEG; + + // Latitude from cartesian coords + // (Angle from 3D coords describing a point on the globe's surface) + // As latitude increases, distance from the Earth's north-south axis out to our surface point decreases. + // Means we need to first find the hypotenuse which becomes base of our triangle in the second step + /* + UK North + /-------\ (Front View) /-------\ + (Top View) /- -\ /- -\ + /- (You) -\ /-(You) -\ + /- /. -\ /- . -\ + /- √X²+Y²/ . X -\ /- Z . -\ + Asia - /... - USA - ..... - + \- Y -/ \- √X²+Y² -/ + \- -/ \- -/ + \- -/ \- -/ + \- -/ \- -/ + \- -----/ \- -----/ + Pacific South + */ + + float hypotenuse = sqrt((xAvg * xAvg) + (yAvg * yAvg)); // Distance from globe's north-south axis to surface intersect + *lat = atan2(zAvg, hypotenuse) * RAD_TO_DEG; } - // All NodeDB processed, find mean values - xAvg /= positionCount; - yAvg /= positionCount; - zAvg /= positionCount; - - // Longitude from cartesian coords - // (Angle from 3D coords describing a point of globe's surface) - /* - UK - /-------\ - (Top View) /- -\ - /- (You) -\ - /- . -\ - /- . X -\ - Asia - ... - USA - \- Y -/ - \- -/ - \- -/ - \- -/ - \- -----/ - Pacific - - */ - - *lng = atan2(yAvg, xAvg) * RAD_TO_DEG; - - // Latitude from cartesian coords - // (Angle from 3D coords describing a point on the globe's surface) - // As latitude increases, distance from the Earth's north-south axis out to our surface point decreases. - // Means we need to first find the hypotenuse which becomes base of our triangle in the second step - /* - UK North - /-------\ (Front View) /-------\ - (Top View) /- -\ /- -\ - /- (You) -\ /-(You) -\ - /- /. -\ /- . -\ - /- √X²+Y²/ . X -\ /- Z . -\ - Asia - /... - USA - ..... - - \- Y -/ \- √X²+Y² -/ - \- -/ \- -/ - \- -/ \- -/ - \- -/ \- -/ - \- -----/ \- -----/ - Pacific South - */ - - float hypotenuse = sqrt((xAvg * xAvg) + (yAvg * yAvg)); // Distance from globe's north-south axis to surface intersect - *lat = atan2(zAvg, hypotenuse) * RAD_TO_DEG; + // Use either our node position, or the mean fallback as the center + latCenter = *lat; + lngCenter = *lng; // ---------------------------------------------- - // This has given us the "mean position" - // This will be a position *somewhere* near the center of our nodes. - // What we actually want is to place our center so that our outermost nodes end up on the border of our map. - // The only real use of our "mean position" is to give us a reference frame: - // which direction is east, and which is west. + // This has given us either: + // - our actual position (preferred), or + // - a mean position (fallback if we had no fix) + // + // What we actually want is to place our center so that our outermost nodes + // end up on the border of our map. The only real use of our "center" is to give + // us a reference frame: which direction is east, and which is west. //------------------------------------------------ - // Find furthest nodes from "mean lat long" + // Find furthest nodes from our center // ======================================== - float northernmost = latCenter; float southernmost = latCenter; float easternmost = lngCenter; @@ -184,14 +299,14 @@ void InkHUD::MapApplet::getMapCenter(float *lat, float *lng) continue; // Check for a new top or bottom latitude - float lat = node->position.latitude_i * 1e-7; - northernmost = max(northernmost, lat); - southernmost = min(southernmost, lat); + float latNode = node->position.latitude_i * 1e-7; + northernmost = max(northernmost, latNode); + southernmost = min(southernmost, latNode); // Longitude is trickier - float lng = node->position.longitude_i * 1e-7; - float degEastward = fmod(((lng - lngCenter) + 360), 360); // Degrees traveled east from lngCenter to reach node - float degWestward = abs(fmod(((lng - lngCenter) - 360), 360)); // Degrees traveled west from lngCenter to reach node + float lngNode = node->position.longitude_i * 1e-7; + float degEastward = fmod(((lngNode - lngCenter) + 360), 360); // Degrees traveled east from lngCenter to reach node + float degWestward = abs(fmod(((lngNode - lngCenter) - 360), 360)); // Degrees traveled west from lngCenter to reach node if (degEastward < degWestward) easternmost = max(easternmost, lngCenter + degEastward); else @@ -250,7 +365,6 @@ InkHUD::MapApplet::Marker InkHUD::MapApplet::calculateMarker(float lat, float ln m.hopsAway = hopsAway; return m; } - // Draw a marker on the map for a node, with a shortname label, and backing box void InkHUD::MapApplet::drawLabeledMarker(meshtastic_NodeInfoLite *node) { @@ -324,6 +438,18 @@ void InkHUD::MapApplet::drawLabeledMarker(meshtastic_NodeInfoLite *node) textX = labelX + paddingW; } + // Prevent overlap with scale bars and their labels + // Define a "safe zone" in the bottom-left where the scale bars and text are drawn + constexpr int16_t safeZoneHeight = 28; // adjust based on your label font height + constexpr int16_t safeZoneWidth = 60; // adjust based on horizontal label width zone + bool overlapsScale = (labelY + labelH > height() - safeZoneHeight) && (labelX < safeZoneWidth); + + // If it overlaps, shift label upward slightly above the safe zone + if (overlapsScale) { + labelY = height() - safeZoneHeight - labelH - 2; + textY = labelY + (labelH / 2); + } + // Backing box fillRect(labelX, labelY, labelW, labelH, WHITE); drawRect(labelX, labelY, labelW, labelH, BLACK); diff --git a/src/graphics/niche/InkHUD/Applets/System/Menu/MenuApplet.cpp b/src/graphics/niche/InkHUD/Applets/System/Menu/MenuApplet.cpp index 7876276a8..09f76ed46 100644 --- a/src/graphics/niche/InkHUD/Applets/System/Menu/MenuApplet.cpp +++ b/src/graphics/niche/InkHUD/Applets/System/Menu/MenuApplet.cpp @@ -709,7 +709,7 @@ void InkHUD::MenuApplet::drawSystemInfoPanel(int16_t left, int16_t top, uint16_t // Voltage float voltage = powerStatus->getBatteryVoltageMv() / 1000.0; char voltageStr[6]; // "XX.XV" - sprintf(voltageStr, "%.1fV", voltage); + sprintf(voltageStr, "%.2fV", voltage); printAt(colC[0], labelT, "Bat", CENTER, TOP); printAt(colC[0], valT, voltageStr, CENTER, TOP); diff --git a/src/main.cpp b/src/main.cpp index d14c768ec..1309acc6f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,6 +23,7 @@ #include "power.h" #if !MESHTASTIC_EXCLUDE_I2C +#include "detect/ScanI2CConsumer.h" #include "detect/ScanI2CTwoWire.h" #include #endif @@ -456,6 +457,12 @@ void setup() LOG_INFO("\n\n//\\ E S H T /\\ S T / C\n"); +#if defined(DEBUG_MUTE) && defined(DEBUG_PORT) + DEBUG_PORT.printf("\r\n\r\n//\\ E S H T /\\ S T / C\r\n"); + DEBUG_PORT.printf("Version %s for %s from %s\r\n", optstr(APP_VERSION), optstr(APP_ENV), optstr(APP_REPO)); + DEBUG_PORT.printf("Debug mute is enabled, there will be no serial output.\r\n"); +#endif + initDeepSleep(); #if defined(MODEM_POWER_EN) @@ -739,46 +746,21 @@ void setup() LOG_DEBUG("acc_info = %i", acc_info.type); #endif - scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::BME_680, meshtastic_TelemetrySensorType_BME680); - scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::BME_280, meshtastic_TelemetrySensorType_BME280); - scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::BMP_280, meshtastic_TelemetrySensorType_BMP280); - scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::BMP_3XX, meshtastic_TelemetrySensorType_BMP3XX); - scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::BMP_085, meshtastic_TelemetrySensorType_BMP085); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA260, meshtastic_TelemetrySensorType_INA260); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA226, meshtastic_TelemetrySensorType_INA226); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA219, meshtastic_TelemetrySensorType_INA219); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA3221, meshtastic_TelemetrySensorType_INA3221); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MAX17048, meshtastic_TelemetrySensorType_MAX17048); - scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MCP9808, meshtastic_TelemetrySensorType_MCP9808); - scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::SHT31, meshtastic_TelemetrySensorType_SHT31); - scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::SHTC3, meshtastic_TelemetrySensorType_SHTC3); - scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::LPS22HB, meshtastic_TelemetrySensorType_LPS22); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::QMC6310, meshtastic_TelemetrySensorType_QMC6310); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::QMI8658, meshtastic_TelemetrySensorType_QMI8658); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::QMC5883L, meshtastic_TelemetrySensorType_QMC5883L); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::HMC5883L, meshtastic_TelemetrySensorType_QMC5883L); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::PMSA0031, meshtastic_TelemetrySensorType_PMSA003I); - scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::RCWL9620, meshtastic_TelemetrySensorType_RCWL9620); - scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::VEML7700, meshtastic_TelemetrySensorType_VEML7700); - scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::TSL2591, meshtastic_TelemetrySensorType_TSL25911FN); - scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::OPT3001, meshtastic_TelemetrySensorType_OPT3001); - scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MLX90632, meshtastic_TelemetrySensorType_MLX90632); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MLX90614, meshtastic_TelemetrySensorType_MLX90614); - scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::SHT4X, meshtastic_TelemetrySensorType_SHT4X); - scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::AHT10, meshtastic_TelemetrySensorType_AHT10); - scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::DFROBOT_LARK, meshtastic_TelemetrySensorType_DFROBOT_LARK); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::ICM20948, meshtastic_TelemetrySensorType_ICM20948); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MAX30102, meshtastic_TelemetrySensorType_MAX30102); - scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::CGRADSENS, meshtastic_TelemetrySensorType_RADSENS); - scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::DFROBOT_RAIN, meshtastic_TelemetrySensorType_DFROBOT_RAIN); - scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::LTR390UV, meshtastic_TelemetrySensorType_LTR390UV); - scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::DPS310, meshtastic_TelemetrySensorType_DPS310); - scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::RAK12035, meshtastic_TelemetrySensorType_RAK12035); - scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::PCT2075, meshtastic_TelemetrySensorType_PCT2075); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::SCD4X, meshtastic_TelemetrySensorType_SCD4X); - scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::TSL2561, meshtastic_TelemetrySensorType_TSL2561); - i2cScanner.reset(); #endif #ifdef HAS_SDCARD @@ -886,7 +868,14 @@ void setup() SPI.begin(); } #elif !defined(ARCH_ESP32) // ARCH_RP2040 +#if defined(RAK3401) || defined(RAK13302) + pinMode(WB_IO2, OUTPUT); + digitalWrite(WB_IO2, HIGH); + SPI1.setPins(LORA_MISO, LORA_SCK, LORA_MOSI); + SPI1.begin(); +#else SPI.begin(); +#endif #else // ESP32 #if defined(HW_SPI1_DEVICE) @@ -985,6 +974,12 @@ void setup() // Now that the mesh service is created, create any modules setupModules(); +#if !MESHTASTIC_EXCLUDE_I2C + // Inform modules about I2C devices + ScanI2CCompleted(i2cScanner.get()); + i2cScanner.reset(); +#endif + // warn the user about a low entropy key if (nodeDB->keyIsLowEntropy && !nodeDB->hasWarned) { LOG_WARN(LOW_ENTROPY_WARNING); diff --git a/src/mesh/LR11x0Interface.cpp b/src/mesh/LR11x0Interface.cpp index 0e23405e5..3831a384d 100644 --- a/src/mesh/LR11x0Interface.cpp +++ b/src/mesh/LR11x0Interface.cpp @@ -218,6 +218,7 @@ template void LR11x0Interface::addReceiveMetadata(meshtastic_Mes // LOG_DEBUG("PacketStatus %x", lora.getPacketStatus()); mp->rx_snr = lora.getSNR(); mp->rx_rssi = lround(lora.getRSSI()); + LOG_DEBUG("Corrected frequency offset: %f", lora.getFrequencyError()); } /** We override to turn on transmitter power as needed. diff --git a/src/mesh/PacketCache.cpp b/src/mesh/PacketCache.cpp new file mode 100644 index 000000000..0edf81741 --- /dev/null +++ b/src/mesh/PacketCache.cpp @@ -0,0 +1,253 @@ +#include "PacketCache.h" +#include "Router.h" + +PacketCache packetCache{}; + +/** + * Allocate a new cache entry and copy the packet header and payload into it + */ +PacketCacheEntry *PacketCache::cache(const meshtastic_MeshPacket *p, bool preserveMetadata) +{ + size_t payload_size = + (p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag) ? p->encrypted.size : p->decoded.payload.size; + PacketCacheEntry *e = (PacketCacheEntry *)malloc(sizeof(PacketCacheEntry) + payload_size + + (preserveMetadata ? sizeof(PacketCacheMetadata) : 0)); + if (!e) { + LOG_ERROR("Unable to allocate memory for packet cache entry"); + return NULL; + } + + *e = {}; + e->header.from = p->from; + e->header.to = p->to; + e->header.id = p->id; + e->header.channel = p->channel; + e->header.next_hop = p->next_hop; + e->header.relay_node = p->relay_node; + e->header.flags = (p->hop_limit & PACKET_FLAGS_HOP_LIMIT_MASK) | (p->want_ack ? PACKET_FLAGS_WANT_ACK_MASK : 0) | + (p->via_mqtt ? PACKET_FLAGS_VIA_MQTT_MASK : 0) | + ((p->hop_start << PACKET_FLAGS_HOP_START_SHIFT) & PACKET_FLAGS_HOP_START_MASK); + + PacketCacheMetadata m{}; + if (preserveMetadata) { + e->has_metadata = true; + m.rx_rssi = (uint8_t)(p->rx_rssi + 200); + m.rx_snr = (uint8_t)((p->rx_snr + 30.0f) / 0.25f); + m.rx_time = p->rx_time; + m.transport_mechanism = p->transport_mechanism; + m.priority = p->priority; + } + if (p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag) { + e->encrypted = true; + e->payload_len = p->encrypted.size; + memcpy(((unsigned char *)e) + sizeof(PacketCacheEntry), p->encrypted.bytes, p->encrypted.size); + } else if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) { + e->encrypted = false; + if (preserveMetadata) { + m.portnum = p->decoded.portnum; + m.want_response = p->decoded.want_response; + m.emoji = p->decoded.emoji; + m.bitfield = p->decoded.bitfield; + if (p->decoded.reply_id) + m.reply_id = p->decoded.reply_id; + else if (p->decoded.request_id) + m.request_id = p->decoded.request_id; + } + e->payload_len = p->decoded.payload.size; + memcpy(((unsigned char *)e) + sizeof(PacketCacheEntry), p->decoded.payload.bytes, p->decoded.payload.size); + } else { + LOG_ERROR("Unable to cache packet with unknown payload type %d", p->which_payload_variant); + free(e); + return NULL; + } + if (preserveMetadata) + memcpy(((unsigned char *)e) + sizeof(PacketCacheEntry) + e->payload_len, &m, sizeof(m)); + + size += sizeof(PacketCacheEntry) + e->payload_len + (e->has_metadata ? sizeof(PacketCacheMetadata) : 0); + insert(e); + return e; +}; + +/** + * Dump a list of packets into the provided buffer + */ +void PacketCache::dump(void *dest, const PacketCacheEntry **entries, size_t num_entries) +{ + unsigned char *pos = (unsigned char *)dest; + for (size_t i = 0; i < num_entries; i++) { + size_t entry_len = + sizeof(PacketCacheEntry) + entries[i]->payload_len + (entries[i]->has_metadata ? sizeof(PacketCacheMetadata) : 0); + memcpy(pos, entries[i], entry_len); + pos += entry_len; + } +} + +/** + * Calculate the length of buffer needed to dump the specified entries + */ +size_t PacketCache::dumpSize(const PacketCacheEntry **entries, size_t num_entries) +{ + size_t total_size = 0; + for (size_t i = 0; i < num_entries; i++) { + total_size += sizeof(PacketCacheEntry) + entries[i]->payload_len; + if (entries[i]->has_metadata) + total_size += sizeof(PacketCacheMetadata); + } + return total_size; +} + +/** + * Find a packet in the cache + */ +PacketCacheEntry *PacketCache::find(NodeNum from, PacketId id) +{ + uint16_t h = PACKET_HASH(from, id); + PacketCacheEntry *e = buckets[PACKET_CACHE_BUCKET(h)]; + while (e) { + if (e->header.from == from && e->header.id == id) + return e; + e = e->next; + } + return NULL; +} + +/** + * Find a packet in the cache by its hash + */ +PacketCacheEntry *PacketCache::find(PacketHash h) +{ + PacketCacheEntry *e = buckets[PACKET_CACHE_BUCKET(h)]; + while (e) { + if (PACKET_HASH(e->header.from, e->header.id) == h) + return e; + e = e->next; + } + return NULL; +} + +/** + * Load a list of packets from the provided buffer + */ +bool PacketCache::load(void *src, PacketCacheEntry **entries, size_t num_entries) +{ + memset(entries, 0, sizeof(PacketCacheEntry *) * num_entries); + unsigned char *pos = (unsigned char *)src; + for (size_t i = 0; i < num_entries; i++) { + PacketCacheEntry e{}; + memcpy(&e, pos, sizeof(PacketCacheEntry)); + size_t entry_len = sizeof(PacketCacheEntry) + e.payload_len + (e.has_metadata ? sizeof(PacketCacheMetadata) : 0); + entries[i] = (PacketCacheEntry *)malloc(entry_len); + size += entry_len; + if (!entries[i]) { + LOG_ERROR("Unable to allocate memory for packet cache entry"); + for (size_t j = 0; j < i; j++) { + size -= sizeof(PacketCacheEntry) + entries[j]->payload_len + + (entries[j]->has_metadata ? sizeof(PacketCacheMetadata) : 0); + free(entries[j]); + entries[j] = NULL; + } + return false; + } + memcpy(entries[i], pos, entry_len); + pos += entry_len; + } + for (size_t i = 0; i < num_entries; i++) + insert(entries[i]); + return true; +} + +/** + * Copy the cached packet into the provided MeshPacket structure + */ +void PacketCache::rehydrate(const PacketCacheEntry *e, meshtastic_MeshPacket *p) +{ + if (!e || !p) + return; + + *p = {}; + p->from = e->header.from; + p->to = e->header.to; + p->id = e->header.id; + p->channel = e->header.channel; + p->next_hop = e->header.next_hop; + p->relay_node = e->header.relay_node; + p->hop_limit = e->header.flags & PACKET_FLAGS_HOP_LIMIT_MASK; + p->want_ack = !!(e->header.flags & PACKET_FLAGS_WANT_ACK_MASK); + p->via_mqtt = !!(e->header.flags & PACKET_FLAGS_VIA_MQTT_MASK); + p->hop_start = (e->header.flags & PACKET_FLAGS_HOP_START_MASK) >> PACKET_FLAGS_HOP_START_SHIFT; + p->which_payload_variant = e->encrypted ? meshtastic_MeshPacket_encrypted_tag : meshtastic_MeshPacket_decoded_tag; + + unsigned char *payload = ((unsigned char *)e) + sizeof(PacketCacheEntry); + PacketCacheMetadata m{}; + if (e->has_metadata) { + memcpy(&m, (payload + e->payload_len), sizeof(m)); + p->rx_rssi = ((int)m.rx_rssi) - 200; + p->rx_snr = ((float)m.rx_snr * 0.25f) - 30.0f; + p->rx_time = m.rx_time; + p->transport_mechanism = (meshtastic_MeshPacket_TransportMechanism)m.transport_mechanism; + p->priority = (meshtastic_MeshPacket_Priority)m.priority; + } + if (e->encrypted) { + memcpy(p->encrypted.bytes, payload, e->payload_len); + p->encrypted.size = e->payload_len; + } else { + memcpy(p->decoded.payload.bytes, payload, e->payload_len); + p->decoded.payload.size = e->payload_len; + if (e->has_metadata) { + // Decrypted-only metadata + p->decoded.portnum = (meshtastic_PortNum)m.portnum; + p->decoded.want_response = m.want_response; + p->decoded.emoji = m.emoji; + p->decoded.bitfield = m.bitfield; + if (m.reply_id) + p->decoded.reply_id = m.reply_id; + else if (m.request_id) + p->decoded.request_id = m.request_id; + } + } +} + +/** + * Release a cache entry + */ +void PacketCache::release(PacketCacheEntry *e) +{ + if (!e) + return; + remove(e); + size -= sizeof(PacketCacheEntry) + e->payload_len + (e->has_metadata ? sizeof(PacketCacheMetadata) : 0); + free(e); +} + +/** + * Insert a new entry into the hash table + */ +void PacketCache::insert(PacketCacheEntry *e) +{ + assert(e); + PacketHash h = PACKET_HASH(e->header.from, e->header.id); + PacketCacheEntry **target = &buckets[PACKET_CACHE_BUCKET(h)]; + e->next = *target; + *target = e; + num_entries++; +} + +/** + * Remove an entry from the hash table + */ +void PacketCache::remove(PacketCacheEntry *e) +{ + assert(e); + PacketHash h = PACKET_HASH(e->header.from, e->header.id); + PacketCacheEntry **target = &buckets[PACKET_CACHE_BUCKET(h)]; + while (*target) { + if (*target == e) { + *target = e->next; + e->next = NULL; + num_entries--; + break; + } else { + target = &(*target)->next; + } + } +} \ No newline at end of file diff --git a/src/mesh/PacketCache.h b/src/mesh/PacketCache.h new file mode 100644 index 000000000..81ad455da --- /dev/null +++ b/src/mesh/PacketCache.h @@ -0,0 +1,75 @@ +#pragma once +#include "RadioInterface.h" + +#define PACKET_HASH(a, b) ((((a ^ b) >> 16) ^ (a ^ b)) & 0xFFFF) // 16 bit fold of packet (from, id) tuple +typedef uint16_t PacketHash; + +#define PACKET_CACHE_BUCKETS 64 // Number of hash table buckets +#define PACKET_CACHE_BUCKET(h) (((h >> 12) ^ (h >> 6) ^ h) & 0x3F) // Fold hash down to 6-bit bucket index + +typedef struct PacketCacheEntry { + PacketCacheEntry *next; + PacketHeader header; + uint16_t payload_len = 0; + union { + uint16_t bitfield; + struct { + uint8_t encrypted : 1; // Payload is encrypted + uint8_t has_metadata : 1; // Payload includes PacketCacheMetadata + uint8_t : 6; // Reserved for future use + uint16_t : 8; // Reserved for future use + }; + }; +} PacketCacheEntry; + +typedef struct PacketCacheMetadata { + PacketCacheMetadata() : _bitfield(0), reply_id(0), _bitfield2(0) {} + union { + uint32_t _bitfield; + struct { + uint16_t portnum : 9; // meshtastic_MeshPacket::decoded::portnum + uint16_t want_response : 1; // meshtastic_MeshPacket::decoded::want_response + uint16_t emoji : 1; // meshtastic_MeshPacket::decoded::emoji + uint16_t bitfield : 5; // meshtastic_MeshPacket::decoded::bitfield (truncated) + uint8_t rx_rssi : 8; // meshtastic_MeshPacket::rx_rssi (map via actual RSSI + 200) + uint8_t rx_snr : 8; // meshtastic_MeshPacket::rx_snr (map via (p->rx_snr + 30.0f) / 0.25f) + }; + }; + union { + uint32_t reply_id; // meshtastic_MeshPacket::decoded.reply_id + uint32_t request_id; // meshtastic_MeshPacket::decoded.request_id + }; + uint32_t rx_time = 0; // meshtastic_MeshPacket::rx_time + uint8_t transport_mechanism = 0; // meshtastic_MeshPacket::transport_mechanism + struct { + uint8_t _bitfield2; + union { + uint8_t priority : 7; // meshtastic_MeshPacket::priority + uint8_t reserved : 1; // Reserved for future use + }; + }; +} PacketCacheMetadata; + +class PacketCache +{ + public: + PacketCacheEntry *cache(const meshtastic_MeshPacket *p, bool preserveMetadata); + static void dump(void *dest, const PacketCacheEntry **entries, size_t num_entries); + size_t dumpSize(const PacketCacheEntry **entries, size_t num_entries); + PacketCacheEntry *find(NodeNum from, PacketId id); + PacketCacheEntry *find(PacketHash h); + bool load(void *src, PacketCacheEntry **entries, size_t num_entries); + size_t getNumEntries() { return num_entries; } + size_t getSize() { return size; } + void rehydrate(const PacketCacheEntry *e, meshtastic_MeshPacket *p); + void release(PacketCacheEntry *e); + + private: + PacketCacheEntry *buckets[PACKET_CACHE_BUCKETS]{}; + size_t num_entries = 0; + size_t size = 0; + void insert(PacketCacheEntry *e); + void remove(PacketCacheEntry *e); +}; + +extern PacketCache packetCache; \ No newline at end of file diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index 210e0ba06..d1e342c80 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -15,6 +15,7 @@ #include "Router.h" #include "SPILock.h" #include "TypeConversions.h" +#include "concurrency/LockGuard.h" #include "main.h" #include "xmodem.h" @@ -56,6 +57,9 @@ void PhoneAPI::handleStartConfig() #endif } + // Allow subclasses to prepare for high-throughput config traffic + onConfigStart(); + // even if we were already connected - restart our state machine if (config_nonce == SPECIAL_NONCE_ONLY_NODES) { // If client only wants node info, jump directly to sending nodes @@ -70,8 +74,13 @@ void PhoneAPI::handleStartConfig() spiLock->unlock(); LOG_DEBUG("Got %d files in manifest", filesManifest.size()); - LOG_INFO("Start API client config"); - nodeInfoForPhone.num = 0; // Don't keep returning old nodeinfos + LOG_INFO("Start API client config millis=%u", millis()); + // Protect against concurrent BLE callbacks: they run in NimBLE's FreeRTOS task and also touch nodeInfoQueue. + { + concurrency::LockGuard guard(&nodeInfoMutex); + nodeInfoForPhone = {}; + nodeInfoQueue.clear(); + } resetReadIndex(); } @@ -93,7 +102,12 @@ void PhoneAPI::close() onConnectionChanged(false); fromRadioScratch = {}; toRadioScratch = {}; - nodeInfoForPhone = {}; + // Clear cached node info under lock because NimBLE callbacks can still be draining it. + { + concurrency::LockGuard guard(&nodeInfoMutex); + nodeInfoForPhone = {}; + nodeInfoQueue.clear(); + } packetForPhone = NULL; filesManifest.clear(); fromRadioNum = 0; @@ -148,6 +162,10 @@ bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength) #if !MESHTASTIC_EXCLUDE_MQTT case meshtastic_ToRadio_mqttClientProxyMessage_tag: LOG_DEBUG("Got MqttClientProxy message"); + if (state != STATE_SEND_PACKETS) { + LOG_WARN("Ignore MqttClientProxy message while completing config handshake"); + break; + } if (mqtt && moduleConfig.mqtt.proxy_to_client_enabled && moduleConfig.mqtt.enabled && (channels.anyMqttEnabled() || moduleConfig.mqtt.map_reporting_enabled)) { mqtt->onClientProxyReceive(toRadioScratch.mqttClientProxyMessage); @@ -239,13 +257,20 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) LOG_DEBUG("Send My NodeInfo"); auto us = nodeDB->readNextMeshNode(readIndex); if (us) { - nodeInfoForPhone = TypeConversions::ConvertToNodeInfo(us); - nodeInfoForPhone.has_hops_away = false; - nodeInfoForPhone.is_favorite = true; + auto info = TypeConversions::ConvertToNodeInfo(us); + info.has_hops_away = false; + info.is_favorite = true; + { + concurrency::LockGuard guard(&nodeInfoMutex); + nodeInfoForPhone = info; + } fromRadioScratch.which_payload_variant = meshtastic_FromRadio_node_info_tag; - fromRadioScratch.node_info = nodeInfoForPhone; + fromRadioScratch.node_info = info; // Should allow us to resume sending NodeInfo in STATE_SEND_OTHER_NODEINFOS - nodeInfoForPhone.num = 0; + { + concurrency::LockGuard guard(&nodeInfoMutex); + nodeInfoForPhone.num = 0; + } } if (config_nonce == SPECIAL_NONCE_ONLY_NODES) { // If client only wants node info, jump directly to sending nodes @@ -431,18 +456,44 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) break; case STATE_SEND_OTHER_NODEINFOS: { - LOG_DEBUG("Send known nodes"); - if (nodeInfoForPhone.num != 0) { + if (readIndex == 2) { // readIndex==2 will be true for the first non-us node + LOG_INFO("Start sending nodeinfos millis=%u", millis()); + } + + meshtastic_NodeInfo infoToSend = {}; + { + concurrency::LockGuard guard(&nodeInfoMutex); + if (nodeInfoForPhone.num == 0 && !nodeInfoQueue.empty()) { + // Serve the next cached node without re-reading from the DB iterator. + nodeInfoForPhone = nodeInfoQueue.front(); + nodeInfoQueue.pop_front(); + } + infoToSend = nodeInfoForPhone; + if (infoToSend.num != 0) + nodeInfoForPhone = {}; + } + + if (infoToSend.num != 0) { // Just in case we stored a different user.id in the past, but should never happen going forward - sprintf(nodeInfoForPhone.user.id, "!%08x", nodeInfoForPhone.num); - LOG_INFO("nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s", nodeInfoForPhone.num, nodeInfoForPhone.last_heard, - nodeInfoForPhone.user.id, nodeInfoForPhone.user.long_name); + sprintf(infoToSend.user.id, "!%08x", infoToSend.num); + + // Logging this really slows down sending nodes on initial connection because the serial console is so slow, so only + // uncomment if you really need to: + // LOG_INFO("nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s", nodeInfoForPhone.num, nodeInfoForPhone.last_heard, + // nodeInfoForPhone.user.id, nodeInfoForPhone.user.long_name); + + // Occasional progress logging. (readIndex==2 will be true for the first non-us node) + if (readIndex == 2 || readIndex % 20 == 0) { + LOG_DEBUG("nodeinfo: %d/%d", readIndex, nodeDB->getNumMeshNodes()); + } + fromRadioScratch.which_payload_variant = meshtastic_FromRadio_node_info_tag; - fromRadioScratch.node_info = nodeInfoForPhone; - // Stay in current state until done sending nodeinfos - nodeInfoForPhone.num = 0; // We just consumed a nodeinfo, will need a new one next time + fromRadioScratch.node_info = infoToSend; + prefetchNodeInfos(); } else { - LOG_DEBUG("Done sending nodeinfo"); + LOG_DEBUG("Done sending %d of %d nodeinfos millis=%u", readIndex, nodeDB->getNumMeshNodes(), millis()); + concurrency::LockGuard guard(&nodeInfoMutex); + nodeInfoQueue.clear(); state = STATE_SEND_FILEMANIFEST; // Go ahead and send that ID right now return getFromRadio(buf); @@ -522,11 +573,15 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) void PhoneAPI::sendConfigComplete() { - LOG_INFO("Config Send Complete"); + LOG_INFO("Config Send Complete millis=%u", millis()); fromRadioScratch.which_payload_variant = meshtastic_FromRadio_config_complete_id_tag; fromRadioScratch.config_complete_id = config_nonce; config_nonce = 0; state = STATE_SEND_PACKETS; + + // Allow subclasses to know we've entered steady-state so they can lower power consumption + onConfigComplete(); + pauseBluetoothLogging = false; } @@ -546,6 +601,33 @@ void PhoneAPI::releaseQueueStatusPhonePacket() } } +void PhoneAPI::prefetchNodeInfos() +{ + bool added = false; + // Keep the queue topped up so BLE reads stay responsive even if DB fetches take a moment. + { + concurrency::LockGuard guard(&nodeInfoMutex); + while (nodeInfoQueue.size() < kNodePrefetchDepth) { + auto nextNode = nodeDB->readNextMeshNode(readIndex); + if (!nextNode) + break; + + auto info = TypeConversions::ConvertToNodeInfo(nextNode); + bool isUs = info.num == nodeDB->getNodeNum(); + info.hops_away = isUs ? 0 : info.hops_away; + info.last_heard = isUs ? getValidTime(RTCQualityFromNet) : info.last_heard; + info.snr = isUs ? 0 : info.snr; + info.via_mqtt = isUs ? false : info.via_mqtt; + info.is_favorite = info.is_favorite || isUs; + nodeInfoQueue.push_back(info); + added = true; + } + } + + if (added) + onNowHasData(0); +} + void PhoneAPI::releaseMqttClientProxyPhonePacket() { if (mqttClientProxyMessageForPhone) { @@ -581,21 +663,17 @@ bool PhoneAPI::available() case STATE_SEND_COMPLETE_ID: return true; - case STATE_SEND_OTHER_NODEINFOS: - if (nodeInfoForPhone.num == 0) { - auto nextNode = nodeDB->readNextMeshNode(readIndex); - if (nextNode) { - nodeInfoForPhone = TypeConversions::ConvertToNodeInfo(nextNode); - bool isUs = nodeInfoForPhone.num == nodeDB->getNodeNum(); - nodeInfoForPhone.hops_away = isUs ? 0 : nodeInfoForPhone.hops_away; - nodeInfoForPhone.last_heard = isUs ? getValidTime(RTCQualityFromNet) : nodeInfoForPhone.last_heard; - nodeInfoForPhone.snr = isUs ? 0 : nodeInfoForPhone.snr; - nodeInfoForPhone.via_mqtt = isUs ? false : nodeInfoForPhone.via_mqtt; - nodeInfoForPhone.is_favorite = nodeInfoForPhone.is_favorite || isUs; // Our node is always a favorite - onNowHasData(0); - } + case STATE_SEND_OTHER_NODEINFOS: { + concurrency::LockGuard guard(&nodeInfoMutex); + if (nodeInfoQueue.empty()) { + // Drop the lock before prefetching; prefetchNodeInfos() will re-acquire it. + goto PREFETCH_NODEINFO; } + } return true; // Always say we have something, because we might need to advance our state machine + PREFETCH_NODEINFO: + prefetchNodeInfos(); + return true; case STATE_SEND_PACKETS: { if (!queueStatusPacketForPhone) queueStatusPacketForPhone = service->getQueueStatusForPhone(); @@ -734,7 +812,7 @@ int PhoneAPI::onNotify(uint32_t newValue) LOG_INFO("Tell client we have new packets %u", newValue); onNowHasData(newValue); } else { - LOG_DEBUG("(Client not yet interested in packets)"); + LOG_DEBUG("Client not yet interested in packets (state=%d)", state); } return timeout ? -1 : 0; // If we timed out, MeshService should stop iterating through observers as we just removed one diff --git a/src/mesh/PhoneAPI.h b/src/mesh/PhoneAPI.h index 0d7772d17..d6682684f 100644 --- a/src/mesh/PhoneAPI.h +++ b/src/mesh/PhoneAPI.h @@ -1,8 +1,10 @@ #pragma once #include "Observer.h" +#include "concurrency/Lock.h" #include "mesh-pb-constants.h" #include "meshtastic/portnums.pb.h" +#include #include #include #include @@ -79,6 +81,12 @@ class PhoneAPI /// We temporarily keep the nodeInfo here between the call to available and getFromRadio meshtastic_NodeInfo nodeInfoForPhone = meshtastic_NodeInfo_init_default; + // Prefetched node info entries ready for immediate transmission to the phone. + std::deque nodeInfoQueue; + // Tunable size of the node info cache so we can keep BLE reads non-blocking. + static constexpr size_t kNodePrefetchDepth = 4; + // Protect nodeInfoForPhone + nodeInfoQueue because NimBLE callbacks run in a separate FreeRTOS task. + concurrency::Lock nodeInfoMutex; meshtastic_ToRadio toRadioScratch = { 0}; // this is a static scratch object, any data must be copied elsewhere before returning @@ -128,6 +136,7 @@ class PhoneAPI bool available(); bool isConnected() { return state != STATE_SEND_NOTHING; } + bool isSendingPackets() { return state == STATE_SEND_PACKETS; } protected: /// Our fromradio packet while it is being assembled @@ -150,6 +159,11 @@ class PhoneAPI */ virtual void onNowHasData(uint32_t fromRadioNum) {} + /// Subclasses can use these lifecycle hooks for transport-specific behavior around config/steady-state + /// (i.e. BLE connection params) + virtual void onConfigStart() {} + virtual void onConfigComplete() {} + /// begin a new connection void handleStartConfig(); @@ -158,6 +172,8 @@ class PhoneAPI void releaseQueueStatusPhonePacket(); + void prefetchNodeInfos(); + void releaseMqttClientProxyPhonePacket(); void releaseClientNotification(); diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 88218e406..31ec5acc5 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -647,23 +647,24 @@ void RadioInterface::limitPower(int8_t loraMaxPower) } #ifndef NUM_PA_POINTS - if (TX_GAIN_LORA > 0) { + if (TX_GAIN_LORA > 0 && !devicestate.owner.is_licensed) { LOG_INFO("Requested Tx power: %d dBm; Device LoRa Tx gain: %d dB", power, TX_GAIN_LORA); power -= TX_GAIN_LORA; } #else - // we have an array of PA gain values. Find the highest power setting that works. - const uint16_t tx_gain[NUM_PA_POINTS] = {TX_GAIN_LORA}; - for (int radio_dbm = 0; radio_dbm < NUM_PA_POINTS; radio_dbm++) { - if (((radio_dbm + tx_gain[radio_dbm]) > power) || - ((radio_dbm == (NUM_PA_POINTS - 1)) && ((radio_dbm + tx_gain[radio_dbm]) <= power))) { - // we've exceeded the power limit, or hit the max we can do - LOG_INFO("Requested Tx power: %d dBm; Device LoRa Tx gain: %d dB", power, tx_gain[radio_dbm]); - power -= tx_gain[radio_dbm]; - break; + if (!devicestate.owner.is_licensed) { + // we have an array of PA gain values. Find the highest power setting that works. + const uint16_t tx_gain[NUM_PA_POINTS] = {TX_GAIN_LORA}; + for (int radio_dbm = 0; radio_dbm < NUM_PA_POINTS; radio_dbm++) { + if (((radio_dbm + tx_gain[radio_dbm]) > power) || + ((radio_dbm == (NUM_PA_POINTS - 1)) && ((radio_dbm + tx_gain[radio_dbm]) <= power))) { + // we've exceeded the power limit, or hit the max we can do + LOG_INFO("Requested Tx power: %d dBm; Device LoRa Tx gain: %d dB", power, tx_gain[radio_dbm]); + power -= tx_gain[radio_dbm]; + break; + } } } - #endif if (power > loraMaxPower) // Clamp power to maximum defined level power = loraMaxPower; diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 60637cbd1..5cf8bfa7d 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -35,6 +35,15 @@ (MAX_RX_TOPHONE + MAX_RX_FROMRADIO + 2 * MAX_TX_QUEUE + \ 2) // max number of packets which can be in flight (either queued from reception or queued for sending) +static MemoryDynamic dynamicPool; +Allocator &packetPool = dynamicPool; +#elif defined(ARCH_STM32WL) +// On STM32 there isn't enough heap left over for the rest of the firmware if we allocate this statically. +// For now, make it dynamic again. +#define MAX_PACKETS \ + (MAX_RX_TOPHONE + MAX_RX_FROMRADIO + 2 * MAX_TX_QUEUE + \ + 2) // max number of packets which can be in flight (either queued from reception or queued for sending) + static MemoryDynamic dynamicPool; Allocator &packetPool = dynamicPool; #else diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index c60ef3a80..e1f07a32b 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -266,6 +266,7 @@ template void SX126xInterface::addReceiveMetadata(meshtastic_Mes // LOG_DEBUG("PacketStatus %x", lora.getPacketStatus()); mp->rx_snr = lora.getSNR(); mp->rx_rssi = lround(lora.getRSSI()); + LOG_DEBUG("Corrected frequency offset: %f", lora.getFrequencyError()); } /** We override to turn on transmitter power as needed. diff --git a/src/mesh/SX128xInterface.cpp b/src/mesh/SX128xInterface.cpp index cbc98eeb1..80872af07 100644 --- a/src/mesh/SX128xInterface.cpp +++ b/src/mesh/SX128xInterface.cpp @@ -204,6 +204,7 @@ template void SX128xInterface::addReceiveMetadata(meshtastic_Mes // LOG_DEBUG("PacketStatus %x", lora.getPacketStatus()); mp->rx_snr = lora.getSNR(); mp->rx_rssi = lround(lora.getRSSI()); + LOG_DEBUG("Corrected frequency offset: %f", lora.getFrequencyError()); } /** We override to turn on transmitter power as needed. diff --git a/src/mesh/generated/meshtastic/apponly.pb.h b/src/mesh/generated/meshtastic/apponly.pb.h index db9dedaaf..f4c33bd79 100644 --- a/src/mesh/generated/meshtastic/apponly.pb.h +++ b/src/mesh/generated/meshtastic/apponly.pb.h @@ -55,7 +55,7 @@ extern const pb_msgdesc_t meshtastic_ChannelSet_msg; /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_APPONLY_PB_H_MAX_SIZE meshtastic_ChannelSet_size -#define meshtastic_ChannelSet_size 695 +#define meshtastic_ChannelSet_size 679 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/meshtastic/channel.pb.h b/src/mesh/generated/meshtastic/channel.pb.h index d5573a1e2..9dc757ab4 100644 --- a/src/mesh/generated/meshtastic/channel.pb.h +++ b/src/mesh/generated/meshtastic/channel.pb.h @@ -34,9 +34,9 @@ typedef enum _meshtastic_Channel_Role { typedef struct _meshtastic_ModuleSettings { /* Bits of precision for the location sent in position packets. */ uint32_t position_precision; - /* Controls whether or not the phone / clients should mute the current channel + /* Controls whether or not the client / device should mute the current channel Useful for noisy public channels you don't necessarily want to disable */ - bool is_client_muted; + bool is_muted; } meshtastic_ModuleSettings; typedef PB_BYTES_ARRAY_T(32) meshtastic_ChannelSettings_psk_t; @@ -97,8 +97,6 @@ typedef struct _meshtastic_ChannelSettings { /* Per-channel module settings. */ bool has_module_settings; meshtastic_ModuleSettings module_settings; - /* Whether or not we should receive notifactions / alerts through this channel */ - bool mute; } meshtastic_ChannelSettings; /* A pair of a channel number, mode and the (sharable) settings for that channel */ @@ -130,16 +128,16 @@ extern "C" { /* Initializer values for message structs */ -#define meshtastic_ChannelSettings_init_default {0, {0, {0}}, "", 0, 0, 0, false, meshtastic_ModuleSettings_init_default, 0} +#define meshtastic_ChannelSettings_init_default {0, {0, {0}}, "", 0, 0, 0, false, meshtastic_ModuleSettings_init_default} #define meshtastic_ModuleSettings_init_default {0, 0} #define meshtastic_Channel_init_default {0, false, meshtastic_ChannelSettings_init_default, _meshtastic_Channel_Role_MIN} -#define meshtastic_ChannelSettings_init_zero {0, {0, {0}}, "", 0, 0, 0, false, meshtastic_ModuleSettings_init_zero, 0} +#define meshtastic_ChannelSettings_init_zero {0, {0, {0}}, "", 0, 0, 0, false, meshtastic_ModuleSettings_init_zero} #define meshtastic_ModuleSettings_init_zero {0, 0} #define meshtastic_Channel_init_zero {0, false, meshtastic_ChannelSettings_init_zero, _meshtastic_Channel_Role_MIN} /* Field tags (for use in manual encoding/decoding) */ #define meshtastic_ModuleSettings_position_precision_tag 1 -#define meshtastic_ModuleSettings_is_client_muted_tag 2 +#define meshtastic_ModuleSettings_is_muted_tag 2 #define meshtastic_ChannelSettings_channel_num_tag 1 #define meshtastic_ChannelSettings_psk_tag 2 #define meshtastic_ChannelSettings_name_tag 3 @@ -147,7 +145,6 @@ extern "C" { #define meshtastic_ChannelSettings_uplink_enabled_tag 5 #define meshtastic_ChannelSettings_downlink_enabled_tag 6 #define meshtastic_ChannelSettings_module_settings_tag 7 -#define meshtastic_ChannelSettings_mute_tag 8 #define meshtastic_Channel_index_tag 1 #define meshtastic_Channel_settings_tag 2 #define meshtastic_Channel_role_tag 3 @@ -160,15 +157,14 @@ X(a, STATIC, SINGULAR, STRING, name, 3) \ X(a, STATIC, SINGULAR, FIXED32, id, 4) \ X(a, STATIC, SINGULAR, BOOL, uplink_enabled, 5) \ X(a, STATIC, SINGULAR, BOOL, downlink_enabled, 6) \ -X(a, STATIC, OPTIONAL, MESSAGE, module_settings, 7) \ -X(a, STATIC, SINGULAR, BOOL, mute, 8) +X(a, STATIC, OPTIONAL, MESSAGE, module_settings, 7) #define meshtastic_ChannelSettings_CALLBACK NULL #define meshtastic_ChannelSettings_DEFAULT NULL #define meshtastic_ChannelSettings_module_settings_MSGTYPE meshtastic_ModuleSettings #define meshtastic_ModuleSettings_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UINT32, position_precision, 1) \ -X(a, STATIC, SINGULAR, BOOL, is_client_muted, 2) +X(a, STATIC, SINGULAR, BOOL, is_muted, 2) #define meshtastic_ModuleSettings_CALLBACK NULL #define meshtastic_ModuleSettings_DEFAULT NULL @@ -191,8 +187,8 @@ extern const pb_msgdesc_t meshtastic_Channel_msg; /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_CHANNEL_PB_H_MAX_SIZE meshtastic_Channel_size -#define meshtastic_ChannelSettings_size 74 -#define meshtastic_Channel_size 89 +#define meshtastic_ChannelSettings_size 72 +#define meshtastic_Channel_size 87 #define meshtastic_ModuleSettings_size 8 #ifdef __cplusplus diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.h b/src/mesh/generated/meshtastic/deviceonly.pb.h index b5b116137..7fab82ff7 100644 --- a/src/mesh/generated/meshtastic/deviceonly.pb.h +++ b/src/mesh/generated/meshtastic/deviceonly.pb.h @@ -360,8 +360,8 @@ extern const pb_msgdesc_t meshtastic_BackupPreferences_msg; /* Maximum encoded size of messages (where known) */ /* meshtastic_NodeDatabase_size depends on runtime parameters */ #define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_BackupPreferences_size -#define meshtastic_BackupPreferences_size 2293 -#define meshtastic_ChannelFile_size 734 +#define meshtastic_BackupPreferences_size 2277 +#define meshtastic_ChannelFile_size 718 #define meshtastic_DeviceState_size 1737 #define meshtastic_NodeInfoLite_size 196 #define meshtastic_PositionLite_size 28 diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index d8d2f2e8a..059af57ae 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -282,6 +282,8 @@ typedef enum _meshtastic_HardwareModel { meshtastic_HardwareModel_HELTEC_WIRELESS_TRACKER_V2 = 113, /* LilyGo T-Watch Ultra */ meshtastic_HardwareModel_T_WATCH_ULTRA = 114, + /* Elecrow ThinkNode M3 */ + meshtastic_HardwareModel_THINKNODE_M3 = 115, /* ------------------------------------------------------------------------------------------------------------------------------------------ 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/generated/meshtastic/telemetry.pb.h b/src/mesh/generated/meshtastic/telemetry.pb.h index a8ca96e95..dec89ba15 100644 --- a/src/mesh/generated/meshtastic/telemetry.pb.h +++ b/src/mesh/generated/meshtastic/telemetry.pb.h @@ -101,7 +101,9 @@ typedef enum _meshtastic_TelemetrySensorType { /* SEN5X PM SENSORS */ meshtastic_TelemetrySensorType_SEN5X = 43, /* TSL2561 light sensor */ - meshtastic_TelemetrySensorType_TSL2561 = 44 + meshtastic_TelemetrySensorType_TSL2561 = 44, + /* BH1750 light sensor */ + meshtastic_TelemetrySensorType_BH1750 = 45 } meshtastic_TelemetrySensorType; /* Struct definitions */ @@ -438,8 +440,8 @@ extern "C" { /* Helper constants for enums */ #define _meshtastic_TelemetrySensorType_MIN meshtastic_TelemetrySensorType_SENSOR_UNSET -#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_TSL2561 -#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_TSL2561+1)) +#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_BH1750 +#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_BH1750+1)) diff --git a/src/mesh/wifi/WiFiAPClient.cpp b/src/mesh/wifi/WiFiAPClient.cpp index 7d210dd33..18f67706a 100644 --- a/src/mesh/wifi/WiFiAPClient.cpp +++ b/src/mesh/wifi/WiFiAPClient.cpp @@ -95,10 +95,12 @@ static void onNetworkConnected() #ifdef ARCH_ESP32 MDNS.addServiceTxt("meshtastic", "tcp", "shortname", String(owner.short_name)); MDNS.addServiceTxt("meshtastic", "tcp", "id", String(nodeDB->getNodeId().c_str())); + MDNS.addServiceTxt("meshtastic", "tcp", "pio_env", optstr(APP_ENV)); // ESP32 prints obtained IP address in WiFiEvent #elif defined(ARCH_RP2040) MDNS.addServiceTxt("meshtastic", "shortname", owner.short_name); MDNS.addServiceTxt("meshtastic", "id", nodeDB->getNodeId().c_str()); + MDNS.addServiceTxt("meshtastic", "pio_env", optstr(APP_ENV)); LOG_INFO("Obtained IP address: %s", WiFi.localIP().toString().c_str()); #endif } diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index e9f52bb7d..9f95a9e20 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -255,7 +255,7 @@ void CannedMessageModule::updateDestinationSelectionList() for (size_t i = 0; i < numMeshNodes; ++i) { meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i); - if (!node || node->num == myNodeNum) + if (!node || node->num == myNodeNum || !node->has_user || node->user.public_key.size != 32) continue; const String &nodeName = node->user.long_name; @@ -976,6 +976,8 @@ void CannedMessageModule::sendText(NodeNum dest, ChannelIndex channel, const cha LOG_INFO("Proactively adding %x as favorite node", p->to); nodeDB->set_favorite(true, p->to); screen->setFrames(graphics::Screen::FOCUS_PRESERVE); + p->pki_encrypted = true; + p->channel = 0; } // Send to mesh and phone (even if no phone connected, to track ACKs) diff --git a/src/modules/ExternalNotificationModule.cpp b/src/modules/ExternalNotificationModule.cpp index 2346cd299..ffc789275 100644 --- a/src/modules/ExternalNotificationModule.cpp +++ b/src/modules/ExternalNotificationModule.cpp @@ -510,7 +510,8 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP } } - if (moduleConfig.external_notification.alert_message && !ch.settings.mute) { + if (moduleConfig.external_notification.alert_message && + (!ch.settings.has_module_settings || !ch.settings.module_settings.is_muted)) { LOG_INFO("externalNotificationModule - Notification Module"); isNagging = true; setExternalState(0, true); @@ -521,7 +522,8 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP } } - if (moduleConfig.external_notification.alert_message_vibra && !ch.settings.mute) { + if (moduleConfig.external_notification.alert_message_vibra && + (!ch.settings.has_module_settings || !ch.settings.module_settings.is_muted)) { LOG_INFO("externalNotificationModule - Notification Module (Vibra)"); isNagging = true; setExternalState(1, true); @@ -532,7 +534,8 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP } } - if (moduleConfig.external_notification.alert_message_buzzer && !ch.settings.mute) { + if (moduleConfig.external_notification.alert_message_buzzer && + (!ch.settings.has_module_settings || !ch.settings.module_settings.is_muted)) { LOG_INFO("externalNotificationModule - Notification Module (Buzzer)"); if (config.device.buzzer_mode != meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY || (!isBroadcast(mp.to) && isToUs(&mp))) { diff --git a/src/modules/Telemetry/DeviceTelemetry.cpp b/src/modules/Telemetry/DeviceTelemetry.cpp index 7e3018564..066b9361d 100644 --- a/src/modules/Telemetry/DeviceTelemetry.cpp +++ b/src/modules/Telemetry/DeviceTelemetry.cpp @@ -26,7 +26,8 @@ int32_t DeviceTelemetryModule::runOnce() Default::getConfiguredOrDefaultMsScaled(moduleConfig.telemetry.device_update_interval, default_telemetry_broadcast_interval_secs, numOnlineNodes))) && airTime->isTxAllowedChannelUtil(!isImpoliteRole) && airTime->isTxAllowedAirUtil() && - config.device.role != meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN) { + config.device.role != meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN && + moduleConfig.telemetry.device_telemetry_enabled) { sendTelemetry(); lastSentToMesh = uptimeLastMs; } else if (service->isToPhoneQueueEmpty()) { diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index 8ac160f8b..2337af808 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -35,175 +35,103 @@ extern void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const c } #if __has_include() #include "Sensor/AHT10.h" -AHT10Sensor aht10Sensor; -#else -NullSensor aht10Sensor; #endif + #if __has_include() #include "Sensor/BME280Sensor.h" -BME280Sensor bme280Sensor; -#else -NullSensor bmp280Sensor; #endif #if __has_include() #include "Sensor/BMP085Sensor.h" -BMP085Sensor bmp085Sensor; -#else -NullSensor bmp085Sensor; #endif #if __has_include() #include "Sensor/BMP280Sensor.h" -BMP280Sensor bmp280Sensor; -#else -NullSensor bme280Sensor; #endif #if __has_include() #include "Sensor/LTR390UVSensor.h" -LTR390UVSensor ltr390uvSensor; -#else -NullSensor ltr390uvSensor; #endif #if __has_include() #include "Sensor/BME680Sensor.h" -BME680Sensor bme680Sensor; -#else -NullSensor bme680Sensor; #endif #if __has_include() #include "Sensor/DPS310Sensor.h" -DPS310Sensor dps310Sensor; -#else -NullSensor dps310Sensor; #endif #if __has_include() #include "Sensor/MCP9808Sensor.h" -MCP9808Sensor mcp9808Sensor; -#else -NullSensor mcp9808Sensor; #endif #if __has_include() #include "Sensor/SHT31Sensor.h" -SHT31Sensor sht31Sensor; -#else -NullSensor sht31Sensor; #endif #if __has_include() #include "Sensor/LPS22HBSensor.h" -LPS22HBSensor lps22hbSensor; -#else -NullSensor lps22hbSensor; #endif #if __has_include() #include "Sensor/SHTC3Sensor.h" -SHTC3Sensor shtc3Sensor; -#else -NullSensor shtc3Sensor; #endif #if __has_include("RAK12035_SoilMoisture.h") && defined(RAK_4631) && RAK_4631 == 1 #include "Sensor/RAK12035Sensor.h" -RAK12035Sensor rak12035Sensor; -#else -NullSensor rak12035Sensor; #endif #if __has_include() #include "Sensor/VEML7700Sensor.h" -VEML7700Sensor veml7700Sensor; -#else -NullSensor veml7700Sensor; #endif #if __has_include() #include "Sensor/TSL2591Sensor.h" -TSL2591Sensor tsl2591Sensor; -#else -NullSensor tsl2591Sensor; #endif #if __has_include() #include "Sensor/OPT3001Sensor.h" -OPT3001Sensor opt3001Sensor; -#else -NullSensor opt3001Sensor; #endif #if __has_include() #include "Sensor/SHT4XSensor.h" -SHT4XSensor sht4xSensor; -#else -NullSensor sht4xSensor; #endif #if __has_include() #include "Sensor/MLX90632Sensor.h" -MLX90632Sensor mlx90632Sensor; -#else -NullSensor mlx90632Sensor; #endif #if __has_include() #include "Sensor/DFRobotLarkSensor.h" -DFRobotLarkSensor dfRobotLarkSensor; -#else -NullSensor dfRobotLarkSensor; #endif #if __has_include() #include "Sensor/DFRobotGravitySensor.h" -DFRobotGravitySensor dfRobotGravitySensor; -#else -NullSensor dfRobotGravitySensor; #endif #if __has_include() #include "Sensor/NAU7802Sensor.h" -NAU7802Sensor nau7802Sensor; -#else -NullSensor nau7802Sensor; #endif #if __has_include() #include "Sensor/BMP3XXSensor.h" -BMP3XXSensor bmp3xxSensor; -#else -NullSensor bmp3xxSensor; #endif #if __has_include() #include "Sensor/PCT2075Sensor.h" -PCT2075Sensor pct2075Sensor; -#else -NullSensor pct2075Sensor; #endif -RCWL9620Sensor rcwl9620Sensor; -CGRadSensSensor cgRadSens; - #endif #ifdef T1000X_SENSOR_EN #include "Sensor/T1000xSensor.h" -T1000xSensor t1000xSensor; #endif + #ifdef SENSECAP_INDICATOR #include "Sensor/IndicatorSensor.h" -IndicatorSensor indicatorSensor; #endif #if __has_include() #include "Sensor/TSL2561Sensor.h" -TSL2561Sensor tsl2561Sensor; -#else -NullSensor tsl2561Sensor; #endif #define FAILED_STATE_SENSOR_READ_MULTIPLIER 10 @@ -212,6 +140,132 @@ NullSensor tsl2561Sensor; #include "graphics/ScreenFonts.h" #include +#include + +static std::forward_list sensors; + +template void addSensor(ScanI2C *i2cScanner, ScanI2C::DeviceType type) +{ + ScanI2C::FoundDevice dev = i2cScanner->find(type); + if (dev.type != ScanI2C::DeviceType::NONE || type == ScanI2C::DeviceType::NONE) { + TelemetrySensor *sensor = new T(); +#if WIRE_INTERFACES_COUNT > 1 + TwoWire *bus = ScanI2CTwoWire::fetchI2CBus(dev.address); + if (dev.address.port != ScanI2C::I2CPort::WIRE1 && sensor->onlyWire1()) { + // This sensor only works on Wire (Wire1 is not supported) + delete sensor; + return; + } +#else + TwoWire *bus = &Wire; +#endif + if (sensor->initDevice(bus, &dev)) { + sensors.push_front(sensor); + return; + } + // destroy sensor + delete sensor; + } +} + +void EnvironmentTelemetryModule::i2cScanFinished(ScanI2C *i2cScanner) +{ + if (!moduleConfig.telemetry.environment_measurement_enabled && !ENVIRONMENTAL_TELEMETRY_MODULE_ENABLE) { + return; + } + LOG_INFO("Environment Telemetry adding I2C devices..."); + + // order by priority of metrics/values (low top, high bottom) + +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR +#ifdef T1000X_SENSOR_EN + // Not a real I2C device + addSensor(i2cScanner, ScanI2C::DeviceType::NONE); +#else +#ifdef SENSECAP_INDICATOR + // Not a real I2C device, uses UART + addSensor(i2cScanner, ScanI2C::DeviceType::NONE); +#endif + addSensor(i2cScanner, ScanI2C::DeviceType::RCWL9620); + addSensor(i2cScanner, ScanI2C::DeviceType::CGRADSENS); +#endif +#endif + +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR_EXTERNAL +#if __has_include() + addSensor(i2cScanner, ScanI2C::DeviceType::DFROBOT_LARK); +#endif +#if __has_include() + addSensor(i2cScanner, ScanI2C::DeviceType::DFROBOT_RAIN); +#endif +#if __has_include() + addSensor(i2cScanner, ScanI2C::DeviceType::AHT10); +#endif +#if __has_include() + addSensor(i2cScanner, ScanI2C::DeviceType::BMP_085); +#endif +#if __has_include() + addSensor(i2cScanner, ScanI2C::DeviceType::BME_280); +#endif +#if __has_include() + addSensor(i2cScanner, ScanI2C::DeviceType::LTR390UV); +#endif +#if __has_include() + addSensor(i2cScanner, ScanI2C::DeviceType::BME_680); +#endif +#if __has_include() + addSensor(i2cScanner, ScanI2C::DeviceType::BMP_280); +#endif +#if __has_include() + addSensor(i2cScanner, ScanI2C::DeviceType::DPS310); +#endif +#if __has_include() + addSensor(i2cScanner, ScanI2C::DeviceType::MCP9808); +#endif +#if __has_include() + addSensor(i2cScanner, ScanI2C::DeviceType::SHT31); +#endif +#if __has_include() + addSensor(i2cScanner, ScanI2C::DeviceType::LPS22HB); +#endif +#if __has_include() + addSensor(i2cScanner, ScanI2C::DeviceType::SHTC3); +#endif +#if __has_include("RAK12035_SoilMoisture.h") && defined(RAK_4631) && RAK_4631 == 1 + addSensor(i2cScanner, ScanI2C::DeviceType::RAK12035); +#endif +#if __has_include() + addSensor(i2cScanner, ScanI2C::DeviceType::VEML7700); +#endif +#if __has_include() + addSensor(i2cScanner, ScanI2C::DeviceType::TSL2591); +#endif +#if __has_include() + addSensor(i2cScanner, ScanI2C::DeviceType::OPT3001); +#endif +#if __has_include() + addSensor(i2cScanner, ScanI2C::DeviceType::SHT4X); +#endif +#if __has_include() + addSensor(i2cScanner, ScanI2C::DeviceType::MLX90632); +#endif + +#if __has_include() + addSensor(i2cScanner, ScanI2C::DeviceType::BMP_3XX); +#endif +#if __has_include() + addSensor(i2cScanner, ScanI2C::DeviceType::PCT2075); +#endif +#if __has_include() + addSensor(i2cScanner, ScanI2C::DeviceType::TSL2561); +#endif +#if __has_include() + addSensor(i2cScanner, ScanI2C::DeviceType::NAU7802); +#endif + +#endif +} + int32_t EnvironmentTelemetryModule::runOnce() { if (sleepOnNextExecution == true) { @@ -244,81 +298,27 @@ int32_t EnvironmentTelemetryModule::runOnce() if (moduleConfig.telemetry.environment_measurement_enabled || ENVIRONMENTAL_TELEMETRY_MODULE_ENABLE) { LOG_INFO("Environment Telemetry: init"); -#ifdef SENSECAP_INDICATOR - result = indicatorSensor.runOnce(); -#endif + + // check if we have at least one sensor + if (!sensors.empty()) { + result = DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; + } + #ifdef T1000X_SENSOR_EN - result = t1000xSensor.runOnce(); #elif !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR_EXTERNAL - if (dfRobotLarkSensor.hasSensor()) - result = dfRobotLarkSensor.runOnce(); - if (dfRobotGravitySensor.hasSensor()) - result = dfRobotGravitySensor.runOnce(); - if (bmp085Sensor.hasSensor()) - result = bmp085Sensor.runOnce(); -#if __has_include() - if (bmp280Sensor.hasSensor()) - result = bmp280Sensor.runOnce(); -#endif - if (bme280Sensor.hasSensor()) - result = bme280Sensor.runOnce(); - if (ltr390uvSensor.hasSensor()) - result = ltr390uvSensor.runOnce(); - if (bmp3xxSensor.hasSensor()) - result = bmp3xxSensor.runOnce(); - if (bme680Sensor.hasSensor()) - result = bme680Sensor.runOnce(); - if (dps310Sensor.hasSensor()) - result = dps310Sensor.runOnce(); - if (mcp9808Sensor.hasSensor()) - result = mcp9808Sensor.runOnce(); - if (shtc3Sensor.hasSensor()) - result = shtc3Sensor.runOnce(); - if (lps22hbSensor.hasSensor()) - result = lps22hbSensor.runOnce(); - if (sht31Sensor.hasSensor()) - result = sht31Sensor.runOnce(); - if (sht4xSensor.hasSensor()) - result = sht4xSensor.runOnce(); if (ina219Sensor.hasSensor()) result = ina219Sensor.runOnce(); if (ina260Sensor.hasSensor()) result = ina260Sensor.runOnce(); if (ina3221Sensor.hasSensor()) result = ina3221Sensor.runOnce(); - if (veml7700Sensor.hasSensor()) - result = veml7700Sensor.runOnce(); - if (tsl2591Sensor.hasSensor()) - result = tsl2591Sensor.runOnce(); - if (opt3001Sensor.hasSensor()) - result = opt3001Sensor.runOnce(); - if (rcwl9620Sensor.hasSensor()) - result = rcwl9620Sensor.runOnce(); - if (aht10Sensor.hasSensor()) - result = aht10Sensor.runOnce(); - if (mlx90632Sensor.hasSensor()) - result = mlx90632Sensor.runOnce(); - if (nau7802Sensor.hasSensor()) - result = nau7802Sensor.runOnce(); if (max17048Sensor.hasSensor()) result = max17048Sensor.runOnce(); - if (cgRadSens.hasSensor()) - result = cgRadSens.runOnce(); - if (tsl2561Sensor.hasSensor()) - result = tsl2561Sensor.runOnce(); - if (pct2075Sensor.hasSensor()) - result = pct2075Sensor.runOnce(); // this only works on the wismesh hub with the solar option. This is not an I2C sensor, so we don't need the // sensormap here. #ifdef HAS_RAKPROT - result = rak9154Sensor.runOnce(); #endif -#if __has_include("RAK12035_SoilMoisture.h") && defined(RAK_4631) && RAK_4631 == 1 - if (rak12035Sensor.hasSensor()) { - result = rak12035Sensor.runOnce(); - } -#endif #endif } // it's possible to have this module enabled, only for displaying values on the screen. @@ -328,11 +328,13 @@ int32_t EnvironmentTelemetryModule::runOnce() // if we somehow got to a second run of this module with measurement disabled, then just wait forever if (!moduleConfig.telemetry.environment_measurement_enabled && !ENVIRONMENTAL_TELEMETRY_MODULE_ENABLE) { return disable(); - } else { -#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR_EXTERNAL - if (bme680Sensor.hasSensor()) - result = bme680Sensor.runTrigger(); -#endif + } + + for (TelemetrySensor *sensor : sensors) { + uint32_t delay = sensor->runOnce(); + if (delay < result) { + result = delay; + } } if (((lastSentToMesh == 0) || @@ -359,6 +361,7 @@ bool EnvironmentTelemetryModule::wantUIFrame() return moduleConfig.telemetry.environment_screen_enabled; } +#if HAS_SCREEN void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { // === Setup display === @@ -508,6 +511,7 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt currentY += rowHeight; } } +#endif bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *t) { @@ -550,72 +554,12 @@ bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m m->which_variant = meshtastic_Telemetry_environment_metrics_tag; m->variant.environment_metrics = meshtastic_EnvironmentMetrics_init_zero; -#ifdef SENSECAP_INDICATOR - valid = valid && indicatorSensor.getMetrics(m); - hasSensor = true; -#endif -#ifdef T1000X_SENSOR_EN // add by WayenWeng - valid = valid && t1000xSensor.getMetrics(m); - hasSensor = true; -#else - if (dfRobotLarkSensor.hasSensor()) { - valid = valid && dfRobotLarkSensor.getMetrics(m); - hasSensor = true; - } - if (dfRobotGravitySensor.hasSensor()) { - valid = valid && dfRobotGravitySensor.getMetrics(m); - hasSensor = true; - } - if (sht31Sensor.hasSensor()) { - valid = valid && sht31Sensor.getMetrics(m); - hasSensor = true; - } - if (sht4xSensor.hasSensor()) { - valid = valid && sht4xSensor.getMetrics(m); - hasSensor = true; - } - if (lps22hbSensor.hasSensor()) { - valid = valid && lps22hbSensor.getMetrics(m); - hasSensor = true; - } - if (shtc3Sensor.hasSensor()) { - valid = valid && shtc3Sensor.getMetrics(m); - hasSensor = true; - } - if (bmp085Sensor.hasSensor()) { - valid = valid && bmp085Sensor.getMetrics(m); - hasSensor = true; - } -#if __has_include() - if (bmp280Sensor.hasSensor()) { - valid = valid && bmp280Sensor.getMetrics(m); - hasSensor = true; - } -#endif - if (bme280Sensor.hasSensor()) { - valid = valid && bme280Sensor.getMetrics(m); - hasSensor = true; - } - if (ltr390uvSensor.hasSensor()) { - valid = valid && ltr390uvSensor.getMetrics(m); - hasSensor = true; - } - if (bmp3xxSensor.hasSensor()) { - valid = valid && bmp3xxSensor.getMetrics(m); - hasSensor = true; - } - if (bme680Sensor.hasSensor()) { - valid = valid && bme680Sensor.getMetrics(m); - hasSensor = true; - } - if (dps310Sensor.hasSensor()) { - valid = valid && dps310Sensor.getMetrics(m); - hasSensor = true; - } - if (mcp9808Sensor.hasSensor()) { - valid = valid && mcp9808Sensor.getMetrics(m); + for (TelemetrySensor *sensor : sensors) { + valid = valid && sensor->getMetrics(m); hasSensor = true; } + +#ifndef T1000X_SENSOR_EN if (ina219Sensor.hasSensor()) { valid = valid && ina219Sensor.getMetrics(m); hasSensor = true; @@ -628,78 +572,14 @@ bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m valid = valid && ina3221Sensor.getMetrics(m); hasSensor = true; } - if (veml7700Sensor.hasSensor()) { - valid = valid && veml7700Sensor.getMetrics(m); - hasSensor = true; - } - if (tsl2591Sensor.hasSensor()) { - valid = valid && tsl2591Sensor.getMetrics(m); - hasSensor = true; - } - if (opt3001Sensor.hasSensor()) { - valid = valid && opt3001Sensor.getMetrics(m); - hasSensor = true; - } - if (mlx90632Sensor.hasSensor()) { - valid = valid && mlx90632Sensor.getMetrics(m); - hasSensor = true; - } - if (rcwl9620Sensor.hasSensor()) { - valid = valid && rcwl9620Sensor.getMetrics(m); - hasSensor = true; - } - if (nau7802Sensor.hasSensor()) { - valid = valid && nau7802Sensor.getMetrics(m); - hasSensor = true; - } - if (tsl2561Sensor.hasSensor()) { - valid = valid && tsl2561Sensor.getMetrics(m); - hasSensor = true; - } - if (aht10Sensor.hasSensor()) { - if (!bmp280Sensor.hasSensor() && !bmp3xxSensor.hasSensor()) { - valid = valid && aht10Sensor.getMetrics(m); - hasSensor = true; - } else if (bmp280Sensor.hasSensor()) { - // prefer bmp280 temp if both sensors are present, fetch only humidity - meshtastic_Telemetry m_ahtx = meshtastic_Telemetry_init_zero; - LOG_INFO("AHTX0+BMP280 module detected: using temp from BMP280 and humy from AHTX0"); - aht10Sensor.getMetrics(&m_ahtx); - m->variant.environment_metrics.relative_humidity = m_ahtx.variant.environment_metrics.relative_humidity; - m->variant.environment_metrics.has_relative_humidity = m_ahtx.variant.environment_metrics.has_relative_humidity; - } else { - // prefer bmp3xx temp if both sensors are present, fetch only humidity - meshtastic_Telemetry m_ahtx = meshtastic_Telemetry_init_zero; - LOG_INFO("AHTX0+BMP3XX module detected: using temp from BMP3XX and humy from AHTX0"); - aht10Sensor.getMetrics(&m_ahtx); - m->variant.environment_metrics.relative_humidity = m_ahtx.variant.environment_metrics.relative_humidity; - m->variant.environment_metrics.has_relative_humidity = m_ahtx.variant.environment_metrics.has_relative_humidity; - } - } if (max17048Sensor.hasSensor()) { valid = valid && max17048Sensor.getMetrics(m); hasSensor = true; } - if (cgRadSens.hasSensor()) { - valid = valid && cgRadSens.getMetrics(m); - hasSensor = true; - } - if (pct2075Sensor.hasSensor()) { - valid = valid && pct2075Sensor.getMetrics(m); - hasSensor = true; - } +#endif #ifdef HAS_RAKPROT valid = valid && rak9154Sensor.getMetrics(m); hasSensor = true; -#endif -#if __has_include("RAK12035_SoilMoisture.h") && defined(RAK_4631) && \ - RAK_4631 == \ - 1 // Not really needed, but may as well just skip at a lower level it if no library or not a RAK_4631 - if (rak12035Sensor.hasSensor()) { - valid = valid && rak12035Sensor.getMetrics(m); - hasSensor = true; - } -#endif #endif return valid && hasSensor; } @@ -737,11 +617,8 @@ bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) meshtastic_Telemetry m = meshtastic_Telemetry_init_zero; m.which_variant = meshtastic_Telemetry_environment_metrics_tag; m.time = getTime(); -#ifdef T1000X_SENSOR_EN - if (t1000xSensor.getMetrics(&m)) { -#else + if (getEnvironmentTelemetry(&m)) { -#endif LOG_INFO("Send: barometric_pressure=%f, current=%f, gas_resistance=%f, relative_humidity=%f, temperature=%f", m.variant.environment_metrics.barometric_pressure, m.variant.environment_metrics.current, m.variant.environment_metrics.gas_resistance, m.variant.environment_metrics.relative_humidity, @@ -803,71 +680,13 @@ AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule { AdminMessageHandleResult result = AdminMessageHandleResult::NOT_HANDLED; #if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR_EXTERNAL - if (dfRobotLarkSensor.hasSensor()) { - result = dfRobotLarkSensor.handleAdminMessage(mp, request, response); - if (result != AdminMessageHandleResult::NOT_HANDLED) - return result; - } - if (dfRobotGravitySensor.hasSensor()) { - result = dfRobotGravitySensor.handleAdminMessage(mp, request, response); - if (result != AdminMessageHandleResult::NOT_HANDLED) - return result; - } - if (sht31Sensor.hasSensor()) { - result = sht31Sensor.handleAdminMessage(mp, request, response); - if (result != AdminMessageHandleResult::NOT_HANDLED) - return result; - } - if (lps22hbSensor.hasSensor()) { - result = lps22hbSensor.handleAdminMessage(mp, request, response); - if (result != AdminMessageHandleResult::NOT_HANDLED) - return result; - } - if (shtc3Sensor.hasSensor()) { - result = shtc3Sensor.handleAdminMessage(mp, request, response); - if (result != AdminMessageHandleResult::NOT_HANDLED) - return result; - } - if (bmp085Sensor.hasSensor()) { - result = bmp085Sensor.handleAdminMessage(mp, request, response); - if (result != AdminMessageHandleResult::NOT_HANDLED) - return result; - } - if (bmp280Sensor.hasSensor()) { - result = bmp280Sensor.handleAdminMessage(mp, request, response); - if (result != AdminMessageHandleResult::NOT_HANDLED) - return result; - } - if (bme280Sensor.hasSensor()) { - result = bme280Sensor.handleAdminMessage(mp, request, response); - if (result != AdminMessageHandleResult::NOT_HANDLED) - return result; - } - if (ltr390uvSensor.hasSensor()) { - result = ltr390uvSensor.handleAdminMessage(mp, request, response); - if (result != AdminMessageHandleResult::NOT_HANDLED) - return result; - } - if (bmp3xxSensor.hasSensor()) { - result = bmp3xxSensor.handleAdminMessage(mp, request, response); - if (result != AdminMessageHandleResult::NOT_HANDLED) - return result; - } - if (bme680Sensor.hasSensor()) { - result = bme680Sensor.handleAdminMessage(mp, request, response); - if (result != AdminMessageHandleResult::NOT_HANDLED) - return result; - } - if (dps310Sensor.hasSensor()) { - result = dps310Sensor.handleAdminMessage(mp, request, response); - if (result != AdminMessageHandleResult::NOT_HANDLED) - return result; - } - if (mcp9808Sensor.hasSensor()) { - result = mcp9808Sensor.handleAdminMessage(mp, request, response); + + for (TelemetrySensor *sensor : sensors) { + result = sensor->handleAdminMessage(mp, request, response); if (result != AdminMessageHandleResult::NOT_HANDLED) return result; } + if (ina219Sensor.hasSensor()) { result = ina219Sensor.handleAdminMessage(mp, request, response); if (result != AdminMessageHandleResult::NOT_HANDLED) @@ -883,60 +702,11 @@ AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule if (result != AdminMessageHandleResult::NOT_HANDLED) return result; } - if (veml7700Sensor.hasSensor()) { - result = veml7700Sensor.handleAdminMessage(mp, request, response); - if (result != AdminMessageHandleResult::NOT_HANDLED) - return result; - } - if (tsl2591Sensor.hasSensor()) { - result = tsl2591Sensor.handleAdminMessage(mp, request, response); - if (result != AdminMessageHandleResult::NOT_HANDLED) - return result; - } - if (opt3001Sensor.hasSensor()) { - result = opt3001Sensor.handleAdminMessage(mp, request, response); - if (result != AdminMessageHandleResult::NOT_HANDLED) - return result; - } - if (mlx90632Sensor.hasSensor()) { - result = mlx90632Sensor.handleAdminMessage(mp, request, response); - if (result != AdminMessageHandleResult::NOT_HANDLED) - return result; - } - if (rcwl9620Sensor.hasSensor()) { - result = rcwl9620Sensor.handleAdminMessage(mp, request, response); - if (result != AdminMessageHandleResult::NOT_HANDLED) - return result; - } - if (nau7802Sensor.hasSensor()) { - result = nau7802Sensor.handleAdminMessage(mp, request, response); - if (result != AdminMessageHandleResult::NOT_HANDLED) - return result; - } - if (aht10Sensor.hasSensor()) { - result = aht10Sensor.handleAdminMessage(mp, request, response); - if (result != AdminMessageHandleResult::NOT_HANDLED) - return result; - } if (max17048Sensor.hasSensor()) { result = max17048Sensor.handleAdminMessage(mp, request, response); if (result != AdminMessageHandleResult::NOT_HANDLED) return result; } - if (cgRadSens.hasSensor()) { - result = cgRadSens.handleAdminMessage(mp, request, response); - if (result != AdminMessageHandleResult::NOT_HANDLED) - return result; - } -#if __has_include("RAK12035_SoilMoisture.h") && defined(RAK_4631) && \ - RAK_4631 == \ - 1 // Not really needed, but may as well just skip it at a lower level if no library or not a RAK_4631 - if (rak12035Sensor.hasSensor()) { - result = rak12035Sensor.handleAdminMessage(mp, request, response); - if (result != AdminMessageHandleResult::NOT_HANDLED) - return result; - } -#endif #endif return result; } diff --git a/src/modules/Telemetry/EnvironmentTelemetry.h b/src/modules/Telemetry/EnvironmentTelemetry.h index d70c063fc..6e4ce82e7 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.h +++ b/src/modules/Telemetry/EnvironmentTelemetry.h @@ -11,10 +11,13 @@ #include "../mesh/generated/meshtastic/telemetry.pb.h" #include "NodeDB.h" #include "ProtobufModule.h" +#include "detect/ScanI2CConsumer.h" #include #include -class EnvironmentTelemetryModule : private concurrency::OSThread, public ProtobufModule +class EnvironmentTelemetryModule : private concurrency::OSThread, + public ScanI2CConsumer, + public ProtobufModule { CallbackObserver nodeStatusObserver = CallbackObserver(this, @@ -22,7 +25,7 @@ class EnvironmentTelemetryModule : private concurrency::OSThread, public Protobu public: EnvironmentTelemetryModule() - : concurrency::OSThread("EnvironmentTelemetry"), + : concurrency::OSThread("EnvironmentTelemetry"), ScanI2CConsumer(), ProtobufModule("EnvironmentTelemetry", meshtastic_PortNum_TELEMETRY_APP, &meshtastic_Telemetry_msg) { lastMeasurementPacket = nullptr; @@ -56,6 +59,8 @@ class EnvironmentTelemetryModule : private concurrency::OSThread, public Protobu meshtastic_AdminMessage *request, meshtastic_AdminMessage *response) override; + void i2cScanFinished(ScanI2C *i2cScanner); + private: bool firstTime = 1; meshtastic_MeshPacket *lastMeasurementPacket; diff --git a/src/modules/Telemetry/PowerTelemetry.cpp b/src/modules/Telemetry/PowerTelemetry.cpp index 479861a2e..e69ee3931 100644 --- a/src/modules/Telemetry/PowerTelemetry.cpp +++ b/src/modules/Telemetry/PowerTelemetry.cpp @@ -108,6 +108,7 @@ bool PowerTelemetryModule::wantUIFrame() return moduleConfig.telemetry.power_screen_enabled; } +#if HAS_SCREEN void PowerTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { display->clear(); @@ -165,6 +166,7 @@ void PowerTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *s drawLine("Ch3", m.ch3_voltage, m.ch3_current); } } +#endif bool PowerTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *t) { diff --git a/src/modules/Telemetry/Sensor/AHT10.cpp b/src/modules/Telemetry/Sensor/AHT10.cpp index 35934533b..52fdc05c0 100644 --- a/src/modules/Telemetry/Sensor/AHT10.cpp +++ b/src/modules/Telemetry/Sensor/AHT10.cpp @@ -15,20 +15,16 @@ AHT10Sensor::AHT10Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_AHT10, "AHT10") {} -int32_t AHT10Sensor::runOnce() +bool AHT10Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { LOG_INFO("Init sensor: %s", sensorName); - if (!hasSensor()) { - return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; - } aht10 = Adafruit_AHTX0(); - status = aht10.begin(nodeTelemetrySensorsMap[sensorType].second, 0, nodeTelemetrySensorsMap[sensorType].first); + status = aht10.begin(bus, 0, dev->address.address); - return initI2CSensor(); + initI2CSensor(); + return status; } -void AHT10Sensor::setup() {} - bool AHT10Sensor::getMetrics(meshtastic_Telemetry *measurement) { LOG_DEBUG("AHT10 getMetrics"); @@ -36,11 +32,16 @@ bool AHT10Sensor::getMetrics(meshtastic_Telemetry *measurement) sensors_event_t humidity, temp; aht10.getEvent(&humidity, &temp); - measurement->variant.environment_metrics.has_temperature = true; - measurement->variant.environment_metrics.has_relative_humidity = true; + // prefer other sensors like bmp280, bmp3xx + if (!measurement->variant.environment_metrics.has_temperature) { + measurement->variant.environment_metrics.has_temperature = true; + measurement->variant.environment_metrics.temperature = temp.temperature; + } - measurement->variant.environment_metrics.temperature = temp.temperature; - measurement->variant.environment_metrics.relative_humidity = humidity.relative_humidity; + if (!measurement->variant.environment_metrics.has_relative_humidity) { + measurement->variant.environment_metrics.has_relative_humidity = true; + measurement->variant.environment_metrics.relative_humidity = humidity.relative_humidity; + } return true; } diff --git a/src/modules/Telemetry/Sensor/AHT10.h b/src/modules/Telemetry/Sensor/AHT10.h index a6fa19952..ab3f5806c 100644 --- a/src/modules/Telemetry/Sensor/AHT10.h +++ b/src/modules/Telemetry/Sensor/AHT10.h @@ -15,13 +15,10 @@ class AHT10Sensor : public TelemetrySensor private: Adafruit_AHTX0 aht10; - protected: - virtual void setup() override; - public: AHT10Sensor(); - virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; }; #endif diff --git a/src/modules/Telemetry/Sensor/BME280Sensor.cpp b/src/modules/Telemetry/Sensor/BME280Sensor.cpp index d7b0a8a38..779b2e603 100644 --- a/src/modules/Telemetry/Sensor/BME280Sensor.cpp +++ b/src/modules/Telemetry/Sensor/BME280Sensor.cpp @@ -10,13 +10,13 @@ BME280Sensor::BME280Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BME280, "BME280") {} -int32_t BME280Sensor::runOnce() +bool BME280Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { LOG_INFO("Init sensor: %s", sensorName); - if (!hasSensor()) { - return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; + status = bme280.begin(dev->address.address, bus); + if (!status) { + return status; } - status = bme280.begin(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second); bme280.setSampling(Adafruit_BME280::MODE_FORCED, Adafruit_BME280::SAMPLING_X1, // Temp. oversampling @@ -24,11 +24,10 @@ int32_t BME280Sensor::runOnce() Adafruit_BME280::SAMPLING_X1, // Humidity oversampling Adafruit_BME280::FILTER_OFF, Adafruit_BME280::STANDBY_MS_1000); - return initI2CSensor(); + initI2CSensor(); + return status; } -void BME280Sensor::setup() {} - bool BME280Sensor::getMetrics(meshtastic_Telemetry *measurement) { measurement->variant.environment_metrics.has_temperature = true; diff --git a/src/modules/Telemetry/Sensor/BME280Sensor.h b/src/modules/Telemetry/Sensor/BME280Sensor.h index d1e21c8d5..fadae46cd 100644 --- a/src/modules/Telemetry/Sensor/BME280Sensor.h +++ b/src/modules/Telemetry/Sensor/BME280Sensor.h @@ -11,13 +11,10 @@ class BME280Sensor : public TelemetrySensor private: Adafruit_BME280 bme280; - protected: - virtual void setup() override; - public: BME280Sensor(); - virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; }; #endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/BME680Sensor.cpp b/src/modules/Telemetry/Sensor/BME680Sensor.cpp index fce029deb..95f3dc5f0 100644 --- a/src/modules/Telemetry/Sensor/BME680Sensor.cpp +++ b/src/modules/Telemetry/Sensor/BME680Sensor.cpp @@ -10,7 +10,7 @@ BME680Sensor::BME680Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BME680, "BME680") {} -int32_t BME680Sensor::runTrigger() +int32_t BME680Sensor::runOnce() { if (!bme680.run()) { checkStatus("runTrigger"); @@ -18,13 +18,10 @@ int32_t BME680Sensor::runTrigger() return 35; } -int32_t BME680Sensor::runOnce() +bool BME680Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { - - if (!hasSensor()) { - return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; - } - if (!bme680.begin(nodeTelemetrySensorsMap[sensorType].first, *nodeTelemetrySensorsMap[sensorType].second)) + status = 0; + if (!bme680.begin(dev->address.address, *bus)) checkStatus("begin"); if (bme680.status == BSEC_OK) { @@ -40,17 +37,15 @@ int32_t BME680Sensor::runOnce() } LOG_INFO("Init sensor: %s with the BSEC Library version %d.%d.%d.%d ", sensorName, bme680.version.major, bme680.version.minor, bme680.version.major_bugfix, bme680.version.minor_bugfix); - } else { - status = 0; } + if (status == 0) LOG_DEBUG("BME680Sensor::runOnce: bme680.status %d", bme680.status); - return initI2CSensor(); + initI2CSensor(); + return status; } -void BME680Sensor::setup() {} - bool BME680Sensor::getMetrics(meshtastic_Telemetry *measurement) { if (bme680.getData(BSEC_OUTPUT_RAW_PRESSURE).signal == 0) diff --git a/src/modules/Telemetry/Sensor/BME680Sensor.h b/src/modules/Telemetry/Sensor/BME680Sensor.h index ce1fa4f3b..f4ead95f7 100644 --- a/src/modules/Telemetry/Sensor/BME680Sensor.h +++ b/src/modules/Telemetry/Sensor/BME680Sensor.h @@ -18,7 +18,6 @@ class BME680Sensor : public TelemetrySensor Bsec2 bme680; protected: - virtual void setup() override; const char *bsecConfigFileName = "/prefs/bsec.dat"; uint8_t bsecState[BSEC_MAX_STATE_BLOB_SIZE] = {0}; uint8_t accuracy = 0; @@ -38,9 +37,9 @@ class BME680Sensor : public TelemetrySensor public: BME680Sensor(); - int32_t runTrigger(); virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; }; #endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/BMP085Sensor.cpp b/src/modules/Telemetry/Sensor/BMP085Sensor.cpp index 8087eb4b9..1fb2ecc28 100644 --- a/src/modules/Telemetry/Sensor/BMP085Sensor.cpp +++ b/src/modules/Telemetry/Sensor/BMP085Sensor.cpp @@ -10,20 +10,17 @@ BMP085Sensor::BMP085Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BMP085, "BMP085") {} -int32_t BMP085Sensor::runOnce() +bool BMP085Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { LOG_INFO("Init sensor: %s", sensorName); - if (!hasSensor()) { - return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; - } + bmp085 = Adafruit_BMP085(); - status = bmp085.begin(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second); + status = bmp085.begin(dev->address.address, bus); - return initI2CSensor(); + initI2CSensor(); + return status; } -void BMP085Sensor::setup() {} - bool BMP085Sensor::getMetrics(meshtastic_Telemetry *measurement) { measurement->variant.environment_metrics.has_temperature = true; diff --git a/src/modules/Telemetry/Sensor/BMP085Sensor.h b/src/modules/Telemetry/Sensor/BMP085Sensor.h index 8dadceab4..12ccf35a1 100644 --- a/src/modules/Telemetry/Sensor/BMP085Sensor.h +++ b/src/modules/Telemetry/Sensor/BMP085Sensor.h @@ -11,13 +11,10 @@ class BMP085Sensor : public TelemetrySensor private: Adafruit_BMP085 bmp085; - protected: - virtual void setup() override; - public: BMP085Sensor(); - virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; }; #endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/BMP280Sensor.cpp b/src/modules/Telemetry/Sensor/BMP280Sensor.cpp index 47069b8e0..2b7407c43 100644 --- a/src/modules/Telemetry/Sensor/BMP280Sensor.cpp +++ b/src/modules/Telemetry/Sensor/BMP280Sensor.cpp @@ -10,25 +10,25 @@ BMP280Sensor::BMP280Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BMP280, "BMP280") {} -int32_t BMP280Sensor::runOnce() +bool BMP280Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { LOG_INFO("Init sensor: %s", sensorName); - if (!hasSensor()) { - return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; + + bmp280 = Adafruit_BMP280(bus); + status = bmp280.begin(dev->address.address); + if (!status) { + return status; } - bmp280 = Adafruit_BMP280(nodeTelemetrySensorsMap[sensorType].second); - status = bmp280.begin(nodeTelemetrySensorsMap[sensorType].first); bmp280.setSampling(Adafruit_BMP280::MODE_FORCED, Adafruit_BMP280::SAMPLING_X1, // Temp. oversampling Adafruit_BMP280::SAMPLING_X1, // Pressure oversampling Adafruit_BMP280::FILTER_OFF, Adafruit_BMP280::STANDBY_MS_1000); - return initI2CSensor(); + initI2CSensor(); + return status; } -void BMP280Sensor::setup() {} - bool BMP280Sensor::getMetrics(meshtastic_Telemetry *measurement) { measurement->variant.environment_metrics.has_temperature = true; diff --git a/src/modules/Telemetry/Sensor/BMP280Sensor.h b/src/modules/Telemetry/Sensor/BMP280Sensor.h index d615411b2..2199fc0cd 100644 --- a/src/modules/Telemetry/Sensor/BMP280Sensor.h +++ b/src/modules/Telemetry/Sensor/BMP280Sensor.h @@ -11,13 +11,10 @@ class BMP280Sensor : public TelemetrySensor private: Adafruit_BMP280 bmp280; - protected: - virtual void setup() override; - public: BMP280Sensor(); - virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; }; #endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/BMP3XXSensor.cpp b/src/modules/Telemetry/Sensor/BMP3XXSensor.cpp index 28a71b48f..ac80732bf 100644 --- a/src/modules/Telemetry/Sensor/BMP3XXSensor.cpp +++ b/src/modules/Telemetry/Sensor/BMP3XXSensor.cpp @@ -6,20 +6,18 @@ BMP3XXSensor::BMP3XXSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BMP3XX, "BMP3XX") {} -void BMP3XXSensor::setup() {} - -int32_t BMP3XXSensor::runOnce() +bool BMP3XXSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { LOG_INFO("Init sensor: %s", sensorName); - if (!hasSensor()) { - return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; - } // Get a singleton instance and initialise the bmp3xx if (bmp3xx == nullptr) { bmp3xx = BMP3XXSingleton::GetInstance(); } - status = bmp3xx->begin_I2C(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second); + status = bmp3xx->begin_I2C(dev->address.address, bus); + if (!status) { + return status; + } // set up oversampling and filter initialization bmp3xx->setTemperatureOversampling(BMP3_OVERSAMPLING_4X); @@ -31,7 +29,8 @@ int32_t BMP3XXSensor::runOnce() for (int i = 0; i < 3; i++) { bmp3xx->performReading(); } - return initI2CSensor(); + initI2CSensor(); + return status; } bool BMP3XXSensor::getMetrics(meshtastic_Telemetry *measurement) diff --git a/src/modules/Telemetry/Sensor/BMP3XXSensor.h b/src/modules/Telemetry/Sensor/BMP3XXSensor.h index 6ab0f533d..7ce14d9db 100644 --- a/src/modules/Telemetry/Sensor/BMP3XXSensor.h +++ b/src/modules/Telemetry/Sensor/BMP3XXSensor.h @@ -43,12 +43,11 @@ class BMP3XXSensor : public TelemetrySensor { protected: BMP3XXSingleton *bmp3xx = nullptr; - virtual void setup() override; public: BMP3XXSensor(); - virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; }; #endif diff --git a/src/modules/Telemetry/Sensor/CGRadSensSensor.cpp b/src/modules/Telemetry/Sensor/CGRadSensSensor.cpp index ac5df1b81..e7b191398 100644 --- a/src/modules/Telemetry/Sensor/CGRadSensSensor.cpp +++ b/src/modules/Telemetry/Sensor/CGRadSensSensor.cpp @@ -14,22 +14,16 @@ CGRadSensSensor::CGRadSensSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_RADSENS, "RadSens") {} -int32_t CGRadSensSensor::runOnce() +bool CGRadSensSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { // Initialize the sensor following the same pattern as RCWL9620Sensor LOG_INFO("Init sensor: %s", sensorName); - if (!hasSensor()) { - return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; - } - status = true; - begin(nodeTelemetrySensorsMap[sensorType].second, nodeTelemetrySensorsMap[sensorType].first); - - return initI2CSensor(); + begin(bus, dev->address.address); + initI2CSensor(); + return status; } -void CGRadSensSensor::setup() {} - void CGRadSensSensor::begin(TwoWire *wire, uint8_t addr) { // Store the Wire and address to the sensor following the same pattern as RCWL9620Sensor diff --git a/src/modules/Telemetry/Sensor/CGRadSensSensor.h b/src/modules/Telemetry/Sensor/CGRadSensSensor.h index 3b15a19a2..c677e8899 100644 --- a/src/modules/Telemetry/Sensor/CGRadSensSensor.h +++ b/src/modules/Telemetry/Sensor/CGRadSensSensor.h @@ -17,14 +17,13 @@ class CGRadSensSensor : public TelemetrySensor TwoWire *_wire = &Wire; protected: - virtual void setup() override; void begin(TwoWire *wire = &Wire, uint8_t addr = 0x66); float getStaticRadiation(); public: CGRadSensSensor(); - virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; }; #endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/DFRobotGravitySensor.cpp b/src/modules/Telemetry/Sensor/DFRobotGravitySensor.cpp index 9581057b0..59a98e291 100644 --- a/src/modules/Telemetry/Sensor/DFRobotGravitySensor.cpp +++ b/src/modules/Telemetry/Sensor/DFRobotGravitySensor.cpp @@ -10,31 +10,39 @@ DFRobotGravitySensor::DFRobotGravitySensor() : TelemetrySensor(meshtastic_TelemetrySensorType_DFROBOT_RAIN, "DFROBOT_RAIN") {} -int32_t DFRobotGravitySensor::runOnce() +DFRobotGravitySensor::~DFRobotGravitySensor() { - LOG_INFO("Init sensor: %s", sensorName); - if (!hasSensor()) { - return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; + if (gravity) { + delete gravity; + gravity = nullptr; } - - gravity = DFRobot_RainfallSensor_I2C(nodeTelemetrySensorsMap[sensorType].second); - status = gravity.begin(); - - return initI2CSensor(); } -void DFRobotGravitySensor::setup() +bool DFRobotGravitySensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { - LOG_DEBUG("%s VID: %x, PID: %x, Version: %s", sensorName, gravity.vid, gravity.pid, gravity.getFirmwareVersion().c_str()); + LOG_INFO("Init sensor: %s", sensorName); + + gravity = new DFRobot_RainfallSensor_I2C(bus); + status = gravity->begin(); + + LOG_DEBUG("%s VID: %x, PID: %x, Version: %s", sensorName, gravity->vid, gravity->pid, gravity->getFirmwareVersion().c_str()); + + initI2CSensor(); + return status; } bool DFRobotGravitySensor::getMetrics(meshtastic_Telemetry *measurement) { + if (!gravity) { + LOG_ERROR("DFRobotGravitySensor not initialized"); + return false; + } + measurement->variant.environment_metrics.has_rainfall_1h = true; measurement->variant.environment_metrics.has_rainfall_24h = true; - measurement->variant.environment_metrics.rainfall_1h = gravity.getRainfall(1); - measurement->variant.environment_metrics.rainfall_24h = gravity.getRainfall(24); + measurement->variant.environment_metrics.rainfall_1h = gravity->getRainfall(1); + measurement->variant.environment_metrics.rainfall_24h = gravity->getRainfall(24); LOG_INFO("Rain 1h: %f mm", measurement->variant.environment_metrics.rainfall_1h); LOG_INFO("Rain 24h: %f mm", measurement->variant.environment_metrics.rainfall_24h); diff --git a/src/modules/Telemetry/Sensor/DFRobotGravitySensor.h b/src/modules/Telemetry/Sensor/DFRobotGravitySensor.h index dfd81a913..2b4890e30 100644 --- a/src/modules/Telemetry/Sensor/DFRobotGravitySensor.h +++ b/src/modules/Telemetry/Sensor/DFRobotGravitySensor.h @@ -14,15 +14,13 @@ class DFRobotGravitySensor : public TelemetrySensor { private: - DFRobot_RainfallSensor_I2C gravity = DFRobot_RainfallSensor_I2C(nodeTelemetrySensorsMap[sensorType].second); - - protected: - virtual void setup() override; + DFRobot_RainfallSensor_I2C *gravity = nullptr; public: DFRobotGravitySensor(); - virtual int32_t runOnce() override; + ~DFRobotGravitySensor(); virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; }; #endif diff --git a/src/modules/Telemetry/Sensor/DFRobotLarkSensor.cpp b/src/modules/Telemetry/Sensor/DFRobotLarkSensor.cpp index d962f1634..2c2aeed6d 100644 --- a/src/modules/Telemetry/Sensor/DFRobotLarkSensor.cpp +++ b/src/modules/Telemetry/Sensor/DFRobotLarkSensor.cpp @@ -11,14 +11,10 @@ DFRobotLarkSensor::DFRobotLarkSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_DFROBOT_LARK, "DFROBOT_LARK") {} -int32_t DFRobotLarkSensor::runOnce() +bool DFRobotLarkSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { LOG_INFO("Init sensor: %s", sensorName); - if (!hasSensor()) { - return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; - } - - lark = DFRobot_LarkWeatherStation_I2C(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second); + lark = DFRobot_LarkWeatherStation_I2C(dev->address.address, bus); if (lark.begin() == 0) // DFRobotLarkSensor init { @@ -28,11 +24,10 @@ int32_t DFRobotLarkSensor::runOnce() LOG_ERROR("DFRobotLarkSensor Init Failed"); status = false; } - return initI2CSensor(); + initI2CSensor(); + return status; } -void DFRobotLarkSensor::setup() {} - bool DFRobotLarkSensor::getMetrics(meshtastic_Telemetry *measurement) { measurement->variant.environment_metrics.has_temperature = true; diff --git a/src/modules/Telemetry/Sensor/DFRobotLarkSensor.h b/src/modules/Telemetry/Sensor/DFRobotLarkSensor.h index 7b67bc5b6..f3e4661a1 100644 --- a/src/modules/Telemetry/Sensor/DFRobotLarkSensor.h +++ b/src/modules/Telemetry/Sensor/DFRobotLarkSensor.h @@ -16,13 +16,10 @@ class DFRobotLarkSensor : public TelemetrySensor private: DFRobot_LarkWeatherStation_I2C lark = DFRobot_LarkWeatherStation_I2C(); - protected: - virtual void setup() override; - public: DFRobotLarkSensor(); - virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; }; #endif diff --git a/src/modules/Telemetry/Sensor/DPS310Sensor.cpp b/src/modules/Telemetry/Sensor/DPS310Sensor.cpp index cc9b83af8..19e54aa4b 100644 --- a/src/modules/Telemetry/Sensor/DPS310Sensor.cpp +++ b/src/modules/Telemetry/Sensor/DPS310Sensor.cpp @@ -9,23 +9,22 @@ DPS310Sensor::DPS310Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_DPS310, "DPS310") {} -int32_t DPS310Sensor::runOnce() +bool DPS310Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { LOG_INFO("Init sensor: %s", sensorName); - if (!hasSensor()) { - return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; + status = dps310.begin_I2C(dev->address.address, bus); + if (!status) { + return status; } - status = dps310.begin_I2C(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second); dps310.configurePressure(DPS310_1HZ, DPS310_4SAMPLES); dps310.configureTemperature(DPS310_1HZ, DPS310_4SAMPLES); dps310.setMode(DPS310_CONT_PRESTEMP); - return initI2CSensor(); + initI2CSensor(); + return status; } -void DPS310Sensor::setup() {} - bool DPS310Sensor::getMetrics(meshtastic_Telemetry *measurement) { sensors_event_t temp, press; diff --git a/src/modules/Telemetry/Sensor/DPS310Sensor.h b/src/modules/Telemetry/Sensor/DPS310Sensor.h index e9b4ece89..4de8b2d1a 100644 --- a/src/modules/Telemetry/Sensor/DPS310Sensor.h +++ b/src/modules/Telemetry/Sensor/DPS310Sensor.h @@ -11,13 +11,10 @@ class DPS310Sensor : public TelemetrySensor private: Adafruit_DPS310 dps310; - protected: - virtual void setup() override; - public: DPS310Sensor(); - virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; }; #endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/IndicatorSensor.cpp b/src/modules/Telemetry/Sensor/IndicatorSensor.cpp index 317357137..26a4bc5fc 100644 --- a/src/modules/Telemetry/Sensor/IndicatorSensor.cpp +++ b/src/modules/Telemetry/Sensor/IndicatorSensor.cpp @@ -61,11 +61,11 @@ static int cmd_send(uint8_t cmd, const char *p_data, uint8_t len) return -1; } -int32_t IndicatorSensor::runOnce() +bool IndicatorSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { LOG_INFO("%s: init", sensorName); setup(); - return 2 * DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; // give it some time to start up + return true; } void IndicatorSensor::setup() diff --git a/src/modules/Telemetry/Sensor/IndicatorSensor.h b/src/modules/Telemetry/Sensor/IndicatorSensor.h index 48ecef8de..22a0d9c83 100644 --- a/src/modules/Telemetry/Sensor/IndicatorSensor.h +++ b/src/modules/Telemetry/Sensor/IndicatorSensor.h @@ -7,13 +7,13 @@ class IndicatorSensor : public TelemetrySensor { - protected: - virtual void setup() override; - public: IndicatorSensor(); - virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; + + private: + void setup(); }; #endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/LPS22HBSensor.cpp b/src/modules/Telemetry/Sensor/LPS22HBSensor.cpp index cf0fbe4a9..4ed78dcb0 100644 --- a/src/modules/Telemetry/Sensor/LPS22HBSensor.cpp +++ b/src/modules/Telemetry/Sensor/LPS22HBSensor.cpp @@ -10,19 +10,17 @@ LPS22HBSensor::LPS22HBSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_LPS22, "LPS22HB") {} -int32_t LPS22HBSensor::runOnce() +bool LPS22HBSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { LOG_INFO("Init sensor: %s", sensorName); - if (!hasSensor()) { - return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; + status = lps22hb.begin_I2C(dev->address.address, bus); + if (!status) { + return status; } - status = lps22hb.begin_I2C(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second); - return initI2CSensor(); -} - -void LPS22HBSensor::setup() -{ lps22hb.setDataRate(LPS22_RATE_10_HZ); + + initI2CSensor(); + return status; } bool LPS22HBSensor::getMetrics(meshtastic_Telemetry *measurement) diff --git a/src/modules/Telemetry/Sensor/LPS22HBSensor.h b/src/modules/Telemetry/Sensor/LPS22HBSensor.h index 24d75e903..90b006fa2 100644 --- a/src/modules/Telemetry/Sensor/LPS22HBSensor.h +++ b/src/modules/Telemetry/Sensor/LPS22HBSensor.h @@ -12,13 +12,10 @@ class LPS22HBSensor : public TelemetrySensor private: Adafruit_LPS22 lps22hb; - protected: - virtual void setup() override; - public: LPS22HBSensor(); - virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; }; #endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/LTR390UVSensor.cpp b/src/modules/Telemetry/Sensor/LTR390UVSensor.cpp index fb84700c4..cb7290fee 100644 --- a/src/modules/Telemetry/Sensor/LTR390UVSensor.cpp +++ b/src/modules/Telemetry/Sensor/LTR390UVSensor.cpp @@ -9,23 +9,23 @@ LTR390UVSensor::LTR390UVSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_LTR390UV, "LTR390UV") {} -int32_t LTR390UVSensor::runOnce() +bool LTR390UVSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { LOG_INFO("Init sensor: %s", sensorName); - if (!hasSensor()) { - return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; + + status = ltr390uv.begin(bus); + if (!status) { + return status; } - status = ltr390uv.begin(nodeTelemetrySensorsMap[sensorType].second); ltr390uv.setMode(LTR390_MODE_UVS); ltr390uv.setGain(LTR390_GAIN_18); // Datasheet default ltr390uv.setResolution(LTR390_RESOLUTION_20BIT); // Datasheet default - return initI2CSensor(); + initI2CSensor(); + return status; } -void LTR390UVSensor::setup() {} - bool LTR390UVSensor::getMetrics(meshtastic_Telemetry *measurement) { LOG_DEBUG("LTR390UV getMetrics"); diff --git a/src/modules/Telemetry/Sensor/LTR390UVSensor.h b/src/modules/Telemetry/Sensor/LTR390UVSensor.h index 40206bce8..e12d17274 100644 --- a/src/modules/Telemetry/Sensor/LTR390UVSensor.h +++ b/src/modules/Telemetry/Sensor/LTR390UVSensor.h @@ -13,13 +13,10 @@ class LTR390UVSensor : public TelemetrySensor float lastLuxReading = 0; float lastUVReading = 0; - protected: - virtual void setup() override; - public: LTR390UVSensor(); - virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; }; #endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/MCP9808Sensor.cpp b/src/modules/Telemetry/Sensor/MCP9808Sensor.cpp index 906634c40..c93d6a927 100644 --- a/src/modules/Telemetry/Sensor/MCP9808Sensor.cpp +++ b/src/modules/Telemetry/Sensor/MCP9808Sensor.cpp @@ -9,19 +9,17 @@ MCP9808Sensor::MCP9808Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_MCP9808, "MCP9808") {} -int32_t MCP9808Sensor::runOnce() +bool MCP9808Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { LOG_INFO("Init sensor: %s", sensorName); - if (!hasSensor()) { - return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; + status = mcp9808.begin(dev->address.address, bus); + if (!status) { + return status; } - status = mcp9808.begin(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second); - return initI2CSensor(); -} - -void MCP9808Sensor::setup() -{ mcp9808.setResolution(2); + + initI2CSensor(); + return status; } bool MCP9808Sensor::getMetrics(meshtastic_Telemetry *measurement) diff --git a/src/modules/Telemetry/Sensor/MCP9808Sensor.h b/src/modules/Telemetry/Sensor/MCP9808Sensor.h index 705a71700..cef7a48c2 100644 --- a/src/modules/Telemetry/Sensor/MCP9808Sensor.h +++ b/src/modules/Telemetry/Sensor/MCP9808Sensor.h @@ -11,13 +11,10 @@ class MCP9808Sensor : public TelemetrySensor private: Adafruit_MCP9808 mcp9808; - protected: - virtual void setup() override; - public: MCP9808Sensor(); - virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; }; #endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/MLX90632Sensor.cpp b/src/modules/Telemetry/Sensor/MLX90632Sensor.cpp index dfc049023..eb84edffc 100644 --- a/src/modules/Telemetry/Sensor/MLX90632Sensor.cpp +++ b/src/modules/Telemetry/Sensor/MLX90632Sensor.cpp @@ -8,16 +8,12 @@ MLX90632Sensor::MLX90632Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_MLX90632, "MLX90632") {} -int32_t MLX90632Sensor::runOnce() +bool MLX90632Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { LOG_INFO("Init sensor: %s", sensorName); - if (!hasSensor()) { - return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; - } MLX90632::status returnError; - if (mlx.begin(nodeTelemetrySensorsMap[sensorType].first, *nodeTelemetrySensorsMap[sensorType].second, returnError) == - true) // MLX90632 init + if (mlx.begin(dev->address.address, *bus, returnError) == true) // MLX90632 init { LOG_DEBUG("MLX90632 Init Succeed"); status = true; @@ -25,11 +21,10 @@ int32_t MLX90632Sensor::runOnce() LOG_ERROR("MLX90632 Init Failed"); status = false; } - return initI2CSensor(); + initI2CSensor(); + return status; } -void MLX90632Sensor::setup() {} - bool MLX90632Sensor::getMetrics(meshtastic_Telemetry *measurement) { measurement->variant.environment_metrics.has_temperature = true; diff --git a/src/modules/Telemetry/Sensor/MLX90632Sensor.h b/src/modules/Telemetry/Sensor/MLX90632Sensor.h index ef7be180a..566db8319 100644 --- a/src/modules/Telemetry/Sensor/MLX90632Sensor.h +++ b/src/modules/Telemetry/Sensor/MLX90632Sensor.h @@ -11,13 +11,10 @@ class MLX90632Sensor : public TelemetrySensor private: MLX90632 mlx = MLX90632(); - protected: - virtual void setup() override; - public: MLX90632Sensor(); - virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; }; #endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp b/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp index b6b5d89f7..e67b78145 100644 --- a/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp +++ b/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp @@ -16,24 +16,23 @@ meshtastic_Nau7802Config nau7802config = meshtastic_Nau7802Config_init_zero; NAU7802Sensor::NAU7802Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_NAU7802, "NAU7802") {} -int32_t NAU7802Sensor::runOnce() +bool NAU7802Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { LOG_INFO("Init sensor: %s", sensorName); - if (!hasSensor()) { - return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; + status = nau7802.begin(*bus); + if (!status) { + return status; } - status = nau7802.begin(*nodeTelemetrySensorsMap[sensorType].second); nau7802.setSampleRate(NAU7802_SPS_320); if (!loadCalibrationData()) { LOG_ERROR("Failed to load calibration data"); } nau7802.calibrateAFE(); LOG_INFO("Offset: %d, Calibration factor: %.2f", nau7802.getZeroOffset(), nau7802.getCalibrationFactor()); - return initI2CSensor(); + initI2CSensor(); + return status; } -void NAU7802Sensor::setup() {} - bool NAU7802Sensor::getMetrics(meshtastic_Telemetry *measurement) { LOG_DEBUG("NAU7802 getMetrics"); diff --git a/src/modules/Telemetry/Sensor/NAU7802Sensor.h b/src/modules/Telemetry/Sensor/NAU7802Sensor.h index cb9e64829..a45e9a78a 100644 --- a/src/modules/Telemetry/Sensor/NAU7802Sensor.h +++ b/src/modules/Telemetry/Sensor/NAU7802Sensor.h @@ -13,15 +13,14 @@ class NAU7802Sensor : public TelemetrySensor NAU7802 nau7802; protected: - virtual void setup() override; const char *nau7802ConfigFileName = "/prefs/nau7802.dat"; bool saveCalibrationData(); bool loadCalibrationData(); public: NAU7802Sensor(); - virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; void tare(); void calibrate(float weight); AdminMessageHandleResult handleAdminMessage(const meshtastic_MeshPacket &mp, meshtastic_AdminMessage *request, diff --git a/src/modules/Telemetry/Sensor/OPT3001Sensor.cpp b/src/modules/Telemetry/Sensor/OPT3001Sensor.cpp index 1f0407374..3407f2f0f 100644 --- a/src/modules/Telemetry/Sensor/OPT3001Sensor.cpp +++ b/src/modules/Telemetry/Sensor/OPT3001Sensor.cpp @@ -9,20 +9,15 @@ OPT3001Sensor::OPT3001Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_OPT3001, "OPT3001") {} -int32_t OPT3001Sensor::runOnce() +bool OPT3001Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { LOG_INFO("Init sensor: %s", sensorName); - if (!hasSensor()) { - return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; - } - auto errorCode = opt3001.begin(nodeTelemetrySensorsMap[sensorType].first); + auto errorCode = opt3001.begin(dev->address.address); status = errorCode == NO_ERROR; + if (!status) { + return status; + } - return initI2CSensor(); -} - -void OPT3001Sensor::setup() -{ OPT3001_Config newConfig; newConfig.RangeNumber = 0b1100; @@ -34,6 +29,10 @@ void OPT3001Sensor::setup() if (errorConfig != NO_ERROR) { LOG_ERROR("OPT3001 configuration error #%d", errorConfig); } + status = errorConfig == NO_ERROR; + + initI2CSensor(); + return status; } bool OPT3001Sensor::getMetrics(meshtastic_Telemetry *measurement) diff --git a/src/modules/Telemetry/Sensor/OPT3001Sensor.h b/src/modules/Telemetry/Sensor/OPT3001Sensor.h index a9da2d705..c8a140b51 100644 --- a/src/modules/Telemetry/Sensor/OPT3001Sensor.h +++ b/src/modules/Telemetry/Sensor/OPT3001Sensor.h @@ -12,13 +12,13 @@ class OPT3001Sensor : public TelemetrySensor private: ClosedCube_OPT3001 opt3001; - protected: - virtual void setup() override; - public: OPT3001Sensor(); - virtual int32_t runOnce() override; +#if WIRE_INTERFACES_COUNT > 1 + virtual bool onlyWire1() { return true; } +#endif virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; }; #endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/PCT2075Sensor.cpp b/src/modules/Telemetry/Sensor/PCT2075Sensor.cpp index d2b50d983..189317bf2 100644 --- a/src/modules/Telemetry/Sensor/PCT2075Sensor.cpp +++ b/src/modules/Telemetry/Sensor/PCT2075Sensor.cpp @@ -9,24 +9,18 @@ PCT2075Sensor::PCT2075Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_PCT2075, "PCT2075") {} -int32_t PCT2075Sensor::runOnce() +bool PCT2075Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { LOG_INFO("Init sensor: %s", sensorName); - if (!hasSensor()) { - return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; - } + status = pct2075.begin(dev->address.address, bus); - status = pct2075.begin(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second); - - return initI2CSensor(); + initI2CSensor(); + return status; } -void PCT2075Sensor::setup() {} - bool PCT2075Sensor::getMetrics(meshtastic_Telemetry *measurement) { measurement->variant.environment_metrics.has_temperature = true; - measurement->variant.environment_metrics.temperature = pct2075.getTemperature(); return true; diff --git a/src/modules/Telemetry/Sensor/PCT2075Sensor.h b/src/modules/Telemetry/Sensor/PCT2075Sensor.h index 842c973d0..55f9423d4 100644 --- a/src/modules/Telemetry/Sensor/PCT2075Sensor.h +++ b/src/modules/Telemetry/Sensor/PCT2075Sensor.h @@ -12,13 +12,10 @@ class PCT2075Sensor : public TelemetrySensor private: Adafruit_PCT2075 pct2075; - protected: - virtual void setup() override; - public: PCT2075Sensor(); - virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; }; #endif diff --git a/src/modules/Telemetry/Sensor/RAK12035Sensor.cpp b/src/modules/Telemetry/Sensor/RAK12035Sensor.cpp index 7a1bb01ce..ff0628cc3 100644 --- a/src/modules/Telemetry/Sensor/RAK12035Sensor.cpp +++ b/src/modules/Telemetry/Sensor/RAK12035Sensor.cpp @@ -6,16 +6,12 @@ RAK12035Sensor::RAK12035Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_RAK12035, "RAK12035") {} -int32_t RAK12035Sensor::runOnce() +bool RAK12035Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { - if (!hasSensor()) { - return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; - } - // TODO:: check for up to 2 additional sensors and start them if present. sensor.set_sensor_addr(RAK120351_ADDR); delay(100); - sensor.begin(nodeTelemetrySensorsMap[sensorType].first); + sensor.begin(dev->address.address); // Get sensor firmware version uint8_t data = 0; @@ -31,8 +27,13 @@ int32_t RAK12035Sensor::runOnce() LOG_ERROR("RAK12035Sensor Init Failed"); status = false; } + if (!status) { + return status; + } + setup(); - return initI2CSensor(); + initI2CSensor(); + return status; } void RAK12035Sensor::setup() diff --git a/src/modules/Telemetry/Sensor/RAK12035Sensor.h b/src/modules/Telemetry/Sensor/RAK12035Sensor.h index 2c32a840d..6a38d2eb3 100644 --- a/src/modules/Telemetry/Sensor/RAK12035Sensor.h +++ b/src/modules/Telemetry/Sensor/RAK12035Sensor.h @@ -16,13 +16,14 @@ class RAK12035Sensor : public TelemetrySensor { private: RAK12035 sensor; - - protected: - virtual void setup() override; + void setup(); public: RAK12035Sensor(); - virtual int32_t runOnce() override; +#if WIRE_INTERFACES_COUNT > 1 + virtual bool onlyWire1() { return true; } +#endif virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; }; #endif diff --git a/src/modules/Telemetry/Sensor/RCWL9620Sensor.cpp b/src/modules/Telemetry/Sensor/RCWL9620Sensor.cpp index 9f7a55cc5..3dbd06e8d 100644 --- a/src/modules/Telemetry/Sensor/RCWL9620Sensor.cpp +++ b/src/modules/Telemetry/Sensor/RCWL9620Sensor.cpp @@ -8,19 +8,15 @@ RCWL9620Sensor::RCWL9620Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_RCWL9620, "RCWL9620") {} -int32_t RCWL9620Sensor::runOnce() +bool RCWL9620Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { LOG_INFO("Init sensor: %s", sensorName); - if (!hasSensor()) { - return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; - } status = 1; - begin(nodeTelemetrySensorsMap[sensorType].second, nodeTelemetrySensorsMap[sensorType].first); - return initI2CSensor(); + begin(bus, dev->address.address); + initI2CSensor(); + return status; } -void RCWL9620Sensor::setup() {} - bool RCWL9620Sensor::getMetrics(meshtastic_Telemetry *measurement) { measurement->variant.environment_metrics.has_distance = true; diff --git a/src/modules/Telemetry/Sensor/RCWL9620Sensor.h b/src/modules/Telemetry/Sensor/RCWL9620Sensor.h index 7f9486d25..408db3633 100644 --- a/src/modules/Telemetry/Sensor/RCWL9620Sensor.h +++ b/src/modules/Telemetry/Sensor/RCWL9620Sensor.h @@ -16,14 +16,13 @@ class RCWL9620Sensor : public TelemetrySensor uint32_t _speed = 200000UL; protected: - virtual void setup() override; void begin(TwoWire *wire = &Wire, uint8_t addr = 0x57, uint8_t sda = -1, uint8_t scl = -1, uint32_t speed = 200000UL); float getDistance(); public: RCWL9620Sensor(); - virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; }; #endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/SHT31Sensor.cpp b/src/modules/Telemetry/Sensor/SHT31Sensor.cpp index 8619a7905..67a36933d 100644 --- a/src/modules/Telemetry/Sensor/SHT31Sensor.cpp +++ b/src/modules/Telemetry/Sensor/SHT31Sensor.cpp @@ -9,20 +9,13 @@ SHT31Sensor::SHT31Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SHT31, "SHT31") {} -int32_t SHT31Sensor::runOnce() +bool SHT31Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { LOG_INFO("Init sensor: %s", sensorName); - if (!hasSensor()) { - return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; - } - sht31 = Adafruit_SHT31(nodeTelemetrySensorsMap[sensorType].second); - status = sht31.begin(nodeTelemetrySensorsMap[sensorType].first); - return initI2CSensor(); -} - -void SHT31Sensor::setup() -{ - // Set up oversampling and filter initialization + sht31 = Adafruit_SHT31(bus); + status = sht31.begin(dev->address.address); + initI2CSensor(); + return status; } bool SHT31Sensor::getMetrics(meshtastic_Telemetry *measurement) diff --git a/src/modules/Telemetry/Sensor/SHT31Sensor.h b/src/modules/Telemetry/Sensor/SHT31Sensor.h index c3d81af95..ecb7d63a6 100644 --- a/src/modules/Telemetry/Sensor/SHT31Sensor.h +++ b/src/modules/Telemetry/Sensor/SHT31Sensor.h @@ -11,13 +11,10 @@ class SHT31Sensor : public TelemetrySensor private: Adafruit_SHT31 sht31; - protected: - virtual void setup() override; - public: SHT31Sensor(); - virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; }; #endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/SHT4XSensor.cpp b/src/modules/Telemetry/Sensor/SHT4XSensor.cpp index 83fdaf6c6..b11795d97 100644 --- a/src/modules/Telemetry/Sensor/SHT4XSensor.cpp +++ b/src/modules/Telemetry/Sensor/SHT4XSensor.cpp @@ -9,16 +9,16 @@ SHT4XSensor::SHT4XSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SHT4X, "SHT4X") {} -int32_t SHT4XSensor::runOnce() +bool SHT4XSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { LOG_INFO("Init sensor: %s", sensorName); - if (!hasSensor()) { - return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; - } uint32_t serialNumber = 0; - sht4x.begin(nodeTelemetrySensorsMap[sensorType].second); + status = sht4x.begin(bus); + if (!status) { + return status; + } serialNumber = sht4x.readSerial(); if (serialNumber != 0) { @@ -29,12 +29,8 @@ int32_t SHT4XSensor::runOnce() status = 0; } - return initI2CSensor(); -} - -void SHT4XSensor::setup() -{ - // Set up oversampling and filter initialization + initI2CSensor(); + return status; } bool SHT4XSensor::getMetrics(meshtastic_Telemetry *measurement) diff --git a/src/modules/Telemetry/Sensor/SHT4XSensor.h b/src/modules/Telemetry/Sensor/SHT4XSensor.h index da608cb82..7311d2366 100644 --- a/src/modules/Telemetry/Sensor/SHT4XSensor.h +++ b/src/modules/Telemetry/Sensor/SHT4XSensor.h @@ -11,13 +11,10 @@ class SHT4XSensor : public TelemetrySensor private: Adafruit_SHT4x sht4x = Adafruit_SHT4x(); - protected: - virtual void setup() override; - public: SHT4XSensor(); - virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; }; #endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/SHTC3Sensor.cpp b/src/modules/Telemetry/Sensor/SHTC3Sensor.cpp index e9c4d2a0b..fdab0b266 100644 --- a/src/modules/Telemetry/Sensor/SHTC3Sensor.cpp +++ b/src/modules/Telemetry/Sensor/SHTC3Sensor.cpp @@ -9,19 +9,13 @@ SHTC3Sensor::SHTC3Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SHTC3, "SHTC3") {} -int32_t SHTC3Sensor::runOnce() +bool SHTC3Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { LOG_INFO("Init sensor: %s", sensorName); - if (!hasSensor()) { - return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; - } - status = shtc3.begin(nodeTelemetrySensorsMap[sensorType].second); - return initI2CSensor(); -} + status = shtc3.begin(bus); -void SHTC3Sensor::setup() -{ - // Set up oversampling and filter initialization + initI2CSensor(); + return status; } bool SHTC3Sensor::getMetrics(meshtastic_Telemetry *measurement) diff --git a/src/modules/Telemetry/Sensor/SHTC3Sensor.h b/src/modules/Telemetry/Sensor/SHTC3Sensor.h index 458af6465..51cee18f7 100644 --- a/src/modules/Telemetry/Sensor/SHTC3Sensor.h +++ b/src/modules/Telemetry/Sensor/SHTC3Sensor.h @@ -11,13 +11,10 @@ class SHTC3Sensor : public TelemetrySensor private: Adafruit_SHTC3 shtc3 = Adafruit_SHTC3(); - protected: - virtual void setup() override; - public: SHTC3Sensor(); - virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; }; #endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/T1000xSensor.cpp b/src/modules/Telemetry/Sensor/T1000xSensor.cpp index 068969e8e..b123450ec 100644 --- a/src/modules/Telemetry/Sensor/T1000xSensor.cpp +++ b/src/modules/Telemetry/Sensor/T1000xSensor.cpp @@ -38,18 +38,10 @@ int8_t ntc_temp2[136] = { T1000xSensor::T1000xSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SENSOR_UNSET, "T1000x") {} -int32_t T1000xSensor::runOnce() +bool T1000xSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { LOG_INFO("Init sensor: %s", sensorName); - if (!hasSensor()) { - return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; - } - return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; -} - -void T1000xSensor::setup() -{ - // Set up oversampling and filter initialization + return true; } float T1000xSensor::getLux() diff --git a/src/modules/Telemetry/Sensor/T1000xSensor.h b/src/modules/Telemetry/Sensor/T1000xSensor.h index a1c771cfa..b840a2d88 100644 --- a/src/modules/Telemetry/Sensor/T1000xSensor.h +++ b/src/modules/Telemetry/Sensor/T1000xSensor.h @@ -7,13 +7,10 @@ class T1000xSensor : public TelemetrySensor { - protected: - virtual void setup() override; - public: T1000xSensor(); - virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; virtual float getLux(); virtual float getTemp(); }; diff --git a/src/modules/Telemetry/Sensor/TSL2561Sensor.cpp b/src/modules/Telemetry/Sensor/TSL2561Sensor.cpp index 9f3b7e460..4e02af642 100644 --- a/src/modules/Telemetry/Sensor/TSL2561Sensor.cpp +++ b/src/modules/Telemetry/Sensor/TSL2561Sensor.cpp @@ -9,22 +9,19 @@ TSL2561Sensor::TSL2561Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_TSL2561, "TSL2561") {} -int32_t TSL2561Sensor::runOnce() +bool TSL2561Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { LOG_INFO("Init sensor: %s", sensorName); - if (!hasSensor()) { - return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; + + status = tsl.begin(bus); + if (!status) { + return status; } - - status = tsl.begin(nodeTelemetrySensorsMap[sensorType].second); - - return initI2CSensor(); -} - -void TSL2561Sensor::setup() -{ tsl.setGain(TSL2561_GAIN_1X); tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_101MS); + + initI2CSensor(); + return status; } bool TSL2561Sensor::getMetrics(meshtastic_Telemetry *measurement) diff --git a/src/modules/Telemetry/Sensor/TSL2561Sensor.h b/src/modules/Telemetry/Sensor/TSL2561Sensor.h index 0329becd8..abf5a8f73 100644 --- a/src/modules/Telemetry/Sensor/TSL2561Sensor.h +++ b/src/modules/Telemetry/Sensor/TSL2561Sensor.h @@ -12,12 +12,9 @@ class TSL2561Sensor : public TelemetrySensor // The magic number is a sensor id, the actual value doesn't matter Adafruit_TSL2561_Unified tsl = Adafruit_TSL2561_Unified(TSL2561_ADDR_LOW, 12345); - protected: - virtual void setup() override; - public: TSL2561Sensor(); - virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; }; #endif diff --git a/src/modules/Telemetry/Sensor/TSL2591Sensor.cpp b/src/modules/Telemetry/Sensor/TSL2591Sensor.cpp index 04443ebec..0899d4470 100644 --- a/src/modules/Telemetry/Sensor/TSL2591Sensor.cpp +++ b/src/modules/Telemetry/Sensor/TSL2591Sensor.cpp @@ -10,21 +10,18 @@ TSL2591Sensor::TSL2591Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_TSL25911FN, "TSL2591") {} -int32_t TSL2591Sensor::runOnce() +bool TSL2591Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { LOG_INFO("Init sensor: %s", sensorName); - if (!hasSensor()) { - return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; + status = tsl.begin(bus); + if (!status) { + return status; } - status = tsl.begin(nodeTelemetrySensorsMap[sensorType].second); - - return initI2CSensor(); -} - -void TSL2591Sensor::setup() -{ tsl.setGain(TSL2591_GAIN_LOW); // 1x gain tsl.setTiming(TSL2591_INTEGRATIONTIME_100MS); + + initI2CSensor(); + return status; } bool TSL2591Sensor::getMetrics(meshtastic_Telemetry *measurement) diff --git a/src/modules/Telemetry/Sensor/TSL2591Sensor.h b/src/modules/Telemetry/Sensor/TSL2591Sensor.h index edf7698b1..1ac430a03 100644 --- a/src/modules/Telemetry/Sensor/TSL2591Sensor.h +++ b/src/modules/Telemetry/Sensor/TSL2591Sensor.h @@ -11,12 +11,9 @@ class TSL2591Sensor : public TelemetrySensor private: Adafruit_TSL2591 tsl; - protected: - virtual void setup() override; - public: TSL2591Sensor(); - virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; }; #endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/TelemetrySensor.h b/src/modules/Telemetry/Sensor/TelemetrySensor.h index 83d7b38b0..3c3e61808 100644 --- a/src/modules/Telemetry/Sensor/TelemetrySensor.h +++ b/src/modules/Telemetry/Sensor/TelemetrySensor.h @@ -6,6 +6,7 @@ #include "../mesh/generated/meshtastic/telemetry.pb.h" #include "MeshModule.h" #include "NodeDB.h" +#include "detect/ScanI2C.h" #include #if !ARCH_PORTDUINO @@ -42,22 +43,32 @@ class TelemetrySensor initialized = true; return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } - virtual void setup() = 0; + + // TODO: check is setup used at all? + virtual void setup() {} public: + virtual ~TelemetrySensor() {} + virtual AdminMessageHandleResult handleAdminMessage(const meshtastic_MeshPacket &mp, meshtastic_AdminMessage *request, meshtastic_AdminMessage *response) { return AdminMessageHandleResult::NOT_HANDLED; } + // TODO: delete after migration bool hasSensor() { return nodeTelemetrySensorsMap[sensorType].first > 0; } - virtual int32_t runOnce() = 0; +#if WIRE_INTERFACES_COUNT > 1 + // Set to true if Implementation only works first I2C port (Wire) + virtual bool onlyWire1() { return false; } +#endif + virtual int32_t runOnce() { return INT32_MAX; } virtual bool isInitialized() { return initialized; } virtual bool isRunning() { return status > 0; } virtual bool getMetrics(meshtastic_Telemetry *measurement) = 0; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { return false; }; }; #endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/VEML7700Sensor.cpp b/src/modules/Telemetry/Sensor/VEML7700Sensor.cpp index b075bf405..c89463be5 100644 --- a/src/modules/Telemetry/Sensor/VEML7700Sensor.cpp +++ b/src/modules/Telemetry/Sensor/VEML7700Sensor.cpp @@ -11,23 +11,22 @@ VEML7700Sensor::VEML7700Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_VEML7700, "VEML7700") {} -int32_t VEML7700Sensor::runOnce() +bool VEML7700Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { LOG_INFO("Init sensor: %s", sensorName); - if (!hasSensor()) { - return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; + status = veml7700.begin(bus); + if (!status) { + return status; } - status = veml7700.begin(nodeTelemetrySensorsMap[sensorType].second); veml7700.setLowThreshold(10000); veml7700.setHighThreshold(20000); veml7700.interruptEnable(true); - return initI2CSensor(); + initI2CSensor(); + return status; } -void VEML7700Sensor::setup() {} - /*! * @brief Copmute lux from ALS reading. * @param rawALS raw ALS register value diff --git a/src/modules/Telemetry/Sensor/VEML7700Sensor.h b/src/modules/Telemetry/Sensor/VEML7700Sensor.h index f40384ad3..92883df08 100644 --- a/src/modules/Telemetry/Sensor/VEML7700Sensor.h +++ b/src/modules/Telemetry/Sensor/VEML7700Sensor.h @@ -16,12 +16,9 @@ class VEML7700Sensor : public TelemetrySensor float computeLux(uint16_t rawALS, bool corrected); float getResolution(void); - protected: - virtual void setup() override; - public: VEML7700Sensor(); - virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; }; #endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/nullSensor.cpp b/src/modules/Telemetry/Sensor/nullSensor.cpp index c84b9d27f..1d545186a 100644 --- a/src/modules/Telemetry/Sensor/nullSensor.cpp +++ b/src/modules/Telemetry/Sensor/nullSensor.cpp @@ -11,7 +11,7 @@ NullSensor::NullSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SENSOR int32_t NullSensor::runOnce() { - return 0; + return INT32_MAX; } void NullSensor::setup() {} diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 8ce352f14..33887557f 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -473,7 +473,9 @@ bool MQTT::publish(const char *topic, const uint8_t *payload, size_t length, boo if (moduleConfig.mqtt.proxy_to_client_enabled) { meshtastic_MqttClientProxyMessage *msg = mqttClientProxyMessagePool.allocZeroed(); msg->which_payload_variant = meshtastic_MqttClientProxyMessage_data_tag; - strcpy(msg->topic, topic); + strlcpy(msg->topic, topic, sizeof(msg->topic)); + if (length > sizeof(msg->payload_variant.data.bytes)) + length = sizeof(msg->payload_variant.data.bytes); msg->payload_variant.data.size = length; memcpy(msg->payload_variant.data.bytes, payload, length); msg->retained = retained; diff --git a/src/nimble/NimbleBluetooth.cpp b/src/nimble/NimbleBluetooth.cpp index 5524765f3..6238031f6 100644 --- a/src/nimble/NimbleBluetooth.cpp +++ b/src/nimble/NimbleBluetooth.cpp @@ -3,12 +3,15 @@ #include "BluetoothCommon.h" #include "NimbleBluetooth.h" #include "PowerFSM.h" +#include "StaticPointerQueue.h" +#include "concurrency/OSThread.h" #include "main.h" #include "mesh/PhoneAPI.h" #include "mesh/mesh-pb-constants.h" #include "sleep.h" #include +#include #include #ifdef NIMBLE_TWO @@ -17,45 +20,291 @@ #include "PowerStatus.h" #endif +#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6) +#if defined(CONFIG_NIMBLE_CPP_IDF) +#include "host/ble_gap.h" +#else +#include "nimble/nimble/host/include/host/ble_gap.h" +#endif + +namespace +{ +constexpr uint16_t kPreferredBleMtu = 517; +constexpr uint16_t kPreferredBleTxOctets = 251; +constexpr uint16_t kPreferredBleTxTimeUs = (kPreferredBleTxOctets + 14) * 8; +} // namespace +#endif + +// Debugging options: careful, they slow things down quite a bit! +// #define DEBUG_NIMBLE_ON_READ_TIMING // uncomment to time onRead duration +// #define DEBUG_NIMBLE_ON_WRITE_TIMING // uncomment to time onWrite duration +// #define DEBUG_NIMBLE_NOTIFY // uncomment to enable notify logging + +#define NIMBLE_BLUETOOTH_TO_PHONE_QUEUE_SIZE 3 +#define NIMBLE_BLUETOOTH_FROM_PHONE_QUEUE_SIZE 3 + NimBLECharacteristic *fromNumCharacteristic; NimBLECharacteristic *BatteryCharacteristic; NimBLECharacteristic *logRadioCharacteristic; NimBLEServer *bleServer; static bool passkeyShowing; +static std::atomic nimbleBluetoothConnHandle{BLE_HS_CONN_HANDLE_NONE}; // BLE_HS_CONN_HANDLE_NONE means "no connection" class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread { + /* + CAUTION: There's a lot going on here and lots of room to break things. + + This NimbleBluetooth.cpp file does some tricky synchronization between the NimBLE FreeRTOS task (which runs the onRead and + onWrite callbacks) and the main task (which runs runOnce and the rest of PhoneAPI). + + The main idea is to add a little bit of synchronization here to make it so that the rest of the codebase doesn't have to + know about concurrency and mutexes, and can just run happily ever after as a cooperative multitasking OSThread system, where + locking isn't something that anyone has to worry about too much! :) + + We achieve this by having some queues and mutexes in this file only, and ensuring that all calls to getFromRadio and + handleToRadio are only made from the main FreeRTOS task. This way, the rest of the codebase doesn't have to worry about + being run concurrently, which would make everything else much much much more complicated. + + PHONE -> RADIO: + - [NimBLE FreeRTOS task:] onWrite callback holds fromPhoneMutex and pushes received packets into fromPhoneQueue. + - [Main task:] runOnceHandleFromPhoneQueue in main task holds fromPhoneMutex, pulls packets from fromPhoneQueue, and calls + handleToRadio **in main task**. + + RADIO -> PHONE: + - [NimBLE FreeRTOS task:] onRead callback sets onReadCallbackIsWaitingForData flag and polls in a busy loop. (unless + there's already a packet waiting in toPhoneQueue) + - [Main task:] runOnceHandleToPhoneQueue sees onReadCallbackIsWaitingForData flag, calls getFromRadio **in main task** to + get packets from radio, holds toPhoneMutex, pushes the packet into toPhoneQueue, and clears the + onReadCallbackIsWaitingForData flag. + - [NimBLE FreeRTOS task:] onRead callback sees that the onReadCallbackIsWaitingForData flag cleared, holds toPhoneMutex, + pops the packet from toPhoneQueue, and returns it to NimBLE. + + MUTEXES: + - fromPhoneMutex protects fromPhoneQueue and fromPhoneQueueSize + - toPhoneMutex protects toPhoneQueue, toPhoneQueueByteSizes, and toPhoneQueueSize + + ATOMICS: + - fromPhoneQueueSize is only increased by onWrite, and only decreased by runOnceHandleFromPhoneQueue (or onDisconnect). + - toPhoneQueueSize is only increased by runOnceHandleToPhoneQueue, and only decreased by onRead (or onDisconnect). + - onReadCallbackIsWaitingForData is a flag. It's only set by onRead, and only cleared by runOnceHandleToPhoneQueue (or + onDisconnect). + + PRELOADING: see comments in runOnceToPhoneCanPreloadNextPacket about when it's safe to preload packets from getFromRadio. + + BLE CONNECTION PARAMS: + - During config, we request a high-throughput, low-latency BLE connection for speed. + - After config, we switch to a lower-power BLE connection for steady-state use to extend battery life. + + MEMORY MANAGEMENT: + - We keep packets on the stack and do not allocate heap. + - We use std::array for fromPhoneQueue and toPhoneQueue to avoid mallocs and frees across FreeRTOS tasks. + - Yes, we have to do some copy operations on pop because of this, but it's worth it to avoid cross-task memory management. + + NOTIFY IS BROKEN: + - Adding NIMBLE_PROPERTY::NOTIFY to FromRadioCharacteristic appears to break things. It is NOT backwards compatible. + + ZERO-SIZE READS: + - Returning a zero-size read from onRead breaks some clients during the config phase. So we have to block onRead until we + have data. + - During the STATE_SEND_PACKETS phase, it's totally OK to return zero-size reads, as clients are expected to do reads + until they get a 0-byte response. + + CROSS-TASK WAKEUP: + - If you call: bluetoothPhoneAPI->setIntervalFromNow(0); to schedule immediate processing of new data, + - Then you should also call: concurrency::mainDelay.interrupt(); to wake up the main loop if it's sleeping. + - Otherwise, you're going to wait ~100ms or so until the main loop wakes up from some other cause. + */ + public: - BluetoothPhoneAPI() : concurrency::OSThread("NimbleBluetooth") { nimble_queue.resize(3); } - std::vector nimble_queue; - std::mutex nimble_mutex; - uint8_t queue_size = 0; - bool has_fromRadio = false; - uint8_t fromRadioBytes[meshtastic_FromRadio_size] = {0}; - size_t numBytes = 0; - bool hasChecked = false; - bool phoneWants = false; + BluetoothPhoneAPI() : concurrency::OSThread("NimbleBluetooth") {} + + /* Packets from phone (BLE onWrite callback) */ + std::mutex fromPhoneMutex; + std::atomic fromPhoneQueueSize{0}; + // We use array here (and pay the cost of memcpy) to avoid dynamic memory allocations and frees across FreeRTOS tasks. + std::array fromPhoneQueue{}; + + /* Packets to phone (BLE onRead callback) */ + std::mutex toPhoneMutex; + std::atomic toPhoneQueueSize{0}; + // We use array here (and pay the cost of memcpy) to avoid dynamic memory allocations and frees across FreeRTOS tasks. + std::array, NIMBLE_BLUETOOTH_TO_PHONE_QUEUE_SIZE> toPhoneQueue{}; + std::array toPhoneQueueByteSizes{}; + // The onReadCallbackIsWaitingForData flag provides synchronization between the NimBLE task's onRead callback and our main + // task's runOnce. It's only set by onRead, and only cleared by runOnce. + std::atomic onReadCallbackIsWaitingForData{false}; + + /* Statistics/logging helpers */ + std::atomic readCount{0}; + std::atomic notifyCount{0}; + std::atomic writeCount{0}; protected: virtual int32_t runOnce() override { - std::lock_guard guard(nimble_mutex); - if (queue_size > 0) { - for (uint8_t i = 0; i < queue_size; i++) { - handleToRadio(nimble_queue.at(i).data(), nimble_queue.at(i).length()); - } - LOG_DEBUG("Queue_size %u", queue_size); - queue_size = 0; - } - if (hasChecked == false && phoneWants == true) { - numBytes = getFromRadio(fromRadioBytes); - hasChecked = true; + while (runOnceHasWorkToDo()) { + /* + PROCESS fromPhoneQueue BEFORE toPhoneQueue: + + In normal STATE_SEND_PACKETS operation, it's unlikely that we'll have both writes and reads to process at the same + time, because either onWrite or onRead will trigger this runOnce. And in STATE_SEND_PACKETS, it's generally ok to + service either the reads or writes first. + + However, during the initial setup wantConfig packet, the clients send a write and immediately send a read, and they + expect the read will respond to the write. (This also happens when a client goes from STATE_SEND_PACKETS back to + another wantConfig, like the iOS client does when requesting the nodedb after requesting the main config only.) + + So it's safest to always service writes (fromPhoneQueue) before reads (toPhoneQueue), so that any "synchronous" + write-then-read sequences from the client work as expected, even if this means we block onRead for a while: this is + what the client wants! + */ + + // PHONE -> RADIO: + runOnceHandleFromPhoneQueue(); // pull data from onWrite to handleToRadio + + // RADIO -> PHONE: + runOnceHandleToPhoneQueue(); // push data from getFromRadio to onRead } // the run is triggered via NimbleBluetoothToRadioCallback and NimbleBluetoothFromRadioCallback return INT32_MAX; } + + virtual void onConfigStart() override + { + LOG_INFO("BLE onConfigStart"); + + // Prefer high throughput during config/setup, at the cost of high power consumption (for a few seconds) + if (bleServer && isConnected()) { + uint16_t conn_handle = nimbleBluetoothConnHandle.load(); + if (conn_handle != BLE_HS_CONN_HANDLE_NONE) { + requestHighThroughputConnection(conn_handle); + } + } + } + + virtual void onConfigComplete() override + { + LOG_INFO("BLE onConfigComplete"); + + // Switch to lower power consumption BLE connection params for steady-state use after config/setup is complete + if (bleServer && isConnected()) { + uint16_t conn_handle = nimbleBluetoothConnHandle.load(); + if (conn_handle != BLE_HS_CONN_HANDLE_NONE) { + requestLowerPowerConnection(conn_handle); + } + } + } + + bool runOnceHasWorkToDo() { return runOnceHasWorkToPhone() || runOnceHasWorkFromPhone(); } + + bool runOnceHasWorkToPhone() { return onReadCallbackIsWaitingForData || runOnceToPhoneCanPreloadNextPacket(); } + + bool runOnceToPhoneCanPreloadNextPacket() + { + /* + * PRELOADING getFromRadio RESPONSES: + * + * It's not safe to preload packets if we're in STATE_SEND_PACKETS, because there may be a while between the time we call + * getFromRadio and when the client actually reads it. If the connection drops in that time, we might lose that packet + * forever. In STATE_SEND_PACKETS, if we wait for onRead before we call getFromRadio, we minimize the time window where + * the client might disconnect before completing the read. + * + * However, if we're in the setup states (sending config, nodeinfo, etc), it's safe and beneficial to preload packets into + * toPhoneQueue because the client will just reconnect after a disconnect, losing nothing. + */ + + if (!isConnected()) { + return false; + } else if (isSendingPackets()) { + // If we're in STATE_SEND_PACKETS, we must wait for onRead before calling getFromRadio. + return false; + } else { + // In other states, we can preload as long as there's space in the toPhoneQueue. + return toPhoneQueueSize < NIMBLE_BLUETOOTH_TO_PHONE_QUEUE_SIZE; + } + } + + void runOnceHandleToPhoneQueue() + { + // Stack buffer for getFromRadio packet + uint8_t fromRadioBytes[meshtastic_FromRadio_size] = {0}; + size_t numBytes = 0; + + if (onReadCallbackIsWaitingForData || runOnceToPhoneCanPreloadNextPacket()) { + numBytes = getFromRadio(fromRadioBytes); + + if (numBytes == 0) { + /* + Client expected a read, but we have nothing to send. + + In STATE_SEND_PACKETS, it is 100% OK to return a 0-byte response, as we expect clients to do read beyond + notifies regularly, to make sure they have nothing else to read. + + In other states, this is fine **so long as we've already processed pending onWrites first**, because the client + may requesting wantConfig and immediately doing a read. + */ + } else { + // Push to toPhoneQueue, protected by toPhoneMutex. Hold the mutex as briefly as possible. + if (toPhoneQueueSize < NIMBLE_BLUETOOTH_TO_PHONE_QUEUE_SIZE) { + // Note: the comparison above is safe without a mutex because we are the only method that *increases* + // toPhoneQueueSize. (It's okay if toPhoneQueueSize *decreases* in the NimBLE task meanwhile.) + + { // scope for toPhoneMutex mutex + std::lock_guard guard(toPhoneMutex); + size_t storeAtIndex = toPhoneQueueSize.load(); + memcpy(toPhoneQueue[storeAtIndex].data(), fromRadioBytes, numBytes); + toPhoneQueueByteSizes[storeAtIndex] = numBytes; + toPhoneQueueSize++; + } +#ifdef DEBUG_NIMBLE_ON_READ_TIMING + LOG_DEBUG("BLE getFromRadio returned numBytes=%u, pushed toPhoneQueueSize=%u", numBytes, + toPhoneQueueSize.load()); +#endif + } else { + // Shouldn't happen because the onRead callback shouldn't be waiting if the queue is full! + LOG_ERROR("Shouldn't happen! Drop FromRadio packet, toPhoneQueue full (%u bytes)", numBytes); + } + } + + // Clear the onReadCallbackIsWaitingForData flag so onRead knows it can proceed. + onReadCallbackIsWaitingForData = false; // only clear this flag AFTER the push + } + } + + bool runOnceHasWorkFromPhone() { return fromPhoneQueueSize > 0; } + + void runOnceHandleFromPhoneQueue() + { + // Handle packets we received from onWrite from the phone. + if (fromPhoneQueueSize > 0) { + // Note: the comparison above is safe without a mutex because we are the only method that *decreases* + // fromPhoneQueueSize. (It's okay if fromPhoneQueueSize *increases* in the NimBLE task meanwhile.) + + LOG_DEBUG("NimbleBluetooth: handling ToRadio packet, fromPhoneQueueSize=%u", fromPhoneQueueSize.load()); + + // Pop the front of fromPhoneQueue, holding the mutex only briefly while we pop. + NimBLEAttValue val; + { // scope for fromPhoneMutex mutex + std::lock_guard guard(fromPhoneMutex); + val = fromPhoneQueue[0]; + + // Shift the rest of the queue down + for (uint8_t i = 1; i < fromPhoneQueueSize; i++) { + fromPhoneQueue[i - 1] = fromPhoneQueue[i]; + } + + // Safe decrement due to onDisconnect + if (fromPhoneQueueSize > 0) + fromPhoneQueueSize--; + } + + handleToRadio(val.data(), val.length()); + } + } + /** * Subclasses can use this as a hook to provide custom notifications for their transport (i.e. bluetooth notifies) */ @@ -63,14 +312,22 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread { PhoneAPI::onNowHasData(fromRadioNum); + int currentNotifyCount = notifyCount.fetch_add(1); + uint8_t cc = bleServer->getConnectedCount(); - LOG_DEBUG("BLE notify fromNum: %d connections: %d", fromRadioNum, cc); + +#ifdef DEBUG_NIMBLE_NOTIFY + // This logging slows things down when there are lots of packets going to the phone, like initial connection: + LOG_DEBUG("BLE notify(%d) fromNum: %d connections: %d", currentNotifyCount, fromRadioNum, cc); +#endif uint8_t val[4]; put_le32(val, fromRadioNum); fromNumCharacteristic->setValue(val, sizeof(val)); #ifdef NIMBLE_TWO + // NOTE: I don't have any NIMBLE_TWO devices, but this line makes me suspicious, and I suspect it needs to just be + // notify(). fromNumCharacteristic->notify(val, sizeof(val), BLE_HS_CONN_HANDLE_NONE); #else fromNumCharacteristic->notify(); @@ -79,6 +336,54 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread /// Check the current underlying physical link to see if the client is currently connected virtual bool checkIsConnected() { return bleServer && bleServer->getConnectedCount() > 0; } + + void requestHighThroughputConnection(uint16_t conn_handle) + { + /* Request a lower-latency, higher-throughput BLE connection. + + This comes at the cost of higher power consumption, so we may want to only use this for initial setup, and then switch to + a slower mode. + + See https://developer.apple.com/library/archive/qa/qa1931/_index.html for formulas to calculate values, iOS/macOS + constraints, and recommendations. (Android doesn't have specific constraints, but seems to be compatible with the Apple + recommendations.) + + Selected settings: + minInterval (units of 1.25ms): 7.5ms = 6 (lower than the Apple recommended minimum, but allows faster when the client + supports it.) + maxInterval (units of 1.25ms): 15ms = 12 + latency: 0 (don't allow peripheral to skip any connection events) + timeout (units of 10ms): 6 seconds = 600 (supervision timeout) + + These are intentionally aggressive to prioritize speed over power consumption, but are only used for a few seconds at + setup. Not worth adjusting much. + */ + LOG_INFO("BLE requestHighThroughputConnection"); + bleServer->updateConnParams(conn_handle, 6, 12, 0, 600); + } + + void requestLowerPowerConnection(uint16_t conn_handle) + { + /* Request a lower power consumption (but higher latency, lower throughput) BLE connection. + + This is suitable for steady-state operation after initial setup is complete. + + See https://developer.apple.com/library/archive/qa/qa1931/_index.html for formulas to calculate values, iOS/macOS + constraints, and recommendations. (Android doesn't have specific constraints, but seems to be compatible with the Apple + recommendations.) + + Selected settings: + minInterval (units of 1.25ms): 30ms = 24 + maxInterval (units of 1.25ms): 50ms = 40 + latency: 2 (allow peripheral to skip up to 2 consecutive connection events to save power) + timeout (units of 10ms): 6 seconds = 600 (supervision timeout) + + There's an opportunity for tuning here if anyone wants to do some power measurements, but these should allow 10-20 packets + per second. + */ + LOG_INFO("BLE requestLowerPowerConnection"); + bleServer->updateConnParams(conn_handle, 24, 40, 2, 600); + } }; static BluetoothPhoneAPI *bluetoothPhoneAPI; @@ -98,16 +403,45 @@ class NimbleBluetoothToRadioCallback : public NimBLECharacteristicCallbacks #endif { + // CAUTION: This callback runs in the NimBLE task!!! Don't do anything except communicate with the main task's runOnce. + // Assumption: onWrite is serialized by NimBLE, so we don't need to lock here against multiple concurrent onWrite calls. + + int currentWriteCount = bluetoothPhoneAPI->writeCount.fetch_add(1); + +#ifdef DEBUG_NIMBLE_ON_WRITE_TIMING + int startMillis = millis(); + LOG_DEBUG("BLE onWrite(%d): start millis=%d", currentWriteCount, startMillis); +#endif + auto val = pCharacteristic->getValue(); if (memcmp(lastToRadio, val.data(), val.length()) != 0) { - if (bluetoothPhoneAPI->queue_size < 3) { + if (bluetoothPhoneAPI->fromPhoneQueueSize < NIMBLE_BLUETOOTH_FROM_PHONE_QUEUE_SIZE) { + // Note: the comparison above is safe without a mutex because we are the only method that *increases* + // fromPhoneQueueSize. (It's okay if fromPhoneQueueSize *decreases* in the main task meanwhile.) memcpy(lastToRadio, val.data(), val.length()); - std::lock_guard guard(bluetoothPhoneAPI->nimble_mutex); - bluetoothPhoneAPI->nimble_queue.at(bluetoothPhoneAPI->queue_size) = val; - bluetoothPhoneAPI->queue_size++; + + { // scope for fromPhoneMutex mutex + // Append to fromPhoneQueue, protected by fromPhoneMutex. Hold the mutex as briefly as possible. + std::lock_guard guard(bluetoothPhoneAPI->fromPhoneMutex); + bluetoothPhoneAPI->fromPhoneQueue.at(bluetoothPhoneAPI->fromPhoneQueueSize) = val; + bluetoothPhoneAPI->fromPhoneQueueSize++; + } + + // After releasing the mutex, schedule immediate processing of the new packet. bluetoothPhoneAPI->setIntervalFromNow(0); + concurrency::mainDelay.interrupt(); // wake up main loop if sleeping + +#ifdef DEBUG_NIMBLE_ON_WRITE_TIMING + int finishMillis = millis(); + LOG_DEBUG("BLE onWrite(%d): append to fromPhoneQueue took %u ms. numBytes=%d", currentWriteCount, + finishMillis - startMillis, val.length()); +#endif + } else { + LOG_WARN("BLE onWrite(%d): Drop ToRadio packet, fromPhoneQueue full (%u bytes)", currentWriteCount, val.length()); } + } else { + LOG_DEBUG("BLE onWrite(%d): Drop duplicate ToRadio packet (%u bytes)", currentWriteCount, val.length()); } } }; @@ -120,21 +454,107 @@ class NimbleBluetoothFromRadioCallback : public NimBLECharacteristicCallbacks virtual void onRead(NimBLECharacteristic *pCharacteristic) #endif { - int tries = 0; - bluetoothPhoneAPI->phoneWants = true; - while (!bluetoothPhoneAPI->hasChecked && tries < 100) { - bluetoothPhoneAPI->setIntervalFromNow(0); - delay(20); - tries++; - } - std::lock_guard guard(bluetoothPhoneAPI->nimble_mutex); - pCharacteristic->setValue(bluetoothPhoneAPI->fromRadioBytes, bluetoothPhoneAPI->numBytes); + // CAUTION: This callback runs in the NimBLE task!!! Don't do anything except communicate with the main task's runOnce. - if (bluetoothPhoneAPI->numBytes != 0) // if we did send something, queue it up right away to reload + int currentReadCount = bluetoothPhoneAPI->readCount.fetch_add(1); + int tries = 0; + int startMillis = millis(); + +#ifdef DEBUG_NIMBLE_ON_READ_TIMING + LOG_DEBUG("BLE onRead(%d): start millis=%d", currentReadCount, startMillis); +#endif + + // Is there a packet ready to go, or do we have to ask the main task to get one for us? + if (bluetoothPhoneAPI->toPhoneQueueSize > 0) { + // Note: the comparison above is safe without a mutex because we are the only method that *decreases* + // toPhoneQueueSize. (It's okay if toPhoneQueueSize *increases* in the main task meanwhile.) + + // There's already a packet queued. Great! We don't need to wait for onReadCallbackIsWaitingForData. +#ifdef DEBUG_NIMBLE_ON_READ_TIMING + LOG_DEBUG("BLE onRead(%d): packet already waiting, no need to set onReadCallbackIsWaitingForData", currentReadCount); +#endif + } else { + // Tell the main task that we'd like a packet. + bluetoothPhoneAPI->onReadCallbackIsWaitingForData = true; + + // Wait for the main task to produce a packet for us, up to about 20 seconds. + // It normally takes just a few milliseconds, but at initial startup, etc, the main task can get blocked for longer + // doing various setup tasks. + while (bluetoothPhoneAPI->onReadCallbackIsWaitingForData && tries < 4000) { + // Schedule the main task runOnce to run ASAP. + bluetoothPhoneAPI->setIntervalFromNow(0); + concurrency::mainDelay.interrupt(); // wake up main loop if sleeping + + if (!bluetoothPhoneAPI->onReadCallbackIsWaitingForData) { + // we may be able to break even before a delay, if the call to interrupt woke up the main loop and it ran + // already +#ifdef DEBUG_NIMBLE_ON_READ_TIMING + LOG_DEBUG("BLE onRead(%d): broke before delay after %u ms, %d tries", currentReadCount, + millis() - startMillis, tries); +#endif + break; + } + + // This delay happens in the NimBLE FreeRTOS task, which really can't do anything until we get a value back. + // No harm in polling pretty frequently. + delay(tries < 20 ? 1 : 5); + tries++; + + if (tries == 4000) { + LOG_WARN( + "BLE onRead(%d): timeout waiting for data after %u ms, %d tries, giving up and returning 0-size response", + currentReadCount, millis() - startMillis, tries); + } + } + } + + // Pop from toPhoneQueue, protected by toPhoneMutex. Hold the mutex as briefly as possible. + uint8_t fromRadioBytes[meshtastic_FromRadio_size] = {0}; // Stack buffer for getFromRadio packet + size_t numBytes = 0; + { // scope for toPhoneMutex mutex + std::lock_guard guard(bluetoothPhoneAPI->toPhoneMutex); + size_t toPhoneQueueSize = bluetoothPhoneAPI->toPhoneQueueSize.load(); + if (toPhoneQueueSize > 0) { + // Copy from the front of the toPhoneQueue + memcpy(fromRadioBytes, bluetoothPhoneAPI->toPhoneQueue[0].data(), bluetoothPhoneAPI->toPhoneQueueByteSizes[0]); + numBytes = bluetoothPhoneAPI->toPhoneQueueByteSizes[0]; + + // Shift the rest of the queue down + for (uint8_t i = 1; i < toPhoneQueueSize; i++) { + memcpy(bluetoothPhoneAPI->toPhoneQueue[i - 1].data(), bluetoothPhoneAPI->toPhoneQueue[i].data(), + bluetoothPhoneAPI->toPhoneQueueByteSizes[i]); + // The above line is similar to: + // bluetoothPhoneAPI->toPhoneQueue[i - 1] = bluetoothPhoneAPI->toPhoneQueue[i] + // but is usually faster because it doesn't have to copy all the trailing bytes beyond + // toPhoneQueueByteSizes[i]. + // + // We deliberately use an array here (and pay the CPU cost of some memcpy) to avoid synchronizing dynamic + // memory allocations and frees across FreeRTOS tasks. + + bluetoothPhoneAPI->toPhoneQueueByteSizes[i - 1] = bluetoothPhoneAPI->toPhoneQueueByteSizes[i]; + } + + // Safe decrement due to onDisconnect + if (bluetoothPhoneAPI->toPhoneQueueSize > 0) + bluetoothPhoneAPI->toPhoneQueueSize--; + } else { + // nothing in the toPhoneQueue; that's fine, and we'll just have numBytes=0. + } + } + +#ifdef DEBUG_NIMBLE_ON_READ_TIMING + int finishMillis = millis(); + LOG_DEBUG("BLE onRead(%d): onReadCallbackIsWaitingForData took %u ms, %d tries. numBytes=%d", currentReadCount, + finishMillis - startMillis, tries, numBytes); +#endif + + pCharacteristic->setValue(fromRadioBytes, numBytes); + + // If we sent something, wake up the main loop if it's sleeping in case there are more packets ready to enqueue. + if (numBytes != 0) { bluetoothPhoneAPI->setIntervalFromNow(0); - bluetoothPhoneAPI->numBytes = 0; - bluetoothPhoneAPI->hasChecked = false; - bluetoothPhoneAPI->phoneWants = false; + concurrency::mainDelay.interrupt(); // wake up main loop if sleeping + } } }; @@ -216,12 +636,40 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks if (screen) screen->endAlert(); } + + // Store the connection handle for future use +#ifdef NIMBLE_TWO + nimbleBluetoothConnHandle = connInfo.getConnHandle(); +#else + nimbleBluetoothConnHandle = desc->conn_handle; +#endif } #ifdef NIMBLE_TWO virtual void onConnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo) { LOG_INFO("BLE incoming connection %s", connInfo.getAddress().toString().c_str()); + +#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6) + const uint16_t connHandle = connInfo.getConnHandle(); + int phyResult = + ble_gap_set_prefered_le_phy(connHandle, BLE_GAP_LE_PHY_2M_MASK, BLE_GAP_LE_PHY_2M_MASK, BLE_GAP_LE_PHY_CODED_ANY); + if (phyResult == 0) { + LOG_INFO("BLE conn %u requested 2M PHY", connHandle); + } else { + LOG_WARN("Failed to prefer 2M PHY for conn %u, rc=%d", connHandle, phyResult); + } + + int dataLenResult = ble_gap_set_data_len(connHandle, kPreferredBleTxOctets, kPreferredBleTxTimeUs); + if (dataLenResult == 0) { + LOG_INFO("BLE conn %u requested data length %u bytes", connHandle, kPreferredBleTxOctets); + } else { + LOG_WARN("Failed to raise data length for conn %u, rc=%d", connHandle, dataLenResult); + } + + LOG_INFO("BLE conn %u initial MTU %u (target %u)", connHandle, connInfo.getMTU(), kPreferredBleMtu); + pServer->updateConnParams(connHandle, 6, 12, 0, 200); +#endif } virtual void onDisconnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo, int reason) @@ -241,19 +689,41 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks bluetoothStatus->updateStatus(&newStatus); if (bluetoothPhoneAPI) { - std::lock_guard guard(bluetoothPhoneAPI->nimble_mutex); bluetoothPhoneAPI->close(); - bluetoothPhoneAPI->hasChecked = false; - bluetoothPhoneAPI->phoneWants = false; - bluetoothPhoneAPI->numBytes = 0; - bluetoothPhoneAPI->queue_size = 0; + + { // scope for fromPhoneMutex mutex + std::lock_guard guard(bluetoothPhoneAPI->fromPhoneMutex); + bluetoothPhoneAPI->fromPhoneQueueSize = 0; + } + + bluetoothPhoneAPI->onReadCallbackIsWaitingForData = false; + { // scope for toPhoneMutex mutex + std::lock_guard guard(bluetoothPhoneAPI->toPhoneMutex); + bluetoothPhoneAPI->toPhoneQueueSize = 0; + } + + bluetoothPhoneAPI->readCount = 0; + bluetoothPhoneAPI->notifyCount = 0; + bluetoothPhoneAPI->writeCount = 0; } // Clear the last ToRadio packet buffer to avoid rejecting first packet from new connection memset(lastToRadio, 0, sizeof(lastToRadio)); + + nimbleBluetoothConnHandle = BLE_HS_CONN_HANDLE_NONE; // BLE_HS_CONN_HANDLE_NONE means "no connection" + #ifdef NIMBLE_TWO // Restart Advertising ble->startAdvertising(); +#else + NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); + if (!pAdvertising->start(0)) { + if (pAdvertising->isAdvertising()) { + LOG_DEBUG("BLE advertising already running"); + } else { + LOG_ERROR("BLE failed to restart advertising"); + } + } #endif } }; @@ -328,6 +798,30 @@ void NimbleBluetooth::setup() NimBLEDevice::init(getDeviceName()); NimBLEDevice::setPower(ESP_PWR_LVL_P9); +#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6) + int mtuResult = NimBLEDevice::setMTU(kPreferredBleMtu); + if (mtuResult == 0) { + LOG_INFO("BLE MTU request set to %u", kPreferredBleMtu); + } else { + LOG_WARN("Unable to request MTU %u, rc=%d", kPreferredBleMtu, mtuResult); + } + + int phyResult = ble_gap_set_prefered_default_le_phy(BLE_GAP_LE_PHY_2M_MASK, BLE_GAP_LE_PHY_2M_MASK); + if (phyResult == 0) { + LOG_INFO("BLE default PHY preference set to 2M"); + } else { + LOG_WARN("Failed to prefer 2M PHY by default, rc=%d", phyResult); + } + + int dataLenResult = ble_gap_write_sugg_def_data_len(kPreferredBleTxOctets, kPreferredBleTxTimeUs); + if (dataLenResult == 0) { + LOG_INFO("BLE suggested data length set to %u bytes", kPreferredBleTxOctets); + } else { + LOG_WARN("Failed to raise suggested data length (%u/%u), rc=%d", kPreferredBleTxOctets, kPreferredBleTxTimeUs, + dataLenResult); + } +#endif + if (config.bluetooth.mode != meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN) { NimBLEDevice::setSecurityAuth(BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM | BLE_SM_PAIR_AUTHREQ_SC); NimBLEDevice::setSecurityInitKey(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID); @@ -353,6 +847,7 @@ void NimbleBluetooth::setupService() // Define the characteristics that the app is looking for if (config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN) { ToRadioCharacteristic = bleService->createCharacteristic(TORADIO_UUID, NIMBLE_PROPERTY::WRITE); + // Allow notifications so phones can stream FromRadio without polling. FromRadioCharacteristic = bleService->createCharacteristic(FROMRADIO_UUID, NIMBLE_PROPERTY::READ); fromNumCharacteristic = bleService->createCharacteristic(FROMNUM_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ); logRadioCharacteristic = @@ -471,4 +966,4 @@ void clearNVS() ESP.restart(); #endif } -#endif \ No newline at end of file +#endif diff --git a/variants/esp32/diy/hydra/variant.h b/variants/esp32/diy/hydra/variant.h index 0d64c1b5e..e5c10e26b 100644 --- a/variants/esp32/diy/hydra/variant.h +++ b/variants/esp32/diy/hydra/variant.h @@ -6,7 +6,6 @@ #define GPS_TX_PIN 15 #define GPS_RX_PIN 12 #define PIN_GPS_EN 4 -#define GPS_POWER_TOGGLE // Moved definition from platformio.ini to here #define BUTTON_PIN 39 // The middle button GPIO on the T-Beam // Note: On the ESP32 base version, gpio34-39 are input-only, and do not have internal pull-ups. diff --git a/variants/esp32/heltec_v2.1/platformio.ini b/variants/esp32/heltec_v2.1/platformio.ini index 763f9764c..4dcd9e583 100644 --- a/variants/esp32/heltec_v2.1/platformio.ini +++ b/variants/esp32/heltec_v2.1/platformio.ini @@ -7,4 +7,3 @@ build_flags = ${esp32_base.build_flags} -D HELTEC_V2_1 -I variants/esp32/heltec_v2.1 - -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. diff --git a/variants/esp32/heltec_wsl_v2.1/platformio.ini b/variants/esp32/heltec_wsl_v2.1/platformio.ini index eb44c88d2..6a77cf11b 100644 --- a/variants/esp32/heltec_wsl_v2.1/platformio.ini +++ b/variants/esp32/heltec_wsl_v2.1/platformio.ini @@ -6,4 +6,3 @@ build_flags = ${esp32_base.build_flags} -D PRIVATE_HW -I variants/esp32/heltec_wsl_v2.1 - -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. diff --git a/variants/esp32/tbeam/platformio.ini b/variants/esp32/tbeam/platformio.ini index ea17751c6..e53f22d30 100644 --- a/variants/esp32/tbeam/platformio.ini +++ b/variants/esp32/tbeam/platformio.ini @@ -10,7 +10,6 @@ build_flags = ${esp32_base.build_flags} -D TBEAM_V10 -I variants/esp32/tbeam - -DGPS_POWER_TOGGLE ; comment this line to disable double press function on the user button to turn off gps entirely. -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue upload_speed = 921600 diff --git a/variants/esp32/tlora_v2_1_16/platformio.ini b/variants/esp32/tlora_v2_1_16/platformio.ini index bd85aa847..8d5bdab9e 100644 --- a/variants/esp32/tlora_v2_1_16/platformio.ini +++ b/variants/esp32/tlora_v2_1_16/platformio.ini @@ -4,5 +4,13 @@ board = ttgo-lora32-v21 board_check = true build_flags = ${esp32_base.build_flags} -D TLORA_V2_1_16 -I variants/esp32/tlora_v2_1_16 - -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. upload_speed = 115200 + +[env:sugarcube] +extends = env:tlora-v2-1-1_6 +board_level = extra +build_flags = + ${env:tlora-v2-1-1_6.build_flags} + -DBUTTON_PIN=0 + -DPIN_BUZZER=25 + -DLED_PIN=-1 \ No newline at end of file diff --git a/variants/esp32/tlora_v2_1_16/variant.h b/variants/esp32/tlora_v2_1_16/variant.h index 48c069ab7..9584dd68b 100644 --- a/variants/esp32/tlora_v2_1_16/variant.h +++ b/variants/esp32/tlora_v2_1_16/variant.h @@ -8,7 +8,11 @@ #define I2C_SDA 21 // I2C pins for this board #define I2C_SCL 22 +#if defined(LED_PIN) && LED_PIN == -1 +#undef LED_PIN +#else #define LED_PIN 25 // If defined we will blink this LED +#endif #define USE_RF95 #define LORA_DIO0 26 // a No connect on the SX1262 module diff --git a/variants/esp32/tlora_v2_1_16_tcxo/platformio.ini b/variants/esp32/tlora_v2_1_16_tcxo/platformio.ini index 9404faa02..a6b9d2254 100644 --- a/variants/esp32/tlora_v2_1_16_tcxo/platformio.ini +++ b/variants/esp32/tlora_v2_1_16_tcxo/platformio.ini @@ -6,6 +6,5 @@ build_flags = ${esp32_base.build_flags} -D TLORA_V2_1_16 -I variants/esp32/tlora_v2_1_16 - -D GPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. -D LORA_TCXO_GPIO=33 upload_speed = 115200 \ No newline at end of file diff --git a/variants/esp32/tlora_v3_3_0_tcxo/platformio.ini b/variants/esp32/tlora_v3_3_0_tcxo/platformio.ini index f1110386e..1258fd8b7 100644 --- a/variants/esp32/tlora_v3_3_0_tcxo/platformio.ini +++ b/variants/esp32/tlora_v3_3_0_tcxo/platformio.ini @@ -5,6 +5,5 @@ build_flags = ${esp32_base.build_flags} -D TLORA_V2_1_16 -I variants/esp32/tlora_v2_1_16 - -D GPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. -D LORA_TCXO_GPIO=12 -D BUTTON_PIN=0 \ No newline at end of file diff --git a/variants/esp32s3/CDEBYTE_EoRa-S3/platformio.ini b/variants/esp32s3/CDEBYTE_EoRa-S3/platformio.ini index dbd420f04..3fcfbf281 100644 --- a/variants/esp32s3/CDEBYTE_EoRa-S3/platformio.ini +++ b/variants/esp32s3/CDEBYTE_EoRa-S3/platformio.ini @@ -5,4 +5,3 @@ build_flags = ${esp32s3_base.build_flags} -D CDEBYTE_EORA_S3 -I variants/esp32s3/CDEBYTE_EoRa-S3 - -D GPS_POWER_TOGGLE diff --git a/variants/esp32s3/crowpanel-esp32s3-5-epaper/platformio.ini b/variants/esp32s3/crowpanel-esp32s3-5-epaper/platformio.ini index 49e84bf4f..eed21a412 100644 --- a/variants/esp32s3/crowpanel-esp32s3-5-epaper/platformio.ini +++ b/variants/esp32s3/crowpanel-esp32s3-5-epaper/platformio.ini @@ -16,7 +16,6 @@ build_flags = -I variants/esp32s3/crowpanel-esp32s3-5-epaper -D PRIVATE_HW -DBOARD_HAS_PSRAM - -DGPS_POWER_TOGGLE -DEINK_DISPLAY_MODEL=GxEPD2_579_GDEY0579T93 ;https://www.good-display.com/product/439.html -DEINK_WIDTH=792 -DEINK_HEIGHT=272 @@ -46,7 +45,6 @@ build_flags = -I variants/esp32s3/crowpanel-esp32s3-5-epaper -D PRIVATE_HW -DBOARD_HAS_PSRAM - -DGPS_POWER_TOGGLE -DEINK_DISPLAY_MODEL=GxEPD2_420_GYE042A87 ; similar Panel: GDEY042T81 : https://www.good-display.com/product/386.html -DEINK_WIDTH=400 -DEINK_HEIGHT=300 @@ -76,7 +74,6 @@ build_flags = -I variants/esp32s3/crowpanel-esp32s3-5-epaper -D PRIVATE_HW -DBOARD_HAS_PSRAM - -DGPS_POWER_TOGGLE -DEINK_DISPLAY_MODEL=GxEPD2_290_GDEY029T94 ;https://www.good-display.com/product/389.html -DEINK_WIDTH=296 -DEINK_HEIGHT=128 diff --git a/variants/esp32s3/heltec_capsule_sensor_v3/platformio.ini b/variants/esp32s3/heltec_capsule_sensor_v3/platformio.ini index d43ffd0df..0bb21581a 100644 --- a/variants/esp32s3/heltec_capsule_sensor_v3/platformio.ini +++ b/variants/esp32s3/heltec_capsule_sensor_v3/platformio.ini @@ -6,5 +6,4 @@ board_build.partitions = default_8MB.csv build_flags = ${esp32s3_base.build_flags} -I variants/esp32s3/heltec_capsule_sensor_v3 -D HELTEC_CAPSULE_SENSOR_V3 - -D GPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. ;-D DEBUG_DISABLED ; uncomment this line to disable DEBUG output diff --git a/variants/esp32s3/heltec_v3/platformio.ini b/variants/esp32s3/heltec_v3/platformio.ini index b521e11ca..af0854e49 100644 --- a/variants/esp32s3/heltec_v3/platformio.ini +++ b/variants/esp32s3/heltec_v3/platformio.ini @@ -8,4 +8,3 @@ build_flags = ${esp32s3_base.build_flags} -D HELTEC_V3 -I variants/esp32s3/heltec_v3 - -D GPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. diff --git a/variants/esp32s3/heltec_v4/platformio.ini b/variants/esp32s3/heltec_v4/platformio.ini index d0a250ad3..7057f9646 100644 --- a/variants/esp32s3/heltec_v4/platformio.ini +++ b/variants/esp32s3/heltec_v4/platformio.ini @@ -7,4 +7,3 @@ build_flags = ${esp32s3_base.build_flags} -D HELTEC_V4 -I variants/esp32s3/heltec_v4 - -D GPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. diff --git a/variants/esp32s3/heltec_wireless_tracker/platformio.ini b/variants/esp32s3/heltec_wireless_tracker/platformio.ini index 2faba45a8..3a373bf4f 100644 --- a/variants/esp32s3/heltec_wireless_tracker/platformio.ini +++ b/variants/esp32s3/heltec_wireless_tracker/platformio.ini @@ -8,7 +8,6 @@ build_flags = ${esp32s3_base.build_flags} -I variants/esp32s3/heltec_wireless_tracker -D HELTEC_TRACKER_V1_1 - -D GPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. ;-D DEBUG_DISABLED ; uncomment this line to disable DEBUG output lib_deps = diff --git a/variants/esp32s3/heltec_wireless_tracker_V1_0/platformio.ini b/variants/esp32s3/heltec_wireless_tracker_V1_0/platformio.ini index 89fe4b385..cd961533d 100644 --- a/variants/esp32s3/heltec_wireless_tracker_V1_0/platformio.ini +++ b/variants/esp32s3/heltec_wireless_tracker_V1_0/platformio.ini @@ -8,7 +8,6 @@ build_flags = ${esp32s3_base.build_flags} -I variants/esp32s3/heltec_wireless_tracker_V1_0 -D HELTEC_TRACKER_V1_0 - -D GPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. ;-D DEBUG_DISABLED ; uncomment this line to disable DEBUG output lib_deps = ${esp32s3_base.lib_deps} diff --git a/variants/esp32s3/heltec_wireless_tracker_v2/platformio.ini b/variants/esp32s3/heltec_wireless_tracker_v2/platformio.ini index 4872561db..0f9265f91 100644 --- a/variants/esp32s3/heltec_wireless_tracker_v2/platformio.ini +++ b/variants/esp32s3/heltec_wireless_tracker_v2/platformio.ini @@ -8,7 +8,6 @@ build_flags = ${esp32s3_base.build_flags} -I variants/esp32s3/heltec_wireless_tracker_v2 -D HELTEC_WIRELESS_TRACKER_V2 - -D GPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. lib_deps = ${esp32s3_base.lib_deps} lovyan03/LovyanGFX@^1.2.0 diff --git a/variants/esp32s3/heltec_wsl_v3/platformio.ini b/variants/esp32s3/heltec_wsl_v3/platformio.ini index 06cde2304..c038a463e 100644 --- a/variants/esp32s3/heltec_wsl_v3/platformio.ini +++ b/variants/esp32s3/heltec_wsl_v3/platformio.ini @@ -7,4 +7,3 @@ build_flags = ${esp32s3_base.build_flags} -D HELTEC_WSL_V3 -I variants/esp32s3/heltec_wsl_v3 - -D GPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. diff --git a/variants/esp32s3/link32_s3_v1/platformio.ini b/variants/esp32s3/link32_s3_v1/platformio.ini index c1b71b3b5..8d88075c4 100644 --- a/variants/esp32s3/link32_s3_v1/platformio.ini +++ b/variants/esp32s3/link32_s3_v1/platformio.ini @@ -5,7 +5,6 @@ build_flags = ${esp32_base.build_flags} -D LINK_32 -I variants/esp32s3/link32_s3_v1 - -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. -DARDUINO_USB_CDC_ON_BOOT -DARDUINO_USB_MODE=1 -DRADIOLIB_EXCLUDE_SX128X=1 diff --git a/variants/esp32s3/t-deck-pro/platformio.ini b/variants/esp32s3/t-deck-pro/platformio.ini index 45c3ae4ea..0b3c7843a 100644 --- a/variants/esp32s3/t-deck-pro/platformio.ini +++ b/variants/esp32s3/t-deck-pro/platformio.ini @@ -7,7 +7,6 @@ upload_protocol = esptool build_flags = ${esp32_base.build_flags} -I variants/esp32s3/t-deck-pro -D T_DECK_PRO - -D GPS_POWER_TOGGLE -D USE_EINK -D EINK_DISPLAY_MODEL=GxEPD2_310_GDEQ031T10 -D EINK_WIDTH=240 diff --git a/variants/esp32s3/t-deck/platformio.ini b/variants/esp32s3/t-deck/platformio.ini index 7c8070c3e..9ab0756d1 100644 --- a/variants/esp32s3/t-deck/platformio.ini +++ b/variants/esp32s3/t-deck/platformio.ini @@ -9,7 +9,6 @@ upload_protocol = esptool build_flags = ${esp32s3_base.build_flags} -D T_DECK -D BOARD_HAS_PSRAM - -D GPS_POWER_TOGGLE -I variants/esp32s3/t-deck lib_deps = ${esp32s3_base.lib_deps} diff --git a/variants/esp32s3/t-eth-elite/platformio.ini b/variants/esp32s3/t-eth-elite/platformio.ini index 6107185ce..1a5823bc3 100644 --- a/variants/esp32s3/t-eth-elite/platformio.ini +++ b/variants/esp32s3/t-eth-elite/platformio.ini @@ -9,7 +9,6 @@ build_flags = -D T_ETH_ELITE -D HAS_UDP_MULTICAST=1 -I variants/esp32s3/t-eth-elite - -D GPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. lib_ignore = Ethernet diff --git a/variants/esp32s3/tlora-pager/platformio.ini b/variants/esp32s3/tlora-pager/platformio.ini index 9800161bb..d63537904 100644 --- a/variants/esp32s3/tlora-pager/platformio.ini +++ b/variants/esp32s3/tlora-pager/platformio.ini @@ -10,7 +10,6 @@ build_flags = ${esp32s3_base.build_flags} -I variants/esp32s3/tlora-pager -D T_LORA_PAGER -D BOARD_HAS_PSRAM - -D GPS_POWER_TOGGLE -D HAS_SDCARD -D SDCARD_USE_SPI1 -D ENABLE_ROTARY_PULLUP diff --git a/variants/esp32s3/tlora_t3s3_epaper/platformio.ini b/variants/esp32s3/tlora_t3s3_epaper/platformio.ini index 71644ee77..eca052f57 100644 --- a/variants/esp32s3/tlora_t3s3_epaper/platformio.ini +++ b/variants/esp32s3/tlora_t3s3_epaper/platformio.ini @@ -8,7 +8,6 @@ build_flags = ${esp32_base.build_flags} -D TLORA_T3S3_EPAPER -I variants/esp32s3/tlora_t3s3_epaper - -DGPS_POWER_TOGGLE -DUSE_EINK -DEINK_DISPLAY_MODEL=GxEPD2_213_BN -DEINK_WIDTH=250 diff --git a/variants/esp32s3/tlora_t3s3_v1/platformio.ini b/variants/esp32s3/tlora_t3s3_v1/platformio.ini index d9624f043..56ece0d62 100644 --- a/variants/esp32s3/tlora_t3s3_v1/platformio.ini +++ b/variants/esp32s3/tlora_t3s3_v1/platformio.ini @@ -6,4 +6,3 @@ upload_protocol = esptool build_flags = ${esp32_base.build_flags} -D TLORA_T3S3_V1 -I variants/esp32s3/tlora_t3s3_v1 - -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. diff --git a/variants/esp32s3/tracksenger/platformio.ini b/variants/esp32s3/tracksenger/platformio.ini index 0e9f08541..213a917b1 100644 --- a/variants/esp32s3/tracksenger/platformio.ini +++ b/variants/esp32s3/tracksenger/platformio.ini @@ -8,7 +8,6 @@ build_flags = ${esp32s3_base.build_flags} -I variants/esp32s3/tracksenger/internal -D HELTEC_TRACKER_V1_1 - -D GPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. ;-D DEBUG_DISABLED ; uncomment this line to disable DEBUG output lib_deps = @@ -25,7 +24,6 @@ build_flags = ${esp32s3_base.build_flags} -I variants/esp32s3/tracksenger/lcd -D HELTEC_TRACKER_V1_1 - -D GPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. ;-D DEBUG_DISABLED ; uncomment this line to disable DEBUG output lib_deps = @@ -42,5 +40,4 @@ build_flags = ${esp32s3_base.build_flags} -I variants/esp32s3/tracksenger/oled -D HELTEC_TRACKER_V1_1 - -D GPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. ;-D DEBUG_DISABLED ; uncomment this line to disable DEBUG output diff --git a/variants/native/portduino/platformio.ini b/variants/native/portduino/platformio.ini index 4e6a592de..49a8a71c7 100644 --- a/variants/native/portduino/platformio.ini +++ b/variants/native/portduino/platformio.ini @@ -17,6 +17,7 @@ extends = native_base build_flags = ${native_base.build_flags} !pkg-config --libs libulfius --silence-errors || : !pkg-config --libs openssl --silence-errors || : + !pkg-config --cflags --libs sdl2 --silence-errors || : [env:native-tft] extends = native_base diff --git a/variants/nrf52840/ELECROW-ThinkNode-M1/platformio.ini b/variants/nrf52840/ELECROW-ThinkNode-M1/platformio.ini index 0578bcfe8..f89b05d1f 100644 --- a/variants/nrf52840/ELECROW-ThinkNode-M1/platformio.ini +++ b/variants/nrf52840/ELECROW-ThinkNode-M1/platformio.ini @@ -9,7 +9,6 @@ debug_tool = jlink build_flags = ${nrf52840_base.build_flags} -Ivariants/nrf52840/ELECROW-ThinkNode-M1 -DELECROW_ThinkNode_M1 - -DGPS_POWER_TOGGLE -DUSE_EINK -DEINK_DISPLAY_MODEL=GxEPD2_154_D67 -DEINK_WIDTH=200 diff --git a/variants/nrf52840/ME25LS01-4Y10TD/platformio.ini b/variants/nrf52840/ME25LS01-4Y10TD/platformio.ini index 89a45694c..1279f12c6 100644 --- a/variants/nrf52840/ME25LS01-4Y10TD/platformio.ini +++ b/variants/nrf52840/ME25LS01-4Y10TD/platformio.ini @@ -8,7 +8,6 @@ build_flags = ${nrf52840_base.build_flags} -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -DME25LS01_4Y10TD - -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/ME25LS01-4Y10TD> lib_deps = diff --git a/variants/nrf52840/ME25LS01-4Y10TD_e-ink/platformio.ini b/variants/nrf52840/ME25LS01-4Y10TD_e-ink/platformio.ini index ad5867bd5..f8d6da008 100644 --- a/variants/nrf52840/ME25LS01-4Y10TD_e-ink/platformio.ini +++ b/variants/nrf52840/ME25LS01-4Y10TD_e-ink/platformio.ini @@ -8,7 +8,6 @@ build_flags = ${nrf52840_base.build_flags} -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -DME25LS01_4Y10TD - -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. -DEINK_DISPLAY_MODEL=GxEPD2_420_GDEY042T81 -DEINK_WIDTH=400 -DEINK_HEIGHT=300 diff --git a/variants/nrf52840/MS24SF1/platformio.ini b/variants/nrf52840/MS24SF1/platformio.ini index f162cbd60..df15b5605 100644 --- a/variants/nrf52840/MS24SF1/platformio.ini +++ b/variants/nrf52840/MS24SF1/platformio.ini @@ -7,7 +7,6 @@ build_flags = ${nrf52840_base.build_flags} -Ivariants/nrf52840/MS24SF1 -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 - -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/MS24SF1> lib_deps = diff --git a/variants/nrf52840/diy/nrf52_promicro_diy_tcxo/variant.h b/variants/nrf52840/diy/nrf52_promicro_diy_tcxo/variant.h index e93442c7e..87342a02f 100644 --- a/variants/nrf52840/diy/nrf52_promicro_diy_tcxo/variant.h +++ b/variants/nrf52840/diy/nrf52_promicro_diy_tcxo/variant.h @@ -94,7 +94,6 @@ NRF52 PRO MICRO PIN ASSIGNMENT #define PIN_GPS_RX (0 + 20) // P0.20 #define PIN_GPS_EN (0 + 24) // P0.24 -#define GPS_POWER_TOGGLE #define GPS_UBLOX // define GPS_DEBUG diff --git a/variants/nrf52840/diy/nrf52_promicro_diy_xtal/variant.h b/variants/nrf52840/diy/nrf52_promicro_diy_xtal/variant.h index 7aafab7da..6e208e79f 100644 --- a/variants/nrf52840/diy/nrf52_promicro_diy_xtal/variant.h +++ b/variants/nrf52840/diy/nrf52_promicro_diy_xtal/variant.h @@ -93,7 +93,6 @@ NRF52 PRO MICRO PIN ASSIGNMENT #define PIN_GPS_RX (0 + 20) // P0.20 #define PIN_GPS_EN (0 + 24) // P0.24 -#define GPS_POWER_TOGGLE #define GPS_UBLOX // define GPS_DEBUG diff --git a/variants/nrf52840/gat562_mesh_trial_tracker/platformio.ini b/variants/nrf52840/gat562_mesh_trial_tracker/platformio.ini index c6a5a7399..c6cd23314 100644 --- a/variants/nrf52840/gat562_mesh_trial_tracker/platformio.ini +++ b/variants/nrf52840/gat562_mesh_trial_tracker/platformio.ini @@ -8,7 +8,6 @@ build_flags = ${nrf52840_base.build_flags} -I variants/nrf52840/gat562_mesh_trial_tracker ;-D GAT562_MESH_TRIAL_TRACKER -D PRIVATE_HW - -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. -DRADIOLIB_EXCLUDE_SX128X=1 -DRADIOLIB_EXCLUDE_SX127X=1 -DRADIOLIB_EXCLUDE_LR11X0=1 diff --git a/variants/nrf52840/heltec_mesh_node_t114/platformio.ini b/variants/nrf52840/heltec_mesh_node_t114/platformio.ini index c7b30b339..c49dadd56 100644 --- a/variants/nrf52840/heltec_mesh_node_t114/platformio.ini +++ b/variants/nrf52840/heltec_mesh_node_t114/platformio.ini @@ -8,7 +8,6 @@ debug_tool = jlink # add -DCFG_SYSVIEW if you want to use the Segger systemview tool for OS profiling. build_flags = ${nrf52840_base.build_flags} -Ivariants/nrf52840/heltec_mesh_node_t114 - -DGPS_POWER_TOGGLE -DHELTEC_T114 build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/heltec_mesh_node_t114> diff --git a/variants/nrf52840/heltec_mesh_solar/platformio.ini b/variants/nrf52840/heltec_mesh_solar/platformio.ini index 625dd1d43..36a7904d6 100644 --- a/variants/nrf52840/heltec_mesh_solar/platformio.ini +++ b/variants/nrf52840/heltec_mesh_solar/platformio.ini @@ -8,7 +8,6 @@ debug_tool = jlink # add -DCFG_SYSVIEW if you want to use the Segger systemview tool for OS profiling. build_flags = ${nrf52840_base.build_flags} -Ivariants/nrf52840/heltec_mesh_solar - -DGPS_POWER_TOGGLE -DHELTEC_MESH_SOLAR build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/heltec_mesh_solar> diff --git a/variants/nrf52840/meshlink/platformio.ini b/variants/nrf52840/meshlink/platformio.ini index 466362242..2a4e27fe8 100644 --- a/variants/nrf52840/meshlink/platformio.ini +++ b/variants/nrf52840/meshlink/platformio.ini @@ -9,7 +9,6 @@ board_level = extra build_flags = ${nrf52840_base.build_flags} -I variants/nrf52840/meshlink -D MESHLINK - -D GPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. -D EINK_DISPLAY_MODEL=GxEPD2_213_B74 -D EINK_WIDTH=250 -D EINK_HEIGHT=122 diff --git a/variants/nrf52840/meshlink_eink/platformio.ini b/variants/nrf52840/meshlink_eink/platformio.ini index af5a0040e..c0c0cb1dd 100644 --- a/variants/nrf52840/meshlink_eink/platformio.ini +++ b/variants/nrf52840/meshlink_eink/platformio.ini @@ -9,7 +9,6 @@ board_level = extra build_flags = ${nrf52840_base.build_flags} -I variants/nrf52840/meshlink_eink -D MESHLINK - -D GPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. -D EINK_DISPLAY_MODEL=GxEPD2_213_B74 -D EINK_WIDTH=250 -D EINK_HEIGHT=122 diff --git a/variants/nrf52840/meshtiny/variant.h b/variants/nrf52840/meshtiny/variant.h index d1139b3be..55aabe930 100644 --- a/variants/nrf52840/meshtiny/variant.h +++ b/variants/nrf52840/meshtiny/variant.h @@ -19,7 +19,9 @@ #ifndef _VARIANT_MESHTINY_ #define _VARIANT_MESHTINY_ +#ifndef MESHTINY #define MESHTINY +#endif /** Master clock frequency */ #define VARIANT_MCK (64000000ul) diff --git a/variants/nrf52840/r1-neo/platformio.ini b/variants/nrf52840/r1-neo/platformio.ini index 6feb55dc9..60f1f6ae1 100644 --- a/variants/nrf52840/r1-neo/platformio.ini +++ b/variants/nrf52840/r1-neo/platformio.ini @@ -6,7 +6,6 @@ board_check = true build_flags = ${nrf52840_base.build_flags} -Ivariants/nrf52840/r1-neo -D R1_NEO - -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. -DRADIOLIB_EXCLUDE_SX128X=1 -DRADIOLIB_EXCLUDE_SX127X=1 -DRADIOLIB_EXCLUDE_LR11X0=1 diff --git a/variants/nrf52840/rak2560/platformio.ini b/variants/nrf52840/rak2560/platformio.ini index edc648b9b..021e6d03b 100644 --- a/variants/nrf52840/rak2560/platformio.ini +++ b/variants/nrf52840/rak2560/platformio.ini @@ -6,7 +6,6 @@ board_check = true build_flags = ${nrf52840_base.build_flags} -I variants/nrf52840/rak2560 -D RAK_4631 - -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. -DRADIOLIB_EXCLUDE_SX128X=1 -DRADIOLIB_EXCLUDE_SX127X=1 -DRADIOLIB_EXCLUDE_LR11X0=1 diff --git a/variants/nrf52840/rak3401_1watt/platformio.ini b/variants/nrf52840/rak3401_1watt/platformio.ini new file mode 100644 index 000000000..1a915a6b3 --- /dev/null +++ b/variants/nrf52840/rak3401_1watt/platformio.ini @@ -0,0 +1,31 @@ +; The very slick RAK wireless RAK 4631 / 4630 board - Unified firmware for 5005/19003, with or without OLED RAK 1921 +[env:rak3401-1watt] +extends = nrf52840_base +board = wiscore_rak4631 +board_check = true +build_flags = ${nrf52840_base.build_flags} + -Ivariants/nrf52840/rak3401_1watt + -D RAK_4631 +; -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. + -D RAK3401 + -D RAK13302 ; RAK 1Watt Power Amplifier + -DRADIOLIB_EXCLUDE_SX128X=1 + -DRADIOLIB_EXCLUDE_SX127X=1 + -DRADIOLIB_EXCLUDE_LR11X0=1 +build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/rak3401_1watt> + +lib_deps = + ${nrf52840_base.lib_deps} + ${networking_base.lib_deps} + melopero/Melopero RV3028@^1.1.0 + rakwireless/RAKwireless NCP5623 RGB LED library@^1.0.2 + beegee-tokyo/RAK12035_SoilMoisture@^1.0.4 + https://github.com/RAKWireless/RAK12034-BMX160/archive/dcead07ffa267d3c906e9ca4a1330ab989e957e2.zip + +; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) +; Note: as of 6/2013 the serial/bootloader based programming takes approximately 30 seconds +;upload_protocol = jlink + +; Allows programming and debug via the RAK NanoDAP as the default debugger tool for the RAK4631 (it is only $10!) +; programming time is about the same as the bootloader version. +; For information on this see the meshtastic developers documentation for "Development on the NRF52" + diff --git a/variants/nrf52840/rak3401_1watt/variant.cpp b/variants/nrf52840/rak3401_1watt/variant.cpp new file mode 100644 index 000000000..e84b60b3b --- /dev/null +++ b/variants/nrf52840/rak3401_1watt/variant.cpp @@ -0,0 +1,45 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Sandeep Mistry All right reserved. + Copyright (c) 2018, Adafruit Industries (adafruit.com) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "variant.h" +#include "nrf.h" +#include "wiring_constants.h" +#include "wiring_digital.h" + +const uint32_t g_ADigitalPinMap[] = { + // P0 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + + // P1 + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + +void initVariant() +{ + // LED1 & LED2 + pinMode(PIN_LED1, OUTPUT); + ledOff(PIN_LED1); + + pinMode(PIN_LED2, OUTPUT); + ledOff(PIN_LED2); + + // 3V3 Power Rail + pinMode(PIN_3V3_EN, OUTPUT); + digitalWrite(PIN_3V3_EN, HIGH); +} diff --git a/variants/nrf52840/rak3401_1watt/variant.h b/variants/nrf52840/rak3401_1watt/variant.h new file mode 100644 index 000000000..d4bb1a175 --- /dev/null +++ b/variants/nrf52840/rak3401_1watt/variant.h @@ -0,0 +1,226 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Sandeep Mistry All right reserved. + Copyright (c) 2018, Adafruit Industries (adafruit.com) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _VARIANT_RAK3401_ +#define _VARIANT_RAK3401_ + +#define RAK4630 + +/** Master clock frequency */ +#define VARIANT_MCK (64000000ul) + +#define USE_LFXO // Board uses 32khz crystal for LF +// define USE_LFRC // Board uses RC for LF + +/*---------------------------------------------------------------------------- + * Headers + *----------------------------------------------------------------------------*/ + +#include "WVariant.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// Number of pins defined in PinDescription array +#define PINS_COUNT (48) +#define NUM_DIGITAL_PINS (48) +#define NUM_ANALOG_INPUTS (6) +#define NUM_ANALOG_OUTPUTS (0) + +// LEDs +#define PIN_LED1 (35) +#define PIN_LED2 (36) + +#define LED_BUILTIN PIN_LED1 +#define LED_CONN PIN_LED2 + +#define LED_GREEN PIN_LED1 +#define LED_BLUE PIN_LED2 + +#define LED_STATE_ON 1 // State when LED is litted + +/* + * Analog pins + */ +#define PIN_A0 (5) +#define PIN_A1 (31) +#define PIN_A2 (28) +#define PIN_A3 (29) +#define PIN_A4 (30) +#define PIN_A5 (31) +#define PIN_A6 (0xff) +#define PIN_A7 (0xff) + +static const uint8_t A0 = PIN_A0; +static const uint8_t A1 = PIN_A1; +static const uint8_t A2 = PIN_A2; +static const uint8_t A3 = PIN_A3; +static const uint8_t A4 = PIN_A4; +static const uint8_t A5 = PIN_A5; +static const uint8_t A6 = PIN_A6; +static const uint8_t A7 = PIN_A7; +#define ADC_RESOLUTION 14 + +// Other pins +#define WB_I2C1_SDA (13) // SENSOR_SLOT IO_SLOT +#define WB_I2C1_SCL (14) // SENSOR_SLOT IO_SLOT + +#define PIN_AREF (2) +#define PIN_NFC1 (9) +#define WB_IO5 PIN_NFC1 +#define WB_IO4 (4) +#define PIN_NFC2 (10) + +static const uint8_t AREF = PIN_AREF; + +/* + * Serial interfaces + */ +#define PIN_SERIAL1_RX (15) +#define PIN_SERIAL1_TX (16) + +// Connected to Jlink CDC +#define PIN_SERIAL2_RX (8) +#define PIN_SERIAL2_TX (6) + +/* + * SPI Interfaces + */ +#define SPI_INTERFACES_COUNT 2 + +#define PIN_SPI_MISO (45) +#define PIN_SPI_MOSI (44) +#define PIN_SPI_SCK (43) + +#define PIN_SPI1_MISO (29) // (0 + 29) +#define PIN_SPI1_MOSI (30) // (0 + 30) +#define PIN_SPI1_SCK (3) // (0 + 3) + +static const uint8_t SS = 42; +static const uint8_t MOSI = PIN_SPI_MOSI; +static const uint8_t MISO = PIN_SPI_MISO; +static const uint8_t SCK = PIN_SPI_SCK; + +/* + * eink display pins + */ +#define PIN_EINK_CS (0 + 26) +#define PIN_EINK_BUSY (0 + 4) +#define PIN_EINK_DC (0 + 17) +#define PIN_EINK_RES (-1) +#define PIN_EINK_SCLK (0 + 3) +#define PIN_EINK_MOSI (0 + 30) // also called SDI + +/* + * Wire Interfaces + */ +#define WIRE_INTERFACES_COUNT 1 + +#define PIN_WIRE_SDA (WB_I2C1_SDA) +#define PIN_WIRE_SCL (WB_I2C1_SCL) + +// QSPI Pins +#define PIN_QSPI_SCK 3 +#define PIN_QSPI_CS 26 +#define PIN_QSPI_IO0 30 +#define PIN_QSPI_IO1 29 +#define PIN_QSPI_IO2 28 +#define PIN_QSPI_IO3 2 + +// On-board QSPI Flash +#define EXTERNAL_FLASH_DEVICES IS25LP080D +#define EXTERNAL_FLASH_USE_QSPI + +// 1watt sx1262 RAK13302 +#define HW_SPI1_DEVICE 1 + +#define LORA_SCK PIN_SPI1_SCK +#define LORA_MISO PIN_SPI1_MISO +#define LORA_MOSI PIN_SPI1_MOSI +#define LORA_CS 26 + +#define USE_SX1262 +#define SX126X_CS (26) +#define SX126X_DIO1 (10) +#define SX126X_BUSY (9) +#define SX126X_RESET (4) + +#define SX126X_POWER_EN (21) +// DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3 +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 + +// Testing USB detection +#define NRF_APM +// If using a power chip like the INA3221 you can override the default battery voltage channel below +// and comment out NRF_APM to use the INA3221 instead of the USB detection for charging +// #define INA3221_BAT_CH INA3221_CH2 +// #define INA3221_ENV_CH INA3221_CH1 + +// enables 3.3V periphery like GPS or IO Module +// Do not toggle this for GPS power savings +#define PIN_3V3_EN (34) +#define WB_IO2 PIN_3V3_EN + +// RAK1910 GPS module +// If using the wisblock GPS module and pluged into Port A on WisBlock base +// IO1 is hooked to PPS (pin 12 on header) = gpio 17 +// IO2 is hooked to GPS RESET = gpio 34, but it can not be used to this because IO2 is ALSO used to control 3V3_S power (1 is on). +// Therefore must be 1 to keep peripherals powered +// Power is on the controllable 3V3_S rail +// #define PIN_GPS_RESET (34) +// #define PIN_GPS_EN PIN_3V3_EN +#define PIN_GPS_PPS (17) // Pulse per second input from the GPS + +#define GPS_RX_PIN PIN_SERIAL1_RX +#define GPS_TX_PIN PIN_SERIAL1_TX + +// Define pin to enable GPS toggle (set GPIO to LOW) via user button triple press + +// RAK12002 RTC Module +#define RV3028_RTC (uint8_t)0b1010010 + +// RAK18001 Buzzer in Slot C +// #define PIN_BUZZER 21 // IO3 is PWM2 +// NEW: set this via protobuf instead! + +// Battery +// The battery sense is hooked to pin A0 (5) +#define BATTERY_PIN PIN_A0 +// and has 12 bit resolution +#define BATTERY_SENSE_RESOLUTION_BITS 12 +#define BATTERY_SENSE_RESOLUTION 4096.0 +#undef AREF_VOLTAGE +#define AREF_VOLTAGE 3.0 +#define VBAT_AR_INTERNAL AR_INTERNAL_3_0 +#define ADC_MULTIPLIER 1.73 + +#define HAS_RTC 1 + +#define RAK_4631 1 + +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------- + * Arduino objects - C++ only + *----------------------------------------------------------------------------*/ + +#endif diff --git a/variants/nrf52840/rak4631/platformio.ini b/variants/nrf52840/rak4631/platformio.ini index 6bf5f44cb..205966529 100644 --- a/variants/nrf52840/rak4631/platformio.ini +++ b/variants/nrf52840/rak4631/platformio.ini @@ -7,7 +7,6 @@ board_check = true build_flags = ${nrf52840_base.build_flags} -I variants/nrf52840/rak4631 -D RAK_4631 - -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. -DEINK_DISPLAY_MODEL=GxEPD2_213_BN -DEINK_WIDTH=250 -DEINK_HEIGHT=122 diff --git a/variants/nrf52840/rak4631_eth_gw/platformio.ini b/variants/nrf52840/rak4631_eth_gw/platformio.ini index 4be8843a2..3c61e3498 100644 --- a/variants/nrf52840/rak4631_eth_gw/platformio.ini +++ b/variants/nrf52840/rak4631_eth_gw/platformio.ini @@ -6,7 +6,6 @@ board_check = true build_flags = ${nrf52840_base.build_flags} -I variants/nrf52840/rak4631_eth_gw -D RAK_4631 - -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. -DHAS_UDP_MULTICAST=1 -DEINK_DISPLAY_MODEL=GxEPD2_213_BN -DEINK_WIDTH=250 diff --git a/variants/nrf52840/rak4631_nomadstar_meteor_pro/platformio.ini b/variants/nrf52840/rak4631_nomadstar_meteor_pro/platformio.ini index e94eef1ee..d7dab2678 100644 --- a/variants/nrf52840/rak4631_nomadstar_meteor_pro/platformio.ini +++ b/variants/nrf52840/rak4631_nomadstar_meteor_pro/platformio.ini @@ -6,7 +6,6 @@ board_check = true build_flags = ${nrf52840_base.build_flags} -I variants/nrf52840/rak4631_nomadstar_meteor_pro -D NOMADSTAR_METEOR_PRO - ;-DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. -DEINK_DISPLAY_MODEL=GxEPD2_213_BN -DEINK_WIDTH=250 -DEINK_HEIGHT=122 diff --git a/variants/nrf52840/rak_wismeshtag/platformio.ini b/variants/nrf52840/rak_wismeshtag/platformio.ini index 08e723302..f04d1f186 100644 --- a/variants/nrf52840/rak_wismeshtag/platformio.ini +++ b/variants/nrf52840/rak_wismeshtag/platformio.ini @@ -7,7 +7,6 @@ build_flags = ${nrf52840_base.build_flags} -I variants/nrf52840/rak_wismeshtag -D WISMESH_TAG -D RAK_4631 - -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. -DRADIOLIB_EXCLUDE_SX128X=1 -DRADIOLIB_EXCLUDE_SX127X=1 -DRADIOLIB_EXCLUDE_LR11X0=1 diff --git a/variants/nrf52840/rak_wismeshtap/platformio.ini b/variants/nrf52840/rak_wismeshtap/platformio.ini index adf301537..3369f9c77 100644 --- a/variants/nrf52840/rak_wismeshtap/platformio.ini +++ b/variants/nrf52840/rak_wismeshtap/platformio.ini @@ -6,7 +6,6 @@ build_flags = ${nrf52840_base.build_flags} -Ivariants/nrf52840/rak_wismeshtap -DWISMESH_TAP -DRAK_4631 - -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. -DEINK_DISPLAY_MODEL=GxEPD2_213_BN -DEINK_WIDTH=250 -DEINK_HEIGHT=122 diff --git a/variants/nrf52840/t-echo-lite/platformio.ini b/variants/nrf52840/t-echo-lite/platformio.ini index 68ae59dcb..90e6487a7 100644 --- a/variants/nrf52840/t-echo-lite/platformio.ini +++ b/variants/nrf52840/t-echo-lite/platformio.ini @@ -9,7 +9,6 @@ debug_tool = jlink build_flags = ${nrf52840_base.build_flags} -Ivariants/nrf52840/t-echo-lite -D T_ECHO_LITE - -D GPS_POWER_TOGGLE -D EINK_DISPLAY_MODEL=GxEPD2_122_T61 -D EINK_WIDTH=192 -D EINK_HEIGHT=176 diff --git a/variants/nrf52840/t-echo/platformio.ini b/variants/nrf52840/t-echo/platformio.ini index 6541c9796..051fb3099 100644 --- a/variants/nrf52840/t-echo/platformio.ini +++ b/variants/nrf52840/t-echo/platformio.ini @@ -9,7 +9,6 @@ debug_tool = jlink # add -DCFG_SYSVIEW if you want to use the Segger systemview tool for OS profiling. build_flags = ${nrf52840_base.build_flags} -Ivariants/nrf52840/t-echo - -DGPS_POWER_TOGGLE -DEINK_DISPLAY_MODEL=GxEPD2_154_D67 -DEINK_WIDTH=200 -DEINK_HEIGHT=200 diff --git a/variants/nrf52840/tracker-t1000-e/platformio.ini b/variants/nrf52840/tracker-t1000-e/platformio.ini index c6c3f269c..905d751fd 100644 --- a/variants/nrf52840/tracker-t1000-e/platformio.ini +++ b/variants/nrf52840/tracker-t1000-e/platformio.ini @@ -7,7 +7,6 @@ build_flags = ${nrf52840_base.build_flags} -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -DTRACKER_T1000_E - -DGPS_POWER_TOGGLE -DMESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR_EXTERNAL=1 -DMESHTASTIC_EXCLUDE_CANNEDMESSAGES=1 -DMESHTASTIC_EXCLUDE_SCREEN=1 diff --git a/variants/nrf52840/wio-sdk-wm1110/platformio.ini b/variants/nrf52840/wio-sdk-wm1110/platformio.ini index 2c65246b8..028129783 100644 --- a/variants/nrf52840/wio-sdk-wm1110/platformio.ini +++ b/variants/nrf52840/wio-sdk-wm1110/platformio.ini @@ -14,7 +14,6 @@ build_flags = ${nrf52840_base.build_flags} -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -DWIO_WM1110 - -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. -DCFG_TUD_CDC=0 board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/wio-sdk-wm1110> diff --git a/variants/nrf52840/wio-t1000-s/platformio.ini b/variants/nrf52840/wio-t1000-s/platformio.ini index 3594bcf07..c6b61fc8a 100644 --- a/variants/nrf52840/wio-t1000-s/platformio.ini +++ b/variants/nrf52840/wio-t1000-s/platformio.ini @@ -8,7 +8,6 @@ build_flags = ${nrf52840_base.build_flags} -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -DWIO_WM1110 - -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/wio-t1000-s> lib_deps = diff --git a/variants/nrf52840/wio-tracker-wm1110/platformio.ini b/variants/nrf52840/wio-tracker-wm1110/platformio.ini index b383043bb..73b7dedd4 100644 --- a/variants/nrf52840/wio-tracker-wm1110/platformio.ini +++ b/variants/nrf52840/wio-tracker-wm1110/platformio.ini @@ -7,7 +7,6 @@ build_flags = ${nrf52840_base.build_flags} -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -DWIO_WM1110 - -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/wio-tracker-wm1110> lib_deps = diff --git a/version.properties b/version.properties index 5c84a8e65..f33f0f1cb 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 7 -build = 12 +build = 13