mirror of
https://github.com/meshtastic/firmware.git
synced 2025-12-13 22:32:27 +00:00
Compare commits
735 Commits
usbhost
...
double-tro
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca6c83e07a | ||
|
|
e5a2ce54e7 | ||
|
|
45f15b8fe6 | ||
|
|
2a14696525 | ||
|
|
05edcc5d6c | ||
|
|
828e11cc48 | ||
|
|
adae68fbfe | ||
|
|
7822f28152 | ||
|
|
d332dfa19b | ||
|
|
fcb1d64eb9 | ||
|
|
0c2673ee2f | ||
|
|
74e6723ad9 | ||
|
|
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 | ||
|
|
2191fe465c | ||
|
|
b75e8913e0 | ||
|
|
87a1449f76 | ||
|
|
803e96800e | ||
|
|
6c69780615 | ||
|
|
d5bb566276 | ||
|
|
569a911455 | ||
|
|
39ff880506 | ||
|
|
c5b95f5a4b | ||
|
|
209157c9dd | ||
|
|
15f4aebcd5 | ||
|
|
2354c52b16 | ||
|
|
fb59d68edd | ||
|
|
227d0fa7dc | ||
|
|
7b854fb5ca | ||
|
|
7c1eff54fb | ||
|
|
f8b160595f | ||
|
|
c92fa6aa8a | ||
|
|
77acbc6814 | ||
|
|
81cb1e427f | ||
|
|
f6ba9604a7 | ||
|
|
9c6544ebfa | ||
|
|
b6eeccadeb | ||
|
|
37d14f942e | ||
|
|
4594ae474e | ||
|
|
f26e657577 | ||
|
|
e7b7479589 | ||
|
|
e1634076f2 | ||
|
|
d6df664102 | ||
|
|
50a5b36498 | ||
|
|
a25bfd264c | ||
|
|
4d6fe936ae | ||
|
|
ec9f3fa6ea | ||
|
|
8356ad97e4 | ||
|
|
bf51c38975 | ||
|
|
3df3c876cc | ||
|
|
f825e61b89 | ||
|
|
64cd62d6af | ||
|
|
68f07c5f9d | ||
|
|
7fb96ce2ba | ||
|
|
12687a1073 | ||
|
|
89de499198 | ||
|
|
4881362340 | ||
|
|
f31fd34ce0 | ||
|
|
18000ccf21 | ||
|
|
7776ec15b6 | ||
|
|
e4c7fca716 | ||
|
|
5b63bd9331 | ||
|
|
289f90bdbe | ||
|
|
26bcc9627d | ||
|
|
09a0df3a1f | ||
|
|
fe329892de | ||
|
|
2681332678 | ||
|
|
f994eb185f | ||
|
|
cc37535b2d | ||
|
|
55c23dec13 | ||
|
|
4dfc062abd | ||
|
|
ced334d13b | ||
|
|
0be21d90c1 | ||
|
|
521fbc44b4 | ||
|
|
361771c9bb | ||
|
|
fa45660b7d | ||
|
|
2e8f4ad6af | ||
|
|
18550ea80c | ||
|
|
1c1c0cc791 | ||
|
|
a0c0388dd9 | ||
|
|
789c1ab59d | ||
|
|
e8367894f2 | ||
|
|
8aae4f1b9d | ||
|
|
5850a7cd6b | ||
|
|
6c89ea7cee | ||
|
|
c62f262f63 | ||
|
|
798040b5b8 | ||
|
|
ba582d6ef4 | ||
|
|
bbf6f01d42 | ||
|
|
8a8f60d129 | ||
|
|
142abb2a4e | ||
|
|
b59409bec0 | ||
|
|
c66125114f | ||
|
|
edb7ec58c6 | ||
|
|
655c6b51fe | ||
|
|
0bd4cefad3 | ||
|
|
0952007805 | ||
|
|
9b1fb795d7 | ||
|
|
3040e5a7bb | ||
|
|
7612799ef6 | ||
|
|
3b82d55176 | ||
|
|
a6b8202cd4 | ||
|
|
cfc1bf10c9 | ||
|
|
c5fad6cca1 | ||
|
|
b8d7222423 | ||
|
|
7d1300ab66 | ||
|
|
16d7de5989 | ||
|
|
102c447fe3 | ||
|
|
d66665b96e | ||
|
|
088be6bf6a | ||
|
|
bd3cbfc1ad | ||
|
|
fddc4e00ca | ||
|
|
5f7eec5504 | ||
|
|
6b94c297b9 | ||
|
|
edeb25cab5 | ||
|
|
44688e8363 | ||
|
|
ca79760372 | ||
|
|
4a669032dc | ||
|
|
b53dd2ec90 | ||
|
|
5ae4ff9162 | ||
|
|
a0e14439cb | ||
|
|
ed394f5f9d | ||
|
|
11db6d4dcc | ||
|
|
10c6836263 | ||
|
|
4e03df5ea7 | ||
|
|
9b41131af8 | ||
|
|
fb34dac08d | ||
|
|
d3e3a91096 | ||
|
|
5f8503c62d | ||
|
|
b0e8321514 | ||
|
|
dd2f77ea0c | ||
|
|
6c7cff7de2 | ||
|
|
46f797c40d | ||
|
|
75b01e17bc | ||
|
|
8685436cbb | ||
|
|
834c3c5cc2 | ||
|
|
25a19b49ad | ||
|
|
a4d96bebfb | ||
|
|
d21d6d2085 | ||
|
|
26c38ffc8e | ||
|
|
237b8908f7 | ||
|
|
06bccef462 | ||
|
|
3120bb8fd7 | ||
|
|
0903ed8232 | ||
|
|
f8ba392a24 | ||
|
|
3dd384dd53 | ||
|
|
2c071a3283 | ||
|
|
596cd7e0b6 | ||
|
|
3f5c30e3b3 | ||
|
|
1a279c6053 | ||
|
|
67e3a17b28 | ||
|
|
24204feb71 | ||
|
|
4ace2638e1 | ||
|
|
5aa486d6c2 | ||
|
|
ba26d03b1b | ||
|
|
9a1c2c9b61 | ||
|
|
5b9db81819 | ||
|
|
f2ba7d7851 | ||
|
|
1eafdfcbc8 | ||
|
|
3d825c51dd | ||
|
|
915f882e1f | ||
|
|
103ea2f168 | ||
|
|
18d005d7e6 | ||
|
|
8791cd7851 | ||
|
|
590db89643 | ||
|
|
ea1d968777 | ||
|
|
40d728a14b | ||
|
|
e39b56547e | ||
|
|
a7f63d5783 | ||
|
|
4fef890466 | ||
|
|
35f5b7ec03 | ||
|
|
5136c8ba24 | ||
|
|
1037fa5622 | ||
|
|
8b42bf7a95 | ||
|
|
1c329d9ffa | ||
|
|
093a37a2b0 | ||
|
|
1daf5aad1f | ||
|
|
4dfcd61d46 | ||
|
|
9d560fe9e1 | ||
|
|
8e32d58077 | ||
|
|
7b24d31636 | ||
|
|
35d9e68053 | ||
|
|
fe3f14a63e | ||
|
|
caf2180075 | ||
|
|
236d2b92dc | ||
|
|
e6a2df5b6d | ||
|
|
f6bb1977bc | ||
|
|
9b0fbcf1d9 | ||
|
|
7574bfb7cb | ||
|
|
ce75bf4496 | ||
|
|
5ce47045e7 | ||
|
|
57e1725419 | ||
|
|
11309662a9 | ||
|
|
890357d579 | ||
|
|
f413c49555 | ||
|
|
c19f573b49 | ||
|
|
5de61b1a3d | ||
|
|
1c1462e776 | ||
|
|
eb6ef1cbea | ||
|
|
9654f5b218 | ||
|
|
68726a1b0e | ||
|
|
5b62bbe8e6 | ||
|
|
e55084629a | ||
|
|
1691e885f2 | ||
|
|
2d7818797d | ||
|
|
f65e2c639e | ||
|
|
95200e8f6b | ||
|
|
36e8dc74f4 | ||
|
|
78c5309e9a | ||
|
|
9feb1d378e | ||
|
|
e5e8683cdb | ||
|
|
d538ad170c | ||
|
|
c64c196778 | ||
|
|
8e552a9f0c | ||
|
|
a02017a5c8 | ||
|
|
0046d957f1 | ||
|
|
4a241deb96 | ||
|
|
8d5ae1d5d2 | ||
|
|
e1e89a5e62 | ||
|
|
a7be93449e | ||
|
|
c8694f9f2d | ||
|
|
062168cd42 | ||
|
|
1877a2c531 | ||
|
|
52f0e5a3db | ||
|
|
ac8c372349 | ||
|
|
1bfa429c38 | ||
|
|
ddd149945a | ||
|
|
e3dd8164a4 | ||
|
|
9b8149f14e | ||
|
|
05f1518951 | ||
|
|
e26de85b5f | ||
|
|
a2df80e833 | ||
|
|
db238ef524 | ||
|
|
f2b935f48f | ||
|
|
e69da71d4e | ||
|
|
7505fe7a7c | ||
|
|
f6857f1bcb | ||
|
|
7fe2c74139 | ||
|
|
be60f9612e | ||
|
|
2de9f015b1 | ||
|
|
c1f4f79d4a | ||
|
|
7b874cf597 | ||
|
|
8568b56ac6 | ||
|
|
f2a880f813 | ||
|
|
691327b2db | ||
|
|
a23c58c10a | ||
|
|
27c6b24e3a | ||
|
|
384436e937 | ||
|
|
eb30aae486 | ||
|
|
079286da04 | ||
|
|
0130899b3b | ||
|
|
d1f3c3c982 | ||
|
|
3b6eefa8bb | ||
|
|
5107531425 | ||
|
|
88655ffc44 | ||
|
|
10bd10b9d1 | ||
|
|
956a0f102b | ||
|
|
bdedd0e1fe | ||
|
|
4c901033b2 | ||
|
|
7d926da98c | ||
|
|
1b793d1f23 | ||
|
|
b5a8e8f51b | ||
|
|
cc5d00e211 | ||
|
|
1a8ab2aadc | ||
|
|
608fdc6f52 | ||
|
|
1d8638b47d | ||
|
|
3ecff48722 | ||
|
|
aa3b14ce72 | ||
|
|
28aeb0f09e | ||
|
|
7c5e2c5393 | ||
|
|
df8b629c2c | ||
|
|
a506dc6b65 | ||
|
|
fc1e6ccb8c | ||
|
|
bbc638ab82 | ||
|
|
4f57a2e248 | ||
|
|
4c6db2c5bd | ||
|
|
bbe548bc98 | ||
|
|
d1fbf65c5d | ||
|
|
7a4a915312 | ||
|
|
4f895f744b | ||
|
|
66a831dfa8 | ||
|
|
516597a73e | ||
|
|
4eb6c9fb8e | ||
|
|
46e2ae8860 | ||
|
|
54c0cbeb66 | ||
|
|
82ddf4732a | ||
|
|
ed0cdefb44 | ||
|
|
8836be0f47 | ||
|
|
96f63f3945 | ||
|
|
d80dcd6afd | ||
|
|
2087629a47 | ||
|
|
878d68c5ef | ||
|
|
86960cdb1d | ||
|
|
fff12979a2 | ||
|
|
6c12baf4ed | ||
|
|
29449a71d4 | ||
|
|
9b983b6487 | ||
|
|
806bfa54b5 | ||
|
|
920aeeeba5 | ||
|
|
32418448de | ||
|
|
b3525c2569 | ||
|
|
19dc2873c5 | ||
|
|
25b8d9b0ca | ||
|
|
8aef3c44f4 | ||
|
|
8345c21eff | ||
|
|
36b94cf823 | ||
|
|
475cfe4af2 | ||
|
|
b851b15a73 | ||
|
|
73347c2542 | ||
|
|
bc9023399d | ||
|
|
a9c9b96eb6 | ||
|
|
1c2a3c620f | ||
|
|
9313d04726 | ||
|
|
44518fea14 | ||
|
|
91049d0db3 | ||
|
|
855514b4f3 | ||
|
|
974741a366 | ||
|
|
5d98f7e307 | ||
|
|
3ca45ae99c | ||
|
|
cf574c71d8 | ||
|
|
abe0a34fc0 | ||
|
|
71b6508ad3 | ||
|
|
55fc4fcd90 | ||
|
|
c3b2b474c6 | ||
|
|
39716ed1ba | ||
|
|
625a529f6c | ||
|
|
31d56c16d5 | ||
|
|
5776385e8c | ||
|
|
8f10de5684 | ||
|
|
e864fcf9a8 | ||
|
|
86af5f5252 | ||
|
|
daa1d582cb | ||
|
|
f197f0e5ec | ||
|
|
3599ca6845 | ||
|
|
1be4fc5ae9 | ||
|
|
ac3e5684d6 | ||
|
|
29cca4d621 | ||
|
|
f3ff80963a | ||
|
|
45e428eb25 | ||
|
|
16d2650236 |
@@ -76,7 +76,7 @@ bool loopCanSleep()
|
||||
// Called just prior to starting Meshtastic. Allows for setting config values before startup.
|
||||
void lateInitVariant()
|
||||
{
|
||||
settingsMap[logoutputlevel] = level_error;
|
||||
portduino_config.logoutputlevel = level_error;
|
||||
channelFile.channels[0] = meshtastic_Channel{
|
||||
.has_settings = true,
|
||||
.settings =
|
||||
@@ -132,7 +132,7 @@ int portduino_main(int argc, char **argv); // Renamed "main" function from Mesht
|
||||
// Start Meshtastic in a thread and wait till it has reached the ON state.
|
||||
int LLVMFuzzerInitialize(int *argc, char ***argv)
|
||||
{
|
||||
settingsMap[maxtophone] = 5;
|
||||
portduino_config.maxtophone = 5;
|
||||
|
||||
meshtasticThread = std::thread([program = *argv[0]]() {
|
||||
char nodeIdStr[12];
|
||||
|
||||
9
.github/actions/setup-base/action.yml
vendored
9
.github/actions/setup-base/action.yml
vendored
@@ -5,17 +5,12 @@ runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
ref: ${{github.event.pull_request.head.ref}}
|
||||
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
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -23,7 +18,7 @@ runs:
|
||||
sudo apt-get install -y cppcheck libbluetooth-dev libgpiod-dev libyaml-cpp-dev lsb-release
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: 3.x
|
||||
cache: pip
|
||||
|
||||
2
.github/workflows/build_debian_src.yml
vendored
2
.github/workflows/build_debian_src.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
path: meshtasticd
|
||||
|
||||
40
.github/workflows/build_esp32.yml
vendored
40
.github/workflows/build_esp32.yml
vendored
@@ -1,40 +0,0 @@
|
||||
name: Build ESP32
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
board:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
build-esp32:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Get release version string
|
||||
shell: bash
|
||||
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
||||
id: version
|
||||
|
||||
- name: Build ESP32
|
||||
id: build
|
||||
uses: meshtastic/gh-action-firmware@main
|
||||
with:
|
||||
pio_platform: esp32
|
||||
pio_env: ${{ inputs.board }}
|
||||
pio_target: build
|
||||
ota_firmware_source: firmware.bin
|
||||
ota_firmware_target: release/bleota.bin
|
||||
|
||||
- name: Store binaries as an artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: firmware-esp32-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip
|
||||
overwrite: true
|
||||
path: |
|
||||
release/*.bin
|
||||
release/*.elf
|
||||
40
.github/workflows/build_esp32_c3.yml
vendored
40
.github/workflows/build_esp32_c3.yml
vendored
@@ -1,40 +0,0 @@
|
||||
name: Build ESP32-C3
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
board:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
build-esp32-c3:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Get release version string
|
||||
shell: bash
|
||||
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
||||
id: version
|
||||
|
||||
- name: Build ESP32-C3
|
||||
id: build
|
||||
uses: meshtastic/gh-action-firmware@main
|
||||
with:
|
||||
pio_platform: esp32
|
||||
pio_env: ${{ inputs.board }}
|
||||
pio_target: build
|
||||
ota_firmware_source: firmware-c3.bin
|
||||
ota_firmware_target: release/bleota-c3.bin
|
||||
|
||||
- name: Store binaries as an artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: firmware-esp32c3-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip
|
||||
overwrite: true
|
||||
path: |
|
||||
release/*.bin
|
||||
release/*.elf
|
||||
40
.github/workflows/build_esp32_c6.yml
vendored
40
.github/workflows/build_esp32_c6.yml
vendored
@@ -1,40 +0,0 @@
|
||||
name: Build ESP32-C6
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
board:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
build-esp32-c6:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Get release version string
|
||||
shell: bash
|
||||
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
||||
id: version
|
||||
|
||||
- name: Build ESP32-C6
|
||||
id: build
|
||||
uses: meshtastic/gh-action-firmware@main
|
||||
with:
|
||||
pio_platform: esp32
|
||||
pio_env: ${{ inputs.board }}
|
||||
pio_target: build
|
||||
ota_firmware_source: firmware-c3.bin
|
||||
ota_firmware_target: release/bleota-c3.bin
|
||||
|
||||
- name: Store binaries as an artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: firmware-esp32c6-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip
|
||||
overwrite: true
|
||||
path: |
|
||||
release/*.bin
|
||||
release/*.elf
|
||||
40
.github/workflows/build_esp32_s3.yml
vendored
40
.github/workflows/build_esp32_s3.yml
vendored
@@ -1,40 +0,0 @@
|
||||
name: Build ESP32-S3
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
board:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
build-esp32-s3:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Get release version string
|
||||
shell: bash
|
||||
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
||||
id: version
|
||||
|
||||
- name: Build ESP32-S3
|
||||
id: build
|
||||
uses: meshtastic/gh-action-firmware@main
|
||||
with:
|
||||
pio_platform: esp32
|
||||
pio_env: ${{ inputs.board }}
|
||||
pio_target: build
|
||||
ota_firmware_source: firmware-s3.bin
|
||||
ota_firmware_target: release/bleota-s3.bin
|
||||
|
||||
- name: Store binaries as an artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: firmware-esp32s3-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip
|
||||
overwrite: true
|
||||
path: |
|
||||
release/*.bin
|
||||
release/*.elf
|
||||
69
.github/workflows/build_firmware.yml
vendored
Normal file
69
.github/workflows/build_firmware.yml
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
version:
|
||||
required: true
|
||||
type: string
|
||||
platform:
|
||||
required: true
|
||||
type: string
|
||||
pio_env:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
pio-build:
|
||||
name: build-${{ inputs.platform }}
|
||||
runs-on: ubuntu-24.04
|
||||
outputs:
|
||||
artifact-id: ${{ steps.upload.outputs.artifact-id }}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
ref: ${{github.event.pull_request.head.ref}}
|
||||
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||
|
||||
- name: Set OTA firmware source and target
|
||||
if: startsWith(inputs.platform, 'esp32')
|
||||
id: ota_dir
|
||||
env:
|
||||
PIO_PLATFORM: ${{ inputs.platform }}
|
||||
run: |
|
||||
if [ "$PIO_PLATFORM" = "esp32s3" ]; then
|
||||
echo "src=firmware-s3.bin" >> $GITHUB_OUTPUT
|
||||
echo "tgt=release/bleota-s3.bin" >> $GITHUB_OUTPUT
|
||||
elif [ "$PIO_PLATFORM" = "esp32c3" ] || [ "$PIO_PLATFORM" = "esp32c6" ]; then
|
||||
echo "src=firmware-c3.bin" >> $GITHUB_OUTPUT
|
||||
echo "tgt=release/bleota-c3.bin" >> $GITHUB_OUTPUT
|
||||
elif [ "$PIO_PLATFORM" = "esp32" ]; then
|
||||
echo "src=firmware.bin" >> $GITHUB_OUTPUT
|
||||
echo "tgt=release/bleota.bin" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Build ${{ inputs.platform }}
|
||||
id: build
|
||||
uses: meshtastic/gh-action-firmware@main
|
||||
with:
|
||||
pio_platform: ${{ inputs.platform }}
|
||||
pio_env: ${{ inputs.pio_env }}
|
||||
pio_target: build
|
||||
ota_firmware_source: ${{ steps.ota_dir.outputs.src || '' }}
|
||||
ota_firmware_target: ${{ steps.ota_dir.outputs.tgt || '' }}
|
||||
|
||||
- name: Store binaries as an artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
id: upload
|
||||
with:
|
||||
name: firmware-${{ inputs.platform }}-${{ inputs.pio_env }}-${{ inputs.version }}.zip
|
||||
overwrite: true
|
||||
path: |
|
||||
release/*.bin
|
||||
release/*.elf
|
||||
release/*.uf2
|
||||
release/*.hex
|
||||
release/*-ota.zip
|
||||
40
.github/workflows/build_nrf52.yml
vendored
40
.github/workflows/build_nrf52.yml
vendored
@@ -1,40 +0,0 @@
|
||||
name: Build NRF52
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
board:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
build-nrf52:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Get release version string
|
||||
shell: bash
|
||||
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
||||
id: version
|
||||
|
||||
- name: Build NRF52
|
||||
id: build
|
||||
uses: meshtastic/gh-action-firmware@main
|
||||
with:
|
||||
pio_platform: nrf52
|
||||
pio_env: ${{ inputs.board }}
|
||||
pio_target: build
|
||||
|
||||
- name: Store binaries as an artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: firmware-nrf52840-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip
|
||||
overwrite: true
|
||||
path: |
|
||||
release/*.uf2
|
||||
release/*.elf
|
||||
release/*.hex
|
||||
release/*-ota.zip
|
||||
232
.github/workflows/build_one_arch.yml
vendored
Normal file
232
.github/workflows/build_one_arch.yml
vendored
Normal file
@@ -0,0 +1,232 @@
|
||||
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
|
||||
|
||||
docker-deb-amd64:
|
||||
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
|
||||
uses: ./.github/workflows/docker_build.yml
|
||||
with:
|
||||
distro: debian
|
||||
platform: linux/amd64
|
||||
runs-on: ubuntu-24.04
|
||||
push: false
|
||||
|
||||
docker-deb-amd64-tft:
|
||||
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
|
||||
uses: ./.github/workflows/docker_build.yml
|
||||
with:
|
||||
distro: debian
|
||||
platform: linux/amd64
|
||||
runs-on: ubuntu-24.04
|
||||
push: false
|
||||
pio_env: native-tft
|
||||
|
||||
docker-alp-amd64:
|
||||
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
|
||||
uses: ./.github/workflows/docker_build.yml
|
||||
with:
|
||||
distro: alpine
|
||||
platform: linux/amd64
|
||||
runs-on: ubuntu-24.04
|
||||
push: false
|
||||
|
||||
docker-alp-amd64-tft:
|
||||
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
|
||||
uses: ./.github/workflows/docker_build.yml
|
||||
with:
|
||||
distro: alpine
|
||||
platform: linux/amd64
|
||||
runs-on: ubuntu-24.04
|
||||
push: false
|
||||
pio_env: native-tft
|
||||
|
||||
docker-deb-arm64:
|
||||
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
|
||||
uses: ./.github/workflows/docker_build.yml
|
||||
with:
|
||||
distro: debian
|
||||
platform: linux/arm64
|
||||
runs-on: ubuntu-24.04-arm
|
||||
push: false
|
||||
|
||||
docker-deb-armv7:
|
||||
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
|
||||
uses: ./.github/workflows/docker_build.yml
|
||||
with:
|
||||
distro: debian
|
||||
platform: linux/arm/v7
|
||||
runs-on: ubuntu-24.04-arm
|
||||
push: false
|
||||
|
||||
gather-artifacts:
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arch:
|
||||
- esp32
|
||||
- esp32s3
|
||||
- esp32c3
|
||||
- esp32c6
|
||||
- nrf52840
|
||||
- rp2040
|
||||
- rp2350
|
||||
- stm32
|
||||
runs-on: ubuntu-latest
|
||||
needs: [version, build]
|
||||
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 }}
|
||||
238
.github/workflows/build_one_target.yml
vendored
Normal file
238
.github/workflows/build_one_target.yml
vendored
Normal file
@@ -0,0 +1,238 @@
|
||||
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
|
||||
|
||||
docker-deb-amd64:
|
||||
if: ${{ inputs.target != '' && inputs.arch == 'native' }}
|
||||
uses: ./.github/workflows/docker_build.yml
|
||||
with:
|
||||
distro: debian
|
||||
platform: linux/amd64
|
||||
runs-on: ubuntu-24.04
|
||||
push: false
|
||||
|
||||
docker-deb-amd64-tft:
|
||||
if: ${{ inputs.target != '' && inputs.arch == 'native' }}
|
||||
uses: ./.github/workflows/docker_build.yml
|
||||
with:
|
||||
distro: debian
|
||||
platform: linux/amd64
|
||||
runs-on: ubuntu-24.04
|
||||
push: false
|
||||
pio_env: native-tft
|
||||
|
||||
docker-alp-amd64:
|
||||
if: ${{ inputs.target != '' && inputs.arch == 'native' }}
|
||||
uses: ./.github/workflows/docker_build.yml
|
||||
with:
|
||||
distro: alpine
|
||||
platform: linux/amd64
|
||||
runs-on: ubuntu-24.04
|
||||
push: false
|
||||
|
||||
docker-alp-amd64-tft:
|
||||
if: ${{ inputs.target != '' && inputs.arch == 'native' }}
|
||||
uses: ./.github/workflows/docker_build.yml
|
||||
with:
|
||||
distro: alpine
|
||||
platform: linux/amd64
|
||||
runs-on: ubuntu-24.04
|
||||
push: false
|
||||
pio_env: native-tft
|
||||
|
||||
docker-deb-arm64:
|
||||
if: ${{ inputs.target != '' && inputs.arch == 'native' }}
|
||||
uses: ./.github/workflows/docker_build.yml
|
||||
with:
|
||||
distro: debian
|
||||
platform: linux/arm64
|
||||
runs-on: ubuntu-24.04-arm
|
||||
push: false
|
||||
|
||||
docker-deb-armv7:
|
||||
if: ${{ inputs.target != '' && inputs.arch == 'native' }}
|
||||
uses: ./.github/workflows/docker_build.yml
|
||||
with:
|
||||
distro: debian
|
||||
platform: linux/arm/v7
|
||||
runs-on: ubuntu-24.04-arm
|
||||
push: false
|
||||
|
||||
gather-artifacts:
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
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 }}
|
||||
38
.github/workflows/build_rpi2040.yml
vendored
38
.github/workflows/build_rpi2040.yml
vendored
@@ -1,38 +0,0 @@
|
||||
name: Build RPI2040
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
board:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
build-rpi2040:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Get release version string
|
||||
shell: bash
|
||||
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
||||
id: version
|
||||
|
||||
- name: Build Raspberry Pi 2040
|
||||
id: build
|
||||
uses: meshtastic/gh-action-firmware@main
|
||||
with:
|
||||
pio_platform: rp2xx0
|
||||
pio_env: ${{ inputs.board }}
|
||||
pio_target: build
|
||||
|
||||
- name: Store binaries as an artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: firmware-rp2040-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip
|
||||
overwrite: true
|
||||
path: |
|
||||
release/*.uf2
|
||||
release/*.elf
|
||||
39
.github/workflows/build_stm32.yml
vendored
39
.github/workflows/build_stm32.yml
vendored
@@ -1,39 +0,0 @@
|
||||
name: Build STM32
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
board:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
build-stm32:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Get release version string
|
||||
shell: bash
|
||||
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
||||
id: version
|
||||
|
||||
- name: Build STM32WL
|
||||
id: build
|
||||
uses: meshtastic/gh-action-firmware@main
|
||||
with:
|
||||
pio_platform: stm32wl
|
||||
pio_env: ${{ inputs.board }}
|
||||
pio_target: build
|
||||
|
||||
- name: Store binaries as an artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: firmware-stm32-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip
|
||||
overwrite: true
|
||||
path: |
|
||||
release/*.hex
|
||||
release/*.bin
|
||||
release/*.elf
|
||||
10
.github/workflows/daily_packaging.yml
vendored
10
.github/workflows/daily_packaging.yml
vendored
@@ -21,16 +21,22 @@ permissions:
|
||||
|
||||
jobs:
|
||||
docker-multiarch:
|
||||
if: github.repository == 'meshtastic/firmware'
|
||||
uses: ./.github/workflows/docker_manifest.yml
|
||||
with:
|
||||
release_channel: daily
|
||||
secrets: inherit
|
||||
|
||||
package-ppa:
|
||||
if: github.repository == 'meshtastic/firmware'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
series: [plucky, noble, jammy]
|
||||
series:
|
||||
- jammy # 22.04 LTS
|
||||
- noble # 24.04 LTS
|
||||
- plucky # 25.04
|
||||
- questing # 25.10
|
||||
uses: ./.github/workflows/package_ppa.yml
|
||||
with:
|
||||
ppa_repo: ppa:meshtastic/daily
|
||||
@@ -38,6 +44,7 @@ jobs:
|
||||
secrets: inherit
|
||||
|
||||
package-obs:
|
||||
if: github.repository == 'meshtastic/firmware'
|
||||
uses: ./.github/workflows/package_obs.yml
|
||||
with:
|
||||
obs_project: network:Meshtastic:daily
|
||||
@@ -45,6 +52,7 @@ jobs:
|
||||
secrets: inherit
|
||||
|
||||
hook-copr:
|
||||
if: github.repository == 'meshtastic/firmware'
|
||||
uses: ./.github/workflows/hook_copr.yml
|
||||
with:
|
||||
copr_project: daily
|
||||
|
||||
2
.github/workflows/docker_build.yml
vendored
2
.github/workflows/docker_build.yml
vendored
@@ -47,7 +47,7 @@ jobs:
|
||||
runs-on: ${{ inputs.runs-on }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
ref: ${{github.event.pull_request.head.ref}}
|
||||
|
||||
2
.github/workflows/docker_manifest.yml
vendored
2
.github/workflows/docker_manifest.yml
vendored
@@ -83,7 +83,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
ref: ${{github.event.pull_request.head.ref}}
|
||||
|
||||
2
.github/workflows/hook_copr.yml
vendored
2
.github/workflows/hook_copr.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
ref: ${{ github.ref }}
|
||||
|
||||
285
.github/workflows/main_matrix.yml
vendored
285
.github/workflows/main_matrix.yml
vendored
@@ -3,7 +3,7 @@ concurrency:
|
||||
group: ci-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
on:
|
||||
# # Triggers the workflow on push but only for the master branch
|
||||
# # Triggers the workflow on push but only for the main branches
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
@@ -27,112 +27,84 @@ on:
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
if: github.repository == 'meshtastic/firmware'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
fail-fast: true
|
||||
matrix:
|
||||
arch: [esp32, esp32s3, esp32c3, esp32c6, nrf52840, rp2040, stm32, check]
|
||||
runs-on: ubuntu-latest
|
||||
arch:
|
||||
- all
|
||||
- check
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- id: checkout
|
||||
uses: actions/checkout@v4
|
||||
name: Checkout base
|
||||
- id: jsonStep
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: 3.x
|
||||
cache: pip
|
||||
- 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
|
||||
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}} quick)
|
||||
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}} --level pr)
|
||||
fi
|
||||
echo "Name: $GITHUB_REF_NAME Base: $GITHUB_BASE_REF Ref: $GITHUB_REF Targets: $TARGETS"
|
||||
echo "${{matrix.arch}}=$(jq -cn --argjson environments "$TARGETS" '{board: $environments}')" >> $GITHUB_OUTPUT
|
||||
echo "Name: $GITHUB_REF_NAME Base: $GITHUB_BASE_REF Ref: $GITHUB_REF"
|
||||
echo "${{matrix.arch}}=$TARGETS" >> $GITHUB_OUTPUT
|
||||
echo "$TARGETS" >> $GITHUB_STEP_SUMMARY
|
||||
outputs:
|
||||
esp32: ${{ steps.jsonStep.outputs.esp32 }}
|
||||
esp32s3: ${{ steps.jsonStep.outputs.esp32s3 }}
|
||||
esp32c3: ${{ steps.jsonStep.outputs.esp32c3 }}
|
||||
esp32c6: ${{ steps.jsonStep.outputs.esp32c6 }}
|
||||
nrf52840: ${{ steps.jsonStep.outputs.nrf52840 }}
|
||||
rp2040: ${{ steps.jsonStep.outputs.rp2040 }}
|
||||
stm32: ${{ steps.jsonStep.outputs.stm32 }}
|
||||
all: ${{ steps.jsonStep.outputs.all }}
|
||||
check: ${{ steps.jsonStep.outputs.check }}
|
||||
|
||||
version:
|
||||
if: github.repository == 'meshtastic/firmware'
|
||||
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: false
|
||||
matrix: ${{ fromJson(needs.setup.outputs.check) }}
|
||||
matrix:
|
||||
check: ${{ fromJson(needs.setup.outputs.check) }}
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name != 'workflow_dispatch' }}
|
||||
if: ${{ github.event_name != 'workflow_dispatch' && github.repository == 'meshtastic/firmware' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Build base
|
||||
id: base
|
||||
uses: ./.github/actions/setup-base
|
||||
- name: Check ${{ matrix.board }}
|
||||
run: bin/check-all.sh ${{ matrix.board }}
|
||||
- name: Check ${{ matrix.check.board }}
|
||||
run: bin/check-all.sh ${{ matrix.check.board }}
|
||||
|
||||
build-esp32:
|
||||
needs: setup
|
||||
build:
|
||||
needs: [setup, version]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.setup.outputs.esp32) }}
|
||||
uses: ./.github/workflows/build_esp32.yml
|
||||
matrix:
|
||||
build: ${{ fromJson(needs.setup.outputs.all) }}
|
||||
uses: ./.github/workflows/build_firmware.yml
|
||||
with:
|
||||
board: ${{ matrix.board }}
|
||||
|
||||
build-esp32-s3:
|
||||
needs: setup
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.setup.outputs.esp32s3) }}
|
||||
uses: ./.github/workflows/build_esp32_s3.yml
|
||||
with:
|
||||
board: ${{ matrix.board }}
|
||||
|
||||
build-esp32-c3:
|
||||
needs: setup
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.setup.outputs.esp32c3) }}
|
||||
uses: ./.github/workflows/build_esp32_c3.yml
|
||||
with:
|
||||
board: ${{ matrix.board }}
|
||||
|
||||
build-esp32-c6:
|
||||
needs: setup
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.setup.outputs.esp32c6) }}
|
||||
uses: ./.github/workflows/build_esp32_c6.yml
|
||||
with:
|
||||
board: ${{ matrix.board }}
|
||||
|
||||
build-nrf52:
|
||||
needs: setup
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.setup.outputs.nrf52840) }}
|
||||
uses: ./.github/workflows/build_nrf52.yml
|
||||
with:
|
||||
board: ${{ matrix.board }}
|
||||
|
||||
build-rpi2040:
|
||||
needs: setup
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.setup.outputs.rp2040) }}
|
||||
uses: ./.github/workflows/build_rpi2040.yml
|
||||
with:
|
||||
board: ${{ matrix.board }}
|
||||
|
||||
build-stm32:
|
||||
needs: setup
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.setup.outputs.stm32) }}
|
||||
uses: ./.github/workflows/build_stm32.yml
|
||||
with:
|
||||
board: ${{ matrix.board }}
|
||||
version: ${{ needs.version.outputs.long }}
|
||||
pio_env: ${{ matrix.build.board }}
|
||||
platform: ${{ matrix.build.platform }}
|
||||
|
||||
build-debian-src:
|
||||
if: github.repository == 'meshtastic/firmware'
|
||||
@@ -143,17 +115,18 @@ jobs:
|
||||
secrets: inherit
|
||||
|
||||
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
|
||||
with:
|
||||
pio_env: native-tft
|
||||
secrets: inherit
|
||||
|
||||
test-native:
|
||||
if: ${{ !contains(github.ref_name, 'event/') }}
|
||||
if: ${{ !contains(github.ref_name, 'event/') && github.repository == 'meshtastic/firmware' }}
|
||||
uses: ./.github/workflows/test_native.yml
|
||||
|
||||
docker-deb-amd64:
|
||||
if: github.repository == 'meshtastic/firmware'
|
||||
uses: ./.github/workflows/docker_build.yml
|
||||
with:
|
||||
distro: debian
|
||||
@@ -162,6 +135,7 @@ jobs:
|
||||
push: false
|
||||
|
||||
docker-deb-amd64-tft:
|
||||
if: github.repository == 'meshtastic/firmware'
|
||||
uses: ./.github/workflows/docker_build.yml
|
||||
with:
|
||||
distro: debian
|
||||
@@ -171,6 +145,7 @@ jobs:
|
||||
pio_env: native-tft
|
||||
|
||||
docker-alp-amd64:
|
||||
if: github.repository == 'meshtastic/firmware'
|
||||
uses: ./.github/workflows/docker_build.yml
|
||||
with:
|
||||
distro: alpine
|
||||
@@ -179,6 +154,7 @@ jobs:
|
||||
push: false
|
||||
|
||||
docker-alp-amd64-tft:
|
||||
if: github.repository == 'meshtastic/firmware'
|
||||
uses: ./.github/workflows/docker_build.yml
|
||||
with:
|
||||
distro: alpine
|
||||
@@ -188,6 +164,7 @@ jobs:
|
||||
pio_env: native-tft
|
||||
|
||||
docker-deb-arm64:
|
||||
if: github.repository == 'meshtastic/firmware'
|
||||
uses: ./.github/workflows/docker_build.yml
|
||||
with:
|
||||
distro: debian
|
||||
@@ -196,6 +173,7 @@ jobs:
|
||||
push: false
|
||||
|
||||
docker-deb-armv7:
|
||||
if: github.repository == 'meshtastic/firmware'
|
||||
uses: ./.github/workflows/docker_build.yml
|
||||
with:
|
||||
distro: debian
|
||||
@@ -204,32 +182,33 @@ jobs:
|
||||
push: false
|
||||
|
||||
gather-artifacts:
|
||||
# trunk-ignore(checkov/CKV2_GHA_1)
|
||||
if: github.repository == 'meshtastic/firmware'
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arch: [esp32, esp32s3, esp32c3, esp32c6, nrf52840, rp2040, stm32]
|
||||
arch:
|
||||
- esp32
|
||||
- esp32s3
|
||||
- esp32c3
|
||||
- esp32c6
|
||||
- nrf52840
|
||||
- rp2040
|
||||
- rp2350
|
||||
- stm32
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
[
|
||||
build-esp32,
|
||||
build-esp32-s3,
|
||||
build-esp32-c3,
|
||||
build-esp32-c6,
|
||||
build-nrf52,
|
||||
build-rpi2040,
|
||||
build-stm32,
|
||||
]
|
||||
needs: [version, build]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
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@v4
|
||||
- uses: actions/download-artifact@v5
|
||||
with:
|
||||
path: ./
|
||||
pattern: firmware-${{matrix.arch}}-*
|
||||
@@ -238,17 +217,13 @@ jobs:
|
||||
- name: Display structure of downloaded files
|
||||
run: ls -R
|
||||
|
||||
- name: Get release version string
|
||||
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
||||
id: version
|
||||
|
||||
- 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}}-${{ steps.version.outputs.long }}
|
||||
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||
overwrite: true
|
||||
path: |
|
||||
./firmware-*.bin
|
||||
@@ -262,9 +237,9 @@ jobs:
|
||||
./Meshtastic_nRF52_factory_erase*.uf2
|
||||
retention-days: 30
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}
|
||||
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||
merge-multiple: true
|
||||
path: ./output
|
||||
|
||||
@@ -278,12 +253,12 @@ jobs:
|
||||
chmod +x ./output/device-update.sh
|
||||
|
||||
- name: Zip firmware
|
||||
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip ./output
|
||||
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
||||
|
||||
- name: Repackage in single elfs zip
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip
|
||||
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
|
||||
overwrite: true
|
||||
path: ./*.elf
|
||||
retention-days: 30
|
||||
@@ -291,66 +266,59 @@ jobs:
|
||||
- uses: scruplelesswizard/comment-artifact@main
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
with:
|
||||
name: firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}
|
||||
description: "Download firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip. This artifact will be available for 90 days from creation"
|
||||
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' }}
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && github.repository == 'meshtastic/firmware' }}
|
||||
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@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: 3.x
|
||||
|
||||
- name: Get release version string
|
||||
run: |
|
||||
echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
||||
echo "deb=$(./bin/buildinfo.py deb)" >> $GITHUB_OUTPUT
|
||||
id: version
|
||||
env:
|
||||
BUILD_LOCATION: local
|
||||
|
||||
- name: Create release
|
||||
uses: softprops/action-gh-release@v2
|
||||
id: create_release
|
||||
with:
|
||||
draft: true
|
||||
prerelease: true
|
||||
name: Meshtastic Firmware ${{ steps.version.outputs.long }} Alpha
|
||||
tag_name: v${{ steps.version.outputs.long }}
|
||||
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@v4
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
pattern: firmware-debian-${{ steps.version.outputs.deb }}~UNRELEASED-src
|
||||
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@v4
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
pattern: platformio-deps-native-tft-${{ steps.version.outputs.long }}
|
||||
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-${{ steps.version.outputs.deb }}-src.zip ./debian-src
|
||||
zip -9 -r ./platformio-deps-native-tft-${{ steps.version.outputs.long }}.zip ./pio-deps-native-tft
|
||||
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
|
||||
@@ -360,8 +328,8 @@ jobs:
|
||||
# Only run when targeting master branch with workflow_dispatch
|
||||
if: ${{ github.ref_name == 'master' }}
|
||||
run: |
|
||||
gh release upload v${{ steps.version.outputs.long }} ./output/meshtasticd-${{ steps.version.outputs.deb }}-src.zip
|
||||
gh release upload v${{ steps.version.outputs.long }} ./output/platformio-deps-native-tft-${{ steps.version.outputs.long }}.zip
|
||||
gh release upload v${{ needs.version.outputs.long }} ./output/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 }}
|
||||
|
||||
@@ -369,26 +337,30 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arch: [esp32, esp32s3, esp32c3, esp32c6, nrf52840, rp2040, stm32]
|
||||
arch:
|
||||
- esp32
|
||||
- esp32s3
|
||||
- esp32c3
|
||||
- esp32c6
|
||||
- nrf52840
|
||||
- rp2040
|
||||
- rp2350
|
||||
- stm32
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||
needs: [release-artifacts]
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && github.repository == 'meshtastic/firmware'}}
|
||||
needs: [release-artifacts, version]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: 3.x
|
||||
|
||||
- name: Get release version string
|
||||
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
||||
id: version
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@v5
|
||||
with:
|
||||
pattern: firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}
|
||||
pattern: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||
merge-multiple: true
|
||||
path: ./output
|
||||
|
||||
@@ -401,16 +373,16 @@ jobs:
|
||||
chmod +x ./output/device-update.sh
|
||||
|
||||
- name: Zip firmware
|
||||
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip ./output
|
||||
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip
|
||||
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}}-${{ steps.version.outputs.long }}.zip ./elfs
|
||||
run: zip -j -9 -r ./debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./elfs
|
||||
|
||||
# For diagnostics
|
||||
- name: Display structure of downloaded files
|
||||
@@ -420,33 +392,30 @@ jobs:
|
||||
# Only run when targeting master branch with workflow_dispatch
|
||||
if: ${{ github.ref_name == 'master' }}
|
||||
run: |
|
||||
gh release upload v${{ steps.version.outputs.long }} ./firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip
|
||||
gh release upload v${{ steps.version.outputs.long }} ./debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip
|
||||
gh release upload v${{ needs.version.outputs.long }} ./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]
|
||||
needs: [release-firmware, version]
|
||||
env:
|
||||
targets: esp32,esp32s3,esp32c3,esp32c6,nrf52840,rp2040,stm32
|
||||
targets: |-
|
||||
esp32,esp32s3,esp32c3,esp32c6,nrf52840,rp2040,rp2350,stm32
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: 3.x
|
||||
|
||||
- name: Get release version string
|
||||
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
||||
id: version
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@v5
|
||||
with:
|
||||
pattern: firmware-{${{ env.targets }}}-${{ steps.version.outputs.long }}
|
||||
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
|
||||
merge-multiple: true
|
||||
path: ./publish
|
||||
|
||||
@@ -460,9 +429,9 @@ jobs:
|
||||
external_repository: meshtastic/meshtastic.github.io
|
||||
publish_branch: master
|
||||
publish_dir: ./publish
|
||||
destination_dir: ${{ env.DEST_PREFIX }}firmware-${{ steps.version.outputs.long }}
|
||||
destination_dir: ${{ env.DEST_PREFIX }}firmware-${{ needs.version.outputs.long }}
|
||||
keep_files: true
|
||||
user_name: github-actions[bot]
|
||||
user_email: github-actions[bot]@users.noreply.github.com
|
||||
commit_message: ${{ steps.version.outputs.long }}
|
||||
commit_message: ${{ needs.version.outputs.long }}
|
||||
enable_jekyll: true
|
||||
|
||||
404
.github/workflows/merge_queue.yml
vendored
Normal file
404
.github/workflows/merge_queue.yml
vendored
Normal file
@@ -0,0 +1,404 @@
|
||||
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-deb-amd64:
|
||||
uses: ./.github/workflows/docker_build.yml
|
||||
with:
|
||||
distro: debian
|
||||
platform: linux/amd64
|
||||
runs-on: ubuntu-24.04
|
||||
push: false
|
||||
|
||||
docker-deb-amd64-tft:
|
||||
uses: ./.github/workflows/docker_build.yml
|
||||
with:
|
||||
distro: debian
|
||||
platform: linux/amd64
|
||||
runs-on: ubuntu-24.04
|
||||
push: false
|
||||
pio_env: native-tft
|
||||
|
||||
docker-alp-amd64:
|
||||
uses: ./.github/workflows/docker_build.yml
|
||||
with:
|
||||
distro: alpine
|
||||
platform: linux/amd64
|
||||
runs-on: ubuntu-24.04
|
||||
push: false
|
||||
|
||||
docker-alp-amd64-tft:
|
||||
uses: ./.github/workflows/docker_build.yml
|
||||
with:
|
||||
distro: alpine
|
||||
platform: linux/amd64
|
||||
runs-on: ubuntu-24.04
|
||||
push: false
|
||||
pio_env: native-tft
|
||||
|
||||
docker-deb-arm64:
|
||||
uses: ./.github/workflows/docker_build.yml
|
||||
with:
|
||||
distro: debian
|
||||
platform: linux/arm64
|
||||
runs-on: ubuntu-24.04-arm
|
||||
push: false
|
||||
|
||||
docker-deb-armv7:
|
||||
uses: ./.github/workflows/docker_build.yml
|
||||
with:
|
||||
distro: debian
|
||||
platform: linux/arm/v7
|
||||
runs-on: ubuntu-24.04-arm
|
||||
push: false
|
||||
|
||||
gather-artifacts:
|
||||
# trunk-ignore(checkov/CKV2_GHA_1)
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arch:
|
||||
- esp32
|
||||
- esp32s3
|
||||
- esp32c3
|
||||
- esp32c6
|
||||
- nrf52840
|
||||
- rp2040
|
||||
- rp2350
|
||||
- stm32
|
||||
runs-on: ubuntu-latest
|
||||
needs: [version, build]
|
||||
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
|
||||
4
.github/workflows/nightly.yml
vendored
4
.github/workflows/nightly.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Trunk Check
|
||||
uses: trunk-io/trunk-action@v1
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
pull-requests: write # For trunk to create PRs
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Trunk Upgrade
|
||||
uses: trunk-io/trunk-action/upgrade@v1
|
||||
|
||||
4
.github/workflows/package_obs.yml
vendored
4
.github/workflows/package_obs.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
||||
needs: build-debian-src
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
path: meshtasticd
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
id: version
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
|
||||
merge-multiple: true
|
||||
|
||||
4
.github/workflows/package_pio_deps.yml
vendored
4
.github/workflows/package_pio_deps.yml
vendored
@@ -24,14 +24,14 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
ref: ${{github.event.pull_request.head.ref}}
|
||||
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: 3.x
|
||||
|
||||
|
||||
4
.github/workflows/package_ppa.yml
vendored
4
.github/workflows/package_ppa.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
||||
needs: build-debian-src
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
path: meshtasticd
|
||||
@@ -60,7 +60,7 @@ jobs:
|
||||
id: version
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
|
||||
merge-multiple: true
|
||||
|
||||
24
.github/workflows/pr_enforce_labels.yml
vendored
Normal file
24
.github/workflows/pr_enforce_labels.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
name: Check PR Labels
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, edited, labeled, unlabeled, synchronize, reopened]
|
||||
|
||||
permissions:
|
||||
pull-requests: read
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
check-label:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check for PR labels
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const labels = context.payload.pull_request.labels.map(label => label.name);
|
||||
const requiredLabels = ['bugfix', 'enhancement', 'hardware-support', 'dependencies', 'submodules', 'github_actions', 'trunk'];
|
||||
const hasRequiredLabel = labels.some(label => requiredLabels.includes(label));
|
||||
if (!hasRequiredLabel) {
|
||||
core.setFailed(`PR must have at least one of the following labels before it can be merged: ${requiredLabels.join(', ')}.`);
|
||||
}
|
||||
238
.github/workflows/pr_tests.yml
vendored
Normal file
238
.github/workflows/pr_tests.yml
vendored
Normal file
@@ -0,0 +1,238 @@
|
||||
name: Tests
|
||||
|
||||
# DISABLED: Changed from automatic PR triggers to manual only
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
reason:
|
||||
description: "Reason for manual test run"
|
||||
required: false
|
||||
default: "Manual test execution"
|
||||
|
||||
concurrency:
|
||||
group: tests-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
checks: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
native-tests:
|
||||
name: "🧪 Native Tests"
|
||||
if: github.repository == 'meshtastic/firmware'
|
||||
uses: ./.github/workflows/test_native.yml
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
checks: write
|
||||
|
||||
test-summary:
|
||||
name: "📊 Test Results"
|
||||
runs-on: ubuntu-latest
|
||||
needs: [native-tests]
|
||||
if: always()
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
checks: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Get release version string
|
||||
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
||||
id: version
|
||||
|
||||
- name: Download test artifacts
|
||||
if: needs.native-tests.result != 'skipped'
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: platformio-test-report-${{ steps.version.outputs.long }}.zip
|
||||
merge-multiple: true
|
||||
|
||||
- name: Parse test results and create detailed summary
|
||||
id: test-results
|
||||
run: |
|
||||
echo "## 🧪 Test Results Summary" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Check overall job status first
|
||||
if [[ "${{ needs.native-tests.result }}" == "success" ]]; then
|
||||
echo "✅ **Overall Status**: PASSED" >> $GITHUB_STEP_SUMMARY
|
||||
elif [[ "${{ needs.native-tests.result }}" == "failure" ]]; then
|
||||
echo "❌ **Overall Status**: FAILED" >> $GITHUB_STEP_SUMMARY
|
||||
elif [[ "${{ needs.native-tests.result }}" == "cancelled" ]]; then
|
||||
echo "⏸️ **Overall Status**: CANCELLED" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Tests were cancelled before completion." >> $GITHUB_STEP_SUMMARY
|
||||
exit 0
|
||||
else
|
||||
echo "⚠️ **Overall Status**: SKIPPED" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Tests were skipped." >> $GITHUB_STEP_SUMMARY
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Parse detailed test results if available
|
||||
if [ -f "testreport.xml" ]; then
|
||||
echo "### 🔍 Individual Test Results" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
python3 << 'EOF'
|
||||
import xml.etree.ElementTree as ET
|
||||
import os
|
||||
|
||||
try:
|
||||
tree = ET.parse('testreport.xml')
|
||||
root = tree.getroot()
|
||||
|
||||
total_tests = 0
|
||||
passed_tests = 0
|
||||
failed_tests = 0
|
||||
skipped_tests = 0
|
||||
|
||||
# Parse testsuite elements
|
||||
for testsuite in root.findall('.//testsuite'):
|
||||
suite_name = testsuite.get('name', 'Unknown')
|
||||
suite_tests = int(testsuite.get('tests', '0'))
|
||||
suite_failures = int(testsuite.get('failures', '0'))
|
||||
suite_errors = int(testsuite.get('errors', '0'))
|
||||
suite_skipped = int(testsuite.get('skipped', '0'))
|
||||
|
||||
total_tests += suite_tests
|
||||
failed_tests += suite_failures + suite_errors
|
||||
skipped_tests += suite_skipped
|
||||
passed_tests += suite_tests - suite_failures - suite_errors - suite_skipped
|
||||
|
||||
if suite_tests > 0:
|
||||
status = "✅" if (suite_failures + suite_errors) == 0 else "❌"
|
||||
print(f"**{status} Test Suite: {suite_name}**")
|
||||
print(f"- Total: {suite_tests}")
|
||||
print(f"- Passed: ✅ {suite_tests - suite_failures - suite_errors - suite_skipped}")
|
||||
print(f"- Failed: ❌ {suite_failures + suite_errors}")
|
||||
if suite_skipped > 0:
|
||||
print(f"- Skipped: ⏭️ {suite_skipped}")
|
||||
print("")
|
||||
|
||||
# Show individual test results for failed suites
|
||||
if suite_failures + suite_errors > 0:
|
||||
print("**Failed Tests:**")
|
||||
for testcase in testsuite.findall('testcase'):
|
||||
test_name = testcase.get('name', 'Unknown')
|
||||
failure = testcase.find('failure')
|
||||
error = testcase.find('error')
|
||||
|
||||
if failure is not None:
|
||||
msg = failure.get('message', 'Unknown error')[:100]
|
||||
print(f"- ❌ `{test_name}`: {msg}")
|
||||
elif error is not None:
|
||||
msg = error.get('message', 'Unknown error')[:100]
|
||||
print(f"- ❌ `{test_name}`: ERROR - {msg}")
|
||||
print("")
|
||||
else:
|
||||
# Show passed tests for successful suites
|
||||
passed_count = 0
|
||||
for testcase in testsuite.findall('testcase'):
|
||||
if testcase.find('failure') is None and testcase.find('error') is None:
|
||||
if passed_count < 5: # Limit to first 5 to avoid spam
|
||||
test_name = testcase.get('name', 'Unknown')
|
||||
print(f"- ✅ `{test_name}`: PASSED")
|
||||
passed_count += 1
|
||||
if passed_count > 5:
|
||||
print(f"- ... and {passed_count - 5} more tests passed")
|
||||
print("")
|
||||
|
||||
# Summary statistics
|
||||
print("### 📊 Test Statistics")
|
||||
print(f"- **Total Tests**: {total_tests}")
|
||||
print(f"- **Passed**: ✅ {passed_tests}")
|
||||
print(f"- **Failed**: ❌ {failed_tests}")
|
||||
if skipped_tests > 0:
|
||||
print(f"- **Skipped**: ⏭️ {skipped_tests}")
|
||||
|
||||
if failed_tests > 0:
|
||||
print(f"\n❌ **{failed_tests} tests failed out of {total_tests} total**")
|
||||
else:
|
||||
print(f"\n✅ **All {total_tests} tests passed!**")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error parsing test results: {e}")
|
||||
EOF
|
||||
else
|
||||
echo "⚠️ **No detailed test report available**" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Test artifacts may not have been generated properly." >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "---" >> $GITHUB_STEP_SUMMARY
|
||||
echo "View detailed logs in the [Actions tab](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Comment test results on PR
|
||||
if: github.event_name == 'pull_request' && needs.native-tests.result != 'skipped'
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
|
||||
// Read the step summary to use as PR comment
|
||||
let testSummary = "## 🧪 Test Results Summary\n\n";
|
||||
|
||||
if ("${{ needs.native-tests.result }}" === "success") {
|
||||
testSummary += "✅ **All tests passed!**\n\n";
|
||||
} else if ("${{ needs.native-tests.result }}" === "failure") {
|
||||
testSummary += "❌ **Some tests failed.**\n\n";
|
||||
} else {
|
||||
testSummary += "⚠️ **Tests did not complete normally.**\n\n";
|
||||
}
|
||||
|
||||
testSummary += `View detailed results: [Actions Run](${context.payload.repository.html_url}/actions/runs/${context.runId})\n\n`;
|
||||
testSummary += "---\n";
|
||||
testSummary += "*This comment will be automatically updated when new commits are pushed.*";
|
||||
|
||||
// Find existing comment
|
||||
const comments = await github.rest.issues.listComments({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number
|
||||
});
|
||||
|
||||
const botComment = comments.data.find(comment =>
|
||||
comment.user.type === 'Bot' &&
|
||||
comment.body.includes('🧪 Test Results Summary')
|
||||
);
|
||||
|
||||
if (botComment) {
|
||||
// Update existing comment
|
||||
await github.rest.issues.updateComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: botComment.id,
|
||||
body: testSummary
|
||||
});
|
||||
} else {
|
||||
// Create new comment
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
body: testSummary
|
||||
});
|
||||
}
|
||||
|
||||
- name: Set overall status
|
||||
run: |
|
||||
if [[ "${{ needs.native-tests.result }}" == "success" ]]; then
|
||||
echo "All tests passed! ✅"
|
||||
exit 0
|
||||
else
|
||||
echo "Some tests failed! ❌"
|
||||
exit 1
|
||||
fi
|
||||
13
.github/workflows/release_channels.yml
vendored
13
.github/workflows/release_channels.yml
vendored
@@ -20,7 +20,11 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
series: [plucky, noble, jammy]
|
||||
series:
|
||||
- jammy # 22.04 LTS
|
||||
- noble # 24.04 LTS
|
||||
- plucky # 25.04
|
||||
- questing # 25.10
|
||||
uses: ./.github/workflows/package_ppa.yml
|
||||
with:
|
||||
ppa_repo: |-
|
||||
@@ -56,10 +60,10 @@ jobs:
|
||||
shell: bash
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: 3.x
|
||||
|
||||
@@ -99,8 +103,9 @@ jobs:
|
||||
with:
|
||||
base: ${{ github.event.repository.default_branch }}
|
||||
branch: create-pull-request/bump-version
|
||||
labels: github_actions
|
||||
title: Bump release version
|
||||
commit-message: automated bumps
|
||||
commit-message: Automated version bumps
|
||||
add-paths: |
|
||||
version.properties
|
||||
debian/changelog
|
||||
|
||||
4
.github/workflows/sec_sast_semgrep_cron.yml
vendored
4
.github/workflows/sec_sast_semgrep_cron.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
steps:
|
||||
# step 1
|
||||
- name: clone application source code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
# step 2
|
||||
- name: full scan
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
|
||||
# step 4
|
||||
- name: publish code scanning alerts
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
uses: github/codeql-action/upload-sarif@v4
|
||||
with:
|
||||
sarif_file: report.sarif
|
||||
category: semgrep
|
||||
|
||||
2
.github/workflows/sec_sast_semgrep_pull.yml
vendored
2
.github/workflows/sec_sast_semgrep_pull.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
steps:
|
||||
# step 1
|
||||
- name: clone application source code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
2
.github/workflows/stale_bot.yml
vendored
2
.github/workflows/stale_bot.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Stale PR+Issues
|
||||
uses: actions/stale@v9.1.0
|
||||
uses: actions/stale@v10.1.0
|
||||
with:
|
||||
days-before-stale: 45
|
||||
exempt-issue-labels: pinned,3.0
|
||||
|
||||
12
.github/workflows/test_native.yml
vendored
12
.github/workflows/test_native.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
name: Native Simulator Tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
ref: ${{github.event.pull_request.head.ref}}
|
||||
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
|
||||
- name: Integration test
|
||||
run: |
|
||||
.pio/build/coverage/program &
|
||||
.pio/build/coverage/program -s &
|
||||
PID=$!
|
||||
timeout 20 bash -c "until ls -al /proc/$PID/fd | grep socket; do sleep 1; done"
|
||||
echo "Simulator started, launching python test..."
|
||||
@@ -70,7 +70,7 @@ jobs:
|
||||
name: Native PlatformIO Tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
ref: ${{github.event.pull_request.head.ref}}
|
||||
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||
@@ -127,7 +127,7 @@ jobs:
|
||||
- platformio-tests
|
||||
if: always()
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
ref: ${{github.event.pull_request.head.ref}}
|
||||
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||
@@ -137,7 +137,7 @@ jobs:
|
||||
id: version
|
||||
|
||||
- name: Download test artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: platformio-test-report-${{ steps.version.outputs.long }}.zip
|
||||
merge-multiple: true
|
||||
@@ -150,7 +150,7 @@ jobs:
|
||||
reporter: java-junit
|
||||
|
||||
- name: Download coverage artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
pattern: lcov-coverage-info-native-*-${{ steps.version.outputs.long }}.zip
|
||||
path: code-coverage-report
|
||||
|
||||
4
.github/workflows/tests.yml
vendored
4
.github/workflows/tests.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
runs-on: test-runner
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
# - uses: actions/setup-python@v5
|
||||
# with:
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
pio upgrade
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
|
||||
2
.github/workflows/trunk_annotate_pr.yml
vendored
2
.github/workflows/trunk_annotate_pr.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Trunk Check
|
||||
uses: trunk-io/trunk-action@v1
|
||||
|
||||
2
.github/workflows/trunk_check.yml
vendored
2
.github/workflows/trunk_check.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Trunk Check
|
||||
uses: trunk-io/trunk-action@v1
|
||||
|
||||
4
.github/workflows/trunk_format_pr.yml
vendored
4
.github/workflows/trunk_format_pr.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
ref: ${{github.event.pull_request.head.ref}}
|
||||
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
git push
|
||||
|
||||
- name: Comment on PR
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
|
||||
4
.github/workflows/update_protobufs.yml
vendored
4
.github/workflows/update_protobufs.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
@@ -34,7 +34,9 @@ jobs:
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
branch: create-pull-request/update-protobufs
|
||||
labels: submodules
|
||||
title: Update protobufs and classes
|
||||
commit-message: Update protobufs
|
||||
add-paths: |
|
||||
protobufs
|
||||
src/mesh
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
version: 0.1
|
||||
cli:
|
||||
version: 1.24.0
|
||||
version: 1.25.0
|
||||
plugins:
|
||||
sources:
|
||||
- id: trunk
|
||||
ref: v1.7.1
|
||||
ref: v1.7.2
|
||||
uri: https://github.com/trunk-io/plugins
|
||||
lint:
|
||||
enabled:
|
||||
- checkov@3.2.450
|
||||
- renovate@41.29.1
|
||||
- checkov@3.2.473
|
||||
- renovate@41.132.5
|
||||
- prettier@3.6.2
|
||||
- trufflehog@3.89.2
|
||||
- trufflehog@3.90.8
|
||||
- yamllint@1.37.1
|
||||
- bandit@1.8.6
|
||||
- trivy@0.64.1
|
||||
- taplo@0.9.3
|
||||
- ruff@0.12.2
|
||||
- isort@6.0.1
|
||||
- trivy@0.67.0
|
||||
- taplo@0.10.0
|
||||
- ruff@0.13.3
|
||||
- isort@6.1.0
|
||||
- markdownlint@0.45.0
|
||||
- oxipng@9.1.5
|
||||
- svgo@4.0.0
|
||||
- actionlint@1.7.7
|
||||
- flake8@7.3.0
|
||||
- hadolint@2.12.1-beta
|
||||
- hadolint@2.14.0
|
||||
- shfmt@3.6.0
|
||||
- shellcheck@0.10.0
|
||||
- black@25.1.0
|
||||
- shellcheck@0.11.0
|
||||
- black@25.9.0
|
||||
- git-diff-check
|
||||
- gitleaks@8.27.2
|
||||
- gitleaks@8.28.0
|
||||
- clang-format@16.0.3
|
||||
ignore:
|
||||
- linters: [ALL]
|
||||
|
||||
10
Dockerfile
10
Dockerfile
@@ -3,7 +3,7 @@
|
||||
# trunk-ignore-all(hadolint/DL3008): Do not pin apt package versions
|
||||
# trunk-ignore-all(hadolint/DL3013): Do not pin pip package versions
|
||||
|
||||
FROM python:3.13-bookworm AS builder
|
||||
FROM python:3.14-slim-trixie AS builder
|
||||
ARG PIO_ENV=native
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
ENV TZ=Etc/UTC
|
||||
@@ -36,7 +36,7 @@ RUN curl -L "https://github.com/meshtastic/web/releases/download/v$(cat /tmp/fir
|
||||
|
||||
##### PRODUCTION BUILD #############
|
||||
|
||||
FROM debian:bookworm-slim
|
||||
FROM debian:trixie-slim
|
||||
LABEL org.opencontainers.image.title="Meshtastic" \
|
||||
org.opencontainers.image.description="Debian Meshtastic daemon and web interface" \
|
||||
org.opencontainers.image.url="https://meshtastic.org" \
|
||||
@@ -51,8 +51,8 @@ ENV TZ=Etc/UTC
|
||||
USER root
|
||||
|
||||
RUN apt-get update && apt-get --no-install-recommends -y install \
|
||||
libc-bin libc6 libgpiod2 libyaml-cpp0.7 libi2c0 libuv1 libusb-1.0-0-dev \
|
||||
liborcania2.3 libulfius2.7 libssl3 \
|
||||
libc-bin libc6 libgpiod3 libyaml-cpp0.8 libi2c0 libuv1t64 libusb-1.0-0-dev \
|
||||
liborcania2.3 libulfius2.7t64 libssl3t64 \
|
||||
libx11-6 libinput10 libxkbcommon-x11-0 \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/* \
|
||||
&& mkdir -p /var/lib/meshtasticd \
|
||||
@@ -61,7 +61,7 @@ RUN apt-get update && apt-get --no-install-recommends -y install \
|
||||
|
||||
# Fetch compiled binary from the builder
|
||||
COPY --from=builder /tmp/firmware/release/meshtasticd /usr/bin/
|
||||
COPY --from=builder /tmp/web /usr/share/meshtasticd/
|
||||
COPY --from=builder /tmp/web /usr/share/meshtasticd/web/
|
||||
# Copy config templates
|
||||
COPY ./bin/config.d /etc/meshtasticd/available.d
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# trunk-ignore-all(hadolint/DL3018): Do not pin apk 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
|
||||
ENV PIP_ROOT_USER_ACTION=ignore
|
||||
|
||||
|
||||
@@ -31,11 +31,13 @@ build_flags =
|
||||
-DMYNEWT_VAL_BLE_HS_LOG_LVL=LOG_LEVEL_CRITICAL
|
||||
-DAXP_DEBUG_PORT=Serial
|
||||
-DCONFIG_BT_NIMBLE_ENABLED
|
||||
-DCONFIG_BT_NIMBLE_MAX_BONDS=6 # default is 3
|
||||
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=2
|
||||
-DCONFIG_BT_NIMBLE_MAX_CCCDS=20
|
||||
-DCONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=8192
|
||||
-DESP_OPENSSL_SUPPRESS_LEGACY_WARNING
|
||||
-DSERIAL_BUFFER_SIZE=4096
|
||||
-DSERIAL_HAS_ON_RECEIVE
|
||||
-DLIBPAX_ARDUINO
|
||||
-DLIBPAX_WIFI
|
||||
-DLIBPAX_BLE
|
||||
@@ -54,8 +56,8 @@ lib_deps =
|
||||
h2zero/NimBLE-Arduino@^1.4.3
|
||||
# renovate: datasource=git-refs depName=libpax packageName=https://github.com/dbinfrago/libpax gitBranch=master
|
||||
https://github.com/dbinfrago/libpax/archive/3cdc0371c375676a97967547f4065607d4c53fd1.zip
|
||||
# renovate: datasource=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib
|
||||
lewisxhe/XPowersLib@0.3.0
|
||||
# renovate: datasource=github-tags depName=XPowersLib packageName=lewisxhe/XPowersLib
|
||||
https://github.com/lewisxhe/XPowersLib/archive/v0.3.0.zip
|
||||
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
|
||||
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
|
||||
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
||||
|
||||
@@ -23,7 +23,7 @@ build_flags =
|
||||
-DMESHTASTIC_EXCLUDE_PAXCOUNTER=1
|
||||
|
||||
build_src_filter =
|
||||
${arduino_base.build_src_filter} -<platform/esp32/> -<platform/stm32wl> -<nimble/> -<mesh/wifi/> -<mesh/api/> -<mesh/http/> -<modules/esp32> -<platform/rp2xx0> -<mesh/eth/> -<mesh/raspihttp>
|
||||
${arduino_base.build_src_filter} -<platform/esp32/> -<platform/stm32wl> -<nimble/> -<mesh/wifi/> -<mesh/api/> -<mesh/http/> -<modules/esp32> -<platform/rp2xx0> -<mesh/eth/> -<mesh/raspihttp> -<serialization/>
|
||||
|
||||
lib_deps=
|
||||
${arduino_base.lib_deps}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
[portduino_base]
|
||||
platform =
|
||||
# renovate: datasource=git-refs depName=platform-native packageName=https://github.com/meshtastic/platform-native gitBranch=develop
|
||||
https://github.com/meshtastic/platform-native/archive/6cb7a455b440dd0738e8ed74a18136ed5cf7ea63.zip
|
||||
https://github.com/meshtastic/platform-native/archive/d3f6e339534233c7217818867368767590ce549e.zip
|
||||
framework = arduino
|
||||
|
||||
build_src_filter =
|
||||
@@ -17,7 +17,6 @@ build_src_filter =
|
||||
+<mesh/raspihttp/>
|
||||
-<mesh/eth/>
|
||||
-<modules/esp32>
|
||||
+<../variants/portduino>
|
||||
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
@@ -32,14 +31,17 @@ lib_deps =
|
||||
https://github.com/pine64/libch341-spi-userspace/archive/af9bc27c9c30fa90772279925b7c5913dff789b4.zip
|
||||
# renovate: datasource=custom.pio depName=adafruit/Adafruit seesaw Library packageName=adafruit/library/Adafruit seesaw Library
|
||||
adafruit/Adafruit seesaw Library@1.7.9
|
||||
# renovate: datasource=git-refs depName=RAK12034-BMX160 packageName=https://github.com/RAKWireless/RAK12034-BMX160 gitBranch=main
|
||||
https://github.com/RAKWireless/RAK12034-BMX160/archive/dcead07ffa267d3c906e9ca4a1330ab989e957e2.zip
|
||||
|
||||
build_flags =
|
||||
${arduino_base.build_flags}
|
||||
-D ARCH_PORTDUINO
|
||||
-fPIC
|
||||
-Isrc/platform/portduino
|
||||
-DRADIOLIB_EEPROM_UNSUPPORTED
|
||||
-DPORTDUINO_LINUX_HARDWARE
|
||||
-DHAS_UDP_MULTICAST
|
||||
-DHAS_UDP_MULTICAST=1
|
||||
-lpthread
|
||||
-lstdc++fs
|
||||
-lbluetooth
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
extends = arduino_base
|
||||
platform =
|
||||
# renovate: datasource=custom.pio depName=platformio/ststm32 packageName=platformio/platform/ststm32
|
||||
platformio/ststm32@19.2.0
|
||||
platformio/ststm32@19.3.0
|
||||
platform_packages =
|
||||
# TODO renovate
|
||||
platformio/framework-arduinoststm32@https://github.com/stm32duino/Arduino_Core_STM32/archive/2.10.1.zip
|
||||
@@ -23,14 +23,20 @@ build_flags =
|
||||
-DMESHTASTIC_EXCLUDE_SCREEN=1
|
||||
-DMESHTASTIC_EXCLUDE_MQTT=1
|
||||
-DMESHTASTIC_EXCLUDE_BLUETOOTH=1
|
||||
-DMESHTASTIC_EXCLUDE_GPS=1
|
||||
-DMESHTASTIC_EXCLUDE_WIFI=1
|
||||
-DMESHTASTIC_EXCLUDE_TZ=1 ; Exclude TZ to save some flash space.
|
||||
-DSERIAL_RX_BUFFER_SIZE=256 ; For GPS - the default of 64 is too small.
|
||||
-DHAS_SCREEN=0 ; Always disable screen for STM32, it is not supported.
|
||||
-DPIO_FRAMEWORK_ARDUINO_NANOLIB_FLOAT_PRINTF ; This is REQUIRED for at least traceroute debug prints - without it the length ends up uninitialized.
|
||||
;-DDEBUG_MUTE
|
||||
-DDEBUG_MUTE ; You can #undef DEBUG_MUTE in certain source files if you need the logs.
|
||||
-fmerge-all-constants
|
||||
-ffunction-sections
|
||||
-fdata-sections
|
||||
-DRADIOLIB_EXCLUDE_SX128X=1
|
||||
-DRADIOLIB_EXCLUDE_SX127X=1
|
||||
-DRADIOLIB_EXCLUDE_LR11X0=1
|
||||
-DHAL_DAC_MODULE_ONLY
|
||||
-DHAL_RNG_MODULE_ENABLED
|
||||
|
||||
build_src_filter =
|
||||
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<mesh/api/> -<mesh/wifi/> -<mesh/http/> -<modules/esp32> -<mesh/eth/> -<input> -<buzz> -<modules/RemoteHardwareModule.cpp> -<platform/nrf52> -<platform/portduino> -<platform/rp2xx0> -<mesh/raspihttp>
|
||||
@@ -44,7 +50,7 @@ lib_deps =
|
||||
${radiolib_base.lib_deps}
|
||||
|
||||
# renovate: datasource=git-refs depName=caveman99-stm32-Crypto packageName=https://github.com/caveman99/Crypto gitBranch=main
|
||||
https://github.com/caveman99/Crypto/archive/eae9c768054118a9399690f8af202853d1ae8516.zip
|
||||
https://github.com/caveman99/Crypto/archive/1aa30eb536bd52a576fde6dfa393bf7349cf102d.zip
|
||||
|
||||
lib_ignore =
|
||||
OneButton
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
sed -i 's/#-DBUILD_EPOCH=$UNIX_TIME/-DBUILD_EPOCH=$UNIX_TIME/' platformio.ini
|
||||
|
||||
export PIP_BREAK_SYSTEM_PACKAGES=1
|
||||
|
||||
if (echo $2 | grep -q "esp32"); then
|
||||
@@ -11,7 +9,7 @@ elif (echo $2 | grep -q "nrf52"); then
|
||||
elif (echo $2 | grep -q "stm32"); then
|
||||
bin/build-stm32.sh $1
|
||||
elif (echo $2 | grep -q "rpi2040"); then
|
||||
bin/build-rpi2040.sh $1
|
||||
bin/build-rp2xx0.sh $1
|
||||
else
|
||||
echo "Unknown target $2"
|
||||
exit 1
|
||||
|
||||
@@ -9,13 +9,4 @@ Lora:
|
||||
DIO3_TCXO_VOLTAGE: true
|
||||
DIO2_AS_RF_SWITCH: true
|
||||
spidev: spidev0.0
|
||||
# CS: 8
|
||||
|
||||
|
||||
### RAK13300in Slot 2 pins
|
||||
# IRQ: 18 #IO6
|
||||
# Reset: 24 # IO4
|
||||
# Busy: 19 # IO5
|
||||
# # Ant_sw: 23 # IO3
|
||||
# spidev: spidev0.1
|
||||
# # CS: 7
|
||||
# CS: 8
|
||||
8
bin/config.d/lora-RAK6421-13300-slot2.yaml
Normal file
8
bin/config.d/lora-RAK6421-13300-slot2.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
Lora:
|
||||
### RAK13300in Slot 2 pins
|
||||
IRQ: 18 #IO6
|
||||
Reset: 24 # IO4
|
||||
Busy: 19 # IO5
|
||||
# Ant_sw: 23 # IO3
|
||||
spidev: spidev0.1
|
||||
# CS: 7
|
||||
@@ -7,6 +7,7 @@ SET "DEBUG=0"
|
||||
SET "PYTHON="
|
||||
SET "TFT_BUILD=0"
|
||||
SET "BIGDB8=0"
|
||||
SET "MUIDB8=0"
|
||||
SET "BIGDB16=0"
|
||||
SET "ESPTOOL_BAUD=115200"
|
||||
SET "ESPTOOL_CMD="
|
||||
@@ -14,11 +15,12 @@ SET "LOGCOUNTER=0"
|
||||
SET "BPS_RESET=0"
|
||||
|
||||
@REM FIXME: Determine mcu from PlatformIO variant, this is unmaintainable.
|
||||
SET "S3=s3 v3 t-deck wireless-paper wireless-tracker station-g2 unphone"
|
||||
SET "S3=s3 v3 t-deck wireless-paper wireless-tracker station-g2 unphone t-eth-elite tlora-pager mesh-tab dreamcatcher ESP32-S3-Pico seeed-sensecap-indicator heltec_capsule_sensor_v3 vision-master icarus tracksenger elecrow-adv"
|
||||
SET "C3=esp32c3"
|
||||
@REM FIXME: Determine flash size from PlatformIO variant, this is unmaintainable.
|
||||
SET "BIGDB_8MB=picomputer-s3 unphone seeed-sensecap-indicator crowpanel-esp32s3 heltec_capsule_sensor_v3 heltec-v3 heltec-vision-master-e213 heltec-vision-master-e290 heltec-vision-master-t190 heltec-wireless-paper heltec-wireless-tracker heltec-wsl-v3 icarus seeed-xiao-s3 tbeam-s3-core tracksenger"
|
||||
SET "BIGDB_16MB=t-deck mesh-tab t-energy-s3 dreamcatcher ESP32-S3-Pico m5stack-cores3 station-g2 t-eth-elite t-watch-s3"
|
||||
SET "BIGDB_8MB=crowpanel-esp32s3 heltec_capsule_sensor_v3 heltec-v3 heltec-vision-master-e213 heltec-vision-master-e290 heltec-vision-master-t190 heltec-wireless-paper heltec-wireless-tracker heltec-wsl-v3 icarus seeed-xiao-s3 tbeam-s3-core tracksenger"
|
||||
SET "MUIDB_8MB=picomputer-s3 unphone seeed-sensecap-indicator"
|
||||
SET "BIGDB_16MB=t-deck mesh-tab t-energy-s3 dreamcatcher ESP32-S3-Pico m5stack-cores3 station-g2 t-eth-elite tlora-pager t-watch-s3 elecrow-adv"
|
||||
|
||||
GOTO getopts
|
||||
:help
|
||||
@@ -100,7 +102,6 @@ IF NOT "!FILENAME:update=!"=="!FILENAME!" (
|
||||
)
|
||||
|
||||
:skip-filename
|
||||
SET "ESPTOOL_BAUD=1200"
|
||||
|
||||
CALL :LOG_MESSAGE DEBUG "Determine the correct esptool command to use..."
|
||||
IF NOT "__%PYTHON%__"=="____" (
|
||||
@@ -120,11 +121,10 @@ IF NOT "__%PYTHON%__"=="____" (
|
||||
|
||||
CALL :LOG_MESSAGE DEBUG "Checking esptool command !ESPTOOL_CMD!..."
|
||||
!ESPTOOL_CMD! >nul 2>&1
|
||||
IF %ERRORLEVEL% GEQ 2 (
|
||||
@REM esptool exits with code 1 if help is displayed.
|
||||
IF %ERRORLEVEL% EQU 9009 (
|
||||
@REM 9009 = command not found on Windows
|
||||
CALL :LOG_MESSAGE ERROR "esptool not found: !ESPTOOL_CMD!"
|
||||
EXIT /B 1
|
||||
GOTO eof
|
||||
)
|
||||
IF %DEBUG% EQU 1 (
|
||||
CALL :LOG_MESSAGE DEBUG "Skipping ESPTOOL_CMD steps."
|
||||
@@ -142,7 +142,7 @@ CALL :LOG_MESSAGE INFO "Using esptool baud: !ESPTOOL_BAUD!."
|
||||
|
||||
IF %BPS_RESET% EQU 1 (
|
||||
@REM Attempt to change mode via 1200bps Reset.
|
||||
CALL :RUN_ESPTOOL !ESPTOOL_BAUD! --after no_reset read_flash_status
|
||||
CALL :RUN_ESPTOOL 1200 --after no_reset read_flash_status
|
||||
GOTO eof
|
||||
)
|
||||
|
||||
@@ -164,6 +164,15 @@ FOR %%a IN (%BIGDB_8MB%) DO (
|
||||
)
|
||||
:end_loop_bigdb_8mb
|
||||
|
||||
FOR %%a IN (%MUIDB_8MB%) DO (
|
||||
IF NOT "!FILENAME:%%a=!"=="!FILENAME!" (
|
||||
@REM We are working with any of %MUIDB_8MB%.
|
||||
SET "MUIDB8=1"
|
||||
GOTO end_loop_muidb_8mb
|
||||
)
|
||||
)
|
||||
:end_loop_muidb_8mb
|
||||
|
||||
FOR %%a IN (%BIGDB_16MB%) DO (
|
||||
IF NOT "!FILENAME:%%a=!"=="!FILENAME!" (
|
||||
@REM We are working with any of %BIGDB_16MB%.
|
||||
@@ -174,6 +183,7 @@ FOR %%a IN (%BIGDB_16MB%) DO (
|
||||
:end_loop_bigdb_16mb
|
||||
|
||||
IF %BIGDB8% EQU 1 CALL :LOG_MESSAGE INFO "BigDB 8mb partition selected."
|
||||
IF %MUIDB8% EQU 1 CALL :LOG_MESSAGE INFO "MUIDB 8mb partition selected."
|
||||
IF %BIGDB16% EQU 1 CALL :LOG_MESSAGE INFO "BigDB 16mb partition selected."
|
||||
|
||||
@REM Extract BASENAME from %FILENAME% for later use.
|
||||
@@ -218,6 +228,12 @@ IF %BIGDB8% EQU 1 (
|
||||
SET "SPIFFS_OFFSET=0x670000"
|
||||
)
|
||||
|
||||
@REM Offsets for MUIDB 8mb.
|
||||
IF %MUIDB8% EQU 1 (
|
||||
SET "OTA_OFFSET=0x5D0000"
|
||||
SET "SPIFFS_OFFSET=0x670000"
|
||||
)
|
||||
|
||||
@REM Offsets for BigDB 16mb.
|
||||
IF %BIGDB16% EQU 1 (
|
||||
SET "OTA_OFFSET=0x650000"
|
||||
|
||||
@@ -1,42 +1,47 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
PYTHON=${PYTHON:-$(which python3 python | head -n 1)}
|
||||
BPS_RESET=false
|
||||
TFT_BUILD=false
|
||||
MCU=""
|
||||
|
||||
# Constants
|
||||
RESET_BAUD=1200
|
||||
FIRMWARE_OFFSET=0x00
|
||||
|
||||
# Variant groups
|
||||
BIGDB_8MB=(
|
||||
"picomputer-s3"
|
||||
"unphone"
|
||||
"seeed-sensecap-indicator"
|
||||
"crowpanel-esp32s3"
|
||||
"heltec_capsule_sensor_v3"
|
||||
"heltec-v3"
|
||||
"heltec-vision-master-e213"
|
||||
"heltec-vision-master-e290"
|
||||
"heltec-vision-master-t190"
|
||||
"heltec-wireless-paper"
|
||||
"heltec-wireless-tracker"
|
||||
"heltec-wsl-v3"
|
||||
"icarus"
|
||||
"seeed-xiao-s3"
|
||||
"tbeam-s3-core"
|
||||
"tracksenger"
|
||||
"crowpanel-esp32s3"
|
||||
"heltec_capsule_sensor_v3"
|
||||
"heltec-v3"
|
||||
"heltec-vision-master-e213"
|
||||
"heltec-vision-master-e290"
|
||||
"heltec-vision-master-t190"
|
||||
"heltec-wireless-paper"
|
||||
"heltec-wireless-tracker"
|
||||
"heltec-wsl-v3"
|
||||
"icarus"
|
||||
"seeed-xiao-s3"
|
||||
"tbeam-s3-core"
|
||||
"tracksenger"
|
||||
)
|
||||
MUIDB_8MB=(
|
||||
"picomputer-s3"
|
||||
"unphone"
|
||||
"seeed-sensecap-indicator"
|
||||
)
|
||||
BIGDB_16MB=(
|
||||
"t-deck"
|
||||
"mesh-tab"
|
||||
"t-energy-s3"
|
||||
"dreamcatcher"
|
||||
"ESP32-S3-Pico"
|
||||
"m5stack-cores3"
|
||||
"station-g2"
|
||||
"t-deck"
|
||||
"mesh-tab"
|
||||
"t-energy-s3"
|
||||
"dreamcatcher"
|
||||
"ESP32-S3-Pico"
|
||||
"m5stack-cores3"
|
||||
"station-g2"
|
||||
"t-eth-elite"
|
||||
"tlora-pager"
|
||||
"t-watch-s3"
|
||||
"elecrow-adv-35-tft"
|
||||
"elecrow-adv-24-28-tft"
|
||||
"elecrow-adv1-43-50-70-tft"
|
||||
"elecrow-adv"
|
||||
)
|
||||
S3_VARIANTS=(
|
||||
"s3"
|
||||
@@ -47,6 +52,7 @@ S3_VARIANTS=(
|
||||
"station-g2"
|
||||
"unphone"
|
||||
"t-eth-elite"
|
||||
"tlora-pager"
|
||||
"mesh-tab"
|
||||
"dreamcatcher"
|
||||
"ESP32-S3-Pico"
|
||||
@@ -106,8 +112,8 @@ while [ $# -gt 0 ]; do
|
||||
shift
|
||||
;;
|
||||
--1200bps-reset)
|
||||
BPS_RESET=true
|
||||
;;
|
||||
BPS_RESET=true
|
||||
;;
|
||||
--) # Stop parsing options
|
||||
shift
|
||||
break
|
||||
@@ -121,7 +127,7 @@ while [ $# -gt 0 ]; do
|
||||
done
|
||||
|
||||
if [[ $BPS_RESET == true ]]; then
|
||||
$ESPTOOL_CMD --baud 1200 --after no_reset read_flash_status
|
||||
$ESPTOOL_CMD --baud $RESET_BAUD --after no_reset read_flash_status
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@@ -158,6 +164,13 @@ if [ -f "${FILENAME}" ] && [ -n "${FILENAME##*"update"*}" ]; then
|
||||
fi
|
||||
done
|
||||
|
||||
for variant in "${MUIDB_8MB[@]}"; do
|
||||
if [ -z "${FILENAME##*"$variant"*}" ]; then
|
||||
OFFSET=0x670000
|
||||
OTA_OFFSET=0x5D0000
|
||||
fi
|
||||
done
|
||||
|
||||
# littlefs* offset for BigDB 16mb and OTA OFFSET.
|
||||
for variant in "${BIGDB_16MB[@]}"; do
|
||||
if [ -z "${FILENAME##*"$variant"*}" ]; then
|
||||
@@ -201,8 +214,8 @@ if [ -f "${FILENAME}" ] && [ -n "${FILENAME##*"update"*}" ]; then
|
||||
fi
|
||||
|
||||
echo "Trying to flash ${FILENAME}, but first erasing and writing system information"
|
||||
$ESPTOOL_CMD erase_flash
|
||||
$ESPTOOL_CMD write_flash 0x00 "${FILENAME}"
|
||||
$ESPTOOL_CMD erase-flash
|
||||
$ESPTOOL_CMD write-flash $FIRMWARE_OFFSET "${FILENAME}"
|
||||
echo "Trying to flash ${OTAFILE} at offset ${OTA_OFFSET}"
|
||||
$ESPTOOL_CMD write_flash $OTA_OFFSET "${OTAFILE}"
|
||||
echo "Trying to flash ${SPIFFSFILE}, at offset ${OFFSET}"
|
||||
|
||||
@@ -6,6 +6,8 @@ SET "SCRIPT_NAME=%~nx0"
|
||||
SET "DEBUG=0"
|
||||
SET "PYTHON="
|
||||
SET "ESPTOOL_BAUD=115200"
|
||||
SET "RESET_BAUD=1200"
|
||||
SET "UPDATE_OFFSET=0x10000"
|
||||
SET "ESPTOOL_CMD="
|
||||
SET "LOGCOUNTER=0"
|
||||
SET "CHANGE_MODE=0"
|
||||
@@ -85,14 +87,13 @@ IF "!FILENAME:update=!"=="!FILENAME!" (
|
||||
)
|
||||
|
||||
:skip-filename
|
||||
SET "ESPTOOL_BAUD=1200"
|
||||
|
||||
CALL :LOG_MESSAGE DEBUG "Determine the correct esptool command to use..."
|
||||
IF NOT "__%PYTHON%__"=="____" (
|
||||
SET "ESPTOOL_CMD=!PYTHON! -m esptool"
|
||||
SET "ESPTOOL_CMD=""!PYTHON!"" -m esptool"
|
||||
CALL :LOG_MESSAGE DEBUG "Python interpreter supplied."
|
||||
) ELSE (
|
||||
CALL :LOG_MESSAGE DEBUG "Python interpreter NOT supplied. Looking for esptool...
|
||||
CALL :LOG_MESSAGE DEBUG "Python interpreter NOT supplied. Looking for esptool..."
|
||||
WHERE esptool >nul 2>&1
|
||||
IF %ERRORLEVEL% EQU 0 (
|
||||
@REM WHERE exits with code 0 if esptool is found.
|
||||
@@ -105,11 +106,11 @@ IF NOT "__%PYTHON%__"=="____" (
|
||||
|
||||
CALL :LOG_MESSAGE DEBUG "Checking esptool command !ESPTOOL_CMD!..."
|
||||
!ESPTOOL_CMD! >nul 2>&1
|
||||
IF %ERRORLEVEL% GEQ 2 (
|
||||
@REM esptool exits with code 1 if help is displayed.
|
||||
CALL :LOG_MESSAGE DEBUG "esptool exit code: %ERRORLEVEL%"
|
||||
IF %ERRORLEVEL% EQU 9009 (
|
||||
@REM 9009 = command not found on Windows
|
||||
CALL :LOG_MESSAGE ERROR "esptool not found: !ESPTOOL_CMD!"
|
||||
EXIT /B 1
|
||||
GOTO eof
|
||||
)
|
||||
IF %DEBUG% EQU 1 (
|
||||
CALL :LOG_MESSAGE DEBUG "Skipping ESPTOOL_CMD steps."
|
||||
@@ -127,13 +128,13 @@ CALL :LOG_MESSAGE INFO "Using esptool baud: !ESPTOOL_BAUD!."
|
||||
|
||||
IF %CHANGE_MODE% EQU 1 (
|
||||
@REM Attempt to change mode via 1200bps Reset.
|
||||
CALL :RUN_ESPTOOL !ESPTOOL_BAUD! --after no_reset read_flash_status
|
||||
CALL :RUN_ESPTOOL !RESET_BAUD! --after no_reset read_flash_status
|
||||
GOTO eof
|
||||
)
|
||||
|
||||
@REM Flashing operations.
|
||||
CALL :LOG_MESSAGE INFO "Trying to flash update "!FILENAME!" at OFFSET 0x10000..."
|
||||
CALL :RUN_ESPTOOL !ESPTOOL_BAUD! write_flash 0x10000 "!FILENAME!" || GOTO eof
|
||||
CALL :LOG_MESSAGE INFO "Trying to flash update "!FILENAME!" at OFFSET !UPDATE_OFFSET!..."
|
||||
CALL :RUN_ESPTOOL !ESPTOOL_BAUD! write-flash !UPDATE_OFFSET! "!FILENAME!" || GOTO eof
|
||||
|
||||
CALL :LOG_MESSAGE INFO "Script complete!."
|
||||
|
||||
@@ -145,9 +146,9 @@ EXIT /B %ERRORLEVEL%
|
||||
:RUN_ESPTOOL
|
||||
@REM Subroutine used to run ESPTOOL_CMD with arguments.
|
||||
@REM Also handles %ERRORLEVEL%.
|
||||
@REM CALL :RUN_ESPTOOL [Baud] [erase_flash|write_flash] [OFFSET] [Filename]
|
||||
@REM CALL :RUN_ESPTOOL [Baud] [erase-flash|write-flash] [OFFSET] [Filename]
|
||||
@REM.
|
||||
@REM Example:: CALL :RUN_ESPTOOL 115200 write_flash 0x10000 "firmwarefile.bin"
|
||||
@REM Example:: CALL :RUN_ESPTOOL 115200 write-flash 0x10000 "firmwarefile.bin"
|
||||
IF %DEBUG% EQU 1 CALL :LOG_MESSAGE DEBUG "About to run command: !ESPTOOL_CMD! --baud %~1 %~2 %~3 %~4"
|
||||
CALL :RESET_ERROR
|
||||
!ESPTOOL_CMD! --baud %~1 %~2 %~3 %~4
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
#!/bin/sh
|
||||
#!/usr/bin/env bash
|
||||
|
||||
PYTHON=${PYTHON:-$(which python3 python|head -n 1)}
|
||||
CHANGE_MODE=false
|
||||
|
||||
# Constants
|
||||
FLASH_BAUD=115200
|
||||
RESET_BAUD=1200
|
||||
UPDATE_OFFSET=0x10000
|
||||
|
||||
# Determine the correct esptool command to use
|
||||
if "$PYTHON" -m esptool version >/dev/null 2>&1; then
|
||||
ESPTOOL_CMD="$PYTHON -m esptool"
|
||||
@@ -64,7 +69,7 @@ done
|
||||
shift "$((OPTIND-1))"
|
||||
|
||||
if [ "$CHANGE_MODE" = true ]; then
|
||||
$ESPTOOL_CMD --baud 1200 --after no_reset read_flash_status
|
||||
$ESPTOOL_CMD --baud $RESET_BAUD --after no_reset read_flash_status
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@@ -75,7 +80,7 @@ fi
|
||||
|
||||
if [ -f "${FILENAME}" ] && [ -z "${FILENAME##*"update"*}" ]; then
|
||||
echo "Trying to flash update ${FILENAME}"
|
||||
$ESPTOOL_CMD --baud 115200 write_flash 0x10000 "${FILENAME}"
|
||||
$ESPTOOL_CMD --baud $FLASH_BAUD write-flash $UPDATE_OFFSET "${FILENAME}"
|
||||
else
|
||||
show_help
|
||||
echo "Invalid file: ${FILENAME}"
|
||||
|
||||
@@ -1,51 +1,75 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""Generate the CI matrix."""
|
||||
|
||||
import configparser
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import random
|
||||
import re
|
||||
from platformio.project.config import ProjectConfig
|
||||
|
||||
rootdir = "variants/"
|
||||
|
||||
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 = []
|
||||
|
||||
if len(options) < 1:
|
||||
print(json.dumps(outlist))
|
||||
exit()
|
||||
cfg = ProjectConfig.get_instance()
|
||||
pio_envs = cfg.envs()
|
||||
|
||||
for subdir, dirs, files in os.walk(rootdir):
|
||||
for file in files:
|
||||
if file == "platformio.ini":
|
||||
config = configparser.ConfigParser()
|
||||
config.read(subdir + "/" + file)
|
||||
for c in config.sections():
|
||||
if c.startswith("env:"):
|
||||
section = config[c].name[4:]
|
||||
if "extends" in config[config[c].name]:
|
||||
if options[0] + "_base" in config[config[c].name]["extends"]:
|
||||
if "board_level" in config[config[c].name]:
|
||||
if (
|
||||
config[config[c].name]["board_level"] == "extra"
|
||||
) & ("extra" in options):
|
||||
outlist.append(section)
|
||||
else:
|
||||
outlist.append(section)
|
||||
# Add the TFT variants if the base variant is selected
|
||||
elif section.replace("-tft", "") in outlist and config[config[c].name].get("board_level") != "extra":
|
||||
outlist.append(section)
|
||||
elif section.replace("-inkhud", "") in outlist and config[config[c].name].get("board_level") != "extra":
|
||||
outlist.append(section)
|
||||
if "board_check" in config[config[c].name]:
|
||||
if (config[config[c].name]["board_check"] == "true") & (
|
||||
"check" in options
|
||||
):
|
||||
outlist.append(section)
|
||||
if ("quick" in options) & (len(outlist) > 3):
|
||||
print(json.dumps(random.sample(outlist, 3)))
|
||||
# Gather all PlatformIO environments for filtering later
|
||||
all_envs = []
|
||||
for pio_env in pio_envs:
|
||||
env_build_flags = cfg.get(f"env:{pio_env}", "build_flags")
|
||||
env_platform = None
|
||||
for flag in env_build_flags:
|
||||
# Extract the platform from the build flags
|
||||
# Example flag: -I variants/esp32s3/heltec-v3
|
||||
match = re.search(r"-I\s?variants/([^/]+)", flag)
|
||||
if match:
|
||||
env_platform = match.group(1)
|
||||
break
|
||||
# Intentionally fail if platform cannot be determined
|
||||
if not env_platform:
|
||||
print(f"Error: Could not determine platform for environment '{pio_env}'")
|
||||
exit(1)
|
||||
# Store env details as a dictionary, and add to 'all_envs' list
|
||||
env = {
|
||||
"ci": {"board": pio_env, "platform": env_platform},
|
||||
"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)),
|
||||
}
|
||||
all_envs.append(env)
|
||||
|
||||
# Filter outputs based on options
|
||||
# Check is mutually exclusive with other options (except 'pr')
|
||||
if "check" in args.platform:
|
||||
for env in all_envs:
|
||||
if env["board_check"]:
|
||||
if "pr" in args.level:
|
||||
if env["board_level"] == "pr":
|
||||
outlist.append(env["ci"])
|
||||
else:
|
||||
outlist.append(env["ci"])
|
||||
# Filter (non-check) builds by platform
|
||||
else:
|
||||
print(json.dumps(outlist))
|
||||
for env in all_envs:
|
||||
if args.platform == env["ci"]["platform"] or args.platform == "all":
|
||||
# Always include board_level = 'pr'
|
||||
if env["board_level"] == "pr":
|
||||
outlist.append(env["ci"])
|
||||
# Include board_level = 'extra' when requested
|
||||
elif "extra" in args.level and env["board_level"] == "extra":
|
||||
outlist.append(env["ci"])
|
||||
# If no board level is specified, include in release builds (not PR)
|
||||
elif "pr" not in args.level and not env["board_level"]:
|
||||
outlist.append(env["ci"])
|
||||
|
||||
# Return as a JSON list
|
||||
print(json.dumps(outlist))
|
||||
|
||||
@@ -87,6 +87,33 @@
|
||||
</screenshots>
|
||||
|
||||
<releases>
|
||||
<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">
|
||||
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.9</url>
|
||||
</release>
|
||||
<release version="2.7.8" date="2025-08-30">
|
||||
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.8</url>
|
||||
</release>
|
||||
<release version="2.7.7" date="2025-08-28">
|
||||
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.7</url>
|
||||
</release>
|
||||
<release version="2.7.6" date="2025-08-12">
|
||||
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.6</url>
|
||||
</release>
|
||||
<release version="2.7.5" date="2025-08-09">
|
||||
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.5</url>
|
||||
</release>
|
||||
<release version="2.7.4" date="2025-07-19">
|
||||
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.4</url>
|
||||
</release>
|
||||
<release version="2.7.3" date="2025-07-10">
|
||||
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.3</url>
|
||||
</release>
|
||||
|
||||
@@ -3,8 +3,11 @@
|
||||
# trunk-ignore-all(flake8/F821): For SConstruct imports
|
||||
import sys
|
||||
from os.path import join
|
||||
import subprocess
|
||||
import json
|
||||
import re
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
from readprops import readProps
|
||||
|
||||
@@ -83,7 +86,7 @@ if platform.name == "espressif32":
|
||||
|
||||
if platform.name == "nordicnrf52":
|
||||
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"))
|
||||
|
||||
Import("projenv")
|
||||
@@ -92,6 +95,17 @@ prefsLoc = projenv["PROJECT_DIR"] + "/version.properties"
|
||||
verObj = readProps(prefsLoc)
|
||||
print("Using meshtastic platformio-custom.py, firmware version " + verObj["long"] + " on " + env.get("PIOENV"))
|
||||
|
||||
# get repository owner if git is installed
|
||||
try:
|
||||
r_owner = (
|
||||
subprocess.check_output(["git", "config", "--get", "remote.origin.url"])
|
||||
.decode("utf-8")
|
||||
.strip().split("/")
|
||||
)
|
||||
repo_owner = r_owner[-2] + "/" + r_owner[-1].replace(".git", "")
|
||||
except subprocess.CalledProcessError:
|
||||
repo_owner = "unknown"
|
||||
|
||||
jsonLoc = env["PROJECT_DIR"] + "/userPrefs.jsonc"
|
||||
with open(jsonLoc) as f:
|
||||
jsonStr = re.sub("//.*","", f.read(), flags=re.MULTILINE)
|
||||
@@ -113,10 +127,16 @@ for pref in userPrefs:
|
||||
pref_flags.append("-D" + pref + "=" + env.StringifyMacro(userPrefs[pref]) + "")
|
||||
|
||||
# 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 = [
|
||||
"-DAPP_VERSION=" + verObj["long"],
|
||||
"-DAPP_VERSION_SHORT=" + verObj["short"],
|
||||
"-DAPP_ENV=" + env.get("PIOENV"),
|
||||
"-DAPP_REPO=" + repo_owner,
|
||||
"-DBUILD_EPOCH=" + str(build_epoch),
|
||||
] + pref_flags
|
||||
|
||||
print ("Using flags:")
|
||||
|
||||
@@ -1 +1 @@
|
||||
2.6.4
|
||||
2.6.6
|
||||
54
boards/heltec_mesh_solar.json
Normal file
54
boards/heltec_mesh_solar.json
Normal file
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "nrf52840_s140_v6.ld"
|
||||
},
|
||||
"core": "nRF5",
|
||||
"cpu": "cortex-m4",
|
||||
"extra_flags": "-DNRF52840_XXAA",
|
||||
"f_cpu": "64000000L",
|
||||
"hwids": [
|
||||
["0x239A", "0x4405"],
|
||||
["0x239A", "0x0029"],
|
||||
["0x239A", "0x002A"],
|
||||
["0x239A", "0x0071"]
|
||||
],
|
||||
"usb_product": "HT-n5262",
|
||||
"mcu": "nrf52840",
|
||||
"variant": "heltec_mesh_solar",
|
||||
"variants_dir": "variants",
|
||||
"bsp": {
|
||||
"name": "adafruit"
|
||||
},
|
||||
"softdevice": {
|
||||
"sd_flags": "-DS140",
|
||||
"sd_name": "s140",
|
||||
"sd_version": "6.1.1",
|
||||
"sd_fwid": "0x00B6"
|
||||
},
|
||||
"bootloader": {
|
||||
"settings_addr": "0xFF000"
|
||||
}
|
||||
},
|
||||
"connectivity": ["bluetooth"],
|
||||
"debug": {
|
||||
"jlink_device": "nRF52840_xxAA",
|
||||
"onboard_tools": ["jlink"],
|
||||
"svd_path": "nrf52840.svd",
|
||||
"openocd_target": "nrf52840-mdk-rs"
|
||||
},
|
||||
"frameworks": ["arduino"],
|
||||
"name": "Heltec nrf (Adafruit BSP)",
|
||||
"upload": {
|
||||
"maximum_ram_size": 248832,
|
||||
"maximum_size": 815104,
|
||||
"speed": 115200,
|
||||
"protocol": "nrfutil",
|
||||
"protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"],
|
||||
"use_1200bps_touch": true,
|
||||
"require_upload_port": true,
|
||||
"wait_for_upload_port": true
|
||||
},
|
||||
"url": "https://heltec.org/project/meshsolar/",
|
||||
"vendor": "Heltec"
|
||||
}
|
||||
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/meshtiny.json
Normal file
52
boards/meshtiny.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": "MeshTiny",
|
||||
"mcu": "nrf52840",
|
||||
"variant": "meshtiny",
|
||||
"bsp": {
|
||||
"name": "adafruit"
|
||||
},
|
||||
"softdevice": {
|
||||
"sd_flags": "-DS140",
|
||||
"sd_name": "s140",
|
||||
"sd_version": "6.1.1",
|
||||
"sd_fwid": "0x00B6"
|
||||
},
|
||||
"bootloader": {
|
||||
"settings_addr": "0xFF000"
|
||||
}
|
||||
},
|
||||
"connectivity": ["bluetooth"],
|
||||
"debug": {
|
||||
"jlink_device": "nRF52840_xxAA",
|
||||
"svd_path": "nrf52840.svd",
|
||||
"openocd_target": "nrf52840-mdk-rs"
|
||||
},
|
||||
"frameworks": ["arduino", "freertos"],
|
||||
"name": "MeshTiny",
|
||||
"upload": {
|
||||
"maximum_ram_size": 248832,
|
||||
"maximum_size": 815104,
|
||||
"speed": 115200,
|
||||
"protocol": "nrfutil",
|
||||
"protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"],
|
||||
"use_1200bps_touch": true,
|
||||
"require_upload_port": true,
|
||||
"wait_for_upload_port": true
|
||||
},
|
||||
"url": "https://github.com/meshtastic/firmware",
|
||||
"vendor": "MTools Tec"
|
||||
}
|
||||
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"
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"partitions": "default_8MB.csv",
|
||||
"partitions": "partition-table-8MB.csv",
|
||||
"memory_type": "qio_opi"
|
||||
},
|
||||
"core": "esp32",
|
||||
|
||||
43
boards/t-deck-pro.json
Normal file
43
boards/t-deck-pro.json
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"memory_type": "qio_qspi",
|
||||
"partitions": "default_16MB.csv"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
"hwids": [["0x303A", "0x1001"]],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "esp32s3"
|
||||
},
|
||||
"connectivity": ["wifi", "bluetooth", "lora"],
|
||||
"debug": {
|
||||
"default_tool": "esp-builtin",
|
||||
"onboard_tools": ["esp-builtin"],
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
"frameworks": ["arduino", "espidf"],
|
||||
"name": "LilyGo T-Deck Pro S3 (16M Flash 8M QSPI PSRAM )",
|
||||
"upload": {
|
||||
"flash_size": "16MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 16777216,
|
||||
"require_upload_port": true,
|
||||
"speed": 921600
|
||||
},
|
||||
"monitor": {
|
||||
"speed": 115200
|
||||
},
|
||||
"url": "https://lilygo.cc/products/t-deck-pro",
|
||||
"vendor": "LilyGo"
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"arduino": {
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"memory_type": "qio_opi",
|
||||
"partitions": "default_8MB.csv"
|
||||
"partitions": "partition-table-8MB.csv"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
},
|
||||
"core": "stm32",
|
||||
"cpu": "cortex-m4",
|
||||
"extra_flags": "-DSTM32WLxx -DSTM32WLE5xx -DARDUINO_GENERIC_WLE5CCUX",
|
||||
"extra_flags": "-DSTM32WLxx -DSTM32WLE5xx -DARDUINO_RAK3172_MODULE",
|
||||
"f_cpu": "48000000L",
|
||||
"mcu": "stm32wle5ccu",
|
||||
"variant": "STM32WLxx/WL54CCU_WL55CCU_WLE4C(8-B-C)U_WLE5C(8-B-C)U",
|
||||
|
||||
31
debian/changelog
vendored
31
debian/changelog
vendored
@@ -1,34 +1,13 @@
|
||||
meshtasticd (2.7.3.0) UNRELEASED; urgency=medium
|
||||
meshtasticd (2.7.12.0) unstable; urgency=medium
|
||||
|
||||
[ Austin Lane ]
|
||||
* Initial packaging
|
||||
* GitHub Actions Automatic version bump
|
||||
* GitHub Actions Automatic version bump
|
||||
* GitHub Actions Automatic version bump
|
||||
* GitHub Actions Automatic version bump
|
||||
* Version 2.5.19
|
||||
|
||||
[ ]
|
||||
* GitHub Actions Automatic version bump
|
||||
|
||||
[ ]
|
||||
* GitHub Actions Automatic version bump
|
||||
[ GitHub Actions ]
|
||||
* Version 2.7.12
|
||||
|
||||
[ ]
|
||||
* 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
|
||||
|
||||
-- Ubuntu <github-actions[bot]@users.noreply.github.com> Thu, 10 Jul 2025 16:29:27 +0000
|
||||
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Wed, 01 Oct 2025 19:51:41 +0000
|
||||
|
||||
5
debian/ci_changelog.sh
vendored
5
debian/ci_changelog.sh
vendored
@@ -1,7 +1,8 @@
|
||||
#!/usr/bin/bash
|
||||
export DEBFULLNAME="GitHub Actions"
|
||||
export DEBEMAIL="github-actions[bot]@users.noreply.github.com"
|
||||
PKG_VERSION=$(python3 bin/buildinfo.py short)
|
||||
|
||||
dch --newversion "$PKG_VERSION.0" \
|
||||
--distribution UNRELEASED \
|
||||
"GitHub Actions Automatic version bump"
|
||||
--distribution unstable \
|
||||
"Version $PKG_VERSION"
|
||||
|
||||
7
partition-table-8MB.csv
Normal file
7
partition-table-8MB.csv
Normal file
@@ -0,0 +1,7 @@
|
||||
# This is a layout for 8MB of flash for MUI devices
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x5C0000,
|
||||
flashApp, app, ota_1, 0x5D0000,0x0A0000,
|
||||
spiffs, data, spiffs, 0x670000,0x180000
|
||||
|
@@ -6,7 +6,8 @@ default_envs = tbeam
|
||||
|
||||
extra_configs =
|
||||
arch/*/*.ini
|
||||
variants/*/platformio.ini
|
||||
variants/*/*/platformio.ini
|
||||
variants/*/diy/*/platformio.ini
|
||||
src/graphics/niche/InkHUD/PlatformioConfig.ini
|
||||
|
||||
description = Meshtastic
|
||||
@@ -52,22 +53,24 @@ build_flags = -Wno-missing-field-initializers
|
||||
-DMESHTASTIC_EXCLUDE_POWERSTRESS=1 ; exclude power stress test module from main firmware
|
||||
-DMESHTASTIC_EXCLUDE_GENERIC_THREAD_MODULE=1
|
||||
-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 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_filters = direct
|
||||
lib_deps =
|
||||
# renovate: datasource=git-refs depName=meshtastic-esp8266-oled-ssd1306 packageName=https://github.com/meshtastic/esp8266-oled-ssd1306 gitBranch=master
|
||||
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/0119501e9983bd894830b02f545c377ee08d66fe.zip
|
||||
# renovate: datasource=custom.pio depName=OneButton packageName=mathertel/library/OneButton
|
||||
mathertel/OneButton@2.6.1
|
||||
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
|
||||
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
|
||||
https://github.com/meshtastic/arduino-fsm/archive/7db3702bf0cfe97b783d6c72595e3f38e0b19159.zip
|
||||
# renovate: datasource=git-refs depName=meshtastic-TinyGPSPlus packageName=https://github.com/meshtastic/TinyGPSPlus gitBranch=master
|
||||
https://github.com/meshtastic/TinyGPSPlus/archive/71a82db35f3b973440044c476d4bcdc673b104f4.zip
|
||||
# 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
|
||||
nanopb/Nanopb@0.4.91
|
||||
# renovate: datasource=custom.pio depName=ErriezCRC32 packageName=erriez/library/ErriezCRC32
|
||||
@@ -101,21 +104,29 @@ lib_deps =
|
||||
# renovate: datasource=custom.pio depName=Syslog packageName=arcao/library/Syslog
|
||||
arcao/Syslog@2.0.0
|
||||
|
||||
; Minimal networking libs for nrf52 (excludes Syslog to save flash)
|
||||
[nrf52_networking_base]
|
||||
lib_deps =
|
||||
# renovate: datasource=custom.pio depName=TBPubSubClient packageName=thingsboard/library/TBPubSubClient
|
||||
thingsboard/TBPubSubClient@2.12.1
|
||||
# renovate: datasource=custom.pio depName=NTPClient packageName=arduino-libraries/library/NTPClient
|
||||
arduino-libraries/NTPClient@3.2.1
|
||||
|
||||
[radiolib_base]
|
||||
lib_deps =
|
||||
# renovate: datasource=custom.pio depName=RadioLib packageName=jgromes/library/RadioLib
|
||||
jgromes/RadioLib@7.2.1
|
||||
jgromes/RadioLib@7.3.0
|
||||
|
||||
[device-ui_base]
|
||||
lib_deps =
|
||||
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
|
||||
https://github.com/meshtastic/device-ui/archive/86a09a7360f92d10053fbbf8d74f67f85b0ceb09.zip
|
||||
https://github.com/meshtastic/device-ui/archive/6d8cc228298a1ecd9913aed757187e9527c1facc.zip
|
||||
|
||||
; Common libs for environmental measurements in telemetry module
|
||||
[environmental_base]
|
||||
lib_deps =
|
||||
# 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
|
||||
adafruit/Adafruit Unified Sensor@1.1.15
|
||||
# renovate: datasource=custom.pio depName=Adafruit BMP280 packageName=adafruit/library/Adafruit BMP280 Library
|
||||
@@ -148,8 +159,8 @@ lib_deps =
|
||||
emotibit/EmotiBit MLX90632@1.0.8
|
||||
# renovate: datasource=custom.pio depName=Adafruit MLX90614 packageName=adafruit/library/Adafruit MLX90614 Library
|
||||
adafruit/Adafruit MLX90614 Library@2.1.5
|
||||
# renovate: datasource=github-tags depName=INA3221 packageName=KodinLanewave/INA3221
|
||||
https://github.com/KodinLanewave/INA3221/archive/1.0.1.zip
|
||||
# renovate: datasource=github-tags depName=INA3221 packageName=sgtwilko/INA3221
|
||||
https://github.com/sgtwilko/INA3221#bb03d7e9bfcc74fc798838a54f4f99738f29fc6a
|
||||
# renovate: datasource=custom.pio depName=QMC5883L Compass packageName=mprograms/library/QMC5883LCompass
|
||||
mprograms/QMC5883LCompass@1.2.3
|
||||
# renovate: datasource=custom.pio depName=DFRobot_RTU packageName=dfrobot/library/DFRobot_RTU
|
||||
@@ -168,6 +179,8 @@ lib_deps =
|
||||
adafruit/Adafruit PCT2075@1.0.5
|
||||
# renovate: datasource=custom.pio depName=DFRobot_BMM150 packageName=dfrobot/library/DFRobot_BMM150
|
||||
dfrobot/DFRobot_BMM150@1.0.0
|
||||
# renovate: datasource=custom.pio depName=Adafruit_TSL2561 packageName=adafruit/library/Adafruit TSL2561
|
||||
adafruit/Adafruit TSL2561@1.1.2
|
||||
|
||||
; (not included in native / portduino)
|
||||
[environmental_extra]
|
||||
@@ -177,7 +190,7 @@ lib_deps =
|
||||
# renovate: datasource=custom.pio depName=Adafruit MAX1704X packageName=adafruit/library/Adafruit MAX1704X
|
||||
adafruit/Adafruit MAX1704X@1.0.3
|
||||
# renovate: datasource=custom.pio depName=Adafruit SHTC3 packageName=adafruit/library/Adafruit SHTC3 Library
|
||||
adafruit/Adafruit SHTC3 Library@1.0.1
|
||||
adafruit/Adafruit SHTC3 Library@1.0.2
|
||||
# renovate: datasource=custom.pio depName=Adafruit LPS2X packageName=adafruit/library/Adafruit LPS2X
|
||||
adafruit/Adafruit LPS2X@2.0.6
|
||||
# renovate: datasource=custom.pio depName=Adafruit SHT31 packageName=adafruit/library/Adafruit SHT31 Library
|
||||
|
||||
Submodule protobufs updated: 584f0a3a35...a1b8c3d171
@@ -8,6 +8,7 @@
|
||||
"replacements:all",
|
||||
"workarounds:all"
|
||||
],
|
||||
"baseBranchPatterns": ["master"],
|
||||
"forkProcessing": "enabled",
|
||||
"ignoreDeps": [
|
||||
"protobufs"
|
||||
|
||||
@@ -183,9 +183,9 @@ class AmbientLightingThread : public concurrency::OSThread
|
||||
#endif
|
||||
#endif
|
||||
pixels.show();
|
||||
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.green, moduleConfig.ambient_lighting.blue);
|
||||
// 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.green, moduleConfig.ambient_lighting.blue);
|
||||
#endif
|
||||
#ifdef RGBLED_CA
|
||||
analogWrite(RGBLED_RED, 255 - moduleConfig.ambient_lighting.red);
|
||||
|
||||
@@ -11,6 +11,11 @@
|
||||
#include <AudioOutputI2S.h>
|
||||
#include <ESP8266SAM.h>
|
||||
|
||||
#ifdef USE_XL9555
|
||||
#include "ExtensionIOXL9555.hpp"
|
||||
extern ExtensionIOXL9555 io;
|
||||
#endif
|
||||
|
||||
#define AUDIO_THREAD_INTERVAL_MS 100
|
||||
|
||||
class AudioThread : public concurrency::OSThread
|
||||
@@ -20,12 +25,16 @@ class AudioThread : public concurrency::OSThread
|
||||
|
||||
void beginRttl(const void *data, uint32_t len)
|
||||
{
|
||||
#ifdef T_LORA_PAGER
|
||||
io.digitalWrite(EXPANDS_AMP_EN, HIGH);
|
||||
#endif
|
||||
setCPUFast(true);
|
||||
rtttlFile = new AudioFileSourcePROGMEM(data, len);
|
||||
i2sRtttl = new AudioGeneratorRTTTL();
|
||||
i2sRtttl->begin(rtttlFile, audioOut);
|
||||
}
|
||||
|
||||
// Also handles actually playing the RTTTL, needs to be called in loop
|
||||
bool isPlaying()
|
||||
{
|
||||
if (i2sRtttl != nullptr) {
|
||||
@@ -45,6 +54,9 @@ class AudioThread : public concurrency::OSThread
|
||||
rtttlFile = nullptr;
|
||||
|
||||
setCPUFast(false);
|
||||
#ifdef T_LORA_PAGER
|
||||
io.digitalWrite(EXPANDS_AMP_EN, LOW);
|
||||
#endif
|
||||
}
|
||||
|
||||
void readAloud(const char *text)
|
||||
@@ -55,10 +67,16 @@ class AudioThread : public concurrency::OSThread
|
||||
i2sRtttl = nullptr;
|
||||
}
|
||||
|
||||
#ifdef T_LORA_PAGER
|
||||
io.digitalWrite(EXPANDS_AMP_EN, HIGH);
|
||||
#endif
|
||||
ESP8266SAM *sam = new ESP8266SAM;
|
||||
sam->Say(audioOut, text);
|
||||
delete sam;
|
||||
setCPUFast(false);
|
||||
#ifdef T_LORA_PAGER
|
||||
io.digitalWrite(EXPANDS_AMP_EN, LOW);
|
||||
#endif
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
@@ -89,14 +89,22 @@ class BluetoothStatus : public Status
|
||||
case ConnectionState::CONNECTED:
|
||||
LOG_DEBUG("BluetoothStatus CONNECTED");
|
||||
#ifdef BLE_LED
|
||||
#ifdef BLE_LED_INVERTED
|
||||
digitalWrite(BLE_LED, LOW);
|
||||
#else
|
||||
digitalWrite(BLE_LED, HIGH);
|
||||
#endif
|
||||
#endif
|
||||
break;
|
||||
|
||||
case ConnectionState::DISCONNECTED:
|
||||
LOG_DEBUG("BluetoothStatus DISCONNECTED");
|
||||
#ifdef BLE_LED
|
||||
#ifdef BLE_LED_INVERTED
|
||||
digitalWrite(BLE_LED, HIGH);
|
||||
#else
|
||||
digitalWrite(BLE_LED, LOW);
|
||||
#endif
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ inline bool Syslog::_sendLog(uint16_t pri, const char *appName, const char *mess
|
||||
{
|
||||
int result;
|
||||
#ifdef ARCH_PORTDUINO
|
||||
bool utf = !settingsMap[ascii_logs];
|
||||
bool utf = !portduino_config.ascii_logs;
|
||||
#else
|
||||
bool utf = true;
|
||||
#endif
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
|
||||
#include "configuration.h"
|
||||
|
||||
// Forward declarations
|
||||
#if defined(DEBUG_HEAP)
|
||||
class MemGet;
|
||||
extern MemGet memGet;
|
||||
#endif
|
||||
|
||||
// DEBUG LED
|
||||
#ifndef LED_STATE_ON
|
||||
#define LED_STATE_ON 1
|
||||
@@ -23,6 +29,7 @@
|
||||
#define MESHTASTIC_LOG_LEVEL_ERROR "ERROR"
|
||||
#define MESHTASTIC_LOG_LEVEL_CRIT "CRIT "
|
||||
#define MESHTASTIC_LOG_LEVEL_TRACE "TRACE"
|
||||
#define MESHTASTIC_LOG_LEVEL_HEAP "HEAP"
|
||||
|
||||
#include "SerialConsole.h"
|
||||
|
||||
@@ -62,6 +69,25 @@
|
||||
#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
|
||||
extern "C" void logLegacy(const char *level, const char *fmt, ...);
|
||||
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
#include "DisplayFormatters.h"
|
||||
|
||||
const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName)
|
||||
const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName,
|
||||
bool usePreset)
|
||||
{
|
||||
|
||||
// If use_preset is false, always return "Custom"
|
||||
if (!usePreset) {
|
||||
return "Custom";
|
||||
}
|
||||
|
||||
switch (preset) {
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO:
|
||||
return useShortName ? "ShortT" : "ShortTurbo";
|
||||
@@ -31,4 +38,46 @@ const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaC
|
||||
return useShortName ? "Custom" : "Invalid";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const char *DisplayFormatters::getDeviceRole(meshtastic_Config_DeviceConfig_Role role)
|
||||
{
|
||||
switch (role) {
|
||||
case meshtastic_Config_DeviceConfig_Role_CLIENT:
|
||||
return "Client";
|
||||
break;
|
||||
case meshtastic_Config_DeviceConfig_Role_CLIENT_MUTE:
|
||||
return "Client Mute";
|
||||
break;
|
||||
case meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN:
|
||||
return "Client Hidden";
|
||||
break;
|
||||
case meshtastic_Config_DeviceConfig_Role_CLIENT_BASE:
|
||||
return "Client Base";
|
||||
break;
|
||||
case meshtastic_Config_DeviceConfig_Role_LOST_AND_FOUND:
|
||||
return "Lost and Found";
|
||||
break;
|
||||
case meshtastic_Config_DeviceConfig_Role_TRACKER:
|
||||
return "Tracker";
|
||||
break;
|
||||
case meshtastic_Config_DeviceConfig_Role_SENSOR:
|
||||
return "Sensor";
|
||||
break;
|
||||
case meshtastic_Config_DeviceConfig_Role_TAK:
|
||||
return "TAK";
|
||||
break;
|
||||
case meshtastic_Config_DeviceConfig_Role_TAK_TRACKER:
|
||||
return "TAK Tracker";
|
||||
break;
|
||||
case meshtastic_Config_DeviceConfig_Role_ROUTER:
|
||||
return "Router";
|
||||
break;
|
||||
case meshtastic_Config_DeviceConfig_Role_ROUTER_LATE:
|
||||
return "Router Late";
|
||||
break;
|
||||
default:
|
||||
return "Unknown";
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -4,5 +4,7 @@
|
||||
class DisplayFormatters
|
||||
{
|
||||
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);
|
||||
static const char *getDeviceRole(meshtastic_Config_DeviceConfig_Role role);
|
||||
};
|
||||
|
||||
@@ -22,6 +22,9 @@ class GPSStatus : public Status
|
||||
|
||||
meshtastic_Position p = meshtastic_Position_init_default;
|
||||
|
||||
/// Time of last valid GPS fix (millis since boot)
|
||||
uint32_t lastFixMillis = 0;
|
||||
|
||||
public:
|
||||
GPSStatus() { statusType = STATUS_TYPE_GPS; }
|
||||
|
||||
@@ -83,6 +86,9 @@ class GPSStatus : public Status
|
||||
|
||||
uint32_t getNumSatellites() const { return p.sats_in_view; }
|
||||
|
||||
/// Return millis() when the last GPS fix occurred (0 = never)
|
||||
uint32_t getLastFixMillis() const { return lastFixMillis; }
|
||||
|
||||
bool matches(const GPSStatus *newStatus) const
|
||||
{
|
||||
#ifdef GPS_DEBUG
|
||||
@@ -114,6 +120,9 @@ class GPSStatus : public Status
|
||||
|
||||
if (isDirty) {
|
||||
if (hasLock) {
|
||||
// Record time of last valid GPS fix
|
||||
lastFixMillis = millis();
|
||||
|
||||
// In debug logs, identify position by @timestamp:stage (stage 3 = notify)
|
||||
LOG_DEBUG("New GPS pos@%x:3 lat=%f lon=%f alt=%d pdop=%.2f track=%.2f speed=%.2f sats=%d", p.timestamp,
|
||||
p.latitude_i * 1e-7, p.longitude_i * 1e-7, p.altitude, p.PDOP * 1e-2, p.ground_track * 1e-5,
|
||||
|
||||
342
src/Power.cpp
342
src/Power.cpp
@@ -20,6 +20,11 @@
|
||||
#include "meshUtils.h"
|
||||
#include "sleep.h"
|
||||
|
||||
#if defined(ARCH_PORTDUINO)
|
||||
#include "api/WiFiServerAPI.h"
|
||||
#include "input/LinuxInputImpl.h"
|
||||
#endif
|
||||
|
||||
// Working USB detection for powered/charging states on the RAK platform
|
||||
#ifdef NRF_APM
|
||||
#include "nrfx_power.h"
|
||||
@@ -120,6 +125,16 @@ NullSensor max17048Sensor;
|
||||
RAK9154Sensor rak9154Sensor;
|
||||
#endif
|
||||
|
||||
#ifdef HAS_PPM
|
||||
// note: XPOWERS_CHIP_XXX must be defined in variant.h
|
||||
#include <XPowersLib.h>
|
||||
XPowersPPM *PPM = NULL;
|
||||
#endif
|
||||
|
||||
#ifdef HAS_BQ27220
|
||||
#include "bq27220.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAS_PMU
|
||||
XPowersLibInterface *PMU = NULL;
|
||||
#else
|
||||
@@ -665,6 +680,10 @@ bool Power::setup()
|
||||
found = true;
|
||||
} else if (lipoInit()) {
|
||||
found = true;
|
||||
} else if (lipoChargerInit()) {
|
||||
found = true;
|
||||
} else if (meshSolarInit()) {
|
||||
found = true;
|
||||
} else if (analogInit()) {
|
||||
found = true;
|
||||
}
|
||||
@@ -672,6 +691,16 @@ bool Power::setup()
|
||||
#ifdef NRF_APM
|
||||
found = true;
|
||||
#endif
|
||||
#ifdef EXT_PWR_DETECT
|
||||
attachInterrupt(
|
||||
EXT_PWR_DETECT,
|
||||
[]() {
|
||||
power->setIntervalFromNow(0);
|
||||
runASAP = true;
|
||||
BaseType_t higherWake = 0;
|
||||
},
|
||||
CHANGE);
|
||||
#endif
|
||||
|
||||
enabled = found;
|
||||
low_voltage_counter = 0;
|
||||
@@ -679,9 +708,65 @@ bool Power::setup()
|
||||
return found;
|
||||
}
|
||||
|
||||
void Power::powerCommandsCheck()
|
||||
{
|
||||
if (rebootAtMsec && millis() > rebootAtMsec) {
|
||||
LOG_INFO("Rebooting");
|
||||
reboot();
|
||||
}
|
||||
|
||||
if (shutdownAtMsec && millis() > shutdownAtMsec) {
|
||||
shutdownAtMsec = 0;
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
void Power::reboot()
|
||||
{
|
||||
notifyReboot.notifyObservers(NULL);
|
||||
#if defined(ARCH_ESP32)
|
||||
ESP.restart();
|
||||
#elif defined(ARCH_NRF52)
|
||||
NVIC_SystemReset();
|
||||
#elif defined(ARCH_RP2040)
|
||||
rp2040.reboot();
|
||||
#elif defined(ARCH_PORTDUINO)
|
||||
deInitApiServer();
|
||||
if (aLinuxInputImpl)
|
||||
aLinuxInputImpl->deInit();
|
||||
SPI.end();
|
||||
Wire.end();
|
||||
Serial1.end();
|
||||
if (screen) {
|
||||
delete screen;
|
||||
screen = nullptr;
|
||||
}
|
||||
LOG_DEBUG("final reboot!");
|
||||
::reboot();
|
||||
#elif defined(ARCH_STM32WL)
|
||||
HAL_NVIC_SystemReset();
|
||||
#else
|
||||
rebootAtMsec = -1;
|
||||
LOG_WARN("FIXME implement reboot for this platform. Note that some settings require a restart to be applied");
|
||||
#endif
|
||||
}
|
||||
|
||||
void Power::shutdown()
|
||||
{
|
||||
LOG_INFO("Shutting Down");
|
||||
|
||||
#if HAS_SCREEN
|
||||
if (screen) {
|
||||
#ifdef T_DECK_PRO
|
||||
screen->showSimpleBanner("Device is powered off.\nConnect USB to start!", 0); // T-Deck Pro has no power button
|
||||
#else
|
||||
screen->showSimpleBanner("Shutting Down...", 0); // stays on screen
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#if !defined(ARCH_STM32WL)
|
||||
playShutdownMelody();
|
||||
#endif
|
||||
nodeDB->saveToDisk();
|
||||
|
||||
#if defined(ARCH_NRF52) || defined(ARCH_ESP32) || defined(ARCH_RP2040)
|
||||
#ifdef PIN_LED1
|
||||
@@ -693,7 +778,11 @@ void Power::shutdown()
|
||||
#ifdef PIN_LED3
|
||||
ledOff(PIN_LED3);
|
||||
#endif
|
||||
doDeepSleep(DELAY_FOREVER, false, false);
|
||||
doDeepSleep(DELAY_FOREVER, true, true);
|
||||
#elif defined(ARCH_PORTDUINO)
|
||||
exit(EXIT_SUCCESS);
|
||||
#else
|
||||
LOG_WARN("FIXME implement shutdown for this platform");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -754,18 +843,27 @@ void Power::readPowerStatus()
|
||||
newStatus.notifyObservers(&powerStatus2);
|
||||
#ifdef DEBUG_HEAP
|
||||
if (lastheap != memGet.getFreeHeap()) {
|
||||
std::string threadlist = "Threads running:";
|
||||
// Use stack-allocated buffer to avoid heap allocations in monitoring code
|
||||
char threadlist[256] = "Threads running:";
|
||||
int threadlistLen = strlen(threadlist);
|
||||
int running = 0;
|
||||
for (int i = 0; i < MAX_THREADS; i++) {
|
||||
auto thread = concurrency::mainController.get(i);
|
||||
if ((thread != nullptr) && (thread->enabled)) {
|
||||
threadlist += vformat(" %s", thread->ThreadName.c_str());
|
||||
// Use snprintf to safely append to stack buffer without heap allocation
|
||||
int remaining = sizeof(threadlist) - threadlistLen - 1;
|
||||
if (remaining > 0) {
|
||||
int written = snprintf(threadlist + threadlistLen, remaining, " %s", thread->ThreadName.c_str());
|
||||
if (written > 0 && written < remaining) {
|
||||
threadlistLen += written;
|
||||
}
|
||||
}
|
||||
running++;
|
||||
}
|
||||
}
|
||||
LOG_DEBUG(threadlist.c_str());
|
||||
LOG_DEBUG("Heap status: %d/%d bytes free (%d), running %d/%d threads", memGet.getFreeHeap(), memGet.getHeapSize(),
|
||||
memGet.getFreeHeap() - lastheap, running, concurrency::mainController.size(false));
|
||||
LOG_HEAP(threadlist);
|
||||
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));
|
||||
lastheap = memGet.getFreeHeap();
|
||||
}
|
||||
#ifdef DEBUG_HEAP_MQTT
|
||||
@@ -777,15 +875,19 @@ void Power::readPowerStatus()
|
||||
sprintf(mac, "!%02x%02x%02x%02x", dmac[2], dmac[3], dmac[4], dmac[5]);
|
||||
|
||||
auto newHeap = memGet.getFreeHeap();
|
||||
std::string heapTopic =
|
||||
(*moduleConfig.mqtt.root ? moduleConfig.mqtt.root : "msh") + std::string("/2/heap/") + std::string(mac);
|
||||
std::string heapString = std::to_string(newHeap);
|
||||
mqtt->pubSub.publish(heapTopic.c_str(), heapString.c_str(), false);
|
||||
// Use stack-allocated buffers to avoid heap allocations in monitoring code
|
||||
char heapTopic[128];
|
||||
snprintf(heapTopic, sizeof(heapTopic), "%s/2/heap/%s", (*moduleConfig.mqtt.root ? moduleConfig.mqtt.root : "msh"), mac);
|
||||
char heapString[16];
|
||||
snprintf(heapString, sizeof(heapString), "%u", newHeap);
|
||||
mqtt->pubSub.publish(heapTopic, heapString, false);
|
||||
|
||||
auto wifiRSSI = WiFi.RSSI();
|
||||
std::string wifiTopic =
|
||||
(*moduleConfig.mqtt.root ? moduleConfig.mqtt.root : "msh") + std::string("/2/wifi/") + std::string(mac);
|
||||
std::string wifiString = std::to_string(wifiRSSI);
|
||||
mqtt->pubSub.publish(wifiTopic.c_str(), wifiString.c_str(), false);
|
||||
char wifiTopic[128];
|
||||
snprintf(wifiTopic, sizeof(wifiTopic), "%s/2/wifi/%s", (*moduleConfig.mqtt.root ? moduleConfig.mqtt.root : "msh"), mac);
|
||||
char wifiString[16];
|
||||
snprintf(wifiString, sizeof(wifiString), "%d", wifiRSSI);
|
||||
mqtt->pubSub.publish(wifiTopic, wifiString, false);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1237,3 +1339,213 @@ bool Power::lipoInit()
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAS_PPM) && HAS_PPM
|
||||
|
||||
/**
|
||||
* Adapter class for BQ25896/BQ27220 Lipo battery charger.
|
||||
*/
|
||||
class LipoCharger : public HasBatteryLevel
|
||||
{
|
||||
private:
|
||||
BQ27220 *bq = nullptr;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Init the I2C BQ25896 Lipo battery charger
|
||||
*/
|
||||
bool runOnce()
|
||||
{
|
||||
if (PPM == nullptr) {
|
||||
PPM = new XPowersPPM;
|
||||
bool result = PPM->init(Wire, I2C_SDA, I2C_SCL, BQ25896_ADDR);
|
||||
if (result) {
|
||||
LOG_INFO("PPM BQ25896 init succeeded");
|
||||
// Set the minimum operating voltage. Below this voltage, the PPM will protect
|
||||
// PPM->setSysPowerDownVoltage(3100);
|
||||
|
||||
// Set input current limit, default is 500mA
|
||||
// PPM->setInputCurrentLimit(800);
|
||||
|
||||
// Disable current limit pin
|
||||
// PPM->disableCurrentLimitPin();
|
||||
|
||||
// Set the charging target voltage, Range:3840 ~ 4608mV ,step:16 mV
|
||||
PPM->setChargeTargetVoltage(4288);
|
||||
|
||||
// Set the precharge current , Range: 64mA ~ 1024mA ,step:64mA
|
||||
// PPM->setPrechargeCurr(64);
|
||||
|
||||
// The premise is that limit pin is disabled, or it will
|
||||
// only follow the maximum charging current set by limit pin.
|
||||
// Set the charging current , Range:0~5056mA ,step:64mA
|
||||
PPM->setChargerConstantCurr(1024);
|
||||
|
||||
// To obtain voltage data, the ADC must be enabled first
|
||||
PPM->enableMeasure();
|
||||
|
||||
// Turn on charging function
|
||||
// If there is no battery connected, do not turn on the charging function
|
||||
PPM->enableCharge();
|
||||
} else {
|
||||
LOG_WARN("PPM BQ25896 init failed");
|
||||
delete PPM;
|
||||
PPM = nullptr;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (bq == nullptr) {
|
||||
bq = new BQ27220;
|
||||
bq->setDefaultCapacity(BQ27220_DESIGN_CAPACITY);
|
||||
|
||||
bool result = bq->init();
|
||||
if (result) {
|
||||
LOG_DEBUG("BQ27220 design capacity: %d", bq->getDesignCapacity());
|
||||
LOG_DEBUG("BQ27220 fullCharge capacity: %d", bq->getFullChargeCapacity());
|
||||
LOG_DEBUG("BQ27220 remaining capacity: %d", bq->getRemainingCapacity());
|
||||
return true;
|
||||
} else {
|
||||
LOG_WARN("BQ27220 init failed");
|
||||
delete bq;
|
||||
bq = nullptr;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Battery state of charge, from 0 to 100 or -1 for unknown
|
||||
*/
|
||||
virtual int getBatteryPercent() override
|
||||
{
|
||||
return -1;
|
||||
// return bq->getChargePercent(); // don't use BQ27220 for battery percent, it is not calibrated
|
||||
}
|
||||
|
||||
/**
|
||||
* The raw voltage of the battery in millivolts, or NAN if unknown
|
||||
*/
|
||||
virtual uint16_t getBattVoltage() override { return bq->getVoltage(); }
|
||||
|
||||
/**
|
||||
* return true if there is a battery installed in this unit
|
||||
*/
|
||||
virtual bool isBatteryConnect() override { return PPM->getBattVoltage() > 0; }
|
||||
|
||||
/**
|
||||
* return true if there is an external power source detected
|
||||
*/
|
||||
virtual bool isVbusIn() override { return PPM->getVbusVoltage() > 0; }
|
||||
|
||||
/**
|
||||
* return true if the battery is currently charging
|
||||
*/
|
||||
virtual bool isCharging() override
|
||||
{
|
||||
bool isCharging = PPM->isCharging();
|
||||
if (isCharging) {
|
||||
LOG_DEBUG("BQ27220 time to full charge: %d min", bq->getTimeToFull());
|
||||
} else {
|
||||
if (!PPM->isVbusIn()) {
|
||||
LOG_DEBUG("BQ27220 time to empty: %d min (%d mAh)", bq->getTimeToEmpty(), bq->getRemainingCapacity());
|
||||
}
|
||||
}
|
||||
return isCharging;
|
||||
}
|
||||
};
|
||||
|
||||
LipoCharger lipoCharger;
|
||||
|
||||
/**
|
||||
* Init the Lipo battery charger
|
||||
*/
|
||||
bool Power::lipoChargerInit()
|
||||
{
|
||||
bool result = lipoCharger.runOnce();
|
||||
LOG_DEBUG("Power::lipoChargerInit lipo sensor is %s", result ? "ready" : "not ready yet");
|
||||
if (!result)
|
||||
return false;
|
||||
batteryLevel = &lipoCharger;
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
/**
|
||||
* The Lipo battery level sensor is unavailable - default to AnalogBatteryLevel
|
||||
*/
|
||||
bool Power::lipoChargerInit()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HELTEC_MESH_SOLAR
|
||||
#include "meshSolarApp.h"
|
||||
|
||||
/**
|
||||
* meshSolar class for an SMBUS battery sensor.
|
||||
*/
|
||||
class meshSolarBatteryLevel : public HasBatteryLevel
|
||||
{
|
||||
|
||||
public:
|
||||
/**
|
||||
* Init the I2C meshSolar battery level sensor
|
||||
*/
|
||||
bool runOnce()
|
||||
{
|
||||
meshSolarStart();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Battery state of charge, from 0 to 100 or -1 for unknown
|
||||
*/
|
||||
virtual int getBatteryPercent() override { return meshSolarGetBatteryPercent(); }
|
||||
|
||||
/**
|
||||
* The raw voltage of the battery in millivolts, or NAN if unknown
|
||||
*/
|
||||
virtual uint16_t getBattVoltage() override { return meshSolarGetBattVoltage(); }
|
||||
|
||||
/**
|
||||
* return true if there is a battery installed in this unit
|
||||
*/
|
||||
virtual bool isBatteryConnect() override { return meshSolarIsBatteryConnect(); }
|
||||
|
||||
/**
|
||||
* return true if there is an external power source detected
|
||||
*/
|
||||
virtual bool isVbusIn() override { return meshSolarIsVbusIn(); }
|
||||
|
||||
/**
|
||||
* return true if the battery is currently charging
|
||||
*/
|
||||
virtual bool isCharging() override { return meshSolarIsCharging(); }
|
||||
};
|
||||
|
||||
meshSolarBatteryLevel meshSolarLevel;
|
||||
|
||||
/**
|
||||
* Init the meshSolar battery level sensor
|
||||
*/
|
||||
bool Power::meshSolarInit()
|
||||
{
|
||||
bool result = meshSolarLevel.runOnce();
|
||||
LOG_DEBUG("Power::meshSolarInit mesh solar sensor is %s", result ? "ready" : "not ready yet");
|
||||
if (!result)
|
||||
return false;
|
||||
batteryLevel = &meshSolarLevel;
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
/**
|
||||
* The meshSolar battery level sensor is unavailable - default to AnalogBatteryLevel
|
||||
*/
|
||||
bool Power::meshSolarInit()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
@@ -72,7 +72,7 @@ extern Power *power;
|
||||
static void shutdownEnter()
|
||||
{
|
||||
LOG_DEBUG("State: SHUTDOWN");
|
||||
power->shutdown();
|
||||
shutdownAtMsec = millis();
|
||||
}
|
||||
|
||||
#include "error.h"
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
#include "memGet.h"
|
||||
#include "mesh/generated/meshtastic/mesh.pb.h"
|
||||
#include <assert.h>
|
||||
#include <cstring>
|
||||
@@ -57,7 +58,7 @@ size_t RedirectablePrint::vprintf(const char *logLevel, const char *format, va_l
|
||||
#endif
|
||||
|
||||
#ifdef ARCH_PORTDUINO
|
||||
bool color = !settingsMap[ascii_logs];
|
||||
bool color = !portduino_config.ascii_logs;
|
||||
#else
|
||||
bool color = true;
|
||||
#endif
|
||||
@@ -99,7 +100,7 @@ void RedirectablePrint::log_to_serial(const char *logLevel, const char *format,
|
||||
size_t r = 0;
|
||||
|
||||
#ifdef ARCH_PORTDUINO
|
||||
bool color = !settingsMap[ascii_logs];
|
||||
bool color = !portduino_config.ascii_logs;
|
||||
#else
|
||||
bool color = true;
|
||||
#endif
|
||||
@@ -166,6 +167,16 @@ void RedirectablePrint::log_to_serial(const char *logLevel, const char *format,
|
||||
print(thread->ThreadName);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -288,7 +299,7 @@ void RedirectablePrint::log(const char *logLevel, const char *format, ...)
|
||||
#if ARCH_PORTDUINO
|
||||
// level trace is special, two possible ways to handle it.
|
||||
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0) {
|
||||
if (settingsStrings[traceFilename] != "") {
|
||||
if (portduino_config.traceFilename != "") {
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
try {
|
||||
@@ -297,18 +308,18 @@ void RedirectablePrint::log(const char *logLevel, const char *format, ...)
|
||||
}
|
||||
va_end(arg);
|
||||
}
|
||||
if (settingsMap[logoutputlevel] < level_trace && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0) {
|
||||
if (portduino_config.logoutputlevel < level_trace && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0) {
|
||||
delete[] newFormat;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (settingsMap[logoutputlevel] < level_debug && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) {
|
||||
if (portduino_config.logoutputlevel < level_debug && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) {
|
||||
delete[] newFormat;
|
||||
return;
|
||||
} else if (settingsMap[logoutputlevel] < level_info && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0) {
|
||||
} else if (portduino_config.logoutputlevel < level_info && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0) {
|
||||
delete[] newFormat;
|
||||
return;
|
||||
} else if (settingsMap[logoutputlevel] < level_warn && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0) {
|
||||
} else if (portduino_config.logoutputlevel < level_warn && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0) {
|
||||
delete[] newFormat;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,14 @@
|
||||
#include "configuration.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
|
||||
#define Port Serial2
|
||||
#else
|
||||
@@ -22,7 +30,12 @@ SerialConsole *console;
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -64,7 +77,22 @@ SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port), con
|
||||
|
||||
int32_t SerialConsole::runOnce()
|
||||
{
|
||||
return runOncePart();
|
||||
#ifdef HELTEC_MESH_SOLAR
|
||||
// After enabling the mesh solar serial port module configuration, command processing is handled by the serial port module.
|
||||
if (moduleConfig.serial.enabled && moduleConfig.serial.override_console_serial_port &&
|
||||
moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MS_CONFIG) {
|
||||
return 250;
|
||||
}
|
||||
#endif
|
||||
|
||||
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()
|
||||
@@ -72,6 +100,18 @@ void SerialConsole::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
|
||||
bool SerialConsole::checkIsConnected()
|
||||
{
|
||||
|
||||
@@ -32,11 +32,14 @@ class SerialConsole : public StreamAPI, public RedirectablePrint, private concur
|
||||
virtual int32_t runOnce() override;
|
||||
|
||||
void flush();
|
||||
void rxInt();
|
||||
|
||||
protected:
|
||||
/// Check the current underlying physical link to see if the client is currently connected
|
||||
virtual bool checkIsConnected() override;
|
||||
|
||||
virtual void onNowHasData(uint32_t fromRadioNum) override;
|
||||
|
||||
/// 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);
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
BuzzerFeedbackThread *buzzerFeedbackThread;
|
||||
|
||||
BuzzerFeedbackThread::BuzzerFeedbackThread() : OSThread("BuzzerFeedback")
|
||||
BuzzerFeedbackThread::BuzzerFeedbackThread()
|
||||
{
|
||||
if (inputBroker)
|
||||
inputObserver.observe(inputBroker);
|
||||
@@ -15,24 +15,24 @@ int BuzzerFeedbackThread::handleInputEvent(const InputEvent *event)
|
||||
{
|
||||
// Only provide feedback if buzzer is enabled for notifications
|
||||
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
|
||||
}
|
||||
|
||||
// Track last event time for potential future use
|
||||
lastEventTime = millis();
|
||||
needsUpdate = true;
|
||||
|
||||
// Handle different input events with appropriate buzzer feedback
|
||||
switch (event->inputEvent) {
|
||||
case INPUT_BROKER_USER_PRESS:
|
||||
case INPUT_BROKER_ALT_PRESS:
|
||||
case INPUT_BROKER_SELECT:
|
||||
case INPUT_BROKER_SELECT_LONG:
|
||||
playBeep(); // Confirmation feedback
|
||||
break;
|
||||
|
||||
case INPUT_BROKER_UP:
|
||||
case INPUT_BROKER_UP_LONG:
|
||||
case INPUT_BROKER_DOWN:
|
||||
case INPUT_BROKER_DOWN_LONG:
|
||||
case INPUT_BROKER_LEFT:
|
||||
case INPUT_BROKER_RIGHT:
|
||||
playChirp(); // Navigation feedback
|
||||
@@ -47,10 +47,6 @@ int BuzzerFeedbackThread::handleInputEvent(const InputEvent *event)
|
||||
playComboTune(); // Ping sent feedback
|
||||
break;
|
||||
|
||||
case INPUT_BROKER_SHUTDOWN:
|
||||
playShutdownMelody(); // Shutdown feedback
|
||||
break;
|
||||
|
||||
default:
|
||||
// For other events, check if it's a printable character
|
||||
if (event->kbchar >= 32 && event->kbchar <= 126) {
|
||||
@@ -62,18 +58,4 @@ int BuzzerFeedbackThread::handleInputEvent(const InputEvent *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
|
||||
|
||||
if (needsUpdate) {
|
||||
needsUpdate = false;
|
||||
// Could add any periodic processing here
|
||||
}
|
||||
|
||||
// Run every 100ms when active, less frequently when idle
|
||||
return needsUpdate ? 100 : 1000;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "input/InputBroker.h"
|
||||
|
||||
class BuzzerFeedbackThread : public concurrency::OSThread
|
||||
class BuzzerFeedbackThread
|
||||
{
|
||||
CallbackObserver<BuzzerFeedbackThread, const InputEvent *> inputObserver =
|
||||
CallbackObserver<BuzzerFeedbackThread, const InputEvent *>(this, &BuzzerFeedbackThread::handleInputEvent);
|
||||
@@ -12,13 +12,6 @@ class BuzzerFeedbackThread : public concurrency::OSThread
|
||||
public:
|
||||
BuzzerFeedbackThread();
|
||||
int handleInputEvent(const InputEvent *event);
|
||||
|
||||
protected:
|
||||
virtual int32_t runOnce() override;
|
||||
|
||||
private:
|
||||
uint32_t lastEventTime = 0;
|
||||
bool needsUpdate = false;
|
||||
};
|
||||
|
||||
extern BuzzerFeedbackThread *buzzerFeedbackThread;
|
||||
|
||||
@@ -140,6 +140,10 @@ bool playNextLeadUpNote()
|
||||
playTones(¬e, 1); // Play single note using existing playTones function
|
||||
|
||||
leadUpNoteIndex++;
|
||||
|
||||
if (leadUpNoteIndex >= leadUpNotesCount) {
|
||||
return false; // this was the final note
|
||||
}
|
||||
return true; // Note was played (playTones handles buzzer availability internally)
|
||||
}
|
||||
|
||||
|
||||
@@ -86,11 +86,13 @@ void OSThread::run()
|
||||
#ifdef DEBUG_HEAP
|
||||
auto newHeap = memGet.getFreeHeap();
|
||||
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)
|
||||
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
|
||||
|
||||
runned();
|
||||
|
||||
if (newDelay >= 0)
|
||||
|
||||
@@ -26,13 +26,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifdef RV3028_RTC
|
||||
#if __has_include("Melopero_RV3028.h")
|
||||
#include "Melopero_RV3028.h"
|
||||
#endif
|
||||
#ifdef PCF8563_RTC
|
||||
#if __has_include("pcf8563.h")
|
||||
#include "pcf8563.h"
|
||||
#endif
|
||||
|
||||
/* Offer chance for variant-specific defines */
|
||||
#include "variant.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Version
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -117,6 +120,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define SX126X_MAX_POWER 22
|
||||
#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 STATION_G2
|
||||
#define NUM_PA_POINTS 19
|
||||
#define TX_GAIN_LORA 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 19, 19, 18, 18
|
||||
#endif
|
||||
|
||||
// Default system gain to 0 if not defined
|
||||
#ifndef TX_GAIN_LORA
|
||||
#define TX_GAIN_LORA 0
|
||||
@@ -135,7 +149,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
// -----------------------------------------------------------------------------
|
||||
// OLED & Input
|
||||
// -----------------------------------------------------------------------------
|
||||
#if defined(SEEED_WIO_TRACKER_L1)
|
||||
#if defined(SEEED_WIO_TRACKER_L1) && !defined(SEEED_WIO_TRACKER_L1_EINK)
|
||||
#define SSD1306_ADDRESS 0x3D
|
||||
#define USE_SH1106
|
||||
#else
|
||||
@@ -150,11 +164,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
// Define if screen should be mirrored left to right
|
||||
// #define SCREEN_MIRROR
|
||||
|
||||
// I2C Keyboards (M5Stack, RAK14004, T-Deck)
|
||||
// I2C Keyboards (M5Stack, RAK14004, T-Deck, T-Deck Pro, T-Lora Pager, CardKB, BBQ10, MPR121, TCA8418)
|
||||
#define CARDKB_ADDR 0x5F
|
||||
#define TDECK_KB_ADDR 0x55
|
||||
#define BBQ10_KB_ADDR 0x1F
|
||||
#define MPR121_KB_ADDR 0x5A
|
||||
#define TCA8418_KB_ADDR 0x34
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// SENSOR
|
||||
@@ -193,8 +208,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define MLX90614_ADDR_DEF 0x5A
|
||||
#define CGRADSENS_ADDR 0x66
|
||||
#define LTR390UV_ADDR 0x53
|
||||
#define XPOWERS_AXP192_AXP2101_ADDRESS 0x34 // same adress as TCA8418
|
||||
#define XPOWERS_AXP192_AXP2101_ADDRESS 0x34 // same adress as TCA8418_KB
|
||||
#define PCT2075_ADDR 0x37
|
||||
#define BQ27220_ADDR 0x55 // same address as TDECK_KB
|
||||
#define BQ25896_ADDR 0x6B
|
||||
#define LTR553ALS_ADDR 0x23
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// ACCELEROMETER
|
||||
@@ -208,6 +226,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define BMX160_ADDR 0x69
|
||||
#define ICM20948_ADDR 0x69
|
||||
#define ICM20948_ADDR_ALT 0x68
|
||||
#define BHI260AP_ADDR 0x28
|
||||
#define BMM150_ADDR 0x13
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -230,6 +249,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
// Touchscreen
|
||||
// -----------------------------------------------------------------------------
|
||||
#define FT6336U_ADDR 0x48
|
||||
#define CST328_ADDR 0x1A
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// RAK12035VB Soil Monitor (using RAK12023 up to 3 RAK12035 monitors can be connected)
|
||||
@@ -248,14 +268,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
// convert 24-bit color to 16-bit (56K)
|
||||
#define COLOR565(r, g, b) (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3))
|
||||
|
||||
/* Step #1: offer chance for variant-specific defines */
|
||||
#include "variant.h"
|
||||
|
||||
#if defined(VEXT_ENABLE) && !defined(VEXT_ON_VALUE)
|
||||
// Older variant.h files might not be defining this value, so stay with the old default
|
||||
#define VEXT_ON_VALUE LOW
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Rotary encoder
|
||||
// -----------------------------------------------------------------------------
|
||||
#ifndef ROTARY_DELAY
|
||||
#define ROTARY_DELAY 5
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// GPS
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@@ -25,8 +25,8 @@ ScanI2C::FoundDevice ScanI2C::firstScreen() const
|
||||
|
||||
ScanI2C::FoundDevice ScanI2C::firstRTC() const
|
||||
{
|
||||
ScanI2C::DeviceType types[] = {RTC_RV3028, RTC_PCF8563};
|
||||
return firstOfOrNONE(2, types);
|
||||
ScanI2C::DeviceType types[] = {RTC_RV3028, RTC_PCF8563, RTC_RX8130CE};
|
||||
return firstOfOrNONE(3, types);
|
||||
}
|
||||
|
||||
ScanI2C::FoundDevice ScanI2C::firstKeyboard() const
|
||||
|
||||
@@ -14,6 +14,7 @@ class ScanI2C
|
||||
SCREEN_ST7567,
|
||||
RTC_RV3028,
|
||||
RTC_PCF8563,
|
||||
RTC_RX8130CE,
|
||||
CARDKB,
|
||||
TDECKKB,
|
||||
BBQ10KB,
|
||||
@@ -74,7 +75,14 @@ class ScanI2C
|
||||
RAK12035,
|
||||
TCA8418KB,
|
||||
PCT2075,
|
||||
CST328,
|
||||
BQ25896,
|
||||
BQ27220,
|
||||
LTR553ALS,
|
||||
BHI260AP,
|
||||
BMM150,
|
||||
TSL2561,
|
||||
DRV2605
|
||||
} DeviceType;
|
||||
|
||||
// typedef uint8_t DeviceAddress;
|
||||
|
||||
@@ -184,14 +184,22 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
||||
type = RTC_RV3028;
|
||||
logFoundDevice("RV3028", (uint8_t)addr.address);
|
||||
rtc.initI2C(*i2cBus);
|
||||
rtc.writeToRegister(0x35, 0x07); // no Clkout
|
||||
rtc.writeToRegister(0x37, 0xB4);
|
||||
// Update RTC EEPROM settings, if necessary
|
||||
if (rtc.readEEPROMRegister(0x35) != 0x07) {
|
||||
rtc.writeEEPROMRegister(0x35, 0x07); // no Clkout
|
||||
}
|
||||
if (rtc.readEEPROMRegister(0x37) != 0xB4) {
|
||||
rtc.writeEEPROMRegister(0x37, 0xB4);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef PCF8563_RTC
|
||||
SCAN_SIMPLE_CASE(PCF8563_RTC, RTC_PCF8563, "PCF8563", (uint8_t)addr.address)
|
||||
#endif
|
||||
#ifdef RX8130CE_RTC
|
||||
SCAN_SIMPLE_CASE(RX8130CE_RTC, RTC_RX8130CE, "RX8130CE", (uint8_t)addr.address)
|
||||
#endif
|
||||
|
||||
case CARDKB_ADDR:
|
||||
// Do we have the RAK14006 instead?
|
||||
@@ -206,7 +214,17 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
||||
}
|
||||
break;
|
||||
|
||||
SCAN_SIMPLE_CASE(TDECK_KB_ADDR, TDECKKB, "T-Deck keyboard", (uint8_t)addr.address);
|
||||
case TDECK_KB_ADDR:
|
||||
// Do we have the T-Deck keyboard or the T-Deck Pro battery sensor?
|
||||
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x04), 1);
|
||||
if (registerValue != 0) {
|
||||
logFoundDevice("BQ27220", (uint8_t)addr.address);
|
||||
type = BQ27220;
|
||||
} else {
|
||||
logFoundDevice("TDECKKB", (uint8_t)addr.address);
|
||||
type = TDECKKB;
|
||||
}
|
||||
break;
|
||||
SCAN_SIMPLE_CASE(BBQ10_KB_ADDR, BBQ10KB, "BB Q10", (uint8_t)addr.address);
|
||||
|
||||
SCAN_SIMPLE_CASE(ST7567_ADDRESS, SCREEN_ST7567, "ST7567", (uint8_t)addr.address);
|
||||
@@ -279,6 +297,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
||||
type = AHT10;
|
||||
break;
|
||||
#endif
|
||||
#if !defined(M5STACK_UNITC6L)
|
||||
case INA_ADDR:
|
||||
case INA_ADDR_ALTERNATE:
|
||||
case INA_ADDR_WAVESHARE_UPS:
|
||||
@@ -325,6 +344,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
||||
// else: probably a RAK12500/UBLOX GPS on I2C
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
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
|
||||
// weird result. and register 0x00 doesn't seems to be colliding with MCP9808 and LIS3DH chips.
|
||||
@@ -358,7 +378,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
||||
case SHT31_4x_ADDR: // same as OPT3001_ADDR_ALT
|
||||
case SHT31_4x_ADDR_ALT: // same as OPT3001_ADDR
|
||||
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;
|
||||
logFoundDevice("SHT4X", (uint8_t)addr.address);
|
||||
} else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2) == 0x5449) {
|
||||
@@ -396,6 +416,12 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
||||
logFoundDevice("BQ24295", (uint8_t)addr.address);
|
||||
break;
|
||||
}
|
||||
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x14), 1); // get ID
|
||||
if ((registerValue & 0b00000011) == 0b00000010) {
|
||||
type = BQ25896;
|
||||
logFoundDevice("BQ25896", (uint8_t)addr.address);
|
||||
break;
|
||||
}
|
||||
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0F), 1); // get ID
|
||||
if (registerValue == 0x6A) {
|
||||
type = LSM6DS3;
|
||||
@@ -440,13 +466,26 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
||||
SCAN_SIMPLE_CASE(LSM6DS3_ADDR, LSM6DS3, "LSM6DS3", (uint8_t)addr.address);
|
||||
SCAN_SIMPLE_CASE(TCA9555_ADDR, TCA9555, "TCA9555", (uint8_t)addr.address);
|
||||
SCAN_SIMPLE_CASE(VEML7700_ADDR, VEML7700, "VEML7700", (uint8_t)addr.address);
|
||||
SCAN_SIMPLE_CASE(TSL25911_ADDR, TSL2591, "TSL2591", (uint8_t)addr.address);
|
||||
case TSL25911_ADDR:
|
||||
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x12), 1);
|
||||
if (registerValue == 0x50) {
|
||||
type = TSL2591;
|
||||
logFoundDevice("TSL25911", (uint8_t)addr.address);
|
||||
} else {
|
||||
type = TSL2561;
|
||||
logFoundDevice("TSL2561", (uint8_t)addr.address);
|
||||
}
|
||||
break;
|
||||
|
||||
SCAN_SIMPLE_CASE(MLX90632_ADDR, MLX90632, "MLX90632", (uint8_t)addr.address);
|
||||
SCAN_SIMPLE_CASE(NAU7802_ADDR, NAU7802, "NAU7802", (uint8_t)addr.address);
|
||||
SCAN_SIMPLE_CASE(MAX1704X_ADDR, MAX17048, "MAX17048", (uint8_t)addr.address);
|
||||
SCAN_SIMPLE_CASE(DFROBOT_RAIN_ADDR, DFROBOT_RAIN, "DFRobot Rain Gauge", (uint8_t)addr.address);
|
||||
SCAN_SIMPLE_CASE(LTR390UV_ADDR, LTR390UV, "LTR390UV", (uint8_t)addr.address);
|
||||
SCAN_SIMPLE_CASE(PCT2075_ADDR, PCT2075, "PCT2075", (uint8_t)addr.address);
|
||||
SCAN_SIMPLE_CASE(CST328_ADDR, CST328, "CST328", (uint8_t)addr.address);
|
||||
SCAN_SIMPLE_CASE(LTR553ALS_ADDR, LTR553ALS, "LTR553ALS", (uint8_t)addr.address);
|
||||
SCAN_SIMPLE_CASE(BHI260AP_ADDR, BHI260AP, "BHI260AP", (uint8_t)addr.address);
|
||||
SCAN_SIMPLE_CASE(SCD4X_ADDR, SCD4X, "SCD4X", (uint8_t)addr.address);
|
||||
SCAN_SIMPLE_CASE(BMM150_ADDR, BMM150, "BMM150", (uint8_t)addr.address);
|
||||
#ifdef HAS_TPS65233
|
||||
@@ -459,8 +498,14 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
||||
type = MLX90614;
|
||||
logFoundDevice("MLX90614", (uint8_t)addr.address);
|
||||
} else {
|
||||
type = MPR121KB;
|
||||
logFoundDevice("MPR121KB", (uint8_t)addr.address);
|
||||
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1); // DRV2605_REG_STATUS
|
||||
if (registerValue == 0xe0) {
|
||||
type = DRV2605;
|
||||
logFoundDevice("DRV2605", (uint8_t)addr.address);
|
||||
} else {
|
||||
type = MPR121KB;
|
||||
logFoundDevice("MPR121KB", (uint8_t)addr.address);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
579
src/gps/GPS.cpp
579
src/gps/GPS.cpp
@@ -1,5 +1,4 @@
|
||||
#include <cstring> // Include for strstr
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "configuration.h"
|
||||
@@ -39,9 +38,9 @@ template <typename T, std::size_t N> std::size_t array_count(const T (&)[N])
|
||||
return N;
|
||||
}
|
||||
|
||||
#if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)
|
||||
#if defined(RAK2560)
|
||||
HardwareSerial *GPS::_serial_gps = &Serial2;
|
||||
#if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) || defined(ARCH_STM32WL)
|
||||
#if defined(GPS_SERIAL_PORT)
|
||||
HardwareSerial *GPS::_serial_gps = &GPS_SERIAL_PORT;
|
||||
#else
|
||||
HardwareSerial *GPS::_serial_gps = &Serial1;
|
||||
#endif
|
||||
@@ -507,26 +506,26 @@ bool GPS::setup()
|
||||
delay(1000);
|
||||
#endif
|
||||
if (probeTries < GPS_PROBETRIES) {
|
||||
LOG_DEBUG("Probe for GPS at %d", serialSpeeds[speedSelect]);
|
||||
gnssModel = probe(serialSpeeds[speedSelect]);
|
||||
if (gnssModel == GNSS_MODEL_UNKNOWN) {
|
||||
if (++speedSelect == array_count(serialSpeeds)) {
|
||||
if (currentStep == 0 && ++speedSelect == array_count(serialSpeeds)) {
|
||||
speedSelect = 0;
|
||||
++probeTries;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Rare Serial Speeds
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C6
|
||||
if (probeTries == GPS_PROBETRIES) {
|
||||
LOG_DEBUG("Probe for GPS at %d", rareSerialSpeeds[speedSelect]);
|
||||
gnssModel = probe(rareSerialSpeeds[speedSelect]);
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (gnssModel != GNSS_MODEL_UNKNOWN) {
|
||||
@@ -643,8 +642,16 @@ bool GPS::setup()
|
||||
delay(250);
|
||||
} else if (IS_ONE_OF(gnssModel, GNSS_MODEL_AG3335, GNSS_MODEL_AG3352)) {
|
||||
|
||||
_serial_gps->write("$PAIR066,1,0,1,0,0,1*3B\r\n"); // Enable GPS+GALILEO+NAVIC
|
||||
|
||||
if (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_IN ||
|
||||
config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_NP_865) {
|
||||
_serial_gps->write("$PAIR066,1,0,1,0,0,1*3B\r\n"); // Enable GPS+GALILEO+NAVIC
|
||||
// GPS GLONASS GALILEO BDS QZSS NAVIC
|
||||
// 1 0 1 0 0 1
|
||||
} else {
|
||||
_serial_gps->write("$PAIR066,1,1,1,1,0,0*3A\r\n"); // Enable GPS+GLONASS+GALILEO+BDS
|
||||
// GPS GLONASS GALILEO BDS QZSS NAVIC
|
||||
// 1 1 1 1 0 0
|
||||
}
|
||||
// Configure NMEA (sentences will output once per fix)
|
||||
_serial_gps->write("$PAIR062,0,1*3F\r\n"); // GGA ON
|
||||
_serial_gps->write("$PAIR062,1,0*3F\r\n"); // GLL OFF
|
||||
@@ -800,6 +807,14 @@ bool GPS::setup()
|
||||
} else {
|
||||
LOG_INFO("GNSS module configuration saved!");
|
||||
}
|
||||
} else if (gnssModel == GNSS_MODEL_CM121) {
|
||||
// only ask for RMC and GGA
|
||||
// enable GGA
|
||||
_serial_gps->write("$CFGMSG,0,0,1,1*1B\r\n");
|
||||
delay(250);
|
||||
// enable RMC
|
||||
_serial_gps->write("$CFGMSG,0,4,1,1*1F\r\n");
|
||||
delay(250);
|
||||
}
|
||||
didSerialInit = true;
|
||||
}
|
||||
@@ -835,9 +850,6 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime)
|
||||
setPowerPMU(true); // Power (PMU): on
|
||||
writePinStandby(false); // Standby (pin): awake (not standby)
|
||||
setPowerUBLOX(true); // Standby (UBLOX): awake
|
||||
#ifdef GNSS_AIROHA
|
||||
lastFixStartMsec = 0;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case GPS_SOFTSLEEP:
|
||||
@@ -855,9 +867,7 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime)
|
||||
writePinStandby(true); // Standby (pin): asleep (not awake)
|
||||
setPowerUBLOX(false, sleepTime); // Standby (UBLOX): asleep, timed
|
||||
#ifdef GNSS_AIROHA
|
||||
if (config.position.gps_update_interval * 1000 >= GPS_FIX_HOLD_TIME * 2) {
|
||||
digitalWrite(PIN_GPS_EN, LOW);
|
||||
}
|
||||
digitalWrite(PIN_GPS_EN, LOW);
|
||||
#endif
|
||||
break;
|
||||
|
||||
@@ -869,9 +879,7 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime)
|
||||
writePinStandby(true); // Standby (pin): asleep
|
||||
setPowerUBLOX(false, 0); // Standby (UBLOX): asleep, indefinitely
|
||||
#ifdef GNSS_AIROHA
|
||||
if (config.position.gps_update_interval * 1000 >= GPS_FIX_HOLD_TIME * 2) {
|
||||
digitalWrite(PIN_GPS_EN, LOW);
|
||||
}
|
||||
digitalWrite(PIN_GPS_EN, LOW);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@@ -1023,7 +1031,7 @@ void GPS::down()
|
||||
LOG_DEBUG("%us until next search", sleepTime / 1000);
|
||||
|
||||
// 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);
|
||||
|
||||
else {
|
||||
@@ -1054,6 +1062,8 @@ void GPS::down()
|
||||
}
|
||||
// If update interval long enough (or softsleep unsupported): hardsleep instead
|
||||
setPowerState(GPS_HARDSLEEP, sleepTime);
|
||||
// Reset the fix quality to 0, since we're off.
|
||||
fixQual = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1082,7 +1092,7 @@ int32_t GPS::runOnce()
|
||||
return disable();
|
||||
}
|
||||
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
|
||||
if (config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
|
||||
@@ -1092,11 +1102,29 @@ int32_t GPS::runOnce()
|
||||
publishUpdate();
|
||||
}
|
||||
|
||||
// Repeaters have no need for GPS
|
||||
if (config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) {
|
||||
return disable();
|
||||
}
|
||||
|
||||
// ======================== GPS_ACTIVE state ========================
|
||||
// In GPS_ACTIVE state, GPS is powered on and we're receiving NMEA messages.
|
||||
// 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 we have received valid NMEA claim we are connected
|
||||
setConnected();
|
||||
@@ -1106,42 +1134,81 @@ int32_t GPS::runOnce()
|
||||
if (!config.position.fixed_position && powerState != GPS_ACTIVE && scheduling.isUpdateDue())
|
||||
up();
|
||||
|
||||
// If we've already set time from the GPS, no need to ask the GPS
|
||||
bool gotTime = (getRTCQuality() >= RTCQualityGPS);
|
||||
if (!gotTime && lookForTime()) { // Note: we count on this && short-circuiting and not resetting the RTC time
|
||||
gotTime = true;
|
||||
shouldPublish = true;
|
||||
}
|
||||
// quality of the previous fix. We set it to 0 when we go down, so it's a way
|
||||
// to check if we're getting a lock after being GPS_OFF.
|
||||
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;
|
||||
}
|
||||
if (powerState == GPS_ACTIVE) {
|
||||
// if gps_update_interval is <=10s, GPS never goes off, so we treat that differently
|
||||
uint32_t updateInterval = Default::getConfiguredOrDefaultMs(config.position.gps_update_interval);
|
||||
|
||||
bool tooLong = scheduling.searchedTooLong();
|
||||
if (tooLong)
|
||||
LOG_WARN("Couldn't publish a valid location: didn't get a GPS lock in time");
|
||||
|
||||
// Once we get a location we no longer desperately want an update
|
||||
if ((gotLoc && gotTime) || tooLong) {
|
||||
|
||||
if (tooLong) {
|
||||
// we didn't get a location during this ack window, therefore declare loss of lock
|
||||
if (hasValidLocation) {
|
||||
LOG_DEBUG("hasValidLocation FALLING EDGE");
|
||||
}
|
||||
p = meshtastic_Position_init_default;
|
||||
hasValidLocation = false;
|
||||
// 1. Got a time for the first time
|
||||
bool gotTime = (getRTCQuality() >= RTCQualityGPS);
|
||||
if (!gotTime && lookForTime()) { // Note: we count on this && short-circuiting and not resetting the RTC time
|
||||
gotTime = true;
|
||||
}
|
||||
|
||||
down();
|
||||
shouldPublish = true; // publish our update for this just finished acquisition window
|
||||
}
|
||||
// 2. Got a lock for the first time, or 3. Got a lock after turning back on
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// If state has changed do a publish
|
||||
publishUpdate();
|
||||
bool tooLong = scheduling.searchedTooLong();
|
||||
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
|
||||
if (hasValidLocation) {
|
||||
p = meshtastic_Position_init_default;
|
||||
hasValidLocation = false;
|
||||
shouldPublish = true;
|
||||
#ifdef GPS_DEBUG
|
||||
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);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
// ===================== end GPS_ACTIVE state ========================
|
||||
|
||||
if (config.position.fixed_position == true && hasValidLocation)
|
||||
return disable(); // This should trigger when we have a fixed position, and get that first position
|
||||
@@ -1190,7 +1257,7 @@ static const char *DETECTED_MESSAGE = "%s detected";
|
||||
LOG_DEBUG(PROBE_MESSAGE, COMMAND, FAMILY_NAME); \
|
||||
clearBuffer(); \
|
||||
_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) { \
|
||||
return detectedDriver; \
|
||||
} \
|
||||
@@ -1198,190 +1265,249 @@ static const char *DETECTED_MESSAGE = "%s detected";
|
||||
|
||||
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)
|
||||
_serial_gps->end();
|
||||
_serial_gps->begin(serialSpeed);
|
||||
_serial_gps->end();
|
||||
_serial_gps->begin(serialSpeed);
|
||||
#elif defined(ARCH_RP2040)
|
||||
_serial_gps->end();
|
||||
_serial_gps->setFIFOSize(256);
|
||||
_serial_gps->begin(serialSpeed);
|
||||
_serial_gps->end();
|
||||
_serial_gps->setFIFOSize(256);
|
||||
_serial_gps->begin(serialSpeed);
|
||||
#else
|
||||
if (_serial_gps->baudRate() != serialSpeed) {
|
||||
LOG_DEBUG("Set Baud to %i", serialSpeed);
|
||||
_serial_gps->updateBaudRate(serialSpeed);
|
||||
}
|
||||
if (_serial_gps->baudRate() != serialSpeed) {
|
||||
LOG_DEBUG("Set GPS Baud to %i", serialSpeed);
|
||||
_serial_gps->updateBaudRate(serialSpeed);
|
||||
}
|
||||
#endif
|
||||
|
||||
memset(&ublox_info, 0, sizeof(ublox_info));
|
||||
uint8_t buffer[768] = {0};
|
||||
delay(100);
|
||||
memset(&ublox_info, 0, sizeof(ublox_info));
|
||||
delay(100);
|
||||
|
||||
// Close all NMEA sentences, valid for L76K, ATGM336H (and likely other AT6558 devices)
|
||||
_serial_gps->write("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n");
|
||||
delay(20);
|
||||
// Close NMEA sequences on Ublox
|
||||
_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);
|
||||
|
||||
// Unicore UFirebirdII Series: UC6580, UM620, UM621, UM670A, UM680A, or UM681A
|
||||
std::vector<ChipInfo> unicore = {{"UC6580", "UC6580", GNSS_MODEL_UC6580}, {"UM600", "UM600", GNSS_MODEL_UC6580}};
|
||||
PROBE_FAMILY("Unicore Family", "$PDTINFO", unicore, 500);
|
||||
|
||||
std::vector<ChipInfo> atgm = {
|
||||
{"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);
|
||||
|
||||
/* 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);
|
||||
|
||||
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);
|
||||
|
||||
// Close all NMEA sentences, valid for MTK3333 and MTK3339 platforms
|
||||
_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");
|
||||
delay(20);
|
||||
std::vector<ChipInfo> mtk = {{"L76B", "Quectel-L76B", GNSS_MODEL_MTK_L76B}, {"PA1010D", "1010D", GNSS_MODEL_MTK_PA1010D},
|
||||
{"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);
|
||||
// Close all NMEA sentences, valid for L76K, ATGM336H (and likely other AT6558 devices)
|
||||
_serial_gps->write("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n");
|
||||
delay(20);
|
||||
// Close NMEA sequences on Ublox
|
||||
_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");
|
||||
currentDelay = 20;
|
||||
currentStep = 1;
|
||||
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));
|
||||
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));
|
||||
// Unicore UFirebirdII Series: UC6580, UM620, UM621, UM670A, UM680A, or UM681A,or CM121
|
||||
std::vector<ChipInfo> unicore = {
|
||||
{"UC6580", "UC6580", GNSS_MODEL_UC6580}, {"UM600", "UM600", GNSS_MODEL_UC6580}, {"CM121", "CM121", GNSS_MODEL_CM121}};
|
||||
PROBE_FAMILY("Unicore Family", "$PDTINFO", unicore, 500);
|
||||
currentDelay = 20;
|
||||
currentStep = 2;
|
||||
return GNSS_MODEL_UNKNOWN;
|
||||
}
|
||||
case 2: {
|
||||
std::vector<ChipInfo> atgm = {
|
||||
{"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);
|
||||
if (len) {
|
||||
uint16_t position = 0;
|
||||
for (int i = 0; i < 30; i++) {
|
||||
ublox_info.swVersion[i] = buffer[position];
|
||||
position++;
|
||||
}
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ublox_info.hwVersion[i] = buffer[position];
|
||||
position++;
|
||||
}
|
||||
// Close all NMEA sentences, valid for MTK3333 and MTK3339 platforms
|
||||
_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");
|
||||
delay(20);
|
||||
std::vector<ChipInfo> mtk = {{"L76B", "Quectel-L76B", GNSS_MODEL_MTK_L76B}, {"PA1010D", "1010D", GNSS_MODEL_MTK_PA1010D},
|
||||
{"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}};
|
||||
|
||||
while (len >= position + 30) {
|
||||
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]);
|
||||
PROBE_FAMILY("MTK Family", "$PMTK605*31", mtk, 500);
|
||||
currentDelay = 20;
|
||||
currentStep = 6;
|
||||
return GNSS_MODEL_UNKNOWN;
|
||||
}
|
||||
case 6: {
|
||||
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);
|
||||
currentDelay = 2000;
|
||||
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));
|
||||
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
|
||||
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;
|
||||
uint16_t len = getACK(buffer, sizeof(buffer), 0x0A, 0x04, 1200);
|
||||
if (len) {
|
||||
uint16_t position = 0;
|
||||
for (int i = 0; i < 30; i++) {
|
||||
ublox_info.swVersion[i] = buffer[position];
|
||||
position++;
|
||||
}
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ublox_info.hwVersion[i] = buffer[position];
|
||||
position++;
|
||||
}
|
||||
|
||||
while (len >= position + 30) {
|
||||
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) {
|
||||
LOG_INFO(DETECTED_MESSAGE, "U-blox 6", "6");
|
||||
return GNSS_MODEL_UBLOX6;
|
||||
} else if (strncmp(ublox_info.hwVersion, "00070000", 8) == 0) {
|
||||
LOG_INFO(DETECTED_MESSAGE, "U-blox 7", "7");
|
||||
return GNSS_MODEL_UBLOX7;
|
||||
} else if (strncmp(ublox_info.hwVersion, "00080000", 8) == 0) {
|
||||
LOG_INFO(DETECTED_MESSAGE, "U-blox 8", "8");
|
||||
return GNSS_MODEL_UBLOX8;
|
||||
} else if (strncmp(ublox_info.hwVersion, "00190000", 8) == 0) {
|
||||
LOG_INFO(DETECTED_MESSAGE, "U-blox 9", "9");
|
||||
return GNSS_MODEL_UBLOX9;
|
||||
} else if (strncmp(ublox_info.hwVersion, "000A0000", 8) == 0) {
|
||||
LOG_INFO(DETECTED_MESSAGE, "U-blox 10", "10");
|
||||
return GNSS_MODEL_UBLOX10;
|
||||
if (strncmp(ublox_info.hwVersion, "00040007", 8) == 0) {
|
||||
LOG_INFO(DETECTED_MESSAGE, "U-blox 6", "6");
|
||||
return GNSS_MODEL_UBLOX6;
|
||||
} else if (strncmp(ublox_info.hwVersion, "00070000", 8) == 0) {
|
||||
LOG_INFO(DETECTED_MESSAGE, "U-blox 7", "7");
|
||||
return GNSS_MODEL_UBLOX7;
|
||||
} else if (strncmp(ublox_info.hwVersion, "00080000", 8) == 0) {
|
||||
LOG_INFO(DETECTED_MESSAGE, "U-blox 8", "8");
|
||||
return GNSS_MODEL_UBLOX8;
|
||||
} else if (strncmp(ublox_info.hwVersion, "00190000", 8) == 0) {
|
||||
LOG_INFO(DETECTED_MESSAGE, "U-blox 9", "9");
|
||||
return GNSS_MODEL_UBLOX9;
|
||||
} else if (strncmp(ublox_info.hwVersion, "000A0000", 8) == 0) {
|
||||
LOG_INFO(DETECTED_MESSAGE, "U-blox 10", "10");
|
||||
return GNSS_MODEL_UBLOX10;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_WARN("No GNSS Module (baudrate %d)", serialSpeed);
|
||||
currentDelay = 2000;
|
||||
currentStep = 0;
|
||||
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();
|
||||
while (millis() - start < timeout) {
|
||||
if (_serial_gps->available()) {
|
||||
response += (char)_serial_gps->read();
|
||||
char c = _serial_gps->read();
|
||||
|
||||
if (response.endsWith(",") || response.endsWith("\r\n")) {
|
||||
// Add char to buffer if there's space
|
||||
if (responseLen < bufferSize - 1) {
|
||||
response[responseLen++] = c;
|
||||
response[responseLen] = '\0';
|
||||
}
|
||||
|
||||
if (c == ',' || (responseLen >= 2 && response[responseLen - 2] == '\r' && response[responseLen - 1] == '\n')) {
|
||||
#ifdef GPS_DEBUG
|
||||
LOG_DEBUG(response.c_str());
|
||||
LOG_DEBUG(response);
|
||||
#endif
|
||||
// check if we can see our chips
|
||||
for (const auto &chipInfo : responseMap) {
|
||||
if (strstr(response.c_str(), chipInfo.detectionString.c_str()) != nullptr) {
|
||||
if (strstr(response, chipInfo.detectionString.c_str()) != nullptr) {
|
||||
LOG_INFO("%s detected", chipInfo.chipName.c_str());
|
||||
delete[] response; // Cleanup before return
|
||||
return chipInfo.driver;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (response.endsWith("\r\n")) {
|
||||
response.trim();
|
||||
response = ""; // Reset the response string for the next potential message
|
||||
if (responseLen >= 2 && response[responseLen - 2] == '\r' && response[responseLen - 1] == '\n') {
|
||||
// Reset the response buffer for the next potential message
|
||||
responseLen = 0;
|
||||
response[0] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef GPS_DEBUG
|
||||
LOG_DEBUG(response.c_str());
|
||||
LOG_DEBUG(response);
|
||||
#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()
|
||||
@@ -1406,7 +1532,7 @@ GPS *GPS::createGps()
|
||||
_en_gpio = PIN_GPS_EN;
|
||||
#endif
|
||||
#ifdef ARCH_PORTDUINO
|
||||
if (!settingsMap[has_gps])
|
||||
if (!portduino_config.has_gps)
|
||||
return nullptr;
|
||||
#endif
|
||||
if (!_rx_gpio || !_serial_gps) // Configured to have no GPS at all
|
||||
@@ -1496,28 +1622,10 @@ static int32_t toDegInt(RawDegrees d)
|
||||
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
|
||||
* Override this method to check for new locations
|
||||
*
|
||||
* @return true if we've acquired a new location
|
||||
* @return true if we've set a new time
|
||||
*/
|
||||
bool GPS::lookForTime()
|
||||
{
|
||||
|
||||
#ifdef GNSS_AIROHA
|
||||
uint8_t fix = reader.fixQuality();
|
||||
if (fix > 0) {
|
||||
if (lastFixStartMsec > 0) {
|
||||
if (Throttle::isWithinTimespanMs(lastFixStartMsec, GPS_FIX_HOLD_TIME)) {
|
||||
return false;
|
||||
} else {
|
||||
clearBuffer();
|
||||
}
|
||||
} else {
|
||||
lastFixStartMsec = millis();
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
auto ti = reader.time;
|
||||
auto d = reader.date;
|
||||
if (ti.isValid() && d.isValid()) { // Note: we don't check for updated, because we'll only be called if needed
|
||||
@@ -1534,13 +1642,13 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
|
||||
t.tm_year = d.year() - 1900;
|
||||
t.tm_isdst = false;
|
||||
if (t.tm_mon > -1) {
|
||||
LOG_DEBUG("NMEA GPS time %02d-%02d-%02d %02d:%02d:%02d age %d", d.year(), d.month(), t.tm_mday, t.tm_hour, t.tm_min,
|
||||
t.tm_sec, ti.age());
|
||||
if (perhapsSetRTC(RTCQualityGPS, t) == RTCSetResultInvalidTime) {
|
||||
// Clear the GPS buffer if we got an invalid time
|
||||
clearBuffer();
|
||||
if (perhapsSetRTC(RTCQualityGPS, t) == RTCSetResultSuccess) {
|
||||
LOG_DEBUG("NMEA GPS time set %02d-%02d-%02d %02d:%02d:%02d age %d", d.year(), d.month(), t.tm_mday, t.tm_hour,
|
||||
t.tm_min, t.tm_sec, ti.age());
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
} else
|
||||
@@ -1555,25 +1663,6 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
|
||||
*/
|
||||
bool GPS::lookForLocation()
|
||||
{
|
||||
#ifdef GNSS_AIROHA
|
||||
if ((config.position.gps_update_interval * 1000) >= (GPS_FIX_HOLD_TIME * 2)) {
|
||||
uint8_t fix = reader.fixQuality();
|
||||
if (fix > 0) {
|
||||
if (lastFixStartMsec > 0) {
|
||||
if (Throttle::isWithinTimespanMs(lastFixStartMsec, GPS_FIX_HOLD_TIME)) {
|
||||
return false;
|
||||
} else {
|
||||
clearBuffer();
|
||||
}
|
||||
} else {
|
||||
lastFixStartMsec = millis();
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// By default, TinyGPS++ does not parse GPGSA lines, which give us
|
||||
// the 2D/3D fixType (see NMEAGPS.h)
|
||||
// At a minimum, use the fixQuality indicator in GPGGA (FIXME?)
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
#define GPS_EN_ACTIVE 1
|
||||
#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 {
|
||||
GNSS_MODEL_ATGM336H,
|
||||
GNSS_MODEL_MTK,
|
||||
@@ -31,7 +34,8 @@ typedef enum {
|
||||
GNSS_MODEL_MTK_PA1616S,
|
||||
GNSS_MODEL_AG3335,
|
||||
GNSS_MODEL_AG3352,
|
||||
GNSS_MODEL_LS20031
|
||||
GNSS_MODEL_LS20031,
|
||||
GNSS_MODEL_CM121
|
||||
} GnssModel_t;
|
||||
|
||||
typedef enum {
|
||||
@@ -150,6 +154,8 @@ class GPS : private concurrency::OSThread
|
||||
TinyGPSPlus reader;
|
||||
uint8_t fixQual = 0; // fix quality from GPGGA
|
||||
uint32_t lastChecksumFailCount = 0;
|
||||
uint8_t currentStep = 0;
|
||||
int32_t currentDelay = 2000;
|
||||
|
||||
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
|
||||
// (20210908) TinyGps++ can only read the GPGSA "FIX TYPE" field
|
||||
@@ -159,7 +165,7 @@ class GPS : private concurrency::OSThread
|
||||
uint8_t fixType = 0; // fix type from GPGSA
|
||||
#endif
|
||||
|
||||
uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0, lastFixStartMsec = 0;
|
||||
uint32_t fixHoldEnds = 0;
|
||||
uint32_t rx_gpio = 0;
|
||||
uint32_t tx_gpio = 0;
|
||||
|
||||
@@ -172,8 +178,6 @@ class GPS : private concurrency::OSThread
|
||||
*/
|
||||
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 hasGPS = false; // Do we have a GPS we are talking to
|
||||
@@ -236,7 +240,7 @@ class GPS : private concurrency::OSThread
|
||||
|
||||
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
|
||||
GnssModel_t probe(int serialSpeed);
|
||||
|
||||
159
src/gps/RTC.cpp
159
src/gps/RTC.cpp
@@ -9,6 +9,9 @@
|
||||
static RTCQuality currentQuality = RTCQualityNone;
|
||||
uint32_t lastSetFromPhoneNtpOrGps = 0;
|
||||
|
||||
static uint32_t lastTimeValidationWarning = 0;
|
||||
static const uint32_t TIME_VALIDATION_WARNING_INTERVAL_MS = 15000; // 15 seconds
|
||||
|
||||
RTCQuality getRTCQuality()
|
||||
{
|
||||
return currentQuality;
|
||||
@@ -23,7 +26,7 @@ static uint64_t zeroOffsetSecs; // GPS based time in secs since 1970 - only upda
|
||||
* Reads the current date and time from the RTC module and updates the system time.
|
||||
* @return True if the RTC was successfully read and the system time was updated, false otherwise.
|
||||
*/
|
||||
void readFromRTC()
|
||||
RTCSetResult readFromRTC()
|
||||
{
|
||||
struct timeval tv; /* btw settimeofday() is helpful here too*/
|
||||
#ifdef RV3028_RTC
|
||||
@@ -44,15 +47,25 @@ void readFromRTC()
|
||||
t.tm_sec = rtc.getSecond();
|
||||
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
|
||||
|
||||
#ifdef BUILD_EPOCH
|
||||
if (tv.tv_sec < BUILD_EPOCH) {
|
||||
if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) {
|
||||
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
|
||||
}
|
||||
return RTCSetResultInvalidTime;
|
||||
}
|
||||
#endif
|
||||
|
||||
LOG_DEBUG("Read RTC time from RV3028 getTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", t.tm_year + 1900, t.tm_mon + 1,
|
||||
t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch);
|
||||
timeStartMsec = now;
|
||||
zeroOffsetSecs = tv.tv_sec;
|
||||
if (currentQuality == RTCQualityNone) {
|
||||
timeStartMsec = now;
|
||||
zeroOffsetSecs = tv.tv_sec;
|
||||
currentQuality = RTCQualityDevice;
|
||||
}
|
||||
return RTCSetResultSuccess;
|
||||
}
|
||||
#elif defined(PCF8563_RTC)
|
||||
if (rtc_found.address == PCF8563_RTC) {
|
||||
@@ -75,15 +88,55 @@ void readFromRTC()
|
||||
t.tm_sec = tc.second;
|
||||
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
|
||||
|
||||
#ifdef BUILD_EPOCH
|
||||
if (tv.tv_sec < BUILD_EPOCH) {
|
||||
if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) {
|
||||
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
|
||||
lastTimeValidationWarning = millis();
|
||||
}
|
||||
return RTCSetResultInvalidTime;
|
||||
}
|
||||
#endif
|
||||
|
||||
LOG_DEBUG("Read RTC time from PCF8563 getDateTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", t.tm_year + 1900, t.tm_mon + 1,
|
||||
t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch);
|
||||
timeStartMsec = now;
|
||||
zeroOffsetSecs = tv.tv_sec;
|
||||
if (currentQuality == RTCQualityNone) {
|
||||
timeStartMsec = now;
|
||||
zeroOffsetSecs = tv.tv_sec;
|
||||
currentQuality = RTCQualityDevice;
|
||||
}
|
||||
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
|
||||
if (!gettimeofday(&tv, NULL)) {
|
||||
@@ -92,8 +145,10 @@ void readFromRTC()
|
||||
LOG_DEBUG("Read RTC time as %ld", printableEpoch);
|
||||
timeStartMsec = now;
|
||||
zeroOffsetSecs = tv.tv_sec;
|
||||
return RTCSetResultSuccess;
|
||||
}
|
||||
#endif
|
||||
return RTCSetResultNotSet;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,7 +156,7 @@ void readFromRTC()
|
||||
*
|
||||
* @param q The quality of the provided time.
|
||||
* @param tv A pointer to a timeval struct containing the time to potentially set the RTC to.
|
||||
* @return True if the RTC was set, false otherwise.
|
||||
* @return RTCSetResult
|
||||
*
|
||||
* If we haven't yet set our RTC this boot, set it from a GPS derived time
|
||||
*/
|
||||
@@ -112,7 +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
|
||||
#ifdef BUILD_EPOCH
|
||||
if (tv->tv_sec < BUILD_EPOCH) {
|
||||
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
|
||||
if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) {
|
||||
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
|
||||
lastTimeValidationWarning = millis();
|
||||
}
|
||||
return RTCSetResultInvalidTime;
|
||||
} else if ((uint64_t)tv->tv_sec > ((uint64_t)BUILD_EPOCH + FORTY_YEARS)) {
|
||||
if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) {
|
||||
// Calculate max allowed time safely to avoid overflow in logging
|
||||
uint64_t maxAllowedTime = (uint64_t)BUILD_EPOCH + FORTY_YEARS;
|
||||
uint32_t maxAllowedPrintable = (maxAllowedTime > UINT32_MAX) ? UINT32_MAX : (uint32_t)maxAllowedTime;
|
||||
LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch,
|
||||
(uint32_t)BUILD_EPOCH, maxAllowedPrintable);
|
||||
lastTimeValidationWarning = millis();
|
||||
}
|
||||
return RTCSetResultInvalidTime;
|
||||
}
|
||||
#endif
|
||||
@@ -175,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,
|
||||
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)
|
||||
settimeofday(tv, NULL);
|
||||
#endif
|
||||
@@ -226,7 +305,27 @@ RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t)
|
||||
time_t res = gm_mktime(&t);
|
||||
struct timeval tv;
|
||||
tv.tv_sec = res;
|
||||
tv.tv_usec = 0; // time.centisecond() * (10 / 1000);
|
||||
tv.tv_usec = 0; // time.centisecond() * (10 / 1000);
|
||||
uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms
|
||||
#ifdef BUILD_EPOCH
|
||||
if (tv.tv_sec < BUILD_EPOCH) {
|
||||
if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) {
|
||||
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
|
||||
lastTimeValidationWarning = millis();
|
||||
}
|
||||
return RTCSetResultInvalidTime;
|
||||
} else if ((uint64_t)tv.tv_sec > ((uint64_t)BUILD_EPOCH + FORTY_YEARS)) {
|
||||
if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) {
|
||||
// Calculate max allowed time safely to avoid overflow in logging
|
||||
uint64_t maxAllowedTime = (uint64_t)BUILD_EPOCH + FORTY_YEARS;
|
||||
uint32_t maxAllowedPrintable = (maxAllowedTime > UINT32_MAX) ? UINT32_MAX : (uint32_t)maxAllowedTime;
|
||||
LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch,
|
||||
(uint32_t)BUILD_EPOCH, maxAllowedPrintable);
|
||||
lastTimeValidationWarning = millis();
|
||||
}
|
||||
return RTCSetResultInvalidTime;
|
||||
}
|
||||
#endif
|
||||
|
||||
// LOG_DEBUG("Got time from GPS month=%d, year=%d, unixtime=%ld", t.tm_mon, t.tm_year, tv.tv_sec);
|
||||
if (t.tm_year < 0 || t.tm_year >= 300) {
|
||||
@@ -283,14 +382,40 @@ uint32_t getValidTime(RTCQuality minQuality, bool local)
|
||||
time_t gm_mktime(struct tm *tm)
|
||||
{
|
||||
#if !MESHTASTIC_EXCLUDE_TZ
|
||||
setenv("TZ", "GMT0", 1);
|
||||
time_t res = mktime(tm);
|
||||
if (*config.device.tzdef) {
|
||||
setenv("TZ", config.device.tzdef, 1);
|
||||
} else {
|
||||
setenv("TZ", "UTC0", 1);
|
||||
time_t result = 0;
|
||||
|
||||
// First, get us to the start of tm->year, by calcuating the number of days since the Unix epoch.
|
||||
int year = 1900 + tm->tm_year; // tm_year is years since 1900
|
||||
int year_minus_one = year - 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
|
||||
return mktime(tm);
|
||||
#endif
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
#include "sys/time.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifdef RX8130CE_RTC
|
||||
#include <ArtronShop_RX8130CE.h>
|
||||
#endif
|
||||
|
||||
enum RTCQuality {
|
||||
|
||||
/// We haven't had our RTC set yet
|
||||
@@ -48,10 +52,13 @@ uint32_t getTime(bool local = false);
|
||||
/// Return time since 1970 in secs. If quality is RTCQualityNone return zero
|
||||
uint32_t getValidTime(RTCQuality minQuality, bool local = false);
|
||||
|
||||
void readFromRTC();
|
||||
RTCSetResult readFromRTC();
|
||||
|
||||
time_t gm_mktime(struct tm *tm);
|
||||
|
||||
#define SEC_PER_DAY 86400
|
||||
#define SEC_PER_HOUR 3600
|
||||
#define SEC_PER_MIN 60
|
||||
#ifdef BUILD_EPOCH
|
||||
static constexpr uint64_t FORTY_YEARS = (40ULL * 365 * SEC_PER_DAY); // Use 64-bit arithmetic to prevent overflow
|
||||
#endif
|
||||
|
||||
@@ -67,20 +67,28 @@ bool EInkDisplay::forceDisplay(uint32_t msecLimit)
|
||||
|
||||
// FIXME - only draw bits have changed (use backbuf similar to the other displays)
|
||||
const bool flipped = config.display.flip_screen;
|
||||
// HACK for L1 EInk
|
||||
#if defined(SEEED_WIO_TRACKER_L1_EINK)
|
||||
// For SEEED_WIO_TRACKER_L1_EINK, setRotation(3) is correct but mirrored; flip both axes
|
||||
for (uint32_t y = 0; y < displayHeight; y++) {
|
||||
for (uint32_t x = 0; x < displayWidth; x++) {
|
||||
auto b = buffer[x + (y / 8) * displayWidth];
|
||||
auto isset = b & (1 << (y & 7));
|
||||
adafruitDisplay->drawPixel((displayWidth - 1) - x, (displayHeight - 1) - y, isset ? GxEPD_BLACK : GxEPD_WHITE);
|
||||
}
|
||||
}
|
||||
#else
|
||||
for (uint32_t y = 0; y < displayHeight; y++) {
|
||||
for (uint32_t x = 0; x < displayWidth; x++) {
|
||||
// get src pixel in the page based ordering the OLED lib uses FIXME, super inefficient
|
||||
auto b = buffer[x + (y / 8) * displayWidth];
|
||||
auto isset = b & (1 << (y & 7));
|
||||
|
||||
// Handle flip here, rather than with setRotation(),
|
||||
// Avoids issues when display width is not a multiple of 8
|
||||
if (flipped)
|
||||
adafruitDisplay->drawPixel((displayWidth - 1) - x, (displayHeight - 1) - y, isset ? GxEPD_BLACK : GxEPD_WHITE);
|
||||
else
|
||||
adafruitDisplay->drawPixel(x, y, isset ? GxEPD_BLACK : GxEPD_WHITE);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Trigger the refresh in GxEPD2
|
||||
LOG_DEBUG("Update E-Paper");
|
||||
@@ -140,17 +148,32 @@ bool EInkDisplay::connect()
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(TTGO_T_ECHO) || defined(ELECROW_ThinkNode_M1)
|
||||
#if defined(TTGO_T_ECHO) || defined(ELECROW_ThinkNode_M1) || defined(T_ECHO_LITE)
|
||||
{
|
||||
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, SPI1);
|
||||
|
||||
adafruitDisplay = new GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT>(*lowLevel);
|
||||
adafruitDisplay->init();
|
||||
#ifdef ELECROW_ThinkNode_M1
|
||||
#if defined(ELECROW_ThinkNode_M1) || defined(T_ECHO_LITE)
|
||||
adafruitDisplay->setRotation(4);
|
||||
#else
|
||||
adafruitDisplay->setRotation(3);
|
||||
#endif
|
||||
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
|
||||
}
|
||||
#elif defined(ELECROW_ThinkNode_M5)
|
||||
{
|
||||
// Start HSPI
|
||||
hspi = new SPIClass(HSPI);
|
||||
hspi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); // SCLK, MISO, MOSI, SS
|
||||
|
||||
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, *hspi);
|
||||
|
||||
adafruitDisplay = new GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT>(*lowLevel);
|
||||
adafruitDisplay->init();
|
||||
|
||||
adafruitDisplay->setRotation(4);
|
||||
|
||||
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
|
||||
}
|
||||
#elif defined(MESHLINK)
|
||||
@@ -206,7 +229,7 @@ bool EInkDisplay::connect()
|
||||
adafruitDisplay->setRotation(0);
|
||||
adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT);
|
||||
}
|
||||
#elif defined(M5_COREINK)
|
||||
#elif defined(M5_COREINK) || defined(T_DECK_PRO)
|
||||
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
|
||||
adafruitDisplay = new GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT>(*lowLevel);
|
||||
adafruitDisplay->init(115200, true, 40, false, SPI, SPISettings(4000000, MSBFIRST, SPI_MODE0));
|
||||
@@ -220,7 +243,7 @@ bool EInkDisplay::connect()
|
||||
adafruitDisplay->setRotation(1);
|
||||
adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT);
|
||||
}
|
||||
#elif defined(HELTEC_MESH_POCKET)
|
||||
#elif defined(HELTEC_MESH_POCKET) || defined(SEEED_WIO_TRACKER_L1_EINK) || defined(HELTEC_MESH_SOLAR_EINK)
|
||||
{
|
||||
spi1 = &SPI1;
|
||||
spi1->begin();
|
||||
@@ -234,6 +257,7 @@ bool EInkDisplay::connect()
|
||||
// Init GxEPD2
|
||||
adafruitDisplay->init();
|
||||
adafruitDisplay->setRotation(3);
|
||||
adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT);
|
||||
}
|
||||
#elif defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_VISION_MASTER_E213)
|
||||
|
||||
|
||||
@@ -80,11 +80,11 @@ class EInkDisplay : public OLEDDisplay
|
||||
// If display uses HSPI
|
||||
#if defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_VISION_MASTER_E213) || \
|
||||
defined(HELTEC_VISION_MASTER_E290) || defined(TLORA_T3S3_EPAPER) || defined(CROWPANEL_ESP32S3_5_EPAPER) || \
|
||||
defined(CROWPANEL_ESP32S3_4_EPAPER) || defined(CROWPANEL_ESP32S3_2_EPAPER)
|
||||
defined(CROWPANEL_ESP32S3_4_EPAPER) || defined(CROWPANEL_ESP32S3_2_EPAPER) || defined(ELECROW_ThinkNode_M5)
|
||||
SPIClass *hspi = NULL;
|
||||
#endif
|
||||
|
||||
#if defined(HELTEC_MESH_POCKET)
|
||||
#if defined(HELTEC_MESH_POCKET) || defined(SEEED_WIO_TRACKER_L1_EINK) || defined(HELTEC_MESH_SOLAR_EINK)
|
||||
SPIClass *spi1 = NULL;
|
||||
#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 "Throttle.h"
|
||||
#include "configuration.h"
|
||||
#include "meshUtils.h"
|
||||
#if HAS_SCREEN
|
||||
#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/Channels.h"
|
||||
#include "mesh/generated/meshtastic/deviceonly.pb.h"
|
||||
#include "meshUtils.h"
|
||||
#include "modules/ExternalNotificationModule.h"
|
||||
#include "modules/TextMessageModule.h"
|
||||
#include "modules/WaypointModule.h"
|
||||
@@ -83,6 +83,11 @@ extern uint16_t TFT_MESH;
|
||||
#include "platform/portduino/PortduinoGlue.h"
|
||||
#endif
|
||||
|
||||
#if defined(T_LORA_PAGER)
|
||||
// KB backlight control
|
||||
#include "input/cardKbI2cImpl.h"
|
||||
#endif
|
||||
|
||||
using namespace meshtastic; /** @todo remove */
|
||||
|
||||
namespace graphics
|
||||
@@ -95,7 +100,7 @@ namespace graphics
|
||||
#define NUM_EXTRA_FRAMES 3 // text message and debug frame
|
||||
// if defined a pixel will blink to show redraws
|
||||
// #define SHOW_REDRAWS
|
||||
|
||||
#define ASCII_BELL '\x07'
|
||||
// A text message frame + debug frame + all the node infos
|
||||
FrameCallback *normalFrames;
|
||||
static uint32_t targetFramerate = IDLE_FRAMERATE;
|
||||
@@ -216,6 +221,44 @@ void Screen::showNumberPicker(const char *message, uint32_t durationMs, uint8_t
|
||||
ui->update();
|
||||
}
|
||||
|
||||
void Screen::showTextInput(const char *header, const char *initialText, uint32_t durationMs,
|
||||
std::function<void(const std::string &)> textCallback)
|
||||
{
|
||||
LOG_INFO("showTextInput called with header='%s', durationMs=%d", header ? header : "NULL", durationMs);
|
||||
|
||||
if (NotificationRenderer::virtualKeyboard) {
|
||||
delete NotificationRenderer::virtualKeyboard;
|
||||
NotificationRenderer::virtualKeyboard = nullptr;
|
||||
}
|
||||
|
||||
NotificationRenderer::textInputCallback = nullptr;
|
||||
|
||||
NotificationRenderer::virtualKeyboard = new VirtualKeyboard();
|
||||
if (header) {
|
||||
NotificationRenderer::virtualKeyboard->setHeader(header);
|
||||
}
|
||||
if (initialText) {
|
||||
NotificationRenderer::virtualKeyboard->setInputText(initialText);
|
||||
}
|
||||
|
||||
// Set up callback with safer cleanup mechanism
|
||||
NotificationRenderer::textInputCallback = textCallback;
|
||||
NotificationRenderer::virtualKeyboard->setCallback([textCallback](const std::string &text) { textCallback(text); });
|
||||
|
||||
// Store the message and set the expiration timestamp (use same pattern as other notifications)
|
||||
strncpy(NotificationRenderer::alertBannerMessage, header ? header : "Text Input", 255);
|
||||
NotificationRenderer::alertBannerMessage[255] = '\0';
|
||||
NotificationRenderer::alertBannerUntil = (durationMs == 0) ? 0 : millis() + durationMs;
|
||||
NotificationRenderer::pauseBanner = false;
|
||||
NotificationRenderer::current_notification_type = notificationTypeEnum::text_input;
|
||||
|
||||
// Set the overlay using the same pattern as other notification types
|
||||
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
|
||||
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
|
||||
ui->setTargetFPS(60);
|
||||
ui->update();
|
||||
}
|
||||
|
||||
static void drawModuleFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
uint8_t module_frame;
|
||||
@@ -317,8 +360,16 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
|
||||
#elif defined(USE_SSD1306)
|
||||
dispdev = new SSD1306Wire(address.address, -1, -1, geometry,
|
||||
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
||||
#elif defined(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) || \
|
||||
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS)
|
||||
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS)
|
||||
dispdev = new TFTDisplay(address.address, -1, -1, geometry,
|
||||
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
||||
#elif defined(USE_EINK) && !defined(USE_EINK_DYNAMICDISPLAY)
|
||||
@@ -332,7 +383,7 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
|
||||
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
||||
#elif ARCH_PORTDUINO
|
||||
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
||||
if (settingsMap[displayPanel] != no_screen) {
|
||||
if (portduino_config.displayPanel != no_screen) {
|
||||
LOG_DEBUG("Make TFTDisplay!");
|
||||
dispdev = new TFTDisplay(address.address, -1, -1, geometry,
|
||||
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
||||
@@ -365,9 +416,6 @@ void Screen::doDeepSleep()
|
||||
{
|
||||
#ifdef USE_EINK
|
||||
setOn(false, graphics::UIRenderer::drawDeepSleepFrame);
|
||||
#ifdef PIN_EINK_EN
|
||||
digitalWrite(PIN_EINK_EN, LOW); // power off backlight
|
||||
#endif
|
||||
#else
|
||||
// Without E-Ink display:
|
||||
setOn(false);
|
||||
@@ -386,24 +434,27 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
|
||||
#ifdef T_WATCH_S3
|
||||
PMU->enablePowerOutput(XPOWERS_ALDO2);
|
||||
#endif
|
||||
#ifdef HELTEC_TRACKER_V1_X
|
||||
uint8_t tft_vext_enabled = digitalRead(VEXT_ENABLE);
|
||||
#endif
|
||||
|
||||
#if !ARCH_PORTDUINO
|
||||
dispdev->displayOn();
|
||||
#endif
|
||||
|
||||
#ifdef PIN_EINK_EN
|
||||
if (uiconfig.screen_brightness == 1)
|
||||
digitalWrite(PIN_EINK_EN, HIGH);
|
||||
#elif defined(PCA_PIN_EINK_EN)
|
||||
if (uiconfig.screen_brightness == 1)
|
||||
io.digitalWrite(PCA_PIN_EINK_EN, HIGH);
|
||||
#endif
|
||||
|
||||
#if defined(ST7789_CS) && \
|
||||
!defined(M5STACK) // set display brightness when turning on screens. Just moved function from TFTDisplay to here.
|
||||
static_cast<TFTDisplay *>(dispdev)->setDisplayBrightness(brightness);
|
||||
#endif
|
||||
|
||||
dispdev->displayOn();
|
||||
#ifdef HELTEC_TRACKER_V1_X
|
||||
// If the TFT VEXT power is not enabled, initialize the UI.
|
||||
if (!tft_vext_enabled) {
|
||||
ui->init();
|
||||
}
|
||||
#if defined(HELTEC_TRACKER_V1_X) || defined(HELTEC_WIRELESS_TRACKER_V2)
|
||||
ui->init();
|
||||
#endif
|
||||
#ifdef USE_ST7789
|
||||
pinMode(VTFT_CTRL, OUTPUT);
|
||||
@@ -425,11 +476,13 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
|
||||
// eInkScreensaver parameter is usually NULL (default argument), default frame used instead
|
||||
setScreensaverFrames(einkScreensaver);
|
||||
#endif
|
||||
#ifdef ELECROW_ThinkNode_M1
|
||||
if (digitalRead(PIN_EINK_EN) == HIGH) {
|
||||
digitalWrite(PIN_EINK_EN, LOW);
|
||||
}
|
||||
|
||||
#ifdef PIN_EINK_EN
|
||||
digitalWrite(PIN_EINK_EN, LOW);
|
||||
#elif defined(PCA_PIN_EINK_EN)
|
||||
io.digitalWrite(PCA_PIN_EINK_EN, LOW);
|
||||
#endif
|
||||
|
||||
dispdev->displayOff();
|
||||
#ifdef USE_ST7789
|
||||
SPI1.end();
|
||||
@@ -505,7 +558,7 @@ void Screen::setup()
|
||||
// === Apply loaded brightness ===
|
||||
#if defined(ST7789_CS)
|
||||
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);
|
||||
#endif
|
||||
LOG_INFO("Applied screen brightness: %d", brightness);
|
||||
@@ -548,11 +601,11 @@ void Screen::setup()
|
||||
#else
|
||||
if (!config.display.flip_screen) {
|
||||
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7789_CS) || \
|
||||
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS)
|
||||
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS)
|
||||
static_cast<TFTDisplay *>(dispdev)->flipScreenVertically();
|
||||
#elif defined(USE_ST7789)
|
||||
static_cast<ST7789Spi *>(dispdev)->flipScreenVertically();
|
||||
#else
|
||||
#elif !defined(M5STACK_UNITC6L)
|
||||
dispdev->flipScreenVertically();
|
||||
#endif
|
||||
}
|
||||
@@ -578,13 +631,13 @@ void Screen::setup()
|
||||
|
||||
#if ARCH_PORTDUINO
|
||||
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
||||
if (settingsMap[touchscreenModule]) {
|
||||
if (portduino_config.touchscreenModule) {
|
||||
touchScreenImpl1 =
|
||||
new TouchScreenImpl1(dispdev->getWidth(), dispdev->getHeight(), static_cast<TFTDisplay *>(dispdev)->getTouch);
|
||||
touchScreenImpl1->init();
|
||||
}
|
||||
}
|
||||
#elif HAS_TOUCHSCREEN
|
||||
#elif HAS_TOUCHSCREEN && !defined(USE_EINK)
|
||||
touchScreenImpl1 =
|
||||
new TouchScreenImpl1(dispdev->getWidth(), dispdev->getHeight(), static_cast<TFTDisplay *>(dispdev)->getTouch);
|
||||
touchScreenImpl1->init();
|
||||
@@ -607,6 +660,19 @@ void Screen::setup()
|
||||
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)
|
||||
{
|
||||
// Nasty hack to force epaper updates for 'key' frames. FIXME, cleanup.
|
||||
@@ -690,7 +756,11 @@ int32_t Screen::runOnce()
|
||||
|
||||
#ifndef DISABLE_WELCOME_UNSET
|
||||
if (!NotificationRenderer::isOverlayBannerShowing() && config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
|
||||
menuHandler::LoraRegionPicker(0);
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
menuHandler::LoraRegionPicker();
|
||||
#else
|
||||
menuHandler::OnboardMessage();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
if (!NotificationRenderer::isOverlayBannerShowing() && rebootAtMsec != 0) {
|
||||
@@ -711,13 +781,19 @@ int32_t Screen::runOnce()
|
||||
handleSetOn(false);
|
||||
break;
|
||||
case Cmd::ON_PRESS:
|
||||
handleOnPress();
|
||||
if (NotificationRenderer::current_notification_type != notificationTypeEnum::text_input) {
|
||||
handleOnPress();
|
||||
}
|
||||
break;
|
||||
case Cmd::SHOW_PREV_FRAME:
|
||||
handleShowPrevFrame();
|
||||
if (NotificationRenderer::current_notification_type != notificationTypeEnum::text_input) {
|
||||
handleShowPrevFrame();
|
||||
}
|
||||
break;
|
||||
case Cmd::SHOW_NEXT_FRAME:
|
||||
handleShowNextFrame();
|
||||
if (NotificationRenderer::current_notification_type != notificationTypeEnum::text_input) {
|
||||
handleShowNextFrame();
|
||||
}
|
||||
break;
|
||||
case Cmd::START_ALERT_FRAME: {
|
||||
showingBootScreen = false; // this should avoid the edge case where an alert triggers before the boot screen goes away
|
||||
@@ -739,7 +815,9 @@ int32_t Screen::runOnce()
|
||||
NotificationRenderer::pauseBanner = false;
|
||||
case Cmd::STOP_BOOT_SCREEN:
|
||||
EINK_ADD_FRAMEFLAG(dispdev, COSMETIC); // E-Ink: Explicitly use full-refresh for next frame
|
||||
setFrames();
|
||||
if (NotificationRenderer::current_notification_type != notificationTypeEnum::text_input) {
|
||||
setFrames();
|
||||
}
|
||||
break;
|
||||
case Cmd::NOOP:
|
||||
break;
|
||||
@@ -775,6 +853,7 @@ int32_t Screen::runOnce()
|
||||
if (showingNormalScreen) {
|
||||
// standard screen loop handling here
|
||||
if (config.display.auto_screen_carousel_secs > 0 &&
|
||||
NotificationRenderer::current_notification_type != notificationTypeEnum::text_input &&
|
||||
!Throttle::isWithinTimespanMs(lastScreenTransition, config.display.auto_screen_carousel_secs * 1000)) {
|
||||
|
||||
// If an E-Ink display struggles with fast refresh, force carousel to use full refresh instead
|
||||
@@ -865,10 +944,17 @@ void Screen::setScreensaverFrames(FrameCallback einkScreensaver)
|
||||
// Called when a frame should be added / removed, or custom frames should be cleared
|
||||
void Screen::setFrames(FrameFocus focus)
|
||||
{
|
||||
// Block setFrames calls when virtual keyboard is active to prevent overlay interference
|
||||
if (NotificationRenderer::current_notification_type == notificationTypeEnum::text_input) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t originalPosition = ui->getUiState()->currentFrame;
|
||||
uint8_t previousFrameCount = framesetInfo.frameCount;
|
||||
FramesetInfo fsi; // Location of specific frames, for applying focus parameter
|
||||
|
||||
graphics::UIRenderer::rebuildFavoritedNodes();
|
||||
|
||||
LOG_DEBUG("Show standard frames");
|
||||
showingNormalScreen = true;
|
||||
|
||||
@@ -885,71 +971,95 @@ void Screen::setFrames(FrameFocus focus)
|
||||
}
|
||||
|
||||
#if defined(DISPLAY_CLOCK_FRAME)
|
||||
fsi.positions.clock = numframes;
|
||||
normalFrames[numframes++] = uiconfig.is_clockface_analog ? graphics::ClockRenderer::drawAnalogClockFrame
|
||||
: graphics::ClockRenderer::drawDigitalClockFrame;
|
||||
indicatorIcons.push_back(digital_icon_clock);
|
||||
if (!hiddenFrames.clock) {
|
||||
fsi.positions.clock = numframes;
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
normalFrames[numframes++] = graphics::ClockRenderer::drawAnalogClockFrame;
|
||||
#else
|
||||
normalFrames[numframes++] = uiconfig.is_clockface_analog ? graphics::ClockRenderer::drawAnalogClockFrame
|
||||
: graphics::ClockRenderer::drawDigitalClockFrame;
|
||||
#endif
|
||||
indicatorIcons.push_back(digital_icon_clock);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Declare this early so it’s available in FOCUS_PRESERVE block
|
||||
bool willInsertTextMessage = shouldDrawMessage(&devicestate.rx_text_message);
|
||||
|
||||
fsi.positions.home = numframes;
|
||||
normalFrames[numframes++] = graphics::UIRenderer::drawDeviceFocused;
|
||||
indicatorIcons.push_back(icon_home);
|
||||
if (!hiddenFrames.home) {
|
||||
fsi.positions.home = numframes;
|
||||
normalFrames[numframes++] = graphics::UIRenderer::drawDeviceFocused;
|
||||
indicatorIcons.push_back(icon_home);
|
||||
}
|
||||
|
||||
fsi.positions.textMessage = numframes;
|
||||
normalFrames[numframes++] = graphics::MessageRenderer::drawTextMessageFrame;
|
||||
indicatorIcons.push_back(icon_mail);
|
||||
|
||||
#ifndef USE_EINK
|
||||
fsi.positions.nodelist = numframes;
|
||||
normalFrames[numframes++] = graphics::NodeListRenderer::drawDynamicNodeListScreen;
|
||||
indicatorIcons.push_back(icon_nodes);
|
||||
if (!hiddenFrames.nodelist) {
|
||||
fsi.positions.nodelist = numframes;
|
||||
normalFrames[numframes++] = graphics::NodeListRenderer::drawDynamicNodeListScreen;
|
||||
indicatorIcons.push_back(icon_nodes);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Show detailed node views only on E-Ink builds
|
||||
#ifdef USE_EINK
|
||||
fsi.positions.nodelist_lastheard = numframes;
|
||||
normalFrames[numframes++] = graphics::NodeListRenderer::drawLastHeardScreen;
|
||||
indicatorIcons.push_back(icon_nodes);
|
||||
|
||||
fsi.positions.nodelist_hopsignal = numframes;
|
||||
normalFrames[numframes++] = graphics::NodeListRenderer::drawHopSignalScreen;
|
||||
indicatorIcons.push_back(icon_signal);
|
||||
|
||||
fsi.positions.nodelist_distance = numframes;
|
||||
normalFrames[numframes++] = graphics::NodeListRenderer::drawDistanceScreen;
|
||||
indicatorIcons.push_back(icon_distance);
|
||||
if (!hiddenFrames.nodelist_lastheard) {
|
||||
fsi.positions.nodelist_lastheard = numframes;
|
||||
normalFrames[numframes++] = graphics::NodeListRenderer::drawLastHeardScreen;
|
||||
indicatorIcons.push_back(icon_nodes);
|
||||
}
|
||||
if (!hiddenFrames.nodelist_hopsignal) {
|
||||
fsi.positions.nodelist_hopsignal = numframes;
|
||||
normalFrames[numframes++] = graphics::NodeListRenderer::drawHopSignalScreen;
|
||||
indicatorIcons.push_back(icon_signal);
|
||||
}
|
||||
if (!hiddenFrames.nodelist_distance) {
|
||||
fsi.positions.nodelist_distance = numframes;
|
||||
normalFrames[numframes++] = graphics::NodeListRenderer::drawDistanceScreen;
|
||||
indicatorIcons.push_back(icon_distance);
|
||||
}
|
||||
#endif
|
||||
#if HAS_GPS
|
||||
fsi.positions.nodelist_bearings = numframes;
|
||||
normalFrames[numframes++] = graphics::NodeListRenderer::drawNodeListWithCompasses;
|
||||
indicatorIcons.push_back(icon_list);
|
||||
|
||||
fsi.positions.gps = numframes;
|
||||
normalFrames[numframes++] = graphics::UIRenderer::drawCompassAndLocationScreen;
|
||||
indicatorIcons.push_back(icon_compass);
|
||||
if (!hiddenFrames.nodelist_bearings) {
|
||||
fsi.positions.nodelist_bearings = numframes;
|
||||
normalFrames[numframes++] = graphics::NodeListRenderer::drawNodeListWithCompasses;
|
||||
indicatorIcons.push_back(icon_list);
|
||||
}
|
||||
if (!hiddenFrames.gps) {
|
||||
fsi.positions.gps = numframes;
|
||||
normalFrames[numframes++] = graphics::UIRenderer::drawCompassAndLocationScreen;
|
||||
indicatorIcons.push_back(icon_compass);
|
||||
}
|
||||
#endif
|
||||
if (RadioLibInterface::instance) {
|
||||
if (RadioLibInterface::instance && !hiddenFrames.lora) {
|
||||
fsi.positions.lora = numframes;
|
||||
normalFrames[numframes++] = graphics::DebugRenderer::drawLoRaFocused;
|
||||
indicatorIcons.push_back(icon_radio);
|
||||
}
|
||||
if (!dismissedFrames.memory) {
|
||||
fsi.positions.memory = numframes;
|
||||
normalFrames[numframes++] = graphics::DebugRenderer::drawMemoryUsage;
|
||||
indicatorIcons.push_back(icon_memory);
|
||||
if (!hiddenFrames.system) {
|
||||
fsi.positions.system = numframes;
|
||||
normalFrames[numframes++] = graphics::DebugRenderer::drawSystemScreen;
|
||||
indicatorIcons.push_back(icon_system);
|
||||
}
|
||||
#if !defined(DISPLAY_CLOCK_FRAME)
|
||||
fsi.positions.clock = numframes;
|
||||
normalFrames[numframes++] = uiconfig.is_clockface_analog ? graphics::ClockRenderer::drawAnalogClockFrame
|
||||
: graphics::ClockRenderer::drawDigitalClockFrame;
|
||||
indicatorIcons.push_back(digital_icon_clock);
|
||||
if (!hiddenFrames.clock) {
|
||||
fsi.positions.clock = numframes;
|
||||
normalFrames[numframes++] = uiconfig.is_clockface_analog ? graphics::ClockRenderer::drawAnalogClockFrame
|
||||
: graphics::ClockRenderer::drawDigitalClockFrame;
|
||||
indicatorIcons.push_back(digital_icon_clock);
|
||||
}
|
||||
#endif
|
||||
if (!hiddenFrames.chirpy) {
|
||||
fsi.positions.chirpy = numframes;
|
||||
normalFrames[numframes++] = graphics::DebugRenderer::drawChirpy;
|
||||
indicatorIcons.push_back(chirpy_small);
|
||||
}
|
||||
|
||||
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
|
||||
if (!dismissedFrames.wifi && isWifiAvailable()) {
|
||||
if (!hiddenFrames.wifi && isWifiAvailable()) {
|
||||
fsi.positions.wifi = numframes;
|
||||
normalFrames[numframes++] = graphics::DebugRenderer::drawDebugInfoWiFiTrampoline;
|
||||
indicatorIcons.push_back(icon_wifi);
|
||||
@@ -991,27 +1101,29 @@ void Screen::setFrames(FrameFocus focus)
|
||||
if (numMeshNodes > 0)
|
||||
numMeshNodes--;
|
||||
|
||||
// Temporary array to hold favorite node frames
|
||||
std::vector<FrameCallback> favoriteFrames;
|
||||
if (!hiddenFrames.show_favorites) {
|
||||
// Temporary array to hold favorite node frames
|
||||
std::vector<FrameCallback> favoriteFrames;
|
||||
|
||||
for (size_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
|
||||
const meshtastic_NodeInfoLite *n = nodeDB->getMeshNodeByIndex(i);
|
||||
if (n && n->num != nodeDB->getNodeNum() && n->is_favorite) {
|
||||
favoriteFrames.push_back(graphics::UIRenderer::drawNodeInfo);
|
||||
for (size_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
|
||||
const meshtastic_NodeInfoLite *n = nodeDB->getMeshNodeByIndex(i);
|
||||
if (n && n->num != nodeDB->getNodeNum() && n->is_favorite) {
|
||||
favoriteFrames.push_back(graphics::UIRenderer::drawNodeInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Insert favorite frames *after* collecting them all
|
||||
if (!favoriteFrames.empty()) {
|
||||
fsi.positions.firstFavorite = numframes;
|
||||
for (auto &f : favoriteFrames) {
|
||||
normalFrames[numframes++] = f;
|
||||
indicatorIcons.push_back(icon_node);
|
||||
// Insert favorite frames *after* collecting them all
|
||||
if (!favoriteFrames.empty()) {
|
||||
fsi.positions.firstFavorite = numframes;
|
||||
for (const auto &f : favoriteFrames) {
|
||||
normalFrames[numframes++] = f;
|
||||
indicatorIcons.push_back(icon_node);
|
||||
}
|
||||
fsi.positions.lastFavorite = numframes - 1;
|
||||
} else {
|
||||
fsi.positions.firstFavorite = 255;
|
||||
fsi.positions.lastFavorite = 255;
|
||||
}
|
||||
fsi.positions.lastFavorite = numframes - 1;
|
||||
} else {
|
||||
fsi.positions.firstFavorite = 255;
|
||||
fsi.positions.lastFavorite = 255;
|
||||
}
|
||||
|
||||
fsi.frameCount = numframes; // Total framecount is used to apply FOCUS_PRESERVE
|
||||
@@ -1050,7 +1162,7 @@ void Screen::setFrames(FrameFocus focus)
|
||||
ui->switchToFrame(fsi.positions.clock);
|
||||
break;
|
||||
case FOCUS_SYSTEM:
|
||||
ui->switchToFrame(fsi.positions.memory);
|
||||
ui->switchToFrame(fsi.positions.system);
|
||||
break;
|
||||
|
||||
case FOCUS_PRESERVE:
|
||||
@@ -1078,30 +1190,101 @@ void Screen::setFrameImmediateDraw(FrameCallback *drawFrames)
|
||||
setFastFramerate();
|
||||
}
|
||||
|
||||
void Screen::toggleFrameVisibility(const std::string &frameName)
|
||||
{
|
||||
#ifndef USE_EINK
|
||||
if (frameName == "nodelist") {
|
||||
hiddenFrames.nodelist = !hiddenFrames.nodelist;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_EINK
|
||||
if (frameName == "nodelist_lastheard") {
|
||||
hiddenFrames.nodelist_lastheard = !hiddenFrames.nodelist_lastheard;
|
||||
}
|
||||
if (frameName == "nodelist_hopsignal") {
|
||||
hiddenFrames.nodelist_hopsignal = !hiddenFrames.nodelist_hopsignal;
|
||||
}
|
||||
if (frameName == "nodelist_distance") {
|
||||
hiddenFrames.nodelist_distance = !hiddenFrames.nodelist_distance;
|
||||
}
|
||||
#endif
|
||||
#if HAS_GPS
|
||||
if (frameName == "nodelist_bearings") {
|
||||
hiddenFrames.nodelist_bearings = !hiddenFrames.nodelist_bearings;
|
||||
}
|
||||
if (frameName == "gps") {
|
||||
hiddenFrames.gps = !hiddenFrames.gps;
|
||||
}
|
||||
#endif
|
||||
if (frameName == "lora") {
|
||||
hiddenFrames.lora = !hiddenFrames.lora;
|
||||
}
|
||||
if (frameName == "clock") {
|
||||
hiddenFrames.clock = !hiddenFrames.clock;
|
||||
}
|
||||
if (frameName == "show_favorites") {
|
||||
hiddenFrames.show_favorites = !hiddenFrames.show_favorites;
|
||||
}
|
||||
if (frameName == "chirpy") {
|
||||
hiddenFrames.chirpy = !hiddenFrames.chirpy;
|
||||
}
|
||||
}
|
||||
|
||||
bool Screen::isFrameHidden(const std::string &frameName) const
|
||||
{
|
||||
#ifndef USE_EINK
|
||||
if (frameName == "nodelist")
|
||||
return hiddenFrames.nodelist;
|
||||
#endif
|
||||
#ifdef USE_EINK
|
||||
if (frameName == "nodelist_lastheard")
|
||||
return hiddenFrames.nodelist_lastheard;
|
||||
if (frameName == "nodelist_hopsignal")
|
||||
return hiddenFrames.nodelist_hopsignal;
|
||||
if (frameName == "nodelist_distance")
|
||||
return hiddenFrames.nodelist_distance;
|
||||
#endif
|
||||
#if HAS_GPS
|
||||
if (frameName == "nodelist_bearings")
|
||||
return hiddenFrames.nodelist_bearings;
|
||||
if (frameName == "gps")
|
||||
return hiddenFrames.gps;
|
||||
#endif
|
||||
if (frameName == "lora")
|
||||
return hiddenFrames.lora;
|
||||
if (frameName == "clock")
|
||||
return hiddenFrames.clock;
|
||||
if (frameName == "show_favorites")
|
||||
return hiddenFrames.show_favorites;
|
||||
if (frameName == "chirpy")
|
||||
return hiddenFrames.chirpy;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Dismisses the currently displayed screen frame, if possible
|
||||
// Relevant for text message, waypoint, others in future?
|
||||
// Triggered with a CardKB keycombo
|
||||
void Screen::dismissCurrentFrame()
|
||||
void Screen::hideCurrentFrame()
|
||||
{
|
||||
uint8_t currentFrame = ui->getUiState()->currentFrame;
|
||||
bool dismissed = false;
|
||||
|
||||
if (currentFrame == framesetInfo.positions.textMessage && devicestate.has_rx_text_message) {
|
||||
LOG_INFO("Dismiss Text Message");
|
||||
LOG_INFO("Hide Text Message");
|
||||
devicestate.has_rx_text_message = false;
|
||||
memset(&devicestate.rx_text_message, 0, sizeof(devicestate.rx_text_message));
|
||||
} else if (currentFrame == framesetInfo.positions.waypoint && devicestate.has_rx_waypoint) {
|
||||
LOG_DEBUG("Dismiss Waypoint");
|
||||
LOG_DEBUG("Hide Waypoint");
|
||||
devicestate.has_rx_waypoint = false;
|
||||
dismissedFrames.waypoint = true;
|
||||
hiddenFrames.waypoint = true;
|
||||
dismissed = true;
|
||||
} else if (currentFrame == framesetInfo.positions.wifi) {
|
||||
LOG_DEBUG("Dismiss WiFi Screen");
|
||||
dismissedFrames.wifi = true;
|
||||
LOG_DEBUG("Hide WiFi Screen");
|
||||
hiddenFrames.wifi = true;
|
||||
dismissed = true;
|
||||
} else if (currentFrame == framesetInfo.positions.memory) {
|
||||
LOG_INFO("Dismiss Memory");
|
||||
dismissedFrames.memory = true;
|
||||
} else if (currentFrame == framesetInfo.positions.lora) {
|
||||
LOG_INFO("Hide LoRa");
|
||||
hiddenFrames.lora = true;
|
||||
dismissed = true;
|
||||
}
|
||||
|
||||
@@ -1134,7 +1317,8 @@ void Screen::blink()
|
||||
delay(50);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1222,6 +1406,10 @@ void Screen::handleShowNextFrame()
|
||||
|
||||
void Screen::setFastFramerate()
|
||||
{
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
dispdev->clear();
|
||||
dispdev->display();
|
||||
#endif
|
||||
// We are about to start a transition so speed up fps
|
||||
targetFramerate = SCREEN_TRANSITION_FRAMERATE;
|
||||
|
||||
@@ -1240,6 +1428,9 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
|
||||
}
|
||||
nodeDB->updateGUI = false;
|
||||
break;
|
||||
case STATUS_TYPE_POWER:
|
||||
forceDisplay(true);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1253,7 +1444,7 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
|
||||
// Outgoing message (likely sent from phone)
|
||||
devicestate.has_rx_text_message = false;
|
||||
memset(&devicestate.rx_text_message, 0, sizeof(devicestate.rx_text_message));
|
||||
dismissedFrames.textMessage = true;
|
||||
hiddenFrames.textMessage = true;
|
||||
hasUnreadMessage = false; // Clear unread state when user replies
|
||||
|
||||
setFrames(FOCUS_PRESERVE); // Stay on same frame, silently update frame list
|
||||
@@ -1267,39 +1458,63 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
|
||||
if (shouldWakeOnReceivedMessage()) {
|
||||
setOn(true); // Wake up the screen first
|
||||
forceDisplay(); // Forces screen redraw
|
||||
}
|
||||
// === Prepare banner content ===
|
||||
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;
|
||||
|
||||
// === Prepare banner content ===
|
||||
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet->from);
|
||||
const char *longName = (node && node->has_user) ? node->user.long_name : nullptr;
|
||||
const char *msgRaw = reinterpret_cast<const char *>(packet->decoded.payload.bytes);
|
||||
|
||||
const char *msgRaw = reinterpret_cast<const char *>(packet->decoded.payload.bytes);
|
||||
char banner[256];
|
||||
|
||||
char banner[256];
|
||||
bool isAlert = false;
|
||||
|
||||
// Check for bell character in message to determine alert type
|
||||
bool isAlert = false;
|
||||
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] == '\x07') {
|
||||
if (msgRaw[i] == ASCII_BELL) {
|
||||
isAlert = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isAlert) {
|
||||
if (longName && longName[0]) {
|
||||
snprintf(banner, sizeof(banner), "Alert Received from\n%s", longName);
|
||||
} else {
|
||||
strcpy(banner, "Alert Received");
|
||||
}
|
||||
// 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 (longName && longName[0]) {
|
||||
snprintf(banner, sizeof(banner), "Alert Received from\n%s", longName);
|
||||
} else {
|
||||
if (longName && longName[0]) {
|
||||
snprintf(banner, sizeof(banner), "New Message from\n%s", longName);
|
||||
} else {
|
||||
strcpy(banner, "New Message");
|
||||
}
|
||||
strcpy(banner, "Alert Received");
|
||||
}
|
||||
|
||||
screen->showSimpleBanner(banner, 3000);
|
||||
} else if (!channel.settings.mute) {
|
||||
if (longName && longName[0]) {
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
strcpy(banner, "New Message");
|
||||
#else
|
||||
snprintf(banner, sizeof(banner), "New Message from\n%s", longName);
|
||||
#endif
|
||||
|
||||
} else {
|
||||
strcpy(banner, "New Message");
|
||||
}
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
screen->setOn(true);
|
||||
screen->showSimpleBanner(banner, 1500);
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1310,6 +1525,11 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
|
||||
// Triggered by MeshModules
|
||||
int Screen::handleUIFrameEvent(const UIFrameEvent *event)
|
||||
{
|
||||
// Block UI frame events when virtual keyboard is active
|
||||
if (NotificationRenderer::current_notification_type == notificationTypeEnum::text_input) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (showingNormalScreen) {
|
||||
// Regenerate the frameset, potentially honoring a module's internal requestFocus() call
|
||||
if (event->action == UIFrameEvent::Action::REGENERATE_FRAMESET)
|
||||
@@ -1332,6 +1552,16 @@ int Screen::handleInputEvent(const InputEvent *event)
|
||||
if (!screenOn)
|
||||
return 0;
|
||||
|
||||
// Handle text input notifications specially - pass input to virtual keyboard
|
||||
if (NotificationRenderer::current_notification_type == notificationTypeEnum::text_input) {
|
||||
NotificationRenderer::inEvent = *event;
|
||||
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
|
||||
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
|
||||
setFastFramerate(); // Draw ASAP
|
||||
ui->update();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef USE_EINK // the screen is the last input handler, so if an event makes it here, we can assume it will prompt a screen draw.
|
||||
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Use fast-refresh for next frame, no skip please
|
||||
EINK_ADD_FRAMEFLAG(dispdev, BLOCKING); // Edge case: if this frame is promoted to COSMETIC, wait for update
|
||||
@@ -1369,7 +1599,7 @@ int Screen::handleInputEvent(const InputEvent *event)
|
||||
} else if (event->inputEvent == INPUT_BROKER_SELECT) {
|
||||
if (this->ui->getUiState()->currentFrame == framesetInfo.positions.home) {
|
||||
menuHandler::homeBaseMenu();
|
||||
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.memory) {
|
||||
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.system) {
|
||||
menuHandler::systemBaseMenu();
|
||||
#if HAS_GPS
|
||||
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.gps && gps) {
|
||||
@@ -1378,10 +1608,17 @@ int Screen::handleInputEvent(const InputEvent *event)
|
||||
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.clock) {
|
||||
menuHandler::clockMenu();
|
||||
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.lora) {
|
||||
menuHandler::LoraRegionPicker();
|
||||
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.textMessage &&
|
||||
devicestate.rx_text_message.from) {
|
||||
menuHandler::messageResponseMenu();
|
||||
menuHandler::loraMenu();
|
||||
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.textMessage) {
|
||||
if (devicestate.rx_text_message.from) {
|
||||
menuHandler::messageResponseMenu();
|
||||
} else {
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
menuHandler::textMessageMenu();
|
||||
#else
|
||||
menuHandler::textMessageBaseMenu();
|
||||
#endif
|
||||
}
|
||||
} else if (framesetInfo.positions.firstFavorite != 255 &&
|
||||
this->ui->getUiState()->currentFrame >= framesetInfo.positions.firstFavorite &&
|
||||
this->ui->getUiState()->currentFrame <= framesetInfo.positions.lastFavorite) {
|
||||
@@ -1439,13 +1676,15 @@ bool shouldWakeOnReceivedMessage()
|
||||
/*
|
||||
The goal here is to determine when we do NOT wake up the screen on message received:
|
||||
- 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 (moduleConfig.external_notification.enabled) {
|
||||
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;
|
||||
}
|
||||
if (powerStatus && powerStatus->getBatteryChargePercent() < 10) {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#define getStringCenteredX(s) ((SCREEN_WIDTH - display->getStringWidth(s)) / 2)
|
||||
namespace graphics
|
||||
{
|
||||
enum notificationTypeEnum { none, text_banner, selection_picker, node_picker, number_picker };
|
||||
enum notificationTypeEnum { none, text_banner, selection_picker, node_picker, number_picker, text_input };
|
||||
|
||||
struct BannerOverlayOptions {
|
||||
const char *message;
|
||||
@@ -81,6 +81,8 @@ class Screen
|
||||
#include <SSD1306Wire.h>
|
||||
#elif defined(USE_ST7789)
|
||||
#include <ST7789Spi.h>
|
||||
#elif defined(USE_SPISSD1306)
|
||||
#include <SSD1306Spi.h>
|
||||
#else
|
||||
// the SH1106/SSD1306 variant is auto-detected
|
||||
#include <AutoOLEDWire.h>
|
||||
@@ -257,15 +259,7 @@ class Screen : public concurrency::OSThread
|
||||
void setup();
|
||||
|
||||
/// Turns the screen on/off. Optionally, pass a custom screensaver frame for E-Ink
|
||||
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});
|
||||
}
|
||||
|
||||
void setOn(bool on, FrameCallback einkScreensaver = NULL);
|
||||
/**
|
||||
* 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
|
||||
@@ -313,6 +307,8 @@ class Screen : public concurrency::OSThread
|
||||
|
||||
void showNodePicker(const char *message, uint32_t durationMs, std::function<void(uint32_t)> bannerCallback);
|
||||
void showNumberPicker(const char *message, uint32_t durationMs, uint8_t digits, std::function<void(uint32_t)> bannerCallback);
|
||||
void showTextInput(const char *header, const char *initialText, uint32_t durationMs,
|
||||
std::function<void(const std::string &)> textCallback);
|
||||
|
||||
void requestMenu(graphics::menuHandler::screenMenus menuToShow)
|
||||
{
|
||||
@@ -591,7 +587,11 @@ class Screen : public concurrency::OSThread
|
||||
void setSSLFrames();
|
||||
|
||||
// Dismiss the currently focussed frame, if possible (e.g. text message, waypoint)
|
||||
void dismissCurrentFrame();
|
||||
void hideCurrentFrame();
|
||||
|
||||
// Menu-driven Show / Hide Toggle
|
||||
void toggleFrameVisibility(const std::string &frameName);
|
||||
bool isFrameHidden(const std::string &frameName) const;
|
||||
|
||||
#ifdef USE_EINK
|
||||
/// Draw an image to remain on E-Ink display after screen off
|
||||
@@ -653,7 +653,7 @@ class Screen : public concurrency::OSThread
|
||||
uint8_t settings = 255;
|
||||
uint8_t wifi = 255;
|
||||
uint8_t deviceFocused = 255;
|
||||
uint8_t memory = 255;
|
||||
uint8_t system = 255;
|
||||
uint8_t gps = 255;
|
||||
uint8_t home = 255;
|
||||
uint8_t textMessage = 255;
|
||||
@@ -663,6 +663,7 @@ class Screen : public concurrency::OSThread
|
||||
uint8_t nodelist_distance = 255;
|
||||
uint8_t nodelist_bearings = 255;
|
||||
uint8_t clock = 255;
|
||||
uint8_t chirpy = 255;
|
||||
uint8_t firstFavorite = 255;
|
||||
uint8_t lastFavorite = 255;
|
||||
uint8_t lora = 255;
|
||||
@@ -671,12 +672,29 @@ class Screen : public concurrency::OSThread
|
||||
uint8_t frameCount = 0;
|
||||
} framesetInfo;
|
||||
|
||||
struct DismissedFrames {
|
||||
struct hiddenFrames {
|
||||
bool textMessage = false;
|
||||
bool waypoint = false;
|
||||
bool wifi = false;
|
||||
bool memory = false;
|
||||
} dismissedFrames;
|
||||
bool system = false;
|
||||
bool home = false;
|
||||
bool clock = false;
|
||||
#ifndef USE_EINK
|
||||
bool nodelist = false;
|
||||
#endif
|
||||
#ifdef USE_EINK
|
||||
bool nodelist_lastheard = false;
|
||||
bool nodelist_hopsignal = false;
|
||||
bool nodelist_distance = false;
|
||||
#endif
|
||||
#if HAS_GPS
|
||||
bool nodelist_bearings = false;
|
||||
bool gps = false;
|
||||
#endif
|
||||
bool lora = false;
|
||||
bool show_favorites = false;
|
||||
bool chirpy = true;
|
||||
} hiddenFrames;
|
||||
|
||||
/// Try to start drawing ASAP
|
||||
void setFastFramerate();
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#include "graphics/fonts/OLEDDisplayFontsCS.h"
|
||||
#endif
|
||||
|
||||
#ifdef CROWPANEL_ESP32S3_5_EPAPER
|
||||
#if defined(CROWPANEL_ESP32S3_5_EPAPER) && defined(USE_EINK)
|
||||
#include "graphics/fonts/EinkDisplayFonts.h"
|
||||
#endif
|
||||
|
||||
@@ -40,6 +40,9 @@
|
||||
#ifdef OLED_PL
|
||||
#define FONT_MEDIUM_LOCAL ArialMT_Plain_16_PL // Height: 19
|
||||
#else
|
||||
#ifdef OLED_RU
|
||||
#define FONT_MEDIUM_LOCAL ArialMT_Plain_16_RU // Height: 19
|
||||
#else
|
||||
#ifdef OLED_UA
|
||||
#define FONT_MEDIUM_LOCAL ArialMT_Plain_16_UA // Height: 19
|
||||
#else
|
||||
@@ -50,9 +53,13 @@
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#ifdef OLED_PL
|
||||
#define FONT_LARGE_LOCAL ArialMT_Plain_24_PL // Height: 28
|
||||
#else
|
||||
#ifdef OLED_RU
|
||||
#define FONT_LARGE_LOCAL ArialMT_Plain_24_RU // Height: 28
|
||||
#else
|
||||
#ifdef OLED_UA
|
||||
#define FONT_LARGE_LOCAL ArialMT_Plain_24_UA // Height: 28
|
||||
#else
|
||||
@@ -63,21 +70,26 @@
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
||||
defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS)) && \
|
||||
defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS)) && \
|
||||
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
||||
// The screen is bigger so use bigger fonts
|
||||
#define FONT_SMALL FONT_MEDIUM_LOCAL // Height: 19
|
||||
#define FONT_MEDIUM 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
|
||||
#define FONT_SMALL FONT_SMALL_LOCAL // Height: 13
|
||||
#define FONT_MEDIUM FONT_MEDIUM_LOCAL // Height: 19
|
||||
#define FONT_LARGE FONT_LARGE_LOCAL // Height: 28
|
||||
#endif
|
||||
|
||||
#if defined(CROWPANEL_ESP32S3_5_EPAPER)
|
||||
#if defined(CROWPANEL_ESP32S3_5_EPAPER) && defined(USE_EINK)
|
||||
#undef FONT_SMALL
|
||||
#undef FONT_MEDIUM
|
||||
#undef FONT_LARGE
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user