diff --git a/.github/meshtastic_logo.png b/.github/meshtastic_logo.png new file mode 100644 index 000000000..11c5db18c Binary files /dev/null and b/.github/meshtastic_logo.png differ diff --git a/.github/workflows/build_docker.yml b/.github/workflows/build_docker.yml deleted file mode 100644 index 18787f16a..000000000 --- a/.github/workflows/build_docker.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: Build Docker - -on: workflow_call - -permissions: - contents: write - packages: write - -jobs: - build-native: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - submodules: recursive - ref: ${{github.event.pull_request.head.ref}} - repository: ${{github.event.pull_request.head.repo.full_name}} - - - name: Get release version string - run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT - id: version - - - name: Docker login - if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }} - uses: docker/login-action@v3 - with: - username: meshtastic - password: ${{ secrets.DOCKER_FIRMWARE_TOKEN }} - - - name: Docker setup - if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }} - uses: docker/setup-buildx-action@v3 - - - name: Docker build and push tagged versions - if: ${{ github.event_name == 'workflow_dispatch' }} - uses: docker/build-push-action@v6 - with: - context: . - file: ./Dockerfile - push: true - tags: meshtastic/meshtasticd:${{ steps.version.outputs.long }} - - - name: Docker build and push - if: ${{ github.ref == 'refs/heads/master' && github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }} - uses: docker/build-push-action@v6 - with: - context: . - file: ./Dockerfile - push: true - tags: meshtastic/meshtasticd:latest diff --git a/.github/workflows/build_native.yml b/.github/workflows/build_native.yml deleted file mode 100644 index cca839328..000000000 --- a/.github/workflows/build_native.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: Build Native - -on: workflow_call - -permissions: - contents: write - packages: write - -jobs: - build-native: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - submodules: recursive - ref: ${{github.event.pull_request.head.ref}} - repository: ${{github.event.pull_request.head.repo.full_name}} - - - name: Setup native build - id: base - uses: ./.github/actions/setup-native - - - name: Build Native - run: bin/build-native.sh - - - name: Get release version string - run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT - id: version - - - name: Store binaries as an artifact - uses: actions/upload-artifact@v4 - with: - name: firmware-native-${{ steps.version.outputs.long }}.zip - overwrite: true - path: | - release/meshtasticd_linux_x86_64 - bin/config-dist.yaml diff --git a/.github/workflows/build_raspbian.yml b/.github/workflows/build_raspbian.yml deleted file mode 100644 index 646c6c9f3..000000000 --- a/.github/workflows/build_raspbian.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: Build Raspbian - -on: workflow_call - -permissions: - contents: write - packages: write - -jobs: - build-raspbian: - runs-on: [self-hosted, linux, ARM64] - steps: - - name: Install libbluetooth - shell: bash - run: | - sudo apt-get update -y --fix-missing - sudo apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev libusb-1.0-0-dev libi2c-dev - - - name: Checkout code - uses: actions/checkout@v4 - with: - submodules: recursive - ref: ${{github.event.pull_request.head.ref}} - repository: ${{github.event.pull_request.head.repo.full_name}} - - - name: Upgrade python tools - shell: bash - run: | - python -m pip install --upgrade pip - pip install -U platformio adafruit-nrfutil - pip install -U meshtastic --pre - - - name: Upgrade platformio - shell: bash - run: | - pio upgrade - - - name: Build Raspbian - run: bin/build-native.sh - - - name: Get release version string - run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT - id: version - - - name: Store binaries as an artifact - uses: actions/upload-artifact@v4 - with: - name: firmware-raspbian-${{ steps.version.outputs.long }}.zip - overwrite: true - path: | - release/meshtasticd_linux_aarch64 - bin/config-dist.yaml diff --git a/.github/workflows/build_raspbian_armv7l.yml b/.github/workflows/build_raspbian_armv7l.yml deleted file mode 100644 index 21b1aea79..000000000 --- a/.github/workflows/build_raspbian_armv7l.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: Build Raspbian Arm - -on: workflow_call - -permissions: - contents: write - packages: write - -jobs: - build-raspbian-armv7l: - runs-on: [self-hosted, linux, ARM] - steps: - - name: Install libbluetooth - shell: bash - run: | - sudo apt-get update -y --fix-missing - sudo apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev libusb-1.0-0-dev libi2c-dev - - - name: Checkout code - uses: actions/checkout@v4 - with: - submodules: recursive - ref: ${{github.event.pull_request.head.ref}} - repository: ${{github.event.pull_request.head.repo.full_name}} - - - name: Upgrade python tools - shell: bash - run: | - python -m pip install --upgrade pip - pip install -U platformio adafruit-nrfutil - pip install -U meshtastic --pre - - - name: Upgrade platformio - shell: bash - run: | - pio upgrade - - - name: Build Raspbian - run: bin/build-native.sh - - - name: Get release version string - run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT - id: version - - - name: Store binaries as an artifact - uses: actions/upload-artifact@v4 - with: - name: firmware-raspbian-armv7l-${{ steps.version.outputs.long }}.zip - overwrite: true - path: | - release/meshtasticd_linux_armv7l - bin/config-dist.yaml diff --git a/.github/workflows/daily_packaging.yml b/.github/workflows/daily_packaging.yml index 14daae74d..11fe2043a 100644 --- a/.github/workflows/daily_packaging.yml +++ b/.github/workflows/daily_packaging.yml @@ -20,6 +20,12 @@ permissions: packages: write jobs: + docker-multiarch: + uses: ./.github/workflows/docker_manifest.yml + with: + release_channel: daily + secrets: inherit + package-ppa: strategy: fail-fast: false @@ -34,7 +40,7 @@ jobs: package-obs: uses: ./.github/workflows/package_obs.yml with: - obs_project: home:meshtastic:daily + obs_project: network:Meshtastic:daily series: unstable secrets: inherit diff --git a/.github/workflows/docker_build.yml b/.github/workflows/docker_build.yml new file mode 100644 index 000000000..eec0785c0 --- /dev/null +++ b/.github/workflows/docker_build.yml @@ -0,0 +1,92 @@ +name: Build Docker + +# Build Docker image, push untagged (digest-only) + +on: + workflow_call: + secrets: + DOCKER_FIRMWARE_TOKEN: + required: false # Only required for push + inputs: + distro: + description: Distro to target + required: true + type: string + # choices: [debian, alpine] + platform: + description: Platform to target + required: true + type: string + runs-on: + description: Runner to use + required: true + type: string + push: + description: Push images to registry + required: false + type: boolean + default: false + outputs: + digest: + description: Digest of built image + value: ${{ jobs.docker-build.outputs.digest }} + +permissions: + contents: write + packages: write + +jobs: + docker-build: + outputs: + digest: ${{ steps.docker_variant.outputs.digest }} + runs-on: ${{ inputs.runs-on }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: recursive + ref: ${{github.event.pull_request.head.ref}} + repository: ${{github.event.pull_request.head.repo.full_name}} + + - name: Get release version string + run: | + echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT + id: version + + - name: Docker login + if: ${{ inputs.push }} + uses: docker/login-action@v3 + with: + username: meshtastic + password: ${{ secrets.DOCKER_FIRMWARE_TOKEN }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Docker setup + uses: docker/setup-buildx-action@v3 + + - name: Sanitize platform string + id: sanitize_platform + # Replace slashes with underscores + run: echo "cleaned_platform=${{ inputs.platform }}" | sed 's/\//_/g' >> $GITHUB_OUTPUT + + - name: Docker tag + id: meta + uses: docker/metadata-action@v5 + with: + images: meshtastic/meshtasticd + tags: | + GHA-${{ steps.version.outputs.long }}-${{ inputs.distro }}-${{ steps.sanitize_platform.outputs.cleaned_platform }} + flavor: latest=false + + - name: Docker build and push + uses: docker/build-push-action@v6 + id: docker_variant + with: + context: . + file: | + ${{ contains(inputs.distro, 'debian') && './Dockerfile' || contains(inputs.distro, 'alpine') && './alpine.Dockerfile' }} + push: ${{ inputs.push }} + tags: ${{ steps.meta.outputs.tags }} # Tag is only meant to be consumed by the "manifest" job + platforms: ${{ inputs.platform }} diff --git a/.github/workflows/docker_manifest.yml b/.github/workflows/docker_manifest.yml new file mode 100644 index 000000000..d1d1a5634 --- /dev/null +++ b/.github/workflows/docker_manifest.yml @@ -0,0 +1,186 @@ +name: Build Docker Multi-Arch Manifest + +on: + workflow_call: + secrets: + DOCKER_FIRMWARE_TOKEN: + required: true + inputs: + release_channel: + description: Release channel to target + required: true + type: string + +permissions: + contents: write + packages: write + +jobs: + docker-debian-amd64: + uses: ./.github/workflows/docker_build.yml + with: + distro: debian + platform: linux/amd64 + runs-on: ubuntu-24.04 + push: true + secrets: inherit + + docker-debian-arm64: + uses: ./.github/workflows/docker_build.yml + with: + distro: debian + platform: linux/arm64 + runs-on: ubuntu-24.04-arm + push: true + secrets: inherit + + docker-debian-armv7: + uses: ./.github/workflows/docker_build.yml + with: + distro: debian + platform: linux/arm/v7 + runs-on: ubuntu-24.04-arm + push: true + secrets: inherit + + docker-alpine-amd64: + uses: ./.github/workflows/docker_build.yml + with: + distro: alpine + platform: linux/amd64 + runs-on: ubuntu-24.04 + push: true + secrets: inherit + + docker-alpine-arm64: + uses: ./.github/workflows/docker_build.yml + with: + distro: alpine + platform: linux/arm64 + runs-on: ubuntu-24.04-arm + push: true + secrets: inherit + + docker-alpine-armv7: + uses: ./.github/workflows/docker_build.yml + with: + distro: alpine + platform: linux/arm/v7 + runs-on: ubuntu-24.04-arm + push: true + secrets: inherit + + docker-manifest: + needs: + # Debian + - docker-debian-amd64 + - docker-debian-arm64 + - docker-debian-armv7 + # Alpine + - docker-alpine-amd64 + - docker-alpine-arm64 + - docker-alpine-armv7 + runs-on: ubuntu-24.04 + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: recursive + ref: ${{github.event.pull_request.head.ref}} + repository: ${{github.event.pull_request.head.repo.full_name}} + + - name: Get release version string + run: | + echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT + echo "short=$(./bin/buildinfo.py short)" >> $GITHUB_OUTPUT + id: version + + - name: Enumerate tags + shell: python + run: | + import os + + short = "${{ steps.version.outputs.short }}" + long = "${{ steps.version.outputs.long }}" + release_channel = "${{ inputs.release_channel }}" + tags = { + "beta": { + "debian": [ + f"{short}", f"{long}", f"{short}-beta", f"{long}-beta", "beta", "latest", + f"{short}-debian", f"{long}-debian", f"{short}-beta-debian", f"{long}-beta-debian", "beta-debian" + ], + "alpine": [ + f"{short}-alpine", f"{long}-alpine", f"{short}-beta-alpine", f"{long}-beta-alpine", "beta-alpine" + ] + }, + "alpha": { + "debian": [ + f"{short}-alpha", f"{long}-alpha", "alpha", + f"{short}-alpha-debian", f"{long}-alpha-debian", "alpha-debian" + ], + "alpine": [ + f"{short}-alpha-alpine", f"{long}-alpha-alpine", "alpha-alpine" + ] + }, + "daily": { + "debian": ["daily", "daily-debian"], + "alpine": ["daily-alpine"] + } + } + + with open(os.environ["GITHUB_OUTPUT"], "a") as fh: + fh.write("debian<> $GITHUB_OUTPUT - id: version - - - name: Download artifacts - uses: actions/download-artifact@v4 - with: - name: firmware-native-${{ steps.version.outputs.long }}.zip - merge-multiple: true - - - name: Display structure of downloaded files - run: ls -R - - - name: build .debpkg - run: | - mkdir -p .debpkg/DEBIAN - mkdir -p .debpkg/usr/share/meshtasticd/web - mkdir -p .debpkg/usr/sbin - mkdir -p .debpkg/etc/meshtasticd - mkdir -p .debpkg/etc/meshtasticd/config.d - mkdir -p .debpkg/etc/meshtasticd/available.d - mkdir -p .debpkg/usr/lib/systemd/system/ - tar -xf build.tar -C .debpkg/usr/share/meshtasticd/web - shopt -s dotglob nullglob - if [ -d .debpkg/usr/share/meshtasticd/web/build ]; then mv .debpkg/usr/share/meshtasticd/web/build/* .debpkg/usr/share/meshtasticd/web/; fi - if [ -d .debpkg/usr/share/meshtasticd/web/build ]; then rmdir .debpkg/usr/share/meshtasticd/web/build; fi - if [ -d .debpkg/usr/share/meshtasticd/web/.DS_Store ]; then rm -f .debpkg/usr/share/meshtasticd/web/.DS_Store; fi - gunzip .debpkg/usr/share/meshtasticd/web/ -r - cp release/meshtasticd_linux_x86_64 .debpkg/usr/sbin/meshtasticd - cp bin/config-dist.yaml .debpkg/etc/meshtasticd/config.yaml - cp bin/config.d/* .debpkg/etc/meshtasticd/available.d/ -r - chmod +x .debpkg/usr/sbin/meshtasticd - cp bin/meshtasticd.service .debpkg/usr/lib/systemd/system/meshtasticd.service - echo "/etc/meshtasticd/config.yaml" > .debpkg/DEBIAN/conffiles - chmod +x .debpkg/DEBIAN/conffiles - # Transition /usr/share/doc/meshtasticd to /usr/share/meshtasticd - echo "rm -rf /usr/share/doc/meshtasticd" > .debpkg/DEBIAN/preinst - chmod +x .debpkg/DEBIAN/preinst - echo "ln -sf /usr/share/meshtasticd /usr/share/doc/meshtasticd" > .debpkg/DEBIAN/postinst - chmod +x .debpkg/DEBIAN/postinst - - - uses: jiro4989/build-deb-action@v3 - with: - package: meshtasticd - package_root: .debpkg - maintainer: Jonathan Bennett - version: ${{ steps.version.outputs.long }} # refs/tags/v*.*.* - arch: amd64 - depends: libyaml-cpp0.7, openssl, libulfius2.7, libi2c0 - desc: Native Linux Meshtastic binary. - - - uses: actions/upload-artifact@v4 - with: - name: meshtasticd_${{ steps.version.outputs.long }}_amd64.deb - overwrite: true - path: | - ./*.deb diff --git a/.github/workflows/package_pio_deps.yml b/.github/workflows/package_pio_deps.yml new file mode 100644 index 000000000..9f535b7b1 --- /dev/null +++ b/.github/workflows/package_pio_deps.yml @@ -0,0 +1,65 @@ +name: Package PlatformIO Library Dependencies +# trunk-ignore-all(checkov/CKV_GHA_7): Allow workflow_dispatch inputs for testing + +on: + workflow_call: + inputs: + pio_env: + description: PlatformIO environment to target + required: true + type: string + workflow_dispatch: + inputs: + pio_env: + description: PlatformIO environment to target + required: true + type: string + +permissions: + contents: write + packages: write + +jobs: + pkg-pio-libdeps: + runs-on: ubuntu-24.04 + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: recursive + ref: ${{github.event.pull_request.head.ref}} + repository: ${{github.event.pull_request.head.repo.full_name}} + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: 3.x + + - name: Install deps + shell: bash + run: | + pip install platformio + + - name: Get release version string + run: | + echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT + id: version + + - name: Fetch libdeps + shell: bash + run: |- + platformio pkg install -e ${{ inputs.pio_env }} + platformio pkg install -e ${{ inputs.pio_env }} -t platformio/tool-scons@4.40502.0 + env: + PLATFORMIO_LIBDEPS_DIR: pio/libdeps + PLATFORMIO_PACKAGES_DIR: pio/packages + PLATFORMIO_CORE_DIR: pio/core + + - name: Store binaries as an artifact + uses: actions/upload-artifact@v4 + with: + name: platformio-deps-${{ inputs.pio_env }}-${{ steps.version.outputs.long }} + overwrite: true + include-hidden-files: true + path: | + pio/* diff --git a/.github/workflows/package_raspbian.yml b/.github/workflows/package_raspbian.yml deleted file mode 100644 index 62613f85f..000000000 --- a/.github/workflows/package_raspbian.yml +++ /dev/null @@ -1,90 +0,0 @@ -name: Package Raspbian - -on: - workflow_call: - workflow_dispatch: - -permissions: - contents: write - packages: write - -jobs: - build-raspbian: - uses: ./.github/workflows/build_raspbian.yml - - package-raspbian: - runs-on: ubuntu-22.04 - needs: build-raspbian - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - submodules: recursive - ref: ${{github.event.pull_request.head.ref}} - repository: ${{github.event.pull_request.head.repo.full_name}} - - - name: Pull web ui - uses: dsaltares/fetch-gh-release-asset@master - with: - repo: meshtastic/web - file: build.tar - target: build.tar - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Get release version string - run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT - id: version - - - name: Download artifacts - uses: actions/download-artifact@v4 - with: - name: firmware-raspbian-${{ steps.version.outputs.long }}.zip - merge-multiple: true - - - name: Display structure of downloaded files - run: ls -R - - - name: build .debpkg - run: | - mkdir -p .debpkg/DEBIAN - mkdir -p .debpkg/usr/share/meshtasticd/web - mkdir -p .debpkg/usr/sbin - mkdir -p .debpkg/etc/meshtasticd - mkdir -p .debpkg/etc/meshtasticd/config.d - mkdir -p .debpkg/etc/meshtasticd/available.d - mkdir -p .debpkg/usr/lib/systemd/system/ - tar -xf build.tar -C .debpkg/usr/share/meshtasticd/web - shopt -s dotglob nullglob - if [ -d .debpkg/usr/share/meshtasticd/web/build ]; then mv .debpkg/usr/share/meshtasticd/web/build/* .debpkg/usr/share/meshtasticd/web/; fi - if [ -d .debpkg/usr/share/meshtasticd/web/build ]; then rmdir .debpkg/usr/share/meshtasticd/web/build; fi - if [ -d .debpkg/usr/share/meshtasticd/web/.DS_Store ]; then rm -f .debpkg/usr/share/meshtasticd/web/.DS_Store; fi - gunzip .debpkg/usr/share/meshtasticd/web/ -r - cp release/meshtasticd_linux_aarch64 .debpkg/usr/sbin/meshtasticd - cp bin/config-dist.yaml .debpkg/etc/meshtasticd/config.yaml - cp bin/config.d/* .debpkg/etc/meshtasticd/available.d/ -r - chmod +x .debpkg/usr/sbin/meshtasticd - cp bin/meshtasticd.service .debpkg/usr/lib/systemd/system/meshtasticd.service - echo "/etc/meshtasticd/config.yaml" > .debpkg/DEBIAN/conffiles - chmod +x .debpkg/DEBIAN/conffiles - # Transition /usr/share/doc/meshtasticd to /usr/share/meshtasticd - echo "rm -rf /usr/share/doc/meshtasticd" > .debpkg/DEBIAN/preinst - chmod +x .debpkg/DEBIAN/preinst - echo "ln -sf /usr/share/meshtasticd /usr/share/doc/meshtasticd" > .debpkg/DEBIAN/postinst - chmod +x .debpkg/DEBIAN/postinst - - - uses: jiro4989/build-deb-action@v3 - with: - package: meshtasticd - package_root: .debpkg - maintainer: Jonathan Bennett - version: ${{ steps.version.outputs.long }} # refs/tags/v*.*.* - arch: arm64 - depends: libyaml-cpp0.7, openssl, libulfius2.7, libi2c0 - desc: Native Linux Meshtastic binary. - - - uses: actions/upload-artifact@v4 - with: - name: meshtasticd_${{ steps.version.outputs.long }}_arm64.deb - overwrite: true - path: | - ./*.deb diff --git a/.github/workflows/package_raspbian_armv7l.yml b/.github/workflows/package_raspbian_armv7l.yml deleted file mode 100644 index 8a9df1710..000000000 --- a/.github/workflows/package_raspbian_armv7l.yml +++ /dev/null @@ -1,90 +0,0 @@ -name: Package Raspbian - -on: - workflow_call: - workflow_dispatch: - -permissions: - contents: write - packages: write - -jobs: - build-raspbian_armv7l: - uses: ./.github/workflows/build_raspbian_armv7l.yml - - package-raspbian_armv7l: - runs-on: ubuntu-22.04 - needs: build-raspbian_armv7l - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - submodules: recursive - ref: ${{github.event.pull_request.head.ref}} - repository: ${{github.event.pull_request.head.repo.full_name}} - - - name: Pull web ui - uses: dsaltares/fetch-gh-release-asset@master - with: - repo: meshtastic/web - file: build.tar - target: build.tar - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Get release version string - run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT - id: version - - - name: Download artifacts - uses: actions/download-artifact@v4 - with: - name: firmware-raspbian-armv7l-${{ steps.version.outputs.long }}.zip - merge-multiple: true - - - name: Display structure of downloaded files - run: ls -R - - - name: build .debpkg - run: | - mkdir -p .debpkg/DEBIAN - mkdir -p .debpkg/usr/share/meshtasticd/web - mkdir -p .debpkg/usr/sbin - mkdir -p .debpkg/etc/meshtasticd - mkdir -p .debpkg/etc/meshtasticd/config.d - mkdir -p .debpkg/etc/meshtasticd/available.d - mkdir -p .debpkg/usr/lib/systemd/system/ - tar -xf build.tar -C .debpkg/usr/share/meshtasticd/web - shopt -s dotglob nullglob - if [ -d .debpkg/usr/share/meshtasticd/web/build ]; then mv .debpkg/usr/share/meshtasticd/web/build/* .debpkg/usr/share/meshtasticd/web/; fi - if [ -d .debpkg/usr/share/meshtasticd/web/build ]; then rmdir .debpkg/usr/share/meshtasticd/web/build; fi - if [ -d .debpkg/usr/share/meshtasticd/web/.DS_Store ]; then rm -f .debpkg/usr/share/meshtasticd/web/.DS_Store; fi - gunzip .debpkg/usr/share/meshtasticd/web/ -r - cp release/meshtasticd_linux_armv7l .debpkg/usr/sbin/meshtasticd - cp bin/config-dist.yaml .debpkg/etc/meshtasticd/config.yaml - cp bin/config.d/* .debpkg/etc/meshtasticd/available.d/ -r - chmod +x .debpkg/usr/sbin/meshtasticd - cp bin/meshtasticd.service .debpkg/usr/lib/systemd/system/meshtasticd.service - echo "/etc/meshtasticd/config.yaml" > .debpkg/DEBIAN/conffiles - chmod +x .debpkg/DEBIAN/conffiles - # Transition /usr/share/doc/meshtasticd to /usr/share/meshtasticd - echo "rm -rf /usr/share/doc/meshtasticd" > .debpkg/DEBIAN/preinst - chmod +x .debpkg/DEBIAN/preinst - echo "ln -sf /usr/share/meshtasticd /usr/share/doc/meshtasticd" > .debpkg/DEBIAN/postinst - chmod +x .debpkg/DEBIAN/postinst - - - uses: jiro4989/build-deb-action@v3 - with: - package: meshtasticd - package_root: .debpkg - maintainer: Jonathan Bennett - version: ${{ steps.version.outputs.long }} # refs/tags/v*.*.* - arch: armhf - depends: libyaml-cpp0.7, openssl, libulfius2.7, libi2c0 - desc: Native Linux Meshtastic binary. - - - uses: actions/upload-artifact@v4 - with: - name: meshtasticd_${{ steps.version.outputs.long }}_armhf.deb - overwrite: true - path: | - ./*.deb diff --git a/.github/workflows/release_channels.yml b/.github/workflows/release_channels.yml index afb7319ed..9cdabde9e 100644 --- a/.github/workflows/release_channels.yml +++ b/.github/workflows/release_channels.yml @@ -9,6 +9,13 @@ permissions: packages: write jobs: + build-docker: + uses: ./.github/workflows/docker_manifest.yml + with: + release_channel: |- + ${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} + secrets: inherit + package-ppa: strategy: fail-fast: false @@ -25,14 +32,14 @@ jobs: uses: ./.github/workflows/package_obs.yml with: obs_project: |- - home:meshtastic:${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} + network:Meshtastic:${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} series: |- ${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} secrets: inherit - # hook-copr: - # uses: ./.github/workflows/hook_copr.yml - # with: - # copr_project: |- - # ${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} - # secrets: inherit + hook-copr: + uses: ./.github/workflows/hook_copr.yml + with: + copr_project: |- + ${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} + secrets: inherit diff --git a/.github/workflows/trunk_annotate.pr.yml b/.github/workflows/trunk_annotate.pr.yml new file mode 100644 index 000000000..ac5cdc0d5 --- /dev/null +++ b/.github/workflows/trunk_annotate.pr.yml @@ -0,0 +1,26 @@ +name: Annotate PR with trunk issues +# See: https://github.com/trunk-io/trunk-action/blob/v1/readme.md#getting-inline-annotations-for-fork-prs + +on: + workflow_run: + workflows: [Pull Request] # Name from `trunk_check.yml` + types: [completed] + +permissions: read-all + +jobs: + trunk_check: + name: Trunk Code Quality Annotate + runs-on: ubuntu-latest + permissions: + checks: write # For trunk to post annotations + contents: read # For repo checkout + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Trunk Check + uses: trunk-io/trunk-action@v1 + with: + post-annotations: true diff --git a/.github/workflows/trunk-check.yml b/.github/workflows/trunk_check.yml similarity index 100% rename from .github/workflows/trunk-check.yml rename to .github/workflows/trunk_check.yml diff --git a/.gitignore b/.gitignore index 803aee139..b63f431d1 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,9 @@ web.tar *.code-workspace .idea +.platformio +.local +.cache .DS_Store Thumbs.db diff --git a/.trunk/configs/.prettierrc b/.trunk/configs/.prettierrc new file mode 100644 index 000000000..edf9dbc9c --- /dev/null +++ b/.trunk/configs/.prettierrc @@ -0,0 +1,10 @@ +{ + "overrides": [ + { + "files": "userPrefs.jsonc", + "options": { + "trailingComma": "none" + } + } + ] +} diff --git a/Dockerfile b/Dockerfile index f3b294a5b..f9a3b9962 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,7 +38,8 @@ USER root RUN apt-get update && apt-get --no-install-recommends -y install libc-bin libc6 libgpiod2 libyaml-cpp0.7 libi2c0 libulfius2.7 libusb-1.0-0-dev liborcania2.3 libssl3 && \ apt-get clean && rm -rf /var/lib/apt/lists/* \ && mkdir -p /var/lib/meshtasticd \ - && mkdir -p /etc/meshtasticd/config.d + && mkdir -p /etc/meshtasticd/config.d \ + && mkdir -p /etc/meshtasticd/ssl # Fetch compiled binary from the builder COPY --from=builder /tmp/firmware/release/meshtasticd /usr/sbin/ diff --git a/README.md b/README.md index ca8a924fd..f34bf1839 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ -# Meshtastic Firmware +
+ +Meshtastic Logo +

