Compare commits

..

8 Commits

Author SHA1 Message Date
Jonathan Bennett
c1aeb1f803 minor changes to bme680 changes 2026-01-05 19:20:11 -06:00
JaeHwan Jin
95ec869d67 chore: trigger CI 2025-11-24 10:46:28 +09:00
JaeHwan Jin
1ac74cefc4 Fix for esp32 CI 2025-11-18 18:58:50 +09:00
JaeHwan Jin
7b6235f4d7 Fix for esp32 CI 2025-11-17 21:19:49 +09:00
JaeHwan Jin
60366f3aff Fix bosch library dependencies 2025-11-14 17:57:33 +09:00
JaeHwan Jin
09303110ff Back bsec2 to environmental_extra 2025-11-14 17:04:17 +09:00
JaeHwan Jin
d88640a134 Modify limited to Portduino and RAK4631 only 2025-11-14 16:41:05 +09:00
JaeHwan Jin
52eb9c9f82 Add BME680 and SHTC3 support to meshtasticd and RAK4631 2025-11-12 17:37:10 +09:00
118 changed files with 698 additions and 2557 deletions

View File

@@ -5,7 +5,7 @@ runs:
using: composite
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
submodules: recursive
ref: ${{github.event.pull_request.head.ref}}

View File

@@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
submodules: recursive
path: meshtasticd

View File

