mirror of
https://github.com/alexhopeoconnor/firmware.git
synced 2026-10-22 03:02:08 +10:00
Compare commits
284
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
803e96800e | ||
|
|
6c69780615 | ||
|
|
d5bb566276 | ||
|
|
39ff880506 | ||
|
|
209157c9dd | ||
|
|
fb59d68edd | ||
|
|
7b854fb5ca | ||
|
|
f8b160595f | ||
|
|
37d14f942e | ||
|
|
4594ae474e | ||
|
|
f26e657577 | ||
|
|
a25bfd264c | ||
|
|
ec9f3fa6ea | ||
|
|
8356ad97e4 | ||
|
|
bf51c38975 | ||
|
|
3df3c876cc | ||
|
|
68f07c5f9d | ||
|
|
7fb96ce2ba | ||
|
|
12687a1073 | ||
|
|
89de499198 | ||
|
|
4881362340 | ||
|
|
18000ccf21 | ||
|
|
7776ec15b6 | ||
|
|
e4c7fca716 | ||
|
|
5b63bd9331 | ||
|
|
289f90bdbe | ||
|
|
09a0df3a1f | ||
|
|
fe329892de | ||
|
|
2681332678 | ||
|
|
f994eb185f | ||
|
|
55c23dec13 | ||
|
|
4dfc062abd | ||
|
|
0be21d90c1 | ||
|
|
a0c0388dd9 | ||
|
|
e8367894f2 | ||
|
|
8aae4f1b9d | ||
|
|
8a8f60d129 | ||
|
|
b59409bec0 | ||
|
|
c66125114f | ||
|
|
edb7ec58c6 | ||
|
|
655c6b51fe | ||
|
|
0bd4cefad3 | ||
|
|
9b1fb795d7 | ||
|
|
3040e5a7bb | ||
|
|
3b82d55176 | ||
|
|
a6b8202cd4 | ||
|
|
cfc1bf10c9 | ||
|
|
7d1300ab66 | ||
|
|
bd3cbfc1ad | ||
|
|
fddc4e00ca | ||
|
|
5f7eec5504 | ||
|
|
6b94c297b9 | ||
|
|
edeb25cab5 | ||
|
|
44688e8363 | ||
|
|
5ae4ff9162 | ||
|
|
ed394f5f9d | ||
|
|
11db6d4dcc | ||
|
|
4e03df5ea7 | ||
|
|
d3e3a91096 | ||
|
|
b0e8321514 | ||
|
|
6c7cff7de2 | ||
|
|
834c3c5cc2 | ||
|
|
25a19b49ad | ||
|
|
a4d96bebfb | ||
|
|
d21d6d2085 | ||
|
|
26c38ffc8e | ||
|
|
237b8908f7 | ||
|
|
06bccef462 | ||
|
|
3120bb8fd7 | ||
|
|
0903ed8232 | ||
|
|
f8ba392a24 | ||
|
|
3dd384dd53 | ||
|
|
2c071a3283 | ||
|
|
596cd7e0b6 | ||
|
|
3f5c30e3b3 | ||
|
|
1a279c6053 | ||
|
|
3d825c51dd | ||
|
|
915f882e1f | ||
|
|
18d005d7e6 | ||
|
|
8791cd7851 | ||
|
|
590db89643 | ||
|
|
ea1d968777 | ||
|
|
40d728a14b | ||
|
|
e39b56547e | ||
|
|
a7f63d5783 | ||
|
|
5136c8ba24 | ||
|
|
8b42bf7a95 | ||
|
|
1daf5aad1f | ||
|
|
fe3f14a63e | ||
|
|
7574bfb7cb | ||
|
|
ce75bf4496 | ||
|
|
5ce47045e7 | ||
|
|
57e1725419 | ||
|
|
11309662a9 | ||
|
|
890357d579 | ||
|
|
f413c49555 | ||
|
|
c19f573b49 | ||
|
|
5de61b1a3d | ||
|
|
1c1462e776 | ||
|
|
eb6ef1cbea | ||
|
|
9654f5b218 | ||
|
|
68726a1b0e | ||
|
|
5b62bbe8e6 | ||
|
|
e55084629a | ||
|
|
1691e885f2 | ||
|
|
2d7818797d | ||
|
|
f65e2c639e | ||
|
|
95200e8f6b | ||
|
|
36e8dc74f4 | ||
|
|
78c5309e9a | ||
|
|
9feb1d378e | ||
|
|
e5e8683cdb | ||
|
|
d538ad170c | ||
|
|
c64c196778 | ||
|
|
8e552a9f0c | ||
|
|
a02017a5c8 | ||
|
|
0046d957f1 | ||
|
|
4a241deb96 | ||
|
|
8d5ae1d5d2 | ||
|
|
e1e89a5e62 | ||
|
|
a7be93449e | ||
|
|
c8694f9f2d | ||
|
|
062168cd42 | ||
|
|
1877a2c531 | ||
|
|
52f0e5a3db | ||
|
|
ac8c372349 | ||
|
|
1bfa429c38 | ||
|
|
ddd149945a | ||
|
|
e3dd8164a4 | ||
|
|
9b8149f14e | ||
|
|
05f1518951 | ||
|
|
e26de85b5f | ||
|
|
a2df80e833 | ||
|
|
db238ef524 | ||
|
|
f2b935f48f | ||
|
|
e69da71d4e | ||
|
|
7505fe7a7c | ||
|
|
f6857f1bcb | ||
|
|
7fe2c74139 | ||
|
|
be60f9612e | ||
|
|
2de9f015b1 | ||
|
|
c1f4f79d4a | ||
|
|
7b874cf597 | ||
|
|
8568b56ac6 | ||
|
|
f2a880f813 | ||
|
|
691327b2db | ||
|
|
a23c58c10a | ||
|
|
27c6b24e3a | ||
|
|
384436e937 | ||
|
|
eb30aae486 | ||
|
|
079286da04 | ||
|
|
0130899b3b | ||
|
|
d1f3c3c982 | ||
|
|
3b6eefa8bb | ||
|
|
5107531425 | ||
|
|
88655ffc44 | ||
|
|
10bd10b9d1 | ||
|
|
956a0f102b | ||
|
|
bdedd0e1fe | ||
|
|
4c901033b2 | ||
|
|
7d926da98c | ||
|
|
1b793d1f23 | ||
|
|
b5a8e8f51b | ||
|
|
cc5d00e211 | ||
|
|
1a8ab2aadc | ||
|
|
608fdc6f52 | ||
|
|
1d8638b47d | ||
|
|
3ecff48722 | ||
|
|
aa3b14ce72 | ||
|
|
28aeb0f09e | ||
|
|
7c5e2c5393 | ||
|
|
df8b629c2c | ||
|
|
a506dc6b65 | ||
|
|
fc1e6ccb8c | ||
|
|
bbc638ab82 | ||
|
|
4f57a2e248 | ||
|
|
4c6db2c5bd | ||
|
|
bbe548bc98 | ||
|
|
d1fbf65c5d | ||
|
|
7a4a915312 | ||
|
|
4f895f744b | ||
|
|
66a831dfa8 | ||
|
|
516597a73e | ||
|
|
4eb6c9fb8e | ||
|
|
46e2ae8860 | ||
|
|
54c0cbeb66 | ||
|
|
82ddf4732a | ||
|
|
ed0cdefb44 | ||
|
|
8836be0f47 | ||
|
|
96f63f3945 | ||
|
|
d80dcd6afd | ||
|
|
2087629a47 | ||
|
|
878d68c5ef | ||
|
|
86960cdb1d | ||
|
|
fff12979a2 | ||
|
|
6c12baf4ed | ||
|
|
29449a71d4 | ||
|
|
9b983b6487 | ||
|
|
806bfa54b5 | ||
|
|
920aeeeba5 | ||
|
|
32418448de | ||
|
|
b3525c2569 | ||
|
|
19dc2873c5 | ||
|
|
25b8d9b0ca | ||
|
|
8aef3c44f4 | ||
|
|
8345c21eff | ||
|
|
36b94cf823 | ||
|
|
475cfe4af2 | ||
|
|
b851b15a73 | ||
|
|
73347c2542 | ||
|
|
bc9023399d | ||
|
|
a9c9b96eb6 | ||
|
|
1c2a3c620f | ||
|
|
9313d04726 | ||
|
|
44518fea14 | ||
|
|
91049d0db3 | ||
|
|
855514b4f3 | ||
|
|
974741a366 | ||
|
|
5d98f7e307 | ||
|
|
3ca45ae99c | ||
|
|
cf574c71d8 | ||
|
|
abe0a34fc0 | ||
|
|
71b6508ad3 | ||
|
|
55fc4fcd90 | ||
|
|
c3b2b474c6 | ||
|
|
39716ed1ba | ||
|
|
625a529f6c | ||
|
|
31d56c16d5 | ||
|
|
5776385e8c | ||
|
|
8f10de5684 | ||
|
|
e864fcf9a8 | ||
|
|
86af5f5252 | ||
|
|
daa1d582cb | ||
|
|
f197f0e5ec | ||
|
|
3599ca6845 | ||
|
|
1be4fc5ae9 | ||
|
|
ac3e5684d6 | ||
|
|
29cca4d621 | ||
|
|
f3ff80963a | ||
|
|
45e428eb25 | ||
|
|
16d2650236 |
@@ -5,17 +5,12 @@ runs:
|
|||||||
using: composite
|
using: composite
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
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: |
|
||||||
@@ -23,7 +18,7 @@ runs:
|
|||||||
sudo apt-get install -y cppcheck libbluetooth-dev libgpiod-dev libyaml-cpp-dev lsb-release
|
sudo apt-get install -y cppcheck libbluetooth-dev libgpiod-dev libyaml-cpp-dev lsb-release
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: 3.x
|
python-version: 3.x
|
||||||
cache: pip
|
cache: pip
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ jobs:
|
|||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
path: meshtasticd
|
path: meshtasticd
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
name: Build ESP32
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
board:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
|
|
||||||
permissions: read-all
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-esp32:
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Get release version string
|
|
||||||
shell: bash
|
|
||||||
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
|
||||||
id: version
|
|
||||||
|
|
||||||
- name: Build ESP32
|
|
||||||
id: build
|
|
||||||
uses: meshtastic/gh-action-firmware@main
|
|
||||||
with:
|
|
||||||
pio_platform: esp32
|
|
||||||
pio_env: ${{ inputs.board }}
|
|
||||||
pio_target: build
|
|
||||||
ota_firmware_source: firmware.bin
|
|
||||||
ota_firmware_target: release/bleota.bin
|
|
||||||
|
|
||||||
- name: Store binaries as an artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: firmware-esp32-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip
|
|
||||||
overwrite: true
|
|
||||||
path: |
|
|
||||||
release/*.bin
|
|
||||||
release/*.elf
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
name: Build ESP32-C3
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
board:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
|
|
||||||
permissions: read-all
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-esp32-c3:
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Get release version string
|
|
||||||
shell: bash
|
|
||||||
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
|
||||||
id: version
|
|
||||||
|
|
||||||
- name: Build ESP32-C3
|
|
||||||
id: build
|
|
||||||
uses: meshtastic/gh-action-firmware@main
|
|
||||||
with:
|
|
||||||
pio_platform: esp32
|
|
||||||
pio_env: ${{ inputs.board }}
|
|
||||||
pio_target: build
|
|
||||||
ota_firmware_source: firmware-c3.bin
|
|
||||||
ota_firmware_target: release/bleota-c3.bin
|
|
||||||
|
|
||||||
- name: Store binaries as an artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: firmware-esp32c3-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip
|
|
||||||
overwrite: true
|
|
||||||
path: |
|
|
||||||
release/*.bin
|
|
||||||
release/*.elf
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
name: Build ESP32-C6
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
board:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
|
|
||||||
permissions: read-all
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-esp32-c6:
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Get release version string
|
|
||||||
shell: bash
|
|
||||||
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
|
||||||
id: version
|
|
||||||
|
|
||||||
- name: Build ESP32-C6
|
|
||||||
id: build
|
|
||||||
uses: meshtastic/gh-action-firmware@main
|
|
||||||
with:
|
|
||||||
pio_platform: esp32
|
|
||||||
pio_env: ${{ inputs.board }}
|
|
||||||
pio_target: build
|
|
||||||
ota_firmware_source: firmware-c3.bin
|
|
||||||
ota_firmware_target: release/bleota-c3.bin
|
|
||||||
|
|
||||||
- name: Store binaries as an artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: firmware-esp32c6-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip
|
|
||||||
overwrite: true
|
|
||||||
path: |
|
|
||||||
release/*.bin
|
|
||||||
release/*.elf
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
name: Build ESP32-S3
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
board:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
|
|
||||||
permissions: read-all
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-esp32-s3:
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Get release version string
|
|
||||||
shell: bash
|
|
||||||
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
|
||||||
id: version
|
|
||||||
|
|
||||||
- name: Build ESP32-S3
|
|
||||||
id: build
|
|
||||||
uses: meshtastic/gh-action-firmware@main
|
|
||||||
with:
|
|
||||||
pio_platform: esp32
|
|
||||||
pio_env: ${{ inputs.board }}
|
|
||||||
pio_target: build
|
|
||||||
ota_firmware_source: firmware-s3.bin
|
|
||||||
ota_firmware_target: release/bleota-s3.bin
|
|
||||||
|
|
||||||
- name: Store binaries as an artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: firmware-esp32s3-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip
|
|
||||||
overwrite: true
|
|
||||||
path: |
|
|
||||||
release/*.bin
|
|
||||||
release/*.elf
|
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
name: Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
platform:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
pio_env:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
|
permissions: read-all
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
pio-build:
|
||||||
|
name: build-${{ inputs.platform }}
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
ref: ${{github.event.pull_request.head.ref}}
|
||||||
|
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||||
|
|
||||||
|
- name: Set OTA firmware source and target
|
||||||
|
if: startsWith(inputs.platform, 'esp32')
|
||||||
|
id: ota_dir
|
||||||
|
env:
|
||||||
|
PIO_PLATFORM: ${{ inputs.platform }}
|
||||||
|
run: |
|
||||||
|
if [ "$PIO_PLATFORM" = "esp32s3" ]; then
|
||||||
|
echo "src=firmware-s3.bin" >> $GITHUB_OUTPUT
|
||||||
|
echo "tgt=release/bleota-s3.bin" >> $GITHUB_OUTPUT
|
||||||
|
elif [ "$PIO_PLATFORM" = "esp32c3" ] || [ "$PIO_PLATFORM" = "esp32c6" ]; then
|
||||||
|
echo "src=firmware-c3.bin" >> $GITHUB_OUTPUT
|
||||||
|
echo "tgt=release/bleota-c3.bin" >> $GITHUB_OUTPUT
|
||||||
|
elif [ "$PIO_PLATFORM" = "esp32" ]; then
|
||||||
|
echo "src=firmware.bin" >> $GITHUB_OUTPUT
|
||||||
|
echo "tgt=release/bleota.bin" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Build ${{ inputs.platform }}
|
||||||
|
id: build
|
||||||
|
uses: meshtastic/gh-action-firmware@main
|
||||||
|
with:
|
||||||
|
pio_platform: ${{ inputs.platform }}
|
||||||
|
pio_env: ${{ inputs.pio_env }}
|
||||||
|
pio_target: build
|
||||||
|
ota_firmware_source: ${{ steps.ota_dir.outputs.src || '' }}
|
||||||
|
ota_firmware_target: ${{ steps.ota_dir.outputs.tgt || '' }}
|
||||||
|
|
||||||
|
- name: Store binaries as an artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: firmware-${{ inputs.platform }}-${{ inputs.pio_env }}-${{ inputs.version }}.zip
|
||||||
|
overwrite: true
|
||||||
|
path: |
|
||||||
|
release/*.bin
|
||||||
|
release/*.elf
|
||||||
|
release/*.uf2
|
||||||
|
release/*.hex
|
||||||
|
release/*-ota.zip
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
name: Build NRF52
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
board:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
|
|
||||||
permissions: read-all
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-nrf52:
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Get release version string
|
|
||||||
shell: bash
|
|
||||||
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
|
||||||
id: version
|
|
||||||
|
|
||||||
- name: Build NRF52
|
|
||||||
id: build
|
|
||||||
uses: meshtastic/gh-action-firmware@main
|
|
||||||
with:
|
|
||||||
pio_platform: nrf52
|
|
||||||
pio_env: ${{ inputs.board }}
|
|
||||||
pio_target: build
|
|
||||||
|
|
||||||
- name: Store binaries as an artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: firmware-nrf52840-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip
|
|
||||||
overwrite: true
|
|
||||||
path: |
|
|
||||||
release/*.uf2
|
|
||||||
release/*.elf
|
|
||||||
release/*.hex
|
|
||||||
release/*-ota.zip
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
name: Build RPI2040
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
board:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
|
|
||||||
permissions: read-all
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-rpi2040:
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Get release version string
|
|
||||||
shell: bash
|
|
||||||
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
|
||||||
id: version
|
|
||||||
|
|
||||||
- name: Build Raspberry Pi 2040
|
|
||||||
id: build
|
|
||||||
uses: meshtastic/gh-action-firmware@main
|
|
||||||
with:
|
|
||||||
pio_platform: rp2xx0
|
|
||||||
pio_env: ${{ inputs.board }}
|
|
||||||
pio_target: build
|
|
||||||
|
|
||||||
- name: Store binaries as an artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: firmware-rp2040-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip
|
|
||||||
overwrite: true
|
|
||||||
path: |
|
|
||||||
release/*.uf2
|
|
||||||
release/*.elf
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
name: Build STM32
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
board:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
|
|
||||||
permissions: read-all
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-stm32:
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Get release version string
|
|
||||||
shell: bash
|
|
||||||
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
|
||||||
id: version
|
|
||||||
|
|
||||||
- name: Build STM32WL
|
|
||||||
id: build
|
|
||||||
uses: meshtastic/gh-action-firmware@main
|
|
||||||
with:
|
|
||||||
pio_platform: stm32wl
|
|
||||||
pio_env: ${{ inputs.board }}
|
|
||||||
pio_target: build
|
|
||||||
|
|
||||||
- name: Store binaries as an artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: firmware-stm32-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip
|
|
||||||
overwrite: true
|
|
||||||
path: |
|
|
||||||
release/*.hex
|
|
||||||
release/*.bin
|
|
||||||
release/*.elf
|
|
||||||
@@ -30,7 +30,11 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
series: [plucky, noble, jammy]
|
series:
|
||||||
|
- jammy # 22.04
|
||||||
|
- noble # 24.04
|
||||||
|
- plucky # 25.04
|
||||||
|
- questing # 25.10
|
||||||
uses: ./.github/workflows/package_ppa.yml
|
uses: ./.github/workflows/package_ppa.yml
|
||||||
with:
|
with:
|
||||||
ppa_repo: ppa:meshtastic/daily
|
ppa_repo: ppa:meshtastic/daily
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ jobs:
|
|||||||
runs-on: ${{ inputs.runs-on }}
|
runs-on: ${{ inputs.runs-on }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
ref: ${{github.event.pull_request.head.ref}}
|
ref: ${{github.event.pull_request.head.ref}}
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ jobs:
|
|||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
ref: ${{github.event.pull_request.head.ref}}
|
ref: ${{github.event.pull_request.head.ref}}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ jobs:
|
|||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
ref: ${{ github.ref }}
|
ref: ${{ github.ref }}
|
||||||
|
|||||||
+157
-102
@@ -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
|
||||||
@@ -30,18 +30,31 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
arch: [esp32, esp32s3, esp32c3, esp32c6, nrf52840, rp2040, stm32, check]
|
arch:
|
||||||
runs-on: ubuntu-latest
|
- esp32
|
||||||
|
- esp32s3
|
||||||
|
- esp32c3
|
||||||
|
- esp32c6
|
||||||
|
- nrf52840
|
||||||
|
- rp2040
|
||||||
|
- rp2350
|
||||||
|
- stm32
|
||||||
|
- check
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- id: checkout
|
- uses: actions/checkout@v5
|
||||||
uses: actions/checkout@v4
|
- uses: actions/setup-python@v6
|
||||||
name: Checkout base
|
with:
|
||||||
- id: jsonStep
|
python-version: 3.x
|
||||||
|
cache: pip
|
||||||
|
- run: pip install -U platformio
|
||||||
|
- name: Generate matrix
|
||||||
|
id: jsonStep
|
||||||
run: |
|
run: |
|
||||||
if [[ "$GITHUB_HEAD_REF" == "" ]]; then
|
if [[ "$GITHUB_HEAD_REF" == "" ]]; then
|
||||||
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}})
|
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}})
|
||||||
else
|
else
|
||||||
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}} quick)
|
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}} pr)
|
||||||
fi
|
fi
|
||||||
echo "Name: $GITHUB_REF_NAME Base: $GITHUB_BASE_REF Ref: $GITHUB_REF Targets: $TARGETS"
|
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
|
echo "${{matrix.arch}}=$(jq -cn --argjson environments "$TARGETS" '{board: $environments}')" >> $GITHUB_OUTPUT
|
||||||
@@ -52,9 +65,25 @@ jobs:
|
|||||||
esp32c6: ${{ steps.jsonStep.outputs.esp32c6 }}
|
esp32c6: ${{ steps.jsonStep.outputs.esp32c6 }}
|
||||||
nrf52840: ${{ steps.jsonStep.outputs.nrf52840 }}
|
nrf52840: ${{ steps.jsonStep.outputs.nrf52840 }}
|
||||||
rp2040: ${{ steps.jsonStep.outputs.rp2040 }}
|
rp2040: ${{ steps.jsonStep.outputs.rp2040 }}
|
||||||
|
rp2350: ${{ steps.jsonStep.outputs.rp2350 }}
|
||||||
stm32: ${{ steps.jsonStep.outputs.stm32 }}
|
stm32: ${{ steps.jsonStep.outputs.stm32 }}
|
||||||
check: ${{ steps.jsonStep.outputs.check }}
|
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:
|
check:
|
||||||
needs: setup
|
needs: setup
|
||||||
strategy:
|
strategy:
|
||||||
@@ -64,7 +93,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.event_name != 'workflow_dispatch' }}
|
if: ${{ github.event_name != 'workflow_dispatch' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- name: Build base
|
- name: Build base
|
||||||
id: base
|
id: base
|
||||||
uses: ./.github/actions/setup-base
|
uses: ./.github/actions/setup-base
|
||||||
@@ -72,67 +101,92 @@ jobs:
|
|||||||
run: bin/check-all.sh ${{ matrix.board }}
|
run: bin/check-all.sh ${{ matrix.board }}
|
||||||
|
|
||||||
build-esp32:
|
build-esp32:
|
||||||
needs: setup
|
needs: [setup, version]
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix: ${{ fromJson(needs.setup.outputs.esp32) }}
|
matrix: ${{ fromJson(needs.setup.outputs.esp32) }}
|
||||||
uses: ./.github/workflows/build_esp32.yml
|
uses: ./.github/workflows/build_firmware.yml
|
||||||
with:
|
with:
|
||||||
board: ${{ matrix.board }}
|
version: ${{ needs.version.outputs.long }}
|
||||||
|
pio_env: ${{ matrix.board }}
|
||||||
|
platform: esp32
|
||||||
|
|
||||||
build-esp32-s3:
|
build-esp32s3:
|
||||||
needs: setup
|
needs: [setup, version]
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix: ${{ fromJson(needs.setup.outputs.esp32s3) }}
|
matrix: ${{ fromJson(needs.setup.outputs.esp32s3) }}
|
||||||
uses: ./.github/workflows/build_esp32_s3.yml
|
uses: ./.github/workflows/build_firmware.yml
|
||||||
with:
|
with:
|
||||||
board: ${{ matrix.board }}
|
version: ${{ needs.version.outputs.long }}
|
||||||
|
pio_env: ${{ matrix.board }}
|
||||||
|
platform: esp32s3
|
||||||
|
|
||||||
build-esp32-c3:
|
build-esp32c3:
|
||||||
needs: setup
|
needs: [setup, version]
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix: ${{ fromJson(needs.setup.outputs.esp32c3) }}
|
matrix: ${{ fromJson(needs.setup.outputs.esp32c3) }}
|
||||||
uses: ./.github/workflows/build_esp32_c3.yml
|
uses: ./.github/workflows/build_firmware.yml
|
||||||
with:
|
with:
|
||||||
board: ${{ matrix.board }}
|
version: ${{ needs.version.outputs.long }}
|
||||||
|
pio_env: ${{ matrix.board }}
|
||||||
|
platform: esp32c3
|
||||||
|
|
||||||
build-esp32-c6:
|
build-esp32c6:
|
||||||
needs: setup
|
needs: [setup, version]
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix: ${{ fromJson(needs.setup.outputs.esp32c6) }}
|
matrix: ${{ fromJson(needs.setup.outputs.esp32c6) }}
|
||||||
uses: ./.github/workflows/build_esp32_c6.yml
|
uses: ./.github/workflows/build_firmware.yml
|
||||||
with:
|
with:
|
||||||
board: ${{ matrix.board }}
|
version: ${{ needs.version.outputs.long }}
|
||||||
|
pio_env: ${{ matrix.board }}
|
||||||
|
platform: esp32c6
|
||||||
|
|
||||||
build-nrf52:
|
build-nrf52840:
|
||||||
needs: setup
|
needs: [setup, version]
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix: ${{ fromJson(needs.setup.outputs.nrf52840) }}
|
matrix: ${{ fromJson(needs.setup.outputs.nrf52840) }}
|
||||||
uses: ./.github/workflows/build_nrf52.yml
|
uses: ./.github/workflows/build_firmware.yml
|
||||||
with:
|
with:
|
||||||
board: ${{ matrix.board }}
|
version: ${{ needs.version.outputs.long }}
|
||||||
|
pio_env: ${{ matrix.board }}
|
||||||
|
platform: nrf52840
|
||||||
|
|
||||||
build-rpi2040:
|
build-rp2040:
|
||||||
needs: setup
|
needs: [setup, version]
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix: ${{ fromJson(needs.setup.outputs.rp2040) }}
|
matrix: ${{ fromJson(needs.setup.outputs.rp2040) }}
|
||||||
uses: ./.github/workflows/build_rpi2040.yml
|
uses: ./.github/workflows/build_firmware.yml
|
||||||
with:
|
with:
|
||||||
board: ${{ matrix.board }}
|
version: ${{ needs.version.outputs.long }}
|
||||||
|
pio_env: ${{ matrix.board }}
|
||||||
|
platform: rp2040
|
||||||
|
|
||||||
|
build-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:
|
build-stm32:
|
||||||
needs: setup
|
needs: [setup, version]
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix: ${{ fromJson(needs.setup.outputs.stm32) }}
|
matrix: ${{ fromJson(needs.setup.outputs.stm32) }}
|
||||||
uses: ./.github/workflows/build_stm32.yml
|
uses: ./.github/workflows/build_firmware.yml
|
||||||
with:
|
with:
|
||||||
board: ${{ matrix.board }}
|
version: ${{ needs.version.outputs.long }}
|
||||||
|
pio_env: ${{ matrix.board }}
|
||||||
|
platform: stm32
|
||||||
|
|
||||||
build-debian-src:
|
build-debian-src:
|
||||||
if: github.repository == 'meshtastic/firmware'
|
if: github.repository == 'meshtastic/firmware'
|
||||||
@@ -204,32 +258,43 @@ jobs:
|
|||||||
push: false
|
push: false
|
||||||
|
|
||||||
gather-artifacts:
|
gather-artifacts:
|
||||||
|
# trunk-ignore(checkov/CKV2_GHA_1)
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
arch: [esp32, esp32s3, esp32c3, esp32c6, nrf52840, rp2040, stm32]
|
arch:
|
||||||
|
- esp32
|
||||||
|
- esp32s3
|
||||||
|
- esp32c3
|
||||||
|
- esp32c6
|
||||||
|
- nrf52840
|
||||||
|
- rp2040
|
||||||
|
- rp2350
|
||||||
|
- stm32
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs:
|
needs:
|
||||||
[
|
[
|
||||||
|
version,
|
||||||
build-esp32,
|
build-esp32,
|
||||||
build-esp32-s3,
|
build-esp32s3,
|
||||||
build-esp32-c3,
|
build-esp32c3,
|
||||||
build-esp32-c6,
|
build-esp32c6,
|
||||||
build-nrf52,
|
build-nrf52840,
|
||||||
build-rpi2040,
|
build-rp2040,
|
||||||
|
build-rp2350,
|
||||||
build-stm32,
|
build-stm32,
|
||||||
]
|
]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
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}}
|
||||||
|
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v5
|
||||||
with:
|
with:
|
||||||
path: ./
|
path: ./
|
||||||
pattern: firmware-${{matrix.arch}}-*
|
pattern: firmware-${{matrix.arch}}-*
|
||||||
@@ -238,17 +303,13 @@ jobs:
|
|||||||
- name: Display structure of downloaded files
|
- name: Display structure of downloaded files
|
||||||
run: ls -R
|
run: ls -R
|
||||||
|
|
||||||
- name: Get release version string
|
|
||||||
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
|
||||||
id: version
|
|
||||||
|
|
||||||
- name: Move files up
|
- name: Move files up
|
||||||
run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat
|
run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat
|
||||||
|
|
||||||
- name: Repackage in single firmware zip
|
- name: Repackage in single firmware zip
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}
|
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
overwrite: true
|
overwrite: true
|
||||||
path: |
|
path: |
|
||||||
./firmware-*.bin
|
./firmware-*.bin
|
||||||
@@ -262,9 +323,9 @@ jobs:
|
|||||||
./Meshtastic_nRF52_factory_erase*.uf2
|
./Meshtastic_nRF52_factory_erase*.uf2
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
|
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v5
|
||||||
with:
|
with:
|
||||||
name: firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}
|
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
path: ./output
|
path: ./output
|
||||||
|
|
||||||
@@ -278,12 +339,12 @@ jobs:
|
|||||||
chmod +x ./output/device-update.sh
|
chmod +x ./output/device-update.sh
|
||||||
|
|
||||||
- name: Zip firmware
|
- name: Zip firmware
|
||||||
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip ./output
|
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
||||||
|
|
||||||
- name: Repackage in single elfs zip
|
- name: Repackage in single elfs zip
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip
|
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
|
||||||
overwrite: true
|
overwrite: true
|
||||||
path: ./*.elf
|
path: ./*.elf
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
@@ -291,8 +352,8 @@ jobs:
|
|||||||
- uses: scruplelesswizard/comment-artifact@main
|
- uses: scruplelesswizard/comment-artifact@main
|
||||||
if: ${{ github.event_name == 'pull_request' }}
|
if: ${{ github.event_name == 'pull_request' }}
|
||||||
with:
|
with:
|
||||||
name: firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}
|
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
description: "Download firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip. This artifact will be available for 90 days from creation"
|
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 }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
release-artifacts:
|
release-artifacts:
|
||||||
@@ -301,56 +362,49 @@ jobs:
|
|||||||
outputs:
|
outputs:
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
needs:
|
needs:
|
||||||
|
- version
|
||||||
- gather-artifacts
|
- gather-artifacts
|
||||||
- build-debian-src
|
- build-debian-src
|
||||||
- package-pio-deps-native-tft
|
- package-pio-deps-native-tft
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: 3.x
|
python-version: 3.x
|
||||||
|
|
||||||
- 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
|
|
||||||
|
|
||||||
- name: Create release
|
- name: Create release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
id: create_release
|
id: create_release
|
||||||
with:
|
with:
|
||||||
draft: true
|
draft: true
|
||||||
prerelease: true
|
prerelease: true
|
||||||
name: Meshtastic Firmware ${{ steps.version.outputs.long }} Alpha
|
name: Meshtastic Firmware ${{ needs.version.outputs.long }} Alpha
|
||||||
tag_name: v${{ steps.version.outputs.long }}
|
tag_name: v${{ needs.version.outputs.long }}
|
||||||
body: |
|
body: |
|
||||||
Autogenerated by github action, developer should edit as required before publishing...
|
Autogenerated by github action, developer should edit as required before publishing...
|
||||||
|
|
||||||
- name: Download source deb
|
- name: Download source deb
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v5
|
||||||
with:
|
with:
|
||||||
pattern: firmware-debian-${{ steps.version.outputs.deb }}~UNRELEASED-src
|
pattern: firmware-debian-${{ needs.version.outputs.deb }}~UNRELEASED-src
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
path: ./output/debian-src
|
path: ./output/debian-src
|
||||||
|
|
||||||
- name: Download `native-tft` pio deps
|
- name: Download `native-tft` pio deps
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v5
|
||||||
with:
|
with:
|
||||||
pattern: platformio-deps-native-tft-${{ steps.version.outputs.long }}
|
pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
path: ./output/pio-deps-native-tft
|
path: ./output/pio-deps-native-tft
|
||||||
|
|
||||||
- name: Zip Linux sources
|
- name: Zip Linux sources
|
||||||
working-directory: output
|
working-directory: output
|
||||||
run: |
|
run: |
|
||||||
zip -j -9 -r ./meshtasticd-${{ steps.version.outputs.deb }}-src.zip ./debian-src
|
zip -j -9 -r ./meshtasticd-${{ needs.version.outputs.deb }}-src.zip ./debian-src
|
||||||
zip -9 -r ./platformio-deps-native-tft-${{ steps.version.outputs.long }}.zip ./pio-deps-native-tft
|
zip -9 -r ./platformio-deps-native-tft-${{ needs.version.outputs.long }}.zip ./pio-deps-native-tft
|
||||||
|
|
||||||
# For diagnostics
|
# For diagnostics
|
||||||
- name: Display structure of downloaded files
|
- name: Display structure of downloaded files
|
||||||
@@ -360,8 +414,8 @@ jobs:
|
|||||||
# Only run when targeting master branch with workflow_dispatch
|
# Only run when targeting master branch with workflow_dispatch
|
||||||
if: ${{ github.ref_name == 'master' }}
|
if: ${{ github.ref_name == 'master' }}
|
||||||
run: |
|
run: |
|
||||||
gh release upload v${{ steps.version.outputs.long }} ./output/meshtasticd-${{ steps.version.outputs.deb }}-src.zip
|
gh release upload v${{ needs.version.outputs.long }} ./output/meshtasticd-${{ needs.version.outputs.deb }}-src.zip
|
||||||
gh release upload v${{ steps.version.outputs.long }} ./output/platformio-deps-native-tft-${{ steps.version.outputs.long }}.zip
|
gh release upload v${{ needs.version.outputs.long }} ./output/platformio-deps-native-tft-${{ needs.version.outputs.long }}.zip
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
@@ -369,26 +423,30 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
arch: [esp32, esp32s3, esp32c3, esp32c6, nrf52840, rp2040, stm32]
|
arch:
|
||||||
|
- esp32
|
||||||
|
- esp32s3
|
||||||
|
- esp32c3
|
||||||
|
- esp32c6
|
||||||
|
- nrf52840
|
||||||
|
- rp2040
|
||||||
|
- rp2350
|
||||||
|
- stm32
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.event_name == 'workflow_dispatch' }}
|
if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||||
needs: [release-artifacts]
|
needs: [release-artifacts, version]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: 3.x
|
python-version: 3.x
|
||||||
|
|
||||||
- name: Get release version string
|
- uses: actions/download-artifact@v5
|
||||||
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
|
||||||
id: version
|
|
||||||
|
|
||||||
- uses: actions/download-artifact@v4
|
|
||||||
with:
|
with:
|
||||||
pattern: firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}
|
pattern: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
path: ./output
|
path: ./output
|
||||||
|
|
||||||
@@ -401,16 +459,16 @@ jobs:
|
|||||||
chmod +x ./output/device-update.sh
|
chmod +x ./output/device-update.sh
|
||||||
|
|
||||||
- name: Zip firmware
|
- name: Zip firmware
|
||||||
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip ./output
|
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
||||||
|
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v5
|
||||||
with:
|
with:
|
||||||
name: debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip
|
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
path: ./elfs
|
path: ./elfs
|
||||||
|
|
||||||
- name: Zip debug elfs
|
- name: Zip debug elfs
|
||||||
run: zip -j -9 -r ./debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip ./elfs
|
run: zip -j -9 -r ./debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./elfs
|
||||||
|
|
||||||
# For diagnostics
|
# For diagnostics
|
||||||
- name: Display structure of downloaded files
|
- name: Display structure of downloaded files
|
||||||
@@ -420,33 +478,30 @@ jobs:
|
|||||||
# Only run when targeting master branch with workflow_dispatch
|
# Only run when targeting master branch with workflow_dispatch
|
||||||
if: ${{ github.ref_name == 'master' }}
|
if: ${{ github.ref_name == 'master' }}
|
||||||
run: |
|
run: |
|
||||||
gh release upload v${{ steps.version.outputs.long }} ./firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip
|
gh release upload v${{ needs.version.outputs.long }} ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
|
||||||
gh release upload v${{ steps.version.outputs.long }} ./debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip
|
gh release upload v${{ needs.version.outputs.long }} ./debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
publish-firmware:
|
publish-firmware:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
if: ${{ github.event_name == 'workflow_dispatch' }}
|
if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||||
needs: [release-firmware]
|
needs: [release-firmware, version]
|
||||||
env:
|
env:
|
||||||
targets: esp32,esp32s3,esp32c3,esp32c6,nrf52840,rp2040,stm32
|
targets: |-
|
||||||
|
esp32,esp32s3,esp32c3,esp32c6,nrf52840,rp2040,rp2350,stm32
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: 3.x
|
python-version: 3.x
|
||||||
|
|
||||||
- name: Get release version string
|
- uses: actions/download-artifact@v5
|
||||||
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
|
||||||
id: version
|
|
||||||
|
|
||||||
- uses: actions/download-artifact@v4
|
|
||||||
with:
|
with:
|
||||||
pattern: firmware-{${{ env.targets }}}-${{ steps.version.outputs.long }}
|
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
path: ./publish
|
path: ./publish
|
||||||
|
|
||||||
@@ -460,9 +515,9 @@ jobs:
|
|||||||
external_repository: meshtastic/meshtastic.github.io
|
external_repository: meshtastic/meshtastic.github.io
|
||||||
publish_branch: master
|
publish_branch: master
|
||||||
publish_dir: ./publish
|
publish_dir: ./publish
|
||||||
destination_dir: ${{ env.DEST_PREFIX }}firmware-${{ steps.version.outputs.long }}
|
destination_dir: ${{ env.DEST_PREFIX }}firmware-${{ needs.version.outputs.long }}
|
||||||
keep_files: true
|
keep_files: true
|
||||||
user_name: github-actions[bot]
|
user_name: github-actions[bot]
|
||||||
user_email: github-actions[bot]@users.noreply.github.com
|
user_email: github-actions[bot]@users.noreply.github.com
|
||||||
commit_message: ${{ steps.version.outputs.long }}
|
commit_message: ${{ needs.version.outputs.long }}
|
||||||
enable_jekyll: true
|
enable_jekyll: true
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -14,7 +14,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Trunk Check
|
- name: Trunk Check
|
||||||
uses: trunk-io/trunk-action@v1
|
uses: trunk-io/trunk-action@v1
|
||||||
@@ -31,7 +31,7 @@ jobs:
|
|||||||
pull-requests: write # For trunk to create PRs
|
pull-requests: write # For trunk to create PRs
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Trunk Upgrade
|
- name: Trunk Upgrade
|
||||||
uses: trunk-io/trunk-action/upgrade@v1
|
uses: trunk-io/trunk-action/upgrade@v1
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ jobs:
|
|||||||
needs: build-debian-src
|
needs: build-debian-src
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
path: meshtasticd
|
path: meshtasticd
|
||||||
@@ -58,7 +58,7 @@ jobs:
|
|||||||
id: version
|
id: version
|
||||||
|
|
||||||
- name: Download artifacts
|
- name: Download artifacts
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v5
|
||||||
with:
|
with:
|
||||||
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
|
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|||||||
@@ -24,14 +24,14 @@ jobs:
|
|||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
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: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: 3.x
|
python-version: 3.x
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ jobs:
|
|||||||
needs: build-debian-src
|
needs: build-debian-src
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
path: meshtasticd
|
path: meshtasticd
|
||||||
@@ -60,7 +60,7 @@ jobs:
|
|||||||
id: version
|
id: version
|
||||||
|
|
||||||
- name: Download artifacts
|
- name: Download artifacts
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v5
|
||||||
with:
|
with:
|
||||||
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
|
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
name: Check PR Labels
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, edited, labeled, unlabeled, synchronize, reopened]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
pull-requests: read
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-label:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- name: Check for PR labels
|
||||||
|
uses: actions/github-script@v8
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const labels = context.payload.pull_request.labels.map(label => label.name);
|
||||||
|
const requiredLabels = ['bugfix', 'enhancement', 'hardware-support', 'dependencies', 'submodules', 'github_actions', 'trunk'];
|
||||||
|
const hasRequiredLabel = labels.some(label => requiredLabels.includes(label));
|
||||||
|
if (!hasRequiredLabel) {
|
||||||
|
core.setFailed(`PR must have at least one of the following labels before it can be merged: ${requiredLabels.join(', ')}.`);
|
||||||
|
}
|
||||||
@@ -0,0 +1,238 @@
|
|||||||
|
name: Tests
|
||||||
|
|
||||||
|
# DISABLED: Changed from automatic PR triggers to manual only
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
reason:
|
||||||
|
description: "Reason for manual test run"
|
||||||
|
required: false
|
||||||
|
default: "Manual test execution"
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: tests-${{ github.head_ref || github.run_id }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
actions: read
|
||||||
|
checks: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
native-tests:
|
||||||
|
name: "🧪 Native Tests"
|
||||||
|
if: github.repository == 'meshtastic/firmware'
|
||||||
|
uses: ./.github/workflows/test_native.yml
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
actions: read
|
||||||
|
checks: write
|
||||||
|
|
||||||
|
test-summary:
|
||||||
|
name: "📊 Test Results"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [native-tests]
|
||||||
|
if: always()
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
actions: read
|
||||||
|
checks: write
|
||||||
|
pull-requests: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Get release version string
|
||||||
|
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
||||||
|
id: version
|
||||||
|
|
||||||
|
- name: Download test artifacts
|
||||||
|
if: needs.native-tests.result != 'skipped'
|
||||||
|
uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
name: platformio-test-report-${{ steps.version.outputs.long }}.zip
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Parse test results and create detailed summary
|
||||||
|
id: test-results
|
||||||
|
run: |
|
||||||
|
echo "## 🧪 Test Results Summary" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
# Check overall job status first
|
||||||
|
if [[ "${{ needs.native-tests.result }}" == "success" ]]; then
|
||||||
|
echo "✅ **Overall Status**: PASSED" >> $GITHUB_STEP_SUMMARY
|
||||||
|
elif [[ "${{ needs.native-tests.result }}" == "failure" ]]; then
|
||||||
|
echo "❌ **Overall Status**: FAILED" >> $GITHUB_STEP_SUMMARY
|
||||||
|
elif [[ "${{ needs.native-tests.result }}" == "cancelled" ]]; then
|
||||||
|
echo "⏸️ **Overall Status**: CANCELLED" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "Tests were cancelled before completion." >> $GITHUB_STEP_SUMMARY
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo "⚠️ **Overall Status**: SKIPPED" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "Tests were skipped." >> $GITHUB_STEP_SUMMARY
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
# Parse detailed test results if available
|
||||||
|
if [ -f "testreport.xml" ]; then
|
||||||
|
echo "### 🔍 Individual Test Results" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
python3 << 'EOF'
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
import os
|
||||||
|
|
||||||
|
try:
|
||||||
|
tree = ET.parse('testreport.xml')
|
||||||
|
root = tree.getroot()
|
||||||
|
|
||||||
|
total_tests = 0
|
||||||
|
passed_tests = 0
|
||||||
|
failed_tests = 0
|
||||||
|
skipped_tests = 0
|
||||||
|
|
||||||
|
# Parse testsuite elements
|
||||||
|
for testsuite in root.findall('.//testsuite'):
|
||||||
|
suite_name = testsuite.get('name', 'Unknown')
|
||||||
|
suite_tests = int(testsuite.get('tests', '0'))
|
||||||
|
suite_failures = int(testsuite.get('failures', '0'))
|
||||||
|
suite_errors = int(testsuite.get('errors', '0'))
|
||||||
|
suite_skipped = int(testsuite.get('skipped', '0'))
|
||||||
|
|
||||||
|
total_tests += suite_tests
|
||||||
|
failed_tests += suite_failures + suite_errors
|
||||||
|
skipped_tests += suite_skipped
|
||||||
|
passed_tests += suite_tests - suite_failures - suite_errors - suite_skipped
|
||||||
|
|
||||||
|
if suite_tests > 0:
|
||||||
|
status = "✅" if (suite_failures + suite_errors) == 0 else "❌"
|
||||||
|
print(f"**{status} Test Suite: {suite_name}**")
|
||||||
|
print(f"- Total: {suite_tests}")
|
||||||
|
print(f"- Passed: ✅ {suite_tests - suite_failures - suite_errors - suite_skipped}")
|
||||||
|
print(f"- Failed: ❌ {suite_failures + suite_errors}")
|
||||||
|
if suite_skipped > 0:
|
||||||
|
print(f"- Skipped: ⏭️ {suite_skipped}")
|
||||||
|
print("")
|
||||||
|
|
||||||
|
# Show individual test results for failed suites
|
||||||
|
if suite_failures + suite_errors > 0:
|
||||||
|
print("**Failed Tests:**")
|
||||||
|
for testcase in testsuite.findall('testcase'):
|
||||||
|
test_name = testcase.get('name', 'Unknown')
|
||||||
|
failure = testcase.find('failure')
|
||||||
|
error = testcase.find('error')
|
||||||
|
|
||||||
|
if failure is not None:
|
||||||
|
msg = failure.get('message', 'Unknown error')[:100]
|
||||||
|
print(f"- ❌ `{test_name}`: {msg}")
|
||||||
|
elif error is not None:
|
||||||
|
msg = error.get('message', 'Unknown error')[:100]
|
||||||
|
print(f"- ❌ `{test_name}`: ERROR - {msg}")
|
||||||
|
print("")
|
||||||
|
else:
|
||||||
|
# Show passed tests for successful suites
|
||||||
|
passed_count = 0
|
||||||
|
for testcase in testsuite.findall('testcase'):
|
||||||
|
if testcase.find('failure') is None and testcase.find('error') is None:
|
||||||
|
if passed_count < 5: # Limit to first 5 to avoid spam
|
||||||
|
test_name = testcase.get('name', 'Unknown')
|
||||||
|
print(f"- ✅ `{test_name}`: PASSED")
|
||||||
|
passed_count += 1
|
||||||
|
if passed_count > 5:
|
||||||
|
print(f"- ... and {passed_count - 5} more tests passed")
|
||||||
|
print("")
|
||||||
|
|
||||||
|
# Summary statistics
|
||||||
|
print("### 📊 Test Statistics")
|
||||||
|
print(f"- **Total Tests**: {total_tests}")
|
||||||
|
print(f"- **Passed**: ✅ {passed_tests}")
|
||||||
|
print(f"- **Failed**: ❌ {failed_tests}")
|
||||||
|
if skipped_tests > 0:
|
||||||
|
print(f"- **Skipped**: ⏭️ {skipped_tests}")
|
||||||
|
|
||||||
|
if failed_tests > 0:
|
||||||
|
print(f"\n❌ **{failed_tests} tests failed out of {total_tests} total**")
|
||||||
|
else:
|
||||||
|
print(f"\n✅ **All {total_tests} tests passed!**")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Error parsing test results: {e}")
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
echo "⚠️ **No detailed test report available**" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "Test artifacts may not have been generated properly." >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "---" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "View detailed logs in the [Actions tab](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
- name: Comment test results on PR
|
||||||
|
if: github.event_name == 'pull_request' && needs.native-tests.result != 'skipped'
|
||||||
|
uses: actions/github-script@v8
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// Read the step summary to use as PR comment
|
||||||
|
let testSummary = "## 🧪 Test Results Summary\n\n";
|
||||||
|
|
||||||
|
if ("${{ needs.native-tests.result }}" === "success") {
|
||||||
|
testSummary += "✅ **All tests passed!**\n\n";
|
||||||
|
} else if ("${{ needs.native-tests.result }}" === "failure") {
|
||||||
|
testSummary += "❌ **Some tests failed.**\n\n";
|
||||||
|
} else {
|
||||||
|
testSummary += "⚠️ **Tests did not complete normally.**\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
testSummary += `View detailed results: [Actions Run](${context.payload.repository.html_url}/actions/runs/${context.runId})\n\n`;
|
||||||
|
testSummary += "---\n";
|
||||||
|
testSummary += "*This comment will be automatically updated when new commits are pushed.*";
|
||||||
|
|
||||||
|
// Find existing comment
|
||||||
|
const comments = await github.rest.issues.listComments({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: context.issue.number
|
||||||
|
});
|
||||||
|
|
||||||
|
const botComment = comments.data.find(comment =>
|
||||||
|
comment.user.type === 'Bot' &&
|
||||||
|
comment.body.includes('🧪 Test Results Summary')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (botComment) {
|
||||||
|
// Update existing comment
|
||||||
|
await github.rest.issues.updateComment({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
comment_id: botComment.id,
|
||||||
|
body: testSummary
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Create new comment
|
||||||
|
await github.rest.issues.createComment({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: context.issue.number,
|
||||||
|
body: testSummary
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Set overall status
|
||||||
|
run: |
|
||||||
|
if [[ "${{ needs.native-tests.result }}" == "success" ]]; then
|
||||||
|
echo "All tests passed! ✅"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo "Some tests failed! ❌"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
@@ -20,7 +20,11 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
series: [plucky, noble, jammy]
|
series:
|
||||||
|
- jammy # 22.04
|
||||||
|
- noble # 24.04
|
||||||
|
- plucky # 25.04
|
||||||
|
# - questing # 25.10
|
||||||
uses: ./.github/workflows/package_ppa.yml
|
uses: ./.github/workflows/package_ppa.yml
|
||||||
with:
|
with:
|
||||||
ppa_repo: |-
|
ppa_repo: |-
|
||||||
@@ -56,10 +60,10 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: 3.x
|
python-version: 3.x
|
||||||
|
|
||||||
@@ -99,8 +103,9 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
base: ${{ github.event.repository.default_branch }}
|
base: ${{ github.event.repository.default_branch }}
|
||||||
branch: create-pull-request/bump-version
|
branch: create-pull-request/bump-version
|
||||||
|
labels: github_actions
|
||||||
title: Bump release version
|
title: Bump release version
|
||||||
commit-message: automated bumps
|
commit-message: Automated version bumps
|
||||||
add-paths: |
|
add-paths: |
|
||||||
version.properties
|
version.properties
|
||||||
debian/changelog
|
debian/changelog
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
# step 1
|
# step 1
|
||||||
- name: clone application source code
|
- name: clone application source code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
# step 2
|
# step 2
|
||||||
- name: full scan
|
- name: full scan
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
# step 1
|
# step 1
|
||||||
- name: clone application source code
|
- name: clone application source code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Stale PR+Issues
|
- name: Stale PR+Issues
|
||||||
uses: actions/stale@v9.1.0
|
uses: actions/stale@v10.0.0
|
||||||
with:
|
with:
|
||||||
days-before-stale: 45
|
days-before-stale: 45
|
||||||
exempt-issue-labels: pinned,3.0
|
exempt-issue-labels: pinned,3.0
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ jobs:
|
|||||||
name: Native Simulator Tests
|
name: Native Simulator Tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
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}}
|
||||||
@@ -70,7 +70,7 @@ jobs:
|
|||||||
name: Native PlatformIO Tests
|
name: Native PlatformIO Tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
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}}
|
||||||
@@ -127,7 +127,7 @@ jobs:
|
|||||||
- platformio-tests
|
- platformio-tests
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
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}}
|
||||||
@@ -137,7 +137,7 @@ jobs:
|
|||||||
id: version
|
id: version
|
||||||
|
|
||||||
- name: Download test artifacts
|
- name: Download test artifacts
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v5
|
||||||
with:
|
with:
|
||||||
name: platformio-test-report-${{ steps.version.outputs.long }}.zip
|
name: platformio-test-report-${{ steps.version.outputs.long }}.zip
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
@@ -150,7 +150,7 @@ jobs:
|
|||||||
reporter: java-junit
|
reporter: java-junit
|
||||||
|
|
||||||
- name: Download coverage artifacts
|
- name: Download coverage artifacts
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v5
|
||||||
with:
|
with:
|
||||||
pattern: lcov-coverage-info-native-*-${{ steps.version.outputs.long }}.zip
|
pattern: lcov-coverage-info-native-*-${{ steps.version.outputs.long }}.zip
|
||||||
path: code-coverage-report
|
path: code-coverage-report
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ jobs:
|
|||||||
runs-on: test-runner
|
runs-on: test-runner
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
# - uses: actions/setup-python@v5
|
# - uses: actions/setup-python@v5
|
||||||
# with:
|
# with:
|
||||||
@@ -47,7 +47,7 @@ jobs:
|
|||||||
pio upgrade
|
pio upgrade
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v5
|
||||||
with:
|
with:
|
||||||
node-version: 22
|
node-version: 22
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Trunk Check
|
- name: Trunk Check
|
||||||
uses: trunk-io/trunk-action@v1
|
uses: trunk-io/trunk-action@v1
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Trunk Check
|
- name: Trunk Check
|
||||||
uses: trunk-io/trunk-action@v1
|
uses: trunk-io/trunk-action@v1
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
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}}
|
||||||
@@ -39,7 +39,7 @@ jobs:
|
|||||||
git push
|
git push
|
||||||
|
|
||||||
- name: Comment on PR
|
- name: Comment on PR
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
script: |
|
script: |
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
@@ -34,7 +34,9 @@ jobs:
|
|||||||
uses: peter-evans/create-pull-request@v7
|
uses: peter-evans/create-pull-request@v7
|
||||||
with:
|
with:
|
||||||
branch: create-pull-request/update-protobufs
|
branch: create-pull-request/update-protobufs
|
||||||
|
labels: submodules
|
||||||
title: Update protobufs and classes
|
title: Update protobufs and classes
|
||||||
|
commit-message: Update protobufs
|
||||||
add-paths: |
|
add-paths: |
|
||||||
protobufs
|
protobufs
|
||||||
src/mesh
|
src/mesh
|
||||||
|
|||||||
+11
-11
@@ -1,34 +1,34 @@
|
|||||||
version: 0.1
|
version: 0.1
|
||||||
cli:
|
cli:
|
||||||
version: 1.24.0
|
version: 1.25.0
|
||||||
plugins:
|
plugins:
|
||||||
sources:
|
sources:
|
||||||
- id: trunk
|
- id: trunk
|
||||||
ref: v1.7.1
|
ref: v1.7.2
|
||||||
uri: https://github.com/trunk-io/plugins
|
uri: https://github.com/trunk-io/plugins
|
||||||
lint:
|
lint:
|
||||||
enabled:
|
enabled:
|
||||||
- checkov@3.2.450
|
- checkov@3.2.471
|
||||||
- renovate@41.29.1
|
- renovate@41.115.6
|
||||||
- prettier@3.6.2
|
- prettier@3.6.2
|
||||||
- trufflehog@3.89.2
|
- trufflehog@3.90.6
|
||||||
- yamllint@1.37.1
|
- yamllint@1.37.1
|
||||||
- bandit@1.8.6
|
- bandit@1.8.6
|
||||||
- trivy@0.64.1
|
- trivy@0.66.0
|
||||||
- taplo@0.9.3
|
- taplo@0.10.0
|
||||||
- ruff@0.12.2
|
- 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
|
||||||
- svgo@4.0.0
|
- svgo@4.0.0
|
||||||
- actionlint@1.7.7
|
- actionlint@1.7.7
|
||||||
- flake8@7.3.0
|
- flake8@7.3.0
|
||||||
- hadolint@2.12.1-beta
|
- hadolint@2.13.1
|
||||||
- shfmt@3.6.0
|
- shfmt@3.6.0
|
||||||
- shellcheck@0.10.0
|
- shellcheck@0.11.0
|
||||||
- black@25.1.0
|
- black@25.1.0
|
||||||
- git-diff-check
|
- git-diff-check
|
||||||
- gitleaks@8.27.2
|
- gitleaks@8.28.0
|
||||||
- clang-format@16.0.3
|
- clang-format@16.0.3
|
||||||
ignore:
|
ignore:
|
||||||
- linters: [ALL]
|
- linters: [ALL]
|
||||||
|
|||||||
+5
-5
@@ -3,7 +3,7 @@
|
|||||||
# trunk-ignore-all(hadolint/DL3008): Do not pin apt package versions
|
# trunk-ignore-all(hadolint/DL3008): Do not pin apt package versions
|
||||||
# trunk-ignore-all(hadolint/DL3013): Do not pin pip package versions
|
# trunk-ignore-all(hadolint/DL3013): Do not pin pip package versions
|
||||||
|
|
||||||
FROM python:3.13-bookworm AS builder
|
FROM python:3.13-slim-trixie AS builder
|
||||||
ARG PIO_ENV=native
|
ARG PIO_ENV=native
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
ENV TZ=Etc/UTC
|
ENV TZ=Etc/UTC
|
||||||
@@ -36,7 +36,7 @@ RUN curl -L "https://github.com/meshtastic/web/releases/download/v$(cat /tmp/fir
|
|||||||
|
|
||||||
##### PRODUCTION BUILD #############
|
##### PRODUCTION BUILD #############
|
||||||
|
|
||||||
FROM debian:bookworm-slim
|
FROM debian:trixie-slim
|
||||||
LABEL org.opencontainers.image.title="Meshtastic" \
|
LABEL org.opencontainers.image.title="Meshtastic" \
|
||||||
org.opencontainers.image.description="Debian Meshtastic daemon and web interface" \
|
org.opencontainers.image.description="Debian Meshtastic daemon and web interface" \
|
||||||
org.opencontainers.image.url="https://meshtastic.org" \
|
org.opencontainers.image.url="https://meshtastic.org" \
|
||||||
@@ -51,8 +51,8 @@ ENV TZ=Etc/UTC
|
|||||||
USER root
|
USER root
|
||||||
|
|
||||||
RUN apt-get update && apt-get --no-install-recommends -y install \
|
RUN apt-get update && apt-get --no-install-recommends -y install \
|
||||||
libc-bin libc6 libgpiod2 libyaml-cpp0.7 libi2c0 libuv1 libusb-1.0-0-dev \
|
libc-bin libc6 libgpiod3 libyaml-cpp0.8 libi2c0 libuv1t64 libusb-1.0-0-dev \
|
||||||
liborcania2.3 libulfius2.7 libssl3 \
|
liborcania2.3 libulfius2.7t64 libssl3t64 \
|
||||||
libx11-6 libinput10 libxkbcommon-x11-0 \
|
libx11-6 libinput10 libxkbcommon-x11-0 \
|
||||||
&& apt-get clean && rm -rf /var/lib/apt/lists/* \
|
&& apt-get clean && rm -rf /var/lib/apt/lists/* \
|
||||||
&& mkdir -p /var/lib/meshtasticd \
|
&& mkdir -p /var/lib/meshtasticd \
|
||||||
@@ -61,7 +61,7 @@ RUN apt-get update && apt-get --no-install-recommends -y install \
|
|||||||
|
|
||||||
# Fetch compiled binary from the builder
|
# Fetch compiled binary from the builder
|
||||||
COPY --from=builder /tmp/firmware/release/meshtasticd /usr/bin/
|
COPY --from=builder /tmp/firmware/release/meshtasticd /usr/bin/
|
||||||
COPY --from=builder /tmp/web /usr/share/meshtasticd/
|
COPY --from=builder /tmp/web /usr/share/meshtasticd/web/
|
||||||
# Copy config templates
|
# Copy config templates
|
||||||
COPY ./bin/config.d /etc/meshtasticd/available.d
|
COPY ./bin/config.d /etc/meshtasticd/available.d
|
||||||
|
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ lib_deps =
|
|||||||
h2zero/NimBLE-Arduino@^1.4.3
|
h2zero/NimBLE-Arduino@^1.4.3
|
||||||
# renovate: datasource=git-refs depName=libpax packageName=https://github.com/dbinfrago/libpax gitBranch=master
|
# renovate: datasource=git-refs depName=libpax packageName=https://github.com/dbinfrago/libpax gitBranch=master
|
||||||
https://github.com/dbinfrago/libpax/archive/3cdc0371c375676a97967547f4065607d4c53fd1.zip
|
https://github.com/dbinfrago/libpax/archive/3cdc0371c375676a97967547f4065607d4c53fd1.zip
|
||||||
# renovate: datasource=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib
|
# renovate: datasource=github-tags depName=XPowersLib packageName=lewisxhe/XPowersLib
|
||||||
lewisxhe/XPowersLib@0.3.0
|
https://github.com/lewisxhe/XPowersLib/archive/v0.3.0.zip
|
||||||
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
|
||||||
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
|
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
|
||||||
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ build_flags =
|
|||||||
-DMESHTASTIC_EXCLUDE_PAXCOUNTER=1
|
-DMESHTASTIC_EXCLUDE_PAXCOUNTER=1
|
||||||
|
|
||||||
build_src_filter =
|
build_src_filter =
|
||||||
${arduino_base.build_src_filter} -<platform/esp32/> -<platform/stm32wl> -<nimble/> -<mesh/wifi/> -<mesh/api/> -<mesh/http/> -<modules/esp32> -<platform/rp2xx0> -<mesh/eth/> -<mesh/raspihttp>
|
${arduino_base.build_src_filter} -<platform/esp32/> -<platform/stm32wl> -<nimble/> -<mesh/wifi/> -<mesh/api/> -<mesh/http/> -<modules/esp32> -<platform/rp2xx0> -<mesh/eth/> -<mesh/raspihttp> -<serialization/>
|
||||||
|
|
||||||
lib_deps=
|
lib_deps=
|
||||||
${arduino_base.lib_deps}
|
${arduino_base.lib_deps}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
[portduino_base]
|
[portduino_base]
|
||||||
platform =
|
platform =
|
||||||
# renovate: datasource=git-refs depName=platform-native packageName=https://github.com/meshtastic/platform-native gitBranch=develop
|
# renovate: datasource=git-refs depName=platform-native packageName=https://github.com/meshtastic/platform-native gitBranch=develop
|
||||||
https://github.com/meshtastic/platform-native/archive/6cb7a455b440dd0738e8ed74a18136ed5cf7ea63.zip
|
https://github.com/meshtastic/platform-native/archive/c490bcd019e0658404088a61b96e653c9da22c45.zip
|
||||||
framework = arduino
|
framework = arduino
|
||||||
|
|
||||||
build_src_filter =
|
build_src_filter =
|
||||||
@@ -17,7 +17,6 @@ build_src_filter =
|
|||||||
+<mesh/raspihttp/>
|
+<mesh/raspihttp/>
|
||||||
-<mesh/eth/>
|
-<mesh/eth/>
|
||||||
-<modules/esp32>
|
-<modules/esp32>
|
||||||
+<../variants/portduino>
|
|
||||||
|
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${env.lib_deps}
|
${env.lib_deps}
|
||||||
@@ -32,14 +31,17 @@ lib_deps =
|
|||||||
https://github.com/pine64/libch341-spi-userspace/archive/af9bc27c9c30fa90772279925b7c5913dff789b4.zip
|
https://github.com/pine64/libch341-spi-userspace/archive/af9bc27c9c30fa90772279925b7c5913dff789b4.zip
|
||||||
# renovate: datasource=custom.pio depName=adafruit/Adafruit seesaw Library packageName=adafruit/library/Adafruit seesaw Library
|
# renovate: datasource=custom.pio depName=adafruit/Adafruit seesaw Library packageName=adafruit/library/Adafruit seesaw Library
|
||||||
adafruit/Adafruit seesaw Library@1.7.9
|
adafruit/Adafruit seesaw Library@1.7.9
|
||||||
|
# renovate: datasource=git-refs depName=RAK12034-BMX160 packageName=https://github.com/RAKWireless/RAK12034-BMX160 gitBranch=main
|
||||||
|
https://github.com/RAKWireless/RAK12034-BMX160/archive/dcead07ffa267d3c906e9ca4a1330ab989e957e2.zip
|
||||||
|
|
||||||
build_flags =
|
build_flags =
|
||||||
${arduino_base.build_flags}
|
${arduino_base.build_flags}
|
||||||
|
-D ARCH_PORTDUINO
|
||||||
-fPIC
|
-fPIC
|
||||||
-Isrc/platform/portduino
|
-Isrc/platform/portduino
|
||||||
-DRADIOLIB_EEPROM_UNSUPPORTED
|
-DRADIOLIB_EEPROM_UNSUPPORTED
|
||||||
-DPORTDUINO_LINUX_HARDWARE
|
-DPORTDUINO_LINUX_HARDWARE
|
||||||
-DHAS_UDP_MULTICAST
|
-DHAS_UDP_MULTICAST=1
|
||||||
-lpthread
|
-lpthread
|
||||||
-lstdc++fs
|
-lstdc++fs
|
||||||
-lbluetooth
|
-lbluetooth
|
||||||
|
|||||||
+10
-4
@@ -2,7 +2,7 @@
|
|||||||
extends = arduino_base
|
extends = arduino_base
|
||||||
platform =
|
platform =
|
||||||
# renovate: datasource=custom.pio depName=platformio/ststm32 packageName=platformio/platform/ststm32
|
# renovate: datasource=custom.pio depName=platformio/ststm32 packageName=platformio/platform/ststm32
|
||||||
platformio/ststm32@19.2.0
|
platformio/ststm32@19.3.0
|
||||||
platform_packages =
|
platform_packages =
|
||||||
# TODO renovate
|
# TODO renovate
|
||||||
platformio/framework-arduinoststm32@https://github.com/stm32duino/Arduino_Core_STM32/archive/2.10.1.zip
|
platformio/framework-arduinoststm32@https://github.com/stm32duino/Arduino_Core_STM32/archive/2.10.1.zip
|
||||||
@@ -23,14 +23,20 @@ build_flags =
|
|||||||
-DMESHTASTIC_EXCLUDE_SCREEN=1
|
-DMESHTASTIC_EXCLUDE_SCREEN=1
|
||||||
-DMESHTASTIC_EXCLUDE_MQTT=1
|
-DMESHTASTIC_EXCLUDE_MQTT=1
|
||||||
-DMESHTASTIC_EXCLUDE_BLUETOOTH=1
|
-DMESHTASTIC_EXCLUDE_BLUETOOTH=1
|
||||||
-DMESHTASTIC_EXCLUDE_GPS=1
|
|
||||||
-DMESHTASTIC_EXCLUDE_WIFI=1
|
-DMESHTASTIC_EXCLUDE_WIFI=1
|
||||||
-DMESHTASTIC_EXCLUDE_TZ=1 ; Exclude TZ to save some flash space.
|
-DMESHTASTIC_EXCLUDE_TZ=1 ; Exclude TZ to save some flash space.
|
||||||
|
-DSERIAL_RX_BUFFER_SIZE=256 ; For GPS - the default of 64 is too small.
|
||||||
|
-DHAS_SCREEN=0 ; Always disable screen for STM32, it is not supported.
|
||||||
-DPIO_FRAMEWORK_ARDUINO_NANOLIB_FLOAT_PRINTF ; This is REQUIRED for at least traceroute debug prints - without it the length ends up uninitialized.
|
-DPIO_FRAMEWORK_ARDUINO_NANOLIB_FLOAT_PRINTF ; This is REQUIRED for at least traceroute debug prints - without it the length ends up uninitialized.
|
||||||
;-DDEBUG_MUTE
|
-DDEBUG_MUTE ; You can #undef DEBUG_MUTE in certain source files if you need the logs.
|
||||||
-fmerge-all-constants
|
-fmerge-all-constants
|
||||||
-ffunction-sections
|
-ffunction-sections
|
||||||
-fdata-sections
|
-fdata-sections
|
||||||
|
-DRADIOLIB_EXCLUDE_SX128X=1
|
||||||
|
-DRADIOLIB_EXCLUDE_SX127X=1
|
||||||
|
-DRADIOLIB_EXCLUDE_LR11X0=1
|
||||||
|
-DHAL_DAC_MODULE_ONLY
|
||||||
|
-DHAL_RNG_MODULE_ENABLED
|
||||||
|
|
||||||
build_src_filter =
|
build_src_filter =
|
||||||
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<mesh/api/> -<mesh/wifi/> -<mesh/http/> -<modules/esp32> -<mesh/eth/> -<input> -<buzz> -<modules/RemoteHardwareModule.cpp> -<platform/nrf52> -<platform/portduino> -<platform/rp2xx0> -<mesh/raspihttp>
|
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<mesh/api/> -<mesh/wifi/> -<mesh/http/> -<modules/esp32> -<mesh/eth/> -<input> -<buzz> -<modules/RemoteHardwareModule.cpp> -<platform/nrf52> -<platform/portduino> -<platform/rp2xx0> -<mesh/raspihttp>
|
||||||
@@ -44,7 +50,7 @@ lib_deps =
|
|||||||
${radiolib_base.lib_deps}
|
${radiolib_base.lib_deps}
|
||||||
|
|
||||||
# renovate: datasource=git-refs depName=caveman99-stm32-Crypto packageName=https://github.com/caveman99/Crypto gitBranch=main
|
# renovate: datasource=git-refs depName=caveman99-stm32-Crypto packageName=https://github.com/caveman99/Crypto gitBranch=main
|
||||||
https://github.com/caveman99/Crypto/archive/eae9c768054118a9399690f8af202853d1ae8516.zip
|
https://github.com/caveman99/Crypto/archive/1aa30eb536bd52a576fde6dfa393bf7349cf102d.zip
|
||||||
|
|
||||||
lib_ignore =
|
lib_ignore =
|
||||||
OneButton
|
OneButton
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -11,7 +9,7 @@ elif (echo $2 | grep -q "nrf52"); then
|
|||||||
elif (echo $2 | grep -q "stm32"); then
|
elif (echo $2 | grep -q "stm32"); then
|
||||||
bin/build-stm32.sh $1
|
bin/build-stm32.sh $1
|
||||||
elif (echo $2 | grep -q "rpi2040"); then
|
elif (echo $2 | grep -q "rpi2040"); then
|
||||||
bin/build-rpi2040.sh $1
|
bin/build-rp2xx0.sh $1
|
||||||
else
|
else
|
||||||
echo "Unknown target $2"
|
echo "Unknown target $2"
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
@@ -9,13 +9,4 @@ Lora:
|
|||||||
DIO3_TCXO_VOLTAGE: true
|
DIO3_TCXO_VOLTAGE: true
|
||||||
DIO2_AS_RF_SWITCH: true
|
DIO2_AS_RF_SWITCH: true
|
||||||
spidev: spidev0.0
|
spidev: spidev0.0
|
||||||
# CS: 8
|
# CS: 8
|
||||||
|
|
||||||
|
|
||||||
### RAK13300in Slot 2 pins
|
|
||||||
# IRQ: 18 #IO6
|
|
||||||
# Reset: 24 # IO4
|
|
||||||
# Busy: 19 # IO5
|
|
||||||
# # Ant_sw: 23 # IO3
|
|
||||||
# spidev: spidev0.1
|
|
||||||
# # CS: 7
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
Lora:
|
||||||
|
### RAK13300in Slot 2 pins
|
||||||
|
IRQ: 18 #IO6
|
||||||
|
Reset: 24 # IO4
|
||||||
|
Busy: 19 # IO5
|
||||||
|
# Ant_sw: 23 # IO3
|
||||||
|
spidev: spidev0.1
|
||||||
|
# CS: 7
|
||||||
+24
-8
@@ -7,6 +7,7 @@ SET "DEBUG=0"
|
|||||||
SET "PYTHON="
|
SET "PYTHON="
|
||||||
SET "TFT_BUILD=0"
|
SET "TFT_BUILD=0"
|
||||||
SET "BIGDB8=0"
|
SET "BIGDB8=0"
|
||||||
|
SET "MUIDB8=0"
|
||||||
SET "BIGDB16=0"
|
SET "BIGDB16=0"
|
||||||
SET "ESPTOOL_BAUD=115200"
|
SET "ESPTOOL_BAUD=115200"
|
||||||
SET "ESPTOOL_CMD="
|
SET "ESPTOOL_CMD="
|
||||||
@@ -14,11 +15,12 @@ SET "LOGCOUNTER=0"
|
|||||||
SET "BPS_RESET=0"
|
SET "BPS_RESET=0"
|
||||||
|
|
||||||
@REM FIXME: Determine mcu from PlatformIO variant, this is unmaintainable.
|
@REM FIXME: Determine mcu from PlatformIO variant, this is unmaintainable.
|
||||||
SET "S3=s3 v3 t-deck wireless-paper wireless-tracker station-g2 unphone"
|
SET "S3=s3 v3 t-deck wireless-paper wireless-tracker station-g2 unphone t-eth-elite tlora-pager mesh-tab dreamcatcher ESP32-S3-Pico seeed-sensecap-indicator heltec_capsule_sensor_v3 vision-master icarus tracksenger elecrow-adv"
|
||||||
SET "C3=esp32c3"
|
SET "C3=esp32c3"
|
||||||
@REM FIXME: Determine flash size from PlatformIO variant, this is unmaintainable.
|
@REM FIXME: Determine flash size from PlatformIO variant, this is unmaintainable.
|
||||||
SET "BIGDB_8MB=picomputer-s3 unphone seeed-sensecap-indicator crowpanel-esp32s3 heltec_capsule_sensor_v3 heltec-v3 heltec-vision-master-e213 heltec-vision-master-e290 heltec-vision-master-t190 heltec-wireless-paper heltec-wireless-tracker heltec-wsl-v3 icarus seeed-xiao-s3 tbeam-s3-core tracksenger"
|
SET "BIGDB_8MB=crowpanel-esp32s3 heltec_capsule_sensor_v3 heltec-v3 heltec-vision-master-e213 heltec-vision-master-e290 heltec-vision-master-t190 heltec-wireless-paper heltec-wireless-tracker heltec-wsl-v3 icarus seeed-xiao-s3 tbeam-s3-core tracksenger"
|
||||||
SET "BIGDB_16MB=t-deck mesh-tab t-energy-s3 dreamcatcher ESP32-S3-Pico m5stack-cores3 station-g2 t-eth-elite t-watch-s3"
|
SET "MUIDB_8MB=picomputer-s3 unphone seeed-sensecap-indicator"
|
||||||
|
SET "BIGDB_16MB=t-deck mesh-tab t-energy-s3 dreamcatcher ESP32-S3-Pico m5stack-cores3 station-g2 t-eth-elite tlora-pager t-watch-s3 elecrow-adv"
|
||||||
|
|
||||||
GOTO getopts
|
GOTO getopts
|
||||||
:help
|
:help
|
||||||
@@ -100,7 +102,6 @@ IF NOT "!FILENAME:update=!"=="!FILENAME!" (
|
|||||||
)
|
)
|
||||||
|
|
||||||
:skip-filename
|
:skip-filename
|
||||||
SET "ESPTOOL_BAUD=1200"
|
|
||||||
|
|
||||||
CALL :LOG_MESSAGE DEBUG "Determine the correct esptool command to use..."
|
CALL :LOG_MESSAGE DEBUG "Determine the correct esptool command to use..."
|
||||||
IF NOT "__%PYTHON%__"=="____" (
|
IF NOT "__%PYTHON%__"=="____" (
|
||||||
@@ -120,11 +121,10 @@ IF NOT "__%PYTHON%__"=="____" (
|
|||||||
|
|
||||||
CALL :LOG_MESSAGE DEBUG "Checking esptool command !ESPTOOL_CMD!..."
|
CALL :LOG_MESSAGE DEBUG "Checking esptool command !ESPTOOL_CMD!..."
|
||||||
!ESPTOOL_CMD! >nul 2>&1
|
!ESPTOOL_CMD! >nul 2>&1
|
||||||
IF %ERRORLEVEL% GEQ 2 (
|
IF %ERRORLEVEL% EQU 9009 (
|
||||||
@REM esptool exits with code 1 if help is displayed.
|
@REM 9009 = command not found on Windows
|
||||||
CALL :LOG_MESSAGE ERROR "esptool not found: !ESPTOOL_CMD!"
|
CALL :LOG_MESSAGE ERROR "esptool not found: !ESPTOOL_CMD!"
|
||||||
EXIT /B 1
|
EXIT /B 1
|
||||||
GOTO eof
|
|
||||||
)
|
)
|
||||||
IF %DEBUG% EQU 1 (
|
IF %DEBUG% EQU 1 (
|
||||||
CALL :LOG_MESSAGE DEBUG "Skipping ESPTOOL_CMD steps."
|
CALL :LOG_MESSAGE DEBUG "Skipping ESPTOOL_CMD steps."
|
||||||
@@ -142,7 +142,7 @@ CALL :LOG_MESSAGE INFO "Using esptool baud: !ESPTOOL_BAUD!."
|
|||||||
|
|
||||||
IF %BPS_RESET% EQU 1 (
|
IF %BPS_RESET% EQU 1 (
|
||||||
@REM Attempt to change mode via 1200bps Reset.
|
@REM Attempt to change mode via 1200bps Reset.
|
||||||
CALL :RUN_ESPTOOL !ESPTOOL_BAUD! --after no_reset read_flash_status
|
CALL :RUN_ESPTOOL 1200 --after no_reset read_flash_status
|
||||||
GOTO eof
|
GOTO eof
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -164,6 +164,15 @@ FOR %%a IN (%BIGDB_8MB%) DO (
|
|||||||
)
|
)
|
||||||
:end_loop_bigdb_8mb
|
:end_loop_bigdb_8mb
|
||||||
|
|
||||||
|
FOR %%a IN (%MUIDB_8MB%) DO (
|
||||||
|
IF NOT "!FILENAME:%%a=!"=="!FILENAME!" (
|
||||||
|
@REM We are working with any of %MUIDB_8MB%.
|
||||||
|
SET "MUIDB8=1"
|
||||||
|
GOTO end_loop_muidb_8mb
|
||||||
|
)
|
||||||
|
)
|
||||||
|
:end_loop_muidb_8mb
|
||||||
|
|
||||||
FOR %%a IN (%BIGDB_16MB%) DO (
|
FOR %%a IN (%BIGDB_16MB%) DO (
|
||||||
IF NOT "!FILENAME:%%a=!"=="!FILENAME!" (
|
IF NOT "!FILENAME:%%a=!"=="!FILENAME!" (
|
||||||
@REM We are working with any of %BIGDB_16MB%.
|
@REM We are working with any of %BIGDB_16MB%.
|
||||||
@@ -174,6 +183,7 @@ FOR %%a IN (%BIGDB_16MB%) DO (
|
|||||||
:end_loop_bigdb_16mb
|
:end_loop_bigdb_16mb
|
||||||
|
|
||||||
IF %BIGDB8% EQU 1 CALL :LOG_MESSAGE INFO "BigDB 8mb partition selected."
|
IF %BIGDB8% EQU 1 CALL :LOG_MESSAGE INFO "BigDB 8mb partition selected."
|
||||||
|
IF %MUIDB8% EQU 1 CALL :LOG_MESSAGE INFO "MUIDB 8mb partition selected."
|
||||||
IF %BIGDB16% EQU 1 CALL :LOG_MESSAGE INFO "BigDB 16mb partition selected."
|
IF %BIGDB16% EQU 1 CALL :LOG_MESSAGE INFO "BigDB 16mb partition selected."
|
||||||
|
|
||||||
@REM Extract BASENAME from %FILENAME% for later use.
|
@REM Extract BASENAME from %FILENAME% for later use.
|
||||||
@@ -218,6 +228,12 @@ IF %BIGDB8% EQU 1 (
|
|||||||
SET "SPIFFS_OFFSET=0x670000"
|
SET "SPIFFS_OFFSET=0x670000"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@REM Offsets for MUIDB 8mb.
|
||||||
|
IF %MUIDB8% EQU 1 (
|
||||||
|
SET "OTA_OFFSET=0x5D0000"
|
||||||
|
SET "SPIFFS_OFFSET=0x670000"
|
||||||
|
)
|
||||||
|
|
||||||
@REM Offsets for BigDB 16mb.
|
@REM Offsets for BigDB 16mb.
|
||||||
IF %BIGDB16% EQU 1 (
|
IF %BIGDB16% EQU 1 (
|
||||||
SET "OTA_OFFSET=0x650000"
|
SET "OTA_OFFSET=0x650000"
|
||||||
|
|||||||
+44
-31
@@ -5,38 +5,43 @@ BPS_RESET=false
|
|||||||
TFT_BUILD=false
|
TFT_BUILD=false
|
||||||
MCU=""
|
MCU=""
|
||||||
|
|
||||||
|
# Constants
|
||||||
|
RESET_BAUD=1200
|
||||||
|
FIRMWARE_OFFSET=0x00
|
||||||
|
|
||||||
# Variant groups
|
# Variant groups
|
||||||
BIGDB_8MB=(
|
BIGDB_8MB=(
|
||||||
"picomputer-s3"
|
"crowpanel-esp32s3"
|
||||||
"unphone"
|
"heltec_capsule_sensor_v3"
|
||||||
"seeed-sensecap-indicator"
|
"heltec-v3"
|
||||||
"crowpanel-esp32s3"
|
"heltec-vision-master-e213"
|
||||||
"heltec_capsule_sensor_v3"
|
"heltec-vision-master-e290"
|
||||||
"heltec-v3"
|
"heltec-vision-master-t190"
|
||||||
"heltec-vision-master-e213"
|
"heltec-wireless-paper"
|
||||||
"heltec-vision-master-e290"
|
"heltec-wireless-tracker"
|
||||||
"heltec-vision-master-t190"
|
"heltec-wsl-v3"
|
||||||
"heltec-wireless-paper"
|
"icarus"
|
||||||
"heltec-wireless-tracker"
|
"seeed-xiao-s3"
|
||||||
"heltec-wsl-v3"
|
"tbeam-s3-core"
|
||||||
"icarus"
|
"tracksenger"
|
||||||
"seeed-xiao-s3"
|
)
|
||||||
"tbeam-s3-core"
|
MUIDB_8MB=(
|
||||||
"tracksenger"
|
"picomputer-s3"
|
||||||
|
"unphone"
|
||||||
|
"seeed-sensecap-indicator"
|
||||||
)
|
)
|
||||||
BIGDB_16MB=(
|
BIGDB_16MB=(
|
||||||
"t-deck"
|
"t-deck"
|
||||||
"mesh-tab"
|
"mesh-tab"
|
||||||
"t-energy-s3"
|
"t-energy-s3"
|
||||||
"dreamcatcher"
|
"dreamcatcher"
|
||||||
"ESP32-S3-Pico"
|
"ESP32-S3-Pico"
|
||||||
"m5stack-cores3"
|
"m5stack-cores3"
|
||||||
"station-g2"
|
"station-g2"
|
||||||
"t-eth-elite"
|
"t-eth-elite"
|
||||||
|
"tlora-pager"
|
||||||
"t-watch-s3"
|
"t-watch-s3"
|
||||||
"elecrow-adv-35-tft"
|
"elecrow-adv"
|
||||||
"elecrow-adv-24-28-tft"
|
|
||||||
"elecrow-adv1-43-50-70-tft"
|
|
||||||
)
|
)
|
||||||
S3_VARIANTS=(
|
S3_VARIANTS=(
|
||||||
"s3"
|
"s3"
|
||||||
@@ -47,6 +52,7 @@ S3_VARIANTS=(
|
|||||||
"station-g2"
|
"station-g2"
|
||||||
"unphone"
|
"unphone"
|
||||||
"t-eth-elite"
|
"t-eth-elite"
|
||||||
|
"tlora-pager"
|
||||||
"mesh-tab"
|
"mesh-tab"
|
||||||
"dreamcatcher"
|
"dreamcatcher"
|
||||||
"ESP32-S3-Pico"
|
"ESP32-S3-Pico"
|
||||||
@@ -106,8 +112,8 @@ while [ $# -gt 0 ]; do
|
|||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
--1200bps-reset)
|
--1200bps-reset)
|
||||||
BPS_RESET=true
|
BPS_RESET=true
|
||||||
;;
|
;;
|
||||||
--) # Stop parsing options
|
--) # Stop parsing options
|
||||||
shift
|
shift
|
||||||
break
|
break
|
||||||
@@ -121,7 +127,7 @@ while [ $# -gt 0 ]; do
|
|||||||
done
|
done
|
||||||
|
|
||||||
if [[ $BPS_RESET == true ]]; then
|
if [[ $BPS_RESET == true ]]; then
|
||||||
$ESPTOOL_CMD --baud 1200 --after no_reset read_flash_status
|
$ESPTOOL_CMD --baud $RESET_BAUD --after no_reset read_flash_status
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -158,6 +164,13 @@ if [ -f "${FILENAME}" ] && [ -n "${FILENAME##*"update"*}" ]; then
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
for variant in "${MUIDB_8MB[@]}"; do
|
||||||
|
if [ -z "${FILENAME##*"$variant"*}" ]; then
|
||||||
|
OFFSET=0x670000
|
||||||
|
OTA_OFFSET=0x5D0000
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
# littlefs* offset for BigDB 16mb and OTA OFFSET.
|
# littlefs* offset for BigDB 16mb and OTA OFFSET.
|
||||||
for variant in "${BIGDB_16MB[@]}"; do
|
for variant in "${BIGDB_16MB[@]}"; do
|
||||||
if [ -z "${FILENAME##*"$variant"*}" ]; then
|
if [ -z "${FILENAME##*"$variant"*}" ]; then
|
||||||
@@ -201,8 +214,8 @@ if [ -f "${FILENAME}" ] && [ -n "${FILENAME##*"update"*}" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Trying to flash ${FILENAME}, but first erasing and writing system information"
|
echo "Trying to flash ${FILENAME}, but first erasing and writing system information"
|
||||||
$ESPTOOL_CMD erase_flash
|
$ESPTOOL_CMD erase-flash
|
||||||
$ESPTOOL_CMD write_flash 0x00 "${FILENAME}"
|
$ESPTOOL_CMD write-flash $FIRMWARE_OFFSET "${FILENAME}"
|
||||||
echo "Trying to flash ${OTAFILE} at offset ${OTA_OFFSET}"
|
echo "Trying to flash ${OTAFILE} at offset ${OTA_OFFSET}"
|
||||||
$ESPTOOL_CMD write_flash $OTA_OFFSET "${OTAFILE}"
|
$ESPTOOL_CMD write_flash $OTA_OFFSET "${OTAFILE}"
|
||||||
echo "Trying to flash ${SPIFFSFILE}, at offset ${OFFSET}"
|
echo "Trying to flash ${SPIFFSFILE}, at offset ${OFFSET}"
|
||||||
|
|||||||
+12
-11
@@ -6,6 +6,8 @@ SET "SCRIPT_NAME=%~nx0"
|
|||||||
SET "DEBUG=0"
|
SET "DEBUG=0"
|
||||||
SET "PYTHON="
|
SET "PYTHON="
|
||||||
SET "ESPTOOL_BAUD=115200"
|
SET "ESPTOOL_BAUD=115200"
|
||||||
|
SET "RESET_BAUD=1200"
|
||||||
|
SET "UPDATE_OFFSET=0x10000"
|
||||||
SET "ESPTOOL_CMD="
|
SET "ESPTOOL_CMD="
|
||||||
SET "LOGCOUNTER=0"
|
SET "LOGCOUNTER=0"
|
||||||
SET "CHANGE_MODE=0"
|
SET "CHANGE_MODE=0"
|
||||||
@@ -85,14 +87,13 @@ IF "!FILENAME:update=!"=="!FILENAME!" (
|
|||||||
)
|
)
|
||||||
|
|
||||||
:skip-filename
|
:skip-filename
|
||||||
SET "ESPTOOL_BAUD=1200"
|
|
||||||
|
|
||||||
CALL :LOG_MESSAGE DEBUG "Determine the correct esptool command to use..."
|
CALL :LOG_MESSAGE DEBUG "Determine the correct esptool command to use..."
|
||||||
IF NOT "__%PYTHON%__"=="____" (
|
IF NOT "__%PYTHON%__"=="____" (
|
||||||
SET "ESPTOOL_CMD=!PYTHON! -m esptool"
|
SET "ESPTOOL_CMD=""!PYTHON!"" -m esptool"
|
||||||
CALL :LOG_MESSAGE DEBUG "Python interpreter supplied."
|
CALL :LOG_MESSAGE DEBUG "Python interpreter supplied."
|
||||||
) ELSE (
|
) ELSE (
|
||||||
CALL :LOG_MESSAGE DEBUG "Python interpreter NOT supplied. Looking for esptool...
|
CALL :LOG_MESSAGE DEBUG "Python interpreter NOT supplied. Looking for esptool..."
|
||||||
WHERE esptool >nul 2>&1
|
WHERE esptool >nul 2>&1
|
||||||
IF %ERRORLEVEL% EQU 0 (
|
IF %ERRORLEVEL% EQU 0 (
|
||||||
@REM WHERE exits with code 0 if esptool is found.
|
@REM WHERE exits with code 0 if esptool is found.
|
||||||
@@ -105,11 +106,11 @@ IF NOT "__%PYTHON%__"=="____" (
|
|||||||
|
|
||||||
CALL :LOG_MESSAGE DEBUG "Checking esptool command !ESPTOOL_CMD!..."
|
CALL :LOG_MESSAGE DEBUG "Checking esptool command !ESPTOOL_CMD!..."
|
||||||
!ESPTOOL_CMD! >nul 2>&1
|
!ESPTOOL_CMD! >nul 2>&1
|
||||||
IF %ERRORLEVEL% GEQ 2 (
|
CALL :LOG_MESSAGE DEBUG "esptool exit code: %ERRORLEVEL%"
|
||||||
@REM esptool exits with code 1 if help is displayed.
|
IF %ERRORLEVEL% EQU 9009 (
|
||||||
|
@REM 9009 = command not found on Windows
|
||||||
CALL :LOG_MESSAGE ERROR "esptool not found: !ESPTOOL_CMD!"
|
CALL :LOG_MESSAGE ERROR "esptool not found: !ESPTOOL_CMD!"
|
||||||
EXIT /B 1
|
EXIT /B 1
|
||||||
GOTO eof
|
|
||||||
)
|
)
|
||||||
IF %DEBUG% EQU 1 (
|
IF %DEBUG% EQU 1 (
|
||||||
CALL :LOG_MESSAGE DEBUG "Skipping ESPTOOL_CMD steps."
|
CALL :LOG_MESSAGE DEBUG "Skipping ESPTOOL_CMD steps."
|
||||||
@@ -127,13 +128,13 @@ CALL :LOG_MESSAGE INFO "Using esptool baud: !ESPTOOL_BAUD!."
|
|||||||
|
|
||||||
IF %CHANGE_MODE% EQU 1 (
|
IF %CHANGE_MODE% EQU 1 (
|
||||||
@REM Attempt to change mode via 1200bps Reset.
|
@REM Attempt to change mode via 1200bps Reset.
|
||||||
CALL :RUN_ESPTOOL !ESPTOOL_BAUD! --after no_reset read_flash_status
|
CALL :RUN_ESPTOOL !RESET_BAUD! --after no_reset read_flash_status
|
||||||
GOTO eof
|
GOTO eof
|
||||||
)
|
)
|
||||||
|
|
||||||
@REM Flashing operations.
|
@REM Flashing operations.
|
||||||
CALL :LOG_MESSAGE INFO "Trying to flash update "!FILENAME!" at OFFSET 0x10000..."
|
CALL :LOG_MESSAGE INFO "Trying to flash update "!FILENAME!" at OFFSET !UPDATE_OFFSET!..."
|
||||||
CALL :RUN_ESPTOOL !ESPTOOL_BAUD! write_flash 0x10000 "!FILENAME!" || GOTO eof
|
CALL :RUN_ESPTOOL !ESPTOOL_BAUD! write-flash !UPDATE_OFFSET! "!FILENAME!" || GOTO eof
|
||||||
|
|
||||||
CALL :LOG_MESSAGE INFO "Script complete!."
|
CALL :LOG_MESSAGE INFO "Script complete!."
|
||||||
|
|
||||||
@@ -145,9 +146,9 @@ EXIT /B %ERRORLEVEL%
|
|||||||
:RUN_ESPTOOL
|
:RUN_ESPTOOL
|
||||||
@REM Subroutine used to run ESPTOOL_CMD with arguments.
|
@REM Subroutine used to run ESPTOOL_CMD with arguments.
|
||||||
@REM Also handles %ERRORLEVEL%.
|
@REM Also handles %ERRORLEVEL%.
|
||||||
@REM CALL :RUN_ESPTOOL [Baud] [erase_flash|write_flash] [OFFSET] [Filename]
|
@REM CALL :RUN_ESPTOOL [Baud] [erase-flash|write-flash] [OFFSET] [Filename]
|
||||||
@REM.
|
@REM.
|
||||||
@REM Example:: CALL :RUN_ESPTOOL 115200 write_flash 0x10000 "firmwarefile.bin"
|
@REM Example:: CALL :RUN_ESPTOOL 115200 write-flash 0x10000 "firmwarefile.bin"
|
||||||
IF %DEBUG% EQU 1 CALL :LOG_MESSAGE DEBUG "About to run command: !ESPTOOL_CMD! --baud %~1 %~2 %~3 %~4"
|
IF %DEBUG% EQU 1 CALL :LOG_MESSAGE DEBUG "About to run command: !ESPTOOL_CMD! --baud %~1 %~2 %~3 %~4"
|
||||||
CALL :RESET_ERROR
|
CALL :RESET_ERROR
|
||||||
!ESPTOOL_CMD! --baud %~1 %~2 %~3 %~4
|
!ESPTOOL_CMD! --baud %~1 %~2 %~3 %~4
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
|
|
||||||
PYTHON=${PYTHON:-$(which python3 python|head -n 1)}
|
PYTHON=${PYTHON:-$(which python3 python|head -n 1)}
|
||||||
CHANGE_MODE=false
|
CHANGE_MODE=false
|
||||||
|
|
||||||
|
# Constants
|
||||||
|
FLASH_BAUD=115200
|
||||||
|
RESET_BAUD=1200
|
||||||
|
UPDATE_OFFSET=0x10000
|
||||||
|
|
||||||
# Determine the correct esptool command to use
|
# Determine the correct esptool command to use
|
||||||
if "$PYTHON" -m esptool version >/dev/null 2>&1; then
|
if "$PYTHON" -m esptool version >/dev/null 2>&1; then
|
||||||
ESPTOOL_CMD="$PYTHON -m esptool"
|
ESPTOOL_CMD="$PYTHON -m esptool"
|
||||||
@@ -64,7 +69,7 @@ done
|
|||||||
shift "$((OPTIND-1))"
|
shift "$((OPTIND-1))"
|
||||||
|
|
||||||
if [ "$CHANGE_MODE" = true ]; then
|
if [ "$CHANGE_MODE" = true ]; then
|
||||||
$ESPTOOL_CMD --baud 1200 --after no_reset read_flash_status
|
$ESPTOOL_CMD --baud $RESET_BAUD --after no_reset read_flash_status
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -75,7 +80,7 @@ fi
|
|||||||
|
|
||||||
if [ -f "${FILENAME}" ] && [ -z "${FILENAME##*"update"*}" ]; then
|
if [ -f "${FILENAME}" ] && [ -z "${FILENAME##*"update"*}" ]; then
|
||||||
echo "Trying to flash update ${FILENAME}"
|
echo "Trying to flash update ${FILENAME}"
|
||||||
$ESPTOOL_CMD --baud 115200 write_flash 0x10000 "${FILENAME}"
|
$ESPTOOL_CMD --baud $FLASH_BAUD write-flash $UPDATE_OFFSET "${FILENAME}"
|
||||||
else
|
else
|
||||||
show_help
|
show_help
|
||||||
echo "Invalid file: ${FILENAME}"
|
echo "Invalid file: ${FILENAME}"
|
||||||
|
|||||||
+57
-36
@@ -2,50 +2,71 @@
|
|||||||
|
|
||||||
"""Generate the CI matrix."""
|
"""Generate the CI matrix."""
|
||||||
|
|
||||||
import configparser
|
|
||||||
import json
|
import json
|
||||||
import os
|
|
||||||
import sys
|
import sys
|
||||||
import random
|
import random
|
||||||
|
import re
|
||||||
rootdir = "variants/"
|
from platformio.project.config import ProjectConfig
|
||||||
|
|
||||||
options = sys.argv[1:]
|
options = sys.argv[1:]
|
||||||
|
|
||||||
outlist = []
|
outlist = []
|
||||||
|
|
||||||
if len(options) < 1:
|
if len(options) < 1:
|
||||||
print(json.dumps(outlist))
|
print(json.dumps(outlist))
|
||||||
exit()
|
exit(1)
|
||||||
|
|
||||||
for subdir, dirs, files in os.walk(rootdir):
|
cfg = ProjectConfig.get_instance()
|
||||||
for file in files:
|
pio_envs = cfg.envs()
|
||||||
if file == "platformio.ini":
|
|
||||||
config = configparser.ConfigParser()
|
# Gather all PlatformIO environments for filtering later
|
||||||
config.read(subdir + "/" + file)
|
all_envs = []
|
||||||
for c in config.sections():
|
for pio_env in pio_envs:
|
||||||
if c.startswith("env:"):
|
env_build_flags = cfg.get(f"env:{pio_env}", 'build_flags')
|
||||||
section = config[c].name[4:]
|
env_platform = None
|
||||||
if "extends" in config[config[c].name]:
|
for flag in env_build_flags:
|
||||||
if options[0] + "_base" in config[config[c].name]["extends"]:
|
# Extract the platform from the build flags
|
||||||
if "board_level" in config[config[c].name]:
|
# Example flag: -I variants/esp32s3/heltec-v3
|
||||||
if (
|
match = re.search(r"-I\s?variants/([^/]+)", flag)
|
||||||
config[config[c].name]["board_level"] == "extra"
|
if match:
|
||||||
) & ("extra" in options):
|
env_platform = match.group(1)
|
||||||
outlist.append(section)
|
break
|
||||||
else:
|
# Intentionally fail if platform cannot be determined
|
||||||
outlist.append(section)
|
if not env_platform:
|
||||||
# Add the TFT variants if the base variant is selected
|
print(f"Error: Could not determine platform for environment '{pio_env}'")
|
||||||
elif section.replace("-tft", "") in outlist and config[config[c].name].get("board_level") != "extra":
|
exit(1)
|
||||||
outlist.append(section)
|
# Store env details as a dictionary, and add to 'all_envs' list
|
||||||
elif section.replace("-inkhud", "") in outlist and config[config[c].name].get("board_level") != "extra":
|
env = {
|
||||||
outlist.append(section)
|
'name': pio_env,
|
||||||
if "board_check" in config[config[c].name]:
|
'platform': env_platform,
|
||||||
if (config[config[c].name]["board_check"] == "true") & (
|
'board_level': cfg.get(f"env:{pio_env}", 'board_level', default=None),
|
||||||
"check" in options
|
'board_check': bool(cfg.get(f"env:{pio_env}", 'board_check', default=False))
|
||||||
):
|
}
|
||||||
outlist.append(section)
|
all_envs.append(env)
|
||||||
if ("quick" in options) & (len(outlist) > 3):
|
|
||||||
print(json.dumps(random.sample(outlist, 3)))
|
# Filter outputs based on options
|
||||||
|
# Check is mutually exclusive with other options (except 'pr')
|
||||||
|
if "check" in options:
|
||||||
|
for env in all_envs:
|
||||||
|
if env['board_check']:
|
||||||
|
if "pr" in options:
|
||||||
|
if env['board_level'] == 'pr':
|
||||||
|
outlist.append(env['name'])
|
||||||
|
else:
|
||||||
|
outlist.append(env['name'])
|
||||||
|
# Filter (non-check) builds by platform
|
||||||
else:
|
else:
|
||||||
print(json.dumps(outlist))
|
for env in all_envs:
|
||||||
|
if options[0] == env['platform']:
|
||||||
|
# Always include board_level = 'pr'
|
||||||
|
if env['board_level'] == 'pr':
|
||||||
|
outlist.append(env['name'])
|
||||||
|
# Include board_level = 'extra' when requested
|
||||||
|
elif "extra" in options and env['board_level'] == "extra":
|
||||||
|
outlist.append(env['name'])
|
||||||
|
# If no board level is specified, include in release builds (not PR)
|
||||||
|
elif "pr" not in options and not env['board_level']:
|
||||||
|
outlist.append(env['name'])
|
||||||
|
|
||||||
|
# Return as a JSON list
|
||||||
|
print(json.dumps(outlist))
|
||||||
|
|||||||
@@ -87,6 +87,24 @@
|
|||||||
</screenshots>
|
</screenshots>
|
||||||
|
|
||||||
<releases>
|
<releases>
|
||||||
|
<release version="2.7.9" date="2025-09-03">
|
||||||
|
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.9</url>
|
||||||
|
</release>
|
||||||
|
<release version="2.7.8" date="2025-08-30">
|
||||||
|
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.8</url>
|
||||||
|
</release>
|
||||||
|
<release version="2.7.7" date="2025-08-28">
|
||||||
|
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.7</url>
|
||||||
|
</release>
|
||||||
|
<release version="2.7.6" date="2025-08-12">
|
||||||
|
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.6</url>
|
||||||
|
</release>
|
||||||
|
<release version="2.7.5" date="2025-08-09">
|
||||||
|
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.5</url>
|
||||||
|
</release>
|
||||||
|
<release version="2.7.4" date="2025-07-19">
|
||||||
|
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.4</url>
|
||||||
|
</release>
|
||||||
<release version="2.7.3" date="2025-07-10">
|
<release version="2.7.3" date="2025-07-10">
|
||||||
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.3</url>
|
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.3</url>
|
||||||
</release>
|
</release>
|
||||||
|
|||||||
@@ -3,8 +3,11 @@
|
|||||||
# trunk-ignore-all(flake8/F821): For SConstruct imports
|
# trunk-ignore-all(flake8/F821): For SConstruct imports
|
||||||
import sys
|
import sys
|
||||||
from os.path import join
|
from os.path import join
|
||||||
|
import subprocess
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
import time
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from readprops import readProps
|
from readprops import readProps
|
||||||
|
|
||||||
@@ -92,6 +95,17 @@ prefsLoc = projenv["PROJECT_DIR"] + "/version.properties"
|
|||||||
verObj = readProps(prefsLoc)
|
verObj = readProps(prefsLoc)
|
||||||
print("Using meshtastic platformio-custom.py, firmware version " + verObj["long"] + " on " + env.get("PIOENV"))
|
print("Using meshtastic platformio-custom.py, firmware version " + verObj["long"] + " on " + env.get("PIOENV"))
|
||||||
|
|
||||||
|
# get repository owner if git is installed
|
||||||
|
try:
|
||||||
|
r_owner = (
|
||||||
|
subprocess.check_output(["git", "config", "--get", "remote.origin.url"])
|
||||||
|
.decode("utf-8")
|
||||||
|
.strip().split("/")
|
||||||
|
)
|
||||||
|
repo_owner = r_owner[-2] + "/" + r_owner[-1].replace(".git", "")
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
repo_owner = "unknown"
|
||||||
|
|
||||||
jsonLoc = env["PROJECT_DIR"] + "/userPrefs.jsonc"
|
jsonLoc = env["PROJECT_DIR"] + "/userPrefs.jsonc"
|
||||||
with open(jsonLoc) as f:
|
with open(jsonLoc) as f:
|
||||||
jsonStr = re.sub("//.*","", f.read(), flags=re.MULTILINE)
|
jsonStr = re.sub("//.*","", f.read(), flags=re.MULTILINE)
|
||||||
@@ -113,10 +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,
|
||||||
|
"-DBUILD_EPOCH=" + str(build_epoch),
|
||||||
] + pref_flags
|
] + pref_flags
|
||||||
|
|
||||||
print ("Using flags:")
|
print ("Using flags:")
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"arduino": {
|
||||||
|
"ldscript": "nrf52840_s140_v6.ld"
|
||||||
|
},
|
||||||
|
"core": "nRF5",
|
||||||
|
"cpu": "cortex-m4",
|
||||||
|
"extra_flags": "-DNRF52840_XXAA",
|
||||||
|
"f_cpu": "64000000L",
|
||||||
|
"hwids": [
|
||||||
|
["0x239A", "0x4405"],
|
||||||
|
["0x239A", "0x0029"],
|
||||||
|
["0x239A", "0x002A"],
|
||||||
|
["0x239A", "0x0071"]
|
||||||
|
],
|
||||||
|
"usb_product": "HT-n5262",
|
||||||
|
"mcu": "nrf52840",
|
||||||
|
"variant": "heltec_mesh_solar",
|
||||||
|
"variants_dir": "variants",
|
||||||
|
"bsp": {
|
||||||
|
"name": "adafruit"
|
||||||
|
},
|
||||||
|
"softdevice": {
|
||||||
|
"sd_flags": "-DS140",
|
||||||
|
"sd_name": "s140",
|
||||||
|
"sd_version": "6.1.1",
|
||||||
|
"sd_fwid": "0x00B6"
|
||||||
|
},
|
||||||
|
"bootloader": {
|
||||||
|
"settings_addr": "0xFF000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connectivity": ["bluetooth"],
|
||||||
|
"debug": {
|
||||||
|
"jlink_device": "nRF52840_xxAA",
|
||||||
|
"onboard_tools": ["jlink"],
|
||||||
|
"svd_path": "nrf52840.svd",
|
||||||
|
"openocd_target": "nrf52840-mdk-rs"
|
||||||
|
},
|
||||||
|
"frameworks": ["arduino"],
|
||||||
|
"name": "Heltec nrf (Adafruit BSP)",
|
||||||
|
"upload": {
|
||||||
|
"maximum_ram_size": 248832,
|
||||||
|
"maximum_size": 815104,
|
||||||
|
"speed": 115200,
|
||||||
|
"protocol": "nrfutil",
|
||||||
|
"protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"],
|
||||||
|
"use_1200bps_touch": true,
|
||||||
|
"require_upload_port": true,
|
||||||
|
"wait_for_upload_port": true
|
||||||
|
},
|
||||||
|
"url": "https://heltec.org/project/meshsolar/",
|
||||||
|
"vendor": "Heltec"
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"arduino": {
|
||||||
|
"ldscript": "nrf52840_s140_v6.ld"
|
||||||
|
},
|
||||||
|
"core": "nRF5",
|
||||||
|
"cpu": "cortex-m4",
|
||||||
|
"extra_flags": "-DARDUINO_NRF52840_FEATHER -DNRF52840_XXAA",
|
||||||
|
"f_cpu": "64000000L",
|
||||||
|
"hwids": [
|
||||||
|
["0x239A", "0x8029"],
|
||||||
|
["0x239A", "0x0029"],
|
||||||
|
["0x239A", "0x002A"],
|
||||||
|
["0x239A", "0x802A"]
|
||||||
|
],
|
||||||
|
"usb_product": "MeshTiny",
|
||||||
|
"mcu": "nrf52840",
|
||||||
|
"variant": "meshtiny",
|
||||||
|
"bsp": {
|
||||||
|
"name": "adafruit"
|
||||||
|
},
|
||||||
|
"softdevice": {
|
||||||
|
"sd_flags": "-DS140",
|
||||||
|
"sd_name": "s140",
|
||||||
|
"sd_version": "6.1.1",
|
||||||
|
"sd_fwid": "0x00B6"
|
||||||
|
},
|
||||||
|
"bootloader": {
|
||||||
|
"settings_addr": "0xFF000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connectivity": ["bluetooth"],
|
||||||
|
"debug": {
|
||||||
|
"jlink_device": "nRF52840_xxAA",
|
||||||
|
"svd_path": "nrf52840.svd",
|
||||||
|
"openocd_target": "nrf52840-mdk-rs"
|
||||||
|
},
|
||||||
|
"frameworks": ["arduino", "freertos"],
|
||||||
|
"name": "MeshTiny",
|
||||||
|
"upload": {
|
||||||
|
"maximum_ram_size": 248832,
|
||||||
|
"maximum_size": 815104,
|
||||||
|
"speed": 115200,
|
||||||
|
"protocol": "nrfutil",
|
||||||
|
"protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"],
|
||||||
|
"use_1200bps_touch": true,
|
||||||
|
"require_upload_port": true,
|
||||||
|
"wait_for_upload_port": true
|
||||||
|
},
|
||||||
|
"url": "https://github.com/meshtastic/firmware",
|
||||||
|
"vendor": "MTools Tec"
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
"build": {
|
"build": {
|
||||||
"arduino": {
|
"arduino": {
|
||||||
"ldscript": "esp32s3_out.ld",
|
"ldscript": "esp32s3_out.ld",
|
||||||
"partitions": "default_8MB.csv",
|
"partitions": "partition-table-8MB.csv",
|
||||||
"memory_type": "qio_opi"
|
"memory_type": "qio_opi"
|
||||||
},
|
},
|
||||||
"core": "esp32",
|
"core": "esp32",
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"arduino": {
|
||||||
|
"ldscript": "esp32s3_out.ld",
|
||||||
|
"memory_type": "qio_qspi",
|
||||||
|
"partitions": "default_16MB.csv"
|
||||||
|
},
|
||||||
|
"core": "esp32",
|
||||||
|
"extra_flags": [
|
||||||
|
"-DBOARD_HAS_PSRAM",
|
||||||
|
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||||
|
"-DARDUINO_USB_MODE=1",
|
||||||
|
"-DARDUINO_RUNNING_CORE=1",
|
||||||
|
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||||
|
],
|
||||||
|
"f_cpu": "240000000L",
|
||||||
|
"f_flash": "80000000L",
|
||||||
|
"flash_mode": "qio",
|
||||||
|
"hwids": [["0x303A", "0x1001"]],
|
||||||
|
"mcu": "esp32s3",
|
||||||
|
"variant": "esp32s3"
|
||||||
|
},
|
||||||
|
"connectivity": ["wifi", "bluetooth", "lora"],
|
||||||
|
"debug": {
|
||||||
|
"default_tool": "esp-builtin",
|
||||||
|
"onboard_tools": ["esp-builtin"],
|
||||||
|
"openocd_target": "esp32s3.cfg"
|
||||||
|
},
|
||||||
|
"frameworks": ["arduino", "espidf"],
|
||||||
|
"name": "LilyGo T-Deck Pro S3 (16M Flash 8M QSPI PSRAM )",
|
||||||
|
"upload": {
|
||||||
|
"flash_size": "16MB",
|
||||||
|
"maximum_ram_size": 327680,
|
||||||
|
"maximum_size": 16777216,
|
||||||
|
"require_upload_port": true,
|
||||||
|
"speed": 921600
|
||||||
|
},
|
||||||
|
"monitor": {
|
||||||
|
"speed": 115200
|
||||||
|
},
|
||||||
|
"url": "https://lilygo.cc/products/t-deck-pro",
|
||||||
|
"vendor": "LilyGo"
|
||||||
|
}
|
||||||
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
"arduino": {
|
"arduino": {
|
||||||
"ldscript": "esp32s3_out.ld",
|
"ldscript": "esp32s3_out.ld",
|
||||||
"memory_type": "qio_opi",
|
"memory_type": "qio_opi",
|
||||||
"partitions": "default_8MB.csv"
|
"partitions": "partition-table-8MB.csv"
|
||||||
},
|
},
|
||||||
"core": "esp32",
|
"core": "esp32",
|
||||||
"extra_flags": [
|
"extra_flags": [
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
},
|
},
|
||||||
"core": "stm32",
|
"core": "stm32",
|
||||||
"cpu": "cortex-m4",
|
"cpu": "cortex-m4",
|
||||||
"extra_flags": "-DSTM32WLxx -DSTM32WLE5xx -DARDUINO_GENERIC_WLE5CCUX",
|
"extra_flags": "-DSTM32WLxx -DSTM32WLE5xx -DARDUINO_RAK3172_MODULE",
|
||||||
"f_cpu": "48000000L",
|
"f_cpu": "48000000L",
|
||||||
"mcu": "stm32wle5ccu",
|
"mcu": "stm32wle5ccu",
|
||||||
"variant": "STM32WLxx/WL54CCU_WL55CCU_WLE4C(8-B-C)U_WLE5C(8-B-C)U",
|
"variant": "STM32WLxx/WL54CCU_WL55CCU_WLE4C(8-B-C)U_WLE5C(8-B-C)U",
|
||||||
|
|||||||
Vendored
+18
-2
@@ -1,4 +1,4 @@
|
|||||||
meshtasticd (2.7.3.0) UNRELEASED; urgency=medium
|
meshtasticd (2.7.9.0) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
[ Austin Lane ]
|
[ Austin Lane ]
|
||||||
* Initial packaging
|
* Initial packaging
|
||||||
@@ -31,4 +31,20 @@ meshtasticd (2.7.3.0) UNRELEASED; urgency=medium
|
|||||||
[ Ubuntu ]
|
[ Ubuntu ]
|
||||||
* GitHub Actions Automatic version bump
|
* GitHub Actions Automatic version bump
|
||||||
|
|
||||||
-- Ubuntu <github-actions[bot]@users.noreply.github.com> Thu, 10 Jul 2025 16:29:27 +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[bot]@users.noreply.github.com> Wed, 03 Sep 2025 23:39:17 +0000
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
# postinst script for meshtasticd
|
# postinst script for meshtasticd
|
||||||
#
|
#
|
||||||
# see: dh_installdeb(1)
|
# see: dh_installdeb(1)
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
# postrm script for meshtasticd
|
# postrm script for meshtasticd
|
||||||
#
|
#
|
||||||
# see: dh_installdeb(1)
|
# see: dh_installdeb(1)
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# This is a layout for 8MB of flash for MUI devices
|
||||||
|
# Name, Type, SubType, Offset, Size, Flags
|
||||||
|
nvs, data, nvs, 0x9000, 0x5000,
|
||||||
|
otadata, data, ota, 0xe000, 0x2000,
|
||||||
|
app0, app, ota_0, 0x10000, 0x5C0000,
|
||||||
|
flashApp, app, ota_1, 0x5D0000,0x0A0000,
|
||||||
|
spiffs, data, spiffs, 0x670000,0x180000
|
||||||
|
+21
-10
@@ -6,7 +6,8 @@ default_envs = tbeam
|
|||||||
|
|
||||||
extra_configs =
|
extra_configs =
|
||||||
arch/*/*.ini
|
arch/*/*.ini
|
||||||
variants/*/platformio.ini
|
variants/*/*/platformio.ini
|
||||||
|
variants/*/diy/*/platformio.ini
|
||||||
src/graphics/niche/InkHUD/PlatformioConfig.ini
|
src/graphics/niche/InkHUD/PlatformioConfig.ini
|
||||||
|
|
||||||
description = Meshtastic
|
description = Meshtastic
|
||||||
@@ -52,16 +53,16 @@ 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
|
||||||
|
|
||||||
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/0119501e9983bd894830b02f545c377ee08d66fe.zip
|
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/0cbc26b1f8f61957af0475f486b362eafe7cc4e2.zip
|
||||||
# renovate: datasource=custom.pio depName=OneButton packageName=mathertel/library/OneButton
|
# renovate: datasource=git-refs depName=meshtastic-OneButton packageName=https://github.com/meshtastic/OneButton gitBranch=master
|
||||||
mathertel/OneButton@2.6.1
|
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
|
||||||
https://github.com/meshtastic/arduino-fsm/archive/7db3702bf0cfe97b783d6c72595e3f38e0b19159.zip
|
https://github.com/meshtastic/arduino-fsm/archive/7db3702bf0cfe97b783d6c72595e3f38e0b19159.zip
|
||||||
# renovate: datasource=git-refs depName=meshtastic-TinyGPSPlus packageName=https://github.com/meshtastic/TinyGPSPlus gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic-TinyGPSPlus packageName=https://github.com/meshtastic/TinyGPSPlus gitBranch=master
|
||||||
@@ -101,6 +102,14 @@ lib_deps =
|
|||||||
# renovate: datasource=custom.pio depName=Syslog packageName=arcao/library/Syslog
|
# renovate: datasource=custom.pio depName=Syslog packageName=arcao/library/Syslog
|
||||||
arcao/Syslog@2.0.0
|
arcao/Syslog@2.0.0
|
||||||
|
|
||||||
|
; Minimal networking libs for nrf52 (excludes Syslog to save flash)
|
||||||
|
[nrf52_networking_base]
|
||||||
|
lib_deps =
|
||||||
|
# renovate: datasource=custom.pio depName=TBPubSubClient packageName=thingsboard/library/TBPubSubClient
|
||||||
|
thingsboard/TBPubSubClient@2.12.1
|
||||||
|
# renovate: datasource=custom.pio depName=NTPClient packageName=arduino-libraries/library/NTPClient
|
||||||
|
arduino-libraries/NTPClient@3.2.1
|
||||||
|
|
||||||
[radiolib_base]
|
[radiolib_base]
|
||||||
lib_deps =
|
lib_deps =
|
||||||
# renovate: datasource=custom.pio depName=RadioLib packageName=jgromes/library/RadioLib
|
# renovate: datasource=custom.pio depName=RadioLib packageName=jgromes/library/RadioLib
|
||||||
@@ -109,13 +118,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/86a09a7360f92d10053fbbf8d74f67f85b0ceb09.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
|
||||||
@@ -148,8 +157,8 @@ lib_deps =
|
|||||||
emotibit/EmotiBit MLX90632@1.0.8
|
emotibit/EmotiBit MLX90632@1.0.8
|
||||||
# renovate: datasource=custom.pio depName=Adafruit MLX90614 packageName=adafruit/library/Adafruit MLX90614 Library
|
# renovate: datasource=custom.pio depName=Adafruit MLX90614 packageName=adafruit/library/Adafruit MLX90614 Library
|
||||||
adafruit/Adafruit MLX90614 Library@2.1.5
|
adafruit/Adafruit MLX90614 Library@2.1.5
|
||||||
# renovate: datasource=github-tags depName=INA3221 packageName=KodinLanewave/INA3221
|
# renovate: datasource=github-tags depName=INA3221 packageName=sgtwilko/INA3221
|
||||||
https://github.com/KodinLanewave/INA3221/archive/1.0.1.zip
|
https://github.com/sgtwilko/INA3221#bb03d7e9bfcc74fc798838a54f4f99738f29fc6a
|
||||||
# renovate: datasource=custom.pio depName=QMC5883L Compass packageName=mprograms/library/QMC5883LCompass
|
# renovate: datasource=custom.pio depName=QMC5883L Compass packageName=mprograms/library/QMC5883LCompass
|
||||||
mprograms/QMC5883LCompass@1.2.3
|
mprograms/QMC5883LCompass@1.2.3
|
||||||
# renovate: datasource=custom.pio depName=DFRobot_RTU packageName=dfrobot/library/DFRobot_RTU
|
# renovate: datasource=custom.pio depName=DFRobot_RTU packageName=dfrobot/library/DFRobot_RTU
|
||||||
@@ -168,6 +177,8 @@ lib_deps =
|
|||||||
adafruit/Adafruit PCT2075@1.0.5
|
adafruit/Adafruit PCT2075@1.0.5
|
||||||
# renovate: datasource=custom.pio depName=DFRobot_BMM150 packageName=dfrobot/library/DFRobot_BMM150
|
# renovate: datasource=custom.pio depName=DFRobot_BMM150 packageName=dfrobot/library/DFRobot_BMM150
|
||||||
dfrobot/DFRobot_BMM150@1.0.0
|
dfrobot/DFRobot_BMM150@1.0.0
|
||||||
|
# renovate: datasource=custom.pio depName=Adafruit_TSL2561 packageName=adafruit/library/Adafruit TSL2561
|
||||||
|
adafruit/Adafruit TSL2561@1.1.2
|
||||||
|
|
||||||
; (not included in native / portduino)
|
; (not included in native / portduino)
|
||||||
[environmental_extra]
|
[environmental_extra]
|
||||||
@@ -177,7 +188,7 @@ lib_deps =
|
|||||||
# renovate: datasource=custom.pio depName=Adafruit MAX1704X packageName=adafruit/library/Adafruit MAX1704X
|
# renovate: datasource=custom.pio depName=Adafruit MAX1704X packageName=adafruit/library/Adafruit MAX1704X
|
||||||
adafruit/Adafruit MAX1704X@1.0.3
|
adafruit/Adafruit MAX1704X@1.0.3
|
||||||
# renovate: datasource=custom.pio depName=Adafruit SHTC3 packageName=adafruit/library/Adafruit SHTC3 Library
|
# renovate: datasource=custom.pio depName=Adafruit SHTC3 packageName=adafruit/library/Adafruit SHTC3 Library
|
||||||
adafruit/Adafruit SHTC3 Library@1.0.1
|
adafruit/Adafruit SHTC3 Library@1.0.2
|
||||||
# renovate: datasource=custom.pio depName=Adafruit LPS2X packageName=adafruit/library/Adafruit LPS2X
|
# renovate: datasource=custom.pio depName=Adafruit LPS2X packageName=adafruit/library/Adafruit LPS2X
|
||||||
adafruit/Adafruit LPS2X@2.0.6
|
adafruit/Adafruit LPS2X@2.0.6
|
||||||
# renovate: datasource=custom.pio depName=Adafruit SHT31 packageName=adafruit/library/Adafruit SHT31 Library
|
# renovate: datasource=custom.pio depName=Adafruit SHT31 packageName=adafruit/library/Adafruit SHT31 Library
|
||||||
|
|||||||
+1
-1
Submodule protobufs updated: 584f0a3a35...945b796a98
@@ -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);
|
||||||
|
|||||||
@@ -89,14 +89,22 @@ class BluetoothStatus : public Status
|
|||||||
case ConnectionState::CONNECTED:
|
case ConnectionState::CONNECTED:
|
||||||
LOG_DEBUG("BluetoothStatus CONNECTED");
|
LOG_DEBUG("BluetoothStatus CONNECTED");
|
||||||
#ifdef BLE_LED
|
#ifdef BLE_LED
|
||||||
|
#ifdef BLE_LED_INVERTED
|
||||||
|
digitalWrite(BLE_LED, LOW);
|
||||||
|
#else
|
||||||
digitalWrite(BLE_LED, HIGH);
|
digitalWrite(BLE_LED, HIGH);
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ConnectionState::DISCONNECTED:
|
case ConnectionState::DISCONNECTED:
|
||||||
LOG_DEBUG("BluetoothStatus DISCONNECTED");
|
LOG_DEBUG("BluetoothStatus DISCONNECTED");
|
||||||
#ifdef BLE_LED
|
#ifdef BLE_LED
|
||||||
|
#ifdef BLE_LED_INVERTED
|
||||||
|
digitalWrite(BLE_LED, HIGH);
|
||||||
|
#else
|
||||||
digitalWrite(BLE_LED, LOW);
|
digitalWrite(BLE_LED, LOW);
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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, ...);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
#include "DisplayFormatters.h"
|
#include "DisplayFormatters.h"
|
||||||
|
|
||||||
const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName)
|
const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName,
|
||||||
|
bool usePreset)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// If use_preset is false, always return "Custom"
|
||||||
|
if (!usePreset) {
|
||||||
|
return "Custom";
|
||||||
|
}
|
||||||
|
|
||||||
switch (preset) {
|
switch (preset) {
|
||||||
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO:
|
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO:
|
||||||
return useShortName ? "ShortT" : "ShortTurbo";
|
return useShortName ? "ShortT" : "ShortTurbo";
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
class DisplayFormatters
|
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);
|
||||||
};
|
};
|
||||||
|
|||||||
+317
-15
@@ -20,6 +20,11 @@
|
|||||||
#include "meshUtils.h"
|
#include "meshUtils.h"
|
||||||
#include "sleep.h"
|
#include "sleep.h"
|
||||||
|
|
||||||
|
#if defined(ARCH_PORTDUINO)
|
||||||
|
#include "api/WiFiServerAPI.h"
|
||||||
|
#include "input/LinuxInputImpl.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
// Working USB detection for powered/charging states on the RAK platform
|
// Working USB detection for powered/charging states on the RAK platform
|
||||||
#ifdef NRF_APM
|
#ifdef NRF_APM
|
||||||
#include "nrfx_power.h"
|
#include "nrfx_power.h"
|
||||||
@@ -120,6 +125,16 @@ NullSensor max17048Sensor;
|
|||||||
RAK9154Sensor rak9154Sensor;
|
RAK9154Sensor rak9154Sensor;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAS_PPM
|
||||||
|
// note: XPOWERS_CHIP_XXX must be defined in variant.h
|
||||||
|
#include <XPowersLib.h>
|
||||||
|
XPowersPPM *PPM = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAS_BQ27220
|
||||||
|
#include "bq27220.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAS_PMU
|
#ifdef HAS_PMU
|
||||||
XPowersLibInterface *PMU = NULL;
|
XPowersLibInterface *PMU = NULL;
|
||||||
#else
|
#else
|
||||||
@@ -665,6 +680,10 @@ bool Power::setup()
|
|||||||
found = true;
|
found = true;
|
||||||
} else if (lipoInit()) {
|
} else if (lipoInit()) {
|
||||||
found = true;
|
found = true;
|
||||||
|
} else if (lipoChargerInit()) {
|
||||||
|
found = true;
|
||||||
|
} else if (meshSolarInit()) {
|
||||||
|
found = true;
|
||||||
} else if (analogInit()) {
|
} else if (analogInit()) {
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
@@ -679,9 +698,65 @@ bool Power::setup()
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Power::powerCommandsCheck()
|
||||||
|
{
|
||||||
|
if (rebootAtMsec && millis() > rebootAtMsec) {
|
||||||
|
LOG_INFO("Rebooting");
|
||||||
|
reboot();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shutdownAtMsec && millis() > shutdownAtMsec) {
|
||||||
|
shutdownAtMsec = 0;
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Power::reboot()
|
||||||
|
{
|
||||||
|
notifyReboot.notifyObservers(NULL);
|
||||||
|
#if defined(ARCH_ESP32)
|
||||||
|
ESP.restart();
|
||||||
|
#elif defined(ARCH_NRF52)
|
||||||
|
NVIC_SystemReset();
|
||||||
|
#elif defined(ARCH_RP2040)
|
||||||
|
rp2040.reboot();
|
||||||
|
#elif defined(ARCH_PORTDUINO)
|
||||||
|
deInitApiServer();
|
||||||
|
if (aLinuxInputImpl)
|
||||||
|
aLinuxInputImpl->deInit();
|
||||||
|
SPI.end();
|
||||||
|
Wire.end();
|
||||||
|
Serial1.end();
|
||||||
|
if (screen) {
|
||||||
|
delete screen;
|
||||||
|
screen = nullptr;
|
||||||
|
}
|
||||||
|
LOG_DEBUG("final reboot!");
|
||||||
|
::reboot();
|
||||||
|
#elif defined(ARCH_STM32WL)
|
||||||
|
HAL_NVIC_SystemReset();
|
||||||
|
#else
|
||||||
|
rebootAtMsec = -1;
|
||||||
|
LOG_WARN("FIXME implement reboot for this platform. Note that some settings require a restart to be applied");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void Power::shutdown()
|
void Power::shutdown()
|
||||||
{
|
{
|
||||||
LOG_INFO("Shutting Down");
|
|
||||||
|
#if HAS_SCREEN
|
||||||
|
if (screen) {
|
||||||
|
#ifdef T_DECK_PRO
|
||||||
|
screen->showSimpleBanner("Device is powered off.\nConnect USB to start!", 0); // T-Deck Pro has no power button
|
||||||
|
#else
|
||||||
|
screen->showSimpleBanner("Shutting Down...", 0); // stays on screen
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if !defined(ARCH_STM32WL)
|
||||||
|
playShutdownMelody();
|
||||||
|
#endif
|
||||||
|
nodeDB->saveToDisk();
|
||||||
|
|
||||||
#if defined(ARCH_NRF52) || defined(ARCH_ESP32) || defined(ARCH_RP2040)
|
#if defined(ARCH_NRF52) || defined(ARCH_ESP32) || defined(ARCH_RP2040)
|
||||||
#ifdef PIN_LED1
|
#ifdef PIN_LED1
|
||||||
@@ -693,7 +768,11 @@ void Power::shutdown()
|
|||||||
#ifdef PIN_LED3
|
#ifdef PIN_LED3
|
||||||
ledOff(PIN_LED3);
|
ledOff(PIN_LED3);
|
||||||
#endif
|
#endif
|
||||||
doDeepSleep(DELAY_FOREVER, false, false);
|
doDeepSleep(DELAY_FOREVER, true, true);
|
||||||
|
#elif defined(ARCH_PORTDUINO)
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
#else
|
||||||
|
LOG_WARN("FIXME implement shutdown for this platform");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -754,18 +833,27 @@ void Power::readPowerStatus()
|
|||||||
newStatus.notifyObservers(&powerStatus2);
|
newStatus.notifyObservers(&powerStatus2);
|
||||||
#ifdef DEBUG_HEAP
|
#ifdef DEBUG_HEAP
|
||||||
if (lastheap != memGet.getFreeHeap()) {
|
if (lastheap != memGet.getFreeHeap()) {
|
||||||
std::string threadlist = "Threads running:";
|
// Use stack-allocated buffer to avoid heap allocations in monitoring code
|
||||||
|
char threadlist[256] = "Threads running:";
|
||||||
|
int threadlistLen = strlen(threadlist);
|
||||||
int running = 0;
|
int running = 0;
|
||||||
for (int i = 0; i < MAX_THREADS; i++) {
|
for (int i = 0; i < MAX_THREADS; i++) {
|
||||||
auto thread = concurrency::mainController.get(i);
|
auto thread = concurrency::mainController.get(i);
|
||||||
if ((thread != nullptr) && (thread->enabled)) {
|
if ((thread != nullptr) && (thread->enabled)) {
|
||||||
threadlist += vformat(" %s", thread->ThreadName.c_str());
|
// Use snprintf to safely append to stack buffer without heap allocation
|
||||||
|
int remaining = sizeof(threadlist) - threadlistLen - 1;
|
||||||
|
if (remaining > 0) {
|
||||||
|
int written = snprintf(threadlist + threadlistLen, remaining, " %s", thread->ThreadName.c_str());
|
||||||
|
if (written > 0 && written < remaining) {
|
||||||
|
threadlistLen += written;
|
||||||
|
}
|
||||||
|
}
|
||||||
running++;
|
running++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LOG_DEBUG(threadlist.c_str());
|
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
|
||||||
@@ -777,15 +865,19 @@ void Power::readPowerStatus()
|
|||||||
sprintf(mac, "!%02x%02x%02x%02x", dmac[2], dmac[3], dmac[4], dmac[5]);
|
sprintf(mac, "!%02x%02x%02x%02x", dmac[2], dmac[3], dmac[4], dmac[5]);
|
||||||
|
|
||||||
auto newHeap = memGet.getFreeHeap();
|
auto newHeap = memGet.getFreeHeap();
|
||||||
std::string heapTopic =
|
// Use stack-allocated buffers to avoid heap allocations in monitoring code
|
||||||
(*moduleConfig.mqtt.root ? moduleConfig.mqtt.root : "msh") + std::string("/2/heap/") + std::string(mac);
|
char heapTopic[128];
|
||||||
std::string heapString = std::to_string(newHeap);
|
snprintf(heapTopic, sizeof(heapTopic), "%s/2/heap/%s", (*moduleConfig.mqtt.root ? moduleConfig.mqtt.root : "msh"), mac);
|
||||||
mqtt->pubSub.publish(heapTopic.c_str(), heapString.c_str(), false);
|
char heapString[16];
|
||||||
|
snprintf(heapString, sizeof(heapString), "%u", newHeap);
|
||||||
|
mqtt->pubSub.publish(heapTopic, heapString, false);
|
||||||
|
|
||||||
auto wifiRSSI = WiFi.RSSI();
|
auto wifiRSSI = WiFi.RSSI();
|
||||||
std::string wifiTopic =
|
char wifiTopic[128];
|
||||||
(*moduleConfig.mqtt.root ? moduleConfig.mqtt.root : "msh") + std::string("/2/wifi/") + std::string(mac);
|
snprintf(wifiTopic, sizeof(wifiTopic), "%s/2/wifi/%s", (*moduleConfig.mqtt.root ? moduleConfig.mqtt.root : "msh"), mac);
|
||||||
std::string wifiString = std::to_string(wifiRSSI);
|
char wifiString[16];
|
||||||
mqtt->pubSub.publish(wifiTopic.c_str(), wifiString.c_str(), false);
|
snprintf(wifiString, sizeof(wifiString), "%d", wifiRSSI);
|
||||||
|
mqtt->pubSub.publish(wifiTopic, wifiString, false);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -1237,3 +1329,213 @@ bool Power::lipoInit()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(HAS_PPM) && HAS_PPM
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter class for BQ25896/BQ27220 Lipo battery charger.
|
||||||
|
*/
|
||||||
|
class LipoCharger : public HasBatteryLevel
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
BQ27220 *bq = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Init the I2C BQ25896 Lipo battery charger
|
||||||
|
*/
|
||||||
|
bool runOnce()
|
||||||
|
{
|
||||||
|
if (PPM == nullptr) {
|
||||||
|
PPM = new XPowersPPM;
|
||||||
|
bool result = PPM->init(Wire, I2C_SDA, I2C_SCL, BQ25896_ADDR);
|
||||||
|
if (result) {
|
||||||
|
LOG_INFO("PPM BQ25896 init succeeded");
|
||||||
|
// Set the minimum operating voltage. Below this voltage, the PPM will protect
|
||||||
|
// PPM->setSysPowerDownVoltage(3100);
|
||||||
|
|
||||||
|
// Set input current limit, default is 500mA
|
||||||
|
// PPM->setInputCurrentLimit(800);
|
||||||
|
|
||||||
|
// Disable current limit pin
|
||||||
|
// PPM->disableCurrentLimitPin();
|
||||||
|
|
||||||
|
// Set the charging target voltage, Range:3840 ~ 4608mV ,step:16 mV
|
||||||
|
PPM->setChargeTargetVoltage(4288);
|
||||||
|
|
||||||
|
// Set the precharge current , Range: 64mA ~ 1024mA ,step:64mA
|
||||||
|
// PPM->setPrechargeCurr(64);
|
||||||
|
|
||||||
|
// The premise is that limit pin is disabled, or it will
|
||||||
|
// only follow the maximum charging current set by limit pin.
|
||||||
|
// Set the charging current , Range:0~5056mA ,step:64mA
|
||||||
|
PPM->setChargerConstantCurr(1024);
|
||||||
|
|
||||||
|
// To obtain voltage data, the ADC must be enabled first
|
||||||
|
PPM->enableMeasure();
|
||||||
|
|
||||||
|
// Turn on charging function
|
||||||
|
// If there is no battery connected, do not turn on the charging function
|
||||||
|
PPM->enableCharge();
|
||||||
|
} else {
|
||||||
|
LOG_WARN("PPM BQ25896 init failed");
|
||||||
|
delete PPM;
|
||||||
|
PPM = nullptr;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bq == nullptr) {
|
||||||
|
bq = new BQ27220;
|
||||||
|
bq->setDefaultCapacity(BQ27220_DESIGN_CAPACITY);
|
||||||
|
|
||||||
|
bool result = bq->init();
|
||||||
|
if (result) {
|
||||||
|
LOG_DEBUG("BQ27220 design capacity: %d", bq->getDesignCapacity());
|
||||||
|
LOG_DEBUG("BQ27220 fullCharge capacity: %d", bq->getFullChargeCapacity());
|
||||||
|
LOG_DEBUG("BQ27220 remaining capacity: %d", bq->getRemainingCapacity());
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
LOG_WARN("BQ27220 init failed");
|
||||||
|
delete bq;
|
||||||
|
bq = nullptr;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Battery state of charge, from 0 to 100 or -1 for unknown
|
||||||
|
*/
|
||||||
|
virtual int getBatteryPercent() override
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
// return bq->getChargePercent(); // don't use BQ27220 for battery percent, it is not calibrated
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The raw voltage of the battery in millivolts, or NAN if unknown
|
||||||
|
*/
|
||||||
|
virtual uint16_t getBattVoltage() override { return bq->getVoltage(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return true if there is a battery installed in this unit
|
||||||
|
*/
|
||||||
|
virtual bool isBatteryConnect() override { return PPM->getBattVoltage() > 0; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return true if there is an external power source detected
|
||||||
|
*/
|
||||||
|
virtual bool isVbusIn() override { return PPM->getVbusVoltage() > 0; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return true if the battery is currently charging
|
||||||
|
*/
|
||||||
|
virtual bool isCharging() override
|
||||||
|
{
|
||||||
|
bool isCharging = PPM->isCharging();
|
||||||
|
if (isCharging) {
|
||||||
|
LOG_DEBUG("BQ27220 time to full charge: %d min", bq->getTimeToFull());
|
||||||
|
} else {
|
||||||
|
if (!PPM->isVbusIn()) {
|
||||||
|
LOG_DEBUG("BQ27220 time to empty: %d min (%d mAh)", bq->getTimeToEmpty(), bq->getRemainingCapacity());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isCharging;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
LipoCharger lipoCharger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init the Lipo battery charger
|
||||||
|
*/
|
||||||
|
bool Power::lipoChargerInit()
|
||||||
|
{
|
||||||
|
bool result = lipoCharger.runOnce();
|
||||||
|
LOG_DEBUG("Power::lipoChargerInit lipo sensor is %s", result ? "ready" : "not ready yet");
|
||||||
|
if (!result)
|
||||||
|
return false;
|
||||||
|
batteryLevel = &lipoCharger;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
/**
|
||||||
|
* The Lipo battery level sensor is unavailable - default to AnalogBatteryLevel
|
||||||
|
*/
|
||||||
|
bool Power::lipoChargerInit()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HELTEC_MESH_SOLAR
|
||||||
|
#include "meshSolarApp.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* meshSolar class for an SMBUS battery sensor.
|
||||||
|
*/
|
||||||
|
class meshSolarBatteryLevel : public HasBatteryLevel
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Init the I2C meshSolar battery level sensor
|
||||||
|
*/
|
||||||
|
bool runOnce()
|
||||||
|
{
|
||||||
|
meshSolarStart();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Battery state of charge, from 0 to 100 or -1 for unknown
|
||||||
|
*/
|
||||||
|
virtual int getBatteryPercent() override { return meshSolarGetBatteryPercent(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The raw voltage of the battery in millivolts, or NAN if unknown
|
||||||
|
*/
|
||||||
|
virtual uint16_t getBattVoltage() override { return meshSolarGetBattVoltage(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return true if there is a battery installed in this unit
|
||||||
|
*/
|
||||||
|
virtual bool isBatteryConnect() override { return meshSolarIsBatteryConnect(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return true if there is an external power source detected
|
||||||
|
*/
|
||||||
|
virtual bool isVbusIn() override { return meshSolarIsVbusIn(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return true if the battery is currently charging
|
||||||
|
*/
|
||||||
|
virtual bool isCharging() override { return meshSolarIsCharging(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
meshSolarBatteryLevel meshSolarLevel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init the meshSolar battery level sensor
|
||||||
|
*/
|
||||||
|
bool Power::meshSolarInit()
|
||||||
|
{
|
||||||
|
bool result = meshSolarLevel.runOnce();
|
||||||
|
LOG_DEBUG("Power::meshSolarInit mesh solar sensor is %s", result ? "ready" : "not ready yet");
|
||||||
|
if (!result)
|
||||||
|
return false;
|
||||||
|
batteryLevel = &meshSolarLevel;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
/**
|
||||||
|
* The meshSolar battery level sensor is unavailable - default to AnalogBatteryLevel
|
||||||
|
*/
|
||||||
|
bool Power::meshSolarInit()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
+1
-1
@@ -72,7 +72,7 @@ extern Power *power;
|
|||||||
static void shutdownEnter()
|
static void shutdownEnter()
|
||||||
{
|
{
|
||||||
LOG_DEBUG("State: SHUTDOWN");
|
LOG_DEBUG("State: SHUTDOWN");
|
||||||
power->shutdown();
|
shutdownAtMsec = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
|
|||||||
@@ -64,6 +64,14 @@ SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port), con
|
|||||||
|
|
||||||
int32_t SerialConsole::runOnce()
|
int32_t SerialConsole::runOnce()
|
||||||
{
|
{
|
||||||
|
#ifdef HELTEC_MESH_SOLAR
|
||||||
|
//After enabling the mesh solar serial port module configuration, command processing is handled by the serial port module.
|
||||||
|
if(moduleConfig.serial.enabled && moduleConfig.serial.override_console_serial_port
|
||||||
|
&& moduleConfig.serial.mode==meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MS_CONFIG)
|
||||||
|
{
|
||||||
|
return 250;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return runOncePart();
|
return runOncePart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,10 +47,6 @@ int BuzzerFeedbackThread::handleInputEvent(const InputEvent *event)
|
|||||||
playComboTune(); // Ping sent feedback
|
playComboTune(); // Ping sent feedback
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INPUT_BROKER_SHUTDOWN:
|
|
||||||
playShutdownMelody(); // Shutdown feedback
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// For other events, check if it's a printable character
|
// For other events, check if it's a printable character
|
||||||
if (event->kbchar >= 32 && event->kbchar <= 126) {
|
if (event->kbchar >= 32 && event->kbchar <= 126) {
|
||||||
@@ -69,10 +65,7 @@ int32_t BuzzerFeedbackThread::runOnce()
|
|||||||
// This thread is primarily event-driven, but we can use runOnce
|
// This thread is primarily event-driven, but we can use runOnce
|
||||||
// for any periodic tasks if needed in the future
|
// for any periodic tasks if needed in the future
|
||||||
|
|
||||||
if (needsUpdate) {
|
needsUpdate = false;
|
||||||
needsUpdate = false;
|
|
||||||
// Could add any periodic processing here
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run every 100ms when active, less frequently when idle
|
// Run every 100ms when active, less frequently when idle
|
||||||
return needsUpdate ? 100 : 1000;
|
return needsUpdate ? 100 : 1000;
|
||||||
|
|||||||
@@ -140,6 +140,10 @@ bool playNextLeadUpNote()
|
|||||||
playTones(¬e, 1); // Play single note using existing playTones function
|
playTones(¬e, 1); // Play single note using existing playTones function
|
||||||
|
|
||||||
leadUpNoteIndex++;
|
leadUpNoteIndex++;
|
||||||
|
|
||||||
|
if (leadUpNoteIndex >= leadUpNotesCount) {
|
||||||
|
return false; // this was the final note
|
||||||
|
}
|
||||||
return true; // Note was played (playTones handles buzzer availability internally)
|
return true; // Note was played (playTones handles buzzer availability internally)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
+11
-5
@@ -26,10 +26,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
#ifdef RV3028_RTC
|
#if __has_include("Melopero_RV3028.h")
|
||||||
#include "Melopero_RV3028.h"
|
#include "Melopero_RV3028.h"
|
||||||
#endif
|
#endif
|
||||||
#ifdef PCF8563_RTC
|
#if __has_include("pcf8563.h")
|
||||||
#include "pcf8563.h"
|
#include "pcf8563.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -135,7 +135,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// OLED & Input
|
// OLED & Input
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
#if defined(SEEED_WIO_TRACKER_L1)
|
#if defined(SEEED_WIO_TRACKER_L1) && !defined(SEEED_WIO_TRACKER_L1_EINK)
|
||||||
#define SSD1306_ADDRESS 0x3D
|
#define SSD1306_ADDRESS 0x3D
|
||||||
#define USE_SH1106
|
#define USE_SH1106
|
||||||
#else
|
#else
|
||||||
@@ -150,11 +150,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
// Define if screen should be mirrored left to right
|
// Define if screen should be mirrored left to right
|
||||||
// #define SCREEN_MIRROR
|
// #define SCREEN_MIRROR
|
||||||
|
|
||||||
// I2C Keyboards (M5Stack, RAK14004, T-Deck)
|
// I2C Keyboards (M5Stack, RAK14004, T-Deck, T-Deck Pro, T-Lora Pager, CardKB, BBQ10, MPR121, TCA8418)
|
||||||
#define CARDKB_ADDR 0x5F
|
#define CARDKB_ADDR 0x5F
|
||||||
#define TDECK_KB_ADDR 0x55
|
#define TDECK_KB_ADDR 0x55
|
||||||
#define BBQ10_KB_ADDR 0x1F
|
#define BBQ10_KB_ADDR 0x1F
|
||||||
#define MPR121_KB_ADDR 0x5A
|
#define MPR121_KB_ADDR 0x5A
|
||||||
|
#define TCA8418_KB_ADDR 0x34
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// SENSOR
|
// SENSOR
|
||||||
@@ -193,8 +194,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#define MLX90614_ADDR_DEF 0x5A
|
#define MLX90614_ADDR_DEF 0x5A
|
||||||
#define CGRADSENS_ADDR 0x66
|
#define CGRADSENS_ADDR 0x66
|
||||||
#define LTR390UV_ADDR 0x53
|
#define LTR390UV_ADDR 0x53
|
||||||
#define XPOWERS_AXP192_AXP2101_ADDRESS 0x34 // same adress as TCA8418
|
#define XPOWERS_AXP192_AXP2101_ADDRESS 0x34 // same adress as TCA8418_KB
|
||||||
#define PCT2075_ADDR 0x37
|
#define PCT2075_ADDR 0x37
|
||||||
|
#define BQ27220_ADDR 0x55 // same address as TDECK_KB
|
||||||
|
#define BQ25896_ADDR 0x6B
|
||||||
|
#define LTR553ALS_ADDR 0x23
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// ACCELEROMETER
|
// ACCELEROMETER
|
||||||
@@ -208,6 +212,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#define BMX160_ADDR 0x69
|
#define BMX160_ADDR 0x69
|
||||||
#define ICM20948_ADDR 0x69
|
#define ICM20948_ADDR 0x69
|
||||||
#define ICM20948_ADDR_ALT 0x68
|
#define ICM20948_ADDR_ALT 0x68
|
||||||
|
#define BHI260AP_ADDR 0x28
|
||||||
#define BMM150_ADDR 0x13
|
#define BMM150_ADDR 0x13
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@@ -230,6 +235,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
// Touchscreen
|
// Touchscreen
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
#define FT6336U_ADDR 0x48
|
#define FT6336U_ADDR 0x48
|
||||||
|
#define CST328_ADDR 0x1A
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// RAK12035VB Soil Monitor (using RAK12023 up to 3 RAK12035 monitors can be connected)
|
// RAK12035VB Soil Monitor (using RAK12023 up to 3 RAK12035 monitors can be connected)
|
||||||
|
|||||||
@@ -74,7 +74,14 @@ class ScanI2C
|
|||||||
RAK12035,
|
RAK12035,
|
||||||
TCA8418KB,
|
TCA8418KB,
|
||||||
PCT2075,
|
PCT2075,
|
||||||
|
CST328,
|
||||||
|
BQ25896,
|
||||||
|
BQ27220,
|
||||||
|
LTR553ALS,
|
||||||
|
BHI260AP,
|
||||||
BMM150,
|
BMM150,
|
||||||
|
TSL2561,
|
||||||
|
DRV2605
|
||||||
} DeviceType;
|
} DeviceType;
|
||||||
|
|
||||||
// typedef uint8_t DeviceAddress;
|
// typedef uint8_t DeviceAddress;
|
||||||
|
|||||||
@@ -184,8 +184,13 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
|||||||
type = RTC_RV3028;
|
type = RTC_RV3028;
|
||||||
logFoundDevice("RV3028", (uint8_t)addr.address);
|
logFoundDevice("RV3028", (uint8_t)addr.address);
|
||||||
rtc.initI2C(*i2cBus);
|
rtc.initI2C(*i2cBus);
|
||||||
rtc.writeToRegister(0x35, 0x07); // no Clkout
|
// Update RTC EEPROM settings, if necessary
|
||||||
rtc.writeToRegister(0x37, 0xB4);
|
if (rtc.readEEPROMRegister(0x35) != 0x07) {
|
||||||
|
rtc.writeEEPROMRegister(0x35, 0x07); // no Clkout
|
||||||
|
}
|
||||||
|
if (rtc.readEEPROMRegister(0x37) != 0xB4) {
|
||||||
|
rtc.writeEEPROMRegister(0x37, 0xB4);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -206,7 +211,17 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
SCAN_SIMPLE_CASE(TDECK_KB_ADDR, TDECKKB, "T-Deck keyboard", (uint8_t)addr.address);
|
case TDECK_KB_ADDR:
|
||||||
|
// Do we have the T-Deck keyboard or the T-Deck Pro battery sensor?
|
||||||
|
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x04), 1);
|
||||||
|
if (registerValue != 0) {
|
||||||
|
logFoundDevice("BQ27220", (uint8_t)addr.address);
|
||||||
|
type = BQ27220;
|
||||||
|
} else {
|
||||||
|
logFoundDevice("TDECKKB", (uint8_t)addr.address);
|
||||||
|
type = TDECKKB;
|
||||||
|
}
|
||||||
|
break;
|
||||||
SCAN_SIMPLE_CASE(BBQ10_KB_ADDR, BBQ10KB, "BB Q10", (uint8_t)addr.address);
|
SCAN_SIMPLE_CASE(BBQ10_KB_ADDR, BBQ10KB, "BB Q10", (uint8_t)addr.address);
|
||||||
|
|
||||||
SCAN_SIMPLE_CASE(ST7567_ADDRESS, SCREEN_ST7567, "ST7567", (uint8_t)addr.address);
|
SCAN_SIMPLE_CASE(ST7567_ADDRESS, SCREEN_ST7567, "ST7567", (uint8_t)addr.address);
|
||||||
@@ -279,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:
|
||||||
@@ -325,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.
|
||||||
@@ -396,6 +413,12 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
|||||||
logFoundDevice("BQ24295", (uint8_t)addr.address);
|
logFoundDevice("BQ24295", (uint8_t)addr.address);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x14), 1); // get ID
|
||||||
|
if ((registerValue & 0b00000011) == 0b00000010) {
|
||||||
|
type = BQ25896;
|
||||||
|
logFoundDevice("BQ25896", (uint8_t)addr.address);
|
||||||
|
break;
|
||||||
|
}
|
||||||
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0F), 1); // get ID
|
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0F), 1); // get ID
|
||||||
if (registerValue == 0x6A) {
|
if (registerValue == 0x6A) {
|
||||||
type = LSM6DS3;
|
type = LSM6DS3;
|
||||||
@@ -440,13 +463,26 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
|||||||
SCAN_SIMPLE_CASE(LSM6DS3_ADDR, LSM6DS3, "LSM6DS3", (uint8_t)addr.address);
|
SCAN_SIMPLE_CASE(LSM6DS3_ADDR, LSM6DS3, "LSM6DS3", (uint8_t)addr.address);
|
||||||
SCAN_SIMPLE_CASE(TCA9555_ADDR, TCA9555, "TCA9555", (uint8_t)addr.address);
|
SCAN_SIMPLE_CASE(TCA9555_ADDR, TCA9555, "TCA9555", (uint8_t)addr.address);
|
||||||
SCAN_SIMPLE_CASE(VEML7700_ADDR, VEML7700, "VEML7700", (uint8_t)addr.address);
|
SCAN_SIMPLE_CASE(VEML7700_ADDR, VEML7700, "VEML7700", (uint8_t)addr.address);
|
||||||
SCAN_SIMPLE_CASE(TSL25911_ADDR, TSL2591, "TSL2591", (uint8_t)addr.address);
|
case TSL25911_ADDR:
|
||||||
|
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x12), 1);
|
||||||
|
if (registerValue == 0x50) {
|
||||||
|
type = TSL2591;
|
||||||
|
logFoundDevice("TSL25911", (uint8_t)addr.address);
|
||||||
|
} else {
|
||||||
|
type = TSL2561;
|
||||||
|
logFoundDevice("TSL2561", (uint8_t)addr.address);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
SCAN_SIMPLE_CASE(MLX90632_ADDR, MLX90632, "MLX90632", (uint8_t)addr.address);
|
SCAN_SIMPLE_CASE(MLX90632_ADDR, MLX90632, "MLX90632", (uint8_t)addr.address);
|
||||||
SCAN_SIMPLE_CASE(NAU7802_ADDR, NAU7802, "NAU7802", (uint8_t)addr.address);
|
SCAN_SIMPLE_CASE(NAU7802_ADDR, NAU7802, "NAU7802", (uint8_t)addr.address);
|
||||||
SCAN_SIMPLE_CASE(MAX1704X_ADDR, MAX17048, "MAX17048", (uint8_t)addr.address);
|
SCAN_SIMPLE_CASE(MAX1704X_ADDR, MAX17048, "MAX17048", (uint8_t)addr.address);
|
||||||
SCAN_SIMPLE_CASE(DFROBOT_RAIN_ADDR, DFROBOT_RAIN, "DFRobot Rain Gauge", (uint8_t)addr.address);
|
SCAN_SIMPLE_CASE(DFROBOT_RAIN_ADDR, DFROBOT_RAIN, "DFRobot Rain Gauge", (uint8_t)addr.address);
|
||||||
SCAN_SIMPLE_CASE(LTR390UV_ADDR, LTR390UV, "LTR390UV", (uint8_t)addr.address);
|
SCAN_SIMPLE_CASE(LTR390UV_ADDR, LTR390UV, "LTR390UV", (uint8_t)addr.address);
|
||||||
SCAN_SIMPLE_CASE(PCT2075_ADDR, PCT2075, "PCT2075", (uint8_t)addr.address);
|
SCAN_SIMPLE_CASE(PCT2075_ADDR, PCT2075, "PCT2075", (uint8_t)addr.address);
|
||||||
|
SCAN_SIMPLE_CASE(CST328_ADDR, CST328, "CST328", (uint8_t)addr.address);
|
||||||
|
SCAN_SIMPLE_CASE(LTR553ALS_ADDR, LTR553ALS, "LTR553ALS", (uint8_t)addr.address);
|
||||||
|
SCAN_SIMPLE_CASE(BHI260AP_ADDR, BHI260AP, "BHI260AP", (uint8_t)addr.address);
|
||||||
SCAN_SIMPLE_CASE(SCD4X_ADDR, SCD4X, "SCD4X", (uint8_t)addr.address);
|
SCAN_SIMPLE_CASE(SCD4X_ADDR, SCD4X, "SCD4X", (uint8_t)addr.address);
|
||||||
SCAN_SIMPLE_CASE(BMM150_ADDR, BMM150, "BMM150", (uint8_t)addr.address);
|
SCAN_SIMPLE_CASE(BMM150_ADDR, BMM150, "BMM150", (uint8_t)addr.address);
|
||||||
#ifdef HAS_TPS65233
|
#ifdef HAS_TPS65233
|
||||||
@@ -459,8 +495,14 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
|||||||
type = MLX90614;
|
type = MLX90614;
|
||||||
logFoundDevice("MLX90614", (uint8_t)addr.address);
|
logFoundDevice("MLX90614", (uint8_t)addr.address);
|
||||||
} else {
|
} else {
|
||||||
type = MPR121KB;
|
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1); // DRV2605_REG_STATUS
|
||||||
logFoundDevice("MPR121KB", (uint8_t)addr.address);
|
if (registerValue == 0xe0) {
|
||||||
|
type = DRV2605;
|
||||||
|
logFoundDevice("DRV2605", (uint8_t)addr.address);
|
||||||
|
} else {
|
||||||
|
type = MPR121KB;
|
||||||
|
logFoundDevice("MPR121KB", (uint8_t)addr.address);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
+73
-76
@@ -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"
|
||||||
@@ -39,9 +38,9 @@ template <typename T, std::size_t N> std::size_t array_count(const T (&)[N])
|
|||||||
return N;
|
return N;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)
|
#if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) || defined(ARCH_STM32WL)
|
||||||
#if defined(RAK2560)
|
#if defined(GPS_SERIAL_PORT)
|
||||||
HardwareSerial *GPS::_serial_gps = &Serial2;
|
HardwareSerial *GPS::_serial_gps = &GPS_SERIAL_PORT;
|
||||||
#else
|
#else
|
||||||
HardwareSerial *GPS::_serial_gps = &Serial1;
|
HardwareSerial *GPS::_serial_gps = &Serial1;
|
||||||
#endif
|
#endif
|
||||||
@@ -643,8 +642,16 @@ bool GPS::setup()
|
|||||||
delay(250);
|
delay(250);
|
||||||
} else if (IS_ONE_OF(gnssModel, GNSS_MODEL_AG3335, GNSS_MODEL_AG3352)) {
|
} else if (IS_ONE_OF(gnssModel, GNSS_MODEL_AG3335, GNSS_MODEL_AG3352)) {
|
||||||
|
|
||||||
_serial_gps->write("$PAIR066,1,0,1,0,0,1*3B\r\n"); // Enable GPS+GALILEO+NAVIC
|
if (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_IN ||
|
||||||
|
config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_NP_865) {
|
||||||
|
_serial_gps->write("$PAIR066,1,0,1,0,0,1*3B\r\n"); // Enable GPS+GALILEO+NAVIC
|
||||||
|
// GPS GLONASS GALILEO BDS QZSS NAVIC
|
||||||
|
// 1 0 1 0 0 1
|
||||||
|
} else {
|
||||||
|
_serial_gps->write("$PAIR066,1,1,1,1,0,0*3A\r\n"); // Enable GPS+GLONASS+GALILEO+BDS
|
||||||
|
// GPS GLONASS GALILEO BDS QZSS NAVIC
|
||||||
|
// 1 1 1 1 0 0
|
||||||
|
}
|
||||||
// Configure NMEA (sentences will output once per fix)
|
// Configure NMEA (sentences will output once per fix)
|
||||||
_serial_gps->write("$PAIR062,0,1*3F\r\n"); // GGA ON
|
_serial_gps->write("$PAIR062,0,1*3F\r\n"); // GGA ON
|
||||||
_serial_gps->write("$PAIR062,1,0*3F\r\n"); // GLL OFF
|
_serial_gps->write("$PAIR062,1,0*3F\r\n"); // GLL OFF
|
||||||
@@ -835,9 +842,6 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime)
|
|||||||
setPowerPMU(true); // Power (PMU): on
|
setPowerPMU(true); // Power (PMU): on
|
||||||
writePinStandby(false); // Standby (pin): awake (not standby)
|
writePinStandby(false); // Standby (pin): awake (not standby)
|
||||||
setPowerUBLOX(true); // Standby (UBLOX): awake
|
setPowerUBLOX(true); // Standby (UBLOX): awake
|
||||||
#ifdef GNSS_AIROHA
|
|
||||||
lastFixStartMsec = 0;
|
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GPS_SOFTSLEEP:
|
case GPS_SOFTSLEEP:
|
||||||
@@ -855,9 +859,7 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime)
|
|||||||
writePinStandby(true); // Standby (pin): asleep (not awake)
|
writePinStandby(true); // Standby (pin): asleep (not awake)
|
||||||
setPowerUBLOX(false, sleepTime); // Standby (UBLOX): asleep, timed
|
setPowerUBLOX(false, sleepTime); // Standby (UBLOX): asleep, timed
|
||||||
#ifdef GNSS_AIROHA
|
#ifdef GNSS_AIROHA
|
||||||
if (config.position.gps_update_interval * 1000 >= GPS_FIX_HOLD_TIME * 2) {
|
digitalWrite(PIN_GPS_EN, LOW);
|
||||||
digitalWrite(PIN_GPS_EN, LOW);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -869,9 +871,7 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime)
|
|||||||
writePinStandby(true); // Standby (pin): asleep
|
writePinStandby(true); // Standby (pin): asleep
|
||||||
setPowerUBLOX(false, 0); // Standby (UBLOX): asleep, indefinitely
|
setPowerUBLOX(false, 0); // Standby (UBLOX): asleep, indefinitely
|
||||||
#ifdef GNSS_AIROHA
|
#ifdef GNSS_AIROHA
|
||||||
if (config.position.gps_update_interval * 1000 >= GPS_FIX_HOLD_TIME * 2) {
|
digitalWrite(PIN_GPS_EN, LOW);
|
||||||
digitalWrite(PIN_GPS_EN, LOW);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1054,6 +1054,8 @@ void GPS::down()
|
|||||||
}
|
}
|
||||||
// If update interval long enough (or softsleep unsupported): hardsleep instead
|
// If update interval long enough (or softsleep unsupported): hardsleep instead
|
||||||
setPowerState(GPS_HARDSLEEP, sleepTime);
|
setPowerState(GPS_HARDSLEEP, sleepTime);
|
||||||
|
// Reset the fix quality to 0, since we're off.
|
||||||
|
fixQual = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1113,11 +1115,19 @@ int32_t GPS::runOnce()
|
|||||||
shouldPublish = true;
|
shouldPublish = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t prev_fixQual = fixQual;
|
||||||
bool gotLoc = lookForLocation();
|
bool gotLoc = lookForLocation();
|
||||||
if (gotLoc && !hasValidLocation) { // declare that we have location ASAP
|
if (gotLoc && !hasValidLocation) { // declare that we have location ASAP
|
||||||
LOG_DEBUG("hasValidLocation RISING EDGE");
|
LOG_DEBUG("hasValidLocation RISING EDGE");
|
||||||
hasValidLocation = true;
|
hasValidLocation = true;
|
||||||
shouldPublish = true;
|
shouldPublish = true;
|
||||||
|
// Hold for 20secs after getting a lock to download ephemeris etc
|
||||||
|
fixHoldEnds = millis() + 20000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gotLoc && prev_fixQual == 0) { // just got a lock after turning back on.
|
||||||
|
fixHoldEnds = millis() + 20000;
|
||||||
|
shouldPublish = true; // Publish immediately, since next publish is at end of hold
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tooLong = scheduling.searchedTooLong();
|
bool tooLong = scheduling.searchedTooLong();
|
||||||
@@ -1126,8 +1136,7 @@ int32_t GPS::runOnce()
|
|||||||
|
|
||||||
// Once we get a location we no longer desperately want an update
|
// Once we get a location we no longer desperately want an update
|
||||||
if ((gotLoc && gotTime) || tooLong) {
|
if ((gotLoc && gotTime) || tooLong) {
|
||||||
|
if (tooLong && !gotLoc) {
|
||||||
if (tooLong) {
|
|
||||||
// we didn't get a location during this ack window, therefore declare loss of lock
|
// we didn't get a location during this ack window, therefore declare loss of lock
|
||||||
if (hasValidLocation) {
|
if (hasValidLocation) {
|
||||||
LOG_DEBUG("hasValidLocation FALLING EDGE");
|
LOG_DEBUG("hasValidLocation FALLING EDGE");
|
||||||
@@ -1135,9 +1144,15 @@ int32_t GPS::runOnce()
|
|||||||
p = meshtastic_Position_init_default;
|
p = meshtastic_Position_init_default;
|
||||||
hasValidLocation = false;
|
hasValidLocation = false;
|
||||||
}
|
}
|
||||||
|
if (millis() > fixHoldEnds) {
|
||||||
down();
|
shouldPublish = true; // publish our update at the end of the lock hold
|
||||||
shouldPublish = true; // publish our update for this just finished acquisition window
|
publishUpdate();
|
||||||
|
down();
|
||||||
|
#ifdef GPS_DEBUG
|
||||||
|
} else {
|
||||||
|
LOG_DEBUG("Holding for GPS data download: %d ms (numSats=%d)", fixHoldEnds - millis(), p.sats_in_view);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If state has changed do a publish
|
// If state has changed do a publish
|
||||||
@@ -1190,7 +1205,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; \
|
||||||
} \
|
} \
|
||||||
@@ -1352,36 +1367,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()
|
||||||
@@ -1496,28 +1530,10 @@ static int32_t toDegInt(RawDegrees d)
|
|||||||
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
|
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
|
||||||
* Override this method to check for new locations
|
* Override this method to check for new locations
|
||||||
*
|
*
|
||||||
* @return true if we've acquired a new location
|
* @return true if we've set a new time
|
||||||
*/
|
*/
|
||||||
bool GPS::lookForTime()
|
bool GPS::lookForTime()
|
||||||
{
|
{
|
||||||
|
|
||||||
#ifdef GNSS_AIROHA
|
|
||||||
uint8_t fix = reader.fixQuality();
|
|
||||||
if (fix > 0) {
|
|
||||||
if (lastFixStartMsec > 0) {
|
|
||||||
if (Throttle::isWithinTimespanMs(lastFixStartMsec, GPS_FIX_HOLD_TIME)) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
clearBuffer();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
lastFixStartMsec = millis();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
auto ti = reader.time;
|
auto ti = reader.time;
|
||||||
auto d = reader.date;
|
auto d = reader.date;
|
||||||
if (ti.isValid() && d.isValid()) { // Note: we don't check for updated, because we'll only be called if needed
|
if (ti.isValid() && d.isValid()) { // Note: we don't check for updated, because we'll only be called if needed
|
||||||
@@ -1534,13 +1550,13 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
|
|||||||
t.tm_year = d.year() - 1900;
|
t.tm_year = d.year() - 1900;
|
||||||
t.tm_isdst = false;
|
t.tm_isdst = false;
|
||||||
if (t.tm_mon > -1) {
|
if (t.tm_mon > -1) {
|
||||||
LOG_DEBUG("NMEA GPS time %02d-%02d-%02d %02d:%02d:%02d age %d", d.year(), d.month(), t.tm_mday, t.tm_hour, t.tm_min,
|
if (perhapsSetRTC(RTCQualityGPS, t) == RTCSetResultSuccess) {
|
||||||
t.tm_sec, ti.age());
|
LOG_DEBUG("NMEA GPS time set %02d-%02d-%02d %02d:%02d:%02d age %d", d.year(), d.month(), t.tm_mday, t.tm_hour,
|
||||||
if (perhapsSetRTC(RTCQualityGPS, t) == RTCSetResultInvalidTime) {
|
t.tm_min, t.tm_sec, ti.age());
|
||||||
// Clear the GPS buffer if we got an invalid time
|
return true;
|
||||||
clearBuffer();
|
} else {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
} else
|
} else
|
||||||
return false;
|
return false;
|
||||||
} else
|
} else
|
||||||
@@ -1555,25 +1571,6 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
|
|||||||
*/
|
*/
|
||||||
bool GPS::lookForLocation()
|
bool GPS::lookForLocation()
|
||||||
{
|
{
|
||||||
#ifdef GNSS_AIROHA
|
|
||||||
if ((config.position.gps_update_interval * 1000) >= (GPS_FIX_HOLD_TIME * 2)) {
|
|
||||||
uint8_t fix = reader.fixQuality();
|
|
||||||
if (fix > 0) {
|
|
||||||
if (lastFixStartMsec > 0) {
|
|
||||||
if (Throttle::isWithinTimespanMs(lastFixStartMsec, GPS_FIX_HOLD_TIME)) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
clearBuffer();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
lastFixStartMsec = millis();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// By default, TinyGPS++ does not parse GPGSA lines, which give us
|
// By default, TinyGPS++ does not parse GPGSA lines, which give us
|
||||||
// the 2D/3D fixType (see NMEAGPS.h)
|
// the 2D/3D fixType (see NMEAGPS.h)
|
||||||
// At a minimum, use the fixQuality indicator in GPGGA (FIXME?)
|
// At a minimum, use the fixQuality indicator in GPGGA (FIXME?)
|
||||||
|
|||||||
+2
-2
@@ -159,7 +159,7 @@ class GPS : private concurrency::OSThread
|
|||||||
uint8_t fixType = 0; // fix type from GPGSA
|
uint8_t fixType = 0; // fix type from GPGSA
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0, lastFixStartMsec = 0;
|
uint32_t fixHoldEnds = 0;
|
||||||
uint32_t rx_gpio = 0;
|
uint32_t rx_gpio = 0;
|
||||||
uint32_t tx_gpio = 0;
|
uint32_t tx_gpio = 0;
|
||||||
|
|
||||||
@@ -236,7 +236,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);
|
||||||
|
|||||||
+102
-17
@@ -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;
|
||||||
@@ -23,7 +26,7 @@ static uint64_t zeroOffsetSecs; // GPS based time in secs since 1970 - only upda
|
|||||||
* Reads the current date and time from the RTC module and updates the system time.
|
* Reads the current date and time from the RTC module and updates the system time.
|
||||||
* @return True if the RTC was successfully read and the system time was updated, false otherwise.
|
* @return True if the RTC was successfully read and the system time was updated, false otherwise.
|
||||||
*/
|
*/
|
||||||
void readFromRTC()
|
RTCSetResult readFromRTC()
|
||||||
{
|
{
|
||||||
struct timeval tv; /* btw settimeofday() is helpful here too*/
|
struct timeval tv; /* btw settimeofday() is helpful here too*/
|
||||||
#ifdef RV3028_RTC
|
#ifdef RV3028_RTC
|
||||||
@@ -44,15 +47,25 @@ void readFromRTC()
|
|||||||
t.tm_sec = rtc.getSecond();
|
t.tm_sec = rtc.getSecond();
|
||||||
tv.tv_sec = gm_mktime(&t);
|
tv.tv_sec = gm_mktime(&t);
|
||||||
tv.tv_usec = 0;
|
tv.tv_usec = 0;
|
||||||
|
|
||||||
uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms
|
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
|
||||||
|
if (tv.tv_sec < 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;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
LOG_DEBUG("Read RTC time from RV3028 getTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", t.tm_year + 1900, t.tm_mon + 1,
|
LOG_DEBUG("Read RTC time from RV3028 getTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", t.tm_year + 1900, t.tm_mon + 1,
|
||||||
t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch);
|
t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch);
|
||||||
timeStartMsec = now;
|
|
||||||
zeroOffsetSecs = tv.tv_sec;
|
|
||||||
if (currentQuality == RTCQualityNone) {
|
if (currentQuality == RTCQualityNone) {
|
||||||
|
timeStartMsec = now;
|
||||||
|
zeroOffsetSecs = tv.tv_sec;
|
||||||
currentQuality = RTCQualityDevice;
|
currentQuality = RTCQualityDevice;
|
||||||
}
|
}
|
||||||
|
return RTCSetResultSuccess;
|
||||||
}
|
}
|
||||||
#elif defined(PCF8563_RTC)
|
#elif defined(PCF8563_RTC)
|
||||||
if (rtc_found.address == PCF8563_RTC) {
|
if (rtc_found.address == PCF8563_RTC) {
|
||||||
@@ -75,15 +88,26 @@ void readFromRTC()
|
|||||||
t.tm_sec = tc.second;
|
t.tm_sec = tc.second;
|
||||||
tv.tv_sec = gm_mktime(&t);
|
tv.tv_sec = gm_mktime(&t);
|
||||||
tv.tv_usec = 0;
|
tv.tv_usec = 0;
|
||||||
|
|
||||||
uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms
|
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
|
||||||
|
if (tv.tv_sec < 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;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
LOG_DEBUG("Read RTC time from PCF8563 getDateTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", t.tm_year + 1900, t.tm_mon + 1,
|
LOG_DEBUG("Read RTC time from PCF8563 getDateTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", t.tm_year + 1900, t.tm_mon + 1,
|
||||||
t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch);
|
t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch);
|
||||||
timeStartMsec = now;
|
|
||||||
zeroOffsetSecs = tv.tv_sec;
|
|
||||||
if (currentQuality == RTCQualityNone) {
|
if (currentQuality == RTCQualityNone) {
|
||||||
|
timeStartMsec = now;
|
||||||
|
zeroOffsetSecs = tv.tv_sec;
|
||||||
currentQuality = RTCQualityDevice;
|
currentQuality = RTCQualityDevice;
|
||||||
}
|
}
|
||||||
|
return RTCSetResultSuccess;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if (!gettimeofday(&tv, NULL)) {
|
if (!gettimeofday(&tv, NULL)) {
|
||||||
@@ -92,8 +116,10 @@ void readFromRTC()
|
|||||||
LOG_DEBUG("Read RTC time as %ld", printableEpoch);
|
LOG_DEBUG("Read RTC time as %ld", printableEpoch);
|
||||||
timeStartMsec = now;
|
timeStartMsec = now;
|
||||||
zeroOffsetSecs = tv.tv_sec;
|
zeroOffsetSecs = tv.tv_sec;
|
||||||
|
return RTCSetResultSuccess;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
return RTCSetResultNotSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -101,7 +127,7 @@ void readFromRTC()
|
|||||||
*
|
*
|
||||||
* @param q The quality of the provided time.
|
* @param q The quality of the provided time.
|
||||||
* @param tv A pointer to a timeval struct containing the time to potentially set the RTC to.
|
* @param tv A pointer to a timeval struct containing the time to potentially set the RTC to.
|
||||||
* @return True if the RTC was set, false otherwise.
|
* @return RTCSetResult
|
||||||
*
|
*
|
||||||
* If we haven't yet set our RTC this boot, set it from a GPS derived time
|
* If we haven't yet set our RTC this boot, set it from a GPS derived time
|
||||||
*/
|
*/
|
||||||
@@ -112,7 +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;
|
||||||
|
} else if ((uint64_t)tv->tv_sec > ((uint64_t)BUILD_EPOCH + FORTY_YEARS)) {
|
||||||
|
if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) {
|
||||||
|
// 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
|
||||||
@@ -226,7 +265,27 @@ RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t)
|
|||||||
time_t res = gm_mktime(&t);
|
time_t res = gm_mktime(&t);
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
tv.tv_sec = res;
|
tv.tv_sec = res;
|
||||||
tv.tv_usec = 0; // time.centisecond() * (10 / 1000);
|
tv.tv_usec = 0; // time.centisecond() * (10 / 1000);
|
||||||
|
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
|
||||||
|
if (tv.tv_sec < 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;
|
||||||
|
} else if ((uint64_t)tv.tv_sec > ((uint64_t)BUILD_EPOCH + FORTY_YEARS)) {
|
||||||
|
if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) {
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// LOG_DEBUG("Got time from GPS month=%d, year=%d, unixtime=%ld", t.tm_mon, t.tm_year, tv.tv_sec);
|
// LOG_DEBUG("Got time from GPS month=%d, year=%d, unixtime=%ld", t.tm_mon, t.tm_year, tv.tv_sec);
|
||||||
if (t.tm_year < 0 || t.tm_year >= 300) {
|
if (t.tm_year < 0 || t.tm_year >= 300) {
|
||||||
@@ -283,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
|
||||||
|
|||||||
+4
-1
@@ -48,10 +48,13 @@ uint32_t getTime(bool local = false);
|
|||||||
/// Return time since 1970 in secs. If quality is RTCQualityNone return zero
|
/// Return time since 1970 in secs. If quality is RTCQualityNone return zero
|
||||||
uint32_t getValidTime(RTCQuality minQuality, bool local = false);
|
uint32_t getValidTime(RTCQuality minQuality, bool local = false);
|
||||||
|
|
||||||
void readFromRTC();
|
RTCSetResult readFromRTC();
|
||||||
|
|
||||||
time_t gm_mktime(struct tm *tm);
|
time_t gm_mktime(struct tm *tm);
|
||||||
|
|
||||||
#define SEC_PER_DAY 86400
|
#define SEC_PER_DAY 86400
|
||||||
#define SEC_PER_HOUR 3600
|
#define SEC_PER_HOUR 3600
|
||||||
#define SEC_PER_MIN 60
|
#define SEC_PER_MIN 60
|
||||||
|
#ifdef BUILD_EPOCH
|
||||||
|
static constexpr uint64_t FORTY_YEARS = (40ULL * 365 * SEC_PER_DAY); // Use 64-bit arithmetic to prevent overflow
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -67,20 +67,28 @@ bool EInkDisplay::forceDisplay(uint32_t msecLimit)
|
|||||||
|
|
||||||
// FIXME - only draw bits have changed (use backbuf similar to the other displays)
|
// FIXME - only draw bits have changed (use backbuf similar to the other displays)
|
||||||
const bool flipped = config.display.flip_screen;
|
const bool flipped = config.display.flip_screen;
|
||||||
|
// HACK for L1 EInk
|
||||||
|
#if defined(SEEED_WIO_TRACKER_L1_EINK)
|
||||||
|
// For SEEED_WIO_TRACKER_L1_EINK, setRotation(3) is correct but mirrored; flip both axes
|
||||||
|
for (uint32_t y = 0; y < displayHeight; y++) {
|
||||||
|
for (uint32_t x = 0; x < displayWidth; x++) {
|
||||||
|
auto b = buffer[x + (y / 8) * displayWidth];
|
||||||
|
auto isset = b & (1 << (y & 7));
|
||||||
|
adafruitDisplay->drawPixel((displayWidth - 1) - x, (displayHeight - 1) - y, isset ? GxEPD_BLACK : GxEPD_WHITE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
for (uint32_t y = 0; y < displayHeight; y++) {
|
for (uint32_t y = 0; y < displayHeight; y++) {
|
||||||
for (uint32_t x = 0; x < displayWidth; x++) {
|
for (uint32_t x = 0; x < displayWidth; x++) {
|
||||||
// get src pixel in the page based ordering the OLED lib uses FIXME, super inefficient
|
|
||||||
auto b = buffer[x + (y / 8) * displayWidth];
|
auto b = buffer[x + (y / 8) * displayWidth];
|
||||||
auto isset = b & (1 << (y & 7));
|
auto isset = b & (1 << (y & 7));
|
||||||
|
|
||||||
// Handle flip here, rather than with setRotation(),
|
|
||||||
// Avoids issues when display width is not a multiple of 8
|
|
||||||
if (flipped)
|
if (flipped)
|
||||||
adafruitDisplay->drawPixel((displayWidth - 1) - x, (displayHeight - 1) - y, isset ? GxEPD_BLACK : GxEPD_WHITE);
|
adafruitDisplay->drawPixel((displayWidth - 1) - x, (displayHeight - 1) - y, isset ? GxEPD_BLACK : GxEPD_WHITE);
|
||||||
else
|
else
|
||||||
adafruitDisplay->drawPixel(x, y, isset ? GxEPD_BLACK : GxEPD_WHITE);
|
adafruitDisplay->drawPixel(x, y, isset ? GxEPD_BLACK : GxEPD_WHITE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Trigger the refresh in GxEPD2
|
// Trigger the refresh in GxEPD2
|
||||||
LOG_DEBUG("Update E-Paper");
|
LOG_DEBUG("Update E-Paper");
|
||||||
@@ -140,17 +148,32 @@ bool EInkDisplay::connect()
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(TTGO_T_ECHO) || defined(ELECROW_ThinkNode_M1)
|
#if defined(TTGO_T_ECHO) || defined(ELECROW_ThinkNode_M1) || defined(T_ECHO_LITE)
|
||||||
{
|
{
|
||||||
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, SPI1);
|
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, SPI1);
|
||||||
|
|
||||||
adafruitDisplay = new GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT>(*lowLevel);
|
adafruitDisplay = new GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT>(*lowLevel);
|
||||||
adafruitDisplay->init();
|
adafruitDisplay->init();
|
||||||
#ifdef ELECROW_ThinkNode_M1
|
#if defined(ELECROW_ThinkNode_M1) || defined(T_ECHO_LITE)
|
||||||
adafruitDisplay->setRotation(4);
|
adafruitDisplay->setRotation(4);
|
||||||
#else
|
#else
|
||||||
adafruitDisplay->setRotation(3);
|
adafruitDisplay->setRotation(3);
|
||||||
#endif
|
#endif
|
||||||
|
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
|
||||||
|
}
|
||||||
|
#elif defined(ELECROW_ThinkNode_M5)
|
||||||
|
{
|
||||||
|
// Start HSPI
|
||||||
|
hspi = new SPIClass(HSPI);
|
||||||
|
hspi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); // SCLK, MISO, MOSI, SS
|
||||||
|
|
||||||
|
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, *hspi);
|
||||||
|
|
||||||
|
adafruitDisplay = new GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT>(*lowLevel);
|
||||||
|
adafruitDisplay->init();
|
||||||
|
|
||||||
|
adafruitDisplay->setRotation(4);
|
||||||
|
|
||||||
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
|
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
|
||||||
}
|
}
|
||||||
#elif defined(MESHLINK)
|
#elif defined(MESHLINK)
|
||||||
@@ -206,7 +229,7 @@ bool EInkDisplay::connect()
|
|||||||
adafruitDisplay->setRotation(0);
|
adafruitDisplay->setRotation(0);
|
||||||
adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT);
|
adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT);
|
||||||
}
|
}
|
||||||
#elif defined(M5_COREINK)
|
#elif defined(M5_COREINK) || defined(T_DECK_PRO)
|
||||||
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
|
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
|
||||||
adafruitDisplay = new GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT>(*lowLevel);
|
adafruitDisplay = new GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT>(*lowLevel);
|
||||||
adafruitDisplay->init(115200, true, 40, false, SPI, SPISettings(4000000, MSBFIRST, SPI_MODE0));
|
adafruitDisplay->init(115200, true, 40, false, SPI, SPISettings(4000000, MSBFIRST, SPI_MODE0));
|
||||||
@@ -220,7 +243,7 @@ bool EInkDisplay::connect()
|
|||||||
adafruitDisplay->setRotation(1);
|
adafruitDisplay->setRotation(1);
|
||||||
adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT);
|
adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT);
|
||||||
}
|
}
|
||||||
#elif defined(HELTEC_MESH_POCKET)
|
#elif defined(HELTEC_MESH_POCKET) || defined(SEEED_WIO_TRACKER_L1_EINK)
|
||||||
{
|
{
|
||||||
spi1 = &SPI1;
|
spi1 = &SPI1;
|
||||||
spi1->begin();
|
spi1->begin();
|
||||||
@@ -234,6 +257,7 @@ bool EInkDisplay::connect()
|
|||||||
// Init GxEPD2
|
// Init GxEPD2
|
||||||
adafruitDisplay->init();
|
adafruitDisplay->init();
|
||||||
adafruitDisplay->setRotation(3);
|
adafruitDisplay->setRotation(3);
|
||||||
|
adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT);
|
||||||
}
|
}
|
||||||
#elif defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_VISION_MASTER_E213)
|
#elif defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_VISION_MASTER_E213)
|
||||||
|
|
||||||
|
|||||||
@@ -80,11 +80,11 @@ class EInkDisplay : public OLEDDisplay
|
|||||||
// If display uses HSPI
|
// If display uses HSPI
|
||||||
#if defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_VISION_MASTER_E213) || \
|
#if defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_VISION_MASTER_E213) || \
|
||||||
defined(HELTEC_VISION_MASTER_E290) || defined(TLORA_T3S3_EPAPER) || defined(CROWPANEL_ESP32S3_5_EPAPER) || \
|
defined(HELTEC_VISION_MASTER_E290) || defined(TLORA_T3S3_EPAPER) || defined(CROWPANEL_ESP32S3_5_EPAPER) || \
|
||||||
defined(CROWPANEL_ESP32S3_4_EPAPER) || defined(CROWPANEL_ESP32S3_2_EPAPER)
|
defined(CROWPANEL_ESP32S3_4_EPAPER) || defined(CROWPANEL_ESP32S3_2_EPAPER) || defined(ELECROW_ThinkNode_M5)
|
||||||
SPIClass *hspi = NULL;
|
SPIClass *hspi = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(HELTEC_MESH_POCKET)
|
#if defined(HELTEC_MESH_POCKET) || defined(SEEED_WIO_TRACKER_L1_EINK)
|
||||||
SPIClass *spi1 = NULL;
|
SPIClass *spi1 = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
+97
-57
@@ -317,8 +317,16 @@ 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(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,
|
||||||
(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_EINK) && !defined(USE_EINK_DYNAMICDISPLAY)
|
#elif defined(USE_EINK) && !defined(USE_EINK_DYNAMICDISPLAY)
|
||||||
@@ -365,9 +373,6 @@ void Screen::doDeepSleep()
|
|||||||
{
|
{
|
||||||
#ifdef USE_EINK
|
#ifdef USE_EINK
|
||||||
setOn(false, graphics::UIRenderer::drawDeepSleepFrame);
|
setOn(false, graphics::UIRenderer::drawDeepSleepFrame);
|
||||||
#ifdef PIN_EINK_EN
|
|
||||||
digitalWrite(PIN_EINK_EN, LOW); // power off backlight
|
|
||||||
#endif
|
|
||||||
#else
|
#else
|
||||||
// Without E-Ink display:
|
// Without E-Ink display:
|
||||||
setOn(false);
|
setOn(false);
|
||||||
@@ -386,13 +391,19 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
|
|||||||
#ifdef T_WATCH_S3
|
#ifdef T_WATCH_S3
|
||||||
PMU->enablePowerOutput(XPOWERS_ALDO2);
|
PMU->enablePowerOutput(XPOWERS_ALDO2);
|
||||||
#endif
|
#endif
|
||||||
#ifdef HELTEC_TRACKER_V1_X
|
|
||||||
uint8_t tft_vext_enabled = digitalRead(VEXT_ENABLE);
|
|
||||||
#endif
|
|
||||||
#if !ARCH_PORTDUINO
|
#if !ARCH_PORTDUINO
|
||||||
dispdev->displayOn();
|
dispdev->displayOn();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef PIN_EINK_EN
|
||||||
|
if (uiconfig.screen_brightness == 1)
|
||||||
|
digitalWrite(PIN_EINK_EN, HIGH);
|
||||||
|
#elif defined(PCA_PIN_EINK_EN)
|
||||||
|
if (uiconfig.screen_brightness == 1)
|
||||||
|
io.digitalWrite(PCA_PIN_EINK_EN, HIGH);
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(ST7789_CS) && \
|
#if defined(ST7789_CS) && \
|
||||||
!defined(M5STACK) // set display brightness when turning on screens. Just moved function from TFTDisplay to here.
|
!defined(M5STACK) // set display brightness when turning on screens. Just moved function from TFTDisplay to here.
|
||||||
static_cast<TFTDisplay *>(dispdev)->setDisplayBrightness(brightness);
|
static_cast<TFTDisplay *>(dispdev)->setDisplayBrightness(brightness);
|
||||||
@@ -400,10 +411,7 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
|
|||||||
|
|
||||||
dispdev->displayOn();
|
dispdev->displayOn();
|
||||||
#ifdef HELTEC_TRACKER_V1_X
|
#ifdef HELTEC_TRACKER_V1_X
|
||||||
// If the TFT VEXT power is not enabled, initialize the UI.
|
ui->init();
|
||||||
if (!tft_vext_enabled) {
|
|
||||||
ui->init();
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ST7789
|
#ifdef USE_ST7789
|
||||||
pinMode(VTFT_CTRL, OUTPUT);
|
pinMode(VTFT_CTRL, OUTPUT);
|
||||||
@@ -425,11 +433,13 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
|
|||||||
// eInkScreensaver parameter is usually NULL (default argument), default frame used instead
|
// eInkScreensaver parameter is usually NULL (default argument), default frame used instead
|
||||||
setScreensaverFrames(einkScreensaver);
|
setScreensaverFrames(einkScreensaver);
|
||||||
#endif
|
#endif
|
||||||
#ifdef ELECROW_ThinkNode_M1
|
|
||||||
if (digitalRead(PIN_EINK_EN) == HIGH) {
|
#ifdef PIN_EINK_EN
|
||||||
digitalWrite(PIN_EINK_EN, LOW);
|
digitalWrite(PIN_EINK_EN, LOW);
|
||||||
}
|
#elif defined(PCA_PIN_EINK_EN)
|
||||||
|
io.digitalWrite(PCA_PIN_EINK_EN, LOW);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
dispdev->displayOff();
|
dispdev->displayOff();
|
||||||
#ifdef USE_ST7789
|
#ifdef USE_ST7789
|
||||||
SPI1.end();
|
SPI1.end();
|
||||||
@@ -505,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);
|
||||||
@@ -548,11 +558,11 @@ void Screen::setup()
|
|||||||
#else
|
#else
|
||||||
if (!config.display.flip_screen) {
|
if (!config.display.flip_screen) {
|
||||||
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7789_CS) || \
|
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7789_CS) || \
|
||||||
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS)
|
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS)
|
||||||
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
|
||||||
}
|
}
|
||||||
@@ -584,7 +594,7 @@ void Screen::setup()
|
|||||||
touchScreenImpl1->init();
|
touchScreenImpl1->init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#elif HAS_TOUCHSCREEN
|
#elif HAS_TOUCHSCREEN && !defined(USE_EINK)
|
||||||
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();
|
||||||
@@ -690,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) {
|
||||||
menuHandler::LoraRegionPicker(0);
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
menuHandler::LoraRegionPicker();
|
||||||
|
#else
|
||||||
|
menuHandler::OnboardMessage();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (!NotificationRenderer::isOverlayBannerShowing() && rebootAtMsec != 0) {
|
if (!NotificationRenderer::isOverlayBannerShowing() && rebootAtMsec != 0) {
|
||||||
@@ -869,6 +883,8 @@ void Screen::setFrames(FrameFocus focus)
|
|||||||
uint8_t previousFrameCount = framesetInfo.frameCount;
|
uint8_t previousFrameCount = framesetInfo.frameCount;
|
||||||
FramesetInfo fsi; // Location of specific frames, for applying focus parameter
|
FramesetInfo fsi; // Location of specific frames, for applying focus parameter
|
||||||
|
|
||||||
|
graphics::UIRenderer::rebuildFavoritedNodes();
|
||||||
|
|
||||||
LOG_DEBUG("Show standard frames");
|
LOG_DEBUG("Show standard frames");
|
||||||
showingNormalScreen = true;
|
showingNormalScreen = true;
|
||||||
|
|
||||||
@@ -886,8 +902,12 @@ void Screen::setFrames(FrameFocus focus)
|
|||||||
|
|
||||||
#if defined(DISPLAY_CLOCK_FRAME)
|
#if defined(DISPLAY_CLOCK_FRAME)
|
||||||
fsi.positions.clock = numframes;
|
fsi.positions.clock = numframes;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
normalFrames[numframes++] = graphics::ClockRenderer::drawAnalogClockFrame;
|
||||||
|
#else
|
||||||
normalFrames[numframes++] = uiconfig.is_clockface_analog ? graphics::ClockRenderer::drawAnalogClockFrame
|
normalFrames[numframes++] = uiconfig.is_clockface_analog ? graphics::ClockRenderer::drawAnalogClockFrame
|
||||||
: graphics::ClockRenderer::drawDigitalClockFrame;
|
: graphics::ClockRenderer::drawDigitalClockFrame;
|
||||||
|
#endif
|
||||||
indicatorIcons.push_back(digital_icon_clock);
|
indicatorIcons.push_back(digital_icon_clock);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -1004,7 +1024,7 @@ void Screen::setFrames(FrameFocus focus)
|
|||||||
// 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 (auto &f : favoriteFrames) {
|
for (const auto &f : favoriteFrames) {
|
||||||
normalFrames[numframes++] = f;
|
normalFrames[numframes++] = f;
|
||||||
indicatorIcons.push_back(icon_node);
|
indicatorIcons.push_back(icon_node);
|
||||||
}
|
}
|
||||||
@@ -1222,6 +1242,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;
|
||||||
|
|
||||||
@@ -1267,40 +1291,49 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
|
|||||||
if (shouldWakeOnReceivedMessage()) {
|
if (shouldWakeOnReceivedMessage()) {
|
||||||
setOn(true); // Wake up the screen first
|
setOn(true); // Wake up the screen first
|
||||||
forceDisplay(); // Forces screen redraw
|
forceDisplay(); // Forces screen redraw
|
||||||
|
|
||||||
// === Prepare banner content ===
|
|
||||||
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet->from);
|
|
||||||
const char *longName = (node && node->has_user) ? node->user.long_name : nullptr;
|
|
||||||
|
|
||||||
const char *msgRaw = reinterpret_cast<const char *>(packet->decoded.payload.bytes);
|
|
||||||
|
|
||||||
char banner[256];
|
|
||||||
|
|
||||||
// Check for bell character in message to determine alert type
|
|
||||||
bool isAlert = false;
|
|
||||||
for (size_t i = 0; i < packet->decoded.payload.size && i < 100; i++) {
|
|
||||||
if (msgRaw[i] == '\x07') {
|
|
||||||
isAlert = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isAlert) {
|
|
||||||
if (longName && longName[0]) {
|
|
||||||
snprintf(banner, sizeof(banner), "Alert Received from\n%s", longName);
|
|
||||||
} else {
|
|
||||||
strcpy(banner, "Alert Received");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (longName && longName[0]) {
|
|
||||||
snprintf(banner, sizeof(banner), "New Message from\n%s", longName);
|
|
||||||
} else {
|
|
||||||
strcpy(banner, "New Message");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
screen->showSimpleBanner(banner, 3000);
|
|
||||||
}
|
}
|
||||||
|
// === Prepare banner content ===
|
||||||
|
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet->from);
|
||||||
|
const char *longName = (node && node->has_user) ? node->user.long_name : nullptr;
|
||||||
|
|
||||||
|
const char *msgRaw = reinterpret_cast<const char *>(packet->decoded.payload.bytes);
|
||||||
|
|
||||||
|
char banner[256];
|
||||||
|
|
||||||
|
// Check for bell character in message to determine alert type
|
||||||
|
bool isAlert = false;
|
||||||
|
for (size_t i = 0; i < packet->decoded.payload.size && i < 100; i++) {
|
||||||
|
if (msgRaw[i] == '\x07') {
|
||||||
|
isAlert = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAlert) {
|
||||||
|
if (longName && longName[0]) {
|
||||||
|
snprintf(banner, sizeof(banner), "Alert Received from\n%s", longName);
|
||||||
|
} else {
|
||||||
|
strcpy(banner, "Alert Received");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (longName && longName[0]) {
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
strcpy(banner, "New Message");
|
||||||
|
#else
|
||||||
|
snprintf(banner, sizeof(banner), "New Message from\n%s", longName);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} else {
|
||||||
|
strcpy(banner, "New Message");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
screen->setOn(true);
|
||||||
|
screen->showSimpleBanner(banner, 1500);
|
||||||
|
playLongBeep();
|
||||||
|
#else
|
||||||
|
screen->showSimpleBanner(banner, 3000);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1379,9 +1412,16 @@ int Screen::handleInputEvent(const InputEvent *event)
|
|||||||
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::LoraRegionPicker();
|
||||||
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.textMessage &&
|
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.textMessage) {
|
||||||
devicestate.rx_text_message.from) {
|
if (devicestate.rx_text_message.from) {
|
||||||
menuHandler::messageResponseMenu();
|
menuHandler::messageResponseMenu();
|
||||||
|
} else {
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
menuHandler::textMessageMenu();
|
||||||
|
#else
|
||||||
|
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 &&
|
||||||
this->ui->getUiState()->currentFrame <= framesetInfo.positions.lastFavorite) {
|
this->ui->getUiState()->currentFrame <= framesetInfo.positions.lastFavorite) {
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
#include "graphics/fonts/OLEDDisplayFontsCS.h"
|
#include "graphics/fonts/OLEDDisplayFontsCS.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CROWPANEL_ESP32S3_5_EPAPER
|
#if defined(CROWPANEL_ESP32S3_5_EPAPER) && defined(USE_EINK)
|
||||||
#include "graphics/fonts/EinkDisplayFonts.h"
|
#include "graphics/fonts/EinkDisplayFonts.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -40,6 +40,9 @@
|
|||||||
#ifdef OLED_PL
|
#ifdef OLED_PL
|
||||||
#define FONT_MEDIUM_LOCAL ArialMT_Plain_16_PL // Height: 19
|
#define FONT_MEDIUM_LOCAL ArialMT_Plain_16_PL // Height: 19
|
||||||
#else
|
#else
|
||||||
|
#ifdef OLED_RU
|
||||||
|
#define FONT_MEDIUM_LOCAL ArialMT_Plain_16_RU // Height: 19
|
||||||
|
#else
|
||||||
#ifdef OLED_UA
|
#ifdef OLED_UA
|
||||||
#define FONT_MEDIUM_LOCAL ArialMT_Plain_16_UA // Height: 19
|
#define FONT_MEDIUM_LOCAL ArialMT_Plain_16_UA // Height: 19
|
||||||
#else
|
#else
|
||||||
@@ -50,9 +53,13 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
#ifdef OLED_PL
|
#ifdef OLED_PL
|
||||||
#define FONT_LARGE_LOCAL ArialMT_Plain_24_PL // Height: 28
|
#define FONT_LARGE_LOCAL ArialMT_Plain_24_PL // Height: 28
|
||||||
#else
|
#else
|
||||||
|
#ifdef OLED_RU
|
||||||
|
#define FONT_LARGE_LOCAL ArialMT_Plain_24_RU // Height: 28
|
||||||
|
#else
|
||||||
#ifdef OLED_UA
|
#ifdef OLED_UA
|
||||||
#define FONT_LARGE_LOCAL ArialMT_Plain_24_UA // Height: 28
|
#define FONT_LARGE_LOCAL ArialMT_Plain_24_UA // Height: 28
|
||||||
#else
|
#else
|
||||||
@@ -63,21 +70,26 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
||||||
defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS)) && \
|
defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS)) && \
|
||||||
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
||||||
// The screen is bigger so use bigger fonts
|
// The screen is bigger so use bigger fonts
|
||||||
#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
|
||||||
#define FONT_LARGE FONT_LARGE_LOCAL // Height: 28
|
#define FONT_LARGE FONT_LARGE_LOCAL // Height: 28
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(CROWPANEL_ESP32S3_5_EPAPER)
|
#if defined(CROWPANEL_ESP32S3_5_EPAPER) && defined(USE_EINK)
|
||||||
#undef FONT_SMALL
|
#undef FONT_SMALL
|
||||||
#undef FONT_MEDIUM
|
#undef FONT_MEDIUM
|
||||||
#undef FONT_LARGE
|
#undef FONT_LARGE
|
||||||
|
|||||||
@@ -124,7 +124,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;
|
||||||
@@ -206,7 +206,7 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
|
|||||||
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 ===
|
||||||
int iconRightEdge = timeX - 1;
|
int iconRightEdge = timeX - 2;
|
||||||
|
|
||||||
bool showMail = false;
|
bool showMail = false;
|
||||||
|
|
||||||
@@ -337,7 +337,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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+268
-22
@@ -562,6 +562,91 @@ class LGFX : public lgfx::LGFX_Device
|
|||||||
|
|
||||||
static LGFX *tft = nullptr;
|
static LGFX *tft = nullptr;
|
||||||
|
|
||||||
|
#elif defined(ST7796_CS)
|
||||||
|
#include <LovyanGFX.hpp> // Graphics and font library for ST7796 driver chip
|
||||||
|
|
||||||
|
class LGFX : public lgfx::LGFX_Device
|
||||||
|
{
|
||||||
|
lgfx::Panel_ST7796 _panel_instance;
|
||||||
|
lgfx::Bus_SPI _bus_instance;
|
||||||
|
lgfx::Light_PWM _light_instance;
|
||||||
|
|
||||||
|
public:
|
||||||
|
LGFX(void)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
auto cfg = _bus_instance.config();
|
||||||
|
|
||||||
|
// SPI
|
||||||
|
cfg.spi_host = ST7796_SPI_HOST;
|
||||||
|
cfg.spi_mode = 0;
|
||||||
|
cfg.freq_write = SPI_FREQUENCY; // SPI clock for transmission (up to 80MHz, rounded to the value obtained by dividing
|
||||||
|
// 80MHz by an integer)
|
||||||
|
cfg.freq_read = SPI_READ_FREQUENCY; // SPI clock when receiving
|
||||||
|
cfg.spi_3wire = false;
|
||||||
|
cfg.use_lock = true; // Set to true to use transaction locking
|
||||||
|
cfg.dma_channel = SPI_DMA_CH_AUTO; // SPI_DMA_CH_AUTO; // Set DMA channel to use (0=not use DMA / 1=1ch / 2=ch /
|
||||||
|
// SPI_DMA_CH_AUTO=auto setting)
|
||||||
|
cfg.pin_sclk = ST7796_SCK; // Set SPI SCLK pin number
|
||||||
|
cfg.pin_mosi = ST7796_SDA; // Set SPI MOSI pin number
|
||||||
|
cfg.pin_miso = ST7796_MISO; // Set SPI MISO pin number (-1 = disable)
|
||||||
|
cfg.pin_dc = ST7796_RS; // Set SPI DC pin number (-1 = disable)
|
||||||
|
|
||||||
|
_bus_instance.config(cfg); // applies the set value to the bus.
|
||||||
|
_panel_instance.setBus(&_bus_instance); // set the bus on the panel.
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Set the display panel control.
|
||||||
|
auto cfg = _panel_instance.config(); // Gets a structure for display panel settings.
|
||||||
|
|
||||||
|
cfg.pin_cs = ST7796_CS; // Pin number where CS is connected (-1 = disable)
|
||||||
|
cfg.pin_rst = ST7796_RESET; // Pin number where RST is connected (-1 = disable)
|
||||||
|
cfg.pin_busy = ST7796_BUSY; // Pin number where BUSY is connected (-1 = disable)
|
||||||
|
|
||||||
|
// cfg.memory_width = TFT_WIDTH; // Maximum width supported by the driver IC
|
||||||
|
// cfg.memory_height = TFT_HEIGHT; // Maximum height supported by the driver IC
|
||||||
|
cfg.panel_width = TFT_WIDTH; // actual displayable width
|
||||||
|
cfg.panel_height = TFT_HEIGHT; // actual displayable height
|
||||||
|
cfg.offset_x = TFT_OFFSET_X; // Panel offset amount in X direction
|
||||||
|
cfg.offset_y = TFT_OFFSET_Y; // Panel offset amount in Y direction
|
||||||
|
cfg.offset_rotation = TFT_OFFSET_ROTATION; // Rotation direction value offset 0~7 (4~7 is mirrored)
|
||||||
|
#ifdef TFT_DUMMY_READ_PIXELS
|
||||||
|
cfg.dummy_read_pixel = TFT_DUMMY_READ_PIXELS; // Number of bits for dummy read before pixel readout
|
||||||
|
#else
|
||||||
|
cfg.dummy_read_pixel = 8; // Number of bits for dummy read before pixel readout
|
||||||
|
#endif
|
||||||
|
cfg.dummy_read_bits = 1; // Number of bits for dummy read before non-pixel data read
|
||||||
|
cfg.readable = true; // Set to true if data can be read
|
||||||
|
cfg.invert = true; // Set to true if the light/darkness of the panel is reversed
|
||||||
|
cfg.rgb_order = false; // Set to true if the panel's red and blue are swapped
|
||||||
|
cfg.dlen_16bit =
|
||||||
|
false; // Set to true for panels that transmit data length in 16-bit units with 16-bit parallel or SPI
|
||||||
|
cfg.bus_shared = true; // If the bus is shared with the SD card, set to true (bus control with drawJpgFile etc.)
|
||||||
|
|
||||||
|
_panel_instance.config(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ST7796_BL
|
||||||
|
// Set the backlight control. (delete if not necessary)
|
||||||
|
{
|
||||||
|
auto cfg = _light_instance.config(); // Gets a structure for backlight settings.
|
||||||
|
|
||||||
|
cfg.pin_bl = ST7796_BL; // Pin number to which the backlight is connected
|
||||||
|
cfg.invert = false; // true to invert the brightness of the backlight
|
||||||
|
cfg.freq = 44100;
|
||||||
|
cfg.pwm_channel = 7;
|
||||||
|
|
||||||
|
_light_instance.config(cfg);
|
||||||
|
_panel_instance.setLight(&_light_instance); // Set the backlight on the panel.
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
setPanel(&_panel_instance); // Sets the panel to use.
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static LGFX *tft = nullptr;
|
||||||
|
|
||||||
#elif defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER)
|
#elif defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER)
|
||||||
|
|
||||||
#include <LovyanGFX.hpp> // Graphics and font library for ILI9341/ILI9342 driver chip
|
#include <LovyanGFX.hpp> // Graphics and font library for ILI9341/ILI9342 driver chip
|
||||||
@@ -667,15 +752,19 @@ static LGFX *tft = nullptr;
|
|||||||
static TFT_eSPI *tft = nullptr; // Invoke library, pins defined in User_Setup.h
|
static TFT_eSPI *tft = nullptr; // Invoke library, pins defined in User_Setup.h
|
||||||
#elif ARCH_PORTDUINO
|
#elif ARCH_PORTDUINO
|
||||||
#include <LovyanGFX.hpp> // Graphics and font library for ST7735 driver chip
|
#include <LovyanGFX.hpp> // Graphics and font library for ST7735 driver chip
|
||||||
|
#if defined(LGFX_SDL)
|
||||||
|
#include <lgfx/v1/platforms/sdl/Panel_sdl.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
class LGFX : public lgfx::LGFX_Device
|
class LGFX : public lgfx::LGFX_Device
|
||||||
{
|
{
|
||||||
lgfx::Panel_Device *_panel_instance;
|
|
||||||
lgfx::Bus_SPI _bus_instance;
|
lgfx::Bus_SPI _bus_instance;
|
||||||
|
|
||||||
lgfx::ITouch *_touch_instance;
|
lgfx::ITouch *_touch_instance;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
lgfx::Panel_Device *_panel_instance;
|
||||||
|
|
||||||
LGFX(void)
|
LGFX(void)
|
||||||
{
|
{
|
||||||
if (settingsMap[displayPanel] == st7789)
|
if (settingsMap[displayPanel] == st7789)
|
||||||
@@ -694,6 +783,11 @@ class LGFX : public lgfx::LGFX_Device
|
|||||||
_panel_instance = new lgfx::Panel_ILI9488;
|
_panel_instance = new lgfx::Panel_ILI9488;
|
||||||
else if (settingsMap[displayPanel] == hx8357d)
|
else if (settingsMap[displayPanel] == hx8357d)
|
||||||
_panel_instance = new lgfx::Panel_HX8357D;
|
_panel_instance = new lgfx::Panel_HX8357D;
|
||||||
|
#if defined(LGFX_SDL)
|
||||||
|
else if (settingsMap[displayPanel] == x11) {
|
||||||
|
_panel_instance = new lgfx::Panel_sdl;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
else {
|
else {
|
||||||
_panel_instance = new lgfx::Panel_NULL;
|
_panel_instance = new lgfx::Panel_NULL;
|
||||||
LOG_ERROR("Unknown display panel configured!");
|
LOG_ERROR("Unknown display panel configured!");
|
||||||
@@ -754,7 +848,13 @@ class LGFX : public lgfx::LGFX_Device
|
|||||||
_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 (settingsMap[displayPanel] == x11) {
|
||||||
|
lgfx::Panel_sdl *sdl_panel_ = (lgfx::Panel_sdl *)_panel_instance;
|
||||||
|
sdl_panel_->setup();
|
||||||
|
sdl_panel_->addKeyCodeMapping(SDLK_RETURN, SDL_SCANCODE_KP_ENTER);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
setPanel(_panel_instance); // Sets the panel to use.
|
setPanel(_panel_instance); // Sets the panel to use.
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -849,9 +949,29 @@ static LGFX *tft = nullptr;
|
|||||||
#include <lgfx/v1/platforms/esp32s3/Bus_RGB.hpp>
|
#include <lgfx/v1/platforms/esp32s3/Bus_RGB.hpp>
|
||||||
#include <lgfx/v1/platforms/esp32s3/Panel_RGB.hpp>
|
#include <lgfx/v1/platforms/esp32s3/Panel_RGB.hpp>
|
||||||
|
|
||||||
|
class PanelInit_ST7701 : public lgfx::Panel_ST7701
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
const uint8_t *getInitCommands(uint8_t listno) const override
|
||||||
|
{
|
||||||
|
// 180 degree hw rotation: vertical flip, horizontal flip
|
||||||
|
static constexpr const uint8_t list1[] = {0x36, 1, 0x10, // MADCTL for vertical flip
|
||||||
|
0xFF, 5, 0x77, 0x01, 0x00, 0x00, 0x10, // Command2 BK0 SEL
|
||||||
|
0xC7, 1, 0x04, // SDIR: X-direction Control (Horizontal Flip)
|
||||||
|
0xFF, 5, 0x77, 0x01, 0x00, 0x00, 0x00, // Command2 BK0 DIS
|
||||||
|
0xFF, 0xFF};
|
||||||
|
switch (listno) {
|
||||||
|
case 1:
|
||||||
|
return list1;
|
||||||
|
default:
|
||||||
|
return lgfx::Panel_ST7701::getInitCommands(listno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class LGFX : public lgfx::LGFX_Device
|
class LGFX : public lgfx::LGFX_Device
|
||||||
{
|
{
|
||||||
lgfx::Panel_ST7701 _panel_instance;
|
PanelInit_ST7701 _panel_instance;
|
||||||
lgfx::Bus_RGB _bus_instance;
|
lgfx::Bus_RGB _bus_instance;
|
||||||
lgfx::Light_PWM _light_instance;
|
lgfx::Light_PWM _light_instance;
|
||||||
lgfx::Touch_FT5x06 _touch_instance;
|
lgfx::Touch_FT5x06 _touch_instance;
|
||||||
@@ -962,8 +1082,9 @@ static LGFX *tft = nullptr;
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(ST7789_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || \
|
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(ST7789_CS) || defined(ST7796_CS) || defined(ILI9341_DRIVER) || \
|
||||||
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST72xx_DE) || (ARCH_PORTDUINO && HAS_SCREEN != 0)
|
defined(ILI9342_DRIVER) || defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST72xx_DE) || \
|
||||||
|
(ARCH_PORTDUINO && HAS_SCREEN != 0)
|
||||||
#include "SPILock.h"
|
#include "SPILock.h"
|
||||||
#include "TFTDisplay.h"
|
#include "TFTDisplay.h"
|
||||||
#include <SPI.h>
|
#include <SPI.h>
|
||||||
@@ -1007,37 +1128,154 @@ TFTDisplay::TFTDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY g
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TFTDisplay::~TFTDisplay()
|
||||||
|
{
|
||||||
|
// Clean up allocated line pixel buffer to prevent memory leak
|
||||||
|
if (linePixelBuffer != nullptr) {
|
||||||
|
free(linePixelBuffer);
|
||||||
|
linePixelBuffer = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Write the buffer to the display memory
|
// Write the buffer to the display memory
|
||||||
void TFTDisplay::display(bool fromBlank)
|
void TFTDisplay::display(bool fromBlank)
|
||||||
{
|
{
|
||||||
if (fromBlank)
|
if (fromBlank)
|
||||||
tft->fillScreen(TFT_BLACK);
|
tft->fillScreen(TFT_BLACK);
|
||||||
// tft->clear();
|
|
||||||
concurrency::LockGuard g(spiLock);
|
concurrency::LockGuard g(spiLock);
|
||||||
|
|
||||||
uint16_t x, y;
|
uint32_t x, y;
|
||||||
|
uint32_t y_byteIndex;
|
||||||
|
uint8_t y_byteMask;
|
||||||
|
uint32_t x_FirstPixelUpdate;
|
||||||
|
uint32_t x_LastPixelUpdate;
|
||||||
|
bool isset, dblbuf_isset;
|
||||||
|
uint16_t colorTftMesh, colorTftBlack;
|
||||||
|
bool somethingChanged = false;
|
||||||
|
|
||||||
for (y = 0; y < displayHeight; y++) {
|
// Store colors byte-reversed so that TFT_eSPI doesn't have to swap bytes in a separate step
|
||||||
for (x = 0; x < displayWidth; x++) {
|
colorTftMesh = (TFT_MESH >> 8) | ((TFT_MESH & 0xFF) << 8);
|
||||||
auto isset = buffer[x + (y / 8) * displayWidth] & (1 << (y & 7));
|
colorTftBlack = (TFT_BLACK >> 8) | ((TFT_BLACK & 0xFF) << 8);
|
||||||
|
|
||||||
|
y = 0;
|
||||||
|
while (y < displayHeight) {
|
||||||
|
y_byteIndex = (y / 8) * displayWidth;
|
||||||
|
y_byteMask = (1 << (y & 7));
|
||||||
|
|
||||||
|
// Step 1: Do a quick scan of 8 rows together. This allows fast-forwarding over unchanged screen areas.
|
||||||
|
if (y_byteMask == 1) {
|
||||||
if (!fromBlank) {
|
if (!fromBlank) {
|
||||||
// get src pixel in the page based ordering the OLED lib uses FIXME, super inefficent
|
for (x = 0; x < displayWidth; x++) {
|
||||||
auto dblbuf_isset = buffer_back[x + (y / 8) * displayWidth] & (1 << (y & 7));
|
if (buffer[x + y_byteIndex] != buffer_back[x + y_byteIndex])
|
||||||
if (isset != dblbuf_isset) {
|
break;
|
||||||
tft->drawPixel(x, y, isset ? TFT_MESH : TFT_BLACK);
|
|
||||||
}
|
}
|
||||||
} else if (isset) {
|
} else {
|
||||||
tft->drawPixel(x, y, TFT_MESH);
|
for (x = 0; x < displayWidth; x++) {
|
||||||
|
if (buffer[x + y_byteIndex] != 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (x >= displayWidth) {
|
||||||
|
// No changed pixels found in these 8 rows, fast-forward to the next 8
|
||||||
|
y = y + 8;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Step 2: Scan each of the 8 rows individually. Find the first pixel in each row that needs updating
|
||||||
|
for (x_FirstPixelUpdate = 0; x_FirstPixelUpdate < displayWidth; x_FirstPixelUpdate++) {
|
||||||
|
isset = buffer[x_FirstPixelUpdate + y_byteIndex] & y_byteMask;
|
||||||
|
|
||||||
|
if (!fromBlank) {
|
||||||
|
// get src pixel in the page based ordering the OLED lib uses
|
||||||
|
dblbuf_isset = buffer_back[x_FirstPixelUpdate + y_byteIndex] & y_byteMask;
|
||||||
|
if (isset != dblbuf_isset) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (isset) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Did we find a pixel that needs updating on this row?
|
||||||
|
if (x_FirstPixelUpdate < displayWidth) {
|
||||||
|
|
||||||
|
// Quickly write out the first changed pixel (saves another array lookup)
|
||||||
|
linePixelBuffer[x_FirstPixelUpdate] = isset ? colorTftMesh : colorTftBlack;
|
||||||
|
x_LastPixelUpdate = x_FirstPixelUpdate;
|
||||||
|
|
||||||
|
// Step 3: copy all remaining pixels in this row into the pixel line buffer,
|
||||||
|
// while also recording the last pixel in the row that needs updating
|
||||||
|
for (x = x_FirstPixelUpdate + 1; x < displayWidth; x++) {
|
||||||
|
isset = buffer[x + y_byteIndex] & y_byteMask;
|
||||||
|
linePixelBuffer[x] = isset ? colorTftMesh : colorTftBlack;
|
||||||
|
|
||||||
|
if (!fromBlank) {
|
||||||
|
dblbuf_isset = buffer_back[x + y_byteIndex] & y_byteMask;
|
||||||
|
if (isset != dblbuf_isset) {
|
||||||
|
x_LastPixelUpdate = x;
|
||||||
|
}
|
||||||
|
} else if (isset) {
|
||||||
|
x_LastPixelUpdate = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 4: Send the changed pixels on this line to the screen as a single block transfer.
|
||||||
|
// This function accepts pixel data MSB first so it can dump the memory straight out the SPI port.
|
||||||
|
tft->pushRect(x_FirstPixelUpdate, y, (x_LastPixelUpdate - x_FirstPixelUpdate + 1), 1,
|
||||||
|
&linePixelBuffer[x_FirstPixelUpdate]);
|
||||||
|
|
||||||
|
somethingChanged = true;
|
||||||
|
}
|
||||||
|
y++;
|
||||||
}
|
}
|
||||||
// Copy the Buffer to the Back Buffer
|
// Copy the Buffer to the Back Buffer
|
||||||
for (y = 0; y < (displayHeight / 8); y++) {
|
if (somethingChanged)
|
||||||
for (x = 0; x < displayWidth; x++) {
|
memcpy(buffer_back, buffer, displayBufferSize);
|
||||||
uint16_t pos = x + y * displayWidth;
|
}
|
||||||
buffer_back[pos] = buffer[pos];
|
|
||||||
|
void TFTDisplay::sdlLoop()
|
||||||
|
{
|
||||||
|
#if defined(LGFX_SDL)
|
||||||
|
static int lastPressed = 0;
|
||||||
|
static int shuttingDown = false;
|
||||||
|
if (settingsMap[displayPanel] == x11) {
|
||||||
|
lgfx::Panel_sdl *sdl_panel_ = (lgfx::Panel_sdl *)tft->_panel_instance;
|
||||||
|
if (sdl_panel_->loop() && !shuttingDown) {
|
||||||
|
LOG_WARN("Window Closed!");
|
||||||
|
InputEvent event = {.inputEvent = (input_broker_event)INPUT_BROKER_SHUTDOWN, .kbchar = 0, .touchX = 0, .touchY = 0};
|
||||||
|
inputBroker->injectInputEvent(&event);
|
||||||
|
}
|
||||||
|
|
||||||
|
// debounce
|
||||||
|
if (lastPressed != 0 && !lgfx::v1::gpio_in(lastPressed))
|
||||||
|
return;
|
||||||
|
if (!lgfx::v1::gpio_in(37)) {
|
||||||
|
lastPressed = 37;
|
||||||
|
InputEvent event = {.inputEvent = (input_broker_event)INPUT_BROKER_RIGHT, .kbchar = 0, .touchX = 0, .touchY = 0};
|
||||||
|
inputBroker->injectInputEvent(&event);
|
||||||
|
} else if (!lgfx::v1::gpio_in(36)) {
|
||||||
|
lastPressed = 36;
|
||||||
|
InputEvent event = {.inputEvent = (input_broker_event)INPUT_BROKER_UP, .kbchar = 0, .touchX = 0, .touchY = 0};
|
||||||
|
inputBroker->injectInputEvent(&event);
|
||||||
|
} else if (!lgfx::v1::gpio_in(38)) {
|
||||||
|
lastPressed = 38;
|
||||||
|
InputEvent event = {.inputEvent = (input_broker_event)INPUT_BROKER_DOWN, .kbchar = 0, .touchX = 0, .touchY = 0};
|
||||||
|
inputBroker->injectInputEvent(&event);
|
||||||
|
} else if (!lgfx::v1::gpio_in(39)) {
|
||||||
|
lastPressed = 39;
|
||||||
|
InputEvent event = {.inputEvent = (input_broker_event)INPUT_BROKER_LEFT, .kbchar = 0, .touchX = 0, .touchY = 0};
|
||||||
|
inputBroker->injectInputEvent(&event);
|
||||||
|
} else if (!lgfx::v1::gpio_in(SDL_SCANCODE_KP_ENTER)) {
|
||||||
|
lastPressed = SDL_SCANCODE_KP_ENTER;
|
||||||
|
InputEvent event = {.inputEvent = (input_broker_event)INPUT_BROKER_SELECT, .kbchar = 0, .touchX = 0, .touchY = 0};
|
||||||
|
inputBroker->injectInputEvent(&event);
|
||||||
|
} else {
|
||||||
|
lastPressed = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send a command to the display (low level function)
|
// Send a command to the display (low level function)
|
||||||
@@ -1184,15 +1422,23 @@ bool TFTDisplay::connect()
|
|||||||
attachInterrupt(digitalPinToInterrupt(SCREEN_TOUCH_INT), rak14014_tpIntHandle, FALLING);
|
attachInterrupt(digitalPinToInterrupt(SCREEN_TOUCH_INT), rak14014_tpIntHandle, FALLING);
|
||||||
#elif defined(T_DECK) || defined(PICOMPUTER_S3) || defined(CHATTER_2)
|
#elif defined(T_DECK) || defined(PICOMPUTER_S3) || defined(CHATTER_2)
|
||||||
tft->setRotation(1); // T-Deck has the TFT in landscape
|
tft->setRotation(1); // T-Deck has the TFT in landscape
|
||||||
#elif defined(T_WATCH_S3) || defined(SENSECAP_INDICATOR)
|
#elif defined(T_WATCH_S3)
|
||||||
tft->setRotation(2); // T-Watch S3 left-handed orientation
|
tft->setRotation(2); // T-Watch S3 left-handed orientation
|
||||||
#elif ARCH_PORTDUINO
|
#elif ARCH_PORTDUINO || defined(SENSECAP_INDICATOR) || defined(T_LORA_PAGER)
|
||||||
tft->setRotation(0); // use config.yaml to set rotation
|
tft->setRotation(0); // use config.yaml to set rotation
|
||||||
#else
|
#else
|
||||||
tft->setRotation(3); // Orient horizontal and wide underneath the silkscreen name label
|
tft->setRotation(3); // Orient horizontal and wide underneath the silkscreen name label
|
||||||
#endif
|
#endif
|
||||||
tft->fillScreen(TFT_BLACK);
|
tft->fillScreen(TFT_BLACK);
|
||||||
|
|
||||||
|
if (this->linePixelBuffer == NULL) {
|
||||||
|
this->linePixelBuffer = (uint16_t *)malloc(sizeof(uint16_t) * displayWidth);
|
||||||
|
|
||||||
|
if (!this->linePixelBuffer) {
|
||||||
|
LOG_ERROR("Not enough memory to create TFT line buffer\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,9 +20,13 @@ class TFTDisplay : public OLEDDisplay
|
|||||||
*/
|
*/
|
||||||
TFTDisplay(uint8_t, int, int, OLEDDISPLAY_GEOMETRY, HW_I2C);
|
TFTDisplay(uint8_t, int, int, OLEDDISPLAY_GEOMETRY, HW_I2C);
|
||||||
|
|
||||||
|
// Destructor to clean up allocated memory
|
||||||
|
~TFTDisplay();
|
||||||
|
|
||||||
// Write the buffer to the display memory
|
// Write the buffer to the display memory
|
||||||
virtual void display() override { display(false); };
|
virtual void display() override { display(false); };
|
||||||
virtual void display(bool fromBlank);
|
virtual void display(bool fromBlank);
|
||||||
|
void sdlLoop();
|
||||||
|
|
||||||
// Turn the display upside down
|
// Turn the display upside down
|
||||||
virtual void flipScreenVertically();
|
virtual void flipScreenVertically();
|
||||||
@@ -57,4 +61,6 @@ class TFTDisplay : public OLEDDisplay
|
|||||||
|
|
||||||
// Connect to the display
|
// Connect to the display
|
||||||
virtual bool connect() override;
|
virtual bool connect() override;
|
||||||
|
|
||||||
|
uint16_t *linePixelBuffer = nullptr;
|
||||||
};
|
};
|
||||||
@@ -186,7 +186,7 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
|
|||||||
{
|
{
|
||||||
display->clear();
|
display->clear();
|
||||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||||
int line = 1;
|
|
||||||
// === Set Title, Blank for Clock
|
// === Set Title, Blank for Clock
|
||||||
const char *titleStr = "";
|
const char *titleStr = "";
|
||||||
// === Header ===
|
// === Header ===
|
||||||
@@ -218,7 +218,6 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
|
|||||||
hour %= 12;
|
hour %= 12;
|
||||||
if (hour == 0)
|
if (hour == 0)
|
||||||
hour = 12;
|
hour = 12;
|
||||||
bool isPM = hour >= 12;
|
|
||||||
snprintf(timeString, sizeof(timeString), "%d:%02d", hour, minute);
|
snprintf(timeString, sizeof(timeString), "%d:%02d", hour, minute);
|
||||||
} else {
|
} else {
|
||||||
snprintf(timeString, sizeof(timeString), "%02d:%02d", hour, minute);
|
snprintf(timeString, sizeof(timeString), "%02d:%02d", hour, minute);
|
||||||
@@ -230,6 +229,8 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
|
|||||||
|
|
||||||
#ifdef T_WATCH_S3
|
#ifdef T_WATCH_S3
|
||||||
float scale = 1.5;
|
float scale = 1.5;
|
||||||
|
#elif defined(CHATTER_2)
|
||||||
|
float scale = 1.1;
|
||||||
#else
|
#else
|
||||||
float scale = 0.75;
|
float scale = 0.75;
|
||||||
if (isHighResolution) {
|
if (isHighResolution) {
|
||||||
@@ -285,6 +286,9 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
|
|||||||
int yOffset = (isHighResolution) ? 3 : 1;
|
int yOffset = (isHighResolution) ? 3 : 1;
|
||||||
#ifdef SENSECAP_INDICATOR
|
#ifdef SENSECAP_INDICATOR
|
||||||
yOffset -= 3;
|
yOffset -= 3;
|
||||||
|
#endif
|
||||||
|
#ifdef T_DECK
|
||||||
|
yOffset -= 5;
|
||||||
#endif
|
#endif
|
||||||
if (config.display.use_12h_clock) {
|
if (config.display.use_12h_clock) {
|
||||||
display->drawString(startingHourMinuteTextX + xOffset, (display->getHeight() - hourMinuteTextY) - yOffset - 2,
|
display->drawString(startingHourMinuteTextX + xOffset, (display->getHeight() - hourMinuteTextY) - yOffset - 2,
|
||||||
@@ -362,7 +366,7 @@ void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
// hour hand radius and y coordinate
|
// hour hand radius and y coordinate
|
||||||
int16_t hourHandRadius = radius * 0.35;
|
int16_t hourHandRadius = radius * 0.35;
|
||||||
if (isHighResolution) {
|
if (isHighResolution) {
|
||||||
int16_t hourHandRadius = radius * 0.55;
|
hourHandRadius = radius * 0.55;
|
||||||
}
|
}
|
||||||
int16_t hourHandNoonY = centerY - hourHandRadius;
|
int16_t hourHandNoonY = centerY - hourHandRadius;
|
||||||
|
|
||||||
@@ -381,7 +385,7 @@ void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
|
|
||||||
bool isPM = hour >= 12;
|
bool isPM = hour >= 12;
|
||||||
if (config.display.use_12h_clock) {
|
if (config.display.use_12h_clock) {
|
||||||
bool isPM = hour >= 12;
|
isPM = hour >= 12;
|
||||||
display->setFont(FONT_SMALL);
|
display->setFont(FONT_SMALL);
|
||||||
int yOffset = isHighResolution ? 1 : 0;
|
int yOffset = isHighResolution ? 1 : 0;
|
||||||
#ifdef USE_EINK
|
#ifdef USE_EINK
|
||||||
|
|||||||
@@ -94,7 +94,8 @@ void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16
|
|||||||
if (!Throttle::isWithinTimespanMs(storeForwardModule->lastHeartbeat,
|
if (!Throttle::isWithinTimespanMs(storeForwardModule->lastHeartbeat,
|
||||||
(storeForwardModule->heartbeatInterval * 1200))) { // no heartbeat, overlap a bit
|
(storeForwardModule->heartbeatInterval * 1200))) { // no heartbeat, overlap a bit
|
||||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
||||||
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || ARCH_PORTDUINO) && \
|
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS) || \
|
||||||
|
ARCH_PORTDUINO) && \
|
||||||
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
||||||
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 12,
|
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 12,
|
||||||
8, imgQuestionL1);
|
8, imgQuestionL1);
|
||||||
@@ -106,7 +107,7 @@ void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16
|
|||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
||||||
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS)) && \
|
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS)) && \
|
||||||
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
||||||
display->drawFastImage(x + SCREEN_WIDTH - 18 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 16,
|
display->drawFastImage(x + SCREEN_WIDTH - 18 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 16,
|
||||||
8, imgSFL1);
|
8, imgSFL1);
|
||||||
@@ -121,7 +122,8 @@ void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16
|
|||||||
} else {
|
} else {
|
||||||
// TODO: Raspberry Pi supports more than just the one screen size
|
// TODO: Raspberry Pi supports more than just the one screen size
|
||||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
||||||
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || ARCH_PORTDUINO) && \
|
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS) || \
|
||||||
|
ARCH_PORTDUINO) && \
|
||||||
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
||||||
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8,
|
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8,
|
||||||
imgInfoL1);
|
imgInfoL1);
|
||||||
@@ -261,12 +263,6 @@ void drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
|
|||||||
display->drawString(x + 1, y, "USB");
|
display->drawString(x + 1, y, "USB");
|
||||||
}
|
}
|
||||||
|
|
||||||
// auto mode = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, true);
|
|
||||||
|
|
||||||
// display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode), y, mode);
|
|
||||||
// if (config.display.heading_bold)
|
|
||||||
// display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode) - 1, y, mode);
|
|
||||||
|
|
||||||
uint32_t currentMillis = millis();
|
uint32_t currentMillis = millis();
|
||||||
uint32_t seconds = currentMillis / 1000;
|
uint32_t seconds = currentMillis / 1000;
|
||||||
uint32_t minutes = seconds / 60;
|
uint32_t minutes = seconds / 60;
|
||||||
@@ -281,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 = "";
|
||||||
|
|
||||||
@@ -390,17 +387,24 @@ 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: Radio Preset ===
|
||||||
auto mode = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false);
|
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;
|
||||||
@@ -412,9 +416,17 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
|
|||||||
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) {
|
||||||
snprintf(frequencyslot, sizeof(frequencyslot), "Freq: %smhz", freqStr);
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
snprintf(frequencyslot, sizeof(frequencyslot), "%sMHz", freqStr);
|
||||||
|
#else
|
||||||
|
snprintf(frequencyslot, sizeof(frequencyslot), "Freq: %sMHz", freqStr);
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
snprintf(frequencyslot, sizeof(frequencyslot), "Freq/Ch: %smhz (%d)", freqStr, config.lora.channel_num);
|
#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);
|
||||||
|
#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) {
|
||||||
@@ -424,6 +436,7 @@ 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);
|
||||||
|
|
||||||
|
#if !defined(M5STACK_UNITC6L)
|
||||||
// === Fourth Row: Channel Utilization ===
|
// === Fourth Row: Channel Utilization ===
|
||||||
const char *chUtil = "ChUtil:";
|
const char *chUtil = "ChUtil:";
|
||||||
char chUtilPercentage[10];
|
char chUtilPercentage[10];
|
||||||
@@ -480,10 +493,11 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
|
|||||||
|
|
||||||
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)[4],
|
||||||
chUtilPercentage);
|
chUtilPercentage);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// ****************************
|
// ****************************
|
||||||
// * Memory Screen *
|
// * System Screen *
|
||||||
// ****************************
|
// ****************************
|
||||||
void drawMemoryUsage(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
void drawMemoryUsage(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||||
{
|
{
|
||||||
@@ -505,8 +519,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;
|
||||||
@@ -531,7 +548,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);
|
||||||
@@ -539,7 +556,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);
|
||||||
@@ -592,12 +609,32 @@ 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 *lastDot = strrchr(appversionstr, '.');
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
if (lastDot != nullptr) {
|
||||||
|
*lastDot = '\0'; // truncate string
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (lastDot) {
|
||||||
|
size_t prefixLen = lastDot - appversionstr;
|
||||||
|
strncpy(appversionstr_formatted, appversionstr, prefixLen);
|
||||||
|
appversionstr_formatted[prefixLen] = '\0';
|
||||||
|
strncat(appversionstr_formatted, " (", sizeof(appversionstr_formatted) - strlen(appversionstr_formatted) - 1);
|
||||||
|
strncat(appversionstr_formatted, lastDot + 1, sizeof(appversionstr_formatted) - strlen(appversionstr_formatted) - 1);
|
||||||
|
strncat(appversionstr_formatted, ")", sizeof(appversionstr_formatted) - strlen(appversionstr_formatted) - 1);
|
||||||
|
strncpy(appversionstr, appversionstr_formatted, sizeof(appversionstr) - 1);
|
||||||
|
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] = "";
|
||||||
@@ -616,6 +653,7 @@ 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
|
||||||
}
|
}
|
||||||
} // namespace DebugRenderer
|
} // namespace DebugRenderer
|
||||||
} // namespace graphics
|
} // namespace graphics
|
||||||
|
|||||||
@@ -15,6 +15,9 @@
|
|||||||
#include "modules/CannedMessageModule.h"
|
#include "modules/CannedMessageModule.h"
|
||||||
#include "modules/KeyVerificationModule.h"
|
#include "modules/KeyVerificationModule.h"
|
||||||
|
|
||||||
|
#include "modules/TraceRouteModule.h"
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
extern uint16_t TFT_MESH;
|
extern uint16_t TFT_MESH;
|
||||||
|
|
||||||
namespace graphics
|
namespace graphics
|
||||||
@@ -23,6 +26,27 @@ 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::OnboardMessage()
|
||||||
|
{
|
||||||
|
static const char *optionsArray[] = {"OK", "Got it!"};
|
||||||
|
enum optionsNumbers { OK, got };
|
||||||
|
BannerOverlayOptions bannerOptions;
|
||||||
|
#if HAS_TFT
|
||||||
|
bannerOptions.message = "Welcome to Meshtastic!\nSwipe to navigate and\nlong press to select\nor open a menu.";
|
||||||
|
#elif defined(BUTTON_PIN)
|
||||||
|
bannerOptions.message = "Welcome to Meshtastic!\nClick to navigate and\nlong press to select\nor open a menu.";
|
||||||
|
#else
|
||||||
|
bannerOptions.message = "Welcome to Meshtastic!\nUse the Select button\nto open menus\nand make selections.";
|
||||||
|
#endif
|
||||||
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
|
bannerOptions.optionsCount = 2;
|
||||||
|
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||||
|
menuHandler::menuQueue = menuHandler::no_timeout_lora_picker;
|
||||||
|
screen->runNow();
|
||||||
|
};
|
||||||
|
screen->showOverlayBanner(bannerOptions);
|
||||||
|
}
|
||||||
|
|
||||||
void menuHandler::LoraRegionPicker(uint32_t duration)
|
void menuHandler::LoraRegionPicker(uint32_t duration)
|
||||||
{
|
{
|
||||||
static const char *optionsArray[] = {"Back",
|
static const char *optionsArray[] = {"Back",
|
||||||
@@ -51,12 +75,18 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
|
|||||||
"PH_915",
|
"PH_915",
|
||||||
"ANZ_433",
|
"ANZ_433",
|
||||||
"KZ_433",
|
"KZ_433",
|
||||||
"KZ_863"};
|
"KZ_863",
|
||||||
|
"NP_865",
|
||||||
|
"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 = 25;
|
bannerOptions.optionsCount = 27;
|
||||||
bannerOptions.InitialSelected = 0;
|
bannerOptions.InitialSelected = 0;
|
||||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||||
if (selected != 0 && config.lora.region != _meshtastic_Config_LoRaConfig_RegionCode(selected)) {
|
if (selected != 0 && config.lora.region != _meshtastic_Config_LoRaConfig_RegionCode(selected)) {
|
||||||
@@ -115,6 +145,22 @@ void menuHandler::TwelveHourPicker()
|
|||||||
screen->showOverlayBanner(bannerOptions);
|
screen->showOverlayBanner(bannerOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reusable confirmation prompt function
|
||||||
|
void menuHandler::showConfirmationBanner(const char *message, std::function<void()> onConfirm)
|
||||||
|
{
|
||||||
|
static const char *confirmOptions[] = {"No", "Yes"};
|
||||||
|
BannerOverlayOptions confirmBanner;
|
||||||
|
confirmBanner.message = message;
|
||||||
|
confirmBanner.optionsArrayPtr = confirmOptions;
|
||||||
|
confirmBanner.optionsCount = 2;
|
||||||
|
confirmBanner.bannerCallback = [onConfirm](int confirmSelected) -> void {
|
||||||
|
if (confirmSelected == 1) {
|
||||||
|
onConfirm();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
screen->showOverlayBanner(confirmBanner);
|
||||||
|
}
|
||||||
|
|
||||||
void menuHandler::ClockFacePicker()
|
void menuHandler::ClockFacePicker()
|
||||||
{
|
{
|
||||||
static const char *optionsArray[] = {"Back", "Digital", "Analog"};
|
static const char *optionsArray[] = {"Back", "Digital", "Analog"};
|
||||||
@@ -151,6 +197,7 @@ void menuHandler::TZPicker()
|
|||||||
"US/Mountain",
|
"US/Mountain",
|
||||||
"US/Central",
|
"US/Central",
|
||||||
"US/Eastern",
|
"US/Eastern",
|
||||||
|
"BR/Brasilia",
|
||||||
"UTC",
|
"UTC",
|
||||||
"EU/Western",
|
"EU/Western",
|
||||||
"EU/"
|
"EU/"
|
||||||
@@ -165,7 +212,7 @@ void menuHandler::TZPicker()
|
|||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
bannerOptions.message = "Pick Timezone";
|
bannerOptions.message = "Pick Timezone";
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
bannerOptions.optionsCount = 17;
|
bannerOptions.optionsCount = 19;
|
||||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||||
if (selected == 0) {
|
if (selected == 0) {
|
||||||
menuHandler::menuQueue = menuHandler::clock_menu;
|
menuHandler::menuQueue = menuHandler::clock_menu;
|
||||||
@@ -184,25 +231,27 @@ void menuHandler::TZPicker()
|
|||||||
strncpy(config.device.tzdef, "CST6CDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef));
|
strncpy(config.device.tzdef, "CST6CDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef));
|
||||||
} else if (selected == 7) { // Eastern
|
} else if (selected == 7) { // Eastern
|
||||||
strncpy(config.device.tzdef, "EST5EDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef));
|
strncpy(config.device.tzdef, "EST5EDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef));
|
||||||
} else if (selected == 8) { // UTC
|
} else if (selected == 8) { // Brazil
|
||||||
strncpy(config.device.tzdef, "UTC", sizeof(config.device.tzdef));
|
strncpy(config.device.tzdef, "BRT3", sizeof(config.device.tzdef));
|
||||||
} else if (selected == 9) { // EU/Western
|
} else if (selected == 9) { // UTC
|
||||||
|
strncpy(config.device.tzdef, "UTC0", sizeof(config.device.tzdef));
|
||||||
|
} else if (selected == 10) { // EU/Western
|
||||||
strncpy(config.device.tzdef, "GMT0BST,M3.5.0/1,M10.5.0", sizeof(config.device.tzdef));
|
strncpy(config.device.tzdef, "GMT0BST,M3.5.0/1,M10.5.0", sizeof(config.device.tzdef));
|
||||||
} else if (selected == 10) { // EU/Central
|
} else if (selected == 11) { // EU/Central
|
||||||
strncpy(config.device.tzdef, "CET-1CEST,M3.5.0,M10.5.0/3", sizeof(config.device.tzdef));
|
strncpy(config.device.tzdef, "CET-1CEST,M3.5.0,M10.5.0/3", sizeof(config.device.tzdef));
|
||||||
} else if (selected == 11) { // EU/Eastern
|
} else if (selected == 12) { // EU/Eastern
|
||||||
strncpy(config.device.tzdef, "EET-2EEST,M3.5.0/3,M10.5.0/4", sizeof(config.device.tzdef));
|
strncpy(config.device.tzdef, "EET-2EEST,M3.5.0/3,M10.5.0/4", sizeof(config.device.tzdef));
|
||||||
} else if (selected == 12) { // Asia/Kolkata
|
} else if (selected == 13) { // Asia/Kolkata
|
||||||
strncpy(config.device.tzdef, "IST-5:30", sizeof(config.device.tzdef));
|
strncpy(config.device.tzdef, "IST-5:30", sizeof(config.device.tzdef));
|
||||||
} else if (selected == 13) { // China
|
} else if (selected == 14) { // China
|
||||||
strncpy(config.device.tzdef, "HKT-8", sizeof(config.device.tzdef));
|
strncpy(config.device.tzdef, "HKT-8", sizeof(config.device.tzdef));
|
||||||
} else if (selected == 14) { // AU/AWST
|
} else if (selected == 15) { // AU/AWST
|
||||||
strncpy(config.device.tzdef, "AWST-8", sizeof(config.device.tzdef));
|
strncpy(config.device.tzdef, "AWST-8", sizeof(config.device.tzdef));
|
||||||
} else if (selected == 15) { // AU/ACST
|
} else if (selected == 16) { // AU/ACST
|
||||||
strncpy(config.device.tzdef, "ACST-9:30ACDT,M10.1.0,M4.1.0/3", sizeof(config.device.tzdef));
|
strncpy(config.device.tzdef, "ACST-9:30ACDT,M10.1.0,M4.1.0/3", sizeof(config.device.tzdef));
|
||||||
} else if (selected == 16) { // AU/AEST
|
} else if (selected == 17) { // AU/AEST
|
||||||
strncpy(config.device.tzdef, "AEST-10AEDT,M10.1.0,M4.1.0/3", sizeof(config.device.tzdef));
|
strncpy(config.device.tzdef, "AEST-10AEDT,M10.1.0,M4.1.0/3", sizeof(config.device.tzdef));
|
||||||
} else if (selected == 17) { // NZ
|
} else if (selected == 18) { // NZ
|
||||||
strncpy(config.device.tzdef, "NZST-12NZDT,M9.5.0,M4.1.0/3", sizeof(config.device.tzdef));
|
strncpy(config.device.tzdef, "NZST-12NZDT,M9.5.0,M4.1.0/3", sizeof(config.device.tzdef));
|
||||||
}
|
}
|
||||||
if (selected != 0) {
|
if (selected != 0) {
|
||||||
@@ -215,7 +264,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";
|
||||||
@@ -239,8 +292,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;
|
||||||
|
|
||||||
@@ -254,7 +310,11 @@ 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;
|
||||||
@@ -288,13 +348,13 @@ void menuHandler::messageResponseMenu()
|
|||||||
|
|
||||||
void menuHandler::homeBaseMenu()
|
void menuHandler::homeBaseMenu()
|
||||||
{
|
{
|
||||||
enum optionsNumbers { Back, Backlight, Position, Preset, Freetext, Bluetooth, Sleep, enumEnd };
|
enum optionsNumbers { Back, Backlight, Position, Preset, Freetext, Sleep, 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;
|
||||||
|
|
||||||
#ifdef PIN_EINK_EN
|
#if defined(PIN_EINK_EN) || defined(PCA_PIN_EINK_EN)
|
||||||
optionsArray[options] = "Toggle Backlight";
|
optionsArray[options] = "Toggle Backlight";
|
||||||
optionsEnumArray[options++] = Backlight;
|
optionsEnumArray[options++] = Backlight;
|
||||||
#else
|
#else
|
||||||
@@ -304,28 +364,46 @@ void menuHandler::homeBaseMenu()
|
|||||||
|
|
||||||
optionsArray[options] = "Send Position";
|
optionsArray[options] = "Send Position";
|
||||||
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";
|
||||||
optionsEnumArray[options++] = Freetext;
|
optionsEnumArray[options++] = Freetext;
|
||||||
}
|
}
|
||||||
optionsArray[options] = "Bluetooth Toggle";
|
|
||||||
optionsEnumArray[options++] = Bluetooth;
|
|
||||||
|
|
||||||
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;
|
||||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||||
if (selected == Backlight) {
|
if (selected == Backlight) {
|
||||||
#ifdef PIN_EINK_EN
|
#if defined(PIN_EINK_EN)
|
||||||
if (digitalRead(PIN_EINK_EN) == HIGH) {
|
if (uiconfig.screen_brightness == 1) {
|
||||||
|
uiconfig.screen_brightness = 0;
|
||||||
digitalWrite(PIN_EINK_EN, LOW);
|
digitalWrite(PIN_EINK_EN, LOW);
|
||||||
} else {
|
} else {
|
||||||
|
uiconfig.screen_brightness = 1;
|
||||||
digitalWrite(PIN_EINK_EN, HIGH);
|
digitalWrite(PIN_EINK_EN, HIGH);
|
||||||
}
|
}
|
||||||
|
saveUIConfig();
|
||||||
|
#elif defined(PCA_PIN_EINK_EN)
|
||||||
|
if (uiconfig.screen_brightness == 1) {
|
||||||
|
uiconfig.screen_brightness = 0;
|
||||||
|
io.digitalWrite(PCA_PIN_EINK_EN, LOW);
|
||||||
|
} else {
|
||||||
|
uiconfig.screen_brightness = 1;
|
||||||
|
io.digitalWrite(PCA_PIN_EINK_EN, HIGH);
|
||||||
|
}
|
||||||
|
saveUIConfig();
|
||||||
#endif
|
#endif
|
||||||
} else if (selected == Sleep) {
|
} else if (selected == Sleep) {
|
||||||
screen->setOn(false);
|
screen->setOn(false);
|
||||||
@@ -336,9 +414,40 @@ void menuHandler::homeBaseMenu()
|
|||||||
cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST);
|
cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST);
|
||||||
} else if (selected == Freetext) {
|
} else if (selected == Freetext) {
|
||||||
cannedMessageModule->LaunchFreetextWithDestination(NODENUM_BROADCAST);
|
cannedMessageModule->LaunchFreetextWithDestination(NODENUM_BROADCAST);
|
||||||
} else if (selected == Bluetooth) {
|
}
|
||||||
menuQueue = bluetooth_toggle_menu;
|
};
|
||||||
screen->runNow();
|
screen->showOverlayBanner(bannerOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
void menuHandler::textMessageMenu()
|
||||||
|
{
|
||||||
|
cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST);
|
||||||
|
}
|
||||||
|
|
||||||
|
void menuHandler::textMessageBaseMenu()
|
||||||
|
{
|
||||||
|
enum optionsNumbers { Back, Preset, Freetext, enumEnd };
|
||||||
|
|
||||||
|
static const char *optionsArray[enumEnd] = {"Back"};
|
||||||
|
static int optionsEnumArray[enumEnd] = {Back};
|
||||||
|
int options = 1;
|
||||||
|
optionsArray[options] = "New Preset Msg";
|
||||||
|
optionsEnumArray[options++] = Preset;
|
||||||
|
if (kb_found) {
|
||||||
|
optionsArray[options] = "New Freetext Msg";
|
||||||
|
optionsEnumArray[options++] = Freetext;
|
||||||
|
}
|
||||||
|
|
||||||
|
BannerOverlayOptions bannerOptions;
|
||||||
|
bannerOptions.message = "Message Action";
|
||||||
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
|
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
||||||
|
bannerOptions.optionsCount = options;
|
||||||
|
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||||
|
if (selected == Preset) {
|
||||||
|
cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST);
|
||||||
|
} else if (selected == Freetext) {
|
||||||
|
cannedMessageModule->LaunchFreetextWithDestination(NODENUM_BROADCAST);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
screen->showOverlayBanner(bannerOptions);
|
screen->showOverlayBanner(bannerOptions);
|
||||||
@@ -346,26 +455,29 @@ void menuHandler::homeBaseMenu()
|
|||||||
|
|
||||||
void menuHandler::systemBaseMenu()
|
void menuHandler::systemBaseMenu()
|
||||||
{
|
{
|
||||||
// Check if brightness is supported
|
enum optionsNumbers { Back, Notifications, ScreenOptions, Bluetooth, PowerMenu, Test, enumEnd };
|
||||||
bool hasSupportBrightness = false;
|
|
||||||
#if defined(ST7789_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107) || HAS_TFT
|
|
||||||
hasSupportBrightness = true;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum optionsNumbers { Back, Notifications, ScreenOptions, PowerMenu, 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;
|
||||||
|
|
||||||
optionsArray[options] = "Notifications";
|
optionsArray[options] = "Notifications";
|
||||||
optionsEnumArray[options++] = Notifications;
|
optionsEnumArray[options++] = Notifications;
|
||||||
#if defined(ST7789_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107) || \
|
#if defined(ST7789_CS) || defined(ST7796_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || \
|
||||||
defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT
|
defined(USE_SH1107) || defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT
|
||||||
optionsArray[options] = "Screen Options";
|
optionsArray[options] = "Screen Options";
|
||||||
optionsEnumArray[options++] = ScreenOptions;
|
optionsEnumArray[options++] = ScreenOptions;
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
optionsArray[options] = "Bluetooth";
|
||||||
|
#else
|
||||||
|
optionsArray[options] = "Bluetooth Toggle";
|
||||||
|
#endif
|
||||||
|
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) {
|
||||||
@@ -374,7 +486,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;
|
||||||
@@ -391,6 +507,9 @@ void menuHandler::systemBaseMenu()
|
|||||||
} else if (selected == Test) {
|
} else if (selected == Test) {
|
||||||
menuHandler::menuQueue = menuHandler::test_menu;
|
menuHandler::menuQueue = menuHandler::test_menu;
|
||||||
screen->runNow();
|
screen->runNow();
|
||||||
|
} else if (selected == Bluetooth) {
|
||||||
|
menuQueue = bluetooth_toggle_menu;
|
||||||
|
screen->runNow();
|
||||||
} else if (selected == Back && !test_enabled) {
|
} else if (selected == Back && !test_enabled) {
|
||||||
test_count++;
|
test_count++;
|
||||||
if (test_count > 4) {
|
if (test_count > 4) {
|
||||||
@@ -403,8 +522,12 @@ void menuHandler::systemBaseMenu()
|
|||||||
|
|
||||||
void menuHandler::favoriteBaseMenu()
|
void menuHandler::favoriteBaseMenu()
|
||||||
{
|
{
|
||||||
enum optionsNumbers { Back, Preset, Freetext, Remove, 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;
|
||||||
|
|
||||||
@@ -412,22 +535,34 @@ 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";
|
||||||
|
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;
|
||||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||||
if (selected == 1) {
|
if (selected == Preset) {
|
||||||
cannedMessageModule->LaunchWithDestination(graphics::UIRenderer::currentFavoriteNodeNum);
|
cannedMessageModule->LaunchWithDestination(graphics::UIRenderer::currentFavoriteNodeNum);
|
||||||
} else if (selected == 2 && kb_found) {
|
} else if (selected == Freetext) {
|
||||||
cannedMessageModule->LaunchFreetextWithDestination(graphics::UIRenderer::currentFavoriteNodeNum);
|
cannedMessageModule->LaunchFreetextWithDestination(graphics::UIRenderer::currentFavoriteNodeNum);
|
||||||
} else if ((!kb_found && selected == 2) || (selected == 3 && kb_found)) {
|
} else if (selected == Remove) {
|
||||||
menuHandler::menuQueue = menuHandler::remove_favorite;
|
menuHandler::menuQueue = menuHandler::remove_favorite;
|
||||||
screen->runNow();
|
screen->runNow();
|
||||||
|
} else if (selected == TraceRoute) {
|
||||||
|
if (traceRouteModule) {
|
||||||
|
traceRouteModule->launch(graphics::UIRenderer::currentFavoriteNodeNum);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
screen->showOverlayBanner(bannerOptions);
|
screen->showOverlayBanner(bannerOptions);
|
||||||
@@ -466,12 +601,20 @@ void menuHandler::positionBaseMenu()
|
|||||||
|
|
||||||
void menuHandler::nodeListMenu()
|
void menuHandler::nodeListMenu()
|
||||||
{
|
{
|
||||||
enum optionsNumbers { Back, Favorite, Verify, Reset };
|
enum optionsNumbers { Back, Favorite, TraceRoute, Verify, Reset, enumEnd };
|
||||||
static const char *optionsArray[] = {"Back", "Add Favorite", "Key Verification", "Reset NodeDB"};
|
#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"};
|
||||||
|
#endif
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
bannerOptions.message = "Node Action";
|
bannerOptions.message = "Node Action";
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
bannerOptions.optionsCount = 4;
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
bannerOptions.optionsCount = 3;
|
||||||
|
#else
|
||||||
|
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;
|
||||||
@@ -482,6 +625,9 @@ void menuHandler::nodeListMenu()
|
|||||||
} else if (selected == Reset) {
|
} else if (selected == Reset) {
|
||||||
menuQueue = reset_node_db_menu;
|
menuQueue = reset_node_db_menu;
|
||||||
screen->runNow();
|
screen->runNow();
|
||||||
|
} else if (selected == TraceRoute) {
|
||||||
|
menuQueue = trace_route_menu;
|
||||||
|
screen->runNow();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
screen->showOverlayBanner(bannerOptions);
|
screen->showOverlayBanner(bannerOptions);
|
||||||
@@ -575,7 +721,11 @@ 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 {
|
||||||
@@ -635,7 +785,7 @@ void menuHandler::BrightnessPickerMenu()
|
|||||||
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190)
|
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190)
|
||||||
// For HELTEC devices, use analogWrite to control backlight
|
// For HELTEC devices, use analogWrite to control backlight
|
||||||
analogWrite(VTFT_LEDA, uiconfig.screen_brightness);
|
analogWrite(VTFT_LEDA, uiconfig.screen_brightness);
|
||||||
#elif defined(ST7789_CS)
|
#elif defined(ST7789_CS) || defined(ST7796_CS)
|
||||||
static_cast<TFTDisplay *>(screen->getDisplayDevice())->setDisplayBrightness(uiconfig.screen_brightness);
|
static_cast<TFTDisplay *>(screen->getDisplayDevice())->setDisplayBrightness(uiconfig.screen_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)
|
||||||
screen->getDisplayDevice()->setBrightness(uiconfig.screen_brightness);
|
screen->getDisplayDevice()->setBrightness(uiconfig.screen_brightness);
|
||||||
@@ -678,6 +828,7 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display)
|
|||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
bannerOptions.optionsCount = 10;
|
bannerOptions.optionsCount = 10;
|
||||||
bannerOptions.bannerCallback = [display](int selected) -> void {
|
bannerOptions.bannerCallback = [display](int selected) -> void {
|
||||||
|
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || defined(T_LORA_PAGER) || HAS_TFT
|
||||||
uint8_t TFT_MESH_r = 0;
|
uint8_t TFT_MESH_r = 0;
|
||||||
uint8_t TFT_MESH_g = 0;
|
uint8_t TFT_MESH_g = 0;
|
||||||
uint8_t TFT_MESH_b = 0;
|
uint8_t TFT_MESH_b = 0;
|
||||||
@@ -729,7 +880,6 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display)
|
|||||||
screen->runNow();
|
screen->runNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT
|
|
||||||
if (selected != 0) {
|
if (selected != 0) {
|
||||||
display->setColor(BLACK);
|
display->setColor(BLACK);
|
||||||
display->fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
|
display->fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||||
@@ -767,7 +917,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 {
|
||||||
@@ -787,14 +941,17 @@ 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 {
|
||||||
if (selected == 1) {
|
if (selected == 1) {
|
||||||
IF_SCREEN(screen->showSimpleBanner("Shutting Down...", 0));
|
InputEvent event = {.inputEvent = (input_broker_event)INPUT_BROKER_SHUTDOWN, .kbchar = 0, .touchX = 0, .touchY = 0};
|
||||||
nodeDB->saveToDisk();
|
inputBroker->injectInputEvent(&event);
|
||||||
power->shutdown();
|
|
||||||
} else {
|
} else {
|
||||||
menuQueue = power_menu;
|
menuQueue = power_menu;
|
||||||
screen->runNow();
|
screen->runNow();
|
||||||
@@ -805,7 +962,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);
|
||||||
@@ -827,13 +989,24 @@ void menuHandler::removeFavoriteMenu()
|
|||||||
bannerOptions.optionsCount = 2;
|
bannerOptions.optionsCount = 2;
|
||||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||||
if (selected == 1) {
|
if (selected == 1) {
|
||||||
|
LOG_INFO("Removing %x as favorite node", graphics::UIRenderer::currentFavoriteNodeNum);
|
||||||
nodeDB->set_favorite(false, graphics::UIRenderer::currentFavoriteNodeNum);
|
nodeDB->set_favorite(false, graphics::UIRenderer::currentFavoriteNodeNum);
|
||||||
screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
|
screen->setFrames(graphics::Screen::FOCUS_DEFAULT);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
screen->showOverlayBanner(bannerOptions);
|
screen->showOverlayBanner(bannerOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void menuHandler::traceRouteMenu()
|
||||||
|
{
|
||||||
|
screen->showNodePicker("Node to Trace", 30000, [](uint32_t nodenum) -> void {
|
||||||
|
LOG_INFO("Menu: Node picker selected node 0x%08x, traceRouteModule=%p", nodenum, traceRouteModule);
|
||||||
|
if (traceRouteModule) {
|
||||||
|
traceRouteModule->startTraceRoute(nodenum);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void menuHandler::testMenu()
|
void menuHandler::testMenu()
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -924,23 +1097,28 @@ void menuHandler::screenOptionsMenu()
|
|||||||
{
|
{
|
||||||
// Check if brightness is supported
|
// Check if brightness is supported
|
||||||
bool hasSupportBrightness = false;
|
bool hasSupportBrightness = false;
|
||||||
#if defined(ST7789_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107) || HAS_TFT
|
#if defined(ST7789_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107)
|
||||||
hasSupportBrightness = true;
|
hasSupportBrightness = true;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(T_DECK)
|
||||||
|
// TDeck Doesn't seem to support brightness at all, at least not reliably
|
||||||
|
hasSupportBrightness = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
enum optionsNumbers { Back, Brightness, ScreenColor };
|
enum optionsNumbers { Back, Brightness, ScreenColor };
|
||||||
static const char *optionsArray[4] = {"Back"};
|
static const char *optionsArray[4] = {"Back"};
|
||||||
static int optionsEnumArray[4] = {Back};
|
static int optionsEnumArray[4] = {Back};
|
||||||
int options = 1;
|
int options = 1;
|
||||||
|
|
||||||
// Only show brightness for B&W displays
|
// Only show brightness for B&W displays
|
||||||
if (hasSupportBrightness && !HAS_TFT) {
|
if (hasSupportBrightness) {
|
||||||
optionsArray[options] = "Brightness";
|
optionsArray[options] = "Brightness";
|
||||||
optionsEnumArray[options++] = Brightness;
|
optionsEnumArray[options++] = Brightness;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only show screen color for TFT displays
|
// Only show screen color for TFT displays
|
||||||
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT
|
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || defined(T_LORA_PAGER) || HAS_TFT
|
||||||
optionsArray[options] = "Screen Color";
|
optionsArray[options] = "Screen Color";
|
||||||
optionsEnumArray[options++] = ScreenColor;
|
optionsEnumArray[options++] = ScreenColor;
|
||||||
#endif
|
#endif
|
||||||
@@ -985,7 +1163,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;
|
||||||
@@ -1048,6 +1230,9 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
|
|||||||
case lora_picker:
|
case lora_picker:
|
||||||
LoraRegionPicker();
|
LoraRegionPicker();
|
||||||
break;
|
break;
|
||||||
|
case no_timeout_lora_picker:
|
||||||
|
LoraRegionPicker(0);
|
||||||
|
break;
|
||||||
case TZ_picker:
|
case TZ_picker:
|
||||||
TZPicker();
|
TZPicker();
|
||||||
break;
|
break;
|
||||||
@@ -1101,6 +1286,9 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
|
|||||||
case remove_favorite:
|
case remove_favorite:
|
||||||
removeFavoriteMenu();
|
removeFavoriteMenu();
|
||||||
break;
|
break;
|
||||||
|
case trace_route_menu:
|
||||||
|
traceRouteMenu();
|
||||||
|
break;
|
||||||
case test_menu:
|
case test_menu:
|
||||||
testMenu();
|
testMenu();
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class menuHandler
|
|||||||
enum screenMenus {
|
enum screenMenus {
|
||||||
menu_none,
|
menu_none,
|
||||||
lora_picker,
|
lora_picker,
|
||||||
|
no_timeout_lora_picker,
|
||||||
TZ_picker,
|
TZ_picker,
|
||||||
twelve_hour_picker,
|
twelve_hour_picker,
|
||||||
clock_face_picker,
|
clock_face_picker,
|
||||||
@@ -36,18 +37,22 @@ class menuHandler
|
|||||||
system_base_menu,
|
system_base_menu,
|
||||||
key_verification_init,
|
key_verification_init,
|
||||||
key_verification_final_prompt,
|
key_verification_final_prompt,
|
||||||
throttle_message
|
trace_route_menu,
|
||||||
|
throttle_message,
|
||||||
};
|
};
|
||||||
static screenMenus menuQueue;
|
static screenMenus menuQueue;
|
||||||
|
|
||||||
|
static void OnboardMessage();
|
||||||
static void LoraRegionPicker(uint32_t duration = 30000);
|
static void LoraRegionPicker(uint32_t duration = 30000);
|
||||||
static void handleMenuSwitch(OLEDDisplay *display);
|
static void handleMenuSwitch(OLEDDisplay *display);
|
||||||
|
static void showConfirmationBanner(const char *message, std::function<void()> onConfirm);
|
||||||
static void clockMenu();
|
static void clockMenu();
|
||||||
static void TZPicker();
|
static void TZPicker();
|
||||||
static void TwelveHourPicker();
|
static void TwelveHourPicker();
|
||||||
static void ClockFacePicker();
|
static void ClockFacePicker();
|
||||||
static void messageResponseMenu();
|
static void messageResponseMenu();
|
||||||
static void homeBaseMenu();
|
static void homeBaseMenu();
|
||||||
|
static void textMessageBaseMenu();
|
||||||
static void systemBaseMenu();
|
static void systemBaseMenu();
|
||||||
static void favoriteBaseMenu();
|
static void favoriteBaseMenu();
|
||||||
static void positionBaseMenu();
|
static void positionBaseMenu();
|
||||||
@@ -63,6 +68,7 @@ class menuHandler
|
|||||||
static void shutdownMenu();
|
static void shutdownMenu();
|
||||||
static void addFavoriteMenu();
|
static void addFavoriteMenu();
|
||||||
static void removeFavoriteMenu();
|
static void removeFavoriteMenu();
|
||||||
|
static void traceRouteMenu();
|
||||||
static void testMenu();
|
static void testMenu();
|
||||||
static void numberTest();
|
static void numberTest();
|
||||||
static void wifiBaseMenu();
|
static void wifiBaseMenu();
|
||||||
@@ -70,6 +76,7 @@ class menuHandler
|
|||||||
static void notificationsMenu();
|
static void notificationsMenu();
|
||||||
static void screenOptionsMenu();
|
static void screenOptionsMenu();
|
||||||
static void powerMenu();
|
static void powerMenu();
|
||||||
|
static void textMessageMenu();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void saveUIConfig();
|
static void saveUIConfig();
|
||||||
|
|||||||
@@ -137,7 +137,11 @@ void drawStringWithEmotes(OLEDDisplay *display, int x, int y, const std::string
|
|||||||
display->drawString(cursorX + 1, fontY, textChunk.c_str());
|
display->drawString(cursorX + 1, fontY, textChunk.c_str());
|
||||||
}
|
}
|
||||||
display->drawString(cursorX, fontY, textChunk.c_str());
|
display->drawString(cursorX, fontY, textChunk.c_str());
|
||||||
|
#if defined(OLED_UA) || defined(OLED_RU)
|
||||||
|
cursorX += display->getStringWidth(textChunk.c_str(), textChunk.length(), true);
|
||||||
|
#else
|
||||||
cursorX += display->getStringWidth(textChunk.c_str());
|
cursorX += display->getStringWidth(textChunk.c_str());
|
||||||
|
#endif
|
||||||
i = nextControl;
|
i = nextControl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -155,7 +159,12 @@ void drawStringWithEmotes(OLEDDisplay *display, int x, int y, const std::string
|
|||||||
display->drawString(cursorX + 1, fontY, remaining.c_str());
|
display->drawString(cursorX + 1, fontY, remaining.c_str());
|
||||||
}
|
}
|
||||||
display->drawString(cursorX, fontY, remaining.c_str());
|
display->drawString(cursorX, fontY, remaining.c_str());
|
||||||
|
#if defined(OLED_UA) || defined(OLED_RU)
|
||||||
|
cursorX += display->getStringWidth(remaining.c_str(), remaining.length(), true);
|
||||||
|
#else
|
||||||
cursorX += display->getStringWidth(remaining.c_str());
|
cursorX += display->getStringWidth(remaining.c_str());
|
||||||
|
#endif
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,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;
|
||||||
|
|
||||||
@@ -192,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,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;
|
||||||
@@ -207,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;
|
||||||
@@ -226,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 ===
|
||||||
@@ -273,7 +349,7 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
currentKey ^= ((size_t)mp.id << 24);
|
currentKey ^= ((size_t)mp.id << 24);
|
||||||
|
|
||||||
if (cachedKey != currentKey) {
|
if (cachedKey != currentKey) {
|
||||||
LOG_INFO("Message cache key is misssed cachedKey=0x%0x, currentKey=0x%x", cachedKey, currentKey);
|
LOG_INFO("Onscreen message scroll cache key needs updating: cachedKey=0x%0x, currentKey=0x%x", cachedKey, currentKey);
|
||||||
|
|
||||||
// Cache miss - regenerate lines and heights
|
// Cache miss - regenerate lines and heights
|
||||||
cachedLines = generateLines(display, headerStr, messageBuf, textWidth);
|
cachedLines = generateLines(display, headerStr, messageBuf, textWidth);
|
||||||
@@ -346,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)
|
||||||
@@ -374,10 +451,16 @@ std::vector<std::string> generateLines(OLEDDisplay *display, const char *headerS
|
|||||||
} else {
|
} else {
|
||||||
word += ch;
|
word += ch;
|
||||||
std::string test = line + word;
|
std::string test = line + word;
|
||||||
// Keep these lines for diagnostics
|
// Keep these lines for diagnostics
|
||||||
// LOG_INFO("Char: '%c' (0x%02X)", ch, (unsigned char)ch);
|
// LOG_INFO("Char: '%c' (0x%02X)", ch, (unsigned char)ch);
|
||||||
// LOG_INFO("Current String: %s", test.c_str());
|
// LOG_INFO("Current String: %s", test.c_str());
|
||||||
if (display->getStringWidth(test.c_str()) > textWidth) {
|
// Note: there are boolean comparison uint16 (getStringWidth) with int (textWidth), hope textWidth is always positive :)
|
||||||
|
#if defined(OLED_UA) || defined(OLED_RU)
|
||||||
|
uint16_t strWidth = display->getStringWidth(test.c_str(), test.length(), true);
|
||||||
|
#else
|
||||||
|
uint16_t strWidth = display->getStringWidth(test.c_str());
|
||||||
|
#endif
|
||||||
|
if (strWidth > textWidth) {
|
||||||
if (!line.empty())
|
if (!line.empty())
|
||||||
lines.push_back(line);
|
lines.push_back(line);
|
||||||
line = word;
|
line = word;
|
||||||
|
|||||||
@@ -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()) {
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ void NotificationRenderer::drawNumberPicker(OLEDDisplay *display, OLEDDisplayUiS
|
|||||||
resetBanner();
|
resetBanner();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (curSelected == numDigits) {
|
if (curSelected == static_cast<int8_t>(numDigits)) {
|
||||||
alertBannerCallback(currentNumber);
|
alertBannerCallback(currentNumber);
|
||||||
resetBanner();
|
resetBanner();
|
||||||
return;
|
return;
|
||||||
@@ -383,7 +383,9 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp
|
|||||||
|
|
||||||
uint8_t firstOptionToShow = 0;
|
uint8_t firstOptionToShow = 0;
|
||||||
if (alertBannerOptions > 0) {
|
if (alertBannerOptions > 0) {
|
||||||
if (curSelected > 1 && alertBannerOptions > visibleTotalLines - lineCount) {
|
if (visibleTotalLines - lineCount == 1) {
|
||||||
|
firstOptionToShow = curSelected;
|
||||||
|
} else if (curSelected > 1 && alertBannerOptions > visibleTotalLines - lineCount) {
|
||||||
if (curSelected > alertBannerOptions - visibleTotalLines + lineCount)
|
if (curSelected > alertBannerOptions - visibleTotalLines + lineCount)
|
||||||
firstOptionToShow = alertBannerOptions - visibleTotalLines + lineCount;
|
firstOptionToShow = alertBannerOptions - visibleTotalLines + lineCount;
|
||||||
else
|
else
|
||||||
@@ -392,6 +394,9 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp
|
|||||||
firstOptionToShow = 0;
|
firstOptionToShow = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Useful log line for troubleshooting:
|
||||||
|
/* LOG_WARN("alertBannerOptions: %u, curSelected: %u, visibleTotalLines: %u, lineCount: %u, firstOptionToShow: %u",
|
||||||
|
alertBannerOptions, curSelected, visibleTotalLines, lineCount, firstOptionToShow); */
|
||||||
|
|
||||||
for (int i = firstOptionToShow; i < alertBannerOptions && linesShown < visibleTotalLines; i++, linesShown++) {
|
for (int i = firstOptionToShow; i < alertBannerOptions && linesShown < visibleTotalLines; i++, linesShown++) {
|
||||||
if (i == curSelected) {
|
if (i == curSelected) {
|
||||||
@@ -454,6 +459,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;
|
||||||
@@ -542,6 +676,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,10 +20,27 @@
|
|||||||
|
|
||||||
// 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;
|
||||||
|
std::vector<meshtastic_NodeInfoLite *> graphics::UIRenderer::favoritedNodes;
|
||||||
|
|
||||||
|
void graphics::UIRenderer::rebuildFavoritedNodes()
|
||||||
|
{
|
||||||
|
favoritedNodes.clear();
|
||||||
|
size_t total = nodeDB->getNumMeshNodes();
|
||||||
|
for (size_t i = 0; i < total; i++) {
|
||||||
|
meshtastic_NodeInfoLite *n = nodeDB->getMeshNodeByIndex(i);
|
||||||
|
if (!n || n->num == nodeDB->getNodeNum())
|
||||||
|
continue;
|
||||||
|
if (n->is_favorite)
|
||||||
|
favoritedNodes.push_back(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(favoritedNodes.begin(), favoritedNodes.end(),
|
||||||
|
[](const meshtastic_NodeInfoLite *a, const meshtastic_NodeInfoLite *b) { return a->num < b->num; });
|
||||||
|
}
|
||||||
|
|
||||||
#if !MESHTASTIC_EXCLUDE_GPS
|
#if !MESHTASTIC_EXCLUDE_GPS
|
||||||
// GeoCoord object for coordinate conversions
|
// GeoCoord object for coordinate conversions
|
||||||
@@ -177,7 +194,7 @@ void UIRenderer::drawNodes(OLEDDisplay *display, int16_t x, int16_t y, const mes
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
||||||
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS)) && \
|
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS)) && \
|
||||||
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
||||||
|
|
||||||
if (isHighResolution) {
|
if (isHighResolution) {
|
||||||
@@ -201,27 +218,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)
|
||||||
{
|
{
|
||||||
// --- Cache favorite nodes for the current frame only, to save computation ---
|
|
||||||
static std::vector<meshtastic_NodeInfoLite *> favoritedNodes;
|
|
||||||
static int prevFrame = -1;
|
|
||||||
|
|
||||||
// --- Only rebuild favorites list if we're on a new frame ---
|
|
||||||
if (state->currentFrame != prevFrame) {
|
|
||||||
prevFrame = state->currentFrame;
|
|
||||||
favoritedNodes.clear();
|
|
||||||
size_t total = nodeDB->getNumMeshNodes();
|
|
||||||
for (size_t i = 0; i < total; i++) {
|
|
||||||
meshtastic_NodeInfoLite *n = nodeDB->getMeshNodeByIndex(i);
|
|
||||||
// Skip nulls and ourself
|
|
||||||
if (!n || n->num == nodeDB->getNodeNum())
|
|
||||||
continue;
|
|
||||||
if (n->is_favorite)
|
|
||||||
favoritedNodes.push_back(n);
|
|
||||||
}
|
|
||||||
// Keep a stable, consistent display order
|
|
||||||
std::sort(favoritedNodes.begin(), favoritedNodes.end(),
|
|
||||||
[](const meshtastic_NodeInfoLite *a, const meshtastic_NodeInfoLite *b) { return a->num < b->num; });
|
|
||||||
}
|
|
||||||
if (favoritedNodes.empty())
|
if (favoritedNodes.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -233,8 +229,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";
|
||||||
@@ -253,9 +256,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")
|
||||||
@@ -310,7 +317,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) {
|
||||||
@@ -482,6 +489,7 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st
|
|||||||
}
|
}
|
||||||
// else show nothing
|
// else show nothing
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// ****************************
|
// ****************************
|
||||||
@@ -495,7 +503,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 ===
|
||||||
|
|
||||||
@@ -510,20 +522,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 ===
|
||||||
@@ -552,6 +569,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;
|
||||||
@@ -657,7 +689,7 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta
|
|||||||
|
|
||||||
char combinedName[50];
|
char combinedName[50];
|
||||||
snprintf(combinedName, sizeof(combinedName), "%s (%s)", longNameStr.empty() ? "" : longNameStr.c_str(), shortnameble);
|
snprintf(combinedName, sizeof(combinedName), "%s (%s)", longNameStr.empty() ? "" : longNameStr.c_str(), shortnameble);
|
||||||
if (SCREEN_WIDTH - (display->getStringWidth(longName) + display->getStringWidth(shortnameble)) > 10) {
|
if (SCREEN_WIDTH - (display->getStringWidth(combinedName)) > 10) {
|
||||||
size_t len = strlen(combinedName);
|
size_t len = strlen(combinedName);
|
||||||
if (len >= 3 && strcmp(combinedName + len - 3, " ()") == 0) {
|
if (len >= 3 && strcmp(combinedName + len - 3, " ()") == 0) {
|
||||||
combinedName[len - 3] = '\0'; // Remove the last three characters
|
combinedName[len - 3] = '\0'; // Remove the last three characters
|
||||||
@@ -668,7 +700,7 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta
|
|||||||
nameX, ((rows == 4) ? getTextPositions(display)[line++] : getTextPositions(display)[line++]) + yOffset, combinedName);
|
nameX, ((rows == 4) ? getTextPositions(display)[line++] : getTextPositions(display)[line++]) + yOffset, combinedName);
|
||||||
} else {
|
} else {
|
||||||
// === LongName Centered ===
|
// === LongName Centered ===
|
||||||
textWidth = display->getStringWidth(longName);
|
textWidth = display->getStringWidth(longNameStr.c_str());
|
||||||
nameX = (SCREEN_WIDTH - textWidth) / 2;
|
nameX = (SCREEN_WIDTH - textWidth) / 2;
|
||||||
display->drawString(nameX, getTextPositions(display)[line++], longNameStr.c_str());
|
display->drawString(nameX, getTextPositions(display)[line++], longNameStr.c_str());
|
||||||
|
|
||||||
@@ -677,6 +709,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
|
||||||
@@ -835,6 +868,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);
|
||||||
|
|
||||||
@@ -843,7 +898,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);
|
||||||
@@ -858,6 +912,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
|
||||||
}
|
}
|
||||||
|
|
||||||
// ****************************
|
// ****************************
|
||||||
@@ -933,15 +988,26 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU
|
|||||||
UIRenderer::formatDateTime(datetimeStr, sizeof(datetimeStr), rtc_sec, display, showTime);
|
UIRenderer::formatDateTime(datetimeStr, sizeof(datetimeStr), rtc_sec, display, showTime);
|
||||||
char fullLine[40];
|
char fullLine[40];
|
||||||
snprintf(fullLine, sizeof(fullLine), " Date: %s", datetimeStr);
|
snprintf(fullLine, sizeof(fullLine), " Date: %s", datetimeStr);
|
||||||
|
#if !defined(M5STACK_UNITC6L)
|
||||||
display->drawString(0, getTextPositions(display)[line++], fullLine);
|
display->drawString(0, getTextPositions(display)[line++], fullLine);
|
||||||
|
#endif
|
||||||
|
|
||||||
// === Third Row: Latitude ===
|
// === Third Row: Latitude ===
|
||||||
char latStr[32];
|
char latStr[32];
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
snprintf(latStr, sizeof(latStr), "Lat:%.5f", geoCoord.getLatitude() * 1e-7);
|
||||||
|
display->drawString(x, getTextPositions(display)[line++] + 2, latStr);
|
||||||
|
#else
|
||||||
snprintf(latStr, sizeof(latStr), " Lat: %.5f", geoCoord.getLatitude() * 1e-7);
|
snprintf(latStr, sizeof(latStr), " Lat: %.5f", geoCoord.getLatitude() * 1e-7);
|
||||||
display->drawString(x, getTextPositions(display)[line++], latStr);
|
display->drawString(x, getTextPositions(display)[line++], latStr);
|
||||||
|
#endif
|
||||||
|
|
||||||
// === Fourth Row: Longitude ===
|
// === Fourth Row: Longitude ===
|
||||||
char lonStr[32];
|
char lonStr[32];
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
snprintf(lonStr, sizeof(lonStr), "Lon:%.3f", geoCoord.getLongitude() * 1e-7);
|
||||||
|
display->drawString(x, getTextPositions(display)[line++] + 4, lonStr);
|
||||||
|
#else
|
||||||
snprintf(lonStr, sizeof(lonStr), " Lon: %.5f", geoCoord.getLongitude() * 1e-7);
|
snprintf(lonStr, sizeof(lonStr), " Lon: %.5f", geoCoord.getLongitude() * 1e-7);
|
||||||
display->drawString(x, getTextPositions(display)[line++], lonStr);
|
display->drawString(x, getTextPositions(display)[line++], lonStr);
|
||||||
|
|
||||||
@@ -953,8 +1019,9 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU
|
|||||||
snprintf(DisplayLineTwo, sizeof(DisplayLineTwo), " Alt: %.0im", geoCoord.getAltitude());
|
snprintf(DisplayLineTwo, sizeof(DisplayLineTwo), " Alt: %.0im", geoCoord.getAltitude());
|
||||||
}
|
}
|
||||||
display->drawString(x, getTextPositions(display)[line++], DisplayLineTwo);
|
display->drawString(x, getTextPositions(display)[line++], DisplayLineTwo);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
#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 ---
|
||||||
@@ -1037,6 +1104,7 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USERPREFS_OEM_TEXT
|
#ifdef USERPREFS_OEM_TEXT
|
||||||
@@ -1193,7 +1261,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);
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ class UIRenderer
|
|||||||
static void drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
static void drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||||
|
|
||||||
static NodeNum currentFavoriteNodeNum;
|
static NodeNum currentFavoriteNodeNum;
|
||||||
|
static std::vector<meshtastic_NodeInfoLite *> favoritedNodes;
|
||||||
|
static void rebuildFavoritedNodes();
|
||||||
|
|
||||||
// OEM screens
|
// OEM screens
|
||||||
#ifdef USERPREFS_OEM_TEXT
|
#ifdef USERPREFS_OEM_TEXT
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#ifdef USE_EINK
|
||||||
|
|
||||||
#include "EinkDisplayFonts.h"
|
#include "EinkDisplayFonts.h"
|
||||||
|
|
||||||
// Created by https://oleddisplay.squix.ch/ Consider a donation
|
// Created by https://oleddisplay.squix.ch/ Consider a donation
|
||||||
@@ -1182,3 +1184,5 @@ const uint8_t Monospaced_plain_30[] PROGMEM = {
|
|||||||
0xF0, 0xFF, 0x01, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0xE0, 0x00, 0xFE, 0x03, 0x00, 0xE0, 0x80, 0x7F,
|
0xF0, 0xFF, 0x01, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0xE0, 0x00, 0xFE, 0x03, 0x00, 0xE0, 0x80, 0x7F,
|
||||||
0x00, 0x00, 0xE0, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x10 // 255
|
0x00, 0x00, 0xE0, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x10 // 255
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif // USE_EINK
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#ifndef EINKDISPLAYFONTS_h
|
#ifndef EINKDISPLAYFONTS_h
|
||||||
#define EINKDISPLAYFONTS_h
|
#define EINKDISPLAYFONTS_h
|
||||||
|
|
||||||
|
#ifdef USE_EINK
|
||||||
|
|
||||||
#ifdef ARDUINO
|
#ifdef ARDUINO
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#elif __MBED__
|
#elif __MBED__
|
||||||
@@ -11,4 +13,7 @@
|
|||||||
* Monospaced Plain 30
|
* Monospaced Plain 30
|
||||||
*/
|
*/
|
||||||
extern const uint8_t Monospaced_plain_30[] PROGMEM;
|
extern const uint8_t Monospaced_plain_30[] PROGMEM;
|
||||||
|
|
||||||
|
#endif // USE_EINK
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#ifdef OLED_CS
|
||||||
|
|
||||||
#include "OLEDDisplayFontsCS.h"
|
#include "OLEDDisplayFontsCS.h"
|
||||||
|
|
||||||
// Font generated or edited with the glyphEditor
|
// Font generated or edited with the glyphEditor
|
||||||
@@ -1860,4 +1862,6 @@ const uint8_t ArialMT_Plain_24_CS[] PROGMEM = {
|
|||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x06, 0xC0, 0xF0, 0x01, 0x06, 0xC0, 0x80, 0x0F, 0x07, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x06, 0xC0, 0xF0, 0x01, 0x06, 0xC0, 0x80, 0x0F, 0x07, 0x00,
|
||||||
0x00, 0xFE, 0x03, 0x00, 0x00, 0xFC, 0x00, 0xC0, 0xC0, 0x1F, 0x00, 0xC0, 0xF8, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00,
|
0x00, 0xFE, 0x03, 0x00, 0x00, 0xFC, 0x00, 0xC0, 0xC0, 0x1F, 0x00, 0xC0, 0xF8, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00,
|
||||||
0x06, // 255
|
0x06, // 255
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif // OLED_CS
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
// trunk-ignore-all(clang-format): Preserve long lines
|
// trunk-ignore-all(clang-format): Preserve long lines
|
||||||
|
#ifdef OLED_PL
|
||||||
#include "OLEDDisplayFontsPL.h"
|
#include "OLEDDisplayFontsPL.h"
|
||||||
|
|
||||||
const uint8_t ArialMT_Plain_10_PL[] PROGMEM = {
|
const uint8_t ArialMT_Plain_10_PL[] PROGMEM = {
|
||||||
@@ -1310,4 +1311,6 @@ const uint8_t ArialMT_Plain_24_PL[] PROGMEM = {
|
|||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x06, 0x00, 0xF0, 0x01, 0x06, 0x00, 0x80, 0x0F, 0x07, 0x80, 0x00, 0xFE, 0x03, 0xE0, 0x00, 0xFC, 0x00, 0x60, 0xC0, 0x1F, 0x00, 0x20, 0xF8, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x06, // 253
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x06, 0x00, 0xF0, 0x01, 0x06, 0x00, 0x80, 0x0F, 0x07, 0x80, 0x00, 0xFE, 0x03, 0xE0, 0x00, 0xFC, 0x00, 0x60, 0xC0, 0x1F, 0x00, 0x20, 0xF8, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x06, // 253
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0x07, 0xE0, 0xFF, 0xFF, 0x07, 0x00, 0x1C, 0x18, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xF0, 0x03, // 254
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0x07, 0xE0, 0xFF, 0xFF, 0x07, 0x00, 0x1C, 0x18, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xF0, 0x03, // 254
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x06, 0xC0, 0xF0, 0x01, 0x06, 0xC0, 0x80, 0x0F, 0x07, 0x00, 0x00, 0xFE, 0x03, 0x00, 0x00, 0xFC, 0x00, 0xC0, 0xC0, 0x1F, 0x00, 0xC0, 0xF8, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x06, // 255
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x06, 0xC0, 0xF0, 0x01, 0x06, 0xC0, 0x80, 0x0F, 0x07, 0x00, 0x00, 0xFE, 0x03, 0x00, 0x00, 0xFC, 0x00, 0xC0, 0xC0, 0x1F, 0x00, 0xC0, 0xF8, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x06, // 255
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif // OLED_PL
|
||||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user