mirror of
https://github.com/meshtastic/firmware.git
synced 2025-12-14 14:52:32 +00:00
Compare commits
84 Commits
static-buf
...
c6l-fixes
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee89bdd569 | ||
|
|
cc1c568916 | ||
|
|
0fecbbf86b | ||
|
|
9b6a7ed3bb | ||
|
|
fdc8796052 | ||
|
|
f32e06a321 | ||
|
|
8095261dfd | ||
|
|
72b9a02f3e | ||
|
|
af26408d73 | ||
|
|
c8f69913d6 | ||
|
|
42fbb62f18 | ||
|
|
e6adb197e4 | ||
|
|
8264d4d65e | ||
|
|
a1cf305336 | ||
|
|
7b2ff7e196 | ||
|
|
cc579dd0bd | ||
|
|
6a92358b68 | ||
|
|
e20a91b945 | ||
|
|
3fbe7fd8b2 | ||
|
|
e2ce369782 | ||
|
|
b14e5770d5 | ||
|
|
68ba3b315c | ||
|
|
2bafac242e | ||
|
|
39648e609a | ||
|
|
dcd53eb7cb | ||
|
|
7821919fae | ||
|
|
f083864f1f | ||
|
|
8e1da8561e | ||
|
|
953fcca304 | ||
|
|
c73fe85ec8 | ||
|
|
9345bdcb22 | ||
|
|
ec29100a88 | ||
|
|
e3772858b3 | ||
|
|
2567c03a3f | ||
|
|
188283b382 | ||
|
|
953fdc9eed | ||
|
|
ec7415b3fd | ||
|
|
a70ffae82c | ||
|
|
6a8732bbaa | ||
|
|
173b75a1c0 | ||
|
|
b0dae54c97 | ||
|
|
71d84404c6 | ||
|
|
6f5bdd73cb | ||
|
|
cc3c568501 | ||
|
|
13ebceb3bc | ||
|
|
70724bef72 | ||
|
|
bf4e2e8e86 | ||
|
|
2dc7760508 | ||
|
|
d201f6a1ed | ||
|
|
9977035499 | ||
|
|
096afa07f8 | ||
|
|
760471d620 | ||
|
|
6165b4f7a9 | ||
|
|
ae814b5463 | ||
|
|
4ee07226e4 | ||
|
|
78dfb05eeb | ||
|
|
9211b1bb4b | ||
|
|
70ac3601b0 | ||
|
|
51acd92a37 | ||
|
|
6d2093650a | ||
|
|
b6dd99917d | ||
|
|
d00b2afe1d | ||
|
|
e49b07ac8c | ||
|
|
8989de118c | ||
|
|
1914fa0321 | ||
|
|
962e5d513c | ||
|
|
ac4bcd2f56 | ||
|
|
e17c50bb86 | ||
|
|
abc0eb196a | ||
|
|
701028b749 | ||
|
|
108bdf7b0d | ||
|
|
f267b5f5f7 | ||
|
|
0cd860e300 | ||
|
|
31fdb36987 | ||
|
|
e7741c20e4 | ||
|
|
d1d16fc25f | ||
|
|
c8afbe68b5 | ||
|
|
18d005d7e6 | ||
|
|
8791cd7851 | ||
|
|
590db89643 | ||
|
|
ea1d968777 | ||
|
|
40d728a14b | ||
|
|
e39b56547e | ||
|
|
a7f63d5783 |
@@ -76,7 +76,7 @@ bool loopCanSleep()
|
|||||||
// Called just prior to starting Meshtastic. Allows for setting config values before startup.
|
// Called just prior to starting Meshtastic. Allows for setting config values before startup.
|
||||||
void lateInitVariant()
|
void lateInitVariant()
|
||||||
{
|
{
|
||||||
settingsMap[logoutputlevel] = level_error;
|
portduino_config.logoutputlevel = level_error;
|
||||||
channelFile.channels[0] = meshtastic_Channel{
|
channelFile.channels[0] = meshtastic_Channel{
|
||||||
.has_settings = true,
|
.has_settings = true,
|
||||||
.settings =
|
.settings =
|
||||||
@@ -132,7 +132,7 @@ int portduino_main(int argc, char **argv); // Renamed "main" function from Mesht
|
|||||||
// Start Meshtastic in a thread and wait till it has reached the ON state.
|
// Start Meshtastic in a thread and wait till it has reached the ON state.
|
||||||
int LLVMFuzzerInitialize(int *argc, char ***argv)
|
int LLVMFuzzerInitialize(int *argc, char ***argv)
|
||||||
{
|
{
|
||||||
settingsMap[maxtophone] = 5;
|
portduino_config.maxtophone = 5;
|
||||||
|
|
||||||
meshtasticThread = std::thread([program = *argv[0]]() {
|
meshtasticThread = std::thread([program = *argv[0]]() {
|
||||||
char nodeIdStr[12];
|
char nodeIdStr[12];
|
||||||
|
|||||||
5
.github/actions/setup-base/action.yml
vendored
5
.github/actions/setup-base/action.yml
vendored
@@ -11,11 +11,6 @@ runs:
|
|||||||
ref: ${{github.event.pull_request.head.ref}}
|
ref: ${{github.event.pull_request.head.ref}}
|
||||||
repository: ${{github.event.pull_request.head.repo.full_name}}
|
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||||
|
|
||||||
- name: Uncomment build epoch
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
sed -i 's/#-DBUILD_EPOCH=$UNIX_TIME/-DBUILD_EPOCH=$UNIX_TIME/' platformio.ini
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
500
.github/workflows/build_one_arch.yml
vendored
Normal file
500
.github/workflows/build_one_arch.yml
vendored
Normal file
@@ -0,0 +1,500 @@
|
|||||||
|
name: Build One Arch
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
arch:
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- esp32
|
||||||
|
- esp32s3
|
||||||
|
- esp32c3
|
||||||
|
- esp32c6
|
||||||
|
- nrf52840
|
||||||
|
- rp2040
|
||||||
|
- rp2350
|
||||||
|
- stm32
|
||||||
|
- native
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
setup:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: 3.x
|
||||||
|
cache: pip
|
||||||
|
- run: pip install -U platformio
|
||||||
|
- name: Generate matrix
|
||||||
|
id: jsonStep
|
||||||
|
run: |
|
||||||
|
if [[ "$GITHUB_HEAD_REF" == "" ]]; then
|
||||||
|
TARGETS=$(./bin/generate_ci_matrix.py ${{inputs.arch}} extra)
|
||||||
|
else
|
||||||
|
TARGETS=$(./bin/generate_ci_matrix.py ${{inputs.arch}} pr)
|
||||||
|
fi
|
||||||
|
echo "Name: $GITHUB_REF_NAME Base: $GITHUB_BASE_REF Ref: $GITHUB_REF Targets: $TARGETS"
|
||||||
|
echo "${{inputs.arch}}=$(jq -cn --argjson environments "$TARGETS" '{board: $environments}')" >> $GITHUB_OUTPUT
|
||||||
|
outputs:
|
||||||
|
esp32: ${{ steps.jsonStep.outputs.esp32 }}
|
||||||
|
esp32s3: ${{ steps.jsonStep.outputs.esp32s3 }}
|
||||||
|
esp32c3: ${{ steps.jsonStep.outputs.esp32c3 }}
|
||||||
|
esp32c6: ${{ steps.jsonStep.outputs.esp32c6 }}
|
||||||
|
nrf52840: ${{ steps.jsonStep.outputs.nrf52840 }}
|
||||||
|
rp2040: ${{ steps.jsonStep.outputs.rp2040 }}
|
||||||
|
rp2350: ${{ steps.jsonStep.outputs.rp2350 }}
|
||||||
|
stm32: ${{ steps.jsonStep.outputs.stm32 }}
|
||||||
|
check: ${{ steps.jsonStep.outputs.check }}
|
||||||
|
|
||||||
|
version:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- name: Get release version string
|
||||||
|
run: |
|
||||||
|
echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
||||||
|
echo "deb=$(./bin/buildinfo.py deb)" >> $GITHUB_OUTPUT
|
||||||
|
id: version
|
||||||
|
env:
|
||||||
|
BUILD_LOCATION: local
|
||||||
|
outputs:
|
||||||
|
long: ${{ steps.version.outputs.long }}
|
||||||
|
deb: ${{ steps.version.outputs.deb }}
|
||||||
|
|
||||||
|
build-esp32:
|
||||||
|
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'esp32'}}
|
||||||
|
needs: [setup, version]
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix: ${{ fromJson(needs.setup.outputs.esp32) }}
|
||||||
|
uses: ./.github/workflows/build_firmware.yml
|
||||||
|
with:
|
||||||
|
version: ${{ needs.version.outputs.long }}
|
||||||
|
pio_env: ${{ matrix.board }}
|
||||||
|
platform: esp32
|
||||||
|
|
||||||
|
build-esp32s3:
|
||||||
|
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'esp32s3'}}
|
||||||
|
needs: [setup, version]
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix: ${{ fromJson(needs.setup.outputs.esp32s3) }}
|
||||||
|
uses: ./.github/workflows/build_firmware.yml
|
||||||
|
with:
|
||||||
|
version: ${{ needs.version.outputs.long }}
|
||||||
|
pio_env: ${{ matrix.board }}
|
||||||
|
platform: esp32s3
|
||||||
|
|
||||||
|
build-esp32c3:
|
||||||
|
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'esp32c3'}}
|
||||||
|
needs: [setup, version]
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix: ${{ fromJson(needs.setup.outputs.esp32c3) }}
|
||||||
|
uses: ./.github/workflows/build_firmware.yml
|
||||||
|
with:
|
||||||
|
version: ${{ needs.version.outputs.long }}
|
||||||
|
pio_env: ${{ matrix.board }}
|
||||||
|
platform: esp32c3
|
||||||
|
|
||||||
|
build-esp32c6:
|
||||||
|
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'esp32c6'}}
|
||||||
|
needs: [setup, version]
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix: ${{ fromJson(needs.setup.outputs.esp32c6) }}
|
||||||
|
uses: ./.github/workflows/build_firmware.yml
|
||||||
|
with:
|
||||||
|
version: ${{ needs.version.outputs.long }}
|
||||||
|
pio_env: ${{ matrix.board }}
|
||||||
|
platform: esp32c6
|
||||||
|
|
||||||
|
build-nrf52840:
|
||||||
|
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'nrf52840'}}
|
||||||
|
needs: [setup, version]
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix: ${{ fromJson(needs.setup.outputs.nrf52840) }}
|
||||||
|
uses: ./.github/workflows/build_firmware.yml
|
||||||
|
with:
|
||||||
|
version: ${{ needs.version.outputs.long }}
|
||||||
|
pio_env: ${{ matrix.board }}
|
||||||
|
platform: nrf52840
|
||||||
|
|
||||||
|
build-rp2040:
|
||||||
|
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'rp2040'}}
|
||||||
|
needs: [setup, version]
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix: ${{ fromJson(needs.setup.outputs.rp2040) }}
|
||||||
|
uses: ./.github/workflows/build_firmware.yml
|
||||||
|
with:
|
||||||
|
version: ${{ needs.version.outputs.long }}
|
||||||
|
pio_env: ${{ matrix.board }}
|
||||||
|
platform: rp2040
|
||||||
|
|
||||||
|
build-rp2350:
|
||||||
|
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'rp2350'}}
|
||||||
|
needs: [setup, version]
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix: ${{ fromJson(needs.setup.outputs.rp2350) }}
|
||||||
|
uses: ./.github/workflows/build_firmware.yml
|
||||||
|
with:
|
||||||
|
version: ${{ needs.version.outputs.long }}
|
||||||
|
pio_env: ${{ matrix.board }}
|
||||||
|
platform: rp2350
|
||||||
|
|
||||||
|
build-stm32:
|
||||||
|
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'stm32' }}
|
||||||
|
needs: [setup, version]
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix: ${{ fromJson(needs.setup.outputs.stm32) }}
|
||||||
|
uses: ./.github/workflows/build_firmware.yml
|
||||||
|
with:
|
||||||
|
version: ${{ needs.version.outputs.long }}
|
||||||
|
pio_env: ${{ matrix.board }}
|
||||||
|
platform: stm32
|
||||||
|
|
||||||
|
build-debian-src:
|
||||||
|
if: ${{ github.repository == 'meshtastic/firmware' && github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
|
||||||
|
uses: ./.github/workflows/build_debian_src.yml
|
||||||
|
with:
|
||||||
|
series: UNRELEASED
|
||||||
|
build_location: local
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
package-pio-deps-native-tft:
|
||||||
|
if: ${{ inputs.arch == 'native' }}
|
||||||
|
uses: ./.github/workflows/package_pio_deps.yml
|
||||||
|
with:
|
||||||
|
pio_env: native-tft
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
test-native:
|
||||||
|
if: ${{ !contains(github.ref_name, 'event/') && github.event_name != 'workflow_dispatch' || !contains(github.ref_name, 'event/') && inputs.arch == 'native' }}
|
||||||
|
uses: ./.github/workflows/test_native.yml
|
||||||
|
|
||||||
|
docker-deb-amd64:
|
||||||
|
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
|
||||||
|
uses: ./.github/workflows/docker_build.yml
|
||||||
|
with:
|
||||||
|
distro: debian
|
||||||
|
platform: linux/amd64
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
push: false
|
||||||
|
|
||||||
|
docker-deb-amd64-tft:
|
||||||
|
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
|
||||||
|
uses: ./.github/workflows/docker_build.yml
|
||||||
|
with:
|
||||||
|
distro: debian
|
||||||
|
platform: linux/amd64
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
push: false
|
||||||
|
pio_env: native-tft
|
||||||
|
|
||||||
|
docker-alp-amd64:
|
||||||
|
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
|
||||||
|
uses: ./.github/workflows/docker_build.yml
|
||||||
|
with:
|
||||||
|
distro: alpine
|
||||||
|
platform: linux/amd64
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
push: false
|
||||||
|
|
||||||
|
docker-alp-amd64-tft:
|
||||||
|
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
|
||||||
|
uses: ./.github/workflows/docker_build.yml
|
||||||
|
with:
|
||||||
|
distro: alpine
|
||||||
|
platform: linux/amd64
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
push: false
|
||||||
|
pio_env: native-tft
|
||||||
|
|
||||||
|
docker-deb-arm64:
|
||||||
|
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
|
||||||
|
uses: ./.github/workflows/docker_build.yml
|
||||||
|
with:
|
||||||
|
distro: debian
|
||||||
|
platform: linux/arm64
|
||||||
|
runs-on: ubuntu-24.04-arm
|
||||||
|
push: false
|
||||||
|
|
||||||
|
docker-deb-armv7:
|
||||||
|
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
|
||||||
|
uses: ./.github/workflows/docker_build.yml
|
||||||
|
with:
|
||||||
|
distro: debian
|
||||||
|
platform: linux/arm/v7
|
||||||
|
runs-on: ubuntu-24.04-arm
|
||||||
|
push: false
|
||||||
|
|
||||||
|
gather-artifacts:
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
arch:
|
||||||
|
- esp32
|
||||||
|
- esp32s3
|
||||||
|
- esp32c3
|
||||||
|
- esp32c6
|
||||||
|
- nrf52840
|
||||||
|
- rp2040
|
||||||
|
- rp2350
|
||||||
|
- stm32
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
[
|
||||||
|
version,
|
||||||
|
build-esp32,
|
||||||
|
build-esp32s3,
|
||||||
|
build-esp32c3,
|
||||||
|
build-esp32c6,
|
||||||
|
build-nrf52840,
|
||||||
|
build-rp2040,
|
||||||
|
build-rp2350,
|
||||||
|
build-stm32,
|
||||||
|
]
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
with:
|
||||||
|
ref: ${{github.event.pull_request.head.ref}}
|
||||||
|
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
path: ./
|
||||||
|
pattern: firmware-${{inputs.arch}}-*
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Display structure of downloaded files
|
||||||
|
run: ls -R
|
||||||
|
|
||||||
|
- name: Move files up
|
||||||
|
run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat
|
||||||
|
|
||||||
|
- name: Repackage in single firmware zip
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}
|
||||||
|
overwrite: true
|
||||||
|
path: |
|
||||||
|
./firmware-*.bin
|
||||||
|
./firmware-*.uf2
|
||||||
|
./firmware-*.hex
|
||||||
|
./firmware-*-ota.zip
|
||||||
|
./device-*.sh
|
||||||
|
./device-*.bat
|
||||||
|
./littlefs-*.bin
|
||||||
|
./bleota*bin
|
||||||
|
./Meshtastic_nRF52_factory_erase*.uf2
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
name: firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}
|
||||||
|
merge-multiple: true
|
||||||
|
path: ./output
|
||||||
|
|
||||||
|
# For diagnostics
|
||||||
|
- name: Show artifacts
|
||||||
|
run: ls -lR
|
||||||
|
|
||||||
|
- name: Device scripts permissions
|
||||||
|
run: |
|
||||||
|
chmod +x ./output/device-install.sh
|
||||||
|
chmod +x ./output/device-update.sh
|
||||||
|
|
||||||
|
- name: Zip firmware
|
||||||
|
run: zip -j -9 -r ./firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
||||||
|
|
||||||
|
- name: Repackage in single elfs zip
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: debug-elfs-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip
|
||||||
|
overwrite: true
|
||||||
|
path: ./*.elf
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
- uses: scruplelesswizard/comment-artifact@main
|
||||||
|
if: ${{ github.event_name == 'pull_request' }}
|
||||||
|
with:
|
||||||
|
name: firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}
|
||||||
|
description: "Download firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip. This artifact will be available for 90 days from creation"
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
release-artifacts:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||||
|
outputs:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
needs:
|
||||||
|
- version
|
||||||
|
- gather-artifacts
|
||||||
|
- build-debian-src
|
||||||
|
- package-pio-deps-native-tft
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- name: Setup Python
|
||||||
|
uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: 3.x
|
||||||
|
|
||||||
|
- name: Create release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
id: create_release
|
||||||
|
with:
|
||||||
|
draft: true
|
||||||
|
prerelease: true
|
||||||
|
name: Meshtastic Firmware ${{ needs.version.outputs.long }} Alpha
|
||||||
|
tag_name: v${{ needs.version.outputs.long }}
|
||||||
|
body: |
|
||||||
|
Autogenerated by github action, developer should edit as required before publishing...
|
||||||
|
|
||||||
|
- name: Download source deb
|
||||||
|
uses: actions/download-artifact@v5
|
||||||
|
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@v5
|
||||||
|
with:
|
||||||
|
pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }}
|
||||||
|
merge-multiple: true
|
||||||
|
path: ./output/pio-deps-native-tft
|
||||||
|
|
||||||
|
- name: Zip Linux sources
|
||||||
|
working-directory: output
|
||||||
|
run: |
|
||||||
|
zip -j -9 -r ./meshtasticd-${{ needs.version.outputs.deb }}-src.zip ./debian-src
|
||||||
|
zip -9 -r ./platformio-deps-native-tft-${{ needs.version.outputs.long }}.zip ./pio-deps-native-tft
|
||||||
|
|
||||||
|
# For diagnostics
|
||||||
|
- name: Display structure of downloaded files
|
||||||
|
run: ls -lR
|
||||||
|
|
||||||
|
- name: Add Linux sources to GtiHub Release
|
||||||
|
# Only run when targeting master branch with workflow_dispatch
|
||||||
|
if: ${{ github.ref_name == 'master' }}
|
||||||
|
run: |
|
||||||
|
gh release upload v${{ needs.version.outputs.long }} ./output/meshtasticd-${{ needs.version.outputs.deb }}-src.zip
|
||||||
|
gh release upload v${{ needs.version.outputs.long }} ./output/platformio-deps-native-tft-${{ needs.version.outputs.long }}.zip
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
release-firmware:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
arch:
|
||||||
|
- esp32
|
||||||
|
- esp32s3
|
||||||
|
- esp32c3
|
||||||
|
- esp32c6
|
||||||
|
- nrf52840
|
||||||
|
- rp2040
|
||||||
|
- rp2350
|
||||||
|
- stm32
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||||
|
needs: [release-artifacts, version]
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- name: Setup Python
|
||||||
|
uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: 3.x
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
pattern: firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}
|
||||||
|
merge-multiple: true
|
||||||
|
path: ./output
|
||||||
|
|
||||||
|
- name: Display structure of downloaded files
|
||||||
|
run: ls -lR
|
||||||
|
|
||||||
|
- name: Device scripts permissions
|
||||||
|
run: |
|
||||||
|
chmod +x ./output/device-install.sh
|
||||||
|
chmod +x ./output/device-update.sh
|
||||||
|
|
||||||
|
- name: Zip firmware
|
||||||
|
run: zip -j -9 -r ./firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
name: debug-elfs-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip
|
||||||
|
merge-multiple: true
|
||||||
|
path: ./elfs
|
||||||
|
|
||||||
|
- name: Zip debug elfs
|
||||||
|
run: zip -j -9 -r ./debug-elfs-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip ./elfs
|
||||||
|
|
||||||
|
# For diagnostics
|
||||||
|
- name: Display structure of downloaded files
|
||||||
|
run: ls -lR
|
||||||
|
|
||||||
|
- name: Add bins and debug elfs to GitHub Release
|
||||||
|
# Only run when targeting master branch with workflow_dispatch
|
||||||
|
if: ${{ github.ref_name == 'master' }}
|
||||||
|
run: |
|
||||||
|
gh release upload v${{ needs.version.outputs.long }} ./firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip
|
||||||
|
gh release upload v${{ needs.version.outputs.long }} ./debug-elfs-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
publish-firmware:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||||
|
needs: [release-firmware, version]
|
||||||
|
env:
|
||||||
|
targets: |-
|
||||||
|
esp32,esp32s3,esp32c3,esp32c6,nrf52840,rp2040,rp2350,stm32
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- name: Setup Python
|
||||||
|
uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: 3.x
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
|
||||||
|
merge-multiple: true
|
||||||
|
path: ./publish
|
||||||
|
|
||||||
|
- name: Publish firmware to meshtastic.github.io
|
||||||
|
uses: peaceiris/actions-gh-pages@v4
|
||||||
|
env:
|
||||||
|
# On event/* branches, use the event name as the destination prefix
|
||||||
|
DEST_PREFIX: ${{ contains(github.ref_name, 'event/') && format('{0}/', github.ref_name) || '' }}
|
||||||
|
with:
|
||||||
|
deploy_key: ${{ secrets.DIST_PAGES_DEPLOY_KEY }}
|
||||||
|
external_repository: meshtastic/meshtastic.github.io
|
||||||
|
publish_branch: master
|
||||||
|
publish_dir: ./publish
|
||||||
|
destination_dir: ${{ env.DEST_PREFIX }}firmware-${{ needs.version.outputs.long }}
|
||||||
|
keep_files: true
|
||||||
|
user_name: github-actions[bot]
|
||||||
|
user_email: github-actions[bot]@users.noreply.github.com
|
||||||
|
commit_message: ${{ needs.version.outputs.long }}
|
||||||
|
enable_jekyll: true
|
||||||
395
.github/workflows/build_one_target.yml
vendored
Normal file
395
.github/workflows/build_one_target.yml
vendored
Normal file
@@ -0,0 +1,395 @@
|
|||||||
|
name: Build One Target
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
arch:
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- esp32
|
||||||
|
- esp32s3
|
||||||
|
- esp32c3
|
||||||
|
- esp32c6
|
||||||
|
- nrf52840
|
||||||
|
- rp2040
|
||||||
|
- rp2350
|
||||||
|
- stm32
|
||||||
|
- native
|
||||||
|
target:
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
description: Choose the target board, e.g. nrf52_promicro_diy_tcxo. If blank, will find available targets.
|
||||||
|
|
||||||
|
# find-target:
|
||||||
|
# type: boolean
|
||||||
|
# default: true
|
||||||
|
# description: 'Find the available targets'
|
||||||
|
jobs:
|
||||||
|
find-targets:
|
||||||
|
if: ${{ inputs.target == '' }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
arch:
|
||||||
|
- esp32
|
||||||
|
- esp32s3
|
||||||
|
- esp32c3
|
||||||
|
- esp32c6
|
||||||
|
- nrf52840
|
||||||
|
- rp2040
|
||||||
|
- rp2350
|
||||||
|
- stm32
|
||||||
|
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: 3.x
|
||||||
|
cache: pip
|
||||||
|
- run: pip install -U platformio
|
||||||
|
- name: Generate matrix
|
||||||
|
id: jsonStep
|
||||||
|
run: |
|
||||||
|
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}} extra)
|
||||||
|
echo "Name: $GITHUB_REF_NAME" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "Base: $GITHUB_BASE_REF" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "Arch: ${{matrix.arch}}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "Ref: $GITHUB_REF" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "Targets:" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo $TARGETS | sed 's/[][]//g; s/", "/\n- /g; s/"//g; s/^/- /' >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
version:
|
||||||
|
if: ${{ inputs.target != '' }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- name: Get release version string
|
||||||
|
run: |
|
||||||
|
echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
||||||
|
echo "deb=$(./bin/buildinfo.py deb)" >> $GITHUB_OUTPUT
|
||||||
|
id: version
|
||||||
|
env:
|
||||||
|
BUILD_LOCATION: local
|
||||||
|
outputs:
|
||||||
|
long: ${{ steps.version.outputs.long }}
|
||||||
|
deb: ${{ steps.version.outputs.deb }}
|
||||||
|
|
||||||
|
build-arch:
|
||||||
|
if: ${{ inputs.target != '' && inputs.arch != 'native' }}
|
||||||
|
needs: [version]
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
uses: ./.github/workflows/build_firmware.yml
|
||||||
|
with:
|
||||||
|
version: ${{ needs.version.outputs.long }}
|
||||||
|
pio_env: ${{ inputs.target }}
|
||||||
|
platform: ${{ inputs.arch }}
|
||||||
|
|
||||||
|
build-debian-src:
|
||||||
|
if: ${{ github.repository == 'meshtastic/firmware' && inputs.arch == 'native' }}
|
||||||
|
uses: ./.github/workflows/build_debian_src.yml
|
||||||
|
with:
|
||||||
|
series: UNRELEASED
|
||||||
|
build_location: local
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
package-pio-deps-native-tft:
|
||||||
|
if: ${{ inputs.arch == 'native' }}
|
||||||
|
uses: ./.github/workflows/package_pio_deps.yml
|
||||||
|
with:
|
||||||
|
pio_env: native-tft
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
test-native:
|
||||||
|
if: ${{ !contains(github.ref_name, 'event/') && github.event_name != 'workflow_dispatch' || !contains(github.ref_name, 'event/') && inputs.arch == 'native' && inputs.target != '' }}
|
||||||
|
uses: ./.github/workflows/test_native.yml
|
||||||
|
|
||||||
|
docker-deb-amd64:
|
||||||
|
if: ${{ inputs.target != '' && inputs.arch == 'native' }}
|
||||||
|
uses: ./.github/workflows/docker_build.yml
|
||||||
|
with:
|
||||||
|
distro: debian
|
||||||
|
platform: linux/amd64
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
push: false
|
||||||
|
|
||||||
|
docker-deb-amd64-tft:
|
||||||
|
if: ${{ inputs.target != '' && inputs.arch == 'native' }}
|
||||||
|
uses: ./.github/workflows/docker_build.yml
|
||||||
|
with:
|
||||||
|
distro: debian
|
||||||
|
platform: linux/amd64
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
push: false
|
||||||
|
pio_env: native-tft
|
||||||
|
|
||||||
|
docker-alp-amd64:
|
||||||
|
if: ${{ inputs.target != '' && inputs.arch == 'native' }}
|
||||||
|
uses: ./.github/workflows/docker_build.yml
|
||||||
|
with:
|
||||||
|
distro: alpine
|
||||||
|
platform: linux/amd64
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
push: false
|
||||||
|
|
||||||
|
docker-alp-amd64-tft:
|
||||||
|
if: ${{ inputs.target != '' && inputs.arch == 'native' }}
|
||||||
|
uses: ./.github/workflows/docker_build.yml
|
||||||
|
with:
|
||||||
|
distro: alpine
|
||||||
|
platform: linux/amd64
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
push: false
|
||||||
|
pio_env: native-tft
|
||||||
|
|
||||||
|
docker-deb-arm64:
|
||||||
|
if: ${{ inputs.target != '' && inputs.arch == 'native' }}
|
||||||
|
uses: ./.github/workflows/docker_build.yml
|
||||||
|
with:
|
||||||
|
distro: debian
|
||||||
|
platform: linux/arm64
|
||||||
|
runs-on: ubuntu-24.04-arm
|
||||||
|
push: false
|
||||||
|
|
||||||
|
docker-deb-armv7:
|
||||||
|
if: ${{ inputs.target != '' && inputs.arch == 'native' }}
|
||||||
|
uses: ./.github/workflows/docker_build.yml
|
||||||
|
with:
|
||||||
|
distro: debian
|
||||||
|
platform: linux/arm/v7
|
||||||
|
runs-on: ubuntu-24.04-arm
|
||||||
|
push: false
|
||||||
|
|
||||||
|
gather-artifacts:
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [version, build-arch]
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
with:
|
||||||
|
ref: ${{github.event.pull_request.head.ref}}
|
||||||
|
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
path: ./
|
||||||
|
pattern: firmware-*-*
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Display structure of downloaded files
|
||||||
|
run: ls -R
|
||||||
|
|
||||||
|
- name: Move files up
|
||||||
|
run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat
|
||||||
|
|
||||||
|
- name: Repackage in single firmware zip
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: firmware-${{inputs.target}}-${{ needs.version.outputs.long }}
|
||||||
|
overwrite: true
|
||||||
|
path: |
|
||||||
|
./firmware-*.bin
|
||||||
|
./firmware-*.uf2
|
||||||
|
./firmware-*.hex
|
||||||
|
./firmware-*-ota.zip
|
||||||
|
./device-*.sh
|
||||||
|
./device-*.bat
|
||||||
|
./littlefs-*.bin
|
||||||
|
./bleota*bin
|
||||||
|
./Meshtastic_nRF52_factory_erase*.uf2
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
pattern: firmware-*-${{ needs.version.outputs.long }}
|
||||||
|
merge-multiple: true
|
||||||
|
path: ./output
|
||||||
|
|
||||||
|
# For diagnostics
|
||||||
|
- name: Show artifacts
|
||||||
|
run: ls -lR
|
||||||
|
|
||||||
|
- name: Device scripts permissions
|
||||||
|
run: |
|
||||||
|
chmod +x ./output/device-install.sh
|
||||||
|
chmod +x ./output/device-update.sh
|
||||||
|
|
||||||
|
- name: Zip firmware
|
||||||
|
run: zip -j -9 -r ./firmware-${{inputs.target}}-${{ needs.version.outputs.long }}.zip ./output
|
||||||
|
|
||||||
|
- name: Repackage in single elfs zip
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: debug-elfs-${{inputs.target}}-${{ needs.version.outputs.long }}.zip
|
||||||
|
overwrite: true
|
||||||
|
path: ./*.elf
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
- uses: scruplelesswizard/comment-artifact@main
|
||||||
|
if: ${{ github.event_name == 'pull_request' }}
|
||||||
|
with:
|
||||||
|
name: firmware-${{inputs.target}}-${{ needs.version.outputs.long }}
|
||||||
|
description: "Download firmware-${{inputs.target}}-${{ needs.version.outputs.long }}.zip. This artifact will be available for 90 days from creation"
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
release-artifacts:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event_name == 'workflow_dispatch' && inputs.target != ''}}
|
||||||
|
outputs:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
needs:
|
||||||
|
- version
|
||||||
|
- gather-artifacts
|
||||||
|
- build-debian-src
|
||||||
|
- package-pio-deps-native-tft
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- name: Setup Python
|
||||||
|
uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: 3.x
|
||||||
|
|
||||||
|
- name: Create release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
id: create_release
|
||||||
|
with:
|
||||||
|
draft: true
|
||||||
|
prerelease: true
|
||||||
|
name: Meshtastic Firmware ${{ needs.version.outputs.long }} Alpha
|
||||||
|
tag_name: v${{ needs.version.outputs.long }}
|
||||||
|
body: |
|
||||||
|
Autogenerated by github action, developer should edit as required before publishing...
|
||||||
|
|
||||||
|
- name: Download source deb
|
||||||
|
uses: actions/download-artifact@v5
|
||||||
|
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@v5
|
||||||
|
with:
|
||||||
|
pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }}
|
||||||
|
merge-multiple: true
|
||||||
|
path: ./output/pio-deps-native-tft
|
||||||
|
|
||||||
|
- name: Zip Linux sources
|
||||||
|
working-directory: output
|
||||||
|
run: |
|
||||||
|
zip -j -9 -r ./meshtasticd-${{ needs.version.outputs.deb }}-src.zip ./debian-src
|
||||||
|
zip -9 -r ./platformio-deps-native-tft-${{ needs.version.outputs.long }}.zip ./pio-deps-native-tft
|
||||||
|
|
||||||
|
# For diagnostics
|
||||||
|
- name: Display structure of downloaded files
|
||||||
|
run: ls -lR
|
||||||
|
|
||||||
|
- name: Add Linux sources to GtiHub Release
|
||||||
|
# Only run when targeting master branch with workflow_dispatch
|
||||||
|
if: ${{ github.ref_name == 'master' }}
|
||||||
|
run: |
|
||||||
|
gh release upload v${{ needs.version.outputs.long }} ./output/meshtasticd-${{ needs.version.outputs.deb }}-src.zip
|
||||||
|
gh release upload v${{ needs.version.outputs.long }} ./output/platformio-deps-native-tft-${{ needs.version.outputs.long }}.zip
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
release-firmware:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event_name == 'workflow_dispatch' && inputs.target != ''}}
|
||||||
|
needs: [release-artifacts, version]
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- name: Setup Python
|
||||||
|
uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: 3.x
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
pattern: firmware-*-${{ needs.version.outputs.long }}
|
||||||
|
merge-multiple: true
|
||||||
|
path: ./output
|
||||||
|
|
||||||
|
- name: Display structure of downloaded files
|
||||||
|
run: ls -lR
|
||||||
|
|
||||||
|
- name: Device scripts permissions
|
||||||
|
run: |
|
||||||
|
chmod +x ./output/device-install.sh
|
||||||
|
chmod +x ./output/device-update.sh
|
||||||
|
|
||||||
|
- name: Zip firmware
|
||||||
|
run: zip -j -9 -r ./firmware-${{inputs.target}}-${{ needs.version.outputs.long }}.zip ./output
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
pattern: debug-elfs-*-${{ needs.version.outputs.long }}.zip
|
||||||
|
merge-multiple: true
|
||||||
|
path: ./elfs
|
||||||
|
|
||||||
|
- name: Zip debug elfs
|
||||||
|
run: zip -j -9 -r ./debug-elfs-${{inputs.target}}-${{ needs.version.outputs.long }}.zip ./elfs
|
||||||
|
|
||||||
|
# For diagnostics
|
||||||
|
- name: Display structure of downloaded files
|
||||||
|
run: ls -lR
|
||||||
|
|
||||||
|
- name: Add bins and debug elfs to GitHub Release
|
||||||
|
# Only run when targeting master branch with workflow_dispatch
|
||||||
|
if: ${{ github.ref_name == 'master' }}
|
||||||
|
run: |
|
||||||
|
gh release upload v${{ needs.version.outputs.long }} ./firmware-${{inputs.target}}-${{ needs.version.outputs.long }}.zip
|
||||||
|
gh release upload v${{ needs.version.outputs.long }} ./debug-elfs-${{inputs.target}}-${{ needs.version.outputs.long }}.zip
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
publish-firmware:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
if: ${{ github.event_name == 'workflow_dispatch' && github.repository == 'meshtastic/firmware' && inputs.target != '' }}
|
||||||
|
needs: [release-firmware, version]
|
||||||
|
env:
|
||||||
|
targets: |-
|
||||||
|
esp32,esp32s3,esp32c3,esp32c6,nrf52840,rp2040,rp2350,stm32
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- name: Setup Python
|
||||||
|
uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: 3.x
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
|
||||||
|
merge-multiple: true
|
||||||
|
path: ./publish
|
||||||
|
|
||||||
|
- name: Publish firmware to meshtastic.github.io
|
||||||
|
uses: peaceiris/actions-gh-pages@v4
|
||||||
|
env:
|
||||||
|
# On event/* branches, use the event name as the destination prefix
|
||||||
|
DEST_PREFIX: ${{ contains(github.ref_name, 'event/') && format('{0}/', github.ref_name) || '' }}
|
||||||
|
with:
|
||||||
|
deploy_key: ${{ secrets.DIST_PAGES_DEPLOY_KEY }}
|
||||||
|
external_repository: meshtastic/meshtastic.github.io
|
||||||
|
publish_branch: master
|
||||||
|
publish_dir: ./publish
|
||||||
|
destination_dir: ${{ env.DEST_PREFIX }}firmware-${{ needs.version.outputs.long }}
|
||||||
|
keep_files: true
|
||||||
|
user_name: github-actions[bot]
|
||||||
|
user_email: github-actions[bot]@users.noreply.github.com
|
||||||
|
commit_message: ${{ needs.version.outputs.long }}
|
||||||
|
enable_jekyll: true
|
||||||
8
.github/workflows/daily_packaging.yml
vendored
8
.github/workflows/daily_packaging.yml
vendored
@@ -21,18 +21,20 @@ permissions:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
docker-multiarch:
|
docker-multiarch:
|
||||||
|
if: github.repository == 'meshtastic/firmware'
|
||||||
uses: ./.github/workflows/docker_manifest.yml
|
uses: ./.github/workflows/docker_manifest.yml
|
||||||
with:
|
with:
|
||||||
release_channel: daily
|
release_channel: daily
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|
||||||
package-ppa:
|
package-ppa:
|
||||||
|
if: github.repository == 'meshtastic/firmware'
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
series:
|
series:
|
||||||
- jammy # 22.04
|
- jammy # 22.04 LTS
|
||||||
- noble # 24.04
|
- noble # 24.04 LTS
|
||||||
- plucky # 25.04
|
- plucky # 25.04
|
||||||
- questing # 25.10
|
- questing # 25.10
|
||||||
uses: ./.github/workflows/package_ppa.yml
|
uses: ./.github/workflows/package_ppa.yml
|
||||||
@@ -42,6 +44,7 @@ jobs:
|
|||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|
||||||
package-obs:
|
package-obs:
|
||||||
|
if: github.repository == 'meshtastic/firmware'
|
||||||
uses: ./.github/workflows/package_obs.yml
|
uses: ./.github/workflows/package_obs.yml
|
||||||
with:
|
with:
|
||||||
obs_project: network:Meshtastic:daily
|
obs_project: network:Meshtastic:daily
|
||||||
@@ -49,6 +52,7 @@ jobs:
|
|||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|
||||||
hook-copr:
|
hook-copr:
|
||||||
|
if: github.repository == 'meshtastic/firmware'
|
||||||
uses: ./.github/workflows/hook_copr.yml
|
uses: ./.github/workflows/hook_copr.yml
|
||||||
with:
|
with:
|
||||||
copr_project: daily
|
copr_project: daily
|
||||||
|
|||||||
20
.github/workflows/main_matrix.yml
vendored
20
.github/workflows/main_matrix.yml
vendored
@@ -3,7 +3,7 @@ concurrency:
|
|||||||
group: ci-${{ github.head_ref || github.run_id }}
|
group: ci-${{ github.head_ref || github.run_id }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
on:
|
on:
|
||||||
# # Triggers the workflow on push but only for the master branch
|
# # Triggers the workflow on push but only for the main branches
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
@@ -27,6 +27,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
setup:
|
setup:
|
||||||
|
if: github.repository == 'meshtastic/firmware'
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@@ -70,6 +71,7 @@ jobs:
|
|||||||
check: ${{ steps.jsonStep.outputs.check }}
|
check: ${{ steps.jsonStep.outputs.check }}
|
||||||
|
|
||||||
version:
|
version:
|
||||||
|
if: github.repository == 'meshtastic/firmware'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
@@ -91,7 +93,7 @@ jobs:
|
|||||||
matrix: ${{ fromJson(needs.setup.outputs.check) }}
|
matrix: ${{ fromJson(needs.setup.outputs.check) }}
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.event_name != 'workflow_dispatch' }}
|
if: ${{ github.event_name != 'workflow_dispatch' && github.repository == 'meshtastic/firmware' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
- name: Build base
|
- name: Build base
|
||||||
@@ -204,10 +206,11 @@ jobs:
|
|||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|
||||||
test-native:
|
test-native:
|
||||||
if: ${{ !contains(github.ref_name, 'event/') }}
|
if: ${{ !contains(github.ref_name, 'event/') && github.repository == 'meshtastic/firmware' }}
|
||||||
uses: ./.github/workflows/test_native.yml
|
uses: ./.github/workflows/test_native.yml
|
||||||
|
|
||||||
docker-deb-amd64:
|
docker-deb-amd64:
|
||||||
|
if: github.repository == 'meshtastic/firmware'
|
||||||
uses: ./.github/workflows/docker_build.yml
|
uses: ./.github/workflows/docker_build.yml
|
||||||
with:
|
with:
|
||||||
distro: debian
|
distro: debian
|
||||||
@@ -216,6 +219,7 @@ jobs:
|
|||||||
push: false
|
push: false
|
||||||
|
|
||||||
docker-deb-amd64-tft:
|
docker-deb-amd64-tft:
|
||||||
|
if: github.repository == 'meshtastic/firmware'
|
||||||
uses: ./.github/workflows/docker_build.yml
|
uses: ./.github/workflows/docker_build.yml
|
||||||
with:
|
with:
|
||||||
distro: debian
|
distro: debian
|
||||||
@@ -225,6 +229,7 @@ jobs:
|
|||||||
pio_env: native-tft
|
pio_env: native-tft
|
||||||
|
|
||||||
docker-alp-amd64:
|
docker-alp-amd64:
|
||||||
|
if: github.repository == 'meshtastic/firmware'
|
||||||
uses: ./.github/workflows/docker_build.yml
|
uses: ./.github/workflows/docker_build.yml
|
||||||
with:
|
with:
|
||||||
distro: alpine
|
distro: alpine
|
||||||
@@ -233,6 +238,7 @@ jobs:
|
|||||||
push: false
|
push: false
|
||||||
|
|
||||||
docker-alp-amd64-tft:
|
docker-alp-amd64-tft:
|
||||||
|
if: github.repository == 'meshtastic/firmware'
|
||||||
uses: ./.github/workflows/docker_build.yml
|
uses: ./.github/workflows/docker_build.yml
|
||||||
with:
|
with:
|
||||||
distro: alpine
|
distro: alpine
|
||||||
@@ -242,6 +248,7 @@ jobs:
|
|||||||
pio_env: native-tft
|
pio_env: native-tft
|
||||||
|
|
||||||
docker-deb-arm64:
|
docker-deb-arm64:
|
||||||
|
if: github.repository == 'meshtastic/firmware'
|
||||||
uses: ./.github/workflows/docker_build.yml
|
uses: ./.github/workflows/docker_build.yml
|
||||||
with:
|
with:
|
||||||
distro: debian
|
distro: debian
|
||||||
@@ -250,6 +257,7 @@ jobs:
|
|||||||
push: false
|
push: false
|
||||||
|
|
||||||
docker-deb-armv7:
|
docker-deb-armv7:
|
||||||
|
if: github.repository == 'meshtastic/firmware'
|
||||||
uses: ./.github/workflows/docker_build.yml
|
uses: ./.github/workflows/docker_build.yml
|
||||||
with:
|
with:
|
||||||
distro: debian
|
distro: debian
|
||||||
@@ -258,6 +266,8 @@ jobs:
|
|||||||
push: false
|
push: false
|
||||||
|
|
||||||
gather-artifacts:
|
gather-artifacts:
|
||||||
|
# trunk-ignore(checkov/CKV2_GHA_1)
|
||||||
|
if: github.repository == 'meshtastic/firmware'
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
@@ -357,7 +367,7 @@ jobs:
|
|||||||
|
|
||||||
release-artifacts:
|
release-artifacts:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.event_name == 'workflow_dispatch' }}
|
if: ${{ github.event_name == 'workflow_dispatch' && github.repository == 'meshtastic/firmware' }}
|
||||||
outputs:
|
outputs:
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
needs:
|
needs:
|
||||||
@@ -432,7 +442,7 @@ jobs:
|
|||||||
- rp2350
|
- rp2350
|
||||||
- stm32
|
- stm32
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.event_name == 'workflow_dispatch' }}
|
if: ${{ github.event_name == 'workflow_dispatch' && github.repository == 'meshtastic/firmware'}}
|
||||||
needs: [release-artifacts, version]
|
needs: [release-artifacts, version]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
|
|||||||
508
.github/workflows/merge_queue.yml
vendored
Normal file
508
.github/workflows/merge_queue.yml
vendored
Normal file
@@ -0,0 +1,508 @@
|
|||||||
|
name: Merge Queue
|
||||||
|
# Not sure how concurrency works in merge_queue, removing for now.
|
||||||
|
# concurrency:
|
||||||
|
# group: merge-queue-${{ github.head_ref || github.run_id }}
|
||||||
|
# cancel-in-progress: true
|
||||||
|
on:
|
||||||
|
# Merge group is a special trigger that is used to trigger the workflow when a merge group is created.
|
||||||
|
merge_group:
|
||||||
|
|
||||||
|
env:
|
||||||
|
FAIL_FAST_PER_ARCH: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
setup:
|
||||||
|
strategy:
|
||||||
|
fail-fast: true
|
||||||
|
matrix:
|
||||||
|
arch:
|
||||||
|
- esp32
|
||||||
|
- esp32s3
|
||||||
|
- esp32c3
|
||||||
|
- esp32c6
|
||||||
|
- nrf52840
|
||||||
|
- rp2040
|
||||||
|
- rp2350
|
||||||
|
- stm32
|
||||||
|
- check
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: 3.x
|
||||||
|
cache: pip
|
||||||
|
- run: pip install -U platformio
|
||||||
|
- name: Generate matrix
|
||||||
|
id: jsonStep
|
||||||
|
run: |
|
||||||
|
if [[ "$GITHUB_HEAD_REF" == "" ]]; then
|
||||||
|
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}})
|
||||||
|
else
|
||||||
|
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}} pr)
|
||||||
|
fi
|
||||||
|
echo "Name: $GITHUB_REF_NAME Base: $GITHUB_BASE_REF Ref: $GITHUB_REF Targets: $TARGETS"
|
||||||
|
echo "${{matrix.arch}}=$(jq -cn --argjson environments "$TARGETS" '{board: $environments}')" >> $GITHUB_OUTPUT
|
||||||
|
outputs:
|
||||||
|
esp32: ${{ steps.jsonStep.outputs.esp32 }}
|
||||||
|
esp32s3: ${{ steps.jsonStep.outputs.esp32s3 }}
|
||||||
|
esp32c3: ${{ steps.jsonStep.outputs.esp32c3 }}
|
||||||
|
esp32c6: ${{ steps.jsonStep.outputs.esp32c6 }}
|
||||||
|
nrf52840: ${{ steps.jsonStep.outputs.nrf52840 }}
|
||||||
|
rp2040: ${{ steps.jsonStep.outputs.rp2040 }}
|
||||||
|
rp2350: ${{ steps.jsonStep.outputs.rp2350 }}
|
||||||
|
stm32: ${{ steps.jsonStep.outputs.stm32 }}
|
||||||
|
check: ${{ steps.jsonStep.outputs.check }}
|
||||||
|
|
||||||
|
version:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- name: Get release version string
|
||||||
|
run: |
|
||||||
|
echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
||||||
|
echo "deb=$(./bin/buildinfo.py deb)" >> $GITHUB_OUTPUT
|
||||||
|
id: version
|
||||||
|
env:
|
||||||
|
BUILD_LOCATION: local
|
||||||
|
outputs:
|
||||||
|
long: ${{ steps.version.outputs.long }}
|
||||||
|
deb: ${{ steps.version.outputs.deb }}
|
||||||
|
|
||||||
|
check:
|
||||||
|
needs: setup
|
||||||
|
strategy:
|
||||||
|
fail-fast: true
|
||||||
|
matrix: ${{ fromJson(needs.setup.outputs.check) }}
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event_name != 'workflow_dispatch' }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- name: Build base
|
||||||
|
id: base
|
||||||
|
uses: ./.github/actions/setup-base
|
||||||
|
- name: Check ${{ matrix.board }}
|
||||||
|
run: bin/check-all.sh ${{ matrix.board }}
|
||||||
|
|
||||||
|
build-esp32:
|
||||||
|
needs: [setup, version]
|
||||||
|
strategy:
|
||||||
|
fail-fast: ${{ vars.FAIL_FAST_PER_ARCH == true }}
|
||||||
|
matrix: ${{ fromJson(needs.setup.outputs.esp32) }}
|
||||||
|
uses: ./.github/workflows/build_firmware.yml
|
||||||
|
with:
|
||||||
|
version: ${{ needs.version.outputs.long }}
|
||||||
|
pio_env: ${{ matrix.board }}
|
||||||
|
platform: esp32
|
||||||
|
|
||||||
|
build-esp32s3:
|
||||||
|
needs: [setup, version]
|
||||||
|
strategy:
|
||||||
|
fail-fast: ${{ vars.FAIL_FAST_PER_ARCH == true }}
|
||||||
|
matrix: ${{ fromJson(needs.setup.outputs.esp32s3) }}
|
||||||
|
uses: ./.github/workflows/build_firmware.yml
|
||||||
|
with:
|
||||||
|
version: ${{ needs.version.outputs.long }}
|
||||||
|
pio_env: ${{ matrix.board }}
|
||||||
|
platform: esp32s3
|
||||||
|
|
||||||
|
build-esp32c3:
|
||||||
|
needs: [setup, version]
|
||||||
|
strategy:
|
||||||
|
fail-fast: ${{ vars.FAIL_FAST_PER_ARCH == true }}
|
||||||
|
matrix: ${{ fromJson(needs.setup.outputs.esp32c3) }}
|
||||||
|
uses: ./.github/workflows/build_firmware.yml
|
||||||
|
with:
|
||||||
|
version: ${{ needs.version.outputs.long }}
|
||||||
|
pio_env: ${{ matrix.board }}
|
||||||
|
platform: esp32c3
|
||||||
|
|
||||||
|
build-esp32c6:
|
||||||
|
needs: [setup, version]
|
||||||
|
strategy:
|
||||||
|
fail-fast: ${{ vars.FAIL_FAST_PER_ARCH == true }}
|
||||||
|
matrix: ${{ fromJson(needs.setup.outputs.esp32c6) }}
|
||||||
|
uses: ./.github/workflows/build_firmware.yml
|
||||||
|
with:
|
||||||
|
version: ${{ needs.version.outputs.long }}
|
||||||
|
pio_env: ${{ matrix.board }}
|
||||||
|
platform: esp32c6
|
||||||
|
|
||||||
|
build-nrf52840:
|
||||||
|
needs: [setup, version]
|
||||||
|
strategy:
|
||||||
|
fail-fast: ${{ vars.FAIL_FAST_PER_ARCH == true }}
|
||||||
|
matrix: ${{ fromJson(needs.setup.outputs.nrf52840) }}
|
||||||
|
uses: ./.github/workflows/build_firmware.yml
|
||||||
|
with:
|
||||||
|
version: ${{ needs.version.outputs.long }}
|
||||||
|
pio_env: ${{ matrix.board }}
|
||||||
|
platform: nrf52840
|
||||||
|
|
||||||
|
build-rp2040:
|
||||||
|
needs: [setup, version]
|
||||||
|
strategy:
|
||||||
|
fail-fast: ${{ vars.FAIL_FAST_PER_ARCH == true }}
|
||||||
|
matrix: ${{ fromJson(needs.setup.outputs.rp2040) }}
|
||||||
|
uses: ./.github/workflows/build_firmware.yml
|
||||||
|
with:
|
||||||
|
version: ${{ needs.version.outputs.long }}
|
||||||
|
pio_env: ${{ matrix.board }}
|
||||||
|
platform: rp2040
|
||||||
|
|
||||||
|
build-rp2350:
|
||||||
|
needs: [setup, version]
|
||||||
|
strategy:
|
||||||
|
fail-fast: ${{ vars.FAIL_FAST_PER_ARCH == true }}
|
||||||
|
matrix: ${{ fromJson(needs.setup.outputs.rp2350) }}
|
||||||
|
uses: ./.github/workflows/build_firmware.yml
|
||||||
|
with:
|
||||||
|
version: ${{ needs.version.outputs.long }}
|
||||||
|
pio_env: ${{ matrix.board }}
|
||||||
|
platform: rp2350
|
||||||
|
|
||||||
|
build-stm32:
|
||||||
|
needs: [setup, version]
|
||||||
|
strategy:
|
||||||
|
fail-fast: ${{ vars.FAIL_FAST_PER_ARCH == true }}
|
||||||
|
matrix: ${{ fromJson(needs.setup.outputs.stm32) }}
|
||||||
|
uses: ./.github/workflows/build_firmware.yml
|
||||||
|
with:
|
||||||
|
version: ${{ needs.version.outputs.long }}
|
||||||
|
pio_env: ${{ matrix.board }}
|
||||||
|
platform: stm32
|
||||||
|
|
||||||
|
build-debian-src:
|
||||||
|
if: github.repository == 'meshtastic/firmware'
|
||||||
|
uses: ./.github/workflows/build_debian_src.yml
|
||||||
|
with:
|
||||||
|
series: UNRELEASED
|
||||||
|
build_location: local
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
package-pio-deps-native-tft:
|
||||||
|
if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||||
|
uses: ./.github/workflows/package_pio_deps.yml
|
||||||
|
with:
|
||||||
|
pio_env: native-tft
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
test-native:
|
||||||
|
if: ${{ !contains(github.ref_name, 'event/') }}
|
||||||
|
uses: ./.github/workflows/test_native.yml
|
||||||
|
|
||||||
|
docker-deb-amd64:
|
||||||
|
uses: ./.github/workflows/docker_build.yml
|
||||||
|
with:
|
||||||
|
distro: debian
|
||||||
|
platform: linux/amd64
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
push: false
|
||||||
|
|
||||||
|
docker-deb-amd64-tft:
|
||||||
|
uses: ./.github/workflows/docker_build.yml
|
||||||
|
with:
|
||||||
|
distro: debian
|
||||||
|
platform: linux/amd64
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
push: false
|
||||||
|
pio_env: native-tft
|
||||||
|
|
||||||
|
docker-alp-amd64:
|
||||||
|
uses: ./.github/workflows/docker_build.yml
|
||||||
|
with:
|
||||||
|
distro: alpine
|
||||||
|
platform: linux/amd64
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
push: false
|
||||||
|
|
||||||
|
docker-alp-amd64-tft:
|
||||||
|
uses: ./.github/workflows/docker_build.yml
|
||||||
|
with:
|
||||||
|
distro: alpine
|
||||||
|
platform: linux/amd64
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
push: false
|
||||||
|
pio_env: native-tft
|
||||||
|
|
||||||
|
docker-deb-arm64:
|
||||||
|
uses: ./.github/workflows/docker_build.yml
|
||||||
|
with:
|
||||||
|
distro: debian
|
||||||
|
platform: linux/arm64
|
||||||
|
runs-on: ubuntu-24.04-arm
|
||||||
|
push: false
|
||||||
|
|
||||||
|
docker-deb-armv7:
|
||||||
|
uses: ./.github/workflows/docker_build.yml
|
||||||
|
with:
|
||||||
|
distro: debian
|
||||||
|
platform: linux/arm/v7
|
||||||
|
runs-on: ubuntu-24.04-arm
|
||||||
|
push: false
|
||||||
|
|
||||||
|
gather-artifacts:
|
||||||
|
# trunk-ignore(checkov/CKV2_GHA_1)
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
arch:
|
||||||
|
- esp32
|
||||||
|
- esp32s3
|
||||||
|
- esp32c3
|
||||||
|
- esp32c6
|
||||||
|
- nrf52840
|
||||||
|
- rp2040
|
||||||
|
- rp2350
|
||||||
|
- stm32
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
[
|
||||||
|
version,
|
||||||
|
build-esp32,
|
||||||
|
build-esp32s3,
|
||||||
|
build-esp32c3,
|
||||||
|
build-esp32c6,
|
||||||
|
build-nrf52840,
|
||||||
|
build-rp2040,
|
||||||
|
build-rp2350,
|
||||||
|
build-stm32,
|
||||||
|
]
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
with:
|
||||||
|
ref: ${{github.event.pull_request.head.ref}}
|
||||||
|
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
path: ./
|
||||||
|
pattern: firmware-${{matrix.arch}}-*
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Display structure of downloaded files
|
||||||
|
run: ls -R
|
||||||
|
|
||||||
|
- name: Move files up
|
||||||
|
run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat
|
||||||
|
|
||||||
|
- name: Repackage in single firmware zip
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
|
overwrite: true
|
||||||
|
path: |
|
||||||
|
./firmware-*.bin
|
||||||
|
./firmware-*.uf2
|
||||||
|
./firmware-*.hex
|
||||||
|
./firmware-*-ota.zip
|
||||||
|
./device-*.sh
|
||||||
|
./device-*.bat
|
||||||
|
./littlefs-*.bin
|
||||||
|
./bleota*bin
|
||||||
|
./Meshtastic_nRF52_factory_erase*.uf2
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
|
merge-multiple: true
|
||||||
|
path: ./output
|
||||||
|
|
||||||
|
# For diagnostics
|
||||||
|
- name: Show artifacts
|
||||||
|
run: ls -lR
|
||||||
|
|
||||||
|
- name: Device scripts permissions
|
||||||
|
run: |
|
||||||
|
chmod +x ./output/device-install.sh
|
||||||
|
chmod +x ./output/device-update.sh
|
||||||
|
|
||||||
|
- name: Zip firmware
|
||||||
|
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
||||||
|
|
||||||
|
- name: Repackage in single elfs zip
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
|
||||||
|
overwrite: true
|
||||||
|
path: ./*.elf
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
- uses: scruplelesswizard/comment-artifact@main
|
||||||
|
if: ${{ github.event_name == 'pull_request' }}
|
||||||
|
with:
|
||||||
|
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
|
description: "Download firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip. This artifact will be available for 90 days from creation"
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
release-artifacts:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||||
|
outputs:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
needs:
|
||||||
|
- version
|
||||||
|
- gather-artifacts
|
||||||
|
- build-debian-src
|
||||||
|
- package-pio-deps-native-tft
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- name: Setup Python
|
||||||
|
uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: 3.x
|
||||||
|
|
||||||
|
- name: Create release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
id: create_release
|
||||||
|
with:
|
||||||
|
draft: true
|
||||||
|
prerelease: true
|
||||||
|
name: Meshtastic Firmware ${{ needs.version.outputs.long }} Alpha
|
||||||
|
tag_name: v${{ needs.version.outputs.long }}
|
||||||
|
body: |
|
||||||
|
Autogenerated by github action, developer should edit as required before publishing...
|
||||||
|
|
||||||
|
- name: Download source deb
|
||||||
|
uses: actions/download-artifact@v5
|
||||||
|
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@v5
|
||||||
|
with:
|
||||||
|
pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }}
|
||||||
|
merge-multiple: true
|
||||||
|
path: ./output/pio-deps-native-tft
|
||||||
|
|
||||||
|
- name: Zip Linux sources
|
||||||
|
working-directory: output
|
||||||
|
run: |
|
||||||
|
zip -j -9 -r ./meshtasticd-${{ needs.version.outputs.deb }}-src.zip ./debian-src
|
||||||
|
zip -9 -r ./platformio-deps-native-tft-${{ needs.version.outputs.long }}.zip ./pio-deps-native-tft
|
||||||
|
|
||||||
|
# For diagnostics
|
||||||
|
- name: Display structure of downloaded files
|
||||||
|
run: ls -lR
|
||||||
|
|
||||||
|
- name: Add Linux sources to GtiHub Release
|
||||||
|
# Only run when targeting master branch with workflow_dispatch
|
||||||
|
if: ${{ github.ref_name == 'master' }}
|
||||||
|
run: |
|
||||||
|
gh release upload v${{ needs.version.outputs.long }} ./output/meshtasticd-${{ needs.version.outputs.deb }}-src.zip
|
||||||
|
gh release upload v${{ needs.version.outputs.long }} ./output/platformio-deps-native-tft-${{ needs.version.outputs.long }}.zip
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
release-firmware:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
arch:
|
||||||
|
- esp32
|
||||||
|
- esp32s3
|
||||||
|
- esp32c3
|
||||||
|
- esp32c6
|
||||||
|
- nrf52840
|
||||||
|
- rp2040
|
||||||
|
- rp2350
|
||||||
|
- stm32
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||||
|
needs: [release-artifacts, version]
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- name: Setup Python
|
||||||
|
uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: 3.x
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
pattern: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
|
merge-multiple: true
|
||||||
|
path: ./output
|
||||||
|
|
||||||
|
- name: Display structure of downloaded files
|
||||||
|
run: ls -lR
|
||||||
|
|
||||||
|
- name: Device scripts permissions
|
||||||
|
run: |
|
||||||
|
chmod +x ./output/device-install.sh
|
||||||
|
chmod +x ./output/device-update.sh
|
||||||
|
|
||||||
|
- name: Zip firmware
|
||||||
|
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
|
||||||
|
merge-multiple: true
|
||||||
|
path: ./elfs
|
||||||
|
|
||||||
|
- name: Zip debug elfs
|
||||||
|
run: zip -j -9 -r ./debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./elfs
|
||||||
|
|
||||||
|
# For diagnostics
|
||||||
|
- name: Display structure of downloaded files
|
||||||
|
run: ls -lR
|
||||||
|
|
||||||
|
- name: Add bins and debug elfs to GitHub Release
|
||||||
|
# Only run when targeting master branch with workflow_dispatch
|
||||||
|
if: ${{ github.ref_name == 'master' }}
|
||||||
|
run: |
|
||||||
|
gh release upload v${{ needs.version.outputs.long }} ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
|
||||||
|
gh release upload v${{ needs.version.outputs.long }} ./debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
publish-firmware:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||||
|
needs: [release-firmware, version]
|
||||||
|
env:
|
||||||
|
targets: |-
|
||||||
|
esp32,esp32s3,esp32c3,esp32c6,nrf52840,rp2040,rp2350,stm32
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- name: Setup Python
|
||||||
|
uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: 3.x
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
|
||||||
|
merge-multiple: true
|
||||||
|
path: ./publish
|
||||||
|
|
||||||
|
- name: Publish firmware to meshtastic.github.io
|
||||||
|
uses: peaceiris/actions-gh-pages@v4
|
||||||
|
env:
|
||||||
|
# On event/* branches, use the event name as the destination prefix
|
||||||
|
DEST_PREFIX: ${{ contains(github.ref_name, 'event/') && format('{0}/', github.ref_name) || '' }}
|
||||||
|
with:
|
||||||
|
deploy_key: ${{ secrets.DIST_PAGES_DEPLOY_KEY }}
|
||||||
|
external_repository: meshtastic/meshtastic.github.io
|
||||||
|
publish_branch: master
|
||||||
|
publish_dir: ./publish
|
||||||
|
destination_dir: ${{ env.DEST_PREFIX }}firmware-${{ needs.version.outputs.long }}
|
||||||
|
keep_files: true
|
||||||
|
user_name: github-actions[bot]
|
||||||
|
user_email: github-actions[bot]@users.noreply.github.com
|
||||||
|
commit_message: ${{ needs.version.outputs.long }}
|
||||||
|
enable_jekyll: true
|
||||||
6
.github/workflows/release_channels.yml
vendored
6
.github/workflows/release_channels.yml
vendored
@@ -21,10 +21,10 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
series:
|
series:
|
||||||
- jammy # 22.04
|
- jammy # 22.04 LTS
|
||||||
- noble # 24.04
|
- noble # 24.04 LTS
|
||||||
- plucky # 25.04
|
- plucky # 25.04
|
||||||
# - questing # 25.10
|
- questing # 25.10
|
||||||
uses: ./.github/workflows/package_ppa.yml
|
uses: ./.github/workflows/package_ppa.yml
|
||||||
with:
|
with:
|
||||||
ppa_repo: |-
|
ppa_repo: |-
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ plugins:
|
|||||||
uri: https://github.com/trunk-io/plugins
|
uri: https://github.com/trunk-io/plugins
|
||||||
lint:
|
lint:
|
||||||
enabled:
|
enabled:
|
||||||
- checkov@3.2.469
|
- checkov@3.2.471
|
||||||
- renovate@41.94.0
|
- renovate@41.115.6
|
||||||
- prettier@3.6.2
|
- prettier@3.6.2
|
||||||
- trufflehog@3.90.5
|
- trufflehog@3.90.6
|
||||||
- yamllint@1.37.1
|
- yamllint@1.37.1
|
||||||
- bandit@1.8.6
|
- bandit@1.8.6
|
||||||
- trivy@0.66.0
|
- trivy@0.66.0
|
||||||
- taplo@0.10.0
|
- taplo@0.10.0
|
||||||
- ruff@0.12.11
|
- ruff@0.13.0
|
||||||
- isort@6.0.1
|
- isort@6.0.1
|
||||||
- markdownlint@0.45.0
|
- markdownlint@0.45.0
|
||||||
- oxipng@9.1.5
|
- oxipng@9.1.5
|
||||||
|
|||||||
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@@ -10,5 +10,10 @@
|
|||||||
},
|
},
|
||||||
"[powershell]": {
|
"[powershell]": {
|
||||||
"editor.defaultFormatter": "ms-vscode.powershell"
|
"editor.defaultFormatter": "ms-vscode.powershell"
|
||||||
|
},
|
||||||
|
"files.associations": {
|
||||||
|
"deque": "cpp",
|
||||||
|
"string": "cpp",
|
||||||
|
"vector": "cpp"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
sed -i 's/#-DBUILD_EPOCH=$UNIX_TIME/-DBUILD_EPOCH=$UNIX_TIME/' platformio.ini
|
|
||||||
|
|
||||||
export PIP_BREAK_SYSTEM_PACKAGES=1
|
export PIP_BREAK_SYSTEM_PACKAGES=1
|
||||||
|
|
||||||
if (echo $2 | grep -q "esp32"); then
|
if (echo $2 | grep -q "esp32"); then
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
PYTHON=${PYTHON:-$(which python3 python | head -n 1)}
|
PYTHON=${PYTHON:-$(which python3 python | head -n 1)}
|
||||||
BPS_RESET=false
|
BPS_RESET=false
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
PYTHON=${PYTHON:-$(which python3 python|head -n 1)}
|
PYTHON=${PYTHON:-$(which python3 python|head -n 1)}
|
||||||
CHANGE_MODE=false
|
CHANGE_MODE=false
|
||||||
|
|||||||
@@ -87,6 +87,9 @@
|
|||||||
</screenshots>
|
</screenshots>
|
||||||
|
|
||||||
<releases>
|
<releases>
|
||||||
|
<release version="2.7.10" date="2025-09-18">
|
||||||
|
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.10</url>
|
||||||
|
</release>
|
||||||
<release version="2.7.9" date="2025-09-03">
|
<release version="2.7.9" date="2025-09-03">
|
||||||
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.9</url>
|
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.9</url>
|
||||||
</release>
|
</release>
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ from os.path import join
|
|||||||
import subprocess
|
import subprocess
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
import time
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from readprops import readProps
|
from readprops import readProps
|
||||||
|
|
||||||
@@ -125,11 +127,16 @@ for pref in userPrefs:
|
|||||||
pref_flags.append("-D" + pref + "=" + env.StringifyMacro(userPrefs[pref]) + "")
|
pref_flags.append("-D" + pref + "=" + env.StringifyMacro(userPrefs[pref]) + "")
|
||||||
|
|
||||||
# General options that are passed to the C and C++ compilers
|
# General options that are passed to the C and C++ compilers
|
||||||
|
# Calculate unix epoch for current day (midnight)
|
||||||
|
current_date = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
|
||||||
|
build_epoch = int(current_date.timestamp())
|
||||||
|
|
||||||
flags = [
|
flags = [
|
||||||
"-DAPP_VERSION=" + verObj["long"],
|
"-DAPP_VERSION=" + verObj["long"],
|
||||||
"-DAPP_VERSION_SHORT=" + verObj["short"],
|
"-DAPP_VERSION_SHORT=" + verObj["short"],
|
||||||
"-DAPP_ENV=" + env.get("PIOENV"),
|
"-DAPP_ENV=" + env.get("PIOENV"),
|
||||||
"-DAPP_REPO=" + repo_owner,
|
"-DAPP_REPO=" + repo_owner,
|
||||||
|
"-DBUILD_EPOCH=" + str(build_epoch),
|
||||||
] + pref_flags
|
] + pref_flags
|
||||||
|
|
||||||
print ("Using flags:")
|
print ("Using flags:")
|
||||||
|
|||||||
50
debian/changelog
vendored
50
debian/changelog
vendored
@@ -1,50 +1,6 @@
|
|||||||
meshtasticd (2.7.9.0) UNRELEASED; urgency=medium
|
meshtasticd (2.7.10.0) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
[ Austin Lane ]
|
|
||||||
* Initial packaging
|
* Initial packaging
|
||||||
* GitHub Actions Automatic version bump
|
* Version 2.5.19
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
|
|
||||||
[ ]
|
-- Austin Lane <vidplace7@gmail.com> Thu, 02 Jan 2025 12:00:00 +0000
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
|
|
||||||
[ ]
|
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
|
|
||||||
[ ]
|
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
|
|
||||||
[ ]
|
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
|
|
||||||
[ ]
|
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
|
|
||||||
[ ]
|
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
|
|
||||||
[ ]
|
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
|
|
||||||
[ Ubuntu ]
|
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
|
|
||||||
[ ]
|
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
|
|
||||||
[ ]
|
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
|
|
||||||
[ ]
|
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
|
|
||||||
[ ]
|
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
|
|
||||||
[ ]
|
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
|
|
||||||
-- <github-actions[bot]@users.noreply.github.com> Wed, 03 Sep 2025 23:39:17 +0000
|
|
||||||
|
|||||||
@@ -53,14 +53,15 @@ build_flags = -Wno-missing-field-initializers
|
|||||||
-DMESHTASTIC_EXCLUDE_POWERSTRESS=1 ; exclude power stress test module from main firmware
|
-DMESHTASTIC_EXCLUDE_POWERSTRESS=1 ; exclude power stress test module from main firmware
|
||||||
-DMESHTASTIC_EXCLUDE_GENERIC_THREAD_MODULE=1
|
-DMESHTASTIC_EXCLUDE_GENERIC_THREAD_MODULE=1
|
||||||
-D MAX_THREADS=40 ; As we've split modules, we have more threads to manage
|
-D MAX_THREADS=40 ; As we've split modules, we have more threads to manage
|
||||||
#-DBUILD_EPOCH=$UNIX_TIME
|
#-DBUILD_EPOCH=$UNIX_TIME ; set in platformio-custom.py now
|
||||||
#-D OLED_PL=1
|
#-D OLED_PL=1
|
||||||
|
#-D DEBUG_HEAP=1 ; uncomment to add free heap space / memory leak debugging logs
|
||||||
|
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
monitor_filters = direct
|
monitor_filters = direct
|
||||||
lib_deps =
|
lib_deps =
|
||||||
# renovate: datasource=git-refs depName=meshtastic-esp8266-oled-ssd1306 packageName=https://github.com/meshtastic/esp8266-oled-ssd1306 gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic-esp8266-oled-ssd1306 packageName=https://github.com/meshtastic/esp8266-oled-ssd1306 gitBranch=master
|
||||||
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/9573abb64dc9c94f3051348f2bf4fc5cedf03c22.zip
|
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/0cbc26b1f8f61957af0475f486b362eafe7cc4e2.zip
|
||||||
# renovate: datasource=git-refs depName=meshtastic-OneButton packageName=https://github.com/meshtastic/OneButton gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic-OneButton packageName=https://github.com/meshtastic/OneButton gitBranch=master
|
||||||
https://github.com/meshtastic/OneButton/archive/fa352d668c53f290cfa480a5f79ad422cd828c70.zip
|
https://github.com/meshtastic/OneButton/archive/fa352d668c53f290cfa480a5f79ad422cd828c70.zip
|
||||||
# renovate: datasource=git-refs depName=meshtastic-arduino-fsm packageName=https://github.com/meshtastic/arduino-fsm gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic-arduino-fsm packageName=https://github.com/meshtastic/arduino-fsm gitBranch=master
|
||||||
@@ -118,13 +119,13 @@ lib_deps =
|
|||||||
[device-ui_base]
|
[device-ui_base]
|
||||||
lib_deps =
|
lib_deps =
|
||||||
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
|
||||||
https://github.com/meshtastic/device-ui/archive/3677476c8a823ee85056b5fb1d146a3e193f8276.zip
|
https://github.com/meshtastic/device-ui/archive/9ed5355a24059750e9b2eb5d669574d9ea42a37b.zip
|
||||||
|
|
||||||
; Common libs for environmental measurements in telemetry module
|
; Common libs for environmental measurements in telemetry module
|
||||||
[environmental_base]
|
[environmental_base]
|
||||||
lib_deps =
|
lib_deps =
|
||||||
# renovate: datasource=custom.pio depName=Adafruit BusIO packageName=adafruit/library/Adafruit BusIO
|
# renovate: datasource=custom.pio depName=Adafruit BusIO packageName=adafruit/library/Adafruit BusIO
|
||||||
adafruit/Adafruit BusIO@1.17.2
|
adafruit/Adafruit BusIO@1.17.3
|
||||||
# renovate: datasource=custom.pio depName=Adafruit Unified Sensor packageName=adafruit/library/Adafruit Unified Sensor
|
# renovate: datasource=custom.pio depName=Adafruit Unified Sensor packageName=adafruit/library/Adafruit Unified Sensor
|
||||||
adafruit/Adafruit Unified Sensor@1.1.15
|
adafruit/Adafruit Unified Sensor@1.1.15
|
||||||
# renovate: datasource=custom.pio depName=Adafruit BMP280 packageName=adafruit/library/Adafruit BMP280 Library
|
# renovate: datasource=custom.pio depName=Adafruit BMP280 packageName=adafruit/library/Adafruit BMP280 Library
|
||||||
|
|||||||
Submodule protobufs updated: a84657c220...46b81e822a
@@ -183,9 +183,9 @@ class AmbientLightingThread : public concurrency::OSThread
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
pixels.show();
|
pixels.show();
|
||||||
LOG_DEBUG("Init NeoPixel Ambient light w/ brightness(current)=%d, red=%d, green=%d, blue=%d",
|
// LOG_DEBUG("Init NeoPixel Ambient light w/ brightness(current)=%d, red=%d, green=%d, blue=%d",
|
||||||
moduleConfig.ambient_lighting.current, moduleConfig.ambient_lighting.red,
|
// moduleConfig.ambient_lighting.current, moduleConfig.ambient_lighting.red,
|
||||||
moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
|
// moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
|
||||||
#endif
|
#endif
|
||||||
#ifdef RGBLED_CA
|
#ifdef RGBLED_CA
|
||||||
analogWrite(RGBLED_RED, 255 - moduleConfig.ambient_lighting.red);
|
analogWrite(RGBLED_RED, 255 - moduleConfig.ambient_lighting.red);
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ inline bool Syslog::_sendLog(uint16_t pri, const char *appName, const char *mess
|
|||||||
{
|
{
|
||||||
int result;
|
int result;
|
||||||
#ifdef ARCH_PORTDUINO
|
#ifdef ARCH_PORTDUINO
|
||||||
bool utf = !settingsMap[ascii_logs];
|
bool utf = !portduino_config.ascii_logs;
|
||||||
#else
|
#else
|
||||||
bool utf = true;
|
bool utf = true;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -2,6 +2,12 @@
|
|||||||
|
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
#if defined(DEBUG_HEAP)
|
||||||
|
class MemGet;
|
||||||
|
extern MemGet memGet;
|
||||||
|
#endif
|
||||||
|
|
||||||
// DEBUG LED
|
// DEBUG LED
|
||||||
#ifndef LED_STATE_ON
|
#ifndef LED_STATE_ON
|
||||||
#define LED_STATE_ON 1
|
#define LED_STATE_ON 1
|
||||||
@@ -23,6 +29,7 @@
|
|||||||
#define MESHTASTIC_LOG_LEVEL_ERROR "ERROR"
|
#define MESHTASTIC_LOG_LEVEL_ERROR "ERROR"
|
||||||
#define MESHTASTIC_LOG_LEVEL_CRIT "CRIT "
|
#define MESHTASTIC_LOG_LEVEL_CRIT "CRIT "
|
||||||
#define MESHTASTIC_LOG_LEVEL_TRACE "TRACE"
|
#define MESHTASTIC_LOG_LEVEL_TRACE "TRACE"
|
||||||
|
#define MESHTASTIC_LOG_LEVEL_HEAP "HEAP"
|
||||||
|
|
||||||
#include "SerialConsole.h"
|
#include "SerialConsole.h"
|
||||||
|
|
||||||
@@ -62,6 +69,25 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(DEBUG_HEAP)
|
||||||
|
#define LOG_HEAP(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_HEAP, __VA_ARGS__)
|
||||||
|
|
||||||
|
// Macro-based heap debugging
|
||||||
|
#define DEBUG_HEAP_BEFORE auto heapBefore = memGet.getFreeHeap();
|
||||||
|
#define DEBUG_HEAP_AFTER(context, ptr) \
|
||||||
|
do { \
|
||||||
|
auto heapAfter = memGet.getFreeHeap(); \
|
||||||
|
if (heapBefore != heapAfter) { \
|
||||||
|
LOG_HEAP("Alloc in %s pointer 0x%x, size: %u, free: %u", context, ptr, heapBefore - heapAfter, heapAfter); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define LOG_HEAP(...)
|
||||||
|
#define DEBUG_HEAP_BEFORE
|
||||||
|
#define DEBUG_HEAP_AFTER(context, ptr)
|
||||||
|
#endif
|
||||||
|
|
||||||
/// A C wrapper for LOG_DEBUG that can be used from arduino C libs that don't know about C++ or meshtastic
|
/// A C wrapper for LOG_DEBUG that can be used from arduino C libs that don't know about C++ or meshtastic
|
||||||
extern "C" void logLegacy(const char *level, const char *fmt, ...);
|
extern "C" void logLegacy(const char *level, const char *fmt, ...);
|
||||||
|
|
||||||
|
|||||||
@@ -38,4 +38,49 @@ const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaC
|
|||||||
return useShortName ? "Custom" : "Invalid";
|
return useShortName ? "Custom" : "Invalid";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *DisplayFormatters::getDeviceRole(meshtastic_Config_DeviceConfig_Role role)
|
||||||
|
{
|
||||||
|
switch (role) {
|
||||||
|
case meshtastic_Config_DeviceConfig_Role_CLIENT:
|
||||||
|
return "Client";
|
||||||
|
break;
|
||||||
|
case meshtastic_Config_DeviceConfig_Role_CLIENT_MUTE:
|
||||||
|
return "Client Mute";
|
||||||
|
break;
|
||||||
|
case meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN:
|
||||||
|
return "Client Hidden";
|
||||||
|
break;
|
||||||
|
case meshtastic_Config_DeviceConfig_Role_CLIENT_BASE:
|
||||||
|
return "Client Base";
|
||||||
|
break;
|
||||||
|
case meshtastic_Config_DeviceConfig_Role_LOST_AND_FOUND:
|
||||||
|
return "Lost and Found";
|
||||||
|
break;
|
||||||
|
case meshtastic_Config_DeviceConfig_Role_TRACKER:
|
||||||
|
return "Tracker";
|
||||||
|
break;
|
||||||
|
case meshtastic_Config_DeviceConfig_Role_SENSOR:
|
||||||
|
return "Sensor";
|
||||||
|
break;
|
||||||
|
case meshtastic_Config_DeviceConfig_Role_TAK:
|
||||||
|
return "TAK";
|
||||||
|
break;
|
||||||
|
case meshtastic_Config_DeviceConfig_Role_TAK_TRACKER:
|
||||||
|
return "TAK Tracker";
|
||||||
|
break;
|
||||||
|
case meshtastic_Config_DeviceConfig_Role_ROUTER:
|
||||||
|
return "Router";
|
||||||
|
break;
|
||||||
|
case meshtastic_Config_DeviceConfig_Role_ROUTER_LATE:
|
||||||
|
return "Router Late";
|
||||||
|
break;
|
||||||
|
case meshtastic_Config_DeviceConfig_Role_REPEATER:
|
||||||
|
return "Repeater";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return "Unknown";
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -6,4 +6,5 @@ class DisplayFormatters
|
|||||||
public:
|
public:
|
||||||
static const char *getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName,
|
static const char *getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName,
|
||||||
bool usePreset);
|
bool usePreset);
|
||||||
|
static const char *getDeviceRole(meshtastic_Config_DeviceConfig_Role role);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ class GPSStatus : public Status
|
|||||||
|
|
||||||
meshtastic_Position p = meshtastic_Position_init_default;
|
meshtastic_Position p = meshtastic_Position_init_default;
|
||||||
|
|
||||||
|
/// Time of last valid GPS fix (millis since boot)
|
||||||
|
uint32_t lastFixMillis = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GPSStatus() { statusType = STATUS_TYPE_GPS; }
|
GPSStatus() { statusType = STATUS_TYPE_GPS; }
|
||||||
|
|
||||||
@@ -83,6 +86,9 @@ class GPSStatus : public Status
|
|||||||
|
|
||||||
uint32_t getNumSatellites() const { return p.sats_in_view; }
|
uint32_t getNumSatellites() const { return p.sats_in_view; }
|
||||||
|
|
||||||
|
/// Return millis() when the last GPS fix occurred (0 = never)
|
||||||
|
uint32_t getLastFixMillis() const { return lastFixMillis; }
|
||||||
|
|
||||||
bool matches(const GPSStatus *newStatus) const
|
bool matches(const GPSStatus *newStatus) const
|
||||||
{
|
{
|
||||||
#ifdef GPS_DEBUG
|
#ifdef GPS_DEBUG
|
||||||
@@ -114,6 +120,9 @@ class GPSStatus : public Status
|
|||||||
|
|
||||||
if (isDirty) {
|
if (isDirty) {
|
||||||
if (hasLock) {
|
if (hasLock) {
|
||||||
|
// Record time of last valid GPS fix
|
||||||
|
lastFixMillis = millis();
|
||||||
|
|
||||||
// In debug logs, identify position by @timestamp:stage (stage 3 = notify)
|
// In debug logs, identify position by @timestamp:stage (stage 3 = notify)
|
||||||
LOG_DEBUG("New GPS pos@%x:3 lat=%f lon=%f alt=%d pdop=%.2f track=%.2f speed=%.2f sats=%d", p.timestamp,
|
LOG_DEBUG("New GPS pos@%x:3 lat=%f lon=%f alt=%d pdop=%.2f track=%.2f speed=%.2f sats=%d", p.timestamp,
|
||||||
p.latitude_i * 1e-7, p.longitude_i * 1e-7, p.altitude, p.PDOP * 1e-2, p.ground_track * 1e-5,
|
p.latitude_i * 1e-7, p.longitude_i * 1e-7, p.altitude, p.PDOP * 1e-2, p.ground_track * 1e-5,
|
||||||
|
|||||||
@@ -851,9 +851,9 @@ void Power::readPowerStatus()
|
|||||||
running++;
|
running++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LOG_DEBUG(threadlist);
|
LOG_HEAP(threadlist);
|
||||||
LOG_DEBUG("Heap status: %d/%d bytes free (%d), running %d/%d threads", memGet.getFreeHeap(), memGet.getHeapSize(),
|
LOG_HEAP("Heap status: %d/%d bytes free (%d), running %d/%d threads", memGet.getFreeHeap(), memGet.getHeapSize(),
|
||||||
memGet.getFreeHeap() - lastheap, running, concurrency::mainController.size(false));
|
memGet.getFreeHeap() - lastheap, running, concurrency::mainController.size(false));
|
||||||
lastheap = memGet.getFreeHeap();
|
lastheap = memGet.getFreeHeap();
|
||||||
}
|
}
|
||||||
#ifdef DEBUG_HEAP_MQTT
|
#ifdef DEBUG_HEAP_MQTT
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "concurrency/OSThread.h"
|
#include "concurrency/OSThread.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
#include "memGet.h"
|
||||||
#include "mesh/generated/meshtastic/mesh.pb.h"
|
#include "mesh/generated/meshtastic/mesh.pb.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@@ -57,7 +58,7 @@ size_t RedirectablePrint::vprintf(const char *logLevel, const char *format, va_l
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ARCH_PORTDUINO
|
#ifdef ARCH_PORTDUINO
|
||||||
bool color = !settingsMap[ascii_logs];
|
bool color = !portduino_config.ascii_logs;
|
||||||
#else
|
#else
|
||||||
bool color = true;
|
bool color = true;
|
||||||
#endif
|
#endif
|
||||||
@@ -99,7 +100,7 @@ void RedirectablePrint::log_to_serial(const char *logLevel, const char *format,
|
|||||||
size_t r = 0;
|
size_t r = 0;
|
||||||
|
|
||||||
#ifdef ARCH_PORTDUINO
|
#ifdef ARCH_PORTDUINO
|
||||||
bool color = !settingsMap[ascii_logs];
|
bool color = !portduino_config.ascii_logs;
|
||||||
#else
|
#else
|
||||||
bool color = true;
|
bool color = true;
|
||||||
#endif
|
#endif
|
||||||
@@ -166,6 +167,16 @@ void RedirectablePrint::log_to_serial(const char *logLevel, const char *format,
|
|||||||
print(thread->ThreadName);
|
print(thread->ThreadName);
|
||||||
print("] ");
|
print("] ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_HEAP
|
||||||
|
// Add heap free space bytes prefix before every log message
|
||||||
|
#ifdef ARCH_PORTDUINO
|
||||||
|
::printf("[heap %u] ", memGet.getFreeHeap());
|
||||||
|
#else
|
||||||
|
printf("[heap %u] ", memGet.getFreeHeap());
|
||||||
|
#endif
|
||||||
|
#endif // DEBUG_HEAP
|
||||||
|
|
||||||
r += vprintf(logLevel, format, arg);
|
r += vprintf(logLevel, format, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,7 +299,7 @@ void RedirectablePrint::log(const char *logLevel, const char *format, ...)
|
|||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
// level trace is special, two possible ways to handle it.
|
// level trace is special, two possible ways to handle it.
|
||||||
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0) {
|
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0) {
|
||||||
if (settingsStrings[traceFilename] != "") {
|
if (portduino_config.traceFilename != "") {
|
||||||
va_list arg;
|
va_list arg;
|
||||||
va_start(arg, format);
|
va_start(arg, format);
|
||||||
try {
|
try {
|
||||||
@@ -297,18 +308,18 @@ void RedirectablePrint::log(const char *logLevel, const char *format, ...)
|
|||||||
}
|
}
|
||||||
va_end(arg);
|
va_end(arg);
|
||||||
}
|
}
|
||||||
if (settingsMap[logoutputlevel] < level_trace && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0) {
|
if (portduino_config.logoutputlevel < level_trace && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0) {
|
||||||
delete[] newFormat;
|
delete[] newFormat;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (settingsMap[logoutputlevel] < level_debug && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) {
|
if (portduino_config.logoutputlevel < level_debug && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) {
|
||||||
delete[] newFormat;
|
delete[] newFormat;
|
||||||
return;
|
return;
|
||||||
} else if (settingsMap[logoutputlevel] < level_info && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0) {
|
} else if (portduino_config.logoutputlevel < level_info && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0) {
|
||||||
delete[] newFormat;
|
delete[] newFormat;
|
||||||
return;
|
return;
|
||||||
} else if (settingsMap[logoutputlevel] < level_warn && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0) {
|
} else if (portduino_config.logoutputlevel < level_warn && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0) {
|
||||||
delete[] newFormat;
|
delete[] newFormat;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,9 +86,9 @@ void OSThread::run()
|
|||||||
#ifdef DEBUG_HEAP
|
#ifdef DEBUG_HEAP
|
||||||
auto newHeap = memGet.getFreeHeap();
|
auto newHeap = memGet.getFreeHeap();
|
||||||
if (newHeap < heap)
|
if (newHeap < heap)
|
||||||
LOG_DEBUG("------ Thread %s leaked heap %d -> %d (%d) ------", ThreadName.c_str(), heap, newHeap, newHeap - heap);
|
LOG_HEAP("------ Thread %s leaked heap %d -> %d (%d) ------", ThreadName.c_str(), heap, newHeap, newHeap - heap);
|
||||||
if (heap < newHeap)
|
if (heap < newHeap)
|
||||||
LOG_DEBUG("++++++ Thread %s freed heap %d -> %d (%d) ++++++", ThreadName.c_str(), heap, newHeap, newHeap - heap);
|
LOG_HEAP("++++++ Thread %s freed heap %d -> %d (%d) ++++++", ThreadName.c_str(), heap, newHeap, newHeap - heap);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
runned();
|
runned();
|
||||||
|
|||||||
@@ -262,6 +262,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#define VEXT_ON_VALUE LOW
|
#define VEXT_ON_VALUE LOW
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Rotary encoder
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
#ifndef ROTARY_DELAY
|
||||||
|
#define ROTARY_DELAY 5
|
||||||
|
#endif
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// GPS
|
// GPS
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -294,6 +294,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
|||||||
type = AHT10;
|
type = AHT10;
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
#if !defined(M5STACK_UNITC6L)
|
||||||
case INA_ADDR:
|
case INA_ADDR:
|
||||||
case INA_ADDR_ALTERNATE:
|
case INA_ADDR_ALTERNATE:
|
||||||
case INA_ADDR_WAVESHARE_UPS:
|
case INA_ADDR_WAVESHARE_UPS:
|
||||||
@@ -340,6 +341,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
|||||||
// else: probably a RAK12500/UBLOX GPS on I2C
|
// else: probably a RAK12500/UBLOX GPS on I2C
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
case MCP9808_ADDR:
|
case MCP9808_ADDR:
|
||||||
// We need to check for STK8BAXX first, since register 0x07 is new data flag for the z-axis and can produce some
|
// We need to check for STK8BAXX first, since register 0x07 is new data flag for the z-axis and can produce some
|
||||||
// weird result. and register 0x00 doesn't seems to be colliding with MCP9808 and LIS3DH chips.
|
// weird result. and register 0x00 doesn't seems to be colliding with MCP9808 and LIS3DH chips.
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#include <cstring> // Include for strstr
|
#include <cstring> // Include for strstr
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
@@ -517,6 +516,7 @@ bool GPS::setup()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Rare Serial Speeds
|
// Rare Serial Speeds
|
||||||
|
#ifndef CONFIG_IDF_TARGET_ESP32C6
|
||||||
if (probeTries == GPS_PROBETRIES) {
|
if (probeTries == GPS_PROBETRIES) {
|
||||||
LOG_DEBUG("Probe for GPS at %d", rareSerialSpeeds[speedSelect]);
|
LOG_DEBUG("Probe for GPS at %d", rareSerialSpeeds[speedSelect]);
|
||||||
gnssModel = probe(rareSerialSpeeds[speedSelect]);
|
gnssModel = probe(rareSerialSpeeds[speedSelect]);
|
||||||
@@ -527,6 +527,7 @@ bool GPS::setup()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gnssModel != GNSS_MODEL_UNKNOWN) {
|
if (gnssModel != GNSS_MODEL_UNKNOWN) {
|
||||||
@@ -808,6 +809,14 @@ bool GPS::setup()
|
|||||||
} else {
|
} else {
|
||||||
LOG_INFO("GNSS module configuration saved!");
|
LOG_INFO("GNSS module configuration saved!");
|
||||||
}
|
}
|
||||||
|
} else if (gnssModel == GNSS_MODEL_CM121) {
|
||||||
|
// only ask for RMC and GGA
|
||||||
|
// enable GGA
|
||||||
|
_serial_gps->write("$CFGMSG,0,0,1,1*1B\r\n");
|
||||||
|
delay(250);
|
||||||
|
// enable RMC
|
||||||
|
_serial_gps->write("$CFGMSG,0,4,1,1*1F\r\n");
|
||||||
|
delay(250);
|
||||||
}
|
}
|
||||||
didSerialInit = true;
|
didSerialInit = true;
|
||||||
}
|
}
|
||||||
@@ -1206,7 +1215,7 @@ static const char *DETECTED_MESSAGE = "%s detected";
|
|||||||
LOG_DEBUG(PROBE_MESSAGE, COMMAND, FAMILY_NAME); \
|
LOG_DEBUG(PROBE_MESSAGE, COMMAND, FAMILY_NAME); \
|
||||||
clearBuffer(); \
|
clearBuffer(); \
|
||||||
_serial_gps->write(COMMAND "\r\n"); \
|
_serial_gps->write(COMMAND "\r\n"); \
|
||||||
GnssModel_t detectedDriver = getProbeResponse(TIMEOUT, RESPONSE_MAP); \
|
GnssModel_t detectedDriver = getProbeResponse(TIMEOUT, RESPONSE_MAP, serialSpeed); \
|
||||||
if (detectedDriver != GNSS_MODEL_UNKNOWN) { \
|
if (detectedDriver != GNSS_MODEL_UNKNOWN) { \
|
||||||
return detectedDriver; \
|
return detectedDriver; \
|
||||||
} \
|
} \
|
||||||
@@ -1240,9 +1249,15 @@ GnssModel_t GPS::probe(int serialSpeed)
|
|||||||
_serial_gps->write("$PUBX,40,GSV,0,0,0,0,0,0*59\r\n");
|
_serial_gps->write("$PUBX,40,GSV,0,0,0,0,0,0*59\r\n");
|
||||||
_serial_gps->write("$PUBX,40,VTG,0,0,0,0,0,0*5E\r\n");
|
_serial_gps->write("$PUBX,40,VTG,0,0,0,0,0,0*5E\r\n");
|
||||||
delay(20);
|
delay(20);
|
||||||
|
// Close NMEA sequences on CM121
|
||||||
|
_serial_gps->write("$CFGMSG,0,1,0,1*1B\r\n");
|
||||||
|
_serial_gps->write("$CFGMSG,0,2,0,1*18\r\n");
|
||||||
|
_serial_gps->write("$CFGMSG,0,3,0,1*19\r\n");
|
||||||
|
delay(20);
|
||||||
|
|
||||||
// Unicore UFirebirdII Series: UC6580, UM620, UM621, UM670A, UM680A, or UM681A
|
// Unicore UFirebirdII Series: UC6580, UM620, UM621, UM670A, UM680A, or UM681A,or CM121
|
||||||
std::vector<ChipInfo> unicore = {{"UC6580", "UC6580", GNSS_MODEL_UC6580}, {"UM600", "UM600", GNSS_MODEL_UC6580}};
|
std::vector<ChipInfo> unicore = {
|
||||||
|
{"UC6580", "UC6580", GNSS_MODEL_UC6580}, {"UM600", "UM600", GNSS_MODEL_UC6580}, {"CM121", "CM121", GNSS_MODEL_CM121}};
|
||||||
PROBE_FAMILY("Unicore Family", "$PDTINFO", unicore, 500);
|
PROBE_FAMILY("Unicore Family", "$PDTINFO", unicore, 500);
|
||||||
|
|
||||||
std::vector<ChipInfo> atgm = {
|
std::vector<ChipInfo> atgm = {
|
||||||
@@ -1368,36 +1383,55 @@ GnssModel_t GPS::probe(int serialSpeed)
|
|||||||
return GNSS_MODEL_UNKNOWN;
|
return GNSS_MODEL_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
GnssModel_t GPS::getProbeResponse(unsigned long timeout, const std::vector<ChipInfo> &responseMap)
|
GnssModel_t GPS::getProbeResponse(unsigned long timeout, const std::vector<ChipInfo> &responseMap, int serialSpeed)
|
||||||
{
|
{
|
||||||
String response = "";
|
// Calculate buffer size based on baud rate - 256 bytes for 9600 baud as baseline
|
||||||
|
// Higher baud rates get proportionally larger buffers to handle more data
|
||||||
|
int bufferSize = (serialSpeed * 256) / 9600;
|
||||||
|
// Clamp buffer size between reasonable limits
|
||||||
|
if (bufferSize < 128)
|
||||||
|
bufferSize = 128;
|
||||||
|
if (bufferSize > 2048)
|
||||||
|
bufferSize = 2048;
|
||||||
|
|
||||||
|
char *response = new char[bufferSize](); // Dynamically allocate based on baud rate
|
||||||
|
uint16_t responseLen = 0;
|
||||||
unsigned long start = millis();
|
unsigned long start = millis();
|
||||||
while (millis() - start < timeout) {
|
while (millis() - start < timeout) {
|
||||||
if (_serial_gps->available()) {
|
if (_serial_gps->available()) {
|
||||||
response += (char)_serial_gps->read();
|
char c = _serial_gps->read();
|
||||||
|
|
||||||
if (response.endsWith(",") || response.endsWith("\r\n")) {
|
// Add char to buffer if there's space
|
||||||
|
if (responseLen < bufferSize - 1) {
|
||||||
|
response[responseLen++] = c;
|
||||||
|
response[responseLen] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == ',' || (responseLen >= 2 && response[responseLen - 2] == '\r' && response[responseLen - 1] == '\n')) {
|
||||||
#ifdef GPS_DEBUG
|
#ifdef GPS_DEBUG
|
||||||
LOG_DEBUG(response.c_str());
|
LOG_DEBUG(response);
|
||||||
#endif
|
#endif
|
||||||
// check if we can see our chips
|
// check if we can see our chips
|
||||||
for (const auto &chipInfo : responseMap) {
|
for (const auto &chipInfo : responseMap) {
|
||||||
if (strstr(response.c_str(), chipInfo.detectionString.c_str()) != nullptr) {
|
if (strstr(response, chipInfo.detectionString.c_str()) != nullptr) {
|
||||||
LOG_INFO("%s detected", chipInfo.chipName.c_str());
|
LOG_INFO("%s detected", chipInfo.chipName.c_str());
|
||||||
|
delete[] response; // Cleanup before return
|
||||||
return chipInfo.driver;
|
return chipInfo.driver;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (response.endsWith("\r\n")) {
|
if (responseLen >= 2 && response[responseLen - 2] == '\r' && response[responseLen - 1] == '\n') {
|
||||||
response.trim();
|
// Reset the response buffer for the next potential message
|
||||||
response = ""; // Reset the response string for the next potential message
|
responseLen = 0;
|
||||||
|
response[0] = '\0';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef GPS_DEBUG
|
#ifdef GPS_DEBUG
|
||||||
LOG_DEBUG(response.c_str());
|
LOG_DEBUG(response);
|
||||||
#endif
|
#endif
|
||||||
return GNSS_MODEL_UNKNOWN; // Return empty string on timeout
|
delete[] response; // Cleanup before return
|
||||||
|
return GNSS_MODEL_UNKNOWN; // Return unknown on timeout
|
||||||
}
|
}
|
||||||
|
|
||||||
GPS *GPS::createGps()
|
GPS *GPS::createGps()
|
||||||
@@ -1422,7 +1456,7 @@ GPS *GPS::createGps()
|
|||||||
_en_gpio = PIN_GPS_EN;
|
_en_gpio = PIN_GPS_EN;
|
||||||
#endif
|
#endif
|
||||||
#ifdef ARCH_PORTDUINO
|
#ifdef ARCH_PORTDUINO
|
||||||
if (!settingsMap[has_gps])
|
if (!portduino_config.has_gps)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
#endif
|
#endif
|
||||||
if (!_rx_gpio || !_serial_gps) // Configured to have no GPS at all
|
if (!_rx_gpio || !_serial_gps) // Configured to have no GPS at all
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ typedef enum {
|
|||||||
GNSS_MODEL_MTK_PA1616S,
|
GNSS_MODEL_MTK_PA1616S,
|
||||||
GNSS_MODEL_AG3335,
|
GNSS_MODEL_AG3335,
|
||||||
GNSS_MODEL_AG3352,
|
GNSS_MODEL_AG3352,
|
||||||
GNSS_MODEL_LS20031
|
GNSS_MODEL_LS20031,
|
||||||
|
GNSS_MODEL_CM121
|
||||||
} GnssModel_t;
|
} GnssModel_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -236,7 +237,7 @@ class GPS : private concurrency::OSThread
|
|||||||
|
|
||||||
virtual int32_t runOnce() override;
|
virtual int32_t runOnce() override;
|
||||||
|
|
||||||
GnssModel_t getProbeResponse(unsigned long timeout, const std::vector<ChipInfo> &responseMap);
|
GnssModel_t getProbeResponse(unsigned long timeout, const std::vector<ChipInfo> &responseMap, int serialSpeed);
|
||||||
|
|
||||||
// Get GNSS model
|
// Get GNSS model
|
||||||
GnssModel_t probe(int serialSpeed);
|
GnssModel_t probe(int serialSpeed);
|
||||||
|
|||||||
@@ -9,6 +9,9 @@
|
|||||||
static RTCQuality currentQuality = RTCQualityNone;
|
static RTCQuality currentQuality = RTCQualityNone;
|
||||||
uint32_t lastSetFromPhoneNtpOrGps = 0;
|
uint32_t lastSetFromPhoneNtpOrGps = 0;
|
||||||
|
|
||||||
|
static uint32_t lastTimeValidationWarning = 0;
|
||||||
|
static const uint32_t TIME_VALIDATION_WARNING_INTERVAL_MS = 15000; // 15 seconds
|
||||||
|
|
||||||
RTCQuality getRTCQuality()
|
RTCQuality getRTCQuality()
|
||||||
{
|
{
|
||||||
return currentQuality;
|
return currentQuality;
|
||||||
@@ -48,7 +51,9 @@ RTCSetResult readFromRTC()
|
|||||||
|
|
||||||
#ifdef BUILD_EPOCH
|
#ifdef BUILD_EPOCH
|
||||||
if (tv.tv_sec < BUILD_EPOCH) {
|
if (tv.tv_sec < BUILD_EPOCH) {
|
||||||
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
|
if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) {
|
||||||
|
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
|
||||||
|
}
|
||||||
return RTCSetResultInvalidTime;
|
return RTCSetResultInvalidTime;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -87,7 +92,10 @@ RTCSetResult readFromRTC()
|
|||||||
|
|
||||||
#ifdef BUILD_EPOCH
|
#ifdef BUILD_EPOCH
|
||||||
if (tv.tv_sec < BUILD_EPOCH) {
|
if (tv.tv_sec < BUILD_EPOCH) {
|
||||||
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
|
if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) {
|
||||||
|
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
|
||||||
|
lastTimeValidationWarning = millis();
|
||||||
|
}
|
||||||
return RTCSetResultInvalidTime;
|
return RTCSetResultInvalidTime;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -130,11 +138,20 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd
|
|||||||
uint32_t printableEpoch = tv->tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms
|
uint32_t printableEpoch = tv->tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms
|
||||||
#ifdef BUILD_EPOCH
|
#ifdef BUILD_EPOCH
|
||||||
if (tv->tv_sec < BUILD_EPOCH) {
|
if (tv->tv_sec < BUILD_EPOCH) {
|
||||||
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
|
if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) {
|
||||||
|
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
|
||||||
|
lastTimeValidationWarning = millis();
|
||||||
|
}
|
||||||
return RTCSetResultInvalidTime;
|
return RTCSetResultInvalidTime;
|
||||||
} else if (tv->tv_sec > (BUILD_EPOCH + FORTY_YEARS)) {
|
} else if ((uint64_t)tv->tv_sec > ((uint64_t)BUILD_EPOCH + FORTY_YEARS)) {
|
||||||
LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch, BUILD_EPOCH,
|
if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) {
|
||||||
BUILD_EPOCH + FORTY_YEARS);
|
// Calculate max allowed time safely to avoid overflow in logging
|
||||||
|
uint64_t maxAllowedTime = (uint64_t)BUILD_EPOCH + FORTY_YEARS;
|
||||||
|
uint32_t maxAllowedPrintable = (maxAllowedTime > UINT32_MAX) ? UINT32_MAX : (uint32_t)maxAllowedTime;
|
||||||
|
LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch,
|
||||||
|
(uint32_t)BUILD_EPOCH, maxAllowedPrintable);
|
||||||
|
lastTimeValidationWarning = millis();
|
||||||
|
}
|
||||||
return RTCSetResultInvalidTime;
|
return RTCSetResultInvalidTime;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -252,11 +269,20 @@ RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t)
|
|||||||
uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms
|
uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms
|
||||||
#ifdef BUILD_EPOCH
|
#ifdef BUILD_EPOCH
|
||||||
if (tv.tv_sec < BUILD_EPOCH) {
|
if (tv.tv_sec < BUILD_EPOCH) {
|
||||||
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
|
if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) {
|
||||||
|
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
|
||||||
|
lastTimeValidationWarning = millis();
|
||||||
|
}
|
||||||
return RTCSetResultInvalidTime;
|
return RTCSetResultInvalidTime;
|
||||||
} else if (tv.tv_sec > (BUILD_EPOCH + FORTY_YEARS)) {
|
} else if ((uint64_t)tv.tv_sec > ((uint64_t)BUILD_EPOCH + FORTY_YEARS)) {
|
||||||
LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch, BUILD_EPOCH,
|
if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) {
|
||||||
BUILD_EPOCH + FORTY_YEARS);
|
// Calculate max allowed time safely to avoid overflow in logging
|
||||||
|
uint64_t maxAllowedTime = (uint64_t)BUILD_EPOCH + FORTY_YEARS;
|
||||||
|
uint32_t maxAllowedPrintable = (maxAllowedTime > UINT32_MAX) ? UINT32_MAX : (uint32_t)maxAllowedTime;
|
||||||
|
LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch,
|
||||||
|
(uint32_t)BUILD_EPOCH, maxAllowedPrintable);
|
||||||
|
lastTimeValidationWarning = millis();
|
||||||
|
}
|
||||||
return RTCSetResultInvalidTime;
|
return RTCSetResultInvalidTime;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -316,14 +342,40 @@ uint32_t getValidTime(RTCQuality minQuality, bool local)
|
|||||||
time_t gm_mktime(struct tm *tm)
|
time_t gm_mktime(struct tm *tm)
|
||||||
{
|
{
|
||||||
#if !MESHTASTIC_EXCLUDE_TZ
|
#if !MESHTASTIC_EXCLUDE_TZ
|
||||||
setenv("TZ", "GMT0", 1);
|
time_t result = 0;
|
||||||
time_t res = mktime(tm);
|
|
||||||
if (*config.device.tzdef) {
|
// First, get us to the start of tm->year, by calcuating the number of days since the Unix epoch.
|
||||||
setenv("TZ", config.device.tzdef, 1);
|
int year = 1900 + tm->tm_year; // tm_year is years since 1900
|
||||||
} else {
|
int year_minus_one = year - 1;
|
||||||
setenv("TZ", "UTC0", 1);
|
int days_before_this_year = 0;
|
||||||
|
days_before_this_year += year_minus_one * 365;
|
||||||
|
// leap days: every 4 years, except 100s, but including 400s.
|
||||||
|
days_before_this_year += year_minus_one / 4 - year_minus_one / 100 + year_minus_one / 400;
|
||||||
|
// subtract from 1970-01-01 to get days since epoch
|
||||||
|
days_before_this_year -= 719162; // (1969 * 365 + 1969 / 4 - 1969 / 100 + 1969 / 400);
|
||||||
|
|
||||||
|
// Now, within this tm->year, compute the days *before* this tm->month starts.
|
||||||
|
int days_before_month[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; // non-leap year
|
||||||
|
int days_this_year_before_this_month = days_before_month[tm->tm_mon]; // tm->tm_mon is 0..11
|
||||||
|
|
||||||
|
// If this is a leap year, and we're past February, add a day:
|
||||||
|
if (tm->tm_mon >= 2 && (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)) {
|
||||||
|
days_this_year_before_this_month += 1;
|
||||||
}
|
}
|
||||||
return res;
|
|
||||||
|
// And within this month:
|
||||||
|
int days_this_month_before_today = tm->tm_mday - 1; // tm->tm_mday is 1..31
|
||||||
|
|
||||||
|
// Now combine them all together, and convert days to seconds:
|
||||||
|
result += (days_before_this_year + days_this_year_before_this_month + days_this_month_before_today);
|
||||||
|
result *= 86400L;
|
||||||
|
|
||||||
|
// Finally, add in the hours, minutes, and seconds of today:
|
||||||
|
result += tm->tm_hour * 3600;
|
||||||
|
result += tm->tm_min * 60;
|
||||||
|
result += tm->tm_sec;
|
||||||
|
|
||||||
|
return result;
|
||||||
#else
|
#else
|
||||||
return mktime(tm);
|
return mktime(tm);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -56,5 +56,5 @@ time_t gm_mktime(struct tm *tm);
|
|||||||
#define SEC_PER_HOUR 3600
|
#define SEC_PER_HOUR 3600
|
||||||
#define SEC_PER_MIN 60
|
#define SEC_PER_MIN 60
|
||||||
#ifdef BUILD_EPOCH
|
#ifdef BUILD_EPOCH
|
||||||
#define FORTY_YEARS (40UL * 365 * SEC_PER_DAY) // probably time to update your firmware
|
static constexpr uint64_t FORTY_YEARS = (40ULL * 365 * SEC_PER_DAY); // Use 64-bit arithmetic to prevent overflow
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#include "PowerMon.h"
|
#include "PowerMon.h"
|
||||||
#include "Throttle.h"
|
#include "Throttle.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
|
#include "meshUtils.h"
|
||||||
#if HAS_SCREEN
|
#if HAS_SCREEN
|
||||||
#include <OLEDDisplay.h>
|
#include <OLEDDisplay.h>
|
||||||
|
|
||||||
@@ -58,7 +59,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#include "mesh-pb-constants.h"
|
#include "mesh-pb-constants.h"
|
||||||
#include "mesh/Channels.h"
|
#include "mesh/Channels.h"
|
||||||
#include "mesh/generated/meshtastic/deviceonly.pb.h"
|
#include "mesh/generated/meshtastic/deviceonly.pb.h"
|
||||||
#include "meshUtils.h"
|
|
||||||
#include "modules/ExternalNotificationModule.h"
|
#include "modules/ExternalNotificationModule.h"
|
||||||
#include "modules/TextMessageModule.h"
|
#include "modules/TextMessageModule.h"
|
||||||
#include "modules/WaypointModule.h"
|
#include "modules/WaypointModule.h"
|
||||||
@@ -317,6 +317,14 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
|
|||||||
#elif defined(USE_SSD1306)
|
#elif defined(USE_SSD1306)
|
||||||
dispdev = new SSD1306Wire(address.address, -1, -1, geometry,
|
dispdev = new SSD1306Wire(address.address, -1, -1, geometry,
|
||||||
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
||||||
|
#elif defined(USE_SPISSD1306)
|
||||||
|
dispdev = new SSD1306Spi(SSD1306_RESET, SSD1306_RS, SSD1306_NSS, GEOMETRY_64_48);
|
||||||
|
if (!dispdev->init()) {
|
||||||
|
LOG_DEBUG("Error: SSD1306 not detected!");
|
||||||
|
} else {
|
||||||
|
static_cast<SSD1306Spi *>(dispdev)->setHorizontalOffset(32);
|
||||||
|
LOG_INFO("SSD1306 init success");
|
||||||
|
}
|
||||||
#elif defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7789_CS) || \
|
#elif defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7789_CS) || \
|
||||||
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS)
|
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS)
|
||||||
dispdev = new TFTDisplay(address.address, -1, -1, geometry,
|
dispdev = new TFTDisplay(address.address, -1, -1, geometry,
|
||||||
@@ -332,7 +340,7 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
|
|||||||
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
||||||
#elif ARCH_PORTDUINO
|
#elif ARCH_PORTDUINO
|
||||||
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
||||||
if (settingsMap[displayPanel] != no_screen) {
|
if (portduino_config.displayPanel != no_screen) {
|
||||||
LOG_DEBUG("Make TFTDisplay!");
|
LOG_DEBUG("Make TFTDisplay!");
|
||||||
dispdev = new TFTDisplay(address.address, -1, -1, geometry,
|
dispdev = new TFTDisplay(address.address, -1, -1, geometry,
|
||||||
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
||||||
@@ -507,7 +515,7 @@ void Screen::setup()
|
|||||||
// === Apply loaded brightness ===
|
// === Apply loaded brightness ===
|
||||||
#if defined(ST7789_CS)
|
#if defined(ST7789_CS)
|
||||||
static_cast<TFTDisplay *>(dispdev)->setDisplayBrightness(brightness);
|
static_cast<TFTDisplay *>(dispdev)->setDisplayBrightness(brightness);
|
||||||
#elif defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107)
|
#elif defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107) || defined(USE_SPISSD1306)
|
||||||
dispdev->setBrightness(brightness);
|
dispdev->setBrightness(brightness);
|
||||||
#endif
|
#endif
|
||||||
LOG_INFO("Applied screen brightness: %d", brightness);
|
LOG_INFO("Applied screen brightness: %d", brightness);
|
||||||
@@ -554,7 +562,7 @@ void Screen::setup()
|
|||||||
static_cast<TFTDisplay *>(dispdev)->flipScreenVertically();
|
static_cast<TFTDisplay *>(dispdev)->flipScreenVertically();
|
||||||
#elif defined(USE_ST7789)
|
#elif defined(USE_ST7789)
|
||||||
static_cast<ST7789Spi *>(dispdev)->flipScreenVertically();
|
static_cast<ST7789Spi *>(dispdev)->flipScreenVertically();
|
||||||
#else
|
#elif !defined(M5STACK_UNITC6L)
|
||||||
dispdev->flipScreenVertically();
|
dispdev->flipScreenVertically();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -580,7 +588,7 @@ void Screen::setup()
|
|||||||
|
|
||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
||||||
if (settingsMap[touchscreenModule]) {
|
if (portduino_config.touchscreenModule) {
|
||||||
touchScreenImpl1 =
|
touchScreenImpl1 =
|
||||||
new TouchScreenImpl1(dispdev->getWidth(), dispdev->getHeight(), static_cast<TFTDisplay *>(dispdev)->getTouch);
|
new TouchScreenImpl1(dispdev->getWidth(), dispdev->getHeight(), static_cast<TFTDisplay *>(dispdev)->getTouch);
|
||||||
touchScreenImpl1->init();
|
touchScreenImpl1->init();
|
||||||
@@ -692,7 +700,11 @@ int32_t Screen::runOnce()
|
|||||||
|
|
||||||
#ifndef DISABLE_WELCOME_UNSET
|
#ifndef DISABLE_WELCOME_UNSET
|
||||||
if (!NotificationRenderer::isOverlayBannerShowing() && config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
|
if (!NotificationRenderer::isOverlayBannerShowing() && config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
menuHandler::LoraRegionPicker();
|
||||||
|
#else
|
||||||
menuHandler::OnboardMessage();
|
menuHandler::OnboardMessage();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (!NotificationRenderer::isOverlayBannerShowing() && rebootAtMsec != 0) {
|
if (!NotificationRenderer::isOverlayBannerShowing() && rebootAtMsec != 0) {
|
||||||
@@ -889,71 +901,95 @@ void Screen::setFrames(FrameFocus focus)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(DISPLAY_CLOCK_FRAME)
|
#if defined(DISPLAY_CLOCK_FRAME)
|
||||||
fsi.positions.clock = numframes;
|
if (!hiddenFrames.clock) {
|
||||||
normalFrames[numframes++] = uiconfig.is_clockface_analog ? graphics::ClockRenderer::drawAnalogClockFrame
|
fsi.positions.clock = numframes;
|
||||||
: graphics::ClockRenderer::drawDigitalClockFrame;
|
#if defined(M5STACK_UNITC6L)
|
||||||
indicatorIcons.push_back(digital_icon_clock);
|
normalFrames[numframes++] = graphics::ClockRenderer::drawAnalogClockFrame;
|
||||||
|
#else
|
||||||
|
normalFrames[numframes++] = uiconfig.is_clockface_analog ? graphics::ClockRenderer::drawAnalogClockFrame
|
||||||
|
: graphics::ClockRenderer::drawDigitalClockFrame;
|
||||||
|
#endif
|
||||||
|
indicatorIcons.push_back(digital_icon_clock);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Declare this early so it’s available in FOCUS_PRESERVE block
|
// Declare this early so it’s available in FOCUS_PRESERVE block
|
||||||
bool willInsertTextMessage = shouldDrawMessage(&devicestate.rx_text_message);
|
bool willInsertTextMessage = shouldDrawMessage(&devicestate.rx_text_message);
|
||||||
|
|
||||||
fsi.positions.home = numframes;
|
if (!hiddenFrames.home) {
|
||||||
normalFrames[numframes++] = graphics::UIRenderer::drawDeviceFocused;
|
fsi.positions.home = numframes;
|
||||||
indicatorIcons.push_back(icon_home);
|
normalFrames[numframes++] = graphics::UIRenderer::drawDeviceFocused;
|
||||||
|
indicatorIcons.push_back(icon_home);
|
||||||
|
}
|
||||||
|
|
||||||
fsi.positions.textMessage = numframes;
|
fsi.positions.textMessage = numframes;
|
||||||
normalFrames[numframes++] = graphics::MessageRenderer::drawTextMessageFrame;
|
normalFrames[numframes++] = graphics::MessageRenderer::drawTextMessageFrame;
|
||||||
indicatorIcons.push_back(icon_mail);
|
indicatorIcons.push_back(icon_mail);
|
||||||
|
|
||||||
#ifndef USE_EINK
|
#ifndef USE_EINK
|
||||||
fsi.positions.nodelist = numframes;
|
if (!hiddenFrames.nodelist) {
|
||||||
normalFrames[numframes++] = graphics::NodeListRenderer::drawDynamicNodeListScreen;
|
fsi.positions.nodelist = numframes;
|
||||||
indicatorIcons.push_back(icon_nodes);
|
normalFrames[numframes++] = graphics::NodeListRenderer::drawDynamicNodeListScreen;
|
||||||
|
indicatorIcons.push_back(icon_nodes);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Show detailed node views only on E-Ink builds
|
// Show detailed node views only on E-Ink builds
|
||||||
#ifdef USE_EINK
|
#ifdef USE_EINK
|
||||||
fsi.positions.nodelist_lastheard = numframes;
|
if (!hiddenFrames.nodelist_lastheard) {
|
||||||
normalFrames[numframes++] = graphics::NodeListRenderer::drawLastHeardScreen;
|
fsi.positions.nodelist_lastheard = numframes;
|
||||||
indicatorIcons.push_back(icon_nodes);
|
normalFrames[numframes++] = graphics::NodeListRenderer::drawLastHeardScreen;
|
||||||
|
indicatorIcons.push_back(icon_nodes);
|
||||||
fsi.positions.nodelist_hopsignal = numframes;
|
}
|
||||||
normalFrames[numframes++] = graphics::NodeListRenderer::drawHopSignalScreen;
|
if (!hiddenFrames.nodelist_hopsignal) {
|
||||||
indicatorIcons.push_back(icon_signal);
|
fsi.positions.nodelist_hopsignal = numframes;
|
||||||
|
normalFrames[numframes++] = graphics::NodeListRenderer::drawHopSignalScreen;
|
||||||
fsi.positions.nodelist_distance = numframes;
|
indicatorIcons.push_back(icon_signal);
|
||||||
normalFrames[numframes++] = graphics::NodeListRenderer::drawDistanceScreen;
|
}
|
||||||
indicatorIcons.push_back(icon_distance);
|
if (!hiddenFrames.nodelist_distance) {
|
||||||
|
fsi.positions.nodelist_distance = numframes;
|
||||||
|
normalFrames[numframes++] = graphics::NodeListRenderer::drawDistanceScreen;
|
||||||
|
indicatorIcons.push_back(icon_distance);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#if HAS_GPS
|
#if HAS_GPS
|
||||||
fsi.positions.nodelist_bearings = numframes;
|
if (!hiddenFrames.nodelist_bearings) {
|
||||||
normalFrames[numframes++] = graphics::NodeListRenderer::drawNodeListWithCompasses;
|
fsi.positions.nodelist_bearings = numframes;
|
||||||
indicatorIcons.push_back(icon_list);
|
normalFrames[numframes++] = graphics::NodeListRenderer::drawNodeListWithCompasses;
|
||||||
|
indicatorIcons.push_back(icon_list);
|
||||||
fsi.positions.gps = numframes;
|
}
|
||||||
normalFrames[numframes++] = graphics::UIRenderer::drawCompassAndLocationScreen;
|
if (!hiddenFrames.gps) {
|
||||||
indicatorIcons.push_back(icon_compass);
|
fsi.positions.gps = numframes;
|
||||||
|
normalFrames[numframes++] = graphics::UIRenderer::drawCompassAndLocationScreen;
|
||||||
|
indicatorIcons.push_back(icon_compass);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
if (RadioLibInterface::instance) {
|
if (RadioLibInterface::instance && !hiddenFrames.lora) {
|
||||||
fsi.positions.lora = numframes;
|
fsi.positions.lora = numframes;
|
||||||
normalFrames[numframes++] = graphics::DebugRenderer::drawLoRaFocused;
|
normalFrames[numframes++] = graphics::DebugRenderer::drawLoRaFocused;
|
||||||
indicatorIcons.push_back(icon_radio);
|
indicatorIcons.push_back(icon_radio);
|
||||||
}
|
}
|
||||||
if (!dismissedFrames.memory) {
|
if (!hiddenFrames.system) {
|
||||||
fsi.positions.memory = numframes;
|
fsi.positions.system = numframes;
|
||||||
normalFrames[numframes++] = graphics::DebugRenderer::drawMemoryUsage;
|
normalFrames[numframes++] = graphics::DebugRenderer::drawSystemScreen;
|
||||||
indicatorIcons.push_back(icon_memory);
|
indicatorIcons.push_back(icon_system);
|
||||||
}
|
}
|
||||||
#if !defined(DISPLAY_CLOCK_FRAME)
|
#if !defined(DISPLAY_CLOCK_FRAME)
|
||||||
fsi.positions.clock = numframes;
|
if (!hiddenFrames.clock) {
|
||||||
normalFrames[numframes++] = uiconfig.is_clockface_analog ? graphics::ClockRenderer::drawAnalogClockFrame
|
fsi.positions.clock = numframes;
|
||||||
: graphics::ClockRenderer::drawDigitalClockFrame;
|
normalFrames[numframes++] = uiconfig.is_clockface_analog ? graphics::ClockRenderer::drawAnalogClockFrame
|
||||||
indicatorIcons.push_back(digital_icon_clock);
|
: graphics::ClockRenderer::drawDigitalClockFrame;
|
||||||
|
indicatorIcons.push_back(digital_icon_clock);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if (!hiddenFrames.chirpy) {
|
||||||
|
fsi.positions.chirpy = numframes;
|
||||||
|
normalFrames[numframes++] = graphics::DebugRenderer::drawChirpy;
|
||||||
|
indicatorIcons.push_back(small_chirpy);
|
||||||
|
}
|
||||||
|
|
||||||
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
|
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
|
||||||
if (!dismissedFrames.wifi && isWifiAvailable()) {
|
if (!hiddenFrames.wifi && isWifiAvailable()) {
|
||||||
fsi.positions.wifi = numframes;
|
fsi.positions.wifi = numframes;
|
||||||
normalFrames[numframes++] = graphics::DebugRenderer::drawDebugInfoWiFiTrampoline;
|
normalFrames[numframes++] = graphics::DebugRenderer::drawDebugInfoWiFiTrampoline;
|
||||||
indicatorIcons.push_back(icon_wifi);
|
indicatorIcons.push_back(icon_wifi);
|
||||||
@@ -995,27 +1031,29 @@ void Screen::setFrames(FrameFocus focus)
|
|||||||
if (numMeshNodes > 0)
|
if (numMeshNodes > 0)
|
||||||
numMeshNodes--;
|
numMeshNodes--;
|
||||||
|
|
||||||
// Temporary array to hold favorite node frames
|
if (!hiddenFrames.show_favorites) {
|
||||||
std::vector<FrameCallback> favoriteFrames;
|
// Temporary array to hold favorite node frames
|
||||||
|
std::vector<FrameCallback> favoriteFrames;
|
||||||
|
|
||||||
for (size_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
|
for (size_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
|
||||||
const meshtastic_NodeInfoLite *n = nodeDB->getMeshNodeByIndex(i);
|
const meshtastic_NodeInfoLite *n = nodeDB->getMeshNodeByIndex(i);
|
||||||
if (n && n->num != nodeDB->getNodeNum() && n->is_favorite) {
|
if (n && n->num != nodeDB->getNodeNum() && n->is_favorite) {
|
||||||
favoriteFrames.push_back(graphics::UIRenderer::drawNodeInfo);
|
favoriteFrames.push_back(graphics::UIRenderer::drawNodeInfo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Insert favorite frames *after* collecting them all
|
// Insert favorite frames *after* collecting them all
|
||||||
if (!favoriteFrames.empty()) {
|
if (!favoriteFrames.empty()) {
|
||||||
fsi.positions.firstFavorite = numframes;
|
fsi.positions.firstFavorite = numframes;
|
||||||
for (const auto &f : favoriteFrames) {
|
for (const auto &f : favoriteFrames) {
|
||||||
normalFrames[numframes++] = f;
|
normalFrames[numframes++] = f;
|
||||||
indicatorIcons.push_back(icon_node);
|
indicatorIcons.push_back(icon_node);
|
||||||
|
}
|
||||||
|
fsi.positions.lastFavorite = numframes - 1;
|
||||||
|
} else {
|
||||||
|
fsi.positions.firstFavorite = 255;
|
||||||
|
fsi.positions.lastFavorite = 255;
|
||||||
}
|
}
|
||||||
fsi.positions.lastFavorite = numframes - 1;
|
|
||||||
} else {
|
|
||||||
fsi.positions.firstFavorite = 255;
|
|
||||||
fsi.positions.lastFavorite = 255;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fsi.frameCount = numframes; // Total framecount is used to apply FOCUS_PRESERVE
|
fsi.frameCount = numframes; // Total framecount is used to apply FOCUS_PRESERVE
|
||||||
@@ -1054,7 +1092,7 @@ void Screen::setFrames(FrameFocus focus)
|
|||||||
ui->switchToFrame(fsi.positions.clock);
|
ui->switchToFrame(fsi.positions.clock);
|
||||||
break;
|
break;
|
||||||
case FOCUS_SYSTEM:
|
case FOCUS_SYSTEM:
|
||||||
ui->switchToFrame(fsi.positions.memory);
|
ui->switchToFrame(fsi.positions.system);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FOCUS_PRESERVE:
|
case FOCUS_PRESERVE:
|
||||||
@@ -1082,30 +1120,101 @@ void Screen::setFrameImmediateDraw(FrameCallback *drawFrames)
|
|||||||
setFastFramerate();
|
setFastFramerate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Screen::toggleFrameVisibility(const std::string &frameName)
|
||||||
|
{
|
||||||
|
#ifndef USE_EINK
|
||||||
|
if (frameName == "nodelist") {
|
||||||
|
hiddenFrames.nodelist = !hiddenFrames.nodelist;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_EINK
|
||||||
|
if (frameName == "nodelist_lastheard") {
|
||||||
|
hiddenFrames.nodelist_lastheard = !hiddenFrames.nodelist_lastheard;
|
||||||
|
}
|
||||||
|
if (frameName == "nodelist_hopsignal") {
|
||||||
|
hiddenFrames.nodelist_hopsignal = !hiddenFrames.nodelist_hopsignal;
|
||||||
|
}
|
||||||
|
if (frameName == "nodelist_distance") {
|
||||||
|
hiddenFrames.nodelist_distance = !hiddenFrames.nodelist_distance;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if HAS_GPS
|
||||||
|
if (frameName == "nodelist_bearings") {
|
||||||
|
hiddenFrames.nodelist_bearings = !hiddenFrames.nodelist_bearings;
|
||||||
|
}
|
||||||
|
if (frameName == "gps") {
|
||||||
|
hiddenFrames.gps = !hiddenFrames.gps;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (frameName == "lora") {
|
||||||
|
hiddenFrames.lora = !hiddenFrames.lora;
|
||||||
|
}
|
||||||
|
if (frameName == "clock") {
|
||||||
|
hiddenFrames.clock = !hiddenFrames.clock;
|
||||||
|
}
|
||||||
|
if (frameName == "show_favorites") {
|
||||||
|
hiddenFrames.show_favorites = !hiddenFrames.show_favorites;
|
||||||
|
}
|
||||||
|
if (frameName == "chirpy") {
|
||||||
|
hiddenFrames.chirpy = !hiddenFrames.chirpy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Screen::isFrameHidden(const std::string &frameName) const
|
||||||
|
{
|
||||||
|
#ifndef USE_EINK
|
||||||
|
if (frameName == "nodelist")
|
||||||
|
return hiddenFrames.nodelist;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_EINK
|
||||||
|
if (frameName == "nodelist_lastheard")
|
||||||
|
return hiddenFrames.nodelist_lastheard;
|
||||||
|
if (frameName == "nodelist_hopsignal")
|
||||||
|
return hiddenFrames.nodelist_hopsignal;
|
||||||
|
if (frameName == "nodelist_distance")
|
||||||
|
return hiddenFrames.nodelist_distance;
|
||||||
|
#endif
|
||||||
|
#if HAS_GPS
|
||||||
|
if (frameName == "nodelist_bearings")
|
||||||
|
return hiddenFrames.nodelist_bearings;
|
||||||
|
if (frameName == "gps")
|
||||||
|
return hiddenFrames.gps;
|
||||||
|
#endif
|
||||||
|
if (frameName == "lora")
|
||||||
|
return hiddenFrames.lora;
|
||||||
|
if (frameName == "clock")
|
||||||
|
return hiddenFrames.clock;
|
||||||
|
if (frameName == "show_favorites")
|
||||||
|
return hiddenFrames.show_favorites;
|
||||||
|
if (frameName == "chirpy")
|
||||||
|
return hiddenFrames.chirpy;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Dismisses the currently displayed screen frame, if possible
|
// Dismisses the currently displayed screen frame, if possible
|
||||||
// Relevant for text message, waypoint, others in future?
|
// Relevant for text message, waypoint, others in future?
|
||||||
// Triggered with a CardKB keycombo
|
// Triggered with a CardKB keycombo
|
||||||
void Screen::dismissCurrentFrame()
|
void Screen::hideCurrentFrame()
|
||||||
{
|
{
|
||||||
uint8_t currentFrame = ui->getUiState()->currentFrame;
|
uint8_t currentFrame = ui->getUiState()->currentFrame;
|
||||||
bool dismissed = false;
|
bool dismissed = false;
|
||||||
|
|
||||||
if (currentFrame == framesetInfo.positions.textMessage && devicestate.has_rx_text_message) {
|
if (currentFrame == framesetInfo.positions.textMessage && devicestate.has_rx_text_message) {
|
||||||
LOG_INFO("Dismiss Text Message");
|
LOG_INFO("Hide Text Message");
|
||||||
devicestate.has_rx_text_message = false;
|
devicestate.has_rx_text_message = false;
|
||||||
memset(&devicestate.rx_text_message, 0, sizeof(devicestate.rx_text_message));
|
memset(&devicestate.rx_text_message, 0, sizeof(devicestate.rx_text_message));
|
||||||
} else if (currentFrame == framesetInfo.positions.waypoint && devicestate.has_rx_waypoint) {
|
} else if (currentFrame == framesetInfo.positions.waypoint && devicestate.has_rx_waypoint) {
|
||||||
LOG_DEBUG("Dismiss Waypoint");
|
LOG_DEBUG("Hide Waypoint");
|
||||||
devicestate.has_rx_waypoint = false;
|
devicestate.has_rx_waypoint = false;
|
||||||
dismissedFrames.waypoint = true;
|
hiddenFrames.waypoint = true;
|
||||||
dismissed = true;
|
dismissed = true;
|
||||||
} else if (currentFrame == framesetInfo.positions.wifi) {
|
} else if (currentFrame == framesetInfo.positions.wifi) {
|
||||||
LOG_DEBUG("Dismiss WiFi Screen");
|
LOG_DEBUG("Hide WiFi Screen");
|
||||||
dismissedFrames.wifi = true;
|
hiddenFrames.wifi = true;
|
||||||
dismissed = true;
|
dismissed = true;
|
||||||
} else if (currentFrame == framesetInfo.positions.memory) {
|
} else if (currentFrame == framesetInfo.positions.lora) {
|
||||||
LOG_INFO("Dismiss Memory");
|
LOG_INFO("Hide LoRa");
|
||||||
dismissedFrames.memory = true;
|
hiddenFrames.lora = true;
|
||||||
dismissed = true;
|
dismissed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1226,6 +1335,10 @@ void Screen::handleShowNextFrame()
|
|||||||
|
|
||||||
void Screen::setFastFramerate()
|
void Screen::setFastFramerate()
|
||||||
{
|
{
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
dispdev->clear();
|
||||||
|
dispdev->display();
|
||||||
|
#endif
|
||||||
// We are about to start a transition so speed up fps
|
// We are about to start a transition so speed up fps
|
||||||
targetFramerate = SCREEN_TRANSITION_FRAMERATE;
|
targetFramerate = SCREEN_TRANSITION_FRAMERATE;
|
||||||
|
|
||||||
@@ -1257,7 +1370,7 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
|
|||||||
// Outgoing message (likely sent from phone)
|
// Outgoing message (likely sent from phone)
|
||||||
devicestate.has_rx_text_message = false;
|
devicestate.has_rx_text_message = false;
|
||||||
memset(&devicestate.rx_text_message, 0, sizeof(devicestate.rx_text_message));
|
memset(&devicestate.rx_text_message, 0, sizeof(devicestate.rx_text_message));
|
||||||
dismissedFrames.textMessage = true;
|
hiddenFrames.textMessage = true;
|
||||||
hasUnreadMessage = false; // Clear unread state when user replies
|
hasUnreadMessage = false; // Clear unread state when user replies
|
||||||
|
|
||||||
setFrames(FOCUS_PRESERVE); // Stay on same frame, silently update frame list
|
setFrames(FOCUS_PRESERVE); // Stay on same frame, silently update frame list
|
||||||
@@ -1297,13 +1410,23 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (longName && longName[0]) {
|
if (longName && longName[0]) {
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
strcpy(banner, "New Message");
|
||||||
|
#else
|
||||||
snprintf(banner, sizeof(banner), "New Message from\n%s", longName);
|
snprintf(banner, sizeof(banner), "New Message from\n%s", longName);
|
||||||
|
#endif
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
strcpy(banner, "New Message");
|
strcpy(banner, "New Message");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
screen->setOn(true);
|
||||||
|
screen->showSimpleBanner(banner, 1500);
|
||||||
|
playLongBeep();
|
||||||
|
#else
|
||||||
screen->showSimpleBanner(banner, 3000);
|
screen->showSimpleBanner(banner, 3000);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1372,7 +1495,7 @@ int Screen::handleInputEvent(const InputEvent *event)
|
|||||||
} else if (event->inputEvent == INPUT_BROKER_SELECT) {
|
} else if (event->inputEvent == INPUT_BROKER_SELECT) {
|
||||||
if (this->ui->getUiState()->currentFrame == framesetInfo.positions.home) {
|
if (this->ui->getUiState()->currentFrame == framesetInfo.positions.home) {
|
||||||
menuHandler::homeBaseMenu();
|
menuHandler::homeBaseMenu();
|
||||||
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.memory) {
|
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.system) {
|
||||||
menuHandler::systemBaseMenu();
|
menuHandler::systemBaseMenu();
|
||||||
#if HAS_GPS
|
#if HAS_GPS
|
||||||
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.gps && gps) {
|
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.gps && gps) {
|
||||||
@@ -1381,12 +1504,16 @@ int Screen::handleInputEvent(const InputEvent *event)
|
|||||||
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.clock) {
|
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.clock) {
|
||||||
menuHandler::clockMenu();
|
menuHandler::clockMenu();
|
||||||
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.lora) {
|
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.lora) {
|
||||||
menuHandler::LoraRegionPicker();
|
menuHandler::loraMenu();
|
||||||
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.textMessage) {
|
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.textMessage) {
|
||||||
if (devicestate.rx_text_message.from) {
|
if (devicestate.rx_text_message.from) {
|
||||||
menuHandler::messageResponseMenu();
|
menuHandler::messageResponseMenu();
|
||||||
} else {
|
} else {
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
menuHandler::textMessageMenu();
|
||||||
|
#else
|
||||||
menuHandler::textMessageBaseMenu();
|
menuHandler::textMessageBaseMenu();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
} else if (framesetInfo.positions.firstFavorite != 255 &&
|
} else if (framesetInfo.positions.firstFavorite != 255 &&
|
||||||
this->ui->getUiState()->currentFrame >= framesetInfo.positions.firstFavorite &&
|
this->ui->getUiState()->currentFrame >= framesetInfo.positions.firstFavorite &&
|
||||||
@@ -1445,13 +1572,15 @@ bool shouldWakeOnReceivedMessage()
|
|||||||
/*
|
/*
|
||||||
The goal here is to determine when we do NOT wake up the screen on message received:
|
The goal here is to determine when we do NOT wake up the screen on message received:
|
||||||
- Any ext. notifications are turned on
|
- Any ext. notifications are turned on
|
||||||
- If role is not client / client_mute
|
- If role is not CLIENT / CLIENT_MUTE / CLIENT_HIDDEN / CLIENT_BASE
|
||||||
- If the battery level is very low
|
- If the battery level is very low
|
||||||
*/
|
*/
|
||||||
if (moduleConfig.external_notification.enabled) {
|
if (moduleConfig.external_notification.enabled) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!meshtastic_Config_DeviceConfig_Role_CLIENT && !meshtastic_Config_DeviceConfig_Role_CLIENT_MUTE) {
|
if (!IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_CLIENT,
|
||||||
|
meshtastic_Config_DeviceConfig_Role_CLIENT_MUTE, meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN,
|
||||||
|
meshtastic_Config_DeviceConfig_Role_CLIENT_BASE)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (powerStatus && powerStatus->getBatteryChargePercent() < 10) {
|
if (powerStatus && powerStatus->getBatteryChargePercent() < 10) {
|
||||||
|
|||||||
@@ -81,6 +81,8 @@ class Screen
|
|||||||
#include <SSD1306Wire.h>
|
#include <SSD1306Wire.h>
|
||||||
#elif defined(USE_ST7789)
|
#elif defined(USE_ST7789)
|
||||||
#include <ST7789Spi.h>
|
#include <ST7789Spi.h>
|
||||||
|
#elif defined(USE_SPISSD1306)
|
||||||
|
#include <SSD1306Spi.h>
|
||||||
#else
|
#else
|
||||||
// the SH1106/SSD1306 variant is auto-detected
|
// the SH1106/SSD1306 variant is auto-detected
|
||||||
#include <AutoOLEDWire.h>
|
#include <AutoOLEDWire.h>
|
||||||
@@ -591,7 +593,11 @@ class Screen : public concurrency::OSThread
|
|||||||
void setSSLFrames();
|
void setSSLFrames();
|
||||||
|
|
||||||
// Dismiss the currently focussed frame, if possible (e.g. text message, waypoint)
|
// Dismiss the currently focussed frame, if possible (e.g. text message, waypoint)
|
||||||
void dismissCurrentFrame();
|
void hideCurrentFrame();
|
||||||
|
|
||||||
|
// Menu-driven Show / Hide Toggle
|
||||||
|
void toggleFrameVisibility(const std::string &frameName);
|
||||||
|
bool isFrameHidden(const std::string &frameName) const;
|
||||||
|
|
||||||
#ifdef USE_EINK
|
#ifdef USE_EINK
|
||||||
/// Draw an image to remain on E-Ink display after screen off
|
/// Draw an image to remain on E-Ink display after screen off
|
||||||
@@ -653,7 +659,7 @@ class Screen : public concurrency::OSThread
|
|||||||
uint8_t settings = 255;
|
uint8_t settings = 255;
|
||||||
uint8_t wifi = 255;
|
uint8_t wifi = 255;
|
||||||
uint8_t deviceFocused = 255;
|
uint8_t deviceFocused = 255;
|
||||||
uint8_t memory = 255;
|
uint8_t system = 255;
|
||||||
uint8_t gps = 255;
|
uint8_t gps = 255;
|
||||||
uint8_t home = 255;
|
uint8_t home = 255;
|
||||||
uint8_t textMessage = 255;
|
uint8_t textMessage = 255;
|
||||||
@@ -663,6 +669,7 @@ class Screen : public concurrency::OSThread
|
|||||||
uint8_t nodelist_distance = 255;
|
uint8_t nodelist_distance = 255;
|
||||||
uint8_t nodelist_bearings = 255;
|
uint8_t nodelist_bearings = 255;
|
||||||
uint8_t clock = 255;
|
uint8_t clock = 255;
|
||||||
|
uint8_t chirpy = 255;
|
||||||
uint8_t firstFavorite = 255;
|
uint8_t firstFavorite = 255;
|
||||||
uint8_t lastFavorite = 255;
|
uint8_t lastFavorite = 255;
|
||||||
uint8_t lora = 255;
|
uint8_t lora = 255;
|
||||||
@@ -671,12 +678,29 @@ class Screen : public concurrency::OSThread
|
|||||||
uint8_t frameCount = 0;
|
uint8_t frameCount = 0;
|
||||||
} framesetInfo;
|
} framesetInfo;
|
||||||
|
|
||||||
struct DismissedFrames {
|
struct hiddenFrames {
|
||||||
bool textMessage = false;
|
bool textMessage = false;
|
||||||
bool waypoint = false;
|
bool waypoint = false;
|
||||||
bool wifi = false;
|
bool wifi = false;
|
||||||
bool memory = false;
|
bool system = false;
|
||||||
} dismissedFrames;
|
bool home = false;
|
||||||
|
bool clock = false;
|
||||||
|
#ifndef USE_EINK
|
||||||
|
bool nodelist = false;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_EINK
|
||||||
|
bool nodelist_lastheard = false;
|
||||||
|
bool nodelist_hopsignal = false;
|
||||||
|
bool nodelist_distance = false;
|
||||||
|
#endif
|
||||||
|
#if HAS_GPS
|
||||||
|
bool nodelist_bearings = false;
|
||||||
|
bool gps = false;
|
||||||
|
#endif
|
||||||
|
bool lora = false;
|
||||||
|
bool show_favorites = false;
|
||||||
|
bool chirpy = true;
|
||||||
|
} hiddenFrames;
|
||||||
|
|
||||||
/// Try to start drawing ASAP
|
/// Try to start drawing ASAP
|
||||||
void setFastFramerate();
|
void setFastFramerate();
|
||||||
|
|||||||
@@ -79,6 +79,10 @@
|
|||||||
#define FONT_SMALL FONT_MEDIUM_LOCAL // Height: 19
|
#define FONT_SMALL FONT_MEDIUM_LOCAL // Height: 19
|
||||||
#define FONT_MEDIUM FONT_LARGE_LOCAL // Height: 28
|
#define FONT_MEDIUM FONT_LARGE_LOCAL // Height: 28
|
||||||
#define FONT_LARGE FONT_LARGE_LOCAL // Height: 28
|
#define FONT_LARGE FONT_LARGE_LOCAL // Height: 28
|
||||||
|
#elif defined(M5STACK_UNITC6L)
|
||||||
|
#define FONT_SMALL FONT_SMALL_LOCAL // Height: 13
|
||||||
|
#define FONT_MEDIUM FONT_SMALL_LOCAL // Height: 13
|
||||||
|
#define FONT_LARGE FONT_SMALL_LOCAL // Height: 13
|
||||||
#else
|
#else
|
||||||
#define FONT_SMALL FONT_SMALL_LOCAL // Height: 13
|
#define FONT_SMALL FONT_SMALL_LOCAL // Height: 13
|
||||||
#define FONT_MEDIUM FONT_MEDIUM_LOCAL // Height: 19
|
#define FONT_MEDIUM FONT_MEDIUM_LOCAL // Height: 19
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "graphics/SharedUIDisplay.h"
|
#include "graphics/SharedUIDisplay.h"
|
||||||
#include "RTC.h"
|
#include "RTC.h"
|
||||||
#include "graphics/ScreenFonts.h"
|
#include "graphics/ScreenFonts.h"
|
||||||
|
#include "graphics/draw/UIRenderer.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "meshtastic/config.pb.h"
|
#include "meshtastic/config.pb.h"
|
||||||
#include "power.h"
|
#include "power.h"
|
||||||
@@ -16,6 +17,10 @@ void determineResolution(int16_t screenheight, int16_t screenwidth)
|
|||||||
isHighResolution = true;
|
isHighResolution = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (screenwidth > 128 && screenheight <= 64) {
|
||||||
|
isHighResolution = false;
|
||||||
|
}
|
||||||
|
|
||||||
// Special case for Heltec Wireless Tracker v1.1
|
// Special case for Heltec Wireless Tracker v1.1
|
||||||
if (screenwidth == 160 && screenheight == 80) {
|
if (screenwidth == 160 && screenheight == 80) {
|
||||||
isHighResolution = false;
|
isHighResolution = false;
|
||||||
@@ -53,7 +58,7 @@ void drawRoundedHighlight(OLEDDisplay *display, int16_t x, int16_t y, int16_t w,
|
|||||||
// *************************
|
// *************************
|
||||||
// * Common Header Drawing *
|
// * Common Header Drawing *
|
||||||
// *************************
|
// *************************
|
||||||
void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr, bool battery_only)
|
void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr, bool force_no_invert, bool show_date)
|
||||||
{
|
{
|
||||||
constexpr int HEADER_OFFSET_Y = 1;
|
constexpr int HEADER_OFFSET_Y = 1;
|
||||||
y += HEADER_OFFSET_Y;
|
y += HEADER_OFFSET_Y;
|
||||||
@@ -69,7 +74,7 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
|
|||||||
const int screenW = display->getWidth();
|
const int screenW = display->getWidth();
|
||||||
const int screenH = display->getHeight();
|
const int screenH = display->getHeight();
|
||||||
|
|
||||||
if (!battery_only) {
|
if (!force_no_invert) {
|
||||||
// === Inverted Header Background ===
|
// === Inverted Header Background ===
|
||||||
if (isInverted) {
|
if (isInverted) {
|
||||||
display->setColor(BLACK);
|
display->setColor(BLACK);
|
||||||
@@ -124,7 +129,7 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
|
|||||||
|
|
||||||
int batteryX = 1;
|
int batteryX = 1;
|
||||||
int batteryY = HEADER_OFFSET_Y + 1;
|
int batteryY = HEADER_OFFSET_Y + 1;
|
||||||
|
#if !defined(M5STACK_UNITC6L)
|
||||||
// === Battery Icons ===
|
// === Battery Icons ===
|
||||||
if (usbPowered && !isCharging) { // This is a basic check to determine USB Powered is flagged but not charging
|
if (usbPowered && !isCharging) { // This is a basic check to determine USB Powered is flagged but not charging
|
||||||
batteryX += 1;
|
batteryX += 1;
|
||||||
@@ -187,13 +192,28 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
|
|||||||
int timeStrWidth = display->getStringWidth("12:34"); // Default alignment
|
int timeStrWidth = display->getStringWidth("12:34"); // Default alignment
|
||||||
int timeX = screenW - xOffset - timeStrWidth + 4;
|
int timeX = screenW - xOffset - timeStrWidth + 4;
|
||||||
|
|
||||||
if (rtc_sec > 0 && !battery_only) {
|
if (rtc_sec > 0) {
|
||||||
// === Build Time String ===
|
// === Build Time String ===
|
||||||
long hms = (rtc_sec % SEC_PER_DAY + SEC_PER_DAY) % SEC_PER_DAY;
|
long hms = (rtc_sec % SEC_PER_DAY + SEC_PER_DAY) % SEC_PER_DAY;
|
||||||
int hour = hms / SEC_PER_HOUR;
|
int hour = hms / SEC_PER_HOUR;
|
||||||
int minute = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
|
int minute = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
|
||||||
snprintf(timeStr, sizeof(timeStr), "%d:%02d", hour, minute);
|
snprintf(timeStr, sizeof(timeStr), "%d:%02d", hour, minute);
|
||||||
|
|
||||||
|
// === Build Date String ===
|
||||||
|
char datetimeStr[25];
|
||||||
|
UIRenderer::formatDateTime(datetimeStr, sizeof(datetimeStr), rtc_sec, display, false);
|
||||||
|
char dateLine[40];
|
||||||
|
|
||||||
|
if (isHighResolution) {
|
||||||
|
snprintf(dateLine, sizeof(dateLine), "%s", datetimeStr);
|
||||||
|
} else {
|
||||||
|
if (hasUnreadMessage) {
|
||||||
|
snprintf(dateLine, sizeof(dateLine), "%s", &datetimeStr[5]);
|
||||||
|
} else {
|
||||||
|
snprintf(dateLine, sizeof(dateLine), "%s", &datetimeStr[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (config.display.use_12h_clock) {
|
if (config.display.use_12h_clock) {
|
||||||
bool isPM = hour >= 12;
|
bool isPM = hour >= 12;
|
||||||
hour %= 12;
|
hour %= 12;
|
||||||
@@ -202,7 +222,11 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
|
|||||||
snprintf(timeStr, sizeof(timeStr), "%d:%02d%s", hour, minute, isPM ? "p" : "a");
|
snprintf(timeStr, sizeof(timeStr), "%d:%02d%s", hour, minute, isPM ? "p" : "a");
|
||||||
}
|
}
|
||||||
|
|
||||||
timeStrWidth = display->getStringWidth(timeStr);
|
if (show_date) {
|
||||||
|
timeStrWidth = display->getStringWidth(dateLine);
|
||||||
|
} else {
|
||||||
|
timeStrWidth = display->getStringWidth(timeStr);
|
||||||
|
}
|
||||||
timeX = screenW - xOffset - timeStrWidth + 3;
|
timeX = screenW - xOffset - timeStrWidth + 3;
|
||||||
|
|
||||||
// === Show Mail or Mute Icon to the Left of Time ===
|
// === Show Mail or Mute Icon to the Left of Time ===
|
||||||
@@ -229,7 +253,7 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
|
|||||||
int iconW = 16, iconH = 12;
|
int iconW = 16, iconH = 12;
|
||||||
int iconX = iconRightEdge - iconW;
|
int iconX = iconRightEdge - iconW;
|
||||||
int iconY = textY + (FONT_HEIGHT_SMALL - iconH) / 2 - 1;
|
int iconY = textY + (FONT_HEIGHT_SMALL - iconH) / 2 - 1;
|
||||||
if (isInverted) {
|
if (isInverted && !force_no_invert) {
|
||||||
display->setColor(WHITE);
|
display->setColor(WHITE);
|
||||||
display->fillRect(iconX - 1, iconY - 1, iconW + 3, iconH + 2);
|
display->fillRect(iconX - 1, iconY - 1, iconW + 3, iconH + 2);
|
||||||
display->setColor(BLACK);
|
display->setColor(BLACK);
|
||||||
@@ -244,7 +268,7 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
|
|||||||
} else {
|
} else {
|
||||||
int iconX = iconRightEdge - (mail_width - 2);
|
int iconX = iconRightEdge - (mail_width - 2);
|
||||||
int iconY = textY + (FONT_HEIGHT_SMALL - mail_height) / 2;
|
int iconY = textY + (FONT_HEIGHT_SMALL - mail_height) / 2;
|
||||||
if (isInverted) {
|
if (isInverted && !force_no_invert) {
|
||||||
display->setColor(WHITE);
|
display->setColor(WHITE);
|
||||||
display->fillRect(iconX - 1, iconY - 1, mail_width + 2, mail_height + 2);
|
display->fillRect(iconX - 1, iconY - 1, mail_width + 2, mail_height + 2);
|
||||||
display->setColor(BLACK);
|
display->setColor(BLACK);
|
||||||
@@ -287,10 +311,17 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// === Draw Time ===
|
if (show_date) {
|
||||||
display->drawString(timeX, textY, timeStr);
|
// === Draw Date ===
|
||||||
if (isBold)
|
display->drawString(timeX, textY, dateLine);
|
||||||
display->drawString(timeX - 1, textY, timeStr);
|
if (isBold)
|
||||||
|
display->drawString(timeX - 1, textY, dateLine);
|
||||||
|
} else {
|
||||||
|
// === Draw Time ===
|
||||||
|
display->drawString(timeX, textY, timeStr);
|
||||||
|
if (isBold)
|
||||||
|
display->drawString(timeX - 1, textY, timeStr);
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// === No Time Available: Mail/Mute Icon Moves to Far Right ===
|
// === No Time Available: Mail/Mute Icon Moves to Far Right ===
|
||||||
@@ -337,7 +368,7 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
display->setColor(WHITE); // Reset for other UI
|
display->setColor(WHITE); // Reset for other UI
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,8 @@ void determineResolution(int16_t screenheight, int16_t screenwidth);
|
|||||||
void drawRoundedHighlight(OLEDDisplay *display, int16_t x, int16_t y, int16_t w, int16_t h, int16_t r);
|
void drawRoundedHighlight(OLEDDisplay *display, int16_t x, int16_t y, int16_t w, int16_t h, int16_t r);
|
||||||
|
|
||||||
// Shared battery/time/mail header
|
// Shared battery/time/mail header
|
||||||
void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr = "", bool battery_only = false);
|
void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr = "", bool force_no_invert = false,
|
||||||
|
bool show_date = false);
|
||||||
|
|
||||||
const int *getTextPositions(OLEDDisplay *display);
|
const int *getTextPositions(OLEDDisplay *display);
|
||||||
|
|
||||||
|
|||||||
@@ -767,24 +767,24 @@ class LGFX : public lgfx::LGFX_Device
|
|||||||
|
|
||||||
LGFX(void)
|
LGFX(void)
|
||||||
{
|
{
|
||||||
if (settingsMap[displayPanel] == st7789)
|
if (portduino_config.displayPanel == st7789)
|
||||||
_panel_instance = new lgfx::Panel_ST7789;
|
_panel_instance = new lgfx::Panel_ST7789;
|
||||||
else if (settingsMap[displayPanel] == st7735)
|
else if (portduino_config.displayPanel == st7735)
|
||||||
_panel_instance = new lgfx::Panel_ST7735;
|
_panel_instance = new lgfx::Panel_ST7735;
|
||||||
else if (settingsMap[displayPanel] == st7735s)
|
else if (portduino_config.displayPanel == st7735s)
|
||||||
_panel_instance = new lgfx::Panel_ST7735S;
|
_panel_instance = new lgfx::Panel_ST7735S;
|
||||||
else if (settingsMap[displayPanel] == st7796)
|
else if (portduino_config.displayPanel == st7796)
|
||||||
_panel_instance = new lgfx::Panel_ST7796;
|
_panel_instance = new lgfx::Panel_ST7796;
|
||||||
else if (settingsMap[displayPanel] == ili9341)
|
else if (portduino_config.displayPanel == ili9341)
|
||||||
_panel_instance = new lgfx::Panel_ILI9341;
|
_panel_instance = new lgfx::Panel_ILI9341;
|
||||||
else if (settingsMap[displayPanel] == ili9342)
|
else if (portduino_config.displayPanel == ili9342)
|
||||||
_panel_instance = new lgfx::Panel_ILI9342;
|
_panel_instance = new lgfx::Panel_ILI9342;
|
||||||
else if (settingsMap[displayPanel] == ili9488)
|
else if (portduino_config.displayPanel == ili9488)
|
||||||
_panel_instance = new lgfx::Panel_ILI9488;
|
_panel_instance = new lgfx::Panel_ILI9488;
|
||||||
else if (settingsMap[displayPanel] == hx8357d)
|
else if (portduino_config.displayPanel == hx8357d)
|
||||||
_panel_instance = new lgfx::Panel_HX8357D;
|
_panel_instance = new lgfx::Panel_HX8357D;
|
||||||
#if defined(LGFX_SDL)
|
#if defined(LGFX_SDL)
|
||||||
else if (settingsMap[displayPanel] == x11) {
|
else if (portduino_config.displayPanel == x11) {
|
||||||
_panel_instance = new lgfx::Panel_sdl;
|
_panel_instance = new lgfx::Panel_sdl;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -795,61 +795,61 @@ class LGFX : public lgfx::LGFX_Device
|
|||||||
|
|
||||||
auto buscfg = _bus_instance.config();
|
auto buscfg = _bus_instance.config();
|
||||||
buscfg.spi_mode = 0;
|
buscfg.spi_mode = 0;
|
||||||
buscfg.spi_host = settingsMap[displayspidev];
|
buscfg.spi_host = portduino_config.display_spi_dev_int;
|
||||||
|
|
||||||
buscfg.pin_dc = settingsMap[displayDC]; // Set SPI DC pin number (-1 = disable)
|
buscfg.pin_dc = portduino_config.displayDC.pin; // Set SPI DC pin number (-1 = disable)
|
||||||
|
|
||||||
_bus_instance.config(buscfg); // applies the set value to the bus.
|
_bus_instance.config(buscfg); // applies the set value to the bus.
|
||||||
_panel_instance->setBus(&_bus_instance); // set the bus on the panel.
|
_panel_instance->setBus(&_bus_instance); // set the bus on the panel.
|
||||||
|
|
||||||
auto cfg = _panel_instance->config(); // Gets a structure for display panel settings.
|
auto cfg = _panel_instance->config(); // Gets a structure for display panel settings.
|
||||||
LOG_DEBUG("Width: %d, Height: %d", settingsMap[displayWidth], settingsMap[displayHeight]);
|
LOG_DEBUG("Width: %d, Height: %d", portduino_config.displayWidth, portduino_config.displayHeight);
|
||||||
cfg.pin_cs = settingsMap[displayCS]; // Pin number where CS is connected (-1 = disable)
|
cfg.pin_cs = portduino_config.displayCS.pin; // Pin number where CS is connected (-1 = disable)
|
||||||
cfg.pin_rst = settingsMap[displayReset];
|
cfg.pin_rst = portduino_config.displayReset.pin;
|
||||||
if (settingsMap[displayRotate]) {
|
if (portduino_config.displayRotate) {
|
||||||
cfg.panel_width = settingsMap[displayHeight]; // actual displayable width
|
cfg.panel_width = portduino_config.displayHeight; // actual displayable width
|
||||||
cfg.panel_height = settingsMap[displayWidth]; // actual displayable height
|
cfg.panel_height = portduino_config.displayWidth; // actual displayable height
|
||||||
} else {
|
} else {
|
||||||
cfg.panel_width = settingsMap[displayWidth]; // actual displayable width
|
cfg.panel_width = portduino_config.displayWidth; // actual displayable width
|
||||||
cfg.panel_height = settingsMap[displayHeight]; // actual displayable height
|
cfg.panel_height = portduino_config.displayHeight; // actual displayable height
|
||||||
}
|
}
|
||||||
cfg.offset_x = settingsMap[displayOffsetX]; // Panel offset amount in X direction
|
cfg.offset_x = portduino_config.displayOffsetX; // Panel offset amount in X direction
|
||||||
cfg.offset_y = settingsMap[displayOffsetY]; // Panel offset amount in Y direction
|
cfg.offset_y = portduino_config.displayOffsetY; // Panel offset amount in Y direction
|
||||||
cfg.offset_rotation = settingsMap[displayOffsetRotate]; // Rotation direction value offset 0~7 (4~7 is mirrored)
|
cfg.offset_rotation = portduino_config.displayOffsetRotate; // Rotation direction value offset 0~7 (4~7 is mirrored)
|
||||||
cfg.invert = settingsMap[displayInvert]; // Set to true if the light/darkness of the panel is reversed
|
cfg.invert = portduino_config.displayInvert; // Set to true if the light/darkness of the panel is reversed
|
||||||
|
|
||||||
_panel_instance->config(cfg);
|
_panel_instance->config(cfg);
|
||||||
|
|
||||||
// Configure settings for touch control.
|
// Configure settings for touch control.
|
||||||
if (settingsMap[touchscreenModule]) {
|
if (portduino_config.touchscreenModule) {
|
||||||
if (settingsMap[touchscreenModule] == xpt2046) {
|
if (portduino_config.touchscreenModule == xpt2046) {
|
||||||
_touch_instance = new lgfx::Touch_XPT2046;
|
_touch_instance = new lgfx::Touch_XPT2046;
|
||||||
} else if (settingsMap[touchscreenModule] == stmpe610) {
|
} else if (portduino_config.touchscreenModule == stmpe610) {
|
||||||
_touch_instance = new lgfx::Touch_STMPE610;
|
_touch_instance = new lgfx::Touch_STMPE610;
|
||||||
} else if (settingsMap[touchscreenModule] == ft5x06) {
|
} else if (portduino_config.touchscreenModule == ft5x06) {
|
||||||
_touch_instance = new lgfx::Touch_FT5x06;
|
_touch_instance = new lgfx::Touch_FT5x06;
|
||||||
}
|
}
|
||||||
auto touch_cfg = _touch_instance->config();
|
auto touch_cfg = _touch_instance->config();
|
||||||
|
|
||||||
touch_cfg.pin_cs = settingsMap[touchscreenCS];
|
touch_cfg.pin_cs = portduino_config.touchscreenCS.pin;
|
||||||
touch_cfg.x_min = 0;
|
touch_cfg.x_min = 0;
|
||||||
touch_cfg.x_max = settingsMap[displayHeight] - 1;
|
touch_cfg.x_max = portduino_config.displayHeight - 1;
|
||||||
touch_cfg.y_min = 0;
|
touch_cfg.y_min = 0;
|
||||||
touch_cfg.y_max = settingsMap[displayWidth] - 1;
|
touch_cfg.y_max = portduino_config.displayWidth - 1;
|
||||||
touch_cfg.pin_int = settingsMap[touchscreenIRQ];
|
touch_cfg.pin_int = portduino_config.touchscreenIRQ.pin;
|
||||||
touch_cfg.bus_shared = true;
|
touch_cfg.bus_shared = true;
|
||||||
touch_cfg.offset_rotation = settingsMap[touchscreenRotate];
|
touch_cfg.offset_rotation = portduino_config.touchscreenRotate;
|
||||||
if (settingsMap[touchscreenI2CAddr] != -1) {
|
if (portduino_config.touchscreenI2CAddr != -1) {
|
||||||
touch_cfg.i2c_addr = settingsMap[touchscreenI2CAddr];
|
touch_cfg.i2c_addr = portduino_config.touchscreenI2CAddr;
|
||||||
} else {
|
} else {
|
||||||
touch_cfg.spi_host = settingsMap[touchscreenspidev];
|
touch_cfg.spi_host = portduino_config.touchscreen_spi_dev_int;
|
||||||
}
|
}
|
||||||
|
|
||||||
_touch_instance->config(touch_cfg);
|
_touch_instance->config(touch_cfg);
|
||||||
_panel_instance->setTouch(_touch_instance);
|
_panel_instance->setTouch(_touch_instance);
|
||||||
}
|
}
|
||||||
#if defined(LGFX_SDL)
|
#if defined(LGFX_SDL)
|
||||||
if (settingsMap[displayPanel] == x11) {
|
if (portduino_config.displayPanel == x11) {
|
||||||
lgfx::Panel_sdl *sdl_panel_ = (lgfx::Panel_sdl *)_panel_instance;
|
lgfx::Panel_sdl *sdl_panel_ = (lgfx::Panel_sdl *)_panel_instance;
|
||||||
sdl_panel_->setup();
|
sdl_panel_->setup();
|
||||||
sdl_panel_->addKeyCodeMapping(SDLK_RETURN, SDL_SCANCODE_KP_ENTER);
|
sdl_panel_->addKeyCodeMapping(SDLK_RETURN, SDL_SCANCODE_KP_ENTER);
|
||||||
@@ -1115,10 +1115,10 @@ TFTDisplay::TFTDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY g
|
|||||||
backlightEnable = p;
|
backlightEnable = p;
|
||||||
|
|
||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
if (settingsMap[displayRotate]) {
|
if (portduino_config.displayRotate) {
|
||||||
setGeometry(GEOMETRY_RAWMODE, settingsMap[configNames::displayWidth], settingsMap[configNames::displayHeight]);
|
setGeometry(GEOMETRY_RAWMODE, portduino_config.displayWidth, portduino_config.displayWidth);
|
||||||
} else {
|
} else {
|
||||||
setGeometry(GEOMETRY_RAWMODE, settingsMap[configNames::displayHeight], settingsMap[configNames::displayWidth]);
|
setGeometry(GEOMETRY_RAWMODE, portduino_config.displayHeight, portduino_config.displayHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif defined(SCREEN_ROTATE)
|
#elif defined(SCREEN_ROTATE)
|
||||||
@@ -1240,7 +1240,7 @@ void TFTDisplay::sdlLoop()
|
|||||||
#if defined(LGFX_SDL)
|
#if defined(LGFX_SDL)
|
||||||
static int lastPressed = 0;
|
static int lastPressed = 0;
|
||||||
static int shuttingDown = false;
|
static int shuttingDown = false;
|
||||||
if (settingsMap[displayPanel] == x11) {
|
if (portduino_config.displayPanel == x11) {
|
||||||
lgfx::Panel_sdl *sdl_panel_ = (lgfx::Panel_sdl *)tft->_panel_instance;
|
lgfx::Panel_sdl *sdl_panel_ = (lgfx::Panel_sdl *)tft->_panel_instance;
|
||||||
if (sdl_panel_->loop() && !shuttingDown) {
|
if (sdl_panel_->loop() && !shuttingDown) {
|
||||||
LOG_WARN("Window Closed!");
|
LOG_WARN("Window Closed!");
|
||||||
@@ -1288,8 +1288,8 @@ void TFTDisplay::sendCommand(uint8_t com)
|
|||||||
backlightEnable->set(true);
|
backlightEnable->set(true);
|
||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
display(true);
|
display(true);
|
||||||
if (settingsMap[displayBacklight] > 0)
|
if (portduino_config.displayBacklight.pin > 0)
|
||||||
digitalWrite(settingsMap[displayBacklight], TFT_BACKLIGHT_ON);
|
digitalWrite(portduino_config.displayBacklight.pin, TFT_BACKLIGHT_ON);
|
||||||
#elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE)
|
#elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE)
|
||||||
tft->wakeup();
|
tft->wakeup();
|
||||||
tft->powerSaveOff();
|
tft->powerSaveOff();
|
||||||
@@ -1312,8 +1312,8 @@ void TFTDisplay::sendCommand(uint8_t com)
|
|||||||
backlightEnable->set(false);
|
backlightEnable->set(false);
|
||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
tft->clear();
|
tft->clear();
|
||||||
if (settingsMap[displayBacklight] > 0)
|
if (portduino_config.displayBacklight.pin > 0)
|
||||||
digitalWrite(settingsMap[displayBacklight], !TFT_BACKLIGHT_ON);
|
digitalWrite(portduino_config.displayBacklight.pin, !TFT_BACKLIGHT_ON);
|
||||||
#elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE)
|
#elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE)
|
||||||
tft->sleep();
|
tft->sleep();
|
||||||
tft->powerSaveOn();
|
tft->powerSaveOn();
|
||||||
|
|||||||
@@ -190,7 +190,8 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
|
|||||||
// === Set Title, Blank for Clock
|
// === Set Title, Blank for Clock
|
||||||
const char *titleStr = "";
|
const char *titleStr = "";
|
||||||
// === Header ===
|
// === Header ===
|
||||||
graphics::drawCommonHeader(display, x, y, titleStr, true);
|
graphics::drawCommonHeader(display, x, y, titleStr, true, true);
|
||||||
|
int line = 0;
|
||||||
|
|
||||||
#ifdef T_WATCH_S3
|
#ifdef T_WATCH_S3
|
||||||
if (nimbleBluetooth && nimbleBluetooth->isConnected()) {
|
if (nimbleBluetooth && nimbleBluetooth->isConnected()) {
|
||||||
@@ -294,6 +295,7 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
|
|||||||
display->drawString(startingHourMinuteTextX + xOffset, (display->getHeight() - hourMinuteTextY) - yOffset - 2,
|
display->drawString(startingHourMinuteTextX + xOffset, (display->getHeight() - hourMinuteTextY) - yOffset - 2,
|
||||||
isPM ? "pm" : "am");
|
isPM ? "pm" : "am");
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef USE_EINK
|
#ifndef USE_EINK
|
||||||
xOffset = (isHighResolution) ? 18 : 10;
|
xOffset = (isHighResolution) ? 18 : 10;
|
||||||
display->drawString(startingHourMinuteTextX + timeStringWidth - xOffset, (display->getHeight() - hourMinuteTextY) - yOffset,
|
display->drawString(startingHourMinuteTextX + timeStringWidth - xOffset, (display->getHeight() - hourMinuteTextY) - yOffset,
|
||||||
@@ -313,7 +315,8 @@ void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
// === Set Title, Blank for Clock
|
// === Set Title, Blank for Clock
|
||||||
const char *titleStr = "";
|
const char *titleStr = "";
|
||||||
// === Header ===
|
// === Header ===
|
||||||
graphics::drawCommonHeader(display, x, y, titleStr, true);
|
graphics::drawCommonHeader(display, x, y, titleStr, true, true);
|
||||||
|
int line = 0;
|
||||||
|
|
||||||
#ifdef T_WATCH_S3
|
#ifdef T_WATCH_S3
|
||||||
if (nimbleBluetooth && nimbleBluetooth->isConnected()) {
|
if (nimbleBluetooth && nimbleBluetooth->isConnected()) {
|
||||||
|
|||||||
@@ -277,12 +277,13 @@ void drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
|
|||||||
std::string uptime = UIRenderer::drawTimeDelta(days, hours, minutes, seconds);
|
std::string uptime = UIRenderer::drawTimeDelta(days, hours, minutes, seconds);
|
||||||
|
|
||||||
// Line 1 (Still)
|
// Line 1 (Still)
|
||||||
|
#if !defined(M5STACK_UNITC6L)
|
||||||
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(uptime.c_str()), y, uptime.c_str());
|
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(uptime.c_str()), y, uptime.c_str());
|
||||||
if (config.display.heading_bold)
|
if (config.display.heading_bold)
|
||||||
display->drawString(x - 1 + SCREEN_WIDTH - display->getStringWidth(uptime.c_str()), y, uptime.c_str());
|
display->drawString(x - 1 + SCREEN_WIDTH - display->getStringWidth(uptime.c_str()), y, uptime.c_str());
|
||||||
|
|
||||||
display->setColor(WHITE);
|
display->setColor(WHITE);
|
||||||
|
#endif
|
||||||
// Setup string to assemble analogClock string
|
// Setup string to assemble analogClock string
|
||||||
std::string analogClock = "";
|
std::string analogClock = "";
|
||||||
|
|
||||||
@@ -329,8 +330,7 @@ void drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
|
|||||||
#if HAS_GPS
|
#if HAS_GPS
|
||||||
if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
|
if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
|
||||||
// Line 3
|
// Line 3
|
||||||
if (config.display.gps_format !=
|
if (uiconfig.gps_format != meshtastic_DeviceUIConfig_GpsCoordinateFormat_DMS) // if DMS then don't draw altitude
|
||||||
meshtastic_Config_DisplayConfig_GpsCoordinateFormat_DMS) // if DMS then don't draw altitude
|
|
||||||
UIRenderer::drawGpsAltitude(display, x, y + FONT_HEIGHT_SMALL * 2, gpsStatus);
|
UIRenderer::drawGpsAltitude(display, x, y + FONT_HEIGHT_SMALL * 2, gpsStatus);
|
||||||
|
|
||||||
// Line 4
|
// Line 4
|
||||||
@@ -386,31 +386,56 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
|
|||||||
char shortnameble[35];
|
char shortnameble[35];
|
||||||
getMacAddr(dmac);
|
getMacAddr(dmac);
|
||||||
snprintf(screen->ourId, sizeof(screen->ourId), "%02x%02x", dmac[4], dmac[5]);
|
snprintf(screen->ourId, sizeof(screen->ourId), "%02x%02x", dmac[4], dmac[5]);
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
snprintf(shortnameble, sizeof(shortnameble), "%s", screen->ourId);
|
||||||
|
#else
|
||||||
snprintf(shortnameble, sizeof(shortnameble), "BLE: %s", screen->ourId);
|
snprintf(shortnameble, sizeof(shortnameble), "BLE: %s", screen->ourId);
|
||||||
|
#endif
|
||||||
int textWidth = display->getStringWidth(shortnameble);
|
int textWidth = display->getStringWidth(shortnameble);
|
||||||
int nameX = (SCREEN_WIDTH - textWidth);
|
int nameX = (SCREEN_WIDTH - textWidth);
|
||||||
display->drawString(nameX, getTextPositions(display)[line++], shortnameble);
|
display->drawString(nameX, getTextPositions(display)[line++], shortnameble);
|
||||||
|
|
||||||
// === Second Row: Radio Preset ===
|
// === Second Row: Role ===
|
||||||
|
auto role = DisplayFormatters::getDeviceRole(config.device.role);
|
||||||
|
char device_role[25];
|
||||||
|
snprintf(device_role, sizeof(device_role), "Role: %s", role);
|
||||||
|
textWidth = display->getStringWidth(device_role);
|
||||||
|
nameX = (SCREEN_WIDTH - textWidth) / 2;
|
||||||
|
display->drawString(nameX, getTextPositions(display)[line++], device_role);
|
||||||
|
|
||||||
|
// === Third Row: Radio Preset ===
|
||||||
auto mode = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false, config.lora.use_preset);
|
auto mode = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false, config.lora.use_preset);
|
||||||
|
|
||||||
char regionradiopreset[25];
|
char regionradiopreset[25];
|
||||||
const char *region = myRegion ? myRegion->name : NULL;
|
const char *region = myRegion ? myRegion->name : NULL;
|
||||||
if (region != nullptr) {
|
if (region != nullptr) {
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
snprintf(regionradiopreset, sizeof(regionradiopreset), "%s", region);
|
||||||
|
#else
|
||||||
snprintf(regionradiopreset, sizeof(regionradiopreset), "%s/%s", region, mode);
|
snprintf(regionradiopreset, sizeof(regionradiopreset), "%s/%s", region, mode);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
textWidth = display->getStringWidth(regionradiopreset);
|
textWidth = display->getStringWidth(regionradiopreset);
|
||||||
nameX = (SCREEN_WIDTH - textWidth) / 2;
|
nameX = (SCREEN_WIDTH - textWidth) / 2;
|
||||||
display->drawString(nameX, getTextPositions(display)[line++], regionradiopreset);
|
display->drawString(nameX, getTextPositions(display)[line++], regionradiopreset);
|
||||||
|
|
||||||
// === Third Row: Frequency / ChanNum ===
|
// === Fourth Row: Frequency / ChanNum ===
|
||||||
char frequencyslot[35];
|
char frequencyslot[35];
|
||||||
char freqStr[16];
|
char freqStr[16];
|
||||||
float freq = RadioLibInterface::instance->getFreq();
|
float freq = RadioLibInterface::instance->getFreq();
|
||||||
snprintf(freqStr, sizeof(freqStr), "%.3f", freq);
|
snprintf(freqStr, sizeof(freqStr), "%.3f", freq);
|
||||||
if (config.lora.channel_num == 0) {
|
if (config.lora.channel_num == 0) {
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
snprintf(frequencyslot, sizeof(frequencyslot), "%sMHz", freqStr);
|
||||||
|
#else
|
||||||
snprintf(frequencyslot, sizeof(frequencyslot), "Freq: %sMHz", freqStr);
|
snprintf(frequencyslot, sizeof(frequencyslot), "Freq: %sMHz", freqStr);
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
snprintf(frequencyslot, sizeof(frequencyslot), "%sMHz (%d)", freqStr, config.lora.channel_num);
|
||||||
|
#else
|
||||||
snprintf(frequencyslot, sizeof(frequencyslot), "Freq/Ch: %sMHz (%d)", freqStr, config.lora.channel_num);
|
snprintf(frequencyslot, sizeof(frequencyslot), "Freq/Ch: %sMHz (%d)", freqStr, config.lora.channel_num);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
size_t len = strlen(frequencyslot);
|
size_t len = strlen(frequencyslot);
|
||||||
if (len >= 4 && strcmp(frequencyslot + len - 4, " (0)") == 0) {
|
if (len >= 4 && strcmp(frequencyslot + len - 4, " (0)") == 0) {
|
||||||
@@ -420,7 +445,8 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
|
|||||||
nameX = (SCREEN_WIDTH - textWidth) / 2;
|
nameX = (SCREEN_WIDTH - textWidth) / 2;
|
||||||
display->drawString(nameX, getTextPositions(display)[line++], frequencyslot);
|
display->drawString(nameX, getTextPositions(display)[line++], frequencyslot);
|
||||||
|
|
||||||
// === Fourth Row: Channel Utilization ===
|
#if !defined(M5STACK_UNITC6L)
|
||||||
|
// === Fifth Row: Channel Utilization ===
|
||||||
const char *chUtil = "ChUtil:";
|
const char *chUtil = "ChUtil:";
|
||||||
char chUtilPercentage[10];
|
char chUtilPercentage[10];
|
||||||
snprintf(chUtilPercentage, sizeof(chUtilPercentage), "%2.0f%%", airTime->channelUtilizationPercent());
|
snprintf(chUtilPercentage, sizeof(chUtilPercentage), "%2.0f%%", airTime->channelUtilizationPercent());
|
||||||
@@ -437,7 +463,7 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
|
|||||||
int total_line_content_width = (chUtil_x + chutil_bar_width + display->getStringWidth(chUtilPercentage) + extraoffset) / 2;
|
int total_line_content_width = (chUtil_x + chutil_bar_width + display->getStringWidth(chUtilPercentage) + extraoffset) / 2;
|
||||||
int starting_position = centerofscreen - total_line_content_width;
|
int starting_position = centerofscreen - total_line_content_width;
|
||||||
|
|
||||||
display->drawString(starting_position, getTextPositions(display)[line++], chUtil);
|
display->drawString(starting_position, getTextPositions(display)[line], chUtil);
|
||||||
|
|
||||||
// Force 56% or higher to show a full 100% bar, text would still show related percent.
|
// Force 56% or higher to show a full 100% bar, text would still show related percent.
|
||||||
if (chutil_percent >= 61) {
|
if (chutil_percent >= 61) {
|
||||||
@@ -474,14 +500,15 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
|
|||||||
display->fillRect(starting_position + chUtil_x, chUtil_y, fillRight, chutil_bar_height);
|
display->fillRect(starting_position + chUtil_x, chUtil_y, fillRight, chutil_bar_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
display->drawString(starting_position + chUtil_x + chutil_bar_width + extraoffset, getTextPositions(display)[4],
|
display->drawString(starting_position + chUtil_x + chutil_bar_width + extraoffset, getTextPositions(display)[line++],
|
||||||
chUtilPercentage);
|
chUtilPercentage);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// ****************************
|
// ****************************
|
||||||
// * System Screen *
|
// * System Screen *
|
||||||
// ****************************
|
// ****************************
|
||||||
void drawMemoryUsage(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||||
{
|
{
|
||||||
display->clear();
|
display->clear();
|
||||||
display->setFont(FONT_SMALL);
|
display->setFont(FONT_SMALL);
|
||||||
@@ -501,8 +528,11 @@ void drawMemoryUsage(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
|
|||||||
#ifdef USE_EINK
|
#ifdef USE_EINK
|
||||||
barsOffset -= 12;
|
barsOffset -= 12;
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
const int barX = x + 45 + barsOffset;
|
||||||
|
#else
|
||||||
const int barX = x + 40 + barsOffset;
|
const int barX = x + 40 + barsOffset;
|
||||||
|
#endif
|
||||||
auto drawUsageRow = [&](const char *label, uint32_t used, uint32_t total, bool isHeap = false) {
|
auto drawUsageRow = [&](const char *label, uint32_t used, uint32_t total, bool isHeap = false) {
|
||||||
if (total == 0)
|
if (total == 0)
|
||||||
return;
|
return;
|
||||||
@@ -527,7 +557,7 @@ void drawMemoryUsage(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
|
|||||||
// Label
|
// Label
|
||||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||||
display->drawString(labelX, getTextPositions(display)[line], label);
|
display->drawString(labelX, getTextPositions(display)[line], label);
|
||||||
|
#if !defined(M5STACK_UNITC6L)
|
||||||
// Bar
|
// Bar
|
||||||
int barY = getTextPositions(display)[line] + (FONT_HEIGHT_SMALL - barHeight) / 2;
|
int barY = getTextPositions(display)[line] + (FONT_HEIGHT_SMALL - barHeight) / 2;
|
||||||
display->setColor(WHITE);
|
display->setColor(WHITE);
|
||||||
@@ -535,7 +565,7 @@ void drawMemoryUsage(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
|
|||||||
|
|
||||||
display->fillRect(barX, barY, fillWidth, barHeight);
|
display->fillRect(barX, barY, fillWidth, barHeight);
|
||||||
display->setColor(WHITE);
|
display->setColor(WHITE);
|
||||||
|
#endif
|
||||||
// Value string
|
// Value string
|
||||||
display->setTextAlignment(TEXT_ALIGN_RIGHT);
|
display->setTextAlignment(TEXT_ALIGN_RIGHT);
|
||||||
display->drawString(SCREEN_WIDTH - 2, getTextPositions(display)[line], combinedStr);
|
display->drawString(SCREEN_WIDTH - 2, getTextPositions(display)[line], combinedStr);
|
||||||
@@ -588,10 +618,16 @@ void drawMemoryUsage(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
|
|||||||
line += 1;
|
line += 1;
|
||||||
}
|
}
|
||||||
line += 1;
|
line += 1;
|
||||||
|
|
||||||
char appversionstr[35];
|
char appversionstr[35];
|
||||||
snprintf(appversionstr, sizeof(appversionstr), "Ver: %s", optstr(APP_VERSION));
|
snprintf(appversionstr, sizeof(appversionstr), "Ver: %s", optstr(APP_VERSION));
|
||||||
char appversionstr_formatted[40];
|
char appversionstr_formatted[40];
|
||||||
char *lastDot = strrchr(appversionstr, '.');
|
char *lastDot = strrchr(appversionstr, '.');
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
if (lastDot != nullptr) {
|
||||||
|
*lastDot = '\0'; // truncate string
|
||||||
|
}
|
||||||
|
#else
|
||||||
if (lastDot) {
|
if (lastDot) {
|
||||||
size_t prefixLen = lastDot - appversionstr;
|
size_t prefixLen = lastDot - appversionstr;
|
||||||
strncpy(appversionstr_formatted, appversionstr, prefixLen);
|
strncpy(appversionstr_formatted, appversionstr, prefixLen);
|
||||||
@@ -602,10 +638,12 @@ void drawMemoryUsage(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
|
|||||||
strncpy(appversionstr, appversionstr_formatted, sizeof(appversionstr) - 1);
|
strncpy(appversionstr, appversionstr_formatted, sizeof(appversionstr) - 1);
|
||||||
appversionstr[sizeof(appversionstr) - 1] = '\0';
|
appversionstr[sizeof(appversionstr) - 1] = '\0';
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
int textWidth = display->getStringWidth(appversionstr);
|
int textWidth = display->getStringWidth(appversionstr);
|
||||||
int nameX = (SCREEN_WIDTH - textWidth) / 2;
|
int nameX = (SCREEN_WIDTH - textWidth) / 2;
|
||||||
display->drawString(nameX, getTextPositions(display)[line], appversionstr);
|
|
||||||
|
|
||||||
|
display->drawString(nameX, getTextPositions(display)[line], appversionstr);
|
||||||
|
#if !defined(M5STACK_UNITC6L)
|
||||||
if (SCREEN_HEIGHT > 64 || (SCREEN_HEIGHT <= 64 && line < 4)) { // Only show uptime if the screen can show it
|
if (SCREEN_HEIGHT > 64 || (SCREEN_HEIGHT <= 64 && line < 4)) { // Only show uptime if the screen can show it
|
||||||
line += 1;
|
line += 1;
|
||||||
char uptimeStr[32] = "";
|
char uptimeStr[32] = "";
|
||||||
@@ -624,6 +662,34 @@ void drawMemoryUsage(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
|
|||||||
nameX = (SCREEN_WIDTH - textWidth) / 2;
|
nameX = (SCREEN_WIDTH - textWidth) / 2;
|
||||||
display->drawString(nameX, getTextPositions(display)[line], uptimeStr);
|
display->drawString(nameX, getTextPositions(display)[line], uptimeStr);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// ****************************
|
||||||
|
// * Chirpy Screen *
|
||||||
|
// ****************************
|
||||||
|
void drawChirpy(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||||
|
{
|
||||||
|
display->clear();
|
||||||
|
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||||
|
display->setFont(FONT_SMALL);
|
||||||
|
int line = 1;
|
||||||
|
int iconX = SCREEN_WIDTH - chirpy_width - (chirpy_width / 3);
|
||||||
|
int iconY = (SCREEN_HEIGHT - chirpy_height) / 2;
|
||||||
|
int textX_offset = 10;
|
||||||
|
if (isHighResolution) {
|
||||||
|
iconX = SCREEN_WIDTH - chirpy_width_hirez - (chirpy_width_hirez / 3);
|
||||||
|
iconY = (SCREEN_HEIGHT - chirpy_height_hirez) / 2;
|
||||||
|
textX_offset = textX_offset * 4;
|
||||||
|
display->drawXbm(iconX, iconY, chirpy_width_hirez, chirpy_height_hirez, chirpy_hirez);
|
||||||
|
} else {
|
||||||
|
display->drawXbm(iconX, iconY, chirpy_width, chirpy_height, chirpy);
|
||||||
|
}
|
||||||
|
|
||||||
|
int textX = (display->getWidth() / 2) - textX_offset - (display->getStringWidth("Hello") / 2);
|
||||||
|
display->drawString(textX, getTextPositions(display)[line++], "Hello");
|
||||||
|
textX = (display->getWidth() / 2) - textX_offset - (display->getStringWidth("World!") / 2);
|
||||||
|
display->drawString(textX, getTextPositions(display)[line++], "World!");
|
||||||
}
|
}
|
||||||
} // namespace DebugRenderer
|
} // namespace DebugRenderer
|
||||||
} // namespace graphics
|
} // namespace graphics
|
||||||
|
|||||||
@@ -31,8 +31,11 @@ void drawDebugInfoWiFiTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state
|
|||||||
// LoRa information display
|
// LoRa information display
|
||||||
void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||||
|
|
||||||
// Memory screen display
|
// System screen display
|
||||||
void drawMemoryUsage(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||||
|
|
||||||
|
// Chirpy screen display
|
||||||
|
void drawChirpy(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||||
} // namespace DebugRenderer
|
} // namespace DebugRenderer
|
||||||
|
|
||||||
} // namespace graphics
|
} // namespace graphics
|
||||||
|
|||||||
@@ -26,6 +26,26 @@ menuHandler::screenMenus menuHandler::menuQueue = menu_none;
|
|||||||
bool test_enabled = false;
|
bool test_enabled = false;
|
||||||
uint8_t test_count = 0;
|
uint8_t test_count = 0;
|
||||||
|
|
||||||
|
void menuHandler::loraMenu()
|
||||||
|
{
|
||||||
|
static const char *optionsArray[] = {"Back", "Region Picker", "Device Role"};
|
||||||
|
enum optionsNumbers { Back = 0, lora_picker = 1, device_role_picker = 2 };
|
||||||
|
BannerOverlayOptions bannerOptions;
|
||||||
|
bannerOptions.message = "LoRa Actions";
|
||||||
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
|
bannerOptions.optionsCount = 3;
|
||||||
|
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||||
|
if (selected == Back) {
|
||||||
|
// No action
|
||||||
|
} else if (selected == lora_picker) {
|
||||||
|
menuHandler::menuQueue = menuHandler::lora_picker;
|
||||||
|
} else if (selected == device_role_picker) {
|
||||||
|
menuHandler::menuQueue = menuHandler::device_role_picker;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
screen->showOverlayBanner(bannerOptions);
|
||||||
|
}
|
||||||
|
|
||||||
void menuHandler::OnboardMessage()
|
void menuHandler::OnboardMessage()
|
||||||
{
|
{
|
||||||
static const char *optionsArray[] = {"OK", "Got it!"};
|
static const char *optionsArray[] = {"OK", "Got it!"};
|
||||||
@@ -79,7 +99,11 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
|
|||||||
"NP_865",
|
"NP_865",
|
||||||
"BR_902"};
|
"BR_902"};
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
bannerOptions.message = "LoRa Region";
|
||||||
|
#else
|
||||||
bannerOptions.message = "Set the LoRa region";
|
bannerOptions.message = "Set the LoRa region";
|
||||||
|
#endif
|
||||||
bannerOptions.durationMs = duration;
|
bannerOptions.durationMs = duration;
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
bannerOptions.optionsCount = 27;
|
bannerOptions.optionsCount = 27;
|
||||||
@@ -119,6 +143,40 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
|
|||||||
screen->showOverlayBanner(bannerOptions);
|
screen->showOverlayBanner(bannerOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void menuHandler::DeviceRolePicker()
|
||||||
|
{
|
||||||
|
static const char *optionsArray[] = {"Back", "Client", "Client Mute", "Lost and Found", "Tracker"};
|
||||||
|
enum optionsNumbers {
|
||||||
|
Back = 0,
|
||||||
|
devicerole_client = 1,
|
||||||
|
devicerole_clientmute = 2,
|
||||||
|
devicerole_lostandfound = 3,
|
||||||
|
devicerole_tracker = 4
|
||||||
|
};
|
||||||
|
BannerOverlayOptions bannerOptions;
|
||||||
|
bannerOptions.message = "Device Role";
|
||||||
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
|
bannerOptions.optionsCount = 5;
|
||||||
|
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||||
|
if (selected == Back) {
|
||||||
|
menuHandler::menuQueue = menuHandler::lora_Menu;
|
||||||
|
screen->runNow();
|
||||||
|
return;
|
||||||
|
} else if (selected == devicerole_client) {
|
||||||
|
config.device.role = meshtastic_Config_DeviceConfig_Role_CLIENT;
|
||||||
|
} else if (selected == devicerole_clientmute) {
|
||||||
|
config.device.role = meshtastic_Config_DeviceConfig_Role_CLIENT_MUTE;
|
||||||
|
} else if (selected == devicerole_lostandfound) {
|
||||||
|
config.device.role = meshtastic_Config_DeviceConfig_Role_LOST_AND_FOUND;
|
||||||
|
} else if (selected == devicerole_tracker) {
|
||||||
|
config.device.role = meshtastic_Config_DeviceConfig_Role_TRACKER;
|
||||||
|
}
|
||||||
|
service->reloadConfig(SEGMENT_CONFIG);
|
||||||
|
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
|
||||||
|
};
|
||||||
|
screen->showOverlayBanner(bannerOptions);
|
||||||
|
}
|
||||||
|
|
||||||
void menuHandler::TwelveHourPicker()
|
void menuHandler::TwelveHourPicker()
|
||||||
{
|
{
|
||||||
static const char *optionsArray[] = {"Back", "12-hour", "24-hour"};
|
static const char *optionsArray[] = {"Back", "12-hour", "24-hour"};
|
||||||
@@ -260,7 +318,11 @@ void menuHandler::TZPicker()
|
|||||||
|
|
||||||
void menuHandler::clockMenu()
|
void menuHandler::clockMenu()
|
||||||
{
|
{
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
static const char *optionsArray[] = {"Back", "Time Format", "Timezone"};
|
||||||
|
#else
|
||||||
static const char *optionsArray[] = {"Back", "Clock Face", "Time Format", "Timezone"};
|
static const char *optionsArray[] = {"Back", "Clock Face", "Time Format", "Timezone"};
|
||||||
|
#endif
|
||||||
enum optionsNumbers { Back = 0, Clock = 1, Time = 2, Timezone = 3 };
|
enum optionsNumbers { Back = 0, Clock = 1, Time = 2, Timezone = 3 };
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
bannerOptions.message = "Clock Action";
|
bannerOptions.message = "Clock Action";
|
||||||
@@ -284,8 +346,11 @@ void menuHandler::clockMenu()
|
|||||||
void menuHandler::messageResponseMenu()
|
void menuHandler::messageResponseMenu()
|
||||||
{
|
{
|
||||||
enum optionsNumbers { Back = 0, Dismiss = 1, Preset = 2, Freetext = 3, Aloud = 4, enumEnd = 5 };
|
enum optionsNumbers { Back = 0, Dismiss = 1, Preset = 2, Freetext = 3, Aloud = 4, enumEnd = 5 };
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
static const char *optionsArray[enumEnd] = {"Back", "Dismiss", "Reply Preset"};
|
||||||
|
#else
|
||||||
static const char *optionsArray[enumEnd] = {"Back", "Dismiss", "Reply via Preset"};
|
static const char *optionsArray[enumEnd] = {"Back", "Dismiss", "Reply via Preset"};
|
||||||
|
#endif
|
||||||
static int optionsEnumArray[enumEnd] = {Back, Dismiss, Preset};
|
static int optionsEnumArray[enumEnd] = {Back, Dismiss, Preset};
|
||||||
int options = 3;
|
int options = 3;
|
||||||
|
|
||||||
@@ -299,13 +364,17 @@ void menuHandler::messageResponseMenu()
|
|||||||
optionsEnumArray[options++] = Aloud;
|
optionsEnumArray[options++] = Aloud;
|
||||||
#endif
|
#endif
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
bannerOptions.message = "Message";
|
||||||
|
#else
|
||||||
bannerOptions.message = "Message Action";
|
bannerOptions.message = "Message Action";
|
||||||
|
#endif
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
||||||
bannerOptions.optionsCount = options;
|
bannerOptions.optionsCount = options;
|
||||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||||
if (selected == Dismiss) {
|
if (selected == Dismiss) {
|
||||||
screen->dismissCurrentFrame();
|
screen->hideCurrentFrame();
|
||||||
} else if (selected == Preset) {
|
} else if (selected == Preset) {
|
||||||
if (devicestate.rx_text_message.to == NODENUM_BROADCAST) {
|
if (devicestate.rx_text_message.to == NODENUM_BROADCAST) {
|
||||||
cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST, devicestate.rx_text_message.channel);
|
cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST, devicestate.rx_text_message.channel);
|
||||||
@@ -346,10 +415,17 @@ void menuHandler::homeBaseMenu()
|
|||||||
optionsArray[options] = "Sleep Screen";
|
optionsArray[options] = "Sleep Screen";
|
||||||
optionsEnumArray[options++] = Sleep;
|
optionsEnumArray[options++] = Sleep;
|
||||||
#endif
|
#endif
|
||||||
|
if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
|
||||||
optionsArray[options] = "Send Position";
|
optionsArray[options] = "Send Position";
|
||||||
|
} else {
|
||||||
|
optionsArray[options] = "Send Node Info";
|
||||||
|
}
|
||||||
optionsEnumArray[options++] = Position;
|
optionsEnumArray[options++] = Position;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
optionsArray[options] = "New Preset";
|
||||||
|
#else
|
||||||
optionsArray[options] = "New Preset Msg";
|
optionsArray[options] = "New Preset Msg";
|
||||||
|
#endif
|
||||||
optionsEnumArray[options++] = Preset;
|
optionsEnumArray[options++] = Preset;
|
||||||
if (kb_found) {
|
if (kb_found) {
|
||||||
optionsArray[options] = "New Freetext Msg";
|
optionsArray[options] = "New Freetext Msg";
|
||||||
@@ -357,7 +433,11 @@ void menuHandler::homeBaseMenu()
|
|||||||
}
|
}
|
||||||
|
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
bannerOptions.message = "Home";
|
||||||
|
#else
|
||||||
bannerOptions.message = "Home Action";
|
bannerOptions.message = "Home Action";
|
||||||
|
#endif
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
||||||
bannerOptions.optionsCount = options;
|
bannerOptions.optionsCount = options;
|
||||||
@@ -396,6 +476,11 @@ void menuHandler::homeBaseMenu()
|
|||||||
screen->showOverlayBanner(bannerOptions);
|
screen->showOverlayBanner(bannerOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void menuHandler::textMessageMenu()
|
||||||
|
{
|
||||||
|
cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST);
|
||||||
|
}
|
||||||
|
|
||||||
void menuHandler::textMessageBaseMenu()
|
void menuHandler::textMessageBaseMenu()
|
||||||
{
|
{
|
||||||
enum optionsNumbers { Back, Preset, Freetext, enumEnd };
|
enum optionsNumbers { Back, Preset, Freetext, enumEnd };
|
||||||
@@ -427,7 +512,7 @@ void menuHandler::textMessageBaseMenu()
|
|||||||
|
|
||||||
void menuHandler::systemBaseMenu()
|
void menuHandler::systemBaseMenu()
|
||||||
{
|
{
|
||||||
enum optionsNumbers { Back, Notifications, ScreenOptions, Bluetooth, PowerMenu, Test, enumEnd };
|
enum optionsNumbers { Back, Notifications, ScreenOptions, Bluetooth, PowerMenu, FrameToggles, Test, enumEnd };
|
||||||
static const char *optionsArray[enumEnd] = {"Back"};
|
static const char *optionsArray[enumEnd] = {"Back"};
|
||||||
static int optionsEnumArray[enumEnd] = {Back};
|
static int optionsEnumArray[enumEnd] = {Back};
|
||||||
int options = 1;
|
int options = 1;
|
||||||
@@ -440,10 +525,19 @@ void menuHandler::systemBaseMenu()
|
|||||||
optionsEnumArray[options++] = ScreenOptions;
|
optionsEnumArray[options++] = ScreenOptions;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
optionsArray[options] = "Frame Visiblity Toggle";
|
||||||
|
optionsEnumArray[options++] = FrameToggles;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
optionsArray[options] = "Bluetooth";
|
||||||
|
#else
|
||||||
optionsArray[options] = "Bluetooth Toggle";
|
optionsArray[options] = "Bluetooth Toggle";
|
||||||
|
#endif
|
||||||
optionsEnumArray[options++] = Bluetooth;
|
optionsEnumArray[options++] = Bluetooth;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
optionsArray[options] = "Power";
|
||||||
|
#else
|
||||||
optionsArray[options] = "Reboot/Shutdown";
|
optionsArray[options] = "Reboot/Shutdown";
|
||||||
|
#endif
|
||||||
optionsEnumArray[options++] = PowerMenu;
|
optionsEnumArray[options++] = PowerMenu;
|
||||||
|
|
||||||
if (test_enabled) {
|
if (test_enabled) {
|
||||||
@@ -452,7 +546,11 @@ void menuHandler::systemBaseMenu()
|
|||||||
}
|
}
|
||||||
|
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
bannerOptions.message = "System";
|
||||||
|
#else
|
||||||
bannerOptions.message = "System Action";
|
bannerOptions.message = "System Action";
|
||||||
|
#endif
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
bannerOptions.optionsCount = options;
|
bannerOptions.optionsCount = options;
|
||||||
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
||||||
@@ -466,6 +564,9 @@ void menuHandler::systemBaseMenu()
|
|||||||
} else if (selected == PowerMenu) {
|
} else if (selected == PowerMenu) {
|
||||||
menuHandler::menuQueue = menuHandler::power_menu;
|
menuHandler::menuQueue = menuHandler::power_menu;
|
||||||
screen->runNow();
|
screen->runNow();
|
||||||
|
} else if (selected == FrameToggles) {
|
||||||
|
menuHandler::menuQueue = menuHandler::FrameToggles;
|
||||||
|
screen->runNow();
|
||||||
} else if (selected == Test) {
|
} else if (selected == Test) {
|
||||||
menuHandler::menuQueue = menuHandler::test_menu;
|
menuHandler::menuQueue = menuHandler::test_menu;
|
||||||
screen->runNow();
|
screen->runNow();
|
||||||
@@ -485,7 +586,11 @@ void menuHandler::systemBaseMenu()
|
|||||||
void menuHandler::favoriteBaseMenu()
|
void menuHandler::favoriteBaseMenu()
|
||||||
{
|
{
|
||||||
enum optionsNumbers { Back, Preset, Freetext, Remove, TraceRoute, enumEnd };
|
enum optionsNumbers { Back, Preset, Freetext, Remove, TraceRoute, enumEnd };
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
static const char *optionsArray[enumEnd] = {"Back", "New Preset"};
|
||||||
|
#else
|
||||||
static const char *optionsArray[enumEnd] = {"Back", "New Preset Msg"};
|
static const char *optionsArray[enumEnd] = {"Back", "New Preset Msg"};
|
||||||
|
#endif
|
||||||
static int optionsEnumArray[enumEnd] = {Back, Preset};
|
static int optionsEnumArray[enumEnd] = {Back, Preset};
|
||||||
int options = 2;
|
int options = 2;
|
||||||
|
|
||||||
@@ -493,13 +598,19 @@ void menuHandler::favoriteBaseMenu()
|
|||||||
optionsArray[options] = "New Freetext Msg";
|
optionsArray[options] = "New Freetext Msg";
|
||||||
optionsEnumArray[options++] = Freetext;
|
optionsEnumArray[options++] = Freetext;
|
||||||
}
|
}
|
||||||
|
#if !defined(M5STACK_UNITC6L)
|
||||||
optionsArray[options] = "Trace Route";
|
optionsArray[options] = "Trace Route";
|
||||||
optionsEnumArray[options++] = TraceRoute;
|
optionsEnumArray[options++] = TraceRoute;
|
||||||
|
#endif
|
||||||
optionsArray[options] = "Remove Favorite";
|
optionsArray[options] = "Remove Favorite";
|
||||||
optionsEnumArray[options++] = Remove;
|
optionsEnumArray[options++] = Remove;
|
||||||
|
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
bannerOptions.message = "Favorites";
|
||||||
|
#else
|
||||||
bannerOptions.message = "Favorites Action";
|
bannerOptions.message = "Favorites Action";
|
||||||
|
#endif
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
||||||
bannerOptions.optionsCount = options;
|
bannerOptions.optionsCount = options;
|
||||||
@@ -522,16 +633,17 @@ void menuHandler::favoriteBaseMenu()
|
|||||||
|
|
||||||
void menuHandler::positionBaseMenu()
|
void menuHandler::positionBaseMenu()
|
||||||
{
|
{
|
||||||
enum optionsNumbers { Back, GPSToggle, CompassMenu, CompassCalibrate, enumEnd };
|
enum optionsNumbers { Back, GPSToggle, GPSFormat, CompassMenu, CompassCalibrate, enumEnd };
|
||||||
|
|
||||||
static const char *optionsArray[enumEnd] = {"Back", "GPS Toggle", "Compass"};
|
static const char *optionsArray[enumEnd] = {"Back", "GPS Toggle", "GPS Format", "Compass"};
|
||||||
static int optionsEnumArray[enumEnd] = {Back, GPSToggle, CompassMenu};
|
static int optionsEnumArray[enumEnd] = {Back, GPSToggle, GPSFormat, CompassMenu};
|
||||||
int options = 3;
|
int options = 4;
|
||||||
|
|
||||||
if (accelerometerThread) {
|
if (accelerometerThread) {
|
||||||
optionsArray[options] = "Compass Calibrate";
|
optionsArray[options] = "Compass Calibrate";
|
||||||
optionsEnumArray[options++] = CompassCalibrate;
|
optionsEnumArray[options++] = CompassCalibrate;
|
||||||
}
|
}
|
||||||
|
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
bannerOptions.message = "Position Action";
|
bannerOptions.message = "Position Action";
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
@@ -541,6 +653,9 @@ void menuHandler::positionBaseMenu()
|
|||||||
if (selected == GPSToggle) {
|
if (selected == GPSToggle) {
|
||||||
menuQueue = gps_toggle_menu;
|
menuQueue = gps_toggle_menu;
|
||||||
screen->runNow();
|
screen->runNow();
|
||||||
|
} else if (selected == GPSFormat) {
|
||||||
|
menuQueue = gps_format_menu;
|
||||||
|
screen->runNow();
|
||||||
} else if (selected == CompassMenu) {
|
} else if (selected == CompassMenu) {
|
||||||
menuQueue = compass_point_north_menu;
|
menuQueue = compass_point_north_menu;
|
||||||
screen->runNow();
|
screen->runNow();
|
||||||
@@ -554,11 +669,19 @@ void menuHandler::positionBaseMenu()
|
|||||||
void menuHandler::nodeListMenu()
|
void menuHandler::nodeListMenu()
|
||||||
{
|
{
|
||||||
enum optionsNumbers { Back, Favorite, TraceRoute, Verify, Reset, enumEnd };
|
enum optionsNumbers { Back, Favorite, TraceRoute, Verify, Reset, enumEnd };
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
static const char *optionsArray[] = {"Back", "Add Favorite", "Reset Node"};
|
||||||
|
#else
|
||||||
static const char *optionsArray[] = {"Back", "Add Favorite", "Trace Route", "Key Verification", "Reset NodeDB"};
|
static const char *optionsArray[] = {"Back", "Add Favorite", "Trace Route", "Key Verification", "Reset NodeDB"};
|
||||||
|
#endif
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
bannerOptions.message = "Node Action";
|
bannerOptions.message = "Node Action";
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
bannerOptions.optionsCount = 3;
|
||||||
|
#else
|
||||||
bannerOptions.optionsCount = 5;
|
bannerOptions.optionsCount = 5;
|
||||||
|
#endif
|
||||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||||
if (selected == Favorite) {
|
if (selected == Favorite) {
|
||||||
menuQueue = add_favorite;
|
menuQueue = add_favorite;
|
||||||
@@ -659,13 +782,62 @@ void menuHandler::GPSToggleMenu()
|
|||||||
bannerOptions.InitialSelected = config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED ? 1 : 2;
|
bannerOptions.InitialSelected = config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED ? 1 : 2;
|
||||||
screen->showOverlayBanner(bannerOptions);
|
screen->showOverlayBanner(bannerOptions);
|
||||||
}
|
}
|
||||||
|
void menuHandler::GPSFormatMenu()
|
||||||
|
{
|
||||||
|
|
||||||
|
static const char *optionsArray[] = {"Back",
|
||||||
|
isHighResolution ? "Decimal Degrees" : "DEC",
|
||||||
|
isHighResolution ? "Degrees Minutes Seconds" : "DMS",
|
||||||
|
isHighResolution ? "Universal Transverse Mercator" : "UTM",
|
||||||
|
isHighResolution ? "Military Grid Reference System" : "MGRS",
|
||||||
|
isHighResolution ? "Open Location Code" : "OLC",
|
||||||
|
isHighResolution ? "Ordnance Survey Grid Ref" : "OSGR",
|
||||||
|
isHighResolution ? "Maidenhead Locator" : "MLS"};
|
||||||
|
BannerOverlayOptions bannerOptions;
|
||||||
|
bannerOptions.message = "GPS Format";
|
||||||
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
|
bannerOptions.optionsCount = 8;
|
||||||
|
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||||
|
if (selected == 1) {
|
||||||
|
uiconfig.gps_format = meshtastic_DeviceUIConfig_GpsCoordinateFormat_DEC;
|
||||||
|
service->reloadConfig(SEGMENT_CONFIG);
|
||||||
|
} else if (selected == 2) {
|
||||||
|
uiconfig.gps_format = meshtastic_DeviceUIConfig_GpsCoordinateFormat_DMS;
|
||||||
|
service->reloadConfig(SEGMENT_CONFIG);
|
||||||
|
} else if (selected == 3) {
|
||||||
|
uiconfig.gps_format = meshtastic_DeviceUIConfig_GpsCoordinateFormat_UTM;
|
||||||
|
service->reloadConfig(SEGMENT_CONFIG);
|
||||||
|
} else if (selected == 4) {
|
||||||
|
uiconfig.gps_format = meshtastic_DeviceUIConfig_GpsCoordinateFormat_MGRS;
|
||||||
|
service->reloadConfig(SEGMENT_CONFIG);
|
||||||
|
} else if (selected == 5) {
|
||||||
|
uiconfig.gps_format = meshtastic_DeviceUIConfig_GpsCoordinateFormat_OLC;
|
||||||
|
service->reloadConfig(SEGMENT_CONFIG);
|
||||||
|
} else if (selected == 6) {
|
||||||
|
uiconfig.gps_format = meshtastic_DeviceUIConfig_GpsCoordinateFormat_OSGR;
|
||||||
|
service->reloadConfig(SEGMENT_CONFIG);
|
||||||
|
} else if (selected == 7) {
|
||||||
|
uiconfig.gps_format = meshtastic_DeviceUIConfig_GpsCoordinateFormat_MLS;
|
||||||
|
service->reloadConfig(SEGMENT_CONFIG);
|
||||||
|
} else {
|
||||||
|
menuQueue = position_base_menu;
|
||||||
|
screen->runNow();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
bannerOptions.InitialSelected = uiconfig.gps_format + 1;
|
||||||
|
screen->showOverlayBanner(bannerOptions);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void menuHandler::BluetoothToggleMenu()
|
void menuHandler::BluetoothToggleMenu()
|
||||||
{
|
{
|
||||||
static const char *optionsArray[] = {"Back", "Enabled", "Disabled"};
|
static const char *optionsArray[] = {"Back", "Enabled", "Disabled"};
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
bannerOptions.message = "Bluetooth";
|
||||||
|
#else
|
||||||
bannerOptions.message = "Toggle Bluetooth";
|
bannerOptions.message = "Toggle Bluetooth";
|
||||||
|
#endif
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
bannerOptions.optionsCount = 3;
|
bannerOptions.optionsCount = 3;
|
||||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||||
@@ -857,7 +1029,11 @@ void menuHandler::rebootMenu()
|
|||||||
{
|
{
|
||||||
static const char *optionsArray[] = {"Back", "Confirm"};
|
static const char *optionsArray[] = {"Back", "Confirm"};
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
bannerOptions.message = "Reboot";
|
||||||
|
#else
|
||||||
bannerOptions.message = "Reboot Device?";
|
bannerOptions.message = "Reboot Device?";
|
||||||
|
#endif
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
bannerOptions.optionsCount = 2;
|
bannerOptions.optionsCount = 2;
|
||||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||||
@@ -877,7 +1053,11 @@ void menuHandler::shutdownMenu()
|
|||||||
{
|
{
|
||||||
static const char *optionsArray[] = {"Back", "Confirm"};
|
static const char *optionsArray[] = {"Back", "Confirm"};
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
bannerOptions.message = "Shutdown";
|
||||||
|
#else
|
||||||
bannerOptions.message = "Shutdown Device?";
|
bannerOptions.message = "Shutdown Device?";
|
||||||
|
#endif
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
bannerOptions.optionsCount = 2;
|
bannerOptions.optionsCount = 2;
|
||||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||||
@@ -894,7 +1074,12 @@ void menuHandler::shutdownMenu()
|
|||||||
|
|
||||||
void menuHandler::addFavoriteMenu()
|
void menuHandler::addFavoriteMenu()
|
||||||
{
|
{
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
screen->showNodePicker("Node Favorite", 30000, [](uint32_t nodenum) -> void {
|
||||||
|
#else
|
||||||
screen->showNodePicker("Node To Favorite", 30000, [](uint32_t nodenum) -> void {
|
screen->showNodePicker("Node To Favorite", 30000, [](uint32_t nodenum) -> void {
|
||||||
|
|
||||||
|
#endif
|
||||||
LOG_WARN("Nodenum: %u", nodenum);
|
LOG_WARN("Nodenum: %u", nodenum);
|
||||||
nodeDB->set_favorite(true, nodenum);
|
nodeDB->set_favorite(true, nodenum);
|
||||||
screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
|
screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
|
||||||
@@ -937,16 +1122,33 @@ void menuHandler::traceRouteMenu()
|
|||||||
void menuHandler::testMenu()
|
void menuHandler::testMenu()
|
||||||
{
|
{
|
||||||
|
|
||||||
static const char *optionsArray[] = {"Back", "Number Picker"};
|
enum optionsNumbers { Back, NumberPicker, ShowChirpy };
|
||||||
|
static const char *optionsArray[4] = {"Back"};
|
||||||
|
static int optionsEnumArray[4] = {Back};
|
||||||
|
int options = 1;
|
||||||
|
|
||||||
|
optionsArray[options] = "Number Picker";
|
||||||
|
optionsEnumArray[options++] = NumberPicker;
|
||||||
|
|
||||||
|
optionsArray[options] = screen->isFrameHidden("chirpy") ? "Show Chirpy" : "Hide Chirpy";
|
||||||
|
optionsEnumArray[options++] = ShowChirpy;
|
||||||
|
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
std::string message = "Test to Run?\n";
|
bannerOptions.message = "Hidden Test Menu";
|
||||||
bannerOptions.message = message.c_str();
|
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
bannerOptions.optionsCount = 2;
|
bannerOptions.optionsCount = options;
|
||||||
|
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
||||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||||
if (selected == 1) {
|
if (selected == NumberPicker) {
|
||||||
menuQueue = number_test;
|
menuQueue = number_test;
|
||||||
screen->runNow();
|
screen->runNow();
|
||||||
|
} else if (selected == ShowChirpy) {
|
||||||
|
screen->toggleFrameVisibility("chirpy");
|
||||||
|
screen->setFrames(Screen::FOCUS_SYSTEM);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
menuQueue = system_base_menu;
|
||||||
|
screen->runNow();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
screen->showOverlayBanner(bannerOptions);
|
screen->showOverlayBanner(bannerOptions);
|
||||||
@@ -1090,7 +1292,11 @@ void menuHandler::powerMenu()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
bannerOptions.message = "Power";
|
||||||
|
#else
|
||||||
bannerOptions.message = "Reboot / Shutdown";
|
bannerOptions.message = "Reboot / Shutdown";
|
||||||
|
#endif
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
bannerOptions.optionsCount = options;
|
bannerOptions.optionsCount = options;
|
||||||
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
||||||
@@ -1143,6 +1349,116 @@ void menuHandler::keyVerificationFinalPrompt()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void menuHandler::FrameToggles_menu()
|
||||||
|
{
|
||||||
|
enum optionsNumbers {
|
||||||
|
Finish,
|
||||||
|
nodelist,
|
||||||
|
nodelist_lastheard,
|
||||||
|
nodelist_hopsignal,
|
||||||
|
nodelist_distance,
|
||||||
|
nodelist_bearings,
|
||||||
|
gps,
|
||||||
|
lora,
|
||||||
|
clock,
|
||||||
|
show_favorites,
|
||||||
|
enumEnd
|
||||||
|
};
|
||||||
|
static const char *optionsArray[enumEnd] = {"Finish"};
|
||||||
|
static int optionsEnumArray[enumEnd] = {Finish};
|
||||||
|
int options = 1;
|
||||||
|
|
||||||
|
// Track last selected index (not enum value!)
|
||||||
|
static int lastSelectedIndex = 0;
|
||||||
|
|
||||||
|
#ifndef USE_EINK
|
||||||
|
optionsArray[options] = screen->isFrameHidden("nodelist") ? "Show Node List" : "Hide Node List";
|
||||||
|
optionsEnumArray[options++] = nodelist;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_EINK
|
||||||
|
optionsArray[options] = screen->isFrameHidden("nodelist_lastheard") ? "Show NL - Last Heard" : "Hide NL - Last Heard";
|
||||||
|
optionsEnumArray[options++] = nodelist_lastheard;
|
||||||
|
optionsArray[options] = screen->isFrameHidden("nodelist_hopsignal") ? "Show NL - Hops/Signal" : "Hide NL - Hops/Signal";
|
||||||
|
optionsEnumArray[options++] = nodelist_hopsignal;
|
||||||
|
optionsArray[options] = screen->isFrameHidden("nodelist_distance") ? "Show NL - Distance" : "Hide NL - Distance";
|
||||||
|
optionsEnumArray[options++] = nodelist_distance;
|
||||||
|
#endif
|
||||||
|
#if HAS_GPS
|
||||||
|
optionsArray[options] = screen->isFrameHidden("nodelist_bearings") ? "Show Bearings" : "Hide Bearings";
|
||||||
|
optionsEnumArray[options++] = nodelist_bearings;
|
||||||
|
|
||||||
|
optionsArray[options] = screen->isFrameHidden("gps") ? "Show Position" : "Hide Position";
|
||||||
|
optionsEnumArray[options++] = gps;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
optionsArray[options] = screen->isFrameHidden("lora") ? "Show LoRa" : "Hide LoRa";
|
||||||
|
optionsEnumArray[options++] = lora;
|
||||||
|
|
||||||
|
optionsArray[options] = screen->isFrameHidden("clock") ? "Show Clock" : "Hide Clock";
|
||||||
|
optionsEnumArray[options++] = clock;
|
||||||
|
|
||||||
|
optionsArray[options] = screen->isFrameHidden("show_favorites") ? "Show Favorites" : "Hide Favorites";
|
||||||
|
optionsEnumArray[options++] = show_favorites;
|
||||||
|
|
||||||
|
BannerOverlayOptions bannerOptions;
|
||||||
|
bannerOptions.message = "Show/Hide Frames";
|
||||||
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
|
bannerOptions.optionsCount = options;
|
||||||
|
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
||||||
|
bannerOptions.InitialSelected = lastSelectedIndex; // Use index, not enum value
|
||||||
|
|
||||||
|
bannerOptions.bannerCallback = [options](int selected) mutable -> void {
|
||||||
|
// Find the index of selected in optionsEnumArray
|
||||||
|
int idx = 0;
|
||||||
|
for (; idx < options; ++idx) {
|
||||||
|
if (optionsEnumArray[idx] == selected)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lastSelectedIndex = idx;
|
||||||
|
|
||||||
|
if (selected == Finish) {
|
||||||
|
screen->setFrames(Screen::FOCUS_DEFAULT);
|
||||||
|
} else if (selected == nodelist) {
|
||||||
|
screen->toggleFrameVisibility("nodelist");
|
||||||
|
menuHandler::menuQueue = menuHandler::FrameToggles;
|
||||||
|
screen->runNow();
|
||||||
|
} else if (selected == nodelist_lastheard) {
|
||||||
|
screen->toggleFrameVisibility("nodelist_lastheard");
|
||||||
|
menuHandler::menuQueue = menuHandler::FrameToggles;
|
||||||
|
screen->runNow();
|
||||||
|
} else if (selected == nodelist_hopsignal) {
|
||||||
|
screen->toggleFrameVisibility("nodelist_hopsignal");
|
||||||
|
menuHandler::menuQueue = menuHandler::FrameToggles;
|
||||||
|
screen->runNow();
|
||||||
|
} else if (selected == nodelist_distance) {
|
||||||
|
screen->toggleFrameVisibility("nodelist_distance");
|
||||||
|
menuHandler::menuQueue = menuHandler::FrameToggles;
|
||||||
|
screen->runNow();
|
||||||
|
} else if (selected == nodelist_bearings) {
|
||||||
|
screen->toggleFrameVisibility("nodelist_bearings");
|
||||||
|
menuHandler::menuQueue = menuHandler::FrameToggles;
|
||||||
|
screen->runNow();
|
||||||
|
} else if (selected == gps) {
|
||||||
|
screen->toggleFrameVisibility("gps");
|
||||||
|
menuHandler::menuQueue = menuHandler::FrameToggles;
|
||||||
|
screen->runNow();
|
||||||
|
} else if (selected == lora) {
|
||||||
|
screen->toggleFrameVisibility("lora");
|
||||||
|
menuHandler::menuQueue = menuHandler::FrameToggles;
|
||||||
|
screen->runNow();
|
||||||
|
} else if (selected == clock) {
|
||||||
|
screen->toggleFrameVisibility("clock");
|
||||||
|
menuHandler::menuQueue = menuHandler::FrameToggles;
|
||||||
|
screen->runNow();
|
||||||
|
} else if (selected == show_favorites) {
|
||||||
|
screen->toggleFrameVisibility("show_favorites");
|
||||||
|
menuHandler::menuQueue = menuHandler::FrameToggles;
|
||||||
|
screen->runNow();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
screen->showOverlayBanner(bannerOptions);
|
||||||
|
}
|
||||||
|
|
||||||
void menuHandler::handleMenuSwitch(OLEDDisplay *display)
|
void menuHandler::handleMenuSwitch(OLEDDisplay *display)
|
||||||
{
|
{
|
||||||
if (menuQueue != menu_none)
|
if (menuQueue != menu_none)
|
||||||
@@ -1150,9 +1466,15 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
|
|||||||
switch (menuQueue) {
|
switch (menuQueue) {
|
||||||
case menu_none:
|
case menu_none:
|
||||||
break;
|
break;
|
||||||
|
case lora_Menu:
|
||||||
|
loraMenu();
|
||||||
|
break;
|
||||||
case lora_picker:
|
case lora_picker:
|
||||||
LoraRegionPicker();
|
LoraRegionPicker();
|
||||||
break;
|
break;
|
||||||
|
case device_role_picker:
|
||||||
|
DeviceRolePicker();
|
||||||
|
break;
|
||||||
case no_timeout_lora_picker:
|
case no_timeout_lora_picker:
|
||||||
LoraRegionPicker(0);
|
LoraRegionPicker(0);
|
||||||
break;
|
break;
|
||||||
@@ -1178,6 +1500,9 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
|
|||||||
case gps_toggle_menu:
|
case gps_toggle_menu:
|
||||||
GPSToggleMenu();
|
GPSToggleMenu();
|
||||||
break;
|
break;
|
||||||
|
case gps_format_menu:
|
||||||
|
GPSFormatMenu();
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
case compass_point_north_menu:
|
case compass_point_north_menu:
|
||||||
compassNorthMenu();
|
compassNorthMenu();
|
||||||
@@ -1239,6 +1564,9 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
|
|||||||
case power_menu:
|
case power_menu:
|
||||||
powerMenu();
|
powerMenu();
|
||||||
break;
|
break;
|
||||||
|
case FrameToggles:
|
||||||
|
FrameToggles_menu();
|
||||||
|
break;
|
||||||
case throttle_message:
|
case throttle_message:
|
||||||
screen->showSimpleBanner("Too Many Attempts\nTry again in 60 seconds.", 5000);
|
screen->showSimpleBanner("Too Many Attempts\nTry again in 60 seconds.", 5000);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ class menuHandler
|
|||||||
public:
|
public:
|
||||||
enum screenMenus {
|
enum screenMenus {
|
||||||
menu_none,
|
menu_none,
|
||||||
|
lora_Menu,
|
||||||
lora_picker,
|
lora_picker,
|
||||||
|
device_role_picker,
|
||||||
no_timeout_lora_picker,
|
no_timeout_lora_picker,
|
||||||
TZ_picker,
|
TZ_picker,
|
||||||
twelve_hour_picker,
|
twelve_hour_picker,
|
||||||
@@ -17,6 +19,7 @@ class menuHandler
|
|||||||
clock_menu,
|
clock_menu,
|
||||||
position_base_menu,
|
position_base_menu,
|
||||||
gps_toggle_menu,
|
gps_toggle_menu,
|
||||||
|
gps_format_menu,
|
||||||
compass_point_north_menu,
|
compass_point_north_menu,
|
||||||
reset_node_db_menu,
|
reset_node_db_menu,
|
||||||
buzzermodemenupicker,
|
buzzermodemenupicker,
|
||||||
@@ -39,11 +42,14 @@ class menuHandler
|
|||||||
key_verification_final_prompt,
|
key_verification_final_prompt,
|
||||||
trace_route_menu,
|
trace_route_menu,
|
||||||
throttle_message,
|
throttle_message,
|
||||||
|
FrameToggles
|
||||||
};
|
};
|
||||||
static screenMenus menuQueue;
|
static screenMenus menuQueue;
|
||||||
|
|
||||||
static void OnboardMessage();
|
static void OnboardMessage();
|
||||||
static void LoraRegionPicker(uint32_t duration = 30000);
|
static void LoraRegionPicker(uint32_t duration = 30000);
|
||||||
|
static void loraMenu();
|
||||||
|
static void DeviceRolePicker();
|
||||||
static void handleMenuSwitch(OLEDDisplay *display);
|
static void handleMenuSwitch(OLEDDisplay *display);
|
||||||
static void showConfirmationBanner(const char *message, std::function<void()> onConfirm);
|
static void showConfirmationBanner(const char *message, std::function<void()> onConfirm);
|
||||||
static void clockMenu();
|
static void clockMenu();
|
||||||
@@ -58,6 +64,7 @@ class menuHandler
|
|||||||
static void positionBaseMenu();
|
static void positionBaseMenu();
|
||||||
static void compassNorthMenu();
|
static void compassNorthMenu();
|
||||||
static void GPSToggleMenu();
|
static void GPSToggleMenu();
|
||||||
|
static void GPSFormatMenu();
|
||||||
static void BuzzerModeMenu();
|
static void BuzzerModeMenu();
|
||||||
static void switchToMUIMenu();
|
static void switchToMUIMenu();
|
||||||
static void TFTColorPickerMenu(OLEDDisplay *display);
|
static void TFTColorPickerMenu(OLEDDisplay *display);
|
||||||
@@ -76,6 +83,8 @@ class menuHandler
|
|||||||
static void notificationsMenu();
|
static void notificationsMenu();
|
||||||
static void screenOptionsMenu();
|
static void screenOptionsMenu();
|
||||||
static void powerMenu();
|
static void powerMenu();
|
||||||
|
static void textMessageMenu();
|
||||||
|
static void FrameToggles_menu();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void saveUIConfig();
|
static void saveUIConfig();
|
||||||
|
|||||||
@@ -181,12 +181,19 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
display->clear();
|
display->clear();
|
||||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||||
display->setFont(FONT_SMALL);
|
display->setFont(FONT_SMALL);
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
const int fixedTopHeight = 24;
|
||||||
|
const int windowX = 0;
|
||||||
|
const int windowY = fixedTopHeight;
|
||||||
|
const int windowWidth = 64;
|
||||||
|
const int windowHeight = SCREEN_HEIGHT - fixedTopHeight;
|
||||||
|
#else
|
||||||
const int navHeight = FONT_HEIGHT_SMALL;
|
const int navHeight = FONT_HEIGHT_SMALL;
|
||||||
const int scrollBottom = SCREEN_HEIGHT - navHeight;
|
const int scrollBottom = SCREEN_HEIGHT - navHeight;
|
||||||
const int usableHeight = scrollBottom;
|
const int usableHeight = scrollBottom;
|
||||||
const int textWidth = SCREEN_WIDTH;
|
const int textWidth = SCREEN_WIDTH;
|
||||||
|
|
||||||
|
#endif
|
||||||
bool isInverted = (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_INVERTED);
|
bool isInverted = (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_INVERTED);
|
||||||
bool isBold = config.display.heading_bold;
|
bool isBold = config.display.heading_bold;
|
||||||
|
|
||||||
@@ -201,7 +208,11 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
graphics::drawCommonHeader(display, x, y, titleStr);
|
graphics::drawCommonHeader(display, x, y, titleStr);
|
||||||
const char *messageString = "No messages";
|
const char *messageString = "No messages";
|
||||||
int center_text = (SCREEN_WIDTH / 2) - (display->getStringWidth(messageString) / 2);
|
int center_text = (SCREEN_WIDTH / 2) - (display->getStringWidth(messageString) / 2);
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
display->drawString(center_text, windowY + (windowHeight / 2) - (FONT_HEIGHT_SMALL / 2) - 5, messageString);
|
||||||
|
#else
|
||||||
display->drawString(center_text, getTextPositions(display)[2], messageString);
|
display->drawString(center_text, getTextPositions(display)[2], messageString);
|
||||||
|
#endif
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,6 +220,10 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(getFrom(&mp));
|
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(getFrom(&mp));
|
||||||
char headerStr[80];
|
char headerStr[80];
|
||||||
const char *sender = "???";
|
const char *sender = "???";
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
if (node && node->has_user)
|
||||||
|
sender = node->user.short_name;
|
||||||
|
#else
|
||||||
if (node && node->has_user) {
|
if (node && node->has_user) {
|
||||||
if (SCREEN_WIDTH >= 200 && strlen(node->user.long_name) > 0) {
|
if (SCREEN_WIDTH >= 200 && strlen(node->user.long_name) > 0) {
|
||||||
sender = node->user.long_name;
|
sender = node->user.long_name;
|
||||||
@@ -216,6 +231,7 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
sender = node->user.short_name;
|
sender = node->user.short_name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
uint32_t seconds = sinceReceived(&mp), minutes = seconds / 60, hours = minutes / 60, days = hours / 24;
|
uint32_t seconds = sinceReceived(&mp), minutes = seconds / 60, hours = minutes / 60, days = hours / 24;
|
||||||
uint8_t timestampHours, timestampMinutes;
|
uint8_t timestampHours, timestampMinutes;
|
||||||
int32_t daysAgo;
|
int32_t daysAgo;
|
||||||
@@ -235,10 +251,61 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
sender);
|
sender);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
snprintf(headerStr, sizeof(headerStr), "%s from %s", UIRenderer::drawTimeDelta(days, hours, minutes, seconds).c_str(),
|
||||||
|
sender);
|
||||||
|
#else
|
||||||
snprintf(headerStr, sizeof(headerStr), "%s ago from %s", UIRenderer::drawTimeDelta(days, hours, minutes, seconds).c_str(),
|
snprintf(headerStr, sizeof(headerStr), "%s ago from %s", UIRenderer::drawTimeDelta(days, hours, minutes, seconds).c_str(),
|
||||||
sender);
|
sender);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
graphics::drawCommonHeader(display, x, y, titleStr);
|
||||||
|
int headerY = getTextPositions(display)[1];
|
||||||
|
display->drawString(x, headerY, headerStr);
|
||||||
|
for (int separatorX = 0; separatorX < SCREEN_WIDTH; separatorX += 2) {
|
||||||
|
display->setPixel(separatorX, fixedTopHeight - 1);
|
||||||
|
}
|
||||||
|
cachedLines.clear();
|
||||||
|
std::string fullMsg(messageBuf);
|
||||||
|
std::string currentLine;
|
||||||
|
for (size_t i = 0; i < fullMsg.size();) {
|
||||||
|
unsigned char c = fullMsg[i];
|
||||||
|
size_t charLen = 1;
|
||||||
|
if ((c & 0xE0) == 0xC0)
|
||||||
|
charLen = 2;
|
||||||
|
else if ((c & 0xF0) == 0xE0)
|
||||||
|
charLen = 3;
|
||||||
|
else if ((c & 0xF8) == 0xF0)
|
||||||
|
charLen = 4;
|
||||||
|
std::string nextChar = fullMsg.substr(i, charLen);
|
||||||
|
std::string testLine = currentLine + nextChar;
|
||||||
|
if (display->getStringWidth(testLine.c_str()) > windowWidth) {
|
||||||
|
cachedLines.push_back(currentLine);
|
||||||
|
currentLine = nextChar;
|
||||||
|
} else {
|
||||||
|
currentLine = testLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
i += charLen;
|
||||||
|
}
|
||||||
|
if (!currentLine.empty())
|
||||||
|
cachedLines.push_back(currentLine);
|
||||||
|
cachedHeights = calculateLineHeights(cachedLines, emotes);
|
||||||
|
int yOffset = windowY;
|
||||||
|
int linesDrawn = 0;
|
||||||
|
for (size_t i = 0; i < cachedLines.size(); ++i) {
|
||||||
|
if (linesDrawn >= 2)
|
||||||
|
break;
|
||||||
|
int lineHeight = cachedHeights[i];
|
||||||
|
if (yOffset + lineHeight > windowY + windowHeight)
|
||||||
|
break;
|
||||||
|
drawStringWithEmotes(display, windowX, yOffset, cachedLines[i], emotes, numEmotes);
|
||||||
|
yOffset += lineHeight;
|
||||||
|
linesDrawn++;
|
||||||
|
}
|
||||||
|
screen->forceDisplay();
|
||||||
|
#else
|
||||||
uint32_t now = millis();
|
uint32_t now = millis();
|
||||||
#ifndef EXCLUDE_EMOJI
|
#ifndef EXCLUDE_EMOJI
|
||||||
// === Bounce animation setup ===
|
// === Bounce animation setup ===
|
||||||
@@ -355,6 +422,7 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
|
|
||||||
// Draw header at the end to sort out overlapping elements
|
// Draw header at the end to sort out overlapping elements
|
||||||
graphics::drawCommonHeader(display, x, y, titleStr);
|
graphics::drawCommonHeader(display, x, y, titleStr);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> generateLines(OLEDDisplay *display, const char *headerStr, const char *messageBuf, int textWidth)
|
std::vector<std::string> generateLines(OLEDDisplay *display, const char *headerStr, const char *messageBuf, int textWidth)
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ extern bool haveGlyphs(const char *str);
|
|||||||
// Global screen instance
|
// Global screen instance
|
||||||
extern graphics::Screen *screen;
|
extern graphics::Screen *screen;
|
||||||
|
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
static uint32_t lastSwitchTime = 0;
|
||||||
|
#else
|
||||||
|
#endif
|
||||||
namespace graphics
|
namespace graphics
|
||||||
{
|
{
|
||||||
namespace NodeListRenderer
|
namespace NodeListRenderer
|
||||||
@@ -393,9 +397,11 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
|
|||||||
{
|
{
|
||||||
const int COMMON_HEADER_HEIGHT = FONT_HEIGHT_SMALL - 1;
|
const int COMMON_HEADER_HEIGHT = FONT_HEIGHT_SMALL - 1;
|
||||||
const int rowYOffset = FONT_HEIGHT_SMALL - 3;
|
const int rowYOffset = FONT_HEIGHT_SMALL - 3;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
int columnWidth = display->getWidth();
|
||||||
|
#else
|
||||||
int columnWidth = display->getWidth() / 2;
|
int columnWidth = display->getWidth() / 2;
|
||||||
|
#endif
|
||||||
display->clear();
|
display->clear();
|
||||||
|
|
||||||
// Draw the battery/time header
|
// Draw the battery/time header
|
||||||
@@ -408,8 +414,11 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
|
|||||||
int totalRowsAvailable = (display->getHeight() - y) / rowYOffset;
|
int totalRowsAvailable = (display->getHeight() - y) / rowYOffset;
|
||||||
|
|
||||||
int visibleNodeRows = totalRowsAvailable;
|
int visibleNodeRows = totalRowsAvailable;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
int totalColumns = 1;
|
||||||
|
#else
|
||||||
int totalColumns = 2;
|
int totalColumns = 2;
|
||||||
|
#endif
|
||||||
int startIndex = scrollIndex * visibleNodeRows * totalColumns;
|
int startIndex = scrollIndex * visibleNodeRows * totalColumns;
|
||||||
if (nodeDB->getMeshNodeByIndex(startIndex)->num == nodeDB->getNodeNum()) {
|
if (nodeDB->getMeshNodeByIndex(startIndex)->num == nodeDB->getNodeNum()) {
|
||||||
startIndex++; // skip own node
|
startIndex++; // skip own node
|
||||||
@@ -445,12 +454,14 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !defined(M5STACK_UNITC6L)
|
||||||
// Draw column separator
|
// Draw column separator
|
||||||
if (shownCount > 0) {
|
if (shownCount > 0) {
|
||||||
const int firstNodeY = y + 3;
|
const int firstNodeY = y + 3;
|
||||||
drawColumnSeparator(display, x, firstNodeY, lastNodeY);
|
drawColumnSeparator(display, x, firstNodeY, lastNodeY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
const int scrollStartY = y + 3;
|
const int scrollStartY = y + 3;
|
||||||
drawScrollbar(display, visibleNodeRows, totalEntries, scrollIndex, 2, scrollStartY);
|
drawScrollbar(display, visibleNodeRows, totalEntries, scrollIndex, 2, scrollStartY);
|
||||||
}
|
}
|
||||||
@@ -468,6 +479,13 @@ void drawDynamicNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state,
|
|||||||
|
|
||||||
unsigned long now = millis();
|
unsigned long now = millis();
|
||||||
|
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
display->clear();
|
||||||
|
if (now - lastSwitchTime >= 3000) {
|
||||||
|
display->display();
|
||||||
|
lastSwitchTime = now;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
// On very first call (on boot or state enter)
|
// On very first call (on boot or state enter)
|
||||||
if (lastRenderedMode == MODE_COUNT) {
|
if (lastRenderedMode == MODE_COUNT) {
|
||||||
currentMode = MODE_LAST_HEARD;
|
currentMode = MODE_LAST_HEARD;
|
||||||
@@ -522,6 +540,14 @@ void drawNodeListWithCompasses(OLEDDisplay *display, OLEDDisplayUiState *state,
|
|||||||
double lat = DegD(ourNode->position.latitude_i);
|
double lat = DegD(ourNode->position.latitude_i);
|
||||||
double lon = DegD(ourNode->position.longitude_i);
|
double lon = DegD(ourNode->position.longitude_i);
|
||||||
|
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
display->clear();
|
||||||
|
uint32_t now = millis();
|
||||||
|
if (now - lastSwitchTime >= 2000) {
|
||||||
|
display->display();
|
||||||
|
lastSwitchTime = now;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (uiconfig.compass_mode != meshtastic_CompassMode_FREEZE_HEADING) {
|
if (uiconfig.compass_mode != meshtastic_CompassMode_FREEZE_HEADING) {
|
||||||
#if HAS_GPS
|
#if HAS_GPS
|
||||||
if (screen->hasHeading()) {
|
if (screen->hasHeading()) {
|
||||||
|
|||||||
@@ -216,9 +216,11 @@ void NotificationRenderer::drawNodePicker(OLEDDisplay *display, OLEDDisplayUiSta
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle input
|
// Handle input
|
||||||
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
|
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_LEFT ||
|
||||||
|
inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
|
||||||
curSelected--;
|
curSelected--;
|
||||||
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
|
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_RIGHT ||
|
||||||
|
inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
|
||||||
curSelected++;
|
curSelected++;
|
||||||
} else if (inEvent.inputEvent == INPUT_BROKER_SELECT) {
|
} else if (inEvent.inputEvent == INPUT_BROKER_SELECT) {
|
||||||
alertBannerCallback(selectedNodenum);
|
alertBannerCallback(selectedNodenum);
|
||||||
@@ -333,9 +335,11 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp
|
|||||||
|
|
||||||
// Handle input
|
// Handle input
|
||||||
if (alertBannerOptions > 0) {
|
if (alertBannerOptions > 0) {
|
||||||
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
|
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_LEFT ||
|
||||||
|
inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
|
||||||
curSelected--;
|
curSelected--;
|
||||||
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
|
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_RIGHT ||
|
||||||
|
inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
|
||||||
curSelected++;
|
curSelected++;
|
||||||
} else if (inEvent.inputEvent == INPUT_BROKER_SELECT) {
|
} else if (inEvent.inputEvent == INPUT_BROKER_SELECT) {
|
||||||
if (optionsEnumPtr != nullptr) {
|
if (optionsEnumPtr != nullptr) {
|
||||||
@@ -459,6 +463,135 @@ void NotificationRenderer::drawNotificationBox(OLEDDisplay *display, OLEDDisplay
|
|||||||
// count lines
|
// count lines
|
||||||
|
|
||||||
uint16_t boxWidth = hPadding * 2 + maxWidth;
|
uint16_t boxWidth = hPadding * 2 + maxWidth;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
if (needs_bell) {
|
||||||
|
if (isHighResolution && boxWidth <= 150)
|
||||||
|
boxWidth += 26;
|
||||||
|
if (!isHighResolution && boxWidth <= 100)
|
||||||
|
boxWidth += 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t screenHeight = display->height();
|
||||||
|
uint8_t effectiveLineHeight = FONT_HEIGHT_SMALL - 3;
|
||||||
|
uint8_t visibleTotalLines = std::min<uint8_t>(lineCount, (screenHeight - vPadding * 2) / effectiveLineHeight);
|
||||||
|
uint16_t contentHeight = visibleTotalLines * effectiveLineHeight;
|
||||||
|
uint16_t boxHeight = contentHeight + vPadding * 2;
|
||||||
|
if (visibleTotalLines == 1)
|
||||||
|
boxHeight += (isHighResolution ? 4 : 3);
|
||||||
|
|
||||||
|
int16_t boxLeft = (display->width() / 2) - (boxWidth / 2);
|
||||||
|
if (totalLines > visibleTotalLines)
|
||||||
|
boxWidth += (isHighResolution ? 4 : 2);
|
||||||
|
int16_t boxTop = (display->height() / 2) - (boxHeight / 2);
|
||||||
|
|
||||||
|
if (visibleTotalLines == 1) {
|
||||||
|
boxTop += 25;
|
||||||
|
}
|
||||||
|
if (alertBannerOptions < 3) {
|
||||||
|
int missingLines = 3 - alertBannerOptions;
|
||||||
|
int moveUp = missingLines * (effectiveLineHeight / 2);
|
||||||
|
boxTop -= moveUp;
|
||||||
|
if (boxTop < 0)
|
||||||
|
boxTop = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Draw Box ===
|
||||||
|
display->setColor(BLACK);
|
||||||
|
display->fillRect(boxLeft, boxTop, boxWidth, boxHeight);
|
||||||
|
display->setColor(WHITE);
|
||||||
|
display->drawRect(boxLeft, boxTop, boxWidth, boxHeight);
|
||||||
|
display->fillRect(boxLeft, boxTop - 2, boxWidth, 1);
|
||||||
|
display->fillRect(boxLeft - 2, boxTop, 1, boxHeight);
|
||||||
|
display->fillRect(boxLeft + boxWidth + 1, boxTop, 1, boxHeight);
|
||||||
|
display->setColor(BLACK);
|
||||||
|
display->fillRect(boxLeft, boxTop, 1, 1);
|
||||||
|
display->fillRect(boxLeft + boxWidth - 1, boxTop, 1, 1);
|
||||||
|
display->fillRect(boxLeft, boxTop + boxHeight - 1, 1, 1);
|
||||||
|
display->fillRect(boxLeft + boxWidth - 1, boxTop + boxHeight - 1, 1, 1);
|
||||||
|
display->setColor(WHITE);
|
||||||
|
int16_t lineY = boxTop + vPadding;
|
||||||
|
int swingRange = 8;
|
||||||
|
static int swingOffset = 0;
|
||||||
|
static bool swingRight = true;
|
||||||
|
static unsigned long lastSwingTime = 0;
|
||||||
|
unsigned long now = millis();
|
||||||
|
int swingSpeedMs = 10 / (swingRange * 2);
|
||||||
|
if (now - lastSwingTime >= (unsigned long)swingSpeedMs) {
|
||||||
|
lastSwingTime = now;
|
||||||
|
if (swingRight) {
|
||||||
|
swingOffset++;
|
||||||
|
if (swingOffset >= swingRange)
|
||||||
|
swingRight = false;
|
||||||
|
} else {
|
||||||
|
swingOffset--;
|
||||||
|
if (swingOffset <= 0)
|
||||||
|
swingRight = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < lineCount; i++) {
|
||||||
|
bool isTitle = (i == 0);
|
||||||
|
int globalOptionIndex = (i - 1) + firstOptionToShow;
|
||||||
|
bool isSelectedOption = (!isTitle && globalOptionIndex >= 0 && globalOptionIndex == curSelected);
|
||||||
|
|
||||||
|
uint16_t visibleWidth = 64 - hPadding * 2;
|
||||||
|
if (totalLines > visibleTotalLines)
|
||||||
|
visibleWidth -= 6;
|
||||||
|
char lineBuffer[lineLengths[i] + 1];
|
||||||
|
strncpy(lineBuffer, lines[i], lineLengths[i]);
|
||||||
|
lineBuffer[lineLengths[i]] = '\0';
|
||||||
|
|
||||||
|
if (isTitle) {
|
||||||
|
if (visibleTotalLines == 1) {
|
||||||
|
display->setColor(BLACK);
|
||||||
|
display->fillRect(boxLeft, boxTop, boxWidth, effectiveLineHeight);
|
||||||
|
display->setColor(WHITE);
|
||||||
|
display->drawString(boxLeft + (boxWidth - lineWidths[i]) / 2, boxTop, lineBuffer);
|
||||||
|
} else {
|
||||||
|
display->setColor(WHITE);
|
||||||
|
display->fillRect(boxLeft, boxTop, boxWidth, effectiveLineHeight);
|
||||||
|
display->setColor(BLACK);
|
||||||
|
display->drawString(boxLeft + (boxWidth - lineWidths[i]) / 2, boxTop, lineBuffer);
|
||||||
|
display->setColor(WHITE);
|
||||||
|
if (needs_bell) {
|
||||||
|
int bellY = boxTop + (FONT_HEIGHT_SMALL - 8) / 2;
|
||||||
|
display->drawXbm(boxLeft + (boxWidth - lineWidths[i]) / 2 - 10, bellY, 8, 8, bell_alert);
|
||||||
|
display->drawXbm(boxLeft + (boxWidth + lineWidths[i]) / 2 + 2, bellY, 8, 8, bell_alert);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lineY = boxTop + effectiveLineHeight + 1;
|
||||||
|
} else if (isSelectedOption) {
|
||||||
|
display->setColor(WHITE);
|
||||||
|
display->fillRect(boxLeft, lineY, boxWidth, effectiveLineHeight);
|
||||||
|
display->setColor(BLACK);
|
||||||
|
if (lineLengths[i] > 15 && lineWidths[i] > visibleWidth) {
|
||||||
|
int textX = boxLeft + hPadding + swingOffset;
|
||||||
|
display->drawString(textX, lineY - 1, lineBuffer);
|
||||||
|
} else {
|
||||||
|
display->drawString(boxLeft + (boxWidth - lineWidths[i]) / 2, lineY - 1, lineBuffer);
|
||||||
|
}
|
||||||
|
display->setColor(WHITE);
|
||||||
|
lineY += effectiveLineHeight;
|
||||||
|
} else {
|
||||||
|
display->setColor(BLACK);
|
||||||
|
display->fillRect(boxLeft, lineY, boxWidth, effectiveLineHeight);
|
||||||
|
display->setColor(WHITE);
|
||||||
|
display->drawString(boxLeft + (boxWidth - lineWidths[i]) / 2, lineY, lineBuffer);
|
||||||
|
lineY += effectiveLineHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (totalLines > visibleTotalLines) {
|
||||||
|
const uint8_t scrollBarWidth = 5;
|
||||||
|
int16_t scrollBarX = boxLeft + boxWidth - scrollBarWidth - 2;
|
||||||
|
int16_t scrollBarY = boxTop + vPadding + effectiveLineHeight;
|
||||||
|
uint16_t scrollBarHeight = boxHeight - vPadding * 2 - effectiveLineHeight;
|
||||||
|
float ratio = (float)visibleTotalLines / totalLines;
|
||||||
|
uint16_t indicatorHeight = std::max((int)(scrollBarHeight * ratio), 4);
|
||||||
|
float scrollRatio = (float)(firstOptionToShow + lineCount - visibleTotalLines) / (totalLines - visibleTotalLines);
|
||||||
|
uint16_t indicatorY = scrollBarY + scrollRatio * (scrollBarHeight - indicatorHeight);
|
||||||
|
display->drawRect(scrollBarX, scrollBarY, scrollBarWidth, scrollBarHeight);
|
||||||
|
display->fillRect(scrollBarX + 1, indicatorY, scrollBarWidth - 2, indicatorHeight);
|
||||||
|
}
|
||||||
|
#else
|
||||||
if (needs_bell) {
|
if (needs_bell) {
|
||||||
if (isHighResolution && boxWidth <= 150)
|
if (isHighResolution && boxWidth <= 150)
|
||||||
boxWidth += 26;
|
boxWidth += 26;
|
||||||
@@ -547,6 +680,7 @@ void NotificationRenderer::drawNotificationBox(OLEDDisplay *display, OLEDDisplay
|
|||||||
display->drawRect(scrollBarX, scrollBarY, scrollBarWidth, scrollBarHeight);
|
display->drawRect(scrollBarX, scrollBarY, scrollBarWidth, scrollBarHeight);
|
||||||
display->fillRect(scrollBarX + 1, indicatorY, scrollBarWidth - 2, indicatorHeight);
|
display->fillRect(scrollBarX + 1, indicatorY, scrollBarWidth - 2, indicatorHeight);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw the last text message we received
|
/// Draw the last text message we received
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
// External variables
|
// External variables
|
||||||
extern graphics::Screen *screen;
|
extern graphics::Screen *screen;
|
||||||
|
static uint32_t lastSwitchTime = 0;
|
||||||
namespace graphics
|
namespace graphics
|
||||||
{
|
{
|
||||||
NodeNum UIRenderer::currentFavoriteNodeNum = 0;
|
NodeNum UIRenderer::currentFavoriteNodeNum = 0;
|
||||||
@@ -116,64 +116,120 @@ void UIRenderer::drawGpsAltitude(OLEDDisplay *display, int16_t x, int16_t y, con
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Draw GPS status coordinates
|
// Draw GPS status coordinates
|
||||||
void UIRenderer::drawGpsCoordinates(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gps)
|
void UIRenderer::drawGpsCoordinates(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gps,
|
||||||
|
const char *mode)
|
||||||
{
|
{
|
||||||
auto gpsFormat = config.display.gps_format;
|
auto gpsFormat = uiconfig.gps_format;
|
||||||
char displayLine[32];
|
char displayLine[32];
|
||||||
|
|
||||||
if (!gps->getIsConnected() && !config.position.fixed_position) {
|
if (!gps->getIsConnected() && !config.position.fixed_position) {
|
||||||
strcpy(displayLine, "No GPS present");
|
strcpy(displayLine, "No GPS present");
|
||||||
display->drawString(x + (display->getWidth() - (display->getStringWidth(displayLine))) / 2, y, displayLine);
|
display->drawString(x, y, displayLine);
|
||||||
} else if (!gps->getHasLock() && !config.position.fixed_position) {
|
} else if (!gps->getHasLock() && !config.position.fixed_position) {
|
||||||
strcpy(displayLine, "No GPS Lock");
|
strcpy(displayLine, "No GPS Lock");
|
||||||
display->drawString(x + (display->getWidth() - (display->getStringWidth(displayLine))) / 2, y, displayLine);
|
display->drawString(x, y, displayLine);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
geoCoord.updateCoords(int32_t(gps->getLatitude()), int32_t(gps->getLongitude()), int32_t(gps->getAltitude()));
|
geoCoord.updateCoords(int32_t(gps->getLatitude()), int32_t(gps->getLongitude()), int32_t(gps->getAltitude()));
|
||||||
|
|
||||||
if (gpsFormat != meshtastic_Config_DisplayConfig_GpsCoordinateFormat_DMS) {
|
if (gpsFormat != meshtastic_DeviceUIConfig_GpsCoordinateFormat_DMS) {
|
||||||
char coordinateLine[22];
|
char coordinateLine_1[22];
|
||||||
if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_DEC) { // Decimal Degrees
|
char coordinateLine_2[22];
|
||||||
snprintf(coordinateLine, sizeof(coordinateLine), "%f %f", geoCoord.getLatitude() * 1e-7,
|
if (gpsFormat == meshtastic_DeviceUIConfig_GpsCoordinateFormat_DEC) { // Decimal Degrees
|
||||||
geoCoord.getLongitude() * 1e-7);
|
snprintf(coordinateLine_1, sizeof(coordinateLine_1), "Lat: %f", geoCoord.getLatitude() * 1e-7);
|
||||||
} else if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_UTM) { // Universal Transverse Mercator
|
snprintf(coordinateLine_2, sizeof(coordinateLine_2), "Lon: %f", geoCoord.getLongitude() * 1e-7);
|
||||||
snprintf(coordinateLine, sizeof(coordinateLine), "%2i%1c %06u %07u", geoCoord.getUTMZone(), geoCoord.getUTMBand(),
|
} else if (gpsFormat == meshtastic_DeviceUIConfig_GpsCoordinateFormat_UTM) { // Universal Transverse Mercator
|
||||||
geoCoord.getUTMEasting(), geoCoord.getUTMNorthing());
|
snprintf(coordinateLine_1, sizeof(coordinateLine_1), "%2i%1c %06u E", geoCoord.getUTMZone(),
|
||||||
} else if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MGRS) { // Military Grid Reference System
|
geoCoord.getUTMBand(), geoCoord.getUTMEasting());
|
||||||
snprintf(coordinateLine, sizeof(coordinateLine), "%2i%1c %1c%1c %05u %05u", geoCoord.getMGRSZone(),
|
snprintf(coordinateLine_2, sizeof(coordinateLine_2), "%07u N", geoCoord.getUTMNorthing());
|
||||||
geoCoord.getMGRSBand(), geoCoord.getMGRSEast100k(), geoCoord.getMGRSNorth100k(),
|
} else if (gpsFormat == meshtastic_DeviceUIConfig_GpsCoordinateFormat_MGRS) { // Military Grid Reference System
|
||||||
geoCoord.getMGRSEasting(), geoCoord.getMGRSNorthing());
|
snprintf(coordinateLine_1, sizeof(coordinateLine_1), "%2i%1c %1c%1c", geoCoord.getMGRSZone(),
|
||||||
} else if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_OLC) { // Open Location Code
|
geoCoord.getMGRSBand(), geoCoord.getMGRSEast100k(), geoCoord.getMGRSNorth100k());
|
||||||
geoCoord.getOLCCode(coordinateLine);
|
snprintf(coordinateLine_2, sizeof(coordinateLine_2), "%05u E %05u N", geoCoord.getMGRSEasting(),
|
||||||
} else if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_OSGR) { // Ordnance Survey Grid Reference
|
geoCoord.getMGRSNorthing());
|
||||||
if (geoCoord.getOSGRE100k() == 'I' || geoCoord.getOSGRN100k() == 'I') // OSGR is only valid around the UK region
|
} else if (gpsFormat == meshtastic_DeviceUIConfig_GpsCoordinateFormat_OLC) { // Open Location Code
|
||||||
snprintf(coordinateLine, sizeof(coordinateLine), "%s", "Out of Boundary");
|
geoCoord.getOLCCode(coordinateLine_1);
|
||||||
else
|
coordinateLine_2[0] = '\0';
|
||||||
snprintf(coordinateLine, sizeof(coordinateLine), "%1c%1c %05u %05u", geoCoord.getOSGRE100k(),
|
} else if (gpsFormat == meshtastic_DeviceUIConfig_GpsCoordinateFormat_OSGR) { // Ordnance Survey Grid Reference
|
||||||
geoCoord.getOSGRN100k(), geoCoord.getOSGREasting(), geoCoord.getOSGRNorthing());
|
if (geoCoord.getOSGRE100k() == 'I' || geoCoord.getOSGRN100k() == 'I') { // OSGR is only valid around the UK region
|
||||||
|
snprintf(coordinateLine_1, sizeof(coordinateLine_1), "%s", "Out of Boundary");
|
||||||
|
coordinateLine_2[0] = '\0';
|
||||||
|
} else {
|
||||||
|
snprintf(coordinateLine_1, sizeof(coordinateLine_1), "%1c%1c", geoCoord.getOSGRE100k(),
|
||||||
|
geoCoord.getOSGRN100k());
|
||||||
|
snprintf(coordinateLine_2, sizeof(coordinateLine_2), "%05u E %05u N", geoCoord.getOSGREasting(),
|
||||||
|
geoCoord.getOSGRNorthing());
|
||||||
|
}
|
||||||
|
} else if (gpsFormat == meshtastic_DeviceUIConfig_GpsCoordinateFormat_MLS) { // Maidenhead Locator System
|
||||||
|
double lat = geoCoord.getLatitude() * 1e-7;
|
||||||
|
double lon = geoCoord.getLongitude() * 1e-7;
|
||||||
|
|
||||||
|
// Normalize
|
||||||
|
if (lat > 90.0)
|
||||||
|
lat = 90.0;
|
||||||
|
if (lat < -90.0)
|
||||||
|
lat = -90.0;
|
||||||
|
while (lon < -180.0)
|
||||||
|
lon += 360.0;
|
||||||
|
while (lon >= 180.0)
|
||||||
|
lon -= 360.0;
|
||||||
|
|
||||||
|
double adjLon = lon + 180.0;
|
||||||
|
double adjLat = lat + 90.0;
|
||||||
|
|
||||||
|
char maiden[10]; // enough for 8-char + null
|
||||||
|
|
||||||
|
// Field (2 letters)
|
||||||
|
int lonField = int(adjLon / 20.0);
|
||||||
|
int latField = int(adjLat / 10.0);
|
||||||
|
adjLon -= lonField * 20.0;
|
||||||
|
adjLat -= latField * 10.0;
|
||||||
|
|
||||||
|
// Square (2 digits)
|
||||||
|
int lonSquare = int(adjLon / 2.0);
|
||||||
|
int latSquare = int(adjLat / 1.0);
|
||||||
|
adjLon -= lonSquare * 2.0;
|
||||||
|
adjLat -= latSquare * 1.0;
|
||||||
|
|
||||||
|
// Subsquare (2 letters)
|
||||||
|
double lonUnit = 2.0 / 24.0;
|
||||||
|
double latUnit = 1.0 / 24.0;
|
||||||
|
int lonSub = int(adjLon / lonUnit);
|
||||||
|
int latSub = int(adjLat / latUnit);
|
||||||
|
|
||||||
|
snprintf(maiden, sizeof(maiden), "%c%c%c%c%c%c", 'A' + lonField, 'A' + latField, '0' + lonSquare, '0' + latSquare,
|
||||||
|
'A' + lonSub, 'A' + latSub);
|
||||||
|
|
||||||
|
snprintf(coordinateLine_1, sizeof(coordinateLine_1), "MH: %s", maiden);
|
||||||
|
coordinateLine_2[0] = '\0'; // only need one line
|
||||||
}
|
}
|
||||||
|
|
||||||
// If fixed position, display text "Fixed GPS" alternating with the coordinates.
|
if (strcmp(mode, "line1") == 0) {
|
||||||
if (config.position.fixed_position) {
|
display->drawString(x, y, coordinateLine_1);
|
||||||
if ((millis() / 10000) % 2) {
|
} else if (strcmp(mode, "line2") == 0) {
|
||||||
display->drawString(x + (display->getWidth() - (display->getStringWidth(coordinateLine))) / 2, y,
|
display->drawString(x, y, coordinateLine_2);
|
||||||
coordinateLine);
|
} else if (strcmp(mode, "combined") == 0) {
|
||||||
} else {
|
display->drawString(x, y, coordinateLine_1);
|
||||||
display->drawString(x + (display->getWidth() - (display->getStringWidth("Fixed GPS"))) / 2, y, "Fixed GPS");
|
if (coordinateLine_2[0] != '\0') {
|
||||||
|
display->drawString(x + display->getStringWidth(coordinateLine_1), y, coordinateLine_2);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
display->drawString(x + (display->getWidth() - (display->getStringWidth(coordinateLine))) / 2, y, coordinateLine);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
char latLine[22];
|
char coordinateLine_1[22];
|
||||||
char lonLine[22];
|
char coordinateLine_2[22];
|
||||||
snprintf(latLine, sizeof(latLine), "%2i° %2i' %2u\" %1c", geoCoord.getDMSLatDeg(), geoCoord.getDMSLatMin(),
|
snprintf(coordinateLine_1, sizeof(coordinateLine_1), "Lat: %2i° %2i' %2u\" %1c", geoCoord.getDMSLatDeg(),
|
||||||
geoCoord.getDMSLatSec(), geoCoord.getDMSLatCP());
|
geoCoord.getDMSLatMin(), geoCoord.getDMSLatSec(), geoCoord.getDMSLatCP());
|
||||||
snprintf(lonLine, sizeof(lonLine), "%3i° %2i' %2u\" %1c", geoCoord.getDMSLonDeg(), geoCoord.getDMSLonMin(),
|
snprintf(coordinateLine_2, sizeof(coordinateLine_2), "Lon: %3i° %2i' %2u\" %1c", geoCoord.getDMSLonDeg(),
|
||||||
geoCoord.getDMSLonSec(), geoCoord.getDMSLonCP());
|
geoCoord.getDMSLonMin(), geoCoord.getDMSLonSec(), geoCoord.getDMSLonCP());
|
||||||
display->drawString(x + (display->getWidth() - (display->getStringWidth(latLine))) / 2, y - FONT_HEIGHT_SMALL * 1,
|
if (strcmp(mode, "line1") == 0) {
|
||||||
latLine);
|
display->drawString(x, y, coordinateLine_1);
|
||||||
display->drawString(x + (display->getWidth() - (display->getStringWidth(lonLine))) / 2, y, lonLine);
|
} else if (strcmp(mode, "line2") == 0) {
|
||||||
|
display->drawString(x, y, coordinateLine_2);
|
||||||
|
} else { // both
|
||||||
|
display->drawString(x, y, coordinateLine_1);
|
||||||
|
display->drawString(x, y + 10, coordinateLine_2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -218,7 +274,6 @@ void UIRenderer::drawNodes(OLEDDisplay *display, int16_t x, int16_t y, const mes
|
|||||||
// **********************
|
// **********************
|
||||||
void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *state, int16_t x, int16_t y)
|
void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (favoritedNodes.empty())
|
if (favoritedNodes.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -230,8 +285,15 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st
|
|||||||
meshtastic_NodeInfoLite *node = favoritedNodes[nodeIndex];
|
meshtastic_NodeInfoLite *node = favoritedNodes[nodeIndex];
|
||||||
if (!node || node->num == nodeDB->getNodeNum() || !node->is_favorite)
|
if (!node || node->num == nodeDB->getNodeNum() || !node->is_favorite)
|
||||||
return;
|
return;
|
||||||
|
uint32_t now = millis();
|
||||||
display->clear();
|
display->clear();
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
if (now - lastSwitchTime >= 10000) // 10000 ms = 10 秒
|
||||||
|
{
|
||||||
|
display->display();
|
||||||
|
lastSwitchTime = now;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
currentFavoriteNodeNum = node->num;
|
currentFavoriteNodeNum = node->num;
|
||||||
// === Create the shortName and title string ===
|
// === Create the shortName and title string ===
|
||||||
const char *shortName = (node->has_user && haveGlyphs(node->user.short_name)) ? node->user.short_name : "Node";
|
const char *shortName = (node->has_user && haveGlyphs(node->user.short_name)) ? node->user.short_name : "Node";
|
||||||
@@ -250,9 +312,13 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st
|
|||||||
// List of available macro Y positions in order, from top to bottom.
|
// List of available macro Y positions in order, from top to bottom.
|
||||||
int line = 1; // which slot to use next
|
int line = 1; // which slot to use next
|
||||||
std::string usernameStr;
|
std::string usernameStr;
|
||||||
|
|
||||||
// === 1. Long Name (always try to show first) ===
|
// === 1. Long Name (always try to show first) ===
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
const char *username = (node->has_user && node->user.long_name[0]) ? node->user.short_name : nullptr;
|
||||||
|
#else
|
||||||
const char *username = (node->has_user && node->user.long_name[0]) ? node->user.long_name : nullptr;
|
const char *username = (node->has_user && node->user.long_name[0]) ? node->user.long_name : nullptr;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (username) {
|
if (username) {
|
||||||
usernameStr = sanitizeString(username); // Sanitize the incoming long_name just in case
|
usernameStr = sanitizeString(username); // Sanitize the incoming long_name just in case
|
||||||
// Print node's long name (e.g. "Backpack Node")
|
// Print node's long name (e.g. "Backpack Node")
|
||||||
@@ -307,7 +373,7 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st
|
|||||||
if (seenStr[0] && line < 5) {
|
if (seenStr[0] && line < 5) {
|
||||||
display->drawString(x, getTextPositions(display)[line++], seenStr);
|
display->drawString(x, getTextPositions(display)[line++], seenStr);
|
||||||
}
|
}
|
||||||
|
#if !defined(M5STACK_UNITC6L)
|
||||||
// === 4. Uptime (only show if metric is present) ===
|
// === 4. Uptime (only show if metric is present) ===
|
||||||
char uptimeStr[32] = "";
|
char uptimeStr[32] = "";
|
||||||
if (node->has_device_metrics && node->device_metrics.has_uptime_seconds) {
|
if (node->has_device_metrics && node->device_metrics.has_uptime_seconds) {
|
||||||
@@ -479,6 +545,7 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st
|
|||||||
}
|
}
|
||||||
// else show nothing
|
// else show nothing
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// ****************************
|
// ****************************
|
||||||
@@ -492,7 +559,11 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta
|
|||||||
int line = 1;
|
int line = 1;
|
||||||
|
|
||||||
// === Header ===
|
// === Header ===
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
graphics::drawCommonHeader(display, x, y, "Home");
|
||||||
|
#else
|
||||||
graphics::drawCommonHeader(display, x, y, "");
|
graphics::drawCommonHeader(display, x, y, "");
|
||||||
|
#endif
|
||||||
|
|
||||||
// === Content below header ===
|
// === Content below header ===
|
||||||
|
|
||||||
@@ -507,20 +578,25 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta
|
|||||||
config.display.heading_bold = false;
|
config.display.heading_bold = false;
|
||||||
|
|
||||||
// Display Region and Channel Utilization
|
// Display Region and Channel Utilization
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
drawNodes(display, x, getTextPositions(display)[line] + 2, nodeStatus, -1, false, "online");
|
||||||
|
#else
|
||||||
drawNodes(display, x + 1, getTextPositions(display)[line] + 2, nodeStatus, -1, false, "online");
|
drawNodes(display, x + 1, getTextPositions(display)[line] + 2, nodeStatus, -1, false, "online");
|
||||||
|
#endif
|
||||||
char uptimeStr[32] = "";
|
char uptimeStr[32] = "";
|
||||||
uint32_t uptime = millis() / 1000;
|
uint32_t uptime = millis() / 1000;
|
||||||
uint32_t days = uptime / 86400;
|
uint32_t days = uptime / 86400;
|
||||||
uint32_t hours = (uptime % 86400) / 3600;
|
uint32_t hours = (uptime % 86400) / 3600;
|
||||||
uint32_t mins = (uptime % 3600) / 60;
|
uint32_t mins = (uptime % 3600) / 60;
|
||||||
// Show as "Up: 2d 3h", "Up: 5h 14m", or "Up: 37m"
|
// Show as "Up: 2d 3h", "Up: 5h 14m", or "Up: 37m"
|
||||||
|
#if !defined(M5STACK_UNITC6L)
|
||||||
if (days)
|
if (days)
|
||||||
snprintf(uptimeStr, sizeof(uptimeStr), "Up: %ud %uh", days, hours);
|
snprintf(uptimeStr, sizeof(uptimeStr), "Up: %ud %uh", days, hours);
|
||||||
else if (hours)
|
else if (hours)
|
||||||
snprintf(uptimeStr, sizeof(uptimeStr), "Up: %uh %um", hours, mins);
|
snprintf(uptimeStr, sizeof(uptimeStr), "Up: %uh %um", hours, mins);
|
||||||
else
|
else
|
||||||
snprintf(uptimeStr, sizeof(uptimeStr), "Up: %um", mins);
|
snprintf(uptimeStr, sizeof(uptimeStr), "Up: %um", mins);
|
||||||
|
#endif
|
||||||
display->drawString(SCREEN_WIDTH - display->getStringWidth(uptimeStr), getTextPositions(display)[line++], uptimeStr);
|
display->drawString(SCREEN_WIDTH - display->getStringWidth(uptimeStr), getTextPositions(display)[line++], uptimeStr);
|
||||||
|
|
||||||
// === Second Row: Satellites and Voltage ===
|
// === Second Row: Satellites and Voltage ===
|
||||||
@@ -549,6 +625,21 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
line += 1;
|
||||||
|
|
||||||
|
// === Node Identity ===
|
||||||
|
int textWidth = 0;
|
||||||
|
int nameX = 0;
|
||||||
|
char shortnameble[35];
|
||||||
|
snprintf(shortnameble, sizeof(shortnameble), "%s",
|
||||||
|
graphics::UIRenderer::haveGlyphs(owner.short_name) ? owner.short_name : "");
|
||||||
|
|
||||||
|
// === ShortName Centered ===
|
||||||
|
textWidth = display->getStringWidth(shortnameble);
|
||||||
|
nameX = (SCREEN_WIDTH - textWidth) / 2;
|
||||||
|
display->drawString(nameX, getTextPositions(display)[line++], shortnameble);
|
||||||
|
#else
|
||||||
if (powerStatus->getHasBattery()) {
|
if (powerStatus->getHasBattery()) {
|
||||||
char batStr[20];
|
char batStr[20];
|
||||||
int batV = powerStatus->getBatteryVoltageMv() / 1000;
|
int batV = powerStatus->getBatteryVoltageMv() / 1000;
|
||||||
@@ -674,6 +765,7 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta
|
|||||||
nameX = (SCREEN_WIDTH - textWidth) / 2;
|
nameX = (SCREEN_WIDTH - textWidth) / 2;
|
||||||
display->drawString(nameX, getTextPositions(display)[line++], shortnameble);
|
display->drawString(nameX, getTextPositions(display)[line++], shortnameble);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start Functions to write date/time to the screen
|
// Start Functions to write date/time to the screen
|
||||||
@@ -832,6 +924,28 @@ void UIRenderer::drawIconScreen(const char *upperMsg, OLEDDisplay *display, OLED
|
|||||||
// needs to be drawn relative to x and y
|
// needs to be drawn relative to x and y
|
||||||
|
|
||||||
// draw centered icon left to right and centered above the one line of app text
|
// draw centered icon left to right and centered above the one line of app text
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
display->drawXbm(x + (SCREEN_WIDTH - 50) / 2, y + (SCREEN_HEIGHT - 28) / 2, icon_width, icon_height, icon_bits);
|
||||||
|
display->setFont(FONT_MEDIUM);
|
||||||
|
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||||
|
display->setFont(FONT_SMALL);
|
||||||
|
// Draw region in upper left
|
||||||
|
if (upperMsg) {
|
||||||
|
int msgWidth = display->getStringWidth(upperMsg);
|
||||||
|
int msgX = x + (SCREEN_WIDTH - msgWidth) / 2;
|
||||||
|
int msgY = y;
|
||||||
|
display->drawString(msgX, msgY, upperMsg);
|
||||||
|
}
|
||||||
|
// Draw version and short name in bottom middle
|
||||||
|
char buf[25];
|
||||||
|
snprintf(buf, sizeof(buf), "%s %s", xstr(APP_VERSION_SHORT),
|
||||||
|
graphics::UIRenderer::haveGlyphs(owner.short_name) ? owner.short_name : "");
|
||||||
|
|
||||||
|
display->drawString(x + getStringCenteredX(buf), y + SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM, buf);
|
||||||
|
screen->forceDisplay();
|
||||||
|
|
||||||
|
display->setTextAlignment(TEXT_ALIGN_LEFT); // Restore left align, just to be kind to any other unsuspecting code
|
||||||
|
#else
|
||||||
display->drawXbm(x + (SCREEN_WIDTH - icon_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - icon_height) / 2 + 2,
|
display->drawXbm(x + (SCREEN_WIDTH - icon_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - icon_height) / 2 + 2,
|
||||||
icon_width, icon_height, icon_bits);
|
icon_width, icon_height, icon_bits);
|
||||||
|
|
||||||
@@ -840,7 +954,6 @@ void UIRenderer::drawIconScreen(const char *upperMsg, OLEDDisplay *display, OLED
|
|||||||
const char *title = "meshtastic.org";
|
const char *title = "meshtastic.org";
|
||||||
display->drawString(x + getStringCenteredX(title), y + SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM, title);
|
display->drawString(x + getStringCenteredX(title), y + SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM, title);
|
||||||
display->setFont(FONT_SMALL);
|
display->setFont(FONT_SMALL);
|
||||||
|
|
||||||
// Draw region in upper left
|
// Draw region in upper left
|
||||||
if (upperMsg)
|
if (upperMsg)
|
||||||
display->drawString(x + 0, y + 0, upperMsg);
|
display->drawString(x + 0, y + 0, upperMsg);
|
||||||
@@ -855,6 +968,7 @@ void UIRenderer::drawIconScreen(const char *upperMsg, OLEDDisplay *display, OLED
|
|||||||
screen->forceDisplay();
|
screen->forceDisplay();
|
||||||
|
|
||||||
display->setTextAlignment(TEXT_ALIGN_LEFT); // Restore left align, just to be kind to any other unsuspecting code
|
display->setTextAlignment(TEXT_ALIGN_LEFT); // Restore left align, just to be kind to any other unsuspecting code
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// ****************************
|
// ****************************
|
||||||
@@ -879,7 +993,26 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU
|
|||||||
config.display.heading_bold = false;
|
config.display.heading_bold = false;
|
||||||
|
|
||||||
const char *displayLine = ""; // Initialize to empty string by default
|
const char *displayLine = ""; // Initialize to empty string by default
|
||||||
if (config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
|
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||||
|
|
||||||
|
bool usePhoneGPS = (ourNode && nodeDB->hasValidPosition(ourNode) &&
|
||||||
|
config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED);
|
||||||
|
|
||||||
|
if (usePhoneGPS) {
|
||||||
|
// Phone-provided GPS is active
|
||||||
|
displayLine = "Phone GPS";
|
||||||
|
int yOffset = (isHighResolution) ? 3 : 1;
|
||||||
|
if (isHighResolution) {
|
||||||
|
NodeListRenderer::drawScaledXBitmap16x16(x, getTextPositions(display)[line] + yOffset - 5, imgSatellite_width,
|
||||||
|
imgSatellite_height, imgSatellite, display);
|
||||||
|
} else {
|
||||||
|
display->drawXbm(x + 1, getTextPositions(display)[line] + yOffset, imgSatellite_width, imgSatellite_height,
|
||||||
|
imgSatellite);
|
||||||
|
}
|
||||||
|
int xOffset = (isHighResolution) ? 6 : 0;
|
||||||
|
display->drawString(x + 11 + xOffset, getTextPositions(display)[line++], displayLine);
|
||||||
|
} else if (config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
|
||||||
|
// GPS disabled / not present
|
||||||
if (config.position.fixed_position) {
|
if (config.position.fixed_position) {
|
||||||
displayLine = "Fixed GPS";
|
displayLine = "Fixed GPS";
|
||||||
} else {
|
} else {
|
||||||
@@ -896,6 +1029,7 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU
|
|||||||
int xOffset = (isHighResolution) ? 6 : 0;
|
int xOffset = (isHighResolution) ? 6 : 0;
|
||||||
display->drawString(x + 11 + xOffset, getTextPositions(display)[line++], displayLine);
|
display->drawString(x + 11 + xOffset, getTextPositions(display)[line++], displayLine);
|
||||||
} else {
|
} else {
|
||||||
|
// Onboard GPS
|
||||||
UIRenderer::drawGps(display, 0, getTextPositions(display)[line++], gpsStatus);
|
UIRenderer::drawGps(display, 0, getTextPositions(display)[line++], gpsStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -922,36 +1056,52 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU
|
|||||||
|
|
||||||
// If GPS is off, no need to display these parts
|
// If GPS is off, no need to display these parts
|
||||||
if (strcmp(displayLine, "GPS off") != 0 && strcmp(displayLine, "No GPS") != 0) {
|
if (strcmp(displayLine, "GPS off") != 0 && strcmp(displayLine, "No GPS") != 0) {
|
||||||
|
// === Second Row: Last GPS Fix ===
|
||||||
|
if (gpsStatus->getLastFixMillis() > 0) {
|
||||||
|
uint32_t delta = (millis() - gpsStatus->getLastFixMillis()) / 1000; // seconds since last fix
|
||||||
|
uint32_t days = delta / 86400;
|
||||||
|
uint32_t hours = (delta % 86400) / 3600;
|
||||||
|
uint32_t mins = (delta % 3600) / 60;
|
||||||
|
uint32_t secs = delta % 60;
|
||||||
|
|
||||||
// === Second Row: Date ===
|
char buf[32];
|
||||||
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true);
|
#if defined(USE_EINK)
|
||||||
char datetimeStr[25];
|
// E-Ink: skip seconds, show only days/hours/mins
|
||||||
bool showTime = false; // set to true for full datetime
|
if (days > 0) {
|
||||||
UIRenderer::formatDateTime(datetimeStr, sizeof(datetimeStr), rtc_sec, display, showTime);
|
snprintf(buf, sizeof(buf), "Last: %ud %uh", days, hours);
|
||||||
char fullLine[40];
|
} else if (hours > 0) {
|
||||||
snprintf(fullLine, sizeof(fullLine), " Date: %s", datetimeStr);
|
snprintf(buf, sizeof(buf), "Last: %uh %um", hours, mins);
|
||||||
display->drawString(0, getTextPositions(display)[line++], fullLine);
|
} else {
|
||||||
|
snprintf(buf, sizeof(buf), "Last: %um", mins);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// Non E-Ink: include seconds where useful
|
||||||
|
if (days > 0) {
|
||||||
|
snprintf(buf, sizeof(buf), "Last: %ud %uh", days, hours);
|
||||||
|
} else if (hours > 0) {
|
||||||
|
snprintf(buf, sizeof(buf), "Last: %uh %um", hours, mins);
|
||||||
|
} else if (mins > 0) {
|
||||||
|
snprintf(buf, sizeof(buf), "Last: %um %us", mins, secs);
|
||||||
|
} else {
|
||||||
|
snprintf(buf, sizeof(buf), "Last: %us", secs);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// === Third Row: Latitude ===
|
display->drawString(0, getTextPositions(display)[line++], buf);
|
||||||
char latStr[32];
|
|
||||||
snprintf(latStr, sizeof(latStr), " Lat: %.5f", geoCoord.getLatitude() * 1e-7);
|
|
||||||
display->drawString(x, getTextPositions(display)[line++], latStr);
|
|
||||||
|
|
||||||
// === Fourth Row: Longitude ===
|
|
||||||
char lonStr[32];
|
|
||||||
snprintf(lonStr, sizeof(lonStr), " Lon: %.5f", geoCoord.getLongitude() * 1e-7);
|
|
||||||
display->drawString(x, getTextPositions(display)[line++], lonStr);
|
|
||||||
|
|
||||||
// === Fifth Row: Altitude ===
|
|
||||||
char DisplayLineTwo[32] = {0};
|
|
||||||
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
|
|
||||||
snprintf(DisplayLineTwo, sizeof(DisplayLineTwo), " Alt: %.0fft", geoCoord.getAltitude() * METERS_TO_FEET);
|
|
||||||
} else {
|
} else {
|
||||||
snprintf(DisplayLineTwo, sizeof(DisplayLineTwo), " Alt: %.0im", geoCoord.getAltitude());
|
display->drawString(0, getTextPositions(display)[line++], "Last: ?");
|
||||||
}
|
}
|
||||||
display->drawString(x, getTextPositions(display)[line++], DisplayLineTwo);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// === Third Row: Line 1 GPS Info ===
|
||||||
|
UIRenderer::drawGpsCoordinates(display, x, getTextPositions(display)[line++], gpsStatus, "line1");
|
||||||
|
|
||||||
|
if (uiconfig.gps_format != meshtastic_DeviceUIConfig_GpsCoordinateFormat_OLC &&
|
||||||
|
uiconfig.gps_format != meshtastic_DeviceUIConfig_GpsCoordinateFormat_MLS) {
|
||||||
|
// === Fourth Row: Line 2 GPS Info ===
|
||||||
|
UIRenderer::drawGpsCoordinates(display, x, getTextPositions(display)[line++], gpsStatus, "line2");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if !defined(M5STACK_UNITC6L)
|
||||||
// === Draw Compass if heading is valid ===
|
// === Draw Compass if heading is valid ===
|
||||||
if (validHeading) {
|
if (validHeading) {
|
||||||
// --- Compass Rendering: landscape (wide) screens use original side-aligned logic ---
|
// --- Compass Rendering: landscape (wide) screens use original side-aligned logic ---
|
||||||
@@ -1034,6 +1184,7 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USERPREFS_OEM_TEXT
|
#ifdef USERPREFS_OEM_TEXT
|
||||||
@@ -1190,7 +1341,6 @@ void UIRenderer::drawNavigationBar(OLEDDisplay *display, OLEDDisplayUiState *sta
|
|||||||
display->setColor(WHITE);
|
display->setColor(WHITE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Knock the corners off the square
|
// Knock the corners off the square
|
||||||
display->setColor(BLACK);
|
display->setColor(BLACK);
|
||||||
display->drawRect(rectX, y - 2, 1, 1);
|
display->drawRect(rectX, y - 2, 1, 1);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "NodeDB.h"
|
||||||
#include "graphics/Screen.h"
|
#include "graphics/Screen.h"
|
||||||
#include "graphics/emotes.h"
|
#include "graphics/emotes.h"
|
||||||
#include <OLEDDisplay.h>
|
#include <OLEDDisplay.h>
|
||||||
@@ -37,7 +38,8 @@ class UIRenderer
|
|||||||
|
|
||||||
// GPS status functions
|
// GPS status functions
|
||||||
static void drawGps(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gpsStatus);
|
static void drawGps(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gpsStatus);
|
||||||
static void drawGpsCoordinates(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gpsStatus);
|
static void drawGpsCoordinates(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gpsStatus,
|
||||||
|
const char *mode = "line1");
|
||||||
static void drawGpsAltitude(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gpsStatus);
|
static void drawGpsAltitude(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gpsStatus);
|
||||||
static void drawGpsPowerStatus(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gpsStatus);
|
static void drawGpsPowerStatus(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gpsStatus);
|
||||||
|
|
||||||
|
|||||||
@@ -118,8 +118,8 @@ const uint8_t icon_radio[] PROGMEM = {
|
|||||||
0xA9 // Row 7: #..#.#.#
|
0xA9 // Row 7: #..#.#.#
|
||||||
};
|
};
|
||||||
|
|
||||||
// 🪙 Memory Icon
|
// 🪙 System Icon
|
||||||
const uint8_t icon_memory[] PROGMEM = {
|
const uint8_t icon_system[] PROGMEM = {
|
||||||
0x24, // Row 0: ..#..#..
|
0x24, // Row 0: ..#..#..
|
||||||
0x3C, // Row 1: ..####..
|
0x3C, // Row 1: ..####..
|
||||||
0xC3, // Row 2: ##....##
|
0xC3, // Row 2: ##....##
|
||||||
@@ -287,6 +287,81 @@ const uint8_t digital_icon_clock[] PROGMEM = {0b00111100, 0b01000010, 0b10000101
|
|||||||
#define analog_icon_clock_height 8
|
#define analog_icon_clock_height 8
|
||||||
const uint8_t analog_icon_clock[] PROGMEM = {0b11111111, 0b01000010, 0b00100100, 0b00011000,
|
const uint8_t analog_icon_clock[] PROGMEM = {0b11111111, 0b01000010, 0b00100100, 0b00011000,
|
||||||
0b00100100, 0b01000010, 0b01000010, 0b11111111};
|
0b00100100, 0b01000010, 0b01000010, 0b11111111};
|
||||||
|
#ifdef M5STACK_UNITC6L
|
||||||
|
#include "img/icon_small.xbm"
|
||||||
|
#else
|
||||||
|
#define chirpy_width 38
|
||||||
|
#define chirpy_height 50
|
||||||
|
static unsigned char chirpy[] = {
|
||||||
|
0xfe, 0xff, 0xff, 0xff, 0xdf, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x80, 0xe3, 0x01,
|
||||||
|
0x00, 0x00, 0xc0, 0xe7, 0x01, 0x00, 0x00, 0xc0, 0xe7, 0x01, 0x00, 0x00, 0xc0, 0xe7, 0x01, 0x00, 0x00, 0x80, 0xe3, 0x01, 0x00,
|
||||||
|
0x00, 0x00, 0xe0, 0x81, 0xff, 0xff, 0x7f, 0xe0, 0xc1, 0xff, 0xff, 0xff, 0xe0, 0xc1, 0xff, 0xff, 0xff, 0xe0, 0xc1, 0xcf, 0x7f,
|
||||||
|
0xfe, 0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0x87, 0x3f, 0xfc,
|
||||||
|
0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0,
|
||||||
|
0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1,
|
||||||
|
0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0xcf, 0x7f, 0xfe, 0xe0, 0xc1, 0xff, 0xff, 0xff, 0xe0, 0xc1, 0xff, 0xff, 0xff, 0xe0, 0x81, 0xff,
|
||||||
|
0xff, 0x7f, 0xe0, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0xc3, 0x00, 0xe0, 0x01, 0x00, 0xc3,
|
||||||
|
0x00, 0xe0, 0x01, 0x80, 0xe1, 0x01, 0xe0, 0x01, 0x80, 0xe1, 0x01, 0xe0, 0x01, 0xc0, 0x30, 0x03, 0xe0, 0x01, 0xc0, 0x30, 0x03,
|
||||||
|
0xe0, 0x01, 0x60, 0x18, 0x06, 0xe0, 0x01, 0x60, 0x18, 0x06, 0xe0, 0x01, 0x30, 0x0c, 0x0c, 0xe0, 0x01, 0x30, 0x0c, 0x0c, 0xe0,
|
||||||
|
0x01, 0x18, 0x06, 0x18, 0xe0, 0x01, 0x18, 0x06, 0x18, 0xe0, 0x01, 0x0c, 0x03, 0x30, 0xe0, 0x01, 0x0c, 0x03, 0x30, 0xe0, 0x01,
|
||||||
|
0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0xe0, 0xfe, 0xff, 0xff, 0xff, 0xdf};
|
||||||
|
|
||||||
|
#define chirpy_width_hirez 76
|
||||||
|
#define chirpy_height_hirez 100
|
||||||
|
static unsigned char chirpy_hirez[] = {
|
||||||
|
0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0x03,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xc0, 0x0f, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x0f, 0xfc, 0x03, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0xf0, 0x3f, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xf0, 0x3f, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0xf0, 0x3f, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xc0, 0x0f, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x0f, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00,
|
||||||
|
0xfc, 0x03, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0xfc, 0x03, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xfc,
|
||||||
|
0x03, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xfc, 0x03,
|
||||||
|
0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0xff, 0xf0, 0xff, 0x3f, 0xfc, 0xff, 0x00, 0xfc, 0x03, 0xf0,
|
||||||
|
0xff, 0xf0, 0xff, 0x3f, 0xfc, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f,
|
||||||
|
0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0,
|
||||||
|
0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff,
|
||||||
|
0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f,
|
||||||
|
0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0,
|
||||||
|
0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff,
|
||||||
|
0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00,
|
||||||
|
0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc,
|
||||||
|
0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03,
|
||||||
|
0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0,
|
||||||
|
0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f,
|
||||||
|
0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0,
|
||||||
|
0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0xff, 0xf0, 0xff,
|
||||||
|
0x3f, 0xfc, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0xff, 0xf0, 0xff, 0x3f, 0xfc, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0x00, 0xfc, 0x03, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xfc, 0x03, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f,
|
||||||
|
0x00, 0xfc, 0x03, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc,
|
||||||
|
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0xfc, 0x03,
|
||||||
|
0x00, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00,
|
||||||
|
0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xc0, 0x03, 0xfc, 0x03, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00,
|
||||||
|
0xc0, 0x03, 0xfc, 0x03, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xc0, 0x03, 0xfc, 0x03, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xc0,
|
||||||
|
0x03, 0xfc, 0x03, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xf0, 0x00, 0x0f, 0x0f, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xf0, 0x00,
|
||||||
|
0x0f, 0x0f, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xf0, 0x00, 0x0f, 0x0f, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xf0, 0x00, 0x0f,
|
||||||
|
0x0f, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x3c, 0xc0, 0x03, 0x3c, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x3c, 0xc0, 0x03, 0x3c,
|
||||||
|
0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x3c, 0xc0, 0x03, 0x3c, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x3c, 0xc0, 0x03, 0x3c, 0x00,
|
||||||
|
0x00, 0xfc, 0x03, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0xf0, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0xf0, 0x00, 0x00,
|
||||||
|
0xfc, 0x03, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0xf0, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0xf0, 0x00, 0x00, 0xfc,
|
||||||
|
0x03, 0x00, 0xc0, 0x03, 0x3c, 0x00, 0xc0, 0x03, 0x00, 0xfc, 0x03, 0x00, 0xc0, 0x03, 0x3c, 0x00, 0xc0, 0x03, 0x00, 0xfc, 0x03,
|
||||||
|
0x00, 0xc0, 0x03, 0x3c, 0x00, 0xc0, 0x03, 0x00, 0xfc, 0x03, 0x00, 0xc0, 0x03, 0x3c, 0x00, 0xc0, 0x03, 0x00, 0xfc, 0x03, 0x00,
|
||||||
|
0xf0, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0xfc, 0x03, 0x00, 0xf0, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0xfc, 0x03, 0x00, 0xf0,
|
||||||
|
0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0xfc, 0x03, 0x00, 0xf0, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xf3, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3};
|
||||||
|
|
||||||
|
#define chirpy_small_image_width 8
|
||||||
|
#define chirpy_small_image_height 8
|
||||||
|
static unsigned char small_chirpy[] = {0x7f, 0x41, 0x55, 0x55, 0x55, 0x55, 0x41, 0x7f};
|
||||||
|
|
||||||
#include "img/icon.xbm"
|
#include "img/icon.xbm"
|
||||||
|
#endif
|
||||||
static_assert(sizeof(icon_bits) >= 0, "Silence unused variable warning");
|
static_assert(sizeof(icon_bits) >= 0, "Silence unused variable warning");
|
||||||
116
src/graphics/img/icon_small.xbm
Normal file
116
src/graphics/img/icon_small.xbm
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
#ifndef USERPREFS_HAS_SPLASH
|
||||||
|
#define icon_width 50
|
||||||
|
#define icon_height 20
|
||||||
|
static uint8_t icon_bits[] = {
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x80, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x80,
|
||||||
|
0x07, 0xc0, 0x07, 0x00, 0x00, 0x00, 0xc0, 0x0f,
|
||||||
|
0xc0, 0x0f, 0x00, 0x00, 0x00, 0xe0, 0x07, 0xe0,
|
||||||
|
0x0f, 0x00, 0x00, 0x00, 0xe0, 0x07, 0xf0, 0x1f,
|
||||||
|
0x00, 0x00, 0x00, 0xf0, 0x03, 0xf0, 0x3f, 0x00,
|
||||||
|
0x00, 0x00, 0xf8, 0x03, 0xf8, 0x7f, 0x00, 0x00,
|
||||||
|
0x00, 0xf8, 0x01, 0xfc, 0x7e, 0x00, 0x00, 0x00,
|
||||||
|
0xfc, 0x00, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0xfe,
|
||||||
|
0x00, 0x7e, 0xf8, 0x00, 0x00, 0x00, 0x7e, 0x00,
|
||||||
|
0x3f, 0xf8, 0x01, 0x00, 0x00, 0x3f, 0x00, 0x1f,
|
||||||
|
0xf0, 0x01, 0x00, 0x00, 0x1f, 0x80, 0x1f, 0xe0,
|
||||||
|
0x03, 0x00, 0x80, 0x1f, 0xc0, 0x0f, 0xe0, 0x03,
|
||||||
|
0x00, 0x80, 0x0f, 0xc0, 0x07, 0xc0, 0x07, 0x00,
|
||||||
|
0xc0, 0x0f, 0xe0, 0x07, 0x80, 0x0f, 0x00, 0xe0,
|
||||||
|
0x07, 0xf0, 0x03, 0x80, 0x1f, 0x00, 0xe0, 0x03,
|
||||||
|
0xf8, 0x03, 0x00, 0x1f, 0x00, 0xf0, 0x03, 0xf8,
|
||||||
|
0x01, 0x00, 0x3f, 0x00, 0xf8, 0x01, 0xfc, 0x00,
|
||||||
|
0x00, 0x7e, 0x00, 0xfc, 0x00, 0xfe, 0x00, 0x00,
|
||||||
|
0x7e, 0x00, 0xfc, 0x00, 0x7e, 0x00, 0x00, 0xfc,
|
||||||
|
0x00, 0x7e, 0x00, 0x3f, 0x00, 0x00, 0xf8, 0x00,
|
||||||
|
0x7e, 0x00, 0x3e, 0x00, 0x00, 0xf8, 0x00, 0x38,
|
||||||
|
0x00, 0x1c, 0x00, 0x00, 0x70, 0x00, 0x10, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00 };
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Chirpy image definitions for M5STACK_UNITC6L compatibility
|
||||||
|
#define chirpy_width 38
|
||||||
|
#define chirpy_height 50
|
||||||
|
static unsigned char chirpy[] = {
|
||||||
|
0xfe, 0xff, 0xff, 0xff, 0xdf, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x80, 0xe3, 0x01,
|
||||||
|
0x00, 0x00, 0xc0, 0xe7, 0x01, 0x00, 0x00, 0xc0, 0xe7, 0x01, 0x00, 0x00, 0xc0, 0xe7, 0x01, 0x00, 0x00, 0x80, 0xe3, 0x01, 0x00,
|
||||||
|
0x00, 0x00, 0xe0, 0x81, 0xff, 0xff, 0x7f, 0xe0, 0xc1, 0xff, 0xff, 0xff, 0xe0, 0xc1, 0xff, 0xff, 0xff, 0xe0, 0xc1, 0xcf, 0x7f,
|
||||||
|
0xfe, 0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0x87, 0x3f, 0xfc,
|
||||||
|
0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0,
|
||||||
|
0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1,
|
||||||
|
0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0xcf, 0x7f, 0xfe, 0xe0, 0xc1, 0xff, 0xff, 0xff, 0xe0, 0xc1, 0xff, 0xff, 0xff, 0xe0, 0x81, 0xff,
|
||||||
|
0xff, 0x7f, 0xe0, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0xc3, 0x00, 0xe0, 0x01, 0x00, 0xc3,
|
||||||
|
0x00, 0xe0, 0x01, 0x80, 0xe1, 0x01, 0xe0, 0x01, 0x80, 0xe1, 0x01, 0xe0, 0x01, 0xc0, 0x30, 0x03, 0xe0, 0x01, 0xc0, 0x30, 0x03,
|
||||||
|
0xe0, 0x01, 0x60, 0x18, 0x06, 0xe0, 0x01, 0x60, 0x18, 0x06, 0xe0, 0x01, 0x30, 0x0c, 0x0c, 0xe0, 0x01, 0x30, 0x0c, 0x0c, 0xe0,
|
||||||
|
0x01, 0x18, 0x06, 0x18, 0xe0, 0x01, 0x18, 0x06, 0x18, 0xe0, 0x01, 0x0c, 0x03, 0x30, 0xe0, 0x01, 0x0c, 0x03, 0x30, 0xe0, 0x01,
|
||||||
|
0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0xe0, 0xfe, 0xff, 0xff, 0xff, 0xdf};
|
||||||
|
|
||||||
|
#define chirpy_width_hirez 76
|
||||||
|
#define chirpy_height_hirez 100
|
||||||
|
static unsigned char chirpy_hirez[] = {
|
||||||
|
0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0x03,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xc0, 0x0f, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x0f, 0xfc, 0x03, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0xf0, 0x3f, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xf0, 0x3f, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0xf0, 0x3f, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xc0, 0x0f, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x0f, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00,
|
||||||
|
0xfc, 0x03, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0xfc,
|
||||||
|
0x03, 0xe0, 0x1f, 0xff, 0xff, 0xff, 0xf8, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03,
|
||||||
|
0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0,
|
||||||
|
0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f,
|
||||||
|
0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe,
|
||||||
|
0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff,
|
||||||
|
0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f,
|
||||||
|
0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0,
|
||||||
|
0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f,
|
||||||
|
0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00,
|
||||||
|
0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc,
|
||||||
|
0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03,
|
||||||
|
0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0,
|
||||||
|
0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f,
|
||||||
|
0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe,
|
||||||
|
0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff,
|
||||||
|
0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f,
|
||||||
|
0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0,
|
||||||
|
0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f,
|
||||||
|
0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00,
|
||||||
|
0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc,
|
||||||
|
0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03,
|
||||||
|
0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0,
|
||||||
|
0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x0f, 0xfe, 0xff, 0x7f, 0xf0, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0x1f,
|
||||||
|
0xff, 0xff, 0xff, 0xf8, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0xfc, 0x03, 0xe0, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0x7f, 0x00, 0xfc, 0x03, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x86, 0x61, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x86, 0x61, 0x00,
|
||||||
|
0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x86, 0x61, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x80, 0x87, 0xe1, 0x01, 0x00,
|
||||||
|
0x00, 0xfc, 0x03, 0x00, 0x00, 0x80, 0x87, 0xe1, 0x01, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xc0, 0x83, 0xc1, 0x03, 0x00, 0x00,
|
||||||
|
0xfc, 0x03, 0x00, 0x00, 0xc0, 0x83, 0xc1, 0x03, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xe0, 0x01, 0x80, 0x07, 0x00, 0x00, 0xfc,
|
||||||
|
0x03, 0x00, 0x00, 0xe0, 0x01, 0x80, 0x07, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x0f, 0x00, 0x00, 0xfc, 0x03,
|
||||||
|
0x00, 0x00, 0xf0, 0x00, 0x00, 0x0f, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x78, 0x00, 0x00, 0x1e, 0x00, 0x00, 0xfc, 0x03, 0x00,
|
||||||
|
0x00, 0x78, 0x00, 0x00, 0x1e, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00,
|
||||||
|
0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x78, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x1e,
|
||||||
|
0x00, 0x00, 0x78, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x0f, 0x00, 0x00, 0xf0, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x0f, 0x00,
|
||||||
|
0x00, 0xf0, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x80, 0x07, 0x00, 0x00, 0xe0, 0x01, 0x00, 0xfc, 0x03, 0x00, 0x80, 0x07, 0x00, 0x00,
|
||||||
|
0xe0, 0x01, 0x00, 0xfc, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 0xfc, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0,
|
||||||
|
0x03, 0x00, 0xfc, 0x03, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x80, 0x07, 0x00, 0xfc, 0x03, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x80, 0x07,
|
||||||
|
0x00, 0xfc, 0x03, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0xfc, 0x03, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00,
|
||||||
|
0xfc, 0x03, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0xfc, 0x03, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0xfc,
|
||||||
|
0x03, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0xfc, 0x03, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0xfc, 0x03,
|
||||||
|
0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0xfc, 0x03, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0xfc, 0x03, 0x00,
|
||||||
|
0x0f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0xfc, 0x03, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0xfc, 0x03, 0x80, 0x07,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, 0xfc, 0x03, 0x80, 0x07, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, 0xfc, 0x03, 0xc0, 0x03, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0xc0, 0x03, 0xfc, 0x03, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xfc, 0x03, 0xe0, 0x01, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x80, 0x07, 0xfc, 0x03, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0xfc, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x0f, 0xfc, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3
|
||||||
|
};
|
||||||
|
|
||||||
|
#define chirpy_small_image_width 8
|
||||||
|
#define chirpy_small_image_height 8
|
||||||
|
static unsigned char small_chirpy[] = {0x7f, 0x41, 0x55, 0x55, 0x55, 0x55, 0x41, 0x7f};
|
||||||
@@ -41,78 +41,78 @@ void tftSetup(void)
|
|||||||
PacketAPI::create(PacketServer::init());
|
PacketAPI::create(PacketServer::init());
|
||||||
deviceScreen->init(new PacketClient);
|
deviceScreen->init(new PacketClient);
|
||||||
#else
|
#else
|
||||||
if (settingsMap[displayPanel] != no_screen) {
|
if (portduino_config.displayPanel != no_screen) {
|
||||||
DisplayDriverConfig displayConfig;
|
DisplayDriverConfig displayConfig;
|
||||||
static char *panels[] = {"NOSCREEN", "X11", "FB", "ST7789", "ST7735", "ST7735S",
|
static char *panels[] = {"NOSCREEN", "X11", "FB", "ST7789", "ST7735", "ST7735S",
|
||||||
"ST7796", "ILI9341", "ILI9342", "ILI9486", "ILI9488", "HX8357D"};
|
"ST7796", "ILI9341", "ILI9342", "ILI9486", "ILI9488", "HX8357D"};
|
||||||
static char *touch[] = {"NOTOUCH", "XPT2046", "STMPE610", "GT911", "FT5x06"};
|
static char *touch[] = {"NOTOUCH", "XPT2046", "STMPE610", "GT911", "FT5x06"};
|
||||||
#if defined(USE_X11)
|
#if defined(USE_X11)
|
||||||
if (settingsMap[displayPanel] == x11) {
|
if (portduino_config.displayPanel == x11) {
|
||||||
if (settingsMap[displayWidth] && settingsMap[displayHeight])
|
if (portduino_config.displayWidth && portduino_config.displayHeight)
|
||||||
displayConfig = DisplayDriverConfig(DisplayDriverConfig::device_t::X11, (uint16_t)settingsMap[displayWidth],
|
displayConfig = DisplayDriverConfig(DisplayDriverConfig::device_t::X11, (uint16_t)portduino_config.displayWidth,
|
||||||
(uint16_t)settingsMap[displayHeight]);
|
(uint16_t)portduino_config.displayHeight);
|
||||||
else
|
else
|
||||||
displayConfig.device(DisplayDriverConfig::device_t::X11);
|
displayConfig.device(DisplayDriverConfig::device_t::X11);
|
||||||
} else
|
} else
|
||||||
#elif defined(USE_FRAMEBUFFER)
|
#elif defined(USE_FRAMEBUFFER)
|
||||||
if (settingsMap[displayPanel] == fb) {
|
if (portduino_config.displayPanel == fb) {
|
||||||
if (settingsMap[displayWidth] && settingsMap[displayHeight])
|
if (portduino_config.displayWidth && portduino_config.displayHeight)
|
||||||
displayConfig = DisplayDriverConfig(DisplayDriverConfig::device_t::FB, (uint16_t)settingsMap[displayWidth],
|
displayConfig = DisplayDriverConfig(DisplayDriverConfig::device_t::FB, (uint16_t)portduino_config.displayWidth,
|
||||||
(uint16_t)settingsMap[displayHeight]);
|
(uint16_t)portduino_config.displayHeight);
|
||||||
else
|
else
|
||||||
displayConfig.device(DisplayDriverConfig::device_t::FB);
|
displayConfig.device(DisplayDriverConfig::device_t::FB);
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
displayConfig.device(DisplayDriverConfig::device_t::CUSTOM_TFT)
|
displayConfig.device(DisplayDriverConfig::device_t::CUSTOM_TFT)
|
||||||
.panel(DisplayDriverConfig::panel_config_t{.type = panels[settingsMap[displayPanel]],
|
.panel(DisplayDriverConfig::panel_config_t{.type = panels[portduino_config.displayPanel],
|
||||||
.panel_width = (uint16_t)settingsMap[displayWidth],
|
.panel_width = (uint16_t)portduino_config.displayWidth,
|
||||||
.panel_height = (uint16_t)settingsMap[displayHeight],
|
.panel_height = (uint16_t)portduino_config.displayHeight,
|
||||||
.rotation = (bool)settingsMap[displayRotate],
|
.rotation = (bool)portduino_config.displayRotate,
|
||||||
.pin_cs = (int16_t)settingsMap[displayCS],
|
.pin_cs = (int16_t)portduino_config.displayCS.pin,
|
||||||
.pin_rst = (int16_t)settingsMap[displayReset],
|
.pin_rst = (int16_t)portduino_config.displayReset.pin,
|
||||||
.offset_x = (uint16_t)settingsMap[displayOffsetX],
|
.offset_x = (uint16_t)portduino_config.displayOffsetX,
|
||||||
.offset_y = (uint16_t)settingsMap[displayOffsetY],
|
.offset_y = (uint16_t)portduino_config.displayOffsetY,
|
||||||
.offset_rotation = (uint8_t)settingsMap[displayOffsetRotate],
|
.offset_rotation = (uint8_t)portduino_config.displayOffsetRotate,
|
||||||
.invert = settingsMap[displayInvert] ? true : false,
|
.invert = portduino_config.displayInvert ? true : false,
|
||||||
.rgb_order = (bool)settingsMap[displayRGBOrder],
|
.rgb_order = (bool)portduino_config.displayRGBOrder,
|
||||||
.dlen_16bit = settingsMap[displayPanel] == ili9486 ||
|
.dlen_16bit = portduino_config.displayPanel == ili9486 ||
|
||||||
settingsMap[displayPanel] == ili9488})
|
portduino_config.displayPanel == ili9488})
|
||||||
.bus(DisplayDriverConfig::bus_config_t{.freq_write = (uint32_t)settingsMap[displayBusFrequency],
|
.bus(DisplayDriverConfig::bus_config_t{.freq_write = (uint32_t)portduino_config.displayBusFrequency,
|
||||||
.freq_read = 16000000,
|
.freq_read = 16000000,
|
||||||
.spi{.pin_dc = (int8_t)settingsMap[displayDC],
|
.spi{.pin_dc = (int8_t)portduino_config.displayDC.pin,
|
||||||
.use_lock = true,
|
.use_lock = true,
|
||||||
.spi_host = (uint16_t)settingsMap[displayspidev]}})
|
.spi_host = (uint16_t)portduino_config.display_spi_dev_int}})
|
||||||
.input(DisplayDriverConfig::input_config_t{.keyboardDevice = settingsStrings[keyboardDevice],
|
.input(DisplayDriverConfig::input_config_t{.keyboardDevice = portduino_config.keyboardDevice,
|
||||||
.pointerDevice = settingsStrings[pointerDevice]})
|
.pointerDevice = portduino_config.pointerDevice})
|
||||||
.light(DisplayDriverConfig::light_config_t{.pin_bl = (int16_t)settingsMap[displayBacklight],
|
.light(DisplayDriverConfig::light_config_t{.pin_bl = (int16_t)portduino_config.displayBacklight.pin,
|
||||||
.pwm_channel = (int8_t)settingsMap[displayBacklightPWMChannel],
|
.pwm_channel = (int8_t)portduino_config.displayBacklightPWMChannel.pin,
|
||||||
.invert = (bool)settingsMap[displayBacklightInvert]});
|
.invert = (bool)portduino_config.displayBacklightInvert});
|
||||||
if (settingsMap[touchscreenI2CAddr] == -1) {
|
if (portduino_config.touchscreenI2CAddr == -1) {
|
||||||
displayConfig.touch(
|
displayConfig.touch(
|
||||||
DisplayDriverConfig::touch_config_t{.type = touch[settingsMap[touchscreenModule]],
|
DisplayDriverConfig::touch_config_t{.type = touch[portduino_config.touchscreenModule],
|
||||||
.freq = (uint32_t)settingsMap[touchscreenBusFrequency],
|
.freq = (uint32_t)portduino_config.touchscreenBusFrequency,
|
||||||
.pin_int = (int16_t)settingsMap[touchscreenIRQ],
|
.pin_int = (int16_t)portduino_config.touchscreenIRQ.pin,
|
||||||
.offset_rotation = (uint8_t)settingsMap[touchscreenRotate],
|
.offset_rotation = (uint8_t)portduino_config.touchscreenRotate,
|
||||||
.spi{
|
.spi{
|
||||||
.spi_host = (int8_t)settingsMap[touchscreenspidev],
|
.spi_host = (int8_t)portduino_config.touchscreen_spi_dev_int,
|
||||||
},
|
},
|
||||||
.pin_cs = (int16_t)settingsMap[touchscreenCS]});
|
.pin_cs = (int16_t)portduino_config.touchscreenCS.pin});
|
||||||
} else {
|
} else {
|
||||||
displayConfig.touch(DisplayDriverConfig::touch_config_t{
|
displayConfig.touch(DisplayDriverConfig::touch_config_t{
|
||||||
.type = touch[settingsMap[touchscreenModule]],
|
.type = touch[portduino_config.touchscreenModule],
|
||||||
.freq = (uint32_t)settingsMap[touchscreenBusFrequency],
|
.freq = (uint32_t)portduino_config.touchscreenBusFrequency,
|
||||||
.x_min = 0,
|
.x_min = 0,
|
||||||
.x_max =
|
.x_max = (int16_t)((portduino_config.touchscreenRotate & 1 ? portduino_config.displayWidth
|
||||||
(int16_t)((settingsMap[touchscreenRotate] & 1 ? settingsMap[displayWidth] : settingsMap[displayHeight]) -
|
: portduino_config.displayHeight) -
|
||||||
1),
|
1),
|
||||||
.y_min = 0,
|
.y_min = 0,
|
||||||
.y_max =
|
.y_max = (int16_t)((portduino_config.touchscreenRotate & 1 ? portduino_config.displayHeight
|
||||||
(int16_t)((settingsMap[touchscreenRotate] & 1 ? settingsMap[displayHeight] : settingsMap[displayWidth]) -
|
: portduino_config.displayWidth) -
|
||||||
1),
|
1),
|
||||||
.pin_int = (int16_t)settingsMap[touchscreenIRQ],
|
.pin_int = (int16_t)portduino_config.touchscreenIRQ.pin,
|
||||||
.offset_rotation = (uint8_t)settingsMap[touchscreenRotate],
|
.offset_rotation = (uint8_t)portduino_config.touchscreenRotate,
|
||||||
.i2c{.i2c_addr = (uint8_t)settingsMap[touchscreenI2CAddr]}});
|
.i2c{.i2c_addr = (uint8_t)portduino_config.touchscreenI2CAddr}});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
deviceScreen = &DeviceScreen::create(&displayConfig);
|
deviceScreen = &DeviceScreen::create(&displayConfig);
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ void ExpressLRSFiveWay::determineAction(KeyType key, PressLength length)
|
|||||||
// Feed input to the canned messages module
|
// Feed input to the canned messages module
|
||||||
void ExpressLRSFiveWay::sendKey(input_broker_event key)
|
void ExpressLRSFiveWay::sendKey(input_broker_event key)
|
||||||
{
|
{
|
||||||
InputEvent e;
|
InputEvent e = {};
|
||||||
e.source = inputSourceName;
|
e.source = inputSourceName;
|
||||||
e.inputEvent = key;
|
e.inputEvent = key;
|
||||||
notifyObservers(&e);
|
notifyObservers(&e);
|
||||||
|
|||||||
@@ -33,9 +33,9 @@ int32_t LinuxInput::runOnce()
|
|||||||
{
|
{
|
||||||
|
|
||||||
if (firstTime) {
|
if (firstTime) {
|
||||||
if (settingsStrings[keyboardDevice] == "")
|
if (portduino_config.keyboardDevice == "")
|
||||||
return disable();
|
return disable();
|
||||||
fd = open(settingsStrings[keyboardDevice].c_str(), O_RDWR);
|
fd = open(portduino_config.keyboardDevice.c_str(), O_RDWR);
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
return disable();
|
return disable();
|
||||||
ret = ioctl(fd, EVIOCGRAB, (void *)1);
|
ret = ioctl(fd, EVIOCGRAB, (void *)1);
|
||||||
@@ -73,7 +73,7 @@ int32_t LinuxInput::runOnce()
|
|||||||
int rd = read(events[i].data.fd, ev, sizeof(ev));
|
int rd = read(events[i].data.fd, ev, sizeof(ev));
|
||||||
assert(rd > ((signed int)sizeof(struct input_event)));
|
assert(rd > ((signed int)sizeof(struct input_event)));
|
||||||
for (int j = 0; j < rd / ((signed int)sizeof(struct input_event)); j++) {
|
for (int j = 0; j < rd / ((signed int)sizeof(struct input_event)); j++) {
|
||||||
InputEvent e;
|
InputEvent e = {};
|
||||||
e.inputEvent = INPUT_BROKER_NONE;
|
e.inputEvent = INPUT_BROKER_NONE;
|
||||||
e.source = this->_originName;
|
e.source = this->_originName;
|
||||||
e.kbchar = 0;
|
e.kbchar = 0;
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ void RotaryEncoderInterruptBase::init(
|
|||||||
|
|
||||||
int32_t RotaryEncoderInterruptBase::runOnce()
|
int32_t RotaryEncoderInterruptBase::runOnce()
|
||||||
{
|
{
|
||||||
InputEvent e;
|
InputEvent e = {};
|
||||||
e.inputEvent = INPUT_BROKER_NONE;
|
e.inputEvent = INPUT_BROKER_NONE;
|
||||||
e.source = this->_originName;
|
e.source = this->_originName;
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@ RotaryEncoderInterruptBaseStateType RotaryEncoderInterruptBase::intHandler(bool
|
|||||||
// Logic to prevent bouncing.
|
// Logic to prevent bouncing.
|
||||||
newState = ROTARY_EVENT_CLEARED;
|
newState = ROTARY_EVENT_CLEARED;
|
||||||
}
|
}
|
||||||
setIntervalFromNow(50); // TODO: this modifies a non-volatile variable!
|
setIntervalFromNow(ROTARY_DELAY); // TODO: this modifies a non-volatile variable!
|
||||||
|
|
||||||
return newState;
|
return newState;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ bool SeesawRotary::init()
|
|||||||
|
|
||||||
int32_t SeesawRotary::runOnce()
|
int32_t SeesawRotary::runOnce()
|
||||||
{
|
{
|
||||||
InputEvent e;
|
InputEvent e = {};
|
||||||
e.inputEvent = INPUT_BROKER_NONE;
|
e.inputEvent = INPUT_BROKER_NONE;
|
||||||
bool currentlyPressed = !ss.digitalRead(SS_SWITCH);
|
bool currentlyPressed = !ss.digitalRead(SS_SWITCH);
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ SerialKeyboard::SerialKeyboard(const char *name) : concurrency::OSThread(name)
|
|||||||
|
|
||||||
void SerialKeyboard::erase()
|
void SerialKeyboard::erase()
|
||||||
{
|
{
|
||||||
InputEvent e;
|
InputEvent e = {};
|
||||||
e.inputEvent = INPUT_BROKER_BACK;
|
e.inputEvent = INPUT_BROKER_BACK;
|
||||||
e.kbchar = 0x08;
|
e.kbchar = 0x08;
|
||||||
e.source = this->_originName;
|
e.source = this->_originName;
|
||||||
@@ -80,7 +80,7 @@ int32_t SerialKeyboard::runOnce()
|
|||||||
|
|
||||||
if (keys < prevKeys) { // a new key has been pressed (and not released), doesn't works for multiple presses at once but
|
if (keys < prevKeys) { // a new key has been pressed (and not released), doesn't works for multiple presses at once but
|
||||||
// shouldn't be a limitation
|
// shouldn't be a limitation
|
||||||
InputEvent e;
|
InputEvent e = {};
|
||||||
e.inputEvent = INPUT_BROKER_NONE;
|
e.inputEvent = INPUT_BROKER_NONE;
|
||||||
e.source = this->_originName;
|
e.source = this->_originName;
|
||||||
// SELECT OR SEND OR CANCEL EVENT
|
// SELECT OR SEND OR CANCEL EVENT
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ TouchScreenImpl1::TouchScreenImpl1(uint16_t width, uint16_t height, bool (*getTo
|
|||||||
void TouchScreenImpl1::init()
|
void TouchScreenImpl1::init()
|
||||||
{
|
{
|
||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
if (settingsMap[touchscreenModule]) {
|
if (portduino_config.touchscreenModule) {
|
||||||
TouchScreenBase::init(true);
|
TouchScreenBase::init(true);
|
||||||
inputBroker->registerSource(this);
|
inputBroker->registerSource(this);
|
||||||
} else {
|
} else {
|
||||||
@@ -47,7 +47,7 @@ bool TouchScreenImpl1::getTouch(int16_t &x, int16_t &y)
|
|||||||
*/
|
*/
|
||||||
void TouchScreenImpl1::onEvent(const TouchEvent &event)
|
void TouchScreenImpl1::onEvent(const TouchEvent &event)
|
||||||
{
|
{
|
||||||
InputEvent e;
|
InputEvent e = {};
|
||||||
e.source = event.source;
|
e.source = event.source;
|
||||||
e.kbchar = 0;
|
e.kbchar = 0;
|
||||||
e.touchX = event.x;
|
e.touchX = event.x;
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ void TrackballInterruptBase::init(uint8_t pinDown, uint8_t pinUp, uint8_t pinLef
|
|||||||
|
|
||||||
int32_t TrackballInterruptBase::runOnce()
|
int32_t TrackballInterruptBase::runOnce()
|
||||||
{
|
{
|
||||||
InputEvent e;
|
InputEvent e = {};
|
||||||
e.inputEvent = INPUT_BROKER_NONE;
|
e.inputEvent = INPUT_BROKER_NONE;
|
||||||
#if defined(T_DECK) // T-deck gets a super-simple debounce on trackball
|
#if defined(T_DECK) // T-deck gets a super-simple debounce on trackball
|
||||||
if (this->action == TB_ACTION_PRESSED) {
|
if (this->action == TB_ACTION_PRESSED) {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#ifndef TB_DIRECTION
|
#ifndef TB_DIRECTION
|
||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
#include "PortduinoGlue.h"
|
#include "PortduinoGlue.h"
|
||||||
#define TB_DIRECTION (PinStatus) settingsMap[tbDirection]
|
#define TB_DIRECTION (PinStatus) portduino_config.lora_usb_vid
|
||||||
#else
|
#else
|
||||||
#define TB_DIRECTION RISING
|
#define TB_DIRECTION RISING
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ void UpDownInterruptBase::init(uint8_t pinDown, uint8_t pinUp, uint8_t pinPress,
|
|||||||
|
|
||||||
int32_t UpDownInterruptBase::runOnce()
|
int32_t UpDownInterruptBase::runOnce()
|
||||||
{
|
{
|
||||||
InputEvent e;
|
InputEvent e = {};
|
||||||
e.inputEvent = INPUT_BROKER_NONE;
|
e.inputEvent = INPUT_BROKER_NONE;
|
||||||
unsigned long now = millis();
|
unsigned long now = millis();
|
||||||
if (this->action == UPDOWN_ACTION_PRESSED) {
|
if (this->action == UPDOWN_ACTION_PRESSED) {
|
||||||
|
|||||||
95
src/input/i2cButton.cpp
Normal file
95
src/input/i2cButton.cpp
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
#include "i2cButton.h"
|
||||||
|
#include "meshUtils.h"
|
||||||
|
|
||||||
|
#include "configuration.h"
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
|
||||||
|
#include "MeshService.h"
|
||||||
|
#include "RadioLibInterface.h"
|
||||||
|
#include "buzz.h"
|
||||||
|
#include "input/InputBroker.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include "modules/CannedMessageModule.h"
|
||||||
|
#include "modules/ExternalNotificationModule.h"
|
||||||
|
#include "power.h"
|
||||||
|
#include "sleep.h"
|
||||||
|
#ifdef ARCH_PORTDUINO
|
||||||
|
#include "platform/portduino/PortduinoGlue.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
i2cButtonThread *i2cButton;
|
||||||
|
|
||||||
|
using namespace concurrency;
|
||||||
|
|
||||||
|
extern void i2c_read_byte(uint8_t addr, uint8_t reg, uint8_t *value);
|
||||||
|
|
||||||
|
extern void i2c_write_byte(uint8_t addr, uint8_t reg, uint8_t value);
|
||||||
|
|
||||||
|
#define PI4IO_M_ADDR 0x43
|
||||||
|
#define getbit(x, y) ((x) >> (y)&0x01)
|
||||||
|
#define PI4IO_REG_IRQ_STA 0x13
|
||||||
|
#define PI4IO_REG_IN_STA 0x0F
|
||||||
|
#define PI4IO_REG_CHIP_RESET 0x01
|
||||||
|
|
||||||
|
i2cButtonThread::i2cButtonThread(const char *name) : OSThread(name)
|
||||||
|
{
|
||||||
|
_originName = name;
|
||||||
|
if (inputBroker)
|
||||||
|
inputBroker->registerSource(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t i2cButtonThread::runOnce()
|
||||||
|
{
|
||||||
|
static bool btn1_pressed = false;
|
||||||
|
static uint32_t press_start_time = 0;
|
||||||
|
const uint32_t LONG_PRESS_TIME = 1000;
|
||||||
|
static bool long_press_triggered = false;
|
||||||
|
|
||||||
|
uint8_t in_data;
|
||||||
|
i2c_read_byte(PI4IO_M_ADDR, PI4IO_REG_IRQ_STA, &in_data);
|
||||||
|
i2c_write_byte(PI4IO_M_ADDR, PI4IO_REG_IRQ_STA, in_data);
|
||||||
|
if (getbit(in_data, 0)) {
|
||||||
|
uint8_t input_state;
|
||||||
|
i2c_read_byte(PI4IO_M_ADDR, PI4IO_REG_IN_STA, &input_state);
|
||||||
|
|
||||||
|
if (!getbit(input_state, 0)) {
|
||||||
|
if (!btn1_pressed) {
|
||||||
|
btn1_pressed = true;
|
||||||
|
press_start_time = millis();
|
||||||
|
long_press_triggered = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (btn1_pressed) {
|
||||||
|
btn1_pressed = false;
|
||||||
|
uint32_t press_duration = millis() - press_start_time;
|
||||||
|
if (long_press_triggered) {
|
||||||
|
long_press_triggered = false;
|
||||||
|
return 50;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (press_duration < LONG_PRESS_TIME) {
|
||||||
|
InputEvent evt;
|
||||||
|
evt.source = "UserButton";
|
||||||
|
evt.inputEvent = INPUT_BROKER_USER_PRESS;
|
||||||
|
evt.kbchar = 0;
|
||||||
|
evt.touchX = 0;
|
||||||
|
evt.touchY = 0;
|
||||||
|
this->notifyObservers(&evt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (btn1_pressed && !long_press_triggered && (millis() - press_start_time >= LONG_PRESS_TIME)) {
|
||||||
|
long_press_triggered = true;
|
||||||
|
InputEvent evt;
|
||||||
|
evt.source = "UserButton";
|
||||||
|
evt.inputEvent = INPUT_BROKER_SELECT;
|
||||||
|
evt.kbchar = 0;
|
||||||
|
evt.touchX = 0;
|
||||||
|
evt.touchY = 0;
|
||||||
|
this->notifyObservers(&evt);
|
||||||
|
}
|
||||||
|
return 50;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
18
src/input/i2cButton.h
Normal file
18
src/input/i2cButton.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "InputBroker.h"
|
||||||
|
#include "OneButton.h"
|
||||||
|
#include "concurrency/OSThread.h"
|
||||||
|
#include "configuration.h"
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
|
||||||
|
class i2cButtonThread : public Observable<const InputEvent *>, public concurrency::OSThread
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
const char *_originName;
|
||||||
|
explicit i2cButtonThread(const char *name);
|
||||||
|
int32_t runOnce() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern i2cButtonThread *i2cButton;
|
||||||
|
#endif
|
||||||
@@ -90,7 +90,7 @@ int32_t KbI2cBase::runOnce()
|
|||||||
while (keyCount--) {
|
while (keyCount--) {
|
||||||
const BBQ10Keyboard::KeyEvent key = Q10keyboard.keyEvent();
|
const BBQ10Keyboard::KeyEvent key = Q10keyboard.keyEvent();
|
||||||
if ((key.key != 0x00) && (key.state == BBQ10Keyboard::StateRelease)) {
|
if ((key.key != 0x00) && (key.state == BBQ10Keyboard::StateRelease)) {
|
||||||
InputEvent e;
|
InputEvent e = {};
|
||||||
e.inputEvent = INPUT_BROKER_NONE;
|
e.inputEvent = INPUT_BROKER_NONE;
|
||||||
e.source = this->_originName;
|
e.source = this->_originName;
|
||||||
switch (key.key) {
|
switch (key.key) {
|
||||||
@@ -187,7 +187,7 @@ int32_t KbI2cBase::runOnce()
|
|||||||
}
|
}
|
||||||
case 0x37: { // MPR121
|
case 0x37: { // MPR121
|
||||||
MPRkeyboard.trigger();
|
MPRkeyboard.trigger();
|
||||||
InputEvent e;
|
InputEvent e = {};
|
||||||
|
|
||||||
while (MPRkeyboard.hasEvent()) {
|
while (MPRkeyboard.hasEvent()) {
|
||||||
char nextEvent = MPRkeyboard.dequeueEvent();
|
char nextEvent = MPRkeyboard.dequeueEvent();
|
||||||
@@ -250,7 +250,7 @@ int32_t KbI2cBase::runOnce()
|
|||||||
}
|
}
|
||||||
case 0x84: { // Adafruit TCA8418
|
case 0x84: { // Adafruit TCA8418
|
||||||
TCAKeyboard.trigger();
|
TCAKeyboard.trigger();
|
||||||
InputEvent e;
|
InputEvent e = {};
|
||||||
while (TCAKeyboard.hasEvent()) {
|
while (TCAKeyboard.hasEvent()) {
|
||||||
char nextEvent = TCAKeyboard.dequeueEvent();
|
char nextEvent = TCAKeyboard.dequeueEvent();
|
||||||
e.inputEvent = INPUT_BROKER_ANYKEY;
|
e.inputEvent = INPUT_BROKER_ANYKEY;
|
||||||
@@ -350,7 +350,7 @@ int32_t KbI2cBase::runOnce()
|
|||||||
}
|
}
|
||||||
if (PrintDataBuf != 0) {
|
if (PrintDataBuf != 0) {
|
||||||
LOG_DEBUG("RAK14004 key 0x%x pressed", PrintDataBuf);
|
LOG_DEBUG("RAK14004 key 0x%x pressed", PrintDataBuf);
|
||||||
InputEvent e;
|
InputEvent e = {};
|
||||||
e.inputEvent = INPUT_BROKER_MATRIXKEY;
|
e.inputEvent = INPUT_BROKER_MATRIXKEY;
|
||||||
e.source = this->_originName;
|
e.source = this->_originName;
|
||||||
e.kbchar = PrintDataBuf;
|
e.kbchar = PrintDataBuf;
|
||||||
@@ -365,7 +365,7 @@ int32_t KbI2cBase::runOnce()
|
|||||||
|
|
||||||
if (i2cBus->available()) {
|
if (i2cBus->available()) {
|
||||||
char c = i2cBus->read();
|
char c = i2cBus->read();
|
||||||
InputEvent e;
|
InputEvent e = {};
|
||||||
e.inputEvent = INPUT_BROKER_NONE;
|
e.inputEvent = INPUT_BROKER_NONE;
|
||||||
e.source = this->_originName;
|
e.source = this->_originName;
|
||||||
switch (c) {
|
switch (c) {
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ int32_t KbMatrixBase::runOnce()
|
|||||||
if (key != 0) {
|
if (key != 0) {
|
||||||
LOG_DEBUG("Key 0x%x pressed", key);
|
LOG_DEBUG("Key 0x%x pressed", key);
|
||||||
// reset shift now that we have a keypress
|
// reset shift now that we have a keypress
|
||||||
InputEvent e;
|
InputEvent e = {};
|
||||||
e.inputEvent = INPUT_BROKER_NONE;
|
e.inputEvent = INPUT_BROKER_NONE;
|
||||||
e.source = this->_originName;
|
e.source = this->_originName;
|
||||||
switch (key) {
|
switch (key) {
|
||||||
|
|||||||
103
src/main.cpp
103
src/main.cpp
@@ -385,10 +385,9 @@ void setup()
|
|||||||
io.digitalWrite(EXPANDS_GPIO_EN, HIGH);
|
io.digitalWrite(EXPANDS_GPIO_EN, HIGH);
|
||||||
io.pinMode(EXPANDS_SD_PULLEN, INPUT);
|
io.pinMode(EXPANDS_SD_PULLEN, INPUT);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
concurrency::hasBeenSetup = true;
|
concurrency::hasBeenSetup = true;
|
||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
SPISettings spiSettings(settingsMap[spiSpeed], MSBFIRST, SPI_MODE0);
|
SPISettings spiSettings(portduino_config.spiSpeed, MSBFIRST, SPI_MODE0);
|
||||||
#else
|
#else
|
||||||
SPISettings spiSettings(4000000, MSBFIRST, SPI_MODE0);
|
SPISettings spiSettings(4000000, MSBFIRST, SPI_MODE0);
|
||||||
#endif
|
#endif
|
||||||
@@ -533,9 +532,9 @@ void setup()
|
|||||||
#elif defined(I2C_SDA) && !defined(ARCH_RP2040)
|
#elif defined(I2C_SDA) && !defined(ARCH_RP2040)
|
||||||
Wire.begin(I2C_SDA, I2C_SCL);
|
Wire.begin(I2C_SDA, I2C_SCL);
|
||||||
#elif defined(ARCH_PORTDUINO)
|
#elif defined(ARCH_PORTDUINO)
|
||||||
if (settingsStrings[i2cdev] != "") {
|
if (portduino_config.i2cdev != "") {
|
||||||
LOG_INFO("Use %s as I2C device", settingsStrings[i2cdev].c_str());
|
LOG_INFO("Use %s as I2C device", portduino_config.i2cdev.c_str());
|
||||||
Wire.begin(settingsStrings[i2cdev].c_str());
|
Wire.begin(portduino_config.i2cdev.c_str());
|
||||||
} else {
|
} else {
|
||||||
LOG_INFO("No I2C device configured, Skip");
|
LOG_INFO("No I2C device configured, Skip");
|
||||||
}
|
}
|
||||||
@@ -544,6 +543,12 @@ void setup()
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
pinMode(LORA_CS, OUTPUT);
|
||||||
|
digitalWrite(LORA_CS, 1);
|
||||||
|
c6l_init();
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef PIN_LCD_RESET
|
#ifdef PIN_LCD_RESET
|
||||||
// FIXME - move this someplace better, LCD is at address 0x3F
|
// FIXME - move this someplace better, LCD is at address 0x3F
|
||||||
pinMode(PIN_LCD_RESET, OUTPUT);
|
pinMode(PIN_LCD_RESET, OUTPUT);
|
||||||
@@ -581,7 +586,7 @@ void setup()
|
|||||||
#if defined(I2C_SDA)
|
#if defined(I2C_SDA)
|
||||||
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE);
|
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE);
|
||||||
#elif defined(ARCH_PORTDUINO)
|
#elif defined(ARCH_PORTDUINO)
|
||||||
if (settingsStrings[i2cdev] != "") {
|
if (portduino_config.i2cdev != "") {
|
||||||
LOG_INFO("Scan for i2c devices");
|
LOG_INFO("Scan for i2c devices");
|
||||||
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE);
|
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE);
|
||||||
}
|
}
|
||||||
@@ -854,7 +859,7 @@ void setup()
|
|||||||
SPI.begin(false);
|
SPI.begin(false);
|
||||||
#endif // HW_SPI1_DEVICE
|
#endif // HW_SPI1_DEVICE
|
||||||
#elif ARCH_PORTDUINO
|
#elif ARCH_PORTDUINO
|
||||||
if (settingsStrings[spidev] != "ch341") {
|
if (portduino_config.lora_spi_dev != "ch341") {
|
||||||
SPI.begin();
|
SPI.begin();
|
||||||
}
|
}
|
||||||
#elif !defined(ARCH_ESP32) // ARCH_RP2040
|
#elif !defined(ARCH_ESP32) // ARCH_RP2040
|
||||||
@@ -877,10 +882,11 @@ void setup()
|
|||||||
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
||||||
|
|
||||||
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || \
|
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || \
|
||||||
defined(ST7789_CS) || defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS)
|
defined(ST7789_CS) || defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS) || \
|
||||||
|
defined(USE_SPISSD1306)
|
||||||
screen = new graphics::Screen(screen_found, screen_model, screen_geometry);
|
screen = new graphics::Screen(screen_found, screen_model, screen_geometry);
|
||||||
#elif defined(ARCH_PORTDUINO)
|
#elif defined(ARCH_PORTDUINO)
|
||||||
if ((screen_found.port != ScanI2C::I2CPort::NO_I2C || settingsMap[displayPanel]) &&
|
if ((screen_found.port != ScanI2C::I2CPort::NO_I2C || portduino_config.displayPanel) &&
|
||||||
config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
||||||
screen = new graphics::Screen(screen_found, screen_model, screen_geometry);
|
screen = new graphics::Screen(screen_found, screen_model, screen_geometry);
|
||||||
}
|
}
|
||||||
@@ -981,13 +987,13 @@ void setup()
|
|||||||
#endif
|
#endif
|
||||||
#if defined(ARCH_PORTDUINO)
|
#if defined(ARCH_PORTDUINO)
|
||||||
|
|
||||||
if (settingsMap.count(userButtonPin) != 0 && settingsMap[userButtonPin] != RADIOLIB_NC) {
|
if (portduino_config.userButtonPin.enabled) {
|
||||||
|
|
||||||
LOG_DEBUG("Use GPIO%02d for button", settingsMap[userButtonPin]);
|
LOG_DEBUG("Use GPIO%02d for button", portduino_config.userButtonPin.pin);
|
||||||
UserButtonThread = new ButtonThread("UserButton");
|
UserButtonThread = new ButtonThread("UserButton");
|
||||||
if (screen) {
|
if (screen) {
|
||||||
ButtonConfig config;
|
ButtonConfig config;
|
||||||
config.pinNumber = (uint8_t)settingsMap[userButtonPin];
|
config.pinNumber = (uint8_t)portduino_config.userButtonPin.pin;
|
||||||
config.activeLow = true;
|
config.activeLow = true;
|
||||||
config.activePullup = true;
|
config.activePullup = true;
|
||||||
config.pullupSense = INPUT_PULLUP;
|
config.pullupSense = INPUT_PULLUP;
|
||||||
@@ -1140,11 +1146,12 @@ void setup()
|
|||||||
// Don't call screen setup until after nodedb is setup (because we need
|
// Don't call screen setup until after nodedb is setup (because we need
|
||||||
// the current region name)
|
// the current region name)
|
||||||
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || \
|
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || \
|
||||||
defined(ST7789_CS) || defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS)
|
defined(ST7789_CS) || defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS) || \
|
||||||
|
defined(USE_SPISSD1306)
|
||||||
if (screen)
|
if (screen)
|
||||||
screen->setup();
|
screen->setup();
|
||||||
#elif defined(ARCH_PORTDUINO)
|
#elif defined(ARCH_PORTDUINO)
|
||||||
if ((screen_found.port != ScanI2C::I2CPort::NO_I2C || settingsMap[displayPanel]) &&
|
if ((screen_found.port != ScanI2C::I2CPort::NO_I2C || portduino_config.displayPanel) &&
|
||||||
config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
||||||
screen->setup();
|
screen->setup();
|
||||||
}
|
}
|
||||||
@@ -1160,15 +1167,10 @@ void setup()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ARCH_PORTDUINO
|
#ifdef ARCH_PORTDUINO
|
||||||
const struct {
|
|
||||||
configNames cfgName;
|
|
||||||
std::string strName;
|
|
||||||
} loraModules[] = {{use_rf95, "RF95"}, {use_sx1262, "sx1262"}, {use_sx1268, "sx1268"}, {use_sx1280, "sx1280"},
|
|
||||||
{use_lr1110, "lr1110"}, {use_lr1120, "lr1120"}, {use_lr1121, "lr1121"}, {use_llcc68, "LLCC68"}};
|
|
||||||
// as one can't use a function pointer to the class constructor:
|
// as one can't use a function pointer to the class constructor:
|
||||||
auto loraModuleInterface = [](configNames cfgName, LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq,
|
auto loraModuleInterface = [](LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
|
||||||
RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy) {
|
RADIOLIB_PIN_TYPE busy) {
|
||||||
switch (cfgName) {
|
switch (portduino_config.lora_module) {
|
||||||
case use_rf95:
|
case use_rf95:
|
||||||
return (RadioInterface *)new RF95Interface(hal, cs, irq, rst, busy);
|
return (RadioInterface *)new RF95Interface(hal, cs, irq, rst, busy);
|
||||||
case use_sx1262:
|
case use_sx1262:
|
||||||
@@ -1185,31 +1187,34 @@ void setup()
|
|||||||
return (RadioInterface *)new LR1121Interface(hal, cs, irq, rst, busy);
|
return (RadioInterface *)new LR1121Interface(hal, cs, irq, rst, busy);
|
||||||
case use_llcc68:
|
case use_llcc68:
|
||||||
return (RadioInterface *)new LLCC68Interface(hal, cs, irq, rst, busy);
|
return (RadioInterface *)new LLCC68Interface(hal, cs, irq, rst, busy);
|
||||||
|
case use_simradio:
|
||||||
|
return (RadioInterface *)new SimRadio;
|
||||||
default:
|
default:
|
||||||
assert(0); // shouldn't happen
|
assert(0); // shouldn't happen
|
||||||
return (RadioInterface *)nullptr;
|
return (RadioInterface *)nullptr;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
for (auto &loraModule : loraModules) {
|
|
||||||
if (settingsMap[loraModule.cfgName] && !rIf) {
|
LOG_DEBUG("Activate %s radio on SPI port %s", portduino_config.loraModules[portduino_config.lora_module].c_str(),
|
||||||
LOG_DEBUG("Activate %s radio on SPI port %s", loraModule.strName.c_str(), settingsStrings[spidev].c_str());
|
portduino_config.lora_spi_dev.c_str());
|
||||||
if (settingsStrings[spidev] == "ch341") {
|
if (portduino_config.lora_spi_dev == "ch341") {
|
||||||
RadioLibHAL = ch341Hal;
|
RadioLibHAL = ch341Hal;
|
||||||
} else {
|
} else {
|
||||||
RadioLibHAL = new LockingArduinoHal(SPI, spiSettings);
|
RadioLibHAL = new LockingArduinoHal(SPI, spiSettings);
|
||||||
}
|
|
||||||
rIf = loraModuleInterface(loraModule.cfgName, (LockingArduinoHal *)RadioLibHAL, settingsMap[cs_pin],
|
|
||||||
settingsMap[irq_pin], settingsMap[reset_pin], settingsMap[busy_pin]);
|
|
||||||
if (!rIf->init()) {
|
|
||||||
LOG_WARN("No %s radio", loraModule.strName.c_str());
|
|
||||||
delete rIf;
|
|
||||||
rIf = NULL;
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
} else {
|
|
||||||
LOG_INFO("%s init success", loraModule.strName.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
rIf =
|
||||||
|
loraModuleInterface((LockingArduinoHal *)RadioLibHAL, portduino_config.lora_cs_pin.pin, portduino_config.lora_irq_pin.pin,
|
||||||
|
portduino_config.lora_reset_pin.pin, portduino_config.lora_busy_pin.pin);
|
||||||
|
|
||||||
|
if (!rIf->init()) {
|
||||||
|
LOG_WARN("No %s radio", portduino_config.loraModules[portduino_config.lora_module].c_str());
|
||||||
|
delete rIf;
|
||||||
|
rIf = NULL;
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
} else {
|
||||||
|
LOG_INFO("%s init success", portduino_config.loraModules[portduino_config.lora_module].c_str());
|
||||||
|
}
|
||||||
|
|
||||||
#elif defined(HW_SPI1_DEVICE)
|
#elif defined(HW_SPI1_DEVICE)
|
||||||
LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI1, spiSettings);
|
LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI1, spiSettings);
|
||||||
#else // HW_SPI1_DEVICE
|
#else // HW_SPI1_DEVICE
|
||||||
@@ -1231,20 +1236,6 @@ void setup()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(ARCH_PORTDUINO)
|
|
||||||
if (!rIf) {
|
|
||||||
rIf = new SimRadio;
|
|
||||||
if (!rIf->init()) {
|
|
||||||
LOG_WARN("No simulated radio");
|
|
||||||
delete rIf;
|
|
||||||
rIf = NULL;
|
|
||||||
} else {
|
|
||||||
LOG_INFO("Use SIMULATED radio!");
|
|
||||||
radioType = SIM_RADIO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(RF95_IRQ) && RADIOLIB_EXCLUDE_SX127X != 1
|
#if defined(RF95_IRQ) && RADIOLIB_EXCLUDE_SX127X != 1
|
||||||
if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) {
|
if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) {
|
||||||
rIf = new RF95Interface(RadioLibHAL, LORA_CS, RF95_IRQ, RF95_RESET, RF95_DIO1);
|
rIf = new RF95Interface(RadioLibHAL, LORA_CS, RF95_IRQ, RF95_RESET, RF95_DIO1);
|
||||||
@@ -1455,7 +1446,7 @@ void setup()
|
|||||||
|
|
||||||
#ifdef ARCH_PORTDUINO
|
#ifdef ARCH_PORTDUINO
|
||||||
#if __has_include(<ulfius.h>)
|
#if __has_include(<ulfius.h>)
|
||||||
if (settingsMap[webserverport] != -1) {
|
if (portduino_config.webserverport != -1) {
|
||||||
piwebServerThread = new PiWebServerThread();
|
piwebServerThread = new PiWebServerThread();
|
||||||
std::atexit([] { delete piwebServerThread; });
|
std::atexit([] { delete piwebServerThread; });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,4 +88,16 @@ uint32_t MemGet::getPsramSize()
|
|||||||
#else
|
#else
|
||||||
return 0;
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void displayPercentHeapFree()
|
||||||
|
{
|
||||||
|
uint32_t freeHeap = memGet.getFreeHeap();
|
||||||
|
uint32_t totalHeap = memGet.getHeapSize();
|
||||||
|
if (totalHeap == 0 || totalHeap == UINT32_MAX) {
|
||||||
|
LOG_INFO("Heap size unavailable");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int percent = (int)((freeHeap * 100) / totalHeap);
|
||||||
|
LOG_INFO("Heap free: %d%% (%u/%u bytes)", percent, freeHeap, totalHeap);
|
||||||
}
|
}
|
||||||
@@ -423,6 +423,33 @@ bool Channels::decryptForHash(ChannelIndex chIndex, ChannelHash channelHash)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Channels::setDefaultPresetCryptoForHash(ChannelHash channelHash)
|
||||||
|
{
|
||||||
|
// Iterate all known presets
|
||||||
|
for (int preset = _meshtastic_Config_LoRaConfig_ModemPreset_MIN; preset <= _meshtastic_Config_LoRaConfig_ModemPreset_MAX;
|
||||||
|
++preset) {
|
||||||
|
const char *name =
|
||||||
|
DisplayFormatters::getModemPresetDisplayName((meshtastic_Config_LoRaConfig_ModemPreset)preset, false, false);
|
||||||
|
if (!name)
|
||||||
|
continue;
|
||||||
|
if (strcmp(name, "Invalid") == 0)
|
||||||
|
continue; // skip invalid placeholder
|
||||||
|
uint8_t h = xorHash((const uint8_t *)name, strlen(name));
|
||||||
|
// Expand default PSK alias 1 to actual bytes and xor into hash
|
||||||
|
uint8_t tmp = h ^ xorHash(defaultpsk, sizeof(defaultpsk));
|
||||||
|
if (tmp == channelHash) {
|
||||||
|
// Set crypto to defaultpsk and report success
|
||||||
|
CryptoKey k;
|
||||||
|
memcpy(k.bytes, defaultpsk, sizeof(defaultpsk));
|
||||||
|
k.length = sizeof(defaultpsk);
|
||||||
|
crypto->setKey(k);
|
||||||
|
LOG_INFO("Matched default preset '%s' for hash 0x%x; set default PSK", name, channelHash);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/** Given a channel index setup crypto for encoding that channel (or the primary channel if that channel is unsecured)
|
/** Given a channel index setup crypto for encoding that channel (or the primary channel if that channel is unsecured)
|
||||||
*
|
*
|
||||||
* This method is called before encoding outbound packets
|
* This method is called before encoding outbound packets
|
||||||
|
|||||||
@@ -94,6 +94,8 @@ class Channels
|
|||||||
|
|
||||||
bool ensureLicensedOperation();
|
bool ensureLicensedOperation();
|
||||||
|
|
||||||
|
bool setDefaultPresetCryptoForHash(ChannelHash channelHash);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** Given a channel index, change to use the crypto key specified by that index
|
/** Given a channel index, change to use the crypto key specified by that index
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -43,12 +43,30 @@ bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
|
|||||||
return Router::shouldFilterReceived(p);
|
return Router::shouldFilterReceived(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FloodingRouter::roleAllowsCancelingDupe(const meshtastic_MeshPacket *p)
|
||||||
|
{
|
||||||
|
if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ||
|
||||||
|
config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER ||
|
||||||
|
config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER_LATE) {
|
||||||
|
// ROUTER, REPEATER, ROUTER_LATE should never cancel relaying a packet (i.e. we should always rebroadcast),
|
||||||
|
// even if we've heard another station rebroadcast it already.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.device.role == meshtastic_Config_DeviceConfig_Role_CLIENT_BASE) {
|
||||||
|
// CLIENT_BASE: if the packet is from or to a favorited node,
|
||||||
|
// we should act like a ROUTER and should never cancel a rebroadcast (i.e. we should always rebroadcast),
|
||||||
|
// even if we've heard another station rebroadcast it already.
|
||||||
|
return !nodeDB->isFromOrToFavoritedNode(*p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// All other roles (such as CLIENT) should cancel a rebroadcast if they hear another station's rebroadcast.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void FloodingRouter::perhapsCancelDupe(const meshtastic_MeshPacket *p)
|
void FloodingRouter::perhapsCancelDupe(const meshtastic_MeshPacket *p)
|
||||||
{
|
{
|
||||||
if (config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER &&
|
if (p->transport_mechanism == meshtastic_MeshPacket_TransportMechanism_TRANSPORT_LORA && roleAllowsCancelingDupe(p)) {
|
||||||
config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER &&
|
|
||||||
config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER_LATE &&
|
|
||||||
p->transport_mechanism == meshtastic_MeshPacket_TransportMechanism_TRANSPORT_LORA) {
|
|
||||||
// cancel rebroadcast of this message *if* there was already one, unless we're a router/repeater!
|
// cancel rebroadcast of this message *if* there was already one, unless we're a router/repeater!
|
||||||
// But only LoRa packets should be able to trigger this.
|
// But only LoRa packets should be able to trigger this.
|
||||||
if (Router::cancelSending(p->from, p->id))
|
if (Router::cancelSending(p->from, p->id))
|
||||||
|
|||||||
@@ -59,6 +59,10 @@ class FloodingRouter : public Router
|
|||||||
*/
|
*/
|
||||||
virtual void sniffReceived(const meshtastic_MeshPacket *p, const meshtastic_Routing *c) override;
|
virtual void sniffReceived(const meshtastic_MeshPacket *p, const meshtastic_Routing *c) override;
|
||||||
|
|
||||||
|
// Return false for roles like ROUTER or REPEATER which should always rebroadcast even when we've heard another rebroadcast of
|
||||||
|
// the same packet
|
||||||
|
bool roleAllowsCancelingDupe(const meshtastic_MeshPacket *p);
|
||||||
|
|
||||||
/* Call when receiving a duplicate packet to check whether we should cancel a packet in the Tx queue */
|
/* Call when receiving a duplicate packet to check whether we should cancel a packet in the Tx queue */
|
||||||
void perhapsCancelDupe(const meshtastic_MeshPacket *p);
|
void perhapsCancelDupe(const meshtastic_MeshPacket *p);
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ static const Module::RfSwitchMode_t rfswitch_table[] = {
|
|||||||
// Particular boards might define a different max power based on what their hardware can do, default to max power output if not
|
// Particular boards might define a different max power based on what their hardware can do, default to max power output if not
|
||||||
// specified (may be dangerous if using external PA and LR11x0 power config forgotten)
|
// specified (may be dangerous if using external PA and LR11x0 power config forgotten)
|
||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
#define LR1110_MAX_POWER settingsMap[lr1110_max_power]
|
#define LR1110_MAX_POWER portduino_config.lr1110_max_power
|
||||||
#endif
|
#endif
|
||||||
#ifndef LR1110_MAX_POWER
|
#ifndef LR1110_MAX_POWER
|
||||||
#define LR1110_MAX_POWER 22
|
#define LR1110_MAX_POWER 22
|
||||||
@@ -30,7 +30,7 @@ static const Module::RfSwitchMode_t rfswitch_table[] = {
|
|||||||
// the 2.4G part maxes at 13dBm
|
// the 2.4G part maxes at 13dBm
|
||||||
|
|
||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
#define LR1120_MAX_POWER settingsMap[lr1120_max_power]
|
#define LR1120_MAX_POWER portduino_config.lr1120_max_power
|
||||||
#endif
|
#endif
|
||||||
#ifndef LR1120_MAX_POWER
|
#ifndef LR1120_MAX_POWER
|
||||||
#define LR1120_MAX_POWER 13
|
#define LR1120_MAX_POWER 13
|
||||||
@@ -55,7 +55,7 @@ template <typename T> bool LR11x0Interface<T>::init()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
float tcxoVoltage = (float)settingsMap[dio3_tcxo_voltage] / 1000;
|
float tcxoVoltage = (float)portduino_config.dio3_tcxo_voltage / 1000;
|
||||||
// FIXME: correct logic to default to not using TCXO if no voltage is specified for LR11x0_DIO3_TCXO_VOLTAGE
|
// FIXME: correct logic to default to not using TCXO if no voltage is specified for LR11x0_DIO3_TCXO_VOLTAGE
|
||||||
#elif !defined(LR11X0_DIO3_TCXO_VOLTAGE)
|
#elif !defined(LR11X0_DIO3_TCXO_VOLTAGE)
|
||||||
float tcxoVoltage =
|
float tcxoVoltage =
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "PointerQueue.h"
|
#include "PointerQueue.h"
|
||||||
|
#include "configuration.h" // For LOG_WARN, LOG_DEBUG, LOG_HEAP
|
||||||
|
|
||||||
template <class T> class Allocator
|
template <class T> class Allocator
|
||||||
{
|
{
|
||||||
@@ -14,13 +15,14 @@ template <class T> class Allocator
|
|||||||
Allocator() : deleter([this](T *p) { this->release(p); }) {}
|
Allocator() : deleter([this](T *p) { this->release(p); }) {}
|
||||||
virtual ~Allocator() {}
|
virtual ~Allocator() {}
|
||||||
|
|
||||||
/// Return a queable object which has been prefilled with zeros. Panic if no buffer is available
|
/// Return a queable object which has been prefilled with zeros. Return nullptr if no buffer is available
|
||||||
/// Note: this method is safe to call from regular OR ISR code
|
/// Note: this method is safe to call from regular OR ISR code
|
||||||
T *allocZeroed()
|
T *allocZeroed()
|
||||||
{
|
{
|
||||||
T *p = allocZeroed(0);
|
T *p = allocZeroed(0);
|
||||||
|
if (!p) {
|
||||||
assert(p); // FIXME panic instead
|
LOG_WARN("Failed to allocate zeroed memory");
|
||||||
|
}
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,10 +41,12 @@ template <class T> class Allocator
|
|||||||
T *allocCopy(const T &src, TickType_t maxWait = portMAX_DELAY)
|
T *allocCopy(const T &src, TickType_t maxWait = portMAX_DELAY)
|
||||||
{
|
{
|
||||||
T *p = alloc(maxWait);
|
T *p = alloc(maxWait);
|
||||||
assert(p);
|
if (!p) {
|
||||||
|
LOG_WARN("Failed to allocate memory for copy");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
if (p)
|
*p = src;
|
||||||
*p = src;
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +87,11 @@ template <class T> class MemoryDynamic : public Allocator<T>
|
|||||||
/// Return a buffer for use by others
|
/// Return a buffer for use by others
|
||||||
virtual void release(T *p) override
|
virtual void release(T *p) override
|
||||||
{
|
{
|
||||||
assert(p);
|
if (p == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
LOG_HEAP("Freeing 0x%x", p);
|
||||||
|
|
||||||
free(p);
|
free(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,3 +104,58 @@ template <class T> class MemoryDynamic : public Allocator<T>
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A static memory pool that uses a fixed buffer instead of heap allocation
|
||||||
|
*/
|
||||||
|
template <class T, int MaxSize> class MemoryPool : public Allocator<T>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
T pool[MaxSize];
|
||||||
|
bool used[MaxSize];
|
||||||
|
|
||||||
|
public:
|
||||||
|
MemoryPool() : pool{}, used{}
|
||||||
|
{
|
||||||
|
// Arrays are now zero-initialized by member initializer list
|
||||||
|
// pool array: all elements are default-constructed (zero for POD types)
|
||||||
|
// used array: all elements are false (zero-initialized)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a buffer for use by others
|
||||||
|
virtual void release(T *p) override
|
||||||
|
{
|
||||||
|
if (!p) {
|
||||||
|
LOG_DEBUG("Failed to release memory, pointer is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the index of this pointer in our pool
|
||||||
|
int index = p - pool;
|
||||||
|
if (index >= 0 && index < MaxSize) {
|
||||||
|
assert(used[index]); // Should be marked as used
|
||||||
|
used[index] = false;
|
||||||
|
LOG_HEAP("Released static pool item %d at 0x%x", index, p);
|
||||||
|
} else {
|
||||||
|
LOG_WARN("Pointer 0x%x not from our pool!", p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Alloc some storage from our static pool
|
||||||
|
virtual T *alloc(TickType_t maxWait) override
|
||||||
|
{
|
||||||
|
// Find first free slot
|
||||||
|
for (int i = 0; i < MaxSize; i++) {
|
||||||
|
if (!used[i]) {
|
||||||
|
used[i] = true;
|
||||||
|
LOG_HEAP("Allocated static pool item %d at 0x%x", i, &pool[i]);
|
||||||
|
return &pool[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No free slots available - return nullptr instead of asserting
|
||||||
|
LOG_WARN("No free slots available in static memory pool!");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -100,7 +100,6 @@ void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src)
|
|||||||
// Was this message directed to us specifically? Will be false if we are sniffing someone elses packets
|
// Was this message directed to us specifically? Will be false if we are sniffing someone elses packets
|
||||||
auto ourNodeNum = nodeDB->getNodeNum();
|
auto ourNodeNum = nodeDB->getNodeNum();
|
||||||
bool toUs = isBroadcast(mp.to) || isToUs(&mp);
|
bool toUs = isBroadcast(mp.to) || isToUs(&mp);
|
||||||
bool fromUs = mp.from == ourNodeNum;
|
|
||||||
|
|
||||||
for (auto i = modules->begin(); i != modules->end(); ++i) {
|
for (auto i = modules->begin(); i != modules->end(); ++i) {
|
||||||
auto &pi = **i;
|
auto &pi = **i;
|
||||||
|
|||||||
@@ -46,11 +46,14 @@ the new node can build its node db)
|
|||||||
|
|
||||||
MeshService *service;
|
MeshService *service;
|
||||||
|
|
||||||
static MemoryDynamic<meshtastic_MqttClientProxyMessage> staticMqttClientProxyMessagePool;
|
#define MAX_MQTT_PROXY_MESSAGES 16
|
||||||
|
static MemoryPool<meshtastic_MqttClientProxyMessage, MAX_MQTT_PROXY_MESSAGES> staticMqttClientProxyMessagePool;
|
||||||
|
|
||||||
static MemoryDynamic<meshtastic_QueueStatus> staticQueueStatusPool;
|
#define MAX_QUEUE_STATUS 4
|
||||||
|
static MemoryPool<meshtastic_QueueStatus, MAX_QUEUE_STATUS> staticQueueStatusPool;
|
||||||
|
|
||||||
static MemoryDynamic<meshtastic_ClientNotification> staticClientNotificationPool;
|
#define MAX_CLIENT_NOTIFICATIONS 4
|
||||||
|
static MemoryPool<meshtastic_ClientNotification, MAX_CLIENT_NOTIFICATIONS> staticClientNotificationPool;
|
||||||
|
|
||||||
Allocator<meshtastic_MqttClientProxyMessage> &mqttClientProxyMessagePool = staticMqttClientProxyMessagePool;
|
Allocator<meshtastic_MqttClientProxyMessage> &mqttClientProxyMessagePool = staticMqttClientProxyMessagePool;
|
||||||
|
|
||||||
@@ -61,8 +64,10 @@ Allocator<meshtastic_QueueStatus> &queueStatusPool = staticQueueStatusPool;
|
|||||||
#include "Router.h"
|
#include "Router.h"
|
||||||
|
|
||||||
MeshService::MeshService()
|
MeshService::MeshService()
|
||||||
: toPhoneQueue(MAX_RX_TOPHONE), toPhoneQueueStatusQueue(MAX_RX_TOPHONE), toPhoneMqttProxyQueue(MAX_RX_TOPHONE),
|
#ifdef ARCH_PORTDUINO
|
||||||
toPhoneClientNotificationQueue(MAX_RX_TOPHONE / 2)
|
: toPhoneQueue(MAX_RX_TOPHONE), toPhoneQueueStatusQueue(MAX_RX_QUEUESTATUS_TOPHONE),
|
||||||
|
toPhoneMqttProxyQueue(MAX_RX_MQTTPROXY_TOPHONE), toPhoneClientNotificationQueue(MAX_RX_NOTIFICATION_TOPHONE)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
lastQueueStatus = {0, 0, 16, 0};
|
lastQueueStatus = {0, 0, 16, 0};
|
||||||
}
|
}
|
||||||
@@ -191,8 +196,10 @@ void MeshService::handleToRadio(meshtastic_MeshPacket &p)
|
|||||||
// (so we update our nodedb for the local node)
|
// (so we update our nodedb for the local node)
|
||||||
|
|
||||||
// Send the packet into the mesh
|
// Send the packet into the mesh
|
||||||
|
DEBUG_HEAP_BEFORE;
|
||||||
sendToMesh(packetPool.allocCopy(p), RX_SRC_USER);
|
auto a = packetPool.allocCopy(p);
|
||||||
|
DEBUG_HEAP_AFTER("MeshService::handleToRadio", a);
|
||||||
|
sendToMesh(a, RX_SRC_USER);
|
||||||
|
|
||||||
bool loopback = false; // if true send any packet the phone sends back itself (for testing)
|
bool loopback = false; // if true send any packet the phone sends back itself (for testing)
|
||||||
if (loopback) {
|
if (loopback) {
|
||||||
@@ -248,7 +255,11 @@ void MeshService::sendToMesh(meshtastic_MeshPacket *p, RxSource src, bool ccToPh
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((res == ERRNO_OK || res == ERRNO_SHOULD_RELEASE) && ccToPhone) { // Check if p is not released in case it couldn't be sent
|
if ((res == ERRNO_OK || res == ERRNO_SHOULD_RELEASE) && ccToPhone) { // Check if p is not released in case it couldn't be sent
|
||||||
sendToPhone(packetPool.allocCopy(*p));
|
DEBUG_HEAP_BEFORE;
|
||||||
|
auto a = packetPool.allocCopy(*p);
|
||||||
|
DEBUG_HEAP_AFTER("MeshService::sendToMesh", a);
|
||||||
|
|
||||||
|
sendToPhone(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Router may ask us to release the packet if it wasn't sent
|
// Router may ask us to release the packet if it wasn't sent
|
||||||
|
|||||||
@@ -9,7 +9,12 @@
|
|||||||
#include "MeshRadio.h"
|
#include "MeshRadio.h"
|
||||||
#include "MeshTypes.h"
|
#include "MeshTypes.h"
|
||||||
#include "Observer.h"
|
#include "Observer.h"
|
||||||
|
#ifdef ARCH_PORTDUINO
|
||||||
#include "PointerQueue.h"
|
#include "PointerQueue.h"
|
||||||
|
#else
|
||||||
|
#include "StaticPointerQueue.h"
|
||||||
|
#endif
|
||||||
|
#include "mesh-pb-constants.h"
|
||||||
#if defined(ARCH_PORTDUINO)
|
#if defined(ARCH_PORTDUINO)
|
||||||
#include "../platform/portduino/SimRadio.h"
|
#include "../platform/portduino/SimRadio.h"
|
||||||
#endif
|
#endif
|
||||||
@@ -37,16 +42,32 @@ class MeshService
|
|||||||
/// FIXME, change to a DropOldestQueue and keep a count of the number of dropped packets to ensure
|
/// FIXME, change to a DropOldestQueue and keep a count of the number of dropped packets to ensure
|
||||||
/// we never hang because android hasn't been there in a while
|
/// we never hang because android hasn't been there in a while
|
||||||
/// FIXME - save this to flash on deep sleep
|
/// FIXME - save this to flash on deep sleep
|
||||||
|
#ifdef ARCH_PORTDUINO
|
||||||
PointerQueue<meshtastic_MeshPacket> toPhoneQueue;
|
PointerQueue<meshtastic_MeshPacket> toPhoneQueue;
|
||||||
|
#else
|
||||||
|
StaticPointerQueue<meshtastic_MeshPacket, MAX_RX_TOPHONE> toPhoneQueue;
|
||||||
|
#endif
|
||||||
|
|
||||||
// keep list of QueueStatus packets to be send to the phone
|
// keep list of QueueStatus packets to be send to the phone
|
||||||
|
#ifdef ARCH_PORTDUINO
|
||||||
PointerQueue<meshtastic_QueueStatus> toPhoneQueueStatusQueue;
|
PointerQueue<meshtastic_QueueStatus> toPhoneQueueStatusQueue;
|
||||||
|
#else
|
||||||
|
StaticPointerQueue<meshtastic_QueueStatus, MAX_RX_QUEUESTATUS_TOPHONE> toPhoneQueueStatusQueue;
|
||||||
|
#endif
|
||||||
|
|
||||||
// keep list of MqttClientProxyMessages to be send to the client for delivery
|
// keep list of MqttClientProxyMessages to be send to the client for delivery
|
||||||
|
#ifdef ARCH_PORTDUINO
|
||||||
PointerQueue<meshtastic_MqttClientProxyMessage> toPhoneMqttProxyQueue;
|
PointerQueue<meshtastic_MqttClientProxyMessage> toPhoneMqttProxyQueue;
|
||||||
|
#else
|
||||||
|
StaticPointerQueue<meshtastic_MqttClientProxyMessage, MAX_RX_MQTTPROXY_TOPHONE> toPhoneMqttProxyQueue;
|
||||||
|
#endif
|
||||||
|
|
||||||
// keep list of ClientNotifications to be send to the client (phone)
|
// keep list of ClientNotifications to be send to the client (phone)
|
||||||
|
#ifdef ARCH_PORTDUINO
|
||||||
PointerQueue<meshtastic_ClientNotification> toPhoneClientNotificationQueue;
|
PointerQueue<meshtastic_ClientNotification> toPhoneClientNotificationQueue;
|
||||||
|
#else
|
||||||
|
StaticPointerQueue<meshtastic_ClientNotification, MAX_RX_NOTIFICATION_TOPHONE> toPhoneClientNotificationQueue;
|
||||||
|
#endif
|
||||||
|
|
||||||
// This holds the last QueueStatus send
|
// This holds the last QueueStatus send
|
||||||
meshtastic_QueueStatus lastQueueStatus;
|
meshtastic_QueueStatus lastQueueStatus;
|
||||||
|
|||||||
@@ -161,6 +161,15 @@ bool NextHopRouter::stopRetransmission(NodeNum from, PacketId id)
|
|||||||
return stopRetransmission(key);
|
return stopRetransmission(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NextHopRouter::roleAllowsCancelingFromTxQueue(const meshtastic_MeshPacket *p)
|
||||||
|
{
|
||||||
|
// Return true if we're allowed to cancel a packet in the txQueue (so we may never transmit it even once)
|
||||||
|
|
||||||
|
// Return false for roles like ROUTER, REPEATER, ROUTER_LATE which should always transmit the packet at least once.
|
||||||
|
|
||||||
|
return roleAllowsCancelingDupe(p); // same logic as FloodingRouter::roleAllowsCancelingDupe
|
||||||
|
}
|
||||||
|
|
||||||
bool NextHopRouter::stopRetransmission(GlobalPacketId key)
|
bool NextHopRouter::stopRetransmission(GlobalPacketId key)
|
||||||
{
|
{
|
||||||
auto old = findPendingPacket(key);
|
auto old = findPendingPacket(key);
|
||||||
@@ -170,17 +179,21 @@ bool NextHopRouter::stopRetransmission(GlobalPacketId key)
|
|||||||
to avoid canceling a transmission if it was ACKed super fast via MQTT */
|
to avoid canceling a transmission if it was ACKed super fast via MQTT */
|
||||||
if (old->numRetransmissions < NUM_RELIABLE_RETX - 1) {
|
if (old->numRetransmissions < NUM_RELIABLE_RETX - 1) {
|
||||||
// We only cancel it if we are the original sender or if we're not a router(_late)/repeater
|
// We only cancel it if we are the original sender or if we're not a router(_late)/repeater
|
||||||
if (isFromUs(p) || (config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER &&
|
if (isFromUs(p) || roleAllowsCancelingFromTxQueue(p)) {
|
||||||
config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER &&
|
|
||||||
config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER_LATE)) {
|
|
||||||
// remove the 'original' (identified by originator and packet->id) from the txqueue and free it
|
// remove the 'original' (identified by originator and packet->id) from the txqueue and free it
|
||||||
cancelSending(getFrom(p), p->id);
|
cancelSending(getFrom(p), p->id);
|
||||||
// now free the pooled copy for retransmission too
|
|
||||||
packetPool.release(p);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Regardless of whether or not we canceled this packet from the txQueue, remove it from our pending list so it doesn't
|
||||||
|
// get scheduled again. (This is the core of stopRetransmission.)
|
||||||
auto numErased = pending.erase(key);
|
auto numErased = pending.erase(key);
|
||||||
assert(numErased == 1);
|
assert(numErased == 1);
|
||||||
|
|
||||||
|
// When we remove an entry from pending, always be sure to release the copy of the packet that was allocated in the call
|
||||||
|
// to startRetransmission.
|
||||||
|
packetPool.release(p);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else
|
} else
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -121,6 +121,9 @@ class NextHopRouter : public FloodingRouter
|
|||||||
*/
|
*/
|
||||||
PendingPacket *startRetransmission(meshtastic_MeshPacket *p, uint8_t numReTx = NUM_INTERMEDIATE_RETX);
|
PendingPacket *startRetransmission(meshtastic_MeshPacket *p, uint8_t numReTx = NUM_INTERMEDIATE_RETX);
|
||||||
|
|
||||||
|
// Return true if we're allowed to cancel a packet in the txQueue (so we may never transmit it even once)
|
||||||
|
bool roleAllowsCancelingFromTxQueue(const meshtastic_MeshPacket *p);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop any retransmissions we are doing of the specified node/packet ID pair
|
* Stop any retransmissions we are doing of the specified node/packet ID pair
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -663,7 +663,7 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
|
|||||||
config.bluetooth.fixed_pin = defaultBLEPin;
|
config.bluetooth.fixed_pin = defaultBLEPin;
|
||||||
|
|
||||||
#if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7789_CS) || \
|
#if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7789_CS) || \
|
||||||
defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS)
|
defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(USE_SPISSD1306)
|
||||||
bool hasScreen = true;
|
bool hasScreen = true;
|
||||||
#ifdef HELTEC_MESH_NODE_T114
|
#ifdef HELTEC_MESH_NODE_T114
|
||||||
uint32_t st7789_id = get_st7789_id(ST7789_NSS, ST7789_SCK, ST7789_SDA, ST7789_RS, ST7789_RESET);
|
uint32_t st7789_id = get_st7789_id(ST7789_NSS, ST7789_SCK, ST7789_SDA, ST7789_RS, ST7789_RESET);
|
||||||
@@ -673,7 +673,7 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
|
|||||||
#endif
|
#endif
|
||||||
#elif ARCH_PORTDUINO
|
#elif ARCH_PORTDUINO
|
||||||
bool hasScreen = false;
|
bool hasScreen = false;
|
||||||
if (settingsMap[displayPanel])
|
if (portduino_config.displayPanel)
|
||||||
hasScreen = true;
|
hasScreen = true;
|
||||||
else
|
else
|
||||||
hasScreen = screen_found.port != ScanI2C::I2CPort::NO_I2C;
|
hasScreen = screen_found.port != ScanI2C::I2CPort::NO_I2C;
|
||||||
@@ -1334,8 +1334,8 @@ void NodeDB::loadFromDisk()
|
|||||||
}
|
}
|
||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
// set any config overrides
|
// set any config overrides
|
||||||
if (settingsMap[has_configDisplayMode]) {
|
if (portduino_config.has_configDisplayMode) {
|
||||||
config.display.displaymode = (_meshtastic_Config_DisplayConfig_DisplayMode)settingsMap[configDisplayMode];
|
config.display.displaymode = (_meshtastic_Config_DisplayConfig_DisplayMode)portduino_config.configDisplayMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1750,6 +1750,65 @@ void NodeDB::set_favorite(bool is_favorite, uint32_t nodeId)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NodeDB::isFavorite(uint32_t nodeId)
|
||||||
|
{
|
||||||
|
// returns true if nodeId is_favorite; false if not or not found
|
||||||
|
|
||||||
|
// NODENUM_BROADCAST will never be in the DB
|
||||||
|
if (nodeId == NODENUM_BROADCAST)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
meshtastic_NodeInfoLite *lite = getMeshNode(nodeId);
|
||||||
|
|
||||||
|
if (lite) {
|
||||||
|
return lite->is_favorite;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NodeDB::isFromOrToFavoritedNode(const meshtastic_MeshPacket &p)
|
||||||
|
{
|
||||||
|
// This method is logically equivalent to:
|
||||||
|
// return isFavorite(p.from) || isFavorite(p.to);
|
||||||
|
// but is more efficient by:
|
||||||
|
// 1. doing only one pass through the database, instead of two
|
||||||
|
// 2. exiting early when a favorite is found, or if both from and to have been seen
|
||||||
|
|
||||||
|
if (p.to == NODENUM_BROADCAST)
|
||||||
|
return isFavorite(p.from); // we never store NODENUM_BROADCAST in the DB, so we only need to check p.from
|
||||||
|
|
||||||
|
meshtastic_NodeInfoLite *lite = NULL;
|
||||||
|
|
||||||
|
bool seenFrom = false;
|
||||||
|
bool seenTo = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < numMeshNodes; i++) {
|
||||||
|
lite = &meshNodes->at(i);
|
||||||
|
|
||||||
|
if (lite->num == p.from) {
|
||||||
|
if (lite->is_favorite)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
seenFrom = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lite->num == p.to) {
|
||||||
|
if (lite->is_favorite)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
seenTo = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seenFrom && seenTo)
|
||||||
|
return false; // we've seen both, and neither is a favorite, so we can stop searching early
|
||||||
|
|
||||||
|
// Note: if we knew that sortMeshDB was always called after any change to is_favorite, we could exit early after searching
|
||||||
|
// all favorited nodes first.
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void NodeDB::pause_sort(bool paused)
|
void NodeDB::pause_sort(bool paused)
|
||||||
{
|
{
|
||||||
sortingIsPaused = paused;
|
sortingIsPaused = paused;
|
||||||
|
|||||||
@@ -185,6 +185,16 @@ class NodeDB
|
|||||||
*/
|
*/
|
||||||
void set_favorite(bool is_favorite, uint32_t nodeId);
|
void set_favorite(bool is_favorite, uint32_t nodeId);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if the node is in the NodeDB and marked as favorite
|
||||||
|
*/
|
||||||
|
bool isFavorite(uint32_t nodeId);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if p->from or p->to is a favorited node
|
||||||
|
*/
|
||||||
|
bool isFromOrToFavoritedNode(const meshtastic_MeshPacket &p);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Other functions like the node picker can request a pause in the node sorting
|
* Other functions like the node picker can request a pause in the node sorting
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ void PhoneAPI::close()
|
|||||||
config_nonce = 0;
|
config_nonce = 0;
|
||||||
config_state = 0;
|
config_state = 0;
|
||||||
pauseBluetoothLogging = false;
|
pauseBluetoothLogging = false;
|
||||||
|
heartbeatReceived = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
#define RF95_MAX_POWER settingsMap[rf95_max_power]
|
#define RF95_MAX_POWER portduino_config.rf95_max_power
|
||||||
#endif
|
#endif
|
||||||
#ifndef RF95_MAX_POWER
|
#ifndef RF95_MAX_POWER
|
||||||
#define RF95_MAX_POWER 20
|
#define RF95_MAX_POWER 20
|
||||||
@@ -94,16 +94,16 @@ void RF95Interface::setTransmitEnable(bool txon)
|
|||||||
#ifdef RF95_TXEN
|
#ifdef RF95_TXEN
|
||||||
digitalWrite(RF95_TXEN, txon ? 1 : 0);
|
digitalWrite(RF95_TXEN, txon ? 1 : 0);
|
||||||
#elif ARCH_PORTDUINO
|
#elif ARCH_PORTDUINO
|
||||||
if (settingsMap[txen_pin] != RADIOLIB_NC) {
|
if (portduino_config.lora_txen_pin.pin != RADIOLIB_NC) {
|
||||||
digitalWrite(settingsMap[txen_pin], txon ? 1 : 0);
|
digitalWrite(portduino_config.lora_txen_pin.pin, txon ? 1 : 0);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef RF95_RXEN
|
#ifdef RF95_RXEN
|
||||||
digitalWrite(RF95_RXEN, txon ? 0 : 1);
|
digitalWrite(RF95_RXEN, txon ? 0 : 1);
|
||||||
#elif ARCH_PORTDUINO
|
#elif ARCH_PORTDUINO
|
||||||
if (settingsMap[rxen_pin] != RADIOLIB_NC) {
|
if (portduino_config.lora_rxen_pin.pin != RADIOLIB_NC) {
|
||||||
digitalWrite(settingsMap[rxen_pin], txon ? 0 : 1);
|
digitalWrite(portduino_config.lora_rxen_pin.pin, txon ? 0 : 1);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -164,13 +164,13 @@ bool RF95Interface::init()
|
|||||||
digitalWrite(RF95_RXEN, 1);
|
digitalWrite(RF95_RXEN, 1);
|
||||||
#endif
|
#endif
|
||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
if (settingsMap[txen_pin] != RADIOLIB_NC) {
|
if (portduino_config.lora_txen_pin.pin != RADIOLIB_NC) {
|
||||||
pinMode(settingsMap[txen_pin], OUTPUT);
|
pinMode(portduino_config.lora_txen_pin.pin, OUTPUT);
|
||||||
digitalWrite(settingsMap[txen_pin], 0);
|
digitalWrite(portduino_config.lora_txen_pin.pin, 0);
|
||||||
}
|
}
|
||||||
if (settingsMap[rxen_pin] != RADIOLIB_NC) {
|
if (portduino_config.lora_rxen_pin.pin != RADIOLIB_NC) {
|
||||||
pinMode(settingsMap[rxen_pin], OUTPUT);
|
pinMode(portduino_config.lora_rxen_pin.pin, OUTPUT);
|
||||||
digitalWrite(settingsMap[rxen_pin], 0);
|
digitalWrite(portduino_config.lora_rxen_pin.pin, 0);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
setTransmitEnable(false);
|
setTransmitEnable(false);
|
||||||
|
|||||||
@@ -311,16 +311,33 @@ uint32_t RadioInterface::getTxDelayMsecWeightedWorst(float snr)
|
|||||||
return (2 * CWmax * slotTimeMsec) + pow_of_2(CWsize) * slotTimeMsec;
|
return (2 * CWmax * slotTimeMsec) + pow_of_2(CWsize) * slotTimeMsec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns true if we should rebroadcast early like a ROUTER */
|
||||||
|
bool RadioInterface::shouldRebroadcastEarlyLikeRouter(meshtastic_MeshPacket *p)
|
||||||
|
{
|
||||||
|
// If we are a ROUTER or REPEATER, we always rebroadcast early
|
||||||
|
if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ||
|
||||||
|
config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/** The delay to use when we want to flood a message */
|
/** The delay to use when we want to flood a message */
|
||||||
uint32_t RadioInterface::getTxDelayMsecWeighted(float snr)
|
uint32_t RadioInterface::getTxDelayMsecWeighted(meshtastic_MeshPacket *p)
|
||||||
{
|
{
|
||||||
// high SNR = large CW size (Long Delay)
|
// high SNR = large CW size (Long Delay)
|
||||||
// low SNR = small CW size (Short Delay)
|
// low SNR = small CW size (Short Delay)
|
||||||
|
float snr = p->rx_snr;
|
||||||
uint32_t delay = 0;
|
uint32_t delay = 0;
|
||||||
uint8_t CWsize = getCWsize(snr);
|
uint8_t CWsize = getCWsize(snr);
|
||||||
// LOG_DEBUG("rx_snr of %f so setting CWsize to:%d", snr, CWsize);
|
// LOG_DEBUG("rx_snr of %f so setting CWsize to:%d", snr, CWsize);
|
||||||
if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ||
|
if (shouldRebroadcastEarlyLikeRouter(p)) {
|
||||||
config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) {
|
|
||||||
delay = random(0, 2 * CWsize) * slotTimeMsec;
|
delay = random(0, 2 * CWsize) * slotTimeMsec;
|
||||||
LOG_DEBUG("rx_snr found in packet. Router: setting tx delay:%d", delay);
|
LOG_DEBUG("rx_snr found in packet. Router: setting tx delay:%d", delay);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -180,8 +180,11 @@ class RadioInterface
|
|||||||
/** The worst-case SNR_based packet delay */
|
/** The worst-case SNR_based packet delay */
|
||||||
uint32_t getTxDelayMsecWeightedWorst(float snr);
|
uint32_t getTxDelayMsecWeightedWorst(float snr);
|
||||||
|
|
||||||
|
/** Returns true if we should rebroadcast early like a ROUTER */
|
||||||
|
bool shouldRebroadcastEarlyLikeRouter(meshtastic_MeshPacket *p);
|
||||||
|
|
||||||
/** The delay to use when we want to flood a message. Use a weighted scale based on SNR */
|
/** The delay to use when we want to flood a message. Use a weighted scale based on SNR */
|
||||||
uint32_t getTxDelayMsecWeighted(float snr);
|
uint32_t getTxDelayMsecWeighted(meshtastic_MeshPacket *p);
|
||||||
|
|
||||||
/** If the packet is not already in the late rebroadcast window, move it there */
|
/** If the packet is not already in the late rebroadcast window, move it there */
|
||||||
virtual void clampToLateRebroadcastWindow(NodeNum from, PacketId id) { return; }
|
virtual void clampToLateRebroadcastWindow(NodeNum from, PacketId id) { return; }
|
||||||
|
|||||||
@@ -310,7 +310,7 @@ void RadioLibInterface::setTransmitDelay()
|
|||||||
// So we want to make sure the other side has had a chance to reconfigure its radio.
|
// So we want to make sure the other side has had a chance to reconfigure its radio.
|
||||||
|
|
||||||
if (p->tx_after) {
|
if (p->tx_after) {
|
||||||
unsigned long add_delay = p->rx_rssi ? getTxDelayMsecWeighted(p->rx_snr) : getTxDelayMsec();
|
unsigned long add_delay = p->rx_rssi ? getTxDelayMsecWeighted(p) : getTxDelayMsec();
|
||||||
unsigned long now = millis();
|
unsigned long now = millis();
|
||||||
p->tx_after = min(max(p->tx_after + add_delay, now + add_delay), now + 2 * getTxDelayMsecWeightedWorst(p->rx_snr));
|
p->tx_after = min(max(p->tx_after + add_delay, now + add_delay), now + 2 * getTxDelayMsecWeightedWorst(p->rx_snr));
|
||||||
notifyLater(p->tx_after - now, TRANSMIT_DELAY_COMPLETED, false);
|
notifyLater(p->tx_after - now, TRANSMIT_DELAY_COMPLETED, false);
|
||||||
@@ -323,7 +323,7 @@ void RadioLibInterface::setTransmitDelay()
|
|||||||
} else {
|
} else {
|
||||||
// If there is a SNR, start a timer scaled based on that SNR.
|
// If there is a SNR, start a timer scaled based on that SNR.
|
||||||
LOG_DEBUG("rx_snr found. hop_limit:%d rx_snr:%f", p->hop_limit, p->rx_snr);
|
LOG_DEBUG("rx_snr found. hop_limit:%d rx_snr:%f", p->hop_limit, p->rx_snr);
|
||||||
startTransmitTimerSNR(p->rx_snr);
|
startTransmitTimerRebroadcast(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,11 +336,11 @@ void RadioLibInterface::startTransmitTimer(bool withDelay)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RadioLibInterface::startTransmitTimerSNR(float snr)
|
void RadioLibInterface::startTransmitTimerRebroadcast(meshtastic_MeshPacket *p)
|
||||||
{
|
{
|
||||||
// If we have work to do and the timer wasn't already scheduled, schedule it now
|
// If we have work to do and the timer wasn't already scheduled, schedule it now
|
||||||
if (!txQueue.empty()) {
|
if (!txQueue.empty()) {
|
||||||
uint32_t delay = getTxDelayMsecWeighted(snr);
|
uint32_t delay = getTxDelayMsecWeighted(p);
|
||||||
notifyLater(delay, TRANSMIT_DELAY_COMPLETED, false); // This will implicitly enable
|
notifyLater(delay, TRANSMIT_DELAY_COMPLETED, false); // This will implicitly enable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -417,7 +417,7 @@ void RadioLibInterface::handleReceiveInterrupt()
|
|||||||
|
|
||||||
int state = iface->readData((uint8_t *)&radioBuffer, length);
|
int state = iface->readData((uint8_t *)&radioBuffer, length);
|
||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
if (settingsMap[logoutputlevel] == level_trace) {
|
if (portduino_config.logoutputlevel == level_trace) {
|
||||||
printBytes("Raw incoming packet: ", (uint8_t *)&radioBuffer, length);
|
printBytes("Raw incoming packet: ", (uint8_t *)&radioBuffer, length);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
|
|||||||
* timer scaled to SNR of to be flooded packet
|
* timer scaled to SNR of to be flooded packet
|
||||||
* @return Timestamp after which the packet may be sent
|
* @return Timestamp after which the packet may be sent
|
||||||
*/
|
*/
|
||||||
void startTransmitTimerSNR(float snr);
|
void startTransmitTimerRebroadcast(meshtastic_MeshPacket *p);
|
||||||
|
|
||||||
void handleTransmitInterrupt();
|
void handleTransmitInterrupt();
|
||||||
void handleReceiveInterrupt();
|
void handleReceiveInterrupt();
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "Default.h"
|
#include "Default.h"
|
||||||
#include "MeshTypes.h"
|
#include "MeshTypes.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
|
#include "memGet.h"
|
||||||
#include "mesh-pb-constants.h"
|
#include "mesh-pb-constants.h"
|
||||||
#include "modules/NodeInfoModule.h"
|
#include "modules/NodeInfoModule.h"
|
||||||
#include "modules/RoutingModule.h"
|
#include "modules/RoutingModule.h"
|
||||||
@@ -21,8 +22,10 @@ ErrorCode ReliableRouter::send(meshtastic_MeshPacket *p)
|
|||||||
if (p->hop_limit == 0) {
|
if (p->hop_limit == 0) {
|
||||||
p->hop_limit = Default::getConfiguredOrDefaultHopLimit(config.lora.hop_limit);
|
p->hop_limit = Default::getConfiguredOrDefaultHopLimit(config.lora.hop_limit);
|
||||||
}
|
}
|
||||||
|
DEBUG_HEAP_BEFORE;
|
||||||
auto copy = packetPool.allocCopy(*p);
|
auto copy = packetPool.allocCopy(*p);
|
||||||
|
DEBUG_HEAP_AFTER("ReliableRouter::send", copy);
|
||||||
|
|
||||||
startRetransmission(copy, NUM_RELIABLE_RETX);
|
startRetransmission(copy, NUM_RELIABLE_RETX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "MeshService.h"
|
#include "MeshService.h"
|
||||||
#include "NodeDB.h"
|
#include "NodeDB.h"
|
||||||
#include "RTC.h"
|
#include "RTC.h"
|
||||||
|
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "detect/LoRaRadioType.h"
|
#include "detect/LoRaRadioType.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
@@ -27,14 +28,24 @@
|
|||||||
|
|
||||||
// I think this is right, one packet for each of the three fifos + one packet being currently assembled for TX or RX
|
// I think this is right, one packet for each of the three fifos + one packet being currently assembled for TX or RX
|
||||||
// And every TX packet might have a retransmission packet or an ack alive at any moment
|
// And every TX packet might have a retransmission packet or an ack alive at any moment
|
||||||
|
|
||||||
|
#ifdef ARCH_PORTDUINO
|
||||||
|
// Portduino (native) targets can use dynamic memory pools with runtime-configurable sizes
|
||||||
#define MAX_PACKETS \
|
#define MAX_PACKETS \
|
||||||
(MAX_RX_TOPHONE + MAX_RX_FROMRADIO + 2 * MAX_TX_QUEUE + \
|
(MAX_RX_TOPHONE + MAX_RX_FROMRADIO + 2 * MAX_TX_QUEUE + \
|
||||||
2) // max number of packets which can be in flight (either queued from reception or queued for sending)
|
2) // max number of packets which can be in flight (either queued from reception or queued for sending)
|
||||||
|
|
||||||
// static MemoryPool<MeshPacket> staticPool(MAX_PACKETS);
|
static MemoryDynamic<meshtastic_MeshPacket> dynamicPool;
|
||||||
static MemoryDynamic<meshtastic_MeshPacket> staticPool;
|
Allocator<meshtastic_MeshPacket> &packetPool = dynamicPool;
|
||||||
|
#else
|
||||||
|
// Embedded targets use static memory pools with compile-time constants
|
||||||
|
#define MAX_PACKETS_STATIC \
|
||||||
|
(MAX_RX_TOPHONE + MAX_RX_FROMRADIO + 2 * MAX_TX_QUEUE + \
|
||||||
|
2) // max number of packets which can be in flight (either queued from reception or queued for sending)
|
||||||
|
|
||||||
|
static MemoryPool<meshtastic_MeshPacket, MAX_PACKETS_STATIC> staticPool;
|
||||||
Allocator<meshtastic_MeshPacket> &packetPool = staticPool;
|
Allocator<meshtastic_MeshPacket> &packetPool = staticPool;
|
||||||
|
#endif
|
||||||
|
|
||||||
static uint8_t bytes[MAX_LORA_PAYLOAD_LEN + 1] __attribute__((__aligned__));
|
static uint8_t bytes[MAX_LORA_PAYLOAD_LEN + 1] __attribute__((__aligned__));
|
||||||
|
|
||||||
@@ -275,7 +286,10 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
|
|||||||
// If the packet is not yet encrypted, do so now
|
// If the packet is not yet encrypted, do so now
|
||||||
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
|
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
|
||||||
ChannelIndex chIndex = p->channel; // keep as a local because we are about to change it
|
ChannelIndex chIndex = p->channel; // keep as a local because we are about to change it
|
||||||
|
|
||||||
|
DEBUG_HEAP_BEFORE;
|
||||||
meshtastic_MeshPacket *p_decoded = packetPool.allocCopy(*p);
|
meshtastic_MeshPacket *p_decoded = packetPool.allocCopy(*p);
|
||||||
|
DEBUG_HEAP_AFTER("Router::send", p_decoded);
|
||||||
|
|
||||||
auto encodeResult = perhapsEncode(p);
|
auto encodeResult = perhapsEncode(p);
|
||||||
if (encodeResult != meshtastic_Routing_Error_NONE) {
|
if (encodeResult != meshtastic_Routing_Error_NONE) {
|
||||||
@@ -416,6 +430,36 @@ DecodeState perhapsDecode(meshtastic_MeshPacket *p)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if HAS_UDP_MULTICAST
|
||||||
|
// Fallback: for UDP multicast, try default preset names with default PSK if normal channel match failed
|
||||||
|
if (!decrypted && p->transport_mechanism == meshtastic_MeshPacket_TransportMechanism_TRANSPORT_MULTICAST_UDP) {
|
||||||
|
if (channels.setDefaultPresetCryptoForHash(p->channel)) {
|
||||||
|
memcpy(bytes, p->encrypted.bytes, rawSize);
|
||||||
|
crypto->decrypt(p->from, p->id, rawSize, bytes);
|
||||||
|
|
||||||
|
meshtastic_Data decodedtmp;
|
||||||
|
memset(&decodedtmp, 0, sizeof(decodedtmp));
|
||||||
|
if (pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &decodedtmp) &&
|
||||||
|
decodedtmp.portnum != meshtastic_PortNum_UNKNOWN_APP) {
|
||||||
|
p->decoded = decodedtmp;
|
||||||
|
p->which_payload_variant = meshtastic_MeshPacket_decoded_tag;
|
||||||
|
// Map to our local default channel index (name+PSK default), not necessarily primary
|
||||||
|
ChannelIndex defaultIndex = channels.getPrimaryIndex();
|
||||||
|
for (ChannelIndex i = 0; i < channels.getNumChannels(); ++i) {
|
||||||
|
if (channels.isDefaultChannel(i)) {
|
||||||
|
defaultIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chIndex = defaultIndex;
|
||||||
|
decrypted = true;
|
||||||
|
} else {
|
||||||
|
LOG_WARN("UDP fallback decode attempted but failed for hash 0x%x", p->channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (decrypted) {
|
if (decrypted) {
|
||||||
// parsing was successful
|
// parsing was successful
|
||||||
p->channel = chIndex; // change to store the index instead of the hash
|
p->channel = chIndex; // change to store the index instead of the hash
|
||||||
@@ -446,7 +490,7 @@ DecodeState perhapsDecode(meshtastic_MeshPacket *p)
|
|||||||
#if ENABLE_JSON_LOGGING
|
#if ENABLE_JSON_LOGGING
|
||||||
LOG_TRACE("%s", MeshPacketSerializer::JsonSerialize(p, false).c_str());
|
LOG_TRACE("%s", MeshPacketSerializer::JsonSerialize(p, false).c_str());
|
||||||
#elif ARCH_PORTDUINO
|
#elif ARCH_PORTDUINO
|
||||||
if (settingsStrings[traceFilename] != "" || settingsMap[logoutputlevel] == level_trace) {
|
if (portduino_config.traceFilename != "" || portduino_config.logoutputlevel == level_trace) {
|
||||||
LOG_TRACE("%s", MeshPacketSerializer::JsonSerialize(p, false).c_str());
|
LOG_TRACE("%s", MeshPacketSerializer::JsonSerialize(p, false).c_str());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -607,8 +651,11 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
|
|||||||
bool skipHandle = false;
|
bool skipHandle = false;
|
||||||
// Also, we should set the time from the ISR and it should have msec level resolution
|
// Also, we should set the time from the ISR and it should have msec level resolution
|
||||||
p->rx_time = getValidTime(RTCQualityFromNet); // store the arrival timestamp for the phone
|
p->rx_time = getValidTime(RTCQualityFromNet); // store the arrival timestamp for the phone
|
||||||
|
|
||||||
// Store a copy of encrypted packet for MQTT
|
// Store a copy of encrypted packet for MQTT
|
||||||
|
DEBUG_HEAP_BEFORE;
|
||||||
meshtastic_MeshPacket *p_encrypted = packetPool.allocCopy(*p);
|
meshtastic_MeshPacket *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
|
// Take those raw bytes and convert them back into a well structured protobuf we can understand
|
||||||
auto decodedState = perhapsDecode(p);
|
auto decodedState = perhapsDecode(p);
|
||||||
@@ -656,7 +703,7 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
|
|||||||
|
|
||||||
// call modules here
|
// call modules here
|
||||||
// If this could be a spoofed packet, don't let the modules see it.
|
// If this could be a spoofed packet, don't let the modules see it.
|
||||||
if (!skipHandle && p->from != nodeDB->getNodeNum()) {
|
if (!skipHandle) {
|
||||||
MeshModule::callModules(*p, src);
|
MeshModule::callModules(*p, src);
|
||||||
|
|
||||||
#if !MESHTASTIC_EXCLUDE_MQTT
|
#if !MESHTASTIC_EXCLUDE_MQTT
|
||||||
@@ -670,8 +717,6 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
|
|||||||
!isFromUs(p) && mqtt)
|
!isFromUs(p) && mqtt)
|
||||||
mqtt->onSend(*p_encrypted, *p, p->channel);
|
mqtt->onSend(*p_encrypted, *p, p->channel);
|
||||||
#endif
|
#endif
|
||||||
} else if (p->from == nodeDB->getNodeNum() && !skipHandle) {
|
|
||||||
MeshModule::callModules(*p, src);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
packetPool.release(p_encrypted); // Release the encrypted packet
|
packetPool.release(p_encrypted); // Release the encrypted packet
|
||||||
@@ -685,7 +730,7 @@ void Router::perhapsHandleReceived(meshtastic_MeshPacket *p)
|
|||||||
LOG_TRACE("%s", MeshPacketSerializer::JsonSerializeEncrypted(p).c_str());
|
LOG_TRACE("%s", MeshPacketSerializer::JsonSerializeEncrypted(p).c_str());
|
||||||
#elif ARCH_PORTDUINO
|
#elif ARCH_PORTDUINO
|
||||||
// Even ignored packets get logged in the trace
|
// Even ignored packets get logged in the trace
|
||||||
if (settingsStrings[traceFilename] != "" || settingsMap[logoutputlevel] == level_trace) {
|
if (portduino_config.traceFilename != "" || portduino_config.logoutputlevel == level_trace) {
|
||||||
p->rx_time = getValidTime(RTCQualityFromNet); // store the arrival timestamp for the phone
|
p->rx_time = getValidTime(RTCQualityFromNet); // store the arrival timestamp for the phone
|
||||||
LOG_TRACE("%s", MeshPacketSerializer::JsonSerializeEncrypted(p).c_str());
|
LOG_TRACE("%s", MeshPacketSerializer::JsonSerializeEncrypted(p).c_str());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
// Particular boards might define a different max power based on what their hardware can do, default to max power output if not
|
// Particular boards might define a different max power based on what their hardware can do, default to max power output if not
|
||||||
// specified (may be dangerous if using external PA and SX126x power config forgotten)
|
// specified (may be dangerous if using external PA and SX126x power config forgotten)
|
||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
#define SX126X_MAX_POWER settingsMap[sx126x_max_power]
|
#define SX126X_MAX_POWER portduino_config.sx126x_max_power
|
||||||
#endif
|
#endif
|
||||||
#ifndef SX126X_MAX_POWER
|
#ifndef SX126X_MAX_POWER
|
||||||
#define SX126X_MAX_POWER 22
|
#define SX126X_MAX_POWER 22
|
||||||
@@ -53,10 +53,10 @@ template <typename T> bool SX126xInterface<T>::init()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
tcxoVoltage = (float)settingsMap[dio3_tcxo_voltage] / 1000;
|
tcxoVoltage = (float)portduino_config.dio3_tcxo_voltage / 1000;
|
||||||
if (settingsMap[sx126x_ant_sw_pin] != RADIOLIB_NC) {
|
if (portduino_config.lora_sx126x_ant_sw_pin.pin != RADIOLIB_NC) {
|
||||||
digitalWrite(settingsMap[sx126x_ant_sw_pin], HIGH);
|
digitalWrite(portduino_config.lora_sx126x_ant_sw_pin.pin, HIGH);
|
||||||
pinMode(settingsMap[sx126x_ant_sw_pin], OUTPUT);
|
pinMode(portduino_config.lora_sx126x_ant_sw_pin.pin, OUTPUT);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (tcxoVoltage == 0.0)
|
if (tcxoVoltage == 0.0)
|
||||||
@@ -98,7 +98,7 @@ template <typename T> bool SX126xInterface<T>::init()
|
|||||||
bool dio2AsRfSwitch = true;
|
bool dio2AsRfSwitch = true;
|
||||||
#elif defined(ARCH_PORTDUINO)
|
#elif defined(ARCH_PORTDUINO)
|
||||||
bool dio2AsRfSwitch = false;
|
bool dio2AsRfSwitch = false;
|
||||||
if (settingsMap[dio2_as_rf_switch]) {
|
if (portduino_config.dio2_as_rf_switch) {
|
||||||
dio2AsRfSwitch = true;
|
dio2AsRfSwitch = true;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@@ -112,9 +112,9 @@ template <typename T> bool SX126xInterface<T>::init()
|
|||||||
// no effect
|
// no effect
|
||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
if (res == RADIOLIB_ERR_NONE) {
|
if (res == RADIOLIB_ERR_NONE) {
|
||||||
LOG_DEBUG("Use MCU pin %i as RXEN and pin %i as TXEN to control RF switching", settingsMap[rxen_pin],
|
LOG_DEBUG("Use MCU pin %i as RXEN and pin %i as TXEN to control RF switching", portduino_config.lora_rxen_pin.pin,
|
||||||
settingsMap[txen_pin]);
|
portduino_config.lora_txen_pin.pin);
|
||||||
lora.setRfSwitchPins(settingsMap[rxen_pin], settingsMap[txen_pin]);
|
lora.setRfSwitchPins(portduino_config.lora_rxen_pin.pin, portduino_config.lora_txen_pin.pin);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
#ifndef SX126X_RXEN
|
#ifndef SX126X_RXEN
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
// Particular boards might define a different max power based on what their hardware can do
|
// Particular boards might define a different max power based on what their hardware can do
|
||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
#define SX128X_MAX_POWER settingsMap[sx128x_max_power]
|
#define SX128X_MAX_POWER portduino_config.sx128x_max_power
|
||||||
#endif
|
#endif
|
||||||
#ifndef SX128X_MAX_POWER
|
#ifndef SX128X_MAX_POWER
|
||||||
#define SX128X_MAX_POWER 13
|
#define SX128X_MAX_POWER 13
|
||||||
@@ -41,13 +41,13 @@ template <typename T> bool SX128xInterface<T>::init()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
if (settingsMap[rxen_pin] != RADIOLIB_NC) {
|
if (portduino_config.lora_rxen_pin.pin != RADIOLIB_NC) {
|
||||||
pinMode(settingsMap[rxen_pin], OUTPUT);
|
pinMode(portduino_config.lora_rxen_pin.pin, OUTPUT);
|
||||||
digitalWrite(settingsMap[rxen_pin], LOW); // Set low before becoming an output
|
digitalWrite(portduino_config.lora_rxen_pin.pin, LOW); // Set low before becoming an output
|
||||||
}
|
}
|
||||||
if (settingsMap[txen_pin] != RADIOLIB_NC) {
|
if (portduino_config.lora_txen_pin.pin != RADIOLIB_NC) {
|
||||||
pinMode(settingsMap[txen_pin], OUTPUT);
|
pinMode(portduino_config.lora_txen_pin.pin, OUTPUT);
|
||||||
digitalWrite(settingsMap[txen_pin], LOW); // Set low before becoming an output
|
digitalWrite(portduino_config.lora_txen_pin.pin, LOW); // Set low before becoming an output
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
#if defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC) // set not rx or tx mode
|
#if defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC) // set not rx or tx mode
|
||||||
@@ -93,8 +93,9 @@ template <typename T> bool SX128xInterface<T>::init()
|
|||||||
lora.setRfSwitchPins(SX128X_RXEN, SX128X_TXEN);
|
lora.setRfSwitchPins(SX128X_RXEN, SX128X_TXEN);
|
||||||
}
|
}
|
||||||
#elif ARCH_PORTDUINO
|
#elif ARCH_PORTDUINO
|
||||||
if (res == RADIOLIB_ERR_NONE && settingsMap[rxen_pin] != RADIOLIB_NC && settingsMap[txen_pin] != RADIOLIB_NC) {
|
if (res == RADIOLIB_ERR_NONE && portduino_config.lora_rxen_pin.pin != RADIOLIB_NC &&
|
||||||
lora.setRfSwitchPins(settingsMap[rxen_pin], settingsMap[txen_pin]);
|
portduino_config.lora_txen_pin.pin != RADIOLIB_NC) {
|
||||||
|
lora.setRfSwitchPins(portduino_config.lora_rxen_pin.pin, portduino_config.lora_txen_pin.pin);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -174,11 +175,11 @@ template <typename T> void SX128xInterface<T>::setStandby()
|
|||||||
LOG_ERROR("SX128x standby %s%d", radioLibErr, err);
|
LOG_ERROR("SX128x standby %s%d", radioLibErr, err);
|
||||||
assert(err == RADIOLIB_ERR_NONE);
|
assert(err == RADIOLIB_ERR_NONE);
|
||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
if (settingsMap[rxen_pin] != RADIOLIB_NC) {
|
if (portduino_config.lora_rxen_pin.pin != RADIOLIB_NC) {
|
||||||
digitalWrite(settingsMap[rxen_pin], LOW);
|
digitalWrite(portduino_config.lora_rxen_pin.pin, LOW);
|
||||||
}
|
}
|
||||||
if (settingsMap[txen_pin] != RADIOLIB_NC) {
|
if (portduino_config.lora_txen_pin.pin != RADIOLIB_NC) {
|
||||||
digitalWrite(settingsMap[txen_pin], LOW);
|
digitalWrite(portduino_config.lora_txen_pin.pin, LOW);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
#if defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC) // we have RXEN/TXEN control - turn off RX and TX power
|
#if defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC) // we have RXEN/TXEN control - turn off RX and TX power
|
||||||
@@ -210,11 +211,11 @@ template <typename T> void SX128xInterface<T>::addReceiveMetadata(meshtastic_Mes
|
|||||||
template <typename T> void SX128xInterface<T>::configHardwareForSend()
|
template <typename T> void SX128xInterface<T>::configHardwareForSend()
|
||||||
{
|
{
|
||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
if (settingsMap[txen_pin] != RADIOLIB_NC) {
|
if (portduino_config.lora_txen_pin.pin != RADIOLIB_NC) {
|
||||||
digitalWrite(settingsMap[txen_pin], HIGH);
|
digitalWrite(portduino_config.lora_txen_pin.pin, HIGH);
|
||||||
}
|
}
|
||||||
if (settingsMap[rxen_pin] != RADIOLIB_NC) {
|
if (portduino_config.lora_rxen_pin.pin != RADIOLIB_NC) {
|
||||||
digitalWrite(settingsMap[rxen_pin], LOW);
|
digitalWrite(portduino_config.lora_rxen_pin.pin, LOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
@@ -241,11 +242,11 @@ template <typename T> void SX128xInterface<T>::startReceive()
|
|||||||
setStandby();
|
setStandby();
|
||||||
|
|
||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
if (settingsMap[rxen_pin] != RADIOLIB_NC) {
|
if (portduino_config.lora_rxen_pin.pin != RADIOLIB_NC) {
|
||||||
digitalWrite(settingsMap[rxen_pin], HIGH);
|
digitalWrite(portduino_config.lora_rxen_pin.pin, HIGH);
|
||||||
}
|
}
|
||||||
if (settingsMap[txen_pin] != RADIOLIB_NC) {
|
if (portduino_config.lora_txen_pin.pin != RADIOLIB_NC) {
|
||||||
digitalWrite(settingsMap[txen_pin], LOW);
|
digitalWrite(portduino_config.lora_txen_pin.pin, LOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|||||||
77
src/mesh/StaticPointerQueue.h
Normal file
77
src/mesh/StaticPointerQueue.h
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "concurrency/OSThread.h"
|
||||||
|
#include "freertosinc.h"
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A static circular buffer queue for pointers.
|
||||||
|
* This provides the same interface as PointerQueue but uses a statically allocated
|
||||||
|
* buffer instead of dynamic allocation.
|
||||||
|
*/
|
||||||
|
template <class T, int MaxElements> class StaticPointerQueue
|
||||||
|
{
|
||||||
|
static_assert(MaxElements > 0, "MaxElements must be greater than 0");
|
||||||
|
|
||||||
|
T *buffer[MaxElements];
|
||||||
|
int head = 0;
|
||||||
|
int tail = 0;
|
||||||
|
int count = 0;
|
||||||
|
concurrency::OSThread *reader = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
StaticPointerQueue()
|
||||||
|
{
|
||||||
|
// Initialize all buffer elements to nullptr to silence warnings and ensure clean state
|
||||||
|
for (int i = 0; i < MaxElements; i++) {
|
||||||
|
buffer[i] = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int numFree() const { return MaxElements - count; }
|
||||||
|
|
||||||
|
bool isEmpty() const { return count == 0; }
|
||||||
|
|
||||||
|
int numUsed() const { return count; }
|
||||||
|
|
||||||
|
bool enqueue(T *x, TickType_t maxWait = portMAX_DELAY)
|
||||||
|
{
|
||||||
|
if (count >= MaxElements) {
|
||||||
|
return false; // Queue is full
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reader) {
|
||||||
|
reader->setInterval(0);
|
||||||
|
concurrency::mainDelay.interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[tail] = x;
|
||||||
|
tail = (tail + 1) % MaxElements;
|
||||||
|
count++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool dequeue(T **p, TickType_t maxWait = portMAX_DELAY)
|
||||||
|
{
|
||||||
|
if (count == 0) {
|
||||||
|
return false; // Queue is empty
|
||||||
|
}
|
||||||
|
|
||||||
|
*p = buffer[head];
|
||||||
|
head = (head + 1) % MaxElements;
|
||||||
|
count--;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a ptr or null if the queue was empty
|
||||||
|
T *dequeuePtr(TickType_t maxWait = portMAX_DELAY)
|
||||||
|
{
|
||||||
|
T *p;
|
||||||
|
return dequeue(&p, maxWait) ? p : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setReader(concurrency::OSThread *t) { reader = t; }
|
||||||
|
|
||||||
|
// For compatibility with PointerQueue interface
|
||||||
|
int getMaxLen() const { return MaxElements; }
|
||||||
|
};
|
||||||
@@ -173,28 +173,10 @@ typedef enum _meshtastic_Config_NetworkConfig_ProtocolFlags {
|
|||||||
meshtastic_Config_NetworkConfig_ProtocolFlags_UDP_BROADCAST = 1
|
meshtastic_Config_NetworkConfig_ProtocolFlags_UDP_BROADCAST = 1
|
||||||
} meshtastic_Config_NetworkConfig_ProtocolFlags;
|
} meshtastic_Config_NetworkConfig_ProtocolFlags;
|
||||||
|
|
||||||
/* How the GPS coordinates are displayed on the OLED screen. */
|
/* Deprecated in 2.7.4: Unused */
|
||||||
typedef enum _meshtastic_Config_DisplayConfig_GpsCoordinateFormat {
|
typedef enum _meshtastic_Config_DisplayConfig_DeprecatedGpsCoordinateFormat {
|
||||||
/* GPS coordinates are displayed in the normal decimal degrees format:
|
meshtastic_Config_DisplayConfig_DeprecatedGpsCoordinateFormat_UNUSED = 0
|
||||||
DD.DDDDDD DDD.DDDDDD */
|
} meshtastic_Config_DisplayConfig_DeprecatedGpsCoordinateFormat;
|
||||||
meshtastic_Config_DisplayConfig_GpsCoordinateFormat_DEC = 0,
|
|
||||||
/* GPS coordinates are displayed in the degrees minutes seconds format:
|
|
||||||
DD°MM'SS"C DDD°MM'SS"C, where C is the compass point representing the locations quadrant */
|
|
||||||
meshtastic_Config_DisplayConfig_GpsCoordinateFormat_DMS = 1,
|
|
||||||
/* Universal Transverse Mercator format:
|
|
||||||
ZZB EEEEEE NNNNNNN, where Z is zone, B is band, E is easting, N is northing */
|
|
||||||
meshtastic_Config_DisplayConfig_GpsCoordinateFormat_UTM = 2,
|
|
||||||
/* Military Grid Reference System format:
|
|
||||||
ZZB CD EEEEE NNNNN, where Z is zone, B is band, C is the east 100k square, D is the north 100k square,
|
|
||||||
E is easting, N is northing */
|
|
||||||
meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MGRS = 3,
|
|
||||||
/* Open Location Code (aka Plus Codes). */
|
|
||||||
meshtastic_Config_DisplayConfig_GpsCoordinateFormat_OLC = 4,
|
|
||||||
/* Ordnance Survey Grid Reference (the National Grid System of the UK).
|
|
||||||
Format: AB EEEEE NNNNN, where A is the east 100k square, B is the north 100k square,
|
|
||||||
E is the easting, N is the northing */
|
|
||||||
meshtastic_Config_DisplayConfig_GpsCoordinateFormat_OSGR = 5
|
|
||||||
} meshtastic_Config_DisplayConfig_GpsCoordinateFormat;
|
|
||||||
|
|
||||||
/* Unit display preference */
|
/* Unit display preference */
|
||||||
typedef enum _meshtastic_Config_DisplayConfig_DisplayUnits {
|
typedef enum _meshtastic_Config_DisplayConfig_DisplayUnits {
|
||||||
@@ -491,7 +473,7 @@ typedef struct _meshtastic_Config_DisplayConfig {
|
|||||||
uint32_t screen_on_secs;
|
uint32_t screen_on_secs;
|
||||||
/* Deprecated in 2.7.4: Unused
|
/* Deprecated in 2.7.4: Unused
|
||||||
How the GPS coordinates are formatted on the OLED screen. */
|
How the GPS coordinates are formatted on the OLED screen. */
|
||||||
meshtastic_Config_DisplayConfig_GpsCoordinateFormat gps_format;
|
meshtastic_Config_DisplayConfig_DeprecatedGpsCoordinateFormat gps_format;
|
||||||
/* Automatically toggles to the next page on the screen like a carousel, based the specified interval in seconds.
|
/* Automatically toggles to the next page on the screen like a carousel, based the specified interval in seconds.
|
||||||
Potentially useful for devices without user buttons. */
|
Potentially useful for devices without user buttons. */
|
||||||
uint32_t auto_screen_carousel_secs;
|
uint32_t auto_screen_carousel_secs;
|
||||||
@@ -515,6 +497,9 @@ typedef struct _meshtastic_Config_DisplayConfig {
|
|||||||
/* If false (default), the device will display the time in 24-hour format on screen.
|
/* If false (default), the device will display the time in 24-hour format on screen.
|
||||||
If true, the device will display the time in 12-hour format on screen. */
|
If true, the device will display the time in 12-hour format on screen. */
|
||||||
bool use_12h_clock;
|
bool use_12h_clock;
|
||||||
|
/* If false (default), the device will use short names for various display screens.
|
||||||
|
If true, node names will show in long format */
|
||||||
|
bool use_long_node_name;
|
||||||
} meshtastic_Config_DisplayConfig;
|
} meshtastic_Config_DisplayConfig;
|
||||||
|
|
||||||
/* Lora Config */
|
/* Lora Config */
|
||||||
@@ -678,9 +663,9 @@ extern "C" {
|
|||||||
#define _meshtastic_Config_NetworkConfig_ProtocolFlags_MAX meshtastic_Config_NetworkConfig_ProtocolFlags_UDP_BROADCAST
|
#define _meshtastic_Config_NetworkConfig_ProtocolFlags_MAX meshtastic_Config_NetworkConfig_ProtocolFlags_UDP_BROADCAST
|
||||||
#define _meshtastic_Config_NetworkConfig_ProtocolFlags_ARRAYSIZE ((meshtastic_Config_NetworkConfig_ProtocolFlags)(meshtastic_Config_NetworkConfig_ProtocolFlags_UDP_BROADCAST+1))
|
#define _meshtastic_Config_NetworkConfig_ProtocolFlags_ARRAYSIZE ((meshtastic_Config_NetworkConfig_ProtocolFlags)(meshtastic_Config_NetworkConfig_ProtocolFlags_UDP_BROADCAST+1))
|
||||||
|
|
||||||
#define _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN meshtastic_Config_DisplayConfig_GpsCoordinateFormat_DEC
|
#define _meshtastic_Config_DisplayConfig_DeprecatedGpsCoordinateFormat_MIN meshtastic_Config_DisplayConfig_DeprecatedGpsCoordinateFormat_UNUSED
|
||||||
#define _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MAX meshtastic_Config_DisplayConfig_GpsCoordinateFormat_OSGR
|
#define _meshtastic_Config_DisplayConfig_DeprecatedGpsCoordinateFormat_MAX meshtastic_Config_DisplayConfig_DeprecatedGpsCoordinateFormat_UNUSED
|
||||||
#define _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_ARRAYSIZE ((meshtastic_Config_DisplayConfig_GpsCoordinateFormat)(meshtastic_Config_DisplayConfig_GpsCoordinateFormat_OSGR+1))
|
#define _meshtastic_Config_DisplayConfig_DeprecatedGpsCoordinateFormat_ARRAYSIZE ((meshtastic_Config_DisplayConfig_DeprecatedGpsCoordinateFormat)(meshtastic_Config_DisplayConfig_DeprecatedGpsCoordinateFormat_UNUSED+1))
|
||||||
|
|
||||||
#define _meshtastic_Config_DisplayConfig_DisplayUnits_MIN meshtastic_Config_DisplayConfig_DisplayUnits_METRIC
|
#define _meshtastic_Config_DisplayConfig_DisplayUnits_MIN meshtastic_Config_DisplayConfig_DisplayUnits_METRIC
|
||||||
#define _meshtastic_Config_DisplayConfig_DisplayUnits_MAX meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL
|
#define _meshtastic_Config_DisplayConfig_DisplayUnits_MAX meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL
|
||||||
@@ -721,7 +706,7 @@ extern "C" {
|
|||||||
#define meshtastic_Config_NetworkConfig_address_mode_ENUMTYPE meshtastic_Config_NetworkConfig_AddressMode
|
#define meshtastic_Config_NetworkConfig_address_mode_ENUMTYPE meshtastic_Config_NetworkConfig_AddressMode
|
||||||
|
|
||||||
|
|
||||||
#define meshtastic_Config_DisplayConfig_gps_format_ENUMTYPE meshtastic_Config_DisplayConfig_GpsCoordinateFormat
|
#define meshtastic_Config_DisplayConfig_gps_format_ENUMTYPE meshtastic_Config_DisplayConfig_DeprecatedGpsCoordinateFormat
|
||||||
#define meshtastic_Config_DisplayConfig_units_ENUMTYPE meshtastic_Config_DisplayConfig_DisplayUnits
|
#define meshtastic_Config_DisplayConfig_units_ENUMTYPE meshtastic_Config_DisplayConfig_DisplayUnits
|
||||||
#define meshtastic_Config_DisplayConfig_oled_ENUMTYPE meshtastic_Config_DisplayConfig_OledType
|
#define meshtastic_Config_DisplayConfig_oled_ENUMTYPE meshtastic_Config_DisplayConfig_OledType
|
||||||
#define meshtastic_Config_DisplayConfig_displaymode_ENUMTYPE meshtastic_Config_DisplayConfig_DisplayMode
|
#define meshtastic_Config_DisplayConfig_displaymode_ENUMTYPE meshtastic_Config_DisplayConfig_DisplayMode
|
||||||
@@ -742,7 +727,7 @@ extern "C" {
|
|||||||
#define meshtastic_Config_PowerConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0}
|
#define meshtastic_Config_PowerConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
#define meshtastic_Config_NetworkConfig_init_default {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_default, "", 0, 0}
|
#define meshtastic_Config_NetworkConfig_init_default {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_default, "", 0, 0}
|
||||||
#define meshtastic_Config_NetworkConfig_IpV4Config_init_default {0, 0, 0, 0}
|
#define meshtastic_Config_NetworkConfig_IpV4Config_init_default {0, 0, 0, 0}
|
||||||
#define meshtastic_Config_DisplayConfig_init_default {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN, 0}
|
#define meshtastic_Config_DisplayConfig_init_default {0, _meshtastic_Config_DisplayConfig_DeprecatedGpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN, 0, 0}
|
||||||
#define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0}
|
#define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0}
|
||||||
#define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0}
|
#define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0}
|
||||||
#define meshtastic_Config_SecurityConfig_init_default {{0, {0}}, {0, {0}}, 0, {{0, {0}}, {0, {0}}, {0, {0}}}, 0, 0, 0, 0}
|
#define meshtastic_Config_SecurityConfig_init_default {{0, {0}}, {0, {0}}, 0, {{0, {0}}, {0, {0}}, {0, {0}}}, 0, 0, 0, 0}
|
||||||
@@ -753,7 +738,7 @@ extern "C" {
|
|||||||
#define meshtastic_Config_PowerConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0}
|
#define meshtastic_Config_PowerConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
#define meshtastic_Config_NetworkConfig_init_zero {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_zero, "", 0, 0}
|
#define meshtastic_Config_NetworkConfig_init_zero {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_zero, "", 0, 0}
|
||||||
#define meshtastic_Config_NetworkConfig_IpV4Config_init_zero {0, 0, 0, 0}
|
#define meshtastic_Config_NetworkConfig_IpV4Config_init_zero {0, 0, 0, 0}
|
||||||
#define meshtastic_Config_DisplayConfig_init_zero {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN, 0}
|
#define meshtastic_Config_DisplayConfig_init_zero {0, _meshtastic_Config_DisplayConfig_DeprecatedGpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN, 0, 0}
|
||||||
#define meshtastic_Config_LoRaConfig_init_zero {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0}
|
#define meshtastic_Config_LoRaConfig_init_zero {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0}
|
||||||
#define meshtastic_Config_BluetoothConfig_init_zero {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0}
|
#define meshtastic_Config_BluetoothConfig_init_zero {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0}
|
||||||
#define meshtastic_Config_SecurityConfig_init_zero {{0, {0}}, {0, {0}}, 0, {{0, {0}}, {0, {0}}, {0, {0}}}, 0, 0, 0, 0}
|
#define meshtastic_Config_SecurityConfig_init_zero {{0, {0}}, {0, {0}}, 0, {{0, {0}}, {0, {0}}, {0, {0}}}, 0, 0, 0, 0}
|
||||||
@@ -820,6 +805,7 @@ extern "C" {
|
|||||||
#define meshtastic_Config_DisplayConfig_wake_on_tap_or_motion_tag 10
|
#define meshtastic_Config_DisplayConfig_wake_on_tap_or_motion_tag 10
|
||||||
#define meshtastic_Config_DisplayConfig_compass_orientation_tag 11
|
#define meshtastic_Config_DisplayConfig_compass_orientation_tag 11
|
||||||
#define meshtastic_Config_DisplayConfig_use_12h_clock_tag 12
|
#define meshtastic_Config_DisplayConfig_use_12h_clock_tag 12
|
||||||
|
#define meshtastic_Config_DisplayConfig_use_long_node_name_tag 13
|
||||||
#define meshtastic_Config_LoRaConfig_use_preset_tag 1
|
#define meshtastic_Config_LoRaConfig_use_preset_tag 1
|
||||||
#define meshtastic_Config_LoRaConfig_modem_preset_tag 2
|
#define meshtastic_Config_LoRaConfig_modem_preset_tag 2
|
||||||
#define meshtastic_Config_LoRaConfig_bandwidth_tag 3
|
#define meshtastic_Config_LoRaConfig_bandwidth_tag 3
|
||||||
@@ -965,7 +951,8 @@ X(a, STATIC, SINGULAR, UENUM, displaymode, 8) \
|
|||||||
X(a, STATIC, SINGULAR, BOOL, heading_bold, 9) \
|
X(a, STATIC, SINGULAR, BOOL, heading_bold, 9) \
|
||||||
X(a, STATIC, SINGULAR, BOOL, wake_on_tap_or_motion, 10) \
|
X(a, STATIC, SINGULAR, BOOL, wake_on_tap_or_motion, 10) \
|
||||||
X(a, STATIC, SINGULAR, UENUM, compass_orientation, 11) \
|
X(a, STATIC, SINGULAR, UENUM, compass_orientation, 11) \
|
||||||
X(a, STATIC, SINGULAR, BOOL, use_12h_clock, 12)
|
X(a, STATIC, SINGULAR, BOOL, use_12h_clock, 12) \
|
||||||
|
X(a, STATIC, SINGULAR, BOOL, use_long_node_name, 13)
|
||||||
#define meshtastic_Config_DisplayConfig_CALLBACK NULL
|
#define meshtastic_Config_DisplayConfig_CALLBACK NULL
|
||||||
#define meshtastic_Config_DisplayConfig_DEFAULT NULL
|
#define meshtastic_Config_DisplayConfig_DEFAULT NULL
|
||||||
|
|
||||||
@@ -1043,7 +1030,7 @@ extern const pb_msgdesc_t meshtastic_Config_SessionkeyConfig_msg;
|
|||||||
#define MESHTASTIC_MESHTASTIC_CONFIG_PB_H_MAX_SIZE meshtastic_Config_size
|
#define MESHTASTIC_MESHTASTIC_CONFIG_PB_H_MAX_SIZE meshtastic_Config_size
|
||||||
#define meshtastic_Config_BluetoothConfig_size 10
|
#define meshtastic_Config_BluetoothConfig_size 10
|
||||||
#define meshtastic_Config_DeviceConfig_size 100
|
#define meshtastic_Config_DeviceConfig_size 100
|
||||||
#define meshtastic_Config_DisplayConfig_size 32
|
#define meshtastic_Config_DisplayConfig_size 34
|
||||||
#define meshtastic_Config_LoRaConfig_size 85
|
#define meshtastic_Config_LoRaConfig_size 85
|
||||||
#define meshtastic_Config_NetworkConfig_IpV4Config_size 20
|
#define meshtastic_Config_NetworkConfig_IpV4Config_size 20
|
||||||
#define meshtastic_Config_NetworkConfig_size 204
|
#define meshtastic_Config_NetworkConfig_size 204
|
||||||
|
|||||||
@@ -28,3 +28,5 @@ PB_BIND(meshtastic_Map, meshtastic_Map, AUTO)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -66,12 +66,40 @@ typedef enum _meshtastic_Language {
|
|||||||
meshtastic_Language_UKRAINIAN = 16,
|
meshtastic_Language_UKRAINIAN = 16,
|
||||||
/* Bulgarian */
|
/* Bulgarian */
|
||||||
meshtastic_Language_BULGARIAN = 17,
|
meshtastic_Language_BULGARIAN = 17,
|
||||||
|
/* Czech */
|
||||||
|
meshtastic_Language_CZECH = 18,
|
||||||
/* Simplified Chinese (experimental) */
|
/* Simplified Chinese (experimental) */
|
||||||
meshtastic_Language_SIMPLIFIED_CHINESE = 30,
|
meshtastic_Language_SIMPLIFIED_CHINESE = 30,
|
||||||
/* Traditional Chinese (experimental) */
|
/* Traditional Chinese (experimental) */
|
||||||
meshtastic_Language_TRADITIONAL_CHINESE = 31
|
meshtastic_Language_TRADITIONAL_CHINESE = 31
|
||||||
} meshtastic_Language;
|
} meshtastic_Language;
|
||||||
|
|
||||||
|
/* How the GPS coordinates are displayed on the OLED screen. */
|
||||||
|
typedef enum _meshtastic_DeviceUIConfig_GpsCoordinateFormat {
|
||||||
|
/* GPS coordinates are displayed in the normal decimal degrees format:
|
||||||
|
DD.DDDDDD DDD.DDDDDD */
|
||||||
|
meshtastic_DeviceUIConfig_GpsCoordinateFormat_DEC = 0,
|
||||||
|
/* GPS coordinates are displayed in the degrees minutes seconds format:
|
||||||
|
DD°MM'SS"C DDD°MM'SS"C, where C is the compass point representing the locations quadrant */
|
||||||
|
meshtastic_DeviceUIConfig_GpsCoordinateFormat_DMS = 1,
|
||||||
|
/* Universal Transverse Mercator format:
|
||||||
|
ZZB EEEEEE NNNNNNN, where Z is zone, B is band, E is easting, N is northing */
|
||||||
|
meshtastic_DeviceUIConfig_GpsCoordinateFormat_UTM = 2,
|
||||||
|
/* Military Grid Reference System format:
|
||||||
|
ZZB CD EEEEE NNNNN, where Z is zone, B is band, C is the east 100k square, D is the north 100k square,
|
||||||
|
E is easting, N is northing */
|
||||||
|
meshtastic_DeviceUIConfig_GpsCoordinateFormat_MGRS = 3,
|
||||||
|
/* Open Location Code (aka Plus Codes). */
|
||||||
|
meshtastic_DeviceUIConfig_GpsCoordinateFormat_OLC = 4,
|
||||||
|
/* Ordnance Survey Grid Reference (the National Grid System of the UK).
|
||||||
|
Format: AB EEEEE NNNNN, where A is the east 100k square, B is the north 100k square,
|
||||||
|
E is the easting, N is the northing */
|
||||||
|
meshtastic_DeviceUIConfig_GpsCoordinateFormat_OSGR = 5,
|
||||||
|
/* Maidenhead Locator System
|
||||||
|
Described here: https://en.wikipedia.org/wiki/Maidenhead_Locator_System */
|
||||||
|
meshtastic_DeviceUIConfig_GpsCoordinateFormat_MLS = 6
|
||||||
|
} meshtastic_DeviceUIConfig_GpsCoordinateFormat;
|
||||||
|
|
||||||
/* Struct definitions */
|
/* Struct definitions */
|
||||||
typedef struct _meshtastic_NodeFilter {
|
typedef struct _meshtastic_NodeFilter {
|
||||||
/* Filter unknown nodes */
|
/* Filter unknown nodes */
|
||||||
@@ -161,6 +189,8 @@ typedef struct _meshtastic_DeviceUIConfig {
|
|||||||
/* Clockface analog style
|
/* Clockface analog style
|
||||||
true for analog clockface, false for digital clockface */
|
true for analog clockface, false for digital clockface */
|
||||||
bool is_clockface_analog;
|
bool is_clockface_analog;
|
||||||
|
/* How the GPS coordinates are formatted on the OLED screen. */
|
||||||
|
meshtastic_DeviceUIConfig_GpsCoordinateFormat gps_format;
|
||||||
} meshtastic_DeviceUIConfig;
|
} meshtastic_DeviceUIConfig;
|
||||||
|
|
||||||
|
|
||||||
@@ -181,9 +211,14 @@ extern "C" {
|
|||||||
#define _meshtastic_Language_MAX meshtastic_Language_TRADITIONAL_CHINESE
|
#define _meshtastic_Language_MAX meshtastic_Language_TRADITIONAL_CHINESE
|
||||||
#define _meshtastic_Language_ARRAYSIZE ((meshtastic_Language)(meshtastic_Language_TRADITIONAL_CHINESE+1))
|
#define _meshtastic_Language_ARRAYSIZE ((meshtastic_Language)(meshtastic_Language_TRADITIONAL_CHINESE+1))
|
||||||
|
|
||||||
|
#define _meshtastic_DeviceUIConfig_GpsCoordinateFormat_MIN meshtastic_DeviceUIConfig_GpsCoordinateFormat_DEC
|
||||||
|
#define _meshtastic_DeviceUIConfig_GpsCoordinateFormat_MAX meshtastic_DeviceUIConfig_GpsCoordinateFormat_MLS
|
||||||
|
#define _meshtastic_DeviceUIConfig_GpsCoordinateFormat_ARRAYSIZE ((meshtastic_DeviceUIConfig_GpsCoordinateFormat)(meshtastic_DeviceUIConfig_GpsCoordinateFormat_MLS+1))
|
||||||
|
|
||||||
#define meshtastic_DeviceUIConfig_theme_ENUMTYPE meshtastic_Theme
|
#define meshtastic_DeviceUIConfig_theme_ENUMTYPE meshtastic_Theme
|
||||||
#define meshtastic_DeviceUIConfig_language_ENUMTYPE meshtastic_Language
|
#define meshtastic_DeviceUIConfig_language_ENUMTYPE meshtastic_Language
|
||||||
#define meshtastic_DeviceUIConfig_compass_mode_ENUMTYPE meshtastic_CompassMode
|
#define meshtastic_DeviceUIConfig_compass_mode_ENUMTYPE meshtastic_CompassMode
|
||||||
|
#define meshtastic_DeviceUIConfig_gps_format_ENUMTYPE meshtastic_DeviceUIConfig_GpsCoordinateFormat
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -191,12 +226,12 @@ extern "C" {
|
|||||||
|
|
||||||
|
|
||||||
/* Initializer values for message structs */
|
/* Initializer values for message structs */
|
||||||
#define meshtastic_DeviceUIConfig_init_default {0, 0, 0, 0, 0, 0, _meshtastic_Theme_MIN, 0, 0, 0, _meshtastic_Language_MIN, false, meshtastic_NodeFilter_init_default, false, meshtastic_NodeHighlight_init_default, {0, {0}}, false, meshtastic_Map_init_default, _meshtastic_CompassMode_MIN, 0, 0}
|
#define meshtastic_DeviceUIConfig_init_default {0, 0, 0, 0, 0, 0, _meshtastic_Theme_MIN, 0, 0, 0, _meshtastic_Language_MIN, false, meshtastic_NodeFilter_init_default, false, meshtastic_NodeHighlight_init_default, {0, {0}}, false, meshtastic_Map_init_default, _meshtastic_CompassMode_MIN, 0, 0, _meshtastic_DeviceUIConfig_GpsCoordinateFormat_MIN}
|
||||||
#define meshtastic_NodeFilter_init_default {0, 0, 0, 0, 0, "", 0}
|
#define meshtastic_NodeFilter_init_default {0, 0, 0, 0, 0, "", 0}
|
||||||
#define meshtastic_NodeHighlight_init_default {0, 0, 0, 0, ""}
|
#define meshtastic_NodeHighlight_init_default {0, 0, 0, 0, ""}
|
||||||
#define meshtastic_GeoPoint_init_default {0, 0, 0}
|
#define meshtastic_GeoPoint_init_default {0, 0, 0}
|
||||||
#define meshtastic_Map_init_default {false, meshtastic_GeoPoint_init_default, "", 0}
|
#define meshtastic_Map_init_default {false, meshtastic_GeoPoint_init_default, "", 0}
|
||||||
#define meshtastic_DeviceUIConfig_init_zero {0, 0, 0, 0, 0, 0, _meshtastic_Theme_MIN, 0, 0, 0, _meshtastic_Language_MIN, false, meshtastic_NodeFilter_init_zero, false, meshtastic_NodeHighlight_init_zero, {0, {0}}, false, meshtastic_Map_init_zero, _meshtastic_CompassMode_MIN, 0, 0}
|
#define meshtastic_DeviceUIConfig_init_zero {0, 0, 0, 0, 0, 0, _meshtastic_Theme_MIN, 0, 0, 0, _meshtastic_Language_MIN, false, meshtastic_NodeFilter_init_zero, false, meshtastic_NodeHighlight_init_zero, {0, {0}}, false, meshtastic_Map_init_zero, _meshtastic_CompassMode_MIN, 0, 0, _meshtastic_DeviceUIConfig_GpsCoordinateFormat_MIN}
|
||||||
#define meshtastic_NodeFilter_init_zero {0, 0, 0, 0, 0, "", 0}
|
#define meshtastic_NodeFilter_init_zero {0, 0, 0, 0, 0, "", 0}
|
||||||
#define meshtastic_NodeHighlight_init_zero {0, 0, 0, 0, ""}
|
#define meshtastic_NodeHighlight_init_zero {0, 0, 0, 0, ""}
|
||||||
#define meshtastic_GeoPoint_init_zero {0, 0, 0}
|
#define meshtastic_GeoPoint_init_zero {0, 0, 0}
|
||||||
@@ -239,6 +274,7 @@ extern "C" {
|
|||||||
#define meshtastic_DeviceUIConfig_compass_mode_tag 16
|
#define meshtastic_DeviceUIConfig_compass_mode_tag 16
|
||||||
#define meshtastic_DeviceUIConfig_screen_rgb_color_tag 17
|
#define meshtastic_DeviceUIConfig_screen_rgb_color_tag 17
|
||||||
#define meshtastic_DeviceUIConfig_is_clockface_analog_tag 18
|
#define meshtastic_DeviceUIConfig_is_clockface_analog_tag 18
|
||||||
|
#define meshtastic_DeviceUIConfig_gps_format_tag 19
|
||||||
|
|
||||||
/* Struct field encoding specification for nanopb */
|
/* Struct field encoding specification for nanopb */
|
||||||
#define meshtastic_DeviceUIConfig_FIELDLIST(X, a) \
|
#define meshtastic_DeviceUIConfig_FIELDLIST(X, a) \
|
||||||
@@ -259,7 +295,8 @@ X(a, STATIC, SINGULAR, BYTES, calibration_data, 14) \
|
|||||||
X(a, STATIC, OPTIONAL, MESSAGE, map_data, 15) \
|
X(a, STATIC, OPTIONAL, MESSAGE, map_data, 15) \
|
||||||
X(a, STATIC, SINGULAR, UENUM, compass_mode, 16) \
|
X(a, STATIC, SINGULAR, UENUM, compass_mode, 16) \
|
||||||
X(a, STATIC, SINGULAR, UINT32, screen_rgb_color, 17) \
|
X(a, STATIC, SINGULAR, UINT32, screen_rgb_color, 17) \
|
||||||
X(a, STATIC, SINGULAR, BOOL, is_clockface_analog, 18)
|
X(a, STATIC, SINGULAR, BOOL, is_clockface_analog, 18) \
|
||||||
|
X(a, STATIC, SINGULAR, UENUM, gps_format, 19)
|
||||||
#define meshtastic_DeviceUIConfig_CALLBACK NULL
|
#define meshtastic_DeviceUIConfig_CALLBACK NULL
|
||||||
#define meshtastic_DeviceUIConfig_DEFAULT NULL
|
#define meshtastic_DeviceUIConfig_DEFAULT NULL
|
||||||
#define meshtastic_DeviceUIConfig_node_filter_MSGTYPE meshtastic_NodeFilter
|
#define meshtastic_DeviceUIConfig_node_filter_MSGTYPE meshtastic_NodeFilter
|
||||||
@@ -316,7 +353,7 @@ extern const pb_msgdesc_t meshtastic_Map_msg;
|
|||||||
|
|
||||||
/* Maximum encoded size of messages (where known) */
|
/* Maximum encoded size of messages (where known) */
|
||||||
#define MESHTASTIC_MESHTASTIC_DEVICE_UI_PB_H_MAX_SIZE meshtastic_DeviceUIConfig_size
|
#define MESHTASTIC_MESHTASTIC_DEVICE_UI_PB_H_MAX_SIZE meshtastic_DeviceUIConfig_size
|
||||||
#define meshtastic_DeviceUIConfig_size 201
|
#define meshtastic_DeviceUIConfig_size 204
|
||||||
#define meshtastic_GeoPoint_size 33
|
#define meshtastic_GeoPoint_size 33
|
||||||
#define meshtastic_Map_size 58
|
#define meshtastic_Map_size 58
|
||||||
#define meshtastic_NodeFilter_size 47
|
#define meshtastic_NodeFilter_size 47
|
||||||
|
|||||||
@@ -360,7 +360,7 @@ extern const pb_msgdesc_t meshtastic_BackupPreferences_msg;
|
|||||||
/* Maximum encoded size of messages (where known) */
|
/* Maximum encoded size of messages (where known) */
|
||||||
/* meshtastic_NodeDatabase_size depends on runtime parameters */
|
/* meshtastic_NodeDatabase_size depends on runtime parameters */
|
||||||
#define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_BackupPreferences_size
|
#define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_BackupPreferences_size
|
||||||
#define meshtastic_BackupPreferences_size 2273
|
#define meshtastic_BackupPreferences_size 2277
|
||||||
#define meshtastic_ChannelFile_size 718
|
#define meshtastic_ChannelFile_size 718
|
||||||
#define meshtastic_DeviceState_size 1737
|
#define meshtastic_DeviceState_size 1737
|
||||||
#define meshtastic_NodeInfoLite_size 196
|
#define meshtastic_NodeInfoLite_size 196
|
||||||
|
|||||||
@@ -187,8 +187,8 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg;
|
|||||||
|
|
||||||
/* Maximum encoded size of messages (where known) */
|
/* Maximum encoded size of messages (where known) */
|
||||||
#define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalConfig_size
|
#define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalConfig_size
|
||||||
#define meshtastic_LocalConfig_size 747
|
#define meshtastic_LocalConfig_size 749
|
||||||
#define meshtastic_LocalModuleConfig_size 671
|
#define meshtastic_LocalModuleConfig_size 673
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
|
|||||||
@@ -259,8 +259,8 @@ typedef enum _meshtastic_HardwareModel {
|
|||||||
meshtastic_HardwareModel_T_DECK_PRO = 102,
|
meshtastic_HardwareModel_T_DECK_PRO = 102,
|
||||||
/* Lilygo TLora Pager */
|
/* Lilygo TLora Pager */
|
||||||
meshtastic_HardwareModel_T_LORA_PAGER = 103,
|
meshtastic_HardwareModel_T_LORA_PAGER = 103,
|
||||||
/* GAT562 Mesh Trial Tracker */
|
/* M5Stack Reserved */
|
||||||
meshtastic_HardwareModel_GAT562_MESH_TRIAL_TRACKER = 104,
|
meshtastic_HardwareModel_M5STACK_RESERVED = 104, /* 0x68 */
|
||||||
/* RAKwireless WisMesh Tag */
|
/* RAKwireless WisMesh Tag */
|
||||||
meshtastic_HardwareModel_WISMESH_TAG = 105,
|
meshtastic_HardwareModel_WISMESH_TAG = 105,
|
||||||
/* RAKwireless WisBlock Core RAK3312 https://docs.rakwireless.com/product-categories/wisduo/rak3112-module/overview/ */
|
/* RAKwireless WisBlock Core RAK3312 https://docs.rakwireless.com/product-categories/wisduo/rak3112-module/overview/ */
|
||||||
@@ -274,6 +274,10 @@ typedef enum _meshtastic_HardwareModel {
|
|||||||
meshtastic_HardwareModel_T_ECHO_LITE = 109,
|
meshtastic_HardwareModel_T_ECHO_LITE = 109,
|
||||||
/* New Heltec LoRA32 with ESP32-S3 CPU */
|
/* New Heltec LoRA32 with ESP32-S3 CPU */
|
||||||
meshtastic_HardwareModel_HELTEC_V4 = 110,
|
meshtastic_HardwareModel_HELTEC_V4 = 110,
|
||||||
|
/* M5Stack C6L */
|
||||||
|
meshtastic_HardwareModel_M5STACK_C6L = 111,
|
||||||
|
/* M5Stack Cardputer Adv */
|
||||||
|
meshtastic_HardwareModel_M5STACK_CARDPUTER_ADV = 112,
|
||||||
/* ------------------------------------------------------------------------------------------------------------------------------------------
|
/* ------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
|
Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
|
||||||
------------------------------------------------------------------------------------------------------------------------------------------ */
|
------------------------------------------------------------------------------------------------------------------------------------------ */
|
||||||
|
|||||||
@@ -356,6 +356,9 @@ typedef struct _meshtastic_ModuleConfig_TelemetryConfig {
|
|||||||
uint32_t health_update_interval;
|
uint32_t health_update_interval;
|
||||||
/* Enable/Disable the health telemetry module on-device display */
|
/* Enable/Disable the health telemetry module on-device display */
|
||||||
bool health_screen_enabled;
|
bool health_screen_enabled;
|
||||||
|
/* Enable/Disable the device telemetry module to send metrics to the mesh
|
||||||
|
Note: We will still send telemtry to the connected phone / client every minute over the API */
|
||||||
|
bool device_telemetry_enabled;
|
||||||
} meshtastic_ModuleConfig_TelemetryConfig;
|
} meshtastic_ModuleConfig_TelemetryConfig;
|
||||||
|
|
||||||
/* Canned Messages Module Config */
|
/* Canned Messages Module Config */
|
||||||
@@ -523,7 +526,7 @@ extern "C" {
|
|||||||
#define meshtastic_ModuleConfig_ExternalNotificationConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
#define meshtastic_ModuleConfig_ExternalNotificationConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
#define meshtastic_ModuleConfig_StoreForwardConfig_init_default {0, 0, 0, 0, 0, 0}
|
#define meshtastic_ModuleConfig_StoreForwardConfig_init_default {0, 0, 0, 0, 0, 0}
|
||||||
#define meshtastic_ModuleConfig_RangeTestConfig_init_default {0, 0, 0, 0}
|
#define meshtastic_ModuleConfig_RangeTestConfig_init_default {0, 0, 0, 0}
|
||||||
#define meshtastic_ModuleConfig_TelemetryConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
#define meshtastic_ModuleConfig_TelemetryConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
#define meshtastic_ModuleConfig_CannedMessageConfig_init_default {0, 0, 0, 0, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, 0, 0, "", 0}
|
#define meshtastic_ModuleConfig_CannedMessageConfig_init_default {0, 0, 0, 0, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, 0, 0, "", 0}
|
||||||
#define meshtastic_ModuleConfig_AmbientLightingConfig_init_default {0, 0, 0, 0, 0}
|
#define meshtastic_ModuleConfig_AmbientLightingConfig_init_default {0, 0, 0, 0, 0}
|
||||||
#define meshtastic_RemoteHardwarePin_init_default {0, "", _meshtastic_RemoteHardwarePinType_MIN}
|
#define meshtastic_RemoteHardwarePin_init_default {0, "", _meshtastic_RemoteHardwarePinType_MIN}
|
||||||
@@ -539,7 +542,7 @@ extern "C" {
|
|||||||
#define meshtastic_ModuleConfig_ExternalNotificationConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
#define meshtastic_ModuleConfig_ExternalNotificationConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
#define meshtastic_ModuleConfig_StoreForwardConfig_init_zero {0, 0, 0, 0, 0, 0}
|
#define meshtastic_ModuleConfig_StoreForwardConfig_init_zero {0, 0, 0, 0, 0, 0}
|
||||||
#define meshtastic_ModuleConfig_RangeTestConfig_init_zero {0, 0, 0, 0}
|
#define meshtastic_ModuleConfig_RangeTestConfig_init_zero {0, 0, 0, 0}
|
||||||
#define meshtastic_ModuleConfig_TelemetryConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
#define meshtastic_ModuleConfig_TelemetryConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
#define meshtastic_ModuleConfig_CannedMessageConfig_init_zero {0, 0, 0, 0, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, 0, 0, "", 0}
|
#define meshtastic_ModuleConfig_CannedMessageConfig_init_zero {0, 0, 0, 0, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, 0, 0, "", 0}
|
||||||
#define meshtastic_ModuleConfig_AmbientLightingConfig_init_zero {0, 0, 0, 0, 0}
|
#define meshtastic_ModuleConfig_AmbientLightingConfig_init_zero {0, 0, 0, 0, 0}
|
||||||
#define meshtastic_RemoteHardwarePin_init_zero {0, "", _meshtastic_RemoteHardwarePinType_MIN}
|
#define meshtastic_RemoteHardwarePin_init_zero {0, "", _meshtastic_RemoteHardwarePinType_MIN}
|
||||||
@@ -627,6 +630,7 @@ extern "C" {
|
|||||||
#define meshtastic_ModuleConfig_TelemetryConfig_health_measurement_enabled_tag 11
|
#define meshtastic_ModuleConfig_TelemetryConfig_health_measurement_enabled_tag 11
|
||||||
#define meshtastic_ModuleConfig_TelemetryConfig_health_update_interval_tag 12
|
#define meshtastic_ModuleConfig_TelemetryConfig_health_update_interval_tag 12
|
||||||
#define meshtastic_ModuleConfig_TelemetryConfig_health_screen_enabled_tag 13
|
#define meshtastic_ModuleConfig_TelemetryConfig_health_screen_enabled_tag 13
|
||||||
|
#define meshtastic_ModuleConfig_TelemetryConfig_device_telemetry_enabled_tag 14
|
||||||
#define meshtastic_ModuleConfig_CannedMessageConfig_rotary1_enabled_tag 1
|
#define meshtastic_ModuleConfig_CannedMessageConfig_rotary1_enabled_tag 1
|
||||||
#define meshtastic_ModuleConfig_CannedMessageConfig_inputbroker_pin_a_tag 2
|
#define meshtastic_ModuleConfig_CannedMessageConfig_inputbroker_pin_a_tag 2
|
||||||
#define meshtastic_ModuleConfig_CannedMessageConfig_inputbroker_pin_b_tag 3
|
#define meshtastic_ModuleConfig_CannedMessageConfig_inputbroker_pin_b_tag 3
|
||||||
@@ -825,7 +829,8 @@ X(a, STATIC, SINGULAR, UINT32, power_update_interval, 9) \
|
|||||||
X(a, STATIC, SINGULAR, BOOL, power_screen_enabled, 10) \
|
X(a, STATIC, SINGULAR, BOOL, power_screen_enabled, 10) \
|
||||||
X(a, STATIC, SINGULAR, BOOL, health_measurement_enabled, 11) \
|
X(a, STATIC, SINGULAR, BOOL, health_measurement_enabled, 11) \
|
||||||
X(a, STATIC, SINGULAR, UINT32, health_update_interval, 12) \
|
X(a, STATIC, SINGULAR, UINT32, health_update_interval, 12) \
|
||||||
X(a, STATIC, SINGULAR, BOOL, health_screen_enabled, 13)
|
X(a, STATIC, SINGULAR, BOOL, health_screen_enabled, 13) \
|
||||||
|
X(a, STATIC, SINGULAR, BOOL, device_telemetry_enabled, 14)
|
||||||
#define meshtastic_ModuleConfig_TelemetryConfig_CALLBACK NULL
|
#define meshtastic_ModuleConfig_TelemetryConfig_CALLBACK NULL
|
||||||
#define meshtastic_ModuleConfig_TelemetryConfig_DEFAULT NULL
|
#define meshtastic_ModuleConfig_TelemetryConfig_DEFAULT NULL
|
||||||
|
|
||||||
@@ -910,7 +915,7 @@ extern const pb_msgdesc_t meshtastic_RemoteHardwarePin_msg;
|
|||||||
#define meshtastic_ModuleConfig_RemoteHardwareConfig_size 96
|
#define meshtastic_ModuleConfig_RemoteHardwareConfig_size 96
|
||||||
#define meshtastic_ModuleConfig_SerialConfig_size 28
|
#define meshtastic_ModuleConfig_SerialConfig_size 28
|
||||||
#define meshtastic_ModuleConfig_StoreForwardConfig_size 24
|
#define meshtastic_ModuleConfig_StoreForwardConfig_size 24
|
||||||
#define meshtastic_ModuleConfig_TelemetryConfig_size 46
|
#define meshtastic_ModuleConfig_TelemetryConfig_size 48
|
||||||
#define meshtastic_ModuleConfig_size 227
|
#define meshtastic_ModuleConfig_size 227
|
||||||
#define meshtastic_RemoteHardwarePin_size 21
|
#define meshtastic_RemoteHardwarePin_size 21
|
||||||
|
|
||||||
|
|||||||
@@ -292,11 +292,14 @@ JSONArray htmlListDir(const char *dirname, uint8_t levels)
|
|||||||
JSONObject thisFileMap;
|
JSONObject thisFileMap;
|
||||||
thisFileMap["size"] = new JSONValue((int)file.size());
|
thisFileMap["size"] = new JSONValue((int)file.size());
|
||||||
#ifdef ARCH_ESP32
|
#ifdef ARCH_ESP32
|
||||||
thisFileMap["name"] = new JSONValue(String(file.path()).substring(1).c_str());
|
String fileName = String(file.path()).substring(1);
|
||||||
|
thisFileMap["name"] = new JSONValue(fileName.c_str());
|
||||||
#else
|
#else
|
||||||
thisFileMap["name"] = new JSONValue(String(file.name()).substring(1).c_str());
|
String fileName = String(file.name()).substring(1);
|
||||||
|
thisFileMap["name"] = new JSONValue(fileName.c_str());
|
||||||
#endif
|
#endif
|
||||||
if (String(file.name()).substring(1).endsWith(".gz")) {
|
String tempName = String(file.name()).substring(1);
|
||||||
|
if (tempName.endsWith(".gz")) {
|
||||||
#ifdef ARCH_ESP32
|
#ifdef ARCH_ESP32
|
||||||
String modifiedFile = String(file.path()).substring(1);
|
String modifiedFile = String(file.path()).substring(1);
|
||||||
#else
|
#else
|
||||||
@@ -339,7 +342,8 @@ void handleFsBrowseStatic(HTTPRequest *req, HTTPResponse *res)
|
|||||||
|
|
||||||
JSONValue *value = new JSONValue(jsonObjOuter);
|
JSONValue *value = new JSONValue(jsonObjOuter);
|
||||||
|
|
||||||
res->print(value->Stringify().c_str());
|
std::string jsonString = value->Stringify();
|
||||||
|
res->print(jsonString.c_str());
|
||||||
|
|
||||||
delete value;
|
delete value;
|
||||||
|
|
||||||
@@ -367,7 +371,8 @@ void handleFsDeleteStatic(HTTPRequest *req, HTTPResponse *res)
|
|||||||
JSONObject jsonObjOuter;
|
JSONObject jsonObjOuter;
|
||||||
jsonObjOuter["status"] = new JSONValue("ok");
|
jsonObjOuter["status"] = new JSONValue("ok");
|
||||||
JSONValue *value = new JSONValue(jsonObjOuter);
|
JSONValue *value = new JSONValue(jsonObjOuter);
|
||||||
res->print(value->Stringify().c_str());
|
std::string jsonString = value->Stringify();
|
||||||
|
res->print(jsonString.c_str());
|
||||||
delete value;
|
delete value;
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
@@ -376,7 +381,8 @@ void handleFsDeleteStatic(HTTPRequest *req, HTTPResponse *res)
|
|||||||
JSONObject jsonObjOuter;
|
JSONObject jsonObjOuter;
|
||||||
jsonObjOuter["status"] = new JSONValue("Error");
|
jsonObjOuter["status"] = new JSONValue("Error");
|
||||||
JSONValue *value = new JSONValue(jsonObjOuter);
|
JSONValue *value = new JSONValue(jsonObjOuter);
|
||||||
res->print(value->Stringify().c_str());
|
std::string jsonString = value->Stringify();
|
||||||
|
res->print(jsonString.c_str());
|
||||||
delete value;
|
delete value;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -622,10 +628,7 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
|
|||||||
tempArray.push_back(new JSONValue((int)logArray[i]));
|
tempArray.push_back(new JSONValue((int)logArray[i]));
|
||||||
}
|
}
|
||||||
JSONValue *result = new JSONValue(tempArray);
|
JSONValue *result = new JSONValue(tempArray);
|
||||||
// Clean up original array to prevent memory leak
|
// Note: Don't delete tempArray elements here - JSONValue now owns them
|
||||||
for (auto *val : tempArray) {
|
|
||||||
delete val;
|
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -656,7 +659,9 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
|
|||||||
// data->wifi
|
// data->wifi
|
||||||
JSONObject jsonObjWifi;
|
JSONObject jsonObjWifi;
|
||||||
jsonObjWifi["rssi"] = new JSONValue(WiFi.RSSI());
|
jsonObjWifi["rssi"] = new JSONValue(WiFi.RSSI());
|
||||||
jsonObjWifi["ip"] = new JSONValue(WiFi.localIP().toString().c_str());
|
String wifiIPString = WiFi.localIP().toString();
|
||||||
|
std::string wifiIP = wifiIPString.c_str();
|
||||||
|
jsonObjWifi["ip"] = new JSONValue(wifiIP.c_str());
|
||||||
|
|
||||||
// data->memory
|
// data->memory
|
||||||
JSONObject jsonObjMemory;
|
JSONObject jsonObjMemory;
|
||||||
@@ -702,7 +707,8 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
|
|||||||
jsonObjOuter["status"] = new JSONValue("ok");
|
jsonObjOuter["status"] = new JSONValue("ok");
|
||||||
// serialize and write it to the stream
|
// serialize and write it to the stream
|
||||||
JSONValue *value = new JSONValue(jsonObjOuter);
|
JSONValue *value = new JSONValue(jsonObjOuter);
|
||||||
res->print(value->Stringify().c_str());
|
std::string jsonString = value->Stringify();
|
||||||
|
res->print(jsonString.c_str());
|
||||||
delete value;
|
delete value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -773,7 +779,8 @@ void handleNodes(HTTPRequest *req, HTTPResponse *res)
|
|||||||
jsonObjOuter["status"] = new JSONValue("ok");
|
jsonObjOuter["status"] = new JSONValue("ok");
|
||||||
// serialize and write it to the stream
|
// serialize and write it to the stream
|
||||||
JSONValue *value = new JSONValue(jsonObjOuter);
|
JSONValue *value = new JSONValue(jsonObjOuter);
|
||||||
res->print(value->Stringify().c_str());
|
std::string jsonString = value->Stringify();
|
||||||
|
res->print(jsonString.c_str());
|
||||||
delete value;
|
delete value;
|
||||||
|
|
||||||
// Clean up the nodesArray to prevent memory leak
|
// Clean up the nodesArray to prevent memory leak
|
||||||
@@ -926,7 +933,8 @@ void handleBlinkLED(HTTPRequest *req, HTTPResponse *res)
|
|||||||
JSONObject jsonObjOuter;
|
JSONObject jsonObjOuter;
|
||||||
jsonObjOuter["status"] = new JSONValue("ok");
|
jsonObjOuter["status"] = new JSONValue("ok");
|
||||||
JSONValue *value = new JSONValue(jsonObjOuter);
|
JSONValue *value = new JSONValue(jsonObjOuter);
|
||||||
res->print(value->Stringify().c_str());
|
std::string jsonString = value->Stringify();
|
||||||
|
res->print(jsonString.c_str());
|
||||||
delete value;
|
delete value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -968,7 +976,8 @@ void handleScanNetworks(HTTPRequest *req, HTTPResponse *res)
|
|||||||
|
|
||||||
// serialize and write it to the stream
|
// serialize and write it to the stream
|
||||||
JSONValue *value = new JSONValue(jsonObjOuter);
|
JSONValue *value = new JSONValue(jsonObjOuter);
|
||||||
res->print(value->Stringify().c_str());
|
std::string jsonString = value->Stringify();
|
||||||
|
res->print(jsonString.c_str());
|
||||||
delete value;
|
delete value;
|
||||||
|
|
||||||
// Clean up the networkObjs to prevent memory leak
|
// Clean up the networkObjs to prevent memory leak
|
||||||
|
|||||||
@@ -15,8 +15,27 @@
|
|||||||
// FIXME - max_count is actually 32 but we save/load this as one long string of preencoded MeshPacket bytes - not a big array in
|
// FIXME - max_count is actually 32 but we save/load this as one long string of preencoded MeshPacket bytes - not a big array in
|
||||||
// RAM #define MAX_RX_TOPHONE (member_size(DeviceState, receive_queue) / member_size(DeviceState, receive_queue[0]))
|
// RAM #define MAX_RX_TOPHONE (member_size(DeviceState, receive_queue) / member_size(DeviceState, receive_queue[0]))
|
||||||
#ifndef MAX_RX_TOPHONE
|
#ifndef MAX_RX_TOPHONE
|
||||||
|
#if defined(ARCH_ESP32) && !(defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3))
|
||||||
|
#define MAX_RX_TOPHONE 8
|
||||||
|
#else
|
||||||
#define MAX_RX_TOPHONE 32
|
#define MAX_RX_TOPHONE 32
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// max number of QueueStatus packets which can be waiting for delivery to phone
|
||||||
|
#ifndef MAX_RX_QUEUESTATUS_TOPHONE
|
||||||
|
#define MAX_RX_QUEUESTATUS_TOPHONE 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// max number of MqttClientProxyMessage packets which can be waiting for delivery to phone
|
||||||
|
#ifndef MAX_RX_MQTTPROXY_TOPHONE
|
||||||
|
#define MAX_RX_MQTTPROXY_TOPHONE 8
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// max number of ClientNotification packets which can be waiting for delivery to phone
|
||||||
|
#ifndef MAX_RX_NOTIFICATION_TOPHONE
|
||||||
|
#define MAX_RX_NOTIFICATION_TOPHONE 2
|
||||||
|
#endif
|
||||||
|
|
||||||
/// Verify baseline assumption of node size. If it increases, we need to reevaluate
|
/// Verify baseline assumption of node size. If it increases, we need to reevaluate
|
||||||
/// the impact of its memory footprint, notably on MAX_NUM_NODES.
|
/// the impact of its memory footprint, notably on MAX_NUM_NODES.
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user