mirror of
https://github.com/meshtastic/firmware.git
synced 2025-12-28 05:30:30 +00:00
Compare commits
429 Commits
v2.1.23.04
...
v2.2.16.1c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c6acfd734 | ||
|
|
c6ae66dcaa | ||
|
|
fc365a1fee | ||
|
|
6c1db94ae7 | ||
|
|
ded1cbf4dd | ||
|
|
399b9f8f6d | ||
|
|
4720b2874f | ||
|
|
1af3e0ddaa | ||
|
|
9f85279e74 | ||
|
|
dd96848bec | ||
|
|
4932e277f1 | ||
|
|
dad05d7873 | ||
|
|
4b7fbeca29 | ||
|
|
2ebaea317a | ||
|
|
d14d2c89c3 | ||
|
|
35938392f1 | ||
|
|
d952da8b1e | ||
|
|
385b29c977 | ||
|
|
dc309f61e8 | ||
|
|
512399c8f5 | ||
|
|
5d94bb601a | ||
|
|
796592b586 | ||
|
|
d552ee3556 | ||
|
|
14b31d4d14 | ||
|
|
4de6eb2e1d | ||
|
|
abaa37133d | ||
|
|
5eac227550 | ||
|
|
671112f47d | ||
|
|
8ea19d665a | ||
|
|
8f57cfaaf4 | ||
|
|
a54e3826e9 | ||
|
|
9188a9a1f2 | ||
|
|
17f1a450b2 | ||
|
|
ba021c97b2 | ||
|
|
b4ad6b0f41 | ||
|
|
28502a762f | ||
|
|
89f0464233 | ||
|
|
46d02affe8 | ||
|
|
62329ad11f | ||
|
|
72b4fe51b1 | ||
|
|
07fc5df9c1 | ||
|
|
1c22d2c885 | ||
|
|
1f931a5e55 | ||
|
|
31c4693c66 | ||
|
|
6ff61b3e04 | ||
|
|
9e90b4af02 | ||
|
|
2544733ad4 | ||
|
|
1b6c11c5f1 | ||
|
|
4c69d06ac0 | ||
|
|
85cbde75fe | ||
|
|
5e70fb9851 | ||
|
|
6e967421a5 | ||
|
|
a05bab35ad | ||
|
|
ac506a581c | ||
|
|
def4ec5822 | ||
|
|
209fb585b0 | ||
|
|
fb89482129 | ||
|
|
8e742f2f80 | ||
|
|
238cf8cdeb | ||
|
|
5df7f07f95 | ||
|
|
6fa026a78b | ||
|
|
39743832ad | ||
|
|
bd2675caf1 | ||
|
|
c489c251ab | ||
|
|
14d03a2bda | ||
|
|
423b8ad603 | ||
|
|
ce8342d3e5 | ||
|
|
57227c0f85 | ||
|
|
1ca2923658 | ||
|
|
d10b1e1d00 | ||
|
|
d3e64350d9 | ||
|
|
102efd4954 | ||
|
|
18cf8ca4fa | ||
|
|
c7f6071f70 | ||
|
|
c7e3485dd7 | ||
|
|
603e564db3 | ||
|
|
ac318a9850 | ||
|
|
1feb74f525 | ||
|
|
d6fc1c314f | ||
|
|
b3852322ef | ||
|
|
cbb8eb65ba | ||
|
|
4712b1ca65 | ||
|
|
57542ce9e6 | ||
|
|
1b20a82b55 | ||
|
|
195706e0e5 | ||
|
|
c1f5878648 | ||
|
|
7380f3b170 | ||
|
|
a9d846c1b3 | ||
|
|
cfb09ee115 | ||
|
|
8f0ce606db | ||
|
|
d04ff29c2a | ||
|
|
8e92754b59 | ||
|
|
dad824c0e9 | ||
|
|
31d7c6826d | ||
|
|
d33521ee86 | ||
|
|
5ad12fed60 | ||
|
|
4af90eeb39 | ||
|
|
08297bb0b7 | ||
|
|
16ef40b21f | ||
|
|
7ef4abb974 | ||
|
|
297267d037 | ||
|
|
f8e766ebc7 | ||
|
|
7bd2b07024 | ||
|
|
b6ddbd0087 | ||
|
|
dc8903ec42 | ||
|
|
46bd6ca7ba | ||
|
|
9d4af1146e | ||
|
|
5ce6ca25f2 | ||
|
|
f2210d8f8d | ||
|
|
5d917885df | ||
|
|
e99ae64ece | ||
|
|
61f888e952 | ||
|
|
a144d5d6cc | ||
|
|
c3e3569c14 | ||
|
|
b1b5bafdda | ||
|
|
91e399a2b6 | ||
|
|
8b16367597 | ||
|
|
0b9accc3b6 | ||
|
|
590b0bbff4 | ||
|
|
19be230b24 | ||
|
|
8df16ad6a6 | ||
|
|
2d62f00ac3 | ||
|
|
9f93b9ab9d | ||
|
|
fc3200134d | ||
|
|
470264b7f9 | ||
|
|
600541ac25 | ||
|
|
298b383127 | ||
|
|
f57020412e | ||
|
|
4a6cc8fd8c | ||
|
|
45c5e0e730 | ||
|
|
527bffb7e0 | ||
|
|
4c35a7fb7d | ||
|
|
0f9936a0e0 | ||
|
|
40395bef01 | ||
|
|
8b8fffda81 | ||
|
|
4052194dfe | ||
|
|
b36ffe5200 | ||
|
|
a60b4d08bf | ||
|
|
227467f638 | ||
|
|
092e6f2424 | ||
|
|
e6b20bff77 | ||
|
|
2c625f6ba1 | ||
|
|
142d56c663 | ||
|
|
f4b40562d3 | ||
|
|
9e203532d0 | ||
|
|
8bd7b5e779 | ||
|
|
1af970765f | ||
|
|
def55ec063 | ||
|
|
092c6cac66 | ||
|
|
e39f129bd6 | ||
|
|
9d1fe8c245 | ||
|
|
786248a6b1 | ||
|
|
d1ac2dc6ea | ||
|
|
87396d9105 | ||
|
|
b388f8edcd | ||
|
|
10265aabd5 | ||
|
|
8780d93941 | ||
|
|
54f0c045e4 | ||
|
|
3ddad671a5 | ||
|
|
dc6f0b8e0b | ||
|
|
33f28c3d56 | ||
|
|
ef1d8c8eee | ||
|
|
950d5f0946 | ||
|
|
fc06754e1f | ||
|
|
fbf74fc0b2 | ||
|
|
7cebd79475 | ||
|
|
aa38f53aed | ||
|
|
2a6c8be684 | ||
|
|
37c3d15978 | ||
|
|
f301e236eb | ||
|
|
94c2ade272 | ||
|
|
a6e4402e41 | ||
|
|
50db2d0e9b | ||
|
|
5ecdbd0dbb | ||
|
|
47c6738c0d | ||
|
|
1552aa0081 | ||
|
|
6ebec8fcd9 | ||
|
|
e9215a5d70 | ||
|
|
5075849ec0 | ||
|
|
7f16b6b342 | ||
|
|
4e3576ae48 | ||
|
|
98290e5d7b | ||
|
|
ad529924f1 | ||
|
|
07d51a2ca4 | ||
|
|
47301a5ac0 | ||
|
|
0d023ea215 | ||
|
|
b5e952db24 | ||
|
|
a1c433748a | ||
|
|
04b2ab82dc | ||
|
|
e96ba7cbcf | ||
|
|
61f6fb22c5 | ||
|
|
db7b77c76e | ||
|
|
350090ec0d | ||
|
|
cdac643749 | ||
|
|
1a2c7f00e1 | ||
|
|
7eff5e7bcb | ||
|
|
17207681ef | ||
|
|
94f7c7e472 | ||
|
|
0a12d67d19 | ||
|
|
3175a3d630 | ||
|
|
4e9bf75340 | ||
|
|
e8970ad66b | ||
|
|
f737ee59ec | ||
|
|
6d211815d9 | ||
|
|
1bae926576 | ||
|
|
a1514b8b64 | ||
|
|
8b82ae6fe3 | ||
|
|
822c150e0d | ||
|
|
0731902744 | ||
|
|
b53cb38a09 | ||
|
|
b02dd0e964 | ||
|
|
c608f0ba81 | ||
|
|
e1839e33f2 | ||
|
|
d6d51bc3f4 | ||
|
|
44a77a10e1 | ||
|
|
8255128eae | ||
|
|
d7a98519f4 | ||
|
|
e256520336 | ||
|
|
fcf798df98 | ||
|
|
dcdf9b64de | ||
|
|
fd563e41f1 | ||
|
|
0fa3685161 | ||
|
|
899f9dd7bf | ||
|
|
9af4ecf48f | ||
|
|
cfb6a1394c | ||
|
|
1254031f7d | ||
|
|
c91e306659 | ||
|
|
4ff343b20f | ||
|
|
134fc75b67 | ||
|
|
fb23e479ac | ||
|
|
5a61695016 | ||
|
|
3bcab0e223 | ||
|
|
17617ce031 | ||
|
|
97f0c734e0 | ||
|
|
e943fffe8c | ||
|
|
ffcb131171 | ||
|
|
bb1fe7cad3 | ||
|
|
ad40493a39 | ||
|
|
ac62330e1c | ||
|
|
53f6a43661 | ||
|
|
7ad94da1c6 | ||
|
|
ecdb75aae0 | ||
|
|
7c98445ca3 | ||
|
|
b21368ecfa | ||
|
|
1a178c7d33 | ||
|
|
5d6f0ea6c4 | ||
|
|
5bd861f3d8 | ||
|
|
6d93fab495 | ||
|
|
6803fd7949 | ||
|
|
a61f969773 | ||
|
|
79cfc4b725 | ||
|
|
cf762bbd42 | ||
|
|
3d2c419d0d | ||
|
|
903f619609 | ||
|
|
2e3f762d3d | ||
|
|
a42266f74b | ||
|
|
a605c69eb4 | ||
|
|
282cc0b16a | ||
|
|
4ab67f3668 | ||
|
|
312028b161 | ||
|
|
ecd48db69c | ||
|
|
03dc36ea12 | ||
|
|
c2ae38405e | ||
|
|
2a1d8c40b4 | ||
|
|
e2441c425a | ||
|
|
00ffe73ebd | ||
|
|
3355019de3 | ||
|
|
5bb207d88b | ||
|
|
5453e4d123 | ||
|
|
7f1b58a222 | ||
|
|
39357b2686 | ||
|
|
d6b629ae04 | ||
|
|
7b1aeb60cd | ||
|
|
5c7c1cd253 | ||
|
|
8cfe130df3 | ||
|
|
feef86942d | ||
|
|
5f3a8b4924 | ||
|
|
0fcaaf39b0 | ||
|
|
a55eac5c20 | ||
|
|
b47c9c165a | ||
|
|
ecceb10910 | ||
|
|
6fc76103a0 | ||
|
|
f35c7be917 | ||
|
|
364364263b | ||
|
|
ef957bfac5 | ||
|
|
5d78795065 | ||
|
|
2dbdda204f | ||
|
|
4767bd5497 | ||
|
|
05efd68097 | ||
|
|
a90eef432f | ||
|
|
762166495f | ||
|
|
929b8f6209 | ||
|
|
5d76771fab | ||
|
|
91eb64d7b7 | ||
|
|
03fe4c629a | ||
|
|
144dfe9805 | ||
|
|
18899fd168 | ||
|
|
fcfd83bc89 | ||
|
|
a3d2b6166c | ||
|
|
fb5f2e48a5 | ||
|
|
d29c975e3c | ||
|
|
00ea6ef5ad | ||
|
|
c44986127e | ||
|
|
6e0b6684ee | ||
|
|
84ddfea491 | ||
|
|
3d6fb13f9a | ||
|
|
54c48329c8 | ||
|
|
ede31a2080 | ||
|
|
9470d4694b | ||
|
|
2074c76780 | ||
|
|
b5b66f43f2 | ||
|
|
9f6584bd67 | ||
|
|
dd69de9f32 | ||
|
|
616553ed72 | ||
|
|
1986267d65 | ||
|
|
1d0ac2caf7 | ||
|
|
746d7268a2 | ||
|
|
9a7777dbdb | ||
|
|
0fe99b0caa | ||
|
|
f9798b7dda | ||
|
|
23fb377fd7 | ||
|
|
4808a5c57f | ||
|
|
d25c368985 | ||
|
|
a5f3dea40b | ||
|
|
f026c3308c | ||
|
|
ed4e7a4cee | ||
|
|
95f67d70ea | ||
|
|
e3260c1d19 | ||
|
|
98f3be0665 | ||
|
|
dc31024764 | ||
|
|
5bbcb40f7a | ||
|
|
8218a729e0 | ||
|
|
cbc3e605dd | ||
|
|
94c41a4fed | ||
|
|
1afe9f75bd | ||
|
|
402f8ba524 | ||
|
|
0084c0881d | ||
|
|
8552cc44b9 | ||
|
|
1e71d346ae | ||
|
|
b238aebe38 | ||
|
|
e05c8e60d9 | ||
|
|
f1bcc300d9 | ||
|
|
11be856507 | ||
|
|
919b2d1e48 | ||
|
|
c4474a7b99 | ||
|
|
0821cff1c8 | ||
|
|
90ec8eae6c | ||
|
|
ba172aae32 | ||
|
|
26338b8f2b | ||
|
|
297708a50b | ||
|
|
a61a4fad3e | ||
|
|
c66b68b0cc | ||
|
|
04cba45c60 | ||
|
|
5aedd84c7d | ||
|
|
38c9a1ea07 | ||
|
|
0eefd0912f | ||
|
|
0cda8e6087 | ||
|
|
2cf648928a | ||
|
|
3cd7d8d6af | ||
|
|
702a83b525 | ||
|
|
32246850aa | ||
|
|
74650ca276 | ||
|
|
0141bbe772 | ||
|
|
049c587ca2 | ||
|
|
1a28225cd5 | ||
|
|
4fd756acd8 | ||
|
|
ff11506922 | ||
|
|
f35b422365 | ||
|
|
08f1ac785a | ||
|
|
146ed067a1 | ||
|
|
2e7c95a110 | ||
|
|
171cca435e | ||
|
|
3a24882e76 | ||
|
|
41b07de5a2 | ||
|
|
1745722dac | ||
|
|
849599cd8e | ||
|
|
e878f55ed3 | ||
|
|
bfc567ad89 | ||
|
|
b665786c77 | ||
|
|
e60a5f1cf2 | ||
|
|
6bdf67c9be | ||
|
|
bbfd62c47e | ||
|
|
a07e30544d | ||
|
|
9716bd8bec | ||
|
|
c2168dd450 | ||
|
|
66c71250b8 | ||
|
|
5c438ae792 | ||
|
|
cdf44ce7fa | ||
|
|
d70bd23260 | ||
|
|
b42ead4400 | ||
|
|
b2704a0082 | ||
|
|
9b94701699 | ||
|
|
61661aed50 | ||
|
|
3a25d6d3b3 | ||
|
|
fd4e9daa7f | ||
|
|
b6f7b7fa47 | ||
|
|
a92a960682 | ||
|
|
5d2ab65a81 | ||
|
|
f3b7f7251c | ||
|
|
2f60bbe5f2 | ||
|
|
0261754269 | ||
|
|
d74cbdaa8b | ||
|
|
3a5b79e4c1 | ||
|
|
9bee35118f | ||
|
|
e6fc2af21f | ||
|
|
ae41944a89 | ||
|
|
62259583e6 | ||
|
|
c5d87fe581 | ||
|
|
80f029aa32 | ||
|
|
b75aa79da5 | ||
|
|
49febc0d9d | ||
|
|
85818b8dfd | ||
|
|
7d299b06a7 | ||
|
|
14080d4667 | ||
|
|
8a806efb95 | ||
|
|
ef2d0cb830 | ||
|
|
5e779bfb33 | ||
|
|
498964e04e | ||
|
|
7d0bea267a | ||
|
|
ed1aa9ddb0 | ||
|
|
97a0b164be | ||
|
|
82706a961f | ||
|
|
06a1b079da | ||
|
|
56afed84df | ||
|
|
945fd7a05c | ||
|
|
e9a55fc296 | ||
|
|
472c43aace | ||
|
|
8b5937892b | ||
|
|
8c20fe5ec4 |
2
.github/pull_request_template.md
vendored
2
.github/pull_request_template.md
vendored
@@ -7,7 +7,7 @@
|
||||
is appreciated." This will allow other devs to potentially save you time by not accidentially duplicating work etc...
|
||||
- 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
|
||||
- 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) (WSL2 is required on windows),
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
45
.github/workflows/build_raspbian.yml
vendored
Normal file
45
.github/workflows/build_raspbian.yml
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
name: Build Raspbian
|
||||
|
||||
on: workflow_call
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
build-raspbian:
|
||||
runs-on: [self-hosted, linux, ARM64]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
ref: ${{github.event.pull_request.head.ref}}
|
||||
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||
|
||||
- name: Upgrade python tools
|
||||
shell: bash
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -U platformio adafruit-nrfutil
|
||||
pip install -U meshtastic --pre
|
||||
|
||||
- name: Upgrade platformio
|
||||
shell: bash
|
||||
run: |
|
||||
pio upgrade
|
||||
|
||||
- name: Build Raspbian
|
||||
run: bin/build-native.sh
|
||||
|
||||
- name: Get release version string
|
||||
run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
||||
id: version
|
||||
|
||||
- name: Store binaries as an artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: firmware-raspbian-${{ steps.version.outputs.version }}.zip
|
||||
path: |
|
||||
release/meshtasticd_linux_arm64
|
||||
bin/config-dist.yaml
|
||||
54
.github/workflows/main_matrix.yml
vendored
54
.github/workflows/main_matrix.yml
vendored
@@ -66,6 +66,7 @@ jobs:
|
||||
- board: tlora-v2-1-1_6
|
||||
- board: tlora-v2-1-1_8
|
||||
- board: tbeam
|
||||
- board: heltec-ht62-esp32c3-sx1262
|
||||
- board: heltec-v1
|
||||
- board: heltec-v2_0
|
||||
- board: heltec-v2_1
|
||||
@@ -95,6 +96,7 @@ jobs:
|
||||
- board: tlora-t3s3-v1
|
||||
- board: t-watch-s3
|
||||
- board: t-deck
|
||||
- board: picomputer-s3
|
||||
uses: ./.github/workflows/build_esp32_s3.yml
|
||||
with:
|
||||
board: ${{ matrix.board }}
|
||||
@@ -102,7 +104,6 @@ jobs:
|
||||
build-nrf52:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
max-parallel: 2
|
||||
matrix:
|
||||
include:
|
||||
- board: rak4631
|
||||
@@ -123,11 +124,22 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- board: pico
|
||||
- board: picow
|
||||
- board: rak11310
|
||||
- board: senselora_rp2040
|
||||
uses: ./.github/workflows/build_rpi2040.yml
|
||||
with:
|
||||
board: ${{ matrix.board }}
|
||||
|
||||
build-raspbian:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
max-parallel: 1
|
||||
uses: ./.github/workflows/build_raspbian.yml
|
||||
|
||||
package-raspbian:
|
||||
uses: ./.github/workflows/package_raspbian.yml
|
||||
|
||||
build-native:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -201,9 +213,20 @@ jobs:
|
||||
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||
|
||||
gather-artifacts:
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
[build-esp32, build-esp32-s3, build-nrf52, build-native, build-rpi2040]
|
||||
[
|
||||
build-esp32,
|
||||
build-esp32-s3,
|
||||
build-nrf52,
|
||||
build-raspbian,
|
||||
build-native,
|
||||
build-rpi2040,
|
||||
package-raspbian,
|
||||
]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
@@ -215,12 +238,15 @@ jobs:
|
||||
with:
|
||||
path: ./
|
||||
|
||||
- name: Display structure of downloaded files
|
||||
run: ls -R
|
||||
|
||||
- name: Get release version string
|
||||
run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
||||
id: version
|
||||
|
||||
- name: Move files up
|
||||
run: mv -b -t ./ ./*tbeam-2*/littlefs*.bin ./*tbeam-2*/bleota.bin ./*tbeam-s3*/bleota-s3.bin ./**/firmware*.bin ./*t-echo*/Meshtastic_nRF52_factory_erase.uf2 ./**/firmware-*.uf2 ./**/firmware-*-ota.zip ./**/*.elf ./*native*/*device-*.sh ./*native*/*device-*.bat
|
||||
run: mv -b -t ./ ./*tbeam-2*/littlefs*.bin ./*tbeam-2*/bleota.bin ./*tbeam-s3*/bleota-s3.bin ./**/firmware*.bin ./*t-echo*/Meshtastic_nRF52_factory_erase.uf2 ./**/firmware-*.uf2 ./**/firmware-*-ota.zip ./**/*.elf ./*native*/*device-*.sh ./*native*/*device-*.bat ./firmware-raspbian-*/release/meshtasticd_linux_arm64 ./firmware-raspbian-*/bin/config-dist.yaml
|
||||
|
||||
- name: Repackage in single firmware zip
|
||||
uses: actions/upload-artifact@v3
|
||||
@@ -232,6 +258,8 @@ jobs:
|
||||
./firmware-*-ota.zip
|
||||
./device-*.sh
|
||||
./device-*.bat
|
||||
./meshtasticd_linux_arm64
|
||||
./config-dist.yaml
|
||||
retention-days: 90
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
@@ -261,14 +289,13 @@ jobs:
|
||||
- name: Create request artifacts
|
||||
continue-on-error: true # FIXME: Why are we getting 502, but things still work?
|
||||
if: ${{ github.event_name == 'pull_request_target' || github.event_name == 'pull_request' }}
|
||||
uses: gavv/pull-request-artifacts@v1.1.0
|
||||
uses: gavv/pull-request-artifacts@v2.1.0
|
||||
with:
|
||||
commit: ${{ (github.event.pull_request_target || github.event.pull_request).head.sha }}
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
artifacts-token: ${{ secrets.ARTIFACTS_TOKEN }}
|
||||
artifacts-repo: meshtastic/artifacts
|
||||
artifacts-branch: device
|
||||
artifacts-dir: pr
|
||||
artifacts: ./firmware-${{ steps.version.outputs.version }}.zip
|
||||
|
||||
release-artifacts:
|
||||
@@ -293,6 +320,13 @@ jobs:
|
||||
name: firmware-${{ steps.version.outputs.version }}
|
||||
path: ./output
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: artifact-deb
|
||||
|
||||
- name: Display structure of downloaded files
|
||||
run: ls -R
|
||||
|
||||
- name: Device scripts permissions
|
||||
run: |
|
||||
chmod +x ./output/device-install.sh
|
||||
@@ -346,6 +380,16 @@ jobs:
|
||||
asset_name: debug-elfs-${{ steps.version.outputs.version }}.zip
|
||||
asset_content_type: application/zip
|
||||
|
||||
- name: Add raspbian .deb
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./meshtasticd_${{ steps.version.outputs.version }}_arm64.deb
|
||||
asset_name: meshtasticd_${{ steps.version.outputs.version }}_arm64.deb
|
||||
asset_content_type: application/vnd.debian.binary-package
|
||||
|
||||
- name: Bump version.properties
|
||||
run: >-
|
||||
bin/bump_version.py
|
||||
|
||||
62
.github/workflows/package_raspbian.yml
vendored
Normal file
62
.github/workflows/package_raspbian.yml
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
name: Package Raspbian
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
build-raspbian:
|
||||
uses: ./.github/workflows/build_raspbian.yml
|
||||
|
||||
package-raspbian:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build-raspbian
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
ref: ${{github.event.pull_request.head.ref}}
|
||||
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||
|
||||
- name: Get release version string
|
||||
run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
||||
id: version
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: firmware-raspbian-${{ steps.version.outputs.version }}.zip
|
||||
|
||||
- name: Display structure of downloaded files
|
||||
run: ls -R
|
||||
|
||||
- name: build .debpkg
|
||||
run: |
|
||||
mkdir -p .debpkg/usr/sbin
|
||||
mkdir -p .debpkg/etc/meshtasticd
|
||||
mkdir -p .debpkg/usr/lib/systemd/system/
|
||||
cp release/meshtasticd_linux_arm64 .debpkg/usr/sbin/meshtasticd
|
||||
cp bin/config-dist.yaml .debpkg/etc/meshtasticd/config.yaml
|
||||
chmod +x .debpkg/usr/sbin/meshtasticd
|
||||
cp bin/meshtasticd.service .debpkg/usr/lib/systemd/system/meshtasticd.service
|
||||
|
||||
- uses: jiro4989/build-deb-action@v3
|
||||
with:
|
||||
package: meshtasticd
|
||||
package_root: .debpkg
|
||||
maintainer: Jonathan Bennett
|
||||
version: ${{ steps.version.outputs.version }} # refs/tags/v*.*.*
|
||||
arch: arm64
|
||||
depends: libyaml-cpp0.7
|
||||
desc: Native Linux Meshtastic binary.
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: artifact-deb
|
||||
path: |
|
||||
./*.deb
|
||||
22
.github/workflows/trunk-check.yml
vendored
Normal file
22
.github/workflows/trunk-check.yml
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
name: Pull Request
|
||||
on: [pull_request]
|
||||
concurrency:
|
||||
group: ${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
trunk_check:
|
||||
name: Trunk Check Runner
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
checks: write # For trunk to post annotations
|
||||
contents: read # For repo checkout
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Trunk Check
|
||||
uses: trunk-io/trunk-action@v1
|
||||
2
.trunk/.gitignore
vendored
2
.trunk/.gitignore
vendored
@@ -2,7 +2,7 @@
|
||||
*logs
|
||||
*actions
|
||||
*notifications
|
||||
*tools
|
||||
plugins
|
||||
user_trunk.yaml
|
||||
user.yaml
|
||||
shims
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
enable=all
|
||||
source-path=SCRIPTDIR
|
||||
disable=SC2154
|
||||
disable=SC2248
|
||||
disable=SC2250
|
||||
|
||||
# If you're having issues with shellcheck following source, disable the errors via:
|
||||
# disable=SC1090
|
||||
# disable=SC1091
|
||||
#
|
||||
@@ -3,7 +3,7 @@ rules:
|
||||
required: only-when-needed
|
||||
extra-allowed: ["{|}"]
|
||||
empty-values:
|
||||
forbid-in-block-mappings: true
|
||||
forbid-in-block-mappings: false
|
||||
forbid-in-flow-mappings: true
|
||||
key-duplicates: {}
|
||||
octal-values:
|
||||
|
||||
@@ -1,48 +1,43 @@
|
||||
version: 0.1
|
||||
cli:
|
||||
version: 1.9.1
|
||||
version: 1.17.2
|
||||
plugins:
|
||||
sources:
|
||||
- id: trunk
|
||||
ref: v0.0.17
|
||||
ref: v1.3.0
|
||||
uri: https://github.com/trunk-io/plugins
|
||||
lint:
|
||||
enabled:
|
||||
- taplo@0.7.0
|
||||
- ruff@0.0.265
|
||||
- yamllint@1.31.0
|
||||
- bandit@1.7.5
|
||||
- checkov@3.1.9
|
||||
- terrascan@1.18.5
|
||||
- trivy@0.47.0
|
||||
#- trufflehog@3.63.2-rc0
|
||||
- taplo@0.8.1
|
||||
- ruff@0.1.6
|
||||
- isort@5.12.0
|
||||
- markdownlint@0.34.0
|
||||
- oxipng@8.0.0
|
||||
- svgo@3.0.2
|
||||
- actionlint@1.6.24
|
||||
- flake8@6.0.0
|
||||
- markdownlint@0.37.0
|
||||
- oxipng@9.0.0
|
||||
- svgo@3.0.5
|
||||
- actionlint@1.6.26
|
||||
- flake8@6.1.0
|
||||
- hadolint@2.12.0
|
||||
- shfmt@3.5.0
|
||||
- shfmt@3.6.0
|
||||
- shellcheck@0.9.0
|
||||
- black@23.3.0
|
||||
- black@23.9.1
|
||||
- git-diff-check
|
||||
- gitleaks@8.16.3
|
||||
- clang-format@14.0.0
|
||||
- prettier@2.8.8
|
||||
disabled:
|
||||
- taplo@0.7.0
|
||||
- shellcheck@0.9.0
|
||||
- shfmt@3.5.0
|
||||
- oxipng@8.0.0
|
||||
- actionlint@1.6.22
|
||||
- markdownlint@0.34.0
|
||||
- hadolint@2.12.0
|
||||
- svgo@3.0.2
|
||||
- gitleaks@8.18.1
|
||||
- clang-format@16.0.3
|
||||
- prettier@3.1.0
|
||||
runtimes:
|
||||
enabled:
|
||||
- python@3.10.8
|
||||
- go@1.19.5
|
||||
- go@1.21.0
|
||||
- node@18.12.1
|
||||
actions:
|
||||
disabled:
|
||||
- trunk-announce
|
||||
- trunk-check-pre-push
|
||||
- trunk-fmt-pre-commit
|
||||
enabled:
|
||||
- trunk-fmt-pre-commit
|
||||
- trunk-check-pre-push
|
||||
- trunk-upgrade-available
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
|
||||
This repository contains the device firmware for the Meshtastic project.
|
||||
|
||||
**[Building Instructions](https://meshtastic.org/docs/development/firmware/build)**
|
||||
**[Flashing Instructions](https://meshtastic.org/docs/getting-started/flashing-firmware/)**
|
||||
- **[Building Instructions](https://meshtastic.org/docs/development/firmware/build)**
|
||||
- **[Flashing Instructions](https://meshtastic.org/docs/getting-started/flashing-firmware/)**
|
||||
|
||||
## Stats
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
; Common settings for ESP targes, mixin with extends = esp32_base
|
||||
[esp32_base]
|
||||
extends = arduino_base
|
||||
platform = platformio/espressif32@^6.3.2
|
||||
platform = platformio/espressif32@6.3.2 # This is a temporary fix to the S3-based devices bluetooth issues until we can determine what within ESP-IDF changed and can develop a suitable patch.
|
||||
|
||||
build_src_filter =
|
||||
${arduino_base.build_src_filter} -<platform/nrf52/> -<platform/stm32wl> -<platform/rp2040> -<mesh/eth/>
|
||||
@@ -30,6 +30,7 @@ build_flags =
|
||||
-DCONFIG_BT_NIMBLE_MAX_CCCDS=20
|
||||
-DCONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=5120
|
||||
-DESP_OPENSSL_SUPPRESS_LEGACY_WARNING
|
||||
-DSERIAL_BUFFER_SIZE=4096
|
||||
;-DDEBUG_HEAP
|
||||
|
||||
lib_deps =
|
||||
@@ -37,8 +38,7 @@ lib_deps =
|
||||
${networking_base.lib_deps}
|
||||
${environmental_base.lib_deps}
|
||||
https://github.com/meshtastic/esp32_https_server.git#23665b3adc080a311dcbb586ed5941b5f94d6ea2
|
||||
h2zero/NimBLE-Arduino@^1.4.0
|
||||
jgromes/RadioLib@^6.1.0
|
||||
h2zero/NimBLE-Arduino@^1.4.1
|
||||
https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6
|
||||
https://github.com/meshtastic/ESP32_Codec2.git#633326c78ac251c059ab3a8c430fcdf25b41672f
|
||||
|
||||
|
||||
@@ -11,11 +11,10 @@ build_flags =
|
||||
-Isrc/platform/nrf52
|
||||
|
||||
build_src_filter =
|
||||
${arduino_base.build_src_filter} -<platform/esp32/> -<platform/stm32wl> -<nimble/> -<mesh/api/> -<mesh/http/> -<modules/esp32> -<platform/rp2040> -<mesh/eth/>
|
||||
${arduino_base.build_src_filter} -<platform/esp32/> -<platform/stm32wl> -<nimble/> -<mesh/wifi/> -<mesh/api/> -<mesh/http/> -<modules/esp32> -<platform/rp2040> -<mesh/eth/>
|
||||
|
||||
lib_deps=
|
||||
${arduino_base.lib_deps}
|
||||
jgromes/RadioLib@^6.1.0
|
||||
|
||||
lib_ignore =
|
||||
BluetoothOTA
|
||||
@@ -1,6 +1,6 @@
|
||||
; The Portduino based sim environment on top of any host OS, all hardware will be simulated
|
||||
[portduino_base]
|
||||
platform = https://github.com/meshtastic/platform-native.git#489ff929dca0bb768256ba2de45f95815111490f
|
||||
platform = https://github.com/meshtastic/platform-native.git#8a66ef82cf38a4135d85cbb5043d0e8ebbb8ba17
|
||||
framework = arduino
|
||||
|
||||
build_src_filter =
|
||||
@@ -10,6 +10,7 @@ build_src_filter =
|
||||
-<platform/nrf52/>
|
||||
-<platform/stm32wl/>
|
||||
-<platform/rp2040>
|
||||
-<mesh/wifi/>
|
||||
-<mesh/http/>
|
||||
-<mesh/eth/>
|
||||
-<modules/esp32>
|
||||
@@ -22,9 +23,9 @@ lib_deps =
|
||||
${env.lib_deps}
|
||||
${networking_base.lib_deps}
|
||||
rweather/Crypto@^0.4.0
|
||||
jgromes/RadioLib@6.1.0
|
||||
|
||||
build_flags =
|
||||
${arduino_base.build_flags}
|
||||
-fPIC
|
||||
-Isrc/platform/portduino
|
||||
-Isrc/platform/portduino
|
||||
-DRADIOLIB_EEPROM_UNSUPPORTED
|
||||
@@ -1,8 +1,8 @@
|
||||
; Common settings for rp2040 Processor based targets
|
||||
[rp2040_base]
|
||||
platform = https://github.com/maxgerhardt/platform-raspberrypi.git#0c33219f53faa035e188925ea1324f472e8b93d2
|
||||
platform = https://github.com/maxgerhardt/platform-raspberrypi.git#612de5399d68b359053f1307ed223d400aea975c
|
||||
extends = arduino_base
|
||||
platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#3.2.2
|
||||
platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#3.6.2
|
||||
|
||||
board_build.core = earlephilhower
|
||||
board_build.filesystem_size = 0.5m
|
||||
@@ -12,7 +12,7 @@ build_flags =
|
||||
-D__PLAT_RP2040__
|
||||
# -D _POSIX_THREADS
|
||||
build_src_filter =
|
||||
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<mesh/api/> -<mesh/http/> -<modules/esp32> -<platform/nrf52/> -<platform/stm32wl> -<mesh/eth/>
|
||||
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<modules/esp32> -<platform/nrf52/> -<platform/stm32wl> -<mesh/eth/> -<mesh/wifi/> -<mesh/http/>
|
||||
|
||||
lib_ignore =
|
||||
BluetoothOTA
|
||||
@@ -20,5 +20,4 @@ lib_ignore =
|
||||
lib_deps =
|
||||
${arduino_base.lib_deps}
|
||||
${environmental_base.lib_deps}
|
||||
jgromes/RadioLib@^6.1.0
|
||||
https://github.com/kokke/tiny-AES-c.git#f06ac37fc31dfdaca2e0d9bec83f90d5663c319b
|
||||
rweather/Crypto
|
||||
@@ -13,17 +13,16 @@ build_flags =
|
||||
-DVECT_TAB_OFFSET=0x08000000
|
||||
|
||||
build_src_filter =
|
||||
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<mesh/api/> -<mesh/http/> -<modules/esp32> -<mesh/eth/> -<input> -<buzz> -<modules/Telemetry> -<platform/nrf52> -<platform/portduino> -<platform/rp2040>
|
||||
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<mesh/api/> -<mesh/wifi/> -<mesh/http/> -<modules/esp32> -<mesh/eth/> -<input> -<buzz> -<modules/Telemetry> -<platform/nrf52> -<platform/portduino> -<platform/rp2040>
|
||||
|
||||
board_upload.offset_address = 0x08000000
|
||||
upload_protocol = stlink
|
||||
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
jgromes/RadioLib@^6.1.0
|
||||
https://github.com/kokke/tiny-AES-c.git#f06ac37fc31dfdaca2e0d9bec83f90d5663c319b
|
||||
https://github.com/littlefs-project/littlefs.git#v2.5.1
|
||||
https://github.com/stm32duino/STM32FreeRTOS.git#10.3.1
|
||||
|
||||
lib_ignore =
|
||||
https://github.com/mathertel/OneButton#2.1.0
|
||||
mathertel/OneButton
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
set -e
|
||||
|
||||
VERSION=`bin/buildinfo.py long`
|
||||
SHORT_VERSION=`bin/buildinfo.py short`
|
||||
VERSION=$(bin/buildinfo.py long)
|
||||
SHORT_VERSION=$(bin/buildinfo.py short)
|
||||
|
||||
OUTDIR=release/
|
||||
|
||||
@@ -13,11 +13,15 @@ mkdir -p $OUTDIR/
|
||||
rm -r $OUTDIR/* || true
|
||||
|
||||
# Important to pull latest version of libs into all device flavors, otherwise some devices might be stale
|
||||
platformio pkg update
|
||||
platformio pkg update
|
||||
|
||||
pio run --environment native
|
||||
cp .pio/build/native/program $OUTDIR/meshtasticd_linux_amd64
|
||||
if command -v raspi-config &>/dev/null; then
|
||||
pio run --environment raspbian
|
||||
cp .pio/build/raspbian/program $OUTDIR/meshtasticd_linux_arm64
|
||||
else
|
||||
pio run --environment native
|
||||
cp .pio/build/native/program $OUTDIR/meshtasticd_linux_amd64
|
||||
fi
|
||||
|
||||
cp bin/device-install.* $OUTDIR
|
||||
cp bin/device-update.* $OUTDIR
|
||||
|
||||
|
||||
@@ -4,23 +4,23 @@
|
||||
|
||||
set -e
|
||||
|
||||
VERSION=`bin/buildinfo.py long`
|
||||
VERSION=$(bin/buildinfo.py long)
|
||||
|
||||
# The shell vars the build tool expects to find
|
||||
export APP_VERSION=$VERSION
|
||||
|
||||
if [[ $# -gt 0 ]]; then
|
||||
# can override which environment by passing arg
|
||||
BOARDS="$@"
|
||||
# can override which environment by passing arg
|
||||
BOARDS="$@"
|
||||
else
|
||||
BOARDS="tlora-v2 tlora-v1 tlora_v1_3 tlora-v2-1-1.6 tbeam heltec-v1 heltec-v2.0 heltec-v2.1 tbeam0.7 meshtastic-diy-v1 rak4631 rak4631_eink rak11200 t-echo pca10059_diy_eink"
|
||||
BOARDS="tlora-v2 tlora-v1 tlora_v1_3 tlora-v2-1-1.6 tbeam heltec-v1 heltec-v2.0 heltec-v2.1 tbeam0.7 meshtastic-diy-v1 rak4631 rak4631_eink rak11200 t-echo pca10059_diy_eink"
|
||||
fi
|
||||
|
||||
echo "BOARDS:${BOARDS}"
|
||||
|
||||
CHECK=""
|
||||
for BOARD in $BOARDS; do
|
||||
CHECK="${CHECK} -e ${BOARD}"
|
||||
CHECK="${CHECK} -e ${BOARD}"
|
||||
done
|
||||
|
||||
pio check --flags "-DAPP_VERSION=${APP_VERSION} --suppressions-list=suppressions.txt" $CHECK --skip-packages --pattern="src/" --fail-on-defect=low --fail-on-defect=medium --fail-on-defect=high
|
||||
pio check --flags "-DAPP_VERSION=${APP_VERSION} --suppressions-list=suppressions.txt" $CHECK --skip-packages --pattern="src/" --fail-on-defect=medium --fail-on-defect=high
|
||||
|
||||
64
bin/config-dist.yaml
Normal file
64
bin/config-dist.yaml
Normal file
@@ -0,0 +1,64 @@
|
||||
### Define your devices here using Broadcom pin numbering
|
||||
### Uncomment the block that corresponds to your hardware
|
||||
---
|
||||
Lora:
|
||||
# Module: sx1262 # Waveshare SX126X XXXM
|
||||
# DIO2_AS_RF_SWITCH: true
|
||||
# CS: 21
|
||||
# IRQ: 16
|
||||
# Busy: 20
|
||||
# Reset: 18
|
||||
|
||||
# Module: sx1262 # Waveshare SX1302 LISTEN ONLY AT THIS TIME!
|
||||
# CS: 7
|
||||
# IRQ: 17
|
||||
# Reset: 22
|
||||
|
||||
# Module: RF95 # Adafruit RFM9x
|
||||
# Reset: 25
|
||||
# CS: 7
|
||||
# IRQ: 22
|
||||
# Busy: 23
|
||||
|
||||
# Module: RF95 # Elecrow Lora RFM95 IOT https://www.elecrow.com/lora-rfm95-iot-board-for-rpi.html
|
||||
# Reset: 22
|
||||
# CS: 7
|
||||
# IRQ: 25
|
||||
|
||||
### Set gpio chip to use in /dev/. Defaults to 0.
|
||||
### Notably the Raspberry Pi 5 puts the GPIO header on gpiochip4
|
||||
# gpiochip: 4
|
||||
|
||||
### Define GPIO buttons here:
|
||||
|
||||
GPIO:
|
||||
# User: 6
|
||||
|
||||
### Define GPS
|
||||
|
||||
GPS:
|
||||
# SerialPath: /dev/ttyS0
|
||||
|
||||
### Set up SPI displays here. Note that I2C displays are generally auto-detected.
|
||||
|
||||
Display:
|
||||
|
||||
### Waveshare 2.8inch RPi LCD
|
||||
# Panel: ST7789
|
||||
# CS: 8
|
||||
# DC: 22 # Data/Command pin
|
||||
# Backlight: 18
|
||||
# Width: 240
|
||||
# Height: 320
|
||||
# Reset: 27
|
||||
# Rotate: true
|
||||
|
||||
Touchscreen:
|
||||
# Module: XPT2046
|
||||
# CS: 7
|
||||
# IRQ: 17
|
||||
|
||||
### Configure device for direct keyboard input
|
||||
|
||||
Input:
|
||||
# KeyboardDevice: /dev/input/event0
|
||||
12
bin/meshtasticd.service
Normal file
12
bin/meshtasticd.service
Normal file
@@ -0,0 +1,12 @@
|
||||
[Unit]
|
||||
Description=Meshtastic Native Daemon
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
User=root
|
||||
Group=root
|
||||
Type=simple
|
||||
ExecStart=/usr/sbin/meshtasticd
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
10
bin/native-install.sh
Executable file
10
bin/native-install.sh
Executable file
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
cp release/meshtasticd_linux_arm64 /usr/sbin/meshtasticd
|
||||
mkdir /etc/meshtasticd
|
||||
if [[ -f "/etc/meshtasticd/config.yaml" ]]; then
|
||||
cp bin/config-dist.yaml /etc/meshtasticd/config-upgrade.yaml
|
||||
else
|
||||
cp bin/config-dist.yaml /etc/meshtasticd/config.yaml
|
||||
fi
|
||||
cp bin/meshtasticd.service /usr/lib/systemd/system/meshtasticd.service
|
||||
@@ -28,8 +28,6 @@
|
||||
"flash_size": "8MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 8388608,
|
||||
"use_1200bps_touch": true,
|
||||
"wait_for_upload_port": true,
|
||||
"require_upload_port": true,
|
||||
"speed": 921600
|
||||
},
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s3_out.ld"
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"memory_type": "qio_opi"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
@@ -13,7 +14,7 @@
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "dio",
|
||||
"flash_mode": "qio",
|
||||
"hwids": [["0x303A", "0x1001"]],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "t-deck"
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s3_out.ld"
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"memory_type": "qio_opi"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
@@ -14,8 +15,8 @@
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "dio",
|
||||
"hwids": [["0X303A", "0x1001"]],
|
||||
"flash_mode": "qio",
|
||||
"hwids": [["0x303A", "0x1001"]],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "t-watch-s3"
|
||||
},
|
||||
@@ -31,8 +32,9 @@
|
||||
"maximum_size": 8388608,
|
||||
"require_upload_port": true,
|
||||
"use_1200bps_touch": true,
|
||||
"wait_for_upload_port": true
|
||||
"wait_for_upload_port": true,
|
||||
"speed": 921600
|
||||
},
|
||||
"url": "http://www.lilygo.cn/",
|
||||
"url": "https://www.lilygo.cc/en-pl/products/t-watch-s3",
|
||||
"vendor": "LilyGo"
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "dio",
|
||||
"hwids": [["0X303A", "0x1001"]],
|
||||
"hwids": [["0x303A", "0x1001"]],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "tbeam-s3-core"
|
||||
},
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
;default_envs = meshtastic-dr-dev
|
||||
;default_envs = m5stack-coreink
|
||||
;default_envs = rak4631
|
||||
;default_envs = rak10701
|
||||
default_envs = wio-e5
|
||||
|
||||
extra_configs =
|
||||
@@ -67,10 +68,11 @@ build_flags = -Wno-missing-field-initializers
|
||||
monitor_speed = 115200
|
||||
|
||||
lib_deps =
|
||||
jgromes/RadioLib@^6.3.0
|
||||
https://github.com/meshtastic/esp8266-oled-ssd1306.git#b38094e03dfa964fbc0e799bc374e91a605c1223 ; ESP8266_SSD1306
|
||||
https://github.com/mathertel/OneButton#2.1.0 ; OneButton library for non-blocking button debounce
|
||||
mathertel/OneButton@^2.5.0 ; OneButton library for non-blocking button debounce
|
||||
https://github.com/meshtastic/arduino-fsm.git#7db3702bf0cfe97b783d6c72595e3f38e0b19159
|
||||
https://github.com/meshtastic/TinyGPSPlus.git#127ad674ef85f0201cb68a065879653ed94792c4
|
||||
https://github.com/meshtastic/TinyGPSPlus.git#076e8d2c8fb702d9be5b08c55b93ff76f8af7e61
|
||||
https://github.com/meshtastic/ArduinoThread.git#72921ac222eed6f526ba1682023cee290d9aa1b3
|
||||
nanopb/Nanopb@^0.4.7
|
||||
erriez/ErriezCRC32@^1.0.1
|
||||
@@ -92,7 +94,7 @@ lib_deps =
|
||||
end2endzone/NonBlockingRTTTL@^1.3.0
|
||||
https://github.com/meshtastic/SparkFun_ATECCX08a_Arduino_Library.git#5cf62b36c6f30bc72a07bdb2c11fc9a22d1e31da
|
||||
|
||||
build_flags = ${env.build_flags} -Os -DRADIOLIB_SPI_PARANOID=0
|
||||
build_flags = ${env.build_flags} -Os
|
||||
build_src_filter = ${env.build_src_filter} -<platform/portduino/>
|
||||
|
||||
; Common libs for communicating over TCP/IP networks such as MQTT
|
||||
@@ -113,6 +115,7 @@ lib_deps =
|
||||
https://github.com/boschsensortec/Bosch-BSEC2-Library#v1.5.2400
|
||||
boschsensortec/BME68x Sensor Library@^1.1.40407
|
||||
adafruit/Adafruit MCP9808 Library@^2.0.0
|
||||
https://github.com/Tinyu-Zhao/INA3221@^0.0.1
|
||||
adafruit/Adafruit INA260 Library@^1.5.0
|
||||
adafruit/Adafruit INA219@^1.2.0
|
||||
adafruit/Adafruit SHTC3 Library@^1.0.0
|
||||
@@ -121,4 +124,4 @@ lib_deps =
|
||||
adafruit/Adafruit PM25 AQI Sensor@^1.0.6
|
||||
adafruit/Adafruit MPU6050@^2.2.4
|
||||
adafruit/Adafruit LIS3DH@^1.2.4
|
||||
https://github.com/lewisxhe/BMA423_Library@^0.0.1
|
||||
https://github.com/lewisxhe/BMA423_Library@^0.0.1
|
||||
|
||||
Submodule protobufs updated: c5fa71fbb6...a34b2c680e
@@ -42,7 +42,7 @@ namespace concurrency
|
||||
class AccelerometerThread : public concurrency::OSThread
|
||||
{
|
||||
public:
|
||||
AccelerometerThread(ScanI2C::DeviceType type = ScanI2C::DeviceType::NONE) : OSThread("AccelerometerThread")
|
||||
explicit AccelerometerThread(ScanI2C::DeviceType type) : OSThread("AccelerometerThread")
|
||||
{
|
||||
if (accelerometer_found.port == ScanI2C::I2CPort::NO_I2C) {
|
||||
LOG_DEBUG("AccelerometerThread disabling due to no sensors found\n");
|
||||
|
||||
75
src/AmbientLightingThread.h
Normal file
75
src/AmbientLightingThread.h
Normal file
@@ -0,0 +1,75 @@
|
||||
#include "configuration.h"
|
||||
|
||||
#ifdef HAS_NCP5623
|
||||
#include <graphics/RAKled.h>
|
||||
NCP5623 rgb;
|
||||
#endif
|
||||
|
||||
namespace concurrency
|
||||
{
|
||||
class AmbientLightingThread : public concurrency::OSThread
|
||||
{
|
||||
public:
|
||||
explicit AmbientLightingThread(ScanI2C::DeviceType type) : OSThread("AmbientLightingThread")
|
||||
{
|
||||
// Uncomment to test module
|
||||
// moduleConfig.ambient_lighting.led_state = true;
|
||||
// moduleConfig.ambient_lighting.current = 10;
|
||||
// // Default to a color based on our node number
|
||||
// moduleConfig.ambient_lighting.red = (myNodeInfo.my_node_num & 0xFF0000) >> 16;
|
||||
// moduleConfig.ambient_lighting.green = (myNodeInfo.my_node_num & 0x00FF00) >> 8;
|
||||
// moduleConfig.ambient_lighting.blue = myNodeInfo.my_node_num & 0x0000FF;
|
||||
|
||||
#ifdef HAS_NCP5623
|
||||
_type = type;
|
||||
if (_type == ScanI2C::DeviceType::NONE) {
|
||||
LOG_DEBUG("AmbientLightingThread disabling due to no RGB leds found on I2C bus\n");
|
||||
disable();
|
||||
return;
|
||||
}
|
||||
if (!moduleConfig.ambient_lighting.led_state) {
|
||||
LOG_DEBUG("AmbientLightingThread disabling due to moduleConfig.ambient_lighting.led_state OFF\n");
|
||||
disable();
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG("AmbientLightingThread initializing\n");
|
||||
if (_type == ScanI2C::NCP5623) {
|
||||
rgb.begin();
|
||||
setLighting();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
protected:
|
||||
int32_t runOnce() override
|
||||
{
|
||||
#ifdef HAS_NCP5623
|
||||
if (_type == ScanI2C::NCP5623 && moduleConfig.ambient_lighting.led_state) {
|
||||
setLighting();
|
||||
return 30000; // 30 seconds to reset from any animations that may have been running from Ext. Notification
|
||||
} else {
|
||||
return disable();
|
||||
}
|
||||
#else
|
||||
return disable();
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
ScanI2C::DeviceType _type = ScanI2C::DeviceType::NONE;
|
||||
|
||||
void setLighting()
|
||||
{
|
||||
#ifdef HAS_NCP5623
|
||||
rgb.setCurrent(moduleConfig.ambient_lighting.current);
|
||||
rgb.setRed(moduleConfig.ambient_lighting.red);
|
||||
rgb.setGreen(moduleConfig.ambient_lighting.green);
|
||||
rgb.setBlue(moduleConfig.ambient_lighting.blue);
|
||||
LOG_DEBUG("Initializing Ambient lighting w/ current=%d, red=%d, green=%d, blue=%d\n",
|
||||
moduleConfig.ambient_lighting.current, moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green,
|
||||
moduleConfig.ambient_lighting.blue);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace concurrency
|
||||
77
src/AudioThread.h
Normal file
77
src/AudioThread.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#pragma once
|
||||
#include "PowerFSM.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
#include "sleep.h"
|
||||
|
||||
#ifdef HAS_I2S
|
||||
#include <AudioFileSourcePROGMEM.h>
|
||||
#include <AudioGeneratorRTTTL.h>
|
||||
#include <AudioOutputI2S.h>
|
||||
#include <ESP8266SAM.h>
|
||||
|
||||
#define AUDIO_THREAD_INTERVAL_MS 100
|
||||
|
||||
class AudioThread : public concurrency::OSThread
|
||||
{
|
||||
public:
|
||||
AudioThread() : OSThread("AudioThread") { initOutput(); }
|
||||
|
||||
void beginRttl(const void *data, uint32_t len)
|
||||
{
|
||||
setCPUFast(true);
|
||||
rtttlFile = new AudioFileSourcePROGMEM(data, len);
|
||||
i2sRtttl = new AudioGeneratorRTTTL();
|
||||
i2sRtttl->begin(rtttlFile, audioOut);
|
||||
}
|
||||
|
||||
bool isPlaying()
|
||||
{
|
||||
if (i2sRtttl != nullptr) {
|
||||
return i2sRtttl->isRunning() && i2sRtttl->loop();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
if (i2sRtttl != nullptr) {
|
||||
i2sRtttl->stop();
|
||||
delete i2sRtttl;
|
||||
i2sRtttl = nullptr;
|
||||
}
|
||||
if (rtttlFile != nullptr) {
|
||||
delete rtttlFile;
|
||||
rtttlFile = nullptr;
|
||||
}
|
||||
|
||||
setCPUFast(false);
|
||||
}
|
||||
|
||||
protected:
|
||||
int32_t runOnce() override
|
||||
{
|
||||
canSleep = true; // Assume we should not keep the board awake
|
||||
|
||||
// if (i2sRtttl != nullptr && i2sRtttl->isRunning()) {
|
||||
// i2sRtttl->loop();
|
||||
// }
|
||||
return AUDIO_THREAD_INTERVAL_MS;
|
||||
}
|
||||
|
||||
private:
|
||||
void initOutput()
|
||||
{
|
||||
audioOut = new AudioOutputI2S(1, AudioOutputI2S::EXTERNAL_I2S);
|
||||
audioOut->SetPinout(DAC_I2S_BCK, DAC_I2S_WS, DAC_I2S_DOUT);
|
||||
audioOut->SetGain(0.2);
|
||||
};
|
||||
|
||||
AudioGeneratorRTTTL *i2sRtttl = nullptr;
|
||||
AudioOutputI2S *audioOut;
|
||||
|
||||
AudioFileSourcePROGMEM *rtttlFile;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "configuration.h"
|
||||
#include "graphics/Screen.h"
|
||||
#include "main.h"
|
||||
#include "modules/ExternalNotificationModule.h"
|
||||
#include "power.h"
|
||||
#include <OneButton.h>
|
||||
|
||||
@@ -36,6 +37,9 @@ class ButtonThread : public concurrency::OSThread
|
||||
#endif
|
||||
#ifdef BUTTON_PIN_TOUCH
|
||||
OneButton userButtonTouch;
|
||||
#endif
|
||||
#if defined(ARCH_RASPBERRY_PI)
|
||||
OneButton userButton;
|
||||
#endif
|
||||
static bool shutdown_on_long_stop;
|
||||
|
||||
@@ -45,8 +49,14 @@ class ButtonThread : public concurrency::OSThread
|
||||
// callback returns the period for the next callback invocation (or 0 if we should no longer be called)
|
||||
ButtonThread() : OSThread("Button")
|
||||
{
|
||||
#ifdef BUTTON_PIN
|
||||
#if defined(ARCH_RASPBERRY_PI) || defined(BUTTON_PIN)
|
||||
#if defined(ARCH_RASPBERRY_PI)
|
||||
if (settingsMap.count(user) != 0 && settingsMap[user] != RADIOLIB_NC)
|
||||
userButton = OneButton(settingsMap[user], true, true);
|
||||
#elif defined(BUTTON_PIN)
|
||||
|
||||
userButton = OneButton(config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN, true, true);
|
||||
#endif
|
||||
#ifdef INPUT_PULLUP_SENSE
|
||||
// Some platforms (nrf52) have a SENSE variant which allows wake from sleep - override what OneButton did
|
||||
pinMode(config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN, INPUT_PULLUP_SENSE);
|
||||
@@ -58,8 +68,13 @@ class ButtonThread : public concurrency::OSThread
|
||||
userButton.attachMultiClick(userButtonMultiPressed);
|
||||
userButton.attachLongPressStart(userButtonPressedLongStart);
|
||||
userButton.attachLongPressStop(userButtonPressedLongStop);
|
||||
#if defined(ARCH_RASPBERRY_PI)
|
||||
if (settingsMap.count(user) != 0 && settingsMap[user] != RADIOLIB_NC)
|
||||
wakeOnIrq(settingsMap[user], FALLING);
|
||||
#else
|
||||
wakeOnIrq(config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN, FALLING);
|
||||
#endif
|
||||
#endif
|
||||
#ifdef BUTTON_PIN_ALT
|
||||
userButtonAlt = OneButton(BUTTON_PIN_ALT, true, true);
|
||||
#ifdef INPUT_PULLUP_SENSE
|
||||
@@ -87,9 +102,14 @@ class ButtonThread : public concurrency::OSThread
|
||||
{
|
||||
canSleep = true; // Assume we should not keep the board awake
|
||||
|
||||
#ifdef BUTTON_PIN
|
||||
#if defined(BUTTON_PIN)
|
||||
userButton.tick();
|
||||
canSleep &= userButton.isIdle();
|
||||
#elif defined(ARCH_RASPBERRY_PI)
|
||||
if (settingsMap.count(user) != 0 && settingsMap[user] != RADIOLIB_NC) {
|
||||
userButton.tick();
|
||||
canSleep &= userButton.isIdle();
|
||||
}
|
||||
#endif
|
||||
#ifdef BUTTON_PIN_ALT
|
||||
userButtonAlt.tick();
|
||||
@@ -121,6 +141,13 @@ class ButtonThread : public concurrency::OSThread
|
||||
!moduleConfig.canned_message.enabled) {
|
||||
powerFSM.trigger(EVENT_PRESS);
|
||||
}
|
||||
#endif
|
||||
#if defined(ARCH_RASPBERRY_PI)
|
||||
if ((settingsMap.count(user) != 0 && settingsMap[user] != RADIOLIB_NC) &&
|
||||
(settingsMap[user] != moduleConfig.canned_message.inputbroker_pin_press) ||
|
||||
!moduleConfig.canned_message.enabled) {
|
||||
powerFSM.trigger(EVENT_PRESS);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
static void userButtonPressedLong()
|
||||
@@ -164,19 +191,27 @@ class ButtonThread : public concurrency::OSThread
|
||||
|
||||
static void userButtonMultiPressed()
|
||||
{
|
||||
#if defined(GPS_POWER_TOGGLE)
|
||||
if (config.position.gps_enabled) {
|
||||
LOG_DEBUG("Flag set to false for gps power\n");
|
||||
} else {
|
||||
LOG_DEBUG("Flag set to true to restore power\n");
|
||||
if (!config.device.disable_triple_click && (gps != nullptr)) {
|
||||
config.position.gps_enabled = !(config.position.gps_enabled);
|
||||
if (config.position.gps_enabled) {
|
||||
LOG_DEBUG("Flag set to true to restore power\n");
|
||||
gps->enable();
|
||||
|
||||
} else {
|
||||
LOG_DEBUG("Flag set to false for gps power\n");
|
||||
gps->disable();
|
||||
}
|
||||
}
|
||||
config.position.gps_enabled = !(config.position.gps_enabled);
|
||||
doGPSpowersave(config.position.gps_enabled);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void userButtonPressedLongStart()
|
||||
{
|
||||
#ifdef T_DECK
|
||||
// False positive long-press triggered on T-Deck with i2s audio, so short circuit
|
||||
if (moduleConfig.external_notification.enabled && (externalNotificationModule->nagCycleCutoff != UINT32_MAX)) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (millis() > 30 * 1000) {
|
||||
LOG_DEBUG("Long press start!\n");
|
||||
longPressTime = millis();
|
||||
@@ -197,4 +232,4 @@ class ButtonThread : public concurrency::OSThread
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace concurrency
|
||||
} // namespace concurrency
|
||||
|
||||
34
src/DisplayFormatters.cpp
Normal file
34
src/DisplayFormatters.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#include "DisplayFormatters.h"
|
||||
|
||||
const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName)
|
||||
{
|
||||
switch (preset) {
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW:
|
||||
return useShortName ? "ShortS" : "ShortSlow";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST:
|
||||
return useShortName ? "ShortF" : "ShortFast";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW:
|
||||
return useShortName ? "MedS" : "MediumSlow";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST:
|
||||
return useShortName ? "MedF" : "MediumFast";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW:
|
||||
return useShortName ? "LongS" : "LongSlow";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST:
|
||||
return useShortName ? "LongF" : "LongFast";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE:
|
||||
return useShortName ? "LongM" : "LongMod";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_VERY_LONG_SLOW:
|
||||
return useShortName ? "VeryL" : "VLongSlow";
|
||||
break;
|
||||
default:
|
||||
return useShortName ? "Custom" : "Invalid";
|
||||
break;
|
||||
}
|
||||
}
|
||||
8
src/DisplayFormatters.h
Normal file
8
src/DisplayFormatters.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include "NodeDB.h"
|
||||
|
||||
class DisplayFormatters
|
||||
{
|
||||
public:
|
||||
static const char *getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName);
|
||||
};
|
||||
@@ -16,8 +16,13 @@
|
||||
#include "buzz/buzz.h"
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
#include "meshUtils.h"
|
||||
#include "sleep.h"
|
||||
#include "utils.h"
|
||||
|
||||
// Working USB detection for powered/charging states on the RAK platform
|
||||
#ifdef NRF_APM
|
||||
#include "nrfx_power.h"
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_HEAP_MQTT
|
||||
#include "mqtt/MQTT.h"
|
||||
@@ -52,6 +57,7 @@ static const adc_atten_t atten = ADC_ATTENUATION;
|
||||
#if HAS_TELEMETRY && !defined(ARCH_PORTDUINO)
|
||||
INA260Sensor ina260Sensor;
|
||||
INA219Sensor ina219Sensor;
|
||||
INA3221Sensor ina3221Sensor;
|
||||
#endif
|
||||
|
||||
#ifdef HAS_PMU
|
||||
@@ -175,9 +181,21 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
||||
uint32_t raw = 0;
|
||||
#ifdef ARCH_ESP32
|
||||
#ifndef BAT_MEASURE_ADC_UNIT // ADC1
|
||||
#ifdef ADC_CTRL
|
||||
if (heltec_version == 5) {
|
||||
pinMode(ADC_CTRL, OUTPUT);
|
||||
digitalWrite(ADC_CTRL, HIGH);
|
||||
delay(10);
|
||||
}
|
||||
#endif
|
||||
for (int i = 0; i < BATTERY_SENSE_SAMPLES; i++) {
|
||||
raw += adc1_get_raw(adc_channel);
|
||||
}
|
||||
#ifdef ADC_CTRL
|
||||
if (heltec_version == 5) {
|
||||
digitalWrite(ADC_CTRL, LOW);
|
||||
}
|
||||
#endif
|
||||
#else // ADC2
|
||||
int32_t adc_buf = 0;
|
||||
for (int i = 0; i < BATTERY_SENSE_SAMPLES; i++) {
|
||||
@@ -221,10 +239,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
||||
/**
|
||||
* return true if there is a battery installed in this unit
|
||||
*/
|
||||
virtual bool isBatteryConnect() override
|
||||
{
|
||||
return getBatteryPercent() != -1;
|
||||
}
|
||||
virtual bool isBatteryConnect() override { return getBatteryPercent() != -1; }
|
||||
|
||||
/// If we see a battery voltage higher than physics allows - assume charger is pumping
|
||||
/// in power
|
||||
@@ -245,10 +260,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
||||
|
||||
/// Assume charging if we have a battery and external power is connected.
|
||||
/// we can't be smart enough to say 'full'?
|
||||
virtual bool isCharging() override
|
||||
{
|
||||
return isBatteryConnect() && isVbusIn();
|
||||
}
|
||||
virtual bool isCharging() override { return isBatteryConnect() && isVbusIn(); }
|
||||
|
||||
private:
|
||||
/// If we see a battery voltage higher than physics allows - assume charger is pumping
|
||||
@@ -275,10 +287,14 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
||||
#if defined(HAS_TELEMETRY) && !defined(ARCH_PORTDUINO)
|
||||
uint16_t getINAVoltage()
|
||||
{
|
||||
if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219] == config.power.device_battery_ina_address) {
|
||||
if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219].first == config.power.device_battery_ina_address) {
|
||||
return ina219Sensor.getBusVoltageMv();
|
||||
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260] == config.power.device_battery_ina_address) {
|
||||
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260].first ==
|
||||
config.power.device_battery_ina_address) {
|
||||
return ina260Sensor.getBusVoltageMv();
|
||||
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA3221].first ==
|
||||
config.power.device_battery_ina_address) {
|
||||
return ina3221Sensor.getBusVoltageMv();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -288,11 +304,12 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
||||
if (!config.power.device_battery_ina_address) {
|
||||
return false;
|
||||
}
|
||||
if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219] == config.power.device_battery_ina_address) {
|
||||
if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219].first == config.power.device_battery_ina_address) {
|
||||
if (!ina219Sensor.isInitialized())
|
||||
return ina219Sensor.runOnce() > 0;
|
||||
return ina219Sensor.isRunning();
|
||||
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260] == config.power.device_battery_ina_address) {
|
||||
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260].first ==
|
||||
config.power.device_battery_ina_address) {
|
||||
if (!ina260Sensor.isInitialized())
|
||||
return ina260Sensor.runOnce() > 0;
|
||||
return ina260Sensor.isRunning();
|
||||
@@ -385,11 +402,8 @@ bool Power::analogInit()
|
||||
*/
|
||||
bool Power::setup()
|
||||
{
|
||||
bool found = axpChipInit();
|
||||
bool found = axpChipInit() || analogInit();
|
||||
|
||||
if (!found) {
|
||||
found = analogInit();
|
||||
}
|
||||
enabled = found;
|
||||
low_voltage_counter = 0;
|
||||
|
||||
@@ -418,9 +432,9 @@ void Power::shutdown()
|
||||
ledOff(PIN_LED2);
|
||||
#endif
|
||||
#ifdef PIN_LED3
|
||||
ledOff(PIN_LED2);
|
||||
ledOff(PIN_LED3);
|
||||
#endif
|
||||
doDeepSleep(DELAY_FOREVER);
|
||||
doDeepSleep(DELAY_FOREVER, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -448,10 +462,25 @@ void Power::readPowerStatus()
|
||||
}
|
||||
}
|
||||
|
||||
OptionalBool NRF_USB = OptFalse;
|
||||
|
||||
#ifdef NRF_APM // Section of code detects USB power on the RAK4631 and updates the power states. Takes 20 seconds or so to detect
|
||||
// changes.
|
||||
|
||||
nrfx_power_usb_state_t nrf_usb_state = nrfx_power_usbstatus_get();
|
||||
|
||||
if (nrf_usb_state == NRFX_POWER_USB_STATE_DISCONNECTED) {
|
||||
powerFSM.trigger(EVENT_POWER_DISCONNECTED);
|
||||
NRF_USB = OptFalse;
|
||||
} else {
|
||||
powerFSM.trigger(EVENT_POWER_CONNECTED);
|
||||
NRF_USB = OptTrue;
|
||||
}
|
||||
#endif
|
||||
// Notify any status instances that are observing us
|
||||
const PowerStatus powerStatus2 =
|
||||
PowerStatus(hasBattery ? OptTrue : OptFalse, batteryLevel->isVbusIn() ? OptTrue : OptFalse,
|
||||
batteryLevel->isCharging() ? OptTrue : OptFalse, batteryVoltageMv, batteryChargePercent);
|
||||
const PowerStatus powerStatus2 = PowerStatus(
|
||||
hasBattery ? OptTrue : OptFalse, batteryLevel->isVbusIn() || NRF_USB == OptTrue ? OptTrue : OptFalse,
|
||||
batteryLevel->isCharging() || NRF_USB == OptTrue ? OptTrue : OptFalse, batteryVoltageMv, batteryChargePercent);
|
||||
LOG_DEBUG("Battery: usbPower=%d, isCharging=%d, batMv=%d, batPct=%d\n", powerStatus2.getHasUSB(),
|
||||
powerStatus2.getIsCharging(), powerStatus2.getBatteryVoltageMv(), powerStatus2.getBatteryChargePercent());
|
||||
newStatus.notifyObservers(&powerStatus2);
|
||||
@@ -865,4 +894,4 @@ bool Power::axpChipInit()
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
* actions to be taken upon entering or exiting each state.
|
||||
*/
|
||||
#include "PowerFSM.h"
|
||||
#include "GPS.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "configuration.h"
|
||||
@@ -46,7 +45,7 @@ static void sdsEnter()
|
||||
{
|
||||
LOG_DEBUG("Enter state: SDS\n");
|
||||
// FIXME - make sure GPS and LORA radio are off first - because we want close to zero current draw
|
||||
doDeepSleep(getConfiguredOrDefaultMs(config.power.sds_secs));
|
||||
doDeepSleep(getConfiguredOrDefaultMs(config.power.sds_secs), false);
|
||||
}
|
||||
|
||||
extern Power *power;
|
||||
@@ -137,9 +136,6 @@ static void lsIdle()
|
||||
static void lsExit()
|
||||
{
|
||||
LOG_INFO("Exit state: LS\n");
|
||||
// setGPSPower(true); // restore GPS power
|
||||
if (gps)
|
||||
gps->forceWake(true);
|
||||
}
|
||||
|
||||
static void nbEnter()
|
||||
@@ -177,7 +173,7 @@ static void serialExit()
|
||||
|
||||
static void powerEnter()
|
||||
{
|
||||
LOG_DEBUG("Enter state: POWER\n");
|
||||
// LOG_DEBUG("Enter state: POWER\n");
|
||||
if (!isPowered()) {
|
||||
// If we got here, we are in the wrong state - we should be in powered, let that state ahndle things
|
||||
LOG_INFO("Loss of power in Powered\n");
|
||||
@@ -249,6 +245,8 @@ Fsm powerFSM(&stateBOOT);
|
||||
void PowerFSM_setup()
|
||||
{
|
||||
bool isRouter = (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ? 1 : 0);
|
||||
bool isTrackerOrSensor = config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER ||
|
||||
config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR;
|
||||
bool hasPower = isPowered();
|
||||
|
||||
LOG_INFO("PowerFSM init, USB power=%d\n", hasPower ? 1 : 0);
|
||||
@@ -297,7 +295,8 @@ void PowerFSM_setup()
|
||||
powerFSM.add_transition(&stateLS, &stateON, EVENT_INPUT, NULL, "Input Device");
|
||||
powerFSM.add_transition(&stateNB, &stateON, EVENT_INPUT, NULL, "Input Device");
|
||||
powerFSM.add_transition(&stateDARK, &stateON, EVENT_INPUT, NULL, "Input Device");
|
||||
powerFSM.add_transition(&stateON, &stateON, EVENT_INPUT, NULL, "Input Device"); // restarts the sleep timer
|
||||
powerFSM.add_transition(&stateON, &stateON, EVENT_INPUT, NULL, "Input Device"); // restarts the sleep timer
|
||||
powerFSM.add_transition(&statePOWER, &statePOWER, EVENT_INPUT, NULL, "Input Device"); // restarts the sleep timer
|
||||
|
||||
powerFSM.add_transition(&stateDARK, &stateON, EVENT_BLUETOOTH_PAIR, NULL, "Bluetooth pairing");
|
||||
powerFSM.add_transition(&stateON, &stateON, EVENT_BLUETOOTH_PAIR, NULL, "Bluetooth pairing");
|
||||
@@ -351,12 +350,12 @@ void PowerFSM_setup()
|
||||
getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs), NULL,
|
||||
"Screen-on timeout");
|
||||
|
||||
// We never enter light-sleep or NB states on NRF52 (because the CPU uses so little power normally)
|
||||
#ifdef ARCH_ESP32
|
||||
State *lowPowerState = &stateLS;
|
||||
// We never enter light-sleep or NB states on NRF52 (because the CPU uses so little power normally)
|
||||
|
||||
// See: https://github.com/meshtastic/firmware/issues/1071
|
||||
if (isRouter || config.power.is_power_saving) {
|
||||
// Don't add power saving transitions if we are a power saving tracker or sensor. Sleep will be initiatiated through the
|
||||
// modules
|
||||
if ((isRouter || config.power.is_power_saving) && !isTrackerOrSensor) {
|
||||
powerFSM.add_timed_transition(&stateNB, &stateLS,
|
||||
getConfiguredOrDefaultMs(config.power.min_wake_secs, default_min_wake_secs), NULL,
|
||||
"Min wake timeout");
|
||||
@@ -364,10 +363,6 @@ void PowerFSM_setup()
|
||||
getConfiguredOrDefaultMs(config.power.wait_bluetooth_secs, default_wait_bluetooth_secs),
|
||||
NULL, "Bluetooth timeout");
|
||||
}
|
||||
|
||||
if (config.power.sds_secs != UINT32_MAX)
|
||||
powerFSM.add_timed_transition(lowPowerState, &stateSDS, getConfiguredOrDefaultMs(config.power.sds_secs), NULL,
|
||||
"mesh timeout");
|
||||
#endif
|
||||
|
||||
powerFSM.run_machine(); // run one iteration of the state machine, so we run our on enter tasks for the initial DARK state
|
||||
|
||||
@@ -21,7 +21,7 @@ class PowerFSMThread : public OSThread
|
||||
|
||||
/// If we are in power state we force the CPU to wake every 10ms to check for serial characters (we don't yet wake
|
||||
/// cpu for serial rx - FIXME)
|
||||
auto state = powerFSM.getState();
|
||||
const State *state = powerFSM.getState();
|
||||
canSleep = (state != &statePOWER) && (state != &stateSERIAL);
|
||||
|
||||
if (powerStatus->getHasUSB()) {
|
||||
@@ -33,7 +33,7 @@ class PowerFSMThread : public OSThread
|
||||
powerFSM.trigger(EVENT_SHUTDOWN);
|
||||
}
|
||||
|
||||
return 10;
|
||||
return 100;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -18,6 +18,12 @@ NoopPrint noopPrint;
|
||||
#if HAS_WIFI || HAS_ETHERNET
|
||||
extern Syslog syslog;
|
||||
#endif
|
||||
void RedirectablePrint::rpInit()
|
||||
{
|
||||
#ifdef HAS_FREE_RTOS
|
||||
inDebugPrint = xSemaphoreCreateMutexStatic(&this->_MutexStorageSpace);
|
||||
#endif
|
||||
}
|
||||
|
||||
void RedirectablePrint::setDestination(Print *_dest)
|
||||
{
|
||||
@@ -66,9 +72,12 @@ size_t RedirectablePrint::log(const char *logLevel, const char *format, ...)
|
||||
return 0;
|
||||
}
|
||||
size_t r = 0;
|
||||
|
||||
#ifdef HAS_FREE_RTOS
|
||||
if (inDebugPrint != nullptr && xSemaphoreTake(inDebugPrint, portMAX_DELAY) == pdTRUE) {
|
||||
#else
|
||||
if (!inDebugPrint) {
|
||||
inDebugPrint = true;
|
||||
#endif
|
||||
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
@@ -141,7 +150,11 @@ size_t RedirectablePrint::log(const char *logLevel, const char *format, ...)
|
||||
va_end(arg);
|
||||
|
||||
isContinuationMessage = !hasNewline;
|
||||
#ifdef HAS_FREE_RTOS
|
||||
xSemaphoreGive(inDebugPrint);
|
||||
#else
|
||||
inDebugPrint = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
return r;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../freertosinc.h"
|
||||
#include <Print.h>
|
||||
#include <stdarg.h>
|
||||
#include <string>
|
||||
@@ -16,14 +17,19 @@ class RedirectablePrint : public Print
|
||||
/// Used to allow multiple logDebug messages to appear on a single log line
|
||||
bool isContinuationMessage = false;
|
||||
|
||||
#ifdef HAS_FREE_RTOS
|
||||
SemaphoreHandle_t inDebugPrint = nullptr;
|
||||
StaticSemaphore_t _MutexStorageSpace;
|
||||
#else
|
||||
volatile bool inDebugPrint = false;
|
||||
|
||||
#endif
|
||||
public:
|
||||
explicit RedirectablePrint(Print *_dest) : dest(_dest) {}
|
||||
|
||||
/**
|
||||
* Set a new destination
|
||||
*/
|
||||
void rpInit();
|
||||
void setDestination(Print *dest);
|
||||
|
||||
virtual size_t write(uint8_t c);
|
||||
@@ -54,4 +60,4 @@ class NoopPrint : public Print
|
||||
/**
|
||||
* A printer that doesn't go anywhere
|
||||
*/
|
||||
extern NoopPrint noopPrint;
|
||||
extern NoopPrint noopPrint;
|
||||
@@ -12,6 +12,7 @@ SerialConsole *console;
|
||||
void consoleInit()
|
||||
{
|
||||
new SerialConsole(); // Must be dynamically allocated because we are now inheriting from thread
|
||||
DEBUG_PORT.rpInit(); // Simply sets up semaphore
|
||||
}
|
||||
|
||||
void consolePrintf(const char *format, ...)
|
||||
|
||||
@@ -53,7 +53,7 @@ class OSThread : public Thread
|
||||
|
||||
static void setup();
|
||||
|
||||
int32_t disable();
|
||||
virtual int32_t disable();
|
||||
|
||||
/**
|
||||
* Wait a specified number msecs starting from the current time (rather than the last time we were run)
|
||||
@@ -67,6 +67,7 @@ class OSThread : public Thread
|
||||
* Returns desired period for next invocation (or RUN_SAME for no change)
|
||||
*/
|
||||
virtual int32_t runOnce() = 0;
|
||||
bool sleepOnNextExecution = false;
|
||||
|
||||
// Do not override this
|
||||
virtual void run();
|
||||
@@ -87,4 +88,4 @@ extern bool hasBeenSetup;
|
||||
|
||||
void assertIsSetup();
|
||||
|
||||
} // namespace concurrency
|
||||
} // namespace concurrency
|
||||
@@ -57,8 +57,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define REQUIRE_RADIO true // If true, we will fail to start if the radio is not found
|
||||
|
||||
/// Convert a preprocessor name into a quoted string
|
||||
#define xstr(s) str(s)
|
||||
#define str(s) #s
|
||||
#define xstr(s) ystr(s)
|
||||
#define ystr(s) #s
|
||||
|
||||
/// Convert a preprocessor name into a quoted string and if that string is empty use "unset"
|
||||
#define optstr(s) (xstr(s)[0] ? xstr(s) : "unset")
|
||||
@@ -101,6 +101,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
// I2C Keyboards (M5Stack, RAK14004, T-Deck)
|
||||
#define CARDKB_ADDR 0x5F
|
||||
#define TDECK_KB_ADDR 0x55
|
||||
#define BBQ10_KB_ADDR 0x1F
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// SENSOR
|
||||
@@ -110,6 +111,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define MCP9808_ADDR 0x18
|
||||
#define INA_ADDR 0x40
|
||||
#define INA_ADDR_ALTERNATE 0x41
|
||||
#define INA3221_ADDR 0x42
|
||||
#define QMC6310_ADDR 0x1C
|
||||
#define QMI8658_ADDR 0x6B
|
||||
#define QMC5883L_ADDR 0x1E
|
||||
@@ -144,9 +146,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define GPS_BAUDRATE 9600
|
||||
|
||||
#ifndef GPS_THREAD_INTERVAL
|
||||
#define GPS_THREAD_INTERVAL 100
|
||||
#define GPS_THREAD_INTERVAL 200
|
||||
#endif
|
||||
|
||||
// convert 24-bit color to 16-bit (56K)
|
||||
#define COLOR565(r, g, b) (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3))
|
||||
|
||||
/* Step #1: offer chance for variant-specific defines */
|
||||
#include "variant.h"
|
||||
|
||||
@@ -183,6 +188,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#ifndef HAS_TELEMETRY
|
||||
#define HAS_TELEMETRY 0
|
||||
#endif
|
||||
#ifndef HAS_SENSOR
|
||||
#define HAS_SENSOR 0
|
||||
#endif
|
||||
#ifndef HAS_RADIO
|
||||
#define HAS_RADIO 0
|
||||
#endif
|
||||
|
||||
@@ -30,8 +30,8 @@ ScanI2C::FoundDevice ScanI2C::firstRTC() const
|
||||
|
||||
ScanI2C::FoundDevice ScanI2C::firstKeyboard() const
|
||||
{
|
||||
ScanI2C::DeviceType types[] = {CARDKB, TDECKKB, RAK14004};
|
||||
return firstOfOrNONE(3, types);
|
||||
ScanI2C::DeviceType types[] = {CARDKB, TDECKKB, BBQ10KB, RAK14004};
|
||||
return firstOfOrNONE(4, types);
|
||||
}
|
||||
|
||||
ScanI2C::FoundDevice ScanI2C::firstAccelerometer() const
|
||||
|
||||
@@ -17,6 +17,7 @@ class ScanI2C
|
||||
RTC_PCF8563,
|
||||
CARDKB,
|
||||
TDECKKB,
|
||||
BBQ10KB,
|
||||
RAK14004,
|
||||
PMU_AXP192_AXP2101,
|
||||
BME_680,
|
||||
@@ -24,6 +25,7 @@ class ScanI2C
|
||||
BMP_280,
|
||||
INA260,
|
||||
INA219,
|
||||
INA3221,
|
||||
MCP9808,
|
||||
SHT31,
|
||||
SHTC3,
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
#include "concurrency/LockGuard.h"
|
||||
#include "configuration.h"
|
||||
|
||||
#if defined(ARCH_RASPBERRY_PI)
|
||||
#include "linux/LinuxHardwareI2C.h"
|
||||
#endif
|
||||
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL)
|
||||
#include "main.h" // atecc
|
||||
#endif
|
||||
@@ -162,7 +164,14 @@ void ScanI2CTwoWire::scanPort(I2CPort port)
|
||||
|
||||
for (addr.address = 1; addr.address < 127; addr.address++) {
|
||||
i2cBus->beginTransmission(addr.address);
|
||||
#ifdef ARCH_PORTDUINO
|
||||
if (i2cBus->read() != -1)
|
||||
err = 0;
|
||||
else
|
||||
err = 2;
|
||||
#else
|
||||
err = i2cBus->endTransmission();
|
||||
#endif
|
||||
type = NONE;
|
||||
if (err == 0) {
|
||||
LOG_DEBUG("I2C device found at address 0x%x\n", addr.address);
|
||||
@@ -213,6 +222,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port)
|
||||
break;
|
||||
|
||||
SCAN_SIMPLE_CASE(TDECK_KB_ADDR, TDECKKB, "T-Deck keyboard found\n");
|
||||
SCAN_SIMPLE_CASE(BBQ10_KB_ADDR, BBQ10KB, "BB Q10 keyboard found\n");
|
||||
SCAN_SIMPLE_CASE(ST7567_ADDRESS, SCREEN_ST7567, "st7567 display found\n");
|
||||
#ifdef HAS_NCP5623
|
||||
SCAN_SIMPLE_CASE(NCP5623_ADDR, NCP5623, "NCP5623 RGB LED found\n");
|
||||
@@ -250,7 +260,10 @@ void ScanI2CTwoWire::scanPort(I2CPort port)
|
||||
type = INA219;
|
||||
}
|
||||
break;
|
||||
|
||||
case INA3221_ADDR:
|
||||
LOG_INFO("INA3221 sensor found at address 0x%x\n", (uint8_t)addr.address);
|
||||
type = INA3221;
|
||||
break;
|
||||
case MCP9808_ADDR:
|
||||
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x07), 2);
|
||||
if (registerValue == 0x0400) {
|
||||
|
||||
@@ -18,6 +18,8 @@ class ScanI2CTwoWire : public ScanI2C
|
||||
|
||||
ScanI2C::FoundDevice find(ScanI2C::DeviceType) const override;
|
||||
|
||||
TwoWire *fetchI2CBus(ScanI2C::DeviceAddress) const;
|
||||
|
||||
bool exists(ScanI2C::DeviceType) const override;
|
||||
|
||||
size_t countDevices() const override;
|
||||
@@ -51,6 +53,4 @@ class ScanI2CTwoWire : public ScanI2C
|
||||
uint16_t getRegisterValue(const RegisterLocation &, ResponseWidth) const;
|
||||
|
||||
DeviceType probeOLED(ScanI2C::DeviceAddress) const;
|
||||
|
||||
TwoWire *fetchI2CBus(ScanI2C::DeviceAddress) const;
|
||||
};
|
||||
|
||||
1386
src/gps/GPS.cpp
1386
src/gps/GPS.cpp
File diff suppressed because it is too large
Load Diff
204
src/gps/GPS.h
204
src/gps/GPS.h
@@ -2,7 +2,16 @@
|
||||
|
||||
#include "GPSStatus.h"
|
||||
#include "Observer.h"
|
||||
#include "TinyGPS++.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "input/RotaryEncoderInterruptImpl1.h"
|
||||
#include "input/UpDownInterruptImpl1.h"
|
||||
#include "modules/PositionModule.h"
|
||||
|
||||
// Allow defining the polarity of the ENABLE output. default is active high
|
||||
#ifndef GPS_EN_ACTIVE
|
||||
#define GPS_EN_ACTIVE 1
|
||||
#endif
|
||||
|
||||
struct uBloxGnssModelInfo {
|
||||
char swVersion[30];
|
||||
@@ -14,10 +23,17 @@ struct uBloxGnssModelInfo {
|
||||
typedef enum {
|
||||
GNSS_MODEL_MTK,
|
||||
GNSS_MODEL_UBLOX,
|
||||
GNSS_MODEL_UC6850,
|
||||
GNSS_MODEL_UC6580,
|
||||
GNSS_MODEL_UNKNOWN,
|
||||
} GnssModel_t;
|
||||
|
||||
typedef enum {
|
||||
GNSS_RESPONSE_NONE,
|
||||
GNSS_RESPONSE_NAK,
|
||||
GNSS_RESPONSE_FRAME_ERRORS,
|
||||
GNSS_RESPONSE_OK,
|
||||
} GPS_RESPONSE;
|
||||
|
||||
// Generate a string representation of DOP
|
||||
const char *getDOPString(uint32_t dop);
|
||||
|
||||
@@ -28,8 +44,29 @@ const char *getDOPString(uint32_t dop);
|
||||
*/
|
||||
class GPS : private concurrency::OSThread
|
||||
{
|
||||
TinyGPSPlus reader;
|
||||
uint8_t fixQual = 0; // fix quality from GPGGA
|
||||
uint32_t lastChecksumFailCount = 0;
|
||||
|
||||
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
|
||||
// (20210908) TinyGps++ can only read the GPGSA "FIX TYPE" field
|
||||
// via optional feature "custom fields", currently disabled (bug #525)
|
||||
TinyGPSCustom gsafixtype; // custom extract fix type from GPGSA
|
||||
TinyGPSCustom gsapdop; // custom extract PDOP from GPGSA
|
||||
uint8_t fixType = 0; // fix type from GPGSA
|
||||
#endif
|
||||
private:
|
||||
uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0, lastWhileActiveMsec = 0;
|
||||
uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0;
|
||||
const int serialSpeeds[6] = {9600, 4800, 38400, 57600, 115200, 9600};
|
||||
|
||||
uint32_t rx_gpio = 0;
|
||||
uint32_t tx_gpio = 0;
|
||||
uint32_t en_gpio = 0;
|
||||
int32_t averageLockTime = 0;
|
||||
uint32_t GPSCycles = 0;
|
||||
|
||||
int speedSelect = 0;
|
||||
int probeTries = 2;
|
||||
|
||||
/**
|
||||
* hasValidLocation - indicates that the position variables contain a complete
|
||||
@@ -39,15 +76,17 @@ class GPS : private concurrency::OSThread
|
||||
|
||||
bool isAwake = false; // true if we want a location right now
|
||||
|
||||
bool wakeAllowed = true; // false if gps must be forced to sleep regardless of what time it is
|
||||
bool isInPowersave = false;
|
||||
|
||||
bool shouldPublish = false; // If we've changed GPS state, this will force a publish the next loop()
|
||||
|
||||
bool hasGPS = false; // Do we have a GPS we are talking to
|
||||
|
||||
bool GPSInitFinished = false; // Init thread finished?
|
||||
bool GPSInitStarted = false; // Init thread finished?
|
||||
|
||||
uint8_t numSatellites = 0;
|
||||
|
||||
CallbackObserver<GPS, void *> notifySleepObserver = CallbackObserver<GPS, void *>(this, &GPS::prepareSleep);
|
||||
CallbackObserver<GPS, void *> notifyDeepSleepObserver = CallbackObserver<GPS, void *>(this, &GPS::prepareDeepSleep);
|
||||
CallbackObserver<GPS, void *> notifyGPSSleepObserver = CallbackObserver<GPS, void *>(this, &GPS::prepareDeepSleep);
|
||||
|
||||
@@ -55,6 +94,24 @@ class GPS : private concurrency::OSThread
|
||||
/** If !NULL we will use this serial port to construct our GPS */
|
||||
static HardwareSerial *_serial_gps;
|
||||
|
||||
static uint8_t _message_PMREQ[];
|
||||
static const uint8_t _message_CFG_RXM_PSM[];
|
||||
static const uint8_t _message_CFG_RXM_ECO[];
|
||||
static const uint8_t _message_CFG_PM2[];
|
||||
static const uint8_t _message_GNSS_7[];
|
||||
static const uint8_t _message_GNSS[];
|
||||
static const uint8_t _message_JAM[];
|
||||
static const uint8_t _message_NAVX5[];
|
||||
static const uint8_t _message_1HZ[];
|
||||
static const uint8_t _message_GGL[];
|
||||
static const uint8_t _message_GSA[];
|
||||
static const uint8_t _message_GSV[];
|
||||
static const uint8_t _message_VTG[];
|
||||
static const uint8_t _message_RMC[];
|
||||
static const uint8_t _message_GGA[];
|
||||
static const uint8_t _message_PMS[];
|
||||
static const uint8_t _message_SAVE[];
|
||||
|
||||
meshtastic_Position p = meshtastic_Position_init_default;
|
||||
|
||||
GPS() : concurrency::OSThread("GPS") {}
|
||||
@@ -69,6 +126,14 @@ class GPS : private concurrency::OSThread
|
||||
*/
|
||||
virtual bool setup();
|
||||
|
||||
// re-enable the thread
|
||||
void enable();
|
||||
|
||||
// Disable the thread
|
||||
int32_t disable() override;
|
||||
|
||||
void setGPSPower(bool on, bool standbyOnly, uint32_t sleepTime);
|
||||
|
||||
/// Returns true if we have acquired GPS lock.
|
||||
virtual bool hasLock();
|
||||
|
||||
@@ -80,68 +145,20 @@ class GPS : private concurrency::OSThread
|
||||
|
||||
bool isPowerSaving() const { return !config.position.gps_enabled; }
|
||||
|
||||
/**
|
||||
* Restart our lock attempt - try to get and broadcast a GPS reading ASAP
|
||||
* called after the CPU wakes from light-sleep state
|
||||
*
|
||||
* Or set to false, to disallow any sort of waking
|
||||
* */
|
||||
void forceWake(bool on);
|
||||
// Empty the input buffer as quickly as possible
|
||||
void clearBuffer();
|
||||
|
||||
// Some GPS modules (ublock) require factory reset
|
||||
virtual bool factoryReset() { return true; }
|
||||
// Create a ublox packet for editing in memory
|
||||
uint8_t makeUBXPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_size, const uint8_t *msg);
|
||||
|
||||
protected:
|
||||
/// Do gps chipset specific init, return true for success
|
||||
virtual bool setupGPS();
|
||||
// scratch space for creating ublox packets
|
||||
uint8_t UBXscratch[250] = {0};
|
||||
|
||||
/// If possible force the GPS into sleep/low power mode
|
||||
virtual void sleep();
|
||||
int rebootsSeen = 0;
|
||||
|
||||
/// wake the GPS into normal operation mode
|
||||
virtual void wake();
|
||||
|
||||
/** Subclasses should look for serial rx characters here and feed it to their GPS parser
|
||||
*
|
||||
* Return true if we received a valid message from the GPS
|
||||
*/
|
||||
virtual bool whileIdle() = 0;
|
||||
|
||||
/** Idle processing while GPS is looking for lock, called once per secondish */
|
||||
virtual void whileActive() {}
|
||||
|
||||
/**
|
||||
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
|
||||
* Override this method to check for new locations
|
||||
*
|
||||
* @return true if we've acquired a time
|
||||
*/
|
||||
virtual bool lookForTime() = 0;
|
||||
|
||||
/**
|
||||
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
|
||||
* Override this method to check for new locations
|
||||
*
|
||||
* @return true if we've acquired a new location
|
||||
*/
|
||||
virtual bool lookForLocation() = 0;
|
||||
|
||||
/// Record that we have a GPS
|
||||
void setConnected();
|
||||
|
||||
void setNumSatellites(uint8_t n);
|
||||
|
||||
private:
|
||||
/// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs
|
||||
/// always returns 0 to indicate okay to sleep
|
||||
int prepareSleep(void *unused);
|
||||
|
||||
/// Prepare the GPS for the cpu entering deep sleep, expect to be gone for at least 100s of msecs
|
||||
/// always returns 0 to indicate okay to sleep
|
||||
int prepareDeepSleep(void *unused);
|
||||
|
||||
// Calculate checksum
|
||||
void UBXChecksum(byte *message, size_t length);
|
||||
int getACK(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t requestedID, uint32_t waitMillis);
|
||||
GPS_RESPONSE getACK(uint8_t c, uint8_t i, uint32_t waitMillis);
|
||||
GPS_RESPONSE getACK(const char *message, uint32_t waitMillis);
|
||||
|
||||
/**
|
||||
* Switch the GPS into a mode where we are actively looking for a lock, or alternatively switch GPS into a low power mode
|
||||
@@ -149,6 +166,59 @@ class GPS : private concurrency::OSThread
|
||||
* calls sleep/wake
|
||||
*/
|
||||
void setAwake(bool on);
|
||||
virtual bool factoryReset();
|
||||
|
||||
// Creates an instance of the GPS class.
|
||||
// Returns the new instance or null if the GPS is not present.
|
||||
static GPS *createGps();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
|
||||
* Override this method to check for new locations
|
||||
*
|
||||
* @return true if we've acquired a time
|
||||
*/
|
||||
|
||||
/**
|
||||
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
|
||||
* Override this method to check for new locations
|
||||
*
|
||||
* @return true if we've acquired a new location
|
||||
*/
|
||||
|
||||
/// Record that we have a GPS
|
||||
void setConnected();
|
||||
|
||||
/** Subclasses should look for serial rx characters here and feed it to their GPS parser
|
||||
*
|
||||
* Return true if we received a valid message from the GPS
|
||||
*/
|
||||
virtual bool whileIdle();
|
||||
|
||||
/**
|
||||
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
|
||||
* Override this method to check for new locations
|
||||
*
|
||||
* @return true if we've acquired a time
|
||||
*/
|
||||
virtual bool lookForTime();
|
||||
|
||||
/**
|
||||
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
|
||||
* Override this method to check for new locations
|
||||
*
|
||||
* @return true if we've acquired a new location
|
||||
*/
|
||||
virtual bool lookForLocation();
|
||||
|
||||
private:
|
||||
/// Prepare the GPS for the cpu entering deep sleep, expect to be gone for at least 100s of msecs
|
||||
/// always returns 0 to indicate okay to sleep
|
||||
int prepareDeepSleep(void *unused);
|
||||
|
||||
// Calculate checksum
|
||||
void UBXChecksum(uint8_t *message, size_t length);
|
||||
|
||||
/** Get how long we should stay looking for each aquisition
|
||||
*/
|
||||
@@ -158,8 +228,6 @@ class GPS : private concurrency::OSThread
|
||||
*/
|
||||
uint32_t getSleepTime() const;
|
||||
|
||||
bool getACK(uint8_t c, uint8_t i);
|
||||
|
||||
/**
|
||||
* Tell users we have new GPS readings
|
||||
*/
|
||||
@@ -169,9 +237,7 @@ class GPS : private concurrency::OSThread
|
||||
|
||||
// Get GNSS model
|
||||
String getNMEA();
|
||||
GnssModel_t probe();
|
||||
|
||||
int getAck(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t requestedID);
|
||||
GnssModel_t probe(int serialSpeed);
|
||||
|
||||
// delay counter to allow more sats before fixed position stops GPS thread
|
||||
uint8_t fixeddelayCtr = 0;
|
||||
@@ -180,8 +246,4 @@ class GPS : private concurrency::OSThread
|
||||
GnssModel_t gnssModel = GNSS_MODEL_UNKNOWN;
|
||||
};
|
||||
|
||||
// Creates an instance of the GPS class.
|
||||
// Returns the new instance or null if the GPS is not present.
|
||||
GPS *createGps();
|
||||
|
||||
extern GPS *gps;
|
||||
@@ -1,266 +0,0 @@
|
||||
#include "NMEAGPS.h"
|
||||
#include "RTC.h"
|
||||
#include "configuration.h"
|
||||
|
||||
#include <TinyGPS++.h>
|
||||
|
||||
// GPS solutions older than this will be rejected - see TinyGPSDatum::age()
|
||||
#define GPS_SOL_EXPIRY_MS 5000 // in millis. give 1 second time to combine different sentences. NMEA Frequency isn't higher anyway
|
||||
#define NMEA_MSG_GXGSA "GNGSA" // GSA message (GPGSA, GNGSA etc)
|
||||
|
||||
static int32_t toDegInt(RawDegrees d)
|
||||
{
|
||||
int32_t degMult = 10000000; // 1e7
|
||||
int32_t r = d.deg * degMult + d.billionths / 100;
|
||||
if (d.negative)
|
||||
r *= -1;
|
||||
return r;
|
||||
}
|
||||
|
||||
bool NMEAGPS::factoryReset()
|
||||
{
|
||||
#ifdef PIN_GPS_REINIT
|
||||
// The L76K GNSS on the T-Echo requires the RESET pin to be pulled LOW
|
||||
digitalWrite(PIN_GPS_REINIT, 0);
|
||||
pinMode(PIN_GPS_REINIT, OUTPUT);
|
||||
delay(150); // The L76K datasheet calls for at least 100MS delay
|
||||
digitalWrite(PIN_GPS_REINIT, 1);
|
||||
#endif
|
||||
|
||||
// send the UBLOX Factory Reset Command regardless of detect state, something is very wrong, just assume it's UBLOX.
|
||||
// Factory Reset
|
||||
byte _message_reset[] = {0xB5, 0x62, 0x06, 0x09, 0x0D, 0x00, 0xFF, 0xFB, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x17, 0x2B, 0x7E};
|
||||
_serial_gps->write(_message_reset, sizeof(_message_reset));
|
||||
delay(1000);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NMEAGPS::setupGPS()
|
||||
{
|
||||
GPS::setupGPS();
|
||||
|
||||
#ifdef PIN_GPS_PPS
|
||||
// pulse per second
|
||||
// FIXME - move into shared GPS code
|
||||
pinMode(PIN_GPS_PPS, INPUT);
|
||||
#endif
|
||||
|
||||
// Currently disabled per issue #525 (TinyGPS++ crash bug)
|
||||
// when fixed upstream, can be un-disabled to enable 3D FixType and PDOP
|
||||
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
|
||||
// see NMEAGPS.h
|
||||
gsafixtype.begin(reader, NMEA_MSG_GXGSA, 2);
|
||||
gsapdop.begin(reader, NMEA_MSG_GXGSA, 15);
|
||||
LOG_DEBUG("Using " NMEA_MSG_GXGSA " for 3DFIX and PDOP\n");
|
||||
#else
|
||||
LOG_DEBUG("GxGSA NOT available\n");
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
|
||||
* Override this method to check for new locations
|
||||
*
|
||||
* @return true if we've acquired a new location
|
||||
*/
|
||||
bool NMEAGPS::lookForTime()
|
||||
{
|
||||
auto ti = reader.time;
|
||||
auto d = reader.date;
|
||||
if (ti.isValid() && d.isValid()) { // Note: we don't check for updated, because we'll only be called if needed
|
||||
/* 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
|
||||
(midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z).
|
||||
*/
|
||||
struct tm t;
|
||||
t.tm_sec = ti.second();
|
||||
t.tm_min = ti.minute();
|
||||
t.tm_hour = ti.hour();
|
||||
t.tm_mday = d.day();
|
||||
t.tm_mon = d.month() - 1;
|
||||
t.tm_year = d.year() - 1900;
|
||||
t.tm_isdst = false;
|
||||
if (t.tm_mon > -1) {
|
||||
LOG_DEBUG("NMEA GPS time %02d-%02d-%02d %02d:%02d:%02d\n", d.year(), d.month(), t.tm_mday, t.tm_hour, t.tm_min,
|
||||
t.tm_sec);
|
||||
perhapsSetRTC(RTCQualityGPS, t);
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
|
||||
* Override this method to check for new locations
|
||||
*
|
||||
* @return true if we've acquired a new location
|
||||
*/
|
||||
bool NMEAGPS::lookForLocation()
|
||||
{
|
||||
// By default, TinyGPS++ does not parse GPGSA lines, which give us
|
||||
// the 2D/3D fixType (see NMEAGPS.h)
|
||||
// At a minimum, use the fixQuality indicator in GPGGA (FIXME?)
|
||||
fixQual = reader.fixQuality();
|
||||
|
||||
#ifndef TINYGPS_OPTION_NO_STATISTICS
|
||||
if (reader.failedChecksum() > lastChecksumFailCount) {
|
||||
LOG_WARN("Warning, %u new GPS checksum failures, for a total of %u.\n", reader.failedChecksum() - lastChecksumFailCount,
|
||||
reader.failedChecksum());
|
||||
lastChecksumFailCount = reader.failedChecksum();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
|
||||
fixType = atoi(gsafixtype.value()); // will set to zero if no data
|
||||
// LOG_DEBUG("FIX QUAL=%d, TYPE=%d\n", fixQual, fixType);
|
||||
#endif
|
||||
|
||||
// check if GPS has an acceptable lock
|
||||
if (!hasLock())
|
||||
return false;
|
||||
|
||||
#ifdef GPS_EXTRAVERBOSE
|
||||
LOG_DEBUG("AGE: LOC=%d FIX=%d DATE=%d TIME=%d\n", reader.location.age(),
|
||||
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
|
||||
gsafixtype.age(),
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
reader.date.age(), reader.time.age());
|
||||
#endif // GPS_EXTRAVERBOSE
|
||||
|
||||
// check if a complete GPS solution set is available for reading
|
||||
// tinyGPSDatum::age() also includes isValid() test
|
||||
// FIXME
|
||||
if (!((reader.location.age() < GPS_SOL_EXPIRY_MS) &&
|
||||
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
|
||||
(gsafixtype.age() < GPS_SOL_EXPIRY_MS) &&
|
||||
#endif
|
||||
(reader.time.age() < GPS_SOL_EXPIRY_MS) && (reader.date.age() < GPS_SOL_EXPIRY_MS))) {
|
||||
LOG_WARN("SOME data is TOO OLD: LOC %u, TIME %u, DATE %u\n", reader.location.age(), reader.time.age(), reader.date.age());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is this a new point or are we re-reading the previous one?
|
||||
if (!reader.location.isUpdated())
|
||||
return false;
|
||||
|
||||
// We know the solution is fresh and valid, so just read the data
|
||||
auto loc = reader.location.value();
|
||||
|
||||
// Bail out EARLY to avoid overwriting previous good data (like #857)
|
||||
if (toDegInt(loc.lat) > 900000000) {
|
||||
#ifdef GPS_EXTRAVERBOSE
|
||||
LOG_DEBUG("Bail out EARLY on LAT %i\n", toDegInt(loc.lat));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
if (toDegInt(loc.lng) > 1800000000) {
|
||||
#ifdef GPS_EXTRAVERBOSE
|
||||
LOG_DEBUG("Bail out EARLY on LNG %i\n", toDegInt(loc.lng));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
p.location_source = meshtastic_Position_LocSource_LOC_INTERNAL;
|
||||
|
||||
// Dilution of precision (an accuracy metric) is reported in 10^2 units, so we need to scale down when we use it
|
||||
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
|
||||
p.HDOP = reader.hdop.value();
|
||||
p.PDOP = TinyGPSPlus::parseDecimal(gsapdop.value());
|
||||
// LOG_DEBUG("PDOP=%d, HDOP=%d\n", p.PDOP, p.HDOP);
|
||||
#else
|
||||
// FIXME! naive PDOP emulation (assumes VDOP==HDOP)
|
||||
// correct formula is PDOP = SQRT(HDOP^2 + VDOP^2)
|
||||
p.HDOP = reader.hdop.value();
|
||||
p.PDOP = 1.41 * reader.hdop.value();
|
||||
#endif
|
||||
|
||||
// Discard incomplete or erroneous readings
|
||||
if (reader.hdop.value() == 0) {
|
||||
LOG_WARN("BOGUS hdop.value() REJECTED: %d\n", reader.hdop.value());
|
||||
return false;
|
||||
}
|
||||
|
||||
p.latitude_i = toDegInt(loc.lat);
|
||||
p.longitude_i = toDegInt(loc.lng);
|
||||
|
||||
p.altitude_geoidal_separation = reader.geoidHeight.meters();
|
||||
p.altitude_hae = reader.altitude.meters() + p.altitude_geoidal_separation;
|
||||
p.altitude = reader.altitude.meters();
|
||||
|
||||
p.fix_quality = fixQual;
|
||||
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
|
||||
p.fix_type = fixType;
|
||||
#endif
|
||||
|
||||
// positional timestamp
|
||||
struct tm t;
|
||||
t.tm_sec = reader.time.second();
|
||||
t.tm_min = reader.time.minute();
|
||||
t.tm_hour = reader.time.hour();
|
||||
t.tm_mday = reader.date.day();
|
||||
t.tm_mon = reader.date.month() - 1;
|
||||
t.tm_year = reader.date.year() - 1900;
|
||||
t.tm_isdst = false;
|
||||
p.timestamp = mktime(&t);
|
||||
|
||||
// Nice to have, if available
|
||||
if (reader.satellites.isUpdated()) {
|
||||
p.sats_in_view = reader.satellites.value();
|
||||
}
|
||||
|
||||
if (reader.course.isUpdated() && reader.course.isValid()) {
|
||||
if (reader.course.value() < 36000) { // sanity check
|
||||
p.ground_track =
|
||||
reader.course.value() * 1e3; // Scale the heading (in degrees * 10^-2) to match the expected degrees * 10^-5
|
||||
} else {
|
||||
LOG_WARN("BOGUS course.value() REJECTED: %d\n", reader.course.value());
|
||||
}
|
||||
}
|
||||
|
||||
if (reader.speed.isUpdated() && reader.speed.isValid()) {
|
||||
p.ground_speed = reader.speed.kmph();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NMEAGPS::hasLock()
|
||||
{
|
||||
// Using GPGGA fix quality indicator
|
||||
if (fixQual >= 1 && fixQual <= 5) {
|
||||
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
|
||||
// Use GPGSA fix type 2D/3D (better) if available
|
||||
if (fixType == 3 || fixType == 0) // zero means "no data received"
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NMEAGPS::hasFlow()
|
||||
{
|
||||
return reader.passedChecksum() > 0;
|
||||
}
|
||||
|
||||
bool NMEAGPS::whileIdle()
|
||||
{
|
||||
bool isValid = false;
|
||||
// if (_serial_gps->available() > 0)
|
||||
// LOG_DEBUG("GPS Bytes Waiting: %u\n", _serial_gps->available());
|
||||
// First consume any chars that have piled up at the receiver
|
||||
while (_serial_gps->available() > 0) {
|
||||
int c = _serial_gps->read();
|
||||
// LOG_DEBUG("%c", c);
|
||||
isValid |= reader.encode(c);
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "GPS.h"
|
||||
#include "Observer.h"
|
||||
#include "TinyGPS++.h"
|
||||
|
||||
/**
|
||||
* A gps class thatreads from a NMEA GPS stream (and FIXME - eventually keeps the gps powered down except when reading)
|
||||
*
|
||||
* When new data is available it will notify observers.
|
||||
*/
|
||||
class NMEAGPS : public GPS
|
||||
{
|
||||
TinyGPSPlus reader;
|
||||
uint8_t fixQual = 0; // fix quality from GPGGA
|
||||
uint32_t lastChecksumFailCount = 0;
|
||||
|
||||
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
|
||||
// (20210908) TinyGps++ can only read the GPGSA "FIX TYPE" field
|
||||
// via optional feature "custom fields", currently disabled (bug #525)
|
||||
TinyGPSCustom gsafixtype; // custom extract fix type from GPGSA
|
||||
TinyGPSCustom gsapdop; // custom extract PDOP from GPGSA
|
||||
uint8_t fixType = 0; // fix type from GPGSA
|
||||
#endif
|
||||
|
||||
public:
|
||||
virtual bool setupGPS() override;
|
||||
|
||||
virtual bool factoryReset() override;
|
||||
|
||||
protected:
|
||||
/** Subclasses should look for serial rx characters here and feed it to their GPS parser
|
||||
*
|
||||
* Return true if we received a valid message from the GPS
|
||||
*/
|
||||
virtual bool whileIdle() override;
|
||||
|
||||
/**
|
||||
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
|
||||
* Override this method to check for new locations
|
||||
*
|
||||
* @return true if we've acquired a time
|
||||
*/
|
||||
virtual bool lookForTime() override;
|
||||
|
||||
/**
|
||||
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
|
||||
* Override this method to check for new locations
|
||||
*
|
||||
* @return true if we've acquired a new location
|
||||
*/
|
||||
virtual bool lookForLocation() override;
|
||||
|
||||
virtual bool hasLock() override;
|
||||
|
||||
virtual bool hasFlow() override;
|
||||
};
|
||||
@@ -103,9 +103,8 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv)
|
||||
|
||||
bool shouldSet;
|
||||
if (q > currentQuality) {
|
||||
currentQuality = q;
|
||||
shouldSet = true;
|
||||
LOG_DEBUG("Upgrading time to RTC %ld secs (quality %d)\n", tv->tv_sec, q);
|
||||
LOG_DEBUG("Upgrading time to quality %d\n", q);
|
||||
} else if (q == RTCQualityGPS && (now - lastSetMsec) > (12 * 60 * 60 * 1000UL)) {
|
||||
// Every 12 hrs we will slam in a new GPS time, to correct for local RTC clock drift
|
||||
shouldSet = true;
|
||||
@@ -114,12 +113,12 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv)
|
||||
shouldSet = false;
|
||||
|
||||
if (shouldSet) {
|
||||
currentQuality = q;
|
||||
lastSetMsec = now;
|
||||
|
||||
// This delta value works on all platforms
|
||||
timeStartMsec = now;
|
||||
zeroOffsetSecs = tv->tv_sec;
|
||||
|
||||
// If this platform has a setable RTC, set it
|
||||
#ifdef RV3028_RTC
|
||||
if (rtc_found.address == RV3028_RTC) {
|
||||
@@ -153,7 +152,7 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv)
|
||||
#endif
|
||||
|
||||
// nrf52 doesn't have a readable RTC (yet - software not written)
|
||||
#ifdef HAS_RTC
|
||||
#if HAS_RTC
|
||||
readFromRTC();
|
||||
#endif
|
||||
|
||||
|
||||
192
src/gps/ubx.h
Normal file
192
src/gps/ubx.h
Normal file
@@ -0,0 +1,192 @@
|
||||
uint8_t GPS::_message_PMREQ[] PROGMEM = {
|
||||
0x00, 0x00, // 4 bytes duration of request task
|
||||
0x00, 0x00, // (milliseconds)
|
||||
0x02, 0x00, // Task flag bitfield
|
||||
0x00, 0x00 // byte index 1 = sleep mode
|
||||
};
|
||||
|
||||
const uint8_t GPS::_message_CFG_RXM_PSM[] PROGMEM = {
|
||||
0x08, // Reserved
|
||||
0x01 // Power save mode
|
||||
};
|
||||
|
||||
const uint8_t GPS::_message_CFG_RXM_ECO[] PROGMEM = {
|
||||
0x08, // Reserved
|
||||
0x04 // eco mode
|
||||
};
|
||||
|
||||
const uint8_t GPS::_message_CFG_PM2[] PROGMEM = {
|
||||
0x01, 0x06, 0x00, 0x00, // version, Reserved
|
||||
0x0E, 0x81, 0x43, 0x01, // flags
|
||||
0xE8, 0x03, 0x00, 0x00, // update period 1000 ms
|
||||
0x10, 0x27, 0x00, 0x00, // search period 10s
|
||||
0x00, 0x00, 0x00, 0x00, // Grod offset 0
|
||||
0x01, 0x00, // onTime 1 second
|
||||
0x00, 0x00, // min search time 0
|
||||
0x2C, 0x01, // reserved
|
||||
0x00, 0x00, 0x4F, 0xC1, // reserved
|
||||
0x03, 0x00, 0x87, 0x02, // reserved
|
||||
0x00, 0x00, 0xFF, 0x00, // reserved
|
||||
0x01, 0x00, 0xD6, 0x4D // reserved
|
||||
};
|
||||
|
||||
const uint8_t GPS::_message_GNSS_7[] = {
|
||||
0x00, // msgVer (0 for this version)
|
||||
0x00, // numTrkChHw (max number of hardware channels, read only, so it's always 0)
|
||||
0xff, // numTrkChUse (max number of channels to use, 0xff = max available)
|
||||
0x02, // numConfigBlocks (number of GNSS systems), most modules support maximum 3 GNSS systems
|
||||
// GNSS config format: gnssId, resTrkCh, maxTrkCh, reserved1, flags
|
||||
0x00, 0x08, 0x10, 0x00, 0x01, 0x00, 0x00, 0x01, // GPS
|
||||
0x01, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x01 // SBAS
|
||||
};
|
||||
|
||||
// It's not critical if the module doesn't acknowledge this configuration.
|
||||
// The module should operate adequately with its factory or previously saved settings.
|
||||
// It appears that there is a firmware bug in some GPS modules: When an attempt is made
|
||||
// to overwrite a saved state with identical values, no ACK/NAK is received, contrary to
|
||||
// what is specified in the Ublox documentation.
|
||||
// There is also a possibility that the module may be GPS-only.
|
||||
const uint8_t GPS::_message_GNSS[] = {
|
||||
0x00, // msgVer (0 for this version)
|
||||
0x00, // numTrkChHw (max number of hardware channels, read only, so it's always 0)
|
||||
0xff, // numTrkChUse (max number of channels to use, 0xff = max available)
|
||||
0x03, // numConfigBlocks (number of GNSS systems), most modules support maximum 3 GNSS systems
|
||||
// GNSS config format: gnssId, resTrkCh, maxTrkCh, reserved1, flags
|
||||
0x00, 0x08, 0x10, 0x00, 0x01, 0x00, 0x01, 0x01, // GPS
|
||||
0x01, 0x01, 0x03, 0x00, 0x01, 0x00, 0x01, 0x01, // SBAS
|
||||
0x06, 0x08, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x01 // GLONASS
|
||||
};
|
||||
|
||||
// Enable interference resistance, because we are using LoRa, WiFi and Bluetooth on same board,
|
||||
// and we need to reduce interference from them
|
||||
const uint8_t GPS::_message_JAM[] = {
|
||||
// bbThreshold (Broadband jamming detection threshold) is set to 0x3F (63 in decimal)
|
||||
// cwThreshold (CW jamming detection threshold) is set to 0x10 (16 in decimal)
|
||||
// algorithmBits (Reserved algorithm settings) is set to 0x16B156 as recommended
|
||||
// enable (Enable interference detection) is set to 1 (enabled)
|
||||
0x3F, 0x10, 0xB1, 0x56, // config: Interference config word
|
||||
// generalBits (General settings) is set to 0x31E as recommended
|
||||
// antSetting (Antenna setting, 0=unknown, 1=passive, 2=active) is set to 0 (unknown)
|
||||
// ToDo: Set to 1 (passive) or 2 (active) if known, for example from UBX-MON-HW, or from board info
|
||||
// enable2 (Set to 1 to scan auxiliary bands, u-blox 8 / u-blox M8 only, otherwise ignored) is set to 1
|
||||
// (enabled)
|
||||
0x1E, 0x03, 0x00, 0x01 // config2: Extra settings for jamming/interference monitor
|
||||
};
|
||||
|
||||
// Configure navigation engine expert settings:
|
||||
const uint8_t GPS::_message_NAVX5[] = {
|
||||
0x00, 0x00, // msgVer (0 for this version)
|
||||
// minMax flag = 1: apply min/max SVs settings
|
||||
// minCno flag = 1: apply minimum C/N0 setting
|
||||
// initial3dfix flag = 0: apply initial 3D fix settings
|
||||
// aop flag = 1: apply aopCfg (useAOP flag) settings (AssistNow Autonomous)
|
||||
0x1B, 0x00, // mask1 (First parameters bitmask)
|
||||
// adr flag = 0: apply ADR sensor fusion on/off setting (useAdr flag)
|
||||
// If firmware is not ADR/UDR, enabling this flag will fail configuration
|
||||
// ToDo: check this with UBX-MON-VER
|
||||
0x00, 0x00, 0x00, 0x00, // mask2 (Second parameters bitmask)
|
||||
0x00, 0x00, // Reserved
|
||||
0x03, // minSVs (Minimum number of satellites for navigation) = 3
|
||||
0x10, // maxSVs (Maximum number of satellites for navigation) = 16
|
||||
0x06, // minCNO (Minimum satellite signal level for navigation) = 6 dBHz
|
||||
0x00, // Reserved
|
||||
0x00, // iniFix3D (Initial fix must be 3D) = 0 (disabled)
|
||||
0x00, 0x00, // Reserved
|
||||
0x00, // ackAiding (Issue acknowledgements for assistance message input) = 0 (disabled)
|
||||
0x00, 0x00, // Reserved
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved
|
||||
0x00, // Reserved
|
||||
0x01, // aopCfg (AssistNow Autonomous configuration) = 1 (enabled)
|
||||
0x00, 0x00, // Reserved
|
||||
0x00, 0x00, // Reserved
|
||||
0x00, 0x00, 0x00, 0x00, // Reserved
|
||||
0x00, 0x00, 0x00, // Reserved
|
||||
0x01, // useAdr (Enable/disable ADR sensor fusion) = 1 (enabled)
|
||||
};
|
||||
|
||||
// Set GPS update rate to 1Hz
|
||||
// Lowering the update rate helps to save power.
|
||||
// Additionally, for some new modules like the M9/M10, an update rate lower than 5Hz
|
||||
// is recommended to avoid a known issue with satellites disappearing.
|
||||
const uint8_t GPS::_message_1HZ[] = {
|
||||
0xE8, 0x03, // Measurement Rate (1000ms for 1Hz)
|
||||
0x01, 0x00, // Navigation rate, always 1 in GPS mode
|
||||
0x01, 0x00, // Time reference
|
||||
};
|
||||
|
||||
// Disable GGL. GGL - Geographic position (latitude and longitude), which provides the current geographical
|
||||
// coordinates.
|
||||
const uint8_t GPS::_message_GGL[] = {
|
||||
0xF0, 0x01, // NMEA ID for GLL
|
||||
0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
|
||||
0x00, // Disable
|
||||
0x01, 0x01, 0x01, 0x01 // Reserved
|
||||
};
|
||||
|
||||
// Enable GSA. GSA - GPS DOP and active satellites, used for detailing the satellites used in the positioning and
|
||||
// the DOP (Dilution of Precision)
|
||||
const uint8_t GPS::_message_GSA[] = {
|
||||
0xF0, 0x02, // NMEA ID for GSA
|
||||
0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
|
||||
0x01, // Enable
|
||||
0x01, 0x01, 0x01, 0x01 // Reserved
|
||||
};
|
||||
|
||||
// Disable GSV. GSV - Satellites in view, details the number and location of satellites in view.
|
||||
const uint8_t GPS::_message_GSV[] = {
|
||||
0xF0, 0x03, // NMEA ID for GSV
|
||||
0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
|
||||
0x00, // Disable
|
||||
0x01, 0x01, 0x01, 0x01 // Reserved
|
||||
};
|
||||
|
||||
// Disable VTG. VTG - Track made good and ground speed, which provides course and speed information relative to
|
||||
// the ground.
|
||||
const uint8_t GPS::_message_VTG[] = {
|
||||
0xF0, 0x05, // NMEA ID for VTG
|
||||
0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
|
||||
0x00, // Disable
|
||||
0x01, 0x01, 0x01, 0x01 // Reserved
|
||||
};
|
||||
|
||||
// Enable RMC. RMC - Recommended Minimum data, the essential gps pvt (position, velocity, time) data.
|
||||
const uint8_t GPS::_message_RMC[] = {
|
||||
0xF0, 0x04, // NMEA ID for RMC
|
||||
0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
|
||||
0x01, // Enable
|
||||
0x01, 0x01, 0x01, 0x01 // Reserved
|
||||
};
|
||||
|
||||
// Enable GGA. GGA - Global Positioning System Fix Data, which provides 3D location and accuracy data.
|
||||
const uint8_t GPS::_message_GGA[] = {
|
||||
0xF0, 0x00, // NMEA ID for GGA
|
||||
0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
|
||||
0x01, // Enable
|
||||
0x01, 0x01, 0x01, 0x01 // Reserved
|
||||
};
|
||||
|
||||
// The Power Management configuration allows the GPS module to operate in different power modes for optimized
|
||||
// power consumption. The modes supported are: 0x00 = Full power: The module operates at full power with no power
|
||||
// saving. 0x01 = Balanced: The module dynamically adjusts the tracking behavior to balance power consumption.
|
||||
// 0x02 = Interval: The module operates in a periodic mode, cycling between tracking and power saving states.
|
||||
// 0x03 = Aggressive with 1 Hz: The module operates in a power saving mode with a 1 Hz update rate.
|
||||
// 0x04 = Aggressive with 2 Hz: The module operates in a power saving mode with a 2 Hz update rate.
|
||||
// 0x05 = Aggressive with 4 Hz: The module operates in a power saving mode with a 4 Hz update rate.
|
||||
// The 'period' field specifies the position update and search period. It is only valid when the powerSetupValue
|
||||
// is set to Interval; otherwise, it must be set to '0'. The 'onTime' field specifies the duration of the ON phase
|
||||
// and must be smaller than the period. It is only valid when the powerSetupValue is set to Interval; otherwise,
|
||||
// it must be set to '0'.
|
||||
const uint8_t GPS::_message_PMS[] = {
|
||||
0x00, // Version (0)
|
||||
0x03, // Power setup value
|
||||
0x00, 0x00, // period: not applicable, set to 0
|
||||
0x00, 0x00, // onTime: not applicable, set to 0
|
||||
0x97, 0x6F // reserved, generated by u-center
|
||||
};
|
||||
|
||||
const uint8_t GPS::_message_SAVE[] = {
|
||||
0x00, 0x00, 0x00, 0x00, // clearMask: no sections cleared
|
||||
0xFF, 0xFF, 0x00, 0x00, // saveMask: save all sections
|
||||
0x00, 0x00, 0x00, 0x00, // loadMask: no sections loaded
|
||||
0x0F // deviceMask: BBR, Flash, EEPROM, and SPI Flash
|
||||
};
|
||||
@@ -46,7 +46,7 @@
|
||||
#define TECHO_DISPLAY_MODEL GxEPD2_154_M09
|
||||
|
||||
#elif defined(HELTEC_WIRELESS_PAPER)
|
||||
//#define TECHO_DISPLAY_MODEL GxEPD2_213_T5D
|
||||
// #define TECHO_DISPLAY_MODEL GxEPD2_213_T5D
|
||||
#define TECHO_DISPLAY_MODEL GxEPD2_213_BN
|
||||
#endif
|
||||
|
||||
@@ -193,14 +193,14 @@ bool EInkDisplay::connect()
|
||||
LOG_INFO("Doing EInk init\n");
|
||||
|
||||
#ifdef PIN_EINK_PWR_ON
|
||||
digitalWrite(PIN_EINK_PWR_ON, HIGH); // If we need to assert a pin to power external peripherals
|
||||
pinMode(PIN_EINK_PWR_ON, OUTPUT);
|
||||
digitalWrite(PIN_EINK_PWR_ON, HIGH); // If we need to assert a pin to power external peripherals
|
||||
#endif
|
||||
|
||||
#ifdef PIN_EINK_EN
|
||||
// backlight power, HIGH is backlight on, LOW is off
|
||||
digitalWrite(PIN_EINK_EN, LOW);
|
||||
pinMode(PIN_EINK_EN, OUTPUT);
|
||||
digitalWrite(PIN_EINK_EN, LOW);
|
||||
#endif
|
||||
|
||||
#if defined(TTGO_T_ECHO)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#include "main.h"
|
||||
|
||||
#ifdef HAS_NCP5623
|
||||
#include <NCP5623.h>
|
||||
extern NCP5623 rgb;
|
||||
|
||||
@@ -24,6 +24,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#if HAS_SCREEN
|
||||
#include <OLEDDisplay.h>
|
||||
|
||||
#include "DisplayFormatters.h"
|
||||
#include "GPS.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
@@ -36,18 +37,25 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "mesh-pb-constants.h"
|
||||
#include "mesh/Channels.h"
|
||||
#include "mesh/generated/meshtastic/deviceonly.pb.h"
|
||||
#include "meshUtils.h"
|
||||
#include "modules/ExternalNotificationModule.h"
|
||||
#include "modules/TextMessageModule.h"
|
||||
#include "sleep.h"
|
||||
#include "target_specific.h"
|
||||
#include "utils.h"
|
||||
|
||||
#if HAS_WIFI && !defined(ARCH_RASPBERRY_PI)
|
||||
#include "mesh/wifi/WiFiAPClient.h"
|
||||
#endif
|
||||
|
||||
#ifdef ARCH_ESP32
|
||||
#include "esp_task_wdt.h"
|
||||
#include "mesh/http/WiFiAPClient.h"
|
||||
#include "modules/esp32/StoreForwardModule.h"
|
||||
#endif
|
||||
|
||||
#if ARCH_RASPBERRY_PI
|
||||
#include "platform/portduino/PortduinoGlue.h"
|
||||
#endif
|
||||
|
||||
#ifdef OLED_RU
|
||||
#include "fonts/OLEDDisplayFontsRU.h"
|
||||
#endif
|
||||
@@ -160,17 +168,9 @@ static void drawIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDispl
|
||||
xstr(APP_VERSION_SHORT)); // Note: we don't bother printing region or now, it makes the string too long
|
||||
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(buf), y + 0, buf);
|
||||
screen->forceDisplay();
|
||||
|
||||
// FIXME - draw serial # somewhere?
|
||||
}
|
||||
|
||||
static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
// Draw region in upper left
|
||||
const char *region = myRegion ? myRegion->name : NULL;
|
||||
drawIconScreen(region, display, state, x, y);
|
||||
}
|
||||
|
||||
static void drawOEMIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
// draw an xbm image.
|
||||
@@ -220,6 +220,28 @@ static void drawOEMBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, i
|
||||
drawOEMIconScreen(region, display, state, x, y);
|
||||
}
|
||||
|
||||
static void drawFrameText(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y, const char *message)
|
||||
{
|
||||
uint16_t x_offset = display->width() / 2;
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->setFont(FONT_MEDIUM);
|
||||
display->drawString(x_offset + x, 26 + y, message);
|
||||
}
|
||||
|
||||
static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
#ifdef ARCH_ESP32
|
||||
if (wakeCause == ESP_SLEEP_WAKEUP_TIMER || wakeCause == ESP_SLEEP_WAKEUP_EXT1) {
|
||||
drawFrameText(display, state, x, y, "Resuming...");
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
// Draw region in upper left
|
||||
const char *region = myRegion ? myRegion->name : NULL;
|
||||
drawIconScreen(region, display, state, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
// Used on boot when a certificate is being created
|
||||
static void drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
@@ -320,22 +342,6 @@ static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state,
|
||||
display->drawString(x_offset + x, y_offset + y, deviceName);
|
||||
}
|
||||
|
||||
static void drawFrameShutdown(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
uint16_t x_offset = display->width() / 2;
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->setFont(FONT_MEDIUM);
|
||||
display->drawString(x_offset + x, 26 + y, "Shutting down...");
|
||||
}
|
||||
|
||||
static void drawFrameReboot(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
uint16_t x_offset = display->width() / 2;
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->setFont(FONT_MEDIUM);
|
||||
display->drawString(x_offset + x, 26 + y, "Rebooting...");
|
||||
}
|
||||
|
||||
static void drawFrameFirmware(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
@@ -365,7 +371,7 @@ static void drawCriticalFaultFrame(OLEDDisplay *display, OLEDDisplayUiState *sta
|
||||
// Ignore messages originating from phone (from the current node 0x0) unless range test or store and forward module are enabled
|
||||
static bool shouldDrawMessage(const meshtastic_MeshPacket *packet)
|
||||
{
|
||||
return packet->from != 0 && !moduleConfig.range_test.enabled && !moduleConfig.store_forward.enabled;
|
||||
return packet->from != 0 && !moduleConfig.store_forward.enabled;
|
||||
}
|
||||
|
||||
/// Draw the last text message we received
|
||||
@@ -374,7 +380,7 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
|
||||
// the max length of this buffer is much longer than we can possibly print
|
||||
static char tempBuf[237];
|
||||
|
||||
meshtastic_MeshPacket &mp = devicestate.rx_text_message;
|
||||
const meshtastic_MeshPacket &mp = devicestate.rx_text_message;
|
||||
meshtastic_NodeInfoLite *node = nodeDB.getMeshNode(getFrom(&mp));
|
||||
// LOG_DEBUG("drawing text message from 0x%x: %s\n", mp.from,
|
||||
// mp.decoded.variant.data.decoded.bytes);
|
||||
@@ -490,7 +496,7 @@ static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *img
|
||||
}
|
||||
|
||||
// Draw nodes status
|
||||
static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, NodeStatus *nodeStatus)
|
||||
static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, const NodeStatus *nodeStatus)
|
||||
{
|
||||
char usersString[20];
|
||||
snprintf(usersString, sizeof(usersString), "%d/%d", nodeStatus->getNumOnline(), nodeStatus->getNumTotal());
|
||||
@@ -858,14 +864,14 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
||||
bool hasNodeHeading = false;
|
||||
|
||||
if (ourNode && hasValidPosition(ourNode)) {
|
||||
meshtastic_PositionLite &op = ourNode->position;
|
||||
const meshtastic_PositionLite &op = ourNode->position;
|
||||
float myHeading = estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
|
||||
drawCompassNorth(display, compassX, compassY, myHeading);
|
||||
|
||||
if (hasValidPosition(node)) {
|
||||
// display direction toward node
|
||||
hasNodeHeading = true;
|
||||
meshtastic_PositionLite &p = node->position;
|
||||
const meshtastic_PositionLite &p = node->position;
|
||||
float d =
|
||||
GeoCoord::latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
|
||||
|
||||
@@ -906,28 +912,44 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
||||
drawColumns(display, x, y, fields);
|
||||
}
|
||||
|
||||
// #ifdef RAK4630
|
||||
// Screen::Screen(uint8_t address, int sda, int scl) : OSThread("Screen"), cmdQueue(32), dispdev(address, sda, scl),
|
||||
// dispdev_oled(address, sda, scl), ui(&dispdev)
|
||||
// {
|
||||
// address_found = address;
|
||||
// cmdQueue.setReader(this);
|
||||
// if (screen_found) {
|
||||
// (void)dispdev;
|
||||
// AutoOLEDWire dispdev = dispdev_oled;
|
||||
// (void)ui;
|
||||
// OLEDDisplayUi ui(&dispdev);
|
||||
// }
|
||||
// }
|
||||
// #else
|
||||
Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_OledType screenType, OLEDDISPLAY_GEOMETRY geometry)
|
||||
: concurrency::OSThread("Screen"), address_found(address), model(screenType), geometry(geometry), cmdQueue(32),
|
||||
dispdev(address.address, -1, -1, geometry, (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE),
|
||||
ui(&dispdev)
|
||||
: concurrency::OSThread("Screen"), address_found(address), model(screenType), geometry(geometry), cmdQueue(32)
|
||||
{
|
||||
#if defined(USE_SH1106) || defined(USE_SH1107) || defined(USE_SH1107_128_64)
|
||||
dispdev = new SH1106Wire(address.address, -1, -1, geometry,
|
||||
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
||||
#elif defined(USE_SSD1306)
|
||||
dispdev = new SSD1306Wire(address.address, -1, -1, geometry,
|
||||
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
||||
#elif defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ST7789_CS) || defined(RAK14014)
|
||||
dispdev = new TFTDisplay(address.address, -1, -1, geometry,
|
||||
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
||||
#elif defined(USE_EINK)
|
||||
dispdev = new EInkDisplay(address.address, -1, -1, geometry,
|
||||
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
||||
#elif defined(USE_ST7567)
|
||||
dispdev = new ST7567Wire(address.address, -1, -1, geometry,
|
||||
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
||||
#elif ARCH_RASPBERRY_PI
|
||||
if (settingsMap[displayPanel] == st7789) {
|
||||
LOG_DEBUG("Making TFTDisplay!\n");
|
||||
dispdev = new TFTDisplay(address.address, -1, -1, geometry,
|
||||
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
||||
} else {
|
||||
dispdev = new AutoOLEDWire(address.address, -1, -1, geometry,
|
||||
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
||||
isAUTOOled = true;
|
||||
}
|
||||
#else
|
||||
dispdev = new AutoOLEDWire(address.address, -1, -1, geometry,
|
||||
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
||||
isAUTOOled = true;
|
||||
#endif
|
||||
|
||||
ui = new OLEDDisplayUi(dispdev);
|
||||
cmdQueue.setReader(this);
|
||||
}
|
||||
// #endif
|
||||
|
||||
/**
|
||||
* Prepare the display for the unit going to the lowest power mode possible. Most screens will just
|
||||
* poweroff, but eink screens will show a "I'm sleeping" graphic, possibly with a QR code
|
||||
@@ -937,8 +959,8 @@ void Screen::doDeepSleep()
|
||||
#ifdef USE_EINK
|
||||
static FrameCallback sleepFrames[] = {drawSleepScreen};
|
||||
static const int sleepFrameCount = sizeof(sleepFrames) / sizeof(sleepFrames[0]);
|
||||
ui.setFrames(sleepFrames, sleepFrameCount);
|
||||
ui.update();
|
||||
ui->setFrames(sleepFrames, sleepFrameCount);
|
||||
ui->update();
|
||||
#endif
|
||||
setOn(false);
|
||||
}
|
||||
@@ -954,14 +976,16 @@ void Screen::handleSetOn(bool on)
|
||||
#ifdef T_WATCH_S3
|
||||
PMU->enablePowerOutput(XPOWERS_ALDO2);
|
||||
#endif
|
||||
dispdev.displayOn();
|
||||
dispdev.displayOn();
|
||||
#if !ARCH_RASPBERRY_PI
|
||||
dispdev->displayOn();
|
||||
#endif
|
||||
dispdev->displayOn();
|
||||
enabled = true;
|
||||
setInterval(0); // Draw ASAP
|
||||
runASAP = true;
|
||||
} else {
|
||||
LOG_INFO("Turning off screen\n");
|
||||
dispdev.displayOff();
|
||||
dispdev->displayOff();
|
||||
#ifdef T_WATCH_S3
|
||||
PMU->disablePowerOutput(XPOWERS_ALDO2);
|
||||
#endif
|
||||
@@ -978,32 +1002,33 @@ void Screen::setup()
|
||||
useDisplay = true;
|
||||
|
||||
#ifdef AutoOLEDWire_h
|
||||
dispdev.setDetected(model);
|
||||
if (isAUTOOled)
|
||||
static_cast<AutoOLEDWire *>(dispdev)->setDetected(model);
|
||||
#endif
|
||||
|
||||
#ifdef USE_SH1107_128_64
|
||||
dispdev.setSubtype(7);
|
||||
static_cast<SH1106Wire *>(dispdev)->setSubtype(7);
|
||||
#endif
|
||||
|
||||
// Initialising the UI will init the display too.
|
||||
ui.init();
|
||||
ui->init();
|
||||
|
||||
displayWidth = dispdev.width();
|
||||
displayHeight = dispdev.height();
|
||||
displayWidth = dispdev->width();
|
||||
displayHeight = dispdev->height();
|
||||
|
||||
ui.setTimePerTransition(0);
|
||||
ui->setTimePerTransition(0);
|
||||
|
||||
ui.setIndicatorPosition(BOTTOM);
|
||||
ui->setIndicatorPosition(BOTTOM);
|
||||
// Defines where the first frame is located in the bar.
|
||||
ui.setIndicatorDirection(LEFT_RIGHT);
|
||||
ui.setFrameAnimation(SLIDE_LEFT);
|
||||
ui->setIndicatorDirection(LEFT_RIGHT);
|
||||
ui->setFrameAnimation(SLIDE_LEFT);
|
||||
// Don't show the page swipe dots while in boot screen.
|
||||
ui.disableAllIndicators();
|
||||
ui->disableAllIndicators();
|
||||
// Store a pointer to Screen so we can get to it from static functions.
|
||||
ui.getUiState()->userData = this;
|
||||
ui->getUiState()->userData = this;
|
||||
|
||||
// Set the utf8 conversion function
|
||||
dispdev.setFontTableLookupFunction(customFontTableLookup);
|
||||
dispdev->setFontTableLookupFunction(customFontTableLookup);
|
||||
|
||||
if (strlen(oemStore.oem_text) > 0)
|
||||
logo_timeout *= 2;
|
||||
@@ -1011,23 +1036,23 @@ void Screen::setup()
|
||||
// Add frames.
|
||||
static FrameCallback bootFrames[] = {drawBootScreen};
|
||||
static const int bootFrameCount = sizeof(bootFrames) / sizeof(bootFrames[0]);
|
||||
ui.setFrames(bootFrames, bootFrameCount);
|
||||
ui->setFrames(bootFrames, bootFrameCount);
|
||||
// No overlays.
|
||||
ui.setOverlays(nullptr, 0);
|
||||
ui->setOverlays(nullptr, 0);
|
||||
|
||||
// Require presses to switch between frames.
|
||||
ui.disableAutoTransition();
|
||||
ui->disableAutoTransition();
|
||||
|
||||
// Set up a log buffer with 3 lines, 32 chars each.
|
||||
dispdev.setLogBuffer(3, 32);
|
||||
dispdev->setLogBuffer(3, 32);
|
||||
|
||||
#ifdef SCREEN_MIRROR
|
||||
dispdev.mirrorScreen();
|
||||
dispdev->mirrorScreen();
|
||||
#else
|
||||
// Standard behaviour is to FLIP the screen (needed on T-Beam). If this config item is set, unflip it, and thereby logically
|
||||
// flip it. If you have a headache now, you're welcome.
|
||||
if (!config.display.flip_screen) {
|
||||
dispdev.flipScreenVertically();
|
||||
dispdev->flipScreenVertically();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1035,20 +1060,30 @@ void Screen::setup()
|
||||
uint8_t dmac[6];
|
||||
getMacAddr(dmac);
|
||||
snprintf(ourId, sizeof(ourId), "%02x%02x", dmac[4], dmac[5]);
|
||||
#if ARCH_RASPBERRY_PI
|
||||
handleSetOn(false); // force clean init
|
||||
#endif
|
||||
|
||||
// Turn on the display.
|
||||
handleSetOn(true);
|
||||
|
||||
// On some ssd1306 clones, the first draw command is discarded, so draw it
|
||||
// twice initially. Skip this for EINK Displays to save a few seconds during boot
|
||||
ui.update();
|
||||
ui->update();
|
||||
#ifndef USE_EINK
|
||||
ui.update();
|
||||
ui->update();
|
||||
#endif
|
||||
serialSinceMsec = millis();
|
||||
|
||||
#if HAS_TOUCHSCREEN
|
||||
touchScreenImpl1 = new TouchScreenImpl1(dispdev.getWidth(), dispdev.getHeight(), dispdev.getTouch);
|
||||
#if ARCH_RASPBERRY_PI
|
||||
if (settingsMap[touchscreenModule]) {
|
||||
touchScreenImpl1 =
|
||||
new TouchScreenImpl1(dispdev->getWidth(), dispdev->getHeight(), static_cast<TFTDisplay *>(dispdev)->getTouch);
|
||||
touchScreenImpl1->init();
|
||||
}
|
||||
#elif HAS_TOUCHSCREEN
|
||||
touchScreenImpl1 =
|
||||
new TouchScreenImpl1(dispdev->getWidth(), dispdev->getHeight(), static_cast<TFTDisplay *>(dispdev)->getTouch);
|
||||
touchScreenImpl1->init();
|
||||
#endif
|
||||
|
||||
@@ -1058,7 +1093,8 @@ void Screen::setup()
|
||||
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
|
||||
if (textMessageModule)
|
||||
textMessageObserver.observe(textMessageModule);
|
||||
inputObserver.observe(inputBroker);
|
||||
if (inputBroker)
|
||||
inputObserver.observe(inputBroker);
|
||||
|
||||
// Modules can notify screen about refresh
|
||||
MeshModule::observeUIEvents(&uiFrameEventObserver);
|
||||
@@ -1068,7 +1104,7 @@ void Screen::forceDisplay()
|
||||
{
|
||||
// Nasty hack to force epaper updates for 'key' frames. FIXME, cleanup.
|
||||
#ifdef USE_EINK
|
||||
dispdev.forceDisplay();
|
||||
static_cast<EInkDisplay *>(dispdev)->forceDisplay();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1099,10 +1135,10 @@ int32_t Screen::runOnce()
|
||||
// Change frames.
|
||||
static FrameCallback bootOEMFrames[] = {drawOEMBootScreen};
|
||||
static const int bootOEMFrameCount = sizeof(bootOEMFrames) / sizeof(bootOEMFrames[0]);
|
||||
ui.setFrames(bootOEMFrames, bootOEMFrameCount);
|
||||
ui.update();
|
||||
ui->setFrames(bootOEMFrames, bootOEMFrameCount);
|
||||
ui->update();
|
||||
#ifndef USE_EINK
|
||||
ui.update();
|
||||
ui->update();
|
||||
#endif
|
||||
showingOEMBootScreen = false;
|
||||
}
|
||||
@@ -1175,16 +1211,16 @@ int32_t Screen::runOnce()
|
||||
|
||||
// this must be before the frameState == FIXED check, because we always
|
||||
// want to draw at least one FIXED frame before doing forceDisplay
|
||||
ui.update();
|
||||
ui->update();
|
||||
|
||||
// Switch to a low framerate (to save CPU) when we are not in transition
|
||||
// but we should only call setTargetFPS when framestate changes, because
|
||||
// otherwise that breaks animations.
|
||||
if (targetFramerate != IDLE_FRAMERATE && ui.getUiState()->frameState == FIXED) {
|
||||
// oldFrameState = ui.getUiState()->frameState;
|
||||
if (targetFramerate != IDLE_FRAMERATE && ui->getUiState()->frameState == FIXED) {
|
||||
// oldFrameState = ui->getUiState()->frameState;
|
||||
targetFramerate = IDLE_FRAMERATE;
|
||||
|
||||
ui.setTargetFPS(targetFramerate);
|
||||
ui->setTargetFPS(targetFramerate);
|
||||
forceDisplay();
|
||||
}
|
||||
|
||||
@@ -1200,7 +1236,7 @@ int32_t Screen::runOnce()
|
||||
}
|
||||
|
||||
// LOG_DEBUG("want fps %d, fixed=%d\n", targetFramerate,
|
||||
// ui.getUiState()->frameState); If we are scrolling we need to be called
|
||||
// ui->getUiState()->frameState); If we are scrolling we need to be called
|
||||
// soon, otherwise just 1 fps (to save CPU) We also ask to be called twice
|
||||
// as fast as we really need so that any rounding errors still result with
|
||||
// the correct framerate
|
||||
@@ -1232,8 +1268,8 @@ void Screen::setSSLFrames()
|
||||
if (address_found.address) {
|
||||
// LOG_DEBUG("showing SSL frames\n");
|
||||
static FrameCallback sslFrames[] = {drawSSLScreen};
|
||||
ui.setFrames(sslFrames, 1);
|
||||
ui.update();
|
||||
ui->setFrames(sslFrames, 1);
|
||||
ui->update();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1243,11 +1279,8 @@ void Screen::setWelcomeFrames()
|
||||
{
|
||||
if (address_found.address) {
|
||||
// LOG_DEBUG("showing Welcome frames\n");
|
||||
ui.disableAllIndicators();
|
||||
|
||||
static FrameCallback welcomeFrames[] = {drawWelcomeScreen};
|
||||
ui.setFrames(welcomeFrames, 1);
|
||||
ui.update();
|
||||
static FrameCallback frames[] = {drawWelcomeScreen};
|
||||
setFrameImmediateDraw(frames);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1311,7 +1344,7 @@ void Screen::setFrames()
|
||||
// call a method on debugInfoScreen object (for more details)
|
||||
normalFrames[numframes++] = &Screen::drawDebugInfoSettingsTrampoline;
|
||||
|
||||
#ifdef ARCH_ESP32
|
||||
#if HAS_WIFI && !defined(ARCH_RASPBERRY_PI)
|
||||
if (isWifiAvailable()) {
|
||||
// call a method on debugInfoScreen object (for more details)
|
||||
normalFrames[numframes++] = &Screen::drawDebugInfoWiFiTrampoline;
|
||||
@@ -1320,8 +1353,8 @@ void Screen::setFrames()
|
||||
|
||||
LOG_DEBUG("Finished building frames. numframes: %d\n", numframes);
|
||||
|
||||
ui.setFrames(normalFrames, numframes);
|
||||
ui.enableAllIndicators();
|
||||
ui->setFrames(normalFrames, numframes);
|
||||
ui->enableAllIndicators();
|
||||
|
||||
prevFrame = -1; // Force drawNodeInfo to pick a new node (because our list
|
||||
// just changed)
|
||||
@@ -1334,12 +1367,15 @@ void Screen::handleStartBluetoothPinScreen(uint32_t pin)
|
||||
LOG_DEBUG("showing bluetooth screen\n");
|
||||
showingNormalScreen = false;
|
||||
|
||||
static FrameCallback btFrames[] = {drawFrameBluetooth};
|
||||
|
||||
static FrameCallback frames[] = {drawFrameBluetooth};
|
||||
snprintf(btPIN, sizeof(btPIN), "%06u", pin);
|
||||
setFrameImmediateDraw(frames);
|
||||
}
|
||||
|
||||
ui.disableAllIndicators();
|
||||
ui.setFrames(btFrames, 1);
|
||||
void Screen::setFrameImmediateDraw(FrameCallback *drawFrames)
|
||||
{
|
||||
ui->disableAllIndicators();
|
||||
ui->setFrames(drawFrames, 1);
|
||||
setFastFramerate();
|
||||
}
|
||||
|
||||
@@ -1348,11 +1384,12 @@ void Screen::handleShutdownScreen()
|
||||
LOG_DEBUG("showing shutdown screen\n");
|
||||
showingNormalScreen = false;
|
||||
|
||||
static FrameCallback shutdownFrames[] = {drawFrameShutdown};
|
||||
auto frame = [](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
|
||||
drawFrameText(display, state, x, y, "Shutting down...");
|
||||
};
|
||||
static FrameCallback frames[] = {frame};
|
||||
|
||||
ui.disableAllIndicators();
|
||||
ui.setFrames(shutdownFrames, 1);
|
||||
setFastFramerate();
|
||||
setFrameImmediateDraw(frames);
|
||||
}
|
||||
|
||||
void Screen::handleRebootScreen()
|
||||
@@ -1360,11 +1397,11 @@ void Screen::handleRebootScreen()
|
||||
LOG_DEBUG("showing reboot screen\n");
|
||||
showingNormalScreen = false;
|
||||
|
||||
static FrameCallback rebootFrames[] = {drawFrameReboot};
|
||||
|
||||
ui.disableAllIndicators();
|
||||
ui.setFrames(rebootFrames, 1);
|
||||
setFastFramerate();
|
||||
auto frame = [](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
|
||||
drawFrameText(display, state, x, y, "Rebooting...");
|
||||
};
|
||||
static FrameCallback frames[] = {frame};
|
||||
setFrameImmediateDraw(frames);
|
||||
}
|
||||
|
||||
void Screen::handleStartFirmwareUpdateScreen()
|
||||
@@ -1372,28 +1409,25 @@ void Screen::handleStartFirmwareUpdateScreen()
|
||||
LOG_DEBUG("showing firmware screen\n");
|
||||
showingNormalScreen = false;
|
||||
|
||||
static FrameCallback btFrames[] = {drawFrameFirmware};
|
||||
|
||||
ui.disableAllIndicators();
|
||||
ui.setFrames(btFrames, 1);
|
||||
setFastFramerate();
|
||||
static FrameCallback frames[] = {drawFrameFirmware};
|
||||
setFrameImmediateDraw(frames);
|
||||
}
|
||||
|
||||
void Screen::blink()
|
||||
{
|
||||
setFastFramerate();
|
||||
uint8_t count = 10;
|
||||
dispdev.setBrightness(254);
|
||||
dispdev->setBrightness(254);
|
||||
while (count > 0) {
|
||||
dispdev.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
dispdev.display();
|
||||
dispdev->fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
dispdev->display();
|
||||
delay(50);
|
||||
dispdev.clear();
|
||||
dispdev.display();
|
||||
dispdev->clear();
|
||||
dispdev->display();
|
||||
delay(50);
|
||||
count = count - 1;
|
||||
}
|
||||
dispdev.setBrightness(brightness);
|
||||
dispdev->setBrightness(brightness);
|
||||
}
|
||||
|
||||
std::string Screen::drawTimeDelta(uint32_t days, uint32_t hours, uint32_t minutes, uint32_t seconds)
|
||||
@@ -1421,15 +1455,15 @@ void Screen::handlePrint(const char *text)
|
||||
if (!useDisplay || !showingNormalScreen)
|
||||
return;
|
||||
|
||||
dispdev.print(text);
|
||||
dispdev->print(text);
|
||||
}
|
||||
|
||||
void Screen::handleOnPress()
|
||||
{
|
||||
// If screen was off, just wake it, otherwise advance to next frame
|
||||
// If we are in a transition, the press must have bounced, drop it.
|
||||
if (ui.getUiState()->frameState == FIXED) {
|
||||
ui.nextFrame();
|
||||
if (ui->getUiState()->frameState == FIXED) {
|
||||
ui->nextFrame();
|
||||
lastScreenTransition = millis();
|
||||
setFastFramerate();
|
||||
}
|
||||
@@ -1439,8 +1473,8 @@ void Screen::handleShowPrevFrame()
|
||||
{
|
||||
// If screen was off, just wake it, otherwise go back to previous frame
|
||||
// If we are in a transition, the press must have bounced, drop it.
|
||||
if (ui.getUiState()->frameState == FIXED) {
|
||||
ui.previousFrame();
|
||||
if (ui->getUiState()->frameState == FIXED) {
|
||||
ui->previousFrame();
|
||||
lastScreenTransition = millis();
|
||||
setFastFramerate();
|
||||
}
|
||||
@@ -1450,8 +1484,8 @@ void Screen::handleShowNextFrame()
|
||||
{
|
||||
// If screen was off, just wake it, otherwise advance to next frame
|
||||
// If we are in a transition, the press must have bounced, drop it.
|
||||
if (ui.getUiState()->frameState == FIXED) {
|
||||
ui.nextFrame();
|
||||
if (ui->getUiState()->frameState == FIXED) {
|
||||
ui->nextFrame();
|
||||
lastScreenTransition = millis();
|
||||
setFastFramerate();
|
||||
}
|
||||
@@ -1466,7 +1500,7 @@ void Screen::setFastFramerate()
|
||||
// We are about to start a transition so speed up fps
|
||||
targetFramerate = SCREEN_TRANSITION_FRAMERATE;
|
||||
|
||||
ui.setTargetFPS(targetFramerate);
|
||||
ui->setTargetFPS(targetFramerate);
|
||||
setInterval(0); // redraw ASAP
|
||||
runASAP = true;
|
||||
}
|
||||
@@ -1553,7 +1587,8 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS)) && \
|
||||
// TODO: Raspberry Pi supports more than just the one screen size
|
||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || ARCH_RASPBERRY_PI) && \
|
||||
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
||||
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8,
|
||||
imgInfoL1);
|
||||
@@ -1580,7 +1615,7 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
||||
// Jm
|
||||
void DebugInfo::drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
#if HAS_WIFI
|
||||
#if HAS_WIFI && !defined(ARCH_RASPBERRY_PI)
|
||||
const char *wifiName = config.network.wifi_ssid;
|
||||
|
||||
display->setFont(FONT_SMALL);
|
||||
@@ -1634,69 +1669,19 @@ void DebugInfo::drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, i
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Connection Failed");
|
||||
} else if (WiFi.status() == WL_IDLE_STATUS) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Idle ... Reconnecting");
|
||||
} else {
|
||||
}
|
||||
#ifdef ARCH_ESP32
|
||||
else {
|
||||
// Codes:
|
||||
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html#wi-fi-reason-code
|
||||
if (getWifiDisconnectReason() == 2) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Authentication Invalid");
|
||||
} else if (getWifiDisconnectReason() == 3) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "De-authenticated");
|
||||
} else if (getWifiDisconnectReason() == 4) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Disassociated Expired");
|
||||
} else if (getWifiDisconnectReason() == 5) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "AP - Too Many Clients");
|
||||
} else if (getWifiDisconnectReason() == 6) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "NOT_AUTHED");
|
||||
} else if (getWifiDisconnectReason() == 7) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "NOT_ASSOCED");
|
||||
} else if (getWifiDisconnectReason() == 8) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Disassociated");
|
||||
} else if (getWifiDisconnectReason() == 9) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "ASSOC_NOT_AUTHED");
|
||||
} else if (getWifiDisconnectReason() == 10) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "DISASSOC_PWRCAP_BAD");
|
||||
} else if (getWifiDisconnectReason() == 11) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "DISASSOC_SUPCHAN_BAD");
|
||||
} else if (getWifiDisconnectReason() == 13) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "IE_INVALID");
|
||||
} else if (getWifiDisconnectReason() == 14) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "MIC_FAILURE");
|
||||
} else if (getWifiDisconnectReason() == 15) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "AP Handshake Timeout");
|
||||
} else if (getWifiDisconnectReason() == 16) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "GROUP_KEY_UPDATE_TIMEOUT");
|
||||
} else if (getWifiDisconnectReason() == 17) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "IE_IN_4WAY_DIFFERS");
|
||||
} else if (getWifiDisconnectReason() == 18) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Invalid Group Cipher");
|
||||
} else if (getWifiDisconnectReason() == 19) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Invalid Pairwise Cipher");
|
||||
} else if (getWifiDisconnectReason() == 20) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "AKMP_INVALID");
|
||||
} else if (getWifiDisconnectReason() == 21) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "UNSUPP_RSN_IE_VERSION");
|
||||
} else if (getWifiDisconnectReason() == 22) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "INVALID_RSN_IE_CAP");
|
||||
} else if (getWifiDisconnectReason() == 23) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "802_1X_AUTH_FAILED");
|
||||
} else if (getWifiDisconnectReason() == 24) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "CIPHER_SUITE_REJECTED");
|
||||
} else if (getWifiDisconnectReason() == 200) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "BEACON_TIMEOUT");
|
||||
} else if (getWifiDisconnectReason() == 201) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "AP Not Found");
|
||||
} else if (getWifiDisconnectReason() == 202) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "AUTH_FAIL");
|
||||
} else if (getWifiDisconnectReason() == 203) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "ASSOC_FAIL");
|
||||
} else if (getWifiDisconnectReason() == 204) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "HANDSHAKE_TIMEOUT");
|
||||
} else if (getWifiDisconnectReason() == 205) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Connection Failed");
|
||||
} else {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Unknown Status");
|
||||
}
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1,
|
||||
WiFi.disconnectReasonName(static_cast<wifi_err_reason_t>(getWifiDisconnectReason())));
|
||||
}
|
||||
#else
|
||||
else {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Unkown status: " + String(WiFi.status()));
|
||||
}
|
||||
#endif
|
||||
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 2, "SSID: " + String(wifiName));
|
||||
|
||||
@@ -1742,37 +1727,7 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
|
||||
display->drawString(x + 1, y, String("USB"));
|
||||
}
|
||||
|
||||
auto mode = "";
|
||||
|
||||
switch (config.lora.modem_preset) {
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW:
|
||||
mode = "ShortS";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST:
|
||||
mode = "ShortF";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW:
|
||||
mode = "MedS";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST:
|
||||
mode = "MedF";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW:
|
||||
mode = "LongS";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST:
|
||||
mode = "LongF";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE:
|
||||
mode = "LongM";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_VERY_LONG_SLOW:
|
||||
mode = "VeryL";
|
||||
break;
|
||||
default:
|
||||
mode = "Custom";
|
||||
break;
|
||||
}
|
||||
auto mode = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, true);
|
||||
|
||||
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode), y, mode);
|
||||
if (config.display.heading_bold)
|
||||
@@ -1839,23 +1794,6 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
|
||||
heartbeat = !heartbeat;
|
||||
#endif
|
||||
}
|
||||
// adjust Brightness cycle through 1 to 254 as long as attachDuringLongPress is true
|
||||
void Screen::adjustBrightness()
|
||||
{
|
||||
if (!useDisplay)
|
||||
return;
|
||||
|
||||
if (brightness == 254) {
|
||||
brightness = 0;
|
||||
} else {
|
||||
brightness++;
|
||||
}
|
||||
int width = brightness / (254.00 / SCREEN_WIDTH);
|
||||
dispdev.drawRect(0, 30, SCREEN_WIDTH, 4);
|
||||
dispdev.fillRect(0, 31, width, 2);
|
||||
dispdev.display();
|
||||
dispdev.setBrightness(brightness);
|
||||
}
|
||||
|
||||
int Screen::handleStatusUpdate(const meshtastic::Status *arg)
|
||||
{
|
||||
@@ -1890,7 +1828,7 @@ int Screen::handleUIFrameEvent(const UIFrameEvent *event)
|
||||
setFastFramerate();
|
||||
// TODO: We might also want switch to corresponding frame,
|
||||
// but we don't know the exact frame number.
|
||||
// ui.switchToFrame(0);
|
||||
// ui->switchToFrame(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1900,7 +1838,7 @@ int Screen::handleUIFrameEvent(const UIFrameEvent *event)
|
||||
int Screen::handleInputEvent(const InputEvent *event)
|
||||
{
|
||||
if (showingNormalScreen && moduleFrames.size() == 0) {
|
||||
LOG_DEBUG("Screen::handleInputEvent from %s\n", event->source);
|
||||
// LOG_DEBUG("Screen::handleInputEvent from %s\n", event->source);
|
||||
if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) {
|
||||
showPrevFrame();
|
||||
} else if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT)) {
|
||||
|
||||
@@ -19,7 +19,6 @@ class Screen
|
||||
void setup() {}
|
||||
void setOn(bool) {}
|
||||
void print(const char *) {}
|
||||
void adjustBrightness() {}
|
||||
void doDeepSleep() {}
|
||||
void forceDisplay() {}
|
||||
void startBluetoothPinScreen(uint32_t pin) {}
|
||||
@@ -161,7 +160,6 @@ class Screen : public concurrency::OSThread
|
||||
void showNextFrame() { enqueueCmd(ScreenCmd{.cmd = Cmd::SHOW_NEXT_FRAME}); }
|
||||
|
||||
// Implementation to Adjust Brightness
|
||||
void adjustBrightness();
|
||||
uint8_t brightness = BRIGHTNESS_DEFAULT;
|
||||
|
||||
/// Starts showing the Bluetooth PIN screen.
|
||||
@@ -326,6 +324,8 @@ class Screen : public concurrency::OSThread
|
||||
// Called periodically from the main loop.
|
||||
int32_t runOnce() final;
|
||||
|
||||
bool isAUTOOled = false;
|
||||
|
||||
private:
|
||||
struct ScreenCmd {
|
||||
Cmd cmd;
|
||||
@@ -363,6 +363,9 @@ class Screen : public concurrency::OSThread
|
||||
/// Try to start drawing ASAP
|
||||
void setFastFramerate();
|
||||
|
||||
// Sets frame up for immediate drawing
|
||||
void setFrameImmediateDraw(FrameCallback *drawFrames);
|
||||
|
||||
/// Called when debug screen is to be drawn, calls through to debugInfo.drawFrame.
|
||||
static void drawDebugInfoTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||
|
||||
@@ -384,22 +387,10 @@ class Screen : public concurrency::OSThread
|
||||
DebugInfo debugInfo;
|
||||
|
||||
/// Display device
|
||||
OLEDDisplay *dispdev;
|
||||
|
||||
#if defined(USE_SH1106) || defined(USE_SH1107) || defined(USE_SH1107_128_64)
|
||||
SH1106Wire dispdev;
|
||||
#elif defined(USE_SSD1306)
|
||||
SSD1306Wire dispdev;
|
||||
#elif defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ST7789_CS)
|
||||
TFTDisplay dispdev;
|
||||
#elif defined(USE_EINK)
|
||||
EInkDisplay dispdev;
|
||||
#elif defined(USE_ST7567)
|
||||
ST7567Wire dispdev;
|
||||
#else
|
||||
AutoOLEDWire dispdev;
|
||||
#endif
|
||||
/// UI helper for rendering to frames and switching between them
|
||||
OLEDDisplayUi ui;
|
||||
OLEDDisplayUi *ui;
|
||||
};
|
||||
|
||||
} // namespace graphics
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
#if ARCH_RASPBERRY_PI
|
||||
#include "platform/portduino/PortduinoGlue.h"
|
||||
#endif
|
||||
|
||||
#ifndef TFT_BACKLIGHT_ON
|
||||
#define TFT_BACKLIGHT_ON HIGH
|
||||
#endif
|
||||
|
||||
// convert 24-bit color to 16-bit (56K)
|
||||
#define COLOR565(r, g, b) (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3))
|
||||
#ifndef TFT_MESH
|
||||
#define TFT_MESH COLOR565(0x67, 0xEA, 0x94)
|
||||
#endif
|
||||
|
||||
#if defined(ST7735S)
|
||||
#include <LovyanGFX.hpp> // Graphics and font library for ST7735 driver chip
|
||||
@@ -81,8 +85,16 @@ class LGFX : public lgfx::LGFX_Device
|
||||
{
|
||||
auto cfg = _light_instance.config(); // Gets a structure for backlight settings.
|
||||
|
||||
#ifdef ST7735_BL_V03
|
||||
if (heltec_version == 3) {
|
||||
cfg.pin_bl = ST7735_BL_V03;
|
||||
} else {
|
||||
cfg.pin_bl = ST7735_BL_V05;
|
||||
}
|
||||
#else
|
||||
cfg.pin_bl = ST7735_BL; // Pin number to which the backlight is connected
|
||||
cfg.invert = true; // true to invert the brightness of the backlight
|
||||
#endif
|
||||
cfg.invert = true; // true to invert the brightness of the backlight
|
||||
// cfg.freq = 44100; // PWM frequency of backlight
|
||||
// cfg.pwm_channel = 1; // PWM channel number to use
|
||||
|
||||
@@ -94,7 +106,11 @@ class LGFX : public lgfx::LGFX_Device
|
||||
}
|
||||
};
|
||||
|
||||
static LGFX tft;
|
||||
static LGFX *tft = nullptr;
|
||||
|
||||
#elif defined(RAK14014)
|
||||
#include <TFT_eSPI.h>
|
||||
TFT_eSPI *tft = nullptr;
|
||||
|
||||
#elif defined(ST7789_CS)
|
||||
#include <LovyanGFX.hpp> // Graphics and font library for ST7735 driver chip
|
||||
@@ -108,10 +124,12 @@ class LGFX : public lgfx::LGFX_Device
|
||||
lgfx::Panel_ST7789 _panel_instance;
|
||||
lgfx::Bus_SPI _bus_instance;
|
||||
lgfx::Light_PWM _light_instance;
|
||||
#if HAS_TOUCHSCREEN
|
||||
#ifdef T_WATCH_S3
|
||||
lgfx::Touch_FT5x06 _touch_instance;
|
||||
#else
|
||||
lgfx::Touch_GT911 _touch_instance;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
public:
|
||||
@@ -126,13 +144,14 @@ class LGFX : public lgfx::LGFX_Device
|
||||
cfg.freq_write = SPI_FREQUENCY; // SPI clock for transmission (up to 80MHz, rounded to the value obtained by dividing
|
||||
// 80MHz by an integer)
|
||||
cfg.freq_read = SPI_READ_FREQUENCY; // SPI clock when receiving
|
||||
cfg.spi_3wire = false; // Set to true if reception is done on the MOSI pin
|
||||
cfg.use_lock = true; // Set to true to use transaction locking
|
||||
cfg.dma_channel = SPI_DMA_CH_AUTO; // SPI_DMA_CH_AUTO; // Set DMA channel to use (0=not use DMA / 1=1ch / 2=ch /
|
||||
cfg.pin_sclk = ST7789_SCK; // Set SPI SCLK pin number
|
||||
cfg.pin_mosi = ST7789_SDA; // Set SPI MOSI pin number
|
||||
cfg.pin_miso = ST7789_MISO; // Set SPI MISO pin number (-1 = disable)
|
||||
cfg.pin_dc = ST7789_RS; // Set SPI DC pin number (-1 = disable)
|
||||
cfg.spi_3wire = false;
|
||||
cfg.use_lock = true; // Set to true to use transaction locking
|
||||
cfg.dma_channel = SPI_DMA_CH_AUTO; // SPI_DMA_CH_AUTO; // Set DMA channel to use (0=not use DMA / 1=1ch / 2=ch /
|
||||
// SPI_DMA_CH_AUTO=auto setting)
|
||||
cfg.pin_sclk = ST7789_SCK; // Set SPI SCLK pin number
|
||||
cfg.pin_mosi = ST7789_SDA; // Set SPI MOSI pin number
|
||||
cfg.pin_miso = ST7789_MISO; // Set SPI MISO pin number (-1 = disable)
|
||||
cfg.pin_dc = ST7789_RS; // Set SPI DC pin number (-1 = disable)
|
||||
|
||||
_bus_instance.config(cfg); // applies the set value to the bus.
|
||||
_panel_instance.setBus(&_bus_instance); // set the bus on the panel.
|
||||
@@ -148,16 +167,16 @@ class LGFX : public lgfx::LGFX_Device
|
||||
// The following setting values are general initial values for each panel, so please comment out any
|
||||
// unknown items and try them.
|
||||
|
||||
cfg.panel_width = TFT_WIDTH; // actual displayable width
|
||||
cfg.panel_height = TFT_HEIGHT; // actual displayable height
|
||||
cfg.offset_x = TFT_OFFSET_X; // Panel offset amount in X direction
|
||||
cfg.offset_y = TFT_OFFSET_Y; // Panel offset amount in Y direction
|
||||
cfg.offset_rotation = 0; // Rotation direction value offset 0~7 (4~7 is mirrored)
|
||||
cfg.dummy_read_pixel = 9; // Number of bits for dummy read before pixel readout
|
||||
cfg.dummy_read_bits = 1; // Number of bits for dummy read before non-pixel data read
|
||||
cfg.readable = true; // Set to true if data can be read
|
||||
cfg.invert = true; // Set to true if the light/darkness of the panel is reversed
|
||||
cfg.rgb_order = false; // Set to true if the panel's red and blue are swapped
|
||||
cfg.panel_width = TFT_WIDTH; // actual displayable width
|
||||
cfg.panel_height = TFT_HEIGHT; // actual displayable height
|
||||
cfg.offset_x = TFT_OFFSET_X; // Panel offset amount in X direction
|
||||
cfg.offset_y = TFT_OFFSET_Y; // Panel offset amount in Y direction
|
||||
cfg.offset_rotation = TFT_OFFSET_ROTATION; // Rotation direction value offset 0~7 (4~7 is mirrored)
|
||||
cfg.dummy_read_pixel = 9; // Number of bits for dummy read before pixel readout
|
||||
cfg.dummy_read_bits = 1; // Number of bits for dummy read before non-pixel data read
|
||||
cfg.readable = true; // Set to true if data can be read
|
||||
cfg.invert = true; // Set to true if the light/darkness of the panel is reversed
|
||||
cfg.rgb_order = false; // Set to true if the panel's red and blue are swapped
|
||||
cfg.dlen_16bit =
|
||||
false; // Set to true for panels that transmit data length in 16-bit units with 16-bit parallel or SPI
|
||||
cfg.bus_shared = true; // If the bus is shared with the SD card, set to true (bus control with drawJpgFile etc.)
|
||||
@@ -181,6 +200,7 @@ class LGFX : public lgfx::LGFX_Device
|
||||
_panel_instance.setLight(&_light_instance); // Set the backlight on the panel.
|
||||
}
|
||||
|
||||
#if HAS_TOUCHSCREEN
|
||||
// Configure settings for touch screen control.
|
||||
{
|
||||
auto cfg = _touch_instance.config();
|
||||
@@ -192,7 +212,7 @@ class LGFX : public lgfx::LGFX_Device
|
||||
cfg.y_max = TFT_WIDTH - 1;
|
||||
cfg.pin_int = SCREEN_TOUCH_INT;
|
||||
cfg.bus_shared = true;
|
||||
cfg.offset_rotation = 0;
|
||||
cfg.offset_rotation = TFT_OFFSET_ROTATION;
|
||||
// cfg.freq = 2500000;
|
||||
|
||||
// I2C
|
||||
@@ -210,12 +230,13 @@ class LGFX : public lgfx::LGFX_Device
|
||||
_touch_instance.config(cfg);
|
||||
_panel_instance.setTouch(&_touch_instance);
|
||||
}
|
||||
#endif
|
||||
|
||||
setPanel(&_panel_instance); // Sets the panel to use.
|
||||
}
|
||||
};
|
||||
|
||||
static LGFX tft;
|
||||
static LGFX *tft = nullptr;
|
||||
|
||||
#elif defined(ILI9341_DRIVER)
|
||||
|
||||
@@ -304,23 +325,96 @@ class LGFX : public lgfx::LGFX_Device
|
||||
}
|
||||
};
|
||||
|
||||
static LGFX tft;
|
||||
static LGFX *tft = nullptr;
|
||||
|
||||
#elif defined(ST7735_CS)
|
||||
#include <TFT_eSPI.h> // Graphics and font library for ILI9341 driver chip
|
||||
|
||||
static TFT_eSPI tft = TFT_eSPI(); // Invoke library, pins defined in User_Setup.h
|
||||
static TFT_eSPI *tft = nullptr; // Invoke library, pins defined in User_Setup.h
|
||||
#elif ARCH_RASPBERRY_PI
|
||||
#include <LovyanGFX.hpp> // Graphics and font library for ST7735 driver chip
|
||||
|
||||
class LGFX : public lgfx::LGFX_Device
|
||||
{
|
||||
lgfx::Panel_LCD *_panel_instance;
|
||||
lgfx::Bus_SPI _bus_instance;
|
||||
|
||||
lgfx::ITouch *_touch_instance;
|
||||
|
||||
public:
|
||||
LGFX(void)
|
||||
{
|
||||
|
||||
_panel_instance = new lgfx::Panel_ST7789;
|
||||
auto buscfg = _bus_instance.config();
|
||||
buscfg.spi_mode = 0;
|
||||
|
||||
buscfg.pin_dc = settingsMap[displayDC]; // Set SPI DC pin number (-1 = disable)
|
||||
|
||||
_bus_instance.config(buscfg); // applies the set value to the bus.
|
||||
_panel_instance->setBus(&_bus_instance); // set the bus on the panel.
|
||||
|
||||
auto cfg = _panel_instance->config(); // Gets a structure for display panel settings.
|
||||
LOG_DEBUG("Height: %d, Width: %d \n", settingsMap[displayHeight], settingsMap[displayWidth]);
|
||||
cfg.pin_cs = settingsMap[displayCS]; // Pin number where CS is connected (-1 = disable)
|
||||
cfg.panel_width = settingsMap[displayWidth]; // actual displayable width
|
||||
cfg.panel_height = settingsMap[displayHeight]; // actual displayable height
|
||||
cfg.offset_x = 0; // Panel offset amount in X direction
|
||||
cfg.offset_y = 0; // Panel offset amount in Y direction
|
||||
cfg.offset_rotation = 0; // Rotation direction value offset 0~7 (4~7 is mirrored)
|
||||
cfg.dummy_read_pixel = 9; // Number of bits for dummy read before pixel readout
|
||||
cfg.dummy_read_bits = 1; // Number of bits for dummy read before non-pixel data read
|
||||
cfg.readable = true; // Set to true if data can be read
|
||||
cfg.invert = true; // Set to true if the light/darkness of the panel is reversed
|
||||
cfg.rgb_order = false; // Set to true if the panel's red and blue are swapped
|
||||
cfg.dlen_16bit = false; // Set to true for panels that transmit data length in 16-bit units with 16-bit parallel or SPI
|
||||
cfg.bus_shared = true; // If the bus is shared with the SD card, set to true (bus control with drawJpgFile etc.)
|
||||
|
||||
_panel_instance->config(cfg);
|
||||
|
||||
// Configure settings for touch control.
|
||||
if (settingsMap[touchscreenModule]) {
|
||||
if (settingsMap[touchscreenModule] == xpt2046) {
|
||||
_touch_instance = new lgfx::Touch_XPT2046;
|
||||
}
|
||||
auto touch_cfg = _touch_instance->config();
|
||||
|
||||
touch_cfg.pin_cs = settingsMap[touchscreenCS];
|
||||
touch_cfg.x_min = 0;
|
||||
touch_cfg.x_max = settingsMap[displayHeight] - 1;
|
||||
touch_cfg.y_min = 0;
|
||||
touch_cfg.y_max = settingsMap[displayWidth] - 1;
|
||||
touch_cfg.pin_int = settingsMap[touchscreenIRQ];
|
||||
touch_cfg.bus_shared = true;
|
||||
touch_cfg.offset_rotation = 1;
|
||||
|
||||
_touch_instance->config(touch_cfg);
|
||||
_panel_instance->setTouch(_touch_instance);
|
||||
}
|
||||
|
||||
setPanel(_panel_instance); // Sets the panel to use.
|
||||
}
|
||||
};
|
||||
|
||||
static LGFX *tft = nullptr;
|
||||
#endif
|
||||
|
||||
#if defined(ST7735_CS) || defined(ST7789_CS) || defined(ILI9341_DRIVER)
|
||||
#if defined(ST7735_CS) || defined(ST7789_CS) || defined(ILI9341_DRIVER) || defined(RAK14014) || ARCH_RASPBERRY_PI
|
||||
#include "SPILock.h"
|
||||
#include "TFTDisplay.h"
|
||||
#include <SPI.h>
|
||||
|
||||
TFTDisplay::TFTDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY geometry, HW_I2C i2cBus)
|
||||
{
|
||||
#ifdef SCREEN_ROTATE
|
||||
LOG_DEBUG("TFTDisplay!\n");
|
||||
#if ARCH_RASPBERRY_PI
|
||||
if (settingsMap[displayRotate]) {
|
||||
setGeometry(GEOMETRY_RAWMODE, settingsMap[configNames::displayHeight], settingsMap[configNames::displayWidth]);
|
||||
} else {
|
||||
setGeometry(GEOMETRY_RAWMODE, settingsMap[configNames::displayWidth], settingsMap[configNames::displayHeight]);
|
||||
}
|
||||
|
||||
#elif defined(SCREEN_ROTATE)
|
||||
setGeometry(GEOMETRY_RAWMODE, TFT_HEIGHT, TFT_WIDTH);
|
||||
#else
|
||||
setGeometry(GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT);
|
||||
@@ -328,19 +422,25 @@ TFTDisplay::TFTDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY g
|
||||
}
|
||||
|
||||
// Write the buffer to the display memory
|
||||
void TFTDisplay::display(void)
|
||||
void TFTDisplay::display(bool fromBlank)
|
||||
{
|
||||
if (fromBlank)
|
||||
tft->clear();
|
||||
concurrency::LockGuard g(spiLock);
|
||||
|
||||
uint16_t x, y;
|
||||
|
||||
for (y = 0; y < displayHeight; y++) {
|
||||
for (x = 0; x < displayWidth; x++) {
|
||||
// get src pixel in the page based ordering the OLED lib uses FIXME, super inefficent
|
||||
auto isset = buffer[x + (y / 8) * displayWidth] & (1 << (y & 7));
|
||||
auto dblbuf_isset = buffer_back[x + (y / 8) * displayWidth] & (1 << (y & 7));
|
||||
if (isset != dblbuf_isset) {
|
||||
tft.drawPixel(x, y, isset ? TFT_MESH : TFT_BLACK);
|
||||
if (!fromBlank) {
|
||||
// get src pixel in the page based ordering the OLED lib uses FIXME, super inefficent
|
||||
auto dblbuf_isset = buffer_back[x + (y / 8) * displayWidth] & (1 << (y & 7));
|
||||
if (isset != dblbuf_isset) {
|
||||
tft->drawPixel(x, y, isset ? TFT_MESH : TFT_BLACK);
|
||||
}
|
||||
} else if (isset) {
|
||||
tft->drawPixel(x, y, TFT_MESH);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -359,26 +459,65 @@ void TFTDisplay::sendCommand(uint8_t com)
|
||||
// handle display on/off directly
|
||||
switch (com) {
|
||||
case DISPLAYON: {
|
||||
#if ARCH_RASPBERRY_PI
|
||||
display(true);
|
||||
if (settingsMap[displayBacklight] > 0)
|
||||
digitalWrite(settingsMap[displayBacklight], TFT_BACKLIGHT_ON);
|
||||
#elif defined(ST7735_BACKLIGHT_EN_V03) && defined(TFT_BACKLIGHT_ON)
|
||||
if (heltec_version == 3) {
|
||||
digitalWrite(ST7735_BACKLIGHT_EN_V03, TFT_BACKLIGHT_ON);
|
||||
} else {
|
||||
digitalWrite(ST7735_BACKLIGHT_EN_V05, TFT_BACKLIGHT_ON);
|
||||
}
|
||||
#endif
|
||||
#if defined(TFT_BL) && defined(TFT_BACKLIGHT_ON)
|
||||
digitalWrite(TFT_BL, TFT_BACKLIGHT_ON);
|
||||
#endif
|
||||
#ifdef VTFT_CTRL_V03
|
||||
if (heltec_version == 3) {
|
||||
digitalWrite(VTFT_CTRL_V03, LOW);
|
||||
} else {
|
||||
digitalWrite(VTFT_CTRL_V05, LOW);
|
||||
}
|
||||
#endif
|
||||
#ifdef VTFT_CTRL
|
||||
digitalWrite(VTFT_CTRL, LOW);
|
||||
#endif
|
||||
#ifndef M5STACK
|
||||
tft.setBrightness(128);
|
||||
|
||||
#ifdef RAK14014
|
||||
#elif !defined(M5STACK)
|
||||
tft->setBrightness(128);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case DISPLAYOFF: {
|
||||
#if ARCH_RASPBERRY_PI
|
||||
tft->clear();
|
||||
if (settingsMap[displayBacklight] > 0)
|
||||
digitalWrite(settingsMap[displayBacklight], !TFT_BACKLIGHT_ON);
|
||||
#elif defined(ST7735_BACKLIGHT_EN_V03) && defined(TFT_BACKLIGHT_ON)
|
||||
if (heltec_version == 3) {
|
||||
digitalWrite(ST7735_BACKLIGHT_EN_V03, !TFT_BACKLIGHT_ON);
|
||||
} else {
|
||||
digitalWrite(ST7735_BACKLIGHT_EN_V05, !TFT_BACKLIGHT_ON);
|
||||
}
|
||||
#endif
|
||||
#if defined(TFT_BL) && defined(TFT_BACKLIGHT_ON)
|
||||
digitalWrite(TFT_BL, !TFT_BACKLIGHT_ON);
|
||||
#endif
|
||||
#ifdef VTFT_CTRL_V03
|
||||
if (heltec_version == 3) {
|
||||
digitalWrite(VTFT_CTRL_V03, HIGH);
|
||||
} else {
|
||||
digitalWrite(VTFT_CTRL_V05, HIGH);
|
||||
}
|
||||
#endif
|
||||
#ifdef VTFT_CTRL
|
||||
digitalWrite(VTFT_CTRL, HIGH);
|
||||
#endif
|
||||
#ifndef M5STACK
|
||||
tft.setBrightness(0);
|
||||
#ifdef RAK14014
|
||||
#elif !defined(M5STACK)
|
||||
tft->setBrightness(0);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@@ -389,10 +528,19 @@ void TFTDisplay::sendCommand(uint8_t com)
|
||||
// Drop all other commands to device (we just update the buffer)
|
||||
}
|
||||
|
||||
void TFTDisplay::flipScreenVertically()
|
||||
{
|
||||
#if defined(T_WATCH_S3)
|
||||
LOG_DEBUG("Flip TFT vertically\n"); // T-Watch S3 right-handed orientation
|
||||
tft->setRotation(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool TFTDisplay::hasTouch(void)
|
||||
{
|
||||
#ifndef M5STACK
|
||||
return tft.touch() != nullptr;
|
||||
#ifdef RAK14014
|
||||
#elif !defined(M5STACK)
|
||||
return tft->touch() != nullptr;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
@@ -400,8 +548,9 @@ bool TFTDisplay::hasTouch(void)
|
||||
|
||||
bool TFTDisplay::getTouch(int16_t *x, int16_t *y)
|
||||
{
|
||||
#ifndef M5STACK
|
||||
return tft.getTouch(x, y);
|
||||
#ifdef RAK14014
|
||||
#elif !defined(M5STACK)
|
||||
return tft->getTouch(x, y);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
@@ -417,21 +566,47 @@ bool TFTDisplay::connect()
|
||||
{
|
||||
concurrency::LockGuard g(spiLock);
|
||||
LOG_INFO("Doing TFT init\n");
|
||||
#ifdef RAK14014
|
||||
tft = new TFT_eSPI;
|
||||
#else
|
||||
tft = new LGFX;
|
||||
#endif
|
||||
|
||||
#ifdef TFT_BL
|
||||
digitalWrite(TFT_BL, TFT_BACKLIGHT_ON);
|
||||
pinMode(TFT_BL, OUTPUT);
|
||||
digitalWrite(TFT_BL, TFT_BACKLIGHT_ON);
|
||||
// pinMode(PIN_3V3_EN, OUTPUT);
|
||||
// digitalWrite(PIN_3V3_EN, HIGH);
|
||||
LOG_INFO("Power to TFT Backlight\n");
|
||||
#endif
|
||||
|
||||
tft.init();
|
||||
#if defined(T_DECK)
|
||||
tft.setRotation(1); // M5Stack/T-Deck have the TFT in landscape
|
||||
#elif defined(M5STACK) || defined(T_WATCH_S3)
|
||||
tft.setRotation(0); // T-Watch S3 has the TFT in portrait
|
||||
#else
|
||||
tft.setRotation(3); // Orient horizontal and wide underneath the silkscreen name label
|
||||
#ifdef ST7735_BACKLIGHT_EN_V03
|
||||
if (heltec_version == 3) {
|
||||
pinMode(ST7735_BACKLIGHT_EN_V03, OUTPUT);
|
||||
digitalWrite(ST7735_BACKLIGHT_EN_V03, TFT_BACKLIGHT_ON);
|
||||
} else {
|
||||
pinMode(ST7735_BACKLIGHT_EN_V05, OUTPUT);
|
||||
digitalWrite(ST7735_BACKLIGHT_EN_V05, TFT_BACKLIGHT_ON);
|
||||
}
|
||||
#endif
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
|
||||
tft->init();
|
||||
|
||||
#if defined(M5STACK)
|
||||
tft->setRotation(0);
|
||||
#elif defined(RAK14014)
|
||||
tft->setRotation(1);
|
||||
tft->setSwapBytes(true);
|
||||
// tft->fillScreen(TFT_BLACK);
|
||||
#elif defined(T_DECK) || defined(PICOMPUTER_S3)
|
||||
tft->setRotation(1); // T-Deck has the TFT in landscape
|
||||
#elif defined(T_WATCH_S3)
|
||||
tft->setRotation(2); // T-Watch S3 left-handed orientation
|
||||
#else
|
||||
tft->setRotation(3); // Orient horizontal and wide underneath the silkscreen name label
|
||||
#endif
|
||||
tft->fillScreen(TFT_BLACK);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,11 @@ class TFTDisplay : public OLEDDisplay
|
||||
TFTDisplay(uint8_t, int, int, OLEDDISPLAY_GEOMETRY, HW_I2C);
|
||||
|
||||
// Write the buffer to the display memory
|
||||
virtual void display(void) override;
|
||||
virtual void display() override { display(false); };
|
||||
virtual void display(bool fromBlank);
|
||||
|
||||
// Turn the display upside down
|
||||
virtual void flipScreenVertically();
|
||||
|
||||
// Touch screen (static handlers)
|
||||
static bool hasTouch(void);
|
||||
|
||||
@@ -14,7 +14,7 @@ const uint8_t imgUser[] PROGMEM = {0x3C, 0x42, 0x99, 0xA5, 0xA5, 0x99, 0x42, 0x3
|
||||
const uint8_t imgPositionEmpty[] PROGMEM = {0x20, 0x30, 0x28, 0x24, 0x42, 0xFF};
|
||||
const uint8_t imgPositionSolid[] PROGMEM = {0x20, 0x30, 0x38, 0x3C, 0x7E, 0xFF};
|
||||
|
||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS)) && \
|
||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || ARCH_RASPBERRY_PI) && \
|
||||
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
||||
const uint8_t imgQuestionL1[] PROGMEM = {0xff, 0x01, 0x01, 0x32, 0x7b, 0x49, 0x49, 0x6f, 0x26, 0x01, 0x01, 0xff};
|
||||
const uint8_t imgQuestionL2[] PROGMEM = {0x0f, 0x08, 0x08, 0x08, 0x06, 0x0f, 0x0f, 0x06, 0x08, 0x08, 0x08, 0x0f};
|
||||
@@ -30,4 +30,4 @@ const uint8_t imgQuestion[] PROGMEM = {0xbf, 0x41, 0xc0, 0x8b, 0xdb, 0x70, 0xa1,
|
||||
const uint8_t imgSF[] PROGMEM = {0xd2, 0xb7, 0xad, 0xbb, 0x92, 0x01, 0xfd, 0xfd, 0x15, 0x85, 0xf5};
|
||||
#endif
|
||||
|
||||
#include "img/icon.xbm"
|
||||
#include "img/icon.xbm"
|
||||
181
src/input/BBQ10Keyboard.cpp
Normal file
181
src/input/BBQ10Keyboard.cpp
Normal file
@@ -0,0 +1,181 @@
|
||||
// Based on arturo182 arduino_bbq10kbd library https://github.com/arturo182/arduino_bbq10kbd
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "BBQ10Keyboard.h"
|
||||
|
||||
#define _REG_VER 1
|
||||
#define _REG_CFG 2
|
||||
#define _REG_INT 3
|
||||
#define _REG_KEY 4
|
||||
#define _REG_BKL 5
|
||||
#define _REG_DEB 6
|
||||
#define _REG_FRQ 7
|
||||
#define _REG_RST 8
|
||||
#define _REG_FIF 9
|
||||
|
||||
#define _WRITE_MASK (1 << 7)
|
||||
|
||||
#define CFG_OVERFLOW_ON (1 << 0)
|
||||
#define CFG_OVERFLOW_INT (1 << 1)
|
||||
#define CFG_CAPSLOCK_INT (1 << 2)
|
||||
#define CFG_NUMLOCK_INT (1 << 3)
|
||||
#define CFG_KEY_INT (1 << 4)
|
||||
#define CFG_PANIC_INT (1 << 5)
|
||||
#define CFG_REPORT_MODS (1 << 6)
|
||||
#define CFG_USE_MODS (1 << 7)
|
||||
|
||||
#define INT_OVERFLOW (1 << 0)
|
||||
#define INT_CAPSLOCK (1 << 1)
|
||||
#define INT_NUMLOCK (1 << 2)
|
||||
#define INT_KEY (1 << 3)
|
||||
#define INT_PANIC (1 << 4)
|
||||
|
||||
#define KEY_CAPSLOCK (1 << 5)
|
||||
#define KEY_NUMLOCK (1 << 6)
|
||||
#define KEY_COUNT_MASK (0x1F)
|
||||
|
||||
BBQ10Keyboard::BBQ10Keyboard() : m_wire(nullptr), m_addr(0), readCallback(nullptr), writeCallback(nullptr) {}
|
||||
|
||||
void BBQ10Keyboard::begin(uint8_t addr, TwoWire *wire)
|
||||
{
|
||||
m_addr = addr;
|
||||
m_wire = wire;
|
||||
|
||||
m_wire->begin();
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void BBQ10Keyboard::begin(i2c_com_fptr_t r, i2c_com_fptr_t w, uint8_t addr)
|
||||
{
|
||||
m_addr = addr;
|
||||
m_wire = nullptr;
|
||||
writeCallback = w;
|
||||
readCallback = r;
|
||||
reset();
|
||||
}
|
||||
|
||||
void BBQ10Keyboard::reset()
|
||||
{
|
||||
if (m_wire) {
|
||||
m_wire->beginTransmission(m_addr);
|
||||
m_wire->write(_REG_RST);
|
||||
m_wire->endTransmission();
|
||||
}
|
||||
if (writeCallback) {
|
||||
uint8_t data = 0;
|
||||
writeCallback(m_addr, _REG_RST, &data, 0);
|
||||
}
|
||||
delay(100);
|
||||
writeRegister(_REG_CFG, readRegister8(_REG_CFG) | CFG_REPORT_MODS);
|
||||
delay(100);
|
||||
}
|
||||
|
||||
void BBQ10Keyboard::attachInterrupt(uint8_t pin, void (*func)(void)) const
|
||||
{
|
||||
pinMode(pin, INPUT_PULLUP);
|
||||
::attachInterrupt(digitalPinToInterrupt(pin), func, RISING);
|
||||
}
|
||||
|
||||
void BBQ10Keyboard::detachInterrupt(uint8_t pin) const
|
||||
{
|
||||
::detachInterrupt(pin);
|
||||
}
|
||||
|
||||
void BBQ10Keyboard::clearInterruptStatus()
|
||||
{
|
||||
writeRegister(_REG_INT, 0x00);
|
||||
}
|
||||
|
||||
uint8_t BBQ10Keyboard::status() const
|
||||
{
|
||||
return readRegister8(_REG_KEY);
|
||||
}
|
||||
|
||||
uint8_t BBQ10Keyboard::keyCount() const
|
||||
{
|
||||
return status() & KEY_COUNT_MASK;
|
||||
}
|
||||
|
||||
BBQ10Keyboard::KeyEvent BBQ10Keyboard::keyEvent() const
|
||||
{
|
||||
KeyEvent event = {.key = '\0', .state = StateIdle};
|
||||
|
||||
if (keyCount() == 0)
|
||||
return event;
|
||||
|
||||
const uint16_t buf = readRegister16(_REG_FIF);
|
||||
event.key = buf >> 8;
|
||||
event.state = KeyState(buf & 0xFF);
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
float BBQ10Keyboard::backlight() const
|
||||
{
|
||||
return readRegister8(_REG_BKL) / 255.0f;
|
||||
}
|
||||
|
||||
void BBQ10Keyboard::setBacklight(float value)
|
||||
{
|
||||
writeRegister(_REG_BKL, value * 255);
|
||||
}
|
||||
|
||||
uint8_t BBQ10Keyboard::readRegister8(uint8_t reg) const
|
||||
{
|
||||
if (m_wire) {
|
||||
m_wire->beginTransmission(m_addr);
|
||||
m_wire->write(reg);
|
||||
m_wire->endTransmission();
|
||||
|
||||
m_wire->requestFrom(m_addr, (uint8_t)1);
|
||||
if (m_wire->available() < 1)
|
||||
return 0;
|
||||
|
||||
return m_wire->read();
|
||||
}
|
||||
if (readCallback) {
|
||||
uint8_t data;
|
||||
readCallback(m_addr, reg, &data, 1);
|
||||
return data;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t BBQ10Keyboard::readRegister16(uint8_t reg) const
|
||||
{
|
||||
uint8_t data[2] = {0};
|
||||
// uint8_t low = 0, high = 0;
|
||||
if (m_wire) {
|
||||
m_wire->beginTransmission(m_addr);
|
||||
m_wire->write(reg);
|
||||
m_wire->endTransmission();
|
||||
|
||||
m_wire->requestFrom(m_addr, (uint8_t)2);
|
||||
if (m_wire->available() < 2)
|
||||
return 0;
|
||||
data[0] = m_wire->read();
|
||||
data[1] = m_wire->read();
|
||||
}
|
||||
if (readCallback) {
|
||||
readCallback(m_addr, reg, data, 2);
|
||||
}
|
||||
return (data[1] << 8) | data[0];
|
||||
}
|
||||
|
||||
void BBQ10Keyboard::writeRegister(uint8_t reg, uint8_t value)
|
||||
{
|
||||
uint8_t data[2];
|
||||
data[0] = reg | _WRITE_MASK;
|
||||
data[1] = value;
|
||||
|
||||
if (m_wire) {
|
||||
m_wire->beginTransmission(m_addr);
|
||||
m_wire->write(data, sizeof(uint8_t) * 2);
|
||||
m_wire->endTransmission();
|
||||
}
|
||||
if (writeCallback) {
|
||||
writeCallback(m_addr, data[0], &(data[1]), 1);
|
||||
}
|
||||
}
|
||||
51
src/input/BBQ10Keyboard.h
Normal file
51
src/input/BBQ10Keyboard.h
Normal file
@@ -0,0 +1,51 @@
|
||||
// Based on arturo182 arduino_bbq10kbd library https://github.com/arturo182/arduino_bbq10kbd
|
||||
|
||||
#include "configuration.h"
|
||||
#include <Wire.h>
|
||||
|
||||
#define KEY_MOD_ALT (0x1A)
|
||||
#define KEY_MOD_SHL (0x1B)
|
||||
#define KEY_MOD_SHR (0x1C)
|
||||
#define KEY_MOD_SYM (0x1D)
|
||||
|
||||
class BBQ10Keyboard
|
||||
{
|
||||
public:
|
||||
typedef uint8_t (*i2c_com_fptr_t)(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, uint8_t len);
|
||||
|
||||
enum KeyState { StateIdle = 0, StatePress, StateLongPress, StateRelease };
|
||||
|
||||
struct KeyEvent {
|
||||
char key;
|
||||
KeyState state;
|
||||
};
|
||||
|
||||
BBQ10Keyboard();
|
||||
|
||||
void begin(uint8_t addr = BBQ10_KB_ADDR, TwoWire *wire = &Wire);
|
||||
|
||||
void begin(i2c_com_fptr_t r, i2c_com_fptr_t w, uint8_t addr = BBQ10_KB_ADDR);
|
||||
|
||||
void reset(void);
|
||||
|
||||
void attachInterrupt(uint8_t pin, void (*func)(void)) const;
|
||||
void detachInterrupt(uint8_t pin) const;
|
||||
void clearInterruptStatus(void);
|
||||
|
||||
uint8_t status(void) const;
|
||||
uint8_t keyCount(void) const;
|
||||
KeyEvent keyEvent(void) const;
|
||||
|
||||
float backlight() const;
|
||||
void setBacklight(float value);
|
||||
|
||||
uint8_t readRegister8(uint8_t reg) const;
|
||||
uint16_t readRegister16(uint8_t reg) const;
|
||||
void writeRegister(uint8_t reg, uint8_t value);
|
||||
|
||||
private:
|
||||
TwoWire *m_wire;
|
||||
uint8_t m_addr;
|
||||
i2c_com_fptr_t readCallback;
|
||||
i2c_com_fptr_t writeCallback;
|
||||
};
|
||||
184
src/input/LinuxInput.cpp
Normal file
184
src/input/LinuxInput.cpp
Normal file
@@ -0,0 +1,184 @@
|
||||
#if ARCH_RASPBERRY_PI
|
||||
#include "LinuxInput.h"
|
||||
#include "configuration.h"
|
||||
|
||||
#include "platform/portduino/PortduinoGlue.h"
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/input.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// Inspired by https://github.com/librerpi/rpi-tools/blob/master/keyboard-proxy/main.c which is GPL-v2
|
||||
|
||||
LinuxInput::LinuxInput(const char *name) : concurrency::OSThread(name)
|
||||
{
|
||||
this->_originName = name;
|
||||
}
|
||||
|
||||
void LinuxInput::deInit()
|
||||
{
|
||||
close(fd);
|
||||
}
|
||||
|
||||
int32_t LinuxInput::runOnce()
|
||||
{
|
||||
|
||||
if (firstTime) {
|
||||
if (settingsStrings[keyboardDevice] == "")
|
||||
return disable();
|
||||
fd = open(settingsStrings[keyboardDevice].c_str(), O_RDWR);
|
||||
if (fd < 0)
|
||||
return disable();
|
||||
ret = ioctl(fd, EVIOCGRAB, (void *)1);
|
||||
if (ret != 0)
|
||||
return disable();
|
||||
|
||||
epollfd = epoll_create1(0);
|
||||
assert(epollfd >= 0);
|
||||
|
||||
ev.events = EPOLLIN;
|
||||
ev.data.fd = fd;
|
||||
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev)) {
|
||||
perror("unable to epoll add");
|
||||
return disable();
|
||||
}
|
||||
// This is the first time the OSThread library has called this function, so do port setup
|
||||
firstTime = 0;
|
||||
}
|
||||
|
||||
int nfds = epoll_wait(epollfd, events, MAX_EVENTS, 1);
|
||||
if (nfds < 0) {
|
||||
printf("%d ", nfds);
|
||||
perror("epoll_wait failed");
|
||||
return disable();
|
||||
} else if (nfds == 0) {
|
||||
return 50;
|
||||
}
|
||||
|
||||
int keys = 0;
|
||||
memset(report, 0, 8);
|
||||
for (int i = 0; i < nfds; i++) {
|
||||
|
||||
struct input_event ev[64];
|
||||
int rd = read(events[i].data.fd, ev, sizeof(ev));
|
||||
assert(rd > ((signed int)sizeof(struct input_event)));
|
||||
for (int j = 0; j < rd / ((signed int)sizeof(struct input_event)); j++) {
|
||||
InputEvent e;
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
|
||||
e.source = this->_originName;
|
||||
e.kbchar = 0;
|
||||
unsigned int type, code;
|
||||
type = ev[j].type;
|
||||
code = ev[j].code;
|
||||
int value = ev[j].value;
|
||||
// printf("Event: time %ld.%06ld, ", ev[j].time.tv_sec, ev[j].time.tv_usec);
|
||||
|
||||
if (type == EV_KEY) {
|
||||
uint8_t mod = 0;
|
||||
|
||||
switch (code) {
|
||||
case KEY_LEFTCTRL:
|
||||
mod = 0x01;
|
||||
break;
|
||||
case KEY_RIGHTCTRL:
|
||||
mod = 0x10;
|
||||
break;
|
||||
case KEY_LEFTSHIFT:
|
||||
mod = 0x02;
|
||||
break;
|
||||
case KEY_RIGHTSHIFT:
|
||||
mod = 0x20;
|
||||
break;
|
||||
case KEY_LEFTALT:
|
||||
mod = 0x04;
|
||||
break;
|
||||
case KEY_RIGHTALT:
|
||||
mod = 0x40;
|
||||
break;
|
||||
case KEY_LEFTMETA:
|
||||
mod = 0x08;
|
||||
break;
|
||||
}
|
||||
if (value == 1) {
|
||||
switch (code) {
|
||||
case KEY_LEFTCTRL:
|
||||
mod = 0x01;
|
||||
break;
|
||||
case KEY_RIGHTCTRL:
|
||||
mod = 0x10;
|
||||
break;
|
||||
case KEY_LEFTSHIFT:
|
||||
mod = 0x02;
|
||||
break;
|
||||
case KEY_RIGHTSHIFT:
|
||||
mod = 0x20;
|
||||
break;
|
||||
case KEY_LEFTALT:
|
||||
mod = 0x04;
|
||||
break;
|
||||
case KEY_RIGHTALT:
|
||||
mod = 0x40;
|
||||
break;
|
||||
case KEY_LEFTMETA:
|
||||
mod = 0x08;
|
||||
break;
|
||||
case KEY_ESC: // ESC
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL;
|
||||
break;
|
||||
case KEY_BACK: // Back
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK;
|
||||
// e.kbchar = key;
|
||||
break;
|
||||
|
||||
case KEY_UP: // Up
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP;
|
||||
break;
|
||||
case KEY_DOWN: // Down
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN;
|
||||
break;
|
||||
case KEY_LEFT: // Left
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT;
|
||||
break;
|
||||
e.kbchar = 0xb4;
|
||||
case KEY_RIGHT: // Right
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT;
|
||||
break;
|
||||
e.kbchar = 0xb7;
|
||||
case KEY_ENTER: // Enter
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT;
|
||||
break;
|
||||
default: // all other keys
|
||||
if (keymap[code]) {
|
||||
e.inputEvent = ANYKEY;
|
||||
e.kbchar = keymap[code];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ev[j].value) {
|
||||
modifiers |= mod;
|
||||
} else {
|
||||
modifiers &= ~mod;
|
||||
}
|
||||
report[0] = modifiers;
|
||||
}
|
||||
if (e.inputEvent != meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE) {
|
||||
if (e.inputEvent == ANYKEY && (modifiers && 0x22))
|
||||
e.kbchar = uppers[e.kbchar]; // doesn't get punctuation. Meh.
|
||||
this->notifyObservers(&e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 50; // Keyscan every 50msec to avoid key bounce
|
||||
}
|
||||
|
||||
#endif
|
||||
65
src/input/LinuxInput.h
Normal file
65
src/input/LinuxInput.h
Normal file
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
#if ARCH_RASPBERRY_PI
|
||||
#include "InputBroker.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/input.h>
|
||||
#include <map>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define MAX_EVENTS 10
|
||||
|
||||
class LinuxInput : public Observable<const InputEvent *>, public concurrency::OSThread
|
||||
{
|
||||
public:
|
||||
explicit LinuxInput(const char *name);
|
||||
void deInit(); // Strictly for cleanly "rebooting" the binary on native
|
||||
|
||||
protected:
|
||||
virtual int32_t runOnce() override;
|
||||
|
||||
private:
|
||||
const char *_originName;
|
||||
bool firstTime = 1;
|
||||
int shift = 0;
|
||||
char key = 0;
|
||||
char prevkey = 0;
|
||||
|
||||
InputEvent eventqueue[50]; // The Linux API will return multiple keypresses at a time. Queue them to not miss any.
|
||||
int queue_length = 0;
|
||||
int queue_progress = 0;
|
||||
|
||||
struct epoll_event events[MAX_EVENTS];
|
||||
int fd;
|
||||
int ret;
|
||||
uint8_t report[8];
|
||||
int epollfd;
|
||||
struct epoll_event ev;
|
||||
uint8_t modifiers = 0;
|
||||
std::map<int, char> keymap{
|
||||
{KEY_A, 'a'}, {KEY_B, 'b'}, {KEY_C, 'c'}, {KEY_D, 'd'}, {KEY_E, 'e'},
|
||||
{KEY_F, 'f'}, {KEY_G, 'g'}, {KEY_H, 'h'}, {KEY_I, 'i'}, {KEY_J, 'j'},
|
||||
{KEY_K, 'k'}, {KEY_L, 'l'}, {KEY_M, 'm'}, {KEY_N, 'n'}, {KEY_O, 'o'},
|
||||
{KEY_P, 'p'}, {KEY_Q, 'q'}, {KEY_R, 'r'}, {KEY_S, 's'}, {KEY_T, 't'},
|
||||
{KEY_U, 'u'}, {KEY_V, 'v'}, {KEY_W, 'w'}, {KEY_X, 'x'}, {KEY_Y, 'y'},
|
||||
{KEY_Z, 'z'}, {KEY_BACKSPACE, 0x08}, {KEY_SPACE, ' '}, {KEY_1, '1'}, {KEY_2, '2'},
|
||||
{KEY_3, '3'}, {KEY_4, '4'}, {KEY_5, '5'}, {KEY_6, '6'}, {KEY_7, '7'},
|
||||
{KEY_8, '8'}, {KEY_9, '9'}, {KEY_0, '0'}, {KEY_DOT, '.'}, {KEY_COMMA, ','},
|
||||
{KEY_MINUS, '-'}, {KEY_EQUAL, '='}, {KEY_LEFTBRACE, '['}, {KEY_RIGHTBRACE, ']'}, {KEY_BACKSLASH, '\\'},
|
||||
{KEY_SEMICOLON, ';'}, {KEY_APOSTROPHE, '\''}, {KEY_SLASH, '/'}, {KEY_TAB, 0x09}};
|
||||
std::map<char, char> uppers{{'a', 'A'}, {'b', 'B'}, {'c', 'C'}, {'d', 'D'}, {'e', 'E'}, {'f', 'F'}, {'g', 'G'}, {'h', 'H'},
|
||||
{'i', 'I'}, {'j', 'J'}, {'k', 'K'}, {'l', 'L'}, {'m', 'M'}, {'n', 'N'}, {'o', 'O'}, {'p', 'P'},
|
||||
{'q', 'Q'}, {'r', 'R'}, {'s', 'S'}, {'t', 'T'}, {'u', 'U'}, {'v', 'V'}, {'w', 'W'}, {'x', 'X'},
|
||||
{'y', 'Y'}, {'z', 'Z'}, {'1', '!'}, {'2', '@'}, {'3', '#'}, {'4', '$'}, {'5', '%'}, {'6', '^'},
|
||||
{'7', '&'}, {'8', '*'}, {'9', '('}, {'0', ')'}, {'.', '>'}, {',', '<'}, {'-', '_'}, {'=', '+'},
|
||||
{'[', '{'}, {']', '}'}, {'\\', '|'}, {';', ':'}, {'\'', '"'}, {'/', '?'}};
|
||||
};
|
||||
#endif
|
||||
14
src/input/LinuxInputImpl.cpp
Normal file
14
src/input/LinuxInputImpl.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#if ARCH_RASPBERRY_PI
|
||||
#include "LinuxInputImpl.h"
|
||||
#include "InputBroker.h"
|
||||
|
||||
LinuxInputImpl *aLinuxInputImpl;
|
||||
|
||||
LinuxInputImpl::LinuxInputImpl() : LinuxInput("LinuxInput") {}
|
||||
|
||||
void LinuxInputImpl::init()
|
||||
{
|
||||
inputBroker->registerSource(this);
|
||||
}
|
||||
|
||||
#endif
|
||||
21
src/input/LinuxInputImpl.h
Normal file
21
src/input/LinuxInputImpl.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifdef ARCH_RASPBERRY_PI
|
||||
#pragma once
|
||||
#include "LinuxInput.h"
|
||||
#include "main.h"
|
||||
|
||||
/**
|
||||
* @brief The idea behind this class to have static methods for the event handlers.
|
||||
* Check attachInterrupt() at RotaryEncoderInteruptBase.cpp
|
||||
* Technically you can have as many rotary encoders hardver attached
|
||||
* to your device as you wish, but you always need to have separate event
|
||||
* handlers, thus you need to have a RotaryEncoderInterrupt implementation.
|
||||
*/
|
||||
|
||||
class LinuxInputImpl : public LinuxInput
|
||||
{
|
||||
public:
|
||||
LinuxInputImpl();
|
||||
void init();
|
||||
};
|
||||
extern LinuxInputImpl *aLinuxInputImpl;
|
||||
#endif
|
||||
@@ -5,12 +5,12 @@ RotaryEncoderInterruptImpl1 *rotaryEncoderInterruptImpl1;
|
||||
|
||||
RotaryEncoderInterruptImpl1::RotaryEncoderInterruptImpl1() : RotaryEncoderInterruptBase("rotEnc1") {}
|
||||
|
||||
void RotaryEncoderInterruptImpl1::init()
|
||||
bool RotaryEncoderInterruptImpl1::init()
|
||||
{
|
||||
if (!moduleConfig.canned_message.rotary1_enabled) {
|
||||
// Input device is disabled.
|
||||
disable();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t pinA = moduleConfig.canned_message.inputbroker_pin_a;
|
||||
@@ -25,6 +25,7 @@ void RotaryEncoderInterruptImpl1::init()
|
||||
RotaryEncoderInterruptImpl1::handleIntA, RotaryEncoderInterruptImpl1::handleIntB,
|
||||
RotaryEncoderInterruptImpl1::handleIntPressed);
|
||||
inputBroker->registerSource(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void RotaryEncoderInterruptImpl1::handleIntA()
|
||||
@@ -38,4 +39,4 @@ void RotaryEncoderInterruptImpl1::handleIntB()
|
||||
void RotaryEncoderInterruptImpl1::handleIntPressed()
|
||||
{
|
||||
rotaryEncoderInterruptImpl1->intPressHandler();
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ class RotaryEncoderInterruptImpl1 : public RotaryEncoderInterruptBase
|
||||
{
|
||||
public:
|
||||
RotaryEncoderInterruptImpl1();
|
||||
void init();
|
||||
bool init();
|
||||
static void handleIntA();
|
||||
static void handleIntB();
|
||||
static void handleIntPressed();
|
||||
|
||||
@@ -40,13 +40,13 @@ int32_t TouchScreenBase::runOnce()
|
||||
int16_t x, y;
|
||||
bool touched = getTouch(x, y);
|
||||
if (touched) {
|
||||
hapticFeedback();
|
||||
this->setInterval(20);
|
||||
_last_x = x;
|
||||
_last_y = y;
|
||||
}
|
||||
if (touched != _touchedOld) {
|
||||
if (touched) {
|
||||
hapticFeedback();
|
||||
_state = TOUCH_EVENT_OCCURRED;
|
||||
_start = millis();
|
||||
_first_x = x;
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
#include "TouchScreenImpl1.h"
|
||||
#include "InputBroker.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "configuration.h"
|
||||
#include "modules/ExternalNotificationModule.h"
|
||||
|
||||
#ifdef ARCH_RASPBERRY_PI
|
||||
#include "platform/portduino/PortduinoGlue.h"
|
||||
#endif
|
||||
|
||||
TouchScreenImpl1 *touchScreenImpl1;
|
||||
|
||||
@@ -11,7 +17,14 @@ TouchScreenImpl1::TouchScreenImpl1(uint16_t width, uint16_t height, bool (*getTo
|
||||
|
||||
void TouchScreenImpl1::init()
|
||||
{
|
||||
#if !HAS_TOUCHSCREEN
|
||||
#if ARCH_RASPBERRY_PI
|
||||
if (settingsMap[touchscreenModule]) {
|
||||
TouchScreenBase::init(true);
|
||||
inputBroker->registerSource(this);
|
||||
} else {
|
||||
TouchScreenBase::init(false);
|
||||
}
|
||||
#elif !HAS_TOUCHSCREEN
|
||||
TouchScreenBase::init(false);
|
||||
return;
|
||||
#else
|
||||
@@ -61,6 +74,14 @@ void TouchScreenImpl1::onEvent(const TouchEvent &event)
|
||||
e.inputEvent = static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL);
|
||||
break;
|
||||
}
|
||||
case TOUCH_ACTION_TAP: {
|
||||
if (moduleConfig.external_notification.enabled && (externalNotificationModule->nagCycleCutoff != UINT32_MAX)) {
|
||||
externalNotificationModule->stopNow();
|
||||
} else {
|
||||
powerFSM.trigger(EVENT_INPUT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
#include "TrackballInterruptBase.h"
|
||||
#include "configuration.h"
|
||||
|
||||
TrackballInterruptBase::TrackballInterruptBase(const char *name)
|
||||
{
|
||||
this->_originName = name;
|
||||
}
|
||||
TrackballInterruptBase::TrackballInterruptBase(const char *name) : concurrency::OSThread(name), _originName(name) {}
|
||||
|
||||
void TrackballInterruptBase::init(uint8_t pinDown, uint8_t pinUp, uint8_t pinLeft, uint8_t pinRight, uint8_t pinPress,
|
||||
char eventDown, char eventUp, char eventLeft, char eventRight, char eventPressed,
|
||||
@@ -35,44 +32,64 @@ void TrackballInterruptBase::init(uint8_t pinDown, uint8_t pinUp, uint8_t pinLef
|
||||
|
||||
LOG_DEBUG("Trackball GPIO initialized (%d, %d, %d, %d, %d)\n", this->_pinUp, this->_pinDown, this->_pinLeft, this->_pinRight,
|
||||
pinPress);
|
||||
|
||||
this->setInterval(100);
|
||||
}
|
||||
|
||||
int32_t TrackballInterruptBase::runOnce()
|
||||
{
|
||||
InputEvent e;
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
|
||||
|
||||
if (this->action == TB_ACTION_PRESSED) {
|
||||
// LOG_DEBUG("Trackball event Press\n");
|
||||
e.inputEvent = this->_eventPressed;
|
||||
} else if (this->action == TB_ACTION_UP) {
|
||||
// LOG_DEBUG("Trackball event UP\n");
|
||||
e.inputEvent = this->_eventUp;
|
||||
} else if (this->action == TB_ACTION_DOWN) {
|
||||
// LOG_DEBUG("Trackball event DOWN\n");
|
||||
e.inputEvent = this->_eventDown;
|
||||
} else if (this->action == TB_ACTION_LEFT) {
|
||||
// LOG_DEBUG("Trackball event LEFT\n");
|
||||
e.inputEvent = this->_eventLeft;
|
||||
} else if (this->action == TB_ACTION_RIGHT) {
|
||||
// LOG_DEBUG("Trackball event RIGHT\n");
|
||||
e.inputEvent = this->_eventRight;
|
||||
}
|
||||
|
||||
if (e.inputEvent != meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE) {
|
||||
e.source = this->_originName;
|
||||
e.kbchar = 0x00;
|
||||
this->notifyObservers(&e);
|
||||
}
|
||||
|
||||
this->action = TB_ACTION_NONE;
|
||||
|
||||
return 100;
|
||||
}
|
||||
|
||||
void TrackballInterruptBase::intPressHandler()
|
||||
{
|
||||
InputEvent e;
|
||||
e.source = this->_originName;
|
||||
e.inputEvent = this->_eventPressed;
|
||||
this->notifyObservers(&e);
|
||||
this->action = TB_ACTION_PRESSED;
|
||||
}
|
||||
|
||||
void TrackballInterruptBase::intDownHandler()
|
||||
{
|
||||
InputEvent e;
|
||||
e.source = this->_originName;
|
||||
e.inputEvent = this->_eventDown;
|
||||
this->notifyObservers(&e);
|
||||
this->action = TB_ACTION_DOWN;
|
||||
}
|
||||
|
||||
void TrackballInterruptBase::intUpHandler()
|
||||
{
|
||||
InputEvent e;
|
||||
e.source = this->_originName;
|
||||
e.inputEvent = this->_eventUp;
|
||||
this->notifyObservers(&e);
|
||||
this->action = TB_ACTION_UP;
|
||||
}
|
||||
|
||||
void TrackballInterruptBase::intLeftHandler()
|
||||
{
|
||||
InputEvent e;
|
||||
e.source = this->_originName;
|
||||
e.inputEvent = this->_eventLeft;
|
||||
this->notifyObservers(&e);
|
||||
this->action = TB_ACTION_LEFT;
|
||||
}
|
||||
|
||||
void TrackballInterruptBase::intRightHandler()
|
||||
{
|
||||
InputEvent e;
|
||||
e.source = this->_originName;
|
||||
e.inputEvent = this->_eventRight;
|
||||
this->notifyObservers(&e);
|
||||
this->action = TB_ACTION_RIGHT;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "InputBroker.h"
|
||||
#include "mesh/NodeDB.h"
|
||||
|
||||
class TrackballInterruptBase : public Observable<const InputEvent *>
|
||||
class TrackballInterruptBase : public Observable<const InputEvent *>, public concurrency::OSThread
|
||||
{
|
||||
public:
|
||||
explicit TrackballInterruptBase(const char *name);
|
||||
@@ -16,6 +16,20 @@ class TrackballInterruptBase : public Observable<const InputEvent *>
|
||||
void intLeftHandler();
|
||||
void intRightHandler();
|
||||
|
||||
virtual int32_t runOnce() override;
|
||||
|
||||
protected:
|
||||
enum TrackballInterruptBaseActionType {
|
||||
TB_ACTION_NONE,
|
||||
TB_ACTION_PRESSED,
|
||||
TB_ACTION_UP,
|
||||
TB_ACTION_DOWN,
|
||||
TB_ACTION_LEFT,
|
||||
TB_ACTION_RIGHT
|
||||
};
|
||||
|
||||
volatile TrackballInterruptBaseActionType action = TB_ACTION_NONE;
|
||||
|
||||
private:
|
||||
uint8_t _pinDown = 0;
|
||||
uint8_t _pinUp = 0;
|
||||
|
||||
@@ -5,12 +5,12 @@ UpDownInterruptImpl1 *upDownInterruptImpl1;
|
||||
|
||||
UpDownInterruptImpl1::UpDownInterruptImpl1() : UpDownInterruptBase("upDown1") {}
|
||||
|
||||
void UpDownInterruptImpl1::init()
|
||||
bool UpDownInterruptImpl1::init()
|
||||
{
|
||||
|
||||
if (!moduleConfig.canned_message.updown1_enabled) {
|
||||
// Input device is disabled.
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t pinUp = moduleConfig.canned_message.inputbroker_pin_a;
|
||||
@@ -24,6 +24,7 @@ void UpDownInterruptImpl1::init()
|
||||
UpDownInterruptBase::init(pinDown, pinUp, pinPress, eventDown, eventUp, eventPressed, UpDownInterruptImpl1::handleIntDown,
|
||||
UpDownInterruptImpl1::handleIntUp, UpDownInterruptImpl1::handleIntPressed);
|
||||
inputBroker->registerSource(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void UpDownInterruptImpl1::handleIntDown()
|
||||
@@ -37,4 +38,4 @@ void UpDownInterruptImpl1::handleIntUp()
|
||||
void UpDownInterruptImpl1::handleIntPressed()
|
||||
{
|
||||
upDownInterruptImpl1->intPressHandler();
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,10 @@ class UpDownInterruptImpl1 : public UpDownInterruptBase
|
||||
{
|
||||
public:
|
||||
UpDownInterruptImpl1();
|
||||
void init();
|
||||
bool init();
|
||||
static void handleIntDown();
|
||||
static void handleIntUp();
|
||||
static void handleIntPressed();
|
||||
};
|
||||
|
||||
extern UpDownInterruptImpl1 *upDownInterruptImpl1;
|
||||
extern UpDownInterruptImpl1 *upDownInterruptImpl1;
|
||||
@@ -7,7 +7,7 @@ CardKbI2cImpl::CardKbI2cImpl() : KbI2cBase("cardKB") {}
|
||||
|
||||
void CardKbI2cImpl::init()
|
||||
{
|
||||
if (cardkb_found.address != CARDKB_ADDR && cardkb_found.address != TDECK_KB_ADDR) {
|
||||
if (cardkb_found.address == 0x00) {
|
||||
disable();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -28,20 +28,9 @@ uint8_t read_from_14004(TwoWire *i2cBus, uint8_t reg, uint8_t *data, uint8_t len
|
||||
return readflag;
|
||||
}
|
||||
|
||||
// Unused for now - flagging it off
|
||||
#if 0
|
||||
void write_to_14004(const TwoWire * i2cBus, uint8_t reg, uint8_t data)
|
||||
{
|
||||
i2cBus->beginTransmission(CARDKB_ADDR);
|
||||
i2cBus->write(reg);
|
||||
i2cBus->write(data);
|
||||
i2cBus->endTransmission(); // stop transmitting
|
||||
}
|
||||
#endif
|
||||
|
||||
int32_t KbI2cBase::runOnce()
|
||||
{
|
||||
if (cardkb_found.address != CARDKB_ADDR && cardkb_found.address != TDECK_KB_ADDR) {
|
||||
if (cardkb_found.address == 0x00) {
|
||||
// Input device is not detected.
|
||||
return INT32_MAX;
|
||||
}
|
||||
@@ -52,11 +41,19 @@ int32_t KbI2cBase::runOnce()
|
||||
#ifdef I2C_SDA1
|
||||
LOG_DEBUG("Using I2C Bus 1 (the second one)\n");
|
||||
i2cBus = &Wire1;
|
||||
if (cardkb_found.address == BBQ10_KB_ADDR) {
|
||||
Q10keyboard.begin(BBQ10_KB_ADDR, &Wire1);
|
||||
Q10keyboard.setBacklight(0);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case ScanI2C::WIRE:
|
||||
LOG_DEBUG("Using I2C Bus 0 (the first one)\n");
|
||||
i2cBus = &Wire;
|
||||
if (cardkb_found.address == BBQ10_KB_ADDR) {
|
||||
Q10keyboard.begin(BBQ10_KB_ADDR, &Wire);
|
||||
Q10keyboard.setBacklight(0);
|
||||
}
|
||||
break;
|
||||
case ScanI2C::NO_I2C:
|
||||
default:
|
||||
@@ -64,7 +61,105 @@ int32_t KbI2cBase::runOnce()
|
||||
}
|
||||
}
|
||||
|
||||
if (kb_model == 0x02) {
|
||||
switch (kb_model) {
|
||||
case 0x11: { // BB Q10
|
||||
int keyCount = Q10keyboard.keyCount();
|
||||
while (keyCount--) {
|
||||
const BBQ10Keyboard::KeyEvent key = Q10keyboard.keyEvent();
|
||||
if ((key.key != 0x00) && (key.state == BBQ10Keyboard::StateRelease)) {
|
||||
InputEvent e;
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
|
||||
e.source = this->_originName;
|
||||
switch (key.key) {
|
||||
case 'p': // TAB
|
||||
case 't': // TAB as well
|
||||
if (is_sym) {
|
||||
e.inputEvent = ANYKEY;
|
||||
e.kbchar = 0x09; // TAB Scancode
|
||||
is_sym = false; // reset sym state after second keypress
|
||||
} else {
|
||||
e.inputEvent = ANYKEY;
|
||||
e.kbchar = key.key;
|
||||
}
|
||||
break;
|
||||
case 'q': // ESC
|
||||
if (is_sym) {
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL;
|
||||
e.kbchar = 0x1b;
|
||||
is_sym = false; // reset sym state after second keypress
|
||||
} else {
|
||||
e.inputEvent = ANYKEY;
|
||||
e.kbchar = key.key;
|
||||
}
|
||||
break;
|
||||
case 0x08: // Back
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK;
|
||||
e.kbchar = key.key;
|
||||
break;
|
||||
case 'e': // sym e
|
||||
if (is_sym) {
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP;
|
||||
e.kbchar = 0xb5;
|
||||
is_sym = false; // reset sym state after second keypress
|
||||
} else {
|
||||
e.inputEvent = ANYKEY;
|
||||
e.kbchar = key.key;
|
||||
}
|
||||
break;
|
||||
case 'x': // sym x
|
||||
if (is_sym) {
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN;
|
||||
e.kbchar = 0xb6;
|
||||
is_sym = false; // reset sym state after second keypress
|
||||
} else {
|
||||
e.inputEvent = ANYKEY;
|
||||
e.kbchar = key.key;
|
||||
}
|
||||
break;
|
||||
case 's': // sym s
|
||||
if (is_sym) {
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT;
|
||||
e.kbchar = 0x00; // tweak for destSelect
|
||||
is_sym = false; // reset sym state after second keypress
|
||||
} else {
|
||||
e.inputEvent = ANYKEY;
|
||||
e.kbchar = key.key;
|
||||
}
|
||||
break;
|
||||
case 'f': // sym f
|
||||
if (is_sym) {
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT;
|
||||
e.kbchar = 0x00; // tweak for destSelect
|
||||
is_sym = false; // reset sym state after second keypress
|
||||
} else {
|
||||
e.inputEvent = ANYKEY;
|
||||
e.kbchar = key.key;
|
||||
}
|
||||
break;
|
||||
case 0x13: // Code scanner says the SYM key is 0x13
|
||||
is_sym = !is_sym;
|
||||
break;
|
||||
case 0x0a: // apparently Enter on Q10 is a line feed instead of carriage return
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT;
|
||||
break;
|
||||
case 0x00: // nopress
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
|
||||
break;
|
||||
default: // all other keys
|
||||
e.inputEvent = ANYKEY;
|
||||
e.kbchar = key.key;
|
||||
is_sym = false; // reset sym state after second keypress
|
||||
break;
|
||||
}
|
||||
|
||||
if (e.inputEvent != meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE) {
|
||||
this->notifyObservers(&e);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x02: {
|
||||
// RAK14004
|
||||
uint8_t rDataBuf[8] = {0};
|
||||
uint8_t PrintDataBuf = 0;
|
||||
@@ -85,9 +180,12 @@ int32_t KbI2cBase::runOnce()
|
||||
e.kbchar = PrintDataBuf;
|
||||
this->notifyObservers(&e);
|
||||
}
|
||||
} else if (kb_model == 0x00 || kb_model == 0x10) {
|
||||
// m5 cardkb and T-Deck
|
||||
i2cBus->requestFrom(kb_model == 0x00 ? CARDKB_ADDR : TDECK_KB_ADDR, 1);
|
||||
break;
|
||||
}
|
||||
case 0x00: // CARDKB
|
||||
case 0x10: { // T-DECK
|
||||
|
||||
i2cBus->requestFrom((int)cardkb_found.address, 1);
|
||||
|
||||
while (i2cBus->available()) {
|
||||
char c = i2cBus->read();
|
||||
@@ -104,17 +202,19 @@ int32_t KbI2cBase::runOnce()
|
||||
break;
|
||||
case 0xb5: // Up
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP;
|
||||
e.kbchar = 0xb5;
|
||||
break;
|
||||
case 0xb6: // Down
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN;
|
||||
e.kbchar = 0xb6;
|
||||
break;
|
||||
case 0xb4: // Left
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT;
|
||||
e.kbchar = c;
|
||||
e.kbchar = 0xb4;
|
||||
break;
|
||||
case 0xb7: // Right
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT;
|
||||
e.kbchar = c;
|
||||
e.kbchar = 0xb7;
|
||||
break;
|
||||
case 0x0d: // Enter
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT;
|
||||
@@ -132,7 +232,9 @@ int32_t KbI2cBase::runOnce()
|
||||
this->notifyObservers(&e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_WARN("Unknown kb_model 0x%02x\n", kb_model);
|
||||
}
|
||||
return 300;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "BBQ10Keyboard.h"
|
||||
#include "InputBroker.h"
|
||||
#include "Wire.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
@@ -16,4 +17,7 @@ class KbI2cBase : public Observable<const InputEvent *>, public concurrency::OST
|
||||
const char *_originName;
|
||||
|
||||
TwoWire *i2cBus = 0;
|
||||
|
||||
BBQ10Keyboard Q10keyboard;
|
||||
bool is_sym = false;
|
||||
};
|
||||
|
||||
131
src/input/kbMatrixBase.cpp
Normal file
131
src/input/kbMatrixBase.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
#include "kbMatrixBase.h"
|
||||
#include "configuration.h"
|
||||
|
||||
#ifdef INPUTBROKER_MATRIX_TYPE
|
||||
|
||||
const byte keys_cols[] = KEYS_COLS;
|
||||
const byte keys_rows[] = KEYS_ROWS;
|
||||
|
||||
#if INPUTBROKER_MATRIX_TYPE == 1
|
||||
|
||||
unsigned char KeyMap[3][sizeof(keys_rows)][sizeof(keys_cols)] = {{{' ', '.', 'm', 'n', 'b', 0xb6},
|
||||
{0x0d, 'l', 'k', 'j', 'h', 0xb4},
|
||||
{'p', 'o', 'i', 'u', 'y', 0xb5},
|
||||
{0x08, 'z', 'x', 'c', 'v', 0xb7},
|
||||
{'a', 's', 'd', 'f', 'g', 0x09},
|
||||
{'q', 'w', 'e', 'r', 't', 0x1a}},
|
||||
{// SHIFT
|
||||
{':', ';', 'M', 'N', 'B', 0xb6},
|
||||
{0x0d, 'L', 'K', 'J', 'H', 0xb4},
|
||||
{'P', 'O', 'I', 'U', 'Y', 0xb5},
|
||||
{0x08, 'Z', 'X', 'C', 'V', 0xb7},
|
||||
{'A', 'S', 'D', 'F', 'G', 0x09},
|
||||
{'Q', 'W', 'E', 'R', 'T', 0x1a}},
|
||||
{// SHIFT-SHIFT
|
||||
{'_', ',', '>', '<', '"', '{'},
|
||||
{'~', '-', '*', '&', '+', '['},
|
||||
{'0', '9', '8', '7', '6', '}'},
|
||||
{'=', '(', ')', '?', '/', ']'},
|
||||
{'!', '@', '#', '$', '%', '\\'},
|
||||
{'1', '2', '3', '4', '5', 0x1a}}};
|
||||
#endif
|
||||
|
||||
KbMatrixBase::KbMatrixBase(const char *name) : concurrency::OSThread(name)
|
||||
{
|
||||
this->_originName = name;
|
||||
}
|
||||
|
||||
int32_t KbMatrixBase::runOnce()
|
||||
{
|
||||
if (!INPUTBROKER_MATRIX_TYPE) {
|
||||
// Input device is not requested.
|
||||
return disable();
|
||||
}
|
||||
|
||||
if (firstTime) {
|
||||
// This is the first time the OSThread library has called this function, so do port setup
|
||||
firstTime = 0;
|
||||
for (byte i = 0; i < sizeof(keys_rows); i++) {
|
||||
pinMode(keys_rows[i], OUTPUT);
|
||||
digitalWrite(keys_rows[i], HIGH);
|
||||
}
|
||||
for (byte i = 0; i < sizeof(keys_cols); i++) {
|
||||
pinMode(keys_cols[i], INPUT_PULLUP);
|
||||
}
|
||||
}
|
||||
|
||||
key = 0;
|
||||
|
||||
if (INPUTBROKER_MATRIX_TYPE == 1) {
|
||||
// scan for keypresses
|
||||
for (byte i = 0; i < sizeof(keys_rows); i++) {
|
||||
digitalWrite(keys_rows[i], LOW);
|
||||
for (byte j = 0; j < sizeof(keys_cols); j++) {
|
||||
if (digitalRead(keys_cols[j]) == LOW) {
|
||||
key = KeyMap[shift][i][j];
|
||||
}
|
||||
}
|
||||
digitalWrite(keys_rows[i], HIGH);
|
||||
}
|
||||
// debounce
|
||||
if (key != prevkey) {
|
||||
if (key != 0) {
|
||||
LOG_DEBUG("Key 0x%x pressed\n", key);
|
||||
// reset shift now that we have a keypress
|
||||
InputEvent e;
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
|
||||
e.source = this->_originName;
|
||||
switch (key) {
|
||||
case 0x1b: // ESC
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL;
|
||||
break;
|
||||
case 0x08: // Back
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK;
|
||||
e.kbchar = key;
|
||||
break;
|
||||
case 0xb5: // Up
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP;
|
||||
break;
|
||||
case 0xb6: // Down
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN;
|
||||
break;
|
||||
case 0xb4: // Left
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT;
|
||||
e.kbchar = key;
|
||||
break;
|
||||
case 0xb7: // Right
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT;
|
||||
e.kbchar = key;
|
||||
break;
|
||||
case 0x0d: // Enter
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT;
|
||||
break;
|
||||
case 0x00: // nopress
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
|
||||
break;
|
||||
case 0x1a: // Shift
|
||||
shift++;
|
||||
if (shift > 2) {
|
||||
shift = 0;
|
||||
}
|
||||
break;
|
||||
default: // all other keys
|
||||
e.inputEvent = ANYKEY;
|
||||
e.kbchar = key;
|
||||
break;
|
||||
}
|
||||
if (e.inputEvent != meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE) {
|
||||
this->notifyObservers(&e);
|
||||
}
|
||||
}
|
||||
prevkey = key;
|
||||
}
|
||||
|
||||
} else {
|
||||
LOG_WARN("Unknown kb_model 0x%02x\n", INPUTBROKER_MATRIX_TYPE);
|
||||
return disable();
|
||||
}
|
||||
return 50; // Keyscan every 50msec to avoid key bounce
|
||||
}
|
||||
|
||||
#endif // INPUTBROKER_MATRIX_TYPE
|
||||
20
src/input/kbMatrixBase.h
Normal file
20
src/input/kbMatrixBase.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "InputBroker.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
|
||||
class KbMatrixBase : public Observable<const InputEvent *>, public concurrency::OSThread
|
||||
{
|
||||
public:
|
||||
explicit KbMatrixBase(const char *name);
|
||||
|
||||
protected:
|
||||
virtual int32_t runOnce() override;
|
||||
|
||||
private:
|
||||
const char *_originName;
|
||||
bool firstTime = 1;
|
||||
int shift = 0;
|
||||
char key = 0;
|
||||
char prevkey = 0;
|
||||
};
|
||||
20
src/input/kbMatrixImpl.cpp
Normal file
20
src/input/kbMatrixImpl.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "kbMatrixImpl.h"
|
||||
#include "InputBroker.h"
|
||||
|
||||
#ifdef INPUTBROKER_MATRIX_TYPE
|
||||
|
||||
KbMatrixImpl *kbMatrixImpl;
|
||||
|
||||
KbMatrixImpl::KbMatrixImpl() : KbMatrixBase("matrixKB") {}
|
||||
|
||||
void KbMatrixImpl::init()
|
||||
{
|
||||
if (!INPUTBROKER_MATRIX_TYPE) {
|
||||
disable();
|
||||
return;
|
||||
}
|
||||
|
||||
inputBroker->registerSource(this);
|
||||
}
|
||||
|
||||
#endif // INPUTBROKER_MATRIX_TYPE
|
||||
19
src/input/kbMatrixImpl.h
Normal file
19
src/input/kbMatrixImpl.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "kbMatrixBase.h"
|
||||
#include "main.h"
|
||||
|
||||
/**
|
||||
* @brief The idea behind this class to have static methods for the event handlers.
|
||||
* Check attachInterrupt() at RotaryEncoderInteruptBase.cpp
|
||||
* Technically you can have as many rotary encoders hardver attached
|
||||
* to your device as you wish, but you always need to have separate event
|
||||
* handlers, thus you need to have a RotaryEncoderInterrupt implementation.
|
||||
*/
|
||||
class KbMatrixImpl : public KbMatrixBase
|
||||
{
|
||||
public:
|
||||
KbMatrixImpl();
|
||||
void init();
|
||||
};
|
||||
|
||||
extern KbMatrixImpl *kbMatrixImpl;
|
||||
256
src/main.cpp
256
src/main.cpp
@@ -29,11 +29,9 @@
|
||||
#include "target_specific.h"
|
||||
#include <Wire.h>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
// #include <driver/rtc_io.h>
|
||||
|
||||
#include "mesh/eth/ethClient.h"
|
||||
#include "mesh/http/WiFiAPClient.h"
|
||||
|
||||
#ifdef ARCH_ESP32
|
||||
#include "mesh/http/WebServer.h"
|
||||
#include "nimble/NimbleBluetooth.h"
|
||||
@@ -47,10 +45,12 @@ NRF52Bluetooth *nrf52Bluetooth;
|
||||
|
||||
#if HAS_WIFI
|
||||
#include "mesh/api/WiFiServerAPI.h"
|
||||
#include "mesh/wifi/WiFiAPClient.h"
|
||||
#endif
|
||||
|
||||
#if HAS_ETHERNET
|
||||
#include "mesh/api/ethServerAPI.h"
|
||||
#include "mesh/eth/ethClient.h"
|
||||
#endif
|
||||
#include "mqtt/MQTT.h"
|
||||
|
||||
@@ -66,13 +66,27 @@ NRF52Bluetooth *nrf52Bluetooth;
|
||||
#include "platform/portduino/SimRadio.h"
|
||||
#endif
|
||||
|
||||
#if HAS_BUTTON
|
||||
#ifdef ARCH_RASPBERRY_PI
|
||||
#include "linux/LinuxHardwareI2C.h"
|
||||
#include "platform/portduino/PortduinoGlue.h"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#endif
|
||||
|
||||
#if HAS_BUTTON || defined(ARCH_RASPBERRY_PI)
|
||||
#include "ButtonThread.h"
|
||||
#endif
|
||||
#include "PowerFSMThread.h"
|
||||
|
||||
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL)
|
||||
#include "AccelerometerThread.h"
|
||||
#include "AmbientLightingThread.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAS_I2S
|
||||
#include "AudioThread.h"
|
||||
AudioThread *audioThread;
|
||||
#endif
|
||||
|
||||
using namespace concurrency;
|
||||
@@ -113,6 +127,7 @@ ATECCX08A atecc;
|
||||
#ifdef T_WATCH_S3
|
||||
Adafruit_DRV2605 drv;
|
||||
#endif
|
||||
|
||||
bool isVibrating = false;
|
||||
|
||||
bool eink_found = true;
|
||||
@@ -121,18 +136,37 @@ uint32_t serialSinceMsec;
|
||||
|
||||
bool pmu_found;
|
||||
|
||||
// Array map of sensor types (as array index) and i2c address as value we'll find in the i2c scan
|
||||
uint8_t nodeTelemetrySensorsMap[_meshtastic_TelemetrySensorType_MAX + 1] = {
|
||||
0}; // one is enough, missing elements will be initialized to 0 anyway.
|
||||
// Array map of sensor types with i2c address and wire as we'll find in the i2c scan
|
||||
std::pair<uint8_t, TwoWire *> nodeTelemetrySensorsMap[_meshtastic_TelemetrySensorType_MAX + 1] = {};
|
||||
|
||||
Router *router = NULL; // Users of router don't care what sort of subclass implements that API
|
||||
|
||||
#ifdef ARCH_RASPBERRY_PI
|
||||
void getPiMacAddr(uint8_t *dmac)
|
||||
{
|
||||
std::fstream macIdentity;
|
||||
macIdentity.open("/sys/kernel/debug/bluetooth/hci0/identity", std::ios::in);
|
||||
std::string macLine;
|
||||
getline(macIdentity, macLine);
|
||||
macIdentity.close();
|
||||
|
||||
dmac[0] = strtol(macLine.substr(0, 2).c_str(), NULL, 16);
|
||||
dmac[1] = strtol(macLine.substr(3, 2).c_str(), NULL, 16);
|
||||
dmac[2] = strtol(macLine.substr(6, 2).c_str(), NULL, 16);
|
||||
dmac[3] = strtol(macLine.substr(9, 2).c_str(), NULL, 16);
|
||||
dmac[4] = strtol(macLine.substr(12, 2).c_str(), NULL, 16);
|
||||
dmac[5] = strtol(macLine.substr(15, 2).c_str(), NULL, 16);
|
||||
}
|
||||
#endif
|
||||
|
||||
const char *getDeviceName()
|
||||
{
|
||||
uint8_t dmac[6];
|
||||
|
||||
#ifdef ARCH_RASPBERRY_PI
|
||||
getPiMacAddr(dmac);
|
||||
#else
|
||||
getMacAddr(dmac);
|
||||
|
||||
#endif
|
||||
// Meshtastic_ab3c or Shortname_abcd
|
||||
static char name[20];
|
||||
snprintf(name, sizeof(name), "%02x%02x", dmac[4], dmac[5]);
|
||||
@@ -145,6 +179,25 @@ const char *getDeviceName()
|
||||
return name;
|
||||
}
|
||||
|
||||
#ifdef VEXT_ENABLE_V03
|
||||
|
||||
#include <soc/rtc.h>
|
||||
|
||||
static uint32_t calibrate_one(rtc_cal_sel_t cal_clk, const char *name)
|
||||
{
|
||||
const uint32_t cal_count = 1000;
|
||||
uint32_t cali_val;
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
cali_val = rtc_clk_cal(cal_clk, cal_count);
|
||||
}
|
||||
return cali_val;
|
||||
}
|
||||
|
||||
int heltec_version = 3;
|
||||
|
||||
#define CALIBRATE_ONE(cali_clk) calibrate_one(cali_clk, #cali_clk)
|
||||
#endif
|
||||
|
||||
static int32_t ledBlinker()
|
||||
{
|
||||
static bool ledOn;
|
||||
@@ -158,17 +211,18 @@ static int32_t ledBlinker()
|
||||
|
||||
uint32_t timeLastPowered = 0;
|
||||
|
||||
#if HAS_BUTTON
|
||||
#if HAS_BUTTON || defined(ARCH_RASPBERRY_PI)
|
||||
bool ButtonThread::shutdown_on_long_stop = false;
|
||||
#endif
|
||||
|
||||
static Periodic *ledPeriodic;
|
||||
static OSThread *powerFSMthread;
|
||||
#if HAS_BUTTON
|
||||
#if HAS_BUTTON || defined(ARCH_RASPBERRY_PI)
|
||||
static OSThread *buttonThread;
|
||||
uint32_t ButtonThread::longPressTime = 0;
|
||||
#endif
|
||||
static OSThread *accelerometerThread;
|
||||
static OSThread *ambientLightingThread;
|
||||
SPISettings spiSettings(4000000, MSBFIRST, SPI_MODE0);
|
||||
|
||||
RadioInterface *rIf = NULL;
|
||||
@@ -214,12 +268,59 @@ void setup()
|
||||
digitalWrite(PIN_EINK_PWR_ON, HIGH);
|
||||
#endif
|
||||
|
||||
#ifdef VEXT_ENABLE
|
||||
#ifdef ST7735_BL_V03 // Heltec Wireless Tracker PCB Change Detect/Hack
|
||||
|
||||
rtc_clk_32k_enable(true);
|
||||
CALIBRATE_ONE(RTC_CAL_RTC_MUX);
|
||||
if (CALIBRATE_ONE(RTC_CAL_32K_XTAL) != 0) {
|
||||
rtc_clk_slow_freq_set(RTC_SLOW_FREQ_32K_XTAL);
|
||||
CALIBRATE_ONE(RTC_CAL_RTC_MUX);
|
||||
CALIBRATE_ONE(RTC_CAL_32K_XTAL);
|
||||
}
|
||||
|
||||
if (rtc_clk_slow_freq_get() != RTC_SLOW_FREQ_32K_XTAL) {
|
||||
heltec_version = 3;
|
||||
} else {
|
||||
heltec_version = 5;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(VEXT_ENABLE_V03)
|
||||
if (heltec_version == 3) {
|
||||
pinMode(VEXT_ENABLE_V03, OUTPUT);
|
||||
digitalWrite(VEXT_ENABLE_V03, 0); // turn on the display power
|
||||
LOG_DEBUG("HELTEC Detect Tracker V1.0\n");
|
||||
} else {
|
||||
pinMode(VEXT_ENABLE_V05, OUTPUT);
|
||||
digitalWrite(VEXT_ENABLE_V05, 1); // turn on the display power
|
||||
LOG_DEBUG("HELTEC Detect Tracker V1.1\n");
|
||||
}
|
||||
#elif defined(VEXT_ENABLE)
|
||||
pinMode(VEXT_ENABLE, OUTPUT);
|
||||
digitalWrite(VEXT_ENABLE, 0); // turn on the display power
|
||||
#endif
|
||||
|
||||
#ifdef VGNSS_CTRL
|
||||
#if defined(VGNSS_CTRL_V03)
|
||||
if (heltec_version == 3) {
|
||||
pinMode(VGNSS_CTRL_V03, OUTPUT);
|
||||
digitalWrite(VGNSS_CTRL_V03, LOW);
|
||||
} else {
|
||||
pinMode(VGNSS_CTRL_V05, OUTPUT);
|
||||
digitalWrite(VGNSS_CTRL_V05, LOW);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(VTFT_CTRL_V03)
|
||||
if (heltec_version == 3) {
|
||||
pinMode(VTFT_CTRL_V03, OUTPUT);
|
||||
digitalWrite(VTFT_CTRL_V03, LOW);
|
||||
} else {
|
||||
pinMode(VTFT_CTRL_V05, OUTPUT);
|
||||
digitalWrite(VTFT_CTRL_V05, LOW);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(VGNSS_CTRL)
|
||||
pinMode(VGNSS_CTRL, OUTPUT);
|
||||
digitalWrite(VGNSS_CTRL, LOW);
|
||||
#endif
|
||||
@@ -337,6 +438,10 @@ void setup()
|
||||
auto i2cCount = i2cScanner->countDevices();
|
||||
if (i2cCount == 0) {
|
||||
LOG_INFO("No I2C devices found\n");
|
||||
Wire.end();
|
||||
#ifdef I2C_SDA1
|
||||
Wire1.end();
|
||||
#endif
|
||||
} else {
|
||||
LOG_INFO("%i I2C devices found\n", i2cCount);
|
||||
}
|
||||
@@ -387,6 +492,10 @@ void setup()
|
||||
// assign an arbitrary value to distinguish from other models
|
||||
kb_model = 0x10;
|
||||
break;
|
||||
case ScanI2C::DeviceType::BBQ10KB:
|
||||
// assign an arbitrary value to distinguish from other models
|
||||
kb_model = 0x11;
|
||||
break;
|
||||
default:
|
||||
// use this as default since it's also just zero
|
||||
LOG_WARN("kb_info.type is unknown(0x%02x), setting kb_model=0x00\n", kb_info.type);
|
||||
@@ -405,14 +514,6 @@ void setup()
|
||||
// Only one supported RGB LED currently
|
||||
#ifdef HAS_NCP5623
|
||||
rgb_found = i2cScanner->find(ScanI2C::DeviceType::NCP5623);
|
||||
|
||||
// Start the RGB LED at 50%
|
||||
|
||||
if (rgb_found.type == ScanI2C::NCP5623) {
|
||||
rgb.begin();
|
||||
rgb.setCurrent(10);
|
||||
rgb.setColor(128, 128, 128);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL)
|
||||
@@ -427,7 +528,8 @@ void setup()
|
||||
{ \
|
||||
auto found = i2cScanner->find(SCANNER_T); \
|
||||
if (found.type != ScanI2C::DeviceType::NONE) { \
|
||||
nodeTelemetrySensorsMap[PB_T] = found.address.address; \
|
||||
nodeTelemetrySensorsMap[PB_T].first = found.address.address; \
|
||||
nodeTelemetrySensorsMap[PB_T].second = i2cScanner->fetchI2CBus(found.address); \
|
||||
LOG_DEBUG("found i2c sensor %s\n", STRING(PB_T)); \
|
||||
} \
|
||||
}
|
||||
@@ -437,6 +539,7 @@ void setup()
|
||||
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::BMP_280, meshtastic_TelemetrySensorType_BMP280)
|
||||
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::INA260, meshtastic_TelemetrySensorType_INA260)
|
||||
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::INA219, meshtastic_TelemetrySensorType_INA219)
|
||||
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::INA3221, meshtastic_TelemetrySensorType_INA3221)
|
||||
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MCP9808, meshtastic_TelemetrySensorType_MCP9808)
|
||||
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MCP9808, meshtastic_TelemetrySensorType_MCP9808)
|
||||
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::SHT31, meshtastic_TelemetrySensorType_SHT31)
|
||||
@@ -480,16 +583,19 @@ void setup()
|
||||
#endif
|
||||
|
||||
// We do this as early as possible because this loads preferences from flash
|
||||
// but we need to do this after main cpu iniot (esp32setup), because we need the random seed set
|
||||
// but we need to do this after main cpu init (esp32setup), because we need the random seed set
|
||||
nodeDB.init();
|
||||
|
||||
// If we're taking on the repeater role, use flood router
|
||||
if (config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER)
|
||||
// If we're taking on the repeater role, use flood router and turn off 3V3_S rail because peripherals are not needed
|
||||
if (config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) {
|
||||
router = new FloodingRouter();
|
||||
else
|
||||
#ifdef PIN_3V3_EN
|
||||
digitalWrite(PIN_3V3_EN, LOW);
|
||||
#endif
|
||||
} else
|
||||
router = new ReliableRouter();
|
||||
|
||||
#if HAS_BUTTON
|
||||
#if HAS_BUTTON || defined(ARCH_RASPBERRY_PI)
|
||||
// Buttons. Moved here cause we need NodeDB to be initialized
|
||||
buttonThread = new ButtonThread();
|
||||
#endif
|
||||
@@ -517,6 +623,12 @@ void setup()
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL)
|
||||
if (rgb_found.type != ScanI2C::DeviceType::NONE) {
|
||||
ambientLightingThread = new AmbientLightingThread(rgb_found.type);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef T_WATCH_S3
|
||||
drv.begin();
|
||||
drv.selectLibrary(1);
|
||||
@@ -528,23 +640,24 @@ void setup()
|
||||
initSPI();
|
||||
#ifdef ARCH_RP2040
|
||||
#ifdef HW_SPI1_DEVICE
|
||||
SPI1.setSCK(RF95_SCK);
|
||||
SPI1.setTX(RF95_MOSI);
|
||||
SPI1.setRX(RF95_MISO);
|
||||
pinMode(RF95_NSS, OUTPUT);
|
||||
digitalWrite(RF95_NSS, HIGH);
|
||||
SPI1.setSCK(LORA_SCK);
|
||||
SPI1.setTX(LORA_MOSI);
|
||||
SPI1.setRX(LORA_MISO);
|
||||
pinMode(LORA_CS, OUTPUT);
|
||||
digitalWrite(LORA_CS, HIGH);
|
||||
SPI1.begin(false);
|
||||
#else // HW_SPI1_DEVICE
|
||||
SPI.setSCK(RF95_SCK);
|
||||
SPI.setTX(RF95_MOSI);
|
||||
SPI.setRX(RF95_MISO);
|
||||
SPI.setSCK(LORA_SCK);
|
||||
SPI.setTX(LORA_MOSI);
|
||||
SPI.setRX(LORA_MISO);
|
||||
SPI.begin(false);
|
||||
#endif // HW_SPI1_DEVICE
|
||||
#elif !defined(ARCH_ESP32) // ARCH_RP2040
|
||||
SPI.begin();
|
||||
#else
|
||||
// ESP32
|
||||
SPI.begin(RF95_SCK, RF95_MISO, RF95_MOSI, RF95_NSS);
|
||||
SPI.begin(LORA_SCK, LORA_MISO, LORA_MOSI, LORA_CS);
|
||||
LOG_WARN("SPI.begin(SCK=%d, MISO=%d, MOSI=%d, NSS=%d)\n", LORA_SCK, LORA_MISO, LORA_MOSI, LORA_CS);
|
||||
SPI.setFrequency(4000000);
|
||||
#endif
|
||||
|
||||
@@ -553,16 +666,22 @@ void setup()
|
||||
|
||||
readFromRTC(); // read the main CPU RTC at first (in case we can't get GPS time)
|
||||
|
||||
gps = createGps();
|
||||
|
||||
// If we're taking on the repeater role, ignore GPS
|
||||
if (config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER) {
|
||||
gps = GPS::createGps();
|
||||
}
|
||||
if (gps) {
|
||||
gpsStatus->observe(&gps->newStatus);
|
||||
} else {
|
||||
LOG_WARN("No GPS found - running without GPS\n");
|
||||
LOG_DEBUG("Running without GPS.\n");
|
||||
}
|
||||
|
||||
nodeStatus->observe(&nodeDB.newStatus);
|
||||
|
||||
#ifdef HAS_I2S
|
||||
LOG_DEBUG("Starting audio thread\n");
|
||||
audioThread = new AudioThread();
|
||||
#endif
|
||||
|
||||
service.init();
|
||||
|
||||
// Now that the mesh service is created, create any modules
|
||||
@@ -578,6 +697,10 @@ void setup()
|
||||
// the current region name)
|
||||
#if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7789_CS)
|
||||
screen->setup();
|
||||
#elif ARCH_RASPBERRY_PI
|
||||
if (screen_found.port != ScanI2C::I2CPort::NO_I2C || settingsMap[displayPanel]) {
|
||||
screen->setup();
|
||||
}
|
||||
#else
|
||||
if (screen_found.port != ScanI2C::I2CPort::NO_I2C)
|
||||
screen->setup();
|
||||
@@ -585,24 +708,43 @@ void setup()
|
||||
|
||||
screen->print("Started...\n");
|
||||
|
||||
// We have now loaded our saved preferences from flash
|
||||
|
||||
// ONCE we will factory reset the GPS for bug #327
|
||||
if (gps && !devicestate.did_gps_reset) {
|
||||
LOG_WARN("GPS FactoryReset requested\n");
|
||||
if (gps->factoryReset()) { // If we don't succeed try again next time
|
||||
devicestate.did_gps_reset = true;
|
||||
nodeDB.saveToDisk(SEGMENT_DEVICESTATE);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SX126X_ANT_SW
|
||||
// make analog PA vs not PA switch on SX126x eval board work properly
|
||||
pinMode(SX126X_ANT_SW, OUTPUT);
|
||||
digitalWrite(SX126X_ANT_SW, 1);
|
||||
#endif
|
||||
|
||||
#ifdef HW_SPI1_DEVICE
|
||||
#ifdef ARCH_RASPBERRY_PI
|
||||
if (settingsMap[use_sx1262]) {
|
||||
if (!rIf) {
|
||||
LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings);
|
||||
rIf = new SX1262Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset],
|
||||
settingsMap[busy]);
|
||||
if (!rIf->init()) {
|
||||
LOG_ERROR("Failed to find SX1262 radio\n");
|
||||
delete rIf;
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
LOG_INFO("SX1262 Radio init succeeded, using SX1262 radio\n");
|
||||
}
|
||||
}
|
||||
} else if (settingsMap[use_rf95]) {
|
||||
if (!rIf) {
|
||||
LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings);
|
||||
rIf = new RF95Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset],
|
||||
settingsMap[busy]);
|
||||
if (!rIf->init()) {
|
||||
LOG_ERROR("Failed to find RF95 radio\n");
|
||||
delete rIf;
|
||||
rIf = NULL;
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
LOG_INFO("RF95 Radio init succeeded, using RF95 radio\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(HW_SPI1_DEVICE)
|
||||
LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI1, spiSettings);
|
||||
#else // HW_SPI1_DEVICE
|
||||
LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings);
|
||||
@@ -637,7 +779,7 @@ void setup()
|
||||
|
||||
#if defined(RF95_IRQ)
|
||||
if (!rIf) {
|
||||
rIf = new RF95Interface(RadioLibHAL, RF95_NSS, RF95_IRQ, RF95_RESET, RF95_DIO1);
|
||||
rIf = new RF95Interface(RadioLibHAL, LORA_CS, RF95_IRQ, RF95_RESET, RF95_DIO1);
|
||||
if (!rIf->init()) {
|
||||
LOG_WARN("Failed to find RF95 radio\n");
|
||||
delete rIf;
|
||||
@@ -648,7 +790,7 @@ void setup()
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(USE_SX1262)
|
||||
#if defined(USE_SX1262) && !defined(ARCH_RASPBERRY_PI)
|
||||
if (!rIf) {
|
||||
rIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY);
|
||||
if (!rIf->init()) {
|
||||
@@ -717,11 +859,15 @@ void setup()
|
||||
|
||||
#ifndef ARCH_PORTDUINO
|
||||
// Initialize Wifi
|
||||
#if HAS_WIFI
|
||||
initWifi();
|
||||
#endif
|
||||
|
||||
#if HAS_ETHERNET
|
||||
// Initialize Ethernet
|
||||
initEthernet();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef ARCH_ESP32
|
||||
// Start web server thread.
|
||||
@@ -749,8 +895,6 @@ void setup()
|
||||
// This must be _after_ service.init because we need our preferences loaded from flash to have proper timeout values
|
||||
PowerFSM_setup(); // we will transition to ON in a couple of seconds, FIXME, only do this for cold boots, not waking from SDS
|
||||
powerFSMthread = new PowerFSMThread();
|
||||
|
||||
// setBluetoothEnable(false); we now don't start bluetooth until we enter the proper state
|
||||
setCPUFast(false); // 80MHz is fine for our slow peripherals
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,12 @@ extern ATECCX08A atecc;
|
||||
#include <Adafruit_DRV2605.h>
|
||||
extern Adafruit_DRV2605 drv;
|
||||
#endif
|
||||
|
||||
#ifdef HAS_I2S
|
||||
#include "AudioThread.h"
|
||||
extern AudioThread *audioThread;
|
||||
#endif
|
||||
|
||||
extern bool isVibrating;
|
||||
|
||||
extern int TCPPort; // set by Portduino
|
||||
@@ -56,6 +62,7 @@ extern graphics::Screen *screen;
|
||||
|
||||
// Return a human readable string of the form "Meshtastic_ab13"
|
||||
const char *getDeviceName();
|
||||
void getPiMacAddr(uint8_t *dmac);
|
||||
|
||||
extern uint32_t timeLastPowered;
|
||||
|
||||
@@ -64,6 +71,8 @@ extern uint32_t shutdownAtMsec;
|
||||
|
||||
extern uint32_t serialSinceMsec;
|
||||
|
||||
extern int heltec_version;
|
||||
|
||||
// If a thread does something that might need for it to be rescheduled ASAP it can set this flag
|
||||
// This will suppress the current delay and instead try to run ASAP.
|
||||
extern bool runASAP;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "Channels.h"
|
||||
#include "CryptoEngine.h"
|
||||
#include "DisplayFormatters.h"
|
||||
#include "NodeDB.h"
|
||||
#include "configuration.h"
|
||||
|
||||
@@ -183,7 +184,7 @@ void Channels::onConfigChanged()
|
||||
{
|
||||
// Make sure the phone hasn't mucked anything up
|
||||
for (int i = 0; i < channelFile.channels_count; i++) {
|
||||
meshtastic_Channel &ch = fixupChannel(i);
|
||||
const meshtastic_Channel &ch = fixupChannel(i);
|
||||
|
||||
if (ch.role == meshtastic_Channel_Role_PRIMARY)
|
||||
primaryIndex = i;
|
||||
@@ -239,38 +240,9 @@ const char *Channels::getName(size_t chIndex)
|
||||
const char *channelName = channelSettings.name;
|
||||
if (!*channelName) { // emptystring
|
||||
// Per mesh.proto spec, if bandwidth is specified we must ignore modemPreset enum, we assume that in that case
|
||||
// the app fucked up and forgot to set channelSettings.name
|
||||
|
||||
// the app effed up and forgot to set channelSettings.name
|
||||
if (config.lora.use_preset) {
|
||||
switch (config.lora.modem_preset) {
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW:
|
||||
channelName = "ShortSlow";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST:
|
||||
channelName = "ShortFast";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW:
|
||||
channelName = "MediumSlow";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST:
|
||||
channelName = "MediumFast";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW:
|
||||
channelName = "LongSlow";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST:
|
||||
channelName = "LongFast";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE:
|
||||
channelName = "LongMod";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_VERY_LONG_SLOW:
|
||||
channelName = "VLongSlow";
|
||||
break;
|
||||
default:
|
||||
channelName = "Invalid";
|
||||
break;
|
||||
}
|
||||
channelName = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false);
|
||||
} else {
|
||||
channelName = "Custom";
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
|
||||
{
|
||||
if (wasSeenRecently(p)) { // Note: this will also add a recent packet record
|
||||
printPacket("Ignoring incoming msg, because we've already seen it", p);
|
||||
if (config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER &&
|
||||
if (!moduleConfig.mqtt.enabled && config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER &&
|
||||
config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER_CLIENT &&
|
||||
config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER) {
|
||||
// cancel rebroadcast of this message *if* there was already one, unless we're a router/repeater!
|
||||
@@ -49,10 +49,13 @@ void FloodingRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas
|
||||
|
||||
tosend->hop_limit--; // bump down the hop count
|
||||
|
||||
// If it is a traceRoute request, update the route that it went via me
|
||||
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag && traceRouteModule &&
|
||||
traceRouteModule->wantPacket(p)) {
|
||||
traceRouteModule->updateRoute(tosend);
|
||||
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
|
||||
// If it is a traceRoute request, update the route that it went via me
|
||||
if (traceRouteModule && traceRouteModule->wantPacket(p))
|
||||
traceRouteModule->updateRoute(tosend);
|
||||
// If it is a neighborInfo packet, update last_sent_by_id
|
||||
if (neighborInfoModule && neighborInfoModule->wantPacket(p))
|
||||
neighborInfoModule->updateLastSentById(tosend);
|
||||
}
|
||||
|
||||
LOG_INFO("Rebroadcasting received floodmsg to neighbors\n");
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "PacketHistory.h"
|
||||
#include "Router.h"
|
||||
#include "modules/NeighborInfoModule.h"
|
||||
#include "modules/TraceRouteModule.h"
|
||||
|
||||
/**
|
||||
@@ -57,4 +58,4 @@ class FloodingRouter : public Router, protected PacketHistory
|
||||
* Look for broadcasts we need to rebroadcast
|
||||
*/
|
||||
virtual void sniffReceived(const meshtastic_MeshPacket *p, const meshtastic_Routing *c) override;
|
||||
};
|
||||
};
|
||||
@@ -73,58 +73,3 @@ template <class T> class MemoryDynamic : public Allocator<T>
|
||||
return p;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A pool based allocator
|
||||
*
|
||||
*/
|
||||
template <class T> class MemoryPool : public Allocator<T>
|
||||
{
|
||||
PointerQueue<T> dead;
|
||||
|
||||
T *buf; // our large raw block of memory
|
||||
|
||||
size_t maxElements;
|
||||
|
||||
public:
|
||||
explicit MemoryPool(size_t _maxElements) : dead(_maxElements), maxElements(_maxElements)
|
||||
{
|
||||
buf = new T[maxElements];
|
||||
|
||||
// prefill dead
|
||||
for (size_t i = 0; i < maxElements; i++)
|
||||
release(&buf[i]);
|
||||
}
|
||||
|
||||
~MemoryPool() { delete[] buf; }
|
||||
|
||||
/// Return a buffer for use by others
|
||||
void release(T *p)
|
||||
{
|
||||
assert(p >= buf &&
|
||||
(size_t)(p - buf) <
|
||||
maxElements); // sanity check to make sure a programmer didn't free something that didn't come from this pool
|
||||
assert(dead.enqueue(p, 0));
|
||||
}
|
||||
|
||||
#ifdef HAS_FREE_RTOS
|
||||
/// Return a buffer from an ISR, if higherPriWoken is set to true you have some work to do ;-)
|
||||
void releaseFromISR(T *p, BaseType_t *higherPriWoken)
|
||||
{
|
||||
assert(p >= buf &&
|
||||
(size_t)(p - buf) <
|
||||
maxElements); // sanity check to make sure a programmer didn't free something that didn't come from this pool
|
||||
assert(dead.enqueueFromISR(p, higherPriWoken));
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
/// Return a queable object which has been prefilled with zeros - allow timeout to wait for available buffers (you
|
||||
/// probably don't want this version).
|
||||
virtual T *alloc(TickType_t maxWait)
|
||||
{
|
||||
T *p = dead.dequeuePtr(maxWait);
|
||||
assert(p);
|
||||
return p;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -140,6 +140,22 @@ void MeshService::reloadOwner(bool shouldSave)
|
||||
}
|
||||
}
|
||||
|
||||
// search the queue for a request id and return the matching nodenum
|
||||
NodeNum MeshService::getNodenumFromRequestId(uint32_t request_id)
|
||||
{
|
||||
NodeNum nodenum = 0;
|
||||
for (int i = 0; i < toPhoneQueue.numUsed(); i++) {
|
||||
meshtastic_MeshPacket *p = toPhoneQueue.dequeuePtr(0);
|
||||
if (p->id == request_id) {
|
||||
nodenum = p->to;
|
||||
// make sure to continue this to make one full loop
|
||||
}
|
||||
// put it right back on the queue
|
||||
toPhoneQueue.enqueue(p, 0);
|
||||
}
|
||||
return nodenum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a ToRadio buffer parse it and properly handle it (setup radio, owner or send packet into the mesh)
|
||||
* Called by PhoneAPI.handleToRadio. Note: p is a scratch buffer, this function is allowed to write to it but it can not keep a
|
||||
@@ -267,14 +283,22 @@ void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies)
|
||||
|
||||
void MeshService::sendToPhone(meshtastic_MeshPacket *p)
|
||||
{
|
||||
perhapsDecode(p);
|
||||
|
||||
if (toPhoneQueue.numFree() == 0) {
|
||||
LOG_WARN("ToPhone queue is full, discarding oldest\n");
|
||||
meshtastic_MeshPacket *d = toPhoneQueue.dequeuePtr(0);
|
||||
if (d)
|
||||
releaseToPool(d);
|
||||
if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP ||
|
||||
p->decoded.portnum == meshtastic_PortNum_RANGE_TEST_APP) {
|
||||
LOG_WARN("ToPhone queue is full, discarding oldest\n");
|
||||
meshtastic_MeshPacket *d = toPhoneQueue.dequeuePtr(0);
|
||||
if (d)
|
||||
releaseToPool(d);
|
||||
} else {
|
||||
LOG_WARN("ToPhone queue is full, dropping packet.\n");
|
||||
releaseToPool(p);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
perhapsDecode(p);
|
||||
assert(toPhoneQueue.enqueue(p, 0));
|
||||
fromNum++;
|
||||
}
|
||||
@@ -312,7 +336,9 @@ meshtastic_NodeInfoLite *MeshService::refreshLocalMeshNode()
|
||||
|
||||
position.time = getValidTime(RTCQualityFromNet);
|
||||
|
||||
updateBatteryLevel(powerStatus->getBatteryChargePercent());
|
||||
if (powerStatus->getHasBattery() == 1) {
|
||||
updateBatteryLevel(powerStatus->getBatteryChargePercent());
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
@@ -320,28 +346,26 @@ meshtastic_NodeInfoLite *MeshService::refreshLocalMeshNode()
|
||||
int MeshService::onGPSChanged(const meshtastic::GPSStatus *newStatus)
|
||||
{
|
||||
// Update our local node info with our position (even if we don't decide to update anyone else)
|
||||
meshtastic_NodeInfoLite *node = refreshLocalMeshNode();
|
||||
const meshtastic_NodeInfoLite *node = refreshLocalMeshNode();
|
||||
meshtastic_Position pos = meshtastic_Position_init_default;
|
||||
|
||||
if (newStatus->getHasLock()) {
|
||||
// load data from GPS object, will add timestamp + battery further down
|
||||
pos = gps->p;
|
||||
} else {
|
||||
// The GPS has lost lock, if we are fixed position we should just keep using
|
||||
// the old position
|
||||
// The GPS has lost lock
|
||||
#ifdef GPS_EXTRAVERBOSE
|
||||
LOG_DEBUG("onGPSchanged() - lost validLocation\n");
|
||||
#endif
|
||||
if (config.position.fixed_position) {
|
||||
LOG_WARN("Using fixed position\n");
|
||||
pos = ConvertToPosition(node->position);
|
||||
}
|
||||
}
|
||||
// Used fixed position if configured regalrdless of GPS lock
|
||||
if (config.position.fixed_position) {
|
||||
LOG_WARN("Using fixed position\n");
|
||||
pos = TypeConversions::ConvertToPosition(node->position);
|
||||
}
|
||||
|
||||
// Finally add a fresh timestamp and battery level reading
|
||||
// I KNOW this is redundant with refreshLocalMeshNode() above, but these are
|
||||
// inexpensive nonblocking calls and can be refactored in due course
|
||||
pos.time = getValidTime(RTCQualityGPS);
|
||||
// Add a fresh timestamp
|
||||
pos.time = getValidTime(RTCQualityFromNet);
|
||||
|
||||
// In debug logs, identify position by @timestamp:stage (stage 4 = nodeDB)
|
||||
LOG_DEBUG("onGPSChanged() pos@%x, time=%u, lat=%d, lon=%d, alt=%d\n", pos.timestamp, pos.time, pos.latitude_i,
|
||||
|
||||
@@ -48,6 +48,14 @@ class MeshService
|
||||
uint32_t oldFromNum = 0;
|
||||
|
||||
public:
|
||||
static bool isTextPayload(const meshtastic_MeshPacket *p)
|
||||
{
|
||||
if (moduleConfig.range_test.enabled && p->decoded.portnum == meshtastic_PortNum_RANGE_TEST_APP) {
|
||||
return true;
|
||||
}
|
||||
return p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP ||
|
||||
p->decoded.portnum == meshtastic_PortNum_DETECTION_SENSOR_APP;
|
||||
}
|
||||
/// Called when some new packets have arrived from one of the radios
|
||||
Observable<uint32_t> fromNumChanged;
|
||||
|
||||
@@ -74,6 +82,9 @@ class MeshService
|
||||
/// Return the next MqttClientProxyMessage packet destined to the phone.
|
||||
meshtastic_MqttClientProxyMessage *getMqttClientProxyMessageForPhone() { return toPhoneMqttProxyQueue.dequeuePtr(0); }
|
||||
|
||||
// search the queue for a request id and return the matching nodenum
|
||||
NodeNum getNodenumFromRequestId(uint32_t request_id);
|
||||
|
||||
// Release QueueStatus packet to pool
|
||||
void releaseQueueStatusToPool(meshtastic_QueueStatus *p) { queueStatusPool.release(p); }
|
||||
|
||||
|
||||
@@ -15,17 +15,22 @@
|
||||
#include "error.h"
|
||||
#include "main.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include "modules/NeighborInfoModule.h"
|
||||
#include <ErriezCRC32.h>
|
||||
#include <pb_decode.h>
|
||||
#include <pb_encode.h>
|
||||
|
||||
#ifdef ARCH_ESP32
|
||||
#include "mesh/http/WiFiAPClient.h"
|
||||
#include "mesh/wifi/WiFiAPClient.h"
|
||||
#include "modules/esp32/StoreForwardModule.h"
|
||||
#include <Preferences.h>
|
||||
#include <nvs_flash.h>
|
||||
#endif
|
||||
|
||||
#ifdef ARCH_RASPBERRY_PI
|
||||
#include "platform/portduino/PortduinoGlue.h"
|
||||
#endif
|
||||
|
||||
#ifdef ARCH_NRF52
|
||||
#include <bluefruit.h>
|
||||
#include <utility/bonding.h>
|
||||
@@ -63,11 +68,7 @@ uint32_t error_address = 0;
|
||||
|
||||
static uint8_t ourMacAddr[6];
|
||||
|
||||
NodeDB::NodeDB()
|
||||
: nodes(devicestate.node_db), numNodes(&devicestate.node_db_count), meshNodes(devicestate.node_db_lite),
|
||||
numMeshNodes(&devicestate.node_db_lite_count)
|
||||
{
|
||||
}
|
||||
NodeDB::NodeDB() : meshNodes(devicestate.node_db_lite), numMeshNodes(&devicestate.node_db_lite_count) {}
|
||||
|
||||
/**
|
||||
* Most (but not always) of the time we want to treat packets 'from' the local phone (where from == 0), as if they originated on
|
||||
@@ -172,6 +173,14 @@ void NodeDB::installDefaultConfig()
|
||||
config.lora.region = meshtastic_Config_LoRaConfig_RegionCode_UNSET;
|
||||
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST;
|
||||
config.lora.hop_limit = HOP_RELIABLE;
|
||||
#ifdef PIN_GPS_EN
|
||||
config.position.gps_en_gpio = PIN_GPS_EN;
|
||||
#endif
|
||||
#ifdef GPS_POWER_TOGGLE
|
||||
config.device.disable_triple_click = false;
|
||||
#else
|
||||
config.device.disable_triple_click = true;
|
||||
#endif
|
||||
config.position.gps_enabled = true;
|
||||
config.position.position_broadcast_smart_enabled = true;
|
||||
config.position.broadcast_smart_minimum_distance = 100;
|
||||
@@ -186,6 +195,12 @@ void NodeDB::installDefaultConfig()
|
||||
config.bluetooth.fixed_pin = defaultBLEPin;
|
||||
#if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7789_CS)
|
||||
bool hasScreen = true;
|
||||
#elif ARCH_RASPBERRY_PI
|
||||
bool hasScreen = false;
|
||||
if (settingsMap[displayPanel])
|
||||
hasScreen = true;
|
||||
else
|
||||
hasScreen = screen_found.port != ScanI2C::I2CPort::NO_I2C;
|
||||
#else
|
||||
bool hasScreen = screen_found.port != ScanI2C::I2CPort::NO_I2C;
|
||||
#endif
|
||||
@@ -193,7 +208,9 @@ void NodeDB::installDefaultConfig()
|
||||
: meshtastic_Config_BluetoothConfig_PairingMode_FIXED_PIN;
|
||||
// for backward compat, default position flags are ALT+MSL
|
||||
config.position.position_flags =
|
||||
(meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE | meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE_MSL);
|
||||
(meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE | meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE_MSL |
|
||||
meshtastic_Config_PositionConfig_PositionFlags_SPEED | meshtastic_Config_PositionConfig_PositionFlags_HEADING |
|
||||
meshtastic_Config_PositionConfig_PositionFlags_DOP | meshtastic_Config_PositionConfig_PositionFlags_SATINVIEW);
|
||||
|
||||
#ifdef T_WATCH_S3
|
||||
config.display.screen_on_secs = 30;
|
||||
@@ -238,15 +255,41 @@ void NodeDB::installDefaultModuleConfig()
|
||||
moduleConfig.external_notification.output_ms = 1000;
|
||||
moduleConfig.external_notification.nag_timeout = 60;
|
||||
#endif
|
||||
#ifdef T_WATCH_S3
|
||||
// Don't worry about the other settings, we'll use the DRV2056 behavior for notifications
|
||||
#ifdef HAS_I2S
|
||||
// Don't worry about the other settings for T-Watch, we'll also use the DRV2056 behavior for notifications
|
||||
moduleConfig.external_notification.enabled = true;
|
||||
moduleConfig.external_notification.use_i2s_as_buzzer = true;
|
||||
moduleConfig.external_notification.alert_message_buzzer = true;
|
||||
moduleConfig.external_notification.nag_timeout = 60;
|
||||
#endif
|
||||
#ifdef NANO_G2_ULTRA
|
||||
moduleConfig.external_notification.enabled = true;
|
||||
moduleConfig.external_notification.alert_message = true;
|
||||
moduleConfig.external_notification.output_ms = 100;
|
||||
moduleConfig.external_notification.active = true;
|
||||
#endif
|
||||
moduleConfig.has_canned_message = true;
|
||||
|
||||
strncpy(moduleConfig.mqtt.address, default_mqtt_address, sizeof(moduleConfig.mqtt.address));
|
||||
strncpy(moduleConfig.mqtt.username, default_mqtt_username, sizeof(moduleConfig.mqtt.username));
|
||||
strncpy(moduleConfig.mqtt.password, default_mqtt_password, sizeof(moduleConfig.mqtt.password));
|
||||
strncpy(moduleConfig.mqtt.root, default_mqtt_root, sizeof(moduleConfig.mqtt.root));
|
||||
moduleConfig.mqtt.encryption_enabled = true;
|
||||
|
||||
moduleConfig.has_neighbor_info = true;
|
||||
moduleConfig.neighbor_info.enabled = false;
|
||||
|
||||
moduleConfig.has_detection_sensor = true;
|
||||
moduleConfig.detection_sensor.enabled = false;
|
||||
moduleConfig.detection_sensor.detection_triggered_high = true;
|
||||
moduleConfig.detection_sensor.minimum_broadcast_secs = 45;
|
||||
|
||||
moduleConfig.has_ambient_lighting = true;
|
||||
moduleConfig.ambient_lighting.current = 10;
|
||||
// Default to a color based on our node number
|
||||
moduleConfig.ambient_lighting.red = (myNodeInfo.my_node_num & 0xFF0000) >> 16;
|
||||
moduleConfig.ambient_lighting.green = (myNodeInfo.my_node_num & 0x00FF00) >> 8;
|
||||
moduleConfig.ambient_lighting.blue = myNodeInfo.my_node_num & 0x0000FF;
|
||||
|
||||
initModuleConfigIntervals();
|
||||
}
|
||||
@@ -263,6 +306,27 @@ void NodeDB::installRoleDefaults(meshtastic_Config_DeviceConfig_Role role)
|
||||
} else if (role == meshtastic_Config_DeviceConfig_Role_SENSOR) {
|
||||
moduleConfig.telemetry.environment_measurement_enabled = true;
|
||||
moduleConfig.telemetry.environment_update_interval = 300;
|
||||
} else if (role == meshtastic_Config_DeviceConfig_Role_LOST_AND_FOUND) {
|
||||
config.position.position_broadcast_smart_enabled = false;
|
||||
config.position.position_broadcast_secs = 300; // Every 5 minutes
|
||||
} else if (role == meshtastic_Config_DeviceConfig_Role_TAK) {
|
||||
config.device.node_info_broadcast_secs = ONE_DAY;
|
||||
config.position.position_broadcast_smart_enabled = false;
|
||||
config.position.position_broadcast_secs = ONE_DAY;
|
||||
// Remove Altitude MSL from flags since CoTs use HAE (height above ellipsoid)
|
||||
config.position.position_flags =
|
||||
(meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE | meshtastic_Config_PositionConfig_PositionFlags_SPEED |
|
||||
meshtastic_Config_PositionConfig_PositionFlags_HEADING | meshtastic_Config_PositionConfig_PositionFlags_DOP);
|
||||
moduleConfig.telemetry.device_update_interval = ONE_DAY;
|
||||
} else if (role == meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN) {
|
||||
config.device.rebroadcast_mode = meshtastic_Config_DeviceConfig_RebroadcastMode_LOCAL_ONLY;
|
||||
config.device.node_info_broadcast_secs = UINT32_MAX;
|
||||
config.position.position_broadcast_smart_enabled = false;
|
||||
config.position.position_broadcast_secs = UINT32_MAX;
|
||||
moduleConfig.neighbor_info.update_interval = UINT32_MAX;
|
||||
moduleConfig.telemetry.device_update_interval = UINT32_MAX;
|
||||
moduleConfig.telemetry.environment_update_interval = UINT32_MAX;
|
||||
moduleConfig.telemetry.air_quality_interval = UINT32_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,6 +335,7 @@ void NodeDB::initModuleConfigIntervals()
|
||||
moduleConfig.telemetry.device_update_interval = default_broadcast_interval_secs;
|
||||
moduleConfig.telemetry.environment_update_interval = default_broadcast_interval_secs;
|
||||
moduleConfig.telemetry.air_quality_interval = default_broadcast_interval_secs;
|
||||
moduleConfig.neighbor_info.update_interval = default_broadcast_interval_secs;
|
||||
}
|
||||
|
||||
void NodeDB::installDefaultChannels()
|
||||
@@ -282,12 +347,38 @@ void NodeDB::installDefaultChannels()
|
||||
|
||||
void NodeDB::resetNodes()
|
||||
{
|
||||
devicestate.node_db_count = 0;
|
||||
memset(devicestate.node_db, 0, sizeof(devicestate.node_db));
|
||||
|
||||
devicestate.node_db_lite_count = 0;
|
||||
memset(devicestate.node_db_lite, 0, sizeof(devicestate.node_db_lite));
|
||||
devicestate.node_db_lite_count = 1;
|
||||
std::fill(&devicestate.node_db_lite[1], &devicestate.node_db_lite[MAX_NUM_NODES - 1], meshtastic_NodeInfoLite());
|
||||
saveDeviceStateToDisk();
|
||||
if (neighborInfoModule && moduleConfig.neighbor_info.enabled)
|
||||
neighborInfoModule->resetNeighbors();
|
||||
}
|
||||
|
||||
void NodeDB::removeNodeByNum(uint nodeNum)
|
||||
{
|
||||
int newPos = 0, removed = 0;
|
||||
for (int i = 0; i < *numMeshNodes; i++) {
|
||||
if (meshNodes[i].num != nodeNum)
|
||||
meshNodes[newPos++] = meshNodes[i];
|
||||
else
|
||||
removed++;
|
||||
}
|
||||
*numMeshNodes -= removed;
|
||||
LOG_DEBUG("NodeDB::removeNodeByNum purged %d entries. Saving changes...\n", removed);
|
||||
saveDeviceStateToDisk();
|
||||
}
|
||||
|
||||
void NodeDB::cleanupMeshDB()
|
||||
{
|
||||
int newPos = 0, removed = 0;
|
||||
for (int i = 0; i < *numMeshNodes; i++) {
|
||||
if (meshNodes[i].has_user)
|
||||
meshNodes[newPos++] = meshNodes[i];
|
||||
else
|
||||
removed++;
|
||||
}
|
||||
*numMeshNodes -= removed;
|
||||
LOG_DEBUG("cleanupMeshDB purged %d entries\n", removed);
|
||||
}
|
||||
|
||||
void NodeDB::installDefaultDeviceState()
|
||||
@@ -295,12 +386,11 @@ void NodeDB::installDefaultDeviceState()
|
||||
LOG_INFO("Installing default DeviceState\n");
|
||||
memset(&devicestate, 0, sizeof(meshtastic_DeviceState));
|
||||
|
||||
*numNodes = 0;
|
||||
*numMeshNodes = 0;
|
||||
|
||||
// init our devicestate with valid flags so protobuf writing/reading will work
|
||||
devicestate.has_my_node = true;
|
||||
devicestate.has_owner = true;
|
||||
devicestate.node_db_count = 0;
|
||||
devicestate.node_db_lite_count = 0;
|
||||
devicestate.version = DEVICESTATE_CUR_VER;
|
||||
devicestate.receive_queue_count = 0; // Not yet implemented FIXME
|
||||
@@ -311,7 +401,6 @@ void NodeDB::installDefaultDeviceState()
|
||||
pickNewNodeNum(); // based on macaddr now
|
||||
snprintf(owner.long_name, sizeof(owner.long_name), "Meshtastic %02x%02x", ourMacAddr[4], ourMacAddr[5]);
|
||||
snprintf(owner.short_name, sizeof(owner.short_name), "%02x%02x", ourMacAddr[4], ourMacAddr[5]);
|
||||
|
||||
snprintf(owner.id, sizeof(owner.id), "!%08x", getNodeNum()); // Default node ID now based on nodenum
|
||||
memcpy(owner.macaddr, ourMacAddr, sizeof(owner.macaddr));
|
||||
}
|
||||
@@ -320,6 +409,7 @@ void NodeDB::init()
|
||||
{
|
||||
LOG_INFO("Initializing NodeDB\n");
|
||||
loadFromDisk();
|
||||
cleanupMeshDB();
|
||||
|
||||
uint32_t devicestateCRC = crc32Buffer(&devicestate, sizeof(devicestate));
|
||||
uint32_t configCRC = crc32Buffer(&config, sizeof(config));
|
||||
@@ -328,34 +418,21 @@ void NodeDB::init()
|
||||
int saveWhat = 0;
|
||||
|
||||
// likewise - we always want the app requirements to come from the running appload
|
||||
myNodeInfo.min_app_version = 20300; // format is Mmmss (where M is 1+the numeric major number. i.e. 20120 means 1.1.20
|
||||
myNodeInfo.max_channels = MAX_NUM_CHANNELS; // tell others the max # of channels we can understand
|
||||
myNodeInfo.min_app_version = 30200; // format is Mmmss (where M is 1+the numeric major number. i.e. 30200 means 2.2.00
|
||||
// Note! We do this after loading saved settings, so that if somehow an invalid nodenum was stored in preferences we won't
|
||||
// keep using that nodenum forever. Crummy guess at our nodenum (but we will check against the nodedb to avoid conflicts)
|
||||
strncpy(myNodeInfo.firmware_version, optstr(APP_VERSION), sizeof(myNodeInfo.firmware_version));
|
||||
pickNewNodeNum();
|
||||
|
||||
// Set our board type so we can share it with others
|
||||
owner.hw_model = HW_VENDOR;
|
||||
// Ensure user (nodeinfo) role is set to whatever we're configured to
|
||||
owner.role = config.device.role;
|
||||
|
||||
// Include our owner in the node db under our nodenum
|
||||
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getNodeNum());
|
||||
info->user = owner;
|
||||
info->has_user = true;
|
||||
|
||||
if (*numNodes > 0) {
|
||||
LOG_DEBUG("Legacy NodeDB detected... Migrating to NodeDBLite\n");
|
||||
uint32_t readIndex = 0;
|
||||
const meshtastic_NodeInfo *oldNodeInfo = nodeDB.readNextNodeInfo(readIndex);
|
||||
while (oldNodeInfo != NULL) {
|
||||
migrateToNodeInfoLite(oldNodeInfo);
|
||||
oldNodeInfo = nodeDB.readNextNodeInfo(readIndex);
|
||||
}
|
||||
LOG_DEBUG("Migration complete! Clearing out legacy NodeDB...\n");
|
||||
devicestate.node_db_count = 0;
|
||||
memset(devicestate.node_db, 0, sizeof(devicestate.node_db));
|
||||
}
|
||||
|
||||
#ifdef ARCH_ESP32
|
||||
Preferences preferences;
|
||||
preferences.begin("meshtastic", false);
|
||||
@@ -365,7 +442,7 @@ void NodeDB::init()
|
||||
#endif
|
||||
|
||||
resetRadioConfig(); // If bogus settings got saved, then fix them
|
||||
LOG_DEBUG("region=%d, NODENUM=0x%x, dbsize=%d\n", config.lora.region, myNodeInfo.my_node_num, *numNodes);
|
||||
LOG_DEBUG("region=%d, NODENUM=0x%x, dbsize=%d\n", config.lora.region, myNodeInfo.my_node_num, *numMeshNodes);
|
||||
|
||||
if (devicestateCRC != crc32Buffer(&devicestate, sizeof(devicestate)))
|
||||
saveWhat |= SEGMENT_DEVICESTATE;
|
||||
@@ -390,25 +467,25 @@ void NodeDB::init()
|
||||
*/
|
||||
void NodeDB::pickNewNodeNum()
|
||||
{
|
||||
NodeNum r = myNodeInfo.my_node_num;
|
||||
|
||||
#ifdef ARCH_RASPBERRY_PI
|
||||
getPiMacAddr(ourMacAddr); // Make sure ourMacAddr is set
|
||||
#else
|
||||
getMacAddr(ourMacAddr); // Make sure ourMacAddr is set
|
||||
#endif
|
||||
|
||||
// Pick an initial nodenum based on the macaddr
|
||||
r = (ourMacAddr[2] << 24) | (ourMacAddr[3] << 16) | (ourMacAddr[4] << 8) | ourMacAddr[5];
|
||||
|
||||
if (r == NODENUM_BROADCAST || r < NUM_RESERVED)
|
||||
r = NUM_RESERVED; // don't pick a reserved node number
|
||||
NodeNum nodeNum = (ourMacAddr[2] << 24) | (ourMacAddr[3] << 16) | (ourMacAddr[4] << 8) | ourMacAddr[5];
|
||||
|
||||
meshtastic_NodeInfoLite *found;
|
||||
while ((found = getMeshNode(r)) && memcmp(found->user.macaddr, owner.macaddr, sizeof(owner.macaddr))) {
|
||||
// FIXME: input for random() is int, so NODENUM_BROADCAST becomes -1
|
||||
NodeNum n = random(NUM_RESERVED, NODENUM_BROADCAST); // try a new random choice
|
||||
LOG_WARN("NOTE! Our desired nodenum 0x%x is in use, so trying for 0x%x\n", r, n);
|
||||
r = n;
|
||||
while ((nodeNum == NODENUM_BROADCAST || nodeNum < NUM_RESERVED) ||
|
||||
((found = getMeshNode(nodeNum)) && memcmp(found->user.macaddr, owner.macaddr, sizeof(owner.macaddr)) != 0)) {
|
||||
NodeNum candidate = random(NUM_RESERVED, LONG_MAX); // try a new random choice
|
||||
LOG_WARN("NOTE! Our desired nodenum 0x%x is invalid or in use, so trying for 0x%x\n", nodeNum, candidate);
|
||||
nodeNum = candidate;
|
||||
}
|
||||
LOG_WARN("Using nodenum 0x%x \n", nodeNum);
|
||||
|
||||
myNodeInfo.my_node_num = r;
|
||||
myNodeInfo.my_node_num = nodeNum;
|
||||
}
|
||||
|
||||
static const char *prefFileName = "/prefs/db.proto";
|
||||
@@ -611,14 +688,6 @@ void NodeDB::saveToDisk(int saveWhat)
|
||||
}
|
||||
}
|
||||
|
||||
const meshtastic_NodeInfo *NodeDB::readNextNodeInfo(uint32_t &readIndex)
|
||||
{
|
||||
if (readIndex < *numNodes)
|
||||
return &nodes[readIndex++];
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const meshtastic_NodeInfoLite *NodeDB::readNextMeshNode(uint32_t &readIndex)
|
||||
{
|
||||
if (readIndex < *numMeshNodes)
|
||||
@@ -680,8 +749,8 @@ void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSou
|
||||
LOG_INFO("updatePosition LOCAL pos@%x, time=%u, latI=%d, lonI=%d, alt=%d\n", p.timestamp, p.time, p.latitude_i,
|
||||
p.longitude_i, p.altitude);
|
||||
|
||||
info->position = ConvertToPositionLite(p);
|
||||
localPosition = p;
|
||||
setLocalPosition(p);
|
||||
info->position = TypeConversions::ConvertToPositionLite(p);
|
||||
} else if ((p.time > 0) && !p.latitude_i && !p.longitude_i && !p.timestamp && !p.location_source) {
|
||||
// FIXME SPECIAL TIME SETTING PACKET FROM EUD TO RADIO
|
||||
// (stop-gap fix for issue #900)
|
||||
@@ -699,7 +768,7 @@ void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSou
|
||||
uint32_t tmp_time = info->position.time;
|
||||
|
||||
// Next, update atomically
|
||||
info->position = ConvertToPositionLite(p);
|
||||
info->position = TypeConversions::ConvertToPositionLite(p);
|
||||
|
||||
// Last, restore any fields that may have been overwritten
|
||||
if (!info->position.time)
|
||||
@@ -789,24 +858,13 @@ void NodeDB::updateFrom(const meshtastic_MeshPacket &mp)
|
||||
|
||||
uint8_t NodeDB::getMeshNodeChannel(NodeNum n)
|
||||
{
|
||||
meshtastic_NodeInfoLite *info = getMeshNode(n);
|
||||
const meshtastic_NodeInfoLite *info = getMeshNode(n);
|
||||
if (!info) {
|
||||
return 0; // defaults to PRIMARY
|
||||
}
|
||||
return info->channel;
|
||||
}
|
||||
|
||||
/// Find a node in our DB, return null for missing
|
||||
/// NOTE: This function might be called from an ISR
|
||||
meshtastic_NodeInfo *NodeDB::getNodeInfo(NodeNum n)
|
||||
{
|
||||
for (int i = 0; i < *numNodes; i++)
|
||||
if (nodes[i].num == n)
|
||||
return &nodes[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// Find a node in our DB, return null for missing
|
||||
/// NOTE: This function might be called from an ISR
|
||||
meshtastic_NodeInfoLite *NodeDB::getMeshNode(NodeNum n)
|
||||
@@ -825,7 +883,8 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n)
|
||||
|
||||
if (!lite) {
|
||||
if ((*numMeshNodes >= MAX_NUM_NODES) || (memGet.getFreeHeap() < meshtastic_NodeInfoLite_size * 3)) {
|
||||
screen->print("warning: node_db_lite full! erasing oldest entry\n");
|
||||
if (screen)
|
||||
screen->print("warning: node_db_lite full! erasing oldest entry\n");
|
||||
// look for oldest node and erase it
|
||||
uint32_t oldest = UINT32_MAX;
|
||||
int oldestIndex = -1;
|
||||
@@ -852,57 +911,6 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n)
|
||||
return lite;
|
||||
}
|
||||
|
||||
void NodeDB::migrateToNodeInfoLite(const meshtastic_NodeInfo *node)
|
||||
{
|
||||
meshtastic_NodeInfoLite *lite = getMeshNode(node->num);
|
||||
|
||||
if (!lite) {
|
||||
if ((*numMeshNodes >= MAX_NUM_NODES) || (memGet.getFreeHeap() < meshtastic_NodeInfoLite_size * 3)) {
|
||||
screen->print("warning: node_db_lite full! erasing oldest entry\n");
|
||||
// look for oldest node and erase it
|
||||
uint32_t oldest = UINT32_MAX;
|
||||
int oldestIndex = -1;
|
||||
for (int i = 0; i < *numMeshNodes; i++) {
|
||||
if (meshNodes[i].last_heard < oldest) {
|
||||
oldest = meshNodes[i].last_heard;
|
||||
oldestIndex = i;
|
||||
}
|
||||
}
|
||||
// Shove the remaining nodes down the chain
|
||||
for (int i = oldestIndex; i < *numMeshNodes - 1; i++) {
|
||||
meshNodes[i] = meshNodes[i + 1];
|
||||
}
|
||||
(*numMeshNodes)--;
|
||||
}
|
||||
// add the node at the end
|
||||
lite = &meshNodes[(*numMeshNodes)++];
|
||||
|
||||
// everything is missing except the nodenum
|
||||
memset(lite, 0, sizeof(*lite));
|
||||
lite->num = node->num;
|
||||
lite->snr = node->snr;
|
||||
lite->last_heard = node->last_heard;
|
||||
lite->channel = node->channel;
|
||||
|
||||
if (node->has_position) {
|
||||
lite->has_position = true;
|
||||
lite->position.latitude_i = node->position.latitude_i;
|
||||
lite->position.longitude_i = node->position.longitude_i;
|
||||
lite->position.altitude = node->position.altitude;
|
||||
lite->position.location_source = node->position.location_source;
|
||||
lite->position.time = node->position.time;
|
||||
}
|
||||
if (node->has_user) {
|
||||
lite->has_user = true;
|
||||
lite->user = node->user;
|
||||
}
|
||||
if (node->has_device_metrics) {
|
||||
lite->has_device_metrics = true;
|
||||
lite->device_metrics = node->device_metrics;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Record an error that should be reported via analytics
|
||||
void recordCriticalError(meshtastic_CriticalErrorCode code, uint32_t address, const char *filename)
|
||||
{
|
||||
|
||||
@@ -19,7 +19,7 @@ DeviceState versions used to be defined in the .proto file but really only this
|
||||
#define SEGMENT_DEVICESTATE 4
|
||||
#define SEGMENT_CHANNELS 8
|
||||
|
||||
#define DEVICESTATE_CUR_VER 20
|
||||
#define DEVICESTATE_CUR_VER 22
|
||||
#define DEVICESTATE_MIN_VER DEVICESTATE_CUR_VER
|
||||
|
||||
extern meshtastic_DeviceState devicestate;
|
||||
@@ -45,9 +45,6 @@ class NodeDB
|
||||
// Eventually use a smarter datastructure
|
||||
// HashMap<NodeNum, NodeInfo> nodes;
|
||||
// Note: these two references just point into our static array we serialize to/from disk
|
||||
meshtastic_NodeInfo *nodes;
|
||||
pb_size_t *numNodes;
|
||||
|
||||
meshtastic_NodeInfoLite *meshNodes;
|
||||
pb_size_t *numMeshNodes;
|
||||
|
||||
@@ -114,7 +111,7 @@ class NodeDB
|
||||
/// Return the number of nodes we've heard from recently (within the last 2 hrs?)
|
||||
size_t getNumOnlineMeshNodes();
|
||||
|
||||
void initConfigIntervals(), initModuleConfigIntervals(), resetNodes();
|
||||
void initConfigIntervals(), initModuleConfigIntervals(), resetNodes(), removeNodeByNum(uint nodeNum);
|
||||
|
||||
bool factoryReset();
|
||||
|
||||
@@ -134,21 +131,16 @@ class NodeDB
|
||||
meshtastic_NodeInfoLite *getMeshNode(NodeNum n);
|
||||
size_t getNumMeshNodes() { return *numMeshNodes; }
|
||||
|
||||
void setLocalPosition(meshtastic_Position position)
|
||||
{
|
||||
LOG_DEBUG("Setting local position: latitude=%i, longitude=%i, time=%i\n", position.latitude_i, position.longitude_i,
|
||||
position.time);
|
||||
localPosition = position;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Find a node in our DB, create an empty NodeInfoLite if missing
|
||||
meshtastic_NodeInfoLite *getOrCreateMeshNode(NodeNum n);
|
||||
void migrateToNodeInfoLite(const meshtastic_NodeInfo *node);
|
||||
/// Find a node in our DB, return null for missing
|
||||
meshtastic_NodeInfo *getNodeInfo(NodeNum n);
|
||||
/// Allow the bluetooth layer to read our next nodeinfo record, or NULL if done reading
|
||||
const meshtastic_NodeInfo *readNextNodeInfo(uint32_t &readIndex);
|
||||
size_t getNumNodes() { return *numNodes; }
|
||||
|
||||
meshtastic_NodeInfo *getNodeByIndex(size_t x)
|
||||
{
|
||||
assert(x < *numNodes);
|
||||
return &nodes[x];
|
||||
}
|
||||
|
||||
/// Notify observers of changes to the DB
|
||||
void notifyObservers(bool forceUpdate = false)
|
||||
@@ -161,6 +153,9 @@ class NodeDB
|
||||
/// read our db from flash
|
||||
void loadFromDisk();
|
||||
|
||||
/// purge db entries without user info
|
||||
void cleanupMeshDB();
|
||||
|
||||
/// Reinit device state from scratch (not loading from disk)
|
||||
void installDefaultDeviceState(), installDefaultChannels(), installDefaultConfig(), installDefaultModuleConfig();
|
||||
};
|
||||
@@ -204,11 +199,12 @@ extern NodeDB nodeDB;
|
||||
#define default_sds_secs IF_ROUTER(ONE_DAY, UINT32_MAX) // Default to forever super deep sleep
|
||||
#define default_ls_secs IF_ROUTER(ONE_DAY, 5 * 60)
|
||||
#define default_min_wake_secs 10
|
||||
#define default_screen_on_secs 60 * 10
|
||||
#define default_screen_on_secs IF_ROUTER(1, 60 * 10)
|
||||
|
||||
#define default_mqtt_address "mqtt.meshtastic.org"
|
||||
#define default_mqtt_username "meshdev"
|
||||
#define default_mqtt_password "large4cats"
|
||||
#define default_mqtt_root "msh"
|
||||
|
||||
inline uint32_t getConfiguredOrDefaultMs(uint32_t configuredInterval)
|
||||
{
|
||||
@@ -224,12 +220,16 @@ inline uint32_t getConfiguredOrDefaultMs(uint32_t configuredInterval, uint32_t d
|
||||
return defaultInterval * 1000;
|
||||
}
|
||||
|
||||
inline uint32_t getConfiguredOrDefault(uint32_t configured, uint32_t defaultValue)
|
||||
{
|
||||
if (configured > 0)
|
||||
return configured;
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/// Sometimes we will have Position objects that only have a time, so check for
|
||||
/// valid lat/lon
|
||||
static inline bool hasValidPosition(const meshtastic_NodeInfo *n)
|
||||
{
|
||||
return n->has_position && (n->position.latitude_i != 0 || n->position.longitude_i != 0);
|
||||
}
|
||||
static inline bool hasValidPosition(const meshtastic_NodeInfoLite *n)
|
||||
{
|
||||
return n->has_position && (n->position.latitude_i != 0 || n->position.longitude_i != 0);
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
#include "Router.h"
|
||||
#include <unordered_set>
|
||||
|
||||
/// We clear our old flood record five minute after we see the last of it
|
||||
#define FLOOD_EXPIRE_TIME (5 * 60 * 1000L)
|
||||
/// We clear our old flood record 10 minutes after we see the last of it
|
||||
#define FLOOD_EXPIRE_TIME (10 * 60 * 1000L)
|
||||
|
||||
/**
|
||||
* A record of a recent message broadcast
|
||||
|
||||
@@ -155,11 +155,18 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
// app not to send locations on our behalf.
|
||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_my_info_tag;
|
||||
fromRadioScratch.my_info = myNodeInfo;
|
||||
state = STATE_SEND_NODEINFO;
|
||||
state = STATE_SEND_METADATA;
|
||||
|
||||
service.refreshLocalMeshNode(); // Update my NodeInfo because the client will be asking for it soon.
|
||||
break;
|
||||
|
||||
case STATE_SEND_METADATA:
|
||||
LOG_INFO("getFromRadio=STATE_SEND_METADATA\n");
|
||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_metadata_tag;
|
||||
fromRadioScratch.metadata = getDeviceMetadata();
|
||||
state = STATE_SEND_NODEINFO;
|
||||
break;
|
||||
|
||||
case STATE_SEND_NODEINFO: {
|
||||
LOG_INFO("getFromRadio=STATE_SEND_NODEINFO\n");
|
||||
|
||||
@@ -279,6 +286,18 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_remote_hardware_tag;
|
||||
fromRadioScratch.moduleConfig.payload_variant.remote_hardware = moduleConfig.remote_hardware;
|
||||
break;
|
||||
case meshtastic_ModuleConfig_neighbor_info_tag:
|
||||
fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_neighbor_info_tag;
|
||||
fromRadioScratch.moduleConfig.payload_variant.neighbor_info = moduleConfig.neighbor_info;
|
||||
break;
|
||||
case meshtastic_ModuleConfig_detection_sensor_tag:
|
||||
fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_detection_sensor_tag;
|
||||
fromRadioScratch.moduleConfig.payload_variant.detection_sensor = moduleConfig.detection_sensor;
|
||||
break;
|
||||
case meshtastic_ModuleConfig_ambient_lighting_tag:
|
||||
fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_ambient_lighting_tag;
|
||||
fromRadioScratch.moduleConfig.payload_variant.ambient_lighting = moduleConfig.ambient_lighting;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("Unknown module config type %d\n", config_state);
|
||||
}
|
||||
@@ -286,15 +305,11 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
config_state++;
|
||||
// Advance when we have sent all of our ModuleConfig objects
|
||||
if (config_state > (_meshtastic_AdminMessage_ModuleConfigType_MAX + 1)) {
|
||||
state = STATE_SEND_METADATA;
|
||||
state = STATE_SEND_COMPLETE_ID;
|
||||
config_state = 0;
|
||||
}
|
||||
break;
|
||||
case STATE_SEND_METADATA:
|
||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_metadata_tag;
|
||||
fromRadioScratch.metadata = getDeviceMetadata();
|
||||
state = STATE_SEND_COMPLETE_ID;
|
||||
break;
|
||||
|
||||
case STATE_SEND_COMPLETE_ID:
|
||||
LOG_INFO("getFromRadio=STATE_SEND_COMPLETE_ID\n");
|
||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_config_complete_id_tag;
|
||||
@@ -394,7 +409,7 @@ bool PhoneAPI::available()
|
||||
if (nodeInfoForPhone.num == 0) {
|
||||
auto nextNode = nodeDB.readNextMeshNode(readIndex);
|
||||
if (nextNode) {
|
||||
nodeInfoForPhone = ConvertToNodeInfo(nextNode);
|
||||
nodeInfoForPhone = TypeConversions::ConvertToNodeInfo(nextNode);
|
||||
}
|
||||
}
|
||||
return true; // Always say we have something, because we might need to advance our state machine
|
||||
|
||||
@@ -37,9 +37,6 @@ bool RF95Interface::init()
|
||||
{
|
||||
RadioLibInterface::init();
|
||||
|
||||
if (power == 0)
|
||||
power = POWER_DEFAULT;
|
||||
|
||||
if (power > MAX_POWER) // This chip has lower power limits than some
|
||||
power = MAX_POWER;
|
||||
|
||||
|
||||
@@ -384,27 +384,27 @@ void RadioInterface::applyModemConfig()
|
||||
switch (loraConfig.modem_preset) {
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST:
|
||||
bw = (myRegion->wideLora) ? 812.5 : 250;
|
||||
cr = 8;
|
||||
cr = 5;
|
||||
sf = 7;
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW:
|
||||
bw = (myRegion->wideLora) ? 812.5 : 250;
|
||||
cr = 8;
|
||||
cr = 5;
|
||||
sf = 8;
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST:
|
||||
bw = (myRegion->wideLora) ? 812.5 : 250;
|
||||
cr = 8;
|
||||
cr = 5;
|
||||
sf = 9;
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW:
|
||||
bw = (myRegion->wideLora) ? 812.5 : 250;
|
||||
cr = 8;
|
||||
cr = 5;
|
||||
sf = 10;
|
||||
break;
|
||||
default: // Config_LoRaConfig_ModemPreset_LONG_FAST is default. Gracefully use this is preset is something illegal.
|
||||
bw = (myRegion->wideLora) ? 812.5 : 250;
|
||||
cr = 8;
|
||||
cr = 5;
|
||||
sf = 11;
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE:
|
||||
@@ -448,7 +448,9 @@ void RadioInterface::applyModemConfig()
|
||||
power = myRegion->powerLimit;
|
||||
|
||||
if (power == 0)
|
||||
power = 17; // Default to default power if we don't have a valid power
|
||||
power = 17; // Default to this power level if we don't have a valid regional power limit (powerLimit of myRegion defaults
|
||||
// to 0, currently no region has an actual power limit of 0 [dBm] so we can assume regions which have this
|
||||
// variable set to 0 don't have a valid power limit)
|
||||
|
||||
// Set final tx_power back onto config
|
||||
loraConfig.tx_power = (int8_t)power; // cppcheck-suppress assignmentAddressToInteger
|
||||
@@ -534,8 +536,8 @@ size_t RadioInterface::beginSending(meshtastic_MeshPacket *p)
|
||||
h->id = p->id;
|
||||
h->channel = p->channel;
|
||||
if (p->hop_limit > HOP_MAX) {
|
||||
LOG_WARN("hop limit %d is too high, setting to %d\n", p->hop_limit, HOP_MAX);
|
||||
p->hop_limit = HOP_MAX;
|
||||
LOG_WARN("hop limit %d is too high, setting to %d\n", p->hop_limit, HOP_RELIABLE);
|
||||
p->hop_limit = HOP_RELIABLE;
|
||||
}
|
||||
h->flags = p->hop_limit | (p->want_ack ? PACKET_FLAGS_WANT_ACK_MASK : 0);
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ class RadioInterface
|
||||
|
||||
float bw = 125;
|
||||
uint8_t sf = 9;
|
||||
uint8_t cr = 7;
|
||||
uint8_t cr = 5;
|
||||
/** Slottime is the minimum time to wait, consisting of:
|
||||
- CAD duration (maximum of SX126x and SX127x);
|
||||
- roundtrip air propagation time (assuming max. 30km between nodes);
|
||||
@@ -223,4 +223,4 @@ class RadioInterface
|
||||
};
|
||||
|
||||
/// Debug printing for packets
|
||||
void printPacket(const char *prefix, const meshtastic_MeshPacket *p);
|
||||
void printPacket(const char *prefix, const meshtastic_MeshPacket *p);
|
||||
@@ -1,9 +1,6 @@
|
||||
#include "RadioLibRF95.h"
|
||||
#include "configuration.h"
|
||||
|
||||
#define RF95_CHIP_VERSION 0x12
|
||||
#define RF95_ALT_VERSION 0x11 // Supposedly some versions of the chip have id 0x11
|
||||
|
||||
// From datasheet but radiolib doesn't know anything about this
|
||||
#define SX127X_REG_TCXO 0x4B
|
||||
|
||||
@@ -13,9 +10,8 @@ int16_t RadioLibRF95::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_
|
||||
uint8_t gain)
|
||||
{
|
||||
// execute common part
|
||||
int16_t state = SX127x::begin(RF95_CHIP_VERSION, syncWord, preambleLength);
|
||||
if (state != RADIOLIB_ERR_NONE)
|
||||
state = SX127x::begin(RF95_ALT_VERSION, syncWord, preambleLength);
|
||||
uint8_t rf95versions[2] = {0x12, 0x11};
|
||||
int16_t state = SX127x::begin(rf95versions, sizeof(rf95versions), syncWord, preambleLength);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// current limit was removed from module' ctor
|
||||
@@ -80,4 +76,4 @@ bool RadioLibRF95::isReceiving()
|
||||
uint8_t RadioLibRF95::readReg(uint8_t addr)
|
||||
{
|
||||
return mod->SPIreadRegister(addr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,29 +249,12 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
|
||||
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
|
||||
ChannelIndex chIndex = p->channel; // keep as a local because we are about to change it
|
||||
|
||||
bool shouldActuallyEncrypt = true;
|
||||
|
||||
if (moduleConfig.mqtt.enabled) {
|
||||
// check if we should send decrypted packets to mqtt
|
||||
|
||||
// truth table:
|
||||
/* mqtt_server mqtt_encryption_enabled should_encrypt
|
||||
* not set 0 1
|
||||
* not set 1 1
|
||||
* set 0 0
|
||||
* set 1 1
|
||||
*
|
||||
* => so we only decrypt mqtt if they have a custom mqtt server AND mqtt_encryption_enabled is FALSE
|
||||
*/
|
||||
|
||||
if (*moduleConfig.mqtt.address && !moduleConfig.mqtt.encryption_enabled) {
|
||||
shouldActuallyEncrypt = false;
|
||||
}
|
||||
|
||||
LOG_INFO("Should encrypt MQTT?: %d\n", shouldActuallyEncrypt);
|
||||
LOG_INFO("Should encrypt MQTT?: %d\n", moduleConfig.mqtt.encryption_enabled);
|
||||
|
||||
// the packet is currently in a decrypted state. send it now if they want decrypted packets
|
||||
if (mqtt && !shouldActuallyEncrypt)
|
||||
if (mqtt && !moduleConfig.mqtt.encryption_enabled)
|
||||
mqtt->onSend(*p, chIndex);
|
||||
}
|
||||
|
||||
@@ -284,7 +267,7 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
|
||||
if (moduleConfig.mqtt.enabled) {
|
||||
// the packet is now encrypted.
|
||||
// check if we should send encrypted packets to mqtt
|
||||
if (mqtt && shouldActuallyEncrypt)
|
||||
if (mqtt && moduleConfig.mqtt.encryption_enabled)
|
||||
mqtt->onSend(*p, chIndex);
|
||||
}
|
||||
}
|
||||
@@ -316,6 +299,12 @@ bool perhapsDecode(meshtastic_MeshPacket *p)
|
||||
config.device.rebroadcast_mode == meshtastic_Config_DeviceConfig_RebroadcastMode_ALL_SKIP_DECODING)
|
||||
return false;
|
||||
|
||||
if (config.device.rebroadcast_mode == meshtastic_Config_DeviceConfig_RebroadcastMode_KNOWN_ONLY &&
|
||||
!nodeDB.getMeshNode(p->from)->has_user) {
|
||||
LOG_DEBUG("Node 0x%x not in NodeDB. Rebroadcast mode KNOWN_ONLY will ignore packet\n", p->from);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag)
|
||||
return true; // If packet was already decoded just return
|
||||
|
||||
|
||||
@@ -20,9 +20,6 @@ bool STM32WLE5JCInterface::init()
|
||||
|
||||
lora.setRfSwitchTable(rfswitch_pins, rfswitch_table);
|
||||
|
||||
if (power == 0)
|
||||
power = STM32WLx_MAX_POWER;
|
||||
|
||||
if (power > STM32WLx_MAX_POWER) // This chip has lower power limits than some
|
||||
power = STM32WLx_MAX_POWER;
|
||||
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
#include "configuration.h"
|
||||
#include "error.h"
|
||||
#include "mesh/NodeDB.h"
|
||||
#ifdef ARCH_RASPBERRY_PI
|
||||
#include "PortduinoGlue.h"
|
||||
#endif
|
||||
|
||||
// Particular boards might define a different max power based on what their hardware can do
|
||||
// Particular boards might define a different max power based on what their hardware can do, default to max power output if not
|
||||
// specified (may be dangerous if using external PA and SX126x power config forgotten)
|
||||
#ifndef SX126X_MAX_POWER
|
||||
#define SX126X_MAX_POWER 22
|
||||
#endif
|
||||
@@ -22,24 +26,28 @@ SX126xInterface<T>::SX126xInterface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs
|
||||
template <typename T> bool SX126xInterface<T>::init()
|
||||
{
|
||||
#ifdef SX126X_POWER_EN
|
||||
digitalWrite(SX126X_POWER_EN, HIGH);
|
||||
pinMode(SX126X_POWER_EN, OUTPUT);
|
||||
digitalWrite(SX126X_POWER_EN, HIGH);
|
||||
#endif
|
||||
|
||||
#ifndef SX126X_E22
|
||||
float tcxoVoltage = 0; // None - we use an XTAL
|
||||
// FIXME: correct logic to default to not using TCXO if no voltage is specified for SX126X_DIO3_TCXO_VOLTAGE
|
||||
#if !defined(SX126X_DIO3_TCXO_VOLTAGE)
|
||||
float tcxoVoltage =
|
||||
0; // "TCXO reference voltage to be set on DIO3. Defaults to 1.6 V, set to 0 to skip." per
|
||||
// https://github.com/jgromes/RadioLib/blob/690a050ebb46e6097c5d00c371e961c1caa3b52e/src/modules/SX126x/SX126x.h#L471C26-L471C104
|
||||
// (DIO3 is free to be used as an IRQ)
|
||||
LOG_DEBUG("SX126X_DIO3_TCXO_VOLTAGE not defined, not using DIO3 as TCXO reference voltage\n");
|
||||
#else
|
||||
// Use DIO3 to power tcxo per https://github.com/jgromes/RadioLib/issues/12#issuecomment-520695575
|
||||
float tcxoVoltage = 1.8;
|
||||
float tcxoVoltage = SX126X_DIO3_TCXO_VOLTAGE;
|
||||
LOG_DEBUG("SX126X_DIO3_TCXO_VOLTAGE defined, using DIO3 as TCXO reference voltage at %f V\n", SX126X_DIO3_TCXO_VOLTAGE);
|
||||
// (DIO3 is not free to be used as an IRQ)
|
||||
#endif
|
||||
// FIXME: May want to set depending on a definition, currently all SX126x variant files use the DC-DC regulator option
|
||||
bool useRegulatorLDO = false; // Seems to depend on the connection to pin 9/DCC_SW - if an inductor DCDC?
|
||||
|
||||
RadioLibInterface::init();
|
||||
|
||||
if (power == 0)
|
||||
power = SX126X_MAX_POWER;
|
||||
|
||||
if (power > SX126X_MAX_POWER) // This chip has lower power limits than some
|
||||
if (power > SX126X_MAX_POWER) // Clamp power to maximum defined level
|
||||
power = SX126X_MAX_POWER;
|
||||
|
||||
limitPower();
|
||||
@@ -54,49 +62,56 @@ template <typename T> bool SX126xInterface<T>::init()
|
||||
LOG_INFO("Bandwidth set to %f\n", bw);
|
||||
LOG_INFO("Power output set to %d\n", power);
|
||||
|
||||
// current limit was removed from module' ctor
|
||||
// override default value (60 mA)
|
||||
// Overriding current limit
|
||||
// (https://github.com/jgromes/RadioLib/blob/690a050ebb46e6097c5d00c371e961c1caa3b52e/src/modules/SX126x/SX126x.cpp#L85) using
|
||||
// value in SX126xInterface.h (currently 140 mA) It may or may not be neccessary, depending on how RadioLib functions, from
|
||||
// SX1261/2 datasheet: OCP after setting DeviceSel with SetPaConfig(): SX1261 - 60 mA, SX1262 - 140 mA For the SX1268 the IC
|
||||
// defaults to 140mA no matter the set power level, but RadioLib set it lower, this would need further checking Default values
|
||||
// are: SX1262, SX1268: 0x38 (140 mA), SX1261: 0x18 (60 mA)
|
||||
// FIXME: Not ideal to increase SX1261 current limit above 60mA as it can only transmit max 15dBm, should probably only do it
|
||||
// if using SX1262 or SX1268
|
||||
res = lora.setCurrentLimit(currentLimit);
|
||||
LOG_DEBUG("Current limit set to %f\n", currentLimit);
|
||||
LOG_DEBUG("Current limit set result %d\n", res);
|
||||
|
||||
#if defined(SX126X_E22)
|
||||
// E22 Emulation explicitly requires DIO2 as RF switch, so set it to TRUE again for good measure. In case somebody defines
|
||||
// SX126X_TX for an E22 Module
|
||||
if (res == RADIOLIB_ERR_NONE) {
|
||||
LOG_DEBUG("SX126X_E22 mode enabled. Setting DIO2 as RF Switch\n");
|
||||
res = lora.setDio2AsRfSwitch(true);
|
||||
#ifdef SX126X_DIO2_AS_RF_SWITCH
|
||||
LOG_DEBUG("Setting DIO2 as RF switch\n");
|
||||
bool dio2AsRfSwitch = true;
|
||||
#elif defined(ARCH_RASPBERRY_PI)
|
||||
bool dio2AsRfSwitch = false;
|
||||
if (settingsMap[dio2_as_rf_switch]) {
|
||||
LOG_DEBUG("Setting DIO2 as RF switch\n");
|
||||
dio2AsRfSwitch = true;
|
||||
}
|
||||
#else
|
||||
LOG_DEBUG("Setting DIO2 as not RF switch\n");
|
||||
bool dio2AsRfSwitch = false;
|
||||
#endif
|
||||
if (res == RADIOLIB_ERR_NONE) {
|
||||
res = lora.setDio2AsRfSwitch(dio2AsRfSwitch);
|
||||
}
|
||||
|
||||
#if defined(SX126X_TXEN) && (SX126X_TXEN != RADIOLIB_NC)
|
||||
// If SX126X_TXEN is connected to the MCU, we are manually controlling RX and TX.
|
||||
// But lora.begin (called above) sets Dio2 as RF switch control, which is not true here, so set it back to false.
|
||||
if (res == RADIOLIB_ERR_NONE) {
|
||||
LOG_DEBUG("SX126X_TXEN pin defined. Setting RF Switch: RXEN=%i, TXEN=%i\n", SX126X_RXEN, SX126X_TXEN);
|
||||
res = lora.setDio2AsRfSwitch(false);
|
||||
lora.setRfSwitchPins(SX126X_RXEN, SX126X_TXEN);
|
||||
}
|
||||
#elif defined(SX126X_RXEN) && (SX126X_RXEN != RADIOLIB_NC && defined(E22_TXEN_CONNECTED_TO_DIO2))
|
||||
// Otherwise, if SX126X_RXEN is connected to the MCU, and E22_TXEN_CONNECTED_TO_DIO2 is defined, we are letting the
|
||||
// E22 control RX and TX via DIO2. In this configuration, the E22's TXEN and DIO2 pins are connected to each other,
|
||||
// but not to the MCU.
|
||||
// However, we must still connect the E22's RXEN pin to the MCU, define SX126X_RXEN accordingly, and then call
|
||||
// setRfSwitchPins, otherwise RX sensitivity (observed via RSSI) is greatly diminished.
|
||||
LOG_DEBUG("SX126X_RXEN and E22_TXEN_CONNECTED_TO_DIO2 are defined; value of res: %d", res);
|
||||
if (res == RADIOLIB_ERR_NONE) {
|
||||
LOG_DEBUG("SX126X_TXEN is RADIOLIB_NC, but SX126X_RXEN and E22_TXEN_CONNECTED_TO_DIO2 are both defined; calling "
|
||||
"lora.setRfSwitchPins.");
|
||||
lora.setRfSwitchPins(SX126X_RXEN, SX126X_TXEN);
|
||||
}
|
||||
// If a pin isn't defined, we set it to RADIOLIB_NC, it is safe to always do external RF switching with RADIOLIB_NC as it has
|
||||
// no effect
|
||||
#ifndef SX126X_RXEN
|
||||
#define SX126X_RXEN RADIOLIB_NC
|
||||
LOG_DEBUG("SX126X_RXEN not defined, defaulting to RADIOLIB_NC\n");
|
||||
#endif
|
||||
#ifndef SX126X_TXEN
|
||||
#define SX126X_TXEN RADIOLIB_NC
|
||||
LOG_DEBUG("SX126X_TXEN not defined, defaulting to RADIOLIB_NC\n");
|
||||
#endif
|
||||
if (res == RADIOLIB_ERR_NONE) {
|
||||
LOG_DEBUG("Using MCU pin %i as RXEN and pin %i as TXEN to control RF switching\n", SX126X_RXEN, SX126X_TXEN);
|
||||
lora.setRfSwitchPins(SX126X_RXEN, SX126X_TXEN);
|
||||
}
|
||||
|
||||
if (config.lora.sx126x_rx_boosted_gain) {
|
||||
uint16_t result = lora.setRxBoostedGainMode(true);
|
||||
LOG_INFO("Set Rx Boosted Gain mode; result: %d\n", result);
|
||||
LOG_INFO("Set RX gain to boosted mode; result: %d\n", result);
|
||||
} else {
|
||||
uint16_t result = lora.setRxBoostedGainMode(false);
|
||||
LOG_INFO("Set Rx Power Saving Gain mode; result: %d\n", result);
|
||||
LOG_INFO("Set RX gain to power saving mode (boosted mode off); result: %d\n", result);
|
||||
}
|
||||
|
||||
#if 0
|
||||
@@ -235,7 +250,7 @@ template <typename T> void SX126xInterface<T>::startReceive()
|
||||
// We use a 16 bit preamble so this should save some power by letting radio sit in standby mostly.
|
||||
// Furthermore, we need the PREAMBLE_DETECTED and HEADER_VALID IRQ flag to detect whether we are actively receiving
|
||||
int err = lora.startReceiveDutyCycleAuto(preambleLength, 8,
|
||||
RADIOLIB_SX126X_IRQ_RX_DEFAULT | RADIOLIB_SX126X_IRQ_RADIOLIB_PREAMBLE_DETECTED |
|
||||
RADIOLIB_SX126X_IRQ_RX_DEFAULT | RADIOLIB_SX126X_IRQ_PREAMBLE_DETECTED |
|
||||
RADIOLIB_SX126X_IRQ_HEADER_VALID);
|
||||
assert(err == RADIOLIB_ERR_NONE);
|
||||
|
||||
@@ -265,11 +280,11 @@ template <typename T> bool SX126xInterface<T>::isChannelActive()
|
||||
/** Could we send right now (i.e. either not actively receiving or transmitting)? */
|
||||
template <typename T> bool SX126xInterface<T>::isActivelyReceiving()
|
||||
{
|
||||
// The IRQ status will be cleared when we start our read operation. Check if we've started a header, but haven't yet
|
||||
// The IRQ status will be cleared when we start our read operation. Check if we've started a header, but haven't yet
|
||||
// received and handled the interrupt for reading the packet/handling errors.
|
||||
|
||||
uint16_t irq = lora.getIrqStatus();
|
||||
bool detected = (irq & (RADIOLIB_SX126X_IRQ_HEADER_VALID | RADIOLIB_SX126X_IRQ_RADIOLIB_PREAMBLE_DETECTED));
|
||||
bool detected = (irq & (RADIOLIB_SX126X_IRQ_HEADER_VALID | RADIOLIB_SX126X_IRQ_PREAMBLE_DETECTED));
|
||||
// Handle false detections
|
||||
if (detected) {
|
||||
uint32_t now = millis();
|
||||
@@ -296,7 +311,7 @@ template <typename T> bool SX126xInterface<T>::sleep()
|
||||
{
|
||||
// Not keeping config is busted - next time nrf52 board boots lora sending fails tcxo related? - see datasheet
|
||||
// \todo Display actual typename of the adapter, not just `SX126x`
|
||||
LOG_DEBUG("sx126x entering sleep mode (FIXME, don't keep config)\n");
|
||||
LOG_DEBUG("SX126x entering sleep mode (FIXME, don't keep config)\n");
|
||||
setStandby(); // Stop any pending operations
|
||||
|
||||
// turn off TCXO if it was powered
|
||||
|
||||
@@ -22,8 +22,8 @@ SX128xInterface<T>::SX128xInterface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs
|
||||
template <typename T> bool SX128xInterface<T>::init()
|
||||
{
|
||||
#ifdef SX128X_POWER_EN
|
||||
digitalWrite(SX128X_POWER_EN, HIGH);
|
||||
pinMode(SX128X_POWER_EN, OUTPUT);
|
||||
digitalWrite(SX128X_POWER_EN, HIGH);
|
||||
#endif
|
||||
|
||||
#ifdef RF95_FAN_EN
|
||||
@@ -32,19 +32,16 @@ template <typename T> bool SX128xInterface<T>::init()
|
||||
#endif
|
||||
|
||||
#if defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC) // set not rx or tx mode
|
||||
digitalWrite(SX128X_RXEN, LOW); // Set low before becoming an output
|
||||
pinMode(SX128X_RXEN, OUTPUT);
|
||||
digitalWrite(SX128X_RXEN, LOW); // Set low before becoming an output
|
||||
#endif
|
||||
#if defined(SX128X_TXEN) && (SX128X_TXEN != RADIOLIB_NC)
|
||||
digitalWrite(SX128X_TXEN, LOW);
|
||||
pinMode(SX128X_TXEN, OUTPUT);
|
||||
digitalWrite(SX128X_TXEN, LOW);
|
||||
#endif
|
||||
|
||||
RadioLibInterface::init();
|
||||
|
||||
if (power == 0)
|
||||
power = SX128X_MAX_POWER;
|
||||
|
||||
if (power > SX128X_MAX_POWER) // This chip has lower power limits than some
|
||||
power = SX128X_MAX_POWER;
|
||||
|
||||
|
||||
55
src/mesh/TypeConversions.cpp
Normal file
55
src/mesh/TypeConversions.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#include "TypeConversions.h"
|
||||
#include "mesh/generated/meshtastic/deviceonly.pb.h"
|
||||
#include "mesh/generated/meshtastic/mesh.pb.h"
|
||||
|
||||
meshtastic_NodeInfo TypeConversions::ConvertToNodeInfo(const meshtastic_NodeInfoLite *lite)
|
||||
{
|
||||
meshtastic_NodeInfo info = meshtastic_NodeInfo_init_default;
|
||||
|
||||
info.num = lite->num;
|
||||
info.snr = lite->snr;
|
||||
info.last_heard = lite->last_heard;
|
||||
info.channel = lite->channel;
|
||||
|
||||
if (lite->has_position) {
|
||||
info.has_position = true;
|
||||
info.position.latitude_i = lite->position.latitude_i;
|
||||
info.position.longitude_i = lite->position.longitude_i;
|
||||
info.position.altitude = lite->position.altitude;
|
||||
info.position.location_source = lite->position.location_source;
|
||||
info.position.time = lite->position.time;
|
||||
}
|
||||
if (lite->has_user) {
|
||||
info.has_user = true;
|
||||
info.user = lite->user;
|
||||
}
|
||||
if (lite->has_device_metrics) {
|
||||
info.has_device_metrics = true;
|
||||
info.device_metrics = lite->device_metrics;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
meshtastic_PositionLite TypeConversions::ConvertToPositionLite(meshtastic_Position position)
|
||||
{
|
||||
meshtastic_PositionLite lite = meshtastic_PositionLite_init_default;
|
||||
lite.latitude_i = position.latitude_i;
|
||||
lite.longitude_i = position.longitude_i;
|
||||
lite.altitude = position.altitude;
|
||||
lite.location_source = position.location_source;
|
||||
lite.time = position.time;
|
||||
|
||||
return lite;
|
||||
}
|
||||
|
||||
meshtastic_Position TypeConversions::ConvertToPosition(meshtastic_PositionLite lite)
|
||||
{
|
||||
meshtastic_Position position = meshtastic_Position_init_default;
|
||||
position.latitude_i = lite.latitude_i;
|
||||
position.longitude_i = lite.longitude_i;
|
||||
position.altitude = lite.altitude;
|
||||
position.location_source = lite.location_source;
|
||||
position.time = lite.time;
|
||||
|
||||
return position;
|
||||
}
|
||||
@@ -1,54 +1,13 @@
|
||||
#include "mesh/generated/meshtastic/deviceonly.pb.h"
|
||||
#include "mesh/generated/meshtastic/mesh.pb.h"
|
||||
|
||||
inline static meshtastic_NodeInfo ConvertToNodeInfo(const meshtastic_NodeInfoLite *lite)
|
||||
#pragma once
|
||||
#include "NodeDB.h"
|
||||
|
||||
class TypeConversions
|
||||
{
|
||||
meshtastic_NodeInfo info = meshtastic_NodeInfo_init_default;
|
||||
|
||||
info.num = lite->num;
|
||||
info.snr = lite->snr;
|
||||
info.last_heard = lite->last_heard;
|
||||
info.channel = lite->channel;
|
||||
|
||||
if (lite->has_position) {
|
||||
info.has_position = true;
|
||||
info.position.latitude_i = lite->position.latitude_i;
|
||||
info.position.longitude_i = lite->position.longitude_i;
|
||||
info.position.altitude = lite->position.altitude;
|
||||
info.position.location_source = lite->position.location_source;
|
||||
info.position.time = lite->position.time;
|
||||
}
|
||||
if (lite->has_user) {
|
||||
info.has_user = true;
|
||||
info.user = lite->user;
|
||||
}
|
||||
if (lite->has_device_metrics) {
|
||||
info.has_device_metrics = true;
|
||||
info.device_metrics = lite->device_metrics;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
inline static meshtastic_PositionLite ConvertToPositionLite(meshtastic_Position position)
|
||||
{
|
||||
meshtastic_PositionLite lite = meshtastic_PositionLite_init_default;
|
||||
lite.latitude_i = position.latitude_i;
|
||||
lite.longitude_i = position.longitude_i;
|
||||
lite.altitude = position.altitude;
|
||||
lite.location_source = position.location_source;
|
||||
lite.time = position.time;
|
||||
|
||||
return lite;
|
||||
}
|
||||
|
||||
inline static meshtastic_Position ConvertToPosition(meshtastic_PositionLite lite)
|
||||
{
|
||||
meshtastic_Position position = meshtastic_Position_init_default;
|
||||
position.latitude_i = lite.latitude_i;
|
||||
position.longitude_i = lite.longitude_i;
|
||||
position.altitude = lite.altitude;
|
||||
position.location_source = lite.location_source;
|
||||
position.time = lite.time;
|
||||
|
||||
return position;
|
||||
}
|
||||
public:
|
||||
static meshtastic_NodeInfo ConvertToNodeInfo(const meshtastic_NodeInfoLite *lite);
|
||||
static meshtastic_PositionLite ConvertToPositionLite(meshtastic_Position position);
|
||||
static meshtastic_Position ConvertToPosition(meshtastic_PositionLite lite);
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user