mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-12 12:42:30 +04:00
Compare commits
1019 Commits
un2-f61a8f
...
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 | ||
|
|
76aecb597a |
@@ -36,19 +36,12 @@ Min level: 1
|
||||
Max level: 1
|
||||
Weight: 3
|
||||
|
||||
Name: L2_Furippa2_128x64
|
||||
Name: L1_Happy_holidays_128x64
|
||||
Min butthurt: 0
|
||||
Max butthurt: 6
|
||||
Min level: 2
|
||||
Max level: 2
|
||||
Weight: 3
|
||||
|
||||
Name: L3_Furippa3_128x64
|
||||
Min butthurt: 0
|
||||
Max butthurt: 6
|
||||
Min level: 3
|
||||
Max butthurt: 14
|
||||
Min level: 1
|
||||
Max level: 3
|
||||
Weight: 3
|
||||
Weight: 4
|
||||
|
||||
Name: L1_Read_books_128x64
|
||||
Min butthurt: 0
|
||||
@@ -57,13 +50,6 @@ Min level: 1
|
||||
Max level: 1
|
||||
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
|
||||
Min butthurt: 8
|
||||
Max butthurt: 13
|
||||
@@ -85,12 +71,61 @@ Min level: 1
|
||||
Max level: 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: 6
|
||||
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
|
||||
Min butthurt: 0
|
||||
@@ -106,30 +141,9 @@ Min level: 3
|
||||
Max level: 3
|
||||
Weight: 3
|
||||
|
||||
Name: L3_Fireplace_128x64
|
||||
Name: L1_Sleigh_ride_128x64
|
||||
Min butthurt: 0
|
||||
Max butthurt: 13
|
||||
Min level: 2
|
||||
Max level: 3
|
||||
Weight: 3
|
||||
|
||||
Name: L2_FlipperCity_128x64
|
||||
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
|
||||
Min level: 1
|
||||
Max level: 3
|
||||
Weight: 3
|
||||
Weight: 4
|
||||
364
.drone.yml
364
.drone.yml
@@ -1,6 +1,10 @@
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: "Build firmware"
|
||||
name: "Release firmware"
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: amd64
|
||||
|
||||
steps:
|
||||
- name: "Update submodules"
|
||||
@@ -11,19 +15,63 @@ steps:
|
||||
- git submodule foreach git config --local gc.auto 0
|
||||
- git log -1 --format='%H'
|
||||
|
||||
- name: "Build default fw"
|
||||
- name: "Build firmware"
|
||||
image: hfdj/fztools
|
||||
pull: never
|
||||
commands:
|
||||
- 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
|
||||
- mkdir artifacts-default
|
||||
- mv dist/f7-C/* artifacts-default/
|
||||
- ls -laS artifacts-default
|
||||
- ls -laS artifacts-default/f7-update-${DRONE_TAG}
|
||||
- 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:
|
||||
FBT_TOOLS_CUSTOM_LINK:
|
||||
from_secret: fbt_link
|
||||
@@ -31,13 +79,24 @@ steps:
|
||||
- name: "Bundle self-update packages"
|
||||
image: kramos/alpine-zip
|
||||
commands:
|
||||
- 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 .
|
||||
- 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}
|
||||
- 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}
|
||||
- ls -laS artifacts-extra-apps
|
||||
- ls -laS artifacts-ofw-anims
|
||||
- ls -laS artifacts-default
|
||||
- mv artifacts-default/ ${DRONE_TAG}
|
||||
- ls -laS ${DRONE_TAG}
|
||||
|
||||
- name: "Upload to updates server"
|
||||
image: appleboy/drone-scp
|
||||
- name: "Upload default to updates srv"
|
||||
image: appleboy/drone-scp:linux-amd64
|
||||
settings:
|
||||
host:
|
||||
from_secret: dep_host
|
||||
@@ -48,8 +107,44 @@ steps:
|
||||
port:
|
||||
from_secret: dep_port
|
||||
target:
|
||||
from_secret: dep_target
|
||||
source: flipper-z-f7-update-${DRONE_TAG}.tgz
|
||||
from_secret: dep_target_new
|
||||
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"
|
||||
image: ddplugins/github-release
|
||||
@@ -61,8 +156,10 @@ steps:
|
||||
api_key:
|
||||
from_secret: github_apikey
|
||||
files:
|
||||
- artifacts-default/*.tgz
|
||||
- artifacts-default/*.zip
|
||||
- ${DRONE_TAG}/*.tgz
|
||||
- ${DRONE_TAG}/*.zip
|
||||
- artifacts-ofw-anims/*.tgz
|
||||
- artifacts-extra-apps/*.tgz
|
||||
title: ${DRONE_TAG}
|
||||
note: CHANGELOG.md
|
||||
checksum:
|
||||
@@ -70,6 +167,17 @@ steps:
|
||||
- sha1
|
||||
- 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"
|
||||
image: appleboy/drone-telegram
|
||||
settings:
|
||||
@@ -84,30 +192,47 @@ steps:
|
||||
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:
|
||||
- artifacts-default/flipper-z-f7-update-${DRONE_TAG}.tgz
|
||||
- ${DRONE_TAG}/flipper-z-f7-update-${DRONE_TAG}.tgz
|
||||
|
||||
- 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:
|
||||
webhook_id:
|
||||
from_secret: ds_wh_id
|
||||
webhook_token:
|
||||
from_secret: ds_wh_token
|
||||
message: "New Unleashed firmware released!
|
||||
|
||||
|
||||
Version: {{build.tag}}
|
||||
|
||||
|
||||
[[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})"
|
||||
token:
|
||||
from_secret: tgtoken
|
||||
to:
|
||||
from_secret: tgid
|
||||
format: markdown
|
||||
message: "Build with extra apps pack:"
|
||||
document:
|
||||
- flipper-z-f7-update-${DRONE_TAG}e.tgz
|
||||
|
||||
trigger:
|
||||
event:
|
||||
@@ -115,3 +240,186 @@ trigger:
|
||||
|
||||
node:
|
||||
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",
|
||||
]
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
*.swp
|
||||
*.swo
|
||||
*.gdb_history
|
||||
|
||||
|
||||
@@ -30,6 +31,10 @@ Brewfile.lock.json
|
||||
# Visual Studio Code
|
||||
.vscode/
|
||||
|
||||
# Kate
|
||||
.kateproject
|
||||
.kateconfig
|
||||
|
||||
# legendary cmake's
|
||||
build
|
||||
CMakeLists.txt
|
||||
@@ -55,3 +60,5 @@ openocd.log
|
||||
# PVS Studio temporary files
|
||||
.PVS-Studio/
|
||||
PVS-Studio.log
|
||||
|
||||
.gdbinit
|
||||
|
||||
9
.gitmodules
vendored
9
.gitmodules
vendored
@@ -22,12 +22,15 @@
|
||||
[submodule "lib/microtar"]
|
||||
path = lib/microtar
|
||||
url = https://github.com/amachronic/microtar.git
|
||||
[submodule "lib/scons"]
|
||||
path = lib/scons
|
||||
url = https://github.com/SCons/scons.git
|
||||
[submodule "lib/mbedtls"]
|
||||
path = lib/mbedtls
|
||||
url = https://github.com/Mbed-TLS/mbedtls.git
|
||||
[submodule "lib/cxxheaderparser"]
|
||||
path = lib/cxxheaderparser
|
||||
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.
|
||||
//-V:M_LET:1048,1044
|
||||
//-V:M_EACH:1048,1044
|
||||
//-V:ARRAY_DEF:760,747,568,776,729,712,654
|
||||
//-V:LIST_DEF:760,747,568,712,729,654,776
|
||||
//-V:BPTREE_DEF2:779,1086,557,773,512
|
||||
//-V:DICT_DEF2:779,524,776,760,1044,1001,729,590,568,747,685
|
||||
//-V:ALGO_DEF:1048,747,1044
|
||||
//-V:TUPLE_DEF2:524,590,1001,760
|
||||
|
||||
# Non-severe malloc/null pointer deref warnings
|
||||
//-V::522:2,3
|
||||
@@ -15,8 +17,30 @@
|
||||
# Potentially null argument warnings
|
||||
//-V:memset:575
|
||||
//-V:memcpy:575
|
||||
//-V:memcmp:575
|
||||
//-V:strlen:575
|
||||
//-V:strcpy:575
|
||||
//-V:strncpy:575
|
||||
//-V:strchr:575
|
||||
|
||||
# For loop warning on M_FOREACH
|
||||
//-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": [
|
||||
{
|
||||
"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",
|
||||
"compileCommands": "${workspaceFolder}/build/latest/compile_commands.json",
|
||||
"configurationProvider": "ms-vscode.cpptools",
|
||||
|
||||
23
.vscode/example/launch.json
vendored
23
.vscode/example/launch.json
vendored
@@ -38,6 +38,7 @@
|
||||
"postAttachCommands": [
|
||||
// "compare-sections",
|
||||
"source debug/flipperapps.py",
|
||||
"fap-set-debug-elf-root build/latest/.extapps",
|
||||
// "source debug/FreeRTOS/FreeRTOS.py",
|
||||
// "svd_load debug/STM32WB55_CM4.svd"
|
||||
]
|
||||
@@ -59,6 +60,7 @@
|
||||
"set confirm off",
|
||||
"set mem inaccessible-by-default off",
|
||||
"source debug/flipperapps.py",
|
||||
"fap-set-debug-elf-root build/latest/.extapps",
|
||||
// "compare-sections",
|
||||
]
|
||||
// "showDevDebugOutput": "raw",
|
||||
@@ -76,9 +78,30 @@
|
||||
"rtos": "FreeRTOS",
|
||||
"postAttachCommands": [
|
||||
"source debug/flipperapps.py",
|
||||
"fap-set-debug-elf-root build/latest/.extapps",
|
||||
]
|
||||
// "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",
|
||||
"type": "python",
|
||||
|
||||
6
.vscode/example/settings.json
vendored
6
.vscode/example/settings.json
vendored
@@ -6,13 +6,13 @@
|
||||
"cortex-debug.enableTelemetry": false,
|
||||
"cortex-debug.variableUseNaturalFormat": 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.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.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.osx": "${workspaceFolder}/toolchain/x86_64-darwin/bin/arm-none-eabi-gdb-py",
|
||||
"editor.formatOnSave": true,
|
||||
|
||||
54
.vscode/example/tasks.json
vendored
54
.vscode/example/tasks.json
vendored
@@ -105,17 +105,23 @@
|
||||
"type": "shell",
|
||||
"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",
|
||||
"group": "build",
|
||||
"type": "shell",
|
||||
"command": "./fbt plugin_dist"
|
||||
"command": "./fbt fap_dist"
|
||||
},
|
||||
{
|
||||
"label": "[Release] Build FAPs",
|
||||
"group": "build",
|
||||
"type": "shell",
|
||||
"command": "./fbt COMPACT=1 DEBUG=0 plugin_dist"
|
||||
"command": "./fbt COMPACT=1 DEBUG=0 fap_dist"
|
||||
},
|
||||
{
|
||||
"label": "[Debug] Launch App on Flipper",
|
||||
@@ -128,6 +134,50 @@
|
||||
"group": "build",
|
||||
"type": "shell",
|
||||
"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"
|
||||
],
|
||||
// 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"
|
||||
]
|
||||
}
|
||||
|
||||
46
CHANGELOG.md
46
CHANGELOG.md
@@ -1,36 +1,44 @@
|
||||
### New changes
|
||||
* SubGHz: Keeloq - added support for FAAC RC,XT, Mutancode, Normstahl
|
||||
* LFRFID: CLI - Add raw_analyze to usage help
|
||||
* PR -> Plugins: Minesweeper: Set cursor to initial position on death (by @TQMatvey | PR #113)
|
||||
* PR -> Plugins: Flappy bird: Border hitboxes, bigger Pilars (by @TQMatvey | PR #114)
|
||||
* Plugins: Updated Minesweeper [(by panki27)](https://github.com/panki27/minesweeper)
|
||||
* Plugins: Added DTMF Dolphin [(by litui)](https://github.com/litui/dtmf_dolphin)
|
||||
* SubGHz: **Nice ON2E (Nice One)** support (by @assasinfil | PR #335)
|
||||
* 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)
|
||||
* Archive and FileBrowser: **Fixed more navigation issues** (by @Willy-JL | PR #334)
|
||||
* Plugins -> SubGHz Bruteforcer: Fix Linear Delta 3 repeats (now its more stable and we will be sure signal is received correctly)
|
||||
* Plugins: Updated TOTP (Authenticator) [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator)
|
||||
* Infrared: Updated universal remote assets (by @Amec0e)
|
||||
* OFW: Feature: infrared add remote to cli
|
||||
* OFW: Remove the back button from MFC keys list
|
||||
* OFW: NFC fixes part 3
|
||||
* OFW: Enable all `view_` methods in SDK
|
||||
* OFW: fbt: fix for cincludes in app's private library definition
|
||||
* OFW: Desktop: fix fap in settings
|
||||
* OFW: Add BLE disconnect request
|
||||
* OFW: **Fix Cyfral & Metakom emulation (My temp fix removed and proper fix from OFW applied)**
|
||||
* OFW: BadUSB: disable CDC mode, USB mode switch fix
|
||||
* OFW: Updater visual fixes
|
||||
|
||||
#### [🎲 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)
|
||||
|
||||
[-> 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
|
||||
* Boosty: https://boosty.to/mmxdev
|
||||
* ETH/BSC/ERC20-Tokens: `0xFebF1bBc8229418FF2408C07AF6Afa49152fEc6a`
|
||||
* 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`
|
||||
* DOGE: `D6R6gYgBn5LwTNmPyvAQR6bZ9EtGgFCpvv`
|
||||
* 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**
|
||||
|
||||
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.
|
||||
|
||||
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,
|
||||
but it got it's limitations and this guide is still mandatory.
|
||||
Also, we already have automatic rules checking and formatting,
|
||||
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.
|
||||
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
Use project wiki to document new/reverse engineered standards.
|
||||
|
||||
@@ -52,7 +52,7 @@ Almost everything in flipper firmware is built around this concept.
|
||||
|
||||
## Naming
|
||||
|
||||
### Type names are CamelCase
|
||||
### Type names are PascalCase
|
||||
|
||||
Examples:
|
||||
|
||||
@@ -89,7 +89,7 @@ Enforced by linter.
|
||||
Suffixes:
|
||||
|
||||
- `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
|
||||
|
||||
|
||||
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 +-------------------------------------------------+)
|
||||
154
ReadMe.md
154
ReadMe.md
@@ -4,46 +4,62 @@
|
||||
</a>
|
||||
</h3>
|
||||
|
||||
### Welcome to Flipper Zero Unleashed Firmware repo!
|
||||
Our goal is to make any features possible in this device without any limitations!
|
||||
### Welcome to the Flipper Zero Unleashed Firmware repo!
|
||||
|
||||
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>
|
||||
|
||||
### 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>
|
||||
Our Discord Community:
|
||||
<br>
|
||||
<a href="https://discord.unleashedflip.com"><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>
|
||||
|
||||
## Dev builds
|
||||
- https://dev.unleashedflip.com/
|
||||
- https://t.me/kotnehleb
|
||||
## Releases in Telegram
|
||||
- https://t.me/unleashed_fw
|
||||
|
||||
# What's changed
|
||||
* SubGHz regional TX restrictions removed
|
||||
* SubGHz frequecy range can be extended in settings file (Warning: It can damage flipper's hardware)
|
||||
* Sub-GHz regional TX restrictions removed
|
||||
* 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
|
||||
* FAAC SLH (Spa) & BFT Mitto (secure with seed) manual creation
|
||||
* Sub-GHz static code brute-force plugin
|
||||
* LFRFID Fuzzer plugin
|
||||
* Custom community plugins and games added
|
||||
* Extra SubGHz frequencies + extra Mifare Classic keys
|
||||
* Custom community plugins and games added + all known working apps can be downloaded in extra pack in every release
|
||||
* Extra Sub-GHz frequencies + extra Mifare Classic keys
|
||||
* Picopass/iClass plugin included in releases
|
||||
* Recompiled IR TV Universal Remote for ALL buttons
|
||||
* Universal remote for Projectors, Fans, A/Cs and Audio(soundbars, etc.)
|
||||
* BadUSB keyboard layouts
|
||||
* Customizable Flipper name
|
||||
* SubGHz -> 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 -> Press OK in frequency analyzer to use detected frequency in Read modes
|
||||
* 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
|
||||
* 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 Sub-GHz protocols list:
|
||||
Thanks to Official team (to thier SubGHz Developer, Skorp) for implementing decoders for these protocols.
|
||||
|
||||
Encoders/sending made by Eng1n33r & @xMasterX:
|
||||
|
||||
### Current modified and new SubGHz protocols list:
|
||||
- Keeloq [Not ALL systems supported for decode or emulation yet!] - [Supported manufacturers list](https://0bin.net/paste/VwR2lNJY#WH9vnPgvcp7w6zVKucFCuNREKAcOij8KsJ6vqLfMn3b)
|
||||
- Keeloq: HCS101
|
||||
- Keeloq: AN-Motors
|
||||
@@ -56,73 +72,119 @@ Also check changelog in releases for latest updates!
|
||||
- Keeloq: Normstahl
|
||||
- CAME Atomo
|
||||
- Nice Flor S
|
||||
- FAAC SLH (Spa) [External seed calculation required (For info conatct me in Discord: Nano#8998)]
|
||||
- BFT Mitto [External seed calculation required (For info conatct me in Discord: Nano#8998)]
|
||||
- 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
|
||||
- Star Line (saving only)
|
||||
- Star Line
|
||||
|
||||
## Support us so we can buy equipment and develop new features
|
||||
Encoders made by @assasinfil & @xMasterX:
|
||||
- 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
|
||||
* ETH/BSC/ERC20-Tokens: `0xFebF1bBc8229418FF2408C07AF6Afa49152fEc6a`
|
||||
* 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`
|
||||
* DOGE: `D6R6gYgBn5LwTNmPyvAQR6bZ9EtGgFCpvv`
|
||||
* LTC: `ltc1q3ex4ejkl0xpx3znwrmth4lyuadr5qgv8tmq8z9`
|
||||
* XMR (Monero): `41xUz92suUu1u5Mu4qkrcs52gtfpu9rnZRdBpCJ244KRHf6xXSvVFevdf2cnjS7RAeYr5hn9MsEfxKoFDRSctFjG5fv1Mhn`
|
||||
* TON: `EQCOqcnYkvzOZUV_9bPE_8oTbOrOF03MnF-VcJyjisTZmpGf`
|
||||
|
||||
### Community apps included:
|
||||
|
||||
- RFID Fuzzer plugin [(by Ganapati)](https://github.com/DarkFlippers/unleashed-firmware/pull/54) with changes by @xMasterX & New protocols by @mvanzanten
|
||||
- Sub-GHz bruteforce plugin [(by Ganapati & xMasterX)](https://github.com/DarkFlippers/unleashed-firmware/pull/57) & Refactored by @derskythe
|
||||
- Sub-GHz playlist plugin [(by darmiel)](https://github.com/DarkFlippers/unleashed-firmware/pull/62)
|
||||
- **RFID Fuzzer** [(by Ganapati & @xMasterX)](https://github.com/DarkFlippers/unleashed-firmware/pull/54) & New protocols by @mvanzanten
|
||||
- **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** [(by darmiel)](https://github.com/DarkFlippers/unleashed-firmware/pull/62)
|
||||
- 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)
|
||||
- MultiConverter plugin [(by theisolinearchip)](https://github.com/theisolinearchip/flipperzero_stuff)
|
||||
- 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)
|
||||
- UPC-A Barcode generator plugin [(by McAzzaMan)](https://github.com/McAzzaMan/flipperzero-firmware/tree/UPC-A_Barcode_Generator/applications/barcode_generator)
|
||||
- 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)
|
||||
- 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)
|
||||
- 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)
|
||||
- Simple Clock (timer by GMMan / settings by kowalski7cc) [(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)
|
||||
- Simple Clock (timer by GMMan) [(original by CompaqDisc)](https://gist.github.com/CompaqDisc/4e329c501bd03c1e801849b81f48ea61)
|
||||
- **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)
|
||||
- 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:
|
||||
- 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)
|
||||
- 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)
|
||||
- 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)
|
||||
- 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
|
||||
|
||||
- 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)
|
||||
- SubGHz -> Detect RAW feature - [(by perspecdev)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/152)
|
||||
- SubGHz -> Save last used frequency and moduluation [(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)
|
||||
- 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 -> External CC1101 module support - [(by quen0n)](https://github.com/DarkFlippers/unleashed-firmware/pull/307)
|
||||
- Sub-GHz -> New frequency analyzer - [(by ClusterM)](https://github.com/DarkFlippers/unleashed-firmware/pull/43)
|
||||
- Sub-GHz -> Save last used frequency [(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)
|
||||
- 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
|
||||
## [- 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 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)
|
||||
|
||||
## [- How to change Flipper name](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/CustomFlipperName.md)
|
||||
|
||||
### **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)
|
||||
|
||||
## [- TOTP (Authenticator) config description](https://github.com/akopachov/flipper-zero_authenticator/blob/master/.github/conf-file_description.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)
|
||||
|
||||
@@ -130,10 +192,16 @@ Games:
|
||||
|
||||
## [- 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**
|
||||
|
||||
## [- 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: [WiFi] Scanner](https://github.com/SequoiaSan/FlipperZero-WiFi-Scanner_Module#readme)
|
||||
@@ -150,20 +218,20 @@ Games:
|
||||
|
||||
## [- 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>
|
||||
|
||||
# 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)
|
||||
## [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)
|
||||
## [SMC5326, UNILARM - SubGHz fixed code bruteforce](https://github.com/Hong5489/flipperzero-gate-bruteforce)
|
||||
## [CAME-12bit, NICE-12bit, Linear-10bit, PT-2240 - Sub-GHz fixed code bruteforce](https://github.com/tobiabocchi/flipperzero-bruteforce)
|
||||
## [SMC5326, UNILARM - Sub-GHz fixed code bruteforce](https://github.com/Hong5489/flipperzero-gate-bruteforce)
|
||||
|
||||
<br>
|
||||
<br>
|
||||
@@ -180,7 +248,7 @@ Games:
|
||||
|
||||
- `applications` - Applications and services used in firmware
|
||||
- `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
|
||||
- `documentation` - Documentation generation system configs and input files
|
||||
- `firmware` - Firmware source code
|
||||
@@ -188,4 +256,4 @@ Games:
|
||||
- `site_scons` - Build helpers
|
||||
- `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.
|
||||
|
||||
78
SConstruct
78
SConstruct
@@ -7,6 +7,7 @@
|
||||
# construction of certain targets behind command-line options.
|
||||
|
||||
import os
|
||||
from fbt.util import path_as_posix
|
||||
|
||||
DefaultEnvironment(tools=[])
|
||||
|
||||
@@ -33,10 +34,6 @@ coreenv = SConscript(
|
||||
)
|
||||
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
|
||||
distenv = coreenv.Clone(
|
||||
tools=[
|
||||
@@ -47,6 +44,7 @@ distenv = coreenv.Clone(
|
||||
"jflash",
|
||||
],
|
||||
ENV=os.environ,
|
||||
UPDATE_BUNDLE_DIR="dist/${DIST_DIR}/f${TARGET_HW}-update-${DIST_SUFFIX}",
|
||||
)
|
||||
|
||||
firmware_env = distenv.AddFwProject(
|
||||
@@ -144,23 +142,39 @@ distenv.Default(basic_dist)
|
||||
dist_dir = distenv.GetProjetDirName()
|
||||
fap_dist = [
|
||||
distenv.Install(
|
||||
f"#/dist/{dist_dir}/apps/debug_elf",
|
||||
firmware_env["FW_EXTAPPS"]["debug"].values(),
|
||||
distenv.Dir(f"#/dist/{dist_dir}/apps/debug_elf"),
|
||||
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])
|
||||
for dist_entry in firmware_env["FW_EXTAPPS"]["dist"].values()
|
||||
distenv.Install(
|
||||
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(fap_dist, firmware_env["FW_EXTAPPS"]["validators"].values())
|
||||
Depends(
|
||||
fap_dist,
|
||||
list(
|
||||
app_artifact.validator
|
||||
for app_artifact in firmware_env["FW_EXTAPPS"].applications.values()
|
||||
),
|
||||
)
|
||||
Alias("fap_dist", fap_dist)
|
||||
# distenv.Default(fap_dist)
|
||||
|
||||
plugin_resources_dist = list(
|
||||
distenv.Install(f"#/assets/resources/apps/{dist_entry[0]}", dist_entry[1])
|
||||
for dist_entry in firmware_env["FW_EXTAPPS"]["dist"].values()
|
||||
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
|
||||
@@ -191,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
|
||||
firmware_debug = distenv.PhonyTarget(
|
||||
"debug",
|
||||
@@ -198,6 +226,7 @@ firmware_debug = distenv.PhonyTarget(
|
||||
source=firmware_env["FW_ELF"],
|
||||
GDBOPTS="${GDBOPTS_BASE}",
|
||||
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)
|
||||
|
||||
@@ -207,6 +236,7 @@ distenv.PhonyTarget(
|
||||
source=firmware_env["FW_ELF"],
|
||||
GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
|
||||
GDBREMOTE="${BLACKMAGIC_ADDR}",
|
||||
FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")),
|
||||
)
|
||||
|
||||
# Debug alien elf
|
||||
@@ -215,7 +245,7 @@ distenv.PhonyTarget(
|
||||
"${GDBPYCOM}",
|
||||
GDBOPTS="${GDBOPTS_BASE}",
|
||||
GDBREMOTE="${OPENOCD_GDB_PIPE}",
|
||||
GDBPYOPTS='-ex "source debug/PyCortexMDebug/PyCortexMDebug.py" ',
|
||||
GDBPYOPTS='-ex "source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py" ',
|
||||
)
|
||||
|
||||
distenv.PhonyTarget(
|
||||
@@ -235,14 +265,14 @@ distenv.PhonyTarget(
|
||||
# Linter
|
||||
distenv.PhonyTarget(
|
||||
"lint",
|
||||
"${PYTHON3} scripts/lint.py check ${LINT_SOURCES}",
|
||||
LINT_SOURCES=firmware_env["LINT_SOURCES"],
|
||||
"${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py check ${LINT_SOURCES}",
|
||||
LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]],
|
||||
)
|
||||
|
||||
distenv.PhonyTarget(
|
||||
"format",
|
||||
"${PYTHON3} scripts/lint.py format ${LINT_SOURCES}",
|
||||
LINT_SOURCES=firmware_env["LINT_SOURCES"],
|
||||
"${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py format ${LINT_SOURCES}",
|
||||
LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]],
|
||||
)
|
||||
|
||||
# PY_LINT_SOURCES contains recursively-built modules' SConscript files + application manifests
|
||||
@@ -282,7 +312,7 @@ distenv.PhonyTarget(
|
||||
)
|
||||
|
||||
# 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
|
||||
@@ -291,6 +321,16 @@ distenv.PhonyTarget(
|
||||
"@echo $( ${BLACKMAGIC_ADDR} $)",
|
||||
)
|
||||
|
||||
|
||||
# Find STLink probe ids
|
||||
distenv.PhonyTarget(
|
||||
"get_stlink",
|
||||
distenv.Action(
|
||||
lambda **kw: distenv.GetDevices(),
|
||||
None,
|
||||
),
|
||||
)
|
||||
|
||||
# Prepare vscode environment
|
||||
vscode_dist = distenv.Install("#.vscode", distenv.Glob("#.vscode/example/*"))
|
||||
distenv.Precious(vscode_dist)
|
||||
|
||||
@@ -31,9 +31,10 @@ void AccessorApp::run(void) {
|
||||
onewire_host_stop(onewire_host);
|
||||
}
|
||||
|
||||
AccessorApp::AccessorApp() {
|
||||
AccessorApp::AccessorApp()
|
||||
: text_store{0} {
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ App(
|
||||
appid="accessor",
|
||||
name="Accessor",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
targets=["f7"],
|
||||
entry_point="accessor_app",
|
||||
cdefines=["APP_ACCESSOR"],
|
||||
requires=["gui"],
|
||||
|
||||
@@ -71,7 +71,7 @@ void WIEGAND::end() {
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
_cardTempHigh |= ((0x80000000 & _cardTemp) >> 31); // shift value to high bits
|
||||
@@ -171,9 +171,6 @@ bool WIEGAND::DoWiegandConversion() {
|
||||
return true;
|
||||
} else {
|
||||
_lastWiegand = sysTick;
|
||||
_bitCount = 0;
|
||||
_cardTemp = 0;
|
||||
_cardTempHigh = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,4 +11,5 @@ App(
|
||||
stack_size=1 * 1024,
|
||||
order=130,
|
||||
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* app = malloc(sizeof(BtDebugApp));
|
||||
|
||||
// Load settings
|
||||
bt_settings_load(&app->settings);
|
||||
|
||||
// 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) {
|
||||
UNUSED(p);
|
||||
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);
|
||||
dialog_message_show_storage_error(dialogs, "Incorrect\nRadioStack");
|
||||
return 255;
|
||||
}
|
||||
|
||||
BtDebugApp* app = bt_debug_app_alloc();
|
||||
// Was bt active?
|
||||
const bool was_active = furi_hal_bt_is_active();
|
||||
// Stop advertising
|
||||
furi_hal_bt_stop_advertising();
|
||||
|
||||
view_dispatcher_run(app->view_dispatcher);
|
||||
|
||||
// Restart advertising
|
||||
if(app->settings.enabled) {
|
||||
if(was_active) {
|
||||
furi_hal_bt_start_advertising();
|
||||
}
|
||||
bt_debug_app_free(app);
|
||||
|
||||
@@ -4,15 +4,14 @@
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
|
||||
#include <dialogs/dialogs.h>
|
||||
|
||||
#include <gui/modules/submenu.h>
|
||||
#include "views/bt_carrier_test.h"
|
||||
#include "views/bt_packet_test.h"
|
||||
#include <bt/bt_settings.h>
|
||||
|
||||
typedef struct {
|
||||
BtSettings settings;
|
||||
Gui* gui;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
Submenu* submenu;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "bt_carrier_test.h"
|
||||
#include "bt_test.h"
|
||||
#include "bt_test_types.h"
|
||||
#include "furi_hal_bt.h"
|
||||
#include <furi_hal_bt.h>
|
||||
|
||||
struct BtCarrierTest {
|
||||
BtTest* bt_test;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "bt_packet_test.h"
|
||||
#include "bt_test.h"
|
||||
#include "bt_test_types.h"
|
||||
#include "furi_hal_bt.h"
|
||||
#include <furi_hal_bt.h>
|
||||
|
||||
struct BtPacketTest {
|
||||
BtTest* bt_test;
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
|
||||
#include <gui/canvas.h>
|
||||
#include <gui/elements.h>
|
||||
|
||||
#include <lib/toolbox/float_tools.h>
|
||||
#include <m-array.h>
|
||||
#include <furi.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
|
||||
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));
|
||||
canvas_draw_str(canvas, 6, 60, model->message);
|
||||
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);
|
||||
canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str);
|
||||
}
|
||||
} else if(model->state == BtTestStateStopped) {
|
||||
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);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
@@ -153,7 +156,7 @@ static bool bt_test_input_callback(InputEvent* event, void* context) {
|
||||
}
|
||||
|
||||
void bt_test_process_up(BtTest* bt_test) {
|
||||
with_view_model(
|
||||
with_view_model( // -V658
|
||||
bt_test->view,
|
||||
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",
|
||||
cdefines=["APP_DISPLAY_TEST"],
|
||||
requires=["gui"],
|
||||
fap_libs=["misc"],
|
||||
stack_size=1 * 1024,
|
||||
order=120,
|
||||
fap_category="Debug",
|
||||
|
||||
@@ -91,7 +91,6 @@ static void display_test_reload_config(DisplayTest* instance) {
|
||||
instance->config_contrast,
|
||||
instance->config_regulation_ratio,
|
||||
instance->config_bias);
|
||||
gui_update(instance->gui);
|
||||
}
|
||||
|
||||
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_dispatcher_add_view(instance->view_dispatcher, DisplayTestViewConfigure, view);
|
||||
|
||||
// Configurtion items
|
||||
// Configuration items
|
||||
VariableItem* item;
|
||||
instance->config_bias = false;
|
||||
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,
|
||||
order=150,
|
||||
fap_category="Debug",
|
||||
fap_icon_assets="icons",
|
||||
)
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#include "assets_icons.h"
|
||||
#include "file_browser_app_i.h"
|
||||
#include "gui/modules/file_browser.h"
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <file_browser_test_icons.h>
|
||||
|
||||
#include <gui/modules/file_browser.h>
|
||||
#include <storage/storage.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) {
|
||||
furi_assert(context);
|
||||
@@ -48,7 +49,7 @@ FileBrowserApp* file_browser_app_alloc(char* arg) {
|
||||
|
||||
app->file_path = furi_string_alloc();
|
||||
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(
|
||||
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",
|
||||
name="LF-RFID Debug",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
targets=["f7"],
|
||||
entry_point="lfrfid_debug_app",
|
||||
requires=[
|
||||
"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);
|
||||
}
|
||||
@@ -8,5 +8,5 @@ App(
|
||||
stack_size=2 * 1024,
|
||||
order=70,
|
||||
fap_icon="uart_10px.png",
|
||||
fap_category="Misc",
|
||||
fap_category="GPIO",
|
||||
)
|
||||
|
||||
@@ -215,30 +215,26 @@ static UartEchoApp* uart_echo_app_alloc() {
|
||||
view_dispatcher_add_view(app->view_dispatcher, 0, app->view);
|
||||
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
|
||||
furi_hal_console_disable();
|
||||
furi_hal_uart_set_br(FuriHalUartIdUSART1, 115200);
|
||||
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;
|
||||
}
|
||||
|
||||
static void uart_echo_app_free(UartEchoApp* 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_join(app->worker_thread);
|
||||
furi_thread_free(app->worker_thread);
|
||||
|
||||
furi_hal_console_enable();
|
||||
|
||||
// Free views
|
||||
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 <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() {
|
||||
size_t heap_size = 0;
|
||||
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
|
||||
void* ptr;
|
||||
|
||||
// allocate memory case
|
||||
heap_size_old = memmgr_get_free_heap();
|
||||
ptr = malloc(alloc_size);
|
||||
heap_size = memmgr_get_free_heap();
|
||||
mu_assert_pointers_not_eq(ptr, NULL);
|
||||
mu_assert(heap_equal(heap_size, heap_size_old - alloc_size), "allocate failed");
|
||||
|
||||
// free memory case
|
||||
heap_size_old = memmgr_get_free_heap();
|
||||
ptr = malloc(100);
|
||||
mu_check(ptr != NULL);
|
||||
// test that memory is zero-initialized after allocation
|
||||
for(int i = 0; i < 100; i++) {
|
||||
mu_assert_int_eq(0, ((uint8_t*)ptr)[i]);
|
||||
}
|
||||
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
|
||||
ptr = malloc(100);
|
||||
memset(ptr, 66, 100);
|
||||
ptr = realloc(ptr, 200);
|
||||
mu_check(ptr != NULL);
|
||||
|
||||
// get filled array with some data
|
||||
original_ptr = malloc(alloc_size);
|
||||
mu_assert_pointers_not_eq(original_ptr, NULL);
|
||||
for(int i = 0; i < alloc_size; i++) {
|
||||
*(unsigned char*)(original_ptr + i) = i;
|
||||
// test that memory is really reallocated
|
||||
for(int i = 0; i < 100; i++) {
|
||||
mu_assert_int_eq(66, ((uint8_t*)ptr)[i]);
|
||||
}
|
||||
|
||||
// malloc array and copy data
|
||||
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);
|
||||
// TODO: fix realloc to copy only old size, and write testcase that leftover of reallocated memory is zero-initialized
|
||||
free(ptr);
|
||||
|
||||
// allocate and zero-initialize array (calloc)
|
||||
original_ptr = malloc(alloc_size);
|
||||
mu_assert_pointers_not_eq(original_ptr, NULL);
|
||||
|
||||
for(int i = 0; i < alloc_size; i++) {
|
||||
*(unsigned char*)(original_ptr + i) = 0;
|
||||
ptr = calloc(100, 2);
|
||||
mu_check(ptr != NULL);
|
||||
for(int i = 0; i < 100 * 2; i++) {
|
||||
mu_assert_int_eq(0, ((uint8_t*)ptr)[i]);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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(InfraredProtocolSamsung32, 1);
|
||||
infrared_test_run_decoder(InfraredProtocolSIRC, 3);
|
||||
infrared_test_run_decoder(InfraredProtocolKaseikyo, 1);
|
||||
}
|
||||
|
||||
MU_TEST(infrared_test_decoder_nec) {
|
||||
@@ -489,6 +490,15 @@ MU_TEST(infrared_test_encoder_rc6) {
|
||||
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) {
|
||||
infrared_test_run_encoder_decoder(InfraredProtocolNEC, 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(InfraredProtocolRC5, 1);
|
||||
infrared_test_run_encoder_decoder(InfraredProtocolSIRC, 1);
|
||||
infrared_test_run_encoder_decoder(InfraredProtocolKaseikyo, 1);
|
||||
}
|
||||
|
||||
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_samsung32);
|
||||
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_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( \
|
||||
double minunit_tmp_e; double minunit_tmp_r; minunit_assert++; minunit_tmp_e = (expected); \
|
||||
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); \
|
||||
snprintf( \
|
||||
minunit_last_message, \
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include <lib/nfc/protocols/nfca.h>
|
||||
#include <lib/nfc/helpers/mf_classic_dict.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/toolbox/stream/file_stream.h>
|
||||
@@ -17,6 +19,7 @@
|
||||
#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_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 uint32_t nfc_test_file_version = 1;
|
||||
@@ -99,7 +102,10 @@ static bool nfc_test_digital_signal_test_encode(
|
||||
|
||||
do {
|
||||
// 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
|
||||
FURI_CRITICAL_ENTER();
|
||||
@@ -133,7 +139,7 @@ static bool nfc_test_digital_signal_test_encode(
|
||||
ref_timings_sum += ref[i];
|
||||
if(timings_diff > timing_tolerance) {
|
||||
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;
|
||||
break;
|
||||
}
|
||||
@@ -287,9 +293,232 @@ MU_TEST(mf_classic_dict_load_test) {
|
||||
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) {
|
||||
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(mf_classic_dict_test);
|
||||
MU_RUN_TEST(mf_classic_dict_load_test);
|
||||
|
||||
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);
|
||||
|
||||
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[0].close_session_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_locker thread start
|
||||
FuriThread* locker_thread = furi_thread_alloc();
|
||||
furi_thread_set_name(locker_thread, "StorageFileLocker");
|
||||
furi_thread_set_stack_size(locker_thread, 2048);
|
||||
furi_thread_set_context(locker_thread, semaphore);
|
||||
furi_thread_set_callback(locker_thread, storage_file_locker);
|
||||
FuriThread* locker_thread =
|
||||
furi_thread_alloc_ex("StorageFileLocker", 2048, storage_file_locker, semaphore);
|
||||
furi_thread_start(locker_thread);
|
||||
|
||||
// wait for file lock
|
||||
@@ -133,11 +130,8 @@ MU_TEST(storage_dir_open_lock) {
|
||||
File* file = storage_file_alloc(storage);
|
||||
|
||||
// file_locker thread start
|
||||
FuriThread* locker_thread = furi_thread_alloc();
|
||||
furi_thread_set_name(locker_thread, "StorageDirLocker");
|
||||
furi_thread_set_stack_size(locker_thread, 2048);
|
||||
furi_thread_set_context(locker_thread, semaphore);
|
||||
furi_thread_set_callback(locker_thread, storage_dir_locker);
|
||||
FuriThread* locker_thread =
|
||||
furi_thread_alloc_ex("StorageDirLocker", 2048, storage_dir_locker, semaphore);
|
||||
furi_thread_start(locker_thread);
|
||||
|
||||
// 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_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"
|
||||
mu_check(stream_seek(stream, 4, StreamOffsetFromStart));
|
||||
mu_check(stream_write_string(stream, string_lee) == 3);
|
||||
mu_check(stream_size(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_assert_int_eq(0, stream_tell(stream));
|
||||
|
||||
// insert formated string at the end of stream
|
||||
// insert formatted string at the end of stream
|
||||
// "" -> "dio666"
|
||||
mu_check(stream_insert_format(stream, "%s%d", "dio", 666));
|
||||
mu_assert_int_eq(6, stream_size(stream));
|
||||
mu_check(stream_eof(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"
|
||||
mu_check(stream_insert_format(stream, "%s%d", "zlo", 555));
|
||||
mu_assert_int_eq(12, stream_size(stream));
|
||||
mu_check(stream_eof(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"
|
||||
mu_check(stream_seek(stream, 6, StreamOffsetFromStart));
|
||||
mu_check(stream_insert_format(stream, "%s%d", "baba", 13));
|
||||
|
||||
@@ -5,15 +5,16 @@
|
||||
#include <lib/subghz/transmitter.h>
|
||||
#include <lib/subghz/subghz_keystore.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>
|
||||
|
||||
#define TAG "SubGhz TEST"
|
||||
#define KEYSTORE_DIR_NAME EXT_PATH("subghz/assets/keeloq_mfcodes")
|
||||
#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 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_COUNT_PARSE 233
|
||||
#define TEST_RANDOM_COUNT_PARSE 329
|
||||
#define TEST_TIMEOUT 10000
|
||||
|
||||
static SubGhzEnvironment* environment_handler;
|
||||
@@ -43,6 +44,10 @@ static void subghz_test_init(void) {
|
||||
environment_handler, CAME_ATOMO_DIR_NAME);
|
||||
subghz_environment_set_nice_flor_s_rainbow_table_file_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);
|
||||
subghz_receiver_set_filter(receiver_handler, SubGhzProtocolFlag_Decodable);
|
||||
@@ -207,6 +212,152 @@ MU_TEST(subghz_keystore_test) {
|
||||
"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
|
||||
MU_TEST(subghz_decoder_came_atomo_test) {
|
||||
mu_assert(
|
||||
@@ -341,6 +492,14 @@ MU_TEST(subghz_decoder_linear_test) {
|
||||
"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_assert(
|
||||
subghz_decoder_test(
|
||||
@@ -413,11 +572,11 @@ MU_TEST(subghz_decoder_honeywell_wdb_test) {
|
||||
"Test decoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_magellen_test) {
|
||||
MU_TEST(subghz_decoder_magellan_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/magellen_raw.sub"), SUBGHZ_PROTOCOL_MAGELLEN_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_MAGELLEN_NAME " error\r\n");
|
||||
EXT_PATH("unit_tests/subghz/magellan_raw.sub"), SUBGHZ_PROTOCOL_MAGELLAN_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_MAGELLAN_NAME " error\r\n");
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_oregon2_test) {
|
||||
MU_TEST(subghz_decoder_ansonic_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/oregon2_raw.sub"), SUBGHZ_PROTOCOL_OREGON2_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_OREGON2_NAME " error\r\n");
|
||||
EXT_PATH("unit_tests/subghz/ansonic_raw.sub"), SUBGHZ_PROTOCOL_ANSONIC_NAME),
|
||||
"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
|
||||
@@ -485,6 +688,12 @@ MU_TEST(subghz_encoder_linear_test) {
|
||||
"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_assert(
|
||||
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");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_encoder_magellen_test) {
|
||||
MU_TEST(subghz_encoder_magellan_test) {
|
||||
mu_assert(
|
||||
subghz_encoder_test(EXT_PATH("unit_tests/subghz/magellen.sub")),
|
||||
"Test encoder " SUBGHZ_PROTOCOL_MAGELLEN_NAME " error\r\n");
|
||||
subghz_encoder_test(EXT_PATH("unit_tests/subghz/magellan.sub")),
|
||||
"Test encoder " SUBGHZ_PROTOCOL_MAGELLAN_NAME " error\r\n");
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
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_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();
|
||||
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_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_star_line_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_secplus_v1_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_phoenix_v2_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_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_came_test);
|
||||
@@ -612,6 +854,7 @@ MU_TEST_SUITE(subghz) {
|
||||
MU_RUN_TEST(subghz_encoder_nice_flo_test);
|
||||
MU_RUN_TEST(subghz_encoder_keelog_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_holtek_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_phoenix_v2_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_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);
|
||||
subghz_test_deinit();
|
||||
|
||||
@@ -9,19 +9,24 @@
|
||||
#define TAG "UnitTests"
|
||||
|
||||
int run_minunit_test_furi();
|
||||
int run_minunit_test_furi_hal();
|
||||
int run_minunit_test_furi_string();
|
||||
int run_minunit_test_infrared();
|
||||
int run_minunit_test_rpc();
|
||||
int run_minunit_test_manifest();
|
||||
int run_minunit_test_flipper_format();
|
||||
int run_minunit_test_flipper_format_string();
|
||||
int run_minunit_test_stream();
|
||||
int run_minunit_test_storage();
|
||||
int run_minunit_test_subghz();
|
||||
int run_minunit_test_dirwalk();
|
||||
int run_minunit_test_power();
|
||||
int run_minunit_test_protocol_dict();
|
||||
int run_minunit_test_lfrfid_protocols();
|
||||
int run_minunit_test_nfc();
|
||||
int run_minunit_test_bit_lib();
|
||||
int run_minunit_test_float_tools();
|
||||
int run_minunit_test_bt();
|
||||
|
||||
typedef int (*UnitTestEntry)();
|
||||
|
||||
@@ -32,19 +37,24 @@ typedef struct {
|
||||
|
||||
const UnitTest unit_tests[] = {
|
||||
{.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 = "storage", .entry = run_minunit_test_storage},
|
||||
{.name = "stream", .entry = run_minunit_test_stream},
|
||||
{.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_string", .entry = run_minunit_test_flipper_format_string},
|
||||
{.name = "rpc", .entry = run_minunit_test_rpc},
|
||||
{.name = "subghz", .entry = run_minunit_test_subghz},
|
||||
{.name = "infrared", .entry = run_minunit_test_infrared},
|
||||
{.name = "nfc", .entry = run_minunit_test_nfc},
|
||||
{.name = "power", .entry = run_minunit_test_power},
|
||||
{.name = "protocol_dict", .entry = run_minunit_test_protocol_dict},
|
||||
{.name = "lfrfid", .entry = run_minunit_test_lfrfid_protocols},
|
||||
{.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() {
|
||||
@@ -60,14 +70,13 @@ void minunit_print_progress() {
|
||||
}
|
||||
|
||||
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) {
|
||||
UNUSED(cli);
|
||||
UNUSED(args);
|
||||
UNUSED(context);
|
||||
uint32_t failed_tests = 0;
|
||||
minunit_run = 0;
|
||||
minunit_assert = 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_cmp_str(args, unit_tests[i].name) == 0) {
|
||||
failed_tests += unit_tests[i].entry();
|
||||
unit_tests[i].entry();
|
||||
} else {
|
||||
printf("Skipping %s\r\n", unit_tests[i].name);
|
||||
}
|
||||
} else {
|
||||
failed_tests += unit_tests[i].entry();
|
||||
unit_tests[i].entry();
|
||||
}
|
||||
}
|
||||
printf("\r\nFailed tests: %lu\r\n", failed_tests);
|
||||
|
||||
// Time report
|
||||
cycle_counter = (furi_get_tick() - cycle_counter);
|
||||
printf("Consumed: %lu ms\r\n", cycle_counter);
|
||||
if(minunit_run != 0) {
|
||||
printf("\r\nFailed tests: %u\r\n", minunit_fail);
|
||||
|
||||
// Wait for tested services and apps to deallocate memory
|
||||
furi_delay_ms(200);
|
||||
uint32_t heap_after = memmgr_get_free_heap();
|
||||
printf("Leaked: %ld\r\n", heap_before - heap_after);
|
||||
// Time report
|
||||
cycle_counter = (furi_get_tick() - cycle_counter);
|
||||
printf("Consumed: %lu ms\r\n", cycle_counter);
|
||||
|
||||
// Final Report
|
||||
if(failed_tests == 0) {
|
||||
notification_message(notification, &sequence_success);
|
||||
printf("Status: PASSED\r\n");
|
||||
} else {
|
||||
notification_message(notification, &sequence_error);
|
||||
printf("Status: FAILED\r\n");
|
||||
// Wait for tested services and apps to deallocate memory
|
||||
furi_delay_ms(200);
|
||||
uint32_t heap_after = memmgr_get_free_heap();
|
||||
printf("Leaked: %ld\r\n", heap_before - heap_after);
|
||||
|
||||
// Final Report
|
||||
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(
|
||||
appid="sample_apps",
|
||||
name="Sample apps bundle",
|
||||
appid="example_apps",
|
||||
name="Example apps bundle",
|
||||
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`.
|
||||
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",
|
||||
cdefines=["APP_ARCHIVE"],
|
||||
requires=["gui"],
|
||||
stack_size=4 * 1024,
|
||||
stack_size=6 * 1024,
|
||||
icon="A_FileManager_14",
|
||||
order=0,
|
||||
)
|
||||
|
||||
@@ -13,7 +13,7 @@ ArchiveAppTypeEnum archive_get_app_type(const char* path) {
|
||||
}
|
||||
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) {
|
||||
return i;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
#include <archive/views/archive_browser_view.h>
|
||||
#include "archive_files.h"
|
||||
#include "archive_apps.h"
|
||||
#include "archive_browser.h"
|
||||
#include "../views/archive_browser_view.h"
|
||||
|
||||
#include <core/common_defines.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 <math.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
static void
|
||||
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);
|
||||
}
|
||||
|
||||
static void
|
||||
archive_list_item_cb(void* context, FuriString* item_path, bool is_folder, bool is_last) {
|
||||
static void archive_list_item_cb(
|
||||
void* context,
|
||||
FuriString* item_path,
|
||||
uint32_t idx,
|
||||
bool is_folder,
|
||||
bool is_last) {
|
||||
furi_assert(context);
|
||||
UNUSED(idx);
|
||||
ArchiveBrowserView* browser = (ArchiveBrowserView*)context;
|
||||
|
||||
if(!is_last) {
|
||||
archive_add_file_item(browser, is_folder, furi_string_get_cstr(item_path));
|
||||
} else {
|
||||
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,
|
||||
FuriString* path,
|
||||
const char* filter_ext,
|
||||
bool skip_assets) {
|
||||
bool skip_assets,
|
||||
bool hide_dot_files) {
|
||||
furi_assert(browser);
|
||||
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_folder_callback(browser->worker, archive_folder_open_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;
|
||||
} else {
|
||||
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));
|
||||
|
||||
if(!archive_file_get_array_size(browser) && archive_is_home(browser)) {
|
||||
archive_switch_tab(browser, TAB_RIGHT);
|
||||
archive_switch_tab(browser, TAB_LEFT);
|
||||
} else {
|
||||
with_view_model(
|
||||
browser->view,
|
||||
@@ -203,7 +221,7 @@ void archive_file_array_rm_selected(ArchiveBrowserView* browser) {
|
||||
false);
|
||||
|
||||
if((items_cnt == 0) && (archive_is_home(browser))) {
|
||||
archive_switch_tab(browser, TAB_RIGHT);
|
||||
archive_switch_tab(browser, TAB_LEFT);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
if(offset_new > 0) {
|
||||
offset_new =
|
||||
CLAMP(offset_new, (int32_t)model->item_cnt - FILE_LIST_BUF_LEN, 0);
|
||||
offset_new = CLAMP(offset_new, (int32_t)model->item_cnt, 0);
|
||||
} else {
|
||||
offset_new = 0;
|
||||
}
|
||||
@@ -453,6 +470,13 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) {
|
||||
} else {
|
||||
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;
|
||||
archive_set_tab(browser, tab);
|
||||
@@ -474,8 +498,10 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) {
|
||||
tab = archive_get_tab(browser);
|
||||
if(archive_is_dir_exists(browser->path)) {
|
||||
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(
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
#include "../archive_i.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 FILE_LIST_BUF_LEN 100
|
||||
#define FILE_LIST_BUF_LEN 50
|
||||
|
||||
static const char* tab_default_paths[] = {
|
||||
[ArchiveTabFavorites] = "/app:favorites",
|
||||
@@ -17,6 +17,7 @@ static const char* tab_default_paths[] = {
|
||||
[ArchiveTabBadUsb] = ANY_PATH("badusb"),
|
||||
[ArchiveTabU2f] = "/app:u2f",
|
||||
[ArchiveTabApplications] = ANY_PATH("apps"),
|
||||
[ArchiveTabInternal] = STORAGE_INT_PATH_PREFIX,
|
||||
[ArchiveTabBrowser] = STORAGE_ANY_PATH_PREFIX,
|
||||
};
|
||||
|
||||
@@ -44,6 +45,7 @@ static const ArchiveFileTypeEnum known_type[] = {
|
||||
[ArchiveTabBadUsb] = ArchiveFileTypeBadUsb,
|
||||
[ArchiveTabU2f] = ArchiveFileTypeU2f,
|
||||
[ArchiveTabApplications] = ArchiveFileTypeApplication,
|
||||
[ArchiveTabInternal] = ArchiveFileTypeUnknown,
|
||||
[ArchiveTabBrowser] = ArchiveFileTypeUnknown,
|
||||
};
|
||||
|
||||
|
||||
@@ -177,7 +177,7 @@ bool archive_favorites_read(void* context) {
|
||||
|
||||
archive_set_item_count(browser, file_count);
|
||||
|
||||
if(need_refresh) {
|
||||
if(need_refresh) { //-V547
|
||||
archive_favourites_rescan();
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,11 @@ void archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder
|
||||
if(is_folder) {
|
||||
file->type = ArchiveFileTypeFolder;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <m-array.h>
|
||||
#include <furi.h>
|
||||
#include <m-algo.h>
|
||||
#include <storage/storage.h>
|
||||
#include "toolbox/path.h"
|
||||
|
||||
@@ -29,6 +30,7 @@ typedef struct {
|
||||
FuriString* custom_name;
|
||||
bool fav;
|
||||
bool is_app;
|
||||
bool is_text_file;
|
||||
} ArchiveFile_t;
|
||||
|
||||
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->fav = false;
|
||||
obj->is_app = false;
|
||||
obj->is_text_file = false;
|
||||
}
|
||||
|
||||
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->fav = src->fav;
|
||||
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) {
|
||||
@@ -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);
|
||||
obj->fav = src->fav;
|
||||
obj->is_app = src->is_app;
|
||||
obj->is_text_file = src->is_text_file;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
ARRAY_DEF(
|
||||
files_array,
|
||||
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))))
|
||||
static int ArchiveFile_t_cmp(const ArchiveFile_t* a, const ArchiveFile_t* b) {
|
||||
if(a->type == ArchiveFileTypeFolder && b->type != ArchiveFileTypeFolder) {
|
||||
return -1;
|
||||
}
|
||||
if(a->type != ArchiveFileTypeFolder && b->type == ArchiveFileTypeFolder) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
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);
|
||||
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: {
|
||||
const char* name = archive_get_name(browser);
|
||||
if(favorites) {
|
||||
archive_favorites_delete(name);
|
||||
archive_favorites_delete("%s", name);
|
||||
archive_file_array_rm_selected(browser);
|
||||
archive_show_file_menu(browser, false);
|
||||
} 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);
|
||||
consumed = true;
|
||||
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:
|
||||
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);
|
||||
}
|
||||
consumed = true;
|
||||
|
||||
@@ -2,3 +2,4 @@ ADD_SCENE(archive, browser, Browser)
|
||||
ADD_SCENE(archive, rename, Rename)
|
||||
ADD_SCENE(archive, delete, Delete)
|
||||
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.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) {
|
||||
archive_app_delete_file(browser, name);
|
||||
} else {
|
||||
archive_delete_file(browser, "%s", name);
|
||||
}
|
||||
archive_show_loading_popup(app, false);
|
||||
archive_show_file_menu(browser, false);
|
||||
return scene_manager_previous_scene(app->scene_manager);
|
||||
} 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);
|
||||
|
||||
FuriString* path_dst;
|
||||
|
||||
path_dst = furi_string_alloc();
|
||||
|
||||
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"
|
||||
|
||||
#define TAG "Archive"
|
||||
#define SCROLL_INTERVAL (333)
|
||||
#define SCROLL_DELAY (2)
|
||||
|
||||
static const char* ArchiveTabNames[] = {
|
||||
[ArchiveTabFavorites] = "Favorites",
|
||||
@@ -17,6 +19,7 @@ static const char* ArchiveTabNames[] = {
|
||||
[ArchiveTabBadUsb] = "Bad USB",
|
||||
[ArchiveTabU2f] = "U2F",
|
||||
[ArchiveTabApplications] = "Apps",
|
||||
[ArchiveTabInternal] = "Internal",
|
||||
[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_pin = furi_string_alloc_set("Pin");
|
||||
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_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),
|
||||
item_info,
|
||||
ArchiveBrowserEventFileMenuInfo);
|
||||
if(selected->is_text_file) {
|
||||
archive_menu_add_item(
|
||||
menu_array_push_raw(model->context_menu),
|
||||
item_show,
|
||||
ArchiveBrowserEventFileMenuShow);
|
||||
}
|
||||
archive_menu_add_item(
|
||||
menu_array_push_raw(model->context_menu),
|
||||
item_rename,
|
||||
@@ -100,6 +110,12 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
|
||||
menu_array_push_raw(model->context_menu),
|
||||
item_pin,
|
||||
ArchiveBrowserEventFileMenuPin);
|
||||
if(selected->type <= ArchiveFileTypeBadUsb) {
|
||||
archive_menu_add_item(
|
||||
menu_array_push_raw(model->context_menu),
|
||||
item_show,
|
||||
ArchiveBrowserEventFileMenuShow);
|
||||
}
|
||||
archive_menu_add_item(
|
||||
menu_array_push_raw(model->context_menu),
|
||||
item_rename,
|
||||
@@ -114,6 +130,12 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
|
||||
menu_array_push_raw(model->context_menu),
|
||||
item_info,
|
||||
ArchiveBrowserEventFileMenuInfo);
|
||||
if(selected->type <= ArchiveFileTypeBadUsb) {
|
||||
archive_menu_add_item(
|
||||
menu_array_push_raw(model->context_menu),
|
||||
item_show,
|
||||
ArchiveBrowserEventFileMenuShow);
|
||||
}
|
||||
archive_menu_add_item(
|
||||
menu_array_push_raw(model->context_menu),
|
||||
item_pin,
|
||||
@@ -136,6 +158,12 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
|
||||
menu_array_push_raw(model->context_menu),
|
||||
item_info,
|
||||
ArchiveBrowserEventFileMenuInfo);
|
||||
if(selected->type <= ArchiveFileTypeBadUsb) {
|
||||
archive_menu_add_item(
|
||||
menu_array_push_raw(model->context_menu),
|
||||
item_show,
|
||||
ArchiveBrowserEventFileMenuShow);
|
||||
}
|
||||
archive_menu_add_item(
|
||||
menu_array_push_raw(model->context_menu),
|
||||
item_rename,
|
||||
@@ -149,6 +177,7 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
|
||||
furi_string_free(item_run);
|
||||
furi_string_free(item_pin);
|
||||
furi_string_free(item_info);
|
||||
furi_string_free(item_show);
|
||||
furi_string_free(item_rename);
|
||||
furi_string_free(item_delete);
|
||||
} /*else {
|
||||
@@ -160,9 +189,9 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
|
||||
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
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);
|
||||
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(
|
||||
TAG,
|
||||
@@ -172,10 +201,10 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
|
||||
model->menu_idx);*/
|
||||
for(size_t i = 0; i < size_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) {
|
||||
@@ -242,13 +271,18 @@ static void draw_list(Canvas* canvas, ArchiveBrowserViewModel* model) {
|
||||
furi_string_set(str_buf, "---");
|
||||
}
|
||||
|
||||
elements_string_fit_width(
|
||||
canvas, str_buf, (scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX) - x_offset);
|
||||
size_t scroll_counter = model->scroll_counter;
|
||||
|
||||
if(model->item_idx == idx) {
|
||||
archive_draw_frame(canvas, i, scrollbar, model->move_fav);
|
||||
if(scroll_counter < SCROLL_DELAY) {
|
||||
scroll_counter = 0;
|
||||
} else {
|
||||
scroll_counter -= SCROLL_DELAY;
|
||||
}
|
||||
} else {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
scroll_counter = 0;
|
||||
}
|
||||
|
||||
if(custom_icon_data) {
|
||||
@@ -258,8 +292,15 @@ static void draw_list(Canvas* canvas, ArchiveBrowserViewModel* model) {
|
||||
canvas_draw_icon(
|
||||
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);
|
||||
}
|
||||
@@ -357,15 +398,20 @@ static bool archive_view_input(InputEvent* event, void* context) {
|
||||
|
||||
bool in_menu;
|
||||
bool move_fav_mode;
|
||||
bool is_loading;
|
||||
with_view_model(
|
||||
browser->view,
|
||||
ArchiveBrowserViewModel * model,
|
||||
{
|
||||
in_menu = model->menu;
|
||||
move_fav_mode = model->move_fav;
|
||||
is_loading = model->folder_loading || model->list_loading;
|
||||
},
|
||||
false);
|
||||
|
||||
if(is_loading) {
|
||||
return false;
|
||||
}
|
||||
if(in_menu) {
|
||||
if(event->type != InputTypeShort) {
|
||||
return true; // RETURN
|
||||
@@ -428,6 +474,7 @@ static bool archive_view_input(InputEvent* event, void* context) {
|
||||
if(move_fav_mode) {
|
||||
browser->callback(ArchiveBrowserEventFavMoveUp, browser->context);
|
||||
}
|
||||
model->scroll_counter = 0;
|
||||
} else if(event->key == InputKeyDown) {
|
||||
model->item_idx = (model->item_idx + 1) % model->item_cnt;
|
||||
if(is_file_list_load_required(model)) {
|
||||
@@ -437,9 +484,10 @@ static bool archive_view_input(InputEvent* event, void* context) {
|
||||
if(move_fav_mode) {
|
||||
browser->callback(ArchiveBrowserEventFavMoveDown, browser->context);
|
||||
}
|
||||
model->scroll_counter = 0;
|
||||
}
|
||||
},
|
||||
true);
|
||||
false);
|
||||
archive_update_offset(browser);
|
||||
}
|
||||
|
||||
@@ -476,6 +524,27 @@ static bool archive_view_input(InputEvent* event, void* context) {
|
||||
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 = malloc(sizeof(ArchiveBrowserView));
|
||||
browser->view = view_alloc();
|
||||
@@ -483,6 +552,10 @@ ArchiveBrowserView* browser_alloc() {
|
||||
view_set_context(browser->view, browser);
|
||||
view_set_draw_callback(browser->view, archive_view_render);
|
||||
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));
|
||||
|
||||
@@ -502,6 +575,8 @@ ArchiveBrowserView* browser_alloc() {
|
||||
void browser_free(ArchiveBrowserView* browser) {
|
||||
furi_assert(browser);
|
||||
|
||||
furi_timer_free(browser->scroll_timer);
|
||||
|
||||
if(browser->worker_running) {
|
||||
file_browser_worker_free(browser->worker);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../helpers/archive_files.h"
|
||||
#include "../helpers/archive_favorites.h"
|
||||
|
||||
#include <gui/gui_i.h>
|
||||
#include <gui/view.h>
|
||||
#include <gui/canvas.h>
|
||||
#include <gui/elements.h>
|
||||
#include <furi.h>
|
||||
#include <gui/modules/file_browser_worker.h>
|
||||
#include <storage/storage.h>
|
||||
#include "../helpers/archive_files.h"
|
||||
#include "../helpers/archive_menu.h"
|
||||
@@ -28,6 +31,7 @@ typedef enum {
|
||||
ArchiveTabBadUsb,
|
||||
ArchiveTabU2f,
|
||||
ArchiveTabApplications,
|
||||
ArchiveTabInternal,
|
||||
ArchiveTabBrowser,
|
||||
ArchiveTabTotal,
|
||||
} ArchiveTabEnum;
|
||||
@@ -40,6 +44,7 @@ typedef enum {
|
||||
ArchiveBrowserEventFileMenuRename,
|
||||
ArchiveBrowserEventFileMenuDelete,
|
||||
ArchiveBrowserEventFileMenuInfo,
|
||||
ArchiveBrowserEventFileMenuShow,
|
||||
ArchiveBrowserEventFileMenuClose,
|
||||
|
||||
ArchiveBrowserEventEnterDir,
|
||||
@@ -77,6 +82,7 @@ struct ArchiveBrowserView {
|
||||
FuriString* path;
|
||||
InputKey last_tab_switch_dir;
|
||||
bool is_root;
|
||||
FuriTimer* scroll_timer;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
@@ -95,6 +101,7 @@ typedef struct {
|
||||
int32_t item_idx;
|
||||
int32_t array_offset;
|
||||
int32_t list_offset;
|
||||
size_t scroll_counter;
|
||||
} ArchiveBrowserViewModel;
|
||||
|
||||
void archive_browser_set_callback(
|
||||
|
||||
@@ -11,4 +11,5 @@ App(
|
||||
stack_size=2 * 1024,
|
||||
icon="A_BadUsb_14",
|
||||
order=70,
|
||||
fap_libs=["assets"],
|
||||
)
|
||||
|
||||
@@ -33,9 +33,26 @@ static void bad_usb_load_settings(BadUsbApp* app) {
|
||||
!storage_file_eof(settings_file) && !isspace(chr)) {
|
||||
furi_string_push_back(app->keyboard_layout, chr);
|
||||
}
|
||||
} else {
|
||||
furi_string_reset(app->keyboard_layout);
|
||||
}
|
||||
storage_file_close(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) {
|
||||
@@ -98,8 +115,12 @@ BadUsbApp* bad_usb_app_alloc(char* arg) {
|
||||
|
||||
if(furi_hal_usb_is_locked()) {
|
||||
app->error = BadUsbAppErrorCloseRpc;
|
||||
app->usb_if_prev = NULL;
|
||||
scene_manager_next_scene(app->scene_manager, BadUsbSceneError);
|
||||
} 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)) {
|
||||
app->bad_usb_script = bad_usb_script_open(app->file_path);
|
||||
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;
|
||||
}
|
||||
|
||||
if(app->usb_if_prev) {
|
||||
furi_check(furi_hal_usb_set_config(app->usb_if_prev, NULL));
|
||||
}
|
||||
|
||||
// Views
|
||||
view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewWork);
|
||||
bad_usb_free(app->bad_usb_view);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "bad_usb_script.h"
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <assets_icons.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
@@ -13,9 +14,10 @@
|
||||
#include <gui/modules/variable_item_list.h>
|
||||
#include <gui/modules/widget.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_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_LAYOUT_EXTENSION ".kl"
|
||||
|
||||
@@ -38,6 +40,8 @@ struct BadUsbApp {
|
||||
FuriString* keyboard_layout;
|
||||
BadUsb* bad_usb_view;
|
||||
BadUsbScript* bad_usb_script;
|
||||
|
||||
FuriHalUsbInterface* usb_if_prev;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
|
||||
@@ -54,6 +54,7 @@ static const DuckyKey ducky_keys[] = {
|
||||
{"ALT-SHIFT", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT},
|
||||
{"ALT-GUI", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_GUI},
|
||||
{"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},
|
||||
{"CONTROL", KEY_MOD_LEFT_CTRL},
|
||||
@@ -75,8 +76,8 @@ static const DuckyKey ducky_keys[] = {
|
||||
{"BREAK", HID_KEYBOARD_PAUSE},
|
||||
{"PAUSE", HID_KEYBOARD_PAUSE},
|
||||
{"CAPSLOCK", HID_KEYBOARD_CAPS_LOCK},
|
||||
{"DELETE", HID_KEYBOARD_DELETE},
|
||||
{"BACKSPACE", HID_KEYPAD_BACKSPACE},
|
||||
{"DELETE", HID_KEYBOARD_DELETE_FORWARD},
|
||||
{"BACKSPACE", HID_KEYBOARD_DELETE},
|
||||
{"END", HID_KEYBOARD_END},
|
||||
{"ESC", HID_KEYBOARD_ESCAPE},
|
||||
{"ESCAPE", HID_KEYBOARD_ESCAPE},
|
||||
@@ -86,7 +87,7 @@ static const DuckyKey ducky_keys[] = {
|
||||
{"PAGEUP", HID_KEYBOARD_PAGE_UP},
|
||||
{"PAGEDOWN", HID_KEYBOARD_PAGE_DOWN},
|
||||
{"PRINTSCREEN", HID_KEYBOARD_PRINT_SCREEN},
|
||||
{"SCROLLOCK", HID_KEYBOARD_SCROLL_LOCK},
|
||||
{"SCROLLLOCK", HID_KEYBOARD_SCROLL_LOCK},
|
||||
{"SPACE", HID_KEYBOARD_SPACEBAR},
|
||||
{"TAB", HID_KEYBOARD_TAB},
|
||||
{"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_2[] = {"ALTCODE "};
|
||||
|
||||
static const char ducky_cmd_lang[] = {"DUCKY_LANG"};
|
||||
|
||||
static const uint8_t numpad_keys[10] = {
|
||||
HID_KEYPAD_0,
|
||||
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) {
|
||||
for(uint8_t i = 0; i < (sizeof(ducky_keys) / sizeof(ducky_keys[0])); i++) {
|
||||
uint8_t key_cmd_len = strlen(ducky_keys[i].name);
|
||||
for(size_t i = 0; i < (sizeof(ducky_keys) / sizeof(ducky_keys[0])); i++) {
|
||||
size_t key_cmd_len = strlen(ducky_keys[i].name);
|
||||
if((strncmp(param, ducky_keys[i].name, key_cmd_len) == 0) &&
|
||||
(ducky_is_line_end(param[key_cmd_len]))) {
|
||||
return ducky_keys[i].keycode;
|
||||
@@ -243,12 +242,8 @@ static int32_t
|
||||
const char* line_tmp = furi_string_get_cstr(line);
|
||||
bool state = false;
|
||||
|
||||
for(uint32_t i = 0; i < line_len; i++) {
|
||||
if((line_tmp[i] != ' ') && (line_tmp[i] != '\t') && (line_tmp[i] != '\n')) {
|
||||
line_tmp = &line_tmp[i];
|
||||
break; // Skip spaces and tabs
|
||||
}
|
||||
if(i == line_len - 1) return SCRIPT_STATE_NEXT_LINE; // Skip empty lines
|
||||
if(line_len == 0) {
|
||||
return SCRIPT_STATE_NEXT_LINE; // Skip empty lines
|
||||
}
|
||||
|
||||
FURI_LOG_D(WORKER_TAG, "line:%s", line_tmp);
|
||||
@@ -260,9 +255,6 @@ static int32_t
|
||||
} else if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) {
|
||||
// ID - executed in ducky_script_preload
|
||||
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) {
|
||||
// DELAY
|
||||
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
||||
@@ -347,10 +339,6 @@ static int32_t
|
||||
furi_hal_hid_kb_release(key);
|
||||
return (0);
|
||||
}
|
||||
if(error != NULL) {
|
||||
strncpy(error, "Unknown error", error_len);
|
||||
}
|
||||
return SCRIPT_STATE_ERROR;
|
||||
}
|
||||
|
||||
static bool ducky_set_usb_id(BadUsbScript* bad_usb, const char* line) {
|
||||
@@ -434,7 +422,7 @@ static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_fil
|
||||
return 0;
|
||||
} else if(delay_val < 0) { // Script error
|
||||
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;
|
||||
} else {
|
||||
return (delay_val + bad_usb->defdelay);
|
||||
@@ -463,10 +451,12 @@ static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_fil
|
||||
bad_usb->st.line_cur++;
|
||||
bad_usb->buf_len = bad_usb->buf_len + bad_usb->buf_start - (i + 1);
|
||||
bad_usb->buf_start = i + 1;
|
||||
furi_string_trim(bad_usb->line);
|
||||
delay_val = ducky_parse_line(
|
||||
bad_usb, bad_usb->line, bad_usb->st.error, sizeof(bad_usb->st.error));
|
||||
|
||||
if(delay_val < 0) {
|
||||
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;
|
||||
FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_usb->st.line_cur);
|
||||
return SCRIPT_STATE_ERROR;
|
||||
@@ -500,8 +490,6 @@ static int32_t bad_usb_worker(void* context) {
|
||||
BadUsbWorkerState worker_state = BadUsbStateInit;
|
||||
int32_t delay_val = 0;
|
||||
|
||||
FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config();
|
||||
|
||||
FURI_LOG_I(WORKER_TAG, "Init");
|
||||
File* script_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
|
||||
bad_usb->line = furi_string_alloc();
|
||||
@@ -533,12 +521,16 @@ static int32_t bad_usb_worker(void* context) {
|
||||
|
||||
} else if(worker_state == BadUsbStateNotConnected) { // State: USB not connected
|
||||
uint32_t flags = furi_thread_flags_wait(
|
||||
WorkerEvtEnd | WorkerEvtConnect, FuriFlagWaitAny, FuriWaitForever);
|
||||
WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle,
|
||||
FuriFlagWaitAny,
|
||||
FuriWaitForever);
|
||||
furi_check((flags & FuriFlagError) == 0);
|
||||
if(flags & WorkerEvtEnd) {
|
||||
break;
|
||||
} else if(flags & WorkerEvtConnect) {
|
||||
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;
|
||||
|
||||
@@ -565,6 +557,31 @@ static int32_t bad_usb_worker(void* context) {
|
||||
}
|
||||
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
|
||||
uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val);
|
||||
uint32_t flags = furi_thread_flags_wait(
|
||||
@@ -582,7 +599,9 @@ static int32_t bad_usb_worker(void* context) {
|
||||
}
|
||||
bad_usb->st.state = worker_state;
|
||||
continue;
|
||||
} else if((flags == FuriFlagErrorTimeout) || (flags == FuriFlagErrorResource)) {
|
||||
} else if(
|
||||
(flags == (unsigned)FuriFlagErrorTimeout) ||
|
||||
(flags == (unsigned)FuriFlagErrorResource)) {
|
||||
if(delay_val > 0) {
|
||||
bad_usb->st.delay_remain--;
|
||||
continue;
|
||||
@@ -621,8 +640,6 @@ static int32_t bad_usb_worker(void* context) {
|
||||
|
||||
furi_hal_hid_set_state_callback(NULL, NULL);
|
||||
|
||||
furi_hal_usb_set_config(usb_mode_prev, NULL);
|
||||
|
||||
storage_file_close(script_file);
|
||||
storage_file_free(script_file);
|
||||
furi_string_free(bad_usb->line);
|
||||
@@ -650,15 +667,10 @@ BadUsbScript* bad_usb_script_open(FuriString* file_path) {
|
||||
bad_usb->st.state = BadUsbStateInit;
|
||||
bad_usb->st.error[0] = '\0';
|
||||
|
||||
bad_usb->thread = furi_thread_alloc();
|
||||
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);
|
||||
|
||||
bad_usb->thread = furi_thread_alloc_ex("BadUsbWorker", 2048, bad_usb_worker, bad_usb);
|
||||
furi_thread_start(bad_usb->thread);
|
||||
return bad_usb;
|
||||
}
|
||||
} //-V773
|
||||
|
||||
void bad_usb_script_close(BadUsbScript* bad_usb) {
|
||||
furi_assert(bad_usb);
|
||||
@@ -678,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));
|
||||
if(!furi_string_empty(layout_path)) {
|
||||
if(!furi_string_empty(layout_path)) { //-V1051
|
||||
if(storage_file_open(
|
||||
layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||
uint16_t layout[128];
|
||||
|
||||
@@ -12,6 +12,7 @@ typedef enum {
|
||||
BadUsbStateInit,
|
||||
BadUsbStateNotConnected,
|
||||
BadUsbStateIdle,
|
||||
BadUsbStateWillRun,
|
||||
BadUsbStateRunning,
|
||||
BadUsbStateDelay,
|
||||
BadUsbStateDone,
|
||||
|
||||
@@ -17,6 +17,8 @@ static bool bad_usb_layout_select(BadUsbApp* bad_usb) {
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
dialog_file_browser_set_basic_options(
|
||||
&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
|
||||
bool res = dialog_file_browser_show(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "../bad_usb_app_i.h"
|
||||
#include "furi_hal_power.h"
|
||||
#include "furi_hal_usb.h"
|
||||
#include <furi_hal_power.h>
|
||||
#include <furi_hal_usb.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
static bool bad_usb_file_select(BadUsbApp* bad_usb) {
|
||||
furi_assert(bad_usb);
|
||||
@@ -8,6 +9,7 @@ static bool bad_usb_file_select(BadUsbApp* bad_usb) {
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
dialog_file_browser_set_basic_options(
|
||||
&browser_options, BAD_USB_APP_SCRIPT_EXTENSION, &I_badusb_10px);
|
||||
browser_options.base_path = BAD_USB_APP_BASE_FOLDER;
|
||||
browser_options.skip_assets = true;
|
||||
|
||||
// Input events and views are managed by file_browser
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "../bad_usb_script.h"
|
||||
#include "../bad_usb_app_i.h"
|
||||
#include "../views/bad_usb_view.h"
|
||||
#include "furi_hal.h"
|
||||
#include <furi_hal.h>
|
||||
#include "toolbox/path.h"
|
||||
|
||||
void bad_usb_scene_work_button_callback(InputKey key, void* context) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "../bad_usb_script.h"
|
||||
#include <toolbox/path.h>
|
||||
#include <gui/elements.h>
|
||||
#include <assets_icons.h>
|
||||
|
||||
#define MAX_NAME_LEN 64
|
||||
|
||||
@@ -44,10 +45,13 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
|
||||
|
||||
canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22);
|
||||
|
||||
if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone)) {
|
||||
if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone) ||
|
||||
(model->state.state == BadUsbStateNotConnected)) {
|
||||
elements_button_center(canvas, "Run");
|
||||
} else if((model->state.state == BadUsbStateRunning) || (model->state.state == BadUsbStateDelay)) {
|
||||
elements_button_center(canvas, "Stop");
|
||||
} else if(model->state.state == BadUsbStateWillRun) {
|
||||
elements_button_center(canvas, "Cancel");
|
||||
}
|
||||
|
||||
if((model->state.state == BadUsbStateNotConnected) ||
|
||||
@@ -60,6 +64,11 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Connect");
|
||||
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "to USB");
|
||||
} else if(model->state.state == BadUsbStateWillRun) {
|
||||
canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run");
|
||||
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "on connect");
|
||||
} else if(model->state.state == BadUsbStateFileError) {
|
||||
canvas_draw_icon(canvas, 4, 26, &I_Error_18x18);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user