@@ -22,7 +22,7 @@ jobs:
outputs:
artifact-id: ${{ steps.upload.outputs.artifact-id }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
with:
submodules: recursive
ref: ${{github.event.pull_request.head.ref}}

176
.github/workflows/build_one_arch.yml vendored Normal file
View File

@@ -0,0 +1,176 @@
name: Build One Arch
on:
workflow_dispatch:
inputs:
# trunk-ignore(checkov/CKV_GHA_7)
arch:
type: choice
options:
- esp32
- esp32s3
- esp32c3
- esp32c6
- nrf52840
- rp2040
- rp2350
- stm32
- native
permissions: read-all
env:
INPUT_ARCH: ${{ github.event.inputs.arch }}
jobs:
setup:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
with:
python-version: 3.x
cache: pip
- run: pip install -U platformio
- name: Generate matrix
id: jsonStep
run: |
TARGETS=$(./bin/generate_ci_matrix.py $INPUT_ARCH --level extra)
echo "Name: $GITHUB_REF_NAME Base: $GITHUB_BASE_REF Ref: $GITHUB_REF"
echo "selected_arch=$TARGETS" >> $GITHUB_OUTPUT
outputs:
selected_arch: ${{ steps.jsonStep.outputs.selected_arch }}
version:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- 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 }}
build:
if: ${{ github.event_name != 'workflow_dispatch' }}
needs: [setup, version]
strategy:
fail-fast: false
matrix:
build: ${{ fromJson(needs.setup.outputs.selected_arch) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.build.board }}
platform: ${{ matrix.build.arch }}
build-debian-src:
if: ${{ github.repository == 'meshtastic/firmware' && github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
uses: ./.github/workflows/build_debian_src.yml
with:
series: UNRELEASED
build_location: local
secrets: inherit
package-pio-deps-native-tft:
if: ${{ inputs.arch == 'native' }}
uses: ./.github/workflows/package_pio_deps.yml
with:
pio_env: native-tft
secrets: inherit
test-native:
if: ${{ !contains(github.ref_name, 'event/') && github.event_name != 'workflow_dispatch' || !contains(github.ref_name, 'event/') && inputs.arch == 'native' }}
uses: ./.github/workflows/test_native.yml
gather-artifacts:
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@v5
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- uses: actions/download-artifact@v6
with:
path: ./
pattern: firmware-${{inputs.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@v5
with:
name: firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}
overwrite: true
path: |
./firmware-*.bin
./firmware-*.uf2
./firmware-*.hex
./firmware-*-ota.zip
./device-*.sh
./device-*.bat
./littlefs-*.bin
./bleota*bin
./Meshtastic_nRF52_factory_erase*.uf2
retention-days: 30
- uses: actions/download-artifact@v6
with:
name: firmware-${{inputs.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
chmod +x ./output/device-update.sh
- name: Zip firmware
run: zip -j -9 -r ./firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip ./output
- name: Repackage in single elfs zip
uses: actions/upload-artifact@v5
with:
name: debug-elfs-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip
overwrite: true
path: ./*.elf
retention-days: 30
- uses: scruplelesswizard/comment-artifact@main
if: ${{ github.event_name == 'pull_request' }}
with:
name: firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}
description: "Download firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip. This artifact will be available for 90 days from creation"
github-token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -15,6 +15,7 @@ on:
- rp2040
- rp2350
- stm32
- native
target:
type: string
required: false
@@ -41,9 +42,10 @@ jobs:
- rp2040
- rp2350
- stm32
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
with:
python-version: 3.x
@@ -58,13 +60,13 @@ jobs:
echo "Arch: ${{matrix.arch}}" >> $GITHUB_STEP_SUMMARY
echo "Ref: $GITHUB_REF" >> $GITHUB_STEP_SUMMARY
echo "Targets:" >> $GITHUB_STEP_SUMMARY
echo $TARGETS | jq -r 'sort_by(.board) |.[] | "- " + .board' >> $GITHUB_STEP_SUMMARY
echo $TARGETS >> $GITHUB_STEP_SUMMARY
version:
if: ${{ inputs.target != '' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
- name: Get release version string
run: |
echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
@@ -85,6 +87,25 @@ jobs:
pio_env: ${{ inputs.target }}
platform: ${{ inputs.arch }}
build-debian-src:
if: ${{ github.repository == 'meshtastic/firmware' && inputs.arch == 'native' }}
uses: ./.github/workflows/build_debian_src.yml
with:
series: UNRELEASED
build_location: local
secrets: inherit
package-pio-deps-native-tft:
if: ${{ inputs.arch == 'native' }}
uses: ./.github/workflows/package_pio_deps.yml
with:
pio_env: native-tft
secrets: inherit
test-native:
if: ${{ !contains(github.ref_name, 'event/') && github.event_name != 'workflow_dispatch' || !contains(github.ref_name, 'event/') && inputs.arch == 'native' && inputs.target != '' }}
uses: ./.github/workflows/test_native.yml
gather-artifacts:
permissions:
contents: write
@@ -93,7 +114,7 @@ jobs:
needs: [version, build]
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}

View File

@@ -47,7 +47,7 @@ jobs:
runs-on: ${{ inputs.runs-on }}
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
submodules: recursive
ref: ${{github.event.pull_request.head.ref}}

View File

@@ -83,7 +83,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
submodules: recursive
ref: ${{github.event.pull_request.head.ref}}

View File

@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
submodules: recursive
ref: ${{ github.ref }}

View File

@@ -35,7 +35,7 @@ jobs:
- check
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
with:
python-version: 3.x
@@ -59,7 +59,7 @@ jobs:
version:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
- name: Get release version string
run: |
echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
@@ -81,7 +81,7 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.event_name != 'workflow_dispatch' && github.repository == 'meshtastic/firmware' }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
- name: Build base
id: base
uses: ./.github/actions/setup-base
@@ -163,7 +163,7 @@ jobs:
needs: [version, build]
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
@@ -242,7 +242,7 @@ jobs:
- package-pio-deps-native-tft
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v6
@@ -311,7 +311,7 @@ jobs:
needs: [release-artifacts, version]
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v6
@@ -366,7 +366,7 @@ jobs:
esp32,esp32s3,esp32c3,esp32c6,nrf52840,rp2040,rp2350,stm32
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v6

View File

@@ -17,7 +17,7 @@ jobs:
- check
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
with:
python-version: 3.x
@@ -40,7 +40,7 @@ jobs:
version:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
- name: Get release version string
run: |
echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
@@ -62,7 +62,7 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.event_name != 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
- name: Build base
id: base
uses: ./.github/actions/setup-base
@@ -142,7 +142,7 @@ jobs:
needs: [version, build]
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
@@ -221,7 +221,7 @@ jobs:
- package-pio-deps-native-tft
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v6
@@ -290,7 +290,7 @@ jobs:
needs: [release-artifacts, version]
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v6
@@ -345,7 +345,7 @@ jobs:
esp32,esp32s3,esp32c3,esp32c6,nrf52840,rp2040,rp2350,stm32
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v6

View File

@@ -14,7 +14,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Trunk Check
uses: trunk-io/trunk-action@v1
@@ -31,7 +31,7 @@ jobs:
pull-requests: write # For trunk to create PRs
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Trunk Upgrade
uses: trunk-io/trunk-action/upgrade@v1

View File

@@ -34,7 +34,7 @@ jobs:
needs: build-debian-src
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
submodules: recursive
path: meshtasticd

View File

@@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
submodules: recursive
ref: ${{github.event.pull_request.head.ref}}

View File

@@ -32,7 +32,7 @@ jobs:
needs: build-debian-src
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
submodules: recursive
path: meshtasticd

View File

@@ -40,7 +40,7 @@ jobs:
checks: write
pull-requests: write
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
with:
submodules: recursive

View File

@@ -60,10 +60,7 @@ jobs:
shell: bash
steps:
- name: Checkout
uses: actions/checkout@v6
with:
# Always use master branch for version bumps
ref: master
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v6

View File

@@ -21,7 +21,7 @@ jobs:
steps:
# step 1
- name: clone application source code
uses: actions/checkout@v6
uses: actions/checkout@v5
# step 2
- name: full scan

View File

@@ -13,7 +13,7 @@ jobs:
steps:
# step 1
- name: clone application source code
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
fetch-depth: 0

View File

@@ -14,7 +14,7 @@ jobs:
name: Native Simulator Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
@@ -70,7 +70,7 @@ jobs:
name: Native PlatformIO Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
@@ -127,7 +127,7 @@ jobs:
- platformio-tests
if: always()
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
@@ -143,7 +143,7 @@ jobs:
merge-multiple: true
- name: Test Report
uses: dorny/test-reporter@v2.2.0
uses: dorny/test-reporter@v2.1.1
with:
name: PlatformIO Tests
path: testreport.xml

View File

@@ -20,7 +20,7 @@ jobs:
runs-on: test-runner
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v5
# - uses: actions/setup-python@v5
# with:

View File

@@ -18,7 +18,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Trunk Check
uses: trunk-io/trunk-action@v1

View File

@@ -16,7 +16,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Trunk Check
uses: trunk-io/trunk-action@v1

View File

@@ -15,7 +15,7 @@ jobs:
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}

View File

@@ -11,7 +11,7 @@ jobs:
pull-requests: write
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
submodules: true

View File

@@ -4,31 +4,31 @@ cli:
plugins:
sources:
- id: trunk
ref: v1.7.4
ref: v1.7.3
uri: https://github.com/trunk-io/plugins
lint:
enabled:
- checkov@3.2.495
- renovate@42.24.1
- checkov@3.2.489
- renovate@41.169.1
- prettier@3.6.2
- trufflehog@3.91.1
- trufflehog@3.90.12
- yamllint@1.37.1
- bandit@1.9.2
- bandit@1.8.6
- trivy@0.67.2
- taplo@0.10.0
- ruff@0.14.6
- ruff@0.14.3
- isort@7.0.0
- markdownlint@0.46.0
- markdownlint@0.45.0
- oxipng@9.1.5
- svgo@4.0.0
- actionlint@1.7.9
- actionlint@1.7.8
- flake8@7.3.0
- hadolint@2.14.0
- shfmt@3.6.0
- shellcheck@0.11.0
- black@25.11.0
- black@25.9.0
- git-diff-check
- gitleaks@8.30.0
- gitleaks@8.28.0
- clang-format@16.0.3
ignore:
- linters: [ALL]

View File

@@ -37,3 +37,4 @@ Join our community and help improve Meshtastic! 🚀
## Stats
![Alt](https://repobeats.axiom.co/api/embed/8025e56c482ec63541593cc5bd322c19d5c0bdcf.svg "Repobeats analytics image")

View File

@@ -7,7 +7,7 @@ extends = arduino_base
platform_packages =
; our custom Git version until they merge our PR
# TODO renovate
platformio/framework-arduinoadafruitnrf52 @ https://github.com/meshtastic/Adafruit_nRF52_Arduino#c770c8a16a351b55b86e347a3d9d7b74ad0bbf39
platformio/framework-arduinoadafruitnrf52 @ https://github.com/meshtastic/Adafruit_nRF52_Arduino#e13f5820002a4fb2a5e6754b42ace185277e5adf
; Don't renovate toolchain-gccarmnoneeabi
platformio/toolchain-gccarmnoneeabi@~1.90301.0

View File

@@ -8,7 +8,7 @@ lib_deps =
${environmental_base.lib_deps}
${environmental_extra.lib_deps}
# renovate: datasource=git-refs depName=Kongduino-Adafruit_nRFCrypto packageName=https://github.com/Kongduino/Adafruit_nRFCrypto gitBranch=master
https://github.com/Kongduino/Adafruit_nRFCrypto/archive/8cde7189b5ead9dcd49f72601b43b969c0bbc06e.zip
https://github.com/Kongduino/Adafruit_nRFCrypto/archive/5f838d2709461a2c981f642917aa50254a25c14c.zip
; Common NRF52 debugging settings follow. See the Meshtastic developer docs for how to connect SWD debugging probes to your board.

View File

@@ -33,6 +33,8 @@ lib_deps =
adafruit/Adafruit seesaw Library@1.7.9
# renovate: datasource=git-refs depName=RAK12034-BMX160 packageName=https://github.com/RAKWireless/RAK12034-BMX160 gitBranch=main
https://github.com/RAKWireless/RAK12034-BMX160/archive/dcead07ffa267d3c906e9ca4a1330ab989e957e2.zip
# renovate: datasource=custom.pio depName=adafruit/Adafruit BME680 Library packageName=adafruit/library/Adafruit BME680
adafruit/Adafruit BME680 Library@^2.0.5
build_flags =
${arduino_base.build_flags}

View File

@@ -2,7 +2,7 @@
extends = arduino_base
platform =
# renovate: datasource=custom.pio depName=platformio/ststm32 packageName=platformio/platform/ststm32
platformio/ststm32@19.4.0
platformio/ststm32@19.3.0
platform_packages =
# TODO renovate
platformio/framework-arduinoststm32@https://github.com/stm32duino/Arduino_Core_STM32/archive/2.10.1.zip

View File

@@ -87,12 +87,6 @@
</screenshots>
<releases>
<release version="2.7.16" date="2025-11-19">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.16</url>
</release>
<release version="2.7.15" date="2025-11-13">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.15</url>
</release>
<release version="2.7.14" date="2025-11-03">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.14</url>
</release>

View File

@@ -1,53 +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"]
],
"usb_product": "elecrow_eink",
"mcu": "nrf52840",
"variant": "ELECROW-ThinkNode-M3",
"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": "elecrow nrf",
"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": "",
"vendor": "ELECROW"
}

View File

@@ -1,53 +0,0 @@
{
"build": {
"arduino": {
"ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_NRF52840_ELECROW_M4 -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
["0x239A", "0x4405"],
["0x239A", "0x0029"],
["0x239A", "0x002A"]
],
"usb_product": "elecrow_thinknode_m4",
"mcu": "nrf52840",
"variant": "ELECROW-ThinkNode-M4",
"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": "ELECROW ThinkNode m4",
"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://www.elecrow.com/thinknode-m4-power-bank-lora-device-with-meshtastic-lora-tracker-function-powered-by-nrf52840.html",
"vendor": "ELECROW"
}

View File

@@ -1,53 +0,0 @@
{
"build": {
"arduino": {
"ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_NRF52840_ELECROW_M6 -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
["0x239A", "0x4405"],
["0x239A", "0x0029"],
["0x239A", "0x002A"]
],
"usb_product": "elecrow_thinknode_m6",
"mcu": "nrf52840",
"variant": "ELECROW-ThinkNode-M6",
"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": "ELECROW ThinkNode M6",
"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://www.elecrow.com/thinknode-m6-outdoor-solar-power-for-lora-powered-by-nrf52840-supports-gps.html",
"vendor": "ELECROW"
}

View File

@@ -1,56 +0,0 @@
{
"build": {
"arduino": {
"ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_NRF52840_MUZI_BASE -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [["0x239A", "0xcafe"]],
"mcu": "nrf52840",
"variant": "muzi-base",
"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": "Muzi Base",
"url": "https://muzi.works/",
"vendor": "MuziWorks",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"speed": 115200,
"protocol": "nrfutil",
"protocols": [
"jlink",
"nrfjprog",
"nrfutil",
"blackmagic",
"cmsis-dap",
"mbed",
"stlink"
],
"use_1200bps_touch": true,
"require_upload_port": true,
"wait_for_upload_port": true
}
}

13
debian/changelog vendored
View File

@@ -1,16 +1,3 @@
meshtasticd (2.7.16.0) unstable; urgency=medium
* Version 2.7.16
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Wed, 19 Nov 2025 16:12:32 +0000
meshtasticd (2.7.15.0) unstable; urgency=medium
* Version 2.7.15
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Thu, 13 Nov 2025 12:31:57 +0000
meshtasticd (2.7.14.0) unstable; urgency=medium
* Version 2.7.14

View File

@@ -50,13 +50,6 @@ BuildRequires: pkgconfig(x11)
BuildRequires: pkgconfig(libinput)
BuildRequires: pkgconfig(xkbcommon-x11)
# libbsd is needed on older Fedora/RHEL to provide 'strlcpy'
%if 0%{?fedora} >= 39 || 0%{?rhel} >= 10
BuildRequires: glibc-devel >= 2.38
%else
BuildRequires: pkgconfig(libbsd-overlay)
%endif
Requires: systemd-udev
%description

View File

@@ -62,7 +62,7 @@ monitor_speed = 115200
monitor_filters = direct
lib_deps =
# renovate: datasource=git-refs depName=meshtastic-esp8266-oled-ssd1306 packageName=https://github.com/meshtastic/esp8266-oled-ssd1306 gitBranch=master
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/2887bf4a19f64d92c984dcc8fd5ca7429e425e4a.zip
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/0cbc26b1f8f61957af0475f486b362eafe7cc4e2.zip
# renovate: datasource=git-refs depName=meshtastic-OneButton packageName=https://github.com/meshtastic/OneButton gitBranch=master
https://github.com/meshtastic/OneButton/archive/fa352d668c53f290cfa480a5f79ad422cd828c70.zip
# renovate: datasource=git-refs depName=meshtastic-arduino-fsm packageName=https://github.com/meshtastic/arduino-fsm gitBranch=master
@@ -90,7 +90,7 @@ framework = arduino
lib_deps =
${env.lib_deps}
# renovate: datasource=custom.pio depName=NonBlockingRTTTL packageName=end2endzone/library/NonBlockingRTTTL
end2endzone/NonBlockingRTTTL@1.4.0
end2endzone/NonBlockingRTTTL@1.3.0
build_flags = ${env.build_flags} -Os
build_src_filter = ${env.build_src_filter} -<platform/portduino/> -<graphics/niche/>
@@ -121,7 +121,7 @@ lib_deps =
[device-ui_base]
lib_deps =
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
https://github.com/meshtastic/device-ui/archive/28167c67dfd13015a0b5eef1828f95fe8e3ab7c3.zip
https://github.com/meshtastic/device-ui/archive/19b7855e9a1d9deff37391659ca7194e4ef57c43.zip
; Common libs for environmental measurements in telemetry module
[environmental_base]
@@ -169,7 +169,7 @@ lib_deps =
# renovate: datasource=git-refs depName=DFRobot_RainfallSensor packageName=https://github.com/DFRobot/DFRobot_RainfallSensor gitBranch=master
https://github.com/DFRobot/DFRobot_RainfallSensor/archive/38fea5e02b40a5430be6dab39a99a6f6347d667e.zip
# renovate: datasource=custom.pio depName=INA226 packageName=robtillaart/library/INA226
robtillaart/INA226@0.6.5
robtillaart/INA226@0.6.4
# 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
@@ -213,6 +213,6 @@ lib_deps =
# 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=custom.pio depName=Sensirion Core packageName=sensirion/library/Sensirion Core
sensirion/Sensirion Core@0.7.2
sensirion/Sensirion Core@0.7.1
# renovate: datasource=custom.pio depName=Sensirion I2C SCD4x packageName=sensirion/library/Sensirion I2C SCD4x
sensirion/Sensirion I2C SCD4x@1.1.0

View File

@@ -194,7 +194,7 @@ static HasBatteryLevel *batteryLevel; // Default to NULL for no battery level se
#ifdef BATTERY_PIN
void battery_adcEnable()
static void adcEnable()
{
#ifdef ADC_CTRL // enable adc voltage divider when we need to read
#ifdef ADC_USE_PULLUP
@@ -214,7 +214,7 @@ void battery_adcEnable()
#endif
}
static void battery_adcDisable()
static void adcDisable()
{
#ifdef ADC_CTRL // disable adc voltage divider when we need to read
#ifdef ADC_USE_PULLUP
@@ -278,11 +278,6 @@ class AnalogBatteryLevel : public HasBatteryLevel
break;
}
}
#if defined(BATTERY_CHARGING_INV)
// bit of trickery to show 99% up until the charge finishes
if (!digitalRead(BATTERY_CHARGING_INV) && battery_SOC > 99)
battery_SOC = 99;
#endif
return clamp((int)(battery_SOC), 0, 100);
}
@@ -325,7 +320,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
uint32_t raw = 0;
float scaled = 0;
battery_adcEnable();
adcEnable();
#ifdef ARCH_ESP32 // ADC block for espressif platforms
raw = espAdcRead();
scaled = esp_adc_cal_raw_to_voltage(raw, adc_characs);
@@ -337,7 +332,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
raw = raw / BATTERY_SENSE_SAMPLES;
scaled = operativeAdcMultiplier * ((1000 * AREF_VOLTAGE) / pow(2, BATTERY_SENSE_RESOLUTION_BITS)) * raw;
#endif
battery_adcDisable();
adcDisable();
if (!initial_read_done) {
// Flush the smoothing filter with an ADC reading, if the reading is plausibly correct
@@ -460,8 +455,6 @@ class AnalogBatteryLevel : public HasBatteryLevel
}
// if it's not HIGH - check the battery
#endif
#elif defined(MUZI_BASE)
return NRF_POWER->USBREGSTATUS & POWER_USBREGSTATUS_VBUSDETECT_Msk;
#endif
return getBattVoltage() > chargingVolt;
}
@@ -477,8 +470,6 @@ class AnalogBatteryLevel : public HasBatteryLevel
#endif
#ifdef EXT_CHRG_DETECT
return digitalRead(EXT_CHRG_DETECT) == ext_chrg_detect_value;
#elif defined(BATTERY_CHARGING_INV)
return !digitalRead(BATTERY_CHARGING_INV);
#else
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(DISABLE_INA_CHARGING_DETECTION)
if (hasINA()) {
@@ -692,8 +683,6 @@ bool Power::setup()
found = true;
} else if (lipoChargerInit()) {
found = true;
} else if (serialBatteryInit()) {
found = true;
} else if (meshSolarInit()) {
found = true;
} else if (analogInit()) {
@@ -709,18 +698,11 @@ bool Power::setup()
[]() {
power->setIntervalFromNow(0);
runASAP = true;
BaseType_t higherWake = 0;
},
CHANGE);
#endif
#ifdef BATTERY_CHARGING_INV
attachInterrupt(
BATTERY_CHARGING_INV,
[]() {
power->setIntervalFromNow(0);
runASAP = true;
},
CHANGE);
#endif
enabled = found;
low_voltage_counter = 0;
@@ -777,8 +759,6 @@ void Power::shutdown()
if (screen) {
#ifdef T_DECK_PRO
screen->showSimpleBanner("Device is powered off.\nConnect USB to start!", 0); // T-Deck Pro has no power button
#elif defined(USE_EINK)
screen->showSimpleBanner("Shutting Down...", 2250); // dismiss after 3 seconds to avoid the banner on the sleep screen
#else
screen->showSimpleBanner("Shutting Down...", 0); // stays on screen
#endif
@@ -926,8 +906,13 @@ void Power::readPowerStatus()
low_voltage_counter++;
LOG_DEBUG("Low voltage counter: %d/10", low_voltage_counter);
if (low_voltage_counter > 10) {
#ifdef ARCH_NRF52
// We can't trigger deep sleep on NRF52, it's freezing the board
LOG_DEBUG("Low voltage detected, but not trigger deep sleep");
#else
LOG_INFO("Low voltage detected, trigger deep sleep");
powerFSM.trigger(EVENT_LOW_BATTERY);
#endif
}
} else {
low_voltage_counter = 0;
@@ -1567,136 +1552,4 @@ bool Power::meshSolarInit()
{
return false;
}
#endif
#ifdef HAS_SERIAL_BATTERY_LEVEL
#include <SoftwareSerial.h>
/**
* SerialBatteryLevel class for pulling battery information from a secondary MCU over serial.
*/
class SerialBatteryLevel : public HasBatteryLevel
{
public:
/**
* Init the I2C meshSolar battery level sensor
*/
bool runOnce()
{
BatterySerial.begin(4800);
return true;
}
/**
* Battery state of charge, from 0 to 100 or -1 for unknown
*/
virtual int getBatteryPercent() override { return v_percent; }
/**
* The raw voltage of the battery in millivolts, or NAN if unknown
*/
virtual uint16_t getBattVoltage() override { return voltage * 1000; }
/**
* return true if there is a battery installed in this unit
*/
virtual bool isBatteryConnect() override
{
// definitely need to gobble up more bytes at once
if (BatterySerial.available() > 5) {
LOG_WARN("SerialBatteryLevel: %u bytes available", BatterySerial.available());
while (BatterySerial.available() > 11) {
BatterySerial.read(); // flush old data
}
LOG_WARN("SerialBatteryLevel: %u bytes now available", BatterySerial.available());
int tries = 0;
while (BatterySerial.read() != 0xFE) {
tries++; // wait for start byte
if (tries > 10) {
LOG_WARN("SerialBatteryLevel: no start byte found");
return 1;
}
}
Data[1] = BatterySerial.read();
Data[2] = BatterySerial.read();
Data[3] = BatterySerial.read();
Data[4] = BatterySerial.read();
Data[5] = BatterySerial.read();
if (Data[5] != 0xFD) {
LOG_WARN("SerialBatteryLevel: invalid end byte %02x", Data[5]);
return true;
}
v_percent = Data[1];
voltage = Data[2] + (((float)Data[3]) / 100) + (((float)Data[4]) / 10000);
voltage *= 2;
LOG_WARN("SerialBatteryLevel: received data %u, %f, %02x", v_percent, voltage, Data[5]);
return true;
}
// This function runs first, so use it to grab the latest data from the secondary MCU
return true;
}
/**
* return true if there is an external power source detected
*/
virtual bool isVbusIn() override
{
#if defined(EXT_CHRG_DETECT)
return digitalRead(EXT_CHRG_DETECT) == ext_chrg_detect_value;
#endif
return false;
}
virtual bool isCharging() override
{
#ifdef EXT_CHRG_DETECT
return digitalRead(EXT_CHRG_DETECT) == ext_chrg_detect_value;
#endif
// by default, we check the battery voltage only
return isVbusIn();
}
private:
SoftwareSerial BatterySerial = SoftwareSerial(SERIAL_BATTERY_RX, SERIAL_BATTERY_TX);
uint8_t Data[6] = {0};
int v_percent = 0;
float voltage = 0.0;
};
SerialBatteryLevel serialBatteryLevel;
/**
* Init the serial battery level sensor
*/
bool Power::serialBatteryInit()
{
#ifdef EXT_PWR_DETECT
pinMode(EXT_PWR_DETECT, INPUT);
#endif
#ifdef EXT_CHRG_DETECT
pinMode(EXT_CHRG_DETECT, ext_chrg_detect_mode);
#endif
bool result = serialBatteryLevel.runOnce();
LOG_DEBUG("Power::serialBatteryInit serial battery sensor is %s", result ? "ready" : "not ready yet");
if (!result)
return false;
batteryLevel = &serialBatteryLevel;
return true;
}
#else
/**
* If this device has no serial battery level sensor, don't try to use it.
*/
bool Power::serialBatteryInit()
{
return false;
}
#endif
#endif

View File

@@ -250,9 +250,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// Touchscreen
// -----------------------------------------------------------------------------
#define FT6336U_ADDR 0x48
#define CST328_ADDR 0x1A // same address as CST226SE
#define CST328_ADDR 0x1A
#define CHSC6X_ADDR 0x2E
#define CST226SE_ADDR_ALT 0x5A
// -----------------------------------------------------------------------------
// RAK12035VB Soil Monitor (using RAK12023 up to 3 RAK12035 monitors can be connected)
@@ -397,13 +396,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define HAS_RGB_LED
#endif
#ifndef LED_STATE_OFF
#define LED_STATE_OFF 0
#endif
#ifndef LED_STATE_ON
#define LED_STATE_ON 1
#endif
// default mapping of pins
#if defined(PIN_BUTTON2) && !defined(CANCEL_BUTTON_PIN)
#define ALT_BUTTON_PIN PIN_BUTTON2
@@ -418,6 +410,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#endif
#endif
// BME680 BSEC2 support detection
#if !defined(MESHTASTIC_BME680_BSEC2_SUPPORTED)
#if defined(RAK_4631) || defined(TBEAM_V10)
#define MESHTASTIC_BME680_BSEC2_SUPPORTED 1
#define MESHTASTIC_BME680_HEADER <bsec2.h>
#else
#define MESHTASTIC_BME680_BSEC2_SUPPORTED 0
#define MESHTASTIC_BME680_HEADER <Adafruit_BME680.h>
#endif // defined(RAK_4631)
#endif // !defined(MESHTASTIC_BME680_BSEC2_SUPPORTED)
// -----------------------------------------------------------------------------
// Global switches to turn off features for a minimized build
// -----------------------------------------------------------------------------

View File

@@ -85,8 +85,7 @@ class ScanI2C
DRV2605,
BH1750,
DA217,
CHSC6X,
CST226SE
CHSC6X
} DeviceType;
// typedef uint8_t DeviceAddress;

View File

@@ -499,18 +499,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
SCAN_SIMPLE_CASE(DFROBOT_RAIN_ADDR, DFROBOT_RAIN, "DFRobot Rain Gauge", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(LTR390UV_ADDR, LTR390UV, "LTR390UV", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(PCT2075_ADDR, PCT2075, "PCT2075", (uint8_t)addr.address);
case CST328_ADDR:
// Do we have the CST328 or the CST226SE
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xAB), 1);
if (registerValue == 0xA9) {
type = CST226SE;
logFoundDevice("CST226SE", (uint8_t)addr.address);
} else {
type = CST328;
logFoundDevice("CST328", (uint8_t)addr.address);
}
break;
SCAN_SIMPLE_CASE(CST328_ADDR, CST328, "CST328", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(CHSC6X_ADDR, CHSC6X, "CHSC6X", (uint8_t)addr.address);
case LTR553ALS_ADDR:
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x86), 1); // Part ID register
@@ -539,12 +528,8 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
#endif
case MLX90614_ADDR_DEF:
// Do we have the MLX90614 or the MPR121KB or the CST226SE
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x06), 1);
if (registerValue == 0xAB) {
type = CST226SE;
logFoundDevice("CST226SE", (uint8_t)addr.address);
} else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0e), 1) == 0x5a) {
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0e), 1);
if (registerValue == 0x5a) {
type = MLX90614;
logFoundDevice("MLX90614", (uint8_t)addr.address);
} else {
@@ -562,11 +547,6 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
case ICM20948_ADDR: // same as BMX160_ADDR
case ICM20948_ADDR_ALT: // same as MPU6050_ADDR
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1);
#ifdef HAS_ICM20948
type = ICM20948;
logFoundDevice("ICM20948", (uint8_t)addr.address);
break;
#endif
if (registerValue == 0xEA) {
type = ICM20948;
logFoundDevice("ICM20948", (uint8_t)addr.address);

View File

@@ -38,16 +38,14 @@ template <typename T, std::size_t N> std::size_t array_count(const T (&)[N])
return N;
}
#ifndef GPS_SERIAL_PORT
#define GPS_SERIAL_PORT Serial1
#endif
#if defined(ARCH_NRF52)
Uart *GPS::_serial_gps = &GPS_SERIAL_PORT;
#elif defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) || defined(ARCH_STM32WL)
#if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) || defined(ARCH_STM32WL)
#if defined(GPS_SERIAL_PORT)
HardwareSerial *GPS::_serial_gps = &GPS_SERIAL_PORT;
#else
HardwareSerial *GPS::_serial_gps = &Serial1;
#endif
#elif defined(ARCH_RP2040)
SerialUART *GPS::_serial_gps = &GPS_SERIAL_PORT;
SerialUART *GPS::_serial_gps = &Serial1;
#else
HardwareSerial *GPS::_serial_gps = nullptr;
#endif
@@ -242,9 +240,6 @@ GPS_RESPONSE GPS::getACK(const char *message, uint32_t waitMillis)
buffer[bytesRead] = b;
bytesRead++;
if ((bytesRead == 767) || (b == '\r')) {
#ifdef GPS_DEBUG
LOG_DEBUG(debugmsg.c_str());
#endif
if (strnstr((char *)buffer, message, bytesRead) != nullptr) {
#ifdef GPS_DEBUG
LOG_DEBUG("Found: %s", message); // Log the found message
@@ -252,6 +247,9 @@ GPS_RESPONSE GPS::getACK(const char *message, uint32_t waitMillis)
return GNSS_RESPONSE_OK;
} else {
bytesRead = 0;
#ifdef GPS_DEBUG
LOG_DEBUG(debugmsg.c_str());
#endif
}
}
}
@@ -896,11 +894,14 @@ void GPS::writePinEN(bool on)
void GPS::writePinStandby(bool standby)
{
#ifdef PIN_GPS_STANDBY // Specifically the standby pin for L76B, L76K and clones
bool val;
if (standby)
val = GPS_STANDBY_ACTIVE;
else
val = !GPS_STANDBY_ACTIVE;
// Determine the new value for the pin
// Normally: active HIGH for awake
#ifdef PIN_GPS_STANDBY_INVERTED
bool val = standby;
#else
bool val = !standby;
#endif
// Write and log
pinMode(PIN_GPS_STANDBY, OUTPUT);
@@ -1274,24 +1275,6 @@ GnssModel_t GPS::probe(int serialSpeed)
memset(&ublox_info, 0, sizeof(ublox_info));
delay(100);
#if defined(PIN_GPS_RESET) && PIN_GPS_RESET != -1
digitalWrite(PIN_GPS_RESET, GPS_RESET_MODE); // assert for 10ms
delay(10);
digitalWrite(PIN_GPS_RESET, !GPS_RESET_MODE);
// attempt to detect the chip based on boot messages
std::vector<ChipInfo> passive_detect = {
{"AG3335", "$PAIR021,AG3335", GNSS_MODEL_AG3335},
{"AG3352", "$PAIR021,AG3352", GNSS_MODEL_AG3352},
{"RYS3520", "$PAIR021,REYAX_RYS3520_V2", GNSS_MODEL_AG3352},
{"UC6580", "UC6580", GNSS_MODEL_UC6580},
// as L76K is sort of a last ditch effort, we won't attempt to detect it by startup messages for now.
/*{"L76K", "SW=URANUS", GNSS_MODEL_MTK}*/};
GnssModel_t detectedDriver = getProbeResponse(500, passive_detect, serialSpeed);
if (detectedDriver != GNSS_MODEL_UNKNOWN) {
return detectedDriver;
}
#endif
// Close all NMEA sentences, valid for L76K, ATGM336H (and likely other AT6558 devices)
_serial_gps->write("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n");
delay(20);
@@ -1490,12 +1473,12 @@ GnssModel_t GPS::getProbeResponse(unsigned long timeout, const std::vector<ChipI
}
if (c == ',' || (responseLen >= 2 && response[responseLen - 2] == '\r' && response[responseLen - 1] == '\n')) {
#ifdef GPS_DEBUG
LOG_DEBUG(response);
#endif
// check if we can see our chips
for (const auto &chipInfo : responseMap) {
if (strstr(response, chipInfo.detectionString.c_str()) != nullptr) {
#ifdef GPS_DEBUG
LOG_DEBUG(response);
#endif
LOG_INFO("%s detected", chipInfo.chipName.c_str());
delete[] response; // Cleanup before return
return chipInfo.driver;
@@ -1503,9 +1486,6 @@ GnssModel_t GPS::getProbeResponse(unsigned long timeout, const std::vector<ChipI
}
}
if (responseLen >= 2 && response[responseLen - 2] == '\r' && response[responseLen - 1] == '\n') {
#ifdef GPS_DEBUG
LOG_DEBUG(response);
#endif
// Reset the response buffer for the next potential message
responseLen = 0;
response[0] = '\0';
@@ -1524,7 +1504,10 @@ GPS *GPS::createGps()
int8_t _rx_gpio = config.position.rx_gpio;
int8_t _tx_gpio = config.position.tx_gpio;
int8_t _en_gpio = config.position.gps_en_gpio;
#if HAS_GPS && !defined(ARCH_ESP32)
_rx_gpio = 1; // We only specify GPS serial ports on ESP32. Otherwise, these are just flags.
_tx_gpio = 1;
#endif
#if defined(GPS_RX_PIN)
if (!_rx_gpio)
_rx_gpio = GPS_RX_PIN;
@@ -1589,6 +1572,8 @@ GPS *GPS::createGps()
#ifdef PIN_GPS_RESET
pinMode(PIN_GPS_RESET, OUTPUT);
digitalWrite(PIN_GPS_RESET, GPS_RESET_MODE); // assert for 10ms
delay(10);
digitalWrite(PIN_GPS_RESET, !GPS_RESET_MODE);
#endif
@@ -1598,28 +1583,16 @@ GPS *GPS::createGps()
_serial_gps->setRxBufferSize(SERIAL_BUFFER_SIZE); // the default is 256
#endif
LOG_DEBUG("Use GPIO%d for GPS RX", new_gps->rx_gpio);
LOG_DEBUG("Use GPIO%d for GPS TX", new_gps->tx_gpio);
// ESP32 has a special set of parameters vs other arduino ports
#if defined(ARCH_ESP32)
LOG_DEBUG("Use GPIO%d for GPS RX", new_gps->rx_gpio);
LOG_DEBUG("Use GPIO%d for GPS TX", new_gps->tx_gpio);
_serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, new_gps->rx_gpio, new_gps->tx_gpio);
#elif defined(ARCH_RP2040)
_serial_gps->setPinout(new_gps->tx_gpio, new_gps->rx_gpio);
_serial_gps->setFIFOSize(256);
_serial_gps->begin(GPS_BAUDRATE);
#elif defined(ARCH_NRF52)
_serial_gps->setPins(new_gps->rx_gpio, new_gps->tx_gpio);
_serial_gps->begin(GPS_BAUDRATE);
#elif defined(ARCH_STM32WL)
_serial_gps->setTx(new_gps->tx_gpio);
_serial_gps->setRx(new_gps->rx_gpio);
_serial_gps->begin(GPS_BAUDRATE);
#elif defined(ARCH_PORTDUINO)
// Portduino can't set the GPS pins directly.
_serial_gps->begin(GPS_BAUDRATE);
#else
#error Unsupported architecture!
_serial_gps->begin(GPS_BAUDRATE);
#endif
}
return new_gps;

View File

@@ -16,11 +16,6 @@
#define GPS_EN_ACTIVE 1
#endif
// Allow defining the polarity of the STANDBY output. default is LOW for standby
#ifndef GPS_STANDBY_ACTIVE
#define GPS_STANDBY_ACTIVE LOW
#endif
static constexpr uint32_t GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS = 10 * 1000UL;
static constexpr uint32_t GPS_FIX_HOLD_MAX_MS = 20000;
@@ -199,8 +194,6 @@ class GPS : private concurrency::OSThread
/** If !NULL we will use this serial port to construct our GPS */
#if defined(ARCH_RP2040)
static SerialUART *_serial_gps;
#elif defined(ARCH_NRF52)
static Uart *_serial_gps;
#else
static HardwareSerial *_serial_gps;
#endif

View File

@@ -112,11 +112,7 @@ RTCSetResult readFromRTC()
#elif defined(RX8130CE_RTC)
if (rtc_found.address == RX8130CE_RTC) {
uint32_t now = millis();
#ifdef MUZI_BASE
ArtronShop_RX8130CE rtc(&Wire1);
#else
ArtronShop_RX8130CE rtc(&Wire);
#endif
tm t;
if (rtc.getTime(&t)) {
tv.tv_sec = gm_mktime(&t);
@@ -249,11 +245,7 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd
}
#elif defined(RX8130CE_RTC)
if (rtc_found.address == RX8130CE_RTC) {
#ifdef MUZI_BASE
ArtronShop_RX8130CE rtc(&Wire1);
#else
ArtronShop_RX8130CE rtc(&Wire);
#endif
tm *t = gmtime(&tv->tv_sec);
if (rtc.setTime(*t)) {
LOG_DEBUG("RX8130CE setDateTime %02d-%02d-%02d %02d:%02d:%02d (%ld)", t->tm_year + 1900, t->tm_mon + 1,
@@ -318,7 +310,7 @@ RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t)
#ifdef BUILD_EPOCH
if (tv.tv_sec < BUILD_EPOCH) {
if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) {
LOG_WARN("Ignore time (%lu) before build epoch (%lu)!", printableEpoch, BUILD_EPOCH);
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
lastTimeValidationWarning = millis();
}
return RTCSetResultInvalidTime;
@@ -327,7 +319,7 @@ RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t)
// Calculate max allowed time safely to avoid overflow in logging
uint64_t maxAllowedTime = (uint64_t)BUILD_EPOCH + FORTY_YEARS;
uint32_t maxAllowedPrintable = (maxAllowedTime > UINT32_MAX) ? UINT32_MAX : (uint32_t)maxAllowedTime;
LOG_WARN("Ignore time (%lu) too far in the future (build epoch: %lu, max allowed: %lu)!", printableEpoch,
LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch,
(uint32_t)BUILD_EPOCH, maxAllowedPrintable);
lastTimeValidationWarning = millis();
}

View File

@@ -324,7 +324,7 @@ static int8_t prevFrame = -1;
// Combined dynamic node list frame cycling through LastHeard, HopSignal, and Distance modes
// Uses a single frame and changes data every few seconds (E-Ink variant is separate)
#if defined(ESP_PLATFORM) && (defined(USE_ST7789) || defined(USE_ST7796))
#if defined(ESP_PLATFORM) && defined(USE_ST7789)
SPIClass SPI1(HSPI);
#endif
@@ -356,18 +356,7 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
#else
dispdev = new ST7789Spi(&SPI1, ST7789_RESET, ST7789_RS, ST7789_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT);
#endif
#elif defined(USE_ST7796)
#ifdef ESP_PLATFORM
dispdev = new ST7796Spi(&SPI1, ST7796_RESET, ST7796_RS, ST7796_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT, ST7796_SDA,
ST7796_MISO, ST7796_SCK, TFT_SPI_FREQUENCY);
#else
dispdev = new ST7796Spi(&SPI1, ST7796_RESET, ST7796_RS, ST7796_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT);
#endif
#if defined(USE_ST7789)
static_cast<ST7789Spi *>(dispdev)->setRGB(TFT_MESH);
#elif defined(USE_ST7796)
static_cast<ST7796Spi *>(dispdev)->setRGB(TFT_MESH);
#endif
#elif defined(USE_SSD1306)
dispdev = new SSD1306Wire(address.address, -1, -1, geometry,
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
@@ -446,14 +435,6 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
PMU->enablePowerOutput(XPOWERS_ALDO2);
#endif
#if defined(MUZI_BASE)
dispdev->init();
dispdev->setBrightness(brightness);
dispdev->flipScreenVertically();
dispdev->resetDisplay();
digitalWrite(SCREEN_12V_ENABLE, HIGH);
delay(100);
#endif
#if !ARCH_PORTDUINO
dispdev->displayOn();
#endif
@@ -485,15 +466,6 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
pinMode(VTFT_LEDA, OUTPUT);
digitalWrite(VTFT_LEDA, TFT_BACKLIGHT_ON);
#endif
#endif
#ifdef USE_ST7796
ui->init();
#ifdef ESP_PLATFORM
analogWrite(VTFT_LEDA, BRIGHTNESS_DEFAULT);
#else
pinMode(VTFT_LEDA, OUTPUT);
digitalWrite(VTFT_LEDA, TFT_BACKLIGHT_ON);
#endif
#endif
enabled = true;
setInterval(0); // Draw ASAP
@@ -512,10 +484,6 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
#endif
dispdev->displayOff();
#ifdef SCREEN_12V_ENABLE
digitalWrite(SCREEN_12V_ENABLE, LOW);
#endif
#ifdef USE_ST7789
SPI1.end();
#if defined(ARCH_ESP32)
@@ -532,21 +500,6 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
nrf_gpio_cfg_default(ST7789_NSS);
#endif
#endif
#ifdef USE_ST7796
SPI1.end();
#if defined(ARCH_ESP32)
pinMode(VTFT_LEDA, OUTPUT);
digitalWrite(VTFT_LEDA, LOW);
pinMode(ST7796_RESET, ANALOG);
pinMode(ST7796_RS, ANALOG);
pinMode(ST7796_NSS, ANALOG);
#else
nrf_gpio_cfg_default(VTFT_LEDA);
nrf_gpio_cfg_default(ST7796_RESET);
nrf_gpio_cfg_default(ST7796_RS);
nrf_gpio_cfg_default(ST7796_NSS);
#endif
#endif
#ifdef T_WATCH_S3
PMU->disablePowerOutput(XPOWERS_ALDO2);
@@ -581,7 +534,7 @@ void Screen::setup()
static_cast<AutoOLEDWire *>(dispdev)->setDetected(model);
#endif
#if defined(USE_SH1107_128_64) || defined(USE_SH1107)
#ifdef USE_SH1107_128_64
static_cast<SH1106Wire *>(dispdev)->setSubtype(7);
#endif
@@ -589,13 +542,6 @@ void Screen::setup()
// Apply custom RGB color (e.g. Heltec T114/T190)
static_cast<ST7789Spi *>(dispdev)->setRGB(TFT_MESH);
#endif
#if defined(MUZI_BASE)
dispdev->delayPoweron = true;
#endif
#if defined(USE_ST7796) && defined(TFT_MESH)
// Custom text color, if defined in variant.h
static_cast<ST7796Spi *>(dispdev)->setRGB(TFT_MESH);
#endif
// === Initialize display and UI system ===
ui->init();
@@ -659,8 +605,6 @@ void Screen::setup()
static_cast<TFTDisplay *>(dispdev)->flipScreenVertically();
#elif defined(USE_ST7789)
static_cast<ST7789Spi *>(dispdev)->flipScreenVertically();
#elif defined(USE_ST7796)
static_cast<ST7796Spi *>(dispdev)->mirrorScreen();
#elif !defined(M5STACK_UNITC6L)
dispdev->flipScreenVertically();
#endif
@@ -693,7 +637,7 @@ void Screen::setup()
touchScreenImpl1->init();
}
}
#elif HAS_TOUCHSCREEN && !defined(USE_EINK) && !HAS_CST226SE
#elif HAS_TOUCHSCREEN && !defined(USE_EINK)
touchScreenImpl1 =
new TouchScreenImpl1(dispdev->getWidth(), dispdev->getHeight(), static_cast<TFTDisplay *>(dispdev)->getTouch);
touchScreenImpl1->init();

View File

@@ -83,8 +83,6 @@ class Screen
#include <ST7789Spi.h>
#elif defined(USE_SPISSD1306)
#include <SSD1306Spi.h>
#elif defined(USE_ST7796)
#include <ST7796Spi.h>
#else
// the SH1106/SSD1306 variant is auto-detected
#include <AutoOLEDWire.h>
@@ -251,8 +249,6 @@ class Screen : public concurrency::OSThread
bool isOverlayBannerShowing();
bool isScreenOn() { return screenOn; }
// Stores the last 4 of our hardware ID, to make finding the device for pairing easier
// FIXME: Needs refactoring and getMacAddr needs to be moved to a utility class
char ourId[5];

View File

@@ -73,7 +73,7 @@
#endif
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(USE_ST7796)) && \
defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS)) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
// The screen is bigger so use bigger fonts
#define FONT_SMALL FONT_MEDIUM_LOCAL // Height: 19

