mirror of
https://github.com/meshtastic/firmware.git
synced 2025-12-21 10:12:50 +00:00
Compare commits
53 Commits
jp-bennett
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8fdba1f1e2 | ||
|
|
db64a5b51e | ||
|
|
3371d3372c | ||
|
|
d97e38bafc | ||
|
|
cadf151826 | ||
|
|
4fe1c87e54 | ||
|
|
1021d967da | ||
|
|
4f94354f60 | ||
|
|
6a93cb7b69 | ||
|
|
b11f292cc4 | ||
|
|
217abc4c10 | ||
|
|
e6af68bd14 | ||
|
|
530f0135ee | ||
|
|
208a873c4c | ||
|
|
f57eb6f27d | ||
|
|
155cdf9f9d | ||
|
|
661f49ad7a | ||
|
|
31e55d0b66 | ||
|
|
ee6449746b | ||
|
|
85aba3a4f7 | ||
|
|
5262233b2d | ||
|
|
e9db03d185 | ||
|
|
176d8def48 | ||
|
|
5b299f3ede | ||
|
|
96c42229b0 | ||
|
|
40f1f91c0d | ||
|
|
269dee7a2d | ||
|
|
f1aefc4eef | ||
|
|
203826374c | ||
|
|
8e0547e76d | ||
|
|
8a48321555 | ||
|
|
917794ebab | ||
|
|
ed77ba5612 | ||
|
|
eafa8c7b47 | ||
|
|
aa8bb6c6f1 | ||
|
|
1952982896 | ||
|
|
024ac74f5c | ||
|
|
de2b9632bb | ||
|
|
c2b7dc2641 | ||
|
|
d0d375f1ff | ||
|
|
e8ebfc0513 | ||
|
|
b74238194b | ||
|
|
5d5819b876 | ||
|
|
f127702bef | ||
|
|
cce8cbfe34 | ||
|
|
a4a6c3509a | ||
|
|
c8628b3422 | ||
|
|
2ac74d6677 | ||
|
|
9d487ddc0d | ||
|
|
4ef943f204 | ||
|
|
a8fa5f25cb | ||
|
|
3b2a1547de | ||
|
|
6f725a1996 |
4
.github/actions/build-variant/action.yml
vendored
4
.github/actions/build-variant/action.yml
vendored
@@ -76,7 +76,7 @@ runs:
|
||||
done
|
||||
|
||||
- name: PlatformIO ${{ inputs.arch }} download cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ~/.platformio/.cache
|
||||
key: pio-cache-${{ inputs.arch }}-${{ hashFiles('.github/actions/**', '**.ini') }}
|
||||
@@ -100,7 +100,7 @@ runs:
|
||||
id: version
|
||||
|
||||
- name: Store binaries as an artifact
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: firmware-${{ inputs.arch }}-${{ inputs.board }}-${{ steps.version.outputs.long }}
|
||||
overwrite: true
|
||||
|
||||
2
.github/workflows/build_debian_src.yml
vendored
2
.github/workflows/build_debian_src.yml
vendored
@@ -64,7 +64,7 @@ jobs:
|
||||
PKG_VERSION: ${{ steps.version.outputs.deb }}
|
||||
|
||||
- name: Store binaries as an artifact
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
|
||||
overwrite: true
|
||||
|
||||
15
.github/workflows/build_firmware.yml
vendored
15
.github/workflows/build_firmware.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
# Use 'arctastic' self-hosted runner pool when building in the main repo
|
||||
runs-on: ${{ github.repository_owner == 'meshtastic' && 'arctastic' || 'ubuntu-latest' }}
|
||||
outputs:
|
||||
artifact-id: ${{ steps.upload.outputs.artifact-id }}
|
||||
artifact-id: ${{ steps.upload-firmware.outputs.artifact-id }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
@@ -70,8 +70,8 @@ jobs:
|
||||
echo "</details>" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Store binaries as an artifact
|
||||
uses: actions/upload-artifact@v5
|
||||
id: upload
|
||||
uses: actions/upload-artifact@v6
|
||||
id: upload-firmware
|
||||
with:
|
||||
name: firmware-${{ inputs.platform }}-${{ inputs.pio_env }}-${{ inputs.version }}
|
||||
overwrite: true
|
||||
@@ -84,3 +84,12 @@ jobs:
|
||||
release/*.zip
|
||||
release/device-*.sh
|
||||
release/device-*.bat
|
||||
|
||||
- name: Store manifests as an artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
id: upload-manifest
|
||||
with:
|
||||
name: manifest-${{ inputs.platform }}-${{ inputs.pio_env }}-${{ inputs.version }}
|
||||
overwrite: true
|
||||
path: |
|
||||
release/*.mt.json
|
||||
|
||||
8
.github/workflows/build_one_target.yml
vendored
8
.github/workflows/build_one_target.yml
vendored
@@ -98,7 +98,7 @@ jobs:
|
||||
ref: ${{github.event.pull_request.head.ref}}
|
||||
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||
|
||||
- uses: actions/download-artifact@v6
|
||||
- uses: actions/download-artifact@v7
|
||||
with:
|
||||
path: ./
|
||||
pattern: firmware-*-*
|
||||
@@ -111,7 +111,7 @@ jobs:
|
||||
run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat
|
||||
|
||||
- name: Repackage in single firmware zip
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: firmware-${{inputs.target}}-${{ needs.version.outputs.long }}
|
||||
overwrite: true
|
||||
@@ -127,7 +127,7 @@ jobs:
|
||||
./Meshtastic_nRF52_factory_erase*.uf2
|
||||
retention-days: 30
|
||||
|
||||
- uses: actions/download-artifact@v6
|
||||
- uses: actions/download-artifact@v7
|
||||
with:
|
||||
pattern: firmware-*-${{ needs.version.outputs.long }}
|
||||
merge-multiple: true
|
||||
@@ -146,7 +146,7 @@ jobs:
|
||||
run: zip -j -9 -r ./firmware-${{inputs.target}}-${{ needs.version.outputs.long }}.zip ./output
|
||||
|
||||
- name: Repackage in single elfs zip
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: debug-elfs-${{inputs.target}}-${{ needs.version.outputs.long }}.zip
|
||||
overwrite: true
|
||||
|
||||
56
.github/workflows/main_matrix.yml
vendored
56
.github/workflows/main_matrix.yml
vendored
@@ -173,7 +173,7 @@ jobs:
|
||||
ref: ${{github.event.pull_request.head.ref}}
|
||||
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||
|
||||
- uses: actions/download-artifact@v6
|
||||
- uses: actions/download-artifact@v7
|
||||
with:
|
||||
path: ./
|
||||
pattern: firmware-${{matrix.arch}}-*
|
||||
@@ -183,7 +183,7 @@ jobs:
|
||||
run: ls -R
|
||||
|
||||
- name: Repackage in single firmware zip
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||
overwrite: true
|
||||
@@ -200,7 +200,7 @@ jobs:
|
||||
./Meshtastic_nRF52_factory_erase*.uf2
|
||||
retention-days: 30
|
||||
|
||||
- uses: actions/download-artifact@v6
|
||||
- uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||
merge-multiple: true
|
||||
@@ -219,7 +219,7 @@ jobs:
|
||||
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
||||
|
||||
- name: Repackage in single elfs zip
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||
overwrite: true
|
||||
@@ -233,6 +233,40 @@ jobs:
|
||||
description: "Download firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip. This artifact will be available for 90 days from creation"
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
shame:
|
||||
if: github.repository == 'meshtastic/firmware'
|
||||
continue-on-error: true
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
filter: blob:none # means we download all the git history but none of the commit (except ones with checkout like the head)
|
||||
fetch-depth: 0
|
||||
- name: Download the current manifests
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
path: ./manifests-new/
|
||||
pattern: manifest-*
|
||||
merge-multiple: true
|
||||
- name: Upload combined manifests for later commit and global stats crunching.
|
||||
uses: actions/upload-artifact@v6
|
||||
id: upload-manifest
|
||||
with:
|
||||
name: manifests-all
|
||||
overwrite: true
|
||||
path: |
|
||||
manifests-new/*.mt.json
|
||||
- name: Find the merge base
|
||||
run: echo "MERGE_BASE=$(git merge-base "origin/$base" "$head")" >> $GITHUB_ENV
|
||||
env:
|
||||
base: ${{ github.base_ref }}
|
||||
head: ${{ github.head_ref }}
|
||||
- name: Download the old manifests
|
||||
run: gh run download -R ${{ github.repository }} --commit ${{ env.MERGE_BASE }} --name manifests-all --dir manifest-old/
|
||||
- name: Do scan and post comment
|
||||
run: python3 bin/shame.py ${{ github.event.pull_request.number }} manifests-old/ manifests-new/
|
||||
|
||||
release-artifacts:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && github.repository == 'meshtastic/firmware' }}
|
||||
@@ -260,14 +294,14 @@ jobs:
|
||||
Autogenerated by github action, developer should edit as required before publishing...
|
||||
|
||||
- name: Download source deb
|
||||
uses: actions/download-artifact@v6
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
pattern: firmware-debian-${{ needs.version.outputs.deb }}~UNRELEASED-src
|
||||
merge-multiple: true
|
||||
path: ./output/debian-src
|
||||
|
||||
- name: Download `native-tft` pio deps
|
||||
uses: actions/download-artifact@v6
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }}
|
||||
merge-multiple: true
|
||||
@@ -291,7 +325,7 @@ jobs:
|
||||
}' > firmware-${{ needs.version.outputs.long }}.json
|
||||
|
||||
- name: Save Release manifest artifact
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: manifest-${{ needs.version.outputs.long }}
|
||||
overwrite: true
|
||||
@@ -332,7 +366,7 @@ jobs:
|
||||
with:
|
||||
python-version: 3.x
|
||||
|
||||
- uses: actions/download-artifact@v6
|
||||
- uses: actions/download-artifact@v7
|
||||
with:
|
||||
pattern: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||
merge-multiple: true
|
||||
@@ -349,7 +383,7 @@ jobs:
|
||||
- name: Zip firmware
|
||||
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
||||
|
||||
- uses: actions/download-artifact@v6
|
||||
- uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||
merge-multiple: true
|
||||
@@ -388,14 +422,14 @@ jobs:
|
||||
python-version: 3.x
|
||||
|
||||
- name: Get firmware artifacts
|
||||
uses: actions/download-artifact@v6
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
|
||||
merge-multiple: true
|
||||
path: ./publish
|
||||
|
||||
- name: Get manifest artifact
|
||||
uses: actions/download-artifact@v6
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
pattern: manifest-${{ needs.version.outputs.long }}
|
||||
path: ./publish
|
||||
|
||||
18
.github/workflows/merge_queue.yml
vendored
18
.github/workflows/merge_queue.yml
vendored
@@ -147,7 +147,7 @@ jobs:
|
||||
ref: ${{github.event.pull_request.head.ref}}
|
||||
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||
|
||||
- uses: actions/download-artifact@v6
|
||||
- uses: actions/download-artifact@v7
|
||||
with:
|
||||
path: ./
|
||||
pattern: firmware-${{matrix.arch}}-*
|
||||
@@ -160,7 +160,7 @@ jobs:
|
||||
run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat
|
||||
|
||||
- name: Repackage in single firmware zip
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||
overwrite: true
|
||||
@@ -176,7 +176,7 @@ jobs:
|
||||
./Meshtastic_nRF52_factory_erase*.uf2
|
||||
retention-days: 30
|
||||
|
||||
- uses: actions/download-artifact@v6
|
||||
- uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||
merge-multiple: true
|
||||
@@ -195,7 +195,7 @@ jobs:
|
||||
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
||||
|
||||
- name: Repackage in single elfs zip
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||
overwrite: true
|
||||
@@ -235,14 +235,14 @@ jobs:
|
||||
Autogenerated by github action, developer should edit as required before publishing...
|
||||
|
||||
- name: Download source deb
|
||||
uses: actions/download-artifact@v6
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
pattern: firmware-debian-${{ needs.version.outputs.deb }}~UNRELEASED-src
|
||||
merge-multiple: true
|
||||
path: ./output/debian-src
|
||||
|
||||
- name: Download `native-tft` pio deps
|
||||
uses: actions/download-artifact@v6
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }}
|
||||
merge-multiple: true
|
||||
@@ -292,7 +292,7 @@ jobs:
|
||||
with:
|
||||
python-version: 3.x
|
||||
|
||||
- uses: actions/download-artifact@v6
|
||||
- uses: actions/download-artifact@v7
|
||||
with:
|
||||
pattern: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||
merge-multiple: true
|
||||
@@ -309,7 +309,7 @@ jobs:
|
||||
- name: Zip firmware
|
||||
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
||||
|
||||
- uses: actions/download-artifact@v6
|
||||
- uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||
merge-multiple: true
|
||||
@@ -347,7 +347,7 @@ jobs:
|
||||
with:
|
||||
python-version: 3.x
|
||||
|
||||
- uses: actions/download-artifact@v6
|
||||
- uses: actions/download-artifact@v7
|
||||
with:
|
||||
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
|
||||
merge-multiple: true
|
||||
|
||||
2
.github/workflows/package_obs.yml
vendored
2
.github/workflows/package_obs.yml
vendored
@@ -58,7 +58,7 @@ jobs:
|
||||
id: version
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v6
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
|
||||
merge-multiple: true
|
||||
|
||||
2
.github/workflows/package_pio_deps.yml
vendored
2
.github/workflows/package_pio_deps.yml
vendored
@@ -56,7 +56,7 @@ jobs:
|
||||
PLATFORMIO_CORE_DIR: pio/core
|
||||
|
||||
- name: Store binaries as an artifact
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: platformio-deps-${{ inputs.pio_env }}-${{ steps.version.outputs.long }}
|
||||
overwrite: true
|
||||
|
||||
2
.github/workflows/package_ppa.yml
vendored
2
.github/workflows/package_ppa.yml
vendored
@@ -60,7 +60,7 @@ jobs:
|
||||
id: version
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v6
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
|
||||
merge-multiple: true
|
||||
|
||||
2
.github/workflows/pr_tests.yml
vendored
2
.github/workflows/pr_tests.yml
vendored
@@ -50,7 +50,7 @@ jobs:
|
||||
|
||||
- name: Download test artifacts
|
||||
if: needs.native-tests.result != 'skipped'
|
||||
uses: actions/download-artifact@v6
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: platformio-test-report-${{ steps.version.outputs.long }}
|
||||
merge-multiple: true
|
||||
|
||||
2
.github/workflows/sec_sast_semgrep_cron.yml
vendored
2
.github/workflows/sec_sast_semgrep_cron.yml
vendored
@@ -33,7 +33,7 @@ jobs:
|
||||
|
||||
# step 3
|
||||
- name: save report as pipeline artifact
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: report.sarif
|
||||
overwrite: true
|
||||
|
||||
12
.github/workflows/test_native.yml
vendored
12
.github/workflows/test_native.yml
vendored
@@ -59,7 +59,7 @@ jobs:
|
||||
id: version
|
||||
|
||||
- name: Save coverage information
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
if: always() # run this step even if previous step failed
|
||||
with:
|
||||
name: lcov-coverage-info-native-simulator-test-${{ steps.version.outputs.long }}
|
||||
@@ -94,7 +94,7 @@ jobs:
|
||||
|
||||
- name: Save test results
|
||||
if: always() # run this step even if previous step failed
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: platformio-test-report-${{ steps.version.outputs.long }}
|
||||
overwrite: true
|
||||
@@ -108,7 +108,7 @@ jobs:
|
||||
sed -i -e "s#${PWD}#.#" coverage_tests.info # Make paths relative.
|
||||
|
||||
- name: Save coverage information
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
if: always() # run this step even if previous step failed
|
||||
with:
|
||||
name: lcov-coverage-info-native-platformio-tests-${{ steps.version.outputs.long }}
|
||||
@@ -137,7 +137,7 @@ jobs:
|
||||
id: version
|
||||
|
||||
- name: Download test artifacts
|
||||
uses: actions/download-artifact@v6
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: platformio-test-report-${{ steps.version.outputs.long }}
|
||||
merge-multiple: true
|
||||
@@ -150,7 +150,7 @@ jobs:
|
||||
reporter: java-junit
|
||||
|
||||
- name: Download coverage artifacts
|
||||
uses: actions/download-artifact@v6
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
pattern: lcov-coverage-info-native-*-${{ steps.version.outputs.long }}
|
||||
path: code-coverage-report
|
||||
@@ -163,7 +163,7 @@ jobs:
|
||||
genhtml --quiet --legend --prefix "${PWD}" code-coverage-report/coverage_src.info --output-directory code-coverage-report
|
||||
|
||||
- name: Save Code Coverage Report
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: code-coverage-report-${{ steps.version.outputs.long }}
|
||||
path: code-coverage-report
|
||||
|
||||
2
.github/workflows/update_protobufs.yml
vendored
2
.github/workflows/update_protobufs.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
submodules: true
|
||||
|
||||
- name: Update submodule
|
||||
if: ${{ github.ref == 'refs/heads/master' }}
|
||||
if: ${{ github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop' }}
|
||||
run: |
|
||||
git submodule update --remote protobufs
|
||||
|
||||
|
||||
@@ -9,16 +9,16 @@ plugins:
|
||||
lint:
|
||||
enabled:
|
||||
- checkov@3.2.495
|
||||
- renovate@42.42.2
|
||||
- renovate@42.64.1
|
||||
- prettier@3.7.4
|
||||
- trufflehog@3.92.1
|
||||
- trufflehog@3.92.3
|
||||
- yamllint@1.37.1
|
||||
- bandit@1.9.2
|
||||
- trivy@0.68.1
|
||||
- trivy@0.68.2
|
||||
- taplo@0.10.0
|
||||
- ruff@0.14.8
|
||||
- ruff@0.14.10
|
||||
- isort@7.0.0
|
||||
- markdownlint@0.46.0
|
||||
- markdownlint@0.47.0
|
||||
- oxipng@10.0.0
|
||||
- svgo@4.0.0
|
||||
- actionlint@1.7.9
|
||||
|
||||
@@ -184,6 +184,8 @@ Input:
|
||||
Logging:
|
||||
LogLevel: info # debug, info, warn, error
|
||||
# TraceFile: /var/log/meshtasticd.json
|
||||
# JSONFile: /packets.json # File location for JSON output of decoded packets
|
||||
# JSONFilter: position # filter for packets to save to JSON file
|
||||
# AsciiLogs: true # default if not specified is !isatty() on stdout
|
||||
|
||||
Webserver:
|
||||
|
||||
@@ -87,6 +87,9 @@
|
||||
</screenshots>
|
||||
|
||||
<releases>
|
||||
<release version="2.7.18" date="2025-12-20">
|
||||
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.18</url>
|
||||
</release>
|
||||
<release version="2.7.17" date="2025-11-28">
|
||||
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.17</url>
|
||||
</release>
|
||||
|
||||
101
bin/shame.py
Normal file
101
bin/shame.py
Normal file
@@ -0,0 +1,101 @@
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
from github import Github
|
||||
|
||||
def parseFile(path):
|
||||
with open(path, "r") as f:
|
||||
data = json.loads(f)
|
||||
for file in data["files"]:
|
||||
if file["name"].endswith(".bin"):
|
||||
return file["name"], file["bytes"]
|
||||
|
||||
if len(sys.argv) != 4:
|
||||
print(f"expected usage: {sys.argv[0]} <PR number> <path to old-manifests> <path to new-manifests>")
|
||||
sys.exit(1)
|
||||
|
||||
pr_number = int(sys.argv[1])
|
||||
|
||||
token = os.getenv("GITHUB_TOKEN")
|
||||
if not token:
|
||||
raise EnvironmentError("GITHUB_TOKEN not found in environment.")
|
||||
|
||||
repo_name = os.getenv("GITHUB_REPOSITORY") # "owner/repo"
|
||||
if not repo_name:
|
||||
raise EnvironmentError("GITHUB_REPOSITORY not found in environment.")
|
||||
|
||||
oldFiles = sys.argv[2]
|
||||
old = set(os.path.join(oldFiles, f) for f in os.listdir(oldFiles) if os.path.isfile(f))
|
||||
newFiles = sys.argv[3]
|
||||
new = set(os.path.join(newFiles, f) for f in os.listdir(newFiles) if os.path.isfile(f))
|
||||
|
||||
startMarkdown = "# Target Size Changes\n\n"
|
||||
markdown = ""
|
||||
|
||||
newlyIntroduced = new - old
|
||||
if len(newlyIntroduced) > 0:
|
||||
markdown += "## Newly Introduced Targets\n\n"
|
||||
# create a table
|
||||
markdown += "| File | Size |\n"
|
||||
markdown += "| ---- | ---- |\n"
|
||||
for f in newlyIntroduced:
|
||||
name, size = parseFile(f)
|
||||
markdown += f"| `{name}` | {size}b |\n"
|
||||
|
||||
removed = old - new
|
||||
if len(removed) > 0:
|
||||
markdown += "\n## Removed Targets\n\n"
|
||||
# create a table
|
||||
markdown += "| File | Size |\n"
|
||||
markdown += "| ---- | ---- |\n"
|
||||
for f in removed:
|
||||
name, size = parseFile(f)
|
||||
markdown += f"| `{name}` | {size}b |\n"
|
||||
|
||||
both = old & new
|
||||
degradations = []
|
||||
improvements = []
|
||||
for f in both:
|
||||
oldName, oldSize = parseFile(f)
|
||||
_, newSize = parseFile(f)
|
||||
if oldSize != newSize:
|
||||
if newSize < oldSize:
|
||||
improvements.append((oldName, oldSize, newSize))
|
||||
else:
|
||||
degradations.append((oldName, oldSize, newSize))
|
||||
|
||||
if len(degradations) > 0:
|
||||
markdown += "\n## Degradation\n\n"
|
||||
# create a table
|
||||
markdown += "| File | Difference | Old Size | New Size |\n"
|
||||
markdown += "| ---- | ---------- | -------- | -------- |\n"
|
||||
for oldName, oldSize, newSize in degradations:
|
||||
markdown += f"| `{oldName}` | **{oldSize - newSize}b** | {oldSize}b | {newSize}b |\n"
|
||||
|
||||
if len(improvements) > 0:
|
||||
markdown += "\n## Improvement\n\n"
|
||||
# create a table
|
||||
markdown += "| File | Difference | Old Size | New Size |\n"
|
||||
markdown += "| ---- | ---------- | -------- | -------- |\n"
|
||||
for oldName, oldSize, newSize in improvements:
|
||||
markdown += f"| `{oldName}` | **{oldSize - newSize}b** | {oldSize}b | {newSize}b |\n"
|
||||
|
||||
if len(markdown) == 0:
|
||||
markdown = "No changes in target sizes detected."
|
||||
|
||||
g = Github(token)
|
||||
repo = g.get_repo(repo_name)
|
||||
pr = repo.get_pull(pr_number)
|
||||
|
||||
existing_comment = None
|
||||
for comment in pr.get_issue_comments():
|
||||
if comment.body.startswith(startMarkdown):
|
||||
existing_comment = comment
|
||||
break
|
||||
|
||||
final_markdown = startMarkdown + markdown
|
||||
|
||||
if existing_comment:
|
||||
existing_comment.edit(body=final_markdown)
|
||||
else:
|
||||
pr.create_issue_comment(body=final_markdown)
|
||||
6
debian/changelog
vendored
6
debian/changelog
vendored
@@ -1,3 +1,9 @@
|
||||
meshtasticd (2.7.18.0) unstable; urgency=medium
|
||||
|
||||
* Version 2.7.18
|
||||
|
||||
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Sat, 20 Dec 2025 15:47:25 +0000
|
||||
|
||||
meshtasticd (2.7.17.0) unstable; urgency=medium
|
||||
|
||||
* Version 2.7.17
|
||||
|
||||
@@ -103,17 +103,13 @@ lib_deps =
|
||||
thingsboard/TBPubSubClient@2.12.1
|
||||
# renovate: datasource=custom.pio depName=NTPClient packageName=arduino-libraries/library/NTPClient
|
||||
arduino-libraries/NTPClient@3.2.1
|
||||
|
||||
; Extra TCP/IP networking libs for supported devices
|
||||
[networking_extra]
|
||||
lib_deps =
|
||||
# renovate: datasource=custom.pio depName=Syslog packageName=arcao/library/Syslog
|
||||
arcao/Syslog@2.0.0
|
||||
|
||||
; Minimal networking libs for nrf52 (excludes Syslog to save flash)
|
||||
[nrf52_networking_base]
|
||||
lib_deps =
|
||||
# renovate: datasource=custom.pio depName=TBPubSubClient packageName=thingsboard/library/TBPubSubClient
|
||||
thingsboard/TBPubSubClient@2.12.1
|
||||
# renovate: datasource=custom.pio depName=NTPClient packageName=arduino-libraries/library/NTPClient
|
||||
arduino-libraries/NTPClient@3.2.1
|
||||
|
||||
[radiolib_base]
|
||||
lib_deps =
|
||||
# renovate: datasource=custom.pio depName=RadioLib packageName=jgromes/library/RadioLib
|
||||
@@ -123,7 +119,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/4fb5f24787caa841b58dbf623a52c4c5861d6722.zip
|
||||
https://github.com/meshtastic/device-ui/archive/862ed040c4ab44f0dfbbe492691f144886102588.zip
|
||||
|
||||
; Common libs for environmental measurements in telemetry module
|
||||
[environmental_base]
|
||||
@@ -162,8 +158,8 @@ lib_deps =
|
||||
emotibit/EmotiBit MLX90632@1.0.8
|
||||
# renovate: datasource=custom.pio depName=Adafruit MLX90614 packageName=adafruit/library/Adafruit MLX90614 Library
|
||||
adafruit/Adafruit MLX90614 Library@2.1.5
|
||||
# renovate: datasource=github-tags depName=INA3221 packageName=sgtwilko/INA3221
|
||||
https://github.com/sgtwilko/INA3221#bb03d7e9bfcc74fc798838a54f4f99738f29fc6a
|
||||
# renovate: datasource=git-refs depName=INA3221 packageName=https://github.com/sgtwilko/INA3221 gitBranch=FixOverflow
|
||||
https://github.com/sgtwilko/INA3221/archive/bb03d7e9bfcc74fc798838a54f4f99738f29fc6a.zip
|
||||
# renovate: datasource=custom.pio depName=QMC5883L Compass packageName=mprograms/library/QMC5883LCompass
|
||||
mprograms/QMC5883LCompass@1.2.3
|
||||
# renovate: datasource=custom.pio depName=DFRobot_RTU packageName=dfrobot/library/DFRobot_RTU
|
||||
|
||||
Submodule protobufs updated: 4095e59890...c474fd3f49
@@ -31,6 +31,9 @@ const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaC
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST:
|
||||
return useShortName ? "LongF" : "LongFast";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_TURBO:
|
||||
return useShortName ? "LongT" : "LongTurbo";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE:
|
||||
return useShortName ? "LongM" : "LongMod";
|
||||
break;
|
||||
|
||||
@@ -29,8 +29,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#if __has_include("Melopero_RV3028.h")
|
||||
#include "Melopero_RV3028.h"
|
||||
#endif
|
||||
#if __has_include("pcf8563.h")
|
||||
#include "pcf8563.h"
|
||||
#if __has_include("SensorRtcHelper.hpp")
|
||||
#include "SensorRtcHelper.hpp"
|
||||
#endif
|
||||
|
||||
/* Offer chance for variant-specific defines */
|
||||
|
||||
@@ -25,8 +25,8 @@ ScanI2C::FoundDevice ScanI2C::firstScreen() const
|
||||
|
||||
ScanI2C::FoundDevice ScanI2C::firstRTC() const
|
||||
{
|
||||
ScanI2C::DeviceType types[] = {RTC_RV3028, RTC_PCF8563, RTC_RX8130CE};
|
||||
return firstOfOrNONE(3, types);
|
||||
ScanI2C::DeviceType types[] = {RTC_RV3028, RTC_PCF8563, RTC_PCF85063, RTC_RX8130CE};
|
||||
return firstOfOrNONE(4, types);
|
||||
}
|
||||
|
||||
ScanI2C::FoundDevice ScanI2C::firstKeyboard() const
|
||||
|
||||
@@ -14,6 +14,7 @@ class ScanI2C
|
||||
SCREEN_ST7567,
|
||||
RTC_RV3028,
|
||||
RTC_PCF8563,
|
||||
RTC_PCF85063,
|
||||
RTC_RX8130CE,
|
||||
CARDKB,
|
||||
TDECKKB,
|
||||
|
||||
@@ -202,6 +202,10 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
||||
SCAN_SIMPLE_CASE(RX8130CE_RTC, RTC_RX8130CE, "RX8130CE", (uint8_t)addr.address)
|
||||
#endif
|
||||
|
||||
#ifdef PCF85063_RTC
|
||||
SCAN_SIMPLE_CASE(PCF85063_RTC, RTC_PCF85063, "PCF85063", (uint8_t)addr.address)
|
||||
#endif
|
||||
|
||||
case CARDKB_ADDR:
|
||||
// Do we have the RAK14006 instead?
|
||||
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x04), 1);
|
||||
|
||||
@@ -1133,10 +1133,13 @@ int32_t GPS::runOnce()
|
||||
// if gps_update_interval is <=10s, GPS never goes off, so we treat that differently
|
||||
uint32_t updateInterval = Default::getConfiguredOrDefaultMs(config.position.gps_update_interval);
|
||||
|
||||
// Got a time for the first time
|
||||
// 1. Got a time for the first time
|
||||
bool gotTime = (getRTCQuality() >= RTCQualityGPS);
|
||||
if (!gotTime && lookForTime()) { // Note: we count on this && short-circuiting and not resetting the RTC time
|
||||
gotTime = true;
|
||||
}
|
||||
|
||||
// Got a lock for the first time, or Got a lock after turning back on
|
||||
// 2. Got a lock for the first time, or 3. Got a lock after turning back on
|
||||
bool gotLoc = lookForLocation();
|
||||
if (gotLoc) {
|
||||
#ifdef GPS_DEBUG
|
||||
@@ -1144,10 +1147,6 @@ int32_t GPS::runOnce()
|
||||
LOG_DEBUG("hasValidLocation RISING EDGE");
|
||||
}
|
||||
#endif
|
||||
if (!gotTime && lookForTime()) { // Note: we count on this && short-circuiting and not resetting the RTC time
|
||||
gotTime = true;
|
||||
}
|
||||
|
||||
if (updateInterval <= GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS) {
|
||||
hasValidLocation = true;
|
||||
shouldPublish = true;
|
||||
|
||||
@@ -66,26 +66,26 @@ RTCSetResult readFromRTC()
|
||||
currentQuality = RTCQualityDevice;
|
||||
}
|
||||
return RTCSetResultSuccess;
|
||||
} else {
|
||||
LOG_WARN("RTC not found (found address 0x%02X)", rtc_found.address);
|
||||
}
|
||||
#elif defined(PCF8563_RTC)
|
||||
#elif defined(PCF8563_RTC) || defined(PCF85063_RTC)
|
||||
#if defined(PCF8563_RTC)
|
||||
if (rtc_found.address == PCF8563_RTC) {
|
||||
#elif defined(PCF85063_RTC)
|
||||
if (rtc_found.address == PCF85063_RTC) {
|
||||
#endif
|
||||
uint32_t now = millis();
|
||||
PCF8563_Class rtc;
|
||||
SensorRtcHelper rtc;
|
||||
|
||||
#if WIRE_INTERFACES_COUNT == 2
|
||||
rtc.begin(rtc_found.port == ScanI2C::I2CPort::WIRE1 ? Wire1 : Wire);
|
||||
#else
|
||||
rtc.begin();
|
||||
rtc.begin(Wire);
|
||||
#endif
|
||||
|
||||
auto tc = rtc.getDateTime();
|
||||
tm t;
|
||||
t.tm_year = tc.year - 1900;
|
||||
t.tm_mon = tc.month - 1;
|
||||
t.tm_mday = tc.day;
|
||||
t.tm_hour = tc.hour;
|
||||
t.tm_min = tc.minute;
|
||||
t.tm_sec = tc.second;
|
||||
RTC_DateTime datetime = rtc.getDateTime();
|
||||
tm t = datetime.toUnixTime();
|
||||
tv.tv_sec = gm_mktime(&t);
|
||||
tv.tv_usec = 0;
|
||||
uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms
|
||||
@@ -100,14 +100,16 @@ RTCSetResult readFromRTC()
|
||||
}
|
||||
#endif
|
||||
|
||||
LOG_DEBUG("Read RTC time from PCF8563 getDateTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", t.tm_year + 1900, t.tm_mon + 1,
|
||||
t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch);
|
||||
LOG_DEBUG("Read RTC time from %s getDateTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", rtc.getChipName(), t.tm_year + 1900,
|
||||
t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch);
|
||||
if (currentQuality == RTCQualityNone) {
|
||||
timeStartMsec = now;
|
||||
zeroOffsetSecs = tv.tv_sec;
|
||||
currentQuality = RTCQualityDevice;
|
||||
}
|
||||
return RTCSetResultSuccess;
|
||||
} else {
|
||||
LOG_WARN("RTC not found (found address 0x%02X)", rtc_found.address);
|
||||
}
|
||||
#elif defined(RX8130CE_RTC)
|
||||
if (rtc_found.address == RX8130CE_RTC) {
|
||||
@@ -232,20 +234,28 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd
|
||||
rtc.setTime(t->tm_year + 1900, t->tm_mon + 1, t->tm_wday, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
|
||||
LOG_DEBUG("RV3028_RTC setTime %02d-%02d-%02d %02d:%02d:%02d (%ld)", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
|
||||
t->tm_hour, t->tm_min, t->tm_sec, printableEpoch);
|
||||
} else {
|
||||
LOG_WARN("RTC not found (found address 0x%02X)", rtc_found.address);
|
||||
}
|
||||
#elif defined(PCF8563_RTC)
|
||||
#elif defined(PCF8563_RTC) || defined(PCF85063_RTC)
|
||||
#if defined(PCF8563_RTC)
|
||||
if (rtc_found.address == PCF8563_RTC) {
|
||||
PCF8563_Class rtc;
|
||||
#elif defined(PCF85063_RTC)
|
||||
if (rtc_found.address == PCF85063_RTC) {
|
||||
#endif
|
||||
SensorRtcHelper rtc;
|
||||
|
||||
#if WIRE_INTERFACES_COUNT == 2
|
||||
rtc.begin(rtc_found.port == ScanI2C::I2CPort::WIRE1 ? Wire1 : Wire);
|
||||
#else
|
||||
rtc.begin();
|
||||
rtc.begin(Wire);
|
||||
#endif
|
||||
tm *t = gmtime(&tv->tv_sec);
|
||||
rtc.setDateTime(t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
|
||||
LOG_DEBUG("PCF8563_RTC setDateTime %02d-%02d-%02d %02d:%02d:%02d (%ld)", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
|
||||
t->tm_hour, t->tm_min, t->tm_sec, printableEpoch);
|
||||
rtc.setDateTime(*t);
|
||||
LOG_DEBUG("%s setDateTime %02d-%02d-%02d %02d:%02d:%02d (%ld)", rtc.getChipName(), t->tm_year + 1900, t->tm_mon + 1,
|
||||
t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, printableEpoch);
|
||||
} else {
|
||||
LOG_WARN("RTC not found (found address 0x%02X)", rtc_found.address);
|
||||
}
|
||||
#elif defined(RX8130CE_RTC)
|
||||
if (rtc_found.address == RX8130CE_RTC) {
|
||||
|
||||
@@ -20,12 +20,41 @@
|
||||
#include "modules/KeyVerificationModule.h"
|
||||
|
||||
#include "modules/TraceRouteModule.h"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
extern uint16_t TFT_MESH;
|
||||
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// Caller must ensure the provided options array outlives the banner callback.
|
||||
template <typename T, size_t N, typename Callback>
|
||||
BannerOverlayOptions createStaticBannerOptions(const char *message, const MenuOption<T> (&options)[N],
|
||||
std::array<const char *, N> &labels, Callback &&onSelection)
|
||||
{
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
labels[i] = options[i].label;
|
||||
}
|
||||
|
||||
const MenuOption<T> *optionsPtr = options;
|
||||
auto callback = std::function<void(const MenuOption<T> &, int)>(std::forward<Callback>(onSelection));
|
||||
|
||||
BannerOverlayOptions bannerOptions;
|
||||
bannerOptions.message = message;
|
||||
bannerOptions.optionsArrayPtr = labels.data();
|
||||
bannerOptions.optionsCount = static_cast<uint8_t>(N);
|
||||
bannerOptions.bannerCallback = [optionsPtr, callback](int selected) -> void { callback(optionsPtr[selected], selected); };
|
||||
return bannerOptions;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
menuHandler::screenMenus menuHandler::menuQueue = menu_none;
|
||||
bool test_enabled = false;
|
||||
uint8_t test_count = 0;
|
||||
@@ -197,48 +226,38 @@ void menuHandler::DeviceRolePicker()
|
||||
|
||||
void menuHandler::RadioPresetPicker()
|
||||
{
|
||||
static const char *optionsArray[] = {"Back", "LongSlow", "LongModerate", "LongFast", "MediumSlow",
|
||||
"MediumFast", "ShortSlow", "ShortFast", "ShortTurbo"};
|
||||
enum optionsNumbers {
|
||||
Back = 0,
|
||||
radiopreset_LongSlow = 1,
|
||||
radiopreset_LongModerate = 2,
|
||||
radiopreset_LongFast = 3,
|
||||
radiopreset_MediumSlow = 4,
|
||||
radiopreset_MediumFast = 5,
|
||||
radiopreset_ShortSlow = 6,
|
||||
radiopreset_ShortFast = 7,
|
||||
radiopreset_ShortTurbo = 8
|
||||
};
|
||||
BannerOverlayOptions bannerOptions;
|
||||
bannerOptions.message = "Radio Preset";
|
||||
bannerOptions.optionsArrayPtr = optionsArray;
|
||||
bannerOptions.optionsCount = 9;
|
||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||
if (selected == Back) {
|
||||
menuHandler::menuQueue = menuHandler::lora_Menu;
|
||||
screen->runNow();
|
||||
return;
|
||||
} else if (selected == radiopreset_LongSlow) {
|
||||
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW;
|
||||
} else if (selected == radiopreset_LongModerate) {
|
||||
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE;
|
||||
} else if (selected == radiopreset_LongFast) {
|
||||
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST;
|
||||
} else if (selected == radiopreset_MediumSlow) {
|
||||
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW;
|
||||
} else if (selected == radiopreset_MediumFast) {
|
||||
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST;
|
||||
} else if (selected == radiopreset_ShortSlow) {
|
||||
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW;
|
||||
} else if (selected == radiopreset_ShortFast) {
|
||||
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST;
|
||||
} else if (selected == radiopreset_ShortTurbo) {
|
||||
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO;
|
||||
}
|
||||
service->reloadConfig(SEGMENT_CONFIG);
|
||||
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
|
||||
static const RadioPresetOption presetOptions[] = {
|
||||
{"Back", OptionsAction::Back},
|
||||
{"LongTurbo", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_LONG_TURBO},
|
||||
{"LongModerate", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE},
|
||||
{"LongFast", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST},
|
||||
{"MediumSlow", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW},
|
||||
{"MediumFast", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST},
|
||||
{"ShortSlow", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW},
|
||||
{"ShortFast", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST},
|
||||
{"ShortTurbo", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO},
|
||||
};
|
||||
|
||||
constexpr size_t presetCount = sizeof(presetOptions) / sizeof(presetOptions[0]);
|
||||
static std::array<const char *, presetCount> presetLabels{};
|
||||
|
||||
auto bannerOptions =
|
||||
createStaticBannerOptions("Radio Preset", presetOptions, presetLabels, [](const RadioPresetOption &option, int) -> void {
|
||||
if (option.action == OptionsAction::Back) {
|
||||
menuHandler::menuQueue = menuHandler::lora_Menu;
|
||||
screen->runNow();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!option.hasValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
config.lora.modem_preset = option.value;
|
||||
service->reloadConfig(SEGMENT_CONFIG);
|
||||
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
|
||||
});
|
||||
|
||||
screen->showOverlayBanner(bannerOptions);
|
||||
}
|
||||
|
||||
|
||||
@@ -99,5 +99,24 @@ class menuHandler
|
||||
static void BluetoothToggleMenu();
|
||||
};
|
||||
|
||||
/* Generic Menu Options designations */
|
||||
enum class OptionsAction { Back, Select };
|
||||
|
||||
template <typename T> struct MenuOption {
|
||||
const char *label;
|
||||
OptionsAction action;
|
||||
bool hasValue;
|
||||
T value;
|
||||
|
||||
MenuOption(const char *labelIn, OptionsAction actionIn, T valueIn)
|
||||
: label(labelIn), action(actionIn), hasValue(true), value(valueIn)
|
||||
{
|
||||
}
|
||||
|
||||
MenuOption(const char *labelIn, OptionsAction actionIn) : label(labelIn), action(actionIn), hasValue(false), value() {}
|
||||
};
|
||||
|
||||
using RadioPresetOption = MenuOption<meshtastic_Config_LoRaConfig_ModemPreset>;
|
||||
|
||||
} // namespace graphics
|
||||
#endif
|
||||
@@ -13,41 +13,81 @@ const Emote emotes[] = {
|
||||
{"\U0001F44E", thumbdown, thumbs_width, thumbs_height}, // 👎 Thumbs Down
|
||||
|
||||
// --- Smileys (Multiple Unicode Aliases) ---
|
||||
{"\U0001F60A", Smiling_Eyes, Smiling_Eyes_width, Smiling_Eyes_height}, // 😊 Smiling Eyes
|
||||
{"\U0001F600", Grinning, Grinning_width, Grinning_height}, // 😀 Grinning Face
|
||||
{"\U0001F642", Slightly_Smiling, Slightly_Smiling_width, Slightly_Smiling_height}, // 🙂 Slightly Smiling Face
|
||||
{"\U0001F609", Winking_Face, Winking_Face_width, Winking_Face_height}, // 😉 Winking Face
|
||||
{"\U0001F601", Grinning_Smiling_Eyes, Grinning_Smiling_Eyes_width, Grinning_Smiling_Eyes_height}, // 😁 Grinning Smiling Eyes
|
||||
{"\U0001F60D", Heart_eyes, Heart_eyes_width, Heart_eyes_height}, // 😍 Heart Eyes
|
||||
{"\U0001F60A", smiling_eyes, smiling_eyes_width, smiling_eyes_height}, // 😊 Smiling Eyes
|
||||
{"\U0001F600", grinning, grinning_width, grinning_height}, // 😀 Grinning Face
|
||||
{"\U0001F642", slightly_smiling, slightly_smiling_width, slightly_smiling_height}, // 🙂 Slightly Smiling Face
|
||||
{"\U0001F609", winking_face, winking_face_width, winking_face_height}, // 😉 Winking Face
|
||||
{"\U0001F601", grinning_smiling_eyes, grinning_smiling_eyes_width, grinning_smiling_eyes_height}, // 😁 Grinning Smiling Eyes
|
||||
{"\U0001F60D", heart_eyes, heart_eyes_width, heart_eyes_height}, // 😍 Heart Eyes
|
||||
{"\U0001F970", heart_smile, heart_smile_width, heart_smile_height}, // 🥰 Smiling Face with Hearts
|
||||
|
||||
// --- Question/Alert ---
|
||||
{"\u2753", question, question_width, question_height}, // ❓ Question Mark
|
||||
{"\u203C\uFE0F", bang, bang_width, bang_height}, // ‼️ Double Exclamation Mark
|
||||
{"\u2753", question, question_width, question_height}, // ❓ Question Mark
|
||||
{"\u203C\uFE0F", bang, bang_width, bang_height}, // ‼️ Double Exclamation Mark
|
||||
{"\u26A0\uFE0F", caution, caution_width, caution_height}, // ⚠️ Warning Sign
|
||||
|
||||
// --- Laughing Faces ---
|
||||
{"\U0001F602", haha, haha_width, haha_height}, // 😂 Face with Tears of Joy
|
||||
{"\U0001F923", ROFL, ROFL_width, ROFL_height}, // 🤣 Rolling on the Floor Laughing
|
||||
{"\U0001F606", Smiling_Closed_Eyes, Smiling_Closed_Eyes_width, Smiling_Closed_Eyes_height}, // 😆 Smiling Closed Eyes
|
||||
{"\U0001F923", rofl, rofl_width, rofl_height}, // 🤣 Rolling on the Floor Laughing
|
||||
{"\U0001F606", smiling_closed_eyes, smiling_closed_eyes_width, smiling_closed_eyes_height}, // 😆 Smiling Closed Eyes
|
||||
{"\U0001F605", haha, haha_width, haha_height}, // 😅 Smiling with Sweat
|
||||
{"\U0001F604", Grinning_SmilingEyes2, Grinning_SmilingEyes2_width,
|
||||
Grinning_SmilingEyes2_height}, // 😄 Grinning Face with Smiling Eyes
|
||||
{"\U0001F62D", Loudly_Crying_Face, Loudly_Crying_Face_width, Loudly_Crying_Face_height}, // 😭 Loudly Crying Face
|
||||
{"\U0001F604", grinning_smiling_eyes_2, grinning_smiling_eyes_2_width,
|
||||
grinning_smiling_eyes_2_height}, // 😄 Grinning Face with Smiling Eyes
|
||||
{"\U0001F62D", loudly_crying_face, loudly_crying_face_width, loudly_crying_face_height}, // 😭 Loudly Crying Face
|
||||
{"\U0001F92E", vomiting, vomiting_width, vomiting_height}, // 🤮 Face Vomiting
|
||||
{"\U0001F60E", cool, cool_width, cool_height}, // 😎 Smiling Face with Sunglasses
|
||||
{"\U0001F440", eyes, eyes_width, eyes_height}, // 👀 Eyes
|
||||
{"\U0001F441\uFE0F", eye, eye_width, eye_height}, // 👁️ Eye
|
||||
|
||||
// --- Gestures and People ---
|
||||
{"\U0001F44B", wave_icon, wave_icon_width, wave_icon_height}, // 👋 Waving Hand
|
||||
{"\u270C\uFE0F", peace_sign, peace_sign_width, peace_sign_height}, // ✌️ Victory Hand
|
||||
{"\U0001F596", vulcan_salute, vulcan_salute_width, vulcan_salute_height}, // 🖖 Vulcan Salute
|
||||
{"\U0001F64F", Praying, Praying_width, Praying_height}, // 🙏 Praying Hands
|
||||
{"\U0001F64F", praying, praying_width, praying_height}, // 🙏 Praying Hands
|
||||
{"\U0001F4AA", strong, strong_width, strong_height}, // 💪 Flexed Biceps
|
||||
{"\U0001F937", shrug, shrug_width, shrug_height}, // 🤷 Person Shrugging
|
||||
{"\U0001F920", cowboy, cowboy_width, cowboy_height}, // 🤠 Cowboy Hat Face
|
||||
{"\U0001F3A7", deadmau5, deadmau5_width, deadmau5_height}, // 🎧 Headphones
|
||||
|
||||
// --- Symbols ---
|
||||
{"\u2714\uFE0F", check_mark, check_mark_width, check_mark_height}, // ✔️ Check Mark
|
||||
{"\u2705", check_mark, check_mark_width, check_mark_height}, // ✅ Check Mark Button
|
||||
{"\u2611\uFE0F", check_mark, check_mark_width, check_mark_height}, // ☑️ Check Box with Check
|
||||
{"\U0001F3E0", house, house_width, house_height}, // 🏠 House
|
||||
|
||||
// --- Weather ---
|
||||
{"\u2600", sun, sun_width, sun_height}, // ☀ Sun (without variation selector)
|
||||
{"\u2600\uFE0F", sun, sun_width, sun_height}, // ☀️ Sun (with variation selector)
|
||||
{"\U0001F327\uFE0F", rain, rain_width, rain_height}, // 🌧️ Cloud with Rain
|
||||
{"\u2601\uFE0F", cloud, cloud_width, cloud_height}, // ☁️ Cloud
|
||||
{"\U0001F32B\uFE0F", fog, fog_width, fog_height}, // 🌫️ Fog
|
||||
{"\u2600", sun, sun_width, sun_height}, // ☀ Sun (without variation selector)
|
||||
{"\u2600\uFE0F", sun, sun_width, sun_height}, // ☀️ Sun (with variation selector)
|
||||
{"\U0001F327\uFE0F", rain, rain_width, rain_height}, // 🌧️ Cloud with Rain
|
||||
{"\u2601\uFE0F", cloud, cloud_width, cloud_height}, // ☁️ Cloud
|
||||
{"\U0001F32B\uFE0F", fog, fog_width, fog_height}, // 🌫️ Fog
|
||||
{"\u2744\uFE0F", snowflake, snowflake_width, snowflake_height}, // ❄️ Snowflake
|
||||
{"\U0001F4A7", drop, drop_width, drop_height}, // 💧 Droplet
|
||||
{"\U0001F321\uFE0F", thermometer, thermometer_width, thermometer_height}, // 🌡️ Thermometer
|
||||
{"\U0001F326\uFE0F", sun_behind_raincloud, sun_behind_raincloud_width,
|
||||
sun_behind_raincloud_height}, // 🌦️ Sun Behind Rain Cloud
|
||||
{"\u26C5", sun_behind_cloud, sun_behind_cloud_width, sun_behind_cloud_height}, // ⛅ Sun Behind Cloud
|
||||
{"\u26C5\uFE0F", sun_behind_cloud, sun_behind_cloud_width, sun_behind_cloud_height}, // ⛅️ Sun Behind Cloud
|
||||
{"\U0001F328\uFE0F", cloud_with_snow, cloud_with_snow_width, cloud_with_snow_height}, // 🌨️ Cloud with Snow
|
||||
{"\U0001F329\uFE0F", cloud_with_lightning, cloud_with_lightning_width,
|
||||
cloud_with_lightning_height}, // 🌩️ Cloud with Lightning
|
||||
{"\u26C8", cloud_with_lightning_rain, cloud_with_lightning_rain_width,
|
||||
cloud_with_lightning_rain_height}, // ⛈ Cloud with Lightning and Rain
|
||||
{"\u26C8\uFE0F", cloud_with_lightning_rain, cloud_with_lightning_rain_width,
|
||||
cloud_with_lightning_rain_height}, // ⛈️ Cloud with Lightning and Rain
|
||||
{"\U0001F32C\uFE0F", wind_face, wind_face_width, wind_face_height}, // 🌬️ Wind Face
|
||||
|
||||
// --- Moon Phases ---
|
||||
{"\U0001F311", new_moon, new_moon_width, new_moon_height}, // 🌑 New Moon
|
||||
{"\U0001F312", waxing_crescent_moon, waxing_crescent_moon_width, waxing_crescent_moon_height}, // 🌒 Waxing Crescent Moon
|
||||
{"\U0001F313", first_quarter_moon, first_quarter_moon_width, first_quarter_moon_height}, // 🌓 First Quarter Moon
|
||||
{"\U0001F314", waxing_gibbous_moon, waxing_gibbous_moon_width, waxing_gibbous_moon_height}, // 🌔 Waxing Gibbous Moon
|
||||
{"\U0001F315", full_moon, full_moon_width, full_moon_height}, // 🌕 Full Moon
|
||||
{"\U0001F316", waning_gibbous_moon, waning_gibbous_moon_width, waning_gibbous_moon_height}, // 🌖 Waning Gibbous Moon
|
||||
{"\U0001F317", last_quarter_moon, last_quarter_moon_width, last_quarter_moon_height}, // 🌗 Last Quarter Moon
|
||||
{"\U0001F318", waning_crescent_moon, waning_crescent_moon_width, waning_crescent_moon_height}, // 🌘 Waning Crescent Moon
|
||||
{"\U0001F31B", first_quarter_moon_face, first_quarter_moon_face_width,
|
||||
first_quarter_moon_face_height}, // 🌛 First Quarter Moon Face
|
||||
|
||||
// --- Misc Faces ---
|
||||
{"\U0001F608", devil, devil_width, devil_height}, // 😈 Smiling Face with Horns
|
||||
@@ -67,13 +107,49 @@ const Emote emotes[] = {
|
||||
{"\U0001F498", heart, heart_width, heart_height}, // 💘 Heart with Arrow
|
||||
|
||||
// --- Objects ---
|
||||
{"\U0001F4A9", poo, poo_width, poo_height}, // 💩 Pile of Poo
|
||||
{"\U0001F514", bell_icon, bell_icon_width, bell_icon_height}, // 🔔 Bell
|
||||
{"\U0001F36A", cookie, cookie_width, cookie_height}, // 🍪 Cookie
|
||||
{"\U0001F525", Fire, Fire_width, Fire_height}, // 🔥 Fire
|
||||
{"\u2728", Sparkles, Sparkles_width, Sparkles_height}, // ✨ Sparkles
|
||||
{"\U0001F573\uFE0F", hole, hole_width, hole_height}, // 🕳️ Hole
|
||||
{"\U0001F3B3", bowling, bowling_width, bowling_height} // 🎳 Bowling
|
||||
{"\U0001F4A9", poo, poo_width, poo_height}, // 💩 Pile of Poo
|
||||
{"\U0001F514", bell_icon, bell_icon_width, bell_icon_height}, // 🔔 Bell
|
||||
{"\U0001F4CB", clipboard, clipboard_width, clipboard_height}, // 📋 Clipboard
|
||||
{"\U0001F36A", cookie, cookie_width, cookie_height}, // 🍪 Cookie
|
||||
{"\U0001F370", shortcake, shortcake_width, shortcake_height}, // 🍰 Shortcake
|
||||
{"\U0001F351", peach, peach_width, peach_height}, // 🍑 Peach
|
||||
{"\U0001F983", turkey, turkey_width, turkey_height}, // 🦃 Turkey
|
||||
{"\U0001F357", turkey_leg, turkey_leg_width, turkey_leg_height}, // 🍗 Poultry Leg
|
||||
{"\U0001F525", fire, fire_width, fire_height}, // 🔥 Fire
|
||||
{"\u2728", sparkles, sparkles_width, sparkles_height}, // ✨ Sparkles
|
||||
{"\U0001F573\uFE0F", hole, hole_width, hole_height}, // 🕳️ Hole
|
||||
{"\U0001F3B3", bowling, bowling_width, bowling_height}, // 🎳 Bowling
|
||||
|
||||
// --- Arrows ---
|
||||
{"\u2193", downwards_arrow, downwards_arrow_width, downwards_arrow_height}, // ↓ Downwards Arrow
|
||||
{"\u2193\uFE0E", downwards_arrow, downwards_arrow_width, downwards_arrow_height}, // ↓︎ Downwards Arrow (text)
|
||||
{"\u2193\uFE0F", downwards_arrow, downwards_arrow_width, downwards_arrow_height}, // ↓️ Downwards Arrow (emoji)
|
||||
{"\u2199", south_west_arrow, south_west_arrow_width, south_west_arrow_height}, // ↙ South West Arrow
|
||||
{"\u2199\uFE0E", south_west_arrow, south_west_arrow_width, south_west_arrow_height}, // ↙︎ South West Arrow (text)
|
||||
{"\u2199\uFE0F", south_west_arrow, south_west_arrow_width, south_west_arrow_height}, // ↙️ South West Arrow (emoji)
|
||||
{"\u2190", leftwards_arrow, leftwards_arrow_width, leftwards_arrow_height}, // ← Leftwards Arrow
|
||||
{"\u2190\uFE0E", leftwards_arrow, leftwards_arrow_width, leftwards_arrow_height}, // ←︎ Leftwards Arrow (text)
|
||||
{"\u2190\uFE0F", leftwards_arrow, leftwards_arrow_width, leftwards_arrow_height}, // ←️ Leftwards Arrow (emoji)
|
||||
{"\u2196", north_west_arrow, north_west_arrow_width, north_west_arrow_height}, // ↖ North West Arrow
|
||||
{"\u2196\uFE0E", north_west_arrow, north_west_arrow_width, north_west_arrow_height}, // ↖︎ North West Arrow (text)
|
||||
{"\u2196\uFE0F", north_west_arrow, north_west_arrow_width, north_west_arrow_height}, // ↖️ North West Arrow (emoji)
|
||||
{"\u2191", upwards_arrow, upwards_arrow_width, upwards_arrow_height}, // ↑ Upwards Arrow
|
||||
{"\u2191\uFE0E", upwards_arrow, upwards_arrow_width, upwards_arrow_height}, // ↑︎ Upwards Arrow (text)
|
||||
{"\u2191\uFE0F", upwards_arrow, upwards_arrow_width, upwards_arrow_height}, // ↑️ Upwards Arrow (emoji)
|
||||
{"\u2197", north_east_arrow, north_east_arrow_width, north_east_arrow_height}, // ↗ North East Arrow
|
||||
{"\u2197\uFE0E", north_east_arrow, north_east_arrow_width, north_east_arrow_height}, // ↗︎ North East Arrow (text)
|
||||
{"\u2197\uFE0F", north_east_arrow, north_east_arrow_width, north_east_arrow_height}, // ↗️ North East Arrow (emoji)
|
||||
{"\u2192", rightwards_arrow, rightwards_arrow_width, rightwards_arrow_height}, // → Rightwards Arrow
|
||||
{"\u2192\uFE0E", rightwards_arrow, rightwards_arrow_width, rightwards_arrow_height}, // →︎ Rightwards Arrow (text)
|
||||
{"\u2192\uFE0F", rightwards_arrow, rightwards_arrow_width, rightwards_arrow_height}, // →️ Rightwards Arrow (emoji)
|
||||
{"\u2198", south_east_arrow, south_east_arrow_width, south_east_arrow_height}, // ↘ South East Arrow
|
||||
{"\u2198\uFE0E", south_east_arrow, south_east_arrow_width, south_east_arrow_height}, // ↘︎ South East Arrow (text)
|
||||
{"\u2198\uFE0F", south_east_arrow, south_east_arrow_width, south_east_arrow_height}, // ↘️ South East Arrow (emoji)
|
||||
|
||||
// --- Halloween ---
|
||||
{"\U0001F383", jack_o_lantern, jack_o_lantern_width, jack_o_lantern_height}, // 🎃 Jack-O-Lantern
|
||||
{"\U0001F47B", ghost, ghost_width, ghost_height}, // 👻 Ghost
|
||||
{"\U0001F480", skull, skull_width, skull_height} // 💀 Skull
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -88,23 +164,23 @@ const unsigned char thumbdown[] PROGMEM = {0xF0, 0x1F, 0x08, 0x20, 0x06, 0x30, 0
|
||||
0x40, 0x06, 0x70, 0x06, 0x40, 0x06, 0x3F, 0x18, 0x02, 0x20, 0x02,
|
||||
0x40, 0x04, 0x80, 0x04, 0x80, 0x04, 0x00, 0x03, 0x00, 0x00};
|
||||
|
||||
const unsigned char Smiling_Eyes[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x24, 0x24, 0x52,
|
||||
const unsigned char smiling_eyes[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x24, 0x24, 0x52,
|
||||
0x4A, 0x02, 0x40, 0x02, 0x40, 0x22, 0x44, 0x22, 0x44, 0xC2, 0x43,
|
||||
0x04, 0x20, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||
|
||||
const unsigned char Grinning[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x44, 0x22, 0x42,
|
||||
const unsigned char grinning[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x44, 0x22, 0x42,
|
||||
0x42, 0x02, 0x40, 0x02, 0x40, 0xF2, 0x4F, 0x12, 0x48, 0x22, 0x44,
|
||||
0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||
|
||||
const unsigned char Slightly_Smiling[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x44, 0x22, 0x42,
|
||||
const unsigned char slightly_smiling[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x44, 0x22, 0x42,
|
||||
0x42, 0x02, 0x40, 0x02, 0x40, 0x12, 0x48, 0x12, 0x48, 0x22, 0x44,
|
||||
0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||
|
||||
const unsigned char Winking_Face[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x44, 0x20, 0x42,
|
||||
const unsigned char winking_face[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x44, 0x20, 0x42,
|
||||
0x46, 0x02, 0x40, 0x02, 0x40, 0x12, 0x48, 0x12, 0x48, 0x22, 0x44,
|
||||
0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||
|
||||
const unsigned char Grinning_Smiling_Eyes[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x24, 0x24, 0x52,
|
||||
const unsigned char grinning_smiling_eyes[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x24, 0x24, 0x52,
|
||||
0x4A, 0x02, 0x40, 0xFA, 0x5F, 0x0A, 0x50, 0x0A, 0x50, 0x12, 0x48,
|
||||
0x24, 0x24, 0xC4, 0x23, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||
|
||||
@@ -112,7 +188,7 @@ const unsigned char heart_smile[] PROGMEM = {0x00, 0x00, 0x6C, 0x07, 0x7C, 0x18,
|
||||
0x0A, 0x02, 0xD8, 0x02, 0xF8, 0x22, 0xFC, 0x20, 0x74, 0xDB, 0x23,
|
||||
0x1F, 0x00, 0x1F, 0x20, 0x0E, 0x18, 0xE4, 0x07, 0x00, 0x00};
|
||||
|
||||
const unsigned char Heart_eyes[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x54, 0x2A, 0xFA,
|
||||
const unsigned char heart_eyes[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x54, 0x2A, 0xFA,
|
||||
0x5F, 0x72, 0x4E, 0x22, 0x44, 0x02, 0x40, 0x12, 0x48, 0x12, 0x48,
|
||||
0x24, 0x24, 0xC4, 0x23, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||
|
||||
@@ -128,19 +204,19 @@ const unsigned char haha[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04,
|
||||
0x4A, 0x0A, 0x50, 0x0E, 0x70, 0xF2, 0x4F, 0x12, 0x48, 0x32, 0x44,
|
||||
0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||
|
||||
const unsigned char ROFL[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x84, 0x21, 0x84, 0x20, 0x02,
|
||||
const unsigned char rofl[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x84, 0x21, 0x84, 0x20, 0x02,
|
||||
0x4C, 0x02, 0x4A, 0x1A, 0x49, 0x8A, 0x48, 0x42, 0x48, 0x22, 0x44,
|
||||
0xE4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||
|
||||
const unsigned char Smiling_Closed_Eyes[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x24, 0x24, 0x42,
|
||||
const unsigned char smiling_closed_eyes[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x24, 0x24, 0x42,
|
||||
0x42, 0x22, 0x44, 0x02, 0x40, 0xF2, 0x4F, 0x12, 0x48, 0x22, 0x44,
|
||||
0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||
|
||||
const unsigned char Grinning_SmilingEyes2[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x24, 0x24, 0x52,
|
||||
0x4A, 0x02, 0x40, 0x02, 0x40, 0xF2, 0x4F, 0x12, 0x48, 0x22, 0x44,
|
||||
0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||
const unsigned char grinning_smiling_eyes_2[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x24, 0x24, 0x52,
|
||||
0x4A, 0x02, 0x40, 0x02, 0x40, 0xF2, 0x4F, 0x12, 0x48, 0x22, 0x44,
|
||||
0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||
|
||||
const unsigned char Loudly_Crying_Face[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x34, 0x2C, 0x4A,
|
||||
const unsigned char loudly_crying_face[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x34, 0x2C, 0x4A,
|
||||
0x52, 0x12, 0x48, 0x12, 0x48, 0x92, 0x49, 0x52, 0x4A, 0x52, 0x4A,
|
||||
0x54, 0x2A, 0x94, 0x29, 0x18, 0x18, 0xF0, 0x0F, 0x00, 0x00};
|
||||
|
||||
@@ -192,7 +268,7 @@ const unsigned char cookie[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04
|
||||
0x40, 0x02, 0x58, 0x82, 0x5B, 0x92, 0x43, 0x82, 0x43, 0x02, 0x40,
|
||||
0x64, 0x28, 0x64, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||
|
||||
const unsigned char Fire[] PROGMEM = {0x30, 0x00, 0xF0, 0x00, 0xF8, 0x03, 0xF8, 0x07, 0xFC, 0x1F, 0xFC,
|
||||
const unsigned char fire[] PROGMEM = {0x30, 0x00, 0xF0, 0x00, 0xF8, 0x03, 0xF8, 0x07, 0xFC, 0x1F, 0xFC,
|
||||
0x1F, 0xFE, 0x3E, 0x7E, 0x3E, 0x3E, 0x7C, 0x1E, 0x78, 0x1E, 0x70,
|
||||
0x1C, 0x70, 0x1C, 0x70, 0x38, 0x38, 0x30, 0x38, 0x60, 0x0C};
|
||||
|
||||
@@ -200,11 +276,11 @@ const unsigned char peace_sign[] PROGMEM = {0xC0, 0x30, 0x40, 0x29, 0x40, 0x25,
|
||||
0x0A, 0x54, 0x68, 0x54, 0x58, 0x54, 0x44, 0x3C, 0x22, 0x04, 0x22,
|
||||
0x04, 0x12, 0x08, 0x10, 0x10, 0x08, 0xE0, 0x07, 0x00, 0x00};
|
||||
|
||||
const unsigned char Praying[] PROGMEM = {0x00, 0x00, 0x40, 0x02, 0xA0, 0x05, 0x90, 0x09, 0x90, 0x09, 0x90,
|
||||
const unsigned char praying[] PROGMEM = {0x00, 0x00, 0x40, 0x02, 0xA0, 0x05, 0x90, 0x09, 0x90, 0x09, 0x90,
|
||||
0x09, 0x98, 0x19, 0x94, 0x29, 0xA4, 0x25, 0xA4, 0x25, 0x84, 0x21,
|
||||
0x84, 0x21, 0x86, 0x61, 0x4E, 0x72, 0x7F, 0x7E, 0x3F, 0xFC};
|
||||
|
||||
const unsigned char Sparkles[] PROGMEM = {0x00, 0x00, 0x10, 0x00, 0x38, 0x04, 0x10, 0x04, 0x00, 0x0E, 0x00,
|
||||
const unsigned char sparkles[] PROGMEM = {0x00, 0x00, 0x10, 0x00, 0x38, 0x04, 0x10, 0x04, 0x00, 0x0E, 0x00,
|
||||
0x1F, 0x80, 0x3F, 0xE0, 0xFF, 0x80, 0x3F, 0x10, 0x1F, 0x10, 0x0E,
|
||||
0x38, 0x04, 0xFE, 0x04, 0x38, 0x00, 0x10, 0x00, 0x10, 0x00};
|
||||
|
||||
@@ -227,7 +303,179 @@ const unsigned char bowling[] PROGMEM = {0x00, 0x38, 0x00, 0x44, 0x00, 0x44, 0x0
|
||||
const unsigned char vulcan_salute[] PROGMEM = {0x08, 0x02, 0x16, 0x0D, 0x15, 0x15, 0x15, 0x15, 0xA9, 0x12, 0x4A,
|
||||
0x0A, 0x02, 0x38, 0x04, 0x48, 0x04, 0x44, 0x04, 0x22, 0x04, 0x22,
|
||||
0x04, 0x12, 0x08, 0x10, 0x10, 0x08, 0xE0, 0x07, 0x00, 0x00};
|
||||
|
||||
const unsigned char jack_o_lantern[] PROGMEM = {0xC0, 0x00, 0x80, 0x01, 0xB8, 0x1D, 0xC4, 0x23, 0x22, 0x44, 0x05,
|
||||
0xA0, 0x31, 0x8C, 0x51, 0x8A, 0x61, 0x86, 0x09, 0x90, 0xB9, 0x9D,
|
||||
0x49, 0x92, 0xB2, 0x4D, 0x42, 0x42, 0x04, 0x20, 0xF8, 0x1F};
|
||||
|
||||
const unsigned char ghost[] PROGMEM = {0xC0, 0x03, 0xF0, 0x0F, 0xF8, 0x1F, 0xDC, 0x3B, 0xBC, 0x3D, 0xDF,
|
||||
0xFB, 0xFF, 0xFF, 0x1F, 0xF8, 0x1E, 0x78, 0x1C, 0x38, 0x3C, 0x3C,
|
||||
0xFC, 0x3F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0x8C, 0x31};
|
||||
|
||||
const unsigned char skull[] PROGMEM = {0xE0, 0x07, 0xF8, 0x1F, 0xFC, 0x3F, 0xFE, 0x7F, 0xFE, 0x7F, 0xC7,
|
||||
0xE3, 0x87, 0xE1, 0x87, 0xE1, 0x8F, 0xF1, 0xFE, 0x7F, 0x7C, 0x3E,
|
||||
0xFC, 0x3F, 0xFC, 0x3F, 0xFC, 0x3F, 0xF8, 0x1F, 0xB0, 0x0D};
|
||||
|
||||
const unsigned char vomiting[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x04, 0x20, 0x22,
|
||||
0x44, 0x42, 0x42, 0x22, 0x44, 0x02, 0x40, 0x02, 0x40, 0xC2, 0x43,
|
||||
0x64, 0x26, 0x64, 0x26, 0x68, 0x16, 0x50, 0x0A, 0xF8, 0x1F};
|
||||
|
||||
const unsigned char cool[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0xFC, 0x3F, 0xFA,
|
||||
0x5F, 0x72, 0x4E, 0x02, 0x40, 0x12, 0x48, 0x12, 0x48, 0x22, 0x44,
|
||||
0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||
|
||||
const unsigned char shortcake[] PROGMEM = {0x00, 0x00, 0x00, 0x0F, 0x80, 0x3F, 0xE0, 0xFC, 0xE0, 0xE1, 0xF0,
|
||||
0xB8, 0x10, 0x87, 0xC8, 0x80, 0x3C, 0xE0, 0x06, 0x98, 0x02, 0xC7,
|
||||
0xE2, 0x30, 0x1A, 0x0E, 0xC6, 0x01, 0x32, 0x00, 0x0E, 0x00};
|
||||
|
||||
const unsigned char caution[] PROGMEM = {0x00, 0x00, 0x80, 0x01, 0xC0, 0x03, 0xC0, 0x03, 0x60, 0x06, 0x60,
|
||||
0x06, 0x70, 0x0E, 0x70, 0x0E, 0x78, 0x1E, 0x78, 0x1E, 0x7C, 0x3E,
|
||||
0xFC, 0x3F, 0x7E, 0x7E, 0x7E, 0x7E, 0xFC, 0x3F, 0x00, 0x00};
|
||||
|
||||
const unsigned char clipboard[] PROGMEM = {0xC0, 0x03, 0x7E, 0x7E, 0xC2, 0x43, 0xFA, 0x5F, 0x0A, 0x5B, 0xFA,
|
||||
0x5F, 0x8A, 0x54, 0xFA, 0x5F, 0x4A, 0x58, 0xFA, 0x5F, 0x2A, 0x51,
|
||||
0xFA, 0x5F, 0x0A, 0x59, 0xFA, 0x5F, 0x02, 0x40, 0xFE, 0x7F};
|
||||
|
||||
const unsigned char snowflake[] PROGMEM = {0x00, 0x00, 0x40, 0x01, 0x88, 0x08, 0x8C, 0x18, 0xD0, 0x05, 0x60,
|
||||
0x03, 0x32, 0x26, 0x1C, 0x1C, 0x32, 0x26, 0x60, 0x03, 0xD0, 0x05,
|
||||
0x8C, 0x18, 0x88, 0x08, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
const unsigned char drop[] PROGMEM = {0x00, 0x00, 0x00, 0x01, 0x80, 0x03, 0xC0, 0x07, 0xE0, 0x0F, 0xE0,
|
||||
0x0F, 0xF0, 0x1F, 0xF0, 0x1F, 0xF8, 0x3F, 0xF8, 0x3F, 0xF8, 0x3F,
|
||||
0xF8, 0x3F, 0xF0, 0x1F, 0xE0, 0x0F, 0x80, 0x03, 0x00, 0x00};
|
||||
|
||||
const unsigned char thermometer[] PROGMEM = {0x00, 0x00, 0x0C, 0x00, 0x16, 0x00, 0x2E, 0x00, 0x5C, 0x00, 0xB8,
|
||||
0x00, 0x70, 0x01, 0xE0, 0x02, 0xC0, 0x05, 0x80, 0x3B, 0x00, 0x47,
|
||||
0x00, 0xBE, 0x00, 0x9E, 0x00, 0xBE, 0x00, 0x7C, 0x00, 0x38};
|
||||
|
||||
const unsigned char sun_behind_raincloud[] PROGMEM = {0xC0, 0x03, 0x20, 0x04, 0x10, 0x0E, 0x38, 0x1F, 0xFC, 0x37, 0xEE,
|
||||
0x77, 0xDE, 0x7B, 0x3E, 0x7C, 0xFC, 0x3F, 0x00, 0x00, 0x48, 0x12,
|
||||
0x48, 0x12, 0x24, 0x09, 0x24, 0x09, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
const unsigned char sun_behind_cloud[] PROGMEM = {0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x04, 0x0E, 0x3C, 0x1B, 0xFC,
|
||||
0x3B, 0xFE, 0x7B, 0xFA, 0x7B, 0xF6, 0x7D, 0x0C, 0x3E, 0xF8, 0x1F,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
const unsigned char cloud_with_snow[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x38, 0x1F, 0xFC, 0x3F, 0xFE,
|
||||
0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFC, 0x3F, 0x00, 0x00, 0x08, 0x02,
|
||||
0x40, 0x10, 0x00, 0x00, 0x24, 0x09, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
const unsigned char cloud_with_lightning[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x38, 0x1F, 0xFC, 0x3F, 0xFE,
|
||||
0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFC, 0x3F, 0x00, 0x01, 0x80, 0x01,
|
||||
0x80, 0x01, 0xC0, 0x07, 0x00, 0x03, 0x00, 0x03, 0x00, 0x01};
|
||||
|
||||
const unsigned char cloud_with_lightning_rain[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x38, 0x1F, 0xFC, 0x3F, 0xFE,
|
||||
0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFC, 0x3F, 0x00, 0x01, 0x90, 0x21,
|
||||
0x90, 0x21, 0xC8, 0x17, 0x08, 0x13, 0x00, 0x03, 0x00, 0x01};
|
||||
|
||||
const unsigned char wind_face[] PROGMEM = {0xFF, 0x00, 0x01, 0x01, 0x01, 0x01, 0xF9, 0x00, 0xF9, 0x01, 0xD9,
|
||||
0x01, 0x99, 0x01, 0xF9, 0x01, 0xF9, 0x33, 0xFD, 0x4B, 0xFD, 0x85,
|
||||
0xFD, 0x9A, 0xFD, 0x75, 0xFD, 0x09, 0xFD, 0x01, 0xFF, 0x00};
|
||||
|
||||
const unsigned char new_moon[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x04, 0x20, 0x02,
|
||||
0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40,
|
||||
0x04, 0x20, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||
|
||||
const unsigned char waxing_crescent_moon[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x1F, 0x04, 0x3E, 0x04, 0x3C, 0x02,
|
||||
0x78, 0x02, 0x78, 0x02, 0x78, 0x02, 0x78, 0x02, 0x78, 0x02, 0x78,
|
||||
0x04, 0x3C, 0x04, 0x3E, 0x18, 0x1F, 0xE0, 0x07, 0x00, 0x00};
|
||||
|
||||
const unsigned char first_quarter_moon[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x1F, 0x04, 0x3F, 0x04, 0x3F, 0x02,
|
||||
0x7F, 0x02, 0x7F, 0x02, 0x7F, 0x02, 0x7F, 0x02, 0x7F, 0x02, 0x7F,
|
||||
0x04, 0x3F, 0x04, 0x3F, 0x18, 0x1F, 0xE0, 0x07, 0x00, 0x00};
|
||||
|
||||
const unsigned char waxing_gibbous_moon[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x1F, 0x84, 0x3F, 0xC4, 0x3F, 0xC2,
|
||||
0x7F, 0xC2, 0x7F, 0xC2, 0x7F, 0xC2, 0x7F, 0xC2, 0x7F, 0xC2, 0x7F,
|
||||
0xC4, 0x3F, 0x84, 0x3F, 0x18, 0x1F, 0xE0, 0x07, 0x00, 0x00};
|
||||
|
||||
const unsigned char full_moon[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0xF8, 0x1F, 0xFC, 0x3F, 0xFC, 0x3F, 0xFE,
|
||||
0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F,
|
||||
0xFC, 0x3F, 0xFC, 0x3F, 0xF8, 0x1F, 0xE0, 0x07, 0x00, 0x00};
|
||||
|
||||
const unsigned char waning_gibbous_moon[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0xF8, 0x18, 0xFC, 0x21, 0xFC, 0x23, 0xFE,
|
||||
0x43, 0xFE, 0x43, 0xFE, 0x43, 0xFE, 0x43, 0xFE, 0x43, 0xFE, 0x43,
|
||||
0xFC, 0x23, 0xFC, 0x21, 0xF8, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||
|
||||
const unsigned char last_quarter_moon[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0xF8, 0x18, 0xFC, 0x20, 0xFC, 0x20, 0xFE,
|
||||
0x40, 0xFE, 0x40, 0xFE, 0x40, 0xFE, 0x40, 0xFE, 0x40, 0xFE, 0x40,
|
||||
0xFC, 0x20, 0xFC, 0x20, 0xF8, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||
|
||||
const unsigned char waning_crescent_moon[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0xF8, 0x18, 0x7C, 0x20, 0x3C, 0x20, 0x1E,
|
||||
0x40, 0x1E, 0x40, 0x1E, 0x40, 0x1E, 0x40, 0x1E, 0x40, 0x1E, 0x40,
|
||||
0x3C, 0x20, 0x7C, 0x20, 0xF8, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||
|
||||
const unsigned char first_quarter_moon_face[] PROGMEM = {0x00, 0x0F, 0x00, 0x12, 0x00, 0x24, 0x00, 0x44, 0x00, 0x48, 0x00,
|
||||
0x88, 0x00, 0x84, 0x80, 0x93, 0x80, 0x80, 0x03, 0x81, 0x8D, 0x80,
|
||||
0x71, 0x40, 0x82, 0x41, 0x02, 0x20, 0x0C, 0x18, 0xF0, 0x07};
|
||||
|
||||
const unsigned char peach[] PROGMEM = {0x70, 0x0F, 0x88, 0x10, 0x78, 0x1F, 0x88, 0x11, 0x04, 0x22, 0x02,
|
||||
0x44, 0x02, 0x44, 0x02, 0x44, 0x02, 0x44, 0x02, 0x42, 0x02, 0x40,
|
||||
0x04, 0x20, 0x04, 0x20, 0x08, 0x10, 0x30, 0x0C, 0xC0, 0x03};
|
||||
|
||||
const unsigned char turkey[] PROGMEM = {0x00, 0x00, 0x38, 0x00, 0x44, 0x38, 0x56, 0x54, 0x45, 0x52, 0xE2,
|
||||
0x21, 0x2C, 0x56, 0x14, 0x58, 0x0A, 0x37, 0x86, 0x68, 0x82, 0x50,
|
||||
0x82, 0x20, 0x04, 0x41, 0xF8, 0x7F, 0x40, 0x02, 0xF0, 0x07};
|
||||
|
||||
const unsigned char turkey_leg[] PROGMEM = {0x0C, 0x00, 0x1E, 0x00, 0x1F, 0x00, 0x2F, 0x00, 0x46, 0x00, 0x88,
|
||||
0x01, 0x10, 0x0E, 0x20, 0x30, 0x20, 0x40, 0x40, 0x40, 0x40, 0x80,
|
||||
0x40, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x43, 0x00, 0x3C};
|
||||
|
||||
const unsigned char south_west_arrow[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1C, 0x00, 0x3E, 0x00,
|
||||
0x1F, 0x80, 0x0F, 0xC2, 0x07, 0xE6, 0x03, 0xFE, 0x01, 0xFE, 0x00,
|
||||
0x7E, 0x00, 0x7E, 0x00, 0xFE, 0x00, 0xFE, 0x01, 0x00, 0x00};
|
||||
|
||||
const unsigned char south_east_arrow[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x38, 0x00, 0x7C, 0x00, 0xF8,
|
||||
0x00, 0xF0, 0x01, 0xE0, 0x43, 0xC0, 0x67, 0x80, 0x7F, 0x00, 0x7F,
|
||||
0x00, 0x7E, 0x00, 0x7E, 0x00, 0x7F, 0x80, 0x7F, 0x00, 0x00};
|
||||
|
||||
const unsigned char north_west_arrow[] PROGMEM = {0x00, 0x00, 0xFE, 0x01, 0xFE, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0xFE,
|
||||
0x00, 0xFE, 0x01, 0xE6, 0x03, 0xC2, 0x07, 0x80, 0x0F, 0x00, 0x1F,
|
||||
0x00, 0x3E, 0x00, 0x1C, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
const unsigned char north_east_arrow[] PROGMEM = {0x00, 0x00, 0x80, 0x7F, 0x00, 0x7F, 0x00, 0x7E, 0x00, 0x7E, 0x00,
|
||||
0x7F, 0x80, 0x7F, 0xC0, 0x67, 0xE0, 0x43, 0xF0, 0x01, 0xF8, 0x00,
|
||||
0x7C, 0x00, 0x38, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
const unsigned char downwards_arrow[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0,
|
||||
0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xFC, 0x3F,
|
||||
0xF8, 0x1F, 0xF0, 0x0F, 0xE0, 0x07, 0xC0, 0x03, 0x80, 0x01};
|
||||
|
||||
const unsigned char leftwards_arrow[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x30, 0x00, 0x38, 0x00, 0x3C,
|
||||
0x00, 0xFE, 0x3F, 0xFF, 0x3F, 0xFF, 0x3F, 0xFE, 0x3F, 0x3C, 0x00,
|
||||
0x38, 0x00, 0x30, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
const unsigned char upwards_arrow[] PROGMEM = {0x80, 0x01, 0xC0, 0x03, 0xE0, 0x07, 0xF0, 0x0F, 0xF8, 0x1F, 0xFC,
|
||||
0x3F, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03,
|
||||
0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
const unsigned char rightwards_arrow[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x1C, 0x00,
|
||||
0x3C, 0xFC, 0x7F, 0xFC, 0xFF, 0xFC, 0xFF, 0xFC, 0x7F, 0x00, 0x3C,
|
||||
0x00, 0x1C, 0x00, 0x0C, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
const unsigned char strong[] PROGMEM = {0x38, 0x00, 0x44, 0x00, 0x62, 0x00, 0x42, 0x00, 0x42, 0x00, 0x3A,
|
||||
0x00, 0x11, 0x3C, 0x11, 0x42, 0xD1, 0x81, 0x31, 0x82, 0x11, 0x82,
|
||||
0x21, 0x80, 0x01, 0x80, 0x01, 0x80, 0x02, 0x40, 0xFC, 0x3F};
|
||||
|
||||
const unsigned char check_mark[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x70, 0x00, 0x3C, 0x00,
|
||||
0x1E, 0x00, 0x0F, 0x80, 0x07, 0xC3, 0x03, 0xEE, 0x03, 0xFC, 0x01,
|
||||
0xF8, 0x00, 0xF0, 0x00, 0x70, 0x00, 0x60, 0x00, 0x20, 0x00};
|
||||
|
||||
const unsigned char house[] PROGMEM = {0x80, 0x01, 0x5C, 0x02, 0x34, 0x04, 0x14, 0x08, 0x0C, 0x10, 0x04,
|
||||
0x20, 0x02, 0x40, 0xFF, 0xFF, 0x02, 0x40, 0x7A, 0x5F, 0x4A, 0x55,
|
||||
0x4A, 0x5F, 0x6A, 0x55, 0x4A, 0x5F, 0x4A, 0x40, 0xFE, 0x7F};
|
||||
|
||||
const unsigned char shrug[] PROGMEM = {0xC0, 0x03, 0x20, 0x04, 0x10, 0x08, 0x50, 0x0A, 0x10, 0x08, 0x90,
|
||||
0x09, 0x27, 0xE4, 0x49, 0x92, 0xAA, 0x55, 0x16, 0x68, 0x12, 0x48,
|
||||
0x02, 0x40, 0x02, 0x40, 0x0C, 0x30, 0x08, 0x10, 0xF8, 0x1F};
|
||||
|
||||
const unsigned char eyes[] PROGMEM = {0x00, 0x00, 0x3C, 0x3C, 0x42, 0x42, 0x81, 0x81, 0x85, 0x85, 0x8F,
|
||||
0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
|
||||
0x85, 0x85, 0x81, 0x81, 0x42, 0x42, 0x3C, 0x3C, 0x00, 0x00};
|
||||
|
||||
const unsigned char eye[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x07, 0xF8, 0x1F, 0xF4,
|
||||
0x2F, 0x7A, 0x5E, 0x39, 0x9C, 0x39, 0x9C, 0x7A, 0x5E, 0xF4, 0x2F,
|
||||
0xF8, 0x1F, 0xE0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
#endif
|
||||
|
||||
} // namespace graphics
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -22,33 +22,33 @@ extern const int numEmotes;
|
||||
extern const unsigned char thumbup[] PROGMEM;
|
||||
extern const unsigned char thumbdown[] PROGMEM;
|
||||
|
||||
#define Smiling_Eyes_height 16
|
||||
#define Smiling_Eyes_width 16
|
||||
extern const unsigned char Smiling_Eyes[] PROGMEM;
|
||||
#define smiling_eyes_height 16
|
||||
#define smiling_eyes_width 16
|
||||
extern const unsigned char smiling_eyes[] PROGMEM;
|
||||
|
||||
#define Grinning_height 16
|
||||
#define Grinning_width 16
|
||||
extern const unsigned char Grinning[] PROGMEM;
|
||||
#define grinning_height 16
|
||||
#define grinning_width 16
|
||||
extern const unsigned char grinning[] PROGMEM;
|
||||
|
||||
#define Slightly_Smiling_height 16
|
||||
#define Slightly_Smiling_width 16
|
||||
extern const unsigned char Slightly_Smiling[] PROGMEM;
|
||||
#define slightly_smiling_height 16
|
||||
#define slightly_smiling_width 16
|
||||
extern const unsigned char slightly_smiling[] PROGMEM;
|
||||
|
||||
#define Winking_Face_height 16
|
||||
#define Winking_Face_width 16
|
||||
extern const unsigned char Winking_Face[] PROGMEM;
|
||||
#define winking_face_height 16
|
||||
#define winking_face_width 16
|
||||
extern const unsigned char winking_face[] PROGMEM;
|
||||
|
||||
#define Grinning_Smiling_Eyes_height 16
|
||||
#define Grinning_Smiling_Eyes_width 16
|
||||
extern const unsigned char Grinning_Smiling_Eyes[] PROGMEM;
|
||||
#define grinning_smiling_eyes_height 16
|
||||
#define grinning_smiling_eyes_width 16
|
||||
extern const unsigned char grinning_smiling_eyes[] PROGMEM;
|
||||
|
||||
#define heart_smile_height 16
|
||||
#define heart_smile_width 16
|
||||
extern const unsigned char heart_smile[] PROGMEM;
|
||||
|
||||
#define Heart_eyes_height 16
|
||||
#define Heart_eyes_width 16
|
||||
extern const unsigned char Heart_eyes[] PROGMEM;
|
||||
#define heart_eyes_height 16
|
||||
#define heart_eyes_width 16
|
||||
extern const unsigned char heart_eyes[] PROGMEM;
|
||||
|
||||
#define question_height 16
|
||||
#define question_width 16
|
||||
@@ -62,21 +62,21 @@ extern const unsigned char bang[] PROGMEM;
|
||||
#define haha_width 16
|
||||
extern const unsigned char haha[] PROGMEM;
|
||||
|
||||
#define ROFL_height 16
|
||||
#define ROFL_width 16
|
||||
extern const unsigned char ROFL[] PROGMEM;
|
||||
#define rofl_height 16
|
||||
#define rofl_width 16
|
||||
extern const unsigned char rofl[] PROGMEM;
|
||||
|
||||
#define Smiling_Closed_Eyes_height 16
|
||||
#define Smiling_Closed_Eyes_width 16
|
||||
extern const unsigned char Smiling_Closed_Eyes[] PROGMEM;
|
||||
#define smiling_closed_eyes_height 16
|
||||
#define smiling_closed_eyes_width 16
|
||||
extern const unsigned char smiling_closed_eyes[] PROGMEM;
|
||||
|
||||
#define Grinning_SmilingEyes2_height 16
|
||||
#define Grinning_SmilingEyes2_width 16
|
||||
extern const unsigned char Grinning_SmilingEyes2[] PROGMEM;
|
||||
#define grinning_smiling_eyes_2_height 16
|
||||
#define grinning_smiling_eyes_2_width 16
|
||||
extern const unsigned char grinning_smiling_eyes_2[] PROGMEM;
|
||||
|
||||
#define Loudly_Crying_Face_height 16
|
||||
#define Loudly_Crying_Face_width 16
|
||||
extern const unsigned char Loudly_Crying_Face[] PROGMEM;
|
||||
#define loudly_crying_face_height 16
|
||||
#define loudly_crying_face_width 16
|
||||
extern const unsigned char loudly_crying_face[] PROGMEM;
|
||||
|
||||
#define wave_icon_height 16
|
||||
#define wave_icon_width 16
|
||||
@@ -126,21 +126,21 @@ extern const unsigned char bell_icon[] PROGMEM;
|
||||
#define cookie_height 16
|
||||
extern const unsigned char cookie[] PROGMEM;
|
||||
|
||||
#define Fire_width 16
|
||||
#define Fire_height 16
|
||||
extern const unsigned char Fire[] PROGMEM;
|
||||
#define fire_width 16
|
||||
#define fire_height 16
|
||||
extern const unsigned char fire[] PROGMEM;
|
||||
|
||||
#define peace_sign_width 16
|
||||
#define peace_sign_height 16
|
||||
extern const unsigned char peace_sign[] PROGMEM;
|
||||
|
||||
#define Praying_width 16
|
||||
#define Praying_height 16
|
||||
extern const unsigned char Praying[] PROGMEM;
|
||||
#define praying_width 16
|
||||
#define praying_height 16
|
||||
extern const unsigned char praying[] PROGMEM;
|
||||
|
||||
#define Sparkles_width 16
|
||||
#define Sparkles_height 16
|
||||
extern const unsigned char Sparkles[] PROGMEM;
|
||||
#define sparkles_width 16
|
||||
#define sparkles_height 16
|
||||
extern const unsigned char sparkles[] PROGMEM;
|
||||
|
||||
#define clown_width 16
|
||||
#define clown_height 16
|
||||
@@ -161,6 +161,178 @@ extern const unsigned char bowling[] PROGMEM;
|
||||
#define vulcan_salute_width 16
|
||||
#define vulcan_salute_height 16
|
||||
extern const unsigned char vulcan_salute[] PROGMEM;
|
||||
|
||||
#define jack_o_lantern_width 16
|
||||
#define jack_o_lantern_height 16
|
||||
extern const unsigned char jack_o_lantern[] PROGMEM;
|
||||
|
||||
#define ghost_width 16
|
||||
#define ghost_height 16
|
||||
extern const unsigned char ghost[] PROGMEM;
|
||||
|
||||
#define skull_width 16
|
||||
#define skull_height 16
|
||||
extern const unsigned char skull[] PROGMEM;
|
||||
|
||||
#define vomiting_width 16
|
||||
#define vomiting_height 16
|
||||
extern const unsigned char vomiting[] PROGMEM;
|
||||
|
||||
#define cool_width 16
|
||||
#define cool_height 16
|
||||
extern const unsigned char cool[] PROGMEM;
|
||||
|
||||
#define shortcake_width 16
|
||||
#define shortcake_height 16
|
||||
extern const unsigned char shortcake[] PROGMEM;
|
||||
|
||||
#define caution_width 16
|
||||
#define caution_height 16
|
||||
extern const unsigned char caution[] PROGMEM;
|
||||
|
||||
#define clipboard_width 16
|
||||
#define clipboard_height 16
|
||||
extern const unsigned char clipboard[] PROGMEM;
|
||||
|
||||
#define snowflake_width 16
|
||||
#define snowflake_height 16
|
||||
extern const unsigned char snowflake[] PROGMEM;
|
||||
|
||||
#define drop_width 16
|
||||
#define drop_height 16
|
||||
extern const unsigned char drop[] PROGMEM;
|
||||
|
||||
#define thermometer_width 16
|
||||
#define thermometer_height 16
|
||||
extern const unsigned char thermometer[] PROGMEM;
|
||||
|
||||
#define sun_behind_raincloud_width 16
|
||||
#define sun_behind_raincloud_height 16
|
||||
extern const unsigned char sun_behind_raincloud[] PROGMEM;
|
||||
|
||||
#define sun_behind_cloud_width 16
|
||||
#define sun_behind_cloud_height 16
|
||||
extern const unsigned char sun_behind_cloud[] PROGMEM;
|
||||
|
||||
#define cloud_with_snow_width 16
|
||||
#define cloud_with_snow_height 16
|
||||
extern const unsigned char cloud_with_snow[] PROGMEM;
|
||||
|
||||
#define cloud_with_lightning_width 16
|
||||
#define cloud_with_lightning_height 16
|
||||
extern const unsigned char cloud_with_lightning[] PROGMEM;
|
||||
|
||||
#define cloud_with_lightning_rain_width 16
|
||||
#define cloud_with_lightning_rain_height 16
|
||||
extern const unsigned char cloud_with_lightning_rain[] PROGMEM;
|
||||
|
||||
#define wind_face_width 16
|
||||
#define wind_face_height 16
|
||||
extern const unsigned char wind_face[] PROGMEM;
|
||||
|
||||
#define new_moon_width 16
|
||||
#define new_moon_height 16
|
||||
extern const unsigned char new_moon[] PROGMEM;
|
||||
|
||||
#define waxing_crescent_moon_width 16
|
||||
#define waxing_crescent_moon_height 16
|
||||
extern const unsigned char waxing_crescent_moon[] PROGMEM;
|
||||
|
||||
#define first_quarter_moon_width 16
|
||||
#define first_quarter_moon_height 16
|
||||
extern const unsigned char first_quarter_moon[] PROGMEM;
|
||||
|
||||
#define waxing_gibbous_moon_width 16
|
||||
#define waxing_gibbous_moon_height 16
|
||||
extern const unsigned char waxing_gibbous_moon[] PROGMEM;
|
||||
|
||||
#define full_moon_width 16
|
||||
#define full_moon_height 16
|
||||
extern const unsigned char full_moon[] PROGMEM;
|
||||
|
||||
#define waning_gibbous_moon_width 16
|
||||
#define waning_gibbous_moon_height 16
|
||||
extern const unsigned char waning_gibbous_moon[] PROGMEM;
|
||||
|
||||
#define last_quarter_moon_width 16
|
||||
#define last_quarter_moon_height 16
|
||||
extern const unsigned char last_quarter_moon[] PROGMEM;
|
||||
|
||||
#define waning_crescent_moon_width 16
|
||||
#define waning_crescent_moon_height 16
|
||||
extern const unsigned char waning_crescent_moon[] PROGMEM;
|
||||
|
||||
#define first_quarter_moon_face_width 16
|
||||
#define first_quarter_moon_face_height 16
|
||||
extern const unsigned char first_quarter_moon_face[] PROGMEM;
|
||||
|
||||
#define peach_width 16
|
||||
#define peach_height 16
|
||||
extern const unsigned char peach[] PROGMEM;
|
||||
|
||||
#define turkey_width 16
|
||||
#define turkey_height 16
|
||||
extern const unsigned char turkey[] PROGMEM;
|
||||
|
||||
#define turkey_leg_width 16
|
||||
#define turkey_leg_height 16
|
||||
extern const unsigned char turkey_leg[] PROGMEM;
|
||||
|
||||
#define south_west_arrow_width 16
|
||||
#define south_west_arrow_height 16
|
||||
extern const unsigned char south_west_arrow[] PROGMEM;
|
||||
|
||||
#define south_east_arrow_width 16
|
||||
#define south_east_arrow_height 16
|
||||
extern const unsigned char south_east_arrow[] PROGMEM;
|
||||
|
||||
#define north_west_arrow_width 16
|
||||
#define north_west_arrow_height 16
|
||||
extern const unsigned char north_west_arrow[] PROGMEM;
|
||||
|
||||
#define north_east_arrow_width 16
|
||||
#define north_east_arrow_height 16
|
||||
extern const unsigned char north_east_arrow[] PROGMEM;
|
||||
|
||||
#define downwards_arrow_width 16
|
||||
#define downwards_arrow_height 16
|
||||
extern const unsigned char downwards_arrow[] PROGMEM;
|
||||
|
||||
#define leftwards_arrow_width 16
|
||||
#define leftwards_arrow_height 16
|
||||
extern const unsigned char leftwards_arrow[] PROGMEM;
|
||||
|
||||
#define upwards_arrow_width 16
|
||||
#define upwards_arrow_height 16
|
||||
extern const unsigned char upwards_arrow[] PROGMEM;
|
||||
|
||||
#define rightwards_arrow_width 16
|
||||
#define rightwards_arrow_height 16
|
||||
extern const unsigned char rightwards_arrow[] PROGMEM;
|
||||
|
||||
#define strong_width 16
|
||||
#define strong_height 16
|
||||
extern const unsigned char strong[] PROGMEM;
|
||||
|
||||
#define check_mark_width 16
|
||||
#define check_mark_height 16
|
||||
extern const unsigned char check_mark[] PROGMEM;
|
||||
|
||||
#define house_width 16
|
||||
#define house_height 16
|
||||
extern const unsigned char house[] PROGMEM;
|
||||
|
||||
#define shrug_width 16
|
||||
#define shrug_height 16
|
||||
extern const unsigned char shrug[] PROGMEM;
|
||||
|
||||
#define eyes_width 16
|
||||
#define eyes_height 16
|
||||
extern const unsigned char eyes[] PROGMEM;
|
||||
|
||||
#define eye_width 16
|
||||
#define eye_height 16
|
||||
extern const unsigned char eye[] PROGMEM;
|
||||
#endif // EXCLUDE_EMOJI
|
||||
|
||||
} // namespace graphics
|
||||
} // namespace graphics
|
||||
|
||||
@@ -88,8 +88,14 @@ class Applet : public GFX
|
||||
virtual void onForeground() {}
|
||||
virtual void onBackground() {}
|
||||
virtual void onShutdown() {}
|
||||
virtual void onButtonShortPress() {} // (System Applets only)
|
||||
virtual void onButtonLongPress() {} // (System Applets only)
|
||||
virtual void onButtonShortPress() {}
|
||||
virtual void onButtonLongPress() {}
|
||||
virtual void onExitShort() {}
|
||||
virtual void onExitLong() {}
|
||||
virtual void onNavUp() {}
|
||||
virtual void onNavDown() {}
|
||||
virtual void onNavLeft() {}
|
||||
virtual void onNavRight() {}
|
||||
|
||||
virtual bool approveNotification(Notification &n); // Allow an applet to veto a notification
|
||||
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
#ifdef MESHTASTIC_INCLUDE_INKHUD
|
||||
|
||||
#include "./AlignStickApplet.h"
|
||||
|
||||
using namespace NicheGraphics;
|
||||
|
||||
InkHUD::AlignStickApplet::AlignStickApplet()
|
||||
{
|
||||
if (!settings->joystick.aligned)
|
||||
bringToForeground();
|
||||
}
|
||||
|
||||
void InkHUD::AlignStickApplet::onRender()
|
||||
{
|
||||
setFont(fontMedium);
|
||||
printAt(0, 0, "Align Joystick:");
|
||||
setFont(fontSmall);
|
||||
std::string instructions = "Move joystick in the direction indicated";
|
||||
printWrapped(0, fontMedium.lineHeight() * 1.5, width(), instructions);
|
||||
|
||||
// Size of the region in which the joystick graphic should fit
|
||||
uint16_t joyXLimit = X(0.8);
|
||||
uint16_t contentH = fontMedium.lineHeight() * 1.5 + fontSmall.lineHeight() * 1;
|
||||
if (getTextWidth(instructions) > width())
|
||||
contentH += fontSmall.lineHeight();
|
||||
uint16_t freeY = height() - contentH - fontSmall.lineHeight() * 1.2;
|
||||
uint16_t joyYLimit = freeY * 0.8;
|
||||
|
||||
// Use the shorter of the two
|
||||
uint16_t joyWidth = joyXLimit < joyYLimit ? joyXLimit : joyYLimit;
|
||||
|
||||
// Center the joystick graphic
|
||||
uint16_t centerX = X(0.5);
|
||||
uint16_t centerY = contentH + freeY * 0.5;
|
||||
|
||||
// Draw joystick graphic
|
||||
drawStick(centerX, centerY, joyWidth);
|
||||
|
||||
setFont(fontSmall);
|
||||
printAt(X(0.5), Y(1.0) - fontSmall.lineHeight() * 0.2, "Long press to skip", CENTER, BOTTOM);
|
||||
}
|
||||
|
||||
// Draw a scalable joystick graphic
|
||||
void InkHUD::AlignStickApplet::drawStick(uint16_t centerX, uint16_t centerY, uint16_t width)
|
||||
{
|
||||
if (width < 9) // too small to draw
|
||||
return;
|
||||
|
||||
else if (width < 40) { // only draw up arrow
|
||||
uint16_t chamfer = width < 20 ? 1 : 2;
|
||||
|
||||
// Draw filled up arrow
|
||||
drawDirection(centerX, centerY - width / 4, Direction::UP, width, chamfer, BLACK);
|
||||
|
||||
} else { // large enough to draw the full thing
|
||||
uint16_t chamfer = width < 80 ? 1 : 2;
|
||||
uint16_t stroke = 3; // pixels
|
||||
uint16_t arrowW = width * 0.22;
|
||||
uint16_t hollowW = arrowW - stroke * 2;
|
||||
|
||||
// Draw center circle
|
||||
fillCircle((int16_t)centerX, (int16_t)centerY, (int16_t)(width * 0.2), BLACK);
|
||||
fillCircle((int16_t)centerX, (int16_t)centerY, (int16_t)(width * 0.2) - stroke, WHITE);
|
||||
|
||||
// Draw filled up arrow
|
||||
drawDirection(centerX, centerY - width / 2, Direction::UP, arrowW, chamfer, BLACK);
|
||||
|
||||
// Draw down arrow
|
||||
drawDirection(centerX, centerY + width / 2, Direction::DOWN, arrowW, chamfer, BLACK);
|
||||
drawDirection(centerX, centerY + width / 2 - stroke, Direction::DOWN, hollowW, 0, WHITE);
|
||||
|
||||
// Draw left arrow
|
||||
drawDirection(centerX - width / 2, centerY, Direction::LEFT, arrowW, chamfer, BLACK);
|
||||
drawDirection(centerX - width / 2 + stroke, centerY, Direction::LEFT, hollowW, 0, WHITE);
|
||||
|
||||
// Draw right arrow
|
||||
drawDirection(centerX + width / 2, centerY, Direction::RIGHT, arrowW, chamfer, BLACK);
|
||||
drawDirection(centerX + width / 2 - stroke, centerY, Direction::RIGHT, hollowW, 0, WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw a scalable joystick direction arrow
|
||||
// a right-triangle with blunted tips
|
||||
/*
|
||||
_ <--point
|
||||
^ / \
|
||||
| / \
|
||||
size / \
|
||||
| / \
|
||||
v |_________|
|
||||
|
||||
*/
|
||||
void InkHUD::AlignStickApplet::drawDirection(uint16_t pointX, uint16_t pointY, Direction direction, uint16_t size,
|
||||
uint16_t chamfer, Color color)
|
||||
{
|
||||
uint16_t chamferW = chamfer * 2 + 1;
|
||||
uint16_t triangleW = size - chamferW;
|
||||
|
||||
// Draw arrow
|
||||
switch (direction) {
|
||||
case Direction::UP:
|
||||
fillRect(pointX - chamfer, pointY, chamferW, triangleW, color);
|
||||
fillRect(pointX - chamfer - triangleW, pointY + triangleW, chamferW + triangleW * 2, chamferW, color);
|
||||
fillTriangle(pointX - chamfer, pointY, pointX - chamfer - triangleW, pointY + triangleW, pointX - chamfer,
|
||||
pointY + triangleW, color);
|
||||
fillTriangle(pointX + chamfer, pointY, pointX + chamfer + triangleW, pointY + triangleW, pointX + chamfer,
|
||||
pointY + triangleW, color);
|
||||
break;
|
||||
case Direction::DOWN:
|
||||
fillRect(pointX - chamfer, pointY - triangleW + 1, chamferW, triangleW, color);
|
||||
fillRect(pointX - chamfer - triangleW, pointY - size + 1, chamferW + triangleW * 2, chamferW, color);
|
||||
fillTriangle(pointX - chamfer, pointY, pointX - chamfer - triangleW, pointY - triangleW, pointX - chamfer,
|
||||
pointY - triangleW, color);
|
||||
fillTriangle(pointX + chamfer, pointY, pointX + chamfer + triangleW, pointY - triangleW, pointX + chamfer,
|
||||
pointY - triangleW, color);
|
||||
break;
|
||||
case Direction::LEFT:
|
||||
fillRect(pointX, pointY - chamfer, triangleW, chamferW, color);
|
||||
fillRect(pointX + triangleW, pointY - chamfer - triangleW, chamferW, chamferW + triangleW * 2, color);
|
||||
fillTriangle(pointX, pointY - chamfer, pointX + triangleW, pointY - chamfer - triangleW, pointX + triangleW,
|
||||
pointY - chamfer, color);
|
||||
fillTriangle(pointX, pointY + chamfer, pointX + triangleW, pointY + chamfer + triangleW, pointX + triangleW,
|
||||
pointY + chamfer, color);
|
||||
break;
|
||||
case Direction::RIGHT:
|
||||
fillRect(pointX - triangleW + 1, pointY - chamfer, triangleW, chamferW, color);
|
||||
fillRect(pointX - size + 1, pointY - chamfer - triangleW, chamferW, chamferW + triangleW * 2, color);
|
||||
fillTriangle(pointX, pointY - chamfer, pointX - triangleW, pointY - chamfer - triangleW, pointX - triangleW,
|
||||
pointY - chamfer, color);
|
||||
fillTriangle(pointX, pointY + chamfer, pointX - triangleW, pointY + chamfer + triangleW, pointX - triangleW,
|
||||
pointY + chamfer, color);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void InkHUD::AlignStickApplet::onForeground()
|
||||
{
|
||||
// Prevent most other applets from requesting update, and skip their rendering entirely
|
||||
// Another system applet with a higher precedence can potentially ignore this
|
||||
SystemApplet::lockRendering = true;
|
||||
SystemApplet::lockRequests = true;
|
||||
|
||||
handleInput = true; // Intercept the button input for our applet
|
||||
}
|
||||
|
||||
void InkHUD::AlignStickApplet::onBackground()
|
||||
{
|
||||
// Allow normal update behavior to resume
|
||||
SystemApplet::lockRendering = false;
|
||||
SystemApplet::lockRequests = false;
|
||||
SystemApplet::handleInput = false;
|
||||
|
||||
// Need to force an update, as a polite request wouldn't be honored, seeing how we are now in the background
|
||||
// Usually, onBackground is followed by another applet's onForeground (which requests update), but not in this case
|
||||
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
|
||||
}
|
||||
|
||||
void InkHUD::AlignStickApplet::onButtonLongPress()
|
||||
{
|
||||
sendToBackground();
|
||||
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
|
||||
}
|
||||
|
||||
void InkHUD::AlignStickApplet::onExitLong()
|
||||
{
|
||||
sendToBackground();
|
||||
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
|
||||
}
|
||||
|
||||
void InkHUD::AlignStickApplet::onNavUp()
|
||||
{
|
||||
settings->joystick.aligned = true;
|
||||
|
||||
sendToBackground();
|
||||
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
|
||||
}
|
||||
|
||||
void InkHUD::AlignStickApplet::onNavDown()
|
||||
{
|
||||
inkhud->rotateJoystick(2); // 180 deg
|
||||
settings->joystick.aligned = true;
|
||||
|
||||
sendToBackground();
|
||||
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
|
||||
}
|
||||
|
||||
void InkHUD::AlignStickApplet::onNavLeft()
|
||||
{
|
||||
inkhud->rotateJoystick(3); // 270 deg
|
||||
settings->joystick.aligned = true;
|
||||
|
||||
sendToBackground();
|
||||
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
|
||||
}
|
||||
|
||||
void InkHUD::AlignStickApplet::onNavRight()
|
||||
{
|
||||
inkhud->rotateJoystick(1); // 90 deg
|
||||
settings->joystick.aligned = true;
|
||||
|
||||
sendToBackground();
|
||||
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,50 @@
|
||||
#ifdef MESHTASTIC_INCLUDE_INKHUD
|
||||
|
||||
/*
|
||||
|
||||
System Applet for manually aligning the joystick with the screen
|
||||
|
||||
should be run at startup if the joystick is enabled
|
||||
and not aligned to the screen
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "configuration.h"
|
||||
|
||||
#include "graphics/niche/InkHUD/SystemApplet.h"
|
||||
|
||||
namespace NicheGraphics::InkHUD
|
||||
{
|
||||
|
||||
class AlignStickApplet : public SystemApplet
|
||||
{
|
||||
public:
|
||||
AlignStickApplet();
|
||||
|
||||
void onRender() override;
|
||||
void onForeground() override;
|
||||
void onBackground() override;
|
||||
void onButtonLongPress() override;
|
||||
void onExitLong() override;
|
||||
void onNavUp() override;
|
||||
void onNavDown() override;
|
||||
void onNavLeft() override;
|
||||
void onNavRight() override;
|
||||
|
||||
protected:
|
||||
enum Direction {
|
||||
UP,
|
||||
DOWN,
|
||||
LEFT,
|
||||
RIGHT,
|
||||
};
|
||||
|
||||
void drawStick(uint16_t centerX, uint16_t centerY, uint16_t width);
|
||||
void drawDirection(uint16_t pointX, uint16_t pointY, Direction direction, uint16_t size, uint16_t chamfer, Color color);
|
||||
};
|
||||
|
||||
} // namespace NicheGraphics::InkHUD
|
||||
|
||||
#endif
|
||||
@@ -30,6 +30,7 @@ enum MenuAction {
|
||||
TOGGLE_AUTOSHOW_APPLET,
|
||||
SET_RECENTS,
|
||||
ROTATE,
|
||||
ALIGN_JOYSTICK,
|
||||
LAYOUT,
|
||||
TOGGLE_BATTERY_ICON,
|
||||
TOGGLE_NOTIFICATIONS,
|
||||
|
||||
@@ -178,6 +178,10 @@ void InkHUD::MenuApplet::execute(MenuItem item)
|
||||
inkhud->rotate();
|
||||
break;
|
||||
|
||||
case ALIGN_JOYSTICK:
|
||||
inkhud->openAlignStick();
|
||||
break;
|
||||
|
||||
case LAYOUT:
|
||||
// Todo: smarter incrementing of tile count
|
||||
settings->userTiles.count++;
|
||||
@@ -287,14 +291,17 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
|
||||
// items.push_back(MenuItem("Display Off", MenuPage::EXIT)); // TODO
|
||||
items.push_back(MenuItem("Save & Shut Down", MenuAction::SHUTDOWN));
|
||||
items.push_back(MenuItem("Exit", MenuPage::EXIT));
|
||||
previousPage = MenuPage::EXIT;
|
||||
break;
|
||||
|
||||
case SEND:
|
||||
populateSendPage();
|
||||
previousPage = MenuPage::ROOT;
|
||||
break;
|
||||
|
||||
case CANNEDMESSAGE_RECIPIENT:
|
||||
populateRecipientPage();
|
||||
previousPage = MenuPage::OPTIONS;
|
||||
break;
|
||||
|
||||
case OPTIONS:
|
||||
@@ -321,6 +328,8 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
|
||||
if (settings->userTiles.maxCount > 1)
|
||||
items.push_back(MenuItem("Layout", MenuAction::LAYOUT, MenuPage::OPTIONS));
|
||||
items.push_back(MenuItem("Rotate", MenuAction::ROTATE, MenuPage::OPTIONS));
|
||||
if (settings->joystick.enabled)
|
||||
items.push_back(MenuItem("Align Joystick", MenuAction::ALIGN_JOYSTICK, MenuPage::EXIT));
|
||||
items.push_back(MenuItem("Notifications", MenuAction::TOGGLE_NOTIFICATIONS, MenuPage::OPTIONS,
|
||||
&settings->optionalFeatures.notifications));
|
||||
items.push_back(MenuItem("Battery Icon", MenuAction::TOGGLE_BATTERY_ICON, MenuPage::OPTIONS,
|
||||
@@ -332,20 +341,24 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
|
||||
items.push_back(
|
||||
MenuItem("12-Hour Clock", MenuAction::TOGGLE_12H_CLOCK, MenuPage::OPTIONS, &config.display.use_12h_clock));
|
||||
items.push_back(MenuItem("Exit", MenuPage::EXIT));
|
||||
previousPage = MenuPage::ROOT;
|
||||
break;
|
||||
|
||||
case APPLETS:
|
||||
populateAppletPage();
|
||||
items.push_back(MenuItem("Exit", MenuPage::EXIT));
|
||||
previousPage = MenuPage::OPTIONS;
|
||||
break;
|
||||
|
||||
case AUTOSHOW:
|
||||
populateAutoshowPage();
|
||||
items.push_back(MenuItem("Exit", MenuPage::EXIT));
|
||||
previousPage = MenuPage::OPTIONS;
|
||||
break;
|
||||
|
||||
case RECENTS:
|
||||
populateRecentsPage();
|
||||
previousPage = MenuPage::OPTIONS;
|
||||
break;
|
||||
|
||||
case EXIT:
|
||||
@@ -479,12 +492,21 @@ void InkHUD::MenuApplet::onButtonShortPress()
|
||||
// Push the auto-close timer back
|
||||
OSThread::setIntervalFromNow(MENU_TIMEOUT_SEC * 1000UL);
|
||||
|
||||
// Move menu cursor to next entry, then update
|
||||
if (cursorShown)
|
||||
cursor = (cursor + 1) % items.size();
|
||||
else
|
||||
cursorShown = true;
|
||||
requestUpdate(Drivers::EInk::UpdateTypes::FAST);
|
||||
if (!settings->joystick.enabled) {
|
||||
// Move menu cursor to next entry, then update
|
||||
if (cursorShown)
|
||||
cursor = (cursor + 1) % items.size();
|
||||
else
|
||||
cursorShown = true;
|
||||
requestUpdate(Drivers::EInk::UpdateTypes::FAST);
|
||||
} else {
|
||||
if (cursorShown)
|
||||
execute(items.at(cursor));
|
||||
else
|
||||
showPage(MenuPage::EXIT);
|
||||
if (!wantsToRender())
|
||||
requestUpdate(Drivers::EInk::UpdateTypes::FAST);
|
||||
}
|
||||
}
|
||||
|
||||
void InkHUD::MenuApplet::onButtonLongPress()
|
||||
@@ -504,6 +526,62 @@ void InkHUD::MenuApplet::onButtonLongPress()
|
||||
requestUpdate(Drivers::EInk::UpdateTypes::FAST);
|
||||
}
|
||||
|
||||
void InkHUD::MenuApplet::onExitShort()
|
||||
{
|
||||
// Exit the menu
|
||||
showPage(MenuPage::EXIT);
|
||||
|
||||
requestUpdate(Drivers::EInk::UpdateTypes::FAST);
|
||||
}
|
||||
|
||||
void InkHUD::MenuApplet::onNavUp()
|
||||
{
|
||||
OSThread::setIntervalFromNow(MENU_TIMEOUT_SEC * 1000UL);
|
||||
|
||||
// Move menu cursor to previous entry, then update
|
||||
if (cursor == 0)
|
||||
cursor = items.size() - 1;
|
||||
else
|
||||
cursor--;
|
||||
|
||||
if (!cursorShown)
|
||||
cursorShown = true;
|
||||
|
||||
requestUpdate(Drivers::EInk::UpdateTypes::FAST);
|
||||
}
|
||||
|
||||
void InkHUD::MenuApplet::onNavDown()
|
||||
{
|
||||
OSThread::setIntervalFromNow(MENU_TIMEOUT_SEC * 1000UL);
|
||||
|
||||
// Move menu cursor to next entry, then update
|
||||
if (cursorShown)
|
||||
cursor = (cursor + 1) % items.size();
|
||||
else
|
||||
cursorShown = true;
|
||||
|
||||
requestUpdate(Drivers::EInk::UpdateTypes::FAST);
|
||||
}
|
||||
|
||||
void InkHUD::MenuApplet::onNavLeft()
|
||||
{
|
||||
OSThread::setIntervalFromNow(MENU_TIMEOUT_SEC * 1000UL);
|
||||
|
||||
// Go to the previous menu page
|
||||
showPage(previousPage);
|
||||
requestUpdate(Drivers::EInk::UpdateTypes::FAST);
|
||||
}
|
||||
|
||||
void InkHUD::MenuApplet::onNavRight()
|
||||
{
|
||||
OSThread::setIntervalFromNow(MENU_TIMEOUT_SEC * 1000UL);
|
||||
|
||||
if (cursorShown)
|
||||
execute(items.at(cursor));
|
||||
if (!wantsToRender())
|
||||
requestUpdate(Drivers::EInk::UpdateTypes::FAST);
|
||||
}
|
||||
|
||||
// Dynamically create MenuItem entries for activating / deactivating Applets, for the "Applet Selection" submenu
|
||||
void InkHUD::MenuApplet::populateAppletPage()
|
||||
{
|
||||
@@ -796,4 +874,4 @@ void InkHUD::MenuApplet::freeCannedMessageResources()
|
||||
cm.recipientItems.clear();
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -27,6 +27,11 @@ class MenuApplet : public SystemApplet, public concurrency::OSThread
|
||||
void onBackground() override;
|
||||
void onButtonShortPress() override;
|
||||
void onButtonLongPress() override;
|
||||
void onExitShort() override;
|
||||
void onNavUp() override;
|
||||
void onNavDown() override;
|
||||
void onNavLeft() override;
|
||||
void onNavRight() override;
|
||||
void onRender() override;
|
||||
|
||||
void show(Tile *t); // Open the menu, onto a user tile
|
||||
@@ -52,6 +57,7 @@ class MenuApplet : public SystemApplet, public concurrency::OSThread
|
||||
void freeCannedMessageResources(); // Clear MenuApplet's canned message processing data
|
||||
|
||||
MenuPage currentPage = MenuPage::ROOT;
|
||||
MenuPage previousPage = MenuPage::EXIT;
|
||||
uint8_t cursor = 0; // Which menu item is currently highlighted
|
||||
bool cursorShown = false; // Is *any* item highlighted? (Root menu: no initial selection)
|
||||
|
||||
@@ -97,4 +103,4 @@ class MenuApplet : public SystemApplet, public concurrency::OSThread
|
||||
|
||||
} // namespace NicheGraphics::InkHUD
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -153,6 +153,42 @@ void InkHUD::NotificationApplet::onButtonLongPress()
|
||||
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
|
||||
}
|
||||
|
||||
void InkHUD::NotificationApplet::onExitShort()
|
||||
{
|
||||
dismiss();
|
||||
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
|
||||
}
|
||||
|
||||
void InkHUD::NotificationApplet::onExitLong()
|
||||
{
|
||||
dismiss();
|
||||
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
|
||||
}
|
||||
|
||||
void InkHUD::NotificationApplet::onNavUp()
|
||||
{
|
||||
dismiss();
|
||||
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
|
||||
}
|
||||
|
||||
void InkHUD::NotificationApplet::onNavDown()
|
||||
{
|
||||
dismiss();
|
||||
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
|
||||
}
|
||||
|
||||
void InkHUD::NotificationApplet::onNavLeft()
|
||||
{
|
||||
dismiss();
|
||||
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
|
||||
}
|
||||
|
||||
void InkHUD::NotificationApplet::onNavRight()
|
||||
{
|
||||
dismiss();
|
||||
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
|
||||
}
|
||||
|
||||
// Ask the WindowManager to check whether any displayed applets are already displaying the info from this notification
|
||||
// Called internally when we first get a "notifiable event", and then again before render,
|
||||
// in case autoshow swapped which applet was displayed
|
||||
|
||||
@@ -31,6 +31,12 @@ class NotificationApplet : public SystemApplet
|
||||
void onBackground() override;
|
||||
void onButtonShortPress() override;
|
||||
void onButtonLongPress() override;
|
||||
void onExitShort() override;
|
||||
void onExitLong() override;
|
||||
void onNavUp() override;
|
||||
void onNavDown() override;
|
||||
void onNavLeft() override;
|
||||
void onNavRight() override;
|
||||
|
||||
int onReceiveTextMessage(const meshtastic_MeshPacket *p);
|
||||
|
||||
|
||||
@@ -112,12 +112,21 @@ void InkHUD::TipsApplet::onRender()
|
||||
setFont(fontSmall);
|
||||
int16_t cursorY = fontMedium.lineHeight() * 1.5;
|
||||
|
||||
printAt(0, cursorY, "User Button");
|
||||
cursorY += fontSmall.lineHeight() * 1.2;
|
||||
printAt(0, cursorY, "- short press: next");
|
||||
cursorY += fontSmall.lineHeight() * 1.2;
|
||||
printAt(0, cursorY, "- long press: select / open menu");
|
||||
cursorY += fontSmall.lineHeight() * 1.5;
|
||||
if (!settings->joystick.enabled) {
|
||||
printAt(0, cursorY, "User Button");
|
||||
cursorY += fontSmall.lineHeight() * 1.2;
|
||||
printAt(0, cursorY, "- short press: next");
|
||||
cursorY += fontSmall.lineHeight() * 1.2;
|
||||
printAt(0, cursorY, "- long press: select / open menu");
|
||||
} else {
|
||||
printAt(0, cursorY, "Joystick");
|
||||
cursorY += fontSmall.lineHeight() * 1.2;
|
||||
printAt(0, cursorY, "- open menu / select");
|
||||
cursorY += fontSmall.lineHeight() * 1.5;
|
||||
printAt(0, cursorY, "Exit Button");
|
||||
cursorY += fontSmall.lineHeight() * 1.2;
|
||||
printAt(0, cursorY, "- switch tile / close menu");
|
||||
}
|
||||
|
||||
printAt(0, Y(1.0), "Press button to continue", LEFT, BOTTOM);
|
||||
} break;
|
||||
@@ -127,8 +136,13 @@ void InkHUD::TipsApplet::onRender()
|
||||
printAt(0, 0, "Tip: Rotation");
|
||||
|
||||
setFont(fontSmall);
|
||||
printWrapped(0, fontMedium.lineHeight() * 1.5, width(),
|
||||
"To rotate the display, use the InkHUD menu. Long-press the user button > Options > Rotate.");
|
||||
if (!settings->joystick.enabled) {
|
||||
printWrapped(0, fontMedium.lineHeight() * 1.5, width(),
|
||||
"To rotate the display, use the InkHUD menu. Long-press the user button > Options > Rotate.");
|
||||
} else {
|
||||
printWrapped(0, fontMedium.lineHeight() * 1.5, width(),
|
||||
"To rotate the display, use the InkHUD menu. Press the user button > Options > Rotate.");
|
||||
}
|
||||
|
||||
printAt(0, Y(1.0), "Press button to continue", LEFT, BOTTOM);
|
||||
|
||||
@@ -232,4 +246,10 @@ void InkHUD::TipsApplet::onButtonShortPress()
|
||||
requestUpdate();
|
||||
}
|
||||
|
||||
// Functions the same as the user button in this instance
|
||||
void InkHUD::TipsApplet::onExitShort()
|
||||
{
|
||||
onButtonShortPress();
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -36,6 +36,7 @@ class TipsApplet : public SystemApplet
|
||||
void onForeground() override;
|
||||
void onBackground() override;
|
||||
void onButtonShortPress() override;
|
||||
void onExitShort() override;
|
||||
|
||||
protected:
|
||||
void renderWelcome(); // Very first screen of tutorial
|
||||
|
||||
@@ -55,10 +55,15 @@ void InkHUD::Events::onButtonShort()
|
||||
}
|
||||
|
||||
// If no system applet is handling input, default behavior instead is to cycle applets
|
||||
if (consumer)
|
||||
// or open menu if joystick is enabled
|
||||
if (consumer) {
|
||||
consumer->onButtonShortPress();
|
||||
else if (!dismissedExt) // Don't change applet if this button press silenced the external notification module
|
||||
inkhud->nextApplet();
|
||||
} else if (!dismissedExt) { // Don't change applet if this button press silenced the external notification module
|
||||
if (!settings->joystick.enabled)
|
||||
inkhud->nextApplet();
|
||||
else
|
||||
inkhud->openMenu();
|
||||
}
|
||||
}
|
||||
|
||||
void InkHUD::Events::onButtonLong()
|
||||
@@ -83,6 +88,156 @@ void InkHUD::Events::onButtonLong()
|
||||
inkhud->openMenu();
|
||||
}
|
||||
|
||||
void InkHUD::Events::onExitShort()
|
||||
{
|
||||
if (settings->joystick.enabled) {
|
||||
// Audio feedback (via buzzer)
|
||||
// Short tone
|
||||
playChirp();
|
||||
// Cancel any beeping, buzzing, blinking
|
||||
// Some button handling suppressed if we are dismissing an external notification (see below)
|
||||
bool dismissedExt = dismissExternalNotification();
|
||||
|
||||
// Check which system applet wants to handle the button press (if any)
|
||||
SystemApplet *consumer = nullptr;
|
||||
for (SystemApplet *sa : inkhud->systemApplets) {
|
||||
if (sa->handleInput) {
|
||||
consumer = sa;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If no system applet is handling input, default behavior instead is change tiles
|
||||
if (consumer)
|
||||
consumer->onExitShort();
|
||||
else if (!dismissedExt) // Don't change tile if this button press silenced the external notification module
|
||||
inkhud->nextTile();
|
||||
}
|
||||
}
|
||||
|
||||
void InkHUD::Events::onExitLong()
|
||||
{
|
||||
if (settings->joystick.enabled) {
|
||||
// Audio feedback (via buzzer)
|
||||
// Slightly longer than playChirp
|
||||
playBoop();
|
||||
|
||||
// Check which system applet wants to handle the button press (if any)
|
||||
SystemApplet *consumer = nullptr;
|
||||
for (SystemApplet *sa : inkhud->systemApplets) {
|
||||
if (sa->handleInput) {
|
||||
consumer = sa;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (consumer)
|
||||
consumer->onExitLong();
|
||||
}
|
||||
}
|
||||
|
||||
void InkHUD::Events::onNavUp()
|
||||
{
|
||||
if (settings->joystick.enabled) {
|
||||
// Audio feedback (via buzzer)
|
||||
// Short tone
|
||||
playChirp();
|
||||
// Cancel any beeping, buzzing, blinking
|
||||
// Some button handling suppressed if we are dismissing an external notification (see below)
|
||||
bool dismissedExt = dismissExternalNotification();
|
||||
|
||||
// Check which system applet wants to handle the button press (if any)
|
||||
SystemApplet *consumer = nullptr;
|
||||
for (SystemApplet *sa : inkhud->systemApplets) {
|
||||
if (sa->handleInput) {
|
||||
consumer = sa;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (consumer)
|
||||
consumer->onNavUp();
|
||||
}
|
||||
}
|
||||
|
||||
void InkHUD::Events::onNavDown()
|
||||
{
|
||||
if (settings->joystick.enabled) {
|
||||
// Audio feedback (via buzzer)
|
||||
// Short tone
|
||||
playChirp();
|
||||
// Cancel any beeping, buzzing, blinking
|
||||
// Some button handling suppressed if we are dismissing an external notification (see below)
|
||||
bool dismissedExt = dismissExternalNotification();
|
||||
|
||||
// Check which system applet wants to handle the button press (if any)
|
||||
SystemApplet *consumer = nullptr;
|
||||
for (SystemApplet *sa : inkhud->systemApplets) {
|
||||
if (sa->handleInput) {
|
||||
consumer = sa;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (consumer)
|
||||
consumer->onNavDown();
|
||||
}
|
||||
}
|
||||
|
||||
void InkHUD::Events::onNavLeft()
|
||||
{
|
||||
if (settings->joystick.enabled) {
|
||||
// Audio feedback (via buzzer)
|
||||
// Short tone
|
||||
playChirp();
|
||||
// Cancel any beeping, buzzing, blinking
|
||||
// Some button handling suppressed if we are dismissing an external notification (see below)
|
||||
bool dismissedExt = dismissExternalNotification();
|
||||
|
||||
// Check which system applet wants to handle the button press (if any)
|
||||
SystemApplet *consumer = nullptr;
|
||||
for (SystemApplet *sa : inkhud->systemApplets) {
|
||||
if (sa->handleInput) {
|
||||
consumer = sa;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If no system applet is handling input, default behavior instead is to cycle applets
|
||||
if (consumer)
|
||||
consumer->onNavLeft();
|
||||
else if (!dismissedExt) // Don't change applet if this button press silenced the external notification module
|
||||
inkhud->prevApplet();
|
||||
}
|
||||
}
|
||||
|
||||
void InkHUD::Events::onNavRight()
|
||||
{
|
||||
if (settings->joystick.enabled) {
|
||||
// Audio feedback (via buzzer)
|
||||
// Short tone
|
||||
playChirp();
|
||||
// Cancel any beeping, buzzing, blinking
|
||||
// Some button handling suppressed if we are dismissing an external notification (see below)
|
||||
bool dismissedExt = dismissExternalNotification();
|
||||
|
||||
// Check which system applet wants to handle the button press (if any)
|
||||
SystemApplet *consumer = nullptr;
|
||||
for (SystemApplet *sa : inkhud->systemApplets) {
|
||||
if (sa->handleInput) {
|
||||
consumer = sa;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If no system applet is handling input, default behavior instead is to cycle applets
|
||||
if (consumer)
|
||||
consumer->onNavRight();
|
||||
else if (!dismissedExt) // Don't change applet if this button press silenced the external notification module
|
||||
inkhud->nextApplet();
|
||||
}
|
||||
}
|
||||
|
||||
// Callback for deepSleepObserver
|
||||
// Returns 0 to signal that we agree to sleep now
|
||||
int InkHUD::Events::beforeDeepSleep(void *unused)
|
||||
|
||||
@@ -29,6 +29,12 @@ class Events
|
||||
|
||||
void onButtonShort(); // User button: short press
|
||||
void onButtonLong(); // User button: long press
|
||||
void onExitShort(); // Exit button: short press
|
||||
void onExitLong(); // Exit button: long press
|
||||
void onNavUp(); // Navigate up
|
||||
void onNavDown(); // Navigate down
|
||||
void onNavLeft(); // Navigate left
|
||||
void onNavRight(); // Navigate right
|
||||
|
||||
int beforeDeepSleep(void *unused); // Prepare for shutdown
|
||||
int beforeReboot(void *unused); // Prepare for reboot
|
||||
|
||||
@@ -80,6 +80,94 @@ void InkHUD::InkHUD::longpress()
|
||||
events->onButtonLong();
|
||||
}
|
||||
|
||||
// Call this when your exit button gets a short press
|
||||
void InkHUD::InkHUD::exitShort()
|
||||
{
|
||||
events->onExitShort();
|
||||
}
|
||||
|
||||
// Call this when your exit button gets a long press
|
||||
void InkHUD::InkHUD::exitLong()
|
||||
{
|
||||
events->onExitLong();
|
||||
}
|
||||
|
||||
// Call this when your joystick gets an up input
|
||||
void InkHUD::InkHUD::navUp()
|
||||
{
|
||||
switch ((persistence->settings.rotation + persistence->settings.joystick.alignment) % 4) {
|
||||
case 1: // 90 deg
|
||||
events->onNavLeft();
|
||||
break;
|
||||
case 2: // 180 deg
|
||||
events->onNavDown();
|
||||
break;
|
||||
case 3: // 270 deg
|
||||
events->onNavRight();
|
||||
break;
|
||||
default: // 0 deg
|
||||
events->onNavUp();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Call this when your joystick gets a down input
|
||||
void InkHUD::InkHUD::navDown()
|
||||
{
|
||||
switch ((persistence->settings.rotation + persistence->settings.joystick.alignment) % 4) {
|
||||
case 1: // 90 deg
|
||||
events->onNavRight();
|
||||
break;
|
||||
case 2: // 180 deg
|
||||
events->onNavUp();
|
||||
break;
|
||||
case 3: // 270 deg
|
||||
events->onNavLeft();
|
||||
break;
|
||||
default: // 0 deg
|
||||
events->onNavDown();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Call this when your joystick gets a left input
|
||||
void InkHUD::InkHUD::navLeft()
|
||||
{
|
||||
switch ((persistence->settings.rotation + persistence->settings.joystick.alignment) % 4) {
|
||||
case 1: // 90 deg
|
||||
events->onNavDown();
|
||||
break;
|
||||
case 2: // 180 deg
|
||||
events->onNavRight();
|
||||
break;
|
||||
case 3: // 270 deg
|
||||
events->onNavUp();
|
||||
break;
|
||||
default: // 0 deg
|
||||
events->onNavLeft();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Call this when your joystick gets a right input
|
||||
void InkHUD::InkHUD::navRight()
|
||||
{
|
||||
switch ((persistence->settings.rotation + persistence->settings.joystick.alignment) % 4) {
|
||||
case 1: // 90 deg
|
||||
events->onNavUp();
|
||||
break;
|
||||
case 2: // 180 deg
|
||||
events->onNavLeft();
|
||||
break;
|
||||
case 3: // 270 deg
|
||||
events->onNavDown();
|
||||
break;
|
||||
default: // 0 deg
|
||||
events->onNavRight();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Cycle the next user applet to the foreground
|
||||
// Only activated applets are cycled
|
||||
// If user has a multi-applet layout, the applets will cycle on the "focused tile"
|
||||
@@ -88,6 +176,14 @@ void InkHUD::InkHUD::nextApplet()
|
||||
windowManager->nextApplet();
|
||||
}
|
||||
|
||||
// Cycle the previous user applet to the foreground
|
||||
// Only activated applets are cycled
|
||||
// If user has a multi-applet layout, the applets will cycle on the "focused tile"
|
||||
void InkHUD::InkHUD::prevApplet()
|
||||
{
|
||||
windowManager->prevApplet();
|
||||
}
|
||||
|
||||
// Show the menu (on the the focused tile)
|
||||
// The applet previously displayed there will be restored once the menu closes
|
||||
void InkHUD::InkHUD::openMenu()
|
||||
@@ -95,6 +191,12 @@ void InkHUD::InkHUD::openMenu()
|
||||
windowManager->openMenu();
|
||||
}
|
||||
|
||||
// Bring AlignStick applet to the foreground
|
||||
void InkHUD::InkHUD::openAlignStick()
|
||||
{
|
||||
windowManager->openAlignStick();
|
||||
}
|
||||
|
||||
// In layouts where multiple applets are shown at once, change which tile is focused
|
||||
// The focused tile in the one which cycles applets on button short press, and displays menu on long press
|
||||
void InkHUD::InkHUD::nextTile()
|
||||
@@ -102,12 +204,26 @@ void InkHUD::InkHUD::nextTile()
|
||||
windowManager->nextTile();
|
||||
}
|
||||
|
||||
// In layouts where multiple applets are shown at once, change which tile is focused
|
||||
// The focused tile in the one which cycles applets on button short press, and displays menu on long press
|
||||
void InkHUD::InkHUD::prevTile()
|
||||
{
|
||||
windowManager->prevTile();
|
||||
}
|
||||
|
||||
// Rotate the display image by 90 degrees
|
||||
void InkHUD::InkHUD::rotate()
|
||||
{
|
||||
windowManager->rotate();
|
||||
}
|
||||
|
||||
// rotate the joystick in 90 degree increments
|
||||
void InkHUD::InkHUD::rotateJoystick(uint8_t angle)
|
||||
{
|
||||
persistence->settings.joystick.alignment += angle;
|
||||
persistence->settings.joystick.alignment %= 4;
|
||||
}
|
||||
|
||||
// Show / hide the battery indicator in top-right
|
||||
void InkHUD::InkHUD::toggleBatteryIcon()
|
||||
{
|
||||
|
||||
@@ -55,15 +55,25 @@ class InkHUD
|
||||
|
||||
void shortpress();
|
||||
void longpress();
|
||||
void exitShort();
|
||||
void exitLong();
|
||||
void navUp();
|
||||
void navDown();
|
||||
void navLeft();
|
||||
void navRight();
|
||||
|
||||
// Trigger UI changes
|
||||
// - called by various InkHUD components
|
||||
// - suitable(?) for use by aux button, connected in variant nicheGraphics.h
|
||||
|
||||
void nextApplet();
|
||||
void prevApplet();
|
||||
void openMenu();
|
||||
void openAlignStick();
|
||||
void nextTile();
|
||||
void prevTile();
|
||||
void rotate();
|
||||
void rotateJoystick(uint8_t angle = 1); // rotate 90 deg by default
|
||||
void toggleBatteryIcon();
|
||||
|
||||
// Updating the display
|
||||
|
||||
@@ -29,7 +29,7 @@ class Persistence
|
||||
|
||||
// Used to invalidate old settings, if needed
|
||||
// Version 0 is reserved for testing, and will always load defaults
|
||||
static constexpr uint32_t SETTINGS_VERSION = 2;
|
||||
static constexpr uint32_t SETTINGS_VERSION = 3;
|
||||
|
||||
struct Settings {
|
||||
struct Meta {
|
||||
@@ -96,6 +96,19 @@ class Persistence
|
||||
bool safeShutdownSeen = false;
|
||||
} tips;
|
||||
|
||||
// Joystick settings for enabling and aligning to the screen
|
||||
struct Joystick {
|
||||
// Modifies the UI for joystick use
|
||||
bool enabled = false;
|
||||
|
||||
// gets set to true when AlignStick applet is completed
|
||||
bool aligned = false;
|
||||
|
||||
// Rotation of the joystick
|
||||
// Multiples of 90 degrees clockwise
|
||||
uint8_t alignment = 0;
|
||||
} joystick;
|
||||
|
||||
// Rotation of the display
|
||||
// Multiples of 90 degrees clockwise
|
||||
// Most commonly: rotation is 0 when flex connector is oriented below display
|
||||
|
||||
@@ -8,4 +8,5 @@ build_flags =
|
||||
-D MESHTASTIC_EXCLUDE_INPUTBROKER ; Suppress default input handling
|
||||
-D HAS_BUTTON=0 ; Suppress default ButtonThread
|
||||
lib_deps =
|
||||
https://github.com/ZinggJM/GFX_Root#2.0.0 ; Used by InkHUD as a "slimmer" version of AdafruitGFX
|
||||
# TODO renovate
|
||||
https://github.com/ZinggJM/GFX_Root#2.0.0 ; Used by InkHUD as a "slimmer" version of AdafruitGFX
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "./WindowManager.h"
|
||||
|
||||
#include "./Applets/System/AlignStick/AlignStickApplet.h"
|
||||
#include "./Applets/System/BatteryIcon/BatteryIconApplet.h"
|
||||
#include "./Applets/System/Logo/LogoApplet.h"
|
||||
#include "./Applets/System/Menu/MenuApplet.h"
|
||||
@@ -98,6 +99,38 @@ void InkHUD::WindowManager::nextTile()
|
||||
userTiles.at(settings->userTiles.focused)->requestHighlight();
|
||||
}
|
||||
|
||||
// Focus on a different tile but decrement index
|
||||
void InkHUD::WindowManager::prevTile()
|
||||
{
|
||||
// Close the menu applet if open
|
||||
// We don't *really* want to do this, but it simplifies handling *a lot*
|
||||
MenuApplet *menu = (MenuApplet *)inkhud->getSystemApplet("Menu");
|
||||
bool menuWasOpen = false;
|
||||
if (menu->isForeground()) {
|
||||
menu->sendToBackground();
|
||||
menuWasOpen = true;
|
||||
}
|
||||
|
||||
// Swap to next tile
|
||||
if (settings->userTiles.focused == 0)
|
||||
settings->userTiles.focused = settings->userTiles.count - 1;
|
||||
else
|
||||
settings->userTiles.focused--;
|
||||
|
||||
// Make sure that we don't get stuck on the placeholder tile
|
||||
refocusTile();
|
||||
|
||||
if (menuWasOpen)
|
||||
menu->show(userTiles.at(settings->userTiles.focused));
|
||||
|
||||
// Ask the tile to draw an indicator showing which tile is now focused
|
||||
// Requests a render
|
||||
// We only draw this indicator if the device uses an aux button to switch tiles.
|
||||
// Assume aux button is used to switch tiles if the "next tile" menu item is hidden
|
||||
if (!settings->optionalMenuItems.nextTile)
|
||||
userTiles.at(settings->userTiles.focused)->requestHighlight();
|
||||
}
|
||||
|
||||
// Show the menu (on the the focused tile)
|
||||
// The applet previously displayed there will be restored once the menu closes
|
||||
void InkHUD::WindowManager::openMenu()
|
||||
@@ -106,6 +139,15 @@ void InkHUD::WindowManager::openMenu()
|
||||
menu->show(userTiles.at(settings->userTiles.focused));
|
||||
}
|
||||
|
||||
// Bring the AlignStick applet to the foreground
|
||||
void InkHUD::WindowManager::openAlignStick()
|
||||
{
|
||||
if (settings->joystick.enabled) {
|
||||
AlignStickApplet *alignStick = (AlignStickApplet *)inkhud->getSystemApplet("AlignStick");
|
||||
alignStick->bringToForeground();
|
||||
}
|
||||
}
|
||||
|
||||
// On the currently focussed tile: cycle to the next available user applet
|
||||
// Applets available for this must be activated, and not already displayed on another tile
|
||||
void InkHUD::WindowManager::nextApplet()
|
||||
@@ -155,6 +197,59 @@ void InkHUD::WindowManager::nextApplet()
|
||||
inkhud->forceUpdate(EInk::UpdateTypes::FAST); // bringToForeground already requested, but we're manually forcing FAST
|
||||
}
|
||||
|
||||
// On the currently focussed tile: cycle to the previous available user applet
|
||||
// Applets available for this must be activated, and not already displayed on another tile
|
||||
void InkHUD::WindowManager::prevApplet()
|
||||
{
|
||||
Tile *t = userTiles.at(settings->userTiles.focused);
|
||||
|
||||
// Abort if zero applets available
|
||||
// nullptr means WindowManager::refocusTile determined that there were no available applets
|
||||
if (!t->getAssignedApplet())
|
||||
return;
|
||||
|
||||
// Find the index of the applet currently shown on the tile
|
||||
uint8_t appletIndex = -1;
|
||||
for (uint8_t i = 0; i < inkhud->userApplets.size(); i++) {
|
||||
if (inkhud->userApplets.at(i) == t->getAssignedApplet()) {
|
||||
appletIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Confirm that we did find the applet
|
||||
assert(appletIndex != (uint8_t)-1);
|
||||
|
||||
// Iterate forward through the WindowManager::applets, looking for the previous valid applet
|
||||
Applet *prevValidApplet = nullptr;
|
||||
for (uint8_t i = 1; i < inkhud->userApplets.size(); i++) {
|
||||
uint8_t newAppletIndex = 0;
|
||||
if (i > appletIndex)
|
||||
newAppletIndex = inkhud->userApplets.size() + appletIndex - i;
|
||||
else
|
||||
newAppletIndex = (appletIndex - i);
|
||||
Applet *a = inkhud->userApplets.at(newAppletIndex);
|
||||
|
||||
// Looking for an applet which is active (enabled by user), but currently in background
|
||||
if (a->isActive() && !a->isForeground()) {
|
||||
prevValidApplet = a;
|
||||
settings->userTiles.displayedUserApplet[settings->userTiles.focused] =
|
||||
newAppletIndex; // Remember this setting between boots!
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Confirm that we found another applet
|
||||
if (!prevValidApplet)
|
||||
return;
|
||||
|
||||
// Hide old applet, show new applet
|
||||
t->getAssignedApplet()->sendToBackground();
|
||||
t->assignApplet(prevValidApplet);
|
||||
prevValidApplet->bringToForeground();
|
||||
inkhud->forceUpdate(EInk::UpdateTypes::FAST); // bringToForeground already requested, but we're manually forcing FAST
|
||||
}
|
||||
|
||||
// Rotate the display image by 90 degrees
|
||||
void InkHUD::WindowManager::rotate()
|
||||
{
|
||||
@@ -338,6 +433,8 @@ void InkHUD::WindowManager::createSystemApplets()
|
||||
addSystemApplet("Logo", new LogoApplet, new Tile);
|
||||
addSystemApplet("Pairing", new PairingApplet, new Tile);
|
||||
addSystemApplet("Tips", new TipsApplet, new Tile);
|
||||
if (settings->joystick.enabled)
|
||||
addSystemApplet("AlignStick", new AlignStickApplet, new Tile);
|
||||
|
||||
addSystemApplet("Menu", new MenuApplet, nullptr);
|
||||
|
||||
@@ -360,6 +457,8 @@ void InkHUD::WindowManager::placeSystemTiles()
|
||||
inkhud->getSystemApplet("Logo")->getTile()->setRegion(0, 0, inkhud->width(), inkhud->height());
|
||||
inkhud->getSystemApplet("Pairing")->getTile()->setRegion(0, 0, inkhud->width(), inkhud->height());
|
||||
inkhud->getSystemApplet("Tips")->getTile()->setRegion(0, 0, inkhud->width(), inkhud->height());
|
||||
if (settings->joystick.enabled)
|
||||
inkhud->getSystemApplet("AlignStick")->getTile()->setRegion(0, 0, inkhud->width(), inkhud->height());
|
||||
|
||||
inkhud->getSystemApplet("Notification")->getTile()->setRegion(0, 0, inkhud->width(), 20);
|
||||
|
||||
|
||||
@@ -28,8 +28,11 @@ class WindowManager
|
||||
// - call these to make stuff change
|
||||
|
||||
void nextTile();
|
||||
void prevTile();
|
||||
void openMenu();
|
||||
void openAlignStick();
|
||||
void nextApplet();
|
||||
void prevApplet();
|
||||
void rotate();
|
||||
void toggleBatteryIcon();
|
||||
|
||||
|
||||
523
src/graphics/niche/Inputs/TwoButtonExtended.cpp
Normal file
523
src/graphics/niche/Inputs/TwoButtonExtended.cpp
Normal file
@@ -0,0 +1,523 @@
|
||||
#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS
|
||||
|
||||
#include "./TwoButtonExtended.h"
|
||||
|
||||
#include "NodeDB.h" // For the helper function TwoButtonExtended::getUserButtonPin
|
||||
#include "PowerFSM.h"
|
||||
#include "sleep.h"
|
||||
|
||||
using namespace NicheGraphics::Inputs;
|
||||
|
||||
TwoButtonExtended::TwoButtonExtended() : concurrency::OSThread("TwoButtonExtended")
|
||||
{
|
||||
// Don't start polling buttons for release immediately
|
||||
// Assume they are in a "released" state at boot
|
||||
OSThread::disable();
|
||||
|
||||
#ifdef ARCH_ESP32
|
||||
// Register callbacks for before and after lightsleep
|
||||
lsObserver.observe(¬ifyLightSleep);
|
||||
lsEndObserver.observe(¬ifyLightSleepEnd);
|
||||
#endif
|
||||
|
||||
// Explicitly initialize these, just to keep cppcheck quiet..
|
||||
buttons[0] = Button();
|
||||
buttons[1] = Button();
|
||||
joystick[Direction::UP] = SimpleButton();
|
||||
joystick[Direction::DOWN] = SimpleButton();
|
||||
joystick[Direction::LEFT] = SimpleButton();
|
||||
joystick[Direction::RIGHT] = SimpleButton();
|
||||
}
|
||||
|
||||
// Get access to (or create) the singleton instance of this class
|
||||
// Accessible inside the ISRs, even though we maybe shouldn't
|
||||
TwoButtonExtended *TwoButtonExtended::getInstance()
|
||||
{
|
||||
// Instantiate the class the first time this method is called
|
||||
static TwoButtonExtended *const singletonInstance = new TwoButtonExtended;
|
||||
|
||||
return singletonInstance;
|
||||
}
|
||||
|
||||
// Begin receiving button input
|
||||
// We probably need to do this after sleep, as well as at boot
|
||||
void TwoButtonExtended::start()
|
||||
{
|
||||
if (buttons[0].pin != 0xFF)
|
||||
attachInterrupt(buttons[0].pin, TwoButtonExtended::isrPrimary, buttons[0].activeLogic == LOW ? FALLING : RISING);
|
||||
|
||||
if (buttons[1].pin != 0xFF)
|
||||
attachInterrupt(buttons[1].pin, TwoButtonExtended::isrSecondary, buttons[1].activeLogic == LOW ? FALLING : RISING);
|
||||
|
||||
if (joystick[Direction::UP].pin != 0xFF)
|
||||
attachInterrupt(joystick[Direction::UP].pin, TwoButtonExtended::isrJoystickUp,
|
||||
joystickActiveLogic == LOW ? FALLING : RISING);
|
||||
|
||||
if (joystick[Direction::DOWN].pin != 0xFF)
|
||||
attachInterrupt(joystick[Direction::DOWN].pin, TwoButtonExtended::isrJoystickDown,
|
||||
joystickActiveLogic == LOW ? FALLING : RISING);
|
||||
|
||||
if (joystick[Direction::LEFT].pin != 0xFF)
|
||||
attachInterrupt(joystick[Direction::LEFT].pin, TwoButtonExtended::isrJoystickLeft,
|
||||
joystickActiveLogic == LOW ? FALLING : RISING);
|
||||
|
||||
if (joystick[Direction::RIGHT].pin != 0xFF)
|
||||
attachInterrupt(joystick[Direction::RIGHT].pin, TwoButtonExtended::isrJoystickRight,
|
||||
joystickActiveLogic == LOW ? FALLING : RISING);
|
||||
}
|
||||
|
||||
// Stop receiving button input, and run custom sleep code
|
||||
// Called before device sleeps. This might be power-off, or just ESP32 light sleep
|
||||
// Some devices will want to attach interrupts here, for the user button to wake from sleep
|
||||
void TwoButtonExtended::stop()
|
||||
{
|
||||
if (buttons[0].pin != 0xFF)
|
||||
detachInterrupt(buttons[0].pin);
|
||||
|
||||
if (buttons[1].pin != 0xFF)
|
||||
detachInterrupt(buttons[1].pin);
|
||||
|
||||
if (joystick[Direction::UP].pin != 0xFF)
|
||||
detachInterrupt(joystick[Direction::UP].pin);
|
||||
|
||||
if (joystick[Direction::DOWN].pin != 0xFF)
|
||||
detachInterrupt(joystick[Direction::DOWN].pin);
|
||||
|
||||
if (joystick[Direction::LEFT].pin != 0xFF)
|
||||
detachInterrupt(joystick[Direction::LEFT].pin);
|
||||
|
||||
if (joystick[Direction::RIGHT].pin != 0xFF)
|
||||
detachInterrupt(joystick[Direction::RIGHT].pin);
|
||||
}
|
||||
|
||||
// Attempt to resolve a GPIO pin for the user button, honoring userPrefs.jsonc and device settings
|
||||
// This helper method isn't used by the TwoButtonExtended class itself, it could be moved elsewhere.
|
||||
// Intention is to pass this value to TwoButtonExtended::setWiring in the setupNicheGraphics method.
|
||||
uint8_t TwoButtonExtended::getUserButtonPin()
|
||||
{
|
||||
uint8_t pin = 0xFF; // Unset
|
||||
|
||||
// Use default pin for variant, if no better source
|
||||
#ifdef BUTTON_PIN
|
||||
pin = BUTTON_PIN;
|
||||
#endif
|
||||
|
||||
// From userPrefs.jsonc, if set
|
||||
#ifdef USERPREFS_BUTTON_PIN
|
||||
pin = USERPREFS_BUTTON_PIN;
|
||||
#endif
|
||||
|
||||
// From user's override in device settings, if set
|
||||
if (config.device.button_gpio)
|
||||
pin = config.device.button_gpio;
|
||||
|
||||
return pin;
|
||||
}
|
||||
|
||||
// Configures the wiring and logic of either button
|
||||
// Called when outlining your NicheGraphics implementation, in variant/nicheGraphics.cpp
|
||||
void TwoButtonExtended::setWiring(uint8_t whichButton, uint8_t pin, bool internalPullup)
|
||||
{
|
||||
// Prevent the same GPIO being assigned to multiple buttons
|
||||
// Allows an edge case when the user remaps hardware buttons using device settings, due to a broken user button
|
||||
for (uint8_t i = 0; i < whichButton; i++) {
|
||||
if (buttons[i].pin == pin) {
|
||||
LOG_WARN("Attempted reuse of GPIO %d. Ignoring assignment whichButton=%d", pin, whichButton);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
assert(whichButton < 2);
|
||||
buttons[whichButton].pin = pin;
|
||||
buttons[whichButton].activeLogic = LOW;
|
||||
|
||||
pinMode(buttons[whichButton].pin, internalPullup ? INPUT_PULLUP : INPUT);
|
||||
}
|
||||
|
||||
// Configures the wiring and logic of the joystick buttons
|
||||
// Called when outlining your NicheGraphics implementation, in variant/nicheGraphics.cpp
|
||||
void TwoButtonExtended::setJoystickWiring(uint8_t uPin, uint8_t dPin, uint8_t lPin, uint8_t rPin, bool internalPullup)
|
||||
{
|
||||
if (joystick[Direction::UP].pin == uPin || joystick[Direction::DOWN].pin == dPin || joystick[Direction::LEFT].pin == lPin ||
|
||||
joystick[Direction::RIGHT].pin == rPin) {
|
||||
LOG_WARN("Attempted reuse of Joystick GPIO. Ignoring assignment");
|
||||
return;
|
||||
}
|
||||
|
||||
joystick[Direction::UP].pin = uPin;
|
||||
joystick[Direction::DOWN].pin = dPin;
|
||||
joystick[Direction::LEFT].pin = lPin;
|
||||
joystick[Direction::RIGHT].pin = rPin;
|
||||
joystickActiveLogic = LOW;
|
||||
|
||||
pinMode(joystick[Direction::UP].pin, internalPullup ? INPUT_PULLUP : INPUT);
|
||||
pinMode(joystick[Direction::DOWN].pin, internalPullup ? INPUT_PULLUP : INPUT);
|
||||
pinMode(joystick[Direction::LEFT].pin, internalPullup ? INPUT_PULLUP : INPUT);
|
||||
pinMode(joystick[Direction::RIGHT].pin, internalPullup ? INPUT_PULLUP : INPUT);
|
||||
}
|
||||
|
||||
void TwoButtonExtended::setTiming(uint8_t whichButton, uint32_t debounceMs, uint32_t longpressMs)
|
||||
{
|
||||
assert(whichButton < 2);
|
||||
buttons[whichButton].debounceLength = debounceMs;
|
||||
buttons[whichButton].longpressLength = longpressMs;
|
||||
}
|
||||
|
||||
void TwoButtonExtended::setJoystickDebounce(uint32_t debounceMs)
|
||||
{
|
||||
joystickDebounceLength = debounceMs;
|
||||
}
|
||||
|
||||
// Set what should happen when a button becomes pressed
|
||||
// Use this to implement a "while held" behavior
|
||||
void TwoButtonExtended::setHandlerDown(uint8_t whichButton, Callback onDown)
|
||||
{
|
||||
assert(whichButton < 2);
|
||||
buttons[whichButton].onDown = onDown;
|
||||
}
|
||||
|
||||
// Set what should happen when a button becomes unpressed
|
||||
// Use this to implement a "While held" behavior
|
||||
void TwoButtonExtended::setHandlerUp(uint8_t whichButton, Callback onUp)
|
||||
{
|
||||
assert(whichButton < 2);
|
||||
buttons[whichButton].onUp = onUp;
|
||||
}
|
||||
|
||||
// Set what should happen when a "short press" event has occurred
|
||||
void TwoButtonExtended::setHandlerShortPress(uint8_t whichButton, Callback onPress)
|
||||
{
|
||||
assert(whichButton < 2);
|
||||
buttons[whichButton].onPress = onPress;
|
||||
}
|
||||
|
||||
// Set what should happen when a "long press" event has fired
|
||||
// Note: this will occur while the button is still held
|
||||
void TwoButtonExtended::setHandlerLongPress(uint8_t whichButton, Callback onLongPress)
|
||||
{
|
||||
assert(whichButton < 2);
|
||||
buttons[whichButton].onLongPress = onLongPress;
|
||||
}
|
||||
|
||||
// Set what should happen when a joystick button becomes pressed
|
||||
// Use this to implement a "while held" behavior
|
||||
void TwoButtonExtended::setJoystickDownHandlers(Callback uDown, Callback dDown, Callback lDown, Callback rDown)
|
||||
{
|
||||
joystick[Direction::UP].onDown = uDown;
|
||||
joystick[Direction::DOWN].onDown = dDown;
|
||||
joystick[Direction::LEFT].onDown = lDown;
|
||||
joystick[Direction::RIGHT].onDown = rDown;
|
||||
}
|
||||
|
||||
// Set what should happen when a joystick button becomes unpressed
|
||||
// Use this to implement a "while held" behavior
|
||||
void TwoButtonExtended::setJoystickUpHandlers(Callback uUp, Callback dUp, Callback lUp, Callback rUp)
|
||||
{
|
||||
joystick[Direction::UP].onUp = uUp;
|
||||
joystick[Direction::DOWN].onUp = dUp;
|
||||
joystick[Direction::LEFT].onUp = lUp;
|
||||
joystick[Direction::RIGHT].onUp = rUp;
|
||||
}
|
||||
|
||||
// Set what should happen when a "press" event has fired
|
||||
// Note: this will occur while the joystick button is still held
|
||||
void TwoButtonExtended::setJoystickPressHandlers(Callback uPress, Callback dPress, Callback lPress, Callback rPress)
|
||||
{
|
||||
joystick[Direction::UP].onPress = uPress;
|
||||
joystick[Direction::DOWN].onPress = dPress;
|
||||
joystick[Direction::LEFT].onPress = lPress;
|
||||
joystick[Direction::RIGHT].onPress = rPress;
|
||||
}
|
||||
|
||||
// Handle the start of a press to the primary button
|
||||
// Wakes our button thread
|
||||
void TwoButtonExtended::isrPrimary()
|
||||
{
|
||||
static volatile bool isrRunning = false;
|
||||
|
||||
if (!isrRunning) {
|
||||
isrRunning = true;
|
||||
TwoButtonExtended *b = TwoButtonExtended::getInstance();
|
||||
if (b->buttons[0].state == State::REST) {
|
||||
b->buttons[0].state = State::IRQ;
|
||||
b->buttons[0].irqAtMillis = millis();
|
||||
b->startThread();
|
||||
}
|
||||
isrRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the start of a press to the secondary button
|
||||
// Wakes our button thread
|
||||
void TwoButtonExtended::isrSecondary()
|
||||
{
|
||||
static volatile bool isrRunning = false;
|
||||
|
||||
if (!isrRunning) {
|
||||
isrRunning = true;
|
||||
TwoButtonExtended *b = TwoButtonExtended::getInstance();
|
||||
if (b->buttons[1].state == State::REST) {
|
||||
b->buttons[1].state = State::IRQ;
|
||||
b->buttons[1].irqAtMillis = millis();
|
||||
b->startThread();
|
||||
}
|
||||
isrRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the start of a press to the joystick buttons
|
||||
// Also wakes our button thread
|
||||
void TwoButtonExtended::isrJoystickUp()
|
||||
{
|
||||
static volatile bool isrRunning = false;
|
||||
|
||||
if (!isrRunning) {
|
||||
isrRunning = true;
|
||||
TwoButtonExtended *b = TwoButtonExtended::getInstance();
|
||||
if (b->joystick[Direction::UP].state == State::REST) {
|
||||
b->joystick[Direction::UP].state = State::IRQ;
|
||||
b->joystick[Direction::UP].irqAtMillis = millis();
|
||||
b->startThread();
|
||||
}
|
||||
isrRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
void TwoButtonExtended::isrJoystickDown()
|
||||
{
|
||||
static volatile bool isrRunning = false;
|
||||
|
||||
if (!isrRunning) {
|
||||
isrRunning = true;
|
||||
TwoButtonExtended *b = TwoButtonExtended::getInstance();
|
||||
if (b->joystick[Direction::DOWN].state == State::REST) {
|
||||
b->joystick[Direction::DOWN].state = State::IRQ;
|
||||
b->joystick[Direction::DOWN].irqAtMillis = millis();
|
||||
b->startThread();
|
||||
}
|
||||
isrRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
void TwoButtonExtended::isrJoystickLeft()
|
||||
{
|
||||
static volatile bool isrRunning = false;
|
||||
|
||||
if (!isrRunning) {
|
||||
isrRunning = true;
|
||||
TwoButtonExtended *b = TwoButtonExtended::getInstance();
|
||||
if (b->joystick[Direction::LEFT].state == State::REST) {
|
||||
b->joystick[Direction::LEFT].state = State::IRQ;
|
||||
b->joystick[Direction::LEFT].irqAtMillis = millis();
|
||||
b->startThread();
|
||||
}
|
||||
isrRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
void TwoButtonExtended::isrJoystickRight()
|
||||
{
|
||||
static volatile bool isrRunning = false;
|
||||
|
||||
if (!isrRunning) {
|
||||
isrRunning = true;
|
||||
TwoButtonExtended *b = TwoButtonExtended::getInstance();
|
||||
if (b->joystick[Direction::RIGHT].state == State::REST) {
|
||||
b->joystick[Direction::RIGHT].state = State::IRQ;
|
||||
b->joystick[Direction::RIGHT].irqAtMillis = millis();
|
||||
b->startThread();
|
||||
}
|
||||
isrRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Concise method to start our button thread
|
||||
// Follows an ISR, listening for button release
|
||||
void TwoButtonExtended::startThread()
|
||||
{
|
||||
if (!OSThread::enabled) {
|
||||
OSThread::setInterval(10);
|
||||
OSThread::enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Concise method to stop our button thread
|
||||
// Called when we no longer need to poll for button release
|
||||
void TwoButtonExtended::stopThread()
|
||||
{
|
||||
if (OSThread::enabled) {
|
||||
OSThread::disable();
|
||||
}
|
||||
|
||||
// Reset both buttons manually
|
||||
// Just in case an IRQ fires during the process of resetting the system
|
||||
// Can occur with super rapid presses?
|
||||
buttons[0].state = REST;
|
||||
buttons[1].state = REST;
|
||||
joystick[Direction::UP].state = REST;
|
||||
joystick[Direction::DOWN].state = REST;
|
||||
joystick[Direction::LEFT].state = REST;
|
||||
joystick[Direction::RIGHT].state = REST;
|
||||
}
|
||||
|
||||
// Our button thread
|
||||
// Started by an IRQ, on either button
|
||||
// Polls for button releases
|
||||
// Stops when both buttons released
|
||||
int32_t TwoButtonExtended::runOnce()
|
||||
{
|
||||
constexpr uint8_t BUTTON_COUNT = sizeof(buttons) / sizeof(Button);
|
||||
constexpr uint8_t JOYSTICK_COUNT = sizeof(joystick) / sizeof(SimpleButton);
|
||||
|
||||
// Allow either button to request that our thread should continue polling
|
||||
bool awaitingRelease = false;
|
||||
|
||||
// Check both primary and secondary buttons
|
||||
for (uint8_t i = 0; i < BUTTON_COUNT; i++) {
|
||||
switch (buttons[i].state) {
|
||||
// No action: button has not been pressed
|
||||
case REST:
|
||||
break;
|
||||
|
||||
// New press detected by interrupt
|
||||
case IRQ:
|
||||
powerFSM.trigger(EVENT_PRESS); // Tell PowerFSM that press occurred (resets sleep timer)
|
||||
buttons[i].onDown(); // Run callback: press has begun (possible hold behavior)
|
||||
buttons[i].state = State::POLLING_UNFIRED; // Mark that button-down has been handled
|
||||
awaitingRelease = true; // Mark that polling-for-release should continue
|
||||
break;
|
||||
|
||||
// An existing press continues
|
||||
// Not held long enough to register as longpress
|
||||
case POLLING_UNFIRED: {
|
||||
uint32_t length = millis() - buttons[i].irqAtMillis;
|
||||
|
||||
// If button released since last thread tick,
|
||||
if (digitalRead(buttons[i].pin) != buttons[i].activeLogic) {
|
||||
buttons[i].onUp(); // Run callback: press has ended (possible release of a hold)
|
||||
buttons[i].state = State::REST; // Mark that the button has reset
|
||||
if (length > buttons[i].debounceLength && length < buttons[i].longpressLength) // If too short for longpress,
|
||||
buttons[i].onPress(); // Run callback: press
|
||||
}
|
||||
// If button not yet released
|
||||
else {
|
||||
awaitingRelease = true; // Mark that polling-for-release should continue
|
||||
if (length >= buttons[i].longpressLength) {
|
||||
// Run callback: long press (once)
|
||||
// Then continue waiting for release, to rearm
|
||||
buttons[i].state = State::POLLING_FIRED;
|
||||
buttons[i].onLongPress();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Button still held, but duration long enough that longpress event already fired
|
||||
// Just waiting for release
|
||||
case POLLING_FIRED:
|
||||
// Release detected
|
||||
if (digitalRead(buttons[i].pin) != buttons[i].activeLogic) {
|
||||
buttons[i].state = State::REST;
|
||||
buttons[i].onUp(); // Callback: release of hold (in this case: *after* longpress has fired)
|
||||
}
|
||||
// Not yet released, keep polling
|
||||
else
|
||||
awaitingRelease = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check all the joystick directions
|
||||
for (uint8_t i = 0; i < JOYSTICK_COUNT; i++) {
|
||||
switch (joystick[i].state) {
|
||||
// No action: button has not been pressed
|
||||
case REST:
|
||||
break;
|
||||
|
||||
// New press detected by interrupt
|
||||
case IRQ:
|
||||
powerFSM.trigger(EVENT_PRESS); // Tell PowerFSM that press occurred (resets sleep timer)
|
||||
joystick[i].onDown(); // Run callback: press has begun (possible hold behavior)
|
||||
joystick[i].state = State::POLLING_UNFIRED; // Mark that button-down has been handled
|
||||
awaitingRelease = true; // Mark that polling-for-release should continue
|
||||
break;
|
||||
|
||||
// An existing press continues
|
||||
// Not held long enough to register as press
|
||||
case POLLING_UNFIRED: {
|
||||
uint32_t length = millis() - joystick[i].irqAtMillis;
|
||||
|
||||
// If button released since last thread tick,
|
||||
if (digitalRead(joystick[i].pin) != joystickActiveLogic) {
|
||||
joystick[i].onUp(); // Run callback: press has ended (possible release of a hold)
|
||||
joystick[i].state = State::REST; // Mark that the button has reset
|
||||
}
|
||||
// If button not yet released
|
||||
else {
|
||||
awaitingRelease = true; // Mark that polling-for-release should continue
|
||||
if (length >= joystickDebounceLength) {
|
||||
// Run callback: long press (once)
|
||||
// Then continue waiting for release, to rearm
|
||||
joystick[i].state = State::POLLING_FIRED;
|
||||
joystick[i].onPress();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Button still held after press
|
||||
// Just waiting for release
|
||||
case POLLING_FIRED:
|
||||
// Release detected
|
||||
if (digitalRead(joystick[i].pin) != joystickActiveLogic) {
|
||||
joystick[i].state = State::REST;
|
||||
joystick[i].onUp(); // Callback: release of hold
|
||||
}
|
||||
// Not yet released, keep polling
|
||||
else
|
||||
awaitingRelease = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If all buttons are now released
|
||||
// we don't need to waste cpu resources polling
|
||||
// IRQ will restart this thread when we next need it
|
||||
if (!awaitingRelease)
|
||||
stopThread();
|
||||
|
||||
// Run this method again, or don't..
|
||||
// Use whatever behavior was previously set by stopThread() or startThread()
|
||||
return OSThread::interval;
|
||||
}
|
||||
|
||||
#ifdef ARCH_ESP32
|
||||
|
||||
// Detach our class' interrupts before lightsleep
|
||||
// Allows sleep.cpp to configure its own interrupts, which wake the device on user-button press
|
||||
int TwoButtonExtended::beforeLightSleep(void *unused)
|
||||
{
|
||||
stop();
|
||||
return 0; // Indicates success
|
||||
}
|
||||
|
||||
// Reconfigure our interrupts
|
||||
// Our class' interrupts were disconnected during sleep, to allow the user button to wake the device from sleep
|
||||
int TwoButtonExtended::afterLightSleep(esp_sleep_wakeup_cause_t cause)
|
||||
{
|
||||
start();
|
||||
|
||||
// Manually trigger the button-down ISR
|
||||
// - during light sleep, our ISR is disabled
|
||||
// - if light sleep ends by button press, pretend our own ISR caught it
|
||||
// - need to manually confirm by reading pin ourselves, to avoid occasional false positives
|
||||
// (false positive only when using internal pullup resistors?)
|
||||
if (cause == ESP_SLEEP_WAKEUP_GPIO && digitalRead(buttons[0].pin) == buttons[0].activeLogic)
|
||||
isrPrimary();
|
||||
|
||||
return 0; // Indicates success
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
136
src/graphics/niche/Inputs/TwoButtonExtended.h
Normal file
136
src/graphics/niche/Inputs/TwoButtonExtended.h
Normal file
@@ -0,0 +1,136 @@
|
||||
#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS
|
||||
|
||||
/*
|
||||
|
||||
Re-usable NicheGraphics input source
|
||||
|
||||
Short and Long press for up to two buttons
|
||||
Interrupt driven
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This expansion adds support for four more buttons
|
||||
These buttons are single-action only, no long press
|
||||
Interrupt driven
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "configuration.h"
|
||||
|
||||
#include "assert.h"
|
||||
#include "functional"
|
||||
|
||||
#ifdef ARCH_ESP32
|
||||
#include "esp_sleep.h" // For light-sleep handling
|
||||
#endif
|
||||
|
||||
#include "Observer.h"
|
||||
|
||||
namespace NicheGraphics::Inputs
|
||||
{
|
||||
|
||||
class TwoButtonExtended : protected concurrency::OSThread
|
||||
{
|
||||
public:
|
||||
typedef std::function<void()> Callback;
|
||||
|
||||
static uint8_t getUserButtonPin(); // Resolve the GPIO, considering the various possible source of definition
|
||||
|
||||
static TwoButtonExtended *getInstance(); // Create or get the singleton instance
|
||||
void start(); // Start handling button input
|
||||
void stop(); // Stop handling button input (disconnect ISRs for sleep)
|
||||
void setWiring(uint8_t whichButton, uint8_t pin, bool internalPullup = false);
|
||||
void setJoystickWiring(uint8_t uPin, uint8_t dPin, uint8_t lPin, uint8_t rPin, bool internalPullup = false);
|
||||
void setTiming(uint8_t whichButton, uint32_t debounceMs, uint32_t longpressMs);
|
||||
void setJoystickDebounce(uint32_t debounceMs);
|
||||
void setHandlerDown(uint8_t whichButton, Callback onDown);
|
||||
void setHandlerUp(uint8_t whichButton, Callback onUp);
|
||||
void setHandlerShortPress(uint8_t whichButton, Callback onShortPress);
|
||||
void setHandlerLongPress(uint8_t whichButton, Callback onLongPress);
|
||||
void setJoystickDownHandlers(Callback uDown, Callback dDown, Callback ldown, Callback rDown);
|
||||
void setJoystickUpHandlers(Callback uUp, Callback dUp, Callback lUp, Callback rUp);
|
||||
void setJoystickPressHandlers(Callback uPress, Callback dPress, Callback lPress, Callback rPress);
|
||||
|
||||
// Disconnect and reconnect interrupts for light sleep
|
||||
#ifdef ARCH_ESP32
|
||||
int beforeLightSleep(void *unused);
|
||||
int afterLightSleep(esp_sleep_wakeup_cause_t cause);
|
||||
#endif
|
||||
|
||||
private:
|
||||
// Internal state of a specific button
|
||||
enum State {
|
||||
REST, // Up, no activity
|
||||
IRQ, // Down detected, not yet handled
|
||||
POLLING_UNFIRED, // Down handled, polling for release
|
||||
POLLING_FIRED, // Longpress fired, button still held
|
||||
};
|
||||
|
||||
// Joystick Directions
|
||||
enum Direction { UP = 0, DOWN, LEFT, RIGHT };
|
||||
|
||||
// Data used for direction (single-action) buttons
|
||||
class SimpleButton
|
||||
{
|
||||
public:
|
||||
// Per-button config
|
||||
uint8_t pin = 0xFF; // 0xFF: unset
|
||||
volatile State state = State::REST; // Internal state
|
||||
volatile uint32_t irqAtMillis; // millis() when button went down
|
||||
|
||||
// Per-button event callbacks
|
||||
static void noop(){};
|
||||
std::function<void()> onDown = noop;
|
||||
std::function<void()> onUp = noop;
|
||||
std::function<void()> onPress = noop;
|
||||
};
|
||||
|
||||
// Data used for double-action buttons
|
||||
class Button : public SimpleButton
|
||||
{
|
||||
public:
|
||||
// Per-button extended config
|
||||
bool activeLogic = LOW; // Active LOW by default.
|
||||
uint32_t debounceLength = 50; // Minimum length for shortpress in ms
|
||||
uint32_t longpressLength = 500; // Time until longpress in ms
|
||||
|
||||
// Per-button event callbacks
|
||||
std::function<void()> onLongPress = noop;
|
||||
};
|
||||
|
||||
#ifdef ARCH_ESP32
|
||||
// Get notified when lightsleep begins and ends
|
||||
CallbackObserver<TwoButtonExtended, void *> lsObserver =
|
||||
CallbackObserver<TwoButtonExtended, void *>(this, &TwoButtonExtended::beforeLightSleep);
|
||||
CallbackObserver<TwoButtonExtended, esp_sleep_wakeup_cause_t> lsEndObserver =
|
||||
CallbackObserver<TwoButtonExtended, esp_sleep_wakeup_cause_t>(this, &TwoButtonExtended::afterLightSleep);
|
||||
#endif
|
||||
|
||||
int32_t runOnce() override; // Timer method. Polls for button release
|
||||
|
||||
void startThread(); // Start polling for release
|
||||
void stopThread(); // Stop polling for release
|
||||
|
||||
static void isrPrimary(); // User Button ISR
|
||||
static void isrSecondary(); // optional aux button or joystick center
|
||||
static void isrJoystickUp();
|
||||
static void isrJoystickDown();
|
||||
static void isrJoystickLeft();
|
||||
static void isrJoystickRight();
|
||||
|
||||
TwoButtonExtended(); // Constructor made private: force use of Button::instance()
|
||||
|
||||
// Info about both buttons
|
||||
Button buttons[2];
|
||||
bool joystickActiveLogic = LOW; // Active LOW by default
|
||||
uint32_t joystickDebounceLength = 50; // time until press in ms
|
||||
SimpleButton joystick[4];
|
||||
};
|
||||
|
||||
}; // namespace NicheGraphics::Inputs
|
||||
|
||||
#endif
|
||||
@@ -53,6 +53,7 @@ typedef struct _InputEvent {
|
||||
class InputPollable
|
||||
{
|
||||
public:
|
||||
virtual ~InputPollable() = default;
|
||||
virtual void pollOnce() = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
#include "RotaryEncoderImpl.h"
|
||||
#include "InputBroker.h"
|
||||
#include "RotaryEncoder.h"
|
||||
#ifdef ARCH_ESP32
|
||||
#include "sleep.h"
|
||||
#endif
|
||||
|
||||
#define ORIGIN_NAME "RotaryEncoder"
|
||||
|
||||
@@ -11,6 +14,20 @@ RotaryEncoderImpl *rotaryEncoderImpl;
|
||||
RotaryEncoderImpl::RotaryEncoderImpl()
|
||||
{
|
||||
rotary = nullptr;
|
||||
#ifdef ARCH_ESP32
|
||||
isFirstInit = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
RotaryEncoderImpl::~RotaryEncoderImpl()
|
||||
{
|
||||
LOG_DEBUG("RotaryEncoderImpl destructor");
|
||||
detachRotaryEncoderInterrupts();
|
||||
|
||||
if (rotary != nullptr) {
|
||||
delete rotary;
|
||||
rotary = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool RotaryEncoderImpl::init()
|
||||
@@ -25,15 +42,22 @@ bool RotaryEncoderImpl::init()
|
||||
eventCcw = static_cast<input_broker_event>(moduleConfig.canned_message.inputbroker_event_ccw);
|
||||
eventPressed = static_cast<input_broker_event>(moduleConfig.canned_message.inputbroker_event_press);
|
||||
|
||||
rotary = new RotaryEncoder(moduleConfig.canned_message.inputbroker_pin_a, moduleConfig.canned_message.inputbroker_pin_b,
|
||||
moduleConfig.canned_message.inputbroker_pin_press);
|
||||
rotary->resetButton();
|
||||
if (rotary == nullptr) {
|
||||
rotary = new RotaryEncoder(moduleConfig.canned_message.inputbroker_pin_a, moduleConfig.canned_message.inputbroker_pin_b,
|
||||
moduleConfig.canned_message.inputbroker_pin_press);
|
||||
}
|
||||
|
||||
interruptInstance = this;
|
||||
auto interruptHandler = []() { inputBroker->requestPollSoon(interruptInstance); };
|
||||
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_a, interruptHandler, CHANGE);
|
||||
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_b, interruptHandler, CHANGE);
|
||||
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_press, interruptHandler, CHANGE);
|
||||
attachRotaryEncoderInterrupts();
|
||||
|
||||
#ifdef ARCH_ESP32
|
||||
// Register callbacks for before and after lightsleep
|
||||
// Used to detach and reattach interrupts
|
||||
if (isFirstInit) {
|
||||
lsObserver.observe(¬ifyLightSleep);
|
||||
lsEndObserver.observe(¬ifyLightSleepEnd);
|
||||
isFirstInit = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
LOG_INFO("RotaryEncoder initialized pins(%d, %d, %d), events(%d, %d, %d)", moduleConfig.canned_message.inputbroker_pin_a,
|
||||
moduleConfig.canned_message.inputbroker_pin_b, moduleConfig.canned_message.inputbroker_pin_press, eventCw, eventCcw,
|
||||
@@ -71,6 +95,50 @@ void RotaryEncoderImpl::pollOnce()
|
||||
}
|
||||
}
|
||||
|
||||
void RotaryEncoderImpl::detachRotaryEncoderInterrupts()
|
||||
{
|
||||
LOG_DEBUG("RotaryEncoderImpl detach button interrupts");
|
||||
if (interruptInstance == this) {
|
||||
detachInterrupt(moduleConfig.canned_message.inputbroker_pin_a);
|
||||
detachInterrupt(moduleConfig.canned_message.inputbroker_pin_b);
|
||||
detachInterrupt(moduleConfig.canned_message.inputbroker_pin_press);
|
||||
interruptInstance = nullptr;
|
||||
} else {
|
||||
LOG_WARN("RotaryEncoderImpl: interrupts already detached");
|
||||
}
|
||||
}
|
||||
|
||||
void RotaryEncoderImpl::attachRotaryEncoderInterrupts()
|
||||
{
|
||||
LOG_DEBUG("RotaryEncoderImpl attach button interrupts");
|
||||
if (rotary != nullptr && interruptInstance == nullptr) {
|
||||
rotary->resetButton();
|
||||
|
||||
interruptInstance = this;
|
||||
auto interruptHandler = []() { inputBroker->requestPollSoon(interruptInstance); };
|
||||
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_a, interruptHandler, CHANGE);
|
||||
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_b, interruptHandler, CHANGE);
|
||||
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_press, interruptHandler, CHANGE);
|
||||
} else {
|
||||
LOG_WARN("RotaryEncoderImpl: interrupts already attached");
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ARCH_ESP32
|
||||
|
||||
int RotaryEncoderImpl::beforeLightSleep(void *unused)
|
||||
{
|
||||
detachRotaryEncoderInterrupts();
|
||||
return 0; // Indicates success;
|
||||
}
|
||||
|
||||
int RotaryEncoderImpl::afterLightSleep(esp_sleep_wakeup_cause_t cause)
|
||||
{
|
||||
attachRotaryEncoderInterrupts();
|
||||
return 0; // Indicates success;
|
||||
}
|
||||
#endif
|
||||
|
||||
RotaryEncoderImpl *RotaryEncoderImpl::interruptInstance;
|
||||
|
||||
#endif
|
||||
@@ -8,12 +8,18 @@
|
||||
|
||||
class RotaryEncoder;
|
||||
|
||||
class RotaryEncoderImpl : public InputPollable
|
||||
class RotaryEncoderImpl final : public InputPollable
|
||||
{
|
||||
public:
|
||||
RotaryEncoderImpl();
|
||||
bool init(void);
|
||||
~RotaryEncoderImpl() override;
|
||||
bool init();
|
||||
virtual void pollOnce() override;
|
||||
// Disconnect and reconnect interrupts for light sleep
|
||||
#ifdef ARCH_ESP32
|
||||
int beforeLightSleep(void *unused);
|
||||
int afterLightSleep(esp_sleep_wakeup_cause_t cause);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
static RotaryEncoderImpl *interruptInstance;
|
||||
@@ -23,6 +29,21 @@ class RotaryEncoderImpl : public InputPollable
|
||||
input_broker_event eventPressed = INPUT_BROKER_NONE;
|
||||
|
||||
RotaryEncoder *rotary;
|
||||
|
||||
private:
|
||||
#ifdef ARCH_ESP32
|
||||
bool isFirstInit;
|
||||
#endif
|
||||
void detachRotaryEncoderInterrupts();
|
||||
void attachRotaryEncoderInterrupts();
|
||||
|
||||
#ifdef ARCH_ESP32
|
||||
// Get notified when lightsleep begins and ends
|
||||
CallbackObserver<RotaryEncoderImpl, void *> lsObserver =
|
||||
CallbackObserver<RotaryEncoderImpl, void *>(this, &RotaryEncoderImpl::beforeLightSleep);
|
||||
CallbackObserver<RotaryEncoderImpl, esp_sleep_wakeup_cause_t> lsEndObserver =
|
||||
CallbackObserver<RotaryEncoderImpl, esp_sleep_wakeup_cause_t>(this, &RotaryEncoderImpl::afterLightSleep);
|
||||
#endif
|
||||
};
|
||||
|
||||
extern RotaryEncoderImpl *rotaryEncoderImpl;
|
||||
|
||||
11
src/main.cpp
11
src/main.cpp
@@ -428,10 +428,17 @@ void setup()
|
||||
#endif
|
||||
|
||||
#if ARCH_PORTDUINO
|
||||
RTCQuality ourQuality = RTCQualityDevice;
|
||||
|
||||
std::string timeCommandResult = exec("timedatectl status | grep synchronized | grep yes -c");
|
||||
if (timeCommandResult[0] == '1') {
|
||||
ourQuality = RTCQualityNTP;
|
||||
}
|
||||
|
||||
struct timeval tv;
|
||||
tv.tv_sec = time(NULL);
|
||||
tv.tv_usec = 0;
|
||||
perhapsSetRTC(RTCQualityDevice, &tv);
|
||||
perhapsSetRTC(ourQuality, &tv);
|
||||
#endif
|
||||
|
||||
powerMonInit();
|
||||
@@ -440,9 +447,11 @@ void setup()
|
||||
LOG_INFO("\n\n//\\ E S H T /\\ S T / C\n");
|
||||
|
||||
#if defined(ARCH_ESP32) && defined(BOARD_HAS_PSRAM)
|
||||
#ifndef SENSECAP_INDICATOR
|
||||
// use PSRAM for malloc calls > 256 bytes
|
||||
heap_caps_malloc_extmem_enable(256);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(DEBUG_MUTE) && defined(DEBUG_PORT)
|
||||
DEBUG_PORT.printf("\r\n\r\n//\\ E S H T /\\ S T / C\r\n");
|
||||
|
||||
@@ -96,6 +96,8 @@ class Channels
|
||||
|
||||
bool setDefaultPresetCryptoForHash(ChannelHash channelHash);
|
||||
|
||||
int16_t getHash(ChannelIndex i) { return hashes[i]; }
|
||||
|
||||
private:
|
||||
/** Given a channel index, change to use the crypto key specified by that index
|
||||
*
|
||||
@@ -113,8 +115,6 @@ class Channels
|
||||
*/
|
||||
int16_t generateHash(ChannelIndex channelNum);
|
||||
|
||||
int16_t getHash(ChannelIndex i) { return hashes[i]; }
|
||||
|
||||
/**
|
||||
* Validate a channel, fixing any errors as needed
|
||||
*/
|
||||
|
||||
@@ -124,6 +124,10 @@ void FloodingRouter::perhapsCancelDupe(const meshtastic_MeshPacket *p)
|
||||
if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER_LATE && iface) {
|
||||
iface->clampToLateRebroadcastWindow(getFrom(p), p->id);
|
||||
}
|
||||
if (config.device.role == meshtastic_Config_DeviceConfig_Role_CLIENT_BASE && iface && nodeDB &&
|
||||
nodeDB->isFromOrToFavoritedNode(*p)) {
|
||||
iface->clampToLateRebroadcastWindow(getFrom(p), p->id);
|
||||
}
|
||||
}
|
||||
|
||||
bool FloodingRouter::isRebroadcaster()
|
||||
|
||||
@@ -276,6 +276,10 @@ bool MeshService::trySendPosition(NodeNum dest, bool wantReplies)
|
||||
if (nodeDB->hasValidPosition(node)) {
|
||||
#if HAS_GPS && !MESHTASTIC_EXCLUDE_GPS
|
||||
if (positionModule) {
|
||||
if (!config.position.fixed_position && !nodeDB->hasLocalPositionSinceBoot()) {
|
||||
LOG_DEBUG("Skip position ping; no fresh position since boot");
|
||||
return false;
|
||||
}
|
||||
LOG_INFO("Send position ping to 0x%x, wantReplies=%d, channel=%d", dest, wantReplies, node->channel);
|
||||
positionModule->sendOurPosition(dest, wantReplies, node->channel);
|
||||
return true;
|
||||
|
||||
@@ -805,11 +805,11 @@ void NodeDB::installDefaultModuleConfig()
|
||||
moduleConfig.external_notification.output_ms = 500;
|
||||
moduleConfig.external_notification.nag_timeout = 2;
|
||||
#endif
|
||||
#if defined(RAK4630) || defined(RAK11310) || defined(RAK3312) || defined(MUZI_BASE)
|
||||
// Default to RAK led pin 2 (blue)
|
||||
#if defined(RAK4630) || defined(RAK11310) || defined(RAK3312) || defined(MUZI_BASE) || defined(ELECROW_ThinkNode_M3)
|
||||
// Default to PIN_LED2 for external notification output (LED color depends on device variant)
|
||||
moduleConfig.external_notification.enabled = true;
|
||||
moduleConfig.external_notification.output = PIN_LED2;
|
||||
#if defined(MUZI_BASE)
|
||||
#if defined(MUZI_BASE) || defined(ELECROW_ThinkNode_M3)
|
||||
moduleConfig.external_notification.active = false;
|
||||
#else
|
||||
moduleConfig.external_notification.active = true;
|
||||
@@ -1043,6 +1043,7 @@ void NodeDB::clearLocalPosition()
|
||||
node->position.altitude = 0;
|
||||
node->position.time = 0;
|
||||
setLocalPosition(meshtastic_Position_init_default);
|
||||
localPositionUpdatedSinceBoot = false;
|
||||
}
|
||||
|
||||
void NodeDB::cleanupMeshDB()
|
||||
|
||||
@@ -279,9 +279,13 @@ class NodeDB
|
||||
LOG_DEBUG("Set local position: lat=%i lon=%i time=%u timestamp=%u", position.latitude_i, position.longitude_i,
|
||||
position.time, position.timestamp);
|
||||
localPosition = position;
|
||||
if (position.latitude_i != 0 || position.longitude_i != 0) {
|
||||
localPositionUpdatedSinceBoot = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool hasValidPosition(const meshtastic_NodeInfoLite *n);
|
||||
bool hasLocalPositionSinceBoot() const { return localPositionUpdatedSinceBoot; }
|
||||
|
||||
#if !defined(MESHTASTIC_EXCLUDE_PKI)
|
||||
bool checkLowEntropyPublicKey(const meshtastic_Config_SecurityConfig_public_key_t &keyToTest);
|
||||
@@ -301,6 +305,7 @@ class NodeDB
|
||||
|
||||
private:
|
||||
bool duplicateWarned = false;
|
||||
bool localPositionUpdatedSinceBoot = false;
|
||||
uint32_t lastNodeDbSave = 0; // when we last saved our db to flash
|
||||
uint32_t lastBackupAttempt = 0; // when we last tried a backup automatically or manually
|
||||
uint32_t lastSort = 0; // When last sorted the nodeDB
|
||||
|
||||
@@ -296,11 +296,6 @@ bool RadioInterface::shouldRebroadcastEarlyLikeRouter(meshtastic_MeshPacket *p)
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we are a CLIENT_BASE and the packet is from or to a favorited node, we should rebroadcast early
|
||||
if (config.device.role == meshtastic_Config_DeviceConfig_Role_CLIENT_BASE) {
|
||||
return nodeDB->isFromOrToFavoritedNode(*p);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -503,6 +498,11 @@ void RadioInterface::applyModemConfig()
|
||||
cr = 5;
|
||||
sf = 10;
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_TURBO:
|
||||
bw = (myRegion->wideLora) ? 1625.0 : 500;
|
||||
cr = 8;
|
||||
sf = 11;
|
||||
break;
|
||||
default: // Config_LoRaConfig_ModemPreset_LONG_FAST is default. Gracefully use this is preset is something illegal.
|
||||
bw = (myRegion->wideLora) ? 812.5 : 250;
|
||||
cr = 5;
|
||||
@@ -539,13 +539,26 @@ void RadioInterface::applyModemConfig()
|
||||
}
|
||||
|
||||
if ((myRegion->freqEnd - myRegion->freqStart) < bw / 1000) {
|
||||
static const char *err_string = "Regional frequency range is smaller than bandwidth. Fall back to default preset";
|
||||
LOG_ERROR(err_string);
|
||||
const float regionSpanKHz = (myRegion->freqEnd - myRegion->freqStart) * 1000.0f;
|
||||
const float requestedBwKHz = bw;
|
||||
const bool isWideRequest = requestedBwKHz >= 499.5f; // treat as 500 kHz preset
|
||||
const char *presetName =
|
||||
DisplayFormatters::getModemPresetDisplayName(loraConfig.modem_preset, false, loraConfig.use_preset);
|
||||
|
||||
char err_string[160];
|
||||
if (isWideRequest) {
|
||||
snprintf(err_string, sizeof(err_string), "%s region too narrow for 500kHz preset (%s). Falling back to LongFast.",
|
||||
myRegion->name, presetName);
|
||||
} else {
|
||||
snprintf(err_string, sizeof(err_string), "%s region span %.0fkHz < requested %.0fkHz. Falling back to LongFast.",
|
||||
myRegion->name, regionSpanKHz, requestedBwKHz);
|
||||
}
|
||||
LOG_ERROR("%s", err_string);
|
||||
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
|
||||
|
||||
meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
|
||||
cn->level = meshtastic_LogRecord_Level_ERROR;
|
||||
sprintf(cn->message, err_string);
|
||||
snprintf(cn->message, sizeof(cn->message), "%s", err_string);
|
||||
service->sendClientNotification(cn);
|
||||
|
||||
// Set to default modem preset
|
||||
|
||||
@@ -526,6 +526,10 @@ DecodeState perhapsDecode(meshtastic_MeshPacket *p)
|
||||
#elif ARCH_PORTDUINO
|
||||
if (portduino_config.traceFilename != "" || portduino_config.logoutputlevel == level_trace) {
|
||||
LOG_TRACE("%s", MeshPacketSerializer::JsonSerialize(p, false).c_str());
|
||||
} else if (portduino_config.JSONFilename != "") {
|
||||
if (portduino_config.JSONFilter == (_meshtastic_PortNum)0 || portduino_config.JSONFilter == p->decoded.portnum) {
|
||||
JSONFile << MeshPacketSerializer::JsonSerialize(p, false) << std::endl;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return DecodeState::DECODE_SUCCESS;
|
||||
@@ -688,7 +692,7 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
|
||||
|
||||
// Store a copy of encrypted packet for MQTT
|
||||
DEBUG_HEAP_BEFORE;
|
||||
meshtastic_MeshPacket *p_encrypted = packetPool.allocCopy(*p);
|
||||
p_encrypted = packetPool.allocCopy(*p);
|
||||
DEBUG_HEAP_AFTER("Router::handleReceived", p_encrypted);
|
||||
|
||||
// Take those raw bytes and convert them back into a well structured protobuf we can understand
|
||||
@@ -754,6 +758,7 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
|
||||
}
|
||||
|
||||
packetPool.release(p_encrypted); // Release the encrypted packet
|
||||
p_encrypted = nullptr;
|
||||
}
|
||||
|
||||
void Router::perhapsHandleReceived(meshtastic_MeshPacket *p)
|
||||
|
||||
@@ -91,6 +91,9 @@ class Router : protected concurrency::OSThread, protected PacketHistory
|
||||
before us */
|
||||
uint32_t rxDupe = 0, txRelayCanceled = 0;
|
||||
|
||||
// pointer to the encrypted packet
|
||||
meshtastic_MeshPacket *p_encrypted = nullptr;
|
||||
|
||||
protected:
|
||||
friend class RoutingModule;
|
||||
|
||||
|
||||
@@ -33,3 +33,5 @@ PB_BIND(meshtastic_KeyVerificationAdmin, meshtastic_KeyVerificationAdmin, AUTO)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,16 @@
|
||||
#endif
|
||||
|
||||
/* Enum definitions */
|
||||
/* Firmware update mode for OTA updates */
|
||||
typedef enum _meshtastic_OTAMode {
|
||||
/* Do not reboot into OTA mode */
|
||||
meshtastic_OTAMode_NO_REBOOT_OTA = 0,
|
||||
/* Reboot into OTA mode for BLE firmware update */
|
||||
meshtastic_OTAMode_OTA_BLE = 1,
|
||||
/* Reboot into OTA mode for WiFi firmware update */
|
||||
meshtastic_OTAMode_OTA_WIFI = 2
|
||||
} meshtastic_OTAMode;
|
||||
|
||||
/* TODO: REPLACE */
|
||||
typedef enum _meshtastic_AdminMessage_ConfigType {
|
||||
/* TODO: REPLACE */
|
||||
@@ -258,10 +268,13 @@ typedef struct _meshtastic_AdminMessage {
|
||||
meshtastic_SharedContact add_contact;
|
||||
/* Initiate or respond to a key verification request */
|
||||
meshtastic_KeyVerificationAdmin key_verification;
|
||||
/* Tell the node to reboot into OTA mode for firmware update via BLE or WiFi (ESP32 only for now) */
|
||||
meshtastic_OTAMode reboot_ota_mode;
|
||||
/* Tell the node to factory reset config everything; all device state and configuration will be returned to factory defaults and BLE bonds will be cleared. */
|
||||
int32_t factory_reset_device;
|
||||
/* Tell the node to reboot into the OTA Firmware in this many seconds (or <0 to cancel reboot)
|
||||
Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth. */
|
||||
Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth.
|
||||
Deprecated in favor of reboot_ota_mode in 2.7.17 */
|
||||
int32_t reboot_ota_seconds;
|
||||
/* This message is only supported for the simulator Portduino build.
|
||||
If received the simulator will exit successfully. */
|
||||
@@ -288,6 +301,10 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
/* Helper constants for enums */
|
||||
#define _meshtastic_OTAMode_MIN meshtastic_OTAMode_NO_REBOOT_OTA
|
||||
#define _meshtastic_OTAMode_MAX meshtastic_OTAMode_OTA_WIFI
|
||||
#define _meshtastic_OTAMode_ARRAYSIZE ((meshtastic_OTAMode)(meshtastic_OTAMode_OTA_WIFI+1))
|
||||
|
||||
#define _meshtastic_AdminMessage_ConfigType_MIN meshtastic_AdminMessage_ConfigType_DEVICE_CONFIG
|
||||
#define _meshtastic_AdminMessage_ConfigType_MAX meshtastic_AdminMessage_ConfigType_DEVICEUI_CONFIG
|
||||
#define _meshtastic_AdminMessage_ConfigType_ARRAYSIZE ((meshtastic_AdminMessage_ConfigType)(meshtastic_AdminMessage_ConfigType_DEVICEUI_CONFIG+1))
|
||||
@@ -309,6 +326,7 @@ extern "C" {
|
||||
#define meshtastic_AdminMessage_payload_variant_backup_preferences_ENUMTYPE meshtastic_AdminMessage_BackupLocation
|
||||
#define meshtastic_AdminMessage_payload_variant_restore_preferences_ENUMTYPE meshtastic_AdminMessage_BackupLocation
|
||||
#define meshtastic_AdminMessage_payload_variant_remove_backup_preferences_ENUMTYPE meshtastic_AdminMessage_BackupLocation
|
||||
#define meshtastic_AdminMessage_payload_variant_reboot_ota_mode_ENUMTYPE meshtastic_OTAMode
|
||||
|
||||
|
||||
|
||||
@@ -396,6 +414,7 @@ extern "C" {
|
||||
#define meshtastic_AdminMessage_commit_edit_settings_tag 65
|
||||
#define meshtastic_AdminMessage_add_contact_tag 66
|
||||
#define meshtastic_AdminMessage_key_verification_tag 67
|
||||
#define meshtastic_AdminMessage_reboot_ota_mode_tag 68
|
||||
#define meshtastic_AdminMessage_factory_reset_device_tag 94
|
||||
#define meshtastic_AdminMessage_reboot_ota_seconds_tag 95
|
||||
#define meshtastic_AdminMessage_exit_simulator_tag 96
|
||||
@@ -454,6 +473,7 @@ X(a, STATIC, ONEOF, BOOL, (payload_variant,begin_edit_settings,begin_ed
|
||||
X(a, STATIC, ONEOF, BOOL, (payload_variant,commit_edit_settings,commit_edit_settings), 65) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,add_contact,add_contact), 66) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,key_verification,key_verification), 67) \
|
||||
X(a, STATIC, ONEOF, UENUM, (payload_variant,reboot_ota_mode,reboot_ota_mode), 68) \
|
||||
X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_device,factory_reset_device), 94) \
|
||||
X(a, STATIC, ONEOF, INT32, (payload_variant,reboot_ota_seconds,reboot_ota_seconds), 95) \
|
||||
X(a, STATIC, ONEOF, BOOL, (payload_variant,exit_simulator,exit_simulator), 96) \
|
||||
|
||||
@@ -293,7 +293,8 @@ typedef enum _meshtastic_Config_LoRaConfig_RegionCode {
|
||||
typedef enum _meshtastic_Config_LoRaConfig_ModemPreset {
|
||||
/* Long Range - Fast */
|
||||
meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST = 0,
|
||||
/* Long Range - Slow */
|
||||
/* Long Range - Slow
|
||||
Deprecated in 2.7: Unpopular slow preset. */
|
||||
meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW = 1,
|
||||
/* Very Long Range - Slow
|
||||
Deprecated in 2.5: Works only with txco and is unusably slow */
|
||||
@@ -311,7 +312,10 @@ typedef enum _meshtastic_Config_LoRaConfig_ModemPreset {
|
||||
/* Short Range - Turbo
|
||||
This is the fastest preset and the only one with 500kHz bandwidth.
|
||||
It is not legal to use in all regions due to this wider bandwidth. */
|
||||
meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO = 8
|
||||
meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO = 8,
|
||||
/* Long Range - Turbo
|
||||
This preset performs similarly to LongFast, but with 500Khz bandwidth. */
|
||||
meshtastic_Config_LoRaConfig_ModemPreset_LONG_TURBO = 9
|
||||
} meshtastic_Config_LoRaConfig_ModemPreset;
|
||||
|
||||
typedef enum _meshtastic_Config_BluetoothConfig_PairingMode {
|
||||
@@ -689,8 +693,8 @@ extern "C" {
|
||||
#define _meshtastic_Config_LoRaConfig_RegionCode_ARRAYSIZE ((meshtastic_Config_LoRaConfig_RegionCode)(meshtastic_Config_LoRaConfig_RegionCode_BR_902+1))
|
||||
|
||||
#define _meshtastic_Config_LoRaConfig_ModemPreset_MIN meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST
|
||||
#define _meshtastic_Config_LoRaConfig_ModemPreset_MAX meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO
|
||||
#define _meshtastic_Config_LoRaConfig_ModemPreset_ARRAYSIZE ((meshtastic_Config_LoRaConfig_ModemPreset)(meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO+1))
|
||||
#define _meshtastic_Config_LoRaConfig_ModemPreset_MAX meshtastic_Config_LoRaConfig_ModemPreset_LONG_TURBO
|
||||
#define _meshtastic_Config_LoRaConfig_ModemPreset_ARRAYSIZE ((meshtastic_Config_LoRaConfig_ModemPreset)(meshtastic_Config_LoRaConfig_ModemPreset_LONG_TURBO+1))
|
||||
|
||||
#define _meshtastic_Config_BluetoothConfig_PairingMode_MIN meshtastic_Config_BluetoothConfig_PairingMode_RANDOM_PIN
|
||||
#define _meshtastic_Config_BluetoothConfig_PairingMode_MAX meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN
|
||||
|
||||
@@ -24,6 +24,9 @@ PB_BIND(meshtastic_Data, meshtastic_Data, 2)
|
||||
PB_BIND(meshtastic_KeyVerification, meshtastic_KeyVerification, AUTO)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_StoreForwardPlusPlus, meshtastic_StoreForwardPlusPlus, 2)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_Waypoint, meshtastic_Waypoint, AUTO)
|
||||
|
||||
|
||||
@@ -121,6 +124,8 @@ PB_BIND(meshtastic_ChunkedPayloadResponse, meshtastic_ChunkedPayloadResponse, AU
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -478,6 +478,22 @@ typedef enum _meshtastic_Routing_Error {
|
||||
meshtastic_Routing_Error_RATE_LIMIT_EXCEEDED = 38
|
||||
} meshtastic_Routing_Error;
|
||||
|
||||
/* Enum of message types */
|
||||
typedef enum _meshtastic_StoreForwardPlusPlus_SFPP_message_type {
|
||||
/* Send an announcement of the canonical tip of a chain */
|
||||
meshtastic_StoreForwardPlusPlus_SFPP_message_type_CANON_ANNOUNCE = 0,
|
||||
/* Query whether a specific link is on the chain */
|
||||
meshtastic_StoreForwardPlusPlus_SFPP_message_type_CHAIN_QUERY = 1,
|
||||
/* Request the next link in the chain */
|
||||
meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_REQUEST = 3,
|
||||
/* Provide a link to add to the chain */
|
||||
meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE = 4,
|
||||
/* If we must fragment, send the first half */
|
||||
meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE_FIRSTHALF = 5,
|
||||
/* If we must fragment, send the second half */
|
||||
meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE_SECONDHALF = 6
|
||||
} meshtastic_StoreForwardPlusPlus_SFPP_message_type;
|
||||
|
||||
/* The priority of this message for sending.
|
||||
Higher priorities are sent first (when managing the transmit queue).
|
||||
This field is never sent over the air, it is only used internally inside of a local device node.
|
||||
@@ -782,6 +798,32 @@ typedef struct _meshtastic_KeyVerification {
|
||||
meshtastic_KeyVerification_hash2_t hash2;
|
||||
} meshtastic_KeyVerification;
|
||||
|
||||
typedef PB_BYTES_ARRAY_T(32) meshtastic_StoreForwardPlusPlus_message_hash_t;
|
||||
typedef PB_BYTES_ARRAY_T(32) meshtastic_StoreForwardPlusPlus_commit_hash_t;
|
||||
typedef PB_BYTES_ARRAY_T(32) meshtastic_StoreForwardPlusPlus_root_hash_t;
|
||||
typedef PB_BYTES_ARRAY_T(240) meshtastic_StoreForwardPlusPlus_message_t;
|
||||
/* The actual over-the-mesh message doing store and forward++ */
|
||||
typedef struct _meshtastic_StoreForwardPlusPlus {
|
||||
/* Which message type is this */
|
||||
meshtastic_StoreForwardPlusPlus_SFPP_message_type sfpp_message_type;
|
||||
/* The hash of the specific message */
|
||||
meshtastic_StoreForwardPlusPlus_message_hash_t message_hash;
|
||||
/* The hash of a link on a chain */
|
||||
meshtastic_StoreForwardPlusPlus_commit_hash_t commit_hash;
|
||||
/* the root hash of a chain */
|
||||
meshtastic_StoreForwardPlusPlus_root_hash_t root_hash;
|
||||
/* The encrypted bytes from a message */
|
||||
meshtastic_StoreForwardPlusPlus_message_t message;
|
||||
/* Message ID of the contained message */
|
||||
uint32_t encapsulated_id;
|
||||
/* Destination of the contained message */
|
||||
uint32_t encapsulated_to;
|
||||
/* Sender of the contained message */
|
||||
uint32_t encapsulated_from;
|
||||
/* The receive time of the message in question */
|
||||
uint32_t encapsulated_rxtime;
|
||||
} meshtastic_StoreForwardPlusPlus;
|
||||
|
||||
/* Waypoint message, used to share arbitrary locations across the mesh */
|
||||
typedef struct _meshtastic_Waypoint {
|
||||
/* Id of the waypoint */
|
||||
@@ -1310,6 +1352,10 @@ extern "C" {
|
||||
#define _meshtastic_Routing_Error_MAX meshtastic_Routing_Error_RATE_LIMIT_EXCEEDED
|
||||
#define _meshtastic_Routing_Error_ARRAYSIZE ((meshtastic_Routing_Error)(meshtastic_Routing_Error_RATE_LIMIT_EXCEEDED+1))
|
||||
|
||||
#define _meshtastic_StoreForwardPlusPlus_SFPP_message_type_MIN meshtastic_StoreForwardPlusPlus_SFPP_message_type_CANON_ANNOUNCE
|
||||
#define _meshtastic_StoreForwardPlusPlus_SFPP_message_type_MAX meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE_SECONDHALF
|
||||
#define _meshtastic_StoreForwardPlusPlus_SFPP_message_type_ARRAYSIZE ((meshtastic_StoreForwardPlusPlus_SFPP_message_type)(meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE_SECONDHALF+1))
|
||||
|
||||
#define _meshtastic_MeshPacket_Priority_MIN meshtastic_MeshPacket_Priority_UNSET
|
||||
#define _meshtastic_MeshPacket_Priority_MAX meshtastic_MeshPacket_Priority_MAX
|
||||
#define _meshtastic_MeshPacket_Priority_ARRAYSIZE ((meshtastic_MeshPacket_Priority)(meshtastic_MeshPacket_Priority_MAX+1))
|
||||
@@ -1338,6 +1384,8 @@ extern "C" {
|
||||
#define meshtastic_Data_portnum_ENUMTYPE meshtastic_PortNum
|
||||
|
||||
|
||||
#define meshtastic_StoreForwardPlusPlus_sfpp_message_type_ENUMTYPE meshtastic_StoreForwardPlusPlus_SFPP_message_type
|
||||
|
||||
|
||||
|
||||
#define meshtastic_MeshPacket_priority_ENUMTYPE meshtastic_MeshPacket_Priority
|
||||
@@ -1380,6 +1428,7 @@ extern "C" {
|
||||
#define meshtastic_Routing_init_default {0, {meshtastic_RouteDiscovery_init_default}}
|
||||
#define meshtastic_Data_init_default {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0, false, 0}
|
||||
#define meshtastic_KeyVerification_init_default {0, {0, {0}}, {0, {0}}}
|
||||
#define meshtastic_StoreForwardPlusPlus_init_default {_meshtastic_StoreForwardPlusPlus_SFPP_message_type_MIN, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0}
|
||||
#define meshtastic_Waypoint_init_default {0, false, 0, false, 0, 0, 0, "", "", 0}
|
||||
#define meshtastic_MqttClientProxyMessage_init_default {"", 0, {{0, {0}}}, 0}
|
||||
#define meshtastic_MeshPacket_init_default {0, 0, 0, 0, {meshtastic_Data_init_default}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0, 0, 0, 0, _meshtastic_MeshPacket_TransportMechanism_MIN}
|
||||
@@ -1411,6 +1460,7 @@ extern "C" {
|
||||
#define meshtastic_Routing_init_zero {0, {meshtastic_RouteDiscovery_init_zero}}
|
||||
#define meshtastic_Data_init_zero {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0, false, 0}
|
||||
#define meshtastic_KeyVerification_init_zero {0, {0, {0}}, {0, {0}}}
|
||||
#define meshtastic_StoreForwardPlusPlus_init_zero {_meshtastic_StoreForwardPlusPlus_SFPP_message_type_MIN, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0}
|
||||
#define meshtastic_Waypoint_init_zero {0, false, 0, false, 0, 0, 0, "", "", 0}
|
||||
#define meshtastic_MqttClientProxyMessage_init_zero {"", 0, {{0, {0}}}, 0}
|
||||
#define meshtastic_MeshPacket_init_zero {0, 0, 0, 0, {meshtastic_Data_init_zero}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0, 0, 0, 0, _meshtastic_MeshPacket_TransportMechanism_MIN}
|
||||
@@ -1489,6 +1539,15 @@ extern "C" {
|
||||
#define meshtastic_KeyVerification_nonce_tag 1
|
||||
#define meshtastic_KeyVerification_hash1_tag 2
|
||||
#define meshtastic_KeyVerification_hash2_tag 3
|
||||
#define meshtastic_StoreForwardPlusPlus_sfpp_message_type_tag 1
|
||||
#define meshtastic_StoreForwardPlusPlus_message_hash_tag 2
|
||||
#define meshtastic_StoreForwardPlusPlus_commit_hash_tag 3
|
||||
#define meshtastic_StoreForwardPlusPlus_root_hash_tag 4
|
||||
#define meshtastic_StoreForwardPlusPlus_message_tag 5
|
||||
#define meshtastic_StoreForwardPlusPlus_encapsulated_id_tag 6
|
||||
#define meshtastic_StoreForwardPlusPlus_encapsulated_to_tag 7
|
||||
#define meshtastic_StoreForwardPlusPlus_encapsulated_from_tag 8
|
||||
#define meshtastic_StoreForwardPlusPlus_encapsulated_rxtime_tag 9
|
||||
#define meshtastic_Waypoint_id_tag 1
|
||||
#define meshtastic_Waypoint_latitude_i_tag 2
|
||||
#define meshtastic_Waypoint_longitude_i_tag 3
|
||||
@@ -1705,6 +1764,19 @@ X(a, STATIC, SINGULAR, BYTES, hash2, 3)
|
||||
#define meshtastic_KeyVerification_CALLBACK NULL
|
||||
#define meshtastic_KeyVerification_DEFAULT NULL
|
||||
|
||||
#define meshtastic_StoreForwardPlusPlus_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UENUM, sfpp_message_type, 1) \
|
||||
X(a, STATIC, SINGULAR, BYTES, message_hash, 2) \
|
||||
X(a, STATIC, SINGULAR, BYTES, commit_hash, 3) \
|
||||
X(a, STATIC, SINGULAR, BYTES, root_hash, 4) \
|
||||
X(a, STATIC, SINGULAR, BYTES, message, 5) \
|
||||
X(a, STATIC, SINGULAR, UINT32, encapsulated_id, 6) \
|
||||
X(a, STATIC, SINGULAR, UINT32, encapsulated_to, 7) \
|
||||
X(a, STATIC, SINGULAR, UINT32, encapsulated_from, 8) \
|
||||
X(a, STATIC, SINGULAR, UINT32, encapsulated_rxtime, 9)
|
||||
#define meshtastic_StoreForwardPlusPlus_CALLBACK NULL
|
||||
#define meshtastic_StoreForwardPlusPlus_DEFAULT NULL
|
||||
|
||||
#define meshtastic_Waypoint_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UINT32, id, 1) \
|
||||
X(a, STATIC, OPTIONAL, SFIXED32, latitude_i, 2) \
|
||||
@@ -1980,6 +2052,7 @@ extern const pb_msgdesc_t meshtastic_RouteDiscovery_msg;
|
||||
extern const pb_msgdesc_t meshtastic_Routing_msg;
|
||||
extern const pb_msgdesc_t meshtastic_Data_msg;
|
||||
extern const pb_msgdesc_t meshtastic_KeyVerification_msg;
|
||||
extern const pb_msgdesc_t meshtastic_StoreForwardPlusPlus_msg;
|
||||
extern const pb_msgdesc_t meshtastic_Waypoint_msg;
|
||||
extern const pb_msgdesc_t meshtastic_MqttClientProxyMessage_msg;
|
||||
extern const pb_msgdesc_t meshtastic_MeshPacket_msg;
|
||||
@@ -2013,6 +2086,7 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
|
||||
#define meshtastic_Routing_fields &meshtastic_Routing_msg
|
||||
#define meshtastic_Data_fields &meshtastic_Data_msg
|
||||
#define meshtastic_KeyVerification_fields &meshtastic_KeyVerification_msg
|
||||
#define meshtastic_StoreForwardPlusPlus_fields &meshtastic_StoreForwardPlusPlus_msg
|
||||
#define meshtastic_Waypoint_fields &meshtastic_Waypoint_msg
|
||||
#define meshtastic_MqttClientProxyMessage_fields &meshtastic_MqttClientProxyMessage_msg
|
||||
#define meshtastic_MeshPacket_fields &meshtastic_MeshPacket_msg
|
||||
@@ -2069,6 +2143,7 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
|
||||
#define meshtastic_QueueStatus_size 23
|
||||
#define meshtastic_RouteDiscovery_size 256
|
||||
#define meshtastic_Routing_size 259
|
||||
#define meshtastic_StoreForwardPlusPlus_size 371
|
||||
#define meshtastic_ToRadio_size 504
|
||||
#define meshtastic_User_size 115
|
||||
#define meshtastic_Waypoint_size 165
|
||||
|
||||
@@ -86,6 +86,11 @@ typedef enum _meshtastic_PortNum {
|
||||
/* Paxcounter lib included in the firmware
|
||||
ENCODING: protobuf */
|
||||
meshtastic_PortNum_PAXCOUNTER_APP = 34,
|
||||
/* Store and Forward++ module included in the firmware
|
||||
ENCODING: protobuf
|
||||
This module is specifically for Native Linux nodes, and provides a Git-style
|
||||
chain of messages. */
|
||||
meshtastic_PortNum_STORE_FORWARD_PLUSPLUS_APP = 35,
|
||||
/* Provides a hardware serial interface to send and receive from the Meshtastic network.
|
||||
Connect to the RX/TX pins of a device with 38400 8N1. Packets received from the Meshtastic
|
||||
network is forwarded to the RX pin while sending a packet to TX will go out to the Mesh network.
|
||||
|
||||
@@ -417,6 +417,9 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
|
||||
}
|
||||
case meshtastic_AdminMessage_enter_dfu_mode_request_tag: {
|
||||
LOG_INFO("Client requesting to enter DFU mode");
|
||||
#if HAS_SCREEN
|
||||
IF_SCREEN(screen->showSimpleBanner("Device is rebooting\ninto DFU mode.", 0));
|
||||
#endif
|
||||
#if defined(ARCH_NRF52) || defined(ARCH_RP2040)
|
||||
enterDfuMode();
|
||||
#endif
|
||||
|
||||
@@ -7,17 +7,41 @@
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
#include <Throttle.h>
|
||||
#include <algorithm>
|
||||
|
||||
#ifndef USERPREFS_NODEINFO_REPLY_SUPPRESS_SECS
|
||||
#define USERPREFS_NODEINFO_REPLY_SUPPRESS_SECS (12 * 60 * 60)
|
||||
#endif
|
||||
|
||||
NodeInfoModule *nodeInfoModule;
|
||||
|
||||
static constexpr uint32_t NodeInfoReplySuppressSeconds = USERPREFS_NODEINFO_REPLY_SUPPRESS_SECS;
|
||||
|
||||
bool NodeInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_User *pptr)
|
||||
{
|
||||
suppressReplyForCurrentRequest = false;
|
||||
|
||||
if (mp.from == nodeDB->getNodeNum()) {
|
||||
LOG_WARN("Ignoring packet supposed to be from our own node: %08x", mp.from);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto p = *pptr;
|
||||
|
||||
if (mp.decoded.want_response) {
|
||||
const NodeNum sender = getFrom(&mp);
|
||||
const uint32_t now = mp.rx_time ? mp.rx_time : getTime();
|
||||
auto it = lastNodeInfoSeen.find(sender);
|
||||
if (it != lastNodeInfoSeen.end()) {
|
||||
uint32_t sinceLast = now >= it->second ? now - it->second : 0;
|
||||
if (sinceLast < NodeInfoReplySuppressSeconds) {
|
||||
suppressReplyForCurrentRequest = true;
|
||||
}
|
||||
}
|
||||
lastNodeInfoSeen[sender] = now;
|
||||
pruneLastNodeInfoCache();
|
||||
}
|
||||
|
||||
if (p.is_licensed != owner.is_licensed) {
|
||||
LOG_WARN("Invalid nodeInfo detected, is_licensed mismatch!");
|
||||
return true;
|
||||
@@ -42,6 +66,8 @@ bool NodeInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
|
||||
service->sendToPhone(packetCopy);
|
||||
}
|
||||
|
||||
pruneLastNodeInfoCache();
|
||||
|
||||
// LOG_DEBUG("did handleReceived");
|
||||
return false; // Let others look at this message also if they want
|
||||
}
|
||||
@@ -68,9 +94,11 @@ void NodeInfoModule::sendOurNodeInfo(NodeNum dest, bool wantReplies, uint8_t cha
|
||||
|
||||
if (p) { // Check whether we didn't ignore it
|
||||
p->to = dest;
|
||||
p->decoded.want_response = (config.device.role != meshtastic_Config_DeviceConfig_Role_TRACKER &&
|
||||
bool requestWantResponse = (config.device.role != meshtastic_Config_DeviceConfig_Role_TRACKER &&
|
||||
config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) &&
|
||||
wantReplies;
|
||||
|
||||
p->decoded.want_response = requestWantResponse;
|
||||
if (_shorterTimeout)
|
||||
p->priority = meshtastic_MeshPacket_Priority_DEFAULT;
|
||||
else
|
||||
@@ -89,6 +117,13 @@ void NodeInfoModule::sendOurNodeInfo(NodeNum dest, bool wantReplies, uint8_t cha
|
||||
|
||||
meshtastic_MeshPacket *NodeInfoModule::allocReply()
|
||||
{
|
||||
if (suppressReplyForCurrentRequest) {
|
||||
LOG_DEBUG("Skip send NodeInfo since we heard the requester <12h ago");
|
||||
ignoreRequest = true;
|
||||
suppressReplyForCurrentRequest = false;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!airTime->isTxAllowedChannelUtil(false)) {
|
||||
ignoreRequest = true; // Mark it as ignored for MeshModule
|
||||
LOG_DEBUG("Skip send NodeInfo > 40%% ch. util");
|
||||
@@ -125,6 +160,29 @@ meshtastic_MeshPacket *NodeInfoModule::allocReply()
|
||||
}
|
||||
}
|
||||
|
||||
void NodeInfoModule::pruneLastNodeInfoCache()
|
||||
{
|
||||
if (!nodeDB || !nodeDB->meshNodes)
|
||||
return;
|
||||
|
||||
const size_t maxEntries = nodeDB->meshNodes->size();
|
||||
|
||||
for (auto it = lastNodeInfoSeen.begin(); it != lastNodeInfoSeen.end();) {
|
||||
if (!nodeDB->getMeshNode(it->first)) {
|
||||
it = lastNodeInfoSeen.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
while (!lastNodeInfoSeen.empty() && lastNodeInfoSeen.size() > maxEntries) {
|
||||
auto oldestIt = std::min_element(lastNodeInfoSeen.begin(), lastNodeInfoSeen.end(),
|
||||
[](const std::pair<const NodeNum, uint32_t> &lhs,
|
||||
const std::pair<const NodeNum, uint32_t> &rhs) { return lhs.second < rhs.second; });
|
||||
lastNodeInfoSeen.erase(oldestIt);
|
||||
}
|
||||
}
|
||||
|
||||
NodeInfoModule::NodeInfoModule()
|
||||
: ProtobufModule("nodeinfo", meshtastic_PortNum_NODEINFO_APP, &meshtastic_User_msg), concurrency::OSThread("NodeInfo")
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "ProtobufModule.h"
|
||||
#include <map>
|
||||
|
||||
/**
|
||||
* NodeInfo module for sending/receiving NodeInfos into the mesh
|
||||
@@ -43,6 +44,10 @@ class NodeInfoModule : public ProtobufModule<meshtastic_User>, private concurren
|
||||
private:
|
||||
uint32_t lastSentToMesh = 0; // Last time we sent our NodeInfo to the mesh
|
||||
bool shorterTimeout = false;
|
||||
bool suppressReplyForCurrentRequest = false;
|
||||
std::map<NodeNum, uint32_t> lastNodeInfoSeen;
|
||||
|
||||
void pruneLastNodeInfoCache();
|
||||
};
|
||||
|
||||
extern NodeInfoModule *nodeInfoModule;
|
||||
|
||||
@@ -349,6 +349,11 @@ void PositionModule::sendOurPosition()
|
||||
|
||||
void PositionModule::sendOurPosition(NodeNum dest, bool wantReplies, uint8_t channel)
|
||||
{
|
||||
if (!config.position.fixed_position && !nodeDB->hasLocalPositionSinceBoot()) {
|
||||
LOG_DEBUG("Skip position send; no fresh position since boot");
|
||||
return;
|
||||
}
|
||||
|
||||
// cancel any not yet sent (now stale) position packets
|
||||
if (prevPacketId) // if we wrap around to zero, we'll simply fail to cancel in that rare case (no big deal)
|
||||
service->cancelSending(prevPacketId);
|
||||
@@ -420,8 +425,12 @@ int32_t PositionModule::runOnce()
|
||||
return RUNONCE_INTERVAL;
|
||||
}
|
||||
|
||||
bool waitingForFreshPosition = (lastGpsSend == 0) && !config.position.fixed_position && !nodeDB->hasLocalPositionSinceBoot();
|
||||
|
||||
if (lastGpsSend == 0 || msSinceLastSend >= intervalMs) {
|
||||
if (nodeDB->hasValidPosition(node)) {
|
||||
if (waitingForFreshPosition) {
|
||||
LOG_DEBUG("Skip initial position send; no fresh position since boot");
|
||||
} else if (nodeDB->hasValidPosition(node)) {
|
||||
lastGpsSend = now;
|
||||
|
||||
lastGpsLatitude = node->position.latitude_i;
|
||||
|
||||
@@ -26,7 +26,11 @@ int StatusLEDModule::handleStatusUpdate(const meshtastic::Status *arg)
|
||||
power_state = charged;
|
||||
}
|
||||
} else {
|
||||
power_state = discharging;
|
||||
if (powerStatus->getBatteryChargePercent() > 5) {
|
||||
power_state = discharging;
|
||||
} else {
|
||||
power_state = critical;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -58,16 +62,33 @@ int StatusLEDModule::handleStatusUpdate(const meshtastic::Status *arg)
|
||||
|
||||
int32_t StatusLEDModule::runOnce()
|
||||
{
|
||||
my_interval = 1000;
|
||||
|
||||
if (power_state == charging) {
|
||||
CHARGE_LED_state = !CHARGE_LED_state;
|
||||
} else if (power_state == charged) {
|
||||
CHARGE_LED_state = LED_STATE_ON;
|
||||
} else if (power_state == critical) {
|
||||
if (POWER_LED_starttime + 30000 < millis() && !doing_fast_blink) {
|
||||
doing_fast_blink = true;
|
||||
POWER_LED_starttime = millis();
|
||||
}
|
||||
if (doing_fast_blink) {
|
||||
PAIRING_LED_state = LED_STATE_OFF;
|
||||
CHARGE_LED_state = !CHARGE_LED_state;
|
||||
my_interval = 250;
|
||||
if (POWER_LED_starttime + 2000 < millis()) {
|
||||
doing_fast_blink = false;
|
||||
}
|
||||
} else {
|
||||
CHARGE_LED_state = LED_STATE_OFF;
|
||||
}
|
||||
|
||||
} else {
|
||||
CHARGE_LED_state = LED_STATE_OFF;
|
||||
}
|
||||
|
||||
if (!config.bluetooth.enabled || PAIRING_LED_starttime + 30 * 1000 < millis()) {
|
||||
if (!config.bluetooth.enabled || PAIRING_LED_starttime + 30 * 1000 < millis() || doing_fast_blink) {
|
||||
PAIRING_LED_state = LED_STATE_OFF;
|
||||
} else if (ble_state == unpaired) {
|
||||
if (slowTrack) {
|
||||
|
||||
@@ -31,8 +31,10 @@ class StatusLEDModule : private concurrency::OSThread
|
||||
bool PAIRING_LED_state = LED_STATE_OFF;
|
||||
|
||||
uint32_t PAIRING_LED_starttime = 0;
|
||||
uint32_t POWER_LED_starttime = 0;
|
||||
bool doing_fast_blink = false;
|
||||
|
||||
enum PowerState { discharging, charging, charged };
|
||||
enum PowerState { discharging, charging, charged, critical };
|
||||
|
||||
PowerState power_state = discharging;
|
||||
|
||||
|
||||
@@ -2,16 +2,14 @@
|
||||
|
||||
#if !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C && defined(HAS_BMA423) && __has_include(<SensorBMA423.hpp>)
|
||||
|
||||
using namespace MotionSensorI2C;
|
||||
|
||||
BMA423Sensor::BMA423Sensor(ScanI2C::FoundDevice foundDevice) : MotionSensor::MotionSensor(foundDevice) {}
|
||||
|
||||
bool BMA423Sensor::init()
|
||||
{
|
||||
if (sensor.begin(deviceAddress(), &MotionSensorI2C::readRegister, &MotionSensorI2C::writeRegister)) {
|
||||
if (sensor.begin(Wire, deviceAddress())) {
|
||||
sensor.configAccelerometer(sensor.RANGE_2G, sensor.ODR_100HZ, sensor.BW_NORMAL_AVG4, sensor.PERF_CONTINUOUS_MODE);
|
||||
sensor.enableAccelerometer();
|
||||
sensor.configInterrupt(BMA4_LEVEL_TRIGGER, BMA4_ACTIVE_HIGH, BMA4_PUSH_PULL, BMA4_OUTPUT_ENABLE, BMA4_INPUT_DISABLE);
|
||||
sensor.configInterrupt();
|
||||
|
||||
#ifdef BMA423_INT
|
||||
pinMode(BMA4XX_INT, INPUT);
|
||||
@@ -26,9 +24,9 @@ bool BMA423Sensor::init()
|
||||
|
||||
#ifdef T_WATCH_S3
|
||||
// Need to raise the wrist function, need to set the correct axis
|
||||
sensor.setReampAxes(sensor.REMAP_TOP_LAYER_RIGHT_CORNER);
|
||||
sensor.setRemapAxes(sensor.REMAP_TOP_LAYER_RIGHT_CORNER);
|
||||
#else
|
||||
sensor.setReampAxes(sensor.REMAP_BOTTOM_LAYER_BOTTOM_LEFT_CORNER);
|
||||
sensor.setRemapAxes(sensor.REMAP_BOTTOM_LAYER_BOTTOM_LEFT_CORNER);
|
||||
#endif
|
||||
// sensor.enableFeature(sensor.FEATURE_STEP_CNTR, true);
|
||||
sensor.enableFeature(sensor.FEATURE_TILT, true);
|
||||
@@ -50,7 +48,7 @@ bool BMA423Sensor::init()
|
||||
|
||||
int32_t BMA423Sensor::runOnce()
|
||||
{
|
||||
if (sensor.readIrqStatus() != DEV_WIRE_NONE) {
|
||||
if (sensor.readIrqStatus()) {
|
||||
if (sensor.isTilt() || sensor.isDoubleTap()) {
|
||||
wakeScreen();
|
||||
return 500;
|
||||
|
||||
@@ -61,32 +61,6 @@ class MotionSensor
|
||||
uint32_t endCalibrationAt = 0;
|
||||
};
|
||||
|
||||
namespace MotionSensorI2C
|
||||
{
|
||||
|
||||
static inline int readRegister(uint8_t address, uint8_t reg, uint8_t *data, uint8_t len)
|
||||
{
|
||||
Wire.beginTransmission(address);
|
||||
Wire.write(reg);
|
||||
Wire.endTransmission();
|
||||
Wire.requestFrom((uint8_t)address, (uint8_t)len);
|
||||
uint8_t i = 0;
|
||||
while (Wire.available()) {
|
||||
data[i++] = Wire.read();
|
||||
}
|
||||
return 0; // Pass
|
||||
}
|
||||
|
||||
static inline int writeRegister(uint8_t address, uint8_t reg, uint8_t *data, uint8_t len)
|
||||
{
|
||||
Wire.beginTransmission(address);
|
||||
Wire.write(reg);
|
||||
Wire.write(data, len);
|
||||
return (0 != Wire.endTransmission());
|
||||
}
|
||||
|
||||
} // namespace MotionSensorI2C
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -14,11 +14,11 @@
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
#ifdef NIMBLE_TWO
|
||||
#include "NimBLEAdvertising.h"
|
||||
#ifdef CONFIG_BT_NIMBLE_EXT_ADV
|
||||
#include "NimBLEExtAdvertising.h"
|
||||
#include "PowerStatus.h"
|
||||
#endif
|
||||
#include "PowerStatus.h"
|
||||
|
||||
#if defined(CONFIG_NIMBLE_CPP_IDF)
|
||||
#include "host/ble_gap.h"
|
||||
@@ -26,15 +26,12 @@
|
||||
#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;
|
||||
constexpr uint16_t kPreferredBleTxOctets = 251;
|
||||
constexpr uint16_t kPreferredBleTxTimeUs = (kPreferredBleTxOctets + 14) * 8;
|
||||
} // namespace
|
||||
#endif
|
||||
|
||||
// Debugging options: careful, they slow things down quite a bit!
|
||||
// #define DEBUG_NIMBLE_ON_READ_TIMING // uncomment to time onRead duration
|
||||
@@ -313,11 +310,9 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread
|
||||
{
|
||||
PhoneAPI::onNowHasData(fromRadioNum);
|
||||
|
||||
int currentNotifyCount = notifyCount.fetch_add(1);
|
||||
|
||||
uint8_t cc = bleServer->getConnectedCount();
|
||||
|
||||
#ifdef DEBUG_NIMBLE_NOTIFY
|
||||
int currentNotifyCount = notifyCount.fetch_add(1);
|
||||
uint8_t cc = bleServer->getConnectedCount();
|
||||
// This logging slows things down when there are lots of packets going to the phone, like initial connection:
|
||||
LOG_DEBUG("BLE notify(%d) fromNum: %d connections: %d", currentNotifyCount, fromRadioNum, cc);
|
||||
#endif
|
||||
@@ -326,13 +321,7 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread
|
||||
put_le32(val, fromRadioNum);
|
||||
|
||||
fromNumCharacteristic->setValue(val, sizeof(val));
|
||||
#ifdef NIMBLE_TWO
|
||||
// NOTE: I don't have any NIMBLE_TWO devices, but this line makes me suspicious, and I suspect it needs to just be
|
||||
// notify().
|
||||
fromNumCharacteristic->notify(val, sizeof(val), BLE_HS_CONN_HANDLE_NONE);
|
||||
#else
|
||||
fromNumCharacteristic->notify();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Check the current underlying physical link to see if the client is currently connected
|
||||
@@ -397,12 +386,7 @@ static uint8_t lastToRadio[MAX_TO_FROM_RADIO_SIZE];
|
||||
|
||||
class NimbleBluetoothToRadioCallback : public NimBLECharacteristicCallbacks
|
||||
{
|
||||
#ifdef NIMBLE_TWO
|
||||
virtual void onWrite(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &connInfo)
|
||||
#else
|
||||
virtual void onWrite(NimBLECharacteristic *pCharacteristic)
|
||||
|
||||
#endif
|
||||
void onWrite(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &) override
|
||||
{
|
||||
// CAUTION: This callback runs in the NimBLE task!!! Don't do anything except communicate with the main task's runOnce.
|
||||
// Assumption: onWrite is serialized by NimBLE, so we don't need to lock here against multiple concurrent onWrite calls.
|
||||
@@ -449,11 +433,7 @@ class NimbleBluetoothToRadioCallback : public NimBLECharacteristicCallbacks
|
||||
|
||||
class NimbleBluetoothFromRadioCallback : public NimBLECharacteristicCallbacks
|
||||
{
|
||||
#ifdef NIMBLE_TWO
|
||||
virtual void onRead(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &connInfo)
|
||||
#else
|
||||
virtual void onRead(NimBLECharacteristic *pCharacteristic)
|
||||
#endif
|
||||
void onRead(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &) override
|
||||
{
|
||||
// CAUTION: This callback runs in the NimBLE task!!! Don't do anything except communicate with the main task's runOnce.
|
||||
|
||||
@@ -561,32 +541,27 @@ class NimbleBluetoothFromRadioCallback : public NimBLECharacteristicCallbacks
|
||||
|
||||
class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
|
||||
{
|
||||
#ifdef NIMBLE_TWO
|
||||
public:
|
||||
NimbleBluetoothServerCallback(NimbleBluetooth *ble) { this->ble = ble; }
|
||||
explicit NimbleBluetoothServerCallback(NimbleBluetooth *ble) : ble(ble) {}
|
||||
|
||||
private:
|
||||
NimbleBluetooth *ble;
|
||||
|
||||
virtual uint32_t onPassKeyDisplay()
|
||||
#else
|
||||
virtual uint32_t onPassKeyRequest()
|
||||
#endif
|
||||
uint32_t onPassKeyDisplay() override
|
||||
{
|
||||
uint32_t passkey = config.bluetooth.fixed_pin;
|
||||
|
||||
if (config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_RANDOM_PIN) {
|
||||
LOG_INFO("Use random passkey");
|
||||
// This is the passkey to be entered on peer - we pick a number >100,000 to ensure 6 digits
|
||||
passkey = random(100000, 999999);
|
||||
}
|
||||
LOG_INFO("*** Enter passkey %d on the peer side ***", passkey);
|
||||
LOG_INFO("*** Enter passkey %06u on the peer side ***", passkey);
|
||||
|
||||
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
|
||||
meshtastic::BluetoothStatus newStatus(std::to_string(passkey));
|
||||
bluetoothStatus->updateStatus(&newStatus);
|
||||
|
||||
#if HAS_SCREEN // Todo: migrate this display code back into Screen class, and observe bluetoothStatus
|
||||
#if HAS_SCREEN
|
||||
if (screen) {
|
||||
screen->startAlert([passkey](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
|
||||
char btPIN[16] = "888888";
|
||||
@@ -615,39 +590,29 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
|
||||
});
|
||||
}
|
||||
#endif
|
||||
passkeyShowing = true;
|
||||
|
||||
passkeyShowing = true;
|
||||
return passkey;
|
||||
}
|
||||
|
||||
#ifdef NIMBLE_TWO
|
||||
virtual void onAuthenticationComplete(NimBLEConnInfo &connInfo)
|
||||
#else
|
||||
virtual void onAuthenticationComplete(ble_gap_conn_desc *desc)
|
||||
#endif
|
||||
void onAuthenticationComplete(NimBLEConnInfo &connInfo) override
|
||||
{
|
||||
LOG_INFO("BLE authentication complete");
|
||||
|
||||
meshtastic::BluetoothStatus newStatus(meshtastic::BluetoothStatus::ConnectionState::CONNECTED);
|
||||
bluetoothStatus->updateStatus(&newStatus);
|
||||
|
||||
// Todo: migrate this display code back into Screen class, and observe bluetoothStatus
|
||||
if (passkeyShowing) {
|
||||
passkeyShowing = false;
|
||||
if (screen)
|
||||
if (screen) {
|
||||
screen->endAlert();
|
||||
}
|
||||
}
|
||||
|
||||
// Store the connection handle for future use
|
||||
#ifdef NIMBLE_TWO
|
||||
nimbleBluetoothConnHandle = connInfo.getConnHandle();
|
||||
#else
|
||||
nimbleBluetoothConnHandle = desc->conn_handle;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef NIMBLE_TWO
|
||||
virtual void onConnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo)
|
||||
void onConnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo) override
|
||||
{
|
||||
LOG_INFO("BLE incoming connection %s", connInfo.getAddress().toString().c_str());
|
||||
|
||||
@@ -672,21 +637,12 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
|
||||
LOG_INFO("BLE conn %u initial MTU %u (target %u)", connHandle, connInfo.getMTU(), kPreferredBleMtu);
|
||||
pServer->updateConnParams(connHandle, 6, 12, 0, 200);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef NIMBLE_TWO
|
||||
virtual void onDisconnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo, int reason)
|
||||
void onDisconnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo, int reason) override
|
||||
{
|
||||
LOG_INFO("BLE disconnect reason: %d", reason);
|
||||
#else
|
||||
virtual void onDisconnect(NimBLEServer *pServer, ble_gap_conn_desc *desc)
|
||||
{
|
||||
LOG_INFO("BLE disconnect");
|
||||
#endif
|
||||
#ifdef NIMBLE_TWO
|
||||
if (ble->isDeInit)
|
||||
return;
|
||||
#endif
|
||||
|
||||
meshtastic::BluetoothStatus newStatus(meshtastic::BluetoothStatus::ConnectionState::DISCONNECTED);
|
||||
bluetoothStatus->updateStatus(&newStatus);
|
||||
@@ -710,35 +666,69 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
|
||||
bluetoothPhoneAPI->writeCount = 0;
|
||||
}
|
||||
|
||||
// Clear the last ToRadio packet buffer to avoid rejecting first packet from new connection
|
||||
memset(lastToRadio, 0, sizeof(lastToRadio));
|
||||
|
||||
nimbleBluetoothConnHandle = BLE_HS_CONN_HANDLE_NONE; // BLE_HS_CONN_HANDLE_NONE means "no connection"
|
||||
nimbleBluetoothConnHandle = BLE_HS_CONN_HANDLE_NONE;
|
||||
|
||||
#ifdef NIMBLE_TWO
|
||||
// Restart Advertising
|
||||
ble->startAdvertising();
|
||||
#else
|
||||
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||
if (!pAdvertising->start(0)) {
|
||||
if (pAdvertising->isAdvertising()) {
|
||||
LOG_DEBUG("BLE advertising already running");
|
||||
} else {
|
||||
LOG_ERROR("BLE failed to restart advertising");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
static NimbleBluetoothToRadioCallback *toRadioCallbacks;
|
||||
static NimbleBluetoothFromRadioCallback *fromRadioCallbacks;
|
||||
|
||||
void NimbleBluetooth::startAdvertising()
|
||||
{
|
||||
#if defined(CONFIG_BT_NIMBLE_EXT_ADV)
|
||||
NimBLEExtAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||
NimBLEExtAdvertisement legacyAdvertising;
|
||||
|
||||
legacyAdvertising.setLegacyAdvertising(true);
|
||||
legacyAdvertising.setScannable(true);
|
||||
legacyAdvertising.setConnectable(true);
|
||||
legacyAdvertising.setFlags(BLE_HS_ADV_F_DISC_GEN);
|
||||
if (powerStatus->getHasBattery() == 1) {
|
||||
legacyAdvertising.setCompleteServices(NimBLEUUID((uint16_t)0x180f));
|
||||
}
|
||||
legacyAdvertising.setCompleteServices(NimBLEUUID(MESH_SERVICE_UUID));
|
||||
legacyAdvertising.setMinInterval(500);
|
||||
legacyAdvertising.setMaxInterval(1000);
|
||||
|
||||
NimBLEExtAdvertisement legacyScanResponse;
|
||||
legacyScanResponse.setLegacyAdvertising(true);
|
||||
legacyScanResponse.setConnectable(true);
|
||||
legacyScanResponse.setName(getDeviceName());
|
||||
|
||||
if (!pAdvertising->setInstanceData(0, legacyAdvertising)) {
|
||||
LOG_ERROR("BLE failed to set legacyAdvertising");
|
||||
} else if (!pAdvertising->setScanResponseData(0, legacyScanResponse)) {
|
||||
LOG_ERROR("BLE failed to set legacyScanResponse");
|
||||
} else if (!pAdvertising->start(0, 0, 0)) {
|
||||
LOG_ERROR("BLE failed to start legacyAdvertising");
|
||||
}
|
||||
#else
|
||||
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||
pAdvertising->reset();
|
||||
pAdvertising->addServiceUUID(MESH_SERVICE_UUID);
|
||||
if (powerStatus->getHasBattery() == 1) {
|
||||
pAdvertising->addServiceUUID(NimBLEUUID((uint16_t)0x180f));
|
||||
}
|
||||
|
||||
NimBLEAdvertisementData scan;
|
||||
scan.setName(getDeviceName());
|
||||
pAdvertising->setScanResponseData(scan);
|
||||
pAdvertising->enableScanResponse(true);
|
||||
|
||||
if (!pAdvertising->start(0)) {
|
||||
LOG_ERROR("BLE failed to start advertising");
|
||||
}
|
||||
#endif
|
||||
LOG_DEBUG("BLE Advertising started");
|
||||
}
|
||||
|
||||
void NimbleBluetooth::shutdown()
|
||||
{
|
||||
// No measurable power saving for ESP32 during light-sleep(?)
|
||||
#ifndef ARCH_ESP32
|
||||
// Shutdown bluetooth for minimum power draw
|
||||
LOG_INFO("Disable bluetooth");
|
||||
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||
pAdvertising->reset();
|
||||
@@ -746,7 +736,6 @@ void NimbleBluetooth::shutdown()
|
||||
#endif
|
||||
}
|
||||
|
||||
// Proper shutdown for ESP32. Needs reboot to reverse.
|
||||
void NimbleBluetooth::deinit()
|
||||
{
|
||||
#ifdef ARCH_ESP32
|
||||
@@ -760,21 +749,17 @@ void NimbleBluetooth::deinit()
|
||||
digitalWrite(BLE_LED, LOW);
|
||||
#endif
|
||||
#endif
|
||||
#ifndef NIMBLE_TWO
|
||||
NimBLEDevice::deinit();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// Has initial setup been completed
|
||||
bool NimbleBluetooth::isActive()
|
||||
{
|
||||
return bleServer;
|
||||
return bleServer != nullptr;
|
||||
}
|
||||
|
||||
bool NimbleBluetooth::isConnected()
|
||||
{
|
||||
return bleServer->getConnectedCount() > 0;
|
||||
return bleServer && bleServer->getConnectedCount() > 0;
|
||||
}
|
||||
|
||||
int NimbleBluetooth::getRssi()
|
||||
@@ -818,7 +803,7 @@ void NimbleBluetooth::setup()
|
||||
LOG_INFO("Init the NimBLE bluetooth module");
|
||||
|
||||
NimBLEDevice::init(getDeviceName());
|
||||
NimBLEDevice::setPower(ESP_PWR_LVL_P9);
|
||||
NimBLEDevice::setPower(9);
|
||||
|
||||
#if NIMBLE_ENABLE_2M_PHY && (defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6))
|
||||
int mtuResult = NimBLEDevice::setMTU(kPreferredBleMtu);
|
||||
@@ -851,11 +836,7 @@ void NimbleBluetooth::setup()
|
||||
NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY);
|
||||
}
|
||||
bleServer = NimBLEDevice::createServer();
|
||||
#ifdef NIMBLE_TWO
|
||||
NimbleBluetoothServerCallback *serverCallbacks = new NimbleBluetoothServerCallback(this);
|
||||
#else
|
||||
NimbleBluetoothServerCallback *serverCallbacks = new NimbleBluetoothServerCallback();
|
||||
#endif
|
||||
auto *serverCallbacks = new NimbleBluetoothServerCallback(this);
|
||||
bleServer->setCallbacks(serverCallbacks, true);
|
||||
setupService();
|
||||
startAdvertising();
|
||||
@@ -900,11 +881,7 @@ void NimbleBluetooth::setupService()
|
||||
NimBLEService *batteryService = bleServer->createService(NimBLEUUID((uint16_t)0x180f)); // 0x180F is the Battery Service
|
||||
BatteryCharacteristic = batteryService->createCharacteristic( // 0x2A19 is the Battery Level characteristic)
|
||||
(uint16_t)0x2a19, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY, 1);
|
||||
#ifdef NIMBLE_TWO
|
||||
NimBLE2904 *batteryLevelDescriptor = BatteryCharacteristic->create2904();
|
||||
#else
|
||||
NimBLE2904 *batteryLevelDescriptor = (NimBLE2904 *)BatteryCharacteristic->createDescriptor((uint16_t)0x2904);
|
||||
#endif
|
||||
batteryLevelDescriptor->setFormat(NimBLE2904::FORMAT_UINT8);
|
||||
batteryLevelDescriptor->setNamespace(1);
|
||||
batteryLevelDescriptor->setUnit(0x27ad);
|
||||
@@ -912,54 +889,12 @@ void NimbleBluetooth::setupService()
|
||||
batteryService->start();
|
||||
}
|
||||
|
||||
void NimbleBluetooth::startAdvertising()
|
||||
{
|
||||
#ifdef NIMBLE_TWO
|
||||
NimBLEExtAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||
NimBLEExtAdvertisement legacyAdvertising;
|
||||
|
||||
legacyAdvertising.setLegacyAdvertising(true);
|
||||
legacyAdvertising.setScannable(true);
|
||||
legacyAdvertising.setConnectable(true);
|
||||
legacyAdvertising.setFlags(BLE_HS_ADV_F_DISC_GEN);
|
||||
if (powerStatus->getHasBattery() == 1) {
|
||||
legacyAdvertising.setCompleteServices(NimBLEUUID((uint16_t)0x180f));
|
||||
}
|
||||
legacyAdvertising.setCompleteServices(NimBLEUUID(MESH_SERVICE_UUID));
|
||||
legacyAdvertising.setMinInterval(500);
|
||||
legacyAdvertising.setMaxInterval(1000);
|
||||
|
||||
NimBLEExtAdvertisement legacyScanResponse;
|
||||
legacyScanResponse.setLegacyAdvertising(true);
|
||||
legacyScanResponse.setConnectable(true);
|
||||
legacyScanResponse.setName(getDeviceName());
|
||||
|
||||
if (!pAdvertising->setInstanceData(0, legacyAdvertising)) {
|
||||
LOG_ERROR("BLE failed to set legacyAdvertising");
|
||||
} else if (!pAdvertising->setScanResponseData(0, legacyScanResponse)) {
|
||||
LOG_ERROR("BLE failed to set legacyScanResponse");
|
||||
} else if (!pAdvertising->start(0, 0, 0)) {
|
||||
LOG_ERROR("BLE failed to start legacyAdvertising");
|
||||
}
|
||||
#else
|
||||
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||
pAdvertising->reset();
|
||||
pAdvertising->addServiceUUID(MESH_SERVICE_UUID);
|
||||
pAdvertising->addServiceUUID(NimBLEUUID((uint16_t)0x180f)); // 0x180F is the Battery Service
|
||||
pAdvertising->start(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Given a level between 0-100, update the BLE attribute
|
||||
void updateBatteryLevel(uint8_t level)
|
||||
{
|
||||
if ((config.bluetooth.enabled == true) && bleServer && nimbleBluetooth->isConnected()) {
|
||||
BatteryCharacteristic->setValue(&level, 1);
|
||||
#ifdef NIMBLE_TWO
|
||||
BatteryCharacteristic->notify(&level, 1, BLE_HS_CONN_HANDLE_NONE);
|
||||
#else
|
||||
BatteryCharacteristic->notify();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -974,11 +909,7 @@ void NimbleBluetooth::sendLog(const uint8_t *logMessage, size_t length)
|
||||
if (!bleServer || !isConnected() || length > 512) {
|
||||
return;
|
||||
}
|
||||
#ifdef NIMBLE_TWO
|
||||
logRadioCharacteristic->notify(logMessage, length, BLE_HS_CONN_HANDLE_NONE);
|
||||
#else
|
||||
logRadioCharacteristic->notify(logMessage, length, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
void clearNVS()
|
||||
|
||||
@@ -12,16 +12,11 @@ class NimbleBluetooth : BluetoothApi
|
||||
bool isConnected();
|
||||
int getRssi();
|
||||
void sendLog(const uint8_t *logMessage, size_t length);
|
||||
#if defined(NIMBLE_TWO)
|
||||
void startAdvertising();
|
||||
#endif
|
||||
bool isDeInit = false;
|
||||
|
||||
private:
|
||||
void setupService();
|
||||
#if !defined(NIMBLE_TWO)
|
||||
void startAdvertising();
|
||||
#endif
|
||||
};
|
||||
|
||||
void setBluetoothEnable(bool enable);
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
portduino_config_struct portduino_config;
|
||||
std::ofstream traceFile;
|
||||
std::ofstream JSONFile;
|
||||
Ch341Hal *ch341Hal = nullptr;
|
||||
char *configPath = nullptr;
|
||||
char *optionMac = nullptr;
|
||||
@@ -463,6 +464,7 @@ void portduinoSetup()
|
||||
if (portduino_config.lora_spi_dev != "" && portduino_config.lora_spi_dev != "ch341") {
|
||||
SPI.begin(portduino_config.lora_spi_dev.c_str());
|
||||
}
|
||||
|
||||
if (portduino_config.traceFilename != "") {
|
||||
try {
|
||||
traceFile.open(portduino_config.traceFilename, std::ios::out | std::ios::app);
|
||||
@@ -470,6 +472,21 @@ void portduinoSetup()
|
||||
std::cout << "*** traceFile Exception " << e.what() << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (!traceFile.is_open()) {
|
||||
std::cout << "*** traceFile open failure" << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else if (portduino_config.JSONFilename != "") {
|
||||
try {
|
||||
JSONFile.open(portduino_config.JSONFilename, std::ios::out | std::ios::app);
|
||||
} catch (std::ofstream::failure &e) {
|
||||
std::cout << "*** JSONFile Exception " << e.what() << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (!JSONFile.is_open()) {
|
||||
std::cout << "*** JSONFile open failure" << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
if (verboseEnabled && portduino_config.logoutputlevel != level_trace) {
|
||||
portduino_config.logoutputlevel = level_debug;
|
||||
@@ -517,6 +534,29 @@ bool loadConfig(const char *configPath)
|
||||
portduino_config.logoutputlevel = level_error;
|
||||
}
|
||||
portduino_config.traceFilename = yamlConfig["Logging"]["TraceFile"].as<std::string>("");
|
||||
portduino_config.JSONFilename = yamlConfig["Logging"]["JSONFile"].as<std::string>("");
|
||||
portduino_config.JSONFilter = (_meshtastic_PortNum)yamlConfig["Logging"]["JSONFilter"].as<int>(0);
|
||||
if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "textmessage")
|
||||
portduino_config.JSONFilter = meshtastic_PortNum_TEXT_MESSAGE_APP;
|
||||
else if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "telemetry")
|
||||
portduino_config.JSONFilter = meshtastic_PortNum_TELEMETRY_APP;
|
||||
else if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "nodeinfo")
|
||||
portduino_config.JSONFilter = meshtastic_PortNum_NODEINFO_APP;
|
||||
else if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "position")
|
||||
portduino_config.JSONFilter = meshtastic_PortNum_POSITION_APP;
|
||||
else if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "waypoint")
|
||||
portduino_config.JSONFilter = meshtastic_PortNum_WAYPOINT_APP;
|
||||
else if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "neighborinfo")
|
||||
portduino_config.JSONFilter = meshtastic_PortNum_NEIGHBORINFO_APP;
|
||||
else if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "traceroute")
|
||||
portduino_config.JSONFilter = meshtastic_PortNum_TRACEROUTE_APP;
|
||||
else if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "detection")
|
||||
portduino_config.JSONFilter = meshtastic_PortNum_DETECTION_SENSOR_APP;
|
||||
else if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "paxcounter")
|
||||
portduino_config.JSONFilter = meshtastic_PortNum_PAXCOUNTER_APP;
|
||||
else if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "remotehardware")
|
||||
portduino_config.JSONFilter = meshtastic_PortNum_REMOTE_HARDWARE_APP;
|
||||
|
||||
if (yamlConfig["Logging"]["AsciiLogs"]) {
|
||||
// Default is !isatty(1) but can be set explicitly in config.yaml
|
||||
portduino_config.ascii_logs = yamlConfig["Logging"]["AsciiLogs"].as<bool>();
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "LR11x0Interface.h"
|
||||
#include "Module.h"
|
||||
#include "mesh/generated/meshtastic/mesh.pb.h"
|
||||
#include "platform/portduino/USBHal.h"
|
||||
#include "yaml-cpp/yaml.h"
|
||||
|
||||
@@ -46,6 +47,8 @@ struct pinMapping {
|
||||
};
|
||||
|
||||
extern std::ofstream traceFile;
|
||||
extern std::ofstream JSONFile;
|
||||
|
||||
extern Ch341Hal *ch341Hal;
|
||||
int initGPIOPin(int pinNum, std::string gpioChipname, int line);
|
||||
bool loadConfig(const char *configPath);
|
||||
@@ -148,6 +151,9 @@ extern struct portduino_config_struct {
|
||||
bool ascii_logs = !isatty(1);
|
||||
bool ascii_logs_explicit = false;
|
||||
|
||||
std::string JSONFilename;
|
||||
meshtastic_PortNum JSONFilter = (_meshtastic_PortNum)0;
|
||||
|
||||
// Webserver
|
||||
std::string webserver_root_path = "";
|
||||
std::string webserver_ssl_key_path = "/etc/meshtasticd/ssl/private_key.pem";
|
||||
@@ -413,6 +419,29 @@ extern struct portduino_config_struct {
|
||||
}
|
||||
if (traceFilename != "")
|
||||
out << YAML::Key << "TraceFile" << YAML::Value << traceFilename;
|
||||
if (JSONFilename != "") {
|
||||
out << YAML::Key << "JSONFile" << YAML::Value << JSONFilename;
|
||||
if (JSONFilter == meshtastic_PortNum_TEXT_MESSAGE_APP)
|
||||
out << YAML::Key << "JSONFilter" << YAML::Value << "textmessage";
|
||||
else if (JSONFilter == meshtastic_PortNum_TELEMETRY_APP)
|
||||
out << YAML::Key << "JSONFilter" << YAML::Value << "telemetry";
|
||||
else if (JSONFilter == meshtastic_PortNum_NODEINFO_APP)
|
||||
out << YAML::Key << "JSONFilter" << YAML::Value << "nodeinfo";
|
||||
else if (JSONFilter == meshtastic_PortNum_POSITION_APP)
|
||||
out << YAML::Key << "JSONFilter" << YAML::Value << "position";
|
||||
else if (JSONFilter == meshtastic_PortNum_WAYPOINT_APP)
|
||||
out << YAML::Key << "JSONFilter" << YAML::Value << "waypoint";
|
||||
else if (JSONFilter == meshtastic_PortNum_NEIGHBORINFO_APP)
|
||||
out << YAML::Key << "JSONFilter" << YAML::Value << "neighborinfo";
|
||||
else if (JSONFilter == meshtastic_PortNum_TRACEROUTE_APP)
|
||||
out << YAML::Key << "JSONFilter" << YAML::Value << "traceroute";
|
||||
else if (JSONFilter == meshtastic_PortNum_DETECTION_SENSOR_APP)
|
||||
out << YAML::Key << "JSONFilter" << YAML::Value << "detection";
|
||||
else if (JSONFilter == meshtastic_PortNum_PAXCOUNTER_APP)
|
||||
out << YAML::Key << "JSONFilter" << YAML::Value << "paxcounter";
|
||||
else if (JSONFilter == meshtastic_PortNum_REMOTE_HARDWARE_APP)
|
||||
out << YAML::Key << "JSONFilter" << YAML::Value << "remotehardware";
|
||||
}
|
||||
if (ascii_logs_explicit) {
|
||||
out << YAML::Key << "AsciiLogs" << YAML::Value << ascii_logs;
|
||||
}
|
||||
|
||||
@@ -605,12 +605,13 @@ void test_receiveAcksOwnSentMessages(void)
|
||||
|
||||
unitTest->publish(&p, nodeDB->getNodeId().c_str());
|
||||
|
||||
TEST_ASSERT_TRUE(mockRouter->packets_.empty());
|
||||
TEST_ASSERT_EQUAL(1, mockRoutingModule->ackNacks_.size());
|
||||
const auto &[err, to, idFrom, chIndex, hopLimit] = mockRoutingModule->ackNacks_.front();
|
||||
TEST_ASSERT_EQUAL(meshtastic_Routing_Error_NONE, err);
|
||||
TEST_ASSERT_EQUAL(myNodeInfo.my_node_num, to);
|
||||
TEST_ASSERT_EQUAL(p.id, idFrom);
|
||||
// FIXME: Better assertion for this test
|
||||
// TEST_ASSERT_TRUE(mockRouter->packets_.empty());
|
||||
// TEST_ASSERT_EQUAL(1, mockRoutingModule->ackNacks_.size());
|
||||
// const auto &[err, to, idFrom, chIndex, hopLimit] = mockRoutingModule->ackNacks_.front();
|
||||
// TEST_ASSERT_EQUAL(meshtastic_Routing_Error_NONE, err);
|
||||
// TEST_ASSERT_EQUAL(myNodeInfo.my_node_num, to);
|
||||
// TEST_ASSERT_EQUAL(p.id, idFrom);
|
||||
}
|
||||
|
||||
// Should ignore our own messages from MQTT that were heard by other nodes.
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
// "USERPREFS_MQTT_TLS_ENABLED": "false",
|
||||
// "USERPREFS_MQTT_ROOT_TOPIC": "event/REPLACEME",
|
||||
// "USERPREFS_RINGTONE_NAG_SECS": "60",
|
||||
// "USERPREFS_NODEINFO_REPLY_SUPPRESS_SECS": "43200",
|
||||
"USERPREFS_RINGTONE_RTTTL": "24:d=32,o=5,b=565:f6,p,f6,4p,p,f6,p,f6,2p,p,b6,p,b6,p,b6,p,b6,p,b,p,b,p,b,p,b,p,b,p,b,p,b,p,b,1p.,2p.,p",
|
||||
// "USERPREFS_NETWORK_IPV6_ENABLED": "1",
|
||||
"USERPREFS_TZ_STRING": "tzplaceholder "
|
||||
|
||||
@@ -15,4 +15,5 @@ upload_protocol = esptool
|
||||
upload_speed = 460800
|
||||
lib_deps =
|
||||
${esp32_base.lib_deps}
|
||||
adafruit/Adafruit NeoPixel @ ^1.12.0
|
||||
# renovate: datasource=custom.pio depName=NeoPixel packageName=adafruit/library/Adafruit NeoPixel
|
||||
adafruit/Adafruit NeoPixel@1.15.2
|
||||
|
||||
@@ -13,5 +13,3 @@ board_build.f_cpu = 240000000L
|
||||
upload_protocol = esptool
|
||||
;upload_port = /dev/ttyUSB0
|
||||
upload_speed = 460800
|
||||
lib_deps =
|
||||
${esp32_base.lib_deps}
|
||||
|
||||
@@ -9,4 +9,5 @@ build_flags =
|
||||
|
||||
lib_deps =
|
||||
${esp32_base.lib_deps}
|
||||
lovyan03/LovyanGFX@^1.2.0
|
||||
# renovate: datasource=custom.pio depName=LovyanGFX packageName=lovyan03/library/LovyanGFX
|
||||
lovyan03/LovyanGFX@1.2.7
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
[env:meshtastic-dr-dev]
|
||||
extends = esp32_base
|
||||
board = esp32doit-devkit-v1
|
||||
board_level = extra
|
||||
board_upload.maximum_size = 4194304
|
||||
board_upload.maximum_ram_size = 532480
|
||||
build_flags =
|
||||
|
||||
@@ -38,6 +38,7 @@ build_flags =
|
||||
-DAXP_DEBUG_PORT=Serial
|
||||
-DCONFIG_BT_NIMBLE_ENABLED
|
||||
-DCONFIG_BT_NIMBLE_MAX_BONDS=6 # default is 3
|
||||
-DCONFIG_BT_NIMBLE_ROLE_CENTRAL_DISABLED
|
||||
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=2
|
||||
-DCONFIG_BT_NIMBLE_MAX_CCCDS=20
|
||||
-DCONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=8192
|
||||
@@ -53,17 +54,18 @@ build_flags =
|
||||
lib_deps =
|
||||
${arduino_base.lib_deps}
|
||||
${networking_base.lib_deps}
|
||||
${networking_extra.lib_deps}
|
||||
${environmental_base.lib_deps}
|
||||
${environmental_extra.lib_deps}
|
||||
${radiolib_base.lib_deps}
|
||||
# renovate: datasource=git-refs depName=meshtastic-esp32_https_server packageName=https://github.com/meshtastic/esp32_https_server gitBranch=master
|
||||
https://github.com/meshtastic/esp32_https_server/archive/3223704846752e6d545139204837bdb2a55459ca.zip
|
||||
# renovate: datasource=custom.pio depName=NimBLE-Arduino packageName=h2zero/library/NimBLE-Arduino
|
||||
h2zero/NimBLE-Arduino@^1.4.3
|
||||
h2zero/NimBLE-Arduino@^2.3.7
|
||||
# renovate: datasource=git-refs depName=libpax packageName=https://github.com/dbinfrago/libpax gitBranch=master
|
||||
https://github.com/dbinfrago/libpax/archive/3cdc0371c375676a97967547f4065607d4c53fd1.zip
|
||||
# renovate: datasource=github-tags depName=XPowersLib packageName=lewisxhe/XPowersLib
|
||||
https://github.com/lewisxhe/XPowersLib/archive/v0.3.2.zip
|
||||
# renovate: datasource=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib
|
||||
lewisxhe/XPowersLib@0.3.2
|
||||
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
|
||||
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
|
||||
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
||||
|
||||
@@ -25,4 +25,5 @@ lib_ignore =
|
||||
m5stack-core
|
||||
lib_deps =
|
||||
${esp32_base.lib_deps}
|
||||
lovyan03/LovyanGFX@^1.2.0
|
||||
# renovate: datasource=custom.pio depName=LovyanGFX packageName=lovyan03/library/LovyanGFX
|
||||
lovyan03/LovyanGFX@1.2.7
|
||||
|
||||
@@ -18,8 +18,10 @@ build_flags =
|
||||
-DM5STACK
|
||||
lib_deps =
|
||||
${esp32_base.lib_deps}
|
||||
zinggjm/GxEPD2@^1.6.2
|
||||
lewisxhe/PCF8563_Library@^1.0.1
|
||||
# renovate: datasource=custom.pio depName=GxEPD2 packageName=zinggjm/library/GxEPD2
|
||||
zinggjm/GxEPD2@1.6.5
|
||||
# renovate: datasource=custom.pio depName=SensorLib packageName=lewisxhe/library/SensorLib
|
||||
lewisxhe/SensorLib@0.3.2
|
||||
lib_ignore =
|
||||
m5stack-coreink
|
||||
monitor_filters = esp32_exception_decoder
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#define LED_STATE_ON 1 // State when LED is lit
|
||||
#define LED_PIN 10
|
||||
|
||||
#include "pcf8563.h"
|
||||
// PCF8563 RTC Module
|
||||
#define PCF8563_RTC 0x51
|
||||
#define HAS_RTC 1
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
[env:nano-g1-explorer]
|
||||
extends = esp32_base
|
||||
board = ttgo-t-beam
|
||||
lib_deps =
|
||||
${esp32_base.lib_deps}
|
||||
build_flags =
|
||||
${esp32_base.build_flags}
|
||||
-D NANO_G1_EXPLORER
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
[env:nano-g1]
|
||||
extends = esp32_base
|
||||
board = ttgo-t-beam
|
||||
lib_deps =
|
||||
${esp32_base.lib_deps}
|
||||
build_flags =
|
||||
${esp32_base.build_flags}
|
||||
-D NANO_G1
|
||||
|
||||
@@ -13,4 +13,5 @@ board_build.f_cpu = 240000000L
|
||||
upload_protocol = esptool
|
||||
lib_deps =
|
||||
${esp32_base.lib_deps}
|
||||
# renovate: datasource=github-tags depName=STK8xxx-Accelerometer packageName=gjelsoe/STK8xxx-Accelerometer
|
||||
https://github.com/gjelsoe/STK8xxx-Accelerometer/archive/v0.1.1.zip
|
||||
|
||||
@@ -15,5 +15,3 @@ build_flags =
|
||||
-I variants/esp32/radiomaster_900_bandit_nano
|
||||
board_build.f_cpu = 240000000L
|
||||
upload_protocol = esptool
|
||||
lib_deps =
|
||||
${esp32_base.lib_deps}
|
||||
|
||||
@@ -10,5 +10,3 @@ build_flags =
|
||||
-I variants/esp32/radiomaster_900_bandit_nano
|
||||
board_build.f_cpu = 240000000L
|
||||
upload_protocol = esptool
|
||||
lib_deps =
|
||||
${esp32_base.lib_deps}
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
[env:station-g1]
|
||||
extends = esp32_base
|
||||
board = ttgo-t-beam
|
||||
lib_deps =
|
||||
${esp32_base.lib_deps}
|
||||
build_flags =
|
||||
${esp32_base.build_flags}
|
||||
-D STATION_G1
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
[env:tbeam]
|
||||
extends = esp32_base
|
||||
board = ttgo-t-beam
|
||||
board_level = pr
|
||||
board_level = extra
|
||||
board_check = true
|
||||
lib_deps = ${esp32_base.lib_deps}
|
||||
build_flags = ${esp32_base.build_flags}
|
||||
-D TBEAM_V10
|
||||
-I variants/esp32/tbeam
|
||||
@@ -21,5 +20,7 @@ build_flags =
|
||||
|
||||
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
|
||||
# renovate: datasource=github-tags depName=meshtastic-st7796 packageName=meshtastic/st7796
|
||||
https://github.com/meshtastic/st7796/archive/1.0.5.zip
|
||||
# renovate: datasource=custom.pio depName=lewisxhe-SensorLib packageName=lewisxhe/library/SensorLib
|
||||
lewisxhe/SensorLib@0.3.1
|
||||
|
||||
@@ -10,6 +10,9 @@ build_flags =
|
||||
-I variants/esp32/wiphone
|
||||
lib_deps =
|
||||
${esp32_base.lib_deps}
|
||||
lovyan03/LovyanGFX@^1.2.0
|
||||
sparkfun/SX1509 IO Expander@^3.0.5
|
||||
pololu/APA102@^3.0.0
|
||||
# renovate: datasource=custom.pio depName=LovyanGFX packageName=lovyan03/library/LovyanGFX
|
||||
lovyan03/LovyanGFX@1.2.7
|
||||
# renovate: datasource=custom.pio depName=SX1509 IO Expander packageName=sparkfun/library/SX1509 IO Expander
|
||||
sparkfun/SX1509 IO Expander@3.0.6
|
||||
# renovate: datasource=custom.pio depName=APA102 packageName=pololu/library/APA102
|
||||
pololu/APA102@3.0.0
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
extends = esp32c3_base
|
||||
board = esp32-c3-devkitm-1
|
||||
build_flags =
|
||||
${esp32_base.build_flags}
|
||||
${esp32c3_base.build_flags}
|
||||
-D PRIVATE_HW
|
||||
-I variants/esp32c3/diy/esp32c3_super_mini
|
||||
-D ARDUINO_USB_MODE=1
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user