Meshtastic Firmware

![GitHub release downloads](https://img.shields.io/github/downloads/meshtastic/firmware/total) [![CI](https://img.shields.io/github/actions/workflow/status/meshtastic/firmware/main_matrix.yml?branch=master&label=actions&logo=github&color=yellow)](https://github.com/meshtastic/firmware/actions/workflows/ci.yml) @@ -6,13 +9,31 @@ [![Fiscal Contributors](https://opencollective.com/meshtastic/tiers/badge.svg?label=Fiscal%20Contributors&color=deeppink)](https://opencollective.com/meshtastic/) [![Vercel](https://img.shields.io/static/v1?label=Powered%20by&message=Vercel&style=flat&logo=vercel&color=000000)](https://vercel.com?utm_source=meshtastic&utm_campaign=oss) +meshtastic%2Ffirmware | Trendshift + +
+ + + +
+ Website + - + Documentation +
+ ## Overview -This repository contains the device firmware for the Meshtastic project. +This repository contains the official device firmware for Meshtastic, an open-source LoRa mesh networking project designed for long-range, low-power communication without relying on internet or cellular infrastructure. The firmware supports various hardware platforms, including ESP32, nRF52, RP2040/RP2350, and Linux-based devices. -- **[Building Instructions](https://meshtastic.org/docs/development/firmware/build)** -- **[Flashing Instructions](https://meshtastic.org/docs/getting-started/flashing-firmware/)** +Meshtastic enables text messaging, location sharing, and telemetry over a decentralized mesh network, making it ideal for outdoor adventures, emergency preparedness, and remote operations. + +### Get Started + +- 🔧 **[Building Instructions](https://meshtastic.org/docs/development/firmware/build)** – Learn how to compile the firmware from source. +- âš¡ **[Flashing Instructions](https://meshtastic.org/docs/getting-started/flashing-firmware/)** – Install or update the firmware on your device. + +Join our community and help improve Meshtastic! 🚀 ## Stats -![Alt](https://repobeats.axiom.co/api/embed/a92f097d9197ae853e780ec53d7d126e545629ab.svg "Repobeats analytics image") +![Alt](https://repobeats.axiom.co/api/embed/8025e56c482ec63541593cc5bd322c19d5c0bdcf.svg "Repobeats analytics image") diff --git a/alpine.Dockerfile b/alpine.Dockerfile index 115602b3b..8b48eeca3 100644 --- a/alpine.Dockerfile +++ b/alpine.Dockerfile @@ -29,7 +29,8 @@ USER root RUN apk add libstdc++ libgpiod yaml-cpp libusb i2c-tools \ && mkdir -p /var/lib/meshtasticd \ - && mkdir -p /etc/meshtasticd/config.d + && mkdir -p /etc/meshtasticd/config.d \ + && mkdir -p /etc/meshtasticd/ssl COPY --from=builder /tmp/firmware/release/meshtasticd /usr/sbin/ WORKDIR /var/lib/meshtasticd diff --git a/arch/nrf52/nrf52.ini b/arch/nrf52/nrf52.ini index 57b276978..b68977c78 100644 --- a/arch/nrf52/nrf52.ini +++ b/arch/nrf52/nrf52.ini @@ -4,7 +4,7 @@ platform = platformio/nordicnrf52@^10.7.0 extends = arduino_base platform_packages = ; our custom Git version until they merge our PR - framework-arduinoadafruitnrf52 @ https://github.com/geeksville/Adafruit_nRF52_Arduino.git + framework-arduinoadafruitnrf52 @ https://github.com/meshtastic/Adafruit_nRF52_Arduino.git#e13f5820002a4fb2a5e6754b42ace185277e5adf toolchain-gccarmnoneeabi@~1.90301.0 build_type = debug diff --git a/arch/stm32/stm32.ini b/arch/stm32/stm32.ini index 7e211496d..46f41db3a 100644 --- a/arch/stm32/stm32.ini +++ b/arch/stm32/stm32.ini @@ -11,9 +11,15 @@ build_flags = ${arduino_base.build_flags} -flto -Isrc/platform/stm32wl -g - -DMESHTASTIC_MINIMIZE_BUILD + -DMESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR + -DMESHTASTIC_EXCLUDE_INPUTBROKER + -DMESHTASTIC_EXCLUDE_I2C + -DMESHTASTIC_EXCLUDE_POWERMON + -DMESHTASTIC_EXCLUDE_SCREEN + -DMESHTASTIC_EXCLUDE_MQTT + -DMESHTASTIC_EXCLUDE_BLUETOOTH + -DMESHTASTIC_EXCLUDE_PKI -DMESHTASTIC_EXCLUDE_GPS - -DDEBUG_MUTE ; -DVECT_TAB_OFFSET=0x08000000 -DconfigUSE_CMSIS_RTOS_V2=1 ; -DSPI_MODE_0=SPI_MODE0 diff --git a/bin/build-firmware.sh b/bin/build-firmware.sh new file mode 100644 index 000000000..c53f1b660 --- /dev/null +++ b/bin/build-firmware.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +sed -i 's/#-DBUILD_EPOCH=$UNIX_TIME/-DBUILD_EPOCH=$UNIX_TIME/' platformio.ini + +export PIP_BREAK_SYSTEM_PACKAGES=1 + +if (echo $2 | grep -q "esp32"); then + bin/build-esp32.sh $1 +elif (echo $2 | grep -q "nrf52"); then + bin/build-nrf52.sh $1 +elif (echo $2 | grep -q "stm32"); then + bin/build-stm32.sh $1 +elif (echo $2 | grep -q "rpi2040"); then + bin/build-rpi2040.sh $1 +else + echo "Unknown target $2" + exit 1 +fi \ No newline at end of file diff --git a/bin/config-dist.yaml b/bin/config-dist.yaml index c8f181308..da4c192c7 100644 --- a/bin/config-dist.yaml +++ b/bin/config-dist.yaml @@ -78,6 +78,8 @@ Lora: # TXen: x # TX and RX enable pins # RXen: x +# SX126X_MAX_POWER: 8 # Limit the output power to 8 dBm, useful for amped nodes + # spiSpeed: 2000000 ### Set default/fallback gpio chip to use in /dev/. Defaults to 0. @@ -182,10 +184,12 @@ Logging: Webserver: # Port: 443 # Port for Webserver & Webservices # RootPath: /usr/share/meshtasticd/web # Root Dir of WebServer +# SSLKey: /etc/meshtasticd/ssl/private_key.pem # Path to SSL Key, generated if not present +# SSLCert: /etc/meshtasticd/ssl/certificate.pem # Path to SSL Certificate, generated if not present General: MaxNodes: 200 MaxMessageQueue: 100 ConfigDirectory: /etc/meshtasticd/config.d/ # MACAddress: AA:BB:CC:DD:EE:FF -# MACAddressSource: eth0 +# MACAddressSource: eth0 \ No newline at end of file diff --git a/bin/config.d/OpenWRT/BananaPi-BPI-R4-sx1262.yaml b/bin/config.d/OpenWRT/BananaPi-BPI-R4-sx1262.yaml new file mode 100644 index 000000000..825ab2699 --- /dev/null +++ b/bin/config.d/OpenWRT/BananaPi-BPI-R4-sx1262.yaml @@ -0,0 +1,9 @@ +Lora: + Module: sx1262 # BananaPi-BPI-R4 SPI via 26p GPIO Header +## CS: 28 + IRQ: 50 + Busy: 62 + Reset: 51 + spidev: spidev1.0 + DIO2_AS_RF_SWITCH: true + DIO3_TCXO_VOLTAGE: true diff --git a/bin/config.d/OpenWRT-One-mikroBUS-LR-IOT-CLICK.yaml b/bin/config.d/OpenWRT/OpenWRT-One-mikroBUS-LR-IOT-CLICK.yaml similarity index 100% rename from bin/config.d/OpenWRT-One-mikroBUS-LR-IOT-CLICK.yaml rename to bin/config.d/OpenWRT/OpenWRT-One-mikroBUS-LR-IOT-CLICK.yaml diff --git a/bin/config.d/OpenWRT_One_mikroBUS_sx1262.yaml b/bin/config.d/OpenWRT/OpenWRT_One_mikroBUS_sx1262.yaml similarity index 100% rename from bin/config.d/OpenWRT_One_mikroBUS_sx1262.yaml rename to bin/config.d/OpenWRT/OpenWRT_One_mikroBUS_sx1262.yaml diff --git a/bin/config.d/lora-MeshAdv-900M30S.yaml b/bin/config.d/lora-MeshAdv-900M30S.yaml index 07dada620..113901d5e 100644 --- a/bin/config.d/lora-MeshAdv-900M30S.yaml +++ b/bin/config.d/lora-MeshAdv-900M30S.yaml @@ -7,3 +7,6 @@ Lora: TXen: 13 RXen: 12 DIO3_TCXO_VOLTAGE: true + # Only for E22-900M33S: + # Limit the output power to 8 dBm + # SX126X_MAX_POWER: 8 \ No newline at end of file diff --git a/bin/platformio-custom.py b/bin/platformio-custom.py index acfeae10c..09e8e6d83 100644 --- a/bin/platformio-custom.py +++ b/bin/platformio-custom.py @@ -102,7 +102,7 @@ pref_flags = [] for pref in userPrefs: if userPrefs[pref].startswith("{"): pref_flags.append("-D" + pref + "=" + userPrefs[pref]) - elif userPrefs[pref].replace(".", "").isdigit(): + elif userPrefs[pref].lstrip("-").replace(".", "").isdigit(): pref_flags.append("-D" + pref + "=" + userPrefs[pref]) elif userPrefs[pref] == "true" or userPrefs[pref] == "false": pref_flags.append("-D" + pref + "=" + userPrefs[pref]) diff --git a/boards/meshlink.json b/boards/meshlink.json new file mode 100644 index 000000000..a608de88a --- /dev/null +++ b/boards/meshlink.json @@ -0,0 +1,52 @@ +{ + "build": { + "arduino": { + "ldscript": "nrf52840_s140_v6.ld" + }, + "core": "nRF5", + "cpu": "cortex-m4", + "extra_flags": "-DMESHLINK -DNRF52840_XXAA", + "f_cpu": "64000000L", + "hwids": [ + ["0x239A", "0x00B3"], + ["0x239A", "0x8029"], + ["0x239A", "0x0029"], + ["0x239A", "0x002A"], + ["0x239A", "0x802A"] + ], + "usb_product": "MeshLink", + "mcu": "nrf52840", + "variant": "meshlink", + "bsp": { + "name": "adafruit" + }, + "softdevice": { + "sd_flags": "-DS140", + "sd_name": "s140", + "sd_version": "6.1.1", + "sd_fwid": "0x00B6" + }, + "bootloader": { + "settings_addr": "0xFF000" + } + }, + "connectivity": ["bluetooth"], + "debug": { + "jlink_device": "nRF52840_xxAA", + "svd_path": "nrf52840.svd" + }, + "frameworks": ["arduino"], + "name": "MeshLink", + "upload": { + "maximum_ram_size": 248832, + "maximum_size": 815104, + "speed": 115200, + "protocol": "nrfutil", + "protocols": ["nrfutil", "jlink", "nrfjprog", "stlink"], + "use_1200bps_touch": true, + "require_upload_port": true, + "wait_for_upload_port": true + }, + "url": "https://www.loraitalia.it", + "vendor": "LoraItalia" +} diff --git a/debian/changelog b/debian/changelog index a1a359cfb..3ec57b805 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,7 +1,9 @@ -meshtasticd (2.5.20.0) UNRELEASED; urgency=medium +meshtasticd (2.5.22.0) UNRELEASED; urgency=medium * Initial packaging * GitHub Actions Automatic version bump * GitHub Actions Automatic version bump + * GitHub Actions Automatic version bump + * GitHub Actions Automatic version bump - -- Austin Lane Wed, 15 Jan 2025 14:08:54 +0000 + -- Austin Lane Wed, 05 Feb 2025 01:10:33 +0000 diff --git a/debian/ci_pack_sdeb.sh b/debian/ci_pack_sdeb.sh index 1f311af93..a8b2252ae 100755 --- a/debian/ci_pack_sdeb.sh +++ b/debian/ci_pack_sdeb.sh @@ -11,7 +11,7 @@ platformio pkg install -e native -t platformio/tool-scons@4.40502.0 tar -cf pio.tar pio/ rm -rf pio # Download the latest meshtastic/web release build.tar to `web.tar` -curl -L https://github.com/meshtastic/web/releases/download/latest/build.tar -o web.tar +curl -L https://github.com/meshtastic/web/releases/latest/download/build.tar -o web.tar package=$(dpkg-parsechangelog --show-field Source) diff --git a/debian/meshtasticd.dirs b/debian/meshtasticd.dirs index 5f57ff7be..45a1ca3db 100644 --- a/debian/meshtasticd.dirs +++ b/debian/meshtasticd.dirs @@ -1,4 +1,5 @@ etc/meshtasticd etc/meshtasticd/config.d etc/meshtasticd/available.d -usr/share/meshtasticd/web \ No newline at end of file +usr/share/meshtasticd/web +etc/meshtasticd/ssl \ No newline at end of file diff --git a/meshtasticd.spec.rpkg b/meshtasticd.spec.rpkg index 1819897b0..0a0f03557 100644 --- a/meshtasticd.spec.rpkg +++ b/meshtasticd.spec.rpkg @@ -21,7 +21,7 @@ Summary: Meshtastic daemon for communicating with Meshtastic devices License: GPL-3.0 URL: https://github.com/meshtastic/firmware Source0: {{{ git_dir_pack }}} -Source1: https://github.com/meshtastic/web/releases/download/latest/build.tar +Source1: https://github.com/meshtastic/web/releases/latest/download/build.tar BuildRequires: systemd-rpm-macros BuildRequires: python3-devel @@ -72,6 +72,8 @@ install -D -m 0644 bin/meshtasticd.service %{buildroot}%{_unitdir}/meshtasticd.s # Install the web files under /usr/share/meshtasticd/web mkdir -p %{buildroot}%{_datadir}/meshtasticd/web cp -r web/* %{buildroot}%{_datadir}/meshtasticd/web +# Install default SSL storage directory (for web) +mkdir -p %{buildroot}%{_sysconfdir}/meshtasticd/ssl %files %license LICENSE @@ -86,6 +88,7 @@ cp -r web/* %{buildroot}%{_datadir}/meshtasticd/web %dir %{_datadir}/meshtasticd %dir %{_datadir}/meshtasticd/web %{_datadir}/meshtasticd/web/* +%dir %{_sysconfdir}/meshtasticd/ssl %changelog %autochangelog \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index d97c90467..171d6c829 100644 --- a/platformio.ini +++ b/platformio.ini @@ -20,7 +20,7 @@ extra_scripts = bin/platformio-custom.py build_flags = -Wno-missing-field-initializers -Wno-format - -Isrc -Isrc/mesh -Isrc/mesh/generated -Isrc/gps -Isrc/buzz -Wl,-Map,${platformio.build_dir}/output.map + -Isrc -Isrc/mesh -Isrc/mesh/generated -Isrc/gps -Isrc/buzz -Wl,-Map,"${platformio.build_dir}"/output.map -DUSE_THREAD_NAMES -DTINYGPS_OPTION_NO_CUSTOM_FIELDS -DPB_ENABLE_MALLOC=1 diff --git a/protobufs b/protobufs index fde27e4ef..068646653 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit fde27e4ef0fcee967063ba353422ed5f9a1c4790 +Subproject commit 068646653e8375fc145988026ad242a3cf70f7ab diff --git a/src/AmbientLightingThread.h b/src/AmbientLightingThread.h index 600583348..c487f9d53 100644 --- a/src/AmbientLightingThread.h +++ b/src/AmbientLightingThread.h @@ -153,7 +153,7 @@ class AmbientLightingThread : public concurrency::OSThread pixels.fill(BUTTON1_COLOR, BUTTON1_COLOR_INDEX, 1); #endif #if defined(BUTTON2_COLOR) && defined(BUTTON2_COLOR_INDEX) - pixels.fill(BUTTON2_COLOR, BUTTON1_COLOR_INDEX, 1); + pixels.fill(BUTTON2_COLOR, BUTTON2_COLOR_INDEX, 1); #endif #endif pixels.show(); diff --git a/src/FSCommon.cpp b/src/FSCommon.cpp index 1f2994b29..461c72c26 100644 --- a/src/FSCommon.cpp +++ b/src/FSCommon.cpp @@ -49,24 +49,6 @@ void OSFS::writeNBytes(uint16_t address, unsigned int num, const byte *input) } #endif -bool lfs_assert_failed = - false; // Note: we use this global on all platforms, though it can only be set true on nrf52 (in our modified lfs_util.h) - -extern "C" void lfs_assert(const char *reason) -{ - LOG_ERROR("LFS assert: %s", reason); - lfs_assert_failed = true; - -#ifndef ARCH_PORTDUINO -#ifdef FSCom - // CORRUPTED FILESYSTEM. This causes bootloop so - // might as well try formatting now. - LOG_ERROR("Trying FSCom.format()"); - FSCom.format(); -#endif -#endif -} - /** * @brief Copies a file from one location to another. * @@ -348,10 +330,16 @@ void rmDir(const char *dirname) #endif } +/** + * Some platforms (nrf52) might need to do an extra step before FSBegin(). + */ +__attribute__((weak, noinline)) void preFSBegin() {} + void fsInit() { #ifdef FSCom - spiLock->lock(); + concurrency::LockGuard g(spiLock); + preFSBegin(); if (!FSBegin()) { LOG_ERROR("Filesystem mount failed"); // assert(0); This auto-formats the partition, so no need to fail here. @@ -362,7 +350,6 @@ void fsInit() LOG_DEBUG("Filesystem files:"); #endif listDir("/", 10); - spiLock->unlock(); #endif } @@ -400,4 +387,4 @@ void setupSDCard() LOG_DEBUG("Total space: %lu MB", (uint32_t)(SD.totalBytes() / (1024 * 1024))); LOG_DEBUG("Used space: %lu MB", (uint32_t)(SD.usedBytes() / (1024 * 1024))); #endif -} +} \ No newline at end of file diff --git a/src/FSCommon.h b/src/FSCommon.h index 254245b29..10ce4aeec 100644 --- a/src/FSCommon.h +++ b/src/FSCommon.h @@ -57,7 +57,4 @@ bool renameFile(const char *pathFrom, const char *pathTo); std::vector getFiles(const char *dirname, uint8_t levels); void listDir(const char *dirname, uint8_t levels, bool del = false); void rmDir(const char *dirname); -void setupSDCard(); - -extern bool lfs_assert_failed; // Note: we use this global on all platforms, though it can only be set true on nrf52 (in our - // modified lfs_util.h) +void setupSDCard(); \ No newline at end of file diff --git a/src/Power.cpp b/src/Power.cpp index ae0908ec6..8d5fe1c32 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -87,7 +87,7 @@ MAX17048Sensor max17048Sensor; #endif #endif -#if HAS_RAKPROT && !defined(ARCH_PORTDUINO) +#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && HAS_RAKPROT && !defined(ARCH_PORTDUINO) RAK9154Sensor rak9154Sensor; #endif @@ -243,7 +243,8 @@ class AnalogBatteryLevel : public HasBatteryLevel virtual uint16_t getBattVoltage() override { -#if defined(HAS_RAKPROT) && !defined(ARCH_PORTDUINO) && !defined(HAS_PMU) +#if HAS_TELEMETRY && defined(HAS_RAKPROT) && !defined(ARCH_PORTDUINO) && !defined(HAS_PMU) && \ + !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR if (hasRAK()) { return getRAKVoltage(); } @@ -406,7 +407,8 @@ class AnalogBatteryLevel : public HasBatteryLevel /// we can't be smart enough to say 'full'? virtual bool isCharging() override { -#if defined(HAS_RAKPROT) && !defined(ARCH_PORTDUINO) && !defined(HAS_PMU) +#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && defined(HAS_RAKPROT) && !defined(ARCH_PORTDUINO) && \ + !defined(HAS_PMU) if (hasRAK()) { return (rak9154Sensor.isCharging()) ? OptTrue : OptFalse; } @@ -447,7 +449,7 @@ class AnalogBatteryLevel : public HasBatteryLevel float last_read_value = (OCV[NUM_OCV_POINTS - 1] * NUM_CELLS); uint32_t last_read_time_ms = 0; -#if defined(HAS_RAKPROT) +#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && defined(HAS_RAKPROT) uint16_t getRAKVoltage() { return rak9154Sensor.getBusVoltageMv(); } diff --git a/src/RedirectablePrint.cpp b/src/RedirectablePrint.cpp index 57f53019d..07f873864 100644 --- a/src/RedirectablePrint.cpp +++ b/src/RedirectablePrint.cpp @@ -79,17 +79,17 @@ size_t RedirectablePrint::vprintf(const char *logLevel, const char *format, va_l } if (color && logLevel != nullptr) { if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) - Print::write("\u001b[34m", 6); + Print::write("\u001b[34m", 5); if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0) - Print::write("\u001b[32m", 6); + Print::write("\u001b[32m", 5); if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0) - Print::write("\u001b[33m", 6); + Print::write("\u001b[33m", 5); if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_ERROR) == 0) - Print::write("\u001b[31m", 6); + Print::write("\u001b[31m", 5); } len = Print::write(printBuf, len); if (color && logLevel != nullptr) { - Print::write("\u001b[0m", 5); + Print::write("\u001b[0m", 4); } return len; } @@ -107,15 +107,15 @@ void RedirectablePrint::log_to_serial(const char *logLevel, const char *format, // include the header if (color) { if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) - Print::write("\u001b[34m", 6); + Print::write("\u001b[34m", 5); if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0) - Print::write("\u001b[32m", 6); + Print::write("\u001b[32m", 5); if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0) - Print::write("\u001b[33m", 6); + Print::write("\u001b[33m", 5); if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_ERROR) == 0) - Print::write("\u001b[31m", 6); + Print::write("\u001b[31m", 5); if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0) - Print::write("\u001b[35m", 6); + Print::write("\u001b[35m", 5); } uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // display local time on logfile @@ -393,4 +393,4 @@ std::string RedirectablePrint::mt_sprintf(const std::string fmt_str, ...) break; } return std::string(formatted.get()); -} +} \ No newline at end of file diff --git a/src/SafeFile.cpp b/src/SafeFile.cpp index f874164ae..c942aa0ee 100644 --- a/src/SafeFile.cpp +++ b/src/SafeFile.cpp @@ -8,10 +8,8 @@ static File openFile(const char *filename, bool fullAtomic) concurrency::LockGuard g(spiLock); LOG_DEBUG("Opening %s, fullAtomic=%d", filename, fullAtomic); #ifdef ARCH_NRF52 - lfs_assert_failed = false; - File file = FSCom.open(filename, FILE_O_WRITE); - file.seek(0); - return file; + FSCom.remove(filename); + return FSCom.open(filename, FILE_O_WRITE); #endif if (!fullAtomic) FSCom.remove(filename); // Nuke the old file to make space (ignore if it !exists) @@ -20,7 +18,6 @@ static File openFile(const char *filename, bool fullAtomic) filenameTmp += ".tmp"; // clear any previous LFS errors - lfs_assert_failed = false; return FSCom.open(filenameTmp.c_str(), FILE_O_WRITE); } @@ -61,9 +58,6 @@ bool SafeFile::close() return false; spiLock->lock(); -#ifdef ARCH_NRF52 - f.truncate(); -#endif f.close(); spiLock->unlock(); @@ -96,8 +90,6 @@ bool SafeFile::close() bool SafeFile::testReadback() { concurrency::LockGuard g(spiLock); - bool lfs_failed = lfs_assert_failed; - lfs_assert_failed = false; String filenameTmp = filename; filenameTmp += ".tmp"; @@ -119,7 +111,7 @@ bool SafeFile::testReadback() return false; } - return !lfs_failed; + return true; } #endif \ No newline at end of file diff --git a/src/detect/einkScan.h b/src/detect/einkScan.h index d20c7b6e5..5bc218d00 100644 --- a/src/detect/einkScan.h +++ b/src/detect/einkScan.h @@ -6,28 +6,28 @@ void d_writeCommand(uint8_t c) { - SPI1.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0)); + SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0)); if (PIN_EINK_DC >= 0) digitalWrite(PIN_EINK_DC, LOW); if (PIN_EINK_CS >= 0) digitalWrite(PIN_EINK_CS, LOW); - SPI1.transfer(c); + SPI.transfer(c); if (PIN_EINK_CS >= 0) digitalWrite(PIN_EINK_CS, HIGH); if (PIN_EINK_DC >= 0) digitalWrite(PIN_EINK_DC, HIGH); - SPI1.endTransaction(); + SPI.endTransaction(); } void d_writeData(uint8_t d) { - SPI1.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0)); + SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0)); if (PIN_EINK_CS >= 0) digitalWrite(PIN_EINK_CS, LOW); - SPI1.transfer(d); + SPI.transfer(d); if (PIN_EINK_CS >= 0) digitalWrite(PIN_EINK_CS, HIGH); - SPI1.endTransaction(); + SPI.endTransaction(); } unsigned long d_waitWhileBusy(uint16_t busy_time) @@ -53,7 +53,7 @@ unsigned long d_waitWhileBusy(uint16_t busy_time) void scanEInkDevice(void) { - SPI1.begin(); + SPI.begin(); d_writeCommand(0x22); d_writeData(0x83); d_writeCommand(0x20); @@ -62,6 +62,6 @@ void scanEInkDevice(void) LOG_DEBUG("EInk display found"); else LOG_DEBUG("EInk display not found"); - SPI1.end(); + SPI.end(); } #endif \ No newline at end of file diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 863f956cf..c2aae0381 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -449,7 +449,22 @@ 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 +#ifdef TRACKER_T1000_E + if (probeTries < 5) { +#else if (probeTries < 2) { +#endif LOG_DEBUG("Probe for GPS at %d", serialSpeeds[speedSelect]); gnssModel = probe(serialSpeeds[speedSelect]); if (gnssModel == GNSS_MODEL_UNKNOWN) { @@ -460,7 +475,11 @@ bool GPS::setup() } } // Rare Serial Speeds +#ifdef TRACKER_T1000_E + if (probeTries == 5) { +#else if (probeTries == 2) { +#endif LOG_DEBUG("Probe for GPS at %d", rareSerialSpeeds[speedSelect]); gnssModel = probe(rareSerialSpeeds[speedSelect]); if (gnssModel == GNSS_MODEL_UNKNOWN) { @@ -772,6 +791,9 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime) setPowerPMU(true); // Power (PMU): on writePinStandby(false); // Standby (pin): awake (not standby) setPowerUBLOX(true); // Standby (UBLOX): awake +#ifdef GNSS_AIROHA + lastFixStartMsec = 0; +#endif break; case GPS_SOFTSLEEP: diff --git a/src/graphics/EInkDisplay2.cpp b/src/graphics/EInkDisplay2.cpp index 6c85582c0..9702b0086 100644 --- a/src/graphics/EInkDisplay2.cpp +++ b/src/graphics/EInkDisplay2.cpp @@ -140,6 +140,15 @@ bool EInkDisplay::connect() adafruitDisplay->setRotation(3); adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight); } +#elif defined(MESHLINK) + { + auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, SPI1); + + adafruitDisplay = new GxEPD2_BW(*lowLevel); + adafruitDisplay->init(); + adafruitDisplay->setRotation(3); + adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight); + } #elif defined(RAK4630) || defined(MAKERPYTHON) { if (eink_found) { diff --git a/src/graphics/EInkDynamicDisplay.cpp b/src/graphics/EInkDynamicDisplay.cpp index 6664646b9..47012ca47 100644 --- a/src/graphics/EInkDynamicDisplay.cpp +++ b/src/graphics/EInkDynamicDisplay.cpp @@ -238,7 +238,7 @@ void EInkDynamicDisplay::checkRateLimiting() // Skip update: too soon for BACKGROUND if (frameFlags == BACKGROUND) { - if (Throttle::isWithinTimespanMs(previousRunMs, EINK_LIMIT_RATE_BACKGROUND_SEC * 1000)) { + if (Throttle::isWithinTimespanMs(previousRunMs, 30000)) { refresh = SKIPPED; reason = EXCEEDED_RATELIMIT_FULL; return; @@ -251,7 +251,7 @@ void EInkDynamicDisplay::checkRateLimiting() // Skip update: too soon for RESPONSIVE if (frameFlags & RESPONSIVE) { - if (Throttle::isWithinTimespanMs(previousRunMs, EINK_LIMIT_RATE_RESPONSIVE_SEC * 1000)) { + if (Throttle::isWithinTimespanMs(previousRunMs, 1000)) { refresh = SKIPPED; reason = EXCEEDED_RATELIMIT_FAST; LOG_DEBUG("refresh=SKIPPED, reason=EXCEEDED_RATELIMIT_FAST, frameFlags=0x%x", frameFlags); diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 198dcc235..0c18f3287 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -123,7 +123,7 @@ static bool heartbeat = false; #define getStringCenteredX(s) ((SCREEN_WIDTH - display->getStringWidth(s)) / 2) -/// Check if the display can render a string (detect special chars; emoji) +// Check if the display can render a string (detect special chars; emoji) static bool haveGlyphs(const char *str) { #if defined(OLED_PL) || defined(OLED_UA) || defined(OLED_RU) || defined(OLED_CS) @@ -162,11 +162,7 @@ static void drawIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDispl display->setFont(FONT_MEDIUM); display->setTextAlignment(TEXT_ALIGN_LEFT); -#ifdef USERPREFS_SPLASH_TITLE - const char *title = USERPREFS_SPLASH_TITLE; -#else const char *title = "meshtastic.org"; -#endif display->drawString(x + getStringCenteredX(title), y + SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM, title); display->setFont(FONT_SMALL); @@ -185,6 +181,56 @@ static void drawIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDispl display->setTextAlignment(TEXT_ALIGN_LEFT); // Restore left align, just to be kind to any other unsuspecting code } +#ifdef USERPREFS_OEM_TEXT + +static void drawOEMIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) +{ + static const uint8_t xbm[] = USERPREFS_OEM_IMAGE_DATA; + display->drawXbm(x + (SCREEN_WIDTH - USERPREFS_OEM_IMAGE_WIDTH) / 2, + y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - USERPREFS_OEM_IMAGE_HEIGHT) / 2 + 2, USERPREFS_OEM_IMAGE_WIDTH, + USERPREFS_OEM_IMAGE_HEIGHT, xbm); + + switch (USERPREFS_OEM_FONT_SIZE) { + case 0: + display->setFont(FONT_SMALL); + break; + case 2: + display->setFont(FONT_LARGE); + break; + default: + display->setFont(FONT_MEDIUM); + break; + } + + display->setTextAlignment(TEXT_ALIGN_LEFT); + const char *title = USERPREFS_OEM_TEXT; + display->drawString(x + getStringCenteredX(title), y + SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM, title); + display->setFont(FONT_SMALL); + + // Draw region in upper left + if (upperMsg) + display->drawString(x + 0, y + 0, upperMsg); + + // Draw version and shortname in upper right + char buf[25]; + snprintf(buf, sizeof(buf), "%s\n%s", xstr(APP_VERSION_SHORT), haveGlyphs(owner.short_name) ? owner.short_name : ""); + + display->setTextAlignment(TEXT_ALIGN_RIGHT); + display->drawString(x + SCREEN_WIDTH, y + 0, buf); + screen->forceDisplay(); + + display->setTextAlignment(TEXT_ALIGN_LEFT); // Restore left align, just to be kind to any other unsuspecting code +} + +static void drawOEMBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) +{ + // Draw region in upper left + const char *region = myRegion ? myRegion->name : NULL; + drawOEMIconScreen(region, display, state, x, y); +} + +#endif + void Screen::drawFrameText(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y, const char *message) { uint16_t x_offset = display->width() / 2; @@ -1400,9 +1446,9 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ static char distStr[20]; if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) { - strncpy(distStr, "? mi", sizeof(distStr)); // might not have location data + strncpy(distStr, "? mi ?°", sizeof(distStr)); // might not have location data } else { - strncpy(distStr, "? km", sizeof(distStr)); + strncpy(distStr, "? km ?°", sizeof(distStr)); } meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); const char *fields[] = {username, lastStr, signalStr, distStr, NULL}; @@ -1435,18 +1481,6 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ float d = GeoCoord::latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i)); - if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) { - if (d < (2 * MILES_TO_FEET)) - snprintf(distStr, sizeof(distStr), "%.0f ft", d * METERS_TO_FEET); - else - snprintf(distStr, sizeof(distStr), "%.1f mi", d * METERS_TO_FEET / MILES_TO_FEET); - } else { - if (d < 2000) - snprintf(distStr, sizeof(distStr), "%.0f m", d); - else - snprintf(distStr, sizeof(distStr), "%.1f km", d / 1000); - } - float bearingToOther = GeoCoord::bearing(DegD(op.latitude_i), DegD(op.longitude_i), DegD(p.latitude_i), DegD(p.longitude_i)); // If the top of the compass is a static north then bearingToOther can be drawn on the compass directly @@ -1454,6 +1488,22 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ if (!config.display.compass_north_top) bearingToOther -= myHeading; screen->drawNodeHeading(display, compassX, compassY, compassDiam, bearingToOther); + + float bearingToOtherDegrees = (bearingToOther < 0) ? bearingToOther + 2 * PI : bearingToOther; + bearingToOtherDegrees = bearingToOtherDegrees * 180 / PI; + + if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) { + if (d < (2 * MILES_TO_FEET)) + snprintf(distStr, sizeof(distStr), "%.0fft %.0f°", d * METERS_TO_FEET, bearingToOtherDegrees); + else + snprintf(distStr, sizeof(distStr), "%.1fmi %.0f°", d * METERS_TO_FEET / MILES_TO_FEET, + bearingToOtherDegrees); + } else { + if (d < 2000) + snprintf(distStr, sizeof(distStr), "%.0fm %.0f°", d, bearingToOtherDegrees); + else + snprintf(distStr, sizeof(distStr), "%.1fkm %.0f°", d / 1000, bearingToOtherDegrees); + } } } if (!hasNodeHeading) { @@ -1658,6 +1708,10 @@ void Screen::setup() // Set the utf8 conversion function dispdev->setFontTableLookupFunction(customFontTableLookup); +#ifdef USERPREFS_OEM_TEXT + logo_timeout *= 2; // Double the time if we have a custom logo +#endif + // Add frames. EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); alertFrames[0] = [this](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void { @@ -1803,6 +1857,22 @@ int32_t Screen::runOnce() showingBootScreen = false; } +#ifdef USERPREFS_OEM_TEXT + static bool showingOEMBootScreen = true; + if (showingOEMBootScreen && (millis() > ((logo_timeout / 2) + serialSinceMsec))) { + LOG_INFO("Switch to OEM screen..."); + // Change frames. + static FrameCallback bootOEMFrames[] = {drawOEMBootScreen}; + static const int bootOEMFrameCount = sizeof(bootOEMFrames) / sizeof(bootOEMFrames[0]); + ui->setFrames(bootOEMFrames, bootOEMFrameCount); + ui->update(); +#ifndef USE_EINK + ui->update(); +#endif + showingOEMBootScreen = false; + } +#endif + #ifndef DISABLE_WELCOME_UNSET if (showingNormalScreen && config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET) { setWelcomeFrames(); @@ -2578,13 +2648,12 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat display->drawString(x + 1, y, String("USB")); } - auto mode = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, true); + // auto mode = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, true); - display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode), y, mode); - if (config.display.heading_bold) - display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode) - 1, y, mode); + // display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode), y, mode); + // if (config.display.heading_bold) + // display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode) - 1, y, mode); - // Line 2 uint32_t currentMillis = millis(); uint32_t seconds = currentMillis / 1000; uint32_t minutes = seconds / 60; @@ -2597,6 +2666,9 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat display->setColor(WHITE); + // Setup string to assemble analogClock string + std::string analogClock = ""; + // Show uptime as days, hours, minutes OR seconds std::string uptime = screen->drawTimeDelta(days, hours, minutes, seconds); @@ -2613,17 +2685,36 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat int min = (hms % SEC_PER_HOUR) / SEC_PER_MIN; int sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN - char timebuf[10]; - snprintf(timebuf, sizeof(timebuf), " %02d:%02d:%02d", hour, min, sec); - uptime += timebuf; + char timebuf[12]; + + if (config.display.use_12h_clock) { + std::string meridiem = "am"; + if (hour >= 12) { + if (hour > 12) + hour -= 12; + meridiem = "pm"; + } + if (hour == 00) { + hour = 12; + } + snprintf(timebuf, sizeof(timebuf), "%d:%02d:%02d%s", hour, min, sec, meridiem.c_str()); + } else { + snprintf(timebuf, sizeof(timebuf), "%02d:%02d:%02d", hour, min, sec); + } + analogClock += timebuf; } - display->drawString(x, y + FONT_HEIGHT_SMALL * 1, uptime.c_str()); + // Line 1 + display->drawString(x + SCREEN_WIDTH - display->getStringWidth(uptime.c_str()), y, uptime.c_str()); + + // Line 2 + display->drawString(x, y + FONT_HEIGHT_SMALL * 1, analogClock.c_str()); // Display Channel Utilization char chUtil[13]; snprintf(chUtil, sizeof(chUtil), "ChUtil %2.0f%%", airTime->channelUtilizationPercent()); display->drawString(x + SCREEN_WIDTH - display->getStringWidth(chUtil), y + FONT_HEIGHT_SMALL * 1, chUtil); + #if HAS_GPS if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) { // Line 3 @@ -2756,4 +2847,4 @@ int Screen::handleAdminMessage(const meshtastic_AdminMessage *arg) } // namespace graphics #else graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {} -#endif // HAS_SCREEN +#endif // HAS_SCREEN \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index ba84513f2..807252510 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -115,10 +115,6 @@ AccelerometerThread *accelerometerThread = nullptr; AudioThread *audioThread = nullptr; #endif -#if defined(TCXO_OPTIONAL) -float tcxoVoltage = SX126X_DIO3_TCXO_VOLTAGE; // if TCXO is optional, put this here so it can be changed further down. -#endif - using namespace concurrency; volatile static const char slipstreamTZString[] = USERPREFS_TZ_STRING; @@ -930,13 +926,16 @@ void setup() #if defined(USE_SX1262) && !defined(ARCH_PORTDUINO) && !defined(TCXO_OPTIONAL) && RADIOLIB_EXCLUDE_SX126X != 1 if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) { - rIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY); - if (!rIf->init()) { + auto *sxIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY); +#ifdef SX126X_DIO3_TCXO_VOLTAGE + sxIf->setTCXOVoltage(SX126X_DIO3_TCXO_VOLTAGE); +#endif + if (!sxIf->init()) { LOG_WARN("No SX1262 radio"); - delete rIf; - rIf = NULL; + delete sxIf; } else { LOG_INFO("SX1262 init success"); + rIf = sxIf; radioType = SX1262_RADIO; } } @@ -944,29 +943,28 @@ void setup() #if defined(USE_SX1262) && !defined(ARCH_PORTDUINO) && defined(TCXO_OPTIONAL) if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) { - // Try using the specified TCXO voltage - rIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY); - if (!rIf->init()) { - LOG_WARN("No SX1262 radio with TCXO, Vref %f V", tcxoVoltage); - delete rIf; - rIf = NULL; - tcxoVoltage = 0; // if it fails, set the TCXO voltage to zero for the next attempt + // try using the specified TCXO voltage + auto *sxIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY); + sxIf->setTCXOVoltage(SX126X_DIO3_TCXO_VOLTAGE); + if (!sxIf->init()) { + LOG_WARN("No SX1262 radio with TCXO, Vref %fV", SX126X_DIO3_TCXO_VOLTAGE); + delete sxIf; } else { - LOG_WARN("SX1262 init success, TCXO, Vref %f V", tcxoVoltage); + LOG_INFO("SX1262 init success, TCXO, Vref %fV", SX126X_DIO3_TCXO_VOLTAGE); + rIf = sxIf; radioType = SX1262_RADIO; } } if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) { - // If specified TCXO voltage fails, attempt to use DIO3 as a reference instea + // If specified TCXO voltage fails, attempt to use DIO3 as a reference instead rIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY); if (!rIf->init()) { - LOG_WARN("No SX1262 radio with XTAL, Vref %f V", tcxoVoltage); + LOG_WARN("No SX1262 radio with XTAL, Vref 0.0V"); delete rIf; rIf = NULL; - tcxoVoltage = SX126X_DIO3_TCXO_VOLTAGE; // if it fails, set the TCXO voltage back for the next radio search } else { - LOG_INFO("SX1262 init success, XTAL, Vref %f V", tcxoVoltage); + LOG_INFO("SX1262 init success, XTAL, Vref 0.0V"); radioType = SX1262_RADIO; } } diff --git a/src/mesh/CryptoEngine.cpp b/src/mesh/CryptoEngine.cpp index 1624ab0d5..4613a6218 100644 --- a/src/mesh/CryptoEngine.cpp +++ b/src/mesh/CryptoEngine.cpp @@ -74,7 +74,7 @@ bool CryptoEngine::encryptCurve25519(uint32_t toNode, uint32_t fromNode, meshtas auth = bytesOut + numBytes; memcpy((uint8_t *)(auth + 8), &extraNonceTmp, sizeof(uint32_t)); // do not use dereference on potential non aligned pointers : *extraNonce = extraNonceTmp; - LOG_INFO("Random nonce value: %d", extraNonceTmp); + LOG_DEBUG("Random nonce value: %d", extraNonceTmp); if (remotePublic.size == 0) { LOG_DEBUG("Node %d or their public_key not found", toNode); return false; diff --git a/src/mesh/LR11x0Interface.cpp b/src/mesh/LR11x0Interface.cpp index ebde41017..a3402381f 100644 --- a/src/mesh/LR11x0Interface.cpp +++ b/src/mesh/LR11x0Interface.cpp @@ -21,12 +21,18 @@ static const Module::RfSwitchMode_t rfswitch_table[] = { // Particular boards might define a different max power based on what their hardware can do, default to max power output if not // specified (may be dangerous if using external PA and LR11x0 power config forgotten) +#if ARCH_PORTDUINO +#define LR1110_MAX_POWER settingsMap[lr1110_max_power] +#endif #ifndef LR1110_MAX_POWER #define LR1110_MAX_POWER 22 #endif // the 2.4G part maxes at 13dBm +#if ARCH_PORTDUINO +#define LR1120_MAX_POWER settingsMap[lr1120_max_power] +#endif #ifndef LR1120_MAX_POWER #define LR1120_MAX_POWER 13 #endif diff --git a/src/mesh/MeshModule.cpp b/src/mesh/MeshModule.cpp index 2f2863fa5..62d3c82bc 100644 --- a/src/mesh/MeshModule.cpp +++ b/src/mesh/MeshModule.cpp @@ -10,6 +10,7 @@ std::vector *MeshModule::modules; const meshtastic_MeshPacket *MeshModule::currentRequest; +uint8_t MeshModule::numPeriodicModules = 0; /** * If any of the current chain of modules has already sent a reply, it will be here. This is useful to allow @@ -35,6 +36,15 @@ MeshModule::~MeshModule() modules->erase(it); } +// âš ï¸ **Only call once** to set the initial delay before a module starts broadcasting periodically +int32_t MeshModule::setStartDelay() +{ + int32_t startDelay = MESHMODULE_MIN_BROADCAST_DELAY_MS + numPeriodicModules * MESHMODULE_BROADCAST_SPACING_MS; + numPeriodicModules++; + + return startDelay; +} + meshtastic_MeshPacket *MeshModule::allocAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, uint8_t hopLimit) { diff --git a/src/mesh/MeshModule.h b/src/mesh/MeshModule.h index a88f1e6ff..f08b8f49c 100644 --- a/src/mesh/MeshModule.h +++ b/src/mesh/MeshModule.h @@ -9,6 +9,9 @@ #include #endif +#define MESHMODULE_MIN_BROADCAST_DELAY_MS 30 * 1000 // Min. delay after boot before sending first broadcast by any module +#define MESHMODULE_BROADCAST_SPACING_MS 15 * 1000 // Initial spacing between broadcasts of different modules + /** handleReceived return enumeration * * Use ProcessMessage::CONTINUE to allows other modules to process a message. @@ -119,6 +122,12 @@ class MeshModule */ static const meshtastic_MeshPacket *currentRequest; + // We keep track of the number of modules that send a periodic broadcast to schedule them spaced out over time + static uint8_t numPeriodicModules; + + // Set the start delay for module that broadcasts periodically + int32_t setStartDelay(); + /** * If your handler wants to send a response, simply set currentReply and it will be sent at the end of response handling. */ diff --git a/src/mesh/MeshService.h b/src/mesh/MeshService.h index 175d8a595..42f701d5c 100644 --- a/src/mesh/MeshService.h +++ b/src/mesh/MeshService.h @@ -64,7 +64,8 @@ class MeshService return true; } return p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP || - p->decoded.portnum == meshtastic_PortNum_DETECTION_SENSOR_APP; + p->decoded.portnum == meshtastic_PortNum_DETECTION_SENSOR_APP || + p->decoded.portnum == meshtastic_PortNum_ALERT_APP; } /// Called when some new packets have arrived from one of the radios Observable fromNumChanged; diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 762982287..9caa03928 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -23,7 +23,6 @@ #include "modules/NeighborInfoModule.h" #include #include -#include #include #include #include @@ -408,7 +407,7 @@ bool NodeDB::resetRadioConfig(bool factory_reset) rebootAtMsec = millis() + (5 * 1000); } -#if (defined(T_DECK) || defined(T_WATCH_S3) || defined(UNPHONE) || defined(PICOMPUTER_S3)) && defined(HAS_TFT) +#if (defined(T_DECK) || defined(T_WATCH_S3) || defined(UNPHONE) || defined(PICOMPUTER_S3)) && HAS_TFT // as long as PhoneAPI shares BT and TFT app switch BT off config.bluetooth.enabled = false; if (moduleConfig.external_notification.nag_timeout == 60) @@ -1529,4 +1528,4 @@ void recordCriticalError(meshtastic_CriticalErrorCode code, uint32_t address, co LOG_ERROR("A critical failure occurred, portduino is exiting"); exit(2); #endif -} \ No newline at end of file +} diff --git a/src/mesh/RF95Interface.cpp b/src/mesh/RF95Interface.cpp index 433bcc932..a488d30e7 100644 --- a/src/mesh/RF95Interface.cpp +++ b/src/mesh/RF95Interface.cpp @@ -9,6 +9,9 @@ #include "PortduinoGlue.h" #endif +#if ARCH_PORTDUINO +#define RF95_MAX_POWER settingsMap[rf95_max_power] +#endif #ifndef RF95_MAX_POWER #define RF95_MAX_POWER 20 #endif @@ -331,4 +334,4 @@ bool RF95Interface::sleep() return true; } -#endif +#endif \ No newline at end of file diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp index 3eae23d27..e7697c872 100644 --- a/src/mesh/RadioLibInterface.cpp +++ b/src/mesh/RadioLibInterface.cpp @@ -340,8 +340,11 @@ void RadioLibInterface::clampToLateRebroadcastWindow(NodeNum from, PacketId id) meshtastic_MeshPacket *p = txQueue.remove(from, id, true, false); if (p) { p->tx_after = millis() + getTxDelayMsecWeightedWorst(p->rx_snr); - txQueue.enqueue(p); - LOG_DEBUG("Move existing queued packet to the late rebroadcast window %dms from now", p->tx_after - millis()); + if (txQueue.enqueue(p)) { + LOG_DEBUG("Move existing queued packet to the late rebroadcast window %dms from now", p->tx_after - millis()); + } else { + packetPool.release(p); + } } } diff --git a/src/mesh/STM32WLE5JCInterface.cpp b/src/mesh/STM32WLE5JCInterface.cpp index 499db9176..ad1f675b6 100644 --- a/src/mesh/STM32WLE5JCInterface.cpp +++ b/src/mesh/STM32WLE5JCInterface.cpp @@ -18,6 +18,9 @@ bool STM32WLE5JCInterface::init() { RadioLibInterface::init(); + // https://github.com/Seeed-Studio/LoRaWan-E5-Node/blob/main/Middlewares/Third_Party/SubGHz_Phy/stm32_radio_driver/radio_driver.c + setTCXOVoltage(1.7); + lora.setRfSwitchTable(rfswitch_pins, rfswitch_table); if (power > STM32WLx_MAX_POWER) // This chip has lower power limits than some @@ -39,4 +42,4 @@ bool STM32WLE5JCInterface::init() return res == RADIOLIB_ERR_NONE; } -#endif // ARCH_STM32WL +#endif // ARCH_STM32WL \ No newline at end of file diff --git a/src/mesh/STM32WLE5JCInterface.h b/src/mesh/STM32WLE5JCInterface.h index fad793332..0c8140290 100644 --- a/src/mesh/STM32WLE5JCInterface.h +++ b/src/mesh/STM32WLE5JCInterface.h @@ -16,9 +16,6 @@ class STM32WLE5JCInterface : public SX126xInterface virtual bool init() override; }; -// https://github.com/Seeed-Studio/LoRaWan-E5-Node/blob/main/Middlewares/Third_Party/SubGHz_Phy/stm32_radio_driver/radio_driver.c -static const float tcxoVoltage = 1.7; - /* https://wiki.seeedstudio.com/LoRa-E5_STM32WLE5JC_Module/ * Wio-E5 module ONLY transmits through RFO_HP * Receive: PA4=1, PA5=0 diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index 45f65440c..d64519fb6 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -11,6 +11,9 @@ // Particular boards might define a different max power based on what their hardware can do, default to max power output if not // specified (may be dangerous if using external PA and SX126x power config forgotten) +#if ARCH_PORTDUINO +#define SX126X_MAX_POWER settingsMap[sx126x_max_power] +#endif #ifndef SX126X_MAX_POWER #define SX126X_MAX_POWER 22 #endif @@ -52,22 +55,13 @@ template bool SX126xInterface::init() enableFan(); #if ARCH_PORTDUINO - float tcxoVoltage = (float)settingsMap[dio3_tcxo_voltage] / 1000; + tcxoVoltage = (float)settingsMap[dio3_tcxo_voltage] / 1000; if (settingsMap[sx126x_ant_sw_pin] != RADIOLIB_NC) { digitalWrite(settingsMap[sx126x_ant_sw_pin], HIGH); pinMode(settingsMap[sx126x_ant_sw_pin], OUTPUT); } -// FIXME: correct logic to default to not using TCXO if no voltage is specified for SX126X_DIO3_TCXO_VOLTAGE -#elif !defined(SX126X_DIO3_TCXO_VOLTAGE) - float tcxoVoltage = - 0; // "TCXO reference voltage to be set on DIO3. Defaults to 1.6 V, set to 0 to skip." per - // https://github.com/jgromes/RadioLib/blob/690a050ebb46e6097c5d00c371e961c1caa3b52e/src/modules/SX126x/SX126x.h#L471C26-L471C104 - // (DIO3 is free to be used as an IRQ) -#elif !defined(TCXO_OPTIONAL) - float tcxoVoltage = SX126X_DIO3_TCXO_VOLTAGE; - // (DIO3 is not free to be used as an IRQ) #endif - if (tcxoVoltage == 0) + if (tcxoVoltage == 0.0) LOG_DEBUG("SX126X_DIO3_TCXO_VOLTAGE not defined, not using DIO3 as TCXO reference voltage"); else LOG_DEBUG("SX126X_DIO3_TCXO_VOLTAGE defined, using DIO3 as TCXO reference voltage at %f V", tcxoVoltage); @@ -85,7 +79,7 @@ template bool SX126xInterface::init() int res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage, useRegulatorLDO); // \todo Display actual typename of the adapter, not just `SX126x` LOG_INFO("SX126x init result %d", res); - if (res == RADIOLIB_ERR_CHIP_NOT_FOUND) + if (res == RADIOLIB_ERR_CHIP_NOT_FOUND || res == RADIOLIB_ERR_SPI_CMD_FAILED) return false; LOG_INFO("Frequency set to %f", getFreq()); @@ -344,4 +338,4 @@ template bool SX126xInterface::sleep() return true; } -#endif +#endif \ No newline at end of file diff --git a/src/mesh/SX126xInterface.h b/src/mesh/SX126xInterface.h index 45b39a68a..47b07c284 100644 --- a/src/mesh/SX126xInterface.h +++ b/src/mesh/SX126xInterface.h @@ -28,8 +28,11 @@ template class SX126xInterface : public RadioLibInterface bool isIRQPending() override { return lora.getIrqFlags() != 0; } + void setTCXOVoltage(float voltage) { tcxoVoltage = voltage; } + protected: float currentLimit = 140; // Higher OCP limit for SX126x PA + float tcxoVoltage = 0.0; /** * Specific module instance diff --git a/src/mesh/SX128xInterface.cpp b/src/mesh/SX128xInterface.cpp index b9921fc87..6f960c6b3 100644 --- a/src/mesh/SX128xInterface.cpp +++ b/src/mesh/SX128xInterface.cpp @@ -10,6 +10,9 @@ #endif // Particular boards might define a different max power based on what their hardware can do +#if ARCH_PORTDUINO +#define SX128X_MAX_POWER settingsMap[sx128x_max_power] +#endif #ifndef SX128X_MAX_POWER #define SX128X_MAX_POWER 13 #endif @@ -312,4 +315,4 @@ template bool SX128xInterface::sleep() return true; } -#endif +#endif \ No newline at end of file diff --git a/src/mesh/compression/unishox2.h b/src/mesh/compression/unishox2.h index 5e2cc8b4c..823128f02 100644 --- a/src/mesh/compression/unishox2.h +++ b/src/mesh/compression/unishox2.h @@ -291,8 +291,8 @@ extern int unishox2_decompress_simple(const char *in, int len, char *out); * @param[in] olen length of 'out' buffer in bytes. Can be omitted if sufficient buffer is provided * @param[in] usx_hcodes Horizontal codes (array of bytes). See macro section for samples. * @param[in] usx_hcode_lens Length of each element in usx_hcodes array - * @param[in] usx_freq_seq Frequently occuring sequences. See USX_FREQ_SEQ_* macros for samples - * @param[in] usx_templates Templates of frequently occuring patterns. See USX_TEMPLATES macro. + * @param[in] usx_freq_seq Frequently occurring sequences. See USX_FREQ_SEQ_* macros for samples + * @param[in] usx_templates Templates of frequently occurring patterns. See USX_TEMPLATES macro. */ extern int unishox2_compress(const char *in, int len, UNISHOX_API_OUT_AND_LEN(char *out, int olen), const unsigned char usx_hcodes[], const unsigned char usx_hcode_lens[], const char *usx_freq_seq[], @@ -310,8 +310,8 @@ extern int unishox2_compress(const char *in, int len, UNISHOX_API_OUT_AND_LEN(ch * @param[in] olen length of 'out' buffer in bytes. Can be omitted if sufficient buffer is provided * @param[in] usx_hcodes Horizontal codes (array of bytes). See macro section for samples. * @param[in] usx_hcode_lens Length of each element in usx_hcodes array - * @param[in] usx_freq_seq Frequently occuring sequences. See USX_FREQ_SEQ_* macros for samples - * @param[in] usx_templates Templates of frequently occuring patterns. See USX_TEMPLATES macro. + * @param[in] usx_freq_seq Frequently occurring sequences. See USX_FREQ_SEQ_* macros for samples + * @param[in] usx_templates Templates of frequently occurring patterns. See USX_TEMPLATES macro. */ extern int unishox2_decompress(const char *in, int len, UNISHOX_API_OUT_AND_LEN(char *out, int olen), const unsigned char usx_hcodes[], const unsigned char usx_hcode_lens[], const char *usx_freq_seq[], @@ -344,4 +344,4 @@ extern int unishox2_decompress_lines(const char *in, int len, UNISHOX_API_OUT_AN const unsigned char usx_hcodes[], const unsigned char usx_hcode_lens[], const char *usx_freq_seq[], const char *usx_templates[], struct us_lnk_lst *prev_lines); -#endif \ No newline at end of file +#endif diff --git a/src/mesh/eth/ethClient.cpp b/src/mesh/eth/ethClient.cpp index 24c4f0db1..70c6e3fe4 100644 --- a/src/mesh/eth/ethClient.cpp +++ b/src/mesh/eth/ethClient.cpp @@ -5,9 +5,6 @@ #include "configuration.h" #include "main.h" #include "mesh/api/ethServerAPI.h" -#if !MESHTASTIC_EXCLUDE_MQTT -#include "mqtt/MQTT.h" -#endif #include "target_specific.h" #include #include @@ -72,12 +69,6 @@ static int32_t reconnectETH() ethStartupComplete = true; } -#if !MESHTASTIC_EXCLUDE_MQTT - // FIXME this is kinda yucky, instead we should just have an observable for 'wifireconnected' - if (mqtt && !moduleConfig.mqtt.proxy_to_client_enabled && !mqtt->isConnectedDirectly()) { - mqtt->reconnect(); - } -#endif } #ifndef DISABLE_NTP diff --git a/src/mesh/generated/meshtastic/config.pb.h b/src/mesh/generated/meshtastic/config.pb.h index 14aed9dfe..4747ddb5a 100644 --- a/src/mesh/generated/meshtastic/config.pb.h +++ b/src/mesh/generated/meshtastic/config.pb.h @@ -468,6 +468,9 @@ typedef struct _meshtastic_Config_DisplayConfig { bool wake_on_tap_or_motion; /* Indicates how to rotate or invert the compass output to accurate display on the display. */ meshtastic_Config_DisplayConfig_CompassOrientation compass_orientation; + /* If false (default), the device will display the time in 24-hour format on screen. + If true, the device will display the time in 12-hour format on screen. */ + bool use_12h_clock; } meshtastic_Config_DisplayConfig; /* Lora Config */ @@ -690,7 +693,7 @@ extern "C" { #define meshtastic_Config_PowerConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_Config_NetworkConfig_init_default {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_default, "", 0} #define meshtastic_Config_NetworkConfig_IpV4Config_init_default {0, 0, 0, 0} -#define meshtastic_Config_DisplayConfig_init_default {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN} +#define meshtastic_Config_DisplayConfig_init_default {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN, 0} #define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0} #define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0} #define meshtastic_Config_SecurityConfig_init_default {{0, {0}}, {0, {0}}, 0, {{0, {0}}, {0, {0}}, {0, {0}}}, 0, 0, 0, 0} @@ -701,7 +704,7 @@ extern "C" { #define meshtastic_Config_PowerConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_Config_NetworkConfig_init_zero {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_zero, "", 0} #define meshtastic_Config_NetworkConfig_IpV4Config_init_zero {0, 0, 0, 0} -#define meshtastic_Config_DisplayConfig_init_zero {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN} +#define meshtastic_Config_DisplayConfig_init_zero {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN, 0} #define meshtastic_Config_LoRaConfig_init_zero {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0} #define meshtastic_Config_BluetoothConfig_init_zero {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0} #define meshtastic_Config_SecurityConfig_init_zero {{0, {0}}, {0, {0}}, 0, {{0, {0}}, {0, {0}}, {0, {0}}}, 0, 0, 0, 0} @@ -765,6 +768,7 @@ extern "C" { #define meshtastic_Config_DisplayConfig_heading_bold_tag 9 #define meshtastic_Config_DisplayConfig_wake_on_tap_or_motion_tag 10 #define meshtastic_Config_DisplayConfig_compass_orientation_tag 11 +#define meshtastic_Config_DisplayConfig_use_12h_clock_tag 12 #define meshtastic_Config_LoRaConfig_use_preset_tag 1 #define meshtastic_Config_LoRaConfig_modem_preset_tag 2 #define meshtastic_Config_LoRaConfig_bandwidth_tag 3 @@ -907,7 +911,8 @@ X(a, STATIC, SINGULAR, UENUM, oled, 7) \ X(a, STATIC, SINGULAR, UENUM, displaymode, 8) \ X(a, STATIC, SINGULAR, BOOL, heading_bold, 9) \ X(a, STATIC, SINGULAR, BOOL, wake_on_tap_or_motion, 10) \ -X(a, STATIC, SINGULAR, UENUM, compass_orientation, 11) +X(a, STATIC, SINGULAR, UENUM, compass_orientation, 11) \ +X(a, STATIC, SINGULAR, BOOL, use_12h_clock, 12) #define meshtastic_Config_DisplayConfig_CALLBACK NULL #define meshtastic_Config_DisplayConfig_DEFAULT NULL @@ -985,7 +990,7 @@ extern const pb_msgdesc_t meshtastic_Config_SessionkeyConfig_msg; #define MESHTASTIC_MESHTASTIC_CONFIG_PB_H_MAX_SIZE meshtastic_Config_size #define meshtastic_Config_BluetoothConfig_size 10 #define meshtastic_Config_DeviceConfig_size 98 -#define meshtastic_Config_DisplayConfig_size 30 +#define meshtastic_Config_DisplayConfig_size 32 #define meshtastic_Config_LoRaConfig_size 85 #define meshtastic_Config_NetworkConfig_IpV4Config_size 20 #define meshtastic_Config_NetworkConfig_size 202 diff --git a/src/mesh/generated/meshtastic/localonly.pb.h b/src/mesh/generated/meshtastic/localonly.pb.h index dc0f507c9..7a6712bf0 100644 --- a/src/mesh/generated/meshtastic/localonly.pb.h +++ b/src/mesh/generated/meshtastic/localonly.pb.h @@ -187,7 +187,7 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg; /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalConfig_size -#define meshtastic_LocalConfig_size 741 +#define meshtastic_LocalConfig_size 743 #define meshtastic_LocalModuleConfig_size 699 #ifdef __cplusplus diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 5cd23c8e3..de8a1a353 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -223,6 +223,9 @@ typedef enum _meshtastic_HardwareModel { /* Mesh-Tab, esp32 based https://github.com/valzzu/Mesh-Tab */ meshtastic_HardwareModel_MESH_TAB = 86, + /* MeshLink board developed by LoraItalia. NRF52840, eByte E22900M22S (Will also come with other frequencies), 25w MPPT solar charger (5v,12v,18v selectable), support for gps, buzzer, oled or e-ink display, 10 gpios, hardware watchdog + https://www.loraitalia.it */ + meshtastic_HardwareModel_MESHLINK = 87, /* ------------------------------------------------------------------------------------------------------------------------------------------ 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. ------------------------------------------------------------------------------------------------------------------------------------------ */ @@ -1772,4 +1775,4 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg; } /* extern "C" */ #endif -#endif +#endif \ No newline at end of file diff --git a/src/mesh/generated/meshtastic/module_config.pb.h b/src/mesh/generated/meshtastic/module_config.pb.h index 697b965c5..848b010d3 100644 --- a/src/mesh/generated/meshtastic/module_config.pb.h +++ b/src/mesh/generated/meshtastic/module_config.pb.h @@ -347,7 +347,7 @@ typedef struct _meshtastic_ModuleConfig_TelemetryConfig { bool health_screen_enabled; } meshtastic_ModuleConfig_TelemetryConfig; -/* TODO: REPLACE */ +/* Canned Messages Module Config */ typedef struct _meshtastic_ModuleConfig_CannedMessageConfig { /* Enable the rotary encoder #1. This is a 'dumb' encoder sending pulses on both A and B pins while rotating. */ bool rotary1_enabled; diff --git a/src/mesh/raspihttp/PiWebServer.cpp b/src/mesh/raspihttp/PiWebServer.cpp index 9d2625410..4fae0bc3d 100644 --- a/src/mesh/raspihttp/PiWebServer.cpp +++ b/src/mesh/raspihttp/PiWebServer.cpp @@ -65,6 +65,9 @@ mail: marchammermann@googlemail.com #define DEFAULT_REALM "default_realm" #define PREFIX "" +#define KEY_PATH settingsStrings[websslkeypath].c_str() +#define CERT_PATH settingsStrings[websslcertpath].c_str() + struct _file_config configWeb; // We need to specify some content-type mapping, so the resources get delivered with the @@ -384,13 +387,13 @@ char *read_file_into_string(const char *filename) int PiWebServerThread::CheckSSLandLoad() { // read certificate - cert_pem = read_file_into_string("certificate.pem"); + cert_pem = read_file_into_string(CERT_PATH); if (cert_pem == NULL) { LOG_ERROR("ERROR SSL Certificate File can't be loaded or is missing"); return 1; } // read private key - key_pem = read_file_into_string("private_key.pem"); + key_pem = read_file_into_string(KEY_PATH); if (key_pem == NULL) { LOG_ERROR("ERROR file private_key can't be loaded or is missing"); return 2; @@ -415,8 +418,8 @@ int PiWebServerThread::CreateSSLCertificate() return 2; } - // Ope file to write private key file - FILE *pkey_file = fopen("private_key.pem", "wb"); + // Open file to write private key file + FILE *pkey_file = fopen(KEY_PATH, "wb"); if (!pkey_file) { LOG_ERROR("Error opening private key file"); return 3; @@ -426,18 +429,19 @@ int PiWebServerThread::CreateSSLCertificate() fclose(pkey_file); // open Certificate file - FILE *x509_file = fopen("certificate.pem", "wb"); + FILE *x509_file = fopen(CERT_PATH, "wb"); if (!x509_file) { LOG_ERROR("Error opening cert"); return 4; } - // write cirtificate + // write certificate PEM_write_X509(x509_file, x509); fclose(x509_file); EVP_PKEY_free(pkey); + LOG_INFO("Create SSL Key %s successful", KEY_PATH); X509_free(x509); - LOG_INFO("Create SSL Cert -certificate.pem- succesfull "); + LOG_INFO("Create SSL Cert %s successful", CERT_PATH); return 0; } diff --git a/src/mesh/wifi/WiFiAPClient.cpp b/src/mesh/wifi/WiFiAPClient.cpp index dcfcdc047..d4a5dbf94 100644 --- a/src/mesh/wifi/WiFiAPClient.cpp +++ b/src/mesh/wifi/WiFiAPClient.cpp @@ -7,9 +7,6 @@ #include "main.h" #include "mesh/api/WiFiServerAPI.h" -#if !MESHTASTIC_EXCLUDE_MQTT -#include "mqtt/MQTT.h" -#endif #include "target_specific.h" #include #include @@ -111,12 +108,6 @@ static void onNetworkConnected() #endif APStartupComplete = true; } - - // FIXME this is kinda yucky, instead we should just have an observable for 'wifireconnected' -#ifndef MESHTASTIC_EXCLUDE_MQTT - if (mqtt) - mqtt->reconnect(); -#endif } static int32_t reconnectWiFi() @@ -225,7 +216,7 @@ bool initWifi() #if !MESHTASTIC_EXCLUDE_WEBSERVER createSSLCert(); // For WebServer #endif - esp_wifi_set_storage(WIFI_STORAGE_RAM); // Disable flash storage for WiFi credentials + WiFi.persistent(false); // Disable flash storage for WiFi credentials #endif if (!*wifiPsw) // Treat empty password as no password wifiPsw = NULL; diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index ea03de8e7..72ac39db7 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -162,7 +162,9 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta case meshtastic_AdminMessage_set_module_config_tag: LOG_INFO("Client set module config"); - handleSetModuleConfig(r->set_module_config); + if (!handleSetModuleConfig(r->set_module_config)) { + myReply = allocErrorResponse(meshtastic_Routing_Error_BAD_REQUEST, &mp); + } break; case meshtastic_AdminMessage_set_channel_tag: @@ -656,15 +658,23 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) saveChanges(changes, requiresReboot); } -void AdminModule::handleSetModuleConfig(const meshtastic_ModuleConfig &c) +bool AdminModule::handleSetModuleConfig(const meshtastic_ModuleConfig &c) { if (!hasOpenEditTransaction) disableBluetooth(); switch (c.which_payload_variant) { case meshtastic_ModuleConfig_mqtt_tag: +#if MESHTASTIC_EXCLUDE_MQTT + LOG_WARN("Set module config: MESHTASTIC_EXCLUDE_MQTT is defined. Not setting MQTT config"); + return false; +#else LOG_INFO("Set module config: MQTT"); + if (!MQTT::isValidConfig(c.payload_variant.mqtt)) { + return false; + } moduleConfig.has_mqtt = true; moduleConfig.mqtt = c.payload_variant.mqtt; +#endif break; case meshtastic_ModuleConfig_serial_tag: LOG_INFO("Set module config: Serial"); @@ -732,6 +742,7 @@ void AdminModule::handleSetModuleConfig(const meshtastic_ModuleConfig &c) break; } saveChanges(SEGMENT_MODULECONFIG); + return true; } void AdminModule::handleSetChannel(const meshtastic_Channel &cc) @@ -1168,4 +1179,4 @@ void disableBluetooth() nrf52Bluetooth->shutdown(); #endif #endif -} +} \ No newline at end of file diff --git a/src/modules/AdminModule.h b/src/modules/AdminModule.h index ee2ebfd96..12c857e04 100644 --- a/src/modules/AdminModule.h +++ b/src/modules/AdminModule.h @@ -50,7 +50,7 @@ class AdminModule : public ProtobufModule, public Obser void handleSetOwner(const meshtastic_User &o); void handleSetChannel(const meshtastic_Channel &cc); void handleSetConfig(const meshtastic_Config &c); - void handleSetModuleConfig(const meshtastic_ModuleConfig &c); + bool handleSetModuleConfig(const meshtastic_ModuleConfig &c); void handleSetChannel(); void handleSetHamMode(const meshtastic_HamParameters &req); void handleStoreDeviceUIConfig(const meshtastic_DeviceUIConfig &uicfg); diff --git a/src/modules/DetectionSensorModule.cpp b/src/modules/DetectionSensorModule.cpp index c479867fc..ca682b772 100644 --- a/src/modules/DetectionSensorModule.cpp +++ b/src/modules/DetectionSensorModule.cpp @@ -81,7 +81,7 @@ int32_t DetectionSensorModule::runOnce() } LOG_INFO("Detection Sensor Module: init"); - return DELAYED_INTERVAL; + return setStartDelay(); } // LOG_DEBUG("Detection Sensor Module: Current pin state: %i", digitalRead(moduleConfig.detection_sensor.monitor_pin)); @@ -161,4 +161,4 @@ bool DetectionSensorModule::hasDetectionEvent() bool currentState = digitalRead(moduleConfig.detection_sensor.monitor_pin); // LOG_DEBUG("Detection Sensor Module: Current state: %i", currentState); return (moduleConfig.detection_sensor.detection_trigger_type & 1) ? currentState : !currentState; -} +} \ No newline at end of file diff --git a/src/modules/NeighborInfoModule.cpp b/src/modules/NeighborInfoModule.cpp index fb658421d..eebf428a4 100644 --- a/src/modules/NeighborInfoModule.cpp +++ b/src/modules/NeighborInfoModule.cpp @@ -121,7 +121,8 @@ Will be used for broadcast. */ int32_t NeighborInfoModule::runOnce() { - if (moduleConfig.neighbor_info.transmit_over_lora && !channels.isDefaultChannel(channels.getPrimaryIndex()) && + if (moduleConfig.neighbor_info.transmit_over_lora && + (!channels.isDefaultChannel(channels.getPrimaryIndex()) || !RadioInterface::uses_default_frequency_slot) && airTime->isTxAllowedChannelUtil(true) && airTime->isTxAllowedAirUtil()) { sendNeighborInfo(NODENUM_BROADCAST, false); } else { diff --git a/src/modules/NodeInfoModule.cpp b/src/modules/NodeInfoModule.cpp index b55d47d5b..ce4a6bd06 100644 --- a/src/modules/NodeInfoModule.cpp +++ b/src/modules/NodeInfoModule.cpp @@ -97,8 +97,9 @@ NodeInfoModule::NodeInfoModule() : ProtobufModule("nodeinfo", meshtastic_PortNum_NODEINFO_APP, &meshtastic_User_msg), concurrency::OSThread("NodeInfo") { isPromiscuous = true; // We always want to update our nodedb, even if we are sniffing on others - setIntervalFromNow(30 * - 1000); // Send our initial owner announcement 30 seconds after we start (to give network time to setup) + + setIntervalFromNow(setStartDelay()); // Send our initial owner announcement 30 seconds + // after we start (to give network time to setup) } int32_t NodeInfoModule::runOnce() @@ -112,4 +113,4 @@ int32_t NodeInfoModule::runOnce() sendOurNodeInfo(NODENUM_BROADCAST, requestReplies); // Send our info (don't request replies) } return Default::getConfiguredOrDefaultMs(config.device.node_info_broadcast_secs, default_node_info_broadcast_secs); -} +} \ No newline at end of file diff --git a/src/modules/PositionModule.cpp b/src/modules/PositionModule.cpp index 6285d7aa5..acbc3143d 100644 --- a/src/modules/PositionModule.cpp +++ b/src/modules/PositionModule.cpp @@ -28,8 +28,9 @@ PositionModule::PositionModule() nodeStatusObserver.observe(&nodeStatus->onNewStatus); if (config.device.role != meshtastic_Config_DeviceConfig_Role_TRACKER && - config.device.role != meshtastic_Config_DeviceConfig_Role_TAK_TRACKER) - setIntervalFromNow(60 * 1000); + config.device.role != meshtastic_Config_DeviceConfig_Role_TAK_TRACKER) { + setIntervalFromNow(setStartDelay()); + } // Power saving trackers should clear their position on startup to avoid waking up and sending a stale position if ((config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER || @@ -160,7 +161,8 @@ bool PositionModule::hasGPS() #endif } -meshtastic_MeshPacket *PositionModule::allocReply() +// Allocate a packet with our position data if we have one +meshtastic_MeshPacket *PositionModule::allocPositionPacket() { if (precision == 0) { LOG_DEBUG("Skip location send because precision is set to 0!"); @@ -262,7 +264,8 @@ meshtastic_MeshPacket *PositionModule::allocReply() p.has_ground_speed = true; } - LOG_INFO("Position reply: time=%i lat=%i lon=%i", p.time, p.latitude_i, p.longitude_i); + LOG_INFO("Position packet: time=%i lat=%i lon=%i", p.time, p.latitude_i, p.longitude_i); + lastSentToMesh = millis(); // TAK Tracker devices should send their position in a TAK packet over the ATAK port if (config.device.role == meshtastic_Config_DeviceConfig_Role_TAK_TRACKER) @@ -271,6 +274,17 @@ meshtastic_MeshPacket *PositionModule::allocReply() return allocDataProtobuf(p); } +meshtastic_MeshPacket *PositionModule::allocReply() +{ + if (config.device.role != meshtastic_Config_DeviceConfig_Role_LOST_AND_FOUND && lastSentToMesh && + Throttle::isWithinTimespanMs(lastSentToMesh, 3 * 60 * 1000)) { + LOG_DEBUG("Skip Position reply since we sent it <3min ago"); + ignoreRequest = true; // Mark it as ignored for MeshModule + return nullptr; + } + return allocPositionPacket(); +} + meshtastic_MeshPacket *PositionModule::allocAtakPli() { LOG_INFO("Send TAK PLI packet"); @@ -333,9 +347,9 @@ void PositionModule::sendOurPosition(NodeNum dest, bool wantReplies, uint8_t cha precision = 0; } - meshtastic_MeshPacket *p = allocReply(); + meshtastic_MeshPacket *p = allocPositionPacket(); if (p == nullptr) { - LOG_DEBUG("allocReply returned a nullptr"); + LOG_DEBUG("allocPositionPacket returned a nullptr"); return; } diff --git a/src/modules/PositionModule.h b/src/modules/PositionModule.h index 1e4aa5d29..dc732a3db 100644 --- a/src/modules/PositionModule.h +++ b/src/modules/PositionModule.h @@ -55,6 +55,7 @@ class PositionModule : public ProtobufModule, private concu virtual int32_t runOnce() override; private: + meshtastic_MeshPacket *allocPositionPacket(); struct SmartPosition getDistanceTraveledSinceLastSend(meshtastic_PositionLite currentPosition); meshtastic_MeshPacket *allocAtakPli(); void trySetRtc(meshtastic_Position p, bool isLocal, bool forceUpdate = false); @@ -62,6 +63,7 @@ class PositionModule : public ProtobufModule, private concu void sendLostAndFoundText(); bool hasQualityTimesource(); bool hasGPS(); + uint32_t lastSentToMesh = 0; // Last time we sent our position to the mesh const uint32_t minimumTimeThreshold = Default::getConfiguredOrDefaultMs(config.position.broadcast_smart_minimum_interval_secs, 30); diff --git a/src/modules/SerialModule.cpp b/src/modules/SerialModule.cpp index bf53b1748..c6a95912b 100644 --- a/src/modules/SerialModule.cpp +++ b/src/modules/SerialModule.cpp @@ -60,7 +60,7 @@ SerialModule *serialModule; SerialModuleRadio *serialModuleRadio; -#if defined(TTGO_T_ECHO) || defined(CANARYONE) +#if defined(TTGO_T_ECHO) || defined(CANARYONE) || defined(MESHLINK) SerialModule::SerialModule() : StreamAPI(&Serial), concurrency::OSThread("Serial") {} static Print *serialPrint = &Serial; #elif defined(CONFIG_IDF_TARGET_ESP32C6) @@ -158,7 +158,7 @@ int32_t SerialModule::runOnce() Serial.begin(baud); Serial.setTimeout(moduleConfig.serial.timeout > 0 ? moduleConfig.serial.timeout : TIMEOUT); } -#elif !defined(TTGO_T_ECHO) && !defined(CANARYONE) +#elif !defined(TTGO_T_ECHO) && !defined(CANARYONE) && !defined(MESHLINK) if (moduleConfig.serial.rxd && moduleConfig.serial.txd) { #ifdef ARCH_RP2040 Serial2.setFIFOSize(RX_BUFFER); @@ -214,7 +214,7 @@ int32_t SerialModule::runOnce() } } -#if !defined(TTGO_T_ECHO) && !defined(CANARYONE) +#if !defined(TTGO_T_ECHO) && !defined(CANARYONE) && !defined(MESHLINK) else if ((moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_WS85)) { processWXSerial(); @@ -416,7 +416,7 @@ uint32_t SerialModule::getBaudRate() */ void SerialModule::processWXSerial() { -#if !defined(TTGO_T_ECHO) && !defined(CANARYONE) && !defined(CONFIG_IDF_TARGET_ESP32C6) +#if !defined(TTGO_T_ECHO) && !defined(CANARYONE) && !defined(CONFIG_IDF_TARGET_ESP32C6) && !defined(MESHLINK) static unsigned int lastAveraged = 0; static unsigned int averageIntervalMillis = 300000; // 5 minutes hard coded. static double dir_sum_sin = 0; diff --git a/src/modules/Telemetry/AirQualityTelemetry.cpp b/src/modules/Telemetry/AirQualityTelemetry.cpp index 6a8077f03..392bd6148 100644 --- a/src/modules/Telemetry/AirQualityTelemetry.cpp +++ b/src/modules/Telemetry/AirQualityTelemetry.cpp @@ -50,12 +50,12 @@ int32_t AirQualityTelemetryModule::runOnce() nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].first = found.address.address; nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].second = i2cScanner->fetchI2CBus(found.address); - return 1000; + return setStartDelay(); } #endif return disable(); } - return 1000; + return setStartDelay(); } return disable(); } else { diff --git a/src/modules/Telemetry/DeviceTelemetry.h b/src/modules/Telemetry/DeviceTelemetry.h index 19b7d5b01..a1d55a596 100644 --- a/src/modules/Telemetry/DeviceTelemetry.h +++ b/src/modules/Telemetry/DeviceTelemetry.h @@ -18,7 +18,7 @@ class DeviceTelemetryModule : private concurrency::OSThread, public ProtobufModu uptimeWrapCount = 0; uptimeLastMs = millis(); nodeStatusObserver.observe(&nodeStatus->onNewStatus); - setIntervalFromNow(45 * 1000); // Wait until NodeInfo is sent + setIntervalFromNow(setStartDelay()); // Wait until NodeInfo is sent } virtual bool wantUIFrame() { return false; } @@ -62,4 +62,4 @@ class DeviceTelemetryModule : private concurrency::OSThread, public ProtobufModu uint32_t uptimeWrapCount; uint32_t uptimeLastMs; -}; +}; \ No newline at end of file diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index 1af6347f2..3fa3e848a 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -107,8 +107,6 @@ int32_t EnvironmentTelemetryModule::runOnce() if (moduleConfig.telemetry.environment_measurement_enabled) { LOG_INFO("Environment Telemetry: init"); - // it's possible to have this module enabled, only for displaying values on the screen. - // therefore, we should only enable the sensor loop if measurement is also enabled #ifdef SENSECAP_INDICATOR result = indicatorSensor.runOnce(); #endif @@ -163,9 +161,17 @@ int32_t EnvironmentTelemetryModule::runOnce() result = max17048Sensor.runOnce(); if (cgRadSens.hasSensor()) result = cgRadSens.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 #endif } - return result; + // it's possible to have this module enabled, only for displaying values on the screen. + // therefore, we should only enable the sensor loop if measurement is also enabled + return result == UINT32_MAX ? disable() : setStartDelay(); } else { // if we somehow got to a second run of this module with measurement disabled, then just wait forever if (!moduleConfig.telemetry.environment_measurement_enabled) { @@ -480,6 +486,10 @@ bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m valid = valid && cgRadSens.getMetrics(m); hasSensor = true; } +#ifdef HAS_RAKPROT + valid = valid && rak9154Sensor.getMetrics(m); + hasSensor = true; +#endif #endif return valid && hasSensor; } diff --git a/src/modules/Telemetry/HealthTelemetry.cpp b/src/modules/Telemetry/HealthTelemetry.cpp index 1b9b49813..a2a18ba03 100644 --- a/src/modules/Telemetry/HealthTelemetry.cpp +++ b/src/modules/Telemetry/HealthTelemetry.cpp @@ -62,7 +62,7 @@ int32_t HealthTelemetryModule::runOnce() if (max30102Sensor.hasSensor()) result = max30102Sensor.runOnce(); } - return result; + return result == UINT32_MAX ? disable() : setStartDelay(); } else { // if we somehow got to a second run of this module with measurement disabled, then just wait forever if (!moduleConfig.telemetry.health_measurement_enabled) { diff --git a/src/modules/Telemetry/PowerTelemetry.cpp b/src/modules/Telemetry/PowerTelemetry.cpp index 9c794e31e..04bcbe200 100644 --- a/src/modules/Telemetry/PowerTelemetry.cpp +++ b/src/modules/Telemetry/PowerTelemetry.cpp @@ -65,7 +65,7 @@ int32_t PowerTelemetryModule::runOnce() if (max17048Sensor.hasSensor() && !max17048Sensor.isInitialized()) result = max17048Sensor.runOnce(); } - return result; + return result == UINT32_MAX ? disable() : setStartDelay(); #else return disable(); #endif @@ -100,7 +100,7 @@ void PowerTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *s { display->setTextAlignment(TEXT_ALIGN_LEFT); display->setFont(FONT_SMALL); - + if (lastMeasurementPacket == nullptr) { // In case of no valid packet, display "Power Telemetry", "No measurement" display->drawString(x, y, "Power Telemetry"); @@ -121,23 +121,23 @@ void PowerTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *s } // Display "Pow. From: ..." - display->drawString(x, y, "Pow. From: " + String(lastSender) + "(" + String(agoSecs) + "s)"); + display->drawString(x, y, "Pow. From: " + String(lastSender) + "(" + String(agoSecs) + "s)"); // Display current and voltage based on ...power_metrics.has_[channel/voltage/current]... flags if (lastMeasurement.variant.power_metrics.has_ch1_voltage || lastMeasurement.variant.power_metrics.has_ch1_current) { display->drawString(x, y += _fontHeight(FONT_SMALL), - "Ch1: " + String(lastMeasurement.variant.power_metrics.ch1_voltage, 2) + - "V " + String(lastMeasurement.variant.power_metrics.ch1_current, 0) + "mA"); + "Ch1: " + String(lastMeasurement.variant.power_metrics.ch1_voltage, 2) + "V " + + String(lastMeasurement.variant.power_metrics.ch1_current, 0) + "mA"); } if (lastMeasurement.variant.power_metrics.has_ch2_voltage || lastMeasurement.variant.power_metrics.has_ch2_current) { display->drawString(x, y += _fontHeight(FONT_SMALL), - "Ch2: " + String(lastMeasurement.variant.power_metrics.ch2_voltage, 2) + - "V " + String(lastMeasurement.variant.power_metrics.ch2_current, 0) + "mA"); + "Ch2: " + String(lastMeasurement.variant.power_metrics.ch2_voltage, 2) + "V " + + String(lastMeasurement.variant.power_metrics.ch2_current, 0) + "mA"); } if (lastMeasurement.variant.power_metrics.has_ch3_voltage || lastMeasurement.variant.power_metrics.has_ch3_current) { display->drawString(x, y += _fontHeight(FONT_SMALL), - "Ch3: " + String(lastMeasurement.variant.power_metrics.ch3_voltage, 2) + - "V " + String(lastMeasurement.variant.power_metrics.ch3_current, 0) + "mA"); + "Ch3: " + String(lastMeasurement.variant.power_metrics.ch3_voltage, 2) + "V " + + String(lastMeasurement.variant.power_metrics.ch3_current, 0) + "mA"); } } diff --git a/src/modules/Telemetry/Sensor/INA226Sensor.cpp b/src/modules/Telemetry/Sensor/INA226Sensor.cpp index 1ee7cd92e..8b1cded60 100644 --- a/src/modules/Telemetry/Sensor/INA226Sensor.cpp +++ b/src/modules/Telemetry/Sensor/INA226Sensor.cpp @@ -40,14 +40,14 @@ bool INA226Sensor::getMetrics(meshtastic_Telemetry *measurement) measurement->variant.environment_metrics.has_current = true; // mV conversion to V - measurement->variant.environment_metrics.voltage = ina226.getBusVoltage() / 1000; + measurement->variant.environment_metrics.voltage = ina226.getBusVoltage(); measurement->variant.environment_metrics.current = ina226.getCurrent_mA(); return true; } uint16_t INA226Sensor::getBusVoltageMv() { - return lround(ina226.getBusVoltage()); + return lround(ina226.getBusVoltage() * 1000); } int16_t INA226Sensor::getCurrentMa() diff --git a/variants/rak2560/RAK9154Sensor.cpp b/src/modules/Telemetry/Sensor/RAK9154Sensor.cpp similarity index 76% rename from variants/rak2560/RAK9154Sensor.cpp rename to src/modules/Telemetry/Sensor/RAK9154Sensor.cpp index 43affe581..ad3925f08 100644 --- a/variants/rak2560/RAK9154Sensor.cpp +++ b/src/modules/Telemetry/Sensor/RAK9154Sensor.cpp @@ -1,9 +1,10 @@ -#ifdef HAS_RAKPROT -#include "../variants/rak2560/RAK9154Sensor.h" -#include "../mesh/generated/meshtastic/telemetry.pb.h" -#include "../modules/Telemetry/Sensor/TelemetrySensor.h" #include "configuration.h" +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && defined(HAS_RAKPROT) + +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "RAK9154Sensor.h" +#include "TelemetrySensor.h" #include "concurrency/Periodic.h" #include @@ -25,6 +26,8 @@ static uint16_t dc_vol = 0; static uint8_t dc_prec = 0; static uint8_t provision = 0; +extern RAK9154Sensor rak9154Sensor; + static void onewire_evt(const uint8_t pid, const uint8_t sid, const SNHUBAPI_EVT_E eid, uint8_t *msg, uint16_t len) { switch (eid) { @@ -78,6 +81,7 @@ static void onewire_evt(const uint8_t pid, const uint8_t sid, const SNHUBAPI_EVT default: break; } + rak9154Sensor.setLastRead(millis()); break; case SNHUBAPI_EVT_REPORT: @@ -106,6 +110,7 @@ static void onewire_evt(const uint8_t pid, const uint8_t sid, const SNHUBAPI_EVT default: break; } + rak9154Sensor.setLastRead(millis()); break; @@ -145,15 +150,18 @@ static int32_t onewireHandle() int32_t RAK9154Sensor::runOnce() { - onewirePeriodic = new Periodic("onewireHandle", onewireHandle); + if (!rak9154Sensor.isInitialized()) { + onewirePeriodic = new Periodic("onewireHandle", onewireHandle); - mySerial.begin(9600); + mySerial.begin(9600); - RakSNHub_Protocl_API.init(onewire_evt); + RakSNHub_Protocl_API.init(onewire_evt); - status = true; - initialized = true; - return 0; + status = true; + initialized = true; + } + + return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } void RAK9154Sensor::setup() @@ -163,7 +171,16 @@ void RAK9154Sensor::setup() bool RAK9154Sensor::getMetrics(meshtastic_Telemetry *measurement) { - return true; + if (getBusVoltageMv() > 0) { + measurement->variant.environment_metrics.has_voltage = true; + measurement->variant.environment_metrics.has_current = true; + + measurement->variant.environment_metrics.voltage = (float)getBusVoltageMv() / 1000; + measurement->variant.environment_metrics.current = (float)getCurrentMa() / 1000; + return true; + } else { + return false; + } } uint16_t RAK9154Sensor::getBusVoltageMv() @@ -171,6 +188,11 @@ uint16_t RAK9154Sensor::getBusVoltageMv() return dc_vol; } +int16_t RAK9154Sensor::getCurrentMa() +{ + return dc_cur; +} + int RAK9154Sensor::getBusBatteryPercent() { return (int)dc_prec; @@ -180,4 +202,8 @@ bool RAK9154Sensor::isCharging() { return (dc_cur > 0) ? true : false; } +void RAK9154Sensor::setLastRead(uint32_t lastRead) +{ + this->lastRead = lastRead; +} #endif // HAS_RAKPROT diff --git a/variants/rak2560/RAK9154Sensor.h b/src/modules/Telemetry/Sensor/RAK9154Sensor.h similarity index 55% rename from variants/rak2560/RAK9154Sensor.h rename to src/modules/Telemetry/Sensor/RAK9154Sensor.h index 6c6f304d6..c96139f9c 100644 --- a/variants/rak2560/RAK9154Sensor.h +++ b/src/modules/Telemetry/Sensor/RAK9154Sensor.h @@ -1,23 +1,30 @@ -#ifdef HAS_RAKPROT +#include "configuration.h" + +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && defined(HAS_RAKPROT) + #ifndef _RAK9154SENSOR_H #define _RAK9154SENSOR_H 1 #include "../mesh/generated/meshtastic/telemetry.pb.h" -#include "../modules/Telemetry/Sensor/TelemetrySensor.h" -#include "../modules/Telemetry/Sensor/VoltageSensor.h" +#include "CurrentSensor.h" +#include "TelemetrySensor.h" +#include "VoltageSensor.h" -class RAK9154Sensor : public TelemetrySensor, VoltageSensor +class RAK9154Sensor : public TelemetrySensor, VoltageSensor, CurrentSensor { private: protected: virtual void setup() override; + uint32_t lastRead = 0; public: RAK9154Sensor(); virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; virtual uint16_t getBusVoltageMv() override; + virtual int16_t getCurrentMa() override; int getBusBatteryPercent(); bool isCharging(); + void setLastRead(uint32_t lastRead); }; #endif // _RAK9154SENSOR_H #endif // HAS_RAKPROT \ No newline at end of file diff --git a/src/modules/WaypointModule.cpp b/src/modules/WaypointModule.cpp index b8b738309..08b48b682 100644 --- a/src/modules/WaypointModule.cpp +++ b/src/modules/WaypointModule.cpp @@ -135,20 +135,6 @@ void WaypointModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, myHeading = screen->estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i)); screen->drawCompassNorth(display, compassX, compassY, myHeading); - // Distance to Waypoint - float d = GeoCoord::latLongToMeter(DegD(wp.latitude_i), DegD(wp.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i)); - if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) { - if (d < (2 * MILES_TO_FEET)) - snprintf(distStr, sizeof(distStr), "%.0f ft", d * METERS_TO_FEET); - else - snprintf(distStr, sizeof(distStr), "%.1f mi", d * METERS_TO_FEET / MILES_TO_FEET); - } else { - if (d < 2000) - snprintf(distStr, sizeof(distStr), "%.0f m", d); - else - snprintf(distStr, sizeof(distStr), "%.1f km", d / 1000); - } - // Compass bearing to waypoint float bearingToOther = GeoCoord::bearing(DegD(op.latitude_i), DegD(op.longitude_i), DegD(wp.latitude_i), DegD(wp.longitude_i)); @@ -157,6 +143,25 @@ void WaypointModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, if (!config.display.compass_north_top) bearingToOther -= myHeading; screen->drawNodeHeading(display, compassX, compassY, compassDiam, bearingToOther); + + float bearingToOtherDegrees = (bearingToOther < 0) ? bearingToOther + 2*PI : bearingToOther; + bearingToOtherDegrees = bearingToOtherDegrees * 180 / PI; + + // Distance to Waypoint + float d = GeoCoord::latLongToMeter(DegD(wp.latitude_i), DegD(wp.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i)); + if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) { + if (d < (2 * MILES_TO_FEET)) + snprintf(distStr, sizeof(distStr), "%.0fft %.0f°", d * METERS_TO_FEET, bearingToOtherDegrees); + else + snprintf(distStr, sizeof(distStr), "%.1fmi %.0f°", d * METERS_TO_FEET / MILES_TO_FEET, bearingToOtherDegrees); + } else { + if (d < 2000) + snprintf(distStr, sizeof(distStr), "%.0fm %.0f°", d, bearingToOtherDegrees); + else + snprintf(distStr, sizeof(distStr), "%.1fkm %.0f°", d / 1000, bearingToOtherDegrees); + } + + } // If our node doesn't have position @@ -166,9 +171,9 @@ void WaypointModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, // ? in the distance field if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) - strncpy(distStr, "? mi", sizeof(distStr)); + strncpy(distStr, "? mi ?°", sizeof(distStr)); else - strncpy(distStr, "? km", sizeof(distStr)); + strncpy(distStr, "? km ?°", sizeof(distStr)); } // Draw compass circle diff --git a/src/motion/BMX160Sensor.cpp b/src/motion/BMX160Sensor.cpp index 06cea3229..3ddbe46ea 100755 --- a/src/motion/BMX160Sensor.cpp +++ b/src/motion/BMX160Sensor.cpp @@ -4,7 +4,7 @@ BMX160Sensor::BMX160Sensor(ScanI2C::FoundDevice foundDevice) : MotionSensor::MotionSensor(foundDevice) {} -#ifdef RAK_4631 +#if defined(RAK_4631) && !defined(RAK2560) #if !defined(MESHTASTIC_EXCLUDE_SCREEN) // screen is defined in main.cpp diff --git a/src/motion/BMX160Sensor.h b/src/motion/BMX160Sensor.h index 9031b4504..fc5a48aa4 100755 --- a/src/motion/BMX160Sensor.h +++ b/src/motion/BMX160Sensor.h @@ -7,7 +7,7 @@ #if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C -#ifdef RAK_4631 +#if defined(RAK_4631) && !defined(RAK2560) #include "Fusion/Fusion.h" #include diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index f642af231..5f16f909f 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -217,6 +217,7 @@ bool isPrivateIpAddress(const IPAddress &ip) {.network = 169u << 24 | 254 << 16, .mask = 0xffff0000}, // 169.254.0.0/16 {.network = 10u << 24, .mask = 0xff000000}, // 10.0.0.0/8 {.network = 127u << 24 | 1, .mask = 0xffffffff}, // 127.0.0.1/32 + {.network = 100u << 24 | 64 << 16, .mask = 0xffc00000}, // 100.64.0.0/10 }; const uint32_t addr = ntohl(ip); for (const auto &cidrRange : privateCidrRanges) { @@ -244,6 +245,73 @@ std::pair parseHostAndPort(String server, uint16_t port = 0) } return std::make_pair(std::move(server), port); } + +bool isDefaultServer(const String &host) +{ + return host.length() == 0 || host == default_mqtt_address; +} + +struct PubSubConfig { + explicit PubSubConfig(const meshtastic_ModuleConfig_MQTTConfig &config) + { + if (*config.address) { + serverAddr = config.address; + mqttUsername = config.username; + mqttPassword = config.password; + } + if (config.tls_enabled) { + serverPort = 8883; + } + std::tie(serverAddr, serverPort) = parseHostAndPort(serverAddr.c_str(), serverPort); + } + + // Defaults + static constexpr uint16_t defaultPort = 1883; + uint16_t serverPort = defaultPort; + String serverAddr = default_mqtt_address; + const char *mqttUsername = default_mqtt_username; + const char *mqttPassword = default_mqtt_password; +}; + +#if HAS_NETWORKING +bool connectPubSub(const PubSubConfig &config, PubSubClient &pubSub, Client &client) +{ + pubSub.setBufferSize(1024); + pubSub.setClient(client); + pubSub.setServer(config.serverAddr.c_str(), config.serverPort); + + LOG_INFO("Connecting directly to MQTT server %s, port: %d, username: %s, password: %s", config.serverAddr.c_str(), + config.serverPort, config.mqttUsername, config.mqttPassword); + + const bool connected = pubSub.connect(owner.id, config.mqttUsername, config.mqttPassword); + if (connected) { + LOG_INFO("MQTT connected"); + } else { + LOG_WARN("Failed to connect to MQTT server"); + } + return connected; +} +#endif + +inline bool isConnectedToNetwork() +{ +#if HAS_WIFI + return WiFi.isConnected(); +#elif HAS_ETHERNET + return Ethernet.linkStatus() == LinkON; +#else + return false; +#endif +} + +/** return true if we have a channel that wants uplink/downlink or map reporting is enabled + */ +bool wantsLink() +{ + const bool hasChannelorMapReport = + moduleConfig.mqtt.enabled && (moduleConfig.mqtt.map_reporting_enabled || channels.anyMqttEnabled()); + return hasChannelorMapReport && (moduleConfig.mqtt.proxy_to_client_enabled || isConnectedToNetwork()); +} } // namespace void MQTT::mqttCallback(char *topic, byte *payload, unsigned int length) @@ -323,7 +391,7 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE) } String host = parseHostAndPort(moduleConfig.mqtt.address).first; - isConfiguredForDefaultServer = host.length() == 0 || host == default_mqtt_address; + isConfiguredForDefaultServer = isDefaultServer(host); IPAddress ip; isMqttServerAddressPrivate = ip.fromString(host.c_str()) && isPrivateIpAddress(ip); @@ -406,57 +474,22 @@ void MQTT::reconnect() return; // Don't try to connect directly to the server } #if HAS_NETWORKING - // Defaults - int serverPort = 1883; - const char *serverAddr = default_mqtt_address; - const char *mqttUsername = default_mqtt_username; - const char *mqttPassword = default_mqtt_password; - - if (*moduleConfig.mqtt.address) { - serverAddr = moduleConfig.mqtt.address; - mqttUsername = moduleConfig.mqtt.username; - mqttPassword = moduleConfig.mqtt.password; - } -#if HAS_WIFI && !defined(ARCH_PORTDUINO) -#if !defined(CONFIG_IDF_TARGET_ESP32C6) + const PubSubConfig config(moduleConfig.mqtt); + MQTTClient *clientConnection = mqttClient.get(); +#if MQTT_SUPPORTS_TLS if (moduleConfig.mqtt.tls_enabled) { - // change default for encrypted to 8883 - try { - serverPort = 8883; - wifiSecureClient.setInsecure(); - - pubSub.setClient(wifiSecureClient); - LOG_INFO("Use TLS-encrypted session"); - } catch (const std::exception &e) { - LOG_ERROR("MQTT ERROR: %s", e.what()); - } + mqttClientTLS.setInsecure(); + LOG_INFO("Use TLS-encrypted session"); + clientConnection = &mqttClientTLS; } else { LOG_INFO("Use non-TLS-encrypted session"); - pubSub.setClient(*mqttClient); } -#else - pubSub.setClient(*mqttClient); #endif -#elif HAS_NETWORKING - pubSub.setClient(*mqttClient); -#endif - - std::pair hostAndPort = parseHostAndPort(serverAddr, serverPort); - serverAddr = hostAndPort.first.c_str(); - serverPort = hostAndPort.second; - pubSub.setServer(serverAddr, serverPort); - pubSub.setBufferSize(1024); - - LOG_INFO("Connect directly to MQTT server %s, port: %d, username: %s, password: %s", serverAddr, serverPort, mqttUsername, - mqttPassword); - - bool connected = pubSub.connect(owner.id, mqttUsername, mqttPassword); - if (connected) { - LOG_INFO("MQTT connected"); + if (connectPubSub(config, pubSub, *clientConnection)) { enabled = true; // Start running background process again runASAP = true; reconnectCount = 0; - isMqttServerAddressPrivate = isPrivateIpAddress(mqttClient->remoteIP()); + isMqttServerAddressPrivate = isPrivateIpAddress(clientConnection->remoteIP()); publishNodeInfo(); sendSubscriptions(); @@ -507,23 +540,6 @@ void MQTT::sendSubscriptions() #endif } -bool MQTT::wantsLink() const -{ - bool hasChannelorMapReport = - moduleConfig.mqtt.enabled && (moduleConfig.mqtt.map_reporting_enabled || channels.anyMqttEnabled()); - - if (hasChannelorMapReport && moduleConfig.mqtt.proxy_to_client_enabled) - return true; - -#if HAS_WIFI - return hasChannelorMapReport && WiFi.isConnected(); -#endif -#if HAS_ETHERNET - return hasChannelorMapReport && Ethernet.linkStatus() == LinkON; -#endif - return false; -} - int32_t MQTT::runOnce() { #if HAS_NETWORKING @@ -567,6 +583,47 @@ int32_t MQTT::runOnce() return 30000; } +bool MQTT::isValidConfig(const meshtastic_ModuleConfig_MQTTConfig &config, MQTTClient *client) +{ + const PubSubConfig parsed(config); + + if (config.enabled && !config.proxy_to_client_enabled) { +#if HAS_NETWORKING + std::unique_ptr clientConnection; + if (config.tls_enabled) { +#if MQTT_SUPPORTS_TLS + MQTTClientTLS *tlsClient = new MQTTClientTLS; + clientConnection.reset(tlsClient); + tlsClient->setInsecure(); +#else + LOG_ERROR("Invalid MQTT config: tls_enabled is not supported on this node"); + return false; +#endif + } else { + clientConnection.reset(new MQTTClient); + } + std::unique_ptr pubSub(new PubSubClient); + if (isConnectedToNetwork()) { + return connectPubSub(parsed, *pubSub, (client != nullptr) ? *client : *clientConnection); + } +#else + LOG_ERROR("Invalid MQTT config: proxy_to_client_enabled must be enabled on nodes that do not have a network"); + return false; +#endif + } + + const bool defaultServer = isDefaultServer(parsed.serverAddr); + if (defaultServer && config.tls_enabled) { + LOG_ERROR("Invalid MQTT config: TLS was enabled, but the default server does not support TLS"); + return false; + } + if (defaultServer && parsed.serverPort != PubSubConfig::defaultPort) { + LOG_ERROR("Invalid MQTT config: Unsupported port '%d' for the default MQTT server", parsed.serverPort); + return false; + } + return true; +} + void MQTT::publishNodeInfo() { // TODO: NodeInfo broadcast over MQTT only (NODENUM_BROADCAST_NO_LORA) diff --git a/src/mqtt/MQTT.h b/src/mqtt/MQTT.h index cf52ad877..5cda90218 100644 --- a/src/mqtt/MQTT.h +++ b/src/mqtt/MQTT.h @@ -10,12 +10,10 @@ #endif #if HAS_WIFI #include -#if !defined(ARCH_PORTDUINO) -#if defined(ESP_ARDUINO_VERSION_MAJOR) && ESP_ARDUINO_VERSION_MAJOR < 3 +#if __has_include() #include #endif #endif -#endif #if HAS_ETHERNET #include #endif @@ -47,10 +45,6 @@ class MQTT : private concurrency::OSThread */ void onSend(const meshtastic_MeshPacket &mp_encrypted, const meshtastic_MeshPacket &mp_decoded, ChannelIndex chIndex); - /** Attempt to connect to server if necessary - */ - void reconnect(); - bool isConnectedDirectly(); bool publish(const char *topic, const char *payload, bool retained); @@ -65,6 +59,9 @@ class MQTT : private concurrency::OSThread bool isUsingDefaultServer() { return isConfiguredForDefaultServer; } + /// Validate the meshtastic_ModuleConfig_MQTTConfig. + static bool isValidConfig(const meshtastic_ModuleConfig_MQTTConfig &config) { return isValidConfig(config, nullptr); } + protected: struct QueueEntry { std::string topic; @@ -80,22 +77,23 @@ class MQTT : private concurrency::OSThread #ifndef PIO_UNIT_TESTING private: #endif - // supposedly the current version is busted: - // http://www.iotsharing.com/2017/08/how-to-use-esp32-mqtts-with-mqtts-mosquitto-broker-tls-ssl.html #if HAS_WIFI using MQTTClient = WiFiClient; -#if !defined(ARCH_PORTDUINO) -#if (defined(ESP_ARDUINO_VERSION_MAJOR) && ESP_ARDUINO_VERSION_MAJOR < 3) || defined(RPI_PICO) - WiFiClientSecure wifiSecureClient; +#if __has_include() + using MQTTClientTLS = WiFiClientSecure; +#define MQTT_SUPPORTS_TLS 1 #endif -#endif -#endif -#if HAS_ETHERNET +#elif HAS_ETHERNET using MQTTClient = EthernetClient; +#else + using MQTTClient = void; #endif #if HAS_NETWORKING std::unique_ptr mqttClient; +#if MQTT_SUPPORTS_TLS + MQTTClientTLS mqttClientTLS; +#endif PubSubClient pubSub; explicit MQTT(std::unique_ptr mqttClient); #endif @@ -111,9 +109,9 @@ class MQTT : private concurrency::OSThread uint32_t map_position_precision = default_map_position_precision; uint32_t map_publish_interval_msecs = default_map_publish_interval_secs * 1000; - /** return true if we have a channel that wants uplink/downlink or map reporting is enabled + /** Attempt to connect to server if necessary */ - bool wantsLink() const; + void reconnect(); /** Tell the server what subscriptions we want (based on channels.downlink_enabled) */ @@ -122,6 +120,8 @@ class MQTT : private concurrency::OSThread /// Callback for direct mqtt subscription messages static void mqttCallback(char *topic, byte *payload, unsigned int length); + static bool isValidConfig(const meshtastic_ModuleConfig_MQTTConfig &config, MQTTClient *client); + /// Called when a new publish arrives from the MQTT server void onReceive(char *topic, byte *payload, size_t length); diff --git a/src/platform/nrf52/architecture.h b/src/platform/nrf52/architecture.h index ce99244ba..3e4397686 100644 --- a/src/platform/nrf52/architecture.h +++ b/src/platform/nrf52/architecture.h @@ -127,4 +127,4 @@ #if !defined(PIN_SERIAL_RX) && !defined(NRF52840_XXAA) // No serial ports on this board - ONLY use segger in memory console #define USE_SEGGER -#endif +#endif \ No newline at end of file diff --git a/src/platform/nrf52/main-nrf52.cpp b/src/platform/nrf52/main-nrf52.cpp index 7ca047654..8483d21c6 100644 --- a/src/platform/nrf52/main-nrf52.cpp +++ b/src/platform/nrf52/main-nrf52.cpp @@ -1,6 +1,7 @@ #include "configuration.h" #include #include +#include #include #include #include @@ -130,6 +131,54 @@ int printf(const char *fmt, ...) return res; } +namespace +{ +constexpr uint8_t NRF52_MAGIC_LFS_IS_CORRUPT = 0xF5; +constexpr uint32_t MULTIPLE_CORRUPTION_DELAY_MILLIS = 20 * 60 * 1000; +static unsigned long millis_until_formatting_again = 0; + +// Report the critical error from loop(), giving a chance for the screen to be initialized first. +inline void reportLittleFSCorruptionOnce() +{ + static bool report_corruption = !!millis_until_formatting_again; + if (report_corruption) { + report_corruption = false; + RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE); + } +} +} // namespace + +void preFSBegin() +{ + // The GPREGRET register keeps its value across warm boots. Check that this is a warm boot and, if GPREGRET + // is set to NRF52_MAGIC_LFS_IS_CORRUPT, format LittleFS. + if (!(NRF_POWER->RESETREAS == 0 && NRF_POWER->GPREGRET == NRF52_MAGIC_LFS_IS_CORRUPT)) + return; + NRF_POWER->GPREGRET = 0; + millis_until_formatting_again = millis() + MULTIPLE_CORRUPTION_DELAY_MILLIS; + InternalFS.format(); + LOG_INFO("LittleFS format complete; restoring default settings"); +} + +extern "C" void lfs_assert(const char *reason) +{ + LOG_ERROR("LittleFS corruption detected: %s", reason); + if (millis_until_formatting_again > millis()) { + RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE); + const long millis_remain = millis_until_formatting_again - millis(); + LOG_WARN("Pausing %d seconds to avoid wear on flash storage", millis_remain / 1000); + delay(millis_remain); + } + LOG_INFO("Rebooting to format LittleFS"); + delay(500); // Give the serial port a bit of time to output that last message. + // Try setting GPREGRET with the SoftDevice first. If that fails (perhaps because the SD hasn't been initialize yet) then set + // NRF_POWER->GPREGRET directly. + if (!(sd_power_gpregret_clr(0, 0xFF) == NRF_SUCCESS && sd_power_gpregret_set(0, NRF52_MAGIC_LFS_IS_CORRUPT) == NRF_SUCCESS)) { + NRF_POWER->GPREGRET = NRF52_MAGIC_LFS_IS_CORRUPT; + } + NVIC_SystemReset(); +} + void checkSDEvents() { if (useSoftDevice) { @@ -154,6 +203,7 @@ void checkSDEvents() void nrf52Loop() { checkSDEvents(); + reportLittleFSCorruptionOnce(); } #ifdef USE_SEMIHOSTING @@ -254,6 +304,11 @@ void cpuDeepSleep(uint32_t msecToWake) nrf_gpio_cfg_default(WB_I2C1_SDA); #endif #endif +#ifdef MESHLINK +#ifdef PIN_WD_EN + digitalWrite(PIN_WD_EN, LOW); +#endif +#endif #ifdef HELTEC_MESH_NODE_T114 nrf_gpio_cfg_default(PIN_GPS_PPS); @@ -309,4 +364,4 @@ void enterDfuMode() #else enterUf2Dfu(); #endif -} +} \ No newline at end of file diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index ab78baa1a..9da65c92c 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -369,6 +369,12 @@ bool loadConfig(const char *configPath) } } + settingsMap[sx126x_max_power] = yamlConfig["Lora"]["SX126X_MAX_POWER"].as(22); + settingsMap[sx128x_max_power] = yamlConfig["Lora"]["SX128X_MAX_POWER"].as(13); + settingsMap[lr1110_max_power] = yamlConfig["Lora"]["LR1110_MAX_POWER"].as(22); + settingsMap[lr1120_max_power] = yamlConfig["Lora"]["LR1120_MAX_POWER"].as(13); + settingsMap[rf95_max_power] = yamlConfig["Lora"]["RF95_MAX_POWER"].as(20); + settingsMap[dio2_as_rf_switch] = yamlConfig["Lora"]["DIO2_AS_RF_SWITCH"].as(false); settingsMap[dio3_tcxo_voltage] = yamlConfig["Lora"]["DIO3_TCXO_VOLTAGE"].as(0) * 1000; if (settingsMap[dio3_tcxo_voltage] == 0 && yamlConfig["Lora"]["DIO3_TCXO_VOLTAGE"].as(false)) { @@ -518,7 +524,12 @@ bool loadConfig(const char *configPath) if (yamlConfig["Webserver"]) { settingsMap[webserverport] = (yamlConfig["Webserver"]["Port"]).as(-1); - settingsStrings[webserverrootpath] = (yamlConfig["Webserver"]["RootPath"]).as(""); + settingsStrings[webserverrootpath] = + (yamlConfig["Webserver"]["RootPath"]).as("/usr/share/meshtasticd/web"); + settingsStrings[websslkeypath] = + (yamlConfig["Webserver"]["SSLKey"]).as("/etc/meshtasticd/ssl/private_key.pem"); + settingsStrings[websslcertpath] = + (yamlConfig["Webserver"]["SSLCert"]).as("/etc/meshtasticd/ssl/certificate.pem"); } if (yamlConfig["General"]) { @@ -569,4 +580,4 @@ bool MAC_from_string(std::string mac_str, uint8_t *dmac) } else { return false; } -} +} \ No newline at end of file diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h index d1e91956d..a52ca88f8 100644 --- a/src/platform/portduino/PortduinoGlue.h +++ b/src/platform/portduino/PortduinoGlue.h @@ -27,6 +27,11 @@ enum configNames { sx126x_ant_sw_pin, sx126x_ant_sw_line, sx126x_ant_sw_gpiochip, + sx126x_max_power, + sx128x_max_power, + lr1110_max_power, + lr1120_max_power, + rf95_max_power, dio2_as_rf_switch, dio3_tcxo_voltage, use_rf95, @@ -76,6 +81,8 @@ enum configNames { webserver, webserverport, webserverrootpath, + websslkeypath, + websslcertpath, maxtophone, maxnodes, ascii_logs, @@ -94,4 +101,4 @@ int initGPIOPin(int pinNum, std::string gpioChipname, int line); bool loadConfig(const char *configPath); static bool ends_with(std::string_view str, std::string_view suffix); void getMacAddr(uint8_t *dmac); -bool MAC_from_string(std::string mac_str, uint8_t *dmac); +bool MAC_from_string(std::string mac_str, uint8_t *dmac); \ No newline at end of file diff --git a/src/power.h b/src/power.h index ab55fc7e1..e9c0deb7c 100644 --- a/src/power.h +++ b/src/power.h @@ -1,5 +1,4 @@ #pragma once -#include "../variants/rak2560/RAK9154Sensor.h" #include "PowerStatus.h" #include "concurrency/OSThread.h" #include "configuration.h" @@ -25,6 +24,8 @@ #define OCV_ARRAY 1400, 1300, 1280, 1270, 1260, 1250, 1240, 1230, 1210, 1150, 1000 #elif defined(CELL_TYPE_LTO) #define OCV_ARRAY 2700, 2560, 2540, 2520, 2500, 2460, 2420, 2400, 2380, 2320, 1500 +#elif defined(TRACKER_T1000_E) +#define OCV_ARRAY 4190, 4078, 4017, 3969, 3887, 3818, 3798, 3791, 3766, 3712, 3100 #else // LiIon #define OCV_ARRAY 4190, 4050, 3990, 3890, 3800, 3720, 3630, 3530, 3420, 3300, 3100 #endif @@ -56,8 +57,8 @@ extern INA3221Sensor ina3221Sensor; extern MAX17048Sensor max17048Sensor; #endif -#if HAS_RAKPROT && !defined(ARCH_PORTDUINO) -#include "../variants/rak2560/RAK9154Sensor.h" +#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && HAS_RAKPROT && !defined(ARCH_PORTDUINO) +#include "modules/Telemetry/Sensor/RAK9154Sensor.h" extern RAK9154Sensor rak9154Sensor; #endif diff --git a/src/serialization/JSONValue.cpp b/src/serialization/JSONValue.cpp index b2e9575bf..20cd90373 100644 --- a/src/serialization/JSONValue.cpp +++ b/src/serialization/JSONValue.cpp @@ -22,7 +22,6 @@ * THE SOFTWARE. */ -#include #include #include #include @@ -851,18 +850,26 @@ std::string JSONValue::StringifyString(const std::string &str) str_out += "\\r"; } else if (chr == '\t') { str_out += "\\t"; - } else if (chr < ' ' || chr > 126) { - str_out += "\\u"; - for (int i = 0; i < 4; i++) { - int value = (chr >> 12) & 0xf; - if (value >= 0 && value <= 9) - str_out += (char)('0' + value); - else if (value >= 10 && value <= 15) - str_out += (char)('A' + (value - 10)); - chr <<= 4; - } + } else if (chr < 0x20 || chr == 0x7F) { + char buf[7]; + snprintf(buf, sizeof(buf), "\\u%04x", chr); + str_out += buf; + } else if (chr < 0x80) { + str_out += chr; } else { str_out += chr; + size_t remain = str.end() - iter - 1; + if ((chr & 0xE0) == 0xC0 && remain >= 1) { + ++iter; + str_out += *iter; + } else if ((chr & 0xF0) == 0xE0 && remain >= 2) { + str_out += *(++iter); + str_out += *(++iter); + } else if ((chr & 0xF8) == 0xF0 && remain >= 3) { + str_out += *(++iter); + str_out += *(++iter); + str_out += *(++iter); + } } ++iter; diff --git a/src/serialization/MeshPacketSerializer.cpp b/src/serialization/MeshPacketSerializer.cpp index 2f0d881f2..2c1dc0ca7 100644 --- a/src/serialization/MeshPacketSerializer.cpp +++ b/src/serialization/MeshPacketSerializer.cpp @@ -220,7 +220,11 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_RouteDiscovery_msg, &scratch)) { decoded = &scratch; - JSONArray route; // Route this message took + JSONArray route; // Route this message took + JSONArray routeBack; // Route this message took back + JSONArray snrTowards; // Snr for forward route + JSONArray snrBack; // Snr for reverse route + // Lambda function for adding a long name to the route auto addToRoute = [](JSONArray *route, NodeNum num) { char long_name[40] = "Unknown"; @@ -236,7 +240,24 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, } addToRoute(&route, mp->from); // Ended at the original destination (source of response) + addToRoute(&routeBack, mp->from); // Started at the original destination (source of response) + for (uint8_t i = 0; i < decoded->route_back_count; i++) { + addToRoute(&routeBack, decoded->route_back[i]); + } + addToRoute(&routeBack, mp->to); // Ended at the original transmitter (destination of response) + + for (uint8_t i = 0; i < decoded->snr_back_count; i++) { + snrBack.push_back(new JSONValue((float)decoded->snr_back[i] / 4)); + } + + for (uint8_t i = 0; i < decoded->snr_towards_count; i++) { + snrTowards.push_back(new JSONValue((float)decoded->snr_towards[i] / 4)); + } + msgPayload["route"] = new JSONValue(route); + msgPayload["route_back"] = new JSONValue(routeBack); + msgPayload["snr_back"] = new JSONValue(snrBack); + msgPayload["snr_towards"] = new JSONValue(snrTowards); jsonObj["payload"] = new JSONValue(msgPayload); } else if (shouldLog) { LOG_ERROR(errStr, msgType.c_str()); diff --git a/src/sleep.cpp b/src/sleep.cpp index 161b6e107..437d7b88b 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -245,6 +245,9 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false, bool skipSaveN #ifdef PIN_3V3_EN digitalWrite(PIN_3V3_EN, LOW); #endif +#ifdef PIN_WD_EN + digitalWrite(PIN_WD_EN, LOW); +#endif #endif ledBlink.set(false); @@ -530,4 +533,4 @@ void enableLoraInterrupt() } #endif } -#endif +#endif \ No newline at end of file diff --git a/test/test_mqtt/MQTT.cpp b/test/test_mqtt/MQTT.cpp index 55ba479e2..50a98001a 100644 --- a/test/test_mqtt/MQTT.cpp +++ b/test/test_mqtt/MQTT.cpp @@ -94,6 +94,7 @@ class MockPubSubServer : public WiFiClient int connect(IPAddress ip, uint16_t port) override { + port_ = port; if (refuseConnection_) return 0; connected_ = true; @@ -101,6 +102,8 @@ class MockPubSubServer : public WiFiClient } int connect(const char *host, uint16_t port) override { + host_ = host; + port_ = port; if (refuseConnection_) return 0; connected_ = true; @@ -197,6 +200,8 @@ class MockPubSubServer : public WiFiClient bool connected_ = false; bool refuseConnection_ = false; // Simulate a failed connection. uint32_t ipAddress_ = 0x01010101; // IP address of the MQTT server. + std::string host_; // Requested host. + uint16_t port_; // Requested port. std::list buffer_; // Buffer of messages for the pubSub client to receive. std::string command_; // Current command received from the pubSub client. std::set subscriptions_; // Topics that the pubSub client has subscribed to. @@ -242,6 +247,8 @@ class MQTTUnitTest : public MQTT mqttClient.release(); delete pubsub; } + using MQTT::isValidConfig; + using MQTT::reconnect; int queueSize() { return mqttQueue.numUsed(); } void reportToMap(std::optional precision = std::nullopt) { @@ -488,7 +495,7 @@ void test_reconnectProxyDoesNotReconnectMqtt(void) moduleConfig.mqtt.proxy_to_client_enabled = true; MQTTUnitTest::restart(); - mqtt->reconnect(); + unitTest->reconnect(); TEST_ASSERT_FALSE(pubsub->connected_); } @@ -799,6 +806,85 @@ void test_customMqttRoot(void) [] { return pubsub->subscriptions_.count("custom/2/e/test/+") && pubsub->subscriptions_.count("custom/2/e/PKI/+"); })); } +// Empty configuration is valid. +void test_configEmptyIsValid(void) +{ + meshtastic_ModuleConfig_MQTTConfig config = {}; + + TEST_ASSERT_TRUE(MQTT::isValidConfig(config)); +} + +// Empty 'enabled' configuration is valid. +void test_configEnabledEmptyIsValid(void) +{ + meshtastic_ModuleConfig_MQTTConfig config = {.enabled = true}; + MockPubSubServer client; + + TEST_ASSERT_TRUE(MQTTUnitTest::isValidConfig(config, &client)); + TEST_ASSERT_TRUE(client.connected_); + TEST_ASSERT_EQUAL_STRING(default_mqtt_address, client.host_.c_str()); + TEST_ASSERT_EQUAL(1883, client.port_); +} + +// Configuration with the default server is valid. +void test_configWithDefaultServer(void) +{ + meshtastic_ModuleConfig_MQTTConfig config = {.address = default_mqtt_address}; + + TEST_ASSERT_TRUE(MQTT::isValidConfig(config)); +} + +// Configuration with the default server and port 8888 is invalid. +void test_configWithDefaultServerAndInvalidPort(void) +{ + meshtastic_ModuleConfig_MQTTConfig config = {.address = default_mqtt_address ":8888"}; + + TEST_ASSERT_FALSE(MQTT::isValidConfig(config)); +} + +// Configuration with the default server and tls_enabled = true is invalid. +void test_configWithDefaultServerAndInvalidTLSEnabled(void) +{ + meshtastic_ModuleConfig_MQTTConfig config = {.tls_enabled = true}; + + TEST_ASSERT_FALSE(MQTT::isValidConfig(config)); +} + +// isValidConfig connects to a custom host and port. +void test_configCustomHostAndPort(void) +{ + meshtastic_ModuleConfig_MQTTConfig config = {.enabled = true, .address = "server:1234"}; + MockPubSubServer client; + + TEST_ASSERT_TRUE(MQTTUnitTest::isValidConfig(config, &client)); + TEST_ASSERT_TRUE(client.connected_); + TEST_ASSERT_EQUAL_STRING("server", client.host_.c_str()); + TEST_ASSERT_EQUAL(1234, client.port_); +} + +// isValidConfig returns false if a connection cannot be established. +void test_configWithConnectionFailure(void) +{ + meshtastic_ModuleConfig_MQTTConfig config = {.enabled = true, .address = "server"}; + MockPubSubServer client; + client.refuseConnection_ = true; + + TEST_ASSERT_FALSE(MQTTUnitTest::isValidConfig(config, &client)); +} + +// isValidConfig returns true when tls_enabled is supported, or false otherwise. +void test_configWithTLSEnabled(void) +{ + meshtastic_ModuleConfig_MQTTConfig config = {.enabled = true, .address = "server", .tls_enabled = true}; + MockPubSubServer client; + +#if MQTT_SUPPORTS_TLS + TEST_ASSERT_TRUE(MQTTUnitTest::isValidConfig(config, &client)); +#else + TEST_ASSERT_FALSE(MQTTUnitTest::isValidConfig(config, &client)); +#endif +} + void setup() { initializeTestEnvironment(); @@ -842,6 +928,14 @@ void setup() RUN_TEST(test_enabled); RUN_TEST(test_disabled); RUN_TEST(test_customMqttRoot); + RUN_TEST(test_configEmptyIsValid); + RUN_TEST(test_configEnabledEmptyIsValid); + RUN_TEST(test_configWithDefaultServer); + RUN_TEST(test_configWithDefaultServerAndInvalidPort); + RUN_TEST(test_configWithDefaultServerAndInvalidTLSEnabled); + RUN_TEST(test_configCustomHostAndPort); + RUN_TEST(test_configWithConnectionFailure); + RUN_TEST(test_configWithTLSEnabled); exit(UNITY_END()); } #else diff --git a/userPrefs.jsonc b/userPrefs.jsonc index 055f59273..de610464d 100644 --- a/userPrefs.jsonc +++ b/userPrefs.jsonc @@ -29,9 +29,13 @@ // "USERPREFS_FIXED_GPS_LON": "2.294508368", // "USERPREFS_LORACONFIG_CHANNEL_NUM": "31", // "USERPREFS_LORACONFIG_MODEM_PRESET": "meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST", - // "USERPREFS_SPLASH_TITLE": "DEFCONtastic", "USERPREFS_TZ_STRING": "tzplaceholder " // "USERPREFS_USE_ADMIN_KEY_0": "{ 0xcd, 0xc0, 0xb4, 0x3c, 0x53, 0x24, 0xdf, 0x13, 0xca, 0x5a, 0xa6, 0x0c, 0x0d, 0xec, 0x85, 0x5a, 0x4c, 0xf6, 0x1a, 0x96, 0x04, 0x1a, 0x3e, 0xfc, 0xbb, 0x8e, 0x33, 0x71, 0xe5, 0xfc, 0xff, 0x3c }", // "USERPREFS_USE_ADMIN_KEY_1": "{}", - // "USERPREFS_USE_ADMIN_KEY_2": "{}" -} \ No newline at end of file + // "USERPREFS_USE_ADMIN_KEY_2": "{}", + // "USERPREFS_OEM_TEXT": "Caterham Car Club", + // "USERPREFS_OEM_FONT_SIZE": "0", + // "USERPREFS_OEM_IMAGE_WIDTH": "50", + // "USERPREFS_OEM_IMAGE_HEIGHT": "28", + // "USERPREFS_OEM_IMAGE_DATA": "{ 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0xC0, 0x07, 0x80, 0x0F, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x18, 0xFF, 0xFF, 0x61, 0x00, 0x00, 0x00, 0x0C, 0xFF, 0xFF, 0xC7, 0x00, 0x00, 0x00, 0x0C, 0xFF, 0xFF, 0xC7, 0x00, 0x00, 0x00, 0x18, 0xFF, 0xFF, 0x67, 0x00, 0x00, 0x00, 0x18, 0x1F, 0xF0, 0x67, 0x00, 0x00, 0x00, 0x30, 0x1F, 0xF8, 0x33, 0x00, 0x00, 0x00, 0x30, 0x00, 0xFC, 0x31, 0x00, 0x00, 0x00, 0x60, 0x00, 0xFE, 0x18, 0x00, 0x00, 0x00, 0x60, 0x00, 0x7E, 0x18, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x3F, 0x0C, 0x00, 0x00, 0x00, 0xC0, 0x80, 0x1F, 0x0C, 0x00, 0x00, 0x00, 0x80, 0x81, 0x1F, 0x06, 0x00, 0x00, 0x00, 0x80, 0xC1, 0x0F, 0x06, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x0F, 0x03, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x0F, 0x03, 0x00, 0x00, 0x00, 0x00, 0xE6, 0x8F, 0x01, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xC7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0C, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00}" +} diff --git a/variants/diy/nrf52_promicro_diy_tcxo/E80_RSSI_per_case.webp b/variants/diy/nrf52_promicro_diy_tcxo/E80_RSSI_per_case.webp new file mode 100644 index 000000000..825c3cbc0 Binary files /dev/null and b/variants/diy/nrf52_promicro_diy_tcxo/E80_RSSI_per_case.webp differ diff --git a/variants/diy/nrf52_promicro_diy_tcxo/readme.md b/variants/diy/nrf52_promicro_diy_tcxo/readme.md new file mode 100644 index 000000000..585ac36de --- /dev/null +++ b/variants/diy/nrf52_promicro_diy_tcxo/readme.md @@ -0,0 +1,107 @@ +# Notes + +## General + +The pinout is contained in the variant.h file, and a [generic schematic](./Schematic_Pro-Micro_Pinouts%202024-12-14.pdf) is located in this directory. + +### Note on DIO2, RXEN, TXEN, and RF switching + +Several modules require external switching between transmit (Tx) and receive (Rx). This can be achieved using several methods: + +1. Link the TXEN pin on the radio module to DIO2 on the same module, and then connect RXEN on the radio module to pin 0.17 on the Pro-Micro. +2. Use DIO2 to drive a logic inverter, so that when DIO2 is `high`, RXEN is `low`, and vice versa. +3. Use DIO2 to drive a pair of MOSFETs or transistors to supply the same function. + +RXEN is not required to be connected if the selected module already has internal RF switching, or if external RF switching logic is already applied. +Also worth noting that the Seeed WIO SX1262 in particular only has RXEN exposed (marked RF_SW) and has the DIO2-TXEN link internally. + +
+ + The table of known modules is at the bottom of the variant.h, and reproduced here for convenience. + +| Mfr | Module | TCXO | RF Switch | Notes | +| ------------ | ---------------- | ---- | --------- | ------------------------------------- | +| Ebyte | E22-900M22S | Yes | Ext | | +| Ebyte | E22-900MM22S | No | Ext | | +| Ebyte | E22-900M30S | Yes | Ext | | +| Ebyte | E22-900M33S | Yes | Ext | MAX_POWER must be set to 8 for this | +| Ebyte | E220-900M22S | No | Ext | LLCC68, looks like DIO3 not connected | +| AI-Thinker | RA-01SH | No | Int | SX1262 | +| Heltec | HT-RA62 | Yes | Int | | +| NiceRF | Lora1262 | yes | Int | | +| Waveshare | Core1262-HF | yes | Ext | | +| Waveshare | LoRa Node Module | yes | Int | | +| Seeed | Wio-SX1262 | yes | Ext | Cute! DIO2/TXEN are not exposed | +| AI-Thinker | RA-02 | No | Int | SX1278 **433mhz band only** | +| RF Solutions | RFM95 | No | Int | Untested | +| Ebyte | E80-900M2213S | Yes | Int | LR1121 radio | + +
+ +## LR1121 modules - E80 is the default + +The E80 from CDEbyte is the most obtainable module at present, and has been selected as the default option. + +Naturally, CDEbyte have chosen to ignore the generic Semtech impelementation of the RF switching logic and have supplied confusing and contradictory documentation, which is explained below. + +tl;dr: The E80 is chosen as the default. **If you wish to use another module, the table in `rfswitch.h` must be adjusted accordingly.** + +### E80 switching - the saga + +The CDEbyte implementation of the LR1121 is contained in their E80 module. As stated above, CDEbyte have chosen to ignore the generic Semtech implementation of the RF switching logic and have their own table, which is located at the bottom of the page [here](https://www.cdebyte.com/products/E80-900M2213S/2#Pin), and reflected on page 6 of their user manual, and reproduced below: + +| DIO5/RFSW0 | DIO6/RFSW1 | RF status | +| ---------- | ---------- | ----------------------------- | +| 0 | 0 | RX | +| 0 | 1 | TX (Sub-1GHz low power mode) | +| 1 | 0 | TX (Sub-1GHz high power mode) | +| 1 | 1 | TX(2.4GHz) | + +However, looking at the sample code they provide on page 9, the values would be: + +| DIO5/RFSW0 | DIO6/RFSW1 | RF status | +| ---------- | ---------- | ----------------------------- | +| 0 | 1 | RX | +| 1 | 1 | TX (Sub-1GHz low power mode) | +| 1 | 0 | TX (Sub-1GHz high power mode) | +| 0 | 0 | TX(2.4GHz) | + +The Semtech default, the values are (taken from [here](https://github.com/Lora-net/SWSD006/blob/v2.6.1/lib/app_subGHz_config_lr11xx.c#L145-L154)): + +
+ +```cpp + .rfswitch = { + .enable = LR11XX_SYSTEM_RFSW0_HIGH | LR11XX_SYSTEM_RFSW1_HIGH | LR11XX_SYSTEM_RFSW2_HIGH, + .standby = 0, + .rx = LR11XX_SYSTEM_RFSW0_HIGH, + .tx = LR11XX_SYSTEM_RFSW0_HIGH | LR11XX_SYSTEM_RFSW1_HIGH, + .tx_hp = LR11XX_SYSTEM_RFSW1_HIGH, + .tx_hf = 0, + .gnss = LR11XX_SYSTEM_RFSW2_HIGH, + .wifi = 0, + }, +``` + +
+ +| DIO5/RFSW0 | DIO6/RFSW1 | RF status | +| ---------- | ---------- | ----------------------------- | +| 1 | 0 | RX | +| 1 | 1 | TX (Sub-1GHz low power mode) | +| 0 | 1 | TX (Sub-1GHz high power mode) | +| 0 | 0 | TX(2.4GHz) | + +It is evident from the tables above that there is no real consistency to those provided by Ebyte. + +#### An experiment + +Tests were conducted in each of the three configurations between a known-good SX1262 and an E80, passing packets in both directions and recording the reported RSSI. The E80 was set at 22db and 14db to activate the high and low power settings respectively. The results are shown in the chart below. + +![Chart showing RSSI readings in each configuration and setting](./E80_RSSI_per_case.webp) + +## Conclusion + +The RF switching is based on the code example given. Logically, this shows the DIO5 and DIO6 are swapped compared to the reference design. + +If future DIYers wish to use an alternative module, the table in `rfswitch.h` must be adjusted accordingly. diff --git a/variants/diy/nrf52_promicro_diy_tcxo/rfswitch.h b/variants/diy/nrf52_promicro_diy_tcxo/rfswitch.h index 2258c3135..71508c037 100644 --- a/variants/diy/nrf52_promicro_diy_tcxo/rfswitch.h +++ b/variants/diy/nrf52_promicro_diy_tcxo/rfswitch.h @@ -1,17 +1,20 @@ #include "RadioLib.h" +// This is rewritten to match the requirements of the E80-900M2213S +// The E80 does not conform to the reference Semtech switches(!) and therefore needs a custom matrix. +// See footnote #3 in "https://www.cdebyte.com/products/E80-900M2213S/2#Pin" // RF Switch Matrix SubG RFO_HP_LF / RFO_LP_LF / RFI_[NP]_LF0 // DIO5 -> RFSW0_V1 // DIO6 -> RFSW1_V2 -// DIO7 -> ANT_CTRL_ON + ESP_IO9/LR_GPS_ANT_DC_EN -> RFI_GPS (Bias-T GPS) (LR11x0 only) +// DIO7 -> not connected on E80 module - note that GNSS and Wifi scanning are not possible. static const uint32_t rfswitch_dio_pins[] = {RADIOLIB_LR11X0_DIO5, RADIOLIB_LR11X0_DIO6, RADIOLIB_LR11X0_DIO7, RADIOLIB_NC, RADIOLIB_NC}; static const Module::RfSwitchMode_t rfswitch_table[] = { // mode DIO5 DIO6 DIO7 - {LR11x0::MODE_STBY, {LOW, LOW, LOW}}, {LR11x0::MODE_RX, {HIGH, LOW, LOW}}, - {LR11x0::MODE_TX, {LOW, HIGH, LOW}}, {LR11x0::MODE_TX_HP, {LOW, HIGH, LOW}}, + {LR11x0::MODE_STBY, {LOW, LOW, LOW}}, {LR11x0::MODE_RX, {LOW, HIGH, LOW}}, + {LR11x0::MODE_TX, {HIGH, HIGH, LOW}}, {LR11x0::MODE_TX_HP, {HIGH, LOW, LOW}}, {LR11x0::MODE_TX_HF, {LOW, LOW, LOW}}, {LR11x0::MODE_GNSS, {LOW, LOW, HIGH}}, {LR11x0::MODE_WIFI, {LOW, LOW, LOW}}, END_OF_MODE_TABLE, -}; \ No newline at end of file +}; diff --git a/variants/diy/nrf52_promicro_diy_tcxo/variant.h b/variants/diy/nrf52_promicro_diy_tcxo/variant.h index 6ffb86cff..de49018f4 100644 --- a/variants/diy/nrf52_promicro_diy_tcxo/variant.h +++ b/variants/diy/nrf52_promicro_diy_tcxo/variant.h @@ -22,26 +22,26 @@ extern "C" { /* NRF52 PRO MICRO PIN ASSIGNMENT -| Pin   | Function   |   | Pin     | Function     | RF95 | +| Pin   | Function   |   | Pin     | Function     | RF95 | | ----- | ----------- | --- | -------- | ------------ | ----- | -| Gnd   |             |   | vbat     |             | | -| P0.06 | Serial2 RX |   | vbat     |             | | -| P0.08 | Serial2 TX |   | Gnd     |             | | -| Gnd   |             |   | reset   |             | | -| Gnd   |             |   | ext_vcc | *see 0.13   | | -| P0.17 | RXEN       |   | P0.31   | BATTERY_PIN | | -| P0.20 | GPS_RX     |   | P0.29   | BUSY         | DIO0 | -| P0.22 | GPS_TX     |   | P0.02   | MISO | MISO | -| P0.24 | GPS_EN     |   | P1.15   | MOSI         | MOSI | -| P1.00 | BUTTON_PIN |   | P1.13   | CS           | CS   | -| P0.11 | SCL         |   | P1.11   | SCK         | SCK | -| P1.04 | SDA         |   | P0.10   | DIO1/IRQ     | DIO1 | -| P1.06 | Free pin   |   | P0.09   | RESET       | RST | -|       |             |   |         |             | | -|       | Mid board   |   |         | Internal     | | -| P1.01 | Free pin   |   | 0.15     | LED         | | -| P1.02 | Free pin   |   | 0.13     | 3V3_EN       | | -| P1.07 | Free pin   |   |         |             | | +| Gnd   |             |   | vbat     |             | | +| P0.06 | Serial2 RX |   | vbat     |             | | +| P0.08 | Serial2 TX |   | Gnd     |             | | +| Gnd   |             |   | reset   |             | | +| Gnd   |             |   | ext_vcc | *see 0.13   | | +| P0.17 | RXEN       |   | P0.31   | BATTERY_PIN | | +| P0.20 | GPS_RX     |   | P0.29   | BUSY         | DIO0 | +| P0.22 | GPS_TX     |   | P0.02   | MISO | MISO | +| P0.24 | GPS_EN     |   | P1.15   | MOSI         | MOSI | +| P1.00 | BUTTON_PIN |   | P1.13   | CS           | CS   | +| P0.11 | SCL         |   | P1.11   | SCK         | SCK | +| P1.04 | SDA         |   | P0.10   | DIO1/IRQ     | DIO1 | +| P1.06 | Free pin   |   | P0.09   | RESET       | RST | +|       |             |   |         |             | | +|       | Mid board   |   |         | Internal     | | +| P1.01 | Free pin   |   | 0.15     | LED         | | +| P1.02 | Free pin   |   | 0.13     | 3V3_EN       | | +| P1.07 | Free pin   |   |         |             | | */ // Number of pins defined in PinDescription array @@ -175,7 +175,7 @@ settings. | NiceRF | Lora1262 | yes | Int | | | Waveshare | Core1262-HF | yes | Ext | | | Waveshare | LoRa Node Module | yes | Int | | -| Seeed | Wio-SX1262 | yes | Int | Sooooo cute! | +| Seeed | Wio-SX1262 | yes | Ext | Cute! DIO2/TXEN are not exposed | | AI-Thinker | RA-02 | No | Int | SX1278 **433mhz band only** | | RF Solutions | RFM95 | No | Int | Untested | | Ebyte | E80-900M2213S | Yes | Int | LR1121 radio | @@ -183,8 +183,7 @@ settings. */ #define SX126X_DIO3_TCXO_VOLTAGE 1.8 -#define TCXO_OPTIONAL // make it so that the firmware can try both TCXO and XTAL -extern float tcxoVoltage; // make this available everywhere +#define TCXO_OPTIONAL // make it so that the firmware can try both TCXO and XTAL #ifdef __cplusplus } @@ -194,4 +193,4 @@ extern float tcxoVoltage; // make this available everywhere * Arduino objects - C++ only *----------------------------------------------------------------------------*/ -#endif \ No newline at end of file +#endif diff --git a/variants/diy/platformio.ini b/variants/diy/platformio.ini index b7f3f6a92..229f48bbf 100644 --- a/variants/diy/platformio.ini +++ b/variants/diy/platformio.ini @@ -70,6 +70,20 @@ lib_deps = ${nrf52840_base.lib_deps} debug_tool = jlink +; Seeed XIAO nRF52840 + XIAO Wio SX1262 DIY +[env:seeed-xiao-nrf52840-wio-sx1262] +board = xiao_ble_sense +extends = nrf52840_base +board_level = extra +build_flags = ${nrf52840_base.build_flags} -Ivariants/diy/seeed-xiao-nrf52840-wio-sx1262 -D PRIVATE_HW + -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 + -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" +board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld +build_src_filter = ${nrf52_base.build_src_filter} +<../variants/diy/seeed-xiao-nrf52840-wio-sx1262> +lib_deps = + ${nrf52840_base.lib_deps} +debug_tool = jlink + ; NanoVHF T-Energy-S3 + E22(0)-xxxM - DIY [env:t-energy-s3_e22] extends = esp32s3_base @@ -86,4 +100,4 @@ build_flags = -D BOARD_HAS_PSRAM -D ARDUINO_USB_MODE=0 -D ARDUINO_USB_CDC_ON_BOOT=1 - -I variants/diy/t-energy-s3_e22 + -I variants/diy/t-energy-s3_e22 \ No newline at end of file diff --git a/variants/diy/seeed-xiao-nrf52840-wio-sx1262/README.md b/variants/diy/seeed-xiao-nrf52840-wio-sx1262/README.md new file mode 100644 index 000000000..194c53434 --- /dev/null +++ b/variants/diy/seeed-xiao-nrf52840-wio-sx1262/README.md @@ -0,0 +1,43 @@ +# XIAO nRF52840 + XIAO Wio SX1262 + +For a mere doubling in price you too can swap out the XIAO ESP32C3 for a XIAO nRF52840, stack the Wio SX1262 radio board either above or underneath the nRF52840, solder the pins, and achieve a massive improvement in battery life! + +I'm not really sure why else you would want to as the ESP32C3 is perfectly cromulent, easily connects to the Wio SX1262 via the B2B connector and has an onboard IPEX connector for the included Bluetooth antenna. So you'll also lose BT range, but you will also have working ADC for the battery in Meshtastic and also have an ESP32C3 to use for something else! + +If you're still reading you are clearly gonna do it anyway, so...mount the Wio SX1262 either on top or underneath depending on your preference. The `variant.h` will work with either configuration though it does map the Wio SX1262's button to nRF52840 Pin `D5` as it can still be used as a user button and it's nice to be able to gracefully shutdown a node by holding it down for 5 seconds. + +If you do decide to wire up the button, orient it so looking straight-down at the Wio SX1262 the radio chip is at the bottom, button in the middle and the hole is at the top - the **left** side of the button should be soldered to `GND` (e.g. the 2nd pin down the top on the **right** row of pins) and the **right** side of the button should be soldered to `D5` (e.g. the 2nd pin up from the button on the **left** row of pins.). This mirrors the original wiring and wiring it in reverse could end up connecting GND to voltage and that's no beuno. + +Serial Pins remain available on `D6` (TX) and `D7` (RX) should you want to use them, The same pins could be repurposed for `i2c` if you would like to have that instead of serial, in `variant.h` you would just need to change: + +```c++ +// RX and TX pins +#define PIN_SERIAL1_RX (6) +#define PIN_SERIAL1_TX (7) +``` + +to + +```c++ +// RX and TX pins +#define PIN_SERIAL1_RX (-1) +#define PIN_SERIAL1_TX (-1) +``` + +and + +```c++ +#define PIN_WIRE_SDA (-1) +#define PIN_WIRE_SCL (-1) +// #define PIN_WIRE_SDA (6) +// #define PIN_WIRE_SCL (7) +``` + +to + +```c++ +#define PIN_WIRE_SDA (6) +#define PIN_WIRE_SCL (7) +``` + +If you wanted both serial and i2c you could even go so far as to use the pads for the PDM mic which is missing on the non-sense board (`P1.00` / `P0.16`)... or move up to the nRF52840 Plus which has even more pins available but hasn't been checked/confirmed if it follows the same pin mapping as the non-plus. diff --git a/variants/diy/seeed-xiao-nrf52840-wio-sx1262/variant.cpp b/variants/diy/seeed-xiao-nrf52840-wio-sx1262/variant.cpp new file mode 100644 index 000000000..2c6c3e539 --- /dev/null +++ b/variants/diy/seeed-xiao-nrf52840-wio-sx1262/variant.cpp @@ -0,0 +1,55 @@ +#include "variant.h" +#include "nrf.h" +#include "wiring_constants.h" +#include "wiring_digital.h" + +const uint32_t g_ADigitalPinMap[] = { + // D0 .. D13 + 2, // D0 is P0.02 (A0) + 3, // D1 is P0.03 (A1) + 28, // D2 is P0.28 (A2) + 29, // D3 is P0.29 (A3) + 4, // D4 is P0.04 (A4,SDA) + 5, // D5 is P0.05 (A5,SCL) + 43, // D6 is P1.11 (TX) + 44, // D7 is P1.12 (RX) + 45, // D8 is P1.13 (SCK) + 46, // D9 is P1.14 (MISO) + 47, // D10 is P1.15 (MOSI) + + // LEDs + 26, // D11 is P0.26 (LED RED) + 6, // D12 is P0.06 (LED BLUE) + 30, // D13 is P0.30 (LED GREEN) + 14, // D14 is P0.14 (READ_BAT) + + // LSM6DS3TR + 40, // D15 is P1.08 (6D_PWR) + 27, // D16 is P0.27 (6D_I2C_SCL) + 7, // D17 is P0.07 (6D_I2C_SDA) + 11, // D18 is P0.11 (6D_INT1) + + // MIC + 42, // 17,//42, // D19 is P1.10 (MIC_PWR) + 32, // 26,//32, // D20 is P1.00 (PDM_CLK) + 16, // 25,//16, // D21 is P0.16 (PDM_DATA) + + // BQ25100 + 13, // D22 is P0.13 (HICHG) + 17, // D23 is P0.17 (~CHG) + + // + 21, // D24 is P0.21 (QSPI_SCK) + 25, // D25 is P0.25 (QSPI_CSN) + 20, // D26 is P0.20 (QSPI_SIO_0 DI) + 24, // D27 is P0.24 (QSPI_SIO_1 DO) + 22, // D28 is P0.22 (QSPI_SIO_2 WP) + 23, // D29 is P0.23 (QSPI_SIO_3 HOLD) + + // NFC + 9, // D30 is P0.09 (NFC1) + 10, // D31 is P0.10 (NFC2) + + // VBAT + 31, // D32 is P0.10 (VBAT) +}; \ No newline at end of file diff --git a/variants/diy/seeed-xiao-nrf52840-wio-sx1262/variant.h b/variants/diy/seeed-xiao-nrf52840-wio-sx1262/variant.h new file mode 100644 index 000000000..7a76727f2 --- /dev/null +++ b/variants/diy/seeed-xiao-nrf52840-wio-sx1262/variant.h @@ -0,0 +1,186 @@ +// basically xiao_ble with pins remapped for: +// Seeed XIAO nRF52840 : https://www.seeedstudio.com/Seeed-XIAO-BLE-nRF52840-p-5201.html +// Seeed Wio SX1626 : https://www.seeedstudio.com/Wio-SX1262-with-XIAO-ESP32S3-p-5982.html + +#ifndef _SEEED_XIAO_NRF52840_SENSE_H_ +#define _SEEED_XIAO_NRF52840_SENSE_H_ + +/** Master clock frequency */ +#define VARIANT_MCK (64000000ul) + +#define USE_LFXO // Board uses 32khz crystal for LF + +/*---------------------------------------------------------------------------- + * Headers + *----------------------------------------------------------------------------*/ + +#include "WVariant.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +#define PINS_COUNT (33) +#define NUM_DIGITAL_PINS (33) +#define NUM_ANALOG_INPUTS (8) // A6 is used for battery, A7 is analog reference +#define NUM_ANALOG_OUTPUTS (0) + +// LEDs +// ---- +#define LED_RED 11 +#define LED_BLUE 12 +#define LED_GREEN 13 + +#define PIN_LED1 LED_GREEN +#define PIN_LED2 LED_BLUE +#define PIN_LED3 LED_RED + +#define PIN_LED PIN_LED1 +#define LED_PWR (PINS_COUNT) + +#define LED_BUILTIN PIN_LED +#define LED_STATE_ON 1 // State when LED is lit + +// XIAO Wio-SX1262 Shield User button +#define PIN_BUTTON1 5 +#define BUTTON_NEED_PULLUP + +// Digital Pins +// ------------ +#define D0 (0ul) +#define D1 (1ul) +#define D2 (2ul) +#define D3 (3ul) +#define D4 (4ul) +#define D5 (5ul) +#define D6 (6ul) +#define D7 (7ul) +#define D8 (8ul) +#define D9 (9ul) +#define D10 (10ul) + +// Analog Pins +// ----------- +#define PIN_A0 (0) +#define PIN_A1 (1) +#define PIN_A2 (2) +#define PIN_A3 (3) +#define PIN_A4 (4) +#define PIN_A5 (5) +#define PIN_VBAT (32) +#define VBAT_ENABLE (14) + +static const uint8_t A0 = PIN_A0; +static const uint8_t A1 = PIN_A1; +static const uint8_t A2 = PIN_A2; +static const uint8_t A3 = PIN_A3; +static const uint8_t A4 = PIN_A4; +static const uint8_t A5 = PIN_A5; +#define ADC_RESOLUTION 12 + +// Other Pins +// ---------- +#define PIN_NFC1 (30) +#define PIN_NFC2 (31) + +// RX and TX pins +#define PIN_SERIAL1_RX (-1) +#define PIN_SERIAL1_TX (-1) +// complains if not defined +#define PIN_SERIAL2_RX (-1) +#define PIN_SERIAL2_TX (-1) + +// 4 is used as RF_SW and 5 for USR button so... +#define PIN_WIRE_SDA (6) +#define PIN_WIRE_SCL (7) + +static const uint8_t SDA = PIN_WIRE_SDA; +static const uint8_t SCL = PIN_WIRE_SCL; + +// SPI SX1262 +// ---------- +#define SPI_SX1262 +#ifdef SPI_SX1262 +#define SPI_INTERFACES_COUNT 1 + +#define PIN_SPI_MISO (9) +#define PIN_SPI_MOSI (10) +#define PIN_SPI_SCK (8) + +static const uint8_t SS = D3; +static const uint8_t MOSI = PIN_SPI_MOSI; +static const uint8_t MISO = PIN_SPI_MISO; +static const uint8_t SCK = PIN_SPI_SCK; + +// supported modules list +#define USE_SX1262 + +// common pinouts for SX126X modules +#define SX126X_CS D3 +#define SX126X_DIO1 D0 +#define SX126X_BUSY D1 +#define SX126X_RESET D2 + +// DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3 +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_RXEN 38 +#define SX126X_TXEN RADIOLIB_NC +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 +#endif + +// Wire Interfaces +// ------------------- +#define WIRE_INTERFACES_COUNT 1 // 2 + +// Sense version has IMU and PDM Mic +// #define XIAO_SENSE +#ifndef XIAO_SENSE +// 6 DoF IMU +#define PIN_LSM6DS3TR_C_POWER (15) +#define PIN_LSM6DS3TR_C_INT1 (18) +// PDM Interfaces +// --------------- +#define PIN_PDM_PWR (19) +#define PIN_PDM_CLK (20) +#define PIN_PDM_DIN (21) +#endif + +// QSPI Pins +// --------- +#define PIN_QSPI_SCK (24) +#define PIN_QSPI_CS (25) +#define PIN_QSPI_IO0 (26) +#define PIN_QSPI_IO1 (27) +#define PIN_QSPI_IO2 (28) +#define PIN_QSPI_IO3 (29) + +// On-board QSPI Flash +// ------------------- +#define EXTERNAL_FLASH_DEVICES P25Q16H +#define EXTERNAL_FLASH_USE_QSPI + +// Battery +// ------- +// P0_14 = 14 Reads battery voltage from divider on signal board. +// PIN_VBAT is reading voltage divider on XIAO and is program pin 32 / or P0.31 +#define BAT_READ 14 +#define BATTERY_SENSE_RESOLUTION_BITS 10 +#define CHARGE_LED 23 // P0_17 = 17 D23 YELLOW CHARGE LED +#define HICHG 22 // P0_13 = 13 D22 Charge-select pin for Lipo for 100 mA instead of default 50mA charge + +// The battery sense is hooked to pin A0 (5) +#define BATTERY_PIN PIN_VBAT // PIN_A0 + +// ratio of voltage divider = 3.0 (R17=1M, R18=510k) +#define ADC_MULTIPLIER 3 // 3.0 + a bit for being optimistic + +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------- + * Arduino objects - C++ only + *----------------------------------------------------------------------------*/ + +#endif \ No newline at end of file diff --git a/variants/heltec_vision_master_e213/platformio.ini b/variants/heltec_vision_master_e213/platformio.ini index 709ae321f..cc6f283b5 100644 --- a/variants/heltec_vision_master_e213/platformio.ini +++ b/variants/heltec_vision_master_e213/platformio.ini @@ -10,12 +10,8 @@ build_flags = -DEINK_HEIGHT=122 -DUSE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk -DEINK_LIMIT_FASTREFRESH=10 ; How many consecutive fast-refreshes are permitted - -DEINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates - -DEINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates -; -D EINK_LIMIT_GHOSTING_PX=2000 ; (Optional) How much image ghosting is tolerated -DEINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached. -DEINK_HASQUIRK_GHOSTING ; Display model is identified as "prone to ghosting" - -DEINK_HASQUIRK_WEAKFASTREFRESH ; Pixels set with fast-refresh are easy to clear, disrupted by sunlight lib_deps = ${esp32s3_base.lib_deps} https://github.com/meshtastic/GxEPD2#b202ebfec6a4821e098cf7a625ba0f6f2400292d diff --git a/variants/heltec_vision_master_e290/platformio.ini b/variants/heltec_vision_master_e290/platformio.ini index e1ba100ae..06804e4f2 100644 --- a/variants/heltec_vision_master_e290/platformio.ini +++ b/variants/heltec_vision_master_e290/platformio.ini @@ -11,12 +11,8 @@ build_flags = -D EINK_HEIGHT=128 -D USE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk -D EINK_LIMIT_FASTREFRESH=10 ; How many consecutive fast-refreshes are permitted - -D EINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates - -D EINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates -D EINK_HASQUIRK_GHOSTING ; Display model is identified as "prone to ghosting" - -D EINK_HASQUIRK_WEAKFASTREFRESH ; Pixels set with fast-refresh are easy to clear, disrupted by sunlight -; -D EINK_LIMIT_GHOSTING_PX=2000 ; How much image ghosting is tolerated -; -D EINK_BACKGROUND_USES_FAST ; (If enabled) don't redraw RESPONSIVE frames at next BACKGROUND update + lib_deps = ${esp32s3_base.lib_deps} diff --git a/variants/heltec_wireless_paper/platformio.ini b/variants/heltec_wireless_paper/platformio.ini index afbbd8be9..a7045b182 100644 --- a/variants/heltec_wireless_paper/platformio.ini +++ b/variants/heltec_wireless_paper/platformio.ini @@ -10,12 +10,8 @@ build_flags = -D EINK_HEIGHT=122 -D USE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk -D EINK_LIMIT_FASTREFRESH=10 ; How many consecutive fast-refreshes are permitted - -D EINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates - -D EINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates -; -D EINK_LIMIT_GHOSTING_PX=2000 ; (Optional) How much image ghosting is tolerated -D EINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached. -D EINK_HASQUIRK_GHOSTING ; Display model is identified as "prone to ghosting" - -D EINK_HASQUIRK_WEAKFASTREFRESH ; Pixels set with fast-refresh are easy to clear, disrupted by sunlight lib_deps = ${esp32s3_base.lib_deps} https://github.com/meshtastic/GxEPD2#b202ebfec6a4821e098cf7a625ba0f6f2400292d diff --git a/variants/heltec_wireless_paper_v1/platformio.ini b/variants/heltec_wireless_paper_v1/platformio.ini index c94bcacca..2ce7559f9 100644 --- a/variants/heltec_wireless_paper_v1/platformio.ini +++ b/variants/heltec_wireless_paper_v1/platformio.ini @@ -11,11 +11,7 @@ build_flags = -D EINK_HEIGHT=122 -D USE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk -D EINK_LIMIT_FASTREFRESH=5 ; How many consecutive fast-refreshes are permitted - -D EINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates - -D EINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates -D EINK_LIMIT_GHOSTING_PX=2000 ; (Optional) How much image ghosting is tolerated - ;-D EINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached. - -D EINK_HASQUIRK_VICIOUSFASTREFRESH ; Identify that pixels drawn by fast-refresh are harder to clear lib_deps = ${esp32s3_base.lib_deps} https://github.com/meshtastic/GxEPD2#55f618961db45a23eff0233546430f1e5a80f63a diff --git a/variants/meshlink/platformio.ini b/variants/meshlink/platformio.ini new file mode 100644 index 000000000..180dddd49 --- /dev/null +++ b/variants/meshlink/platformio.ini @@ -0,0 +1,30 @@ +; MeshLink board developed by LoraItalia. NRF52840, eByte E22900M22S (Will also come with other frequencies), 25w MPPT solar charger (5v,12v,18v selectable), support for gps, buzzer, oled or e-ink display, 10 gpios, hardware watchdog +; https://www.loraitalia.it +; firmware for boards with or without oled display +[env:meshlink] +extends = nrf52840_base +board = meshlink +;board_check = true +build_flags = ${nrf52840_base.build_flags} -I variants/meshlink -D MESHLINK + -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" + -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 + -D USE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk + -D EINK_LIMIT_FASTREFRESH=5 ; How many consecutive fast-refreshes are permitted + -D EINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates + -D EINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates + -D EINK_LIMIT_GHOSTING_PX=2000 ; (Optional) How much image ghosting is tolerated + -D EINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached. + -D EINK_HASQUIRK_VICIOUSFASTREFRESH ; Identify that pixels drawn by fast-refresh are harder to clear + + +build_src_filter = ${nrf52_base.build_src_filter} +<../variants/meshlink> +lib_deps = + ${nrf52840_base.lib_deps} + https://github.com/meshtastic/GxEPD2#55f618961db45a23eff0233546430f1e5a80f63a +debug_tool = jlink +; 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 \ No newline at end of file diff --git a/variants/meshlink/variant.cpp b/variants/meshlink/variant.cpp new file mode 100644 index 000000000..81a5097c4 --- /dev/null +++ b/variants/meshlink/variant.cpp @@ -0,0 +1,23 @@ +#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() +{ + pinMode(PIN_LED1, OUTPUT); + digitalWrite(PIN_LED1, HIGH); // turn off the white led while booting + // otherwise it will stay lit for several seconds (could be annoying) + +#ifdef PIN_WD_EN + pinMode(PIN_WD_EN, OUTPUT); + digitalWrite(PIN_WD_EN, HIGH); // Enable the Watchdog at boot +#endif +} \ No newline at end of file diff --git a/variants/meshlink/variant.h b/variants/meshlink/variant.h new file mode 100644 index 000000000..54df03691 --- /dev/null +++ b/variants/meshlink/variant.h @@ -0,0 +1,153 @@ +#ifndef _VARIANT_MESHLINK_ +#define _VARIANT_MESHLINK_ +#ifndef MESHLINK +#define MESHLINK +#endif +/** 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 (2) +#define NUM_ANALOG_OUTPUTS (0) + +#define BUTTON_PIN (-1) // If defined, this will be used for user button presses, +#define BUTTON_NEED_PULLUP + +// LEDs +#define PIN_LED1 (24) // Built in white led for status +#define LED_BLUE PIN_LED1 +#define LED_BUILTIN PIN_LED1 + +#define LED_STATE_ON 0 // State when LED is litted +#define LED_INVERTED 1 + +// Testing USB detection +// #define NRF_APM + +/* + * Analog pins + */ +#define PIN_A1 (3) // P0.03/AIN1 +#define ADC_RESOLUTION 14 + +// Other pins +// #define PIN_AREF (2) +// static const uint8_t AREF = PIN_AREF; + +/* + * Serial interfaces + */ +#define PIN_SERIAL1_RX (32 + 8) +#define PIN_SERIAL1_TX (7) + +/* + * SPI Interfaces + */ +#define SPI_INTERFACES_COUNT 2 + +#define PIN_SPI_MISO (8) +#define PIN_SPI_MOSI (32 + 9) +#define PIN_SPI_SCK (11) + +#define PIN_SPI1_MISO (23) +#define PIN_SPI1_MOSI (21) +#define PIN_SPI1_SCK (19) + +static const uint8_t SS = 12; +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 USE_EINK + +#define PIN_EINK_CS (15) +#define PIN_EINK_BUSY (16) +#define PIN_EINK_DC (14) +#define PIN_EINK_RES (17) +#define PIN_EINK_SCLK (19) +#define PIN_EINK_MOSI (21) // also called SDI + +/* + * Wire Interfaces + */ +#define WIRE_INTERFACES_COUNT 1 + +#define PIN_WIRE_SDA (1) +#define PIN_WIRE_SCL (27) + +// QSPI Pins +#define PIN_QSPI_SCK 19 +#define PIN_QSPI_CS 22 +#define PIN_QSPI_IO0 21 +#define PIN_QSPI_IO1 23 +#define PIN_QSPI_IO2 32 +#define PIN_QSPI_IO3 20 + +// On-board QSPI Flash +#define EXTERNAL_FLASH_DEVICES W25Q16JVUXIQ +#define EXTERNAL_FLASH_USE_QSPI + +#define USE_SX1262 +#define SX126X_CS (12) +#define SX126X_DIO1 (32 + 1) +#define SX126X_BUSY (32 + 3) +#define SX126X_RESET (6) +// #define SX126X_RXEN (13) +// 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 + +// pin 25 is used to enable or disable the watchdog. This pin has to be disabled when cpu is put to sleep +// otherwise the timer will expire and wd will reboot the cpu +#define PIN_WD_EN (25) + +#define PIN_GPS_PPS (26) // Pulse per second input from the GPS + +#define GPS_TX_PIN PIN_SERIAL1_RX // This is for bits going TOWARDS the CPU +#define GPS_RX_PIN PIN_SERIAL1_TX // This is for bits going TOWARDS the GPS + +// #define GPS_THREAD_INTERVAL 50 + +// Define pin to enable GPS toggle (set GPIO to LOW) via user button triple press +#define PIN_GPS_EN (0) +#define GPS_EN_ACTIVE LOW + +#define PIN_BUZZER (31) // P0.31/AIN7 + +// Battery +// The battery sense is hooked to pin A0 (2) +#define BATTERY_PIN (2) +// 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.42 // fine tuning of voltage + +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------- + * Arduino objects - C++ only + *----------------------------------------------------------------------------*/ +#endif \ No newline at end of file diff --git a/variants/meshlink_eink/platformio.ini b/variants/meshlink_eink/platformio.ini new file mode 100644 index 000000000..db3647e73 --- /dev/null +++ b/variants/meshlink_eink/platformio.ini @@ -0,0 +1,30 @@ +; MeshLink board developed by LoraItalia. NRF52840, eByte E22900M22S (Will also come with other frequencies), 25w MPPT solar charger (5v,12v,18v selectable), support for gps, buzzer, oled or e-ink display, 10 gpios, hardware watchdog +; https://www.loraitalia.it +; firmware for boards with a 250x122 e-ink display +[env:meshlink_eink] +extends = nrf52840_base +board = meshlink +;board_check = true +build_flags = ${nrf52840_base.build_flags} -I variants/meshlink_eink -D MESHLINK + -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" + -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 + -D USE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk + -D EINK_LIMIT_FASTREFRESH=5 ; How many consecutive fast-refreshes are permitted + -D EINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates + -D EINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates + -D EINK_LIMIT_GHOSTING_PX=2000 ; (Optional) How much image ghosting is tolerated + -D EINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached. + -D EINK_HASQUIRK_VICIOUSFASTREFRESH ; Identify that pixels drawn by fast-refresh are harder to clear + + +build_src_filter = ${nrf52_base.build_src_filter} +<../variants/meshlink_eink> +lib_deps = + ${nrf52840_base.lib_deps} + https://github.com/meshtastic/GxEPD2#55f618961db45a23eff0233546430f1e5a80f63a +debug_tool = jlink +; 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 \ No newline at end of file diff --git a/variants/meshlink_eink/variant.cpp b/variants/meshlink_eink/variant.cpp new file mode 100644 index 000000000..81a5097c4 --- /dev/null +++ b/variants/meshlink_eink/variant.cpp @@ -0,0 +1,23 @@ +#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() +{ + pinMode(PIN_LED1, OUTPUT); + digitalWrite(PIN_LED1, HIGH); // turn off the white led while booting + // otherwise it will stay lit for several seconds (could be annoying) + +#ifdef PIN_WD_EN + pinMode(PIN_WD_EN, OUTPUT); + digitalWrite(PIN_WD_EN, HIGH); // Enable the Watchdog at boot +#endif +} \ No newline at end of file diff --git a/variants/meshlink_eink/variant.h b/variants/meshlink_eink/variant.h new file mode 100644 index 000000000..b605d7082 --- /dev/null +++ b/variants/meshlink_eink/variant.h @@ -0,0 +1,153 @@ +#ifndef _VARIANT_MESHLINK_ +#define _VARIANT_MESHLINK_ +#ifndef MESHLINK +#define MESHLINK +#endif +/** 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 (2) +#define NUM_ANALOG_OUTPUTS (0) + +#define BUTTON_PIN (-1) // If defined, this will be used for user button presses, +#define BUTTON_NEED_PULLUP + +// LEDs +#define PIN_LED1 (24) // Built in white led for status +#define LED_BLUE PIN_LED1 +#define LED_BUILTIN PIN_LED1 + +#define LED_STATE_ON 0 // State when LED is litted +#define LED_INVERTED 1 + +// Testing USB detection +// #define NRF_APM + +/* + * Analog pins + */ +#define PIN_A1 (3) // P0.03/AIN1 +#define ADC_RESOLUTION 14 + +// Other pins +// #define PIN_AREF (2) +// static const uint8_t AREF = PIN_AREF; + +/* + * Serial interfaces + */ +#define PIN_SERIAL1_RX (32 + 8) +#define PIN_SERIAL1_TX (7) + +/* + * SPI Interfaces + */ +#define SPI_INTERFACES_COUNT 2 + +#define PIN_SPI_MISO (8) +#define PIN_SPI_MOSI (32 + 9) +#define PIN_SPI_SCK (11) + +#define PIN_SPI1_MISO (23) +#define PIN_SPI1_MOSI (21) +#define PIN_SPI1_SCK (19) + +static const uint8_t SS = 12; +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 USE_EINK + +#define PIN_EINK_CS (15) +#define PIN_EINK_BUSY (16) +#define PIN_EINK_DC (14) +#define PIN_EINK_RES (17) +#define PIN_EINK_SCLK (19) +#define PIN_EINK_MOSI (21) // also called SDI + +/* + * Wire Interfaces + */ +#define WIRE_INTERFACES_COUNT 1 + +#define PIN_WIRE_SDA (1) +#define PIN_WIRE_SCL (27) + +// QSPI Pins +#define PIN_QSPI_SCK 19 +#define PIN_QSPI_CS 22 +#define PIN_QSPI_IO0 21 +#define PIN_QSPI_IO1 23 +#define PIN_QSPI_IO2 32 +#define PIN_QSPI_IO3 20 + +// On-board QSPI Flash +#define EXTERNAL_FLASH_DEVICES W25Q16JVUXIQ +#define EXTERNAL_FLASH_USE_QSPI + +#define USE_SX1262 +#define SX126X_CS (12) +#define SX126X_DIO1 (32 + 1) +#define SX126X_BUSY (32 + 3) +#define SX126X_RESET (6) +// #define SX126X_RXEN (13) +// 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 + +// pin 25 is used to enable or disable the watchdog. This pin has to be disabled when cpu is put to sleep +// otherwise the timer will expire and wd will reboot the cpu +#define PIN_WD_EN (25) + +#define PIN_GPS_PPS (26) // Pulse per second input from the GPS + +#define GPS_TX_PIN PIN_SERIAL1_RX // This is for bits going TOWARDS the CPU +#define GPS_RX_PIN PIN_SERIAL1_TX // This is for bits going TOWARDS the GPS + +// #define GPS_THREAD_INTERVAL 50 + +// Define pin to enable GPS toggle (set GPIO to LOW) via user button triple press +#define PIN_GPS_EN (0) +#define GPS_EN_ACTIVE LOW + +#define PIN_BUZZER (31) // P0.31/AIN7 + +// Battery +// The battery sense is hooked to pin A0 (2) +#define BATTERY_PIN (2) +// 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.42 // fine tuning of voltage + +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------- + * Arduino objects - C++ only + *----------------------------------------------------------------------------*/ +#endif \ No newline at end of file diff --git a/variants/my_esp32s3_diy_eink/platformio.ini b/variants/my_esp32s3_diy_eink/platformio.ini index e81f2c1ab..b2404566f 100644 --- a/variants/my_esp32s3_diy_eink/platformio.ini +++ b/variants/my_esp32s3_diy_eink/platformio.ini @@ -14,7 +14,9 @@ lib_deps = ${esp32_base.lib_deps} zinggjm/GxEPD2@^1.5.1 adafruit/Adafruit NeoPixel @ ^1.12.0 -build_unflags = -DARDUINO_USB_MODE=1 +build_unflags = + ${esp32s3_base.build_unflags} + -DARDUINO_USB_MODE=1 build_flags = ;${esp32_base.build_flags} -D MY_ESP32S3_DIY -I variants/my_esp32s3_diy_eink ${esp32_base.build_flags} -D PRIVATE_HW -I variants/my_esp32s3_diy_eink diff --git a/variants/my_esp32s3_diy_oled/platformio.ini b/variants/my_esp32s3_diy_oled/platformio.ini index 2d7a5cd91..0fbbaa899 100644 --- a/variants/my_esp32s3_diy_oled/platformio.ini +++ b/variants/my_esp32s3_diy_oled/platformio.ini @@ -13,7 +13,9 @@ platform_packages = lib_deps = ${esp32_base.lib_deps} adafruit/Adafruit NeoPixel @ ^1.12.0 -build_unflags = -DARDUINO_USB_MODE=1 +build_unflags = + ${esp32s3_base.build_unflags} + -DARDUINO_USB_MODE=1 build_flags = ;${esp32_base.build_flags} -D MY_ESP32S3_DIY -I variants/my_esp32s3_diy_oled ${esp32_base.build_flags} -D PRIVATE_HW -I variants/my_esp32s3_diy_oled diff --git a/variants/rak2560/create_uf2.py b/variants/rak2560/create_uf2.py deleted file mode 100644 index af78f3e09..000000000 --- a/variants/rak2560/create_uf2.py +++ /dev/null @@ -1,113 +0,0 @@ -import struct - -Import("env") # noqa: F821 - - -# Parse input and create UF2 file -def create_uf2(source, target, env): - # source_hex = target[0].get_abspath() - source_hex = target[0].get_string(False) - source_hex = ".\\" + source_hex - print("#########################################################") - print("Create UF2 from " + source_hex) - print("#########################################################") - # print("Source: " + source_hex) - target = source_hex.replace(".hex", "") - target = target + ".uf2" - # print("Target: " + target) - - with open(source_hex, mode="rb") as f: - inpbuf = f.read() - - outbuf = convert_from_hex_to_uf2(inpbuf.decode("utf-8")) - - write_file(target, outbuf) - print("#########################################################") - print(target + " is ready to flash to target device") - print("#########################################################") - - -# Add callback after .hex file was created -env.AddPostAction("$BUILD_DIR/${PROGNAME}.hex", create_uf2) # noqa: F821 - -# UF2 creation taken from uf2conv.py -UF2_MAGIC_START0 = 0x0A324655 # "UF2\n" -UF2_MAGIC_START1 = 0x9E5D5157 # Randomly selected -UF2_MAGIC_END = 0x0AB16F30 # Ditto - -familyid = 0xADA52840 - - -class Block: - def __init__(self, addr): - self.addr = addr - self.bytes = bytearray(256) - - def encode(self, blockno, numblocks): - global familyid - flags = 0x0 - if familyid: - flags |= 0x2000 - hd = struct.pack( - " diff --git a/variants/tlora_t3s3_epaper/platformio.ini b/variants/tlora_t3s3_epaper/platformio.ini index ceb4fbaf5..3f3b3fe50 100644 --- a/variants/tlora_t3s3_epaper/platformio.ini +++ b/variants/tlora_t3s3_epaper/platformio.ini @@ -12,11 +12,6 @@ build_flags = -DEINK_HEIGHT=122 -DUSE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk -DEINK_LIMIT_FASTREFRESH=10 ; How many consecutive fast-refreshes are permitted - -DEINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates - -DEINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates - -DEINK_HASQUIRK_VICIOUSFASTREFRESH ; Identify that pixels drawn by fast-refresh are harder to clear - ;-DEINK_LIMIT_GHOSTING_PX=2000 ; (Optional) How much image ghosting is tolerated - ;-DEINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached. lib_deps = ${esp32s3_base.lib_deps} diff --git a/variants/tracker-t1000-e/variant.h b/variants/tracker-t1000-e/variant.h index 6a1f99600..e65f26c93 100644 --- a/variants/tracker-t1000-e/variant.h +++ b/variants/tracker-t1000-e/variant.h @@ -55,7 +55,7 @@ extern "C" { #define BUTTON_PIN (0 + 6) // P0.06 #define BUTTON_ACTIVE_LOW false #define BUTTON_ACTIVE_PULLUP false -#define BUTTON_SENSE_TYPE 0x6 +#define BUTTON_SENSE_TYPE 0x5 // enable input pull-down #define HAS_WIRE 1 diff --git a/version.properties b/version.properties index 4312ae59a..2e207e21e 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 5 -build = 20 +build = 22