mirror of
https://github.com/alexhopeoconnor/firmware.git
synced 2026-10-17 09:01:30 +10:00
Compare commits
313
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d84355d15a | ||
|
|
5d12edbe07 | ||
|
|
398d1adea1 | ||
|
|
393cd96ba2 | ||
|
|
6658ec2603 | ||
|
|
b941dab900 | ||
|
|
ffe6f706f9 | ||
|
|
0ed537a336 | ||
|
|
86dad90573 | ||
|
|
20db5fdbb7 | ||
|
|
5b1ea922f3 | ||
|
|
5b94f580dc | ||
|
|
3d524be965 | ||
|
|
4ec59ce75d | ||
|
|
cb28c38828 | ||
|
|
969aefa551 | ||
|
|
3f5828c0e9 | ||
|
|
5cf365245e | ||
|
|
fdd17ac75c | ||
|
|
9e40c8893f | ||
|
|
afd5e29bce | ||
|
|
20f838a61e | ||
|
|
58736f5184 | ||
|
|
22031c5886 | ||
|
|
fe4e75eb66 | ||
|
|
42075da09e | ||
|
|
6ba82ed5b6 | ||
|
|
935b0504d4 | ||
|
|
a8fed3256e | ||
|
|
1626836a71 | ||
|
|
66161da2a7 | ||
|
|
6bbdfe1797 | ||
|
|
7ea28d34b2 | ||
|
|
65bf1630b3 | ||
|
|
3972fe1fd0 | ||
|
|
5ced739a57 | ||
|
|
b7bf251798 | ||
|
|
90742fc87e | ||
|
|
f7f356e2e2 | ||
|
|
1fad7facd6 | ||
|
|
153ab81a9f | ||
|
|
8d73d67246 | ||
|
|
0d46943bb8 | ||
|
|
deb45e471c | ||
|
|
3601eabbf8 | ||
|
|
eb2f3cef89 | ||
|
|
85be5965b0 | ||
|
|
1e34c1ef1b | ||
|
|
5f852a1c05 | ||
|
|
1d90ea6f1d | ||
|
|
65eee22396 | ||
|
|
cb938266c7 | ||
|
|
8093e2ed5a | ||
|
|
80af726877 | ||
|
|
3f5dc78744 | ||
|
|
563adc6aaa | ||
|
|
67f806b0a5 | ||
|
|
c7dc536640 | ||
|
|
6920a5f8b1 | ||
|
|
2b7a230977 | ||
|
|
1e16185fdd | ||
|
|
60c2592694 | ||
|
|
fc0ad4956e | ||
|
|
43aa90fc3f | ||
|
|
c28bdbd7e6 | ||
|
|
857c7b3a3a | ||
|
|
83cac93ca8 | ||
|
|
d34fa3e7df | ||
|
|
75cb8bb5ea | ||
|
|
2a99cc6a25 | ||
|
|
b5f98d1270 | ||
|
|
ef697319da | ||
|
|
efd68f9fad | ||
|
|
3a74e049ab | ||
|
|
3b1b308357 | ||
|
|
5a068431ed | ||
|
|
25f086a64f | ||
|
|
a93d4d7e9d | ||
|
|
54781cf51a | ||
|
|
ad7d19c317 | ||
|
|
58496e56d2 | ||
|
|
2c18a41613 | ||
|
|
c65eecc295 | ||
|
|
cac45d9ced | ||
|
|
02d42f87d3 | ||
|
|
9c439f8d0a | ||
|
|
cf998f03b6 | ||
|
|
f9d1f2414a | ||
|
|
13b25c0614 | ||
|
|
607b631114 | ||
|
|
99e88bb499 | ||
|
|
4f84cf011d | ||
|
|
417ca86c30 | ||
|
|
7df37f8254 | ||
|
|
0726a9f774 | ||
|
|
f8cc1d6046 | ||
|
|
9383d0bcfc | ||
|
|
f615990c00 | ||
|
|
8feb34e7a8 | ||
|
|
a5523b04ef | ||
|
|
af518fbd2b | ||
|
|
8c37d6923b | ||
|
|
299ef95f25 | ||
|
|
8f81b194d3 | ||
|
|
6bc3e31537 | ||
|
|
840b29860f | ||
|
|
15460c8f97 | ||
|
|
a77ac21e0d | ||
|
|
2b6d8f6824 | ||
|
+19 |
4c91beeda9 | ||
|
|
d0cf79a991 | ||
|
|
86986d6337 | ||
|
|
5408e81de7 | ||
|
|
178c3114d9 | ||
|
|
357a33f454 | ||
|
|
4fccda26c6 | ||
|
|
f94c01b452 | ||
|
|
3ca68e7788 | ||
|
|
a4ba312579 | ||
|
|
a227fd7028 | ||
|
|
6b44b5786e | ||
|
|
5feba46b53 | ||
|
|
0cbf53b7a7 | ||
|
|
321ccbf5e2 | ||
|
|
57268bf4ea | ||
|
|
56fd9c7813 | ||
|
|
32db70037d | ||
|
|
778823e623 | ||
|
|
545826d319 | ||
|
|
dbef1de286 | ||
|
|
b0bd3df226 | ||
|
|
167bcf2863 | ||
|
|
ff485d5ff8 | ||
|
|
184e4ddd83 | ||
|
|
aed2c7ca9b | ||
|
|
e1f9ccd337 | ||
|
|
df14561560 | ||
|
|
c8ff02cc35 | ||
|
|
d770ef27c7 | ||
|
|
71edf2fb48 | ||
|
|
c79e1925d9 | ||
|
|
3d649d518f | ||
|
|
947f8176dc | ||
|
|
d5bde83ff5 | ||
|
|
c3321771ef | ||
|
|
b6e5534a8c | ||
|
|
f10d786d82 | ||
|
|
1a7f560372 | ||
|
|
38c7ad0ed6 | ||
|
|
bcc24705a5 | ||
|
|
8137200cb8 | ||
|
|
bfbce2e314 | ||
|
|
f86291546d | ||
|
|
640dd5f7d7 | ||
|
+1 |
15297cb56f | ||
|
|
6d299eac67 | ||
|
|
31fe15bb73 | ||
|
|
97983d8014 | ||
|
|
c366e53326 | ||
|
|
0adcadb1ce | ||
|
|
21d5a34fec | ||
|
|
648148af8a | ||
|
|
5b03b1fe52 | ||
|
|
f96a8593f2 | ||
|
|
4954723f97 | ||
|
|
e047397642 | ||
|
|
75f3d123f3 | ||
|
|
6b73ac3850 | ||
|
|
44941b79cd | ||
|
|
65adfa894f | ||
|
|
6df0449408 | ||
|
|
a092f6bb22 | ||
|
|
927a4e47b8 | ||
|
|
df0ee74b30 | ||
|
|
af18f061cf | ||
|
|
139e45dff8 | ||
|
|
0df8719fc8 | ||
|
|
b12cac3e00 | ||
|
|
28c4acf5c4 | ||
|
|
579a0af516 | ||
|
|
73adeee385 | ||
|
|
bb3d6d5326 | ||
|
|
7cbab4838c | ||
|
|
4ce554e09d | ||
|
|
eb145f8adc | ||
|
|
a60e7cfe62 | ||
|
|
53231ae4b1 | ||
|
|
39139cc2ea | ||
|
|
5280caf9d8 | ||
|
|
ba016fd91a | ||
|
|
e2cf401ad3 | ||
|
|
4a4b1f4a87 | ||
|
|
779e446d14 | ||
|
|
f73d18384d | ||
|
|
11bb2ee84e | ||
|
|
2361776992 | ||
|
|
a324c4af10 | ||
|
|
5df5ab2790 | ||
|
|
74ea6206d9 | ||
|
|
b238744445 | ||
|
|
94b7149958 | ||
|
|
ac611c4b62 | ||
|
|
89df5ef669 | ||
|
|
be5f0a9ade | ||
|
|
bfc3eebd54 | ||
|
|
538a5f0dfc | ||
|
|
b7db22055d | ||
|
|
b008c7a170 | ||
|
|
c8a9cdc148 | ||
|
|
0703e0e6d7 | ||
|
|
644fa5b54e | ||
|
|
f514bc230b | ||
|
|
0022148323 | ||
|
|
9d06c1bf34 | ||
|
|
1d30342c00 | ||
|
|
e9d4485bb5 | ||
|
|
7b03980e0a | ||
|
|
004179c045 | ||
|
|
8f630bfcf3 | ||
|
|
7bbfe99fbe | ||
|
|
caae6bc597 | ||
|
|
200e79e800 | ||
|
|
c19fc62683 | ||
|
|
4cf01e7e53 | ||
|
|
ad4b1d9c2b | ||
|
|
68733a6c51 | ||
|
|
22617076f8 | ||
|
|
6f5a7672b4 | ||
|
|
e08c050720 | ||
|
|
28b4f37a93 | ||
|
|
5dd06edd00 | ||
|
|
eeb7373043 | ||
|
|
dbded86dcb | ||
|
|
45fbc0f9d3 | ||
|
|
61b39acc7d | ||
|
|
8af9e7fbdc | ||
|
|
1f7ed6888a | ||
|
|
31bf51b3f2 | ||
|
|
334a4f04cd | ||
|
|
b18742c211 | ||
|
|
03084f6d3b | ||
|
|
94d7b71aa8 | ||
|
|
415686dd06 | ||
|
|
b2f2f6b305 | ||
|
|
df400850c1 | ||
|
|
6ab2f02dbc | ||
|
|
d7d6fe7f0f | ||
|
|
d44ceb6eb2 | ||
|
|
4fd0a8276b | ||
|
|
1d219a93ab | ||
|
|
f710cd6ecb | ||
|
|
571c1ac34c | ||
|
|
fb635987d1 | ||
|
|
a922751afc | ||
|
|
c1e3f56324 | ||
|
|
d0562e1ee6 | ||
|
|
4eb4c4b584 | ||
|
|
69a42e1fd2 | ||
|
|
fd498bebad | ||
|
|
23a8b5a66f | ||
|
|
e1e8d6124d | ||
|
|
10b2eae70c | ||
|
|
cfda9bb8ef | ||
|
|
d1edd386b6 | ||
|
|
b6a1020fc5 | ||
|
|
91ad861b26 | ||
|
|
d54ae5dad8 | ||
|
|
c8079d4115 | ||
|
|
90778a4e78 | ||
|
|
63a97a54e1 | ||
|
|
a2e8e232f1 | ||
|
|
7efc3e3770 | ||
|
|
3d58c6e916 | ||
|
|
c038cfe69a | ||
|
|
0770f25e79 | ||
|
|
8a9830282a | ||
|
|
8894a0b711 | ||
|
|
57a3ff8dfc | ||
|
|
6cff13623f | ||
|
|
b312f226b4 | ||
|
|
b627fa720b | ||
|
|
9faf178bdc | ||
|
|
c98f134b40 | ||
|
|
5838b26d90 | ||
|
|
7221fc4d4b | ||
|
|
a417760887 | ||
|
|
04d2dd3b1c | ||
|
|
6b88d37b73 | ||
|
|
d407ec1975 | ||
|
|
6d6a0734b0 | ||
|
|
0157a769c3 | ||
|
|
73932dd1c3 | ||
|
|
bc2abf3db4 | ||
|
|
073eb2c672 | ||
|
|
4744010295 | ||
|
|
d8d02cd6ec | ||
|
|
3e3299f549 | ||
|
|
eefc08087d | ||
|
|
fb6d199d36 | ||
|
|
fb3bf783dd | ||
|
|
fc268d43d0 | ||
|
|
c38aff7e52 | ||
|
|
7d4600f8c2 | ||
|
|
ff50ba4002 | ||
|
|
5c401b8e34 | ||
|
|
c96ebf15fd | ||
|
|
3e4239daf8 | ||
|
|
ab97c0126f | ||
|
|
d34d694731 | ||
|
|
e545897d4e | ||
|
|
caa6ec0e8a | ||
|
|
afbd9e2180 | ||
|
|
64116cd0d3 |
@@ -20,7 +20,7 @@ ENV PIP_ROOT_USER_ACTION=ignore
|
|||||||
RUN apt-get update && apt-get install --no-install-recommends -y \
|
RUN apt-get update && apt-get install --no-install-recommends -y \
|
||||||
cmake git zip libgpiod-dev libbluetooth-dev libi2c-dev \
|
cmake git zip libgpiod-dev libbluetooth-dev libi2c-dev \
|
||||||
libunistring-dev libmicrohttpd-dev libgnutls28-dev libgcrypt20-dev \
|
libunistring-dev libmicrohttpd-dev libgnutls28-dev libgcrypt20-dev \
|
||||||
libusb-1.0-0-dev libssl-dev pkg-config libsqlite3-dev && \
|
libusb-1.0-0-dev libssl-dev pkg-config libsqlite3-dev libsdl2-dev && \
|
||||||
apt-get clean && rm -rf /var/lib/apt/lists/* && \
|
apt-get clean && rm -rf /var/lib/apt/lists/* && \
|
||||||
pip install --no-cache-dir -U \
|
pip install --no-cache-dir -U \
|
||||||
platformio==6.1.16 \
|
platformio==6.1.16 \
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ runs:
|
|||||||
id: version
|
id: version
|
||||||
|
|
||||||
- name: Store binaries as an artifact
|
- name: Store binaries as an artifact
|
||||||
uses: actions/upload-artifact@v6
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: firmware-${{ inputs.arch }}-${{ inputs.board }}-${{ steps.version.outputs.long }}
|
name: firmware-${{ inputs.arch }}-${{ inputs.board }}-${{ steps.version.outputs.long }}
|
||||||
overwrite: true
|
overwrite: true
|
||||||
|
|||||||
@@ -4,16 +4,16 @@
|
|||||||
|
|
||||||
- Before starting on some new big chunk of code, it it is optional but highly recommended to open an issue first
|
- Before starting on some new big chunk of code, it it is optional but highly recommended to open an issue first
|
||||||
to say "Hey, I think this idea X should be implemented and I'm starting work on it. My general plan is Y, any feedback
|
to say "Hey, I think this idea X should be implemented and I'm starting work on it. My general plan is Y, any feedback
|
||||||
is appreciated." This will allow other devs to potentially save you time by not accidentially duplicating work etc...
|
is appreciated." This will allow other devs to potentially save you time by not accidentally duplicating work etc...
|
||||||
- Please do not check in files that don't have real changes
|
- Please do not check in files that don't have real changes
|
||||||
- Please do not reformat lines that you didn't have to change the code on
|
- Please do not reformat lines that you didn't have to change the code on
|
||||||
- We recommend using the [Visual Studio Code](https://platformio.org/install/ide?install=vscode) editor along with the ['Trunk Check' extension](https://marketplace.visualstudio.com/items?itemName=trunk.io) (In beta for windows, WSL2 for the linux version),
|
- We recommend using the [Visual Studio Code](https://platformio.org/install/ide?install=vscode) editor along with the ['Trunk Check' extension](https://marketplace.visualstudio.com/items?itemName=trunk.io) (In beta for windows, WSL2 for the Linux version),
|
||||||
because it automatically follows our indentation rules and its auto reformatting will not cause spurious changes to lines.
|
because it automatically follows our indentation rules and its auto reformatting will not cause spurious changes to lines.
|
||||||
- If your PR fixes a bug, mention "fixes #bugnum" somewhere in your pull request description.
|
- If your PR fixes a bug, mention "fixes #bugnum" somewhere in your pull request description.
|
||||||
- If your other co-developers have comments on your PR please tweak as needed.
|
- If your other co-developers have comments on your PR please tweak as needed.
|
||||||
- Please also enable "Allow edits by maintainers".
|
- Please also enable "Allow edits by maintainers".
|
||||||
- Please do not submit untested code.
|
- Please do not submit untested code.
|
||||||
- If you do not have the affected hardware to test your code changes adequately against regressions, please indicate this, so that contributors and commnunity members can help test your changes.
|
- If you do not have the affected hardware to test your code changes adequately against regressions, please indicate this, so that contributors and community members can help test your changes.
|
||||||
- If your PR gets accepted you can request a "Contributor" role in the Meshtastic Discord
|
- If your PR gets accepted you can request a "Contributor" role in the Meshtastic Discord
|
||||||
|
|
||||||
## 🤝 Attestations
|
## 🤝 Attestations
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ jobs:
|
|||||||
sudo mk-build-deps --install --remove --tool='apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes' debian/control
|
sudo mk-build-deps --install --remove --tool='apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes' debian/control
|
||||||
|
|
||||||
- name: Import GPG key
|
- name: Import GPG key
|
||||||
uses: crazy-max/ghaction-import-gpg@v6
|
uses: crazy-max/ghaction-import-gpg@v7
|
||||||
with:
|
with:
|
||||||
gpg_private_key: ${{ secrets.PPA_GPG_PRIVATE_KEY }}
|
gpg_private_key: ${{ secrets.PPA_GPG_PRIVATE_KEY }}
|
||||||
id: gpg
|
id: gpg
|
||||||
@@ -64,7 +64,7 @@ jobs:
|
|||||||
PKG_VERSION: ${{ steps.version.outputs.deb }}
|
PKG_VERSION: ${{ steps.version.outputs.deb }}
|
||||||
|
|
||||||
- name: Store binaries as an artifact
|
- name: Store binaries as an artifact
|
||||||
uses: actions/upload-artifact@v6
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
|
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
|
||||||
overwrite: true
|
overwrite: true
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ jobs:
|
|||||||
echo "</details>" >> $GITHUB_STEP_SUMMARY
|
echo "</details>" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
- name: Store binaries as an artifact
|
- name: Store binaries as an artifact
|
||||||
uses: actions/upload-artifact@v6
|
uses: actions/upload-artifact@v7
|
||||||
id: upload-firmware
|
id: upload-firmware
|
||||||
with:
|
with:
|
||||||
name: firmware-${{ inputs.platform }}-${{ inputs.pio_env }}-${{ inputs.version }}
|
name: firmware-${{ inputs.platform }}-${{ inputs.pio_env }}-${{ inputs.version }}
|
||||||
@@ -127,7 +127,7 @@ jobs:
|
|||||||
release/device-*.bat
|
release/device-*.bat
|
||||||
|
|
||||||
- name: Store manifests as an artifact
|
- name: Store manifests as an artifact
|
||||||
uses: actions/upload-artifact@v6
|
uses: actions/upload-artifact@v7
|
||||||
id: upload-manifest
|
id: upload-manifest
|
||||||
with:
|
with:
|
||||||
name: manifest-${{ inputs.platform }}-${{ inputs.pio_env }}-${{ inputs.version }}
|
name: manifest-${{ inputs.platform }}-${{ inputs.pio_env }}-${{ inputs.version }}
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ jobs:
|
|||||||
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@v7
|
- uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
path: ./
|
path: ./
|
||||||
pattern: firmware-*-*
|
pattern: firmware-*-*
|
||||||
@@ -111,7 +111,7 @@ jobs:
|
|||||||
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@v6
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: firmware-${{inputs.target}}-${{ needs.version.outputs.long }}
|
name: firmware-${{inputs.target}}-${{ needs.version.outputs.long }}
|
||||||
overwrite: true
|
overwrite: true
|
||||||
@@ -127,7 +127,7 @@ jobs:
|
|||||||
./Meshtastic_nRF52_factory_erase*.uf2
|
./Meshtastic_nRF52_factory_erase*.uf2
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
|
|
||||||
- uses: actions/download-artifact@v7
|
- uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
pattern: firmware-*-${{ needs.version.outputs.long }}
|
pattern: firmware-*-${{ needs.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
@@ -146,7 +146,7 @@ jobs:
|
|||||||
run: zip -j -9 -r ./firmware-${{inputs.target}}-${{ needs.version.outputs.long }}.zip ./output
|
run: zip -j -9 -r ./firmware-${{inputs.target}}-${{ needs.version.outputs.long }}.zip ./output
|
||||||
|
|
||||||
- name: Repackage in single elfs zip
|
- name: Repackage in single elfs zip
|
||||||
uses: actions/upload-artifact@v6
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: debug-elfs-${{inputs.target}}-${{ needs.version.outputs.long }}.zip
|
name: debug-elfs-${{inputs.target}}-${{ needs.version.outputs.long }}.zip
|
||||||
overwrite: true
|
overwrite: true
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ jobs:
|
|||||||
series:
|
series:
|
||||||
- jammy # 22.04 LTS
|
- jammy # 22.04 LTS
|
||||||
- noble # 24.04 LTS
|
- noble # 24.04 LTS
|
||||||
- plucky # 25.04
|
|
||||||
- questing # 25.10
|
- questing # 25.10
|
||||||
|
- resolute # 26.04 LTS
|
||||||
uses: ./.github/workflows/package_ppa.yml
|
uses: ./.github/workflows/package_ppa.yml
|
||||||
with:
|
with:
|
||||||
ppa_repo: ppa:meshtastic/daily
|
ppa_repo: ppa:meshtastic/daily
|
||||||
|
|||||||
@@ -60,16 +60,16 @@ jobs:
|
|||||||
|
|
||||||
- name: Docker login
|
- name: Docker login
|
||||||
if: ${{ inputs.push }}
|
if: ${{ inputs.push }}
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v4
|
||||||
with:
|
with:
|
||||||
username: meshtastic
|
username: meshtastic
|
||||||
password: ${{ secrets.DOCKER_FIRMWARE_TOKEN }}
|
password: ${{ secrets.DOCKER_FIRMWARE_TOKEN }}
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v4
|
||||||
|
|
||||||
- name: Docker setup
|
- name: Docker setup
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v4
|
||||||
|
|
||||||
- name: Sanitize platform string
|
- name: Sanitize platform string
|
||||||
id: sanitize_platform
|
id: sanitize_platform
|
||||||
@@ -78,7 +78,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Docker tag
|
- name: Docker tag
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v6
|
||||||
with:
|
with:
|
||||||
images: meshtastic/meshtasticd
|
images: meshtastic/meshtasticd
|
||||||
tags: |
|
tags: |
|
||||||
@@ -86,7 +86,7 @@ jobs:
|
|||||||
flavor: latest=false
|
flavor: latest=false
|
||||||
|
|
||||||
- name: Docker build and push
|
- name: Docker build and push
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v7
|
||||||
id: docker_variant
|
id: docker_variant
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
|
|||||||
@@ -139,14 +139,14 @@ jobs:
|
|||||||
id: tags
|
id: tags
|
||||||
|
|
||||||
- name: Docker login
|
- name: Docker login
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v4
|
||||||
with:
|
with:
|
||||||
username: meshtastic
|
username: meshtastic
|
||||||
password: ${{ secrets.DOCKER_FIRMWARE_TOKEN }}
|
password: ${{ secrets.DOCKER_FIRMWARE_TOKEN }}
|
||||||
|
|
||||||
- name: Docker meta (Debian)
|
- name: Docker meta (Debian)
|
||||||
id: meta_debian
|
id: meta_debian
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v6
|
||||||
with:
|
with:
|
||||||
images: meshtastic/meshtasticd
|
images: meshtastic/meshtasticd
|
||||||
tags: |
|
tags: |
|
||||||
@@ -167,7 +167,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Docker meta (Alpine)
|
- name: Docker meta (Alpine)
|
||||||
id: meta_alpine
|
id: meta_alpine
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v6
|
||||||
with:
|
with:
|
||||||
images: meshtastic/meshtasticd
|
images: meshtastic/meshtasticd
|
||||||
tags: |
|
tags: |
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ jobs:
|
|||||||
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@v7
|
- uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
path: ./
|
path: ./
|
||||||
pattern: firmware-${{matrix.arch}}-*
|
pattern: firmware-${{matrix.arch}}-*
|
||||||
@@ -187,7 +187,7 @@ jobs:
|
|||||||
run: ls -R
|
run: ls -R
|
||||||
|
|
||||||
- name: Repackage in single firmware zip
|
- name: Repackage in single firmware zip
|
||||||
uses: actions/upload-artifact@v6
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
overwrite: true
|
overwrite: true
|
||||||
@@ -205,7 +205,7 @@ jobs:
|
|||||||
./Meshtastic_nRF52_factory_erase*.uf2
|
./Meshtastic_nRF52_factory_erase*.uf2
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
|
|
||||||
- uses: actions/download-artifact@v7
|
- uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
@@ -224,7 +224,7 @@ jobs:
|
|||||||
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.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@v6
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
overwrite: true
|
overwrite: true
|
||||||
@@ -250,13 +250,13 @@ jobs:
|
|||||||
filter: blob:none # means we download all the git history but none of the commit (except ones with checkout like the head)
|
filter: blob:none # means we download all the git history but none of the commit (except ones with checkout like the head)
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Download the current manifests
|
- name: Download the current manifests
|
||||||
uses: actions/download-artifact@v7
|
uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
path: ./manifests-new/
|
path: ./manifests-new/
|
||||||
pattern: manifest-*
|
pattern: manifest-*
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
- name: Upload combined manifests for later commit and global stats crunching.
|
- name: Upload combined manifests for later commit and global stats crunching.
|
||||||
uses: actions/upload-artifact@v6
|
uses: actions/upload-artifact@v7
|
||||||
id: upload-manifest
|
id: upload-manifest
|
||||||
with:
|
with:
|
||||||
name: manifests-${{ github.sha }}
|
name: manifests-${{ github.sha }}
|
||||||
@@ -324,14 +324,14 @@ jobs:
|
|||||||
body: ${{ steps.release_notes.outputs.notes }}
|
body: ${{ steps.release_notes.outputs.notes }}
|
||||||
|
|
||||||
- name: Download source deb
|
- name: Download source deb
|
||||||
uses: actions/download-artifact@v7
|
uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
pattern: firmware-debian-${{ needs.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@v7
|
uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }}
|
pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
@@ -355,7 +355,7 @@ jobs:
|
|||||||
}' > firmware-${{ needs.version.outputs.long }}.json
|
}' > firmware-${{ needs.version.outputs.long }}.json
|
||||||
|
|
||||||
- name: Save Release manifest artifact
|
- name: Save Release manifest artifact
|
||||||
uses: actions/upload-artifact@v6
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: manifest-${{ needs.version.outputs.long }}
|
name: manifest-${{ needs.version.outputs.long }}
|
||||||
overwrite: true
|
overwrite: true
|
||||||
@@ -396,7 +396,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
python-version: 3.x
|
python-version: 3.x
|
||||||
|
|
||||||
- uses: actions/download-artifact@v7
|
- uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
pattern: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
pattern: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
@@ -413,7 +413,7 @@ jobs:
|
|||||||
- name: Zip firmware
|
- name: Zip firmware
|
||||||
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
||||||
|
|
||||||
- uses: actions/download-artifact@v7
|
- uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
@@ -454,14 +454,14 @@ jobs:
|
|||||||
python-version: 3.x
|
python-version: 3.x
|
||||||
|
|
||||||
- name: Get firmware artifacts
|
- name: Get firmware artifacts
|
||||||
uses: actions/download-artifact@v7
|
uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
|
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
path: ./publish
|
path: ./publish
|
||||||
|
|
||||||
- name: Get manifest artifact
|
- name: Get manifest artifact
|
||||||
uses: actions/download-artifact@v7
|
uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
pattern: manifest-${{ needs.version.outputs.long }}
|
pattern: manifest-${{ needs.version.outputs.long }}
|
||||||
path: ./publish
|
path: ./publish
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ jobs:
|
|||||||
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@v7
|
- uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
path: ./
|
path: ./
|
||||||
pattern: firmware-${{matrix.arch}}-*
|
pattern: firmware-${{matrix.arch}}-*
|
||||||
@@ -160,7 +160,7 @@ jobs:
|
|||||||
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@v6
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
overwrite: true
|
overwrite: true
|
||||||
@@ -176,7 +176,7 @@ jobs:
|
|||||||
./Meshtastic_nRF52_factory_erase*.uf2
|
./Meshtastic_nRF52_factory_erase*.uf2
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
|
|
||||||
- uses: actions/download-artifact@v7
|
- uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
@@ -195,7 +195,7 @@ jobs:
|
|||||||
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.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@v6
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
overwrite: true
|
overwrite: true
|
||||||
@@ -235,14 +235,14 @@ jobs:
|
|||||||
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@v7
|
uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
pattern: firmware-debian-${{ needs.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@v7
|
uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }}
|
pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
@@ -292,7 +292,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
python-version: 3.x
|
python-version: 3.x
|
||||||
|
|
||||||
- uses: actions/download-artifact@v7
|
- uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
pattern: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
pattern: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
@@ -309,7 +309,7 @@ jobs:
|
|||||||
- name: Zip firmware
|
- name: Zip firmware
|
||||||
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
||||||
|
|
||||||
- uses: actions/download-artifact@v7
|
- uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
@@ -347,7 +347,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
python-version: 3.x
|
python-version: 3.x
|
||||||
|
|
||||||
- uses: actions/download-artifact@v7
|
- uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
|
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|||||||
@@ -0,0 +1,213 @@
|
|||||||
|
name: Issue Triage (Models)
|
||||||
|
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types: [opened]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
models: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.issue.number }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
triage:
|
||||||
|
if: ${{ github.repository == 'meshtastic/firmware' && github.event.issue.user.type != 'Bot' }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────
|
||||||
|
# Step 1: Quality check (spam/AI-slop detection) - runs first, exits early if spam
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────
|
||||||
|
- name: Detect spam or low-quality content
|
||||||
|
uses: actions/ai-inference@v2
|
||||||
|
id: quality
|
||||||
|
continue-on-error: true
|
||||||
|
with:
|
||||||
|
max-tokens: 20
|
||||||
|
prompt: |
|
||||||
|
Is this GitHub issue spam, AI-generated slop, or low quality?
|
||||||
|
|
||||||
|
Title: ${{ github.event.issue.title }}
|
||||||
|
Body: ${{ github.event.issue.body }}
|
||||||
|
|
||||||
|
Respond with exactly one of: spam, ai-generated, needs-review, ok
|
||||||
|
system-prompt: You detect spam and low-quality contributions. Be conservative - only flag obvious spam or AI slop.
|
||||||
|
model: openai/gpt-4o-mini
|
||||||
|
|
||||||
|
- name: Apply quality label if needed
|
||||||
|
if: steps.quality.outputs.response != '' && steps.quality.outputs.response != 'ok'
|
||||||
|
uses: actions/github-script@v8
|
||||||
|
env:
|
||||||
|
QUALITY_LABEL: ${{ steps.quality.outputs.response }}
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const label = (process.env.QUALITY_LABEL || '').trim().toLowerCase();
|
||||||
|
const labelMeta = {
|
||||||
|
'spam': { color: 'd73a4a', description: 'Possible spam' },
|
||||||
|
'ai-generated': { color: 'fbca04', description: 'Possible AI-generated low-quality content' },
|
||||||
|
'needs-review': { color: 'f9d0c4', description: 'Needs human review' },
|
||||||
|
};
|
||||||
|
const meta = labelMeta[label];
|
||||||
|
if (!meta) return;
|
||||||
|
|
||||||
|
// Ensure label exists
|
||||||
|
try {
|
||||||
|
await github.rest.issues.getLabel({ owner: context.repo.owner, repo: context.repo.repo, name: label });
|
||||||
|
} catch (e) {
|
||||||
|
if (e.status !== 404) throw e;
|
||||||
|
await github.rest.issues.createLabel({ owner: context.repo.owner, repo: context.repo.repo, name: label, color: meta.color, description: meta.description });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply label
|
||||||
|
await github.rest.issues.addLabels({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.payload.issue.number, labels: [label] });
|
||||||
|
|
||||||
|
// Set output to skip remaining steps
|
||||||
|
core.setOutput('is_spam', 'true');
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────
|
||||||
|
# Step 2: Duplicate detection - only if not spam
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────
|
||||||
|
- name: Detect duplicate issues
|
||||||
|
if: steps.quality.outputs.response == 'ok' || steps.quality.outputs.response == ''
|
||||||
|
uses: pelikhan/action-genai-issue-dedup@bdb3b5d9451c1090ffcdf123d7447a5e7c7a2528 # v0.0.19
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────
|
||||||
|
# Step 3: Completeness check + auto-labeling (combined into one AI call)
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────
|
||||||
|
- name: Determine if completeness check should be skipped
|
||||||
|
if: steps.quality.outputs.response == 'ok' || steps.quality.outputs.response == ''
|
||||||
|
uses: actions/github-script@v8
|
||||||
|
id: check-skip
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const title = (context.payload.issue.title || '').toLowerCase();
|
||||||
|
const labels = (context.payload.issue.labels || []).map(label => label.name);
|
||||||
|
const hasFeatureRequest = title.includes('feature request');
|
||||||
|
const hasEnhancement = labels.includes('enhancement');
|
||||||
|
const shouldSkip = hasFeatureRequest && hasEnhancement;
|
||||||
|
core.setOutput('should_skip', shouldSkip ? 'true' : 'false');
|
||||||
|
|
||||||
|
- name: Analyze issue completeness and determine labels
|
||||||
|
if: (steps.quality.outputs.response == 'ok' || steps.quality.outputs.response == '') && steps.check-skip.outputs.should_skip != 'true'
|
||||||
|
uses: actions/ai-inference@v2
|
||||||
|
id: analysis
|
||||||
|
continue-on-error: true
|
||||||
|
with:
|
||||||
|
prompt: |
|
||||||
|
Analyze this GitHub issue for completeness and determine if it needs labels.
|
||||||
|
|
||||||
|
IMPORTANT: Distinguish between:
|
||||||
|
- Device/firmware bugs (crashes, reboots, lockups, radio/GPS/display/power issues) - these need device logs
|
||||||
|
- Build/release/packaging issues (missing files, CI failures, download problems) - these do NOT need device logs
|
||||||
|
- Documentation or website issues - these do NOT need device logs
|
||||||
|
|
||||||
|
If this is a device/firmware bug, request device logs and explain how to get them:
|
||||||
|
|
||||||
|
Web Flasher logs:
|
||||||
|
- Go to https://flasher.meshtastic.org
|
||||||
|
- Connect the device via USB and click Connect
|
||||||
|
- Open the device console/log output, reproduce the problem, then copy/download and attach/paste the logs
|
||||||
|
|
||||||
|
Meshtastic CLI logs:
|
||||||
|
- Run: meshtastic --port <serial-port> --noproto
|
||||||
|
- Reproduce the problem, then copy/paste the terminal output
|
||||||
|
|
||||||
|
Also request key context if missing: device model/variant, firmware version, region, steps to reproduce, expected vs actual.
|
||||||
|
|
||||||
|
Respond ONLY with valid JSON (no markdown, no code fences):
|
||||||
|
{"complete": true, "comment": "", "label": "none"}
|
||||||
|
OR
|
||||||
|
{"complete": false, "comment": "Your helpful comment", "label": "needs-logs"}
|
||||||
|
|
||||||
|
Use "needs-logs" ONLY if this is a device/firmware bug AND no logs are attached.
|
||||||
|
Use "needs-info" if basic info like firmware version or steps to reproduce are missing.
|
||||||
|
Use "none" if the issue is complete, is a feature request, or is a build/CI/packaging issue.
|
||||||
|
|
||||||
|
Title: ${{ github.event.issue.title }}
|
||||||
|
Body: ${{ github.event.issue.body }}
|
||||||
|
system-prompt: You are a helpful assistant that triages GitHub issues. Be conservative with labels. Only request device logs for actual device/firmware bugs, not for build/release/CI issues.
|
||||||
|
model: openai/gpt-4o-mini
|
||||||
|
|
||||||
|
- name: Process analysis result
|
||||||
|
if: (steps.quality.outputs.response == 'ok' || steps.quality.outputs.response == '') && steps.check-skip.outputs.should_skip != 'true' && steps.analysis.outputs.response != ''
|
||||||
|
uses: actions/github-script@v8
|
||||||
|
id: process
|
||||||
|
env:
|
||||||
|
AI_RESPONSE: ${{ steps.analysis.outputs.response }}
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
let raw = (process.env.AI_RESPONSE || '').trim();
|
||||||
|
|
||||||
|
// Strip markdown code fences if present
|
||||||
|
raw = raw.replace(/^```(?:json)?\s*/i, '').replace(/\s*```$/i, '').trim();
|
||||||
|
|
||||||
|
let complete = true;
|
||||||
|
let comment = '';
|
||||||
|
let label = 'none';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(raw);
|
||||||
|
complete = !!parsed.complete;
|
||||||
|
comment = (parsed.comment ?? '').toString().trim();
|
||||||
|
label = (parsed.label ?? 'none').toString().trim().toLowerCase();
|
||||||
|
} catch {
|
||||||
|
// If JSON parse fails, log warning and don't comment (avoid posting raw JSON)
|
||||||
|
console.log('Failed to parse AI response as JSON:', raw);
|
||||||
|
complete = true;
|
||||||
|
comment = '';
|
||||||
|
label = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate label
|
||||||
|
const allowedLabels = new Set(['needs-logs', 'needs-info', 'none']);
|
||||||
|
if (!allowedLabels.has(label)) label = 'none';
|
||||||
|
|
||||||
|
// Only comment if we have a valid parsed comment (not raw JSON)
|
||||||
|
const shouldComment = !complete && comment.length > 0 && !comment.startsWith('{');
|
||||||
|
core.setOutput('should_comment', shouldComment ? 'true' : 'false');
|
||||||
|
core.setOutput('comment_body', comment);
|
||||||
|
core.setOutput('label', label);
|
||||||
|
|
||||||
|
- name: Apply triage label
|
||||||
|
if: steps.process.outputs.label != '' && steps.process.outputs.label != 'none'
|
||||||
|
uses: actions/github-script@v8
|
||||||
|
env:
|
||||||
|
LABEL_NAME: ${{ steps.process.outputs.label }}
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const label = process.env.LABEL_NAME;
|
||||||
|
const labelMeta = {
|
||||||
|
'needs-logs': { color: 'cfd3d7', description: 'Device logs requested for triage' },
|
||||||
|
'needs-info': { color: 'f9d0c4', description: 'More information requested for triage' },
|
||||||
|
};
|
||||||
|
const meta = labelMeta[label];
|
||||||
|
if (!meta) return;
|
||||||
|
|
||||||
|
// Ensure label exists
|
||||||
|
try {
|
||||||
|
await github.rest.issues.getLabel({ owner: context.repo.owner, repo: context.repo.repo, name: label });
|
||||||
|
} catch (e) {
|
||||||
|
if (e.status !== 404) throw e;
|
||||||
|
await github.rest.issues.createLabel({ owner: context.repo.owner, repo: context.repo.repo, name: label, color: meta.color, description: meta.description });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply label
|
||||||
|
await github.rest.issues.addLabels({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.payload.issue.number, labels: [label] });
|
||||||
|
|
||||||
|
- name: Comment on issue
|
||||||
|
if: steps.process.outputs.should_comment == 'true'
|
||||||
|
uses: actions/github-script@v8
|
||||||
|
env:
|
||||||
|
COMMENT_BODY: ${{ steps.process.outputs.comment_body }}
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
await github.rest.issues.createComment({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: context.payload.issue.number,
|
||||||
|
body: process.env.COMMENT_BODY
|
||||||
|
});
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
name: PR Triage (Models)
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types: [opened]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
|
issues: write
|
||||||
|
models: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
triage:
|
||||||
|
if: ${{ github.repository == 'meshtastic/firmware' && github.event.pull_request.user.type != 'Bot' }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────
|
||||||
|
# Step 1: Check if PR already has automation/type labels (skip if so)
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────
|
||||||
|
- name: Check existing labels
|
||||||
|
uses: actions/github-script@v8
|
||||||
|
id: check-labels
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const skipLabels = new Set(['automation']);
|
||||||
|
const typeLabels = new Set(['bugfix', 'hardware-support', 'enhancement', 'dependencies', 'submodules', 'github_actions', 'trunk', 'cleanup']);
|
||||||
|
const prLabels = context.payload.pull_request.labels.map(l => l.name);
|
||||||
|
|
||||||
|
const shouldSkipAll = prLabels.some(l => skipLabels.has(l));
|
||||||
|
const hasTypeLabel = prLabels.some(l => typeLabels.has(l));
|
||||||
|
|
||||||
|
core.setOutput('skip_all', shouldSkipAll ? 'true' : 'false');
|
||||||
|
core.setOutput('has_type_label', hasTypeLabel ? 'true' : 'false');
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────
|
||||||
|
# Step 2: Quality check (spam/AI-slop detection)
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────
|
||||||
|
- name: Detect spam or low-quality content
|
||||||
|
if: steps.check-labels.outputs.skip_all != 'true'
|
||||||
|
uses: actions/ai-inference@v2
|
||||||
|
id: quality
|
||||||
|
continue-on-error: true
|
||||||
|
with:
|
||||||
|
max-tokens: 20
|
||||||
|
prompt: |
|
||||||
|
Is this GitHub pull request spam, AI-generated slop, or low quality?
|
||||||
|
|
||||||
|
Title: ${{ github.event.pull_request.title }}
|
||||||
|
Body: ${{ github.event.pull_request.body }}
|
||||||
|
|
||||||
|
Respond with exactly one of: spam, ai-generated, needs-review, ok
|
||||||
|
system-prompt: You detect spam and low-quality contributions. Be conservative - only flag obvious spam or AI slop.
|
||||||
|
model: openai/gpt-4o-mini
|
||||||
|
|
||||||
|
- name: Apply quality label if needed
|
||||||
|
if: steps.check-labels.outputs.skip_all != 'true' && steps.quality.outputs.response != '' && steps.quality.outputs.response != 'ok'
|
||||||
|
uses: actions/github-script@v8
|
||||||
|
id: quality-label
|
||||||
|
env:
|
||||||
|
QUALITY_LABEL: ${{ steps.quality.outputs.response }}
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const label = (process.env.QUALITY_LABEL || '').trim().toLowerCase();
|
||||||
|
const labelMeta = {
|
||||||
|
'spam': { color: 'd73a4a', description: 'Possible spam' },
|
||||||
|
'ai-generated': { color: 'fbca04', description: 'Possible AI-generated low-quality content' },
|
||||||
|
'needs-review': { color: 'f9d0c4', description: 'Needs human review' },
|
||||||
|
};
|
||||||
|
const meta = labelMeta[label];
|
||||||
|
if (!meta) return;
|
||||||
|
|
||||||
|
// Ensure label exists
|
||||||
|
try {
|
||||||
|
await github.rest.issues.getLabel({ owner: context.repo.owner, repo: context.repo.repo, name: label });
|
||||||
|
} catch (e) {
|
||||||
|
if (e.status !== 404) throw e;
|
||||||
|
await github.rest.issues.createLabel({ owner: context.repo.owner, repo: context.repo.repo, name: label, color: meta.color, description: meta.description });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply label
|
||||||
|
await github.rest.issues.addLabels({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.payload.pull_request.number, labels: [label] });
|
||||||
|
|
||||||
|
core.setOutput('is_spam', 'true');
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────
|
||||||
|
# Step 3: Auto-label PR type (bugfix/hardware-support/enhancement)
|
||||||
|
# Only skip for spam/ai-generated; still classify needs-review PRs
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────
|
||||||
|
- name: Classify PR for labeling
|
||||||
|
if: steps.check-labels.outputs.skip_all != 'true' && steps.check-labels.outputs.has_type_label != 'true' && steps.quality.outputs.response != 'spam' && steps.quality.outputs.response != 'ai-generated'
|
||||||
|
uses: actions/ai-inference@v2
|
||||||
|
id: classify
|
||||||
|
continue-on-error: true
|
||||||
|
with:
|
||||||
|
max-tokens: 30
|
||||||
|
prompt: |
|
||||||
|
Classify this pull request into exactly one category.
|
||||||
|
|
||||||
|
Return exactly one of: bugfix, hardware-support, enhancement
|
||||||
|
|
||||||
|
Use bugfix if it fixes a bug, crash, or incorrect behavior.
|
||||||
|
Use hardware-support if it adds or improves support for a specific hardware device/variant.
|
||||||
|
Use enhancement if it adds a new feature, improves performance, or refactors code.
|
||||||
|
|
||||||
|
Title: ${{ github.event.pull_request.title }}
|
||||||
|
Body: ${{ github.event.pull_request.body }}
|
||||||
|
system-prompt: You classify pull requests into categories. Be conservative and pick the most appropriate single label.
|
||||||
|
model: openai/gpt-4o-mini
|
||||||
|
|
||||||
|
- name: Apply type label
|
||||||
|
if: steps.check-labels.outputs.skip_all != 'true' && steps.check-labels.outputs.has_type_label != 'true' && steps.classify.outputs.response != ''
|
||||||
|
uses: actions/github-script@v8
|
||||||
|
env:
|
||||||
|
TYPE_LABEL: ${{ steps.classify.outputs.response }}
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const label = (process.env.TYPE_LABEL || '').trim().toLowerCase();
|
||||||
|
const labelMeta = {
|
||||||
|
'bugfix': { color: 'd73a4a', description: 'Bug fix' },
|
||||||
|
'hardware-support': { color: '0e8a16', description: 'Hardware support addition or improvement' },
|
||||||
|
'enhancement': { color: 'a2eeef', description: 'New feature or enhancement' },
|
||||||
|
};
|
||||||
|
const meta = labelMeta[label];
|
||||||
|
if (!meta) return;
|
||||||
|
|
||||||
|
// Ensure label exists
|
||||||
|
try {
|
||||||
|
await github.rest.issues.getLabel({ owner: context.repo.owner, repo: context.repo.repo, name: label });
|
||||||
|
} catch (e) {
|
||||||
|
if (e.status !== 404) throw e;
|
||||||
|
await github.rest.issues.createLabel({ owner: context.repo.owner, repo: context.repo.repo, name: label, color: meta.color, description: meta.description });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply label
|
||||||
|
await github.rest.issues.addLabels({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.payload.pull_request.number, labels: [label] });
|
||||||
@@ -58,7 +58,7 @@ jobs:
|
|||||||
id: version
|
id: version
|
||||||
|
|
||||||
- name: Download artifacts
|
- name: Download artifacts
|
||||||
uses: actions/download-artifact@v7
|
uses: actions/download-artifact@v8
|
||||||
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
|
||||||
|
|||||||
@@ -54,9 +54,19 @@ jobs:
|
|||||||
PLATFORMIO_LIBDEPS_DIR: pio/libdeps
|
PLATFORMIO_LIBDEPS_DIR: pio/libdeps
|
||||||
PLATFORMIO_PACKAGES_DIR: pio/packages
|
PLATFORMIO_PACKAGES_DIR: pio/packages
|
||||||
PLATFORMIO_CORE_DIR: pio/core
|
PLATFORMIO_CORE_DIR: pio/core
|
||||||
|
PLATFORMIO_SETTING_ENABLE_TELEMETRY: 0
|
||||||
|
PLATFORMIO_SETTING_CHECK_PLATFORMIO_INTERVAL: 3650
|
||||||
|
PLATFORMIO_SETTING_CHECK_PRUNE_SYSTEM_THRESHOLD: 10240
|
||||||
|
|
||||||
|
- name: Mangle platformio cache
|
||||||
|
# Add "1" to epoch-timestamps of all downloads in the cache.
|
||||||
|
# This is a hack to prevent internet access at build-time.
|
||||||
|
run: |
|
||||||
|
cp pio/core/.cache/downloads/usage.db pio/core/.cache/downloads/usage.db.bak
|
||||||
|
jq -c 'with_entries(.value |= (. | tostring + "1" | tonumber))' pio/core/.cache/downloads/usage.db.bak > pio/core/.cache/downloads/usage.db
|
||||||
|
|
||||||
- name: Store binaries as an artifact
|
- name: Store binaries as an artifact
|
||||||
uses: actions/upload-artifact@v6
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: platformio-deps-${{ inputs.pio_env }}-${{ steps.version.outputs.long }}
|
name: platformio-deps-${{ inputs.pio_env }}-${{ steps.version.outputs.long }}
|
||||||
overwrite: true
|
overwrite: true
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ jobs:
|
|||||||
sudo apt-get install -y dput
|
sudo apt-get install -y dput
|
||||||
|
|
||||||
- name: Import GPG key
|
- name: Import GPG key
|
||||||
uses: crazy-max/ghaction-import-gpg@v6
|
uses: crazy-max/ghaction-import-gpg@v7
|
||||||
with:
|
with:
|
||||||
gpg_private_key: ${{ secrets.PPA_GPG_PRIVATE_KEY }}
|
gpg_private_key: ${{ secrets.PPA_GPG_PRIVATE_KEY }}
|
||||||
id: gpg
|
id: gpg
|
||||||
@@ -60,7 +60,7 @@ jobs:
|
|||||||
id: version
|
id: version
|
||||||
|
|
||||||
- name: Download artifacts
|
- name: Download artifacts
|
||||||
uses: actions/download-artifact@v7
|
uses: actions/download-artifact@v8
|
||||||
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
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Download test artifacts
|
- name: Download test artifacts
|
||||||
if: needs.native-tests.result != 'skipped'
|
if: needs.native-tests.result != 'skipped'
|
||||||
uses: actions/download-artifact@v7
|
uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
name: platformio-test-report-${{ steps.version.outputs.long }}
|
name: platformio-test-report-${{ steps.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ jobs:
|
|||||||
series:
|
series:
|
||||||
- jammy # 22.04 LTS
|
- jammy # 22.04 LTS
|
||||||
- noble # 24.04 LTS
|
- noble # 24.04 LTS
|
||||||
- plucky # 25.04
|
|
||||||
- questing # 25.10
|
- questing # 25.10
|
||||||
|
- resolute # 26.04 LTS
|
||||||
uses: ./.github/workflows/package_ppa.yml
|
uses: ./.github/workflows/package_ppa.yml
|
||||||
with:
|
with:
|
||||||
ppa_repo: |-
|
ppa_repo: |-
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ jobs:
|
|||||||
|
|
||||||
# step 3
|
# step 3
|
||||||
- name: save report as pipeline artifact
|
- name: save report as pipeline artifact
|
||||||
uses: actions/upload-artifact@v6
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: report.sarif
|
name: report.sarif
|
||||||
overwrite: true
|
overwrite: true
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Stale PR+Issues
|
- name: Stale PR+Issues
|
||||||
uses: actions/stale@v10.1.1
|
uses: actions/stale@v10.2.0
|
||||||
with:
|
with:
|
||||||
days-before-stale: 45
|
days-before-stale: 45
|
||||||
stale-issue-message: This issue has not had any comment or update in the last month. If it is still relevant, please post update comments. If no comments are made, this issue will be closed automagically in 7 days.
|
stale-issue-message: This issue has not had any comment or update in the last month. If it is still relevant, please post update comments. If no comments are made, this issue will be closed automagically in 7 days.
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ jobs:
|
|||||||
id: version
|
id: version
|
||||||
|
|
||||||
- name: Save coverage information
|
- name: Save coverage information
|
||||||
uses: actions/upload-artifact@v6
|
uses: actions/upload-artifact@v7
|
||||||
if: always() # run this step even if previous step failed
|
if: always() # run this step even if previous step failed
|
||||||
with:
|
with:
|
||||||
name: lcov-coverage-info-native-simulator-test-${{ steps.version.outputs.long }}
|
name: lcov-coverage-info-native-simulator-test-${{ steps.version.outputs.long }}
|
||||||
@@ -94,7 +94,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Save test results
|
- name: Save test results
|
||||||
if: always() # run this step even if previous step failed
|
if: always() # run this step even if previous step failed
|
||||||
uses: actions/upload-artifact@v6
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: platformio-test-report-${{ steps.version.outputs.long }}
|
name: platformio-test-report-${{ steps.version.outputs.long }}
|
||||||
overwrite: true
|
overwrite: true
|
||||||
@@ -108,7 +108,7 @@ jobs:
|
|||||||
sed -i -e "s#${PWD}#.#" coverage_tests.info # Make paths relative.
|
sed -i -e "s#${PWD}#.#" coverage_tests.info # Make paths relative.
|
||||||
|
|
||||||
- name: Save coverage information
|
- name: Save coverage information
|
||||||
uses: actions/upload-artifact@v6
|
uses: actions/upload-artifact@v7
|
||||||
if: always() # run this step even if previous step failed
|
if: always() # run this step even if previous step failed
|
||||||
with:
|
with:
|
||||||
name: lcov-coverage-info-native-platformio-tests-${{ steps.version.outputs.long }}
|
name: lcov-coverage-info-native-platformio-tests-${{ steps.version.outputs.long }}
|
||||||
@@ -137,20 +137,20 @@ jobs:
|
|||||||
id: version
|
id: version
|
||||||
|
|
||||||
- name: Download test artifacts
|
- name: Download test artifacts
|
||||||
uses: actions/download-artifact@v7
|
uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
name: platformio-test-report-${{ steps.version.outputs.long }}
|
name: platformio-test-report-${{ steps.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|
||||||
- name: Test Report
|
- name: Test Report
|
||||||
uses: dorny/test-reporter@v2.5.0
|
uses: dorny/test-reporter@v2.6.0
|
||||||
with:
|
with:
|
||||||
name: PlatformIO Tests
|
name: PlatformIO Tests
|
||||||
path: testreport.xml
|
path: testreport.xml
|
||||||
reporter: java-junit
|
reporter: java-junit
|
||||||
|
|
||||||
- name: Download coverage artifacts
|
- name: Download coverage artifacts
|
||||||
uses: actions/download-artifact@v7
|
uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
pattern: lcov-coverage-info-native-*-${{ steps.version.outputs.long }}
|
pattern: lcov-coverage-info-native-*-${{ steps.version.outputs.long }}
|
||||||
path: code-coverage-report
|
path: code-coverage-report
|
||||||
@@ -163,7 +163,7 @@ jobs:
|
|||||||
genhtml --quiet --legend --prefix "${PWD}" code-coverage-report/coverage_src.info --output-directory code-coverage-report
|
genhtml --quiet --legend --prefix "${PWD}" code-coverage-report/coverage_src.info --output-directory code-coverage-report
|
||||||
|
|
||||||
- name: Save Code Coverage Report
|
- name: Save Code Coverage Report
|
||||||
uses: actions/upload-artifact@v6
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: code-coverage-report-${{ steps.version.outputs.long }}
|
name: code-coverage-report-${{ steps.version.outputs.long }}
|
||||||
path: code-coverage-report
|
path: code-coverage-report
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ __pycache__
|
|||||||
*~
|
*~
|
||||||
|
|
||||||
venv/
|
venv/
|
||||||
|
.venv/
|
||||||
release/
|
release/
|
||||||
.vscode/extensions.json
|
.vscode/extensions.json
|
||||||
/compile_commands.json
|
/compile_commands.json
|
||||||
@@ -50,3 +51,6 @@ idf_component.yml
|
|||||||
CMakeLists.txt
|
CMakeLists.txt
|
||||||
/sdkconfig.*
|
/sdkconfig.*
|
||||||
.dummy/*
|
.dummy/*
|
||||||
|
|
||||||
|
# PYTHONPATH used by the Nix shell
|
||||||
|
.python3
|
||||||
|
|||||||
+14
-14
@@ -4,29 +4,29 @@ cli:
|
|||||||
plugins:
|
plugins:
|
||||||
sources:
|
sources:
|
||||||
- id: trunk
|
- id: trunk
|
||||||
ref: v1.7.4
|
ref: v1.7.5
|
||||||
uri: https://github.com/trunk-io/plugins
|
uri: https://github.com/trunk-io/plugins
|
||||||
lint:
|
lint:
|
||||||
enabled:
|
enabled:
|
||||||
- checkov@3.2.497
|
- checkov@3.2.507
|
||||||
- renovate@42.81.8
|
- renovate@43.57.0
|
||||||
- prettier@3.8.0
|
- prettier@3.8.1
|
||||||
- trufflehog@3.92.4
|
- trufflehog@3.93.7
|
||||||
- yamllint@1.38.0
|
- yamllint@1.38.0
|
||||||
- bandit@1.9.2
|
- bandit@1.9.4
|
||||||
- trivy@0.68.2
|
- trivy@0.69.3
|
||||||
- taplo@0.10.0
|
- taplo@0.10.0
|
||||||
- ruff@0.14.11
|
- ruff@0.15.5
|
||||||
- isort@7.0.0
|
- isort@8.0.1
|
||||||
- markdownlint@0.47.0
|
- markdownlint@0.48.0
|
||||||
- oxipng@10.0.0
|
- oxipng@10.1.0
|
||||||
- svgo@4.0.0
|
- svgo@4.0.1
|
||||||
- actionlint@1.7.10
|
- actionlint@1.7.11
|
||||||
- flake8@7.3.0
|
- flake8@7.3.0
|
||||||
- hadolint@2.14.0
|
- hadolint@2.14.0
|
||||||
- shfmt@3.6.0
|
- shfmt@3.6.0
|
||||||
- shellcheck@0.11.0
|
- shellcheck@0.11.0
|
||||||
- black@25.12.0
|
- black@26.1.0
|
||||||
- git-diff-check
|
- git-diff-check
|
||||||
- gitleaks@8.30.0
|
- gitleaks@8.30.0
|
||||||
- clang-format@16.0.3
|
- clang-format@16.0.3
|
||||||
|
|||||||
+2
-2
@@ -14,7 +14,7 @@ RUN apt-get update && apt-get install --no-install-recommends -y \
|
|||||||
curl wget g++ zip git ca-certificates pkg-config \
|
curl wget g++ zip git ca-certificates pkg-config \
|
||||||
libgpiod-dev libyaml-cpp-dev libbluetooth-dev libi2c-dev libuv1-dev \
|
libgpiod-dev libyaml-cpp-dev libbluetooth-dev libi2c-dev libuv1-dev \
|
||||||
libusb-1.0-0-dev libulfius-dev liborcania-dev libssl-dev \
|
libusb-1.0-0-dev libulfius-dev liborcania-dev libssl-dev \
|
||||||
libx11-dev libinput-dev libxkbcommon-x11-dev libsqlite3-dev \
|
libx11-dev libinput-dev libxkbcommon-x11-dev libsqlite3-dev libsdl2-dev \
|
||||||
&& apt-get clean && rm -rf /var/lib/apt/lists/* \
|
&& apt-get clean && rm -rf /var/lib/apt/lists/* \
|
||||||
&& pip install --no-cache-dir -U platformio \
|
&& pip install --no-cache-dir -U platformio \
|
||||||
&& mkdir /tmp/firmware
|
&& mkdir /tmp/firmware
|
||||||
@@ -53,7 +53,7 @@ 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 libgpiod3 libyaml-cpp0.8 libi2c0 libuv1t64 libusb-1.0-0-dev \
|
libc-bin libc6 libgpiod3 libyaml-cpp0.8 libi2c0 libuv1t64 libusb-1.0-0-dev \
|
||||||
liborcania2.3 libulfius2.7t64 libssl3t64 \
|
liborcania2.3 libulfius2.7t64 libssl3t64 \
|
||||||
libx11-6 libinput10 libxkbcommon-x11-0 \
|
libx11-6 libinput10 libxkbcommon-x11-0 libsdl2-2.0-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 \
|
||||||
&& mkdir -p /etc/meshtasticd/config.d \
|
&& mkdir -p /etc/meshtasticd/config.d \
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# Lightweight container for running native PlatformIO tests on non-Linux hosts
|
||||||
|
FROM python:3.14-slim-trixie
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
ENV PIP_ROOT_USER_ACTION=ignore
|
||||||
|
|
||||||
|
# hadolint ignore=DL3008
|
||||||
|
RUN apt-get update && apt-get install --no-install-recommends -y \
|
||||||
|
g++ git ca-certificates pkg-config \
|
||||||
|
libgpiod-dev libyaml-cpp-dev libbluetooth-dev libi2c-dev libuv1-dev \
|
||||||
|
libusb-1.0-0-dev libulfius-dev liborcania-dev libssl-dev \
|
||||||
|
libx11-dev libinput-dev libxkbcommon-x11-dev libsqlite3-dev libsdl2-dev \
|
||||||
|
&& apt-get clean && rm -rf /var/lib/apt/lists/* \
|
||||||
|
&& pip install --no-cache-dir platformio==6.1.19 \
|
||||||
|
&& useradd --create-home --shell /usr/sbin/nologin meshtastic
|
||||||
|
|
||||||
|
WORKDIR /firmware
|
||||||
|
RUN chown -R meshtastic:meshtastic /firmware
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||||
|
CMD platformio --version || exit 1
|
||||||
|
|
||||||
|
USER meshtastic
|
||||||
|
|
||||||
|
# Run tests by default; override with docker run args for specific filters
|
||||||
|
CMD ["platformio", "test", "-e", "coverage", "-v"]
|
||||||
+2
-2
@@ -11,7 +11,7 @@ RUN apk --no-cache add \
|
|||||||
bash g++ libstdc++-dev linux-headers zip git ca-certificates libbsd-dev \
|
bash g++ libstdc++-dev linux-headers zip git ca-certificates libbsd-dev \
|
||||||
libgpiod-dev yaml-cpp-dev bluez-dev \
|
libgpiod-dev yaml-cpp-dev bluez-dev \
|
||||||
libusb-dev i2c-tools-dev libuv-dev openssl-dev pkgconf argp-standalone \
|
libusb-dev i2c-tools-dev libuv-dev openssl-dev pkgconf argp-standalone \
|
||||||
libx11-dev libinput-dev libxkbcommon-dev sqlite-dev \
|
libx11-dev libinput-dev libxkbcommon-dev sqlite-dev sdl2-dev \
|
||||||
&& rm -rf /var/cache/apk/* \
|
&& rm -rf /var/cache/apk/* \
|
||||||
&& pip install --no-cache-dir -U platformio \
|
&& pip install --no-cache-dir -U platformio \
|
||||||
&& mkdir /tmp/firmware
|
&& mkdir /tmp/firmware
|
||||||
@@ -42,7 +42,7 @@ USER root
|
|||||||
|
|
||||||
RUN apk --no-cache add \
|
RUN apk --no-cache add \
|
||||||
shadow libstdc++ libbsd libgpiod yaml-cpp libusb \
|
shadow libstdc++ libbsd libgpiod yaml-cpp libusb \
|
||||||
i2c-tools libuv libx11 libinput libxkbcommon \
|
i2c-tools libuv libx11 libinput libxkbcommon sdl2 \
|
||||||
&& rm -rf /var/cache/apk/* \
|
&& rm -rf /var/cache/apk/* \
|
||||||
&& mkdir -p /var/lib/meshtasticd \
|
&& mkdir -p /var/lib/meshtasticd \
|
||||||
&& mkdir -p /etc/meshtasticd/config.d \
|
&& mkdir -p /etc/meshtasticd/config.d \
|
||||||
|
|||||||
+1
-1
@@ -23,4 +23,4 @@ for BOARD in $BOARDS; do
|
|||||||
CHECK="${CHECK} -e ${BOARD}"
|
CHECK="${CHECK} -e ${BOARD}"
|
||||||
done
|
done
|
||||||
|
|
||||||
pio check --flags "-DAPP_VERSION=${APP_VERSION} --suppressions-list=suppressions.txt --inline-suppr" $CHECK --skip-packages --pattern="src/" --fail-on-defect=medium --fail-on-defect=high
|
pio check --flags "-DAPP_VERSION=${APP_VERSION} --suppressions-list=suppressions.txt --inline-suppr" $CHECK --skip-packages --pattern="src/" --fail-on-defect=low --fail-on-defect=medium --fail-on-defect=high
|
||||||
|
|||||||
@@ -187,6 +187,7 @@ Logging:
|
|||||||
LogLevel: info # debug, info, warn, error
|
LogLevel: info # debug, info, warn, error
|
||||||
# TraceFile: /var/log/meshtasticd.json
|
# TraceFile: /var/log/meshtasticd.json
|
||||||
# JSONFile: /packets.json # File location for JSON output of decoded packets
|
# JSONFile: /packets.json # File location for JSON output of decoded packets
|
||||||
|
# JSONFileRotate: 60 # Rotate JSON file every N minutes, or 0 for no rotation
|
||||||
# JSONFilter: position # filter for packets to save to JSON file
|
# JSONFilter: position # filter for packets to save to JSON file
|
||||||
# AsciiLogs: true # default if not specified is !isatty() on stdout
|
# AsciiLogs: true # default if not specified is !isatty() on stdout
|
||||||
|
|
||||||
@@ -214,3 +215,4 @@ General:
|
|||||||
AvailableDirectory: /etc/meshtasticd/available.d/
|
AvailableDirectory: /etc/meshtasticd/available.d/
|
||||||
# MACAddress: AA:BB:CC:DD:EE:FF
|
# MACAddress: AA:BB:CC:DD:EE:FF
|
||||||
# MACAddressSource: eth0
|
# MACAddressSource: eth0
|
||||||
|
# APIPort: 4403
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ Lora:
|
|||||||
Reset: 16 # IO4
|
Reset: 16 # IO4
|
||||||
Busy: 24 # IO5
|
Busy: 24 # IO5
|
||||||
# Ant_sw: 13 # IO3
|
# Ant_sw: 13 # IO3
|
||||||
|
Enable_Pins:
|
||||||
|
- 12
|
||||||
|
- 13
|
||||||
DIO3_TCXO_VOLTAGE: true
|
DIO3_TCXO_VOLTAGE: true
|
||||||
DIO2_AS_RF_SWITCH: true
|
DIO2_AS_RF_SWITCH: true
|
||||||
spidev: spidev0.0
|
spidev: spidev0.0
|
||||||
|
|||||||
@@ -4,5 +4,8 @@ Lora:
|
|||||||
Reset: 24 # IO4
|
Reset: 24 # IO4
|
||||||
Busy: 19 # IO5
|
Busy: 19 # IO5
|
||||||
# Ant_sw: 23 # IO3
|
# Ant_sw: 23 # IO3
|
||||||
|
Enable_Pins:
|
||||||
|
- 26
|
||||||
|
- 23
|
||||||
spidev: spidev0.1
|
spidev: spidev0.1
|
||||||
# CS: 7
|
# CS: 7
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
Lora:
|
||||||
|
|
||||||
|
### RAK13300in Slot 1
|
||||||
|
Module: sx1262
|
||||||
|
IRQ: 22 #IO6
|
||||||
|
Reset: 16 # IO4
|
||||||
|
Busy: 24 # IO5
|
||||||
|
# Ant_sw: 13 # IO3
|
||||||
|
Enable_Pins:
|
||||||
|
- 12
|
||||||
|
- 13
|
||||||
|
DIO3_TCXO_VOLTAGE: true
|
||||||
|
DIO2_AS_RF_SWITCH: true
|
||||||
|
spidev: spidev0.0
|
||||||
|
# CS: 8
|
||||||
|
TX_GAIN_LORA: [8]
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
Lora:
|
||||||
|
### RAK13300in Slot 2 pins
|
||||||
|
IRQ: 18 #IO6
|
||||||
|
Reset: 24 # IO4
|
||||||
|
Busy: 19 # IO5
|
||||||
|
# Ant_sw: 23 # IO3
|
||||||
|
Enable_Pins:
|
||||||
|
- 26
|
||||||
|
- 23
|
||||||
|
spidev: spidev0.1
|
||||||
|
# CS: 7
|
||||||
|
TX_GAIN_LORA: [8]
|
||||||
@@ -5,7 +5,11 @@ Lora:
|
|||||||
IRQ: 22 #IO6
|
IRQ: 22 #IO6
|
||||||
Reset: 16 # IO4
|
Reset: 16 # IO4
|
||||||
Busy: 24 # IO5
|
Busy: 24 # IO5
|
||||||
# Ant_sw: 13 # IO3
|
Enable_Pins:
|
||||||
|
- 12
|
||||||
|
- 13
|
||||||
DIO3_TCXO_VOLTAGE: true
|
DIO3_TCXO_VOLTAGE: true
|
||||||
DIO2_AS_RF_SWITCH: true
|
DIO2_AS_RF_SWITCH: true
|
||||||
spidev: spidev0.0
|
spidev: spidev0.0
|
||||||
|
# GPIO_DETECT_PA: 13
|
||||||
|
TX_GAIN_LORA: [8]
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# For use with Armbian luckfox-lyra-ultra-w
|
||||||
|
# Enable overlay 'luckfox-lyra-ultra-w-spi0-cs0-spidev' with armbian-config
|
||||||
|
# https://github.com/wehooper4/Meshtastic-Hardware/tree/main/Luckfox%20Ultra%20Hat
|
||||||
|
# 1 Watt Lyra Ultra hat
|
||||||
|
Lora:
|
||||||
|
Module: sx1262
|
||||||
|
DIO2_AS_RF_SWITCH: true
|
||||||
|
DIO3_TCXO_VOLTAGE: true
|
||||||
|
CS: 10
|
||||||
|
IRQ: 5
|
||||||
|
Busy: 11
|
||||||
|
Reset: 9
|
||||||
|
RXen: 14
|
||||||
|
|
||||||
|
spidev: spidev0.0 #pins are (CS=10, CLK=8, MOSI=6, MISO=7)
|
||||||
|
spiSpeed: 2000000
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# For use with Armbian luckfox-lyra-ultra-w
|
||||||
|
# Enable overlay 'luckfox-lyra-ultra-w-spi0-cs0-spidev' with armbian-config
|
||||||
|
# https://github.com/wehooper4/Meshtastic-Hardware/tree/main/Luckfox%20Ultra%20Hat
|
||||||
|
# 2 Watt Lyra Ultra hat
|
||||||
|
Lora:
|
||||||
|
Module: sx1262
|
||||||
|
DIO2_AS_RF_SWITCH: true
|
||||||
|
DIO3_TCXO_VOLTAGE: true
|
||||||
|
SX126X_MAX_POWER: 8
|
||||||
|
CS: 10
|
||||||
|
IRQ: 5
|
||||||
|
Busy: 11
|
||||||
|
Reset: 9
|
||||||
|
RXen: 14
|
||||||
|
|
||||||
|
spidev: spidev0.0 #pins are (CS=10, CLK=8, MOSI=6, MISO=7)
|
||||||
|
spiSpeed: 2000000
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# For use with Armbian luckfox-lyra // luckfox-lyra-plus
|
||||||
|
# Enable overlay 'luckfox-lyra-plus-spi0-cs0_rmio13-spidev' with armbian-config
|
||||||
|
# Waveshare LoRa HAT for Raspberry Pi Pico
|
||||||
|
# https://www.waveshare.com/wiki/Pico-LoRa-SX1262
|
||||||
|
Lora:
|
||||||
|
Module: sx1262
|
||||||
|
DIO2_AS_RF_SWITCH: true
|
||||||
|
DIO3_TCXO_VOLTAGE: true
|
||||||
|
spidev: spidev0.0
|
||||||
|
CS: # GPIO0_B5
|
||||||
|
pin: 13
|
||||||
|
gpiochip: 0
|
||||||
|
line: 13
|
||||||
|
IRQ: # GPIO1_C2
|
||||||
|
pin: 50
|
||||||
|
gpiochip: 1
|
||||||
|
line: 18
|
||||||
|
Busy: # GPIO0_B4
|
||||||
|
pin: 12
|
||||||
|
gpiochip: 0
|
||||||
|
line: 12
|
||||||
|
Reset: # GPIO0_A2
|
||||||
|
pin: 2
|
||||||
|
gpiochip: 0
|
||||||
|
line: 2
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
Lora:
|
||||||
|
Module: sx1262
|
||||||
|
CS: 0
|
||||||
|
IRQ: 6
|
||||||
|
Reset: 1
|
||||||
|
Busy: 4
|
||||||
|
RXen: 2
|
||||||
|
DIO2_AS_RF_SWITCH: true
|
||||||
|
spidev: ch341
|
||||||
|
USB_PID: 0x5512
|
||||||
|
USB_VID: 0x1A86
|
||||||
|
DIO3_TCXO_VOLTAGE: true
|
||||||
|
# USB_Serialnum: 12345678
|
||||||
|
SX126X_MAX_POWER: 22
|
||||||
|
# Reduce output power to improve EMI
|
||||||
|
TX_GAIN_LORA: [12, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 9, 8, 8, 7]
|
||||||
|
# Note: This module integrates an additional PA to achieve higher output power.
|
||||||
|
# The 'power' parameter here does not represent the actual RF output.
|
||||||
|
# TX_GAIN_LORA defines the gain offset applied at each SX1262 input power step (1–22 dBm).
|
||||||
|
# Each array element corresponds to the additional gain when that input level is set,
|
||||||
|
# The effective RF output is: Pout ≈ Pset + TX_GAIN_LORA[index].
|
||||||
|
# Please refer to https://github.com/linser233/uMesh/blob/main/RF_Power.md for detailed information.
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
Lora:
|
|
||||||
Module: sx1262
|
|
||||||
CS: 0
|
|
||||||
IRQ: 6
|
|
||||||
Reset: 1
|
|
||||||
Busy: 4
|
|
||||||
RXen: 2
|
|
||||||
DIO2_AS_RF_SWITCH: true
|
|
||||||
spidev: ch341
|
|
||||||
USB_PID: 0x5512
|
|
||||||
USB_VID: 0x1A86
|
|
||||||
DIO3_TCXO_VOLTAGE: true
|
|
||||||
# USB_Serialnum: 12345678
|
|
||||||
SX126X_MAX_POWER: 30
|
|
||||||
# Reduce output power to improve EMI
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
Lora:
|
||||||
|
Module: sx1268
|
||||||
|
CS: 0
|
||||||
|
IRQ: 6
|
||||||
|
Reset: 1
|
||||||
|
Busy: 4
|
||||||
|
RXen: 2
|
||||||
|
DIO2_AS_RF_SWITCH: true
|
||||||
|
spidev: ch341
|
||||||
|
USB_PID: 0x5512
|
||||||
|
USB_VID: 0x1A86
|
||||||
|
DIO3_TCXO_VOLTAGE: true
|
||||||
|
# USB_Serialnum: 12345678
|
||||||
|
SX126X_MAX_POWER: 22
|
||||||
|
# Reduce output power to improve EMI
|
||||||
|
TX_GAIN_LORA: [12, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 9, 8, 8, 7]
|
||||||
|
# Note: This module integrates an additional PA to achieve higher output power.
|
||||||
|
# The 'power' parameter here does not represent the actual RF output.
|
||||||
|
# TX_GAIN_LORA defines the gain offset applied at each SX1262 input power step (1–22 dBm).
|
||||||
|
# Each array element corresponds to the additional gain when that input level is set,
|
||||||
|
# The effective RF output is: Pout ≈ Pset + TX_GAIN_LORA[index].
|
||||||
|
# Please refer to https://github.com/linser233/uMesh/blob/main/RF_Power.md for detailed information.
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
Lora:
|
|
||||||
Module: sx1268
|
|
||||||
CS: 0
|
|
||||||
IRQ: 6
|
|
||||||
Reset: 1
|
|
||||||
Busy: 4
|
|
||||||
RXen: 2
|
|
||||||
DIO2_AS_RF_SWITCH: true
|
|
||||||
spidev: ch341
|
|
||||||
USB_PID: 0x5512
|
|
||||||
USB_VID: 0x1A86
|
|
||||||
DIO3_TCXO_VOLTAGE: true
|
|
||||||
# USB_Serialnum: 12345678
|
|
||||||
SX126X_MAX_POWER: 30
|
|
||||||
# Reduce output power to improve EMI
|
|
||||||
+2
-10
@@ -156,16 +156,8 @@ IF %BPS_RESET% EQU 1 (
|
|||||||
SET "PROGNAME=!FILENAME:.factory.bin=!"
|
SET "PROGNAME=!FILENAME:.factory.bin=!"
|
||||||
CALL :LOG_MESSAGE DEBUG "Computed PROGNAME: !PROGNAME!"
|
CALL :LOG_MESSAGE DEBUG "Computed PROGNAME: !PROGNAME!"
|
||||||
|
|
||||||
IF "__!MCU!__" == "__esp32s3__" (
|
@REM Determine OTA filename based on MCU type (unified OTA format)
|
||||||
@REM We are working with ESP32-S3
|
SET "OTA_FILENAME=mt-!MCU!-ota.bin"
|
||||||
SET "OTA_FILENAME=bleota-s3.bin"
|
|
||||||
) ELSE IF "__!MCU!__" == "__esp32c3__" (
|
|
||||||
@REM We are working with ESP32-C3
|
|
||||||
SET "OTA_FILENAME=bleota-c3.bin"
|
|
||||||
) ELSE (
|
|
||||||
@REM Everything else
|
|
||||||
SET "OTA_FILENAME=bleota.bin"
|
|
||||||
)
|
|
||||||
CALL :LOG_MESSAGE DEBUG "Set OTA_FILENAME to: !OTA_FILENAME!"
|
CALL :LOG_MESSAGE DEBUG "Set OTA_FILENAME to: !OTA_FILENAME!"
|
||||||
|
|
||||||
@REM Set SPIFFS filename with "littlefs-" prefix.
|
@REM Set SPIFFS filename with "littlefs-" prefix.
|
||||||
|
|||||||
+21
-14
@@ -32,6 +32,19 @@ if ! command -v jq >/dev/null 2>&1; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# esptool v5 supports commands with dashes and deprecates commands with
|
||||||
|
# underscores. Prior versions only support commands with underscores
|
||||||
|
if ${ESPTOOL_CMD} | grep --quiet write-flash
|
||||||
|
then
|
||||||
|
ESPTOOL_WRITE_FLASH=write-flash
|
||||||
|
ESPTOOL_ERASE_FLASH=erase-flash
|
||||||
|
ESPTOOL_READ_FLASH_STATUS=read-flash-status
|
||||||
|
else
|
||||||
|
ESPTOOL_WRITE_FLASH=write_flash
|
||||||
|
ESPTOOL_ERASE_FLASH=erase_flash
|
||||||
|
ESPTOOL_READ_FLASH_STATUS=read_flash_status
|
||||||
|
fi
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Usage info
|
# Usage info
|
||||||
@@ -83,8 +96,8 @@ while [ $# -gt 0 ]; do
|
|||||||
done
|
done
|
||||||
|
|
||||||
if [[ $BPS_RESET == true ]]; then
|
if [[ $BPS_RESET == true ]]; then
|
||||||
$ESPTOOL_CMD --baud $RESET_BAUD --after no_reset read_flash_status
|
$ESPTOOL_CMD --baud $RESET_BAUD --after no_reset ${ESPTOOL_READ_FLASH_STATUS}
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
[ -z "$FILENAME" ] && [ -n "$1" ] && {
|
[ -z "$FILENAME" ] && [ -n "$1" ] && {
|
||||||
@@ -118,14 +131,8 @@ if [[ -f "$FILENAME" && "$FILENAME" == *.factory.bin ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Determine OTA filename based on MCU type
|
# Determine OTA filename based on MCU type (unified OTA format)
|
||||||
if [ "$MCU" == "esp32s3" ]; then
|
OTAFILE="mt-${MCU}-ota.bin"
|
||||||
OTAFILE=bleota-s3.bin
|
|
||||||
elif [ "$MCU" == "esp32c3" ]; then
|
|
||||||
OTAFILE=bleota-c3.bin
|
|
||||||
else
|
|
||||||
OTAFILE=bleota.bin
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Set SPIFFS filename with "littlefs-" prefix.
|
# Set SPIFFS filename with "littlefs-" prefix.
|
||||||
SPIFFSFILE="littlefs-${PROGNAME/firmware-/}.bin"
|
SPIFFSFILE="littlefs-${PROGNAME/firmware-/}.bin"
|
||||||
@@ -144,12 +151,12 @@ if [[ -f "$FILENAME" && "$FILENAME" == *.factory.bin ]]; 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 ${ESPTOOL_ERASE_FLASH}
|
||||||
$ESPTOOL_CMD write-flash $FIRMWARE_OFFSET "${FILENAME}"
|
$ESPTOOL_CMD ${ESPTOOL_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 ${ESPTOOL_WRITE_FLASH} $OTA_OFFSET "${OTAFILE}"
|
||||||
echo "Trying to flash ${SPIFFSFILE}, at offset ${OFFSET}"
|
echo "Trying to flash ${SPIFFSFILE}, at offset ${OFFSET}"
|
||||||
$ESPTOOL_CMD write_flash $OFFSET "${SPIFFSFILE}"
|
$ESPTOOL_CMD ${ESPTOOL_WRITE_FLASH} $OFFSET "${SPIFFSFILE}"
|
||||||
|
|
||||||
else
|
else
|
||||||
show_help
|
show_help
|
||||||
|
|||||||
+13
-2
@@ -20,6 +20,17 @@ else
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# esptool v5 supports commands with dashes and deprecates commands with
|
||||||
|
# underscores. Prior versions only support commands with underscores
|
||||||
|
if ${ESPTOOL_CMD} | grep --quiet write-flash
|
||||||
|
then
|
||||||
|
ESPTOOL_WRITE_FLASH=write-flash
|
||||||
|
ESPTOOL_READ_FLASH_STATUS=read-flash-status
|
||||||
|
else
|
||||||
|
ESPTOOL_WRITE_FLASH=write_flash
|
||||||
|
ESPTOOL_READ_FLASH_STATUS=read_flash_status
|
||||||
|
fi
|
||||||
|
|
||||||
# Usage info
|
# Usage info
|
||||||
show_help() {
|
show_help() {
|
||||||
cat << EOF
|
cat << EOF
|
||||||
@@ -69,7 +80,7 @@ done
|
|||||||
shift "$((OPTIND-1))"
|
shift "$((OPTIND-1))"
|
||||||
|
|
||||||
if [ "$CHANGE_MODE" = true ]; then
|
if [ "$CHANGE_MODE" = true ]; then
|
||||||
$ESPTOOL_CMD --baud $RESET_BAUD --after no_reset read_flash_status
|
$ESPTOOL_CMD --baud $RESET_BAUD --after no_reset ${ESPTOOL_READ_FLASH_STATUS}
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -80,7 +91,7 @@ fi
|
|||||||
|
|
||||||
if [[ -f "$FILENAME" && "$FILENAME" != *.factory.bin ]]; then
|
if [[ -f "$FILENAME" && "$FILENAME" != *.factory.bin ]]; then
|
||||||
echo "Trying to flash update ${FILENAME}"
|
echo "Trying to flash update ${FILENAME}"
|
||||||
$ESPTOOL_CMD --baud $FLASH_BAUD write-flash $UPDATE_OFFSET "${FILENAME}"
|
$ESPTOOL_CMD --baud $FLASH_BAUD ${ESPTOOL_WRITE_FLASH} $UPDATE_OFFSET "${FILENAME}"
|
||||||
else
|
else
|
||||||
show_help
|
show_help
|
||||||
echo "Invalid file: ${FILENAME}"
|
echo "Invalid file: ${FILENAME}"
|
||||||
|
|||||||
@@ -87,6 +87,12 @@
|
|||||||
</screenshots>
|
</screenshots>
|
||||||
|
|
||||||
<releases>
|
<releases>
|
||||||
|
<release version="2.7.20" date="2026-02-11">
|
||||||
|
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.20</url>
|
||||||
|
</release>
|
||||||
|
<release version="2.7.19" date="2026-01-22">
|
||||||
|
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.19</url>
|
||||||
|
</release>
|
||||||
<release version="2.7.18" date="2026-01-02">
|
<release version="2.7.18" date="2026-01-02">
|
||||||
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.18</url>
|
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.18</url>
|
||||||
</release>
|
</release>
|
||||||
|
|||||||
Executable
+44
@@ -0,0 +1,44 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Run native PlatformIO tests inside Docker (for macOS / non-Linux hosts).
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./bin/test-native-docker.sh # run all native tests
|
||||||
|
# ./bin/test-native-docker.sh -f test_transmit_history # run specific test filter
|
||||||
|
# ./bin/test-native-docker.sh --rebuild # force rebuild the image
|
||||||
|
#
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
IMAGE_NAME="meshtastic-native-test"
|
||||||
|
|
||||||
|
REBUILD=false
|
||||||
|
EXTRA_ARGS=()
|
||||||
|
|
||||||
|
for arg in "$@"; do
|
||||||
|
if [[ "$arg" == "--rebuild" ]]; then
|
||||||
|
REBUILD=true
|
||||||
|
else
|
||||||
|
EXTRA_ARGS+=("$arg")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if $REBUILD || ! docker image inspect "$IMAGE_NAME" >/dev/null 2>&1; then
|
||||||
|
echo "Building test image (first run may take a few minutes)..."
|
||||||
|
docker build -t "$IMAGE_NAME" -f "$ROOT_DIR/Dockerfile.test" "$ROOT_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Disable BUILD_EPOCH to avoid full rebuilds between test runs (matches CI)
|
||||||
|
sed_cmd='s/-DBUILD_EPOCH=$UNIX_TIME/#-DBUILD_EPOCH=$UNIX_TIME/'
|
||||||
|
|
||||||
|
# Default: run all tests. Pass extra args (e.g. -f test_transmit_history) through.
|
||||||
|
if [[ ${#EXTRA_ARGS[@]} -eq 0 ]]; then
|
||||||
|
CMD=("platformio" "test" "-e" "coverage" "-v")
|
||||||
|
else
|
||||||
|
CMD=("platformio" "test" "-e" "coverage" "-v" "${EXTRA_ARGS[@]}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec docker run --rm \
|
||||||
|
-v "$ROOT_DIR:/src:ro" \
|
||||||
|
"$IMAGE_NAME" \
|
||||||
|
bash -c "rm -rf /tmp/fw-test && cp -a /src /tmp/fw-test && cd /tmp/fw-test && sed -i '${sed_cmd}' platformio.ini && ${CMD[*]}"
|
||||||
@@ -32,7 +32,8 @@
|
|||||||
"connectivity": ["bluetooth"],
|
"connectivity": ["bluetooth"],
|
||||||
"debug": {
|
"debug": {
|
||||||
"jlink_device": "nRF52840_xxAA",
|
"jlink_device": "nRF52840_xxAA",
|
||||||
"svd_path": "nrf52840.svd"
|
"svd_path": "nrf52840.svd",
|
||||||
|
"openocd_target": "nrf52840-mdk-rs"
|
||||||
},
|
},
|
||||||
"frameworks": ["arduino"],
|
"frameworks": ["arduino"],
|
||||||
"name": "Minesemi ME25LS01",
|
"name": "Minesemi ME25LS01",
|
||||||
|
|||||||
@@ -33,7 +33,8 @@
|
|||||||
"connectivity": ["bluetooth"],
|
"connectivity": ["bluetooth"],
|
||||||
"debug": {
|
"debug": {
|
||||||
"jlink_device": "nRF52840_xxAA",
|
"jlink_device": "nRF52840_xxAA",
|
||||||
"svd_path": "nrf52840.svd"
|
"svd_path": "nrf52840.svd",
|
||||||
|
"openocd_target": "nrf52840-mdk-rs"
|
||||||
},
|
},
|
||||||
"frameworks": ["arduino"],
|
"frameworks": ["arduino"],
|
||||||
"name": "MeshLink",
|
"name": "MeshLink",
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"arduino": {
|
||||||
|
"ldscript": "esp32s3_out.ld",
|
||||||
|
"partitions": "default.csv"
|
||||||
|
},
|
||||||
|
"core": "esp32",
|
||||||
|
"extra_flags": [
|
||||||
|
"-DBOARD_HAS_PSRAM",
|
||||||
|
"-DARDUINO_ESP32S3_DEV",
|
||||||
|
"-DARDUINO_USB_MODE=1",
|
||||||
|
"-DARDUINO_USB_CDC_ON_BOOT=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"],
|
||||||
|
"debug": {
|
||||||
|
"default_tool": "esp-builtin",
|
||||||
|
"onboard_tools": ["esp-builtin"],
|
||||||
|
"openocd_target": "esp32s3.cfg"
|
||||||
|
},
|
||||||
|
"frameworks": ["arduino", "espidf"],
|
||||||
|
"name": "LilyGo Mini-Epaper-S3 (4 MB Flash, 2MB PSRAM)",
|
||||||
|
"upload": {
|
||||||
|
"flash_size": "4MB",
|
||||||
|
"maximum_ram_size": 327680,
|
||||||
|
"maximum_size": 4194304,
|
||||||
|
"require_upload_port": true,
|
||||||
|
"speed": 460800
|
||||||
|
},
|
||||||
|
"url": "https://www.lilygo.cc",
|
||||||
|
"vendor": "LilyGo"
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"arduino": {
|
||||||
|
"ldscript": "nrf52840_s140_v6.ld"
|
||||||
|
},
|
||||||
|
"core": "nRF5",
|
||||||
|
"cpu": "cortex-m4",
|
||||||
|
"extra_flags": "-DMINIMESH_LITE -DNRF52840_XXAA",
|
||||||
|
"f_cpu": "64000000L",
|
||||||
|
"hwids": [
|
||||||
|
["0x239A", "0x8029"],
|
||||||
|
["0x239A", "0x0029"],
|
||||||
|
["0x239A", "0x002A"]
|
||||||
|
],
|
||||||
|
"usb_product": "Minimesh Lite",
|
||||||
|
"mcu": "nrf52840",
|
||||||
|
"variant": "dls_Minimesh_Lite",
|
||||||
|
"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"],
|
||||||
|
"name": "Minimesh Lite",
|
||||||
|
"upload": {
|
||||||
|
"maximum_ram_size": 248832,
|
||||||
|
"maximum_size": 815104,
|
||||||
|
"speed": 115200,
|
||||||
|
"protocol": "nrfutil",
|
||||||
|
"protocols": ["nrfutil", "jlink", "nrfjprog", "stlink"],
|
||||||
|
"use_1200bps_touch": true,
|
||||||
|
"require_upload_port": true,
|
||||||
|
"wait_for_upload_port": true
|
||||||
|
},
|
||||||
|
"url": "https://deeplabstudio.com",
|
||||||
|
"vendor": "Deeplab Studio"
|
||||||
|
}
|
||||||
+2
-1
@@ -32,7 +32,8 @@
|
|||||||
"connectivity": ["bluetooth"],
|
"connectivity": ["bluetooth"],
|
||||||
"debug": {
|
"debug": {
|
||||||
"jlink_device": "nRF52840_xxAA",
|
"jlink_device": "nRF52840_xxAA",
|
||||||
"svd_path": "nrf52840.svd"
|
"svd_path": "nrf52840.svd",
|
||||||
|
"openocd_target": "nrf52840-mdk-rs"
|
||||||
},
|
},
|
||||||
"frameworks": ["arduino"],
|
"frameworks": ["arduino"],
|
||||||
"name": "MINEWSEMI_MS24SF1_SX1262",
|
"name": "MINEWSEMI_MS24SF1_SX1262",
|
||||||
|
|||||||
@@ -32,7 +32,8 @@
|
|||||||
"connectivity": ["bluetooth"],
|
"connectivity": ["bluetooth"],
|
||||||
"debug": {
|
"debug": {
|
||||||
"jlink_device": "nRF52840_xxAA",
|
"jlink_device": "nRF52840_xxAA",
|
||||||
"svd_path": "nrf52840.svd"
|
"svd_path": "nrf52840.svd",
|
||||||
|
"openocd_target": "nrf52840-mdk-rs"
|
||||||
},
|
},
|
||||||
"frameworks": ["arduino"],
|
"frameworks": ["arduino"],
|
||||||
"name": "BQ nRF52840",
|
"name": "BQ nRF52840",
|
||||||
|
|||||||
@@ -33,7 +33,8 @@
|
|||||||
"connectivity": ["bluetooth"],
|
"connectivity": ["bluetooth"],
|
||||||
"debug": {
|
"debug": {
|
||||||
"jlink_device": "nRF52840_xxAA",
|
"jlink_device": "nRF52840_xxAA",
|
||||||
"svd_path": "nrf52840.svd"
|
"svd_path": "nrf52840.svd",
|
||||||
|
"openocd_target": "nrf52840-mdk-rs"
|
||||||
},
|
},
|
||||||
"frameworks": ["arduino"],
|
"frameworks": ["arduino"],
|
||||||
"name": "ProMicro compatible nRF52840",
|
"name": "ProMicro compatible nRF52840",
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
"extra_flags": [
|
"extra_flags": [
|
||||||
"-DBOARD_HAS_PSRAM",
|
"-DBOARD_HAS_PSRAM",
|
||||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||||
"-DARDUINO_USB_MODE=0",
|
"-DARDUINO_USB_MODE=1",
|
||||||
"-DARDUINO_RUNNING_CORE=1",
|
"-DARDUINO_RUNNING_CORE=1",
|
||||||
"-DARDUINO_EVENT_RUNNING_CORE=0"
|
"-DARDUINO_EVENT_RUNNING_CORE=0"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -33,7 +33,8 @@
|
|||||||
"connectivity": ["bluetooth"],
|
"connectivity": ["bluetooth"],
|
||||||
"debug": {
|
"debug": {
|
||||||
"jlink_device": "nRF52840_xxAA",
|
"jlink_device": "nRF52840_xxAA",
|
||||||
"svd_path": "nrf52840.svd"
|
"svd_path": "nrf52840.svd",
|
||||||
|
"openocd_target": "nrf52840-mdk-rs"
|
||||||
},
|
},
|
||||||
"frameworks": ["arduino"],
|
"frameworks": ["arduino"],
|
||||||
"name": "Seeed T1000-E",
|
"name": "Seeed T1000-E",
|
||||||
|
|||||||
@@ -25,7 +25,8 @@
|
|||||||
"connectivity": ["bluetooth"],
|
"connectivity": ["bluetooth"],
|
||||||
"debug": {
|
"debug": {
|
||||||
"jlink_device": "nRF52840_xxAA",
|
"jlink_device": "nRF52840_xxAA",
|
||||||
"svd_path": "nrf52840.svd"
|
"svd_path": "nrf52840.svd",
|
||||||
|
"openocd_target": "nrf52840-mdk-rs"
|
||||||
},
|
},
|
||||||
"frameworks": ["arduino", "freertos"],
|
"frameworks": ["arduino", "freertos"],
|
||||||
"name": "Seeed WIO WM1110",
|
"name": "Seeed WIO WM1110",
|
||||||
|
|||||||
@@ -32,7 +32,8 @@
|
|||||||
"connectivity": ["bluetooth"],
|
"connectivity": ["bluetooth"],
|
||||||
"debug": {
|
"debug": {
|
||||||
"jlink_device": "nRF52840_xxAA",
|
"jlink_device": "nRF52840_xxAA",
|
||||||
"svd_path": "nrf52840.svd"
|
"svd_path": "nrf52840.svd",
|
||||||
|
"openocd_target": "nrf52840-mdk-rs"
|
||||||
},
|
},
|
||||||
"frameworks": ["arduino"],
|
"frameworks": ["arduino"],
|
||||||
"name": "Seeed WIO WM1110",
|
"name": "Seeed WIO WM1110",
|
||||||
|
|||||||
@@ -32,7 +32,8 @@
|
|||||||
"connectivity": ["bluetooth"],
|
"connectivity": ["bluetooth"],
|
||||||
"debug": {
|
"debug": {
|
||||||
"jlink_device": "nRF52840_xxAA",
|
"jlink_device": "nRF52840_xxAA",
|
||||||
"svd_path": "nrf52840.svd"
|
"svd_path": "nrf52840.svd",
|
||||||
|
"openocd_target": "nrf52840-mdk-rs"
|
||||||
},
|
},
|
||||||
"frameworks": ["arduino"],
|
"frameworks": ["arduino"],
|
||||||
"name": "Seeed WIO WM1110",
|
"name": "Seeed WIO WM1110",
|
||||||
|
|||||||
+1
-1
@@ -12,6 +12,6 @@ Ex:
|
|||||||
- `logo_320x480.png`
|
- `logo_320x480.png`
|
||||||
- `logo_320x240.png`
|
- `logo_320x240.png`
|
||||||
|
|
||||||
This file is copied to `data/boot/logo.png` before filesytem image compilation.
|
This file is copied to `data/boot/logo.png` before filesystem image compilation.
|
||||||
|
|
||||||
For additional examples see the [`event/defcon33` branch](https://github.com/meshtastic/firmware/tree/event/defcon33).
|
For additional examples see the [`event/defcon33` branch](https://github.com/meshtastic/firmware/tree/event/defcon33).
|
||||||
|
|||||||
Vendored
+12
@@ -1,3 +1,15 @@
|
|||||||
|
meshtasticd (2.7.20.0) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Version 2.7.20
|
||||||
|
|
||||||
|
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Wed, 11 Feb 2026 12:19:54 +0000
|
||||||
|
|
||||||
|
meshtasticd (2.7.19.0) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Version 2.7.19
|
||||||
|
|
||||||
|
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Thu, 22 Jan 2026 22:17:40 +0000
|
||||||
|
|
||||||
meshtasticd (2.7.18.0) unstable; urgency=medium
|
meshtasticd (2.7.18.0) unstable; urgency=medium
|
||||||
|
|
||||||
* Version 2.7.18
|
* Version 2.7.18
|
||||||
|
|||||||
Vendored
+3
@@ -3,6 +3,9 @@ export DEBEMAIL="jbennett@incomsystems.biz"
|
|||||||
export PLATFORMIO_LIBDEPS_DIR=pio/libdeps
|
export PLATFORMIO_LIBDEPS_DIR=pio/libdeps
|
||||||
export PLATFORMIO_PACKAGES_DIR=pio/packages
|
export PLATFORMIO_PACKAGES_DIR=pio/packages
|
||||||
export PLATFORMIO_CORE_DIR=pio/core
|
export PLATFORMIO_CORE_DIR=pio/core
|
||||||
|
export PLATFORMIO_SETTING_ENABLE_TELEMETRY=0
|
||||||
|
export PLATFORMIO_SETTING_CHECK_PLATFORMIO_INTERVAL=3650
|
||||||
|
export PLATFORMIO_SETTING_CHECK_PRUNE_SYSTEM_THRESHOLD=10240
|
||||||
|
|
||||||
# Download libraries to `pio`
|
# Download libraries to `pio`
|
||||||
platformio pkg install -e native-tft
|
platformio pkg install -e native-tft
|
||||||
|
|||||||
Vendored
+2
-1
@@ -26,7 +26,8 @@ Build-Depends: debhelper-compat (= 13),
|
|||||||
libx11-dev,
|
libx11-dev,
|
||||||
libinput-dev,
|
libinput-dev,
|
||||||
libxkbcommon-x11-dev,
|
libxkbcommon-x11-dev,
|
||||||
libsqlite3-dev
|
libsqlite3-dev,
|
||||||
|
libsdl2-dev
|
||||||
Standards-Version: 4.6.2
|
Standards-Version: 4.6.2
|
||||||
Homepage: https://github.com/meshtastic/firmware
|
Homepage: https://github.com/meshtastic/firmware
|
||||||
Rules-Requires-Root: no
|
Rules-Requires-Root: no
|
||||||
|
|||||||
Vendored
+4
-1
@@ -9,7 +9,10 @@
|
|||||||
PIO_ENV:=\
|
PIO_ENV:=\
|
||||||
PLATFORMIO_CORE_DIR=pio/core \
|
PLATFORMIO_CORE_DIR=pio/core \
|
||||||
PLATFORMIO_LIBDEPS_DIR=pio/libdeps \
|
PLATFORMIO_LIBDEPS_DIR=pio/libdeps \
|
||||||
PLATFORMIO_PACKAGES_DIR=pio/packages
|
PLATFORMIO_PACKAGES_DIR=pio/packages \
|
||||||
|
PLATFORMIO_SETTING_ENABLE_TELEMETRY=0 \
|
||||||
|
PLATFORMIO_SETTING_CHECK_PLATFORMIO_INTERVAL=3650 \
|
||||||
|
PLATFORMIO_SETTING_CHECK_PRUNE_SYSTEM_THRESHOLD=10240
|
||||||
|
|
||||||
# Raspbian armhf builds should be compatible with armv6-hardfloat
|
# Raspbian armhf builds should be compatible with armv6-hardfloat
|
||||||
# https://www.valvers.com/open-software/raspberry-pi/bare-metal-programming-in-c-part-1/#rpi1-compiler-flags
|
# https://www.valvers.com/open-software/raspberry-pi/bare-metal-programming-in-c-part-1/#rpi1-compiler-flags
|
||||||
|
|||||||
@@ -0,0 +1,625 @@
|
|||||||
|
# BLE Mesh Implementation Plan
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Add BLE as a mesh transport alongside LoRa, enabling Meshtastic nodes to relay packets over Bluetooth Low Energy. Follow the same handler pattern used by UDP multicast ([UdpMulticastHandler](src/mesh/udp/UdpMulticastHandler.h)) — a standalone handler class with `onSend()` hooked into `Router::send()` and received packets injected via `router->enqueueReceivedMessage()`. No changes to `RadioInterface` or Router architecture needed.
|
||||||
|
|
||||||
|
BLE mesh is useful for:
|
||||||
|
|
||||||
|
- Dense indoor deployments where LoRa range is excessive
|
||||||
|
- Extending mesh reach through devices that lack LoRa hardware
|
||||||
|
- Hybrid LoRa+BLE networks (gateway nodes bridge both transports)
|
||||||
|
- Lower latency links between nearby nodes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### Pattern: Same as UDP Multicast
|
||||||
|
|
||||||
|
```
|
||||||
|
Router::send()
|
||||||
|
│
|
||||||
|
┌───────────────────┼───────────────────┐
|
||||||
|
│ │ │
|
||||||
|
▼ ▼ ▼
|
||||||
|
iface->send(p) udpHandler->onSend(p) bleMeshHandler->onSend(p)
|
||||||
|
(LoRa) (UDP) (BLE mesh)
|
||||||
|
|
||||||
|
Router::enqueueReceivedMessage()
|
||||||
|
▲
|
||||||
|
┌───────────────────┼───────────────────┐
|
||||||
|
│ │ │
|
||||||
|
LoRa RX ISR udpHandler->onReceive() bleMeshHandler->onReceive()
|
||||||
|
```
|
||||||
|
|
||||||
|
The existing UDP pattern ([Router.cpp:378-382](src/mesh/Router.cpp#L378-L382)):
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#if HAS_UDP_MULTICAST
|
||||||
|
if (udpHandler && config.network.enabled_protocols & ...) {
|
||||||
|
udpHandler->onSend(const_cast<meshtastic_MeshPacket *>(p));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
```
|
||||||
|
|
||||||
|
BLE mesh hooks in identically, right next to it.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 1: Protobuf & Configuration
|
||||||
|
|
||||||
|
### 1.1 Transport mechanism enum
|
||||||
|
|
||||||
|
**File:** protobufs repo (`mesh.proto`)
|
||||||
|
|
||||||
|
Add a new transport type alongside the existing ones:
|
||||||
|
|
||||||
|
```protobuf
|
||||||
|
enum TransportMechanism {
|
||||||
|
// ... existing 0-7 ...
|
||||||
|
TRANSPORT_BLE_MESH = 8;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.2 BLE Mesh configuration
|
||||||
|
|
||||||
|
Add BLE mesh config to the device config protobuf:
|
||||||
|
|
||||||
|
```protobuf
|
||||||
|
message BleMeshConfig {
|
||||||
|
bool enabled = 1;
|
||||||
|
|
||||||
|
// Operating mode
|
||||||
|
enum Mode {
|
||||||
|
OFF = 0;
|
||||||
|
ADVERTISE_AND_SCAN = 1; // connectionless (extended adv)
|
||||||
|
GATT = 2; // connection-based
|
||||||
|
HYBRID = 3; // both
|
||||||
|
}
|
||||||
|
Mode mode = 2;
|
||||||
|
|
||||||
|
// Scan/advertising interval in ms (latency vs power tradeoff)
|
||||||
|
uint32_t interval_ms = 3; // default: 500ms
|
||||||
|
|
||||||
|
// TX power level (platform-mapped)
|
||||||
|
int8_t tx_power = 4; // dBm
|
||||||
|
|
||||||
|
// Max BLE mesh peers to track
|
||||||
|
uint32_t max_peers = 5; // default: 8
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 2: BLE Mesh Handler — Core Class
|
||||||
|
|
||||||
|
### 2.1 Base handler (platform-agnostic)
|
||||||
|
|
||||||
|
**New file:** `src/mesh/BLEMeshHandler.h`
|
||||||
|
|
||||||
|
Follow the `UdpMulticastHandler` pattern — a simple class, not a `RadioInterface`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#pragma once
|
||||||
|
#if HAS_BLE_MESH
|
||||||
|
|
||||||
|
#include "configuration.h"
|
||||||
|
#include "mesh/Router.h"
|
||||||
|
|
||||||
|
class BLEMeshHandler {
|
||||||
|
public:
|
||||||
|
BLEMeshHandler() : isRunning(false) {}
|
||||||
|
|
||||||
|
virtual void start() = 0;
|
||||||
|
virtual void stop() = 0;
|
||||||
|
|
||||||
|
// Called from Router::send() — broadcast packet over BLE
|
||||||
|
virtual bool onSend(const meshtastic_MeshPacket *mp) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool isRunning;
|
||||||
|
|
||||||
|
// Shared RX path: decode and enqueue into router
|
||||||
|
void deliverToRouter(const uint8_t *data, size_t len) {
|
||||||
|
meshtastic_MeshPacket mp;
|
||||||
|
if (!pb_decode_from_bytes(data, len, &meshtastic_MeshPacket_msg, &mp))
|
||||||
|
return;
|
||||||
|
if (mp.which_payload_variant != meshtastic_MeshPacket_encrypted_tag)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mp.transport_mechanism =
|
||||||
|
meshtastic_MeshPacket_TransportMechanism_TRANSPORT_BLE_MESH;
|
||||||
|
mp.rx_snr = 0;
|
||||||
|
mp.rx_rssi = 0;
|
||||||
|
|
||||||
|
UniquePacketPoolPacket p = packetPool.allocUniqueCopy(mp);
|
||||||
|
if (router)
|
||||||
|
router->enqueueReceivedMessage(p.release());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shared TX path: encode packet to bytes
|
||||||
|
size_t encodeForBLE(const meshtastic_MeshPacket *mp, uint8_t *buf, size_t bufLen) {
|
||||||
|
return pb_encode_to_bytes(buf, bufLen, &meshtastic_MeshPacket_msg, mp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // HAS_BLE_MESH
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 Wire format
|
||||||
|
|
||||||
|
Same as UDP multicast — protobuf-encoded `meshtastic_MeshPacket` (already encrypted). This means:
|
||||||
|
|
||||||
|
- Packets are encoded with `pb_encode_to_bytes()` on TX
|
||||||
|
- Decoded with `pb_decode_from_bytes()` on RX
|
||||||
|
- Encryption already applied by Router before `onSend()` is called
|
||||||
|
- Same deduplication via `PacketHistory` (keyed on `sender, id`)
|
||||||
|
|
||||||
|
### 2.3 Echo prevention
|
||||||
|
|
||||||
|
Same pattern as UDP ([UdpMulticastHandler.h:102](src/mesh/udp/UdpMulticastHandler.h#L102)):
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
if (mp->transport_mechanism == meshtastic_MeshPacket_TransportMechanism_TRANSPORT_BLE_MESH) {
|
||||||
|
LOG_ERROR("Attempt to send BLE-sourced packet over BLE");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 3: ESP32 Implementation (NimBLE)
|
||||||
|
|
||||||
|
### Platform: ESP32, ESP32-S3, ESP32-C3, ESP32-C6
|
||||||
|
|
||||||
|
**New files:** `src/nimble/NimbleBLEMesh.h` / `NimbleBLEMesh.cpp`
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#pragma once
|
||||||
|
#if HAS_BLE_MESH && defined(ARCH_ESP32)
|
||||||
|
|
||||||
|
#include "mesh/BLEMeshHandler.h"
|
||||||
|
#include "nimble/NimbleBluetooth.h"
|
||||||
|
|
||||||
|
class NimbleBLEMesh : public BLEMeshHandler {
|
||||||
|
public:
|
||||||
|
void start() override;
|
||||||
|
void stop() override;
|
||||||
|
bool onSend(const meshtastic_MeshPacket *mp) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// --- Advertising mode ---
|
||||||
|
void startScanning();
|
||||||
|
void stopScanning();
|
||||||
|
static void onScanResult(const ble_gap_disc_desc *desc, void *arg);
|
||||||
|
void broadcastViaAdv(const uint8_t *data, size_t len);
|
||||||
|
|
||||||
|
// --- GATT mode ---
|
||||||
|
void setupMeshService();
|
||||||
|
static int onMeshCharWrite(uint16_t conn_handle,
|
||||||
|
const ble_gatt_error *error,
|
||||||
|
ble_gatt_attr *attr, void *arg);
|
||||||
|
void sendViaGATT(const uint8_t *data, size_t len);
|
||||||
|
|
||||||
|
// --- Peer tracking ---
|
||||||
|
struct BLEPeer {
|
||||||
|
NodeNum nodeNum;
|
||||||
|
ble_addr_t addr;
|
||||||
|
int8_t rssi;
|
||||||
|
uint32_t lastSeenMs;
|
||||||
|
uint16_t connHandle; // 0 if not connected
|
||||||
|
};
|
||||||
|
std::vector<BLEPeer> peers;
|
||||||
|
void updatePeer(const ble_addr_t &addr, NodeNum nodeNum, int8_t rssi);
|
||||||
|
void pruneStale();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.1 Advertising mode (connectionless)
|
||||||
|
|
||||||
|
Encode mesh packets into BLE extended advertisements with Meshtastic manufacturer data.
|
||||||
|
|
||||||
|
**TX flow:**
|
||||||
|
|
||||||
|
1. `onSend()` called from `Router::send()`
|
||||||
|
2. Encode packet via `encodeForBLE()`
|
||||||
|
3. Pack into extended advertisement manufacturer data
|
||||||
|
4. Broadcast for configurable number of intervals
|
||||||
|
|
||||||
|
**RX flow:**
|
||||||
|
|
||||||
|
1. NimBLE scan callback fires
|
||||||
|
2. Check manufacturer ID for Meshtastic magic
|
||||||
|
3. Extract protobuf bytes
|
||||||
|
4. Call `deliverToRouter()`
|
||||||
|
|
||||||
|
**Advertisement structure:**
|
||||||
|
|
||||||
|
```
|
||||||
|
AD Type: 0xFF (Manufacturer Specific)
|
||||||
|
Company ID: 0x????? (Meshtastic BLE SIG ID or 0xFFFF)
|
||||||
|
Mesh Version: 1 byte
|
||||||
|
Protobuf payload: variable (up to ~244 bytes with extended adv)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key NimBLE APIs:**
|
||||||
|
|
||||||
|
- `ble_gap_ext_adv_set_data()` / `ble_gap_ext_adv_start()` — extended adv TX
|
||||||
|
- `ble_gap_disc()` or `ble_gap_ext_disc()` — scanning
|
||||||
|
- Scan callback receives `ble_gap_disc_desc` or `ble_gap_ext_disc_desc`
|
||||||
|
|
||||||
|
### 3.2 GATT mode (connection-based)
|
||||||
|
|
||||||
|
Add a new GATT service for mesh relay, separate from the phone API service:
|
||||||
|
|
||||||
|
**New Mesh Relay Service:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Mesh Relay Service UUID: (new, distinct from phone service in BluetoothCommon.h)
|
||||||
|
├── MeshTX (WRITE_NO_RSP) — peers write mesh packets to us
|
||||||
|
├── MeshRX (NOTIFY) — we notify peers of new mesh packets
|
||||||
|
└── MeshPeer (READ) — our NodeNum for discovery
|
||||||
|
```
|
||||||
|
|
||||||
|
**TX:** Write to MeshTX on all connected peers (broadcast) or specific next-hop peer (directed).
|
||||||
|
**RX:** MeshTX write callback → `deliverToRouter()`.
|
||||||
|
|
||||||
|
### 3.3 Coexistence with phone BLE
|
||||||
|
|
||||||
|
Both services registered on the same NimBLE host:
|
||||||
|
|
||||||
|
- Phone API service: existing UUIDs ([BluetoothCommon.h](src/BluetoothCommon.h))
|
||||||
|
- Mesh relay service: new UUIDs
|
||||||
|
- Separate connection handles for phone vs mesh peers
|
||||||
|
- Use extended adv set 0 for phone, set 1 for mesh (NimBLE supports multiple adv sets)
|
||||||
|
- Reserve 1 connection for phone, remaining for mesh peers
|
||||||
|
|
||||||
|
### 3.4 ESP32 variant considerations
|
||||||
|
|
||||||
|
| Variant | BLE | Extended Adv | Max Conn | Strategy |
|
||||||
|
| -------- | --- | ------------ | -------- | --------------------------------------- |
|
||||||
|
| ESP32 | 4.2 | No | 9 | GATT-only (legacy adv too small at 31B) |
|
||||||
|
| ESP32-S3 | 5.0 | Yes | 9 | Full adv+GATT |
|
||||||
|
| ESP32-C3 | 5.0 | Yes | 3 | Adv preferred (limited connections) |
|
||||||
|
| ESP32-C6 | 5.0 | Yes | 9 | Full adv+GATT |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 4: nRF52 Implementation (SoftDevice/Bluefruit)
|
||||||
|
|
||||||
|
### Platform: nRF52840 (primary), nRF52833
|
||||||
|
|
||||||
|
**New files:** `src/platform/nrf52/NRF52BLEMesh.h` / `NRF52BLEMesh.cpp`
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#pragma once
|
||||||
|
#if HAS_BLE_MESH && defined(ARCH_NRF52)
|
||||||
|
|
||||||
|
#include "mesh/BLEMeshHandler.h"
|
||||||
|
#include <bluefruit.h>
|
||||||
|
|
||||||
|
class NRF52BLEMesh : public BLEMeshHandler {
|
||||||
|
public:
|
||||||
|
void start() override;
|
||||||
|
void stop() override;
|
||||||
|
bool onSend(const meshtastic_MeshPacket *mp) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Advertising mode
|
||||||
|
void startScanning();
|
||||||
|
static void onScanCallback(ble_gap_evt_adv_report_t *report);
|
||||||
|
void broadcastViaAdv(const uint8_t *data, size_t len);
|
||||||
|
|
||||||
|
// GATT mode
|
||||||
|
BLEService meshService;
|
||||||
|
BLECharacteristic meshTxChar;
|
||||||
|
BLECharacteristic meshRxChar;
|
||||||
|
void setupMeshService();
|
||||||
|
static void onMeshWrite(uint16_t connHandle, BLECharacteristic *chr,
|
||||||
|
uint8_t *data, uint16_t len);
|
||||||
|
void sendViaGATT(const uint8_t *data, size_t len);
|
||||||
|
|
||||||
|
// Peers
|
||||||
|
struct BLEPeer {
|
||||||
|
NodeNum nodeNum;
|
||||||
|
ble_gap_addr_t addr;
|
||||||
|
int8_t rssi;
|
||||||
|
uint32_t lastSeenMs;
|
||||||
|
uint16_t connHandle;
|
||||||
|
};
|
||||||
|
std::vector<BLEPeer> peers;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.1 Advertising mode
|
||||||
|
|
||||||
|
nRF52840 supports BLE 5.0 extended advertisements (up to 247 bytes).
|
||||||
|
|
||||||
|
**Bluefruit APIs:**
|
||||||
|
|
||||||
|
- `Bluefruit.Scanner.setRxCallback()` — scan result callback
|
||||||
|
- `Bluefruit.Scanner.start()` / `.stop()` — control scanning
|
||||||
|
- `Bluefruit.Advertising` — for standard adv
|
||||||
|
- Raw SoftDevice `sd_ble_gap_adv_set_configure()` if Bluefruit doesn't expose extended adv data
|
||||||
|
|
||||||
|
**Implementation:**
|
||||||
|
|
||||||
|
- Use Bluefruit Scanner for RX (filter by Meshtastic manufacturer ID)
|
||||||
|
- For TX, either use Bluefruit Advertising API or drop to raw SoftDevice for extended adv payloads
|
||||||
|
|
||||||
|
### 4.2 GATT mode
|
||||||
|
|
||||||
|
Same service structure as ESP32, using Bluefruit GATT APIs:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
meshService = BLEService(MESH_RELAY_SERVICE_UUID);
|
||||||
|
meshTxChar = BLECharacteristic(MESH_TX_CHAR_UUID);
|
||||||
|
meshTxChar.setProperties(CHR_PROPS_WRITE_WO_RESP);
|
||||||
|
meshTxChar.setWriteCallback(onMeshWrite);
|
||||||
|
meshTxChar.setMaxLen(512);
|
||||||
|
|
||||||
|
meshRxChar = BLECharacteristic(MESH_RX_CHAR_UUID);
|
||||||
|
meshRxChar.setProperties(CHR_PROPS_NOTIFY);
|
||||||
|
meshRxChar.setMaxLen(512);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Peer connections:**
|
||||||
|
|
||||||
|
- nRF52840 SoftDevice S140 supports up to 20 concurrent connections
|
||||||
|
- Use `Bluefruit.Central` for outbound connections to mesh peers
|
||||||
|
- Bluefruit supports simultaneous peripheral (phone) + central (mesh peers)
|
||||||
|
|
||||||
|
### 4.3 Coexistence with phone BLE
|
||||||
|
|
||||||
|
Existing phone service ([NRF52Bluetooth.cpp](src/platform/nrf52/NRF52Bluetooth.cpp)) uses peripheral role:
|
||||||
|
|
||||||
|
- Keep phone API service as-is
|
||||||
|
- Add mesh relay service alongside it
|
||||||
|
- Scanner shared between peer discovery and mesh RX (filter by service UUID or manufacturer data)
|
||||||
|
- SoftDevice connection budget: 1 peripheral (phone) + N central (mesh peers)
|
||||||
|
|
||||||
|
### 4.4 nRF52 extras
|
||||||
|
|
||||||
|
| Feature | nRF52840 | nRF52833 |
|
||||||
|
| ---------------------- | -------- | -------- |
|
||||||
|
| Extended Adv | Yes | Yes |
|
||||||
|
| Max Connections | 20 | 20 |
|
||||||
|
| RAM per connection | ~1.8KB | ~1.8KB |
|
||||||
|
| Coded PHY (Long Range) | Yes | Yes |
|
||||||
|
|
||||||
|
**Coded PHY:** BLE Coded PHY (125kbps) provides ~4x range vs 1Mbps PHY. Could be used for extended-range BLE mesh links as a configuration option.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 5: Router Integration
|
||||||
|
|
||||||
|
### 5.1 Hook into Router::send()
|
||||||
|
|
||||||
|
**File:** [src/mesh/Router.cpp](src/mesh/Router.cpp) — add right next to UDP handler:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#if HAS_BLE_MESH
|
||||||
|
if (bleMeshHandler && config.bluetooth.mesh_enabled) {
|
||||||
|
bleMeshHandler->onSend(p);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 Global handler instance
|
||||||
|
|
||||||
|
**File:** [src/main.h](src/main.h):
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#ifdef HAS_BLE_MESH
|
||||||
|
#include "mesh/BLEMeshHandler.h"
|
||||||
|
extern BLEMeshHandler *bleMeshHandler;
|
||||||
|
#endif
|
||||||
|
```
|
||||||
|
|
||||||
|
**File:** [src/main.cpp](src/main.cpp):
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#ifdef HAS_BLE_MESH
|
||||||
|
BLEMeshHandler *bleMeshHandler = nullptr;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// In setup():
|
||||||
|
#ifdef HAS_BLE_MESH
|
||||||
|
#if defined(ARCH_ESP32)
|
||||||
|
bleMeshHandler = new NimbleBLEMesh();
|
||||||
|
#elif defined(ARCH_NRF52)
|
||||||
|
bleMeshHandler = new NRF52BLEMesh();
|
||||||
|
#endif
|
||||||
|
bleMeshHandler->start();
|
||||||
|
#endif
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.3 NODENUM_BROADCAST_NO_LORA
|
||||||
|
|
||||||
|
Already reserved ([MeshTypes.h:13-14](src/mesh/MeshTypes.h#L13-L14)) and already dropped by LoRa ([RadioLibInterface.cpp:170](src/mesh/RadioLibInterface.cpp#L170)). BLE mesh handler should still send packets addressed to `NODENUM_BROADCAST_NO_LORA` — this is its purpose.
|
||||||
|
|
||||||
|
### 5.4 Feature flag
|
||||||
|
|
||||||
|
**File:** `src/configuration.h` or per-variant `platformio.ini`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#define HAS_BLE_MESH 1 // Enable BLE mesh transport
|
||||||
|
```
|
||||||
|
|
||||||
|
Disabled by default. Enabled per-board in variant configs for boards with sufficient BLE capability.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 6: Peer Discovery & Management
|
||||||
|
|
||||||
|
### 6.1 Discovery methods
|
||||||
|
|
||||||
|
1. **Passive scanning**: Listen for Meshtastic manufacturer data in BLE advertisements
|
||||||
|
2. **Service discovery**: Scan for Mesh Relay Service UUID (GATT mode)
|
||||||
|
3. **NodeDB integration**: Update `meshtastic_NodeInfoLite` with BLE reachability when a peer is discovered
|
||||||
|
|
||||||
|
### 6.2 Peer table
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct BLEMeshPeer {
|
||||||
|
NodeNum nodeNum;
|
||||||
|
// Platform-specific BLE address (ble_addr_t on ESP32, ble_gap_addr_t on nRF52)
|
||||||
|
uint8_t addr[6];
|
||||||
|
uint8_t addrType;
|
||||||
|
int8_t rssi;
|
||||||
|
uint32_t lastSeenMs;
|
||||||
|
uint16_t connHandle; // 0 = not connected
|
||||||
|
uint8_t failedConnAttempts;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Max peers: configurable (default 8), bounded by platform connection limits.
|
||||||
|
|
||||||
|
### 6.3 Connection policy (GATT mode)
|
||||||
|
|
||||||
|
- Auto-connect if RSSI > threshold and slots available
|
||||||
|
- Disconnect stale peers (no traffic for N minutes)
|
||||||
|
- Prefer advertising for broadcasts, GATT for directed/reliable
|
||||||
|
- Exponential backoff on failed connection attempts
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 7: Power Management
|
||||||
|
|
||||||
|
### 7.1 Scan duty cycling
|
||||||
|
|
||||||
|
```
|
||||||
|
Active scanning: ~15mA (nRF52), ~90mA (ESP32)
|
||||||
|
Advertising: ~5mA peak per event
|
||||||
|
Idle: ~0mA additional
|
||||||
|
|
||||||
|
Default strategy:
|
||||||
|
- Scan window: 50ms every 500ms (10% duty cycle)
|
||||||
|
- ~1.5mA average on nRF52, ~9mA on ESP32
|
||||||
|
- Configurable via BleMeshConfig.interval_ms
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.2 Sleep integration
|
||||||
|
|
||||||
|
- Light sleep: pause scanning, continue advertising at reduced rate
|
||||||
|
- Deep sleep: disable BLE mesh entirely
|
||||||
|
- Hook into existing power management observers
|
||||||
|
|
||||||
|
### 7.3 Role-based behavior
|
||||||
|
|
||||||
|
| Device Role | BLE Mesh Behavior |
|
||||||
|
| ----------- | ------------------------------------------- |
|
||||||
|
| CLIENT | Scan on-demand, advertise when sending |
|
||||||
|
| CLIENT_MUTE | Receive only, no rebroadcast |
|
||||||
|
| ROUTER | Continuous scan+advertise, max connections |
|
||||||
|
| ROUTER_LATE | Delayed rebroadcast (same as LoRa behavior) |
|
||||||
|
| SENSOR | Minimal: advertise telemetry only |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 8: Testing & Validation
|
||||||
|
|
||||||
|
### 8.1 Unit tests
|
||||||
|
|
||||||
|
- BLE wire format encode/decode roundtrip
|
||||||
|
- PacketHistory deduplication across LoRa + BLE
|
||||||
|
- Peer table management (add, prune, limits)
|
||||||
|
- Echo prevention (don't re-send BLE-sourced packets over BLE)
|
||||||
|
|
||||||
|
### 8.2 Integration tests
|
||||||
|
|
||||||
|
- Two ESP32 nodes: BLE-only mesh (no LoRa)
|
||||||
|
- Two nRF52 nodes: BLE-only mesh
|
||||||
|
- Three nodes: multi-hop BLE relay
|
||||||
|
- Hybrid: Node A (LoRa+BLE) <-> Node B (BLE only) <-> Node C (LoRa+BLE)
|
||||||
|
- Phone API + BLE mesh coexistence
|
||||||
|
- Power consumption profiling
|
||||||
|
|
||||||
|
### 8.3 Compatibility matrix
|
||||||
|
|
||||||
|
| Test | ESP32 | ESP32-S3 | ESP32-C3 | ESP32-C6 | nRF52840 |
|
||||||
|
| ------------- | ----------- | -------- | -------- | -------- | -------- |
|
||||||
|
| Adv mode | GATT only\* | Yes | Yes | Yes | Yes |
|
||||||
|
| GATT mode | Yes | Yes | Yes | Yes | Yes |
|
||||||
|
| Phone coexist | Yes | Yes | Yes | Yes | Yes |
|
||||||
|
| Multi-hop | Yes | Yes | Yes | Yes | Yes |
|
||||||
|
|
||||||
|
\*ESP32 (BLE 4.2): legacy adv limited to 31B, use GATT-only mode
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementation Order
|
||||||
|
|
||||||
|
### Milestone 1: Foundation
|
||||||
|
|
||||||
|
1. Protobuf additions (`TRANSPORT_BLE_MESH`, `BleMeshConfig`)
|
||||||
|
2. `BLEMeshHandler` base class
|
||||||
|
3. Router hook (alongside UDP handler)
|
||||||
|
4. Feature flag + main.cpp wiring
|
||||||
|
|
||||||
|
### Milestone 2: ESP32 MVP
|
||||||
|
|
||||||
|
5. `NimbleBLEMesh` — advertising mode on ESP32-S3
|
||||||
|
6. Phone BLE coexistence validation
|
||||||
|
7. Basic two-node BLE mesh test
|
||||||
|
|
||||||
|
### Milestone 3: nRF52 MVP
|
||||||
|
|
||||||
|
8. `NRF52BLEMesh` — advertising mode
|
||||||
|
9. Cross-platform interop (ESP32 <-> nRF52 BLE mesh)
|
||||||
|
|
||||||
|
### Milestone 4: GATT Mode
|
||||||
|
|
||||||
|
10. GATT service + connection management for ESP32
|
||||||
|
11. GATT service + connection management for nRF52
|
||||||
|
12. Peer discovery & table management
|
||||||
|
|
||||||
|
### Milestone 5: Polish
|
||||||
|
|
||||||
|
13. Power management integration
|
||||||
|
14. Full test matrix
|
||||||
|
15. ESP32 (BLE 4.2) GATT-only fallback
|
||||||
|
16. Admin UI / config in phone apps
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Risks & Mitigations
|
||||||
|
|
||||||
|
| Risk | Impact | Mitigation |
|
||||||
|
| -------------------------------- | ------------------------- | ---------------------------------------------------------------------------- |
|
||||||
|
| BLE scan power drain | Battery life | Duty cycling, role-based policy |
|
||||||
|
| Phone BLE drops during mesh scan | UX regression | Separate adv sets, careful interleaving |
|
||||||
|
| NimBLE/Bluefruit API differences | Platform code duplication | Thin platform subclasses, shared base logic |
|
||||||
|
| No extended adv on ESP32 (4.2) | Can't fit packets in adv | GATT-only fallback |
|
||||||
|
| BLE range (~30-100m) | Coverage gaps | Hybrid LoRa+BLE; Coded PHY on nRF52 |
|
||||||
|
| Packet storms in dense BLE mesh | Saturation | Existing flood mitigation (PacketHistory, hop_limit, role-based rebroadcast) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Files to Create
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
| ------------------------------------- | --------------------------------------------- |
|
||||||
|
| `src/mesh/BLEMeshHandler.h` | Base handler class (like UdpMulticastHandler) |
|
||||||
|
| `src/nimble/NimbleBLEMesh.h` | ESP32 BLE mesh header |
|
||||||
|
| `src/nimble/NimbleBLEMesh.cpp` | ESP32 NimBLE implementation |
|
||||||
|
| `src/platform/nrf52/NRF52BLEMesh.h` | nRF52 BLE mesh header |
|
||||||
|
| `src/platform/nrf52/NRF52BLEMesh.cpp` | nRF52 Bluefruit implementation |
|
||||||
|
|
||||||
|
## Files to Modify
|
||||||
|
|
||||||
|
| File | Change |
|
||||||
|
| --------------------------------------- | -------------------------------------------------- |
|
||||||
|
| `src/mesh/Router.cpp` | Add `bleMeshHandler->onSend()` next to UDP handler |
|
||||||
|
| `src/main.h` | Declare `extern BLEMeshHandler *bleMeshHandler` |
|
||||||
|
| `src/main.cpp` | Instantiate + start BLE mesh handler |
|
||||||
|
| `src/configuration.h` | `HAS_BLE_MESH` feature flag |
|
||||||
|
| Protobufs (`mesh.proto`) | `TRANSPORT_BLE_MESH` enum + `BleMeshConfig` |
|
||||||
|
| `src/nimble/NimbleBluetooth.cpp` | Coexistence (adv set separation) |
|
||||||
|
| `src/platform/nrf52/NRF52Bluetooth.cpp` | Coexistence (scanner sharing) |
|
||||||
|
| Platform `variant.h` / `platformio.ini` | Enable per-board |
|
||||||
Generated
+44
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-compat": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1767039857,
|
||||||
|
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1766314097,
|
||||||
|
"narHash": "sha256-laJftWbghBehazn/zxVJ8NdENVgjccsWAdAqKXhErrM=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "306ea70f9eb0fb4e040f8540e2deab32ed7e2055",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-compat": "flake-compat",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
{
|
||||||
|
description = "Nix flake to compile Meshtastic firmware";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||||
|
|
||||||
|
# Shim to make flake.nix work with stable Nix.
|
||||||
|
flake-compat = {
|
||||||
|
url = "github:NixOS/flake-compat";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs =
|
||||||
|
inputs:
|
||||||
|
let
|
||||||
|
lib = inputs.nixpkgs.lib;
|
||||||
|
|
||||||
|
forAllSystems =
|
||||||
|
fn:
|
||||||
|
lib.genAttrs lib.systems.flakeExposed (
|
||||||
|
system:
|
||||||
|
fn {
|
||||||
|
pkgs = import inputs.nixpkgs {
|
||||||
|
inherit system;
|
||||||
|
};
|
||||||
|
inherit system;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
devShells = forAllSystems (
|
||||||
|
{ pkgs, ... }:
|
||||||
|
let
|
||||||
|
python3 = pkgs.python312.withPackages (
|
||||||
|
ps: with ps; [
|
||||||
|
google
|
||||||
|
]
|
||||||
|
);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
default = pkgs.mkShell {
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
python3
|
||||||
|
platformio
|
||||||
|
];
|
||||||
|
|
||||||
|
shellHook = ''
|
||||||
|
# Set up PlatformIO to use a local core directory.
|
||||||
|
export PLATFORMIO_CORE_DIR=$PWD/.platformio
|
||||||
|
# Tell pip to put packages into $PIP_PREFIX instead of the usual
|
||||||
|
# location. This is especially necessary under NixOS to avoid having
|
||||||
|
# pip trying to write to the read-only Nix store. For more info,
|
||||||
|
# see https://wiki.nixos.org/wiki/Python
|
||||||
|
export PIP_PREFIX=$PWD/.python3
|
||||||
|
export PYTHONPATH="$PIP_PREFIX/${python3.sitePackages}"
|
||||||
|
export PATH="$PIP_PREFIX/bin:$PATH"
|
||||||
|
# Avoids reproducibility issues with some Python packages
|
||||||
|
# See https://nixos.org/manual/nixpkgs/stable/#python-setup.py-bdist_wheel-cannot-create-.whl
|
||||||
|
unset SOURCE_DATE_EPOCH
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -49,6 +49,7 @@ BuildRequires: pkgconfig(libulfius)
|
|||||||
BuildRequires: pkgconfig(x11)
|
BuildRequires: pkgconfig(x11)
|
||||||
BuildRequires: pkgconfig(libinput)
|
BuildRequires: pkgconfig(libinput)
|
||||||
BuildRequires: pkgconfig(xkbcommon-x11)
|
BuildRequires: pkgconfig(xkbcommon-x11)
|
||||||
|
BuildRequires: pkgconfig(sdl2)
|
||||||
|
|
||||||
# libbsd is needed on older Fedora/RHEL to provide 'strlcpy'
|
# libbsd is needed on older Fedora/RHEL to provide 'strlcpy'
|
||||||
%if 0%{?fedora} >= 39 || 0%{?rhel} >= 10
|
%if 0%{?fedora} >= 39 || 0%{?rhel} >= 10
|
||||||
@@ -59,8 +60,14 @@ BuildRequires: pkgconfig(libbsd-overlay)
|
|||||||
|
|
||||||
Requires: systemd-udev
|
Requires: systemd-udev
|
||||||
|
|
||||||
|
# Declare that this package provides the user/group it creates in %pre
|
||||||
|
# Required for Fedora 43+ which tracks users/groups as RPM dependencies
|
||||||
|
Provides: user(%{meshtasticd_user})
|
||||||
|
Provides: group(%{meshtasticd_user})
|
||||||
|
Provides: group(spi)
|
||||||
|
|
||||||
%description
|
%description
|
||||||
Meshtastic daemon for controlling Meshtastic devices. Meshtastic is an off-grid
|
Meshtastic daemon. Meshtastic is an off-grid
|
||||||
text communication platform that uses inexpensive LoRa radios.
|
text communication platform that uses inexpensive LoRa radios.
|
||||||
|
|
||||||
%prep
|
%prep
|
||||||
@@ -151,6 +158,7 @@ fi
|
|||||||
%license LICENSE
|
%license LICENSE
|
||||||
%doc README.md
|
%doc README.md
|
||||||
%{_bindir}/meshtasticd
|
%{_bindir}/meshtasticd
|
||||||
|
%{_bindir}/meshtasticd-start.sh
|
||||||
%dir %{_localstatedir}/lib/meshtasticd
|
%dir %{_localstatedir}/lib/meshtasticd
|
||||||
%{_udevrulesdir}/99-meshtasticd-udev.rules
|
%{_udevrulesdir}/99-meshtasticd-udev.rules
|
||||||
%dir %{_sysconfdir}/meshtasticd
|
%dir %{_sysconfdir}/meshtasticd
|
||||||
|
|||||||
@@ -43,13 +43,11 @@ class Esp32C3ExceptionDecoder(DeviceMonitorFilterBase):
|
|||||||
self.enabled = self.setup_paths()
|
self.enabled = self.setup_paths()
|
||||||
|
|
||||||
if self.config.get("env:" + self.environment, "build_type") != "debug":
|
if self.config.get("env:" + self.environment, "build_type") != "debug":
|
||||||
print(
|
print("""
|
||||||
"""
|
|
||||||
Please build project in debug configuration to get more details about an exception.
|
Please build project in debug configuration to get more details about an exception.
|
||||||
See https://docs.platformio.org/page/projectconf/build_configurations.html
|
See https://docs.platformio.org/page/projectconf/build_configurations.html
|
||||||
|
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|||||||
+35
-16
@@ -50,12 +50,15 @@ build_flags = -Wno-missing-field-initializers
|
|||||||
-DRADIOLIB_EXCLUDE_APRS=1
|
-DRADIOLIB_EXCLUDE_APRS=1
|
||||||
-DRADIOLIB_EXCLUDE_LORAWAN=1
|
-DRADIOLIB_EXCLUDE_LORAWAN=1
|
||||||
-DMESHTASTIC_EXCLUDE_DROPZONE=1
|
-DMESHTASTIC_EXCLUDE_DROPZONE=1
|
||||||
|
-DMESHTASTIC_EXCLUDE_REPLYBOT=1
|
||||||
-DMESHTASTIC_EXCLUDE_REMOTEHARDWARE=1
|
-DMESHTASTIC_EXCLUDE_REMOTEHARDWARE=1
|
||||||
-DMESHTASTIC_EXCLUDE_HEALTH_TELEMETRY=1
|
-DMESHTASTIC_EXCLUDE_HEALTH_TELEMETRY=1
|
||||||
-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
|
||||||
-DMESHTASTIC_EXCLUDE_POWERMON=1
|
-DMESHTASTIC_EXCLUDE_POWERMON=1
|
||||||
|
-DMESHTASTIC_EXCLUDE_STATUS=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
|
||||||
|
-DLED_BUILTIN=-1
|
||||||
#-DBUILD_EPOCH=$UNIX_TIME ; set in platformio-custom.py now
|
#-DBUILD_EPOCH=$UNIX_TIME ; set in platformio-custom.py now
|
||||||
#-D OLED_PL=1
|
#-D OLED_PL=1
|
||||||
#-D DEBUG_HEAP=1 ; uncomment to add free heap space / memory leak debugging logs
|
#-D DEBUG_HEAP=1 ; uncomment to add free heap space / memory leak debugging logs
|
||||||
@@ -65,7 +68,7 @@ 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/b34c6817c25d6faabb3a8a162b5d14fb75395433.zip
|
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/21e484f409cde18d44012caef84c244eb5ca28f3.zip
|
||||||
# renovate: datasource=git-refs depName=meshtastic-OneButton packageName=https://github.com/meshtastic/OneButton gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic-OneButton packageName=https://github.com/meshtastic/OneButton gitBranch=master
|
||||||
https://github.com/meshtastic/OneButton/archive/fa352d668c53f290cfa480a5f79ad422cd828c70.zip
|
https://github.com/meshtastic/OneButton/archive/fa352d668c53f290cfa480a5f79ad422cd828c70.zip
|
||||||
# renovate: datasource=git-refs depName=meshtastic-arduino-fsm packageName=https://github.com/meshtastic/arduino-fsm gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic-arduino-fsm packageName=https://github.com/meshtastic/arduino-fsm gitBranch=master
|
||||||
@@ -114,12 +117,12 @@ lib_deps =
|
|||||||
[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
|
||||||
jgromes/RadioLib@7.5.0
|
jgromes/RadioLib@7.6.0
|
||||||
|
|
||||||
[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/5a870c623a4e9ab7a7abe3d02950536f107d1a31.zip
|
https://github.com/meshtastic/device-ui/archive/6c75195e9987b7a49563232234f2f868dd343cae.zip
|
||||||
|
|
||||||
; Common libs for environmental measurements in telemetry module
|
; Common libs for environmental measurements in telemetry module
|
||||||
[environmental_base]
|
[environmental_base]
|
||||||
@@ -135,7 +138,7 @@ lib_deps =
|
|||||||
# renovate: datasource=custom.pio depName=Adafruit BME280 packageName=adafruit/library/Adafruit BME280 Library
|
# renovate: datasource=custom.pio depName=Adafruit BME280 packageName=adafruit/library/Adafruit BME280 Library
|
||||||
adafruit/Adafruit BME280 Library@2.3.0
|
adafruit/Adafruit BME280 Library@2.3.0
|
||||||
# renovate: datasource=custom.pio depName=Adafruit DPS310 packageName=adafruit/library/Adafruit DPS310
|
# renovate: datasource=custom.pio depName=Adafruit DPS310 packageName=adafruit/library/Adafruit DPS310
|
||||||
adafruit/Adafruit DPS310@1.1.5
|
adafruit/Adafruit DPS310@1.1.6
|
||||||
# renovate: datasource=custom.pio depName=Adafruit MCP9808 packageName=adafruit/library/Adafruit MCP9808 Library
|
# renovate: datasource=custom.pio depName=Adafruit MCP9808 packageName=adafruit/library/Adafruit MCP9808 Library
|
||||||
adafruit/Adafruit MCP9808 Library@2.0.2
|
adafruit/Adafruit MCP9808 Library@2.0.2
|
||||||
# renovate: datasource=custom.pio depName=Adafruit INA260 packageName=adafruit/library/Adafruit INA260 Library
|
# renovate: datasource=custom.pio depName=Adafruit INA260 packageName=adafruit/library/Adafruit INA260 Library
|
||||||
@@ -143,11 +146,11 @@ lib_deps =
|
|||||||
# renovate: datasource=custom.pio depName=Adafruit INA219 packageName=adafruit/library/Adafruit INA219
|
# renovate: datasource=custom.pio depName=Adafruit INA219 packageName=adafruit/library/Adafruit INA219
|
||||||
adafruit/Adafruit INA219@1.2.3
|
adafruit/Adafruit INA219@1.2.3
|
||||||
# renovate: datasource=custom.pio depName=Adafruit MPU6050 packageName=adafruit/library/Adafruit MPU6050
|
# renovate: datasource=custom.pio depName=Adafruit MPU6050 packageName=adafruit/library/Adafruit MPU6050
|
||||||
adafruit/Adafruit MPU6050@2.2.6
|
adafruit/Adafruit MPU6050@2.2.9
|
||||||
# renovate: datasource=custom.pio depName=Adafruit LIS3DH packageName=adafruit/library/Adafruit LIS3DH
|
# renovate: datasource=custom.pio depName=Adafruit LIS3DH packageName=adafruit/library/Adafruit LIS3DH
|
||||||
adafruit/Adafruit LIS3DH@1.3.0
|
adafruit/Adafruit LIS3DH@1.3.0
|
||||||
# renovate: datasource=custom.pio depName=Adafruit AHTX0 packageName=adafruit/library/Adafruit AHTX0
|
# renovate: datasource=custom.pio depName=Adafruit AHTX0 packageName=adafruit/library/Adafruit AHTX0
|
||||||
adafruit/Adafruit AHTX0@2.0.5
|
adafruit/Adafruit AHTX0@2.0.6
|
||||||
# renovate: datasource=custom.pio depName=Adafruit LSM6DS packageName=adafruit/library/Adafruit LSM6DS
|
# renovate: datasource=custom.pio depName=Adafruit LSM6DS packageName=adafruit/library/Adafruit LSM6DS
|
||||||
adafruit/Adafruit LSM6DS@4.7.4
|
adafruit/Adafruit LSM6DS@4.7.4
|
||||||
# renovate: datasource=custom.pio depName=Adafruit TSL2591 packageName=adafruit/library/Adafruit TSL2591 Library
|
# renovate: datasource=custom.pio depName=Adafruit TSL2591 packageName=adafruit/library/Adafruit TSL2591 Library
|
||||||
@@ -155,7 +158,7 @@ lib_deps =
|
|||||||
# renovate: datasource=custom.pio depName=EmotiBit MLX90632 packageName=emotibit/library/EmotiBit MLX90632
|
# renovate: datasource=custom.pio depName=EmotiBit MLX90632 packageName=emotibit/library/EmotiBit MLX90632
|
||||||
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.6
|
||||||
# renovate: datasource=git-refs depName=INA3221 packageName=https://github.com/sgtwilko/INA3221 gitBranch=FixOverflow
|
# renovate: datasource=git-refs depName=INA3221 packageName=https://github.com/sgtwilko/INA3221 gitBranch=FixOverflow
|
||||||
https://github.com/sgtwilko/INA3221/archive/bb03d7e9bfcc74fc798838a54f4f99738f29fc6a.zip
|
https://github.com/sgtwilko/INA3221/archive/bb03d7e9bfcc74fc798838a54f4f99738f29fc6a.zip
|
||||||
# renovate: datasource=custom.pio depName=QMC5883L Compass packageName=mprograms/library/QMC5883LCompass
|
# renovate: datasource=custom.pio depName=QMC5883L Compass packageName=mprograms/library/QMC5883LCompass
|
||||||
@@ -177,12 +180,12 @@ lib_deps =
|
|||||||
# 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
|
# renovate: datasource=custom.pio depName=Adafruit_TSL2561 packageName=adafruit/library/Adafruit TSL2561
|
||||||
adafruit/Adafruit TSL2561@1.1.2
|
adafruit/Adafruit TSL2561@1.1.3
|
||||||
# renovate: datasource=custom.pio depName=BH1750_WE packageName=wollewald/library/BH1750_WE
|
# renovate: datasource=custom.pio depName=BH1750_WE packageName=wollewald/library/BH1750_WE
|
||||||
wollewald/BH1750_WE@1.1.10
|
wollewald/BH1750_WE@1.1.10
|
||||||
|
|
||||||
; (not included in native / portduino)
|
; Common environmental sensor libraries (not included in native / portduino)
|
||||||
[environmental_extra]
|
[environmental_extra_common]
|
||||||
lib_deps =
|
lib_deps =
|
||||||
# renovate: datasource=custom.pio depName=Adafruit BMP3XX packageName=adafruit/library/Adafruit BMP3XX Library
|
# renovate: datasource=custom.pio depName=Adafruit BMP3XX packageName=adafruit/library/Adafruit BMP3XX Library
|
||||||
adafruit/Adafruit BMP3XX Library@2.1.6
|
adafruit/Adafruit BMP3XX Library@2.1.6
|
||||||
@@ -202,13 +205,29 @@ lib_deps =
|
|||||||
sparkfun/SparkFun Qwiic Scale NAU7802 Arduino Library@1.0.6
|
sparkfun/SparkFun Qwiic Scale NAU7802 Arduino Library@1.0.6
|
||||||
# renovate: datasource=custom.pio depName=ClosedCube OPT3001 packageName=closedcube/library/ClosedCube OPT3001
|
# renovate: datasource=custom.pio depName=ClosedCube OPT3001 packageName=closedcube/library/ClosedCube OPT3001
|
||||||
closedcube/ClosedCube OPT3001@1.1.2
|
closedcube/ClosedCube OPT3001@1.1.2
|
||||||
|
# renovate: datasource=git-refs depName=meshtastic-DFRobot_LarkWeatherStation packageName=https://github.com/meshtastic/DFRobot_LarkWeatherStation gitBranch=master
|
||||||
|
https://github.com/meshtastic/DFRobot_LarkWeatherStation/archive/4de3a9cadef0f6a5220a8a906cf9775b02b0040d.zip
|
||||||
|
# renovate: datasource=custom.pio depName=Sensirion Core packageName=sensirion/library/Sensirion Core
|
||||||
|
sensirion/Sensirion Core@0.7.3
|
||||||
|
# renovate: datasource=custom.pio depName=Sensirion I2C SCD4x packageName=sensirion/library/Sensirion I2C SCD4x
|
||||||
|
sensirion/Sensirion I2C SCD4x@1.1.0
|
||||||
|
# renovate: datasource=custom.pio depName=Sensirion I2C SFA3x packageName=sensirion/library/Sensirion I2C SFA3x
|
||||||
|
sensirion/Sensirion I2C SFA3x@1.0.0
|
||||||
|
# renovate: datasource=custom.pio depName=Sensirion I2C SCD30 packageName=sensirion/library/Sensirion I2C SCD30
|
||||||
|
sensirion/Sensirion I2C SCD30@1.0.0
|
||||||
|
|
||||||
|
; Environmental sensors with BSEC2 (Bosch proprietary IAQ)
|
||||||
|
[environmental_extra]
|
||||||
|
lib_deps =
|
||||||
|
${environmental_extra_common.lib_deps}
|
||||||
# renovate: datasource=custom.pio depName=Bosch BSEC2 packageName=boschsensortec/library/bsec2
|
# renovate: datasource=custom.pio depName=Bosch BSEC2 packageName=boschsensortec/library/bsec2
|
||||||
boschsensortec/bsec2@1.10.2610
|
boschsensortec/bsec2@1.10.2610
|
||||||
# renovate: datasource=custom.pio depName=Bosch BME68x packageName=boschsensortec/library/BME68x Sensor Library
|
# renovate: datasource=custom.pio depName=Bosch BME68x packageName=boschsensortec/library/BME68x Sensor Library
|
||||||
boschsensortec/BME68x Sensor Library@1.3.40408
|
boschsensortec/BME68x Sensor Library@1.3.40408
|
||||||
# renovate: datasource=git-refs depName=meshtastic-DFRobot_LarkWeatherStation packageName=https://github.com/meshtastic/DFRobot_LarkWeatherStation gitBranch=master
|
|
||||||
https://github.com/meshtastic/DFRobot_LarkWeatherStation/archive/4de3a9cadef0f6a5220a8a906cf9775b02b0040d.zip
|
; Environmental sensors without BSEC (saves ~3.5KB DRAM for original ESP32 targets)
|
||||||
# renovate: datasource=custom.pio depName=Sensirion Core packageName=sensirion/library/Sensirion Core
|
[environmental_extra_no_bsec]
|
||||||
sensirion/Sensirion Core@0.7.2
|
lib_deps =
|
||||||
# renovate: datasource=custom.pio depName=Sensirion I2C SCD4x packageName=sensirion/library/Sensirion I2C SCD4x
|
${environmental_extra_common.lib_deps}
|
||||||
sensirion/Sensirion I2C SCD4x@1.1.0
|
# renovate: datasource=custom.pio depName=adafruit/Adafruit BME680 Library packageName=adafruit/library/Adafruit BME680
|
||||||
|
adafruit/Adafruit BME680 Library@^2.0.5
|
||||||
|
|||||||
+1
-1
Submodule protobufs updated: 4b9f104a18...2edc5ab7b1
@@ -0,0 +1,12 @@
|
|||||||
|
(import (
|
||||||
|
let
|
||||||
|
lock = builtins.fromJSON (builtins.readFile ./flake.lock);
|
||||||
|
nodeName = lock.nodes.root.inputs.flake-compat;
|
||||||
|
in
|
||||||
|
fetchTarball {
|
||||||
|
url =
|
||||||
|
lock.nodes.${nodeName}.locked.url
|
||||||
|
or "https://github.com/NixOS/flake-compat/archive/${lock.nodes.${nodeName}.locked.rev}.tar.gz";
|
||||||
|
sha256 = lock.nodes.${nodeName}.locked.narHash;
|
||||||
|
}
|
||||||
|
) { src = ./.; }).shellNix
|
||||||
+68
-59
@@ -1,19 +1,23 @@
|
|||||||
|
#ifndef AMBIENTLIGHTINGTHREAD_H
|
||||||
|
#define AMBIENTLIGHTINGTHREAD_H
|
||||||
|
|
||||||
#include "Observer.h"
|
#include "Observer.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
|
#include "detect/ScanI2C.h"
|
||||||
|
#include "sleep.h"
|
||||||
|
|
||||||
#ifdef HAS_NCP5623
|
#ifdef HAS_NCP5623
|
||||||
#include <graphics/RAKled.h>
|
#include <Wire.h>
|
||||||
NCP5623 rgb;
|
|
||||||
|
#include <NCP5623.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAS_LP5562
|
#ifdef HAS_LP5562
|
||||||
#include <graphics/NomadStarLED.h>
|
#include <graphics/NomadStarLED.h>
|
||||||
LP5562 rgbw;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAS_NEOPIXEL
|
#ifdef HAS_NEOPIXEL
|
||||||
#include <graphics/NeoPixel.h>
|
#include <Adafruit_NeoPixel.h>
|
||||||
Adafruit_NeoPixel pixels(NEOPIXEL_COUNT, NEOPIXEL_DATA, NEOPIXEL_TYPE);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef UNPHONE
|
#ifdef UNPHONE
|
||||||
@@ -21,10 +25,24 @@ Adafruit_NeoPixel pixels(NEOPIXEL_COUNT, NEOPIXEL_DATA, NEOPIXEL_TYPE);
|
|||||||
extern unPhone unphone;
|
extern unPhone unphone;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace concurrency
|
|
||||||
{
|
|
||||||
class AmbientLightingThread : public concurrency::OSThread
|
class AmbientLightingThread : public concurrency::OSThread
|
||||||
{
|
{
|
||||||
|
friend class StatusLEDModule; // Let the LEDStatusModule trigger the ambient lighting for notifications and battery status.
|
||||||
|
friend class ExternalNotificationModule; // Let the ExternalNotificationModule trigger the ambient lighting for notifications.
|
||||||
|
|
||||||
|
private:
|
||||||
|
#ifdef HAS_NCP5623
|
||||||
|
NCP5623 rgb;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAS_LP5562
|
||||||
|
LP5562 rgbw;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAS_NEOPIXEL
|
||||||
|
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NEOPIXEL_COUNT, NEOPIXEL_DATA, NEOPIXEL_TYPE);
|
||||||
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit AmbientLightingThread(ScanI2C::DeviceType type) : OSThread("AmbientLighting")
|
explicit AmbientLightingThread(ScanI2C::DeviceType type) : OSThread("AmbientLighting")
|
||||||
{
|
{
|
||||||
@@ -36,14 +54,15 @@ class AmbientLightingThread : public concurrency::OSThread
|
|||||||
moduleConfig.ambient_lighting.led_state = true;
|
moduleConfig.ambient_lighting.led_state = true;
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
// Uncomment to test module
|
#if AMBIENT_LIGHTING_TEST
|
||||||
// moduleConfig.ambient_lighting.led_state = true;
|
// define to enable test
|
||||||
// moduleConfig.ambient_lighting.current = 10;
|
moduleConfig.ambient_lighting.led_state = true;
|
||||||
|
moduleConfig.ambient_lighting.current = 10;
|
||||||
// Default to a color based on our node number
|
// Default to a color based on our node number
|
||||||
// moduleConfig.ambient_lighting.red = (myNodeInfo.my_node_num & 0xFF0000) >> 16;
|
moduleConfig.ambient_lighting.red = (myNodeInfo.my_node_num & 0xFF0000) >> 16;
|
||||||
// moduleConfig.ambient_lighting.green = (myNodeInfo.my_node_num & 0x00FF00) >> 8;
|
moduleConfig.ambient_lighting.green = (myNodeInfo.my_node_num & 0x00FF00) >> 8;
|
||||||
// moduleConfig.ambient_lighting.blue = myNodeInfo.my_node_num & 0x0000FF;
|
moduleConfig.ambient_lighting.blue = myNodeInfo.my_node_num & 0x0000FF;
|
||||||
|
#endif
|
||||||
#if defined(HAS_NCP5623) || defined(HAS_LP5562)
|
#if defined(HAS_NCP5623) || defined(HAS_LP5562)
|
||||||
_type = type;
|
_type = type;
|
||||||
if (_type == ScanI2C::DeviceType::NONE) {
|
if (_type == ScanI2C::DeviceType::NONE) {
|
||||||
@@ -53,11 +72,6 @@ class AmbientLightingThread : public concurrency::OSThread
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAS_RGB_LED
|
#ifdef HAS_RGB_LED
|
||||||
if (!moduleConfig.ambient_lighting.led_state) {
|
|
||||||
LOG_DEBUG("AmbientLighting Disable due to moduleConfig.ambient_lighting.led_state OFF");
|
|
||||||
disable();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
LOG_DEBUG("AmbientLighting init");
|
LOG_DEBUG("AmbientLighting init");
|
||||||
#ifdef HAS_NCP5623
|
#ifdef HAS_NCP5623
|
||||||
if (_type == ScanI2C::NCP5623) {
|
if (_type == ScanI2C::NCP5623) {
|
||||||
@@ -77,7 +91,13 @@ class AmbientLightingThread : public concurrency::OSThread
|
|||||||
pixels.clear(); // Set all pixel colors to 'off'
|
pixels.clear(); // Set all pixel colors to 'off'
|
||||||
pixels.setBrightness(moduleConfig.ambient_lighting.current);
|
pixels.setBrightness(moduleConfig.ambient_lighting.current);
|
||||||
#endif
|
#endif
|
||||||
setLighting();
|
if (!moduleConfig.ambient_lighting.led_state) {
|
||||||
|
LOG_DEBUG("AmbientLighting Disable due to moduleConfig.ambient_lighting.led_state OFF");
|
||||||
|
disable();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setLighting(moduleConfig.ambient_lighting.current, moduleConfig.ambient_lighting.red,
|
||||||
|
moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
|
||||||
#endif
|
#endif
|
||||||
#if defined(HAS_NCP5623) || defined(HAS_LP5562)
|
#if defined(HAS_NCP5623) || defined(HAS_LP5562)
|
||||||
}
|
}
|
||||||
@@ -91,7 +111,8 @@ class AmbientLightingThread : public concurrency::OSThread
|
|||||||
#if defined(HAS_NCP5623) || defined(HAS_LP5562)
|
#if defined(HAS_NCP5623) || defined(HAS_LP5562)
|
||||||
if ((_type == ScanI2C::NCP5623 || _type == ScanI2C::LP5562) && moduleConfig.ambient_lighting.led_state) {
|
if ((_type == ScanI2C::NCP5623 || _type == ScanI2C::LP5562) && moduleConfig.ambient_lighting.led_state) {
|
||||||
#endif
|
#endif
|
||||||
setLighting();
|
setLighting(moduleConfig.ambient_lighting.current, moduleConfig.ambient_lighting.red,
|
||||||
|
moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
|
||||||
return 30000; // 30 seconds to reset from any animations that may have been running from Ext. Notification
|
return 30000; // 30 seconds to reset from any animations that may have been running from Ext. Notification
|
||||||
#if defined(HAS_NCP5623) || defined(HAS_LP5562)
|
#if defined(HAS_NCP5623) || defined(HAS_LP5562)
|
||||||
}
|
}
|
||||||
@@ -148,65 +169,53 @@ class AmbientLightingThread : public concurrency::OSThread
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setLighting()
|
protected:
|
||||||
|
void setLighting(float current, uint8_t red, uint8_t green, uint8_t blue)
|
||||||
{
|
{
|
||||||
#ifdef HAS_NCP5623
|
#ifdef HAS_NCP5623
|
||||||
rgb.setCurrent(moduleConfig.ambient_lighting.current);
|
rgb.setCurrent(current);
|
||||||
rgb.setRed(moduleConfig.ambient_lighting.red);
|
rgb.setRed(red);
|
||||||
rgb.setGreen(moduleConfig.ambient_lighting.green);
|
rgb.setGreen(green);
|
||||||
rgb.setBlue(moduleConfig.ambient_lighting.blue);
|
rgb.setBlue(blue);
|
||||||
LOG_DEBUG("Init NCP5623 Ambient light w/ current=%d, red=%d, green=%d, blue=%d",
|
LOG_DEBUG("Init NCP5623 Ambient light w/ current=%f, red=%d, green=%d, blue=%d", current, red, green, blue);
|
||||||
moduleConfig.ambient_lighting.current, moduleConfig.ambient_lighting.red,
|
|
||||||
moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAS_LP5562
|
#ifdef HAS_LP5562
|
||||||
rgbw.setCurrent(moduleConfig.ambient_lighting.current);
|
rgbw.setCurrent(current);
|
||||||
rgbw.setRed(moduleConfig.ambient_lighting.red);
|
rgbw.setRed(red);
|
||||||
rgbw.setGreen(moduleConfig.ambient_lighting.green);
|
rgbw.setGreen(green);
|
||||||
rgbw.setBlue(moduleConfig.ambient_lighting.blue);
|
rgbw.setBlue(blue);
|
||||||
LOG_DEBUG("Init LP5562 Ambient light w/ current=%d, red=%d, green=%d, blue=%d", moduleConfig.ambient_lighting.current,
|
LOG_DEBUG("Init LP5562 Ambient light w/ current=%f, red=%d, green=%d, blue=%d", current, red, green, blue);
|
||||||
moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAS_NEOPIXEL
|
#ifdef HAS_NEOPIXEL
|
||||||
pixels.fill(pixels.Color(moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green,
|
pixels.fill(pixels.Color(red, green, blue), 0, NEOPIXEL_COUNT);
|
||||||
moduleConfig.ambient_lighting.blue),
|
|
||||||
0, NEOPIXEL_COUNT);
|
|
||||||
|
|
||||||
// RadioMaster Bandit has addressable LED at the two buttons
|
// RadioMaster Bandit has addressable LED at the two buttons
|
||||||
// this allow us to set different lighting for them in variant.h file.
|
// this allow us to set different lighting for them in variant.h file.
|
||||||
#ifdef RADIOMASTER_900_BANDIT
|
|
||||||
#if defined(BUTTON1_COLOR) && defined(BUTTON1_COLOR_INDEX)
|
#if defined(BUTTON1_COLOR) && defined(BUTTON1_COLOR_INDEX)
|
||||||
pixels.fill(BUTTON1_COLOR, BUTTON1_COLOR_INDEX, 1);
|
pixels.fill(BUTTON1_COLOR, BUTTON1_COLOR_INDEX, 1);
|
||||||
#endif
|
#endif
|
||||||
#if defined(BUTTON2_COLOR) && defined(BUTTON2_COLOR_INDEX)
|
#if defined(BUTTON2_COLOR) && defined(BUTTON2_COLOR_INDEX)
|
||||||
pixels.fill(BUTTON2_COLOR, BUTTON2_COLOR_INDEX, 1);
|
pixels.fill(BUTTON2_COLOR, BUTTON2_COLOR_INDEX, 1);
|
||||||
#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)=%f, red=%d, green=%d, blue=%d",
|
||||||
// moduleConfig.ambient_lighting.current, moduleConfig.ambient_lighting.red,
|
// current, red, green, 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 - red);
|
||||||
analogWrite(RGBLED_GREEN, 255 - moduleConfig.ambient_lighting.green);
|
analogWrite(RGBLED_GREEN, 255 - green);
|
||||||
analogWrite(RGBLED_BLUE, 255 - moduleConfig.ambient_lighting.blue);
|
analogWrite(RGBLED_BLUE, 255 - blue);
|
||||||
LOG_DEBUG("Init Ambient light RGB Common Anode w/ red=%d, green=%d, blue=%d", moduleConfig.ambient_lighting.red,
|
LOG_DEBUG("Init Ambient light RGB Common Anode w/ red=%d, green=%d, blue=%d", red, green, blue);
|
||||||
moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
|
|
||||||
#elif defined(RGBLED_RED)
|
#elif defined(RGBLED_RED)
|
||||||
analogWrite(RGBLED_RED, moduleConfig.ambient_lighting.red);
|
analogWrite(RGBLED_RED, red);
|
||||||
analogWrite(RGBLED_GREEN, moduleConfig.ambient_lighting.green);
|
analogWrite(RGBLED_GREEN, green);
|
||||||
analogWrite(RGBLED_BLUE, moduleConfig.ambient_lighting.blue);
|
analogWrite(RGBLED_BLUE, blue);
|
||||||
LOG_DEBUG("Init Ambient light RGB Common Cathode w/ red=%d, green=%d, blue=%d", moduleConfig.ambient_lighting.red,
|
LOG_DEBUG("Init Ambient light RGB Common Cathode w/ red=%d, green=%d, blue=%d", red, green, blue);
|
||||||
moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef UNPHONE
|
#ifdef UNPHONE
|
||||||
unphone.rgb(moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green,
|
unphone.rgb(red, green, blue);
|
||||||
moduleConfig.ambient_lighting.blue);
|
LOG_DEBUG("Init unPhone Ambient light w/ red=%d, green=%d, blue=%d", red, green, blue);
|
||||||
LOG_DEBUG("Init unPhone Ambient light w/ red=%d, green=%d, blue=%d", moduleConfig.ambient_lighting.red,
|
|
||||||
moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
#endif // AMBIENTLIGHTINGTHREAD_H
|
||||||
} // namespace concurrency
|
|
||||||
+11
-16
@@ -4,6 +4,7 @@
|
|||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "sleep.h"
|
#include "sleep.h"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#ifdef HAS_I2S
|
#ifdef HAS_I2S
|
||||||
#include <AudioFileSourcePROGMEM.h>
|
#include <AudioFileSourcePROGMEM.h>
|
||||||
@@ -29,9 +30,9 @@ class AudioThread : public concurrency::OSThread
|
|||||||
io.digitalWrite(EXPANDS_AMP_EN, HIGH);
|
io.digitalWrite(EXPANDS_AMP_EN, HIGH);
|
||||||
#endif
|
#endif
|
||||||
setCPUFast(true);
|
setCPUFast(true);
|
||||||
rtttlFile = new AudioFileSourcePROGMEM(data, len);
|
rtttlFile = std::unique_ptr<AudioFileSourcePROGMEM>(new AudioFileSourcePROGMEM(data, len));
|
||||||
i2sRtttl = new AudioGeneratorRTTTL();
|
i2sRtttl = std::unique_ptr<AudioGeneratorRTTTL>(new AudioGeneratorRTTTL());
|
||||||
i2sRtttl->begin(rtttlFile, audioOut);
|
i2sRtttl->begin(rtttlFile.get(), audioOut.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also handles actually playing the RTTTL, needs to be called in loop
|
// Also handles actually playing the RTTTL, needs to be called in loop
|
||||||
@@ -47,14 +48,10 @@ class AudioThread : public concurrency::OSThread
|
|||||||
{
|
{
|
||||||
if (i2sRtttl != nullptr) {
|
if (i2sRtttl != nullptr) {
|
||||||
i2sRtttl->stop();
|
i2sRtttl->stop();
|
||||||
delete i2sRtttl;
|
|
||||||
i2sRtttl = nullptr;
|
i2sRtttl = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rtttlFile != nullptr) {
|
rtttlFile = nullptr;
|
||||||
delete rtttlFile;
|
|
||||||
rtttlFile = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
setCPUFast(false);
|
setCPUFast(false);
|
||||||
#ifdef T_LORA_PAGER
|
#ifdef T_LORA_PAGER
|
||||||
@@ -66,16 +63,14 @@ class AudioThread : public concurrency::OSThread
|
|||||||
{
|
{
|
||||||
if (i2sRtttl != nullptr) {
|
if (i2sRtttl != nullptr) {
|
||||||
i2sRtttl->stop();
|
i2sRtttl->stop();
|
||||||
delete i2sRtttl;
|
|
||||||
i2sRtttl = nullptr;
|
i2sRtttl = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef T_LORA_PAGER
|
#ifdef T_LORA_PAGER
|
||||||
io.digitalWrite(EXPANDS_AMP_EN, HIGH);
|
io.digitalWrite(EXPANDS_AMP_EN, HIGH);
|
||||||
#endif
|
#endif
|
||||||
ESP8266SAM *sam = new ESP8266SAM;
|
auto sam = std::unique_ptr<ESP8266SAM>(new ESP8266SAM);
|
||||||
sam->Say(audioOut, text);
|
sam->Say(audioOut.get(), text);
|
||||||
delete sam;
|
|
||||||
setCPUFast(false);
|
setCPUFast(false);
|
||||||
#ifdef T_LORA_PAGER
|
#ifdef T_LORA_PAGER
|
||||||
io.digitalWrite(EXPANDS_AMP_EN, LOW);
|
io.digitalWrite(EXPANDS_AMP_EN, LOW);
|
||||||
@@ -96,15 +91,15 @@ class AudioThread : public concurrency::OSThread
|
|||||||
private:
|
private:
|
||||||
void initOutput()
|
void initOutput()
|
||||||
{
|
{
|
||||||
audioOut = new AudioOutputI2S(1, AudioOutputI2S::EXTERNAL_I2S);
|
audioOut = std::unique_ptr<AudioOutputI2S>(new AudioOutputI2S(1, AudioOutputI2S::EXTERNAL_I2S));
|
||||||
audioOut->SetPinout(DAC_I2S_BCK, DAC_I2S_WS, DAC_I2S_DOUT, DAC_I2S_MCLK);
|
audioOut->SetPinout(DAC_I2S_BCK, DAC_I2S_WS, DAC_I2S_DOUT, DAC_I2S_MCLK);
|
||||||
audioOut->SetGain(0.2);
|
audioOut->SetGain(0.2);
|
||||||
};
|
};
|
||||||
|
|
||||||
AudioGeneratorRTTTL *i2sRtttl = nullptr;
|
std::unique_ptr<AudioGeneratorRTTTL> i2sRtttl = nullptr;
|
||||||
AudioOutputI2S *audioOut = nullptr;
|
std::unique_ptr<AudioOutputI2S> audioOut = nullptr;
|
||||||
|
|
||||||
AudioFileSourcePROGMEM *rtttlFile = nullptr;
|
std::unique_ptr<AudioFileSourcePROGMEM> rtttlFile = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+2
-10
@@ -89,22 +89,14 @@ 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, LED_STATE_ON);
|
||||||
digitalWrite(BLE_LED, LOW);
|
|
||||||
#else
|
|
||||||
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, LED_STATE_OFF);
|
||||||
digitalWrite(BLE_LED, HIGH);
|
|
||||||
#else
|
|
||||||
digitalWrite(BLE_LED, LOW);
|
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
-66
@@ -1,66 +0,0 @@
|
|||||||
#include "Led.h"
|
|
||||||
#include "PowerMon.h"
|
|
||||||
#include "main.h"
|
|
||||||
#include "power.h"
|
|
||||||
|
|
||||||
GpioVirtPin ledForceOn, ledBlink;
|
|
||||||
|
|
||||||
#if defined(LED_PIN)
|
|
||||||
// Most boards have a GPIO for LED control
|
|
||||||
static GpioHwPin ledRawHwPin(LED_PIN);
|
|
||||||
#else
|
|
||||||
static GpioVirtPin ledRawHwPin; // Dummy pin for no hardware
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if LED_STATE_ON == 0
|
|
||||||
static GpioVirtPin ledHwPin;
|
|
||||||
static GpioNotTransformer ledInverter(&ledHwPin, &ledRawHwPin);
|
|
||||||
#else
|
|
||||||
static GpioPin &ledHwPin = ledRawHwPin;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(HAS_PMU)
|
|
||||||
/**
|
|
||||||
* A GPIO controlled by the PMU
|
|
||||||
*/
|
|
||||||
class GpioPmuPin : public GpioPin
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void set(bool value)
|
|
||||||
{
|
|
||||||
if (pmu_found && PMU) {
|
|
||||||
// blink the axp led
|
|
||||||
PMU->setChargingLedMode(value ? XPOWERS_CHG_LED_ON : XPOWERS_CHG_LED_OFF);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} ledPmuHwPin;
|
|
||||||
|
|
||||||
// In some cases we need to drive a PMU LED and a normal LED
|
|
||||||
static GpioSplitter ledFinalPin(&ledHwPin, &ledPmuHwPin);
|
|
||||||
#else
|
|
||||||
static GpioPin &ledFinalPin = ledHwPin;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_POWERMON
|
|
||||||
/**
|
|
||||||
* We monitor changes to the LED drive output because we use that as a sanity test in our power monitor stuff.
|
|
||||||
*/
|
|
||||||
class MonitoredLedPin : public GpioPin
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void set(bool value)
|
|
||||||
{
|
|
||||||
if (powerMon) {
|
|
||||||
if (value)
|
|
||||||
powerMon->setState(meshtastic_PowerMon_State_LED_On);
|
|
||||||
else
|
|
||||||
powerMon->clearState(meshtastic_PowerMon_State_LED_On);
|
|
||||||
}
|
|
||||||
ledFinalPin.set(value);
|
|
||||||
}
|
|
||||||
} monitoredLedPin;
|
|
||||||
#else
|
|
||||||
static GpioPin &monitoredLedPin = ledFinalPin;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static GpioBinaryTransformer ledForcer(&ledForceOn, &ledBlink, &monitoredLedPin, GpioBinaryTransformer::Or);
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
#include "GpioLogic.h"
|
|
||||||
#include "configuration.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ledForceOn and ledForceOff both override the normal ledBlinker behavior (which is controlled by main)
|
|
||||||
*/
|
|
||||||
extern GpioVirtPin ledForceOn, ledBlink;
|
|
||||||
+217
-99
@@ -1,11 +1,14 @@
|
|||||||
/**
|
/**
|
||||||
* @file Power.cpp
|
* @file Power.cpp
|
||||||
* @brief This file contains the implementation of the Power class, which is responsible for managing power-related functionality
|
* @brief This file contains the implementation of the Power class, which is
|
||||||
* of the device. It includes battery level sensing, power management unit (PMU) control, and power state machine management. The
|
* responsible for managing power-related functionality of the device. It
|
||||||
* Power class is used by the main device class to manage power-related functionality.
|
* includes battery level sensing, power management unit (PMU) control, and
|
||||||
|
* power state machine management. The Power class is used by the main device
|
||||||
|
* class to manage power-related functionality.
|
||||||
*
|
*
|
||||||
* The file also includes implementations of various battery level sensors, such as the AnalogBatteryLevel class, which assumes
|
* The file also includes implementations of various battery level sensors, such
|
||||||
* the battery voltage is attached via a voltage-divider to an analog input.
|
* as the AnalogBatteryLevel class, which assumes the battery voltage is
|
||||||
|
* attached via a voltage-divider to an analog input.
|
||||||
*
|
*
|
||||||
* This file is part of the Meshtastic project.
|
* This file is part of the Meshtastic project.
|
||||||
* For more information, see: https://meshtastic.org/
|
* For more information, see: https://meshtastic.org/
|
||||||
@@ -19,6 +22,7 @@
|
|||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "meshUtils.h"
|
#include "meshUtils.h"
|
||||||
|
#include "power/PowerHAL.h"
|
||||||
#include "sleep.h"
|
#include "sleep.h"
|
||||||
|
|
||||||
#if defined(ARCH_PORTDUINO)
|
#if defined(ARCH_PORTDUINO)
|
||||||
@@ -171,22 +175,12 @@ Power *power;
|
|||||||
|
|
||||||
using namespace meshtastic;
|
using namespace meshtastic;
|
||||||
|
|
||||||
#ifndef AREF_VOLTAGE
|
// NRF52 has AREF_VOLTAGE defined in architecture.h but
|
||||||
#if defined(ARCH_NRF52)
|
// make sure it's included. If something is wrong with NRF52
|
||||||
/*
|
// definition - compilation will fail on missing definition
|
||||||
* Internal Reference is +/-0.6V, with an adjustable gain of 1/6, 1/5, 1/4,
|
#if !defined(AREF_VOLTAGE) && !defined(ARCH_NRF52)
|
||||||
* 1/3, 1/2 or 1, meaning 3.6, 3.0, 2.4, 1.8, 1.2 or 0.6V for the ADC levels.
|
|
||||||
*
|
|
||||||
* External Reference is VDD/4, with an adjustable gain of 1, 2 or 4, meaning
|
|
||||||
* VDD/4, VDD/2 or VDD for the ADC levels.
|
|
||||||
*
|
|
||||||
* Default settings are internal reference with 1/6 gain (GND..3.6V ADC range)
|
|
||||||
*/
|
|
||||||
#define AREF_VOLTAGE 3.6
|
|
||||||
#else
|
|
||||||
#define AREF_VOLTAGE 3.3
|
#define AREF_VOLTAGE 3.3
|
||||||
#endif
|
#endif
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If this board has a battery level sensor, set this to a valid implementation
|
* If this board has a battery level sensor, set this to a valid implementation
|
||||||
@@ -233,7 +227,8 @@ static void battery_adcDisable()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple battery level sensor that assumes the battery voltage is attached via a voltage-divider to an analog input
|
* A simple battery level sensor that assumes the battery voltage is attached
|
||||||
|
* via a voltage-divider to an analog input
|
||||||
*/
|
*/
|
||||||
class AnalogBatteryLevel : public HasBatteryLevel
|
class AnalogBatteryLevel : public HasBatteryLevel
|
||||||
{
|
{
|
||||||
@@ -311,7 +306,8 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
|||||||
|
|
||||||
#ifndef BATTERY_SENSE_SAMPLES
|
#ifndef BATTERY_SENSE_SAMPLES
|
||||||
#define BATTERY_SENSE_SAMPLES \
|
#define BATTERY_SENSE_SAMPLES \
|
||||||
15 // Set the number of samples, it has an effect of increasing sensitivity in complex electromagnetic environment.
|
15 // Set the number of samples, it has an effect of increasing sensitivity in
|
||||||
|
// complex electromagnetic environment.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef BATTERY_PIN
|
#ifdef BATTERY_PIN
|
||||||
@@ -341,7 +337,8 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
|||||||
battery_adcDisable();
|
battery_adcDisable();
|
||||||
|
|
||||||
if (!initial_read_done) {
|
if (!initial_read_done) {
|
||||||
// Flush the smoothing filter with an ADC reading, if the reading is plausibly correct
|
// Flush the smoothing filter with an ADC reading, if the reading is
|
||||||
|
// plausibly correct
|
||||||
if (scaled > last_read_value)
|
if (scaled > last_read_value)
|
||||||
last_read_value = scaled;
|
last_read_value = scaled;
|
||||||
initial_read_done = true;
|
initial_read_done = true;
|
||||||
@@ -350,8 +347,8 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
|||||||
last_read_value += (scaled - last_read_value) * 0.5; // Virtual LPF
|
last_read_value += (scaled - last_read_value) * 0.5; // Virtual LPF
|
||||||
}
|
}
|
||||||
|
|
||||||
// LOG_DEBUG("battery gpio %d raw val=%u scaled=%u filtered=%u", BATTERY_PIN, raw, (uint32_t)(scaled), (uint32_t)
|
// LOG_DEBUG("battery gpio %d raw val=%u scaled=%u filtered=%u",
|
||||||
// (last_read_value));
|
// BATTERY_PIN, raw, (uint32_t)(scaled), (uint32_t) (last_read_value));
|
||||||
}
|
}
|
||||||
return last_read_value;
|
return last_read_value;
|
||||||
#endif // BATTERY_PIN
|
#endif // BATTERY_PIN
|
||||||
@@ -420,7 +417,8 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
|||||||
/**
|
/**
|
||||||
* return true if there is a battery installed in this unit
|
* return true if there is a battery installed in this unit
|
||||||
*/
|
*/
|
||||||
// if we have a integrated device with a battery, we can assume that the battery is always connected
|
// if we have a integrated device with a battery, we can assume that the
|
||||||
|
// battery is always connected
|
||||||
#ifdef BATTERY_IMMUTABLE
|
#ifdef BATTERY_IMMUTABLE
|
||||||
virtual bool isBatteryConnect() override { return true; }
|
virtual bool isBatteryConnect() override { return true; }
|
||||||
#elif defined(ADC_V)
|
#elif defined(ADC_V)
|
||||||
@@ -441,10 +439,10 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
|||||||
virtual bool isBatteryConnect() override { return getBatteryPercent() != -1; }
|
virtual bool isBatteryConnect() override { return getBatteryPercent() != -1; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// If we see a battery voltage higher than physics allows - assume charger is pumping
|
/// If we see a battery voltage higher than physics allows - assume charger is
|
||||||
/// in power
|
/// pumping in power On some boards we don't have the power management chip
|
||||||
/// On some boards we don't have the power management chip (like AXPxxxx)
|
/// (like AXPxxxx) so we use EXT_PWR_DETECT GPIO pin to detect external power
|
||||||
/// so we use EXT_PWR_DETECT GPIO pin to detect external power source
|
/// source
|
||||||
virtual bool isVbusIn() override
|
virtual bool isVbusIn() override
|
||||||
{
|
{
|
||||||
#ifdef EXT_PWR_DETECT
|
#ifdef EXT_PWR_DETECT
|
||||||
@@ -461,8 +459,14 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
|||||||
}
|
}
|
||||||
// if it's not HIGH - check the battery
|
// if it's not HIGH - check the battery
|
||||||
#endif
|
#endif
|
||||||
#elif defined(MUZI_BASE)
|
// If we have an EXT_PWR_DETECT pin and it indicates no external power, believe it.
|
||||||
return NRF_POWER->USBREGSTATUS & POWER_USBREGSTATUS_VBUSDETECT_Msk;
|
return false;
|
||||||
|
|
||||||
|
// technically speaking this should work for all(?) NRF52 boards
|
||||||
|
// but needs testing across multiple devices. NRF52 USB would not even work if
|
||||||
|
// VBUS was not properly connected and detected by the CPU
|
||||||
|
#elif defined(MUZI_BASE) || defined(PROMICRO_DIY_TCXO)
|
||||||
|
return powerHAL_isVBUSConnected();
|
||||||
#endif
|
#endif
|
||||||
return getBattVoltage() > chargingVolt;
|
return getBattVoltage() > chargingVolt;
|
||||||
}
|
}
|
||||||
@@ -485,8 +489,9 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
|||||||
#else
|
#else
|
||||||
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(DISABLE_INA_CHARGING_DETECTION)
|
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(DISABLE_INA_CHARGING_DETECTION)
|
||||||
if (hasINA()) {
|
if (hasINA()) {
|
||||||
// get current flow from INA sensor - negative value means power flowing into the battery
|
// get current flow from INA sensor - negative value means power flowing
|
||||||
// default assuming BATTERY+ <--> INA_VIN+ <--> SHUNT RESISTOR <--> INA_VIN- <--> LOAD
|
// into the battery default assuming BATTERY+ <--> INA_VIN+ <--> SHUNT
|
||||||
|
// RESISTOR <--> INA_VIN- <--> LOAD
|
||||||
LOG_DEBUG("Using INA on I2C addr 0x%x for charging detection", config.power.device_battery_ina_address);
|
LOG_DEBUG("Using INA on I2C addr 0x%x for charging detection", config.power.device_battery_ina_address);
|
||||||
#if defined(INA_CHARGING_DETECTION_INVERT)
|
#if defined(INA_CHARGING_DETECTION_INVERT)
|
||||||
return getINACurrent() > 0;
|
return getINACurrent() > 0;
|
||||||
@@ -502,8 +507,8 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// If we see a battery voltage higher than physics allows - assume charger is pumping
|
/// If we see a battery voltage higher than physics allows - assume charger is
|
||||||
/// in power
|
/// pumping in power
|
||||||
|
|
||||||
/// For heltecs with no battery connected, the measured voltage is 2204, so
|
/// For heltecs with no battery connected, the measured voltage is 2204, so
|
||||||
// need to be higher than that, in this case is 2500mV (3000-500)
|
// need to be higher than that, in this case is 2500mV (3000-500)
|
||||||
@@ -512,7 +517,8 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
|||||||
const float noBatVolt = (OCV[NUM_OCV_POINTS - 1] - 500) * NUM_CELLS;
|
const float noBatVolt = (OCV[NUM_OCV_POINTS - 1] - 500) * NUM_CELLS;
|
||||||
// Start value from minimum voltage for the filter to not start from 0
|
// Start value from minimum voltage for the filter to not start from 0
|
||||||
// that could trigger some events.
|
// that could trigger some events.
|
||||||
// This value is over-written by the first ADC reading, it the voltage seems reasonable.
|
// This value is over-written by the first ADC reading, it the voltage seems
|
||||||
|
// reasonable.
|
||||||
bool initial_read_done = false;
|
bool initial_read_done = false;
|
||||||
float last_read_value = (OCV[NUM_OCV_POINTS - 1] * NUM_CELLS);
|
float last_read_value = (OCV[NUM_OCV_POINTS - 1] * NUM_CELLS);
|
||||||
uint32_t last_read_time_ms = 0;
|
uint32_t last_read_time_ms = 0;
|
||||||
@@ -654,7 +660,8 @@ bool Power::analogInit()
|
|||||||
#ifdef CONFIG_IDF_TARGET_ESP32S3
|
#ifdef CONFIG_IDF_TARGET_ESP32S3
|
||||||
// ESP32S3
|
// ESP32S3
|
||||||
else if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP_FIT) {
|
else if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP_FIT) {
|
||||||
LOG_INFO("ADC config based on Two Point values and fitting curve coefficients stored in eFuse");
|
LOG_INFO("ADC config based on Two Point values and fitting curve "
|
||||||
|
"coefficients stored in eFuse");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
else {
|
else {
|
||||||
@@ -662,13 +669,7 @@ bool Power::analogInit()
|
|||||||
}
|
}
|
||||||
#endif // ARCH_ESP32
|
#endif // ARCH_ESP32
|
||||||
|
|
||||||
#ifdef ARCH_NRF52
|
// NRF52 ADC init moved to powerHAL_init in nrf52 platform
|
||||||
#ifdef VBAT_AR_INTERNAL
|
|
||||||
analogReference(VBAT_AR_INTERNAL);
|
|
||||||
#else
|
|
||||||
analogReference(AR_INTERNAL); // 3.6V
|
|
||||||
#endif
|
|
||||||
#endif // ARCH_NRF52
|
|
||||||
|
|
||||||
#ifndef ARCH_ESP32
|
#ifndef ARCH_ESP32
|
||||||
analogReadResolution(BATTERY_SENSE_RESOLUTION_BITS);
|
analogReadResolution(BATTERY_SENSE_RESOLUTION_BITS);
|
||||||
@@ -691,7 +692,9 @@ bool Power::setup()
|
|||||||
bool found = false;
|
bool found = false;
|
||||||
if (axpChipInit()) {
|
if (axpChipInit()) {
|
||||||
found = true;
|
found = true;
|
||||||
} else if (lipoInit()) {
|
} else if (cw2015Init()) {
|
||||||
|
found = true;
|
||||||
|
} else if (max17048Init()) {
|
||||||
found = true;
|
found = true;
|
||||||
} else if (lipoChargerInit()) {
|
} else if (lipoChargerInit()) {
|
||||||
found = true;
|
found = true;
|
||||||
@@ -701,11 +704,11 @@ bool Power::setup()
|
|||||||
found = true;
|
found = true;
|
||||||
} else if (analogInit()) {
|
} else if (analogInit()) {
|
||||||
found = true;
|
found = true;
|
||||||
}
|
} else {
|
||||||
|
|
||||||
#ifdef NRF_APM
|
#ifdef NRF_APM
|
||||||
found = true;
|
found = true;
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
#ifdef EXT_PWR_DETECT
|
#ifdef EXT_PWR_DETECT
|
||||||
attachInterrupt(
|
attachInterrupt(
|
||||||
EXT_PWR_DETECT,
|
EXT_PWR_DETECT,
|
||||||
@@ -723,6 +726,16 @@ bool Power::setup()
|
|||||||
runASAP = true;
|
runASAP = true;
|
||||||
},
|
},
|
||||||
CHANGE);
|
CHANGE);
|
||||||
|
#endif
|
||||||
|
#ifdef EXT_CHRG_DETECT
|
||||||
|
attachInterrupt(
|
||||||
|
EXT_CHRG_DETECT,
|
||||||
|
[]() {
|
||||||
|
power->setIntervalFromNow(0);
|
||||||
|
runASAP = true;
|
||||||
|
BaseType_t higherWake = 0;
|
||||||
|
},
|
||||||
|
CHANGE);
|
||||||
#endif
|
#endif
|
||||||
enabled = found;
|
enabled = found;
|
||||||
low_voltage_counter = 0;
|
low_voltage_counter = 0;
|
||||||
@@ -769,7 +782,8 @@ void Power::reboot()
|
|||||||
HAL_NVIC_SystemReset();
|
HAL_NVIC_SystemReset();
|
||||||
#else
|
#else
|
||||||
rebootAtMsec = -1;
|
rebootAtMsec = -1;
|
||||||
LOG_WARN("FIXME implement reboot for this platform. Note that some settings require a restart to be applied");
|
LOG_WARN("FIXME implement reboot for this platform. Note that some settings "
|
||||||
|
"require a restart to be applied");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -779,9 +793,12 @@ void Power::shutdown()
|
|||||||
#if HAS_SCREEN
|
#if HAS_SCREEN
|
||||||
if (screen) {
|
if (screen) {
|
||||||
#ifdef T_DECK_PRO
|
#ifdef T_DECK_PRO
|
||||||
screen->showSimpleBanner("Device is powered off.\nConnect USB to start!", 0); // T-Deck Pro has no power button
|
screen->showSimpleBanner("Device is powered off.\nConnect USB to start!",
|
||||||
|
0); // T-Deck Pro has no power button
|
||||||
#elif defined(USE_EINK)
|
#elif defined(USE_EINK)
|
||||||
screen->showSimpleBanner("Shutting Down...", 2250); // dismiss after 3 seconds to avoid the banner on the sleep screen
|
screen->showSimpleBanner("Shutting Down...",
|
||||||
|
2250); // dismiss after 3 seconds to avoid the
|
||||||
|
// banner on the sleep screen
|
||||||
#else
|
#else
|
||||||
screen->showSimpleBanner("Shutting Down...", 0); // stays on screen
|
screen->showSimpleBanner("Shutting Down...", 0); // stays on screen
|
||||||
#endif
|
#endif
|
||||||
@@ -803,6 +820,9 @@ void Power::shutdown()
|
|||||||
#endif
|
#endif
|
||||||
#ifdef PIN_LED3
|
#ifdef PIN_LED3
|
||||||
ledOff(PIN_LED3);
|
ledOff(PIN_LED3);
|
||||||
|
#endif
|
||||||
|
#ifdef LED_NOTIFICATION
|
||||||
|
ledOff(LED_NOTIFICATION);
|
||||||
#endif
|
#endif
|
||||||
doDeepSleep(DELAY_FOREVER, true, true);
|
doDeepSleep(DELAY_FOREVER, true, true);
|
||||||
#elif defined(ARCH_PORTDUINO)
|
#elif defined(ARCH_PORTDUINO)
|
||||||
@@ -820,22 +840,26 @@ void Power::readPowerStatus()
|
|||||||
int32_t batteryVoltageMv = -1; // Assume unknown
|
int32_t batteryVoltageMv = -1; // Assume unknown
|
||||||
int8_t batteryChargePercent = -1;
|
int8_t batteryChargePercent = -1;
|
||||||
OptionalBool usbPowered = OptUnknown;
|
OptionalBool usbPowered = OptUnknown;
|
||||||
OptionalBool hasBattery = OptUnknown; // These must be static because NRF_APM code doesn't run every time
|
OptionalBool hasBattery = OptUnknown; // These must be static because NRF_APM
|
||||||
|
// code doesn't run every time
|
||||||
OptionalBool isChargingNow = OptUnknown;
|
OptionalBool isChargingNow = OptUnknown;
|
||||||
|
|
||||||
if (batteryLevel) {
|
if (batteryLevel) {
|
||||||
hasBattery = batteryLevel->isBatteryConnect() ? OptTrue : OptFalse;
|
hasBattery = batteryLevel->isBatteryConnect() ? OptTrue : OptFalse;
|
||||||
|
#ifndef NRF_APM
|
||||||
usbPowered = batteryLevel->isVbusIn() ? OptTrue : OptFalse;
|
usbPowered = batteryLevel->isVbusIn() ? OptTrue : OptFalse;
|
||||||
isChargingNow = batteryLevel->isCharging() ? OptTrue : OptFalse;
|
isChargingNow = batteryLevel->isCharging() ? OptTrue : OptFalse;
|
||||||
|
#endif
|
||||||
if (hasBattery) {
|
if (hasBattery) {
|
||||||
batteryVoltageMv = batteryLevel->getBattVoltage();
|
batteryVoltageMv = batteryLevel->getBattVoltage();
|
||||||
// If the AXP192 returns a valid battery percentage, use it
|
// If the AXP192 returns a valid battery percentage, use it
|
||||||
if (batteryLevel->getBatteryPercent() >= 0) {
|
if (batteryLevel->getBatteryPercent() >= 0) {
|
||||||
batteryChargePercent = batteryLevel->getBatteryPercent();
|
batteryChargePercent = batteryLevel->getBatteryPercent();
|
||||||
} else {
|
} else {
|
||||||
// If the AXP192 returns a percentage less than 0, the feature is either not supported or there is an error
|
// If the AXP192 returns a percentage less than 0, the feature is either
|
||||||
// In that case, we compute an estimate of the charge percent based on open circuit voltage table defined
|
// not supported or there is an error In that case, we compute an
|
||||||
// in power.h
|
// estimate of the charge percent based on open circuit voltage table
|
||||||
|
// defined in power.h
|
||||||
batteryChargePercent = clamp((int)(((batteryVoltageMv - (OCV[NUM_OCV_POINTS - 1] * NUM_CELLS)) * 1e2) /
|
batteryChargePercent = clamp((int)(((batteryVoltageMv - (OCV[NUM_OCV_POINTS - 1] * NUM_CELLS)) * 1e2) /
|
||||||
((OCV[0] * NUM_CELLS) - (OCV[NUM_OCV_POINTS - 1] * NUM_CELLS))),
|
((OCV[0] * NUM_CELLS) - (OCV[NUM_OCV_POINTS - 1] * NUM_CELLS))),
|
||||||
0, 100);
|
0, 100);
|
||||||
@@ -843,12 +867,12 @@ void Power::readPowerStatus()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: IMO we shouldn't be littering our code with all these ifdefs. Way better instead to make a Nrf52IsUsbPowered subclass
|
// FIXME: IMO we shouldn't be littering our code with all these ifdefs. Way
|
||||||
// (which shares a superclass with the BatteryLevel stuff)
|
// better instead to make a Nrf52IsUsbPowered subclass (which shares a
|
||||||
// that just provides a few methods. But in the interest of fixing this bug I'm going to follow current
|
// superclass with the BatteryLevel stuff) that just provides a few methods. But
|
||||||
// practice.
|
// in the interest of fixing this bug I'm going to follow current practice.
|
||||||
#ifdef NRF_APM // Section of code detects USB power on the RAK4631 and updates the power states. Takes 20 seconds or so to detect
|
#ifdef NRF_APM // Section of code detects USB power on the RAK4631 and updates
|
||||||
// changes.
|
// the power states. Takes 20 seconds or so to detect changes.
|
||||||
|
|
||||||
nrfx_power_usb_state_t nrf_usb_state = nrfx_power_usbstatus_get();
|
nrfx_power_usb_state_t nrf_usb_state = nrfx_power_usbstatus_get();
|
||||||
// LOG_DEBUG("NRF Power %d", nrf_usb_state);
|
// LOG_DEBUG("NRF Power %d", nrf_usb_state);
|
||||||
@@ -922,8 +946,9 @@ void Power::readPowerStatus()
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// If we have a battery at all and it is less than 0%, force deep sleep if we have more than 10 low readings in
|
// If we have a battery at all and it is less than 0%, force deep sleep if we
|
||||||
// a row. NOTE: min LiIon/LiPo voltage is 2.0 to 2.5V, current OCV min is set to 3100 that is large enough.
|
// have more than 10 low readings in a row. NOTE: min LiIon/LiPo voltage
|
||||||
|
// is 2.0 to 2.5V, current OCV min is set to 3100 that is large enough.
|
||||||
//
|
//
|
||||||
|
|
||||||
if (batteryLevel && powerStatus2.getHasBattery() && !powerStatus2.getHasUSB()) {
|
if (batteryLevel && powerStatus2.getHasBattery() && !powerStatus2.getHasUSB()) {
|
||||||
@@ -945,8 +970,8 @@ int32_t Power::runOnce()
|
|||||||
readPowerStatus();
|
readPowerStatus();
|
||||||
|
|
||||||
#ifdef HAS_PMU
|
#ifdef HAS_PMU
|
||||||
// WE no longer use the IRQ line to wake the CPU (due to false wakes from sleep), but we do poll
|
// WE no longer use the IRQ line to wake the CPU (due to false wakes from
|
||||||
// the IRQ status by reading the registers over I2C
|
// sleep), but we do poll the IRQ status by reading the registers over I2C
|
||||||
if (PMU) {
|
if (PMU) {
|
||||||
|
|
||||||
PMU->getIrqStatus();
|
PMU->getIrqStatus();
|
||||||
@@ -988,7 +1013,8 @@ int32_t Power::runOnce()
|
|||||||
PMU->clearIrqStatus();
|
PMU->clearIrqStatus();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// Only read once every 20 seconds once the power status for the app has been initialized
|
// Only read once every 20 seconds once the power status for the app has been
|
||||||
|
// initialized
|
||||||
return (statusHandler && statusHandler->isInitialized()) ? (1000 * 20) : RUN_SAME;
|
return (statusHandler && statusHandler->isInitialized()) ? (1000 * 20) : RUN_SAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -996,10 +1022,12 @@ int32_t Power::runOnce()
|
|||||||
* Init the power manager chip
|
* Init the power manager chip
|
||||||
*
|
*
|
||||||
* axp192 power
|
* axp192 power
|
||||||
DCDC1 0.7-3.5V @ 1200mA max -> OLED // If you turn this off you'll lose comms to the axp192 because the OLED and the
|
DCDC1 0.7-3.5V @ 1200mA max -> OLED // If you turn this off you'll lose
|
||||||
axp192 share the same i2c bus, instead use ssd1306 sleep mode DCDC2 -> unused DCDC3 0.7-3.5V @ 700mA max -> ESP32 (keep this
|
comms to the axp192 because the OLED and the axp192 share the same i2c bus,
|
||||||
on!) LDO1 30mA -> charges GPS backup battery // charges the tiny J13 battery by the GPS to power the GPS ram (for a couple of
|
instead use ssd1306 sleep mode DCDC2 -> unused DCDC3 0.7-3.5V @ 700mA max ->
|
||||||
days), can not be turned off LDO2 200mA -> LORA LDO3 200mA -> GPS
|
ESP32 (keep this on!) LDO1 30mA -> charges GPS backup battery // charges the
|
||||||
|
tiny J13 battery by the GPS to power the GPS ram (for a couple of days), can
|
||||||
|
not be turned off LDO2 200mA -> LORA LDO3 200mA -> GPS
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
bool Power::axpChipInit()
|
bool Power::axpChipInit()
|
||||||
@@ -1044,9 +1072,10 @@ bool Power::axpChipInit()
|
|||||||
|
|
||||||
if (!PMU) {
|
if (!PMU) {
|
||||||
/*
|
/*
|
||||||
* In XPowersLib, if the XPowersAXPxxx object is released, Wire.end() will be called at the same time.
|
* In XPowersLib, if the XPowersAXPxxx object is released, Wire.end() will
|
||||||
* In order not to affect other devices, if the initialization of the PMU fails, Wire needs to be re-initialized once,
|
* be called at the same time. In order not to affect other devices, if the
|
||||||
* if there are multiple devices sharing the bus.
|
* initialization of the PMU fails, Wire needs to be re-initialized once, if
|
||||||
|
* there are multiple devices sharing the bus.
|
||||||
* * */
|
* * */
|
||||||
#ifndef PMU_USE_WIRE1
|
#ifndef PMU_USE_WIRE1
|
||||||
w->begin(I2C_SDA, I2C_SCL);
|
w->begin(I2C_SDA, I2C_SCL);
|
||||||
@@ -1063,8 +1092,8 @@ bool Power::axpChipInit()
|
|||||||
PMU->enablePowerOutput(XPOWERS_LDO2);
|
PMU->enablePowerOutput(XPOWERS_LDO2);
|
||||||
|
|
||||||
// oled module power channel,
|
// oled module power channel,
|
||||||
// disable it will cause abnormal communication between boot and AXP power supply,
|
// disable it will cause abnormal communication between boot and AXP power
|
||||||
// do not turn it off
|
// supply, do not turn it off
|
||||||
PMU->setPowerChannelVoltage(XPOWERS_DCDC1, 3300);
|
PMU->setPowerChannelVoltage(XPOWERS_DCDC1, 3300);
|
||||||
// enable oled power
|
// enable oled power
|
||||||
PMU->enablePowerOutput(XPOWERS_DCDC1);
|
PMU->enablePowerOutput(XPOWERS_DCDC1);
|
||||||
@@ -1091,7 +1120,8 @@ bool Power::axpChipInit()
|
|||||||
PMU->setChargeTargetVoltage(XPOWERS_AXP192_CHG_VOL_4V2);
|
PMU->setChargeTargetVoltage(XPOWERS_AXP192_CHG_VOL_4V2);
|
||||||
} else if (PMU->getChipModel() == XPOWERS_AXP2101) {
|
} else if (PMU->getChipModel() == XPOWERS_AXP2101) {
|
||||||
|
|
||||||
/*The alternative version of T-Beam 1.1 differs from T-Beam V1.1 in that it uses an AXP2101 power chip*/
|
/*The alternative version of T-Beam 1.1 differs from T-Beam V1.1 in that it
|
||||||
|
* uses an AXP2101 power chip*/
|
||||||
if (HW_VENDOR == meshtastic_HardwareModel_TBEAM) {
|
if (HW_VENDOR == meshtastic_HardwareModel_TBEAM) {
|
||||||
// Unuse power channel
|
// Unuse power channel
|
||||||
PMU->disablePowerOutput(XPOWERS_DCDC2);
|
PMU->disablePowerOutput(XPOWERS_DCDC2);
|
||||||
@@ -1126,8 +1156,8 @@ bool Power::axpChipInit()
|
|||||||
// t-beam s3 core
|
// t-beam s3 core
|
||||||
/**
|
/**
|
||||||
* gnss module power channel
|
* gnss module power channel
|
||||||
* The default ALDO4 is off, you need to turn on the GNSS power first, otherwise it will be invalid during
|
* The default ALDO4 is off, you need to turn on the GNSS power first,
|
||||||
* initialization
|
* otherwise it will be invalid during initialization
|
||||||
*/
|
*/
|
||||||
PMU->setPowerChannelVoltage(XPOWERS_ALDO4, 3300);
|
PMU->setPowerChannelVoltage(XPOWERS_ALDO4, 3300);
|
||||||
PMU->enablePowerOutput(XPOWERS_ALDO4);
|
PMU->enablePowerOutput(XPOWERS_ALDO4);
|
||||||
@@ -1177,7 +1207,8 @@ bool Power::axpChipInit()
|
|||||||
// disable all axp chip interrupt
|
// disable all axp chip interrupt
|
||||||
PMU->disableIRQ(XPOWERS_AXP2101_ALL_IRQ);
|
PMU->disableIRQ(XPOWERS_AXP2101_ALL_IRQ);
|
||||||
|
|
||||||
// Set the constant current charging current of AXP2101, temporarily use 500mA by default
|
// Set the constant current charging current of AXP2101, temporarily use
|
||||||
|
// 500mA by default
|
||||||
PMU->setChargerConstantCurr(XPOWERS_AXP2101_CHG_CUR_500MA);
|
PMU->setChargerConstantCurr(XPOWERS_AXP2101_CHG_CUR_500MA);
|
||||||
|
|
||||||
// Set up the charging voltage
|
// Set up the charging voltage
|
||||||
@@ -1243,11 +1274,12 @@ bool Power::axpChipInit()
|
|||||||
PMU->getPowerChannelVoltage(XPOWERS_BLDO2));
|
PMU->getPowerChannelVoltage(XPOWERS_BLDO2));
|
||||||
}
|
}
|
||||||
|
|
||||||
// We can safely ignore this approach for most (or all) boards because MCU turned off
|
// We can safely ignore this approach for most (or all) boards because MCU
|
||||||
// earlier than battery discharged to 2.6V.
|
// turned off earlier than battery discharged to 2.6V.
|
||||||
//
|
//
|
||||||
// Unfortanly for now we can't use this killswitch for RAK4630-based boards because they have a bug with
|
// Unfortunately for now we can't use this killswitch for RAK4630-based boards
|
||||||
// battery voltage measurement. Probably it sometimes drops to low values.
|
// because they have a bug with battery voltage measurement. Probably it
|
||||||
|
// sometimes drops to low values.
|
||||||
#ifndef RAK4630
|
#ifndef RAK4630
|
||||||
// Set PMU shutdown voltage at 2.6V to maximize battery utilization
|
// Set PMU shutdown voltage at 2.6V to maximize battery utilization
|
||||||
PMU->setSysPowerDownVoltage(2600);
|
PMU->setSysPowerDownVoltage(2600);
|
||||||
@@ -1266,10 +1298,12 @@ bool Power::axpChipInit()
|
|||||||
attachInterrupt(
|
attachInterrupt(
|
||||||
PMU_IRQ, [] { pmu_irq = true; }, FALLING);
|
PMU_IRQ, [] { pmu_irq = true; }, FALLING);
|
||||||
|
|
||||||
// we do not look for AXPXXX_CHARGING_FINISHED_IRQ & AXPXXX_CHARGING_IRQ because it occurs repeatedly while there is
|
// we do not look for AXPXXX_CHARGING_FINISHED_IRQ & AXPXXX_CHARGING_IRQ
|
||||||
// no battery also it could cause inadvertent waking from light sleep just because the battery filled
|
// because it occurs repeatedly while there is no battery also it could cause
|
||||||
// we don't look for AXPXXX_BATT_REMOVED_IRQ because it occurs repeatedly while no battery installed
|
// inadvertent waking from light sleep just because the battery filled we
|
||||||
// we don't look at AXPXXX_VBUS_REMOVED_IRQ because we don't have anything hooked to vbus
|
// don't look for AXPXXX_BATT_REMOVED_IRQ because it occurs repeatedly while
|
||||||
|
// no battery installed we don't look at AXPXXX_VBUS_REMOVED_IRQ because we
|
||||||
|
// don't have anything hooked to vbus
|
||||||
PMU->enableIRQ(pmuIrqMask);
|
PMU->enableIRQ(pmuIrqMask);
|
||||||
|
|
||||||
PMU->clearIrqStatus();
|
PMU->clearIrqStatus();
|
||||||
@@ -1291,7 +1325,7 @@ bool Power::axpChipInit()
|
|||||||
/**
|
/**
|
||||||
* Wrapper class for an I2C MAX17048 Lipo battery sensor.
|
* Wrapper class for an I2C MAX17048 Lipo battery sensor.
|
||||||
*/
|
*/
|
||||||
class LipoBatteryLevel : public HasBatteryLevel
|
class MAX17048BatteryLevel : public HasBatteryLevel
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
MAX17048Singleton *max17048 = nullptr;
|
MAX17048Singleton *max17048 = nullptr;
|
||||||
@@ -1339,18 +1373,18 @@ class LipoBatteryLevel : public HasBatteryLevel
|
|||||||
virtual bool isCharging() override { return max17048->isBatteryCharging(); }
|
virtual bool isCharging() override { return max17048->isBatteryCharging(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
LipoBatteryLevel lipoLevel;
|
MAX17048BatteryLevel max17048Level;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init the Lipo battery level sensor
|
* Init the Lipo battery level sensor
|
||||||
*/
|
*/
|
||||||
bool Power::lipoInit()
|
bool Power::max17048Init()
|
||||||
{
|
{
|
||||||
bool result = lipoLevel.runOnce();
|
bool result = max17048Level.runOnce();
|
||||||
LOG_DEBUG("Power::lipoInit lipo sensor is %s", result ? "ready" : "not ready yet");
|
LOG_DEBUG("Power::max17048Init lipo sensor is %s", result ? "ready" : "not ready yet");
|
||||||
if (!result)
|
if (!result)
|
||||||
return false;
|
return false;
|
||||||
batteryLevel = &lipoLevel;
|
batteryLevel = &max17048Level;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1358,7 +1392,88 @@ bool Power::lipoInit()
|
|||||||
/**
|
/**
|
||||||
* The Lipo battery level sensor is unavailable - default to AnalogBatteryLevel
|
* The Lipo battery level sensor is unavailable - default to AnalogBatteryLevel
|
||||||
*/
|
*/
|
||||||
bool Power::lipoInit()
|
bool Power::max17048Init()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !MESHTASTIC_EXCLUDE_I2C && HAS_CW2015
|
||||||
|
|
||||||
|
class CW2015BatteryLevel : public AnalogBatteryLevel
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Battery state of charge, from 0 to 100 or -1 for unknown
|
||||||
|
*/
|
||||||
|
virtual int getBatteryPercent() override
|
||||||
|
{
|
||||||
|
int data = -1;
|
||||||
|
Wire.beginTransmission(CW2015_ADDR);
|
||||||
|
Wire.write(0x04);
|
||||||
|
if (Wire.endTransmission() == 0) {
|
||||||
|
if (Wire.requestFrom(CW2015_ADDR, (uint8_t)1)) {
|
||||||
|
data = Wire.read();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The raw voltage of the battery in millivolts, or NAN if unknown
|
||||||
|
*/
|
||||||
|
virtual uint16_t getBattVoltage() override
|
||||||
|
{
|
||||||
|
uint16_t mv = 0;
|
||||||
|
Wire.beginTransmission(CW2015_ADDR);
|
||||||
|
Wire.write(0x02);
|
||||||
|
if (Wire.endTransmission() == 0) {
|
||||||
|
if (Wire.requestFrom(CW2015_ADDR, (uint8_t)2)) {
|
||||||
|
mv = Wire.read();
|
||||||
|
mv <<= 8;
|
||||||
|
mv |= Wire.read();
|
||||||
|
// Voltage is read in 305uV units, convert to mV
|
||||||
|
mv = mv * 305 / 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mv;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
CW2015BatteryLevel cw2015Level;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init the CW2015 battery level sensor
|
||||||
|
*/
|
||||||
|
bool Power::cw2015Init()
|
||||||
|
{
|
||||||
|
|
||||||
|
Wire.beginTransmission(CW2015_ADDR);
|
||||||
|
uint8_t getInfo[] = {0x0a, 0x00};
|
||||||
|
Wire.write(getInfo, 2);
|
||||||
|
Wire.endTransmission();
|
||||||
|
delay(10);
|
||||||
|
Wire.beginTransmission(CW2015_ADDR);
|
||||||
|
Wire.write(0x00);
|
||||||
|
bool result = false;
|
||||||
|
if (Wire.endTransmission() == 0) {
|
||||||
|
if (Wire.requestFrom(CW2015_ADDR, (uint8_t)1)) {
|
||||||
|
uint8_t data = Wire.read();
|
||||||
|
LOG_DEBUG("CW2015 init read data: 0x%x", data);
|
||||||
|
if (data == 0x73) {
|
||||||
|
result = true;
|
||||||
|
batteryLevel = &cw2015Level;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
/**
|
||||||
|
* The CW2015 battery level sensor is unavailable - default to AnalogBatteryLevel
|
||||||
|
*/
|
||||||
|
bool Power::cw2015Init()
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1385,8 +1500,8 @@ class LipoCharger : public HasBatteryLevel
|
|||||||
bool result = PPM->init(Wire, I2C_SDA, I2C_SCL, BQ25896_ADDR);
|
bool result = PPM->init(Wire, I2C_SDA, I2C_SCL, BQ25896_ADDR);
|
||||||
if (result) {
|
if (result) {
|
||||||
LOG_INFO("PPM BQ25896 init succeeded");
|
LOG_INFO("PPM BQ25896 init succeeded");
|
||||||
// Set the minimum operating voltage. Below this voltage, the PPM will protect
|
// Set the minimum operating voltage. Below this voltage, the PPM will
|
||||||
// PPM->setSysPowerDownVoltage(3100);
|
// protect PPM->setSysPowerDownVoltage(3100);
|
||||||
|
|
||||||
// Set input current limit, default is 500mA
|
// Set input current limit, default is 500mA
|
||||||
// PPM->setInputCurrentLimit(800);
|
// PPM->setInputCurrentLimit(800);
|
||||||
@@ -1409,7 +1524,8 @@ class LipoCharger : public HasBatteryLevel
|
|||||||
PPM->enableMeasure();
|
PPM->enableMeasure();
|
||||||
|
|
||||||
// Turn on charging function
|
// Turn on charging function
|
||||||
// If there is no battery connected, do not turn on the charging function
|
// If there is no battery connected, do not turn on the charging
|
||||||
|
// function
|
||||||
PPM->enableCharge();
|
PPM->enableCharge();
|
||||||
} else {
|
} else {
|
||||||
LOG_WARN("PPM BQ25896 init failed");
|
LOG_WARN("PPM BQ25896 init failed");
|
||||||
@@ -1444,7 +1560,8 @@ class LipoCharger : public HasBatteryLevel
|
|||||||
virtual int getBatteryPercent() override
|
virtual int getBatteryPercent() override
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
// return bq->getChargePercent(); // don't use BQ27220 for battery percent, it is not calibrated
|
// return bq->getChargePercent(); // don't use BQ27220 for battery percent,
|
||||||
|
// it is not calibrated
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1566,7 +1683,8 @@ bool Power::meshSolarInit()
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
/**
|
/**
|
||||||
* The meshSolar battery level sensor is unavailable - default to AnalogBatteryLevel
|
* The meshSolar battery level sensor is unavailable - default to
|
||||||
|
* AnalogBatteryLevel
|
||||||
*/
|
*/
|
||||||
bool Power::meshSolarInit()
|
bool Power::meshSolarInit()
|
||||||
{
|
{
|
||||||
|
|||||||
+12
-6
@@ -9,13 +9,13 @@
|
|||||||
*/
|
*/
|
||||||
#include "PowerFSM.h"
|
#include "PowerFSM.h"
|
||||||
#include "Default.h"
|
#include "Default.h"
|
||||||
#include "Led.h"
|
|
||||||
#include "MeshService.h"
|
#include "MeshService.h"
|
||||||
#include "NodeDB.h"
|
#include "NodeDB.h"
|
||||||
#include "PowerMon.h"
|
#include "PowerMon.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "graphics/Screen.h"
|
#include "graphics/Screen.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
#include "modules/StatusLEDModule.h"
|
||||||
#include "sleep.h"
|
#include "sleep.h"
|
||||||
#include "target_specific.h"
|
#include "target_specific.h"
|
||||||
|
|
||||||
@@ -38,7 +38,10 @@ static bool isPowered()
|
|||||||
return true;
|
return true;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool isRouter = (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ? 1 : 0);
|
bool isRouter = ((config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ||
|
||||||
|
config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER_LATE)
|
||||||
|
? 1
|
||||||
|
: 0);
|
||||||
|
|
||||||
// If we are not a router and we already have AC power go to POWER state after init, otherwise go to ON
|
// If we are not a router and we already have AC power go to POWER state after init, otherwise go to ON
|
||||||
// We assume routers might be powered all the time, but from a low current (solar) source
|
// We assume routers might be powered all the time, but from a low current (solar) source
|
||||||
@@ -103,7 +106,7 @@ static void lsIdle()
|
|||||||
uint32_t sleepTime = SLEEP_TIME;
|
uint32_t sleepTime = SLEEP_TIME;
|
||||||
|
|
||||||
powerMon->setState(meshtastic_PowerMon_State_CPU_LightSleep);
|
powerMon->setState(meshtastic_PowerMon_State_CPU_LightSleep);
|
||||||
ledBlink.set(false); // Never leave led on while in light sleep
|
statusLEDModule->setPowerLED(false);
|
||||||
esp_sleep_source_t wakeCause2 = doLightSleep(sleepTime * 1000LL);
|
esp_sleep_source_t wakeCause2 = doLightSleep(sleepTime * 1000LL);
|
||||||
powerMon->clearState(meshtastic_PowerMon_State_CPU_LightSleep);
|
powerMon->clearState(meshtastic_PowerMon_State_CPU_LightSleep);
|
||||||
|
|
||||||
@@ -111,7 +114,7 @@ static void lsIdle()
|
|||||||
case ESP_SLEEP_WAKEUP_TIMER:
|
case ESP_SLEEP_WAKEUP_TIMER:
|
||||||
// Normal case: timer expired, we should just go back to sleep ASAP
|
// Normal case: timer expired, we should just go back to sleep ASAP
|
||||||
|
|
||||||
ledBlink.set(true); // briefly turn on led
|
statusLEDModule->setPowerLED(true);
|
||||||
wakeCause2 = doLightSleep(100); // leave led on for 1ms
|
wakeCause2 = doLightSleep(100); // leave led on for 1ms
|
||||||
|
|
||||||
secsSlept += sleepTime;
|
secsSlept += sleepTime;
|
||||||
@@ -146,7 +149,7 @@ static void lsIdle()
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Time to stop sleeping!
|
// Time to stop sleeping!
|
||||||
ledBlink.set(false);
|
statusLEDModule->setPowerLED(false);
|
||||||
LOG_INFO("Reached ls_secs, service loop()");
|
LOG_INFO("Reached ls_secs, service loop()");
|
||||||
powerFSM.trigger(EVENT_WAKE_TIMER);
|
powerFSM.trigger(EVENT_WAKE_TIMER);
|
||||||
}
|
}
|
||||||
@@ -262,7 +265,10 @@ Fsm powerFSM(&stateBOOT);
|
|||||||
|
|
||||||
void PowerFSM_setup()
|
void PowerFSM_setup()
|
||||||
{
|
{
|
||||||
bool isRouter = (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ? 1 : 0);
|
bool isRouter = ((config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ||
|
||||||
|
config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER_LATE)
|
||||||
|
? 1
|
||||||
|
: 0);
|
||||||
bool hasPower = isPowered();
|
bool hasPower = isPowered();
|
||||||
|
|
||||||
LOG_INFO("PowerFSM init, USB power=%d", hasPower ? 1 : 0);
|
LOG_INFO("PowerFSM init, USB power=%d", hasPower ? 1 : 0);
|
||||||
|
|||||||
+18
-29
@@ -227,34 +227,21 @@ void RedirectablePrint::log_to_ble(const char *logLevel, const char *format, va_
|
|||||||
isBleConnected = nrf52Bluetooth != nullptr && nrf52Bluetooth->isConnected();
|
isBleConnected = nrf52Bluetooth != nullptr && nrf52Bluetooth->isConnected();
|
||||||
#endif
|
#endif
|
||||||
if (isBleConnected) {
|
if (isBleConnected) {
|
||||||
char *message;
|
|
||||||
size_t initialLen;
|
|
||||||
size_t len;
|
|
||||||
initialLen = strlen(format);
|
|
||||||
message = new char[initialLen + 1];
|
|
||||||
len = vsnprintf(message, initialLen + 1, format, arg);
|
|
||||||
if (len > initialLen) {
|
|
||||||
delete[] message;
|
|
||||||
message = new char[len + 1];
|
|
||||||
vsnprintf(message, len + 1, format, arg);
|
|
||||||
}
|
|
||||||
auto thread = concurrency::OSThread::currentThread;
|
auto thread = concurrency::OSThread::currentThread;
|
||||||
meshtastic_LogRecord logRecord = meshtastic_LogRecord_init_zero;
|
meshtastic_LogRecord logRecord = meshtastic_LogRecord_init_zero;
|
||||||
logRecord.level = getLogLevel(logLevel);
|
logRecord.level = getLogLevel(logLevel);
|
||||||
strcpy(logRecord.message, message);
|
vsprintf(logRecord.message, format, arg);
|
||||||
if (thread)
|
if (thread)
|
||||||
strcpy(logRecord.source, thread->ThreadName.c_str());
|
strcpy(logRecord.source, thread->ThreadName.c_str());
|
||||||
logRecord.time = getValidTime(RTCQuality::RTCQualityDevice, true);
|
logRecord.time = getValidTime(RTCQuality::RTCQualityDevice, true);
|
||||||
|
|
||||||
uint8_t *buffer = new uint8_t[meshtastic_LogRecord_size];
|
auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[meshtastic_LogRecord_size]);
|
||||||
size_t size = pb_encode_to_bytes(buffer, meshtastic_LogRecord_size, meshtastic_LogRecord_fields, &logRecord);
|
size_t size = pb_encode_to_bytes(buffer.get(), meshtastic_LogRecord_size, meshtastic_LogRecord_fields, &logRecord);
|
||||||
#ifdef ARCH_ESP32
|
#ifdef ARCH_ESP32
|
||||||
nimbleBluetooth->sendLog(buffer, size);
|
nimbleBluetooth->sendLog(buffer.get(), size);
|
||||||
#elif defined(ARCH_NRF52)
|
#elif defined(ARCH_NRF52)
|
||||||
nrf52Bluetooth->sendLog(buffer, size);
|
nrf52Bluetooth->sendLog(buffer.get(), size);
|
||||||
#endif
|
#endif
|
||||||
delete[] message;
|
|
||||||
delete[] buffer;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@@ -292,8 +279,8 @@ void RedirectablePrint::log(const char *logLevel, const char *format, ...)
|
|||||||
|
|
||||||
// append \n to format
|
// append \n to format
|
||||||
size_t len = strlen(format);
|
size_t len = strlen(format);
|
||||||
char *newFormat = new char[len + 2];
|
auto newFormat = std::unique_ptr<char[]>(new char[len + 2]);
|
||||||
strcpy(newFormat, format);
|
strcpy(newFormat.get(), format);
|
||||||
newFormat[len] = '\n';
|
newFormat[len] = '\n';
|
||||||
newFormat[len + 1] = '\0';
|
newFormat[len + 1] = '\0';
|
||||||
|
|
||||||
@@ -310,23 +297,18 @@ void RedirectablePrint::log(const char *logLevel, const char *format, ...)
|
|||||||
va_end(arg);
|
va_end(arg);
|
||||||
}
|
}
|
||||||
if (portduino_config.logoutputlevel < level_trace && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0) {
|
if (portduino_config.logoutputlevel < level_trace && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0) {
|
||||||
delete[] newFormat;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (portduino_config.logoutputlevel < level_debug && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) {
|
if (portduino_config.logoutputlevel < level_debug && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) {
|
||||||
delete[] newFormat;
|
|
||||||
return;
|
return;
|
||||||
} else if (portduino_config.logoutputlevel < level_info && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0) {
|
} else if (portduino_config.logoutputlevel < level_info && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0) {
|
||||||
delete[] newFormat;
|
|
||||||
return;
|
return;
|
||||||
} else if (portduino_config.logoutputlevel < level_warn && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0) {
|
} else if (portduino_config.logoutputlevel < level_warn && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0) {
|
||||||
delete[] newFormat;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (moduleConfig.serial.override_console_serial_port && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) {
|
if (moduleConfig.serial.override_console_serial_port && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) {
|
||||||
delete[] newFormat;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,11 +320,19 @@ void RedirectablePrint::log(const char *logLevel, const char *format, ...)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
va_list arg;
|
va_list arg;
|
||||||
|
va_list arg_copy;
|
||||||
|
|
||||||
va_start(arg, format);
|
va_start(arg, format);
|
||||||
|
|
||||||
log_to_serial(logLevel, newFormat, arg);
|
va_copy(arg_copy, arg);
|
||||||
log_to_syslog(logLevel, newFormat, arg);
|
log_to_serial(logLevel, newFormat.get(), arg_copy);
|
||||||
log_to_ble(logLevel, newFormat, arg);
|
va_end(arg_copy);
|
||||||
|
|
||||||
|
va_copy(arg_copy, arg);
|
||||||
|
log_to_syslog(logLevel, newFormat.get(), arg_copy);
|
||||||
|
va_end(arg_copy);
|
||||||
|
|
||||||
|
log_to_ble(logLevel, newFormat.get(), arg);
|
||||||
|
|
||||||
va_end(arg);
|
va_end(arg);
|
||||||
#ifdef HAS_FREE_RTOS
|
#ifdef HAS_FREE_RTOS
|
||||||
@@ -352,7 +342,6 @@ void RedirectablePrint::log(const char *logLevel, const char *format, ...)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
delete[] newFormat;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ void consoleInit()
|
|||||||
#if defined(SERIAL_HAS_ON_RECEIVE)
|
#if defined(SERIAL_HAS_ON_RECEIVE)
|
||||||
// onReceive does only exist for HardwareSerial not for USB CDC serial
|
// onReceive does only exist for HardwareSerial not for USB CDC serial
|
||||||
Port.onReceive([sc]() { sc->rxInt(); });
|
Port.onReceive([sc]() { sc->rxInt(); });
|
||||||
|
#else
|
||||||
|
(void)sc;
|
||||||
#endif
|
#endif
|
||||||
DEBUG_PORT.rpInit(); // Simply sets up semaphore
|
DEBUG_PORT.rpInit(); // Simply sets up semaphore
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,8 +76,10 @@ bool NotifiedWorkerThread::notifyLater(uint32_t delay, uint32_t v, bool overwrit
|
|||||||
|
|
||||||
void NotifiedWorkerThread::checkNotification()
|
void NotifiedWorkerThread::checkNotification()
|
||||||
{
|
{
|
||||||
auto n = notification;
|
// Atomically read and clear. (This avoids a potential race condition where an interrupt handler could set a new notification
|
||||||
notification = 0; // clear notification
|
// after checkNotification reads but before it clears, which would cause us to miss that notification until the next one comes
|
||||||
|
// in.)
|
||||||
|
auto n = notification.exchange(0); // read+clear atomically: like `n = notification; notification = 0;` but interrupt-safe
|
||||||
if (n) {
|
if (n) {
|
||||||
onNotify(n);
|
onNotify(n);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "OSThread.h"
|
#include "OSThread.h"
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
namespace concurrency
|
namespace concurrency
|
||||||
{
|
{
|
||||||
@@ -13,7 +14,7 @@ class NotifiedWorkerThread : public OSThread
|
|||||||
/**
|
/**
|
||||||
* The notification that was most recently used to wake the thread. Read from runOnce()
|
* The notification that was most recently used to wake the thread. Read from runOnce()
|
||||||
*/
|
*/
|
||||||
uint32_t notification = 0;
|
std::atomic<uint32_t> notification{0};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
NotifiedWorkerThread(const char *name) : OSThread(name) {}
|
NotifiedWorkerThread(const char *name) : OSThread(name) {}
|
||||||
|
|||||||
@@ -1,24 +1,29 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "concurrency/OSThread.h"
|
#include "concurrency/OSThread.h"
|
||||||
|
|
||||||
namespace concurrency
|
namespace concurrency
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Periodically invoke a callback. This just provides C-style callback conventions
|
* @brief Periodically invoke a callback.
|
||||||
* rather than a virtual function - FIXME, remove?
|
* Supports both legacy function pointers and modern callables.
|
||||||
*/
|
*/
|
||||||
class Periodic : public OSThread
|
class Periodic : public OSThread
|
||||||
{
|
{
|
||||||
int32_t (*callback)();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// callback returns the period for the next callback invocation (or 0 if we should no longer be called)
|
// callback returns the period for the next callback invocation (or 0 if we should no longer be called)
|
||||||
Periodic(const char *name, int32_t (*_callback)()) : OSThread(name), callback(_callback) {}
|
Periodic(const char *name, int32_t (*cb)()) : OSThread(name), callback(cb) {}
|
||||||
|
Periodic(const char *name, std::function<int32_t()> cb) : OSThread(name), callback(std::move(cb)) {}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int32_t runOnce() override { return callback(); }
|
int32_t runOnce() override { return callback ? callback() : 0; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::function<int32_t()> callback;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace concurrency
|
} // namespace concurrency
|
||||||
|
|||||||
+27
-18
@@ -149,16 +149,30 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#define TX_GAIN_LORA 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 9, 9, 8, 7
|
#define TX_GAIN_LORA 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 9, 9, 8, 7
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_KCT8103L_PA
|
||||||
|
// Power Amps are often non-linear, so we can use an array of values for the power curve
|
||||||
|
#define NUM_PA_POINTS 22
|
||||||
|
#define TX_GAIN_LORA 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 13, 13, 13, 12, 12, 11, 10, 9, 8, 7
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef RAK13302
|
#ifdef RAK13302
|
||||||
#define NUM_PA_POINTS 22
|
#define NUM_PA_POINTS 22
|
||||||
#define TX_GAIN_LORA 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8
|
#define TX_GAIN_LORA 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Default system gain to 0 if not defined
|
// Default system gain to 0 if not defined
|
||||||
|
#ifndef NUM_PA_POINTS
|
||||||
|
#define NUM_PA_POINTS 1
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef TX_GAIN_LORA
|
#ifndef TX_GAIN_LORA
|
||||||
#define TX_GAIN_LORA 0
|
#define TX_GAIN_LORA 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef HAS_LORA_FEM
|
||||||
|
#define HAS_LORA_FEM 0
|
||||||
|
#endif
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Feature toggles
|
// Feature toggles
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@@ -213,6 +227,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#define SHTC3_ADDR 0x70
|
#define SHTC3_ADDR 0x70
|
||||||
#define LPS22HB_ADDR 0x5C
|
#define LPS22HB_ADDR 0x5C
|
||||||
#define LPS22HB_ADDR_ALT 0x5D
|
#define LPS22HB_ADDR_ALT 0x5D
|
||||||
|
#define SFA30_ADDR 0x5D
|
||||||
#define SHT31_4x_ADDR 0x44
|
#define SHT31_4x_ADDR 0x44
|
||||||
#define SHT31_4x_ADDR_ALT 0x45
|
#define SHT31_4x_ADDR_ALT 0x45
|
||||||
#define PMSA003I_ADDR 0x12
|
#define PMSA003I_ADDR 0x12
|
||||||
@@ -229,6 +244,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#define NAU7802_ADDR 0x2A
|
#define NAU7802_ADDR 0x2A
|
||||||
#define MAX30102_ADDR 0x57
|
#define MAX30102_ADDR 0x57
|
||||||
#define SCD4X_ADDR 0x62
|
#define SCD4X_ADDR 0x62
|
||||||
|
#define CW2015_ADDR 0x62
|
||||||
#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
|
||||||
@@ -237,6 +253,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#define BQ27220_ADDR 0x55 // same address as TDECK_KB
|
#define BQ27220_ADDR 0x55 // same address as TDECK_KB
|
||||||
#define BQ25896_ADDR 0x6B
|
#define BQ25896_ADDR 0x6B
|
||||||
#define LTR553ALS_ADDR 0x23
|
#define LTR553ALS_ADDR 0x23
|
||||||
|
#define SEN5X_ADDR 0x69
|
||||||
|
#define SCD30_ADDR 0x61
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// ACCELEROMETER
|
// ACCELEROMETER
|
||||||
@@ -253,6 +271,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#define BHI260AP_ADDR 0x28
|
#define BHI260AP_ADDR 0x28
|
||||||
#define BMM150_ADDR 0x13
|
#define BMM150_ADDR 0x13
|
||||||
#define DA217_ADDR 0x26
|
#define DA217_ADDR 0x26
|
||||||
|
#define BMI270_ADDR 0x68
|
||||||
|
#define BMI270_ADDR_ALT 0x69
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// LED
|
// LED
|
||||||
@@ -386,9 +406,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#ifndef HAS_RADIO
|
#ifndef HAS_RADIO
|
||||||
#define HAS_RADIO 0
|
#define HAS_RADIO 0
|
||||||
#endif
|
#endif
|
||||||
#ifndef HAS_RTC
|
|
||||||
#define HAS_RTC 0
|
|
||||||
#endif
|
|
||||||
#ifndef HAS_CPU_SHUTDOWN
|
#ifndef HAS_CPU_SHUTDOWN
|
||||||
#define HAS_CPU_SHUTDOWN 0
|
#define HAS_CPU_SHUTDOWN 0
|
||||||
#endif
|
#endif
|
||||||
@@ -424,12 +441,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#define HAS_RGB_LED
|
#define HAS_RGB_LED
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef LED_STATE_OFF
|
|
||||||
#define LED_STATE_OFF 0
|
|
||||||
#endif
|
|
||||||
#ifndef LED_STATE_ON
|
#ifndef LED_STATE_ON
|
||||||
#define LED_STATE_ON 1
|
#define LED_STATE_ON 1
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef LED_STATE_OFF
|
||||||
|
#define LED_STATE_OFF (LED_STATE_ON ^ 1)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ledOff
|
||||||
|
#define ledOff(pin) pinMode(pin, INPUT)
|
||||||
|
#endif
|
||||||
|
|
||||||
// default mapping of pins
|
// default mapping of pins
|
||||||
#if defined(PIN_BUTTON2) && !defined(CANCEL_BUTTON_PIN)
|
#if defined(PIN_BUTTON2) && !defined(CANCEL_BUTTON_PIN)
|
||||||
@@ -445,18 +466,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// BME680 BSEC2 support detection
|
|
||||||
#if !defined(MESHTASTIC_BME680_BSEC2_SUPPORTED)
|
|
||||||
#if defined(RAK_4631) || defined(TBEAM_V10)
|
|
||||||
|
|
||||||
#define MESHTASTIC_BME680_BSEC2_SUPPORTED 1
|
|
||||||
#define MESHTASTIC_BME680_HEADER <bsec2.h>
|
|
||||||
#else
|
|
||||||
#define MESHTASTIC_BME680_BSEC2_SUPPORTED 0
|
|
||||||
#define MESHTASTIC_BME680_HEADER <Adafruit_BME680.h>
|
|
||||||
#endif // defined(RAK_4631)
|
|
||||||
#endif // !defined(MESHTASTIC_BME680_BSEC2_SUPPORTED)
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Global switches to turn off features for a minimized build
|
// Global switches to turn off features for a minimized build
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -37,14 +37,14 @@ ScanI2C::FoundDevice ScanI2C::firstKeyboard() const
|
|||||||
|
|
||||||
ScanI2C::FoundDevice ScanI2C::firstAccelerometer() const
|
ScanI2C::FoundDevice ScanI2C::firstAccelerometer() const
|
||||||
{
|
{
|
||||||
ScanI2C::DeviceType types[] = {MPU6050, LIS3DH, BMA423, LSM6DS3, BMX160, STK8BAXX, ICM20948, QMA6100P, BMM150};
|
ScanI2C::DeviceType types[] = {MPU6050, LIS3DH, BMA423, LSM6DS3, BMX160, STK8BAXX, ICM20948, QMA6100P, BMM150, BMI270};
|
||||||
return firstOfOrNONE(9, types);
|
return firstOfOrNONE(10, types);
|
||||||
}
|
}
|
||||||
|
|
||||||
ScanI2C::FoundDevice ScanI2C::firstAQI() const
|
ScanI2C::FoundDevice ScanI2C::firstAQI() const
|
||||||
{
|
{
|
||||||
ScanI2C::DeviceType types[] = {PMSA003I, SCD4X};
|
ScanI2C::DeviceType types[] = {PMSA003I, SEN5X, SCD4X, SFA30};
|
||||||
return firstOfOrNONE(2, types);
|
return firstOfOrNONE(4, types);
|
||||||
}
|
}
|
||||||
|
|
||||||
ScanI2C::FoundDevice ScanI2C::firstRGBLED() const
|
ScanI2C::FoundDevice ScanI2C::firstRGBLED() const
|
||||||
|
|||||||
@@ -88,7 +88,13 @@ class ScanI2C
|
|||||||
BH1750,
|
BH1750,
|
||||||
DA217,
|
DA217,
|
||||||
CHSC6X,
|
CHSC6X,
|
||||||
CST226SE
|
CST226SE,
|
||||||
|
BMI270,
|
||||||
|
SEN5X,
|
||||||
|
SFA30,
|
||||||
|
CW2015,
|
||||||
|
SCD30,
|
||||||
|
ADS1115
|
||||||
} DeviceType;
|
} DeviceType;
|
||||||
|
|
||||||
// typedef uint8_t DeviceAddress;
|
// typedef uint8_t DeviceAddress;
|
||||||
|
|||||||
+129
-13
@@ -1,4 +1,6 @@
|
|||||||
#include "ScanI2CTwoWire.h"
|
#include "ScanI2CTwoWire.h"
|
||||||
|
#include "configuration.h"
|
||||||
|
#include "detect/ScanI2C.h"
|
||||||
|
|
||||||
#if !MESHTASTIC_EXCLUDE_I2C
|
#if !MESHTASTIC_EXCLUDE_I2C
|
||||||
|
|
||||||
@@ -8,6 +10,7 @@
|
|||||||
#endif
|
#endif
|
||||||
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL)
|
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL)
|
||||||
#include "meshUtils.h" // vformat
|
#include "meshUtils.h" // vformat
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool in_array(uint8_t *array, int size, uint8_t lookfor)
|
bool in_array(uint8_t *array, int size, uint8_t lookfor)
|
||||||
@@ -114,6 +117,64 @@ uint16_t ScanI2CTwoWire::getRegisterValue(const ScanI2CTwoWire::RegisterLocation
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ScanI2CTwoWire::i2cCommandResponseLength(ScanI2C::DeviceAddress addr, uint16_t command, uint8_t expectedLength) const
|
||||||
|
{
|
||||||
|
TwoWire *i2cBus = fetchI2CBus(addr);
|
||||||
|
i2cBus->beginTransmission(addr.address);
|
||||||
|
if (command > 0xFF) {
|
||||||
|
i2cBus->write((uint8_t)(command >> 8));
|
||||||
|
}
|
||||||
|
i2cBus->write((uint8_t)(command & 0xFF));
|
||||||
|
if (i2cBus->endTransmission() != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
delay(20);
|
||||||
|
uint8_t received = i2cBus->requestFrom(addr.address, expectedLength);
|
||||||
|
bool match = (received == expectedLength);
|
||||||
|
while (i2cBus->available())
|
||||||
|
i2cBus->read();
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// for SEN5X detection
|
||||||
|
// Note, this code needs to be called before setting the I2C bus speed
|
||||||
|
// for the screen at high speed. The speed needs to be at 100kHz, otherwise
|
||||||
|
// detection will not work
|
||||||
|
String readSEN5xProductName(TwoWire *i2cBus, uint8_t address)
|
||||||
|
{
|
||||||
|
uint8_t cmd[] = {0xD0, 0x14};
|
||||||
|
uint8_t response[48] = {0};
|
||||||
|
|
||||||
|
i2cBus->beginTransmission(address);
|
||||||
|
i2cBus->write(cmd, 2);
|
||||||
|
if (i2cBus->endTransmission() != 0)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
delay(20);
|
||||||
|
if (i2cBus->requestFrom(address, (uint8_t)48) != 48)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
for (int i = 0; i < 48 && i2cBus->available(); ++i) {
|
||||||
|
response[i] = i2cBus->read();
|
||||||
|
}
|
||||||
|
|
||||||
|
char productName[33] = {0};
|
||||||
|
int j = 0;
|
||||||
|
for (int i = 0; i < 48 && j < 32; i += 3) {
|
||||||
|
if (response[i] >= 32 && response[i] <= 126)
|
||||||
|
productName[j++] = response[i];
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (response[i + 1] >= 32 && response[i + 1] <= 126)
|
||||||
|
productName[j++] = response[i + 1];
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return String(productName);
|
||||||
|
}
|
||||||
|
|
||||||
#define SCAN_SIMPLE_CASE(ADDR, T, ...) \
|
#define SCAN_SIMPLE_CASE(ADDR, T, ...) \
|
||||||
case ADDR: \
|
case ADDR: \
|
||||||
logFoundDevice(__VA_ARGS__); \
|
logFoundDevice(__VA_ARGS__); \
|
||||||
@@ -390,8 +451,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
|||||||
if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2) == 0x5449) {
|
if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2) == 0x5449) {
|
||||||
type = OPT3001;
|
type = OPT3001;
|
||||||
logFoundDevice("OPT3001", (uint8_t)addr.address);
|
logFoundDevice("OPT3001", (uint8_t)addr.address);
|
||||||
} else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x89), 6) !=
|
} else if (i2cCommandResponseLength(addr, 0x89, 6)) { // SHT4x serial number (6 bytes inc. CRC)
|
||||||
0) { // unique SHT4x serial number (6 bytes inc. CRC)
|
|
||||||
type = SHT4X;
|
type = SHT4X;
|
||||||
logFoundDevice("SHT4X", (uint8_t)addr.address);
|
logFoundDevice("SHT4X", (uint8_t)addr.address);
|
||||||
} else {
|
} else {
|
||||||
@@ -416,6 +476,19 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case LPS22HB_ADDR_ALT:
|
case LPS22HB_ADDR_ALT:
|
||||||
|
// SFA30 detection: send 2-byte command 0xD060 (Get Device Marking) and check for 48-byte response
|
||||||
|
if (i2cCommandResponseLength(addr, 0xD060, 48)) {
|
||||||
|
type = SFA30;
|
||||||
|
logFoundDevice("SFA30", (uint8_t)addr.address);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Fallback: LPS22HB detection at alternate address using WHO_AM_I register (0x0F == 0xB1)
|
||||||
|
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0F), 1);
|
||||||
|
if (registerValue == 0xB1) {
|
||||||
|
type = LPS22HB;
|
||||||
|
logFoundDevice("LPS22HB", (uint8_t)addr.address);
|
||||||
|
}
|
||||||
|
break;
|
||||||
SCAN_SIMPLE_CASE(LPS22HB_ADDR, LPS22HB, "LPS22HB", (uint8_t)addr.address)
|
SCAN_SIMPLE_CASE(LPS22HB_ADDR, LPS22HB, "LPS22HB", (uint8_t)addr.address)
|
||||||
SCAN_SIMPLE_CASE(QMC6310U_ADDR, QMC6310U, "QMC6310U", (uint8_t)addr.address)
|
SCAN_SIMPLE_CASE(QMC6310U_ADDR, QMC6310U, "QMC6310U", (uint8_t)addr.address)
|
||||||
|
|
||||||
@@ -508,6 +581,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
|||||||
SCAN_SIMPLE_CASE(DFROBOT_RAIN_ADDR, DFROBOT_RAIN, "DFRobot Rain Gauge", (uint8_t)addr.address);
|
SCAN_SIMPLE_CASE(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(SCD30_ADDR, SCD30, "SCD30", (uint8_t)addr.address);
|
||||||
case CST328_ADDR:
|
case CST328_ADDR:
|
||||||
// Do we have the CST328 or the CST226SE
|
// Do we have the CST328 or the CST226SE
|
||||||
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xAB), 1);
|
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xAB), 1);
|
||||||
@@ -541,7 +615,17 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
SCAN_SIMPLE_CASE(BHI260AP_ADDR, BHI260AP, "BHI260AP", (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);
|
case SCD4X_ADDR: {
|
||||||
|
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x8), 1);
|
||||||
|
if (registerValue == 0x18) {
|
||||||
|
logFoundDevice("CW2015", (uint8_t)addr.address);
|
||||||
|
type = CW2015;
|
||||||
|
} else {
|
||||||
|
logFoundDevice("SCD4X", (uint8_t)addr.address);
|
||||||
|
type = SCD4X;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
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
|
||||||
SCAN_SIMPLE_CASE(TPS65233_ADDR, TPS65233, "TPS65233", (uint8_t)addr.address);
|
SCAN_SIMPLE_CASE(TPS65233_ADDR, TPS65233, "TPS65233", (uint8_t)addr.address);
|
||||||
@@ -568,8 +652,8 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ICM20948_ADDR: // same as BMX160_ADDR
|
case ICM20948_ADDR: // same as BMX160_ADDR, BMI270_ADDR_ALT, and SEN5X_ADDR
|
||||||
case ICM20948_ADDR_ALT: // same as MPU6050_ADDR
|
case ICM20948_ADDR_ALT: // same as MPU6050_ADDR, BMI270_ADDR
|
||||||
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1);
|
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1);
|
||||||
#ifdef HAS_ICM20948
|
#ifdef HAS_ICM20948
|
||||||
type = ICM20948;
|
type = ICM20948;
|
||||||
@@ -580,14 +664,39 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
|||||||
type = ICM20948;
|
type = ICM20948;
|
||||||
logFoundDevice("ICM20948", (uint8_t)addr.address);
|
logFoundDevice("ICM20948", (uint8_t)addr.address);
|
||||||
break;
|
break;
|
||||||
} else if (addr.address == BMX160_ADDR) {
|
} else if (registerValue == 0x24) {
|
||||||
|
type = BMI270;
|
||||||
|
logFoundDevice("BMI270", (uint8_t)addr.address);
|
||||||
|
break;
|
||||||
|
} else if (registerValue == 0xD8) { // BMX160 chip ID at register 0x00
|
||||||
type = BMX160;
|
type = BMX160;
|
||||||
logFoundDevice("BMX160", (uint8_t)addr.address);
|
logFoundDevice("BMX160", (uint8_t)addr.address);
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
type = MPU6050;
|
String prod = "";
|
||||||
logFoundDevice("MPU6050", (uint8_t)addr.address);
|
prod = readSEN5xProductName(i2cBus, addr.address);
|
||||||
break;
|
if (prod.startsWith("SEN55")) {
|
||||||
|
type = SEN5X;
|
||||||
|
logFoundDevice("Sensirion SEN55", addr.address);
|
||||||
|
break;
|
||||||
|
} else if (prod.startsWith("SEN54")) {
|
||||||
|
type = SEN5X;
|
||||||
|
logFoundDevice("Sensirion SEN54", addr.address);
|
||||||
|
break;
|
||||||
|
} else if (prod.startsWith("SEN50")) {
|
||||||
|
type = SEN5X;
|
||||||
|
logFoundDevice("Sensirion SEN50", addr.address);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (addr.address == BMX160_ADDR) {
|
||||||
|
type = BMX160;
|
||||||
|
logFoundDevice("BMX160", (uint8_t)addr.address);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
type = MPU6050;
|
||||||
|
logFoundDevice("MPU6050", (uint8_t)addr.address);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -616,11 +725,18 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
|||||||
if (len == 5 && memcmp(expectedInfo, info, len) == 0) {
|
if (len == 5 && memcmp(expectedInfo, info, len) == 0) {
|
||||||
LOG_INFO("NXP SE050 crypto chip found");
|
LOG_INFO("NXP SE050 crypto chip found");
|
||||||
type = NXP_SE050;
|
type = NXP_SE050;
|
||||||
|
break;
|
||||||
} else {
|
|
||||||
LOG_INFO("FT6336U touchscreen found");
|
|
||||||
type = FT6336U;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x01), 2);
|
||||||
|
if (registerValue == 0x8583 || registerValue == 0x8580) {
|
||||||
|
type = ADS1115;
|
||||||
|
logFoundDevice("ADS1115 ADC", (uint8_t)addr.address);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INFO("FT6336U touchscreen found");
|
||||||
|
type = FT6336U;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,8 @@ class ScanI2CTwoWire : public ScanI2C
|
|||||||
|
|
||||||
uint16_t getRegisterValue(const RegisterLocation &, ResponseWidth, bool) const;
|
uint16_t getRegisterValue(const RegisterLocation &, ResponseWidth, bool) const;
|
||||||
|
|
||||||
|
bool i2cCommandResponseLength(DeviceAddress addr, uint16_t command, uint8_t expectedLength) const;
|
||||||
|
|
||||||
DeviceType probeOLED(ScanI2C::DeviceAddress) const;
|
DeviceType probeOLED(ScanI2C::DeviceAddress) const;
|
||||||
|
|
||||||
static void logFoundDevice(const char *device, uint8_t address);
|
static void logFoundDevice(const char *device, uint8_t address);
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#include "reClockI2C.h"
|
||||||
|
#include "ScanI2CTwoWire.h"
|
||||||
|
|
||||||
|
uint32_t reClockI2C(uint32_t desiredClock, TwoWire *i2cBus, bool force)
|
||||||
|
{
|
||||||
|
|
||||||
|
uint32_t currentClock = 0;
|
||||||
|
|
||||||
|
/* See https://github.com/arduino/Arduino/issues/11457
|
||||||
|
Currently, only ESP32 can getClock()
|
||||||
|
While all cores can setClock()
|
||||||
|
https://github.com/sandeepmistry/arduino-nRF5/blob/master/libraries/Wire/Wire.h#L50
|
||||||
|
https://github.com/earlephilhower/arduino-pico/blob/master/libraries/Wire/src/Wire.h#L60
|
||||||
|
https://github.com/stm32duino/Arduino_Core_STM32/blob/main/libraries/Wire/src/Wire.h#L103
|
||||||
|
For cases when I2C speed is different to the ones defined by sensors (see defines in sensor classes)
|
||||||
|
we need to reclock I2C and set it back to the previous desired speed.
|
||||||
|
Only for cases where we can know OR predefine the speed, we can do this.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TODO add getClock function or return a predefined clock speed per variant?
|
||||||
|
#ifdef CAN_RECLOCK_I2C
|
||||||
|
currentClock = i2cBus->getClock();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if ((currentClock != desiredClock) || force) {
|
||||||
|
LOG_DEBUG("Changing I2C clock to %u", desiredClock);
|
||||||
|
i2cBus->setClock(desiredClock);
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentClock;
|
||||||
|
}
|
||||||
+7
-37
@@ -1,41 +1,11 @@
|
|||||||
#ifdef CAN_RECLOCK_I2C
|
|
||||||
|
#ifndef RECLOCK_I2C_
|
||||||
|
#define RECLOCK_I2C_
|
||||||
|
|
||||||
#include "ScanI2CTwoWire.h"
|
#include "ScanI2CTwoWire.h"
|
||||||
|
#include <Wire.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
uint32_t reClockI2C(uint32_t desiredClock, TwoWire *i2cBus)
|
uint32_t reClockI2C(uint32_t desiredClock, TwoWire *i2cBus, bool force);
|
||||||
{
|
|
||||||
|
|
||||||
uint32_t currentClock;
|
|
||||||
|
|
||||||
/* See https://github.com/arduino/Arduino/issues/11457
|
|
||||||
Currently, only ESP32 can getClock()
|
|
||||||
While all cores can setClock()
|
|
||||||
https://github.com/sandeepmistry/arduino-nRF5/blob/master/libraries/Wire/Wire.h#L50
|
|
||||||
https://github.com/earlephilhower/arduino-pico/blob/master/libraries/Wire/src/Wire.h#L60
|
|
||||||
https://github.com/stm32duino/Arduino_Core_STM32/blob/main/libraries/Wire/src/Wire.h#L103
|
|
||||||
For cases when I2C speed is different to the ones defined by sensors (see defines in sensor classes)
|
|
||||||
we need to reclock I2C and set it back to the previous desired speed.
|
|
||||||
Only for cases where we can know OR predefine the speed, we can do this.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef ARCH_ESP32
|
|
||||||
currentClock = i2cBus->getClock();
|
|
||||||
#elif defined(ARCH_NRF52)
|
|
||||||
// TODO add getClock function or return a predefined clock speed per variant?
|
|
||||||
return 0;
|
|
||||||
#elif defined(ARCH_RP2040)
|
|
||||||
// TODO add getClock function or return a predefined clock speed per variant
|
|
||||||
return 0;
|
|
||||||
#elif defined(ARCH_STM32WL)
|
|
||||||
// TODO add getClock function or return a predefined clock speed per variant
|
|
||||||
return 0;
|
|
||||||
#else
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (currentClock != desiredClock) {
|
|
||||||
LOG_DEBUG("Changing I2C clock to %u", desiredClock);
|
|
||||||
i2cBus->setClock(desiredClock);
|
|
||||||
}
|
|
||||||
return currentClock;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+22
-18
@@ -52,7 +52,7 @@ SerialUART *GPS::_serial_gps = &GPS_SERIAL_PORT;
|
|||||||
HardwareSerial *GPS::_serial_gps = nullptr;
|
HardwareSerial *GPS::_serial_gps = nullptr;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
GPS *gps = nullptr;
|
std::unique_ptr<GPS> gps = nullptr;
|
||||||
|
|
||||||
static GPSUpdateScheduling scheduling;
|
static GPSUpdateScheduling scheduling;
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ static const char *getGPSPowerStateString(GPSPowerState state)
|
|||||||
|
|
||||||
#ifdef PIN_GPS_SWITCH
|
#ifdef PIN_GPS_SWITCH
|
||||||
// If we have a hardware switch, define a periodic watcher outside of the GPS runOnce thread, since this can be sleeping
|
// If we have a hardware switch, define a periodic watcher outside of the GPS runOnce thread, since this can be sleeping
|
||||||
// idefinitely
|
// indefinitely
|
||||||
|
|
||||||
int lastState = LOW;
|
int lastState = LOW;
|
||||||
bool firstrun = true;
|
bool firstrun = true;
|
||||||
@@ -127,7 +127,7 @@ static int32_t gpsSwitch()
|
|||||||
return 1000;
|
return 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
static concurrency::Periodic *gpsPeriodic;
|
static std::unique_ptr<concurrency::Periodic> gpsPeriodic;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void UBXChecksum(uint8_t *message, size_t length)
|
static void UBXChecksum(uint8_t *message, size_t length)
|
||||||
@@ -586,14 +586,14 @@ bool GPS::setup()
|
|||||||
_serial_gps->write("$PMTK301,2*2E\r\n");
|
_serial_gps->write("$PMTK301,2*2E\r\n");
|
||||||
delay(250);
|
delay(250);
|
||||||
} else if (gnssModel == GNSS_MODEL_ATGM336H) {
|
} else if (gnssModel == GNSS_MODEL_ATGM336H) {
|
||||||
// Set the intial configuration of the device - these _should_ work for most AT6558 devices
|
// Set the initial configuration of the device - these _should_ work for most AT6558 devices
|
||||||
msglen = makeCASPacket(0x06, 0x07, sizeof(_message_CAS_CFG_NAVX_CONF), _message_CAS_CFG_NAVX_CONF);
|
msglen = makeCASPacket(0x06, 0x07, sizeof(_message_CAS_CFG_NAVX_CONF), _message_CAS_CFG_NAVX_CONF);
|
||||||
_serial_gps->write(UBXscratch, msglen);
|
_serial_gps->write(UBXscratch, msglen);
|
||||||
if (getACKCas(0x06, 0x07, 250) != GNSS_RESPONSE_OK) {
|
if (getACKCas(0x06, 0x07, 250) != GNSS_RESPONSE_OK) {
|
||||||
LOG_WARN("ATGM336H: Could not set Config");
|
LOG_WARN("ATGM336H: Could not set Config");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the update frequence to 1Hz
|
// Set the update frequency to 1Hz
|
||||||
msglen = makeCASPacket(0x06, 0x04, sizeof(_message_CAS_CFG_RATE_1HZ), _message_CAS_CFG_RATE_1HZ);
|
msglen = makeCASPacket(0x06, 0x04, sizeof(_message_CAS_CFG_RATE_1HZ), _message_CAS_CFG_RATE_1HZ);
|
||||||
_serial_gps->write(UBXscratch, msglen);
|
_serial_gps->write(UBXscratch, msglen);
|
||||||
if (getACKCas(0x06, 0x04, 250) != GNSS_RESPONSE_OK) {
|
if (getACKCas(0x06, 0x04, 250) != GNSS_RESPONSE_OK) {
|
||||||
@@ -700,7 +700,7 @@ bool GPS::setup()
|
|||||||
} else { // 8,9
|
} else { // 8,9
|
||||||
LOG_INFO("GPS+SBAS+GLONASS+Galileo configured");
|
LOG_INFO("GPS+SBAS+GLONASS+Galileo configured");
|
||||||
}
|
}
|
||||||
// Documentation say, we need wait atleast 0.5s after reconfiguration of GNSS module, before sending next
|
// Documentation say, we need wait at least 0.5s after reconfiguration of GNSS module, before sending next
|
||||||
// commands for the M8 it tends to be more... 1 sec should be enough ;>)
|
// commands for the M8 it tends to be more... 1 sec should be enough ;>)
|
||||||
delay(1000);
|
delay(1000);
|
||||||
}
|
}
|
||||||
@@ -733,7 +733,7 @@ bool GPS::setup()
|
|||||||
SEND_UBX_PACKET(0x06, 0x86, _message_PMS, "enable powersave for GPS", 500);
|
SEND_UBX_PACKET(0x06, 0x86, _message_PMS, "enable powersave for GPS", 500);
|
||||||
SEND_UBX_PACKET(0x06, 0x3B, _message_CFG_PM2, "enable powersave details for GPS", 500);
|
SEND_UBX_PACKET(0x06, 0x3B, _message_CFG_PM2, "enable powersave details for GPS", 500);
|
||||||
|
|
||||||
// For M8 we want to enable NMEA vserion 4.10 so we can see the additional sats.
|
// For M8 we want to enable NMEA version 4.10 so we can see the additional sats.
|
||||||
if (gnssModel == GNSS_MODEL_UBLOX8) {
|
if (gnssModel == GNSS_MODEL_UBLOX8) {
|
||||||
clearBuffer();
|
clearBuffer();
|
||||||
SEND_UBX_PACKET(0x06, 0x17, _message_NMEA, "enable NMEA 4.10", 500);
|
SEND_UBX_PACKET(0x06, 0x17, _message_NMEA, "enable NMEA 4.10", 500);
|
||||||
@@ -905,6 +905,12 @@ void GPS::writePinStandby(bool standby)
|
|||||||
// Write and log
|
// Write and log
|
||||||
pinMode(PIN_GPS_STANDBY, OUTPUT);
|
pinMode(PIN_GPS_STANDBY, OUTPUT);
|
||||||
digitalWrite(PIN_GPS_STANDBY, val);
|
digitalWrite(PIN_GPS_STANDBY, val);
|
||||||
|
|
||||||
|
// Enter backup mode on PA1010D; TODO: may be applicable to other MTK GPS too
|
||||||
|
if (IS_ONE_OF(gnssModel, GNSS_MODEL_MTK_PA1010D)) {
|
||||||
|
_serial_gps->write("$PMTK225,4*2F\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef GPS_DEBUG
|
#ifdef GPS_DEBUG
|
||||||
LOG_DEBUG("Pin STANDBY %s", val == HIGH ? "HI" : "LOW");
|
LOG_DEBUG("Pin STANDBY %s", val == HIGH ? "HI" : "LOW");
|
||||||
#endif
|
#endif
|
||||||
@@ -1205,7 +1211,7 @@ int32_t GPS::runOnce()
|
|||||||
return disable(); // This should trigger when we have a fixed position, and get that first position
|
return disable(); // This should trigger when we have a fixed position, and get that first position
|
||||||
|
|
||||||
// 9600bps is approx 1 byte per msec, so considering our buffer size we never need to wake more often than 200ms
|
// 9600bps is approx 1 byte per msec, so considering our buffer size we never need to wake more often than 200ms
|
||||||
// if not awake we can run super infrquently (once every 5 secs?) to see if we need to wake.
|
// if not awake we can run super infrequently (once every 5 secs?) to see if we need to wake.
|
||||||
return (powerState == GPS_ACTIVE) ? GPS_THREAD_INTERVAL : 5000;
|
return (powerState == GPS_ACTIVE) ? GPS_THREAD_INTERVAL : 5000;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1479,7 +1485,7 @@ GnssModel_t GPS::getProbeResponse(unsigned long timeout, const std::vector<ChipI
|
|||||||
if (bufferSize > 2048)
|
if (bufferSize > 2048)
|
||||||
bufferSize = 2048;
|
bufferSize = 2048;
|
||||||
|
|
||||||
char *response = new char[bufferSize](); // Dynamically allocate based on baud rate
|
auto response = std::unique_ptr<char[]>(new char[bufferSize]); // Dynamically allocate based on baud rate
|
||||||
uint16_t responseLen = 0;
|
uint16_t responseLen = 0;
|
||||||
unsigned long start = millis();
|
unsigned long start = millis();
|
||||||
while (millis() - start < timeout) {
|
while (millis() - start < timeout) {
|
||||||
@@ -1495,19 +1501,18 @@ GnssModel_t GPS::getProbeResponse(unsigned long timeout, const std::vector<ChipI
|
|||||||
if (c == ',' || (responseLen >= 2 && response[responseLen - 2] == '\r' && response[responseLen - 1] == '\n')) {
|
if (c == ',' || (responseLen >= 2 && response[responseLen - 2] == '\r' && response[responseLen - 1] == '\n')) {
|
||||||
// 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, chipInfo.detectionString.c_str()) != nullptr) {
|
if (strstr(response.get(), chipInfo.detectionString.c_str()) != nullptr) {
|
||||||
#ifdef GPS_DEBUG
|
#ifdef GPS_DEBUG
|
||||||
LOG_DEBUG(response);
|
LOG_DEBUG(response.get());
|
||||||
#endif
|
#endif
|
||||||
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 (responseLen >= 2 && response[responseLen - 2] == '\r' && response[responseLen - 1] == '\n') {
|
if (responseLen >= 2 && response[responseLen - 2] == '\r' && response[responseLen - 1] == '\n') {
|
||||||
#ifdef GPS_DEBUG
|
#ifdef GPS_DEBUG
|
||||||
LOG_DEBUG(response);
|
LOG_DEBUG(response.get());
|
||||||
#endif
|
#endif
|
||||||
// Reset the response buffer for the next potential message
|
// Reset the response buffer for the next potential message
|
||||||
responseLen = 0;
|
responseLen = 0;
|
||||||
@@ -1516,13 +1521,12 @@ GnssModel_t GPS::getProbeResponse(unsigned long timeout, const std::vector<ChipI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef GPS_DEBUG
|
#ifdef GPS_DEBUG
|
||||||
LOG_DEBUG(response);
|
LOG_DEBUG(response.get());
|
||||||
#endif
|
#endif
|
||||||
delete[] response; // Cleanup before return
|
|
||||||
return GNSS_MODEL_UNKNOWN; // Return unknown on timeout
|
return GNSS_MODEL_UNKNOWN; // Return unknown on timeout
|
||||||
}
|
}
|
||||||
|
|
||||||
GPS *GPS::createGps()
|
std::unique_ptr<GPS> GPS::createGps()
|
||||||
{
|
{
|
||||||
int8_t _rx_gpio = config.position.rx_gpio;
|
int8_t _rx_gpio = config.position.rx_gpio;
|
||||||
int8_t _tx_gpio = config.position.tx_gpio;
|
int8_t _tx_gpio = config.position.tx_gpio;
|
||||||
@@ -1547,7 +1551,7 @@ GPS *GPS::createGps()
|
|||||||
if (!_rx_gpio || !_serial_gps) // Configured to have no GPS at all
|
if (!_rx_gpio || !_serial_gps) // Configured to have no GPS at all
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
GPS *new_gps = new GPS;
|
auto new_gps = std::unique_ptr<GPS>(new GPS());
|
||||||
new_gps->rx_gpio = _rx_gpio;
|
new_gps->rx_gpio = _rx_gpio;
|
||||||
new_gps->tx_gpio = _tx_gpio;
|
new_gps->tx_gpio = _tx_gpio;
|
||||||
|
|
||||||
@@ -1575,7 +1579,7 @@ GPS *GPS::createGps()
|
|||||||
#ifdef PIN_GPS_SWITCH
|
#ifdef PIN_GPS_SWITCH
|
||||||
// toggle GPS via external GPIO switch
|
// toggle GPS via external GPIO switch
|
||||||
pinMode(PIN_GPS_SWITCH, INPUT);
|
pinMode(PIN_GPS_SWITCH, INPUT);
|
||||||
gpsPeriodic = new concurrency::Periodic("GPSSwitch", gpsSwitch);
|
gpsPeriodic = std::unique_ptr<concurrency::Periodic>(new concurrency::Periodic("GPSSwitch", gpsSwitch));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Currently disabled per issue #525 (TinyGPS++ crash bug)
|
// Currently disabled per issue #525 (TinyGPS++ crash bug)
|
||||||
|
|||||||
+4
-2
@@ -2,6 +2,8 @@
|
|||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#if !MESHTASTIC_EXCLUDE_GPS
|
#if !MESHTASTIC_EXCLUDE_GPS
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "GPSStatus.h"
|
#include "GPSStatus.h"
|
||||||
#include "GpioLogic.h"
|
#include "GpioLogic.h"
|
||||||
#include "Observer.h"
|
#include "Observer.h"
|
||||||
@@ -118,7 +120,7 @@ class GPS : private concurrency::OSThread
|
|||||||
|
|
||||||
// Creates an instance of the GPS class.
|
// Creates an instance of the GPS class.
|
||||||
// Returns the new instance or null if the GPS is not present.
|
// Returns the new instance or null if the GPS is not present.
|
||||||
static GPS *createGps();
|
static std::unique_ptr<GPS> createGps();
|
||||||
|
|
||||||
// Wake the GPS hardware - ready for an update
|
// Wake the GPS hardware - ready for an update
|
||||||
void up();
|
void up();
|
||||||
@@ -256,5 +258,5 @@ class GPS : private concurrency::OSThread
|
|||||||
uint8_t fixeddelayCtr = 0;
|
uint8_t fixeddelayCtr = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern GPS *gps;
|
extern std::unique_ptr<GPS> gps;
|
||||||
#endif // Exclude GPS
|
#endif // Exclude GPS
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ GeoCoord::GeoCoord(int32_t lat, int32_t lon, int32_t alt) : _latitude(lat), _lon
|
|||||||
|
|
||||||
GeoCoord::GeoCoord(float lat, float lon, int32_t alt) : _altitude(alt)
|
GeoCoord::GeoCoord(float lat, float lon, int32_t alt) : _altitude(alt)
|
||||||
{
|
{
|
||||||
// Change decimial representation to int32_t. I.e., 12.345 becomes 123450000
|
// Change decimal representation to int32_t. I.e., 12.345 becomes 123450000
|
||||||
_latitude = int32_t(lat * 1e+7);
|
_latitude = int32_t(lat * 1e+7);
|
||||||
_longitude = int32_t(lon * 1e+7);
|
_longitude = int32_t(lon * 1e+7);
|
||||||
GeoCoord::setCoords();
|
GeoCoord::setCoords();
|
||||||
@@ -20,7 +20,7 @@ GeoCoord::GeoCoord(float lat, float lon, int32_t alt) : _altitude(alt)
|
|||||||
|
|
||||||
GeoCoord::GeoCoord(double lat, double lon, int32_t alt) : _altitude(alt)
|
GeoCoord::GeoCoord(double lat, double lon, int32_t alt) : _altitude(alt)
|
||||||
{
|
{
|
||||||
// Change decimial representation to int32_t. I.e., 12.345 becomes 123450000
|
// Change decimal representation to int32_t. I.e., 12.345 becomes 123450000
|
||||||
_latitude = int32_t(lat * 1e+7);
|
_latitude = int32_t(lat * 1e+7);
|
||||||
_longitude = int32_t(lon * 1e+7);
|
_longitude = int32_t(lon * 1e+7);
|
||||||
GeoCoord::setCoords();
|
GeoCoord::setCoords();
|
||||||
@@ -467,10 +467,10 @@ int32_t GeoCoord::bearingTo(const GeoCoord &pointB)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new point bassed on the passed in poin
|
* Create a new point based on the passed-in point
|
||||||
* Ported from http://www.edwilliams.org/avform147.htm#LL
|
* Ported from http://www.edwilliams.org/avform147.htm#LL
|
||||||
* @param bearing
|
* @param bearing
|
||||||
* The bearing in raidans
|
* The bearing in radians
|
||||||
* @param range_meters
|
* @param range_meters
|
||||||
* range in meters
|
* range in meters
|
||||||
* @return GeoCoord object of point at bearing and range from initial point
|
* @return GeoCoord object of point at bearing and range from initial point
|
||||||
@@ -593,4 +593,4 @@ double GeoCoord::toRadians(double deg)
|
|||||||
double GeoCoord::toDegrees(double r)
|
double GeoCoord::toDegrees(double r)
|
||||||
{
|
{
|
||||||
return r * 180 / PI;
|
return r * 180 / PI;
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-13
@@ -72,11 +72,13 @@ RTCSetResult readFromRTC()
|
|||||||
#elif defined(PCF8563_RTC) || defined(PCF85063_RTC)
|
#elif defined(PCF8563_RTC) || defined(PCF85063_RTC)
|
||||||
#if defined(PCF8563_RTC)
|
#if defined(PCF8563_RTC)
|
||||||
if (rtc_found.address == PCF8563_RTC) {
|
if (rtc_found.address == PCF8563_RTC) {
|
||||||
|
SensorPCF8563 rtc;
|
||||||
#elif defined(PCF85063_RTC)
|
#elif defined(PCF85063_RTC)
|
||||||
if (rtc_found.address == PCF85063_RTC) {
|
if (rtc_found.address == PCF85063_RTC) {
|
||||||
|
SensorPCF85063 rtc;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
uint32_t now = millis();
|
uint32_t now = millis();
|
||||||
SensorRtcHelper rtc;
|
|
||||||
|
|
||||||
#if WIRE_INTERFACES_COUNT == 2
|
#if WIRE_INTERFACES_COUNT == 2
|
||||||
rtc.begin(rtc_found.port == ScanI2C::I2CPort::WIRE1 ? Wire1 : Wire);
|
rtc.begin(rtc_found.port == ScanI2C::I2CPort::WIRE1 ? Wire1 : Wire);
|
||||||
@@ -221,7 +223,7 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd
|
|||||||
// This delta value works on all platforms
|
// This delta value works on all platforms
|
||||||
timeStartMsec = now;
|
timeStartMsec = now;
|
||||||
zeroOffsetSecs = tv->tv_sec;
|
zeroOffsetSecs = tv->tv_sec;
|
||||||
// If this platform has a setable RTC, set it
|
// If this platform has a settable RTC, set it
|
||||||
#ifdef RV3028_RTC
|
#ifdef RV3028_RTC
|
||||||
if (rtc_found.address == RV3028_RTC) {
|
if (rtc_found.address == RV3028_RTC) {
|
||||||
Melopero_RV3028 rtc;
|
Melopero_RV3028 rtc;
|
||||||
@@ -240,10 +242,12 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd
|
|||||||
#elif defined(PCF8563_RTC) || defined(PCF85063_RTC)
|
#elif defined(PCF8563_RTC) || defined(PCF85063_RTC)
|
||||||
#if defined(PCF8563_RTC)
|
#if defined(PCF8563_RTC)
|
||||||
if (rtc_found.address == PCF8563_RTC) {
|
if (rtc_found.address == PCF8563_RTC) {
|
||||||
|
SensorPCF8563 rtc;
|
||||||
#elif defined(PCF85063_RTC)
|
#elif defined(PCF85063_RTC)
|
||||||
if (rtc_found.address == PCF85063_RTC) {
|
if (rtc_found.address == PCF85063_RTC) {
|
||||||
|
SensorPCF85063 rtc;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
SensorRtcHelper rtc;
|
|
||||||
|
|
||||||
#if WIRE_INTERFACES_COUNT == 2
|
#if WIRE_INTERFACES_COUNT == 2
|
||||||
rtc.begin(rtc_found.port == ScanI2C::I2CPort::WIRE1 ? Wire1 : Wire);
|
rtc.begin(rtc_found.port == ScanI2C::I2CPort::WIRE1 ? Wire1 : Wire);
|
||||||
@@ -276,11 +280,7 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd
|
|||||||
settimeofday(tv, NULL);
|
settimeofday(tv, NULL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// nrf52 doesn't have a readable RTC (yet - software not written)
|
|
||||||
#if HAS_RTC
|
|
||||||
readFromRTC();
|
readFromRTC();
|
||||||
#endif
|
|
||||||
|
|
||||||
return RTCSetResultSuccess;
|
return RTCSetResultSuccess;
|
||||||
} else {
|
} else {
|
||||||
return RTCSetResultNotSet; // RTC was already set with a higher quality time
|
return RTCSetResultNotSet; // RTC was already set with a higher quality time
|
||||||
@@ -312,7 +312,7 @@ const char *RtcName(RTCQuality quality)
|
|||||||
* @param t The time to potentially set the RTC to.
|
* @param t The time to potentially set the RTC to.
|
||||||
* @return True if the RTC was set to the provided time, false otherwise.
|
* @return True if the RTC was set to the provided time, false otherwise.
|
||||||
*/
|
*/
|
||||||
RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t)
|
RTCSetResult perhapsSetRTC(RTCQuality q, const struct tm &t)
|
||||||
{
|
{
|
||||||
/* Convert to unix time
|
/* Convert to unix time
|
||||||
The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January 1, 1970
|
The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January 1, 1970
|
||||||
@@ -397,12 +397,12 @@ uint32_t getValidTime(RTCQuality minQuality, bool local)
|
|||||||
return (currentQuality >= minQuality) ? getTime(local) : 0;
|
return (currentQuality >= minQuality) ? getTime(local) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
time_t gm_mktime(struct tm *tm)
|
time_t gm_mktime(const struct tm *tm)
|
||||||
{
|
{
|
||||||
#if !MESHTASTIC_EXCLUDE_TZ
|
#if !MESHTASTIC_EXCLUDE_TZ
|
||||||
time_t result = 0;
|
time_t result = 0;
|
||||||
|
|
||||||
// First, get us to the start of tm->year, by calcuating the number of days since the Unix epoch.
|
// First, get us to the start of tm->year, by calculating the number of days since the Unix epoch.
|
||||||
int year = 1900 + tm->tm_year; // tm_year is years since 1900
|
int year = 1900 + tm->tm_year; // tm_year is years since 1900
|
||||||
int year_minus_one = year - 1;
|
int year_minus_one = year - 1;
|
||||||
int days_before_this_year = 0;
|
int days_before_this_year = 0;
|
||||||
@@ -413,8 +413,8 @@ time_t gm_mktime(struct tm *tm)
|
|||||||
days_before_this_year -= 719162; // (1969 * 365 + 1969 / 4 - 1969 / 100 + 1969 / 400);
|
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.
|
// 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
|
static const 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
|
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 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)) {
|
if (tm->tm_mon >= 2 && (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)) {
|
||||||
@@ -435,6 +435,7 @@ time_t gm_mktime(struct tm *tm)
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
#else
|
#else
|
||||||
return mktime(tm);
|
struct tm tmCopy = *tm;
|
||||||
|
return mktime(&tmCopy);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -41,7 +41,7 @@ extern uint32_t lastSetFromPhoneNtpOrGps;
|
|||||||
|
|
||||||
/// 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
|
||||||
RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate = false);
|
RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate = false);
|
||||||
RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t);
|
RTCSetResult perhapsSetRTC(RTCQuality q, const struct tm &t);
|
||||||
|
|
||||||
/// Return a string name for the quality
|
/// Return a string name for the quality
|
||||||
const char *RtcName(RTCQuality quality);
|
const char *RtcName(RTCQuality quality);
|
||||||
@@ -54,7 +54,7 @@ uint32_t getValidTime(RTCQuality minQuality, bool local = false);
|
|||||||
|
|
||||||
RTCSetResult readFromRTC();
|
RTCSetResult readFromRTC();
|
||||||
|
|
||||||
time_t gm_mktime(struct tm *tm);
|
time_t gm_mktime(const struct tm *tm);
|
||||||
|
|
||||||
#define SEC_PER_DAY 86400
|
#define SEC_PER_DAY 86400
|
||||||
#define SEC_PER_HOUR 3600
|
#define SEC_PER_HOUR 3600
|
||||||
|
|||||||
+1
-1
@@ -37,7 +37,7 @@ static const uint8_t _message_CAS_CFG_RATE_1HZ[] = {
|
|||||||
|
|
||||||
// CFG-NAVX (0x06, 0x07)
|
// CFG-NAVX (0x06, 0x07)
|
||||||
// Initial ATGM33H-5N configuration, Updates for Dynamic Mode, Fix Mode, and SV system
|
// Initial ATGM33H-5N configuration, Updates for Dynamic Mode, Fix Mode, and SV system
|
||||||
// Qwirk: The ATGM33H-5N-31 should only support GPS+BDS, however it will happily enable
|
// Quirk: The ATGM33H-5N-31 should only support GPS+BDS, however it will happily enable
|
||||||
// and use GPS+BDS+GLONASS iff the correct CFG_NAVX command is used.
|
// and use GPS+BDS+GLONASS iff the correct CFG_NAVX command is used.
|
||||||
static const uint8_t _message_CAS_CFG_NAVX_CONF[] = {
|
static const uint8_t _message_CAS_CFG_NAVX_CONF[] = {
|
||||||
0x03, 0x01, 0x00, 0x00, // Update Mask: Dynamic Mode, Fix Mode, Nav Settings
|
0x03, 0x01, 0x00, 0x00, // Update Mask: Dynamic Mode, Fix Mode, Nav Settings
|
||||||
|
|||||||
+6
-6
@@ -57,7 +57,7 @@ static const uint8_t _message_CFG_PM2[] PROGMEM = {
|
|||||||
0x00, 0x00, 0x00, 0x00 // 0x64, 0x40, 0x01, 0x00 // reserved 11
|
0x00, 0x00, 0x00, 0x00 // 0x64, 0x40, 0x01, 0x00 // reserved 11
|
||||||
};
|
};
|
||||||
|
|
||||||
// Constallation setup, none required for Neo-6
|
// Constellation setup, none required for Neo-6
|
||||||
|
|
||||||
// For Neo-7 GPS & SBAS
|
// For Neo-7 GPS & SBAS
|
||||||
static const uint8_t _message_GNSS_7[] = {
|
static const uint8_t _message_GNSS_7[] = {
|
||||||
@@ -157,7 +157,7 @@ static const uint8_t _message_NAVX5[] = {
|
|||||||
0x00, 0x00, 0x00, 0x00, // Reserved 9
|
0x00, 0x00, 0x00, 0x00, // Reserved 9
|
||||||
0x00, // Reserved 10
|
0x00, // Reserved 10
|
||||||
0x00, // Reserved 11
|
0x00, // Reserved 11
|
||||||
0x00, // usePPP (Precice Point Positioning) (0 = false, 1 = true)
|
0x00, // usePPP (Precise Point Positioning) (0 = false, 1 = true)
|
||||||
0x01, // useAOP (AssistNow Autonomous configuration) = 1 (enabled)
|
0x01, // useAOP (AssistNow Autonomous configuration) = 1 (enabled)
|
||||||
0x00, // Reserved 12
|
0x00, // Reserved 12
|
||||||
0x00, // Reserved 13
|
0x00, // Reserved 13
|
||||||
@@ -185,7 +185,7 @@ static const uint8_t _message_NAVX5_8[] = {
|
|||||||
0x00, // Reserved 4
|
0x00, // Reserved 4
|
||||||
0x00, 0x00, // Reserved 5
|
0x00, 0x00, // Reserved 5
|
||||||
0x00, 0x00, // Reserved 6
|
0x00, 0x00, // Reserved 6
|
||||||
0x00, // usePPP (Precice Point Positioning) (0 = false, 1 = true)
|
0x00, // usePPP (Precise Point Positioning) (0 = false, 1 = true)
|
||||||
0x01, // aopCfg (AssistNow Autonomous configuration) = 1 (enabled)
|
0x01, // aopCfg (AssistNow Autonomous configuration) = 1 (enabled)
|
||||||
0x00, 0x00, // Reserved 7
|
0x00, 0x00, // Reserved 7
|
||||||
0x00, 0x00, // aopOrbMaxErr = 0 to reset to firmware default
|
0x00, 0x00, // aopOrbMaxErr = 0 to reset to firmware default
|
||||||
@@ -314,7 +314,7 @@ static const uint8_t _message_DISABLE_TXT_INFO[] = {
|
|||||||
// This command applies to M8 products
|
// This command applies to M8 products
|
||||||
static const uint8_t _message_PMS[] = {
|
static const uint8_t _message_PMS[] = {
|
||||||
0x00, // Version (0)
|
0x00, // Version (0)
|
||||||
0x03, // Power setup value 3 = Agresssive 1Hz
|
0x03, // Power setup value 3 = Agressive 1Hz
|
||||||
0x00, 0x00, // period: not applicable, set to 0
|
0x00, 0x00, // period: not applicable, set to 0
|
||||||
0x00, 0x00, // onTime: not applicable, set to 0
|
0x00, 0x00, // onTime: not applicable, set to 0
|
||||||
0x00, 0x00 // reserved, generated by u-center
|
0x00, 0x00 // reserved, generated by u-center
|
||||||
@@ -337,7 +337,7 @@ static const uint8_t _message_SAVE_10[] = {
|
|||||||
// As the M10 has no flash, the best we can do to preserve the config is to set it in RAM and BBR.
|
// As the M10 has no flash, the best we can do to preserve the config is to set it in RAM and BBR.
|
||||||
// BBR will survive a restart, and power off for a while, but modules with small backup
|
// BBR will survive a restart, and power off for a while, but modules with small backup
|
||||||
// batteries or super caps will not retain the config for a long power off time.
|
// batteries or super caps will not retain the config for a long power off time.
|
||||||
// for all configurations using sleep / low power modes, V_BCKP needs to be hooked to permanent power for fast aquisition after
|
// for all configurations using sleep / low power modes, V_BCKP needs to be hooked to permanent power for fast acquisition after
|
||||||
// sleep
|
// sleep
|
||||||
|
|
||||||
// VALSET Commands for M10
|
// VALSET Commands for M10
|
||||||
@@ -462,7 +462,7 @@ Default GNSS configuration is: GPS, Galileo, BDS B1l, with QZSS and SBAS enabled
|
|||||||
The PMREQ puts the receiver to sleep and wakeup re-acquires really fast and seems to not need
|
The PMREQ puts the receiver to sleep and wakeup re-acquires really fast and seems to not need
|
||||||
the PM config. Lets try without it.
|
the PM config. Lets try without it.
|
||||||
PMREQ sort of works with SBAS, but the awake time is too short to re-acquire any SBAS sats.
|
PMREQ sort of works with SBAS, but the awake time is too short to re-acquire any SBAS sats.
|
||||||
The defination of "Got Fix" doesn't seem to include SBAS. Much more too this...
|
The definition of "Got Fix" doesn't seem to include SBAS. Much more too this...
|
||||||
Even if it was, it can take minutes (up to 12.5),
|
Even if it was, it can take minutes (up to 12.5),
|
||||||
even under good sat visibility conditions to re-acquire the SBAS data.
|
even under good sat visibility conditions to re-acquire the SBAS data.
|
||||||
|
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ bool EInkDisplay::forceDisplay(uint32_t msecLimit)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// End the update process - virtual method, overriden in derived class
|
// End the update process - virtual method, overridden in derived class
|
||||||
void EInkDisplay::endUpdate()
|
void EInkDisplay::endUpdate()
|
||||||
{
|
{
|
||||||
// Power off display hardware, then deep-sleep (Except Wireless Paper V1.1, no deep-sleep)
|
// Power off display hardware, then deep-sleep (Except Wireless Paper V1.1, no deep-sleep)
|
||||||
@@ -259,6 +259,17 @@ bool EInkDisplay::connect()
|
|||||||
adafruitDisplay->setRotation(3);
|
adafruitDisplay->setRotation(3);
|
||||||
adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT);
|
adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT);
|
||||||
}
|
}
|
||||||
|
#elif defined(MINI_EPAPER_S3)
|
||||||
|
spi1 = new SPIClass(HSPI);
|
||||||
|
spi1->begin(PIN_SPI1_SCK, PIN_SPI1_MISO, PIN_SPI1_MOSI, PIN_EINK_CS);
|
||||||
|
|
||||||
|
// Create GxEPD2 objects
|
||||||
|
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, *spi1);
|
||||||
|
adafruitDisplay = new GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT>(*lowLevel);
|
||||||
|
|
||||||
|
// Init GxEPD2
|
||||||
|
adafruitDisplay->init();
|
||||||
|
adafruitDisplay->setRotation(1);
|
||||||
#elif defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_VISION_MASTER_E213)
|
#elif defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_VISION_MASTER_E213)
|
||||||
|
|
||||||
// Detect display model, before starting SPI
|
// Detect display model, before starting SPI
|
||||||
|
|||||||
@@ -93,7 +93,8 @@ class EInkDisplay : public OLEDDisplay
|
|||||||
SPIClass *hspi = NULL;
|
SPIClass *hspi = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(HELTEC_MESH_POCKET) || defined(SEEED_WIO_TRACKER_L1_EINK) || defined(HELTEC_MESH_SOLAR_EINK)
|
#if defined(HELTEC_MESH_POCKET) || defined(SEEED_WIO_TRACKER_L1_EINK) || defined(HELTEC_MESH_SOLAR_EINK) || \
|
||||||
|
defined(MINI_EPAPER_S3)
|
||||||
SPIClass *spi1 = NULL;
|
SPIClass *spi1 = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ EInkDynamicDisplay::EInkDynamicDisplay(uint8_t address, int sda, int scl, OLEDDI
|
|||||||
{
|
{
|
||||||
// If tracking ghost pixels, grab memory
|
// If tracking ghost pixels, grab memory
|
||||||
#ifdef EINK_LIMIT_GHOSTING_PX
|
#ifdef EINK_LIMIT_GHOSTING_PX
|
||||||
dirtyPixels = new uint8_t[EInkDisplay::displayBufferSize](); // Init with zeros
|
dirtyPixels = std::unique_ptr<uint8_t[]>(new uint8_t[EInkDisplay::displayBufferSize]()); // Init with zeros
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ EInkDynamicDisplay::~EInkDynamicDisplay()
|
|||||||
{
|
{
|
||||||
// If we were tracking ghost pixels, free the memory
|
// If we were tracking ghost pixels, free the memory
|
||||||
#ifdef EINK_LIMIT_GHOSTING_PX
|
#ifdef EINK_LIMIT_GHOSTING_PX
|
||||||
delete[] dirtyPixels;
|
dirtyPixels = nullptr;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +95,7 @@ void EInkDynamicDisplay::adjustRefreshCounters()
|
|||||||
// Trigger the display update by calling base class
|
// Trigger the display update by calling base class
|
||||||
bool EInkDynamicDisplay::update()
|
bool EInkDynamicDisplay::update()
|
||||||
{
|
{
|
||||||
// Detemine the refresh mode to use, and start the update
|
// Determine the refresh mode to use, and start the update
|
||||||
bool refreshApproved = determineMode();
|
bool refreshApproved = determineMode();
|
||||||
if (refreshApproved) {
|
if (refreshApproved) {
|
||||||
EInkDisplay::forceDisplay(0); // Bypass base class' own rate-limiting system
|
EInkDisplay::forceDisplay(0); // Bypass base class' own rate-limiting system
|
||||||
@@ -317,7 +317,7 @@ void EInkDynamicDisplay::checkFrameMatchesPrevious()
|
|||||||
LOG_DEBUG("refresh=SKIPPED, reason=FRAME_MATCHED_PREVIOUS, frameFlags=0x%x", frameFlags);
|
LOG_DEBUG("refresh=SKIPPED, reason=FRAME_MATCHED_PREVIOUS, frameFlags=0x%x", frameFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Have too many fast-refreshes occured consecutively, since last full refresh?
|
// Have too many fast-refreshes occurred consecutively, since last full refresh?
|
||||||
void EInkDynamicDisplay::checkConsecutiveFastRefreshes()
|
void EInkDynamicDisplay::checkConsecutiveFastRefreshes()
|
||||||
{
|
{
|
||||||
// If a decision was already reached, don't run the check
|
// If a decision was already reached, don't run the check
|
||||||
@@ -454,7 +454,7 @@ void EInkDynamicDisplay::checkExcessiveGhosting()
|
|||||||
void EInkDynamicDisplay::resetGhostPixelTracking()
|
void EInkDynamicDisplay::resetGhostPixelTracking()
|
||||||
{
|
{
|
||||||
// Copy the current frame into dirtyPixels[] from the display buffer
|
// Copy the current frame into dirtyPixels[] from the display buffer
|
||||||
memcpy(dirtyPixels, EInkDisplay::buffer, EInkDisplay::displayBufferSize);
|
memcpy(dirtyPixels.get(), EInkDisplay::buffer, EInkDisplay::displayBufferSize);
|
||||||
}
|
}
|
||||||
#endif // EINK_LIMIT_GHOSTING_PX
|
#endif // EINK_LIMIT_GHOSTING_PX
|
||||||
|
|
||||||
@@ -561,4 +561,4 @@ void EInkDynamicDisplay::awaitRefresh()
|
|||||||
}
|
}
|
||||||
#endif // HAS_EINK_ASYNCFULL
|
#endif // HAS_EINK_ASYNCFULL
|
||||||
|
|
||||||
#endif // USE_EINK_DYNAMICDISPLAY
|
#endif // USE_EINK_DYNAMICDISPLAY
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user