mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-12 20:49:49 +04:00
Compare commits
1154 Commits
un2-a1ede0
...
subghz_pro
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc6adcbeb1 | ||
|
|
842b497829 | ||
|
|
928d57b050 | ||
|
|
641934f8dd | ||
|
|
de07313173 | ||
|
|
921db0bb22 | ||
|
|
f93462667c | ||
|
|
c56eaae89d | ||
|
|
185bb3277a | ||
|
|
b5a60dc10b | ||
|
|
f4af57d15a | ||
|
|
0abb88842a | ||
|
|
bc06d407f3 | ||
|
|
cb89bb9628 | ||
|
|
fbf99d6cb3 | ||
|
|
d0c6c3402c | ||
|
|
5a730e3adc | ||
|
|
628f089c42 | ||
|
|
3accc005e7 | ||
|
|
e08b10fa3c | ||
|
|
9007f0e360 | ||
|
|
8608ba01aa | ||
|
|
3747f95d5a | ||
|
|
6c4334e7cb | ||
|
|
d247eb43de | ||
|
|
ca12057426 | ||
|
|
8b22952dac | ||
|
|
70af1e3bfb | ||
|
|
896a78c645 | ||
|
|
37b84a9dbb | ||
|
|
5a2f693551 | ||
|
|
82f77edc70 | ||
|
|
80b8a0dddb | ||
|
|
5d8acc2210 | ||
|
|
e96bae8473 | ||
|
|
89a07d3573 | ||
|
|
fb4e2e02be | ||
|
|
c1e2d2443a | ||
|
|
08acbda895 | ||
|
|
02fad884fb | ||
|
|
6f37c25bdb | ||
|
|
27012b6be7 | ||
|
|
d179199a42 | ||
|
|
73a9425f73 | ||
|
|
462836346e | ||
|
|
a4c28ed5bf | ||
|
|
b8deb931c2 | ||
|
|
1831902fbd | ||
|
|
99b126f0ef | ||
|
|
f388a7ecab | ||
|
|
92d55ec90f | ||
|
|
f86582b99e | ||
|
|
c2545a2f7b | ||
|
|
9d2297410e | ||
|
|
371f3b3869 | ||
|
|
6b99999566 | ||
|
|
11f3464e1b | ||
|
|
e5bef61364 | ||
|
|
c0afbf659a | ||
|
|
a6dc16a98c | ||
|
|
aa646c7590 | ||
|
|
416a6425de | ||
|
|
24a4ae8014 | ||
|
|
7b4f1e5d26 | ||
|
|
ec2d0588df | ||
|
|
433db87976 | ||
|
|
0f2bf693c1 | ||
|
|
01f5167577 | ||
|
|
6a3f1cb9e4 | ||
|
|
56b4de7e5d | ||
|
|
285c324e8d | ||
|
|
4e0c2b99a9 | ||
|
|
ac31be8db8 | ||
|
|
6e21e204ab | ||
|
|
2298cc1d7b | ||
|
|
81064f8e43 | ||
|
|
db012807f5 | ||
|
|
bd2d7bf42f | ||
|
|
e9df7c9b72 | ||
|
|
14331a197b | ||
|
|
e40444b526 | ||
|
|
0521bd4320 | ||
|
|
6871c2c142 | ||
|
|
b79cac4473 | ||
|
|
66ab192cd7 | ||
|
|
2c22061580 | ||
|
|
97087fadb9 | ||
|
|
affef106ea | ||
|
|
5cf4f5fab4 | ||
|
|
d9f6b02da5 | ||
|
|
a551317cc2 | ||
|
|
3eacb0c715 | ||
|
|
0e0046c803 | ||
|
|
36f1283bb3 | ||
|
|
1b1f58408d | ||
|
|
4e02ac3442 | ||
|
|
a098e0561e | ||
|
|
175287ee0a | ||
|
|
7d866fade1 | ||
|
|
c5e225656d | ||
|
|
a49c8d5a08 | ||
|
|
07093d072f | ||
|
|
727c21d60a | ||
|
|
2bd80f510a | ||
|
|
fda38c9d04 | ||
|
|
16a5b13be4 | ||
|
|
05be200762 | ||
|
|
b47174d206 | ||
|
|
502e8d1d8d | ||
|
|
5d6ce87259 | ||
|
|
1707a1d1a4 | ||
|
|
7927fa371e | ||
|
|
ef2c668e31 | ||
|
|
fe3862e113 | ||
|
|
539c4e2dd0 | ||
|
|
b9351649ed | ||
|
|
be33e099be | ||
|
|
ba36f4672c | ||
|
|
67c2d1cf61 | ||
|
|
e17877b6a1 | ||
|
|
82c730b6be | ||
|
|
4265057ee8 | ||
|
|
163be139eb | ||
|
|
71871949ec | ||
|
|
d7ecc95de4 | ||
|
|
a7a4f9b885 | ||
|
|
eaffe0ec17 | ||
|
|
3890c7f9fa | ||
|
|
6f44900e05 | ||
|
|
a005087636 | ||
|
|
8f2f2d810a | ||
|
|
d02c586b92 | ||
|
|
ed78c4ca04 | ||
|
|
db1a8f8014 | ||
|
|
6c517d5030 | ||
|
|
575cce48af | ||
|
|
4de21410b7 | ||
|
|
f0c3cde2f3 | ||
|
|
3993f2b89c | ||
|
|
39841bd5a9 | ||
|
|
2006bd1c95 | ||
|
|
bf4d00a7d1 | ||
|
|
bbc1ba62fc | ||
|
|
31259d5304 | ||
|
|
0afc4a8982 | ||
|
|
c8cf3e41a7 | ||
|
|
8288a08eb3 | ||
|
|
985e9f3d74 | ||
|
|
b1f581239b | ||
|
|
d6d7a5ece2 | ||
|
|
0dccbe28bb | ||
|
|
99253a0e28 | ||
|
|
30081b79c4 | ||
|
|
e31e880264 | ||
|
|
cee9b640b3 | ||
|
|
20f98050f2 | ||
|
|
cdfd9891b7 | ||
|
|
468d0ea966 | ||
|
|
00076deece | ||
|
|
23ecc186c2 | ||
|
|
b7c2b9eff1 | ||
|
|
81316255f9 | ||
|
|
6c768f2019 | ||
|
|
111c7557b3 | ||
|
|
7a3a1aaf0d | ||
|
|
e3d473bf42 | ||
|
|
224d0aefe4 | ||
|
|
e4c9568867 | ||
|
|
0ee04fb968 | ||
|
|
1eda913367 | ||
|
|
3df5a99968 | ||
|
|
d035872cf6 | ||
|
|
1ff5843ee6 | ||
|
|
f116e8165f | ||
|
|
147f42a2b7 | ||
|
|
79d45c97f9 | ||
|
|
6e179bda1f | ||
|
|
9f279ac872 | ||
|
|
72f250195c | ||
|
|
246dcdb23f | ||
|
|
683b5cdc82 | ||
|
|
92135f8f06 | ||
|
|
771dc95a44 | ||
|
|
cc8066d9a0 | ||
|
|
bbe55aa9d6 | ||
|
|
5a7d6fcc63 | ||
|
|
66e606fe96 | ||
|
|
425f8f0254 | ||
|
|
52680fd14e | ||
|
|
559c4567cb | ||
|
|
8c50225da4 | ||
|
|
edddfbcb2c | ||
|
|
bfbcce030c | ||
|
|
5458157924 | ||
|
|
7edf90c2e5 | ||
|
|
46fb86265c | ||
|
|
52bcf27c2a | ||
|
|
3c52dca0c2 | ||
|
|
ed4b2b3362 | ||
|
|
b05cba931b | ||
|
|
1d46df70a9 | ||
|
|
f45f2a45da | ||
|
|
a0d5aa0c7b | ||
|
|
cee11464de | ||
|
|
a617ce1813 | ||
|
|
05641378d9 | ||
|
|
fbe7209817 | ||
|
|
c630e351fc | ||
|
|
26b5b7e406 | ||
|
|
d94caf0ba6 | ||
|
|
16f8f2c2df | ||
|
|
989442eab6 | ||
|
|
ab452bd575 | ||
|
|
cbabd46a4d | ||
|
|
743ee7b990 | ||
|
|
d91dc04585 | ||
|
|
e6f7e3a37e | ||
|
|
2929b12134 | ||
|
|
3985cb61c8 | ||
|
|
376e45a09d | ||
|
|
fcd3799aee | ||
|
|
dfd46f5539 | ||
|
|
01a9854f8a | ||
|
|
d9be815889 | ||
|
|
7f3ebcd110 | ||
|
|
bf2ab1f4d5 | ||
|
|
5db7fdf985 | ||
|
|
b1496ee9bd | ||
|
|
cd29fb8568 | ||
|
|
a8e5f22500 | ||
|
|
e12958d408 | ||
|
|
55054fc1a7 | ||
|
|
f5fe0ff694 | ||
|
|
d93ed003fe | ||
|
|
24a23e5dc7 | ||
|
|
126a9efd09 | ||
|
|
eee5c35400 | ||
|
|
4dc4d34d04 | ||
|
|
8fc834090d | ||
|
|
a5d92d7a08 | ||
|
|
884a751ae7 | ||
|
|
411855ce81 | ||
|
|
5134f44c09 | ||
|
|
f4cb458391 | ||
|
|
34ff250eea | ||
|
|
709fa633ff | ||
|
|
898b29b921 | ||
|
|
f3939a715a | ||
|
|
3174d29dfa | ||
|
|
e4fd3b1b0f | ||
|
|
856c8a1784 | ||
|
|
f0015fcf3b | ||
|
|
42fc209c63 | ||
|
|
2aafc207c0 | ||
|
|
ecb0e5c520 | ||
|
|
c875aca468 | ||
|
|
80a778e4cb | ||
|
|
792b035cac | ||
|
|
e07b96e04b | ||
|
|
7f08b25928 | ||
|
|
f8cc914a15 | ||
|
|
c6e30a1ed9 | ||
|
|
5853e0bdd9 | ||
|
|
3dd76f5c2f | ||
|
|
59bcad0cfc | ||
|
|
034a436ae0 | ||
|
|
c303b44f3f | ||
|
|
6e1875f285 | ||
|
|
5a9da13d84 | ||
|
|
e4aad248cf | ||
|
|
0650178946 | ||
|
|
6aad6840f6 | ||
|
|
7aaecd9278 | ||
|
|
b52674607a | ||
|
|
adfac7affb | ||
|
|
7a211e0018 | ||
|
|
62b287aac7 | ||
|
|
fb39d6e679 | ||
|
|
b02a60f223 | ||
|
|
0b92e5ae74 | ||
|
|
27826f95ca | ||
|
|
24c12ab852 | ||
|
|
959ea4d8c1 | ||
|
|
7f09f5f616 | ||
|
|
dfe141c78f | ||
|
|
dce2271330 | ||
|
|
c708a301f5 | ||
|
|
927fcca081 | ||
|
|
db16b3ef1d | ||
|
|
379807423f | ||
|
|
8c94e3e78d | ||
|
|
5123fac5d9 | ||
|
|
cd5596027f | ||
|
|
374452796c | ||
|
|
a0814aab31 | ||
|
|
341610b8a1 | ||
|
|
414a13131a | ||
|
|
08acfa50c0 | ||
|
|
75c47a8d64 | ||
|
|
6aeb9478aa | ||
|
|
f41c06ab74 | ||
|
|
5f82732b4f | ||
|
|
ddc5540eeb | ||
|
|
729a1b5788 | ||
|
|
9e1a6a6d2e | ||
|
|
13acdf00da | ||
|
|
d50418e839 | ||
|
|
682d2d9b6f | ||
|
|
deaf80fddc | ||
|
|
895dba06ba | ||
|
|
c89a594d30 | ||
|
|
70f0ab1442 | ||
|
|
073fb3861a | ||
|
|
75e9de12b0 | ||
|
|
78139c5936 | ||
|
|
ad9d746a27 | ||
|
|
20621da8ac | ||
|
|
b30aa890e6 | ||
|
|
d2df35a35b | ||
|
|
44830593c1 | ||
|
|
8d2143add5 | ||
|
|
4dc2a7f212 | ||
|
|
b7046b2179 | ||
|
|
3bac76f75e | ||
|
|
8488f4406e | ||
|
|
4ec5dbcee8 | ||
|
|
78c64737aa | ||
|
|
76047e6e48 | ||
|
|
cd4f719783 | ||
|
|
b3f32b6938 | ||
|
|
3a4b5e6445 | ||
|
|
754a29009d | ||
|
|
8c791a998a | ||
|
|
74c46a515c | ||
|
|
797ddc0755 | ||
|
|
bc99538ca8 | ||
|
|
b6442f2971 | ||
|
|
c3b8d3cef4 | ||
|
|
3fa11aa4a0 | ||
|
|
ddf4f268ea | ||
|
|
a921a828e5 | ||
|
|
a94c553c5a | ||
|
|
50ee2eb8aa | ||
|
|
2c450bd835 | ||
|
|
1cdccbae23 | ||
|
|
109c34398f | ||
|
|
c24bea6b06 | ||
|
|
fa223a4f4b | ||
|
|
5e74622b2a | ||
|
|
9740dd8c75 | ||
|
|
41c43f4805 | ||
|
|
890419864c | ||
|
|
604b93645e | ||
|
|
7d4d2e19a9 | ||
|
|
ecf7dfd8fd | ||
|
|
c8fb64ce22 | ||
|
|
11c6d2d0be | ||
|
|
23155f9891 | ||
|
|
9248de50d1 | ||
|
|
b8dd75884c | ||
|
|
f7dfe77aaf | ||
|
|
ed5b2da9ad | ||
|
|
4571dd25a3 | ||
|
|
5d4dcc925d | ||
|
|
28345b2fbd | ||
|
|
82f8ad4540 | ||
|
|
df88a805a0 | ||
|
|
72dc8e95d0 | ||
|
|
1534bcf7cb | ||
|
|
62fbf868fe | ||
|
|
ba429f6c88 | ||
|
|
3d760077b6 | ||
|
|
189117326b | ||
|
|
84fbe6ad36 | ||
|
|
891d5845dd | ||
|
|
d3c5ccbfe6 | ||
|
|
91c159e823 | ||
|
|
8457da39e0 | ||
|
|
27cc057573 | ||
|
|
f28307128f | ||
|
|
27bfe861d9 | ||
|
|
a51a6914f4 | ||
|
|
00b4903576 | ||
|
|
a35b536120 | ||
|
|
6b30dc5114 | ||
|
|
b5aede37c7 | ||
|
|
3821570d74 | ||
|
|
6eb6297ab7 | ||
|
|
4ef23f3a58 | ||
|
|
95f8895b43 | ||
|
|
ebac25a057 | ||
|
|
06cf1bb868 | ||
|
|
7fae0d5070 | ||
|
|
0f1e52a271 | ||
|
|
f955864c01 | ||
|
|
e8e000d17b | ||
|
|
b11b9f1b38 | ||
|
|
26e5527a93 | ||
|
|
1c2f4da4ee | ||
|
|
cc30c0d01d | ||
|
|
4e3506ab2e | ||
|
|
a850aa8f2c | ||
|
|
984528d366 | ||
|
|
622abc902a | ||
|
|
08f48280cd | ||
|
|
ea054423b0 | ||
|
|
a96eb74f91 | ||
|
|
dc6379a1ca | ||
|
|
f89983e2fa | ||
|
|
66d31b3a0e | ||
|
|
d58b9f3fe8 | ||
|
|
4accce93cf | ||
|
|
27ee0f73f7 | ||
|
|
3108dc7c8c | ||
|
|
90573fbeed | ||
|
|
d7735a1efb | ||
|
|
69879c3693 | ||
|
|
529a20b9d2 | ||
|
|
43be797926 | ||
|
|
4840196b88 | ||
|
|
5db9238331 | ||
|
|
473f9c0e44 | ||
|
|
727f043747 | ||
|
|
ded7e727d0 | ||
|
|
08eb666f7b | ||
|
|
1390f10a6f | ||
|
|
f43b76efc2 | ||
|
|
8a279758fd | ||
|
|
4e347b207c | ||
|
|
b88aa9f391 | ||
|
|
3ec517bb54 | ||
|
|
bce32bf893 | ||
|
|
29ba7956c1 | ||
|
|
05293ce860 | ||
|
|
b21618b4cc | ||
|
|
96f6e77fd2 | ||
|
|
f5985ee27c | ||
|
|
4f05c1816b | ||
|
|
1c926cf8a2 | ||
|
|
1243dad8fd | ||
|
|
ac400abede | ||
|
|
a7fc43d1ac | ||
|
|
5992e2d4f2 | ||
|
|
aecdb8fdcd | ||
|
|
9192520c70 | ||
|
|
6d368c3e3a | ||
|
|
a34fbf6976 | ||
|
|
8582670a34 | ||
|
|
749a2f5977 | ||
|
|
1481036abe | ||
|
|
ad3bff0b67 | ||
|
|
c2cb14834d | ||
|
|
02369c8394 | ||
|
|
b0970953b9 | ||
|
|
dff73dfd38 | ||
|
|
2f96fad7c2 | ||
|
|
4d9ecbacae | ||
|
|
10580b5117 | ||
|
|
566e80abf6 | ||
|
|
a8541d5191 | ||
|
|
3b1bc8b7e1 | ||
|
|
8deecc9241 | ||
|
|
0bfaaa0fe1 | ||
|
|
488211b4d7 | ||
|
|
05147e5b7a | ||
|
|
0abe9ee417 | ||
|
|
09f80d0086 | ||
|
|
7ff9414656 | ||
|
|
aba70c33f1 | ||
|
|
f3553deb16 | ||
|
|
2796623551 | ||
|
|
5856746fc9 | ||
|
|
6d8ecec051 | ||
|
|
060a48cdfc | ||
|
|
91eed4209d | ||
|
|
787eafd7b8 | ||
|
|
10b55bcbd2 | ||
|
|
19e230823b | ||
|
|
d1418bf7f6 | ||
|
|
c34ae66b6e | ||
|
|
e0d716647c | ||
|
|
a9c2b4d6a0 | ||
|
|
4fb03cde62 | ||
|
|
7e049d185f | ||
|
|
376fa071a1 | ||
|
|
797eab8924 | ||
|
|
c7de198d1a | ||
|
|
4cee550cc6 | ||
|
|
e7107e39f7 | ||
|
|
0286636183 | ||
|
|
2bdc34274f | ||
|
|
842ba616ee | ||
|
|
9e2c3eb96d | ||
|
|
fa87216a1e | ||
|
|
921b47ac50 | ||
|
|
a81a5ca57c | ||
|
|
577334a394 | ||
|
|
84ba2690a5 | ||
|
|
de2334f314 | ||
|
|
4a4c041fd9 | ||
|
|
d5624cd842 | ||
|
|
ca72a4f225 | ||
|
|
453666192c | ||
|
|
cbc9720738 | ||
|
|
361ca8b750 | ||
|
|
cefff35661 | ||
|
|
fd5b1f1943 | ||
|
|
ef7052fbad | ||
|
|
22062faae2 | ||
|
|
8a09ac2569 | ||
|
|
5d18b189ec | ||
|
|
57f68c11e9 | ||
|
|
94e7bb7fe3 | ||
|
|
da32d252db | ||
|
|
1a4071a7c1 | ||
|
|
084cdc6ad0 | ||
|
|
36e15a1352 | ||
|
|
94453d9100 | ||
|
|
30801d4eb8 | ||
|
|
f10e82c64d | ||
|
|
0238da8ac9 | ||
|
|
5e5d6cf1a0 | ||
|
|
f2e6384860 | ||
|
|
0c3fc5cc30 | ||
|
|
1464c65d10 | ||
|
|
96a97f1bfb | ||
|
|
2dea6969fe | ||
|
|
675d1f007f | ||
|
|
65b6dd5707 | ||
|
|
3681a5478c | ||
|
|
fbcb1f0c6e | ||
|
|
a0488624b5 | ||
|
|
d56e399ae5 | ||
|
|
021ba9dc1a | ||
|
|
b5e7bb3334 | ||
|
|
1dc79fddf0 | ||
|
|
f43fd7a351 | ||
|
|
03153b4d02 | ||
|
|
327df4a813 | ||
|
|
1fa4c646e6 | ||
|
|
1c12613863 | ||
|
|
d541f142c8 | ||
|
|
966528b16a | ||
|
|
87fb852bcf | ||
|
|
d87caac5fe | ||
|
|
6ff437ad18 | ||
|
|
76e8b00dc8 | ||
|
|
f73d28677b | ||
|
|
bb3dd48447 | ||
|
|
17ea9315e0 | ||
|
|
63b40992ef | ||
|
|
27921e42ff | ||
|
|
ebf39f189d | ||
|
|
2954ec6d97 | ||
|
|
31a9a3f5f3 | ||
|
|
2fcff37f88 | ||
|
|
35a4eef3fc | ||
|
|
01e24f6837 | ||
|
|
9d728a1c65 | ||
|
|
7fb1af07b8 | ||
|
|
e756c680e5 | ||
|
|
9df393c301 | ||
|
|
df693e7e57 | ||
|
|
74fb904fb0 | ||
|
|
b85f533a20 | ||
|
|
6f9f0dbe6e | ||
|
|
6701cbacf9 | ||
|
|
aae82b2694 | ||
|
|
556b604f7b | ||
|
|
8794aad5d0 | ||
|
|
df808be8d7 | ||
|
|
99ef76e638 | ||
|
|
5c3a5cd8f7 | ||
|
|
349ef2ecb8 | ||
|
|
1fc67f76a2 | ||
|
|
6a470a464e | ||
|
|
c535b8f4ce | ||
|
|
2daf39018b | ||
|
|
741ad34b2c | ||
|
|
e42dda7cfb | ||
|
|
9a21dae29c | ||
|
|
c43ec414bb | ||
|
|
cbc5e3fa92 | ||
|
|
2b06b41ffd | ||
|
|
7d59eb1848 | ||
|
|
1c81dc2dee | ||
|
|
176f27e037 | ||
|
|
42cc41cda3 | ||
|
|
8d9786f89a | ||
|
|
11576e58c8 | ||
|
|
21e5b66bf6 | ||
|
|
7945afd1d2 | ||
|
|
dfbd978466 | ||
|
|
e1e208922e | ||
|
|
196fc562d8 | ||
|
|
de58d69c06 | ||
|
|
e40376bc63 | ||
|
|
853b182b8b | ||
|
|
ef4bdf3909 | ||
|
|
79fbaf2620 | ||
|
|
45d6826fa0 | ||
|
|
4070eeb1c9 | ||
|
|
7f67445c85 | ||
|
|
eb3a8734fb | ||
|
|
38c62cf247 | ||
|
|
1ce591a6c5 | ||
|
|
0ed290161d | ||
|
|
0b9d632363 | ||
|
|
a66f94d22e | ||
|
|
2a6a3a1bf7 | ||
|
|
79812a1fe0 | ||
|
|
0ab14c37c9 | ||
|
|
eec21b073b | ||
|
|
b70395eba9 | ||
|
|
297f185ef4 | ||
|
|
0261dc3075 | ||
|
|
60cd7b8b32 | ||
|
|
3d36105142 | ||
|
|
849afc8798 | ||
|
|
c535ce9b76 | ||
|
|
84f9af3e7e | ||
|
|
97e8da7a7b | ||
|
|
769c53b6da | ||
|
|
6b47bc1af4 | ||
|
|
1b3156521c | ||
|
|
a82c3ccc22 | ||
|
|
e121e6a287 | ||
|
|
03140e4349 | ||
|
|
43d35b42e5 | ||
|
|
7ed56fca54 | ||
|
|
bc8fe6144b | ||
|
|
0e4cda851c | ||
|
|
6267dc7d5b | ||
|
|
1a33e1a00b | ||
|
|
2985268fe3 | ||
|
|
5cdf339a95 | ||
|
|
08e533ccf2 | ||
|
|
a63e0dbec7 | ||
|
|
513c352820 | ||
|
|
bf62f66dc8 | ||
|
|
ab65c17dd5 | ||
|
|
6e7b7f1d89 | ||
|
|
4f9b4d80c7 | ||
|
|
bd21970ccb | ||
|
|
79193e7f37 | ||
|
|
f086d5b7a3 | ||
|
|
757b218397 | ||
|
|
0321717a86 | ||
|
|
2a5d629318 | ||
|
|
6ebdf6dffb | ||
|
|
b6435374d7 | ||
|
|
2c51c8cb31 | ||
|
|
6d7e424fdf | ||
|
|
8e14291363 | ||
|
|
c9473e6d30 | ||
|
|
ffa6249b64 | ||
|
|
daea8a963d | ||
|
|
7829428455 | ||
|
|
59022b4ff7 | ||
|
|
bbf4d90ec1 | ||
|
|
0f3c0aaa11 | ||
|
|
78a4c82bf5 | ||
|
|
af1ecd82ba | ||
|
|
963c6a95f3 | ||
|
|
04761b12c0 | ||
|
|
078e2c2db9 | ||
|
|
063b13df00 | ||
|
|
37159c7506 | ||
|
|
3da1c229bb | ||
|
|
51d478489a | ||
|
|
9bb0dbaa3e | ||
|
|
1c8451fad4 | ||
|
|
669822cdd2 | ||
|
|
4f3ef83c77 | ||
|
|
c511c67e71 | ||
|
|
b9c483fbf8 | ||
|
|
00fcd9cfcd | ||
|
|
cf5b87f82e | ||
|
|
8bbfadffe5 | ||
|
|
15dcf73083 | ||
|
|
5ef358df31 | ||
|
|
a5e3df0375 | ||
|
|
fae17d2eb9 | ||
|
|
c957bf09e0 | ||
|
|
817aa273f4 | ||
|
|
0adcaf7592 | ||
|
|
7e8bf03b25 | ||
|
|
a6886b096e | ||
|
|
a0dc770b98 | ||
|
|
a1b368abda | ||
|
|
5681016dce | ||
|
|
17cb7533cb | ||
|
|
1e183d5450 | ||
|
|
0e9f6bef77 | ||
|
|
0f81248579 | ||
|
|
337c5b5a8b | ||
|
|
be97baef06 | ||
|
|
d0ccf11992 | ||
|
|
eb3523b8e2 | ||
|
|
073a0942e8 | ||
|
|
52bd4a1a3c | ||
|
|
5989651b10 | ||
|
|
bb8b9f0b57 | ||
|
|
aface38b42 | ||
|
|
f7fb055ae3 | ||
|
|
1b64a95ec6 | ||
|
|
de5eb16ef2 | ||
|
|
c3731e6540 | ||
|
|
0e338f7964 | ||
|
|
4240b59311 | ||
|
|
41de5f3c52 | ||
|
|
b56fed477a | ||
|
|
02fe403b2b | ||
|
|
a821b80ffc | ||
|
|
950dee48a0 | ||
|
|
73441af9c6 | ||
|
|
f9730bcafe | ||
|
|
3c7a4eeaed | ||
|
|
90cefe7c71 | ||
|
|
368079f6f4 | ||
|
|
8b71ac9483 | ||
|
|
3cba34bbc8 | ||
|
|
741c784f2d | ||
|
|
394507bc81 | ||
|
|
8240b25fe0 | ||
|
|
1d196b41e9 | ||
|
|
666d30f2dd | ||
|
|
95df532612 | ||
|
|
64774f191f | ||
|
|
f6eed35729 | ||
|
|
930b369812 | ||
|
|
721ab717d7 | ||
|
|
aec36e7041 | ||
|
|
e7c4b40dbe | ||
|
|
820afd2aec | ||
|
|
a66e8d9ac9 | ||
|
|
f94e8f4ac8 | ||
|
|
a959fa32bc | ||
|
|
3c094c4741 | ||
|
|
625b1eb126 | ||
|
|
22902aabcc | ||
|
|
b93f2dc293 | ||
|
|
af40b6b020 | ||
|
|
ef72ad2462 | ||
|
|
3420cda83a | ||
|
|
68219f0d7b | ||
|
|
0a0c06222a | ||
|
|
52650ab0e3 | ||
|
|
dd601dd940 | ||
|
|
933471e2fb | ||
|
|
317c9ec190 | ||
|
|
e90d335cdf | ||
|
|
1657604eb2 | ||
|
|
684fdc632f | ||
|
|
c3bdbcd5cc | ||
|
|
65a593c52e | ||
|
|
9e39f82906 | ||
|
|
3985b456c3 | ||
|
|
c89e5e11a4 | ||
|
|
9f0aef330e | ||
|
|
328d049b6a | ||
|
|
ce294c3b1e | ||
|
|
e81b7be232 | ||
|
|
4d11213494 | ||
|
|
e38895794b | ||
|
|
2d6c2886ae | ||
|
|
aa2ecbe80f | ||
|
|
65005e71d2 | ||
|
|
0a86ad43ca | ||
|
|
e8913f2e33 | ||
|
|
04e50c9f89 | ||
|
|
7175459bcd | ||
|
|
c709b0b126 | ||
|
|
18f49c2644 | ||
|
|
939a77b4e7 | ||
|
|
1bb6113eaa | ||
|
|
0ab400a27e | ||
|
|
bf8fd71c00 | ||
|
|
3bd74b7f01 | ||
|
|
d68ac50efd | ||
|
|
e3ea5bca76 | ||
|
|
8ea0178a0d | ||
|
|
24d65e8655 | ||
|
|
d92212f0ea | ||
|
|
60d125e72a | ||
|
|
bb2bcc63a4 | ||
|
|
eee90c6c40 | ||
|
|
95182b266c | ||
|
|
7973013085 | ||
|
|
0b1f573a72 | ||
|
|
677de32026 | ||
|
|
5ac2431cd1 | ||
|
|
4b408b6988 | ||
|
|
af1647c84b | ||
|
|
356389ace9 | ||
|
|
345d9704fd | ||
|
|
0b2867aaa4 | ||
|
|
fe199ab54a | ||
|
|
adab2b9e03 | ||
|
|
c8bc9e26e0 | ||
|
|
e1f5999095 | ||
|
|
79b37556d4 | ||
|
|
9c4612e571 | ||
|
|
0652830c51 | ||
|
|
c417d467f7 | ||
|
|
a09d0a8bd4 | ||
|
|
ebc2b66372 | ||
|
|
abfa804ae0 | ||
|
|
9a0eacc91c | ||
|
|
a22b107335 | ||
|
|
0f1e03fa51 | ||
|
|
2ab9769b4b | ||
|
|
ee665e1089 | ||
|
|
5e940ea9d0 | ||
|
|
f3d45ab350 | ||
|
|
7d139517b8 | ||
|
|
f51877b7c8 | ||
|
|
92b8a256e4 | ||
|
|
0d27086c40 | ||
|
|
46bffba498 | ||
|
|
165d9972e7 | ||
|
|
bb5c545211 | ||
|
|
ddaa8ec1fd | ||
|
|
104a1998a5 | ||
|
|
38297cb7af | ||
|
|
419a90aa28 | ||
|
|
348c4c3b5f | ||
|
|
0bcfe7b7a4 | ||
|
|
7fe0e6c073 | ||
|
|
28d9d5194f | ||
|
|
296bdfd433 | ||
|
|
ddc6265920 | ||
|
|
2d3469e057 | ||
|
|
8e4595e4f7 | ||
|
|
328fcdd924 | ||
|
|
0dc55957ae | ||
|
|
5319c69e59 | ||
|
|
1115a0b877 | ||
|
|
908eca335e | ||
|
|
e242941d70 | ||
|
|
4e66d37ac8 | ||
|
|
0dff574757 | ||
|
|
6acf76e4ed | ||
|
|
d7c75b9db8 | ||
|
|
4fd52b0564 | ||
|
|
95bfaa8b23 | ||
|
|
639988a979 | ||
|
|
0a63b2fdd8 | ||
|
|
00b430a912 | ||
|
|
73b8d496fe | ||
|
|
d52d9ffe79 | ||
|
|
0dcf752342 | ||
|
|
9b55076eed | ||
|
|
c4479a9056 | ||
|
|
e7316464cb | ||
|
|
85d341104f | ||
|
|
cb47edf82f | ||
|
|
d5f791b1fa | ||
|
|
4da3f9cf15 | ||
|
|
8333ebd401 | ||
|
|
3d25838a53 | ||
|
|
3ea6d59c2f | ||
|
|
5277933980 | ||
|
|
93a6e17ce5 | ||
|
|
0349380347 | ||
|
|
09b622d4ae | ||
|
|
20e3a202aa | ||
|
|
0a68d80028 | ||
|
|
457b9ae2a9 | ||
|
|
4b921803cb | ||
|
|
9e8c841df0 | ||
|
|
2cfb5bdfc6 | ||
|
|
b287cb4b4a | ||
|
|
99ddb235e0 | ||
|
|
9cd0592aaf | ||
|
|
fa7aa00f57 | ||
|
|
981740bcc6 | ||
|
|
09fec61153 | ||
|
|
dcf076b11e | ||
|
|
3434305630 | ||
|
|
492f147568 | ||
|
|
be3ee9f2fe | ||
|
|
9fae52166c | ||
|
|
8dab5fd231 | ||
|
|
4c0c70ff64 | ||
|
|
26f852839a | ||
|
|
f11df49468 | ||
|
|
2fd8c71626 | ||
|
|
ba69ee88f3 | ||
|
|
8e362b087a | ||
|
|
88ea7c8d12 | ||
|
|
f9c08249a0 | ||
|
|
8995d03fad | ||
|
|
b20c01d68b | ||
|
|
1d08a8aaa2 | ||
|
|
2f1d20210b | ||
|
|
85900c50ff | ||
|
|
fc3b62aa41 | ||
|
|
8b7a52b97b | ||
|
|
a99d29c0f3 | ||
|
|
72ab2b98ad | ||
|
|
aff99a72e8 | ||
|
|
56b9361bc0 | ||
|
|
7c9f2cd3e8 | ||
|
|
ebaa84b0c2 | ||
|
|
89e27d10a2 | ||
|
|
a8edb41eae | ||
|
|
5c8df66b7c | ||
|
|
378bf05068 | ||
|
|
406d830fb6 | ||
|
|
85573745ec | ||
|
|
c72b305480 | ||
|
|
7d2d2b3dd9 | ||
|
|
d530238fae | ||
|
|
30f10dce80 | ||
|
|
0adad32418 | ||
|
|
cf4a0b06e2 | ||
|
|
fd127cee8c | ||
|
|
e8920ef15a | ||
|
|
ee614f11fc | ||
|
|
c2bf81dd64 | ||
|
|
c19a8fd636 | ||
|
|
5ed9ca838a | ||
|
|
29ab1088bd | ||
|
|
517d4ffb73 | ||
|
|
b39e0dd09e | ||
|
|
1e7b45159e | ||
|
|
9b75f957d9 | ||
|
|
e8f8cefd6f | ||
|
|
984d89c6d0 | ||
|
|
d159c56e28 | ||
|
|
d0e24bb455 | ||
|
|
e00f7009f3 | ||
|
|
64ecdce5ca | ||
|
|
de49bcc80c | ||
|
|
8793b9f0aa | ||
|
|
c6945313f6 | ||
|
|
fd41176353 | ||
|
|
d8fbaba7a0 | ||
|
|
de1d83038f | ||
|
|
f8af0c1509 | ||
|
|
9605af0e85 | ||
|
|
ed080ea5fc | ||
|
|
a24ef44289 | ||
|
|
1019025b13 | ||
|
|
a44739cde9 | ||
|
|
c19f3a3a6a | ||
|
|
6189511a87 | ||
|
|
12bbe3e68e | ||
|
|
12dda6b76b | ||
|
|
92a6cce70e | ||
|
|
8fc3140292 | ||
|
|
235af1aacb | ||
|
|
a0a8e0457a | ||
|
|
f291c9546d | ||
|
|
a73535070c | ||
|
|
8931cb2088 | ||
|
|
33e4d2a17f | ||
|
|
ca3ce2edce | ||
|
|
1ab59bac83 | ||
|
|
221bd13e09 | ||
|
|
b508fa7dfc | ||
|
|
fc2a643d12 | ||
|
|
2a30214ca2 | ||
|
|
120a74ee1a | ||
|
|
c1bb10a694 | ||
|
|
33892ebfb7 | ||
|
|
f8f7c2b895 | ||
|
|
96dd3ad1ce | ||
|
|
2740368b0e | ||
|
|
f56c94922d | ||
|
|
42df7aa04a | ||
|
|
732f744b3c | ||
|
|
5c821eb076 | ||
|
|
e8d62db634 | ||
|
|
e56f97245b | ||
|
|
26dfa9b9a5 | ||
|
|
62284209e6 | ||
|
|
6d86629e9a | ||
|
|
0139891d64 | ||
|
|
8415dcf823 | ||
|
|
9a9abd59e9 | ||
|
|
7417259dcd | ||
|
|
a3c6d8bb4a | ||
|
|
3807d61034 | ||
|
|
ab55ddbfb1 | ||
|
|
328fbffb80 | ||
|
|
79c3040629 | ||
|
|
4f6d27a214 | ||
|
|
f0e8da98ee | ||
|
|
c2e58f9633 | ||
|
|
63eced449e | ||
|
|
49ba5fc9f3 | ||
|
|
dd28e5ea7b | ||
|
|
da7b9c86b9 | ||
|
|
3303047a4a | ||
|
|
488563000a | ||
|
|
6c08564d37 | ||
|
|
5b8311cdea | ||
|
|
68429e191d | ||
|
|
5e7bcea29d | ||
|
|
a139f015b9 | ||
|
|
6579576490 | ||
|
|
57251eb028 | ||
|
|
f33ea3ebe0 | ||
|
|
84d12da45a | ||
|
|
72713d6f4e | ||
|
|
468bc1dace | ||
|
|
56f760aa07 | ||
|
|
6db6d123d5 | ||
|
|
68009c6230 | ||
|
|
02c27becb0 | ||
|
|
576dab02a4 | ||
|
|
4942bd2105 | ||
|
|
1ee82ba865 | ||
|
|
6e9658608e | ||
|
|
d49ca17824 | ||
|
|
fc776446de | ||
|
|
5a7fa30199 | ||
|
|
e4cdae4922 | ||
|
|
c7a67c1308 | ||
|
|
fd3e70492d | ||
|
|
a4ee73b470 | ||
|
|
8f5cbdc4c2 | ||
|
|
6d999abb80 | ||
|
|
89d49fdba7 | ||
|
|
04d67ca8ae | ||
|
|
4f7ca617cc | ||
|
|
f61a8fda53 | ||
|
|
5e35e51c57 | ||
|
|
dfbe21e720 | ||
|
|
18e7d2eb41 | ||
|
|
e7aaf3dbb2 | ||
|
|
76aecb597a | ||
|
|
d2018dfa1d | ||
|
|
8ec5527ae4 | ||
|
|
3a50021348 | ||
|
|
20c63664ca | ||
|
|
9b6abd8ef0 | ||
|
|
f96f2e2411 | ||
|
|
6442caa3e4 | ||
|
|
f06930e4ae | ||
|
|
865baed0bb | ||
|
|
f81999ea4a | ||
|
|
89a4b77e73 | ||
|
|
ead9f134f4 | ||
|
|
9ff29d12b2 | ||
|
|
5f3637ca44 | ||
|
|
effcb445ce | ||
|
|
55bad280ee | ||
|
|
b003ede76c | ||
|
|
57362b3eab | ||
|
|
cb0d9ec591 | ||
|
|
dad4772bec | ||
|
|
a16542cda6 | ||
|
|
a5d22154a9 | ||
|
|
ce173fd44c | ||
|
|
d547307357 | ||
|
|
c198a51b1c | ||
|
|
d5b239595f | ||
|
|
947e44ce86 | ||
|
|
55f8beef9f | ||
|
|
e46e6f8ee9 | ||
|
|
ff6c4f9957 | ||
|
|
8fdee1e460 | ||
|
|
558f85603d | ||
|
|
b909321699 | ||
|
|
ff52f7d9ba | ||
|
|
abfe53f156 | ||
|
|
9a2228bfa4 | ||
|
|
da17adb5ee | ||
|
|
7b16de2d6f | ||
|
|
735056628c | ||
|
|
9941df48bb | ||
|
|
8e0e4b5e2c | ||
|
|
220adf2375 | ||
|
|
39cd10f061 | ||
|
|
af1fb2018a | ||
|
|
3dde6089fc | ||
|
|
5f28eafcd2 | ||
|
|
94d2d5c99f | ||
|
|
3a569d4be8 | ||
|
|
db2f4847ba | ||
|
|
50dc2d7389 | ||
|
|
ede3bac799 | ||
|
|
eb4ff3c0fd | ||
|
|
afff1adf8f | ||
|
|
92a738bf77 | ||
|
|
b3d9523322 | ||
|
|
155cbeeb11 | ||
|
|
0d273bd15c | ||
|
|
2e047ff411 | ||
|
|
80a406afd3 | ||
|
|
6b5631e5b0 | ||
|
|
80735d4f51 | ||
|
|
e1e46b89b1 | ||
|
|
b7bdc4c985 | ||
|
|
f429d14ab5 | ||
|
|
744b61ca28 | ||
|
|
8520daf28b | ||
|
|
aa9f958f07 | ||
|
|
b3f96306ed | ||
|
|
022315e93d | ||
|
|
702786078d | ||
|
|
e78d73c35f | ||
|
|
2552278a3d | ||
|
|
34406f0607 | ||
|
|
04f5ad83f8 | ||
|
|
06c0adfe98 | ||
|
|
b0d4146c76 | ||
|
|
be293757c0 | ||
|
|
79e7c87b94 | ||
|
|
b6dfeac7ca | ||
|
|
349a151330 | ||
|
|
40dc80499f | ||
|
|
b0c31da36a | ||
|
|
a76259add9 | ||
|
|
3f3ee1822a | ||
|
|
0714df4a4a | ||
|
|
52361b4adf | ||
|
|
61fe66c178 | ||
|
|
016ebd3afc | ||
|
|
82b9d74b38 | ||
|
|
6d1929af25 | ||
|
|
384397c282 | ||
|
|
0abd54ccc8 | ||
|
|
ea833d891f | ||
|
|
9152299562 | ||
|
|
906124b091 | ||
|
|
9c0391a887 | ||
|
|
7ded162c94 | ||
|
|
382f620aff | ||
|
|
323a56e987 | ||
|
|
b65a2e9c94 | ||
|
|
eed8cd1824 | ||
|
|
500456b03d | ||
|
|
4b8221d813 | ||
|
|
c7a454752a | ||
|
|
b1f8073333 | ||
|
|
e489c57fc0 | ||
|
|
b4a3ac468f | ||
|
|
877a9202fd | ||
|
|
acbe7a10e2 | ||
|
|
b6b33a70e9 | ||
|
|
1132653d7a | ||
|
|
3fd30a9132 | ||
|
|
50e9f7d3ca | ||
|
|
7643fdad7c | ||
|
|
b86f42e7fb | ||
|
|
9ba7e625ca | ||
|
|
1adf76d54d | ||
|
|
7ec4cb4b7a | ||
|
|
0c5146e047 | ||
|
|
127b700642 |
@@ -36,19 +36,12 @@ Min level: 1
|
|||||||
Max level: 1
|
Max level: 1
|
||||||
Weight: 3
|
Weight: 3
|
||||||
|
|
||||||
Name: L2_Furippa2_128x64
|
Name: L1_Happy_holidays_128x64
|
||||||
Min butthurt: 0
|
Min butthurt: 0
|
||||||
Max butthurt: 6
|
Max butthurt: 14
|
||||||
Min level: 2
|
Min level: 1
|
||||||
Max level: 2
|
|
||||||
Weight: 3
|
|
||||||
|
|
||||||
Name: L3_Furippa3_128x64
|
|
||||||
Min butthurt: 0
|
|
||||||
Max butthurt: 6
|
|
||||||
Min level: 3
|
|
||||||
Max level: 3
|
Max level: 3
|
||||||
Weight: 3
|
Weight: 4
|
||||||
|
|
||||||
Name: L1_Read_books_128x64
|
Name: L1_Read_books_128x64
|
||||||
Min butthurt: 0
|
Min butthurt: 0
|
||||||
@@ -57,13 +50,6 @@ Min level: 1
|
|||||||
Max level: 1
|
Max level: 1
|
||||||
Weight: 3
|
Weight: 3
|
||||||
|
|
||||||
Name: L2_Hacking_pc_128x64
|
|
||||||
Min butthurt: 0
|
|
||||||
Max butthurt: 8
|
|
||||||
Min level: 2
|
|
||||||
Max level: 2
|
|
||||||
Weight: 3
|
|
||||||
|
|
||||||
Name: L1_Cry_128x64
|
Name: L1_Cry_128x64
|
||||||
Min butthurt: 8
|
Min butthurt: 8
|
||||||
Max butthurt: 13
|
Max butthurt: 13
|
||||||
@@ -85,6 +71,62 @@ Min level: 1
|
|||||||
Max level: 3
|
Max level: 3
|
||||||
Weight: 3
|
Weight: 3
|
||||||
|
|
||||||
|
Name: L1_Mods_128x64
|
||||||
|
Min butthurt: 0
|
||||||
|
Max butthurt: 9
|
||||||
|
Min level: 1
|
||||||
|
Max level: 3
|
||||||
|
Weight: 4
|
||||||
|
|
||||||
|
Name: L1_Painting_128x64
|
||||||
|
Min butthurt: 0
|
||||||
|
Max butthurt: 7
|
||||||
|
Min level: 1
|
||||||
|
Max level: 3
|
||||||
|
Weight: 3
|
||||||
|
|
||||||
|
Name: L1_Leaving_sad_128x64
|
||||||
|
Min butthurt: 14
|
||||||
|
Max butthurt: 14
|
||||||
|
Min level: 1
|
||||||
|
Max level: 3
|
||||||
|
Weight: 3
|
||||||
|
|
||||||
|
Name: L2_Wake_up_128x64
|
||||||
|
Min butthurt: 0
|
||||||
|
Max butthurt: 12
|
||||||
|
Min level: 2
|
||||||
|
Max level: 3
|
||||||
|
Weight: 4
|
||||||
|
|
||||||
|
Name: L2_Furippa2_128x64
|
||||||
|
Min butthurt: 0
|
||||||
|
Max butthurt: 6
|
||||||
|
Min level: 2
|
||||||
|
Max level: 2
|
||||||
|
Weight: 3
|
||||||
|
|
||||||
|
Name: L2_Hacking_pc_128x64
|
||||||
|
Min butthurt: 0
|
||||||
|
Max butthurt: 8
|
||||||
|
Min level: 2
|
||||||
|
Max level: 2
|
||||||
|
Weight: 3
|
||||||
|
|
||||||
|
Name: L2_Soldering_128x64
|
||||||
|
Min butthurt: 0
|
||||||
|
Max butthurt: 10
|
||||||
|
Min level: 2
|
||||||
|
Max level: 2
|
||||||
|
Weight: 3
|
||||||
|
|
||||||
|
Name: L3_Furippa3_128x64
|
||||||
|
Min butthurt: 0
|
||||||
|
Max butthurt: 6
|
||||||
|
Min level: 3
|
||||||
|
Max level: 3
|
||||||
|
Weight: 3
|
||||||
|
|
||||||
Name: L3_Hijack_radio_128x64
|
Name: L3_Hijack_radio_128x64
|
||||||
Min butthurt: 0
|
Min butthurt: 0
|
||||||
Max butthurt: 8
|
Max butthurt: 8
|
||||||
@@ -99,23 +141,9 @@ Min level: 3
|
|||||||
Max level: 3
|
Max level: 3
|
||||||
Weight: 3
|
Weight: 3
|
||||||
|
|
||||||
Name: L3_Fireplace_128x64
|
Name: L1_Sleigh_ride_128x64
|
||||||
Min butthurt: 0
|
Min butthurt: 0
|
||||||
Max butthurt: 13
|
|
||||||
Min level: 2
|
|
||||||
Max level: 3
|
|
||||||
Weight: 3
|
|
||||||
|
|
||||||
Name: L2_Soldering_128x64
|
|
||||||
Min butthurt: 0
|
|
||||||
Max butthurt: 10
|
|
||||||
Min level: 2
|
|
||||||
Max level: 2
|
|
||||||
Weight: 3
|
|
||||||
|
|
||||||
Name: L1_Leaving_sad_128x64
|
|
||||||
Min butthurt: 14
|
|
||||||
Max butthurt: 14
|
Max butthurt: 14
|
||||||
Min level: 1
|
Min level: 1
|
||||||
Max level: 3
|
Max level: 3
|
||||||
Weight: 3
|
Weight: 4
|
||||||
365
.drone.yml
365
.drone.yml
@@ -1,6 +1,10 @@
|
|||||||
kind: pipeline
|
kind: pipeline
|
||||||
type: docker
|
type: docker
|
||||||
name: "Build firmware"
|
name: "Release firmware"
|
||||||
|
|
||||||
|
platform:
|
||||||
|
os: linux
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: "Update submodules"
|
- name: "Update submodules"
|
||||||
@@ -11,19 +15,63 @@ steps:
|
|||||||
- git submodule foreach git config --local gc.auto 0
|
- git submodule foreach git config --local gc.auto 0
|
||||||
- git log -1 --format='%H'
|
- git log -1 --format='%H'
|
||||||
|
|
||||||
- name: "Build default fw"
|
- name: "Build firmware"
|
||||||
image: hfdj/fztools
|
image: hfdj/fztools
|
||||||
pull: never
|
pull: never
|
||||||
commands:
|
commands:
|
||||||
- export DIST_SUFFIX=${DRONE_TAG}
|
- export DIST_SUFFIX=${DRONE_TAG}
|
||||||
- export WORKFLOW_BRANCH_OR_TAG=dev-cfw
|
- export WORKFLOW_BRANCH_OR_TAG=release-cfw
|
||||||
- ./fbt COMPACT=1 DEBUG=0 updater_package
|
- ./fbt COMPACT=1 DEBUG=0 updater_package
|
||||||
- mkdir artifacts-default
|
- mkdir artifacts-default
|
||||||
- mv dist/f7-C/* artifacts-default/
|
- mv dist/f7-C/* artifacts-default/
|
||||||
- ls -laS artifacts-default
|
- ls -laS artifacts-default
|
||||||
- ls -laS artifacts-default/f7-update-${DRONE_TAG}
|
- ls -laS artifacts-default/f7-update-${DRONE_TAG}
|
||||||
- sed -i 's/(version)/'${DRONE_TAG}'/g' CHANGELOG.md
|
- sed -i 's/(version)/'${DRONE_TAG}'/g' CHANGELOG.md
|
||||||
- echo '# [Install via Web Updater](https://my.flipp.dev/?url=https://unleashedflip.com/builds/flipper-z-f7-update-'${DRONE_TAG}'.tgz&channel=dev-cfw&version='${DRONE_TAG}')' >> CHANGELOG.md
|
- echo '# [Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw/${DRONE_TAG}/flipper-z-f7-update-'${DRONE_TAG}'.tgz&channel=release-cfw&version='${DRONE_TAG}')' >> CHANGELOG.md
|
||||||
|
- echo '' >> CHANGELOG.md
|
||||||
|
- echo '### [Version without custom animations - Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-'${DRONE_TAG}'n.tgz&channel=release-cfw&version='${DRONE_TAG}'n)' >> CHANGELOG.md
|
||||||
|
- echo '' >> CHANGELOG.md
|
||||||
|
- echo '### [Version with extra apps - Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&channel=release-cfw&version='${DRONE_TAG}'e)' >> CHANGELOG.md
|
||||||
|
environment:
|
||||||
|
FBT_TOOLS_CUSTOM_LINK:
|
||||||
|
from_secret: fbt_link
|
||||||
|
|
||||||
|
- name: "Build with extra apps"
|
||||||
|
image: hfdj/fztools
|
||||||
|
pull: never
|
||||||
|
commands:
|
||||||
|
- git clone https://github.com/xMasterX/unleashed-extra-pack.git
|
||||||
|
- cp -R unleashed-extra-pack/apps/* assets/resources/apps/
|
||||||
|
- rm -rf unleashed-extra-pack
|
||||||
|
- export DIST_SUFFIX=${DRONE_TAG}e
|
||||||
|
- export WORKFLOW_BRANCH_OR_TAG=release-cfw
|
||||||
|
- export FORCE_NO_DIRTY=yes
|
||||||
|
- rm -f build/f7-firmware-C/toolbox/version.*
|
||||||
|
- ./fbt COMPACT=1 DEBUG=0 updater_package
|
||||||
|
- mkdir artifacts-extra-apps
|
||||||
|
- mv dist/f7-C/* artifacts-extra-apps/
|
||||||
|
- ls -laS artifacts-extra-apps
|
||||||
|
- ls -laS artifacts-extra-apps/f7-update-${DRONE_TAG}e
|
||||||
|
environment:
|
||||||
|
FBT_TOOLS_CUSTOM_LINK:
|
||||||
|
from_secret: fbt_link
|
||||||
|
|
||||||
|
- name: "Build with ofw anims"
|
||||||
|
image: hfdj/fztools
|
||||||
|
pull: never
|
||||||
|
commands:
|
||||||
|
- rm -f assets/dolphin/external/manifest.txt
|
||||||
|
- cp .ci_files/anims_ofw.txt assets/dolphin/external/manifest.txt
|
||||||
|
- rm -rf assets/resources/apps/
|
||||||
|
- export DIST_SUFFIX=${DRONE_TAG}n
|
||||||
|
- export WORKFLOW_BRANCH_OR_TAG=no-custom-anims
|
||||||
|
- export FORCE_NO_DIRTY=yes
|
||||||
|
- rm -f build/f7-firmware-C/toolbox/version.*
|
||||||
|
- ./fbt COMPACT=1 DEBUG=0 updater_package
|
||||||
|
- mkdir artifacts-ofw-anims
|
||||||
|
- mv dist/f7-C/* artifacts-ofw-anims/
|
||||||
|
- ls -laS artifacts-ofw-anims
|
||||||
|
- ls -laS artifacts-ofw-anims/f7-update-${DRONE_TAG}n
|
||||||
environment:
|
environment:
|
||||||
FBT_TOOLS_CUSTOM_LINK:
|
FBT_TOOLS_CUSTOM_LINK:
|
||||||
from_secret: fbt_link
|
from_secret: fbt_link
|
||||||
@@ -31,14 +79,24 @@ steps:
|
|||||||
- name: "Bundle self-update packages"
|
- name: "Bundle self-update packages"
|
||||||
image: kramos/alpine-zip
|
image: kramos/alpine-zip
|
||||||
commands:
|
commands:
|
||||||
- tar czpf artifacts-default/flipper-z-f7-update-${DRONE_TAG}.tgz -C artifacts-default f7-update-${DRONE_TAG}
|
- cp artifacts-extra-apps/flipper-z-f7-update-${DRONE_TAG}e.tgz .
|
||||||
|
- cp artifacts-ofw-anims/flipper-z-f7-update-${DRONE_TAG}n.tgz .
|
||||||
- cp artifacts-default/flipper-z-f7-update-${DRONE_TAG}.tgz .
|
- cp artifacts-default/flipper-z-f7-update-${DRONE_TAG}.tgz .
|
||||||
|
- zip -r artifacts-extra-apps/flipper-z-f7-update-${DRONE_TAG}e.zip artifacts-extra-apps/f7-update-${DRONE_TAG}e
|
||||||
|
- zip -r artifacts-ofw-anims/flipper-z-f7-update-${DRONE_TAG}n.zip artifacts-ofw-anims/f7-update-${DRONE_TAG}n
|
||||||
- zip -r artifacts-default/flipper-z-f7-update-${DRONE_TAG}.zip artifacts-default/f7-update-${DRONE_TAG}
|
- zip -r artifacts-default/flipper-z-f7-update-${DRONE_TAG}.zip artifacts-default/f7-update-${DRONE_TAG}
|
||||||
|
- tar czpf artifacts-default/flipper-z-any-scripts-${DRONE_TAG}.tgz scripts debug
|
||||||
|
- rm -rf artifacts-extra-apps/f7-update-${DRONE_TAG}
|
||||||
|
- rm -rf artifacts-ofw-anims/f7-update-${DRONE_TAG}
|
||||||
- rm -rf artifacts-default/f7-update-${DRONE_TAG}
|
- rm -rf artifacts-default/f7-update-${DRONE_TAG}
|
||||||
|
- ls -laS artifacts-extra-apps
|
||||||
|
- ls -laS artifacts-ofw-anims
|
||||||
- ls -laS artifacts-default
|
- ls -laS artifacts-default
|
||||||
|
- mv artifacts-default/ ${DRONE_TAG}
|
||||||
|
- ls -laS ${DRONE_TAG}
|
||||||
|
|
||||||
- name: "Upload to updates server"
|
- name: "Upload default to updates srv"
|
||||||
image: appleboy/drone-scp
|
image: appleboy/drone-scp:linux-amd64
|
||||||
settings:
|
settings:
|
||||||
host:
|
host:
|
||||||
from_secret: dep_host
|
from_secret: dep_host
|
||||||
@@ -49,8 +107,44 @@ steps:
|
|||||||
port:
|
port:
|
||||||
from_secret: dep_port
|
from_secret: dep_port
|
||||||
target:
|
target:
|
||||||
from_secret: dep_target
|
from_secret: dep_target_new
|
||||||
source: flipper-z-f7-update-${DRONE_TAG}.tgz
|
source:
|
||||||
|
- ${DRONE_TAG}/*.tgz
|
||||||
|
- ${DRONE_TAG}/*.zip
|
||||||
|
- ${DRONE_TAG}/*.json
|
||||||
|
- ${DRONE_TAG}/*.elf
|
||||||
|
- ${DRONE_TAG}/*.dfu
|
||||||
|
- ${DRONE_TAG}/*.bin
|
||||||
|
|
||||||
|
- name: "Upload no-anims to updates srv"
|
||||||
|
image: appleboy/drone-scp:linux-amd64
|
||||||
|
settings:
|
||||||
|
host:
|
||||||
|
from_secret: dep_host
|
||||||
|
username:
|
||||||
|
from_secret: dep_user
|
||||||
|
password:
|
||||||
|
from_secret: dep_passwd
|
||||||
|
port:
|
||||||
|
from_secret: dep_port
|
||||||
|
target:
|
||||||
|
from_secret: dep_target_noanim
|
||||||
|
source: flipper-z-f7-update-${DRONE_TAG}n.tgz
|
||||||
|
|
||||||
|
- name: "Upload extra apps version to updates srv"
|
||||||
|
image: appleboy/drone-scp:linux-amd64
|
||||||
|
settings:
|
||||||
|
host:
|
||||||
|
from_secret: dep_host
|
||||||
|
username:
|
||||||
|
from_secret: dep_user
|
||||||
|
password:
|
||||||
|
from_secret: dep_passwd
|
||||||
|
port:
|
||||||
|
from_secret: dep_port
|
||||||
|
target:
|
||||||
|
from_secret: dep_target_extra
|
||||||
|
source: flipper-z-f7-update-${DRONE_TAG}e.tgz
|
||||||
|
|
||||||
- name: "Do Github release"
|
- name: "Do Github release"
|
||||||
image: ddplugins/github-release
|
image: ddplugins/github-release
|
||||||
@@ -62,8 +156,10 @@ steps:
|
|||||||
api_key:
|
api_key:
|
||||||
from_secret: github_apikey
|
from_secret: github_apikey
|
||||||
files:
|
files:
|
||||||
- artifacts-default/*.tgz
|
- ${DRONE_TAG}/*.tgz
|
||||||
- artifacts-default/*.zip
|
- ${DRONE_TAG}/*.zip
|
||||||
|
- artifacts-ofw-anims/*.tgz
|
||||||
|
- artifacts-extra-apps/*.tgz
|
||||||
title: ${DRONE_TAG}
|
title: ${DRONE_TAG}
|
||||||
note: CHANGELOG.md
|
note: CHANGELOG.md
|
||||||
checksum:
|
checksum:
|
||||||
@@ -71,6 +167,17 @@ steps:
|
|||||||
- sha1
|
- sha1
|
||||||
- crc32
|
- crc32
|
||||||
|
|
||||||
|
- name: "Trigger update server reindex"
|
||||||
|
image: hfdj/fztools
|
||||||
|
pull: never
|
||||||
|
environment:
|
||||||
|
UPD_KEY:
|
||||||
|
from_secret: git_update_serv_token
|
||||||
|
UPD_URL:
|
||||||
|
from_secret: git_update_server_url
|
||||||
|
commands:
|
||||||
|
- curl -X POST -F 'key='$UPD_KEY'' $UPD_URL
|
||||||
|
|
||||||
- name: "Send files to telegram"
|
- name: "Send files to telegram"
|
||||||
image: appleboy/drone-telegram
|
image: appleboy/drone-telegram
|
||||||
settings:
|
settings:
|
||||||
@@ -85,30 +192,47 @@ steps:
|
|||||||
Version: {{build.tag}}
|
Version: {{build.tag}}
|
||||||
|
|
||||||
|
|
||||||
[-Github-](https://github.com/DarkFlippers/unleashed-firmware/releases/tag/${DRONE_TAG})
|
[-Github - Changelog-](https://github.com/DarkFlippers/unleashed-firmware/releases/tag/${DRONE_TAG})
|
||||||
|
|
||||||
|
|
||||||
[-Install via Web Updater-](https://my.flipp.dev/?url=https://unleashedflip.com/builds/flipper-z-f7-update-${DRONE_TAG}.tgz&channel=dev-cfw&version=${DRONE_TAG})"
|
[-How to install firmware-](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)
|
||||||
|
|
||||||
|
|
||||||
|
[-Download latest extra apps pack-](https://download-directory.github.io/?url=https://github.com/xMasterX/unleashed-extra-pack/tree/main/apps)
|
||||||
|
|
||||||
|
|
||||||
|
[-Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/${DRONE_TAG}/flipper-z-f7-update-${DRONE_TAG}.tgz&channel=release-cfw&version=${DRONE_TAG})
|
||||||
|
|
||||||
|
|
||||||
|
[-Version without custom animations - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-${DRONE_TAG}n.tgz&channel=release-cfw&version=${DRONE_TAG}n)
|
||||||
|
|
||||||
|
|
||||||
|
[-Version with extra apps - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_TAG}e.tgz&channel=release-cfw&version=${DRONE_TAG}e)"
|
||||||
document:
|
document:
|
||||||
- artifacts-default/flipper-z-f7-update-${DRONE_TAG}.tgz
|
- ${DRONE_TAG}/flipper-z-f7-update-${DRONE_TAG}.tgz
|
||||||
|
|
||||||
- name: "Send discord notification"
|
- name: "Send discord notification"
|
||||||
image: appleboy/drone-discord
|
image: hfdj/fztools
|
||||||
|
pull: never
|
||||||
|
environment:
|
||||||
|
DISCORD_WEBHOOK:
|
||||||
|
from_secret: dis_release_webhook
|
||||||
|
commands:
|
||||||
|
- wget "https://raw.githubusercontent.com/fieu/discord.sh/e1dc1a7595efad2cad8f072f0b3531c470f5b7c8/discord.sh"
|
||||||
|
- chmod +x ./discord.sh
|
||||||
|
- ./discord.sh --text 'New Unleashed firmware released!\n\nVersion - '${DRONE_TAG}'\n\n[[Github - Changelog]](https://github.com/DarkFlippers/unleashed-firmware/releases/tag/'${DRONE_TAG}')\n\n[-How to install firmware-](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)\n\n[-Download latest extra apps pack-](https://download-directory.github.io/?url=https://github.com/xMasterX/unleashed-extra-pack/tree/main/apps)\n\n[-Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/'${DRONE_TAG}'/flipper-z-f7-update-'${DRONE_TAG}'.tgz&channel=release-cfw&version='${DRONE_TAG}')\n\n[-Version without custom animations - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-'${DRONE_TAG}'n.tgz&channel=release-cfw&version='${DRONE_TAG}'n)\n\n[-Version with extra apps - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&channel=release-cfw&version='${DRONE_TAG}'e)'
|
||||||
|
|
||||||
|
- name: "Send extra pack build to telegram"
|
||||||
|
image: appleboy/drone-telegram
|
||||||
settings:
|
settings:
|
||||||
webhook_id:
|
token:
|
||||||
from_secret: ds_wh_id
|
from_secret: tgtoken
|
||||||
webhook_token:
|
to:
|
||||||
from_secret: ds_wh_token
|
from_secret: tgid
|
||||||
message: "New Unleashed firmware released!
|
format: markdown
|
||||||
|
message: "Build with extra apps pack:"
|
||||||
|
document:
|
||||||
Version: {{build.tag}}
|
- flipper-z-f7-update-${DRONE_TAG}e.tgz
|
||||||
|
|
||||||
|
|
||||||
[[Github]](https://github.com/DarkFlippers/unleashed-firmware/releases/tag/${DRONE_TAG})
|
|
||||||
|
|
||||||
|
|
||||||
[-Install via Web Updater-](https://my.flipp.dev/?url=https://unleashedflip.com/builds/flipper-z-f7-update-${DRONE_TAG}.tgz&channel=dev-cfw&version=${DRONE_TAG})"
|
|
||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
event:
|
event:
|
||||||
@@ -116,3 +240,186 @@ trigger:
|
|||||||
|
|
||||||
node:
|
node:
|
||||||
typ: haupt
|
typ: haupt
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: "Dev build"
|
||||||
|
|
||||||
|
platform:
|
||||||
|
os: linux
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: "Update submodules"
|
||||||
|
image: alpine/git
|
||||||
|
commands:
|
||||||
|
- git submodule sync
|
||||||
|
- git -c protocol.version=2 submodule update --init --force --recursive
|
||||||
|
- git submodule foreach git config --local gc.auto 0
|
||||||
|
- git log -1 --format='%H'
|
||||||
|
|
||||||
|
- name: "Build dev FW"
|
||||||
|
image: hfdj/fztools
|
||||||
|
pull: never
|
||||||
|
commands:
|
||||||
|
- export DIST_SUFFIX=${DRONE_BUILD_NUMBER}
|
||||||
|
- export WORKFLOW_BRANCH_OR_TAG=dev-cfw
|
||||||
|
- ./fbt COMPACT=1 DEBUG=0 updater_package
|
||||||
|
- mkdir artifacts-default
|
||||||
|
- mv dist/f7-C/* artifacts-default/
|
||||||
|
- ls -laS artifacts-default
|
||||||
|
- ls -laS artifacts-default/f7-update-${DRONE_BUILD_NUMBER}
|
||||||
|
environment:
|
||||||
|
FBT_TOOLS_CUSTOM_LINK:
|
||||||
|
from_secret: fbt_link
|
||||||
|
|
||||||
|
- name: "Build dev with extra apps"
|
||||||
|
image: hfdj/fztools
|
||||||
|
pull: never
|
||||||
|
commands:
|
||||||
|
- git clone https://github.com/xMasterX/unleashed-extra-pack.git
|
||||||
|
- cp -R unleashed-extra-pack/apps/* assets/resources/apps/
|
||||||
|
- rm -rf unleashed-extra-pack
|
||||||
|
- export DIST_SUFFIX=${DRONE_BUILD_NUMBER}e
|
||||||
|
- export WORKFLOW_BRANCH_OR_TAG=dev-cfw
|
||||||
|
- export FORCE_NO_DIRTY=yes
|
||||||
|
- rm -f build/f7-firmware-C/toolbox/version.*
|
||||||
|
- ./fbt COMPACT=1 DEBUG=0 updater_package
|
||||||
|
- mkdir artifacts-extra-apps
|
||||||
|
- mv dist/f7-C/* artifacts-extra-apps/
|
||||||
|
- ls -laS artifacts-extra-apps
|
||||||
|
- ls -laS artifacts-extra-apps/f7-update-${DRONE_BUILD_NUMBER}e
|
||||||
|
environment:
|
||||||
|
FBT_TOOLS_CUSTOM_LINK:
|
||||||
|
from_secret: fbt_link
|
||||||
|
|
||||||
|
- name: "Bundle self-update packages"
|
||||||
|
image: kramos/alpine-zip
|
||||||
|
commands:
|
||||||
|
- cp artifacts-extra-apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}e.tgz .
|
||||||
|
- cp artifacts-default/flipper-z-f7-update-${DRONE_BUILD_NUMBER}.tgz .
|
||||||
|
- rm -rf artifacts-default/f7-update-${DRONE_BUILD_NUMBER}
|
||||||
|
- ls -laS artifacts-default
|
||||||
|
- mv artifacts-default/ dev
|
||||||
|
- ls -laS dev
|
||||||
|
|
||||||
|
- name: "Clean dev folder"
|
||||||
|
image: appleboy/drone-ssh:linux-amd64
|
||||||
|
settings:
|
||||||
|
host:
|
||||||
|
from_secret: dep_host
|
||||||
|
username:
|
||||||
|
from_secret: dep_user
|
||||||
|
password:
|
||||||
|
from_secret: dep_passwd
|
||||||
|
port:
|
||||||
|
from_secret: dep_port
|
||||||
|
command_timeout: 30s
|
||||||
|
script:
|
||||||
|
- cd web/unleashedflip.com/public_html/fw/dev && rm -f ./*
|
||||||
|
|
||||||
|
- name: "Upload default to updates srv"
|
||||||
|
image: appleboy/drone-scp:linux-amd64
|
||||||
|
settings:
|
||||||
|
host:
|
||||||
|
from_secret: dep_host
|
||||||
|
username:
|
||||||
|
from_secret: dep_user
|
||||||
|
password:
|
||||||
|
from_secret: dep_passwd
|
||||||
|
port:
|
||||||
|
from_secret: dep_port
|
||||||
|
target:
|
||||||
|
from_secret: dep_target_new
|
||||||
|
source:
|
||||||
|
- dev/*.tgz
|
||||||
|
- dev/*.zip
|
||||||
|
- dev/*.json
|
||||||
|
- dev/*.elf
|
||||||
|
- dev/*.dfu
|
||||||
|
- dev/*.bin
|
||||||
|
|
||||||
|
- name: "Upload extra apps version to updates srv"
|
||||||
|
image: appleboy/drone-scp:linux-amd64
|
||||||
|
settings:
|
||||||
|
host:
|
||||||
|
from_secret: dep_host
|
||||||
|
username:
|
||||||
|
from_secret: dep_user
|
||||||
|
password:
|
||||||
|
from_secret: dep_passwd
|
||||||
|
port:
|
||||||
|
from_secret: dep_port
|
||||||
|
target:
|
||||||
|
from_secret: dep_target_extra
|
||||||
|
source: flipper-z-f7-update-${DRONE_BUILD_NUMBER}e.tgz
|
||||||
|
|
||||||
|
- name: "Trigger update server reindex"
|
||||||
|
image: hfdj/fztools
|
||||||
|
pull: never
|
||||||
|
environment:
|
||||||
|
UPD_KEY:
|
||||||
|
from_secret: git_update_serv_token
|
||||||
|
UPD_URL:
|
||||||
|
from_secret: git_update_server_url
|
||||||
|
commands:
|
||||||
|
- curl -X POST -F 'key='$UPD_KEY'' $UPD_URL
|
||||||
|
|
||||||
|
- name: "Send files to telegram"
|
||||||
|
image: appleboy/drone-telegram
|
||||||
|
settings:
|
||||||
|
token:
|
||||||
|
from_secret: tgtoken
|
||||||
|
to:
|
||||||
|
from_secret: tgid_dev
|
||||||
|
format: markdown
|
||||||
|
message: "Unleashed firmware dev build successful!
|
||||||
|
|
||||||
|
|
||||||
|
Build: {{build.number}}
|
||||||
|
|
||||||
|
SHA: {{commit.sha}}
|
||||||
|
|
||||||
|
|
||||||
|
Commit: {{commit.message}}
|
||||||
|
|
||||||
|
|
||||||
|
[-Version with extra apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}e.tgz&channel=dev-cfw&version=${DRONE_BUILD_NUMBER}e)
|
||||||
|
|
||||||
|
|
||||||
|
[-Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-${DRONE_BUILD_NUMBER}.tgz&channel=dev-cfw&version=${DRONE_BUILD_NUMBER})"
|
||||||
|
document:
|
||||||
|
- dev/flipper-z-f7-update-${DRONE_BUILD_NUMBER}.tgz
|
||||||
|
|
||||||
|
- name: "Send extra pack build to telegram"
|
||||||
|
image: appleboy/drone-telegram
|
||||||
|
settings:
|
||||||
|
token:
|
||||||
|
from_secret: tgtoken
|
||||||
|
to:
|
||||||
|
from_secret: tgid_dev
|
||||||
|
format: markdown
|
||||||
|
message: "Build with extra apps pack:"
|
||||||
|
document:
|
||||||
|
- flipper-z-f7-update-${DRONE_BUILD_NUMBER}e.tgz
|
||||||
|
|
||||||
|
- name: "Send discord notification"
|
||||||
|
image: hfdj/fztools
|
||||||
|
pull: never
|
||||||
|
environment:
|
||||||
|
DISCORD_WEBHOOK:
|
||||||
|
from_secret: dis_dev_webhook
|
||||||
|
commands:
|
||||||
|
- wget "https://raw.githubusercontent.com/fieu/discord.sh/e1dc1a7595efad2cad8f072f0b3531c470f5b7c8/discord.sh"
|
||||||
|
- chmod +x ./discord.sh
|
||||||
|
- ./discord.sh --text 'Unleashed firmware dev build successful!\n\nBuild - '${DRONE_BUILD_NUMBER}'\n\nSHA - '${DRONE_COMMIT_SHA}'\n\n[-Version with extra apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'e.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}'e)\n\n[-Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}')'
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
branch:
|
||||||
|
- dev
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
|
||||||
|
node:
|
||||||
|
typ: haupt
|
||||||
|
|||||||
6
.github/CODEOWNERS
vendored
6
.github/CODEOWNERS
vendored
@@ -1 +1,5 @@
|
|||||||
* @xMasterX
|
# Default
|
||||||
|
* @xMasterX
|
||||||
|
|
||||||
|
# Assets
|
||||||
|
/assets/resources/infrared/ @xMasterX @amec0e
|
||||||
7
.github/FUNDING.yml
vendored
Normal file
7
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
ko_fi: masterx
|
||||||
|
custom:
|
||||||
|
[
|
||||||
|
"https://boosty.to/mmxdev",
|
||||||
|
"https://pay.cloudtips.ru/p/7b3e9d65",
|
||||||
|
"https://yoomoney.ru/fundraise/XA49mgQLPA0.221209",
|
||||||
|
]
|
||||||
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -4,5 +4,5 @@ contact_links:
|
|||||||
url: https://t.me/flipperzero_unofficial
|
url: https://t.me/flipperzero_unofficial
|
||||||
about: Unofficial Telegram chat
|
about: Unofficial Telegram chat
|
||||||
- name: Discord
|
- name: Discord
|
||||||
url: https://discord.gg/58D6E8BtTU
|
url: https://discord.unleashedflip.com
|
||||||
about: Unofficial Discord Community
|
about: Unofficial Discord Community
|
||||||
|
|||||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,4 +1,5 @@
|
|||||||
*.swp
|
*.swp
|
||||||
|
*.swo
|
||||||
*.gdb_history
|
*.gdb_history
|
||||||
|
|
||||||
|
|
||||||
@@ -30,6 +31,10 @@ Brewfile.lock.json
|
|||||||
# Visual Studio Code
|
# Visual Studio Code
|
||||||
.vscode/
|
.vscode/
|
||||||
|
|
||||||
|
# Kate
|
||||||
|
.kateproject
|
||||||
|
.kateconfig
|
||||||
|
|
||||||
# legendary cmake's
|
# legendary cmake's
|
||||||
build
|
build
|
||||||
CMakeLists.txt
|
CMakeLists.txt
|
||||||
@@ -55,3 +60,5 @@ openocd.log
|
|||||||
# PVS Studio temporary files
|
# PVS Studio temporary files
|
||||||
.PVS-Studio/
|
.PVS-Studio/
|
||||||
PVS-Studio.log
|
PVS-Studio.log
|
||||||
|
|
||||||
|
.gdbinit
|
||||||
|
|||||||
9
.gitmodules
vendored
9
.gitmodules
vendored
@@ -22,12 +22,15 @@
|
|||||||
[submodule "lib/microtar"]
|
[submodule "lib/microtar"]
|
||||||
path = lib/microtar
|
path = lib/microtar
|
||||||
url = https://github.com/amachronic/microtar.git
|
url = https://github.com/amachronic/microtar.git
|
||||||
[submodule "lib/scons"]
|
|
||||||
path = lib/scons
|
|
||||||
url = https://github.com/SCons/scons.git
|
|
||||||
[submodule "lib/mbedtls"]
|
[submodule "lib/mbedtls"]
|
||||||
path = lib/mbedtls
|
path = lib/mbedtls
|
||||||
url = https://github.com/Mbed-TLS/mbedtls.git
|
url = https://github.com/Mbed-TLS/mbedtls.git
|
||||||
[submodule "lib/cxxheaderparser"]
|
[submodule "lib/cxxheaderparser"]
|
||||||
path = lib/cxxheaderparser
|
path = lib/cxxheaderparser
|
||||||
url = https://github.com/robotpy/cxxheaderparser.git
|
url = https://github.com/robotpy/cxxheaderparser.git
|
||||||
|
[submodule "applications/plugins/subbrute"]
|
||||||
|
path = applications/plugins/subbrute
|
||||||
|
url = https://github.com/derskythe/flipperzero-subbrute.git
|
||||||
|
[submodule "applications/plugins/dap_link/lib/free-dap"]
|
||||||
|
path = applications/plugins/dap_link/lib/free-dap
|
||||||
|
url = https://github.com/ataradov/free-dap.git
|
||||||
|
|||||||
24
.pvsconfig
24
.pvsconfig
@@ -1,10 +1,12 @@
|
|||||||
# MLib macros we can't do much about.
|
# MLib macros we can't do much about.
|
||||||
|
//-V:M_LET:1048,1044
|
||||||
//-V:M_EACH:1048,1044
|
//-V:M_EACH:1048,1044
|
||||||
//-V:ARRAY_DEF:760,747,568,776,729,712,654
|
//-V:ARRAY_DEF:760,747,568,776,729,712,654
|
||||||
//-V:LIST_DEF:760,747,568,712,729,654,776
|
//-V:LIST_DEF:760,747,568,712,729,654,776
|
||||||
//-V:BPTREE_DEF2:779,1086,557,773,512
|
//-V:BPTREE_DEF2:779,1086,557,773,512
|
||||||
//-V:DICT_DEF2:779,524,776,760,1044,1001,729,590,568,747,685
|
//-V:DICT_DEF2:779,524,776,760,1044,1001,729,590,568,747,685
|
||||||
//-V:ALGO_DEF:1048,747,1044
|
//-V:ALGO_DEF:1048,747,1044
|
||||||
|
//-V:TUPLE_DEF2:524,590,1001,760
|
||||||
|
|
||||||
# Non-severe malloc/null pointer deref warnings
|
# Non-severe malloc/null pointer deref warnings
|
||||||
//-V::522:2,3
|
//-V::522:2,3
|
||||||
@@ -15,8 +17,30 @@
|
|||||||
# Potentially null argument warnings
|
# Potentially null argument warnings
|
||||||
//-V:memset:575
|
//-V:memset:575
|
||||||
//-V:memcpy:575
|
//-V:memcpy:575
|
||||||
|
//-V:memcmp:575
|
||||||
|
//-V:strlen:575
|
||||||
//-V:strcpy:575
|
//-V:strcpy:575
|
||||||
|
//-V:strncpy:575
|
||||||
//-V:strchr:575
|
//-V:strchr:575
|
||||||
|
|
||||||
# For loop warning on M_FOREACH
|
# For loop warning on M_FOREACH
|
||||||
//-V:for:1044
|
//-V:for:1044
|
||||||
|
|
||||||
|
# Bitwise OR
|
||||||
|
//-V:bit:792
|
||||||
|
|
||||||
|
# Do not complain about similar code
|
||||||
|
//-V::525
|
||||||
|
|
||||||
|
# Common embedded development pointer operations
|
||||||
|
//-V::566
|
||||||
|
//-V::1032
|
||||||
|
|
||||||
|
# Warnings about length mismatch
|
||||||
|
//-V:property_value_out:666
|
||||||
|
|
||||||
|
# Model-related warnings
|
||||||
|
//-V:with_view_model:1044,1048
|
||||||
|
|
||||||
|
# Functions that always return the same error code
|
||||||
|
//-V:picopass_device_decrypt:1048
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
--rules-config .pvsconfig -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/STM32CubeWB -e lib/u8g2 -e */arm-none-eabi/*
|
--ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/STM32CubeWB -e lib/u8g2 -e lib/nanopb -e */arm-none-eabi/* -e applications/plugins/dap_link/lib/free-dap
|
||||||
|
|||||||
2
.vscode/example/c_cpp_properties.json
vendored
2
.vscode/example/c_cpp_properties.json
vendored
@@ -2,7 +2,7 @@
|
|||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"name": "Win32",
|
"name": "Win32",
|
||||||
"compilerPath": "${workspaceFolder}/toolchain/i686-windows/bin/arm-none-eabi-gcc.exe",
|
"compilerPath": "${workspaceFolder}/toolchain/x86_64-windows/bin/arm-none-eabi-gcc.exe",
|
||||||
"intelliSenseMode": "gcc-arm",
|
"intelliSenseMode": "gcc-arm",
|
||||||
"compileCommands": "${workspaceFolder}/build/latest/compile_commands.json",
|
"compileCommands": "${workspaceFolder}/build/latest/compile_commands.json",
|
||||||
"configurationProvider": "ms-vscode.cpptools",
|
"configurationProvider": "ms-vscode.cpptools",
|
||||||
|
|||||||
23
.vscode/example/launch.json
vendored
23
.vscode/example/launch.json
vendored
@@ -38,6 +38,7 @@
|
|||||||
"postAttachCommands": [
|
"postAttachCommands": [
|
||||||
// "compare-sections",
|
// "compare-sections",
|
||||||
"source debug/flipperapps.py",
|
"source debug/flipperapps.py",
|
||||||
|
"fap-set-debug-elf-root build/latest/.extapps",
|
||||||
// "source debug/FreeRTOS/FreeRTOS.py",
|
// "source debug/FreeRTOS/FreeRTOS.py",
|
||||||
// "svd_load debug/STM32WB55_CM4.svd"
|
// "svd_load debug/STM32WB55_CM4.svd"
|
||||||
]
|
]
|
||||||
@@ -59,6 +60,7 @@
|
|||||||
"set confirm off",
|
"set confirm off",
|
||||||
"set mem inaccessible-by-default off",
|
"set mem inaccessible-by-default off",
|
||||||
"source debug/flipperapps.py",
|
"source debug/flipperapps.py",
|
||||||
|
"fap-set-debug-elf-root build/latest/.extapps",
|
||||||
// "compare-sections",
|
// "compare-sections",
|
||||||
]
|
]
|
||||||
// "showDevDebugOutput": "raw",
|
// "showDevDebugOutput": "raw",
|
||||||
@@ -76,9 +78,30 @@
|
|||||||
"rtos": "FreeRTOS",
|
"rtos": "FreeRTOS",
|
||||||
"postAttachCommands": [
|
"postAttachCommands": [
|
||||||
"source debug/flipperapps.py",
|
"source debug/flipperapps.py",
|
||||||
|
"fap-set-debug-elf-root build/latest/.extapps",
|
||||||
]
|
]
|
||||||
// "showDevDebugOutput": "raw",
|
// "showDevDebugOutput": "raw",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Attach FW (DAP)",
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"executable": "./build/latest/firmware.elf",
|
||||||
|
"request": "attach",
|
||||||
|
"type": "cortex-debug",
|
||||||
|
"servertype": "openocd",
|
||||||
|
"device": "cmsis-dap",
|
||||||
|
"svdFile": "./debug/STM32WB55_CM4.svd",
|
||||||
|
"rtos": "FreeRTOS",
|
||||||
|
"configFiles": [
|
||||||
|
"interface/cmsis-dap.cfg",
|
||||||
|
"./debug/stm32wbx.cfg",
|
||||||
|
],
|
||||||
|
"postAttachCommands": [
|
||||||
|
"source debug/flipperapps.py",
|
||||||
|
"fap-set-debug-elf-root build/latest/.extapps",
|
||||||
|
],
|
||||||
|
// "showDevDebugOutput": "raw",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "fbt debug",
|
"name": "fbt debug",
|
||||||
"type": "python",
|
"type": "python",
|
||||||
|
|||||||
6
.vscode/example/settings.json
vendored
6
.vscode/example/settings.json
vendored
@@ -6,13 +6,13 @@
|
|||||||
"cortex-debug.enableTelemetry": false,
|
"cortex-debug.enableTelemetry": false,
|
||||||
"cortex-debug.variableUseNaturalFormat": true,
|
"cortex-debug.variableUseNaturalFormat": true,
|
||||||
"cortex-debug.showRTOS": true,
|
"cortex-debug.showRTOS": true,
|
||||||
"cortex-debug.armToolchainPath.windows": "${workspaceFolder}/toolchain/i686-windows/bin",
|
"cortex-debug.armToolchainPath.windows": "${workspaceFolder}/toolchain/x86_64-windows/bin",
|
||||||
"cortex-debug.armToolchainPath.linux": "${workspaceFolder}/toolchain/x86_64-linux/bin",
|
"cortex-debug.armToolchainPath.linux": "${workspaceFolder}/toolchain/x86_64-linux/bin",
|
||||||
"cortex-debug.armToolchainPath.osx": "${workspaceFolder}/toolchain/x86_64-darwin/bin",
|
"cortex-debug.armToolchainPath.osx": "${workspaceFolder}/toolchain/x86_64-darwin/bin",
|
||||||
"cortex-debug.openocdPath.windows": "${workspaceFolder}/toolchain/i686-windows/openocd/bin/openocd.exe",
|
"cortex-debug.openocdPath.windows": "${workspaceFolder}/toolchain/x86_64-windows/openocd/bin/openocd.exe",
|
||||||
"cortex-debug.openocdPath.linux": "${workspaceFolder}/toolchain/x86_64-linux/openocd/bin/openocd",
|
"cortex-debug.openocdPath.linux": "${workspaceFolder}/toolchain/x86_64-linux/openocd/bin/openocd",
|
||||||
"cortex-debug.openocdPath.osx": "${workspaceFolder}/toolchain/x86_64-darwin/openocd/bin/openocd",
|
"cortex-debug.openocdPath.osx": "${workspaceFolder}/toolchain/x86_64-darwin/openocd/bin/openocd",
|
||||||
"cortex-debug.gdbPath.windows": "${workspaceFolder}/toolchain/i686-windows/bin/arm-none-eabi-gdb-py.bat",
|
"cortex-debug.gdbPath.windows": "${workspaceFolder}/toolchain/x86_64-windows/bin/arm-none-eabi-gdb-py.bat",
|
||||||
"cortex-debug.gdbPath.linux": "${workspaceFolder}/toolchain/x86_64-linux/bin/arm-none-eabi-gdb-py",
|
"cortex-debug.gdbPath.linux": "${workspaceFolder}/toolchain/x86_64-linux/bin/arm-none-eabi-gdb-py",
|
||||||
"cortex-debug.gdbPath.osx": "${workspaceFolder}/toolchain/x86_64-darwin/bin/arm-none-eabi-gdb-py",
|
"cortex-debug.gdbPath.osx": "${workspaceFolder}/toolchain/x86_64-darwin/bin/arm-none-eabi-gdb-py",
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
|
|||||||
54
.vscode/example/tasks.json
vendored
54
.vscode/example/tasks.json
vendored
@@ -105,17 +105,23 @@
|
|||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "./fbt COMPACT=1 DEBUG=0 FORCE=1 flash_usb_full"
|
"command": "./fbt COMPACT=1 DEBUG=0 FORCE=1 flash_usb_full"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "[Debug] Create PVS-Studio report",
|
||||||
|
"group": "build",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "./fbt firmware_pvs"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "[Debug] Build FAPs",
|
"label": "[Debug] Build FAPs",
|
||||||
"group": "build",
|
"group": "build",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "./fbt plugin_dist"
|
"command": "./fbt fap_dist"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "[Release] Build FAPs",
|
"label": "[Release] Build FAPs",
|
||||||
"group": "build",
|
"group": "build",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "./fbt COMPACT=1 DEBUG=0 plugin_dist"
|
"command": "./fbt COMPACT=1 DEBUG=0 fap_dist"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "[Debug] Launch App on Flipper",
|
"label": "[Debug] Launch App on Flipper",
|
||||||
@@ -128,6 +134,50 @@
|
|||||||
"group": "build",
|
"group": "build",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "./fbt COMPACT=1 DEBUG=0 launch_app APPSRC=${relativeFileDirname}"
|
"command": "./fbt COMPACT=1 DEBUG=0 launch_app APPSRC=${relativeFileDirname}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "[Debug] Launch App on Flipper with Serial Console",
|
||||||
|
"dependsOrder": "sequence",
|
||||||
|
"group": "build",
|
||||||
|
"dependsOn": [
|
||||||
|
"[Debug] Launch App on Flipper",
|
||||||
|
"Serial Console"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "[Debug] Build and upload all FAPs to Flipper over USB",
|
||||||
|
"group": "build",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "./fbt fap_deploy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "[Release] Build and upload all FAPs to Flipper over USB",
|
||||||
|
"group": "build",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "./fbt COMPACT=1 DEBUG=0 fap_deploy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Press Ctrl+] to quit
|
||||||
|
"label": "Serial Console",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "./fbt cli",
|
||||||
|
"group": "none",
|
||||||
|
"isBackground": true,
|
||||||
|
"options": {
|
||||||
|
"env": {
|
||||||
|
"FBT_NO_SYNC": "0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"presentation": {
|
||||||
|
"reveal": "always",
|
||||||
|
"revealProblems": "never",
|
||||||
|
"showReuseMessage": false,
|
||||||
|
"panel": "dedicated",
|
||||||
|
"focus": true,
|
||||||
|
"echo": true,
|
||||||
|
"close": true,
|
||||||
|
"group": "Logger"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
7
.vscode/extensions.json
vendored
7
.vscode/extensions.json
vendored
@@ -11,5 +11,8 @@
|
|||||||
"augustocdias.tasks-shell-input"
|
"augustocdias.tasks-shell-input"
|
||||||
],
|
],
|
||||||
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
||||||
"unwantedRecommendations": []
|
"unwantedRecommendations": [
|
||||||
}
|
"twxs.cmake",
|
||||||
|
"ms-vscode.cmake-tools"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|||||||
67
CHANGELOG.md
67
CHANGELOG.md
@@ -1,53 +1,44 @@
|
|||||||
### New changes
|
### New changes
|
||||||
* Issues with hopping in subghz are fixed in this release
|
* SubGHz: **Nice ON2E (Nice One)** support (by @assasinfil | PR #335)
|
||||||
* OFW PR: Desktop: Set external apps as favorites (OFW PR 1816 by @djsime1) (and fixed forgotten furi_record_close)
|
* SubGHz: Remove 467.75 From freq analyzer since it has too much noise (Frequency is still can be used, just excluded from FA to avoid false detections)
|
||||||
* PR -> Plugins: Add CAME 12bit 303MHz to SubGHz Bruteforcer (by @derskythe | PR #87)
|
* Archive and FileBrowser: **Fixed more navigation issues** (by @Willy-JL | PR #334)
|
||||||
* PR -> BadUSB: Added Norwegian keyboard layout for BadUSB (by @jd-raymaker | PR #88)
|
* Plugins -> SubGHz Bruteforcer: Fix Linear Delta 3 repeats (now its more stable and we will be sure signal is received correctly)
|
||||||
* PR -> Plugins: Feature: allow to set ball speed in Arkanoid (by @an4tur0r | PR #92)
|
* Plugins: Updated TOTP (Authenticator) [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator)
|
||||||
* Add USB Keyboard (& Mouse) plugin, replacing default USB Mouse demo included (plugin by @huuck) [Link to original](https://github.com/huuck/FlipperZeroUSBKeyboard)
|
* OFW: **Fix Cyfral & Metakom emulation (My temp fix removed and proper fix from OFW applied)**
|
||||||
* Fix USB Keyboard plugin wrong icon in mouse screen, rewrite view models to new type
|
* OFW: BadUSB: disable CDC mode, USB mode switch fix
|
||||||
* Updated universal remote assets (by @Amec0e)
|
* OFW: Updater visual fixes
|
||||||
* Plugins: SubGHz Bruteforcer - Fix wrong max value in BF existing dump
|
|
||||||
* API 3.0 -> 3.2 (all previous compiled apps still compatible)
|
|
||||||
* Add 312.2 MHz to subghz user config
|
|
||||||
* SubGHz: Fix double click after delete scene, fix rename bug
|
|
||||||
* SubGHz: proper free of rainbow tables
|
|
||||||
* SubGHz: Fixed stability issues with Came atomo, Nice Flor S, limited max history items to 60 (was 65)
|
|
||||||
* SubGHz: Fix Read screen GUI (still bugged in OFW)
|
|
||||||
* Adapted all plugins and other code to new FuriString, fixed all new issues with new string type
|
|
||||||
* Adapted all plugins to new printf format
|
|
||||||
* Adapted all plugins to new view model format (aka Removing lambdas)
|
|
||||||
* Adapted all plugins to new furi_stream
|
|
||||||
* OFW: Elf loader: do not load .ARM.* sections
|
|
||||||
* OFW: Removing lambdas
|
|
||||||
* OFW: BadUSB: add SYSRQ keys
|
|
||||||
* OFW: Gui: fix memory leak in file browser module
|
|
||||||
* OFW: music_player: Return to browser instead of exiting on back button
|
|
||||||
* OFW: More correct elf loader
|
|
||||||
* OFW: Furi stream buffer
|
|
||||||
* OFW: Printf function attributes
|
|
||||||
* OFW: App name in CLI loader command, RFID data edit fix
|
|
||||||
* OFW: Show in-app icons & names in archive browser
|
|
||||||
* OFW: M*LIB: non-inlined strings, FuriString primitive
|
|
||||||
* OFW: Remove string_push_uint64
|
|
||||||
* OFW: Mifare Classic read improvements
|
|
||||||
* OFW PR: updated icon names (OFW PR 1829 by nminaylov)
|
|
||||||
|
|
||||||
#### [🎲 Download extra apps pack](https://download-directory.github.io/?url=https://github.com/UberGuidoZ/Flipper/tree/main/Applications/Unleashed)
|
#### [🎲 Download latest extra apps pack](https://download-directory.github.io/?url=https://github.com/xMasterX/unleashed-extra-pack/tree/main/apps)
|
||||||
|
|
||||||
[-> How to install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)
|
[-> How to install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)
|
||||||
|
|
||||||
[-> Download qFlipper 1.2.1 (allows .tgz installation) (official link)](https://update.flipperzero.one/builds/qFlipper/1.2.1/)
|
[-> Download qFlipper (official link)](https://flipperzero.one/update)
|
||||||
|
|
||||||
## Please support development of the project
|
## Please support development of the project
|
||||||
* ETH/BSC/ERC20-Tokens: `0xFebF1bBc8229418FF2408C07AF6Afa49152fEc6a`
|
* Boosty: https://boosty.to/mmxdev
|
||||||
|
* Ko-Fi: https://ko-fi.com/masterx
|
||||||
|
* cloudtips (only RU payments accepted): https://pay.cloudtips.ru/p/7b3e9d65
|
||||||
|
* YooMoney (only RU payments accepted): https://yoomoney.ru/fundraise/XA49mgQLPA0.221209
|
||||||
|
* USDT(TRC20): `TSXcitMSnWXUFqiUfEXrTVpVewXy2cYhrs`
|
||||||
|
* BCH: `qquxfyzntuqufy2dx0hrfr4sndp0tucvky4sw8qyu3`
|
||||||
|
* ETH/BSC/ERC20-Tokens: `darkflippers.eth` (or `0xFebF1bBc8229418FF2408C07AF6Afa49152fEc6a`)
|
||||||
* BTC: `bc1q0np836jk9jwr4dd7p6qv66d04vamtqkxrecck9`
|
* BTC: `bc1q0np836jk9jwr4dd7p6qv66d04vamtqkxrecck9`
|
||||||
* DOGE: `D6R6gYgBn5LwTNmPyvAQR6bZ9EtGgFCpvv`
|
* DOGE: `D6R6gYgBn5LwTNmPyvAQR6bZ9EtGgFCpvv`
|
||||||
* LTC: `ltc1q3ex4ejkl0xpx3znwrmth4lyuadr5qgv8tmq8z9`
|
* LTC: `ltc1q3ex4ejkl0xpx3znwrmth4lyuadr5qgv8tmq8z9`
|
||||||
|
* XMR (Monero): `41xUz92suUu1u5Mu4qkrcs52gtfpu9rnZRdBpCJ244KRHf6xXSvVFevdf2cnjS7RAeYr5hn9MsEfxKoFDRSctFjG5fv1Mhn`
|
||||||
|
* TON: `EQCOqcnYkvzOZUV_9bPE_8oTbOrOF03MnF-VcJyjisTZmpGf`
|
||||||
|
|
||||||
|
### Thanks to our sponsors:
|
||||||
|
callmezimbra, Quen0n, MERRON, grvpvl (lvpvrg), art_col, ThurstonWaffles, Moneron, UterGrooll, LUCFER, Northpirate, zloepuzo, T.Rat, Alexey B., ionelife, ...
|
||||||
|
and all other great people who supported our project and me (xMasterX), thanks to you all!
|
||||||
|
|
||||||
**Note: To avoid issues with .dfu, prefer installing using .tgz with qFlipper, web updater or by self update package, all needed assets will be installed**
|
**Note: To avoid issues with .dfu, prefer installing using .tgz with qFlipper, web updater or by self update package, all needed assets will be installed**
|
||||||
|
|
||||||
Self-update package (update from microSD) - `flipper-z-f7-update-(version).zip` or download `.tgz` for iOS mobile app / qFlipper
|
**Recommended option - Web Updater**
|
||||||
|
|
||||||
|
What means `n` or `e` in - `flipper-z-f7-update-(version)(n / e).tgz` ? - `n` means this build comes without our custom animations, only official flipper animations,
|
||||||
|
`e` means build has extra apps pack preinstalled
|
||||||
|
|
||||||
|
Self-update package (update from microSD) - `flipper-z-f7-update-(version).zip` or download `.tgz` for mobile app / qFlipper
|
||||||
|
|
||||||
Update using qFlipper (1.2.0+) is now possible with `.tgz` update package! Also you can use Web Updater or self-update package.
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,15 +3,15 @@
|
|||||||
Nice to see you reading this document, we really appreciate it.
|
Nice to see you reading this document, we really appreciate it.
|
||||||
|
|
||||||
As all documents of this kind it's unable to cover everything.
|
As all documents of this kind it's unable to cover everything.
|
||||||
But it will cover general rules that we enforcing on PR review.
|
But it will cover general rules that we are enforcing on PR review.
|
||||||
|
|
||||||
Also we already have automatic rules checking and formatting,
|
Also, we already have automatic rules checking and formatting,
|
||||||
but it got it's limitations and this guide is still mandatory.
|
but it got its limitations and this guide is still mandatory.
|
||||||
|
|
||||||
Some part of this project do have it's own naming and coding guides.
|
Some part of this project do have its own naming and coding guides.
|
||||||
For example: assets. Take a look into `ReadMe.md` in assets folder for more details.
|
For example: assets. Take a look into `ReadMe.md` in assets folder for more details.
|
||||||
|
|
||||||
Also 3rd party libraries are none of our concern.
|
Also, 3rd party libraries are none of our concern.
|
||||||
|
|
||||||
And yes, this set is not final and we are open to discussion.
|
And yes, this set is not final and we are open to discussion.
|
||||||
If you want to add/remove/change something here please feel free to open new ticket.
|
If you want to add/remove/change something here please feel free to open new ticket.
|
||||||
@@ -30,7 +30,7 @@ Our guide is inspired by, but not claiming to be compatible with:
|
|||||||
|
|
||||||
Code we write is intended to be public.
|
Code we write is intended to be public.
|
||||||
Avoid one-liners from hell and keep code complexity under control.
|
Avoid one-liners from hell and keep code complexity under control.
|
||||||
Try to make code self explanatory and add comments if needed.
|
Try to make code self-explanatory and add comments if needed.
|
||||||
Leave references to standards that you are implementing.
|
Leave references to standards that you are implementing.
|
||||||
Use project wiki to document new/reverse engineered standards.
|
Use project wiki to document new/reverse engineered standards.
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ Almost everything in flipper firmware is built around this concept.
|
|||||||
|
|
||||||
## Naming
|
## Naming
|
||||||
|
|
||||||
### Type names are CamelCase
|
### Type names are PascalCase
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ Enforced by linter.
|
|||||||
Suffixes:
|
Suffixes:
|
||||||
|
|
||||||
- `alloc` - allocate and init instance. C style constructor. Returns pointer to instance.
|
- `alloc` - allocate and init instance. C style constructor. Returns pointer to instance.
|
||||||
- `free` - deinit and release instance. C style destructor. Takes pointer to instance.
|
- `free` - de-init and release instance. C style destructor. Takes pointer to instance.
|
||||||
|
|
||||||
# C++ coding style
|
# C++ coding style
|
||||||
|
|
||||||
|
|||||||
21
Makefile
21
Makefile
@@ -1,21 +0,0 @@
|
|||||||
$(info +-------------------------------------------------+)
|
|
||||||
$(info | |)
|
|
||||||
$(info | Hello, this is Flipper team speaking! |)
|
|
||||||
$(info | |)
|
|
||||||
$(info | We've migrated to new build system |)
|
|
||||||
$(info | It's nice and based on scons |)
|
|
||||||
$(info | |)
|
|
||||||
$(info | Crash course: |)
|
|
||||||
$(info | |)
|
|
||||||
$(info | `./fbt` |)
|
|
||||||
$(info | `./fbt flash` |)
|
|
||||||
$(info | `./fbt debug` |)
|
|
||||||
$(info | |)
|
|
||||||
$(info | More details in documentation/fbt.md |)
|
|
||||||
$(info | |)
|
|
||||||
$(info | Also Please leave your feedback here: |)
|
|
||||||
$(info | https://flipp.dev/4RDu |)
|
|
||||||
$(info | or |)
|
|
||||||
$(info | https://flipp.dev/2XM8 |)
|
|
||||||
$(info | |)
|
|
||||||
$(info +-------------------------------------------------+)
|
|
||||||
174
ReadMe.md
174
ReadMe.md
@@ -4,122 +4,204 @@
|
|||||||
</a>
|
</a>
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
### Welcome to Flipper Zero Unleashed Firmware repo!
|
### Welcome to the Flipper Zero Unleashed Firmware repo!
|
||||||
Our goal is to make any features possible in this device without any limitations!
|
|
||||||
|
|
||||||
Please help us implement emulation for all subghz dynamic (rolling code) protocols!
|
**This firmware is a fork from** [flipperdevices/flipperzero-firmware](https://github.com/flipperdevices/flipperzero-firmware)
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
### This software is for experimental purposes only and is not meant for any illegal activity/purposes. <br> We do not condone illegal activity and strongly encourage keeping transmissions to legal/valid uses allowed by law. <br> Also this software is made without any support from Flipper Devices and in no way related to official devs.
|
Our goal is to make all features possible on this device without any limitations!
|
||||||
|
|
||||||
|
Please help us implement emulation for all Sub-GHz dynamic (rolling code) protocols!
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
### This software is for experimental purposes only and is not meant for any illegal activity/purposes. <br> We do not condone illegal activity and strongly encourage keeping transmissions to legal/valid uses allowed by law. <br> Also, this software is made without any support from Flipper Devices and is in no way related to the official devs.
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
Our Discord Community:
|
Our Discord Community:
|
||||||
<br>
|
<br>
|
||||||
<a href="https://discord.gg/flipperzero-unofficial"><img src="https://discordapp.com/api/guilds/937479784148115456/widget.png?style=banner4" alt="Unofficial Discord Community"></a>
|
<a href="https://discord.unleashedflip.com"><img src="https://discordapp.com/api/guilds/937479784148115456/widget.png?style=banner4" alt="Unofficial Discord Community" target="_blank"></a>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
|
## Dev builds
|
||||||
|
- https://dev.unleashedflip.com/
|
||||||
|
- https://t.me/kotnehleb
|
||||||
|
## Releases in Telegram
|
||||||
|
- https://t.me/unleashed_fw
|
||||||
|
|
||||||
# What's changed
|
# What's changed
|
||||||
* SubGHz regional TX restrictions removed
|
* Sub-GHz regional TX restrictions removed
|
||||||
* SubGHz frequecy range can be extended in settings file (Warning: It can damage flipper's hardware)
|
* Sub-GHz frequency range can be extended in settings file (Warning: It can damage Flipper's hardware)
|
||||||
* Many rolling code protocols now have the ability to save & send captured signals
|
* Many rolling code protocols now have the ability to save & send captured signals
|
||||||
* FAAC SLH (Spa) & BFT Mitto (secure with seed) manual creation
|
* FAAC SLH (Spa) & BFT Mitto (secure with seed) manual creation
|
||||||
* Sub-GHz static code brute-force plugin
|
* Sub-GHz static code brute-force plugin
|
||||||
* LFRFID Fuzzer plugin
|
* LFRFID Fuzzer plugin
|
||||||
* Custom community plugins and games added
|
* Custom community plugins and games added + all known working apps can be downloaded in extra pack in every release
|
||||||
* Extra SubGHz frequencies + extra Mifare Classic keys
|
* Extra Sub-GHz frequencies + extra Mifare Classic keys
|
||||||
* Picopass/iClass plugin included in releases
|
* Picopass/iClass plugin included in releases
|
||||||
* Recompiled IR TV Universal Remote for ALL buttons
|
* Recompiled IR TV Universal Remote for ALL buttons
|
||||||
* Universal remote for Projectors, Fans, A/Cs and Audio(soundbars, etc.)
|
* Universal remote for Projectors, Fans, A/Cs and Audio(soundbars, etc.)
|
||||||
* BadUSB keyboard layouts
|
* BadUSB keyboard layouts
|
||||||
* Customizable Flipper name
|
* Customizable Flipper name
|
||||||
* SubGHz -> Press OK in frequency analyzer to use detected frequency in Read modes
|
* Sub-GHz -> Press OK in frequency analyzer to use detected frequency in Read modes
|
||||||
* SubGHz -> Long press OK button in SubGHz Frequency analyzer to switch to Read menu
|
* Sub-GHz -> Long press OK button in Sub-GHz Frequency analyzer to switch to Read menu
|
||||||
|
* Sub-GHz -> External CC1101 module support
|
||||||
* Other small fixes and changes throughout
|
* Other small fixes and changes throughout
|
||||||
* See other changes in changelog and in readme below
|
* See other changes in changelog and in readme below
|
||||||
|
|
||||||
Also check changelog in releases for latest updates!
|
Also check the changelog in releases for latest updates!
|
||||||
|
|
||||||
### Current modified and new SubGHz protocols list:
|
### Current modified and new Sub-GHz protocols list:
|
||||||
- HCS101
|
Thanks to Official team (to thier SubGHz Developer, Skorp) for implementing decoders for these protocols.
|
||||||
- An-Motors
|
|
||||||
|
Encoders/sending made by Eng1n33r & @xMasterX:
|
||||||
|
|
||||||
|
- Keeloq [Not ALL systems supported for decode or emulation yet!] - [Supported manufacturers list](https://0bin.net/paste/VwR2lNJY#WH9vnPgvcp7w6zVKucFCuNREKAcOij8KsJ6vqLfMn3b)
|
||||||
|
- Keeloq: HCS101
|
||||||
|
- Keeloq: AN-Motors
|
||||||
|
- Keeloq: JCM Tech
|
||||||
|
- Keeloq: MHouse
|
||||||
|
- Keeloq: Nice Smilo
|
||||||
|
- Keeloq: DTM Neo
|
||||||
|
- Keeloq: FAAC RC,XT
|
||||||
|
- Keeloq: Mutancode
|
||||||
|
- Keeloq: Normstahl
|
||||||
- CAME Atomo
|
- CAME Atomo
|
||||||
- FAAC SLH (Spa) [External seed calculation required]
|
|
||||||
- BFT Mitto [External seed calculation required]
|
|
||||||
- Keeloq [Not ALL systems supported yet!]
|
|
||||||
- Nice Flor S
|
- Nice Flor S
|
||||||
|
- FAAC SLH (Spa) [External seed calculation required (For info contact me in Discord: Nano#8998)]
|
||||||
|
- BFT Mitto [External seed calculation required (For info contact me in Discord: Nano#8998)]
|
||||||
- Security+ v1 & v2
|
- Security+ v1 & v2
|
||||||
- Star Line (saving only)
|
- Star Line
|
||||||
|
|
||||||
## Support us so we can buy equipment and develop new features
|
Encoders made by @assasinfil & @xMasterX:
|
||||||
* ETH/BSC/ERC20-Tokens: `0xFebF1bBc8229418FF2408C07AF6Afa49152fEc6a`
|
- Somfy Telis
|
||||||
|
- Somfy Keytis
|
||||||
|
- KingGates Stylo 4k
|
||||||
|
- Alutech AT-4N
|
||||||
|
- Nice ON2E (Nice One)
|
||||||
|
|
||||||
|
## Please support development of the project
|
||||||
|
The majority of this project is developed and maintained by me, @xMasterX.
|
||||||
|
I'm unemployed because of the war, and the only income I receive is from your donations.
|
||||||
|
Our team is small and the guys are working on this project as much as they can solely based on the enthusiasm they have for this project and the community.
|
||||||
|
- @assasinfil - SubGHz
|
||||||
|
- @Svaarich - UI design and animations
|
||||||
|
- @Amec0e - Infrared assets
|
||||||
|
- Community moderators in Telegram, Discord, and Reddit
|
||||||
|
- And of course our GitHub community. Your PRs are a very important part of this firmware and open-source development.
|
||||||
|
|
||||||
|
The amount of work done on this project is huge and we need your support, no matter how large or small. Even if you just say, "Thank you Unleashed firmware developers!" somewhere. Doing so will help us continue our work and will help drive us to make the firmware better every time.
|
||||||
|
Also, regarding our releases, every build has and always will be free and open-source. There will be no paywall releases or closed-source apps within the firmware. As long as I am working on this project it will never happen.
|
||||||
|
You can support us by using links or addresses below:
|
||||||
|
* Boosty: https://boosty.to/mmxdev
|
||||||
|
* Ko-Fi: https://ko-fi.com/masterx
|
||||||
|
* cloudtips (only RU payments accepted): https://pay.cloudtips.ru/p/7b3e9d65
|
||||||
|
* YooMoney (only RU payments accepted): https://yoomoney.ru/fundraise/XA49mgQLPA0.221209
|
||||||
|
* USDT(TRC20): `TSXcitMSnWXUFqiUfEXrTVpVewXy2cYhrs`
|
||||||
|
* BCH: `qquxfyzntuqufy2dx0hrfr4sndp0tucvky4sw8qyu3`
|
||||||
|
* ETH/BSC/ERC20-Tokens: `darkflippers.eth` (or `0xFebF1bBc8229418FF2408C07AF6Afa49152fEc6a`)
|
||||||
* BTC: `bc1q0np836jk9jwr4dd7p6qv66d04vamtqkxrecck9`
|
* BTC: `bc1q0np836jk9jwr4dd7p6qv66d04vamtqkxrecck9`
|
||||||
* DOGE: `D6R6gYgBn5LwTNmPyvAQR6bZ9EtGgFCpvv`
|
* DOGE: `D6R6gYgBn5LwTNmPyvAQR6bZ9EtGgFCpvv`
|
||||||
* LTC: `ltc1q3ex4ejkl0xpx3znwrmth4lyuadr5qgv8tmq8z9`
|
* LTC: `ltc1q3ex4ejkl0xpx3znwrmth4lyuadr5qgv8tmq8z9`
|
||||||
|
* XMR (Monero): `41xUz92suUu1u5Mu4qkrcs52gtfpu9rnZRdBpCJ244KRHf6xXSvVFevdf2cnjS7RAeYr5hn9MsEfxKoFDRSctFjG5fv1Mhn`
|
||||||
|
* TON: `EQCOqcnYkvzOZUV_9bPE_8oTbOrOF03MnF-VcJyjisTZmpGf`
|
||||||
|
|
||||||
### Community apps included:
|
### Community apps included:
|
||||||
|
|
||||||
- RFID Fuzzer plugin [(by Ganapati)](https://github.com/DarkFlippers/unleashed-firmware/pull/54) with changes by @xMasterX & New protocols by @mvanzanten
|
- **RFID Fuzzer** [(by Ganapati & @xMasterX)](https://github.com/DarkFlippers/unleashed-firmware/pull/54) & New protocols by @mvanzanten
|
||||||
- Sub-GHz bruteforce plugin [(by Ganapati & xMasterX)](https://github.com/DarkFlippers/unleashed-firmware/pull/57) & Refactored by @derskythe
|
- **Sub-GHz bruteforcer** [(by @derskythe & xMasterX)](https://github.com/derskythe/flipperzero-subbrute) [(original by Ganapati & xMasterX)](https://github.com/DarkFlippers/unleashed-firmware/pull/57)
|
||||||
- Sub-GHz playlist plugin [(by darmiel)](https://github.com/DarkFlippers/unleashed-firmware/pull/62)
|
- **Sub-GHz playlist** [(by darmiel)](https://github.com/DarkFlippers/unleashed-firmware/pull/62)
|
||||||
- ESP8266 Deauther plugin [(by SequoiaSan)](https://github.com/SequoiaSan/FlipperZero-Wifi-ESP8266-Deauther-Module)
|
- ESP8266 Deauther plugin [(by SequoiaSan)](https://github.com/SequoiaSan/FlipperZero-Wifi-ESP8266-Deauther-Module)
|
||||||
- WiFi Scanner plugin [(by SequoiaSan)](https://github.com/SequoiaSan/FlipperZero-WiFi-Scanner_Module)
|
- WiFi Scanner plugin [(by SequoiaSan)](https://github.com/SequoiaSan/FlipperZero-WiFi-Scanner_Module)
|
||||||
- MultiConverter plugin [(by theisolinearchip)](https://github.com/theisolinearchip/flipperzero_stuff)
|
- MultiConverter plugin [(by theisolinearchip)](https://github.com/theisolinearchip/flipperzero_stuff)
|
||||||
- USB Keyboard plugin [(by huuck)](https://github.com/huuck/FlipperZeroUSBKeyboard)
|
- USB Keyboard plugin [(by huuck)](https://github.com/huuck/FlipperZeroUSBKeyboard)
|
||||||
- WAV player plugin (fixed) [(OFW: DrZlo13)](https://github.com/flipperdevices/flipperzero-firmware/tree/zlo/wav-player)
|
- WAV Player [(OFW: DrZlo13)](https://github.com/flipperdevices/flipperzero-firmware/tree/zlo/wav-player) - Fixed and improved by [LTVA1](https://github.com/LTVA1/wav_player)
|
||||||
- UPC-A Barcode generator plugin [(by McAzzaMan)](https://github.com/McAzzaMan/flipperzero-firmware/tree/UPC-A_Barcode_Generator/applications/barcode_generator)
|
- Barcode generator plugin [(original by McAzzaMan)](https://github.com/McAzzaMan/flipperzero-firmware/tree/UPC-A_Barcode_Generator/applications/barcode_generator) - [EAN-8 and refactoring](https://github.com/DarkFlippers/unleashed-firmware/pull/154) by @msvsergey
|
||||||
- GPIO: Sentry Safe plugin [(by H4ckd4ddy)](https://github.com/H4ckd4ddy/flipperzero-sentry-safe-plugin)
|
- GPIO: Sentry Safe plugin [(by H4ckd4ddy)](https://github.com/H4ckd4ddy/flipperzero-sentry-safe-plugin)
|
||||||
- ESP32: WiFi Marauder companion plugin [(by 0xchocolate)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion)
|
- ESP32: WiFi Marauder companion plugin [(by 0xchocolate)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion)
|
||||||
- NRF24: Sniffer & MouseJacker (with changes) [(by mothball187)](https://github.com/mothball187/flipperzero-nrf24/tree/main/mousejacker)
|
- NRF24: Sniffer & MouseJacker (with changes) [(by mothball187)](https://github.com/mothball187/flipperzero-nrf24/tree/main/mousejacker)
|
||||||
- Simple Clock (timer by GMMan / settings by kowalski7cc) [(Original by CompaqDisc)](https://gist.github.com/CompaqDisc/4e329c501bd03c1e801849b81f48ea61)
|
- Simple Clock (timer by GMMan) [(original by CompaqDisc)](https://gist.github.com/CompaqDisc/4e329c501bd03c1e801849b81f48ea61)
|
||||||
- UniversalRF Remix / Sub-GHz Remote [(by ESurge)](https://github.com/ESurge/flipperzero-firmware-unirfremix)[(updated and all protocol support added by darmiel & xMasterX)](https://github.com/darmiel/flipper-playlist/tree/feat/unirf-protocols)
|
- **Sub-GHz Remote** (UniversalRF Remix) [(by @darmiel & @xMasterX)](https://github.com/darmiel/flipper-playlist/tree/feat/unirf-protocols) (original by @ESurge)
|
||||||
- Spectrum Analyzer (with changes) [(by jolcese)](https://github.com/jolcese/flipperzero-firmware/tree/spectrum/applications/spectrum_analyzer) - [Ultra Narrow mode & scan channels non-consecutively](https://github.com/theY4Kman/flipperzero-firmware/commits?author=theY4Kman)
|
- Spectrum Analyzer (with changes) [(by jolcese)](https://github.com/jolcese/flipperzero-firmware/tree/spectrum/applications/spectrum_analyzer) - [Ultra Narrow mode & scan channels non-consecutively](https://github.com/theY4Kman/flipperzero-firmware/commits?author=theY4Kman)
|
||||||
|
- Metronome [(by panki27)](https://github.com/panki27/Metronome)
|
||||||
|
- DTMF Dolphin [(by litui)](https://github.com/litui/dtmf_dolphin)
|
||||||
|
- **TOTP (Authenticator)** [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator)
|
||||||
|
- GPS [(by ezod)](https://github.com/ezod/flipperzero-gps) works with module `NMEA 0183` via UART (13TX, 14RX, GND pins on Flipper)
|
||||||
|
- i2c Tools [(by NaejEL)](https://github.com/NaejEL/flipperzero-i2ctools) - C0 -> SCL / C1 -> SDA / GND -> GND | 3v3 logic levels only!
|
||||||
|
- HC-SR04 Distance sensor - Ported and modified by @xMasterX [(original by Sanqui)](https://github.com/Sanqui/flipperzero-firmware/tree/hc_sr04) - How to connect -> (5V -> VCC) / (GND -> GND) / (13|TX -> Trig) / (14|RX -> Echo)
|
||||||
|
- Morse Code [(by wh00hw)](https://github.com/wh00hw/MorseCodeFAP)
|
||||||
|
- **Unitemp - Temperature sensors reader** (DHT11/22, DS18B20, BMP280, HTU21x and more) [(by quen0n)](https://github.com/quen0n/unitemp-flipperzero)
|
||||||
|
- BH1750 - Lightmeter [(by oleksiikutuzov)](https://github.com/oleksiikutuzov/flipperzero-lightmeter)
|
||||||
|
- **iButton Fuzzer** [(by xMasterX)](https://github.com/xMasterX/ibutton-fuzzer)
|
||||||
|
- HEX Viewer [(by QtRoS)](https://github.com/QtRoS/flipper-zero-hex-viewer)
|
||||||
|
- POCSAG Pager [(by xMasterX & Shmuma)](https://github.com/xMasterX/flipper-pager)
|
||||||
|
- Text Viewer [(by kowalski7cc & kyhwana)](https://github.com/kowalski7cc/flipper-zero-text-viewer/tree/refactor-text-app)
|
||||||
|
- **UART Terminal** [(by cool4uma)](https://github.com/cool4uma/UART_Terminal/tree/main)
|
||||||
|
- **ProtoView** [(by antirez)](https://github.com/antirez/protoview)
|
||||||
|
|
||||||
Games:
|
Games:
|
||||||
- DOOM (fixed) [(By p4nic4ttack)](https://github.com/p4nic4ttack/doom-flipper-zero/)
|
- DOOM (fixed) [(by p4nic4ttack)](https://github.com/p4nic4ttack/doom-flipper-zero/)
|
||||||
- Zombiez [(Reworked By DevMilanIan)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/240) [(Original By Dooskington)](https://github.com/Dooskington/flipperzero-zombiez)
|
- Zombiez [(Reworked By DevMilanIan)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/240) [(Original By Dooskington)](https://github.com/Dooskington/flipperzero-zombiez)
|
||||||
- Flappy Bird [(By DroomOne)](https://github.com/DroomOne/flipperzero-firmware/tree/dev/applications/flappy_bird)
|
- Flappy Bird [(by DroomOne)](https://github.com/DroomOne/flipperzero-firmware/tree/dev/applications/flappy_bird)
|
||||||
- Arkanoid (refactored by xMasterX) [(by gotnull)](https://github.com/gotnull/flipperzero-firmware-wPlugins)
|
- Arkanoid (refactored by xMasterX) [(by gotnull)](https://github.com/gotnull/flipperzero-firmware-wPlugins)
|
||||||
- Tic Tac Toe (refactored by xMasterX) [(by gotnull)](https://github.com/gotnull/flipperzero-firmware-wPlugins)
|
- Tic Tac Toe (refactored by xMasterX) [(by gotnull)](https://github.com/gotnull/flipperzero-firmware-wPlugins)
|
||||||
- Tetris (with fixes) [(by jeffplang)](https://github.com/jeffplang/flipperzero-firmware/tree/tetris_game/applications/tetris_game)
|
- Tetris (with fixes) [(by jeffplang)](https://github.com/jeffplang/flipperzero-firmware/tree/tetris_game/applications/tetris_game)
|
||||||
|
- Minesweeper [(by panki27)](https://github.com/panki27/minesweeper)
|
||||||
|
- Heap Defence (aka Stack Attack) - Ported to latest firmware by @xMasterX - [(original by wquinoa & Vedmein)](https://github.com/Vedmein/flipperzero-firmware/tree/hd/svisto-perdelki)
|
||||||
|
- Game15 [(by x27)](https://github.com/x27/flipperzero-game15)
|
||||||
|
- Solitaire [(by teeebor)](https://github.com/teeebor/flipper_games)
|
||||||
|
- BlackJack [(by teeebor)](https://github.com/teeebor/flipper_games)
|
||||||
|
- 2048 game [(by eugene-kirzhanov)](https://github.com/eugene-kirzhanov/flipper-zero-2048-game)
|
||||||
|
|
||||||
### Other changes
|
### Other changes
|
||||||
|
|
||||||
- BadUSB -> Keyboard layouts [(by rien > dummy-decoy)](https://github.com/dummy-decoy/flipperzero-firmware/tree/dummy_decoy/bad_usb_keyboard_layout)
|
- BadUSB -> Keyboard layouts [(by rien > dummy-decoy)](https://github.com/dummy-decoy/flipperzero-firmware/tree/dummy_decoy/bad_usb_keyboard_layout)
|
||||||
- SubGHz -> New frequency analyzer - [(by ClusterM)](https://github.com/DarkFlippers/unleashed-firmware/pull/43)
|
- Sub-GHz -> External CC1101 module support - [(by quen0n)](https://github.com/DarkFlippers/unleashed-firmware/pull/307)
|
||||||
- SubGHz -> Detect RAW feature - [(by perspecdev)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/152)
|
- Sub-GHz -> New frequency analyzer - [(by ClusterM)](https://github.com/DarkFlippers/unleashed-firmware/pull/43)
|
||||||
- SubGHz -> Save last used frequency and moduluation [(by derskythe)](https://github.com/DarkFlippers/unleashed-firmware/pull/77)
|
- Sub-GHz -> Save last used frequency [(by derskythe)](https://github.com/DarkFlippers/unleashed-firmware/pull/77)
|
||||||
- SubGHz -> Press OK in frequency analyzer to use detected frequency in Read modes [(by derskythe)](https://github.com/DarkFlippers/unleashed-firmware/pull/77)
|
- Sub-GHz -> Press OK in frequency analyzer to use detected frequency in Read modes [(by derskythe)](https://github.com/DarkFlippers/unleashed-firmware/pull/77)
|
||||||
* SubGHz -> Long press OK button in SubGHz Frequency analyzer to switch to Read menu [(by derskythe)](https://github.com/DarkFlippers/unleashed-firmware/pull/79)
|
- Sub-GHz -> Long press OK button in Sub-GHz Frequency analyzer to switch to Read menu [(by derskythe)](https://github.com/DarkFlippers/unleashed-firmware/pull/79)
|
||||||
|
- Lock device with pin(or regular lock if pin not set) by holding UP button on main screen [(by an4tur0r)](https://github.com/DarkFlippers/unleashed-firmware/pull/107)
|
||||||
|
|
||||||
# Instructions
|
# Instructions
|
||||||
## [- How to install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)
|
## [- How to install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)
|
||||||
|
|
||||||
## [- How to build firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToBuild.md)
|
## [- How to build firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToBuild.md)
|
||||||
|
|
||||||
|
## [- How to connect external CC1101 module](https://github.com/quen0n/flipperzero-ext-cc1101)
|
||||||
|
|
||||||
## [- BadUSB: how to add new keyboard layouts](https://github.com/dummy-decoy/flipperzero_badusb_kl)
|
## [- BadUSB: how to add new keyboard layouts](https://github.com/dummy-decoy/flipperzero_badusb_kl)
|
||||||
|
|
||||||
## [- How to change Flipper name](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/CustomFlipperName.md)
|
## [- How to change Flipper name](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/CustomFlipperName.md)
|
||||||
|
|
||||||
### **Plugins**
|
### **Plugins**
|
||||||
|
|
||||||
## [- 🎲 Download Extra plugins for Unleashed](https://github.com/UberGuidoZ/Flipper/tree/main/Applications/Unleashed)
|
## [- 🎲 Download Extra plugins for Unleashed](https://github.com/xMasterX/unleashed-extra-pack)
|
||||||
|
|
||||||
## [- Configure Sub-GHz Remote App](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemotePlugin.md)
|
## [- Configure Sub-GHz Remote App](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemotePlugin.md)
|
||||||
|
|
||||||
|
## [- TOTP (Authenticator) config description](https://github.com/akopachov/flipper-zero_authenticator/blob/master/docs/conf-file_description.md)
|
||||||
|
|
||||||
## [- Barcode Generator](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/BarcodeGenerator.md)
|
## [- Barcode Generator](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/BarcodeGenerator.md)
|
||||||
|
|
||||||
## [- Multi Converter](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/MultiConverter.md)
|
## [- Multi Converter](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/MultiConverter.md)
|
||||||
|
|
||||||
## [- WAV Player sample files & how to convert](https://github.com/UberGuidoZ/Flipper/tree/main/Wav_Player#readme)
|
## [- WAV Player sample files & how to convert](https://github.com/UberGuidoZ/Flipper/tree/main/Wav_Player#readme)
|
||||||
|
|
||||||
## [- SubGHz playlist generator script](https://github.com/darmiel/flipper-scripts/blob/main/playlist/playlist_creator_by_chunk.py)
|
## [- Sub-GHz playlist generator script](https://github.com/darmiel/flipper-scripts/blob/main/playlist/playlist_creator_by_chunk.py)
|
||||||
|
|
||||||
### **Plugins that works with external hardware**
|
### **Plugins that works with external hardware**
|
||||||
|
|
||||||
|
## [- How to use: Unitemp - Temperature sensors reader](https://github.com/quen0n/unitemp-flipperzero#readme)
|
||||||
|
|
||||||
|
## [- How to use: [NMEA] GPS](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/applications/plugins/gps_nmea_uart/README.md)
|
||||||
|
|
||||||
|
## [- How to use: i2c Tools](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/applications/plugins/flipper_i2ctools/README.md)
|
||||||
|
|
||||||
## [- How to use: [NRF24] plugins](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/NRF24.md)
|
## [- How to use: [NRF24] plugins](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/NRF24.md)
|
||||||
|
|
||||||
## [- How to use: [WiFi] Scanner](https://github.com/SequoiaSan/FlipperZero-WiFi-Scanner_Module#readme)
|
## [- How to use: [WiFi] Scanner](https://github.com/SequoiaSan/FlipperZero-WiFi-Scanner_Module#readme)
|
||||||
@@ -136,27 +218,27 @@ Games:
|
|||||||
|
|
||||||
## [- How to use: [GPIO] SentrySafe plugin](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SentrySafe.md)
|
## [- How to use: [GPIO] SentrySafe plugin](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SentrySafe.md)
|
||||||
|
|
||||||
### **SubGHz**
|
### **Sub-GHz**
|
||||||
|
|
||||||
## [- Transmission is blocked? - How to extend SubGHz frequency range](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/DangerousSettings.md)
|
## [- Transmission is blocked? - How to extend Sub-GHz frequency range](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/DangerousSettings.md)
|
||||||
|
|
||||||
## [- How to add extra SubGHz frequencies](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzSettings.md)
|
## [- How to add extra Sub-GHz frequencies](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzSettings.md)
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
# Where I can find IR, SubGhz, ... files, DBs, and other stuff?
|
# Where I can find IR, Sub-GHz, ... files, DBs, and other stuff?
|
||||||
## [UberGuidoZ Playground - Large collection of files - Github](https://github.com/UberGuidoZ/Flipper)
|
## [UberGuidoZ Playground - Large collection of files - Github](https://github.com/UberGuidoZ/Flipper)
|
||||||
## [Awesome Flipper Zero - Github](https://github.com/djsime1/awesome-flipperzero)
|
## [Awesome Flipper Zero - Github](https://github.com/djsime1/awesome-flipperzero)
|
||||||
## [CAME-12bit, NICE-12bit, Linear-10bit, PT-2240 - SubGHz fixed code bruteforce](https://github.com/tobiabocchi/flipperzero-bruteforce)
|
## [CAME-12bit, NICE-12bit, Linear-10bit, PT-2240 - Sub-GHz fixed code bruteforce](https://github.com/tobiabocchi/flipperzero-bruteforce)
|
||||||
## [SMC5326, UNILARM - SubGHz fixed code bruteforce](https://github.com/Hong5489/flipperzero-gate-bruteforce)
|
## [SMC5326, UNILARM - Sub-GHz fixed code bruteforce](https://github.com/Hong5489/flipperzero-gate-bruteforce)
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
# Links
|
# Links
|
||||||
|
|
||||||
* Unofficial Discord: [discord.gg/flipperzero-unofficial](https://discord.gg/flipperzero-unofficial)
|
* Unofficial Discord: [discord.unleashedflip.com](https://discord.unleashedflip.com)
|
||||||
* Docs by atmanos / How to write your own app (outdated API): [https://flipper.atmanos.com/docs/overview/intro](https://flipper.atmanos.com/docs/overview/intro)
|
* Docs by atmanos / How to write your own app (outdated API): [https://flipper.atmanos.com/docs/overview/intro](https://flipper.atmanos.com/docs/overview/intro)
|
||||||
|
|
||||||
* Official Docs: [http://docs.flipperzero.one](http://docs.flipperzero.one)
|
* Official Docs: [http://docs.flipperzero.one](http://docs.flipperzero.one)
|
||||||
@@ -166,7 +248,7 @@ Games:
|
|||||||
|
|
||||||
- `applications` - Applications and services used in firmware
|
- `applications` - Applications and services used in firmware
|
||||||
- `assets` - Assets used by applications and services
|
- `assets` - Assets used by applications and services
|
||||||
- `furi` - Furi Core: os level primitives and helpers
|
- `furi` - Furi Core: OS-level primitives and helpers
|
||||||
- `debug` - Debug tool: GDB-plugins, SVD-file and etc
|
- `debug` - Debug tool: GDB-plugins, SVD-file and etc
|
||||||
- `documentation` - Documentation generation system configs and input files
|
- `documentation` - Documentation generation system configs and input files
|
||||||
- `firmware` - Firmware source code
|
- `firmware` - Firmware source code
|
||||||
@@ -174,4 +256,4 @@ Games:
|
|||||||
- `site_scons` - Build helpers
|
- `site_scons` - Build helpers
|
||||||
- `scripts` - Supplementary scripts and python libraries home
|
- `scripts` - Supplementary scripts and python libraries home
|
||||||
|
|
||||||
Also pay attention to `ReadMe.md` files inside those directories.
|
Also, pay attention to the `ReadMe.md` files inside those directories.
|
||||||
|
|||||||
141
SConstruct
141
SConstruct
@@ -7,7 +7,7 @@
|
|||||||
# construction of certain targets behind command-line options.
|
# construction of certain targets behind command-line options.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import subprocess
|
from fbt.util import path_as_posix
|
||||||
|
|
||||||
DefaultEnvironment(tools=[])
|
DefaultEnvironment(tools=[])
|
||||||
|
|
||||||
@@ -15,62 +15,36 @@ EnsurePythonVersion(3, 8)
|
|||||||
|
|
||||||
# Progress(["OwO\r", "owo\r", "uwu\r", "owo\r"], interval=15)
|
# Progress(["OwO\r", "owo\r", "uwu\r", "owo\r"], interval=15)
|
||||||
|
|
||||||
|
|
||||||
# This environment is created only for loading options & validating file/dir existence
|
# This environment is created only for loading options & validating file/dir existence
|
||||||
fbt_variables = SConscript("site_scons/commandline.scons")
|
fbt_variables = SConscript("site_scons/commandline.scons")
|
||||||
cmd_environment = Environment(tools=[], variables=fbt_variables)
|
cmd_environment = Environment(
|
||||||
Help(fbt_variables.GenerateHelpText(cmd_environment))
|
toolpath=["#/scripts/fbt_tools"],
|
||||||
|
tools=[
|
||||||
|
("fbt_help", {"vars": fbt_variables}),
|
||||||
|
],
|
||||||
|
variables=fbt_variables,
|
||||||
|
)
|
||||||
|
|
||||||
# Building basic environment - tools, utility methods, cross-compilation
|
# Building basic environment - tools, utility methods, cross-compilation
|
||||||
# settings, gcc flags for Cortex-M4, basic builders and more
|
# settings, gcc flags for Cortex-M4, basic builders and more
|
||||||
coreenv = SConscript(
|
coreenv = SConscript(
|
||||||
"site_scons/environ.scons",
|
"site_scons/environ.scons",
|
||||||
exports={"VAR_ENV": cmd_environment},
|
exports={"VAR_ENV": cmd_environment},
|
||||||
|
toolpath=["#/scripts/fbt_tools"],
|
||||||
)
|
)
|
||||||
SConscript("site_scons/cc.scons", exports={"ENV": coreenv})
|
SConscript("site_scons/cc.scons", exports={"ENV": coreenv})
|
||||||
|
|
||||||
# Store root dir in environment for certain tools
|
|
||||||
coreenv["ROOT_DIR"] = Dir(".")
|
|
||||||
|
|
||||||
|
|
||||||
# Create a separate "dist" environment and add construction envs to it
|
# Create a separate "dist" environment and add construction envs to it
|
||||||
distenv = coreenv.Clone(
|
distenv = coreenv.Clone(
|
||||||
tools=["fbt_dist", "openocd", "blackmagic", "jflash"],
|
tools=[
|
||||||
OPENOCD_GDB_PIPE=[
|
"fbt_dist",
|
||||||
"|openocd -c 'gdb_port pipe; log_output debug/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}"
|
"fbt_debugopts",
|
||||||
|
"openocd",
|
||||||
|
"blackmagic",
|
||||||
|
"jflash",
|
||||||
],
|
],
|
||||||
GDBOPTS_BASE=[
|
|
||||||
"-ex",
|
|
||||||
"target extended-remote ${GDBREMOTE}",
|
|
||||||
"-ex",
|
|
||||||
"set confirm off",
|
|
||||||
"-ex",
|
|
||||||
"set pagination off",
|
|
||||||
],
|
|
||||||
GDBOPTS_BLACKMAGIC=[
|
|
||||||
"-ex",
|
|
||||||
"monitor swdp_scan",
|
|
||||||
"-ex",
|
|
||||||
"monitor debug_bmp enable",
|
|
||||||
"-ex",
|
|
||||||
"attach 1",
|
|
||||||
"-ex",
|
|
||||||
"set mem inaccessible-by-default off",
|
|
||||||
],
|
|
||||||
GDBPYOPTS=[
|
|
||||||
"-ex",
|
|
||||||
"source debug/FreeRTOS/FreeRTOS.py",
|
|
||||||
"-ex",
|
|
||||||
"source debug/flipperapps.py",
|
|
||||||
"-ex",
|
|
||||||
"source debug/PyCortexMDebug/PyCortexMDebug.py",
|
|
||||||
"-ex",
|
|
||||||
"svd_load ${SVD_FILE}",
|
|
||||||
"-ex",
|
|
||||||
"compare-sections",
|
|
||||||
],
|
|
||||||
JFLASHPROJECT="${ROOT_DIR.abspath}/debug/fw.jflash",
|
|
||||||
ENV=os.environ,
|
ENV=os.environ,
|
||||||
|
UPDATE_BUNDLE_DIR="dist/${DIST_DIR}/f${TARGET_HW}-update-${DIST_SUFFIX}",
|
||||||
)
|
)
|
||||||
|
|
||||||
firmware_env = distenv.AddFwProject(
|
firmware_env = distenv.AddFwProject(
|
||||||
@@ -166,32 +140,49 @@ basic_dist = distenv.DistCommand("fw_dist", distenv["DIST_DEPENDS"])
|
|||||||
distenv.Default(basic_dist)
|
distenv.Default(basic_dist)
|
||||||
|
|
||||||
dist_dir = distenv.GetProjetDirName()
|
dist_dir = distenv.GetProjetDirName()
|
||||||
plugin_dist = [
|
fap_dist = [
|
||||||
distenv.Install(
|
distenv.Install(
|
||||||
f"#/dist/{dist_dir}/apps/debug_elf",
|
distenv.Dir(f"#/dist/{dist_dir}/apps/debug_elf"),
|
||||||
firmware_env["FW_EXTAPPS"]["debug"].values(),
|
list(
|
||||||
|
app_artifact.debug
|
||||||
|
for app_artifact in firmware_env["FW_EXTAPPS"].applications.values()
|
||||||
|
),
|
||||||
),
|
),
|
||||||
*(
|
*(
|
||||||
distenv.Install(f"#/dist/{dist_dir}/apps/{dist_entry[0]}", dist_entry[1])
|
distenv.Install(
|
||||||
for dist_entry in firmware_env["FW_EXTAPPS"]["dist"].values()
|
f"#/dist/{dist_dir}/apps/{app_artifact.app.fap_category}",
|
||||||
|
app_artifact.compact[0],
|
||||||
|
)
|
||||||
|
for app_artifact in firmware_env["FW_EXTAPPS"].applications.values()
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
Depends(plugin_dist, firmware_env["FW_EXTAPPS"]["validators"].values())
|
Depends(
|
||||||
Alias("plugin_dist", plugin_dist)
|
fap_dist,
|
||||||
# distenv.Default(plugin_dist)
|
list(
|
||||||
|
app_artifact.validator
|
||||||
plugin_resources_dist = list(
|
for app_artifact in firmware_env["FW_EXTAPPS"].applications.values()
|
||||||
distenv.Install(f"#/assets/resources/apps/{dist_entry[0]}", dist_entry[1])
|
),
|
||||||
for dist_entry in firmware_env["FW_EXTAPPS"]["dist"].values()
|
)
|
||||||
|
Alias("fap_dist", fap_dist)
|
||||||
|
# distenv.Default(fap_dist)
|
||||||
|
|
||||||
|
distenv.Depends(firmware_env["FW_RESOURCES"], firmware_env["FW_EXTAPPS"].resources_dist)
|
||||||
|
|
||||||
|
# Copy all faps to device
|
||||||
|
|
||||||
|
fap_deploy = distenv.PhonyTarget(
|
||||||
|
"fap_deploy",
|
||||||
|
"${PYTHON3} ${ROOT_DIR}/scripts/storage.py send ${SOURCE} /ext/apps",
|
||||||
|
source=Dir("#/assets/resources/apps"),
|
||||||
)
|
)
|
||||||
distenv.Depends(firmware_env["FW_RESOURCES"], plugin_resources_dist)
|
|
||||||
|
|
||||||
|
|
||||||
# Target for bundling core2 package for qFlipper
|
# Target for bundling core2 package for qFlipper
|
||||||
copro_dist = distenv.CoproBuilder(
|
copro_dist = distenv.CoproBuilder(
|
||||||
distenv.Dir("assets/core2_firmware"),
|
"#/build/core2_firmware.tgz",
|
||||||
[],
|
[],
|
||||||
)
|
)
|
||||||
|
distenv.AlwaysBuild(copro_dist)
|
||||||
distenv.Alias("copro_dist", copro_dist)
|
distenv.Alias("copro_dist", copro_dist)
|
||||||
|
|
||||||
firmware_flash = distenv.AddOpenOCDFlashTarget(firmware_env)
|
firmware_flash = distenv.AddOpenOCDFlashTarget(firmware_env)
|
||||||
@@ -214,6 +205,20 @@ firmware_bm_flash = distenv.PhonyTarget(
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
gdb_backtrace_all_threads = distenv.PhonyTarget(
|
||||||
|
"gdb_trace_all",
|
||||||
|
"$GDB $GDBOPTS $SOURCES $GDBFLASH",
|
||||||
|
source=firmware_env["FW_ELF"],
|
||||||
|
GDBOPTS="${GDBOPTS_BASE}",
|
||||||
|
GDBREMOTE="${OPENOCD_GDB_PIPE}",
|
||||||
|
GDBFLASH=[
|
||||||
|
"-ex",
|
||||||
|
"thread apply all bt",
|
||||||
|
"-ex",
|
||||||
|
"quit",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
# Debugging firmware
|
# Debugging firmware
|
||||||
firmware_debug = distenv.PhonyTarget(
|
firmware_debug = distenv.PhonyTarget(
|
||||||
"debug",
|
"debug",
|
||||||
@@ -221,6 +226,7 @@ firmware_debug = distenv.PhonyTarget(
|
|||||||
source=firmware_env["FW_ELF"],
|
source=firmware_env["FW_ELF"],
|
||||||
GDBOPTS="${GDBOPTS_BASE}",
|
GDBOPTS="${GDBOPTS_BASE}",
|
||||||
GDBREMOTE="${OPENOCD_GDB_PIPE}",
|
GDBREMOTE="${OPENOCD_GDB_PIPE}",
|
||||||
|
FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")),
|
||||||
)
|
)
|
||||||
distenv.Depends(firmware_debug, firmware_flash)
|
distenv.Depends(firmware_debug, firmware_flash)
|
||||||
|
|
||||||
@@ -230,6 +236,7 @@ distenv.PhonyTarget(
|
|||||||
source=firmware_env["FW_ELF"],
|
source=firmware_env["FW_ELF"],
|
||||||
GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
|
GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
|
||||||
GDBREMOTE="${BLACKMAGIC_ADDR}",
|
GDBREMOTE="${BLACKMAGIC_ADDR}",
|
||||||
|
FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Debug alien elf
|
# Debug alien elf
|
||||||
@@ -238,7 +245,7 @@ distenv.PhonyTarget(
|
|||||||
"${GDBPYCOM}",
|
"${GDBPYCOM}",
|
||||||
GDBOPTS="${GDBOPTS_BASE}",
|
GDBOPTS="${GDBOPTS_BASE}",
|
||||||
GDBREMOTE="${OPENOCD_GDB_PIPE}",
|
GDBREMOTE="${OPENOCD_GDB_PIPE}",
|
||||||
GDBPYOPTS='-ex "source debug/PyCortexMDebug/PyCortexMDebug.py" ',
|
GDBPYOPTS='-ex "source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py" ',
|
||||||
)
|
)
|
||||||
|
|
||||||
distenv.PhonyTarget(
|
distenv.PhonyTarget(
|
||||||
@@ -258,14 +265,14 @@ distenv.PhonyTarget(
|
|||||||
# Linter
|
# Linter
|
||||||
distenv.PhonyTarget(
|
distenv.PhonyTarget(
|
||||||
"lint",
|
"lint",
|
||||||
"${PYTHON3} scripts/lint.py check ${LINT_SOURCES}",
|
"${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py check ${LINT_SOURCES}",
|
||||||
LINT_SOURCES=firmware_env["LINT_SOURCES"],
|
LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]],
|
||||||
)
|
)
|
||||||
|
|
||||||
distenv.PhonyTarget(
|
distenv.PhonyTarget(
|
||||||
"format",
|
"format",
|
||||||
"${PYTHON3} scripts/lint.py format ${LINT_SOURCES}",
|
"${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py format ${LINT_SOURCES}",
|
||||||
LINT_SOURCES=firmware_env["LINT_SOURCES"],
|
LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]],
|
||||||
)
|
)
|
||||||
|
|
||||||
# PY_LINT_SOURCES contains recursively-built modules' SConscript files + application manifests
|
# PY_LINT_SOURCES contains recursively-built modules' SConscript files + application manifests
|
||||||
@@ -305,7 +312,7 @@ distenv.PhonyTarget(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Start Flipper CLI via PySerial's miniterm
|
# Start Flipper CLI via PySerial's miniterm
|
||||||
distenv.PhonyTarget("cli", "${PYTHON3} scripts/serial_cli.py")
|
distenv.PhonyTarget("cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py")
|
||||||
|
|
||||||
|
|
||||||
# Find blackmagic probe
|
# Find blackmagic probe
|
||||||
@@ -314,6 +321,16 @@ distenv.PhonyTarget(
|
|||||||
"@echo $( ${BLACKMAGIC_ADDR} $)",
|
"@echo $( ${BLACKMAGIC_ADDR} $)",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Find STLink probe ids
|
||||||
|
distenv.PhonyTarget(
|
||||||
|
"get_stlink",
|
||||||
|
distenv.Action(
|
||||||
|
lambda **kw: distenv.GetDevices(),
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
# Prepare vscode environment
|
# Prepare vscode environment
|
||||||
vscode_dist = distenv.Install("#.vscode", distenv.Glob("#.vscode/example/*"))
|
vscode_dist = distenv.Install("#.vscode", distenv.Glob("#.vscode/example/*"))
|
||||||
distenv.Precious(vscode_dist)
|
distenv.Precious(vscode_dist)
|
||||||
|
|||||||
@@ -31,9 +31,10 @@ void AccessorApp::run(void) {
|
|||||||
onewire_host_stop(onewire_host);
|
onewire_host_stop(onewire_host);
|
||||||
}
|
}
|
||||||
|
|
||||||
AccessorApp::AccessorApp() {
|
AccessorApp::AccessorApp()
|
||||||
|
: text_store{0} {
|
||||||
notification = static_cast<NotificationApp*>(furi_record_open(RECORD_NOTIFICATION));
|
notification = static_cast<NotificationApp*>(furi_record_open(RECORD_NOTIFICATION));
|
||||||
onewire_host = onewire_host_alloc();
|
onewire_host = onewire_host_alloc(&ibutton_gpio);
|
||||||
furi_hal_power_enable_otg();
|
furi_hal_power_enable_otg();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ App(
|
|||||||
appid="accessor",
|
appid="accessor",
|
||||||
name="Accessor",
|
name="Accessor",
|
||||||
apptype=FlipperAppType.DEBUG,
|
apptype=FlipperAppType.DEBUG,
|
||||||
|
targets=["f7"],
|
||||||
entry_point="accessor_app",
|
entry_point="accessor_app",
|
||||||
cdefines=["APP_ACCESSOR"],
|
cdefines=["APP_ACCESSOR"],
|
||||||
requires=["gui"],
|
requires=["gui"],
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ void WIEGAND::end() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WIEGAND::ReadD0() {
|
void WIEGAND::ReadD0() {
|
||||||
_bitCount++; // Increament bit count for Interrupt connected to D0
|
_bitCount++; // Increment bit count for Interrupt connected to D0
|
||||||
if(_bitCount > 31) // If bit count more than 31, process high bits
|
if(_bitCount > 31) // If bit count more than 31, process high bits
|
||||||
{
|
{
|
||||||
_cardTempHigh |= ((0x80000000 & _cardTemp) >> 31); // shift value to high bits
|
_cardTempHigh |= ((0x80000000 & _cardTemp) >> 31); // shift value to high bits
|
||||||
@@ -171,9 +171,6 @@ bool WIEGAND::DoWiegandConversion() {
|
|||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
_lastWiegand = sysTick;
|
_lastWiegand = sysTick;
|
||||||
_bitCount = 0;
|
|
||||||
_cardTemp = 0;
|
|
||||||
_cardTempHigh = 0;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,4 +11,5 @@ App(
|
|||||||
stack_size=1 * 1024,
|
stack_size=1 * 1024,
|
||||||
order=130,
|
order=130,
|
||||||
fap_category="Debug",
|
fap_category="Debug",
|
||||||
|
fap_libs=["assets"],
|
||||||
)
|
)
|
||||||
|
|||||||
148
applications/debug/battery_test_app/views/battery_info.c
Normal file
148
applications/debug/battery_test_app/views/battery_info.c
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
#include "battery_info.h"
|
||||||
|
#include <furi.h>
|
||||||
|
#include <gui/elements.h>
|
||||||
|
#include <assets_icons.h>
|
||||||
|
|
||||||
|
#define LOW_CHARGE_THRESHOLD 10
|
||||||
|
#define HIGH_DRAIN_CURRENT_THRESHOLD 100
|
||||||
|
|
||||||
|
struct BatteryInfo {
|
||||||
|
View* view;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void draw_stat(Canvas* canvas, int x, int y, const Icon* icon, char* val) {
|
||||||
|
canvas_draw_frame(canvas, x - 7, y + 7, 30, 13);
|
||||||
|
canvas_draw_icon(canvas, x, y, icon);
|
||||||
|
canvas_set_color(canvas, ColorWhite);
|
||||||
|
canvas_draw_box(canvas, x - 4, y + 16, 24, 6);
|
||||||
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
canvas_draw_str_aligned(canvas, x + 8, y + 22, AlignCenter, AlignBottom, val);
|
||||||
|
};
|
||||||
|
|
||||||
|
static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) {
|
||||||
|
char emote[20] = {};
|
||||||
|
char header[20] = {};
|
||||||
|
char value[20] = {};
|
||||||
|
|
||||||
|
int32_t drain_current = data->gauge_current * (-1000);
|
||||||
|
uint32_t charge_current = data->gauge_current * 1000;
|
||||||
|
|
||||||
|
// Draw battery
|
||||||
|
canvas_draw_icon(canvas, x, y, &I_BatteryBody_52x28);
|
||||||
|
if(charge_current > 0) {
|
||||||
|
canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceCharging_29x14);
|
||||||
|
} else if(drain_current > HIGH_DRAIN_CURRENT_THRESHOLD) {
|
||||||
|
canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceConfused_29x14);
|
||||||
|
} else if(data->charge < LOW_CHARGE_THRESHOLD) {
|
||||||
|
canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNopower_29x14);
|
||||||
|
} else {
|
||||||
|
canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNormal_29x14);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw bubble
|
||||||
|
elements_bubble(canvas, 53, 0, 71, 39);
|
||||||
|
|
||||||
|
// Set text
|
||||||
|
if(charge_current > 0) {
|
||||||
|
snprintf(emote, sizeof(emote), "%s", "Yummy!");
|
||||||
|
snprintf(header, sizeof(header), "%s", "Charging at");
|
||||||
|
snprintf(
|
||||||
|
value,
|
||||||
|
sizeof(value),
|
||||||
|
"%lu.%luV %lumA",
|
||||||
|
(uint32_t)(data->vbus_voltage),
|
||||||
|
(uint32_t)(data->vbus_voltage * 10) % 10,
|
||||||
|
charge_current);
|
||||||
|
} else if(drain_current > 0) {
|
||||||
|
snprintf(
|
||||||
|
emote,
|
||||||
|
sizeof(emote),
|
||||||
|
"%s",
|
||||||
|
drain_current > HIGH_DRAIN_CURRENT_THRESHOLD ? "Oh no!" : "Om-nom-nom!");
|
||||||
|
snprintf(header, sizeof(header), "%s", "Consumption is");
|
||||||
|
snprintf(
|
||||||
|
value,
|
||||||
|
sizeof(value),
|
||||||
|
"%ld %s",
|
||||||
|
drain_current,
|
||||||
|
drain_current > HIGH_DRAIN_CURRENT_THRESHOLD ? "mA!" : "mA");
|
||||||
|
} else if(drain_current != 0) {
|
||||||
|
snprintf(header, 20, "...");
|
||||||
|
} else if(data->charging_voltage < 4.2) {
|
||||||
|
// Non-default battery charging limit, mention it
|
||||||
|
snprintf(emote, sizeof(emote), "Charged!");
|
||||||
|
snprintf(header, sizeof(header), "Limited to");
|
||||||
|
snprintf(
|
||||||
|
value,
|
||||||
|
sizeof(value),
|
||||||
|
"%lu.%luV",
|
||||||
|
(uint32_t)(data->charging_voltage),
|
||||||
|
(uint32_t)(data->charging_voltage * 10) % 10);
|
||||||
|
} else {
|
||||||
|
snprintf(header, sizeof(header), "Charged!");
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas_draw_str_aligned(canvas, 92, y + 3, AlignCenter, AlignCenter, emote);
|
||||||
|
canvas_draw_str_aligned(canvas, 92, y + 15, AlignCenter, AlignCenter, header);
|
||||||
|
canvas_draw_str_aligned(canvas, 92, y + 27, AlignCenter, AlignCenter, value);
|
||||||
|
};
|
||||||
|
|
||||||
|
static void battery_info_draw_callback(Canvas* canvas, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
BatteryInfoModel* model = context;
|
||||||
|
|
||||||
|
canvas_clear(canvas);
|
||||||
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
draw_battery(canvas, model, 0, 5);
|
||||||
|
|
||||||
|
char batt_level[10];
|
||||||
|
char temperature[10];
|
||||||
|
char voltage[10];
|
||||||
|
char health[10];
|
||||||
|
|
||||||
|
snprintf(batt_level, sizeof(batt_level), "%lu%%", (uint32_t)model->charge);
|
||||||
|
snprintf(temperature, sizeof(temperature), "%lu C", (uint32_t)model->gauge_temperature);
|
||||||
|
snprintf(
|
||||||
|
voltage,
|
||||||
|
sizeof(voltage),
|
||||||
|
"%lu.%01lu V",
|
||||||
|
(uint32_t)model->gauge_voltage,
|
||||||
|
(uint32_t)(model->gauge_voltage * 10) % 10UL);
|
||||||
|
snprintf(health, sizeof(health), "%d%%", model->health);
|
||||||
|
|
||||||
|
draw_stat(canvas, 8, 42, &I_Battery_16x16, batt_level);
|
||||||
|
draw_stat(canvas, 40, 42, &I_Temperature_16x16, temperature);
|
||||||
|
draw_stat(canvas, 72, 42, &I_Voltage_16x16, voltage);
|
||||||
|
draw_stat(canvas, 104, 42, &I_Health_16x16, health);
|
||||||
|
}
|
||||||
|
|
||||||
|
BatteryInfo* battery_info_alloc() {
|
||||||
|
BatteryInfo* battery_info = malloc(sizeof(BatteryInfo));
|
||||||
|
battery_info->view = view_alloc();
|
||||||
|
view_set_context(battery_info->view, battery_info);
|
||||||
|
view_allocate_model(battery_info->view, ViewModelTypeLocking, sizeof(BatteryInfoModel));
|
||||||
|
view_set_draw_callback(battery_info->view, battery_info_draw_callback);
|
||||||
|
|
||||||
|
return battery_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
void battery_info_free(BatteryInfo* battery_info) {
|
||||||
|
furi_assert(battery_info);
|
||||||
|
view_free(battery_info->view);
|
||||||
|
free(battery_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
View* battery_info_get_view(BatteryInfo* battery_info) {
|
||||||
|
furi_assert(battery_info);
|
||||||
|
return battery_info->view;
|
||||||
|
}
|
||||||
|
|
||||||
|
void battery_info_set_data(BatteryInfo* battery_info, BatteryInfoModel* data) {
|
||||||
|
furi_assert(battery_info);
|
||||||
|
furi_assert(data);
|
||||||
|
with_view_model(
|
||||||
|
battery_info->view,
|
||||||
|
BatteryInfoModel * model,
|
||||||
|
{ memcpy(model, data, sizeof(BatteryInfoModel)); },
|
||||||
|
true);
|
||||||
|
}
|
||||||
23
applications/debug/battery_test_app/views/battery_info.h
Normal file
23
applications/debug/battery_test_app/views/battery_info.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gui/view.h>
|
||||||
|
|
||||||
|
typedef struct BatteryInfo BatteryInfo;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
float vbus_voltage;
|
||||||
|
float gauge_voltage;
|
||||||
|
float gauge_current;
|
||||||
|
float gauge_temperature;
|
||||||
|
float charging_voltage;
|
||||||
|
uint8_t charge;
|
||||||
|
uint8_t health;
|
||||||
|
} BatteryInfoModel;
|
||||||
|
|
||||||
|
BatteryInfo* battery_info_alloc();
|
||||||
|
|
||||||
|
void battery_info_free(BatteryInfo* battery_info);
|
||||||
|
|
||||||
|
View* battery_info_get_view(BatteryInfo* battery_info);
|
||||||
|
|
||||||
|
void battery_info_set_data(BatteryInfo* battery_info, BatteryInfoModel* data);
|
||||||
@@ -31,9 +31,6 @@ uint32_t bt_debug_start_view(void* context) {
|
|||||||
BtDebugApp* bt_debug_app_alloc() {
|
BtDebugApp* bt_debug_app_alloc() {
|
||||||
BtDebugApp* app = malloc(sizeof(BtDebugApp));
|
BtDebugApp* app = malloc(sizeof(BtDebugApp));
|
||||||
|
|
||||||
// Load settings
|
|
||||||
bt_settings_load(&app->settings);
|
|
||||||
|
|
||||||
// Gui
|
// Gui
|
||||||
app->gui = furi_record_open(RECORD_GUI);
|
app->gui = furi_record_open(RECORD_GUI);
|
||||||
|
|
||||||
@@ -98,20 +95,22 @@ void bt_debug_app_free(BtDebugApp* app) {
|
|||||||
int32_t bt_debug_app(void* p) {
|
int32_t bt_debug_app(void* p) {
|
||||||
UNUSED(p);
|
UNUSED(p);
|
||||||
if(!furi_hal_bt_is_testing_supported()) {
|
if(!furi_hal_bt_is_testing_supported()) {
|
||||||
FURI_LOG_E(TAG, "Incorrect radio stack: radio testing fetures are absent.");
|
FURI_LOG_E(TAG, "Incorrect radio stack: radio testing features are absent.");
|
||||||
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
|
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
|
||||||
dialog_message_show_storage_error(dialogs, "Incorrect\nRadioStack");
|
dialog_message_show_storage_error(dialogs, "Incorrect\nRadioStack");
|
||||||
return 255;
|
return 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
BtDebugApp* app = bt_debug_app_alloc();
|
BtDebugApp* app = bt_debug_app_alloc();
|
||||||
|
// Was bt active?
|
||||||
|
const bool was_active = furi_hal_bt_is_active();
|
||||||
// Stop advertising
|
// Stop advertising
|
||||||
furi_hal_bt_stop_advertising();
|
furi_hal_bt_stop_advertising();
|
||||||
|
|
||||||
view_dispatcher_run(app->view_dispatcher);
|
view_dispatcher_run(app->view_dispatcher);
|
||||||
|
|
||||||
// Restart advertising
|
// Restart advertising
|
||||||
if(app->settings.enabled) {
|
if(was_active) {
|
||||||
furi_hal_bt_start_advertising();
|
furi_hal_bt_start_advertising();
|
||||||
}
|
}
|
||||||
bt_debug_app_free(app);
|
bt_debug_app_free(app);
|
||||||
|
|||||||
@@ -4,15 +4,14 @@
|
|||||||
#include <gui/gui.h>
|
#include <gui/gui.h>
|
||||||
#include <gui/view.h>
|
#include <gui/view.h>
|
||||||
#include <gui/view_dispatcher.h>
|
#include <gui/view_dispatcher.h>
|
||||||
|
#include <gui/modules/submenu.h>
|
||||||
|
|
||||||
#include <dialogs/dialogs.h>
|
#include <dialogs/dialogs.h>
|
||||||
|
|
||||||
#include <gui/modules/submenu.h>
|
|
||||||
#include "views/bt_carrier_test.h"
|
#include "views/bt_carrier_test.h"
|
||||||
#include "views/bt_packet_test.h"
|
#include "views/bt_packet_test.h"
|
||||||
#include <bt/bt_settings.h>
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
BtSettings settings;
|
|
||||||
Gui* gui;
|
Gui* gui;
|
||||||
ViewDispatcher* view_dispatcher;
|
ViewDispatcher* view_dispatcher;
|
||||||
Submenu* submenu;
|
Submenu* submenu;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include "bt_carrier_test.h"
|
#include "bt_carrier_test.h"
|
||||||
#include "bt_test.h"
|
#include "bt_test.h"
|
||||||
#include "bt_test_types.h"
|
#include "bt_test_types.h"
|
||||||
#include "furi_hal_bt.h"
|
#include <furi_hal_bt.h>
|
||||||
|
|
||||||
struct BtCarrierTest {
|
struct BtCarrierTest {
|
||||||
BtTest* bt_test;
|
BtTest* bt_test;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include "bt_packet_test.h"
|
#include "bt_packet_test.h"
|
||||||
#include "bt_test.h"
|
#include "bt_test.h"
|
||||||
#include "bt_test_types.h"
|
#include "bt_test_types.h"
|
||||||
#include "furi_hal_bt.h"
|
#include <furi_hal_bt.h>
|
||||||
|
|
||||||
struct BtPacketTest {
|
struct BtPacketTest {
|
||||||
BtTest* bt_test;
|
BtTest* bt_test;
|
||||||
|
|||||||
@@ -2,8 +2,11 @@
|
|||||||
|
|
||||||
#include <gui/canvas.h>
|
#include <gui/canvas.h>
|
||||||
#include <gui/elements.h>
|
#include <gui/elements.h>
|
||||||
|
|
||||||
|
#include <lib/toolbox/float_tools.h>
|
||||||
#include <m-array.h>
|
#include <m-array.h>
|
||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
|
#include <inttypes.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
struct BtTestParam {
|
struct BtTestParam {
|
||||||
@@ -98,16 +101,16 @@ static void bt_test_draw_callback(Canvas* canvas, void* _model) {
|
|||||||
elements_scrollbar(canvas, model->position, BtTestParamArray_size(model->params));
|
elements_scrollbar(canvas, model->position, BtTestParamArray_size(model->params));
|
||||||
canvas_draw_str(canvas, 6, 60, model->message);
|
canvas_draw_str(canvas, 6, 60, model->message);
|
||||||
if(model->state == BtTestStateStarted) {
|
if(model->state == BtTestStateStarted) {
|
||||||
if(model->rssi != 0.0f) {
|
if(!float_is_equal(model->rssi, 0.0f)) {
|
||||||
snprintf(info_str, sizeof(info_str), "RSSI:%3.1f dB", (double)model->rssi);
|
snprintf(info_str, sizeof(info_str), "RSSI:%3.1f dB", (double)model->rssi);
|
||||||
canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str);
|
canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str);
|
||||||
}
|
}
|
||||||
} else if(model->state == BtTestStateStopped) {
|
} else if(model->state == BtTestStateStopped) {
|
||||||
if(model->packets_num_rx) {
|
if(model->packets_num_rx) {
|
||||||
snprintf(info_str, sizeof(info_str), "%ld pack rcv", model->packets_num_rx);
|
snprintf(info_str, sizeof(info_str), "%" PRIu32 " pack rcv", model->packets_num_rx);
|
||||||
canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str);
|
canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str);
|
||||||
} else if(model->packets_num_tx) {
|
} else if(model->packets_num_tx) {
|
||||||
snprintf(info_str, sizeof(info_str), "%ld pack sent", model->packets_num_tx);
|
snprintf(info_str, sizeof(info_str), "%" PRIu32 " pack sent", model->packets_num_tx);
|
||||||
canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str);
|
canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -153,7 +156,7 @@ static bool bt_test_input_callback(InputEvent* event, void* context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void bt_test_process_up(BtTest* bt_test) {
|
void bt_test_process_up(BtTest* bt_test) {
|
||||||
with_view_model(
|
with_view_model( // -V658
|
||||||
bt_test->view,
|
bt_test->view,
|
||||||
BtTestModel * model,
|
BtTestModel * model,
|
||||||
{
|
{
|
||||||
|
|||||||
10
applications/debug/direct_draw/application.fam
Normal file
10
applications/debug/direct_draw/application.fam
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
App(
|
||||||
|
appid="direct_draw",
|
||||||
|
name="Direct Draw",
|
||||||
|
apptype=FlipperAppType.DEBUG,
|
||||||
|
entry_point="direct_draw_app",
|
||||||
|
requires=["gui", "input"],
|
||||||
|
stack_size=2 * 1024,
|
||||||
|
order=70,
|
||||||
|
fap_category="Debug",
|
||||||
|
)
|
||||||
112
applications/debug/direct_draw/direct_draw.c
Normal file
112
applications/debug/direct_draw/direct_draw.c
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
#include <furi.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <gui/canvas_i.h>
|
||||||
|
#include <input/input.h>
|
||||||
|
|
||||||
|
#define BUFFER_SIZE (32U)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FuriPubSub* input;
|
||||||
|
FuriPubSubSubscription* input_subscription;
|
||||||
|
Gui* gui;
|
||||||
|
Canvas* canvas;
|
||||||
|
bool stop;
|
||||||
|
uint32_t counter;
|
||||||
|
} DirectDraw;
|
||||||
|
|
||||||
|
static void gui_input_events_callback(const void* value, void* ctx) {
|
||||||
|
furi_assert(value);
|
||||||
|
furi_assert(ctx);
|
||||||
|
|
||||||
|
DirectDraw* instance = ctx;
|
||||||
|
const InputEvent* event = value;
|
||||||
|
|
||||||
|
if(event->key == InputKeyBack && event->type == InputTypeShort) {
|
||||||
|
instance->stop = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static DirectDraw* direct_draw_alloc() {
|
||||||
|
DirectDraw* instance = malloc(sizeof(DirectDraw));
|
||||||
|
|
||||||
|
instance->input = furi_record_open(RECORD_INPUT_EVENTS);
|
||||||
|
instance->gui = furi_record_open(RECORD_GUI);
|
||||||
|
instance->canvas = gui_direct_draw_acquire(instance->gui);
|
||||||
|
|
||||||
|
instance->input_subscription =
|
||||||
|
furi_pubsub_subscribe(instance->input, gui_input_events_callback, instance);
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void direct_draw_free(DirectDraw* instance) {
|
||||||
|
furi_pubsub_unsubscribe(instance->input, instance->input_subscription);
|
||||||
|
|
||||||
|
instance->canvas = NULL;
|
||||||
|
gui_direct_draw_release(instance->gui);
|
||||||
|
furi_record_close(RECORD_GUI);
|
||||||
|
furi_record_close(RECORD_INPUT_EVENTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void direct_draw_block(Canvas* canvas, uint32_t size, uint32_t counter) {
|
||||||
|
size += 16;
|
||||||
|
uint8_t width = canvas_width(canvas) - size;
|
||||||
|
uint8_t height = canvas_height(canvas) - size;
|
||||||
|
|
||||||
|
uint8_t x = counter % width;
|
||||||
|
if((counter / width) % 2) {
|
||||||
|
x = width - x;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t y = counter % height;
|
||||||
|
if((counter / height) % 2) {
|
||||||
|
y = height - y;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas_draw_box(canvas, x, y, size, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void direct_draw_run(DirectDraw* instance) {
|
||||||
|
size_t start = DWT->CYCCNT;
|
||||||
|
size_t counter = 0;
|
||||||
|
float fps = 0;
|
||||||
|
|
||||||
|
vTaskPrioritySet(furi_thread_get_current_id(), FuriThreadPriorityIdle);
|
||||||
|
|
||||||
|
do {
|
||||||
|
size_t elapsed = DWT->CYCCNT - start;
|
||||||
|
char buffer[BUFFER_SIZE] = {0};
|
||||||
|
|
||||||
|
if(elapsed >= 64000000) {
|
||||||
|
fps = (float)counter / ((float)elapsed / 64000000.0f);
|
||||||
|
|
||||||
|
start = DWT->CYCCNT;
|
||||||
|
counter = 0;
|
||||||
|
}
|
||||||
|
snprintf(buffer, BUFFER_SIZE, "FPS: %.1f", (double)fps);
|
||||||
|
|
||||||
|
canvas_reset(instance->canvas);
|
||||||
|
canvas_set_color(instance->canvas, ColorXOR);
|
||||||
|
direct_draw_block(instance->canvas, instance->counter % 16, instance->counter);
|
||||||
|
direct_draw_block(instance->canvas, instance->counter * 2 % 16, instance->counter * 2);
|
||||||
|
direct_draw_block(instance->canvas, instance->counter * 3 % 16, instance->counter * 3);
|
||||||
|
direct_draw_block(instance->canvas, instance->counter * 4 % 16, instance->counter * 4);
|
||||||
|
direct_draw_block(instance->canvas, instance->counter * 5 % 16, instance->counter * 5);
|
||||||
|
canvas_draw_str(instance->canvas, 10, 10, buffer);
|
||||||
|
canvas_commit(instance->canvas);
|
||||||
|
|
||||||
|
counter++;
|
||||||
|
instance->counter++;
|
||||||
|
furi_thread_yield();
|
||||||
|
} while(!instance->stop);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t direct_draw_app(void* p) {
|
||||||
|
UNUSED(p);
|
||||||
|
|
||||||
|
DirectDraw* instance = direct_draw_alloc();
|
||||||
|
direct_draw_run(instance);
|
||||||
|
direct_draw_free(instance);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ App(
|
|||||||
entry_point="display_test_app",
|
entry_point="display_test_app",
|
||||||
cdefines=["APP_DISPLAY_TEST"],
|
cdefines=["APP_DISPLAY_TEST"],
|
||||||
requires=["gui"],
|
requires=["gui"],
|
||||||
|
fap_libs=["misc"],
|
||||||
stack_size=1 * 1024,
|
stack_size=1 * 1024,
|
||||||
order=120,
|
order=120,
|
||||||
fap_category="Debug",
|
fap_category="Debug",
|
||||||
|
|||||||
@@ -91,7 +91,6 @@ static void display_test_reload_config(DisplayTest* instance) {
|
|||||||
instance->config_contrast,
|
instance->config_contrast,
|
||||||
instance->config_regulation_ratio,
|
instance->config_regulation_ratio,
|
||||||
instance->config_bias);
|
instance->config_bias);
|
||||||
gui_update(instance->gui);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void display_config_set_bias(VariableItem* item) {
|
static void display_config_set_bias(VariableItem* item) {
|
||||||
@@ -145,7 +144,7 @@ DisplayTest* display_test_alloc() {
|
|||||||
view_set_previous_callback(view, display_test_previous_callback);
|
view_set_previous_callback(view, display_test_previous_callback);
|
||||||
view_dispatcher_add_view(instance->view_dispatcher, DisplayTestViewConfigure, view);
|
view_dispatcher_add_view(instance->view_dispatcher, DisplayTestViewConfigure, view);
|
||||||
|
|
||||||
// Configurtion items
|
// Configuration items
|
||||||
VariableItem* item;
|
VariableItem* item;
|
||||||
instance->config_bias = false;
|
instance->config_bias = false;
|
||||||
instance->config_contrast = 32;
|
instance->config_contrast = 32;
|
||||||
|
|||||||
9
applications/debug/example_custom_font/application.fam
Normal file
9
applications/debug/example_custom_font/application.fam
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
App(
|
||||||
|
appid="example_custom_font",
|
||||||
|
name="Example: custom font",
|
||||||
|
apptype=FlipperAppType.DEBUG,
|
||||||
|
entry_point="example_custom_font_main",
|
||||||
|
requires=["gui"],
|
||||||
|
stack_size=1 * 1024,
|
||||||
|
fap_category="Debug",
|
||||||
|
)
|
||||||
98
applications/debug/example_custom_font/example_custom_font.c
Normal file
98
applications/debug/example_custom_font/example_custom_font.c
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
#include <furi.h>
|
||||||
|
#include <furi_hal.h>
|
||||||
|
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <input/input.h>
|
||||||
|
|
||||||
|
//This arrays contains the font itself. You can use any u8g2 font you want
|
||||||
|
|
||||||
|
/*
|
||||||
|
Fontname: -Raccoon-Fixed4x6-Medium-R-Normal--6-60-75-75-P-40-ISO10646-1
|
||||||
|
Copyright:
|
||||||
|
Glyphs: 95/203
|
||||||
|
BBX Build Mode: 0
|
||||||
|
*/
|
||||||
|
const uint8_t u8g2_font_tom_thumb_4x6_tr[725] =
|
||||||
|
"_\0\2\2\2\3\3\4\4\3\6\0\377\5\377\5\0\0\352\1\330\2\270 \5\340\315\0!\6\265\310"
|
||||||
|
"\254\0\42\6\213\313$\25#\10\227\310\244\241\206\12$\10\227\310\215\70b\2%\10\227\310d\324F\1"
|
||||||
|
"&\10\227\310(\65R\22'\5\251\313\10(\6\266\310\251\62)\10\226\310\304\224\24\0*\6\217\312\244"
|
||||||
|
"\16+\7\217\311\245\225\0,\6\212\310)\0-\5\207\312\14.\5\245\310\4/\7\227\310Ve\4\60"
|
||||||
|
"\7\227\310-k\1\61\6\226\310\255\6\62\10\227\310h\220\312\1\63\11\227\310h\220\62X\0\64\10\227"
|
||||||
|
"\310$\65b\1\65\10\227\310\214\250\301\2\66\10\227\310\315\221F\0\67\10\227\310\314TF\0\70\10\227"
|
||||||
|
"\310\214\64\324\10\71\10\227\310\214\64\342\2:\6\255\311\244\0;\7\222\310e\240\0<\10\227\310\246\32"
|
||||||
|
"d\20=\6\217\311l\60>\11\227\310d\220A*\1\77\10\227\310\314\224a\2@\10\227\310UC\3"
|
||||||
|
"\1A\10\227\310UC\251\0B\10\227\310\250\264\322\2C\7\227\310\315\32\10D\10\227\310\250d-\0"
|
||||||
|
"E\10\227\310\214\70\342\0F\10\227\310\214\70b\4G\10\227\310\315\221\222\0H\10\227\310$\65\224\12"
|
||||||
|
"I\7\227\310\254X\15J\7\227\310\226\252\2K\10\227\310$\265\222\12L\7\227\310\304\346\0M\10\227"
|
||||||
|
"\310\244\61\224\12N\10\227\310\244q\250\0O\7\227\310UV\5P\10\227\310\250\264b\4Q\10\227\310"
|
||||||
|
"Uj$\1R\10\227\310\250\64V\1S\10\227\310m\220\301\2T\7\227\310\254\330\2U\7\227\310$"
|
||||||
|
"W\22V\10\227\310$\253L\0W\10\227\310$\65\206\12X\10\227\310$\325R\1Y\10\227\310$U"
|
||||||
|
"V\0Z\7\227\310\314T\16[\7\227\310\214X\16\134\10\217\311d\220A\0]\7\227\310\314r\4^"
|
||||||
|
"\5\213\313\65_\5\207\310\14`\6\212\313\304\0a\7\223\310\310\65\2b\10\227\310D\225\324\2c\7"
|
||||||
|
"\223\310\315\14\4d\10\227\310\246\245\222\0e\6\223\310\235\2f\10\227\310\246\264b\2g\10\227\307\35"
|
||||||
|
"\61%\0h\10\227\310D\225\254\0i\6\265\310\244\1j\10\233\307f\30U\5k\10\227\310\304\264T"
|
||||||
|
"\1l\7\227\310\310\326\0m\7\223\310<R\0n\7\223\310\250d\5o\7\223\310U\252\2p\10\227"
|
||||||
|
"\307\250\244V\4q\10\227\307-\225d\0r\6\223\310\315\22s\10\223\310\215\70\22\0t\10\227\310\245"
|
||||||
|
"\25\243\0u\7\223\310$+\11v\10\223\310$\65R\2w\7\223\310\244q\4x\7\223\310\244\62\25"
|
||||||
|
"y\11\227\307$\225dJ\0z\7\223\310\254\221\6{\10\227\310\251\32D\1|\6\265\310(\1}\11"
|
||||||
|
"\227\310\310\14RR\0~\6\213\313\215\4\0\0\0\4\377\377\0";
|
||||||
|
|
||||||
|
// Screen is 128x64 px
|
||||||
|
static void app_draw_callback(Canvas* canvas, void* ctx) {
|
||||||
|
UNUSED(ctx);
|
||||||
|
|
||||||
|
canvas_clear(canvas);
|
||||||
|
|
||||||
|
canvas_set_custom_u8g2_font(canvas, u8g2_font_tom_thumb_4x6_tr);
|
||||||
|
|
||||||
|
canvas_draw_str(canvas, 0, 6, "This is a tiny custom font");
|
||||||
|
canvas_draw_str(canvas, 0, 12, "012345.?! ,:;\"\'@#$%");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void app_input_callback(InputEvent* input_event, void* ctx) {
|
||||||
|
furi_assert(ctx);
|
||||||
|
|
||||||
|
FuriMessageQueue* event_queue = ctx;
|
||||||
|
furi_message_queue_put(event_queue, input_event, FuriWaitForever);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t example_custom_font_main(void* p) {
|
||||||
|
UNUSED(p);
|
||||||
|
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
||||||
|
|
||||||
|
// Configure view port
|
||||||
|
ViewPort* view_port = view_port_alloc();
|
||||||
|
view_port_draw_callback_set(view_port, app_draw_callback, view_port);
|
||||||
|
view_port_input_callback_set(view_port, app_input_callback, event_queue);
|
||||||
|
|
||||||
|
// Register view port in GUI
|
||||||
|
Gui* gui = furi_record_open(RECORD_GUI);
|
||||||
|
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||||
|
|
||||||
|
InputEvent event;
|
||||||
|
|
||||||
|
bool running = true;
|
||||||
|
|
||||||
|
while(running) {
|
||||||
|
if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) {
|
||||||
|
if((event.type == InputTypePress) || (event.type == InputTypeRepeat)) {
|
||||||
|
switch(event.key) {
|
||||||
|
case InputKeyBack:
|
||||||
|
running = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
view_port_enabled_set(view_port, false);
|
||||||
|
gui_remove_view_port(gui, view_port);
|
||||||
|
view_port_free(view_port);
|
||||||
|
furi_message_queue_free(event_queue);
|
||||||
|
|
||||||
|
furi_record_close(RECORD_GUI);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -8,4 +8,5 @@ App(
|
|||||||
stack_size=2 * 1024,
|
stack_size=2 * 1024,
|
||||||
order=150,
|
order=150,
|
||||||
fap_category="Debug",
|
fap_category="Debug",
|
||||||
|
fap_icon_assets="icons",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
#include "assets_icons.h"
|
|
||||||
#include "file_browser_app_i.h"
|
#include "file_browser_app_i.h"
|
||||||
#include "gui/modules/file_browser.h"
|
#include <file_browser_test_icons.h>
|
||||||
#include <furi.h>
|
|
||||||
#include <furi_hal.h>
|
#include <gui/modules/file_browser.h>
|
||||||
#include <storage/storage.h>
|
#include <storage/storage.h>
|
||||||
#include <lib/toolbox/path.h>
|
#include <lib/toolbox/path.h>
|
||||||
|
#include <furi.h>
|
||||||
|
#include <furi_hal.h>
|
||||||
|
|
||||||
static bool file_browser_app_custom_event_callback(void* context, uint32_t event) {
|
static bool file_browser_app_custom_event_callback(void* context, uint32_t event) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
@@ -48,7 +49,7 @@ FileBrowserApp* file_browser_app_alloc(char* arg) {
|
|||||||
|
|
||||||
app->file_path = furi_string_alloc();
|
app->file_path = furi_string_alloc();
|
||||||
app->file_browser = file_browser_alloc(app->file_path);
|
app->file_browser = file_browser_alloc(app->file_path);
|
||||||
file_browser_configure(app->file_browser, "*", true, &I_badusb_10px, true);
|
file_browser_configure(app->file_browser, "*", NULL, true, false, &I_badusb_10px, true);
|
||||||
|
|
||||||
view_dispatcher_add_view(
|
view_dispatcher_add_view(
|
||||||
app->view_dispatcher, FileBrowserAppViewStart, widget_get_view(app->widget));
|
app->view_dispatcher, FileBrowserAppViewStart, widget_get_view(app->widget));
|
||||||
|
|||||||
BIN
applications/debug/file_browser_test/icons/badusb_10px.png
Normal file
BIN
applications/debug/file_browser_test/icons/badusb_10px.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 576 B |
@@ -2,6 +2,7 @@ App(
|
|||||||
appid="lfrfid_debug",
|
appid="lfrfid_debug",
|
||||||
name="LF-RFID Debug",
|
name="LF-RFID Debug",
|
||||||
apptype=FlipperAppType.DEBUG,
|
apptype=FlipperAppType.DEBUG,
|
||||||
|
targets=["f7"],
|
||||||
entry_point="lfrfid_debug_app",
|
entry_point="lfrfid_debug_app",
|
||||||
requires=[
|
requires=[
|
||||||
"gui",
|
"gui",
|
||||||
|
|||||||
11
applications/debug/locale_test/application.fam
Normal file
11
applications/debug/locale_test/application.fam
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
App(
|
||||||
|
appid="locale_test",
|
||||||
|
name="Locale Test",
|
||||||
|
apptype=FlipperAppType.DEBUG,
|
||||||
|
entry_point="locale_test_app",
|
||||||
|
cdefines=["APP_LOCALE"],
|
||||||
|
requires=["gui", "locale"],
|
||||||
|
stack_size=2 * 1024,
|
||||||
|
order=70,
|
||||||
|
fap_category="Debug",
|
||||||
|
)
|
||||||
102
applications/debug/locale_test/locale_test.c
Normal file
102
applications/debug/locale_test/locale_test.c
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
#include <furi.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <gui/elements.h>
|
||||||
|
#include <gui/view_dispatcher.h>
|
||||||
|
#include <gui/modules/dialog_ex.h>
|
||||||
|
#include <locale/locale.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Gui* gui;
|
||||||
|
ViewDispatcher* view_dispatcher;
|
||||||
|
View* view;
|
||||||
|
} LocaleTestApp;
|
||||||
|
|
||||||
|
static void locale_test_view_draw_callback(Canvas* canvas, void* _model) {
|
||||||
|
UNUSED(_model);
|
||||||
|
|
||||||
|
// Prepare canvas
|
||||||
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
|
||||||
|
FuriString* tmp_string = furi_string_alloc();
|
||||||
|
|
||||||
|
float temp = 25.3f;
|
||||||
|
LocaleMeasurementUnits units = locale_get_measurement_unit();
|
||||||
|
if(units == LocaleMeasurementUnitsMetric) {
|
||||||
|
furi_string_printf(tmp_string, "Temp: %5.1fC", (double)temp);
|
||||||
|
} else {
|
||||||
|
temp = locale_celsius_to_fahrenheit(temp);
|
||||||
|
furi_string_printf(tmp_string, "Temp: %5.1fF", (double)temp);
|
||||||
|
}
|
||||||
|
canvas_draw_str(canvas, 0, 10, furi_string_get_cstr(tmp_string));
|
||||||
|
|
||||||
|
FuriHalRtcDateTime datetime;
|
||||||
|
furi_hal_rtc_get_datetime(&datetime);
|
||||||
|
|
||||||
|
locale_format_time(tmp_string, &datetime, locale_get_time_format(), false);
|
||||||
|
canvas_draw_str(canvas, 0, 25, furi_string_get_cstr(tmp_string));
|
||||||
|
|
||||||
|
locale_format_date(tmp_string, &datetime, locale_get_date_format(), "/");
|
||||||
|
canvas_draw_str(canvas, 0, 40, furi_string_get_cstr(tmp_string));
|
||||||
|
|
||||||
|
furi_string_free(tmp_string);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool locale_test_view_input_callback(InputEvent* event, void* context) {
|
||||||
|
UNUSED(event);
|
||||||
|
UNUSED(context);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t locale_test_exit(void* context) {
|
||||||
|
UNUSED(context);
|
||||||
|
return VIEW_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static LocaleTestApp* locale_test_alloc() {
|
||||||
|
LocaleTestApp* app = malloc(sizeof(LocaleTestApp));
|
||||||
|
|
||||||
|
// Gui
|
||||||
|
app->gui = furi_record_open(RECORD_GUI);
|
||||||
|
|
||||||
|
// View dispatcher
|
||||||
|
app->view_dispatcher = view_dispatcher_alloc();
|
||||||
|
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||||
|
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||||
|
|
||||||
|
// Views
|
||||||
|
app->view = view_alloc();
|
||||||
|
view_set_draw_callback(app->view, locale_test_view_draw_callback);
|
||||||
|
view_set_input_callback(app->view, locale_test_view_input_callback);
|
||||||
|
|
||||||
|
view_set_previous_callback(app->view, locale_test_exit);
|
||||||
|
view_dispatcher_add_view(app->view_dispatcher, 0, app->view);
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, 0);
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void locale_test_free(LocaleTestApp* app) {
|
||||||
|
furi_assert(app);
|
||||||
|
|
||||||
|
// Free views
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, 0);
|
||||||
|
|
||||||
|
view_free(app->view);
|
||||||
|
view_dispatcher_free(app->view_dispatcher);
|
||||||
|
|
||||||
|
// Close gui record
|
||||||
|
furi_record_close(RECORD_GUI);
|
||||||
|
app->gui = NULL;
|
||||||
|
|
||||||
|
// Free rest
|
||||||
|
free(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t locale_test_app(void* p) {
|
||||||
|
UNUSED(p);
|
||||||
|
LocaleTestApp* app = locale_test_alloc();
|
||||||
|
view_dispatcher_run(app->view_dispatcher);
|
||||||
|
locale_test_free(app);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
10
applications/debug/rpc_debug_app/application.fam
Normal file
10
applications/debug/rpc_debug_app/application.fam
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
App(
|
||||||
|
appid="rpc_debug",
|
||||||
|
name="RPC Debug",
|
||||||
|
apptype=FlipperAppType.DEBUG,
|
||||||
|
entry_point="rpc_debug_app",
|
||||||
|
requires=["gui", "rpc_start", "notification"],
|
||||||
|
stack_size=2 * 1024,
|
||||||
|
order=10,
|
||||||
|
fap_category="Debug",
|
||||||
|
)
|
||||||
138
applications/debug/rpc_debug_app/rpc_debug_app.c
Normal file
138
applications/debug/rpc_debug_app/rpc_debug_app.c
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
#include "rpc_debug_app.h"
|
||||||
|
#include <core/log.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static bool rpc_debug_app_custom_event_callback(void* context, uint32_t event) {
|
||||||
|
furi_assert(context);
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
return scene_manager_handle_custom_event(app->scene_manager, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool rpc_debug_app_back_event_callback(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
return scene_manager_handle_back_event(app->scene_manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rpc_debug_app_tick_event_callback(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
scene_manager_handle_tick_event(app->scene_manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rpc_debug_app_rpc_command_callback(RpcAppSystemEvent event, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
furi_assert(app->rpc);
|
||||||
|
|
||||||
|
if(event == RpcAppEventSessionClose) {
|
||||||
|
scene_manager_stop(app->scene_manager);
|
||||||
|
view_dispatcher_stop(app->view_dispatcher);
|
||||||
|
rpc_system_app_set_callback(app->rpc, NULL, NULL);
|
||||||
|
app->rpc = NULL;
|
||||||
|
} else if(event == RpcAppEventAppExit) {
|
||||||
|
scene_manager_stop(app->scene_manager);
|
||||||
|
view_dispatcher_stop(app->view_dispatcher);
|
||||||
|
rpc_system_app_confirm(app->rpc, RpcAppEventAppExit, true);
|
||||||
|
} else {
|
||||||
|
rpc_system_app_confirm(app->rpc, event, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool rpc_debug_app_rpc_init_rpc(RpcDebugApp* app, const char* args) {
|
||||||
|
bool ret = false;
|
||||||
|
if(args && strlen(args)) {
|
||||||
|
uint32_t rpc = 0;
|
||||||
|
if(sscanf(args, "RPC %lX", &rpc) == 1) {
|
||||||
|
app->rpc = (RpcAppSystem*)rpc;
|
||||||
|
rpc_system_app_set_callback(app->rpc, rpc_debug_app_rpc_command_callback, app);
|
||||||
|
rpc_system_app_send_started(app->rpc);
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static RpcDebugApp* rpc_debug_app_alloc() {
|
||||||
|
RpcDebugApp* app = malloc(sizeof(RpcDebugApp));
|
||||||
|
|
||||||
|
app->gui = furi_record_open(RECORD_GUI);
|
||||||
|
app->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||||
|
app->scene_manager = scene_manager_alloc(&rpc_debug_app_scene_handlers, app);
|
||||||
|
app->view_dispatcher = view_dispatcher_alloc();
|
||||||
|
|
||||||
|
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
||||||
|
view_dispatcher_set_custom_event_callback(
|
||||||
|
app->view_dispatcher, rpc_debug_app_custom_event_callback);
|
||||||
|
view_dispatcher_set_navigation_event_callback(
|
||||||
|
app->view_dispatcher, rpc_debug_app_back_event_callback);
|
||||||
|
view_dispatcher_set_tick_event_callback(
|
||||||
|
app->view_dispatcher, rpc_debug_app_tick_event_callback, 100);
|
||||||
|
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||||
|
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||||
|
|
||||||
|
app->widget = widget_alloc();
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher, RpcDebugAppViewWidget, widget_get_view(app->widget));
|
||||||
|
app->submenu = submenu_alloc();
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher, RpcDebugAppViewSubmenu, submenu_get_view(app->submenu));
|
||||||
|
app->text_box = text_box_alloc();
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher, RpcDebugAppViewTextBox, text_box_get_view(app->text_box));
|
||||||
|
app->text_input = text_input_alloc();
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher, RpcDebugAppViewTextInput, text_input_get_view(app->text_input));
|
||||||
|
app->byte_input = byte_input_alloc();
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher, RpcDebugAppViewByteInput, byte_input_get_view(app->byte_input));
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rpc_debug_app_free(RpcDebugApp* app) {
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewByteInput);
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewTextInput);
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewTextBox);
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewSubmenu);
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewWidget);
|
||||||
|
|
||||||
|
free(app->byte_input);
|
||||||
|
free(app->text_input);
|
||||||
|
free(app->text_box);
|
||||||
|
free(app->submenu);
|
||||||
|
free(app->widget);
|
||||||
|
|
||||||
|
free(app->scene_manager);
|
||||||
|
free(app->view_dispatcher);
|
||||||
|
|
||||||
|
furi_record_close(RECORD_NOTIFICATION);
|
||||||
|
app->notifications = NULL;
|
||||||
|
furi_record_close(RECORD_GUI);
|
||||||
|
app->gui = NULL;
|
||||||
|
|
||||||
|
if(app->rpc) {
|
||||||
|
rpc_system_app_set_callback(app->rpc, NULL, NULL);
|
||||||
|
rpc_system_app_send_exited(app->rpc);
|
||||||
|
app->rpc = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t rpc_debug_app(void* args) {
|
||||||
|
RpcDebugApp* app = rpc_debug_app_alloc();
|
||||||
|
|
||||||
|
if(rpc_debug_app_rpc_init_rpc(app, args)) {
|
||||||
|
notification_message(app->notifications, &sequence_display_backlight_on);
|
||||||
|
scene_manager_next_scene(app->scene_manager, RpcDebugAppSceneStart);
|
||||||
|
} else {
|
||||||
|
scene_manager_next_scene(app->scene_manager, RpcDebugAppSceneStartDummy);
|
||||||
|
}
|
||||||
|
|
||||||
|
view_dispatcher_run(app->view_dispatcher);
|
||||||
|
|
||||||
|
rpc_debug_app_free(app);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
54
applications/debug/rpc_debug_app/rpc_debug_app.h
Normal file
54
applications/debug/rpc_debug_app/rpc_debug_app.h
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <furi.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <gui/view.h>
|
||||||
|
#include <gui/scene_manager.h>
|
||||||
|
#include <gui/view_dispatcher.h>
|
||||||
|
|
||||||
|
#include <gui/modules/widget.h>
|
||||||
|
#include <gui/modules/submenu.h>
|
||||||
|
#include <gui/modules/text_box.h>
|
||||||
|
#include <gui/modules/text_input.h>
|
||||||
|
#include <gui/modules/byte_input.h>
|
||||||
|
|
||||||
|
#include <rpc/rpc_app.h>
|
||||||
|
#include <notification/notification_messages.h>
|
||||||
|
|
||||||
|
#include "scenes/rpc_debug_app_scene.h"
|
||||||
|
|
||||||
|
#define DATA_STORE_SIZE 64U
|
||||||
|
#define TEXT_STORE_SIZE 64U
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Gui* gui;
|
||||||
|
RpcAppSystem* rpc;
|
||||||
|
SceneManager* scene_manager;
|
||||||
|
ViewDispatcher* view_dispatcher;
|
||||||
|
NotificationApp* notifications;
|
||||||
|
|
||||||
|
Widget* widget;
|
||||||
|
Submenu* submenu;
|
||||||
|
TextBox* text_box;
|
||||||
|
TextInput* text_input;
|
||||||
|
ByteInput* byte_input;
|
||||||
|
|
||||||
|
char text_store[TEXT_STORE_SIZE];
|
||||||
|
uint8_t data_store[DATA_STORE_SIZE];
|
||||||
|
} RpcDebugApp;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
RpcDebugAppViewWidget,
|
||||||
|
RpcDebugAppViewSubmenu,
|
||||||
|
RpcDebugAppViewTextBox,
|
||||||
|
RpcDebugAppViewTextInput,
|
||||||
|
RpcDebugAppViewByteInput,
|
||||||
|
} RpcDebugAppView;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
// Reserve first 100 events for button types and indexes, starting from 0
|
||||||
|
RpcDebugAppCustomEventInputErrorCode = 100,
|
||||||
|
RpcDebugAppCustomEventInputErrorText,
|
||||||
|
RpcDebugAppCustomEventInputDataExchange,
|
||||||
|
RpcDebugAppCustomEventRpcDataExchange,
|
||||||
|
} RpcDebugAppCustomEvent;
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
#include "rpc_debug_app_scene.h"
|
||||||
|
|
||||||
|
// Generate scene on_enter handlers array
|
||||||
|
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
|
||||||
|
void (*const rpc_debug_app_on_enter_handlers[])(void*) = {
|
||||||
|
#include "rpc_debug_app_scene_config.h"
|
||||||
|
};
|
||||||
|
#undef ADD_SCENE
|
||||||
|
|
||||||
|
// Generate scene on_event handlers array
|
||||||
|
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
|
||||||
|
bool (*const rpc_debug_app_on_event_handlers[])(void* context, SceneManagerEvent event) = {
|
||||||
|
#include "rpc_debug_app_scene_config.h"
|
||||||
|
};
|
||||||
|
#undef ADD_SCENE
|
||||||
|
|
||||||
|
// Generate scene on_exit handlers array
|
||||||
|
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
|
||||||
|
void (*const rpc_debug_app_on_exit_handlers[])(void* context) = {
|
||||||
|
#include "rpc_debug_app_scene_config.h"
|
||||||
|
};
|
||||||
|
#undef ADD_SCENE
|
||||||
|
|
||||||
|
// Initialize scene handlers configuration structure
|
||||||
|
const SceneManagerHandlers rpc_debug_app_scene_handlers = {
|
||||||
|
.on_enter_handlers = rpc_debug_app_on_enter_handlers,
|
||||||
|
.on_event_handlers = rpc_debug_app_on_event_handlers,
|
||||||
|
.on_exit_handlers = rpc_debug_app_on_exit_handlers,
|
||||||
|
.scene_num = RpcDebugAppSceneNum,
|
||||||
|
};
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gui/scene_manager.h>
|
||||||
|
|
||||||
|
// Generate scene id and total number
|
||||||
|
#define ADD_SCENE(prefix, name, id) RpcDebugAppScene##id,
|
||||||
|
typedef enum {
|
||||||
|
#include "rpc_debug_app_scene_config.h"
|
||||||
|
RpcDebugAppSceneNum,
|
||||||
|
} RpcDebugAppScene;
|
||||||
|
#undef ADD_SCENE
|
||||||
|
|
||||||
|
extern const SceneManagerHandlers rpc_debug_app_scene_handlers;
|
||||||
|
|
||||||
|
// Generate scene on_enter handlers declaration
|
||||||
|
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
|
||||||
|
#include "rpc_debug_app_scene_config.h"
|
||||||
|
#undef ADD_SCENE
|
||||||
|
|
||||||
|
// Generate scene on_event handlers declaration
|
||||||
|
#define ADD_SCENE(prefix, name, id) \
|
||||||
|
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
|
||||||
|
#include "rpc_debug_app_scene_config.h"
|
||||||
|
#undef ADD_SCENE
|
||||||
|
|
||||||
|
// Generate scene on_exit handlers declaration
|
||||||
|
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
|
||||||
|
#include "rpc_debug_app_scene_config.h"
|
||||||
|
#undef ADD_SCENE
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
ADD_SCENE(rpc_debug_app, start, Start)
|
||||||
|
ADD_SCENE(rpc_debug_app, start_dummy, StartDummy)
|
||||||
|
ADD_SCENE(rpc_debug_app, test_app_error, TestAppError)
|
||||||
|
ADD_SCENE(rpc_debug_app, test_data_exchange, TestDataExchange)
|
||||||
|
ADD_SCENE(rpc_debug_app, input_error_code, InputErrorCode)
|
||||||
|
ADD_SCENE(rpc_debug_app, input_error_text, InputErrorText)
|
||||||
|
ADD_SCENE(rpc_debug_app, input_data_exchange, InputDataExchange)
|
||||||
|
ADD_SCENE(rpc_debug_app, receive_data_exchange, ReceiveDataExchange)
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
#include "../rpc_debug_app.h"
|
||||||
|
|
||||||
|
static void rpc_debug_app_scene_input_data_exchange_result_callback(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
view_dispatcher_send_custom_event(
|
||||||
|
app->view_dispatcher, RpcDebugAppCustomEventInputDataExchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_input_data_exchange_on_enter(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
byte_input_set_header_text(app->byte_input, "Enter data to exchange");
|
||||||
|
byte_input_set_result_callback(
|
||||||
|
app->byte_input,
|
||||||
|
rpc_debug_app_scene_input_data_exchange_result_callback,
|
||||||
|
NULL,
|
||||||
|
app,
|
||||||
|
app->data_store,
|
||||||
|
DATA_STORE_SIZE);
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewByteInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rpc_debug_app_scene_input_data_exchange_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == RpcDebugAppCustomEventInputDataExchange) {
|
||||||
|
rpc_system_app_exchange_data(app->rpc, app->data_store, DATA_STORE_SIZE);
|
||||||
|
scene_manager_previous_scene(app->scene_manager);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_input_data_exchange_on_exit(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
UNUSED(app);
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
#include "../rpc_debug_app.h"
|
||||||
|
|
||||||
|
static bool rpc_debug_app_scene_input_error_code_validator_callback(
|
||||||
|
const char* text,
|
||||||
|
FuriString* error,
|
||||||
|
void* context) {
|
||||||
|
UNUSED(context);
|
||||||
|
|
||||||
|
for(; *text; ++text) {
|
||||||
|
const char c = *text;
|
||||||
|
if(c < '0' || c > '9') {
|
||||||
|
furi_string_printf(error, "%s", "Please enter\na number!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rpc_debug_app_scene_input_error_code_result_callback(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, RpcDebugAppCustomEventInputErrorCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_input_error_code_on_enter(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
strncpy(app->text_store, "666", TEXT_STORE_SIZE);
|
||||||
|
text_input_set_header_text(app->text_input, "Enter error code");
|
||||||
|
text_input_set_validator(
|
||||||
|
app->text_input, rpc_debug_app_scene_input_error_code_validator_callback, NULL);
|
||||||
|
text_input_set_result_callback(
|
||||||
|
app->text_input,
|
||||||
|
rpc_debug_app_scene_input_error_code_result_callback,
|
||||||
|
app,
|
||||||
|
app->text_store,
|
||||||
|
TEXT_STORE_SIZE,
|
||||||
|
true);
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewTextInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rpc_debug_app_scene_input_error_code_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == RpcDebugAppCustomEventInputErrorCode) {
|
||||||
|
char* end;
|
||||||
|
int error_code = strtol(app->text_store, &end, 10);
|
||||||
|
if(!*end) {
|
||||||
|
rpc_system_app_set_error_code(app->rpc, error_code);
|
||||||
|
}
|
||||||
|
scene_manager_previous_scene(app->scene_manager);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_input_error_code_on_exit(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
text_input_reset(app->text_input);
|
||||||
|
text_input_set_validator(app->text_input, NULL, NULL);
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
#include "../rpc_debug_app.h"
|
||||||
|
|
||||||
|
static void rpc_debug_app_scene_input_error_text_result_callback(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, RpcDebugAppCustomEventInputErrorText);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_input_error_text_on_enter(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
strncpy(app->text_store, "I'm a scary error message!", TEXT_STORE_SIZE);
|
||||||
|
text_input_set_header_text(app->text_input, "Enter error text");
|
||||||
|
text_input_set_result_callback(
|
||||||
|
app->text_input,
|
||||||
|
rpc_debug_app_scene_input_error_text_result_callback,
|
||||||
|
app,
|
||||||
|
app->text_store,
|
||||||
|
TEXT_STORE_SIZE,
|
||||||
|
true);
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewTextInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rpc_debug_app_scene_input_error_text_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == RpcDebugAppCustomEventInputErrorText) {
|
||||||
|
rpc_system_app_set_error_text(app->rpc, app->text_store);
|
||||||
|
scene_manager_previous_scene(app->scene_manager);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_input_error_text_on_exit(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
text_input_reset(app->text_input);
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
#include "../rpc_debug_app.h"
|
||||||
|
|
||||||
|
static void rpc_debug_app_scene_start_format_hex(
|
||||||
|
const uint8_t* data,
|
||||||
|
size_t data_size,
|
||||||
|
char* buf,
|
||||||
|
size_t buf_size) {
|
||||||
|
furi_assert(data);
|
||||||
|
furi_assert(buf);
|
||||||
|
|
||||||
|
const size_t byte_width = 3;
|
||||||
|
const size_t line_width = 7;
|
||||||
|
|
||||||
|
data_size = MIN(data_size, buf_size / (byte_width + 1));
|
||||||
|
|
||||||
|
for(size_t i = 0; i < data_size; ++i) {
|
||||||
|
char* p = buf + (i * byte_width);
|
||||||
|
char sep = !((i + 1) % line_width) ? '\n' : ' ';
|
||||||
|
snprintf(p, byte_width + 1, "%02X%c", data[i], sep);
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[buf_size - 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rpc_debug_app_scene_receive_data_exchange_callback(
|
||||||
|
const uint8_t* data,
|
||||||
|
size_t data_size,
|
||||||
|
void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
if(data) {
|
||||||
|
rpc_debug_app_scene_start_format_hex(data, data_size, app->text_store, TEXT_STORE_SIZE);
|
||||||
|
} else {
|
||||||
|
strncpy(app->text_store, "<Data empty>", TEXT_STORE_SIZE);
|
||||||
|
}
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, RpcDebugAppCustomEventRpcDataExchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_receive_data_exchange_on_enter(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
strncpy(app->text_store, "Received data will appear here...", TEXT_STORE_SIZE);
|
||||||
|
|
||||||
|
text_box_set_text(app->text_box, app->text_store);
|
||||||
|
text_box_set_font(app->text_box, TextBoxFontHex);
|
||||||
|
|
||||||
|
rpc_system_app_set_data_exchange_callback(
|
||||||
|
app->rpc, rpc_debug_app_scene_receive_data_exchange_callback, app);
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewTextBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rpc_debug_app_scene_receive_data_exchange_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == RpcDebugAppCustomEventRpcDataExchange) {
|
||||||
|
notification_message(app->notifications, &sequence_blink_cyan_100);
|
||||||
|
notification_message(app->notifications, &sequence_display_backlight_on);
|
||||||
|
text_box_set_text(app->text_box, app->text_store);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_receive_data_exchange_on_exit(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
text_box_reset(app->text_box);
|
||||||
|
rpc_system_app_set_data_exchange_callback(app->rpc, NULL, NULL);
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
#include "../rpc_debug_app.h"
|
||||||
|
|
||||||
|
enum SubmenuIndex {
|
||||||
|
SubmenuIndexTestAppError,
|
||||||
|
SubmenuIndexTestDataExchange,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void rpc_debug_app_scene_start_submenu_callback(void* context, uint32_t index) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_start_on_enter(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
Submenu* submenu = app->submenu;
|
||||||
|
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Test App Error",
|
||||||
|
SubmenuIndexTestAppError,
|
||||||
|
rpc_debug_app_scene_start_submenu_callback,
|
||||||
|
app);
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Test Data Exchange",
|
||||||
|
SubmenuIndexTestDataExchange,
|
||||||
|
rpc_debug_app_scene_start_submenu_callback,
|
||||||
|
app);
|
||||||
|
|
||||||
|
submenu_set_selected_item(submenu, SubmenuIndexTestAppError);
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewSubmenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rpc_debug_app_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
SceneManager* scene_manager = app->scene_manager;
|
||||||
|
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
const uint32_t submenu_index = event.event;
|
||||||
|
if(submenu_index == SubmenuIndexTestAppError) {
|
||||||
|
scene_manager_next_scene(scene_manager, RpcDebugAppSceneTestAppError);
|
||||||
|
consumed = true;
|
||||||
|
} else if(submenu_index == SubmenuIndexTestDataExchange) {
|
||||||
|
scene_manager_next_scene(scene_manager, RpcDebugAppSceneTestDataExchange);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_start_on_exit(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
submenu_reset(app->submenu);
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
#include "../rpc_debug_app.h"
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_start_dummy_on_enter(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
widget_add_text_box_element(
|
||||||
|
app->widget,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
128,
|
||||||
|
64,
|
||||||
|
AlignCenter,
|
||||||
|
AlignCenter,
|
||||||
|
"This application\nis meant to be run\nin \e#RPC\e# mode.",
|
||||||
|
false);
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rpc_debug_app_scene_start_dummy_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
UNUSED(app);
|
||||||
|
UNUSED(event);
|
||||||
|
|
||||||
|
bool consumed = false;
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_start_dummy_on_exit(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
widget_reset(app->widget);
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
#include "../rpc_debug_app.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SubmenuIndexSetErrorCode,
|
||||||
|
SubmenuIndexSetErrorText,
|
||||||
|
} SubmenuIndex;
|
||||||
|
|
||||||
|
static void rpc_debug_app_scene_test_app_error_submenu_callback(void* context, uint32_t index) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_test_app_error_on_enter(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
Submenu* submenu = app->submenu;
|
||||||
|
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Set Error Code",
|
||||||
|
SubmenuIndexSetErrorCode,
|
||||||
|
rpc_debug_app_scene_test_app_error_submenu_callback,
|
||||||
|
app);
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Set Error Text",
|
||||||
|
SubmenuIndexSetErrorText,
|
||||||
|
rpc_debug_app_scene_test_app_error_submenu_callback,
|
||||||
|
app);
|
||||||
|
|
||||||
|
submenu_set_selected_item(submenu, SubmenuIndexSetErrorCode);
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewSubmenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rpc_debug_app_scene_test_app_error_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
SceneManager* scene_manager = app->scene_manager;
|
||||||
|
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
const uint32_t submenu_index = event.event;
|
||||||
|
if(submenu_index == SubmenuIndexSetErrorCode) {
|
||||||
|
scene_manager_next_scene(scene_manager, RpcDebugAppSceneInputErrorCode);
|
||||||
|
consumed = true;
|
||||||
|
} else if(submenu_index == SubmenuIndexSetErrorText) {
|
||||||
|
scene_manager_next_scene(scene_manager, RpcDebugAppSceneInputErrorText);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_test_app_error_on_exit(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
submenu_reset(app->submenu);
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
#include "../rpc_debug_app.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SubmenuIndexSendData,
|
||||||
|
SubmenuIndexReceiveData,
|
||||||
|
} SubmenuIndex;
|
||||||
|
|
||||||
|
static void
|
||||||
|
rpc_debug_app_scene_test_data_exchange_submenu_callback(void* context, uint32_t index) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_test_data_exchange_on_enter(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
Submenu* submenu = app->submenu;
|
||||||
|
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Send Data",
|
||||||
|
SubmenuIndexSendData,
|
||||||
|
rpc_debug_app_scene_test_data_exchange_submenu_callback,
|
||||||
|
app);
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Receive Data",
|
||||||
|
SubmenuIndexReceiveData,
|
||||||
|
rpc_debug_app_scene_test_data_exchange_submenu_callback,
|
||||||
|
app);
|
||||||
|
|
||||||
|
submenu_set_selected_item(submenu, SubmenuIndexSendData);
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewSubmenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rpc_debug_app_scene_test_data_exchange_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
SceneManager* scene_manager = app->scene_manager;
|
||||||
|
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
const uint32_t submenu_index = event.event;
|
||||||
|
if(submenu_index == SubmenuIndexSendData) {
|
||||||
|
scene_manager_next_scene(scene_manager, RpcDebugAppSceneInputDataExchange);
|
||||||
|
consumed = true;
|
||||||
|
} else if(submenu_index == SubmenuIndexReceiveData) {
|
||||||
|
scene_manager_next_scene(scene_manager, RpcDebugAppSceneReceiveDataExchange);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_test_data_exchange_on_exit(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
submenu_reset(app->submenu);
|
||||||
|
}
|
||||||
@@ -7,6 +7,6 @@ App(
|
|||||||
requires=["gui"],
|
requires=["gui"],
|
||||||
stack_size=2 * 1024,
|
stack_size=2 * 1024,
|
||||||
order=70,
|
order=70,
|
||||||
fap_icon="../../../assets/icons/Archive/keyboard_10px.png",
|
fap_icon="uart_10px.png",
|
||||||
fap_category="Misc",
|
fap_category="GPIO",
|
||||||
)
|
)
|
||||||
|
|||||||
BIN
applications/debug/uart_echo/uart_10px.png
Normal file
BIN
applications/debug/uart_echo/uart_10px.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
@@ -215,30 +215,26 @@ static UartEchoApp* uart_echo_app_alloc() {
|
|||||||
view_dispatcher_add_view(app->view_dispatcher, 0, app->view);
|
view_dispatcher_add_view(app->view_dispatcher, 0, app->view);
|
||||||
view_dispatcher_switch_to_view(app->view_dispatcher, 0);
|
view_dispatcher_switch_to_view(app->view_dispatcher, 0);
|
||||||
|
|
||||||
|
app->worker_thread = furi_thread_alloc_ex("UsbUartWorker", 1024, uart_echo_worker, app);
|
||||||
|
furi_thread_start(app->worker_thread);
|
||||||
|
|
||||||
// Enable uart listener
|
// Enable uart listener
|
||||||
furi_hal_console_disable();
|
furi_hal_console_disable();
|
||||||
furi_hal_uart_set_br(FuriHalUartIdUSART1, 115200);
|
furi_hal_uart_set_br(FuriHalUartIdUSART1, 115200);
|
||||||
furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_echo_on_irq_cb, app);
|
furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_echo_on_irq_cb, app);
|
||||||
|
|
||||||
app->worker_thread = furi_thread_alloc();
|
|
||||||
furi_thread_set_name(app->worker_thread, "UsbUartWorker");
|
|
||||||
furi_thread_set_stack_size(app->worker_thread, 1024);
|
|
||||||
furi_thread_set_context(app->worker_thread, app);
|
|
||||||
furi_thread_set_callback(app->worker_thread, uart_echo_worker);
|
|
||||||
furi_thread_start(app->worker_thread);
|
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uart_echo_app_free(UartEchoApp* app) {
|
static void uart_echo_app_free(UartEchoApp* app) {
|
||||||
furi_assert(app);
|
furi_assert(app);
|
||||||
|
|
||||||
|
furi_hal_console_enable(); // this will also clear IRQ callback so thread is no longer referenced
|
||||||
|
|
||||||
furi_thread_flags_set(furi_thread_get_id(app->worker_thread), WorkerEventStop);
|
furi_thread_flags_set(furi_thread_get_id(app->worker_thread), WorkerEventStop);
|
||||||
furi_thread_join(app->worker_thread);
|
furi_thread_join(app->worker_thread);
|
||||||
furi_thread_free(app->worker_thread);
|
furi_thread_free(app->worker_thread);
|
||||||
|
|
||||||
furi_hal_console_enable();
|
|
||||||
|
|
||||||
// Free views
|
// Free views
|
||||||
view_dispatcher_remove_view(app->view_dispatcher, 0);
|
view_dispatcher_remove_view(app->view_dispatcher, 0);
|
||||||
|
|
||||||
|
|||||||
110
applications/debug/unit_tests/bt/bt_test.c
Normal file
110
applications/debug/unit_tests/bt/bt_test.c
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
#include <furi.h>
|
||||||
|
#include <furi_hal.h>
|
||||||
|
#include "../minunit.h"
|
||||||
|
|
||||||
|
#include <bt/bt_service/bt_keys_storage.h>
|
||||||
|
#include <storage/storage.h>
|
||||||
|
|
||||||
|
#define BT_TEST_KEY_STORAGE_FILE_PATH EXT_PATH("unit_tests/bt_test.keys")
|
||||||
|
#define BT_TEST_NVM_RAM_BUFF_SIZE (507 * 4) // The same as in ble NVM storage
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Storage* storage;
|
||||||
|
BtKeysStorage* bt_keys_storage;
|
||||||
|
uint8_t* nvm_ram_buff_dut;
|
||||||
|
uint8_t* nvm_ram_buff_ref;
|
||||||
|
} BtTest;
|
||||||
|
|
||||||
|
BtTest* bt_test = NULL;
|
||||||
|
|
||||||
|
void bt_test_alloc() {
|
||||||
|
bt_test = malloc(sizeof(BtTest));
|
||||||
|
bt_test->storage = furi_record_open(RECORD_STORAGE);
|
||||||
|
bt_test->nvm_ram_buff_dut = malloc(BT_TEST_NVM_RAM_BUFF_SIZE);
|
||||||
|
bt_test->nvm_ram_buff_ref = malloc(BT_TEST_NVM_RAM_BUFF_SIZE);
|
||||||
|
bt_test->bt_keys_storage = bt_keys_storage_alloc(BT_TEST_KEY_STORAGE_FILE_PATH);
|
||||||
|
bt_keys_storage_set_ram_params(
|
||||||
|
bt_test->bt_keys_storage, bt_test->nvm_ram_buff_dut, BT_TEST_NVM_RAM_BUFF_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bt_test_free() {
|
||||||
|
furi_assert(bt_test);
|
||||||
|
free(bt_test->nvm_ram_buff_ref);
|
||||||
|
free(bt_test->nvm_ram_buff_dut);
|
||||||
|
bt_keys_storage_free(bt_test->bt_keys_storage);
|
||||||
|
furi_record_close(RECORD_STORAGE);
|
||||||
|
free(bt_test);
|
||||||
|
bt_test = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bt_test_keys_storage_profile() {
|
||||||
|
// Emulate nvm change on initial connection
|
||||||
|
const int nvm_change_size_on_connection = 88;
|
||||||
|
for(size_t i = 0; i < nvm_change_size_on_connection; i++) {
|
||||||
|
bt_test->nvm_ram_buff_dut[i] = rand();
|
||||||
|
bt_test->nvm_ram_buff_ref[i] = bt_test->nvm_ram_buff_dut[i];
|
||||||
|
}
|
||||||
|
// Emulate update storage on initial connect
|
||||||
|
mu_assert(
|
||||||
|
bt_keys_storage_update(
|
||||||
|
bt_test->bt_keys_storage, bt_test->nvm_ram_buff_dut, nvm_change_size_on_connection),
|
||||||
|
"Failed to update key storage on initial connect");
|
||||||
|
memset(bt_test->nvm_ram_buff_dut, 0, BT_TEST_NVM_RAM_BUFF_SIZE);
|
||||||
|
mu_assert(bt_keys_storage_load(bt_test->bt_keys_storage), "Failed to load NVM");
|
||||||
|
mu_assert(
|
||||||
|
memcmp(
|
||||||
|
bt_test->nvm_ram_buff_ref, bt_test->nvm_ram_buff_dut, nvm_change_size_on_connection) ==
|
||||||
|
0,
|
||||||
|
"Wrong buffer loaded");
|
||||||
|
|
||||||
|
const int nvm_disconnect_update_offset = 84;
|
||||||
|
const int nvm_disconnect_update_size = 324;
|
||||||
|
const int nvm_total_size = nvm_change_size_on_connection -
|
||||||
|
(nvm_change_size_on_connection - nvm_disconnect_update_offset) +
|
||||||
|
nvm_disconnect_update_size;
|
||||||
|
// Emulate update storage on initial disconnect
|
||||||
|
for(size_t i = nvm_disconnect_update_offset;
|
||||||
|
i < nvm_disconnect_update_offset + nvm_disconnect_update_size;
|
||||||
|
i++) {
|
||||||
|
bt_test->nvm_ram_buff_dut[i] = rand();
|
||||||
|
bt_test->nvm_ram_buff_ref[i] = bt_test->nvm_ram_buff_dut[i];
|
||||||
|
}
|
||||||
|
mu_assert(
|
||||||
|
bt_keys_storage_update(
|
||||||
|
bt_test->bt_keys_storage,
|
||||||
|
&bt_test->nvm_ram_buff_dut[nvm_disconnect_update_offset],
|
||||||
|
nvm_disconnect_update_size),
|
||||||
|
"Failed to update key storage on initial disconnect");
|
||||||
|
memset(bt_test->nvm_ram_buff_dut, 0, BT_TEST_NVM_RAM_BUFF_SIZE);
|
||||||
|
mu_assert(bt_keys_storage_load(bt_test->bt_keys_storage), "Failed to load NVM");
|
||||||
|
mu_assert(
|
||||||
|
memcmp(bt_test->nvm_ram_buff_ref, bt_test->nvm_ram_buff_dut, nvm_total_size) == 0,
|
||||||
|
"Wrong buffer loaded");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bt_test_keys_remove_test_file() {
|
||||||
|
mu_assert(
|
||||||
|
storage_simply_remove(bt_test->storage, BT_TEST_KEY_STORAGE_FILE_PATH),
|
||||||
|
"Can't remove test file");
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST(bt_test_keys_storage_serial_profile) {
|
||||||
|
furi_assert(bt_test);
|
||||||
|
|
||||||
|
bt_test_keys_remove_test_file();
|
||||||
|
bt_test_keys_storage_profile();
|
||||||
|
bt_test_keys_remove_test_file();
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST_SUITE(test_bt) {
|
||||||
|
bt_test_alloc();
|
||||||
|
|
||||||
|
MU_RUN_TEST(bt_test_keys_storage_serial_profile);
|
||||||
|
|
||||||
|
bt_test_free();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run_minunit_test_bt() {
|
||||||
|
MU_RUN_SUITE(test_bt);
|
||||||
|
return MU_EXIT_CODE;
|
||||||
|
}
|
||||||
60
applications/debug/unit_tests/float_tools/float_tools_test.c
Normal file
60
applications/debug/unit_tests/float_tools/float_tools_test.c
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#include <float.h>
|
||||||
|
#include <float_tools.h>
|
||||||
|
|
||||||
|
#include "../minunit.h"
|
||||||
|
|
||||||
|
MU_TEST(float_tools_equal_test) {
|
||||||
|
mu_check(float_is_equal(FLT_MAX, FLT_MAX));
|
||||||
|
mu_check(float_is_equal(FLT_MIN, FLT_MIN));
|
||||||
|
mu_check(float_is_equal(-FLT_MAX, -FLT_MAX));
|
||||||
|
mu_check(float_is_equal(-FLT_MIN, -FLT_MIN));
|
||||||
|
|
||||||
|
mu_check(!float_is_equal(FLT_MIN, FLT_MAX));
|
||||||
|
mu_check(!float_is_equal(-FLT_MIN, FLT_MAX));
|
||||||
|
mu_check(!float_is_equal(FLT_MIN, -FLT_MAX));
|
||||||
|
mu_check(!float_is_equal(-FLT_MIN, -FLT_MAX));
|
||||||
|
|
||||||
|
const float pi = 3.14159f;
|
||||||
|
mu_check(float_is_equal(pi, pi));
|
||||||
|
mu_check(float_is_equal(-pi, -pi));
|
||||||
|
mu_check(!float_is_equal(pi, -pi));
|
||||||
|
mu_check(!float_is_equal(-pi, pi));
|
||||||
|
|
||||||
|
const float one_third = 1.f / 3.f;
|
||||||
|
const float one_third_dec = 0.3333333f;
|
||||||
|
mu_check(one_third != one_third_dec);
|
||||||
|
mu_check(float_is_equal(one_third, one_third_dec));
|
||||||
|
|
||||||
|
const float big_num = 1.e12f;
|
||||||
|
const float med_num = 95.389f;
|
||||||
|
const float smol_num = 1.e-12f;
|
||||||
|
mu_check(float_is_equal(big_num, big_num));
|
||||||
|
mu_check(float_is_equal(med_num, med_num));
|
||||||
|
mu_check(float_is_equal(smol_num, smol_num));
|
||||||
|
mu_check(!float_is_equal(smol_num, big_num));
|
||||||
|
mu_check(!float_is_equal(med_num, smol_num));
|
||||||
|
mu_check(!float_is_equal(big_num, med_num));
|
||||||
|
|
||||||
|
const float more_than_one = 1.f + FLT_EPSILON;
|
||||||
|
const float less_than_one = 1.f - FLT_EPSILON;
|
||||||
|
mu_check(!float_is_equal(more_than_one, less_than_one));
|
||||||
|
mu_check(!float_is_equal(more_than_one, -less_than_one));
|
||||||
|
mu_check(!float_is_equal(-more_than_one, less_than_one));
|
||||||
|
mu_check(!float_is_equal(-more_than_one, -less_than_one));
|
||||||
|
|
||||||
|
const float slightly_more_than_one = 1.f + FLT_EPSILON / 2.f;
|
||||||
|
const float slightly_less_than_one = 1.f - FLT_EPSILON / 2.f;
|
||||||
|
mu_check(float_is_equal(slightly_more_than_one, slightly_less_than_one));
|
||||||
|
mu_check(float_is_equal(-slightly_more_than_one, -slightly_less_than_one));
|
||||||
|
mu_check(!float_is_equal(slightly_more_than_one, -slightly_less_than_one));
|
||||||
|
mu_check(!float_is_equal(-slightly_more_than_one, slightly_less_than_one));
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST_SUITE(float_tools_suite) {
|
||||||
|
MU_RUN_TEST(float_tools_equal_test);
|
||||||
|
}
|
||||||
|
|
||||||
|
int run_minunit_test_float_tools() {
|
||||||
|
MU_RUN_SUITE(float_tools_suite);
|
||||||
|
return MU_EXIT_CODE;
|
||||||
|
}
|
||||||
@@ -3,98 +3,37 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
// this test is not accurate, but gives a basic understanding
|
|
||||||
// that memory management is working fine
|
|
||||||
|
|
||||||
// do not include memmgr.h here
|
|
||||||
// we also test that we are linking against stdlib
|
|
||||||
extern size_t memmgr_get_free_heap(void);
|
|
||||||
extern size_t memmgr_get_minimum_free_heap(void);
|
|
||||||
|
|
||||||
// current heap managment realization consume:
|
|
||||||
// X bytes after allocate and 0 bytes after allocate and free,
|
|
||||||
// where X = sizeof(void*) + sizeof(size_t), look to BlockLink_t
|
|
||||||
const size_t heap_overhead_max_size = sizeof(void*) + sizeof(size_t);
|
|
||||||
|
|
||||||
bool heap_equal(size_t heap_size, size_t heap_size_old) {
|
|
||||||
// heap borders with overhead
|
|
||||||
const size_t heap_low = heap_size_old - heap_overhead_max_size;
|
|
||||||
const size_t heap_high = heap_size_old + heap_overhead_max_size;
|
|
||||||
|
|
||||||
// not extact, so we must test it against bigger numbers than "overhead size"
|
|
||||||
const bool result = ((heap_size >= heap_low) && (heap_size <= heap_high));
|
|
||||||
|
|
||||||
// debug allocation info
|
|
||||||
if(!result) {
|
|
||||||
printf("\n(hl: %zu) <= (p: %zu) <= (hh: %zu)\n", heap_low, heap_size, heap_high);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_furi_memmgr() {
|
void test_furi_memmgr() {
|
||||||
size_t heap_size = 0;
|
void* ptr;
|
||||||
size_t heap_size_old = 0;
|
|
||||||
const int alloc_size = 128;
|
|
||||||
|
|
||||||
void* ptr = NULL;
|
|
||||||
void* original_ptr = NULL;
|
|
||||||
|
|
||||||
// do not include furi memmgr.h case
|
|
||||||
#ifdef FURI_MEMMGR_GUARD
|
|
||||||
mu_fail("do not link against furi memmgr.h");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// allocate memory case
|
// allocate memory case
|
||||||
heap_size_old = memmgr_get_free_heap();
|
ptr = malloc(100);
|
||||||
ptr = malloc(alloc_size);
|
mu_check(ptr != NULL);
|
||||||
heap_size = memmgr_get_free_heap();
|
// test that memory is zero-initialized after allocation
|
||||||
mu_assert_pointers_not_eq(ptr, NULL);
|
for(int i = 0; i < 100; i++) {
|
||||||
mu_assert(heap_equal(heap_size, heap_size_old - alloc_size), "allocate failed");
|
mu_assert_int_eq(0, ((uint8_t*)ptr)[i]);
|
||||||
|
}
|
||||||
// free memory case
|
|
||||||
heap_size_old = memmgr_get_free_heap();
|
|
||||||
free(ptr);
|
free(ptr);
|
||||||
ptr = NULL;
|
|
||||||
heap_size = memmgr_get_free_heap();
|
|
||||||
mu_assert(heap_equal(heap_size, heap_size_old + alloc_size), "free failed");
|
|
||||||
|
|
||||||
// reallocate memory case
|
// reallocate memory case
|
||||||
|
ptr = malloc(100);
|
||||||
|
memset(ptr, 66, 100);
|
||||||
|
ptr = realloc(ptr, 200);
|
||||||
|
mu_check(ptr != NULL);
|
||||||
|
|
||||||
// get filled array with some data
|
// test that memory is really reallocated
|
||||||
original_ptr = malloc(alloc_size);
|
for(int i = 0; i < 100; i++) {
|
||||||
mu_assert_pointers_not_eq(original_ptr, NULL);
|
mu_assert_int_eq(66, ((uint8_t*)ptr)[i]);
|
||||||
for(int i = 0; i < alloc_size; i++) {
|
|
||||||
*(unsigned char*)(original_ptr + i) = i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// malloc array and copy data
|
// TODO: fix realloc to copy only old size, and write testcase that leftover of reallocated memory is zero-initialized
|
||||||
ptr = malloc(alloc_size);
|
|
||||||
mu_assert_pointers_not_eq(ptr, NULL);
|
|
||||||
memcpy(ptr, original_ptr, alloc_size);
|
|
||||||
|
|
||||||
// reallocate array
|
|
||||||
heap_size_old = memmgr_get_free_heap();
|
|
||||||
ptr = realloc(ptr, alloc_size * 2);
|
|
||||||
heap_size = memmgr_get_free_heap();
|
|
||||||
mu_assert(heap_equal(heap_size, heap_size_old - alloc_size), "reallocate failed");
|
|
||||||
mu_assert_int_eq(memcmp(original_ptr, ptr, alloc_size), 0);
|
|
||||||
free(original_ptr);
|
|
||||||
free(ptr);
|
free(ptr);
|
||||||
|
|
||||||
// allocate and zero-initialize array (calloc)
|
// allocate and zero-initialize array (calloc)
|
||||||
original_ptr = malloc(alloc_size);
|
ptr = calloc(100, 2);
|
||||||
mu_assert_pointers_not_eq(original_ptr, NULL);
|
mu_check(ptr != NULL);
|
||||||
|
for(int i = 0; i < 100 * 2; i++) {
|
||||||
for(int i = 0; i < alloc_size; i++) {
|
mu_assert_int_eq(0, ((uint8_t*)ptr)[i]);
|
||||||
*(unsigned char*)(original_ptr + i) = 0;
|
|
||||||
}
|
}
|
||||||
heap_size_old = memmgr_get_free_heap();
|
|
||||||
ptr = calloc(1, alloc_size);
|
|
||||||
heap_size = memmgr_get_free_heap();
|
|
||||||
mu_assert(heap_equal(heap_size, heap_size_old - alloc_size), "callocate failed");
|
|
||||||
mu_assert_int_eq(memcmp(original_ptr, ptr, alloc_size), 0);
|
|
||||||
|
|
||||||
free(original_ptr);
|
|
||||||
free(ptr);
|
free(ptr);
|
||||||
}
|
}
|
||||||
|
|||||||
116
applications/debug/unit_tests/furi_hal/furi_hal_tests.c
Normal file
116
applications/debug/unit_tests/furi_hal/furi_hal_tests.c
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <furi.h>
|
||||||
|
#include <furi_hal.h>
|
||||||
|
#include <lp5562_reg.h>
|
||||||
|
#include "../minunit.h"
|
||||||
|
|
||||||
|
#define DATA_SIZE 4
|
||||||
|
|
||||||
|
static void furi_hal_i2c_int_setup() {
|
||||||
|
furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void furi_hal_i2c_int_teardown() {
|
||||||
|
furi_hal_i2c_release(&furi_hal_i2c_handle_power);
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST(furi_hal_i2c_int_1b) {
|
||||||
|
bool ret = false;
|
||||||
|
uint8_t data_one = 0;
|
||||||
|
|
||||||
|
// 1 byte: read, write, read
|
||||||
|
ret = furi_hal_i2c_read_reg_8(
|
||||||
|
&furi_hal_i2c_handle_power,
|
||||||
|
LP5562_ADDRESS,
|
||||||
|
LP5562_CHANNEL_BLUE_CURRENT_REGISTER,
|
||||||
|
&data_one,
|
||||||
|
LP5562_I2C_TIMEOUT);
|
||||||
|
mu_assert(ret, "0 read_reg_8 failed");
|
||||||
|
mu_assert(data_one != 0, "0 invalid data");
|
||||||
|
ret = furi_hal_i2c_write_reg_8(
|
||||||
|
&furi_hal_i2c_handle_power,
|
||||||
|
LP5562_ADDRESS,
|
||||||
|
LP5562_CHANNEL_BLUE_CURRENT_REGISTER,
|
||||||
|
data_one,
|
||||||
|
LP5562_I2C_TIMEOUT);
|
||||||
|
mu_assert(ret, "1 write_reg_8 failed");
|
||||||
|
ret = furi_hal_i2c_read_reg_8(
|
||||||
|
&furi_hal_i2c_handle_power,
|
||||||
|
LP5562_ADDRESS,
|
||||||
|
LP5562_CHANNEL_BLUE_CURRENT_REGISTER,
|
||||||
|
&data_one,
|
||||||
|
LP5562_I2C_TIMEOUT);
|
||||||
|
mu_assert(ret, "2 read_reg_8 failed");
|
||||||
|
mu_assert(data_one != 0, "2 invalid data");
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST(furi_hal_i2c_int_3b) {
|
||||||
|
bool ret = false;
|
||||||
|
uint8_t data_many[DATA_SIZE] = {0};
|
||||||
|
|
||||||
|
// 3 byte: read, write, read
|
||||||
|
data_many[0] = LP5562_CHANNEL_BLUE_CURRENT_REGISTER;
|
||||||
|
ret = furi_hal_i2c_tx(
|
||||||
|
&furi_hal_i2c_handle_power, LP5562_ADDRESS, data_many, 1, LP5562_I2C_TIMEOUT);
|
||||||
|
mu_assert(ret, "3 tx failed");
|
||||||
|
ret = furi_hal_i2c_rx(
|
||||||
|
&furi_hal_i2c_handle_power,
|
||||||
|
LP5562_ADDRESS,
|
||||||
|
data_many + 1,
|
||||||
|
DATA_SIZE - 1,
|
||||||
|
LP5562_I2C_TIMEOUT);
|
||||||
|
mu_assert(ret, "4 rx failed");
|
||||||
|
for(size_t i = 0; i < DATA_SIZE; i++) mu_assert(data_many[i] != 0, "4 invalid data_many");
|
||||||
|
|
||||||
|
ret = furi_hal_i2c_tx(
|
||||||
|
&furi_hal_i2c_handle_power, LP5562_ADDRESS, data_many, DATA_SIZE, LP5562_I2C_TIMEOUT);
|
||||||
|
mu_assert(ret, "5 tx failed");
|
||||||
|
|
||||||
|
ret = furi_hal_i2c_tx(
|
||||||
|
&furi_hal_i2c_handle_power, LP5562_ADDRESS, data_many, 1, LP5562_I2C_TIMEOUT);
|
||||||
|
mu_assert(ret, "6 tx failed");
|
||||||
|
ret = furi_hal_i2c_rx(
|
||||||
|
&furi_hal_i2c_handle_power,
|
||||||
|
LP5562_ADDRESS,
|
||||||
|
data_many + 1,
|
||||||
|
DATA_SIZE - 1,
|
||||||
|
LP5562_I2C_TIMEOUT);
|
||||||
|
mu_assert(ret, "7 rx failed");
|
||||||
|
for(size_t i = 0; i < DATA_SIZE; i++) mu_assert(data_many[i] != 0, "7 invalid data_many");
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST(furi_hal_i2c_int_1b_fail) {
|
||||||
|
bool ret = false;
|
||||||
|
uint8_t data_one = 0;
|
||||||
|
|
||||||
|
// 1 byte: fail, read, fail, write, fail, read
|
||||||
|
data_one = 0;
|
||||||
|
ret = furi_hal_i2c_read_reg_8(
|
||||||
|
&furi_hal_i2c_handle_power,
|
||||||
|
LP5562_ADDRESS + 0x10,
|
||||||
|
LP5562_CHANNEL_BLUE_CURRENT_REGISTER,
|
||||||
|
&data_one,
|
||||||
|
LP5562_I2C_TIMEOUT);
|
||||||
|
mu_assert(!ret, "8 read_reg_8 failed");
|
||||||
|
mu_assert(data_one == 0, "8 invalid data");
|
||||||
|
ret = furi_hal_i2c_read_reg_8(
|
||||||
|
&furi_hal_i2c_handle_power,
|
||||||
|
LP5562_ADDRESS,
|
||||||
|
LP5562_CHANNEL_BLUE_CURRENT_REGISTER,
|
||||||
|
&data_one,
|
||||||
|
LP5562_I2C_TIMEOUT);
|
||||||
|
mu_assert(ret, "9 read_reg_8 failed");
|
||||||
|
mu_assert(data_one != 0, "9 invalid data");
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST_SUITE(furi_hal_i2c_int_suite) {
|
||||||
|
MU_SUITE_CONFIGURE(&furi_hal_i2c_int_setup, &furi_hal_i2c_int_teardown);
|
||||||
|
MU_RUN_TEST(furi_hal_i2c_int_1b);
|
||||||
|
MU_RUN_TEST(furi_hal_i2c_int_3b);
|
||||||
|
MU_RUN_TEST(furi_hal_i2c_int_1b_fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
int run_minunit_test_furi_hal() {
|
||||||
|
MU_RUN_SUITE(furi_hal_i2c_int_suite);
|
||||||
|
return MU_EXIT_CODE;
|
||||||
|
}
|
||||||
@@ -424,6 +424,7 @@ MU_TEST(infrared_test_decoder_mixed) {
|
|||||||
infrared_test_run_decoder(InfraredProtocolRC5, 5);
|
infrared_test_run_decoder(InfraredProtocolRC5, 5);
|
||||||
infrared_test_run_decoder(InfraredProtocolSamsung32, 1);
|
infrared_test_run_decoder(InfraredProtocolSamsung32, 1);
|
||||||
infrared_test_run_decoder(InfraredProtocolSIRC, 3);
|
infrared_test_run_decoder(InfraredProtocolSIRC, 3);
|
||||||
|
infrared_test_run_decoder(InfraredProtocolKaseikyo, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
MU_TEST(infrared_test_decoder_nec) {
|
MU_TEST(infrared_test_decoder_nec) {
|
||||||
@@ -489,6 +490,15 @@ MU_TEST(infrared_test_encoder_rc6) {
|
|||||||
infrared_test_run_encoder(InfraredProtocolRC6, 1);
|
infrared_test_run_encoder(InfraredProtocolRC6, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MU_TEST(infrared_test_decoder_kaseikyo) {
|
||||||
|
infrared_test_run_decoder(InfraredProtocolKaseikyo, 1);
|
||||||
|
infrared_test_run_decoder(InfraredProtocolKaseikyo, 2);
|
||||||
|
infrared_test_run_decoder(InfraredProtocolKaseikyo, 3);
|
||||||
|
infrared_test_run_decoder(InfraredProtocolKaseikyo, 4);
|
||||||
|
infrared_test_run_decoder(InfraredProtocolKaseikyo, 5);
|
||||||
|
infrared_test_run_decoder(InfraredProtocolKaseikyo, 6);
|
||||||
|
}
|
||||||
|
|
||||||
MU_TEST(infrared_test_encoder_decoder_all) {
|
MU_TEST(infrared_test_encoder_decoder_all) {
|
||||||
infrared_test_run_encoder_decoder(InfraredProtocolNEC, 1);
|
infrared_test_run_encoder_decoder(InfraredProtocolNEC, 1);
|
||||||
infrared_test_run_encoder_decoder(InfraredProtocolNECext, 1);
|
infrared_test_run_encoder_decoder(InfraredProtocolNECext, 1);
|
||||||
@@ -498,6 +508,7 @@ MU_TEST(infrared_test_encoder_decoder_all) {
|
|||||||
infrared_test_run_encoder_decoder(InfraredProtocolRC6, 1);
|
infrared_test_run_encoder_decoder(InfraredProtocolRC6, 1);
|
||||||
infrared_test_run_encoder_decoder(InfraredProtocolRC5, 1);
|
infrared_test_run_encoder_decoder(InfraredProtocolRC5, 1);
|
||||||
infrared_test_run_encoder_decoder(InfraredProtocolSIRC, 1);
|
infrared_test_run_encoder_decoder(InfraredProtocolSIRC, 1);
|
||||||
|
infrared_test_run_encoder_decoder(InfraredProtocolKaseikyo, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
MU_TEST_SUITE(infrared_test) {
|
MU_TEST_SUITE(infrared_test) {
|
||||||
@@ -515,6 +526,7 @@ MU_TEST_SUITE(infrared_test) {
|
|||||||
MU_RUN_TEST(infrared_test_decoder_nec);
|
MU_RUN_TEST(infrared_test_decoder_nec);
|
||||||
MU_RUN_TEST(infrared_test_decoder_samsung32);
|
MU_RUN_TEST(infrared_test_decoder_samsung32);
|
||||||
MU_RUN_TEST(infrared_test_decoder_necext1);
|
MU_RUN_TEST(infrared_test_decoder_necext1);
|
||||||
|
MU_RUN_TEST(infrared_test_decoder_kaseikyo);
|
||||||
MU_RUN_TEST(infrared_test_decoder_mixed);
|
MU_RUN_TEST(infrared_test_decoder_mixed);
|
||||||
MU_RUN_TEST(infrared_test_encoder_decoder_all);
|
MU_RUN_TEST(infrared_test_encoder_decoder_all);
|
||||||
}
|
}
|
||||||
|
|||||||
75
applications/debug/unit_tests/manifest/manifest.c
Normal file
75
applications/debug/unit_tests/manifest/manifest.c
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
#include <furi.c>
|
||||||
|
#include "../minunit.h"
|
||||||
|
#include <update_util/resources/manifest.h>
|
||||||
|
|
||||||
|
#define TAG "Manifest"
|
||||||
|
|
||||||
|
MU_TEST(manifest_type_test) {
|
||||||
|
mu_assert(ResourceManifestEntryTypeUnknown == 0, "ResourceManifestEntryTypeUnknown != 0\r\n");
|
||||||
|
mu_assert(ResourceManifestEntryTypeVersion == 1, "ResourceManifestEntryTypeVersion != 1\r\n");
|
||||||
|
mu_assert(
|
||||||
|
ResourceManifestEntryTypeTimestamp == 2, "ResourceManifestEntryTypeTimestamp != 2\r\n");
|
||||||
|
mu_assert(
|
||||||
|
ResourceManifestEntryTypeDirectory == 3, "ResourceManifestEntryTypeDirectory != 3\r\n");
|
||||||
|
mu_assert(ResourceManifestEntryTypeFile == 4, "ResourceManifestEntryTypeFile != 4\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST(manifest_iteration_test) {
|
||||||
|
bool result = true;
|
||||||
|
size_t counters[5] = {0};
|
||||||
|
|
||||||
|
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||||
|
ResourceManifestReader* manifest_reader = resource_manifest_reader_alloc(storage);
|
||||||
|
do {
|
||||||
|
// Open manifest file
|
||||||
|
if(!resource_manifest_reader_open(manifest_reader, EXT_PATH("unit_tests/Manifest"))) {
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate forward
|
||||||
|
ResourceManifestEntry* entry_ptr = NULL;
|
||||||
|
while((entry_ptr = resource_manifest_reader_next(manifest_reader))) {
|
||||||
|
FURI_LOG_D(TAG, "F:%u:%s", entry_ptr->type, furi_string_get_cstr(entry_ptr->name));
|
||||||
|
if(entry_ptr->type > 4) {
|
||||||
|
mu_fail("entry_ptr->type > 4\r\n");
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
counters[entry_ptr->type]++;
|
||||||
|
}
|
||||||
|
if(!result) break;
|
||||||
|
|
||||||
|
// Iterate backward
|
||||||
|
while((entry_ptr = resource_manifest_reader_previous(manifest_reader))) {
|
||||||
|
FURI_LOG_D(TAG, "B:%u:%s", entry_ptr->type, furi_string_get_cstr(entry_ptr->name));
|
||||||
|
if(entry_ptr->type > 4) {
|
||||||
|
mu_fail("entry_ptr->type > 4\r\n");
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
counters[entry_ptr->type]--;
|
||||||
|
}
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
resource_manifest_reader_free(manifest_reader);
|
||||||
|
furi_record_close(RECORD_STORAGE);
|
||||||
|
|
||||||
|
mu_assert(counters[ResourceManifestEntryTypeUnknown] == 0, "Unknown counter != 0\r\n");
|
||||||
|
mu_assert(counters[ResourceManifestEntryTypeVersion] == 0, "Version counter != 0\r\n");
|
||||||
|
mu_assert(counters[ResourceManifestEntryTypeTimestamp] == 0, "Timestamp counter != 0\r\n");
|
||||||
|
mu_assert(counters[ResourceManifestEntryTypeDirectory] == 0, "Directory counter != 0\r\n");
|
||||||
|
mu_assert(counters[ResourceManifestEntryTypeFile] == 0, "File counter != 0\r\n");
|
||||||
|
|
||||||
|
mu_assert(result, "Manifest forward iterate failed\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST_SUITE(manifest_suite) {
|
||||||
|
MU_RUN_TEST(manifest_type_test);
|
||||||
|
MU_RUN_TEST(manifest_iteration_test);
|
||||||
|
}
|
||||||
|
|
||||||
|
int run_minunit_test_manifest() {
|
||||||
|
MU_RUN_SUITE(manifest_suite);
|
||||||
|
return MU_EXIT_CODE;
|
||||||
|
}
|
||||||
@@ -316,7 +316,7 @@ void minunit_print_fail(const char* error);
|
|||||||
MU__SAFE_BLOCK( \
|
MU__SAFE_BLOCK( \
|
||||||
double minunit_tmp_e; double minunit_tmp_r; minunit_assert++; minunit_tmp_e = (expected); \
|
double minunit_tmp_e; double minunit_tmp_r; minunit_assert++; minunit_tmp_e = (expected); \
|
||||||
minunit_tmp_r = (result); \
|
minunit_tmp_r = (result); \
|
||||||
if(fabs(minunit_tmp_e - minunit_tmp_r) > MINUNIT_EPSILON) { \
|
if(fabs(minunit_tmp_e - minunit_tmp_r) > (double)MINUNIT_EPSILON) { \
|
||||||
int minunit_significant_figures = 1 - log10(MINUNIT_EPSILON); \
|
int minunit_significant_figures = 1 - log10(MINUNIT_EPSILON); \
|
||||||
snprintf( \
|
snprintf( \
|
||||||
minunit_last_message, \
|
minunit_last_message, \
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
#include <lib/nfc/protocols/nfca.h>
|
#include <lib/nfc/protocols/nfca.h>
|
||||||
#include <lib/nfc/helpers/mf_classic_dict.h>
|
#include <lib/nfc/helpers/mf_classic_dict.h>
|
||||||
#include <lib/digital_signal/digital_signal.h>
|
#include <lib/digital_signal/digital_signal.h>
|
||||||
|
#include <lib/nfc/nfc_device.h>
|
||||||
|
#include <lib/nfc/helpers/nfc_generators.h>
|
||||||
|
|
||||||
#include <lib/flipper_format/flipper_format_i.h>
|
#include <lib/flipper_format/flipper_format_i.h>
|
||||||
#include <lib/toolbox/stream/file_stream.h>
|
#include <lib/toolbox/stream/file_stream.h>
|
||||||
@@ -16,6 +18,8 @@
|
|||||||
#define NFC_TEST_RESOURCES_DIR EXT_PATH("unit_tests/nfc/")
|
#define NFC_TEST_RESOURCES_DIR EXT_PATH("unit_tests/nfc/")
|
||||||
#define NFC_TEST_SIGNAL_SHORT_FILE "nfc_nfca_signal_short.nfc"
|
#define NFC_TEST_SIGNAL_SHORT_FILE "nfc_nfca_signal_short.nfc"
|
||||||
#define NFC_TEST_SIGNAL_LONG_FILE "nfc_nfca_signal_long.nfc"
|
#define NFC_TEST_SIGNAL_LONG_FILE "nfc_nfca_signal_long.nfc"
|
||||||
|
#define NFC_TEST_DICT_PATH EXT_PATH("unit_tests/mf_classic_dict.nfc")
|
||||||
|
#define NFC_TEST_NFC_DEV_PATH EXT_PATH("unit_tests/nfc/nfc_dev_test.nfc")
|
||||||
|
|
||||||
static const char* nfc_test_file_type = "Flipper NFC test";
|
static const char* nfc_test_file_type = "Flipper NFC test";
|
||||||
static const uint32_t nfc_test_file_version = 1;
|
static const uint32_t nfc_test_file_version = 1;
|
||||||
@@ -98,7 +102,10 @@ static bool nfc_test_digital_signal_test_encode(
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
// Read test data
|
// Read test data
|
||||||
if(!nfc_test_read_signal_from_file(file_name)) break;
|
if(!nfc_test_read_signal_from_file(file_name)) {
|
||||||
|
FURI_LOG_E(TAG, "Failed to read signal from file");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Encode signal
|
// Encode signal
|
||||||
FURI_CRITICAL_ENTER();
|
FURI_CRITICAL_ENTER();
|
||||||
@@ -132,7 +139,7 @@ static bool nfc_test_digital_signal_test_encode(
|
|||||||
ref_timings_sum += ref[i];
|
ref_timings_sum += ref[i];
|
||||||
if(timings_diff > timing_tolerance) {
|
if(timings_diff > timing_tolerance) {
|
||||||
FURI_LOG_E(
|
FURI_LOG_E(
|
||||||
TAG, "Too big differece in %d timings. Ref: %ld, DUT: %ld", i, ref[i], dut[i]);
|
TAG, "Too big difference in %d timings. Ref: %ld, DUT: %ld", i, ref[i], dut[i]);
|
||||||
timing_check_success = false;
|
timing_check_success = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -220,11 +227,301 @@ MU_TEST(mf_classic_dict_test) {
|
|||||||
furi_string_free(temp_str);
|
furi_string_free(temp_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MU_TEST(mf_classic_dict_load_test) {
|
||||||
|
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||||
|
mu_assert(storage != NULL, "storage != NULL assert failed\r\n");
|
||||||
|
|
||||||
|
// Delete unit test dict file if exists
|
||||||
|
if(storage_file_exists(storage, NFC_TEST_DICT_PATH)) {
|
||||||
|
mu_assert(
|
||||||
|
storage_simply_remove(storage, NFC_TEST_DICT_PATH),
|
||||||
|
"remove == true assert failed\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create unit test dict file
|
||||||
|
Stream* file_stream = file_stream_alloc(storage);
|
||||||
|
mu_assert(file_stream != NULL, "file_stream != NULL assert failed\r\n");
|
||||||
|
mu_assert(
|
||||||
|
file_stream_open(file_stream, NFC_TEST_DICT_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS),
|
||||||
|
"file_stream_open == true assert failed\r\n");
|
||||||
|
|
||||||
|
// Write unit test dict file
|
||||||
|
char key_str[] = "a0a1a2a3a4a5";
|
||||||
|
mu_assert(
|
||||||
|
stream_write_cstring(file_stream, key_str) == strlen(key_str),
|
||||||
|
"write == true assert failed\r\n");
|
||||||
|
// Close unit test dict file
|
||||||
|
mu_assert(file_stream_close(file_stream), "file_stream_close == true assert failed\r\n");
|
||||||
|
|
||||||
|
// Load unit test dict file
|
||||||
|
MfClassicDict* instance = NULL;
|
||||||
|
instance = mf_classic_dict_alloc(MfClassicDictTypeUnitTest);
|
||||||
|
mu_assert(instance != NULL, "mf_classic_dict_alloc\r\n");
|
||||||
|
uint32_t total_keys = mf_classic_dict_get_total_keys(instance);
|
||||||
|
mu_assert(total_keys == 1, "total_keys == 1 assert failed\r\n");
|
||||||
|
|
||||||
|
// Read key
|
||||||
|
uint64_t key_ref = 0xa0a1a2a3a4a5;
|
||||||
|
uint64_t key_dut = 0;
|
||||||
|
FuriString* temp_str = furi_string_alloc();
|
||||||
|
mu_assert(
|
||||||
|
mf_classic_dict_get_next_key_str(instance, temp_str),
|
||||||
|
"get_next_key_str == true assert failed\r\n");
|
||||||
|
mu_assert(furi_string_cmp_str(temp_str, key_str) == 0, "invalid key loaded\r\n");
|
||||||
|
mu_assert(mf_classic_dict_rewind(instance), "mf_classic_dict_rewind == 1 assert failed\r\n");
|
||||||
|
mu_assert(
|
||||||
|
mf_classic_dict_get_next_key(instance, &key_dut),
|
||||||
|
"get_next_key == true assert failed\r\n");
|
||||||
|
mu_assert(key_dut == key_ref, "invalid key loaded\r\n");
|
||||||
|
furi_string_free(temp_str);
|
||||||
|
mf_classic_dict_free(instance);
|
||||||
|
|
||||||
|
// Check that MfClassicDict added new line to the end of the file
|
||||||
|
mu_assert(
|
||||||
|
file_stream_open(file_stream, NFC_TEST_DICT_PATH, FSAM_READ, FSOM_OPEN_EXISTING),
|
||||||
|
"file_stream_open == true assert failed\r\n");
|
||||||
|
mu_assert(stream_seek(file_stream, -1, StreamOffsetFromEnd), "seek == true assert failed\r\n");
|
||||||
|
uint8_t last_char = 0;
|
||||||
|
mu_assert(stream_read(file_stream, &last_char, 1) == 1, "read == true assert failed\r\n");
|
||||||
|
mu_assert(last_char == '\n', "last_char == '\\n' assert failed\r\n");
|
||||||
|
mu_assert(file_stream_close(file_stream), "file_stream_close == true assert failed\r\n");
|
||||||
|
|
||||||
|
// Delete unit test dict file
|
||||||
|
mu_assert(
|
||||||
|
storage_simply_remove(storage, NFC_TEST_DICT_PATH), "remove == true assert failed\r\n");
|
||||||
|
stream_free(file_stream);
|
||||||
|
furi_record_close(RECORD_STORAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST(nfca_file_test) {
|
||||||
|
NfcDevice* nfc = nfc_device_alloc();
|
||||||
|
mu_assert(nfc != NULL, "nfc_device_data != NULL assert failed\r\n");
|
||||||
|
nfc->format = NfcDeviceSaveFormatUid;
|
||||||
|
|
||||||
|
// Fill the UID, sak, ATQA and type
|
||||||
|
uint8_t uid[7] = {0x04, 0x01, 0x23, 0x45, 0x67, 0x89, 0x00};
|
||||||
|
memcpy(nfc->dev_data.nfc_data.uid, uid, 7);
|
||||||
|
nfc->dev_data.nfc_data.uid_len = 7;
|
||||||
|
|
||||||
|
nfc->dev_data.nfc_data.sak = 0x08;
|
||||||
|
nfc->dev_data.nfc_data.atqa[0] = 0x00;
|
||||||
|
nfc->dev_data.nfc_data.atqa[1] = 0x04;
|
||||||
|
nfc->dev_data.nfc_data.type = FuriHalNfcTypeA;
|
||||||
|
|
||||||
|
// Save the NFC device data to the file
|
||||||
|
mu_assert(
|
||||||
|
nfc_device_save(nfc, NFC_TEST_NFC_DEV_PATH), "nfc_device_save == true assert failed\r\n");
|
||||||
|
nfc_device_free(nfc);
|
||||||
|
|
||||||
|
// Load the NFC device data from the file
|
||||||
|
NfcDevice* nfc_validate = nfc_device_alloc();
|
||||||
|
mu_assert(
|
||||||
|
nfc_device_load(nfc_validate, NFC_TEST_NFC_DEV_PATH, true),
|
||||||
|
"nfc_device_load == true assert failed\r\n");
|
||||||
|
|
||||||
|
// Check the UID, sak, ATQA and type
|
||||||
|
mu_assert(memcmp(nfc_validate->dev_data.nfc_data.uid, uid, 7) == 0, "uid assert failed\r\n");
|
||||||
|
mu_assert(nfc_validate->dev_data.nfc_data.sak == 0x08, "sak == 0x08 assert failed\r\n");
|
||||||
|
mu_assert(
|
||||||
|
nfc_validate->dev_data.nfc_data.atqa[0] == 0x00, "atqa[0] == 0x00 assert failed\r\n");
|
||||||
|
mu_assert(
|
||||||
|
nfc_validate->dev_data.nfc_data.atqa[1] == 0x04, "atqa[1] == 0x04 assert failed\r\n");
|
||||||
|
mu_assert(
|
||||||
|
nfc_validate->dev_data.nfc_data.type == FuriHalNfcTypeA,
|
||||||
|
"type == FuriHalNfcTypeA assert failed\r\n");
|
||||||
|
nfc_device_free(nfc_validate);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mf_classic_generator_test(uint8_t uid_len, MfClassicType type) {
|
||||||
|
NfcDevice* nfc_dev = nfc_device_alloc();
|
||||||
|
mu_assert(nfc_dev != NULL, "nfc_device_data != NULL assert failed\r\n");
|
||||||
|
nfc_dev->format = NfcDeviceSaveFormatMifareClassic;
|
||||||
|
|
||||||
|
// Create a test file
|
||||||
|
nfc_generate_mf_classic(&nfc_dev->dev_data, uid_len, type);
|
||||||
|
|
||||||
|
// Get the uid from generated MFC
|
||||||
|
uint8_t uid[7] = {0};
|
||||||
|
memcpy(uid, nfc_dev->dev_data.nfc_data.uid, uid_len);
|
||||||
|
uint8_t sak = nfc_dev->dev_data.nfc_data.sak;
|
||||||
|
uint8_t atqa[2] = {};
|
||||||
|
memcpy(atqa, nfc_dev->dev_data.nfc_data.atqa, 2);
|
||||||
|
|
||||||
|
MfClassicData* mf_data = &nfc_dev->dev_data.mf_classic_data;
|
||||||
|
// Check the manufacturer block (should be uid[uid_len] + BCC (for 4byte only) + SAK + ATQA0 + ATQA1 + 0xFF[rest])
|
||||||
|
uint8_t manufacturer_block[16] = {0};
|
||||||
|
memcpy(manufacturer_block, nfc_dev->dev_data.mf_classic_data.block[0].value, 16);
|
||||||
|
mu_assert(
|
||||||
|
memcmp(manufacturer_block, uid, uid_len) == 0,
|
||||||
|
"manufacturer_block uid doesn't match the file\r\n");
|
||||||
|
|
||||||
|
uint8_t position = 0;
|
||||||
|
if(uid_len == 4) {
|
||||||
|
position = uid_len;
|
||||||
|
|
||||||
|
uint8_t bcc = 0;
|
||||||
|
|
||||||
|
for(int i = 0; i < uid_len; i++) {
|
||||||
|
bcc ^= uid[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
mu_assert(manufacturer_block[position] == bcc, "manufacturer_block bcc assert failed\r\n");
|
||||||
|
} else {
|
||||||
|
position = uid_len - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mu_assert(manufacturer_block[position + 1] == sak, "manufacturer_block sak assert failed\r\n");
|
||||||
|
|
||||||
|
mu_assert(
|
||||||
|
manufacturer_block[position + 2] == atqa[0], "manufacturer_block atqa0 assert failed\r\n");
|
||||||
|
|
||||||
|
mu_assert(
|
||||||
|
manufacturer_block[position + 3] == atqa[1], "manufacturer_block atqa1 assert failed\r\n");
|
||||||
|
|
||||||
|
for(uint8_t i = position + 4; i < 16; i++) {
|
||||||
|
mu_assert(
|
||||||
|
manufacturer_block[i] == 0xFF, "manufacturer_block[i] == 0xFF assert failed\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reference sector trailers (should be 0xFF[6] + 0xFF + 0x07 + 0x80 + 0x69 + 0xFF[6])
|
||||||
|
uint8_t sector_trailer[16] = {
|
||||||
|
0xFF,
|
||||||
|
0xFF,
|
||||||
|
0xFF,
|
||||||
|
0xFF,
|
||||||
|
0xFF,
|
||||||
|
0xFF,
|
||||||
|
0xFF,
|
||||||
|
0x07,
|
||||||
|
0x80,
|
||||||
|
0x69,
|
||||||
|
0xFF,
|
||||||
|
0xFF,
|
||||||
|
0xFF,
|
||||||
|
0xFF,
|
||||||
|
0xFF,
|
||||||
|
0xFF};
|
||||||
|
// Reference block data
|
||||||
|
uint8_t block_data[16] = {};
|
||||||
|
memset(block_data, 0xff, sizeof(block_data));
|
||||||
|
uint16_t total_blocks = mf_classic_get_total_block_num(type);
|
||||||
|
for(size_t i = 1; i < total_blocks; i++) {
|
||||||
|
if(mf_classic_is_sector_trailer(i)) {
|
||||||
|
mu_assert(
|
||||||
|
memcmp(mf_data->block[i].value, sector_trailer, 16) == 0,
|
||||||
|
"Failed sector trailer compare");
|
||||||
|
} else {
|
||||||
|
mu_assert(memcmp(mf_data->block[i].value, block_data, 16) == 0, "Failed data compare");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Save the NFC device data to the file
|
||||||
|
mu_assert(
|
||||||
|
nfc_device_save(nfc_dev, NFC_TEST_NFC_DEV_PATH),
|
||||||
|
"nfc_device_save == true assert failed\r\n");
|
||||||
|
// Verify that key cache is saved
|
||||||
|
FuriString* key_cache_name = furi_string_alloc();
|
||||||
|
furi_string_set_str(key_cache_name, "/ext/nfc/.cache/");
|
||||||
|
for(size_t i = 0; i < uid_len; i++) {
|
||||||
|
furi_string_cat_printf(key_cache_name, "%02X", uid[i]);
|
||||||
|
}
|
||||||
|
furi_string_cat_printf(key_cache_name, ".keys");
|
||||||
|
mu_assert(
|
||||||
|
storage_common_stat(nfc_dev->storage, furi_string_get_cstr(key_cache_name), NULL) ==
|
||||||
|
FSE_OK,
|
||||||
|
"Key cache file save failed");
|
||||||
|
nfc_device_free(nfc_dev);
|
||||||
|
|
||||||
|
// Load the NFC device data from the file
|
||||||
|
NfcDevice* nfc_validate = nfc_device_alloc();
|
||||||
|
mu_assert(nfc_validate, "Nfc device alloc assert");
|
||||||
|
mu_assert(
|
||||||
|
nfc_device_load(nfc_validate, NFC_TEST_NFC_DEV_PATH, false),
|
||||||
|
"nfc_device_load == true assert failed\r\n");
|
||||||
|
|
||||||
|
// Check the UID, sak, ATQA and type
|
||||||
|
mu_assert(
|
||||||
|
memcmp(nfc_validate->dev_data.nfc_data.uid, uid, uid_len) == 0,
|
||||||
|
"uid compare assert failed\r\n");
|
||||||
|
mu_assert(nfc_validate->dev_data.nfc_data.sak == sak, "sak compare assert failed\r\n");
|
||||||
|
mu_assert(
|
||||||
|
memcmp(nfc_validate->dev_data.nfc_data.atqa, atqa, 2) == 0,
|
||||||
|
"atqa compare assert failed\r\n");
|
||||||
|
mu_assert(
|
||||||
|
nfc_validate->dev_data.nfc_data.type == FuriHalNfcTypeA,
|
||||||
|
"type == FuriHalNfcTypeA assert failed\r\n");
|
||||||
|
|
||||||
|
// Check the manufacturer block
|
||||||
|
mu_assert(
|
||||||
|
memcmp(nfc_validate->dev_data.mf_classic_data.block[0].value, manufacturer_block, 16) == 0,
|
||||||
|
"manufacturer_block assert failed\r\n");
|
||||||
|
// Check other blocks
|
||||||
|
for(size_t i = 1; i < total_blocks; i++) {
|
||||||
|
if(mf_classic_is_sector_trailer(i)) {
|
||||||
|
mu_assert(
|
||||||
|
memcmp(mf_data->block[i].value, sector_trailer, 16) == 0,
|
||||||
|
"Failed sector trailer compare");
|
||||||
|
} else {
|
||||||
|
mu_assert(memcmp(mf_data->block[i].value, block_data, 16) == 0, "Failed data compare");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nfc_device_free(nfc_validate);
|
||||||
|
|
||||||
|
// Check saved key cache
|
||||||
|
NfcDevice* nfc_keys = nfc_device_alloc();
|
||||||
|
mu_assert(nfc_validate, "Nfc device alloc assert");
|
||||||
|
nfc_keys->dev_data.nfc_data.uid_len = uid_len;
|
||||||
|
memcpy(nfc_keys->dev_data.nfc_data.uid, uid, uid_len);
|
||||||
|
mu_assert(nfc_device_load_key_cache(nfc_keys), "Failed to load key cache");
|
||||||
|
uint8_t total_sec = mf_classic_get_total_sectors_num(type);
|
||||||
|
uint8_t default_key[6] = {};
|
||||||
|
memset(default_key, 0xff, 6);
|
||||||
|
for(size_t i = 0; i < total_sec; i++) {
|
||||||
|
MfClassicSectorTrailer* sec_tr =
|
||||||
|
mf_classic_get_sector_trailer_by_sector(&nfc_keys->dev_data.mf_classic_data, i);
|
||||||
|
mu_assert(memcmp(sec_tr->key_a, default_key, 6) == 0, "Failed key compare");
|
||||||
|
mu_assert(memcmp(sec_tr->key_b, default_key, 6) == 0, "Failed key compare");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete key cache file
|
||||||
|
mu_assert(
|
||||||
|
storage_common_remove(nfc_keys->storage, furi_string_get_cstr(key_cache_name)) == FSE_OK,
|
||||||
|
"Failed to remove key cache file");
|
||||||
|
furi_string_free(key_cache_name);
|
||||||
|
nfc_device_free(nfc_keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST(mf_mini_file_test) {
|
||||||
|
mf_classic_generator_test(4, MfClassicTypeMini);
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST(mf_classic_1k_4b_file_test) {
|
||||||
|
mf_classic_generator_test(4, MfClassicType1k);
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST(mf_classic_4k_4b_file_test) {
|
||||||
|
mf_classic_generator_test(4, MfClassicType4k);
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST(mf_classic_1k_7b_file_test) {
|
||||||
|
mf_classic_generator_test(7, MfClassicType1k);
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST(mf_classic_4k_7b_file_test) {
|
||||||
|
mf_classic_generator_test(7, MfClassicType4k);
|
||||||
|
}
|
||||||
|
|
||||||
MU_TEST_SUITE(nfc) {
|
MU_TEST_SUITE(nfc) {
|
||||||
nfc_test_alloc();
|
nfc_test_alloc();
|
||||||
|
|
||||||
|
MU_RUN_TEST(nfca_file_test);
|
||||||
|
MU_RUN_TEST(mf_mini_file_test);
|
||||||
|
MU_RUN_TEST(mf_classic_1k_4b_file_test);
|
||||||
|
MU_RUN_TEST(mf_classic_4k_4b_file_test);
|
||||||
|
MU_RUN_TEST(mf_classic_1k_7b_file_test);
|
||||||
|
MU_RUN_TEST(mf_classic_4k_7b_file_test);
|
||||||
MU_RUN_TEST(nfc_digital_signal_test);
|
MU_RUN_TEST(nfc_digital_signal_test);
|
||||||
MU_RUN_TEST(mf_classic_dict_test);
|
MU_RUN_TEST(mf_classic_dict_test);
|
||||||
|
MU_RUN_TEST(mf_classic_dict_load_test);
|
||||||
|
|
||||||
nfc_test_free();
|
nfc_test_free();
|
||||||
}
|
}
|
||||||
|
|||||||
62
applications/debug/unit_tests/power/power_test.c
Normal file
62
applications/debug/unit_tests/power/power_test.c
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
#include <furi.h>
|
||||||
|
#include <furi_hal.h>
|
||||||
|
#include "../minunit.h"
|
||||||
|
|
||||||
|
static void power_test_deinit(void) {
|
||||||
|
// Try to reset to default charging voltage
|
||||||
|
furi_hal_power_set_battery_charging_voltage(4.208f);
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST(test_power_charge_voltage_exact) {
|
||||||
|
// Power of 16mV charge voltages get applied exactly
|
||||||
|
// (bq25896 charge controller works in 16mV increments)
|
||||||
|
//
|
||||||
|
// This test may need adapted if other charge controllers are used in the future.
|
||||||
|
for(uint16_t charge_mv = 3840; charge_mv <= 4208; charge_mv += 16) {
|
||||||
|
float charge_volt = (float)charge_mv / 1000.0f;
|
||||||
|
furi_hal_power_set_battery_charging_voltage(charge_volt);
|
||||||
|
mu_assert_double_eq(charge_volt, furi_hal_power_get_battery_charging_voltage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST(test_power_charge_voltage_floating_imprecision) {
|
||||||
|
// 4.016f should act as 4.016 V, even with floating point imprecision
|
||||||
|
furi_hal_power_set_battery_charging_voltage(4.016f);
|
||||||
|
mu_assert_double_eq(4.016f, furi_hal_power_get_battery_charging_voltage());
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST(test_power_charge_voltage_inexact) {
|
||||||
|
// Charge voltages that are not power of 16mV get truncated down
|
||||||
|
furi_hal_power_set_battery_charging_voltage(3.841f);
|
||||||
|
mu_assert_double_eq(3.840, furi_hal_power_get_battery_charging_voltage());
|
||||||
|
|
||||||
|
furi_hal_power_set_battery_charging_voltage(3.900f);
|
||||||
|
mu_assert_double_eq(3.888, furi_hal_power_get_battery_charging_voltage());
|
||||||
|
|
||||||
|
furi_hal_power_set_battery_charging_voltage(4.200f);
|
||||||
|
mu_assert_double_eq(4.192, furi_hal_power_get_battery_charging_voltage());
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST(test_power_charge_voltage_invalid_clamped) {
|
||||||
|
// Out-of-range charge voltages get clamped to 3.840 V and 4.208 V
|
||||||
|
furi_hal_power_set_battery_charging_voltage(3.808f);
|
||||||
|
mu_assert_double_eq(3.840, furi_hal_power_get_battery_charging_voltage());
|
||||||
|
|
||||||
|
// NOTE: Intentionally picking a small increment above 4.208 V to reduce the risk of an
|
||||||
|
// unhappy battery if this fails.
|
||||||
|
furi_hal_power_set_battery_charging_voltage(4.240f);
|
||||||
|
mu_assert_double_eq(4.208, furi_hal_power_get_battery_charging_voltage());
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST_SUITE(test_power_suite) {
|
||||||
|
MU_RUN_TEST(test_power_charge_voltage_exact);
|
||||||
|
MU_RUN_TEST(test_power_charge_voltage_floating_imprecision);
|
||||||
|
MU_RUN_TEST(test_power_charge_voltage_inexact);
|
||||||
|
MU_RUN_TEST(test_power_charge_voltage_invalid_clamped);
|
||||||
|
power_test_deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run_minunit_test_power() {
|
||||||
|
MU_RUN_SUITE(test_power_suite);
|
||||||
|
return MU_EXIT_CODE;
|
||||||
|
}
|
||||||
@@ -89,7 +89,7 @@ static void test_rpc_setup(void) {
|
|||||||
}
|
}
|
||||||
furi_check(rpc_session[0].session);
|
furi_check(rpc_session[0].session);
|
||||||
|
|
||||||
rpc_session[0].output_stream = furi_stream_buffer_alloc(1000, 1);
|
rpc_session[0].output_stream = furi_stream_buffer_alloc(4096, 1);
|
||||||
rpc_session_set_send_bytes_callback(rpc_session[0].session, output_bytes_callback);
|
rpc_session_set_send_bytes_callback(rpc_session[0].session, output_bytes_callback);
|
||||||
rpc_session[0].close_session_semaphore = xSemaphoreCreateBinary();
|
rpc_session[0].close_session_semaphore = xSemaphoreCreateBinary();
|
||||||
rpc_session[0].terminate_semaphore = xSemaphoreCreateBinary();
|
rpc_session[0].terminate_semaphore = xSemaphoreCreateBinary();
|
||||||
|
|||||||
@@ -43,11 +43,8 @@ MU_TEST(storage_file_open_lock) {
|
|||||||
File* file = storage_file_alloc(storage);
|
File* file = storage_file_alloc(storage);
|
||||||
|
|
||||||
// file_locker thread start
|
// file_locker thread start
|
||||||
FuriThread* locker_thread = furi_thread_alloc();
|
FuriThread* locker_thread =
|
||||||
furi_thread_set_name(locker_thread, "StorageFileLocker");
|
furi_thread_alloc_ex("StorageFileLocker", 2048, storage_file_locker, semaphore);
|
||||||
furi_thread_set_stack_size(locker_thread, 2048);
|
|
||||||
furi_thread_set_context(locker_thread, semaphore);
|
|
||||||
furi_thread_set_callback(locker_thread, storage_file_locker);
|
|
||||||
furi_thread_start(locker_thread);
|
furi_thread_start(locker_thread);
|
||||||
|
|
||||||
// wait for file lock
|
// wait for file lock
|
||||||
@@ -133,11 +130,8 @@ MU_TEST(storage_dir_open_lock) {
|
|||||||
File* file = storage_file_alloc(storage);
|
File* file = storage_file_alloc(storage);
|
||||||
|
|
||||||
// file_locker thread start
|
// file_locker thread start
|
||||||
FuriThread* locker_thread = furi_thread_alloc();
|
FuriThread* locker_thread =
|
||||||
furi_thread_set_name(locker_thread, "StorageDirLocker");
|
furi_thread_alloc_ex("StorageDirLocker", 2048, storage_dir_locker, semaphore);
|
||||||
furi_thread_set_stack_size(locker_thread, 2048);
|
|
||||||
furi_thread_set_context(locker_thread, semaphore);
|
|
||||||
furi_thread_set_callback(locker_thread, storage_dir_locker);
|
|
||||||
furi_thread_start(locker_thread);
|
furi_thread_start(locker_thread);
|
||||||
|
|
||||||
// wait for dir lock
|
// wait for dir lock
|
||||||
|
|||||||
@@ -72,8 +72,32 @@ MU_TEST_1(stream_composite_subtest, Stream* stream) {
|
|||||||
mu_check(stream_seek(stream, -3, StreamOffsetFromEnd));
|
mu_check(stream_seek(stream, -3, StreamOffsetFromEnd));
|
||||||
mu_check(stream_tell(stream) == 4);
|
mu_check(stream_tell(stream) == 4);
|
||||||
|
|
||||||
// write string with replacemet
|
// test seeks to char. content: '1337_69'
|
||||||
|
stream_rewind(stream);
|
||||||
|
mu_check(stream_seek_to_char(stream, '3', StreamDirectionForward));
|
||||||
|
mu_check(stream_tell(stream) == 1);
|
||||||
|
mu_check(stream_seek_to_char(stream, '3', StreamDirectionForward));
|
||||||
|
mu_check(stream_tell(stream) == 2);
|
||||||
|
mu_check(stream_seek_to_char(stream, '_', StreamDirectionForward));
|
||||||
|
mu_check(stream_tell(stream) == 4);
|
||||||
|
mu_check(stream_seek_to_char(stream, '9', StreamDirectionForward));
|
||||||
|
mu_check(stream_tell(stream) == 6);
|
||||||
|
mu_check(!stream_seek_to_char(stream, '9', StreamDirectionForward));
|
||||||
|
mu_check(stream_tell(stream) == 6);
|
||||||
|
mu_check(stream_seek_to_char(stream, '_', StreamDirectionBackward));
|
||||||
|
mu_check(stream_tell(stream) == 4);
|
||||||
|
mu_check(stream_seek_to_char(stream, '3', StreamDirectionBackward));
|
||||||
|
mu_check(stream_tell(stream) == 2);
|
||||||
|
mu_check(stream_seek_to_char(stream, '3', StreamDirectionBackward));
|
||||||
|
mu_check(stream_tell(stream) == 1);
|
||||||
|
mu_check(!stream_seek_to_char(stream, '3', StreamDirectionBackward));
|
||||||
|
mu_check(stream_tell(stream) == 1);
|
||||||
|
mu_check(stream_seek_to_char(stream, '1', StreamDirectionBackward));
|
||||||
|
mu_check(stream_tell(stream) == 0);
|
||||||
|
|
||||||
|
// write string with replacement
|
||||||
// "1337_69" -> "1337lee"
|
// "1337_69" -> "1337lee"
|
||||||
|
mu_check(stream_seek(stream, 4, StreamOffsetFromStart));
|
||||||
mu_check(stream_write_string(stream, string_lee) == 3);
|
mu_check(stream_write_string(stream, string_lee) == 3);
|
||||||
mu_check(stream_size(stream) == 7);
|
mu_check(stream_size(stream) == 7);
|
||||||
mu_check(stream_tell(stream) == 7);
|
mu_check(stream_tell(stream) == 7);
|
||||||
@@ -219,21 +243,21 @@ MU_TEST_1(stream_composite_subtest, Stream* stream) {
|
|||||||
mu_check(stream_eof(stream));
|
mu_check(stream_eof(stream));
|
||||||
mu_assert_int_eq(0, stream_tell(stream));
|
mu_assert_int_eq(0, stream_tell(stream));
|
||||||
|
|
||||||
// insert formated string at the end of stream
|
// insert formatted string at the end of stream
|
||||||
// "" -> "dio666"
|
// "" -> "dio666"
|
||||||
mu_check(stream_insert_format(stream, "%s%d", "dio", 666));
|
mu_check(stream_insert_format(stream, "%s%d", "dio", 666));
|
||||||
mu_assert_int_eq(6, stream_size(stream));
|
mu_assert_int_eq(6, stream_size(stream));
|
||||||
mu_check(stream_eof(stream));
|
mu_check(stream_eof(stream));
|
||||||
mu_assert_int_eq(6, stream_tell(stream));
|
mu_assert_int_eq(6, stream_tell(stream));
|
||||||
|
|
||||||
// insert formated string at the end of stream
|
// insert formatted string at the end of stream
|
||||||
// "dio666" -> "dio666zlo555"
|
// "dio666" -> "dio666zlo555"
|
||||||
mu_check(stream_insert_format(stream, "%s%d", "zlo", 555));
|
mu_check(stream_insert_format(stream, "%s%d", "zlo", 555));
|
||||||
mu_assert_int_eq(12, stream_size(stream));
|
mu_assert_int_eq(12, stream_size(stream));
|
||||||
mu_check(stream_eof(stream));
|
mu_check(stream_eof(stream));
|
||||||
mu_assert_int_eq(12, stream_tell(stream));
|
mu_assert_int_eq(12, stream_tell(stream));
|
||||||
|
|
||||||
// insert formated string at the 6 pos
|
// insert formatted string at the 6 pos
|
||||||
// "dio666" -> "dio666baba13zlo555"
|
// "dio666" -> "dio666baba13zlo555"
|
||||||
mu_check(stream_seek(stream, 6, StreamOffsetFromStart));
|
mu_check(stream_seek(stream, 6, StreamOffsetFromStart));
|
||||||
mu_check(stream_insert_format(stream, "%s%d", "baba", 13));
|
mu_check(stream_insert_format(stream, "%s%d", "baba", 13));
|
||||||
|
|||||||
@@ -5,15 +5,16 @@
|
|||||||
#include <lib/subghz/transmitter.h>
|
#include <lib/subghz/transmitter.h>
|
||||||
#include <lib/subghz/subghz_keystore.h>
|
#include <lib/subghz/subghz_keystore.h>
|
||||||
#include <lib/subghz/subghz_file_encoder_worker.h>
|
#include <lib/subghz/subghz_file_encoder_worker.h>
|
||||||
#include <lib/subghz/protocols/registry.h>
|
#include <lib/subghz/protocols/protocol_items.h>
|
||||||
#include <flipper_format/flipper_format_i.h>
|
#include <flipper_format/flipper_format_i.h>
|
||||||
|
|
||||||
#define TAG "SubGhz TEST"
|
#define TAG "SubGhz TEST"
|
||||||
#define KEYSTORE_DIR_NAME EXT_PATH("subghz/assets/keeloq_mfcodes")
|
#define KEYSTORE_DIR_NAME EXT_PATH("subghz/assets/keeloq_mfcodes")
|
||||||
#define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo")
|
#define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo")
|
||||||
#define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s")
|
#define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s")
|
||||||
|
#define ALUTECH_AT_4N_DIR_NAME EXT_PATH("subghz/assets/alutech_at_4n")
|
||||||
#define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub")
|
#define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub")
|
||||||
#define TEST_RANDOM_COUNT_PARSE 233
|
#define TEST_RANDOM_COUNT_PARSE 329
|
||||||
#define TEST_TIMEOUT 10000
|
#define TEST_TIMEOUT 10000
|
||||||
|
|
||||||
static SubGhzEnvironment* environment_handler;
|
static SubGhzEnvironment* environment_handler;
|
||||||
@@ -43,6 +44,10 @@ static void subghz_test_init(void) {
|
|||||||
environment_handler, CAME_ATOMO_DIR_NAME);
|
environment_handler, CAME_ATOMO_DIR_NAME);
|
||||||
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
|
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
|
||||||
environment_handler, NICE_FLOR_S_DIR_NAME);
|
environment_handler, NICE_FLOR_S_DIR_NAME);
|
||||||
|
subghz_environment_set_alutech_at_4n_rainbow_table_file_name(
|
||||||
|
environment_handler, ALUTECH_AT_4N_DIR_NAME);
|
||||||
|
subghz_environment_set_protocol_registry(
|
||||||
|
environment_handler, (void*)&subghz_protocol_registry);
|
||||||
|
|
||||||
receiver_handler = subghz_receiver_alloc_init(environment_handler);
|
receiver_handler = subghz_receiver_alloc_init(environment_handler);
|
||||||
subghz_receiver_set_filter(receiver_handler, SubGhzProtocolFlag_Decodable);
|
subghz_receiver_set_filter(receiver_handler, SubGhzProtocolFlag_Decodable);
|
||||||
@@ -207,6 +212,152 @@ MU_TEST(subghz_keystore_test) {
|
|||||||
"Test keystore error");
|
"Test keystore error");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SubGhzHalAsyncTxTestTypeNormal,
|
||||||
|
SubGhzHalAsyncTxTestTypeInvalidStart,
|
||||||
|
SubGhzHalAsyncTxTestTypeInvalidMid,
|
||||||
|
SubGhzHalAsyncTxTestTypeInvalidEnd,
|
||||||
|
SubGhzHalAsyncTxTestTypeResetStart,
|
||||||
|
SubGhzHalAsyncTxTestTypeResetMid,
|
||||||
|
SubGhzHalAsyncTxTestTypeResetEnd,
|
||||||
|
} SubGhzHalAsyncTxTestType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
SubGhzHalAsyncTxTestType type;
|
||||||
|
size_t pos;
|
||||||
|
} SubGhzHalAsyncTxTest;
|
||||||
|
|
||||||
|
#define SUBGHZ_HAL_TEST_DURATION 1
|
||||||
|
|
||||||
|
static LevelDuration subghz_hal_async_tx_test_yield(void* context) {
|
||||||
|
SubGhzHalAsyncTxTest* test = context;
|
||||||
|
bool is_odd = test->pos % 2;
|
||||||
|
|
||||||
|
if(test->type == SubGhzHalAsyncTxTestTypeNormal) {
|
||||||
|
if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
|
||||||
|
} else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_reset();
|
||||||
|
} else {
|
||||||
|
furi_crash("Yield after reset");
|
||||||
|
}
|
||||||
|
} else if(test->type == SubGhzHalAsyncTxTestTypeInvalidStart) {
|
||||||
|
if(test->pos == 0) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION);
|
||||||
|
} else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
|
||||||
|
} else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_reset();
|
||||||
|
} else {
|
||||||
|
furi_crash("Yield after reset");
|
||||||
|
}
|
||||||
|
} else if(test->type == SubGhzHalAsyncTxTestTypeInvalidMid) {
|
||||||
|
if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION);
|
||||||
|
} else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
|
||||||
|
} else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_reset();
|
||||||
|
} else {
|
||||||
|
furi_crash("Yield after reset");
|
||||||
|
}
|
||||||
|
} else if(test->type == SubGhzHalAsyncTxTestTypeInvalidEnd) {
|
||||||
|
if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION);
|
||||||
|
} else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
|
||||||
|
} else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_reset();
|
||||||
|
} else {
|
||||||
|
furi_crash("Yield after reset");
|
||||||
|
}
|
||||||
|
} else if(test->type == SubGhzHalAsyncTxTestTypeResetStart) {
|
||||||
|
if(test->pos == 0) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_reset();
|
||||||
|
} else {
|
||||||
|
furi_crash("Yield after reset");
|
||||||
|
}
|
||||||
|
} else if(test->type == SubGhzHalAsyncTxTestTypeResetMid) {
|
||||||
|
if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
|
||||||
|
} else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_reset();
|
||||||
|
} else {
|
||||||
|
furi_crash("Yield after reset");
|
||||||
|
}
|
||||||
|
} else if(test->type == SubGhzHalAsyncTxTestTypeResetEnd) {
|
||||||
|
if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
|
||||||
|
} else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_reset();
|
||||||
|
} else {
|
||||||
|
furi_crash("Yield after reset");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
furi_crash("Programming error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestType type) {
|
||||||
|
SubGhzHalAsyncTxTest test = {0};
|
||||||
|
test.type = type;
|
||||||
|
furi_hal_subghz_reset();
|
||||||
|
furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async);
|
||||||
|
furi_hal_subghz_set_frequency_and_path(433920000);
|
||||||
|
|
||||||
|
if(!furi_hal_subghz_start_async_tx(subghz_hal_async_tx_test_yield, &test)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(!furi_hal_subghz_is_async_tx_complete()) {
|
||||||
|
furi_delay_ms(10);
|
||||||
|
}
|
||||||
|
furi_hal_subghz_stop_async_tx();
|
||||||
|
furi_hal_subghz_sleep();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST(subghz_hal_async_tx_test) {
|
||||||
|
mu_assert(
|
||||||
|
subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeNormal),
|
||||||
|
"Test furi_hal_async_tx normal");
|
||||||
|
mu_assert(
|
||||||
|
subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeInvalidStart),
|
||||||
|
"Test furi_hal_async_tx invalid start");
|
||||||
|
mu_assert(
|
||||||
|
subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeInvalidMid),
|
||||||
|
"Test furi_hal_async_tx invalid mid");
|
||||||
|
mu_assert(
|
||||||
|
subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeInvalidEnd),
|
||||||
|
"Test furi_hal_async_tx invalid end");
|
||||||
|
mu_assert(
|
||||||
|
subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeResetStart),
|
||||||
|
"Test furi_hal_async_tx reset start");
|
||||||
|
mu_assert(
|
||||||
|
subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeResetMid),
|
||||||
|
"Test furi_hal_async_tx reset mid");
|
||||||
|
mu_assert(
|
||||||
|
subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeResetEnd),
|
||||||
|
"Test furi_hal_async_tx reset end");
|
||||||
|
}
|
||||||
|
|
||||||
//test decoders
|
//test decoders
|
||||||
MU_TEST(subghz_decoder_came_atomo_test) {
|
MU_TEST(subghz_decoder_came_atomo_test) {
|
||||||
mu_assert(
|
mu_assert(
|
||||||
@@ -341,6 +492,14 @@ MU_TEST(subghz_decoder_linear_test) {
|
|||||||
"Test decoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n");
|
"Test decoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MU_TEST(subghz_decoder_linear_delta3_test) {
|
||||||
|
mu_assert(
|
||||||
|
subghz_decoder_test(
|
||||||
|
EXT_PATH("unit_tests/subghz/linear_delta3_raw.sub"),
|
||||||
|
SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME),
|
||||||
|
"Test decoder " SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME " error\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
MU_TEST(subghz_decoder_megacode_test) {
|
MU_TEST(subghz_decoder_megacode_test) {
|
||||||
mu_assert(
|
mu_assert(
|
||||||
subghz_decoder_test(
|
subghz_decoder_test(
|
||||||
@@ -413,11 +572,11 @@ MU_TEST(subghz_decoder_honeywell_wdb_test) {
|
|||||||
"Test decoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n");
|
"Test decoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
MU_TEST(subghz_decoder_magellen_test) {
|
MU_TEST(subghz_decoder_magellan_test) {
|
||||||
mu_assert(
|
mu_assert(
|
||||||
subghz_decoder_test(
|
subghz_decoder_test(
|
||||||
EXT_PATH("unit_tests/subghz/magellen_raw.sub"), SUBGHZ_PROTOCOL_MAGELLEN_NAME),
|
EXT_PATH("unit_tests/subghz/magellan_raw.sub"), SUBGHZ_PROTOCOL_MAGELLAN_NAME),
|
||||||
"Test decoder " SUBGHZ_PROTOCOL_MAGELLEN_NAME " error\r\n");
|
"Test decoder " SUBGHZ_PROTOCOL_MAGELLAN_NAME " error\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
MU_TEST(subghz_decoder_intertechno_v3_test) {
|
MU_TEST(subghz_decoder_intertechno_v3_test) {
|
||||||
@@ -435,11 +594,55 @@ MU_TEST(subghz_decoder_clemsa_test) {
|
|||||||
"Test decoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n");
|
"Test decoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
MU_TEST(subghz_decoder_oregon2_test) {
|
MU_TEST(subghz_decoder_ansonic_test) {
|
||||||
mu_assert(
|
mu_assert(
|
||||||
subghz_decoder_test(
|
subghz_decoder_test(
|
||||||
EXT_PATH("unit_tests/subghz/oregon2_raw.sub"), SUBGHZ_PROTOCOL_OREGON2_NAME),
|
EXT_PATH("unit_tests/subghz/ansonic_raw.sub"), SUBGHZ_PROTOCOL_ANSONIC_NAME),
|
||||||
"Test decoder " SUBGHZ_PROTOCOL_OREGON2_NAME " error\r\n");
|
"Test decoder " SUBGHZ_PROTOCOL_ANSONIC_NAME " error\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST(subghz_decoder_smc5326_test) {
|
||||||
|
mu_assert(
|
||||||
|
subghz_decoder_test(
|
||||||
|
EXT_PATH("unit_tests/subghz/smc5326_raw.sub"), SUBGHZ_PROTOCOL_SMC5326_NAME),
|
||||||
|
"Test decoder " SUBGHZ_PROTOCOL_SMC5326_NAME " error\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST(subghz_decoder_holtek_ht12x_test) {
|
||||||
|
mu_assert(
|
||||||
|
subghz_decoder_test(
|
||||||
|
EXT_PATH("unit_tests/subghz/holtek_ht12x_raw.sub"), SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME),
|
||||||
|
"Test decoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST(subghz_decoder_dooya_test) {
|
||||||
|
mu_assert(
|
||||||
|
subghz_decoder_test(
|
||||||
|
EXT_PATH("unit_tests/subghz/dooya_raw.sub"), SUBGHZ_PROTOCOL_DOOYA_NAME),
|
||||||
|
"Test decoder " SUBGHZ_PROTOCOL_DOOYA_NAME " error\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST(subghz_decoder_alutech_at_4n_test) {
|
||||||
|
mu_assert(
|
||||||
|
subghz_decoder_test(
|
||||||
|
EXT_PATH("unit_tests/subghz/alutech_at_4n_raw.sub"),
|
||||||
|
SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME),
|
||||||
|
"Test decoder " SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME " error\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST(subghz_decoder_nice_one_test) {
|
||||||
|
mu_assert(
|
||||||
|
subghz_decoder_test(
|
||||||
|
EXT_PATH("unit_tests/subghz/nice_one_raw.sub"), SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME),
|
||||||
|
"Test decoder " SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME " error\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST(subghz_decoder_kinggates_stylo4k_test) {
|
||||||
|
mu_assert(
|
||||||
|
subghz_decoder_test(
|
||||||
|
EXT_PATH("unit_tests/subghz/kinggates_stylo4k_raw.sub"),
|
||||||
|
SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME),
|
||||||
|
"Test decoder " SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME " error\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
//test encoders
|
//test encoders
|
||||||
@@ -485,6 +688,12 @@ MU_TEST(subghz_encoder_linear_test) {
|
|||||||
"Test encoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n");
|
"Test encoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MU_TEST(subghz_encoder_linear_delta3_test) {
|
||||||
|
mu_assert(
|
||||||
|
subghz_encoder_test(EXT_PATH("unit_tests/subghz/linear_delta3.sub")),
|
||||||
|
"Test encoder " SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME " error\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
MU_TEST(subghz_encoder_megacode_test) {
|
MU_TEST(subghz_encoder_megacode_test) {
|
||||||
mu_assert(
|
mu_assert(
|
||||||
subghz_encoder_test(EXT_PATH("unit_tests/subghz/megacode.sub")),
|
subghz_encoder_test(EXT_PATH("unit_tests/subghz/megacode.sub")),
|
||||||
@@ -545,10 +754,10 @@ MU_TEST(subghz_encoder_honeywell_wdb_test) {
|
|||||||
"Test encoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n");
|
"Test encoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
MU_TEST(subghz_encoder_magellen_test) {
|
MU_TEST(subghz_encoder_magellan_test) {
|
||||||
mu_assert(
|
mu_assert(
|
||||||
subghz_encoder_test(EXT_PATH("unit_tests/subghz/magellen.sub")),
|
subghz_encoder_test(EXT_PATH("unit_tests/subghz/magellan.sub")),
|
||||||
"Test encoder " SUBGHZ_PROTOCOL_MAGELLEN_NAME " error\r\n");
|
"Test encoder " SUBGHZ_PROTOCOL_MAGELLAN_NAME " error\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
MU_TEST(subghz_encoder_intertechno_v3_test) {
|
MU_TEST(subghz_encoder_intertechno_v3_test) {
|
||||||
@@ -563,6 +772,30 @@ MU_TEST(subghz_encoder_clemsa_test) {
|
|||||||
"Test encoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n");
|
"Test encoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MU_TEST(subghz_encoder_ansonic_test) {
|
||||||
|
mu_assert(
|
||||||
|
subghz_encoder_test(EXT_PATH("unit_tests/subghz/ansonic.sub")),
|
||||||
|
"Test encoder " SUBGHZ_PROTOCOL_ANSONIC_NAME " error\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST(subghz_encoder_smc5326_test) {
|
||||||
|
mu_assert(
|
||||||
|
subghz_encoder_test(EXT_PATH("unit_tests/subghz/smc5326.sub")),
|
||||||
|
"Test encoder " SUBGHZ_PROTOCOL_SMC5326_NAME " error\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST(subghz_encoder_holtek_ht12x_test) {
|
||||||
|
mu_assert(
|
||||||
|
subghz_encoder_test(EXT_PATH("unit_tests/subghz/holtek_ht12x.sub")),
|
||||||
|
"Test encoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST(subghz_encoder_dooya_test) {
|
||||||
|
mu_assert(
|
||||||
|
subghz_encoder_test(EXT_PATH("unit_tests/subghz/dooya.sub")),
|
||||||
|
"Test encoder " SUBGHZ_PROTOCOL_DOOYA_NAME " error\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
MU_TEST(subghz_random_test) {
|
MU_TEST(subghz_random_test) {
|
||||||
mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n");
|
mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n");
|
||||||
}
|
}
|
||||||
@@ -571,6 +804,8 @@ MU_TEST_SUITE(subghz) {
|
|||||||
subghz_test_init();
|
subghz_test_init();
|
||||||
MU_RUN_TEST(subghz_keystore_test);
|
MU_RUN_TEST(subghz_keystore_test);
|
||||||
|
|
||||||
|
MU_RUN_TEST(subghz_hal_async_tx_test);
|
||||||
|
|
||||||
MU_RUN_TEST(subghz_decoder_came_atomo_test);
|
MU_RUN_TEST(subghz_decoder_came_atomo_test);
|
||||||
MU_RUN_TEST(subghz_decoder_came_test);
|
MU_RUN_TEST(subghz_decoder_came_test);
|
||||||
MU_RUN_TEST(subghz_decoder_came_twee_test);
|
MU_RUN_TEST(subghz_decoder_came_twee_test);
|
||||||
@@ -590,6 +825,7 @@ MU_TEST_SUITE(subghz) {
|
|||||||
MU_RUN_TEST(subghz_decoder_somfy_telis_test);
|
MU_RUN_TEST(subghz_decoder_somfy_telis_test);
|
||||||
MU_RUN_TEST(subghz_decoder_star_line_test);
|
MU_RUN_TEST(subghz_decoder_star_line_test);
|
||||||
MU_RUN_TEST(subghz_decoder_linear_test);
|
MU_RUN_TEST(subghz_decoder_linear_test);
|
||||||
|
MU_RUN_TEST(subghz_decoder_linear_delta3_test);
|
||||||
MU_RUN_TEST(subghz_decoder_megacode_test);
|
MU_RUN_TEST(subghz_decoder_megacode_test);
|
||||||
MU_RUN_TEST(subghz_decoder_secplus_v1_test);
|
MU_RUN_TEST(subghz_decoder_secplus_v1_test);
|
||||||
MU_RUN_TEST(subghz_decoder_secplus_v2_test);
|
MU_RUN_TEST(subghz_decoder_secplus_v2_test);
|
||||||
@@ -600,10 +836,16 @@ MU_TEST_SUITE(subghz) {
|
|||||||
MU_RUN_TEST(subghz_decoder_doitrand_test);
|
MU_RUN_TEST(subghz_decoder_doitrand_test);
|
||||||
MU_RUN_TEST(subghz_decoder_phoenix_v2_test);
|
MU_RUN_TEST(subghz_decoder_phoenix_v2_test);
|
||||||
MU_RUN_TEST(subghz_decoder_honeywell_wdb_test);
|
MU_RUN_TEST(subghz_decoder_honeywell_wdb_test);
|
||||||
MU_RUN_TEST(subghz_decoder_magellen_test);
|
MU_RUN_TEST(subghz_decoder_magellan_test);
|
||||||
MU_RUN_TEST(subghz_decoder_intertechno_v3_test);
|
MU_RUN_TEST(subghz_decoder_intertechno_v3_test);
|
||||||
MU_RUN_TEST(subghz_decoder_clemsa_test);
|
MU_RUN_TEST(subghz_decoder_clemsa_test);
|
||||||
MU_RUN_TEST(subghz_decoder_oregon2_test);
|
MU_RUN_TEST(subghz_decoder_ansonic_test);
|
||||||
|
MU_RUN_TEST(subghz_decoder_smc5326_test);
|
||||||
|
MU_RUN_TEST(subghz_decoder_holtek_ht12x_test);
|
||||||
|
MU_RUN_TEST(subghz_decoder_dooya_test);
|
||||||
|
MU_RUN_TEST(subghz_decoder_alutech_at_4n_test);
|
||||||
|
MU_RUN_TEST(subghz_decoder_nice_one_test);
|
||||||
|
MU_RUN_TEST(subghz_decoder_kinggates_stylo4k_test);
|
||||||
|
|
||||||
MU_RUN_TEST(subghz_encoder_princeton_test);
|
MU_RUN_TEST(subghz_encoder_princeton_test);
|
||||||
MU_RUN_TEST(subghz_encoder_came_test);
|
MU_RUN_TEST(subghz_encoder_came_test);
|
||||||
@@ -612,6 +854,7 @@ MU_TEST_SUITE(subghz) {
|
|||||||
MU_RUN_TEST(subghz_encoder_nice_flo_test);
|
MU_RUN_TEST(subghz_encoder_nice_flo_test);
|
||||||
MU_RUN_TEST(subghz_encoder_keelog_test);
|
MU_RUN_TEST(subghz_encoder_keelog_test);
|
||||||
MU_RUN_TEST(subghz_encoder_linear_test);
|
MU_RUN_TEST(subghz_encoder_linear_test);
|
||||||
|
MU_RUN_TEST(subghz_encoder_linear_delta3_test);
|
||||||
MU_RUN_TEST(subghz_encoder_megacode_test);
|
MU_RUN_TEST(subghz_encoder_megacode_test);
|
||||||
MU_RUN_TEST(subghz_encoder_holtek_test);
|
MU_RUN_TEST(subghz_encoder_holtek_test);
|
||||||
MU_RUN_TEST(subghz_encoder_secplus_v1_test);
|
MU_RUN_TEST(subghz_encoder_secplus_v1_test);
|
||||||
@@ -622,9 +865,13 @@ MU_TEST_SUITE(subghz) {
|
|||||||
MU_RUN_TEST(subghz_encoder_doitrand_test);
|
MU_RUN_TEST(subghz_encoder_doitrand_test);
|
||||||
MU_RUN_TEST(subghz_encoder_phoenix_v2_test);
|
MU_RUN_TEST(subghz_encoder_phoenix_v2_test);
|
||||||
MU_RUN_TEST(subghz_encoder_honeywell_wdb_test);
|
MU_RUN_TEST(subghz_encoder_honeywell_wdb_test);
|
||||||
MU_RUN_TEST(subghz_encoder_magellen_test);
|
MU_RUN_TEST(subghz_encoder_magellan_test);
|
||||||
MU_RUN_TEST(subghz_encoder_intertechno_v3_test);
|
MU_RUN_TEST(subghz_encoder_intertechno_v3_test);
|
||||||
MU_RUN_TEST(subghz_encoder_clemsa_test);
|
MU_RUN_TEST(subghz_encoder_clemsa_test);
|
||||||
|
MU_RUN_TEST(subghz_encoder_ansonic_test);
|
||||||
|
MU_RUN_TEST(subghz_encoder_smc5326_test);
|
||||||
|
MU_RUN_TEST(subghz_encoder_holtek_ht12x_test);
|
||||||
|
MU_RUN_TEST(subghz_encoder_dooya_test);
|
||||||
|
|
||||||
MU_RUN_TEST(subghz_random_test);
|
MU_RUN_TEST(subghz_random_test);
|
||||||
subghz_test_deinit();
|
subghz_test_deinit();
|
||||||
|
|||||||
@@ -9,19 +9,24 @@
|
|||||||
#define TAG "UnitTests"
|
#define TAG "UnitTests"
|
||||||
|
|
||||||
int run_minunit_test_furi();
|
int run_minunit_test_furi();
|
||||||
|
int run_minunit_test_furi_hal();
|
||||||
int run_minunit_test_furi_string();
|
int run_minunit_test_furi_string();
|
||||||
int run_minunit_test_infrared();
|
int run_minunit_test_infrared();
|
||||||
int run_minunit_test_rpc();
|
int run_minunit_test_rpc();
|
||||||
|
int run_minunit_test_manifest();
|
||||||
int run_minunit_test_flipper_format();
|
int run_minunit_test_flipper_format();
|
||||||
int run_minunit_test_flipper_format_string();
|
int run_minunit_test_flipper_format_string();
|
||||||
int run_minunit_test_stream();
|
int run_minunit_test_stream();
|
||||||
int run_minunit_test_storage();
|
int run_minunit_test_storage();
|
||||||
int run_minunit_test_subghz();
|
int run_minunit_test_subghz();
|
||||||
int run_minunit_test_dirwalk();
|
int run_minunit_test_dirwalk();
|
||||||
|
int run_minunit_test_power();
|
||||||
int run_minunit_test_protocol_dict();
|
int run_minunit_test_protocol_dict();
|
||||||
int run_minunit_test_lfrfid_protocols();
|
int run_minunit_test_lfrfid_protocols();
|
||||||
int run_minunit_test_nfc();
|
int run_minunit_test_nfc();
|
||||||
int run_minunit_test_bit_lib();
|
int run_minunit_test_bit_lib();
|
||||||
|
int run_minunit_test_float_tools();
|
||||||
|
int run_minunit_test_bt();
|
||||||
|
|
||||||
typedef int (*UnitTestEntry)();
|
typedef int (*UnitTestEntry)();
|
||||||
|
|
||||||
@@ -32,19 +37,24 @@ typedef struct {
|
|||||||
|
|
||||||
const UnitTest unit_tests[] = {
|
const UnitTest unit_tests[] = {
|
||||||
{.name = "furi", .entry = run_minunit_test_furi},
|
{.name = "furi", .entry = run_minunit_test_furi},
|
||||||
|
{.name = "furi_hal", .entry = run_minunit_test_furi_hal},
|
||||||
{.name = "furi_string", .entry = run_minunit_test_furi_string},
|
{.name = "furi_string", .entry = run_minunit_test_furi_string},
|
||||||
{.name = "storage", .entry = run_minunit_test_storage},
|
{.name = "storage", .entry = run_minunit_test_storage},
|
||||||
{.name = "stream", .entry = run_minunit_test_stream},
|
{.name = "stream", .entry = run_minunit_test_stream},
|
||||||
{.name = "dirwalk", .entry = run_minunit_test_dirwalk},
|
{.name = "dirwalk", .entry = run_minunit_test_dirwalk},
|
||||||
|
{.name = "manifest", .entry = run_minunit_test_manifest},
|
||||||
{.name = "flipper_format", .entry = run_minunit_test_flipper_format},
|
{.name = "flipper_format", .entry = run_minunit_test_flipper_format},
|
||||||
{.name = "flipper_format_string", .entry = run_minunit_test_flipper_format_string},
|
{.name = "flipper_format_string", .entry = run_minunit_test_flipper_format_string},
|
||||||
{.name = "rpc", .entry = run_minunit_test_rpc},
|
{.name = "rpc", .entry = run_minunit_test_rpc},
|
||||||
{.name = "subghz", .entry = run_minunit_test_subghz},
|
{.name = "subghz", .entry = run_minunit_test_subghz},
|
||||||
{.name = "infrared", .entry = run_minunit_test_infrared},
|
{.name = "infrared", .entry = run_minunit_test_infrared},
|
||||||
{.name = "nfc", .entry = run_minunit_test_nfc},
|
{.name = "nfc", .entry = run_minunit_test_nfc},
|
||||||
|
{.name = "power", .entry = run_minunit_test_power},
|
||||||
{.name = "protocol_dict", .entry = run_minunit_test_protocol_dict},
|
{.name = "protocol_dict", .entry = run_minunit_test_protocol_dict},
|
||||||
{.name = "lfrfid", .entry = run_minunit_test_lfrfid_protocols},
|
{.name = "lfrfid", .entry = run_minunit_test_lfrfid_protocols},
|
||||||
{.name = "bit_lib", .entry = run_minunit_test_bit_lib},
|
{.name = "bit_lib", .entry = run_minunit_test_bit_lib},
|
||||||
|
{.name = "float_tools", .entry = run_minunit_test_float_tools},
|
||||||
|
{.name = "bt", .entry = run_minunit_test_bt},
|
||||||
};
|
};
|
||||||
|
|
||||||
void minunit_print_progress() {
|
void minunit_print_progress() {
|
||||||
@@ -60,14 +70,13 @@ void minunit_print_progress() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void minunit_print_fail(const char* str) {
|
void minunit_print_fail(const char* str) {
|
||||||
printf(FURI_LOG_CLR_E "%s\r\n" FURI_LOG_CLR_RESET, str);
|
printf(_FURI_LOG_CLR_E "%s\r\n" _FURI_LOG_CLR_RESET, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
void unit_tests_cli(Cli* cli, FuriString* args, void* context) {
|
void unit_tests_cli(Cli* cli, FuriString* args, void* context) {
|
||||||
UNUSED(cli);
|
UNUSED(cli);
|
||||||
UNUSED(args);
|
UNUSED(args);
|
||||||
UNUSED(context);
|
UNUSED(context);
|
||||||
uint32_t failed_tests = 0;
|
|
||||||
minunit_run = 0;
|
minunit_run = 0;
|
||||||
minunit_assert = 0;
|
minunit_assert = 0;
|
||||||
minunit_fail = 0;
|
minunit_fail = 0;
|
||||||
@@ -93,32 +102,35 @@ void unit_tests_cli(Cli* cli, FuriString* args, void* context) {
|
|||||||
|
|
||||||
if(furi_string_size(args)) {
|
if(furi_string_size(args)) {
|
||||||
if(furi_string_cmp_str(args, unit_tests[i].name) == 0) {
|
if(furi_string_cmp_str(args, unit_tests[i].name) == 0) {
|
||||||
failed_tests += unit_tests[i].entry();
|
unit_tests[i].entry();
|
||||||
} else {
|
} else {
|
||||||
printf("Skipping %s\r\n", unit_tests[i].name);
|
printf("Skipping %s\r\n", unit_tests[i].name);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
failed_tests += unit_tests[i].entry();
|
unit_tests[i].entry();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
printf("\r\nFailed tests: %lu\r\n", failed_tests);
|
|
||||||
|
|
||||||
// Time report
|
if(minunit_run != 0) {
|
||||||
cycle_counter = (furi_get_tick() - cycle_counter);
|
printf("\r\nFailed tests: %u\r\n", minunit_fail);
|
||||||
printf("Consumed: %lu ms\r\n", cycle_counter);
|
|
||||||
|
|
||||||
// Wait for tested services and apps to deallocate memory
|
// Time report
|
||||||
furi_delay_ms(200);
|
cycle_counter = (furi_get_tick() - cycle_counter);
|
||||||
uint32_t heap_after = memmgr_get_free_heap();
|
printf("Consumed: %lu ms\r\n", cycle_counter);
|
||||||
printf("Leaked: %ld\r\n", heap_before - heap_after);
|
|
||||||
|
|
||||||
// Final Report
|
// Wait for tested services and apps to deallocate memory
|
||||||
if(failed_tests == 0) {
|
furi_delay_ms(200);
|
||||||
notification_message(notification, &sequence_success);
|
uint32_t heap_after = memmgr_get_free_heap();
|
||||||
printf("Status: PASSED\r\n");
|
printf("Leaked: %ld\r\n", heap_before - heap_after);
|
||||||
} else {
|
|
||||||
notification_message(notification, &sequence_error);
|
// Final Report
|
||||||
printf("Status: FAILED\r\n");
|
if(minunit_fail == 0) {
|
||||||
|
notification_message(notification, &sequence_success);
|
||||||
|
printf("Status: PASSED\r\n");
|
||||||
|
} else {
|
||||||
|
notification_message(notification, &sequence_error);
|
||||||
|
printf("Status: FAILED\r\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
App(
|
App(
|
||||||
appid="sample_apps",
|
appid="example_apps",
|
||||||
name="Sample apps bundle",
|
name="Example apps bundle",
|
||||||
apptype=FlipperAppType.METAPACKAGE,
|
apptype=FlipperAppType.METAPACKAGE,
|
||||||
)
|
)
|
||||||
|
|||||||
24
applications/examples/example_images/ReadMe.md
Normal file
24
applications/examples/example_images/ReadMe.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Application icons
|
||||||
|
To use icons, do the following:
|
||||||
|
* add a line to the application manifest: `fap_icon_assets="folder"`, where `folder` points to the folder where your icons are located
|
||||||
|
* add `#include "application_id_icons.h"` to the application code, where `application_id` is the appid from the manifest
|
||||||
|
* every icon in the folder will be available as a `I_icon_name` variable, where `icon_name` is the name of the icon file without the extension
|
||||||
|
|
||||||
|
## Example
|
||||||
|
We have an application with the following manifest:
|
||||||
|
```
|
||||||
|
App(
|
||||||
|
appid="example_images",
|
||||||
|
...
|
||||||
|
fap_icon_assets="images",
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
So the icons are in the `images` folder and will be available in the generated `example_images_icons.h` file.
|
||||||
|
|
||||||
|
The example code is located in `example_images_main.c` and contains the following line:
|
||||||
|
```
|
||||||
|
#include "example_images_icons.h"
|
||||||
|
```
|
||||||
|
|
||||||
|
Image `dolphin_71x25.png` is available as `I_dolphin_71x25`.
|
||||||
@@ -4,6 +4,8 @@
|
|||||||
#include <gui/gui.h>
|
#include <gui/gui.h>
|
||||||
#include <input/input.h>
|
#include <input/input.h>
|
||||||
|
|
||||||
|
/* Magic happens here -- this file is generated by fbt.
|
||||||
|
* Just set fap_icon_assets in application.fam and #include {APPID}_icons.h */
|
||||||
#include "example_images_icons.h"
|
#include "example_images_icons.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
44
applications/examples/example_thermo/README.md
Normal file
44
applications/examples/example_thermo/README.md
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# 1-Wire Thermometer
|
||||||
|
This example application demonstrates the use of the 1-Wire library with a DS18B20 thermometer.
|
||||||
|
It also covers basic GUI, input handling, threads and localisation.
|
||||||
|
|
||||||
|
## Electrical connections
|
||||||
|
Before launching the application, connect the sensor to Flipper's external GPIO according to the table below:
|
||||||
|
| DS18B20 | Flipper |
|
||||||
|
| :-----: | :-----: |
|
||||||
|
| VDD | 9 |
|
||||||
|
| GND | 18 |
|
||||||
|
| DQ | 17 |
|
||||||
|
|
||||||
|
*NOTE 1*: GND is also available on pins 8 and 11.
|
||||||
|
|
||||||
|
*NOTE 2*: For any other pin than 17, connect an external 4.7k pull-up resistor to pin 9.
|
||||||
|
|
||||||
|
## Launching the application
|
||||||
|
In order to launch this demo, follow the steps below:
|
||||||
|
1. Make sure your Flipper has an SD card installed.
|
||||||
|
2. Connect your Flipper to the computer via a USB cable.
|
||||||
|
3. Run `./fbt launch_app APPSRC=example_thermo` in your terminal emulator of choice.
|
||||||
|
|
||||||
|
## Changing the data pin
|
||||||
|
It is possible to use other GPIO pin as a 1-Wire data pin. In order to change it, set the `THERMO_GPIO_PIN` macro to any of the options listed below:
|
||||||
|
|
||||||
|
```c
|
||||||
|
/* Possible GPIO pin choices:
|
||||||
|
- gpio_ext_pc0
|
||||||
|
- gpio_ext_pc1
|
||||||
|
- gpio_ext_pc3
|
||||||
|
- gpio_ext_pb2
|
||||||
|
- gpio_ext_pb3
|
||||||
|
- gpio_ext_pa4
|
||||||
|
- gpio_ext_pa6
|
||||||
|
- gpio_ext_pa7
|
||||||
|
- ibutton_gpio
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define THERMO_GPIO_PIN (ibutton_gpio)
|
||||||
|
```
|
||||||
|
Do not forget about the external pull-up resistor as these pins do not have one built-in.
|
||||||
|
|
||||||
|
With the changes been made, recompile and launch the application again.
|
||||||
|
The on-screen text should reflect it by asking to connect the thermometer to another pin.
|
||||||
10
applications/examples/example_thermo/application.fam
Normal file
10
applications/examples/example_thermo/application.fam
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
App(
|
||||||
|
appid="example_thermo",
|
||||||
|
name="Example: Thermometer",
|
||||||
|
apptype=FlipperAppType.EXTERNAL,
|
||||||
|
entry_point="example_thermo_main",
|
||||||
|
requires=["gui"],
|
||||||
|
stack_size=1 * 1024,
|
||||||
|
fap_icon="example_thermo_10px.png",
|
||||||
|
fap_category="Examples",
|
||||||
|
)
|
||||||
356
applications/examples/example_thermo/example_thermo.c
Normal file
356
applications/examples/example_thermo/example_thermo.c
Normal file
@@ -0,0 +1,356 @@
|
|||||||
|
/*
|
||||||
|
* This file contains an example application that reads and displays
|
||||||
|
* the temperature from a DS18B20 1-wire thermometer.
|
||||||
|
*
|
||||||
|
* It also covers basic GUI, input handling, threads and localisation.
|
||||||
|
*
|
||||||
|
* References:
|
||||||
|
* [1] DS18B20 Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/DS18B20.pdf
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <gui/view_port.h>
|
||||||
|
|
||||||
|
#include <core/thread.h>
|
||||||
|
#include <core/kernel.h>
|
||||||
|
|
||||||
|
#include <locale/locale.h>
|
||||||
|
|
||||||
|
#include <one_wire/maxim_crc.h>
|
||||||
|
#include <one_wire/one_wire_host.h>
|
||||||
|
|
||||||
|
#define UPDATE_PERIOD_MS 1000UL
|
||||||
|
#define TEXT_STORE_SIZE 64U
|
||||||
|
|
||||||
|
#define DS18B20_CMD_CONVERT 0x44U
|
||||||
|
#define DS18B20_CMD_READ_SCRATCHPAD 0xbeU
|
||||||
|
|
||||||
|
#define DS18B20_CFG_RESOLUTION_POS 5U
|
||||||
|
#define DS18B20_CFG_RESOLUTION_MASK 0x03U
|
||||||
|
#define DS18B20_DECIMAL_PART_MASK 0x0fU
|
||||||
|
|
||||||
|
#define DS18B20_SIGN_MASK 0xf0U
|
||||||
|
|
||||||
|
/* Possible GPIO pin choices:
|
||||||
|
- gpio_ext_pc0
|
||||||
|
- gpio_ext_pc1
|
||||||
|
- gpio_ext_pc3
|
||||||
|
- gpio_ext_pb2
|
||||||
|
- gpio_ext_pb3
|
||||||
|
- gpio_ext_pa4
|
||||||
|
- gpio_ext_pa6
|
||||||
|
- gpio_ext_pa7
|
||||||
|
- ibutton_gpio
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define THERMO_GPIO_PIN (ibutton_gpio)
|
||||||
|
|
||||||
|
/* Flags which the reader thread responds to */
|
||||||
|
typedef enum {
|
||||||
|
ReaderThreadFlagExit = 1,
|
||||||
|
} ReaderThreadFlag;
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
struct {
|
||||||
|
uint8_t temp_lsb; /* Least significant byte of the temperature */
|
||||||
|
uint8_t temp_msb; /* Most significant byte of the temperature */
|
||||||
|
uint8_t user_alarm_high; /* User register 1 (Temp high alarm) */
|
||||||
|
uint8_t user_alarm_low; /* User register 2 (Temp low alarm) */
|
||||||
|
uint8_t config; /* Configuration register */
|
||||||
|
uint8_t reserved[3]; /* Not used */
|
||||||
|
uint8_t crc; /* CRC checksum for error detection */
|
||||||
|
} fields;
|
||||||
|
uint8_t bytes[9];
|
||||||
|
} DS18B20Scratchpad;
|
||||||
|
|
||||||
|
/* Application context structure */
|
||||||
|
typedef struct {
|
||||||
|
Gui* gui;
|
||||||
|
ViewPort* view_port;
|
||||||
|
FuriThread* reader_thread;
|
||||||
|
FuriMessageQueue* event_queue;
|
||||||
|
OneWireHost* onewire;
|
||||||
|
float temp_celsius;
|
||||||
|
bool has_device;
|
||||||
|
} ExampleThermoContext;
|
||||||
|
|
||||||
|
/*************** 1-Wire Communication and Processing *****************/
|
||||||
|
|
||||||
|
/* Commands the thermometer to begin measuring the temperature. */
|
||||||
|
static void example_thermo_request_temperature(ExampleThermoContext* context) {
|
||||||
|
OneWireHost* onewire = context->onewire;
|
||||||
|
|
||||||
|
/* All 1-wire transactions must happen in a critical section, i.e
|
||||||
|
not interrupted by other threads. */
|
||||||
|
FURI_CRITICAL_ENTER();
|
||||||
|
|
||||||
|
bool success = false;
|
||||||
|
do {
|
||||||
|
/* Each communication with a 1-wire device starts by a reset.
|
||||||
|
The functon will return true if a device responded with a presence pulse. */
|
||||||
|
if(!onewire_host_reset(onewire)) break;
|
||||||
|
/* After the reset, a ROM operation must follow.
|
||||||
|
If there is only one device connected, the "Skip ROM" command is most appropriate
|
||||||
|
(it can also be used to address all of the connected devices in some cases).*/
|
||||||
|
onewire_host_skip(onewire);
|
||||||
|
/* After the ROM operation, a device-specific command is issued.
|
||||||
|
In this case, it's a request to start measuring the temperature. */
|
||||||
|
onewire_host_write(onewire, DS18B20_CMD_CONVERT);
|
||||||
|
|
||||||
|
success = true;
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
context->has_device = success;
|
||||||
|
|
||||||
|
FURI_CRITICAL_EXIT();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reads the measured temperature from the thermometer. */
|
||||||
|
static void example_thermo_read_temperature(ExampleThermoContext* context) {
|
||||||
|
/* If there was no device detected, don't try to read the temperature */
|
||||||
|
if(!context->has_device) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OneWireHost* onewire = context->onewire;
|
||||||
|
|
||||||
|
/* All 1-wire transactions must happen in a critical section, i.e
|
||||||
|
not interrupted by other threads. */
|
||||||
|
FURI_CRITICAL_ENTER();
|
||||||
|
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
do {
|
||||||
|
DS18B20Scratchpad buf;
|
||||||
|
|
||||||
|
/* Attempt reading the temperature 10 times before giving up */
|
||||||
|
size_t attempts_left = 10;
|
||||||
|
do {
|
||||||
|
/* Each communication with a 1-wire device starts by a reset.
|
||||||
|
The functon will return true if a device responded with a presence pulse. */
|
||||||
|
if(!onewire_host_reset(onewire)) continue;
|
||||||
|
|
||||||
|
/* After the reset, a ROM operation must follow.
|
||||||
|
If there is only one device connected, the "Skip ROM" command is most appropriate
|
||||||
|
(it can also be used to address all of the connected devices in some cases).*/
|
||||||
|
onewire_host_skip(onewire);
|
||||||
|
|
||||||
|
/* After the ROM operation, a device-specific command is issued.
|
||||||
|
This time, it will be the "Read Scratchpad" command which will
|
||||||
|
prepare the device's internal buffer memory for reading. */
|
||||||
|
onewire_host_write(onewire, DS18B20_CMD_READ_SCRATCHPAD);
|
||||||
|
|
||||||
|
/* The actual reading happens here. A total of 9 bytes is read. */
|
||||||
|
onewire_host_read_bytes(onewire, buf.bytes, sizeof(buf.bytes));
|
||||||
|
|
||||||
|
/* Calculate the checksum and compare it with one provided by the device. */
|
||||||
|
const uint8_t crc = maxim_crc8(buf.bytes, sizeof(buf.bytes) - 1, MAXIM_CRC8_INIT);
|
||||||
|
|
||||||
|
/* Checksums match, exit the loop */
|
||||||
|
if(crc == buf.fields.crc) break;
|
||||||
|
|
||||||
|
} while(--attempts_left);
|
||||||
|
|
||||||
|
if(attempts_left == 0) break;
|
||||||
|
|
||||||
|
/* Get the measurement resolution from the configuration register. (See [1] page 9) */
|
||||||
|
const uint8_t resolution_mode = (buf.fields.config >> DS18B20_CFG_RESOLUTION_POS) &
|
||||||
|
DS18B20_CFG_RESOLUTION_MASK;
|
||||||
|
|
||||||
|
/* Generate a mask for undefined bits in the decimal part. (See [1] page 6) */
|
||||||
|
const uint8_t decimal_mask =
|
||||||
|
(DS18B20_DECIMAL_PART_MASK << (DS18B20_CFG_RESOLUTION_MASK - resolution_mode)) &
|
||||||
|
DS18B20_DECIMAL_PART_MASK;
|
||||||
|
|
||||||
|
/* Get the integer and decimal part of the temperature (See [1] page 6) */
|
||||||
|
const uint8_t integer_part = (buf.fields.temp_msb << 4U) | (buf.fields.temp_lsb >> 4U);
|
||||||
|
const uint8_t decimal_part = buf.fields.temp_lsb & decimal_mask;
|
||||||
|
|
||||||
|
/* Calculate the sign of the temperature (See [1] page 6) */
|
||||||
|
const bool is_negative = (buf.fields.temp_msb & DS18B20_SIGN_MASK) != 0;
|
||||||
|
|
||||||
|
/* Combine the integer and decimal part together */
|
||||||
|
const float temp_celsius_abs = integer_part + decimal_part / 16.f;
|
||||||
|
|
||||||
|
/* Set the appropriate sign */
|
||||||
|
context->temp_celsius = is_negative ? -temp_celsius_abs : temp_celsius_abs;
|
||||||
|
|
||||||
|
success = true;
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
context->has_device = success;
|
||||||
|
|
||||||
|
FURI_CRITICAL_EXIT();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Periodically requests measurements and reads temperature. This function runs in a separare thread. */
|
||||||
|
static int32_t example_thermo_reader_thread_callback(void* ctx) {
|
||||||
|
ExampleThermoContext* context = ctx;
|
||||||
|
|
||||||
|
for(;;) {
|
||||||
|
/* Tell the termometer to start measuring the temperature. The process may take up to 750ms. */
|
||||||
|
example_thermo_request_temperature(context);
|
||||||
|
|
||||||
|
/* Wait for the measurement to finish. At the same time wait for an exit signal. */
|
||||||
|
const uint32_t flags =
|
||||||
|
furi_thread_flags_wait(ReaderThreadFlagExit, FuriFlagWaitAny, UPDATE_PERIOD_MS);
|
||||||
|
|
||||||
|
/* If an exit signal was received, return from this thread. */
|
||||||
|
if(flags != (unsigned)FuriFlagErrorTimeout) break;
|
||||||
|
|
||||||
|
/* The measurement is now ready, read it from the termometer. */
|
||||||
|
example_thermo_read_temperature(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************** GUI, Input and Main Loop *****************/
|
||||||
|
|
||||||
|
/* Draw the GUI of the application. The screen is completely redrawn during each call. */
|
||||||
|
static void example_thermo_draw_callback(Canvas* canvas, void* ctx) {
|
||||||
|
ExampleThermoContext* context = ctx;
|
||||||
|
char text_store[TEXT_STORE_SIZE];
|
||||||
|
const size_t middle_x = canvas_width(canvas) / 2U;
|
||||||
|
|
||||||
|
canvas_set_font(canvas, FontPrimary);
|
||||||
|
canvas_draw_str_aligned(canvas, middle_x, 12, AlignCenter, AlignBottom, "Thermometer Demo");
|
||||||
|
canvas_draw_line(canvas, 0, 16, 128, 16);
|
||||||
|
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
canvas_draw_str_aligned(
|
||||||
|
canvas, middle_x, 30, AlignCenter, AlignBottom, "Connnect thermometer");
|
||||||
|
|
||||||
|
snprintf(
|
||||||
|
text_store,
|
||||||
|
TEXT_STORE_SIZE,
|
||||||
|
"to GPIO pin %ld",
|
||||||
|
furi_hal_resources_get_ext_pin_number(&THERMO_GPIO_PIN));
|
||||||
|
canvas_draw_str_aligned(canvas, middle_x, 42, AlignCenter, AlignBottom, text_store);
|
||||||
|
|
||||||
|
canvas_set_font(canvas, FontKeyboard);
|
||||||
|
|
||||||
|
if(context->has_device) {
|
||||||
|
float temp;
|
||||||
|
char temp_units;
|
||||||
|
|
||||||
|
/* The applicaton is locale-aware.
|
||||||
|
Change Settings->System->Units to check it out. */
|
||||||
|
switch(locale_get_measurement_unit()) {
|
||||||
|
case LocaleMeasurementUnitsMetric:
|
||||||
|
temp = context->temp_celsius;
|
||||||
|
temp_units = 'C';
|
||||||
|
break;
|
||||||
|
case LocaleMeasurementUnitsImperial:
|
||||||
|
temp = locale_celsius_to_fahrenheit(context->temp_celsius);
|
||||||
|
temp_units = 'F';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
furi_crash("Illegal measurement units");
|
||||||
|
}
|
||||||
|
/* If a reading is available, display it */
|
||||||
|
snprintf(text_store, TEXT_STORE_SIZE, "Temperature: %+.1f%c", (double)temp, temp_units);
|
||||||
|
} else {
|
||||||
|
/* Or show a message that no data is available */
|
||||||
|
strncpy(text_store, "-- No data --", TEXT_STORE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas_draw_str_aligned(canvas, middle_x, 58, AlignCenter, AlignBottom, text_store);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function is called from the GUI thread. All it does is put the event
|
||||||
|
into the application's queue so it can be processed later. */
|
||||||
|
static void example_thermo_input_callback(InputEvent* event, void* ctx) {
|
||||||
|
ExampleThermoContext* context = ctx;
|
||||||
|
furi_message_queue_put(context->event_queue, event, FuriWaitForever);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Starts the reader thread and handles the input */
|
||||||
|
static void example_thermo_run(ExampleThermoContext* context) {
|
||||||
|
/* Configure the hardware in host mode */
|
||||||
|
onewire_host_start(context->onewire);
|
||||||
|
|
||||||
|
/* Start the reader thread. It will talk to the thermometer in the background. */
|
||||||
|
furi_thread_start(context->reader_thread);
|
||||||
|
|
||||||
|
/* An endless loop which handles the input*/
|
||||||
|
for(bool is_running = true; is_running;) {
|
||||||
|
InputEvent event;
|
||||||
|
/* Wait for an input event. Input events come from the GUI thread via a callback. */
|
||||||
|
const FuriStatus status =
|
||||||
|
furi_message_queue_get(context->event_queue, &event, FuriWaitForever);
|
||||||
|
|
||||||
|
/* This application is only interested in short button presses. */
|
||||||
|
if((status != FuriStatusOk) || (event.type != InputTypeShort)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* When the user presses the "Back" button, break the loop and exit the application. */
|
||||||
|
if(event.key == InputKeyBack) {
|
||||||
|
is_running = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Signal the reader thread to cease operation and exit */
|
||||||
|
furi_thread_flags_set(furi_thread_get_id(context->reader_thread), ReaderThreadFlagExit);
|
||||||
|
|
||||||
|
/* Wait for the reader thread to finish */
|
||||||
|
furi_thread_join(context->reader_thread);
|
||||||
|
|
||||||
|
/* Reset the hardware */
|
||||||
|
onewire_host_stop(context->onewire);
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************** Initialisation & startup *****************************/
|
||||||
|
|
||||||
|
/* Allocate the memory and initialise the variables */
|
||||||
|
static ExampleThermoContext* example_thermo_context_alloc() {
|
||||||
|
ExampleThermoContext* context = malloc(sizeof(ExampleThermoContext));
|
||||||
|
|
||||||
|
context->view_port = view_port_alloc();
|
||||||
|
view_port_draw_callback_set(context->view_port, example_thermo_draw_callback, context);
|
||||||
|
view_port_input_callback_set(context->view_port, example_thermo_input_callback, context);
|
||||||
|
|
||||||
|
context->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
||||||
|
|
||||||
|
context->reader_thread = furi_thread_alloc();
|
||||||
|
furi_thread_set_stack_size(context->reader_thread, 1024U);
|
||||||
|
furi_thread_set_context(context->reader_thread, context);
|
||||||
|
furi_thread_set_callback(context->reader_thread, example_thermo_reader_thread_callback);
|
||||||
|
|
||||||
|
context->gui = furi_record_open(RECORD_GUI);
|
||||||
|
gui_add_view_port(context->gui, context->view_port, GuiLayerFullscreen);
|
||||||
|
|
||||||
|
context->onewire = onewire_host_alloc(&THERMO_GPIO_PIN);
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Release the unused resources and deallocate memory */
|
||||||
|
static void example_thermo_context_free(ExampleThermoContext* context) {
|
||||||
|
view_port_enabled_set(context->view_port, false);
|
||||||
|
gui_remove_view_port(context->gui, context->view_port);
|
||||||
|
|
||||||
|
onewire_host_free(context->onewire);
|
||||||
|
furi_thread_free(context->reader_thread);
|
||||||
|
furi_message_queue_free(context->event_queue);
|
||||||
|
view_port_free(context->view_port);
|
||||||
|
|
||||||
|
furi_record_close(RECORD_GUI);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The application's entry point. Execution starts from here. */
|
||||||
|
int32_t example_thermo_main(void* p) {
|
||||||
|
UNUSED(p);
|
||||||
|
|
||||||
|
/* Allocate all of the necessary structures */
|
||||||
|
ExampleThermoContext* context = example_thermo_context_alloc();
|
||||||
|
|
||||||
|
/* Start the applicaton's main loop. It won't return until the application was requested to exit. */
|
||||||
|
example_thermo_run(context);
|
||||||
|
|
||||||
|
/* Release all unneeded resources */
|
||||||
|
example_thermo_context_free(context);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
BIN
applications/examples/example_thermo/example_thermo_10px.png
Normal file
BIN
applications/examples/example_thermo/example_thermo_10px.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.1 KiB |
@@ -5,7 +5,7 @@ App(
|
|||||||
entry_point="archive_app",
|
entry_point="archive_app",
|
||||||
cdefines=["APP_ARCHIVE"],
|
cdefines=["APP_ARCHIVE"],
|
||||||
requires=["gui"],
|
requires=["gui"],
|
||||||
stack_size=4 * 1024,
|
stack_size=6 * 1024,
|
||||||
icon="A_FileManager_14",
|
icon="A_FileManager_14",
|
||||||
order=0,
|
order=0,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ ArchiveAppTypeEnum archive_get_app_type(const char* path) {
|
|||||||
}
|
}
|
||||||
app_name++;
|
app_name++;
|
||||||
|
|
||||||
for(size_t i = 0; i < COUNT_OF(known_apps); i++) {
|
for(size_t i = 0; i < COUNT_OF(known_apps); i++) { //-V1008
|
||||||
if(strncmp(app_name, known_apps[i], strlen(known_apps[i])) == 0) {
|
if(strncmp(app_name, known_apps[i], strlen(known_apps[i])) == 0) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
#include <archive/views/archive_browser_view.h>
|
|
||||||
#include "archive_files.h"
|
#include "archive_files.h"
|
||||||
#include "archive_apps.h"
|
#include "archive_apps.h"
|
||||||
#include "archive_browser.h"
|
#include "archive_browser.h"
|
||||||
|
#include "../views/archive_browser_view.h"
|
||||||
|
|
||||||
#include <core/common_defines.h>
|
#include <core/common_defines.h>
|
||||||
#include <core/log.h>
|
#include <core/log.h>
|
||||||
#include "gui/modules/file_browser_worker.h"
|
#include <gui/modules/file_browser_worker.h>
|
||||||
#include <fap_loader/fap_loader_app.h>
|
#include <fap_loader/fap_loader_app.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <furi_hal.h>
|
||||||
|
|
||||||
static void
|
static void
|
||||||
archive_folder_open_cb(void* context, uint32_t item_cnt, int32_t file_idx, bool is_root) {
|
archive_folder_open_cb(void* context, uint32_t item_cnt, int32_t file_idx, bool is_root) {
|
||||||
@@ -55,16 +57,29 @@ static void archive_list_load_cb(void* context, uint32_t list_load_offset) {
|
|||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void archive_list_item_cb(
|
||||||
archive_list_item_cb(void* context, FuriString* item_path, bool is_folder, bool is_last) {
|
void* context,
|
||||||
|
FuriString* item_path,
|
||||||
|
uint32_t idx,
|
||||||
|
bool is_folder,
|
||||||
|
bool is_last) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
|
UNUSED(idx);
|
||||||
ArchiveBrowserView* browser = (ArchiveBrowserView*)context;
|
ArchiveBrowserView* browser = (ArchiveBrowserView*)context;
|
||||||
|
|
||||||
if(!is_last) {
|
if(!is_last) {
|
||||||
archive_add_file_item(browser, is_folder, furi_string_get_cstr(item_path));
|
archive_add_file_item(browser, is_folder, furi_string_get_cstr(item_path));
|
||||||
} else {
|
} else {
|
||||||
with_view_model(
|
with_view_model(
|
||||||
browser->view, ArchiveBrowserViewModel * model, { model->list_loading = false; }, true);
|
browser->view,
|
||||||
|
ArchiveBrowserViewModel * model,
|
||||||
|
{
|
||||||
|
if(model->item_cnt <= BROWSER_SORT_THRESHOLD) {
|
||||||
|
files_array_sort(model->files);
|
||||||
|
}
|
||||||
|
model->list_loading = false;
|
||||||
|
},
|
||||||
|
true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,10 +95,12 @@ static void archive_file_browser_set_path(
|
|||||||
ArchiveBrowserView* browser,
|
ArchiveBrowserView* browser,
|
||||||
FuriString* path,
|
FuriString* path,
|
||||||
const char* filter_ext,
|
const char* filter_ext,
|
||||||
bool skip_assets) {
|
bool skip_assets,
|
||||||
|
bool hide_dot_files) {
|
||||||
furi_assert(browser);
|
furi_assert(browser);
|
||||||
if(!browser->worker_running) {
|
if(!browser->worker_running) {
|
||||||
browser->worker = file_browser_worker_alloc(path, filter_ext, skip_assets);
|
browser->worker =
|
||||||
|
file_browser_worker_alloc(path, NULL, filter_ext, skip_assets, hide_dot_files);
|
||||||
file_browser_worker_set_callback_context(browser->worker, browser);
|
file_browser_worker_set_callback_context(browser->worker, browser);
|
||||||
file_browser_worker_set_folder_callback(browser->worker, archive_folder_open_cb);
|
file_browser_worker_set_folder_callback(browser->worker, archive_folder_open_cb);
|
||||||
file_browser_worker_set_list_callback(browser->worker, archive_list_load_cb);
|
file_browser_worker_set_list_callback(browser->worker, archive_list_load_cb);
|
||||||
@@ -92,7 +109,8 @@ static void archive_file_browser_set_path(
|
|||||||
browser->worker_running = true;
|
browser->worker_running = true;
|
||||||
} else {
|
} else {
|
||||||
furi_assert(browser->worker);
|
furi_assert(browser->worker);
|
||||||
file_browser_worker_set_config(browser->worker, path, filter_ext, skip_assets);
|
file_browser_worker_set_config(
|
||||||
|
browser->worker, path, filter_ext, skip_assets, hide_dot_files);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,7 +154,7 @@ void archive_update_focus(ArchiveBrowserView* browser, const char* target) {
|
|||||||
archive_get_items(browser, furi_string_get_cstr(browser->path));
|
archive_get_items(browser, furi_string_get_cstr(browser->path));
|
||||||
|
|
||||||
if(!archive_file_get_array_size(browser) && archive_is_home(browser)) {
|
if(!archive_file_get_array_size(browser) && archive_is_home(browser)) {
|
||||||
archive_switch_tab(browser, TAB_RIGHT);
|
archive_switch_tab(browser, TAB_LEFT);
|
||||||
} else {
|
} else {
|
||||||
with_view_model(
|
with_view_model(
|
||||||
browser->view,
|
browser->view,
|
||||||
@@ -203,7 +221,7 @@ void archive_file_array_rm_selected(ArchiveBrowserView* browser) {
|
|||||||
false);
|
false);
|
||||||
|
|
||||||
if((items_cnt == 0) && (archive_is_home(browser))) {
|
if((items_cnt == 0) && (archive_is_home(browser))) {
|
||||||
archive_switch_tab(browser, TAB_RIGHT);
|
archive_switch_tab(browser, TAB_LEFT);
|
||||||
}
|
}
|
||||||
|
|
||||||
archive_update_offset(browser);
|
archive_update_offset(browser);
|
||||||
@@ -265,8 +283,7 @@ void archive_file_array_load(ArchiveBrowserView* browser, int8_t dir) {
|
|||||||
offset_new = model->item_idx - FILE_LIST_BUF_LEN / 4 * 1;
|
offset_new = model->item_idx - FILE_LIST_BUF_LEN / 4 * 1;
|
||||||
}
|
}
|
||||||
if(offset_new > 0) {
|
if(offset_new > 0) {
|
||||||
offset_new =
|
offset_new = CLAMP(offset_new, (int32_t)model->item_cnt, 0);
|
||||||
CLAMP(offset_new, (int32_t)model->item_cnt - FILE_LIST_BUF_LEN, 0);
|
|
||||||
} else {
|
} else {
|
||||||
offset_new = 0;
|
offset_new = 0;
|
||||||
}
|
}
|
||||||
@@ -453,6 +470,13 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) {
|
|||||||
} else {
|
} else {
|
||||||
tab = (tab + 1) % ArchiveTabTotal;
|
tab = (tab + 1) % ArchiveTabTotal;
|
||||||
}
|
}
|
||||||
|
if(tab == ArchiveTabInternal && !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||||
|
if(key == InputKeyLeft) {
|
||||||
|
tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal;
|
||||||
|
} else {
|
||||||
|
tab = (tab + 1) % ArchiveTabTotal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
browser->is_root = true;
|
browser->is_root = true;
|
||||||
archive_set_tab(browser, tab);
|
archive_set_tab(browser, tab);
|
||||||
@@ -474,8 +498,10 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) {
|
|||||||
tab = archive_get_tab(browser);
|
tab = archive_get_tab(browser);
|
||||||
if(archive_is_dir_exists(browser->path)) {
|
if(archive_is_dir_exists(browser->path)) {
|
||||||
bool skip_assets = (strcmp(archive_get_tab_ext(tab), "*") == 0) ? false : true;
|
bool skip_assets = (strcmp(archive_get_tab_ext(tab), "*") == 0) ? false : true;
|
||||||
|
// Hide dot files everywhere except Browser
|
||||||
|
bool hide_dot_files = (strcmp(archive_get_tab_ext(tab), "*") == 0) ? false : true;
|
||||||
archive_file_browser_set_path(
|
archive_file_browser_set_path(
|
||||||
browser, browser->path, archive_get_tab_ext(tab), skip_assets);
|
browser, browser->path, archive_get_tab_ext(tab), skip_assets, hide_dot_files);
|
||||||
tab_empty = false; // Empty check will be performed later
|
tab_empty = false; // Empty check will be performed later
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
#include "../archive_i.h"
|
#include "../archive_i.h"
|
||||||
#include <storage/storage.h>
|
#include <storage/storage.h>
|
||||||
|
|
||||||
#define TAB_RIGHT InputKeyRight // Default tab swith direction
|
#define TAB_LEFT InputKeyLeft // Default tab switch direction
|
||||||
#define TAB_DEFAULT ArchiveTabFavorites // Start tab
|
#define TAB_DEFAULT ArchiveTabFavorites // Start tab
|
||||||
#define FILE_LIST_BUF_LEN 100
|
#define FILE_LIST_BUF_LEN 50
|
||||||
|
|
||||||
static const char* tab_default_paths[] = {
|
static const char* tab_default_paths[] = {
|
||||||
[ArchiveTabFavorites] = "/app:favorites",
|
[ArchiveTabFavorites] = "/app:favorites",
|
||||||
@@ -17,6 +17,7 @@ static const char* tab_default_paths[] = {
|
|||||||
[ArchiveTabBadUsb] = ANY_PATH("badusb"),
|
[ArchiveTabBadUsb] = ANY_PATH("badusb"),
|
||||||
[ArchiveTabU2f] = "/app:u2f",
|
[ArchiveTabU2f] = "/app:u2f",
|
||||||
[ArchiveTabApplications] = ANY_PATH("apps"),
|
[ArchiveTabApplications] = ANY_PATH("apps"),
|
||||||
|
[ArchiveTabInternal] = STORAGE_INT_PATH_PREFIX,
|
||||||
[ArchiveTabBrowser] = STORAGE_ANY_PATH_PREFIX,
|
[ArchiveTabBrowser] = STORAGE_ANY_PATH_PREFIX,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -44,6 +45,7 @@ static const ArchiveFileTypeEnum known_type[] = {
|
|||||||
[ArchiveTabBadUsb] = ArchiveFileTypeBadUsb,
|
[ArchiveTabBadUsb] = ArchiveFileTypeBadUsb,
|
||||||
[ArchiveTabU2f] = ArchiveFileTypeU2f,
|
[ArchiveTabU2f] = ArchiveFileTypeU2f,
|
||||||
[ArchiveTabApplications] = ArchiveFileTypeApplication,
|
[ArchiveTabApplications] = ArchiveFileTypeApplication,
|
||||||
|
[ArchiveTabInternal] = ArchiveFileTypeUnknown,
|
||||||
[ArchiveTabBrowser] = ArchiveFileTypeUnknown,
|
[ArchiveTabBrowser] = ArchiveFileTypeUnknown,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ bool archive_favorites_read(void* context) {
|
|||||||
|
|
||||||
archive_set_item_count(browser, file_count);
|
archive_set_item_count(browser, file_count);
|
||||||
|
|
||||||
if(need_refresh) {
|
if(need_refresh) { //-V547
|
||||||
archive_favourites_rescan();
|
archive_favourites_rescan();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,11 @@ void archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder
|
|||||||
if(is_folder) {
|
if(is_folder) {
|
||||||
file->type = ArchiveFileTypeFolder;
|
file->type = ArchiveFileTypeFolder;
|
||||||
} else {
|
} else {
|
||||||
|
char tmp_extension[MAX_EXT_LEN];
|
||||||
|
path_extract_extension(file->path, tmp_extension, MAX_EXT_LEN);
|
||||||
|
if((strcmp(tmp_extension, ".txt") == 0) || (strcmp(tmp_extension, ".md") == 0)) {
|
||||||
|
file->is_text_file = true;
|
||||||
|
}
|
||||||
file->type = ArchiveFileTypeUnknown;
|
file->type = ArchiveFileTypeUnknown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <m-array.h>
|
#include <m-array.h>
|
||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
|
#include <m-algo.h>
|
||||||
#include <storage/storage.h>
|
#include <storage/storage.h>
|
||||||
#include "toolbox/path.h"
|
#include "toolbox/path.h"
|
||||||
|
|
||||||
@@ -29,6 +30,7 @@ typedef struct {
|
|||||||
FuriString* custom_name;
|
FuriString* custom_name;
|
||||||
bool fav;
|
bool fav;
|
||||||
bool is_app;
|
bool is_app;
|
||||||
|
bool is_text_file;
|
||||||
} ArchiveFile_t;
|
} ArchiveFile_t;
|
||||||
|
|
||||||
static void ArchiveFile_t_init(ArchiveFile_t* obj) {
|
static void ArchiveFile_t_init(ArchiveFile_t* obj) {
|
||||||
@@ -38,6 +40,7 @@ static void ArchiveFile_t_init(ArchiveFile_t* obj) {
|
|||||||
obj->custom_name = furi_string_alloc();
|
obj->custom_name = furi_string_alloc();
|
||||||
obj->fav = false;
|
obj->fav = false;
|
||||||
obj->is_app = false;
|
obj->is_app = false;
|
||||||
|
obj->is_text_file = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ArchiveFile_t_init_set(ArchiveFile_t* obj, const ArchiveFile_t* src) {
|
static void ArchiveFile_t_init_set(ArchiveFile_t* obj, const ArchiveFile_t* src) {
|
||||||
@@ -52,6 +55,7 @@ static void ArchiveFile_t_init_set(ArchiveFile_t* obj, const ArchiveFile_t* src)
|
|||||||
obj->custom_name = furi_string_alloc_set(src->custom_name);
|
obj->custom_name = furi_string_alloc_set(src->custom_name);
|
||||||
obj->fav = src->fav;
|
obj->fav = src->fav;
|
||||||
obj->is_app = src->is_app;
|
obj->is_app = src->is_app;
|
||||||
|
obj->is_text_file = src->is_text_file;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ArchiveFile_t_set(ArchiveFile_t* obj, const ArchiveFile_t* src) {
|
static void ArchiveFile_t_set(ArchiveFile_t* obj, const ArchiveFile_t* src) {
|
||||||
@@ -66,6 +70,7 @@ static void ArchiveFile_t_set(ArchiveFile_t* obj, const ArchiveFile_t* src) {
|
|||||||
furi_string_set(obj->custom_name, src->custom_name);
|
furi_string_set(obj->custom_name, src->custom_name);
|
||||||
obj->fav = src->fav;
|
obj->fav = src->fav;
|
||||||
obj->is_app = src->is_app;
|
obj->is_app = src->is_app;
|
||||||
|
obj->is_text_file = src->is_text_file;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ArchiveFile_t_clear(ArchiveFile_t* obj) {
|
static void ArchiveFile_t_clear(ArchiveFile_t* obj) {
|
||||||
@@ -77,13 +82,29 @@ static void ArchiveFile_t_clear(ArchiveFile_t* obj) {
|
|||||||
furi_string_free(obj->custom_name);
|
furi_string_free(obj->custom_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
ARRAY_DEF(
|
static int ArchiveFile_t_cmp(const ArchiveFile_t* a, const ArchiveFile_t* b) {
|
||||||
files_array,
|
if(a->type == ArchiveFileTypeFolder && b->type != ArchiveFileTypeFolder) {
|
||||||
ArchiveFile_t,
|
return -1;
|
||||||
(INIT(API_2(ArchiveFile_t_init)),
|
}
|
||||||
SET(API_6(ArchiveFile_t_set)),
|
if(a->type != ArchiveFileTypeFolder && b->type == ArchiveFileTypeFolder) {
|
||||||
INIT_SET(API_6(ArchiveFile_t_init_set)),
|
return 1;
|
||||||
CLEAR(API_2(ArchiveFile_t_clear))))
|
}
|
||||||
|
|
||||||
|
return furi_string_cmpi(a->path, b->path);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define M_OPL_ArchiveFile_t() \
|
||||||
|
(INIT(API_2(ArchiveFile_t_init)), \
|
||||||
|
SET(API_6(ArchiveFile_t_set)), \
|
||||||
|
INIT_SET(API_6(ArchiveFile_t_init_set)), \
|
||||||
|
CLEAR(API_2(ArchiveFile_t_clear)), \
|
||||||
|
CMP(API_6(ArchiveFile_t_cmp)), \
|
||||||
|
SWAP(M_SWAP_DEFAULT), \
|
||||||
|
EQUAL(API_6(M_EQUAL_DEFAULT)))
|
||||||
|
|
||||||
|
ARRAY_DEF(files_array, ArchiveFile_t)
|
||||||
|
|
||||||
|
ALGO_DEF(files_array, ARRAY_OPLIST(files_array, M_OPL_ArchiveFile_t()))
|
||||||
|
|
||||||
void archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder, bool is_app);
|
void archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder, bool is_app);
|
||||||
bool archive_get_items(void* context, const char* path);
|
bool archive_get_items(void* context, const char* path);
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) {
|
|||||||
case ArchiveBrowserEventFileMenuPin: {
|
case ArchiveBrowserEventFileMenuPin: {
|
||||||
const char* name = archive_get_name(browser);
|
const char* name = archive_get_name(browser);
|
||||||
if(favorites) {
|
if(favorites) {
|
||||||
archive_favorites_delete(name);
|
archive_favorites_delete("%s", name);
|
||||||
archive_file_array_rm_selected(browser);
|
archive_file_array_rm_selected(browser);
|
||||||
archive_show_file_menu(browser, false);
|
archive_show_file_menu(browser, false);
|
||||||
} else if(archive_is_known_app(selected->type)) {
|
} else if(archive_is_known_app(selected->type)) {
|
||||||
@@ -150,8 +150,17 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) {
|
|||||||
scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneInfo);
|
scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneInfo);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
break;
|
break;
|
||||||
|
case ArchiveBrowserEventFileMenuShow:
|
||||||
|
archive_show_file_menu(browser, false);
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
archive->scene_manager, ArchiveAppSceneBrowser, SCENE_STATE_DEFAULT);
|
||||||
|
scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneShow);
|
||||||
|
consumed = true;
|
||||||
|
break;
|
||||||
case ArchiveBrowserEventFileMenuDelete:
|
case ArchiveBrowserEventFileMenuDelete:
|
||||||
if(archive_get_tab(browser) != ArchiveTabFavorites) {
|
if(archive_get_tab(browser) != ArchiveTabFavorites) {
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
archive->scene_manager, ArchiveAppSceneBrowser, SCENE_STATE_NEED_REFRESH);
|
||||||
scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneDelete);
|
scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneDelete);
|
||||||
}
|
}
|
||||||
consumed = true;
|
consumed = true;
|
||||||
|
|||||||
@@ -2,3 +2,4 @@ ADD_SCENE(archive, browser, Browser)
|
|||||||
ADD_SCENE(archive, rename, Rename)
|
ADD_SCENE(archive, rename, Rename)
|
||||||
ADD_SCENE(archive, delete, Delete)
|
ADD_SCENE(archive, delete, Delete)
|
||||||
ADD_SCENE(archive, info, Info)
|
ADD_SCENE(archive, info, Info)
|
||||||
|
ADD_SCENE(archive, show, Show)
|
||||||
|
|||||||
@@ -48,11 +48,16 @@ bool archive_scene_delete_on_event(void* context, SceneManagerEvent event) {
|
|||||||
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
if(event.event == GuiButtonTypeRight) {
|
if(event.event == GuiButtonTypeRight) {
|
||||||
|
// Show loading popup on delete
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, ArchiveViewStack);
|
||||||
|
archive_show_loading_popup(app, true);
|
||||||
|
|
||||||
if(selected->is_app) {
|
if(selected->is_app) {
|
||||||
archive_app_delete_file(browser, name);
|
archive_app_delete_file(browser, name);
|
||||||
} else {
|
} else {
|
||||||
archive_delete_file(browser, "%s", name);
|
archive_delete_file(browser, "%s", name);
|
||||||
}
|
}
|
||||||
|
archive_show_loading_popup(app, false);
|
||||||
archive_show_file_menu(browser, false);
|
archive_show_file_menu(browser, false);
|
||||||
return scene_manager_previous_scene(app->scene_manager);
|
return scene_manager_previous_scene(app->scene_manager);
|
||||||
} else if(event.event == GuiButtonTypeLeft) {
|
} else if(event.event == GuiButtonTypeLeft) {
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) {
|
|||||||
ArchiveFile_t* file = archive_get_current_file(archive->browser);
|
ArchiveFile_t* file = archive_get_current_file(archive->browser);
|
||||||
|
|
||||||
FuriString* path_dst;
|
FuriString* path_dst;
|
||||||
|
|
||||||
path_dst = furi_string_alloc();
|
path_dst = furi_string_alloc();
|
||||||
|
|
||||||
if(file->type == ArchiveFileTypeFolder) {
|
if(file->type == ArchiveFileTypeFolder) {
|
||||||
|
|||||||
147
applications/main/archive/scenes/archive_scene_show.c
Normal file
147
applications/main/archive/scenes/archive_scene_show.c
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
#include "../archive_i.h"
|
||||||
|
#include "../helpers/archive_browser.h"
|
||||||
|
#include <storage/storage.h>
|
||||||
|
|
||||||
|
#define TAG "Archive"
|
||||||
|
|
||||||
|
#define SHOW_MAX_FILE_SIZE 5000
|
||||||
|
|
||||||
|
void archive_scene_show_widget_callback(GuiButtonType result, InputType type, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
ArchiveApp* app = (ArchiveApp*)context;
|
||||||
|
if(type == InputTypeShort) {
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool text_show_read_lines(File* file, FuriString* str_result) {
|
||||||
|
//furi_string_reset(str_result);
|
||||||
|
uint8_t buffer[SHOW_MAX_FILE_SIZE];
|
||||||
|
|
||||||
|
uint16_t read_count = storage_file_read(file, buffer, SHOW_MAX_FILE_SIZE);
|
||||||
|
if(storage_file_get_error(file) != FSE_OK) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(uint16_t i = 0; i < read_count; i++) {
|
||||||
|
furi_string_push_back(str_result, buffer[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void archive_scene_show_on_enter(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
ArchiveApp* instance = context;
|
||||||
|
|
||||||
|
FuriString* filename;
|
||||||
|
filename = furi_string_alloc();
|
||||||
|
|
||||||
|
FuriString* buffer;
|
||||||
|
buffer = furi_string_alloc();
|
||||||
|
|
||||||
|
ArchiveFile_t* current = archive_get_current_file(instance->browser);
|
||||||
|
Storage* fs_api = furi_record_open(RECORD_STORAGE);
|
||||||
|
File* file = storage_file_alloc(fs_api);
|
||||||
|
|
||||||
|
FileInfo fileinfo;
|
||||||
|
FS_Error error = storage_common_stat(fs_api, furi_string_get_cstr(current->path), &fileinfo);
|
||||||
|
if(error == FSE_OK) {
|
||||||
|
if((fileinfo.size < SHOW_MAX_FILE_SIZE) && (fileinfo.size > 2)) {
|
||||||
|
bool ok = storage_file_open(
|
||||||
|
file, furi_string_get_cstr(current->path), FSAM_READ, FSOM_OPEN_EXISTING);
|
||||||
|
if(ok) {
|
||||||
|
if(!text_show_read_lines(file, buffer)) {
|
||||||
|
goto text_file_read_err;
|
||||||
|
}
|
||||||
|
if(!furi_string_size(buffer)) {
|
||||||
|
goto text_file_read_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
storage_file_seek(file, 0, true);
|
||||||
|
|
||||||
|
widget_add_text_scroll_element(
|
||||||
|
instance->widget, 0, 0, 128, 64, furi_string_get_cstr(buffer));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
text_file_read_err:
|
||||||
|
widget_add_text_box_element(
|
||||||
|
instance->widget,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
128,
|
||||||
|
64,
|
||||||
|
AlignLeft,
|
||||||
|
AlignCenter,
|
||||||
|
"\e#Error:\nStorage file open error\e#",
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
storage_file_close(file);
|
||||||
|
} else if(fileinfo.size < 2) {
|
||||||
|
widget_add_text_box_element(
|
||||||
|
instance->widget,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
128,
|
||||||
|
64,
|
||||||
|
AlignLeft,
|
||||||
|
AlignCenter,
|
||||||
|
"\e#Error:\nFile is too small\e#",
|
||||||
|
false);
|
||||||
|
} else {
|
||||||
|
widget_add_text_box_element(
|
||||||
|
instance->widget,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
128,
|
||||||
|
64,
|
||||||
|
AlignLeft,
|
||||||
|
AlignCenter,
|
||||||
|
"\e#Error:\nFile is too large to show\e#",
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
widget_add_text_box_element(
|
||||||
|
instance->widget,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
128,
|
||||||
|
64,
|
||||||
|
AlignLeft,
|
||||||
|
AlignCenter,
|
||||||
|
"\e#Error:\nFile system error\e#",
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
path_extract_filename(current->path, filename, false);
|
||||||
|
|
||||||
|
// This one to return and cursor select this file
|
||||||
|
path_extract_filename_no_ext(furi_string_get_cstr(current->path), filename);
|
||||||
|
strlcpy(instance->text_store, furi_string_get_cstr(filename), MAX_NAME_LEN);
|
||||||
|
|
||||||
|
furi_string_free(buffer);
|
||||||
|
|
||||||
|
storage_file_free(file);
|
||||||
|
furi_record_close(RECORD_STORAGE);
|
||||||
|
|
||||||
|
furi_string_free(filename);
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(instance->view_dispatcher, ArchiveViewWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool archive_scene_show_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
furi_assert(context);
|
||||||
|
ArchiveApp* app = (ArchiveApp*)context;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
scene_manager_next_scene(app->scene_manager, ArchiveAppSceneBrowser);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void archive_scene_show_on_exit(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
ArchiveApp* app = (ArchiveApp*)context;
|
||||||
|
|
||||||
|
widget_reset(app->widget);
|
||||||
|
}
|
||||||
@@ -6,6 +6,8 @@
|
|||||||
#include "../helpers/archive_browser.h"
|
#include "../helpers/archive_browser.h"
|
||||||
|
|
||||||
#define TAG "Archive"
|
#define TAG "Archive"
|
||||||
|
#define SCROLL_INTERVAL (333)
|
||||||
|
#define SCROLL_DELAY (2)
|
||||||
|
|
||||||
static const char* ArchiveTabNames[] = {
|
static const char* ArchiveTabNames[] = {
|
||||||
[ArchiveTabFavorites] = "Favorites",
|
[ArchiveTabFavorites] = "Favorites",
|
||||||
@@ -17,6 +19,7 @@ static const char* ArchiveTabNames[] = {
|
|||||||
[ArchiveTabBadUsb] = "Bad USB",
|
[ArchiveTabBadUsb] = "Bad USB",
|
||||||
[ArchiveTabU2f] = "U2F",
|
[ArchiveTabU2f] = "U2F",
|
||||||
[ArchiveTabApplications] = "Apps",
|
[ArchiveTabApplications] = "Apps",
|
||||||
|
[ArchiveTabInternal] = "Internal",
|
||||||
[ArchiveTabBrowser] = "Browser",
|
[ArchiveTabBrowser] = "Browser",
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -51,6 +54,7 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
|
|||||||
FuriString* item_run = furi_string_alloc_set("Run In App");
|
FuriString* item_run = furi_string_alloc_set("Run In App");
|
||||||
FuriString* item_pin = furi_string_alloc_set("Pin");
|
FuriString* item_pin = furi_string_alloc_set("Pin");
|
||||||
FuriString* item_info = furi_string_alloc_set("Info");
|
FuriString* item_info = furi_string_alloc_set("Info");
|
||||||
|
FuriString* item_show = furi_string_alloc_set("Show");
|
||||||
FuriString* item_rename = furi_string_alloc_set("Rename");
|
FuriString* item_rename = furi_string_alloc_set("Rename");
|
||||||
FuriString* item_delete = furi_string_alloc_set("Delete");
|
FuriString* item_delete = furi_string_alloc_set("Delete");
|
||||||
|
|
||||||
@@ -79,6 +83,12 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
|
|||||||
menu_array_push_raw(model->context_menu),
|
menu_array_push_raw(model->context_menu),
|
||||||
item_info,
|
item_info,
|
||||||
ArchiveBrowserEventFileMenuInfo);
|
ArchiveBrowserEventFileMenuInfo);
|
||||||
|
if(selected->is_text_file) {
|
||||||
|
archive_menu_add_item(
|
||||||
|
menu_array_push_raw(model->context_menu),
|
||||||
|
item_show,
|
||||||
|
ArchiveBrowserEventFileMenuShow);
|
||||||
|
}
|
||||||
archive_menu_add_item(
|
archive_menu_add_item(
|
||||||
menu_array_push_raw(model->context_menu),
|
menu_array_push_raw(model->context_menu),
|
||||||
item_rename,
|
item_rename,
|
||||||
@@ -100,6 +110,12 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
|
|||||||
menu_array_push_raw(model->context_menu),
|
menu_array_push_raw(model->context_menu),
|
||||||
item_pin,
|
item_pin,
|
||||||
ArchiveBrowserEventFileMenuPin);
|
ArchiveBrowserEventFileMenuPin);
|
||||||
|
if(selected->type <= ArchiveFileTypeBadUsb) {
|
||||||
|
archive_menu_add_item(
|
||||||
|
menu_array_push_raw(model->context_menu),
|
||||||
|
item_show,
|
||||||
|
ArchiveBrowserEventFileMenuShow);
|
||||||
|
}
|
||||||
archive_menu_add_item(
|
archive_menu_add_item(
|
||||||
menu_array_push_raw(model->context_menu),
|
menu_array_push_raw(model->context_menu),
|
||||||
item_rename,
|
item_rename,
|
||||||
@@ -114,6 +130,12 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
|
|||||||
menu_array_push_raw(model->context_menu),
|
menu_array_push_raw(model->context_menu),
|
||||||
item_info,
|
item_info,
|
||||||
ArchiveBrowserEventFileMenuInfo);
|
ArchiveBrowserEventFileMenuInfo);
|
||||||
|
if(selected->type <= ArchiveFileTypeBadUsb) {
|
||||||
|
archive_menu_add_item(
|
||||||
|
menu_array_push_raw(model->context_menu),
|
||||||
|
item_show,
|
||||||
|
ArchiveBrowserEventFileMenuShow);
|
||||||
|
}
|
||||||
archive_menu_add_item(
|
archive_menu_add_item(
|
||||||
menu_array_push_raw(model->context_menu),
|
menu_array_push_raw(model->context_menu),
|
||||||
item_pin,
|
item_pin,
|
||||||
@@ -136,6 +158,12 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
|
|||||||
menu_array_push_raw(model->context_menu),
|
menu_array_push_raw(model->context_menu),
|
||||||
item_info,
|
item_info,
|
||||||
ArchiveBrowserEventFileMenuInfo);
|
ArchiveBrowserEventFileMenuInfo);
|
||||||
|
if(selected->type <= ArchiveFileTypeBadUsb) {
|
||||||
|
archive_menu_add_item(
|
||||||
|
menu_array_push_raw(model->context_menu),
|
||||||
|
item_show,
|
||||||
|
ArchiveBrowserEventFileMenuShow);
|
||||||
|
}
|
||||||
archive_menu_add_item(
|
archive_menu_add_item(
|
||||||
menu_array_push_raw(model->context_menu),
|
menu_array_push_raw(model->context_menu),
|
||||||
item_rename,
|
item_rename,
|
||||||
@@ -149,6 +177,7 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
|
|||||||
furi_string_free(item_run);
|
furi_string_free(item_run);
|
||||||
furi_string_free(item_pin);
|
furi_string_free(item_pin);
|
||||||
furi_string_free(item_info);
|
furi_string_free(item_info);
|
||||||
|
furi_string_free(item_show);
|
||||||
furi_string_free(item_rename);
|
furi_string_free(item_rename);
|
||||||
furi_string_free(item_delete);
|
furi_string_free(item_delete);
|
||||||
} /*else {
|
} /*else {
|
||||||
@@ -160,9 +189,9 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
|
|||||||
|
|
||||||
canvas_set_color(canvas, ColorWhite);
|
canvas_set_color(canvas, ColorWhite);
|
||||||
uint8_t calc_height = menu_height - ((MENU_ITEMS - size_menu) * line_height);
|
uint8_t calc_height = menu_height - ((MENU_ITEMS - size_menu) * line_height);
|
||||||
canvas_draw_box(canvas, 71, 11, 57, calc_height + 4);
|
canvas_draw_box(canvas, 71, 1, 57, calc_height + 4);
|
||||||
canvas_set_color(canvas, ColorBlack);
|
canvas_set_color(canvas, ColorBlack);
|
||||||
elements_slightly_rounded_frame(canvas, 70, 12, 58, calc_height + 4);
|
elements_slightly_rounded_frame(canvas, 70, 2, 58, calc_height + 4);
|
||||||
|
|
||||||
/*FURI_LOG_D(
|
/*FURI_LOG_D(
|
||||||
TAG,
|
TAG,
|
||||||
@@ -172,10 +201,10 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
|
|||||||
model->menu_idx);*/
|
model->menu_idx);*/
|
||||||
for(size_t i = 0; i < size_menu; i++) {
|
for(size_t i = 0; i < size_menu; i++) {
|
||||||
ArchiveContextMenuItem_t* current = menu_array_get(model->context_menu, i);
|
ArchiveContextMenuItem_t* current = menu_array_get(model->context_menu, i);
|
||||||
canvas_draw_str(canvas, 82, 21 + i * line_height, furi_string_get_cstr(current->text));
|
canvas_draw_str(canvas, 82, 11 + i * line_height, furi_string_get_cstr(current->text));
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas_draw_icon(canvas, 74, 14 + model->menu_idx * line_height, &I_ButtonRight_4x7);
|
canvas_draw_icon(canvas, 74, 4 + model->menu_idx * line_height, &I_ButtonRight_4x7);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void archive_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar, bool moving) {
|
static void archive_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar, bool moving) {
|
||||||
@@ -242,13 +271,18 @@ static void draw_list(Canvas* canvas, ArchiveBrowserViewModel* model) {
|
|||||||
furi_string_set(str_buf, "---");
|
furi_string_set(str_buf, "---");
|
||||||
}
|
}
|
||||||
|
|
||||||
elements_string_fit_width(
|
size_t scroll_counter = model->scroll_counter;
|
||||||
canvas, str_buf, (scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX) - x_offset);
|
|
||||||
|
|
||||||
if(model->item_idx == idx) {
|
if(model->item_idx == idx) {
|
||||||
archive_draw_frame(canvas, i, scrollbar, model->move_fav);
|
archive_draw_frame(canvas, i, scrollbar, model->move_fav);
|
||||||
|
if(scroll_counter < SCROLL_DELAY) {
|
||||||
|
scroll_counter = 0;
|
||||||
|
} else {
|
||||||
|
scroll_counter -= SCROLL_DELAY;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
canvas_set_color(canvas, ColorBlack);
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
scroll_counter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(custom_icon_data) {
|
if(custom_icon_data) {
|
||||||
@@ -258,8 +292,15 @@ static void draw_list(Canvas* canvas, ArchiveBrowserViewModel* model) {
|
|||||||
canvas_draw_icon(
|
canvas_draw_icon(
|
||||||
canvas, 2 + x_offset, 16 + i * FRAME_HEIGHT, ArchiveItemIcons[file_type]);
|
canvas, 2 + x_offset, 16 + i * FRAME_HEIGHT, ArchiveItemIcons[file_type]);
|
||||||
}
|
}
|
||||||
canvas_draw_str(
|
|
||||||
canvas, 15 + x_offset, 24 + i * FRAME_HEIGHT, furi_string_get_cstr(str_buf));
|
elements_scrollable_text_line(
|
||||||
|
canvas,
|
||||||
|
15 + x_offset,
|
||||||
|
24 + i * FRAME_HEIGHT,
|
||||||
|
((scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX) - x_offset),
|
||||||
|
str_buf,
|
||||||
|
scroll_counter,
|
||||||
|
(model->item_idx != idx));
|
||||||
|
|
||||||
furi_string_free(str_buf);
|
furi_string_free(str_buf);
|
||||||
}
|
}
|
||||||
@@ -357,15 +398,20 @@ static bool archive_view_input(InputEvent* event, void* context) {
|
|||||||
|
|
||||||
bool in_menu;
|
bool in_menu;
|
||||||
bool move_fav_mode;
|
bool move_fav_mode;
|
||||||
|
bool is_loading;
|
||||||
with_view_model(
|
with_view_model(
|
||||||
browser->view,
|
browser->view,
|
||||||
ArchiveBrowserViewModel * model,
|
ArchiveBrowserViewModel * model,
|
||||||
{
|
{
|
||||||
in_menu = model->menu;
|
in_menu = model->menu;
|
||||||
move_fav_mode = model->move_fav;
|
move_fav_mode = model->move_fav;
|
||||||
|
is_loading = model->folder_loading || model->list_loading;
|
||||||
},
|
},
|
||||||
false);
|
false);
|
||||||
|
|
||||||
|
if(is_loading) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if(in_menu) {
|
if(in_menu) {
|
||||||
if(event->type != InputTypeShort) {
|
if(event->type != InputTypeShort) {
|
||||||
return true; // RETURN
|
return true; // RETURN
|
||||||
@@ -428,6 +474,7 @@ static bool archive_view_input(InputEvent* event, void* context) {
|
|||||||
if(move_fav_mode) {
|
if(move_fav_mode) {
|
||||||
browser->callback(ArchiveBrowserEventFavMoveUp, browser->context);
|
browser->callback(ArchiveBrowserEventFavMoveUp, browser->context);
|
||||||
}
|
}
|
||||||
|
model->scroll_counter = 0;
|
||||||
} else if(event->key == InputKeyDown) {
|
} else if(event->key == InputKeyDown) {
|
||||||
model->item_idx = (model->item_idx + 1) % model->item_cnt;
|
model->item_idx = (model->item_idx + 1) % model->item_cnt;
|
||||||
if(is_file_list_load_required(model)) {
|
if(is_file_list_load_required(model)) {
|
||||||
@@ -437,9 +484,10 @@ static bool archive_view_input(InputEvent* event, void* context) {
|
|||||||
if(move_fav_mode) {
|
if(move_fav_mode) {
|
||||||
browser->callback(ArchiveBrowserEventFavMoveDown, browser->context);
|
browser->callback(ArchiveBrowserEventFavMoveDown, browser->context);
|
||||||
}
|
}
|
||||||
|
model->scroll_counter = 0;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
true);
|
false);
|
||||||
archive_update_offset(browser);
|
archive_update_offset(browser);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -476,6 +524,27 @@ static bool archive_view_input(InputEvent* event, void* context) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void browser_scroll_timer(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
ArchiveBrowserView* browser = context;
|
||||||
|
with_view_model(
|
||||||
|
browser->view, ArchiveBrowserViewModel * model, { model->scroll_counter++; }, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void browser_view_enter(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
ArchiveBrowserView* browser = context;
|
||||||
|
with_view_model(
|
||||||
|
browser->view, ArchiveBrowserViewModel * model, { model->scroll_counter = 0; }, true);
|
||||||
|
furi_timer_start(browser->scroll_timer, SCROLL_INTERVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void browser_view_exit(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
ArchiveBrowserView* browser = context;
|
||||||
|
furi_timer_stop(browser->scroll_timer);
|
||||||
|
}
|
||||||
|
|
||||||
ArchiveBrowserView* browser_alloc() {
|
ArchiveBrowserView* browser_alloc() {
|
||||||
ArchiveBrowserView* browser = malloc(sizeof(ArchiveBrowserView));
|
ArchiveBrowserView* browser = malloc(sizeof(ArchiveBrowserView));
|
||||||
browser->view = view_alloc();
|
browser->view = view_alloc();
|
||||||
@@ -483,6 +552,10 @@ ArchiveBrowserView* browser_alloc() {
|
|||||||
view_set_context(browser->view, browser);
|
view_set_context(browser->view, browser);
|
||||||
view_set_draw_callback(browser->view, archive_view_render);
|
view_set_draw_callback(browser->view, archive_view_render);
|
||||||
view_set_input_callback(browser->view, archive_view_input);
|
view_set_input_callback(browser->view, archive_view_input);
|
||||||
|
view_set_enter_callback(browser->view, browser_view_enter);
|
||||||
|
view_set_exit_callback(browser->view, browser_view_exit);
|
||||||
|
|
||||||
|
browser->scroll_timer = furi_timer_alloc(browser_scroll_timer, FuriTimerTypePeriodic, browser);
|
||||||
|
|
||||||
browser->path = furi_string_alloc_set(archive_get_default_path(TAB_DEFAULT));
|
browser->path = furi_string_alloc_set(archive_get_default_path(TAB_DEFAULT));
|
||||||
|
|
||||||
@@ -502,6 +575,8 @@ ArchiveBrowserView* browser_alloc() {
|
|||||||
void browser_free(ArchiveBrowserView* browser) {
|
void browser_free(ArchiveBrowserView* browser) {
|
||||||
furi_assert(browser);
|
furi_assert(browser);
|
||||||
|
|
||||||
|
furi_timer_free(browser->scroll_timer);
|
||||||
|
|
||||||
if(browser->worker_running) {
|
if(browser->worker_running) {
|
||||||
file_browser_worker_free(browser->worker);
|
file_browser_worker_free(browser->worker);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "../helpers/archive_files.h"
|
||||||
|
#include "../helpers/archive_favorites.h"
|
||||||
|
|
||||||
#include <gui/gui_i.h>
|
#include <gui/gui_i.h>
|
||||||
#include <gui/view.h>
|
#include <gui/view.h>
|
||||||
#include <gui/canvas.h>
|
#include <gui/canvas.h>
|
||||||
#include <gui/elements.h>
|
#include <gui/elements.h>
|
||||||
#include <furi.h>
|
#include <gui/modules/file_browser_worker.h>
|
||||||
#include <storage/storage.h>
|
#include <storage/storage.h>
|
||||||
#include "../helpers/archive_files.h"
|
#include "../helpers/archive_files.h"
|
||||||
#include "../helpers/archive_menu.h"
|
#include "../helpers/archive_menu.h"
|
||||||
@@ -28,6 +31,7 @@ typedef enum {
|
|||||||
ArchiveTabBadUsb,
|
ArchiveTabBadUsb,
|
||||||
ArchiveTabU2f,
|
ArchiveTabU2f,
|
||||||
ArchiveTabApplications,
|
ArchiveTabApplications,
|
||||||
|
ArchiveTabInternal,
|
||||||
ArchiveTabBrowser,
|
ArchiveTabBrowser,
|
||||||
ArchiveTabTotal,
|
ArchiveTabTotal,
|
||||||
} ArchiveTabEnum;
|
} ArchiveTabEnum;
|
||||||
@@ -40,6 +44,7 @@ typedef enum {
|
|||||||
ArchiveBrowserEventFileMenuRename,
|
ArchiveBrowserEventFileMenuRename,
|
||||||
ArchiveBrowserEventFileMenuDelete,
|
ArchiveBrowserEventFileMenuDelete,
|
||||||
ArchiveBrowserEventFileMenuInfo,
|
ArchiveBrowserEventFileMenuInfo,
|
||||||
|
ArchiveBrowserEventFileMenuShow,
|
||||||
ArchiveBrowserEventFileMenuClose,
|
ArchiveBrowserEventFileMenuClose,
|
||||||
|
|
||||||
ArchiveBrowserEventEnterDir,
|
ArchiveBrowserEventEnterDir,
|
||||||
@@ -77,6 +82,7 @@ struct ArchiveBrowserView {
|
|||||||
FuriString* path;
|
FuriString* path;
|
||||||
InputKey last_tab_switch_dir;
|
InputKey last_tab_switch_dir;
|
||||||
bool is_root;
|
bool is_root;
|
||||||
|
FuriTimer* scroll_timer;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -95,6 +101,7 @@ typedef struct {
|
|||||||
int32_t item_idx;
|
int32_t item_idx;
|
||||||
int32_t array_offset;
|
int32_t array_offset;
|
||||||
int32_t list_offset;
|
int32_t list_offset;
|
||||||
|
size_t scroll_counter;
|
||||||
} ArchiveBrowserViewModel;
|
} ArchiveBrowserViewModel;
|
||||||
|
|
||||||
void archive_browser_set_callback(
|
void archive_browser_set_callback(
|
||||||
|
|||||||
@@ -11,4 +11,5 @@ App(
|
|||||||
stack_size=2 * 1024,
|
stack_size=2 * 1024,
|
||||||
icon="A_BadUsb_14",
|
icon="A_BadUsb_14",
|
||||||
order=70,
|
order=70,
|
||||||
|
fap_libs=["assets"],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -33,9 +33,26 @@ static void bad_usb_load_settings(BadUsbApp* app) {
|
|||||||
!storage_file_eof(settings_file) && !isspace(chr)) {
|
!storage_file_eof(settings_file) && !isspace(chr)) {
|
||||||
furi_string_push_back(app->keyboard_layout, chr);
|
furi_string_push_back(app->keyboard_layout, chr);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
furi_string_reset(app->keyboard_layout);
|
||||||
}
|
}
|
||||||
storage_file_close(settings_file);
|
storage_file_close(settings_file);
|
||||||
storage_file_free(settings_file);
|
storage_file_free(settings_file);
|
||||||
|
|
||||||
|
if(!furi_string_empty(app->keyboard_layout)) {
|
||||||
|
Storage* fs_api = furi_record_open(RECORD_STORAGE);
|
||||||
|
FileInfo layout_file_info;
|
||||||
|
FS_Error file_check_err = storage_common_stat(
|
||||||
|
fs_api, furi_string_get_cstr(app->keyboard_layout), &layout_file_info);
|
||||||
|
furi_record_close(RECORD_STORAGE);
|
||||||
|
if(file_check_err != FSE_OK) {
|
||||||
|
furi_string_reset(app->keyboard_layout);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(layout_file_info.size != 256) {
|
||||||
|
furi_string_reset(app->keyboard_layout);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bad_usb_save_settings(BadUsbApp* app) {
|
static void bad_usb_save_settings(BadUsbApp* app) {
|
||||||
@@ -98,8 +115,12 @@ BadUsbApp* bad_usb_app_alloc(char* arg) {
|
|||||||
|
|
||||||
if(furi_hal_usb_is_locked()) {
|
if(furi_hal_usb_is_locked()) {
|
||||||
app->error = BadUsbAppErrorCloseRpc;
|
app->error = BadUsbAppErrorCloseRpc;
|
||||||
|
app->usb_if_prev = NULL;
|
||||||
scene_manager_next_scene(app->scene_manager, BadUsbSceneError);
|
scene_manager_next_scene(app->scene_manager, BadUsbSceneError);
|
||||||
} else {
|
} else {
|
||||||
|
app->usb_if_prev = furi_hal_usb_get_config();
|
||||||
|
furi_check(furi_hal_usb_set_config(NULL, NULL));
|
||||||
|
|
||||||
if(!furi_string_empty(app->file_path)) {
|
if(!furi_string_empty(app->file_path)) {
|
||||||
app->bad_usb_script = bad_usb_script_open(app->file_path);
|
app->bad_usb_script = bad_usb_script_open(app->file_path);
|
||||||
bad_usb_script_set_keyboard_layout(app->bad_usb_script, app->keyboard_layout);
|
bad_usb_script_set_keyboard_layout(app->bad_usb_script, app->keyboard_layout);
|
||||||
@@ -121,6 +142,10 @@ void bad_usb_app_free(BadUsbApp* app) {
|
|||||||
app->bad_usb_script = NULL;
|
app->bad_usb_script = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(app->usb_if_prev) {
|
||||||
|
furi_check(furi_hal_usb_set_config(app->usb_if_prev, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
// Views
|
// Views
|
||||||
view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewWork);
|
view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewWork);
|
||||||
bad_usb_free(app->bad_usb_view);
|
bad_usb_free(app->bad_usb_view);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "bad_usb_script.h"
|
#include "bad_usb_script.h"
|
||||||
|
|
||||||
#include <gui/gui.h>
|
#include <gui/gui.h>
|
||||||
|
#include <assets_icons.h>
|
||||||
#include <gui/view_dispatcher.h>
|
#include <gui/view_dispatcher.h>
|
||||||
#include <gui/scene_manager.h>
|
#include <gui/scene_manager.h>
|
||||||
#include <gui/modules/submenu.h>
|
#include <gui/modules/submenu.h>
|
||||||
@@ -13,9 +14,10 @@
|
|||||||
#include <gui/modules/variable_item_list.h>
|
#include <gui/modules/variable_item_list.h>
|
||||||
#include <gui/modules/widget.h>
|
#include <gui/modules/widget.h>
|
||||||
#include "views/bad_usb_view.h"
|
#include "views/bad_usb_view.h"
|
||||||
|
#include <furi_hal_usb.h>
|
||||||
|
|
||||||
#define BAD_USB_APP_BASE_FOLDER ANY_PATH("badusb")
|
#define BAD_USB_APP_BASE_FOLDER ANY_PATH("badusb")
|
||||||
#define BAD_USB_APP_PATH_LAYOUT_FOLDER BAD_USB_APP_BASE_FOLDER "/layouts"
|
#define BAD_USB_APP_PATH_LAYOUT_FOLDER BAD_USB_APP_BASE_FOLDER "/assets/layouts"
|
||||||
#define BAD_USB_APP_SCRIPT_EXTENSION ".txt"
|
#define BAD_USB_APP_SCRIPT_EXTENSION ".txt"
|
||||||
#define BAD_USB_APP_LAYOUT_EXTENSION ".kl"
|
#define BAD_USB_APP_LAYOUT_EXTENSION ".kl"
|
||||||
|
|
||||||
@@ -38,6 +40,8 @@ struct BadUsbApp {
|
|||||||
FuriString* keyboard_layout;
|
FuriString* keyboard_layout;
|
||||||
BadUsb* bad_usb_view;
|
BadUsb* bad_usb_view;
|
||||||
BadUsbScript* bad_usb_script;
|
BadUsbScript* bad_usb_script;
|
||||||
|
|
||||||
|
FuriHalUsbInterface* usb_if_prev;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ static const DuckyKey ducky_keys[] = {
|
|||||||
{"ALT-SHIFT", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT},
|
{"ALT-SHIFT", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT},
|
||||||
{"ALT-GUI", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_GUI},
|
{"ALT-GUI", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_GUI},
|
||||||
{"GUI-SHIFT", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT},
|
{"GUI-SHIFT", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT},
|
||||||
|
{"GUI-CTRL", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL},
|
||||||
|
|
||||||
{"CTRL", KEY_MOD_LEFT_CTRL},
|
{"CTRL", KEY_MOD_LEFT_CTRL},
|
||||||
{"CONTROL", KEY_MOD_LEFT_CTRL},
|
{"CONTROL", KEY_MOD_LEFT_CTRL},
|
||||||
@@ -75,8 +76,8 @@ static const DuckyKey ducky_keys[] = {
|
|||||||
{"BREAK", HID_KEYBOARD_PAUSE},
|
{"BREAK", HID_KEYBOARD_PAUSE},
|
||||||
{"PAUSE", HID_KEYBOARD_PAUSE},
|
{"PAUSE", HID_KEYBOARD_PAUSE},
|
||||||
{"CAPSLOCK", HID_KEYBOARD_CAPS_LOCK},
|
{"CAPSLOCK", HID_KEYBOARD_CAPS_LOCK},
|
||||||
{"DELETE", HID_KEYBOARD_DELETE},
|
{"DELETE", HID_KEYBOARD_DELETE_FORWARD},
|
||||||
{"BACKSPACE", HID_KEYPAD_BACKSPACE},
|
{"BACKSPACE", HID_KEYBOARD_DELETE},
|
||||||
{"END", HID_KEYBOARD_END},
|
{"END", HID_KEYBOARD_END},
|
||||||
{"ESC", HID_KEYBOARD_ESCAPE},
|
{"ESC", HID_KEYBOARD_ESCAPE},
|
||||||
{"ESCAPE", HID_KEYBOARD_ESCAPE},
|
{"ESCAPE", HID_KEYBOARD_ESCAPE},
|
||||||
@@ -86,7 +87,7 @@ static const DuckyKey ducky_keys[] = {
|
|||||||
{"PAGEUP", HID_KEYBOARD_PAGE_UP},
|
{"PAGEUP", HID_KEYBOARD_PAGE_UP},
|
||||||
{"PAGEDOWN", HID_KEYBOARD_PAGE_DOWN},
|
{"PAGEDOWN", HID_KEYBOARD_PAGE_DOWN},
|
||||||
{"PRINTSCREEN", HID_KEYBOARD_PRINT_SCREEN},
|
{"PRINTSCREEN", HID_KEYBOARD_PRINT_SCREEN},
|
||||||
{"SCROLLOCK", HID_KEYBOARD_SCROLL_LOCK},
|
{"SCROLLLOCK", HID_KEYBOARD_SCROLL_LOCK},
|
||||||
{"SPACE", HID_KEYBOARD_SPACEBAR},
|
{"SPACE", HID_KEYBOARD_SPACEBAR},
|
||||||
{"TAB", HID_KEYBOARD_TAB},
|
{"TAB", HID_KEYBOARD_TAB},
|
||||||
{"MENU", HID_KEYBOARD_APPLICATION},
|
{"MENU", HID_KEYBOARD_APPLICATION},
|
||||||
@@ -119,8 +120,6 @@ static const char ducky_cmd_altchar[] = {"ALTCHAR "};
|
|||||||
static const char ducky_cmd_altstr_1[] = {"ALTSTRING "};
|
static const char ducky_cmd_altstr_1[] = {"ALTSTRING "};
|
||||||
static const char ducky_cmd_altstr_2[] = {"ALTCODE "};
|
static const char ducky_cmd_altstr_2[] = {"ALTCODE "};
|
||||||
|
|
||||||
static const char ducky_cmd_lang[] = {"DUCKY_LANG"};
|
|
||||||
|
|
||||||
static const uint8_t numpad_keys[10] = {
|
static const uint8_t numpad_keys[10] = {
|
||||||
HID_KEYPAD_0,
|
HID_KEYPAD_0,
|
||||||
HID_KEYPAD_1,
|
HID_KEYPAD_1,
|
||||||
@@ -224,8 +223,8 @@ static bool ducky_string(BadUsbScript* bad_usb, const char* param) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static uint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool accept_chars) {
|
static uint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool accept_chars) {
|
||||||
for(uint8_t i = 0; i < (sizeof(ducky_keys) / sizeof(ducky_keys[0])); i++) {
|
for(size_t i = 0; i < (sizeof(ducky_keys) / sizeof(ducky_keys[0])); i++) {
|
||||||
uint8_t key_cmd_len = strlen(ducky_keys[i].name);
|
size_t key_cmd_len = strlen(ducky_keys[i].name);
|
||||||
if((strncmp(param, ducky_keys[i].name, key_cmd_len) == 0) &&
|
if((strncmp(param, ducky_keys[i].name, key_cmd_len) == 0) &&
|
||||||
(ducky_is_line_end(param[key_cmd_len]))) {
|
(ducky_is_line_end(param[key_cmd_len]))) {
|
||||||
return ducky_keys[i].keycode;
|
return ducky_keys[i].keycode;
|
||||||
@@ -237,17 +236,14 @@ static uint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
|
static int32_t
|
||||||
|
ducky_parse_line(BadUsbScript* bad_usb, FuriString* line, char* error, size_t error_len) {
|
||||||
uint32_t line_len = furi_string_size(line);
|
uint32_t line_len = furi_string_size(line);
|
||||||
const char* line_tmp = furi_string_get_cstr(line);
|
const char* line_tmp = furi_string_get_cstr(line);
|
||||||
bool state = false;
|
bool state = false;
|
||||||
|
|
||||||
for(uint32_t i = 0; i < line_len; i++) {
|
if(line_len == 0) {
|
||||||
if((line_tmp[i] != ' ') && (line_tmp[i] != '\t') && (line_tmp[i] != '\n')) {
|
return SCRIPT_STATE_NEXT_LINE; // Skip empty lines
|
||||||
line_tmp = &line_tmp[i];
|
|
||||||
break; // Skip spaces and tabs
|
|
||||||
}
|
|
||||||
if(i == line_len - 1) return SCRIPT_STATE_NEXT_LINE; // Skip empty lines
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FURI_LOG_D(WORKER_TAG, "line:%s", line_tmp);
|
FURI_LOG_D(WORKER_TAG, "line:%s", line_tmp);
|
||||||
@@ -259,9 +255,6 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
|
|||||||
} else if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) {
|
} else if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) {
|
||||||
// ID - executed in ducky_script_preload
|
// ID - executed in ducky_script_preload
|
||||||
return (0);
|
return (0);
|
||||||
} else if(strncmp(line_tmp, ducky_cmd_lang, strlen(ducky_cmd_lang)) == 0) {
|
|
||||||
// DUCKY_LANG - ignore command to retain compatibility with existing scripts
|
|
||||||
return (0);
|
|
||||||
} else if(strncmp(line_tmp, ducky_cmd_delay, strlen(ducky_cmd_delay)) == 0) {
|
} else if(strncmp(line_tmp, ducky_cmd_delay, strlen(ducky_cmd_delay)) == 0) {
|
||||||
// DELAY
|
// DELAY
|
||||||
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
||||||
@@ -270,6 +263,9 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
|
|||||||
if((state) && (delay_val > 0)) {
|
if((state) && (delay_val > 0)) {
|
||||||
return (int32_t)delay_val;
|
return (int32_t)delay_val;
|
||||||
}
|
}
|
||||||
|
if(error != NULL) {
|
||||||
|
snprintf(error, error_len, "Invalid number %s", line_tmp);
|
||||||
|
}
|
||||||
return SCRIPT_STATE_ERROR;
|
return SCRIPT_STATE_ERROR;
|
||||||
} else if(
|
} else if(
|
||||||
(strncmp(line_tmp, ducky_cmd_defdelay_1, strlen(ducky_cmd_defdelay_1)) == 0) ||
|
(strncmp(line_tmp, ducky_cmd_defdelay_1, strlen(ducky_cmd_defdelay_1)) == 0) ||
|
||||||
@@ -277,17 +273,26 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
|
|||||||
// DEFAULT_DELAY
|
// DEFAULT_DELAY
|
||||||
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
||||||
state = ducky_get_number(line_tmp, &bad_usb->defdelay);
|
state = ducky_get_number(line_tmp, &bad_usb->defdelay);
|
||||||
|
if(!state && error != NULL) {
|
||||||
|
snprintf(error, error_len, "Invalid number %s", line_tmp);
|
||||||
|
}
|
||||||
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
||||||
} else if(strncmp(line_tmp, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) {
|
} else if(strncmp(line_tmp, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) {
|
||||||
// STRING
|
// STRING
|
||||||
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
||||||
state = ducky_string(bad_usb, line_tmp);
|
state = ducky_string(bad_usb, line_tmp);
|
||||||
|
if(!state && error != NULL) {
|
||||||
|
snprintf(error, error_len, "Invalid string %s", line_tmp);
|
||||||
|
}
|
||||||
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
||||||
} else if(strncmp(line_tmp, ducky_cmd_altchar, strlen(ducky_cmd_altchar)) == 0) {
|
} else if(strncmp(line_tmp, ducky_cmd_altchar, strlen(ducky_cmd_altchar)) == 0) {
|
||||||
// ALTCHAR
|
// ALTCHAR
|
||||||
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
||||||
ducky_numlock_on();
|
ducky_numlock_on();
|
||||||
state = ducky_altchar(line_tmp);
|
state = ducky_altchar(line_tmp);
|
||||||
|
if(!state && error != NULL) {
|
||||||
|
snprintf(error, error_len, "Invalid altchar %s", line_tmp);
|
||||||
|
}
|
||||||
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
||||||
} else if(
|
} else if(
|
||||||
(strncmp(line_tmp, ducky_cmd_altstr_1, strlen(ducky_cmd_altstr_1)) == 0) ||
|
(strncmp(line_tmp, ducky_cmd_altstr_1, strlen(ducky_cmd_altstr_1)) == 0) ||
|
||||||
@@ -296,11 +301,17 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
|
|||||||
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
||||||
ducky_numlock_on();
|
ducky_numlock_on();
|
||||||
state = ducky_altstring(line_tmp);
|
state = ducky_altstring(line_tmp);
|
||||||
|
if(!state && error != NULL) {
|
||||||
|
snprintf(error, error_len, "Invalid altstring %s", line_tmp);
|
||||||
|
}
|
||||||
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
||||||
} else if(strncmp(line_tmp, ducky_cmd_repeat, strlen(ducky_cmd_repeat)) == 0) {
|
} else if(strncmp(line_tmp, ducky_cmd_repeat, strlen(ducky_cmd_repeat)) == 0) {
|
||||||
// REPEAT
|
// REPEAT
|
||||||
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
||||||
state = ducky_get_number(line_tmp, &bad_usb->repeat_cnt);
|
state = ducky_get_number(line_tmp, &bad_usb->repeat_cnt);
|
||||||
|
if(!state && error != NULL) {
|
||||||
|
snprintf(error, error_len, "Invalid number %s", line_tmp);
|
||||||
|
}
|
||||||
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
||||||
} else if(strncmp(line_tmp, ducky_cmd_sysrq, strlen(ducky_cmd_sysrq)) == 0) {
|
} else if(strncmp(line_tmp, ducky_cmd_sysrq, strlen(ducky_cmd_sysrq)) == 0) {
|
||||||
// SYSRQ
|
// SYSRQ
|
||||||
@@ -313,7 +324,12 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
|
|||||||
} else {
|
} else {
|
||||||
// Special keys + modifiers
|
// Special keys + modifiers
|
||||||
uint16_t key = ducky_get_keycode(bad_usb, line_tmp, false);
|
uint16_t key = ducky_get_keycode(bad_usb, line_tmp, false);
|
||||||
if(key == HID_KEYBOARD_NONE) return SCRIPT_STATE_ERROR;
|
if(key == HID_KEYBOARD_NONE) {
|
||||||
|
if(error != NULL) {
|
||||||
|
snprintf(error, error_len, "No keycode defined for %s", line_tmp);
|
||||||
|
}
|
||||||
|
return SCRIPT_STATE_ERROR;
|
||||||
|
}
|
||||||
if((key & 0xFF00) != 0) {
|
if((key & 0xFF00) != 0) {
|
||||||
// It's a modifier key
|
// It's a modifier key
|
||||||
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
||||||
@@ -323,7 +339,6 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
|
|||||||
furi_hal_hid_kb_release(key);
|
furi_hal_hid_kb_release(key);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
return SCRIPT_STATE_ERROR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ducky_set_usb_id(BadUsbScript* bad_usb, const char* line) {
|
static bool ducky_set_usb_id(BadUsbScript* bad_usb, const char* line) {
|
||||||
@@ -401,12 +416,13 @@ static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_fil
|
|||||||
|
|
||||||
if(bad_usb->repeat_cnt > 0) {
|
if(bad_usb->repeat_cnt > 0) {
|
||||||
bad_usb->repeat_cnt--;
|
bad_usb->repeat_cnt--;
|
||||||
delay_val = ducky_parse_line(bad_usb, bad_usb->line_prev);
|
delay_val = ducky_parse_line(
|
||||||
|
bad_usb, bad_usb->line_prev, bad_usb->st.error, sizeof(bad_usb->st.error));
|
||||||
if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
|
if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
|
||||||
return 0;
|
return 0;
|
||||||
} else if(delay_val < 0) { // Script error
|
} else if(delay_val < 0) { // Script error
|
||||||
bad_usb->st.error_line = bad_usb->st.line_cur - 1;
|
bad_usb->st.error_line = bad_usb->st.line_cur - 1;
|
||||||
FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_usb->st.line_cur - 1);
|
FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_usb->st.line_cur - 1U);
|
||||||
return SCRIPT_STATE_ERROR;
|
return SCRIPT_STATE_ERROR;
|
||||||
} else {
|
} else {
|
||||||
return (delay_val + bad_usb->defdelay);
|
return (delay_val + bad_usb->defdelay);
|
||||||
@@ -435,8 +451,12 @@ static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_fil
|
|||||||
bad_usb->st.line_cur++;
|
bad_usb->st.line_cur++;
|
||||||
bad_usb->buf_len = bad_usb->buf_len + bad_usb->buf_start - (i + 1);
|
bad_usb->buf_len = bad_usb->buf_len + bad_usb->buf_start - (i + 1);
|
||||||
bad_usb->buf_start = i + 1;
|
bad_usb->buf_start = i + 1;
|
||||||
delay_val = ducky_parse_line(bad_usb, bad_usb->line);
|
furi_string_trim(bad_usb->line);
|
||||||
if(delay_val < 0) {
|
delay_val = ducky_parse_line(
|
||||||
|
bad_usb, bad_usb->line, bad_usb->st.error, sizeof(bad_usb->st.error));
|
||||||
|
if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
|
||||||
|
return 0;
|
||||||
|
} else if(delay_val < 0) {
|
||||||
bad_usb->st.error_line = bad_usb->st.line_cur;
|
bad_usb->st.error_line = bad_usb->st.line_cur;
|
||||||
FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_usb->st.line_cur);
|
FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_usb->st.line_cur);
|
||||||
return SCRIPT_STATE_ERROR;
|
return SCRIPT_STATE_ERROR;
|
||||||
@@ -470,8 +490,6 @@ static int32_t bad_usb_worker(void* context) {
|
|||||||
BadUsbWorkerState worker_state = BadUsbStateInit;
|
BadUsbWorkerState worker_state = BadUsbStateInit;
|
||||||
int32_t delay_val = 0;
|
int32_t delay_val = 0;
|
||||||
|
|
||||||
FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config();
|
|
||||||
|
|
||||||
FURI_LOG_I(WORKER_TAG, "Init");
|
FURI_LOG_I(WORKER_TAG, "Init");
|
||||||
File* script_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
|
File* script_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
|
||||||
bad_usb->line = furi_string_alloc();
|
bad_usb->line = furi_string_alloc();
|
||||||
@@ -503,12 +521,16 @@ static int32_t bad_usb_worker(void* context) {
|
|||||||
|
|
||||||
} else if(worker_state == BadUsbStateNotConnected) { // State: USB not connected
|
} else if(worker_state == BadUsbStateNotConnected) { // State: USB not connected
|
||||||
uint32_t flags = furi_thread_flags_wait(
|
uint32_t flags = furi_thread_flags_wait(
|
||||||
WorkerEvtEnd | WorkerEvtConnect, FuriFlagWaitAny, FuriWaitForever);
|
WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle,
|
||||||
|
FuriFlagWaitAny,
|
||||||
|
FuriWaitForever);
|
||||||
furi_check((flags & FuriFlagError) == 0);
|
furi_check((flags & FuriFlagError) == 0);
|
||||||
if(flags & WorkerEvtEnd) {
|
if(flags & WorkerEvtEnd) {
|
||||||
break;
|
break;
|
||||||
} else if(flags & WorkerEvtConnect) {
|
} else if(flags & WorkerEvtConnect) {
|
||||||
worker_state = BadUsbStateIdle; // Ready to run
|
worker_state = BadUsbStateIdle; // Ready to run
|
||||||
|
} else if(flags & WorkerEvtToggle) {
|
||||||
|
worker_state = BadUsbStateWillRun; // Will run when USB is connected
|
||||||
}
|
}
|
||||||
bad_usb->st.state = worker_state;
|
bad_usb->st.state = worker_state;
|
||||||
|
|
||||||
@@ -535,6 +557,31 @@ static int32_t bad_usb_worker(void* context) {
|
|||||||
}
|
}
|
||||||
bad_usb->st.state = worker_state;
|
bad_usb->st.state = worker_state;
|
||||||
|
|
||||||
|
} else if(worker_state == BadUsbStateWillRun) { // State: start on connection
|
||||||
|
uint32_t flags = furi_thread_flags_wait(
|
||||||
|
WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle,
|
||||||
|
FuriFlagWaitAny,
|
||||||
|
FuriWaitForever);
|
||||||
|
furi_check((flags & FuriFlagError) == 0);
|
||||||
|
if(flags & WorkerEvtEnd) {
|
||||||
|
break;
|
||||||
|
} else if(flags & WorkerEvtConnect) { // Start executing script
|
||||||
|
DOLPHIN_DEED(DolphinDeedBadUsbPlayScript);
|
||||||
|
delay_val = 0;
|
||||||
|
bad_usb->buf_len = 0;
|
||||||
|
bad_usb->st.line_cur = 0;
|
||||||
|
bad_usb->defdelay = 0;
|
||||||
|
bad_usb->repeat_cnt = 0;
|
||||||
|
bad_usb->file_end = false;
|
||||||
|
storage_file_seek(script_file, 0, true);
|
||||||
|
// extra time for PC to recognize Flipper as keyboard
|
||||||
|
furi_thread_flags_wait(0, FuriFlagWaitAny, 1500);
|
||||||
|
worker_state = BadUsbStateRunning;
|
||||||
|
} else if(flags & WorkerEvtToggle) { // Cancel scheduled execution
|
||||||
|
worker_state = BadUsbStateNotConnected;
|
||||||
|
}
|
||||||
|
bad_usb->st.state = worker_state;
|
||||||
|
|
||||||
} else if(worker_state == BadUsbStateRunning) { // State: running
|
} else if(worker_state == BadUsbStateRunning) { // State: running
|
||||||
uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val);
|
uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val);
|
||||||
uint32_t flags = furi_thread_flags_wait(
|
uint32_t flags = furi_thread_flags_wait(
|
||||||
@@ -552,7 +599,9 @@ static int32_t bad_usb_worker(void* context) {
|
|||||||
}
|
}
|
||||||
bad_usb->st.state = worker_state;
|
bad_usb->st.state = worker_state;
|
||||||
continue;
|
continue;
|
||||||
} else if((flags == FuriFlagErrorTimeout) || (flags == FuriFlagErrorResource)) {
|
} else if(
|
||||||
|
(flags == (unsigned)FuriFlagErrorTimeout) ||
|
||||||
|
(flags == (unsigned)FuriFlagErrorResource)) {
|
||||||
if(delay_val > 0) {
|
if(delay_val > 0) {
|
||||||
bad_usb->st.delay_remain--;
|
bad_usb->st.delay_remain--;
|
||||||
continue;
|
continue;
|
||||||
@@ -591,8 +640,6 @@ static int32_t bad_usb_worker(void* context) {
|
|||||||
|
|
||||||
furi_hal_hid_set_state_callback(NULL, NULL);
|
furi_hal_hid_set_state_callback(NULL, NULL);
|
||||||
|
|
||||||
furi_hal_usb_set_config(usb_mode_prev, NULL);
|
|
||||||
|
|
||||||
storage_file_close(script_file);
|
storage_file_close(script_file);
|
||||||
storage_file_free(script_file);
|
storage_file_free(script_file);
|
||||||
furi_string_free(bad_usb->line);
|
furi_string_free(bad_usb->line);
|
||||||
@@ -618,16 +665,12 @@ BadUsbScript* bad_usb_script_open(FuriString* file_path) {
|
|||||||
bad_usb_script_set_default_keyboard_layout(bad_usb);
|
bad_usb_script_set_default_keyboard_layout(bad_usb);
|
||||||
|
|
||||||
bad_usb->st.state = BadUsbStateInit;
|
bad_usb->st.state = BadUsbStateInit;
|
||||||
|
bad_usb->st.error[0] = '\0';
|
||||||
|
|
||||||
bad_usb->thread = furi_thread_alloc();
|
bad_usb->thread = furi_thread_alloc_ex("BadUsbWorker", 2048, bad_usb_worker, bad_usb);
|
||||||
furi_thread_set_name(bad_usb->thread, "BadUsbWorker");
|
|
||||||
furi_thread_set_stack_size(bad_usb->thread, 2048);
|
|
||||||
furi_thread_set_context(bad_usb->thread, bad_usb);
|
|
||||||
furi_thread_set_callback(bad_usb->thread, bad_usb_worker);
|
|
||||||
|
|
||||||
furi_thread_start(bad_usb->thread);
|
furi_thread_start(bad_usb->thread);
|
||||||
return bad_usb;
|
return bad_usb;
|
||||||
}
|
} //-V773
|
||||||
|
|
||||||
void bad_usb_script_close(BadUsbScript* bad_usb) {
|
void bad_usb_script_close(BadUsbScript* bad_usb) {
|
||||||
furi_assert(bad_usb);
|
furi_assert(bad_usb);
|
||||||
@@ -647,7 +690,7 @@ void bad_usb_script_set_keyboard_layout(BadUsbScript* bad_usb, FuriString* layou
|
|||||||
}
|
}
|
||||||
|
|
||||||
File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
|
File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
|
||||||
if(!furi_string_empty(layout_path)) {
|
if(!furi_string_empty(layout_path)) { //-V1051
|
||||||
if(storage_file_open(
|
if(storage_file_open(
|
||||||
layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) {
|
layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||||
uint16_t layout[128];
|
uint16_t layout[128];
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ typedef enum {
|
|||||||
BadUsbStateInit,
|
BadUsbStateInit,
|
||||||
BadUsbStateNotConnected,
|
BadUsbStateNotConnected,
|
||||||
BadUsbStateIdle,
|
BadUsbStateIdle,
|
||||||
|
BadUsbStateWillRun,
|
||||||
BadUsbStateRunning,
|
BadUsbStateRunning,
|
||||||
BadUsbStateDelay,
|
BadUsbStateDelay,
|
||||||
BadUsbStateDone,
|
BadUsbStateDone,
|
||||||
@@ -25,6 +26,7 @@ typedef struct {
|
|||||||
uint16_t line_nb;
|
uint16_t line_nb;
|
||||||
uint32_t delay_remain;
|
uint32_t delay_remain;
|
||||||
uint16_t error_line;
|
uint16_t error_line;
|
||||||
|
char error[64];
|
||||||
} BadUsbState;
|
} BadUsbState;
|
||||||
|
|
||||||
BadUsbScript* bad_usb_script_open(FuriString* file_path);
|
BadUsbScript* bad_usb_script_open(FuriString* file_path);
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ static bool bad_usb_layout_select(BadUsbApp* bad_usb) {
|
|||||||
DialogsFileBrowserOptions browser_options;
|
DialogsFileBrowserOptions browser_options;
|
||||||
dialog_file_browser_set_basic_options(
|
dialog_file_browser_set_basic_options(
|
||||||
&browser_options, BAD_USB_APP_LAYOUT_EXTENSION, &I_keyboard_10px);
|
&browser_options, BAD_USB_APP_LAYOUT_EXTENSION, &I_keyboard_10px);
|
||||||
|
browser_options.base_path = BAD_USB_APP_PATH_LAYOUT_FOLDER;
|
||||||
|
browser_options.skip_assets = false;
|
||||||
|
|
||||||
// Input events and views are managed by file_browser
|
// Input events and views are managed by file_browser
|
||||||
bool res = dialog_file_browser_show(
|
bool res = dialog_file_browser_show(
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user