View File

@@ -427,35 +427,33 @@ static LGFX *tft = nullptr;
#include "lgfx/v1/Touch.hpp"
namespace lgfx
{
inline namespace v1
{
inline namespace v1
{
class TOUCH_CHSC6X : public ITouch
{
public:
public:
TOUCH_CHSC6X(void)
{
_cfg.i2c_addr = TOUCH_SLAVE_ADDRESS;
_cfg.x_min = 0;
_cfg.x_max = 240;
_cfg.y_min = 0;
_cfg.y_max = 320;
_cfg.i2c_addr = TOUCH_SLAVE_ADDRESS;
_cfg.x_min = 0;
_cfg.x_max = 240;
_cfg.y_min = 0;
_cfg.y_max = 320;
};
bool init(void) override
{
if (chsc6xTouch == nullptr) {
chsc6xTouch = new chsc6x(&Wire1, TOUCH_SDA_PIN, TOUCH_SCL_PIN, TOUCH_INT_PIN, TOUCH_RST_PIN);
bool init(void) override {
if(chsc6xTouch==nullptr) {
chsc6xTouch=new chsc6x(&Wire1,TOUCH_SDA_PIN,TOUCH_SCL_PIN,TOUCH_INT_PIN,TOUCH_RST_PIN);
}
chsc6xTouch->chsc6x_init();
return true;
};
uint_fast8_t getTouchRaw(touch_point_t *tp, uint_fast8_t count) override
{
uint16_t raw_x, raw_y;
if (chsc6xTouch->chsc6x_read_touch_info(&raw_x, &raw_y) == 0) {
tp[0].x = 320 - 1 - raw_y;
tp[0].y = 240 - 1 - raw_x;
uint_fast8_t getTouchRaw(touch_point_t* tp, uint_fast8_t count) override {
uint16_t raw_x,raw_y;
if (chsc6xTouch->chsc6x_read_touch_info(&raw_x, &raw_y)==0) {
tp[0].x = 320-1-raw_y;
tp[0].y = 240-1-raw_x ;
tp[0].size = 1;
tp[0].id = 1;
return 1;
@@ -464,14 +462,13 @@ class TOUCH_CHSC6X : public ITouch
return 0;
};
void wakeup(void) override{};
void sleep(void) override{};
void wakeup(void) override {};
void sleep(void) override {};
private:
chsc6x *chsc6xTouch = nullptr;
};
} // namespace v1
} // namespace lgfx
chsc6x *chsc6xTouch=nullptr;
};
}
}
#endif
class LGFX : public lgfx::LGFX_Device
{
@@ -516,9 +513,9 @@ class LGFX : public lgfx::LGFX_Device
{ // Set the display panel control.
auto cfg = _panel_instance.config(); // Gets a structure for display panel settings.
cfg.pin_cs = ST7789_CS; // Pin number where CS is connected (-1 = disable)
cfg.pin_rst = ST7789_RESET; // Pin number where RST is connected (-1 = disable)
cfg.pin_busy = ST7789_BUSY; // Pin number where BUSY is connected (-1 = disable)
cfg.pin_cs = ST7789_CS; // Pin number where CS is connected (-1 = disable)
cfg.pin_rst = ST7789_RESET; // Pin number where RST is connected (-1 = disable)
cfg.pin_busy = ST7789_BUSY; // Pin number where BUSY is connected (-1 = disable)
// The following setting values are general initial values for each panel, so please comment out any
// unknown items and try them.

View File

@@ -101,23 +101,3 @@ void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength)
else
snprintf(timeStr, maxLength, "unknown age");
}
void getUptimeStr(uint32_t uptimeMillis, const char *prefix, char *uptimeStr, uint8_t maxLength, bool includeSecs)
{
uint32_t days = uptimeMillis / 86400000;
uint32_t hours = (uptimeMillis % 86400000) / 3600000;
uint32_t mins = (uptimeMillis % 3600000) / 60000;
uint32_t secs = (uptimeMillis % 60000) / 1000;
if (days) {
snprintf(uptimeStr, maxLength, "%s: %ud %uh", prefix, days, hours);
} else if (hours) {
snprintf(uptimeStr, maxLength, "%s: %uh %um", prefix, hours, mins);
} else if (!includeSecs) {
snprintf(uptimeStr, maxLength, "%s: %um", prefix, mins);
} else if (mins) {
snprintf(uptimeStr, maxLength, "%s: %um %us", prefix, mins, secs);
} else {
snprintf(uptimeStr, maxLength, "%s: %us", prefix, secs);
}
}

View File

@@ -24,10 +24,3 @@ bool deltaToTimestamp(uint32_t secondsAgo, uint8_t *hours, uint8_t *minutes, int
* @param maxLength Maximum length of the resulting string buffer
*/
void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength);
/**
* Get a compact human-readable string that only shows the largest non-zero time components.
* For example, 0 days 1 hour 2 minutes will display as "1h 2m" but 1 day 2 hours 3 minutes
* will display as "1d 2h".
*/
void getUptimeStr(uint32_t uptimeMillis, const char *prefix, char *uptimeStr, uint8_t maxLength, bool includeSecs = false);

View File

@@ -194,12 +194,17 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
graphics::drawCommonHeader(display, x, y, titleStr, true, true);
int line = 0;
#ifdef T_WATCH_S3
if (nimbleBluetooth && nimbleBluetooth->isConnected()) {
graphics::ClockRenderer::drawBluetoothConnectedIcon(display, display->getWidth() - 18, display->getHeight() - 14);
}
#endif
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // Display local timezone
char timeString[16];
int hour = 0;
int minute = 0;
int second = 0;
if (rtc_sec > 0) {
long hms = rtc_sec % SEC_PER_DAY;
hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
@@ -210,11 +215,11 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
}
bool isPM = hour >= 12;
// hour = hour > 12 ? hour - 12 : hour;
if (config.display.use_12h_clock) {
hour %= 12;
if (hour == 0) {
if (hour == 0)
hour = 12;
}
snprintf(timeString, sizeof(timeString), "%d:%02d", hour, minute);
} else {
snprintf(timeString, sizeof(timeString), "%02d:%02d", hour, minute);
@@ -224,56 +229,24 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
char secondString[8];
snprintf(secondString, sizeof(secondString), "%02d", second);
static bool scaleInitialized = false;
static float scale = 0.75f;
static float segmentWidth = SEGMENT_WIDTH * 0.75f;
static float segmentHeight = SEGMENT_HEIGHT * 0.75f;
if (!scaleInitialized) {
float screenwidth_target_ratio = 0.80f; // Target 80% of display width (adjustable)
float max_scale = 3.5f; // Safety limit to avoid runaway scaling
float step = 0.05f; // Step increment per iteration
float target_width = display->getWidth() * screenwidth_target_ratio;
float target_height =
display->getHeight() -
(isHighResolution
? 46
: 33); // Be careful adjusting this number, we have to account for header and the text under the time
float calculated_width_size = 0.0f;
float calculated_height_size = 0.0f;
while (true) {
segmentWidth = SEGMENT_WIDTH * scale;
segmentHeight = SEGMENT_HEIGHT * scale;
calculated_width_size = segmentHeight + ((segmentWidth + (segmentHeight * 2) + 4) * 4);
calculated_height_size = segmentHeight + ((segmentHeight + (segmentHeight * 2) + 4) * 2);
if (calculated_width_size >= target_width || calculated_height_size >= target_height || scale >= max_scale) {
break;
}
scale += step;
}
// If we overshot width, back off one step and recompute segment sizes
if (calculated_width_size > target_width || calculated_height_size > target_height) {
scale -= step;
segmentWidth = SEGMENT_WIDTH * scale;
segmentHeight = SEGMENT_HEIGHT * scale;
}
scaleInitialized = true;
#ifdef T_WATCH_S3
float scale = 1.5;
#elif defined(CHATTER_2)
float scale = 1.1;
#else
float scale = 0.75;
if (isHighResolution) {
scale = 1.5;
}
#endif
size_t len = strlen(timeString);
uint16_t segmentWidth = SEGMENT_WIDTH * scale;
uint16_t segmentHeight = SEGMENT_HEIGHT * scale;
// calculate hours:minutes string width
uint16_t timeStringWidth = len * 5; // base spacing between characters
uint16_t timeStringWidth = strlen(timeString) * 5;
for (size_t i = 0; i < len; i++) {
for (uint8_t i = 0; i < strlen(timeString); i++) {
char character = timeString[i];
if (character == ':') {
@@ -284,21 +257,19 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
}
uint16_t hourMinuteTextX = (display->getWidth() / 2) - (timeStringWidth / 2);
uint16_t startingHourMinuteTextX = hourMinuteTextX;
uint16_t hourMinuteTextY = (display->getHeight() / 2) - (((segmentWidth * 2) + (segmentHeight * 3) + 8) / 2) + 2;
uint16_t hourMinuteTextY = (display->getHeight() / 2) - (((segmentWidth * 2) + (segmentHeight * 3) + 8) / 2);
// iterate over characters in hours:minutes string and draw segmented characters
for (size_t i = 0; i < len; i++) {
for (uint8_t i = 0; i < strlen(timeString); i++) {
char character = timeString[i];
if (character == ':') {
drawSegmentedDisplayColon(display, hourMinuteTextX, hourMinuteTextY, scale);
hourMinuteTextX += segmentHeight + 6;
if (scale >= 2.0f) {
hourMinuteTextX += (uint16_t)(4.5f * scale);
}
} else {
drawSegmentedDisplayCharacter(display, hourMinuteTextX, hourMinuteTextY, character - '0', scale);
@@ -308,29 +279,38 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
hourMinuteTextX += 5;
}
// draw seconds string + AM/PM
// draw seconds string
display->setFont(FONT_SMALL);
int xOffset = (isHighResolution) ? 0 : -1;
if (hour >= 10) {
xOffset += (isHighResolution) ? 32 : 18;
}
int yOffset = (isHighResolution) ? 3 : 1;
#ifdef SENSECAP_INDICATOR
yOffset -= 3;
#endif
#ifdef T_DECK
yOffset -= 5;
#endif
if (config.display.use_12h_clock) {
display->drawString(startingHourMinuteTextX + xOffset, (display->getHeight() - hourMinuteTextY) - 1, isPM ? "pm" : "am");
display->drawString(startingHourMinuteTextX + xOffset, (display->getHeight() - hourMinuteTextY) - yOffset - 2,
isPM ? "pm" : "am");
}
#ifndef USE_EINK
xOffset = (isHighResolution) ? 18 : 10;
if (scale >= 2.0f) {
xOffset -= (int)(4.5f * scale);
}
display->drawString(startingHourMinuteTextX + timeStringWidth - xOffset, (display->getHeight() - hourMinuteTextY) - 1,
display->drawString(startingHourMinuteTextX + timeStringWidth - xOffset, (display->getHeight() - hourMinuteTextY) - yOffset,
secondString);
#endif
graphics::drawCommonFooter(display, x, y);
}
void drawBluetoothConnectedIcon(OLEDDisplay *display, int16_t x, int16_t y)
{
display->drawFastImage(x, y, 18, 14, bluetoothConnectedIcon);
}
// Draw an analog clock
void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
@@ -341,6 +321,11 @@ void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
graphics::drawCommonHeader(display, x, y, titleStr, true, true);
int line = 0;
#ifdef T_WATCH_S3
if (nimbleBluetooth && nimbleBluetooth->isConnected()) {
drawBluetoothConnectedIcon(display, display->getWidth() - 18, display->getHeight() - 14);
}
#endif
// clock face center coordinates
int16_t centerX = display->getWidth() / 2;
int16_t centerY = display->getHeight() / 2;

View File

@@ -24,6 +24,7 @@ void drawVerticalSegment(OLEDDisplay *display, int x, int y, int width, int heig
// UI elements for clock displays
// void drawWatchFaceToggleButton(OLEDDisplay *display, int16_t x, int16_t y, bool digitalMode = true, float scale = 1);
void drawBluetoothConnectedIcon(OLEDDisplay *display, int16_t x, int16_t y);
} // namespace ClockRenderer

View File

@@ -11,7 +11,6 @@
#include "gps/RTC.h"
#include "graphics/ScreenFonts.h"
#include "graphics/SharedUIDisplay.h"
#include "graphics/TimeFormatters.h"
#include "graphics/images.h"
#include "main.h"
#include "mesh/Channels.h"
@@ -97,8 +96,7 @@ void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16
(storeForwardModule->heartbeatInterval * 1200))) { // no heartbeat, overlap a bit
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS) || \
defined(USE_ST7796) || \
ARCH_PORTDUINO) && \
ARCH_PORTDUINO) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 12,
8, imgQuestionL1);
@@ -110,7 +108,7 @@ void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16
#endif
} else {
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS) || defined(USE_ST7796)) && \
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS)) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
display->drawFastImage(x + SCREEN_WIDTH - 18 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 16,
8, imgSFL1);
@@ -126,8 +124,7 @@ void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16
// TODO: Raspberry Pi supports more than just the one screen size
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS) || \
defined(USE_ST7796) || \
ARCH_PORTDUINO) && \
ARCH_PORTDUINO) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8,
imgInfoL1);
@@ -653,7 +650,17 @@ void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x
if (SCREEN_HEIGHT > 64 || (SCREEN_HEIGHT <= 64 && line <= 5)) { // Only show uptime if the screen can show it
char uptimeStr[32] = "";
getUptimeStr(millis(), "Up", uptimeStr, sizeof(uptimeStr));
uint32_t uptime = millis() / 1000;
uint32_t days = uptime / 86400;
uint32_t hours = (uptime % 86400) / 3600;
uint32_t mins = (uptime % 3600) / 60;
// Show as "Up: 2d 3h", "Up: 5h 14m", or "Up: 37m"
if (days)
snprintf(uptimeStr, sizeof(uptimeStr), " Up: %ud %uh", days, hours);
else if (hours)
snprintf(uptimeStr, sizeof(uptimeStr), " Up: %uh %um", hours, mins);
else
snprintf(uptimeStr, sizeof(uptimeStr), " Uptime: %um", mins);
textWidth = display->getStringWidth(uptimeStr);
nameX = (SCREEN_WIDTH - textWidth) / 2;
display->drawString(nameX, getTextPositions(display)[line++], uptimeStr);
@@ -722,4 +729,4 @@ void drawChirpy(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int1
} // namespace DebugRenderer
} // namespace graphics
#endif
#endif

