mirror of
https://github.com/meshtastic/firmware.git
synced 2025-12-17 16:22:48 +00:00
Compare commits
562 Commits
fix-msg-al
...
gps-reset-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
06a9a52bb9 | ||
|
|
7b8c1b9f66 | ||
|
|
b29099e3b3 | ||
|
|
407c820eda | ||
|
|
94d86b3c9b | ||
|
|
b849c4c5fe | ||
|
|
766cbd3258 | ||
|
|
9453fad7a6 | ||
|
|
4f817d69eb | ||
|
|
de83b448f9 | ||
|
|
c145be8e05 | ||
|
|
28f53d132a | ||
|
|
f045ca2303 | ||
|
|
b6830a68a0 | ||
|
|
13c4c2037d | ||
|
|
95d3ecb239 | ||
|
|
799cf0e8b3 | ||
|
|
35fa418739 | ||
|
|
585d9d36a8 | ||
|
|
b682ab3967 | ||
|
|
49b9d5151d | ||
|
|
3f8707cafe | ||
|
|
39780656ef | ||
|
|
153cf65214 | ||
|
|
07d354fa02 | ||
|
|
f4e93b4a2d | ||
|
|
18c4956aba | ||
|
|
15ee1c2819 | ||
|
|
f4ff210311 | ||
|
|
26747038bb | ||
|
|
8e082686a3 | ||
|
|
871986d183 | ||
|
|
821d8aa15d | ||
|
|
c4656dacf7 | ||
|
|
64d92679d0 | ||
|
|
b5aa16bade | ||
|
|
c740550d16 | ||
|
|
cb3ce1b1a8 | ||
|
|
2ad52812c0 | ||
|
|
5b9563a357 | ||
|
|
126954c2ed | ||
|
|
f6eede8597 | ||
|
|
6f2241751e | ||
|
|
1d4134c08c | ||
|
|
ffb168be00 | ||
|
|
27dc3be14a | ||
|
|
f2a63faddd | ||
|
|
d2403437ff | ||
|
|
05c176c16a | ||
|
|
7afc6ef833 | ||
|
|
68e739359f | ||
|
|
2357ea0042 | ||
|
|
cbdbaf62f1 | ||
|
|
e1c259ae32 | ||
|
|
b4dea63f44 | ||
|
|
30022c9377 | ||
|
|
0283e0658b | ||
|
|
4f142e6766 | ||
|
|
af8407aca9 | ||
|
|
e5d67310d6 | ||
|
|
47df9d4a79 | ||
|
|
4df79374b0 | ||
|
|
0bfc342b48 | ||
|
|
d9905f3c31 | ||
|
|
ee2ed0a8fb | ||
|
|
acab814b6f | ||
|
|
073c35c782 | ||
|
|
32ebc70bca | ||
|
|
5953b4704e | ||
|
|
a34c584028 | ||
|
|
865b46ceef | ||
|
|
cd3b31c5da | ||
|
|
51b3b937dc | ||
|
|
ec5a54c523 | ||
|
|
4e0a4cc45f | ||
|
|
858e8c6fef | ||
|
|
a6df18e60a | ||
|
|
9a8aeb25ab | ||
|
|
e8f4d07e9f | ||
|
|
034d2dd025 | ||
|
|
b8bfed2810 | ||
|
|
dbb439f121 | ||
|
|
c4d7ad2190 | ||
|
|
5814f3e7d2 | ||
|
|
910fe911f8 | ||
|
|
9ab9650248 | ||
|
|
37a0f774a2 | ||
|
|
a71b47b5bb | ||
|
|
9df5aa8c70 | ||
|
|
130833b5be | ||
|
|
fe6509a0f2 | ||
|
|
1212c2c11b | ||
|
|
fcaa168d2d | ||
|
|
e24e2ccf62 | ||
|
|
7537d28419 | ||
|
|
26f25069dd | ||
|
|
5eeffdb290 | ||
|
|
5d71776527 | ||
|
|
661e596dbb | ||
|
|
a6732682de | ||
|
|
f0126d44e2 | ||
|
|
fb08e17c39 | ||
|
|
11aff46af1 | ||
|
|
cb11e6b720 | ||
|
|
464663b496 | ||
|
|
981d058e9f | ||
|
|
9056915e7b | ||
|
|
554112ceb5 | ||
|
|
29458cd8c4 | ||
|
|
8bf32dc042 | ||
|
|
73cadce581 | ||
|
|
eee80ce636 | ||
|
|
30d6962e79 | ||
|
|
f99747180e | ||
|
|
91d928d4c5 | ||
|
|
64bfe73c06 | ||
|
|
fca5343460 | ||
|
|
cafb007ec4 | ||
|
|
694b669eb7 | ||
|
|
7899340131 | ||
|
|
0bb1c1fe6f | ||
|
|
05febc25e1 | ||
|
|
e5a2ce54e7 | ||
|
|
45f15b8fe6 | ||
|
|
2a14696525 | ||
|
|
05edcc5d6c | ||
|
|
828e11cc48 | ||
|
|
adae68fbfe | ||
|
|
7822f28152 | ||
|
|
d332dfa19b | ||
|
|
fe2e2753aa | ||
|
|
fcb1d64eb9 | ||
|
|
0c2673ee2f | ||
|
|
9c5513dcfe | ||
|
|
74e6723ad9 | ||
|
|
e8e8ee0993 | ||
|
|
a7f15097da | ||
|
|
e4ec719e6f | ||
|
|
9b7b8ffb21 | ||
|
|
f0e4ea7664 | ||
|
|
468b40e8db | ||
|
|
bd9076b740 | ||
|
|
81a5aeff74 | ||
|
|
f13e7c20ba | ||
|
|
5bcc47dddb | ||
|
|
668cc9fd64 | ||
|
|
1d5b343836 | ||
|
|
8023f475ee | ||
|
|
b696e083f3 | ||
|
|
b214f09ca1 | ||
|
|
68a2c4adda | ||
|
|
518680514f | ||
|
|
fc1737c949 | ||
|
|
735784e6e4 | ||
|
|
87e3540f48 | ||
|
|
329a494ce2 | ||
|
|
627c0145e7 | ||
|
|
29f4d99bf6 | ||
|
|
036a58735e | ||
|
|
18ca9e80d5 | ||
|
|
5c2997ef53 | ||
|
|
c147ce9a85 | ||
|
|
27f316b931 | ||
|
|
f7cf5e6b0a | ||
|
|
7c373b76c4 | ||
|
|
de6a02756d | ||
|
|
d708ed5908 | ||
|
|
7c4367cddc | ||
|
|
6022b749ba | ||
|
|
cbd30f95f3 | ||
|
|
9ded6a5215 | ||
|
|
1e4bcb04d5 | ||
|
|
c4dff21e5b | ||
|
|
888692a373 | ||
|
|
fe4fb085e6 | ||
|
|
7c5e2bc95a | ||
|
|
ed32650b9b | ||
|
|
1b97cf57ad | ||
|
|
e8296914a5 | ||
|
|
0e38fef5bf | ||
|
|
b7f6a2acb6 | ||
|
|
0c2283e19e | ||
|
|
78d010fd29 | ||
|
|
037e56b1fd | ||
|
|
c7208ca05b | ||
|
|
f72a4c50bd | ||
|
|
775595cb37 | ||
|
|
047600d088 | ||
|
|
560eb2c455 | ||
|
|
1be3820152 | ||
|
|
da98622f59 | ||
|
|
03baad2c11 | ||
|
|
0ddaf710e4 | ||
|
|
50cfe7c705 | ||
|
|
76c1d69560 | ||
|
|
17863e96e2 | ||
|
|
c48a64e183 | ||
|
|
e954591ca5 | ||
|
|
305f513834 | ||
|
|
0860fee209 | ||
|
|
878ac3ec84 | ||
|
|
a62e1cfa3c | ||
|
|
ca02808c5d | ||
|
|
b978c6c86c | ||
|
|
51ad9d0244 | ||
|
|
76d4807130 | ||
|
|
5ec09783c5 | ||
|
|
2eb0fcbcaf | ||
|
|
9bb7bb467b | ||
|
|
de67714248 | ||
|
|
e0cf9130be | ||
|
|
f82667d71e | ||
|
|
1d283523f2 | ||
|
|
ec28c383af | ||
|
|
641a2fc63d | ||
|
|
f7469159cf | ||
|
|
af83670376 | ||
|
|
849bbad279 | ||
|
|
b28d095096 | ||
|
|
17afdb9ccf | ||
|
|
d5164b4fbf | ||
|
|
ad44940732 | ||
|
|
8b466b1db3 | ||
|
|
f9937967fa | ||
|
|
4fd568f384 | ||
|
|
dae9b1c024 | ||
|
|
a8a6644192 | ||
|
|
34a595b88e | ||
|
|
e32ce3fafe | ||
|
|
69c61f8247 | ||
|
|
b08e4efb78 | ||
|
|
ee6857511a | ||
|
|
9df4d57168 | ||
|
|
500e7920ae | ||
|
|
ee8fa9f328 | ||
|
|
02efef3aaf | ||
|
|
0f6131d2c8 | ||
|
|
8d323a1cf1 | ||
|
|
a3e6f16378 | ||
|
|
85fe7d26ed | ||
|
|
a1c658a467 | ||
|
|
777e11bad9 | ||
|
|
a15d654767 | ||
|
|
033fc0c8f3 | ||
|
|
7633ddcfd1 | ||
|
|
8717c60f13 | ||
|
|
067939ca24 | ||
|
|
abc011aeb9 | ||
|
|
a4a6ee1df4 | ||
|
|
6448f069f8 | ||
|
|
f6a28e15d2 | ||
|
|
2b60bae61c | ||
|
|
bc516ebbac | ||
|
|
045176789e | ||
|
|
667b7c50e2 | ||
|
|
9b1a118103 | ||
|
|
64c268f055 | ||
|
|
806bf6ce2c | ||
|
|
7eb0109e33 | ||
|
|
4dec912a39 | ||
|
|
73147c4028 | ||
|
|
e8627b2d01 | ||
|
|
ab00e991f6 | ||
|
|
a2d86454d3 | ||
|
|
bc3db1b5c1 | ||
|
|
2f1198ddf3 | ||
|
|
0624059683 | ||
|
|
52ee655fd2 | ||
|
|
9980c56d81 | ||
|
|
fc9f1ac056 | ||
|
|
c65dbe490e | ||
|
|
44636cc9f5 | ||
|
|
aa876ba42f | ||
|
|
12c3ddf457 | ||
|
|
d9f0590f8e | ||
|
|
191d20ed04 | ||
|
|
79bc286b35 | ||
|
|
8d9fda38d6 | ||
|
|
3c25652cdf | ||
|
|
9b3d76967b | ||
|
|
fd5ca8b73c | ||
|
|
18058ef507 | ||
|
|
d9ba0633f6 | ||
|
|
68fc931518 | ||
|
|
0ad6b813fc | ||
|
|
d41fb7bcb5 | ||
|
|
fef4a2987d | ||
|
|
1fc8d54d4c | ||
|
|
47a82bdb98 | ||
|
|
8ed6514771 | ||
|
|
17ecd69416 | ||
|
|
bb6f19dddf | ||
|
|
2fdc0d0928 | ||
|
|
85cdcad194 | ||
|
|
0b4a28866b | ||
|
|
91e2e3f0e8 | ||
|
|
14e64d6b9e | ||
|
|
58602d59bd | ||
|
|
d461eb35fc | ||
|
|
371313080b | ||
|
|
db55d8a59d | ||
|
|
949f881ae8 | ||
|
|
ca3c45a2f3 | ||
|
|
c33c368315 | ||
|
|
1835ff2d78 | ||
|
|
8e04f9f631 | ||
|
|
83be632a1a | ||
|
|
1ed7aad976 | ||
|
|
edb5c0f88e | ||
|
|
060a129995 | ||
|
|
a1ca553bc0 | ||
|
|
189aec9fe3 | ||
|
|
94d4bdf05c | ||
|
|
1968a009dd | ||
|
|
8e608e8186 | ||
|
|
d998f70b56 | ||
|
|
f55db903b2 | ||
|
|
91efaba389 | ||
|
|
a8c66547cc | ||
|
|
f77ca2533b | ||
|
|
07b58a82d5 | ||
|
|
e1485b530f | ||
|
|
2fbfb19304 | ||
|
|
e7840122e8 | ||
|
|
a4e09aa9da | ||
|
|
db941bff3b | ||
|
|
13e1f99c7e | ||
|
|
319cd6fa7b | ||
|
|
0db2e40ee3 | ||
|
|
59f9e2a00b | ||
|
|
97d0f3286e | ||
|
|
388c821028 | ||
|
|
3d51287ba7 | ||
|
|
1e1f2a69b7 | ||
|
|
da4bc0f97c | ||
|
|
b3df32c6c5 | ||
|
|
cea9e1238b | ||
|
|
11eb4a5b90 | ||
|
|
d1fd102952 | ||
|
|
27b07cd1c5 | ||
|
|
5701755608 | ||
|
|
d09baddce5 | ||
|
|
c42513d7c8 | ||
|
|
2010871e4b | ||
|
|
c811e4c573 | ||
|
|
040b3b8c7f | ||
|
|
d558df8a3a | ||
|
|
4100ba83a3 | ||
|
|
b49496d99d | ||
|
|
34c2191f63 | ||
|
|
52527e281d | ||
|
|
9b6cf53730 | ||
|
|
6a3b2ceafe | ||
|
|
a76cc88dc2 | ||
|
|
3463006f73 | ||
|
|
db2f79b6c4 | ||
|
|
1d3c47c5fa | ||
|
|
44968415a5 | ||
|
|
8db9b24934 | ||
|
|
1fc07607cb | ||
|
|
22b71a1e95 | ||
|
|
2ccf91f443 | ||
|
|
58e4dcea61 | ||
|
|
6f56ccd283 | ||
|
|
bfb03b422a | ||
|
|
cc3ff1504a | ||
|
|
9b6a7ed3bb | ||
|
|
fdc8796052 | ||
|
|
787642ad4c | ||
|
|
54f9f7a591 | ||
|
|
0e26702c46 | ||
|
|
6677255f6c | ||
|
|
3a63a56cff | ||
|
|
edb250e782 | ||
|
|
f32e06a321 | ||
|
|
8095261dfd | ||
|
|
72b9a02f3e | ||
|
|
af26408d73 | ||
|
|
c8f69913d6 | ||
|
|
42fbb62f18 | ||
|
|
e6adb197e4 | ||
|
|
cfb34a8816 | ||
|
|
8264d4d65e | ||
|
|
c11680fcc0 | ||
|
|
479c1f5346 | ||
|
|
a1cf305336 | ||
|
|
7b2ff7e196 | ||
|
|
b1d314db1e | ||
|
|
cc579dd0bd | ||
|
|
1ac2382d7c | ||
|
|
2ef5b968f9 | ||
|
|
6a92358b68 | ||
|
|
e20a91b945 | ||
|
|
3fbe7fd8b2 | ||
|
|
8841c1540d | ||
|
|
e2ce369782 | ||
|
|
901bcc24ee | ||
|
|
b14e5770d5 | ||
|
|
68ba3b315c | ||
|
|
2bafac242e | ||
|
|
39648e609a | ||
|
|
dcd53eb7cb | ||
|
|
7821919fae | ||
|
|
f083864f1f | ||
|
|
8e1da8561e | ||
|
|
953fcca304 | ||
|
|
20bd237ff6 | ||
|
|
c73fe85ec8 | ||
|
|
b13d023d58 | ||
|
|
9345bdcb22 | ||
|
|
902405a985 | ||
|
|
ec29100a88 | ||
|
|
017d07e108 | ||
|
|
89cccdbbe2 | ||
|
|
8f0e17a653 | ||
|
|
e3772858b3 | ||
|
|
c71c1f2d15 | ||
|
|
2567c03a3f | ||
|
|
d8381aa905 | ||
|
|
188283b382 | ||
|
|
953fdc9eed | ||
|
|
ec7415b3fd | ||
|
|
a70ffae82c | ||
|
|
6a8732bbaa | ||
|
|
173b75a1c0 | ||
|
|
b0dae54c97 | ||
|
|
71d84404c6 | ||
|
|
ba18467bd1 | ||
|
|
6f5bdd73cb | ||
|
|
d427b477e3 | ||
|
|
46317f413a | ||
|
|
f16aa730d3 | ||
|
|
cc3c568501 | ||
|
|
13ebceb3bc | ||
|
|
22fcd102a0 | ||
|
|
c9cb2cfd94 | ||
|
|
d31e3839fb | ||
|
|
43078a40eb | ||
|
|
4ac99c5df1 | ||
|
|
c9702fe4d0 | ||
|
|
e0f88be2d7 | ||
|
|
1c256ccfd7 | ||
|
|
b9d53d667e | ||
|
|
5d3c92f1a2 | ||
|
|
6c932d51ec | ||
|
|
f0b7aab030 | ||
|
|
5fca3a30ec | ||
|
|
a76f591231 | ||
|
|
20f68929c8 | ||
|
|
42e4759634 | ||
|
|
3d86c99c25 | ||
|
|
09de0e3edb | ||
|
|
70724bef72 | ||
|
|
bf4e2e8e86 | ||
|
|
2dc7760508 | ||
|
|
00772996b6 | ||
|
|
d201f6a1ed | ||
|
|
9977035499 | ||
|
|
096afa07f8 | ||
|
|
760471d620 | ||
|
|
6165b4f7a9 | ||
|
|
de3a65579d | ||
|
|
ae814b5463 | ||
|
|
4ee07226e4 | ||
|
|
78dfb05eeb | ||
|
|
90ddbf6f2c | ||
|
|
9211b1bb4b | ||
|
|
70ac3601b0 | ||
|
|
51acd92a37 | ||
|
|
6d2093650a | ||
|
|
566c2c3fdf | ||
|
|
b6dd99917d | ||
|
|
bfadd9c866 | ||
|
|
f8d44f8f6c | ||
|
|
ccff2769fe | ||
|
|
d00b2afe1d | ||
|
|
e49b07ac8c | ||
|
|
e6bfc4a97a | ||
|
|
a297d21707 | ||
|
|
a8cf4dfe2d | ||
|
|
8989de118c | ||
|
|
1914fa0321 | ||
|
|
ead67446a3 | ||
|
|
43cf12edfb | ||
|
|
962e5d513c | ||
|
|
106a052950 | ||
|
|
0fc33c352a | ||
|
|
e0890b2a13 | ||
|
|
5579d87845 | ||
|
|
35340fc6e2 | ||
|
|
4ab125bbf7 | ||
|
|
87eff2c4a9 | ||
|
|
527e88ca46 | ||
|
|
4140ecfb49 | ||
|
|
27cdd464d1 | ||
|
|
5a463373f2 | ||
|
|
b768860866 | ||
|
|
c63102a312 | ||
|
|
b1f55ef6e8 | ||
|
|
b305acf7e5 | ||
|
|
ab5332950c | ||
|
|
484b4cd848 | ||
|
|
3cc2b70e4f | ||
|
|
7e00054fd7 | ||
|
|
693181b2be | ||
|
|
39c663f203 | ||
|
|
71f659cba6 | ||
|
|
4e879a7b26 | ||
|
|
8c9c00172c | ||
|
|
d5300a1141 | ||
|
|
ac4bcd2f56 | ||
|
|
83ae72cbb2 | ||
|
|
e17c50bb86 | ||
|
|
a31fdf01ce | ||
|
|
1594421214 | ||
|
|
02cb306bb1 | ||
|
|
6b7ad9c4e1 | ||
|
|
fa1ccf4779 | ||
|
|
67ecb60bcd | ||
|
|
9da92626e5 | ||
|
|
abc0eb196a | ||
|
|
701028b749 | ||
|
|
108bdf7b0d | ||
|
|
6f7149e9a2 | ||
|
|
95dc61f57b | ||
|
|
0aa48c9c22 | ||
|
|
088318512a | ||
|
|
f267b5f5f7 | ||
|
|
0cd860e300 | ||
|
|
31fdb36987 | ||
|
|
e7741c20e4 | ||
|
|
ca4b98f2b1 | ||
|
|
d1d16fc25f | ||
|
|
c8afbe68b5 | ||
|
|
1643249db7 | ||
|
|
b75e8913e0 | ||
|
|
87a1449f76 | ||
|
|
803e96800e | ||
|
|
6c69780615 | ||
|
|
d5bb566276 | ||
|
|
c62f262f63 | ||
|
|
798040b5b8 | ||
|
|
ba582d6ef4 | ||
|
|
bbf6f01d42 | ||
|
|
142abb2a4e | ||
|
|
18d005d7e6 | ||
|
|
8791cd7851 | ||
|
|
590db89643 | ||
|
|
ea1d968777 | ||
|
|
40d728a14b | ||
|
|
e39b56547e | ||
|
|
a7f63d5783 | ||
|
|
4dfcd61d46 | ||
|
|
9d560fe9e1 | ||
|
|
8e32d58077 | ||
|
|
7b24d31636 | ||
|
|
35d9e68053 | ||
|
|
caf2180075 | ||
|
|
236d2b92dc | ||
|
|
e6a2df5b6d | ||
|
|
f6bb1977bc | ||
|
|
9b0fbcf1d9 |
@@ -1,7 +1,7 @@
|
|||||||
# trunk-ignore-all(terrascan/AC_DOCKER_0002): Known terrascan issue
|
# trunk-ignore-all(terrascan/AC_DOCKER_0002): Known terrascan issue
|
||||||
# trunk-ignore-all(hadolint/DL3008): Do not pin apt package versions
|
# trunk-ignore-all(hadolint/DL3008): Do not pin apt package versions
|
||||||
# trunk-ignore-all(hadolint/DL3013): Do not pin pip package versions
|
# trunk-ignore-all(hadolint/DL3013): Do not pin pip package versions
|
||||||
FROM mcr.microsoft.com/devcontainers/cpp:1-debian-12
|
FROM mcr.microsoft.com/devcontainers/cpp:2-debian-12
|
||||||
|
|
||||||
USER root
|
USER root
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
"features": {
|
"features": {
|
||||||
"ghcr.io/devcontainers/features/python:1": {
|
"ghcr.io/devcontainers/features/python:1": {
|
||||||
"installTools": true,
|
"installTools": true,
|
||||||
"version": "latest"
|
"version": "3.13"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"customizations": {
|
"customizations": {
|
||||||
|
|||||||
5
.github/actions/setup-base/action.yml
vendored
5
.github/actions/setup-base/action.yml
vendored
@@ -11,11 +11,6 @@ runs:
|
|||||||
ref: ${{github.event.pull_request.head.ref}}
|
ref: ${{github.event.pull_request.head.ref}}
|
||||||
repository: ${{github.event.pull_request.head.repo.full_name}}
|
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||||
|
|
||||||
- name: Uncomment build epoch
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
sed -i 's/#-DBUILD_EPOCH=$UNIX_TIME/-DBUILD_EPOCH=$UNIX_TIME/' platformio.ini
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
3
.github/workflows/build_firmware.yml
vendored
3
.github/workflows/build_firmware.yml
vendored
@@ -19,6 +19,8 @@ jobs:
|
|||||||
pio-build:
|
pio-build:
|
||||||
name: build-${{ inputs.platform }}
|
name: build-${{ inputs.platform }}
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
|
outputs:
|
||||||
|
artifact-id: ${{ steps.upload.outputs.artifact-id }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
@@ -55,6 +57,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Store binaries as an artifact
|
- name: Store binaries as an artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
id: upload
|
||||||
with:
|
with:
|
||||||
name: firmware-${{ inputs.platform }}-${{ inputs.pio_env }}-${{ inputs.version }}.zip
|
name: firmware-${{ inputs.platform }}-${{ inputs.pio_env }}-${{ inputs.version }}.zip
|
||||||
overwrite: true
|
overwrite: true
|
||||||
|
|||||||
176
.github/workflows/build_one_arch.yml
vendored
Normal file
176
.github/workflows/build_one_arch.yml
vendored
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
name: Build One Arch
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
# trunk-ignore(checkov/CKV_GHA_7)
|
||||||
|
arch:
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- esp32
|
||||||
|
- esp32s3
|
||||||
|
- esp32c3
|
||||||
|
- esp32c6
|
||||||
|
- nrf52840
|
||||||
|
- rp2040
|
||||||
|
- rp2350
|
||||||
|
- stm32
|
||||||
|
- native
|
||||||
|
|
||||||
|
permissions: read-all
|
||||||
|
|
||||||
|
env:
|
||||||
|
INPUT_ARCH: ${{ github.event.inputs.arch }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
setup:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: 3.x
|
||||||
|
cache: pip
|
||||||
|
- run: pip install -U platformio
|
||||||
|
- name: Generate matrix
|
||||||
|
id: jsonStep
|
||||||
|
run: |
|
||||||
|
TARGETS=$(./bin/generate_ci_matrix.py $INPUT_ARCH --level extra)
|
||||||
|
echo "Name: $GITHUB_REF_NAME Base: $GITHUB_BASE_REF Ref: $GITHUB_REF"
|
||||||
|
echo "selected_arch=$TARGETS" >> $GITHUB_OUTPUT
|
||||||
|
outputs:
|
||||||
|
selected_arch: ${{ steps.jsonStep.outputs.selected_arch }}
|
||||||
|
|
||||||
|
version:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- name: Get release version string
|
||||||
|
run: |
|
||||||
|
echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
||||||
|
echo "deb=$(./bin/buildinfo.py deb)" >> $GITHUB_OUTPUT
|
||||||
|
id: version
|
||||||
|
env:
|
||||||
|
BUILD_LOCATION: local
|
||||||
|
outputs:
|
||||||
|
long: ${{ steps.version.outputs.long }}
|
||||||
|
deb: ${{ steps.version.outputs.deb }}
|
||||||
|
|
||||||
|
build:
|
||||||
|
if: ${{ github.event_name != 'workflow_dispatch' }}
|
||||||
|
needs: [setup, version]
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
build: ${{ fromJson(needs.setup.outputs.selected_arch) }}
|
||||||
|
uses: ./.github/workflows/build_firmware.yml
|
||||||
|
with:
|
||||||
|
version: ${{ needs.version.outputs.long }}
|
||||||
|
pio_env: ${{ matrix.build.board }}
|
||||||
|
platform: ${{ matrix.build.arch }}
|
||||||
|
|
||||||
|
build-debian-src:
|
||||||
|
if: ${{ github.repository == 'meshtastic/firmware' && github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
|
||||||
|
uses: ./.github/workflows/build_debian_src.yml
|
||||||
|
with:
|
||||||
|
series: UNRELEASED
|
||||||
|
build_location: local
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
package-pio-deps-native-tft:
|
||||||
|
if: ${{ inputs.arch == 'native' }}
|
||||||
|
uses: ./.github/workflows/package_pio_deps.yml
|
||||||
|
with:
|
||||||
|
pio_env: native-tft
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
test-native:
|
||||||
|
if: ${{ !contains(github.ref_name, 'event/') && github.event_name != 'workflow_dispatch' || !contains(github.ref_name, 'event/') && inputs.arch == 'native' }}
|
||||||
|
uses: ./.github/workflows/test_native.yml
|
||||||
|
|
||||||
|
gather-artifacts:
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
arch:
|
||||||
|
- esp32
|
||||||
|
- esp32s3
|
||||||
|
- esp32c3
|
||||||
|
- esp32c6
|
||||||
|
- nrf52840
|
||||||
|
- rp2040
|
||||||
|
- rp2350
|
||||||
|
- stm32
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [version, build]
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
with:
|
||||||
|
ref: ${{github.event.pull_request.head.ref}}
|
||||||
|
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
path: ./
|
||||||
|
pattern: firmware-${{inputs.arch}}-*
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Display structure of downloaded files
|
||||||
|
run: ls -R
|
||||||
|
|
||||||
|
- name: Move files up
|
||||||
|
run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat
|
||||||
|
|
||||||
|
- name: Repackage in single firmware zip
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}
|
||||||
|
overwrite: true
|
||||||
|
path: |
|
||||||
|
./firmware-*.bin
|
||||||
|
./firmware-*.uf2
|
||||||
|
./firmware-*.hex
|
||||||
|
./firmware-*-ota.zip
|
||||||
|
./device-*.sh
|
||||||
|
./device-*.bat
|
||||||
|
./littlefs-*.bin
|
||||||
|
./bleota*bin
|
||||||
|
./Meshtastic_nRF52_factory_erase*.uf2
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
name: firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}
|
||||||
|
merge-multiple: true
|
||||||
|
path: ./output
|
||||||
|
|
||||||
|
# For diagnostics
|
||||||
|
- name: Show artifacts
|
||||||
|
run: ls -lR
|
||||||
|
|
||||||
|
- name: Device scripts permissions
|
||||||
|
run: |
|
||||||
|
chmod +x ./output/device-install.sh
|
||||||
|
chmod +x ./output/device-update.sh
|
||||||
|
|
||||||
|
- name: Zip firmware
|
||||||
|
run: zip -j -9 -r ./firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
||||||
|
|
||||||
|
- name: Repackage in single elfs zip
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: debug-elfs-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip
|
||||||
|
overwrite: true
|
||||||
|
path: ./*.elf
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
- uses: scruplelesswizard/comment-artifact@main
|
||||||
|
if: ${{ github.event_name == 'pull_request' }}
|
||||||
|
with:
|
||||||
|
name: firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}
|
||||||
|
description: "Download firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip. This artifact will be available for 90 days from creation"
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
182
.github/workflows/build_one_target.yml
vendored
Normal file
182
.github/workflows/build_one_target.yml
vendored
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
name: Build One Target
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
# trunk-ignore(checkov/CKV_GHA_7)
|
||||||
|
arch:
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- esp32
|
||||||
|
- esp32s3
|
||||||
|
- esp32c3
|
||||||
|
- esp32c6
|
||||||
|
- nrf52840
|
||||||
|
- rp2040
|
||||||
|
- rp2350
|
||||||
|
- stm32
|
||||||
|
- native
|
||||||
|
target:
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
description: Choose the target board, e.g. nrf52_promicro_diy_tcxo. If blank, will find available targets.
|
||||||
|
# find-target:
|
||||||
|
# type: boolean
|
||||||
|
# default: true
|
||||||
|
# description: 'Find the available targets'
|
||||||
|
|
||||||
|
permissions: read-all
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
find-targets:
|
||||||
|
if: ${{ inputs.target == '' }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
arch:
|
||||||
|
- esp32
|
||||||
|
- esp32s3
|
||||||
|
- esp32c3
|
||||||
|
- esp32c6
|
||||||
|
- nrf52840
|
||||||
|
- rp2040
|
||||||
|
- rp2350
|
||||||
|
- stm32
|
||||||
|
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: 3.x
|
||||||
|
cache: pip
|
||||||
|
- run: pip install -U platformio
|
||||||
|
- name: Generate matrix
|
||||||
|
id: jsonStep
|
||||||
|
run: |
|
||||||
|
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}} --level extra)
|
||||||
|
echo "Name: $GITHUB_REF_NAME" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "Base: $GITHUB_BASE_REF" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "Arch: ${{matrix.arch}}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "Ref: $GITHUB_REF" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "Targets:" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo $TARGETS >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
version:
|
||||||
|
if: ${{ inputs.target != '' }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- name: Get release version string
|
||||||
|
run: |
|
||||||
|
echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
||||||
|
echo "deb=$(./bin/buildinfo.py deb)" >> $GITHUB_OUTPUT
|
||||||
|
id: version
|
||||||
|
env:
|
||||||
|
BUILD_LOCATION: local
|
||||||
|
outputs:
|
||||||
|
long: ${{ steps.version.outputs.long }}
|
||||||
|
deb: ${{ steps.version.outputs.deb }}
|
||||||
|
|
||||||
|
build:
|
||||||
|
if: ${{ inputs.target != '' && inputs.arch != 'native' }}
|
||||||
|
needs: [version]
|
||||||
|
uses: ./.github/workflows/build_firmware.yml
|
||||||
|
with:
|
||||||
|
version: ${{ needs.version.outputs.long }}
|
||||||
|
pio_env: ${{ inputs.target }}
|
||||||
|
platform: ${{ inputs.arch }}
|
||||||
|
|
||||||
|
build-debian-src:
|
||||||
|
if: ${{ github.repository == 'meshtastic/firmware' && inputs.arch == 'native' }}
|
||||||
|
uses: ./.github/workflows/build_debian_src.yml
|
||||||
|
with:
|
||||||
|
series: UNRELEASED
|
||||||
|
build_location: local
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
package-pio-deps-native-tft:
|
||||||
|
if: ${{ inputs.arch == 'native' }}
|
||||||
|
uses: ./.github/workflows/package_pio_deps.yml
|
||||||
|
with:
|
||||||
|
pio_env: native-tft
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
test-native:
|
||||||
|
if: ${{ !contains(github.ref_name, 'event/') && github.event_name != 'workflow_dispatch' || !contains(github.ref_name, 'event/') && inputs.arch == 'native' && inputs.target != '' }}
|
||||||
|
uses: ./.github/workflows/test_native.yml
|
||||||
|
|
||||||
|
gather-artifacts:
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [version, build]
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
with:
|
||||||
|
ref: ${{github.event.pull_request.head.ref}}
|
||||||
|
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
path: ./
|
||||||
|
pattern: firmware-*-*
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Display structure of downloaded files
|
||||||
|
run: ls -R
|
||||||
|
|
||||||
|
- name: Move files up
|
||||||
|
run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat
|
||||||
|
|
||||||
|
- name: Repackage in single firmware zip
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: firmware-${{inputs.target}}-${{ needs.version.outputs.long }}
|
||||||
|
overwrite: true
|
||||||
|
path: |
|
||||||
|
./firmware-*.bin
|
||||||
|
./firmware-*.uf2
|
||||||
|
./firmware-*.hex
|
||||||
|
./firmware-*-ota.zip
|
||||||
|
./device-*.sh
|
||||||
|
./device-*.bat
|
||||||
|
./littlefs-*.bin
|
||||||
|
./bleota*bin
|
||||||
|
./Meshtastic_nRF52_factory_erase*.uf2
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
pattern: firmware-*-${{ needs.version.outputs.long }}
|
||||||
|
merge-multiple: true
|
||||||
|
path: ./output
|
||||||
|
|
||||||
|
# For diagnostics
|
||||||
|
- name: Show artifacts
|
||||||
|
run: ls -lR
|
||||||
|
|
||||||
|
- name: Device scripts permissions
|
||||||
|
run: |
|
||||||
|
chmod +x ./output/device-install.sh
|
||||||
|
chmod +x ./output/device-update.sh
|
||||||
|
|
||||||
|
- name: Zip firmware
|
||||||
|
run: zip -j -9 -r ./firmware-${{inputs.target}}-${{ needs.version.outputs.long }}.zip ./output
|
||||||
|
|
||||||
|
- name: Repackage in single elfs zip
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: debug-elfs-${{inputs.target}}-${{ needs.version.outputs.long }}.zip
|
||||||
|
overwrite: true
|
||||||
|
path: ./*.elf
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
- uses: scruplelesswizard/comment-artifact@main
|
||||||
|
if: ${{ github.event_name == 'pull_request' }}
|
||||||
|
with:
|
||||||
|
name: firmware-${{inputs.target}}-${{ needs.version.outputs.long }}
|
||||||
|
description: "Download firmware-${{inputs.target}}-${{ needs.version.outputs.long }}.zip. This artifact will be available for 90 days from creation"
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
8
.github/workflows/daily_packaging.yml
vendored
8
.github/workflows/daily_packaging.yml
vendored
@@ -21,18 +21,20 @@ permissions:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
docker-multiarch:
|
docker-multiarch:
|
||||||
|
if: github.repository == 'meshtastic/firmware'
|
||||||
uses: ./.github/workflows/docker_manifest.yml
|
uses: ./.github/workflows/docker_manifest.yml
|
||||||
with:
|
with:
|
||||||
release_channel: daily
|
release_channel: daily
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|
||||||
package-ppa:
|
package-ppa:
|
||||||
|
if: github.repository == 'meshtastic/firmware'
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
series:
|
series:
|
||||||
- jammy # 22.04
|
- jammy # 22.04 LTS
|
||||||
- noble # 24.04
|
- noble # 24.04 LTS
|
||||||
- plucky # 25.04
|
- plucky # 25.04
|
||||||
- questing # 25.10
|
- questing # 25.10
|
||||||
uses: ./.github/workflows/package_ppa.yml
|
uses: ./.github/workflows/package_ppa.yml
|
||||||
@@ -42,6 +44,7 @@ jobs:
|
|||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|
||||||
package-obs:
|
package-obs:
|
||||||
|
if: github.repository == 'meshtastic/firmware'
|
||||||
uses: ./.github/workflows/package_obs.yml
|
uses: ./.github/workflows/package_obs.yml
|
||||||
with:
|
with:
|
||||||
obs_project: network:Meshtastic:daily
|
obs_project: network:Meshtastic:daily
|
||||||
@@ -49,6 +52,7 @@ jobs:
|
|||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|
||||||
hook-copr:
|
hook-copr:
|
||||||
|
if: github.repository == 'meshtastic/firmware'
|
||||||
uses: ./.github/workflows/hook_copr.yml
|
uses: ./.github/workflows/hook_copr.yml
|
||||||
with:
|
with:
|
||||||
copr_project: daily
|
copr_project: daily
|
||||||
|
|||||||
215
.github/workflows/main_matrix.yml
vendored
215
.github/workflows/main_matrix.yml
vendored
@@ -3,7 +3,7 @@ concurrency:
|
|||||||
group: ci-${{ github.head_ref || github.run_id }}
|
group: ci-${{ github.head_ref || github.run_id }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
on:
|
on:
|
||||||
# # Triggers the workflow on push but only for the master branch
|
# # Triggers the workflow on push but only for the main branches
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
@@ -28,17 +28,10 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
setup:
|
setup:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: true
|
||||||
matrix:
|
matrix:
|
||||||
arch:
|
arch:
|
||||||
- esp32
|
- all
|
||||||
- esp32s3
|
|
||||||
- esp32c3
|
|
||||||
- esp32c6
|
|
||||||
- nrf52840
|
|
||||||
- rp2040
|
|
||||||
- rp2350
|
|
||||||
- stm32
|
|
||||||
- check
|
- check
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
@@ -48,29 +41,19 @@ jobs:
|
|||||||
python-version: 3.x
|
python-version: 3.x
|
||||||
cache: pip
|
cache: pip
|
||||||
- run: pip install -U platformio
|
- run: pip install -U platformio
|
||||||
- name: Uncomment build epoch
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
sed -i 's/#-DBUILD_EPOCH=$UNIX_TIME/-DBUILD_EPOCH=$UNIX_TIME/' platformio.ini
|
|
||||||
- name: Generate matrix
|
- name: Generate matrix
|
||||||
id: jsonStep
|
id: jsonStep
|
||||||
run: |
|
run: |
|
||||||
if [[ "$GITHUB_HEAD_REF" == "" ]]; then
|
if [[ "$GITHUB_HEAD_REF" == "" ]]; then
|
||||||
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}})
|
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}})
|
||||||
else
|
else
|
||||||
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}} pr)
|
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}} --level pr)
|
||||||
fi
|
fi
|
||||||
echo "Name: $GITHUB_REF_NAME Base: $GITHUB_BASE_REF Ref: $GITHUB_REF Targets: $TARGETS"
|
echo "Name: $GITHUB_REF_NAME Base: $GITHUB_BASE_REF Ref: $GITHUB_REF"
|
||||||
echo "${{matrix.arch}}=$(jq -cn --argjson environments "$TARGETS" '{board: $environments}')" >> $GITHUB_OUTPUT
|
echo "${{matrix.arch}}=$TARGETS" >> $GITHUB_OUTPUT
|
||||||
|
echo "$TARGETS" >> $GITHUB_STEP_SUMMARY
|
||||||
outputs:
|
outputs:
|
||||||
esp32: ${{ steps.jsonStep.outputs.esp32 }}
|
all: ${{ steps.jsonStep.outputs.all }}
|
||||||
esp32s3: ${{ steps.jsonStep.outputs.esp32s3 }}
|
|
||||||
esp32c3: ${{ steps.jsonStep.outputs.esp32c3 }}
|
|
||||||
esp32c6: ${{ steps.jsonStep.outputs.esp32c6 }}
|
|
||||||
nrf52840: ${{ steps.jsonStep.outputs.nrf52840 }}
|
|
||||||
rp2040: ${{ steps.jsonStep.outputs.rp2040 }}
|
|
||||||
rp2350: ${{ steps.jsonStep.outputs.rp2350 }}
|
|
||||||
stm32: ${{ steps.jsonStep.outputs.stm32 }}
|
|
||||||
check: ${{ steps.jsonStep.outputs.check }}
|
check: ${{ steps.jsonStep.outputs.check }}
|
||||||
|
|
||||||
version:
|
version:
|
||||||
@@ -92,105 +75,30 @@ jobs:
|
|||||||
needs: setup
|
needs: setup
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix: ${{ fromJson(needs.setup.outputs.check) }}
|
matrix:
|
||||||
|
check: ${{ fromJson(needs.setup.outputs.check) }}
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.event_name != 'workflow_dispatch' }}
|
if: ${{ github.event_name != 'workflow_dispatch' && github.repository == 'meshtastic/firmware' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
- name: Build base
|
- name: Build base
|
||||||
id: base
|
id: base
|
||||||
uses: ./.github/actions/setup-base
|
uses: ./.github/actions/setup-base
|
||||||
- name: Check ${{ matrix.board }}
|
- name: Check ${{ matrix.check.board }}
|
||||||
run: bin/check-all.sh ${{ matrix.board }}
|
run: bin/check-all.sh ${{ matrix.check.board }}
|
||||||
|
|
||||||
build-esp32:
|
build:
|
||||||
needs: [setup, version]
|
needs: [setup, version]
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix: ${{ fromJson(needs.setup.outputs.esp32) }}
|
matrix:
|
||||||
|
build: ${{ fromJson(needs.setup.outputs.all) }}
|
||||||
uses: ./.github/workflows/build_firmware.yml
|
uses: ./.github/workflows/build_firmware.yml
|
||||||
with:
|
with:
|
||||||
version: ${{ needs.version.outputs.long }}
|
version: ${{ needs.version.outputs.long }}
|
||||||
pio_env: ${{ matrix.board }}
|
pio_env: ${{ matrix.build.board }}
|
||||||
platform: esp32
|
platform: ${{ matrix.build.platform }}
|
||||||
|
|
||||||
build-esp32s3:
|
|
||||||
needs: [setup, version]
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix: ${{ fromJson(needs.setup.outputs.esp32s3) }}
|
|
||||||
uses: ./.github/workflows/build_firmware.yml
|
|
||||||
with:
|
|
||||||
version: ${{ needs.version.outputs.long }}
|
|
||||||
pio_env: ${{ matrix.board }}
|
|
||||||
platform: esp32s3
|
|
||||||
|
|
||||||
build-esp32c3:
|
|
||||||
needs: [setup, version]
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix: ${{ fromJson(needs.setup.outputs.esp32c3) }}
|
|
||||||
uses: ./.github/workflows/build_firmware.yml
|
|
||||||
with:
|
|
||||||
version: ${{ needs.version.outputs.long }}
|
|
||||||
pio_env: ${{ matrix.board }}
|
|
||||||
platform: esp32c3
|
|
||||||
|
|
||||||
build-esp32c6:
|
|
||||||
needs: [setup, version]
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix: ${{ fromJson(needs.setup.outputs.esp32c6) }}
|
|
||||||
uses: ./.github/workflows/build_firmware.yml
|
|
||||||
with:
|
|
||||||
version: ${{ needs.version.outputs.long }}
|
|
||||||
pio_env: ${{ matrix.board }}
|
|
||||||
platform: esp32c6
|
|
||||||
|
|
||||||
build-nrf52840:
|
|
||||||
needs: [setup, version]
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix: ${{ fromJson(needs.setup.outputs.nrf52840) }}
|
|
||||||
uses: ./.github/workflows/build_firmware.yml
|
|
||||||
with:
|
|
||||||
version: ${{ needs.version.outputs.long }}
|
|
||||||
pio_env: ${{ matrix.board }}
|
|
||||||
platform: nrf52840
|
|
||||||
|
|
||||||
build-rp2040:
|
|
||||||
needs: [setup, version]
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix: ${{ fromJson(needs.setup.outputs.rp2040) }}
|
|
||||||
uses: ./.github/workflows/build_firmware.yml
|
|
||||||
with:
|
|
||||||
version: ${{ needs.version.outputs.long }}
|
|
||||||
pio_env: ${{ matrix.board }}
|
|
||||||
platform: rp2040
|
|
||||||
|
|
||||||
build-rp2350:
|
|
||||||
needs: [setup, version]
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix: ${{ fromJson(needs.setup.outputs.rp2350) }}
|
|
||||||
uses: ./.github/workflows/build_firmware.yml
|
|
||||||
with:
|
|
||||||
version: ${{ needs.version.outputs.long }}
|
|
||||||
pio_env: ${{ matrix.board }}
|
|
||||||
platform: rp2350
|
|
||||||
|
|
||||||
build-stm32:
|
|
||||||
needs: [setup, version]
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix: ${{ fromJson(needs.setup.outputs.stm32) }}
|
|
||||||
uses: ./.github/workflows/build_firmware.yml
|
|
||||||
with:
|
|
||||||
version: ${{ needs.version.outputs.long }}
|
|
||||||
pio_env: ${{ matrix.board }}
|
|
||||||
platform: stm32
|
|
||||||
|
|
||||||
build-debian-src:
|
build-debian-src:
|
||||||
if: github.repository == 'meshtastic/firmware'
|
if: github.repository == 'meshtastic/firmware'
|
||||||
@@ -201,67 +109,41 @@ jobs:
|
|||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|
||||||
package-pio-deps-native-tft:
|
package-pio-deps-native-tft:
|
||||||
if: ${{ github.event_name == 'workflow_dispatch' }}
|
if: ${{ github.repository == 'meshtastic/firmware' && github.event_name == 'workflow_dispatch' }}
|
||||||
uses: ./.github/workflows/package_pio_deps.yml
|
uses: ./.github/workflows/package_pio_deps.yml
|
||||||
with:
|
with:
|
||||||
pio_env: native-tft
|
pio_env: native-tft
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|
||||||
test-native:
|
test-native:
|
||||||
if: ${{ !contains(github.ref_name, 'event/') }}
|
if: ${{ !contains(github.ref_name, 'event/') && github.repository == 'meshtastic/firmware' }}
|
||||||
uses: ./.github/workflows/test_native.yml
|
uses: ./.github/workflows/test_native.yml
|
||||||
|
|
||||||
docker-deb-amd64:
|
docker:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
distro: [debian, alpine]
|
||||||
|
platform: [linux/amd64, linux/arm64, linux/arm/v7]
|
||||||
|
pio_env: [native, native-tft]
|
||||||
|
exclude:
|
||||||
|
- distro: alpine
|
||||||
|
platform: linux/arm/v7
|
||||||
|
- pio_env: native-tft
|
||||||
|
platform: linux/arm64
|
||||||
|
- pio_env: native-tft
|
||||||
|
platform: linux/arm/v7
|
||||||
uses: ./.github/workflows/docker_build.yml
|
uses: ./.github/workflows/docker_build.yml
|
||||||
with:
|
with:
|
||||||
distro: debian
|
distro: ${{ matrix.distro }}
|
||||||
platform: linux/amd64
|
platform: ${{ matrix.platform }}
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ${{ contains(matrix.platform, 'arm') && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }}
|
||||||
push: false
|
pio_env: ${{ matrix.pio_env }}
|
||||||
|
|
||||||
docker-deb-amd64-tft:
|
|
||||||
uses: ./.github/workflows/docker_build.yml
|
|
||||||
with:
|
|
||||||
distro: debian
|
|
||||||
platform: linux/amd64
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
push: false
|
|
||||||
pio_env: native-tft
|
|
||||||
|
|
||||||
docker-alp-amd64:
|
|
||||||
uses: ./.github/workflows/docker_build.yml
|
|
||||||
with:
|
|
||||||
distro: alpine
|
|
||||||
platform: linux/amd64
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
push: false
|
|
||||||
|
|
||||||
docker-alp-amd64-tft:
|
|
||||||
uses: ./.github/workflows/docker_build.yml
|
|
||||||
with:
|
|
||||||
distro: alpine
|
|
||||||
platform: linux/amd64
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
push: false
|
|
||||||
pio_env: native-tft
|
|
||||||
|
|
||||||
docker-deb-arm64:
|
|
||||||
uses: ./.github/workflows/docker_build.yml
|
|
||||||
with:
|
|
||||||
distro: debian
|
|
||||||
platform: linux/arm64
|
|
||||||
runs-on: ubuntu-24.04-arm
|
|
||||||
push: false
|
|
||||||
|
|
||||||
docker-deb-armv7:
|
|
||||||
uses: ./.github/workflows/docker_build.yml
|
|
||||||
with:
|
|
||||||
distro: debian
|
|
||||||
platform: linux/arm/v7
|
|
||||||
runs-on: ubuntu-24.04-arm
|
|
||||||
push: false
|
push: false
|
||||||
|
|
||||||
gather-artifacts:
|
gather-artifacts:
|
||||||
|
# trunk-ignore(checkov/CKV2_GHA_1)
|
||||||
|
if: github.repository == 'meshtastic/firmware'
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
@@ -278,18 +160,7 @@ jobs:
|
|||||||
- rp2350
|
- rp2350
|
||||||
- stm32
|
- stm32
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs:
|
needs: [version, build]
|
||||||
[
|
|
||||||
version,
|
|
||||||
build-esp32,
|
|
||||||
build-esp32s3,
|
|
||||||
build-esp32c3,
|
|
||||||
build-esp32c6,
|
|
||||||
build-nrf52840,
|
|
||||||
build-rp2040,
|
|
||||||
build-rp2350,
|
|
||||||
build-stm32,
|
|
||||||
]
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v5
|
||||||
@@ -361,7 +232,7 @@ jobs:
|
|||||||
|
|
||||||
release-artifacts:
|
release-artifacts:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.event_name == 'workflow_dispatch' }}
|
if: ${{ github.event_name == 'workflow_dispatch' && github.repository == 'meshtastic/firmware' }}
|
||||||
outputs:
|
outputs:
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
needs:
|
needs:
|
||||||
@@ -436,7 +307,7 @@ jobs:
|
|||||||
- rp2350
|
- rp2350
|
||||||
- stm32
|
- stm32
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.event_name == 'workflow_dispatch' }}
|
if: ${{ github.event_name == 'workflow_dispatch' && github.repository == 'meshtastic/firmware'}}
|
||||||
needs: [release-artifacts, version]
|
needs: [release-artifacts, version]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
|
|||||||
376
.github/workflows/merge_queue.yml
vendored
Normal file
376
.github/workflows/merge_queue.yml
vendored
Normal file
@@ -0,0 +1,376 @@
|
|||||||
|
name: Merge Queue
|
||||||
|
# Not sure how concurrency works in merge_queue, removing for now.
|
||||||
|
# concurrency:
|
||||||
|
# group: merge-queue-${{ github.head_ref || github.run_id }}
|
||||||
|
# cancel-in-progress: true
|
||||||
|
on:
|
||||||
|
# Merge group is a special trigger that is used to trigger the workflow when a merge group is created.
|
||||||
|
merge_group:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
setup:
|
||||||
|
strategy:
|
||||||
|
fail-fast: true
|
||||||
|
matrix:
|
||||||
|
arch:
|
||||||
|
- all
|
||||||
|
- check
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: 3.x
|
||||||
|
cache: pip
|
||||||
|
- run: pip install -U platformio
|
||||||
|
- name: Generate matrix
|
||||||
|
id: jsonStep
|
||||||
|
run: |
|
||||||
|
if [[ "$GITHUB_HEAD_REF" == "" ]]; then
|
||||||
|
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}})
|
||||||
|
else
|
||||||
|
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}} --level pr)
|
||||||
|
fi
|
||||||
|
echo "Name: $GITHUB_REF_NAME Base: $GITHUB_BASE_REF Ref: $GITHUB_REF"
|
||||||
|
echo "${{matrix.arch}}=$TARGETS" >> $GITHUB_OUTPUT
|
||||||
|
outputs:
|
||||||
|
all: ${{ steps.jsonStep.outputs.all }}
|
||||||
|
check: ${{ steps.jsonStep.outputs.check }}
|
||||||
|
|
||||||
|
version:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- name: Get release version string
|
||||||
|
run: |
|
||||||
|
echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
||||||
|
echo "deb=$(./bin/buildinfo.py deb)" >> $GITHUB_OUTPUT
|
||||||
|
id: version
|
||||||
|
env:
|
||||||
|
BUILD_LOCATION: local
|
||||||
|
outputs:
|
||||||
|
long: ${{ steps.version.outputs.long }}
|
||||||
|
deb: ${{ steps.version.outputs.deb }}
|
||||||
|
|
||||||
|
check:
|
||||||
|
needs: setup
|
||||||
|
strategy:
|
||||||
|
fail-fast: true
|
||||||
|
matrix:
|
||||||
|
check: ${{ fromJson(needs.setup.outputs.check) }}
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event_name != 'workflow_dispatch' }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- name: Build base
|
||||||
|
id: base
|
||||||
|
uses: ./.github/actions/setup-base
|
||||||
|
- name: Check ${{ matrix.check.board }}
|
||||||
|
run: bin/check-all.sh ${{ matrix.check.board }}
|
||||||
|
|
||||||
|
build:
|
||||||
|
needs: [setup, version]
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
build: ${{ fromJson(needs.setup.outputs.all) }}
|
||||||
|
uses: ./.github/workflows/build_firmware.yml
|
||||||
|
with:
|
||||||
|
version: ${{ needs.version.outputs.long }}
|
||||||
|
pio_env: ${{ matrix.build.board }}
|
||||||
|
platform: ${{ matrix.build.platform }}
|
||||||
|
|
||||||
|
build-debian-src:
|
||||||
|
if: github.repository == 'meshtastic/firmware'
|
||||||
|
uses: ./.github/workflows/build_debian_src.yml
|
||||||
|
with:
|
||||||
|
series: UNRELEASED
|
||||||
|
build_location: local
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
package-pio-deps-native-tft:
|
||||||
|
if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||||
|
uses: ./.github/workflows/package_pio_deps.yml
|
||||||
|
with:
|
||||||
|
pio_env: native-tft
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
test-native:
|
||||||
|
if: ${{ !contains(github.ref_name, 'event/') }}
|
||||||
|
uses: ./.github/workflows/test_native.yml
|
||||||
|
|
||||||
|
docker:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
distro: [debian, alpine]
|
||||||
|
platform: [linux/amd64, linux/arm64, linux/arm/v7]
|
||||||
|
pio_env: [native, native-tft]
|
||||||
|
exclude:
|
||||||
|
- distro: alpine
|
||||||
|
platform: linux/arm/v7
|
||||||
|
- pio_env: native-tft
|
||||||
|
platform: linux/arm64
|
||||||
|
- pio_env: native-tft
|
||||||
|
platform: linux/arm/v7
|
||||||
|
uses: ./.github/workflows/docker_build.yml
|
||||||
|
with:
|
||||||
|
distro: ${{ matrix.distro }}
|
||||||
|
platform: ${{ matrix.platform }}
|
||||||
|
runs-on: ${{ contains(matrix.platform, 'arm') && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }}
|
||||||
|
pio_env: ${{ matrix.pio_env }}
|
||||||
|
push: false
|
||||||
|
|
||||||
|
gather-artifacts:
|
||||||
|
# trunk-ignore(checkov/CKV2_GHA_1)
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
arch:
|
||||||
|
- esp32
|
||||||
|
- esp32s3
|
||||||
|
- esp32c3
|
||||||
|
- esp32c6
|
||||||
|
- nrf52840
|
||||||
|
- rp2040
|
||||||
|
- rp2350
|
||||||
|
- stm32
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [version, build]
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
with:
|
||||||
|
ref: ${{github.event.pull_request.head.ref}}
|
||||||
|
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
path: ./
|
||||||
|
pattern: firmware-${{matrix.arch}}-*
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Display structure of downloaded files
|
||||||
|
run: ls -R
|
||||||
|
|
||||||
|
- name: Move files up
|
||||||
|
run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat
|
||||||
|
|
||||||
|
- name: Repackage in single firmware zip
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
|
overwrite: true
|
||||||
|
path: |
|
||||||
|
./firmware-*.bin
|
||||||
|
./firmware-*.uf2
|
||||||
|
./firmware-*.hex
|
||||||
|
./firmware-*-ota.zip
|
||||||
|
./device-*.sh
|
||||||
|
./device-*.bat
|
||||||
|
./littlefs-*.bin
|
||||||
|
./bleota*bin
|
||||||
|
./Meshtastic_nRF52_factory_erase*.uf2
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
|
merge-multiple: true
|
||||||
|
path: ./output
|
||||||
|
|
||||||
|
# For diagnostics
|
||||||
|
- name: Show artifacts
|
||||||
|
run: ls -lR
|
||||||
|
|
||||||
|
- name: Device scripts permissions
|
||||||
|
run: |
|
||||||
|
chmod +x ./output/device-install.sh
|
||||||
|
chmod +x ./output/device-update.sh
|
||||||
|
|
||||||
|
- name: Zip firmware
|
||||||
|
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
||||||
|
|
||||||
|
- name: Repackage in single elfs zip
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
|
||||||
|
overwrite: true
|
||||||
|
path: ./*.elf
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
- uses: scruplelesswizard/comment-artifact@main
|
||||||
|
if: ${{ github.event_name == 'pull_request' }}
|
||||||
|
with:
|
||||||
|
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
|
description: "Download firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip. This artifact will be available for 90 days from creation"
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
release-artifacts:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||||
|
outputs:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
needs:
|
||||||
|
- version
|
||||||
|
- gather-artifacts
|
||||||
|
- build-debian-src
|
||||||
|
- package-pio-deps-native-tft
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- name: Setup Python
|
||||||
|
uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: 3.x
|
||||||
|
|
||||||
|
- name: Create release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
id: create_release
|
||||||
|
with:
|
||||||
|
draft: true
|
||||||
|
prerelease: true
|
||||||
|
name: Meshtastic Firmware ${{ needs.version.outputs.long }} Alpha
|
||||||
|
tag_name: v${{ needs.version.outputs.long }}
|
||||||
|
body: |
|
||||||
|
Autogenerated by github action, developer should edit as required before publishing...
|
||||||
|
|
||||||
|
- name: Download source deb
|
||||||
|
uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
pattern: firmware-debian-${{ needs.version.outputs.deb }}~UNRELEASED-src
|
||||||
|
merge-multiple: true
|
||||||
|
path: ./output/debian-src
|
||||||
|
|
||||||
|
- name: Download `native-tft` pio deps
|
||||||
|
uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }}
|
||||||
|
merge-multiple: true
|
||||||
|
path: ./output/pio-deps-native-tft
|
||||||
|
|
||||||
|
- name: Zip Linux sources
|
||||||
|
working-directory: output
|
||||||
|
run: |
|
||||||
|
zip -j -9 -r ./meshtasticd-${{ needs.version.outputs.deb }}-src.zip ./debian-src
|
||||||
|
zip -9 -r ./platformio-deps-native-tft-${{ needs.version.outputs.long }}.zip ./pio-deps-native-tft
|
||||||
|
|
||||||
|
# For diagnostics
|
||||||
|
- name: Display structure of downloaded files
|
||||||
|
run: ls -lR
|
||||||
|
|
||||||
|
- name: Add Linux sources to GtiHub Release
|
||||||
|
# Only run when targeting master branch with workflow_dispatch
|
||||||
|
if: ${{ github.ref_name == 'master' }}
|
||||||
|
run: |
|
||||||
|
gh release upload v${{ needs.version.outputs.long }} ./output/meshtasticd-${{ needs.version.outputs.deb }}-src.zip
|
||||||
|
gh release upload v${{ needs.version.outputs.long }} ./output/platformio-deps-native-tft-${{ needs.version.outputs.long }}.zip
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
release-firmware:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
arch:
|
||||||
|
- esp32
|
||||||
|
- esp32s3
|
||||||
|
- esp32c3
|
||||||
|
- esp32c6
|
||||||
|
- nrf52840
|
||||||
|
- rp2040
|
||||||
|
- rp2350
|
||||||
|
- stm32
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||||
|
needs: [release-artifacts, version]
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- name: Setup Python
|
||||||
|
uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: 3.x
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
pattern: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
|
merge-multiple: true
|
||||||
|
path: ./output
|
||||||
|
|
||||||
|
- name: Display structure of downloaded files
|
||||||
|
run: ls -lR
|
||||||
|
|
||||||
|
- name: Device scripts permissions
|
||||||
|
run: |
|
||||||
|
chmod +x ./output/device-install.sh
|
||||||
|
chmod +x ./output/device-update.sh
|
||||||
|
|
||||||
|
- name: Zip firmware
|
||||||
|
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
|
||||||
|
merge-multiple: true
|
||||||
|
path: ./elfs
|
||||||
|
|
||||||
|
- name: Zip debug elfs
|
||||||
|
run: zip -j -9 -r ./debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./elfs
|
||||||
|
|
||||||
|
# For diagnostics
|
||||||
|
- name: Display structure of downloaded files
|
||||||
|
run: ls -lR
|
||||||
|
|
||||||
|
- name: Add bins and debug elfs to GitHub Release
|
||||||
|
# Only run when targeting master branch with workflow_dispatch
|
||||||
|
if: ${{ github.ref_name == 'master' }}
|
||||||
|
run: |
|
||||||
|
gh release upload v${{ needs.version.outputs.long }} ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
|
||||||
|
gh release upload v${{ needs.version.outputs.long }} ./debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
publish-firmware:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||||
|
needs: [release-firmware, version]
|
||||||
|
env:
|
||||||
|
targets: |-
|
||||||
|
esp32,esp32s3,esp32c3,esp32c6,nrf52840,rp2040,rp2350,stm32
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- name: Setup Python
|
||||||
|
uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: 3.x
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
|
||||||
|
merge-multiple: true
|
||||||
|
path: ./publish
|
||||||
|
|
||||||
|
- name: Publish firmware to meshtastic.github.io
|
||||||
|
uses: peaceiris/actions-gh-pages@v4
|
||||||
|
env:
|
||||||
|
# On event/* branches, use the event name as the destination prefix
|
||||||
|
DEST_PREFIX: ${{ contains(github.ref_name, 'event/') && format('{0}/', github.ref_name) || '' }}
|
||||||
|
with:
|
||||||
|
deploy_key: ${{ secrets.DIST_PAGES_DEPLOY_KEY }}
|
||||||
|
external_repository: meshtastic/meshtastic.github.io
|
||||||
|
publish_branch: master
|
||||||
|
publish_dir: ./publish
|
||||||
|
destination_dir: ${{ env.DEST_PREFIX }}firmware-${{ needs.version.outputs.long }}
|
||||||
|
keep_files: true
|
||||||
|
user_name: github-actions[bot]
|
||||||
|
user_email: github-actions[bot]@users.noreply.github.com
|
||||||
|
commit_message: ${{ needs.version.outputs.long }}
|
||||||
|
enable_jekyll: true
|
||||||
2
.github/workflows/pr_enforce_labels.yml
vendored
2
.github/workflows/pr_enforce_labels.yml
vendored
@@ -10,7 +10,7 @@ permissions:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-label:
|
check-label:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check for PR labels
|
- name: Check for PR labels
|
||||||
uses: actions/github-script@v8
|
uses: actions/github-script@v8
|
||||||
|
|||||||
6
.github/workflows/release_channels.yml
vendored
6
.github/workflows/release_channels.yml
vendored
@@ -21,10 +21,10 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
series:
|
series:
|
||||||
- jammy # 22.04
|
- jammy # 22.04 LTS
|
||||||
- noble # 24.04
|
- noble # 24.04 LTS
|
||||||
- plucky # 25.04
|
- plucky # 25.04
|
||||||
# - questing # 25.10
|
- questing # 25.10
|
||||||
uses: ./.github/workflows/package_ppa.yml
|
uses: ./.github/workflows/package_ppa.yml
|
||||||
with:
|
with:
|
||||||
ppa_repo: |-
|
ppa_repo: |-
|
||||||
|
|||||||
2
.github/workflows/sec_sast_semgrep_cron.yml
vendored
2
.github/workflows/sec_sast_semgrep_cron.yml
vendored
@@ -41,7 +41,7 @@ jobs:
|
|||||||
|
|
||||||
# step 4
|
# step 4
|
||||||
- name: publish code scanning alerts
|
- name: publish code scanning alerts
|
||||||
uses: github/codeql-action/upload-sarif@v3
|
uses: github/codeql-action/upload-sarif@v4
|
||||||
with:
|
with:
|
||||||
sarif_file: report.sarif
|
sarif_file: report.sarif
|
||||||
category: semgrep
|
category: semgrep
|
||||||
|
|||||||
8
.github/workflows/stale_bot.yml
vendored
8
.github/workflows/stale_bot.yml
vendored
@@ -17,8 +17,10 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Stale PR+Issues
|
- name: Stale PR+Issues
|
||||||
uses: actions/stale@v10.0.0
|
uses: actions/stale@v10.1.0
|
||||||
with:
|
with:
|
||||||
days-before-stale: 45
|
days-before-stale: 45
|
||||||
exempt-issue-labels: pinned,3.0
|
stale-issue-message: This issue has not had any comment or update in the last month. If it is still relevant, please post update comments. If no comments are made, this issue will be closed automagically in 7 days.
|
||||||
exempt-pr-labels: pinned,3.0
|
close-issue-message: This issue has not had any comment since the last notice. It has been closed automatically. If this is incorrect, or the issue becomes relevant again, please request that it is reopened.
|
||||||
|
exempt-issue-labels: pinned,3.0,triaged,backlog
|
||||||
|
exempt-pr-labels: pinned,3.0,triaged,backlog
|
||||||
|
|||||||
2
.github/workflows/test_native.yml
vendored
2
.github/workflows/test_native.yml
vendored
@@ -40,7 +40,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Integration test
|
- name: Integration test
|
||||||
run: |
|
run: |
|
||||||
.pio/build/coverage/program &
|
.pio/build/coverage/program -s &
|
||||||
PID=$!
|
PID=$!
|
||||||
timeout 20 bash -c "until ls -al /proc/$PID/fd | grep socket; do sleep 1; done"
|
timeout 20 bash -c "until ls -al /proc/$PID/fd | grep socket; do sleep 1; done"
|
||||||
echo "Simulator started, launching python test..."
|
echo "Simulator started, launching python test..."
|
||||||
|
|||||||
4
.github/workflows/tests.yml
vendored
4
.github/workflows/tests.yml
vendored
@@ -47,9 +47,9 @@ jobs:
|
|||||||
pio upgrade
|
pio upgrade
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v5
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: 22
|
node-version: 24
|
||||||
|
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4
|
||||||
|
|||||||
@@ -4,29 +4,29 @@ cli:
|
|||||||
plugins:
|
plugins:
|
||||||
sources:
|
sources:
|
||||||
- id: trunk
|
- id: trunk
|
||||||
ref: v1.7.2
|
ref: v1.7.3
|
||||||
uri: https://github.com/trunk-io/plugins
|
uri: https://github.com/trunk-io/plugins
|
||||||
lint:
|
lint:
|
||||||
enabled:
|
enabled:
|
||||||
- checkov@3.2.469
|
- checkov@3.2.486
|
||||||
- renovate@41.94.0
|
- renovate@41.157.0
|
||||||
- prettier@3.6.2
|
- prettier@3.6.2
|
||||||
- trufflehog@3.90.5
|
- trufflehog@3.90.11
|
||||||
- yamllint@1.37.1
|
- yamllint@1.37.1
|
||||||
- bandit@1.8.6
|
- bandit@1.8.6
|
||||||
- trivy@0.66.0
|
- trivy@0.67.2
|
||||||
- taplo@0.10.0
|
- taplo@0.10.0
|
||||||
- ruff@0.12.11
|
- ruff@0.14.1
|
||||||
- isort@6.0.1
|
- isort@7.0.0
|
||||||
- markdownlint@0.45.0
|
- markdownlint@0.45.0
|
||||||
- oxipng@9.1.5
|
- oxipng@9.1.5
|
||||||
- svgo@4.0.0
|
- svgo@4.0.0
|
||||||
- actionlint@1.7.7
|
- actionlint@1.7.8
|
||||||
- flake8@7.3.0
|
- flake8@7.3.0
|
||||||
- hadolint@2.13.1
|
- hadolint@2.14.0
|
||||||
- shfmt@3.6.0
|
- shfmt@3.6.0
|
||||||
- shellcheck@0.11.0
|
- shellcheck@0.11.0
|
||||||
- black@25.1.0
|
- black@25.9.0
|
||||||
- git-diff-check
|
- git-diff-check
|
||||||
- gitleaks@8.28.0
|
- gitleaks@8.28.0
|
||||||
- clang-format@16.0.3
|
- clang-format@16.0.3
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
# trunk-ignore-all(hadolint/DL3008): Do not pin apt package versions
|
# trunk-ignore-all(hadolint/DL3008): Do not pin apt package versions
|
||||||
# trunk-ignore-all(hadolint/DL3013): Do not pin pip package versions
|
# trunk-ignore-all(hadolint/DL3013): Do not pin pip package versions
|
||||||
|
|
||||||
FROM python:3.13-slim-trixie AS builder
|
FROM python:3.14-slim-trixie AS builder
|
||||||
ARG PIO_ENV=native
|
ARG PIO_ENV=native
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
ENV TZ=Etc/UTC
|
ENV TZ=Etc/UTC
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
# trunk-ignore-all(hadolint/DL3018): Do not pin apk package versions
|
# trunk-ignore-all(hadolint/DL3018): Do not pin apk package versions
|
||||||
# trunk-ignore-all(hadolint/DL3013): Do not pin pip package versions
|
# trunk-ignore-all(hadolint/DL3013): Do not pin pip package versions
|
||||||
|
|
||||||
FROM python:3.13-alpine3.22 AS builder
|
FROM python:3.14-alpine3.22 AS builder
|
||||||
ARG PIO_ENV=native
|
ARG PIO_ENV=native
|
||||||
ENV PIP_ROOT_USER_ACTION=ignore
|
ENV PIP_ROOT_USER_ACTION=ignore
|
||||||
|
|
||||||
|
|||||||
@@ -31,11 +31,13 @@ build_flags =
|
|||||||
-DMYNEWT_VAL_BLE_HS_LOG_LVL=LOG_LEVEL_CRITICAL
|
-DMYNEWT_VAL_BLE_HS_LOG_LVL=LOG_LEVEL_CRITICAL
|
||||||
-DAXP_DEBUG_PORT=Serial
|
-DAXP_DEBUG_PORT=Serial
|
||||||
-DCONFIG_BT_NIMBLE_ENABLED
|
-DCONFIG_BT_NIMBLE_ENABLED
|
||||||
|
-DCONFIG_BT_NIMBLE_MAX_BONDS=6 # default is 3
|
||||||
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=2
|
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=2
|
||||||
-DCONFIG_BT_NIMBLE_MAX_CCCDS=20
|
-DCONFIG_BT_NIMBLE_MAX_CCCDS=20
|
||||||
-DCONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=8192
|
-DCONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=8192
|
||||||
-DESP_OPENSSL_SUPPRESS_LEGACY_WARNING
|
-DESP_OPENSSL_SUPPRESS_LEGACY_WARNING
|
||||||
-DSERIAL_BUFFER_SIZE=4096
|
-DSERIAL_BUFFER_SIZE=4096
|
||||||
|
-DSERIAL_HAS_ON_RECEIVE
|
||||||
-DLIBPAX_ARDUINO
|
-DLIBPAX_ARDUINO
|
||||||
-DLIBPAX_WIFI
|
-DLIBPAX_WIFI
|
||||||
-DLIBPAX_BLE
|
-DLIBPAX_BLE
|
||||||
@@ -55,7 +57,7 @@ lib_deps =
|
|||||||
# renovate: datasource=git-refs depName=libpax packageName=https://github.com/dbinfrago/libpax gitBranch=master
|
# renovate: datasource=git-refs depName=libpax packageName=https://github.com/dbinfrago/libpax gitBranch=master
|
||||||
https://github.com/dbinfrago/libpax/archive/3cdc0371c375676a97967547f4065607d4c53fd1.zip
|
https://github.com/dbinfrago/libpax/archive/3cdc0371c375676a97967547f4065607d4c53fd1.zip
|
||||||
# renovate: datasource=github-tags depName=XPowersLib packageName=lewisxhe/XPowersLib
|
# renovate: datasource=github-tags depName=XPowersLib packageName=lewisxhe/XPowersLib
|
||||||
https://github.com/lewisxhe/XPowersLib/archive/v0.3.0.zip
|
https://github.com/lewisxhe/XPowersLib/archive/v0.3.1.zip
|
||||||
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
|
||||||
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
|
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
|
||||||
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ lib_deps =
|
|||||||
${environmental_extra.lib_deps}
|
${environmental_extra.lib_deps}
|
||||||
${radiolib_base.lib_deps}
|
${radiolib_base.lib_deps}
|
||||||
# renovate: datasource=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib
|
# renovate: datasource=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib
|
||||||
lewisxhe/XPowersLib@0.3.0
|
lewisxhe/XPowersLib@0.3.1
|
||||||
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
|
||||||
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
|
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
|
||||||
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
[portduino_base]
|
[portduino_base]
|
||||||
platform =
|
platform =
|
||||||
# renovate: datasource=git-refs depName=platform-native packageName=https://github.com/meshtastic/platform-native gitBranch=develop
|
# renovate: datasource=git-refs depName=platform-native packageName=https://github.com/meshtastic/platform-native gitBranch=develop
|
||||||
https://github.com/meshtastic/platform-native/archive/c490bcd019e0658404088a61b96e653c9da22c45.zip
|
https://github.com/meshtastic/platform-native/archive/d3f6e339534233c7217818867368767590ce549e.zip
|
||||||
framework = arduino
|
framework = arduino
|
||||||
|
|
||||||
build_src_filter =
|
build_src_filter =
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
sed -i 's/#-DBUILD_EPOCH=$UNIX_TIME/-DBUILD_EPOCH=$UNIX_TIME/' platformio.ini
|
|
||||||
|
|
||||||
export PIP_BREAK_SYSTEM_PACKAGES=1
|
export PIP_BREAK_SYSTEM_PACKAGES=1
|
||||||
|
|
||||||
if (echo $2 | grep -q "esp32"); then
|
if (echo $2 | grep -q "esp32"); then
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
PYTHON=${PYTHON:-$(which python3 python | head -n 1)}
|
PYTHON=${PYTHON:-$(which python3 python | head -n 1)}
|
||||||
BPS_RESET=false
|
BPS_RESET=false
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
PYTHON=${PYTHON:-$(which python3 python|head -n 1)}
|
PYTHON=${PYTHON:-$(which python3 python|head -n 1)}
|
||||||
CHANGE_MODE=false
|
CHANGE_MODE=false
|
||||||
|
|||||||
@@ -1,28 +1,32 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
"""Generate the CI matrix."""
|
"""Generate the CI matrix."""
|
||||||
|
|
||||||
|
import argparse
|
||||||
import json
|
import json
|
||||||
import sys
|
|
||||||
import random
|
|
||||||
import re
|
import re
|
||||||
from platformio.project.config import ProjectConfig
|
from platformio.project.config import ProjectConfig
|
||||||
|
|
||||||
options = sys.argv[1:]
|
parser = argparse.ArgumentParser(description="Generate the CI matrix")
|
||||||
|
parser.add_argument("platform", help="Platform to build for")
|
||||||
|
parser.add_argument(
|
||||||
|
"--level",
|
||||||
|
choices=["extra", "pr"],
|
||||||
|
nargs="*",
|
||||||
|
default=[],
|
||||||
|
help="Board level to build for (omit for full release boards)",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
outlist = []
|
outlist = []
|
||||||
|
|
||||||
if len(options) < 1:
|
|
||||||
print(json.dumps(outlist))
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
cfg = ProjectConfig.get_instance()
|
cfg = ProjectConfig.get_instance()
|
||||||
pio_envs = cfg.envs()
|
pio_envs = cfg.envs()
|
||||||
|
|
||||||
# Gather all PlatformIO environments for filtering later
|
# Gather all PlatformIO environments for filtering later
|
||||||
all_envs = []
|
all_envs = []
|
||||||
for pio_env in pio_envs:
|
for pio_env in pio_envs:
|
||||||
env_build_flags = cfg.get(f"env:{pio_env}", 'build_flags')
|
env_build_flags = cfg.get(f"env:{pio_env}", "build_flags")
|
||||||
env_platform = None
|
env_platform = None
|
||||||
for flag in env_build_flags:
|
for flag in env_build_flags:
|
||||||
# Extract the platform from the build flags
|
# Extract the platform from the build flags
|
||||||
@@ -37,36 +41,35 @@ for pio_env in pio_envs:
|
|||||||
exit(1)
|
exit(1)
|
||||||
# Store env details as a dictionary, and add to 'all_envs' list
|
# Store env details as a dictionary, and add to 'all_envs' list
|
||||||
env = {
|
env = {
|
||||||
'name': pio_env,
|
"ci": {"board": pio_env, "platform": env_platform},
|
||||||
'platform': env_platform,
|
"board_level": cfg.get(f"env:{pio_env}", "board_level", default=None),
|
||||||
'board_level': cfg.get(f"env:{pio_env}", 'board_level', default=None),
|
"board_check": bool(cfg.get(f"env:{pio_env}", "board_check", default=False)),
|
||||||
'board_check': bool(cfg.get(f"env:{pio_env}", 'board_check', default=False))
|
|
||||||
}
|
}
|
||||||
all_envs.append(env)
|
all_envs.append(env)
|
||||||
|
|
||||||
# Filter outputs based on options
|
# Filter outputs based on options
|
||||||
# Check is mutually exclusive with other options (except 'pr')
|
# Check is mutually exclusive with other options (except 'pr')
|
||||||
if "check" in options:
|
if "check" in args.platform:
|
||||||
for env in all_envs:
|
for env in all_envs:
|
||||||
if env['board_check']:
|
if env["board_check"]:
|
||||||
if "pr" in options:
|
if "pr" in args.level:
|
||||||
if env['board_level'] == 'pr':
|
if env["board_level"] == "pr":
|
||||||
outlist.append(env['name'])
|
outlist.append(env["ci"])
|
||||||
else:
|
else:
|
||||||
outlist.append(env['name'])
|
outlist.append(env["ci"])
|
||||||
# Filter (non-check) builds by platform
|
# Filter (non-check) builds by platform
|
||||||
else:
|
else:
|
||||||
for env in all_envs:
|
for env in all_envs:
|
||||||
if options[0] == env['platform']:
|
if args.platform == env["ci"]["platform"] or args.platform == "all":
|
||||||
# Always include board_level = 'pr'
|
# Always include board_level = 'pr'
|
||||||
if env['board_level'] == 'pr':
|
if env["board_level"] == "pr":
|
||||||
outlist.append(env['name'])
|
outlist.append(env["ci"])
|
||||||
# Include board_level = 'extra' when requested
|
# Include board_level = 'extra' when requested
|
||||||
elif "extra" in options and env['board_level'] == "extra":
|
elif "extra" in args.level and env["board_level"] == "extra":
|
||||||
outlist.append(env['name'])
|
outlist.append(env["ci"])
|
||||||
# If no board level is specified, include in release builds (not PR)
|
# If no board level is specified, include in release builds (not PR)
|
||||||
elif "pr" not in options and not env['board_level']:
|
elif "pr" not in args.level and not env["board_level"]:
|
||||||
outlist.append(env['name'])
|
outlist.append(env["ci"])
|
||||||
|
|
||||||
# Return as a JSON list
|
# Return as a JSON list
|
||||||
print(json.dumps(outlist))
|
print(json.dumps(outlist))
|
||||||
|
|||||||
116
bin/kill-github-actions.sh
Executable file
116
bin/kill-github-actions.sh
Executable file
@@ -0,0 +1,116 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Script to cancel all running GitHub Actions workflows
|
||||||
|
# Requires GitHub CLI (gh) to be installed and authenticated
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Function to print colored output
|
||||||
|
print_status() {
|
||||||
|
echo -e "${GREEN}[INFO]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_warning() {
|
||||||
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if gh CLI is installed
|
||||||
|
if ! command -v gh &> /dev/null; then
|
||||||
|
print_error "GitHub CLI (gh) is not installed. Please install it first:"
|
||||||
|
echo " brew install gh"
|
||||||
|
echo " Or visit: https://cli.github.com/"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if authenticated
|
||||||
|
if ! gh auth status &> /dev/null; then
|
||||||
|
print_error "GitHub CLI is not authenticated. Please run:"
|
||||||
|
echo " gh auth login"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get repository info
|
||||||
|
REPO=$(gh repo view --json owner,name -q '.owner.login + "/" + .name')
|
||||||
|
if [[ -z "$REPO" ]]; then
|
||||||
|
print_error "Could not determine repository. Make sure you're in a GitHub repository."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_status "Working with repository: $REPO"
|
||||||
|
|
||||||
|
# Get all active workflows (both queued and in-progress)
|
||||||
|
print_status "Fetching active workflows (queued and in-progress)..."
|
||||||
|
QUEUED_WORKFLOWS=$(gh run list --status queued --json databaseId,displayTitle,headBranch,status --limit 100)
|
||||||
|
IN_PROGRESS_WORKFLOWS=$(gh run list --status in_progress --json databaseId,displayTitle,headBranch,status --limit 100)
|
||||||
|
|
||||||
|
# Combine both lists
|
||||||
|
ALL_WORKFLOWS=$(echo "$QUEUED_WORKFLOWS $IN_PROGRESS_WORKFLOWS" | jq -s 'add | unique_by(.databaseId)')
|
||||||
|
|
||||||
|
if [[ "$ALL_WORKFLOWS" == "[]" ]]; then
|
||||||
|
print_status "No active workflows found."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Parse and display active workflows
|
||||||
|
echo
|
||||||
|
print_warning "Found active workflows:"
|
||||||
|
echo "$ALL_WORKFLOWS" | jq -r '.[] | " - \(.displayTitle) (Branch: \(.headBranch), Status: \(.status), ID: \(.databaseId))"'
|
||||||
|
|
||||||
|
echo
|
||||||
|
read -p "Do you want to cancel ALL these workflows? (y/N): " -n 1 -r
|
||||||
|
echo
|
||||||
|
|
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
print_status "Cancelled by user."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Cancel each workflow
|
||||||
|
print_status "Cancelling workflows..."
|
||||||
|
CANCELLED_COUNT=0
|
||||||
|
FAILED_COUNT=0
|
||||||
|
|
||||||
|
while IFS= read -r WORKFLOW_ID; do
|
||||||
|
if [[ -n "$WORKFLOW_ID" ]]; then
|
||||||
|
print_status "Cancelling workflow ID: $WORKFLOW_ID"
|
||||||
|
if gh run cancel "$WORKFLOW_ID" 2>/dev/null; then
|
||||||
|
((CANCELLED_COUNT++))
|
||||||
|
else
|
||||||
|
print_error "Failed to cancel workflow ID: $WORKFLOW_ID"
|
||||||
|
((FAILED_COUNT++))
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done < <(echo "$ALL_WORKFLOWS" | jq -r '.[].databaseId')
|
||||||
|
|
||||||
|
echo
|
||||||
|
print_status "Summary:"
|
||||||
|
echo " - Cancelled: $CANCELLED_COUNT workflows"
|
||||||
|
if [[ $FAILED_COUNT -gt 0 ]]; then
|
||||||
|
echo " - Failed: $FAILED_COUNT workflows"
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_status "Done!"
|
||||||
|
|
||||||
|
# Optional: Show remaining active workflows
|
||||||
|
echo
|
||||||
|
print_status "Checking for any remaining active workflows..."
|
||||||
|
REMAINING_QUEUED=$(gh run list --status queued --json databaseId --limit 10)
|
||||||
|
REMAINING_IN_PROGRESS=$(gh run list --status in_progress --json databaseId --limit 10)
|
||||||
|
REMAINING_ALL=$(echo "$REMAINING_QUEUED $REMAINING_IN_PROGRESS" | jq -s 'add | unique_by(.databaseId)')
|
||||||
|
|
||||||
|
if [[ "$REMAINING_ALL" == "[]" ]]; then
|
||||||
|
print_status "All workflows successfully cancelled."
|
||||||
|
else
|
||||||
|
REMAINING_COUNT=$(echo "$REMAINING_ALL" | jq '. | length')
|
||||||
|
print_warning "Still $REMAINING_COUNT workflows active (may take a moment to update status)"
|
||||||
|
fi
|
||||||
@@ -87,6 +87,18 @@
|
|||||||
</screenshots>
|
</screenshots>
|
||||||
|
|
||||||
<releases>
|
<releases>
|
||||||
|
<release version="2.7.13" date="2025-10-11">
|
||||||
|
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.13</url>
|
||||||
|
</release>
|
||||||
|
<release version="2.7.12" date="2025-10-01">
|
||||||
|
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.12</url>
|
||||||
|
</release>
|
||||||
|
<release version="2.7.11" date="2025-09-24">
|
||||||
|
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.11</url>
|
||||||
|
</release>
|
||||||
|
<release version="2.7.10" date="2025-09-18">
|
||||||
|
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.10</url>
|
||||||
|
</release>
|
||||||
<release version="2.7.9" date="2025-09-03">
|
<release version="2.7.9" date="2025-09-03">
|
||||||
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.9</url>
|
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.9</url>
|
||||||
</release>
|
</release>
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ from os.path import join
|
|||||||
import subprocess
|
import subprocess
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
import time
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from readprops import readProps
|
from readprops import readProps
|
||||||
|
|
||||||
@@ -84,7 +86,7 @@ if platform.name == "espressif32":
|
|||||||
|
|
||||||
if platform.name == "nordicnrf52":
|
if platform.name == "nordicnrf52":
|
||||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.hex",
|
env.AddPostAction("$BUILD_DIR/${PROGNAME}.hex",
|
||||||
env.VerboseAction(f"\"{sys.executable}\" ./bin/uf2conv.py $BUILD_DIR/firmware.hex -c -f 0xADA52840 -o $BUILD_DIR/firmware.uf2",
|
env.VerboseAction(f"\"{sys.executable}\" ./bin/uf2conv.py \"$BUILD_DIR/firmware.hex\" -c -f 0xADA52840 -o \"$BUILD_DIR/firmware.uf2\"",
|
||||||
"Generating UF2 file"))
|
"Generating UF2 file"))
|
||||||
|
|
||||||
Import("projenv")
|
Import("projenv")
|
||||||
@@ -125,11 +127,16 @@ for pref in userPrefs:
|
|||||||
pref_flags.append("-D" + pref + "=" + env.StringifyMacro(userPrefs[pref]) + "")
|
pref_flags.append("-D" + pref + "=" + env.StringifyMacro(userPrefs[pref]) + "")
|
||||||
|
|
||||||
# General options that are passed to the C and C++ compilers
|
# General options that are passed to the C and C++ compilers
|
||||||
|
# Calculate unix epoch for current day (midnight)
|
||||||
|
current_date = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
|
||||||
|
build_epoch = int(current_date.timestamp())
|
||||||
|
|
||||||
flags = [
|
flags = [
|
||||||
"-DAPP_VERSION=" + verObj["long"],
|
"-DAPP_VERSION=" + verObj["long"],
|
||||||
"-DAPP_VERSION_SHORT=" + verObj["short"],
|
"-DAPP_VERSION_SHORT=" + verObj["short"],
|
||||||
"-DAPP_ENV=" + env.get("PIOENV"),
|
"-DAPP_ENV=" + env.get("PIOENV"),
|
||||||
"-DAPP_REPO=" + repo_owner,
|
"-DAPP_REPO=" + repo_owner,
|
||||||
|
"-DBUILD_EPOCH=" + str(build_epoch),
|
||||||
] + pref_flags
|
] + pref_flags
|
||||||
|
|
||||||
print ("Using flags:")
|
print ("Using flags:")
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
2.6.4
|
2.6.7
|
||||||
43
boards/heltec_v4.json
Normal file
43
boards/heltec_v4.json
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"arduino": {
|
||||||
|
"ldscript": "esp32s3_out.ld",
|
||||||
|
"partitions": "default_16MB.csv",
|
||||||
|
"memory_type": "qio_qspi"
|
||||||
|
},
|
||||||
|
"core": "esp32",
|
||||||
|
"extra_flags": [
|
||||||
|
"-DBOARD_HAS_PSRAM",
|
||||||
|
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||||
|
"-DARDUINO_USB_MODE=0",
|
||||||
|
"-DARDUINO_RUNNING_CORE=1",
|
||||||
|
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||||
|
],
|
||||||
|
"f_cpu": "240000000L",
|
||||||
|
"f_flash": "80000000L",
|
||||||
|
"flash_mode": "qio",
|
||||||
|
"psram_type": "qspi",
|
||||||
|
"hwids": [["0x303A", "0x1001"]],
|
||||||
|
"mcu": "esp32s3",
|
||||||
|
"variant": "heltec_v4"
|
||||||
|
},
|
||||||
|
"connectivity": ["wifi", "bluetooth", "lora"],
|
||||||
|
"debug": {
|
||||||
|
"default_tool": "esp-builtin",
|
||||||
|
"onboard_tools": ["esp-builtin"],
|
||||||
|
"openocd_target": "esp32s3.cfg"
|
||||||
|
},
|
||||||
|
"frameworks": ["arduino", "espidf"],
|
||||||
|
"name": "heltec_wifi_lora_32 v4 (16 MB FLASH, 2 MB PSRAM)",
|
||||||
|
"upload": {
|
||||||
|
"flash_size": "16MB",
|
||||||
|
"maximum_ram_size": 2097152,
|
||||||
|
"maximum_size": 16777216,
|
||||||
|
"use_1200bps_touch": true,
|
||||||
|
"wait_for_upload_port": true,
|
||||||
|
"require_upload_port": true,
|
||||||
|
"speed": 921600
|
||||||
|
},
|
||||||
|
"url": "https://heltec.org/",
|
||||||
|
"vendor": "heltec"
|
||||||
|
}
|
||||||
37
boards/heltec_wireless_tracker_v2.json
Normal file
37
boards/heltec_wireless_tracker_v2.json
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"arduino": {
|
||||||
|
"ldscript": "esp32s3_out.ld",
|
||||||
|
"partitions": "default_8MB.csv"
|
||||||
|
},
|
||||||
|
"core": "esp32",
|
||||||
|
"extra_flags": [
|
||||||
|
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||||
|
"-DARDUINO_USB_MODE=0",
|
||||||
|
"-DARDUINO_RUNNING_CORE=1",
|
||||||
|
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||||
|
],
|
||||||
|
"f_cpu": "240000000L",
|
||||||
|
"f_flash": "80000000L",
|
||||||
|
"flash_mode": "qio",
|
||||||
|
"hwids": [["0x303A", "0x1001"]],
|
||||||
|
"mcu": "esp32s3",
|
||||||
|
"variant": "heltec_wireless_tracker_v2"
|
||||||
|
},
|
||||||
|
"connectivity": ["wifi", "bluetooth", "lora"],
|
||||||
|
"debug": {
|
||||||
|
"openocd_target": "esp32s3.cfg"
|
||||||
|
},
|
||||||
|
"frameworks": ["arduino", "espidf"],
|
||||||
|
"name": "Heltec Wireless Tracker V2",
|
||||||
|
"upload": {
|
||||||
|
"flash_size": "8MB",
|
||||||
|
"maximum_ram_size": 327680,
|
||||||
|
"maximum_size": 8388608,
|
||||||
|
"wait_for_upload_port": true,
|
||||||
|
"require_upload_port": true,
|
||||||
|
"speed": 921600
|
||||||
|
},
|
||||||
|
"url": "https://heltec.org",
|
||||||
|
"vendor": "Heltec"
|
||||||
|
}
|
||||||
52
boards/r1-neo.json
Normal file
52
boards/r1-neo.json
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"arduino": {
|
||||||
|
"ldscript": "nrf52840_s140_v6.ld"
|
||||||
|
},
|
||||||
|
"core": "nRF5",
|
||||||
|
"cpu": "cortex-m4",
|
||||||
|
"extra_flags": "-DARDUINO_NRF52840_FEATHER -DNRF52840_XXAA",
|
||||||
|
"f_cpu": "64000000L",
|
||||||
|
"hwids": [
|
||||||
|
["0x239A", "0x8029"],
|
||||||
|
["0x239A", "0x0029"],
|
||||||
|
["0x239A", "0x002A"],
|
||||||
|
["0x239A", "0x802A"]
|
||||||
|
],
|
||||||
|
"usb_product": "Muzi R1 Neo",
|
||||||
|
"mcu": "nrf52840",
|
||||||
|
"variant": "r1-neo",
|
||||||
|
"bsp": {
|
||||||
|
"name": "adafruit"
|
||||||
|
},
|
||||||
|
"softdevice": {
|
||||||
|
"sd_flags": "-DS140",
|
||||||
|
"sd_name": "s140",
|
||||||
|
"sd_version": "6.1.1",
|
||||||
|
"sd_fwid": "0x00B6"
|
||||||
|
},
|
||||||
|
"bootloader": {
|
||||||
|
"settings_addr": "0xFF000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connectivity": ["bluetooth"],
|
||||||
|
"debug": {
|
||||||
|
"jlink_device": "nRF52840_xxAA",
|
||||||
|
"svd_path": "nrf52840.svd",
|
||||||
|
"openocd_target": "nrf52840-mdk-rs"
|
||||||
|
},
|
||||||
|
"frameworks": ["arduino", "freertos"],
|
||||||
|
"name": "WisCore RAK4631 Board",
|
||||||
|
"upload": {
|
||||||
|
"maximum_ram_size": 248832,
|
||||||
|
"maximum_size": 815104,
|
||||||
|
"speed": 115200,
|
||||||
|
"protocol": "nrfutil",
|
||||||
|
"protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"],
|
||||||
|
"use_1200bps_touch": true,
|
||||||
|
"require_upload_port": true,
|
||||||
|
"wait_for_upload_port": true
|
||||||
|
},
|
||||||
|
"url": "https://muzi.works/",
|
||||||
|
"vendor": "Muzi Works"
|
||||||
|
}
|
||||||
53
debian/changelog
vendored
53
debian/changelog
vendored
@@ -1,50 +1,19 @@
|
|||||||
meshtasticd (2.7.9.0) UNRELEASED; urgency=medium
|
meshtasticd (2.7.13.0) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Version 2.7.13
|
||||||
|
|
||||||
|
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Sat, 11 Oct 2025 15:27:28 +0000
|
||||||
|
|
||||||
|
meshtasticd (2.7.12.0) unstable; urgency=medium
|
||||||
|
|
||||||
[ Austin Lane ]
|
[ Austin Lane ]
|
||||||
* Initial packaging
|
* Initial packaging
|
||||||
* GitHub Actions Automatic version bump
|
* Version 2.5.19
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
|
|
||||||
[ ]
|
[ ]
|
||||||
* GitHub Actions Automatic version bump
|
* GitHub Actions Automatic version bump
|
||||||
|
|
||||||
[ ]
|
[ GitHub Actions ]
|
||||||
* GitHub Actions Automatic version bump
|
* Version 2.7.12
|
||||||
|
|
||||||
[ ]
|
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Wed, 01 Oct 2025 19:51:41 +0000
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
|
|
||||||
[ ]
|
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
|
|
||||||
[ ]
|
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
|
|
||||||
[ ]
|
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
|
|
||||||
[ ]
|
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
|
|
||||||
[ Ubuntu ]
|
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
|
|
||||||
[ ]
|
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
|
|
||||||
[ ]
|
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
|
|
||||||
[ ]
|
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
|
|
||||||
[ ]
|
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
|
|
||||||
[ ]
|
|
||||||
* GitHub Actions Automatic version bump
|
|
||||||
|
|
||||||
-- <github-actions[bot]@users.noreply.github.com> Wed, 03 Sep 2025 23:39:17 +0000
|
|
||||||
|
|||||||
5
debian/ci_changelog.sh
vendored
5
debian/ci_changelog.sh
vendored
@@ -1,7 +1,8 @@
|
|||||||
#!/usr/bin/bash
|
#!/usr/bin/bash
|
||||||
|
export DEBFULLNAME="GitHub Actions"
|
||||||
export DEBEMAIL="github-actions[bot]@users.noreply.github.com"
|
export DEBEMAIL="github-actions[bot]@users.noreply.github.com"
|
||||||
PKG_VERSION=$(python3 bin/buildinfo.py short)
|
PKG_VERSION=$(python3 bin/buildinfo.py short)
|
||||||
|
|
||||||
dch --newversion "$PKG_VERSION.0" \
|
dch --newversion "$PKG_VERSION.0" \
|
||||||
--distribution UNRELEASED \
|
--distribution unstable \
|
||||||
"GitHub Actions Automatic version bump"
|
"Version $PKG_VERSION"
|
||||||
|
|||||||
2
debian/meshtasticd.postinst
vendored
2
debian/meshtasticd.postinst
vendored
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/bin/sh
|
||||||
# postinst script for meshtasticd
|
# postinst script for meshtasticd
|
||||||
#
|
#
|
||||||
# see: dh_installdeb(1)
|
# see: dh_installdeb(1)
|
||||||
|
|||||||
2
debian/meshtasticd.postrm
vendored
2
debian/meshtasticd.postrm
vendored
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/bin/sh
|
||||||
# postrm script for meshtasticd
|
# postrm script for meshtasticd
|
||||||
#
|
#
|
||||||
# see: dh_installdeb(1)
|
# see: dh_installdeb(1)
|
||||||
|
|||||||
@@ -53,14 +53,16 @@ build_flags = -Wno-missing-field-initializers
|
|||||||
-DMESHTASTIC_EXCLUDE_POWERSTRESS=1 ; exclude power stress test module from main firmware
|
-DMESHTASTIC_EXCLUDE_POWERSTRESS=1 ; exclude power stress test module from main firmware
|
||||||
-DMESHTASTIC_EXCLUDE_GENERIC_THREAD_MODULE=1
|
-DMESHTASTIC_EXCLUDE_GENERIC_THREAD_MODULE=1
|
||||||
-D MAX_THREADS=40 ; As we've split modules, we have more threads to manage
|
-D MAX_THREADS=40 ; As we've split modules, we have more threads to manage
|
||||||
#-DBUILD_EPOCH=$UNIX_TIME
|
#-DBUILD_EPOCH=$UNIX_TIME ; set in platformio-custom.py now
|
||||||
#-D OLED_PL=1
|
#-D OLED_PL=1
|
||||||
|
#-D DEBUG_HEAP=1 ; uncomment to add free heap space / memory leak debugging logs
|
||||||
|
#-D DEBUG_LOOP_TIMING=1 ; uncomment to add main loop timing logs
|
||||||
|
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
monitor_filters = direct
|
monitor_filters = direct
|
||||||
lib_deps =
|
lib_deps =
|
||||||
# renovate: datasource=git-refs depName=meshtastic-esp8266-oled-ssd1306 packageName=https://github.com/meshtastic/esp8266-oled-ssd1306 gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic-esp8266-oled-ssd1306 packageName=https://github.com/meshtastic/esp8266-oled-ssd1306 gitBranch=master
|
||||||
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/9573abb64dc9c94f3051348f2bf4fc5cedf03c22.zip
|
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/0cbc26b1f8f61957af0475f486b362eafe7cc4e2.zip
|
||||||
# renovate: datasource=git-refs depName=meshtastic-OneButton packageName=https://github.com/meshtastic/OneButton gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic-OneButton packageName=https://github.com/meshtastic/OneButton gitBranch=master
|
||||||
https://github.com/meshtastic/OneButton/archive/fa352d668c53f290cfa480a5f79ad422cd828c70.zip
|
https://github.com/meshtastic/OneButton/archive/fa352d668c53f290cfa480a5f79ad422cd828c70.zip
|
||||||
# renovate: datasource=git-refs depName=meshtastic-arduino-fsm packageName=https://github.com/meshtastic/arduino-fsm gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic-arduino-fsm packageName=https://github.com/meshtastic/arduino-fsm gitBranch=master
|
||||||
@@ -68,7 +70,7 @@ lib_deps =
|
|||||||
# renovate: datasource=git-refs depName=meshtastic-TinyGPSPlus packageName=https://github.com/meshtastic/TinyGPSPlus gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic-TinyGPSPlus packageName=https://github.com/meshtastic/TinyGPSPlus gitBranch=master
|
||||||
https://github.com/meshtastic/TinyGPSPlus/archive/71a82db35f3b973440044c476d4bcdc673b104f4.zip
|
https://github.com/meshtastic/TinyGPSPlus/archive/71a82db35f3b973440044c476d4bcdc673b104f4.zip
|
||||||
# renovate: datasource=git-refs depName=meshtastic-ArduinoThread packageName=https://github.com/meshtastic/ArduinoThread gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic-ArduinoThread packageName=https://github.com/meshtastic/ArduinoThread gitBranch=master
|
||||||
https://github.com/meshtastic/ArduinoThread/archive/7c3ee9e1951551b949763b1f5280f8db1fa4068d.zip
|
https://github.com/meshtastic/ArduinoThread/archive/b841b0415721f1341ea41cccfb4adccfaf951567.zip
|
||||||
# renovate: datasource=custom.pio depName=Nanopb packageName=nanopb/library/Nanopb
|
# renovate: datasource=custom.pio depName=Nanopb packageName=nanopb/library/Nanopb
|
||||||
nanopb/Nanopb@0.4.91
|
nanopb/Nanopb@0.4.91
|
||||||
# renovate: datasource=custom.pio depName=ErriezCRC32 packageName=erriez/library/ErriezCRC32
|
# renovate: datasource=custom.pio depName=ErriezCRC32 packageName=erriez/library/ErriezCRC32
|
||||||
@@ -113,18 +115,18 @@ lib_deps =
|
|||||||
[radiolib_base]
|
[radiolib_base]
|
||||||
lib_deps =
|
lib_deps =
|
||||||
# renovate: datasource=custom.pio depName=RadioLib packageName=jgromes/library/RadioLib
|
# renovate: datasource=custom.pio depName=RadioLib packageName=jgromes/library/RadioLib
|
||||||
jgromes/RadioLib@7.2.1
|
jgromes/RadioLib@7.3.0
|
||||||
|
|
||||||
[device-ui_base]
|
[device-ui_base]
|
||||||
lib_deps =
|
lib_deps =
|
||||||
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
|
||||||
https://github.com/meshtastic/device-ui/archive/233d18ef42e9d189f90fdfe621f0cd7edff2d221.zip
|
https://github.com/meshtastic/device-ui/archive/19b7855e9a1d9deff37391659ca7194e4ef57c43.zip
|
||||||
|
|
||||||
; Common libs for environmental measurements in telemetry module
|
; Common libs for environmental measurements in telemetry module
|
||||||
[environmental_base]
|
[environmental_base]
|
||||||
lib_deps =
|
lib_deps =
|
||||||
# renovate: datasource=custom.pio depName=Adafruit BusIO packageName=adafruit/library/Adafruit BusIO
|
# renovate: datasource=custom.pio depName=Adafruit BusIO packageName=adafruit/library/Adafruit BusIO
|
||||||
adafruit/Adafruit BusIO@1.17.2
|
adafruit/Adafruit BusIO@1.17.4
|
||||||
# renovate: datasource=custom.pio depName=Adafruit Unified Sensor packageName=adafruit/library/Adafruit Unified Sensor
|
# renovate: datasource=custom.pio depName=Adafruit Unified Sensor packageName=adafruit/library/Adafruit Unified Sensor
|
||||||
adafruit/Adafruit Unified Sensor@1.1.15
|
adafruit/Adafruit Unified Sensor@1.1.15
|
||||||
# renovate: datasource=custom.pio depName=Adafruit BMP280 packageName=adafruit/library/Adafruit BMP280 Library
|
# renovate: datasource=custom.pio depName=Adafruit BMP280 packageName=adafruit/library/Adafruit BMP280 Library
|
||||||
@@ -162,7 +164,7 @@ lib_deps =
|
|||||||
# renovate: datasource=custom.pio depName=QMC5883L Compass packageName=mprograms/library/QMC5883LCompass
|
# renovate: datasource=custom.pio depName=QMC5883L Compass packageName=mprograms/library/QMC5883LCompass
|
||||||
mprograms/QMC5883LCompass@1.2.3
|
mprograms/QMC5883LCompass@1.2.3
|
||||||
# renovate: datasource=custom.pio depName=DFRobot_RTU packageName=dfrobot/library/DFRobot_RTU
|
# renovate: datasource=custom.pio depName=DFRobot_RTU packageName=dfrobot/library/DFRobot_RTU
|
||||||
dfrobot/DFRobot_RTU@1.0.3
|
dfrobot/DFRobot_RTU@1.0.6
|
||||||
# renovate: datasource=git-refs depName=DFRobot_RainfallSensor packageName=https://github.com/DFRobot/DFRobot_RainfallSensor gitBranch=master
|
# renovate: datasource=git-refs depName=DFRobot_RainfallSensor packageName=https://github.com/DFRobot/DFRobot_RainfallSensor gitBranch=master
|
||||||
https://github.com/DFRobot/DFRobot_RainfallSensor/archive/38fea5e02b40a5430be6dab39a99a6f6347d667e.zip
|
https://github.com/DFRobot/DFRobot_RainfallSensor/archive/38fea5e02b40a5430be6dab39a99a6f6347d667e.zip
|
||||||
# renovate: datasource=custom.pio depName=INA226 packageName=robtillaart/library/INA226
|
# renovate: datasource=custom.pio depName=INA226 packageName=robtillaart/library/INA226
|
||||||
|
|||||||
Submodule protobufs updated: a84657c220...bf149bbdcc
@@ -183,9 +183,9 @@ class AmbientLightingThread : public concurrency::OSThread
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
pixels.show();
|
pixels.show();
|
||||||
LOG_DEBUG("Init NeoPixel Ambient light w/ brightness(current)=%d, red=%d, green=%d, blue=%d",
|
// LOG_DEBUG("Init NeoPixel Ambient light w/ brightness(current)=%d, red=%d, green=%d, blue=%d",
|
||||||
moduleConfig.ambient_lighting.current, moduleConfig.ambient_lighting.red,
|
// moduleConfig.ambient_lighting.current, moduleConfig.ambient_lighting.red,
|
||||||
moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
|
// moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
|
||||||
#endif
|
#endif
|
||||||
#ifdef RGBLED_CA
|
#ifdef RGBLED_CA
|
||||||
analogWrite(RGBLED_RED, 255 - moduleConfig.ambient_lighting.red);
|
analogWrite(RGBLED_RED, 255 - moduleConfig.ambient_lighting.red);
|
||||||
|
|||||||
@@ -11,6 +11,11 @@
|
|||||||
#include <AudioOutputI2S.h>
|
#include <AudioOutputI2S.h>
|
||||||
#include <ESP8266SAM.h>
|
#include <ESP8266SAM.h>
|
||||||
|
|
||||||
|
#ifdef USE_XL9555
|
||||||
|
#include "ExtensionIOXL9555.hpp"
|
||||||
|
extern ExtensionIOXL9555 io;
|
||||||
|
#endif
|
||||||
|
|
||||||
#define AUDIO_THREAD_INTERVAL_MS 100
|
#define AUDIO_THREAD_INTERVAL_MS 100
|
||||||
|
|
||||||
class AudioThread : public concurrency::OSThread
|
class AudioThread : public concurrency::OSThread
|
||||||
@@ -20,12 +25,16 @@ class AudioThread : public concurrency::OSThread
|
|||||||
|
|
||||||
void beginRttl(const void *data, uint32_t len)
|
void beginRttl(const void *data, uint32_t len)
|
||||||
{
|
{
|
||||||
|
#ifdef T_LORA_PAGER
|
||||||
|
io.digitalWrite(EXPANDS_AMP_EN, HIGH);
|
||||||
|
#endif
|
||||||
setCPUFast(true);
|
setCPUFast(true);
|
||||||
rtttlFile = new AudioFileSourcePROGMEM(data, len);
|
rtttlFile = new AudioFileSourcePROGMEM(data, len);
|
||||||
i2sRtttl = new AudioGeneratorRTTTL();
|
i2sRtttl = new AudioGeneratorRTTTL();
|
||||||
i2sRtttl->begin(rtttlFile, audioOut);
|
i2sRtttl->begin(rtttlFile, audioOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Also handles actually playing the RTTTL, needs to be called in loop
|
||||||
bool isPlaying()
|
bool isPlaying()
|
||||||
{
|
{
|
||||||
if (i2sRtttl != nullptr) {
|
if (i2sRtttl != nullptr) {
|
||||||
@@ -45,6 +54,9 @@ class AudioThread : public concurrency::OSThread
|
|||||||
rtttlFile = nullptr;
|
rtttlFile = nullptr;
|
||||||
|
|
||||||
setCPUFast(false);
|
setCPUFast(false);
|
||||||
|
#ifdef T_LORA_PAGER
|
||||||
|
io.digitalWrite(EXPANDS_AMP_EN, LOW);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void readAloud(const char *text)
|
void readAloud(const char *text)
|
||||||
@@ -55,10 +67,16 @@ class AudioThread : public concurrency::OSThread
|
|||||||
i2sRtttl = nullptr;
|
i2sRtttl = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef T_LORA_PAGER
|
||||||
|
io.digitalWrite(EXPANDS_AMP_EN, HIGH);
|
||||||
|
#endif
|
||||||
ESP8266SAM *sam = new ESP8266SAM;
|
ESP8266SAM *sam = new ESP8266SAM;
|
||||||
sam->Say(audioOut, text);
|
sam->Say(audioOut, text);
|
||||||
delete sam;
|
delete sam;
|
||||||
setCPUFast(false);
|
setCPUFast(false);
|
||||||
|
#ifdef T_LORA_PAGER
|
||||||
|
io.digitalWrite(EXPANDS_AMP_EN, LOW);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -2,6 +2,12 @@
|
|||||||
|
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
#if defined(DEBUG_HEAP)
|
||||||
|
class MemGet;
|
||||||
|
extern MemGet memGet;
|
||||||
|
#endif
|
||||||
|
|
||||||
// DEBUG LED
|
// DEBUG LED
|
||||||
#ifndef LED_STATE_ON
|
#ifndef LED_STATE_ON
|
||||||
#define LED_STATE_ON 1
|
#define LED_STATE_ON 1
|
||||||
@@ -23,6 +29,7 @@
|
|||||||
#define MESHTASTIC_LOG_LEVEL_ERROR "ERROR"
|
#define MESHTASTIC_LOG_LEVEL_ERROR "ERROR"
|
||||||
#define MESHTASTIC_LOG_LEVEL_CRIT "CRIT "
|
#define MESHTASTIC_LOG_LEVEL_CRIT "CRIT "
|
||||||
#define MESHTASTIC_LOG_LEVEL_TRACE "TRACE"
|
#define MESHTASTIC_LOG_LEVEL_TRACE "TRACE"
|
||||||
|
#define MESHTASTIC_LOG_LEVEL_HEAP "HEAP"
|
||||||
|
|
||||||
#include "SerialConsole.h"
|
#include "SerialConsole.h"
|
||||||
|
|
||||||
@@ -62,6 +69,25 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(DEBUG_HEAP)
|
||||||
|
#define LOG_HEAP(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_HEAP, __VA_ARGS__)
|
||||||
|
|
||||||
|
// Macro-based heap debugging
|
||||||
|
#define DEBUG_HEAP_BEFORE auto heapBefore = memGet.getFreeHeap();
|
||||||
|
#define DEBUG_HEAP_AFTER(context, ptr) \
|
||||||
|
do { \
|
||||||
|
auto heapAfter = memGet.getFreeHeap(); \
|
||||||
|
if (heapBefore != heapAfter) { \
|
||||||
|
LOG_HEAP("Alloc in %s pointer 0x%x, size: %u, free: %u", context, ptr, heapBefore - heapAfter, heapAfter); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define LOG_HEAP(...)
|
||||||
|
#define DEBUG_HEAP_BEFORE
|
||||||
|
#define DEBUG_HEAP_AFTER(context, ptr)
|
||||||
|
#endif
|
||||||
|
|
||||||
/// A C wrapper for LOG_DEBUG that can be used from arduino C libs that don't know about C++ or meshtastic
|
/// A C wrapper for LOG_DEBUG that can be used from arduino C libs that don't know about C++ or meshtastic
|
||||||
extern "C" void logLegacy(const char *level, const char *fmt, ...);
|
extern "C" void logLegacy(const char *level, const char *fmt, ...);
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,9 @@ const char *DisplayFormatters::getDeviceRole(meshtastic_Config_DeviceConfig_Role
|
|||||||
case meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN:
|
case meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN:
|
||||||
return "Client Hidden";
|
return "Client Hidden";
|
||||||
break;
|
break;
|
||||||
|
case meshtastic_Config_DeviceConfig_Role_CLIENT_BASE:
|
||||||
|
return "Client Base";
|
||||||
|
break;
|
||||||
case meshtastic_Config_DeviceConfig_Role_LOST_AND_FOUND:
|
case meshtastic_Config_DeviceConfig_Role_LOST_AND_FOUND:
|
||||||
return "Lost and Found";
|
return "Lost and Found";
|
||||||
break;
|
break;
|
||||||
@@ -73,9 +76,6 @@ const char *DisplayFormatters::getDeviceRole(meshtastic_Config_DeviceConfig_Role
|
|||||||
case meshtastic_Config_DeviceConfig_Role_ROUTER_LATE:
|
case meshtastic_Config_DeviceConfig_Role_ROUTER_LATE:
|
||||||
return "Router Late";
|
return "Router Late";
|
||||||
break;
|
break;
|
||||||
case meshtastic_Config_DeviceConfig_Role_REPEATER:
|
|
||||||
return "Repeater";
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
return "Unknown";
|
return "Unknown";
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -6,7 +6,5 @@ class DisplayFormatters
|
|||||||
public:
|
public:
|
||||||
static const char *getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName,
|
static const char *getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName,
|
||||||
bool usePreset);
|
bool usePreset);
|
||||||
|
|
||||||
public:
|
|
||||||
static const char *getDeviceRole(meshtastic_Config_DeviceConfig_Role role);
|
static const char *getDeviceRole(meshtastic_Config_DeviceConfig_Role role);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,16 +14,16 @@ class NodeStatus : public Status
|
|||||||
CallbackObserver<NodeStatus, const NodeStatus *> statusObserver =
|
CallbackObserver<NodeStatus, const NodeStatus *> statusObserver =
|
||||||
CallbackObserver<NodeStatus, const NodeStatus *>(this, &NodeStatus::updateStatus);
|
CallbackObserver<NodeStatus, const NodeStatus *>(this, &NodeStatus::updateStatus);
|
||||||
|
|
||||||
uint8_t numOnline = 0;
|
uint16_t numOnline = 0;
|
||||||
uint8_t numTotal = 0;
|
uint16_t numTotal = 0;
|
||||||
|
|
||||||
uint8_t lastNumTotal = 0;
|
uint16_t lastNumTotal = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool forceUpdate = false;
|
bool forceUpdate = false;
|
||||||
|
|
||||||
NodeStatus() { statusType = STATUS_TYPE_NODE; }
|
NodeStatus() { statusType = STATUS_TYPE_NODE; }
|
||||||
NodeStatus(uint8_t numOnline, uint8_t numTotal, bool forceUpdate = false) : Status()
|
NodeStatus(uint16_t numOnline, uint16_t numTotal, bool forceUpdate = false) : Status()
|
||||||
{
|
{
|
||||||
this->forceUpdate = forceUpdate;
|
this->forceUpdate = forceUpdate;
|
||||||
this->numOnline = numOnline;
|
this->numOnline = numOnline;
|
||||||
@@ -34,11 +34,11 @@ class NodeStatus : public Status
|
|||||||
|
|
||||||
void observe(Observable<const NodeStatus *> *source) { statusObserver.observe(source); }
|
void observe(Observable<const NodeStatus *> *source) { statusObserver.observe(source); }
|
||||||
|
|
||||||
uint8_t getNumOnline() const { return numOnline; }
|
uint16_t getNumOnline() const { return numOnline; }
|
||||||
|
|
||||||
uint8_t getNumTotal() const { return numTotal; }
|
uint16_t getNumTotal() const { return numTotal; }
|
||||||
|
|
||||||
uint8_t getLastNumTotal() const { return lastNumTotal; }
|
uint16_t getLastNumTotal() const { return lastNumTotal; }
|
||||||
|
|
||||||
bool matches(const NodeStatus *newStatus) const
|
bool matches(const NodeStatus *newStatus) const
|
||||||
{
|
{
|
||||||
@@ -56,7 +56,7 @@ class NodeStatus : public Status
|
|||||||
numTotal = newStatus->getNumTotal();
|
numTotal = newStatus->getNumTotal();
|
||||||
}
|
}
|
||||||
if (isDirty || newStatus->forceUpdate) {
|
if (isDirty || newStatus->forceUpdate) {
|
||||||
LOG_DEBUG("Node status update: %d online, %d total", numOnline, numTotal);
|
LOG_DEBUG("Node status update: %u online, %u total", numOnline, numTotal);
|
||||||
onNewStatus.notifyObservers(this);
|
onNewStatus.notifyObservers(this);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -562,6 +562,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
|||||||
config.power.device_battery_ina_address) {
|
config.power.device_battery_ina_address) {
|
||||||
if (!ina226Sensor.isInitialized())
|
if (!ina226Sensor.isInitialized())
|
||||||
return ina226Sensor.runOnce() > 0;
|
return ina226Sensor.runOnce() > 0;
|
||||||
|
return ina226Sensor.isRunning();
|
||||||
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260].first ==
|
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260].first ==
|
||||||
config.power.device_battery_ina_address) {
|
config.power.device_battery_ina_address) {
|
||||||
if (!ina260Sensor.isInitialized())
|
if (!ina260Sensor.isInitialized())
|
||||||
@@ -691,6 +692,16 @@ bool Power::setup()
|
|||||||
#ifdef NRF_APM
|
#ifdef NRF_APM
|
||||||
found = true;
|
found = true;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef EXT_PWR_DETECT
|
||||||
|
attachInterrupt(
|
||||||
|
EXT_PWR_DETECT,
|
||||||
|
[]() {
|
||||||
|
power->setIntervalFromNow(0);
|
||||||
|
runASAP = true;
|
||||||
|
BaseType_t higherWake = 0;
|
||||||
|
},
|
||||||
|
CHANGE);
|
||||||
|
#endif
|
||||||
|
|
||||||
enabled = found;
|
enabled = found;
|
||||||
low_voltage_counter = 0;
|
low_voltage_counter = 0;
|
||||||
@@ -851,9 +862,9 @@ void Power::readPowerStatus()
|
|||||||
running++;
|
running++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LOG_DEBUG(threadlist);
|
LOG_HEAP(threadlist);
|
||||||
LOG_DEBUG("Heap status: %d/%d bytes free (%d), running %d/%d threads", memGet.getFreeHeap(), memGet.getHeapSize(),
|
LOG_HEAP("Heap status: %d/%d bytes free (%d), running %d/%d threads", memGet.getFreeHeap(), memGet.getHeapSize(),
|
||||||
memGet.getFreeHeap() - lastheap, running, concurrency::mainController.size(false));
|
memGet.getFreeHeap() - lastheap, running, concurrency::mainController.size(false));
|
||||||
lastheap = memGet.getFreeHeap();
|
lastheap = memGet.getFreeHeap();
|
||||||
}
|
}
|
||||||
#ifdef DEBUG_HEAP_MQTT
|
#ifdef DEBUG_HEAP_MQTT
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "concurrency/OSThread.h"
|
#include "concurrency/OSThread.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
#include "memGet.h"
|
||||||
#include "mesh/generated/meshtastic/mesh.pb.h"
|
#include "mesh/generated/meshtastic/mesh.pb.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@@ -166,6 +167,16 @@ void RedirectablePrint::log_to_serial(const char *logLevel, const char *format,
|
|||||||
print(thread->ThreadName);
|
print(thread->ThreadName);
|
||||||
print("] ");
|
print("] ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_HEAP
|
||||||
|
// Add heap free space bytes prefix before every log message
|
||||||
|
#ifdef ARCH_PORTDUINO
|
||||||
|
::printf("[heap %u] ", memGet.getFreeHeap());
|
||||||
|
#else
|
||||||
|
printf("[heap %u] ", memGet.getFreeHeap());
|
||||||
|
#endif
|
||||||
|
#endif // DEBUG_HEAP
|
||||||
|
|
||||||
r += vprintf(logLevel, format, arg);
|
r += vprintf(logLevel, format, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,14 @@
|
|||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "time.h"
|
#include "time.h"
|
||||||
|
|
||||||
|
#if defined(ARDUINO_USB_CDC_ON_BOOT) && ARDUINO_USB_CDC_ON_BOOT
|
||||||
|
#define IS_USB_SERIAL
|
||||||
|
#ifdef SERIAL_HAS_ON_RECEIVE
|
||||||
|
#undef SERIAL_HAS_ON_RECEIVE
|
||||||
|
#endif
|
||||||
|
#include "HWCDC.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef RP2040_SLOW_CLOCK
|
#ifdef RP2040_SLOW_CLOCK
|
||||||
#define Port Serial2
|
#define Port Serial2
|
||||||
#else
|
#else
|
||||||
@@ -22,7 +30,12 @@ SerialConsole *console;
|
|||||||
|
|
||||||
void consoleInit()
|
void consoleInit()
|
||||||
{
|
{
|
||||||
new SerialConsole(); // Must be dynamically allocated because we are now inheriting from thread
|
auto sc = new SerialConsole(); // Must be dynamically allocated because we are now inheriting from thread
|
||||||
|
|
||||||
|
#if defined(SERIAL_HAS_ON_RECEIVE)
|
||||||
|
// onReceive does only exist for HardwareSerial not for USB CDC serial
|
||||||
|
Port.onReceive([sc]() { sc->rxInt(); });
|
||||||
|
#endif
|
||||||
DEBUG_PORT.rpInit(); // Simply sets up semaphore
|
DEBUG_PORT.rpInit(); // Simply sets up semaphore
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,14 +78,21 @@ SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port), con
|
|||||||
int32_t SerialConsole::runOnce()
|
int32_t SerialConsole::runOnce()
|
||||||
{
|
{
|
||||||
#ifdef HELTEC_MESH_SOLAR
|
#ifdef HELTEC_MESH_SOLAR
|
||||||
//After enabling the mesh solar serial port module configuration, command processing is handled by the serial port module.
|
// After enabling the mesh solar serial port module configuration, command processing is handled by the serial port module.
|
||||||
if(moduleConfig.serial.enabled && moduleConfig.serial.override_console_serial_port
|
if (moduleConfig.serial.enabled && moduleConfig.serial.override_console_serial_port &&
|
||||||
&& moduleConfig.serial.mode==meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MS_CONFIG)
|
moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MS_CONFIG) {
|
||||||
{
|
|
||||||
return 250;
|
return 250;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return runOncePart();
|
|
||||||
|
int32_t delay = runOncePart();
|
||||||
|
#if defined(SERIAL_HAS_ON_RECEIVE) || defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||||
|
return Port.available() ? delay : INT32_MAX;
|
||||||
|
#elif defined(IS_USB_SERIAL)
|
||||||
|
return HWCDC::isPlugged() ? delay : (1000 * 20);
|
||||||
|
#else
|
||||||
|
return delay;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void SerialConsole::flush()
|
void SerialConsole::flush()
|
||||||
@@ -80,6 +100,18 @@ void SerialConsole::flush()
|
|||||||
Port.flush();
|
Port.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// trigger tx of serial data
|
||||||
|
void SerialConsole::onNowHasData(uint32_t fromRadioNum)
|
||||||
|
{
|
||||||
|
setIntervalFromNow(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// trigger rx of serial data
|
||||||
|
void SerialConsole::rxInt()
|
||||||
|
{
|
||||||
|
setIntervalFromNow(0);
|
||||||
|
}
|
||||||
|
|
||||||
// For the serial port we can't really detect if any client is on the other side, so instead just look for recent messages
|
// For the serial port we can't really detect if any client is on the other side, so instead just look for recent messages
|
||||||
bool SerialConsole::checkIsConnected()
|
bool SerialConsole::checkIsConnected()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -32,11 +32,14 @@ class SerialConsole : public StreamAPI, public RedirectablePrint, private concur
|
|||||||
virtual int32_t runOnce() override;
|
virtual int32_t runOnce() override;
|
||||||
|
|
||||||
void flush();
|
void flush();
|
||||||
|
void rxInt();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// Check the current underlying physical link to see if the client is currently connected
|
/// Check the current underlying physical link to see if the client is currently connected
|
||||||
virtual bool checkIsConnected() override;
|
virtual bool checkIsConnected() override;
|
||||||
|
|
||||||
|
virtual void onNowHasData(uint32_t fromRadioNum) override;
|
||||||
|
|
||||||
/// Possibly switch to protobufs if we see a valid protobuf message
|
/// Possibly switch to protobufs if we see a valid protobuf message
|
||||||
virtual void log_to_serial(const char *logLevel, const char *format, va_list arg);
|
virtual void log_to_serial(const char *logLevel, const char *format, va_list arg);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
BuzzerFeedbackThread *buzzerFeedbackThread;
|
BuzzerFeedbackThread *buzzerFeedbackThread;
|
||||||
|
|
||||||
BuzzerFeedbackThread::BuzzerFeedbackThread() : OSThread("BuzzerFeedback")
|
BuzzerFeedbackThread::BuzzerFeedbackThread()
|
||||||
{
|
{
|
||||||
if (inputBroker)
|
if (inputBroker)
|
||||||
inputObserver.observe(inputBroker);
|
inputObserver.observe(inputBroker);
|
||||||
@@ -15,14 +15,11 @@ int BuzzerFeedbackThread::handleInputEvent(const InputEvent *event)
|
|||||||
{
|
{
|
||||||
// Only provide feedback if buzzer is enabled for notifications
|
// Only provide feedback if buzzer is enabled for notifications
|
||||||
if (config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_DISABLED ||
|
if (config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_DISABLED ||
|
||||||
config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_NOTIFICATIONS_ONLY) {
|
config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_NOTIFICATIONS_ONLY ||
|
||||||
|
config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY) {
|
||||||
return 0; // Let other handlers process the event
|
return 0; // Let other handlers process the event
|
||||||
}
|
}
|
||||||
|
|
||||||
// Track last event time for potential future use
|
|
||||||
lastEventTime = millis();
|
|
||||||
needsUpdate = true;
|
|
||||||
|
|
||||||
// Handle different input events with appropriate buzzer feedback
|
// Handle different input events with appropriate buzzer feedback
|
||||||
switch (event->inputEvent) {
|
switch (event->inputEvent) {
|
||||||
case INPUT_BROKER_USER_PRESS:
|
case INPUT_BROKER_USER_PRESS:
|
||||||
@@ -61,15 +58,4 @@ int BuzzerFeedbackThread::handleInputEvent(const InputEvent *event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return 0; // Allow other handlers to process the event
|
return 0; // Allow other handlers to process the event
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t BuzzerFeedbackThread::runOnce()
|
|
||||||
{
|
|
||||||
// This thread is primarily event-driven, but we can use runOnce
|
|
||||||
// for any periodic tasks if needed in the future
|
|
||||||
|
|
||||||
needsUpdate = false;
|
|
||||||
|
|
||||||
// Run every 100ms when active, less frequently when idle
|
|
||||||
return needsUpdate ? 100 : 1000;
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
#include "concurrency/OSThread.h"
|
#include "concurrency/OSThread.h"
|
||||||
#include "input/InputBroker.h"
|
#include "input/InputBroker.h"
|
||||||
|
|
||||||
class BuzzerFeedbackThread : public concurrency::OSThread
|
class BuzzerFeedbackThread
|
||||||
{
|
{
|
||||||
CallbackObserver<BuzzerFeedbackThread, const InputEvent *> inputObserver =
|
CallbackObserver<BuzzerFeedbackThread, const InputEvent *> inputObserver =
|
||||||
CallbackObserver<BuzzerFeedbackThread, const InputEvent *>(this, &BuzzerFeedbackThread::handleInputEvent);
|
CallbackObserver<BuzzerFeedbackThread, const InputEvent *>(this, &BuzzerFeedbackThread::handleInputEvent);
|
||||||
@@ -12,13 +12,6 @@ class BuzzerFeedbackThread : public concurrency::OSThread
|
|||||||
public:
|
public:
|
||||||
BuzzerFeedbackThread();
|
BuzzerFeedbackThread();
|
||||||
int handleInputEvent(const InputEvent *event);
|
int handleInputEvent(const InputEvent *event);
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual int32_t runOnce() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint32_t lastEventTime = 0;
|
|
||||||
bool needsUpdate = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern BuzzerFeedbackThread *buzzerFeedbackThread;
|
extern BuzzerFeedbackThread *buzzerFeedbackThread;
|
||||||
|
|||||||
@@ -86,11 +86,13 @@ void OSThread::run()
|
|||||||
#ifdef DEBUG_HEAP
|
#ifdef DEBUG_HEAP
|
||||||
auto newHeap = memGet.getFreeHeap();
|
auto newHeap = memGet.getFreeHeap();
|
||||||
if (newHeap < heap)
|
if (newHeap < heap)
|
||||||
LOG_DEBUG("------ Thread %s leaked heap %d -> %d (%d) ------", ThreadName.c_str(), heap, newHeap, newHeap - heap);
|
LOG_HEAP("------ Thread %s leaked heap %d -> %d (%d) ------", ThreadName.c_str(), heap, newHeap, newHeap - heap);
|
||||||
if (heap < newHeap)
|
if (heap < newHeap)
|
||||||
LOG_DEBUG("++++++ Thread %s freed heap %d -> %d (%d) ++++++", ThreadName.c_str(), heap, newHeap, newHeap - heap);
|
LOG_HEAP("++++++ Thread %s freed heap %d -> %d (%d) ++++++", ThreadName.c_str(), heap, newHeap, newHeap - heap);
|
||||||
|
#endif
|
||||||
|
#ifdef DEBUG_LOOP_TIMING
|
||||||
|
LOG_DEBUG("====== Thread next run in: %d", newDelay);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
runned();
|
runned();
|
||||||
|
|
||||||
if (newDelay >= 0)
|
if (newDelay >= 0)
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#include "pcf8563.h"
|
#include "pcf8563.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Offer chance for variant-specific defines */
|
||||||
|
#include "variant.h"
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Version
|
// Version
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@@ -117,6 +120,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#define SX126X_MAX_POWER 22
|
#define SX126X_MAX_POWER 22
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_GC1109_PA
|
||||||
|
// Power Amps are often non-linear, so we can use an array of values for the power curve
|
||||||
|
#define NUM_PA_POINTS 22
|
||||||
|
#define TX_GAIN_LORA 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 9, 9, 8, 7
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RAK13302
|
||||||
|
#define NUM_PA_POINTS 22
|
||||||
|
#define TX_GAIN_LORA 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8
|
||||||
|
#endif
|
||||||
|
|
||||||
// Default system gain to 0 if not defined
|
// Default system gain to 0 if not defined
|
||||||
#ifndef TX_GAIN_LORA
|
#ifndef TX_GAIN_LORA
|
||||||
#define TX_GAIN_LORA 0
|
#define TX_GAIN_LORA 0
|
||||||
@@ -254,14 +268,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
// convert 24-bit color to 16-bit (56K)
|
// convert 24-bit color to 16-bit (56K)
|
||||||
#define COLOR565(r, g, b) (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3))
|
#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"
|
|
||||||
|
|
||||||
#if defined(VEXT_ENABLE) && !defined(VEXT_ON_VALUE)
|
#if defined(VEXT_ENABLE) && !defined(VEXT_ON_VALUE)
|
||||||
// Older variant.h files might not be defining this value, so stay with the old default
|
// Older variant.h files might not be defining this value, so stay with the old default
|
||||||
#define VEXT_ON_VALUE LOW
|
#define VEXT_ON_VALUE LOW
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Rotary encoder
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
#ifndef ROTARY_DELAY
|
||||||
|
#define ROTARY_DELAY 5
|
||||||
|
#endif
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// GPS
|
// GPS
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@@ -431,7 +449,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#define MESHTASTIC_EXCLUDE_SERIAL 1
|
#define MESHTASTIC_EXCLUDE_SERIAL 1
|
||||||
#define MESHTASTIC_EXCLUDE_POWERSTRESS 1
|
#define MESHTASTIC_EXCLUDE_POWERSTRESS 1
|
||||||
#define MESHTASTIC_EXCLUDE_ADMIN 1
|
#define MESHTASTIC_EXCLUDE_ADMIN 1
|
||||||
#define MESHTASTIC_EXCLUDE_AMBIENTLIGHTING 1
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// // Turn off wifi even if HW supports wifi (webserver relies on wifi and is also disabled)
|
// // Turn off wifi even if HW supports wifi (webserver relies on wifi and is also disabled)
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ ScanI2C::FoundDevice ScanI2C::firstScreen() const
|
|||||||
|
|
||||||
ScanI2C::FoundDevice ScanI2C::firstRTC() const
|
ScanI2C::FoundDevice ScanI2C::firstRTC() const
|
||||||
{
|
{
|
||||||
ScanI2C::DeviceType types[] = {RTC_RV3028, RTC_PCF8563};
|
ScanI2C::DeviceType types[] = {RTC_RV3028, RTC_PCF8563, RTC_RX8130CE};
|
||||||
return firstOfOrNONE(2, types);
|
return firstOfOrNONE(3, types);
|
||||||
}
|
}
|
||||||
|
|
||||||
ScanI2C::FoundDevice ScanI2C::firstKeyboard() const
|
ScanI2C::FoundDevice ScanI2C::firstKeyboard() const
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ class ScanI2C
|
|||||||
SCREEN_ST7567,
|
SCREEN_ST7567,
|
||||||
RTC_RV3028,
|
RTC_RV3028,
|
||||||
RTC_PCF8563,
|
RTC_PCF8563,
|
||||||
|
RTC_RX8130CE,
|
||||||
CARDKB,
|
CARDKB,
|
||||||
TDECKKB,
|
TDECKKB,
|
||||||
BBQ10KB,
|
BBQ10KB,
|
||||||
|
|||||||
16
src/detect/ScanI2CConsumer.cpp
Normal file
16
src/detect/ScanI2CConsumer.cpp
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#include "ScanI2CConsumer.h"
|
||||||
|
#include <forward_list>
|
||||||
|
|
||||||
|
static std::forward_list<ScanI2CConsumer *> ScanI2CConsumers;
|
||||||
|
|
||||||
|
ScanI2CConsumer::ScanI2CConsumer()
|
||||||
|
{
|
||||||
|
ScanI2CConsumers.push_front(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScanI2CCompleted(ScanI2C *i2cScanner)
|
||||||
|
{
|
||||||
|
for (ScanI2CConsumer *consumer : ScanI2CConsumers) {
|
||||||
|
consumer->i2cScanFinished(i2cScanner);
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/detect/ScanI2CConsumer.h
Normal file
13
src/detect/ScanI2CConsumer.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ScanI2C.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
class ScanI2CConsumer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ScanI2CConsumer();
|
||||||
|
virtual void i2cScanFinished(ScanI2C *i2cScanner) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ScanI2CCompleted(ScanI2C *i2cScanner);
|
||||||
@@ -197,6 +197,9 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
|||||||
#ifdef PCF8563_RTC
|
#ifdef PCF8563_RTC
|
||||||
SCAN_SIMPLE_CASE(PCF8563_RTC, RTC_PCF8563, "PCF8563", (uint8_t)addr.address)
|
SCAN_SIMPLE_CASE(PCF8563_RTC, RTC_PCF8563, "PCF8563", (uint8_t)addr.address)
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef RX8130CE_RTC
|
||||||
|
SCAN_SIMPLE_CASE(RX8130CE_RTC, RTC_RX8130CE, "RX8130CE", (uint8_t)addr.address)
|
||||||
|
#endif
|
||||||
|
|
||||||
case CARDKB_ADDR:
|
case CARDKB_ADDR:
|
||||||
// Do we have the RAK14006 instead?
|
// Do we have the RAK14006 instead?
|
||||||
@@ -294,6 +297,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
|||||||
type = AHT10;
|
type = AHT10;
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
#if !defined(M5STACK_UNITC6L)
|
||||||
case INA_ADDR:
|
case INA_ADDR:
|
||||||
case INA_ADDR_ALTERNATE:
|
case INA_ADDR_ALTERNATE:
|
||||||
case INA_ADDR_WAVESHARE_UPS:
|
case INA_ADDR_WAVESHARE_UPS:
|
||||||
@@ -340,6 +344,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
|||||||
// else: probably a RAK12500/UBLOX GPS on I2C
|
// else: probably a RAK12500/UBLOX GPS on I2C
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
case MCP9808_ADDR:
|
case MCP9808_ADDR:
|
||||||
// We need to check for STK8BAXX first, since register 0x07 is new data flag for the z-axis and can produce some
|
// We need to check for STK8BAXX first, since register 0x07 is new data flag for the z-axis and can produce some
|
||||||
// weird result. and register 0x00 doesn't seems to be colliding with MCP9808 and LIS3DH chips.
|
// weird result. and register 0x00 doesn't seems to be colliding with MCP9808 and LIS3DH chips.
|
||||||
@@ -373,7 +378,8 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
|||||||
case SHT31_4x_ADDR: // same as OPT3001_ADDR_ALT
|
case SHT31_4x_ADDR: // same as OPT3001_ADDR_ALT
|
||||||
case SHT31_4x_ADDR_ALT: // same as OPT3001_ADDR
|
case SHT31_4x_ADDR_ALT: // same as OPT3001_ADDR
|
||||||
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x89), 2);
|
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x89), 2);
|
||||||
if (registerValue == 0x11a2 || registerValue == 0x11da || registerValue == 0xe9c || registerValue == 0xc8d) {
|
if (registerValue == 0x11a2 || registerValue == 0x11da || registerValue == 0x11f3 || registerValue == 0xe9c ||
|
||||||
|
registerValue == 0xc8d) {
|
||||||
type = SHT4X;
|
type = SHT4X;
|
||||||
logFoundDevice("SHT4X", (uint8_t)addr.address);
|
logFoundDevice("SHT4X", (uint8_t)addr.address);
|
||||||
} else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2) == 0x5449) {
|
} else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2) == 0x5449) {
|
||||||
@@ -575,7 +581,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port)
|
|||||||
scanPort(port, nullptr, 0);
|
scanPort(port, nullptr, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TwoWire *ScanI2CTwoWire::fetchI2CBus(ScanI2C::DeviceAddress address) const
|
TwoWire *ScanI2CTwoWire::fetchI2CBus(ScanI2C::DeviceAddress address)
|
||||||
{
|
{
|
||||||
if (address.port == ScanI2C::I2CPort::WIRE) {
|
if (address.port == ScanI2C::I2CPort::WIRE) {
|
||||||
return &Wire;
|
return &Wire;
|
||||||
|
|||||||
@@ -23,12 +23,12 @@ class ScanI2CTwoWire : public ScanI2C
|
|||||||
|
|
||||||
ScanI2C::FoundDevice find(ScanI2C::DeviceType) const override;
|
ScanI2C::FoundDevice find(ScanI2C::DeviceType) const override;
|
||||||
|
|
||||||
TwoWire *fetchI2CBus(ScanI2C::DeviceAddress) const;
|
|
||||||
|
|
||||||
bool exists(ScanI2C::DeviceType) const override;
|
bool exists(ScanI2C::DeviceType) const override;
|
||||||
|
|
||||||
size_t countDevices() const override;
|
size_t countDevices() const override;
|
||||||
|
|
||||||
|
static TwoWire *fetchI2CBus(ScanI2C::DeviceAddress);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
FoundDevice firstOfOrNONE(size_t, DeviceType[]) const override;
|
FoundDevice firstOfOrNONE(size_t, DeviceType[]) const override;
|
||||||
|
|
||||||
|
|||||||
534
src/gps/GPS.cpp
534
src/gps/GPS.cpp
@@ -1,5 +1,4 @@
|
|||||||
#include <cstring> // Include for strstr
|
#include <cstring> // Include for strstr
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
@@ -241,6 +240,9 @@ GPS_RESPONSE GPS::getACK(const char *message, uint32_t waitMillis)
|
|||||||
buffer[bytesRead] = b;
|
buffer[bytesRead] = b;
|
||||||
bytesRead++;
|
bytesRead++;
|
||||||
if ((bytesRead == 767) || (b == '\r')) {
|
if ((bytesRead == 767) || (b == '\r')) {
|
||||||
|
#ifdef GPS_DEBUG
|
||||||
|
LOG_DEBUG(debugmsg.c_str());
|
||||||
|
#endif
|
||||||
if (strnstr((char *)buffer, message, bytesRead) != nullptr) {
|
if (strnstr((char *)buffer, message, bytesRead) != nullptr) {
|
||||||
#ifdef GPS_DEBUG
|
#ifdef GPS_DEBUG
|
||||||
LOG_DEBUG("Found: %s", message); // Log the found message
|
LOG_DEBUG("Found: %s", message); // Log the found message
|
||||||
@@ -248,9 +250,6 @@ GPS_RESPONSE GPS::getACK(const char *message, uint32_t waitMillis)
|
|||||||
return GNSS_RESPONSE_OK;
|
return GNSS_RESPONSE_OK;
|
||||||
} else {
|
} else {
|
||||||
bytesRead = 0;
|
bytesRead = 0;
|
||||||
#ifdef GPS_DEBUG
|
|
||||||
LOG_DEBUG(debugmsg.c_str());
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -495,38 +494,27 @@ bool GPS::setup()
|
|||||||
if (!didSerialInit) {
|
if (!didSerialInit) {
|
||||||
int msglen = 0;
|
int msglen = 0;
|
||||||
if (tx_gpio && gnssModel == GNSS_MODEL_UNKNOWN) {
|
if (tx_gpio && gnssModel == GNSS_MODEL_UNKNOWN) {
|
||||||
#ifdef TRACKER_T1000_E
|
|
||||||
// add power up/down strategy, improve ag3335 detection success
|
|
||||||
digitalWrite(PIN_GPS_EN, LOW);
|
|
||||||
delay(500);
|
|
||||||
digitalWrite(GPS_VRTC_EN, LOW);
|
|
||||||
delay(1000);
|
|
||||||
digitalWrite(GPS_VRTC_EN, HIGH);
|
|
||||||
delay(500);
|
|
||||||
digitalWrite(PIN_GPS_EN, HIGH);
|
|
||||||
delay(1000);
|
|
||||||
#endif
|
|
||||||
if (probeTries < GPS_PROBETRIES) {
|
if (probeTries < GPS_PROBETRIES) {
|
||||||
LOG_DEBUG("Probe for GPS at %d", serialSpeeds[speedSelect]);
|
|
||||||
gnssModel = probe(serialSpeeds[speedSelect]);
|
gnssModel = probe(serialSpeeds[speedSelect]);
|
||||||
if (gnssModel == GNSS_MODEL_UNKNOWN) {
|
if (gnssModel == GNSS_MODEL_UNKNOWN) {
|
||||||
if (++speedSelect == array_count(serialSpeeds)) {
|
if (currentStep == 0 && ++speedSelect == array_count(serialSpeeds)) {
|
||||||
speedSelect = 0;
|
speedSelect = 0;
|
||||||
++probeTries;
|
++probeTries;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Rare Serial Speeds
|
// Rare Serial Speeds
|
||||||
|
#ifndef CONFIG_IDF_TARGET_ESP32C6
|
||||||
if (probeTries == GPS_PROBETRIES) {
|
if (probeTries == GPS_PROBETRIES) {
|
||||||
LOG_DEBUG("Probe for GPS at %d", rareSerialSpeeds[speedSelect]);
|
|
||||||
gnssModel = probe(rareSerialSpeeds[speedSelect]);
|
gnssModel = probe(rareSerialSpeeds[speedSelect]);
|
||||||
if (gnssModel == GNSS_MODEL_UNKNOWN) {
|
if (gnssModel == GNSS_MODEL_UNKNOWN) {
|
||||||
if (++speedSelect == array_count(rareSerialSpeeds)) {
|
if (currentStep == 0 && ++speedSelect == array_count(rareSerialSpeeds)) {
|
||||||
LOG_WARN("Give up on GPS probe and set to %d", GPS_BAUDRATE);
|
LOG_WARN("Give up on GPS probe and set to %d", GPS_BAUDRATE);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gnssModel != GNSS_MODEL_UNKNOWN) {
|
if (gnssModel != GNSS_MODEL_UNKNOWN) {
|
||||||
@@ -1032,7 +1020,7 @@ void GPS::down()
|
|||||||
LOG_DEBUG("%us until next search", sleepTime / 1000);
|
LOG_DEBUG("%us until next search", sleepTime / 1000);
|
||||||
|
|
||||||
// If update interval less than 10 seconds, no attempt to sleep
|
// If update interval less than 10 seconds, no attempt to sleep
|
||||||
if (updateInterval <= 10 * 1000UL || sleepTime == 0)
|
if (updateInterval <= GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS || sleepTime == 0)
|
||||||
setPowerState(GPS_IDLE);
|
setPowerState(GPS_IDLE);
|
||||||
|
|
||||||
else {
|
else {
|
||||||
@@ -1093,7 +1081,7 @@ int32_t GPS::runOnce()
|
|||||||
return disable();
|
return disable();
|
||||||
}
|
}
|
||||||
if (!setup())
|
if (!setup())
|
||||||
return 2000; // Setup failed, re-run in two seconds
|
return currentDelay; // Setup failed, re-run in two seconds
|
||||||
|
|
||||||
// We have now loaded our saved preferences from flash
|
// We have now loaded our saved preferences from flash
|
||||||
if (config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
|
if (config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
|
||||||
@@ -1103,11 +1091,29 @@ int32_t GPS::runOnce()
|
|||||||
publishUpdate();
|
publishUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Repeaters have no need for GPS
|
// ======================== GPS_ACTIVE state ========================
|
||||||
if (config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) {
|
// In GPS_ACTIVE state, GPS is powered on and we're receiving NMEA messages.
|
||||||
return disable();
|
// We use the following logic to determine when to update the local position
|
||||||
}
|
// or time by running GPS::publishUpdate.
|
||||||
|
// Note: Local position update is asynchronous to position broadcast. We
|
||||||
|
// generally run this state every gps_update_interval seconds, and in most cases
|
||||||
|
// gps_update_interval is faster than the position broadcast interval so there's a
|
||||||
|
// fresh position ready when the device wants to broadcast one on the mesh.
|
||||||
|
//
|
||||||
|
// 1. Got a time for the first time --> set the time, don't publish.
|
||||||
|
// 2. Got a lock for the first time
|
||||||
|
// --> If gps_update_interval is <= 10s --> publishUpdate
|
||||||
|
// --> Otherwise, hold for MIN(gps_update_interval - GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS, 20s)
|
||||||
|
// 3. Got a lock after turning back on
|
||||||
|
// --> If gps_update_interval is <= 10s --> publishUpdate
|
||||||
|
// --> Otherwise, hold for MIN(gps_update_interval - GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS, 20s)
|
||||||
|
// 4. Hold has expired
|
||||||
|
// --> If we have a time and a location --> publishUpdate
|
||||||
|
// --> down()
|
||||||
|
// 5. Search time has expired
|
||||||
|
// --> If we have a time and a location --> publishUpdate
|
||||||
|
// --> If we had a location before but don't now --> publishUpdate
|
||||||
|
// --> down()
|
||||||
if (whileActive()) {
|
if (whileActive()) {
|
||||||
// if we have received valid NMEA claim we are connected
|
// if we have received valid NMEA claim we are connected
|
||||||
setConnected();
|
setConnected();
|
||||||
@@ -1117,55 +1123,81 @@ int32_t GPS::runOnce()
|
|||||||
if (!config.position.fixed_position && powerState != GPS_ACTIVE && scheduling.isUpdateDue())
|
if (!config.position.fixed_position && powerState != GPS_ACTIVE && scheduling.isUpdateDue())
|
||||||
up();
|
up();
|
||||||
|
|
||||||
// If we've already set time from the GPS, no need to ask the GPS
|
// quality of the previous fix. We set it to 0 when we go down, so it's a way
|
||||||
bool gotTime = (getRTCQuality() >= RTCQualityGPS);
|
// to check if we're getting a lock after being GPS_OFF.
|
||||||
if (!gotTime && lookForTime()) { // Note: we count on this && short-circuiting and not resetting the RTC time
|
|
||||||
gotTime = true;
|
|
||||||
shouldPublish = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t prev_fixQual = fixQual;
|
uint8_t prev_fixQual = fixQual;
|
||||||
bool gotLoc = lookForLocation();
|
|
||||||
if (gotLoc && !hasValidLocation) { // declare that we have location ASAP
|
|
||||||
LOG_DEBUG("hasValidLocation RISING EDGE");
|
|
||||||
hasValidLocation = true;
|
|
||||||
shouldPublish = true;
|
|
||||||
// Hold for 20secs after getting a lock to download ephemeris etc
|
|
||||||
fixHoldEnds = millis() + 20000;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gotLoc && prev_fixQual == 0) { // just got a lock after turning back on.
|
if (powerState == GPS_ACTIVE) {
|
||||||
fixHoldEnds = millis() + 20000;
|
// if gps_update_interval is <=10s, GPS never goes off, so we treat that differently
|
||||||
shouldPublish = true; // Publish immediately, since next publish is at end of hold
|
uint32_t updateInterval = Default::getConfiguredOrDefaultMs(config.position.gps_update_interval);
|
||||||
}
|
|
||||||
|
|
||||||
bool tooLong = scheduling.searchedTooLong();
|
// 1. Got a time for the first time
|
||||||
if (tooLong)
|
bool gotTime = (getRTCQuality() >= RTCQualityGPS);
|
||||||
LOG_WARN("Couldn't publish a valid location: didn't get a GPS lock in time");
|
if (!gotTime && lookForTime()) { // Note: we count on this && short-circuiting and not resetting the RTC time
|
||||||
|
gotTime = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Once we get a location we no longer desperately want an update
|
// 2. Got a lock for the first time, or 3. Got a lock after turning back on
|
||||||
if ((gotLoc && gotTime) || tooLong) {
|
bool gotLoc = lookForLocation();
|
||||||
|
if (gotLoc) {
|
||||||
|
#ifdef GPS_DEBUG
|
||||||
|
if (!hasValidLocation) { // declare that we have location ASAP
|
||||||
|
LOG_DEBUG("hasValidLocation RISING EDGE");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (updateInterval <= GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS) {
|
||||||
|
hasValidLocation = true;
|
||||||
|
shouldPublish = true;
|
||||||
|
} else if (!hasValidLocation || prev_fixQual == 0 || (fixHoldEnds + GPS_THREAD_INTERVAL) < millis()) {
|
||||||
|
hasValidLocation = true;
|
||||||
|
// Hold for up to 20secs after getting a lock to download ephemeris etc
|
||||||
|
uint32_t holdTime = updateInterval - GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS;
|
||||||
|
if (holdTime > GPS_FIX_HOLD_MAX_MS)
|
||||||
|
holdTime = GPS_FIX_HOLD_MAX_MS;
|
||||||
|
fixHoldEnds = millis() + holdTime;
|
||||||
|
#ifdef GPS_DEBUG
|
||||||
|
LOG_DEBUG("Holding for %ums after lock", holdTime);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tooLong = scheduling.searchedTooLong();
|
||||||
if (tooLong && !gotLoc) {
|
if (tooLong && !gotLoc) {
|
||||||
|
LOG_WARN("Couldn't publish a valid location: didn't get a GPS lock in time");
|
||||||
// we didn't get a location during this ack window, therefore declare loss of lock
|
// we didn't get a location during this ack window, therefore declare loss of lock
|
||||||
if (hasValidLocation) {
|
if (hasValidLocation) {
|
||||||
LOG_DEBUG("hasValidLocation FALLING EDGE");
|
p = meshtastic_Position_init_default;
|
||||||
}
|
hasValidLocation = false;
|
||||||
p = meshtastic_Position_init_default;
|
shouldPublish = true;
|
||||||
hasValidLocation = false;
|
|
||||||
}
|
|
||||||
if (millis() > fixHoldEnds) {
|
|
||||||
shouldPublish = true; // publish our update at the end of the lock hold
|
|
||||||
publishUpdate();
|
|
||||||
down();
|
|
||||||
#ifdef GPS_DEBUG
|
#ifdef GPS_DEBUG
|
||||||
} else {
|
LOG_DEBUG("hasValidLocation FALLING EDGE");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hold has expired , Search time has expired, we got a time only, or we never needed to hold.
|
||||||
|
bool holdExpired = (fixHoldEnds != 0 && millis() > fixHoldEnds);
|
||||||
|
if (shouldPublish || tooLong || holdExpired) {
|
||||||
|
if (gotTime && hasValidLocation) {
|
||||||
|
shouldPublish = true;
|
||||||
|
}
|
||||||
|
if (shouldPublish) {
|
||||||
|
fixHoldEnds = 0;
|
||||||
|
publishUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// There's a chance we just got a time, so keep going to see if we can get a location too
|
||||||
|
if (tooLong || holdExpired) {
|
||||||
|
down();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef GPS_DEBUG
|
||||||
|
} else if (fixHoldEnds != 0) {
|
||||||
LOG_DEBUG("Holding for GPS data download: %d ms (numSats=%d)", fixHoldEnds - millis(), p.sats_in_view);
|
LOG_DEBUG("Holding for GPS data download: %d ms (numSats=%d)", fixHoldEnds - millis(), p.sats_in_view);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ===================== end GPS_ACTIVE state ========================
|
||||||
// If state has changed do a publish
|
|
||||||
publishUpdate();
|
|
||||||
|
|
||||||
if (config.position.fixed_position == true && hasValidLocation)
|
if (config.position.fixed_position == true && hasValidLocation)
|
||||||
return disable(); // This should trigger when we have a fixed position, and get that first position
|
return disable(); // This should trigger when we have a fixed position, and get that first position
|
||||||
@@ -1214,7 +1246,7 @@ static const char *DETECTED_MESSAGE = "%s detected";
|
|||||||
LOG_DEBUG(PROBE_MESSAGE, COMMAND, FAMILY_NAME); \
|
LOG_DEBUG(PROBE_MESSAGE, COMMAND, FAMILY_NAME); \
|
||||||
clearBuffer(); \
|
clearBuffer(); \
|
||||||
_serial_gps->write(COMMAND "\r\n"); \
|
_serial_gps->write(COMMAND "\r\n"); \
|
||||||
GnssModel_t detectedDriver = getProbeResponse(TIMEOUT, RESPONSE_MAP); \
|
GnssModel_t detectedDriver = getProbeResponse(TIMEOUT, RESPONSE_MAP, serialSpeed); \
|
||||||
if (detectedDriver != GNSS_MODEL_UNKNOWN) { \
|
if (detectedDriver != GNSS_MODEL_UNKNOWN) { \
|
||||||
return detectedDriver; \
|
return detectedDriver; \
|
||||||
} \
|
} \
|
||||||
@@ -1222,196 +1254,270 @@ static const char *DETECTED_MESSAGE = "%s detected";
|
|||||||
|
|
||||||
GnssModel_t GPS::probe(int serialSpeed)
|
GnssModel_t GPS::probe(int serialSpeed)
|
||||||
{
|
{
|
||||||
|
uint8_t buffer[768] = {0};
|
||||||
|
|
||||||
|
switch (currentStep) {
|
||||||
|
case 0: {
|
||||||
#if defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) || defined(ARCH_STM32WL)
|
#if defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) || defined(ARCH_STM32WL)
|
||||||
_serial_gps->end();
|
_serial_gps->end();
|
||||||
_serial_gps->begin(serialSpeed);
|
_serial_gps->begin(serialSpeed);
|
||||||
#elif defined(ARCH_RP2040)
|
#elif defined(ARCH_RP2040)
|
||||||
_serial_gps->end();
|
_serial_gps->end();
|
||||||
_serial_gps->setFIFOSize(256);
|
_serial_gps->setFIFOSize(256);
|
||||||
_serial_gps->begin(serialSpeed);
|
_serial_gps->begin(serialSpeed);
|
||||||
#else
|
#else
|
||||||
if (_serial_gps->baudRate() != serialSpeed) {
|
if (_serial_gps->baudRate() != serialSpeed) {
|
||||||
LOG_DEBUG("Set Baud to %i", serialSpeed);
|
LOG_DEBUG("Set GPS Baud to %i", serialSpeed);
|
||||||
_serial_gps->updateBaudRate(serialSpeed);
|
_serial_gps->updateBaudRate(serialSpeed);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
memset(&ublox_info, 0, sizeof(ublox_info));
|
memset(&ublox_info, 0, sizeof(ublox_info));
|
||||||
uint8_t buffer[768] = {0};
|
delay(100);
|
||||||
delay(100);
|
|
||||||
|
|
||||||
// Close all NMEA sentences, valid for L76K, ATGM336H (and likely other AT6558 devices)
|
#if defined(PIN_GPS_RESET) && PIN_GPS_RESET != -1
|
||||||
_serial_gps->write("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n");
|
digitalWrite(PIN_GPS_RESET, GPS_RESET_MODE); // assert for 10ms
|
||||||
delay(20);
|
delay(10);
|
||||||
// Close NMEA sequences on Ublox
|
digitalWrite(PIN_GPS_RESET, !GPS_RESET_MODE);
|
||||||
_serial_gps->write("$PUBX,40,GLL,0,0,0,0,0,0*5C\r\n");
|
|
||||||
_serial_gps->write("$PUBX,40,GSV,0,0,0,0,0,0*59\r\n");
|
|
||||||
_serial_gps->write("$PUBX,40,VTG,0,0,0,0,0,0*5E\r\n");
|
|
||||||
delay(20);
|
|
||||||
// Close NMEA sequences on CM121
|
|
||||||
_serial_gps->write("$CFGMSG,0,1,0,1*1B\r\n");
|
|
||||||
_serial_gps->write("$CFGMSG,0,2,0,1*18\r\n");
|
|
||||||
_serial_gps->write("$CFGMSG,0,3,0,1*19\r\n");
|
|
||||||
delay(20);
|
|
||||||
|
|
||||||
// Unicore UFirebirdII Series: UC6580, UM620, UM621, UM670A, UM680A, or UM681A,or CM121
|
// attempt to detect the chip based on boot messages
|
||||||
std::vector<ChipInfo> unicore = {
|
std::vector<ChipInfo> passive_detect = {
|
||||||
{"UC6580", "UC6580", GNSS_MODEL_UC6580}, {"UM600", "UM600", GNSS_MODEL_UC6580}, {"CM121", "CM121", GNSS_MODEL_CM121}};
|
{"AG3335", "$PAIR021,AG3335", GNSS_MODEL_AG3335},
|
||||||
PROBE_FAMILY("Unicore Family", "$PDTINFO", unicore, 500);
|
{"AG3352", "$PAIR021,AG3352", GNSS_MODEL_AG3352},
|
||||||
|
{"RYS3520", "$PAIR021,REYAX_RYS3520_V2", GNSS_MODEL_AG3352},
|
||||||
std::vector<ChipInfo> atgm = {
|
{"UC6580", "UC6580", GNSS_MODEL_UC6580},
|
||||||
{"ATGM336H", "$GPTXT,01,01,02,HW=ATGM336H", GNSS_MODEL_ATGM336H},
|
// as L76K is sort of a last ditch effort, we won't attempt to detect it by startup messages for now.
|
||||||
/* ATGM332D series (-11(GPS), -21(BDS), -31(GPS+BDS), -51(GPS+GLONASS), -71-0(GPS+BDS+GLONASS)) based on AT6558 */
|
/*{"L76K", "SW=URANUS", GNSS_MODEL_MTK}*/};
|
||||||
{"ATGM332D", "$GPTXT,01,01,02,HW=ATGM332D", GNSS_MODEL_ATGM336H}};
|
GnssModel_t detectedDriver = getProbeResponse(500, passive_detect, serialSpeed);
|
||||||
PROBE_FAMILY("ATGM33xx Family", "$PCAS06,1*1A", atgm, 500);
|
if (detectedDriver != GNSS_MODEL_UNKNOWN) {
|
||||||
|
return detectedDriver;
|
||||||
/* Airoha (Mediatek) AG3335A/M/S, A3352Q, Quectel L89 2.0, SimCom SIM65M */
|
}
|
||||||
_serial_gps->write("$PAIR062,2,0*3C\r\n"); // GSA OFF to reduce volume
|
#endif
|
||||||
_serial_gps->write("$PAIR062,3,0*3D\r\n"); // GSV OFF to reduce volume
|
// Close all NMEA sentences, valid for L76K, ATGM336H (and likely other AT6558 devices)
|
||||||
_serial_gps->write("$PAIR513*3D\r\n"); // save configuration
|
_serial_gps->write("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n");
|
||||||
std::vector<ChipInfo> airoha = {{"AG3335", "$PAIR021,AG3335", GNSS_MODEL_AG3335},
|
delay(20);
|
||||||
{"AG3352", "$PAIR021,AG3352", GNSS_MODEL_AG3352},
|
// Close NMEA sequences on Ublox
|
||||||
{"RYS3520", "$PAIR021,REYAX_RYS3520_V2", GNSS_MODEL_AG3352}};
|
_serial_gps->write("$PUBX,40,GLL,0,0,0,0,0,0*5C\r\n");
|
||||||
PROBE_FAMILY("Airoha Family", "$PAIR021*39", airoha, 1000);
|
_serial_gps->write("$PUBX,40,GSV,0,0,0,0,0,0*59\r\n");
|
||||||
|
_serial_gps->write("$PUBX,40,VTG,0,0,0,0,0,0*5E\r\n");
|
||||||
PROBE_SIMPLE("LC86", "$PQTMVERNO*58", "$PQTMVERNO,LC86", GNSS_MODEL_AG3352, 500);
|
delay(20);
|
||||||
PROBE_SIMPLE("L76K", "$PCAS06,0*1B", "$GPTXT,01,01,02,SW=", GNSS_MODEL_MTK, 500);
|
// Close NMEA sequences on CM121
|
||||||
|
_serial_gps->write("$CFGMSG,0,1,0,1*1B\r\n");
|
||||||
// Close all NMEA sentences, valid for MTK3333 and MTK3339 platforms
|
_serial_gps->write("$CFGMSG,0,2,0,1*18\r\n");
|
||||||
_serial_gps->write("$PMTK514,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*2E\r\n");
|
_serial_gps->write("$CFGMSG,0,3,0,1*19\r\n");
|
||||||
delay(20);
|
currentDelay = 20;
|
||||||
std::vector<ChipInfo> mtk = {{"L76B", "Quectel-L76B", GNSS_MODEL_MTK_L76B}, {"PA1010D", "1010D", GNSS_MODEL_MTK_PA1010D},
|
currentStep = 1;
|
||||||
{"PA1616S", "1616S", GNSS_MODEL_MTK_PA1616S}, {"LS20031", "MC-1513", GNSS_MODEL_MTK_L76B},
|
|
||||||
{"L96", "Quectel-L96", GNSS_MODEL_MTK_L76B}, {"L80-R", "_3337_", GNSS_MODEL_MTK_L76B},
|
|
||||||
{"L80", "_3339_", GNSS_MODEL_MTK_L76B}};
|
|
||||||
|
|
||||||
PROBE_FAMILY("MTK Family", "$PMTK605*31", mtk, 500);
|
|
||||||
|
|
||||||
uint8_t cfg_rate[] = {0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x00, 0x00};
|
|
||||||
UBXChecksum(cfg_rate, sizeof(cfg_rate));
|
|
||||||
clearBuffer();
|
|
||||||
_serial_gps->write(cfg_rate, sizeof(cfg_rate));
|
|
||||||
// Check that the returned response class and message ID are correct
|
|
||||||
GPS_RESPONSE response = getACK(0x06, 0x08, 750);
|
|
||||||
if (response == GNSS_RESPONSE_NONE) {
|
|
||||||
LOG_WARN("No GNSS Module (baudrate %d)", serialSpeed);
|
|
||||||
return GNSS_MODEL_UNKNOWN;
|
return GNSS_MODEL_UNKNOWN;
|
||||||
} else if (response == GNSS_RESPONSE_FRAME_ERRORS) {
|
|
||||||
LOG_INFO("UBlox Frame Errors (baudrate %d)", serialSpeed);
|
|
||||||
}
|
}
|
||||||
|
case 1: {
|
||||||
|
|
||||||
memset(buffer, 0, sizeof(buffer));
|
// Unicore UFirebirdII Series: UC6580, UM620, UM621, UM670A, UM680A, or UM681A,or CM121
|
||||||
uint8_t _message_MONVER[8] = {
|
std::vector<ChipInfo> unicore = {
|
||||||
0xB5, 0x62, // Sync message for UBX protocol
|
{"UC6580", "UC6580", GNSS_MODEL_UC6580}, {"UM600", "UM600", GNSS_MODEL_UC6580}, {"CM121", "CM121", GNSS_MODEL_CM121}};
|
||||||
0x0A, 0x04, // Message class and ID (UBX-MON-VER)
|
PROBE_FAMILY("Unicore Family", "$PDTINFO", unicore, 500);
|
||||||
0x00, 0x00, // Length of payload (we're asking for an answer, so no payload)
|
currentDelay = 20;
|
||||||
0x00, 0x00 // Checksum
|
currentStep = 2;
|
||||||
};
|
return GNSS_MODEL_UNKNOWN;
|
||||||
// Get Ublox gnss module hardware and software info
|
}
|
||||||
UBXChecksum(_message_MONVER, sizeof(_message_MONVER));
|
case 2: {
|
||||||
clearBuffer();
|
std::vector<ChipInfo> atgm = {
|
||||||
_serial_gps->write(_message_MONVER, sizeof(_message_MONVER));
|
{"ATGM336H", "$GPTXT,01,01,02,HW=ATGM336H", GNSS_MODEL_ATGM336H},
|
||||||
|
/* ATGM332D series (-11(GPS), -21(BDS), -31(GPS+BDS), -51(GPS+GLONASS), -71-0(GPS+BDS+GLONASS)) based on AT6558 */
|
||||||
|
{"ATGM332D", "$GPTXT,01,01,02,HW=ATGM332D", GNSS_MODEL_ATGM336H}};
|
||||||
|
PROBE_FAMILY("ATGM33xx Family", "$PCAS06,1*1A", atgm, 500);
|
||||||
|
currentDelay = 20;
|
||||||
|
currentStep = 3;
|
||||||
|
return GNSS_MODEL_UNKNOWN;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
/* Airoha (Mediatek) AG3335A/M/S, A3352Q, Quectel L89 2.0, SimCom SIM65M */
|
||||||
|
_serial_gps->write("$PAIR062,2,0*3C\r\n"); // GSA OFF to reduce volume
|
||||||
|
_serial_gps->write("$PAIR062,3,0*3D\r\n"); // GSV OFF to reduce volume
|
||||||
|
_serial_gps->write("$PAIR513*3D\r\n"); // save configuration
|
||||||
|
std::vector<ChipInfo> airoha = {{"AG3335", "$PAIR021,AG3335", GNSS_MODEL_AG3335},
|
||||||
|
{"AG3352", "$PAIR021,AG3352", GNSS_MODEL_AG3352},
|
||||||
|
{"RYS3520", "$PAIR021,REYAX_RYS3520_V2", GNSS_MODEL_AG3352}};
|
||||||
|
PROBE_FAMILY("Airoha Family", "$PAIR021*39", airoha, 1000);
|
||||||
|
currentDelay = 20;
|
||||||
|
currentStep = 4;
|
||||||
|
return GNSS_MODEL_UNKNOWN;
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
PROBE_SIMPLE("LC86", "$PQTMVERNO*58", "$PQTMVERNO,LC86", GNSS_MODEL_AG3352, 500);
|
||||||
|
PROBE_SIMPLE("L76K", "$PCAS06,0*1B", "$GPTXT,01,01,02,SW=", GNSS_MODEL_MTK, 500);
|
||||||
|
currentDelay = 20;
|
||||||
|
currentStep = 5;
|
||||||
|
return GNSS_MODEL_UNKNOWN;
|
||||||
|
}
|
||||||
|
case 5: {
|
||||||
|
|
||||||
uint16_t len = getACK(buffer, sizeof(buffer), 0x0A, 0x04, 1200);
|
// Close all NMEA sentences, valid for MTK3333 and MTK3339 platforms
|
||||||
if (len) {
|
_serial_gps->write("$PMTK514,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*2E\r\n");
|
||||||
uint16_t position = 0;
|
delay(20);
|
||||||
for (int i = 0; i < 30; i++) {
|
std::vector<ChipInfo> mtk = {{"L76B", "Quectel-L76B", GNSS_MODEL_MTK_L76B}, {"PA1010D", "1010D", GNSS_MODEL_MTK_PA1010D},
|
||||||
ublox_info.swVersion[i] = buffer[position];
|
{"PA1616S", "1616S", GNSS_MODEL_MTK_PA1616S}, {"LS20031", "MC-1513", GNSS_MODEL_MTK_L76B},
|
||||||
position++;
|
{"L96", "Quectel-L96", GNSS_MODEL_MTK_L76B}, {"L80-R", "_3337_", GNSS_MODEL_MTK_L76B},
|
||||||
}
|
{"L80", "_3339_", GNSS_MODEL_MTK_L76B}};
|
||||||
for (int i = 0; i < 10; i++) {
|
|
||||||
ublox_info.hwVersion[i] = buffer[position];
|
|
||||||
position++;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (len >= position + 30) {
|
PROBE_FAMILY("MTK Family", "$PMTK605*31", mtk, 500);
|
||||||
for (int i = 0; i < 30; i++) {
|
currentDelay = 20;
|
||||||
ublox_info.extension[ublox_info.extensionNo][i] = buffer[position];
|
currentStep = 6;
|
||||||
position++;
|
return GNSS_MODEL_UNKNOWN;
|
||||||
}
|
}
|
||||||
ublox_info.extensionNo++;
|
case 6: {
|
||||||
if (ublox_info.extensionNo > 9)
|
uint8_t cfg_rate[] = {0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x00, 0x00};
|
||||||
break;
|
UBXChecksum(cfg_rate, sizeof(cfg_rate));
|
||||||
}
|
clearBuffer();
|
||||||
|
_serial_gps->write(cfg_rate, sizeof(cfg_rate));
|
||||||
LOG_DEBUG("Module Info : ");
|
// Check that the returned response class and message ID are correct
|
||||||
LOG_DEBUG("Soft version: %s", ublox_info.swVersion);
|
GPS_RESPONSE response = getACK(0x06, 0x08, 750);
|
||||||
LOG_DEBUG("Hard version: %s", ublox_info.hwVersion);
|
if (response == GNSS_RESPONSE_NONE) {
|
||||||
LOG_DEBUG("Extensions:%d", ublox_info.extensionNo);
|
LOG_WARN("No GNSS Module (baudrate %d)", serialSpeed);
|
||||||
for (int i = 0; i < ublox_info.extensionNo; i++) {
|
currentDelay = 2000;
|
||||||
LOG_DEBUG(" %s", ublox_info.extension[i]);
|
currentStep = 0;
|
||||||
|
return GNSS_MODEL_UNKNOWN;
|
||||||
|
} else if (response == GNSS_RESPONSE_FRAME_ERRORS) {
|
||||||
|
LOG_INFO("UBlox Frame Errors (baudrate %d)", serialSpeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(buffer, 0, sizeof(buffer));
|
memset(buffer, 0, sizeof(buffer));
|
||||||
|
uint8_t _message_MONVER[8] = {
|
||||||
|
0xB5, 0x62, // Sync message for UBX protocol
|
||||||
|
0x0A, 0x04, // Message class and ID (UBX-MON-VER)
|
||||||
|
0x00, 0x00, // Length of payload (we're asking for an answer, so no payload)
|
||||||
|
0x00, 0x00 // Checksum
|
||||||
|
};
|
||||||
|
// Get Ublox gnss module hardware and software info
|
||||||
|
UBXChecksum(_message_MONVER, sizeof(_message_MONVER));
|
||||||
|
clearBuffer();
|
||||||
|
_serial_gps->write(_message_MONVER, sizeof(_message_MONVER));
|
||||||
|
|
||||||
// tips: extensionNo field is 0 on some 6M GNSS modules
|
uint16_t len = getACK(buffer, sizeof(buffer), 0x0A, 0x04, 1200);
|
||||||
for (int i = 0; i < ublox_info.extensionNo; ++i) {
|
if (len) {
|
||||||
if (!strncmp(ublox_info.extension[i], "MOD=", 4)) {
|
uint16_t position = 0;
|
||||||
strncpy((char *)buffer, &(ublox_info.extension[i][4]), sizeof(buffer));
|
for (int i = 0; i < 30; i++) {
|
||||||
} else if (!strncmp(ublox_info.extension[i], "PROTVER", 7)) {
|
ublox_info.swVersion[i] = buffer[position];
|
||||||
char *ptr = nullptr;
|
position++;
|
||||||
memset(buffer, 0, sizeof(buffer));
|
}
|
||||||
strncpy((char *)buffer, &(ublox_info.extension[i][8]), sizeof(buffer));
|
for (int i = 0; i < 10; i++) {
|
||||||
LOG_DEBUG("Protocol Version:%s", (char *)buffer);
|
ublox_info.hwVersion[i] = buffer[position];
|
||||||
if (strlen((char *)buffer)) {
|
position++;
|
||||||
ublox_info.protocol_version = strtoul((char *)buffer, &ptr, 10);
|
}
|
||||||
LOG_DEBUG("ProtVer=%d", ublox_info.protocol_version);
|
|
||||||
} else {
|
while (len >= position + 30) {
|
||||||
ublox_info.protocol_version = 0;
|
for (int i = 0; i < 30; i++) {
|
||||||
|
ublox_info.extension[ublox_info.extensionNo][i] = buffer[position];
|
||||||
|
position++;
|
||||||
|
}
|
||||||
|
ublox_info.extensionNo++;
|
||||||
|
if (ublox_info.extensionNo > 9)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG("Module Info : ");
|
||||||
|
LOG_DEBUG("Soft version: %s", ublox_info.swVersion);
|
||||||
|
LOG_DEBUG("Hard version: %s", ublox_info.hwVersion);
|
||||||
|
LOG_DEBUG("Extensions:%d", ublox_info.extensionNo);
|
||||||
|
for (int i = 0; i < ublox_info.extensionNo; i++) {
|
||||||
|
LOG_DEBUG(" %s", ublox_info.extension[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(buffer, 0, sizeof(buffer));
|
||||||
|
|
||||||
|
// tips: extensionNo field is 0 on some 6M GNSS modules
|
||||||
|
for (int i = 0; i < ublox_info.extensionNo; ++i) {
|
||||||
|
if (!strncmp(ublox_info.extension[i], "MOD=", 4)) {
|
||||||
|
strncpy((char *)buffer, &(ublox_info.extension[i][4]), sizeof(buffer));
|
||||||
|
} else if (!strncmp(ublox_info.extension[i], "PROTVER", 7)) {
|
||||||
|
char *ptr = nullptr;
|
||||||
|
memset(buffer, 0, sizeof(buffer));
|
||||||
|
strncpy((char *)buffer, &(ublox_info.extension[i][8]), sizeof(buffer));
|
||||||
|
LOG_DEBUG("Protocol Version:%s", (char *)buffer);
|
||||||
|
if (strlen((char *)buffer)) {
|
||||||
|
ublox_info.protocol_version = strtoul((char *)buffer, &ptr, 10);
|
||||||
|
LOG_DEBUG("ProtVer=%d", ublox_info.protocol_version);
|
||||||
|
} else {
|
||||||
|
ublox_info.protocol_version = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if (strncmp(ublox_info.hwVersion, "00040007", 8) == 0) {
|
||||||
if (strncmp(ublox_info.hwVersion, "00040007", 8) == 0) {
|
LOG_INFO(DETECTED_MESSAGE, "U-blox 6", "6");
|
||||||
LOG_INFO(DETECTED_MESSAGE, "U-blox 6", "6");
|
return GNSS_MODEL_UBLOX6;
|
||||||
return GNSS_MODEL_UBLOX6;
|
} else if (strncmp(ublox_info.hwVersion, "00070000", 8) == 0) {
|
||||||
} else if (strncmp(ublox_info.hwVersion, "00070000", 8) == 0) {
|
LOG_INFO(DETECTED_MESSAGE, "U-blox 7", "7");
|
||||||
LOG_INFO(DETECTED_MESSAGE, "U-blox 7", "7");
|
return GNSS_MODEL_UBLOX7;
|
||||||
return GNSS_MODEL_UBLOX7;
|
} else if (strncmp(ublox_info.hwVersion, "00080000", 8) == 0) {
|
||||||
} else if (strncmp(ublox_info.hwVersion, "00080000", 8) == 0) {
|
LOG_INFO(DETECTED_MESSAGE, "U-blox 8", "8");
|
||||||
LOG_INFO(DETECTED_MESSAGE, "U-blox 8", "8");
|
return GNSS_MODEL_UBLOX8;
|
||||||
return GNSS_MODEL_UBLOX8;
|
} else if (strncmp(ublox_info.hwVersion, "00190000", 8) == 0) {
|
||||||
} else if (strncmp(ublox_info.hwVersion, "00190000", 8) == 0) {
|
LOG_INFO(DETECTED_MESSAGE, "U-blox 9", "9");
|
||||||
LOG_INFO(DETECTED_MESSAGE, "U-blox 9", "9");
|
return GNSS_MODEL_UBLOX9;
|
||||||
return GNSS_MODEL_UBLOX9;
|
} else if (strncmp(ublox_info.hwVersion, "000A0000", 8) == 0) {
|
||||||
} else if (strncmp(ublox_info.hwVersion, "000A0000", 8) == 0) {
|
LOG_INFO(DETECTED_MESSAGE, "U-blox 10", "10");
|
||||||
LOG_INFO(DETECTED_MESSAGE, "U-blox 10", "10");
|
return GNSS_MODEL_UBLOX10;
|
||||||
return GNSS_MODEL_UBLOX10;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
LOG_WARN("No GNSS Module (baudrate %d)", serialSpeed);
|
LOG_WARN("No GNSS Module (baudrate %d)", serialSpeed);
|
||||||
|
currentDelay = 2000;
|
||||||
|
currentStep = 0;
|
||||||
return GNSS_MODEL_UNKNOWN;
|
return GNSS_MODEL_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
GnssModel_t GPS::getProbeResponse(unsigned long timeout, const std::vector<ChipInfo> &responseMap)
|
GnssModel_t GPS::getProbeResponse(unsigned long timeout, const std::vector<ChipInfo> &responseMap, int serialSpeed)
|
||||||
{
|
{
|
||||||
String response = "";
|
// Calculate buffer size based on baud rate - 256 bytes for 9600 baud as baseline
|
||||||
|
// Higher baud rates get proportionally larger buffers to handle more data
|
||||||
|
int bufferSize = (serialSpeed * 256) / 9600;
|
||||||
|
// Clamp buffer size between reasonable limits
|
||||||
|
if (bufferSize < 128)
|
||||||
|
bufferSize = 128;
|
||||||
|
if (bufferSize > 2048)
|
||||||
|
bufferSize = 2048;
|
||||||
|
|
||||||
|
char *response = new char[bufferSize](); // Dynamically allocate based on baud rate
|
||||||
|
uint16_t responseLen = 0;
|
||||||
unsigned long start = millis();
|
unsigned long start = millis();
|
||||||
while (millis() - start < timeout) {
|
while (millis() - start < timeout) {
|
||||||
if (_serial_gps->available()) {
|
if (_serial_gps->available()) {
|
||||||
response += (char)_serial_gps->read();
|
char c = _serial_gps->read();
|
||||||
|
|
||||||
if (response.endsWith(",") || response.endsWith("\r\n")) {
|
// Add char to buffer if there's space
|
||||||
#ifdef GPS_DEBUG
|
if (responseLen < bufferSize - 1) {
|
||||||
LOG_DEBUG(response.c_str());
|
response[responseLen++] = c;
|
||||||
#endif
|
response[responseLen] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == ',' || (responseLen >= 2 && response[responseLen - 2] == '\r' && response[responseLen - 1] == '\n')) {
|
||||||
// check if we can see our chips
|
// check if we can see our chips
|
||||||
for (const auto &chipInfo : responseMap) {
|
for (const auto &chipInfo : responseMap) {
|
||||||
if (strstr(response.c_str(), chipInfo.detectionString.c_str()) != nullptr) {
|
if (strstr(response, chipInfo.detectionString.c_str()) != nullptr) {
|
||||||
|
#ifdef GPS_DEBUG
|
||||||
|
LOG_DEBUG(response);
|
||||||
|
#endif
|
||||||
LOG_INFO("%s detected", chipInfo.chipName.c_str());
|
LOG_INFO("%s detected", chipInfo.chipName.c_str());
|
||||||
|
delete[] response; // Cleanup before return
|
||||||
return chipInfo.driver;
|
return chipInfo.driver;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (response.endsWith("\r\n")) {
|
if (responseLen >= 2 && response[responseLen - 2] == '\r' && response[responseLen - 1] == '\n') {
|
||||||
response.trim();
|
#ifdef GPS_DEBUG
|
||||||
response = ""; // Reset the response string for the next potential message
|
LOG_DEBUG(response);
|
||||||
|
#endif
|
||||||
|
// Reset the response buffer for the next potential message
|
||||||
|
responseLen = 0;
|
||||||
|
response[0] = '\0';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef GPS_DEBUG
|
#ifdef GPS_DEBUG
|
||||||
LOG_DEBUG(response.c_str());
|
LOG_DEBUG(response);
|
||||||
#endif
|
#endif
|
||||||
return GNSS_MODEL_UNKNOWN; // Return empty string on timeout
|
delete[] response; // Cleanup before return
|
||||||
|
return GNSS_MODEL_UNKNOWN; // Return unknown on timeout
|
||||||
}
|
}
|
||||||
|
|
||||||
GPS *GPS::createGps()
|
GPS *GPS::createGps()
|
||||||
@@ -1487,8 +1593,6 @@ GPS *GPS::createGps()
|
|||||||
|
|
||||||
#ifdef PIN_GPS_RESET
|
#ifdef PIN_GPS_RESET
|
||||||
pinMode(PIN_GPS_RESET, OUTPUT);
|
pinMode(PIN_GPS_RESET, OUTPUT);
|
||||||
digitalWrite(PIN_GPS_RESET, GPS_RESET_MODE); // assert for 10ms
|
|
||||||
delay(10);
|
|
||||||
digitalWrite(PIN_GPS_RESET, !GPS_RESET_MODE);
|
digitalWrite(PIN_GPS_RESET, !GPS_RESET_MODE);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,9 @@
|
|||||||
#define GPS_EN_ACTIVE 1
|
#define GPS_EN_ACTIVE 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static constexpr uint32_t GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS = 10 * 1000UL;
|
||||||
|
static constexpr uint32_t GPS_FIX_HOLD_MAX_MS = 20000;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
GNSS_MODEL_ATGM336H,
|
GNSS_MODEL_ATGM336H,
|
||||||
GNSS_MODEL_MTK,
|
GNSS_MODEL_MTK,
|
||||||
@@ -151,6 +154,8 @@ class GPS : private concurrency::OSThread
|
|||||||
TinyGPSPlus reader;
|
TinyGPSPlus reader;
|
||||||
uint8_t fixQual = 0; // fix quality from GPGGA
|
uint8_t fixQual = 0; // fix quality from GPGGA
|
||||||
uint32_t lastChecksumFailCount = 0;
|
uint32_t lastChecksumFailCount = 0;
|
||||||
|
uint8_t currentStep = 0;
|
||||||
|
int32_t currentDelay = 2000;
|
||||||
|
|
||||||
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
|
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
|
||||||
// (20210908) TinyGps++ can only read the GPGSA "FIX TYPE" field
|
// (20210908) TinyGps++ can only read the GPGSA "FIX TYPE" field
|
||||||
@@ -173,8 +178,6 @@ class GPS : private concurrency::OSThread
|
|||||||
*/
|
*/
|
||||||
bool hasValidLocation = false; // default to false, until we complete our first read
|
bool hasValidLocation = false; // default to false, until we complete our first read
|
||||||
|
|
||||||
bool isInPowersave = false;
|
|
||||||
|
|
||||||
bool shouldPublish = false; // If we've changed GPS state, this will force a publish the next loop()
|
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 hasGPS = false; // Do we have a GPS we are talking to
|
||||||
@@ -237,7 +240,7 @@ class GPS : private concurrency::OSThread
|
|||||||
|
|
||||||
virtual int32_t runOnce() override;
|
virtual int32_t runOnce() override;
|
||||||
|
|
||||||
GnssModel_t getProbeResponse(unsigned long timeout, const std::vector<ChipInfo> &responseMap);
|
GnssModel_t getProbeResponse(unsigned long timeout, const std::vector<ChipInfo> &responseMap, int serialSpeed);
|
||||||
|
|
||||||
// Get GNSS model
|
// Get GNSS model
|
||||||
GnssModel_t probe(int serialSpeed);
|
GnssModel_t probe(int serialSpeed);
|
||||||
|
|||||||
134
src/gps/RTC.cpp
134
src/gps/RTC.cpp
@@ -9,6 +9,9 @@
|
|||||||
static RTCQuality currentQuality = RTCQualityNone;
|
static RTCQuality currentQuality = RTCQualityNone;
|
||||||
uint32_t lastSetFromPhoneNtpOrGps = 0;
|
uint32_t lastSetFromPhoneNtpOrGps = 0;
|
||||||
|
|
||||||
|
static uint32_t lastTimeValidationWarning = 0;
|
||||||
|
static const uint32_t TIME_VALIDATION_WARNING_INTERVAL_MS = 15000; // 15 seconds
|
||||||
|
|
||||||
RTCQuality getRTCQuality()
|
RTCQuality getRTCQuality()
|
||||||
{
|
{
|
||||||
return currentQuality;
|
return currentQuality;
|
||||||
@@ -48,7 +51,9 @@ RTCSetResult readFromRTC()
|
|||||||
|
|
||||||
#ifdef BUILD_EPOCH
|
#ifdef BUILD_EPOCH
|
||||||
if (tv.tv_sec < BUILD_EPOCH) {
|
if (tv.tv_sec < BUILD_EPOCH) {
|
||||||
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
|
if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) {
|
||||||
|
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
|
||||||
|
}
|
||||||
return RTCSetResultInvalidTime;
|
return RTCSetResultInvalidTime;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -87,7 +92,10 @@ RTCSetResult readFromRTC()
|
|||||||
|
|
||||||
#ifdef BUILD_EPOCH
|
#ifdef BUILD_EPOCH
|
||||||
if (tv.tv_sec < BUILD_EPOCH) {
|
if (tv.tv_sec < BUILD_EPOCH) {
|
||||||
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
|
if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) {
|
||||||
|
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
|
||||||
|
lastTimeValidationWarning = millis();
|
||||||
|
}
|
||||||
return RTCSetResultInvalidTime;
|
return RTCSetResultInvalidTime;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -101,6 +109,35 @@ RTCSetResult readFromRTC()
|
|||||||
}
|
}
|
||||||
return RTCSetResultSuccess;
|
return RTCSetResultSuccess;
|
||||||
}
|
}
|
||||||
|
#elif defined(RX8130CE_RTC)
|
||||||
|
if (rtc_found.address == RX8130CE_RTC) {
|
||||||
|
uint32_t now = millis();
|
||||||
|
ArtronShop_RX8130CE rtc(&Wire);
|
||||||
|
tm t;
|
||||||
|
if (rtc.getTime(&t)) {
|
||||||
|
tv.tv_sec = gm_mktime(&t);
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
|
||||||
|
uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms
|
||||||
|
LOG_DEBUG("Read RTC time from RX8130CE getDateTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", t.tm_year + 1900,
|
||||||
|
t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch);
|
||||||
|
#ifdef BUILD_EPOCH
|
||||||
|
if (tv.tv_sec < BUILD_EPOCH) {
|
||||||
|
if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) {
|
||||||
|
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
|
||||||
|
lastTimeValidationWarning = millis();
|
||||||
|
}
|
||||||
|
return RTCSetResultInvalidTime;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (currentQuality == RTCQualityNone) {
|
||||||
|
timeStartMsec = now;
|
||||||
|
zeroOffsetSecs = tv.tv_sec;
|
||||||
|
currentQuality = RTCQualityDevice;
|
||||||
|
}
|
||||||
|
return RTCSetResultSuccess;
|
||||||
|
}
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
if (!gettimeofday(&tv, NULL)) {
|
if (!gettimeofday(&tv, NULL)) {
|
||||||
uint32_t now = millis();
|
uint32_t now = millis();
|
||||||
@@ -130,15 +167,20 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd
|
|||||||
uint32_t printableEpoch = tv->tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms
|
uint32_t printableEpoch = tv->tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms
|
||||||
#ifdef BUILD_EPOCH
|
#ifdef BUILD_EPOCH
|
||||||
if (tv->tv_sec < BUILD_EPOCH) {
|
if (tv->tv_sec < BUILD_EPOCH) {
|
||||||
#ifdef GPS_DEBUG
|
if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) {
|
||||||
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
|
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
|
||||||
#endif
|
lastTimeValidationWarning = millis();
|
||||||
|
}
|
||||||
return RTCSetResultInvalidTime;
|
return RTCSetResultInvalidTime;
|
||||||
} else if (tv->tv_sec > (BUILD_EPOCH + FORTY_YEARS)) {
|
} else if ((uint64_t)tv->tv_sec > ((uint64_t)BUILD_EPOCH + FORTY_YEARS)) {
|
||||||
#ifdef GPS_DEBUG
|
if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) {
|
||||||
LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch, BUILD_EPOCH,
|
// Calculate max allowed time safely to avoid overflow in logging
|
||||||
BUILD_EPOCH + FORTY_YEARS);
|
uint64_t maxAllowedTime = (uint64_t)BUILD_EPOCH + FORTY_YEARS;
|
||||||
#endif
|
uint32_t maxAllowedPrintable = (maxAllowedTime > UINT32_MAX) ? UINT32_MAX : (uint32_t)maxAllowedTime;
|
||||||
|
LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch,
|
||||||
|
(uint32_t)BUILD_EPOCH, maxAllowedPrintable);
|
||||||
|
lastTimeValidationWarning = millis();
|
||||||
|
}
|
||||||
return RTCSetResultInvalidTime;
|
return RTCSetResultInvalidTime;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -201,6 +243,17 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd
|
|||||||
LOG_DEBUG("PCF8563_RTC setDateTime %02d-%02d-%02d %02d:%02d:%02d (%ld)", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
|
LOG_DEBUG("PCF8563_RTC setDateTime %02d-%02d-%02d %02d:%02d:%02d (%ld)", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
|
||||||
t->tm_hour, t->tm_min, t->tm_sec, printableEpoch);
|
t->tm_hour, t->tm_min, t->tm_sec, printableEpoch);
|
||||||
}
|
}
|
||||||
|
#elif defined(RX8130CE_RTC)
|
||||||
|
if (rtc_found.address == RX8130CE_RTC) {
|
||||||
|
ArtronShop_RX8130CE rtc(&Wire);
|
||||||
|
tm *t = gmtime(&tv->tv_sec);
|
||||||
|
if (rtc.setTime(*t)) {
|
||||||
|
LOG_DEBUG("RX8130CE setDateTime %02d-%02d-%02d %02d:%02d:%02d (%ld)", t->tm_year + 1900, t->tm_mon + 1,
|
||||||
|
t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, printableEpoch);
|
||||||
|
} else {
|
||||||
|
LOG_WARN("Failed to set time for RX8130CE");
|
||||||
|
}
|
||||||
|
}
|
||||||
#elif defined(ARCH_ESP32)
|
#elif defined(ARCH_ESP32)
|
||||||
settimeofday(tv, NULL);
|
settimeofday(tv, NULL);
|
||||||
#endif
|
#endif
|
||||||
@@ -256,15 +309,20 @@ RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t)
|
|||||||
uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms
|
uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms
|
||||||
#ifdef BUILD_EPOCH
|
#ifdef BUILD_EPOCH
|
||||||
if (tv.tv_sec < BUILD_EPOCH) {
|
if (tv.tv_sec < BUILD_EPOCH) {
|
||||||
#ifdef GPS_DEBUG
|
if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) {
|
||||||
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
|
LOG_WARN("Ignore time (%lu) before build epoch (%lu)!", printableEpoch, BUILD_EPOCH);
|
||||||
#endif
|
lastTimeValidationWarning = millis();
|
||||||
|
}
|
||||||
return RTCSetResultInvalidTime;
|
return RTCSetResultInvalidTime;
|
||||||
} else if (tv.tv_sec > (BUILD_EPOCH + FORTY_YEARS)) {
|
} else if ((uint64_t)tv.tv_sec > ((uint64_t)BUILD_EPOCH + FORTY_YEARS)) {
|
||||||
#ifdef GPS_DEBUG
|
if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) {
|
||||||
LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch, BUILD_EPOCH,
|
// Calculate max allowed time safely to avoid overflow in logging
|
||||||
BUILD_EPOCH + FORTY_YEARS);
|
uint64_t maxAllowedTime = (uint64_t)BUILD_EPOCH + FORTY_YEARS;
|
||||||
#endif
|
uint32_t maxAllowedPrintable = (maxAllowedTime > UINT32_MAX) ? UINT32_MAX : (uint32_t)maxAllowedTime;
|
||||||
|
LOG_WARN("Ignore time (%lu) too far in the future (build epoch: %lu, max allowed: %lu)!", printableEpoch,
|
||||||
|
(uint32_t)BUILD_EPOCH, maxAllowedPrintable);
|
||||||
|
lastTimeValidationWarning = millis();
|
||||||
|
}
|
||||||
return RTCSetResultInvalidTime;
|
return RTCSetResultInvalidTime;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -324,14 +382,40 @@ uint32_t getValidTime(RTCQuality minQuality, bool local)
|
|||||||
time_t gm_mktime(struct tm *tm)
|
time_t gm_mktime(struct tm *tm)
|
||||||
{
|
{
|
||||||
#if !MESHTASTIC_EXCLUDE_TZ
|
#if !MESHTASTIC_EXCLUDE_TZ
|
||||||
setenv("TZ", "GMT0", 1);
|
time_t result = 0;
|
||||||
time_t res = mktime(tm);
|
|
||||||
if (*config.device.tzdef) {
|
// First, get us to the start of tm->year, by calcuating the number of days since the Unix epoch.
|
||||||
setenv("TZ", config.device.tzdef, 1);
|
int year = 1900 + tm->tm_year; // tm_year is years since 1900
|
||||||
} else {
|
int year_minus_one = year - 1;
|
||||||
setenv("TZ", "UTC0", 1);
|
int days_before_this_year = 0;
|
||||||
|
days_before_this_year += year_minus_one * 365;
|
||||||
|
// leap days: every 4 years, except 100s, but including 400s.
|
||||||
|
days_before_this_year += year_minus_one / 4 - year_minus_one / 100 + year_minus_one / 400;
|
||||||
|
// subtract from 1970-01-01 to get days since epoch
|
||||||
|
days_before_this_year -= 719162; // (1969 * 365 + 1969 / 4 - 1969 / 100 + 1969 / 400);
|
||||||
|
|
||||||
|
// Now, within this tm->year, compute the days *before* this tm->month starts.
|
||||||
|
int days_before_month[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; // non-leap year
|
||||||
|
int days_this_year_before_this_month = days_before_month[tm->tm_mon]; // tm->tm_mon is 0..11
|
||||||
|
|
||||||
|
// If this is a leap year, and we're past February, add a day:
|
||||||
|
if (tm->tm_mon >= 2 && (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)) {
|
||||||
|
days_this_year_before_this_month += 1;
|
||||||
}
|
}
|
||||||
return res;
|
|
||||||
|
// And within this month:
|
||||||
|
int days_this_month_before_today = tm->tm_mday - 1; // tm->tm_mday is 1..31
|
||||||
|
|
||||||
|
// Now combine them all together, and convert days to seconds:
|
||||||
|
result += (days_before_this_year + days_this_year_before_this_month + days_this_month_before_today);
|
||||||
|
result *= 86400L;
|
||||||
|
|
||||||
|
// Finally, add in the hours, minutes, and seconds of today:
|
||||||
|
result += tm->tm_hour * 3600;
|
||||||
|
result += tm->tm_min * 60;
|
||||||
|
result += tm->tm_sec;
|
||||||
|
|
||||||
|
return result;
|
||||||
#else
|
#else
|
||||||
return mktime(tm);
|
return mktime(tm);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -4,6 +4,10 @@
|
|||||||
#include "sys/time.h"
|
#include "sys/time.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#ifdef RX8130CE_RTC
|
||||||
|
#include <ArtronShop_RX8130CE.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
enum RTCQuality {
|
enum RTCQuality {
|
||||||
|
|
||||||
/// We haven't had our RTC set yet
|
/// We haven't had our RTC set yet
|
||||||
@@ -56,5 +60,5 @@ time_t gm_mktime(struct tm *tm);
|
|||||||
#define SEC_PER_HOUR 3600
|
#define SEC_PER_HOUR 3600
|
||||||
#define SEC_PER_MIN 60
|
#define SEC_PER_MIN 60
|
||||||
#ifdef BUILD_EPOCH
|
#ifdef BUILD_EPOCH
|
||||||
#define FORTY_YEARS (40UL * 365 * SEC_PER_DAY) // probably time to update your firmware
|
static constexpr uint64_t FORTY_YEARS = (40ULL * 365 * SEC_PER_DAY); // Use 64-bit arithmetic to prevent overflow
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -243,7 +243,7 @@ bool EInkDisplay::connect()
|
|||||||
adafruitDisplay->setRotation(1);
|
adafruitDisplay->setRotation(1);
|
||||||
adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT);
|
adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT);
|
||||||
}
|
}
|
||||||
#elif defined(HELTEC_MESH_POCKET) || defined(SEEED_WIO_TRACKER_L1_EINK)
|
#elif defined(HELTEC_MESH_POCKET) || defined(SEEED_WIO_TRACKER_L1_EINK) || defined(HELTEC_MESH_SOLAR_EINK)
|
||||||
{
|
{
|
||||||
spi1 = &SPI1;
|
spi1 = &SPI1;
|
||||||
spi1->begin();
|
spi1->begin();
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ class EInkDisplay : public OLEDDisplay
|
|||||||
SPIClass *hspi = NULL;
|
SPIClass *hspi = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(HELTEC_MESH_POCKET) || defined(SEEED_WIO_TRACKER_L1_EINK)
|
#if defined(HELTEC_MESH_POCKET) || defined(SEEED_WIO_TRACKER_L1_EINK) || defined(HELTEC_MESH_SOLAR_EINK)
|
||||||
SPIClass *spi1 = NULL;
|
SPIClass *spi1 = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
687
src/graphics/Panel_sdl.cpp
Normal file
687
src/graphics/Panel_sdl.cpp
Normal file
@@ -0,0 +1,687 @@
|
|||||||
|
/*----------------------------------------------------------------------------/
|
||||||
|
Lovyan GFX - Graphics library for embedded devices.
|
||||||
|
|
||||||
|
Original Source:
|
||||||
|
https://github.com/lovyan03/LovyanGFX/
|
||||||
|
|
||||||
|
Licence:
|
||||||
|
[FreeBSD](https://github.com/lovyan03/LovyanGFX/blob/master/license.txt)
|
||||||
|
|
||||||
|
Author:
|
||||||
|
[lovyan03](https://twitter.com/lovyan03)
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
[ciniml](https://github.com/ciniml)
|
||||||
|
[mongonta0716](https://github.com/mongonta0716)
|
||||||
|
[tobozo](https://github.com/tobozo)
|
||||||
|
|
||||||
|
Porting for SDL:
|
||||||
|
[imliubo](https://github.com/imliubo)
|
||||||
|
/----------------------------------------------------------------------------*/
|
||||||
|
#include "Panel_sdl.hpp"
|
||||||
|
|
||||||
|
#if defined(SDL_h_)
|
||||||
|
|
||||||
|
// #include "../common.hpp"
|
||||||
|
// #include "../../misc/common_function.hpp"
|
||||||
|
// #include "../../Bus.hpp"
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <math.h>
|
||||||
|
#include <vector>
|
||||||
|
#ifndef M_PI
|
||||||
|
#define M_PI 3.14159265358979323846
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace lgfx
|
||||||
|
{
|
||||||
|
inline namespace v1
|
||||||
|
{
|
||||||
|
SDL_Keymod Panel_sdl::_keymod = KMOD_NONE;
|
||||||
|
static SDL_semaphore *_update_in_semaphore = nullptr;
|
||||||
|
static SDL_semaphore *_update_out_semaphore = nullptr;
|
||||||
|
volatile static uint32_t _in_step_exec = 0;
|
||||||
|
volatile static uint32_t _msec_step_exec = 512;
|
||||||
|
static bool _inited = false;
|
||||||
|
static bool _all_close = false;
|
||||||
|
|
||||||
|
volatile uint8_t Panel_sdl::_gpio_dummy_values[EMULATED_GPIO_MAX];
|
||||||
|
|
||||||
|
static inline void *heap_alloc_dma(size_t length)
|
||||||
|
{
|
||||||
|
return malloc(length);
|
||||||
|
} // aligned_alloc(16, length);
|
||||||
|
static inline void heap_free(void *buf)
|
||||||
|
{
|
||||||
|
free(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::list<monitor_t *> _list_monitor;
|
||||||
|
|
||||||
|
static monitor_t *const getMonitorByWindowID(uint32_t windowID)
|
||||||
|
{
|
||||||
|
for (auto &m : _list_monitor) {
|
||||||
|
if (SDL_GetWindowID(m->window) == windowID) {
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static std::vector<Panel_sdl::KeyCodeMapping_t> _key_code_map;
|
||||||
|
|
||||||
|
void Panel_sdl::addKeyCodeMapping(SDL_KeyCode keyCode, uint8_t gpio)
|
||||||
|
{
|
||||||
|
if (gpio > EMULATED_GPIO_MAX)
|
||||||
|
return;
|
||||||
|
KeyCodeMapping_t map;
|
||||||
|
map.keycode = keyCode;
|
||||||
|
map.gpio = gpio;
|
||||||
|
_key_code_map.push_back(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Panel_sdl::getKeyCodeMapping(SDL_KeyCode keyCode)
|
||||||
|
{
|
||||||
|
for (const auto &i : _key_code_map) {
|
||||||
|
if (i.keycode == keyCode)
|
||||||
|
return i.gpio;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel_sdl::_event_proc(void)
|
||||||
|
{
|
||||||
|
SDL_Event event;
|
||||||
|
while (SDL_PollEvent(&event)) {
|
||||||
|
if ((event.type == SDL_KEYDOWN) || (event.type == SDL_KEYUP)) {
|
||||||
|
auto mon = getMonitorByWindowID(event.button.windowID);
|
||||||
|
int gpio = -1;
|
||||||
|
|
||||||
|
/// Check key mapping
|
||||||
|
gpio = getKeyCodeMapping((SDL_KeyCode)event.key.keysym.sym);
|
||||||
|
if (gpio < 0) {
|
||||||
|
switch (event.key.keysym.sym) { /// M5StackのBtnA~BtnCのエミュレート;
|
||||||
|
// case SDLK_LEFT: gpio = 39; break;
|
||||||
|
// case SDLK_DOWN: gpio = 38; break;
|
||||||
|
// case SDLK_RIGHT: gpio = 37; break;
|
||||||
|
// case SDLK_UP: gpio = 36; break;
|
||||||
|
|
||||||
|
/// L/Rキーで画面回転
|
||||||
|
case SDLK_r:
|
||||||
|
case SDLK_l:
|
||||||
|
if (event.type == SDL_KEYDOWN && event.key.keysym.mod == _keymod) {
|
||||||
|
if (mon != nullptr) {
|
||||||
|
mon->frame_rotation = (mon->frame_rotation += event.key.keysym.sym == SDLK_r ? 1 : -1);
|
||||||
|
int x, y, w, h;
|
||||||
|
SDL_GetWindowSize(mon->window, &w, &h);
|
||||||
|
SDL_GetWindowPosition(mon->window, &x, &y);
|
||||||
|
SDL_SetWindowSize(mon->window, h, w);
|
||||||
|
SDL_SetWindowPosition(mon->window, x + (w - h) / 2, y + (h - w) / 2);
|
||||||
|
mon->panel->sdl_invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/// 1~6キーで画面拡大率変更
|
||||||
|
case SDLK_1:
|
||||||
|
case SDLK_2:
|
||||||
|
case SDLK_3:
|
||||||
|
case SDLK_4:
|
||||||
|
case SDLK_5:
|
||||||
|
case SDLK_6:
|
||||||
|
if (event.type == SDL_KEYDOWN && event.key.keysym.mod == _keymod) {
|
||||||
|
if (mon != nullptr) {
|
||||||
|
int size = 1 + (event.key.keysym.sym - SDLK_1);
|
||||||
|
_update_scaling(mon, size, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type == SDL_KEYDOWN) {
|
||||||
|
Panel_sdl::gpio_lo(gpio);
|
||||||
|
} else {
|
||||||
|
Panel_sdl::gpio_hi(gpio);
|
||||||
|
}
|
||||||
|
} else if (event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEMOTION) {
|
||||||
|
auto mon = getMonitorByWindowID(event.button.windowID);
|
||||||
|
if (mon != nullptr) {
|
||||||
|
{
|
||||||
|
int x, y, w, h;
|
||||||
|
SDL_GetWindowSize(mon->window, &w, &h);
|
||||||
|
SDL_GetMouseState(&x, &y);
|
||||||
|
float sf = sinf(mon->frame_angle * M_PI / 180);
|
||||||
|
float cf = cosf(mon->frame_angle * M_PI / 180);
|
||||||
|
x -= w / 2.0f;
|
||||||
|
y -= h / 2.0f;
|
||||||
|
float nx = y * sf + x * cf;
|
||||||
|
float ny = y * cf - x * sf;
|
||||||
|
if (mon->frame_rotation & 1) {
|
||||||
|
std::swap(w, h);
|
||||||
|
}
|
||||||
|
x = (nx * mon->frame_width / w) + (mon->frame_width >> 1);
|
||||||
|
y = (ny * mon->frame_height / h) + (mon->frame_height >> 1);
|
||||||
|
mon->touch_x = x - mon->frame_inner_x;
|
||||||
|
mon->touch_y = y - mon->frame_inner_y;
|
||||||
|
}
|
||||||
|
if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_LEFT) {
|
||||||
|
mon->touched = true;
|
||||||
|
}
|
||||||
|
if (event.type == SDL_MOUSEBUTTONUP && event.button.button == SDL_BUTTON_LEFT) {
|
||||||
|
mon->touched = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (event.type == SDL_WINDOWEVENT) {
|
||||||
|
auto monitor = getMonitorByWindowID(event.window.windowID);
|
||||||
|
if (monitor) {
|
||||||
|
if (event.window.event == SDL_WINDOWEVENT_RESIZED) {
|
||||||
|
int mw, mh;
|
||||||
|
SDL_GetRendererOutputSize(monitor->renderer, &mw, &mh);
|
||||||
|
if (monitor->frame_rotation & 1) {
|
||||||
|
std::swap(mw, mh);
|
||||||
|
}
|
||||||
|
monitor->scaling_x = (mw * 2 / monitor->frame_width) / 2.0f;
|
||||||
|
monitor->scaling_y = (mh * 2 / monitor->frame_height) / 2.0f;
|
||||||
|
monitor->panel->sdl_invalidate();
|
||||||
|
} else if (event.window.event == SDL_WINDOWEVENT_CLOSE) {
|
||||||
|
monitor->closing = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (event.type == SDL_QUIT) {
|
||||||
|
for (auto &m : _list_monitor) {
|
||||||
|
m->closing = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// デバッガでステップ実行されていることを検出するスレッド用関数。
|
||||||
|
static int detectDebugger(bool *running)
|
||||||
|
{
|
||||||
|
uint32_t prev_ms = SDL_GetTicks();
|
||||||
|
do {
|
||||||
|
SDL_Delay(1);
|
||||||
|
uint32_t ms = SDL_GetTicks();
|
||||||
|
/// 時間間隔が広すぎる場合はステップ実行中 (ブレークポイントで止まった)と判断する。
|
||||||
|
/// また、解除されたと判断した後も1023msecほど状態を維持する。
|
||||||
|
if (ms - prev_ms > 64) {
|
||||||
|
_in_step_exec = _msec_step_exec;
|
||||||
|
} else if (_in_step_exec) {
|
||||||
|
--_in_step_exec;
|
||||||
|
}
|
||||||
|
prev_ms = ms;
|
||||||
|
} while (*running);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel_sdl::_update_proc(void)
|
||||||
|
{
|
||||||
|
for (auto it = _list_monitor.begin(); it != _list_monitor.end();) {
|
||||||
|
if ((*it)->closing) {
|
||||||
|
if ((*it)->texture_frameimage) {
|
||||||
|
SDL_DestroyTexture((*it)->texture_frameimage);
|
||||||
|
}
|
||||||
|
SDL_DestroyTexture((*it)->texture);
|
||||||
|
SDL_DestroyRenderer((*it)->renderer);
|
||||||
|
SDL_DestroyWindow((*it)->window);
|
||||||
|
_list_monitor.erase(it++);
|
||||||
|
if (_list_monitor.empty()) {
|
||||||
|
_all_close = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
(*it)->panel->sdl_update();
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Panel_sdl::setup(void)
|
||||||
|
{
|
||||||
|
if (_inited)
|
||||||
|
return 1;
|
||||||
|
_inited = true;
|
||||||
|
|
||||||
|
/// Add default keycode mapping
|
||||||
|
/// M5StackのBtnA~BtnCのエミュレート;
|
||||||
|
addKeyCodeMapping(SDLK_LEFT, 39);
|
||||||
|
addKeyCodeMapping(SDLK_DOWN, 38);
|
||||||
|
addKeyCodeMapping(SDLK_RIGHT, 37);
|
||||||
|
addKeyCodeMapping(SDLK_UP, 36);
|
||||||
|
|
||||||
|
SDL_CreateThread((SDL_ThreadFunction)detectDebugger, "dbg", &_inited);
|
||||||
|
|
||||||
|
_update_in_semaphore = SDL_CreateSemaphore(0);
|
||||||
|
_update_out_semaphore = SDL_CreateSemaphore(0);
|
||||||
|
for (size_t pin = 0; pin < EMULATED_GPIO_MAX; ++pin) {
|
||||||
|
gpio_hi(pin);
|
||||||
|
}
|
||||||
|
/*Initialize the SDL*/
|
||||||
|
SDL_Init(SDL_INIT_VIDEO);
|
||||||
|
SDL_StartTextInput();
|
||||||
|
|
||||||
|
// SDL_SetThreadPriority(SDL_ThreadPriority::SDL_THREAD_PRIORITY_HIGH);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Panel_sdl::loop(void)
|
||||||
|
{
|
||||||
|
if (!_inited)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
_event_proc();
|
||||||
|
SDL_SemWaitTimeout(_update_in_semaphore, 1);
|
||||||
|
_update_proc();
|
||||||
|
_event_proc();
|
||||||
|
if (SDL_SemValue(_update_out_semaphore) == 0) {
|
||||||
|
SDL_SemPost(_update_out_semaphore);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _all_close;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Panel_sdl::close(void)
|
||||||
|
{
|
||||||
|
if (!_inited)
|
||||||
|
return 1;
|
||||||
|
_inited = false;
|
||||||
|
|
||||||
|
SDL_StopTextInput();
|
||||||
|
SDL_DestroySemaphore(_update_in_semaphore);
|
||||||
|
SDL_DestroySemaphore(_update_out_semaphore);
|
||||||
|
SDL_Quit();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Panel_sdl::main(int (*fn)(bool *), uint32_t msec_step_exec)
|
||||||
|
{
|
||||||
|
_msec_step_exec = msec_step_exec;
|
||||||
|
|
||||||
|
/// SDLの準備
|
||||||
|
if (0 != Panel_sdl::setup()) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ユーザコード関数の動作・停止フラグ
|
||||||
|
bool running = true;
|
||||||
|
|
||||||
|
/// ユーザコード関数を起動する
|
||||||
|
auto thread = SDL_CreateThread((SDL_ThreadFunction)fn, "fn", &running);
|
||||||
|
|
||||||
|
/// 全部のウィンドウが閉じられるまでSDLのイベント・描画処理を継続
|
||||||
|
while (0 == Panel_sdl::loop()) {
|
||||||
|
};
|
||||||
|
|
||||||
|
/// ユーザコード関数を終了する
|
||||||
|
running = false;
|
||||||
|
SDL_WaitThread(thread, nullptr);
|
||||||
|
|
||||||
|
/// SDLを終了する
|
||||||
|
return Panel_sdl::close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel_sdl::setScaling(uint_fast8_t scaling_x, uint_fast8_t scaling_y)
|
||||||
|
{
|
||||||
|
monitor.scaling_x = scaling_x;
|
||||||
|
monitor.scaling_y = scaling_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel_sdl::setFrameImage(const void *frame_image, int frame_width, int frame_height, int inner_x, int inner_y)
|
||||||
|
{
|
||||||
|
monitor.frame_image = frame_image;
|
||||||
|
monitor.frame_width = frame_width;
|
||||||
|
monitor.frame_height = frame_height;
|
||||||
|
monitor.frame_inner_x = inner_x;
|
||||||
|
monitor.frame_inner_y = inner_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel_sdl::setFrameRotation(uint_fast16_t frame_rotation)
|
||||||
|
{
|
||||||
|
monitor.frame_rotation = frame_rotation;
|
||||||
|
monitor.frame_angle = (monitor.frame_rotation) * 90;
|
||||||
|
}
|
||||||
|
|
||||||
|
Panel_sdl::~Panel_sdl(void)
|
||||||
|
{
|
||||||
|
_list_monitor.remove(&monitor);
|
||||||
|
SDL_DestroyMutex(_sdl_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
Panel_sdl::Panel_sdl(void) : Panel_FrameBufferBase()
|
||||||
|
{
|
||||||
|
_sdl_mutex = SDL_CreateMutex();
|
||||||
|
_auto_display = true;
|
||||||
|
monitor.panel = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Panel_sdl::init(bool use_reset)
|
||||||
|
{
|
||||||
|
initFrameBuffer(_cfg.panel_width * 4, _cfg.panel_height);
|
||||||
|
bool res = Panel_FrameBufferBase::init(use_reset);
|
||||||
|
|
||||||
|
_list_monitor.push_back(&monitor);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
color_depth_t Panel_sdl::setColorDepth(color_depth_t depth)
|
||||||
|
{
|
||||||
|
auto bits = depth & color_depth_t::bit_mask;
|
||||||
|
if (bits >= 16) {
|
||||||
|
depth = (bits > 16) ? rgb888_3Byte : rgb565_2Byte;
|
||||||
|
} else {
|
||||||
|
depth = (depth == color_depth_t::grayscale_8bit) ? grayscale_8bit : rgb332_1Byte;
|
||||||
|
}
|
||||||
|
_write_depth = depth;
|
||||||
|
_read_depth = depth;
|
||||||
|
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
Panel_sdl::lock_t::lock_t(Panel_sdl *parent) : _parent{parent}
|
||||||
|
{
|
||||||
|
SDL_LockMutex(parent->_sdl_mutex);
|
||||||
|
};
|
||||||
|
|
||||||
|
Panel_sdl::lock_t::~lock_t(void)
|
||||||
|
{
|
||||||
|
++_parent->_modified_counter;
|
||||||
|
SDL_UnlockMutex(_parent->_sdl_mutex);
|
||||||
|
if (SDL_SemValue(_update_in_semaphore) < 2) {
|
||||||
|
SDL_SemPost(_update_in_semaphore);
|
||||||
|
if (!_in_step_exec) {
|
||||||
|
SDL_SemWaitTimeout(_update_out_semaphore, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void Panel_sdl::drawPixelPreclipped(uint_fast16_t x, uint_fast16_t y, uint32_t rawcolor)
|
||||||
|
{
|
||||||
|
lock_t lock(this);
|
||||||
|
Panel_FrameBufferBase::drawPixelPreclipped(x, y, rawcolor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel_sdl::writeFillRectPreclipped(uint_fast16_t x, uint_fast16_t y, uint_fast16_t w, uint_fast16_t h, uint32_t rawcolor)
|
||||||
|
{
|
||||||
|
lock_t lock(this);
|
||||||
|
Panel_FrameBufferBase::writeFillRectPreclipped(x, y, w, h, rawcolor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel_sdl::writeBlock(uint32_t rawcolor, uint32_t length)
|
||||||
|
{
|
||||||
|
// lock_t lock(this);
|
||||||
|
Panel_FrameBufferBase::writeBlock(rawcolor, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel_sdl::writeImage(uint_fast16_t x, uint_fast16_t y, uint_fast16_t w, uint_fast16_t h, pixelcopy_t *param, bool use_dma)
|
||||||
|
{
|
||||||
|
lock_t lock(this);
|
||||||
|
Panel_FrameBufferBase::writeImage(x, y, w, h, param, use_dma);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel_sdl::writeImageARGB(uint_fast16_t x, uint_fast16_t y, uint_fast16_t w, uint_fast16_t h, pixelcopy_t *param)
|
||||||
|
{
|
||||||
|
lock_t lock(this);
|
||||||
|
Panel_FrameBufferBase::writeImageARGB(x, y, w, h, param);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel_sdl::writePixels(pixelcopy_t *param, uint32_t len, bool use_dma)
|
||||||
|
{
|
||||||
|
lock_t lock(this);
|
||||||
|
Panel_FrameBufferBase::writePixels(param, len, use_dma);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel_sdl::display(uint_fast16_t x, uint_fast16_t y, uint_fast16_t w, uint_fast16_t h)
|
||||||
|
{
|
||||||
|
(void)x;
|
||||||
|
(void)y;
|
||||||
|
(void)w;
|
||||||
|
(void)h;
|
||||||
|
if (_in_step_exec) {
|
||||||
|
if (_display_counter != _modified_counter) {
|
||||||
|
do {
|
||||||
|
SDL_SemPost(_update_in_semaphore);
|
||||||
|
SDL_SemWaitTimeout(_update_out_semaphore, 1);
|
||||||
|
} while (_display_counter != _modified_counter);
|
||||||
|
SDL_Delay(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint_fast8_t Panel_sdl::getTouchRaw(touch_point_t *tp, uint_fast8_t count)
|
||||||
|
{
|
||||||
|
(void)count;
|
||||||
|
tp->x = monitor.touch_x;
|
||||||
|
tp->y = monitor.touch_y;
|
||||||
|
tp->size = monitor.touched ? 1 : 0;
|
||||||
|
tp->id = 0;
|
||||||
|
return monitor.touched;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel_sdl::setWindowTitle(const char *title)
|
||||||
|
{
|
||||||
|
_window_title = title;
|
||||||
|
if (monitor.window) {
|
||||||
|
SDL_SetWindowTitle(monitor.window, _window_title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel_sdl::_update_scaling(monitor_t *mon, float sx, float sy)
|
||||||
|
{
|
||||||
|
mon->scaling_x = sx;
|
||||||
|
mon->scaling_y = sy;
|
||||||
|
int nw = mon->frame_width;
|
||||||
|
int nh = mon->frame_height;
|
||||||
|
if (mon->frame_rotation & 1) {
|
||||||
|
std::swap(nw, nh);
|
||||||
|
}
|
||||||
|
|
||||||
|
int x, y, w, h;
|
||||||
|
int rw, rh;
|
||||||
|
SDL_GetRendererOutputSize(mon->renderer, &rw, &rh);
|
||||||
|
SDL_GetWindowSize(mon->window, &w, &h);
|
||||||
|
nw = nw * sx * w / rw;
|
||||||
|
nh = nh * sy * h / rh;
|
||||||
|
SDL_GetWindowPosition(mon->window, &x, &y);
|
||||||
|
SDL_SetWindowSize(mon->window, nw, nh);
|
||||||
|
SDL_SetWindowPosition(mon->window, x + (w - nw) / 2, y + (h - nh) / 2);
|
||||||
|
mon->panel->sdl_invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel_sdl::sdl_create(monitor_t *m)
|
||||||
|
{
|
||||||
|
int flag = SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI;
|
||||||
|
#if SDL_FULLSCREEN
|
||||||
|
flag |= SDL_WINDOW_FULLSCREEN;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (m->frame_width < _cfg.panel_width) {
|
||||||
|
m->frame_width = _cfg.panel_width;
|
||||||
|
}
|
||||||
|
if (m->frame_height < _cfg.panel_height) {
|
||||||
|
m->frame_height = _cfg.panel_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
int window_width = m->frame_width * m->scaling_x;
|
||||||
|
int window_height = m->frame_height * m->scaling_y;
|
||||||
|
int scaling_x = m->scaling_x;
|
||||||
|
int scaling_y = m->scaling_y;
|
||||||
|
if (m->frame_rotation & 1) {
|
||||||
|
std::swap(window_width, window_height);
|
||||||
|
std::swap(scaling_x, scaling_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
m->window = SDL_CreateWindow(_window_title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, window_width, window_height,
|
||||||
|
flag); /*last param. SDL_WINDOW_BORDERLESS to hide borders*/
|
||||||
|
}
|
||||||
|
m->renderer = SDL_CreateRenderer(m->window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
||||||
|
m->texture =
|
||||||
|
SDL_CreateTexture(m->renderer, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING, _cfg.panel_width, _cfg.panel_height);
|
||||||
|
SDL_SetTextureBlendMode(m->texture, SDL_BLENDMODE_NONE);
|
||||||
|
|
||||||
|
if (m->frame_image) {
|
||||||
|
// 枠画像用のサーフェイスを作成
|
||||||
|
auto sf = SDL_CreateRGBSurfaceFrom((void *)m->frame_image, m->frame_width, m->frame_height, 32, m->frame_width * 4,
|
||||||
|
0xFF000000, 0xFF0000, 0xFF00, 0xFF);
|
||||||
|
if (sf != nullptr) {
|
||||||
|
// 枠画像からテクスチャを作成
|
||||||
|
m->texture_frameimage = SDL_CreateTextureFromSurface(m->renderer, sf);
|
||||||
|
SDL_FreeSurface(sf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SDL_SetTextureBlendMode(m->texture_frameimage, SDL_BLENDMODE_BLEND);
|
||||||
|
_update_scaling(m, scaling_x, scaling_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel_sdl::sdl_update(void)
|
||||||
|
{
|
||||||
|
if (monitor.renderer == nullptr) {
|
||||||
|
sdl_create(&monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool step_exec = _in_step_exec;
|
||||||
|
|
||||||
|
if (_texupdate_counter != _modified_counter) {
|
||||||
|
pixelcopy_t pc(nullptr, color_depth_t::rgb888_3Byte, _write_depth, false);
|
||||||
|
if (_write_depth == rgb565_2Byte) {
|
||||||
|
pc.fp_copy = pixelcopy_t::copy_rgb_fast<bgr888_t, swap565_t>;
|
||||||
|
} else if (_write_depth == rgb888_3Byte) {
|
||||||
|
pc.fp_copy = pixelcopy_t::copy_rgb_fast<bgr888_t, bgr888_t>;
|
||||||
|
} else if (_write_depth == rgb332_1Byte) {
|
||||||
|
pc.fp_copy = pixelcopy_t::copy_rgb_fast<bgr888_t, rgb332_t>;
|
||||||
|
} else if (_write_depth == grayscale_8bit) {
|
||||||
|
pc.fp_copy = pixelcopy_t::copy_rgb_fast<bgr888_t, grayscale_t>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 == SDL_LockMutex(_sdl_mutex)) {
|
||||||
|
_texupdate_counter = _modified_counter;
|
||||||
|
for (int y = 0; y < _cfg.panel_height; ++y) {
|
||||||
|
pc.src_x32 = 0;
|
||||||
|
pc.src_data = _lines_buffer[y];
|
||||||
|
pc.fp_copy(&_texturebuf[y * _cfg.panel_width], 0, _cfg.panel_width, &pc);
|
||||||
|
}
|
||||||
|
SDL_UnlockMutex(_sdl_mutex);
|
||||||
|
SDL_UpdateTexture(monitor.texture, nullptr, _texturebuf, _cfg.panel_width * sizeof(rgb888_t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int angle = monitor.frame_angle;
|
||||||
|
int target = (monitor.frame_rotation) * 90;
|
||||||
|
angle = (((target * 4) + (angle * 4) + (angle < target ? 8 : 0)) >> 3);
|
||||||
|
|
||||||
|
if (monitor.frame_angle != angle) { // 表示する向きを変える
|
||||||
|
monitor.frame_angle = angle;
|
||||||
|
sdl_invalidate();
|
||||||
|
} else if (monitor.frame_rotation & ~3u) {
|
||||||
|
monitor.frame_rotation &= 3;
|
||||||
|
monitor.frame_angle = (monitor.frame_rotation) * 90;
|
||||||
|
sdl_invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_invalidated || (_display_counter != _texupdate_counter)) {
|
||||||
|
SDL_RendererInfo info;
|
||||||
|
if (0 == SDL_GetRendererInfo(monitor.renderer, &info)) {
|
||||||
|
// ステップ実行中はVSYNCを待機しない
|
||||||
|
if (((bool)(info.flags & SDL_RENDERER_PRESENTVSYNC)) == step_exec) {
|
||||||
|
SDL_RenderSetVSync(monitor.renderer, !step_exec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
int red = 0;
|
||||||
|
int green = 0;
|
||||||
|
int blue = 0;
|
||||||
|
#if defined(M5GFX_BACK_COLOR)
|
||||||
|
red = ((M5GFX_BACK_COLOR) >> 16) & 0xFF;
|
||||||
|
green = ((M5GFX_BACK_COLOR) >> 8) & 0xFF;
|
||||||
|
blue = ((M5GFX_BACK_COLOR)) & 0xFF;
|
||||||
|
#endif
|
||||||
|
SDL_SetRenderDrawColor(monitor.renderer, red, green, blue, 0xFF);
|
||||||
|
}
|
||||||
|
SDL_RenderClear(monitor.renderer);
|
||||||
|
if (_invalidated) {
|
||||||
|
_invalidated = false;
|
||||||
|
int mw, mh;
|
||||||
|
SDL_GetRendererOutputSize(monitor.renderer, &mw, &mh);
|
||||||
|
}
|
||||||
|
render_texture(monitor.texture, monitor.frame_inner_x, monitor.frame_inner_y, _cfg.panel_width, _cfg.panel_height, angle);
|
||||||
|
render_texture(monitor.texture_frameimage, 0, 0, monitor.frame_width, monitor.frame_height, angle);
|
||||||
|
SDL_RenderPresent(monitor.renderer);
|
||||||
|
_display_counter = _texupdate_counter;
|
||||||
|
if (_invalidated) {
|
||||||
|
_invalidated = false;
|
||||||
|
SDL_SetRenderDrawColor(monitor.renderer, 0, 0, 0, 0xFF);
|
||||||
|
SDL_RenderClear(monitor.renderer);
|
||||||
|
render_texture(monitor.texture, monitor.frame_inner_x, monitor.frame_inner_y, _cfg.panel_width, _cfg.panel_height,
|
||||||
|
angle);
|
||||||
|
render_texture(monitor.texture_frameimage, 0, 0, monitor.frame_width, monitor.frame_height, angle);
|
||||||
|
SDL_RenderPresent(monitor.renderer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel_sdl::render_texture(SDL_Texture *texture, int tx, int ty, int tw, int th, float angle)
|
||||||
|
{
|
||||||
|
SDL_Point pivot;
|
||||||
|
pivot.x = (monitor.frame_width / 2.0f - tx) * (float)monitor.scaling_x;
|
||||||
|
pivot.y = (monitor.frame_height / 2.0f - ty) * (float)monitor.scaling_y;
|
||||||
|
SDL_Rect dstrect;
|
||||||
|
dstrect.w = tw * monitor.scaling_x;
|
||||||
|
dstrect.h = th * monitor.scaling_y;
|
||||||
|
int mw, mh;
|
||||||
|
SDL_GetRendererOutputSize(monitor.renderer, &mw, &mh);
|
||||||
|
dstrect.x = mw / 2.0f - pivot.x;
|
||||||
|
dstrect.y = mh / 2.0f - pivot.y;
|
||||||
|
SDL_RenderCopyEx(monitor.renderer, texture, nullptr, &dstrect, angle, &pivot, SDL_RendererFlip::SDL_FLIP_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Panel_sdl::initFrameBuffer(size_t width, size_t height)
|
||||||
|
{
|
||||||
|
uint8_t **lineArray = (uint8_t **)heap_alloc_dma(height * sizeof(uint8_t *));
|
||||||
|
if (nullptr == lineArray) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_texturebuf = (rgb888_t *)heap_alloc_dma(width * height * sizeof(rgb888_t));
|
||||||
|
|
||||||
|
/// 8byte alignment;
|
||||||
|
width = (width + 7) & ~7u;
|
||||||
|
|
||||||
|
_lines_buffer = lineArray;
|
||||||
|
memset(lineArray, 0, height * sizeof(uint8_t *));
|
||||||
|
|
||||||
|
uint8_t *framebuffer = (uint8_t *)heap_alloc_dma(width * height + 16);
|
||||||
|
|
||||||
|
auto fb = framebuffer;
|
||||||
|
{
|
||||||
|
for (size_t y = 0; y < height; ++y) {
|
||||||
|
lineArray[y] = fb;
|
||||||
|
fb += width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel_sdl::deinitFrameBuffer(void)
|
||||||
|
{
|
||||||
|
auto lines = _lines_buffer;
|
||||||
|
_lines_buffer = nullptr;
|
||||||
|
if (lines != nullptr) {
|
||||||
|
heap_free(lines[0]);
|
||||||
|
heap_free(lines);
|
||||||
|
}
|
||||||
|
if (_texturebuf) {
|
||||||
|
heap_free(_texturebuf);
|
||||||
|
_texturebuf = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
} // namespace v1
|
||||||
|
} // namespace lgfx
|
||||||
|
|
||||||
|
#endif
|
||||||
166
src/graphics/Panel_sdl.hpp
Normal file
166
src/graphics/Panel_sdl.hpp
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
/*----------------------------------------------------------------------------/
|
||||||
|
Lovyan GFX - Graphics library for embedded devices.
|
||||||
|
|
||||||
|
Original Source:
|
||||||
|
https://github.com/lovyan03/LovyanGFX/
|
||||||
|
|
||||||
|
Licence:
|
||||||
|
[FreeBSD](https://github.com/lovyan03/LovyanGFX/blob/master/license.txt)
|
||||||
|
|
||||||
|
Author:
|
||||||
|
[lovyan03](https://twitter.com/lovyan03)
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
[ciniml](https://github.com/ciniml)
|
||||||
|
[mongonta0716](https://github.com/mongonta0716)
|
||||||
|
[tobozo](https://github.com/tobozo)
|
||||||
|
|
||||||
|
Porting for SDL:
|
||||||
|
[imliubo](https://github.com/imliubo)
|
||||||
|
/----------------------------------------------------------------------------*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define SDL_MAIN_HANDLED
|
||||||
|
// cppcheck-suppress preprocessorErrorDirective
|
||||||
|
#if __has_include(<SDL2/SDL.h>)
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <SDL2/SDL_main.h>
|
||||||
|
#elif __has_include(<SDL.h>)
|
||||||
|
#include <SDL.h>
|
||||||
|
#include <SDL_main.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(SDL_h_)
|
||||||
|
#include "lgfx/v1/Touch.hpp"
|
||||||
|
#include "lgfx/v1/misc/range.hpp"
|
||||||
|
#include "lgfx/v1/panel/Panel_FrameBufferBase.hpp"
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace lgfx
|
||||||
|
{
|
||||||
|
inline namespace v1
|
||||||
|
{
|
||||||
|
|
||||||
|
struct Panel_sdl;
|
||||||
|
struct monitor_t {
|
||||||
|
SDL_Window *window = nullptr;
|
||||||
|
SDL_Renderer *renderer = nullptr;
|
||||||
|
SDL_Texture *texture = nullptr;
|
||||||
|
SDL_Texture *texture_frameimage = nullptr;
|
||||||
|
Panel_sdl *panel = nullptr;
|
||||||
|
|
||||||
|
// 外枠
|
||||||
|
const void *frame_image = 0;
|
||||||
|
uint_fast16_t frame_width = 0;
|
||||||
|
uint_fast16_t frame_height = 0;
|
||||||
|
uint_fast16_t frame_inner_x = 0;
|
||||||
|
uint_fast16_t frame_inner_y = 0;
|
||||||
|
int_fast16_t frame_rotation = 0;
|
||||||
|
int_fast16_t frame_angle = 0;
|
||||||
|
|
||||||
|
float scaling_x = 1;
|
||||||
|
float scaling_y = 1;
|
||||||
|
int_fast16_t touch_x, touch_y;
|
||||||
|
bool touched = false;
|
||||||
|
bool closing = false;
|
||||||
|
};
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct Touch_sdl : public ITouch {
|
||||||
|
bool init(void) override { return true; }
|
||||||
|
void wakeup(void) override {}
|
||||||
|
void sleep(void) override {}
|
||||||
|
bool isEnable(void) override { return true; };
|
||||||
|
uint_fast8_t getTouchRaw(touch_point_t *tp, uint_fast8_t count) override { return 0; }
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct Panel_sdl : public Panel_FrameBufferBase {
|
||||||
|
static constexpr size_t EMULATED_GPIO_MAX = 128;
|
||||||
|
static volatile uint8_t _gpio_dummy_values[EMULATED_GPIO_MAX];
|
||||||
|
|
||||||
|
public:
|
||||||
|
Panel_sdl(void);
|
||||||
|
virtual ~Panel_sdl(void);
|
||||||
|
|
||||||
|
bool init(bool use_reset) override;
|
||||||
|
|
||||||
|
color_depth_t setColorDepth(color_depth_t depth) override;
|
||||||
|
|
||||||
|
void display(uint_fast16_t x, uint_fast16_t y, uint_fast16_t w, uint_fast16_t h) override;
|
||||||
|
|
||||||
|
// void setInvert(bool invert) override {}
|
||||||
|
void drawPixelPreclipped(uint_fast16_t x, uint_fast16_t y, uint32_t rawcolor) override;
|
||||||
|
void writeFillRectPreclipped(uint_fast16_t x, uint_fast16_t y, uint_fast16_t w, uint_fast16_t h, uint32_t rawcolor) override;
|
||||||
|
void writeBlock(uint32_t rawcolor, uint32_t length) override;
|
||||||
|
void writeImage(uint_fast16_t x, uint_fast16_t y, uint_fast16_t w, uint_fast16_t h, pixelcopy_t *param,
|
||||||
|
bool use_dma) override;
|
||||||
|
void writeImageARGB(uint_fast16_t x, uint_fast16_t y, uint_fast16_t w, uint_fast16_t h, pixelcopy_t *param) override;
|
||||||
|
void writePixels(pixelcopy_t *param, uint32_t len, bool use_dma) override;
|
||||||
|
|
||||||
|
uint_fast8_t getTouchRaw(touch_point_t *tp, uint_fast8_t count) override;
|
||||||
|
|
||||||
|
void setWindowTitle(const char *title);
|
||||||
|
void setScaling(uint_fast8_t scaling_x, uint_fast8_t scaling_y);
|
||||||
|
void setFrameImage(const void *frame_image, int frame_width, int frame_height, int inner_x, int inner_y);
|
||||||
|
void setFrameRotation(uint_fast16_t frame_rotaion);
|
||||||
|
void setBrightness(uint8_t brightness) override{};
|
||||||
|
|
||||||
|
static volatile void gpio_hi(uint32_t pin) { _gpio_dummy_values[pin & (EMULATED_GPIO_MAX - 1)] = 1; }
|
||||||
|
static volatile void gpio_lo(uint32_t pin) { _gpio_dummy_values[pin & (EMULATED_GPIO_MAX - 1)] = 0; }
|
||||||
|
static volatile bool gpio_in(uint32_t pin) { return _gpio_dummy_values[pin & (EMULATED_GPIO_MAX - 1)]; }
|
||||||
|
|
||||||
|
static int setup(void);
|
||||||
|
static int loop(void);
|
||||||
|
static int close(void);
|
||||||
|
|
||||||
|
static int main(int (*fn)(bool *), uint32_t msec_step_exec = 512);
|
||||||
|
|
||||||
|
static void setShortcutKeymod(SDL_Keymod keymod) { _keymod = keymod; }
|
||||||
|
|
||||||
|
struct KeyCodeMapping_t {
|
||||||
|
SDL_KeyCode keycode = SDLK_UNKNOWN;
|
||||||
|
uint8_t gpio = 0;
|
||||||
|
};
|
||||||
|
static void addKeyCodeMapping(SDL_KeyCode keyCode, uint8_t gpio);
|
||||||
|
static int getKeyCodeMapping(SDL_KeyCode keyCode);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
const char *_window_title = "LGFX Simulator";
|
||||||
|
SDL_mutex *_sdl_mutex = nullptr;
|
||||||
|
|
||||||
|
void sdl_create(monitor_t *m);
|
||||||
|
void sdl_update(void);
|
||||||
|
|
||||||
|
touch_point_t _touch_point;
|
||||||
|
monitor_t monitor;
|
||||||
|
|
||||||
|
rgb888_t *_texturebuf = nullptr;
|
||||||
|
uint_fast16_t _modified_counter;
|
||||||
|
uint_fast16_t _texupdate_counter;
|
||||||
|
uint_fast16_t _display_counter;
|
||||||
|
bool _invalidated;
|
||||||
|
|
||||||
|
static void _event_proc(void);
|
||||||
|
static void _update_proc(void);
|
||||||
|
static void _update_scaling(monitor_t *m, float sx, float sy);
|
||||||
|
void sdl_invalidate(void) { _invalidated = true; }
|
||||||
|
void render_texture(SDL_Texture *texture, int tx, int ty, int tw, int th, float angle);
|
||||||
|
bool initFrameBuffer(size_t width, size_t height);
|
||||||
|
void deinitFrameBuffer(void);
|
||||||
|
|
||||||
|
static SDL_Keymod _keymod;
|
||||||
|
|
||||||
|
struct lock_t {
|
||||||
|
lock_t(Panel_sdl *parent);
|
||||||
|
~lock_t();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Panel_sdl *_parent;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
} // namespace v1
|
||||||
|
} // namespace lgfx
|
||||||
|
#endif
|
||||||
@@ -25,6 +25,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#include "PowerMon.h"
|
#include "PowerMon.h"
|
||||||
#include "Throttle.h"
|
#include "Throttle.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
|
#include "meshUtils.h"
|
||||||
#if HAS_SCREEN
|
#if HAS_SCREEN
|
||||||
#include <OLEDDisplay.h>
|
#include <OLEDDisplay.h>
|
||||||
|
|
||||||
@@ -58,7 +59,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#include "mesh-pb-constants.h"
|
#include "mesh-pb-constants.h"
|
||||||
#include "mesh/Channels.h"
|
#include "mesh/Channels.h"
|
||||||
#include "mesh/generated/meshtastic/deviceonly.pb.h"
|
#include "mesh/generated/meshtastic/deviceonly.pb.h"
|
||||||
#include "meshUtils.h"
|
|
||||||
#include "modules/ExternalNotificationModule.h"
|
#include "modules/ExternalNotificationModule.h"
|
||||||
#include "modules/TextMessageModule.h"
|
#include "modules/TextMessageModule.h"
|
||||||
#include "modules/WaypointModule.h"
|
#include "modules/WaypointModule.h"
|
||||||
@@ -83,6 +83,11 @@ extern uint16_t TFT_MESH;
|
|||||||
#include "platform/portduino/PortduinoGlue.h"
|
#include "platform/portduino/PortduinoGlue.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(T_LORA_PAGER)
|
||||||
|
// KB backlight control
|
||||||
|
#include "input/cardKbI2cImpl.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace meshtastic; /** @todo remove */
|
using namespace meshtastic; /** @todo remove */
|
||||||
|
|
||||||
namespace graphics
|
namespace graphics
|
||||||
@@ -95,7 +100,7 @@ namespace graphics
|
|||||||
#define NUM_EXTRA_FRAMES 3 // text message and debug frame
|
#define NUM_EXTRA_FRAMES 3 // text message and debug frame
|
||||||
// if defined a pixel will blink to show redraws
|
// if defined a pixel will blink to show redraws
|
||||||
// #define SHOW_REDRAWS
|
// #define SHOW_REDRAWS
|
||||||
|
#define ASCII_BELL '\x07'
|
||||||
// A text message frame + debug frame + all the node infos
|
// A text message frame + debug frame + all the node infos
|
||||||
FrameCallback *normalFrames;
|
FrameCallback *normalFrames;
|
||||||
static uint32_t targetFramerate = IDLE_FRAMERATE;
|
static uint32_t targetFramerate = IDLE_FRAMERATE;
|
||||||
@@ -355,6 +360,14 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
|
|||||||
#elif defined(USE_SSD1306)
|
#elif defined(USE_SSD1306)
|
||||||
dispdev = new SSD1306Wire(address.address, -1, -1, geometry,
|
dispdev = new SSD1306Wire(address.address, -1, -1, geometry,
|
||||||
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
||||||
|
#elif defined(USE_SPISSD1306)
|
||||||
|
dispdev = new SSD1306Spi(SSD1306_RESET, SSD1306_RS, SSD1306_NSS, GEOMETRY_64_48);
|
||||||
|
if (!dispdev->init()) {
|
||||||
|
LOG_DEBUG("Error: SSD1306 not detected!");
|
||||||
|
} else {
|
||||||
|
static_cast<SSD1306Spi *>(dispdev)->setHorizontalOffset(32);
|
||||||
|
LOG_INFO("SSD1306 init success");
|
||||||
|
}
|
||||||
#elif defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7789_CS) || \
|
#elif defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7789_CS) || \
|
||||||
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS)
|
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS)
|
||||||
dispdev = new TFTDisplay(address.address, -1, -1, geometry,
|
dispdev = new TFTDisplay(address.address, -1, -1, geometry,
|
||||||
@@ -440,7 +453,7 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
dispdev->displayOn();
|
dispdev->displayOn();
|
||||||
#ifdef HELTEC_TRACKER_V1_X
|
#if defined(HELTEC_TRACKER_V1_X) || defined(HELTEC_WIRELESS_TRACKER_V2)
|
||||||
ui->init();
|
ui->init();
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ST7789
|
#ifdef USE_ST7789
|
||||||
@@ -545,7 +558,7 @@ void Screen::setup()
|
|||||||
// === Apply loaded brightness ===
|
// === Apply loaded brightness ===
|
||||||
#if defined(ST7789_CS)
|
#if defined(ST7789_CS)
|
||||||
static_cast<TFTDisplay *>(dispdev)->setDisplayBrightness(brightness);
|
static_cast<TFTDisplay *>(dispdev)->setDisplayBrightness(brightness);
|
||||||
#elif defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107)
|
#elif defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107) || defined(USE_SPISSD1306)
|
||||||
dispdev->setBrightness(brightness);
|
dispdev->setBrightness(brightness);
|
||||||
#endif
|
#endif
|
||||||
LOG_INFO("Applied screen brightness: %d", brightness);
|
LOG_INFO("Applied screen brightness: %d", brightness);
|
||||||
@@ -592,7 +605,7 @@ void Screen::setup()
|
|||||||
static_cast<TFTDisplay *>(dispdev)->flipScreenVertically();
|
static_cast<TFTDisplay *>(dispdev)->flipScreenVertically();
|
||||||
#elif defined(USE_ST7789)
|
#elif defined(USE_ST7789)
|
||||||
static_cast<ST7789Spi *>(dispdev)->flipScreenVertically();
|
static_cast<ST7789Spi *>(dispdev)->flipScreenVertically();
|
||||||
#else
|
#elif !defined(M5STACK_UNITC6L)
|
||||||
dispdev->flipScreenVertically();
|
dispdev->flipScreenVertically();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -647,6 +660,19 @@ void Screen::setup()
|
|||||||
MeshModule::observeUIEvents(&uiFrameEventObserver);
|
MeshModule::observeUIEvents(&uiFrameEventObserver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Screen::setOn(bool on, FrameCallback einkScreensaver)
|
||||||
|
{
|
||||||
|
#if defined(T_LORA_PAGER)
|
||||||
|
if (cardKbI2cImpl)
|
||||||
|
cardKbI2cImpl->toggleBacklight(on);
|
||||||
|
#endif
|
||||||
|
if (!on)
|
||||||
|
// We handle off commands immediately, because they might be called because the CPU is shutting down
|
||||||
|
handleSetOn(false, einkScreensaver);
|
||||||
|
else
|
||||||
|
enqueueCmd(ScreenCmd{.cmd = Cmd::SET_ON});
|
||||||
|
}
|
||||||
|
|
||||||
void Screen::forceDisplay(bool forceUiUpdate)
|
void Screen::forceDisplay(bool forceUiUpdate)
|
||||||
{
|
{
|
||||||
// Nasty hack to force epaper updates for 'key' frames. FIXME, cleanup.
|
// Nasty hack to force epaper updates for 'key' frames. FIXME, cleanup.
|
||||||
@@ -730,7 +756,11 @@ int32_t Screen::runOnce()
|
|||||||
|
|
||||||
#ifndef DISABLE_WELCOME_UNSET
|
#ifndef DISABLE_WELCOME_UNSET
|
||||||
if (!NotificationRenderer::isOverlayBannerShowing() && config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
|
if (!NotificationRenderer::isOverlayBannerShowing() && config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
menuHandler::LoraRegionPicker();
|
||||||
|
#else
|
||||||
menuHandler::OnboardMessage();
|
menuHandler::OnboardMessage();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (!NotificationRenderer::isOverlayBannerShowing() && rebootAtMsec != 0) {
|
if (!NotificationRenderer::isOverlayBannerShowing() && rebootAtMsec != 0) {
|
||||||
@@ -943,8 +973,12 @@ void Screen::setFrames(FrameFocus focus)
|
|||||||
#if defined(DISPLAY_CLOCK_FRAME)
|
#if defined(DISPLAY_CLOCK_FRAME)
|
||||||
if (!hiddenFrames.clock) {
|
if (!hiddenFrames.clock) {
|
||||||
fsi.positions.clock = numframes;
|
fsi.positions.clock = numframes;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
normalFrames[numframes++] = graphics::ClockRenderer::drawAnalogClockFrame;
|
||||||
|
#else
|
||||||
normalFrames[numframes++] = uiconfig.is_clockface_analog ? graphics::ClockRenderer::drawAnalogClockFrame
|
normalFrames[numframes++] = uiconfig.is_clockface_analog ? graphics::ClockRenderer::drawAnalogClockFrame
|
||||||
: graphics::ClockRenderer::drawDigitalClockFrame;
|
: graphics::ClockRenderer::drawDigitalClockFrame;
|
||||||
|
#endif
|
||||||
indicatorIcons.push_back(digital_icon_clock);
|
indicatorIcons.push_back(digital_icon_clock);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -1021,7 +1055,7 @@ void Screen::setFrames(FrameFocus focus)
|
|||||||
if (!hiddenFrames.chirpy) {
|
if (!hiddenFrames.chirpy) {
|
||||||
fsi.positions.chirpy = numframes;
|
fsi.positions.chirpy = numframes;
|
||||||
normalFrames[numframes++] = graphics::DebugRenderer::drawChirpy;
|
normalFrames[numframes++] = graphics::DebugRenderer::drawChirpy;
|
||||||
indicatorIcons.push_back(small_chirpy);
|
indicatorIcons.push_back(chirpy_small);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
|
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
|
||||||
@@ -1283,7 +1317,8 @@ void Screen::blink()
|
|||||||
delay(50);
|
delay(50);
|
||||||
count = count - 1;
|
count = count - 1;
|
||||||
}
|
}
|
||||||
// The dispdev->setBrightness does not work for t-deck display, it seems to run the setBrightness function in OLEDDisplay.
|
// The dispdev->setBrightness does not work for t-deck display, it seems to run the setBrightness function in
|
||||||
|
// OLEDDisplay.
|
||||||
dispdev->setBrightness(brightness);
|
dispdev->setBrightness(brightness);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1371,6 +1406,10 @@ void Screen::handleShowNextFrame()
|
|||||||
|
|
||||||
void Screen::setFastFramerate()
|
void Screen::setFastFramerate()
|
||||||
{
|
{
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
dispdev->clear();
|
||||||
|
dispdev->display();
|
||||||
|
#endif
|
||||||
// We are about to start a transition so speed up fps
|
// We are about to start a transition so speed up fps
|
||||||
targetFramerate = SCREEN_TRANSITION_FRAMERATE;
|
targetFramerate = SCREEN_TRANSITION_FRAMERATE;
|
||||||
|
|
||||||
@@ -1389,6 +1428,9 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
|
|||||||
}
|
}
|
||||||
nodeDB->updateGUI = false;
|
nodeDB->updateGUI = false;
|
||||||
break;
|
break;
|
||||||
|
case STATUS_TYPE_POWER:
|
||||||
|
forceDisplay(true);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1419,36 +1461,61 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
|
|||||||
}
|
}
|
||||||
// === Prepare banner content ===
|
// === Prepare banner content ===
|
||||||
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet->from);
|
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet->from);
|
||||||
|
const meshtastic_Channel channel =
|
||||||
|
channels.getByIndex(packet->channel ? packet->channel : channels.getPrimaryIndex());
|
||||||
const char *longName = (node && node->has_user) ? node->user.long_name : nullptr;
|
const char *longName = (node && node->has_user) ? node->user.long_name : nullptr;
|
||||||
|
|
||||||
const char *msgRaw = reinterpret_cast<const char *>(packet->decoded.payload.bytes);
|
const char *msgRaw = reinterpret_cast<const char *>(packet->decoded.payload.bytes);
|
||||||
|
|
||||||
char banner[256];
|
char banner[256];
|
||||||
|
|
||||||
// Check for bell character in message to determine alert type
|
|
||||||
bool isAlert = false;
|
bool isAlert = false;
|
||||||
for (size_t i = 0; i < packet->decoded.payload.size && i < 100; i++) {
|
|
||||||
if (msgRaw[i] == '\x07') {
|
|
||||||
isAlert = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (moduleConfig.external_notification.alert_bell || moduleConfig.external_notification.alert_bell_vibra ||
|
||||||
|
moduleConfig.external_notification.alert_bell_buzzer)
|
||||||
|
// Check for bell character to determine if this message is an alert
|
||||||
|
for (size_t i = 0; i < packet->decoded.payload.size && i < 100; i++) {
|
||||||
|
if (msgRaw[i] == ASCII_BELL) {
|
||||||
|
isAlert = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlike generic messages, alerts (when enabled via the ext notif module) ignore any
|
||||||
|
// 'mute' preferences set to any specific node or channel.
|
||||||
if (isAlert) {
|
if (isAlert) {
|
||||||
if (longName && longName[0]) {
|
if (longName && longName[0]) {
|
||||||
snprintf(banner, sizeof(banner), "Alert Received from\n%s", longName);
|
snprintf(banner, sizeof(banner), "Alert Received from\n%s", longName);
|
||||||
} else {
|
} else {
|
||||||
strcpy(banner, "Alert Received");
|
strcpy(banner, "Alert Received");
|
||||||
}
|
}
|
||||||
} else {
|
screen->showSimpleBanner(banner, 3000);
|
||||||
|
} else if (!channel.settings.has_module_settings || !channel.settings.module_settings.is_muted) {
|
||||||
if (longName && longName[0]) {
|
if (longName && longName[0]) {
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
strcpy(banner, "New Message");
|
||||||
|
#else
|
||||||
snprintf(banner, sizeof(banner), "New Message from\n%s", longName);
|
snprintf(banner, sizeof(banner), "New Message from\n%s", longName);
|
||||||
|
#endif
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
strcpy(banner, "New Message");
|
strcpy(banner, "New Message");
|
||||||
}
|
}
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
screen->setOn(true);
|
||||||
|
screen->showSimpleBanner(banner, 1500);
|
||||||
|
if (config.device.buzzer_mode != meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY ||
|
||||||
|
(isAlert && moduleConfig.external_notification.alert_bell_buzzer) ||
|
||||||
|
(!isBroadcast(packet->to) && isToUs(packet))) {
|
||||||
|
// Beep if not in DIRECT_MSG_ONLY mode or if in DIRECT_MSG_ONLY mode and either
|
||||||
|
// - packet contains an alert and alert bell buzzer is enabled
|
||||||
|
// - packet is a non-broadcast that is addressed to this node
|
||||||
|
playLongBeep();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
screen->showSimpleBanner(banner, 3000);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
screen->showSimpleBanner(banner, 3000);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1546,7 +1613,11 @@ int Screen::handleInputEvent(const InputEvent *event)
|
|||||||
if (devicestate.rx_text_message.from) {
|
if (devicestate.rx_text_message.from) {
|
||||||
menuHandler::messageResponseMenu();
|
menuHandler::messageResponseMenu();
|
||||||
} else {
|
} else {
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
menuHandler::textMessageMenu();
|
||||||
|
#else
|
||||||
menuHandler::textMessageBaseMenu();
|
menuHandler::textMessageBaseMenu();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
} else if (framesetInfo.positions.firstFavorite != 255 &&
|
} else if (framesetInfo.positions.firstFavorite != 255 &&
|
||||||
this->ui->getUiState()->currentFrame >= framesetInfo.positions.firstFavorite &&
|
this->ui->getUiState()->currentFrame >= framesetInfo.positions.firstFavorite &&
|
||||||
@@ -1605,13 +1676,15 @@ bool shouldWakeOnReceivedMessage()
|
|||||||
/*
|
/*
|
||||||
The goal here is to determine when we do NOT wake up the screen on message received:
|
The goal here is to determine when we do NOT wake up the screen on message received:
|
||||||
- Any ext. notifications are turned on
|
- Any ext. notifications are turned on
|
||||||
- If role is not client / client_mute
|
- If role is not CLIENT / CLIENT_MUTE / CLIENT_HIDDEN / CLIENT_BASE
|
||||||
- If the battery level is very low
|
- If the battery level is very low
|
||||||
*/
|
*/
|
||||||
if (moduleConfig.external_notification.enabled) {
|
if (moduleConfig.external_notification.enabled) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!meshtastic_Config_DeviceConfig_Role_CLIENT && !meshtastic_Config_DeviceConfig_Role_CLIENT_MUTE) {
|
if (!IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_CLIENT,
|
||||||
|
meshtastic_Config_DeviceConfig_Role_CLIENT_MUTE, meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN,
|
||||||
|
meshtastic_Config_DeviceConfig_Role_CLIENT_BASE)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (powerStatus && powerStatus->getBatteryChargePercent() < 10) {
|
if (powerStatus && powerStatus->getBatteryChargePercent() < 10) {
|
||||||
|
|||||||
@@ -81,6 +81,8 @@ class Screen
|
|||||||
#include <SSD1306Wire.h>
|
#include <SSD1306Wire.h>
|
||||||
#elif defined(USE_ST7789)
|
#elif defined(USE_ST7789)
|
||||||
#include <ST7789Spi.h>
|
#include <ST7789Spi.h>
|
||||||
|
#elif defined(USE_SPISSD1306)
|
||||||
|
#include <SSD1306Spi.h>
|
||||||
#else
|
#else
|
||||||
// the SH1106/SSD1306 variant is auto-detected
|
// the SH1106/SSD1306 variant is auto-detected
|
||||||
#include <AutoOLEDWire.h>
|
#include <AutoOLEDWire.h>
|
||||||
@@ -257,15 +259,7 @@ class Screen : public concurrency::OSThread
|
|||||||
void setup();
|
void setup();
|
||||||
|
|
||||||
/// Turns the screen on/off. Optionally, pass a custom screensaver frame for E-Ink
|
/// Turns the screen on/off. Optionally, pass a custom screensaver frame for E-Ink
|
||||||
void setOn(bool on, FrameCallback einkScreensaver = NULL)
|
void setOn(bool on, FrameCallback einkScreensaver = NULL);
|
||||||
{
|
|
||||||
if (!on)
|
|
||||||
// We handle off commands immediately, because they might be called because the CPU is shutting down
|
|
||||||
handleSetOn(false, einkScreensaver);
|
|
||||||
else
|
|
||||||
enqueueCmd(ScreenCmd{.cmd = Cmd::SET_ON});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare the display for the unit going to the lowest power mode possible. Most screens will just
|
* 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
|
* poweroff, but eink screens will show a "I'm sleeping" graphic, possibly with a QR code
|
||||||
|
|||||||
@@ -79,6 +79,10 @@
|
|||||||
#define FONT_SMALL FONT_MEDIUM_LOCAL // Height: 19
|
#define FONT_SMALL FONT_MEDIUM_LOCAL // Height: 19
|
||||||
#define FONT_MEDIUM FONT_LARGE_LOCAL // Height: 28
|
#define FONT_MEDIUM FONT_LARGE_LOCAL // Height: 28
|
||||||
#define FONT_LARGE FONT_LARGE_LOCAL // Height: 28
|
#define FONT_LARGE FONT_LARGE_LOCAL // Height: 28
|
||||||
|
#elif defined(M5STACK_UNITC6L)
|
||||||
|
#define FONT_SMALL FONT_SMALL_LOCAL // Height: 13
|
||||||
|
#define FONT_MEDIUM FONT_SMALL_LOCAL // Height: 13
|
||||||
|
#define FONT_LARGE FONT_SMALL_LOCAL // Height: 13
|
||||||
#else
|
#else
|
||||||
#define FONT_SMALL FONT_SMALL_LOCAL // Height: 13
|
#define FONT_SMALL FONT_SMALL_LOCAL // Height: 13
|
||||||
#define FONT_MEDIUM FONT_MEDIUM_LOCAL // Height: 19
|
#define FONT_MEDIUM FONT_MEDIUM_LOCAL // Height: 19
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#include "graphics/SharedUIDisplay.h"
|
#include "configuration.h"
|
||||||
|
#if HAS_SCREEN
|
||||||
#include "RTC.h"
|
#include "RTC.h"
|
||||||
#include "graphics/ScreenFonts.h"
|
#include "graphics/ScreenFonts.h"
|
||||||
|
#include "graphics/SharedUIDisplay.h"
|
||||||
#include "graphics/draw/UIRenderer.h"
|
#include "graphics/draw/UIRenderer.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "meshtastic/config.pb.h"
|
#include "meshtastic/config.pb.h"
|
||||||
@@ -129,7 +131,7 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
|
|||||||
|
|
||||||
int batteryX = 1;
|
int batteryX = 1;
|
||||||
int batteryY = HEADER_OFFSET_Y + 1;
|
int batteryY = HEADER_OFFSET_Y + 1;
|
||||||
|
#if !defined(M5STACK_UNITC6L)
|
||||||
// === Battery Icons ===
|
// === Battery Icons ===
|
||||||
if (usbPowered && !isCharging) { // This is a basic check to determine USB Powered is flagged but not charging
|
if (usbPowered && !isCharging) { // This is a basic check to determine USB Powered is flagged but not charging
|
||||||
batteryX += 1;
|
batteryX += 1;
|
||||||
@@ -284,7 +286,7 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
|
|||||||
int iconX = iconRightEdge - mute_symbol_big_width;
|
int iconX = iconRightEdge - mute_symbol_big_width;
|
||||||
int iconY = textY + (FONT_HEIGHT_SMALL - mute_symbol_big_height) / 2;
|
int iconY = textY + (FONT_HEIGHT_SMALL - mute_symbol_big_height) / 2;
|
||||||
|
|
||||||
if (isInverted) {
|
if (isInverted && !force_no_invert) {
|
||||||
display->setColor(WHITE);
|
display->setColor(WHITE);
|
||||||
display->fillRect(iconX - 1, iconY - 1, mute_symbol_big_width + 2, mute_symbol_big_height + 2);
|
display->fillRect(iconX - 1, iconY - 1, mute_symbol_big_width + 2, mute_symbol_big_height + 2);
|
||||||
display->setColor(BLACK);
|
display->setColor(BLACK);
|
||||||
@@ -368,7 +370,7 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
display->setColor(WHITE); // Reset for other UI
|
display->setColor(WHITE); // Reset for other UI
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -423,3 +425,4 @@ std::string sanitizeString(const std::string &input)
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace graphics
|
} // namespace graphics
|
||||||
|
#endif
|
||||||
@@ -751,10 +751,8 @@ static LGFX *tft = nullptr;
|
|||||||
|
|
||||||
static TFT_eSPI *tft = nullptr; // Invoke library, pins defined in User_Setup.h
|
static TFT_eSPI *tft = nullptr; // Invoke library, pins defined in User_Setup.h
|
||||||
#elif ARCH_PORTDUINO
|
#elif ARCH_PORTDUINO
|
||||||
|
#include "Panel_sdl.hpp"
|
||||||
#include <LovyanGFX.hpp> // Graphics and font library for ST7735 driver chip
|
#include <LovyanGFX.hpp> // Graphics and font library for ST7735 driver chip
|
||||||
#if defined(LGFX_SDL)
|
|
||||||
#include <lgfx/v1/platforms/sdl/Panel_sdl.hpp>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class LGFX : public lgfx::LGFX_Device
|
class LGFX : public lgfx::LGFX_Device
|
||||||
{
|
{
|
||||||
@@ -783,10 +781,10 @@ class LGFX : public lgfx::LGFX_Device
|
|||||||
_panel_instance = new lgfx::Panel_ILI9488;
|
_panel_instance = new lgfx::Panel_ILI9488;
|
||||||
else if (portduino_config.displayPanel == hx8357d)
|
else if (portduino_config.displayPanel == hx8357d)
|
||||||
_panel_instance = new lgfx::Panel_HX8357D;
|
_panel_instance = new lgfx::Panel_HX8357D;
|
||||||
#if defined(LGFX_SDL)
|
#if defined(SDL_h_)
|
||||||
else if (portduino_config.displayPanel == x11) {
|
|
||||||
|
else if (portduino_config.displayPanel == x11)
|
||||||
_panel_instance = new lgfx::Panel_sdl;
|
_panel_instance = new lgfx::Panel_sdl;
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
else {
|
else {
|
||||||
_panel_instance = new lgfx::Panel_NULL;
|
_panel_instance = new lgfx::Panel_NULL;
|
||||||
@@ -799,8 +797,9 @@ class LGFX : public lgfx::LGFX_Device
|
|||||||
|
|
||||||
buscfg.pin_dc = portduino_config.displayDC.pin; // Set SPI DC pin number (-1 = disable)
|
buscfg.pin_dc = portduino_config.displayDC.pin; // Set SPI DC pin number (-1 = disable)
|
||||||
|
|
||||||
_bus_instance.config(buscfg); // applies the set value to the bus.
|
_bus_instance.config(buscfg); // applies the set value to the bus.
|
||||||
_panel_instance->setBus(&_bus_instance); // set the bus on the panel.
|
if (portduino_config.displayPanel != x11)
|
||||||
|
_panel_instance->setBus(&_bus_instance); // set the bus on the panel.
|
||||||
|
|
||||||
auto cfg = _panel_instance->config(); // Gets a structure for display panel settings.
|
auto cfg = _panel_instance->config(); // Gets a structure for display panel settings.
|
||||||
LOG_DEBUG("Width: %d, Height: %d", portduino_config.displayWidth, portduino_config.displayHeight);
|
LOG_DEBUG("Width: %d, Height: %d", portduino_config.displayWidth, portduino_config.displayHeight);
|
||||||
@@ -848,7 +847,7 @@ class LGFX : public lgfx::LGFX_Device
|
|||||||
_touch_instance->config(touch_cfg);
|
_touch_instance->config(touch_cfg);
|
||||||
_panel_instance->setTouch(_touch_instance);
|
_panel_instance->setTouch(_touch_instance);
|
||||||
}
|
}
|
||||||
#if defined(LGFX_SDL)
|
#if defined(SDL_h_)
|
||||||
if (portduino_config.displayPanel == x11) {
|
if (portduino_config.displayPanel == x11) {
|
||||||
lgfx::Panel_sdl *sdl_panel_ = (lgfx::Panel_sdl *)_panel_instance;
|
lgfx::Panel_sdl *sdl_panel_ = (lgfx::Panel_sdl *)_panel_instance;
|
||||||
sdl_panel_->setup();
|
sdl_panel_->setup();
|
||||||
@@ -1237,7 +1236,7 @@ void TFTDisplay::display(bool fromBlank)
|
|||||||
|
|
||||||
void TFTDisplay::sdlLoop()
|
void TFTDisplay::sdlLoop()
|
||||||
{
|
{
|
||||||
#if defined(LGFX_SDL)
|
#if defined(SDL_h_)
|
||||||
static int lastPressed = 0;
|
static int lastPressed = 0;
|
||||||
static int shuttingDown = false;
|
static int shuttingDown = false;
|
||||||
if (portduino_config.displayPanel == x11) {
|
if (portduino_config.displayPanel == x11) {
|
||||||
@@ -1247,27 +1246,26 @@ void TFTDisplay::sdlLoop()
|
|||||||
InputEvent event = {.inputEvent = (input_broker_event)INPUT_BROKER_SHUTDOWN, .kbchar = 0, .touchX = 0, .touchY = 0};
|
InputEvent event = {.inputEvent = (input_broker_event)INPUT_BROKER_SHUTDOWN, .kbchar = 0, .touchX = 0, .touchY = 0};
|
||||||
inputBroker->injectInputEvent(&event);
|
inputBroker->injectInputEvent(&event);
|
||||||
}
|
}
|
||||||
|
|
||||||
// debounce
|
// debounce
|
||||||
if (lastPressed != 0 && !lgfx::v1::gpio_in(lastPressed))
|
if (lastPressed != 0 && !sdl_panel_->gpio_in(lastPressed))
|
||||||
return;
|
return;
|
||||||
if (!lgfx::v1::gpio_in(37)) {
|
if (!sdl_panel_->gpio_in(37)) {
|
||||||
lastPressed = 37;
|
lastPressed = 37;
|
||||||
InputEvent event = {.inputEvent = (input_broker_event)INPUT_BROKER_RIGHT, .kbchar = 0, .touchX = 0, .touchY = 0};
|
InputEvent event = {.inputEvent = (input_broker_event)INPUT_BROKER_RIGHT, .kbchar = 0, .touchX = 0, .touchY = 0};
|
||||||
inputBroker->injectInputEvent(&event);
|
inputBroker->injectInputEvent(&event);
|
||||||
} else if (!lgfx::v1::gpio_in(36)) {
|
} else if (!sdl_panel_->gpio_in(36)) {
|
||||||
lastPressed = 36;
|
lastPressed = 36;
|
||||||
InputEvent event = {.inputEvent = (input_broker_event)INPUT_BROKER_UP, .kbchar = 0, .touchX = 0, .touchY = 0};
|
InputEvent event = {.inputEvent = (input_broker_event)INPUT_BROKER_UP, .kbchar = 0, .touchX = 0, .touchY = 0};
|
||||||
inputBroker->injectInputEvent(&event);
|
inputBroker->injectInputEvent(&event);
|
||||||
} else if (!lgfx::v1::gpio_in(38)) {
|
} else if (!sdl_panel_->gpio_in(38)) {
|
||||||
lastPressed = 38;
|
lastPressed = 38;
|
||||||
InputEvent event = {.inputEvent = (input_broker_event)INPUT_BROKER_DOWN, .kbchar = 0, .touchX = 0, .touchY = 0};
|
InputEvent event = {.inputEvent = (input_broker_event)INPUT_BROKER_DOWN, .kbchar = 0, .touchX = 0, .touchY = 0};
|
||||||
inputBroker->injectInputEvent(&event);
|
inputBroker->injectInputEvent(&event);
|
||||||
} else if (!lgfx::v1::gpio_in(39)) {
|
} else if (!sdl_panel_->gpio_in(39)) {
|
||||||
lastPressed = 39;
|
lastPressed = 39;
|
||||||
InputEvent event = {.inputEvent = (input_broker_event)INPUT_BROKER_LEFT, .kbchar = 0, .touchX = 0, .touchY = 0};
|
InputEvent event = {.inputEvent = (input_broker_event)INPUT_BROKER_LEFT, .kbchar = 0, .touchX = 0, .touchY = 0};
|
||||||
inputBroker->injectInputEvent(&event);
|
inputBroker->injectInputEvent(&event);
|
||||||
} else if (!lgfx::v1::gpio_in(SDL_SCANCODE_KP_ENTER)) {
|
} else if (!sdl_panel_->gpio_in(SDL_SCANCODE_KP_ENTER)) {
|
||||||
lastPressed = SDL_SCANCODE_KP_ENTER;
|
lastPressed = SDL_SCANCODE_KP_ENTER;
|
||||||
InputEvent event = {.inputEvent = (input_broker_event)INPUT_BROKER_SELECT, .kbchar = 0, .touchX = 0, .touchY = 0};
|
InputEvent event = {.inputEvent = (input_broker_event)INPUT_BROKER_SELECT, .kbchar = 0, .touchX = 0, .touchY = 0};
|
||||||
inputBroker->injectInputEvent(&event);
|
inputBroker->injectInputEvent(&event);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "VirtualKeyboard.h"
|
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
|
#if HAS_SCREEN
|
||||||
|
#include "VirtualKeyboard.h"
|
||||||
#include "graphics/Screen.h"
|
#include "graphics/Screen.h"
|
||||||
#include "graphics/ScreenFonts.h"
|
#include "graphics/ScreenFonts.h"
|
||||||
#include "graphics/SharedUIDisplay.h"
|
#include "graphics/SharedUIDisplay.h"
|
||||||
@@ -736,3 +737,4 @@ bool VirtualKeyboard::isTimedOut() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace graphics
|
} // namespace graphics
|
||||||
|
#endif
|
||||||
@@ -2,11 +2,13 @@
|
|||||||
#if HAS_SCREEN
|
#if HAS_SCREEN
|
||||||
#include "ClockRenderer.h"
|
#include "ClockRenderer.h"
|
||||||
#include "NodeDB.h"
|
#include "NodeDB.h"
|
||||||
|
#include "UIRenderer.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "gps/GeoCoord.h"
|
#include "gps/GeoCoord.h"
|
||||||
#include "gps/RTC.h"
|
#include "gps/RTC.h"
|
||||||
#include "graphics/ScreenFonts.h"
|
#include "graphics/ScreenFonts.h"
|
||||||
#include "graphics/SharedUIDisplay.h"
|
#include "graphics/SharedUIDisplay.h"
|
||||||
|
#include "graphics/draw/UIRenderer.h"
|
||||||
#include "graphics/emotes.h"
|
#include "graphics/emotes.h"
|
||||||
#include "graphics/images.h"
|
#include "graphics/images.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
@@ -190,6 +192,7 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
|
|||||||
const char *titleStr = "";
|
const char *titleStr = "";
|
||||||
// === Header ===
|
// === Header ===
|
||||||
graphics::drawCommonHeader(display, x, y, titleStr, true, true);
|
graphics::drawCommonHeader(display, x, y, titleStr, true, true);
|
||||||
|
int line = 0;
|
||||||
|
|
||||||
#ifdef T_WATCH_S3
|
#ifdef T_WATCH_S3
|
||||||
if (nimbleBluetooth && nimbleBluetooth->isConnected()) {
|
if (nimbleBluetooth && nimbleBluetooth->isConnected()) {
|
||||||
@@ -314,6 +317,7 @@ void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
const char *titleStr = "";
|
const char *titleStr = "";
|
||||||
// === Header ===
|
// === Header ===
|
||||||
graphics::drawCommonHeader(display, x, y, titleStr, true, true);
|
graphics::drawCommonHeader(display, x, y, titleStr, true, true);
|
||||||
|
int line = 0;
|
||||||
|
|
||||||
#ifdef T_WATCH_S3
|
#ifdef T_WATCH_S3
|
||||||
if (nimbleBluetooth && nimbleBluetooth->isConnected()) {
|
if (nimbleBluetooth && nimbleBluetooth->isConnected()) {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#include "configuration.h"
|
||||||
|
#if HAS_SCREEN
|
||||||
#include "CompassRenderer.h"
|
#include "CompassRenderer.h"
|
||||||
#include "NodeDB.h"
|
#include "NodeDB.h"
|
||||||
#include "UIRenderer.h"
|
#include "UIRenderer.h"
|
||||||
@@ -135,3 +137,4 @@ uint16_t getCompassDiam(uint32_t displayWidth, uint32_t displayHeight)
|
|||||||
|
|
||||||
} // namespace CompassRenderer
|
} // namespace CompassRenderer
|
||||||
} // namespace graphics
|
} // namespace graphics
|
||||||
|
#endif
|
||||||
@@ -277,12 +277,13 @@ void drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
|
|||||||
std::string uptime = UIRenderer::drawTimeDelta(days, hours, minutes, seconds);
|
std::string uptime = UIRenderer::drawTimeDelta(days, hours, minutes, seconds);
|
||||||
|
|
||||||
// Line 1 (Still)
|
// Line 1 (Still)
|
||||||
|
#if !defined(M5STACK_UNITC6L)
|
||||||
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(uptime.c_str()), y, uptime.c_str());
|
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(uptime.c_str()), y, uptime.c_str());
|
||||||
if (config.display.heading_bold)
|
if (config.display.heading_bold)
|
||||||
display->drawString(x - 1 + SCREEN_WIDTH - display->getStringWidth(uptime.c_str()), y, uptime.c_str());
|
display->drawString(x - 1 + SCREEN_WIDTH - display->getStringWidth(uptime.c_str()), y, uptime.c_str());
|
||||||
|
|
||||||
display->setColor(WHITE);
|
display->setColor(WHITE);
|
||||||
|
#endif
|
||||||
// Setup string to assemble analogClock string
|
// Setup string to assemble analogClock string
|
||||||
std::string analogClock = "";
|
std::string analogClock = "";
|
||||||
|
|
||||||
@@ -329,8 +330,7 @@ void drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
|
|||||||
#if HAS_GPS
|
#if HAS_GPS
|
||||||
if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
|
if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
|
||||||
// Line 3
|
// Line 3
|
||||||
if (config.display.gps_format !=
|
if (uiconfig.gps_format != meshtastic_DeviceUIConfig_GpsCoordinateFormat_DMS) // if DMS then don't draw altitude
|
||||||
meshtastic_Config_DisplayConfig_GpsCoordinateFormat_DMS) // if DMS then don't draw altitude
|
|
||||||
UIRenderer::drawGpsAltitude(display, x, y + FONT_HEIGHT_SMALL * 2, gpsStatus);
|
UIRenderer::drawGpsAltitude(display, x, y + FONT_HEIGHT_SMALL * 2, gpsStatus);
|
||||||
|
|
||||||
// Line 4
|
// Line 4
|
||||||
@@ -386,7 +386,11 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
|
|||||||
char shortnameble[35];
|
char shortnameble[35];
|
||||||
getMacAddr(dmac);
|
getMacAddr(dmac);
|
||||||
snprintf(screen->ourId, sizeof(screen->ourId), "%02x%02x", dmac[4], dmac[5]);
|
snprintf(screen->ourId, sizeof(screen->ourId), "%02x%02x", dmac[4], dmac[5]);
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
snprintf(shortnameble, sizeof(shortnameble), "%s", screen->ourId);
|
||||||
|
#else
|
||||||
snprintf(shortnameble, sizeof(shortnameble), "BLE: %s", screen->ourId);
|
snprintf(shortnameble, sizeof(shortnameble), "BLE: %s", screen->ourId);
|
||||||
|
#endif
|
||||||
int textWidth = display->getStringWidth(shortnameble);
|
int textWidth = display->getStringWidth(shortnameble);
|
||||||
int nameX = (SCREEN_WIDTH - textWidth);
|
int nameX = (SCREEN_WIDTH - textWidth);
|
||||||
display->drawString(nameX, getTextPositions(display)[line++], shortnameble);
|
display->drawString(nameX, getTextPositions(display)[line++], shortnameble);
|
||||||
@@ -405,7 +409,11 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
|
|||||||
char regionradiopreset[25];
|
char regionradiopreset[25];
|
||||||
const char *region = myRegion ? myRegion->name : NULL;
|
const char *region = myRegion ? myRegion->name : NULL;
|
||||||
if (region != nullptr) {
|
if (region != nullptr) {
|
||||||
snprintf(regionradiopreset, sizeof(regionradiopreset), "Reg: %s/%s", region, mode);
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
snprintf(regionradiopreset, sizeof(regionradiopreset), "%s", region);
|
||||||
|
#else
|
||||||
|
snprintf(regionradiopreset, sizeof(regionradiopreset), "%s/%s", region, mode);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
textWidth = display->getStringWidth(regionradiopreset);
|
textWidth = display->getStringWidth(regionradiopreset);
|
||||||
nameX = (SCREEN_WIDTH - textWidth) / 2;
|
nameX = (SCREEN_WIDTH - textWidth) / 2;
|
||||||
@@ -417,9 +425,17 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
|
|||||||
float freq = RadioLibInterface::instance->getFreq();
|
float freq = RadioLibInterface::instance->getFreq();
|
||||||
snprintf(freqStr, sizeof(freqStr), "%.3f", freq);
|
snprintf(freqStr, sizeof(freqStr), "%.3f", freq);
|
||||||
if (config.lora.channel_num == 0) {
|
if (config.lora.channel_num == 0) {
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
snprintf(frequencyslot, sizeof(frequencyslot), "%sMHz", freqStr);
|
||||||
|
#else
|
||||||
snprintf(frequencyslot, sizeof(frequencyslot), "Freq: %sMHz", freqStr);
|
snprintf(frequencyslot, sizeof(frequencyslot), "Freq: %sMHz", freqStr);
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
snprintf(frequencyslot, sizeof(frequencyslot), "%sMHz (%d)", freqStr, config.lora.channel_num);
|
||||||
|
#else
|
||||||
snprintf(frequencyslot, sizeof(frequencyslot), "Freq/Ch: %sMHz (%d)", freqStr, config.lora.channel_num);
|
snprintf(frequencyslot, sizeof(frequencyslot), "Freq/Ch: %sMHz (%d)", freqStr, config.lora.channel_num);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
size_t len = strlen(frequencyslot);
|
size_t len = strlen(frequencyslot);
|
||||||
if (len >= 4 && strcmp(frequencyslot + len - 4, " (0)") == 0) {
|
if (len >= 4 && strcmp(frequencyslot + len - 4, " (0)") == 0) {
|
||||||
@@ -429,6 +445,7 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
|
|||||||
nameX = (SCREEN_WIDTH - textWidth) / 2;
|
nameX = (SCREEN_WIDTH - textWidth) / 2;
|
||||||
display->drawString(nameX, getTextPositions(display)[line++], frequencyslot);
|
display->drawString(nameX, getTextPositions(display)[line++], frequencyslot);
|
||||||
|
|
||||||
|
#if !defined(M5STACK_UNITC6L)
|
||||||
// === Fifth Row: Channel Utilization ===
|
// === Fifth Row: Channel Utilization ===
|
||||||
const char *chUtil = "ChUtil:";
|
const char *chUtil = "ChUtil:";
|
||||||
char chUtilPercentage[10];
|
char chUtilPercentage[10];
|
||||||
@@ -485,6 +502,7 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
|
|||||||
|
|
||||||
display->drawString(starting_position + chUtil_x + chutil_bar_width + extraoffset, getTextPositions(display)[line++],
|
display->drawString(starting_position + chUtil_x + chutil_bar_width + extraoffset, getTextPositions(display)[line++],
|
||||||
chUtilPercentage);
|
chUtilPercentage);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// ****************************
|
// ****************************
|
||||||
@@ -510,8 +528,11 @@ void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x
|
|||||||
#ifdef USE_EINK
|
#ifdef USE_EINK
|
||||||
barsOffset -= 12;
|
barsOffset -= 12;
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
const int barX = x + 45 + barsOffset;
|
||||||
|
#else
|
||||||
const int barX = x + 40 + barsOffset;
|
const int barX = x + 40 + barsOffset;
|
||||||
|
#endif
|
||||||
auto drawUsageRow = [&](const char *label, uint32_t used, uint32_t total, bool isHeap = false) {
|
auto drawUsageRow = [&](const char *label, uint32_t used, uint32_t total, bool isHeap = false) {
|
||||||
if (total == 0)
|
if (total == 0)
|
||||||
return;
|
return;
|
||||||
@@ -536,7 +557,7 @@ void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x
|
|||||||
// Label
|
// Label
|
||||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||||
display->drawString(labelX, getTextPositions(display)[line], label);
|
display->drawString(labelX, getTextPositions(display)[line], label);
|
||||||
|
#if !defined(M5STACK_UNITC6L)
|
||||||
// Bar
|
// Bar
|
||||||
int barY = getTextPositions(display)[line] + (FONT_HEIGHT_SMALL - barHeight) / 2;
|
int barY = getTextPositions(display)[line] + (FONT_HEIGHT_SMALL - barHeight) / 2;
|
||||||
display->setColor(WHITE);
|
display->setColor(WHITE);
|
||||||
@@ -544,7 +565,7 @@ void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x
|
|||||||
|
|
||||||
display->fillRect(barX, barY, fillWidth, barHeight);
|
display->fillRect(barX, barY, fillWidth, barHeight);
|
||||||
display->setColor(WHITE);
|
display->setColor(WHITE);
|
||||||
|
#endif
|
||||||
// Value string
|
// Value string
|
||||||
display->setTextAlignment(TEXT_ALIGN_RIGHT);
|
display->setTextAlignment(TEXT_ALIGN_RIGHT);
|
||||||
display->drawString(SCREEN_WIDTH - 2, getTextPositions(display)[line], combinedStr);
|
display->drawString(SCREEN_WIDTH - 2, getTextPositions(display)[line], combinedStr);
|
||||||
@@ -597,10 +618,16 @@ void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x
|
|||||||
line += 1;
|
line += 1;
|
||||||
}
|
}
|
||||||
line += 1;
|
line += 1;
|
||||||
|
|
||||||
char appversionstr[35];
|
char appversionstr[35];
|
||||||
snprintf(appversionstr, sizeof(appversionstr), "Ver: %s", optstr(APP_VERSION));
|
snprintf(appversionstr, sizeof(appversionstr), "Ver: %s", optstr(APP_VERSION));
|
||||||
char appversionstr_formatted[40];
|
char appversionstr_formatted[40];
|
||||||
char *lastDot = strrchr(appversionstr, '.');
|
char *lastDot = strrchr(appversionstr, '.');
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
if (lastDot != nullptr) {
|
||||||
|
*lastDot = '\0'; // truncate string
|
||||||
|
}
|
||||||
|
#else
|
||||||
if (lastDot) {
|
if (lastDot) {
|
||||||
size_t prefixLen = lastDot - appversionstr;
|
size_t prefixLen = lastDot - appversionstr;
|
||||||
strncpy(appversionstr_formatted, appversionstr, prefixLen);
|
strncpy(appversionstr_formatted, appversionstr, prefixLen);
|
||||||
@@ -611,10 +638,12 @@ void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x
|
|||||||
strncpy(appversionstr, appversionstr_formatted, sizeof(appversionstr) - 1);
|
strncpy(appversionstr, appversionstr_formatted, sizeof(appversionstr) - 1);
|
||||||
appversionstr[sizeof(appversionstr) - 1] = '\0';
|
appversionstr[sizeof(appversionstr) - 1] = '\0';
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
int textWidth = display->getStringWidth(appversionstr);
|
int textWidth = display->getStringWidth(appversionstr);
|
||||||
int nameX = (SCREEN_WIDTH - textWidth) / 2;
|
int nameX = (SCREEN_WIDTH - textWidth) / 2;
|
||||||
display->drawString(nameX, getTextPositions(display)[line], appversionstr);
|
|
||||||
|
|
||||||
|
display->drawString(nameX, getTextPositions(display)[line], appversionstr);
|
||||||
|
#if !defined(M5STACK_UNITC6L)
|
||||||
if (SCREEN_HEIGHT > 64 || (SCREEN_HEIGHT <= 64 && line < 4)) { // Only show uptime if the screen can show it
|
if (SCREEN_HEIGHT > 64 || (SCREEN_HEIGHT <= 64 && line < 4)) { // Only show uptime if the screen can show it
|
||||||
line += 1;
|
line += 1;
|
||||||
char uptimeStr[32] = "";
|
char uptimeStr[32] = "";
|
||||||
@@ -633,6 +662,7 @@ void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x
|
|||||||
nameX = (SCREEN_WIDTH - textWidth) / 2;
|
nameX = (SCREEN_WIDTH - textWidth) / 2;
|
||||||
display->drawString(nameX, getTextPositions(display)[line], uptimeStr);
|
display->drawString(nameX, getTextPositions(display)[line], uptimeStr);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// ****************************
|
// ****************************
|
||||||
@@ -661,6 +691,7 @@ void drawChirpy(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int1
|
|||||||
textX = (display->getWidth() / 2) - textX_offset - (display->getStringWidth("World!") / 2);
|
textX = (display->getWidth() / 2) - textX_offset - (display->getStringWidth("World!") / 2);
|
||||||
display->drawString(textX, getTextPositions(display)[line++], "World!");
|
display->drawString(textX, getTextPositions(display)[line++], "World!");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace DebugRenderer
|
} // namespace DebugRenderer
|
||||||
} // namespace graphics
|
} // namespace graphics
|
||||||
#endif
|
#endif
|
||||||
@@ -33,7 +33,6 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
|
|||||||
|
|
||||||
// System screen display
|
// System screen display
|
||||||
void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||||
|
|
||||||
// Chirpy screen display
|
// Chirpy screen display
|
||||||
void drawChirpy(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
void drawChirpy(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||||
} // namespace DebugRenderer
|
} // namespace DebugRenderer
|
||||||
|
|||||||
@@ -31,19 +31,21 @@ uint8_t test_count = 0;
|
|||||||
|
|
||||||
void menuHandler::loraMenu()
|
void menuHandler::loraMenu()
|
||||||
{
|
{
|
||||||
static const char *optionsArray[] = {"Back", "Region Picker", "Device Role"};
|
static const char *optionsArray[] = {"Back", "Device Role", "Radio Preset", "LoRa Region"};
|
||||||
enum optionsNumbers { Back = 0, lora_picker = 1, device_role_picker = 2 };
|
enum optionsNumbers { Back = 0, device_role_picker = 1, radio_preset_picker = 2, lora_picker = 3 };
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
bannerOptions.message = "LoRa Actions";
|
bannerOptions.message = "LoRa Actions";
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
bannerOptions.optionsCount = 3;
|
bannerOptions.optionsCount = 4;
|
||||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||||
if (selected == Back) {
|
if (selected == Back) {
|
||||||
// No action
|
// No action
|
||||||
} else if (selected == lora_picker) {
|
|
||||||
menuHandler::menuQueue = menuHandler::lora_picker;
|
|
||||||
} else if (selected == device_role_picker) {
|
} else if (selected == device_role_picker) {
|
||||||
menuHandler::menuQueue = menuHandler::device_role_picker;
|
menuHandler::menuQueue = menuHandler::device_role_picker;
|
||||||
|
} else if (selected == radio_preset_picker) {
|
||||||
|
menuHandler::menuQueue = menuHandler::radio_preset_picker;
|
||||||
|
} else if (selected == lora_picker) {
|
||||||
|
menuHandler::menuQueue = menuHandler::lora_picker;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
screen->showOverlayBanner(bannerOptions);
|
screen->showOverlayBanner(bannerOptions);
|
||||||
@@ -102,7 +104,11 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
|
|||||||
"NP_865",
|
"NP_865",
|
||||||
"BR_902"};
|
"BR_902"};
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
bannerOptions.message = "LoRa Region";
|
||||||
|
#else
|
||||||
bannerOptions.message = "Set the LoRa region";
|
bannerOptions.message = "Set the LoRa region";
|
||||||
|
#endif
|
||||||
bannerOptions.durationMs = duration;
|
bannerOptions.durationMs = duration;
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
bannerOptions.optionsCount = 27;
|
bannerOptions.optionsCount = 27;
|
||||||
@@ -110,6 +116,8 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
|
|||||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||||
if (selected != 0 && config.lora.region != _meshtastic_Config_LoRaConfig_RegionCode(selected)) {
|
if (selected != 0 && config.lora.region != _meshtastic_Config_LoRaConfig_RegionCode(selected)) {
|
||||||
config.lora.region = _meshtastic_Config_LoRaConfig_RegionCode(selected);
|
config.lora.region = _meshtastic_Config_LoRaConfig_RegionCode(selected);
|
||||||
|
auto changes = SEGMENT_CONFIG;
|
||||||
|
|
||||||
// This is needed as we wait til picking the LoRa region to generate keys for the first time.
|
// This is needed as we wait til picking the LoRa region to generate keys for the first time.
|
||||||
if (!owner.is_licensed) {
|
if (!owner.is_licensed) {
|
||||||
bool keygenSuccess = false;
|
bool keygenSuccess = false;
|
||||||
@@ -118,6 +126,7 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
|
|||||||
if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) {
|
if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) {
|
||||||
keygenSuccess = true;
|
keygenSuccess = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
LOG_INFO("Generate new PKI keys");
|
LOG_INFO("Generate new PKI keys");
|
||||||
crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes);
|
crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes);
|
||||||
@@ -135,7 +144,14 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
|
|||||||
if (myRegion->dutyCycle < 100) {
|
if (myRegion->dutyCycle < 100) {
|
||||||
config.lora.ignore_mqtt = true; // Ignore MQTT by default if region has a duty cycle limit
|
config.lora.ignore_mqtt = true; // Ignore MQTT by default if region has a duty cycle limit
|
||||||
}
|
}
|
||||||
service->reloadConfig(SEGMENT_CONFIG);
|
|
||||||
|
if (strncmp(moduleConfig.mqtt.root, default_mqtt_root, strlen(default_mqtt_root)) == 0) {
|
||||||
|
// Default broker is in use, so subscribe to the appropriate MQTT root topic for this region
|
||||||
|
sprintf(moduleConfig.mqtt.root, "%s/%s", default_mqtt_root, myRegion->name);
|
||||||
|
changes |= SEGMENT_MODULECONFIG;
|
||||||
|
}
|
||||||
|
|
||||||
|
service->reloadConfig(changes);
|
||||||
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
|
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -176,6 +192,53 @@ void menuHandler::DeviceRolePicker()
|
|||||||
screen->showOverlayBanner(bannerOptions);
|
screen->showOverlayBanner(bannerOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void menuHandler::RadioPresetPicker()
|
||||||
|
{
|
||||||
|
static const char *optionsArray[] = {"Back", "LongSlow", "LongModerate", "LongFast", "MediumSlow",
|
||||||
|
"MediumFast", "ShortSlow", "ShortFast", "ShortTurbo"};
|
||||||
|
enum optionsNumbers {
|
||||||
|
Back = 0,
|
||||||
|
radiopreset_LongSlow = 1,
|
||||||
|
radiopreset_LongModerate = 2,
|
||||||
|
radiopreset_LongFast = 3,
|
||||||
|
radiopreset_MediumSlow = 4,
|
||||||
|
radiopreset_MediumFast = 5,
|
||||||
|
radiopreset_ShortSlow = 6,
|
||||||
|
radiopreset_ShortFast = 7,
|
||||||
|
radiopreset_ShortTurbo = 8
|
||||||
|
};
|
||||||
|
BannerOverlayOptions bannerOptions;
|
||||||
|
bannerOptions.message = "Radio Preset";
|
||||||
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
|
bannerOptions.optionsCount = 9;
|
||||||
|
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||||
|
if (selected == Back) {
|
||||||
|
menuHandler::menuQueue = menuHandler::lora_Menu;
|
||||||
|
screen->runNow();
|
||||||
|
return;
|
||||||
|
} else if (selected == radiopreset_LongSlow) {
|
||||||
|
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW;
|
||||||
|
} else if (selected == radiopreset_LongModerate) {
|
||||||
|
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE;
|
||||||
|
} else if (selected == radiopreset_LongFast) {
|
||||||
|
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST;
|
||||||
|
} else if (selected == radiopreset_MediumSlow) {
|
||||||
|
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW;
|
||||||
|
} else if (selected == radiopreset_MediumFast) {
|
||||||
|
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST;
|
||||||
|
} else if (selected == radiopreset_ShortSlow) {
|
||||||
|
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW;
|
||||||
|
} else if (selected == radiopreset_ShortFast) {
|
||||||
|
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST;
|
||||||
|
} else if (selected == radiopreset_ShortTurbo) {
|
||||||
|
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO;
|
||||||
|
}
|
||||||
|
service->reloadConfig(SEGMENT_CONFIG);
|
||||||
|
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
|
||||||
|
};
|
||||||
|
screen->showOverlayBanner(bannerOptions);
|
||||||
|
}
|
||||||
|
|
||||||
void menuHandler::TwelveHourPicker()
|
void menuHandler::TwelveHourPicker()
|
||||||
{
|
{
|
||||||
static const char *optionsArray[] = {"Back", "12-hour", "24-hour"};
|
static const char *optionsArray[] = {"Back", "12-hour", "24-hour"};
|
||||||
@@ -317,7 +380,11 @@ void menuHandler::TZPicker()
|
|||||||
|
|
||||||
void menuHandler::clockMenu()
|
void menuHandler::clockMenu()
|
||||||
{
|
{
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
static const char *optionsArray[] = {"Back", "Time Format", "Timezone"};
|
||||||
|
#else
|
||||||
static const char *optionsArray[] = {"Back", "Clock Face", "Time Format", "Timezone"};
|
static const char *optionsArray[] = {"Back", "Clock Face", "Time Format", "Timezone"};
|
||||||
|
#endif
|
||||||
enum optionsNumbers { Back = 0, Clock = 1, Time = 2, Timezone = 3 };
|
enum optionsNumbers { Back = 0, Clock = 1, Time = 2, Timezone = 3 };
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
bannerOptions.message = "Clock Action";
|
bannerOptions.message = "Clock Action";
|
||||||
@@ -341,8 +408,11 @@ void menuHandler::clockMenu()
|
|||||||
void menuHandler::messageResponseMenu()
|
void menuHandler::messageResponseMenu()
|
||||||
{
|
{
|
||||||
enum optionsNumbers { Back = 0, Dismiss = 1, Preset = 2, Freetext = 3, Aloud = 4, enumEnd = 5 };
|
enum optionsNumbers { Back = 0, Dismiss = 1, Preset = 2, Freetext = 3, Aloud = 4, enumEnd = 5 };
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
static const char *optionsArray[enumEnd] = {"Back", "Dismiss", "Reply Preset"};
|
||||||
|
#else
|
||||||
static const char *optionsArray[enumEnd] = {"Back", "Dismiss", "Reply via Preset"};
|
static const char *optionsArray[enumEnd] = {"Back", "Dismiss", "Reply via Preset"};
|
||||||
|
#endif
|
||||||
static int optionsEnumArray[enumEnd] = {Back, Dismiss, Preset};
|
static int optionsEnumArray[enumEnd] = {Back, Dismiss, Preset};
|
||||||
int options = 3;
|
int options = 3;
|
||||||
|
|
||||||
@@ -356,7 +426,11 @@ void menuHandler::messageResponseMenu()
|
|||||||
optionsEnumArray[options++] = Aloud;
|
optionsEnumArray[options++] = Aloud;
|
||||||
#endif
|
#endif
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
bannerOptions.message = "Message";
|
||||||
|
#else
|
||||||
bannerOptions.message = "Message Action";
|
bannerOptions.message = "Message Action";
|
||||||
|
#endif
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
||||||
bannerOptions.optionsCount = options;
|
bannerOptions.optionsCount = options;
|
||||||
@@ -409,7 +483,11 @@ void menuHandler::homeBaseMenu()
|
|||||||
optionsArray[options] = "Send Node Info";
|
optionsArray[options] = "Send Node Info";
|
||||||
}
|
}
|
||||||
optionsEnumArray[options++] = Position;
|
optionsEnumArray[options++] = Position;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
optionsArray[options] = "New Preset";
|
||||||
|
#else
|
||||||
optionsArray[options] = "New Preset Msg";
|
optionsArray[options] = "New Preset Msg";
|
||||||
|
#endif
|
||||||
optionsEnumArray[options++] = Preset;
|
optionsEnumArray[options++] = Preset;
|
||||||
if (kb_found) {
|
if (kb_found) {
|
||||||
optionsArray[options] = "New Freetext Msg";
|
optionsArray[options] = "New Freetext Msg";
|
||||||
@@ -417,7 +495,11 @@ void menuHandler::homeBaseMenu()
|
|||||||
}
|
}
|
||||||
|
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
bannerOptions.message = "Home";
|
||||||
|
#else
|
||||||
bannerOptions.message = "Home Action";
|
bannerOptions.message = "Home Action";
|
||||||
|
#endif
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
||||||
bannerOptions.optionsCount = options;
|
bannerOptions.optionsCount = options;
|
||||||
@@ -456,6 +538,11 @@ void menuHandler::homeBaseMenu()
|
|||||||
screen->showOverlayBanner(bannerOptions);
|
screen->showOverlayBanner(bannerOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void menuHandler::textMessageMenu()
|
||||||
|
{
|
||||||
|
cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST);
|
||||||
|
}
|
||||||
|
|
||||||
void menuHandler::textMessageBaseMenu()
|
void menuHandler::textMessageBaseMenu()
|
||||||
{
|
{
|
||||||
enum optionsNumbers { Back, Preset, Freetext, enumEnd };
|
enum optionsNumbers { Back, Preset, Freetext, enumEnd };
|
||||||
@@ -502,11 +589,17 @@ void menuHandler::systemBaseMenu()
|
|||||||
|
|
||||||
optionsArray[options] = "Frame Visiblity Toggle";
|
optionsArray[options] = "Frame Visiblity Toggle";
|
||||||
optionsEnumArray[options++] = FrameToggles;
|
optionsEnumArray[options++] = FrameToggles;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
optionsArray[options] = "Bluetooth";
|
||||||
|
#else
|
||||||
optionsArray[options] = "Bluetooth Toggle";
|
optionsArray[options] = "Bluetooth Toggle";
|
||||||
|
#endif
|
||||||
optionsEnumArray[options++] = Bluetooth;
|
optionsEnumArray[options++] = Bluetooth;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
optionsArray[options] = "Power";
|
||||||
|
#else
|
||||||
optionsArray[options] = "Reboot/Shutdown";
|
optionsArray[options] = "Reboot/Shutdown";
|
||||||
|
#endif
|
||||||
optionsEnumArray[options++] = PowerMenu;
|
optionsEnumArray[options++] = PowerMenu;
|
||||||
|
|
||||||
if (test_enabled) {
|
if (test_enabled) {
|
||||||
@@ -515,7 +608,11 @@ void menuHandler::systemBaseMenu()
|
|||||||
}
|
}
|
||||||
|
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
bannerOptions.message = "System";
|
||||||
|
#else
|
||||||
bannerOptions.message = "System Action";
|
bannerOptions.message = "System Action";
|
||||||
|
#endif
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
bannerOptions.optionsCount = options;
|
bannerOptions.optionsCount = options;
|
||||||
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
||||||
@@ -551,7 +648,11 @@ void menuHandler::systemBaseMenu()
|
|||||||
void menuHandler::favoriteBaseMenu()
|
void menuHandler::favoriteBaseMenu()
|
||||||
{
|
{
|
||||||
enum optionsNumbers { Back, Preset, Freetext, Remove, TraceRoute, enumEnd };
|
enum optionsNumbers { Back, Preset, Freetext, Remove, TraceRoute, enumEnd };
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
static const char *optionsArray[enumEnd] = {"Back", "New Preset"};
|
||||||
|
#else
|
||||||
static const char *optionsArray[enumEnd] = {"Back", "New Preset Msg"};
|
static const char *optionsArray[enumEnd] = {"Back", "New Preset Msg"};
|
||||||
|
#endif
|
||||||
static int optionsEnumArray[enumEnd] = {Back, Preset};
|
static int optionsEnumArray[enumEnd] = {Back, Preset};
|
||||||
int options = 2;
|
int options = 2;
|
||||||
|
|
||||||
@@ -559,13 +660,19 @@ void menuHandler::favoriteBaseMenu()
|
|||||||
optionsArray[options] = "New Freetext Msg";
|
optionsArray[options] = "New Freetext Msg";
|
||||||
optionsEnumArray[options++] = Freetext;
|
optionsEnumArray[options++] = Freetext;
|
||||||
}
|
}
|
||||||
|
#if !defined(M5STACK_UNITC6L)
|
||||||
optionsArray[options] = "Trace Route";
|
optionsArray[options] = "Trace Route";
|
||||||
optionsEnumArray[options++] = TraceRoute;
|
optionsEnumArray[options++] = TraceRoute;
|
||||||
|
#endif
|
||||||
optionsArray[options] = "Remove Favorite";
|
optionsArray[options] = "Remove Favorite";
|
||||||
optionsEnumArray[options++] = Remove;
|
optionsEnumArray[options++] = Remove;
|
||||||
|
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
bannerOptions.message = "Favorites";
|
||||||
|
#else
|
||||||
bannerOptions.message = "Favorites Action";
|
bannerOptions.message = "Favorites Action";
|
||||||
|
#endif
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
||||||
bannerOptions.optionsCount = options;
|
bannerOptions.optionsCount = options;
|
||||||
@@ -588,11 +695,11 @@ void menuHandler::favoriteBaseMenu()
|
|||||||
|
|
||||||
void menuHandler::positionBaseMenu()
|
void menuHandler::positionBaseMenu()
|
||||||
{
|
{
|
||||||
enum optionsNumbers { Back, GPSToggle, CompassMenu, CompassCalibrate, enumEnd };
|
enum optionsNumbers { Back, GPSToggle, GPSFormat, CompassMenu, CompassCalibrate, enumEnd };
|
||||||
|
|
||||||
static const char *optionsArray[enumEnd] = {"Back", "GPS Toggle", "Compass"};
|
static const char *optionsArray[enumEnd] = {"Back", "GPS Toggle", "GPS Format", "Compass"};
|
||||||
static int optionsEnumArray[enumEnd] = {Back, GPSToggle, CompassMenu};
|
static int optionsEnumArray[enumEnd] = {Back, GPSToggle, GPSFormat, CompassMenu};
|
||||||
int options = 3;
|
int options = 4;
|
||||||
|
|
||||||
if (accelerometerThread) {
|
if (accelerometerThread) {
|
||||||
optionsArray[options] = "Compass Calibrate";
|
optionsArray[options] = "Compass Calibrate";
|
||||||
@@ -608,6 +715,9 @@ void menuHandler::positionBaseMenu()
|
|||||||
if (selected == GPSToggle) {
|
if (selected == GPSToggle) {
|
||||||
menuQueue = gps_toggle_menu;
|
menuQueue = gps_toggle_menu;
|
||||||
screen->runNow();
|
screen->runNow();
|
||||||
|
} else if (selected == GPSFormat) {
|
||||||
|
menuQueue = gps_format_menu;
|
||||||
|
screen->runNow();
|
||||||
} else if (selected == CompassMenu) {
|
} else if (selected == CompassMenu) {
|
||||||
menuQueue = compass_point_north_menu;
|
menuQueue = compass_point_north_menu;
|
||||||
screen->runNow();
|
screen->runNow();
|
||||||
@@ -621,11 +731,19 @@ void menuHandler::positionBaseMenu()
|
|||||||
void menuHandler::nodeListMenu()
|
void menuHandler::nodeListMenu()
|
||||||
{
|
{
|
||||||
enum optionsNumbers { Back, Favorite, TraceRoute, Verify, Reset, enumEnd };
|
enum optionsNumbers { Back, Favorite, TraceRoute, Verify, Reset, enumEnd };
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
static const char *optionsArray[] = {"Back", "Add Favorite", "Reset Node"};
|
||||||
|
#else
|
||||||
static const char *optionsArray[] = {"Back", "Add Favorite", "Trace Route", "Key Verification", "Reset NodeDB"};
|
static const char *optionsArray[] = {"Back", "Add Favorite", "Trace Route", "Key Verification", "Reset NodeDB"};
|
||||||
|
#endif
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
bannerOptions.message = "Node Action";
|
bannerOptions.message = "Node Action";
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
bannerOptions.optionsCount = 3;
|
||||||
|
#else
|
||||||
bannerOptions.optionsCount = 5;
|
bannerOptions.optionsCount = 5;
|
||||||
|
#endif
|
||||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||||
if (selected == Favorite) {
|
if (selected == Favorite) {
|
||||||
menuQueue = add_favorite;
|
menuQueue = add_favorite;
|
||||||
@@ -644,6 +762,31 @@ void menuHandler::nodeListMenu()
|
|||||||
screen->showOverlayBanner(bannerOptions);
|
screen->showOverlayBanner(bannerOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void menuHandler::nodeNameLengthMenu()
|
||||||
|
{
|
||||||
|
enum OptionsNumbers { Back, Long, Short };
|
||||||
|
static const char *optionsArray[] = {"Back", "Long", "Short"};
|
||||||
|
BannerOverlayOptions bannerOptions;
|
||||||
|
bannerOptions.message = "Node Name Length";
|
||||||
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
|
bannerOptions.optionsCount = 3;
|
||||||
|
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||||
|
if (selected == Long) {
|
||||||
|
// Set names to long
|
||||||
|
LOG_INFO("Setting names to long");
|
||||||
|
config.display.use_long_node_name = true;
|
||||||
|
} else if (selected == Short) {
|
||||||
|
// Set names to short
|
||||||
|
LOG_INFO("Setting names to short");
|
||||||
|
config.display.use_long_node_name = false;
|
||||||
|
} else if (selected == Back) {
|
||||||
|
menuQueue = screen_options_menu;
|
||||||
|
screen->runNow();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
screen->showOverlayBanner(bannerOptions);
|
||||||
|
}
|
||||||
|
|
||||||
void menuHandler::resetNodeDBMenu()
|
void menuHandler::resetNodeDBMenu()
|
||||||
{
|
{
|
||||||
static const char *optionsArray[] = {"Back", "Confirm"};
|
static const char *optionsArray[] = {"Back", "Confirm"};
|
||||||
@@ -726,13 +869,69 @@ void menuHandler::GPSToggleMenu()
|
|||||||
bannerOptions.InitialSelected = config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED ? 1 : 2;
|
bannerOptions.InitialSelected = config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED ? 1 : 2;
|
||||||
screen->showOverlayBanner(bannerOptions);
|
screen->showOverlayBanner(bannerOptions);
|
||||||
}
|
}
|
||||||
|
void menuHandler::GPSFormatMenu()
|
||||||
|
{
|
||||||
|
|
||||||
|
static const char *optionsArray[] = {"Back",
|
||||||
|
isHighResolution ? "Decimal Degrees" : "DEC",
|
||||||
|
isHighResolution ? "Degrees Minutes Seconds" : "DMS",
|
||||||
|
isHighResolution ? "Universal Transverse Mercator" : "UTM",
|
||||||
|
isHighResolution ? "Military Grid Reference System" : "MGRS",
|
||||||
|
isHighResolution ? "Open Location Code" : "OLC",
|
||||||
|
isHighResolution ? "Ordnance Survey Grid Ref" : "OSGR",
|
||||||
|
isHighResolution ? "Maidenhead Locator" : "MLS"};
|
||||||
|
BannerOverlayOptions bannerOptions;
|
||||||
|
bannerOptions.message = "GPS Format";
|
||||||
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
|
bannerOptions.optionsCount = 8;
|
||||||
|
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||||
|
if (selected == 1) {
|
||||||
|
uiconfig.gps_format = meshtastic_DeviceUIConfig_GpsCoordinateFormat_DEC;
|
||||||
|
saveUIConfig();
|
||||||
|
service->reloadConfig(SEGMENT_CONFIG);
|
||||||
|
} else if (selected == 2) {
|
||||||
|
uiconfig.gps_format = meshtastic_DeviceUIConfig_GpsCoordinateFormat_DMS;
|
||||||
|
saveUIConfig();
|
||||||
|
service->reloadConfig(SEGMENT_CONFIG);
|
||||||
|
} else if (selected == 3) {
|
||||||
|
uiconfig.gps_format = meshtastic_DeviceUIConfig_GpsCoordinateFormat_UTM;
|
||||||
|
saveUIConfig();
|
||||||
|
service->reloadConfig(SEGMENT_CONFIG);
|
||||||
|
} else if (selected == 4) {
|
||||||
|
uiconfig.gps_format = meshtastic_DeviceUIConfig_GpsCoordinateFormat_MGRS;
|
||||||
|
saveUIConfig();
|
||||||
|
service->reloadConfig(SEGMENT_CONFIG);
|
||||||
|
} else if (selected == 5) {
|
||||||
|
uiconfig.gps_format = meshtastic_DeviceUIConfig_GpsCoordinateFormat_OLC;
|
||||||
|
saveUIConfig();
|
||||||
|
service->reloadConfig(SEGMENT_CONFIG);
|
||||||
|
} else if (selected == 6) {
|
||||||
|
uiconfig.gps_format = meshtastic_DeviceUIConfig_GpsCoordinateFormat_OSGR;
|
||||||
|
saveUIConfig();
|
||||||
|
service->reloadConfig(SEGMENT_CONFIG);
|
||||||
|
} else if (selected == 7) {
|
||||||
|
uiconfig.gps_format = meshtastic_DeviceUIConfig_GpsCoordinateFormat_MLS;
|
||||||
|
saveUIConfig();
|
||||||
|
service->reloadConfig(SEGMENT_CONFIG);
|
||||||
|
} else {
|
||||||
|
menuQueue = position_base_menu;
|
||||||
|
screen->runNow();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
bannerOptions.InitialSelected = uiconfig.gps_format + 1;
|
||||||
|
screen->showOverlayBanner(bannerOptions);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void menuHandler::BluetoothToggleMenu()
|
void menuHandler::BluetoothToggleMenu()
|
||||||
{
|
{
|
||||||
static const char *optionsArray[] = {"Back", "Enabled", "Disabled"};
|
static const char *optionsArray[] = {"Back", "Enabled", "Disabled"};
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
bannerOptions.message = "Bluetooth";
|
||||||
|
#else
|
||||||
bannerOptions.message = "Toggle Bluetooth";
|
bannerOptions.message = "Toggle Bluetooth";
|
||||||
|
#endif
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
bannerOptions.optionsCount = 3;
|
bannerOptions.optionsCount = 3;
|
||||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||||
@@ -747,11 +946,11 @@ void menuHandler::BluetoothToggleMenu()
|
|||||||
|
|
||||||
void menuHandler::BuzzerModeMenu()
|
void menuHandler::BuzzerModeMenu()
|
||||||
{
|
{
|
||||||
static const char *optionsArray[] = {"All Enabled", "Disabled", "Notifications", "System Only"};
|
static const char *optionsArray[] = {"All Enabled", "Disabled", "Notifications", "System Only", "DMs Only"};
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
bannerOptions.message = "Buzzer Mode";
|
bannerOptions.message = "Buzzer Mode";
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
bannerOptions.optionsCount = 4;
|
bannerOptions.optionsCount = 5;
|
||||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||||
config.device.buzzer_mode = (meshtastic_Config_DeviceConfig_BuzzerMode)selected;
|
config.device.buzzer_mode = (meshtastic_Config_DeviceConfig_BuzzerMode)selected;
|
||||||
service->reloadConfig(SEGMENT_CONFIG);
|
service->reloadConfig(SEGMENT_CONFIG);
|
||||||
@@ -924,7 +1123,11 @@ void menuHandler::rebootMenu()
|
|||||||
{
|
{
|
||||||
static const char *optionsArray[] = {"Back", "Confirm"};
|
static const char *optionsArray[] = {"Back", "Confirm"};
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
bannerOptions.message = "Reboot";
|
||||||
|
#else
|
||||||
bannerOptions.message = "Reboot Device?";
|
bannerOptions.message = "Reboot Device?";
|
||||||
|
#endif
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
bannerOptions.optionsCount = 2;
|
bannerOptions.optionsCount = 2;
|
||||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||||
@@ -944,7 +1147,11 @@ void menuHandler::shutdownMenu()
|
|||||||
{
|
{
|
||||||
static const char *optionsArray[] = {"Back", "Confirm"};
|
static const char *optionsArray[] = {"Back", "Confirm"};
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
bannerOptions.message = "Shutdown";
|
||||||
|
#else
|
||||||
bannerOptions.message = "Shutdown Device?";
|
bannerOptions.message = "Shutdown Device?";
|
||||||
|
#endif
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
bannerOptions.optionsCount = 2;
|
bannerOptions.optionsCount = 2;
|
||||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||||
@@ -961,7 +1168,12 @@ void menuHandler::shutdownMenu()
|
|||||||
|
|
||||||
void menuHandler::addFavoriteMenu()
|
void menuHandler::addFavoriteMenu()
|
||||||
{
|
{
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
screen->showNodePicker("Node Favorite", 30000, [](uint32_t nodenum) -> void {
|
||||||
|
#else
|
||||||
screen->showNodePicker("Node To Favorite", 30000, [](uint32_t nodenum) -> void {
|
screen->showNodePicker("Node To Favorite", 30000, [](uint32_t nodenum) -> void {
|
||||||
|
|
||||||
|
#endif
|
||||||
LOG_WARN("Nodenum: %u", nodenum);
|
LOG_WARN("Nodenum: %u", nodenum);
|
||||||
nodeDB->set_favorite(true, nodenum);
|
nodeDB->set_favorite(true, nodenum);
|
||||||
screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
|
screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
|
||||||
@@ -1117,11 +1329,16 @@ void menuHandler::screenOptionsMenu()
|
|||||||
hasSupportBrightness = false;
|
hasSupportBrightness = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
enum optionsNumbers { Back, Brightness, ScreenColor };
|
enum optionsNumbers { Back, NodeNameLength, Brightness, ScreenColor };
|
||||||
static const char *optionsArray[4] = {"Back"};
|
static const char *optionsArray[5] = {"Back"};
|
||||||
static int optionsEnumArray[4] = {Back};
|
static int optionsEnumArray[5] = {Back};
|
||||||
int options = 1;
|
int options = 1;
|
||||||
|
|
||||||
|
#if defined(T_DECK) || defined(T_LORA_PAGER)
|
||||||
|
optionsArray[options] = "Show Long/Short Name";
|
||||||
|
optionsEnumArray[options++] = NodeNameLength;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Only show brightness for B&W displays
|
// Only show brightness for B&W displays
|
||||||
if (hasSupportBrightness) {
|
if (hasSupportBrightness) {
|
||||||
optionsArray[options] = "Brightness";
|
optionsArray[options] = "Brightness";
|
||||||
@@ -1146,6 +1363,9 @@ void menuHandler::screenOptionsMenu()
|
|||||||
} else if (selected == ScreenColor) {
|
} else if (selected == ScreenColor) {
|
||||||
menuHandler::menuQueue = menuHandler::tftcolormenupicker;
|
menuHandler::menuQueue = menuHandler::tftcolormenupicker;
|
||||||
screen->runNow();
|
screen->runNow();
|
||||||
|
} else if (selected == NodeNameLength) {
|
||||||
|
menuHandler::menuQueue = menuHandler::node_name_length_menu;
|
||||||
|
screen->runNow();
|
||||||
} else {
|
} else {
|
||||||
menuQueue = system_base_menu;
|
menuQueue = system_base_menu;
|
||||||
screen->runNow();
|
screen->runNow();
|
||||||
@@ -1174,7 +1394,11 @@ void menuHandler::powerMenu()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
bannerOptions.message = "Power";
|
||||||
|
#else
|
||||||
bannerOptions.message = "Reboot / Shutdown";
|
bannerOptions.message = "Reboot / Shutdown";
|
||||||
|
#endif
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
bannerOptions.optionsCount = options;
|
bannerOptions.optionsCount = options;
|
||||||
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
||||||
@@ -1240,6 +1464,8 @@ void menuHandler::FrameToggles_menu()
|
|||||||
lora,
|
lora,
|
||||||
clock,
|
clock,
|
||||||
show_favorites,
|
show_favorites,
|
||||||
|
show_telemetry,
|
||||||
|
show_power,
|
||||||
enumEnd
|
enumEnd
|
||||||
};
|
};
|
||||||
static const char *optionsArray[enumEnd] = {"Finish"};
|
static const char *optionsArray[enumEnd] = {"Finish"};
|
||||||
@@ -1278,6 +1504,12 @@ void menuHandler::FrameToggles_menu()
|
|||||||
optionsArray[options] = screen->isFrameHidden("show_favorites") ? "Show Favorites" : "Hide Favorites";
|
optionsArray[options] = screen->isFrameHidden("show_favorites") ? "Show Favorites" : "Hide Favorites";
|
||||||
optionsEnumArray[options++] = show_favorites;
|
optionsEnumArray[options++] = show_favorites;
|
||||||
|
|
||||||
|
optionsArray[options] = moduleConfig.telemetry.environment_screen_enabled ? "Hide Telemetry" : "Show Telemetry";
|
||||||
|
optionsEnumArray[options++] = show_telemetry;
|
||||||
|
|
||||||
|
optionsArray[options] = moduleConfig.telemetry.power_screen_enabled ? "Hide Power" : "Show Power";
|
||||||
|
optionsEnumArray[options++] = show_power;
|
||||||
|
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
bannerOptions.message = "Show/Hide Frames";
|
bannerOptions.message = "Show/Hide Frames";
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
@@ -1332,6 +1564,14 @@ void menuHandler::FrameToggles_menu()
|
|||||||
screen->toggleFrameVisibility("show_favorites");
|
screen->toggleFrameVisibility("show_favorites");
|
||||||
menuHandler::menuQueue = menuHandler::FrameToggles;
|
menuHandler::menuQueue = menuHandler::FrameToggles;
|
||||||
screen->runNow();
|
screen->runNow();
|
||||||
|
} else if (selected == show_telemetry) {
|
||||||
|
moduleConfig.telemetry.environment_screen_enabled = !moduleConfig.telemetry.environment_screen_enabled;
|
||||||
|
menuHandler::menuQueue = menuHandler::FrameToggles;
|
||||||
|
screen->runNow();
|
||||||
|
} else if (selected == show_power) {
|
||||||
|
moduleConfig.telemetry.power_screen_enabled = !moduleConfig.telemetry.power_screen_enabled;
|
||||||
|
menuHandler::menuQueue = menuHandler::FrameToggles;
|
||||||
|
screen->runNow();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
screen->showOverlayBanner(bannerOptions);
|
screen->showOverlayBanner(bannerOptions);
|
||||||
@@ -1353,6 +1593,9 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
|
|||||||
case device_role_picker:
|
case device_role_picker:
|
||||||
DeviceRolePicker();
|
DeviceRolePicker();
|
||||||
break;
|
break;
|
||||||
|
case radio_preset_picker:
|
||||||
|
RadioPresetPicker();
|
||||||
|
break;
|
||||||
case no_timeout_lora_picker:
|
case no_timeout_lora_picker:
|
||||||
LoraRegionPicker(0);
|
LoraRegionPicker(0);
|
||||||
break;
|
break;
|
||||||
@@ -1378,6 +1621,9 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
|
|||||||
case gps_toggle_menu:
|
case gps_toggle_menu:
|
||||||
GPSToggleMenu();
|
GPSToggleMenu();
|
||||||
break;
|
break;
|
||||||
|
case gps_format_menu:
|
||||||
|
GPSFormatMenu();
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
case compass_point_north_menu:
|
case compass_point_north_menu:
|
||||||
compassNorthMenu();
|
compassNorthMenu();
|
||||||
@@ -1397,6 +1643,9 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
|
|||||||
case brightness_picker:
|
case brightness_picker:
|
||||||
BrightnessPickerMenu();
|
BrightnessPickerMenu();
|
||||||
break;
|
break;
|
||||||
|
case node_name_length_menu:
|
||||||
|
nodeNameLengthMenu();
|
||||||
|
break;
|
||||||
case reboot_menu:
|
case reboot_menu:
|
||||||
rebootMenu();
|
rebootMenu();
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ class menuHandler
|
|||||||
lora_Menu,
|
lora_Menu,
|
||||||
lora_picker,
|
lora_picker,
|
||||||
device_role_picker,
|
device_role_picker,
|
||||||
|
radio_preset_picker,
|
||||||
no_timeout_lora_picker,
|
no_timeout_lora_picker,
|
||||||
TZ_picker,
|
TZ_picker,
|
||||||
twelve_hour_picker,
|
twelve_hour_picker,
|
||||||
@@ -19,6 +20,7 @@ class menuHandler
|
|||||||
clock_menu,
|
clock_menu,
|
||||||
position_base_menu,
|
position_base_menu,
|
||||||
gps_toggle_menu,
|
gps_toggle_menu,
|
||||||
|
gps_format_menu,
|
||||||
compass_point_north_menu,
|
compass_point_north_menu,
|
||||||
reset_node_db_menu,
|
reset_node_db_menu,
|
||||||
buzzermodemenupicker,
|
buzzermodemenupicker,
|
||||||
@@ -41,6 +43,7 @@ class menuHandler
|
|||||||
key_verification_final_prompt,
|
key_verification_final_prompt,
|
||||||
trace_route_menu,
|
trace_route_menu,
|
||||||
throttle_message,
|
throttle_message,
|
||||||
|
node_name_length_menu,
|
||||||
FrameToggles
|
FrameToggles
|
||||||
};
|
};
|
||||||
static screenMenus menuQueue;
|
static screenMenus menuQueue;
|
||||||
@@ -49,6 +52,7 @@ class menuHandler
|
|||||||
static void LoraRegionPicker(uint32_t duration = 30000);
|
static void LoraRegionPicker(uint32_t duration = 30000);
|
||||||
static void loraMenu();
|
static void loraMenu();
|
||||||
static void DeviceRolePicker();
|
static void DeviceRolePicker();
|
||||||
|
static void RadioPresetPicker();
|
||||||
static void handleMenuSwitch(OLEDDisplay *display);
|
static void handleMenuSwitch(OLEDDisplay *display);
|
||||||
static void showConfirmationBanner(const char *message, std::function<void()> onConfirm);
|
static void showConfirmationBanner(const char *message, std::function<void()> onConfirm);
|
||||||
static void clockMenu();
|
static void clockMenu();
|
||||||
@@ -63,6 +67,7 @@ class menuHandler
|
|||||||
static void positionBaseMenu();
|
static void positionBaseMenu();
|
||||||
static void compassNorthMenu();
|
static void compassNorthMenu();
|
||||||
static void GPSToggleMenu();
|
static void GPSToggleMenu();
|
||||||
|
static void GPSFormatMenu();
|
||||||
static void BuzzerModeMenu();
|
static void BuzzerModeMenu();
|
||||||
static void switchToMUIMenu();
|
static void switchToMUIMenu();
|
||||||
static void TFTColorPickerMenu(OLEDDisplay *display);
|
static void TFTColorPickerMenu(OLEDDisplay *display);
|
||||||
@@ -81,7 +86,9 @@ class menuHandler
|
|||||||
static void notificationsMenu();
|
static void notificationsMenu();
|
||||||
static void screenOptionsMenu();
|
static void screenOptionsMenu();
|
||||||
static void powerMenu();
|
static void powerMenu();
|
||||||
|
static void nodeNameLengthMenu();
|
||||||
static void FrameToggles_menu();
|
static void FrameToggles_menu();
|
||||||
|
static void textMessageMenu();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void saveUIConfig();
|
static void saveUIConfig();
|
||||||
|
|||||||
@@ -181,12 +181,19 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
display->clear();
|
display->clear();
|
||||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||||
display->setFont(FONT_SMALL);
|
display->setFont(FONT_SMALL);
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
const int fixedTopHeight = 24;
|
||||||
|
const int windowX = 0;
|
||||||
|
const int windowY = fixedTopHeight;
|
||||||
|
const int windowWidth = 64;
|
||||||
|
const int windowHeight = SCREEN_HEIGHT - fixedTopHeight;
|
||||||
|
#else
|
||||||
const int navHeight = FONT_HEIGHT_SMALL;
|
const int navHeight = FONT_HEIGHT_SMALL;
|
||||||
const int scrollBottom = SCREEN_HEIGHT - navHeight;
|
const int scrollBottom = SCREEN_HEIGHT - navHeight;
|
||||||
const int usableHeight = scrollBottom;
|
const int usableHeight = scrollBottom;
|
||||||
const int textWidth = SCREEN_WIDTH;
|
const int textWidth = SCREEN_WIDTH;
|
||||||
|
|
||||||
|
#endif
|
||||||
bool isInverted = (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_INVERTED);
|
bool isInverted = (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_INVERTED);
|
||||||
bool isBold = config.display.heading_bold;
|
bool isBold = config.display.heading_bold;
|
||||||
|
|
||||||
@@ -201,7 +208,11 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
graphics::drawCommonHeader(display, x, y, titleStr);
|
graphics::drawCommonHeader(display, x, y, titleStr);
|
||||||
const char *messageString = "No messages";
|
const char *messageString = "No messages";
|
||||||
int center_text = (SCREEN_WIDTH / 2) - (display->getStringWidth(messageString) / 2);
|
int center_text = (SCREEN_WIDTH / 2) - (display->getStringWidth(messageString) / 2);
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
display->drawString(center_text, windowY + (windowHeight / 2) - (FONT_HEIGHT_SMALL / 2) - 5, messageString);
|
||||||
|
#else
|
||||||
display->drawString(center_text, getTextPositions(display)[2], messageString);
|
display->drawString(center_text, getTextPositions(display)[2], messageString);
|
||||||
|
#endif
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,6 +220,10 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(getFrom(&mp));
|
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(getFrom(&mp));
|
||||||
char headerStr[80];
|
char headerStr[80];
|
||||||
const char *sender = "???";
|
const char *sender = "???";
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
if (node && node->has_user)
|
||||||
|
sender = node->user.short_name;
|
||||||
|
#else
|
||||||
if (node && node->has_user) {
|
if (node && node->has_user) {
|
||||||
if (SCREEN_WIDTH >= 200 && strlen(node->user.long_name) > 0) {
|
if (SCREEN_WIDTH >= 200 && strlen(node->user.long_name) > 0) {
|
||||||
sender = node->user.long_name;
|
sender = node->user.long_name;
|
||||||
@@ -216,6 +231,7 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
sender = node->user.short_name;
|
sender = node->user.short_name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
uint32_t seconds = sinceReceived(&mp), minutes = seconds / 60, hours = minutes / 60, days = hours / 24;
|
uint32_t seconds = sinceReceived(&mp), minutes = seconds / 60, hours = minutes / 60, days = hours / 24;
|
||||||
uint8_t timestampHours, timestampMinutes;
|
uint8_t timestampHours, timestampMinutes;
|
||||||
int32_t daysAgo;
|
int32_t daysAgo;
|
||||||
@@ -235,10 +251,61 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
sender);
|
sender);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
snprintf(headerStr, sizeof(headerStr), "%s from %s", UIRenderer::drawTimeDelta(days, hours, minutes, seconds).c_str(),
|
||||||
|
sender);
|
||||||
|
#else
|
||||||
snprintf(headerStr, sizeof(headerStr), "%s ago from %s", UIRenderer::drawTimeDelta(days, hours, minutes, seconds).c_str(),
|
snprintf(headerStr, sizeof(headerStr), "%s ago from %s", UIRenderer::drawTimeDelta(days, hours, minutes, seconds).c_str(),
|
||||||
sender);
|
sender);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
graphics::drawCommonHeader(display, x, y, titleStr);
|
||||||
|
int headerY = getTextPositions(display)[1];
|
||||||
|
display->drawString(x, headerY, headerStr);
|
||||||
|
for (int separatorX = 0; separatorX < SCREEN_WIDTH; separatorX += 2) {
|
||||||
|
display->setPixel(separatorX, fixedTopHeight - 1);
|
||||||
|
}
|
||||||
|
cachedLines.clear();
|
||||||
|
std::string fullMsg(messageBuf);
|
||||||
|
std::string currentLine;
|
||||||
|
for (size_t i = 0; i < fullMsg.size();) {
|
||||||
|
unsigned char c = fullMsg[i];
|
||||||
|
size_t charLen = 1;
|
||||||
|
if ((c & 0xE0) == 0xC0)
|
||||||
|
charLen = 2;
|
||||||
|
else if ((c & 0xF0) == 0xE0)
|
||||||
|
charLen = 3;
|
||||||
|
else if ((c & 0xF8) == 0xF0)
|
||||||
|
charLen = 4;
|
||||||
|
std::string nextChar = fullMsg.substr(i, charLen);
|
||||||
|
std::string testLine = currentLine + nextChar;
|
||||||
|
if (display->getStringWidth(testLine.c_str()) > windowWidth) {
|
||||||
|
cachedLines.push_back(currentLine);
|
||||||
|
currentLine = nextChar;
|
||||||
|
} else {
|
||||||
|
currentLine = testLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
i += charLen;
|
||||||
|
}
|
||||||
|
if (!currentLine.empty())
|
||||||
|
cachedLines.push_back(currentLine);
|
||||||
|
cachedHeights = calculateLineHeights(cachedLines, emotes);
|
||||||
|
int yOffset = windowY;
|
||||||
|
int linesDrawn = 0;
|
||||||
|
for (size_t i = 0; i < cachedLines.size(); ++i) {
|
||||||
|
if (linesDrawn >= 2)
|
||||||
|
break;
|
||||||
|
int lineHeight = cachedHeights[i];
|
||||||
|
if (yOffset + lineHeight > windowY + windowHeight)
|
||||||
|
break;
|
||||||
|
drawStringWithEmotes(display, windowX, yOffset, cachedLines[i], emotes, numEmotes);
|
||||||
|
yOffset += lineHeight;
|
||||||
|
linesDrawn++;
|
||||||
|
}
|
||||||
|
screen->forceDisplay();
|
||||||
|
#else
|
||||||
uint32_t now = millis();
|
uint32_t now = millis();
|
||||||
#ifndef EXCLUDE_EMOJI
|
#ifndef EXCLUDE_EMOJI
|
||||||
// === Bounce animation setup ===
|
// === Bounce animation setup ===
|
||||||
@@ -355,6 +422,7 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
|
|
||||||
// Draw header at the end to sort out overlapping elements
|
// Draw header at the end to sort out overlapping elements
|
||||||
graphics::drawCommonHeader(display, x, y, titleStr);
|
graphics::drawCommonHeader(display, x, y, titleStr);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> generateLines(OLEDDisplay *display, const char *headerStr, const char *messageBuf, int textWidth)
|
std::vector<std::string> generateLines(OLEDDisplay *display, const char *headerStr, const char *messageBuf, int textWidth)
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ extern bool haveGlyphs(const char *str);
|
|||||||
// Global screen instance
|
// Global screen instance
|
||||||
extern graphics::Screen *screen;
|
extern graphics::Screen *screen;
|
||||||
|
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
static uint32_t lastSwitchTime = 0;
|
||||||
|
#else
|
||||||
|
#endif
|
||||||
namespace graphics
|
namespace graphics
|
||||||
{
|
{
|
||||||
namespace NodeListRenderer
|
namespace NodeListRenderer
|
||||||
@@ -51,26 +55,32 @@ static int scrollIndex = 0;
|
|||||||
|
|
||||||
const char *getSafeNodeName(meshtastic_NodeInfoLite *node)
|
const char *getSafeNodeName(meshtastic_NodeInfoLite *node)
|
||||||
{
|
{
|
||||||
|
const char *name = NULL;
|
||||||
static char nodeName[16] = "?";
|
static char nodeName[16] = "?";
|
||||||
if (node->has_user && strlen(node->user.short_name) > 0) {
|
if (config.display.use_long_node_name == true) {
|
||||||
bool valid = true;
|
if (node->has_user && strlen(node->user.long_name) > 0) {
|
||||||
const char *name = node->user.short_name;
|
name = node->user.long_name;
|
||||||
for (size_t i = 0; i < strlen(name); i++) {
|
|
||||||
uint8_t c = (uint8_t)name[i];
|
|
||||||
if (c < 32 || c > 126) {
|
|
||||||
valid = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (valid) {
|
|
||||||
strncpy(nodeName, name, sizeof(nodeName) - 1);
|
|
||||||
nodeName[sizeof(nodeName) - 1] = '\0';
|
|
||||||
} else {
|
} else {
|
||||||
snprintf(nodeName, sizeof(nodeName), "(%04X)", (uint16_t)(node->num & 0xFFFF));
|
snprintf(nodeName, sizeof(nodeName), "(%04X)", (uint16_t)(node->num & 0xFFFF));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (node->has_user && strlen(node->user.short_name) > 0) {
|
||||||
|
name = node->user.short_name;
|
||||||
|
} else {
|
||||||
|
snprintf(nodeName, sizeof(nodeName), "(%04X)", (uint16_t)(node->num & 0xFFFF));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use sanitizeString() function and copy directly into nodeName
|
||||||
|
std::string sanitized_name = sanitizeString(name ? name : "");
|
||||||
|
|
||||||
|
if (!sanitized_name.empty()) {
|
||||||
|
strncpy(nodeName, sanitized_name.c_str(), sizeof(nodeName) - 1);
|
||||||
|
nodeName[sizeof(nodeName) - 1] = '\0';
|
||||||
} else {
|
} else {
|
||||||
snprintf(nodeName, sizeof(nodeName), "(%04X)", (uint16_t)(node->num & 0xFFFF));
|
snprintf(nodeName, sizeof(nodeName), "(%04X)", (uint16_t)(node->num & 0xFFFF));
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodeName;
|
return nodeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -393,9 +403,11 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
|
|||||||
{
|
{
|
||||||
const int COMMON_HEADER_HEIGHT = FONT_HEIGHT_SMALL - 1;
|
const int COMMON_HEADER_HEIGHT = FONT_HEIGHT_SMALL - 1;
|
||||||
const int rowYOffset = FONT_HEIGHT_SMALL - 3;
|
const int rowYOffset = FONT_HEIGHT_SMALL - 3;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
int columnWidth = display->getWidth();
|
||||||
|
#else
|
||||||
int columnWidth = display->getWidth() / 2;
|
int columnWidth = display->getWidth() / 2;
|
||||||
|
#endif
|
||||||
display->clear();
|
display->clear();
|
||||||
|
|
||||||
// Draw the battery/time header
|
// Draw the battery/time header
|
||||||
@@ -408,8 +420,11 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
|
|||||||
int totalRowsAvailable = (display->getHeight() - y) / rowYOffset;
|
int totalRowsAvailable = (display->getHeight() - y) / rowYOffset;
|
||||||
|
|
||||||
int visibleNodeRows = totalRowsAvailable;
|
int visibleNodeRows = totalRowsAvailable;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
int totalColumns = 1;
|
||||||
|
#else
|
||||||
int totalColumns = 2;
|
int totalColumns = 2;
|
||||||
|
#endif
|
||||||
int startIndex = scrollIndex * visibleNodeRows * totalColumns;
|
int startIndex = scrollIndex * visibleNodeRows * totalColumns;
|
||||||
if (nodeDB->getMeshNodeByIndex(startIndex)->num == nodeDB->getNodeNum()) {
|
if (nodeDB->getMeshNodeByIndex(startIndex)->num == nodeDB->getNodeNum()) {
|
||||||
startIndex++; // skip own node
|
startIndex++; // skip own node
|
||||||
@@ -445,12 +460,14 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !defined(M5STACK_UNITC6L)
|
||||||
// Draw column separator
|
// Draw column separator
|
||||||
if (shownCount > 0) {
|
if (shownCount > 0) {
|
||||||
const int firstNodeY = y + 3;
|
const int firstNodeY = y + 3;
|
||||||
drawColumnSeparator(display, x, firstNodeY, lastNodeY);
|
drawColumnSeparator(display, x, firstNodeY, lastNodeY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
const int scrollStartY = y + 3;
|
const int scrollStartY = y + 3;
|
||||||
drawScrollbar(display, visibleNodeRows, totalEntries, scrollIndex, 2, scrollStartY);
|
drawScrollbar(display, visibleNodeRows, totalEntries, scrollIndex, 2, scrollStartY);
|
||||||
}
|
}
|
||||||
@@ -468,6 +485,13 @@ void drawDynamicNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state,
|
|||||||
|
|
||||||
unsigned long now = millis();
|
unsigned long now = millis();
|
||||||
|
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
display->clear();
|
||||||
|
if (now - lastSwitchTime >= 3000) {
|
||||||
|
display->display();
|
||||||
|
lastSwitchTime = now;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
// On very first call (on boot or state enter)
|
// On very first call (on boot or state enter)
|
||||||
if (lastRenderedMode == MODE_COUNT) {
|
if (lastRenderedMode == MODE_COUNT) {
|
||||||
currentMode = MODE_LAST_HEARD;
|
currentMode = MODE_LAST_HEARD;
|
||||||
@@ -522,6 +546,14 @@ void drawNodeListWithCompasses(OLEDDisplay *display, OLEDDisplayUiState *state,
|
|||||||
double lat = DegD(ourNode->position.latitude_i);
|
double lat = DegD(ourNode->position.latitude_i);
|
||||||
double lon = DegD(ourNode->position.longitude_i);
|
double lon = DegD(ourNode->position.longitude_i);
|
||||||
|
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
display->clear();
|
||||||
|
uint32_t now = millis();
|
||||||
|
if (now - lastSwitchTime >= 2000) {
|
||||||
|
display->display();
|
||||||
|
lastSwitchTime = now;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (uiconfig.compass_mode != meshtastic_CompassMode_FREEZE_HEADING) {
|
if (uiconfig.compass_mode != meshtastic_CompassMode_FREEZE_HEADING) {
|
||||||
#if HAS_GPS
|
#if HAS_GPS
|
||||||
if (screen->hasHeading()) {
|
if (screen->hasHeading()) {
|
||||||
|
|||||||
@@ -250,9 +250,11 @@ void NotificationRenderer::drawNodePicker(OLEDDisplay *display, OLEDDisplayUiSta
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle input
|
// Handle input
|
||||||
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
|
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_LEFT ||
|
||||||
|
inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
|
||||||
curSelected--;
|
curSelected--;
|
||||||
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
|
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_RIGHT ||
|
||||||
|
inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
|
||||||
curSelected++;
|
curSelected++;
|
||||||
} else if (inEvent.inputEvent == INPUT_BROKER_SELECT) {
|
} else if (inEvent.inputEvent == INPUT_BROKER_SELECT) {
|
||||||
alertBannerCallback(selectedNodenum);
|
alertBannerCallback(selectedNodenum);
|
||||||
@@ -365,9 +367,11 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp
|
|||||||
|
|
||||||
// Handle input
|
// Handle input
|
||||||
if (alertBannerOptions > 0) {
|
if (alertBannerOptions > 0) {
|
||||||
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
|
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_LEFT ||
|
||||||
|
inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
|
||||||
curSelected--;
|
curSelected--;
|
||||||
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
|
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_RIGHT ||
|
||||||
|
inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
|
||||||
curSelected++;
|
curSelected++;
|
||||||
} else if (inEvent.inputEvent == INPUT_BROKER_SELECT) {
|
} else if (inEvent.inputEvent == INPUT_BROKER_SELECT) {
|
||||||
if (optionsEnumPtr != nullptr) {
|
if (optionsEnumPtr != nullptr) {
|
||||||
@@ -491,6 +495,135 @@ void NotificationRenderer::drawNotificationBox(OLEDDisplay *display, OLEDDisplay
|
|||||||
// count lines
|
// count lines
|
||||||
|
|
||||||
uint16_t boxWidth = hPadding * 2 + maxWidth;
|
uint16_t boxWidth = hPadding * 2 + maxWidth;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
if (needs_bell) {
|
||||||
|
if (isHighResolution && boxWidth <= 150)
|
||||||
|
boxWidth += 26;
|
||||||
|
if (!isHighResolution && boxWidth <= 100)
|
||||||
|
boxWidth += 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t screenHeight = display->height();
|
||||||
|
uint8_t effectiveLineHeight = FONT_HEIGHT_SMALL - 3;
|
||||||
|
uint8_t visibleTotalLines = std::min<uint8_t>(lineCount, (screenHeight - vPadding * 2) / effectiveLineHeight);
|
||||||
|
uint16_t contentHeight = visibleTotalLines * effectiveLineHeight;
|
||||||
|
uint16_t boxHeight = contentHeight + vPadding * 2;
|
||||||
|
if (visibleTotalLines == 1)
|
||||||
|
boxHeight += (isHighResolution ? 4 : 3);
|
||||||
|
|
||||||
|
int16_t boxLeft = (display->width() / 2) - (boxWidth / 2);
|
||||||
|
if (totalLines > visibleTotalLines)
|
||||||
|
boxWidth += (isHighResolution ? 4 : 2);
|
||||||
|
int16_t boxTop = (display->height() / 2) - (boxHeight / 2);
|
||||||
|
|
||||||
|
if (visibleTotalLines == 1) {
|
||||||
|
boxTop += 25;
|
||||||
|
}
|
||||||
|
if (alertBannerOptions < 3) {
|
||||||
|
int missingLines = 3 - alertBannerOptions;
|
||||||
|
int moveUp = missingLines * (effectiveLineHeight / 2);
|
||||||
|
boxTop -= moveUp;
|
||||||
|
if (boxTop < 0)
|
||||||
|
boxTop = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Draw Box ===
|
||||||
|
display->setColor(BLACK);
|
||||||
|
display->fillRect(boxLeft, boxTop, boxWidth, boxHeight);
|
||||||
|
display->setColor(WHITE);
|
||||||
|
display->drawRect(boxLeft, boxTop, boxWidth, boxHeight);
|
||||||
|
display->fillRect(boxLeft, boxTop - 2, boxWidth, 1);
|
||||||
|
display->fillRect(boxLeft - 2, boxTop, 1, boxHeight);
|
||||||
|
display->fillRect(boxLeft + boxWidth + 1, boxTop, 1, boxHeight);
|
||||||
|
display->setColor(BLACK);
|
||||||
|
display->fillRect(boxLeft, boxTop, 1, 1);
|
||||||
|
display->fillRect(boxLeft + boxWidth - 1, boxTop, 1, 1);
|
||||||
|
display->fillRect(boxLeft, boxTop + boxHeight - 1, 1, 1);
|
||||||
|
display->fillRect(boxLeft + boxWidth - 1, boxTop + boxHeight - 1, 1, 1);
|
||||||
|
display->setColor(WHITE);
|
||||||
|
int16_t lineY = boxTop + vPadding;
|
||||||
|
int swingRange = 8;
|
||||||
|
static int swingOffset = 0;
|
||||||
|
static bool swingRight = true;
|
||||||
|
static unsigned long lastSwingTime = 0;
|
||||||
|
unsigned long now = millis();
|
||||||
|
int swingSpeedMs = 10 / (swingRange * 2);
|
||||||
|
if (now - lastSwingTime >= (unsigned long)swingSpeedMs) {
|
||||||
|
lastSwingTime = now;
|
||||||
|
if (swingRight) {
|
||||||
|
swingOffset++;
|
||||||
|
if (swingOffset >= swingRange)
|
||||||
|
swingRight = false;
|
||||||
|
} else {
|
||||||
|
swingOffset--;
|
||||||
|
if (swingOffset <= 0)
|
||||||
|
swingRight = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < lineCount; i++) {
|
||||||
|
bool isTitle = (i == 0);
|
||||||
|
int globalOptionIndex = (i - 1) + firstOptionToShow;
|
||||||
|
bool isSelectedOption = (!isTitle && globalOptionIndex >= 0 && globalOptionIndex == curSelected);
|
||||||
|
|
||||||
|
uint16_t visibleWidth = 64 - hPadding * 2;
|
||||||
|
if (totalLines > visibleTotalLines)
|
||||||
|
visibleWidth -= 6;
|
||||||
|
char lineBuffer[lineLengths[i] + 1];
|
||||||
|
strncpy(lineBuffer, lines[i], lineLengths[i]);
|
||||||
|
lineBuffer[lineLengths[i]] = '\0';
|
||||||
|
|
||||||
|
if (isTitle) {
|
||||||
|
if (visibleTotalLines == 1) {
|
||||||
|
display->setColor(BLACK);
|
||||||
|
display->fillRect(boxLeft, boxTop, boxWidth, effectiveLineHeight);
|
||||||
|
display->setColor(WHITE);
|
||||||
|
display->drawString(boxLeft + (boxWidth - lineWidths[i]) / 2, boxTop, lineBuffer);
|
||||||
|
} else {
|
||||||
|
display->setColor(WHITE);
|
||||||
|
display->fillRect(boxLeft, boxTop, boxWidth, effectiveLineHeight);
|
||||||
|
display->setColor(BLACK);
|
||||||
|
display->drawString(boxLeft + (boxWidth - lineWidths[i]) / 2, boxTop, lineBuffer);
|
||||||
|
display->setColor(WHITE);
|
||||||
|
if (needs_bell) {
|
||||||
|
int bellY = boxTop + (FONT_HEIGHT_SMALL - 8) / 2;
|
||||||
|
display->drawXbm(boxLeft + (boxWidth - lineWidths[i]) / 2 - 10, bellY, 8, 8, bell_alert);
|
||||||
|
display->drawXbm(boxLeft + (boxWidth + lineWidths[i]) / 2 + 2, bellY, 8, 8, bell_alert);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lineY = boxTop + effectiveLineHeight + 1;
|
||||||
|
} else if (isSelectedOption) {
|
||||||
|
display->setColor(WHITE);
|
||||||
|
display->fillRect(boxLeft, lineY, boxWidth, effectiveLineHeight);
|
||||||
|
display->setColor(BLACK);
|
||||||
|
if (lineLengths[i] > 15 && lineWidths[i] > visibleWidth) {
|
||||||
|
int textX = boxLeft + hPadding + swingOffset;
|
||||||
|
display->drawString(textX, lineY - 1, lineBuffer);
|
||||||
|
} else {
|
||||||
|
display->drawString(boxLeft + (boxWidth - lineWidths[i]) / 2, lineY - 1, lineBuffer);
|
||||||
|
}
|
||||||
|
display->setColor(WHITE);
|
||||||
|
lineY += effectiveLineHeight;
|
||||||
|
} else {
|
||||||
|
display->setColor(BLACK);
|
||||||
|
display->fillRect(boxLeft, lineY, boxWidth, effectiveLineHeight);
|
||||||
|
display->setColor(WHITE);
|
||||||
|
display->drawString(boxLeft + (boxWidth - lineWidths[i]) / 2, lineY, lineBuffer);
|
||||||
|
lineY += effectiveLineHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (totalLines > visibleTotalLines) {
|
||||||
|
const uint8_t scrollBarWidth = 5;
|
||||||
|
int16_t scrollBarX = boxLeft + boxWidth - scrollBarWidth - 2;
|
||||||
|
int16_t scrollBarY = boxTop + vPadding + effectiveLineHeight;
|
||||||
|
uint16_t scrollBarHeight = boxHeight - vPadding * 2 - effectiveLineHeight;
|
||||||
|
float ratio = (float)visibleTotalLines / totalLines;
|
||||||
|
uint16_t indicatorHeight = std::max((int)(scrollBarHeight * ratio), 4);
|
||||||
|
float scrollRatio = (float)(firstOptionToShow + lineCount - visibleTotalLines) / (totalLines - visibleTotalLines);
|
||||||
|
uint16_t indicatorY = scrollBarY + scrollRatio * (scrollBarHeight - indicatorHeight);
|
||||||
|
display->drawRect(scrollBarX, scrollBarY, scrollBarWidth, scrollBarHeight);
|
||||||
|
display->fillRect(scrollBarX + 1, indicatorY, scrollBarWidth - 2, indicatorHeight);
|
||||||
|
}
|
||||||
|
#else
|
||||||
if (needs_bell) {
|
if (needs_bell) {
|
||||||
if (isHighResolution && boxWidth <= 150)
|
if (isHighResolution && boxWidth <= 150)
|
||||||
boxWidth += 26;
|
boxWidth += 26;
|
||||||
@@ -579,6 +712,7 @@ void NotificationRenderer::drawNotificationBox(OLEDDisplay *display, OLEDDisplay
|
|||||||
display->drawRect(scrollBarX, scrollBarY, scrollBarWidth, scrollBarHeight);
|
display->drawRect(scrollBarX, scrollBarY, scrollBarWidth, scrollBarHeight);
|
||||||
display->fillRect(scrollBarX + 1, indicatorY, scrollBarWidth - 2, indicatorHeight);
|
display->fillRect(scrollBarX + 1, indicatorY, scrollBarWidth - 2, indicatorHeight);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw the last text message we received
|
/// Draw the last text message we received
|
||||||
|
|||||||
@@ -20,7 +20,9 @@
|
|||||||
|
|
||||||
// External variables
|
// External variables
|
||||||
extern graphics::Screen *screen;
|
extern graphics::Screen *screen;
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
static uint32_t lastSwitchTime = 0;
|
||||||
|
#endif
|
||||||
namespace graphics
|
namespace graphics
|
||||||
{
|
{
|
||||||
NodeNum UIRenderer::currentFavoriteNodeNum = 0;
|
NodeNum UIRenderer::currentFavoriteNodeNum = 0;
|
||||||
@@ -116,64 +118,124 @@ void UIRenderer::drawGpsAltitude(OLEDDisplay *display, int16_t x, int16_t y, con
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Draw GPS status coordinates
|
// Draw GPS status coordinates
|
||||||
void UIRenderer::drawGpsCoordinates(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gps)
|
void UIRenderer::drawGpsCoordinates(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gps,
|
||||||
|
const char *mode)
|
||||||
{
|
{
|
||||||
auto gpsFormat = config.display.gps_format;
|
auto gpsFormat = uiconfig.gps_format;
|
||||||
char displayLine[32];
|
char displayLine[32];
|
||||||
|
|
||||||
if (!gps->getIsConnected() && !config.position.fixed_position) {
|
if (!gps->getIsConnected() && !config.position.fixed_position) {
|
||||||
strcpy(displayLine, "No GPS present");
|
if (strcmp(mode, "line1") == 0) {
|
||||||
display->drawString(x + (display->getWidth() - (display->getStringWidth(displayLine))) / 2, y, displayLine);
|
strcpy(displayLine, "No GPS present");
|
||||||
|
display->drawString(x, y, displayLine);
|
||||||
|
}
|
||||||
} else if (!gps->getHasLock() && !config.position.fixed_position) {
|
} else if (!gps->getHasLock() && !config.position.fixed_position) {
|
||||||
strcpy(displayLine, "No GPS Lock");
|
if (strcmp(mode, "line1") == 0) {
|
||||||
display->drawString(x + (display->getWidth() - (display->getStringWidth(displayLine))) / 2, y, displayLine);
|
strcpy(displayLine, "No GPS Lock");
|
||||||
|
display->drawString(x, y, displayLine);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
geoCoord.updateCoords(int32_t(gps->getLatitude()), int32_t(gps->getLongitude()), int32_t(gps->getAltitude()));
|
geoCoord.updateCoords(int32_t(gps->getLatitude()), int32_t(gps->getLongitude()), int32_t(gps->getAltitude()));
|
||||||
|
|
||||||
if (gpsFormat != meshtastic_Config_DisplayConfig_GpsCoordinateFormat_DMS) {
|
if (gpsFormat != meshtastic_DeviceUIConfig_GpsCoordinateFormat_DMS) {
|
||||||
char coordinateLine[22];
|
char coordinateLine_1[22];
|
||||||
if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_DEC) { // Decimal Degrees
|
char coordinateLine_2[22];
|
||||||
snprintf(coordinateLine, sizeof(coordinateLine), "%f %f", geoCoord.getLatitude() * 1e-7,
|
if (gpsFormat == meshtastic_DeviceUIConfig_GpsCoordinateFormat_DEC) { // Decimal Degrees
|
||||||
geoCoord.getLongitude() * 1e-7);
|
snprintf(coordinateLine_1, sizeof(coordinateLine_1), "Lat: %f", geoCoord.getLatitude() * 1e-7);
|
||||||
} else if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_UTM) { // Universal Transverse Mercator
|
snprintf(coordinateLine_2, sizeof(coordinateLine_2), "Lon: %f", geoCoord.getLongitude() * 1e-7);
|
||||||
snprintf(coordinateLine, sizeof(coordinateLine), "%2i%1c %06u %07u", geoCoord.getUTMZone(), geoCoord.getUTMBand(),
|
} else if (gpsFormat == meshtastic_DeviceUIConfig_GpsCoordinateFormat_UTM) { // Universal Transverse Mercator
|
||||||
geoCoord.getUTMEasting(), geoCoord.getUTMNorthing());
|
snprintf(coordinateLine_1, sizeof(coordinateLine_1), "%2i%1c %06u E", geoCoord.getUTMZone(),
|
||||||
} else if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MGRS) { // Military Grid Reference System
|
geoCoord.getUTMBand(), geoCoord.getUTMEasting());
|
||||||
snprintf(coordinateLine, sizeof(coordinateLine), "%2i%1c %1c%1c %05u %05u", geoCoord.getMGRSZone(),
|
snprintf(coordinateLine_2, sizeof(coordinateLine_2), "%07u N", geoCoord.getUTMNorthing());
|
||||||
geoCoord.getMGRSBand(), geoCoord.getMGRSEast100k(), geoCoord.getMGRSNorth100k(),
|
} else if (gpsFormat == meshtastic_DeviceUIConfig_GpsCoordinateFormat_MGRS) { // Military Grid Reference System
|
||||||
geoCoord.getMGRSEasting(), geoCoord.getMGRSNorthing());
|
snprintf(coordinateLine_1, sizeof(coordinateLine_1), "%2i%1c %1c%1c", geoCoord.getMGRSZone(),
|
||||||
} else if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_OLC) { // Open Location Code
|
geoCoord.getMGRSBand(), geoCoord.getMGRSEast100k(), geoCoord.getMGRSNorth100k());
|
||||||
geoCoord.getOLCCode(coordinateLine);
|
snprintf(coordinateLine_2, sizeof(coordinateLine_2), "%05u E %05u N", geoCoord.getMGRSEasting(),
|
||||||
} else if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_OSGR) { // Ordnance Survey Grid Reference
|
geoCoord.getMGRSNorthing());
|
||||||
if (geoCoord.getOSGRE100k() == 'I' || geoCoord.getOSGRN100k() == 'I') // OSGR is only valid around the UK region
|
} else if (gpsFormat == meshtastic_DeviceUIConfig_GpsCoordinateFormat_OLC) { // Open Location Code
|
||||||
snprintf(coordinateLine, sizeof(coordinateLine), "%s", "Out of Boundary");
|
geoCoord.getOLCCode(coordinateLine_1);
|
||||||
else
|
coordinateLine_2[0] = '\0';
|
||||||
snprintf(coordinateLine, sizeof(coordinateLine), "%1c%1c %05u %05u", geoCoord.getOSGRE100k(),
|
} else if (gpsFormat == meshtastic_DeviceUIConfig_GpsCoordinateFormat_OSGR) { // Ordnance Survey Grid Reference
|
||||||
geoCoord.getOSGRN100k(), geoCoord.getOSGREasting(), geoCoord.getOSGRNorthing());
|
if (geoCoord.getOSGRE100k() == 'I' || geoCoord.getOSGRN100k() == 'I') { // OSGR is only valid around the UK region
|
||||||
|
snprintf(coordinateLine_1, sizeof(coordinateLine_1), "%s", "Out of Boundary");
|
||||||
|
coordinateLine_2[0] = '\0';
|
||||||
|
} else {
|
||||||
|
snprintf(coordinateLine_1, sizeof(coordinateLine_1), "%1c%1c", geoCoord.getOSGRE100k(),
|
||||||
|
geoCoord.getOSGRN100k());
|
||||||
|
snprintf(coordinateLine_2, sizeof(coordinateLine_2), "%05u E %05u N", geoCoord.getOSGREasting(),
|
||||||
|
geoCoord.getOSGRNorthing());
|
||||||
|
}
|
||||||
|
} else if (gpsFormat == meshtastic_DeviceUIConfig_GpsCoordinateFormat_MLS) { // Maidenhead Locator System
|
||||||
|
double lat = geoCoord.getLatitude() * 1e-7;
|
||||||
|
double lon = geoCoord.getLongitude() * 1e-7;
|
||||||
|
|
||||||
|
// Normalize
|
||||||
|
if (lat > 90.0)
|
||||||
|
lat = 90.0;
|
||||||
|
if (lat < -90.0)
|
||||||
|
lat = -90.0;
|
||||||
|
while (lon < -180.0)
|
||||||
|
lon += 360.0;
|
||||||
|
while (lon >= 180.0)
|
||||||
|
lon -= 360.0;
|
||||||
|
|
||||||
|
double adjLon = lon + 180.0;
|
||||||
|
double adjLat = lat + 90.0;
|
||||||
|
|
||||||
|
char maiden[10]; // enough for 8-char + null
|
||||||
|
|
||||||
|
// Field (2 letters)
|
||||||
|
int lonField = int(adjLon / 20.0);
|
||||||
|
int latField = int(adjLat / 10.0);
|
||||||
|
adjLon -= lonField * 20.0;
|
||||||
|
adjLat -= latField * 10.0;
|
||||||
|
|
||||||
|
// Square (2 digits)
|
||||||
|
int lonSquare = int(adjLon / 2.0);
|
||||||
|
int latSquare = int(adjLat / 1.0);
|
||||||
|
adjLon -= lonSquare * 2.0;
|
||||||
|
adjLat -= latSquare * 1.0;
|
||||||
|
|
||||||
|
// Subsquare (2 letters)
|
||||||
|
double lonUnit = 2.0 / 24.0;
|
||||||
|
double latUnit = 1.0 / 24.0;
|
||||||
|
int lonSub = int(adjLon / lonUnit);
|
||||||
|
int latSub = int(adjLat / latUnit);
|
||||||
|
|
||||||
|
snprintf(maiden, sizeof(maiden), "%c%c%c%c%c%c", 'A' + lonField, 'A' + latField, '0' + lonSquare, '0' + latSquare,
|
||||||
|
'A' + lonSub, 'A' + latSub);
|
||||||
|
|
||||||
|
snprintf(coordinateLine_1, sizeof(coordinateLine_1), "MH: %s", maiden);
|
||||||
|
coordinateLine_2[0] = '\0'; // only need one line
|
||||||
}
|
}
|
||||||
|
|
||||||
// If fixed position, display text "Fixed GPS" alternating with the coordinates.
|
if (strcmp(mode, "line1") == 0) {
|
||||||
if (config.position.fixed_position) {
|
display->drawString(x, y, coordinateLine_1);
|
||||||
if ((millis() / 10000) % 2) {
|
} else if (strcmp(mode, "line2") == 0) {
|
||||||
display->drawString(x + (display->getWidth() - (display->getStringWidth(coordinateLine))) / 2, y,
|
display->drawString(x, y, coordinateLine_2);
|
||||||
coordinateLine);
|
} else if (strcmp(mode, "combined") == 0) {
|
||||||
} else {
|
display->drawString(x, y, coordinateLine_1);
|
||||||
display->drawString(x + (display->getWidth() - (display->getStringWidth("Fixed GPS"))) / 2, y, "Fixed GPS");
|
if (coordinateLine_2[0] != '\0') {
|
||||||
|
display->drawString(x + display->getStringWidth(coordinateLine_1), y, coordinateLine_2);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
display->drawString(x + (display->getWidth() - (display->getStringWidth(coordinateLine))) / 2, y, coordinateLine);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
char latLine[22];
|
char coordinateLine_1[22];
|
||||||
char lonLine[22];
|
char coordinateLine_2[22];
|
||||||
snprintf(latLine, sizeof(latLine), "%2i° %2i' %2u\" %1c", geoCoord.getDMSLatDeg(), geoCoord.getDMSLatMin(),
|
snprintf(coordinateLine_1, sizeof(coordinateLine_1), "Lat: %2i° %2i' %2u\" %1c", geoCoord.getDMSLatDeg(),
|
||||||
geoCoord.getDMSLatSec(), geoCoord.getDMSLatCP());
|
geoCoord.getDMSLatMin(), geoCoord.getDMSLatSec(), geoCoord.getDMSLatCP());
|
||||||
snprintf(lonLine, sizeof(lonLine), "%3i° %2i' %2u\" %1c", geoCoord.getDMSLonDeg(), geoCoord.getDMSLonMin(),
|
snprintf(coordinateLine_2, sizeof(coordinateLine_2), "Lon: %3i° %2i' %2u\" %1c", geoCoord.getDMSLonDeg(),
|
||||||
geoCoord.getDMSLonSec(), geoCoord.getDMSLonCP());
|
geoCoord.getDMSLonMin(), geoCoord.getDMSLonSec(), geoCoord.getDMSLonCP());
|
||||||
display->drawString(x + (display->getWidth() - (display->getStringWidth(latLine))) / 2, y - FONT_HEIGHT_SMALL * 1,
|
if (strcmp(mode, "line1") == 0) {
|
||||||
latLine);
|
display->drawString(x, y, coordinateLine_1);
|
||||||
display->drawString(x + (display->getWidth() - (display->getStringWidth(lonLine))) / 2, y, lonLine);
|
} else if (strcmp(mode, "line2") == 0) {
|
||||||
|
display->drawString(x, y, coordinateLine_2);
|
||||||
|
} else { // both
|
||||||
|
display->drawString(x, y, coordinateLine_1);
|
||||||
|
display->drawString(x, y + 10, coordinateLine_2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -218,7 +280,6 @@ void UIRenderer::drawNodes(OLEDDisplay *display, int16_t x, int16_t y, const mes
|
|||||||
// **********************
|
// **********************
|
||||||
void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *state, int16_t x, int16_t y)
|
void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (favoritedNodes.empty())
|
if (favoritedNodes.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -230,8 +291,15 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st
|
|||||||
meshtastic_NodeInfoLite *node = favoritedNodes[nodeIndex];
|
meshtastic_NodeInfoLite *node = favoritedNodes[nodeIndex];
|
||||||
if (!node || node->num == nodeDB->getNodeNum() || !node->is_favorite)
|
if (!node || node->num == nodeDB->getNodeNum() || !node->is_favorite)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
display->clear();
|
display->clear();
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
uint32_t now = millis();
|
||||||
|
if (now - lastSwitchTime >= 10000) // 10000 ms = 10 秒
|
||||||
|
{
|
||||||
|
display->display();
|
||||||
|
lastSwitchTime = now;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
currentFavoriteNodeNum = node->num;
|
currentFavoriteNodeNum = node->num;
|
||||||
// === Create the shortName and title string ===
|
// === Create the shortName and title string ===
|
||||||
const char *shortName = (node->has_user && haveGlyphs(node->user.short_name)) ? node->user.short_name : "Node";
|
const char *shortName = (node->has_user && haveGlyphs(node->user.short_name)) ? node->user.short_name : "Node";
|
||||||
@@ -250,9 +318,13 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st
|
|||||||
// List of available macro Y positions in order, from top to bottom.
|
// List of available macro Y positions in order, from top to bottom.
|
||||||
int line = 1; // which slot to use next
|
int line = 1; // which slot to use next
|
||||||
std::string usernameStr;
|
std::string usernameStr;
|
||||||
|
|
||||||
// === 1. Long Name (always try to show first) ===
|
// === 1. Long Name (always try to show first) ===
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
const char *username = (node->has_user && node->user.long_name[0]) ? node->user.short_name : nullptr;
|
||||||
|
#else
|
||||||
const char *username = (node->has_user && node->user.long_name[0]) ? node->user.long_name : nullptr;
|
const char *username = (node->has_user && node->user.long_name[0]) ? node->user.long_name : nullptr;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (username) {
|
if (username) {
|
||||||
usernameStr = sanitizeString(username); // Sanitize the incoming long_name just in case
|
usernameStr = sanitizeString(username); // Sanitize the incoming long_name just in case
|
||||||
// Print node's long name (e.g. "Backpack Node")
|
// Print node's long name (e.g. "Backpack Node")
|
||||||
@@ -307,7 +379,7 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st
|
|||||||
if (seenStr[0] && line < 5) {
|
if (seenStr[0] && line < 5) {
|
||||||
display->drawString(x, getTextPositions(display)[line++], seenStr);
|
display->drawString(x, getTextPositions(display)[line++], seenStr);
|
||||||
}
|
}
|
||||||
|
#if !defined(M5STACK_UNITC6L)
|
||||||
// === 4. Uptime (only show if metric is present) ===
|
// === 4. Uptime (only show if metric is present) ===
|
||||||
char uptimeStr[32] = "";
|
char uptimeStr[32] = "";
|
||||||
if (node->has_device_metrics && node->device_metrics.has_uptime_seconds) {
|
if (node->has_device_metrics && node->device_metrics.has_uptime_seconds) {
|
||||||
@@ -479,6 +551,7 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st
|
|||||||
}
|
}
|
||||||
// else show nothing
|
// else show nothing
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// ****************************
|
// ****************************
|
||||||
@@ -490,9 +563,14 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta
|
|||||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||||
display->setFont(FONT_SMALL);
|
display->setFont(FONT_SMALL);
|
||||||
int line = 1;
|
int line = 1;
|
||||||
|
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||||
|
|
||||||
// === Header ===
|
// === Header ===
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
graphics::drawCommonHeader(display, x, y, "Home");
|
||||||
|
#else
|
||||||
graphics::drawCommonHeader(display, x, y, "");
|
graphics::drawCommonHeader(display, x, y, "");
|
||||||
|
#endif
|
||||||
|
|
||||||
// === Content below header ===
|
// === Content below header ===
|
||||||
|
|
||||||
@@ -507,20 +585,25 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta
|
|||||||
config.display.heading_bold = false;
|
config.display.heading_bold = false;
|
||||||
|
|
||||||
// Display Region and Channel Utilization
|
// Display Region and Channel Utilization
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
drawNodes(display, x, getTextPositions(display)[line] + 2, nodeStatus, -1, false, "online");
|
||||||
|
#else
|
||||||
drawNodes(display, x + 1, getTextPositions(display)[line] + 2, nodeStatus, -1, false, "online");
|
drawNodes(display, x + 1, getTextPositions(display)[line] + 2, nodeStatus, -1, false, "online");
|
||||||
|
#endif
|
||||||
char uptimeStr[32] = "";
|
char uptimeStr[32] = "";
|
||||||
uint32_t uptime = millis() / 1000;
|
uint32_t uptime = millis() / 1000;
|
||||||
uint32_t days = uptime / 86400;
|
uint32_t days = uptime / 86400;
|
||||||
uint32_t hours = (uptime % 86400) / 3600;
|
uint32_t hours = (uptime % 86400) / 3600;
|
||||||
uint32_t mins = (uptime % 3600) / 60;
|
uint32_t mins = (uptime % 3600) / 60;
|
||||||
// Show as "Up: 2d 3h", "Up: 5h 14m", or "Up: 37m"
|
// Show as "Up: 2d 3h", "Up: 5h 14m", or "Up: 37m"
|
||||||
|
#if !defined(M5STACK_UNITC6L)
|
||||||
if (days)
|
if (days)
|
||||||
snprintf(uptimeStr, sizeof(uptimeStr), "Up: %ud %uh", days, hours);
|
snprintf(uptimeStr, sizeof(uptimeStr), "Up: %ud %uh", days, hours);
|
||||||
else if (hours)
|
else if (hours)
|
||||||
snprintf(uptimeStr, sizeof(uptimeStr), "Up: %uh %um", hours, mins);
|
snprintf(uptimeStr, sizeof(uptimeStr), "Up: %uh %um", hours, mins);
|
||||||
else
|
else
|
||||||
snprintf(uptimeStr, sizeof(uptimeStr), "Up: %um", mins);
|
snprintf(uptimeStr, sizeof(uptimeStr), "Up: %um", mins);
|
||||||
|
#endif
|
||||||
display->drawString(SCREEN_WIDTH - display->getStringWidth(uptimeStr), getTextPositions(display)[line++], uptimeStr);
|
display->drawString(SCREEN_WIDTH - display->getStringWidth(uptimeStr), getTextPositions(display)[line++], uptimeStr);
|
||||||
|
|
||||||
// === Second Row: Satellites and Voltage ===
|
// === Second Row: Satellites and Voltage ===
|
||||||
@@ -549,6 +632,21 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
line += 1;
|
||||||
|
|
||||||
|
// === Node Identity ===
|
||||||
|
int textWidth = 0;
|
||||||
|
int nameX = 0;
|
||||||
|
char shortnameble[35];
|
||||||
|
snprintf(shortnameble, sizeof(shortnameble), "%s",
|
||||||
|
graphics::UIRenderer::haveGlyphs(owner.short_name) ? owner.short_name : "");
|
||||||
|
|
||||||
|
// === ShortName Centered ===
|
||||||
|
textWidth = display->getStringWidth(shortnameble);
|
||||||
|
nameX = (SCREEN_WIDTH - textWidth) / 2;
|
||||||
|
display->drawString(nameX, getTextPositions(display)[line++], shortnameble);
|
||||||
|
#else
|
||||||
if (powerStatus->getHasBattery()) {
|
if (powerStatus->getHasBattery()) {
|
||||||
char batStr[20];
|
char batStr[20];
|
||||||
int batV = powerStatus->getBatteryVoltageMv() / 1000;
|
int batV = powerStatus->getBatteryVoltageMv() / 1000;
|
||||||
@@ -641,10 +739,8 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta
|
|||||||
int textWidth = 0;
|
int textWidth = 0;
|
||||||
int nameX = 0;
|
int nameX = 0;
|
||||||
int yOffset = (isHighResolution) ? 0 : 5;
|
int yOffset = (isHighResolution) ? 0 : 5;
|
||||||
const char *longName = nullptr;
|
|
||||||
std::string longNameStr;
|
std::string longNameStr;
|
||||||
|
|
||||||
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
|
||||||
if (ourNode && ourNode->has_user && strlen(ourNode->user.long_name) > 0) {
|
if (ourNode && ourNode->has_user && strlen(ourNode->user.long_name) > 0) {
|
||||||
longNameStr = sanitizeString(ourNode->user.long_name);
|
longNameStr = sanitizeString(ourNode->user.long_name);
|
||||||
}
|
}
|
||||||
@@ -674,6 +770,7 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta
|
|||||||
nameX = (SCREEN_WIDTH - textWidth) / 2;
|
nameX = (SCREEN_WIDTH - textWidth) / 2;
|
||||||
display->drawString(nameX, getTextPositions(display)[line++], shortnameble);
|
display->drawString(nameX, getTextPositions(display)[line++], shortnameble);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start Functions to write date/time to the screen
|
// Start Functions to write date/time to the screen
|
||||||
@@ -832,6 +929,28 @@ void UIRenderer::drawIconScreen(const char *upperMsg, OLEDDisplay *display, OLED
|
|||||||
// needs to be drawn relative to x and y
|
// needs to be drawn relative to x and y
|
||||||
|
|
||||||
// draw centered icon left to right and centered above the one line of app text
|
// draw centered icon left to right and centered above the one line of app text
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
display->drawXbm(x + (SCREEN_WIDTH - 50) / 2, y + (SCREEN_HEIGHT - 28) / 2, icon_width, icon_height, icon_bits);
|
||||||
|
display->setFont(FONT_MEDIUM);
|
||||||
|
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||||
|
display->setFont(FONT_SMALL);
|
||||||
|
// Draw region in upper left
|
||||||
|
if (upperMsg) {
|
||||||
|
int msgWidth = display->getStringWidth(upperMsg);
|
||||||
|
int msgX = x + (SCREEN_WIDTH - msgWidth) / 2;
|
||||||
|
int msgY = y;
|
||||||
|
display->drawString(msgX, msgY, upperMsg);
|
||||||
|
}
|
||||||
|
// Draw version and short name in bottom middle
|
||||||
|
char buf[25];
|
||||||
|
snprintf(buf, sizeof(buf), "%s %s", xstr(APP_VERSION_SHORT),
|
||||||
|
graphics::UIRenderer::haveGlyphs(owner.short_name) ? owner.short_name : "");
|
||||||
|
|
||||||
|
display->drawString(x + getStringCenteredX(buf), y + SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM, buf);
|
||||||
|
screen->forceDisplay();
|
||||||
|
|
||||||
|
display->setTextAlignment(TEXT_ALIGN_LEFT); // Restore left align, just to be kind to any other unsuspecting code
|
||||||
|
#else
|
||||||
display->drawXbm(x + (SCREEN_WIDTH - icon_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - icon_height) / 2 + 2,
|
display->drawXbm(x + (SCREEN_WIDTH - icon_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - icon_height) / 2 + 2,
|
||||||
icon_width, icon_height, icon_bits);
|
icon_width, icon_height, icon_bits);
|
||||||
|
|
||||||
@@ -840,7 +959,6 @@ void UIRenderer::drawIconScreen(const char *upperMsg, OLEDDisplay *display, OLED
|
|||||||
const char *title = "meshtastic.org";
|
const char *title = "meshtastic.org";
|
||||||
display->drawString(x + getStringCenteredX(title), y + SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM, title);
|
display->drawString(x + getStringCenteredX(title), y + SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM, title);
|
||||||
display->setFont(FONT_SMALL);
|
display->setFont(FONT_SMALL);
|
||||||
|
|
||||||
// Draw region in upper left
|
// Draw region in upper left
|
||||||
if (upperMsg)
|
if (upperMsg)
|
||||||
display->drawString(x + 0, y + 0, upperMsg);
|
display->drawString(x + 0, y + 0, upperMsg);
|
||||||
@@ -855,6 +973,7 @@ void UIRenderer::drawIconScreen(const char *upperMsg, OLEDDisplay *display, OLED
|
|||||||
screen->forceDisplay();
|
screen->forceDisplay();
|
||||||
|
|
||||||
display->setTextAlignment(TEXT_ALIGN_LEFT); // Restore left align, just to be kind to any other unsuspecting code
|
display->setTextAlignment(TEXT_ALIGN_LEFT); // Restore left align, just to be kind to any other unsuspecting code
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// ****************************
|
// ****************************
|
||||||
@@ -881,24 +1000,7 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU
|
|||||||
const char *displayLine = ""; // Initialize to empty string by default
|
const char *displayLine = ""; // Initialize to empty string by default
|
||||||
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||||
|
|
||||||
bool usePhoneGPS = (ourNode && nodeDB->hasValidPosition(ourNode) &&
|
if (config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
|
||||||
config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED);
|
|
||||||
|
|
||||||
if (usePhoneGPS) {
|
|
||||||
// Phone-provided GPS is active
|
|
||||||
displayLine = "Phone GPS";
|
|
||||||
int yOffset = (isHighResolution) ? 3 : 1;
|
|
||||||
if (isHighResolution) {
|
|
||||||
NodeListRenderer::drawScaledXBitmap16x16(x, getTextPositions(display)[line] + yOffset - 5, imgSatellite_width,
|
|
||||||
imgSatellite_height, imgSatellite, display);
|
|
||||||
} else {
|
|
||||||
display->drawXbm(x + 1, getTextPositions(display)[line] + yOffset, imgSatellite_width, imgSatellite_height,
|
|
||||||
imgSatellite);
|
|
||||||
}
|
|
||||||
int xOffset = (isHighResolution) ? 6 : 0;
|
|
||||||
display->drawString(x + 11 + xOffset, getTextPositions(display)[line++], displayLine);
|
|
||||||
} else if (config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
|
|
||||||
// GPS disabled / not present
|
|
||||||
if (config.position.fixed_position) {
|
if (config.position.fixed_position) {
|
||||||
displayLine = "Fixed GPS";
|
displayLine = "Fixed GPS";
|
||||||
} else {
|
} else {
|
||||||
@@ -954,11 +1056,11 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU
|
|||||||
#if defined(USE_EINK)
|
#if defined(USE_EINK)
|
||||||
// E-Ink: skip seconds, show only days/hours/mins
|
// E-Ink: skip seconds, show only days/hours/mins
|
||||||
if (days > 0) {
|
if (days > 0) {
|
||||||
snprintf(buf, sizeof(buf), " Last: %ud %uh", days, hours);
|
snprintf(buf, sizeof(buf), "Last: %ud %uh", days, hours);
|
||||||
} else if (hours > 0) {
|
} else if (hours > 0) {
|
||||||
snprintf(buf, sizeof(buf), " Last: %uh %um", hours, mins);
|
snprintf(buf, sizeof(buf), "Last: %uh %um", hours, mins);
|
||||||
} else {
|
} else {
|
||||||
snprintf(buf, sizeof(buf), " Last: %um", mins);
|
snprintf(buf, sizeof(buf), "Last: %um", mins);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
// Non E-Ink: include seconds where useful
|
// Non E-Ink: include seconds where useful
|
||||||
@@ -978,29 +1080,26 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU
|
|||||||
display->drawString(0, getTextPositions(display)[line++], "Last: ?");
|
display->drawString(0, getTextPositions(display)[line++], "Last: ?");
|
||||||
}
|
}
|
||||||
|
|
||||||
// === Third Row: Latitude ===
|
// === Third Row: Line 1 GPS Info ===
|
||||||
char latStr[32];
|
UIRenderer::drawGpsCoordinates(display, x, getTextPositions(display)[line++], gpsStatus, "line1");
|
||||||
snprintf(latStr, sizeof(latStr), "Lat: %.5f", geoCoord.getLatitude() * 1e-7);
|
|
||||||
display->drawString(x, getTextPositions(display)[line++], latStr);
|
|
||||||
|
|
||||||
// === Fourth Row: Longitude ===
|
if (uiconfig.gps_format != meshtastic_DeviceUIConfig_GpsCoordinateFormat_OLC &&
|
||||||
char lonStr[32];
|
uiconfig.gps_format != meshtastic_DeviceUIConfig_GpsCoordinateFormat_MLS) {
|
||||||
snprintf(lonStr, sizeof(lonStr), "Lon: %.5f", geoCoord.getLongitude() * 1e-7);
|
// === Fourth Row: Line 2 GPS Info ===
|
||||||
display->drawString(x, getTextPositions(display)[line++], lonStr);
|
UIRenderer::drawGpsCoordinates(display, x, getTextPositions(display)[line++], gpsStatus, "line2");
|
||||||
|
|
||||||
// === Fifth Row: Altitude ===
|
|
||||||
char DisplayLineTwo[32] = {0};
|
|
||||||
int32_t alt = (strcmp(displayLine, "Phone GPS") == 0 && ourNode && nodeDB->hasValidPosition(ourNode))
|
|
||||||
? ourNode->position.altitude
|
|
||||||
: geoCoord.getAltitude();
|
|
||||||
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
|
|
||||||
snprintf(DisplayLineTwo, sizeof(DisplayLineTwo), "Alt: %.0fft", geoCoord.getAltitude() * METERS_TO_FEET);
|
|
||||||
} else {
|
|
||||||
snprintf(DisplayLineTwo, sizeof(DisplayLineTwo), "Alt: %.0im", geoCoord.getAltitude());
|
|
||||||
}
|
}
|
||||||
display->drawString(x, getTextPositions(display)[line++], DisplayLineTwo);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// === Final Row: Altitude ===
|
||||||
|
char altitudeLine[32] = {0};
|
||||||
|
int32_t alt = geoCoord.getAltitude();
|
||||||
|
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
|
||||||
|
snprintf(altitudeLine, sizeof(altitudeLine), "Alt: %.0fft", alt * METERS_TO_FEET);
|
||||||
|
} else {
|
||||||
|
snprintf(altitudeLine, sizeof(altitudeLine), "Alt: %.0im", alt);
|
||||||
|
}
|
||||||
|
display->drawString(x, getTextPositions(display)[line++], altitudeLine);
|
||||||
|
}
|
||||||
|
#if !defined(M5STACK_UNITC6L)
|
||||||
// === Draw Compass if heading is valid ===
|
// === Draw Compass if heading is valid ===
|
||||||
if (validHeading) {
|
if (validHeading) {
|
||||||
// --- Compass Rendering: landscape (wide) screens use original side-aligned logic ---
|
// --- Compass Rendering: landscape (wide) screens use original side-aligned logic ---
|
||||||
@@ -1083,6 +1182,7 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#endif // HAS_GPS
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USERPREFS_OEM_TEXT
|
#ifdef USERPREFS_OEM_TEXT
|
||||||
@@ -1175,14 +1275,13 @@ void UIRenderer::drawNavigationBar(OLEDDisplay *display, OLEDDisplayUiState *sta
|
|||||||
const int totalWidth = (pageEnd - pageStart) * iconSize + (pageEnd - pageStart - 1) * spacing;
|
const int totalWidth = (pageEnd - pageStart) * iconSize + (pageEnd - pageStart - 1) * spacing;
|
||||||
const int xStart = (SCREEN_WIDTH - totalWidth) / 2;
|
const int xStart = (SCREEN_WIDTH - totalWidth) / 2;
|
||||||
|
|
||||||
// Only show bar briefly after switching frames
|
|
||||||
static uint32_t navBarLastShown = 0;
|
|
||||||
static bool cosmeticRefreshDone = false;
|
|
||||||
|
|
||||||
bool navBarVisible = millis() - lastFrameChangeTime <= ICON_DISPLAY_DURATION_MS;
|
bool navBarVisible = millis() - lastFrameChangeTime <= ICON_DISPLAY_DURATION_MS;
|
||||||
int y = navBarVisible ? (SCREEN_HEIGHT - iconSize - 1) : SCREEN_HEIGHT;
|
int y = navBarVisible ? (SCREEN_HEIGHT - iconSize - 1) : SCREEN_HEIGHT;
|
||||||
|
|
||||||
#if defined(USE_EINK)
|
#if defined(USE_EINK)
|
||||||
|
// Only show bar briefly after switching frames
|
||||||
|
static uint32_t navBarLastShown = 0;
|
||||||
|
static bool cosmeticRefreshDone = false;
|
||||||
static bool navBarPrevVisible = false;
|
static bool navBarPrevVisible = false;
|
||||||
|
|
||||||
if (navBarVisible && !navBarPrevVisible) {
|
if (navBarVisible && !navBarPrevVisible) {
|
||||||
@@ -1239,7 +1338,6 @@ void UIRenderer::drawNavigationBar(OLEDDisplay *display, OLEDDisplayUiState *sta
|
|||||||
display->setColor(WHITE);
|
display->setColor(WHITE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Knock the corners off the square
|
// Knock the corners off the square
|
||||||
display->setColor(BLACK);
|
display->setColor(BLACK);
|
||||||
display->drawRect(rectX, y - 2, 1, 1);
|
display->drawRect(rectX, y - 2, 1, 1);
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ class UIRenderer
|
|||||||
|
|
||||||
// GPS status functions
|
// GPS status functions
|
||||||
static void drawGps(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gpsStatus);
|
static void drawGps(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gpsStatus);
|
||||||
static void drawGpsCoordinates(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gpsStatus);
|
static void drawGpsCoordinates(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gpsStatus,
|
||||||
|
const char *mode = "line1");
|
||||||
static void drawGpsAltitude(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gpsStatus);
|
static void drawGpsAltitude(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gpsStatus);
|
||||||
static void drawGpsPowerStatus(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gpsStatus);
|
static void drawGpsPowerStatus(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gpsStatus);
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#include "configuration.h"
|
||||||
|
#if HAS_SCREEN
|
||||||
#include "emotes.h"
|
#include "emotes.h"
|
||||||
|
|
||||||
namespace graphics
|
namespace graphics
|
||||||
@@ -16,6 +18,8 @@ const Emote emotes[] = {
|
|||||||
{"\U0001F642", Slightly_Smiling, Slightly_Smiling_width, Slightly_Smiling_height}, // 🙂 Slightly Smiling Face
|
{"\U0001F642", Slightly_Smiling, Slightly_Smiling_width, Slightly_Smiling_height}, // 🙂 Slightly Smiling Face
|
||||||
{"\U0001F609", Winking_Face, Winking_Face_width, Winking_Face_height}, // 😉 Winking Face
|
{"\U0001F609", Winking_Face, Winking_Face_width, Winking_Face_height}, // 😉 Winking Face
|
||||||
{"\U0001F601", Grinning_Smiling_Eyes, Grinning_Smiling_Eyes_width, Grinning_Smiling_Eyes_height}, // 😁 Grinning Smiling Eyes
|
{"\U0001F601", Grinning_Smiling_Eyes, Grinning_Smiling_Eyes_width, Grinning_Smiling_Eyes_height}, // 😁 Grinning Smiling Eyes
|
||||||
|
{"\U0001F60D", Heart_eyes, Heart_eyes_width, Heart_eyes_height}, // 😍 Heart Eyes
|
||||||
|
{"\U0001F970", heart_smile, heart_smile_width, heart_smile_height}, // 🥰 Smiling Face with Hearts
|
||||||
|
|
||||||
// --- Question/Alert ---
|
// --- Question/Alert ---
|
||||||
{"\u2753", question, question_width, question_height}, // ❓ Question Mark
|
{"\u2753", question, question_width, question_height}, // ❓ Question Mark
|
||||||
@@ -28,11 +32,15 @@ const Emote emotes[] = {
|
|||||||
{"\U0001F605", haha, haha_width, haha_height}, // 😅 Smiling with Sweat
|
{"\U0001F605", haha, haha_width, haha_height}, // 😅 Smiling with Sweat
|
||||||
{"\U0001F604", Grinning_SmilingEyes2, Grinning_SmilingEyes2_width,
|
{"\U0001F604", Grinning_SmilingEyes2, Grinning_SmilingEyes2_width,
|
||||||
Grinning_SmilingEyes2_height}, // 😄 Grinning Face with Smiling Eyes
|
Grinning_SmilingEyes2_height}, // 😄 Grinning Face with Smiling Eyes
|
||||||
|
{"\U0001F62D", Loudly_Crying_Face, Loudly_Crying_Face_width, Loudly_Crying_Face_height}, // 😭 Loudly Crying Face
|
||||||
|
|
||||||
// --- Gestures and People ---
|
// --- Gestures and People ---
|
||||||
{"\U0001F44B", wave_icon, wave_icon_width, wave_icon_height}, // 👋 Waving Hand
|
{"\U0001F44B", wave_icon, wave_icon_width, wave_icon_height}, // 👋 Waving Hand
|
||||||
{"\U0001F920", cowboy, cowboy_width, cowboy_height}, // 🤠 Cowboy Hat Face
|
{"\u270C\uFE0F", peace_sign, peace_sign_width, peace_sign_height}, // ✌️ Victory Hand
|
||||||
{"\U0001F3A7", deadmau5, deadmau5_width, deadmau5_height}, // 🎧 Headphones
|
{"\U0001F596", vulcan_salute, vulcan_salute_width, vulcan_salute_height}, // 🖖 Vulcan Salute
|
||||||
|
{"\U0001F64F", Praying, Praying_width, Praying_height}, // 🙏 Praying Hands
|
||||||
|
{"\U0001F920", cowboy, cowboy_width, cowboy_height}, // 🤠 Cowboy Hat Face
|
||||||
|
{"\U0001F3A7", deadmau5, deadmau5_width, deadmau5_height}, // 🎧 Headphones
|
||||||
|
|
||||||
// --- Weather ---
|
// --- Weather ---
|
||||||
{"\u2600", sun, sun_width, sun_height}, // ☀ Sun (without variation selector)
|
{"\u2600", sun, sun_width, sun_height}, // ☀ Sun (without variation selector)
|
||||||
@@ -43,8 +51,12 @@ const Emote emotes[] = {
|
|||||||
|
|
||||||
// --- Misc Faces ---
|
// --- Misc Faces ---
|
||||||
{"\U0001F608", devil, devil_width, devil_height}, // 😈 Smiling Face with Horns
|
{"\U0001F608", devil, devil_width, devil_height}, // 😈 Smiling Face with Horns
|
||||||
|
{"\U0001F921", clown, clown_width, clown_height}, // 🤡 Clown Face
|
||||||
|
{"\U0001F916", robo, robo_width, robo_height}, // 🤖 Robot Face
|
||||||
|
|
||||||
// --- Hearts (Multiple Unicode Aliases) ---
|
// --- Hearts (Multiple Unicode Aliases) ---
|
||||||
|
{"\u2665", heart, heart_width, heart_height}, // ♥ Black Heart Suit
|
||||||
|
{"\u2665\uFE0F", heart, heart_width, heart_height}, // ♥️ Black Heart Suit (emoji presentation)
|
||||||
{"\u2764\uFE0F", heart, heart_width, heart_height}, // ❤️ Red Heart
|
{"\u2764\uFE0F", heart, heart_width, heart_height}, // ❤️ Red Heart
|
||||||
{"\U0001F9E1", heart, heart_width, heart_height}, // 🧡 Orange Heart
|
{"\U0001F9E1", heart, heart_width, heart_height}, // 🧡 Orange Heart
|
||||||
{"\U00002763", heart, heart_width, heart_height}, // ❣ Heart Exclamation
|
{"\U00002763", heart, heart_width, heart_height}, // ❣ Heart Exclamation
|
||||||
@@ -55,223 +67,167 @@ const Emote emotes[] = {
|
|||||||
{"\U0001F498", heart, heart_width, heart_height}, // 💘 Heart with Arrow
|
{"\U0001F498", heart, heart_width, heart_height}, // 💘 Heart with Arrow
|
||||||
|
|
||||||
// --- Objects ---
|
// --- Objects ---
|
||||||
{"\U0001F4A9", poo, poo_width, poo_height}, // 💩 Pile of Poo
|
{"\U0001F4A9", poo, poo_width, poo_height}, // 💩 Pile of Poo
|
||||||
{"\U0001F514", bell_icon, bell_icon_width, bell_icon_height} // 🔔 Bell
|
{"\U0001F514", bell_icon, bell_icon_width, bell_icon_height}, // 🔔 Bell
|
||||||
|
{"\U0001F36A", cookie, cookie_width, cookie_height}, // 🍪 Cookie
|
||||||
|
{"\U0001F525", Fire, Fire_width, Fire_height}, // 🔥 Fire
|
||||||
|
{"\u2728", Sparkles, Sparkles_width, Sparkles_height}, // ✨ Sparkles
|
||||||
|
{"\U0001F573\uFE0F", hole, hole_width, hole_height}, // 🕳️ Hole
|
||||||
|
{"\U0001F3B3", bowling, bowling_width, bowling_height} // 🎳 Bowling
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
const int numEmotes = sizeof(emotes) / sizeof(emotes[0]);
|
const int numEmotes = sizeof(emotes) / sizeof(emotes[0]);
|
||||||
|
|
||||||
#ifndef EXCLUDE_EMOJI
|
#ifndef EXCLUDE_EMOJI
|
||||||
const unsigned char thumbup[] PROGMEM = {
|
const unsigned char thumbup[] PROGMEM = {0x00, 0x03, 0x80, 0x04, 0x80, 0x04, 0x40, 0x04, 0x20, 0x02, 0x18,
|
||||||
0x00, 0x1C, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x80, 0x09, 0x00, 0x00,
|
0x02, 0x06, 0x3F, 0x06, 0x40, 0x06, 0x70, 0x06, 0x40, 0x06, 0x70,
|
||||||
0xC0, 0x08, 0x00, 0x00, 0x40, 0x08, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00,
|
0x06, 0x40, 0x06, 0x30, 0x08, 0x20, 0xF0, 0x1F, 0x00, 0x00};
|
||||||
0x0C, 0xCE, 0x7F, 0x00, 0x04, 0x20, 0x80, 0x00, 0x02, 0x20, 0x80, 0x00, 0x02, 0x60, 0xC0, 0x00, 0x01, 0xF8, 0xFF, 0x01,
|
|
||||||
0x01, 0x08, 0x00, 0x01, 0x01, 0x08, 0x00, 0x01, 0x01, 0xF8, 0xFF, 0x00, 0x01, 0x10, 0x80, 0x00, 0x01, 0x18, 0x80, 0x00,
|
|
||||||
0x02, 0x30, 0xC0, 0x00, 0x06, 0xE0, 0x3F, 0x00, 0x0C, 0x20, 0x30, 0x00, 0x38, 0x20, 0x10, 0x00, 0xE0, 0xCF, 0x1F, 0x00,
|
|
||||||
};
|
|
||||||
|
|
||||||
const unsigned char thumbdown[] PROGMEM = {
|
const unsigned char thumbdown[] PROGMEM = {0xF0, 0x1F, 0x08, 0x20, 0x06, 0x30, 0x06, 0x40, 0x06, 0x70, 0x06,
|
||||||
0xE0, 0xCF, 0x1F, 0x00, 0x38, 0x20, 0x10, 0x00, 0x0C, 0x20, 0x30, 0x00, 0x06, 0xE0, 0x3F, 0x00, 0x02, 0x30, 0xC0, 0x00,
|
0x40, 0x06, 0x70, 0x06, 0x40, 0x06, 0x3F, 0x18, 0x02, 0x20, 0x02,
|
||||||
0x01, 0x18, 0x80, 0x00, 0x01, 0x10, 0x80, 0x00, 0x01, 0xF8, 0xFF, 0x00, 0x01, 0x08, 0x00, 0x01, 0x01, 0x08, 0x00, 0x01,
|
0x40, 0x04, 0x80, 0x04, 0x80, 0x04, 0x00, 0x03, 0x00, 0x00};
|
||||||
0x01, 0xF8, 0xFF, 0x01, 0x02, 0x60, 0xC0, 0x00, 0x02, 0x20, 0x80, 0x00, 0x04, 0x20, 0x80, 0x00, 0x0C, 0xCE, 0x7F, 0x00,
|
|
||||||
0x18, 0x02, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x40, 0x08, 0x00, 0x00, 0xC0, 0x08, 0x00, 0x00,
|
|
||||||
0x80, 0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00,
|
|
||||||
};
|
|
||||||
|
|
||||||
const unsigned char Smiling_Eyes[] PROGMEM = {
|
const unsigned char Smiling_Eyes[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x24, 0x24, 0x52,
|
||||||
0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1,
|
0x4A, 0x02, 0x40, 0x02, 0x40, 0x22, 0x44, 0x22, 0x44, 0xC2, 0x43,
|
||||||
0xf0, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xcf, 0xfc, 0xff, 0xff, 0xcf, 0xfc, 0xff, 0xff, 0xcf,
|
0x04, 0x20, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||||
0x7e, 0xf8, 0xc3, 0xdf, 0x3e, 0xf0, 0x81, 0xdf, 0xbf, 0xf7, 0xbd, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xff, 0x3f, 0xff,
|
|
||||||
0x6f, 0xff, 0xdf, 0xfe, 0x6f, 0xff, 0xdf, 0xfe, 0x9f, 0xff, 0x3f, 0xff, 0xfe, 0xff, 0xff, 0xdf, 0x7e, 0xff, 0xdf, 0xdf,
|
|
||||||
0x7c, 0xff, 0xdf, 0xcf, 0xfc, 0xfe, 0xef, 0xcf, 0xf8, 0xf9, 0xf7, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
|
|
||||||
0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x07, 0xc0};
|
|
||||||
|
|
||||||
const unsigned char Grinning[] PROGMEM = {
|
const unsigned char Grinning[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x44, 0x22, 0x42,
|
||||||
0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1,
|
0x42, 0x02, 0x40, 0x02, 0x40, 0xF2, 0x4F, 0x12, 0x48, 0x22, 0x44,
|
||||||
0xf0, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xcf, 0xfc, 0xf9, 0xf3, 0xcf, 0xfc, 0xf0, 0xe1, 0xcf,
|
0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||||
0xfe, 0xf0, 0xe1, 0xdf, 0xfe, 0xf0, 0xe1, 0xdf, 0xff, 0xf0, 0xe1, 0xff, 0xff, 0xf9, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
||||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x80, 0xff, 0xbe, 0xff, 0xbf, 0xdf, 0x7e, 0x00, 0xc0, 0xdf,
|
|
||||||
0x7c, 0x00, 0xc0, 0xcf, 0xfc, 0x00, 0xe0, 0xcf, 0xf8, 0x01, 0xf0, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
|
|
||||||
0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
|
|
||||||
|
|
||||||
const unsigned char Slightly_Smiling[] PROGMEM = {
|
const unsigned char Slightly_Smiling[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x44, 0x22, 0x42,
|
||||||
0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1,
|
0x42, 0x02, 0x40, 0x02, 0x40, 0x12, 0x48, 0x12, 0x48, 0x22, 0x44,
|
||||||
0xf0, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xcf, 0xfc, 0xf9, 0xf3, 0xcf, 0xfc, 0xf0, 0xe1, 0xcf,
|
0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||||
0xfe, 0xf0, 0xe1, 0xdf, 0xfe, 0xf0, 0xe1, 0xdf, 0xff, 0xf0, 0xe1, 0xff, 0xff, 0xf9, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
||||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xdf, 0x7e, 0xff, 0xdf, 0xdf,
|
|
||||||
0x7c, 0xff, 0xdf, 0xcf, 0xfc, 0xfe, 0xef, 0xcf, 0xf8, 0xf9, 0xf7, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
|
|
||||||
0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
|
|
||||||
|
|
||||||
const unsigned char Winking_Face[] PROGMEM = {
|
const unsigned char Winking_Face[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x44, 0x20, 0x42,
|
||||||
0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1,
|
0x46, 0x02, 0x40, 0x02, 0x40, 0x12, 0x48, 0x12, 0x48, 0x22, 0x44,
|
||||||
0xf0, 0xf0, 0xff, 0xc3, 0x78, 0xef, 0xc3, 0xc7, 0xb8, 0xdf, 0xbd, 0xcf, 0xfc, 0xf9, 0x7f, 0xcf, 0xfc, 0xf0, 0xff, 0xcf,
|
0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||||
0xfe, 0xf0, 0xc3, 0xdf, 0xfe, 0xf0, 0x81, 0xdf, 0xff, 0xf0, 0xbf, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
||||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xdf, 0x7e, 0xff, 0xdf, 0xdf,
|
|
||||||
0x7c, 0xff, 0xdf, 0xcf, 0xfc, 0xfe, 0xef, 0xcf, 0xf8, 0xf9, 0xf7, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
|
|
||||||
0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x07, 0xc0};
|
|
||||||
|
|
||||||
const unsigned char Grinning_Smiling_Eyes[] PROGMEM = {
|
const unsigned char Grinning_Smiling_Eyes[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x24, 0x24, 0x52,
|
||||||
0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1,
|
0x4A, 0x02, 0x40, 0xFA, 0x5F, 0x0A, 0x50, 0x0A, 0x50, 0x12, 0x48,
|
||||||
0xf0, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xcf, 0xfc, 0xf8, 0xe3, 0xcf, 0x7c, 0xf7, 0xdd, 0xcf,
|
0x24, 0x24, 0xC4, 0x23, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||||
0xbe, 0xef, 0xbe, 0xdf, 0xbe, 0xef, 0xbe, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
||||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, 0xff, 0x5e, 0x55, 0x55, 0xdf, 0x5e, 0x55, 0x55, 0xdf,
|
|
||||||
0x3c, 0x00, 0x80, 0xcf, 0x7c, 0x55, 0xd5, 0xcf, 0xf8, 0x54, 0xe5, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
|
|
||||||
0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
|
|
||||||
|
|
||||||
const unsigned char question[] PROGMEM = {
|
const unsigned char heart_smile[] PROGMEM = {0x00, 0x00, 0x6C, 0x07, 0x7C, 0x18, 0x7C, 0x20, 0x38, 0x24, 0x52,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0xE0, 0xFF, 0x07, 0x00,
|
0x0A, 0x02, 0xD8, 0x02, 0xF8, 0x22, 0xFC, 0x20, 0x74, 0xDB, 0x23,
|
||||||
0xE0, 0xC3, 0x0F, 0x00, 0xF0, 0x81, 0x0F, 0x00, 0xF0, 0x01, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x80, 0x0F, 0x00,
|
0x1F, 0x00, 0x1F, 0x20, 0x0E, 0x18, 0xE4, 0x07, 0x00, 0x00};
|
||||||
0x00, 0xC0, 0x0F, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x7C, 0x00, 0x00,
|
|
||||||
0x00, 0x3C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x3E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
};
|
|
||||||
|
|
||||||
const unsigned char bang[] PROGMEM = {
|
const unsigned char Heart_eyes[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x54, 0x2A, 0xFA,
|
||||||
0xFF, 0x0F, 0xFC, 0x3F, 0xFF, 0x0F, 0xFC, 0x3F, 0xFF, 0x0F, 0xFC, 0x3F, 0xFF, 0x07, 0xF8, 0x3F, 0xFF, 0x07, 0xF8, 0x3F,
|
0x5F, 0x72, 0x4E, 0x22, 0x44, 0x02, 0x40, 0x12, 0x48, 0x12, 0x48,
|
||||||
0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F,
|
0x24, 0x24, 0xC4, 0x23, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||||
0xFE, 0x03, 0xF0, 0x1F, 0xFE, 0x03, 0xF0, 0x1F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F,
|
|
||||||
0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x01, 0xE0, 0x0F, 0xFC, 0x01, 0xE0, 0x0F,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0xC0, 0x03, 0xFC, 0x03, 0xF0, 0x0F, 0xFE, 0x03, 0xF0, 0x1F,
|
|
||||||
0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFC, 0x03, 0xF0, 0x0F, 0xF8, 0x01, 0xE0, 0x07,
|
|
||||||
};
|
|
||||||
|
|
||||||
const unsigned char haha[] PROGMEM = {
|
const unsigned char question[] PROGMEM = {0xE0, 0x07, 0x10, 0x08, 0x08, 0x10, 0x88, 0x11, 0x48, 0x12, 0x48,
|
||||||
0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0x7f, 0xc0, 0xe0, 0xf9, 0xf3, 0xc0,
|
0x12, 0x48, 0x12, 0x30, 0x11, 0x80, 0x08, 0x40, 0x04, 0x40, 0x02,
|
||||||
0xf0, 0xfe, 0xef, 0xc1, 0x38, 0xff, 0x9f, 0xc3, 0xd8, 0xff, 0x7f, 0xc3, 0xfc, 0xf8, 0xe3, 0xc7, 0x7c, 0xf7, 0xdd, 0xcf,
|
0xC0, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x40, 0x02, 0x80, 0x01};
|
||||||
0xbe, 0xef, 0xbe, 0xcf, 0xfe, 0xff, 0xff, 0xcf, 0xef, 0xff, 0xff, 0xde, 0xe7, 0xff, 0xff, 0xdc, 0xeb, 0xff, 0xff, 0xda,
|
|
||||||
0xed, 0xff, 0xff, 0xd6, 0xee, 0xff, 0xff, 0xce, 0x36, 0x00, 0x80, 0xcd, 0xb8, 0xff, 0xbf, 0xc3, 0x7e, 0x00, 0xc0, 0xdf,
|
|
||||||
0x7c, 0x00, 0xc0, 0xcf, 0xfc, 0x00, 0xe0, 0xcf, 0xf8, 0x01, 0xf0, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
|
|
||||||
0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
|
|
||||||
|
|
||||||
const unsigned char ROFL[] PROGMEM = {
|
const unsigned char bang[] PROGMEM = {0x30, 0x0C, 0x48, 0x12, 0x48, 0x12, 0x48, 0x12, 0x48, 0x12, 0x48,
|
||||||
0x00, 0x00, 0x00, 0xc0, 0x00, 0xfc, 0x07, 0xc0, 0x00, 0xff, 0x1f, 0xc0, 0x80, 0xff, 0x7f, 0xc0, 0xc0, 0xff, 0xff, 0xc0,
|
0x12, 0x48, 0x12, 0x48, 0x12, 0x48, 0x12, 0x48, 0x12, 0x30, 0x0C,
|
||||||
0xe0, 0x9f, 0xff, 0xc1, 0xf0, 0x9f, 0xff, 0xc0, 0xf8, 0x9f, 0x7f, 0xcb, 0xf8, 0x9f, 0xbf, 0xcb, 0xfc, 0x9f, 0xdf, 0xdb,
|
0x00, 0x00, 0x30, 0x0C, 0x48, 0x12, 0x30, 0x0C, 0x00, 0x00};
|
||||||
0xfc, 0x1f, 0x08, 0xdc, 0xfe, 0x1f, 0xf8, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0x1e, 0xf0, 0x7f, 0xfe, 0x1e, 0xf0, 0xbf, 0xfe,
|
|
||||||
0xfe, 0xf3, 0xdf, 0xfe, 0xfe, 0xf3, 0x6f, 0xfe, 0xfe, 0xf3, 0x37, 0xfe, 0xfe, 0xeb, 0x1b, 0xfe, 0xfc, 0xef, 0x0d, 0xde,
|
|
||||||
0xfc, 0xe7, 0x06, 0xcf, 0xf8, 0x6b, 0x83, 0xcf, 0xf8, 0x0d, 0xc0, 0xc7, 0xf0, 0xed, 0xff, 0xc7, 0xe0, 0xee, 0xff, 0xc3,
|
|
||||||
0xc0, 0xee, 0xff, 0xc1, 0x80, 0xee, 0xff, 0xc0, 0x00, 0xe6, 0x3f, 0xc0, 0x00, 0xf0, 0x0f, 0xc0, 0x00, 0x00, 0x00, 0xc0};
|
|
||||||
|
|
||||||
const unsigned char Smiling_Closed_Eyes[] PROGMEM = {
|
const unsigned char haha[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x24, 0x24, 0x52,
|
||||||
0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1,
|
0x4A, 0x0A, 0x50, 0x0E, 0x70, 0xF2, 0x4F, 0x12, 0x48, 0x32, 0x44,
|
||||||
0xf0, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xcf, 0x7c, 0xfe, 0xcf, 0xcf, 0xfc, 0xfc, 0xe7, 0xcf,
|
0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||||
0xfe, 0xf9, 0xf3, 0xdf, 0xfe, 0xf3, 0xf9, 0xdf, 0xff, 0xf9, 0xf3, 0xff, 0xff, 0xfc, 0xe7, 0xff, 0x7f, 0xfe, 0xcf, 0xff,
|
|
||||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x80, 0xff, 0xbe, 0xff, 0xbf, 0xdf, 0x7e, 0x00, 0xc0, 0xdf,
|
|
||||||
0x7c, 0x00, 0xc0, 0xcf, 0xfc, 0x00, 0xe0, 0xcf, 0xf8, 0x01, 0xf0, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
|
|
||||||
0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
|
|
||||||
|
|
||||||
const unsigned char Grinning_SmilingEyes2[] PROGMEM = {
|
const unsigned char ROFL[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x84, 0x21, 0x84, 0x20, 0x02,
|
||||||
0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0x7f, 0xc0, 0xe0, 0xff, 0xff, 0xc0,
|
0x4C, 0x02, 0x4A, 0x1A, 0x49, 0x8A, 0x48, 0x42, 0x48, 0x22, 0x44,
|
||||||
0xf0, 0xff, 0xff, 0xc1, 0xf8, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc3, 0xfc, 0xf8, 0xe3, 0xc7, 0x7c, 0xf7, 0xdd, 0xc7,
|
0xE4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||||
0xbe, 0xef, 0xbe, 0xcf, 0xfe, 0xff, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xdf,
|
|
||||||
0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xdf, 0x3f, 0x00, 0x80, 0xdf, 0xbe, 0xff, 0xbf, 0xcf, 0x7e, 0x00, 0xc0, 0xcf,
|
|
||||||
0x7c, 0x00, 0xc0, 0xc7, 0xfc, 0x00, 0xe0, 0xc7, 0xf8, 0x01, 0xf0, 0xc3, 0xf8, 0x03, 0xf8, 0xc3, 0xf0, 0xff, 0xff, 0xc1,
|
|
||||||
0xe0, 0xff, 0xff, 0xc0, 0xc0, 0xff, 0x7f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
|
|
||||||
|
|
||||||
const unsigned char wave_icon[] PROGMEM = {
|
const unsigned char Smiling_Closed_Eyes[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x24, 0x24, 0x42,
|
||||||
0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x7f, 0xc0, 0x00, 0x00, 0xc0, 0xc1, 0x00, 0x00, 0x00, 0xc7,
|
0x42, 0x22, 0x44, 0x02, 0x40, 0xF2, 0x4F, 0x12, 0x48, 0x22, 0x44,
|
||||||
0x00, 0x00, 0x1e, 0xcc, 0x00, 0x00, 0x30, 0xc8, 0x00, 0x00, 0x60, 0xd8, 0x00, 0x08, 0xc0, 0xd0, 0x00, 0x1a, 0x81, 0xd1,
|
0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||||
0x00, 0x36, 0x03, 0xd3, 0x80, 0x6d, 0x06, 0xd2, 0x00, 0xdb, 0x0c, 0xc2, 0x80, 0xb6, 0x1d, 0xc0, 0x80, 0x6d, 0x1f, 0xc0,
|
|
||||||
0x00, 0xdb, 0x3f, 0xc0, 0x00, 0xf6, 0x7f, 0xc0, 0x00, 0xfc, 0x7f, 0xc0, 0x08, 0xf8, 0x7f, 0xc0, 0x48, 0xf0, 0x7f, 0xc0,
|
|
||||||
0x48, 0xe0, 0x7f, 0xc0, 0xc8, 0xc0, 0x3f, 0xc0, 0x98, 0x81, 0x1f, 0xc0, 0x10, 0x03, 0x00, 0xc0, 0x30, 0x0e, 0x00, 0xc0,
|
|
||||||
0x20, 0x38, 0x00, 0xc0, 0xe0, 0x00, 0x00, 0xc0, 0x80, 0x07, 0x00, 0xc0, 0x00, 0x1e, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0};
|
|
||||||
|
|
||||||
const unsigned char cowboy[] PROGMEM = {
|
const unsigned char Grinning_SmilingEyes2[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x24, 0x24, 0x52,
|
||||||
0x00, 0x0c, 0x0c, 0xc0, 0x00, 0x02, 0x10, 0xc0, 0x00, 0x01, 0x20, 0xc0, 0xbc, 0x00, 0x40, 0xcf, 0xc2, 0x01, 0xe0, 0xd0,
|
0x4A, 0x02, 0x40, 0x02, 0x40, 0xF2, 0x4F, 0x12, 0x48, 0x22, 0x44,
|
||||||
0x01, 0x01, 0x20, 0xe0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0,
|
0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||||
0xc1, 0x3f, 0xff, 0xe0, 0xe1, 0xff, 0xff, 0xe1, 0xf2, 0xf3, 0xf3, 0xd3, 0xf4, 0xf1, 0xe3, 0xcb, 0xfc, 0xf1, 0xe3, 0xc7,
|
|
||||||
0xf8, 0xf1, 0xe3, 0xc7, 0xf8, 0xf1, 0xe3, 0xc7, 0xf8, 0xfb, 0xf7, 0xc7, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xc7,
|
|
||||||
0x70, 0xf8, 0x8f, 0xc3, 0x70, 0x03, 0xb0, 0xc3, 0x70, 0xfe, 0xbf, 0xc3, 0x60, 0x00, 0x80, 0xc1, 0xc0, 0x00, 0xc0, 0xc0,
|
|
||||||
0x80, 0x01, 0x60, 0xc0, 0x00, 0x07, 0x38, 0xc0, 0x00, 0xfe, 0x1f, 0xc0, 0x00, 0xf0, 0x03, 0xc0, 0x00, 0x00, 0x00, 0xc0};
|
|
||||||
|
|
||||||
const unsigned char deadmau5[] PROGMEM = {
|
const unsigned char Loudly_Crying_Face[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x34, 0x2C, 0x4A,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x07, 0x00,
|
0x52, 0x12, 0x48, 0x12, 0x48, 0x92, 0x49, 0x52, 0x4A, 0x52, 0x4A,
|
||||||
0x00, 0xFC, 0x03, 0x00, 0x00, 0xFF, 0x3F, 0x00, 0x80, 0xFF, 0x0F, 0x00, 0xC0, 0xFF, 0xFF, 0x00, 0xE0, 0xFF, 0x3F, 0x00,
|
0x54, 0x2A, 0x94, 0x29, 0x18, 0x18, 0xF0, 0x0F, 0x00, 0x00};
|
||||||
0xE0, 0xFF, 0xFF, 0x01, 0xF0, 0xFF, 0x7F, 0x00, 0xF0, 0xFF, 0xFF, 0x03, 0xF8, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x07,
|
|
||||||
0xFC, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x0F, 0xFC, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x0F, 0xFE, 0xFF, 0xFF, 0x00,
|
|
||||||
0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xE0, 0xFF, 0x3F, 0xFC,
|
|
||||||
0x0F, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0x1F, 0xF8, 0x0F, 0xFC, 0x3F, 0x00, 0x80, 0xFF, 0x0F, 0xF8, 0x1F, 0xFC, 0x1F, 0x00,
|
|
||||||
0x00, 0xFF, 0x0F, 0xFC, 0x3F, 0xFC, 0x0F, 0x00, 0x00, 0xF8, 0x1F, 0xFF, 0xFF, 0xFE, 0x01, 0x00, 0x00, 0x00, 0xFC, 0xFF,
|
|
||||||
0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00,
|
|
||||||
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0xC0, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x80, 0x07, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
};
|
|
||||||
|
|
||||||
const unsigned char sun[] PROGMEM = {
|
const unsigned char wave_icon[] PROGMEM = {0x00, 0x00, 0xC0, 0x18, 0x30, 0x21, 0x48, 0x5A, 0x94, 0x64, 0x24,
|
||||||
0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x30, 0xC0, 0x00, 0x03,
|
0x25, 0x4A, 0x24, 0x12, 0x44, 0x22, 0x44, 0x04, 0x40, 0x08, 0x40,
|
||||||
0x70, 0x00, 0x80, 0x03, 0xF0, 0x00, 0xC0, 0x03, 0xF0, 0xF8, 0xC7, 0x03, 0xE0, 0xFC, 0xCF, 0x01, 0x00, 0xFE, 0x1F, 0x00,
|
0x12, 0x40, 0x22, 0x20, 0xC4, 0x10, 0x18, 0x0F, 0x00, 0x00};
|
||||||
0x00, 0xFF, 0x3F, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x8E, 0xFF, 0x7F, 0x1C, 0x9F, 0xFF, 0x7F, 0x3E,
|
|
||||||
0x9F, 0xFF, 0x7F, 0x3E, 0x8E, 0xFF, 0x7F, 0x1C, 0x80, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x00, 0xFF, 0x3F, 0x00,
|
|
||||||
0x00, 0xFE, 0x1F, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0xC0, 0xF9, 0xE7, 0x00, 0xE0, 0x01, 0xE0, 0x01, 0xF0, 0x01, 0xE0, 0x03,
|
|
||||||
0xF0, 0xC0, 0xC0, 0x03, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00,
|
|
||||||
};
|
|
||||||
|
|
||||||
const unsigned char rain[] PROGMEM = {
|
const unsigned char cowboy[] PROGMEM = {0x70, 0x0E, 0x8F, 0xF1, 0x11, 0x88, 0x21, 0x84, 0xC2, 0x43, 0x1E,
|
||||||
0xC0, 0x0F, 0xC0, 0x00, 0x40, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x03, 0x38, 0x00,
|
0x78, 0xE2, 0x47, 0x42, 0x42, 0x12, 0x48, 0x12, 0x48, 0x22, 0x44,
|
||||||
0x00, 0x0E, 0x0C, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x20,
|
0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||||
0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, 0x30, 0x02, 0x00,
|
|
||||||
0x00, 0x10, 0x06, 0x00, 0x00, 0x08, 0xFC, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x01, 0x80, 0x00, 0x01, 0x00,
|
|
||||||
0xC0, 0xC0, 0x81, 0x03, 0xA0, 0x60, 0xC1, 0x03, 0x90, 0x20, 0x41, 0x01, 0xF0, 0xE0, 0xC0, 0x01, 0x60, 0x4C,
|
|
||||||
0x98, 0x00, 0x00, 0x0E, 0x1C, 0x00, 0x00, 0x0B, 0x12, 0x00, 0x00, 0x09, 0x1A, 0x00, 0x00, 0x06, 0x0E, 0x00,
|
|
||||||
};
|
|
||||||
|
|
||||||
const unsigned char cloud[] PROGMEM = {
|
const unsigned char deadmau5[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0xE4, 0x27, 0x12, 0x48, 0x0A,
|
||||||
0x00, 0x80, 0x07, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x70, 0x30, 0x00, 0x00, 0x10, 0x60, 0x00, 0x80, 0x1F, 0x40, 0x00,
|
0x50, 0x0E, 0x70, 0x11, 0x88, 0x19, 0x98, 0x19, 0x98, 0x19, 0x98,
|
||||||
0xC0, 0x0F, 0xC0, 0x00, 0xC0, 0x00, 0x80, 0x00, 0x60, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x01,
|
0x19, 0x98, 0x19, 0x98, 0x11, 0x88, 0x0E, 0x70, 0x00, 0x00};
|
||||||
0x20, 0x00, 0x00, 0x07, 0x38, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x08, 0x06, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x10,
|
|
||||||
0x02, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20,
|
|
||||||
0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0x10,
|
|
||||||
0x02, 0x00, 0x00, 0x10, 0x06, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x0C, 0xFC, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x03,
|
|
||||||
};
|
|
||||||
|
|
||||||
const unsigned char fog[] PROGMEM = {
|
const unsigned char sun[] PROGMEM = {0x00, 0x00, 0x80, 0x01, 0xEC, 0x37, 0xFC, 0x3F, 0xF8, 0x1F, 0xFC,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x3F, 0xFE, 0x7F, 0xFC, 0x3F, 0xFC, 0x3F, 0xFE, 0x7F, 0xFC, 0x3F,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x3C, 0x00, 0xFE, 0x01, 0xFF, 0x00, 0x87, 0xC7, 0xC3, 0x01, 0x03, 0xFE, 0x80, 0x01,
|
0xF8, 0x1F, 0xFC, 0x3F, 0xEC, 0x37, 0x80, 0x01, 0x00, 0x00};
|
||||||
0x00, 0x38, 0x00, 0x00, 0xFC, 0x00, 0x7E, 0x00, 0xFF, 0x83, 0xFF, 0x01, 0x03, 0xFF, 0x81, 0x01, 0x00, 0x7C, 0x00, 0x00,
|
|
||||||
0xF8, 0x00, 0x3E, 0x00, 0xFE, 0x01, 0xFF, 0x00, 0x87, 0xC7, 0xC3, 0x01, 0x03, 0xFE, 0x80, 0x01, 0x00, 0x38, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
};
|
|
||||||
|
|
||||||
const unsigned char devil[] PROGMEM = {
|
const unsigned char rain[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x38, 0x1F, 0xFC, 0x3F, 0xFE,
|
||||||
0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf0, 0x0f, 0xfc, 0x0f, 0xfc,
|
0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFC, 0x3F, 0x00, 0x00, 0x48, 0x12,
|
||||||
0x3f, 0xff, 0x3f, 0xff, 0xfe, 0xff, 0xff, 0xdf, 0xfe, 0xff, 0xff, 0xdf, 0xfe, 0xff, 0xff, 0xdf, 0xfc, 0xff, 0xff, 0xcf,
|
0x48, 0x12, 0x24, 0x09, 0x24, 0x09, 0x00, 0x00, 0x00, 0x00};
|
||||||
0xfc, 0xff, 0xff, 0xcf, 0xf8, 0xff, 0xff, 0xc7, 0xf0, 0xff, 0xff, 0xc3, 0xf0, 0xff, 0xff, 0xc3, 0xf0, 0xf1, 0xe3, 0xc3,
|
|
||||||
0xf0, 0xe7, 0xf9, 0xc3, 0xf0, 0xe7, 0xf9, 0xc3, 0xf0, 0xe3, 0xf1, 0xc3, 0xf0, 0xe3, 0xf1, 0xc3, 0xf0, 0xe7, 0xf9, 0xc3,
|
|
||||||
0xf0, 0xff, 0xff, 0xc3, 0xe0, 0xfd, 0xef, 0xc1, 0xe0, 0xf3, 0xf3, 0xc1, 0xc0, 0x07, 0xf8, 0xc0, 0x80, 0x1f, 0x7e, 0xc0,
|
|
||||||
0x00, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0};
|
|
||||||
|
|
||||||
const unsigned char heart[] PROGMEM = {
|
const unsigned char cloud[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x38, 0x1F, 0xFC,
|
||||||
0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0xF0, 0x00, 0xF8, 0x0F, 0xFC, 0x07, 0xFC, 0x1F, 0x06, 0x0E, 0xFE, 0x3F, 0x03, 0x18,
|
0x3F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFC, 0x3F, 0xF8, 0x1F,
|
||||||
0xFE, 0xFF, 0x7F, 0x10, 0xFF, 0xFF, 0xFF, 0x31, 0xFF, 0xFF, 0xFF, 0x33, 0xFF, 0xFF, 0xFF, 0x37, 0xFF, 0xFF, 0xFF, 0x37,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||||
0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFE, 0xFF, 0xFF, 0x1F, 0xFE, 0xFF, 0xFF, 0x1F,
|
|
||||||
0xFC, 0xFF, 0xFF, 0x0F, 0xFC, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x03, 0xF0, 0xFF, 0xFF, 0x03,
|
|
||||||
0xE0, 0xFF, 0xFF, 0x01, 0xC0, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x00, 0xFF, 0x3F, 0x00, 0x00, 0xFE, 0x1F, 0x00,
|
|
||||||
0x00, 0xFC, 0x0F, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00,
|
|
||||||
};
|
|
||||||
|
|
||||||
const unsigned char poo[] PROGMEM = {
|
const unsigned char fog[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x88, 0x88, 0x54, 0x55, 0x22, 0x22, 0x00,
|
||||||
0x00, 0x1c, 0x00, 0xc0, 0x00, 0x7c, 0x00, 0xc0, 0x00, 0xfc, 0x00, 0xc0, 0x00, 0x7c, 0x03, 0xc0, 0x00, 0xbe, 0x03, 0xc0,
|
0x00, 0x44, 0x44, 0xAA, 0x2A, 0x11, 0x11, 0x00, 0x00, 0x88, 0x88,
|
||||||
0x00, 0xdf, 0x0f, 0xc0, 0x80, 0xcf, 0x0f, 0xc0, 0xc0, 0xf1, 0x0f, 0xc0, 0x60, 0xfc, 0x0f, 0xc0, 0x30, 0xff, 0x07, 0xc0,
|
0x54, 0x55, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||||
0x90, 0xff, 0x3b, 0xc0, 0xc0, 0xff, 0x7d, 0xc0, 0xf8, 0xff, 0xfc, 0xc0, 0xf8, 0x3f, 0xf0, 0xc0, 0x78, 0x88, 0xc0, 0xc0,
|
|
||||||
0x20, 0xe3, 0x18, 0xc0, 0x98, 0xe7, 0xbc, 0xc1, 0x9c, 0x64, 0xa4, 0xc3, 0x9e, 0x64, 0xa4, 0xc7, 0xbe, 0xe4, 0xa4, 0xc7,
|
|
||||||
0xbc, 0x27, 0xbc, 0xc7, 0x38, 0x03, 0xd9, 0xc3, 0x00, 0xf0, 0x63, 0xc0, 0xf8, 0xfc, 0x3f, 0xcf, 0xfc, 0xff, 0x87, 0xdf,
|
|
||||||
0xfe, 0xff, 0xe0, 0xdf, 0xfc, 0x1f, 0xfe, 0xdf, 0xf8, 0x07, 0xf8, 0xcf, 0xf0, 0x03, 0xe0, 0xc7, 0x00, 0x00, 0x00, 0xc0};
|
|
||||||
|
|
||||||
const unsigned char bell_icon[] PROGMEM = {
|
const unsigned char devil[] PROGMEM = {0x06, 0x60, 0xCA, 0x53, 0x32, 0x4C, 0x22, 0x44, 0x44, 0x22, 0x3A,
|
||||||
0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b11110000,
|
0x5C, 0x32, 0x4C, 0x52, 0x4A, 0x72, 0x4E, 0x02, 0x40, 0x22, 0x44,
|
||||||
0b00000011, 0b00000000, 0b00000000, 0b11111100, 0b00001111, 0b00000000, 0b00000000, 0b00001111, 0b00111100, 0b00000000,
|
0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||||
0b00000000, 0b00000011, 0b00110000, 0b00000000, 0b10000000, 0b00000001, 0b01100000, 0b00000000, 0b11000000, 0b00000000,
|
|
||||||
0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000,
|
const unsigned char heart[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x3C, 0x3C, 0x7E, 0x7E, 0xFE, 0x7F, 0xFE,
|
||||||
0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000,
|
0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFC, 0x3F, 0xF8, 0x1F, 0xF8, 0x1F,
|
||||||
0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000,
|
0xF0, 0x0F, 0xE0, 0x07, 0xC0, 0x03, 0x80, 0x01, 0x00, 0x00};
|
||||||
0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b01000000, 0b00000000, 0b10000000, 0b00000000, 0b01100000, 0b00000000,
|
|
||||||
0b10000000, 0b00000001, 0b01110000, 0b00000000, 0b10000000, 0b00000011, 0b00110000, 0b00000000, 0b00000000, 0b00000011,
|
const unsigned char poo[] PROGMEM = {0x00, 0x00, 0x80, 0x01, 0x40, 0x02, 0x20, 0x04, 0x10, 0x04, 0xF0,
|
||||||
0b00011000, 0b00000000, 0b00000000, 0b00000110, 0b11110000, 0b11111111, 0b11111111, 0b00000011, 0b00000000, 0b00001100,
|
0x08, 0x10, 0x10, 0x48, 0x12, 0x08, 0x18, 0xE8, 0x21, 0x1C, 0x40,
|
||||||
0b00001100, 0b00000000, 0b00000000, 0b00011000, 0b00000110, 0b00000000, 0b00000000, 0b11111000, 0b00000111, 0b00000000,
|
0x42, 0x42, 0x82, 0x41, 0x02, 0x30, 0xFC, 0x0F, 0x00, 0x00};
|
||||||
0b00000000, 0b11100000, 0b00000001, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
|
|
||||||
0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000};
|
const unsigned char bell_icon[] PROGMEM = {0x00, 0x00, 0x80, 0x01, 0x80, 0x01, 0xE0, 0x07, 0xF0, 0x0F, 0xF0,
|
||||||
|
0x0F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFC, 0x3F,
|
||||||
|
0xFC, 0x3F, 0xFE, 0x7F, 0xFE, 0x7F, 0x80, 0x01, 0x00, 0x00};
|
||||||
|
|
||||||
|
const unsigned char cookie[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x34, 0x22, 0x32,
|
||||||
|
0x40, 0x02, 0x58, 0x82, 0x5B, 0x92, 0x43, 0x82, 0x43, 0x02, 0x40,
|
||||||
|
0x64, 0x28, 0x64, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||||
|
|
||||||
|
const unsigned char Fire[] PROGMEM = {0x30, 0x00, 0xF0, 0x00, 0xF8, 0x03, 0xF8, 0x07, 0xFC, 0x1F, 0xFC,
|
||||||
|
0x1F, 0xFE, 0x3E, 0x7E, 0x3E, 0x3E, 0x7C, 0x1E, 0x78, 0x1E, 0x70,
|
||||||
|
0x1C, 0x70, 0x1C, 0x70, 0x38, 0x38, 0x30, 0x38, 0x60, 0x0C};
|
||||||
|
|
||||||
|
const unsigned char peace_sign[] PROGMEM = {0xC0, 0x30, 0x40, 0x29, 0x40, 0x25, 0x40, 0x15, 0x40, 0x12, 0x38,
|
||||||
|
0x0A, 0x54, 0x68, 0x54, 0x58, 0x54, 0x44, 0x3C, 0x22, 0x04, 0x22,
|
||||||
|
0x04, 0x12, 0x08, 0x10, 0x10, 0x08, 0xE0, 0x07, 0x00, 0x00};
|
||||||
|
|
||||||
|
const unsigned char Praying[] PROGMEM = {0x00, 0x00, 0x40, 0x02, 0xA0, 0x05, 0x90, 0x09, 0x90, 0x09, 0x90,
|
||||||
|
0x09, 0x98, 0x19, 0x94, 0x29, 0xA4, 0x25, 0xA4, 0x25, 0x84, 0x21,
|
||||||
|
0x84, 0x21, 0x86, 0x61, 0x4E, 0x72, 0x7F, 0x7E, 0x3F, 0xFC};
|
||||||
|
|
||||||
|
const unsigned char Sparkles[] PROGMEM = {0x00, 0x00, 0x10, 0x00, 0x38, 0x04, 0x10, 0x04, 0x00, 0x0E, 0x00,
|
||||||
|
0x1F, 0x80, 0x3F, 0xE0, 0xFF, 0x80, 0x3F, 0x10, 0x1F, 0x10, 0x0E,
|
||||||
|
0x38, 0x04, 0xFE, 0x04, 0x38, 0x00, 0x10, 0x00, 0x10, 0x00};
|
||||||
|
|
||||||
|
const unsigned char clown[] PROGMEM = {0x00, 0x00, 0xEE, 0x77, 0x1A, 0x58, 0x06, 0x60, 0x24, 0x24, 0x72,
|
||||||
|
0x4E, 0x22, 0x44, 0x82, 0x41, 0x82, 0x41, 0x1A, 0x58, 0xF2, 0x4F,
|
||||||
|
0x14, 0x28, 0xE4, 0x27, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
|
||||||
|
|
||||||
|
const unsigned char robo[] PROGMEM = {0x80, 0x01, 0xC0, 0x03, 0x80, 0x01, 0xFC, 0x3F, 0x04, 0x20, 0x74,
|
||||||
|
0x2E, 0x52, 0x4A, 0x72, 0x4E, 0x02, 0x40, 0x02, 0x40, 0xA2, 0x4A,
|
||||||
|
0x52, 0x45, 0x04, 0x20, 0x04, 0x20, 0xFC, 0x3F, 0x00, 0x00};
|
||||||
|
|
||||||
|
const unsigned char hole[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x0F, 0x3C, 0x3C,
|
||||||
|
0x06, 0x60, 0x0C, 0x30, 0xF0, 0x0F, 0x00, 0x00, 0x00, 0x00};
|
||||||
|
|
||||||
|
const unsigned char bowling[] PROGMEM = {0x00, 0x38, 0x00, 0x44, 0x00, 0x44, 0x00, 0x44, 0x00, 0x28, 0x00,
|
||||||
|
0x38, 0x00, 0x28, 0x78, 0x44, 0x84, 0x82, 0x22, 0x83, 0x52, 0x83,
|
||||||
|
0x02, 0x83, 0x02, 0x45, 0x84, 0x44, 0x78, 0x38, 0x00, 0x00};
|
||||||
|
|
||||||
|
const unsigned char vulcan_salute[] PROGMEM = {0x08, 0x02, 0x16, 0x0D, 0x15, 0x15, 0x15, 0x15, 0xA9, 0x12, 0x4A,
|
||||||
|
0x0A, 0x02, 0x38, 0x04, 0x48, 0x04, 0x44, 0x04, 0x22, 0x04, 0x22,
|
||||||
|
0x04, 0x12, 0x08, 0x10, 0x10, 0x08, 0xE0, 0x07, 0x00, 0x00};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // namespace graphics
|
} // namespace graphics
|
||||||
|
#endif
|
||||||
@@ -17,98 +17,150 @@ extern const int numEmotes;
|
|||||||
|
|
||||||
#ifndef EXCLUDE_EMOJI
|
#ifndef EXCLUDE_EMOJI
|
||||||
// === Emote Bitmaps ===
|
// === Emote Bitmaps ===
|
||||||
#define thumbs_height 25
|
#define thumbs_height 16
|
||||||
#define thumbs_width 25
|
#define thumbs_width 16
|
||||||
extern const unsigned char thumbup[] PROGMEM;
|
extern const unsigned char thumbup[] PROGMEM;
|
||||||
extern const unsigned char thumbdown[] PROGMEM;
|
extern const unsigned char thumbdown[] PROGMEM;
|
||||||
|
|
||||||
#define Smiling_Eyes_height 30
|
#define Smiling_Eyes_height 16
|
||||||
#define Smiling_Eyes_width 30
|
#define Smiling_Eyes_width 16
|
||||||
extern const unsigned char Smiling_Eyes[] PROGMEM;
|
extern const unsigned char Smiling_Eyes[] PROGMEM;
|
||||||
|
|
||||||
#define Grinning_height 30
|
#define Grinning_height 16
|
||||||
#define Grinning_width 30
|
#define Grinning_width 16
|
||||||
extern const unsigned char Grinning[] PROGMEM;
|
extern const unsigned char Grinning[] PROGMEM;
|
||||||
|
|
||||||
#define Slightly_Smiling_height 30
|
#define Slightly_Smiling_height 16
|
||||||
#define Slightly_Smiling_width 30
|
#define Slightly_Smiling_width 16
|
||||||
extern const unsigned char Slightly_Smiling[] PROGMEM;
|
extern const unsigned char Slightly_Smiling[] PROGMEM;
|
||||||
|
|
||||||
#define Winking_Face_height 30
|
#define Winking_Face_height 16
|
||||||
#define Winking_Face_width 30
|
#define Winking_Face_width 16
|
||||||
extern const unsigned char Winking_Face[] PROGMEM;
|
extern const unsigned char Winking_Face[] PROGMEM;
|
||||||
|
|
||||||
#define Grinning_Smiling_Eyes_height 30
|
#define Grinning_Smiling_Eyes_height 16
|
||||||
#define Grinning_Smiling_Eyes_width 30
|
#define Grinning_Smiling_Eyes_width 16
|
||||||
extern const unsigned char Grinning_Smiling_Eyes[] PROGMEM;
|
extern const unsigned char Grinning_Smiling_Eyes[] PROGMEM;
|
||||||
|
|
||||||
#define question_height 25
|
#define heart_smile_height 16
|
||||||
#define question_width 25
|
#define heart_smile_width 16
|
||||||
|
extern const unsigned char heart_smile[] PROGMEM;
|
||||||
|
|
||||||
|
#define Heart_eyes_height 16
|
||||||
|
#define Heart_eyes_width 16
|
||||||
|
extern const unsigned char Heart_eyes[] PROGMEM;
|
||||||
|
|
||||||
|
#define question_height 16
|
||||||
|
#define question_width 16
|
||||||
extern const unsigned char question[] PROGMEM;
|
extern const unsigned char question[] PROGMEM;
|
||||||
|
|
||||||
#define bang_height 30
|
#define bang_height 16
|
||||||
#define bang_width 30
|
#define bang_width 16
|
||||||
extern const unsigned char bang[] PROGMEM;
|
extern const unsigned char bang[] PROGMEM;
|
||||||
|
|
||||||
#define haha_height 30
|
#define haha_height 16
|
||||||
#define haha_width 30
|
#define haha_width 16
|
||||||
extern const unsigned char haha[] PROGMEM;
|
extern const unsigned char haha[] PROGMEM;
|
||||||
|
|
||||||
#define ROFL_height 30
|
#define ROFL_height 16
|
||||||
#define ROFL_width 30
|
#define ROFL_width 16
|
||||||
extern const unsigned char ROFL[] PROGMEM;
|
extern const unsigned char ROFL[] PROGMEM;
|
||||||
|
|
||||||
#define Smiling_Closed_Eyes_height 30
|
#define Smiling_Closed_Eyes_height 16
|
||||||
#define Smiling_Closed_Eyes_width 30
|
#define Smiling_Closed_Eyes_width 16
|
||||||
extern const unsigned char Smiling_Closed_Eyes[] PROGMEM;
|
extern const unsigned char Smiling_Closed_Eyes[] PROGMEM;
|
||||||
|
|
||||||
#define Grinning_SmilingEyes2_height 30
|
#define Grinning_SmilingEyes2_height 16
|
||||||
#define Grinning_SmilingEyes2_width 30
|
#define Grinning_SmilingEyes2_width 16
|
||||||
extern const unsigned char Grinning_SmilingEyes2[] PROGMEM;
|
extern const unsigned char Grinning_SmilingEyes2[] PROGMEM;
|
||||||
|
|
||||||
#define wave_icon_height 30
|
#define Loudly_Crying_Face_height 16
|
||||||
#define wave_icon_width 30
|
#define Loudly_Crying_Face_width 16
|
||||||
|
extern const unsigned char Loudly_Crying_Face[] PROGMEM;
|
||||||
|
|
||||||
|
#define wave_icon_height 16
|
||||||
|
#define wave_icon_width 16
|
||||||
extern const unsigned char wave_icon[] PROGMEM;
|
extern const unsigned char wave_icon[] PROGMEM;
|
||||||
|
|
||||||
#define cowboy_height 30
|
#define cowboy_height 16
|
||||||
#define cowboy_width 30
|
#define cowboy_width 16
|
||||||
extern const unsigned char cowboy[] PROGMEM;
|
extern const unsigned char cowboy[] PROGMEM;
|
||||||
|
|
||||||
#define deadmau5_height 30
|
#define deadmau5_height 16
|
||||||
#define deadmau5_width 60
|
#define deadmau5_width 16
|
||||||
extern const unsigned char deadmau5[] PROGMEM;
|
extern const unsigned char deadmau5[] PROGMEM;
|
||||||
|
|
||||||
#define sun_height 30
|
#define sun_height 16
|
||||||
#define sun_width 30
|
#define sun_width 16
|
||||||
extern const unsigned char sun[] PROGMEM;
|
extern const unsigned char sun[] PROGMEM;
|
||||||
|
|
||||||
#define rain_height 30
|
#define rain_height 16
|
||||||
#define rain_width 30
|
#define rain_width 16
|
||||||
extern const unsigned char rain[] PROGMEM;
|
extern const unsigned char rain[] PROGMEM;
|
||||||
|
|
||||||
#define cloud_height 30
|
#define cloud_height 16
|
||||||
#define cloud_width 30
|
#define cloud_width 16
|
||||||
extern const unsigned char cloud[] PROGMEM;
|
extern const unsigned char cloud[] PROGMEM;
|
||||||
|
|
||||||
#define fog_height 25
|
#define fog_height 16
|
||||||
#define fog_width 25
|
#define fog_width 16
|
||||||
extern const unsigned char fog[] PROGMEM;
|
extern const unsigned char fog[] PROGMEM;
|
||||||
|
|
||||||
#define devil_height 30
|
#define devil_height 16
|
||||||
#define devil_width 30
|
#define devil_width 16
|
||||||
extern const unsigned char devil[] PROGMEM;
|
extern const unsigned char devil[] PROGMEM;
|
||||||
|
|
||||||
#define heart_height 30
|
#define heart_height 16
|
||||||
#define heart_width 30
|
#define heart_width 16
|
||||||
extern const unsigned char heart[] PROGMEM;
|
extern const unsigned char heart[] PROGMEM;
|
||||||
|
|
||||||
#define poo_height 30
|
#define poo_height 16
|
||||||
#define poo_width 30
|
#define poo_width 16
|
||||||
extern const unsigned char poo[] PROGMEM;
|
extern const unsigned char poo[] PROGMEM;
|
||||||
|
|
||||||
#define bell_icon_width 30
|
#define bell_icon_width 16
|
||||||
#define bell_icon_height 30
|
#define bell_icon_height 16
|
||||||
extern const unsigned char bell_icon[] PROGMEM;
|
extern const unsigned char bell_icon[] PROGMEM;
|
||||||
|
|
||||||
|
#define cookie_width 16
|
||||||
|
#define cookie_height 16
|
||||||
|
extern const unsigned char cookie[] PROGMEM;
|
||||||
|
|
||||||
|
#define Fire_width 16
|
||||||
|
#define Fire_height 16
|
||||||
|
extern const unsigned char Fire[] PROGMEM;
|
||||||
|
|
||||||
|
#define peace_sign_width 16
|
||||||
|
#define peace_sign_height 16
|
||||||
|
extern const unsigned char peace_sign[] PROGMEM;
|
||||||
|
|
||||||
|
#define Praying_width 16
|
||||||
|
#define Praying_height 16
|
||||||
|
extern const unsigned char Praying[] PROGMEM;
|
||||||
|
|
||||||
|
#define Sparkles_width 16
|
||||||
|
#define Sparkles_height 16
|
||||||
|
extern const unsigned char Sparkles[] PROGMEM;
|
||||||
|
|
||||||
|
#define clown_width 16
|
||||||
|
#define clown_height 16
|
||||||
|
extern const unsigned char clown[] PROGMEM;
|
||||||
|
|
||||||
|
#define robo_width 16
|
||||||
|
#define robo_height 16
|
||||||
|
extern const unsigned char robo[] PROGMEM;
|
||||||
|
|
||||||
|
#define hole_width 16
|
||||||
|
#define hole_height 16
|
||||||
|
extern const unsigned char hole[] PROGMEM;
|
||||||
|
|
||||||
|
#define bowling_width 16
|
||||||
|
#define bowling_height 16
|
||||||
|
extern const unsigned char bowling[] PROGMEM;
|
||||||
|
|
||||||
|
#define vulcan_salute_width 16
|
||||||
|
#define vulcan_salute_height 16
|
||||||
|
extern const unsigned char vulcan_salute[] PROGMEM;
|
||||||
#endif // EXCLUDE_EMOJI
|
#endif // EXCLUDE_EMOJI
|
||||||
|
|
||||||
} // namespace graphics
|
} // namespace graphics
|
||||||
@@ -290,7 +290,7 @@ const uint8_t analog_icon_clock[] PROGMEM = {0b11111111, 0b01000010, 0b00100100,
|
|||||||
|
|
||||||
#define chirpy_width 38
|
#define chirpy_width 38
|
||||||
#define chirpy_height 50
|
#define chirpy_height 50
|
||||||
static unsigned char chirpy[] = {
|
const uint8_t chirpy[] = {
|
||||||
0xfe, 0xff, 0xff, 0xff, 0xdf, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x80, 0xe3, 0x01,
|
0xfe, 0xff, 0xff, 0xff, 0xdf, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x80, 0xe3, 0x01,
|
||||||
0x00, 0x00, 0xc0, 0xe7, 0x01, 0x00, 0x00, 0xc0, 0xe7, 0x01, 0x00, 0x00, 0xc0, 0xe7, 0x01, 0x00, 0x00, 0x80, 0xe3, 0x01, 0x00,
|
0x00, 0x00, 0xc0, 0xe7, 0x01, 0x00, 0x00, 0xc0, 0xe7, 0x01, 0x00, 0x00, 0xc0, 0xe7, 0x01, 0x00, 0x00, 0x80, 0xe3, 0x01, 0x00,
|
||||||
0x00, 0x00, 0xe0, 0x81, 0xff, 0xff, 0x7f, 0xe0, 0xc1, 0xff, 0xff, 0xff, 0xe0, 0xc1, 0xff, 0xff, 0xff, 0xe0, 0xc1, 0xcf, 0x7f,
|
0x00, 0x00, 0xe0, 0x81, 0xff, 0xff, 0x7f, 0xe0, 0xc1, 0xff, 0xff, 0xff, 0xe0, 0xc1, 0xff, 0xff, 0xff, 0xe0, 0xc1, 0xcf, 0x7f,
|
||||||
@@ -306,7 +306,7 @@ static unsigned char chirpy[] = {
|
|||||||
|
|
||||||
#define chirpy_width_hirez 76
|
#define chirpy_width_hirez 76
|
||||||
#define chirpy_height_hirez 100
|
#define chirpy_height_hirez 100
|
||||||
static unsigned char chirpy_hirez[] = {
|
const uint8_t chirpy_hirez[] = {
|
||||||
0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0x03,
|
0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0x03,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00,
|
||||||
@@ -358,7 +358,11 @@ static unsigned char chirpy_hirez[] = {
|
|||||||
|
|
||||||
#define chirpy_small_image_width 8
|
#define chirpy_small_image_width 8
|
||||||
#define chirpy_small_image_height 8
|
#define chirpy_small_image_height 8
|
||||||
static unsigned char small_chirpy[] = {0x7f, 0x41, 0x55, 0x55, 0x55, 0x55, 0x41, 0x7f};
|
const uint8_t chirpy_small[] = {0x7f, 0x41, 0x55, 0x55, 0x55, 0x55, 0x41, 0x7f};
|
||||||
|
|
||||||
|
#ifdef M5STACK_UNITC6L
|
||||||
|
#include "img/icon_small.xbm"
|
||||||
|
#else
|
||||||
#include "img/icon.xbm"
|
#include "img/icon.xbm"
|
||||||
|
#endif
|
||||||
static_assert(sizeof(icon_bits) >= 0, "Silence unused variable warning");
|
static_assert(sizeof(icon_bits) >= 0, "Silence unused variable warning");
|
||||||
30
src/graphics/img/icon_small.xbm
Normal file
30
src/graphics/img/icon_small.xbm
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#ifndef USERPREFS_HAS_SPLASH
|
||||||
|
#define icon_width 50
|
||||||
|
#define icon_height 20
|
||||||
|
static uint8_t icon_bits[] = {
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x80, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x80,
|
||||||
|
0x07, 0xc0, 0x07, 0x00, 0x00, 0x00, 0xc0, 0x0f,
|
||||||
|
0xc0, 0x0f, 0x00, 0x00, 0x00, 0xe0, 0x07, 0xe0,
|
||||||
|
0x0f, 0x00, 0x00, 0x00, 0xe0, 0x07, 0xf0, 0x1f,
|
||||||
|
0x00, 0x00, 0x00, 0xf0, 0x03, 0xf0, 0x3f, 0x00,
|
||||||
|
0x00, 0x00, 0xf8, 0x03, 0xf8, 0x7f, 0x00, 0x00,
|
||||||
|
0x00, 0xf8, 0x01, 0xfc, 0x7e, 0x00, 0x00, 0x00,
|
||||||
|
0xfc, 0x00, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0xfe,
|
||||||
|
0x00, 0x7e, 0xf8, 0x00, 0x00, 0x00, 0x7e, 0x00,
|
||||||
|
0x3f, 0xf8, 0x01, 0x00, 0x00, 0x3f, 0x00, 0x1f,
|
||||||
|
0xf0, 0x01, 0x00, 0x00, 0x1f, 0x80, 0x1f, 0xe0,
|
||||||
|
0x03, 0x00, 0x80, 0x1f, 0xc0, 0x0f, 0xe0, 0x03,
|
||||||
|
0x00, 0x80, 0x0f, 0xc0, 0x07, 0xc0, 0x07, 0x00,
|
||||||
|
0xc0, 0x0f, 0xe0, 0x07, 0x80, 0x0f, 0x00, 0xe0,
|
||||||
|
0x07, 0xf0, 0x03, 0x80, 0x1f, 0x00, 0xe0, 0x03,
|
||||||
|
0xf8, 0x03, 0x00, 0x1f, 0x00, 0xf0, 0x03, 0xf8,
|
||||||
|
0x01, 0x00, 0x3f, 0x00, 0xf8, 0x01, 0xfc, 0x00,
|
||||||
|
0x00, 0x7e, 0x00, 0xfc, 0x00, 0xfe, 0x00, 0x00,
|
||||||
|
0x7e, 0x00, 0xfc, 0x00, 0x7e, 0x00, 0x00, 0xfc,
|
||||||
|
0x00, 0x7e, 0x00, 0x3f, 0x00, 0x00, 0xf8, 0x00,
|
||||||
|
0x7e, 0x00, 0x3e, 0x00, 0x00, 0xf8, 0x00, 0x38,
|
||||||
|
0x00, 0x1c, 0x00, 0x00, 0x70, 0x00, 0x10, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00 };
|
||||||
|
#endif
|
||||||
@@ -13,45 +13,147 @@ void InkHUD::MapApplet::onRender()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper: draw rounded rectangle centered at x,y
|
||||||
|
auto fillRoundedRect = [&](int16_t cx, int16_t cy, int16_t w, int16_t h, int16_t r, uint16_t color) {
|
||||||
|
int16_t x = cx - (w / 2);
|
||||||
|
int16_t y = cy - (h / 2);
|
||||||
|
|
||||||
|
// center rects
|
||||||
|
fillRect(x + r, y, w - 2 * r, h, color);
|
||||||
|
fillRect(x, y + r, r, h - 2 * r, color);
|
||||||
|
fillRect(x + w - r, y + r, r, h - 2 * r, color);
|
||||||
|
|
||||||
|
// corners
|
||||||
|
fillCircle(x + r, y + r, r, color);
|
||||||
|
fillCircle(x + w - r - 1, y + r, r, color);
|
||||||
|
fillCircle(x + r, y + h - r - 1, r, color);
|
||||||
|
fillCircle(x + w - r - 1, y + h - r - 1, r, color);
|
||||||
|
};
|
||||||
|
|
||||||
// Find center of map
|
// Find center of map
|
||||||
// - latitude and longitude
|
|
||||||
// - will be placed at X(0.5), Y(0.5)
|
|
||||||
getMapCenter(&latCenter, &lngCenter);
|
getMapCenter(&latCenter, &lngCenter);
|
||||||
|
|
||||||
// Calculate North+East distance of each node to map center
|
|
||||||
// - which nodes to use controlled by virtual shouldDrawNode method
|
|
||||||
calculateAllMarkers();
|
calculateAllMarkers();
|
||||||
|
|
||||||
// Set the region shown on the map
|
|
||||||
// - default: fit all nodes, plus padding
|
|
||||||
// - maybe overriden by derived applet
|
|
||||||
// - getMapSize *sets* passed parameters (C-style)
|
|
||||||
getMapSize(&widthMeters, &heightMeters);
|
getMapSize(&widthMeters, &heightMeters);
|
||||||
|
|
||||||
// Set the metersToPx conversion value
|
|
||||||
calculateMapScale();
|
calculateMapScale();
|
||||||
|
|
||||||
// Special marker for own node
|
// Draw all markers first
|
||||||
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
|
||||||
if (ourNode && nodeDB->hasValidPosition(ourNode))
|
|
||||||
drawLabeledMarker(ourNode);
|
|
||||||
|
|
||||||
// Draw all markers
|
|
||||||
for (Marker m : markers) {
|
for (Marker m : markers) {
|
||||||
int16_t x = X(0.5) + (m.eastMeters * metersToPx);
|
int16_t x = X(0.5) + (m.eastMeters * metersToPx);
|
||||||
int16_t y = Y(0.5) - (m.northMeters * metersToPx);
|
int16_t y = Y(0.5) - (m.northMeters * metersToPx);
|
||||||
|
|
||||||
// Cross Size
|
// Add white halo outline first
|
||||||
constexpr uint16_t csMin = 5;
|
constexpr int outlinePad = 1;
|
||||||
constexpr uint16_t csMax = 12;
|
int boxSize = 11;
|
||||||
|
int radius = 2; // rounded corner radius
|
||||||
|
|
||||||
// Too many hops away
|
// White halo background
|
||||||
if (m.hasHopsAway && m.hopsAway > config.lora.hop_limit) // Too many mops
|
fillRoundedRect(x, y, boxSize + (outlinePad * 2), boxSize + (outlinePad * 2), radius + 1, WHITE);
|
||||||
printAt(x, y, "!", CENTER, MIDDLE);
|
|
||||||
else if (!m.hasHopsAway) // Unknown hops
|
// Draw inner box
|
||||||
drawCross(x, y, csMin);
|
fillRoundedRect(x, y, boxSize, boxSize, radius, BLACK);
|
||||||
else // The fewer hops, the larger the cross
|
|
||||||
drawCross(x, y, map(m.hopsAway, 0, config.lora.hop_limit, csMax, csMin));
|
// Text inside
|
||||||
|
setFont(fontSmall);
|
||||||
|
setTextColor(WHITE);
|
||||||
|
|
||||||
|
// Draw actual marker on top
|
||||||
|
if (m.hasHopsAway && m.hopsAway > config.lora.hop_limit) {
|
||||||
|
printAt(x + 1, y + 1, "X", CENTER, MIDDLE);
|
||||||
|
} else if (!m.hasHopsAway) {
|
||||||
|
printAt(x + 1, y + 1, "?", CENTER, MIDDLE);
|
||||||
|
} else {
|
||||||
|
char hopStr[4];
|
||||||
|
snprintf(hopStr, sizeof(hopStr), "%d", m.hopsAway);
|
||||||
|
printAt(x, y + 1, hopStr, CENTER, MIDDLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore default font and color
|
||||||
|
setFont(fontSmall);
|
||||||
|
setTextColor(BLACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dual map scale bars
|
||||||
|
int16_t horizPx = width() * 0.25f;
|
||||||
|
int16_t vertPx = height() * 0.25f;
|
||||||
|
float horizMeters = horizPx / metersToPx;
|
||||||
|
float vertMeters = vertPx / metersToPx;
|
||||||
|
|
||||||
|
auto formatDistance = [&](float meters, char *out, size_t len) {
|
||||||
|
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
|
||||||
|
float feet = meters * 3.28084f;
|
||||||
|
if (feet < 528)
|
||||||
|
snprintf(out, len, "%.0f ft", feet);
|
||||||
|
else {
|
||||||
|
float miles = feet / 5280.0f;
|
||||||
|
snprintf(out, len, miles < 10 ? "%.1f mi" : "%.0f mi", miles);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (meters >= 1000)
|
||||||
|
snprintf(out, len, "%.1f km", meters / 1000.0f);
|
||||||
|
else
|
||||||
|
snprintf(out, len, "%.0f m", meters);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Horizontal scale bar
|
||||||
|
int16_t horizBarY = height() - 2;
|
||||||
|
int16_t horizBarX = 1;
|
||||||
|
drawLine(horizBarX, horizBarY, horizBarX + horizPx, horizBarY, BLACK);
|
||||||
|
drawLine(horizBarX, horizBarY - 3, horizBarX, horizBarY + 3, BLACK);
|
||||||
|
drawLine(horizBarX + horizPx, horizBarY - 3, horizBarX + horizPx, horizBarY + 3, BLACK);
|
||||||
|
|
||||||
|
char horizLabel[32];
|
||||||
|
formatDistance(horizMeters, horizLabel, sizeof(horizLabel));
|
||||||
|
int16_t horizLabelW = getTextWidth(horizLabel);
|
||||||
|
int16_t horizLabelH = getFont().lineHeight();
|
||||||
|
int16_t horizLabelX = horizBarX + horizPx + 4;
|
||||||
|
int16_t horizLabelY = horizBarY - horizLabelH + 1;
|
||||||
|
fillRect(horizLabelX - 2, horizLabelY - 1, horizLabelW + 4, horizLabelH + 2, WHITE);
|
||||||
|
printAt(horizLabelX, horizBarY, horizLabel, LEFT, BOTTOM);
|
||||||
|
|
||||||
|
// Vertical scale bar
|
||||||
|
int16_t vertBarX = 1;
|
||||||
|
int16_t vertBarBottom = horizBarY;
|
||||||
|
int16_t vertBarTop = vertBarBottom - vertPx;
|
||||||
|
drawLine(vertBarX, vertBarBottom, vertBarX, vertBarTop, BLACK);
|
||||||
|
drawLine(vertBarX - 3, vertBarBottom, vertBarX + 3, vertBarBottom, BLACK);
|
||||||
|
drawLine(vertBarX - 3, vertBarTop, vertBarX + 3, vertBarTop, BLACK);
|
||||||
|
|
||||||
|
char vertTopLabel[32];
|
||||||
|
formatDistance(vertMeters, vertTopLabel, sizeof(vertTopLabel));
|
||||||
|
int16_t topLabelY = vertBarTop - getFont().lineHeight() - 2;
|
||||||
|
int16_t topLabelW = getTextWidth(vertTopLabel);
|
||||||
|
int16_t topLabelH = getFont().lineHeight();
|
||||||
|
fillRect(vertBarX - 2, topLabelY - 1, topLabelW + 6, topLabelH + 2, WHITE);
|
||||||
|
printAt(vertBarX + (topLabelW / 2) + 1, topLabelY + (topLabelH / 2), vertTopLabel, CENTER, MIDDLE);
|
||||||
|
|
||||||
|
char vertBottomLabel[32];
|
||||||
|
formatDistance(vertMeters, vertBottomLabel, sizeof(vertBottomLabel));
|
||||||
|
int16_t bottomLabelY = vertBarBottom + 4;
|
||||||
|
int16_t bottomLabelW = getTextWidth(vertBottomLabel);
|
||||||
|
int16_t bottomLabelH = getFont().lineHeight();
|
||||||
|
fillRect(vertBarX - 2, bottomLabelY - 1, bottomLabelW + 6, bottomLabelH + 2, WHITE);
|
||||||
|
printAt(vertBarX + (bottomLabelW / 2) + 1, bottomLabelY + (bottomLabelH / 2), vertBottomLabel, CENTER, MIDDLE);
|
||||||
|
|
||||||
|
// Draw our node LAST with full white fill + outline
|
||||||
|
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||||
|
if (ourNode && nodeDB->hasValidPosition(ourNode)) {
|
||||||
|
Marker self = calculateMarker(ourNode->position.latitude_i * 1e-7, ourNode->position.longitude_i * 1e-7, false, 0);
|
||||||
|
|
||||||
|
int16_t centerX = X(0.5) + (self.eastMeters * metersToPx);
|
||||||
|
int16_t centerY = Y(0.5) - (self.northMeters * metersToPx);
|
||||||
|
|
||||||
|
// White fill background + halo
|
||||||
|
fillCircle(centerX, centerY, 8, WHITE); // big white base
|
||||||
|
drawCircle(centerX, centerY, 8, WHITE); // crisp edge
|
||||||
|
|
||||||
|
// Black bullseye on top
|
||||||
|
drawCircle(centerX, centerY, 6, BLACK);
|
||||||
|
fillCircle(centerX, centerY, 2, BLACK);
|
||||||
|
|
||||||
|
// Crosshairs
|
||||||
|
drawLine(centerX - 8, centerY, centerX + 8, centerY, BLACK);
|
||||||
|
drawLine(centerX, centerY - 8, centerX, centerY + 8, BLACK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,116 +165,129 @@ void InkHUD::MapApplet::onRender()
|
|||||||
|
|
||||||
void InkHUD::MapApplet::getMapCenter(float *lat, float *lng)
|
void InkHUD::MapApplet::getMapCenter(float *lat, float *lng)
|
||||||
{
|
{
|
||||||
// Find mean lat long coords
|
// If we have a valid position for our own node, use that as the anchor
|
||||||
// ============================
|
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||||
// - assigning X, Y and Z values to position on Earth's surface in 3D space, relative to center of planet
|
if (ourNode && nodeDB->hasValidPosition(ourNode)) {
|
||||||
// - averages the x, y and z coords
|
*lat = ourNode->position.latitude_i * 1e-7;
|
||||||
// - uses tan to find angles for lat / long degrees
|
*lng = ourNode->position.longitude_i * 1e-7;
|
||||||
// - longitude: triangle formed by x and y (on plane of the equator)
|
} else {
|
||||||
// - latitude: triangle formed by z (north south),
|
// Find mean lat long coords
|
||||||
// and the line along plane of equator which stretches from earth's axis to where point xyz intersects planet's surface
|
// ============================
|
||||||
|
// - assigning X, Y and Z values to position on Earth's surface in 3D space, relative to center of planet
|
||||||
|
// - averages the x, y and z coords
|
||||||
|
// - uses tan to find angles for lat / long degrees
|
||||||
|
// - longitude: triangle formed by x and y (on plane of the equator)
|
||||||
|
// - latitude: triangle formed by z (north south),
|
||||||
|
// and the line along plane of equator which stretches from earth's axis to where point xyz intersects planet's
|
||||||
|
// surface
|
||||||
|
|
||||||
// Working totals, averaged after nodeDB processed
|
// Working totals, averaged after nodeDB processed
|
||||||
uint32_t positionCount = 0;
|
uint32_t positionCount = 0;
|
||||||
float xAvg = 0;
|
float xAvg = 0;
|
||||||
float yAvg = 0;
|
float yAvg = 0;
|
||||||
float zAvg = 0;
|
float zAvg = 0;
|
||||||
|
|
||||||
// For each node in db
|
// For each node in db
|
||||||
for (uint32_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
|
for (uint32_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
|
||||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i);
|
meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i);
|
||||||
|
|
||||||
// Skip if no position
|
// Skip if no position
|
||||||
if (!nodeDB->hasValidPosition(node))
|
if (!nodeDB->hasValidPosition(node))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Skip if derived applet doesn't want to show this node on the map
|
// Skip if derived applet doesn't want to show this node on the map
|
||||||
if (!shouldDrawNode(node))
|
if (!shouldDrawNode(node))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Latitude and Longitude of node, in radians
|
// Latitude and Longitude of node, in radians
|
||||||
float latRad = node->position.latitude_i * (1e-7) * DEG_TO_RAD;
|
float latRad = node->position.latitude_i * (1e-7) * DEG_TO_RAD;
|
||||||
float lngRad = node->position.longitude_i * (1e-7) * DEG_TO_RAD;
|
float lngRad = node->position.longitude_i * (1e-7) * DEG_TO_RAD;
|
||||||
|
|
||||||
// Convert to cartesian points, with center of earth at 0, 0, 0
|
// Convert to cartesian points, with center of earth at 0, 0, 0
|
||||||
// Exact distance from center is irrelevant, as we're only interested in the vector
|
// Exact distance from center is irrelevant, as we're only interested in the vector
|
||||||
float x = cos(latRad) * cos(lngRad);
|
float x = cos(latRad) * cos(lngRad);
|
||||||
float y = cos(latRad) * sin(lngRad);
|
float y = cos(latRad) * sin(lngRad);
|
||||||
float z = sin(latRad);
|
float z = sin(latRad);
|
||||||
|
|
||||||
// To find mean values shortly
|
// To find mean values shortly
|
||||||
xAvg += x;
|
xAvg += x;
|
||||||
yAvg += y;
|
yAvg += y;
|
||||||
zAvg += z;
|
zAvg += z;
|
||||||
positionCount++;
|
positionCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All NodeDB processed, find mean values
|
||||||
|
xAvg /= positionCount;
|
||||||
|
yAvg /= positionCount;
|
||||||
|
zAvg /= positionCount;
|
||||||
|
|
||||||
|
// Longitude from cartesian coords
|
||||||
|
// (Angle from 3D coords describing a point of globe's surface)
|
||||||
|
/*
|
||||||
|
UK
|
||||||
|
/-------\
|
||||||
|
(Top View) /- -\
|
||||||
|
/- (You) -\
|
||||||
|
/- . -\
|
||||||
|
/- . X -\
|
||||||
|
Asia - ... - USA
|
||||||
|
\- Y -/
|
||||||
|
\- -/
|
||||||
|
\- -/
|
||||||
|
\- -/
|
||||||
|
\- -----/
|
||||||
|
Pacific
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
*lng = atan2(yAvg, xAvg) * RAD_TO_DEG;
|
||||||
|
|
||||||
|
// Latitude from cartesian coords
|
||||||
|
// (Angle from 3D coords describing a point on the globe's surface)
|
||||||
|
// As latitude increases, distance from the Earth's north-south axis out to our surface point decreases.
|
||||||
|
// Means we need to first find the hypotenuse which becomes base of our triangle in the second step
|
||||||
|
/*
|
||||||
|
UK North
|
||||||
|
/-------\ (Front View) /-------\
|
||||||
|
(Top View) /- -\ /- -\
|
||||||
|
/- (You) -\ /-(You) -\
|
||||||
|
/- /. -\ /- . -\
|
||||||
|
/- √X²+Y²/ . X -\ /- Z . -\
|
||||||
|
Asia - /... - USA - ..... -
|
||||||
|
\- Y -/ \- √X²+Y² -/
|
||||||
|
\- -/ \- -/
|
||||||
|
\- -/ \- -/
|
||||||
|
\- -/ \- -/
|
||||||
|
\- -----/ \- -----/
|
||||||
|
Pacific South
|
||||||
|
*/
|
||||||
|
|
||||||
|
float hypotenuse = sqrt((xAvg * xAvg) + (yAvg * yAvg)); // Distance from globe's north-south axis to surface intersect
|
||||||
|
*lat = atan2(zAvg, hypotenuse) * RAD_TO_DEG;
|
||||||
}
|
}
|
||||||
|
|
||||||
// All NodeDB processed, find mean values
|
// Use either our node position, or the mean fallback as the center
|
||||||
xAvg /= positionCount;
|
latCenter = *lat;
|
||||||
yAvg /= positionCount;
|
lngCenter = *lng;
|
||||||
zAvg /= positionCount;
|
|
||||||
|
|
||||||
// Longitude from cartesian coords
|
|
||||||
// (Angle from 3D coords describing a point of globe's surface)
|
|
||||||
/*
|
|
||||||
UK
|
|
||||||
/-------\
|
|
||||||
(Top View) /- -\
|
|
||||||
/- (You) -\
|
|
||||||
/- . -\
|
|
||||||
/- . X -\
|
|
||||||
Asia - ... - USA
|
|
||||||
\- Y -/
|
|
||||||
\- -/
|
|
||||||
\- -/
|
|
||||||
\- -/
|
|
||||||
\- -----/
|
|
||||||
Pacific
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
*lng = atan2(yAvg, xAvg) * RAD_TO_DEG;
|
|
||||||
|
|
||||||
// Latitude from cartesian coords
|
|
||||||
// (Angle from 3D coords describing a point on the globe's surface)
|
|
||||||
// As latitude increases, distance from the Earth's north-south axis out to our surface point decreases.
|
|
||||||
// Means we need to first find the hypotenuse which becomes base of our triangle in the second step
|
|
||||||
/*
|
|
||||||
UK North
|
|
||||||
/-------\ (Front View) /-------\
|
|
||||||
(Top View) /- -\ /- -\
|
|
||||||
/- (You) -\ /-(You) -\
|
|
||||||
/- /. -\ /- . -\
|
|
||||||
/- √X²+Y²/ . X -\ /- Z . -\
|
|
||||||
Asia - /... - USA - ..... -
|
|
||||||
\- Y -/ \- √X²+Y² -/
|
|
||||||
\- -/ \- -/
|
|
||||||
\- -/ \- -/
|
|
||||||
\- -/ \- -/
|
|
||||||
\- -----/ \- -----/
|
|
||||||
Pacific South
|
|
||||||
*/
|
|
||||||
|
|
||||||
float hypotenuse = sqrt((xAvg * xAvg) + (yAvg * yAvg)); // Distance from globe's north-south axis to surface intersect
|
|
||||||
*lat = atan2(zAvg, hypotenuse) * RAD_TO_DEG;
|
|
||||||
|
|
||||||
// ----------------------------------------------
|
// ----------------------------------------------
|
||||||
// This has given us the "mean position"
|
// This has given us either:
|
||||||
// This will be a position *somewhere* near the center of our nodes.
|
// - our actual position (preferred), or
|
||||||
// What we actually want is to place our center so that our outermost nodes end up on the border of our map.
|
// - a mean position (fallback if we had no fix)
|
||||||
// The only real use of our "mean position" is to give us a reference frame:
|
//
|
||||||
// which direction is east, and which is west.
|
// What we actually want is to place our center so that our outermost nodes
|
||||||
|
// end up on the border of our map. The only real use of our "center" is to give
|
||||||
|
// us a reference frame: which direction is east, and which is west.
|
||||||
//------------------------------------------------
|
//------------------------------------------------
|
||||||
|
|
||||||
// Find furthest nodes from "mean lat long"
|
// Find furthest nodes from our center
|
||||||
// ========================================
|
// ========================================
|
||||||
|
|
||||||
float northernmost = latCenter;
|
float northernmost = latCenter;
|
||||||
float southernmost = latCenter;
|
float southernmost = latCenter;
|
||||||
float easternmost = lngCenter;
|
float easternmost = lngCenter;
|
||||||
float westernmost = lngCenter;
|
float westernmost = lngCenter;
|
||||||
|
|
||||||
for (uint8_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
|
for (size_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
|
||||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i);
|
meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i);
|
||||||
|
|
||||||
// Skip if no position
|
// Skip if no position
|
||||||
@@ -184,14 +299,14 @@ void InkHUD::MapApplet::getMapCenter(float *lat, float *lng)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Check for a new top or bottom latitude
|
// Check for a new top or bottom latitude
|
||||||
float lat = node->position.latitude_i * 1e-7;
|
float latNode = node->position.latitude_i * 1e-7;
|
||||||
northernmost = max(northernmost, lat);
|
northernmost = max(northernmost, latNode);
|
||||||
southernmost = min(southernmost, lat);
|
southernmost = min(southernmost, latNode);
|
||||||
|
|
||||||
// Longitude is trickier
|
// Longitude is trickier
|
||||||
float lng = node->position.longitude_i * 1e-7;
|
float lngNode = node->position.longitude_i * 1e-7;
|
||||||
float degEastward = fmod(((lng - lngCenter) + 360), 360); // Degrees traveled east from lngCenter to reach node
|
float degEastward = fmod(((lngNode - lngCenter) + 360), 360); // Degrees traveled east from lngCenter to reach node
|
||||||
float degWestward = abs(fmod(((lng - lngCenter) - 360), 360)); // Degrees traveled west from lngCenter to reach node
|
float degWestward = abs(fmod(((lngNode - lngCenter) - 360), 360)); // Degrees traveled west from lngCenter to reach node
|
||||||
if (degEastward < degWestward)
|
if (degEastward < degWestward)
|
||||||
easternmost = max(easternmost, lngCenter + degEastward);
|
easternmost = max(easternmost, lngCenter + degEastward);
|
||||||
else
|
else
|
||||||
@@ -250,7 +365,6 @@ InkHUD::MapApplet::Marker InkHUD::MapApplet::calculateMarker(float lat, float ln
|
|||||||
m.hopsAway = hopsAway;
|
m.hopsAway = hopsAway;
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw a marker on the map for a node, with a shortname label, and backing box
|
// Draw a marker on the map for a node, with a shortname label, and backing box
|
||||||
void InkHUD::MapApplet::drawLabeledMarker(meshtastic_NodeInfoLite *node)
|
void InkHUD::MapApplet::drawLabeledMarker(meshtastic_NodeInfoLite *node)
|
||||||
{
|
{
|
||||||
@@ -324,6 +438,18 @@ void InkHUD::MapApplet::drawLabeledMarker(meshtastic_NodeInfoLite *node)
|
|||||||
textX = labelX + paddingW;
|
textX = labelX + paddingW;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prevent overlap with scale bars and their labels
|
||||||
|
// Define a "safe zone" in the bottom-left where the scale bars and text are drawn
|
||||||
|
constexpr int16_t safeZoneHeight = 28; // adjust based on your label font height
|
||||||
|
constexpr int16_t safeZoneWidth = 60; // adjust based on horizontal label width zone
|
||||||
|
bool overlapsScale = (labelY + labelH > height() - safeZoneHeight) && (labelX < safeZoneWidth);
|
||||||
|
|
||||||
|
// If it overlaps, shift label upward slightly above the safe zone
|
||||||
|
if (overlapsScale) {
|
||||||
|
labelY = height() - safeZoneHeight - labelH - 2;
|
||||||
|
textY = labelY + (labelH / 2);
|
||||||
|
}
|
||||||
|
|
||||||
// Backing box
|
// Backing box
|
||||||
fillRect(labelX, labelY, labelW, labelH, WHITE);
|
fillRect(labelX, labelY, labelW, labelH, WHITE);
|
||||||
drawRect(labelX, labelY, labelW, labelH, BLACK);
|
drawRect(labelX, labelY, labelW, labelH, BLACK);
|
||||||
@@ -348,8 +474,8 @@ void InkHUD::MapApplet::drawLabeledMarker(meshtastic_NodeInfoLite *node)
|
|||||||
// Need at least two, to draw a sensible map
|
// Need at least two, to draw a sensible map
|
||||||
bool InkHUD::MapApplet::enoughMarkers()
|
bool InkHUD::MapApplet::enoughMarkers()
|
||||||
{
|
{
|
||||||
uint8_t count = 0;
|
size_t count = 0;
|
||||||
for (uint8_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
|
for (size_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
|
||||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i);
|
meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i);
|
||||||
|
|
||||||
// Count nodes
|
// Count nodes
|
||||||
|
|||||||
@@ -127,6 +127,11 @@ void InkHUD::NodeListApplet::onRender()
|
|||||||
// Y value (top) of the current card. Increases as we draw.
|
// Y value (top) of the current card. Increases as we draw.
|
||||||
uint16_t cardTopY = headerDivY + padDivH;
|
uint16_t cardTopY = headerDivY + padDivH;
|
||||||
|
|
||||||
|
// Clean up deleted nodes before drawing
|
||||||
|
cards.erase(
|
||||||
|
std::remove_if(cards.begin(), cards.end(), [](const CardInfo &c) { return nodeDB->getMeshNode(c.nodeNum) == nullptr; }),
|
||||||
|
cards.end());
|
||||||
|
|
||||||
// -- Each node in list --
|
// -- Each node in list --
|
||||||
for (auto card = cards.begin(); card != cards.end(); ++card) {
|
for (auto card = cards.begin(); card != cards.end(); ++card) {
|
||||||
|
|
||||||
@@ -141,6 +146,11 @@ void InkHUD::NodeListApplet::onRender()
|
|||||||
|
|
||||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeNum);
|
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeNum);
|
||||||
|
|
||||||
|
// Skip deleted nodes
|
||||||
|
if (!node) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// -- Shortname --
|
// -- Shortname --
|
||||||
// Parse special chars in the short name
|
// Parse special chars in the short name
|
||||||
// Use "?" if unknown
|
// Use "?" if unknown
|
||||||
@@ -188,7 +198,7 @@ void InkHUD::NodeListApplet::onRender()
|
|||||||
drawSignalIndicator(signalX, signalY, signalW, signalH, signal);
|
drawSignalIndicator(signalX, signalY, signalW, signalH, signal);
|
||||||
}
|
}
|
||||||
// Otherwise, print "hops away" info, if available
|
// Otherwise, print "hops away" info, if available
|
||||||
else if (hopsAway != CardInfo::HOPS_UNKNOWN) {
|
else if (hopsAway != CardInfo::HOPS_UNKNOWN && node) {
|
||||||
std::string hopString = to_string(node->hops_away);
|
std::string hopString = to_string(node->hops_away);
|
||||||
hopString += " Hop";
|
hopString += " Hop";
|
||||||
if (node->hops_away != 1)
|
if (node->hops_away != 1)
|
||||||
|
|||||||
@@ -709,7 +709,7 @@ void InkHUD::MenuApplet::drawSystemInfoPanel(int16_t left, int16_t top, uint16_t
|
|||||||
// Voltage
|
// Voltage
|
||||||
float voltage = powerStatus->getBatteryVoltageMv() / 1000.0;
|
float voltage = powerStatus->getBatteryVoltageMv() / 1000.0;
|
||||||
char voltageStr[6]; // "XX.XV"
|
char voltageStr[6]; // "XX.XV"
|
||||||
sprintf(voltageStr, "%.1fV", voltage);
|
sprintf(voltageStr, "%.2fV", voltage);
|
||||||
printAt(colC[0], labelT, "Bat", CENTER, TOP);
|
printAt(colC[0], labelT, "Bat", CENTER, TOP);
|
||||||
printAt(colC[0], valT, voltageStr, CENTER, TOP);
|
printAt(colC[0], valT, voltageStr, CENTER, TOP);
|
||||||
|
|
||||||
|
|||||||
@@ -274,7 +274,12 @@ int32_t ButtonThread::runOnce()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
btnEvent = BUTTON_EVENT_NONE;
|
btnEvent = BUTTON_EVENT_NONE;
|
||||||
return 50;
|
|
||||||
|
// only pull when the button is pressed, we get notified via IRQ on a new press
|
||||||
|
if (!userButton.isIdle() || waitingForLongPress) {
|
||||||
|
return 50;
|
||||||
|
}
|
||||||
|
return 100; // FIXME: Why can't we rely on interrupts and use INT32_MAX here?
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ void ExpressLRSFiveWay::determineAction(KeyType key, PressLength length)
|
|||||||
// Feed input to the canned messages module
|
// Feed input to the canned messages module
|
||||||
void ExpressLRSFiveWay::sendKey(input_broker_event key)
|
void ExpressLRSFiveWay::sendKey(input_broker_event key)
|
||||||
{
|
{
|
||||||
InputEvent e;
|
InputEvent e = {};
|
||||||
e.source = inputSourceName;
|
e.source = inputSourceName;
|
||||||
e.inputEvent = key;
|
e.inputEvent = key;
|
||||||
notifyObservers(&e);
|
notifyObservers(&e);
|
||||||
|
|||||||
@@ -3,16 +3,66 @@
|
|||||||
|
|
||||||
InputBroker *inputBroker = nullptr;
|
InputBroker *inputBroker = nullptr;
|
||||||
|
|
||||||
InputBroker::InputBroker(){};
|
InputBroker::InputBroker()
|
||||||
|
{
|
||||||
|
#ifdef HAS_FREE_RTOS
|
||||||
|
inputEventQueue = xQueueCreate(5, sizeof(InputEvent));
|
||||||
|
pollSoonQueue = xQueueCreate(5, sizeof(InputPollable *));
|
||||||
|
xTaskCreate(pollSoonWorker, "input-pollSoon", 2 * 1024, this, 10, &pollSoonTask);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void InputBroker::registerSource(Observable<const InputEvent *> *source)
|
void InputBroker::registerSource(Observable<const InputEvent *> *source)
|
||||||
{
|
{
|
||||||
this->inputEventObserver.observe(source);
|
this->inputEventObserver.observe(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAS_FREE_RTOS
|
||||||
|
void InputBroker::requestPollSoon(InputPollable *pollable)
|
||||||
|
{
|
||||||
|
if (xPortInIsrContext() == pdTRUE) {
|
||||||
|
xQueueSendFromISR(pollSoonQueue, &pollable, NULL);
|
||||||
|
} else {
|
||||||
|
xQueueSend(pollSoonQueue, &pollable, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputBroker::queueInputEvent(const InputEvent *event)
|
||||||
|
{
|
||||||
|
if (xPortInIsrContext() == pdTRUE) {
|
||||||
|
xQueueSendFromISR(inputEventQueue, event, NULL);
|
||||||
|
} else {
|
||||||
|
xQueueSend(inputEventQueue, event, portMAX_DELAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputBroker::processInputEventQueue()
|
||||||
|
{
|
||||||
|
InputEvent event;
|
||||||
|
while (xQueueReceive(inputEventQueue, &event, 0)) {
|
||||||
|
handleInputEvent(&event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int InputBroker::handleInputEvent(const InputEvent *event)
|
int InputBroker::handleInputEvent(const InputEvent *event)
|
||||||
{
|
{
|
||||||
powerFSM.trigger(EVENT_INPUT); // todo: not every input should wake, like long hold release
|
powerFSM.trigger(EVENT_INPUT); // todo: not every input should wake, like long hold release
|
||||||
this->notifyObservers(event);
|
this->notifyObservers(event);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAS_FREE_RTOS
|
||||||
|
void InputBroker::pollSoonWorker(void *p)
|
||||||
|
{
|
||||||
|
InputBroker *instance = (InputBroker *)p;
|
||||||
|
while (true) {
|
||||||
|
InputPollable *pollable = NULL;
|
||||||
|
xQueueReceive(instance->pollSoonQueue, &pollable, portMAX_DELAY);
|
||||||
|
if (pollable) {
|
||||||
|
pollable->pollOnce();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Observer.h"
|
#include "Observer.h"
|
||||||
|
#include "freertosinc.h"
|
||||||
|
|
||||||
enum input_broker_event {
|
enum input_broker_event {
|
||||||
INPUT_BROKER_NONE = 0,
|
INPUT_BROKER_NONE = 0,
|
||||||
@@ -41,6 +43,13 @@ typedef struct _InputEvent {
|
|||||||
uint16_t touchX;
|
uint16_t touchX;
|
||||||
uint16_t touchY;
|
uint16_t touchY;
|
||||||
} InputEvent;
|
} InputEvent;
|
||||||
|
|
||||||
|
class InputPollable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void pollOnce() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
class InputBroker : public Observable<const InputEvent *>
|
class InputBroker : public Observable<const InputEvent *>
|
||||||
{
|
{
|
||||||
CallbackObserver<InputBroker, const InputEvent *> inputEventObserver =
|
CallbackObserver<InputBroker, const InputEvent *> inputEventObserver =
|
||||||
@@ -50,9 +59,22 @@ class InputBroker : public Observable<const InputEvent *>
|
|||||||
InputBroker();
|
InputBroker();
|
||||||
void registerSource(Observable<const InputEvent *> *source);
|
void registerSource(Observable<const InputEvent *> *source);
|
||||||
void injectInputEvent(const InputEvent *event) { handleInputEvent(event); }
|
void injectInputEvent(const InputEvent *event) { handleInputEvent(event); }
|
||||||
|
#ifdef HAS_FREE_RTOS
|
||||||
|
void requestPollSoon(InputPollable *pollable);
|
||||||
|
void queueInputEvent(const InputEvent *event);
|
||||||
|
void processInputEventQueue();
|
||||||
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int handleInputEvent(const InputEvent *event);
|
int handleInputEvent(const InputEvent *event);
|
||||||
|
|
||||||
|
private:
|
||||||
|
#ifdef HAS_FREE_RTOS
|
||||||
|
QueueHandle_t inputEventQueue;
|
||||||
|
QueueHandle_t pollSoonQueue;
|
||||||
|
TaskHandle_t pollSoonTask;
|
||||||
|
static void pollSoonWorker(void *p);
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
extern InputBroker *inputBroker;
|
extern InputBroker *inputBroker;
|
||||||
@@ -73,7 +73,7 @@ int32_t LinuxInput::runOnce()
|
|||||||
int rd = read(events[i].data.fd, ev, sizeof(ev));
|
int rd = read(events[i].data.fd, ev, sizeof(ev));
|
||||||
assert(rd > ((signed int)sizeof(struct input_event)));
|
assert(rd > ((signed int)sizeof(struct input_event)));
|
||||||
for (int j = 0; j < rd / ((signed int)sizeof(struct input_event)); j++) {
|
for (int j = 0; j < rd / ((signed int)sizeof(struct input_event)); j++) {
|
||||||
InputEvent e;
|
InputEvent e = {};
|
||||||
e.inputEvent = INPUT_BROKER_NONE;
|
e.inputEvent = INPUT_BROKER_NONE;
|
||||||
e.source = this->_originName;
|
e.source = this->_originName;
|
||||||
e.kbchar = 0;
|
e.kbchar = 0;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
RotaryEncoderImpl *rotaryEncoderImpl;
|
RotaryEncoderImpl *rotaryEncoderImpl;
|
||||||
|
|
||||||
RotaryEncoderImpl::RotaryEncoderImpl() : concurrency::OSThread(ORIGIN_NAME), originName(ORIGIN_NAME)
|
RotaryEncoderImpl::RotaryEncoderImpl()
|
||||||
{
|
{
|
||||||
rotary = nullptr;
|
rotary = nullptr;
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,6 @@ bool RotaryEncoderImpl::init()
|
|||||||
if (!moduleConfig.canned_message.updown1_enabled || moduleConfig.canned_message.inputbroker_pin_a == 0 ||
|
if (!moduleConfig.canned_message.updown1_enabled || moduleConfig.canned_message.inputbroker_pin_a == 0 ||
|
||||||
moduleConfig.canned_message.inputbroker_pin_b == 0) {
|
moduleConfig.canned_message.inputbroker_pin_b == 0) {
|
||||||
// Input device is disabled.
|
// Input device is disabled.
|
||||||
disable();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,7 +29,11 @@ bool RotaryEncoderImpl::init()
|
|||||||
moduleConfig.canned_message.inputbroker_pin_press);
|
moduleConfig.canned_message.inputbroker_pin_press);
|
||||||
rotary->resetButton();
|
rotary->resetButton();
|
||||||
|
|
||||||
inputBroker->registerSource(this);
|
interruptInstance = this;
|
||||||
|
auto interruptHandler = []() { inputBroker->requestPollSoon(interruptInstance); };
|
||||||
|
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_a, interruptHandler, CHANGE);
|
||||||
|
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_b, interruptHandler, CHANGE);
|
||||||
|
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_press, interruptHandler, CHANGE);
|
||||||
|
|
||||||
LOG_INFO("RotaryEncoder initialized pins(%d, %d, %d), events(%d, %d, %d)", moduleConfig.canned_message.inputbroker_pin_a,
|
LOG_INFO("RotaryEncoder initialized pins(%d, %d, %d), events(%d, %d, %d)", moduleConfig.canned_message.inputbroker_pin_a,
|
||||||
moduleConfig.canned_message.inputbroker_pin_b, moduleConfig.canned_message.inputbroker_pin_press, eventCw, eventCcw,
|
moduleConfig.canned_message.inputbroker_pin_b, moduleConfig.canned_message.inputbroker_pin_press, eventCw, eventCcw,
|
||||||
@@ -38,36 +41,36 @@ bool RotaryEncoderImpl::init()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t RotaryEncoderImpl::runOnce()
|
void RotaryEncoderImpl::pollOnce()
|
||||||
{
|
{
|
||||||
InputEvent e{originName, INPUT_BROKER_NONE, 0, 0, 0};
|
InputEvent e{ORIGIN_NAME, INPUT_BROKER_NONE, 0, 0, 0};
|
||||||
|
|
||||||
static uint32_t lastPressed = millis();
|
static uint32_t lastPressed = millis();
|
||||||
if (rotary->readButton() == RotaryEncoder::ButtonState::BUTTON_PRESSED) {
|
if (rotary->readButton() == RotaryEncoder::ButtonState::BUTTON_PRESSED) {
|
||||||
if (lastPressed + 200 < millis()) {
|
if (lastPressed + 200 < millis()) {
|
||||||
LOG_DEBUG("Rotary event Press");
|
LOG_DEBUG("Rotary event Press");
|
||||||
lastPressed = millis();
|
lastPressed = millis();
|
||||||
e.inputEvent = this->eventPressed;
|
e.inputEvent = this->eventPressed;
|
||||||
}
|
inputBroker->queueInputEvent(&e);
|
||||||
} else {
|
|
||||||
switch (rotary->process()) {
|
|
||||||
case RotaryEncoder::DIRECTION_CW:
|
|
||||||
LOG_DEBUG("Rotary event CW");
|
|
||||||
e.inputEvent = this->eventCw;
|
|
||||||
break;
|
|
||||||
case RotaryEncoder::DIRECTION_CCW:
|
|
||||||
LOG_DEBUG("Rotary event CCW");
|
|
||||||
e.inputEvent = this->eventCcw;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.inputEvent != INPUT_BROKER_NONE) {
|
switch (rotary->process()) {
|
||||||
this->notifyObservers(&e);
|
case RotaryEncoder::DIRECTION_CW:
|
||||||
|
LOG_DEBUG("Rotary event CW");
|
||||||
|
e.inputEvent = this->eventCw;
|
||||||
|
inputBroker->queueInputEvent(&e);
|
||||||
|
break;
|
||||||
|
case RotaryEncoder::DIRECTION_CCW:
|
||||||
|
LOG_DEBUG("Rotary event CCW");
|
||||||
|
e.inputEvent = this->eventCcw;
|
||||||
|
inputBroker->queueInputEvent(&e);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 10;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RotaryEncoderImpl *RotaryEncoderImpl::interruptInstance;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// This is a non-interrupt version of RotaryEncoder which is based on a debounce inherent FSM table (see RotaryEncoder library)
|
// This is a version of RotaryEncoder which is based on a debounce inherent FSM table (see RotaryEncoder library)
|
||||||
|
|
||||||
#include "InputBroker.h"
|
#include "InputBroker.h"
|
||||||
#include "concurrency/OSThread.h"
|
#include "concurrency/OSThread.h"
|
||||||
@@ -8,21 +8,21 @@
|
|||||||
|
|
||||||
class RotaryEncoder;
|
class RotaryEncoder;
|
||||||
|
|
||||||
class RotaryEncoderImpl : public Observable<const InputEvent *>, public concurrency::OSThread
|
class RotaryEncoderImpl : public InputPollable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RotaryEncoderImpl();
|
RotaryEncoderImpl();
|
||||||
bool init(void);
|
bool init(void);
|
||||||
|
virtual void pollOnce() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual int32_t runOnce() override;
|
static RotaryEncoderImpl *interruptInstance;
|
||||||
|
|
||||||
input_broker_event eventCw = INPUT_BROKER_NONE;
|
input_broker_event eventCw = INPUT_BROKER_NONE;
|
||||||
input_broker_event eventCcw = INPUT_BROKER_NONE;
|
input_broker_event eventCcw = INPUT_BROKER_NONE;
|
||||||
input_broker_event eventPressed = INPUT_BROKER_NONE;
|
input_broker_event eventPressed = INPUT_BROKER_NONE;
|
||||||
|
|
||||||
RotaryEncoder *rotary;
|
RotaryEncoder *rotary;
|
||||||
const char *originName;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern RotaryEncoderImpl *rotaryEncoderImpl;
|
extern RotaryEncoderImpl *rotaryEncoderImpl;
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ void RotaryEncoderInterruptBase::init(
|
|||||||
|
|
||||||
if (!isRAK || pinPress != 0) {
|
if (!isRAK || pinPress != 0) {
|
||||||
pinMode(pinPress, INPUT_PULLUP);
|
pinMode(pinPress, INPUT_PULLUP);
|
||||||
attachInterrupt(pinPress, onIntPress, RISING);
|
attachInterrupt(pinPress, onIntPress, CHANGE);
|
||||||
}
|
}
|
||||||
if (!isRAK || this->_pinA != 0) {
|
if (!isRAK || this->_pinA != 0) {
|
||||||
pinMode(this->_pinA, INPUT_PULLUP);
|
pinMode(this->_pinA, INPUT_PULLUP);
|
||||||
@@ -45,7 +45,7 @@ void RotaryEncoderInterruptBase::init(
|
|||||||
|
|
||||||
int32_t RotaryEncoderInterruptBase::runOnce()
|
int32_t RotaryEncoderInterruptBase::runOnce()
|
||||||
{
|
{
|
||||||
InputEvent e;
|
InputEvent e = {};
|
||||||
e.inputEvent = INPUT_BROKER_NONE;
|
e.inputEvent = INPUT_BROKER_NONE;
|
||||||
e.source = this->_originName;
|
e.source = this->_originName;
|
||||||
unsigned long now = millis();
|
unsigned long now = millis();
|
||||||
@@ -151,7 +151,7 @@ RotaryEncoderInterruptBaseStateType RotaryEncoderInterruptBase::intHandler(bool
|
|||||||
// Logic to prevent bouncing.
|
// Logic to prevent bouncing.
|
||||||
newState = ROTARY_EVENT_CLEARED;
|
newState = ROTARY_EVENT_CLEARED;
|
||||||
}
|
}
|
||||||
setIntervalFromNow(50); // TODO: this modifies a non-volatile variable!
|
setIntervalFromNow(ROTARY_DELAY); // TODO: this modifies a non-volatile variable!
|
||||||
|
|
||||||
return newState;
|
return newState;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ bool SeesawRotary::init()
|
|||||||
|
|
||||||
int32_t SeesawRotary::runOnce()
|
int32_t SeesawRotary::runOnce()
|
||||||
{
|
{
|
||||||
InputEvent e;
|
InputEvent e = {};
|
||||||
e.inputEvent = INPUT_BROKER_NONE;
|
e.inputEvent = INPUT_BROKER_NONE;
|
||||||
bool currentlyPressed = !ss.digitalRead(SS_SWITCH);
|
bool currentlyPressed = !ss.digitalRead(SS_SWITCH);
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ SerialKeyboard::SerialKeyboard(const char *name) : concurrency::OSThread(name)
|
|||||||
|
|
||||||
void SerialKeyboard::erase()
|
void SerialKeyboard::erase()
|
||||||
{
|
{
|
||||||
InputEvent e;
|
InputEvent e = {};
|
||||||
e.inputEvent = INPUT_BROKER_BACK;
|
e.inputEvent = INPUT_BROKER_BACK;
|
||||||
e.kbchar = 0x08;
|
e.kbchar = 0x08;
|
||||||
e.source = this->_originName;
|
e.source = this->_originName;
|
||||||
@@ -80,7 +80,7 @@ int32_t SerialKeyboard::runOnce()
|
|||||||
|
|
||||||
if (keys < prevKeys) { // a new key has been pressed (and not released), doesn't works for multiple presses at once but
|
if (keys < prevKeys) { // a new key has been pressed (and not released), doesn't works for multiple presses at once but
|
||||||
// shouldn't be a limitation
|
// shouldn't be a limitation
|
||||||
InputEvent e;
|
InputEvent e = {};
|
||||||
e.inputEvent = INPUT_BROKER_NONE;
|
e.inputEvent = INPUT_BROKER_NONE;
|
||||||
e.source = this->_originName;
|
e.source = this->_originName;
|
||||||
// SELECT OR SEND OR CANCEL EVENT
|
// SELECT OR SEND OR CANCEL EVENT
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user