Compare commits
768 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
385e291f51 | ||
|
|
7e60078791 | ||
|
|
073eecd147 | ||
|
|
525fe9b96c | ||
|
|
c7f411fc7c | ||
|
|
fc96500329 | ||
|
|
4e87c4411c | ||
|
|
9eb9c473db | ||
|
|
bfd147062f | ||
|
|
890ec7bdb2 | ||
|
|
76269b397f | ||
|
|
9fb6b1718f | ||
|
|
57c82988e2 | ||
|
|
1e3b037fea | ||
|
|
1e7808991d | ||
|
|
4f4cdf4f9e | ||
|
|
78f2c656d0 | ||
|
|
37ec969f96 | ||
|
|
f1a6693bb7 | ||
|
|
8ffd5a1d4f | ||
|
|
29eb5e8327 | ||
|
|
c175c21189 | ||
|
|
fc2862bd16 | ||
|
|
c9f814a9a7 | ||
|
|
92d2d3960b | ||
|
|
7872cb050d | ||
|
|
89029311c1 | ||
|
|
f6f586decb | ||
|
|
471c06b169 | ||
|
|
040bb1d1e0 | ||
|
|
bbaf5946f0 | ||
|
|
5286f23c9a | ||
|
|
7e9e33d462 | ||
|
|
04225f7bc2 | ||
|
|
dd0f1b2704 | ||
|
|
669807524e | ||
|
|
97a5405293 | ||
|
|
f298c7d053 | ||
|
|
a59f5344de | ||
|
|
13cfce48fa | ||
|
|
0261c243e0 | ||
|
|
ab325d6d2c | ||
|
|
b20930c111 | ||
|
|
770788d0a4 | ||
|
|
d02f615cad | ||
|
|
e17fe7e075 | ||
|
|
286686137f | ||
|
|
77c1112fe8 | ||
|
|
2d4ba357f7 | ||
|
|
455d0f8d66 | ||
|
|
5b0e7c6e82 | ||
|
|
78c665abb9 | ||
|
|
9a86d52d00 | ||
|
|
c5973f9a55 | ||
|
|
eb684aac03 | ||
|
|
7b4f8fb6d6 | ||
|
|
8065dbb2b7 | ||
|
|
049e791382 | ||
|
|
4fb8552563 | ||
|
|
90576f44d8 | ||
|
|
9e0a2964a4 | ||
|
|
49b16fdf0c | ||
|
|
1fcec8ce3b | ||
|
|
d32386a027 | ||
|
|
9b57d28c7d | ||
|
|
b9fd726c14 | ||
|
|
f165418b18 | ||
|
|
e193f63687 | ||
|
|
1eb37dded8 | ||
|
|
13889124c1 | ||
|
|
9005aaa14e | ||
|
|
df4e325e43 | ||
|
|
4ebc07b691 | ||
|
|
79a8d023ca | ||
|
|
330d83e7c3 | ||
|
|
a74384f3f5 | ||
|
|
da732c291f | ||
|
|
648d9dd19f | ||
|
|
e9faf657df | ||
|
|
103ffde025 | ||
|
|
baeb002245 | ||
|
|
0ce7a3f0ec | ||
|
|
0befad82a7 | ||
|
|
b357d8ae5b | ||
|
|
dd9beff9a5 | ||
|
|
d652664126 | ||
|
|
4666c12547 | ||
|
|
2b74260e2b | ||
|
|
620d336e55 | ||
|
|
e845a3388b | ||
|
|
a25235dc03 | ||
|
|
d3cbc8ea78 | ||
|
|
b6e197371d | ||
|
|
5cc3ff16a3 | ||
|
|
d93d5d2e37 | ||
|
|
7491af8ad7 | ||
|
|
fce95431e6 | ||
|
|
591a07c0fe | ||
|
|
c410f2d151 | ||
|
|
9502fa62e6 | ||
|
|
2a6480ec48 | ||
|
|
7c5ab885be | ||
|
|
21cfb151a8 | ||
|
|
84505b1717 | ||
|
|
d735e3006e | ||
|
|
af5d82dbde | ||
|
|
a97072eca0 | ||
|
|
cef6e248e7 | ||
|
|
b4c379f5fc | ||
|
|
3bb1206b9c | ||
|
|
002532401d | ||
|
|
8957c5892f | ||
|
|
1b8f41d353 | ||
|
|
0c51cc3738 | ||
|
|
2b9a8f0822 | ||
|
|
ddcfff3b59 | ||
|
|
449a3959b0 | ||
|
|
719a0c485b | ||
|
|
a4bbdc443f | ||
|
|
999afdf05e | ||
|
|
f492f6deb6 | ||
|
|
60f7ec8998 | ||
|
|
469d0ade72 | ||
|
|
1f33b03c30 | ||
|
|
351be2f327 | ||
|
|
3f401e8cac | ||
|
|
b20b21c553 | ||
|
|
c62863b1dc | ||
|
|
8505a0f260 | ||
|
|
98d878cdfe | ||
|
|
6730731652 | ||
|
|
27c35f69aa | ||
|
|
53671283ae | ||
|
|
d9fc7b32c3 | ||
|
|
9a03536e3d | ||
|
|
efebb8bb0b | ||
|
|
3bd1ae0be4 | ||
|
|
a07291d904 | ||
|
|
c0ac457cad | ||
|
|
6813a31895 | ||
|
|
8f5251583f | ||
|
|
c2122a6859 | ||
|
|
6dd65adebd | ||
|
|
c227143b53 | ||
|
|
cdd696c1ff | ||
|
|
3e6817cd18 | ||
|
|
a5ed607261 | ||
|
|
7118200885 | ||
|
|
b7f9064f0d | ||
|
|
5dc5bce1b2 | ||
|
|
bc7fef1d1a | ||
|
|
1908d131ca | ||
|
|
8cd2a00a25 | ||
|
|
c097852ab0 | ||
|
|
b02212009a | ||
|
|
9d1971f0fa | ||
|
|
2d6261703a | ||
|
|
a97c2ae6eb | ||
|
|
76e2c39c63 | ||
|
|
ab9fe42f58 | ||
|
|
9d78ce6193 | ||
|
|
959b540c02 | ||
|
|
68781492ad | ||
|
|
590e147186 | ||
|
|
0b358674ff | ||
|
|
0df01f2586 | ||
|
|
ca23665463 | ||
|
|
f55ac8e9c9 | ||
|
|
6e37fe6343 | ||
|
|
217bd934d7 | ||
|
|
58715f454c | ||
|
|
772f2a15ff | ||
|
|
5b0d8381b9 | ||
|
|
d841d86bbc | ||
|
|
ecaae87b79 | ||
|
|
5835abbcf6 | ||
|
|
2f7c2a2aea | ||
|
|
87ec7b09aa | ||
|
|
f8ec072093 | ||
|
|
781d2f0ad6 | ||
|
|
7bbd2c0e80 | ||
|
|
77bac11d82 | ||
|
|
315cfe4f2d | ||
|
|
707ed75138 | ||
|
|
c0e180759d | ||
|
|
6ceb423033 | ||
|
|
96c4286e7d | ||
|
|
f320ecbde8 | ||
|
|
d014ae0bff | ||
|
|
12a7934ca1 | ||
|
|
64bc791e48 | ||
|
|
1f33506962 | ||
|
|
ba9a94d026 | ||
|
|
6f13966d19 | ||
|
|
96cfad4e57 | ||
|
|
a26ebb1b69 | ||
|
|
7a764efc10 | ||
|
|
49b1f4c5af | ||
|
|
fbe56531d2 | ||
|
|
aa6b29a4b5 | ||
|
|
c88b9732eb | ||
|
|
2c29e8b179 | ||
|
|
d2d6b8e12f | ||
|
|
badfaa8545 | ||
|
|
c5b67d821d | ||
|
|
63bf7a29f3 | ||
|
|
845dd1f9e3 | ||
|
|
c9c44a934d | ||
|
|
2f6981a27f | ||
|
|
8739469db3 | ||
|
|
0c0c0babba | ||
|
|
950b32232f | ||
|
|
2f6034b067 | ||
|
|
159f7622e4 | ||
|
|
2cc2fa906a | ||
|
|
249390a75e | ||
|
|
189cf7ea5e | ||
|
|
56fe211466 | ||
|
|
955d03acb1 | ||
|
|
1b71a0f436 | ||
|
|
2af9e1431e | ||
|
|
ee961d01ed | ||
|
|
a3343bc1af | ||
|
|
ee04d57a7f | ||
|
|
076251e6a8 | ||
|
|
fdb3cee3f2 | ||
|
|
92e05bc438 | ||
|
|
69de2a047f | ||
|
|
05c458225f | ||
|
|
866125f2ab | ||
|
|
d0263e4fa3 | ||
|
|
e884ace0ab | ||
|
|
2ac4106004 | ||
|
|
59a63e4aea | ||
|
|
fbdf1f19f0 | ||
|
|
082101f99f | ||
|
|
f6761d637f | ||
|
|
2cf704abe0 | ||
|
|
ef612d0b58 | ||
|
|
43673c0598 | ||
|
|
9b45749bf0 | ||
|
|
f22243a209 | ||
|
|
ef32ac5cd4 | ||
|
|
52d85c9a41 | ||
|
|
a51c08bfb1 | ||
|
|
fb47c3a03d | ||
|
|
3b8f5ea099 | ||
|
|
63e0b53054 | ||
|
|
989f52494d | ||
|
|
0307e4161e | ||
|
|
c7c8b34adf | ||
|
|
1faf6c99db | ||
|
|
5f041ea7e9 | ||
|
|
14271be816 | ||
|
|
30d600040b | ||
|
|
10dd2337fc | ||
|
|
5ae4edf8fd | ||
|
|
d3df6d3749 | ||
|
|
8ba43e1745 | ||
|
|
cedd87001d | ||
|
|
afc901ddbe | ||
|
|
e4eb7ab589 | ||
|
|
b052928af2 | ||
|
|
a3b1c4608e | ||
|
|
204c42234c | ||
|
|
0fe046b92d | ||
|
|
fd6842cb5b | ||
|
|
4f40128bbe | ||
|
|
3d4580c03e | ||
|
|
66638655a5 | ||
|
|
2d214df76f | ||
|
|
8f1cd33d14 | ||
|
|
b2c47a7dea | ||
|
|
5f3f62ed46 | ||
|
|
bfdad7d4d5 | ||
|
|
577336d2df | ||
|
|
245638a1d4 | ||
|
|
ada05a1374 | ||
|
|
4b07f9e160 | ||
|
|
f8d8dc25c0 | ||
|
|
256ba8fa1b | ||
|
|
aa8b86c6b2 | ||
|
|
ae6b7e7259 | ||
|
|
2761c85564 | ||
|
|
ec41c11e58 | ||
|
|
3a91da5e52 | ||
|
|
690b843678 | ||
|
|
3f1ada29ac | ||
|
|
0c9df6ccbd | ||
|
|
b62b01fe7c | ||
|
|
efaf669479 | ||
|
|
94cd96cfde | ||
|
|
923ecc9d8a | ||
|
|
d5cb7ebf3b | ||
|
|
d6c2e9063a | ||
|
|
3088fe3f91 | ||
|
|
d4781280b7 | ||
|
|
008187caa4 | ||
|
|
6dffaae72e | ||
|
|
ae76ce4024 | ||
|
|
087945d7cb | ||
|
|
fc72d16bcb | ||
|
|
5817afd4ac | ||
|
|
765a6fcc62 | ||
|
|
0b20c46b79 | ||
|
|
c6864417c6 | ||
|
|
27f74382aa | ||
|
|
77961e8e93 | ||
|
|
3c564796e0 | ||
|
|
a4e5c7224f | ||
|
|
7ffe601743 | ||
|
|
c6091338ab | ||
|
|
99467cd874 | ||
|
|
587102f6bc | ||
|
|
f41d79d586 | ||
|
|
fc5030ff7b | ||
|
|
ca40cb4be3 | ||
|
|
e4141df0e9 | ||
|
|
c690f81574 | ||
|
|
47b942ca00 | ||
|
|
621313d63c | ||
|
|
d361935c09 | ||
|
|
a2bea87332 | ||
|
|
13b8c140b4 | ||
|
|
69a11e7375 | ||
|
|
60a01567d9 | ||
|
|
f064e56dc9 | ||
|
|
42ae27973e | ||
|
|
f6e2695a4d | ||
|
|
e840465ef3 | ||
|
|
f24e8e5f5c | ||
|
|
6376ab51f1 | ||
|
|
bd29d78a29 | ||
|
|
b35cd76854 | ||
|
|
205282c4bc | ||
|
|
a4fd74b58e | ||
|
|
25072f0637 | ||
|
|
937955b36d | ||
|
|
2e8867eda6 | ||
|
|
bd399a134b | ||
|
|
45caf394f0 | ||
|
|
d67e2187d0 | ||
|
|
c8584d576a | ||
|
|
418a12e75f | ||
|
|
05d237ee84 | ||
|
|
9a044f31a3 | ||
|
|
8320754b98 | ||
|
|
703ce2e292 | ||
|
|
c9353ebee3 | ||
|
|
9601868e0f | ||
|
|
fea2228b16 | ||
|
|
7d4ce483c5 | ||
|
|
e6605e5ac8 | ||
|
|
2472d6fd1b | ||
|
|
3311146aba | ||
|
|
793528b057 | ||
|
|
b8adaf6fbe | ||
|
|
24329a26de | ||
|
|
f1a65f9d0e | ||
|
|
ae46b3df32 | ||
|
|
d5c1e3c6e0 | ||
|
|
a95f612452 | ||
|
|
3201d1c3bc | ||
|
|
2b10a03178 | ||
|
|
80fb7e4ab8 | ||
|
|
7c1ddd9447 | ||
|
|
c55074f7fe | ||
|
|
36643cf5f5 | ||
|
|
92a62d93ef | ||
|
|
450e877cfb | ||
|
|
c0fbfccf43 | ||
|
|
51ccc3aa9e | ||
|
|
e35f137986 | ||
|
|
71c35304d6 | ||
|
|
c1733a4ac6 | ||
|
|
66a7e8eab9 | ||
|
|
a872231f8a | ||
|
|
52ec4d511c | ||
|
|
aa7fb86798 | ||
|
|
875eeb699c | ||
|
|
b239b4dc57 | ||
|
|
19db5ba421 | ||
|
|
e225af28dc | ||
|
|
eecf89a9c0 | ||
|
|
c9b1ee532d | ||
|
|
8c27baae84 | ||
|
|
8c225a3c65 | ||
|
|
7ff1f3a759 | ||
|
|
bbc8fc0269 | ||
|
|
9d81511153 | ||
|
|
16d63bd0ce | ||
|
|
f2b7ff2b79 | ||
|
|
bc8453283f | ||
|
|
2ff5046dcd | ||
|
|
917090856f | ||
|
|
b45d633a34 | ||
|
|
da03490310 | ||
|
|
fdfe62edf0 | ||
|
|
aedca25fa8 | ||
|
|
b29bcbbd41 | ||
|
|
8e8170b667 | ||
|
|
2fa38c7dc4 | ||
|
|
ca8a0ca8d2 | ||
|
|
58bb7169a0 | ||
|
|
cb541d75a9 | ||
|
|
4ee01acb40 | ||
|
|
d678c48884 | ||
|
|
9f9f02fc6f | ||
|
|
abf135abce | ||
|
|
f7beec4728 | ||
|
|
ccf3450864 | ||
|
|
86553a4fc9 | ||
|
|
55349ea570 | ||
|
|
486b03e985 | ||
|
|
ccb232b6ac | ||
|
|
c25efac0c1 | ||
|
|
22af1b551a | ||
|
|
c696d226b2 | ||
|
|
9035a06b4a | ||
|
|
056940a4ad | ||
|
|
e7af338c31 | ||
|
|
9069e5b33e | ||
|
|
82db1f1db6 | ||
|
|
f46059ec4c | ||
|
|
0c71de4e59 | ||
|
|
0fa654e53a | ||
|
|
45c17659cc | ||
|
|
b901f8d9ae | ||
|
|
9c60a7966f | ||
|
|
6d66a53f8d | ||
|
|
324627482a | ||
|
|
0c6c189028 | ||
|
|
8a68ae0d04 | ||
|
|
5661e5dad6 | ||
|
|
e9affb50d2 | ||
|
|
c00173dbd2 | ||
|
|
e8c6fccd63 | ||
|
|
487b8c6e9e | ||
|
|
b2481d1450 | ||
|
|
399fbc5d65 | ||
|
|
48b38ed94b | ||
|
|
c0444ef16f | ||
|
|
1719a8e764 | ||
|
|
242bcc8353 | ||
|
|
092af0f9f9 | ||
|
|
133a7ff166 | ||
|
|
5df08410e7 | ||
|
|
9f9787bc03 | ||
|
|
7129a19f35 | ||
|
|
f45ffc8773 | ||
|
|
3162f74945 | ||
|
|
6cef3e41e7 | ||
|
|
c0e2ec8dec | ||
|
|
aee81c8dcd | ||
|
|
9e736ab0d7 | ||
|
|
85752b0fc7 | ||
|
|
c6f34c59b4 | ||
|
|
7f07725840 | ||
|
|
c81d090464 | ||
|
|
c524732849 | ||
|
|
5e303f8a1f | ||
|
|
2246564279 | ||
|
|
eff0c1fe89 | ||
|
|
ad322476d2 | ||
|
|
2561742683 | ||
|
|
fa9e31fe03 | ||
|
|
3ac5b045c4 | ||
|
|
6a593e01e1 | ||
|
|
6f6dd2291e | ||
|
|
2b4ddc07f5 | ||
|
|
63c650c33e | ||
|
|
dc29161f37 | ||
|
|
8a6fdafc79 | ||
|
|
ea40bd991c | ||
|
|
e19dd46f0f | ||
|
|
532b06c280 | ||
|
|
a8480d1eaf | ||
|
|
0cf7aaffff | ||
|
|
e2e1819ef1 | ||
|
|
31b89e2932 | ||
|
|
a021ff7eb8 | ||
|
|
bb5d0fac90 | ||
|
|
df5ed64514 | ||
|
|
9db5f9ff67 | ||
|
|
ca83a78e13 | ||
|
|
13eef9a309 | ||
|
|
2a8ac2c0c6 | ||
|
|
c97342db99 | ||
|
|
d7b2a0ed79 | ||
|
|
af0a1b5db5 | ||
|
|
9cf030d587 | ||
|
|
c04d70d5e5 | ||
|
|
2a47819fd6 | ||
|
|
4516c8f9b5 | ||
|
|
e4fdf26dc7 | ||
|
|
dd511588a2 | ||
|
|
79dad8ec8c | ||
|
|
39d14fedc2 | ||
|
|
1da38fc748 | ||
|
|
b5f50efdcd | ||
|
|
046e691d4e | ||
|
|
e72531b090 | ||
|
|
81e320c9cf | ||
|
|
fa8cc74141 | ||
|
|
c7d9ff7cc0 | ||
|
|
8704a9d08f | ||
|
|
c0d27e2ce9 | ||
|
|
84b9028ecb | ||
|
|
4fda7098c0 | ||
|
|
8e8264efb0 | ||
|
|
54e780a6ca | ||
|
|
125eb2b784 | ||
|
|
6ea9cdc83b | ||
|
|
c0711fde69 | ||
|
|
20b8d2c4a5 | ||
|
|
73ae151971 | ||
|
|
f4806c9dd7 | ||
|
|
79532210e8 | ||
|
|
d7f26493a5 | ||
|
|
b9d025dd58 | ||
|
|
f435086a5a | ||
|
|
3dcdf372d7 | ||
|
|
cd84f2867c | ||
|
|
cafe00e463 | ||
|
|
fd9ffbbb88 | ||
|
|
d1be7cf142 | ||
|
|
d1f0be215b | ||
|
|
3a2c17998e | ||
|
|
a0dd051511 | ||
|
|
4faff3ec6f | ||
|
|
f110225173 | ||
|
|
2684257e7e | ||
|
|
51fb1021df | ||
|
|
51d0d0d6c5 | ||
|
|
047df76373 | ||
|
|
6da4e30215 | ||
|
|
dbf0569e29 | ||
|
|
18220b88b3 | ||
|
|
665da2fb00 | ||
|
|
57ffe6622d | ||
|
|
485fec9649 | ||
|
|
bd85736226 | ||
|
|
4ec8986934 | ||
|
|
b963216764 | ||
|
|
813fd95bc8 | ||
|
|
3598c91c29 | ||
|
|
507cd1dd20 | ||
|
|
e39506824d | ||
|
|
f68a31ab28 | ||
|
|
b1181deb58 | ||
|
|
89b32dd7ee | ||
|
|
c54e87f9a2 | ||
|
|
eee7e1de57 | ||
|
|
3c60df1565 | ||
|
|
a827017bd2 | ||
|
|
95c502c658 | ||
|
|
0f573901d5 | ||
|
|
fdc9bf5783 | ||
|
|
37e0f9a325 | ||
|
|
0c06d8db3c | ||
|
|
0be4bbb369 | ||
|
|
f02ab88393 | ||
|
|
c9d4de8808 | ||
|
|
adb912b665 | ||
|
|
3f5da1e03e | ||
|
|
0a40d920e3 | ||
|
|
39311f1e40 | ||
|
|
9cd24a5646 | ||
|
|
1c0efde315 | ||
|
|
c82905bbdd | ||
|
|
275eace968 | ||
|
|
5688c8b81e | ||
|
|
8b2798abd5 | ||
|
|
6d977923b6 | ||
|
|
52dacaed37 | ||
|
|
7a381eaea1 | ||
|
|
69391e186b | ||
|
|
06f8beaa17 | ||
|
|
3798f4ca5b | ||
|
|
4fd243a6e4 | ||
|
|
d458f673be | ||
|
|
cfcb00b943 | ||
|
|
977e47d109 | ||
|
|
cfeb40f36d | ||
|
|
4fcc3ac1de | ||
|
|
f4afa6931b | ||
|
|
71be71d63d | ||
|
|
de9f7e6c39 | ||
|
|
7c8db2b501 | ||
|
|
cd653f9434 | ||
|
|
74bc05936d | ||
|
|
7aacfd66ef | ||
|
|
3636b87db0 | ||
|
|
d6bd328576 | ||
|
|
0af5b225c4 | ||
|
|
f7dcef39ce | ||
|
|
07042178d2 | ||
|
|
243878f2a0 | ||
|
|
d3f8a76cce | ||
|
|
20131a51a2 | ||
|
|
1c9a369774 | ||
|
|
dcb426f58f | ||
|
|
35bcb5297a | ||
|
|
84e3d7c276 | ||
|
|
9b03f0ac8e | ||
|
|
eb402809e2 | ||
|
|
e9c9e40624 | ||
|
|
01eed97b91 | ||
|
|
94a47dba7d | ||
|
|
bce2c9347b | ||
|
|
da8b1d41c7 | ||
|
|
3ddae5faec | ||
|
|
34faea6100 | ||
|
|
01848a9e5d | ||
|
|
10db80541f | ||
|
|
edd1268f5f | ||
|
|
11c16e8bbc | ||
|
|
7d411351c0 | ||
|
|
df21602c90 | ||
|
|
ce4ccf3cc4 | ||
|
|
a7f93de3ad | ||
|
|
8e8257adf3 | ||
|
|
e627725dfc | ||
|
|
b3ba557b8b | ||
|
|
bd03650140 | ||
|
|
42f51f33a8 | ||
|
|
8295b88d96 | ||
|
|
70313b2660 | ||
|
|
745d3775b4 | ||
|
|
aa176b6593 | ||
|
|
b0e3a7524f | ||
|
|
5ceee50bb5 | ||
|
|
ebdad76fb2 | ||
|
|
925829dc58 | ||
|
|
e04ea853dc | ||
|
|
9587729bb0 | ||
|
|
6ec368bf02 | ||
|
|
d71c7b512f | ||
|
|
349701ac14 | ||
|
|
d424fa5ea8 | ||
|
|
ca6293eefe | ||
|
|
d289e8a86f | ||
|
|
96328526b7 | ||
|
|
279c89dca3 | ||
|
|
a7a52e08d1 | ||
|
|
f6336855d0 | ||
|
|
727d8a6456 | ||
|
|
7b80b95381 | ||
|
|
2867f8fd53 | ||
|
|
cdf416cb73 | ||
|
|
7716d62018 | ||
|
|
d5f76b16b9 | ||
|
|
552406b15f | ||
|
|
abb52e5446 | ||
|
|
de37a0c31e | ||
|
|
6e31ba30c7 | ||
|
|
8fe1c518d9 | ||
|
|
b6006fe3d5 | ||
|
|
3e8173c4bd | ||
|
|
d8a15d6324 | ||
|
|
9a3d558f61 | ||
|
|
85ddf3be1b | ||
|
|
3ca42b8f51 | ||
|
|
b75c7ad179 | ||
|
|
44f89c969d | ||
|
|
5595fb38c1 | ||
|
|
c0e0e095c9 | ||
|
|
6c1c0640f2 | ||
|
|
698102371f | ||
|
|
997ed283bf | ||
|
|
9128f7d4b3 | ||
|
|
93d0257be7 | ||
|
|
adc71e7ed2 | ||
|
|
516e18ca80 | ||
|
|
4777e53c23 | ||
|
|
d6912cfd8e | ||
|
|
621306e610 | ||
|
|
0e507e1923 | ||
|
|
15a0b3694d | ||
|
|
6e4cf22cf0 | ||
|
|
58859848a3 | ||
|
|
55f61826bf | ||
|
|
f80d357b77 | ||
|
|
c972197643 | ||
|
|
c06b7b2b48 | ||
|
|
3c69beef94 | ||
|
|
e55c5e10bc | ||
|
|
e321528a6d | ||
|
|
ee897bce6c | ||
|
|
186a52172c | ||
|
|
21570fc24f | ||
|
|
2edc6b363d | ||
|
|
0c74303e9d | ||
|
|
244e597a9f | ||
|
|
15833e1e53 | ||
|
|
73d64d378a | ||
|
|
8d04410f45 | ||
|
|
36d28d2da6 | ||
|
|
4a653ab054 | ||
|
|
651bd71454 | ||
|
|
1e9ebbc476 | ||
|
|
27c16ba185 | ||
|
|
51a8c7118a | ||
|
|
808c4ff5ca | ||
|
|
ded2b86e55 | ||
|
|
9efcdc7c67 | ||
|
|
34e6dbec81 | ||
|
|
62b655ccea | ||
|
|
3c2aac87f7 | ||
|
|
3aba097096 | ||
|
|
f45451ca74 | ||
|
|
c35fec9f20 | ||
|
|
88fa24ce79 | ||
|
|
59577b9d79 | ||
|
|
c349ad62e7 | ||
|
|
d5b57840d9 | ||
|
|
f8a3d143cb | ||
|
|
c717dfc33d | ||
|
|
22d9096c3d | ||
|
|
8080bc608b | ||
|
|
c4b9d60afa | ||
|
|
dda5568e2c | ||
|
|
2d8e00e2a0 | ||
|
|
901ff6bb1e | ||
|
|
7312c56d6c | ||
|
|
031c58e21c | ||
|
|
35b1cfcc42 | ||
|
|
e545778154 | ||
|
|
6fd2bc5f83 | ||
|
|
9a587b2743 | ||
|
|
bacc1b1dad | ||
|
|
575b69c541 | ||
|
|
73092b4b40 | ||
|
|
877dc824a9 | ||
|
|
89c76dca11 | ||
|
|
2253ea1b41 | ||
|
|
b732a13d6c | ||
|
|
8e0c224813 | ||
|
|
5a96dc0083 | ||
|
|
181db06b0c | ||
|
|
47ccfb6106 | ||
|
|
5f97740ab7 | ||
|
|
90d6878bbb | ||
|
|
5c70f36aa5 | ||
|
|
09cc0a85db | ||
|
|
a47fcdacb5 | ||
|
|
ef0891ae5d | ||
|
|
a8d7700295 | ||
|
|
412916ba7c | ||
|
|
616290edcc | ||
|
|
9ed19892e2 | ||
|
|
88cf60ad9d | ||
|
|
7f59e76c72 | ||
|
|
dcb9125b32 | ||
|
|
2743b9d310 | ||
|
|
2f779bfd37 | ||
|
|
db2193b526 | ||
|
|
7205e9a5b4 | ||
|
|
1ca83509dd | ||
|
|
7135a12300 | ||
|
|
fae9ea8b3b | ||
|
|
b96ee7be72 | ||
|
|
9e449bebf9 | ||
|
|
8bdbbfbe16 | ||
|
|
0ba4925f75 | ||
|
|
26d50fda9a | ||
|
|
bc22ab7b87 | ||
|
|
d5e3e63d6d |
2
.github/pull_request_template.md
vendored
@@ -7,7 +7,7 @@
|
||||
is appreciated." This will allow other devs to potentially save you time by not accidentially duplicating work etc...
|
||||
- Please do not check in files that don't have real changes
|
||||
- Please do not reformat lines that you didn't have to change the code on
|
||||
- We recommend using the [Visual Studio Code](https://platformio.org/install/ide?install=vscode) editor,
|
||||
- We recommend using the [Visual Studio Code](https://platformio.org/install/ide?install=vscode) editor and the 'clang-format' extension,
|
||||
because automatically follows our indentation rules and it's auto reformatting will not cause spurious changes to lines.
|
||||
- If your PR fixes a bug, mention "fixes #bugnum" somewhere in your pull request description.
|
||||
- If your other co-developers have comments on your PR please tweak as needed.
|
||||
|
||||
35
.github/workflows/main.yml
vendored
@@ -4,23 +4,38 @@ on:
|
||||
- pull_request
|
||||
|
||||
jobs:
|
||||
main:
|
||||
name: Main
|
||||
setup:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Checkout submodules
|
||||
uses: textbook/git-checkout-submodule-action@master
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@master
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.x
|
||||
- name: Install Platform IO
|
||||
- name: Install Platform IO and meshtastic-python
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -U platformio
|
||||
pip install -U platformio meshtastic
|
||||
- name: Install extra python tools
|
||||
run: |
|
||||
pip install -U adafruit-nrfutil
|
||||
- name: Build
|
||||
run: platformio run -e tbeam -e heltec -e lora-relay-v1 -e linux
|
||||
- name: Install libs needed for linux build
|
||||
run: |
|
||||
sudo apt install -y libpsocksxx-dev
|
||||
- name: Build for tbeam
|
||||
run: platformio run -e tbeam
|
||||
- name: Build for heltec
|
||||
run: platformio run -e heltec
|
||||
- name: Build for lora-relay-v1
|
||||
run: platformio run -e lora-relay-v1
|
||||
- name: Build for native
|
||||
run: platformio run -e native
|
||||
- name: Integration test
|
||||
run: |
|
||||
.pio/build/native/program &
|
||||
sleep 1
|
||||
python3 -c 'from meshtastic.test import testSimulator; testSimulator()'
|
||||
|
||||
|
||||
3
.gitignore
vendored
@@ -15,7 +15,8 @@ Thumbs.db
|
||||
.built
|
||||
.context
|
||||
.cproject
|
||||
.idea/*
|
||||
.vagrant
|
||||
nanopb*
|
||||
flash.uf2
|
||||
cmake-build*
|
||||
|
||||
|
||||
3
.gitmodules
vendored
@@ -4,3 +4,6 @@
|
||||
[submodule "sdk-nrfxlib"]
|
||||
path = sdk-nrfxlib
|
||||
url = https://github.com/nrfconnect/sdk-nrfxlib.git
|
||||
[submodule "design"]
|
||||
path = design
|
||||
url = https://github.com/meshtastic/meshtastic-design.git
|
||||
|
||||
7
.idea/codeStyles/Project.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<clangFormatSettings>
|
||||
<option name="ENABLED" value="true" />
|
||||
</clangFormatSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
||||
2
.idea/meshtastic-esp32.iml
generated
Normal file
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module classpath="CMake" type="CPP_MODULE" version="4" />
|
||||
4
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/meshtastic-esp32.iml" filepath="$PROJECT_DIR$/.idea/meshtastic-esp32.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
9
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/design" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/proto" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/sdk-nrfxlib" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
137
.idea/workspace.xml
generated
Normal file
@@ -0,0 +1,137 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CMakeRunConfigurationManager" shouldGenerate="true" shouldDeleteObsolete="true">
|
||||
<generated>
|
||||
<config projectName="meshtastic-esp32" targetName="Debug" />
|
||||
<config projectName="meshtastic-esp32" targetName="Production" />
|
||||
<config projectName="meshtastic-esp32" targetName="Z_DUMMY_TARGET" />
|
||||
</generated>
|
||||
</component>
|
||||
<component name="CMakeSettings">
|
||||
<configurations>
|
||||
<configuration PROFILE_NAME="native" CONFIG_NAME="native" ENABLED="true" />
|
||||
</configurations>
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="58922733-b05b-4b90-9655-b9b18914977a" name="Default Changelist" comment="">
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/docs/software/TODO.md" beforeDir="false" afterPath="$PROJECT_DIR$/docs/software/TODO.md" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/proto" beforeDir="false" afterPath="$PROJECT_DIR$/proto" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/mesh/generated/mesh.pb.h" beforeDir="false" afterPath="$PROJECT_DIR$/src/mesh/generated/mesh.pb.h" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="ExecutionTargetManager" SELECTED_TARGET="CMakeBuildProfile:native" />
|
||||
<component name="Git.Settings">
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="ProjectId" id="1pmWHw2wau2TbdKvXvmQUB0EUE9" />
|
||||
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent">
|
||||
<property name="ASKED_ADD_EXTERNAL_FILES" value="true" />
|
||||
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
|
||||
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
|
||||
<property name="WebServerToolWindowFactoryState" value="false" />
|
||||
<property name="cf.advertisement.text.overridden" value="true" />
|
||||
<property name="cf.first.check.clang-format" value="false" />
|
||||
<property name="node.js.detected.package.eslint" value="true" />
|
||||
<property name="node.js.detected.package.tslint" value="true" />
|
||||
<property name="node.js.path.for.package.eslint" value="project" />
|
||||
<property name="node.js.path.for.package.tslint" value="project" />
|
||||
<property name="node.js.selected.package.eslint" value="(autodetect)" />
|
||||
<property name="node.js.selected.package.tslint" value="(autodetect)" />
|
||||
<property name="settings.editor.selected.configurable" value="CMakeSettings" />
|
||||
</component>
|
||||
<component name="RunManager" selected="PlatformIO.PlatformIO Upload">
|
||||
<configuration default="true" type="CLion_Remote" version="1" remoteCommand="tcp:localhost:2345" symbolFile="" sysroot="">
|
||||
<debugger kind="GDB" isBundled="true" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<configuration name="gdbremote-localhost-2345" type="CLion_Remote" version="1" remoteCommand="tcp:localhost:2345" symbolFile="" sysroot="">
|
||||
<debugger kind="GDB" isBundled="true" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<configuration name="Z_DUMMY_TARGET" type="CMakeRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="meshtastic-esp32" TARGET_NAME="Z_DUMMY_TARGET" CONFIG_NAME="native" RUN_TARGET_PROJECT_NAME="meshtastic-esp32" RUN_TARGET_NAME="Z_DUMMY_TARGET">
|
||||
<method v="2">
|
||||
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
<configuration default="true" type="GradleAppRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" PASS_PARENT_ENVS_2="true">
|
||||
<method v="2">
|
||||
<option name="com.jetbrains.cidr.cpp.gradle.execution.GradleNativeBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
<configuration name="PlatformIO Debug" type="platformio" factoryName="PlatformIO Debug" REDIRECT_INPUT="false" ELEVATE="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="meshtastic-esp32" TARGET_NAME="Debug" CONFIG_NAME="native" RUN_TARGET_PROJECT_NAME="meshtastic-esp32" RUN_TARGET_NAME="Debug">
|
||||
<method v="2">
|
||||
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
<configuration name="PlatformIO Upload" type="platformio" factoryName="PlatformIO Upload" REDIRECT_INPUT="false" ELEVATE="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="meshtastic-esp32" TARGET_NAME="Production" CONFIG_NAME="native" RUN_TARGET_PROJECT_NAME="meshtastic-esp32" RUN_TARGET_NAME="Production">
|
||||
<method v="2">
|
||||
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
<list>
|
||||
<item itemvalue="CMake Application.Z_DUMMY_TARGET" />
|
||||
<item itemvalue="GDB Remote Debug.gdbremote-localhost-2345" />
|
||||
<item itemvalue="PlatformIO.PlatformIO Debug" />
|
||||
<item itemvalue="PlatformIO.PlatformIO Upload" />
|
||||
</list>
|
||||
</component>
|
||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="58922733-b05b-4b90-9655-b9b18914977a" name="Default Changelist" comment="" />
|
||||
<created>1615788661896</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1615788661896</updated>
|
||||
<workItem from="1615788663210" duration="6661000" />
|
||||
<workItem from="1615938346019" duration="1208000" />
|
||||
<workItem from="1615971126983" duration="5945000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
<component name="Vcs.Log.Tabs.Properties">
|
||||
<option name="TAB_STATES">
|
||||
<map>
|
||||
<entry key="MAIN">
|
||||
<value>
|
||||
<State />
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
<option name="oldMeFiltersMigrated" value="true" />
|
||||
</component>
|
||||
<component name="XDebuggerManager">
|
||||
<breakpoint-manager>
|
||||
<breakpoints>
|
||||
<line-breakpoint enabled="true" type="com.jetbrains.cidr.execution.debugger.OCBreakpointType">
|
||||
<url>file://$PROJECT_DIR$/src/mesh/StreamAPI.cpp</url>
|
||||
<line>20</line>
|
||||
<option name="timeStamp" value="4" />
|
||||
</line-breakpoint>
|
||||
<line-breakpoint enabled="true" type="com.jetbrains.cidr.execution.debugger.OCBreakpointType">
|
||||
<url>file://$PROJECT_DIR$/src/mesh/wifi/WiFiServerAPI.cpp</url>
|
||||
<line>53</line>
|
||||
<option name="timeStamp" value="6" />
|
||||
</line-breakpoint>
|
||||
</breakpoints>
|
||||
</breakpoint-manager>
|
||||
</component>
|
||||
<component name="XSLT-Support.FileAssociations.UIState">
|
||||
<expand />
|
||||
<select />
|
||||
</component>
|
||||
</project>
|
||||
3
.vscode/extensions.json
vendored
@@ -2,6 +2,7 @@
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
"platformio.platformio-ide",
|
||||
"xaver.clang-format"
|
||||
]
|
||||
}
|
||||
|
||||
12
.vscode/settings.json
vendored
@@ -68,5 +68,15 @@
|
||||
"protobufs",
|
||||
"wifi"
|
||||
],
|
||||
"C_Cpp.dimInactiveRegions": true
|
||||
"C_Cpp.dimInactiveRegions": true,
|
||||
"cmake.configureOnOpen": true,
|
||||
"protoc": {
|
||||
"compile_on_save": false,
|
||||
"compile_all_path": "/home/kevinh/development/meshtastic/meshtastic-esp32/proto",
|
||||
"options": [
|
||||
"--java_out=/tmp",
|
||||
"-I=/home/kevinh/development/meshtastic/meshtastic-esp32/proto"
|
||||
]
|
||||
},
|
||||
"editor.formatOnSave": true
|
||||
}
|
||||
36
CMakeLists.txt
Normal file
@@ -0,0 +1,36 @@
|
||||
# !!! WARNING !!! AUTO-GENERATED FILE, PLEASE DO NOT MODIFY IT AND USE
|
||||
# https://docs.platformio.org/page/projectconf/section_env_build.html#build-flags
|
||||
#
|
||||
# If you need to override existing CMake configuration or add extra,
|
||||
# please create `CMakeListsUser.txt` in the root of project.
|
||||
# The `CMakeListsUser.txt` will not be overwritten by PlatformIO.
|
||||
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
set(CMAKE_SYSTEM_NAME Generic)
|
||||
set(CMAKE_C_COMPILER_WORKS 1)
|
||||
set(CMAKE_CXX_COMPILER_WORKS 1)
|
||||
|
||||
project("meshtastic-esp32" C CXX)
|
||||
|
||||
include(CMakeListsPrivate.txt)
|
||||
|
||||
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/CMakeListsUser.txt)
|
||||
include(CMakeListsUser.txt)
|
||||
endif()
|
||||
|
||||
include_directories("$ENV{HOME}/.platformio/packages/framework-portduino")
|
||||
include_directories("/usr/include")
|
||||
|
||||
add_custom_target(
|
||||
Production ALL
|
||||
COMMAND platformio -c clion run "$<$<NOT:$<CONFIG:All>>:-e${CMAKE_BUILD_TYPE}>"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
add_custom_target(
|
||||
Debug ALL
|
||||
COMMAND platformio -c clion run --target debug "$<$<NOT:$<CONFIG:All>>:-e${CMAKE_BUILD_TYPE}>"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
add_executable(Z_DUMMY_TARGET ${SRC_LIST})
|
||||
2041
CMakeListsPrivate.txt
Normal file
16
README.md
@@ -4,7 +4,7 @@ This is the device side code for the [meshtastic.org](https://www.meshtastic.org
|
||||
|
||||

|
||||
|
||||
Meshtastic™ is a project that lets you use
|
||||
Meshtastic® is a project that lets you use
|
||||
inexpensive GPS mesh radios as an extensible, super long battery life mesh GPS communicator. These radios are great for hiking, skiing, paragliding -
|
||||
essentially any hobby where you don't have reliable internet access. Each member of your private mesh can always see the location and distance of all other
|
||||
members and any text messages sent to your group chat.
|
||||
@@ -30,11 +30,14 @@ We currently support three models of radios.
|
||||
- [T-Beam V0.7 w/ NEO-6M](https://www.aliexpress.com/item/4000574335430.html) (will work but **you must use the tbeam0.7 firmware ** - but the T-Beam V1.0 or later are better!)
|
||||
- board labels "TTGO T22_V07 20180711"
|
||||
- 3D printable cases
|
||||
- [T-Beam V0](https://www.thingiverse.com/thing:3773717)
|
||||
- [T-Beam V0](https://www.thingiverse.com/thing:3773717) (GPS and LoRa antenna misaligned if GPS placed as pictured)
|
||||
- [T-Beam V1 (SMA-antenna)](https://www.thingiverse.com/thing:3830711)
|
||||
- [T-Beam V1 (IPEX-antenna)](https://www.thingiverse.com/thing:4587297)
|
||||
- [T-Beam V1 (SMA-antenna)](https://www.thingiverse.com/thing:4677388) (Mounting option for larger GPS antenna but LoRa antenna enclosed)
|
||||
- [T-Beam V1 (IPEX-antenna)](https://www.thingiverse.com/thing:4587297) (GPS and LoRa antenna misaligned if GPS placed as pictured)
|
||||
- [T-Beam V1 (IPEX-antenna)](https://www.thingiverse.com/thing:4589651)
|
||||
- [T-Beam V1 (IPEX-antenna)](https://www.thingiverse.com/thing:4619981) (GPS and LoRa antenna misaligned if GPS placed as pictured)
|
||||
- Laser-cut cases
|
||||
- [T-Beam V1](https://www.thingiverse.com/thing:4552771)
|
||||
- [T-Beam V1 (SMA-antenna)](https://www.thingiverse.com/thing:4552771)
|
||||
|
||||
- [TTGO LORA32](https://www.aliexpress.com/item/4000211331316.html) - No GPS
|
||||
- version 2.1
|
||||
@@ -43,7 +46,10 @@ We currently support three models of radios.
|
||||
- [TTGO LORA32 v1](https://www.thingiverse.com/thing:3385109)
|
||||
|
||||
- [Heltec LoRa 32](https://heltec.org/project/wifi-lora-32/) - No GPS
|
||||
- [Official Heltec case](https://www.aliexpress.com/item/4001050707951.html)
|
||||
- [3D Printable case](https://www.thingiverse.com/thing:3125854)
|
||||
|
||||
Note: The GPS and LoRa stock antennas should be placed in a way, that the GPS antenna faces the sky and the LoRa antenna radiates 360 degrees horizontally. For better GPS reception you might want to [upgrade the GPS antenna](https://meshtastic.discourse.group/t/the-importance-of-gps-antennas-and-request-to-3d-case-documentation-people/1505) and to properly align the antennas you might want to upgrade to a LoRa antenna that can be adjusted to radiate into the right directions.
|
||||
|
||||
**Make sure to get the frequency for your country**
|
||||
|
||||
@@ -193,7 +199,7 @@ We'd love to have you join us on this merry little project. Please see our [deve
|
||||
|
||||
# Credits
|
||||
|
||||
This project is run by volunteers. Past contributors include:
|
||||
This project is run by volunteers. We are a friendly group and welcome any contribution (code fixes, documentation, features, bug reports etc...). We try to be good about listing contributor names in release notes, but it has become unwieldy for the main-devs to keep updating the list below and we've neglected it too long. If you'd like your name included in this list please send a pull request to edit this README and simply add your line yourself. Thank you very much for your help!
|
||||
|
||||
- @astro-arphid: Added support for 433MHz radios in europe.
|
||||
- @claesg: Various documentation fixes and 3D print enclosures
|
||||
|
||||
@@ -4,11 +4,7 @@ set -e
|
||||
|
||||
VERSION=`bin/buildinfo.py`
|
||||
|
||||
COUNTRIES="US EU433 EU865 CN JP ANZ KR"
|
||||
#COUNTRIES=US
|
||||
#COUNTRIES=CN
|
||||
|
||||
BOARDS_ESP32="tlora-v2 tlora-v1 tlora-v2-1-1.6 tbeam heltec tbeam0.7"
|
||||
BOARDS_ESP32="tlora-v2 tlora-v1 tlora_v1_3 tlora-v2-1-1.6 tbeam heltec tbeam0.7"
|
||||
#BOARDS_ESP32=tbeam
|
||||
|
||||
# FIXME note nrf52840dk build is for some reason only generating a BIN file but not a HEX file nrf52840dk-geeksville is fine
|
||||
@@ -21,35 +17,26 @@ ARCHIVEDIR=release/archive
|
||||
|
||||
rm -f $OUTDIR/firmware*
|
||||
|
||||
mkdir -p $OUTDIR/bins
|
||||
mkdir -p $OUTDIR/bins $ARCHIVEDIR
|
||||
rm -r $OUTDIR/bins/*
|
||||
mkdir -p $OUTDIR/bins/universal $OUTDIR/elfs/universal
|
||||
|
||||
# build the named environment and copy the bins to the release directory
|
||||
function do_build() {
|
||||
BOARD=$1
|
||||
COUNTRY=$2
|
||||
isNrf=$3
|
||||
|
||||
echo "Building $COUNTRY for $BOARD with $PLATFORMIO_BUILD_FLAGS"
|
||||
echo "Building for $BOARD with $PLATFORMIO_BUILD_FLAGS"
|
||||
rm -f .pio/build/$BOARD/firmware.*
|
||||
|
||||
# The shell vars the build tool expects to find
|
||||
export APP_VERSION=$VERSION
|
||||
|
||||
# Are we building a universal/regionless rom?
|
||||
if [ "x$COUNTRY" != "x" ]
|
||||
then
|
||||
export HW_VERSION="1.0-$COUNTRY"
|
||||
export COUNTRY
|
||||
basename=firmware-$BOARD-$COUNTRY-$VERSION
|
||||
else
|
||||
export HW_VERSION="1.0"
|
||||
unset COUNTRY
|
||||
basename=universal/firmware-$BOARD-$VERSION
|
||||
fi
|
||||
export HW_VERSION="1.0"
|
||||
basename=universal/firmware-$BOARD-$VERSION
|
||||
|
||||
pio run --jobs 4 --environment $BOARD # -v
|
||||
pio run --environment $BOARD # -v
|
||||
SRCELF=.pio/build/$BOARD/firmware.elf
|
||||
cp $SRCELF $OUTDIR/elfs/$basename.elf
|
||||
|
||||
@@ -69,10 +56,6 @@ function do_boards() {
|
||||
declare boards=$1
|
||||
declare isNrf=$2
|
||||
for board in $boards; do
|
||||
for country in $COUNTRIES; do
|
||||
do_build $board $country "$isNrf"
|
||||
done
|
||||
|
||||
# Build universal
|
||||
do_build $board "" "$isNrf"
|
||||
done
|
||||
@@ -92,7 +75,7 @@ pio run --environment tbeam -t buildfs
|
||||
cp .pio/build/tbeam/spiffs.bin $OUTDIR/bins/universal/spiffs-$VERSION.bin
|
||||
|
||||
# keep the bins in archive also
|
||||
cp $OUTDIR/bins/firmware* $OUTDIR/bins/universal/spiffs* $OUTDIR/elfs/firmware* $OUTDIR/bins/universal/firmware* $OUTDIR/elfs/universal/firmware* $ARCHIVEDIR
|
||||
cp $OUTDIR/bins/universal/spiffs* $OUTDIR/bins/universal/firmware* $OUTDIR/elfs/universal/firmware* $ARCHIVEDIR
|
||||
|
||||
echo Updating android bins $OUTDIR/forandroid
|
||||
rm -rf $OUTDIR/forandroid
|
||||
@@ -107,12 +90,12 @@ release. It is used by the android app for forcing software updates. Do not ed
|
||||
Generated by bin/buildall.sh -->
|
||||
|
||||
<resources>
|
||||
<string name="cur_firmware_version">$VERSION</string>
|
||||
<string name="cur_firmware_version" translatable="false">$VERSION</string>
|
||||
</resources>
|
||||
XML
|
||||
|
||||
echo Generating $ARCHIVEDIR/firmware-$VERSION.zip
|
||||
rm -f $ARCHIVEDIR/firmware-$VERSION.zip
|
||||
zip --junk-paths $ARCHIVEDIR/firmware-$VERSION.zip $ARCHIVEDIR/spiffs-$VERSION.bin $OUTDIR/bins/firmware-*-$VERSION.* images/system-info.bin bin/device-install.sh bin/device-update.sh
|
||||
zip --junk-paths $ARCHIVEDIR/firmware-$VERSION.zip $ARCHIVEDIR/spiffs-$VERSION.bin $OUTDIR/bins/universal/firmware-*-$VERSION.* images/system-info.bin bin/device-install.sh bin/device-update.sh
|
||||
|
||||
echo BUILT ALL
|
||||
|
||||
38
bin/build-nightly.sh
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/bin/bash
|
||||
source ~/.bashrc
|
||||
|
||||
# Meshtastic Nightly Build Script.
|
||||
# McHamster (jm@casler.org)
|
||||
#
|
||||
# This is the script that is used for the nightly build server.
|
||||
#
|
||||
# It's probably not useful for most people, but you may want to run your own
|
||||
# nightly builds.
|
||||
#
|
||||
# The last line of ~/.bashrc contains an inclusion of platformio in the path.
|
||||
# Without this, the build script won't run from the crontab:
|
||||
#
|
||||
# export PATH="$HOME/.platformio/penv/bin:$PATH"
|
||||
#
|
||||
# The crontab contains:
|
||||
# 0 2 * * * cd ~/meshtastic/github/meshtastic && source "~/.bashrc"; ./build-nightly.sh > ~/cronout.txt 2> ~/cronout.txt
|
||||
|
||||
cd Meshtastic-device
|
||||
|
||||
git pull
|
||||
|
||||
bin/build-all.sh
|
||||
|
||||
date_stamp=$(date +'%Y-%m-%d')
|
||||
|
||||
cd ..
|
||||
|
||||
# TODO: Archive the same binaries used by the build-all script.
|
||||
#zip -r meshtastic_device_nightly_${date_stamp} Meshtastic-device/release/latest/bins
|
||||
cp Meshtastic-device/release/archive/`ls -t ./Meshtastic-device/release/archive/| head -1` meshtastic_device_nightly_${date_stamp}.zip
|
||||
|
||||
# Copy the file to the webserver
|
||||
scp meshtastic_device_nightly_${date_stamp}.zip jm@10.11.12.20:/volume1/web/meshtastic/nightly_builds/
|
||||
|
||||
# Delete the local copy
|
||||
rm meshtastic_device_nightly_${date_stamp}.zip
|
||||
@@ -1,21 +1,24 @@
|
||||
#!/bin/sh
|
||||
|
||||
PYTHON=${PYTHON:-python3}
|
||||
|
||||
set -e
|
||||
|
||||
# Usage info
|
||||
show_help() {
|
||||
cat << EOF
|
||||
Usage: ${0##*/} [-h] [-p ESPTOOL_PORT] [-f FILENAME]
|
||||
Usage: ${0##*/} [-h] [-p ESPTOOL_PORT] [-P PYTHON] [-f FILENAME]
|
||||
Flash image file to device, but first erasing and writing system information"
|
||||
|
||||
-h Display this help and exit
|
||||
-p ESPTOOL_PORT Set the environment variable for ESPTOOL_PORT. If not set, ESPTOOL iterates all ports (Dangerrous).
|
||||
-f FILENAME The .bin file to flash. Custom to your device type and region.
|
||||
-P PYTHON Specify alternate python interpreter to use to invoke esptool. (Default: "$PYTHON")
|
||||
-f FILENAME The .bin file to flash. Custom to your device type and region.
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
while getopts ":h:p:f:" opt; do
|
||||
while getopts ":hp:P:f:" opt; do
|
||||
case "${opt}" in
|
||||
h)
|
||||
show_help
|
||||
@@ -23,6 +26,8 @@ while getopts ":h:p:f:" opt; do
|
||||
;;
|
||||
p) export ESPTOOL_PORT=${OPTARG}
|
||||
;;
|
||||
P) PYTHON=${OPTARG}
|
||||
;;
|
||||
f) FILENAME=${OPTARG}
|
||||
;;
|
||||
*)
|
||||
@@ -36,10 +41,10 @@ shift "$((OPTIND-1))"
|
||||
|
||||
if [ -f "${FILENAME}" ]; then
|
||||
echo "Trying to flash ${FILENAME}, but first erasing and writing system information"
|
||||
esptool.py --baud 921600 erase_flash
|
||||
esptool.py --baud 921600 write_flash 0x1000 system-info.bin
|
||||
esptool.py --baud 921600 write_flash 0x00390000 spiffs-*.bin
|
||||
esptool.py --baud 921600 write_flash 0x10000 ${FILENAME}
|
||||
$PYTHON -m esptool --baud 921600 erase_flash
|
||||
$PYTHON -m esptool --baud 921600 write_flash 0x1000 system-info.bin
|
||||
$PYTHON -m esptool --baud 921600 write_flash 0x00390000 spiffs-*.bin
|
||||
$PYTHON -m esptool --baud 921600 write_flash 0x10000 ${FILENAME}
|
||||
else
|
||||
echo "Invalid file: ${FILENAME}"
|
||||
show_help
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
#!/bin/sh
|
||||
|
||||
PYTHON=${PYTHON:-python3}
|
||||
|
||||
# Usage info
|
||||
show_help() {
|
||||
cat << EOF
|
||||
Usage: ${0##*/} [-h] [-p ESPTOOL_PORT] -f FILENAME
|
||||
Usage: ${0##*/} [-h] [-p ESPTOOL_PORT] [-P PYTHON] -f FILENAME
|
||||
Flash image file to device, leave existing system intact."
|
||||
|
||||
-h Display this help and exit
|
||||
-p ESPTOOL_PORT Set the environment variable for ESPTOOL_PORT. If not set, ESPTOOL iterates all ports (Dangerrous).
|
||||
-f FILENAME The .bin file to flash. Custom to your device type and region.
|
||||
-P PYTHON Specify alternate python interpreter to use to invoke esptool. (Default: "$PYTHON")
|
||||
-f FILENAME The .bin file to flash. Custom to your device type and region.
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
while getopts ":h:p:f:" opt; do
|
||||
while getopts ":hp:P:f:" opt; do
|
||||
case "${opt}" in
|
||||
h)
|
||||
show_help
|
||||
@@ -21,6 +24,8 @@ while getopts ":h:p:f:" opt; do
|
||||
;;
|
||||
p) export ESPTOOL_PORT=${OPTARG}
|
||||
;;
|
||||
P) PYTHON=${OPTARG}
|
||||
;;
|
||||
f) FILENAME=${OPTARG}
|
||||
;;
|
||||
*)
|
||||
@@ -34,7 +39,9 @@ shift "$((OPTIND-1))"
|
||||
|
||||
if [ -f "${FILENAME}" ]; then
|
||||
echo "Trying to flash update ${FILENAME}."
|
||||
esptool.py --baud 921600 write_flash 0x10000 ${FILENAME}
|
||||
$PYTHON -m esptool --baud 921600 write_flash 0x10000 ${FILENAME}
|
||||
echo "Erasing the otadata partition, which will turn off flash flippy-flop and force the first image to be used"
|
||||
$PYTHON -m esptool --baud 921600 erase_region 0xe000 0x2000
|
||||
else
|
||||
echo "Invalid file: ${FILENAME}"
|
||||
show_help
|
||||
|
||||
@@ -241,7 +241,7 @@ def print_addr(name, value, resolver):
|
||||
def print_stack_full(lines, resolver):
|
||||
print("stack:")
|
||||
for line in lines:
|
||||
print(line.offset + ":")
|
||||
print(str(line.offset) + ":")
|
||||
for content in line.content:
|
||||
print(" " + resolver.resolve_stack_addr(content))
|
||||
|
||||
|
||||
19
bin/gen-images.sh
Executable file
@@ -0,0 +1,19 @@
|
||||
|
||||
|
||||
set -e
|
||||
|
||||
# regen the design bins first
|
||||
cd design
|
||||
bin/generate-pngs.sh
|
||||
cd ..
|
||||
|
||||
# assumes 50 wide, 28 high
|
||||
convert design/logo/png/Mesh_Logo_Black_Small.png -background white -alpha Background src/graphics/img/icon.xbm
|
||||
|
||||
inkscape --batch-process -o images/compass.png -w 48 -h 48 images/location_searching-24px.svg
|
||||
convert compass.png -background white -alpha Background src/graphics/img/compass.xbm
|
||||
|
||||
inkscape --batch-process -o images/face.png -w 13 -h 13 images/face-24px.svg
|
||||
|
||||
inkscape --batch-process -o images/pin.png -w 13 -h 13 images/room-24px.svg
|
||||
convert pin.png -background white -alpha Background src/graphics/img/pin.xbm
|
||||
@@ -6,6 +6,8 @@ BOOTDIR=/home/kevinh/development/meshtastic/Adafruit_nRF52_Bootloader
|
||||
|
||||
nrfjprog --eraseall -f nrf52
|
||||
|
||||
# to get tool run "sudo apt-get install srecord"
|
||||
|
||||
# this generates an intel hex file that can be programmed into a NRF52 to tell the adafruit bootloader that the current app image is valid
|
||||
# Bootloader settings are at BOOTLOADER_SETTINGS (rw) : ORIGIN = 0xFF000, LENGTH = 0x1000
|
||||
# first 4 bytes should be 0x01 to indicate valid app image
|
||||
@@ -14,7 +16,7 @@ echo "01 00 00 00 00 00 00 00" | xxd -r -p - >/tmp/bootconf.bin
|
||||
srec_cat /tmp/bootconf.bin -binary -offset 0xff000 -output /tmp/bootconf.hex -intel
|
||||
|
||||
echo Generating merged hex file
|
||||
mergehex -m $BOOTDIR/_build/build-ttgo_eink/ttgo_eink_bootloader-0.3.2-125-gf38f8f4-dirty_s140_6.1.1.hex .pio/build/eink/firmware.hex /tmp/bootconf.hex -o ttgo_eink_full.hex
|
||||
mergehex -m $BOOTDIR/_build/build-ttgo_eink/ttgo_eink_bootloader-0.3.2-213-gf67f592-dirty_s140_6.1.1.hex .pio/build/eink/firmware.hex /tmp/bootconf.hex -o ttgo_eink_full.hex
|
||||
|
||||
echo Telling bootloader app region is valid and telling CPU to run
|
||||
nrfjprog --program ttgo_eink_full.hex -f nrf52 --reset
|
||||
|
||||
3
bin/native-gdbserver.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
set -e
|
||||
pio run --environment native
|
||||
gdbserver --once localhost:2345 .pio/build/native/program
|
||||
3
bin/native-run.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
set -e
|
||||
pio run --environment native
|
||||
.pio/build/native/program
|
||||
@@ -1,3 +1,3 @@
|
||||
|
||||
|
||||
JLinkGDBServerCLExe -if SWD -select USB -port 2331 -device NRF52840_XXAA
|
||||
JLinkGDBServerCLExe -if SWD -select USB -port 2331 -device NRF52840_XXAA -SuppressInfoUpdateFW -DisableAutoUpdateFW -rtos GDBServer/RTOSPlugin_FreeRTOS
|
||||
|
||||
@@ -8,9 +8,9 @@ config.read(prefsLoc)
|
||||
version = dict(config.items('VERSION'))
|
||||
verStr = "{}.{}.{}".format(version["major"], version["minor"], version["build"])
|
||||
|
||||
print(f"Using meshtastic platform-custom.py, firmare version {verStr}")
|
||||
print("Using meshtastic platform-custom.py, firmare version " + verStr)
|
||||
|
||||
# General options that are passed to the C and C++ compilers
|
||||
projenv.Append(CCFLAGS=[
|
||||
f"-DAPP_VERSION={verStr}"
|
||||
"-DAPP_VERSION=" + verStr
|
||||
])
|
||||
|
||||
3
bin/program-1.0-tbeam.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
esptool.py --baud 921600 write_flash 0x10000 release/archive/old/firmware-tbeam-EU865-1.0.0.bin
|
||||
echo "Erasing the otadata partition, which will turn off flash flippy-flop and force the first image to be used"
|
||||
esptool.py --baud 921600 erase_region 0xe000 0x2000
|
||||
1
bin/program-1.1-tbeam.sh
Executable file
@@ -0,0 +1 @@
|
||||
esptool.py --baud 921600 write_flash 0x10000 release/archive/old/firmware-tbeam-1.1.50.bin
|
||||
@@ -2,5 +2,7 @@
|
||||
set -e
|
||||
|
||||
VERSION=`bin/buildinfo.py`
|
||||
FILENAME=release/latest/bins/universal/firmware-tbeam-$VERSION.bin
|
||||
|
||||
esptool.py --baud 921600 write_flash 0x10000 release/latest/bins/firmware-tbeam-US-$VERSION.bin
|
||||
echo Installing $FILENAME
|
||||
esptool.py --baud 921600 write_flash 0x10000 $FILENAME
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# You probably don't need this - it is a basic test of the serial flash on the TTGO eink board
|
||||
|
||||
nrfjprog -qspiini nrf52/ttgo_eink_qpsi.ini --qspieraseall
|
||||
nrfjprog --qspiini nrf52/ttgo_eink_qpsi.ini --qspieraseall
|
||||
nrfjprog --qspiini nrf52/ttgo_eink_qpsi.ini --memwr 0x12000000 --val 0xdeadbeef --verify
|
||||
nrfjprog --qspiini nrf52/ttgo_eink_qpsi.ini --readqspi spi.hex
|
||||
objdump -s spi.hex | less
|
||||
|
||||
@@ -8,7 +8,7 @@ echo "prebuilt binaries for your computer into nanopb-0.4.4"
|
||||
|
||||
# the nanopb tool seems to require that the .options file be in the current directory!
|
||||
cd proto
|
||||
../nanopb-0.4.4/generator-bin/protoc --nanopb_out=-v:../src/mesh -I=../proto *.proto
|
||||
../nanopb-0.4.4/generator-bin/protoc --nanopb_out=-v:../src/mesh/generated -I=../proto *.proto
|
||||
|
||||
echo "Regenerating protobuf documentation - if you see an error message"
|
||||
echo "you can ignore it unless doing a new protobuf release to github."
|
||||
|
||||
11
bin/run-both.sh
Executable file
@@ -0,0 +1,11 @@
|
||||
set -e
|
||||
|
||||
pio run
|
||||
|
||||
echo uploading to usb1
|
||||
pio run --upload-port /dev/ttyUSB1 -t upload &
|
||||
|
||||
echo uploading to usb0
|
||||
pio run --upload-port /dev/ttyUSB0 -t upload &
|
||||
|
||||
wait
|
||||
61
boards/eink0.1.json
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "nrf52840_s140_v6.ld"
|
||||
},
|
||||
"core": "nRF5",
|
||||
"cpu": "cortex-m4",
|
||||
"extra_flags": "-DARDUINO_NRF52840_TTGO_EINK -DNRF52840_XXAA",
|
||||
"f_cpu": "64000000L",
|
||||
"hwids": [
|
||||
[
|
||||
"0x239A",
|
||||
"0x4405"
|
||||
]
|
||||
],
|
||||
"usb_product": "TTGO_eink",
|
||||
"mcu": "nrf52840",
|
||||
"variant": "eink0.1",
|
||||
"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"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino"
|
||||
],
|
||||
"name": "TTGO eink (Adafruit BSP)",
|
||||
"upload": {
|
||||
"maximum_ram_size": 248832,
|
||||
"maximum_size": 815104,
|
||||
"require_upload_port": true,
|
||||
"speed": 115200,
|
||||
"protocol": "jlink",
|
||||
"protocols": [
|
||||
"jlink",
|
||||
"nrfjprog",
|
||||
"stlink"
|
||||
]
|
||||
},
|
||||
"url": "FIXME",
|
||||
"vendor": "TTGO"
|
||||
}
|
||||
48
boards/lora_isp4520.json
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "nrf52832_s132_v6.ld"
|
||||
},
|
||||
"core": "nRF5",
|
||||
"cpu": "cortex-m4",
|
||||
"extra_flags": "-DNRF52832_XXAA -DNRF52",
|
||||
"f_cpu": "64000000L",
|
||||
"mcu": "nrf52832",
|
||||
"variant": "lora_isp4520",
|
||||
"bsp": {
|
||||
"name": "adafruit"
|
||||
},
|
||||
"softdevice": {
|
||||
"sd_flags": "-DS132",
|
||||
"sd_name": "s132",
|
||||
"sd_version": "6.1.1",
|
||||
"sd_fwid": "0x00B7"
|
||||
}
|
||||
},
|
||||
"connectivity": [
|
||||
"bluetooth"
|
||||
],
|
||||
"debug": {
|
||||
"jlink_device": "nRF52832_xxAA",
|
||||
"svd_path": "nrf52.svd"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino"
|
||||
],
|
||||
"name": "lora ISP4520",
|
||||
"upload": {
|
||||
"maximum_ram_size": 65536,
|
||||
"maximum_size": 524288,
|
||||
"require_upload_port": true,
|
||||
"speed": 115200,
|
||||
"protocol": "nrfutil",
|
||||
"protocols": [
|
||||
"jlink",
|
||||
"nrfjprog",
|
||||
"nrfutil",
|
||||
"stlink"
|
||||
]
|
||||
},
|
||||
"url": "",
|
||||
"vendor": "PsiSoft"
|
||||
}
|
||||
|
Before Width: | Height: | Size: 532 B After Width: | Height: | Size: 532 B |
|
Before Width: | Height: | Size: 442 B After Width: | Height: | Size: 442 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
1
design
Submodule
@@ -1,13 +1,13 @@
|
||||
# What is Meshtastic?
|
||||
|
||||
Meshtastic™ is a project that lets you use
|
||||
Meshtastic® is a project that lets you use
|
||||
inexpensive (\$30 ish) GPS radios as an extensible, long battery life, secure, mesh GPS communicator. These radios are great for hiking, skiing, paragliding - essentially any hobby where you don't have reliable internet access. Each member of your private mesh can always see the location and distance of all other members and any text messages sent to your group chat.
|
||||
|
||||
The radios automatically create a mesh to forward packets as needed, so everyone in the group can receive messages from even the furthest member. The radios will optionally work with your phone, but no phone is required.
|
||||
|
||||
Note: Questions after reading this? See our new [forum](https://meshtastic.discourse.group/).
|
||||
|
||||
### Uses
|
||||
## Uses
|
||||
|
||||
- Outdoor sports where cellular coverage is limited. (Hiking, Skiing, Boating, Paragliding, Gliders etc..)
|
||||
- Applications where closed source GPS communicators just won't cut it (it is easy to add features for glider pilots etc...)
|
||||
@@ -17,7 +17,7 @@ Note: Questions after reading this? See our new [forum](https://meshtastic.disco
|
||||
|
||||
[](https://www.youtube.com/watch?v=WlNbMbVZlHI "Meshtastic early demo")
|
||||
|
||||
### Features
|
||||
## Features
|
||||
|
||||
Not all of these features are fully implemented yet - see **important** disclaimers below. But they should be in by the time we decide to call this project beta (three months?)
|
||||
|
||||
@@ -41,7 +41,8 @@ For an detailed walk-through aimed at beginners, we recommend [meshtastic.letsta
|
||||
|
||||
### Related Groups
|
||||
|
||||
Telegram group for **Italy**-based users [t.me/meshtastic_italia](http://t.me/meshtastic_italia) (Italian language, unofficial).
|
||||
Telegram group for **Italy**-based users [t.me/meshtastic_italia](http://t.me/meshtastic_italia) (Italian language, unofficial).<br/>
|
||||
Telegram group for **Russian**-based users [t.me/meshtastic_russia](https://t.me/meshtastic_russia) (Russian language, unofficial).
|
||||
|
||||
# Updates
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
We use the same channel maps as LoRaWAN (though this is not LoRaWAN).
|
||||
|
||||

|
||||

|
||||
|
||||
See [this site](https://www.rfwireless-world.com/Tutorials/LoRa-channels-list.html) for more information.
|
||||
|
||||
@@ -28,3 +28,78 @@ The maximum output power for North America is +30 dBm ERP.
|
||||
|
||||
The band is from 902 to 928 MHz. It mentions channel number and its respective channel frequency. All the 13 channels are separated by 2.16 MHz with respect to the adjacent channels.
|
||||
Channel zero starts at 903.08 MHz center frequency.
|
||||
|
||||
## Data-rates
|
||||
|
||||
### About
|
||||
|
||||
Various data-rates are selectable when configuring a channel and are inversely proportional to the theoretical range of the devices.
|
||||
|
||||
Considerations:
|
||||
|
||||
* Spreading Factor - How much we "spread" our data over time.
|
||||
* * Each step up in Spreading Factor dobules the airtime to transmit.
|
||||
* * Each step up in Spreading Factor adds about 2.5db extra link budget.
|
||||
* Bandwidth - How big of a slice of the spectrum we use.
|
||||
* * Each doubling of the bandwidth is almost 3db less link budget.
|
||||
* * Bandwidths less than 31 may be unstable unless you have a high quality Crystal Ossilator.
|
||||
* Coding Rate - How much redundency we encode to resist noise.
|
||||
* * Increasing coding rate increases reliability while decrasing data-rate.
|
||||
* * 4/5 - 1.25x overhead
|
||||
* * 4/6 - 1.5x overhead
|
||||
* * 4/7 - 1.75x overhead
|
||||
* * 4/8 - 2x overhead
|
||||
|
||||
|
||||
### Pre-Defined
|
||||
|
||||
We have four predefined channels. These are the most common settings and have been proven to work well:
|
||||
|
||||
| Channel setting | Alt Channel Name | Data-rate | SF / Symbols | Coding Rate | Bandwidth | Link Budget |
|
||||
|:---------------------------|:-----------------|:---------------------|:-------------|:------------|:----------|:------------|
|
||||
| Short range (but fast) | Short Fast | 21.875 kbps | 7 / 128 | 4/5 | 125 | 134dB |
|
||||
| Medium range (but fast) | Medium | 5.469 kbps | 7 / 128 | 4/5 | 500 | 140dB |
|
||||
| Long range (but slower) | Long Alt | 0.275 kbps | 9 / 512 | 4/8 | 31 | 153dB |
|
||||
| Very long range (but slow) | Long Slow | 0.183 kbps (default) | 12 / 4096 | 4/8 | 125 | 154dB |
|
||||
|
||||
The link budget used by these calculations assumes a transmit power of 17dBm and an antenna with 0dB gain. Adjust your link budget assumptions based on your actual devices.
|
||||
|
||||
### Custom Settings
|
||||
|
||||
You may want to select other channels for your usage. The other settings can be set by using the Python API.
|
||||
|
||||
> meshtastic --setchan spread_factor 10 --setchan coding_rate 8 --setchan bandwidth 125
|
||||
|
||||
After applying the settings, you will need to restart the device. After your device is restarted, it will generate a new crypto key and you will need to share the newly generated QR Code or URL to all your other devices.
|
||||
|
||||
Some example settings:
|
||||
|
||||
| Data-rate | SF / Symbols | Coding Rate | Bandwidth | Link Budget | Note |
|
||||
|:---------------------|:-------------|:------------|:----------|:------------|:-----|
|
||||
| 37.50 kbps | 6 / 64 | 4/5 | 500 | 129dB | Fastest possible speed |
|
||||
| 3.125 kbps | 8 / 256 | 4/5 | 125 | 143dB | |
|
||||
| 1.953 kbps | 8 / 256 | 4/8 | 125 | 143dB | |
|
||||
| 1.343 kbps | 11 / 2048 | 4/8 | 500 | 145dB | |
|
||||
| 1.099 kbps | 9 / 512 | 4/8 | 125 | 146dB | |
|
||||
| 0.814 kbps | 10 / 1024 | 4/6 | 125 | 149dB | |
|
||||
| 0.610 kbps | 10 / 1024 | 4/8 | 125 | 149dB | |
|
||||
| 0.488 kbps | 11 / 2048 | 4/6 | 125 | 152dB | |
|
||||
| 0.336 kbps | 11 / 2048 | 4/8 | 125 | 152dB | |
|
||||
| 0.073 kbps | 12 / 4096 | 4/5 | 31 | 160dB | Twice the range and/or coverage of "Long Slow", low resliance to noise |
|
||||
| 0.046 kbps | 12 / 4096 | 4/8 | 31 | 160dB | Twice the range and/or coverage of "Long Slow", high resliance to noise |
|
||||
|
||||
The link budget used by these calculations assumes a transmit power of 17dBm and an antenna with 0dB gain. Adjust your link budget assumptions based on your actual devices.
|
||||
|
||||
These channel settings may have not been tested. Use at your own discression. Share on https://meshtastic.discourse.group with your successes or failure.
|
||||
|
||||
## Cryptography
|
||||
|
||||
The preshared key used by the devices can be modified.
|
||||
|
||||
* 0 = No crypto
|
||||
* 1 = Default channel key
|
||||
* 2 - 10 = The default channel key, except with 1 through 9 added to the last byte
|
||||
|
||||
Use of cryptography can also be modified. To disable cryptography (maybe useful if you have HAM radio license):
|
||||
|
||||
> meshtastic --setchan psk 0
|
||||
|
||||
@@ -2,8 +2,145 @@
|
||||
|
||||
You probably don't care about this section - skip to the next one.
|
||||
|
||||
## before next release
|
||||
|
||||
* DONE naks are being dropped (though enqueuedLocal) sometimes before phone/PC gets them
|
||||
* DONE have android fill in if local GPS has poor signal
|
||||
* fix heltec battery scaling
|
||||
* add reference counting to mesh packets
|
||||
* allow multiple simultanteous phoneapi connections
|
||||
* DONE split position.time and last_heard
|
||||
* DONE update android app to use last_heard
|
||||
* DONE turn off bluetooth interface ENTIRELY while using serial API (was python client times out on connect sometimes)
|
||||
* DONE gps assistance from phone not working?
|
||||
* DONE test latest firmware update with is_router
|
||||
* DONE firmware OTA updates of is_router true nodes fails?
|
||||
* DONE add UI in android app to reset to defaults https://github.com/meshtastic/Meshtastic-Android/issues/263
|
||||
* DONE TEST THIS! changing channels requires a reboot to take effect https://github.com/meshtastic/Meshtastic-device/issues/752
|
||||
* DONE bug report with remote info request timing out
|
||||
* DONE retest channel changing in android (using sim?)
|
||||
* DONE move remote admin doc from forum into git
|
||||
* DONE check crashlytics
|
||||
* DONE ask for a documentation czar
|
||||
* DONE timestamps on oled screen are wrong - don't seem to be updating based on message rx (actually: this is expected behavior when no node on the mesh has GPS time)
|
||||
* DONE add ch-del
|
||||
* DONE channel hash suffixes are wrong on android
|
||||
* DONE before next relase: test empty channel sets on android
|
||||
* DONE channel sharing in android
|
||||
* DONE test 1.0 firmware update on android
|
||||
* DONE test 1.1 firmware update on android
|
||||
* DONE test 1.2.10 firmware update on android
|
||||
* DONE test link sharing on android
|
||||
* FIXED? luxon bug report - seeing rx acks for nodes that are not on the network
|
||||
* DONE release py
|
||||
* DONE show GPS time only if we know what global time is
|
||||
* DONE android should always provide time to nodes - so that it is easier for the mesh to learn the current time
|
||||
|
||||
## MQTT
|
||||
|
||||
## Multichannel support
|
||||
|
||||
* DONE cleanup the external notification and serial plugins
|
||||
* non ack version of stress test fails sometimes!
|
||||
* tx fault test has a bug #734 - * turn off fault 8: https://github.com/meshtastic/Meshtastic-device/issues/734
|
||||
* DONE move device types into an enum in nodeinfo
|
||||
* DONE fix android to use new device types for firmware update
|
||||
* nrf52 should preserve local time across reset
|
||||
* cdcacm bug on nrf52: emittx thinks it emitted but client sees nothing. works again later
|
||||
* nrf52: segger logs have errors in formatting that should be impossible (because not going through serial, try stalling on segger)
|
||||
* DONE call RouterPlugin for *all* packets - not just Router packets
|
||||
* DONE generate channel hash from the name of the channel+the psk (not just one or the other)
|
||||
* DONE send a hint that can be used to select which channel to try and hash against with each message
|
||||
* DONE remove deprecated
|
||||
* DONE fix setchannel in phoneapi.cpp
|
||||
* DONE set mynodeinfo.max_channels
|
||||
* DONE set mynodeinfo.num_bands (formerly num_channels)
|
||||
* DONE fix sniffing of non Routing packets
|
||||
* DONE enable remote setttings access by moving settings operations into a regular plugin (move settings ops out of PhoneAPI)
|
||||
* DONE move portnum up?
|
||||
* DONE remove region specific builds from the firmware
|
||||
* DONE test single channel without python
|
||||
* DONE Use "default" for name if name is empty
|
||||
* DONE fix python data packet receiving (nothing showing in log?)
|
||||
* DONE implement 'get channels' Admin plugin operation
|
||||
* DONE use get-channels from python
|
||||
* DONE use get channels & get settings from android
|
||||
* DONE use set-channel from python
|
||||
* DONE make settings changes from python work
|
||||
* DONE pthon should stop fetching channels once we've reached our first empty channel definition (hasSettings == true)
|
||||
* DONE add check for old devices with new API library
|
||||
* DONE release python api
|
||||
* DONE release protobufs
|
||||
* DONE release to developers
|
||||
* DONE fix setch-fast in python tool
|
||||
* age out pendingrequests in the python API
|
||||
* DONE stress test channel download from python, sometimes it seems like we don't get all replies, bug was due to simultaneous android connection
|
||||
* DONE combine acks and responses in a single message if possible (do routing plugin LAST and drop ACK if someone else has already replied)
|
||||
* DONE don't send packets we received from the phone BACK TOWARDS THE PHONE (possibly use fromnode 0 for packets the phone sends?)
|
||||
* DONE fix 1.1.50 android debug panel display
|
||||
* DONE test android channel setting
|
||||
* DONE release to users
|
||||
* DONE warn in android app about unset regions
|
||||
* DONE use set-channel from android
|
||||
* DONE add gui in android app for setting region
|
||||
* DONE clean up python channel usage
|
||||
* DONE use bindToChannel to limit admin access for remote nodes
|
||||
* DONE move channels and radio config out of device settings
|
||||
* DONE test remote info and remote settings changes
|
||||
* make python tests more exhaustive
|
||||
* DONE pick default random admin key
|
||||
* exclude admin channels from URL?
|
||||
* make a way to share just secondary channels via URL
|
||||
* generalize the concept of "shortstrings" use it for both PSKs and well known channel names. Possibly use a ShortString class.
|
||||
* use single byte 'well known' channel names for admin, gpio, etc...
|
||||
* use presence of gpio channel to enable gpio ops, same for serial etc...
|
||||
* DONE restrict gpio & serial & settings operations to the admin channel (unless local to the current node)
|
||||
* DONE add channel restrictions for plugins (and restrict routing plugin to the "control" channel)
|
||||
* stress test multi channel
|
||||
* DONE investigate @mc-hamster report of heap corruption
|
||||
* DONE use set-user from android
|
||||
* untrusted users should not be allowed to provide bogus times (via position broadcasts) to the rest of the mesh. Invent a new lowest quality notion of UntrustedTime.
|
||||
* use portuino TCP connection to debug with python API
|
||||
* document the relationship between want_response (indicating remote node received it) and want_ack (indicating that this message should be sent reliably - and also get acks from the first rx node and naks if it is never delivered)
|
||||
* DONE android should stop fetching channels once we've reached our first empty channel definition (hasSettings == true)
|
||||
* DONE warn in python api if we are too new to talk to the device code
|
||||
* DONE make a post warning about 1.2, telling how to stay on old android & python clients. link to this from the android dialog message and python version warning.
|
||||
* DONE "FIXME - move the radioconfig/user/channel READ operations into SettingsMessage as well"
|
||||
* DONE scrub protobufs to make sure they are absoloute minimum wiresize (in particular Data, ChannelSets and positions)
|
||||
* DONE change syncword (now ox2b)
|
||||
* allow chaning packets in single transmission - to increase airtime efficiency and amortize packet overhead
|
||||
* DONE move most parts of meshpacket into the Data packet, so that we can chain multiple Data for sending when they all have a common destination and key.
|
||||
* when selecting a MeshPacket for transmit, scan the TX queue for any Data packets we can merge together as a WirePayload. In the low level send/rx code expand that into multiple MeshPackets as needed (thus 'hiding' from MeshPacket that over the wire we send multiple datapackets
|
||||
* DONE confirm we are still calling the plugins for messages inbound from the phone (or generated locally)
|
||||
* DONE confirm we are still multi hop routing flood broadcasts
|
||||
* DONE confirm we are still doing resends on unicast reliable packets
|
||||
* add history to routed packets: https://meshtastic.discourse.group/t/packet-source-tracking/2764/2
|
||||
* add support for full DSR unicast delivery
|
||||
* DONE move acks into routing
|
||||
* DONE make all subpackets different versions of data
|
||||
* DONE move routing control into a data packet
|
||||
* have phoneapi done via plugin (will allow multiple simultaneous API clients - stop disabling BLE while using phone API)
|
||||
* use reference counting and dynamic sizing for meshpackets.
|
||||
* let multiple PhoneAPI endpoints work at once
|
||||
* allow multiple simultaneous bluetooth connections (create the bluetooth phoneapi instance dynamically based on client id)
|
||||
* DONE figure out how to add micro_delta to position, make it so that phone apps don't need to understand it?
|
||||
* only send battery updates a max of once a minute
|
||||
* DONE add python channel selection for sending
|
||||
* DONE record recevied channel in meshpacket
|
||||
* test remote settings operations (confirm it works 3 hops away)
|
||||
* DONE make a primaryChannel global and properly maintain it when the phone sends setChannel
|
||||
* DONE move setCrypto call into packet send and packet decode code
|
||||
* implement 'small location diffs' change
|
||||
* move battery level out of position?
|
||||
* consider "A special exception (FIXME, not sure if this is a good idea) - packets that arrive on the local interface
|
||||
are allowed on any channel (this lets the local user do anything)." Probably by adding a "secure_local_interface" settings bool.
|
||||
* DOUBLE CHECK android app can still upgrade 1.1 and 1.0 loads
|
||||
|
||||
For app cleanup:
|
||||
|
||||
* use structured logging to kep logs in ram. Also send logs as packets to api clients
|
||||
* DONE writeup nice python options docs (common cases, link to protobuf docs)
|
||||
* have android app link to user manual
|
||||
* DONE only do wantReplies once per packet type, if we change network settings force it again
|
||||
* update positions and nodeinfos based on packets we just merely witness on the mesh. via isPromsciousPort bool, remove sniffing
|
||||
* DONE make device build always have a valid version
|
||||
@@ -32,11 +169,10 @@ For app cleanup:
|
||||
* DONE move user info into regular data packets (use new app framework)
|
||||
* DONE test that positions, text messages and user info still work
|
||||
* DONE test that position, text messages and user info work properly with new android app and old device code
|
||||
* do UDP tunnel
|
||||
* fix the RTC drift bug
|
||||
* DONE do UDP tunnel
|
||||
* DONE fix the RTC drift bug
|
||||
* move python ping functionality into device, reply with rxsnr info
|
||||
* use channels for gpio security https://github.com/meshtastic/Meshtastic-device/issues/104
|
||||
* generate autodocs
|
||||
* MeshPackets for sending should be reference counted so that API clients would have the option of checking sent status (would allow removing the nasty 30 sec timer in gpio watch sending)
|
||||
|
||||
For high speed/lots of devices/short range tasks:
|
||||
@@ -51,6 +187,44 @@ This should nicely help 'router' nodes do the right thing when long range, or if
|
||||
* turn on amazon reviews support
|
||||
* add a tablet layout (with map next to messages) in the android app
|
||||
|
||||
# Completed
|
||||
|
||||
## eink 1.0
|
||||
|
||||
* DONE check email of reported issues
|
||||
* DONE turn off vbus driving (in bootloader)
|
||||
* new battery level sensing
|
||||
* current draw no good
|
||||
* DONE: fix backlight
|
||||
* DONE - USB is busted because of power enable mode?
|
||||
* test CPU voltage? something is bad with RAM (removing eink module does not help)
|
||||
* test that board leaves bootloader always
|
||||
* test USB - works in bootloader
|
||||
* test LEDs
|
||||
* Test BME280
|
||||
* test gps
|
||||
* check GPS fast locking
|
||||
* tested! dlora
|
||||
* test eink backlight
|
||||
* tested! eink
|
||||
* test buttons
|
||||
* test battery charging
|
||||
* test serial flash
|
||||
* send updated app and bootloader image
|
||||
* OHH BME280! THAT IS GREAT!
|
||||
* make new screen work, ask for datasheet
|
||||
* say I think you could ship this
|
||||
* leds seem busted
|
||||
* fix hw_model: "nrf52unknown"
|
||||
* use larger icon for meshtastic logo
|
||||
* send email about variants & faster flash programming - https://github.com/geeksville/Meshtastic-esp32/commit/f110225173a77326aac029321cdb6491bfa640f6
|
||||
* send PR for bootloader
|
||||
* fix nrf52 time/date
|
||||
* send new master bin file
|
||||
* send email about low power mode problems
|
||||
* support new flash chip in appload, possibly use low power mode
|
||||
* swbug! stuck busy tx occurred!
|
||||
|
||||
# Old docs to merge
|
||||
|
||||
MESH RADIO PROTOCOL
|
||||
|
||||
7
docs/software/android-too-old.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Your android application needs updating
|
||||
|
||||
Hi.
|
||||
|
||||
If you've landed here that means your android application is too old for the running device firmware. Usually our updates are backwards compatible, but about once a year we have a "major protocol update" which requires all apps and devices to update to keep working with that version. Version 1.2 in March 2021 was one of those updates.
|
||||
|
||||
If you have problems/questions please post in our [forum](https://meshtastic.discourse.group) and some nice person will probably help.
|
||||
@@ -39,9 +39,20 @@ cd Meshtastic-device
|
||||
|
||||
## Decoding stack traces
|
||||
|
||||
### Option 1
|
||||
|
||||
If you get a crash, you can decode the addresses from the `Backtrace:` line:
|
||||
|
||||
1. Save the `Backtrace: 0x....` line to a file, e.g., `backtrace.txt`.
|
||||
2. Run `bin/exception_decoder.py backtrace.txt` (this uses symbols from the
|
||||
last `firmware.elf`, so you must be running the same binary that's still in
|
||||
your `.pio/build` directory).
|
||||
|
||||
### Option 2
|
||||
|
||||
You can run the exception decoder to monitor the serial output and decode backtraces in real time.
|
||||
|
||||
1. From within PlatformIO, open a new terminal.
|
||||
2. At the the terminal, enter:
|
||||
`pio device monitor --port /dev/cu.SLAB_USBtoUART -f esp32_exception_decoder`
|
||||
Replace the value of port with the location of your serial port.
|
||||
|
||||
17
docs/software/channels.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Multiple channel support
|
||||
|
||||
Version 1.2 of the software adds support for "multiple (simultaneous) channels". The idea behind this feature is that a mesh can allow multiple users/groups to be share common mesh infrastructure. Even including routing messages for others when no one except that subgroup of users has the encryption keys for their private channel.
|
||||
|
||||
### What is the PRIMARY channel
|
||||
|
||||
The way this works is that each node keeps a list of channels it knows about. One of those channels (normally the first 1) is labelled as the "PRIMARY" channel. The primary channel is the **only** channel that is used to set radio parameters. i.e. this channel controls things like spread factor, coding rate, bandwidth etc... Indirectly this channel also is used to select the specific frequency that all members of this mesh are talking over.
|
||||
|
||||
This channel may or may not have a PSK (encryption). If you are providing mesh to 'the public' we recommend that you always leave this channel with its default psk. The default PSK is technically encrypted (and random users sniffing the ether would have to use meshtastic to decode it), but the key is included in the github source code and you should assume any 'attacker' would have it. But for a 'public' mesh you want this, because it allows anyone using meshtastic in your area to send packets through 'your' mesh.
|
||||
|
||||
Note: Older meshtastic applications that don't yet understand multi-channel support will only show the user this channel.
|
||||
|
||||
### How to use SECONDARY channels
|
||||
|
||||
Any channel you add after that PRIMARY channel is SECONDARY. Secondary channels are used only for encyryption and (in the case of some special applications) security. If you would like to have a private channel over a more public mesh, you probably want to create a SECONDARY channel. When sharing that URL with your private group you will share the "Complete URL". The complete URL includes your secondary channel (for encryption) and the primary channel (to provide radio/mesh access).
|
||||
|
||||
Secondary channels **must** have a PSK (encryption).
|
||||
@@ -13,7 +13,7 @@ the project developers are not cryptography experts. Therefore we ask two things
|
||||
Based on comments from reviewers (see below), here's some tips for usage of these radios. So you can know the level of protection offered:
|
||||
|
||||
* It is pretty likely that the AES256 security is implemented 'correctly' and an observer will not be able to decode your messages.
|
||||
* Warning: If an attacker is able to get one of the radios in their position, they could either a) extract the channel key from that device or b) use that radio to listen to new communications.
|
||||
* Warning: If an attacker is able to get one of the radios in their posession, they could either a) extract the channel key from that device or b) use that radio to listen to new communications.
|
||||
* Warning: If an attacker is able to get the "Channel QR code/URL" that you share with others - that attacker could then be able to read any messages sent on the channel (either tomorrow or in the past - if they kept a raw copy of those broadcast packets)
|
||||
|
||||
Possible future areas of work (if there is enough interest - post in our [forum](https://meshtastic.discourse.group) if you want this):
|
||||
@@ -48,4 +48,4 @@ I'm assuming that meshtastic is being used to hike in places where someone capab
|
||||
* I think the bigger encryption question is "what does the encryption need to do"? As it stands, an attacker who has yet to capture any of the devices cannot reasonably capture text or location data. An attacker who captures any device in the channel/mesh can read everything going to that device, everything stored on that device, and any other communication within the channel that they captured in encrypted form. If that capability basically matches your expectations, it is suitable for whatever adventures this was intended for, then, based on information publicly available or widely disclosed, the encryption is good. If those properties are distressing (like, device history is deliberately limited and you don't want a device captured today to endanger the information sent over the channel yesterday) we could talk about ways to achieve that (most likely synchronizing time and replacing the key with its own SHA256 every X hours, and ensuring the old key is not retained unnecessarily).
|
||||
* Two other things to keep in mind are that AES-CTR does not itself provide authenticity (e.g. an attacker can flip bits in replaying data and scramble the resulting plaintext), and that the current scheme gives some hints about transmission in the size. So, if you worry about an adversary deliberately messing-up messages or knowing the length of a text message, it looks like those might be possible.
|
||||
|
||||
I'm guessing that the network behaves somewhat like a store-and-forward network - or, at least, that the goal is to avoid establishing a two-way connection to transmit data. I'm afraid I haven't worked with mesh networks much, but remember studying them briefly in school about ten years ago.
|
||||
I'm guessing that the network behaves somewhat like a store-and-forward network - or, at least, that the goal is to avoid establishing a two-way connection to transmit data. I'm afraid I haven't worked with mesh networks much, but remember studying them briefly in school about ten years ago.
|
||||
|
||||
@@ -10,7 +10,7 @@ you'll automatically get our fixed libraries.
|
||||
IDF release/v3.3 46b12a560
|
||||
IDF release/v3.3 367c3c09c
|
||||
https://docs.espressif.com/projects/esp-idf/en/release-v3.3/get-started/linux-setup.html
|
||||
kevinh@kevin-server:~/development/meshtastic/esp32-arduino-lib-builder\$ python /home/kevinh/development/meshtastic/esp32-arduino-lib-builder/esp-idf/components/esptool*py/esptool/esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dout --flash_freq 40m --flash_size detect 0x1000 /home/kevinh/development/meshtastic/esp32-arduino-lib-builder/build/bootloader/bootloader.bin
|
||||
kevinh@kevin-server:~/development/meshtastic/esp32-arduino-lib-builder\$ python /home/kevinh/development/meshtastic/
|
||||
cp -a out/tools/sdk/* components/arduino/tools/sdk
|
||||
cp -ar components/arduino/* ~/.platformio/packages/framework-arduinoespressif32
|
||||
|
||||
@@ -21,3 +21,9 @@ you'll automatically get our fixed libraries.
|
||||
cp -ar out/tools/sdk/* ~/.platformio/packages/framework-arduinoespressif32/tools/sdk
|
||||
|
||||
```
|
||||
|
||||
How to flash new bootloader
|
||||
|
||||
```
|
||||
esp32-arduino-lib-builder/esp-idf/components/esptool*py/esptool/esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dout --flash_freq 40m --flash_size detect 0x1000 /home/kevinh/development/meshtastic/esp32-arduino-lib-builder/build/bootloader/bootloader.bin
|
||||
```
|
||||
|
||||
192
docs/software/mqtt.md
Normal file
@@ -0,0 +1,192 @@
|
||||
|
||||
# Table of Contents
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Abstract](#abstract)
|
||||
- [Short term goals](#short-term-goals)
|
||||
- [Long term goals](#long-term-goals)
|
||||
- [Multiple Channel support / Security](#multiple-channel-support--security)
|
||||
- [On device API](#on-device-api)
|
||||
- [MQTT transport](#mqtt-transport)
|
||||
- [Topics](#topics)
|
||||
- [Service Envelope](#service-envelope)
|
||||
- [NODEID](#nodeid)
|
||||
- [USERID](#userid)
|
||||
- [CHANNELID](#channelid)
|
||||
- [Gateway nodes](#gateway-nodes)
|
||||
- [Optional web services](#optional-web-services)
|
||||
- [Public MQTT broker service](#public-mqtt-broker-service)
|
||||
- [Riot.im messaging bridge](#riotim-messaging-bridge)
|
||||
- [Deprecated concepts](#deprecated-concepts)
|
||||
- [MESHID (deprecated)](#meshid-deprecated)
|
||||
- [DESTCLASS (deprecated)](#destclass-deprecated)
|
||||
- [DESTID (deprecated)](#destid-deprecated)
|
||||
- [Rejected idea: RAW UDP](#rejected-idea-raw-udp)
|
||||
- [Development plan](#development-plan)
|
||||
- [Work items](#work-items)
|
||||
- [Enhancements in following releases](#enhancements-in-following-releases)
|
||||
|
||||
## Abstract
|
||||
|
||||
This is a mini-doc/RFC sketching out a development plan to satisfy a number of 1.1 goals.
|
||||
|
||||
- [MQTT](https://opensource.com/article/18/6/mqtt) internet accessible API. Issue #[369](https://github.com/meshtastic/Meshtastic-device/issues/169)
|
||||
- An open API to easily run custom mini-apps on the devices
|
||||
- A text messaging bridge when a node in the mesh can gateway to the internet. Issue #[353](https://github.com/meshtastic/Meshtastic-device/issues/353) and this nicely documented [android issue](https://github.com/meshtastic/Meshtastic-Android/issues/2).
|
||||
- An easy way to let desktop app developers remotely control GPIOs. Issue #[182](https://github.com/meshtastic/Meshtastic-device/issues/182)
|
||||
- Remote attribute access (to change settings of distant nodes). Issue #182
|
||||
|
||||
## Short term goals
|
||||
|
||||
- We want a clean API for novice developers to write mini "apps" that run **on the device** with the existing messaging/location "apps".
|
||||
- We want the ability to have a gateway web service, so that if any node in the mesh can connect to the internet (via its connected phone app or directly) then that node will provide bidirectional messaging between nodes and the internet.
|
||||
- We want an easy way for novice developers to remotely read and control GPIOs (because this is an often requested use case), without those developers having to write any device code.
|
||||
- We want a way to gateway text messaging between our current private meshes and the broader internet (when that mesh is able to connect to the internet)
|
||||
- We want a way to remotely set any device/channel parameter on a node. This is particularly important for administering physically inaccessible router nodes. Ideally this mechanism would also be used for administering the local node (so one common mechanism for both cases).
|
||||
- This work should be independent of our current (semi-custom) LoRa transport, so that in the future we can swap out that transport if we wish (to QMesh or Reticulum?)
|
||||
- Our networks are (usually) very slow and low bandwidth, so the messaging must be very airtime efficient.
|
||||
|
||||
## Long term goals
|
||||
|
||||
- Store and forward messaging should be supported, so apps can send messages that might be delivered to their destination in **hours** or **days** if a node/mesh was partitioned.
|
||||
|
||||
## Multiple Channel support / Security
|
||||
|
||||
Mini-apps API can bind to particular channels. They will only see messages sent on that channel.
|
||||
|
||||
During the 1.0 timeframe only one channel was supported per node. Starting in the 1.1 tree we will do things like "remote admin operations / channel settings etc..." are on the "Control" channel and only especially trusted users should have the keys to access that channel.
|
||||
|
||||
FIXME - explain this more, talk about how useful for users and security domains.
|
||||
- add channels as security
|
||||
|
||||
## On device API
|
||||
|
||||
For information on the related on-device API see [here](device-api.md).
|
||||
|
||||
## MQTT transport
|
||||
|
||||
Any gateway-device will contact the MQTT broker.
|
||||
|
||||
### Topics
|
||||
|
||||
The "mesh/crypt/CHANNELID/NODEID/PORTID" [topic](https://www.hivemq.com/blog/mqtt-essentials-part-5-mqtt-topics-best-practices/) will be used for messages sent from/to a mesh.
|
||||
|
||||
Gateway nodes will foward any MeshPacket from a local mesh channel with uplink_enabled. The packet (encapsulated in a ServiceEnvelope) will remain encrypted with the key for the specified channel.
|
||||
|
||||
For any channels in the local node with downlink_enabled, the gateway node will forward packets from MQTT to the local mesh. It will do this by subscribing to mesh/crypt/CHANNELID/# and forwarding relevant packets.
|
||||
|
||||
If the channelid 'well known'/public it could be decrypted by a web service (if the web service was provided with the associated channel key), in which case it will be decrypted by a web service and appear at "mesh/clear/CHANNELID/NODEID/PORTID". Note: This is not in the initial deliverable.
|
||||
|
||||
FIXME, discuss how text message global mirroring could scale (or not)
|
||||
FIXME, possibly don't global mirror text messages - instead rely on matrix/riot?
|
||||
FIXME, discuss possible attacks by griefers and how they can be prvented
|
||||
|
||||
#### Service Envelope
|
||||
|
||||
The payload published on mesh/... will always be wrapped in a [ServiceEnvelope protobuf](https://github.com/meshtastic/Meshtastic-protobufs/blob/master/docs/docs.md#.ServiceEnvelope).
|
||||
|
||||
ServiceEnvelope will include the message, and full information about arrival time, who forwarded it, source channel, source mesh id, etc...
|
||||
|
||||
#### NODEID
|
||||
|
||||
The unique ID for a node. A hex string that starts with a ! symbol.
|
||||
|
||||
#### USERID
|
||||
|
||||
A user ID string. This string is either a user ID if known or a nodeid to simply deliver the message to whoever the local user is of a particular device (i.e. person who might see the screen). FIXME, see what riot.im uses and perhaps use that convention? Or use the signal +phone number convention? Or the email addr?
|
||||
|
||||
#### CHANNELID
|
||||
|
||||
FIXME, figure out how channelids work
|
||||
|
||||
### Gateway nodes
|
||||
|
||||
Any meshtastic node that has a direct connection to the internet (either via a helper app or installed wifi/4G/satellite hardware) can function as a "Gateway node".
|
||||
|
||||
Gateway nodes (via code running in the phone) will contain two tables to whitelist particular traffic to either be delivered toward the internet, or down toward the mesh. Users that are developing custom apps will be able to customize these filters/subscriptions.
|
||||
|
||||
Since multiple gateway nodes might be connected to a single mesh, it is possible that duplicate messages will be published on any particular topic. Therefore subscribers to these topics should
|
||||
deduplicate if needed by using the packet ID of each message.
|
||||
|
||||
### Optional web services
|
||||
|
||||
#### Public MQTT broker service
|
||||
|
||||
An existing public [MQTT broker](https://mosquitto.org/) will be the default for this service, but clients can use any MQTT broker they choose.
|
||||
|
||||
FIXME - figure out how to avoid impersonation (because we are initially using a public mqtt server with no special security options). FIXME, include some ideas on this in the ServiceEnvelope documentation.
|
||||
|
||||
#### Riot.im messaging bridge
|
||||
|
||||
@Geeksville will run a riot.im bridge that talks to the public MQTT broker and sends/receives into the riot.im network.
|
||||
|
||||
There is apparently [already](https://github.com/derEisele/tuple) a riot.im [bridge](https://matrix.org/bridges/) for MQTT. That will possibly need to be customized a bit. But by doing this, we should be able to let random riot.im users send/receive messages to/from any meshtastic device. (FIXME ponder security). See this [issue](https://github.com/meshtastic/Meshtastic-Android/issues/2#issuecomment-645660990) with discussion with the dev.
|
||||
|
||||
### Deprecated concepts
|
||||
|
||||
You can ignore these for now...
|
||||
|
||||
#### MESHID (deprecated)
|
||||
|
||||
Earlier drafts of this document included the concept of a MESHID. That concept has been removed for now, but might be useful in the future. The old idea is listed below:
|
||||
|
||||
A unique ID for this mesh. There will be some sort of key exchange process so that the mesh ID can not be impersonated by other meshes.
|
||||
|
||||
#### DESTCLASS (deprecated)
|
||||
|
||||
Earlier drafts of this document included the concept of a DESTCLASS. That concept has been removed for now, but might be useful in the future. The old idea is listed below:
|
||||
|
||||
The type of DESTID this message should be delivered to. A short one letter sequence:
|
||||
|
||||
| Symbol | Description |
|
||||
| ------ | ------------------------------------------------------------- |
|
||||
| R | riot.im |
|
||||
| L | local mesh node ID or ^all |
|
||||
| A | an application specific message, ID will be an APP ID |
|
||||
| S | SMS gateway, DESTID is a phone number to reach via Twilio.com |
|
||||
| E | Emergency message, see bug #fixme for more context |
|
||||
|
||||
#### DESTID (deprecated)
|
||||
|
||||
Earlier drafts of this document included the concept of a DESTCLASS. That concept has been removed for now, but might be useful in the future. The old idea is listed below:
|
||||
|
||||
Can be...
|
||||
|
||||
- an internet username: kevinh@geeksville.com
|
||||
- ^ALL for anyone
|
||||
- An app ID (to allow apps out in the web to receive arbitrary binary data from nodes or simply other apps using meshtastic as a transport). They would connect to the MQTT broker and subscribe to their topic
|
||||
|
||||
## Rejected idea: RAW UDP
|
||||
|
||||
A number of commenters have requested/proposed using UDP for the transport. We've considered this option and decided to use MQTT instead for the following reasons:
|
||||
|
||||
- Most UDP uses cases would need to have a server anyways so that nodes can reach each other from anywhere (i.e. if most gateways will be behind some form of NAT which would need to be tunnelled)
|
||||
- Raw UDP is dropped **very** agressively by many cellular providers. MQTT from the gateway to a broker can be done over a TCP connection for this reason.
|
||||
- MQTT provides a nice/documented/standard security model to build upon
|
||||
- MQTT is fairly wire efficient with multiple broker implementations/providers and numerous client libraries for any language. The actual implementation of MQTT is quite simple.
|
||||
|
||||
## Development plan
|
||||
|
||||
Given the previous problem/goals statement, here's the initial thoughts on the work items required. As this idea becomes a bit more fully baked we should add details
|
||||
on how this will be implemented and guesses at approximate work items.
|
||||
|
||||
### Work items
|
||||
|
||||
- Change nodeIDs to be base64 instead of eight hex digits.
|
||||
- DONE Refactor the position features into a position "mini-app". Use only the new public on-device API to implement this app.
|
||||
- DONE Refactor the on device texting features into a messaging "mini-app". (Similar to the position mini-app)
|
||||
- Add new multi channel concept
|
||||
- Send new channels to python client
|
||||
- Let python client add channels
|
||||
- Add portion of channelid to the raw lora packet header
|
||||
- Confirm that we can now forward encrypted packets without decrypting at each node
|
||||
- Use a channel named "remotehw" to secure the GPIO service. If that channel is not found, don't even start the service. Document this as the standard method for securing services.
|
||||
- Add first cut of the "gateway node" code (i.e. MQTT broker client) to the python API (very little code needed for this component)
|
||||
- Confirm that texting works to/from the internet
|
||||
- Confirm that positions are optionally sent to the internet
|
||||
- Add the first cut of the "gateway node" code to the android app (very little code needed for this component)
|
||||
|
||||
### Enhancements in following releases
|
||||
|
||||
The initial gateway will be added to the python tool. But the gateway implementation is designed to be fairly trivial/dumb. After the initial release the actual gateway code can be ported to also run inside of the android app. In fact, we could have ESP32 based nodes include a built-in "gateway node" implementation.
|
||||
|
||||
Store and forward could be added so that nodes on the mesh could deliver messages (i.e. text messages) on an "as possible" basis. This would allow things like "hiker sends a message to friend - mesh can not currently reach friend - eventually (days later) mesh can somehow reach friend, message gets delivered"
|
||||
@@ -15,10 +15,10 @@ Packets can be sent/received either as raw binary structures or as [Protobufs](h
|
||||
The relevant bits of the class heirarchy are as follows
|
||||
|
||||
* [MeshPlugin](/src/mesh/MeshPlugin.h) (in src/mesh/MeshPlugin.h) - you probably don't want to use this baseclass directly
|
||||
* [SinglePortPlugin](/src/mesh/SinglePortPlugin.h) (in src/mesh/SinglePortPlugin.h) - for plugins that receive from a single port number (the normal case)
|
||||
* [ProtobufPlugin](/src/mesh/ProtobufPlugin.h) (in src/mesh/ProtobufPlugin.h) - for plugins that are sending/receiving a single particular Protobuf type. Inherit from this if you are using protocol buffers in your plugin.
|
||||
* [SinglePortPlugin](/src/mesh/SinglePortPlugin.h) (in src/mesh/SinglePortPlugin.h) - for plugins that send/receive from a single port number (the normal case)
|
||||
* [ProtobufPlugin](/src/mesh/ProtobufPlugin.h) (in src/mesh/ProtobufPlugin.h) - for plugins that send/receive a single particular Protobuf type. Inherit from this if you are using protocol buffers in your plugin.
|
||||
|
||||
You will typically want to inherit from either SinglePortPlugin (if you are just sending raw bytes) or ProtobufPlugin (if you are sending protobufs). You'll implement your own handleReceived/handleReceivedProtobuf - probably based on the example code.
|
||||
You will typically want to inherit from either SinglePortPlugin (if you are just sending/receiving raw bytes) or ProtobufPlugin (if you are sending/receiving protobufs). You'll implement your own handleReceived/handleReceivedProtobuf - probably based on the example code.
|
||||
|
||||
If your plugin needs to perform any operations at startup you can override and implement the setup() method to run your code.
|
||||
|
||||
@@ -45,25 +45,30 @@ A number of [key services](/src/plugins) are implemented using the plugin API, t
|
||||
The easiest way to get started is:
|
||||
|
||||
* [Build and install](build-instructions.md) the standard codebase from github.
|
||||
* Copy [src/plugins/ReplyPlugin.*](/src/plugins/ReplyPlugin.cpp) into src/plugins/YourPlugin.*. Then change the port number from REPLY_APP to PRIVATE_APP.
|
||||
* Copy [src/plugins/ReplyPlugin.*](/src/plugins/ReplyPlugin.cpp) into src/plugins/YourPlugin.*. Then change the port number from *PortNum_REPLY_APP* to *PortNum_PRIVATE_APP*.
|
||||
* Edit plugins/Plugins.cpp:setupPlugins() to add a call to create an instance of your plugin (see comment at head of that function)
|
||||
* Rebuild with your new messaging goodness and install on the device
|
||||
* Use the [meshtastic commandline tool](https://github.com/meshtastic/Meshtastic-python) to send a packet to your board "meshtastic --dest 1234 --ping"
|
||||
* Use the [meshtastic commandline tool](https://github.com/meshtastic/Meshtastic-python) to send a packet to your board, for example "*meshtastic --dest 1234 --sendping*", where *1234* is another mesh node to send the ping to.
|
||||
|
||||
## Threading
|
||||
|
||||
It is very common that you would like your plugin to be invoked periodically.
|
||||
We use a crude/basic cooperative threading system to allow this on any of our supported platforms. Simply inherit from OSThread and implement runOnce(). See the OSThread [documentation](/src/concurrency/OSThread.h) for more details. For an example consumer of this API see RemoteHardwarePlugin::runOnce.
|
||||
|
||||
## Sending messages
|
||||
|
||||
If you would like to proactively send messages (rather than just responding to them), just call service.sendToMesh(). For an example of this see [NodeInfoPlugin::sendOurNodeInfo(...)](/src/plugins/NodeInfoPlugin.cpp).
|
||||
|
||||
## Picking a port number
|
||||
|
||||
For any new 'apps' that run on the device or via sister apps on phones/PCs they should pick and use a unique 'portnum' for their application.
|
||||
|
||||
If you are making a new app using meshtastic, please send in a pull request to add your 'portnum' to [the master list](https://github.com/meshtastic/Meshtastic-protobufs/blob/master/portnums.proto). PortNums should be assigned in the following range:
|
||||
|
||||
* 0-63 Core Meshtastic use, do not use for third party apps
|
||||
* 64-127 Registered 3rd party apps, send in a pull request that adds a new entry to portnums.proto to register your application
|
||||
* 256-511 Use one of these portnums for your private applications that you don't want to register publically
|
||||
* 1024-66559 Are reserved for use by IP tunneling (see FIXME for more information)
|
||||
* **0-63** Core Meshtastic use; do not use for third party apps
|
||||
* **64-127** Registered 3rd party apps. Send in a pull request that adds a new entry to portnums.proto to register your application
|
||||
* **256-511** Use one of these portnums for your private applications that you don't want to register publically
|
||||
* **1024-66559** Are reserved for use by IP tunneling (see *FIXME* for more information)
|
||||
|
||||
All other values are reserved.
|
||||
|
||||
@@ -73,4 +78,4 @@ If you would like to use protocol buffers to define the structures you send over
|
||||
|
||||
* Create a new .proto file in the protos directory. You can use the existing [remote_hardware.proto](https://github.com/meshtastic/Meshtastic-protobufs/blob/master/remote_hardware.proto) file as an example.
|
||||
* Run "bin/regen-protos.sh" to regenerate the C code for accessing the protocol buffers. If you don't have the required nanopb tool, follow the instructions printed by the script to get it.
|
||||
* Done! You can now use your new protobuf just like any of the existing protobufs in meshtastic.
|
||||
* Done! You can now use your new protobuf just like any of the existing protobufs in meshtastic.
|
||||
|
||||
89
docs/software/plugins/ExternalNotificationPlugin.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# About
|
||||
|
||||
The ExternalNotification Plugin will allow you to connect a speaker, LED or other device to notify you when a message has been received from the mesh network.
|
||||
|
||||
# Configuration
|
||||
|
||||
These are the settings that can be configured.
|
||||
|
||||
ext_notification_plugin_enabled
|
||||
Is the plugin enabled?
|
||||
|
||||
0 = Disabled (Default)
|
||||
1 = Enabled
|
||||
|
||||
ext_notification_plugin_active
|
||||
Is your external circuit triggered when our GPIO is low or high?
|
||||
|
||||
0 = Active Low (Default)
|
||||
1 = Active High
|
||||
|
||||
ext_notification_plugin_alert_message
|
||||
Do you want to be notified on an incoming message?
|
||||
|
||||
0 = Disabled (Default)
|
||||
1 = Alert when a text message comes
|
||||
|
||||
ext_notification_plugin_alert_bell
|
||||
Do you want to be notified on an incoming bell?
|
||||
|
||||
0 = Disabled (Default)
|
||||
1 = Alert when the bell character is received
|
||||
|
||||
ext_notification_plugin_output
|
||||
What GPIO is your external circuit attached?
|
||||
|
||||
GPIO of the output. (Default = 13)
|
||||
|
||||
ext_notification_plugin_output_ms
|
||||
How long do you want us to trigger your external circuit?
|
||||
|
||||
Amount of time in ms for the alert. Default is 1000.
|
||||
|
||||
|
||||
# Usage Notes
|
||||
|
||||
For basic usage, start with:
|
||||
|
||||
ext_notification_plugin_enabled = 1
|
||||
ext_notification_plugin_alert_message = 1
|
||||
|
||||
Depending on how your external cirtcuit configured is configured, you may need to set the active state to true.
|
||||
|
||||
ext_notification_plugin_active = 1
|
||||
|
||||
## Alert Types
|
||||
|
||||
We support being alerted on two events:
|
||||
|
||||
1) Incoming Text Message
|
||||
|
||||
2) Incoming Text Message that contains the ascii bell character. At present, only the Python API can send an ascii bell character, but more support may be added in the future.
|
||||
|
||||
### Bell Character
|
||||
|
||||
The bell character is ASCII 0x07. Include 0x07 anywhere in the text message and with ext_notification_plugin_alert_bell enabled, we will issue an external notification.
|
||||
|
||||
# External Hardware
|
||||
|
||||
Be mindful of the max current sink and source of the esp32 GPIO. The easiest devices to interface with would be either an LED or Active Buzzer.
|
||||
|
||||
Ideas for external hardware:
|
||||
|
||||
* LED
|
||||
* Active Buzzer
|
||||
* Flame thrower
|
||||
* Strobe Light
|
||||
* Siren
|
||||
|
||||
# Known Problems
|
||||
|
||||
* This won't directly support an passive (normal) speaker as it does not generate any audio wave forms.
|
||||
* This currently only supports the esp32. Other targets may be possible, I just don't have to test with.
|
||||
* This plugin only monitors text messages. We won't trigger on any other packet types.
|
||||
|
||||
# Need more help?
|
||||
|
||||
Go to the Meshtastic Discourse Group if you have any questions or to share how you have used this.
|
||||
|
||||
https://meshtastic.discourse.group
|
||||
135
docs/software/plugins/RangeTestPlugin.md
Normal file
@@ -0,0 +1,135 @@
|
||||
# About
|
||||
|
||||
The RangeTest Plugin will help you perform range and coverage tests.
|
||||
|
||||
# Configuration
|
||||
|
||||
These are the settings that can be configured.
|
||||
|
||||
range_test_plugin_enabled
|
||||
Is the plugin enabled?
|
||||
|
||||
0 = Disabled (Default)
|
||||
1 = Enabled
|
||||
|
||||
range_test_plugin_save
|
||||
If enabled, we will save a log of all received messages to /static/rangetest.csv which you can access from the webserver. We will abort
|
||||
writing if there is less than 50k of space on the filesystem to prevent filling up the storage.
|
||||
|
||||
0 = Disabled (Default)
|
||||
1 = Enabled
|
||||
|
||||
range_test_plugin_sender
|
||||
Number of seconds to wait between sending packets. Using the long_slow channel configuration, it's best not to go more frequent than once every 60 seconds. You can be more agressive with faster settings. 0 is default which disables sending messages.
|
||||
|
||||
# Usage Notes
|
||||
|
||||
For basic usage, you will need two devices both with a GPS. A device with a paired phone with GPS may work, I have not tried it.
|
||||
|
||||
The first thing to do is to turn on the plugin. With the plugin turned on, the other settings will be available:
|
||||
|
||||
range_test_plugin_enabled = 1
|
||||
|
||||
If you want to send a message every 60 seconds:
|
||||
|
||||
range_test_plugin_sender = 60
|
||||
|
||||
To save a log of the messages:
|
||||
|
||||
range_test_plugin_save = 1
|
||||
|
||||
Recommended settings for a sender at different radio settings:
|
||||
|
||||
Long Slow ... range_test_plugin_sender = 60
|
||||
Long Alt ... range_test_plugin_sender = 30
|
||||
Medium ... range_test_plugin_sender = 15
|
||||
Short Fast ... range_test_plugin_sender = 15
|
||||
|
||||
## Python API Examples
|
||||
|
||||
### Sender
|
||||
|
||||
meshtastic --set range_test_plugin_enabled 1
|
||||
meshtastic --set range_test_plugin_sender 60
|
||||
|
||||
### Receiver
|
||||
|
||||
meshtastic --set range_test_plugin_enabled 1
|
||||
meshtastic --set range_test_plugin_save 1
|
||||
|
||||
## Other things to keep in mind
|
||||
|
||||
Be sure to turn off either the plugin configured as a sender or the device where the plugin setup as sender when not in use. This will use a lot of time on air and will spam your channel.
|
||||
|
||||
Also be mindful of your space usage on the file system. It has protections from filling up the space but it's best to delete old range test results.
|
||||
|
||||
# Application Examples
|
||||
|
||||
## Google Integration
|
||||
|
||||
@jfirwin on our forum [meshtastic.discourse.org](https://meshtastic.discourse.group/t/new-plugin-rangetestplugin/2591/49?u=mc-hamster) shared how to integrate the resulting csv file with Google Products.
|
||||
|
||||
### Earth
|
||||
|
||||
Steps:
|
||||
|
||||
1. [Download](https://www.google.com/earth/versions/#download-pro) 1 and open Google Earth
|
||||
1. Select File > Import
|
||||
2. Select CSV
|
||||
3. Select Delimited, Comma
|
||||
4. Make sure the button that states “This dataset does not contain latitude/longitude information, but street addresses” is unchecked
|
||||
5. Select “rx lat” & “rx long” for the appropriate lat/lng fields
|
||||
6. Click finish
|
||||
2. When it prompts you to create a style template, click yes.
|
||||
1. Set the name field to whichever column you want to be displayed on the map (don’t worry about this too much, when you click on an icon, all the relavant data appears)
|
||||
2. select a color, icon, etc. and hit ok.
|
||||
|
||||
Your data will load onto the map, make sure to click the checkbox next to your dataset in the sidebar to view it.
|
||||
|
||||
### My Maps
|
||||
|
||||
You can use [My Maps](http://mymaps.google.com/). It takes CSVs and the whole interface is much easier to work with.
|
||||
|
||||
Google has instructions on how to do that [here](https://support.google.com/mymaps/answer/3024836?co=GENIE.Platform%3DDesktop&hl=en#zippy=%2Cstep-prepare-your-info%2Cstep-import-info-into-the-map).
|
||||
|
||||
You can style the ranges differently based on the values, so you can have the pins be darker the if the SNR or RSSI (if that gets added) is higher.
|
||||
|
||||
# Known Problems
|
||||
|
||||
* If turned on, using mesh network will become unwieldly because messages are sent over the same channel as the other messages. See TODO below.
|
||||
|
||||
# TODO
|
||||
|
||||
* Right now range test messages go over the TEXT_MESSAGE_APP port. We need a toggle to switch to optionally send over RANGE_TEST_APP.
|
||||
|
||||
# FAQ
|
||||
|
||||
Q: Where is rangetest.csv saved?
|
||||
A: Turn on the WiFi on your device as either a WiFi client or a WiFi AP. Once you can connect to your device, go to /static and you will see rangetest.csv.
|
||||
|
||||
Q: Do I need to have WiFi turned on for the file to be saved?
|
||||
A: Nope, it'll just work.
|
||||
|
||||
Q: Do I need a phone for this plugin?
|
||||
A: There's no need for a phone.
|
||||
|
||||
Q: Can I use this as a message logger?
|
||||
A: While it's not the intended purpose, sure, why not. Do it!
|
||||
|
||||
Q: What will happen if I run out of space on my device?
|
||||
A: We have a protection in place to keep you from completly filling up your device. This will make sure that other device critical functions will continue to work. We will reserve at least 50k of free space.
|
||||
|
||||
Q: What do I do with the rangetest.csv file when I'm done?
|
||||
A: Go to /static and delete the file.
|
||||
|
||||
Q: Can I use this as a sender while on battery power?
|
||||
A: Yes, but your battery will run down quicker than normal. While sending, we tell the device not to go into low-power mode since it needs to keep to a fairly strict timer.
|
||||
|
||||
Q: Why is this operating on incoming messages instead of the existing location discovery protocol?
|
||||
A: This plugin is still young and currently supports monitoring just one port at a time. I decided to use the existing message port because that is easy to test with. A future version will listen to multiple ports to be more promiscuous.
|
||||
|
||||
# Need more help?
|
||||
|
||||
Go to the Meshtastic Discourse Group if you have any questions or to share how you have used this.
|
||||
|
||||
https://meshtastic.discourse.group
|
||||
40
docs/software/plugins/SerialPlugin.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# About
|
||||
|
||||
A simple interface to send messages over the mesh network by sending strings
|
||||
over a serial port.
|
||||
|
||||
Default is to use RX GPIO 16 and TX GPIO 17.
|
||||
|
||||
|
||||
# Basic Usage:
|
||||
|
||||
1) Enable the plugin by setting serialplugin_enabled to 1.
|
||||
2) Set the pins (serialplugin_rxd / serialplugin_rxd) for your preferred RX and TX GPIO pins.
|
||||
On tbeam, recommend to use:
|
||||
RXD 35
|
||||
TXD 15
|
||||
3) Set serialplugin_timeout to the amount of time to wait before we consider
|
||||
your packet as "done".
|
||||
4) (Optional) In SerialPlugin.h set the port to PortNum_TEXT_MESSAGE_APP if you want to
|
||||
send messages to/from the general text message channel.
|
||||
5) Connect to your device over the serial interface at 38400 8N1.
|
||||
6) Send a packet up to 240 bytes in length. This will get relayed over the mesh network.
|
||||
7) (Optional) Set serialplugin_echo to 1 and any message you send out will be echoed back
|
||||
to your device.
|
||||
|
||||
# TODO (in this order):
|
||||
|
||||
* Define a verbose RX mode to report on mesh and packet infomration.
|
||||
- This won't happen any time soon.
|
||||
|
||||
# Known Problems
|
||||
|
||||
* Until the plugin is initilized by the startup sequence, the TX pin is in a floating
|
||||
state. Device connected to that pin may see this as "noise".
|
||||
* Will not work on NRF and the Linux device targets.
|
||||
|
||||
# Need help?
|
||||
|
||||
Need help with this plugin? Post your question on the Meshtastic Discourse:
|
||||
|
||||
https://meshtastic.discourse.group
|
||||
105
docs/software/plugins/StoreForwardPlugin.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# About
|
||||
|
||||
This is a work in progress and is not yet available.
|
||||
|
||||
The Store Forward Plugin is an implementation of a Store and Forward system to enable resilient messaging in the event that a client device is disconnected from the main network.
|
||||
|
||||
Because of the increased network traffic for this overhead, it's not adviced to use this if you are duty cycle limited for your airtime usage nor is it adviced to use this for SF12 (Long range but Slow).
|
||||
|
||||
# Requirements
|
||||
|
||||
Initial Requirements:
|
||||
|
||||
* Must be installed on a router node.
|
||||
* * This is an artificial limitation, but is in place to enforce best practices.
|
||||
* * Router nodes are intended to be always online. If this plugin misses any messages, the reliability of the stored messages will be reduced
|
||||
* Esp32 Processor based device with external PSRAM. (tbeam v1.0 and tbeamv1.1, maybe others)
|
||||
|
||||
# Implementation timeline
|
||||
|
||||
Not necessarily in this order:
|
||||
|
||||
UC 1) MVP - automagically forward packets to a client that may have missed packets.
|
||||
|
||||
UC 2) Client Interface (Web, Android, Python or iOS when that happens) to optionally request packets be resent. This is to support the case where Router has not detected that the client was away. This is because the router will only know you're away if you've been gone for a period of time but will have no way of knowing if you were offline for a short number of minutes. This will cover the case where you have ducked into a cave or you're swapping out your battery.
|
||||
|
||||
UC 3) router sends a periodic “heartbeat” to let the clients know they’re part of the main mesh
|
||||
|
||||
UC 4) support for a mesh to have multiple routers that have the store & forward functionality (for redundancy)
|
||||
|
||||
UC 5) Support for "long term" delayed messages and "short term" delayed messages. Handle the cases slightly different to improve user expierence. A short term delayed message would be a message that was resent becaue a node was not heard from for <5 minutes. A long term delayed message is a message that has not been delivered in >5 minutes.
|
||||
|
||||
UC 6) Eventually we could add a "want_store_and_forward" bit to MeshPacket and that could be nicer than whitelists in this plugin. Initially we'd only set that bit in text messages (and any other plugin messages that can cope with this). This change would be backward wire compatible so can add easily later.
|
||||
|
||||
UC 7) Currently the way we allocate messages in the device code is super inefficient. It always allocates the worst case message size. Really we should dynamically allocate just the # of bytes we need. This would allow many more MeshPackets to be kept in RAM.
|
||||
|
||||
UC 8) We'll want a "delayed" bit in MeshPacket. This will indicate that the message was not received in real time.
|
||||
|
||||
# Things to consider
|
||||
|
||||
Not all these cases will be initially implemented. It's just a running stream of thoughts to be considered.
|
||||
|
||||
## Main Mesh Network with Router
|
||||
|
||||
The store and forward plugin is intended to be enabled on a router that designates your "main" mesh network.
|
||||
|
||||
## Store and Forward on Multiple Routers
|
||||
|
||||
If multiple routers with the plugin are enabled, they should be able to share their stored database amongst each other. This enable resilliancy from one router going offline.
|
||||
|
||||
## Fragmented networks - No router
|
||||
|
||||
In this case, the mesh network has been fragmented by two client devices leaving the main network.
|
||||
|
||||
If two Meshtastic devices walk away from the main mesh, they will be able to message each other but not message the main network. When they return to the main network, they will receive the messages they have missed from the main mesh network.
|
||||
|
||||
## Fragmented network - With routers
|
||||
|
||||
In this case, we have two routers separate by a great distance, each serving multiple devices. One of the routers have gone offline. This has now created two physically seaprated mesh networks using the same channel configuration.
|
||||
|
||||
Q: How do we rejoin both fragmented networks? Do we care about messages that were unrouted between fagments?
|
||||
|
||||
## Identifing Delayed Messages
|
||||
|
||||
When a message is replayed for a node, identify the packet as "Delayed". This will indicate that the message was not received in real time.
|
||||
|
||||
# Router Data Structures
|
||||
|
||||
Structure of received messages:
|
||||
|
||||
receivedMessages
|
||||
Port_No
|
||||
packetID
|
||||
to
|
||||
from
|
||||
rxTimeMsec
|
||||
data
|
||||
|
||||
Structure of nodes and last time we heard from them. This is a record of any packet type.
|
||||
|
||||
receivedRecord
|
||||
From
|
||||
rxTimeMillis
|
||||
|
||||
# General Operation for UC1 - automagically forward packets to a client that may have missed packets
|
||||
|
||||
On every handled packet
|
||||
* Record the sender from and the time we heard from that sender into senderRecord.
|
||||
|
||||
On every handled packet
|
||||
|
||||
* If the packet is a message, save the messsage into receivedMessages
|
||||
|
||||
On every handled packet, if we have not heard from that sender in a period of time greater than timeAway, let's assume that they have been away from the network.
|
||||
|
||||
* In this case, we will resend them all the messages they have missed since they were gone
|
||||
|
||||
## Expected problems this implementation
|
||||
|
||||
* If the client has been away for less than 5 minutes and has received the previously sent message, the client will gracefully ignore it. This is thanks to PacketHistory::wasSeenRecently in PacketHistory.cpp.
|
||||
* * If the client has been away for more than 5 minutes and we resend packets that they have already received, it's possible they will see duplicate messages. This should be unlikely but is still possible.
|
||||
|
||||
|
||||
# Designed limitations
|
||||
|
||||
The Store and Forward plugin will subscribe to specific packet types and channels and only save those. This will both reduce the amount of data we will need to store and reduce the overhead on the network. Eg: There's no need to replay ACK packets nor is there's no need to replay old location packets.
|
||||
121
docs/software/remote-admin.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# Remote node administration
|
||||
|
||||
This is the first documentation for how to use the [multiple channels](channels.md) feature to enable remote adminstration of meshtastic nodes. i.e. let you talk through the mesh to some far away node and change that nodes settings. This is an advanced feature that (currently) few users would need. Also, keep in mind it is possible (if you are not careful) to assign settings to that remote node that cause it to completely drop off of your mesh.
|
||||
|
||||
Btw: I promised to document how multi-channel is now used to secure remote GPIO/serial access. But probably best to debug these instructions first, so I'll wait on that. If you **do** need to use remote GPIO/serial now, just follow these instructions but name your new channel "gpio" or "serial".
|
||||
|
||||
## Creating the "admin" channel
|
||||
|
||||
Okay - now that we've summarized what multiple-channel support is, we can move on to using it to provide remote administrative access to a node.
|
||||
|
||||
By default, nodes will **only** respond to adminstrative commands via the local USB/bluetooth/TCP interface. This provides basic security to prevent unauthorized access. This is actually how 'normal' administration and settings changes work. The only difference for the remote case is that we are sending those commands over the mesh.
|
||||
|
||||
Before a node will allow remote admin access, it must find a channel
|
||||
```
|
||||
meshtastic --info
|
||||
Connected to radio
|
||||
...
|
||||
Channels:
|
||||
PRIMARY psk=default { "modemConfig": "Bw125Cr48Sf4096", "psk": "AQ==" }
|
||||
|
||||
Primary channel URL: https://www.meshtastic.org/d/#CgUYAyIBAQ
|
||||
```
|
||||
|
||||
So from this output you see that this node knows about only one channel and that its PSK is set to the default value.
|
||||
|
||||
But if you then add an admin channel (with "meshtastic --ch-add admin"). Note: the name is important it must be "admin" (sorry):
|
||||
|
||||
Your channels will now look like this:
|
||||
```
|
||||
meshtastic --ch-add admin
|
||||
Connected to radio
|
||||
Writing modified channels to device
|
||||
|
||||
meshtastic --info
|
||||
Connected to radio
|
||||
...
|
||||
Channels:
|
||||
PRIMARY psk=default { "modemConfig": "Bw125Cr48Sf4096", "psk": "AQ==" }
|
||||
SECONDARY psk=secret { "psk": "HW7E3nMbiNbvr6MhsDonLCmj7eSAhttzjbIx/r5OQmg=", "name": "admin" }
|
||||
|
||||
Primary channel URL: https://www.meshtastic.org/d/#CgUYAyIBAQ
|
||||
Complete URL (includes all channels): https://www.meshtastic.org/d/#CgUYAyIBAQopIiAdbsTecxuI1u-voyGwOicsKaPt5ICG23ONsjH-vk5CaCoFYWRtaW4
|
||||
```
|
||||
|
||||
Notice that now we have a new secondary channel. Also, the "--info" option prints out TWO URLs. The "complete URL" includes all of the channels this node understands. You should consider this URL something you should be very cautious about sharing. In the case of remote adminstration, you only need the node you want to adminster and the node you are locally connected to know this new "admin" channel.
|
||||
|
||||
## Sharing the admin channel with other nodes
|
||||
|
||||
I'm going to assume you've already created the admin channel on your "local node" i.e. the meshtastic node sitting on your desk at your home. But now you want to enable access on the "remote node" you want to eventually have far away from you.
|
||||
|
||||
For this step you need physical access to both the nodes.
|
||||
|
||||
1. Create the "admin" channel on the "local node" using the instructions above.
|
||||
2. Copy the "Complete URL" someplace for permanent reference/access.
|
||||
3. Connect meshtastic-python to the "remote node" over the USB port.
|
||||
4. For the "remote node" type "meshtastic --seturl the-url-from-step-2".
|
||||
5. Run "meshtastic --info" and confirm that the "Complete URL" is the same for both of the nodes.
|
||||
6. Done!
|
||||
|
||||
At this point you can take your remote node and install it far away and still be able to change any of its settings.
|
||||
|
||||
## Remotely administering your node
|
||||
|
||||
Now that both your local node and the remote node contain your secret admin channel key, you can do things like this:
|
||||
|
||||
Get the node list from the local node.
|
||||
|
||||
```
|
||||
meshtastic --nodes
|
||||
Connected to radio
|
||||
/----------------------------------------------------------------------------------------------------------\
|
||||
|N| User |AKA| ID | Position |Battery| SNR | LastHeard | Since |
|
||||
|-+------------+---+---------+------------------------+-------+---------+-------------------+--------------|
|
||||
|1|Unknown 9058|?58|!28979058|25.0382°, 121.5731°, N/A| N/A |-13.50 dB|2021-03-22 09:25:42|19 seconds ago|
|
||||
\----------------------------------------------------------------------------------------------------------/
|
||||
```
|
||||
|
||||
Using the node ID from that list, send a message through the mesh telling that node to change its owner name.
|
||||
|
||||
```
|
||||
meshtastic --dest \!28979058 --set-owner "Im Remote"
|
||||
Connected to radio
|
||||
Setting device owner to Im Remote
|
||||
INFO:root:Requesting configuration from remote node (this could take a while)
|
||||
```
|
||||
|
||||
And you can now confirm via the local node that the remote node has changed:
|
||||
|
||||
```
|
||||
meshtastic --nodes
|
||||
Connected to radio
|
||||
/----------------------------------------------------------------------------------------------------\
|
||||
|N| User |AKA| ID | Position |Battery| SNR | LastHeard | Since |
|
||||
|-+---------+---+---------+------------------------+-------+-------+-------------------+-------------|
|
||||
|1|Im Remote|IR |!28979058|25.0382°, 121.5731°, N/A| N/A |8.75 dB|2021-03-22 09:35:42|3 minutes ago|
|
||||
\----------------------------------------------------------------------------------------------------/
|
||||
```
|
||||
|
||||
Note: you can change **any** parameter, add channels or get info from the remote node. Here's an example of setting ls_secs and printing the complete device info from the remote node.
|
||||
|
||||
```
|
||||
meshtastic --dest \!28979058 --set ls_secs 301 --info
|
||||
Connected to radio
|
||||
INFO:root:Requesting configuration from remote node (this could take a while)
|
||||
Set ls_secs to 301
|
||||
Writing modified preferences to device
|
||||
|
||||
|
||||
Preferences: { "lsSecs": 301, "region": "TW" }
|
||||
|
||||
Channels:
|
||||
PRIMARY psk=default { "modemConfig": "Bw125Cr48Sf4096", "psk": "AQ==" }
|
||||
SECONDARY psk=secret { "psk": "HW7E3nMbiNbvr6MhsDonLCmj7eSAhttzjbIx/r5OQmg=", "name": "admin" }
|
||||
|
||||
Primary channel URL: https://www.meshtastic.org/d/#CgUYAyIBAQ
|
||||
Complete URL (includes all channels): https://www.meshtastic.org/d/#CgUYAyIBAQopIiAdbsTecxuI1u-voyGwOicsKaPt5ICG23ONsjH-vk5CaCoFYWRtaW4
|
||||
```
|
||||
|
||||
## Areas for future development
|
||||
|
||||
In the future we will add a "deadman timer" to this feature so that the remote node will revert any changes if you fail to send a special "commit changes" command. This will protect against sending bad settings to nodes that you can't physically access. Instead if the node does not receive a commit message within 10 minutes it will revert all changes and (hopefully) rejoin the mesh.
|
||||
23
docs/software/remote-hardware-service.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Remote Hardware Service
|
||||
|
||||
FIXME - the following are a collection of notes moved from elsewhere. We need to refactor these notes into actual documentation on the remote-hardware/gpio service.
|
||||
|
||||
### 1.7.2. New 'no-code-IOT' mini-app
|
||||
|
||||
Add a new 'remote GPIO/serial port/SPI/I2C access' mini-app. This new standard app would use the MQTT messaging layer to let users (developers that don't need to write device code) do basic (potentially dangerous) operations remotely.
|
||||
|
||||
#### 1.7.2.1. Supported operations in the initial release
|
||||
|
||||
Initially supported features for no-code-IOT.
|
||||
|
||||
- Set any GPIO
|
||||
- Read any GPIO
|
||||
|
||||
#### 1.7.2.2. Supported operations eventually
|
||||
|
||||
General ideas for no-code IOT.
|
||||
|
||||
- Subscribe for notification of GPIO input status change (i.e. when pin goes low, send my app a message)
|
||||
- Write/read N bytes over I2C/SPI bus Y (as one atomic I2C/SPI transaction)
|
||||
- Send N bytes out serial port Z
|
||||
- Subscribe for notification for when regex X matches the bytes that were received on serial port Z
|
||||
@@ -6,5 +6,7 @@ This is a mini design doc for developing the meshtastic software.
|
||||
* Our [project board](https://github.com/orgs/meshtastic/projects/1) - shows what things we are currently working on and remaining work items for the current release.
|
||||
* [Power Management](power.md)
|
||||
* [Mesh algorithm](mesh-alg.md)
|
||||
* [Channels](channels.md) - documentation on how multiple simultaneous channels are used
|
||||
* [Remote adminstration](remote-admin.md)
|
||||
* [External client API](device-api.md) and porting guide for new clients (iOS, python, etc...)
|
||||
* TODO: how to port the device code to a new device.
|
||||
|
||||
BIN
images/Insufficient space.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
@@ -1,11 +0,0 @@
|
||||
# using height of 50 to have 14 pixels beneath icon for text
|
||||
inkscape -z -e icon.png -w 50 -h 50 icon-24px.svg
|
||||
convert icon.png -background white -alpha Background ../src/icon.xbm
|
||||
|
||||
inkscape -z -e compass.png -w 48 -h 48 location_searching-24px.svg
|
||||
convert compass.png -background white -alpha Background ../src/compass.xbm
|
||||
|
||||
inkscape -z -e face.png -w 13 -h 13 face-24px.svg
|
||||
|
||||
inkscape -z -e pin.png -w 13 -h 13 room-24px.svg
|
||||
convert pin.png -background white -alpha Background ../src/pin.xbm
|
||||
BIN
images/platformio-erase.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
@@ -8,10 +8,10 @@
|
||||
MemSize = 0x200000
|
||||
|
||||
; Define the desired ReadMode. Valid options are FASTREAD, READ2O, READ2IO, READ4O and READ4IO
|
||||
ReadMode = READ2IO
|
||||
ReadMode = READ4IO
|
||||
|
||||
; Define the desired WriteMode. Valid options are PP, PP2O, PP4O and PP4IO
|
||||
WriteMode = PP
|
||||
WriteMode = PP4IO
|
||||
|
||||
; Define the desired AddressMode. Valid options are BIT24 and BIT32
|
||||
AddressMode = BIT24
|
||||
@@ -38,12 +38,10 @@ DIO0Pin = 12
|
||||
DIO0Port = 1
|
||||
DIO1Pin = 13
|
||||
DIO1Port = 1
|
||||
|
||||
;These two pins are not connected, but we must name something
|
||||
DIO2Pin = 3
|
||||
DIO2Port = 1
|
||||
DIO2Pin = 7
|
||||
DIO2Port = 0
|
||||
DIO3Pin = 5
|
||||
DIO3Port = 1
|
||||
DIO3Port = 0
|
||||
|
||||
; Define the Index of the Write In Progress (WIP) bit in the status register. Valid options are in the range of 0 to 7.
|
||||
WIPIndex = 0
|
||||
@@ -57,13 +55,8 @@ PPSize = PAGE256
|
||||
; Numbers can be given in decimal, hex (starting with either 0x or 0X) and binary (starting with either 0b or 0B) formats.
|
||||
; The custom instructions will be executed in the order found.
|
||||
|
||||
; This example includes two commands, first a WREN (WRite ENable) and then a WRSR (WRite Satus Register) enabling the Quad Operation and the High Performance
|
||||
; mode for the MX25R6435F memory present in the nRF52840 DK.
|
||||
;InitializationCustomInstruction = 0x06
|
||||
;InitializationCustomInstruction = 0x01, [0x40, 0, 0x2]
|
||||
|
||||
; For MX25R1635F on TTGO board, only two data lines are connected
|
||||
; This example includes two commands, first a WREN (WRite ENable) and then a WRSR (WRite Satus Register) disabling Quad Operation and the High Performance
|
||||
; This example includes two commands, first a WREN (WRite ENable) and then a WRSR (WRite Satus Register) enabling Quad Operation and the High Performance
|
||||
; mode. For normal operation you might want low power mode instead.
|
||||
InitializationCustomInstruction = 0x06
|
||||
InitializationCustomInstruction = 0x01, [0x00, 0, 0x2]
|
||||
InitializationCustomInstruction = 0x01, [0x40, 0, 0x2]
|
||||
|
||||
69
nrf52/ttgo_eink_qpsi0.1.ini
Normal file
@@ -0,0 +1,69 @@
|
||||
; nrfjprog.exe configuration file.
|
||||
|
||||
; Note: QSPI flash is mapped into memory at address 0x12000000
|
||||
|
||||
[DEFAULT_CONFIGURATION]
|
||||
; Define the capacity of the flash memory device in bytes. Set to 0 if no external memory device is present in your board.
|
||||
; MX25R1635F is 16Mbit/2Mbyte
|
||||
MemSize = 0x200000
|
||||
|
||||
; Define the desired ReadMode. Valid options are FASTREAD, READ2O, READ2IO, READ4O and READ4IO
|
||||
ReadMode = READ2IO
|
||||
|
||||
; Define the desired WriteMode. Valid options are PP, PP2O, PP4O and PP4IO
|
||||
WriteMode = PP
|
||||
|
||||
; Define the desired AddressMode. Valid options are BIT24 and BIT32
|
||||
AddressMode = BIT24
|
||||
|
||||
; Define the desired Frequency. Valid options are M2, M4, M8, M16 and M32
|
||||
Frequency = M16
|
||||
|
||||
; Define the desired SPI mode. Valid options are MODE0 and MODE3
|
||||
SpiMode = MODE0
|
||||
|
||||
; Define the desired SckDelay. Valid options are in the range 0 to 255
|
||||
SckDelay = 0x80
|
||||
|
||||
; Define the desired IO level for DIO2 and DIO3 during a custom instruction. Valid options are LEVEL_HIGH and LEVEL_LOW
|
||||
CustomInstructionIO2Level = LEVEL_LOW
|
||||
CustomInstructionIO3Level = LEVEL_HIGH
|
||||
|
||||
; Define the assigned pins for the QSPI peripheral. Valid options are those existing in your device
|
||||
CSNPin = 15
|
||||
CSNPort = 1
|
||||
SCKPin = 14
|
||||
SCKPort = 1
|
||||
DIO0Pin = 12
|
||||
DIO0Port = 1
|
||||
DIO1Pin = 13
|
||||
DIO1Port = 1
|
||||
|
||||
;These two pins are not connected, but we must name something
|
||||
DIO2Pin = 3
|
||||
DIO2Port = 1
|
||||
DIO3Pin = 5
|
||||
DIO3Port = 1
|
||||
|
||||
; Define the Index of the Write In Progress (WIP) bit in the status register. Valid options are in the range of 0 to 7.
|
||||
WIPIndex = 0
|
||||
|
||||
; Define page size for commands. Valid sizes are PAGE256 and PAGE512.
|
||||
PPSize = PAGE256
|
||||
|
||||
; Custom instructions to send to the external memory after initialization. Format is instruction code plus data to send in between optional brakets.
|
||||
; These instructions will be executed each time the qspi peripheral is initiated by nrfjprog.
|
||||
; To improve execution speed on consecutive interations with QSPI, you can run nrfjprog once with custom initialization, and then comment out the lines below.
|
||||
; Numbers can be given in decimal, hex (starting with either 0x or 0X) and binary (starting with either 0b or 0B) formats.
|
||||
; The custom instructions will be executed in the order found.
|
||||
|
||||
; This example includes two commands, first a WREN (WRite ENable) and then a WRSR (WRite Satus Register) enabling the Quad Operation and the High Performance
|
||||
; mode for the MX25R6435F memory present in the nRF52840 DK.
|
||||
;InitializationCustomInstruction = 0x06
|
||||
;InitializationCustomInstruction = 0x01, [0x40, 0, 0x2]
|
||||
|
||||
; For MX25R1635F on TTGO board, only two data lines are connected
|
||||
; This example includes two commands, first a WREN (WRite ENable) and then a WRSR (WRite Satus Register) disabling Quad Operation and the High Performance
|
||||
; mode. For normal operation you might want low power mode instead.
|
||||
InitializationCustomInstruction = 0x06
|
||||
InitializationCustomInstruction = 0x01, [0x00, 0, 0x2]
|
||||
133
platformio.ini
@@ -9,8 +9,16 @@
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[platformio]
|
||||
default_envs = tbeam # lora-relay-v1 # nrf52840dk-geeksville # linux # or if you'd like to change the default to something like lora-relay-v1 put that here
|
||||
;default_envs = heltec # lora-relay-v1 # nrf52840dk-geeksville # linux # or if you'd like to change the default to something like lora-relay-v1 put that here
|
||||
default_envs = tbeam
|
||||
;default_envs = tbeam0.7
|
||||
;default_envs = heltec
|
||||
;default_envs = tlora-v1
|
||||
;default_envs = tlora_v1_3
|
||||
;default_envs = tlora-v2
|
||||
;default_envs = lora-relay-v1 # nrf board
|
||||
;default_envs = eink
|
||||
;default_envs = nrf52840dk-geeksville
|
||||
;default_envs = native # lora-relay-v1 # nrf52840dk-geeksville # linux # or if you'd like to change the default to something like lora-relay-v1 put that here
|
||||
|
||||
[common]
|
||||
; common is not currently used
|
||||
@@ -27,19 +35,16 @@ default_envs = tbeam # lora-relay-v1 # nrf52840dk-geeksville # linux # or if you
|
||||
extra_scripts = bin/platformio-custom.py
|
||||
|
||||
; note: we add src to our include search path so that lmic_project_config can override
|
||||
; note: TINYGPS_OPTION_NO_CUSTOM_FIELDS is VERY important. We don't use custom fields and somewhere in that pile
|
||||
; of code is a heap corruption bug!
|
||||
; FIXME: fix lib/BluetoothOTA dependency back on src/ so we can remove -Isrc
|
||||
build_flags = -Wno-missing-field-initializers -Isrc -Isrc/mesh -Isrc/gps -Ilib/nanopb/include -Wl,-Map,.pio/build/output.map
|
||||
build_flags = -Wno-missing-field-initializers
|
||||
-Wno-format
|
||||
-Isrc -Isrc/mesh -Isrc/gps -Ilib/nanopb/include -Isrc/buzz -Wl,-Map,.pio/build/output.map
|
||||
-DHW_VERSION_${sysenv.COUNTRY}
|
||||
-DHW_VERSION=${sysenv.HW_VERSION}
|
||||
-DUSE_THREAD_NAMES
|
||||
-DTINYGPSPLUS_OPTION_NO_CUSTOM_FIELDS
|
||||
|
||||
; leave this commented out to avoid breaking Windows
|
||||
;upload_port = /dev/ttyUSB0
|
||||
;monitor_port = /dev/ttyUSB0
|
||||
|
||||
;upload_port = /dev/cu.SLAB_USBtoUART
|
||||
;monitor_port = /dev/cu.SLAB_USBtoUART
|
||||
-DTINYGPS_OPTION_NO_CUSTOM_FIELDS
|
||||
|
||||
; the default is esptool
|
||||
; upload_protocol = esp-prog
|
||||
@@ -60,17 +65,17 @@ debug_tool = jlink
|
||||
; monitor adapter_khz 10000
|
||||
|
||||
lib_deps =
|
||||
https://github.com/meshtastic/esp8266-oled-ssd1306.git ; ESP8266_SSD1306
|
||||
https://github.com/meshtastic/esp8266-oled-ssd1306.git#35d796226b853b0c0ff818b2f1aa3d35e7296a96 ; ESP8266_SSD1306
|
||||
https://github.com/geeksville/OneButton.git ; OneButton library for non-blocking button debounce
|
||||
1202 ; CRC32, explicitly needed because dependency is missing in the ble ota update lib
|
||||
https://github.com/meshtastic/arduino-fsm.git#2f106146071fc7bc620e1e8d4b88dc4e0266ce39
|
||||
https://github.com/meshtastic/arduino-fsm.git#55c47b6cded91645aff05a27b6e5821d8d0f64be
|
||||
https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.git#31015a55e630a2df77d9d714669c621a5bf355ad
|
||||
https://github.com/meshtastic/RadioLib.git#8657380241bce681c33aab46598bbf13b11f876c
|
||||
https://github.com/meshtastic/TinyGPSPlus.git#9c1d584d2469523381e077b0b9c1bf868d6c0206
|
||||
https://github.com/meshtastic/RadioLib.git#07de964e929238949035fb0d5887026a3058df1a
|
||||
https://github.com/meshtastic/TinyGPSPlus.git#f0f47067ef2f67c856475933188251c1ef615e79
|
||||
https://github.com/meshtastic/AXP202X_Library.git#8404abb6d4b486748636bc6ad72d2a47baaf5460
|
||||
Wire ; explicitly needed here because the AXP202 library forgets to add it
|
||||
SPI
|
||||
https://github.com/geeksville/ArduinoThread.git#333ffd09b596977c217ba25da4258f588b462ac6
|
||||
https://github.com/geeksville/ArduinoThread.git#72921ac222eed6f526ba1682023cee290d9aa1b3
|
||||
|
||||
; Common settings for conventional (non Portduino) Ardino targets
|
||||
[arduino_base]
|
||||
@@ -99,11 +104,20 @@ build_flags =
|
||||
lib_deps =
|
||||
${arduino_base.lib_deps}
|
||||
https://github.com/meshtastic/esp32_https_server.git
|
||||
adafruit/DHT sensor library@^1.4.1
|
||||
adafruit/Adafruit Unified Sensor@^1.1.4
|
||||
# Hmm - this doesn't work yet
|
||||
# board_build.ldscript = linker/esp32.extram.bss.ld
|
||||
lib_ignore = segger_rtt
|
||||
platform_packages =
|
||||
framework-arduinoespressif32@https://github.com/meshtastic/arduino-esp32.git#2814f110aa618429bdd9a0a2d6a93c55f29f87a6
|
||||
framework-arduinoespressif32@https://github.com/meshtastic/arduino-esp32.git#352c8ea7cb73f10433ed139f34251979c470ad56
|
||||
|
||||
; leave this commented out to avoid breaking Windows
|
||||
upload_port = /dev/ttyUSB0
|
||||
;monitor_port = /dev/ttyUSB0
|
||||
|
||||
;upload_port = /dev/cu.SLAB_USBtoUART
|
||||
;monitor_port = /dev/cu.SLAB_USBtoUART
|
||||
|
||||
; customize the partition table
|
||||
; http://docs.platformio.org/en/latest/platforms/espressif32.html#partition-tables
|
||||
@@ -145,6 +159,12 @@ build_flags =
|
||||
${esp32_base.build_flags} -D TLORA_V1
|
||||
|
||||
; note: the platformio definition for lora32-v2 seems stale, it is missing a pins_arduino.h file, therefore I don't think it works
|
||||
[env:tlora_v1_3]
|
||||
extends = esp32_base
|
||||
board = ttgo-lora32-v1
|
||||
build_flags =
|
||||
${esp32_base.build_flags} -D TLORA_V1_3
|
||||
|
||||
[env:tlora-v2]
|
||||
extends = esp32_base
|
||||
board = ttgo-lora32-v1
|
||||
@@ -174,20 +194,40 @@ src_filter =
|
||||
; platform = nordicnrf52
|
||||
platform = https://github.com/meshtastic/platform-nordicnrf52.git#1a2639a6b0f79b5df66bea3e3089f0d5285fdc63
|
||||
extends = arduino_base
|
||||
debug_tool = jlink
|
||||
build_type = debug ; I'm debugging with ICE a lot now
|
||||
; note: liboberon provides the AES256 implementation for NRF52 (though not using the hardware acceleration of the NRF52840 - FIXME)
|
||||
build_flags =
|
||||
${arduino_base.build_flags} -Wno-unused-variable
|
||||
-Isrc/nrf52
|
||||
-Isdk-nrfxlib/crypto/nrf_oberon/include -Lsdk-nrfxlib/crypto/nrf_oberon/lib/cortex-m4/hard-float/ -lliboberon_3.0.3
|
||||
-Isdk-nrfxlib/crypto/nrf_oberon/include -Lsdk-nrfxlib/crypto/nrf_oberon/lib/cortex-m4/hard-float/ -lliboberon_3.0.7
|
||||
;-DCFG_DEBUG=3
|
||||
src_filter =
|
||||
${arduino_base.src_filter} -<esp32/> -<nimble/> -<meshwifi/>
|
||||
${arduino_base.src_filter} -<esp32/> -<nimble/> -<mesh/wifi/> -<mesh/http/> -<plugins/esp32>
|
||||
lib_ignore =
|
||||
BluetoothOTA
|
||||
monitor_port = /dev/ttyACM1
|
||||
|
||||
# we pass in options to jlink so it can understand freertos (note: we don't use "jlink" as the tool)
|
||||
debug_tool = jlink
|
||||
debug_port = :2331
|
||||
# Note: the ARGUMENTS MUST BE on multiple lines. Otherwise platformio/commands/debug/helpers.py misparses everything into the "executable"
|
||||
# attribute and leaves "arguments" empty
|
||||
# /home/kevinh/.platformio/packages/tool-jlink/JLinkGDBServerCLExe
|
||||
# This doesn't work yet, so not using for now
|
||||
disabled_debug_server =
|
||||
/usr/bin/JLinkGDBServerCLExe
|
||||
-singlerun
|
||||
-if
|
||||
SWD
|
||||
-select
|
||||
USB
|
||||
-device
|
||||
nRF52840_xxAA
|
||||
-port
|
||||
2331
|
||||
-rtos
|
||||
GDBServer/RTOSPlugin_FreeRTOS
|
||||
|
||||
debug_extra_cmds =
|
||||
source gdbinit
|
||||
|
||||
@@ -199,6 +239,24 @@ debug_init_break =
|
||||
;debug_init_break = tbreak loop
|
||||
;debug_init_break = tbreak Reset_Handler
|
||||
|
||||
[env:lora_isp4520]
|
||||
extends = nrf52_base
|
||||
board = lora_isp4520
|
||||
|
||||
# add our variants files to the include and src paths
|
||||
build_flags = ${nrf52_base.build_flags} -Ivariants/lora_isp4520
|
||||
|
||||
# No screen and GPS on the board. We still need RTC.cpp for the RTC clock.
|
||||
src_filter = ${nrf52_base.src_filter} +<../variants/lora_isp4520> -<graphics> -<gps> +<gps/GPS.cpp> +<gps/RTC.cpp>
|
||||
lib_ignore = ${nrf52_base.lib_ignore}
|
||||
ESP8266_SSD1306
|
||||
SparkFun Ublox Arduino Library
|
||||
AXP202X_Library
|
||||
TinyGPSPlus
|
||||
|
||||
upload_protocol = jlink
|
||||
monitor_port = /dev/ttyUSB0
|
||||
|
||||
; The NRF52840-dk development board
|
||||
; Note: By default no lora device is created for this build - it uses a simulated interface
|
||||
[env:nrf52840dk]
|
||||
@@ -247,7 +305,7 @@ src_filter = ${nrf52_base.src_filter} +<../variants/ppr1>
|
||||
lib_deps =
|
||||
${arduino_base.lib_deps}
|
||||
|
||||
; Prototype eink/nrf52840/sx1262 device
|
||||
; First prototype eink/nrf52840/sx1262 device
|
||||
[env:eink]
|
||||
extends = nrf52_base
|
||||
board = eink
|
||||
@@ -261,6 +319,20 @@ lib_deps =
|
||||
https://github.com/geeksville/EPD_Libraries.git
|
||||
TFT_eSPI
|
||||
|
||||
; First prototype eink/nrf52840/sx1262 device
|
||||
[env:eink0.1]
|
||||
extends = nrf52_base
|
||||
board = eink0.1
|
||||
# add our variants files to the include and src paths
|
||||
# define build flags for the TFT_eSPI library
|
||||
build_flags = ${nrf52_base.build_flags} -Ivariants/eink0.1
|
||||
-DBUSY_PIN=3 -DRST_PIN=2 -DDC_PIN=28 -DCS_PIN=30
|
||||
src_filter = ${nrf52_base.src_filter} +<../variants/eink0.1>
|
||||
lib_deps =
|
||||
${arduino_base.lib_deps}
|
||||
https://github.com/geeksville/EPD_Libraries.git
|
||||
TFT_eSPI
|
||||
|
||||
; The https://github.com/BigCorvus/SX1262-LoRa-BLE-Relay board by @BigCorvus
|
||||
[env:lora-relay-v1]
|
||||
extends = nrf52_base
|
||||
@@ -308,9 +380,20 @@ lib_deps =
|
||||
TFT_eSPI
|
||||
|
||||
; The Portduino based sim environment on top of linux
|
||||
[env:linux]
|
||||
platform = https://github.com/geeksville/platform-portduino.git
|
||||
src_filter = ${env.src_filter} -<esp32/> -<nimble/> -<nrf52/> -<meshwifi/>
|
||||
[env:native]
|
||||
platform = https://github.com/geeksville/platform-native.git
|
||||
src_filter = ${env.src_filter} -<esp32/> -<nimble/> -<nrf52/> -<mesh/http/> -<plugins/esp32>
|
||||
build_flags = ${arduino_base.build_flags} -O0
|
||||
framework = arduino
|
||||
board = linux_x86_64
|
||||
board = native
|
||||
lib_deps =
|
||||
${arduino_base.lib_deps}
|
||||
rweather/Crypto
|
||||
|
||||
; The GenieBlocks LORA prototype board
|
||||
; note: @geeksville disabled because genieblocks_lora is not checked into the boards directory, please send in a PR to add it ;-)
|
||||
;[env:genieblocks_lora]
|
||||
;extends = esp32_base
|
||||
;board = genieblocks_lora
|
||||
;build_flags =
|
||||
; ${esp32_base.build_flags} -D GENIEBLOCKS
|
||||
2
proto
@@ -16,6 +16,10 @@ template <class T> class Observer
|
||||
public:
|
||||
virtual ~Observer();
|
||||
|
||||
/// Stop watching our current obserable
|
||||
void unobserve();
|
||||
|
||||
/// Start watching a specified observable
|
||||
void observe(Observable<T> *o);
|
||||
|
||||
private:
|
||||
@@ -82,6 +86,11 @@ template <class T> class Observable
|
||||
};
|
||||
|
||||
template <class T> Observer<T>::~Observer()
|
||||
{
|
||||
unobserve();
|
||||
}
|
||||
|
||||
template <class T> void Observer<T>::unobserve()
|
||||
{
|
||||
if (observed)
|
||||
observed->removeObserver(this);
|
||||
|
||||
@@ -1,15 +1,39 @@
|
||||
#include "power.h"
|
||||
#include "NodeDB.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "main.h"
|
||||
#include "sleep.h"
|
||||
#include "utils.h"
|
||||
|
||||
#ifdef TBEAM_V10
|
||||
// FIXME. nasty hack cleanup how we load axp192
|
||||
#undef AXP192_SLAVE_ADDRESS
|
||||
#include "axp20x.h"
|
||||
|
||||
#ifdef TBEAM_V10
|
||||
AXP20X_Class axp;
|
||||
#else
|
||||
// Copy of the base class defined in axp20x.h.
|
||||
// I'd rather not inlude axp20x.h as it brings Wire dependency.
|
||||
class HasBatteryLevel {
|
||||
public:
|
||||
/**
|
||||
* Battery state of charge, from 0 to 100 or -1 for unknown
|
||||
*/
|
||||
virtual int getBattPercentage() { return -1; }
|
||||
|
||||
/**
|
||||
* The raw voltage of the battery or NAN if unknown
|
||||
*/
|
||||
virtual float getBattVoltage() { return NAN; }
|
||||
|
||||
/**
|
||||
* return true if there is a battery installed in this unit
|
||||
*/
|
||||
virtual bool isBatteryConnect() { return false; }
|
||||
|
||||
virtual bool isVBUSPlug() { return false; }
|
||||
virtual bool isChargeing() { return false; }
|
||||
};
|
||||
#endif
|
||||
|
||||
bool pmu_irq = false;
|
||||
@@ -50,7 +74,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
||||
*/
|
||||
virtual int getBattPercentage()
|
||||
{
|
||||
float v = getBattVoltage() / 1000;
|
||||
float v = getBattVoltage();
|
||||
|
||||
if (v < noBatVolt)
|
||||
return -1; // If voltage is super low assume no battery installed
|
||||
@@ -66,13 +90,26 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
||||
*/
|
||||
virtual float getBattVoltage()
|
||||
{
|
||||
// Tested ttgo eink nrf52 board and the reported value is perfect
|
||||
// DEBUG_MSG("raw val %u", raw);
|
||||
return
|
||||
|
||||
#ifndef ADC_MULTIPLIER
|
||||
#define ADC_MULTIPLIER 2.0
|
||||
#endif
|
||||
|
||||
#ifdef BATTERY_PIN
|
||||
1000.0 * 2.0 * (AREF_VOLTAGE / 1024.0) * analogRead(BATTERY_PIN);
|
||||
// Do not call analogRead() often.
|
||||
const uint32_t min_read_interval = 5000;
|
||||
if (millis() - last_read_time_ms > min_read_interval) {
|
||||
last_read_time_ms = millis();
|
||||
uint32_t raw = analogRead(BATTERY_PIN);
|
||||
float scaled = 1000.0 * ADC_MULTIPLIER * (AREF_VOLTAGE / 1024.0) * raw;
|
||||
// DEBUG_MSG("raw val=%u scaled=%u\n", raw, (uint32_t)(scaled));
|
||||
last_read_value = scaled;
|
||||
return scaled;
|
||||
} else {
|
||||
return last_read_value;
|
||||
}
|
||||
#else
|
||||
NAN;
|
||||
return NAN;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -83,7 +120,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
||||
|
||||
/// If we see a battery voltage higher than physics allows - assume charger is pumping
|
||||
/// in power
|
||||
virtual bool isVBUSPlug() { return getBattVoltage() > chargingVolt; }
|
||||
virtual bool isVBUSPlug() { return getBattVoltage() > 1000 * chargingVolt; }
|
||||
|
||||
/// Assume charging if we have a battery and external power is connected.
|
||||
/// we can't be smart enough to say 'full'?
|
||||
@@ -92,7 +129,9 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
||||
private:
|
||||
/// If we see a battery voltage higher than physics allows - assume charger is pumping
|
||||
/// in power
|
||||
const float fullVolt = 4.2, emptyVolt = 3.27, chargingVolt = 4.3, noBatVolt = 2.1;
|
||||
const float fullVolt = 4200, emptyVolt = 3270, chargingVolt = 4210, noBatVolt = 2100;
|
||||
float last_read_value = 0.0;
|
||||
uint32_t last_read_time_ms = 0;
|
||||
} analogLevel;
|
||||
|
||||
Power::Power() : OSThread("Power") {}
|
||||
@@ -139,6 +178,8 @@ void Power::shutdown()
|
||||
#ifdef TBEAM_V10
|
||||
DEBUG_MSG("Shutting down\n");
|
||||
axp.shutdown();
|
||||
#elif NRF52_SERIES
|
||||
doDeepSleep(DELAY_FOREVER);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -268,7 +309,42 @@ bool Power::axp192Init()
|
||||
DEBUG_MSG("DCDC3: %s\n", axp.isDCDC3Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("Exten: %s\n", axp.isExtenEnable() ? "ENABLE" : "DISABLE");
|
||||
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1320MA); // actual limit (in HW) on the tbeam is 450mA
|
||||
if (radioConfig.preferences.charge_current == ChargeCurrent_MAUnset) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_450MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA100) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_100MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA190) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_190MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA280) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_280MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA360) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_360MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA450) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_450MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA550) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_550MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA630) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_630MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA700) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_700MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA780) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_780MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA880) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_880MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA960) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_960MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA1000) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1000MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA1080) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1080MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA1160) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1160MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA1240) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1240MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA1320) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1320MA);
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
// Not connected
|
||||
|
||||
@@ -9,6 +9,23 @@
|
||||
#include "sleep.h"
|
||||
#include "target_specific.h"
|
||||
|
||||
/// Should we behave as if we have AC power now?
|
||||
static bool isPowered()
|
||||
{
|
||||
bool isRouter = radioConfig.preferences.is_router;
|
||||
|
||||
// If we are not a router and we already have AC power go to POWER state after init, otherwise go to ON
|
||||
// We assume routers might be powered all the time, but from a low current (solar) source
|
||||
bool isLowPower = radioConfig.preferences.is_low_power || isRouter;
|
||||
|
||||
/* To determine if we're externally powered, assumptions
|
||||
1) If we're powered up and there's no battery, we must be getting power externally. (because we'd be dead otherwise)
|
||||
|
||||
2) If we detect USB power from the power management chip, we must be getting power externally.
|
||||
*/
|
||||
return !isLowPower && powerStatus && (!powerStatus->getHasBattery() || powerStatus->getHasUSB());
|
||||
}
|
||||
|
||||
static void sdsEnter()
|
||||
{
|
||||
// FIXME - make sure GPS and LORA radio are off first - because we want close to zero current draw
|
||||
@@ -97,7 +114,8 @@ static void lsIdle()
|
||||
static void lsExit()
|
||||
{
|
||||
// setGPSPower(true); // restore GPS power
|
||||
gps->forceWake(true);
|
||||
if (gps)
|
||||
gps->forceWake(true);
|
||||
}
|
||||
|
||||
static void nbEnter()
|
||||
@@ -118,14 +136,34 @@ static void serialEnter()
|
||||
{
|
||||
setBluetoothEnable(false);
|
||||
screen->setOn(true);
|
||||
screen->print("Using API...\n");
|
||||
screen->print("Serial connected\n");
|
||||
}
|
||||
|
||||
static void serialExit()
|
||||
{
|
||||
screen->print("Serial disconnected\n");
|
||||
}
|
||||
|
||||
static void powerEnter()
|
||||
{
|
||||
screen->setOn(true);
|
||||
setBluetoothEnable(true);
|
||||
screen->print("Powered...\n");
|
||||
if (!isPowered()) {
|
||||
// If we got here, we are in the wrong state - we should be in powered, let that state ahndle things
|
||||
DEBUG_MSG("Loss of power in Powered\n");
|
||||
powerFSM.trigger(EVENT_POWER_DISCONNECTED);
|
||||
} else {
|
||||
screen->setOn(true);
|
||||
setBluetoothEnable(true);
|
||||
screen->print("Powered...\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void powerIdle()
|
||||
{
|
||||
if (!isPowered()) {
|
||||
// If we got here, we are in the wrong state
|
||||
DEBUG_MSG("Loss of power in Powered\n");
|
||||
powerFSM.trigger(EVENT_POWER_DISCONNECTED);
|
||||
}
|
||||
}
|
||||
|
||||
static void powerExit()
|
||||
@@ -144,12 +182,20 @@ static void onEnter()
|
||||
|
||||
uint32_t now = millis();
|
||||
|
||||
if (now - lastPingMs > 30 * 1000) { // if more than a minute since our last press, ask node we are looking at to update their state
|
||||
if (now - lastPingMs >
|
||||
30 * 1000) { // if more than a minute since our last press, ask node we are looking at to update their state
|
||||
if (displayedNodeNum)
|
||||
service.sendNetworkPing(displayedNodeNum, true); // Refresh the currently displayed node
|
||||
lastPingMs = now;
|
||||
}
|
||||
}
|
||||
|
||||
static void onIdle()
|
||||
{
|
||||
if (isPowered()) {
|
||||
// If we got here, we are in the wrong state - we should be in powered, let that state ahndle things
|
||||
powerFSM.trigger(EVENT_POWER_CONNECTED);
|
||||
}
|
||||
}
|
||||
|
||||
static void screenPress()
|
||||
@@ -163,19 +209,17 @@ State stateSDS(sdsEnter, NULL, NULL, "SDS");
|
||||
State stateLS(lsEnter, lsIdle, lsExit, "LS");
|
||||
State stateNB(nbEnter, NULL, NULL, "NB");
|
||||
State stateDARK(darkEnter, NULL, NULL, "DARK");
|
||||
State stateSERIAL(serialEnter, NULL, NULL, "SERIAL");
|
||||
State stateSERIAL(serialEnter, NULL, serialExit, "SERIAL");
|
||||
State stateBOOT(bootEnter, NULL, NULL, "BOOT");
|
||||
State stateON(onEnter, NULL, NULL, "ON");
|
||||
State statePOWER(powerEnter, NULL, powerExit, "POWER");
|
||||
State stateON(onEnter, onIdle, NULL, "ON");
|
||||
State statePOWER(powerEnter, powerIdle, powerExit, "POWER");
|
||||
Fsm powerFSM(&stateBOOT);
|
||||
|
||||
void PowerFSM_setup()
|
||||
{
|
||||
// If we are not a router and we already have AC power go to POWER state after init, otherwise go to ON
|
||||
// We assume routers might be powered all the time, but from a low current (solar) source
|
||||
bool isLowPower = radioConfig.preferences.is_low_power;
|
||||
bool hasPower = !isLowPower && powerStatus && powerStatus->getHasUSB();
|
||||
bool isRouter = radioConfig.preferences.is_router;
|
||||
bool hasPower = isPowered();
|
||||
|
||||
DEBUG_MSG("PowerFSM init, USB power=%d\n", hasPower);
|
||||
powerFSM.add_timed_transition(&stateBOOT, hasPower ? &statePOWER : &stateON, 3 * 1000, NULL, "boot timeout");
|
||||
|
||||
@@ -222,25 +266,33 @@ void PowerFSM_setup()
|
||||
powerFSM.add_transition(&stateON, &stateON, EVENT_RECEIVED_TEXT_MSG, NULL, "Received text"); // restarts the sleep timer
|
||||
}
|
||||
|
||||
// If we are not in statePOWER but get a serial connection, suppress sleep (and keep the screen on) while connected
|
||||
powerFSM.add_transition(&stateLS, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API");
|
||||
powerFSM.add_transition(&stateNB, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API");
|
||||
powerFSM.add_transition(&stateDARK, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API");
|
||||
powerFSM.add_transition(&stateON, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API");
|
||||
powerFSM.add_transition(&statePOWER, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API");
|
||||
|
||||
if (!isLowPower) {
|
||||
powerFSM.add_transition(&stateLS, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect");
|
||||
powerFSM.add_transition(&stateNB, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect");
|
||||
powerFSM.add_transition(&stateDARK, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect");
|
||||
powerFSM.add_transition(&stateON, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect");
|
||||
}
|
||||
// If we get power connected, go to the power connect state
|
||||
powerFSM.add_transition(&stateLS, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect");
|
||||
powerFSM.add_transition(&stateNB, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect");
|
||||
powerFSM.add_transition(&stateDARK, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect");
|
||||
powerFSM.add_transition(&stateON, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect");
|
||||
|
||||
powerFSM.add_transition(&statePOWER, &stateON, EVENT_POWER_DISCONNECTED, NULL, "power disconnected");
|
||||
powerFSM.add_transition(&stateSERIAL, &stateON, EVENT_POWER_DISCONNECTED, NULL, "power disconnected");
|
||||
// powerFSM.add_transition(&stateSERIAL, &stateON, EVENT_POWER_DISCONNECTED, NULL, "power disconnected");
|
||||
|
||||
powerFSM.add_transition(&stateSERIAL, &stateNB, EVENT_SERIAL_DISCONNECTED, NULL, "serial disconnect");
|
||||
// the only way to leave state serial is for the client to disconnect (or we timeout and force disconnect them)
|
||||
// when we leave, go to ON (which might not be the correct state if we have power connected, we will fix that in onEnter)
|
||||
powerFSM.add_transition(&stateSERIAL, &stateON, EVENT_SERIAL_DISCONNECTED, NULL, "serial disconnect");
|
||||
|
||||
powerFSM.add_transition(&stateDARK, &stateDARK, EVENT_CONTACT_FROM_PHONE, NULL, "Contact from phone");
|
||||
|
||||
// each time we get a new update packet make sure we are staying in the ON state so the screen stays awake (also we don't
|
||||
// shutdown bluetooth if is_router)
|
||||
powerFSM.add_transition(&stateDARK, &stateON, EVENT_FIRMWARE_UPDATE, NULL, "Got firmware update");
|
||||
powerFSM.add_transition(&stateON, &stateON, EVENT_FIRMWARE_UPDATE, NULL, "Got firmware update");
|
||||
|
||||
powerFSM.add_transition(&stateNB, &stateDARK, EVENT_PACKET_FOR_PHONE, NULL, "Packet for phone");
|
||||
|
||||
powerFSM.add_timed_transition(&stateON, &stateDARK, getPref_screen_on_secs() * 1000, NULL, "Screen-on timeout");
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#define EVENT_SERIAL_DISCONNECTED 12
|
||||
#define EVENT_POWER_CONNECTED 13
|
||||
#define EVENT_POWER_DISCONNECTED 14
|
||||
#define EVENT_FIRMWARE_UPDATE 15 // We just received a new firmware update packet from the phone
|
||||
|
||||
extern Fsm powerFSM;
|
||||
extern State statePOWER, stateSERIAL;
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace meshtastic
|
||||
*/
|
||||
enum OptionalBool { OptFalse = 0, OptTrue = 1, OptUnknown = 2 };
|
||||
|
||||
/// Describes the state of the GPS system.
|
||||
/// Describes the state of the Power system.
|
||||
class PowerStatus : public Status
|
||||
{
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#include "RedirectablePrint.h"
|
||||
#include "RTC.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "configuration.h"
|
||||
#include <assert.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
/**
|
||||
* A printer that doesn't go anywhere
|
||||
@@ -15,10 +19,86 @@ void RedirectablePrint::setDestination(Print *_dest)
|
||||
|
||||
size_t RedirectablePrint::write(uint8_t c)
|
||||
{
|
||||
// Always send the characters to our segger JTAG debugger
|
||||
#ifdef SEGGER_STDOUT_CH
|
||||
SEGGER_RTT_PutCharSkip(SEGGER_STDOUT_CH, c);
|
||||
SEGGER_RTT_PutChar(SEGGER_STDOUT_CH, c);
|
||||
#endif
|
||||
|
||||
dest->write(c);
|
||||
return 1; // We always claim one was written, rather than trusting what the serial port said (which could be zero)
|
||||
return 1; // We always claim one was written, rather than trusting what the
|
||||
// serial port said (which could be zero)
|
||||
}
|
||||
|
||||
size_t RedirectablePrint::vprintf(const char *format, va_list arg)
|
||||
{
|
||||
va_list copy;
|
||||
|
||||
va_copy(copy, arg);
|
||||
int len = vsnprintf(printBuf, printBufLen, format, copy);
|
||||
va_end(copy);
|
||||
if (len < 0) {
|
||||
va_end(arg);
|
||||
return 0;
|
||||
};
|
||||
if (len >= printBufLen) {
|
||||
delete[] printBuf;
|
||||
printBufLen *= 2;
|
||||
printBuf = new char[printBufLen];
|
||||
len = vsnprintf(printBuf, printBufLen, format, arg);
|
||||
}
|
||||
|
||||
len = Print::write(printBuf, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t RedirectablePrint::logDebug(const char *format, ...)
|
||||
{
|
||||
size_t r = 0;
|
||||
|
||||
if (!inDebugPrint) {
|
||||
inDebugPrint = true;
|
||||
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
|
||||
// Cope with 0 len format strings, but look for new line terminator
|
||||
bool hasNewline = *format && format[strlen(format) - 1] == '\n';
|
||||
|
||||
// If we are the first message on a report, include the header
|
||||
if (!isContinuationMessage) {
|
||||
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityFromNet);
|
||||
if (rtc_sec > 0) {
|
||||
long hms = rtc_sec % SEC_PER_DAY;
|
||||
// hms += tz.tz_dsttime * SEC_PER_HOUR;
|
||||
// hms -= tz.tz_minuteswest * SEC_PER_MIN;
|
||||
// mod `hms` to ensure in positive range of [0...SEC_PER_DAY)
|
||||
hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
|
||||
|
||||
// Tear apart hms into h:m:s
|
||||
int hour = hms / SEC_PER_HOUR;
|
||||
int min = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
|
||||
int sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN
|
||||
|
||||
r += printf("%02d:%02d:%02d %u ", hour, min, sec, millis() / 1000);
|
||||
} else
|
||||
r += printf("??:??:?? %u ", millis() / 1000);
|
||||
|
||||
auto thread = concurrency::OSThread::currentThread;
|
||||
if (thread) {
|
||||
print("[");
|
||||
// printf("%p ", thread);
|
||||
// assert(thread->ThreadName.length());
|
||||
print(thread->ThreadName);
|
||||
print("] ");
|
||||
}
|
||||
}
|
||||
|
||||
r += vprintf(format, arg);
|
||||
va_end(arg);
|
||||
|
||||
isContinuationMessage = !hasNewline;
|
||||
inDebugPrint = false;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Print.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/**
|
||||
* A Printable that can be switched to squirt its bytes to a different sink.
|
||||
@@ -11,6 +12,15 @@ class RedirectablePrint : public Print
|
||||
{
|
||||
Print *dest;
|
||||
|
||||
/// We dynamically grow this scratch buffer if necessary
|
||||
char *printBuf = new char[64];
|
||||
size_t printBufLen = 64;
|
||||
|
||||
/// Used to allow multiple logDebug messages to appear on a single log line
|
||||
bool isContinuationMessage = false;
|
||||
|
||||
volatile bool inDebugPrint = false;
|
||||
|
||||
public:
|
||||
RedirectablePrint(Print *_dest) : dest(_dest) {}
|
||||
|
||||
@@ -20,6 +30,21 @@ class RedirectablePrint : public Print
|
||||
void setDestination(Print *dest);
|
||||
|
||||
virtual size_t write(uint8_t c);
|
||||
|
||||
/**
|
||||
* Debug logging print message
|
||||
*
|
||||
* If the provide format string ends with a newline we assume it is the final print of a single
|
||||
* log message. Otherwise we assume more prints will come before the log message ends. This
|
||||
* allows you to call logDebug a few times to build up a single log message line if you wish.
|
||||
*
|
||||
* FIXME, eventually add log levels (INFO, WARN, ERROR) and subsystems. Move into
|
||||
* a different class.
|
||||
*/
|
||||
size_t logDebug(const char * format, ...) __attribute__ ((format (printf, 2, 3)));
|
||||
|
||||
/** like printf but va_list based */
|
||||
size_t vprintf(const char *format, va_list arg);
|
||||
};
|
||||
|
||||
class NoopPrint : public Print
|
||||
|
||||
@@ -1,24 +1,34 @@
|
||||
#include "SerialConsole.h"
|
||||
#include "NodeDB.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "configuration.h"
|
||||
#include "NodeDB.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
#define Port Serial
|
||||
|
||||
SerialConsole console;
|
||||
SerialConsole *console;
|
||||
|
||||
void consoleInit()
|
||||
{
|
||||
new SerialConsole(); // Must be dynamically allocated because we are now inheriting from thread
|
||||
}
|
||||
|
||||
void consolePrintf(const char *format, ...)
|
||||
{
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
console->vprintf(format, arg);
|
||||
va_end(arg);
|
||||
}
|
||||
|
||||
SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port)
|
||||
{
|
||||
assert(!console);
|
||||
console = this;
|
||||
canWrite = false; // We don't send packets to our port until it has talked to us first
|
||||
// setDestination(&noopPrint); for testing, try turning off 'all' debug output and see what leaks
|
||||
}
|
||||
// setDestination(&noopPrint); for testing, try turning off 'all' debug output and see what leaks
|
||||
|
||||
/// Do late init that can't happen at constructor time
|
||||
void SerialConsole::init()
|
||||
{
|
||||
Port.begin(SERIAL_BAUD);
|
||||
StreamAPI::init();
|
||||
emitRebooted();
|
||||
}
|
||||
|
||||
@@ -26,14 +36,14 @@ void SerialConsole::init()
|
||||
* we override this to notice when we've received a protobuf over the serial
|
||||
* stream. Then we shunt off debug serial output.
|
||||
*/
|
||||
void SerialConsole::handleToRadio(const uint8_t *buf, size_t len)
|
||||
bool SerialConsole::handleToRadio(const uint8_t *buf, size_t len)
|
||||
{
|
||||
// Turn off debug serial printing once the API is activated, because other threads could print and corrupt packets
|
||||
if(!radioConfig.preferences.debug_log_enabled)
|
||||
if (!radioConfig.preferences.debug_log_enabled)
|
||||
setDestination(&noopPrint);
|
||||
canWrite = true;
|
||||
|
||||
StreamAPI::handleToRadio(buf, len);
|
||||
return StreamAPI::handleToRadio(buf, len);
|
||||
}
|
||||
|
||||
/// Hookable to find out when connection changes
|
||||
|
||||
@@ -11,14 +11,11 @@ class SerialConsole : public StreamAPI, public RedirectablePrint
|
||||
public:
|
||||
SerialConsole();
|
||||
|
||||
/// Do late init that can't happen at constructor time
|
||||
virtual void init();
|
||||
|
||||
/**
|
||||
* we override this to notice when we've received a protobuf over the serial stream. Then we shunt off
|
||||
* debug serial output.
|
||||
*/
|
||||
virtual void handleToRadio(const uint8_t *buf, size_t len);
|
||||
virtual bool handleToRadio(const uint8_t *buf, size_t len);
|
||||
|
||||
virtual size_t write(uint8_t c)
|
||||
{
|
||||
@@ -32,4 +29,8 @@ class SerialConsole : public StreamAPI, public RedirectablePrint
|
||||
virtual void onConnectionChanged(bool connected);
|
||||
};
|
||||
|
||||
extern SerialConsole console;
|
||||
// A simple wrapper to allow non class aware code write to the console
|
||||
void consolePrintf(const char *format, ...);
|
||||
void consoleInit();
|
||||
|
||||
extern SerialConsole *console;
|
||||
|
||||
107
src/airtime.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
#include "airtime.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
#define periodsToLog 48
|
||||
|
||||
AirTime *airTime;
|
||||
|
||||
uint32_t secondsPerPeriod = 3600;
|
||||
uint32_t lastMillis = 0;
|
||||
uint32_t secSinceBoot = 0;
|
||||
|
||||
// AirTime at;
|
||||
|
||||
// Don't read out of this directly. Use the helper functions.
|
||||
struct airtimeStruct {
|
||||
uint32_t periodTX[periodsToLog]; // AirTime transmitted
|
||||
uint32_t periodRX[periodsToLog]; // AirTime received and repeated (Only valid mesh packets)
|
||||
uint32_t periodRX_ALL[periodsToLog]; // AirTime received regardless of valid mesh packet. Could include noise.
|
||||
uint8_t lastPeriodIndex;
|
||||
} airtimes;
|
||||
|
||||
void AirTime::logAirtime(reportTypes reportType, uint32_t airtime_ms)
|
||||
{
|
||||
if (reportType == TX_LOG) {
|
||||
DEBUG_MSG("AirTime - Packet transmitted : %ums\n", airtime_ms);
|
||||
airtimes.periodTX[0] = airtimes.periodTX[0] + airtime_ms;
|
||||
} else if (reportType == RX_LOG) {
|
||||
DEBUG_MSG("AirTime - Packet received : %ums\n", airtime_ms);
|
||||
airtimes.periodRX[0] = airtimes.periodRX[0] + airtime_ms;
|
||||
} else if (reportType == RX_ALL_LOG) {
|
||||
DEBUG_MSG("AirTime - Packet received (noise?) : %ums\n", airtime_ms);
|
||||
airtimes.periodRX_ALL[0] = airtimes.periodRX_ALL[0] + airtime_ms;
|
||||
} else {
|
||||
DEBUG_MSG("AirTime - Unknown report time. This should never happen!!\n");
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t currentPeriodIndex()
|
||||
{
|
||||
return ((getSecondsSinceBoot() / secondsPerPeriod) % periodsToLog);
|
||||
}
|
||||
|
||||
void airtimeRotatePeriod()
|
||||
{
|
||||
|
||||
if (airtimes.lastPeriodIndex != currentPeriodIndex()) {
|
||||
DEBUG_MSG("Rotating airtimes to a new period = %u\n", currentPeriodIndex());
|
||||
|
||||
for (int i = periodsToLog - 2; i >= 0; --i) {
|
||||
airtimes.periodTX[i + 1] = airtimes.periodTX[i];
|
||||
airtimes.periodRX[i + 1] = airtimes.periodRX[i];
|
||||
airtimes.periodRX_ALL[i + 1] = airtimes.periodRX_ALL[i];
|
||||
}
|
||||
airtimes.periodTX[0] = 0;
|
||||
airtimes.periodRX[0] = 0;
|
||||
airtimes.periodRX_ALL[0] = 0;
|
||||
|
||||
airtimes.lastPeriodIndex = currentPeriodIndex();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t *airtimeReport(reportTypes reportType)
|
||||
{
|
||||
|
||||
if (reportType == TX_LOG) {
|
||||
return airtimes.periodTX;
|
||||
} else if (reportType == RX_LOG) {
|
||||
return airtimes.periodRX;
|
||||
} else if (reportType == RX_ALL_LOG) {
|
||||
return airtimes.periodRX_ALL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t getPeriodsToLog()
|
||||
{
|
||||
return periodsToLog;
|
||||
}
|
||||
|
||||
uint32_t getSecondsPerPeriod()
|
||||
{
|
||||
return secondsPerPeriod;
|
||||
}
|
||||
|
||||
uint32_t getSecondsSinceBoot()
|
||||
{
|
||||
return secSinceBoot;
|
||||
}
|
||||
|
||||
AirTime::AirTime() : concurrency::OSThread("AirTime") {}
|
||||
|
||||
int32_t AirTime::runOnce()
|
||||
{
|
||||
//DEBUG_MSG("AirTime::runOnce()\n");
|
||||
|
||||
airtimeRotatePeriod();
|
||||
secSinceBoot++;
|
||||
|
||||
/*
|
||||
This actually doesn't need to be run once per second but we currently use it for the
|
||||
secSinceBoot counter.
|
||||
|
||||
If we have a better counter of how long the device has been online (and not millis())
|
||||
then we can change this to something less frequent. Maybe once ever 5 seconds?
|
||||
*/
|
||||
return (1000 * 1);
|
||||
}
|
||||
53
src/airtime.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "configuration.h"
|
||||
#include <Arduino.h>
|
||||
#include <functional>
|
||||
|
||||
/*
|
||||
TX_LOG - Time on air this device has transmitted
|
||||
|
||||
RX_LOG - Time on air used by valid and routable mesh packets, does not include
|
||||
TX air time
|
||||
|
||||
RX_ALL_LOG - Time of all received lora packets. This includes packets that are not
|
||||
for meshtastic devices. Does not include TX air time.
|
||||
|
||||
Example analytics:
|
||||
|
||||
TX_LOG + RX_LOG = Total air time for a perticular meshtastic channel.
|
||||
|
||||
TX_LOG + RX_LOG = Total air time for a perticular meshtastic channel, including
|
||||
other lora radios.
|
||||
|
||||
RX_ALL_LOG - RX_LOG = Other lora radios on our frequency channel.
|
||||
*/
|
||||
enum reportTypes { TX_LOG, RX_LOG, RX_ALL_LOG };
|
||||
|
||||
void logAirtime(reportTypes reportType, uint32_t airtime_ms);
|
||||
|
||||
void airtimeRotatePeriod();
|
||||
|
||||
uint8_t currentPeriodIndex();
|
||||
uint8_t getPeriodsToLog();
|
||||
|
||||
uint32_t getSecondsSinceBoot();
|
||||
|
||||
uint32_t *airtimeReport(reportTypes reportType);
|
||||
|
||||
uint32_t getSecondsPerPeriod();
|
||||
|
||||
class AirTime : private concurrency::OSThread
|
||||
{
|
||||
|
||||
public:
|
||||
AirTime();
|
||||
|
||||
void logAirtime(reportTypes reportType, uint32_t airtime_ms);
|
||||
|
||||
protected:
|
||||
virtual int32_t runOnce() override;
|
||||
};
|
||||
|
||||
extern AirTime *airTime;
|
||||
66
src/buzz/buzz.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#include "buzz.h"
|
||||
#include "configuration.h"
|
||||
|
||||
#ifdef NRF52_SERIES
|
||||
#include "variant.h"
|
||||
#endif
|
||||
|
||||
#ifndef PIN_BUZZER
|
||||
|
||||
// Noop methods for boards w/o buzzer
|
||||
void playBeep(){};
|
||||
void playStartMelody(){};
|
||||
void playShutdownMelody(){};
|
||||
|
||||
#else
|
||||
#include "Tone.h"
|
||||
|
||||
extern "C" void delay(uint32_t dwMs);
|
||||
|
||||
struct ToneDuration {
|
||||
int frequency_khz;
|
||||
int duration_ms;
|
||||
};
|
||||
|
||||
// Some common frequencies.
|
||||
#define NOTE_C3 131
|
||||
#define NOTE_CS3 139
|
||||
#define NOTE_D3 147
|
||||
#define NOTE_DS3 156
|
||||
#define NOTE_E3 165
|
||||
#define NOTE_F3 175
|
||||
#define NOTE_FS3 185
|
||||
#define NOTE_G3 196
|
||||
#define NOTE_GS3 208
|
||||
#define NOTE_A3 220
|
||||
#define NOTE_AS3 233
|
||||
#define NOTE_B3 247
|
||||
|
||||
const int DURATION_1_8 = 125; // 1/8 note
|
||||
const int DURATION_1_4 = 250; // 1/4 note
|
||||
|
||||
void playTones(const ToneDuration *tone_durations, int size) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
const auto &tone_duration = tone_durations[i];
|
||||
tone(PIN_BUZZER, tone_duration.frequency_khz, tone_duration.duration_ms);
|
||||
// to distinguish the notes, set a minimum time between them.
|
||||
delay(1.3 * tone_duration.duration_ms);
|
||||
}
|
||||
}
|
||||
|
||||
void playBeep() { tone(PIN_BUZZER, NOTE_B3, DURATION_1_4); }
|
||||
|
||||
void playStartMelody() {
|
||||
ToneDuration melody[] = {{NOTE_B3, DURATION_1_4},
|
||||
{NOTE_B3, DURATION_1_8},
|
||||
{NOTE_B3, DURATION_1_8}};
|
||||
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
||||
}
|
||||
|
||||
void playShutdownMelody() {
|
||||
ToneDuration melody[] = {{NOTE_B3, DURATION_1_4},
|
||||
{NOTE_G3, DURATION_1_8},
|
||||
{NOTE_D3, DURATION_1_8}};
|
||||
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
||||
}
|
||||
#endif
|
||||
5
src/buzz/buzz.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
void playBeep();
|
||||
void playStartMelody();
|
||||
void playShutdownMelody();
|
||||
@@ -9,6 +9,7 @@ enum class Cmd {
|
||||
SET_OFF,
|
||||
ON_PRESS,
|
||||
START_BLUETOOTH_PIN_SCREEN,
|
||||
START_FIRMWARE_UPDATE_SCREEN,
|
||||
STOP_BLUETOOTH_PIN_SCREEN,
|
||||
STOP_BOOT_SCREEN,
|
||||
PRINT,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "concurrency/BinarySemaphoreFreeRTOS.h"
|
||||
#include "configuration.h"
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef HAS_FREE_RTOS
|
||||
|
||||
@@ -9,6 +10,7 @@ namespace concurrency
|
||||
BinarySemaphoreFreeRTOS::BinarySemaphoreFreeRTOS()
|
||||
{
|
||||
semaphore = xSemaphoreCreateBinary();
|
||||
assert(semaphore);
|
||||
}
|
||||
|
||||
BinarySemaphoreFreeRTOS::~BinarySemaphoreFreeRTOS()
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "configuration.h"
|
||||
#include "../freertosinc.h"
|
||||
|
||||
namespace concurrency
|
||||
@@ -28,4 +27,4 @@ class BinarySemaphoreFreeRTOS
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
} // namespace concurrency
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "NotifiedWorkerThread.h"
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
#include <assert.h>
|
||||
|
||||
namespace concurrency
|
||||
@@ -28,6 +29,7 @@ IRAM_ATTR bool NotifiedWorkerThread::notifyCommon(uint32_t v, bool overwrite)
|
||||
if (overwrite || notification == 0) {
|
||||
enabled = true;
|
||||
setInterval(0); // Run ASAP
|
||||
runASAP = true;
|
||||
|
||||
notification = v;
|
||||
if (debugNotification)
|
||||
|
||||
@@ -14,6 +14,8 @@ bool OSThread::showRun = false;
|
||||
/// Show debugging info for threads we decide not to run;
|
||||
bool OSThread::showWaiting = false;
|
||||
|
||||
const OSThread *OSThread::currentThread;
|
||||
|
||||
ThreadController mainController, timerController;
|
||||
InterruptableDelay mainDelay;
|
||||
|
||||
@@ -26,10 +28,14 @@ void OSThread::setup()
|
||||
OSThread::OSThread(const char *_name, uint32_t period, ThreadController *_controller)
|
||||
: Thread(NULL, period), controller(_controller)
|
||||
{
|
||||
assertIsSetup();
|
||||
|
||||
ThreadName = _name;
|
||||
|
||||
if (controller)
|
||||
controller->add(this);
|
||||
if (controller) {
|
||||
bool added = controller->add(this);
|
||||
assert(added);
|
||||
}
|
||||
}
|
||||
|
||||
OSThread::~OSThread()
|
||||
@@ -68,12 +74,47 @@ bool OSThread::shouldRun(unsigned long time)
|
||||
|
||||
void OSThread::run()
|
||||
{
|
||||
currentThread = this;
|
||||
auto newDelay = runOnce();
|
||||
|
||||
runned();
|
||||
|
||||
if (newDelay >= 0)
|
||||
setInterval(newDelay);
|
||||
|
||||
currentThread = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* This flag is set **only** when setup() starts, to provide a way for us to check for sloppy static constructor calls.
|
||||
* Call assertIsSetup() to force a crash if someone tries to create an instance too early.
|
||||
*
|
||||
* it is super important to never allocate those object statically. instead, you should explicitly
|
||||
* new them at a point where you are guaranteed that other objects that this instance
|
||||
* depends on have already been created.
|
||||
*
|
||||
* in particular, for OSThread that means "all instances must be declared via new() in setup() or later" -
|
||||
* this makes it guaranteed that the global mainController is fully constructed first.
|
||||
*/
|
||||
bool hasBeenSetup;
|
||||
|
||||
void assertIsSetup()
|
||||
{
|
||||
|
||||
/**
|
||||
* Dear developer comrade - If this assert fails() that means you need to fix the following:
|
||||
*
|
||||
* This flag is set **only** when setup() starts, to provide a way for us to check for sloppy static constructor calls.
|
||||
* Call assertIsSetup() to force a crash if someone tries to create an instance too early.
|
||||
*
|
||||
* it is super important to never allocate those object statically. instead, you should explicitly
|
||||
* new them at a point where you are guaranteed that other objects that this instance
|
||||
* depends on have already been created.
|
||||
*
|
||||
* in particular, for OSThread that means "all instances must be declared via new() in setup() or later" -
|
||||
* this makes it guaranteed that the global mainController is fully constructed first.
|
||||
*/
|
||||
assert(hasBeenSetup);
|
||||
}
|
||||
|
||||
} // namespace concurrency
|
||||
|
||||
@@ -17,14 +17,14 @@ extern InterruptableDelay mainDelay;
|
||||
|
||||
/**
|
||||
* @brief Base threading
|
||||
*
|
||||
*
|
||||
* This is a pseudo threading layer that is super easy to port, well suited to our slow network and very ram & power efficient.
|
||||
*
|
||||
* TODO FIXME @geeksville
|
||||
*
|
||||
* move more things into OSThreads
|
||||
* remove lock/lockguard
|
||||
*
|
||||
*
|
||||
* move typedQueue into concurrency
|
||||
* remove freertos from typedqueue
|
||||
*/
|
||||
@@ -42,6 +42,9 @@ class OSThread : public Thread
|
||||
static bool showWaiting;
|
||||
|
||||
public:
|
||||
/// For debug printing only (might be null)
|
||||
static const OSThread *currentThread;
|
||||
|
||||
OSThread(const char *name, uint32_t period = 0, ThreadController *controller = &mainController);
|
||||
|
||||
virtual ~OSThread();
|
||||
@@ -67,4 +70,19 @@ class OSThread : public Thread
|
||||
virtual void run();
|
||||
};
|
||||
|
||||
/**
|
||||
* This flag is set **only** when setup() starts, to provide a way for us to check for sloppy static constructor calls.
|
||||
* Call assertIsSetup() to force a crash if someone tries to create an instance too early.
|
||||
*
|
||||
* it is super important to never allocate those object statically. instead, you should explicitly
|
||||
* new them at a point where you are guaranteed that other objects that this instance
|
||||
* depends on have already been created.
|
||||
*
|
||||
* in particular, for OSThread that means "all instances must be declared via new() in setup() or later" -
|
||||
* this makes it guaranteed that the global mainController is fully constructed first.
|
||||
*/
|
||||
extern bool hasBeenSetup;
|
||||
|
||||
void assertIsSetup();
|
||||
|
||||
} // namespace concurrency
|
||||
|
||||
@@ -163,7 +163,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#if defined(TBEAM_V10)
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#define HW_VENDOR "tbeam"
|
||||
#define HW_VENDOR HardwareModel_TBEAM
|
||||
|
||||
// #define BUTTON_NEED_PULLUP // if set we need to turn on the internal CPU pullup during sleep
|
||||
|
||||
@@ -204,7 +204,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#elif defined(TBEAM_V07)
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#define HW_VENDOR "tbeam0.7"
|
||||
#define HW_VENDOR HardwareModel_TBEAM0p7
|
||||
|
||||
// #define BUTTON_NEED_PULLUP // if set we need to turn on the internal CPU pullup during sleep
|
||||
|
||||
@@ -228,7 +228,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#elif defined(ARDUINO_HELTEC_WIFI_LORA_32_V2)
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#define HW_VENDOR "heltec"
|
||||
#define HW_VENDOR HardwareModel_HELTEC
|
||||
|
||||
// the default ESP32 Pin of 15 is the Oled SCL, set to 36 and 37 and works fine.
|
||||
// Tested on Neo6m module.
|
||||
@@ -258,7 +258,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#elif defined(TLORA_V1)
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#define HW_VENDOR "tlora-v1"
|
||||
#define HW_VENDOR HardwareModel_TLORA_V1
|
||||
#undef GPS_RX_PIN
|
||||
#undef GPS_TX_PIN
|
||||
#define GPS_RX_PIN 36
|
||||
@@ -282,13 +282,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#elif defined(TLORA_V2)
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#define HW_VENDOR "tlora-v2"
|
||||
#define HW_VENDOR HardwareModel_TLORA_V2
|
||||
|
||||
#undef GPS_RX_PIN
|
||||
#undef GPS_TX_PIN
|
||||
#define GPS_RX_PIN 36
|
||||
#define GPS_TX_PIN 13 // per @eugene
|
||||
|
||||
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||
|
||||
#define I2C_SDA 21 // I2C pins for this board
|
||||
#define I2C_SCL 22
|
||||
|
||||
@@ -307,14 +309,41 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define LORA_DIO1 35 // Not really used
|
||||
#define LORA_DIO2 34 // Not really used
|
||||
|
||||
#elif defined(TLORA_V2_1_16)
|
||||
#elif defined(TLORA_V1_3)
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#define HW_VENDOR "tlora-v2-1-1.6"
|
||||
#define HW_VENDOR HardwareModel_TLORA_V1_1p3
|
||||
|
||||
#undef GPS_RX_PIN
|
||||
#undef GPS_TX_PIN
|
||||
#define GPS_RX_PIN 36
|
||||
#define GPS_TX_PIN 39
|
||||
#define GPS_TX_PIN 13 // per @eugene
|
||||
|
||||
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||
|
||||
#define I2C_SDA 4 // I2C pins for this board
|
||||
#define I2C_SCL 15
|
||||
|
||||
#define RESET_OLED 16 // If defined, this pin will be used to reset the display controller
|
||||
|
||||
#define VEXT_ENABLE 21 // active low, powers the oled display and the lora antenna boost
|
||||
#define LED_PIN 25 // If defined we will blink this LED
|
||||
#define BUTTON_PIN 36
|
||||
#define BUTTON_NEED_PULLUP
|
||||
|
||||
#define USE_RF95
|
||||
#define LORA_DIO0 26 // a No connect on the SX1262 module
|
||||
#define LORA_RESET 14
|
||||
#define LORA_DIO1 35 // Not really used
|
||||
#define LORA_DIO2 34 // Not really used
|
||||
|
||||
#elif defined(TLORA_V2_1_16)
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#define HW_VENDOR HardwareModel_TLORA_V2_1_1p6
|
||||
|
||||
#undef GPS_RX_PIN
|
||||
#undef GPS_TX_PIN
|
||||
#define GPS_RX_PIN 15 // per @der_bear on the forum, 36 is incorrect for this board type and 15 is a better pick
|
||||
#define GPS_TX_PIN 13
|
||||
|
||||
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||
|
||||
@@ -325,9 +354,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#define VEXT_ENABLE 21 // active low, powers the oled display and the lora antenna boost
|
||||
#define LED_PIN 25 // If defined we will blink this LED
|
||||
#define BUTTON_PIN \
|
||||
12 // If defined, this will be used for user button presses, if your board doesn't have a physical switch, you can wire one
|
||||
// between this pin and ground
|
||||
#define BUTTON_PIN 12 // If defined, this will be used for user button presses,
|
||||
|
||||
#define BUTTON_NEED_PULLUP
|
||||
|
||||
#define USE_RF95
|
||||
@@ -336,12 +364,42 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define LORA_DIO1 35 // Not really used
|
||||
#define LORA_DIO2 34 // Not really used
|
||||
|
||||
#elif defined(GENIEBLOCKS)
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#define HW_VENDOR HardwareModel_GENIEBLOCKS
|
||||
#undef GPS_RX_PIN
|
||||
#undef GPS_TX_PIN
|
||||
#define GPS_RX_PIN 5
|
||||
#define GPS_TX_PIN 18
|
||||
#define GPS_RESET_N 10
|
||||
#define GPS_EXTINT 23 // On MAX-M8 module pin name is EXTINT. On L70 module pin name is STANDBY.
|
||||
|
||||
#define BATTERY_PIN 39 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||
#define BATTERY_EN_PIN 14 // Voltage voltage divider enable pin connected to mosfet
|
||||
|
||||
#define I2C_SDA 4 // I2C pins for this board
|
||||
#define I2C_SCL 2
|
||||
|
||||
#define LED_PIN 12 // If defined we will blink this LED
|
||||
//#define BUTTON_PIN 36 // If defined, this will be used for user button presses (ToDo problem on that line on debug screen -->
|
||||
// Long press start!) #define BUTTON_NEED_PULLUP //GPIOs 34 to 39 are GPIs – input only pins. These pins don’t have internal
|
||||
// pull-ups or pull-down resistors.
|
||||
|
||||
#define USE_RF95
|
||||
#define LORA_DIO0 38 // a No connect on the SX1262 module
|
||||
#define LORA_RESET 9
|
||||
|
||||
#define RF95_SCK 22
|
||||
#define RF95_MISO 19
|
||||
#define RF95_MOSI 13
|
||||
#define RF95_NSS 21
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO_NRF52840_PCA10056
|
||||
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#define HW_VENDOR "nrf52dk"
|
||||
#define HW_VENDOR HardwareModel_NRF52840DK
|
||||
|
||||
// This board uses 0 to be mean LED on
|
||||
#undef LED_INVERTED
|
||||
@@ -349,21 +407,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#elif defined(ARDUINO_NRF52840_PPR)
|
||||
|
||||
#define HW_VENDOR "ppr"
|
||||
#define HW_VENDOR HardwareModel_PPR
|
||||
|
||||
#elif NRF52_SERIES
|
||||
|
||||
#define HW_VENDOR "nrf52unknown" // FIXME - unknown nrf52 board
|
||||
#define HW_VENDOR HardwareModel_NRF52_UNKNOWN
|
||||
|
||||
#elif PORTDUINO
|
||||
|
||||
#define HW_VENDOR "portduino"
|
||||
#define HW_VENDOR HardwareModel_PORTDUINO
|
||||
|
||||
#define USE_SIM_RADIO
|
||||
|
||||
#define USE_RF95
|
||||
#define LORA_DIO0 26 // a No connect on the SX1262 module
|
||||
#define LORA_RESET 23
|
||||
#define LORA_RESET RADIOLIB_NC
|
||||
#define LORA_DIO1 33 // Not really used
|
||||
#define LORA_DIO2 32 // Not really used
|
||||
|
||||
@@ -371,7 +429,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define RF95_SCK 5
|
||||
#define RF95_MISO 19
|
||||
#define RF95_MOSI 27
|
||||
#define RF95_NSS 18
|
||||
#define RF95_NSS RADIOLIB_NC // the ch341f spi controller does CS for us
|
||||
|
||||
#endif
|
||||
|
||||
@@ -399,7 +457,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "SerialConsole.h"
|
||||
|
||||
#define DEBUG_PORT console // Serial debug port
|
||||
#define DEBUG_PORT (*console) // Serial debug port
|
||||
|
||||
// What platforms should use SEGGER?
|
||||
#ifdef NRF52_SERIES
|
||||
@@ -428,7 +486,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define DEBUG_MSG(...) SEGGER_RTT_printf(0, __VA_ARGS__)
|
||||
#else
|
||||
#ifdef DEBUG_PORT
|
||||
#define DEBUG_MSG(...) DEBUG_PORT.printf(__VA_ARGS__)
|
||||
#define DEBUG_MSG(...) DEBUG_PORT.logDebug(__VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_MSG(...)
|
||||
#endif
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
/// Error codes for critical error
|
||||
enum CriticalErrorCode { NoError, ErrTxWatchdog, ErrSleepEnterWait, ErrNoRadio, ErrUnspecified, UBloxInitFailed };
|
||||
#include "mesh/generated/mesh.pb.h" // For CriticalErrorCode
|
||||
|
||||
/// Record an error that should be reported via analytics
|
||||
void recordCriticalError(CriticalErrorCode code, uint32_t address = 0);
|
||||
void recordCriticalError(CriticalErrorCode code = CriticalErrorCode_Unspecified, uint32_t address = 0);
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "../concurrency/LockGuard.h"
|
||||
#include "../graphics/Screen.h"
|
||||
#include "../main.h"
|
||||
#include "BluetoothSoftwareUpdate.h"
|
||||
#include "NodeDB.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "RadioLibInterface.h"
|
||||
#include "configuration.h"
|
||||
#include "nimble/BluetoothUtil.h"
|
||||
#include "NodeDB.h"
|
||||
|
||||
#include <CRC32.h>
|
||||
#include <Update.h>
|
||||
@@ -14,7 +16,6 @@
|
||||
int16_t updateResultHandle = -1;
|
||||
|
||||
static CRC32 crc;
|
||||
static uint32_t rebootAtMsec = 0; // If not zero we will reboot at this time (used to reboot shortly after the update completes)
|
||||
|
||||
static uint32_t updateExpectedSize, updateActualSize;
|
||||
static uint8_t update_result;
|
||||
@@ -47,9 +48,10 @@ int update_size_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_
|
||||
// void stopMeshBluetoothService();
|
||||
// stopMeshBluetoothService();
|
||||
|
||||
screen->startFirmwareUpdateScreen();
|
||||
if (RadioLibInterface::instance)
|
||||
RadioLibInterface::instance->sleep(); // FIXME, nasty hack - the RF95 ISR/SPI code on ESP32 can fail while we are
|
||||
// writing flash - shut the radio off during updates
|
||||
RadioLibInterface::instance->disable(); // FIXME, nasty hack - the RF95 ISR/SPI code on ESP32 can fail while we
|
||||
// are writing flash - shut the radio off during updates
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +77,7 @@ int update_data_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_
|
||||
crc.update(data, len);
|
||||
Update.write(data, len);
|
||||
updateActualSize += len;
|
||||
powerFSM.trigger(EVENT_CONTACT_FROM_PHONE);
|
||||
powerFSM.trigger(EVENT_FIRMWARE_UPDATE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -104,8 +106,7 @@ int update_crc32_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble
|
||||
if (update_region == U_SPIFFS) {
|
||||
DEBUG_MSG("SPIFFS updated!\n");
|
||||
nodeDB.saveToDisk(); // Since we just wiped spiffs, we need to save our current state
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
DEBUG_MSG("Appload updated, rebooting in 5 seconds!\n");
|
||||
rebootAtMsec = millis() + 5000;
|
||||
}
|
||||
@@ -137,14 +138,6 @@ int update_region_callback(uint16_t conn_handle, uint16_t attr_handle, struct bl
|
||||
return chr_readwrite8(&update_region, sizeof(update_region), ctxt);
|
||||
}
|
||||
|
||||
void bluetoothRebootCheck()
|
||||
{
|
||||
if (rebootAtMsec && millis() > rebootAtMsec) {
|
||||
DEBUG_MSG("Rebooting for update\n");
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
See bluetooth-api.md
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
|
||||
void reinitUpdateService();
|
||||
|
||||
void bluetoothRebootCheck();
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
@@ -18,9 +18,6 @@ class ESP32CryptoEngine : public CryptoEngine
|
||||
|
||||
mbedtls_aes_context aes;
|
||||
|
||||
/// How many bytes in our key
|
||||
uint8_t keySize = 0;
|
||||
|
||||
public:
|
||||
ESP32CryptoEngine() { mbedtls_aes_init(&aes); }
|
||||
|
||||
@@ -35,12 +32,12 @@ class ESP32CryptoEngine : public CryptoEngine
|
||||
* @param bytes a _static_ buffer that will remain valid for the life of this crypto instance (i.e. this class will cache the
|
||||
* provided pointer)
|
||||
*/
|
||||
virtual void setKey(size_t numBytes, uint8_t *bytes)
|
||||
virtual void setKey(const CryptoKey &k)
|
||||
{
|
||||
keySize = numBytes;
|
||||
DEBUG_MSG("Installing AES%d key!\n", numBytes * 8);
|
||||
if (numBytes != 0) {
|
||||
auto res = mbedtls_aes_setkey_enc(&aes, bytes, numBytes * 8);
|
||||
CryptoEngine::setKey(k);
|
||||
|
||||
if (key.length != 0) {
|
||||
auto res = mbedtls_aes_setkey_enc(&aes, key.bytes, key.length * 8);
|
||||
assert(!res);
|
||||
}
|
||||
}
|
||||
@@ -52,12 +49,12 @@ class ESP32CryptoEngine : public CryptoEngine
|
||||
*/
|
||||
virtual void encrypt(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes)
|
||||
{
|
||||
if (keySize != 0) {
|
||||
if (key.length > 0) {
|
||||
uint8_t stream_block[16];
|
||||
static uint8_t scratch[MAX_BLOCKSIZE];
|
||||
size_t nc_off = 0;
|
||||
|
||||
// DEBUG_MSG("ESP32 encrypt!\n");
|
||||
// DEBUG_MSG("ESP32 crypt fr=%x, num=%x, numBytes=%d!\n", fromNode, (uint32_t) packetNum, numBytes);
|
||||
initNonce(fromNode, packetNum);
|
||||
assert(numBytes <= MAX_BLOCKSIZE);
|
||||
memcpy(scratch, bytes, numBytes);
|
||||
|
||||
@@ -7,9 +7,10 @@
|
||||
#include "sleep.h"
|
||||
#include "target_specific.h"
|
||||
#include "utils.h"
|
||||
#include <Preferences.h>
|
||||
#include <driver/rtc_io.h>
|
||||
#include <nvs.h>
|
||||
#include <nvs_flash.h>
|
||||
#include <driver/rtc_io.h>
|
||||
|
||||
void getMacAddr(uint8_t *dmac)
|
||||
{
|
||||
@@ -45,11 +46,24 @@ void esp32Setup()
|
||||
DEBUG_MSG("NVS: UsedEntries %d, FreeEntries %d, AllEntries %d\n", nvs_stats.used_entries, nvs_stats.free_entries,
|
||||
nvs_stats.total_entries);
|
||||
|
||||
DEBUG_MSG("Setup Preferences in Flash Storage\n");
|
||||
|
||||
// Create object to store our persistant data
|
||||
Preferences preferences;
|
||||
preferences.begin("meshtastic", false);
|
||||
|
||||
uint32_t rebootCounter = preferences.getUInt("rebootCounter", 0);
|
||||
rebootCounter++;
|
||||
preferences.putUInt("rebootCounter", rebootCounter);
|
||||
preferences.end();
|
||||
DEBUG_MSG("Number of Device Reboots: %d\n", rebootCounter);
|
||||
|
||||
// enableModemSleep();
|
||||
|
||||
// Since we are turning on watchdogs rather late in the release schedule, we really don't want to catch any
|
||||
// false positives. The wait-to-sleep timeout for shutting down radios is 30 secs, so pick 45 for now.
|
||||
#define APP_WATCHDOG_SECS 45
|
||||
// #define APP_WATCHDOG_SECS 45
|
||||
#define APP_WATCHDOG_SECS 90
|
||||
|
||||
res = esp_task_wdt_init(APP_WATCHDOG_SECS, true);
|
||||
assert(res == ESP_OK);
|
||||
@@ -83,7 +97,6 @@ void esp32Loop()
|
||||
{
|
||||
esp_task_wdt_reset(); // service our app level watchdog
|
||||
loopBLE();
|
||||
bluetoothRebootCheck();
|
||||
|
||||
// for debug printing
|
||||
// radio.radioIf.canSleep();
|
||||
|
||||
@@ -25,7 +25,7 @@ uint8_t GPS::i2cAddress = 0;
|
||||
|
||||
GPS *gps;
|
||||
|
||||
/// Multiple GPS instances might use the same serial port (in sequence), but we can
|
||||
/// Multiple GPS instances might use the same serial port (in sequence), but we can
|
||||
/// only init that port once.
|
||||
static bool didSerialInit;
|
||||
|
||||
@@ -33,7 +33,7 @@ bool GPS::setupGPS()
|
||||
{
|
||||
if (_serial_gps && !didSerialInit) {
|
||||
didSerialInit = true;
|
||||
|
||||
|
||||
#ifdef GPS_RX_PIN
|
||||
_serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
|
||||
#else
|
||||
@@ -73,6 +73,13 @@ bool GPS::setup()
|
||||
return ok;
|
||||
}
|
||||
|
||||
GPS::~GPS()
|
||||
{
|
||||
// we really should unregister our sleep observer
|
||||
notifySleepObserver.unobserve();
|
||||
notifyDeepSleepObserver.unobserve();
|
||||
}
|
||||
|
||||
// Allow defining the polarity of the WAKE output. default is active high
|
||||
#ifndef GPS_WAKE_ACTIVE
|
||||
#define GPS_WAKE_ACTIVE 1
|
||||
@@ -86,8 +93,8 @@ void GPS::wake()
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void GPS::sleep() {
|
||||
void GPS::sleep()
|
||||
{
|
||||
#ifdef PIN_GPS_WAKE
|
||||
digitalWrite(PIN_GPS_WAKE, GPS_WAKE_ACTIVE ? 0 : 1);
|
||||
pinMode(PIN_GPS_WAKE, OUTPUT);
|
||||
@@ -158,7 +165,8 @@ uint32_t GPS::getWakeTime() const
|
||||
return t; // already maxint
|
||||
|
||||
if (t == 0)
|
||||
t = 15 * 60; // Allow up to 5 mins for each attempt (probably will be much less if we can find sats)
|
||||
t = radioConfig.preferences.is_router ? 5 * 60 : 15 * 60; // Allow up to 15 mins for each attempt (probably will be much
|
||||
// less if we can find sats) or less if a router
|
||||
|
||||
t *= 1000; // msecs
|
||||
|
||||
@@ -179,8 +187,8 @@ uint32_t GPS::getSleepTime() const
|
||||
if (t == UINT32_MAX)
|
||||
return t; // already maxint
|
||||
|
||||
if (t == 0)
|
||||
t = 2 * 60; // 2 mins
|
||||
if (t == 0) // default - unset in preferences
|
||||
t = radioConfig.preferences.is_router ? 24 * 60 * 60 : 2 * 60; // 2 mins or once per day for routers
|
||||
|
||||
t *= 1000;
|
||||
|
||||
@@ -239,6 +247,7 @@ int32_t GPS::runOnce()
|
||||
}
|
||||
|
||||
// We've been awake too long - force sleep
|
||||
now = millis();
|
||||
auto wakeTime = getWakeTime();
|
||||
bool tooLong = wakeTime != UINT32_MAX && (now - lastWakeStartMsec) > wakeTime;
|
||||
|
||||
@@ -299,3 +308,49 @@ int GPS::prepareDeepSleep(void *unused)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef GPS_TX_PIN
|
||||
#include "UBloxGPS.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAS_AIR530_GPS
|
||||
#include "Air530GPS.h"
|
||||
#elif !defined(NO_GPS)
|
||||
#include "NMEAGPS.h"
|
||||
#endif
|
||||
|
||||
|
||||
GPS* createGps() {
|
||||
|
||||
#ifdef NO_GPS
|
||||
return nullptr;
|
||||
#else
|
||||
// If we don't have bidirectional comms, we can't even try talking to UBLOX
|
||||
#ifdef GPS_TX_PIN
|
||||
// Init GPS - first try ublox
|
||||
UBloxGPS *ublox = new UBloxGPS();
|
||||
|
||||
if (!ublox->setup()) {
|
||||
DEBUG_MSG("ERROR: No UBLOX GPS found\n");
|
||||
delete ublox;
|
||||
ublox = NULL;
|
||||
} else {
|
||||
return ublox;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (GPS::_serial_gps) {
|
||||
// Some boards might have only the TX line from the GPS connected, in that case, we can't configure it at all. Just
|
||||
// assume NMEA at 9600 baud.
|
||||
DEBUG_MSG("Hoping that NMEA might work\n");
|
||||
#ifdef HAS_AIR530_GPS
|
||||
GPS* new_gps = new Air530GPS();
|
||||
#else
|
||||
GPS* new_gps = new NMEAGPS();
|
||||
#endif
|
||||
new_gps->setup();
|
||||
return new_gps;
|
||||
}
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
@@ -47,7 +47,7 @@ class GPS : private concurrency::OSThread
|
||||
|
||||
GPS() : concurrency::OSThread("GPS") {}
|
||||
|
||||
virtual ~GPS() {} // FIXME, we really should unregister our sleep observer
|
||||
virtual ~GPS();
|
||||
|
||||
/** We will notify this observable anytime GPS state has changed meaningfully */
|
||||
Observable<const meshtastic::GPSStatus *> newStatus;
|
||||
@@ -71,6 +71,9 @@ class GPS : private concurrency::OSThread
|
||||
* */
|
||||
void forceWake(bool on);
|
||||
|
||||
// Some GPS modules (ublock) require factory reset
|
||||
virtual bool factoryReset() { return true; }
|
||||
|
||||
protected:
|
||||
/// Do gps chipset specific init, return true for success
|
||||
virtual bool setupGPS();
|
||||
@@ -145,4 +148,8 @@ class GPS : private concurrency::OSThread
|
||||
virtual int32_t runOnce();
|
||||
};
|
||||
|
||||
// Creates an instance of the GPS class.
|
||||
// Returns the new instance or null if the GPS is not present.
|
||||
GPS* createGps();
|
||||
|
||||
extern GPS *gps;
|
||||
|
||||
@@ -47,6 +47,8 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
|
||||
t.tm_mon = d.month() - 1;
|
||||
t.tm_year = d.year() - 1900;
|
||||
t.tm_isdst = false;
|
||||
DEBUG_MSG("NMEA GPS time %d\n", t.tm_sec);
|
||||
|
||||
perhapsSetRTC(RTCQualityGPS, t);
|
||||
|
||||
return true;
|
||||
@@ -87,11 +89,17 @@ bool NMEAGPS::lookForLocation()
|
||||
auto loc = reader.location.value();
|
||||
latitude = toDegInt(loc.lat);
|
||||
longitude = toDegInt(loc.lng);
|
||||
foundLocation = true;
|
||||
|
||||
// expect gps pos lat=37.520825, lon=-122.309162, alt=158
|
||||
DEBUG_MSG("new NMEA GPS pos lat=%f, lon=%f, alt=%d, hdop=%g, heading=%f\n", latitude * 1e-7, longitude * 1e-7, altitude,
|
||||
dop * 1e-2, heading * 1e-5);
|
||||
// Some GPSes (Air530) seem to send a zero longitude when the current fix is bogus
|
||||
if(longitude == 0)
|
||||
DEBUG_MSG("Ignoring bogus NMEA position\n");
|
||||
else {
|
||||
foundLocation = true;
|
||||
|
||||
// expect gps pos lat=37.520825, lon=-122.309162, alt=158
|
||||
DEBUG_MSG("new NMEA GPS pos lat=%f, lon=%f, alt=%d, hdop=%g, heading=%f\n", latitude * 1e-7, longitude * 1e-7, altitude,
|
||||
dop * 1e-2, heading * 1e-5);
|
||||
}
|
||||
}
|
||||
|
||||
return foundLocation;
|
||||
|
||||
@@ -31,15 +31,31 @@ void readFromRTC()
|
||||
/// If we haven't yet set our RTC this boot, set it from a GPS derived time
|
||||
bool perhapsSetRTC(RTCQuality q, const struct timeval *tv)
|
||||
{
|
||||
static uint32_t lastSetMsec = 0;
|
||||
uint32_t now = millis();
|
||||
|
||||
bool shouldSet;
|
||||
if (q > currentQuality) {
|
||||
currentQuality = q;
|
||||
DEBUG_MSG("Setting RTC %ld secs\n", tv->tv_sec);
|
||||
shouldSet = true;
|
||||
DEBUG_MSG("Upgrading time to RTC %ld secs (quality %d)\n", tv->tv_sec, q);
|
||||
} else if(q == RTCQualityGPS && (now - lastSetMsec) > (12 * 60 * 60 * 1000L)) {
|
||||
// Every 12 hrs we will slam in a new GPS time, to correct for local RTC clock drift
|
||||
shouldSet = true;
|
||||
DEBUG_MSG("Reapplying external time to correct clock drift %ld secs\n", tv->tv_sec);
|
||||
}
|
||||
else
|
||||
shouldSet = false;
|
||||
|
||||
if (shouldSet) {
|
||||
lastSetMsec = now;
|
||||
#ifndef NO_ESP32
|
||||
settimeofday(tv, NULL);
|
||||
#else
|
||||
DEBUG_MSG("ERROR TIME SETTING NOT IMPLEMENTED!\n");
|
||||
#endif
|
||||
readFromRTC();
|
||||
#else
|
||||
timeStartMsec = now;
|
||||
zeroOffsetSecs = tv->tv_sec;
|
||||
#endif
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
||||
@@ -27,4 +27,8 @@ uint32_t getTime();
|
||||
/// Return time since 1970 in secs. If quality is RTCQualityNone return zero
|
||||
uint32_t getValidTime(RTCQuality minQuality);
|
||||
|
||||
void readFromRTC();
|
||||
void readFromRTC();
|
||||
|
||||
#define SEC_PER_DAY 86400
|
||||
#define SEC_PER_HOUR 3600
|
||||
#define SEC_PER_MIN 60
|
||||
@@ -43,7 +43,7 @@ bool UBloxGPS::setupGPS()
|
||||
DEBUG_MSG("Connected to UBLOX GPS successfully\n");
|
||||
|
||||
if (!setUBXMode())
|
||||
recordCriticalError(UBloxInitFailed); // Don't halt the boot if saving the config fails, but do report the bug
|
||||
recordCriticalError(CriticalErrorCode_UBloxInitFailed); // Don't halt the boot if saving the config fails, but do report the bug
|
||||
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "EInkDisplay.h"
|
||||
#include "SPILock.h"
|
||||
#include "epd1in54.h" // Screen specific library
|
||||
#include "graphics/configs.h"
|
||||
#include <SPI.h>
|
||||
#include <TFT_eSPI.h> // Graphics library and Sprite class
|
||||
|
||||
@@ -123,7 +122,8 @@ bool EInkDisplay::connect()
|
||||
#endif
|
||||
|
||||
#ifdef PIN_EINK_EN
|
||||
digitalWrite(PIN_EINK_EN, HIGH);
|
||||
// backlight power, HIGH is backlight on, LOW is off
|
||||
digitalWrite(PIN_EINK_EN, LOW);
|
||||
pinMode(PIN_EINK_EN, OUTPUT);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -26,21 +26,35 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "Screen.h"
|
||||
#include "configs.h"
|
||||
#include "configuration.h"
|
||||
#include "fonts.h"
|
||||
#include "gps/RTC.h"
|
||||
#include "graphics/images.h"
|
||||
#include "main.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include "meshwifi/meshwifi.h"
|
||||
#include "mesh/Channels.h"
|
||||
#include "plugins/TextMessagePlugin.h"
|
||||
#include "target_specific.h"
|
||||
#include "utils.h"
|
||||
|
||||
#ifndef NO_ESP32
|
||||
#include "mesh/http/WiFiAPClient.h"
|
||||
#endif
|
||||
|
||||
using namespace meshtastic; /** @todo remove */
|
||||
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
// This means the *visible* area (sh1106 can address 132, but shows 128 for example)
|
||||
#define IDLE_FRAMERATE 1 // in fps
|
||||
#define COMPASS_DIAM 44
|
||||
|
||||
// DEBUG
|
||||
#define NUM_EXTRA_FRAMES 3 // text message and debug frame
|
||||
// if defined a pixel will blink to show redraws
|
||||
// #define SHOW_REDRAWS
|
||||
|
||||
// A text message frame + debug frame + all the node infos
|
||||
static FrameCallback normalFrames[MAX_NUM_NODES + NUM_EXTRA_FRAMES];
|
||||
static uint32_t targetFramerate = IDLE_FRAMERATE;
|
||||
@@ -52,6 +66,10 @@ uint8_t imgBattery[16] = {0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
|
||||
// Threshold values for the GPS lock accuracy bar display
|
||||
uint32_t dopThresholds[5] = {2000, 1000, 500, 200, 100};
|
||||
|
||||
// At some point, we're going to ask all of the plugins if they would like to display a screen frame
|
||||
// we'll need to hold onto pointers for the plugins that can draw a frame.
|
||||
std::vector<MeshPlugin *> pluginFrames;
|
||||
|
||||
// Stores the last 4 of our hardware ID, to make finding the device for pairing easier
|
||||
static char ourId[5];
|
||||
|
||||
@@ -126,11 +144,35 @@ static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int1
|
||||
drawIconScreen(region, display, state, x, y);
|
||||
}
|
||||
|
||||
#ifdef HAS_EINK
|
||||
/// Used on eink displays while in deep sleep
|
||||
static void drawSleepScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
drawIconScreen("Sleeping...", display, state, x, y);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void drawPluginFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
uint8_t plugin_frame;
|
||||
// there's a little but in the UI transition code
|
||||
// where it invokes the function at the correct offset
|
||||
// in the array of "drawScreen" functions; however,
|
||||
// the passed-state doesn't quite reflect the "current"
|
||||
// screen, so we have to detect it.
|
||||
if (state->frameState == IN_TRANSITION && state->transitionFrameRelationship == INCOMING) {
|
||||
// if we're transitioning from the end of the frame list back around to the first
|
||||
// frame, then we want this to be `0`
|
||||
plugin_frame = state->transitionFrameTarget;
|
||||
} else {
|
||||
// otherwise, just display the plugin frame that's aligned with the current frame
|
||||
plugin_frame = state->currentFrame;
|
||||
// DEBUG_MSG("Screen is not in transition. Frame: %d\n\n", plugin_frame);
|
||||
}
|
||||
// DEBUG_MSG("Drawing Plugin Frame %d\n\n", plugin_frame);
|
||||
MeshPlugin &pi = *pluginFrames.at(plugin_frame);
|
||||
pi.drawFrame(display, state, x, y);
|
||||
}
|
||||
|
||||
static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
@@ -152,13 +194,42 @@ static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state,
|
||||
display->drawString(64 + x, 48 + y, buf);
|
||||
}
|
||||
|
||||
static void drawFrameFirmware(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->setFont(FONT_MEDIUM);
|
||||
display->drawString(64 + x, y, "Updating");
|
||||
|
||||
display->setFont(FONT_SMALL);
|
||||
display->drawString(64 + x, FONT_HEIGHT_SMALL + y + 2, "Please wait...");
|
||||
|
||||
// display->setFont(FONT_LARGE);
|
||||
// display->drawString(64 + x, 26 + y, btPIN);
|
||||
}
|
||||
|
||||
/// Draw the last text message we received
|
||||
static void drawCriticalFaultFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
displayedNodeNum = 0; // Not currently showing a node pane
|
||||
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display->setFont(FONT_MEDIUM);
|
||||
|
||||
char tempBuf[24];
|
||||
snprintf(tempBuf, sizeof(tempBuf), "Critical fault #%d", myNodeInfo.error_code);
|
||||
display->drawString(0 + x, 0 + y, tempBuf);
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display->setFont(FONT_SMALL);
|
||||
display->drawString(0 + x, FONT_HEIGHT_MEDIUM + y, "For help, please post on\nmeshtastic.discourse.group");
|
||||
}
|
||||
|
||||
/// Draw the last text message we received
|
||||
static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
displayedNodeNum = 0; // Not currently showing a node pane
|
||||
|
||||
MeshPacket &mp = devicestate.rx_text_message;
|
||||
NodeInfo *node = nodeDB.getNode(mp.from);
|
||||
NodeInfo *node = nodeDB.getNode(getFrom(&mp));
|
||||
// DEBUG_MSG("drawing text message from 0x%x: %s\n", mp.from,
|
||||
// mp.decoded.variant.data.decoded.bytes);
|
||||
|
||||
@@ -173,8 +244,7 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
|
||||
|
||||
// the max length of this buffer is much longer than we can possibly print
|
||||
static char tempBuf[96];
|
||||
assert(mp.decoded.which_payload == SubPacket_data_tag);
|
||||
snprintf(tempBuf, sizeof(tempBuf), " %s", mp.decoded.data.payload.bytes);
|
||||
snprintf(tempBuf, sizeof(tempBuf), " %s", mp.decoded.payload.bytes);
|
||||
|
||||
display->drawStringMaxWidth(4 + x, 10 + y, SCREEN_WIDTH - (6 + x), tempBuf);
|
||||
}
|
||||
@@ -290,7 +360,7 @@ static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus
|
||||
display->drawFastImage(x + 24, y, 8, 8, imgSatellite);
|
||||
|
||||
// Draw the number of satellites
|
||||
sprintf(satsString, "%lu", gps->getNumSatellites());
|
||||
sprintf(satsString, "%u", gps->getNumSatellites());
|
||||
display->drawString(x + 34, y - 2, satsString);
|
||||
}
|
||||
}
|
||||
@@ -543,11 +613,11 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
||||
uint32_t agoSecs = sinceLastSeen(node);
|
||||
static char lastStr[20];
|
||||
if (agoSecs < 120) // last 2 mins?
|
||||
snprintf(lastStr, sizeof(lastStr), "%lu seconds ago", agoSecs);
|
||||
snprintf(lastStr, sizeof(lastStr), "%u seconds ago", agoSecs);
|
||||
else if (agoSecs < 120 * 60) // last 2 hrs
|
||||
snprintf(lastStr, sizeof(lastStr), "%lu minutes ago", agoSecs / 60);
|
||||
snprintf(lastStr, sizeof(lastStr), "%u minutes ago", agoSecs / 60);
|
||||
else
|
||||
snprintf(lastStr, sizeof(lastStr), "%lu hours ago", agoSecs / 60 / 60);
|
||||
snprintf(lastStr, sizeof(lastStr), "%u hours ago", agoSecs / 60 / 60);
|
||||
|
||||
static char distStr[20];
|
||||
strcpy(distStr, "? km"); // might not have location data
|
||||
@@ -647,6 +717,7 @@ void Screen::handleSetOn(bool on)
|
||||
dispdev.displayOn();
|
||||
enabled = true;
|
||||
setInterval(0); // Draw ASAP
|
||||
runASAP = true;
|
||||
} else {
|
||||
DEBUG_MSG("Turning off screen\n");
|
||||
dispdev.displayOff();
|
||||
@@ -721,7 +792,8 @@ void Screen::setup()
|
||||
powerStatusObserver.observe(&powerStatus->onNewStatus);
|
||||
gpsStatusObserver.observe(&gpsStatus->onNewStatus);
|
||||
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
|
||||
textMessageObserver.observe(&textMessagePlugin);
|
||||
if (textMessagePlugin)
|
||||
textMessageObserver.observe(textMessagePlugin);
|
||||
}
|
||||
|
||||
void Screen::forceDisplay()
|
||||
@@ -748,9 +820,6 @@ int32_t Screen::runOnce()
|
||||
showingBootScreen = false;
|
||||
}
|
||||
|
||||
// Update the screen last, after we've figured out what to show.
|
||||
debug_info()->setChannelNameStatus(getChannelName());
|
||||
|
||||
// Process incoming commands.
|
||||
for (;;) {
|
||||
ScreenCmd cmd;
|
||||
@@ -770,6 +839,9 @@ int32_t Screen::runOnce()
|
||||
case Cmd::START_BLUETOOTH_PIN_SCREEN:
|
||||
handleStartBluetoothPinScreen(cmd.bluetooth_pin);
|
||||
break;
|
||||
case Cmd::START_FIRMWARE_UPDATE_SCREEN:
|
||||
handleStartFirmwareUpdateScreen();
|
||||
break;
|
||||
case Cmd::STOP_BLUETOOTH_PIN_SCREEN:
|
||||
case Cmd::STOP_BOOT_SCREEN:
|
||||
setFrames();
|
||||
@@ -779,7 +851,7 @@ int32_t Screen::runOnce()
|
||||
free(cmd.print_text);
|
||||
break;
|
||||
default:
|
||||
DEBUG_MSG("BUG: invalid cmd");
|
||||
DEBUG_MSG("BUG: invalid cmd\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -842,6 +914,11 @@ void Screen::setFrames()
|
||||
DEBUG_MSG("showing standard frames\n");
|
||||
showingNormalScreen = true;
|
||||
|
||||
pluginFrames = MeshPlugin::GetMeshPluginsWithUIFrames();
|
||||
DEBUG_MSG("Showing %d plugin frames\n", pluginFrames.size());
|
||||
int totalFrameCount = MAX_NUM_NODES + NUM_EXTRA_FRAMES + pluginFrames.size();
|
||||
DEBUG_MSG("Total frame count: %d\n", totalFrameCount);
|
||||
|
||||
// We don't show the node info our our node (if we have it yet - we should)
|
||||
size_t numnodes = nodeStatus->getNumTotal();
|
||||
if (numnodes > 0)
|
||||
@@ -849,7 +926,23 @@ void Screen::setFrames()
|
||||
|
||||
size_t numframes = 0;
|
||||
|
||||
// If we have a text message - show it first
|
||||
// put all of the plugin frames first.
|
||||
// this is a little bit of a dirty hack; since we're going to call
|
||||
// the same drawPluginFrame handler here for all of these plugin frames
|
||||
// and then we'll just assume that the state->currentFrame value
|
||||
// is the same offset into the pluginFrames vector
|
||||
// so that we can invoke the plugin's callback
|
||||
for (auto i = pluginFrames.begin(); i != pluginFrames.end(); ++i) {
|
||||
normalFrames[numframes++] = drawPluginFrame;
|
||||
}
|
||||
|
||||
DEBUG_MSG("Added plugins. numframes: %d\n", numframes);
|
||||
|
||||
// If we have a critical fault, show it first
|
||||
if (myNodeInfo.error_code)
|
||||
normalFrames[numframes++] = drawCriticalFaultFrame;
|
||||
|
||||
// If we have a text message - show it next
|
||||
if (devicestate.has_rx_text_message)
|
||||
normalFrames[numframes++] = drawTextMessageFrame;
|
||||
|
||||
@@ -866,10 +959,14 @@ void Screen::setFrames()
|
||||
// call a method on debugInfoScreen object (for more details)
|
||||
normalFrames[numframes++] = &Screen::drawDebugInfoSettingsTrampoline;
|
||||
|
||||
#ifndef NO_ESP32
|
||||
if (isWifiAvailable()) {
|
||||
// call a method on debugInfoScreen object (for more details)
|
||||
normalFrames[numframes++] = &Screen::drawDebugInfoWiFiTrampoline;
|
||||
}
|
||||
#endif
|
||||
|
||||
DEBUG_MSG("Finished building frames. numframes: %d\n", numframes);
|
||||
|
||||
ui.setFrames(normalFrames, numframes);
|
||||
ui.enableAllIndicators();
|
||||
@@ -894,9 +991,40 @@ void Screen::handleStartBluetoothPinScreen(uint32_t pin)
|
||||
setFastFramerate();
|
||||
}
|
||||
|
||||
void Screen::handleStartFirmwareUpdateScreen()
|
||||
{
|
||||
DEBUG_MSG("showing firmware screen\n");
|
||||
showingNormalScreen = false;
|
||||
|
||||
static FrameCallback btFrames[] = {drawFrameFirmware};
|
||||
|
||||
ui.disableAllIndicators();
|
||||
ui.setFrames(btFrames, 1);
|
||||
setFastFramerate();
|
||||
}
|
||||
|
||||
void Screen::blink()
|
||||
{
|
||||
setFastFramerate();
|
||||
uint8_t count = 10;
|
||||
dispdev.setBrightness(254);
|
||||
while (count > 0) {
|
||||
dispdev.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
dispdev.display();
|
||||
delay(50);
|
||||
dispdev.clear();
|
||||
dispdev.display();
|
||||
delay(50);
|
||||
count = count - 1;
|
||||
}
|
||||
dispdev.setBrightness(brightness);
|
||||
}
|
||||
|
||||
void Screen::handlePrint(const char *text)
|
||||
{
|
||||
DEBUG_MSG("Screen: %s", text);
|
||||
// the string passed into us probably has a newline, but that would confuse the logging system
|
||||
// so strip it
|
||||
DEBUG_MSG("Screen: %.*s\n", strlen(text) - 1, text);
|
||||
if (!useDisplay || !showingNormalScreen)
|
||||
return;
|
||||
|
||||
@@ -926,6 +1054,7 @@ void Screen::setFastFramerate()
|
||||
targetFramerate = SCREEN_TRANSITION_FRAMERATE;
|
||||
ui.setTargetFPS(targetFramerate);
|
||||
setInterval(0); // redraw ASAP
|
||||
runASAP = true;
|
||||
}
|
||||
|
||||
void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
@@ -940,7 +1069,8 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
||||
char channelStr[20];
|
||||
{
|
||||
concurrency::LockGuard guard(&lock);
|
||||
snprintf(channelStr, sizeof(channelStr), "%s", channelName.c_str());
|
||||
auto chName = channels.getPrimaryName();
|
||||
snprintf(channelStr, sizeof(channelStr), "%s", chName);
|
||||
}
|
||||
|
||||
// Display power status
|
||||
@@ -1012,11 +1142,13 @@ void DebugInfo::drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, i
|
||||
if (WiFi.status() == WL_CONNECTED || isSoftAPForced() || radioConfig.preferences.wifi_ap_mode) {
|
||||
if (radioConfig.preferences.wifi_ap_mode || isSoftAPForced()) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "IP: " + String(WiFi.softAPIP().toString().c_str()));
|
||||
|
||||
// Number of connections to the AP. Default mmax for the esp32 is 4
|
||||
display->drawString(x + SCREEN_WIDTH - display->getStringWidth("(" + String(WiFi.softAPgetStationNum()) + "/4)"),
|
||||
y + FONT_HEIGHT_SMALL * 1, "(" + String(WiFi.softAPgetStationNum()) + "/4)");
|
||||
} else {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "IP: " + String(WiFi.localIP().toString().c_str()));
|
||||
}
|
||||
display->drawString(x + SCREEN_WIDTH - display->getStringWidth("(" + String(WiFi.softAPgetStationNum()) + "/4)"),
|
||||
y + FONT_HEIGHT_SMALL * 1, "(" + String(WiFi.softAPgetStationNum()) + "/4)");
|
||||
|
||||
} else if (WiFi.status() == WL_NO_SSID_AVAIL) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "SSID Not Found");
|
||||
@@ -1145,8 +1277,8 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
|
||||
display->drawString(x, y, String("USB"));
|
||||
}
|
||||
|
||||
display->drawString(x + SCREEN_WIDTH - display->getStringWidth("Mode " + String(channelSettings.modem_config)), y,
|
||||
"Mode " + String(channelSettings.modem_config));
|
||||
auto mode = "Mode " + String(channels.getPrimary().modem_config);
|
||||
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode), y, mode);
|
||||
|
||||
// Line 2
|
||||
uint32_t currentMillis = millis();
|
||||
@@ -1154,14 +1286,41 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
|
||||
uint32_t minutes = seconds / 60;
|
||||
uint32_t hours = minutes / 60;
|
||||
uint32_t days = hours / 24;
|
||||
currentMillis %= 1000;
|
||||
seconds %= 60;
|
||||
minutes %= 60;
|
||||
hours %= 24;
|
||||
// currentMillis %= 1000;
|
||||
// seconds %= 60;
|
||||
// minutes %= 60;
|
||||
// hours %= 24;
|
||||
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1,
|
||||
String(days) + "d " + (hours < 10 ? "0" : "") + String(hours) + ":" + (minutes < 10 ? "0" : "") +
|
||||
String(minutes) + ":" + (seconds < 10 ? "0" : "") + String(seconds));
|
||||
// Show uptime as days, hours, minutes OR seconds
|
||||
String uptime;
|
||||
if (days >= 2)
|
||||
uptime += String(days) + "d ";
|
||||
else if (hours >= 2)
|
||||
uptime += String(hours) + "h ";
|
||||
else if (minutes >= 1)
|
||||
uptime += String(minutes) + "m ";
|
||||
else
|
||||
uptime += String(seconds) + "s ";
|
||||
|
||||
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityFromNet);
|
||||
if (rtc_sec > 0) {
|
||||
long hms = rtc_sec % SEC_PER_DAY;
|
||||
// hms += tz.tz_dsttime * SEC_PER_HOUR;
|
||||
// hms -= tz.tz_minuteswest * SEC_PER_MIN;
|
||||
// mod `hms` to ensure in positive range of [0...SEC_PER_DAY)
|
||||
hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
|
||||
|
||||
// Tear apart hms into h:m:s
|
||||
int hour = hms / SEC_PER_HOUR;
|
||||
int min = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
|
||||
int sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN
|
||||
|
||||
char timebuf[9];
|
||||
snprintf(timebuf, sizeof(timebuf), "%02d:%02d:%02d", hour, min, sec);
|
||||
uptime += timebuf;
|
||||
}
|
||||
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, uptime);
|
||||
|
||||
#ifndef NO_ESP32
|
||||
// Show CPU Frequency.
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef NO_SCREEN
|
||||
namespace graphics
|
||||
{
|
||||
// Noop class for boards without screen.
|
||||
class Screen
|
||||
{
|
||||
public:
|
||||
Screen(char){}
|
||||
void onPress() {}
|
||||
void setup() {}
|
||||
void setOn(bool) {}
|
||||
void print(const char*){}
|
||||
void adjustBrightness(){}
|
||||
void doDeepSleep() {}
|
||||
};
|
||||
}
|
||||
|
||||
#else
|
||||
#include <cstring>
|
||||
|
||||
#include <OLEDDisplayUi.h>
|
||||
@@ -39,13 +57,6 @@ class DebugInfo
|
||||
DebugInfo(const DebugInfo &) = delete;
|
||||
DebugInfo &operator=(const DebugInfo &) = delete;
|
||||
|
||||
/// Sets the name of the channel.
|
||||
void setChannelNameStatus(const char *name)
|
||||
{
|
||||
concurrency::LockGuard guard(&lock);
|
||||
channelName = name;
|
||||
}
|
||||
|
||||
private:
|
||||
friend Screen;
|
||||
|
||||
@@ -56,8 +67,6 @@ class DebugInfo
|
||||
void drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||
void drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||
|
||||
std::string channelName;
|
||||
|
||||
/// Protects all of internal state.
|
||||
concurrency::Lock lock;
|
||||
};
|
||||
@@ -107,6 +116,8 @@ class Screen : public concurrency::OSThread
|
||||
*/
|
||||
void doDeepSleep();
|
||||
|
||||
void blink();
|
||||
|
||||
/// Handles a button press.
|
||||
void onPress() { enqueueCmd(ScreenCmd{.cmd = Cmd::ON_PRESS}); }
|
||||
|
||||
@@ -126,6 +137,14 @@ class Screen : public concurrency::OSThread
|
||||
enqueueCmd(cmd);
|
||||
}
|
||||
|
||||
void startFirmwareUpdateScreen()
|
||||
{
|
||||
ScreenCmd cmd;
|
||||
cmd.cmd = Cmd::START_FIRMWARE_UPDATE_SCREEN;
|
||||
enqueueCmd(cmd);
|
||||
}
|
||||
|
||||
|
||||
/// Stops showing the bluetooth PIN screen.
|
||||
void stopBluetoothPinScreen() { enqueueCmd(ScreenCmd{.cmd = Cmd::STOP_BLUETOOTH_PIN_SCREEN}); }
|
||||
|
||||
@@ -231,6 +250,7 @@ class Screen : public concurrency::OSThread
|
||||
void handleOnPress();
|
||||
void handleStartBluetoothPinScreen(uint32_t pin);
|
||||
void handlePrint(const char *text);
|
||||
void handleStartFirmwareUpdateScreen();
|
||||
|
||||
/// Rebuilds our list of frames (screens) to default ones.
|
||||
void setFrames();
|
||||
@@ -276,3 +296,4 @@ class Screen : public concurrency::OSThread
|
||||
};
|
||||
|
||||
} // namespace graphics
|
||||
#endif
|
||||
@@ -3,7 +3,6 @@
|
||||
#ifdef ST7735_CS
|
||||
#include "SPILock.h"
|
||||
#include "TFTDisplay.h"
|
||||
#include "graphics/configs.h"
|
||||
#include <SPI.h>
|
||||
#include <TFT_eSPI.h> // Graphics and font library for ST7735 driver chip
|
||||
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "fonts.h"
|
||||
|
||||
// This means the *visible* area (sh1106 can address 132, but shows 128 for example)
|
||||
#define IDLE_FRAMERATE 1 // in fps
|
||||
#define COMPASS_DIAM 44
|
||||
|
||||
// DEBUG
|
||||
#define NUM_EXTRA_FRAMES 2 // text message and debug frame
|
||||
// if defined a pixel will blink to show redraws
|
||||
// #define SHOW_REDRAWS
|
||||