View File

@@ -119,7 +119,6 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
auto changes = SEGMENT_CONFIG;
// This is needed as we wait til picking the LoRa region to generate keys for the first time.
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN || MESHTASTIC_EXCLUDE_PKI)
if (!owner.is_licensed) {
bool keygenSuccess = false;
if (config.security.private_key.size == 32) {
@@ -140,7 +139,6 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32);
}
}
#endif
config.lora.tx_enabled = true;
initRegion();
if (myRegion->dutyCycle < 100) {
@@ -583,8 +581,11 @@ void menuHandler::systemBaseMenu()
optionsArray[options] = "Notifications";
optionsEnumArray[options++] = Notifications;
#if defined(ST7789_CS) || defined(ST7796_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || \
defined(USE_SH1107) || defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT
optionsArray[options] = "Display Options";
optionsEnumArray[options++] = ScreenOptions;
#endif
#if defined(M5STACK_UNITC6L)
optionsArray[options] = "Bluetooth";
@@ -784,24 +785,17 @@ void menuHandler::nodeNameLengthMenu()
void menuHandler::resetNodeDBMenu()
{
static const char *optionsArray[] = {"Back", "Reset All", "Preserve Favorites"};
static const char *optionsArray[] = {"Back", "Confirm"};
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Confirm Reset NodeDB";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 3;
bannerOptions.optionsCount = 2;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 1 || selected == 2) {
disableBluetooth();
screen->setFrames(Screen::FOCUS_DEFAULT);
}
if (selected == 1) {
disableBluetooth();
LOG_INFO("Initiate node-db reset");
nodeDB->resetNodes();
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
} else if (selected == 2) {
LOG_INFO("Initiate node-db reset but keeping favorites");
nodeDB->resetNodes(1);
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
}
};
screen->showOverlayBanner(bannerOptions);
@@ -937,9 +931,7 @@ void menuHandler::BluetoothToggleMenu()
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 3;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 0)
return;
else if (selected != (config.bluetooth.enabled ? 1 : 2)) {
if (selected == 1 || selected == 2) {
InputEvent event = {.inputEvent = (input_broker_event)170, .kbchar = 170, .touchX = 0, .touchY = 0};
inputBroker->injectInputEvent(&event);
}
@@ -1355,7 +1347,7 @@ void menuHandler::screenOptionsMenu()
optionsEnumArray[options++] = ScreenColor;
#endif
optionsArray[options] = "Frame Visibility Toggle";
optionsArray[options] = "Frame Visiblity Toggle";
optionsEnumArray[options++] = FrameToggles;
optionsArray[options] = "Display Units";
@@ -1752,4 +1744,4 @@ void menuHandler::saveUIConfig()
} // namespace graphics
#endif
#endif

View File

@@ -11,7 +11,6 @@
#include "graphics/Screen.h"
#include "graphics/ScreenFonts.h"
#include "graphics/SharedUIDisplay.h"
#include "graphics/TimeFormatters.h"
#include "graphics/images.h"
#include "main.h"
#include "target_specific.h"
@@ -257,7 +256,7 @@ void UIRenderer::drawNodes(OLEDDisplay *display, int16_t x, int16_t y, const mes
}
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS) || defined(USE_ST7796)) && \
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS)) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
if (isHighResolution) {
@@ -384,7 +383,17 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st
// === 4. Uptime (only show if metric is present) ===
char uptimeStr[32] = "";
if (node->has_device_metrics && node->device_metrics.has_uptime_seconds) {
getUptimeStr(node->device_metrics.uptime_seconds * 1000, " Up", uptimeStr, sizeof(uptimeStr));
uint32_t uptime = node->device_metrics.uptime_seconds;
uint32_t days = uptime / 86400;
uint32_t hours = (uptime % 86400) / 3600;
uint32_t mins = (uptime % 3600) / 60;
// Show as "Up: 2d 3h", "Up: 5h 14m", or "Up: 37m"
if (days)
snprintf(uptimeStr, sizeof(uptimeStr), " Uptime: %ud %uh", days, hours);
else if (hours)
snprintf(uptimeStr, sizeof(uptimeStr), " Uptime: %uh %um", hours, mins);
else
snprintf(uptimeStr, sizeof(uptimeStr), " Uptime: %um", mins);
}
if (uptimeStr[0] && line < 5) {
display->drawString(x, getTextPositions(display)[line++], uptimeStr);
@@ -583,8 +592,18 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta
drawNodes(display, x + 1, getTextPositions(display)[line] + 2, nodeStatus, -1, false, "online");
#endif
char uptimeStr[32] = "";
uint32_t uptime = millis() / 1000;
uint32_t days = uptime / 86400;
uint32_t hours = (uptime % 86400) / 3600;
uint32_t mins = (uptime % 3600) / 60;
// Show as "Up: 2d 3h", "Up: 5h 14m", or "Up: 37m"
#if !defined(M5STACK_UNITC6L)
getUptimeStr(millis(), "Up", uptimeStr, sizeof(uptimeStr));
if (days)
snprintf(uptimeStr, sizeof(uptimeStr), "Up: %ud %uh", days, hours);
else if (hours)
snprintf(uptimeStr, sizeof(uptimeStr), "Up: %uh %um", hours, mins);
else
snprintf(uptimeStr, sizeof(uptimeStr), "Up: %um", mins);
#endif
display->drawString(SCREEN_WIDTH - display->getStringWidth(uptimeStr), getTextPositions(display)[line++], uptimeStr);
@@ -1029,17 +1048,36 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU
if (strcmp(displayLine, "GPS off") != 0 && strcmp(displayLine, "No GPS") != 0) {
// === Second Row: Last GPS Fix ===
if (gpsStatus->getLastFixMillis() > 0) {
uint32_t delta = millis() - gpsStatus->getLastFixMillis();
char uptimeStr[32];
uint32_t delta = (millis() - gpsStatus->getLastFixMillis()) / 1000; // seconds since last fix
uint32_t days = delta / 86400;
uint32_t hours = (delta % 86400) / 3600;
uint32_t mins = (delta % 3600) / 60;
uint32_t secs = delta % 60;
char buf[32];
#if defined(USE_EINK)
// E-Ink: skip seconds, show only days/hours/mins
getUptimeStr(delta, "Last", uptimeStr, sizeof(uptimeStr), false);
if (days > 0) {
snprintf(buf, sizeof(buf), "Last: %ud %uh", days, hours);
} else if (hours > 0) {
snprintf(buf, sizeof(buf), "Last: %uh %um", hours, mins);
} else {
snprintf(buf, sizeof(buf), "Last: %um", mins);
}
#else
// Non E-Ink: include seconds where useful
getUptimeStr(delta, "Last", uptimeStr, sizeof(uptimeStr), true);
if (days > 0) {
snprintf(buf, sizeof(buf), "Last: %ud %uh", days, hours);
} else if (hours > 0) {
snprintf(buf, sizeof(buf), "Last: %uh %um", hours, mins);
} else if (mins > 0) {
snprintf(buf, sizeof(buf), "Last: %um %us", mins, secs);
} else {
snprintf(buf, sizeof(buf), "Last: %us", secs);
}
#endif
display->drawString(0, getTextPositions(display)[line++], uptimeStr);
display->drawString(0, getTextPositions(display)[line++], buf);
} else {
display->drawString(0, getTextPositions(display)[line++], "Last: ?");
}
@@ -1384,4 +1422,4 @@ std::string UIRenderer::drawTimeDelta(uint32_t days, uint32_t hours, uint32_t mi
} // namespace graphics
#endif // HAS_SCREEN
#endif // HAS_SCREEN

View File

@@ -27,7 +27,8 @@ const uint8_t bluetoothConnectedIcon[36] PROGMEM = {0xfe, 0x01, 0xff, 0x03, 0x03
0xfe, 0x31, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0xf0, 0x3f, 0xe0, 0x1f};
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(USE_ST7796) || defined(ST7796_CS) || ARCH_PORTDUINO) && \
defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || \
ARCH_PORTDUINO) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
const uint8_t imgQuestionL1[] PROGMEM = {0xff, 0x01, 0x01, 0x32, 0x7b, 0x49, 0x49, 0x6f, 0x26, 0x01, 0x01, 0xff};
const uint8_t imgQuestionL2[] PROGMEM = {0x0f, 0x08, 0x08, 0x08, 0x06, 0x0f, 0x0f, 0x06, 0x08, 0x08, 0x08, 0x0f};

View File

@@ -5,6 +5,7 @@ A pattern / collection of resources for creating custom UIs, to target small gro
For an example, see the `heltec-vision-master-e290-inkhud` platformio env.
- platformio.ini
- suppress default Meshtastic components (Screen, ButtonThread, etc)
- define `MESHTASTIC_INCLUDE_NICHE_GRAPHICS`
- (possibly) Edit `build_src_filter` to include our new nicheGraphics.h file

View File

@@ -52,7 +52,7 @@ int InputBroker::handleInputEvent(const InputEvent *event)
powerFSM.trigger(EVENT_INPUT); // todo: not every input should wake, like long hold release
if (event && event->inputEvent != INPUT_BROKER_NONE && externalNotificationModule &&
moduleConfig.external_notification.enabled && externalNotificationModule->nagging()) {
moduleConfig.external_notification.enabled) {
externalNotificationModule->stopNow();
}

View File

@@ -477,10 +477,6 @@ void setup()
#ifdef RESET_OLED
pinMode(RESET_OLED, OUTPUT);
digitalWrite(RESET_OLED, 1);
delay(2);
digitalWrite(RESET_OLED, 0);
delay(10);
digitalWrite(RESET_OLED, 1);
#endif
#ifdef SENSOR_POWER_CTRL_PIN
@@ -877,7 +873,7 @@ void setup()
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || \
defined(ST7789_CS) || defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(USE_ST7796) || \
defined(ST7789_CS) || defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS) || \
defined(USE_SPISSD1306)
screen = new graphics::Screen(screen_found, screen_model, screen_geometry);
#elif defined(ARCH_PORTDUINO)
@@ -963,7 +959,6 @@ void setup()
i2cScanner.reset();
#endif
#if !defined(MESHTASTIC_EXCLUDE_PKI)
// warn the user about a low entropy key
if (nodeDB->keyIsLowEntropy && !nodeDB->hasWarned) {
LOG_WARN(LOW_ENTROPY_WARNING);
@@ -974,7 +969,6 @@ void setup()
service->sendClientNotification(cn);
nodeDB->hasWarned = true;
}
#endif
// buttons are now inputBroker, so have to come after setupModules
#if HAS_BUTTON
@@ -1154,7 +1148,7 @@ void setup()
// Don't call screen setup until after nodedb is setup (because we need
// the current region name)
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || \
defined(ST7789_CS) || defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(USE_ST7796) || \
defined(ST7789_CS) || defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS) || \
defined(USE_SPISSD1306)
if (screen)
screen->setup();

View File

@@ -244,8 +244,6 @@ template <typename T> void LR11x0Interface<T>::startReceive()
// We use a 16 bit preamble so this should save some power by letting radio sit in standby mostly.
int err =
lora.startReceive(RADIOLIB_LR11X0_RX_TIMEOUT_INF, MESHTASTIC_RADIOLIB_IRQ_RX_FLAGS, RADIOLIB_IRQ_RX_DEFAULT_MASK, 0);
if (err)
LOG_ERROR("StartReceive error: %d", err);
assert(err == RADIOLIB_ERR_NONE);
RadioLibInterface::startReceive();
@@ -306,4 +304,4 @@ template <typename T> bool LR11x0Interface<T>::sleep()
return true;
}
#endif
#endif

View File

