Compare commits

..
Author SHA1 Message Date
Ben Meadors d84355d15a Add basic BLE Mesh support for ESP32 and NRF52 platforms 2026-03-07 15:22:49 -06:00
269 changed files with 3520 additions and 6159 deletions
+2
View File
@@ -8,6 +8,8 @@ runs:
uses: actions/checkout@v6
with:
submodules: recursive
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- name: Install dependencies
shell: bash
+5 -3
View File
@@ -16,7 +16,8 @@ on:
type: string
permissions:
contents: read
contents: write
packages: write
jobs:
build-debian-src:
@@ -27,6 +28,8 @@ jobs:
with:
submodules: recursive
path: meshtasticd
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- name: Install deps
shell: bash
@@ -39,7 +42,6 @@ jobs:
sudo mk-build-deps --install --remove --tool='apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes' debian/control
- name: Import GPG key
if: github.event_name != 'pull_request' && github.event_name != 'pull_request_target'
uses: crazy-max/ghaction-import-gpg@v7
with:
gpg_private_key: ${{ secrets.PPA_GPG_PRIVATE_KEY }}
@@ -58,7 +60,7 @@ jobs:
run: debian/ci_pack_sdeb.sh
env:
SERIES: ${{ inputs.series }}
GPG_KEY_ID: ${{ steps.gpg.outputs.keyid || '' }}
GPG_KEY_ID: ${{ steps.gpg.outputs.keyid }}
PKG_VERSION: ${{ steps.version.outputs.deb }}
- name: Store binaries as an artifact
+2
View File
@@ -26,6 +26,8 @@ jobs:
- uses: actions/checkout@v6
with:
submodules: recursive
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- name: Build ${{ inputs.platform }}
id: build
+1 -1
View File
@@ -87,7 +87,7 @@ jobs:
gather-artifacts:
permissions:
contents: read
contents: write
pull-requests: write
runs-on: ubuntu-latest
needs: [version, build]
+2 -2
View File
@@ -5,7 +5,7 @@ on:
workflow_dispatch:
push:
branches:
- develop # Default branch, same as 'cron' above
- master
paths:
- debian/**
- "*.rpkg"
@@ -16,7 +16,7 @@ on:
- .github/workflows/hook_copr.yml
permissions:
contents: read
contents: write
packages: write
jobs:
+14 -23
View File
@@ -37,7 +37,7 @@ on:
value: ${{ jobs.docker-build.outputs.digest }}
permissions:
contents: read
contents: write
packages: write
jobs:
@@ -50,31 +50,14 @@ jobs:
uses: actions/checkout@v6
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: Set up QEMU
uses: docker/setup-qemu-action@v4
with:
cache-image: true
- name: Docker setup
uses: docker/setup-buildx-action@v4
with:
# Add Google/Amazon DockerHub mirrors to buildkit config
# https://docs.docker.com/build/ci/github-actions/configure-builder/#registry-mirror
buildkitd-config-inline: |
[registry."docker.io"]
mirrors = ["mirror.gcr.io", "public.ecr.aws"]
- name: Sanitize platform string
id: sanitize_platform
# Replace slashes with underscores
run: echo "cleaned_platform=${{ inputs.platform }}" | sed 's/\//_/g' >> $GITHUB_OUTPUT
- name: Docker login
if: ${{ inputs.push }}
uses: docker/login-action@v4
@@ -82,6 +65,17 @@ jobs:
username: meshtastic
password: ${{ secrets.DOCKER_FIRMWARE_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
- name: Docker setup
uses: docker/setup-buildx-action@v4
- 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@v6
@@ -103,6 +97,3 @@ jobs:
platforms: ${{ inputs.platform }}
build-args: |
PIO_ENV=${{ inputs.pio_env }}
# Cache image layers in GitHub Actions cache to speed up subsequent builds.
cache-from: type=gha
cache-to: type=gha,mode=max
+3 -1
View File
@@ -12,7 +12,7 @@ on:
type: string
permissions:
contents: read
contents: write
packages: write
jobs:
@@ -86,6 +86,8 @@ jobs:
uses: actions/checkout@v6
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: |
+4 -1
View File
@@ -11,7 +11,8 @@ on:
type: string
permissions:
contents: read
contents: write
packages: write
jobs:
build-copr-hook:
@@ -21,6 +22,8 @@ jobs:
uses: actions/checkout@v6
with:
submodules: recursive
ref: ${{ github.ref }}
repository: ${{ github.repository }}
- name: Trigger COPR build
uses: vidplace7/copr-build@main
+24 -23
View File
@@ -15,7 +15,8 @@ on:
- "**.md"
- version.properties
pull_request:
# Note: This is different from "pull_request". Need to specify ref when doing checkouts.
pull_request_target:
branches:
- master
- develop
@@ -28,8 +29,6 @@ on:
workflow_dispatch:
permissions: read-all
jobs:
setup:
strategy:
@@ -89,6 +88,8 @@ jobs:
- uses: actions/checkout@v6
with:
submodules: recursive
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- name: Check ${{ matrix.check.board }}
uses: meshtastic/gh-action-firmware@main
with:
@@ -125,16 +126,9 @@ jobs:
test-native:
if: ${{ !contains(github.ref_name, 'event/') && github.repository == 'meshtastic/firmware' }}
permissions: # Needed for dorny/test-reporter.
contents: read
actions: read
checks: write
uses: ./.github/workflows/test_native.yml
docker:
permissions: # Needed for pushing to GHCR.
contents: read
packages: write
strategy:
fail-fast: false
matrix:
@@ -159,6 +153,9 @@ jobs:
gather-artifacts:
# trunk-ignore(checkov/CKV2_GHA_1)
if: github.repository == 'meshtastic/firmware'
permissions:
contents: write
pull-requests: write
strategy:
fail-fast: false
matrix:
@@ -176,6 +173,9 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- uses: actions/download-artifact@v8
with:
@@ -231,6 +231,13 @@ jobs:
path: ./*.elf
retention-days: 30
- uses: scruplelesswizard/comment-artifact@main
if: ${{ github.event_name == 'pull_request' }}
with:
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
description: "Download firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip. This artifact will be available for 90 days from creation"
github-token: ${{ secrets.GITHUB_TOKEN }}
shame:
if: github.repository == 'meshtastic/firmware'
continue-on-error: true
@@ -238,7 +245,7 @@ jobs:
needs: [build]
steps:
- uses: actions/checkout@v6
if: github.event_name == 'pull_request'
if: github.event_name == 'pull_request_target'
with:
filter: blob:none # means we download all the git history but none of the commit (except ones with checkout like the head)
fetch-depth: 0
@@ -256,26 +263,24 @@ jobs:
overwrite: true
path: manifests-new/*.mt.json
- name: Find the merge base
if: github.event_name == 'pull_request'
if: github.event_name == 'pull_request_target'
run: echo "MERGE_BASE=$(git merge-base "origin/$base" "$head")" >> $GITHUB_ENV
env:
base: ${{ github.base_ref }}
head: ${{ github.sha }}
# Currently broken (for-loop through EVERY artifact -- rate limiting)
# - name: Download the old manifests
# if: github.event_name == 'pull_request'
# if: github.event_name == 'pull_request_target'
# run: gh run download -R "$repo" --name "manifests-$merge_base" --dir manifest-old/
# env:
# GH_TOKEN: ${{ github.token }}
# merge_base: ${{ env.MERGE_BASE }}
# repo: ${{ github.repository }}
# - name: Do scan and post comment
# if: github.event_name == 'pull_request'
# if: github.event_name == 'pull_request_target'
# run: python3 bin/shame.py ${{ github.event.pull_request.number }} manifests-old/ manifests-new/
release-artifacts:
permissions: # Needed for 'gh release upload'.
contents: write
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' && github.repository == 'meshtastic/firmware' }}
outputs:
@@ -301,17 +306,15 @@ jobs:
id: release_notes
run: |
chmod +x ./bin/generate_release_notes.py
NOTES=$(./bin/generate_release_notes.py ${{ needs.version.outputs.long }} --compare-ref HEAD 2>release_notes.log)
NOTES=$(./bin/generate_release_notes.py ${{ needs.version.outputs.long }})
echo "notes<<EOF" >> $GITHUB_OUTPUT
echo "$NOTES" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
echo "### Release note range" >> $GITHUB_STEP_SUMMARY
cat release_notes.log >> $GITHUB_STEP_SUMMARY
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create release
uses: softprops/action-gh-release@v3
uses: softprops/action-gh-release@v2
id: create_release
with:
draft: true
@@ -369,8 +372,6 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
release-firmware:
permissions: # Needed for 'gh release upload'.
contents: write
strategy:
fail-fast: false
matrix:
@@ -468,7 +469,7 @@ jobs:
- name: Generate release notes
run: |
chmod +x ./bin/generate_release_notes.py
./bin/generate_release_notes.py ${{ needs.version.outputs.long }} --compare-ref HEAD > ./publish/release_notes.md
./bin/generate_release_notes.py ${{ needs.version.outputs.long }} > ./publish/release_notes.md
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+371
View File
@@ -0,0 +1,371 @@
name: Merge Queue
# Not sure how concurrency works in merge_queue, removing for now.
# concurrency:
# group: merge-queue-${{ github.head_ref || github.run_id }}
# cancel-in-progress: true
on:
# Merge group is a special trigger that is used to trigger the workflow when a merge group is created.
merge_group:
jobs:
setup:
strategy:
fail-fast: true
matrix:
arch:
- all
- check
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: 3.x
cache: pip
- run: pip install -U platformio
- name: Generate matrix
id: jsonStep
run: |
if [[ "$GITHUB_HEAD_REF" == "" ]]; then
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}})
else
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}} --level pr)
fi
echo "Name: $GITHUB_REF_NAME Base: $GITHUB_BASE_REF Ref: $GITHUB_REF"
echo "${{matrix.arch}}=$TARGETS" >> $GITHUB_OUTPUT
outputs:
all: ${{ steps.jsonStep.outputs.all }}
check: ${{ steps.jsonStep.outputs.check }}
version:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Get release version string
run: |
echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
echo "deb=$(./bin/buildinfo.py deb)" >> $GITHUB_OUTPUT
id: version
env:
BUILD_LOCATION: local
outputs:
long: ${{ steps.version.outputs.long }}
deb: ${{ steps.version.outputs.deb }}
check:
needs: setup
strategy:
fail-fast: true
matrix:
check: ${{ fromJson(needs.setup.outputs.check) }}
runs-on: ubuntu-latest
if: ${{ github.event_name != 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v6
- name: Build base
id: base
uses: ./.github/actions/setup-base
- name: Check ${{ matrix.check.board }}
run: bin/check-all.sh ${{ matrix.check.board }}
build:
needs: [setup, version]
strategy:
matrix:
build: ${{ fromJson(needs.setup.outputs.all) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.build.board }}
platform: ${{ matrix.build.platform }}
build-debian-src:
if: github.repository == 'meshtastic/firmware'
uses: ./.github/workflows/build_debian_src.yml
with:
series: UNRELEASED
build_location: local
secrets: inherit
package-pio-deps-native-tft:
if: ${{ github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/package_pio_deps.yml
with:
pio_env: native-tft
secrets: inherit
test-native:
if: ${{ !contains(github.ref_name, 'event/') }}
uses: ./.github/workflows/test_native.yml
docker:
strategy:
fail-fast: false
matrix:
distro: [debian, alpine]
platform: [linux/amd64, linux/arm64, linux/arm/v7]
pio_env: [native, native-tft]
exclude:
- distro: alpine
platform: linux/arm/v7
- pio_env: native-tft
platform: linux/arm64
- pio_env: native-tft
platform: linux/arm/v7
uses: ./.github/workflows/docker_build.yml
with:
distro: ${{ matrix.distro }}
platform: ${{ matrix.platform }}
runs-on: ${{ contains(matrix.platform, 'arm') && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }}
pio_env: ${{ matrix.pio_env }}
push: false
gather-artifacts:
# trunk-ignore(checkov/CKV2_GHA_1)
permissions:
contents: write
pull-requests: write
strategy:
fail-fast: false
matrix:
arch:
- esp32
- esp32s3
- esp32c3
- esp32c6
- nrf52840
- rp2040
- rp2350
- stm32
runs-on: ubuntu-latest
needs: [version, build]
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- uses: actions/download-artifact@v8
with:
path: ./
pattern: firmware-${{matrix.arch}}-*
merge-multiple: true
- name: Display structure of downloaded files
run: ls -R
- name: Move files up
run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat
- name: Repackage in single firmware zip
uses: actions/upload-artifact@v7
with:
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
overwrite: true
path: |
./firmware-*.bin
./firmware-*.uf2
./firmware-*.hex
./firmware-*.zip
./device-*.sh
./device-*.bat
./littlefs-*.bin
./bleota*bin
./Meshtastic_nRF52_factory_erase*.uf2
retention-days: 30
- uses: actions/download-artifact@v8
with:
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true
path: ./output
# For diagnostics
- name: Show artifacts
run: ls -lR
- name: Device scripts permissions
run: |
chmod +x ./output/device-install.sh || true
chmod +x ./output/device-update.sh || true
- name: Zip firmware
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
- name: Repackage in single elfs zip
uses: actions/upload-artifact@v7
with:
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
overwrite: true
path: ./*.elf
retention-days: 30
- uses: scruplelesswizard/comment-artifact@main
if: ${{ github.event_name == 'pull_request' }}
with:
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
description: "Download firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip. This artifact will be available for 90 days from creation"
github-token: ${{ secrets.GITHUB_TOKEN }}
release-artifacts:
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' }}
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
needs:
- version
- gather-artifacts
- build-debian-src
- package-pio-deps-native-tft
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Create release
uses: softprops/action-gh-release@v2
id: create_release
with:
draft: true
prerelease: true
name: Meshtastic Firmware ${{ needs.version.outputs.long }} Alpha
tag_name: v${{ needs.version.outputs.long }}
body: |
Autogenerated by github action, developer should edit as required before publishing...
- name: Download source deb
uses: actions/download-artifact@v8
with:
pattern: firmware-debian-${{ needs.version.outputs.deb }}~UNRELEASED-src
merge-multiple: true
path: ./output/debian-src
- name: Download `native-tft` pio deps
uses: actions/download-artifact@v8
with:
pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }}
merge-multiple: true
path: ./output/pio-deps-native-tft
- name: Zip Linux sources
working-directory: output
run: |
zip -j -9 -r ./meshtasticd-${{ needs.version.outputs.deb }}-src.zip ./debian-src
zip -9 -r ./platformio-deps-native-tft-${{ needs.version.outputs.long }}.zip ./pio-deps-native-tft
# For diagnostics
- name: Display structure of downloaded files
run: ls -lR
- name: Add Linux sources to GtiHub Release
# Only run when targeting master branch with workflow_dispatch
if: ${{ github.ref_name == 'master' }}
run: |
gh release upload v${{ needs.version.outputs.long }} ./output/meshtasticd-${{ needs.version.outputs.deb }}-src.zip
gh release upload v${{ needs.version.outputs.long }} ./output/platformio-deps-native-tft-${{ needs.version.outputs.long }}.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
release-firmware:
strategy:
fail-fast: false
matrix:
arch:
- esp32
- esp32s3
- esp32c3
- esp32c6
- nrf52840
- rp2040
- rp2350
- stm32
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' }}
needs: [release-artifacts, version]
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: 3.x
- uses: actions/download-artifact@v8
with:
pattern: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true
path: ./output
- name: Display structure of downloaded files
run: ls -lR
- name: Device scripts permissions
run: |
chmod +x ./output/device-install.sh || true
chmod +x ./output/device-update.sh || true
- name: Zip firmware
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
- uses: actions/download-artifact@v8
with:
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true
path: ./elfs
- name: Zip debug elfs
run: zip -j -9 -r ./debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./elfs
# For diagnostics
- name: Display structure of downloaded files
run: ls -lR
- name: Add bins and debug elfs to GitHub Release
# Only run when targeting master branch with workflow_dispatch
if: ${{ github.ref_name == 'master' }}
run: |
gh release upload v${{ needs.version.outputs.long }} ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
gh release upload v${{ needs.version.outputs.long }} ./debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
publish-firmware:
runs-on: ubuntu-24.04
if: ${{ github.event_name == 'workflow_dispatch' }}
needs: [release-firmware, version]
env:
targets: |-
esp32,esp32s3,esp32c3,esp32c6,nrf52840,rp2040,rp2350,stm32
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: 3.x
- uses: actions/download-artifact@v8
with:
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
merge-multiple: true
path: ./publish
- name: Publish firmware to meshtastic.github.io
uses: peaceiris/actions-gh-pages@v4
env:
# On event/* branches, use the event name as the destination prefix
DEST_PREFIX: ${{ contains(github.ref_name, 'event/') && format('{0}/', github.ref_name) || '' }}
with:
deploy_key: ${{ secrets.DIST_PAGES_DEPLOY_KEY }}
external_repository: meshtastic/meshtastic.github.io
publish_branch: master
publish_dir: ./publish
destination_dir: ${{ env.DEST_PREFIX }}firmware-${{ needs.version.outputs.long }}
keep_files: true
user_name: github-actions[bot]
user_email: github-actions[bot]@users.noreply.github.com
commit_message: ${{ needs.version.outputs.long }}
enable_jekyll: true
+5 -5
View File
@@ -38,7 +38,7 @@ jobs:
- name: Apply quality label if needed
if: steps.quality.outputs.response != '' && steps.quality.outputs.response != 'ok'
uses: actions/github-script@v9
uses: actions/github-script@v8
env:
QUALITY_LABEL: ${{ steps.quality.outputs.response }}
with:
@@ -80,7 +80,7 @@ jobs:
# ─────────────────────────────────────────────────────────────────────────
- name: Determine if completeness check should be skipped
if: steps.quality.outputs.response == 'ok' || steps.quality.outputs.response == ''
uses: actions/github-script@v9
uses: actions/github-script@v8
id: check-skip
with:
script: |
@@ -134,7 +134,7 @@ jobs:
- name: Process analysis result
if: (steps.quality.outputs.response == 'ok' || steps.quality.outputs.response == '') && steps.check-skip.outputs.should_skip != 'true' && steps.analysis.outputs.response != ''
uses: actions/github-script@v9
uses: actions/github-script@v8
id: process
env:
AI_RESPONSE: ${{ steps.analysis.outputs.response }}
@@ -174,7 +174,7 @@ jobs:
- name: Apply triage label
if: steps.process.outputs.label != '' && steps.process.outputs.label != 'none'
uses: actions/github-script@v9
uses: actions/github-script@v8
env:
LABEL_NAME: ${{ steps.process.outputs.label }}
with:
@@ -200,7 +200,7 @@ jobs:
- name: Comment on issue
if: steps.process.outputs.should_comment == 'true'
uses: actions/github-script@v9
uses: actions/github-script@v8
env:
COMMENT_BODY: ${{ steps.process.outputs.comment_body }}
with:
+3 -3
View File
@@ -22,7 +22,7 @@ jobs:
# Step 1: Check if PR already has automation/type labels (skip if so)
# ─────────────────────────────────────────────────────────────────────────
- name: Check existing labels
uses: actions/github-script@v9
uses: actions/github-script@v8
id: check-labels
with:
script: |
@@ -58,7 +58,7 @@ jobs:
- name: Apply quality label if needed
if: steps.check-labels.outputs.skip_all != 'true' && steps.quality.outputs.response != '' && steps.quality.outputs.response != 'ok'
uses: actions/github-script@v9
uses: actions/github-script@v8
id: quality-label
env:
QUALITY_LABEL: ${{ steps.quality.outputs.response }}
@@ -113,7 +113,7 @@ jobs:
- name: Apply type label
if: steps.check-labels.outputs.skip_all != 'true' && steps.check-labels.outputs.has_type_label != 'true' && steps.classify.outputs.response != ''
uses: actions/github-script@v9
uses: actions/github-script@v8
env:
TYPE_LABEL: ${{ steps.classify.outputs.response }}
with:
+2 -1
View File
@@ -18,7 +18,8 @@ on:
type: string
permissions:
contents: read
contents: write
packages: write
jobs:
build-debian-src:
+4 -1
View File
@@ -16,7 +16,8 @@ on:
type: string
permissions:
contents: read
contents: write
packages: write
jobs:
pkg-pio-libdeps:
@@ -26,6 +27,8 @@ jobs:
uses: actions/checkout@v6
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@v6
+8 -43
View File
@@ -5,8 +5,6 @@ on:
secrets:
PPA_GPG_PRIVATE_KEY:
required: true
PPA_SFTP_PRIVATE_KEY:
required: true
inputs:
ppa_repo:
description: Meshtastic PPA to target
@@ -18,7 +16,8 @@ on:
type: string
permissions:
contents: read
contents: write
packages: write
jobs:
build-debian-src:
@@ -29,7 +28,6 @@ jobs:
build_location: ppa
package-ppa:
if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}
runs-on: ubuntu-24.04
needs: build-debian-src
steps:
@@ -38,12 +36,14 @@ jobs:
with:
submodules: recursive
path: meshtasticd
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- name: Install deps
shell: bash
run: |
sudo apt-get update -y --fix-missing
sudo apt-get install -y dput openssh-client
sudo apt-get install -y dput
- name: Import GPG key
uses: crazy-max/ghaction-import-gpg@v7
@@ -68,42 +68,7 @@ jobs:
- name: Display structure of downloaded files
run: ls -lah
- name: Trust Launchpad's SSH key
- name: Publish with dput
if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}
run: |
mkdir -p ~/.ssh
ssh-keyscan -H ppa.launchpad.net >> ~/.ssh/known_hosts
- name: Setup dput config
env:
ppa_login: meshtasticorg
run: |
sudo tee /etc/meshtastic-dput.cf >/dev/null <<EOF
[ppa]
fqdn = ppa.launchpad.net
method = ftp
incoming = ~%(ppa)s
login = anonymous
[ssh-ppa]
fqdn = ppa.launchpad.net
method = sftp
incoming = ~%(ssh-ppa)s
login = ${ppa_login}
EOF
- name: Import SSH key
uses: webfactory/ssh-agent@v0.10.0
with:
ssh-private-key: ${{ secrets.PPA_SFTP_PRIVATE_KEY }}
id: ssh
- name: Publish with dput (sftp)
timeout-minutes: 30 # dput is terrible, sometimes runs 'forever'
env:
up_ppa_repo: ${{ inputs.ppa_repo }}
up_series: ${{ inputs.series }}
up_version: ${{ steps.version.outputs.deb }}
run: >
dput -c /etc/meshtastic-dput.cf
ssh-${up_ppa_repo}
meshtasticd_${up_version}~${up_series}_source.changes
dput ${{ inputs.ppa_repo }} meshtasticd_${{ steps.version.outputs.deb }}~${{ inputs.series }}_source.changes
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check for PR labels
uses: actions/github-script@v9
uses: actions/github-script@v8
with:
script: |
const labels = context.payload.pull_request.labels.map(label => label.name);
+1 -1
View File
@@ -177,7 +177,7 @@ jobs:
- name: Comment test results on PR
if: github.event_name == 'pull_request' && needs.native-tests.result != 'skipped'
uses: actions/github-script@v9
uses: actions/github-script@v8
with:
script: |
const fs = require('fs');
+8 -1
View File
@@ -16,6 +16,8 @@ jobs:
steps:
- uses: actions/checkout@v6
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
submodules: recursive
- name: Setup native build
@@ -70,6 +72,8 @@ jobs:
steps:
- uses: actions/checkout@v6
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
submodules: recursive
- name: Setup native build
@@ -124,6 +128,9 @@ jobs:
if: always()
steps:
- uses: actions/checkout@v6
with:
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
@@ -136,7 +143,7 @@ jobs:
merge-multiple: true
- name: Test Report
uses: dorny/test-reporter@v3.0.0
uses: dorny/test-reporter@v2.6.0
with:
name: PlatformIO Tests
path: testreport.xml
+1 -1
View File
@@ -52,7 +52,7 @@ jobs:
node-version: 24
- name: Setup pnpm
uses: pnpm/action-setup@v6
uses: pnpm/action-setup@v4
with:
version: latest
+1 -1
View File
@@ -6,7 +6,7 @@ permissions: read-all
jobs:
update-protobufs:
runs-on: ubuntu-latest
permissions: # Needed for peter-evans/create-pull-request.
permissions:
contents: write
pull-requests: write
steps:
+8 -8
View File
@@ -4,31 +4,31 @@ cli:
plugins:
sources:
- id: trunk
ref: v1.7.6
ref: v1.7.5
uri: https://github.com/trunk-io/plugins
lint:
enabled:
- checkov@3.2.517
- renovate@43.110.9
- checkov@3.2.507
- renovate@43.57.0
- prettier@3.8.1
- trufflehog@3.94.3
- trufflehog@3.93.7
- yamllint@1.38.0
- bandit@1.9.4
- trivy@0.69.3
- taplo@0.10.0
- ruff@0.15.9
- ruff@0.15.5
- isort@8.0.1
- markdownlint@0.48.0
- oxipng@10.1.0
- svgo@4.0.1
- actionlint@1.7.12
- actionlint@1.7.11
- flake8@7.3.0
- hadolint@2.14.0
- shfmt@3.6.0
- shellcheck@0.11.0
- black@26.3.1
- black@26.1.0
- git-diff-check
- gitleaks@8.30.1
- gitleaks@8.30.0
- clang-format@16.0.3
ignore:
- linters: [ALL]
@@ -1,9 +1,3 @@
Meta:
name: BananaPi-BPI-R4-sx1262
support: community
compatible:
- bananapi_bpi-r4 # OpenWrt target
Lora:
Module: sx1262 # BananaPi-BPI-R4 SPI via 26p GPIO Header
## CS: 28
@@ -1,10 +1,4 @@
## https://www.mikroe.com/lr-iot-click
Meta:
name: OpenWRT One mikroBUS LR-IOT-CLICK
support: community
compatible:
- openwrt_one # OpenWrt target
Lora:
Module: lr1110 # OpenWRT ONE mikroBUS with LR-IOT-CLICK
# CS: 25
@@ -1,9 +1,3 @@
Meta:
name: OpenWRT One mikroBUS sx1262
support: community
compatible:
- openwrt_one # OpenWrt target
Lora:
Module: sx1262
IRQ: 10
-28
View File
@@ -1,28 +0,0 @@
# meshtasticd configuration files
This directory contains YAML configuration files for meshtasticd. Each file describes a specific hardware configuration, including the LoRa module and pin assignments. These configurations are used by meshtasticd to correctly interface with the hardware.
## Metadata structure
Each configuration file includes a `Meta` section that provides information about the configuration.
This configuration is consumed by configuration-selection tools.
```yaml
Meta:
name: MeshAdv-Pi E22-900M30S # A unique identifier for this configuration.
support: community # community, official, or deprecated; determined by Meshtastic Leads.
compatible: # A list of compatible products or platforms.
- raspberry-pi
```
`name`: A unique identifier for the configuration, typically reflecting the hardware it supports.
`support`: Indicates the level of support for this configuration. It can be one of the following:
- `community`: Supported by the Meshtastic community. Meshtastic Members may not possess, or have not tested this configuration.
- `official`: Fully supported by Meshtastic. Meshtastic Members have tested and verified this configuration.
- `deprecated`: No longer recommended for deployment by Meshtastic.
`compatible`: A list of compatible products or platforms that can use this configuration.
This will vary depending on the intended use case / platform.
Multiple compatible entries can be included. E.g. Armbian `BOARD` value or OpenWrt `TARGET` value.
These tags can be consumed by different configuration-selection tools, filtering based upon their platform/etc.
-6
View File
@@ -1,9 +1,3 @@
Meta:
name: Waveshare 1.44inch LCD HAT
support: community
compatible:
- raspberry-pi
### Waveshare 1.44inch LCD HAT
Display:
Panel: ST7735S
-6
View File
@@ -1,9 +1,3 @@
Meta:
name: Waveshare 2.8inch LCD HAT
support: community
compatible:
- raspberry-pi
Display:
### Waveshare 2.8inch RPi LCD
@@ -1,22 +1,20 @@
---
Meta:
name: Femtofox Ebyte E80-900M22S with TCXO
support: community
compatible:
- luckfox-pico-mini # Armbian
Lora:
## Ebyte E80-900M22S
## This is a bit experimental
##
##
Module: lr1121
gpiochip: 1 # subtract 32 from the gpio numbers
DIO3_TCXO_VOLTAGE: 1.8
CS: 16 #pin6 / GPIO48 1C0
IRQ: 23 #pin17 / GPIO55 1C7
Busy: 22 #pin16 / GPIO54 1C6
Reset: 25 #pin13 / GPIO57 1D1
spidev: spidev0.0 #pins are (CS=16, CLK=17, MOSI=18, MISO=19)
spiSpeed: 2000000
---
Lora:
## Ebyte E80-900M22S
## This is a bit experimental
##
##
Module: lr1121
gpiochip: 1 # subtract 32 from the gpio numbers
DIO3_TCXO_VOLTAGE: 1.8
CS: 16 #pin6 / GPIO48 1C0
IRQ: 23 #pin17 / GPIO55 1C7
Busy: 22 #pin16 / GPIO54 1C6
Reset: 25 #pin13 / GPIO57 1D1
spidev: spidev0.0 #pins are (CS=16, CLK=17, MOSI=18, MISO=19)
spiSpeed: 2000000
General:
MACAddressSource: eth0
@@ -1,24 +1,21 @@
---
Meta:
name: Femtofox SX1262 TCXO
support: community
compatible:
- luckfox-pico-mini # Armbian
Lora:
## Ebyte E22-900M30S, E22-900M22S with or without external RF switching setup
## HT-RA62 (Has internal switching, but whatever)
## Seeed WIO SX1262 (already has TXEN-DIO2 link, but needs RXEN)
## Will work with any module with or without RF switching, and with TCXO
Module: sx1262
gpiochip: 1 # subtract 32 from the gpio numbers
DIO2_AS_RF_SWITCH: true
DIO3_TCXO_VOLTAGE: true
CS: 16 #pin6 / GPIO48 1C0
IRQ: 23 #pin17 / GPIO55 1C7
Busy: 22 #pin16 / GPIO54 1C6
Reset: 25 #pin13 / GPIO57 1D1
RXen: 24 #pin12 / GPIO56 1D0 # Not strictly needed for auto-switching, but why complicate things?
# TXen: bridge to DIO2 on E22 module
spidev: spidev0.0 #pins are (CS=16, CLK=17, MOSI=18, MISO=19)
spiSpeed: 2000000
---
Lora:
## Ebyte E22-900M30S, E22-900M22S with or without external RF switching setup
## HT-RA62 (Has internal switching, but whatever)
## Seeed WIO SX1262 (already has TXEN-DIO2 link, but needs RXEN)
## Will work with any module with or without RF switching, and with TCXO
Module: sx1262
gpiochip: 1 # subtract 32 from the gpio numbers
DIO2_AS_RF_SWITCH: true
DIO3_TCXO_VOLTAGE: true
CS: 16 #pin6 / GPIO48 1C0
IRQ: 23 #pin17 / GPIO55 1C7
Busy: 22 #pin16 / GPIO54 1C6
Reset: 25 #pin13 / GPIO57 1D1
RXen: 24 #pin12 / GPIO56 1D0 # Not strictly needed for auto-switching, but why complicate things?
# TXen: bridge to DIO2 on E22 module
spidev: spidev0.0 #pins are (CS=16, CLK=17, MOSI=18, MISO=19)
spiSpeed: 2000000
General:
MACAddressSource: eth0
@@ -1,24 +1,21 @@
---
Meta:
name: Femtofox SX1262 XTAL
support: community
compatible:
- luckfox-pico-mini # Armbian
Lora:
## Ebyte E22-900MM22S with no external RF switching setup
## Waveshare SX126X XXXM, AI Thinker RA-01SH
## Will work with any module with or without RF switching and no TCXO
Module: sx1262
gpiochip: 1 # subtract 32 from the gpio numbers
DIO2_AS_RF_SWITCH: true
DIO3_TCXO_VOLTAGE: false
CS: 16 #pin6 / GPIO48 1C0
IRQ: 23 #pin17 / GPIO55 1C7
Busy: 22 #pin16 / GPIO54 1C6
Reset: 25 #pin13 / GPIO57 1D1
RXen: 24 #pin12 / GPIO56 1D0 # Not strictly needed for auto-switching, but why complicate things?
# TXen: bridge to DIO2 on E22 module
spidev: spidev0.0 #pins are (CS=16, CLK=17, MOSI=18, MISO=19)
spiSpeed: 2000000
---
Lora:
## Ebyte E22-900MM22S with no external RF switching setup
## Waveshare SX126X XXXM, AI Thinker RA-01SH
## Will work with any module with or without RF switching and no TCXO
Module: sx1262
gpiochip: 1 # subtract 32 from the gpio numbers
DIO2_AS_RF_SWITCH: true
DIO3_TCXO_VOLTAGE: false
CS: 16 #pin6 / GPIO48 1C0
IRQ: 23 #pin17 / GPIO55 1C7
Busy: 22 #pin16 / GPIO54 1C6
Reset: 25 #pin13 / GPIO57 1D1
RXen: 24 #pin12 / GPIO56 1D0 # Not strictly needed for auto-switching, but why complicate things?
# TXen: bridge to DIO2 on E22 module
spidev: spidev0.0 #pins are (CS=16, CLK=17, MOSI=18, MISO=19)
spiSpeed: 2000000
General:
MACAddressSource: eth0
-6
View File
@@ -1,9 +1,3 @@
Meta:
name: Adafruit RFM9x
support: deprecated
compatible:
- raspberry-pi
Lora:
Module: RF95 # Adafruit RFM9x
Reset: 25
-6
View File
@@ -1,11 +1,5 @@
# MeshAdv-Pi E22-900M30S
# https://github.com/chrismyers2000/MeshAdv-Pi-Hat
Meta:
name: MeshAdv-Pi E22-900M30S
support: community
compatible:
- raspberry-pi
Lora:
Module: sx1262
CS: 21
@@ -1,11 +1,5 @@
# MeshAdv Mini E22-900M22S
# https://github.com/chrismyers2000/MeshAdv-Mini
Meta:
name: MeshAdv Mini E22-900M22S
support: community
compatible:
- raspberry-pi
Lora:
Module: sx1262 # Ebyte E22-900M22S
CS: 8
+1 -7
View File
@@ -1,12 +1,6 @@
Meta:
name: RAK6421 + RAK13300 Slot 1
support: official
compatible:
- raspberry-pi
Lora:
### RAK13300 in Slot 1
### RAK13300in Slot 1
Module: sx1262
IRQ: 22 #IO6
Reset: 16 # IO4
+1 -7
View File
@@ -1,11 +1,5 @@
Meta:
name: RAK6421 + RAK13300 Slot 2
support: official
compatible:
- raspberry-pi
Lora:
### RAK13300 in Slot 2 pins
### RAK13300in Slot 2 pins
IRQ: 18 #IO6
Reset: 24 # IO4
Busy: 19 # IO5
+2 -8
View File
@@ -1,12 +1,6 @@
Meta:
name: RAK6421 + RAK13302 Slot 1
support: official
compatible:
- raspberry-pi
Lora:
### RAK13302 in Slot 1
### RAK13300in Slot 1
Module: sx1262
IRQ: 22 #IO6
Reset: 16 # IO4
@@ -19,4 +13,4 @@ Lora:
DIO2_AS_RF_SWITCH: true
spidev: spidev0.0
# CS: 8
TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8]
TX_GAIN_LORA: [8]
+2 -8
View File
@@ -1,11 +1,5 @@
Meta:
name: RAK6421 + RAK13302 Slot 2
support: official
compatible:
- raspberry-pi
Lora:
### RAK13302 in Slot 2 pins
### RAK13300in Slot 2 pins
IRQ: 18 #IO6
Reset: 24 # IO4
Busy: 19 # IO5
@@ -15,4 +9,4 @@ Lora:
- 23
spidev: spidev0.1
# CS: 7
TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8]
TX_GAIN_LORA: [8]
@@ -1,39 +0,0 @@
# MeshAdv-Pi E22-900M30S
# https://github.com/chrismyers2000/MeshAdv-Pi-Hat
Meta:
name: MeshAdv-Pi E22-900M30S
support: community
compatible:
- ebyte-ecb41-pge # Armbian
Lora:
Module: sx1262
CS: # GPIO0_A1 (physical 40)
pin: 1
gpiochip: 0
line: 1
IRQ: # GPIO0_A3 (physical 36)
pin: 3
gpiochip: 0
line: 3
Busy: # GPIO0_A0 (physical 38)
pin: 0
gpiochip: 0
line: 0
Reset: # GPIO0_B4 (physical 12)
pin: 12
gpiochip: 0
line: 12
TXen: # GPIO1_D1 (physical 33)
pin: 57
gpiochip: 1
line: 25
RXen: # GPIO1_B3 (physical 32)
pin: 43
gpiochip: 1
line: 11
DIO3_TCXO_VOLTAGE: true
# Only for E22-900M33S:
# Limit the output power to 8 dBm
# SX126X_MAX_POWER: 8
spidev: spidev0.0
@@ -1,33 +0,0 @@
# MeshAdv Mini E22-900M22S
# https://github.com/chrismyers2000/MeshAdv-Mini
Meta:
name: MeshAdv Mini E22-900M22S
support: community
compatible:
- ebyte-ecb41-pge # Armbian
Lora:
Module: sx1262 # Ebyte E22-900M22S
CS: # GPIO0_B6 (physical 24, SPI1_CSN0)
pin: 14
gpiochip: 0
line: 14
IRQ: # GPIO0_A3 (physical 36)
pin: 3
gpiochip: 0
line: 3
Busy: # GPIO0_A0 (physical 38)
pin: 0
gpiochip: 0
line: 0
Reset: # GPIO1_C3 (physical 18)
pin: 51
gpiochip: 1
line: 19
RXen: # GPIO1_B3 (physical 32)
pin: 43
gpiochip: 1
line: 11
DIO2_AS_RF_SWITCH: true
DIO3_TCXO_VOLTAGE: true
spidev: spidev0.0
@@ -1,38 +0,0 @@
Meta:
name: RAK6421 + RAK13300 Slot 1
support: community # Promote when tested
compatible:
- ebyte-ecb41-pge # Armbian
Lora:
Module: sx1262
IRQ: # GPIO0_A5 (IO6, physical 15)
pin: 5
gpiochip: 0
line: 5
Reset: # GPIO0_A3 (IO4, physical 36)
pin: 3
gpiochip: 0
line: 3
Busy: # GPIO1_C3 (IO5, physical 18)
pin: 51
gpiochip: 1
line: 19
# Ant_sw: # GPIO1_C2 (IO3, physical 16)
# pin: 50
# gpiochip: 1
# line: 18
Enable_Pins:
- pin: 43 # GPIO1_B3 (physical 32)
gpiochip: 1
line: 11
- pin: 57 # GPIO1_D1 (physical 33)
gpiochip: 1
line: 25
DIO3_TCXO_VOLTAGE: true
DIO2_AS_RF_SWITCH: true
spidev: spidev0.0
# CS: # GPIO0_B6 (SPI1_CSN0, physical 24)
# pin: 14
# gpiochip: 0
# line: 14
@@ -1,36 +0,0 @@
Meta:
name: RAK6421 + RAK13300 Slot 2
support: community # Promote when tested
compatible:
- ebyte-ecb41-pge # Armbian
Lora:
Module: sx1262
IRQ: # GPIO0_B4 (IO6, physical 12)
pin: 12
gpiochip: 0
line: 12
Reset: # GPIO1_C3 (IO4, physical 18)
pin: 51
gpiochip: 1
line: 19
Busy: # GPIO0_B3 (IO5, physical 35)
pin: 11
gpiochip: 0
line: 11
# Ant_sw: # GPIO1_C2 (IO3, physical 16)
# pin: 50
# gpiochip: 1
# line: 18
Enable_Pins:
- pin: 2 # GPIO0_A2 (physical 37)
gpiochip: 0
line: 2
- pin: 50 # GPIO1_C2 (physical 16)
gpiochip: 1
line: 18
spidev: spidev0.1
# CS: # GPIO0_A7 (SPI1_CSN1, physical 26)
# pin: 7
# gpiochip: 0
# line: 7
@@ -1,39 +0,0 @@
Meta:
name: RAK6421 + RAK13302 Slot 1
support: community # Promote when tested
compatible:
- ebyte-ecb41-pge # Armbian
Lora:
Module: sx1262
IRQ: # GPIO0_A5 (IO6, physical 15)
pin: 5
gpiochip: 0
line: 5
Reset: # GPIO0_A3 (IO4, physical 36)
pin: 3
gpiochip: 0
line: 3
Busy: # GPIO1_C3 (IO5, physical 18)
pin: 51
gpiochip: 1
line: 19
# Ant_sw: # GPIO1_C2 (IO3, physical 16)
# pin: 50
# gpiochip: 1
# line: 18
Enable_Pins:
- pin: 43 # GPIO1_B3 (physical 32)
gpiochip: 1
line: 11
- pin: 57 # GPIO1_D1 (physical 33)
gpiochip: 1
line: 25
DIO3_TCXO_VOLTAGE: true
DIO2_AS_RF_SWITCH: true
spidev: spidev0.0
# CS: # GPIO0_B6 (SPI1_CSN0, physical 24)
# pin: 14
# gpiochip: 0
# line: 14
TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8]
@@ -1,37 +0,0 @@
Meta:
name: RAK6421 + RAK13302 Slot 2
support: community # Promote when tested
compatible:
- ebyte-ecb41-pge # Armbian
Lora:
Module: sx1262
IRQ: # GPIO0_B4 (IO6, physical 12)
pin: 12
gpiochip: 0
line: 12
Reset: # GPIO1_C3 (IO4, physical 18)
pin: 51
gpiochip: 1
line: 19
Busy: # GPIO0_B3 (IO5, physical 35)
pin: 11
gpiochip: 0
line: 11
# Ant_sw: # GPIO1_C2 (IO3, physical 16)
# pin: 50
# gpiochip: 1
# line: 18
Enable_Pins:
- pin: 2 # GPIO0_A2 (physical 37)
gpiochip: 0
line: 2
- pin: 50 # GPIO1_C2 (physical 16)
gpiochip: 1
line: 18
spidev: spidev0.1
# CS: # GPIO0_A7 (SPI1_CSN1, physical 26)
# pin: 7
# gpiochip: 0
# line: 7
TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8]
@@ -1,30 +0,0 @@
Meta:
name: Waveshare SX1262
support: deprecated
compatible:
- ebyte-ecb41-pge # Armbian
Lora:
Module: sx1262 # Waveshare SX126X XXXM
DIO2_AS_RF_SWITCH: true
CS: # GPIO0_A1 (physical 40)
pin: 1
gpiochip: 0
line: 1
IRQ: # GPIO0_A3 (physical 36)
pin: 3
gpiochip: 0
line: 3
Busy: # GPIO0_A0 (physical 38)
pin: 0
gpiochip: 0
line: 0
Reset: # GPIO0_B4 (physical 12)
pin: 12
gpiochip: 0
line: 12
SX126X_ANT_SW: # GPIO1_B2 (physical 31)
pin: 42
gpiochip: 1
line: 10
spidev: spidev0.0
+2 -8
View File
@@ -1,12 +1,6 @@
Meta:
name: RAK6421 + RAK13300 Slot 1 (Autoconf default)
support: official
compatible:
- raspberry-pi
Lora:
### RAK13300 in Slot 1
### RAK13300in Slot 1
Module: sx1262
IRQ: 22 #IO6
Reset: 16 # IO4
@@ -18,4 +12,4 @@ Lora:
DIO2_AS_RF_SWITCH: true
spidev: spidev0.0
# GPIO_DETECT_PA: 13
TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8]
TX_GAIN_LORA: [8]
@@ -1,9 +1,3 @@
Meta:
name: Luckfox Lyra PicoCalc Wio LoRa SX1262
support: official
compatible:
- luckfox-lyra-plus # Armbian
Lora:
Module: sx1262
DIO2_AS_RF_SWITCH: true
-7
View File
@@ -2,13 +2,6 @@
# Enable overlay 'luckfox-lyra-ultra-w-spi0-cs0-spidev' with armbian-config
# https://github.com/wehooper4/Meshtastic-Hardware/tree/main/Luckfox%20Ultra%20Hat
# 1 Watt Lyra Ultra hat
Meta:
name: wehooper4 Luckfox Ultra 1W
support: community
compatible:
- luckfox-pico-ultra # Armbian
- luckfox-lyra-ultra # Armbian
Lora:
Module: sx1262
DIO2_AS_RF_SWITCH: true
-7
View File
@@ -2,13 +2,6 @@
# Enable overlay 'luckfox-lyra-ultra-w-spi0-cs0-spidev' with armbian-config
# https://github.com/wehooper4/Meshtastic-Hardware/tree/main/Luckfox%20Ultra%20Hat
# 2 Watt Lyra Ultra hat
Meta:
name: wehooper4 Luckfox Ultra 2W
support: community
compatible:
- luckfox-pico-ultra # Armbian
- luckfox-lyra-ultra # Armbian
Lora:
Module: sx1262
DIO2_AS_RF_SWITCH: true
@@ -2,12 +2,6 @@
# Enable overlay 'luckfox-lyra-plus-spi0-cs0_rmio13-spidev' with armbian-config
# Waveshare LoRa HAT for Raspberry Pi Pico
# https://www.waveshare.com/wiki/Pico-LoRa-SX1262
Meta:
name: Waveshare LoRa HAT for Raspberry Pi Pico
support: community
compatible:
- luckfox-lyra-plus # Armbian
Lora:
Module: sx1262
DIO2_AS_RF_SWITCH: true
@@ -1,39 +0,0 @@
# MeshAdv-Pi E22-900M30S
# https://github.com/chrismyers2000/MeshAdv-Pi-Hat
Meta:
name: MeshAdv-Pi E22-900M30S
support: community
compatible:
- luckfox-lyra-zero-w # Armbian
Lora:
Module: sx1262
CS: # GPIO0_C2 (physical 40)
pin: 18
gpiochip: 0
line: 18
IRQ: # GPIO1_D1 (physical 36)
pin: 57
gpiochip: 1
line: 25
Busy: # GPIO0_C1 (physical 38)
pin: 17
gpiochip: 0
line: 17
Reset: # GPIO0_B6 (physical 12)
pin: 14
gpiochip: 0
line: 14
TXen: # GPIO1_C2 (physical 33)
pin: 50
gpiochip: 1
line: 18
RXen: # GPIO1_D2 (physical 32)
pin: 58
gpiochip: 1
line: 26
DIO3_TCXO_VOLTAGE: true
# Only for E22-900M33S:
# Limit the output power to 8 dBm
# SX126X_MAX_POWER: 8
spidev: spidev0.0
@@ -1,33 +0,0 @@
# MeshAdv Mini E22-900M22S
# https://github.com/chrismyers2000/MeshAdv-Mini
Meta:
name: MeshAdv Mini E22-900M22S
support: community
compatible:
- luckfox-lyra-zero-w # Armbian
Lora:
Module: sx1262 # Ebyte E22-900M22S
CS: # GPIO0_B2_d (phys 24, RPi CE0)
pin: 10
gpiochip: 0
line: 10
IRQ: # GPIO1_D1_d (phys 36, RPi GPIO16)
pin: 57
gpiochip: 1
line: 25
Busy: # GPIO0_C1_d (phys 38, RPi GPIO20)
pin: 17
gpiochip: 0
line: 17
Reset: # GPIO0_B4_d (phys 18, RPi GPIO24)
pin: 12
gpiochip: 0
line: 12
RXen: # GPIO1_D2_d (phys 32, RPi GPIO12)
pin: 58
gpiochip: 1
line: 26
DIO2_AS_RF_SWITCH: true
DIO3_TCXO_VOLTAGE: true
spidev: spidev0.0
@@ -1,38 +0,0 @@
Meta:
name: RAK6421 + RAK13300 Slot 1
support: community # Promote when tested
compatible:
- luckfox-lyra-zero-w # Armbian
Lora:
Module: sx1262
IRQ: # GPIO0_A5 (IO6)
pin: 5
gpiochip: 0
line: 5
Reset: # GPIO1_D1 (IO4)
pin: 57
gpiochip: 1
line: 25
Busy: # GPIO0_B4 (IO5)
pin: 12
gpiochip: 0
line: 12
# Ant_sw: # GPIO1_C2 (IO3)
# pin: 50
# gpiochip: 1
# line: 18
Enable_Pins:
- pin: 58 # GPIO1_D2
gpiochip: 1
line: 26
- pin: 50 # GPIO1_C2
gpiochip: 1
line: 18
DIO3_TCXO_VOLTAGE: true
DIO2_AS_RF_SWITCH: true
spidev: spidev0.0
# CS: # GPIO0_B2
# pin: 10
# gpiochip: 0
# line: 10
@@ -1,36 +0,0 @@
Meta:
name: RAK6421 + RAK13300 Slot 2
support: community # Promote when tested
compatible:
- luckfox-lyra-zero-w # Armbian
Lora:
Module: sx1262
IRQ: # GPIO0_B6 (IO6)
pin: 14
gpiochip: 0
line: 14
Reset: # GPIO0_B4 (IO4)
pin: 12
gpiochip: 0
line: 12
Busy: # GPIO1_C0 (IO5)
pin: 48
gpiochip: 1
line: 16
# Ant_sw: # GPIO0_B5 (IO3)
# pin: 13
# gpiochip: 0
# line: 13
Enable_Pins:
- pin: 51 # GPIO1_C3
gpiochip: 1
line: 19
- pin: 13 # GPIO0_B5
gpiochip: 0
line: 13
spidev: spidev0.1
# CS: # GPIO0_B1
# pin: 9
# gpiochip: 0
# line: 9
@@ -1,39 +0,0 @@
Meta:
name: RAK6421 + RAK13300 Slot 1
support: community # Promote when tested
compatible:
- luckfox-lyra-zero-w # Armbian
Lora:
Module: sx1262
IRQ: # GPIO0_A5 (IO6)
pin: 5
gpiochip: 0
line: 5
Reset: # GPIO1_D1 (IO4)
pin: 57
gpiochip: 1
line: 25
Busy: # GPIO0_B4 (IO5)
pin: 12
gpiochip: 0
line: 12
# Ant_sw: # GPIO1_C2 (IO3)
# pin: 50
# gpiochip: 1
# line: 18
Enable_Pins:
- pin: 58 # GPIO1_D2
gpiochip: 1
line: 26
- pin: 50 # GPIO1_C2
gpiochip: 1
line: 18
DIO3_TCXO_VOLTAGE: true
DIO2_AS_RF_SWITCH: true
spidev: spidev0.0
# CS: # GPIO0_B2
# pin: 10
# gpiochip: 0
# line: 10
TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8]
@@ -1,37 +0,0 @@
Meta:
name: RAK6421 + RAK13300 Slot 2
support: community # Promote when tested
compatible:
- luckfox-lyra-zero-w # Armbian
Lora:
Module: sx1262
IRQ: # GPIO0_B6 (IO6)
pin: 14
gpiochip: 0
line: 14
Reset: # GPIO0_B4 (IO4)
pin: 12
gpiochip: 0
line: 12
Busy: # GPIO1_C0 (IO5)
pin: 48
gpiochip: 1
line: 16
# Ant_sw: # GPIO0_B5 (IO3)
# pin: 13
# gpiochip: 0
# line: 13
Enable_Pins:
- pin: 51 # GPIO1_C3
gpiochip: 1
line: 19
- pin: 13 # GPIO0_B5
gpiochip: 0
line: 13
spidev: spidev0.1
# CS: # GPIO0_B1
# pin: 9
# gpiochip: 0
# line: 9
TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8]
@@ -1,30 +0,0 @@
Meta:
name: Waveshare SX1262
support: deprecated
compatible:
- luckfox-lyra-zero-w # Armbian
Lora:
Module: sx1262 # Waveshare SX126X XXXM
DIO2_AS_RF_SWITCH: true
CS: # GPIO0_C2 (physical 40)
pin: 18
gpiochip: 0
line: 18
IRQ: # GPIO1_D1 (physical 36)
pin: 57
gpiochip: 1
line: 25
Busy: # GPIO0_C1 (physical 38)
pin: 17
gpiochip: 0
line: 17
Reset: # GPIO0_B6 (physical 12)
pin: 14
gpiochip: 0
line: 14
SX126X_ANT_SW: # GPIO1_B3 (physical 31)
pin: 43
gpiochip: 1
line: 11
spidev: spidev0.0
-6
View File
@@ -1,9 +1,3 @@
Meta:
name: Lora Meshstick SX1262
support: official
compatible:
- usb
Lora:
Module: sx1262
CS: 0
@@ -1,41 +0,0 @@
# !! WARNING: Hats on the OK3506 board are installed "backwards" (facing outwards)
# MeshAdv-Pi E22-900M30S
# https://github.com/chrismyers2000/MeshAdv-Pi-Hat
Meta:
name: MeshAdv-Pi E22-900M30S
support: community
compatible:
- forlinx-ok3506-s12 # Armbian
Lora:
Module: sx1262
CS: # GPIO0_B0 (physical 40)
pin: 8
gpiochip: 0
line: 8
IRQ: # GPIO3_B0 (physical 36)
pin: 104
gpiochip: 3
line: 8
Busy: # GPIO0_B1 (physical 38)
pin: 9
gpiochip: 0
line: 9
Reset: # GPIO0_B6 (physical 12)
pin: 14
gpiochip: 0
line: 14
TXen: # GPIO0_A3 (physical 33)
pin: 3
gpiochip: 0
line: 3
RXen: # GPIO0_A2 (physical 32)
pin: 2
gpiochip: 0
line: 2
DIO3_TCXO_VOLTAGE: true
# Only for E22-900M33S:
# Limit the output power to 8 dBm
# SX126X_MAX_POWER: 8
spidev: spidev0.0
@@ -1,35 +0,0 @@
# !! WARNING: Hats on the OK3506 board are installed "backwards" (facing outwards)
# MeshAdv Mini E22-900M22S
# https://github.com/chrismyers2000/MeshAdv-Mini
Meta:
name: MeshAdv Mini E22-900M22S
support: community
compatible:
- forlinx-ok3506-s12 # Armbian
Lora:
Module: sx1262 # Ebyte E22-900M22S
CS: # GPIO0_C3 (physical 24, SPI0_CSN0)
pin: 19
gpiochip: 0
line: 19
IRQ: # GPIO3_B0 (physical 36)
pin: 104
gpiochip: 3
line: 8
Busy: # GPIO0_B1 (physical 38)
pin: 9
gpiochip: 0
line: 9
Reset: # GPIO3_A6 (physical 18)
pin: 102
gpiochip: 3
line: 6
RXen: # GPIO0_A2 (physical 32)
pin: 2
gpiochip: 0
line: 2
DIO2_AS_RF_SWITCH: true
DIO3_TCXO_VOLTAGE: true
spidev: spidev0.0
@@ -1,40 +0,0 @@
# !! WARNING: Hats on the OK3506 board are installed "backwards" (facing outwards)
Meta:
name: RAK6421 + RAK13300 Slot 1
support: community # Promote when tested
compatible:
- forlinx-ok3506-s12 # Armbian
Lora:
Module: sx1262
IRQ: # GPIO3_B5 (IO6, physical 15)
pin: 109
gpiochip: 3
line: 13
Reset: # GPIO3_B0 (IO4, physical 36)
pin: 104
gpiochip: 3
line: 8
Busy: # GPIO3_A6 (IO5, physical 18)
pin: 102
gpiochip: 3
line: 6
# Ant_sw: # GPIO0_A3 (IO3, physical 33)
# pin: 3
# gpiochip: 0
# line: 3
Enable_Pins:
- pin: 2 # GPIO0_A2 (physical 32)
gpiochip: 0
line: 2
- pin: 3 # GPIO0_A3 (physical 33)
gpiochip: 0
line: 3
DIO3_TCXO_VOLTAGE: true
DIO2_AS_RF_SWITCH: true
spidev: spidev0.0
# CS: # GPIO0_C3 (SPI0_CSN0, physical 24)
# pin: 19
# gpiochip: 0
# line: 19
@@ -1,38 +0,0 @@
# !! WARNING: Hats on the OK3506 board are installed "backwards" (facing outwards)
Meta:
name: RAK6421 + RAK13300 Slot 2
support: community # Promote when tested
compatible:
- forlinx-ok3506-s12 # Armbian
Lora:
Module: sx1262
IRQ: # GPIO0_B6 (IO6, physical 12)
pin: 14
gpiochip: 0
line: 14
Reset: # GPIO3_A6 (IO4, physical 18)
pin: 102
gpiochip: 3
line: 6
Busy: # GPIO0_B2 (IO5, physical 35)
pin: 10
gpiochip: 0
line: 10
# Ant_sw: # GPIO3_A7 (IO3, physical 16)
# pin: 103
# gpiochip: 3
# line: 7
Enable_Pins:
- pin: 106 # GPIO3_B2 (physical 37)
gpiochip: 3
line: 10
- pin: 103 # GPIO3_A7 (physical 16)
gpiochip: 3
line: 7
spidev: spidev0.1
# CS: # GPIO0_B7 (SPI0_CSN1, physical 26)
# pin: 15
# gpiochip: 0
# line: 15
@@ -1,41 +0,0 @@
# !! WARNING: Hats on the OK3506 board are installed "backwards" (facing outwards)
Meta:
name: RAK6421 + RAK13302 Slot 1
support: community # Promote when tested
compatible:
- forlinx-ok3506-s12 # Armbian
Lora:
Module: sx1262
IRQ: # GPIO3_B5 (IO6, physical 15)
pin: 109
gpiochip: 3
line: 13
Reset: # GPIO3_B0 (IO4, physical 36)
pin: 104
gpiochip: 3
line: 8
Busy: # GPIO3_A6 (IO5, physical 18)
pin: 102
gpiochip: 3
line: 6
# Ant_sw: # GPIO0_A3 (IO3, physical 33)
# pin: 3
# gpiochip: 0
# line: 3
Enable_Pins:
- pin: 2 # GPIO0_A2 (physical 32)
gpiochip: 0
line: 2
- pin: 3 # GPIO0_A3 (physical 33)
gpiochip: 0
line: 3
DIO3_TCXO_VOLTAGE: true
DIO2_AS_RF_SWITCH: true
spidev: spidev0.0
# CS: # GPIO0_C3 (SPI0_CSN0, physical 24)
# pin: 19
# gpiochip: 0
# line: 19
TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8]
@@ -1,39 +0,0 @@
# !! WARNING: Hats on the OK3506 board are installed "backwards" (facing outwards)
Meta:
name: RAK6421 + RAK13302 Slot 2
support: community # Promote when tested
compatible:
- forlinx-ok3506-s12 # Armbian
Lora:
Module: sx1262
IRQ: # GPIO0_B6 (IO6, physical 12)
pin: 14
gpiochip: 0
line: 14
Reset: # GPIO3_A6 (IO4, physical 18)
pin: 102
gpiochip: 3
line: 6
Busy: # GPIO0_B2 (IO5, physical 35)
pin: 10
gpiochip: 0
line: 10
# Ant_sw: # GPIO3_A7 (IO3, physical 16)
# pin: 103
# gpiochip: 3
# line: 7
Enable_Pins:
- pin: 106 # GPIO3_B2 (physical 37)
gpiochip: 3
line: 10
- pin: 103 # GPIO3_A7 (physical 16)
gpiochip: 3
line: 7
spidev: spidev0.1
# CS: # GPIO0_B7 (SPI0_CSN1, physical 26)
# pin: 15
# gpiochip: 0
# line: 15
TX_GAIN_LORA: [9, 9, 10, 11, 9, 8, 9, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 10, 9, 8]
@@ -1,32 +0,0 @@
# !! WARNING: Hats on the OK3506 board are installed "backwards" (facing outwards)
Meta:
name: Waveshare SX1262
support: deprecated
compatible:
- forlinx-ok3506-s12 # Armbian
Lora:
Module: sx1262 # Waveshare SX126X XXXM
DIO2_AS_RF_SWITCH: true
CS: # GPIO0_B0 (physical 40)
pin: 8
gpiochip: 0
line: 8
IRQ: # GPIO3_B0 (physical 36)
pin: 104
gpiochip: 3
line: 8
Busy: # GPIO0_B1 (physical 38)
pin: 9
gpiochip: 0
line: 9
Reset: # GPIO0_B6 (physical 12)
pin: 14
gpiochip: 0
line: 14
SX126X_ANT_SW: # GPIO3_B3 (physical 31)
pin: 107
gpiochip: 3
line: 11
spidev: spidev0.0
-6
View File
@@ -1,9 +1,3 @@
Meta:
name: Lora Meshstick SX1262
support: community
compatible:
- usb
Lora:
Module: lr1121
CS: 0
+1 -7
View File
@@ -1,11 +1,5 @@
Meta:
name: Pinedio USB SX1262
support: deprecated
compatible:
- usb
Lora:
Module: sx1262
CS: 0
IRQ: 10
spidev: ch341
spidev: ch341
@@ -1,9 +1,3 @@
Meta:
name: raxda-rock2f-starter-edition-hat
support: community
compatible:
- rock-2f # Armbian
Lora:
### Raxda Rock 2F running Armbian Linux 6.1.99-vendor-rk35xx
@@ -1,11 +1,5 @@
# https://www.waveshare.com/core1262-868m.htm
# https://github.com/markbirss/lora-starter-edition-sx1262-i2c
Meta:
name: lora-starter-edition-sx1262-i2c
support: community
compatible:
- raspberry-pi
Lora:
Module: sx1262 # Starter Edition SX1262 I2C Raspberry Pi HAT
DIO2_AS_RF_SWITCH: true
@@ -1,9 +1,3 @@
Meta:
name: meshstick-1262
support: official
compatible:
- usb
Lora:
Module: sx1262
CS: 0
-6
View File
@@ -1,9 +1,3 @@
Meta:
name: meshtoad-e22
support: official
compatible:
- usb
Lora:
Module: sx1262
CS: 0
@@ -1,9 +1,3 @@
Meta:
name: umesh-1262-30dbm
support: community
compatible:
- clockwork-uconsole
Lora:
Module: sx1262
CS: 0
@@ -1,9 +1,3 @@
Meta:
name: umesh-1268-30dbm
support: community
compatible:
- clockwork-uconsole
Lora:
Module: sx1268
CS: 0
-6
View File
@@ -1,9 +1,3 @@
Meta:
name: Waveshare SX1262
support: deprecated
compatible:
- raspberry-pi
Lora:
Module: sx1262 # Waveshare SX126X XXXM
DIO2_AS_RF_SWITCH: true
@@ -1,12 +1,5 @@
# https://www.waveshare.com/pico-lora-sx1262-868m.htm
# https://github.com/markbirss/lora-ws-raspberry-pi-pico-to-rpi-adapter
Meta:
name: ws-raspberry-pico-to-rpi-adapter
support: community
compatible:
- raspberry-pi
Lora:
Module: sx1262 # Waveshare Raspberry Pi Pico to Raspberry Pi HAT Adapter
DIO2_AS_RF_SWITCH: true
@@ -15,12 +15,6 @@
# 5 CS 24
# 26 DIO1/IRQ 26
Meta:
name: ws-raspberry-pico-to-orangepi-03
support: community
compatible:
- orange-pi-zero-3 # Armbian
Lora:
Module: sx1262 # Waveshare Raspberry Pico Lora module
DIO2_AS_RF_SWITCH: true
+1 -1
View File
@@ -43,7 +43,7 @@ for pio_env in pio_envs:
env = {
"ci": {"board": pio_env, "platform": env_platform},
"board_level": cfg.get(f"env:{pio_env}", "board_level", default=None),
"board_check": cfg.get(f"env:{pio_env}", "board_check", default="false").strip().lower() == "true",
"board_check": bool(cfg.get(f"env:{pio_env}", "board_check", default=False)),
}
all_envs.append(env)
+29 -54
View File
@@ -1,31 +1,25 @@
#!/usr/bin/env python3
"""Generate release notes from the actual release commit range."""
"""
Generate release notes from merged PRs on develop and master branches.
Categorizes PRs into Enhancements and Bug Fixes/Maintenance sections.
"""
import argparse
import json
import re
import subprocess
import re
import json
import sys
from datetime import datetime
def get_last_release_tag(compare_ref, exclude_tag=None):
"""Get the most recent version tag merged into compare_ref."""
def get_last_release_tag():
"""Get the most recent release tag."""
result = subprocess.run(
["git", "tag", "--merged", compare_ref, "--sort=-version:refname", "v*"],
["git", "describe", "--tags", "--abbrev=0"],
capture_output=True,
text=True,
check=True,
)
for line in result.stdout.splitlines():
candidate = line.strip()
if not candidate:
continue
if exclude_tag and candidate == exclude_tag:
continue
return candidate
raise subprocess.CalledProcessError(result.returncode, result.args, output=result.stdout, stderr=result.stderr)
return result.stdout.strip()
def get_tag_date(tag):
@@ -39,18 +33,18 @@ def get_tag_date(tag):
return result.stdout.strip()
def get_merged_prs_in_range(tag, compare_ref):
"""Get all merged PRs in the git range between tag and compare_ref."""
def get_merged_prs_since_tag(tag, branch):
"""Get all merged PRs since the given tag on the specified branch."""
# Get commits since tag on the branch - look for PR numbers in parentheses
result = subprocess.run(
[
"git",
"log",
f"{tag}..{compare_ref}",
f"{tag}..origin/{branch}",
"--oneline",
],
capture_output=True,
text=True,
check=True,
)
prs = []
@@ -71,25 +65,6 @@ def get_merged_prs_in_range(tag, compare_ref):
return prs
def parse_args():
"""Parse CLI arguments."""
parser = argparse.ArgumentParser(
description="Generate release notes from the actual release commit range."
)
parser.add_argument("new_version", help="Version that will be tagged for this release")
parser.add_argument(
"--base-tag",
dest="base_tag",
help="Existing version tag to diff from. Defaults to the latest version tag merged into the compare ref.",
)
parser.add_argument(
"--compare-ref",
default="HEAD",
help="Git ref to diff to. Defaults to HEAD.",
)
return parser.parse_args()
def get_pr_details(pr_number):
"""Get PR details from GitHub API via gh CLI."""
try:
@@ -293,28 +268,28 @@ def get_new_contributors(pr_details_list, tag, repo="meshtastic/firmware"):
def main():
args = parse_args()
new_version = args.new_version
compare_ref = args.compare_ref
current_tag = f"v{new_version}"
if len(sys.argv) < 2:
print("Usage: generate_release_notes.py <new_version>", file=sys.stderr)
sys.exit(1)
new_version = sys.argv[1]
# Get last release tag
try:
last_tag = args.base_tag or get_last_release_tag(compare_ref, exclude_tag=current_tag)
last_tag = get_last_release_tag()
except subprocess.CalledProcessError:
print("Error: Could not find last release tag", file=sys.stderr)
sys.exit(1)
print(
f"Resolved release note range: {last_tag}..{compare_ref}",
file=sys.stderr,
)
# Collect PRs from both branches
all_pr_numbers = set()
try:
all_pr_numbers = set(get_merged_prs_in_range(last_tag, compare_ref))
except subprocess.CalledProcessError as e:
print(f"Error: Could not get PRs for range {last_tag}..{compare_ref}: {e}", file=sys.stderr)
sys.exit(1)
for branch in ["develop", "master"]:
try:
prs = get_merged_prs_since_tag(last_tag, branch)
all_pr_numbers.update(prs)
except Exception as e:
print(f"Warning: Could not get PRs from {branch}: {e}", file=sys.stderr)
# Get details for all PRs
enhancements = []
@@ -87,12 +87,6 @@
</screenshots>
<releases>
<release version="2.7.22" date="2026-04-06">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.22</url>
</release>
<release version="2.7.21" date="2026-03-11">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.21</url>
</release>
<release version="2.7.20" date="2026-02-11">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.20</url>
</release>
-54
View File
@@ -1,54 +0,0 @@
{
"build": {
"arduino": {
"ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
["0x239A", "0x4405"],
["0x239A", "0x0029"],
["0x239A", "0x002A"],
["0x2886", "0x1667"]
],
"usb_product": "HT-n5262G",
"mcu": "nrf52840",
"variant": "heltec_mesh_node_t096",
"variants_dir": "variants",
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_flags": "-DS140",
"sd_name": "s140",
"sd_version": "6.1.1",
"sd_fwid": "0x00B6"
},
"bootloader": {
"settings_addr": "0xFF000"
}
},
"connectivity": ["bluetooth"],
"debug": {
"jlink_device": "nRF52840_xxAA",
"onboard_tools": ["jlink"],
"svd_path": "nrf52840.svd",
"openocd_target": "nrf52840-mdk-rs"
},
"frameworks": ["arduino"],
"name": "Heltec nrf (Adafruit BSP)",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"speed": 115200,
"protocol": "nrfutil",
"protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"],
"use_1200bps_touch": true,
"require_upload_port": true,
"wait_for_upload_port": true
},
"url": "https://heltec.org/",
"vendor": "Heltec"
}
-38
View File
@@ -1,38 +0,0 @@
{
"build": {
"arduino": {
"ldscript": "esp32s3_out.ld",
"memory_type": "qio_opi",
"partitions": "default_16MB.csv"
},
"core": "esp32",
"extra_flags": [
"-DBOARD_HAS_PSRAM",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=0",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=1"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "qio",
"hwids": [["0x303A", "0x1001"]],
"mcu": "esp32s3",
"variant": "esp32s3"
},
"connectivity": ["wifi", "bluetooth", "lora"],
"debug": {
"openocd_target": "esp32s3.cfg"
},
"frameworks": ["arduino", "espidf"],
"name": "LilyGo T5-ePaper-S3",
"upload": {
"flash_size": "16MB",
"maximum_ram_size": 327680,
"maximum_size": 16777216,
"require_upload_port": true,
"speed": 921600
},
"url": "https://lilygo.cc/products/t5-e-paper-s3-pro",
"vendor": "LILYGO"
}
-29
View File
@@ -1,29 +0,0 @@
{
"build": {
"arduino": {
"ldscript": "esp32_out.ld"
},
"core": "esp32",
"extra_flags": "-DARDUINO_T_Beam",
"f_cpu": "240000000L",
"f_flash": "40000000L",
"flash_mode": "dio",
"mcu": "esp32",
"variant": "tbeam"
},
"connectivity": ["wifi", "bluetooth", "can", "ethernet"],
"debug": {
"openocd_board": "esp-wroom-32.cfg"
},
"frameworks": ["arduino", "espidf"],
"name": "TTGO T-Beam (PSRAM Disabled)",
"upload": {
"flash_size": "4MB",
"maximum_ram_size": 1310720,
"maximum_size": 4194304,
"require_upload_port": true,
"speed": 460800
},
"url": "https://github.com/LilyGO/TTGO-T-Beam",
"vendor": "TTGO"
}
-12
View File
@@ -1,15 +1,3 @@
meshtasticd (2.7.22.0) unstable; urgency=medium
* Version 2.7.22
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Mon, 06 Apr 2026 11:34:12 +0000
meshtasticd (2.7.21.0) unstable; urgency=medium
* Version 2.7.21
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Wed, 11 Mar 2026 11:45:36 +0000
meshtasticd (2.7.20.0) unstable; urgency=medium
* Version 2.7.20
+2 -11
View File
@@ -10,10 +10,6 @@ export PLATFORMIO_SETTING_CHECK_PRUNE_SYSTEM_THRESHOLD=10240
# Download libraries to `pio`
platformio pkg install -e native-tft
platformio pkg install -e native-tft -t platformio/tool-scons@4.40502.0
# Mangle PlatformIO cache to prevent internet access at build-time
# Simply adds 1 to all expiry (epoch) timestamps, adding ~500 years to expiry date
cp pio/core/.cache/downloads/usage.db pio/core/.cache/downloads/usage.db.bak
jq -c 'with_entries(.value |= (. | tostring + "1" | tonumber))' pio/core/.cache/downloads/usage.db.bak >pio/core/.cache/downloads/usage.db
# Compress `pio` directory to prevent dh_clean from sanitizing it
tar -cf pio.tar pio/
rm -rf pio
@@ -27,10 +23,5 @@ rm -rf debian/changelog
dch --create --distribution "$SERIES" --package "$package" --newversion "$PKG_VERSION~$SERIES" \
"GitHub Actions Automatic packaging for $PKG_VERSION~$SERIES"
if [[ -n $GPG_KEY_ID ]]; then
# Build and sign the source deb
debuild -S -nc -k"$GPG_KEY_ID"
else
# Build the source deb without signing (forks)
debuild -S -nc
fi
# Build the source deb
debuild -S -nc -k"$GPG_KEY_ID"
+625
View File
@@ -0,0 +1,625 @@
# BLE Mesh Implementation Plan
## Overview
Add BLE as a mesh transport alongside LoRa, enabling Meshtastic nodes to relay packets over Bluetooth Low Energy. Follow the same handler pattern used by UDP multicast ([UdpMulticastHandler](src/mesh/udp/UdpMulticastHandler.h)) — a standalone handler class with `onSend()` hooked into `Router::send()` and received packets injected via `router->enqueueReceivedMessage()`. No changes to `RadioInterface` or Router architecture needed.
BLE mesh is useful for:
- Dense indoor deployments where LoRa range is excessive
- Extending mesh reach through devices that lack LoRa hardware
- Hybrid LoRa+BLE networks (gateway nodes bridge both transports)
- Lower latency links between nearby nodes
---
## Architecture
### Pattern: Same as UDP Multicast
```
Router::send()
│
┌───────────────────┼───────────────────┐
│ │ │
▼ ▼ ▼
iface->send(p) udpHandler->onSend(p) bleMeshHandler->onSend(p)
(LoRa) (UDP) (BLE mesh)
Router::enqueueReceivedMessage()
▲
┌───────────────────┼───────────────────┐
│ │ │
LoRa RX ISR udpHandler->onReceive() bleMeshHandler->onReceive()
```
The existing UDP pattern ([Router.cpp:378-382](src/mesh/Router.cpp#L378-L382)):
```cpp
#if HAS_UDP_MULTICAST
if (udpHandler && config.network.enabled_protocols & ...) {
udpHandler->onSend(const_cast<meshtastic_MeshPacket *>(p));
}
#endif
```
BLE mesh hooks in identically, right next to it.
---
## Phase 1: Protobuf & Configuration
### 1.1 Transport mechanism enum
**File:** protobufs repo (`mesh.proto`)
Add a new transport type alongside the existing ones:
```protobuf
enum TransportMechanism {
// ... existing 0-7 ...
TRANSPORT_BLE_MESH = 8;
}
```
### 1.2 BLE Mesh configuration
Add BLE mesh config to the device config protobuf:
```protobuf
message BleMeshConfig {
bool enabled = 1;
// Operating mode
enum Mode {
OFF = 0;
ADVERTISE_AND_SCAN = 1; // connectionless (extended adv)
GATT = 2; // connection-based
HYBRID = 3; // both
}
Mode mode = 2;
// Scan/advertising interval in ms (latency vs power tradeoff)
uint32_t interval_ms = 3; // default: 500ms
// TX power level (platform-mapped)
int8_t tx_power = 4; // dBm
// Max BLE mesh peers to track
uint32_t max_peers = 5; // default: 8
}
```
---
## Phase 2: BLE Mesh Handler — Core Class
### 2.1 Base handler (platform-agnostic)
**New file:** `src/mesh/BLEMeshHandler.h`
Follow the `UdpMulticastHandler` pattern — a simple class, not a `RadioInterface`:
```cpp
#pragma once
#if HAS_BLE_MESH
#include "configuration.h"
#include "mesh/Router.h"
class BLEMeshHandler {
public:
BLEMeshHandler() : isRunning(false) {}
virtual void start() = 0;
virtual void stop() = 0;
// Called from Router::send() — broadcast packet over BLE
virtual bool onSend(const meshtastic_MeshPacket *mp) = 0;
protected:
bool isRunning;
// Shared RX path: decode and enqueue into router
void deliverToRouter(const uint8_t *data, size_t len) {
meshtastic_MeshPacket mp;
if (!pb_decode_from_bytes(data, len, &meshtastic_MeshPacket_msg, &mp))
return;
if (mp.which_payload_variant != meshtastic_MeshPacket_encrypted_tag)
return;
mp.transport_mechanism =
meshtastic_MeshPacket_TransportMechanism_TRANSPORT_BLE_MESH;
mp.rx_snr = 0;
mp.rx_rssi = 0;
UniquePacketPoolPacket p = packetPool.allocUniqueCopy(mp);
if (router)
router->enqueueReceivedMessage(p.release());
}
// Shared TX path: encode packet to bytes
size_t encodeForBLE(const meshtastic_MeshPacket *mp, uint8_t *buf, size_t bufLen) {
return pb_encode_to_bytes(buf, bufLen, &meshtastic_MeshPacket_msg, mp);
}
};
#endif // HAS_BLE_MESH
```
### 2.2 Wire format
Same as UDP multicast — protobuf-encoded `meshtastic_MeshPacket` (already encrypted). This means:
- Packets are encoded with `pb_encode_to_bytes()` on TX
- Decoded with `pb_decode_from_bytes()` on RX
- Encryption already applied by Router before `onSend()` is called
- Same deduplication via `PacketHistory` (keyed on `sender, id`)
### 2.3 Echo prevention
Same pattern as UDP ([UdpMulticastHandler.h:102](src/mesh/udp/UdpMulticastHandler.h#L102)):
```cpp
if (mp->transport_mechanism == meshtastic_MeshPacket_TransportMechanism_TRANSPORT_BLE_MESH) {
LOG_ERROR("Attempt to send BLE-sourced packet over BLE");
return false;
}
```
---
## Phase 3: ESP32 Implementation (NimBLE)
### Platform: ESP32, ESP32-S3, ESP32-C3, ESP32-C6
**New files:** `src/nimble/NimbleBLEMesh.h` / `NimbleBLEMesh.cpp`
```cpp
#pragma once
#if HAS_BLE_MESH && defined(ARCH_ESP32)
#include "mesh/BLEMeshHandler.h"
#include "nimble/NimbleBluetooth.h"
class NimbleBLEMesh : public BLEMeshHandler {
public:
void start() override;
void stop() override;
bool onSend(const meshtastic_MeshPacket *mp) override;
private:
// --- Advertising mode ---
void startScanning();
void stopScanning();
static void onScanResult(const ble_gap_disc_desc *desc, void *arg);
void broadcastViaAdv(const uint8_t *data, size_t len);
// --- GATT mode ---
void setupMeshService();
static int onMeshCharWrite(uint16_t conn_handle,
const ble_gatt_error *error,
ble_gatt_attr *attr, void *arg);
void sendViaGATT(const uint8_t *data, size_t len);
// --- Peer tracking ---
struct BLEPeer {
NodeNum nodeNum;
ble_addr_t addr;
int8_t rssi;
uint32_t lastSeenMs;
uint16_t connHandle; // 0 if not connected
};
std::vector<BLEPeer> peers;
void updatePeer(const ble_addr_t &addr, NodeNum nodeNum, int8_t rssi);
void pruneStale();
};
#endif
```
### 3.1 Advertising mode (connectionless)
Encode mesh packets into BLE extended advertisements with Meshtastic manufacturer data.
**TX flow:**
1. `onSend()` called from `Router::send()`
2. Encode packet via `encodeForBLE()`
3. Pack into extended advertisement manufacturer data
4. Broadcast for configurable number of intervals
**RX flow:**
1. NimBLE scan callback fires
2. Check manufacturer ID for Meshtastic magic
3. Extract protobuf bytes
4. Call `deliverToRouter()`
**Advertisement structure:**
```
AD Type: 0xFF (Manufacturer Specific)
Company ID: 0x????? (Meshtastic BLE SIG ID or 0xFFFF)
Mesh Version: 1 byte
Protobuf payload: variable (up to ~244 bytes with extended adv)
```
**Key NimBLE APIs:**
- `ble_gap_ext_adv_set_data()` / `ble_gap_ext_adv_start()` — extended adv TX
- `ble_gap_disc()` or `ble_gap_ext_disc()` — scanning
- Scan callback receives `ble_gap_disc_desc` or `ble_gap_ext_disc_desc`
### 3.2 GATT mode (connection-based)
Add a new GATT service for mesh relay, separate from the phone API service:
**New Mesh Relay Service:**
```
Mesh Relay Service UUID: (new, distinct from phone service in BluetoothCommon.h)
├── MeshTX (WRITE_NO_RSP) — peers write mesh packets to us
├── MeshRX (NOTIFY) — we notify peers of new mesh packets
└── MeshPeer (READ) — our NodeNum for discovery
```
**TX:** Write to MeshTX on all connected peers (broadcast) or specific next-hop peer (directed).
**RX:** MeshTX write callback → `deliverToRouter()`.
### 3.3 Coexistence with phone BLE
Both services registered on the same NimBLE host:
- Phone API service: existing UUIDs ([BluetoothCommon.h](src/BluetoothCommon.h))
- Mesh relay service: new UUIDs
- Separate connection handles for phone vs mesh peers
- Use extended adv set 0 for phone, set 1 for mesh (NimBLE supports multiple adv sets)
- Reserve 1 connection for phone, remaining for mesh peers
### 3.4 ESP32 variant considerations
| Variant | BLE | Extended Adv | Max Conn | Strategy |
| -------- | --- | ------------ | -------- | --------------------------------------- |
| ESP32 | 4.2 | No | 9 | GATT-only (legacy adv too small at 31B) |
| ESP32-S3 | 5.0 | Yes | 9 | Full adv+GATT |
| ESP32-C3 | 5.0 | Yes | 3 | Adv preferred (limited connections) |
| ESP32-C6 | 5.0 | Yes | 9 | Full adv+GATT |
---
## Phase 4: nRF52 Implementation (SoftDevice/Bluefruit)
### Platform: nRF52840 (primary), nRF52833
**New files:** `src/platform/nrf52/NRF52BLEMesh.h` / `NRF52BLEMesh.cpp`
```cpp
#pragma once
#if HAS_BLE_MESH && defined(ARCH_NRF52)
#include "mesh/BLEMeshHandler.h"
#include <bluefruit.h>
class NRF52BLEMesh : public BLEMeshHandler {
public:
void start() override;
void stop() override;
bool onSend(const meshtastic_MeshPacket *mp) override;
private:
// Advertising mode
void startScanning();
static void onScanCallback(ble_gap_evt_adv_report_t *report);
void broadcastViaAdv(const uint8_t *data, size_t len);
// GATT mode
BLEService meshService;
BLECharacteristic meshTxChar;
BLECharacteristic meshRxChar;
void setupMeshService();
static void onMeshWrite(uint16_t connHandle, BLECharacteristic *chr,
uint8_t *data, uint16_t len);
void sendViaGATT(const uint8_t *data, size_t len);
// Peers
struct BLEPeer {
NodeNum nodeNum;
ble_gap_addr_t addr;
int8_t rssi;
uint32_t lastSeenMs;
uint16_t connHandle;
};
std::vector<BLEPeer> peers;
};
#endif
```
### 4.1 Advertising mode
nRF52840 supports BLE 5.0 extended advertisements (up to 247 bytes).
**Bluefruit APIs:**
- `Bluefruit.Scanner.setRxCallback()` — scan result callback
- `Bluefruit.Scanner.start()` / `.stop()` — control scanning
- `Bluefruit.Advertising` — for standard adv
- Raw SoftDevice `sd_ble_gap_adv_set_configure()` if Bluefruit doesn't expose extended adv data
**Implementation:**
- Use Bluefruit Scanner for RX (filter by Meshtastic manufacturer ID)
- For TX, either use Bluefruit Advertising API or drop to raw SoftDevice for extended adv payloads
### 4.2 GATT mode
Same service structure as ESP32, using Bluefruit GATT APIs:
```cpp
meshService = BLEService(MESH_RELAY_SERVICE_UUID);
meshTxChar = BLECharacteristic(MESH_TX_CHAR_UUID);
meshTxChar.setProperties(CHR_PROPS_WRITE_WO_RESP);
meshTxChar.setWriteCallback(onMeshWrite);
meshTxChar.setMaxLen(512);
meshRxChar = BLECharacteristic(MESH_RX_CHAR_UUID);
meshRxChar.setProperties(CHR_PROPS_NOTIFY);
meshRxChar.setMaxLen(512);
```
**Peer connections:**
- nRF52840 SoftDevice S140 supports up to 20 concurrent connections
- Use `Bluefruit.Central` for outbound connections to mesh peers
- Bluefruit supports simultaneous peripheral (phone) + central (mesh peers)
### 4.3 Coexistence with phone BLE
Existing phone service ([NRF52Bluetooth.cpp](src/platform/nrf52/NRF52Bluetooth.cpp)) uses peripheral role:
- Keep phone API service as-is
- Add mesh relay service alongside it
- Scanner shared between peer discovery and mesh RX (filter by service UUID or manufacturer data)
- SoftDevice connection budget: 1 peripheral (phone) + N central (mesh peers)
### 4.4 nRF52 extras
| Feature | nRF52840 | nRF52833 |
| ---------------------- | -------- | -------- |
| Extended Adv | Yes | Yes |
| Max Connections | 20 | 20 |
| RAM per connection | ~1.8KB | ~1.8KB |
| Coded PHY (Long Range) | Yes | Yes |
**Coded PHY:** BLE Coded PHY (125kbps) provides ~4x range vs 1Mbps PHY. Could be used for extended-range BLE mesh links as a configuration option.
---
## Phase 5: Router Integration
### 5.1 Hook into Router::send()
**File:** [src/mesh/Router.cpp](src/mesh/Router.cpp) — add right next to UDP handler:
```cpp
#if HAS_BLE_MESH
if (bleMeshHandler && config.bluetooth.mesh_enabled) {
bleMeshHandler->onSend(p);
}
#endif
```
### 5.2 Global handler instance
**File:** [src/main.h](src/main.h):
```cpp
#ifdef HAS_BLE_MESH
#include "mesh/BLEMeshHandler.h"
extern BLEMeshHandler *bleMeshHandler;
#endif
```
**File:** [src/main.cpp](src/main.cpp):
```cpp
#ifdef HAS_BLE_MESH
BLEMeshHandler *bleMeshHandler = nullptr;
#endif
// In setup():
#ifdef HAS_BLE_MESH
#if defined(ARCH_ESP32)
bleMeshHandler = new NimbleBLEMesh();
#elif defined(ARCH_NRF52)
bleMeshHandler = new NRF52BLEMesh();
#endif
bleMeshHandler->start();
#endif
```
### 5.3 NODENUM_BROADCAST_NO_LORA
Already reserved ([MeshTypes.h:13-14](src/mesh/MeshTypes.h#L13-L14)) and already dropped by LoRa ([RadioLibInterface.cpp:170](src/mesh/RadioLibInterface.cpp#L170)). BLE mesh handler should still send packets addressed to `NODENUM_BROADCAST_NO_LORA` — this is its purpose.
### 5.4 Feature flag
**File:** `src/configuration.h` or per-variant `platformio.ini`:
```cpp
#define HAS_BLE_MESH 1 // Enable BLE mesh transport
```
Disabled by default. Enabled per-board in variant configs for boards with sufficient BLE capability.
---
## Phase 6: Peer Discovery & Management
### 6.1 Discovery methods
1. **Passive scanning**: Listen for Meshtastic manufacturer data in BLE advertisements
2. **Service discovery**: Scan for Mesh Relay Service UUID (GATT mode)
3. **NodeDB integration**: Update `meshtastic_NodeInfoLite` with BLE reachability when a peer is discovered
### 6.2 Peer table
```cpp
struct BLEMeshPeer {
NodeNum nodeNum;
// Platform-specific BLE address (ble_addr_t on ESP32, ble_gap_addr_t on nRF52)
uint8_t addr[6];
uint8_t addrType;
int8_t rssi;
uint32_t lastSeenMs;
uint16_t connHandle; // 0 = not connected
uint8_t failedConnAttempts;
};
```
Max peers: configurable (default 8), bounded by platform connection limits.
### 6.3 Connection policy (GATT mode)
- Auto-connect if RSSI > threshold and slots available
- Disconnect stale peers (no traffic for N minutes)
- Prefer advertising for broadcasts, GATT for directed/reliable
- Exponential backoff on failed connection attempts
---
## Phase 7: Power Management
### 7.1 Scan duty cycling
```
Active scanning: ~15mA (nRF52), ~90mA (ESP32)
Advertising: ~5mA peak per event
Idle: ~0mA additional
Default strategy:
- Scan window: 50ms every 500ms (10% duty cycle)
- ~1.5mA average on nRF52, ~9mA on ESP32
- Configurable via BleMeshConfig.interval_ms
```
### 7.2 Sleep integration
- Light sleep: pause scanning, continue advertising at reduced rate
- Deep sleep: disable BLE mesh entirely
- Hook into existing power management observers
### 7.3 Role-based behavior
| Device Role | BLE Mesh Behavior |
| ----------- | ------------------------------------------- |
| CLIENT | Scan on-demand, advertise when sending |
| CLIENT_MUTE | Receive only, no rebroadcast |
| ROUTER | Continuous scan+advertise, max connections |
| ROUTER_LATE | Delayed rebroadcast (same as LoRa behavior) |
| SENSOR | Minimal: advertise telemetry only |
---
## Phase 8: Testing & Validation
### 8.1 Unit tests
- BLE wire format encode/decode roundtrip
- PacketHistory deduplication across LoRa + BLE
- Peer table management (add, prune, limits)
- Echo prevention (don't re-send BLE-sourced packets over BLE)
### 8.2 Integration tests
- Two ESP32 nodes: BLE-only mesh (no LoRa)
- Two nRF52 nodes: BLE-only mesh
- Three nodes: multi-hop BLE relay
- Hybrid: Node A (LoRa+BLE) <-> Node B (BLE only) <-> Node C (LoRa+BLE)
- Phone API + BLE mesh coexistence
- Power consumption profiling
### 8.3 Compatibility matrix
| Test | ESP32 | ESP32-S3 | ESP32-C3 | ESP32-C6 | nRF52840 |
| ------------- | ----------- | -------- | -------- | -------- | -------- |
| Adv mode | GATT only\* | Yes | Yes | Yes | Yes |
| GATT mode | Yes | Yes | Yes | Yes | Yes |
| Phone coexist | Yes | Yes | Yes | Yes | Yes |
| Multi-hop | Yes | Yes | Yes | Yes | Yes |
\*ESP32 (BLE 4.2): legacy adv limited to 31B, use GATT-only mode
---
## Implementation Order
### Milestone 1: Foundation
1. Protobuf additions (`TRANSPORT_BLE_MESH`, `BleMeshConfig`)
2. `BLEMeshHandler` base class
3. Router hook (alongside UDP handler)
4. Feature flag + main.cpp wiring
### Milestone 2: ESP32 MVP
5. `NimbleBLEMesh` — advertising mode on ESP32-S3
6. Phone BLE coexistence validation
7. Basic two-node BLE mesh test
### Milestone 3: nRF52 MVP
8. `NRF52BLEMesh` — advertising mode
9. Cross-platform interop (ESP32 <-> nRF52 BLE mesh)
### Milestone 4: GATT Mode
10. GATT service + connection management for ESP32
11. GATT service + connection management for nRF52
12. Peer discovery & table management
### Milestone 5: Polish
13. Power management integration
14. Full test matrix
15. ESP32 (BLE 4.2) GATT-only fallback
16. Admin UI / config in phone apps
---
## Risks & Mitigations
| Risk | Impact | Mitigation |
| -------------------------------- | ------------------------- | ---------------------------------------------------------------------------- |
| BLE scan power drain | Battery life | Duty cycling, role-based policy |
| Phone BLE drops during mesh scan | UX regression | Separate adv sets, careful interleaving |
| NimBLE/Bluefruit API differences | Platform code duplication | Thin platform subclasses, shared base logic |
| No extended adv on ESP32 (4.2) | Can't fit packets in adv | GATT-only fallback |
| BLE range (~30-100m) | Coverage gaps | Hybrid LoRa+BLE; Coded PHY on nRF52 |
| Packet storms in dense BLE mesh | Saturation | Existing flood mitigation (PacketHistory, hop_limit, role-based rebroadcast) |
---
## Files to Create
| File | Purpose |
| ------------------------------------- | --------------------------------------------- |
| `src/mesh/BLEMeshHandler.h` | Base handler class (like UdpMulticastHandler) |
| `src/nimble/NimbleBLEMesh.h` | ESP32 BLE mesh header |
| `src/nimble/NimbleBLEMesh.cpp` | ESP32 NimBLE implementation |
| `src/platform/nrf52/NRF52BLEMesh.h` | nRF52 BLE mesh header |
| `src/platform/nrf52/NRF52BLEMesh.cpp` | nRF52 Bluefruit implementation |
## Files to Modify
| File | Change |
| --------------------------------------- | -------------------------------------------------- |
| `src/mesh/Router.cpp` | Add `bleMeshHandler->onSend()` next to UDP handler |
| `src/main.h` | Declare `extern BLEMeshHandler *bleMeshHandler` |
| `src/main.cpp` | Instantiate + start BLE mesh handler |
| `src/configuration.h` | `HAS_BLE_MESH` feature flag |
| Protobufs (`mesh.proto`) | `TRANSPORT_BLE_MESH` enum + `BleMeshConfig` |
| `src/nimble/NimbleBluetooth.cpp` | Coexistence (adv set separation) |
| `src/platform/nrf52/NRF52Bluetooth.cpp` | Coexistence (scanner sharing) |
| Platform `variant.h` / `platformio.ini` | Enable per-board |
+97 -109
View File
@@ -77,10 +77,10 @@ lib_deps =
https://github.com/meshtastic/TinyGPSPlus/archive/71a82db35f3b973440044c476d4bcdc673b104f4.zip
# renovate: datasource=git-refs depName=meshtastic-ArduinoThread packageName=https://github.com/meshtastic/ArduinoThread gitBranch=master
https://github.com/meshtastic/ArduinoThread/archive/b841b0415721f1341ea41cccfb4adccfaf951567.zip
# renovate: datasource=github-tags depName=Nanopb packageName=nanopb/nanopb
https://github.com/nanopb/nanopb/archive/refs/tags/nanopb-0.4.9.1.zip
# renovate: datasource=github-tags depName=ErriezCRC32 packageName=Erriez/ErriezCRC32
https://github.com/Erriez/ErriezCRC32/archive/refs/tags/1.0.1.zip
# renovate: datasource=custom.pio depName=Nanopb packageName=nanopb/library/Nanopb
nanopb/Nanopb@0.4.91
# renovate: datasource=custom.pio depName=ErriezCRC32 packageName=erriez/library/ErriezCRC32
erriez/ErriezCRC32@1.0.1
; Used for the code analysis in PIO Home / Inspect
check_tool = cppcheck
@@ -95,151 +95,139 @@ check_flags =
framework = arduino
lib_deps =
${env.lib_deps}
# renovate: datasource=github-tags depName=NonBlockingRTTTL packageName=end2endzone/NonBlockingRTTTL
https://github.com/end2endzone/NonBlockingRTTTL/archive/refs/tags/1.4.0.zip
build_unflags =
-std=c++11
-std=gnu++11
# renovate: datasource=custom.pio depName=NonBlockingRTTTL packageName=end2endzone/library/NonBlockingRTTTL
end2endzone/NonBlockingRTTTL@1.4.0
build_flags = ${env.build_flags} -Os
-std=gnu++17
build_src_filter = ${env.build_src_filter} -<platform/portduino/> -<graphics/niche/>
; Common libs for communicating over TCP/IP networks such as MQTT
[networking_base]
lib_deps =
# renovate: datasource=github-tags depName=TBPubSubClient packageName=thingsboard/pubsubclient
https://github.com/thingsboard/pubsubclient/archive/refs/tags/v2.12.1.zip
# renovate: datasource=github-tags depName=NTPClient packageName=arduino-libraries/NTPClient
https://github.com/arduino-libraries/NTPClient/archive/refs/tags/3.2.1.zip
# renovate: datasource=custom.pio depName=TBPubSubClient packageName=thingsboard/library/TBPubSubClient
thingsboard/TBPubSubClient@2.12.1
# renovate: datasource=custom.pio depName=NTPClient packageName=arduino-libraries/library/NTPClient
arduino-libraries/NTPClient@3.2.1
; Extra TCP/IP networking libs for supported devices
[networking_extra]
lib_deps =
# renovate: datasource=github-tags depName=Syslog packageName=arcao/Syslog
https://github.com/arcao/Syslog/archive/refs/tags/v2.0.zip
# renovate: datasource=custom.pio depName=Syslog packageName=arcao/library/Syslog
arcao/Syslog@2.0.0
[radiolib_base]
lib_deps =
# renovate: datasource=github-tags depName=RadioLib packageName=jgromes/RadioLib
https://github.com/jgromes/RadioLib/archive/refs/tags/7.6.0.zip
# renovate: datasource=custom.pio depName=RadioLib packageName=jgromes/library/RadioLib
jgromes/RadioLib@7.6.0
[device-ui_base]
lib_deps =
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
https://github.com/meshtastic/device-ui/archive/1897dd17fceb1f101bb1a3245680aa3439edcfdd.zip
https://github.com/meshtastic/device-ui/archive/6c75195e9987b7a49563232234f2f868dd343cae.zip
; Common libs for environmental measurements in telemetry module
[environmental_base]
lib_deps =
# renovate: datasource=github-tags depName=Adafruit BusIO packageName=adafruit/Adafruit_BusIO
https://github.com/adafruit/Adafruit_BusIO/archive/refs/tags/1.17.4.zip
# renovate: datasource=github-tags depName=Adafruit Unified Sensor packageName=adafruit/Adafruit_Sensor
https://github.com/adafruit/Adafruit_Sensor/archive/refs/tags/1.1.15.zip
# renovate: datasource=github-tags depName=Adafruit GFX packageName=adafruit/Adafruit-GFX-Library
https://github.com/adafruit/Adafruit-GFX-Library/archive/refs/tags/1.12.6.zip
# renovate: datasource=github-tags depName=NeoPixel packageName=adafruit/Adafruit_NeoPixel
https://github.com/adafruit/Adafruit_NeoPixel/archive/refs/tags/1.15.4.zip
# renovate: datasource=github-tags depName=Adafruit SSD1306 packageName=adafruit/Adafruit_SSD1306
https://github.com/adafruit/Adafruit_SSD1306/archive/refs/tags/2.5.16.zip
# renovate: datasource=github-tags depName=Adafruit BMP280 packageName=adafruit/Adafruit_BMP280_Library
https://github.com/adafruit/Adafruit_BMP280_Library/archive/refs/tags/3.0.0.zip
# renovate: datasource=github-tags depName=Adafruit BMP085 packageName=adafruit/Adafruit-BMP085-Library
https://github.com/adafruit/Adafruit-BMP085-Library/archive/refs/tags/1.2.4.zip
# renovate: datasource=github-tags depName=Adafruit BME280 packageName=adafruit/Adafruit_BME280_Library
https://github.com/adafruit/Adafruit_BME280_Library/archive/refs/tags/2.3.0.zip
# renovate: datasource=github-tags depName=Adafruit DPS310 packageName=adafruit/Adafruit_DPS310
https://github.com/adafruit/Adafruit_DPS310/archive/refs/tags/1.1.6.zip
# renovate: datasource=github-tags depName=Adafruit SH110x packageName=adafruit/Adafruit_SH110x
https://github.com/adafruit/Adafruit_SH110x/archive/refs/tags/2.1.14.zip
# renovate: datasource=github-tags depName=Adafruit MCP9808 packageName=adafruit/Adafruit_MCP9808_Library
https://github.com/adafruit/Adafruit_MCP9808_Library/archive/refs/tags/2.0.2.zip
# renovate: datasource=github-tags depName=Adafruit INA260 packageName=adafruit/Adafruit_INA260
https://github.com/adafruit/Adafruit_INA260/archive/refs/tags/1.5.3.zip
# renovate: datasource=github-tags depName=Adafruit INA219 packageName=adafruit/Adafruit_INA219
https://github.com/adafruit/Adafruit_INA219/archive/refs/tags/1.2.3.zip
# renovate: datasource=github-tags depName=Adafruit MPU6050 packageName=adafruit/Adafruit_MPU6050
https://github.com/adafruit/Adafruit_MPU6050/archive/refs/tags/2.2.9.zip
# renovate: datasource=github-tags depName=Adafruit LIS3DH packageName=adafruit/Adafruit_LIS3DH
https://github.com/adafruit/Adafruit_LIS3DH/archive/refs/tags/1.3.0.zip
# renovate: datasource=github-tags depName=Adafruit AHTX0 packageName=adafruit/Adafruit_AHTX0
https://github.com/adafruit/Adafruit_AHTX0/archive/refs/tags/2.0.6.zip
# renovate: datasource=github-tags depName=Adafruit LSM6DS packageName=adafruit/Adafruit_LSM6DS
https://github.com/adafruit/Adafruit_LSM6DS/archive/refs/tags/4.7.4.zip
# renovate: datasource=github-tags depName=Adafruit TSL2591 packageName=adafruit/Adafruit_TSL2591_Library
https://github.com/adafruit/Adafruit_TSL2591_Library/archive/refs/tags/1.4.5.zip
# renovate: datasource=github-tags depName=EmotiBit MLX90632 packageName=emotibit/EmotiBit_MLX90632
https://github.com/EmotiBit/EmotiBit_MLX90632/archive/refs/tags/v1.0.8.zip
# renovate: datasource=github-tags depName=Adafruit MLX90614 packageName=adafruit/Adafruit_MLX90614
https://github.com/adafruit/Adafruit-MLX90614-Library/archive/refs/tags/2.1.6.zip
# renovate: datasource=custom.pio depName=Adafruit BusIO packageName=adafruit/library/Adafruit BusIO
adafruit/Adafruit BusIO@1.17.4
# renovate: datasource=custom.pio depName=Adafruit Unified Sensor packageName=adafruit/library/Adafruit Unified Sensor
adafruit/Adafruit Unified Sensor@1.1.15
# renovate: datasource=custom.pio depName=Adafruit BMP280 packageName=adafruit/library/Adafruit BMP280 Library
adafruit/Adafruit BMP280 Library@3.0.0
# renovate: datasource=custom.pio depName=Adafruit BMP085 packageName=adafruit/library/Adafruit BMP085 Library
adafruit/Adafruit BMP085 Library@1.2.4
# renovate: datasource=custom.pio depName=Adafruit BME280 packageName=adafruit/library/Adafruit BME280 Library
adafruit/Adafruit BME280 Library@2.3.0
# renovate: datasource=custom.pio depName=Adafruit DPS310 packageName=adafruit/library/Adafruit DPS310
adafruit/Adafruit DPS310@1.1.6
# renovate: datasource=custom.pio depName=Adafruit MCP9808 packageName=adafruit/library/Adafruit MCP9808 Library
adafruit/Adafruit MCP9808 Library@2.0.2
# renovate: datasource=custom.pio depName=Adafruit INA260 packageName=adafruit/library/Adafruit INA260 Library
adafruit/Adafruit INA260 Library@1.5.3
# renovate: datasource=custom.pio depName=Adafruit INA219 packageName=adafruit/library/Adafruit INA219
adafruit/Adafruit INA219@1.2.3
# renovate: datasource=custom.pio depName=Adafruit MPU6050 packageName=adafruit/library/Adafruit MPU6050
adafruit/Adafruit MPU6050@2.2.9
# renovate: datasource=custom.pio depName=Adafruit LIS3DH packageName=adafruit/library/Adafruit LIS3DH
adafruit/Adafruit LIS3DH@1.3.0
# renovate: datasource=custom.pio depName=Adafruit AHTX0 packageName=adafruit/library/Adafruit AHTX0
adafruit/Adafruit AHTX0@2.0.6
# renovate: datasource=custom.pio depName=Adafruit LSM6DS packageName=adafruit/library/Adafruit LSM6DS
adafruit/Adafruit LSM6DS@4.7.4
# renovate: datasource=custom.pio depName=Adafruit TSL2591 packageName=adafruit/library/Adafruit TSL2591 Library
adafruit/Adafruit TSL2591 Library@1.4.5
# renovate: datasource=custom.pio depName=EmotiBit MLX90632 packageName=emotibit/library/EmotiBit MLX90632
emotibit/EmotiBit MLX90632@1.0.8
# renovate: datasource=custom.pio depName=Adafruit MLX90614 packageName=adafruit/library/Adafruit MLX90614 Library
adafruit/Adafruit MLX90614 Library@2.1.6
# renovate: datasource=git-refs depName=INA3221 packageName=https://github.com/sgtwilko/INA3221 gitBranch=FixOverflow
https://github.com/sgtwilko/INA3221/archive/bb03d7e9bfcc74fc798838a54f4f99738f29fc6a.zip
# renovate: datasource=github-tags depName=QMC5883L Compass packageName=mprograms/QMC5883LCompass
https://github.com/mprograms/QMC5883LCompass/archive/refs/tags/v1.2.3.zip
# renovate: datasource=github-tags depName=DFRobot_RTU packageName=dfrobot/DFRobot_RTU
https://github.com/DFRobot/DFRobot_RTU/archive/refs/tags/V1.0.6.zip
# renovate: datasource=custom.pio depName=QMC5883L Compass packageName=mprograms/library/QMC5883LCompass
mprograms/QMC5883LCompass@1.2.3
# renovate: datasource=custom.pio depName=DFRobot_RTU packageName=dfrobot/library/DFRobot_RTU
dfrobot/DFRobot_RTU@1.0.6
# renovate: datasource=git-refs depName=DFRobot_RainfallSensor packageName=https://github.com/DFRobot/DFRobot_RainfallSensor gitBranch=master
https://github.com/DFRobot/DFRobot_RainfallSensor/archive/38fea5e02b40a5430be6dab39a99a6f6347d667e.zip
# renovate: datasource=github-tags depName=INA226 packageName=robtillaart/INA226
https://github.com/RobTillaart/INA226/archive/refs/tags/0.6.6.zip
# renovate: datasource=github-tags depName=SparkFun MAX3010x packageName=sparkfun/SparkFun_MAX3010x_Sensor_Library
https://github.com/sparkfun/SparkFun_MAX3010x_Sensor_Library/archive/refs/tags/v1.1.2.zip
# renovate: datasource=github-tags depName=SparkFun 9DoF IMU Breakout ICM 20948 packageName=sparkfun/SparkFun_ICM-20948_ArduinoLibrary
https://github.com/sparkfun/SparkFun_ICM-20948_ArduinoLibrary/archive/refs/tags/v1.3.2.zip
# renovate: datasource=github-tags depName=Adafruit LTR390 Library packageName=adafruit/Adafruit_LTR390
https://github.com/adafruit/Adafruit_LTR390/archive/refs/tags/1.1.2.zip
# renovate: datasource=github-tags depName=Adafruit PCT2075 packageName=adafruit/Adafruit_PCT2075
https://github.com/adafruit/Adafruit_PCT2075/archive/refs/tags/1.0.6.zip
# renovate: datasource=github-tags depName=DFRobot_BMM150 packageName=dfrobot/DFRobot_BMM150
https://github.com/DFRobot/DFRobot_BMM150/archive/refs/tags/V1.0.0.zip
# renovate: datasource=github-tags depName=Adafruit_TSL2561 packageName=adafruit/Adafruit_TSL2561
https://github.com/adafruit/Adafruit_TSL2561/archive/refs/tags/1.1.3.zip
# renovate: datasource=github-tags depName=BH1750_WE packageName=wollewald/BH1750_WE
https://github.com/wollewald/BH1750_WE/archive/refs/tags/1.1.10.zip
# renovate: datasource=custom.pio depName=INA226 packageName=robtillaart/library/INA226
robtillaart/INA226@0.6.6
# renovate: datasource=custom.pio depName=SparkFun MAX3010x packageName=sparkfun/library/SparkFun MAX3010x Pulse and Proximity Sensor Library
sparkfun/SparkFun MAX3010x Pulse and Proximity Sensor Library@1.1.2
# renovate: datasource=custom.pio depName=SparkFun 9DoF IMU Breakout ICM 20948 packageName=sparkfun/library/SparkFun 9DoF IMU Breakout - ICM 20948 - Arduino Library
sparkfun/SparkFun 9DoF IMU Breakout - ICM 20948 - Arduino Library@1.3.2
# renovate: datasource=custom.pio depName=Adafruit LTR390 Library packageName=adafruit/library/Adafruit LTR390 Library
adafruit/Adafruit LTR390 Library@1.1.2
# renovate: datasource=custom.pio depName=Adafruit PCT2075 packageName=adafruit/library/Adafruit PCT2075
adafruit/Adafruit PCT2075@1.0.6
# renovate: datasource=custom.pio depName=DFRobot_BMM150 packageName=dfrobot/library/DFRobot_BMM150
dfrobot/DFRobot_BMM150@1.0.0
# renovate: datasource=custom.pio depName=Adafruit_TSL2561 packageName=adafruit/library/Adafruit TSL2561
adafruit/Adafruit TSL2561@1.1.3
# renovate: datasource=custom.pio depName=BH1750_WE packageName=wollewald/library/BH1750_WE
wollewald/BH1750_WE@1.1.10
; Common environmental sensor libraries (not included in native / portduino)
[environmental_extra_common]
lib_deps =
# renovate: datasource=github-tags depName=Adafruit BMP3XX packageName=adafruit/Adafruit_BMP3XX
https://github.com/adafruit/Adafruit_BMP3XX/archive/refs/tags/2.1.6.zip
# renovate: datasource=github-tags depName=Adafruit MAX1704X packageName=adafruit/Adafruit_MAX1704X
https://github.com/adafruit/Adafruit_MAX1704X/archive/refs/tags/1.0.3.zip
# renovate: datasource=github-tags depName=Adafruit SHTC3 packageName=adafruit/Adafruit_SHTC3
https://github.com/adafruit/Adafruit_SHTC3/archive/refs/tags/1.0.2.zip
# renovate: datasource=github-tags depName=Adafruit LPS2X packageName=adafruit/Adafruit_LPS2X
https://github.com/adafruit/Adafruit_LPS2X/archive/refs/tags/2.0.6.zip
# renovate: datasource=github-tags depName=Adafruit SHT31 packageName=adafruit/Adafruit_SHT31
https://github.com/adafruit/Adafruit_SHT31/archive/refs/tags/2.2.2.zip
# renovate: datasource=github-tags depName=Adafruit VEML7700 packageName=adafruit/Adafruit_VEML7700
https://github.com/adafruit/Adafruit_VEML7700/archive/refs/tags/2.1.6.zip
# renovate: datasource=github-tags depName=Adafruit SHT4x packageName=adafruit/Adafruit_SHT4X
https://github.com/adafruit/Adafruit_SHT4X/archive/refs/tags/1.0.5.zip
# renovate: datasource=github-tags depName=SparkFun Qwiic Scale NAU7802 packageName=sparkfun/SparkFun_Qwiic_Scale_NAU7802_Arduino_Library
https://github.com/sparkfun/SparkFun_Qwiic_Scale_NAU7802_Arduino_Library/archive/refs/tags/v1.0.6.zip
# renovate: datasource=custom.pio depName=Adafruit BMP3XX packageName=adafruit/library/Adafruit BMP3XX Library
adafruit/Adafruit BMP3XX Library@2.1.6
# renovate: datasource=custom.pio depName=Adafruit MAX1704X packageName=adafruit/library/Adafruit MAX1704X
adafruit/Adafruit MAX1704X@1.0.3
# renovate: datasource=custom.pio depName=Adafruit SHTC3 packageName=adafruit/library/Adafruit SHTC3 Library
adafruit/Adafruit SHTC3 Library@1.0.2
# renovate: datasource=custom.pio depName=Adafruit LPS2X packageName=adafruit/library/Adafruit LPS2X
adafruit/Adafruit LPS2X@2.0.6
# renovate: datasource=custom.pio depName=Adafruit SHT31 packageName=adafruit/library/Adafruit SHT31 Library
adafruit/Adafruit SHT31 Library@2.2.2
# renovate: datasource=custom.pio depName=Adafruit VEML7700 packageName=adafruit/library/Adafruit VEML7700 Library
adafruit/Adafruit VEML7700 Library@2.1.6
# renovate: datasource=custom.pio depName=Adafruit SHT4x packageName=adafruit/library/Adafruit SHT4x Library
adafruit/Adafruit SHT4x Library@1.0.5
# renovate: datasource=custom.pio depName=SparkFun Qwiic Scale NAU7802 packageName=sparkfun/library/SparkFun Qwiic Scale NAU7802 Arduino Library
sparkfun/SparkFun Qwiic Scale NAU7802 Arduino Library@1.0.6
# renovate: datasource=custom.pio depName=ClosedCube OPT3001 packageName=closedcube/library/ClosedCube OPT3001
closedcube/ClosedCube OPT3001@1.1.2
# renovate: datasource=git-refs depName=meshtastic-DFRobot_LarkWeatherStation packageName=https://github.com/meshtastic/DFRobot_LarkWeatherStation gitBranch=master
https://github.com/meshtastic/DFRobot_LarkWeatherStation/archive/4de3a9cadef0f6a5220a8a906cf9775b02b0040d.zip
# renovate: datasource=github-tags depName=Sensirion Core packageName=sensirion/arduino-core
https://github.com/Sensirion/arduino-core/archive/refs/tags/0.7.3.zip
# renovate: datasource=github-tags depName=Sensirion I2C SCD4x packageName=sensirion/arduino-i2c-scd4x
https://github.com/Sensirion/arduino-i2c-scd4x/archive/refs/tags/1.1.0.zip
# renovate: datasource=github-tags depName=Sensirion I2C SFA3x packageName=sensirion/arduino-i2c-sfa3x
https://github.com/Sensirion/arduino-i2c-sfa3x/archive/refs/tags/1.0.0.zip
# renovate: datasource=github-tags depName=Sensirion I2C SCD30 packageName=sensirion/arduino-i2c-scd30
https://github.com/Sensirion/arduino-i2c-scd30/archive/refs/tags/1.0.0.zip
# renovate: datasource=custom.pio depName=Sensirion Core packageName=sensirion/library/Sensirion Core
sensirion/Sensirion Core@0.7.3
# renovate: datasource=custom.pio depName=Sensirion I2C SCD4x packageName=sensirion/library/Sensirion I2C SCD4x
sensirion/Sensirion I2C SCD4x@1.1.0
# renovate: datasource=custom.pio depName=Sensirion I2C SFA3x packageName=sensirion/library/Sensirion I2C SFA3x
sensirion/Sensirion I2C SFA3x@1.0.0
# renovate: datasource=custom.pio depName=Sensirion I2C SCD30 packageName=sensirion/library/Sensirion I2C SCD30
sensirion/Sensirion I2C SCD30@1.0.0
; Environmental sensors with BSEC2 (Bosch proprietary IAQ)
[environmental_extra]
lib_deps =
${environmental_extra_common.lib_deps}
# renovate: datasource=github-tags depName=Bosch BSEC2 packageName=boschsensortec/Bosch-BSEC2-Library
https://github.com/boschsensortec/Bosch-BSEC2-Library/archive/refs/tags/1.10.2610.zip
# renovate: datasource=github-tags depName=Bosch BME68x packageName=boschsensortec/Bosch-BME68x-Library
https://github.com/boschsensortec/Bosch-BME68x-Library/archive/refs/tags/v1.3.40408.zip
# renovate: datasource=custom.pio depName=Bosch BSEC2 packageName=boschsensortec/library/bsec2
boschsensortec/bsec2@1.10.2610
# renovate: datasource=custom.pio depName=Bosch BME68x packageName=boschsensortec/library/BME68x Sensor Library
boschsensortec/BME68x Sensor Library@1.3.40408
; Environmental sensors without BSEC (saves ~3.5KB DRAM for original ESP32 targets)
[environmental_extra_no_bsec]
lib_deps =
${environmental_extra_common.lib_deps}
# renovate: datasource=github-tags depName=Adafruit_BME680 packageName=adafruit/Adafruit_BME680
https://github.com/adafruit/Adafruit_BME680/archive/refs/tags/2.0.6.zip
# renovate: datasource=custom.pio depName=adafruit/Adafruit BME680 Library packageName=adafruit/library/Adafruit BME680
adafruit/Adafruit BME680 Library@^2.0.5
-2
View File
@@ -4,8 +4,6 @@
":dependencyDashboard",
":semanticCommitTypeAll(chore)",
":ignoreModulesAndTests",
":noUnscheduledUpdates",
"schedule:daily",
"group:recommended",
"replacements:all",
"workarounds:all"
+2 -10
View File
@@ -98,6 +98,7 @@ Syslog &Syslog::logMask(uint8_t priMask)
void Syslog::enable()
{
this->_client->begin(this->_port);
this->_enabled = true;
}
@@ -165,21 +166,14 @@ inline bool Syslog::_sendLog(uint16_t pri, const char *appName, const char *mess
if ((pri & LOG_FACMASK) == 0)
pri = LOG_MAKEPRI(LOG_FAC(this->_priDefault), pri);
// W5100S: acquire UDP socket on-demand to avoid permanent socket consumption
if (!this->_client->begin(this->_port)) {
return false;
}
if (this->_server != NULL) {
result = this->_client->beginPacket(this->_server, this->_port);
} else {
result = this->_client->beginPacket(this->_ip, this->_port);
}
if (result != 1) {
this->_client->stop();
if (result != 1)
return false;
}
this->_client->print('<');
this->_client->print(pri);
@@ -199,8 +193,6 @@ inline bool Syslog::_sendLog(uint16_t pri, const char *appName, const char *mess
this->_client->print(message);
this->_client->endPacket();
this->_client->stop(); // W5100S: release UDP socket for other services
return true;
}
-7
View File
@@ -21,15 +21,8 @@
// How many messages are stored (RAM + flash).
// Define -DMESSAGE_HISTORY_LIMIT=N in build_flags to control memory usage.
#ifndef MESSAGE_HISTORY_LIMIT
#if defined(ARCH_ESP32) && \
!(defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32S2))
// Baseline ESP32 (non-PSRAM variants) has limited heap; reduce message history on resource-constrained builds.
// Override with -DMESSAGE_HISTORY_LIMIT=N if needed.
#define MESSAGE_HISTORY_LIMIT 10
#else
#define MESSAGE_HISTORY_LIMIT 20
#endif
#endif
// Internal alias used everywhere in code – do NOT redefine elsewhere.
#define MAX_MESSAGES_SAVED MESSAGE_HISTORY_LIMIT
-8
View File
@@ -35,11 +35,6 @@
#include "nrfx_power.h"
#endif
#if defined(ARCH_NRF52)
#include "Nrf52SaadcLock.h"
#include "concurrency/LockGuard.h"
#endif
#if defined(DEBUG_HEAP_MQTT) && !MESHTASTIC_EXCLUDE_MQTT
#include "mqtt/MQTT.h"
#include "target_specific.h"
@@ -333,9 +328,6 @@ class AnalogBatteryLevel : public HasBatteryLevel
scaled = esp_adc_cal_raw_to_voltage(raw, adc_characs);
scaled *= operativeAdcMultiplier;
#else // block for all other platforms
#ifdef ARCH_NRF52
concurrency::LockGuard saadcGuard(concurrency::nrf52SaadcLock);
#endif
for (uint32_t i = 0; i < BATTERY_SENSE_SAMPLES; i++) {
raw += analogRead(BATTERY_PIN);
}
+1 -1
View File
@@ -345,7 +345,7 @@ void RedirectablePrint::log(const char *logLevel, const char *format, ...)
return;
}
void RedirectablePrint::hexDump(const char *logLevel, const unsigned char *buf, uint16_t len)
void RedirectablePrint::hexDump(const char *logLevel, unsigned char *buf, uint16_t len)
{
const char alphabet[17] = "0123456789abcdef";
log(logLevel, " +------------------------------------------------+ +----------------+");
+1 -1
View File
@@ -44,7 +44,7 @@ class RedirectablePrint : public Print
/** like printf but va_list based */
size_t vprintf(const char *logLevel, const char *format, va_list arg);
void hexDump(const char *logLevel, const unsigned char *buf, uint16_t len);
void hexDump(const char *logLevel, unsigned char *buf, uint16_t len);
std::string mt_sprintf(const std::string fmt_str, ...);
+1 -56
View File
@@ -6,11 +6,6 @@
#include "Tone.h"
#endif
#if defined(HAS_I2S)
#include "main.h"
#include <unordered_map>
#endif
#if !defined(ARCH_PORTDUINO)
extern "C" void delay(uint32_t dwMs);
#endif
@@ -55,50 +50,6 @@ const int DURATION_1_2 = 500; // 1/2 note
const int DURATION_3_4 = 750; // 3/4 note
const int DURATION_1_1 = 1000; // 1/1 note
#ifdef HAS_I2S
void playTonesRTTTL(const ToneDuration *tone_durations, int size)
{
// translate ToneDuration[] to RTTTL string and play using audioThread
static std::unordered_map<int, std::string> freqToNote = {
{NOTE_C3, "c4"}, {NOTE_CS3, "c#4"}, {NOTE_D3, "d4"}, {NOTE_DS3, "d#4"}, {NOTE_E3, "e4"}, {NOTE_F3, "f4"},
{NOTE_FS3, "f#4"}, {NOTE_G3, "g4"}, {NOTE_GS3, "g#4"}, {NOTE_A3, "a4"}, {NOTE_AS3, "a#4"}, {NOTE_B3, "b4"},
{NOTE_C4, "c5"}, {NOTE_E4, "e5"}, {NOTE_G4, "g5"}, {NOTE_A4, "a5"}, {NOTE_C5, "c6"}, {NOTE_E5, "e6"},
{NOTE_G5, "g6"}, {NOTE_F5, "f6"}, {NOTE_G6, "g7"}, {NOTE_E7, "e8"}};
char rtttl[128] = "tone:d=32,o=4,b=200:"; // default duration and octave
for (int i = 0; i < size; i++) {
const auto &td = tone_durations[i];
std::string note = "b4";
if (freqToNote.find(td.frequency_khz) != freqToNote.end()) {
note = freqToNote[td.frequency_khz];
}
int dur = 32; // default duration
if (td.duration_ms >= 1000)
dur = 1;
else if (td.duration_ms >= 500)
dur = 2;
else if (td.duration_ms >= 250)
dur = 4;
else if (td.duration_ms >= 125)
dur = 8;
else if (td.duration_ms >= 62)
dur = 16;
else
dur = 32;
char noteStr[64];
snprintf(noteStr, sizeof(noteStr), "%s,%d", note.c_str(), dur);
strncat(rtttl, noteStr, sizeof(rtttl) - strlen(rtttl) - 1);
audioThread->beginRttl(rtttl, strlen(rtttl));
while (audioThread->isPlaying()) {
delay(10);
}
return;
}
}
#endif
void playTones(const ToneDuration *tone_durations, int size)
{
if (config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_DISABLED ||
@@ -106,13 +57,7 @@ void playTones(const ToneDuration *tone_durations, int size)
// Buzzer is disabled or not set to system tones
return;
}
#ifdef HAS_I2S
if (moduleConfig.external_notification.use_i2s_as_buzzer && audioThread) {
playTonesRTTTL(tone_durations, size);
return;
}
#endif
#if defined(PIN_BUZZER)
#ifdef PIN_BUZZER
if (!config.device.buzzer_gpio)
config.device.buzzer_gpio = PIN_BUZZER;
#endif
+1 -77
View File
@@ -1,85 +1,10 @@
#include "concurrency/BinarySemaphorePosix.h"
#include "configuration.h"
#include <errno.h>
#include <sys/time.h>
#ifndef HAS_FREE_RTOS
namespace concurrency
{
#ifdef ARCH_PORTDUINO
BinarySemaphorePosix::BinarySemaphorePosix()
{
if (pthread_mutex_init(&mutex, NULL) != 0) {
throw std::runtime_error("pthread_mutex_init failed");
}
if (pthread_cond_init(&cond, NULL) != 0) {
pthread_mutex_destroy(&mutex);
throw std::runtime_error("pthread_cond_init failed");
}
signaled = false;
}
BinarySemaphorePosix::~BinarySemaphorePosix()
{
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
}
/**
* Returns false if we timed out
*/
bool BinarySemaphorePosix::take(uint32_t msec)
{
pthread_mutex_lock(&mutex);
if (!signaled) {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += msec / 1000;
ts.tv_nsec += (msec % 1000) * 1000000L;
if (ts.tv_nsec >= 1000000000L) {
ts.tv_sec += 1;
ts.tv_nsec -= 1000000000L;
}
while (!signaled) {
int rc = pthread_cond_timedwait(&cond, &mutex, &ts);
if (rc == ETIMEDOUT)
break;
if (rc != 0) {
// Some other error occurred
pthread_mutex_unlock(&mutex);
throw std::runtime_error("pthread_cond_timedwait failed: " + std::to_string(rc));
}
}
}
bool wasSignaled = signaled;
signaled = false; // consume the signal (binary semaphore)
pthread_mutex_unlock(&mutex);
return wasSignaled;
}
void BinarySemaphorePosix::give()
{
pthread_mutex_lock(&mutex);
signaled = true;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
IRAM_ATTR void BinarySemaphorePosix::giveFromISR(BaseType_t *pxHigherPriorityTaskWoken)
{
give();
if (pxHigherPriorityTaskWoken)
*pxHigherPriorityTaskWoken = true;
}
#else
BinarySemaphorePosix::BinarySemaphorePosix() {}
@@ -97,8 +22,7 @@ bool BinarySemaphorePosix::take(uint32_t msec)
void BinarySemaphorePosix::give() {}
IRAM_ATTR void BinarySemaphorePosix::giveFromISR(BaseType_t *pxHigherPriorityTaskWoken) {}
#endif
} // namespace concurrency
#endif
#endif
+2 -11
View File
@@ -2,10 +2,6 @@
#include "../freertosinc.h"
#ifdef ARCH_PORTDUINO
#include <pthread.h>
#endif
namespace concurrency
{
@@ -13,12 +9,7 @@ namespace concurrency
class BinarySemaphorePosix
{
#ifdef ARCH_PORTDUINO
pthread_mutex_t mutex;
pthread_cond_t cond;
bool signaled;
#endif
// SemaphoreHandle_t semaphore;
public:
BinarySemaphorePosix();
@@ -36,4 +27,4 @@ class BinarySemaphorePosix
#endif
} // namespace concurrency
} // namespace concurrency
-11
View File
@@ -151,19 +151,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifdef USE_KCT8103L_PA
// Power Amps are often non-linear, so we can use an array of values for the power curve
#if defined(HELTEC_WIRELESS_TRACKER_V2)
#define NUM_PA_POINTS 22
#define TX_GAIN_LORA 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 13, 13, 13, 12, 12, 11, 10, 9, 8, 7
#elif defined(HELTEC_MESH_NODE_T096)
#define NUM_PA_POINTS 22
#define TX_GAIN_LORA 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 13, 13, 13, 12, 11, 10, 9, 8, 7
#else
// If a board enables USE_KCT8103L_PA but does not match a known variant and has
// not already provided a PA curve, fail at compile time to avoid unsafe defaults.
#if !defined(NUM_PA_POINTS) || !defined(TX_GAIN_LORA)
#error "USE_KCT8103L_PA is defined, but no PA gain curve (NUM_PA_POINTS / TX_GAIN_LORA) is configured for this board."
#endif
#endif
#endif
#ifdef RAK13302
+1 -3
View File
@@ -341,9 +341,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
type = DPS310;
break;
}
if (type == DPS310) {
break;
}
break;
default:
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1); // GET_ID
switch (registerValue) {
-28
View File
@@ -2,7 +2,6 @@
#include "configuration.h"
#include "detect/ScanI2C.h"
#include "main.h"
#include "modules/NodeInfoModule.h"
#include <Throttle.h>
#include <sys/time.h>
#include <time.h>
@@ -13,14 +12,6 @@ uint32_t lastSetFromPhoneNtpOrGps = 0;
static uint32_t lastTimeValidationWarning = 0;
static const uint32_t TIME_VALIDATION_WARNING_INTERVAL_MS = 15000; // 15 seconds
static void triggerNodeInfoCheckOnTimeSource(RTCQuality oldQuality, RTCQuality newQuality)
{
if (oldQuality == RTCQualityNone && newQuality > RTCQualityNone && nodeInfoModule) {
LOG_DEBUG("Time source acquired (%s -> %s), triggering NodeInfo recheck", RtcName(oldQuality), RtcName(newQuality));
nodeInfoModule->triggerImmediateNodeInfoCheck();
}
}
RTCQuality getRTCQuality()
{
return currentQuality;
@@ -70,11 +61,9 @@ RTCSetResult readFromRTC()
LOG_DEBUG("Read RTC time from RV3028 getTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", t.tm_year + 1900, t.tm_mon + 1,
t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch);
if (currentQuality == RTCQualityNone) {
RTCQuality oldQuality = currentQuality;
timeStartMsec = now;
zeroOffsetSecs = tv.tv_sec;
currentQuality = RTCQualityDevice;
triggerNodeInfoCheckOnTimeSource(oldQuality, currentQuality);
}
return RTCSetResultSuccess;
} else {
@@ -116,11 +105,9 @@ RTCSetResult readFromRTC()
LOG_DEBUG("Read RTC time from %s getDateTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", rtc.getChipName(), t.tm_year + 1900,
t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch);
if (currentQuality == RTCQualityNone) {
RTCQuality oldQuality = currentQuality;
timeStartMsec = now;
zeroOffsetSecs = tv.tv_sec;
currentQuality = RTCQualityDevice;
triggerNodeInfoCheckOnTimeSource(oldQuality, currentQuality);
}
return RTCSetResultSuccess;
} else {
@@ -152,11 +139,9 @@ RTCSetResult readFromRTC()
}
#endif
if (currentQuality == RTCQualityNone) {
RTCQuality oldQuality = currentQuality;
timeStartMsec = now;
zeroOffsetSecs = tv.tv_sec;
currentQuality = RTCQualityDevice;
triggerNodeInfoCheckOnTimeSource(oldQuality, currentQuality);
}
return RTCSetResultSuccess;
}
@@ -229,7 +214,6 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd
}
if (shouldSet) {
RTCQuality oldQuality = currentQuality;
currentQuality = q;
lastSetMsec = now;
if (currentQuality >= RTCQualityNTP) {
@@ -297,7 +281,6 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd
#endif
readFromRTC();
triggerNodeInfoCheckOnTimeSource(oldQuality, currentQuality);
return RTCSetResultSuccess;
} else {
return RTCSetResultNotSet; // RTC was already set with a higher quality time
@@ -414,17 +397,6 @@ uint32_t getValidTime(RTCQuality minQuality, bool local)
return (currentQuality >= minQuality) ? getTime(local) : 0;
}
#ifdef PIO_UNIT_TESTING
void setBootRelativeTimeForUnitTest(uint32_t secondsSinceBoot)
{
currentQuality = RTCQualityNone;
zeroOffsetSecs = 0;
timeStartMsec = millis() - (secondsSinceBoot * 1000);
lastSetFromPhoneNtpOrGps = 0;
lastTimeValidationWarning = 0;
}
#endif
time_t gm_mktime(const struct tm *tm)
{
#if !MESHTASTIC_EXCLUDE_TZ
-4
View File
@@ -54,10 +54,6 @@ uint32_t getValidTime(RTCQuality minQuality, bool local = false);
RTCSetResult readFromRTC();
#ifdef PIO_UNIT_TESTING
void setBootRelativeTimeForUnitTest(uint32_t secondsSinceBoot);
#endif
time_t gm_mktime(const struct tm *tm);
#define SEC_PER_DAY 86400
+13 -11
View File
@@ -1,6 +1,6 @@
#include "configuration.h"
#if defined(USE_EINK) && !defined(USE_EINK_PARALLELDISPLAY)
#ifdef USE_EINK
#include "EInkDisplay2.h"
#include "SPILock.h"
#include "main.h"
@@ -143,10 +143,6 @@ bool EInkDisplay::connect()
#ifdef ELECROW_ThinkNode_M1
// ThinkNode M1 has a hardware dimmable backlight. Start enabled
digitalWrite(PIN_EINK_EN, HIGH);
#elif defined(MINI_EPAPER_S3)
// T-Mini Epaper S3 requires panel power rail enabled before SPI transfer.
digitalWrite(PIN_EINK_EN, HIGH);
delay(10);
#else
digitalWrite(PIN_EINK_EN, LOW);
#endif
@@ -206,8 +202,7 @@ bool EInkDisplay::connect()
}
#elif defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_VISION_MASTER_E290) || defined(TLORA_T3S3_EPAPER) || \
defined(CROWPANEL_ESP32S3_5_EPAPER) || defined(CROWPANEL_ESP32S3_4_EPAPER) || defined(CROWPANEL_ESP32S3_2_EPAPER) || \
defined(MINI_EPAPER_S3)
defined(CROWPANEL_ESP32S3_5_EPAPER) || defined(CROWPANEL_ESP32S3_4_EPAPER) || defined(CROWPANEL_ESP32S3_2_EPAPER)
{
// Start HSPI
hspi = new SPIClass(HSPI);
@@ -221,13 +216,9 @@ bool EInkDisplay::connect()
// Init GxEPD2
adafruitDisplay->init();
#if defined(MINI_EPAPER_S3)
adafruitDisplay->setRotation(3);
#else
adafruitDisplay->setRotation(3);
#if defined(CROWPANEL_ESP32S3_5_EPAPER) || defined(CROWPANEL_ESP32S3_4_EPAPER)
adafruitDisplay->setRotation(0);
#endif
#endif
}
#elif defined(PCA10059) || defined(ME25LS01)
@@ -268,6 +259,17 @@ bool EInkDisplay::connect()
adafruitDisplay->setRotation(3);
adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT);
}
#elif defined(MINI_EPAPER_S3)
spi1 = new SPIClass(HSPI);
spi1->begin(PIN_SPI1_SCK, PIN_SPI1_MISO, PIN_SPI1_MOSI, PIN_EINK_CS);
// Create GxEPD2 objects
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, *spi1);
adafruitDisplay = new GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT>(*lowLevel);
// Init GxEPD2
adafruitDisplay->init();
adafruitDisplay->setRotation(1);
#elif defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_VISION_MASTER_E213)
// Detect display model, before starting SPI

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