@@ -653,7 +653,7 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
strncpy(config.network.ntp_server, "meshtastic.pool.ntp.org", 32);
#if (defined(T_DECK) || defined(T_WATCH_S3) || defined(UNPHONE) || defined(PICOMPUTER_S3) || defined(SENSECAP_INDICATOR) || \
defined(ELECROW_PANEL) || defined(HELTEC_V4_TFT)) && \
defined(ELECROW_PANEL)||defined(HELTEC_V4_TFT)) && \
HAS_TFT
// switch BT off by default; use TFT programming mode or hotkey to enable
config.bluetooth.enabled = false;
@@ -664,7 +664,7 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
config.bluetooth.fixed_pin = defaultBLEPin;
#if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7789_CS) || \
defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(USE_SPISSD1306) || defined(USE_ST7796)
defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(USE_SPISSD1306)
bool hasScreen = true;
#ifdef HELTEC_MESH_NODE_T114
uint32_t st7789_id = get_st7789_id(ST7789_NSS, ST7789_SCK, ST7789_SDA, ST7789_RS, ST7789_RESET);
@@ -734,9 +734,6 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
config.display.screen_on_secs = 30;
config.display.wake_on_tap_or_motion = true;
#endif
#ifdef COMPASS_ORIENTATION
config.display.compass_orientation = COMPASS_ORIENTATION;
#endif
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WIFI
if (WiFiOTA::isUpdated()) {
WiFiOTA::recoverConfig(&config.network);
@@ -2011,7 +2008,6 @@ UserLicenseStatus NodeDB::getLicenseStatus(uint32_t nodeNum)
return info->user.is_licensed ? UserLicenseStatus::Licensed : UserLicenseStatus::NotLicensed;
}
#if !defined(MESHTASTIC_EXCLUDE_PKI)
bool NodeDB::checkLowEntropyPublicKey(const meshtastic_Config_SecurityConfig_public_key_t &keyToTest)
{
if (keyToTest.size == 32) {
@@ -2026,7 +2022,6 @@ bool NodeDB::checkLowEntropyPublicKey(const meshtastic_Config_SecurityConfig_pub
}
return false;
}
#endif
bool NodeDB::backupPreferences(meshtastic_AdminMessage_BackupLocation location)
{

View File

@@ -283,9 +283,7 @@ class NodeDB
bool hasValidPosition(const meshtastic_NodeInfoLite *n);
#if !defined(MESHTASTIC_EXCLUDE_PKI)
bool checkLowEntropyPublicKey(const meshtastic_Config_SecurityConfig_public_key_t &keyToTest);
#endif
bool backupPreferences(meshtastic_AdminMessage_BackupLocation location);
bool restorePreferences(meshtastic_AdminMessage_BackupLocation location,
@@ -375,4 +373,4 @@ extern uint32_t error_address;
ModuleConfig_RangeTestConfig_size + ModuleConfig_SerialConfig_size + ModuleConfig_StoreForwardConfig_size + \
ModuleConfig_TelemetryConfig_size + ModuleConfig_size)
// Please do not remove this comment, it makes trunk and compiler happy at the same time.
// Please do not remove this comment, it makes trunk and compiler happy at the same time.

View File

@@ -288,12 +288,6 @@ typedef enum _meshtastic_HardwareModel {
meshtastic_HardwareModel_WISMESH_TAP_V2 = 116,
/* RAK3401 */
meshtastic_HardwareModel_RAK3401 = 117,
/* RAK6421 Hat+ */
meshtastic_HardwareModel_RAK6421 = 118,
/* Elecrow ThinkNode M4 */
meshtastic_HardwareModel_THINKNODE_M4 = 119,
/* Elecrow ThinkNode M6 */
meshtastic_HardwareModel_THINKNODE_M6 = 120,
/* ------------------------------------------------------------------------------------------------------------------------------------------
Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
------------------------------------------------------------------------------------------------------------------------------------------ */
@@ -831,11 +825,7 @@ typedef struct _meshtastic_MeshPacket {
Note: Our crypto implementation uses this field as well.
See [crypto](/docs/overview/encryption) for details. */
uint32_t from;
/* The (immediate) destination for this packet
If the value is 4,294,967,295 (maximum value of an unsigned 32bit integer), this indicates that the packet was
not destined for a specific node, but for a channel as indicated by the value of `channel` below.
If the value is another, this indicates that the packet was destined for a specific
node (i.e. a kind of "Direct Message" to this node) and not broadcast on a channel. */
/* The (immediate) destination for this packet */
uint32_t to;
/* (Usually) If set, this indicates the index in the secondary_channels table that this packet was sent/received on.
If unset, packet was on the primary channel.

View File

@@ -773,7 +773,6 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
config.lora = validatedLora;
// If we're setting region for the first time, init the region and regenerate the keys
if (isRegionUnset && config.lora.region > meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN || MESHTASTIC_EXCLUDE_PKI)
if (!owner.is_licensed) {
bool keygenSuccess = false;
if (config.security.private_key.size == 32) {
@@ -792,7 +791,6 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32);
}
}
#endif
config.lora.tx_enabled = true;
initRegion();
if (myRegion->dutyCycle < 100) {

View File

@@ -836,7 +836,6 @@ bool CannedMessageModule::handleFreeTextInput(const InputEvent *event)
if (event->inputEvent == INPUT_BROKER_BACK && this->freetext.length() > 0) {
payload = 0x08;
lastTouchMillis = millis();
requestFocus();
runOnce();
return true;
}
@@ -845,7 +844,6 @@ bool CannedMessageModule::handleFreeTextInput(const InputEvent *event)
if (event->inputEvent == INPUT_BROKER_LEFT) {
payload = INPUT_BROKER_LEFT;
lastTouchMillis = millis();
requestFocus();
runOnce();
return true;
}
@@ -853,7 +851,6 @@ bool CannedMessageModule::handleFreeTextInput(const InputEvent *event)
if (event->inputEvent == INPUT_BROKER_RIGHT) {
payload = INPUT_BROKER_RIGHT;
lastTouchMillis = millis();
requestFocus();
runOnce();
return true;
}

View File

@@ -314,10 +314,11 @@ void ExternalNotificationModule::stopNow()
audioThread->stop();
#endif
// Turn off all outputs
LOG_INFO("Turning off setExternalStates");
LOG_INFO("Turning off setExternalStates: ");
for (int i = 0; i < 3; i++) {
setExternalState(i, false);
externalTurnedOn[i] = 0;
LOG_INFO("%d ", i);
}
setIntervalFromNow(0);
#ifdef T_WATCH_S3

View File

@@ -13,8 +13,6 @@
#include "input/TrackballInterruptImpl1.h"
#endif
#include "modules/StatusLEDModule.h"
#if !MESHTASTIC_EXCLUDE_I2C
#include "input/cardKbI2cImpl.h"
#endif
@@ -121,10 +119,6 @@ void setupModules()
buzzerFeedbackThread = new BuzzerFeedbackThread();
}
#endif
#if defined(LED_CHARGE) || defined(LED_PAIRING)
statusLEDModule = new StatusLEDModule();
#endif
#if !MESHTASTIC_EXCLUDE_ADMIN
adminModule = new AdminModule();
#endif
@@ -181,13 +175,12 @@ void setupModules()
// new ReplyModule();
#if (HAS_BUTTON || ARCH_PORTDUINO) && !MESHTASTIC_EXCLUDE_INPUTBROKER
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
#ifndef T_LORA_PAGER
rotaryEncoderInterruptImpl1 = new RotaryEncoderInterruptImpl1();
if (!rotaryEncoderInterruptImpl1->init()) {
delete rotaryEncoderInterruptImpl1;
rotaryEncoderInterruptImpl1 = nullptr;
}
#elif defined(T_LORA_PAGER)
#ifdef T_LORA_PAGER
// use a special FSM based rotary encoder version for T-LoRa Pager
rotaryEncoderImpl = new RotaryEncoderImpl();
if (!rotaryEncoderImpl->init()) {

View File

@@ -34,8 +34,7 @@ void NeighborInfoModule::printNodeDBNeighbors()
}
}
/* Send our initial owner announcement 35 seconds after we start (to give
* network time to setup) */
/* Send our initial owner announcement 35 seconds after we start (to give network time to setup) */
NeighborInfoModule::NeighborInfoModule()
: ProtobufModule("neighborinfo", meshtastic_PortNum_NEIGHBORINFO_APP, &meshtastic_NeighborInfo_msg),
concurrency::OSThread("NeighborInfo")
@@ -54,8 +53,8 @@ NeighborInfoModule::NeighborInfoModule()
}
/*
Collect neighbor info from the nodeDB's history, capping at a maximum number of
entries and max time Assumes that the neighborInfo packet has been allocated
Collect neighbor info from the nodeDB's history, capping at a maximum number of entries and max time
Assumes that the neighborInfo packet has been allocated
@returns the number of entries collected
*/
uint32_t NeighborInfoModule::collectNeighborInfo(meshtastic_NeighborInfo *neighborInfo)
@@ -72,8 +71,8 @@ uint32_t NeighborInfoModule::collectNeighborInfo(meshtastic_NeighborInfo *neighb
if ((neighborInfo->neighbors_count < MAX_NUM_NEIGHBORS) && (nbr.node_id != my_node_id)) {
neighborInfo->neighbors[neighborInfo->neighbors_count].node_id = nbr.node_id;
neighborInfo->neighbors[neighborInfo->neighbors_count].snr = nbr.snr;
// Note: we don't set the last_rx_time and node_broadcast_intervals_secs
// here, because we don't want to send this over the mesh
// Note: we don't set the last_rx_time and node_broadcast_intervals_secs here, because we don't want to send this over
// the mesh
neighborInfo->neighbors_count++;
}
}
@@ -89,9 +88,8 @@ void NeighborInfoModule::cleanUpNeighbors()
uint32_t now = getTime();
NodeNum my_node_id = nodeDB->getNodeNum();
for (auto it = neighbors.rbegin(); it != neighbors.rend();) {
// We will remove a neighbor if we haven't heard from them in twice the
// broadcast interval cannot use isWithinTimespanMs() as it->last_rx_time is
// seconds since 1970
// We will remove a neighbor if we haven't heard from them in twice the broadcast interval
// cannot use isWithinTimespanMs() as it->last_rx_time is seconds since 1970
if ((now - it->last_rx_time > it->node_broadcast_interval_secs * 2) && (it->node_id != my_node_id)) {
LOG_DEBUG("Remove neighbor with node ID 0x%x", it->node_id);
it = std::vector<meshtastic_Neighbor>::reverse_iterator(
@@ -134,55 +132,25 @@ int32_t NeighborInfoModule::runOnce()
return Default::getConfiguredOrDefaultMs(moduleConfig.neighbor_info.update_interval, default_neighbor_info_broadcast_secs);
}
meshtastic_MeshPacket *NeighborInfoModule::allocReply()
{
LOG_INFO("NeighborInfoRequested.");
if (lastSentReply && Throttle::isWithinTimespanMs(lastSentReply, 3 * 60 * 1000)) {
LOG_DEBUG("Skip Neighbors reply since we sent a reply <3min ago");
ignoreRequest = true; // Mark it as ignored for MeshModule
return nullptr;
}
meshtastic_NeighborInfo neighborInfo = meshtastic_NeighborInfo_init_zero;
collectNeighborInfo(&neighborInfo);
meshtastic_MeshPacket *reply = allocDataProtobuf(neighborInfo);
if (reply) {
lastSentReply = millis(); // Track when we sent this reply
}
return reply;
}
/*
Collect a received neighbor info packet from another node
Pass it to an upper client; do not persist this data on the mesh
*/
bool NeighborInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_NeighborInfo *np)
{
LOG_DEBUG("NeighborInfo: handleReceivedProtobuf");
if (np) {
printNeighborInfo("RECEIVED", np);
// Ignore dummy/interceptable packets: single neighbor with nodeId 0 and snr 0
if (np->neighbors_count != 1 || np->neighbors[0].node_id != 0 || np->neighbors[0].snr != 0.0f) {
LOG_DEBUG(" Updating neighbours");
updateNeighbors(mp, np);
} else {
LOG_DEBUG(" Ignoring dummy neighbor info packet (single neighbor with nodeId 0, snr 0)");
}
updateNeighbors(mp, np);
} else if (mp.hop_start != 0 && mp.hop_start == mp.hop_limit) {
LOG_DEBUG("Get or create neighbor: %u with snr %f", mp.from, mp.rx_snr);
// If the hopLimit is the same as hopStart, then it is a neighbor
getOrCreateNeighbor(mp.from, mp.from, 0,
mp.rx_snr); // Set the broadcast interval to 0, as we don't know it
getOrCreateNeighbor(mp.from, mp.from, 0, mp.rx_snr); // Set the broadcast interval to 0, as we don't know it
}
// Allow others to handle this packet
return false;
}
/*
Copy the content of a current NeighborInfo packet into a new one and update the
last_sent_by_id to our NodeNum
Copy the content of a current NeighborInfo packet into a new one and update the last_sent_by_id to our NodeNum
*/
void NeighborInfoModule::alterReceivedProtobuf(meshtastic_MeshPacket &p, meshtastic_NeighborInfo *n)
{
@@ -200,10 +168,8 @@ void NeighborInfoModule::resetNeighbors()
void NeighborInfoModule::updateNeighbors(const meshtastic_MeshPacket &mp, const meshtastic_NeighborInfo *np)
{
LOG_DEBUG("updateNeighbors");
// The last sent ID will be 0 if the packet is from the phone, which we don't
// count as an edge. So we assume that if it's zero, then this packet is from
// our node.
// The last sent ID will be 0 if the packet is from the phone, which we don't count as
// an edge. So we assume that if it's zero, then this packet is from our node.
if (mp.which_payload_variant == meshtastic_MeshPacket_decoded_tag && mp.from) {
getOrCreateNeighbor(mp.from, np->last_sent_by_id, np->node_broadcast_interval_secs, mp.rx_snr);
}
@@ -222,8 +188,7 @@ meshtastic_Neighbor *NeighborInfoModule::getOrCreateNeighbor(NodeNum originalSen
// if found, update it
neighbors[i].snr = snr;
neighbors[i].last_rx_time = getTime();
// Only if this is the original sender, the broadcast interval corresponds
// to it
// Only if this is the original sender, the broadcast interval corresponds to it
if (originalSender == n && node_broadcast_interval_secs != 0)
neighbors[i].node_broadcast_interval_secs = node_broadcast_interval_secs;
return &neighbors[i];
@@ -235,12 +200,10 @@ meshtastic_Neighbor *NeighborInfoModule::getOrCreateNeighbor(NodeNum originalSen
new_nbr.node_id = n;
new_nbr.snr = snr;
new_nbr.last_rx_time = getTime();
// Only if this is the original sender, the broadcast interval corresponds to
// it
// Only if this is the original sender, the broadcast interval corresponds to it
if (originalSender == n && node_broadcast_interval_secs != 0)
new_nbr.node_broadcast_interval_secs = node_broadcast_interval_secs;
else // Assume the same broadcast interval as us for the neighbor if we don't
// know it
else // Assume the same broadcast interval as us for the neighbor if we don't know it
new_nbr.node_broadcast_interval_secs = moduleConfig.neighbor_info.update_interval;
if (neighbors.size() < MAX_NUM_NEIGHBORS) {

View File

@@ -28,10 +28,6 @@ class NeighborInfoModule : public ProtobufModule<meshtastic_NeighborInfo>, priva
*/
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_NeighborInfo *nb) override;
/* Messages can be received that have the want_response bit set. If set, this callback will be invoked
* so that subclasses can (optionally) send a response back to the original sender. */
virtual meshtastic_MeshPacket *allocReply() override;
/*
* Collect neighbor info from the nodeDB's history, capping at a maximum number of entries and max time
* @return the number of entries collected
@@ -70,8 +66,5 @@ class NeighborInfoModule : public ProtobufModule<meshtastic_NeighborInfo>, priva
/* These are for debugging only */
void printNeighborInfo(const char *header, const meshtastic_NeighborInfo *np);
void printNodeDBNeighbors();
private:
uint32_t lastSentReply = 0; // Last time we sent a position reply (used for reply throttling only)
};
extern NeighborInfoModule *neighborInfoModule;

View File

@@ -64,9 +64,7 @@ SerialModule *serialModule;
SerialModuleRadio *serialModuleRadio;
#if defined(TTGO_T_ECHO) || defined(CANARYONE) || defined(MESHLINK) || defined(ELECROW_ThinkNode_M1) || \
defined(ELECROW_ThinkNode_M4) || defined(ELECROW_ThinkNode_M5) || defined(HELTEC_MESH_SOLAR) || defined(T_ECHO_LITE) || \
defined(ELECROW_ThinkNode_M3) || defined(MUZI_BASE)
defined(ELECROW_ThinkNode_M5) || defined(HELTEC_MESH_SOLAR) || defined(T_ECHO_LITE)
SerialModule::SerialModule() : StreamAPI(&Serial), concurrency::OSThread("Serial")
{
api_type = TYPE_SERIAL;
@@ -206,9 +204,7 @@ int32_t SerialModule::runOnce()
Serial.setTimeout(moduleConfig.serial.timeout > 0 ? moduleConfig.serial.timeout : TIMEOUT);
}
#elif !defined(TTGO_T_ECHO) && !defined(T_ECHO_LITE) && !defined(CANARYONE) && !defined(MESHLINK) && \
!defined(ELECROW_ThinkNode_M1) && !defined(ELECROW_ThinkNode_M3) && !defined(ELECROW_ThinkNode_M4) && \
!defined(ELECROW_ThinkNode_M5) && !defined(MUZI_BASE)
!defined(ELECROW_ThinkNode_M1) && !defined(ELECROW_ThinkNode_M5)
if (moduleConfig.serial.rxd && moduleConfig.serial.txd) {
#ifdef ARCH_RP2040
Serial2.setFIFOSize(RX_BUFFER);
@@ -265,8 +261,7 @@ int32_t SerialModule::runOnce()
}
#if !defined(TTGO_T_ECHO) && !defined(T_ECHO_LITE) && !defined(CANARYONE) && !defined(MESHLINK) && \
!defined(ELECROW_ThinkNode_M1) && !defined(ELECROW_ThinkNode_M3) && !defined(ELECROW_ThinkNode_M4) && \
!defined(ELECROW_ThinkNode_M5) && !defined(MUZI_BASE)
!defined(ELECROW_ThinkNode_M1) && !defined(ELECROW_ThinkNode_M5)
else if ((moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_WS85)) {
processWXSerial();
@@ -541,9 +536,7 @@ ParsedLine parseLine(const char *line)
void SerialModule::processWXSerial()
{
#if !defined(TTGO_T_ECHO) && !defined(T_ECHO_LITE) && !defined(CANARYONE) && !defined(CONFIG_IDF_TARGET_ESP32C6) && \
!defined(MESHLINK) && !defined(ELECROW_ThinkNode_M1) && !defined(ELECROW_ThinkNode_M3) && !defined(ELECROW_ThinkNode_M4) && \
!defined(ELECROW_ThinkNode_M5) && !defined(ARCH_STM32WL) && !defined(MUZI_BASE)
!defined(MESHLINK) && !defined(ELECROW_ThinkNode_M1) && !defined(ELECROW_ThinkNode_M5) && !defined(ARCH_STM32WL)
static unsigned int lastAveraged = 0;
static unsigned int averageIntervalMillis = 300000; // 5 minutes hard coded.
static double dir_sum_sin = 0;

View File

@@ -1,130 +0,0 @@
#include "StatusLEDModule.h"
#include "MeshService.h"
#include "configuration.h"
#include <Arduino.h>
/*
StatusLEDModule manages the device's status LEDs, updating their states based on power and Bluetooth status.
It reflects charging, charged, discharging, and Bluetooth connection states using the appropriate LEDs.
*/
StatusLEDModule *statusLEDModule;
StatusLEDModule::StatusLEDModule() : concurrency::OSThread("StatusLEDModule")
{
bluetoothStatusObserver.observe(&bluetoothStatus->onNewStatus);
powerStatusObserver.observe(&powerStatus->onNewStatus);
if (inputBroker)
inputObserver.observe(inputBroker);
}
int StatusLEDModule::handleStatusUpdate(const meshtastic::Status *arg)
{
switch (arg->getStatusType()) {
case STATUS_TYPE_POWER: {
meshtastic::PowerStatus *_powerStatus = (meshtastic::PowerStatus *)arg;
if (_powerStatus->getHasUSB()) {
power_state = charging;
if (_powerStatus->getBatteryChargePercent() >= 100) {
power_state = charged;
}
} else {
power_state = discharging;
}
break;
}
case STATUS_TYPE_BLUETOOTH: {
meshtastic::BluetoothStatus *bluetoothStatus = (meshtastic::BluetoothStatus *)arg;
switch (bluetoothStatus->getConnectionState()) {
case meshtastic::BluetoothStatus::ConnectionState::DISCONNECTED: {
ble_state = unpaired;
PAIRING_LED_starttime = millis();
break;
}
case meshtastic::BluetoothStatus::ConnectionState::PAIRING: {
ble_state = pairing;
PAIRING_LED_starttime = millis();
break;
}
case meshtastic::BluetoothStatus::ConnectionState::CONNECTED: {
ble_state = connected;
PAIRING_LED_starttime = millis();
break;
}
}
break;
}
}
return 0;
};
int StatusLEDModule::handleInputEvent(const InputEvent *event)
{
lastUserbuttonTime = millis();
return 0;
}
int32_t StatusLEDModule::runOnce()
{
if (power_state == charging) {
CHARGE_LED_state = !CHARGE_LED_state;
} else if (power_state == charged) {
CHARGE_LED_state = LED_STATE_ON;
} else {
CHARGE_LED_state = LED_STATE_OFF;
}
if (!config.bluetooth.enabled || PAIRING_LED_starttime + 30 * 1000 < millis()) {
PAIRING_LED_state = LED_STATE_OFF;
} else if (ble_state == unpaired) {
if (slowTrack) {
PAIRING_LED_state = !PAIRING_LED_state;
slowTrack = false;
} else {
slowTrack = true;
}
} else if (ble_state == pairing) {
PAIRING_LED_state = !PAIRING_LED_state;
} else {
PAIRING_LED_state = LED_STATE_ON;
}
bool chargeIndicatorLED1 = LED_STATE_OFF;
bool chargeIndicatorLED2 = LED_STATE_OFF;
bool chargeIndicatorLED3 = LED_STATE_OFF;
bool chargeIndicatorLED4 = LED_STATE_OFF;
if (lastUserbuttonTime + 10 * 1000 > millis()) {
// should this be off at very low percentages?
chargeIndicatorLED1 = LED_STATE_ON;
if (powerStatus && powerStatus->getBatteryChargePercent() >= 25)
chargeIndicatorLED2 = LED_STATE_ON;
if (powerStatus && powerStatus->getBatteryChargePercent() >= 50)
chargeIndicatorLED3 = LED_STATE_ON;
if (powerStatus && powerStatus->getBatteryChargePercent() >= 75)
chargeIndicatorLED4 = LED_STATE_ON;
}
#ifdef LED_CHARGE
digitalWrite(LED_CHARGE, CHARGE_LED_state);
#endif
// digitalWrite(green_LED_PIN, LED_STATE_OFF);
#ifdef LED_PAIRING
digitalWrite(LED_PAIRING, PAIRING_LED_state);
#endif
#ifdef Battery_LED_1
digitalWrite(Battery_LED_1, chargeIndicatorLED1);
#endif
#ifdef Battery_LED_2
digitalWrite(Battery_LED_2, chargeIndicatorLED2);
#endif
#ifdef Battery_LED_3
digitalWrite(Battery_LED_3, chargeIndicatorLED3);
#endif
#ifdef Battery_LED_4
digitalWrite(Battery_LED_4, chargeIndicatorLED4);
#endif
return (my_interval);
}

View File

@@ -1,50 +0,0 @@
#pragma once
#include "BluetoothStatus.h"
#include "MeshModule.h"
#include "PowerStatus.h"
#include "concurrency/OSThread.h"
#include "configuration.h"
#include "input/InputBroker.h"
#include <Arduino.h>
#include <functional>
class StatusLEDModule : private concurrency::OSThread
{
bool slowTrack = false;
public:
StatusLEDModule();
int handleStatusUpdate(const meshtastic::Status *);
int handleInputEvent(const InputEvent *arg);
protected:
unsigned int my_interval = 1000; // interval in millisconds
virtual int32_t runOnce() override;
CallbackObserver<StatusLEDModule, const meshtastic::Status *> bluetoothStatusObserver =
CallbackObserver<StatusLEDModule, const meshtastic::Status *>(this, &StatusLEDModule::handleStatusUpdate);
CallbackObserver<StatusLEDModule, const meshtastic::Status *> powerStatusObserver =
CallbackObserver<StatusLEDModule, const meshtastic::Status *>(this, &StatusLEDModule::handleStatusUpdate);
CallbackObserver<StatusLEDModule, const InputEvent *> inputObserver =
CallbackObserver<StatusLEDModule, const InputEvent *>(this, &StatusLEDModule::handleInputEvent);
private:
bool CHARGE_LED_state = LED_STATE_OFF;
bool PAIRING_LED_state = LED_STATE_OFF;
uint32_t PAIRING_LED_starttime = 0;
uint32_t lastUserbuttonTime = 0;
enum PowerState { discharging, charging, charged };
PowerState power_state = discharging;
enum BLEState { unpaired, pairing, connected };
BLEState ble_state = unpaired;
};
extern StatusLEDModule *statusLEDModule;

View File

@@ -85,8 +85,10 @@ int SystemCommandsModule::handleInputEvent(const InputEvent *event)
switch (event->inputEvent) {
// GPS
case INPUT_BROKER_GPS_TOGGLE:
LOG_WARN("GPS Toggle");
#if !MESHTASTIC_EXCLUDE_GPS
if (gps) {
LOG_WARN("GPS Toggle2");
if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED &&
config.position.fixed_position == false) {
nodeDB->clearLocalPosition();

View File

@@ -53,7 +53,7 @@ extern void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const c
#include "Sensor/LTR390UVSensor.h"
#endif
#if __has_include(<bsec2.h>)
#if __has_include(MESHTASTIC_BME680_HEADER)
#include "Sensor/BME680Sensor.h"
#endif
@@ -214,7 +214,7 @@ void EnvironmentTelemetryModule::i2cScanFinished(ScanI2C *i2cScanner)
#if __has_include(<Adafruit_LTR390.h>)
addSensor<LTR390UVSensor>(i2cScanner, ScanI2C::DeviceType::LTR390UV);
#endif
#if __has_include(<bsec2.h>)
#if __has_include(MESHTASTIC_BME680_HEADER)
addSensor<BME680Sensor>(i2cScanner, ScanI2C::DeviceType::BME_680);
#endif
#if __has_include(<Adafruit_BMP280.h>)

View File

@@ -35,7 +35,7 @@ bool AHT10Sensor::getMetrics(meshtastic_Telemetry *measurement)
// prefer other sensors like bmp280, bmp3xx
if (!measurement->variant.environment_metrics.has_temperature) {
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.temperature = temp.temperature + AHT10_TEMP_OFFSET;
measurement->variant.environment_metrics.temperature = temp.temperature;
}
if (!measurement->variant.environment_metrics.has_relative_humidity) {

View File

@@ -6,10 +6,6 @@
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(<Adafruit_AHTX0.h>)
#ifndef AHT10_TEMP_OFFSET
#define AHT10_TEMP_OFFSET 0
#endif
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TelemetrySensor.h"
#include <Adafruit_AHTX0.h>

View File

@@ -1,6 +1,6 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(<bsec2.h>)
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(MESHTASTIC_BME680_HEADER)
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "BME680Sensor.h"
@@ -10,6 +10,7 @@
BME680Sensor::BME680Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BME680, "BME680") {}
#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
int32_t BME680Sensor::runOnce()
{
if (!bme680.run()) {
@@ -17,10 +18,13 @@ int32_t BME680Sensor::runOnce()
}
return 35;
}
#endif // defined(MESHTASTIC_BME680_BSEC2_SUPPORTED)
bool BME680Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
status = 0;
#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
if (!bme680.begin(dev->address.address, *bus))
checkStatus("begin");
@@ -42,12 +46,25 @@ bool BME680Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
if (status == 0)
LOG_DEBUG("BME680Sensor::runOnce: bme680.status %d", bme680.status);
#else
bme680 = makeBME680(bus);
if (!bme680->begin(dev->address.address)) {
LOG_ERROR("Init sensor: %s failed at begin()", sensorName);
return status;
}
status = 1;
#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
initI2CSensor();
return status;
}
bool BME680Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
if (bme680.getData(BSEC_OUTPUT_RAW_PRESSURE).signal == 0)
return false;
@@ -65,9 +82,27 @@ bool BME680Sensor::getMetrics(meshtastic_Telemetry *measurement)
// Check if we need to save state to filesystem (every STATE_SAVE_PERIOD ms)
measurement->variant.environment_metrics.iaq = bme680.getData(BSEC_OUTPUT_IAQ).signal;
updateState();
#else
if (!bme680->performReading()) {
LOG_ERROR("BME680Sensor::getMetrics: performReading failed");
return false;
}
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_relative_humidity = true;
measurement->variant.environment_metrics.has_barometric_pressure = true;
measurement->variant.environment_metrics.has_gas_resistance = true;
measurement->variant.environment_metrics.temperature = bme680->readTemperature();
measurement->variant.environment_metrics.relative_humidity = bme680->readHumidity();
measurement->variant.environment_metrics.barometric_pressure = bme680->readPressure() / 100.0F;
measurement->variant.environment_metrics.gas_resistance = bme680->readGas() / 1000.0;
#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
return true;
}
#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
void BME680Sensor::loadState()
{
#ifdef FSCom
@@ -144,5 +179,6 @@ void BME680Sensor::checkStatus(const char *functionName)
else if (bme680.sensor.status > BME68X_OK)
LOG_WARN("%s BME68X code: %d", functionName, bme680.sensor.status);
}
#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
#endif

View File

@@ -1,23 +1,40 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(<bsec2.h>)
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(MESHTASTIC_BME680_HEADER)
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TelemetrySensor.h"
#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
#include <bme68xLibrary.h>
#include <bsec2.h>
#else
#include <Adafruit_BME680.h>
#include <memory>
#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
#define STATE_SAVE_PERIOD UINT32_C(360 * 60 * 1000) // That's 6 hours worth of millis()
#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
const uint8_t bsec_config[] = {
#include "config/bme680/bme680_iaq_33v_3s_4d/bsec_iaq.txt"
};
#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
class BME680Sensor : public TelemetrySensor
{
private:
#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
Bsec2 bme680;
#else
using BME680Ptr = std::unique_ptr<Adafruit_BME680>;
static BME680Ptr makeBME680(TwoWire *bus) { return std::make_unique<Adafruit_BME680>(bus); }
BME680Ptr bme680;
#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
protected:
#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
const char *bsecConfigFileName = "/prefs/bsec.dat";
uint8_t bsecState[BSEC_MAX_STATE_BLOB_SIZE] = {0};
uint8_t accuracy = 0;
@@ -34,10 +51,13 @@ class BME680Sensor : public TelemetrySensor
void loadState();
void updateState();
void checkStatus(const char *functionName);
#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
public:
BME680Sensor();
#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
virtual int32_t runOnce() override;
#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
};

View File

@@ -13,10 +13,7 @@ DFRobotGravitySensor::DFRobotGravitySensor() : TelemetrySensor(meshtastic_Teleme
DFRobotGravitySensor::~DFRobotGravitySensor()
{
if (gravity) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"
delete gravity;
#pragma GCC diagnostic pop
gravity = nullptr;
}
}

View File

@@ -11,113 +11,6 @@ extern graphics::Screen *screen;
TraceRouteModule *traceRouteModule;
void TraceRouteModule::setResultText(const String &text)
{
resultText = text;
resultLines.clear();
resultLinesDirty = true;
}
void TraceRouteModule::clearResultLines()
{
resultLines.clear();
resultLinesDirty = false;
}
#if HAS_SCREEN
void TraceRouteModule::rebuildResultLines(OLEDDisplay *display)
{
if (!display) {
resultLinesDirty = false;
return;
}
resultLines.clear();
if (resultText.length() == 0) {
resultLinesDirty = false;
return;
}
int maxWidth = display->getWidth() - 4;
if (maxWidth <= 0) {
resultLinesDirty = false;
return;
}
int start = 0;
int textLength = resultText.length();
while (start <= textLength) {
int newlinePos = resultText.indexOf('\n', start);
String segment;
if (newlinePos != -1) {
segment = resultText.substring(start, newlinePos);
start = newlinePos + 1;
} else {
segment = resultText.substring(start);
start = textLength + 1;
}
if (segment.length() == 0) {
resultLines.push_back("");
continue;
}
if (display->getStringWidth(segment) <= maxWidth) {
resultLines.push_back(segment);
continue;
}
String remaining = segment;
while (remaining.length() > 0) {
String tempLine = "";
int lastGoodBreak = -1;
bool lineComplete = false;
for (int i = 0; i < static_cast<int>(remaining.length()); i++) {
char ch = remaining.charAt(i);
String testLine = tempLine + ch;
if (display->getStringWidth(testLine) > maxWidth) {
if (lastGoodBreak >= 0) {
resultLines.push_back(remaining.substring(0, lastGoodBreak + 1));
remaining = remaining.substring(lastGoodBreak + 1);
lineComplete = true;
break;
} else if (tempLine.length() > 0) {
resultLines.push_back(tempLine);
remaining = remaining.substring(i);
lineComplete = true;
break;
} else {
resultLines.push_back(String(ch));
remaining = remaining.substring(i + 1);
lineComplete = true;
break;
}
} else {
tempLine = testLine;
if (ch == ' ' || ch == '>' || ch == '<' || ch == '-' || ch == '(' || ch == ')' || ch == ',') {
lastGoodBreak = i;
}
}
}
if (!lineComplete) {
if (tempLine.length() > 0) {
resultLines.push_back(tempLine);
}
break;
}
}
}
resultLinesDirty = false;
}
#endif
bool TraceRouteModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_RouteDiscovery *r)
{
// We only alter the packet in alterReceivedProtobuf()
@@ -513,7 +406,7 @@ bool TraceRouteModule::startTraceRoute(NodeNum node)
if (node == 0 || node == NODENUM_BROADCAST) {
LOG_ERROR("Invalid node number for trace route: 0x%08x", node);
runState = TRACEROUTE_STATE_RESULT;
setResultText("Invalid node");
resultText = "Invalid node";
resultShowTime = millis();
tracingNode = 0;
@@ -527,7 +420,7 @@ bool TraceRouteModule::startTraceRoute(NodeNum node)
if (node == nodeDB->getNodeNum()) {
LOG_ERROR("Cannot trace route to self: 0x%08x", node);
runState = TRACEROUTE_STATE_RESULT;
setResultText("Cannot trace self");
resultText = "Cannot trace self";
resultShowTime = millis();
tracingNode = 0;
@@ -554,8 +447,6 @@ bool TraceRouteModule::startTraceRoute(NodeNum node)
unsigned long wait = (cooldownMs - (now - lastTraceRouteTime)) / 1000;
bannerText = String("Wait for ") + String(wait) + String("s");
runState = TRACEROUTE_STATE_COOLDOWN;
resultText = "";
clearResultLines();
requestFocus();
UIFrameEvent e;
@@ -568,8 +459,6 @@ bool TraceRouteModule::startTraceRoute(NodeNum node)
tracingNode = node;
lastTraceRouteTime = now;
runState = TRACEROUTE_STATE_TRACKING;
resultText = "";
clearResultLines();
bannerText = String("Tracing ") + getNodeName(node);
LOG_INFO("TraceRoute UI: Starting trace route to node 0x%08x, requesting focus", node);
@@ -612,7 +501,7 @@ bool TraceRouteModule::startTraceRoute(NodeNum node)
} else {
LOG_ERROR("MeshService is NULL!");
runState = TRACEROUTE_STATE_RESULT;
setResultText("Service unavailable");
resultText = "Service unavailable";
resultShowTime = millis();
tracingNode = 0;
@@ -625,7 +514,7 @@ bool TraceRouteModule::startTraceRoute(NodeNum node)
} else {
LOG_ERROR("Failed to allocate TraceRoute packet from router");
runState = TRACEROUTE_STATE_RESULT;
setResultText("Failed to send");
resultText = "Failed to send";
resultShowTime = millis();
tracingNode = 0;
@@ -643,7 +532,7 @@ void TraceRouteModule::launch(NodeNum node)
if (node == 0 || node == NODENUM_BROADCAST) {
LOG_ERROR("Invalid node number for trace route: 0x%08x", node);
runState = TRACEROUTE_STATE_RESULT;
setResultText("Invalid node");
resultText = "Invalid node";
resultShowTime = millis();
tracingNode = 0;
@@ -657,7 +546,7 @@ void TraceRouteModule::launch(NodeNum node)
if (node == nodeDB->getNodeNum()) {
LOG_ERROR("Cannot trace route to self: 0x%08x", node);
runState = TRACEROUTE_STATE_RESULT;
setResultText("Cannot trace self");
resultText = "Cannot trace self";
resultShowTime = millis();
tracingNode = 0;
@@ -679,8 +568,6 @@ void TraceRouteModule::launch(NodeNum node)
unsigned long wait = (cooldownMs - (now - lastTraceRouteTime)) / 1000;
bannerText = String("Wait for ") + String(wait) + String("s");
runState = TRACEROUTE_STATE_COOLDOWN;
resultText = "";
clearResultLines();
requestFocus();
UIFrameEvent e;
@@ -693,8 +580,6 @@ void TraceRouteModule::launch(NodeNum node)
runState = TRACEROUTE_STATE_TRACKING;
tracingNode = node;
lastTraceRouteTime = now;
resultText = "";
clearResultLines();
bannerText = String("Tracing ") + getNodeName(node);
requestFocus();
@@ -729,14 +614,14 @@ void TraceRouteModule::launch(NodeNum node)
} else {
LOG_ERROR("MeshService is NULL!");
runState = TRACEROUTE_STATE_RESULT;
setResultText("Service unavailable");
resultText = "Service unavailable";
resultShowTime = millis();
tracingNode = 0;
}
} else {
LOG_ERROR("Failed to allocate TraceRoute packet from router");
runState = TRACEROUTE_STATE_RESULT;
setResultText("Failed to send");
resultText = "Failed to send";
resultShowTime = millis();
tracingNode = 0;
}
@@ -744,7 +629,7 @@ void TraceRouteModule::launch(NodeNum node)
void TraceRouteModule::handleTraceRouteResult(const String &result)
{
setResultText(result);
resultText = result;
runState = TRACEROUTE_STATE_RESULT;
resultShowTime = millis();
tracingNode = 0;
@@ -794,15 +679,83 @@ void TraceRouteModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state
display->setFont(FONT_SMALL);
if (resultText.length() > 0) {
if (resultLinesDirty) {
rebuildResultLines(display);
std::vector<String> lines;
String currentLine = "";
int maxWidth = display->getWidth() - 4;
int start = 0;
int newlinePos = resultText.indexOf('\n', start);
while (newlinePos != -1 || start < static_cast<int>(resultText.length())) {
String segment;
if (newlinePos != -1) {
segment = resultText.substring(start, newlinePos);
start = newlinePos + 1;
newlinePos = resultText.indexOf('\n', start);
} else {
segment = resultText.substring(start);
start = resultText.length();
}
if (display->getStringWidth(segment) <= maxWidth) {
lines.push_back(segment);
} else {
// Try to break at better positions (space, >, <, -)
String remaining = segment;
while (remaining.length() > 0) {
String tempLine = "";
int lastGoodBreak = -1;
bool lineComplete = false;
for (int i = 0; i < static_cast<int>(remaining.length()); i++) {
char ch = remaining.charAt(i);
String testLine = tempLine + ch;
if (display->getStringWidth(testLine) > maxWidth) {
if (lastGoodBreak >= 0) {
// Break at the last good position
lines.push_back(remaining.substring(0, lastGoodBreak + 1));
remaining = remaining.substring(lastGoodBreak + 1);
lineComplete = true;
break;
} else if (tempLine.length() > 0) {
lines.push_back(tempLine);
remaining = remaining.substring(i);
lineComplete = true;
break;
} else {
// Single character exceeds width
lines.push_back(String(ch));
remaining = remaining.substring(i + 1);
lineComplete = true;
break;
}
} else {
tempLine = testLine;
// Mark good break positions
if (ch == ' ' || ch == '>' || ch == '<' || ch == '-' || ch == '(' || ch == ')') {
lastGoodBreak = i;
}
}
}
if (!lineComplete) {
// Reached end of remaining text
if (tempLine.length() > 0) {
lines.push_back(tempLine);
}
break;
}
}
}
}
int lineHeight = FONT_HEIGHT_SMALL + 1; // Use proper font height with 1px spacing
for (size_t i = 0; i < resultLines.size(); i++) {
for (size_t i = 0; i < lines.size(); i++) {
int lineY = contentStartY + (i * lineHeight);
if (lineY + FONT_HEIGHT_SMALL <= display->getHeight()) {
display->drawString(x + 2, lineY, resultLines[i]);
display->drawString(x + 2, lineY, lines[i]);
}
}
}
@@ -826,7 +779,7 @@ int32_t TraceRouteModule::runOnce()
if (runState == TRACEROUTE_STATE_TRACKING && now - lastTraceRouteTime > trackingTimeoutMs) {
LOG_INFO("TraceRoute timeout, no response received");
runState = TRACEROUTE_STATE_RESULT;
setResultText("No response received");
resultText = "No response received";
resultShowTime = now;
tracingNode = 0;
@@ -862,8 +815,6 @@ int32_t TraceRouteModule::runOnce()
// Cooldown finished
LOG_INFO("TraceRoute cooldown finished, returning to IDLE");
runState = TRACEROUTE_STATE_IDLE;
resultText = "";
clearResultLines();
bannerText = "";
UIFrameEvent e;
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET;
@@ -877,7 +828,6 @@ int32_t TraceRouteModule::runOnce()
LOG_INFO("TraceRoute result display timeout, returning to IDLE");
runState = TRACEROUTE_STATE_IDLE;
resultText = "";
clearResultLines();
bannerText = "";
tracingNode = 0;
UIFrameEvent e;

View File

@@ -7,7 +7,6 @@
#if HAS_SCREEN
#include "OLEDDisplayUi.h"
#endif
#include <vector>
#define ROUTE_SIZE sizeof(((meshtastic_RouteDiscovery *)0)->route) / sizeof(((meshtastic_RouteDiscovery *)0)->route[0])
@@ -50,11 +49,6 @@ class TraceRouteModule : public ProtobufModule<meshtastic_RouteDiscovery>,
virtual int32_t runOnce() override;
private:
void setResultText(const String &text);
void clearResultLines();
#if HAS_SCREEN
void rebuildResultLines(OLEDDisplay *display);
#endif
// Call to add unknown hops (e.g. when a node couldn't decrypt it) to the route based on hopStart and current hopLimit
void insertUnknownHops(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r, bool isTowardsDestination);
@@ -80,8 +74,6 @@ class TraceRouteModule : public ProtobufModule<meshtastic_RouteDiscovery>,
unsigned long trackingTimeoutMs = 10000;
String bannerText;
String resultText;
std::vector<String> resultLines;
bool resultLinesDirty = false;
NodeNum tracingNode = 0;
bool initialized = false;
};

View File

@@ -115,13 +115,7 @@ int32_t BMX160Sensor::runOnce()
void BMX160Sensor::calibrate(uint16_t forSeconds)
{
#if !defined(MESHTASTIC_EXCLUDE_SCREEN)
sBmx160SensorData_t magAccel;
sBmx160SensorData_t gAccel;
LOG_DEBUG("BMX160 calibration started for %is", forSeconds);
sensor.getAllData(&magAccel, NULL, &gAccel);
highestX = magAccel.x, lowestX = magAccel.x;
highestY = magAccel.y, lowestY = magAccel.y;
highestZ = magAccel.z, lowestZ = magAccel.z;
doCalibration = true;
uint16_t calibrateFor = forSeconds * 1000; // calibrate for seconds provided
@@ -133,4 +127,4 @@ void BMX160Sensor::calibrate(uint16_t forSeconds)
#endif
#endif
#endif

View File

@@ -47,21 +47,6 @@ int32_t ICM20948Sensor::runOnce()
int32_t ICM20948Sensor::runOnce()
{
#if !defined(MESHTASTIC_EXCLUDE_SCREEN) && HAS_SCREEN
#if defined(MUZI_BASE) // temporarily gated to single device due to feature freeze
if (screen && !screen->isScreenOn() && !config.display.wake_on_tap_or_motion && !config.device.double_tap_as_button_press) {
if (!isAsleep) {
LOG_DEBUG("sleeping IMU");
sensor->sleep(true);
isAsleep = true;
}
return MOTION_SENSOR_CHECK_INTERVAL_MS;
}
if (isAsleep) {
sensor->sleep(false);
isAsleep = false;
}
#endif
float magX = 0, magY = 0, magZ = 0;
if (sensor->dataReady()) {
sensor->getAGMT();
@@ -171,20 +156,7 @@ int32_t ICM20948Sensor::runOnce()
void ICM20948Sensor::calibrate(uint16_t forSeconds)
{
#if !defined(MESHTASTIC_EXCLUDE_SCREEN) && HAS_SCREEN
LOG_DEBUG("Old calibration data: highestX = %f, lowestX = %f, highestY = %f, lowestY = %f, highestZ = %f, lowestZ = %f",
highestX, lowestX, highestY, lowestY, highestZ, lowestZ);
LOG_DEBUG("BMX160 calibration started for %is", forSeconds);
if (sensor->dataReady()) {
sensor->getAGMT();
highestX = sensor->agmt.mag.axes.x;
lowestX = sensor->agmt.mag.axes.x;
highestY = sensor->agmt.mag.axes.y;
lowestY = sensor->agmt.mag.axes.y;
highestZ = sensor->agmt.mag.axes.z;
lowestZ = sensor->agmt.mag.axes.z;
} else {
highestX = 0, lowestX = 0, highestY = 0, lowestY = 0, highestZ = 0, lowestZ = 0;
}
doCalibration = true;
uint16_t calibrateFor = forSeconds * 1000; // calibrate for seconds provided
@@ -323,4 +295,4 @@ bool ICM20948Singleton::setWakeOnMotion()
return true;
}
#endif
#endif

View File

@@ -82,13 +82,7 @@ class ICM20948Sensor : public MotionSensor
private:
ICM20948Singleton *sensor = nullptr;
bool showingScreen = false;
#ifdef MUZI_BASE
bool isAsleep = false;
float highestX = 449.000000, lowestX = -140.000000, highestY = 422.000000, lowestY = -232.000000, highestZ = 749.000000,
lowestZ = 98.000000;
#else
float highestX = 0, lowestX = 0, highestY = 0, lowestY = 0, highestZ = 0, lowestZ = 0;
#endif
public:
explicit ICM20948Sensor(ScanI2C::FoundDevice foundDevice);

View File

@@ -69,8 +69,7 @@ void MotionSensor::wakeScreen()
{
if (powerFSM.getState() == &stateDARK) {
LOG_DEBUG("Motion wakeScreen detected");
if (config.display.wake_on_tap_or_motion)
powerFSM.trigger(EVENT_INPUT);
powerFSM.trigger(EVENT_INPUT);
}
}
@@ -88,4 +87,4 @@ void MotionSensor::buttonPress() {}
#endif
#endif
#endif

View File

@@ -51,7 +51,6 @@ constexpr int reconnectMax = 5;
static uint8_t bytes[meshtastic_MqttClientProxyMessage_size + 30]; // 12 for channel name and 16 for nodeid
static bool isMqttServerAddressPrivate = false;
static bool isConnected = false;
inline void onReceiveProto(char *topic, byte *payload, size_t length)
{
@@ -321,10 +320,8 @@ bool connectPubSub(const PubSubConfig &config, PubSubClient &pubSub, Client &cli
std::string nodeId = nodeDB->getNodeId();
const bool connected = pubSub.connect(nodeId.c_str(), config.mqttUsername, config.mqttPassword);
if (connected) {
isConnected = true;
LOG_INFO("MQTT connected");
} else {
isConnected = false;
LOG_WARN("Failed to connect to MQTT server");
}
return connected;
@@ -510,7 +507,6 @@ bool MQTT::publish(const char *topic, const uint8_t *payload, size_t length, boo
void MQTT::reconnect()
{
isConnected = false;
if (wantsLink()) {
if (moduleConfig.mqtt.proxy_to_client_enabled) {
LOG_INFO("MQTT connect via client proxy instead");
@@ -538,7 +534,7 @@ void MQTT::reconnect()
runASAP = true;
reconnectCount = 0;
isMqttServerAddressPrivate = isPrivateIpAddress(clientConnection->remoteIP());
isConnected = true;
publishNodeInfo();
sendSubscriptions();
} else {
@@ -695,9 +691,6 @@ void MQTT::publishQueuedMessages()
if (mqttQueue.isEmpty())
return;
if (!moduleConfig.mqtt.proxy_to_client_enabled && !isConnected)
return;
LOG_DEBUG("Publish enqueued MQTT message");
const std::unique_ptr<QueueEntry> entry(mqttQueue.dequeuePtr(0));
LOG_INFO("publish %s, %u bytes from queue", entry->topic.c_str(), entry->envBytes.size());
@@ -902,4 +895,4 @@ void MQTT::perhapsReportToMap()
// Update the last report time
last_report_to_map = millis();
}
}

View File

@@ -20,14 +20,13 @@
#include "PowerStatus.h"
#endif
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6)
#if defined(CONFIG_NIMBLE_CPP_IDF)
#include "host/ble_gap.h"
#else
#include "nimble/nimble/host/include/host/ble_gap.h"
#endif
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6)
namespace
{
constexpr uint16_t kPreferredBleMtu = 517;
@@ -777,35 +776,16 @@ bool NimbleBluetooth::isConnected()
int NimbleBluetooth::getRssi()
{
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6)
if (!bleServer || !isConnected()) {
return 0; // No active BLE connection
}
uint16_t connHandle = nimbleBluetoothConnHandle.load();
if (connHandle == BLE_HS_CONN_HANDLE_NONE) {
const auto peers = bleServer->getPeerDevices();
if (!peers.empty()) {
connHandle = peers.front();
nimbleBluetoothConnHandle = connHandle;
}
}
if (connHandle == BLE_HS_CONN_HANDLE_NONE) {
return 0; // Connection handle not available yet
}
int8_t rssi = 0;
const int rc = ble_gap_conn_rssi(connHandle, &rssi);
if (rc == 0) {
return rssi;
}
LOG_DEBUG("BLE RSSI read failed, rc=%d", rc);
if (bleServer && isConnected()) {
auto service = bleServer->getServiceByUUID(MESH_SERVICE_UUID);
uint16_t handle = service->getHandle();
#ifdef NIMBLE_TWO
return NimBLEDevice::getClientByHandle(handle)->getRssi();
#else
return NimBLEDevice::getClientByID(handle)->getRssi();
#endif
return 0;
}
return 0; // FIXME figure out where to source this
}
void NimbleBluetooth::setup()

View File

@@ -1,43 +0,0 @@
#include "configuration.h"
#ifdef HAS_CST226SE
#include "TouchDrvCSTXXX.hpp"
#include "input/TouchScreenImpl1.h"
#include <Wire.h>
TouchDrvCSTXXX tsPanel;
static constexpr uint8_t PossibleAddresses[2] = {CST328_ADDR, CST226SE_ADDR_ALT};
uint8_t i2cAddress = 0;
bool readTouch(int16_t *x, int16_t *y)
{
int16_t x_array[1], y_array[1];
uint8_t touched = tsPanel.getPoint(x_array, y_array, 1);
if (touched > 0) {
*y = x_array[0];
*x = (TFT_WIDTH - y_array[0]);
// Check bounds
if (*x < 0 || *x >= TFT_WIDTH || *y < 0 || *y >= TFT_HEIGHT) {
return false;
}
return true; // Valid touch detected
}
return false; // No valid touch data
}
void lateInitVariant()
{
tsPanel.setTouchDrvModel(TouchDrv_CST226);
for (uint8_t addr : PossibleAddresses) {
if (tsPanel.begin(Wire, addr, I2C_SDA, I2C_SCL)) {
i2cAddress = addr;
LOG_DEBUG("CST226SE init OK at address 0x%02X", addr);
touchScreenImpl1 = new TouchScreenImpl1(TFT_WIDTH, TFT_HEIGHT, readTouch);
touchScreenImpl1->init();
return;
}
}
LOG_ERROR("CST226SE init failed at all known addresses");
}
#endif

View File

@@ -57,23 +57,17 @@
#define HW_VENDOR meshtastic_HardwareModel_NOMADSTAR_METEOR_PRO
#elif defined(R1_NEO)
#define HW_VENDOR meshtastic_HardwareModel_MUZI_R1_NEO
#elif defined(RAK3401)
#define HW_VENDOR meshtastic_HardwareModel_RAK3401
// MAke sure all custom RAK4630 boards are defined before the generic RAK4630
#elif defined(RAK4630)
#define HW_VENDOR meshtastic_HardwareModel_RAK4631
#elif defined(RAK3401)
#define HW_VENDOR meshtastic_HardwareModel_RAK3401
#elif defined(TTGO_T_ECHO)
#define HW_VENDOR meshtastic_HardwareModel_T_ECHO
#elif defined(T_ECHO_LITE)
#define HW_VENDOR meshtastic_HardwareModel_T_ECHO_LITE
#elif defined(ELECROW_ThinkNode_M1)
#define HW_VENDOR meshtastic_HardwareModel_THINKNODE_M1
#elif defined(ELECROW_ThinkNode_M3)
#define HW_VENDOR meshtastic_HardwareModel_THINKNODE_M3
#elif defined(ELECROW_ThinkNode_M6)
#define HW_VENDOR meshtastic_HardwareModel_THINKNODE_M6
#elif defined(ELECROW_ThinkNode_M4)
#define HW_VENDOR meshtastic_HardwareModel_THINKNODE_M4
#elif defined(NANO_G2_ULTRA)
#define HW_VENDOR meshtastic_HardwareModel_NANO_G2_ULTRA
#elif defined(CANARYONE)
@@ -110,8 +104,6 @@
#define HW_VENDOR meshtastic_HardwareModel_SEEED_WIO_TRACKER_L1
#elif defined(HELTEC_MESH_SOLAR)
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_MESH_SOLAR
#elif defined(MUZI_BASE)
#define HW_VENDOR meshtastic_HardwareModel_RESERVED_FRIED_CHICKEN
#else
#define HW_VENDOR meshtastic_HardwareModel_NRF52_UNKNOWN
#endif
@@ -136,9 +128,7 @@
#endif
#ifdef PIN_LED1
#define LED_PIN PIN_LED1 // LED1 on nrf52840-DK
#endif
#ifdef PIN_BUTTON1
#define BUTTON_PIN PIN_BUTTON1

View File

@@ -4,14 +4,6 @@
#include <InternalFileSystem.h>
#include <SPI.h>
#include <Wire.h>
#define APP_WATCHDOG_SECS 90
#define NRFX_WDT_ENABLED 1
#define NRFX_WDT0_ENABLED 1
#define NRFX_WDT_CONFIG_NO_IRQ 1
#include <nrfx_wdt.c>
#include <nrfx_wdt.h>
#include <assert.h>
#include <ble_gap.h>
#include <memory.h>
@@ -22,22 +14,11 @@
#include "error.h"
#include "main.h"
#include "meshUtils.h"
#include "power.h"
#include <hal/nrf_lpcomp.h>
#ifdef BQ25703A_ADDR
#include "BQ25713.h"
#endif
// Weak empty variant initialization function.
// May be redefined by variant files.
void variant_shutdown() __attribute__((weak));
void variant_shutdown() {}
static nrfx_wdt_t nrfx_wdt = NRFX_WDT_INSTANCE(0);
static nrfx_wdt_channel_id nrfx_wdt_channel_id_nrf52_main;
static inline void debugger_break(void)
{
__asm volatile("bkpt #0x01\n\t"
@@ -221,15 +202,6 @@ void checkSDEvents()
void nrf52Loop()
{
{
static bool watchdog_running = false;
if (!watchdog_running) {
nrfx_wdt_enable(&nrfx_wdt);
watchdog_running = true;
}
}
nrfx_wdt_channel_feed(&nrfx_wdt, nrfx_wdt_channel_id_nrf52_main);
checkSDEvents();
reportLittleFSCorruptionOnce();
}
@@ -297,22 +269,6 @@ void nrf52Setup()
LOG_DEBUG("Set random seed %u", seed.seed32);
randomSeed(seed.seed32);
nRFCrypto.end();
// Set up nrfx watchdog. Do not enable the watchdog yet (we do that
// the first time through the main loop), so that other threads can
// allocate their own wdt channel to protect themselves from hangs.
nrfx_wdt_config_t wdt0_config = {
.behaviour = NRF_WDT_BEHAVIOUR_PAUSE_SLEEP_HALT, .reload_value = APP_WATCHDOG_SECS * 1000,
// Note: Not using wdt interrupts.
// .interrupt_priority = NRFX_WDT_DEFAULT_CONFIG_IRQ_PRIORITY
};
nrfx_err_t r = nrfx_wdt_init(&nrfx_wdt, &wdt0_config,
nullptr // Watchdog event handler, not used, we just reset.
);
assert(r == NRFX_SUCCESS);
r = nrfx_wdt_channel_alloc(&nrfx_wdt, &nrfx_wdt_channel_id_nrf52_main);
assert(r == NRFX_SUCCESS);
}
void cpuDeepSleep(uint32_t msecToWake)
@@ -396,7 +352,6 @@ void cpuDeepSleep(uint32_t msecToWake)
NRF_GPIO->DIRCLR = (1 << pin);
}
#endif
variant_shutdown();
// Sleepy trackers or sensors can low power "sleep"
// Don't enter this if we're sleeping portMAX_DELAY, since that's a shutdown event
@@ -434,23 +389,6 @@ void cpuDeepSleep(uint32_t msecToWake)
nrf_gpio_cfg_sense_set(BUTTON_PIN, sense); // Apply SENSE to wake up the device from the deep sleep
#endif
#ifdef BATTERY_LPCOMP_INPUT
// Wake up if power rises again
nrf_lpcomp_config_t c;
c.reference = BATTERY_LPCOMP_THRESHOLD;
c.detection = NRF_LPCOMP_DETECT_UP;
c.hyst = NRF_LPCOMP_HYST_NOHYST;
nrf_lpcomp_configure(NRF_LPCOMP, &c);
nrf_lpcomp_input_select(NRF_LPCOMP, BATTERY_LPCOMP_INPUT);
nrf_lpcomp_enable(NRF_LPCOMP);
battery_adcEnable();
nrf_lpcomp_task_trigger(NRF_LPCOMP, NRF_LPCOMP_TASK_START);
while (!nrf_lpcomp_event_check(NRF_LPCOMP, NRF_LPCOMP_EVENT_READY))
;
#endif
auto ok = sd_power_system_off();
if (ok != NRF_SUCCESS) {
LOG_ERROR("FIXME: Ignoring soft device (EasyDMA pending?) and forcing system-off!");
@@ -482,4 +420,4 @@ void enterDfuMode()
#else
enterUf2Dfu();
#endif
}
}

View File

@@ -146,20 +146,6 @@ void getMacAddr(uint8_t *dmac)
}
}
std::string cleanupNameForAutoconf(std::string name)
{
// Convert spaces -> dashes, lowercase
std::transform(name.begin(), name.end(), name.begin(), [](unsigned char c) {
if (c == ' ') {
return '-';
}
return (char)std::tolower(c);
});
return name;
}
/** apps run under portduino can optionally define a portduinoSetup() to
* use portduino specific init code (such as gpioBind) to setup portduino on their host machine,
* before running 'arduino' code.
@@ -232,11 +218,6 @@ void portduinoSetup()
// If LoRa `Module: auto` (default in config.yaml),
// attempt to auto config based on Product Strings
if (portduino_config.lora_module == use_autoconf) {
bool found_hat = false;
bool found_rak_eeprom = false;
bool found_ch341 = false;
char hat_vendor[96] = {0};
char autoconf_product[96] = {0};
// Try CH341
try {
@@ -246,32 +227,21 @@ void portduinoSetup()
ch341Hal->getProductString(autoconf_product, 95);
delete ch341Hal;
std::cout << "autoconf: Found CH341 device " << autoconf_product << std::endl;
found_ch341 = true;
} catch (...) {
std::cout << "autoconf: Could not locate CH341 device" << std::endl;
}
// Try Pi HAT+
if (strlen(autoconf_product) < 6) {
std::cout << "autoconf: Looking for Pi HAT+..." << std::endl;
if (access("/proc/device-tree/hat/vendor", R_OK) == 0) {
std::ifstream hatVendorFile("/proc/device-tree/hat/vendor");
if (hatVendorFile.is_open()) {
hatVendorFile.read(hat_vendor, 95);
hatVendorFile.close();
}
}
if (access("/proc/device-tree/hat/product", R_OK) == 0) {
std::ifstream hatProductFile("/proc/device-tree/hat/product");
if (hatProductFile.is_open()) {
hatProductFile.read(autoconf_product, 95);
hatProductFile.close();
}
std::cout << "autoconf: Found Pi HAT+ " << hat_vendor << " " << autoconf_product << " at /proc/device-tree/hat"
<< std::endl;
found_hat = true;
std::cout << "autoconf: Found Pi HAT+ " << autoconf_product << " at /proc/device-tree/hat/product" << std::endl;
} else {
std::cout << "autoconf: Could not locate Pi HAT+ at /proc/device-tree/hat" << std::endl;
std::cout << "autoconf: Could not locate Pi HAT+ at /proc/device-tree/hat/product" << std::endl;
}
}
// attempt to load autoconf data from an EEPROM on 0x50
@@ -327,7 +297,6 @@ void portduinoSetup()
autoconf_product[0] = 0x0;
} else {
std::cout << "autoconf: Found eeprom data " << autoconf_raw << std::endl;
found_rak_eeprom = true;
if (mac_start != nullptr) {
std::cout << "autoconf: Found mac data " << mac_start << std::endl;
if (strlen(mac_start) == 12)
@@ -356,29 +325,12 @@ void portduinoSetup()
if (strlen(autoconf_product) > 0) {
// From configProducts map in PortduinoGlue.h
std::string product_config = "";
if (configProducts.find(autoconf_product) != configProducts.end()) {
try {
product_config = configProducts.at(autoconf_product);
} else {
if (found_hat) {
product_config =
cleanupNameForAutoconf("lora-hat-" + std::string(hat_vendor) + "-" + autoconf_product + ".yaml");
} else if (found_ch341) {
product_config = cleanupNameForAutoconf("lora-usb-" + std::string(autoconf_product) + ".yaml");
}
// Don't try to automatically find config for a device with RAK eeprom.
if (found_rak_eeprom) {
std::cerr << "autoconf: Found unknown RAK product " << autoconf_product << std::endl;
exit(EXIT_FAILURE);
}
if (access((portduino_config.available_directory + product_config).c_str(), R_OK) != 0) {
std::cerr << "autoconf: Unable to find config for " << autoconf_product << "(tried " << product_config << ")"
<< std::endl;
exit(EXIT_FAILURE);
}
} catch (std::out_of_range &e) {
std::cerr << "autoconf: Unable to find config for " << autoconf_product << std::endl;
exit(EXIT_FAILURE);
}
if (loadConfig((portduino_config.available_directory + product_config).c_str())) {
std::cout << "autoconf: Using " << product_config << " as config file for " << autoconf_product << std::endl;
} else {

View File

@@ -34,8 +34,8 @@
#define OCV_ARRAY 4200, 3876, 3826, 3763, 3713, 3660, 3573, 3485, 3422, 3359, 3300
#elif defined(SEEED_SOLAR_NODE)
#define OCV_ARRAY 4200, 3986, 3922, 3812, 3734, 3645, 3527, 3420, 3281, 3087, 2786
#elif defined(WISMESH_TAG)
#define OCV_ARRAY 4240, 4112, 4029, 3970, 3906, 3846, 3824, 3802, 3776, 3650, 3072
#elif defined(R1_NEO)
#define OCV_ARRAY 4330, 4292, 4254, 4216, 4178, 4140, 4102, 4064, 4026, 3988, 3950
#else // LiIon
#define OCV_ARRAY 4190, 4050, 3990, 3890, 3800, 3720, 3630, 3530, 3420, 3300, 3100
#endif
@@ -132,20 +132,16 @@ class Power : private concurrency::OSThread
bool lipoChargerInit();
/// Setup a meshSolar battery sensor
bool meshSolarInit();
/// Setup a serial battery sensor
bool serialBatteryInit();
private:
void shutdown();
void reboot();
// open circuit voltage lookup table
uint8_t low_voltage_counter;
uint32_t lastLogTime = 0;
int32_t lastLogTime = 0;
#ifdef DEBUG_HEAP
uint32_t lastheap;
#endif
};
void battery_adcEnable();
extern Power *power;

View File

@@ -4,22 +4,12 @@ extends = esp32_base
board = ttgo-t-beam
board_level = pr
board_check = true
lib_deps = ${esp32_base.lib_deps}
build_flags = ${esp32_base.build_flags}
lib_deps =
${esp32_base.lib_deps}
build_flags =
${esp32_base.build_flags}
-D TBEAM_V10
-I variants/esp32/tbeam
-DBOARD_HAS_PSRAM
-mfix-esp32-psram-cache-issue
upload_speed = 921600
[env:tbeam-displayshield]
extends = env:tbeam
build_flags =
${env:tbeam.build_flags}
-D USE_ST7796
lib_deps =
${env:tbeam.lib_deps}
https://github.com/meshtastic/st7796/archive/refs/tags/1.0.5.zip ; display addon
lewisxhe/SensorLib@0.3.1 ; touchscreen addon

View File

@@ -42,35 +42,4 @@
#define GPS_UBLOX
#define GPS_RX_PIN 34
#define GPS_TX_PIN 12
// #define GPS_DEBUG
// Used when the display shield is chosen
#ifdef USE_ST7796
#undef EXT_NOTIFY_OUT
#undef LED_STATE_ON
#undef LED_PIN
#define HAS_CST226SE 1
#define HAS_TOUCHSCREEN 1
// #define TOUCH_IRQ 35 // broken in this version of the lib 0.3.1
#ifndef TOUCH_IRQ
#define TOUCH_IRQ -1
#endif
#define CANNED_MESSAGE_MODULE_ENABLE 1
#define USE_VIRTUAL_KEYBOARD 1
#define ST7796_NSS 25
#define ST7796_RS 13 // DC
#define ST7796_SDA 14 // MOSI
#define ST7796_SCK 15
#define ST7796_RESET 2
#define ST7796_MISO -1
#define ST7796_BUSY -1
#define VTFT_LEDA 4
#define TFT_SPI_FREQUENCY 60000000
#define TFT_HEIGHT 222
#define TFT_WIDTH 480
#define BRIGHTNESS_DEFAULT 100 // Medium Low Brightness
#define SCREEN_TRANSITION_FRAMERATE 5 // fps
#endif
// #define GPS_DEBUG

View File

@@ -1,17 +0,0 @@
[env:thinknode_m3]
extends = nrf52840_base
board = ThinkNode-M3
board_check = true
debug_tool = jlink
build_flags =
${nrf52840_base.build_flags}
-Ivariants/nrf52840/ELECROW-ThinkNode-M3
-DELECROW_ThinkNode_M3
-DGPS_POWER_TOGGLE
-D CONFIG_NFCT_PINS_AS_GPIOS=1
-L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard"
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/ELECROW-ThinkNode-M3>
lib_deps =
${nrf52840_base.lib_deps}
khoih-prog/nRF52_PWM@^1.0.1
lewisxhe/PCF8563_Library@^1.0.1

View File

@@ -1,15 +0,0 @@
#include "RadioLib.h"
#include "nrf.h"
// set RF switch configuration for ELECROW ThinkNode M3
// ELECROW ThinkNode M3 uses DIO5 and DIO6 for RF switching
static const uint32_t rfswitch_dio_pins[] = {RADIOLIB_LR11X0_DIO5, RADIOLIB_LR11X0_DIO6, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC};
static const Module::RfSwitchMode_t rfswitch_table[] = {
// mode DIO5 DIO6
{LR11x0::MODE_STBY, {LOW, LOW}}, {LR11x0::MODE_RX, {HIGH, LOW}},
{LR11x0::MODE_TX, {HIGH, HIGH}}, {LR11x0::MODE_TX_HP, {LOW, HIGH}},
{LR11x0::MODE_TX_HF, {LOW, LOW}}, {LR11x0::MODE_GNSS, {LOW, LOW}},
{LR11x0::MODE_WIFI, {LOW, LOW}}, END_OF_MODE_TABLE,
};

View File

@@ -1,93 +0,0 @@
/*
Copyright (c) 2014-2015 Arduino LLC. All right reserved.
Copyright (c) 2016 Sandeep Mistry All right reserved.
Copyright (c) 2018, Adafruit Industries (adafruit.com)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "variant.h"
#include "meshUtils.h"
#include "nrf.h"
#include "wiring_constants.h"
#include "wiring_digital.h"
const uint32_t g_ADigitalPinMap[] = {
// P0
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
// P1
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
void initVariant()
{
pinMode(KEY_POWER, OUTPUT);
digitalWrite(KEY_POWER, HIGH);
pinMode(RGB_POWER, OUTPUT);
digitalWrite(RGB_POWER, HIGH);
pinMode(green_LED_PIN, OUTPUT);
digitalWrite(green_LED_PIN, LED_STATE_OFF);
pinMode(LED_BLUE, OUTPUT);
pinMode(PIN_POWER_USB, INPUT);
pinMode(PIN_POWER_DONE, INPUT);
pinMode(PIN_POWER_CHRG, INPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(EEPROM_POWER, OUTPUT);
digitalWrite(EEPROM_POWER, HIGH);
pinMode(PIN_EN1, OUTPUT);
digitalWrite(PIN_EN1, HIGH);
pinMode(PIN_EN2, OUTPUT);
digitalWrite(PIN_EN2, HIGH);
pinMode(ACC_POWER, OUTPUT);
digitalWrite(ACC_POWER, LOW);
pinMode(DHT_POWER, OUTPUT);
digitalWrite(DHT_POWER, HIGH);
pinMode(Battery_POWER, OUTPUT);
digitalWrite(Battery_POWER, HIGH);
pinMode(GPS_POWER, OUTPUT);
digitalWrite(GPS_POWER, HIGH);
}
// called from main-nrf52.cpp during the cpuDeepSleep() function
void variant_shutdown()
{
digitalWrite(EEPROM_POWER, LOW);
digitalWrite(KEY_POWER, LOW);
for (int pin = 0; pin < 48; pin++) {
if (pin == PIN_POWER_USB || pin == BUTTON_PIN || pin == PIN_EN1 || pin == PIN_EN2 || pin == DHT_POWER ||
pin == ACC_POWER || pin == Battery_POWER || pin == GPS_POWER || pin == LR1110_SPI_MISO_PIN ||
pin == LR1110_SPI_MOSI_PIN || pin == LR1110_SPI_SCK_PIN || pin == LR1110_SPI_NSS_PIN || pin == LR1110_BUSY_PIN ||
pin == LR1110_NRESET_PIN || pin == LR1110_IRQ_PIN || pin == GPS_TX_PIN || pin == GPS_RX_PIN || pin == green_LED_PIN ||
pin == red_LED_PIN || pin == LED_BLUE) {
continue;
}
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
if (pin >= 32) {
NRF_P1->DIRCLR = (1 << (pin - 32));
} else {
NRF_GPIO->DIRCLR = (1 << pin);
}
}
nrf_gpio_cfg_input(BUTTON_PIN, NRF_GPIO_PIN_PULLUP); // Configure the pin to be woken up as an input
nrf_gpio_pin_sense_t sense1 = NRF_GPIO_PIN_SENSE_LOW;
nrf_gpio_cfg_sense_set(BUTTON_PIN, sense1);
nrf_gpio_cfg_input(PIN_POWER_USB, NRF_GPIO_PIN_PULLDOWN); // Configure the pin to be woken up as an input
nrf_gpio_pin_sense_t sense2 = NRF_GPIO_PIN_SENSE_HIGH;
nrf_gpio_cfg_sense_set(PIN_POWER_USB, sense2);
}

View File

@@ -1,122 +0,0 @@
/*
Copyright (c) 2014-2015 Arduino LLC. All right reserved.
Copyright (c) 2016 Sandeep Mistry All right reserved.
Copyright (c) 2018, Adafruit Industries (adafruit.com)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _VARIANT_ELECROW_EINK_V1_0_
#define _VARIANT_ELECROW_EINK_V1_0_
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#include "WVariant.h"
#define VARIANT_MCK (64000000ul)
#define USE_LFXO // Board uses 32khz crystal for LF
#define ELECROW_ThinkNode_M3 1
// Number of pins defined in PinDescription array
#define PINS_COUNT (48)
#define NUM_DIGITAL_PINS (48)
#define NUM_ANALOG_INPUTS (1)
#define NUM_ANALOG_OUTPUTS (0)
// Power Pin
#define NRF_APM
#define GPS_POWER 14
#define PIN_POWER_USB 31
#define EXT_PWR_DETECT PIN_POWER_USB
#define PIN_POWER_DONE 24
#define PIN_POWER_CHRG 32
#define KEY_POWER 16
#define ACC_POWER 2
#define DHT_POWER 3
#define Battery_POWER 17
#define RGB_POWER 29
#define EEPROM_POWER 7
// LED
#define red_LED_PIN 33
#define LED_POWER red_LED_PIN
#define LED_CHARGE LED_POWER // Signals the Status LED Module to handle this LED
#define green_LED_PIN 35
#define LED_BLUE 37
#define LED_PAIRING LED_BLUE // Signals the Status LED Module to handle this LED
#define LED_BUILTIN -1
#define LED_STATE_ON LOW
#define LED_STATE_OFF HIGH
// BUZZER
#define PIN_BUZZER 23
#define PIN_EN1 36
#define PIN_EN2 34
/*Wire Interfaces*/
#define WIRE_INTERFACES_COUNT 1
#define PIN_WIRE_SDA 26
#define PIN_WIRE_SCL 27
// Temperature correction for sensor
#define AHT10_TEMP_OFFSET -5.0
/*GPS pins*/
#define HAS_GPS 1
#define GPS_BAUDRATE 9600
#define PIN_GPS_RESET 25
#define PIN_GPS_STANDBY 21
#define GPS_TX_PIN 20
#define GPS_RX_PIN 22
#define GPS_THREAD_INTERVAL 50
#define PIN_SERIAL1_RX GPS_TX_PIN
#define PIN_SERIAL1_TX GPS_RX_PIN
// Button
#define BUTTON_PIN 12
#define BUTTON_PIN_ALT (0 + 12)
// Battery
#define BATTERY_PIN 5
#define BATTERY_SENSE_RESOLUTION_BITS 12
#define BATTERY_SENSE_RESOLUTION 4096.0
#undef AREF_VOLTAGE
#define AREF_VOLTAGE 2.4
#define VBAT_AR_INTERNAL AR_INTERNAL_2_4
#define ADC_MULTIPLIER (1.75)
/*SPI Interfaces*/
#define SPI_INTERFACES_COUNT 1
#define PIN_SPI_MISO (32 + 15) // P1.15 47
#define PIN_SPI_MOSI (32 + 14) // P1.14 46
#define PIN_SPI_SCK (32 + 13) // P1.13 45
#define PIN_SPI_NSS (32 + 12) // P1.12 44
/*LORA Interfaces*/
#define USE_LR1110
#define LR1110_IRQ_PIN 40
#define LR1110_NRESET_PIN 42
#define LR1110_BUSY_PIN 43
#define LR1110_SPI_NSS_PIN 44
#define LR1110_SPI_SCK_PIN 45
#define LR1110_SPI_MOSI_PIN 46
#define LR1110_SPI_MISO_PIN 47
#define LR11X0_DIO3_TCXO_VOLTAGE 3.3
#define LR11X0_DIO_AS_RF_SWITCH
// PCF8563 RTC Module
#define PCF8563_RTC 0x51
#ifdef __cplusplus
}
#endif
#endif

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