mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-12 20:49:49 +04:00
Compare commits
1465 Commits
un2-60bce7
...
subghz_pro
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc6adcbeb1 | ||
|
|
842b497829 | ||
|
|
928d57b050 | ||
|
|
641934f8dd | ||
|
|
de07313173 | ||
|
|
921db0bb22 | ||
|
|
f93462667c | ||
|
|
c56eaae89d | ||
|
|
185bb3277a | ||
|
|
b5a60dc10b | ||
|
|
f4af57d15a | ||
|
|
0abb88842a | ||
|
|
bc06d407f3 | ||
|
|
cb89bb9628 | ||
|
|
fbf99d6cb3 | ||
|
|
d0c6c3402c | ||
|
|
5a730e3adc | ||
|
|
628f089c42 | ||
|
|
3accc005e7 | ||
|
|
e08b10fa3c | ||
|
|
9007f0e360 | ||
|
|
8608ba01aa | ||
|
|
3747f95d5a | ||
|
|
6c4334e7cb | ||
|
|
d247eb43de | ||
|
|
ca12057426 | ||
|
|
8b22952dac | ||
|
|
70af1e3bfb | ||
|
|
896a78c645 | ||
|
|
37b84a9dbb | ||
|
|
5a2f693551 | ||
|
|
82f77edc70 | ||
|
|
80b8a0dddb | ||
|
|
5d8acc2210 | ||
|
|
e96bae8473 | ||
|
|
89a07d3573 | ||
|
|
fb4e2e02be | ||
|
|
c1e2d2443a | ||
|
|
08acbda895 | ||
|
|
02fad884fb | ||
|
|
6f37c25bdb | ||
|
|
27012b6be7 | ||
|
|
d179199a42 | ||
|
|
73a9425f73 | ||
|
|
462836346e | ||
|
|
a4c28ed5bf | ||
|
|
b8deb931c2 | ||
|
|
1831902fbd | ||
|
|
99b126f0ef | ||
|
|
f388a7ecab | ||
|
|
92d55ec90f | ||
|
|
f86582b99e | ||
|
|
c2545a2f7b | ||
|
|
9d2297410e | ||
|
|
371f3b3869 | ||
|
|
6b99999566 | ||
|
|
11f3464e1b | ||
|
|
e5bef61364 | ||
|
|
c0afbf659a | ||
|
|
a6dc16a98c | ||
|
|
aa646c7590 | ||
|
|
416a6425de | ||
|
|
24a4ae8014 | ||
|
|
7b4f1e5d26 | ||
|
|
ec2d0588df | ||
|
|
433db87976 | ||
|
|
0f2bf693c1 | ||
|
|
01f5167577 | ||
|
|
6a3f1cb9e4 | ||
|
|
56b4de7e5d | ||
|
|
285c324e8d | ||
|
|
4e0c2b99a9 | ||
|
|
ac31be8db8 | ||
|
|
6e21e204ab | ||
|
|
2298cc1d7b | ||
|
|
81064f8e43 | ||
|
|
db012807f5 | ||
|
|
bd2d7bf42f | ||
|
|
e9df7c9b72 | ||
|
|
14331a197b | ||
|
|
e40444b526 | ||
|
|
0521bd4320 | ||
|
|
6871c2c142 | ||
|
|
b79cac4473 | ||
|
|
66ab192cd7 | ||
|
|
2c22061580 | ||
|
|
97087fadb9 | ||
|
|
affef106ea | ||
|
|
5cf4f5fab4 | ||
|
|
d9f6b02da5 | ||
|
|
a551317cc2 | ||
|
|
3eacb0c715 | ||
|
|
0e0046c803 | ||
|
|
36f1283bb3 | ||
|
|
1b1f58408d | ||
|
|
4e02ac3442 | ||
|
|
a098e0561e | ||
|
|
175287ee0a | ||
|
|
7d866fade1 | ||
|
|
c5e225656d | ||
|
|
a49c8d5a08 | ||
|
|
07093d072f | ||
|
|
727c21d60a | ||
|
|
2bd80f510a | ||
|
|
fda38c9d04 | ||
|
|
16a5b13be4 | ||
|
|
05be200762 | ||
|
|
b47174d206 | ||
|
|
502e8d1d8d | ||
|
|
5d6ce87259 | ||
|
|
1707a1d1a4 | ||
|
|
7927fa371e | ||
|
|
ef2c668e31 | ||
|
|
fe3862e113 | ||
|
|
539c4e2dd0 | ||
|
|
b9351649ed | ||
|
|
be33e099be | ||
|
|
ba36f4672c | ||
|
|
67c2d1cf61 | ||
|
|
e17877b6a1 | ||
|
|
82c730b6be | ||
|
|
4265057ee8 | ||
|
|
163be139eb | ||
|
|
71871949ec | ||
|
|
d7ecc95de4 | ||
|
|
a7a4f9b885 | ||
|
|
eaffe0ec17 | ||
|
|
3890c7f9fa | ||
|
|
6f44900e05 | ||
|
|
a005087636 | ||
|
|
8f2f2d810a | ||
|
|
d02c586b92 | ||
|
|
ed78c4ca04 | ||
|
|
db1a8f8014 | ||
|
|
6c517d5030 | ||
|
|
575cce48af | ||
|
|
4de21410b7 | ||
|
|
f0c3cde2f3 | ||
|
|
3993f2b89c | ||
|
|
39841bd5a9 | ||
|
|
2006bd1c95 | ||
|
|
bf4d00a7d1 | ||
|
|
bbc1ba62fc | ||
|
|
31259d5304 | ||
|
|
0afc4a8982 | ||
|
|
c8cf3e41a7 | ||
|
|
8288a08eb3 | ||
|
|
985e9f3d74 | ||
|
|
b1f581239b | ||
|
|
d6d7a5ece2 | ||
|
|
0dccbe28bb | ||
|
|
99253a0e28 | ||
|
|
30081b79c4 | ||
|
|
e31e880264 | ||
|
|
cee9b640b3 | ||
|
|
20f98050f2 | ||
|
|
cdfd9891b7 | ||
|
|
468d0ea966 | ||
|
|
00076deece | ||
|
|
23ecc186c2 | ||
|
|
b7c2b9eff1 | ||
|
|
81316255f9 | ||
|
|
6c768f2019 | ||
|
|
111c7557b3 | ||
|
|
7a3a1aaf0d | ||
|
|
e3d473bf42 | ||
|
|
224d0aefe4 | ||
|
|
e4c9568867 | ||
|
|
0ee04fb968 | ||
|
|
1eda913367 | ||
|
|
3df5a99968 | ||
|
|
d035872cf6 | ||
|
|
1ff5843ee6 | ||
|
|
f116e8165f | ||
|
|
147f42a2b7 | ||
|
|
79d45c97f9 | ||
|
|
6e179bda1f | ||
|
|
9f279ac872 | ||
|
|
72f250195c | ||
|
|
246dcdb23f | ||
|
|
683b5cdc82 | ||
|
|
92135f8f06 | ||
|
|
771dc95a44 | ||
|
|
cc8066d9a0 | ||
|
|
bbe55aa9d6 | ||
|
|
5a7d6fcc63 | ||
|
|
66e606fe96 | ||
|
|
425f8f0254 | ||
|
|
52680fd14e | ||
|
|
559c4567cb | ||
|
|
8c50225da4 | ||
|
|
edddfbcb2c | ||
|
|
bfbcce030c | ||
|
|
5458157924 | ||
|
|
7edf90c2e5 | ||
|
|
46fb86265c | ||
|
|
52bcf27c2a | ||
|
|
3c52dca0c2 | ||
|
|
ed4b2b3362 | ||
|
|
b05cba931b | ||
|
|
1d46df70a9 | ||
|
|
f45f2a45da | ||
|
|
a0d5aa0c7b | ||
|
|
cee11464de | ||
|
|
a617ce1813 | ||
|
|
05641378d9 | ||
|
|
fbe7209817 | ||
|
|
c630e351fc | ||
|
|
26b5b7e406 | ||
|
|
d94caf0ba6 | ||
|
|
16f8f2c2df | ||
|
|
989442eab6 | ||
|
|
ab452bd575 | ||
|
|
cbabd46a4d | ||
|
|
743ee7b990 | ||
|
|
d91dc04585 | ||
|
|
e6f7e3a37e | ||
|
|
2929b12134 | ||
|
|
3985cb61c8 | ||
|
|
376e45a09d | ||
|
|
fcd3799aee | ||
|
|
dfd46f5539 | ||
|
|
01a9854f8a | ||
|
|
d9be815889 | ||
|
|
7f3ebcd110 | ||
|
|
bf2ab1f4d5 | ||
|
|
5db7fdf985 | ||
|
|
b1496ee9bd | ||
|
|
cd29fb8568 | ||
|
|
a8e5f22500 | ||
|
|
e12958d408 | ||
|
|
55054fc1a7 | ||
|
|
f5fe0ff694 | ||
|
|
d93ed003fe | ||
|
|
24a23e5dc7 | ||
|
|
126a9efd09 | ||
|
|
eee5c35400 | ||
|
|
4dc4d34d04 | ||
|
|
8fc834090d | ||
|
|
a5d92d7a08 | ||
|
|
884a751ae7 | ||
|
|
411855ce81 | ||
|
|
5134f44c09 | ||
|
|
f4cb458391 | ||
|
|
34ff250eea | ||
|
|
709fa633ff | ||
|
|
898b29b921 | ||
|
|
f3939a715a | ||
|
|
3174d29dfa | ||
|
|
e4fd3b1b0f | ||
|
|
856c8a1784 | ||
|
|
f0015fcf3b | ||
|
|
42fc209c63 | ||
|
|
2aafc207c0 | ||
|
|
ecb0e5c520 | ||
|
|
c875aca468 | ||
|
|
80a778e4cb | ||
|
|
792b035cac | ||
|
|
e07b96e04b | ||
|
|
7f08b25928 | ||
|
|
f8cc914a15 | ||
|
|
c6e30a1ed9 | ||
|
|
5853e0bdd9 | ||
|
|
3dd76f5c2f | ||
|
|
59bcad0cfc | ||
|
|
034a436ae0 | ||
|
|
c303b44f3f | ||
|
|
6e1875f285 | ||
|
|
5a9da13d84 | ||
|
|
e4aad248cf | ||
|
|
0650178946 | ||
|
|
6aad6840f6 | ||
|
|
7aaecd9278 | ||
|
|
b52674607a | ||
|
|
adfac7affb | ||
|
|
7a211e0018 | ||
|
|
62b287aac7 | ||
|
|
fb39d6e679 | ||
|
|
b02a60f223 | ||
|
|
0b92e5ae74 | ||
|
|
27826f95ca | ||
|
|
24c12ab852 | ||
|
|
959ea4d8c1 | ||
|
|
7f09f5f616 | ||
|
|
dfe141c78f | ||
|
|
dce2271330 | ||
|
|
c708a301f5 | ||
|
|
927fcca081 | ||
|
|
db16b3ef1d | ||
|
|
379807423f | ||
|
|
8c94e3e78d | ||
|
|
5123fac5d9 | ||
|
|
cd5596027f | ||
|
|
374452796c | ||
|
|
a0814aab31 | ||
|
|
341610b8a1 | ||
|
|
414a13131a | ||
|
|
08acfa50c0 | ||
|
|
75c47a8d64 | ||
|
|
6aeb9478aa | ||
|
|
f41c06ab74 | ||
|
|
5f82732b4f | ||
|
|
ddc5540eeb | ||
|
|
729a1b5788 | ||
|
|
9e1a6a6d2e | ||
|
|
13acdf00da | ||
|
|
d50418e839 | ||
|
|
682d2d9b6f | ||
|
|
deaf80fddc | ||
|
|
895dba06ba | ||
|
|
c89a594d30 | ||
|
|
70f0ab1442 | ||
|
|
073fb3861a | ||
|
|
75e9de12b0 | ||
|
|
78139c5936 | ||
|
|
ad9d746a27 | ||
|
|
20621da8ac | ||
|
|
b30aa890e6 | ||
|
|
d2df35a35b | ||
|
|
44830593c1 | ||
|
|
8d2143add5 | ||
|
|
4dc2a7f212 | ||
|
|
b7046b2179 | ||
|
|
3bac76f75e | ||
|
|
8488f4406e | ||
|
|
4ec5dbcee8 | ||
|
|
78c64737aa | ||
|
|
76047e6e48 | ||
|
|
cd4f719783 | ||
|
|
b3f32b6938 | ||
|
|
3a4b5e6445 | ||
|
|
754a29009d | ||
|
|
8c791a998a | ||
|
|
74c46a515c | ||
|
|
797ddc0755 | ||
|
|
bc99538ca8 | ||
|
|
b6442f2971 | ||
|
|
c3b8d3cef4 | ||
|
|
3fa11aa4a0 | ||
|
|
ddf4f268ea | ||
|
|
a921a828e5 | ||
|
|
a94c553c5a | ||
|
|
50ee2eb8aa | ||
|
|
2c450bd835 | ||
|
|
1cdccbae23 | ||
|
|
109c34398f | ||
|
|
c24bea6b06 | ||
|
|
fa223a4f4b | ||
|
|
5e74622b2a | ||
|
|
9740dd8c75 | ||
|
|
41c43f4805 | ||
|
|
890419864c | ||
|
|
604b93645e | ||
|
|
7d4d2e19a9 | ||
|
|
ecf7dfd8fd | ||
|
|
c8fb64ce22 | ||
|
|
11c6d2d0be | ||
|
|
23155f9891 | ||
|
|
9248de50d1 | ||
|
|
b8dd75884c | ||
|
|
f7dfe77aaf | ||
|
|
ed5b2da9ad | ||
|
|
4571dd25a3 | ||
|
|
5d4dcc925d | ||
|
|
28345b2fbd | ||
|
|
82f8ad4540 | ||
|
|
df88a805a0 | ||
|
|
72dc8e95d0 | ||
|
|
1534bcf7cb | ||
|
|
62fbf868fe | ||
|
|
ba429f6c88 | ||
|
|
3d760077b6 | ||
|
|
189117326b | ||
|
|
84fbe6ad36 | ||
|
|
891d5845dd | ||
|
|
d3c5ccbfe6 | ||
|
|
91c159e823 | ||
|
|
8457da39e0 | ||
|
|
27cc057573 | ||
|
|
f28307128f | ||
|
|
27bfe861d9 | ||
|
|
a51a6914f4 | ||
|
|
00b4903576 | ||
|
|
a35b536120 | ||
|
|
6b30dc5114 | ||
|
|
b5aede37c7 | ||
|
|
3821570d74 | ||
|
|
6eb6297ab7 | ||
|
|
4ef23f3a58 | ||
|
|
95f8895b43 | ||
|
|
ebac25a057 | ||
|
|
06cf1bb868 | ||
|
|
7fae0d5070 | ||
|
|
0f1e52a271 | ||
|
|
f955864c01 | ||
|
|
e8e000d17b | ||
|
|
b11b9f1b38 | ||
|
|
26e5527a93 | ||
|
|
1c2f4da4ee | ||
|
|
cc30c0d01d | ||
|
|
4e3506ab2e | ||
|
|
a850aa8f2c | ||
|
|
984528d366 | ||
|
|
622abc902a | ||
|
|
08f48280cd | ||
|
|
ea054423b0 | ||
|
|
a96eb74f91 | ||
|
|
dc6379a1ca | ||
|
|
f89983e2fa | ||
|
|
66d31b3a0e | ||
|
|
d58b9f3fe8 | ||
|
|
4accce93cf | ||
|
|
27ee0f73f7 | ||
|
|
3108dc7c8c | ||
|
|
90573fbeed | ||
|
|
d7735a1efb | ||
|
|
69879c3693 | ||
|
|
529a20b9d2 | ||
|
|
43be797926 | ||
|
|
4840196b88 | ||
|
|
5db9238331 | ||
|
|
473f9c0e44 | ||
|
|
727f043747 | ||
|
|
ded7e727d0 | ||
|
|
08eb666f7b | ||
|
|
1390f10a6f | ||
|
|
f43b76efc2 | ||
|
|
8a279758fd | ||
|
|
4e347b207c | ||
|
|
b88aa9f391 | ||
|
|
3ec517bb54 | ||
|
|
bce32bf893 | ||
|
|
29ba7956c1 | ||
|
|
05293ce860 | ||
|
|
b21618b4cc | ||
|
|
96f6e77fd2 | ||
|
|
f5985ee27c | ||
|
|
4f05c1816b | ||
|
|
1c926cf8a2 | ||
|
|
1243dad8fd | ||
|
|
ac400abede | ||
|
|
a7fc43d1ac | ||
|
|
5992e2d4f2 | ||
|
|
aecdb8fdcd | ||
|
|
9192520c70 | ||
|
|
6d368c3e3a | ||
|
|
a34fbf6976 | ||
|
|
8582670a34 | ||
|
|
749a2f5977 | ||
|
|
1481036abe | ||
|
|
ad3bff0b67 | ||
|
|
c2cb14834d | ||
|
|
02369c8394 | ||
|
|
b0970953b9 | ||
|
|
dff73dfd38 | ||
|
|
2f96fad7c2 | ||
|
|
4d9ecbacae | ||
|
|
10580b5117 | ||
|
|
566e80abf6 | ||
|
|
a8541d5191 | ||
|
|
3b1bc8b7e1 | ||
|
|
8deecc9241 | ||
|
|
0bfaaa0fe1 | ||
|
|
488211b4d7 | ||
|
|
05147e5b7a | ||
|
|
0abe9ee417 | ||
|
|
09f80d0086 | ||
|
|
7ff9414656 | ||
|
|
aba70c33f1 | ||
|
|
f3553deb16 | ||
|
|
2796623551 | ||
|
|
5856746fc9 | ||
|
|
6d8ecec051 | ||
|
|
060a48cdfc | ||
|
|
91eed4209d | ||
|
|
787eafd7b8 | ||
|
|
10b55bcbd2 | ||
|
|
19e230823b | ||
|
|
d1418bf7f6 | ||
|
|
c34ae66b6e | ||
|
|
e0d716647c | ||
|
|
a9c2b4d6a0 | ||
|
|
4fb03cde62 | ||
|
|
7e049d185f | ||
|
|
376fa071a1 | ||
|
|
797eab8924 | ||
|
|
c7de198d1a | ||
|
|
4cee550cc6 | ||
|
|
e7107e39f7 | ||
|
|
0286636183 | ||
|
|
2bdc34274f | ||
|
|
842ba616ee | ||
|
|
9e2c3eb96d | ||
|
|
fa87216a1e | ||
|
|
921b47ac50 | ||
|
|
a81a5ca57c | ||
|
|
577334a394 | ||
|
|
84ba2690a5 | ||
|
|
de2334f314 | ||
|
|
4a4c041fd9 | ||
|
|
d5624cd842 | ||
|
|
ca72a4f225 | ||
|
|
453666192c | ||
|
|
cbc9720738 | ||
|
|
361ca8b750 | ||
|
|
cefff35661 | ||
|
|
fd5b1f1943 | ||
|
|
ef7052fbad | ||
|
|
22062faae2 | ||
|
|
8a09ac2569 | ||
|
|
5d18b189ec | ||
|
|
57f68c11e9 | ||
|
|
94e7bb7fe3 | ||
|
|
da32d252db | ||
|
|
1a4071a7c1 | ||
|
|
084cdc6ad0 | ||
|
|
36e15a1352 | ||
|
|
94453d9100 | ||
|
|
30801d4eb8 | ||
|
|
f10e82c64d | ||
|
|
0238da8ac9 | ||
|
|
5e5d6cf1a0 | ||
|
|
f2e6384860 | ||
|
|
0c3fc5cc30 | ||
|
|
1464c65d10 | ||
|
|
96a97f1bfb | ||
|
|
2dea6969fe | ||
|
|
675d1f007f | ||
|
|
65b6dd5707 | ||
|
|
3681a5478c | ||
|
|
fbcb1f0c6e | ||
|
|
a0488624b5 | ||
|
|
d56e399ae5 | ||
|
|
021ba9dc1a | ||
|
|
b5e7bb3334 | ||
|
|
1dc79fddf0 | ||
|
|
f43fd7a351 | ||
|
|
03153b4d02 | ||
|
|
327df4a813 | ||
|
|
1fa4c646e6 | ||
|
|
1c12613863 | ||
|
|
d541f142c8 | ||
|
|
966528b16a | ||
|
|
87fb852bcf | ||
|
|
d87caac5fe | ||
|
|
6ff437ad18 | ||
|
|
76e8b00dc8 | ||
|
|
f73d28677b | ||
|
|
bb3dd48447 | ||
|
|
17ea9315e0 | ||
|
|
63b40992ef | ||
|
|
27921e42ff | ||
|
|
ebf39f189d | ||
|
|
2954ec6d97 | ||
|
|
31a9a3f5f3 | ||
|
|
2fcff37f88 | ||
|
|
35a4eef3fc | ||
|
|
01e24f6837 | ||
|
|
9d728a1c65 | ||
|
|
7fb1af07b8 | ||
|
|
e756c680e5 | ||
|
|
9df393c301 | ||
|
|
df693e7e57 | ||
|
|
74fb904fb0 | ||
|
|
b85f533a20 | ||
|
|
6f9f0dbe6e | ||
|
|
6701cbacf9 | ||
|
|
aae82b2694 | ||
|
|
556b604f7b | ||
|
|
8794aad5d0 | ||
|
|
df808be8d7 | ||
|
|
99ef76e638 | ||
|
|
5c3a5cd8f7 | ||
|
|
349ef2ecb8 | ||
|
|
1fc67f76a2 | ||
|
|
6a470a464e | ||
|
|
c535b8f4ce | ||
|
|
2daf39018b | ||
|
|
741ad34b2c | ||
|
|
e42dda7cfb | ||
|
|
9a21dae29c | ||
|
|
c43ec414bb | ||
|
|
cbc5e3fa92 | ||
|
|
2b06b41ffd | ||
|
|
7d59eb1848 | ||
|
|
1c81dc2dee | ||
|
|
176f27e037 | ||
|
|
42cc41cda3 | ||
|
|
8d9786f89a | ||
|
|
11576e58c8 | ||
|
|
21e5b66bf6 | ||
|
|
7945afd1d2 | ||
|
|
dfbd978466 | ||
|
|
e1e208922e | ||
|
|
196fc562d8 | ||
|
|
de58d69c06 | ||
|
|
e40376bc63 | ||
|
|
853b182b8b | ||
|
|
ef4bdf3909 | ||
|
|
79fbaf2620 | ||
|
|
45d6826fa0 | ||
|
|
4070eeb1c9 | ||
|
|
7f67445c85 | ||
|
|
eb3a8734fb | ||
|
|
38c62cf247 | ||
|
|
1ce591a6c5 | ||
|
|
0ed290161d | ||
|
|
0b9d632363 | ||
|
|
a66f94d22e | ||
|
|
2a6a3a1bf7 | ||
|
|
79812a1fe0 | ||
|
|
0ab14c37c9 | ||
|
|
eec21b073b | ||
|
|
b70395eba9 | ||
|
|
297f185ef4 | ||
|
|
0261dc3075 | ||
|
|
60cd7b8b32 | ||
|
|
3d36105142 | ||
|
|
849afc8798 | ||
|
|
c535ce9b76 | ||
|
|
84f9af3e7e | ||
|
|
97e8da7a7b | ||
|
|
769c53b6da | ||
|
|
6b47bc1af4 | ||
|
|
1b3156521c | ||
|
|
a82c3ccc22 | ||
|
|
e121e6a287 | ||
|
|
03140e4349 | ||
|
|
43d35b42e5 | ||
|
|
7ed56fca54 | ||
|
|
bc8fe6144b | ||
|
|
0e4cda851c | ||
|
|
6267dc7d5b | ||
|
|
1a33e1a00b | ||
|
|
2985268fe3 | ||
|
|
5cdf339a95 | ||
|
|
08e533ccf2 | ||
|
|
a63e0dbec7 | ||
|
|
513c352820 | ||
|
|
bf62f66dc8 | ||
|
|
ab65c17dd5 | ||
|
|
6e7b7f1d89 | ||
|
|
4f9b4d80c7 | ||
|
|
bd21970ccb | ||
|
|
79193e7f37 | ||
|
|
f086d5b7a3 | ||
|
|
757b218397 | ||
|
|
0321717a86 | ||
|
|
2a5d629318 | ||
|
|
6ebdf6dffb | ||
|
|
b6435374d7 | ||
|
|
2c51c8cb31 | ||
|
|
6d7e424fdf | ||
|
|
8e14291363 | ||
|
|
c9473e6d30 | ||
|
|
ffa6249b64 | ||
|
|
daea8a963d | ||
|
|
7829428455 | ||
|
|
59022b4ff7 | ||
|
|
bbf4d90ec1 | ||
|
|
0f3c0aaa11 | ||
|
|
78a4c82bf5 | ||
|
|
af1ecd82ba | ||
|
|
963c6a95f3 | ||
|
|
04761b12c0 | ||
|
|
078e2c2db9 | ||
|
|
063b13df00 | ||
|
|
37159c7506 | ||
|
|
3da1c229bb | ||
|
|
51d478489a | ||
|
|
9bb0dbaa3e | ||
|
|
1c8451fad4 | ||
|
|
669822cdd2 | ||
|
|
4f3ef83c77 | ||
|
|
c511c67e71 | ||
|
|
b9c483fbf8 | ||
|
|
00fcd9cfcd | ||
|
|
cf5b87f82e | ||
|
|
8bbfadffe5 | ||
|
|
15dcf73083 | ||
|
|
5ef358df31 | ||
|
|
a5e3df0375 | ||
|
|
fae17d2eb9 | ||
|
|
c957bf09e0 | ||
|
|
817aa273f4 | ||
|
|
0adcaf7592 | ||
|
|
7e8bf03b25 | ||
|
|
a6886b096e | ||
|
|
a0dc770b98 | ||
|
|
a1b368abda | ||
|
|
5681016dce | ||
|
|
17cb7533cb | ||
|
|
1e183d5450 | ||
|
|
0e9f6bef77 | ||
|
|
0f81248579 | ||
|
|
337c5b5a8b | ||
|
|
be97baef06 | ||
|
|
d0ccf11992 | ||
|
|
eb3523b8e2 | ||
|
|
073a0942e8 | ||
|
|
52bd4a1a3c | ||
|
|
5989651b10 | ||
|
|
bb8b9f0b57 | ||
|
|
aface38b42 | ||
|
|
f7fb055ae3 | ||
|
|
1b64a95ec6 | ||
|
|
de5eb16ef2 | ||
|
|
c3731e6540 | ||
|
|
0e338f7964 | ||
|
|
4240b59311 | ||
|
|
41de5f3c52 | ||
|
|
b56fed477a | ||
|
|
02fe403b2b | ||
|
|
a821b80ffc | ||
|
|
950dee48a0 | ||
|
|
73441af9c6 | ||
|
|
f9730bcafe | ||
|
|
3c7a4eeaed | ||
|
|
90cefe7c71 | ||
|
|
368079f6f4 | ||
|
|
8b71ac9483 | ||
|
|
3cba34bbc8 | ||
|
|
741c784f2d | ||
|
|
394507bc81 | ||
|
|
8240b25fe0 | ||
|
|
1d196b41e9 | ||
|
|
666d30f2dd | ||
|
|
95df532612 | ||
|
|
64774f191f | ||
|
|
f6eed35729 | ||
|
|
930b369812 | ||
|
|
721ab717d7 | ||
|
|
aec36e7041 | ||
|
|
e7c4b40dbe | ||
|
|
820afd2aec | ||
|
|
a66e8d9ac9 | ||
|
|
f94e8f4ac8 | ||
|
|
a959fa32bc | ||
|
|
3c094c4741 | ||
|
|
625b1eb126 | ||
|
|
22902aabcc | ||
|
|
b93f2dc293 | ||
|
|
af40b6b020 | ||
|
|
ef72ad2462 | ||
|
|
3420cda83a | ||
|
|
68219f0d7b | ||
|
|
0a0c06222a | ||
|
|
52650ab0e3 | ||
|
|
dd601dd940 | ||
|
|
933471e2fb | ||
|
|
317c9ec190 | ||
|
|
e90d335cdf | ||
|
|
1657604eb2 | ||
|
|
684fdc632f | ||
|
|
c3bdbcd5cc | ||
|
|
65a593c52e | ||
|
|
9e39f82906 | ||
|
|
3985b456c3 | ||
|
|
c89e5e11a4 | ||
|
|
9f0aef330e | ||
|
|
328d049b6a | ||
|
|
ce294c3b1e | ||
|
|
e81b7be232 | ||
|
|
4d11213494 | ||
|
|
e38895794b | ||
|
|
2d6c2886ae | ||
|
|
aa2ecbe80f | ||
|
|
65005e71d2 | ||
|
|
0a86ad43ca | ||
|
|
e8913f2e33 | ||
|
|
04e50c9f89 | ||
|
|
7175459bcd | ||
|
|
c709b0b126 | ||
|
|
18f49c2644 | ||
|
|
939a77b4e7 | ||
|
|
1bb6113eaa | ||
|
|
0ab400a27e | ||
|
|
bf8fd71c00 | ||
|
|
3bd74b7f01 | ||
|
|
d68ac50efd | ||
|
|
e3ea5bca76 | ||
|
|
8ea0178a0d | ||
|
|
24d65e8655 | ||
|
|
d92212f0ea | ||
|
|
60d125e72a | ||
|
|
bb2bcc63a4 | ||
|
|
eee90c6c40 | ||
|
|
95182b266c | ||
|
|
7973013085 | ||
|
|
0b1f573a72 | ||
|
|
677de32026 | ||
|
|
5ac2431cd1 | ||
|
|
4b408b6988 | ||
|
|
af1647c84b | ||
|
|
356389ace9 | ||
|
|
345d9704fd | ||
|
|
0b2867aaa4 | ||
|
|
fe199ab54a | ||
|
|
adab2b9e03 | ||
|
|
c8bc9e26e0 | ||
|
|
e1f5999095 | ||
|
|
79b37556d4 | ||
|
|
9c4612e571 | ||
|
|
0652830c51 | ||
|
|
c417d467f7 | ||
|
|
a09d0a8bd4 | ||
|
|
ebc2b66372 | ||
|
|
abfa804ae0 | ||
|
|
9a0eacc91c | ||
|
|
a22b107335 | ||
|
|
0f1e03fa51 | ||
|
|
2ab9769b4b | ||
|
|
ee665e1089 | ||
|
|
5e940ea9d0 | ||
|
|
f3d45ab350 | ||
|
|
7d139517b8 | ||
|
|
f51877b7c8 | ||
|
|
92b8a256e4 | ||
|
|
0d27086c40 | ||
|
|
46bffba498 | ||
|
|
165d9972e7 | ||
|
|
bb5c545211 | ||
|
|
ddaa8ec1fd | ||
|
|
104a1998a5 | ||
|
|
38297cb7af | ||
|
|
419a90aa28 | ||
|
|
348c4c3b5f | ||
|
|
0bcfe7b7a4 | ||
|
|
7fe0e6c073 | ||
|
|
28d9d5194f | ||
|
|
296bdfd433 | ||
|
|
ddc6265920 | ||
|
|
2d3469e057 | ||
|
|
8e4595e4f7 | ||
|
|
328fcdd924 | ||
|
|
0dc55957ae | ||
|
|
5319c69e59 | ||
|
|
1115a0b877 | ||
|
|
908eca335e | ||
|
|
e242941d70 | ||
|
|
4e66d37ac8 | ||
|
|
0dff574757 | ||
|
|
6acf76e4ed | ||
|
|
d7c75b9db8 | ||
|
|
4fd52b0564 | ||
|
|
95bfaa8b23 | ||
|
|
639988a979 | ||
|
|
0a63b2fdd8 | ||
|
|
00b430a912 | ||
|
|
73b8d496fe | ||
|
|
d52d9ffe79 | ||
|
|
0dcf752342 | ||
|
|
9b55076eed | ||
|
|
c4479a9056 | ||
|
|
e7316464cb | ||
|
|
85d341104f | ||
|
|
cb47edf82f | ||
|
|
d5f791b1fa | ||
|
|
4da3f9cf15 | ||
|
|
8333ebd401 | ||
|
|
3d25838a53 | ||
|
|
3ea6d59c2f | ||
|
|
5277933980 | ||
|
|
93a6e17ce5 | ||
|
|
0349380347 | ||
|
|
09b622d4ae | ||
|
|
20e3a202aa | ||
|
|
0a68d80028 | ||
|
|
457b9ae2a9 | ||
|
|
4b921803cb | ||
|
|
9e8c841df0 | ||
|
|
2cfb5bdfc6 | ||
|
|
b287cb4b4a | ||
|
|
99ddb235e0 | ||
|
|
9cd0592aaf | ||
|
|
fa7aa00f57 | ||
|
|
981740bcc6 | ||
|
|
09fec61153 | ||
|
|
dcf076b11e | ||
|
|
3434305630 | ||
|
|
492f147568 | ||
|
|
be3ee9f2fe | ||
|
|
9fae52166c | ||
|
|
8dab5fd231 | ||
|
|
4c0c70ff64 | ||
|
|
26f852839a | ||
|
|
f11df49468 | ||
|
|
2fd8c71626 | ||
|
|
ba69ee88f3 | ||
|
|
8e362b087a | ||
|
|
88ea7c8d12 | ||
|
|
f9c08249a0 | ||
|
|
8995d03fad | ||
|
|
b20c01d68b | ||
|
|
1d08a8aaa2 | ||
|
|
2f1d20210b | ||
|
|
85900c50ff | ||
|
|
fc3b62aa41 | ||
|
|
8b7a52b97b | ||
|
|
a99d29c0f3 | ||
|
|
72ab2b98ad | ||
|
|
aff99a72e8 | ||
|
|
56b9361bc0 | ||
|
|
7c9f2cd3e8 | ||
|
|
ebaa84b0c2 | ||
|
|
89e27d10a2 | ||
|
|
a8edb41eae | ||
|
|
5c8df66b7c | ||
|
|
378bf05068 | ||
|
|
406d830fb6 | ||
|
|
85573745ec | ||
|
|
c72b305480 | ||
|
|
7d2d2b3dd9 | ||
|
|
d530238fae | ||
|
|
30f10dce80 | ||
|
|
0adad32418 | ||
|
|
cf4a0b06e2 | ||
|
|
fd127cee8c | ||
|
|
e8920ef15a | ||
|
|
ee614f11fc | ||
|
|
c2bf81dd64 | ||
|
|
c19a8fd636 | ||
|
|
5ed9ca838a | ||
|
|
29ab1088bd | ||
|
|
517d4ffb73 | ||
|
|
b39e0dd09e | ||
|
|
1e7b45159e | ||
|
|
9b75f957d9 | ||
|
|
e8f8cefd6f | ||
|
|
984d89c6d0 | ||
|
|
d159c56e28 | ||
|
|
d0e24bb455 | ||
|
|
e00f7009f3 | ||
|
|
64ecdce5ca | ||
|
|
de49bcc80c | ||
|
|
8793b9f0aa | ||
|
|
c6945313f6 | ||
|
|
fd41176353 | ||
|
|
d8fbaba7a0 | ||
|
|
de1d83038f | ||
|
|
f8af0c1509 | ||
|
|
9605af0e85 | ||
|
|
ed080ea5fc | ||
|
|
a24ef44289 | ||
|
|
1019025b13 | ||
|
|
a44739cde9 | ||
|
|
c19f3a3a6a | ||
|
|
6189511a87 | ||
|
|
12bbe3e68e | ||
|
|
12dda6b76b | ||
|
|
92a6cce70e | ||
|
|
8fc3140292 | ||
|
|
235af1aacb | ||
|
|
a0a8e0457a | ||
|
|
f291c9546d | ||
|
|
a73535070c | ||
|
|
8931cb2088 | ||
|
|
33e4d2a17f | ||
|
|
ca3ce2edce | ||
|
|
1ab59bac83 | ||
|
|
221bd13e09 | ||
|
|
b508fa7dfc | ||
|
|
fc2a643d12 | ||
|
|
2a30214ca2 | ||
|
|
120a74ee1a | ||
|
|
c1bb10a694 | ||
|
|
33892ebfb7 | ||
|
|
f8f7c2b895 | ||
|
|
96dd3ad1ce | ||
|
|
2740368b0e | ||
|
|
f56c94922d | ||
|
|
42df7aa04a | ||
|
|
732f744b3c | ||
|
|
5c821eb076 | ||
|
|
e8d62db634 | ||
|
|
e56f97245b | ||
|
|
26dfa9b9a5 | ||
|
|
62284209e6 | ||
|
|
6d86629e9a | ||
|
|
0139891d64 | ||
|
|
8415dcf823 | ||
|
|
9a9abd59e9 | ||
|
|
7417259dcd | ||
|
|
a3c6d8bb4a | ||
|
|
3807d61034 | ||
|
|
ab55ddbfb1 | ||
|
|
328fbffb80 | ||
|
|
79c3040629 | ||
|
|
4f6d27a214 | ||
|
|
f0e8da98ee | ||
|
|
c2e58f9633 | ||
|
|
63eced449e | ||
|
|
49ba5fc9f3 | ||
|
|
dd28e5ea7b | ||
|
|
da7b9c86b9 | ||
|
|
3303047a4a | ||
|
|
488563000a | ||
|
|
6c08564d37 | ||
|
|
5b8311cdea | ||
|
|
68429e191d | ||
|
|
5e7bcea29d | ||
|
|
a139f015b9 | ||
|
|
6579576490 | ||
|
|
57251eb028 | ||
|
|
f33ea3ebe0 | ||
|
|
84d12da45a | ||
|
|
72713d6f4e | ||
|
|
468bc1dace | ||
|
|
56f760aa07 | ||
|
|
6db6d123d5 | ||
|
|
68009c6230 | ||
|
|
02c27becb0 | ||
|
|
576dab02a4 | ||
|
|
4942bd2105 | ||
|
|
1ee82ba865 | ||
|
|
6e9658608e | ||
|
|
d49ca17824 | ||
|
|
fc776446de | ||
|
|
5a7fa30199 | ||
|
|
e4cdae4922 | ||
|
|
c7a67c1308 | ||
|
|
fd3e70492d | ||
|
|
a4ee73b470 | ||
|
|
8f5cbdc4c2 | ||
|
|
6d999abb80 | ||
|
|
89d49fdba7 | ||
|
|
04d67ca8ae | ||
|
|
4f7ca617cc | ||
|
|
f61a8fda53 | ||
|
|
5e35e51c57 | ||
|
|
dfbe21e720 | ||
|
|
18e7d2eb41 | ||
|
|
e7aaf3dbb2 | ||
|
|
76aecb597a | ||
|
|
d2018dfa1d | ||
|
|
8ec5527ae4 | ||
|
|
3a50021348 | ||
|
|
20c63664ca | ||
|
|
9b6abd8ef0 | ||
|
|
f96f2e2411 | ||
|
|
6442caa3e4 | ||
|
|
f06930e4ae | ||
|
|
865baed0bb | ||
|
|
f81999ea4a | ||
|
|
89a4b77e73 | ||
|
|
ead9f134f4 | ||
|
|
9ff29d12b2 | ||
|
|
5f3637ca44 | ||
|
|
effcb445ce | ||
|
|
55bad280ee | ||
|
|
b003ede76c | ||
|
|
57362b3eab | ||
|
|
cb0d9ec591 | ||
|
|
dad4772bec | ||
|
|
a16542cda6 | ||
|
|
a5d22154a9 | ||
|
|
ce173fd44c | ||
|
|
d547307357 | ||
|
|
c198a51b1c | ||
|
|
d5b239595f | ||
|
|
947e44ce86 | ||
|
|
55f8beef9f | ||
|
|
e46e6f8ee9 | ||
|
|
ff6c4f9957 | ||
|
|
8fdee1e460 | ||
|
|
558f85603d | ||
|
|
b909321699 | ||
|
|
ff52f7d9ba | ||
|
|
abfe53f156 | ||
|
|
9a2228bfa4 | ||
|
|
da17adb5ee | ||
|
|
7b16de2d6f | ||
|
|
735056628c | ||
|
|
9941df48bb | ||
|
|
8e0e4b5e2c | ||
|
|
220adf2375 | ||
|
|
39cd10f061 | ||
|
|
af1fb2018a | ||
|
|
3dde6089fc | ||
|
|
5f28eafcd2 | ||
|
|
94d2d5c99f | ||
|
|
3a569d4be8 | ||
|
|
db2f4847ba | ||
|
|
50dc2d7389 | ||
|
|
ede3bac799 | ||
|
|
eb4ff3c0fd | ||
|
|
afff1adf8f | ||
|
|
92a738bf77 | ||
|
|
b3d9523322 | ||
|
|
155cbeeb11 | ||
|
|
0d273bd15c | ||
|
|
2e047ff411 | ||
|
|
80a406afd3 | ||
|
|
6b5631e5b0 | ||
|
|
80735d4f51 | ||
|
|
e1e46b89b1 | ||
|
|
b7bdc4c985 | ||
|
|
f429d14ab5 | ||
|
|
744b61ca28 | ||
|
|
8520daf28b | ||
|
|
aa9f958f07 | ||
|
|
b3f96306ed | ||
|
|
022315e93d | ||
|
|
702786078d | ||
|
|
e78d73c35f | ||
|
|
2552278a3d | ||
|
|
34406f0607 | ||
|
|
04f5ad83f8 | ||
|
|
06c0adfe98 | ||
|
|
b0d4146c76 | ||
|
|
be293757c0 | ||
|
|
79e7c87b94 | ||
|
|
b6dfeac7ca | ||
|
|
349a151330 | ||
|
|
40dc80499f | ||
|
|
b0c31da36a | ||
|
|
a76259add9 | ||
|
|
3f3ee1822a | ||
|
|
0714df4a4a | ||
|
|
52361b4adf | ||
|
|
61fe66c178 | ||
|
|
016ebd3afc | ||
|
|
82b9d74b38 | ||
|
|
6d1929af25 | ||
|
|
384397c282 | ||
|
|
0abd54ccc8 | ||
|
|
ea833d891f | ||
|
|
9152299562 | ||
|
|
906124b091 | ||
|
|
9c0391a887 | ||
|
|
7ded162c94 | ||
|
|
382f620aff | ||
|
|
323a56e987 | ||
|
|
b65a2e9c94 | ||
|
|
eed8cd1824 | ||
|
|
500456b03d | ||
|
|
4b8221d813 | ||
|
|
c7a454752a | ||
|
|
b1f8073333 | ||
|
|
e489c57fc0 | ||
|
|
b4a3ac468f | ||
|
|
877a9202fd | ||
|
|
acbe7a10e2 | ||
|
|
b6b33a70e9 | ||
|
|
1132653d7a | ||
|
|
3fd30a9132 | ||
|
|
50e9f7d3ca | ||
|
|
e7de975885 | ||
|
|
f5a1872db4 | ||
|
|
409d2e5f05 | ||
|
|
91d7a38ca6 | ||
|
|
00323892f2 | ||
|
|
45840a1146 | ||
|
|
b0c9af9c06 | ||
|
|
7643fdad7c | ||
|
|
a1ede0a2fc | ||
|
|
b86f42e7fb | ||
|
|
31c0346adc | ||
|
|
9ba7e625ca | ||
|
|
981f7ff8b0 | ||
|
|
c38c42906f | ||
|
|
7c8eacb168 | ||
|
|
c13929330e | ||
|
|
88ca267466 | ||
|
|
1adf76d54d | ||
|
|
7ec4cb4b7a | ||
|
|
aedde45de8 | ||
|
|
184b467f62 | ||
|
|
0c5146e047 | ||
|
|
221ac1ed3b | ||
|
|
464c725a48 | ||
|
|
3e538a8ade | ||
|
|
ae76f45339 | ||
|
|
4cb585e4b9 | ||
|
|
16ea8dc568 | ||
|
|
4c9d62a33f | ||
|
|
d10e16ca3c | ||
|
|
37b5e58a60 | ||
|
|
b4b3c182c5 | ||
|
|
bdd052937b | ||
|
|
1a1f711897 | ||
|
|
4000f0cac5 | ||
|
|
1f742b611a | ||
|
|
38a82a1907 | ||
|
|
d1843c0094 | ||
|
|
3367bc6f68 | ||
|
|
515e6db69c | ||
|
|
81faca79ee | ||
|
|
eaa3adf98a | ||
|
|
dde18cd343 | ||
|
|
72b3d7f414 | ||
|
|
19cb469e4b | ||
|
|
01f7a3e5b5 | ||
|
|
6dde5586af | ||
|
|
5de2c32c81 | ||
|
|
69b9c54b2f | ||
|
|
dbf4b65d84 | ||
|
|
2ffb246e69 | ||
|
|
e3a5df5959 | ||
|
|
90eefeb2ed | ||
|
|
fda541c7fb | ||
|
|
42494d801f | ||
|
|
1253a78dba | ||
|
|
d07c2dbe54 | ||
|
|
8f1812655e | ||
|
|
3802171009 | ||
|
|
03ad1770f8 | ||
|
|
b86756b581 | ||
|
|
11681d8ee8 | ||
|
|
061f53cd3c | ||
|
|
4bbeeb19e2 | ||
|
|
61189c3c82 | ||
|
|
9bf11d9fd2 | ||
|
|
c76fcf5072 | ||
|
|
007a11d70d | ||
|
|
b61e41163b | ||
|
|
a69e150e2f | ||
|
|
f16cdd1477 | ||
|
|
ac286dfed8 | ||
|
|
a93008b218 | ||
|
|
31aaa593fc | ||
|
|
5a2719663f | ||
|
|
adccb87499 | ||
|
|
693f78e501 | ||
|
|
6eb610762e | ||
|
|
3e4d8a41e0 | ||
|
|
c60bfbf271 | ||
|
|
0796263e81 | ||
|
|
4bf29827f8 | ||
|
|
baf5034817 | ||
|
|
04e16970db | ||
|
|
a8b48771e4 | ||
|
|
1424878d65 | ||
|
|
a37b0d464c | ||
|
|
96502e21ae | ||
|
|
b5d6d60535 | ||
|
|
32e64fd29e | ||
|
|
8f9d81b972 | ||
|
|
23e0566273 | ||
|
|
4141483147 | ||
|
|
30e005d5c4 | ||
|
|
e3a2711eb3 | ||
|
|
8569641ce6 | ||
|
|
cafd06c71b | ||
|
|
06a7bda69b | ||
|
|
c43ce93936 | ||
|
|
2288855163 | ||
|
|
c0765c1114 | ||
|
|
683c6254da | ||
|
|
2ef515ef56 | ||
|
|
667be798fc | ||
|
|
0f9598099a | ||
|
|
0d6f729386 | ||
|
|
b452b6fd32 | ||
|
|
9403128a03 | ||
|
|
71589b28a7 | ||
|
|
8b0fa6d0b1 | ||
|
|
cf47da0ff4 | ||
|
|
d6b7fae7e4 | ||
|
|
110dc48b96 | ||
|
|
37c666ddf5 | ||
|
|
6ddca568b9 | ||
|
|
8993db56b8 | ||
|
|
3c1efda1db | ||
|
|
b62b7956a6 | ||
|
|
dce5af5c2e | ||
|
|
fbacdc5b7b | ||
|
|
8dba4f25ae | ||
|
|
43ef4046ed | ||
|
|
1e63f57bf7 | ||
|
|
649887fe0f | ||
|
|
be42c390f2 | ||
|
|
cbda5d996f | ||
|
|
de1ec97512 | ||
|
|
8af749c965 | ||
|
|
4d3f45e911 | ||
|
|
63fee41a1f | ||
|
|
f441fed68d | ||
|
|
6e0eeed773 | ||
|
|
6bf306200e | ||
|
|
e2faf31b45 | ||
|
|
85eb740559 | ||
|
|
cea14ae9c5 | ||
|
|
e9a11cfce0 | ||
|
|
87a14b96e1 | ||
|
|
bbd3f9cf71 | ||
|
|
230f09dddd | ||
|
|
24e744f1d1 | ||
|
|
127b700642 | ||
|
|
0f9ea925d3 | ||
|
|
836de3df16 | ||
|
|
c92217a109 | ||
|
|
41c93431c8 | ||
|
|
f0ea8f3a84 | ||
|
|
4d8f294e7a | ||
|
|
f543753873 | ||
|
|
1fb1a68842 | ||
|
|
54fedb9bc8 | ||
|
|
8af9c00ddb | ||
|
|
5a22803bbc | ||
|
|
824f5ea027 | ||
|
|
76d38e832e | ||
|
|
aba20b6af8 | ||
|
|
226f8517f3 | ||
|
|
7df70d7c62 | ||
|
|
9176387b9f | ||
|
|
972c0dcb4a | ||
|
|
62e56e2618 | ||
|
|
f202d27206 | ||
|
|
bcfb12bf28 | ||
|
|
5883e134d4 | ||
|
|
f8b532f063 | ||
|
|
e25b424188 | ||
|
|
4241ad24a3 | ||
|
|
6bcc6f363b | ||
|
|
3dcd8a73f1 | ||
|
|
9ad7f7825d | ||
|
|
aa00606d22 | ||
|
|
9c62e1f6ac | ||
|
|
b934c41ed0 | ||
|
|
0ebb3f060e | ||
|
|
8f2bd3be57 | ||
|
|
cc02d57857 | ||
|
|
0c5f11f713 | ||
|
|
006d27ed93 | ||
|
|
746b732034 | ||
|
|
2ec9aeeefa | ||
|
|
8be08093e2 | ||
|
|
406247c5d8 | ||
|
|
22a87d5707 | ||
|
|
4271246d8a | ||
|
|
a3db6718c2 | ||
|
|
0f0473bee6 | ||
|
|
497be7ccb0 | ||
|
|
12a6290e91 | ||
|
|
e6e1e7fe15 | ||
|
|
cb14d23108 | ||
|
|
c4783664c0 | ||
|
|
91f3774246 | ||
|
|
1f8a034a71 | ||
|
|
8cc3e2f35a | ||
|
|
069dd29f08 | ||
|
|
91c06a2168 | ||
|
|
545c4349d6 | ||
|
|
a40e1a2be2 | ||
|
|
286300b35b | ||
|
|
633145495c | ||
|
|
a0bcbf731d | ||
|
|
60242cd7c4 | ||
|
|
3e9409a1a8 | ||
|
|
a9210b2849 | ||
|
|
a58807c57a | ||
|
|
5bb7cabea6 | ||
|
|
f201062819 | ||
|
|
9f501034c3 | ||
|
|
7bd0c8ff2c | ||
|
|
cdcf80ed05 | ||
|
|
3e3a167764 | ||
|
|
fa9602bd68 | ||
|
|
efb09380bd | ||
|
|
61fee8e269 | ||
|
|
5e30b14d90 | ||
|
|
c07e3a34dd | ||
|
|
be7e11e60f | ||
|
|
e96e414561 | ||
|
|
0c99cb52ec | ||
|
|
ad9e1ce4df | ||
|
|
22dc5190d1 | ||
|
|
f2fd97d9c5 | ||
|
|
08084d5763 | ||
|
|
add1ad6949 | ||
|
|
87654e60b8 | ||
|
|
6f92cd645e | ||
|
|
23f6ea2e05 | ||
|
|
a6b98ccbbe | ||
|
|
ba5f590dab | ||
|
|
f1048733d2 | ||
|
|
ea7f68fcab | ||
|
|
8013aacd94 | ||
|
|
be8f409098 | ||
|
|
97e6fe8f4e | ||
|
|
54757428e6 | ||
|
|
bd39d81324 | ||
|
|
2a2078d9b5 | ||
|
|
01ca588488 | ||
|
|
f86eada292 | ||
|
|
bc777b2eff | ||
|
|
6f91fa42f0 | ||
|
|
e6d22ed147 | ||
|
|
436f70b69b | ||
|
|
ec9ce0cad7 | ||
|
|
7e2008095e | ||
|
|
92e440c77d | ||
|
|
666821e9ce | ||
|
|
1bca477a43 | ||
|
|
41571ce9ad | ||
|
|
038d098c85 | ||
|
|
b03cc8ddc3 | ||
|
|
c8e3d9b040 | ||
|
|
aeb02500de | ||
|
|
eadd7801af | ||
|
|
6d2b0a3b6c | ||
|
|
8093721c24 | ||
|
|
32a7642761 | ||
|
|
e8bb45496d | ||
|
|
3846852f2b | ||
|
|
17d01f5c29 | ||
|
|
e6bcba6959 | ||
|
|
e13edc2f70 | ||
|
|
de6ff1d9c9 | ||
|
|
bea15134ba | ||
|
|
28a55bf576 | ||
|
|
e70121e20f | ||
|
|
432ff41d6a | ||
|
|
87393a086c | ||
|
|
6000d47a0f | ||
|
|
d986ef4104 | ||
|
|
f85dc1675d | ||
|
|
7c7ac07e6a | ||
|
|
ca02826cfd | ||
|
|
96ad7f3cef | ||
|
|
c213ff596a | ||
|
|
b2589698ff | ||
|
|
3360f818a1 | ||
|
|
066da4080b | ||
|
|
b2c118f267 | ||
|
|
a8db46124e | ||
|
|
672e27f258 | ||
|
|
e762a68265 | ||
|
|
8659becc9d | ||
|
|
82e1e8af6a | ||
|
|
a71d05a114 | ||
|
|
8d68bf62a5 | ||
|
|
2c85adb270 | ||
|
|
e2123c55bb | ||
|
|
f5ff6438d1 | ||
|
|
9f3b80e606 | ||
|
|
111656d2c1 | ||
|
|
2045a29d3f | ||
|
|
26e46f9267 | ||
|
|
d003db0404 | ||
|
|
5a31e35dc2 | ||
|
|
c7cd5721ed | ||
|
|
fb476c29e6 | ||
|
|
d80329b323 | ||
|
|
3d3c422751 | ||
|
|
ed385594a3 | ||
|
|
787df44c79 | ||
|
|
f0eedc3243 | ||
|
|
365d055dc5 | ||
|
|
650ba8a91f | ||
|
|
654d6dc6ec | ||
|
|
18b70ac6b5 | ||
|
|
51369d6219 | ||
|
|
181533df1b | ||
|
|
d85731636f | ||
|
|
6f66f87fab | ||
|
|
85d4dedb15 | ||
|
|
0a4cddb84c | ||
|
|
4e17230290 | ||
|
|
aa99ac53c7 | ||
|
|
875f70196b | ||
|
|
1e71d212fe | ||
|
|
269a6ce562 | ||
|
|
cd2014c51b |
@@ -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,6 +71,62 @@ 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: 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
|
||||
Max butthurt: 8
|
||||
@@ -99,23 +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_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
|
||||
365
.drone.yml
365
.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,14 +79,24 @@ steps:
|
||||
- name: "Bundle self-update packages"
|
||||
image: kramos/alpine-zip
|
||||
commands:
|
||||
- tar czpf artifacts-default/flipper-z-f7-update-${DRONE_TAG}.tgz -C artifacts-default f7-update-${DRONE_TAG}
|
||||
- cp artifacts-extra-apps/flipper-z-f7-update-${DRONE_TAG}e.tgz .
|
||||
- cp artifacts-ofw-anims/flipper-z-f7-update-${DRONE_TAG}n.tgz .
|
||||
- cp artifacts-default/flipper-z-f7-update-${DRONE_TAG}.tgz .
|
||||
- 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
|
||||
@@ -49,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
|
||||
@@ -62,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:
|
||||
@@ -71,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:
|
||||
@@ -85,30 +192,47 @@ steps:
|
||||
Version: {{build.tag}}
|
||||
|
||||
|
||||
[-Github-](https://github.com/Eng1n33r/flipperzero-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/Eng1n33r/flipperzero-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:
|
||||
@@ -116,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
|
||||
|
||||
5
.github/CODEOWNERS
vendored
Normal file
5
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# Default
|
||||
* @xMasterX
|
||||
|
||||
# Assets
|
||||
/assets/resources/infrared/ @xMasterX @amec0e
|
||||
7
.github/FUNDING.yml
vendored
Normal file
7
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
ko_fi: masterx
|
||||
custom:
|
||||
[
|
||||
"https://boosty.to/mmxdev",
|
||||
"https://pay.cloudtips.ru/p/7b3e9d65",
|
||||
"https://yoomoney.ru/fundraise/XA49mgQLPA0.221209",
|
||||
]
|
||||
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -4,5 +4,5 @@ contact_links:
|
||||
url: https://t.me/flipperzero_unofficial
|
||||
about: Unofficial Telegram chat
|
||||
- name: Discord
|
||||
url: https://discord.gg/58D6E8BtTU
|
||||
url: https://discord.unleashedflip.com
|
||||
about: Unofficial Discord Community
|
||||
|
||||
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",
|
||||
|
||||
30
.vscode/example/launch.json
vendored
30
.vscode/example/launch.json
vendored
@@ -9,6 +9,10 @@
|
||||
"type": "command",
|
||||
"command": "shellCommand.execute",
|
||||
"args": {
|
||||
"useSingleResult": true,
|
||||
"env": {
|
||||
"PATH": "${workspaceFolder};${env:PATH}"
|
||||
},
|
||||
"command": "./fbt get_blackmagic",
|
||||
"description": "Get Blackmagic device",
|
||||
}
|
||||
@@ -24,15 +28,17 @@
|
||||
"servertype": "openocd",
|
||||
"device": "stlink",
|
||||
"svdFile": "./debug/STM32WB55_CM4.svd",
|
||||
// If you're debugging early in the boot process, before OS scheduler is running,
|
||||
// you have to comment out the following line.
|
||||
"rtos": "FreeRTOS",
|
||||
"configFiles": [
|
||||
"interface/stlink.cfg",
|
||||
"./debug/stm32wbx.cfg",
|
||||
],
|
||||
"postAttachCommands": [
|
||||
// "attach 1",
|
||||
// "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"
|
||||
]
|
||||
@@ -54,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",
|
||||
@@ -71,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",
|
||||
|
||||
8
.vscode/example/settings.json
vendored
8
.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,
|
||||
@@ -22,4 +22,4 @@
|
||||
"SConstruct": "python",
|
||||
"*.fam": "python",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
]
|
||||
}
|
||||
|
||||
48
CHANGELOG.md
48
CHANGELOG.md
@@ -1,16 +1,44 @@
|
||||
### New changes
|
||||
* Picopass plugin fixed (bug with mbedtls lib fixed by hedger in OFW PR 1742)
|
||||
* PR: Fix SubGHz last used settings (PR 70 by derskythe)
|
||||
* PR: Random UID for detect reader mode - changes each time when you exit NFC app and launch it (PR 69 by h4sh5)
|
||||
* OFW PR: Dummy mode (aka dumb mode) (OFW PR 1739 by nminaylov)
|
||||
* OFW PR: picopass se identify (OFW PR 1701 by pcunning)
|
||||
* OFW PR: faploader api extension and lib fixes (OFW PR 1742 by hedger)
|
||||
* 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)
|
||||
* 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
|
||||
|
||||
#### **DFU files no longer included in releases to avoid issues with wrong manual installation of assets - use web updater or microSD update package**
|
||||
#### [🎲 Download latest extra apps pack](https://download-directory.github.io/?url=https://github.com/xMasterX/unleashed-extra-pack/tree/main/apps)
|
||||
|
||||
**Note: To avoid issues prefer installing using web updater or by self update package, all needed assets will be installed**
|
||||
[-> How to install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)
|
||||
|
||||
Self-update package (update from microSD) - `flipper-z-f7-update-(version).zip` or `.tgz` for iOS mobile app
|
||||
[-> Download qFlipper (official link)](https://flipperzero.one/update)
|
||||
|
||||
## Please support development of the project
|
||||
* Boosty: https://boosty.to/mmxdev
|
||||
* Ko-Fi: https://ko-fi.com/masterx
|
||||
* cloudtips (only RU payments accepted): https://pay.cloudtips.ru/p/7b3e9d65
|
||||
* YooMoney (only RU payments accepted): https://yoomoney.ru/fundraise/XA49mgQLPA0.221209
|
||||
* USDT(TRC20): `TSXcitMSnWXUFqiUfEXrTVpVewXy2cYhrs`
|
||||
* BCH: `qquxfyzntuqufy2dx0hrfr4sndp0tucvky4sw8qyu3`
|
||||
* ETH/BSC/ERC20-Tokens: `darkflippers.eth` (or `0xFebF1bBc8229418FF2408C07AF6Afa49152fEc6a`)
|
||||
* BTC: `bc1q0np836jk9jwr4dd7p6qv66d04vamtqkxrecck9`
|
||||
* 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**
|
||||
|
||||
**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
|
||||
|
||||
DFU for update using qFlipper is no longer included in releases to avoid issues with assets - 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 +-------------------------------------------------+)
|
||||
189
ReadMe.md
189
ReadMe.md
@@ -1,118 +1,208 @@
|
||||
<h3 align="center">
|
||||
<a href="https://github.com/Eng1n33r/flipperzero-firmware">
|
||||
<a href="https://github.com/DarkFlippers/unleashed-firmware">
|
||||
<img src="https://user-images.githubusercontent.com/10697207/186202043-26947e28-b1cc-459a-8f20-ffcc7fc0c71c.png" align="center" alt="fzCUSTOM" border="0">
|
||||
</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.gg/flipperzero-unofficial"><img src="https://discordapp.com/api/guilds/937479784148115456/widget.png?style=banner4" alt="Unofficial Discord Community"></a>
|
||||
<a href="https://discord.unleashedflip.com"><img src="https://discordapp.com/api/guilds/937479784148115456/widget.png?style=banner4" alt="Unofficial Discord Community" target="_blank"></a>
|
||||
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
|
||||
## 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
|
||||
* 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
|
||||
|
||||
See changelog in releases for latest updates!
|
||||
Also check the changelog in releases for latest updates!
|
||||
|
||||
### Current modified and new SubGHz protocols list:
|
||||
- HCS101
|
||||
- An-Motors
|
||||
### 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:
|
||||
|
||||
- Keeloq [Not ALL systems supported for decode or emulation yet!] - [Supported manufacturers list](https://0bin.net/paste/VwR2lNJY#WH9vnPgvcp7w6zVKucFCuNREKAcOij8KsJ6vqLfMn3b)
|
||||
- Keeloq: HCS101
|
||||
- Keeloq: AN-Motors
|
||||
- Keeloq: JCM Tech
|
||||
- Keeloq: MHouse
|
||||
- Keeloq: Nice Smilo
|
||||
- Keeloq: DTM Neo
|
||||
- Keeloq: FAAC RC,XT
|
||||
- Keeloq: Mutancode
|
||||
- Keeloq: Normstahl
|
||||
- CAME Atomo
|
||||
- FAAC SLH (Spa) [External seed calculation required]
|
||||
- BFT Mitto [External seed calculation required]
|
||||
- Keeloq [Not ALL systems supported yet!]
|
||||
- Nice Flor S
|
||||
- FAAC SLH (Spa) [External seed calculation required (For info contact me in Discord: Nano#8998)]
|
||||
- BFT Mitto [External seed calculation required (For info contact me in Discord: Nano#8998)]
|
||||
- Security+ v1 & v2
|
||||
- Star Line
|
||||
|
||||
## Support us so we can buy equipment and develop new features
|
||||
* ETH/BSC/ERC20-Tokens: `0xFebF1bBc8229418FF2408C07AF6Afa49152fEc6a`
|
||||
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
|
||||
* 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/Eng1n33r/flipperzero-firmware/pull/54) with some changes by xMasterX
|
||||
- Sub-GHz bruteforce plugin [(by Ganapati & xMasterX)](https://github.com/Eng1n33r/flipperzero-firmware/pull/57)
|
||||
- Sub-GHz playlist plugin [(by darmiel)](https://github.com/Eng1n33r/flipperzero-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)
|
||||
- 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)
|
||||
- USB Keyboard plugin [(by huuck)](https://github.com/huuck/FlipperZeroUSBKeyboard)
|
||||
- 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/ClusterM)
|
||||
- SubGHz -> Detect RAW feature - [(by perspecdev)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/152)
|
||||
- SubGHz -> Save last used config settings - [(by derskythe)](https://github.com/Eng1n33r/flipperzero-firmware/pull/67)
|
||||
- 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/Eng1n33r/flipperzero-firmware/blob/dev/documentation/HowToInstall.md)
|
||||
## [- How to install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)
|
||||
|
||||
## [- How to build firmware](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/HowToBuild.md)
|
||||
## [- How to build firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToBuild.md)
|
||||
|
||||
## [- How to connect external CC1101 module](https://github.com/quen0n/flipperzero-ext-cc1101)
|
||||
|
||||
## [- BadUSB: how to add new keyboard layouts](https://github.com/dummy-decoy/flipperzero_badusb_kl)
|
||||
|
||||
## [- How to change Flipper name](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/CustomFlipperName.md)
|
||||
## [- How to change Flipper name](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/CustomFlipperName.md)
|
||||
|
||||
### **Plugins**
|
||||
|
||||
## [- Configure Sub-GHz Remote App](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/SubGHzRemotePlugin.md)
|
||||
## [- 🎲 Download Extra plugins for Unleashed](https://github.com/xMasterX/unleashed-extra-pack)
|
||||
|
||||
## [- Barcode Generator](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/BarcodeGenerator.md)
|
||||
## [- Configure Sub-GHz Remote App](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemotePlugin.md)
|
||||
|
||||
## [- Multi Converter](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/MultiConverter.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)
|
||||
|
||||
## [- Multi Converter](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/MultiConverter.md)
|
||||
|
||||
## [- WAV Player sample files & how to convert](https://github.com/UberGuidoZ/Flipper/tree/main/Wav_Player#readme)
|
||||
|
||||
## [- 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: [NRF24] plugins](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/NRF24.md)
|
||||
## [- 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)
|
||||
|
||||
@@ -126,28 +216,29 @@ Games:
|
||||
|
||||
## [- Windows: How to Upload .bin to ESP32/ESP8266](https://github.com/SequoiaSan/Guide-How-To-Upload-bin-to-ESP8266-ESP32)
|
||||
|
||||
## [- How to use: [GPIO] SentrySafe plugin](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/SentrySafe.md)
|
||||
## [- How to use: [GPIO] SentrySafe plugin](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SentrySafe.md)
|
||||
|
||||
### **SubGHz**
|
||||
### **Sub-GHz**
|
||||
|
||||
## [- Transmission is blocked? - How to extend SubGHz frequency range](https://github.com/Eng1n33r/flipperzero-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/Eng1n33r/flipperzero-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?
|
||||
## [Awesome Flipper Zero - Github](https://github.com/djsime1/awesome-flipperzero)
|
||||
# Where I can find IR, Sub-GHz, ... files, DBs, and other stuff?
|
||||
## [UberGuidoZ Playground - Large collection of files - Github](https://github.com/UberGuidoZ/Flipper)
|
||||
## [CAME-12bit, NICE-12bit, Linear-10bit, PT-2240 - SubGHz fixed code bruteforce](https://github.com/tobiabocchi/flipperzero-bruteforce)
|
||||
## [Awesome Flipper Zero - Github](https://github.com/djsime1/awesome-flipperzero)
|
||||
## [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>
|
||||
|
||||
# Links
|
||||
|
||||
* Unofficial Discord: [discord.gg/flipperzero-unofficial](https://discord.gg/flipperzero-unofficial)
|
||||
* Unofficial Discord: [discord.unleashedflip.com](https://discord.unleashedflip.com)
|
||||
* Docs by atmanos / How to write your own app (outdated API): [https://flipper.atmanos.com/docs/overview/intro](https://flipper.atmanos.com/docs/overview/intro)
|
||||
|
||||
* Official Docs: [http://docs.flipperzero.one](http://docs.flipperzero.one)
|
||||
@@ -157,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
|
||||
@@ -165,4 +256,4 @@ Games:
|
||||
- `site_scons` - Build helpers
|
||||
- `scripts` - Supplementary scripts and python libraries home
|
||||
|
||||
Also pay attention to `ReadMe.md` files inside of those directories.
|
||||
Also, pay attention to the `ReadMe.md` files inside those directories.
|
||||
|
||||
148
SConstruct
148
SConstruct
@@ -7,7 +7,7 @@
|
||||
# construction of certain targets behind command-line options.
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
from fbt.util import path_as_posix
|
||||
|
||||
DefaultEnvironment(tools=[])
|
||||
|
||||
@@ -15,60 +15,36 @@ EnsurePythonVersion(3, 8)
|
||||
|
||||
# Progress(["OwO\r", "owo\r", "uwu\r", "owo\r"], interval=15)
|
||||
|
||||
|
||||
# This environment is created only for loading options & validating file/dir existence
|
||||
fbt_variables = SConscript("site_scons/commandline.scons")
|
||||
cmd_environment = Environment(tools=[], variables=fbt_variables)
|
||||
Help(fbt_variables.GenerateHelpText(cmd_environment))
|
||||
cmd_environment = Environment(
|
||||
toolpath=["#/scripts/fbt_tools"],
|
||||
tools=[
|
||||
("fbt_help", {"vars": fbt_variables}),
|
||||
],
|
||||
variables=fbt_variables,
|
||||
)
|
||||
|
||||
# Building basic environment - tools, utility methods, cross-compilation
|
||||
# settings, gcc flags for Cortex-M4, basic builders and more
|
||||
coreenv = SConscript(
|
||||
"site_scons/environ.scons",
|
||||
exports={"VAR_ENV": cmd_environment},
|
||||
toolpath=["#/scripts/fbt_tools"],
|
||||
)
|
||||
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=["fbt_dist", "openocd", "blackmagic", "jflash"],
|
||||
OPENOCD_GDB_PIPE=[
|
||||
"|openocd -c 'gdb_port pipe; log_output debug/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}"
|
||||
tools=[
|
||||
"fbt_dist",
|
||||
"fbt_debugopts",
|
||||
"openocd",
|
||||
"blackmagic",
|
||||
"jflash",
|
||||
],
|
||||
GDBOPTS_BASE=[
|
||||
"-ex",
|
||||
"target extended-remote ${GDBREMOTE}",
|
||||
"-ex",
|
||||
"set confirm off",
|
||||
],
|
||||
GDBOPTS_BLACKMAGIC=[
|
||||
"-ex",
|
||||
"monitor swdp_scan",
|
||||
"-ex",
|
||||
"monitor debug_bmp enable",
|
||||
"-ex",
|
||||
"attach 1",
|
||||
"-ex",
|
||||
"set mem inaccessible-by-default off",
|
||||
],
|
||||
GDBPYOPTS=[
|
||||
"-ex",
|
||||
"source debug/FreeRTOS/FreeRTOS.py",
|
||||
"-ex",
|
||||
"source debug/flipperapps.py",
|
||||
"-ex",
|
||||
"source debug/PyCortexMDebug/PyCortexMDebug.py",
|
||||
"-ex",
|
||||
"svd_load ${SVD_FILE}",
|
||||
"-ex",
|
||||
"compare-sections",
|
||||
],
|
||||
JFLASHPROJECT="${ROOT_DIR.abspath}/debug/fw.jflash",
|
||||
ENV=os.environ,
|
||||
UPDATE_BUNDLE_DIR="dist/${DIST_DIR}/f${TARGET_HW}-update-${DIST_SUFFIX}",
|
||||
)
|
||||
|
||||
firmware_env = distenv.AddFwProject(
|
||||
@@ -164,32 +140,49 @@ basic_dist = distenv.DistCommand("fw_dist", distenv["DIST_DEPENDS"])
|
||||
distenv.Default(basic_dist)
|
||||
|
||||
dist_dir = distenv.GetProjetDirName()
|
||||
plugin_dist = [
|
||||
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(plugin_dist, firmware_env["FW_EXTAPPS"]["validators"].values())
|
||||
Alias("plugin_dist", plugin_dist)
|
||||
# distenv.Default(plugin_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()
|
||||
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)
|
||||
|
||||
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
|
||||
copro_dist = distenv.CoproBuilder(
|
||||
distenv.Dir("assets/core2_firmware"),
|
||||
"#/build/core2_firmware.tgz",
|
||||
[],
|
||||
)
|
||||
distenv.AlwaysBuild(copro_dist)
|
||||
distenv.Alias("copro_dist", copro_dist)
|
||||
|
||||
firmware_flash = distenv.AddOpenOCDFlashTarget(firmware_env)
|
||||
@@ -212,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",
|
||||
@@ -219,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)
|
||||
|
||||
@@ -228,16 +236,26 @@ 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
|
||||
distenv.PhonyTarget(
|
||||
"debug_other",
|
||||
"${GDBPYCOM}",
|
||||
GDBPYOPTS='-ex "source debug/PyCortexMDebug/PyCortexMDebug.py" ',
|
||||
GDBOPTS="${GDBOPTS_BASE}",
|
||||
GDBREMOTE="${OPENOCD_GDB_PIPE}",
|
||||
GDBPYOPTS='-ex "source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py" ',
|
||||
)
|
||||
|
||||
distenv.PhonyTarget(
|
||||
"debug_other_blackmagic",
|
||||
"${GDBPYCOM}",
|
||||
GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
|
||||
GDBREMOTE="$${BLACKMAGIC_ADDR}",
|
||||
)
|
||||
|
||||
|
||||
# Just start OpenOCD
|
||||
distenv.PhonyTarget(
|
||||
"openocd",
|
||||
@@ -247,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
|
||||
@@ -294,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
|
||||
@@ -303,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ App(
|
||||
"vibro_test",
|
||||
"keypad_test",
|
||||
"usb_test",
|
||||
"usb_mouse",
|
||||
"uart_echo",
|
||||
"USB_Mouse",
|
||||
"UART_Echo",
|
||||
"display_test",
|
||||
"text_box_test",
|
||||
"file_browser_test",
|
||||
|
||||
@@ -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,15 +2,17 @@
|
||||
|
||||
#include <gui/canvas.h>
|
||||
#include <gui/elements.h>
|
||||
|
||||
#include <lib/toolbox/float_tools.h>
|
||||
#include <m-array.h>
|
||||
#include <m-string.h>
|
||||
#include <furi.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct BtTestParam {
|
||||
const char* label;
|
||||
uint8_t current_value_index;
|
||||
string_t current_value_text;
|
||||
FuriString* current_value_text;
|
||||
uint8_t values_count;
|
||||
BtTestParamChangeCallback change_callback;
|
||||
void* context;
|
||||
@@ -85,7 +87,8 @@ static void bt_test_draw_callback(Canvas* canvas, void* _model) {
|
||||
canvas_draw_str(canvas, 50, param_text_y, "<");
|
||||
}
|
||||
|
||||
canvas_draw_str(canvas, 61, param_text_y, string_get_cstr(param->current_value_text));
|
||||
canvas_draw_str(
|
||||
canvas, 61, param_text_y, furi_string_get_cstr(param->current_value_text));
|
||||
|
||||
if(param->current_value_index < (param->values_count - 1)) {
|
||||
canvas_draw_str(canvas, 113, param_text_y, ">");
|
||||
@@ -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,8 +156,10 @@ static bool bt_test_input_callback(InputEvent* event, void* context) {
|
||||
}
|
||||
|
||||
void bt_test_process_up(BtTest* bt_test) {
|
||||
with_view_model(
|
||||
bt_test->view, (BtTestModel * model) {
|
||||
with_view_model( // -V658
|
||||
bt_test->view,
|
||||
BtTestModel * model,
|
||||
{
|
||||
uint8_t params_on_screen = 3;
|
||||
if(model->position > 0) {
|
||||
model->position--;
|
||||
@@ -168,13 +173,15 @@ void bt_test_process_up(BtTest* bt_test) {
|
||||
model->window_position = model->position - (params_on_screen - 1);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
void bt_test_process_down(BtTest* bt_test) {
|
||||
with_view_model(
|
||||
bt_test->view, (BtTestModel * model) {
|
||||
bt_test->view,
|
||||
BtTestModel * model,
|
||||
{
|
||||
uint8_t params_on_screen = 3;
|
||||
if(model->position < (BtTestParamArray_size(model->params) - 1)) {
|
||||
model->position++;
|
||||
@@ -187,8 +194,8 @@ void bt_test_process_down(BtTest* bt_test) {
|
||||
model->position = 0;
|
||||
model->window_position = 0;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
BtTestParam* bt_test_get_selected_param(BtTestModel* model) {
|
||||
@@ -213,7 +220,9 @@ BtTestParam* bt_test_get_selected_param(BtTestModel* model) {
|
||||
void bt_test_process_left(BtTest* bt_test) {
|
||||
BtTestParam* param;
|
||||
with_view_model(
|
||||
bt_test->view, (BtTestModel * model) {
|
||||
bt_test->view,
|
||||
BtTestModel * model,
|
||||
{
|
||||
param = bt_test_get_selected_param(model);
|
||||
if(param->current_value_index > 0) {
|
||||
param->current_value_index--;
|
||||
@@ -225,8 +234,8 @@ void bt_test_process_left(BtTest* bt_test) {
|
||||
model->packets_num_tx = 0;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
},
|
||||
true);
|
||||
if(param->change_callback) {
|
||||
param->change_callback(param);
|
||||
}
|
||||
@@ -235,7 +244,9 @@ void bt_test_process_left(BtTest* bt_test) {
|
||||
void bt_test_process_right(BtTest* bt_test) {
|
||||
BtTestParam* param;
|
||||
with_view_model(
|
||||
bt_test->view, (BtTestModel * model) {
|
||||
bt_test->view,
|
||||
BtTestModel * model,
|
||||
{
|
||||
param = bt_test_get_selected_param(model);
|
||||
if(param->current_value_index < (param->values_count - 1)) {
|
||||
param->current_value_index++;
|
||||
@@ -247,8 +258,8 @@ void bt_test_process_right(BtTest* bt_test) {
|
||||
model->packets_num_tx = 0;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
},
|
||||
true);
|
||||
if(param->change_callback) {
|
||||
param->change_callback(param);
|
||||
}
|
||||
@@ -257,7 +268,9 @@ void bt_test_process_right(BtTest* bt_test) {
|
||||
void bt_test_process_ok(BtTest* bt_test) {
|
||||
BtTestState state;
|
||||
with_view_model(
|
||||
bt_test->view, (BtTestModel * model) {
|
||||
bt_test->view,
|
||||
BtTestModel * model,
|
||||
{
|
||||
if(model->state == BtTestStateStarted) {
|
||||
model->state = BtTestStateStopped;
|
||||
model->message = BT_TEST_START_MESSAGE;
|
||||
@@ -269,8 +282,8 @@ void bt_test_process_ok(BtTest* bt_test) {
|
||||
model->message = BT_TEST_STOP_MESSAGE;
|
||||
}
|
||||
state = model->state;
|
||||
return true;
|
||||
});
|
||||
},
|
||||
true);
|
||||
if(bt_test->change_state_callback) {
|
||||
bt_test->change_state_callback(state, bt_test->context);
|
||||
}
|
||||
@@ -278,13 +291,15 @@ void bt_test_process_ok(BtTest* bt_test) {
|
||||
|
||||
void bt_test_process_back(BtTest* bt_test) {
|
||||
with_view_model(
|
||||
bt_test->view, (BtTestModel * model) {
|
||||
bt_test->view,
|
||||
BtTestModel * model,
|
||||
{
|
||||
model->state = BtTestStateStopped;
|
||||
model->rssi = 0.0f;
|
||||
model->packets_num_rx = 0;
|
||||
model->packets_num_tx = 0;
|
||||
return false;
|
||||
});
|
||||
},
|
||||
false);
|
||||
if(bt_test->back_callback) {
|
||||
bt_test->back_callback(bt_test->context);
|
||||
}
|
||||
@@ -299,7 +314,9 @@ BtTest* bt_test_alloc() {
|
||||
view_set_input_callback(bt_test->view, bt_test_input_callback);
|
||||
|
||||
with_view_model(
|
||||
bt_test->view, (BtTestModel * model) {
|
||||
bt_test->view,
|
||||
BtTestModel * model,
|
||||
{
|
||||
model->state = BtTestStateStopped;
|
||||
model->message = "Ok - Start";
|
||||
BtTestParamArray_init(model->params);
|
||||
@@ -308,8 +325,8 @@ BtTest* bt_test_alloc() {
|
||||
model->rssi = 0.0f;
|
||||
model->packets_num_tx = 0;
|
||||
model->packets_num_rx = 0;
|
||||
return true;
|
||||
});
|
||||
},
|
||||
true);
|
||||
|
||||
return bt_test;
|
||||
}
|
||||
@@ -318,15 +335,17 @@ void bt_test_free(BtTest* bt_test) {
|
||||
furi_assert(bt_test);
|
||||
|
||||
with_view_model(
|
||||
bt_test->view, (BtTestModel * model) {
|
||||
bt_test->view,
|
||||
BtTestModel * model,
|
||||
{
|
||||
BtTestParamArray_it_t it;
|
||||
for(BtTestParamArray_it(it, model->params); !BtTestParamArray_end_p(it);
|
||||
BtTestParamArray_next(it)) {
|
||||
string_clear(BtTestParamArray_ref(it)->current_value_text);
|
||||
furi_string_free(BtTestParamArray_ref(it)->current_value_text);
|
||||
}
|
||||
BtTestParamArray_clear(model->params);
|
||||
return false;
|
||||
});
|
||||
},
|
||||
false);
|
||||
view_free(bt_test->view);
|
||||
free(bt_test);
|
||||
}
|
||||
@@ -347,16 +366,18 @@ BtTestParam* bt_test_param_add(
|
||||
furi_assert(bt_test);
|
||||
|
||||
with_view_model(
|
||||
bt_test->view, (BtTestModel * model) {
|
||||
bt_test->view,
|
||||
BtTestModel * model,
|
||||
{
|
||||
param = BtTestParamArray_push_new(model->params);
|
||||
param->label = label;
|
||||
param->values_count = values_count;
|
||||
param->change_callback = change_callback;
|
||||
param->context = context;
|
||||
param->current_value_index = 0;
|
||||
string_init(param->current_value_text);
|
||||
return true;
|
||||
});
|
||||
param->current_value_text = furi_string_alloc();
|
||||
},
|
||||
true);
|
||||
|
||||
return param;
|
||||
}
|
||||
@@ -364,28 +385,19 @@ BtTestParam* bt_test_param_add(
|
||||
void bt_test_set_rssi(BtTest* bt_test, float rssi) {
|
||||
furi_assert(bt_test);
|
||||
with_view_model(
|
||||
bt_test->view, (BtTestModel * model) {
|
||||
model->rssi = rssi;
|
||||
return true;
|
||||
});
|
||||
bt_test->view, BtTestModel * model, { model->rssi = rssi; }, true);
|
||||
}
|
||||
|
||||
void bt_test_set_packets_tx(BtTest* bt_test, uint32_t packets_num) {
|
||||
furi_assert(bt_test);
|
||||
with_view_model(
|
||||
bt_test->view, (BtTestModel * model) {
|
||||
model->packets_num_tx = packets_num;
|
||||
return true;
|
||||
});
|
||||
bt_test->view, BtTestModel * model, { model->packets_num_tx = packets_num; }, true);
|
||||
}
|
||||
|
||||
void bt_test_set_packets_rx(BtTest* bt_test, uint32_t packets_num) {
|
||||
furi_assert(bt_test);
|
||||
with_view_model(
|
||||
bt_test->view, (BtTestModel * model) {
|
||||
model->packets_num_rx = packets_num;
|
||||
return true;
|
||||
});
|
||||
bt_test->view, BtTestModel * model, { model->packets_num_rx = packets_num; }, true);
|
||||
}
|
||||
|
||||
void bt_test_set_change_state_callback(BtTest* bt_test, BtTestChangeStateCallback callback) {
|
||||
@@ -410,7 +422,7 @@ void bt_test_set_current_value_index(BtTestParam* param, uint8_t current_value_i
|
||||
}
|
||||
|
||||
void bt_test_set_current_value_text(BtTestParam* param, const char* current_value_text) {
|
||||
string_set_str(param->current_value_text, current_value_text);
|
||||
furi_string_set(param->current_value_text, current_value_text);
|
||||
}
|
||||
|
||||
uint8_t bt_test_get_current_value_index(BtTestParam* param) {
|
||||
|
||||
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) {
|
||||
@@ -113,11 +112,11 @@ static void display_config_set_regulation_ratio(VariableItem* item) {
|
||||
static void display_config_set_contrast(VariableItem* item) {
|
||||
DisplayTest* instance = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
string_t temp;
|
||||
string_init(temp);
|
||||
string_cat_printf(temp, "%d", index);
|
||||
variable_item_set_current_value_text(item, string_get_cstr(temp));
|
||||
string_clear(temp);
|
||||
FuriString* temp;
|
||||
temp = furi_string_alloc();
|
||||
furi_string_cat_printf(temp, "%d", index);
|
||||
variable_item_set_current_value_text(item, furi_string_get_cstr(temp));
|
||||
furi_string_free(temp);
|
||||
instance->config_contrast = index;
|
||||
display_test_reload_config(instance);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -110,7 +110,9 @@ static bool view_display_test_input_callback(InputEvent* event, void* context) {
|
||||
bool consumed = false;
|
||||
if(event->type == InputTypeShort || event->type == InputTypeRepeat) {
|
||||
with_view_model(
|
||||
instance->view, (ViewDisplayTestModel * model) {
|
||||
instance->view,
|
||||
ViewDisplayTestModel * model,
|
||||
{
|
||||
if(event->key == InputKeyLeft && model->test > 0) {
|
||||
model->test--;
|
||||
consumed = true;
|
||||
@@ -129,8 +131,8 @@ static bool view_display_test_input_callback(InputEvent* event, void* context) {
|
||||
model->flip_flop = !model->flip_flop;
|
||||
consumed = true;
|
||||
}
|
||||
return consumed;
|
||||
});
|
||||
},
|
||||
consumed);
|
||||
}
|
||||
|
||||
return consumed;
|
||||
@@ -149,10 +151,7 @@ static void view_display_test_exit(void* context) {
|
||||
static void view_display_test_timer_callback(void* context) {
|
||||
ViewDisplayTest* instance = context;
|
||||
with_view_model(
|
||||
instance->view, (ViewDisplayTestModel * model) {
|
||||
model->counter++;
|
||||
return true;
|
||||
});
|
||||
instance->view, ViewDisplayTestModel * model, { model->counter++; }, true);
|
||||
}
|
||||
|
||||
ViewDisplayTest* view_display_test_alloc() {
|
||||
|
||||
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,11 +1,11 @@
|
||||
#include "assets_icons.h"
|
||||
#include "file_browser_app_i.h"
|
||||
#include "gui/modules/file_browser.h"
|
||||
#include "m-string.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);
|
||||
@@ -47,9 +47,9 @@ FileBrowserApp* file_browser_app_alloc(char* arg) {
|
||||
|
||||
app->widget = widget_alloc();
|
||||
|
||||
string_init(app->file_path);
|
||||
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));
|
||||
@@ -84,7 +84,7 @@ void file_browser_app_free(FileBrowserApp* app) {
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
|
||||
string_clear(app->file_path);
|
||||
furi_string_free(app->file_path);
|
||||
|
||||
free(app);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ struct FileBrowserApp {
|
||||
Widget* widget;
|
||||
FileBrowser* file_browser;
|
||||
|
||||
string_t file_path;
|
||||
FuriString* file_path;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
|
||||
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 |
@@ -1,8 +1,5 @@
|
||||
#include "../file_browser_app_i.h"
|
||||
#include <core/check.h>
|
||||
#include <core/log.h>
|
||||
#include "furi_hal.h"
|
||||
#include "m-string.h"
|
||||
#include <furi.h>
|
||||
|
||||
#define DEFAULT_PATH "/"
|
||||
#define EXTENSION "*"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "../file_browser_app_i.h"
|
||||
#include "furi_hal.h"
|
||||
#include "m-string.h"
|
||||
#include <furi.h>
|
||||
|
||||
void file_browser_scene_result_ok_callback(InputType type, void* context) {
|
||||
furi_assert(context);
|
||||
@@ -24,7 +23,13 @@ void file_browser_scene_result_on_enter(void* context) {
|
||||
FileBrowserApp* app = context;
|
||||
|
||||
widget_add_string_multiline_element(
|
||||
app->widget, 64, 10, AlignCenter, AlignTop, FontSecondary, string_get_cstr(app->file_path));
|
||||
app->widget,
|
||||
64,
|
||||
10,
|
||||
AlignCenter,
|
||||
AlignTop,
|
||||
FontSecondary,
|
||||
furi_string_get_cstr(app->file_path));
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, FileBrowserAppViewResult);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ bool file_browser_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
string_set_str(app->file_path, ANY_PATH("badusb/demo_windows.txt"));
|
||||
furi_string_set(app->file_path, ANY_PATH("badusb/demo_windows.txt"));
|
||||
scene_manager_next_scene(app->scene_manager, FileBrowserSceneBrowser);
|
||||
consumed = true;
|
||||
} else if(event.type == SceneManagerEventTypeTick) {
|
||||
|
||||
@@ -2,6 +2,7 @@ App(
|
||||
appid="lfrfid_debug",
|
||||
name="LF-RFID Debug",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
targets=["f7"],
|
||||
entry_point="lfrfid_debug_app",
|
||||
requires=[
|
||||
"gui",
|
||||
|
||||
@@ -52,23 +52,29 @@ static void lfrfid_debug_view_tune_draw_callback(Canvas* canvas, void* _model) {
|
||||
|
||||
static void lfrfid_debug_view_tune_button_up(LfRfidTuneView* tune_view) {
|
||||
with_view_model(
|
||||
tune_view->view, (LfRfidTuneViewModel * model) {
|
||||
tune_view->view,
|
||||
LfRfidTuneViewModel * model,
|
||||
{
|
||||
if(model->pos > 0) model->pos--;
|
||||
return true;
|
||||
});
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
static void lfrfid_debug_view_tune_button_down(LfRfidTuneView* tune_view) {
|
||||
with_view_model(
|
||||
tune_view->view, (LfRfidTuneViewModel * model) {
|
||||
tune_view->view,
|
||||
LfRfidTuneViewModel * model,
|
||||
{
|
||||
if(model->pos < 1) model->pos++;
|
||||
return true;
|
||||
});
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
static void lfrfid_debug_view_tune_button_left(LfRfidTuneView* tune_view) {
|
||||
with_view_model(
|
||||
tune_view->view, (LfRfidTuneViewModel * model) {
|
||||
tune_view->view,
|
||||
LfRfidTuneViewModel * model,
|
||||
{
|
||||
if(model->pos == 0) {
|
||||
if(model->fine) {
|
||||
model->ARR -= 1;
|
||||
@@ -84,13 +90,15 @@ static void lfrfid_debug_view_tune_button_left(LfRfidTuneView* tune_view) {
|
||||
}
|
||||
|
||||
model->dirty = true;
|
||||
return true;
|
||||
});
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
static void lfrfid_debug_view_tune_button_right(LfRfidTuneView* tune_view) {
|
||||
with_view_model(
|
||||
tune_view->view, (LfRfidTuneViewModel * model) {
|
||||
tune_view->view,
|
||||
LfRfidTuneViewModel * model,
|
||||
{
|
||||
if(model->pos == 0) {
|
||||
if(model->fine) {
|
||||
model->ARR += 1;
|
||||
@@ -106,16 +114,13 @@ static void lfrfid_debug_view_tune_button_right(LfRfidTuneView* tune_view) {
|
||||
}
|
||||
|
||||
model->dirty = true;
|
||||
return true;
|
||||
});
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
static void lfrfid_debug_view_tune_button_ok(LfRfidTuneView* tune_view) {
|
||||
with_view_model(
|
||||
tune_view->view, (LfRfidTuneViewModel * model) {
|
||||
model->fine = !model->fine;
|
||||
return true;
|
||||
});
|
||||
tune_view->view, LfRfidTuneViewModel * model, { model->fine = !model->fine; }, true);
|
||||
}
|
||||
|
||||
static bool lfrfid_debug_view_tune_input_callback(InputEvent* event, void* context) {
|
||||
@@ -158,14 +163,16 @@ LfRfidTuneView* lfrfid_debug_view_tune_alloc() {
|
||||
view_allocate_model(tune_view->view, ViewModelTypeLocking, sizeof(LfRfidTuneViewModel));
|
||||
|
||||
with_view_model(
|
||||
tune_view->view, (LfRfidTuneViewModel * model) {
|
||||
tune_view->view,
|
||||
LfRfidTuneViewModel * model,
|
||||
{
|
||||
model->dirty = true;
|
||||
model->fine = false;
|
||||
model->ARR = 511;
|
||||
model->CCR = 255;
|
||||
model->pos = 0;
|
||||
return true;
|
||||
});
|
||||
},
|
||||
true);
|
||||
|
||||
view_set_draw_callback(tune_view->view, lfrfid_debug_view_tune_draw_callback);
|
||||
view_set_input_callback(tune_view->view, lfrfid_debug_view_tune_input_callback);
|
||||
@@ -184,24 +191,28 @@ View* lfrfid_debug_view_tune_get_view(LfRfidTuneView* tune_view) {
|
||||
|
||||
void lfrfid_debug_view_tune_clean(LfRfidTuneView* tune_view) {
|
||||
with_view_model(
|
||||
tune_view->view, (LfRfidTuneViewModel * model) {
|
||||
tune_view->view,
|
||||
LfRfidTuneViewModel * model,
|
||||
{
|
||||
model->dirty = true;
|
||||
model->fine = false;
|
||||
model->ARR = 511;
|
||||
model->CCR = 255;
|
||||
model->pos = 0;
|
||||
return true;
|
||||
});
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
bool lfrfid_debug_view_tune_is_dirty(LfRfidTuneView* tune_view) {
|
||||
bool result = false;
|
||||
with_view_model(
|
||||
tune_view->view, (LfRfidTuneViewModel * model) {
|
||||
tune_view->view,
|
||||
LfRfidTuneViewModel * model,
|
||||
{
|
||||
result = model->dirty;
|
||||
model->dirty = false;
|
||||
return false;
|
||||
});
|
||||
},
|
||||
false);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -209,10 +220,7 @@ bool lfrfid_debug_view_tune_is_dirty(LfRfidTuneView* tune_view) {
|
||||
uint32_t lfrfid_debug_view_tune_get_arr(LfRfidTuneView* tune_view) {
|
||||
uint32_t result = false;
|
||||
with_view_model(
|
||||
tune_view->view, (LfRfidTuneViewModel * model) {
|
||||
result = model->ARR;
|
||||
return false;
|
||||
});
|
||||
tune_view->view, LfRfidTuneViewModel * model, { result = model->ARR; }, false);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -220,10 +228,7 @@ uint32_t lfrfid_debug_view_tune_get_arr(LfRfidTuneView* tune_view) {
|
||||
uint32_t lfrfid_debug_view_tune_get_ccr(LfRfidTuneView* tune_view) {
|
||||
uint32_t result = false;
|
||||
with_view_model(
|
||||
tune_view->view, (LfRfidTuneViewModel * model) {
|
||||
result = model->CCR;
|
||||
return false;
|
||||
});
|
||||
tune_view->view, LfRfidTuneViewModel * model, { result = model->CCR; }, false);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
App(
|
||||
appid="uart_echo",
|
||||
appid="UART_Echo",
|
||||
name="UART Echo",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="uart_echo_app",
|
||||
@@ -7,6 +7,6 @@ App(
|
||||
requires=["gui"],
|
||||
stack_size=2 * 1024,
|
||||
order=70,
|
||||
fap_icon="../../../assets/icons/Archive/keyboard_10px.png",
|
||||
fap_category="Misc",
|
||||
fap_icon="uart_10px.png",
|
||||
fap_category="GPIO",
|
||||
)
|
||||
|
||||
BIN
applications/debug/uart_echo/uart_10px.png
Normal file
BIN
applications/debug/uart_echo/uart_10px.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
@@ -1,10 +1,8 @@
|
||||
#include <furi.h>
|
||||
#include <m-string.h>
|
||||
#include <gui/gui.h>
|
||||
#include <notification/notification.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <gui/elements.h>
|
||||
#include <stream_buffer.h>
|
||||
#include <furi_hal_uart.h>
|
||||
#include <furi_hal_console.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
@@ -21,11 +19,11 @@ typedef struct {
|
||||
ViewDispatcher* view_dispatcher;
|
||||
View* view;
|
||||
FuriThread* worker_thread;
|
||||
StreamBufferHandle_t rx_stream;
|
||||
FuriStreamBuffer* rx_stream;
|
||||
} UartEchoApp;
|
||||
|
||||
typedef struct {
|
||||
string_t text;
|
||||
FuriString* text;
|
||||
} ListElement;
|
||||
|
||||
struct UartDumpModel {
|
||||
@@ -64,10 +62,11 @@ static void uart_echo_view_draw_callback(Canvas* canvas, void* _model) {
|
||||
canvas,
|
||||
0,
|
||||
(i + 1) * (canvas_current_font_height(canvas) - 1),
|
||||
string_get_cstr(model->list[i]->text));
|
||||
furi_string_get_cstr(model->list[i]->text));
|
||||
|
||||
if(i == model->line) {
|
||||
uint8_t width = canvas_string_width(canvas, string_get_cstr(model->list[i]->text));
|
||||
uint8_t width =
|
||||
canvas_string_width(canvas, furi_string_get_cstr(model->list[i]->text));
|
||||
|
||||
canvas_draw_box(
|
||||
canvas,
|
||||
@@ -92,13 +91,11 @@ static uint32_t uart_echo_exit(void* context) {
|
||||
|
||||
static void uart_echo_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) {
|
||||
furi_assert(context);
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
UartEchoApp* app = context;
|
||||
|
||||
if(ev == UartIrqEventRXNE) {
|
||||
xStreamBufferSendFromISR(app->rx_stream, &data, 1, &xHigherPriorityTaskWoken);
|
||||
furi_stream_buffer_send(app->rx_stream, &data, 1, 0);
|
||||
furi_thread_flags_set(furi_thread_get_id(app->worker_thread), WorkerEventRx);
|
||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +110,7 @@ static void uart_echo_push_to_list(UartDumpModel* model, const char data) {
|
||||
model->escape = true;
|
||||
} else if((data >= ' ' && data <= '~') || (data == '\n' || data == '\r')) {
|
||||
bool new_string_needed = false;
|
||||
if(string_size(model->list[model->line]->text) >= COLUMNS_ON_SCREEN) {
|
||||
if(furi_string_size(model->list[model->line]->text) >= COLUMNS_ON_SCREEN) {
|
||||
new_string_needed = true;
|
||||
} else if((data == '\n' || data == '\r')) {
|
||||
// pack line breaks
|
||||
@@ -132,13 +129,13 @@ static void uart_echo_push_to_list(UartDumpModel* model, const char data) {
|
||||
model->list[i - 1] = model->list[i];
|
||||
}
|
||||
|
||||
string_reset(first->text);
|
||||
furi_string_reset(first->text);
|
||||
model->list[model->line] = first;
|
||||
}
|
||||
}
|
||||
|
||||
if(data != '\n' && data != '\r') {
|
||||
string_push_back(model->list[model->line]->text, data);
|
||||
furi_string_push_back(model->list[model->line]->text, data);
|
||||
}
|
||||
}
|
||||
model->last_char = data;
|
||||
@@ -158,25 +155,24 @@ static int32_t uart_echo_worker(void* context) {
|
||||
size_t length = 0;
|
||||
do {
|
||||
uint8_t data[64];
|
||||
length = xStreamBufferReceive(app->rx_stream, data, 64, 0);
|
||||
length = furi_stream_buffer_receive(app->rx_stream, data, 64, 0);
|
||||
if(length > 0) {
|
||||
furi_hal_uart_tx(FuriHalUartIdUSART1, data, length);
|
||||
with_view_model(
|
||||
app->view, (UartDumpModel * model) {
|
||||
app->view,
|
||||
UartDumpModel * model,
|
||||
{
|
||||
for(size_t i = 0; i < length; i++) {
|
||||
uart_echo_push_to_list(model, data[i]);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
},
|
||||
false);
|
||||
}
|
||||
} while(length > 0);
|
||||
|
||||
notification_message(app->notification, &sequence_notification);
|
||||
with_view_model(
|
||||
app->view, (UartDumpModel * model) {
|
||||
UNUSED(model);
|
||||
return true;
|
||||
});
|
||||
app->view, UartDumpModel * model, { UNUSED(model); }, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,7 +182,7 @@ static int32_t uart_echo_worker(void* context) {
|
||||
static UartEchoApp* uart_echo_app_alloc() {
|
||||
UartEchoApp* app = malloc(sizeof(UartEchoApp));
|
||||
|
||||
app->rx_stream = xStreamBufferCreate(2048, 1);
|
||||
app->rx_stream = furi_stream_buffer_alloc(2048, 1);
|
||||
|
||||
// Gui
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
@@ -203,55 +199,55 @@ static UartEchoApp* uart_echo_app_alloc() {
|
||||
view_set_input_callback(app->view, uart_echo_view_input_callback);
|
||||
view_allocate_model(app->view, ViewModelTypeLocking, sizeof(UartDumpModel));
|
||||
with_view_model(
|
||||
app->view, (UartDumpModel * model) {
|
||||
app->view,
|
||||
UartDumpModel * model,
|
||||
{
|
||||
for(size_t i = 0; i < LINES_ON_SCREEN; i++) {
|
||||
model->line = 0;
|
||||
model->escape = false;
|
||||
model->list[i] = malloc(sizeof(ListElement));
|
||||
string_init(model->list[i]->text);
|
||||
model->list[i]->text = furi_string_alloc();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
},
|
||||
true);
|
||||
|
||||
view_set_previous_callback(app->view, uart_echo_exit);
|
||||
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);
|
||||
|
||||
with_view_model(
|
||||
app->view, (UartDumpModel * model) {
|
||||
app->view,
|
||||
UartDumpModel * model,
|
||||
{
|
||||
for(size_t i = 0; i < LINES_ON_SCREEN; i++) {
|
||||
string_clear(model->list[i]->text);
|
||||
furi_string_free(model->list[i]->text);
|
||||
free(model->list[i]);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
},
|
||||
true);
|
||||
view_free(app->view);
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
|
||||
@@ -260,7 +256,7 @@ static void uart_echo_app_free(UartEchoApp* app) {
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
app->gui = NULL;
|
||||
|
||||
vStreamBufferDelete(app->rx_stream);
|
||||
furi_stream_buffer_free(app->rx_stream);
|
||||
|
||||
// Free rest
|
||||
free(app);
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -58,7 +58,7 @@ static const char* test_data_win = "Filetype: Flipper Format test\r\n"
|
||||
#define ARRAY_W_BSIZE(x) (x), (sizeof(x))
|
||||
|
||||
MU_TEST_1(flipper_format_read_and_update_test, FlipperFormat* flipper_format) {
|
||||
string_t tmpstr;
|
||||
FuriString* tmpstr;
|
||||
uint32_t version;
|
||||
uint32_t uint32_data[COUNT_OF(test_uint_data)];
|
||||
int32_t int32_data[COUNT_OF(test_int_data)];
|
||||
@@ -101,14 +101,14 @@ MU_TEST_1(flipper_format_read_and_update_test, FlipperFormat* flipper_format) {
|
||||
mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format)));
|
||||
|
||||
// read test
|
||||
string_init(tmpstr);
|
||||
tmpstr = furi_string_alloc();
|
||||
|
||||
mu_check(flipper_format_read_header(flipper_format, tmpstr, &version));
|
||||
mu_assert_string_eq(test_filetype, string_get_cstr(tmpstr));
|
||||
mu_assert_string_eq(test_filetype, furi_string_get_cstr(tmpstr));
|
||||
mu_assert_int_eq(test_version, version);
|
||||
|
||||
mu_check(flipper_format_read_string(flipper_format, test_string_key, tmpstr));
|
||||
mu_assert_string_eq(test_string_data, string_get_cstr(tmpstr));
|
||||
mu_assert_string_eq(test_string_data, furi_string_get_cstr(tmpstr));
|
||||
|
||||
mu_check(flipper_format_get_value_count(flipper_format, test_int_key, &count));
|
||||
mu_assert_int_eq(COUNT_OF(test_int_data), count);
|
||||
@@ -133,7 +133,7 @@ MU_TEST_1(flipper_format_read_and_update_test, FlipperFormat* flipper_format) {
|
||||
|
||||
mu_check(!flipper_format_read_string(flipper_format, "Key that doesn't exist", tmpstr));
|
||||
|
||||
string_clear(tmpstr);
|
||||
furi_string_free(tmpstr);
|
||||
|
||||
// update data
|
||||
mu_check(flipper_format_rewind(flipper_format));
|
||||
@@ -155,14 +155,14 @@ MU_TEST_1(flipper_format_read_and_update_test, FlipperFormat* flipper_format) {
|
||||
uint8_t hex_updated_data[COUNT_OF(test_hex_updated_data)];
|
||||
|
||||
mu_check(flipper_format_rewind(flipper_format));
|
||||
string_init(tmpstr);
|
||||
tmpstr = furi_string_alloc();
|
||||
|
||||
mu_check(flipper_format_read_header(flipper_format, tmpstr, &version));
|
||||
mu_assert_string_eq(test_filetype, string_get_cstr(tmpstr));
|
||||
mu_assert_string_eq(test_filetype, furi_string_get_cstr(tmpstr));
|
||||
mu_assert_int_eq(test_version, version);
|
||||
|
||||
mu_check(flipper_format_read_string(flipper_format, test_string_key, tmpstr));
|
||||
mu_assert_string_eq(test_string_updated_data, string_get_cstr(tmpstr));
|
||||
mu_assert_string_eq(test_string_updated_data, furi_string_get_cstr(tmpstr));
|
||||
|
||||
mu_check(flipper_format_get_value_count(flipper_format, test_int_key, &count));
|
||||
mu_assert_int_eq(COUNT_OF(test_int_updated_data), count);
|
||||
@@ -190,7 +190,7 @@ MU_TEST_1(flipper_format_read_and_update_test, FlipperFormat* flipper_format) {
|
||||
|
||||
mu_check(!flipper_format_read_string(flipper_format, "Key that doesn't exist", tmpstr));
|
||||
|
||||
string_clear(tmpstr);
|
||||
furi_string_free(tmpstr);
|
||||
|
||||
// update data
|
||||
mu_check(flipper_format_rewind(flipper_format));
|
||||
@@ -214,14 +214,14 @@ MU_TEST_1(flipper_format_read_and_update_test, FlipperFormat* flipper_format) {
|
||||
uint8_t hex_new_data[COUNT_OF(test_hex_new_data)];
|
||||
|
||||
mu_check(flipper_format_rewind(flipper_format));
|
||||
string_init(tmpstr);
|
||||
tmpstr = furi_string_alloc();
|
||||
|
||||
mu_check(flipper_format_read_header(flipper_format, tmpstr, &version));
|
||||
mu_assert_string_eq(test_filetype, string_get_cstr(tmpstr));
|
||||
mu_assert_string_eq(test_filetype, furi_string_get_cstr(tmpstr));
|
||||
mu_assert_int_eq(test_version, version);
|
||||
|
||||
mu_check(flipper_format_read_string(flipper_format, test_string_key, tmpstr));
|
||||
mu_assert_string_eq(test_string_updated_2_data, string_get_cstr(tmpstr));
|
||||
mu_assert_string_eq(test_string_updated_2_data, furi_string_get_cstr(tmpstr));
|
||||
|
||||
mu_check(flipper_format_get_value_count(flipper_format, test_int_key, &count));
|
||||
mu_assert_int_eq(COUNT_OF(test_int_updated_2_data), count);
|
||||
@@ -255,7 +255,7 @@ MU_TEST_1(flipper_format_read_and_update_test, FlipperFormat* flipper_format) {
|
||||
|
||||
mu_check(!flipper_format_read_string(flipper_format, "Key that doesn't exist", tmpstr));
|
||||
|
||||
string_clear(tmpstr);
|
||||
furi_string_free(tmpstr);
|
||||
|
||||
// delete key test
|
||||
mu_check(flipper_format_rewind(flipper_format));
|
||||
|
||||
@@ -57,6 +57,23 @@ static const char* test_data_win = "Filetype: Flipper File test\r\n"
|
||||
"Hex data: DE AD BE";
|
||||
|
||||
#define READ_TEST_FLP "ff_flp.test"
|
||||
#define READ_TEST_ODD "ff_oddities.test"
|
||||
static const char* test_data_odd = "Filetype: Flipper File test\n"
|
||||
// Tabs before newline
|
||||
"Version: 666\t\t\n"
|
||||
"# This is comment\n"
|
||||
// Windows newline in a UNIX file
|
||||
"String data: String\r\n"
|
||||
// Trailing whitespace
|
||||
"Int32 data: 1234 -6345 7813 0 \n"
|
||||
// Extra whitespace
|
||||
"Uint32 data: 1234 0 5678 9098 7654321 \n"
|
||||
// Mixed whitespace
|
||||
"Float data: 1.5\t \t1000.0\n"
|
||||
// Leading tabs after key
|
||||
"Bool data:\t\ttrue false\n"
|
||||
// Mixed trailing whitespace
|
||||
"Hex data: DE AD BE\t ";
|
||||
|
||||
// data created by user on linux machine
|
||||
static const char* test_file_linux = TEST_DIR READ_TEST_NIX;
|
||||
@@ -64,6 +81,8 @@ static const char* test_file_linux = TEST_DIR READ_TEST_NIX;
|
||||
static const char* test_file_windows = TEST_DIR READ_TEST_WIN;
|
||||
// data created by flipper itself
|
||||
static const char* test_file_flipper = TEST_DIR READ_TEST_FLP;
|
||||
// data containing odd user input
|
||||
static const char* test_file_oddities = TEST_DIR READ_TEST_ODD;
|
||||
|
||||
static bool storage_write_string(const char* path, const char* data) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
@@ -102,8 +121,8 @@ static bool test_read(const char* file_name) {
|
||||
bool result = false;
|
||||
|
||||
FlipperFormat* file = flipper_format_file_alloc(storage);
|
||||
string_t string_value;
|
||||
string_init(string_value);
|
||||
FuriString* string_value;
|
||||
string_value = furi_string_alloc();
|
||||
uint32_t uint32_value;
|
||||
void* scratchpad = malloc(512);
|
||||
|
||||
@@ -111,11 +130,11 @@ static bool test_read(const char* file_name) {
|
||||
if(!flipper_format_file_open_existing(file, file_name)) break;
|
||||
|
||||
if(!flipper_format_read_header(file, string_value, &uint32_value)) break;
|
||||
if(string_cmp_str(string_value, test_filetype) != 0) break;
|
||||
if(furi_string_cmp_str(string_value, test_filetype) != 0) break;
|
||||
if(uint32_value != test_version) break;
|
||||
|
||||
if(!flipper_format_read_string(file, test_string_key, string_value)) break;
|
||||
if(string_cmp_str(string_value, test_string_data) != 0) break;
|
||||
if(furi_string_cmp_str(string_value, test_string_data) != 0) break;
|
||||
|
||||
if(!flipper_format_get_value_count(file, test_int_key, &uint32_value)) break;
|
||||
if(uint32_value != COUNT_OF(test_int_data)) break;
|
||||
@@ -150,7 +169,7 @@ static bool test_read(const char* file_name) {
|
||||
} while(false);
|
||||
|
||||
free(scratchpad);
|
||||
string_clear(string_value);
|
||||
furi_string_free(string_value);
|
||||
|
||||
flipper_format_free(file);
|
||||
|
||||
@@ -164,8 +183,8 @@ static bool test_read_updated(const char* file_name) {
|
||||
bool result = false;
|
||||
|
||||
FlipperFormat* file = flipper_format_file_alloc(storage);
|
||||
string_t string_value;
|
||||
string_init(string_value);
|
||||
FuriString* string_value;
|
||||
string_value = furi_string_alloc();
|
||||
uint32_t uint32_value;
|
||||
void* scratchpad = malloc(512);
|
||||
|
||||
@@ -173,11 +192,11 @@ static bool test_read_updated(const char* file_name) {
|
||||
if(!flipper_format_file_open_existing(file, file_name)) break;
|
||||
|
||||
if(!flipper_format_read_header(file, string_value, &uint32_value)) break;
|
||||
if(string_cmp_str(string_value, test_filetype) != 0) break;
|
||||
if(furi_string_cmp_str(string_value, test_filetype) != 0) break;
|
||||
if(uint32_value != test_version) break;
|
||||
|
||||
if(!flipper_format_read_string(file, test_string_key, string_value)) break;
|
||||
if(string_cmp_str(string_value, test_string_updated_data) != 0) break;
|
||||
if(furi_string_cmp_str(string_value, test_string_updated_data) != 0) break;
|
||||
|
||||
if(!flipper_format_get_value_count(file, test_int_key, &uint32_value)) break;
|
||||
if(uint32_value != COUNT_OF(test_int_updated_data)) break;
|
||||
@@ -228,7 +247,7 @@ static bool test_read_updated(const char* file_name) {
|
||||
} while(false);
|
||||
|
||||
free(scratchpad);
|
||||
string_clear(string_value);
|
||||
furi_string_free(string_value);
|
||||
|
||||
flipper_format_free(file);
|
||||
|
||||
@@ -401,14 +420,14 @@ static bool test_read_multikey(const char* file_name) {
|
||||
bool result = false;
|
||||
FlipperFormat* file = flipper_format_file_alloc(storage);
|
||||
|
||||
string_t string_value;
|
||||
string_init(string_value);
|
||||
FuriString* string_value;
|
||||
string_value = furi_string_alloc();
|
||||
uint32_t uint32_value;
|
||||
|
||||
do {
|
||||
if(!flipper_format_file_open_existing(file, file_name)) break;
|
||||
if(!flipper_format_read_header(file, string_value, &uint32_value)) break;
|
||||
if(string_cmp_str(string_value, test_filetype) != 0) break;
|
||||
if(furi_string_cmp_str(string_value, test_filetype) != 0) break;
|
||||
if(uint32_value != test_version) break;
|
||||
|
||||
bool error = false;
|
||||
@@ -429,7 +448,7 @@ static bool test_read_multikey(const char* file_name) {
|
||||
result = true;
|
||||
} while(false);
|
||||
|
||||
string_clear(string_value);
|
||||
furi_string_free(string_value);
|
||||
|
||||
flipper_format_free(file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
@@ -503,6 +522,12 @@ MU_TEST(flipper_format_multikey_test) {
|
||||
mu_assert(test_read_multikey(TEST_DIR "ff_multiline.test"), "Multikey read test error");
|
||||
}
|
||||
|
||||
MU_TEST(flipper_format_oddities_test) {
|
||||
mu_assert(
|
||||
storage_write_string(test_file_oddities, test_data_odd), "Write test error [Oddities]");
|
||||
mu_assert(test_read(test_file_linux), "Read test error [Oddities]");
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(flipper_format) {
|
||||
tests_setup();
|
||||
MU_RUN_TEST(flipper_format_write_test);
|
||||
@@ -516,6 +541,7 @@ MU_TEST_SUITE(flipper_format) {
|
||||
MU_RUN_TEST(flipper_format_update_2_test);
|
||||
MU_RUN_TEST(flipper_format_update_2_result_test);
|
||||
MU_RUN_TEST(flipper_format_multikey_test);
|
||||
MU_RUN_TEST(flipper_format_oddities_test);
|
||||
tests_teardown();
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
469
applications/debug/unit_tests/furi/furi_string_test.c
Normal file
469
applications/debug/unit_tests/furi/furi_string_test.c
Normal file
@@ -0,0 +1,469 @@
|
||||
#include <furi.h>
|
||||
#include "../minunit.h"
|
||||
|
||||
static void test_setup(void) {
|
||||
}
|
||||
|
||||
static void test_teardown(void) {
|
||||
}
|
||||
|
||||
static FuriString* furi_string_alloc_vprintf_test(const char format[], ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
FuriString* string = furi_string_alloc_vprintf(format, args);
|
||||
va_end(args);
|
||||
return string;
|
||||
}
|
||||
|
||||
MU_TEST(mu_test_furi_string_alloc_free) {
|
||||
FuriString* tmp;
|
||||
FuriString* string;
|
||||
|
||||
// test alloc and free
|
||||
string = furi_string_alloc();
|
||||
mu_check(string != NULL);
|
||||
mu_check(furi_string_empty(string));
|
||||
furi_string_free(string);
|
||||
|
||||
// test furi_string_alloc_set_str and free
|
||||
string = furi_string_alloc_set_str("test");
|
||||
mu_check(string != NULL);
|
||||
mu_check(!furi_string_empty(string));
|
||||
mu_check(furi_string_cmp(string, "test") == 0);
|
||||
furi_string_free(string);
|
||||
|
||||
// test furi_string_alloc_set and free
|
||||
tmp = furi_string_alloc_set("more");
|
||||
string = furi_string_alloc_set(tmp);
|
||||
furi_string_free(tmp);
|
||||
mu_check(string != NULL);
|
||||
mu_check(!furi_string_empty(string));
|
||||
mu_check(furi_string_cmp(string, "more") == 0);
|
||||
furi_string_free(string);
|
||||
|
||||
// test alloc_printf and free
|
||||
string = furi_string_alloc_printf("test %d %s %c 0x%02x", 1, "two", '3', 0x04);
|
||||
mu_check(string != NULL);
|
||||
mu_check(!furi_string_empty(string));
|
||||
mu_check(furi_string_cmp(string, "test 1 two 3 0x04") == 0);
|
||||
furi_string_free(string);
|
||||
|
||||
// test alloc_vprintf and free
|
||||
string = furi_string_alloc_vprintf_test("test %d %s %c 0x%02x", 4, "five", '6', 0x07);
|
||||
mu_check(string != NULL);
|
||||
mu_check(!furi_string_empty(string));
|
||||
mu_check(furi_string_cmp(string, "test 4 five 6 0x07") == 0);
|
||||
furi_string_free(string);
|
||||
|
||||
// test alloc_move and free
|
||||
tmp = furi_string_alloc_set("move");
|
||||
string = furi_string_alloc_move(tmp);
|
||||
mu_check(string != NULL);
|
||||
mu_check(!furi_string_empty(string));
|
||||
mu_check(furi_string_cmp(string, "move") == 0);
|
||||
furi_string_free(string);
|
||||
}
|
||||
|
||||
MU_TEST(mu_test_furi_string_mem) {
|
||||
FuriString* string = furi_string_alloc_set("test");
|
||||
mu_check(string != NULL);
|
||||
mu_check(!furi_string_empty(string));
|
||||
|
||||
// TODO: how to test furi_string_reserve?
|
||||
|
||||
// test furi_string_reset
|
||||
furi_string_reset(string);
|
||||
mu_check(furi_string_empty(string));
|
||||
|
||||
// test furi_string_swap
|
||||
furi_string_set(string, "test");
|
||||
FuriString* swap_string = furi_string_alloc_set("swap");
|
||||
furi_string_swap(string, swap_string);
|
||||
mu_check(furi_string_cmp(string, "swap") == 0);
|
||||
mu_check(furi_string_cmp(swap_string, "test") == 0);
|
||||
furi_string_free(swap_string);
|
||||
|
||||
// test furi_string_move
|
||||
FuriString* move_string = furi_string_alloc_set("move");
|
||||
furi_string_move(string, move_string);
|
||||
mu_check(furi_string_cmp(string, "move") == 0);
|
||||
// move_string is now empty
|
||||
// and tested by leaked memory check at the end of the tests
|
||||
|
||||
furi_string_set(string, "abracadabra");
|
||||
|
||||
// test furi_string_hash
|
||||
mu_assert_int_eq(0xc3bc16d7, furi_string_hash(string));
|
||||
|
||||
// test furi_string_size
|
||||
mu_assert_int_eq(11, furi_string_size(string));
|
||||
|
||||
// test furi_string_empty
|
||||
mu_check(!furi_string_empty(string));
|
||||
furi_string_reset(string);
|
||||
mu_check(furi_string_empty(string));
|
||||
|
||||
furi_string_free(string);
|
||||
}
|
||||
|
||||
MU_TEST(mu_test_furi_string_getters) {
|
||||
FuriString* string = furi_string_alloc_set("test");
|
||||
|
||||
// test furi_string_get_char
|
||||
mu_check(furi_string_get_char(string, 0) == 't');
|
||||
mu_check(furi_string_get_char(string, 1) == 'e');
|
||||
mu_check(furi_string_get_char(string, 2) == 's');
|
||||
mu_check(furi_string_get_char(string, 3) == 't');
|
||||
|
||||
// test furi_string_get_cstr
|
||||
mu_assert_string_eq("test", furi_string_get_cstr(string));
|
||||
furi_string_free(string);
|
||||
}
|
||||
|
||||
static FuriString* furi_string_vprintf_test(FuriString* string, const char format[], ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
furi_string_vprintf(string, format, args);
|
||||
va_end(args);
|
||||
return string;
|
||||
}
|
||||
|
||||
MU_TEST(mu_test_furi_string_setters) {
|
||||
FuriString* tmp;
|
||||
FuriString* string = furi_string_alloc();
|
||||
|
||||
// test furi_string_set_str
|
||||
furi_string_set_str(string, "test");
|
||||
mu_assert_string_eq("test", furi_string_get_cstr(string));
|
||||
|
||||
// test furi_string_set
|
||||
tmp = furi_string_alloc_set("more");
|
||||
furi_string_set(string, tmp);
|
||||
furi_string_free(tmp);
|
||||
mu_assert_string_eq("more", furi_string_get_cstr(string));
|
||||
|
||||
// test furi_string_set_strn
|
||||
furi_string_set_strn(string, "test", 2);
|
||||
mu_assert_string_eq("te", furi_string_get_cstr(string));
|
||||
|
||||
// test furi_string_set_char
|
||||
furi_string_set_char(string, 0, 'a');
|
||||
furi_string_set_char(string, 1, 'b');
|
||||
mu_assert_string_eq("ab", furi_string_get_cstr(string));
|
||||
|
||||
// test furi_string_set_n
|
||||
tmp = furi_string_alloc_set("dodecahedron");
|
||||
furi_string_set_n(string, tmp, 4, 5);
|
||||
furi_string_free(tmp);
|
||||
mu_assert_string_eq("cahed", furi_string_get_cstr(string));
|
||||
|
||||
// test furi_string_printf
|
||||
furi_string_printf(string, "test %d %s %c 0x%02x", 1, "two", '3', 0x04);
|
||||
mu_assert_string_eq("test 1 two 3 0x04", furi_string_get_cstr(string));
|
||||
|
||||
// test furi_string_vprintf
|
||||
furi_string_vprintf_test(string, "test %d %s %c 0x%02x", 4, "five", '6', 0x07);
|
||||
mu_assert_string_eq("test 4 five 6 0x07", furi_string_get_cstr(string));
|
||||
|
||||
furi_string_free(string);
|
||||
}
|
||||
|
||||
static FuriString* furi_string_cat_vprintf_test(FuriString* string, const char format[], ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
furi_string_cat_vprintf(string, format, args);
|
||||
va_end(args);
|
||||
return string;
|
||||
}
|
||||
|
||||
MU_TEST(mu_test_furi_string_appends) {
|
||||
FuriString* tmp;
|
||||
FuriString* string = furi_string_alloc();
|
||||
|
||||
// test furi_string_push_back
|
||||
furi_string_push_back(string, 't');
|
||||
furi_string_push_back(string, 'e');
|
||||
furi_string_push_back(string, 's');
|
||||
furi_string_push_back(string, 't');
|
||||
mu_assert_string_eq("test", furi_string_get_cstr(string));
|
||||
furi_string_push_back(string, '!');
|
||||
mu_assert_string_eq("test!", furi_string_get_cstr(string));
|
||||
|
||||
// test furi_string_cat_str
|
||||
furi_string_cat_str(string, "test");
|
||||
mu_assert_string_eq("test!test", furi_string_get_cstr(string));
|
||||
|
||||
// test furi_string_cat
|
||||
tmp = furi_string_alloc_set("more");
|
||||
furi_string_cat(string, tmp);
|
||||
furi_string_free(tmp);
|
||||
mu_assert_string_eq("test!testmore", furi_string_get_cstr(string));
|
||||
|
||||
// test furi_string_cat_printf
|
||||
furi_string_cat_printf(string, "test %d %s %c 0x%02x", 1, "two", '3', 0x04);
|
||||
mu_assert_string_eq("test!testmoretest 1 two 3 0x04", furi_string_get_cstr(string));
|
||||
|
||||
// test furi_string_cat_vprintf
|
||||
furi_string_cat_vprintf_test(string, "test %d %s %c 0x%02x", 4, "five", '6', 0x07);
|
||||
mu_assert_string_eq(
|
||||
"test!testmoretest 1 two 3 0x04test 4 five 6 0x07", furi_string_get_cstr(string));
|
||||
|
||||
furi_string_free(string);
|
||||
}
|
||||
|
||||
MU_TEST(mu_test_furi_string_compare) {
|
||||
FuriString* string_1 = furi_string_alloc_set("string_1");
|
||||
FuriString* string_2 = furi_string_alloc_set("string_2");
|
||||
|
||||
// test furi_string_cmp
|
||||
mu_assert_int_eq(0, furi_string_cmp(string_1, string_1));
|
||||
mu_assert_int_eq(0, furi_string_cmp(string_2, string_2));
|
||||
mu_assert_int_eq(-1, furi_string_cmp(string_1, string_2));
|
||||
mu_assert_int_eq(1, furi_string_cmp(string_2, string_1));
|
||||
|
||||
// test furi_string_cmp_str
|
||||
mu_assert_int_eq(0, furi_string_cmp_str(string_1, "string_1"));
|
||||
mu_assert_int_eq(0, furi_string_cmp_str(string_2, "string_2"));
|
||||
mu_assert_int_eq(-1, furi_string_cmp_str(string_1, "string_2"));
|
||||
mu_assert_int_eq(1, furi_string_cmp_str(string_2, "string_1"));
|
||||
|
||||
// test furi_string_cmpi
|
||||
furi_string_set(string_1, "string");
|
||||
furi_string_set(string_2, "StrIng");
|
||||
mu_assert_int_eq(0, furi_string_cmpi(string_1, string_1));
|
||||
mu_assert_int_eq(0, furi_string_cmpi(string_2, string_2));
|
||||
mu_assert_int_eq(0, furi_string_cmpi(string_1, string_2));
|
||||
mu_assert_int_eq(0, furi_string_cmpi(string_2, string_1));
|
||||
furi_string_set(string_1, "string_1");
|
||||
furi_string_set(string_2, "StrIng_2");
|
||||
mu_assert_int_eq(32, furi_string_cmp(string_1, string_2));
|
||||
mu_assert_int_eq(-32, furi_string_cmp(string_2, string_1));
|
||||
mu_assert_int_eq(-1, furi_string_cmpi(string_1, string_2));
|
||||
mu_assert_int_eq(1, furi_string_cmpi(string_2, string_1));
|
||||
|
||||
// test furi_string_cmpi_str
|
||||
furi_string_set(string_1, "string");
|
||||
mu_assert_int_eq(0, furi_string_cmp_str(string_1, "string"));
|
||||
mu_assert_int_eq(32, furi_string_cmp_str(string_1, "String"));
|
||||
mu_assert_int_eq(32, furi_string_cmp_str(string_1, "STring"));
|
||||
mu_assert_int_eq(32, furi_string_cmp_str(string_1, "STRing"));
|
||||
mu_assert_int_eq(32, furi_string_cmp_str(string_1, "STRIng"));
|
||||
mu_assert_int_eq(32, furi_string_cmp_str(string_1, "STRINg"));
|
||||
mu_assert_int_eq(32, furi_string_cmp_str(string_1, "STRING"));
|
||||
mu_assert_int_eq(0, furi_string_cmpi_str(string_1, "string"));
|
||||
mu_assert_int_eq(0, furi_string_cmpi_str(string_1, "String"));
|
||||
mu_assert_int_eq(0, furi_string_cmpi_str(string_1, "STring"));
|
||||
mu_assert_int_eq(0, furi_string_cmpi_str(string_1, "STRing"));
|
||||
mu_assert_int_eq(0, furi_string_cmpi_str(string_1, "STRIng"));
|
||||
mu_assert_int_eq(0, furi_string_cmpi_str(string_1, "STRINg"));
|
||||
mu_assert_int_eq(0, furi_string_cmpi_str(string_1, "STRING"));
|
||||
|
||||
furi_string_free(string_1);
|
||||
furi_string_free(string_2);
|
||||
}
|
||||
|
||||
MU_TEST(mu_test_furi_string_search) {
|
||||
// 012345678901234567
|
||||
FuriString* haystack = furi_string_alloc_set("test321test123test");
|
||||
FuriString* needle = furi_string_alloc_set("test");
|
||||
|
||||
// test furi_string_search
|
||||
mu_assert_int_eq(0, furi_string_search(haystack, needle));
|
||||
mu_assert_int_eq(7, furi_string_search(haystack, needle, 1));
|
||||
mu_assert_int_eq(14, furi_string_search(haystack, needle, 8));
|
||||
mu_assert_int_eq(FURI_STRING_FAILURE, furi_string_search(haystack, needle, 15));
|
||||
|
||||
FuriString* tmp = furi_string_alloc_set("testnone");
|
||||
mu_assert_int_eq(FURI_STRING_FAILURE, furi_string_search(haystack, tmp));
|
||||
furi_string_free(tmp);
|
||||
|
||||
// test furi_string_search_str
|
||||
mu_assert_int_eq(0, furi_string_search_str(haystack, "test"));
|
||||
mu_assert_int_eq(7, furi_string_search_str(haystack, "test", 1));
|
||||
mu_assert_int_eq(14, furi_string_search_str(haystack, "test", 8));
|
||||
mu_assert_int_eq(4, furi_string_search_str(haystack, "321"));
|
||||
mu_assert_int_eq(11, furi_string_search_str(haystack, "123"));
|
||||
mu_assert_int_eq(FURI_STRING_FAILURE, furi_string_search_str(haystack, "testnone"));
|
||||
mu_assert_int_eq(FURI_STRING_FAILURE, furi_string_search_str(haystack, "test", 15));
|
||||
|
||||
// test furi_string_search_char
|
||||
mu_assert_int_eq(0, furi_string_search_char(haystack, 't'));
|
||||
mu_assert_int_eq(1, furi_string_search_char(haystack, 'e'));
|
||||
mu_assert_int_eq(2, furi_string_search_char(haystack, 's'));
|
||||
mu_assert_int_eq(3, furi_string_search_char(haystack, 't', 1));
|
||||
mu_assert_int_eq(7, furi_string_search_char(haystack, 't', 4));
|
||||
mu_assert_int_eq(FURI_STRING_FAILURE, furi_string_search_char(haystack, 'x'));
|
||||
|
||||
// test furi_string_search_rchar
|
||||
mu_assert_int_eq(17, furi_string_search_rchar(haystack, 't'));
|
||||
mu_assert_int_eq(15, furi_string_search_rchar(haystack, 'e'));
|
||||
mu_assert_int_eq(16, furi_string_search_rchar(haystack, 's'));
|
||||
mu_assert_int_eq(13, furi_string_search_rchar(haystack, '3'));
|
||||
mu_assert_int_eq(FURI_STRING_FAILURE, furi_string_search_rchar(haystack, '3', 14));
|
||||
mu_assert_int_eq(FURI_STRING_FAILURE, furi_string_search_rchar(haystack, 'x'));
|
||||
|
||||
furi_string_free(haystack);
|
||||
furi_string_free(needle);
|
||||
}
|
||||
|
||||
MU_TEST(mu_test_furi_string_equality) {
|
||||
FuriString* string = furi_string_alloc_set("test");
|
||||
FuriString* string_eq = furi_string_alloc_set("test");
|
||||
FuriString* string_neq = furi_string_alloc_set("test2");
|
||||
|
||||
// test furi_string_equal
|
||||
mu_check(furi_string_equal(string, string_eq));
|
||||
mu_check(!furi_string_equal(string, string_neq));
|
||||
|
||||
// test furi_string_equal_str
|
||||
mu_check(furi_string_equal_str(string, "test"));
|
||||
mu_check(!furi_string_equal_str(string, "test2"));
|
||||
mu_check(furi_string_equal_str(string_neq, "test2"));
|
||||
mu_check(!furi_string_equal_str(string_neq, "test"));
|
||||
|
||||
furi_string_free(string);
|
||||
furi_string_free(string_eq);
|
||||
furi_string_free(string_neq);
|
||||
}
|
||||
|
||||
MU_TEST(mu_test_furi_string_replace) {
|
||||
FuriString* needle = furi_string_alloc_set("test");
|
||||
FuriString* replace = furi_string_alloc_set("replace");
|
||||
FuriString* string = furi_string_alloc_set("test123test");
|
||||
|
||||
// test furi_string_replace_at
|
||||
furi_string_replace_at(string, 4, 3, "!biglongword!");
|
||||
mu_assert_string_eq("test!biglongword!test", furi_string_get_cstr(string));
|
||||
|
||||
// test furi_string_replace
|
||||
mu_assert_int_eq(17, furi_string_replace(string, needle, replace, 1));
|
||||
mu_assert_string_eq("test!biglongword!replace", furi_string_get_cstr(string));
|
||||
mu_assert_int_eq(0, furi_string_replace(string, needle, replace));
|
||||
mu_assert_string_eq("replace!biglongword!replace", furi_string_get_cstr(string));
|
||||
mu_assert_int_eq(FURI_STRING_FAILURE, furi_string_replace(string, needle, replace));
|
||||
mu_assert_string_eq("replace!biglongword!replace", furi_string_get_cstr(string));
|
||||
|
||||
// test furi_string_replace_str
|
||||
mu_assert_int_eq(20, furi_string_replace_str(string, "replace", "test", 1));
|
||||
mu_assert_string_eq("replace!biglongword!test", furi_string_get_cstr(string));
|
||||
mu_assert_int_eq(0, furi_string_replace_str(string, "replace", "test"));
|
||||
mu_assert_string_eq("test!biglongword!test", furi_string_get_cstr(string));
|
||||
mu_assert_int_eq(FURI_STRING_FAILURE, furi_string_replace_str(string, "replace", "test"));
|
||||
mu_assert_string_eq("test!biglongword!test", furi_string_get_cstr(string));
|
||||
|
||||
// test furi_string_replace_all
|
||||
furi_string_replace_all(string, needle, replace);
|
||||
mu_assert_string_eq("replace!biglongword!replace", furi_string_get_cstr(string));
|
||||
|
||||
// test furi_string_replace_all_str
|
||||
furi_string_replace_all_str(string, "replace", "test");
|
||||
mu_assert_string_eq("test!biglongword!test", furi_string_get_cstr(string));
|
||||
|
||||
furi_string_free(string);
|
||||
furi_string_free(needle);
|
||||
furi_string_free(replace);
|
||||
}
|
||||
|
||||
MU_TEST(mu_test_furi_string_start_end) {
|
||||
FuriString* string = furi_string_alloc_set("start_end");
|
||||
FuriString* start = furi_string_alloc_set("start");
|
||||
FuriString* end = furi_string_alloc_set("end");
|
||||
|
||||
// test furi_string_start_with
|
||||
mu_check(furi_string_start_with(string, start));
|
||||
mu_check(!furi_string_start_with(string, end));
|
||||
|
||||
// test furi_string_start_with_str
|
||||
mu_check(furi_string_start_with_str(string, "start"));
|
||||
mu_check(!furi_string_start_with_str(string, "end"));
|
||||
|
||||
// test furi_string_end_with
|
||||
mu_check(furi_string_end_with(string, end));
|
||||
mu_check(!furi_string_end_with(string, start));
|
||||
|
||||
// test furi_string_end_with_str
|
||||
mu_check(furi_string_end_with_str(string, "end"));
|
||||
mu_check(!furi_string_end_with_str(string, "start"));
|
||||
|
||||
furi_string_free(string);
|
||||
furi_string_free(start);
|
||||
furi_string_free(end);
|
||||
}
|
||||
|
||||
MU_TEST(mu_test_furi_string_trim) {
|
||||
FuriString* string = furi_string_alloc_set("biglongstring");
|
||||
|
||||
// test furi_string_left
|
||||
furi_string_left(string, 7);
|
||||
mu_assert_string_eq("biglong", furi_string_get_cstr(string));
|
||||
|
||||
// test furi_string_right
|
||||
furi_string_right(string, 3);
|
||||
mu_assert_string_eq("long", furi_string_get_cstr(string));
|
||||
|
||||
// test furi_string_mid
|
||||
furi_string_mid(string, 1, 2);
|
||||
mu_assert_string_eq("on", furi_string_get_cstr(string));
|
||||
|
||||
// test furi_string_trim
|
||||
furi_string_set(string, " \n\r\tbiglongstring \n\r\t ");
|
||||
furi_string_trim(string);
|
||||
mu_assert_string_eq("biglongstring", furi_string_get_cstr(string));
|
||||
furi_string_set(string, "aaaabaaaabbaaabaaaabbtestaaaaaabbaaabaababaa");
|
||||
furi_string_trim(string, "ab");
|
||||
mu_assert_string_eq("test", furi_string_get_cstr(string));
|
||||
|
||||
furi_string_free(string);
|
||||
}
|
||||
|
||||
MU_TEST(mu_test_furi_string_utf8) {
|
||||
FuriString* utf8_string = furi_string_alloc_set("イルカ");
|
||||
|
||||
// test furi_string_utf8_length
|
||||
mu_assert_int_eq(9, furi_string_size(utf8_string));
|
||||
mu_assert_int_eq(3, furi_string_utf8_length(utf8_string));
|
||||
|
||||
// test furi_string_utf8_decode
|
||||
const uint8_t dolphin_emoji_array[4] = {0xF0, 0x9F, 0x90, 0xAC};
|
||||
FuriStringUTF8State state = FuriStringUTF8StateStarting;
|
||||
FuriStringUnicodeValue value = 0;
|
||||
furi_string_utf8_decode(dolphin_emoji_array[0], &state, &value);
|
||||
mu_assert_int_eq(FuriStringUTF8StateDecoding3, state);
|
||||
furi_string_utf8_decode(dolphin_emoji_array[1], &state, &value);
|
||||
mu_assert_int_eq(FuriStringUTF8StateDecoding2, state);
|
||||
furi_string_utf8_decode(dolphin_emoji_array[2], &state, &value);
|
||||
mu_assert_int_eq(FuriStringUTF8StateDecoding1, state);
|
||||
furi_string_utf8_decode(dolphin_emoji_array[3], &state, &value);
|
||||
mu_assert_int_eq(FuriStringUTF8StateStarting, state);
|
||||
mu_assert_int_eq(0x1F42C, value);
|
||||
|
||||
// test furi_string_utf8_push
|
||||
furi_string_set(utf8_string, "");
|
||||
furi_string_utf8_push(utf8_string, value);
|
||||
mu_assert_string_eq("🐬", furi_string_get_cstr(utf8_string));
|
||||
|
||||
furi_string_free(utf8_string);
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(test_suite) {
|
||||
MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
|
||||
|
||||
MU_RUN_TEST(mu_test_furi_string_alloc_free);
|
||||
MU_RUN_TEST(mu_test_furi_string_mem);
|
||||
MU_RUN_TEST(mu_test_furi_string_getters);
|
||||
MU_RUN_TEST(mu_test_furi_string_setters);
|
||||
MU_RUN_TEST(mu_test_furi_string_appends);
|
||||
MU_RUN_TEST(mu_test_furi_string_compare);
|
||||
MU_RUN_TEST(mu_test_furi_string_search);
|
||||
MU_RUN_TEST(mu_test_furi_string_equality);
|
||||
MU_RUN_TEST(mu_test_furi_string_replace);
|
||||
MU_RUN_TEST(mu_test_furi_string_start_end);
|
||||
MU_RUN_TEST(mu_test_furi_string_trim);
|
||||
MU_RUN_TEST(mu_test_furi_string_utf8);
|
||||
}
|
||||
|
||||
int run_minunit_test_furi_string() {
|
||||
MU_RUN_SUITE(test_suite);
|
||||
|
||||
return MU_EXIT_CODE;
|
||||
}
|
||||
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;
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
typedef struct {
|
||||
InfraredDecoderHandler* decoder_handler;
|
||||
InfraredEncoderHandler* encoder_handler;
|
||||
string_t file_path;
|
||||
FuriString* file_path;
|
||||
FlipperFormat* ff;
|
||||
} InfraredTest;
|
||||
|
||||
@@ -23,7 +23,7 @@ static void infrared_test_alloc() {
|
||||
test->decoder_handler = infrared_alloc_decoder();
|
||||
test->encoder_handler = infrared_alloc_encoder();
|
||||
test->ff = flipper_format_buffered_file_alloc(storage);
|
||||
string_init(test->file_path);
|
||||
test->file_path = furi_string_alloc();
|
||||
}
|
||||
|
||||
static void infrared_test_free() {
|
||||
@@ -31,18 +31,18 @@ static void infrared_test_free() {
|
||||
infrared_free_decoder(test->decoder_handler);
|
||||
infrared_free_encoder(test->encoder_handler);
|
||||
flipper_format_free(test->ff);
|
||||
string_clear(test->file_path);
|
||||
furi_string_free(test->file_path);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
free(test);
|
||||
test = NULL;
|
||||
}
|
||||
|
||||
static bool infrared_test_prepare_file(const char* protocol_name) {
|
||||
string_t file_type;
|
||||
string_init(file_type);
|
||||
FuriString* file_type;
|
||||
file_type = furi_string_alloc();
|
||||
bool success = false;
|
||||
|
||||
string_printf(
|
||||
furi_string_printf(
|
||||
test->file_path,
|
||||
"%s%s%s%s",
|
||||
IR_TEST_FILES_DIR,
|
||||
@@ -52,14 +52,15 @@ static bool infrared_test_prepare_file(const char* protocol_name) {
|
||||
|
||||
do {
|
||||
uint32_t format_version;
|
||||
if(!flipper_format_buffered_file_open_existing(test->ff, string_get_cstr(test->file_path)))
|
||||
if(!flipper_format_buffered_file_open_existing(
|
||||
test->ff, furi_string_get_cstr(test->file_path)))
|
||||
break;
|
||||
if(!flipper_format_read_header(test->ff, file_type, &format_version)) break;
|
||||
if(string_cmp_str(file_type, "IR tests file") || format_version != 1) break;
|
||||
if(furi_string_cmp_str(file_type, "IR tests file") || format_version != 1) break;
|
||||
success = true;
|
||||
} while(false);
|
||||
|
||||
string_clear(file_type);
|
||||
furi_string_free(file_type);
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -68,18 +69,18 @@ static bool infrared_test_load_raw_signal(
|
||||
const char* signal_name,
|
||||
uint32_t** timings,
|
||||
uint32_t* timings_count) {
|
||||
string_t buf;
|
||||
string_init(buf);
|
||||
FuriString* buf;
|
||||
buf = furi_string_alloc();
|
||||
bool success = false;
|
||||
|
||||
do {
|
||||
bool is_name_found = false;
|
||||
for(; !is_name_found && flipper_format_read_string(ff, "name", buf);
|
||||
is_name_found = !string_cmp_str(buf, signal_name))
|
||||
is_name_found = !furi_string_cmp(buf, signal_name))
|
||||
;
|
||||
|
||||
if(!is_name_found) break;
|
||||
if(!flipper_format_read_string(ff, "type", buf) || string_cmp_str(buf, "raw")) break;
|
||||
if(!flipper_format_read_string(ff, "type", buf) || furi_string_cmp_str(buf, "raw")) break;
|
||||
if(!flipper_format_get_value_count(ff, "data", timings_count)) break;
|
||||
if(!*timings_count) break;
|
||||
|
||||
@@ -91,18 +92,18 @@ static bool infrared_test_load_raw_signal(
|
||||
success = true;
|
||||
} while(false);
|
||||
|
||||
string_clear(buf);
|
||||
furi_string_free(buf);
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool infrared_test_read_message(FlipperFormat* ff, InfraredMessage* message) {
|
||||
string_t buf;
|
||||
string_init(buf);
|
||||
FuriString* buf;
|
||||
buf = furi_string_alloc();
|
||||
bool success = false;
|
||||
|
||||
do {
|
||||
if(!flipper_format_read_string(ff, "protocol", buf)) break;
|
||||
message->protocol = infrared_get_protocol_by_name(string_get_cstr(buf));
|
||||
message->protocol = infrared_get_protocol_by_name(furi_string_get_cstr(buf));
|
||||
if(!infrared_is_protocol_valid(message->protocol)) break;
|
||||
if(!flipper_format_read_hex(ff, "address", (uint8_t*)&message->address, sizeof(uint32_t)))
|
||||
break;
|
||||
@@ -112,7 +113,7 @@ static bool infrared_test_read_message(FlipperFormat* ff, InfraredMessage* messa
|
||||
success = true;
|
||||
} while(false);
|
||||
|
||||
string_clear(buf);
|
||||
furi_string_free(buf);
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -121,18 +122,19 @@ static bool infrared_test_load_messages(
|
||||
const char* signal_name,
|
||||
InfraredMessage** messages,
|
||||
uint32_t* messages_count) {
|
||||
string_t buf;
|
||||
string_init(buf);
|
||||
FuriString* buf;
|
||||
buf = furi_string_alloc();
|
||||
bool success = false;
|
||||
|
||||
do {
|
||||
bool is_name_found = false;
|
||||
for(; !is_name_found && flipper_format_read_string(ff, "name", buf);
|
||||
is_name_found = !string_cmp_str(buf, signal_name))
|
||||
is_name_found = !furi_string_cmp(buf, signal_name))
|
||||
;
|
||||
|
||||
if(!is_name_found) break;
|
||||
if(!flipper_format_read_string(ff, "type", buf) || string_cmp_str(buf, "parsed_array"))
|
||||
if(!flipper_format_read_string(ff, "type", buf) ||
|
||||
furi_string_cmp_str(buf, "parsed_array"))
|
||||
break;
|
||||
if(!flipper_format_read_uint32(ff, "count", messages_count, 1)) break;
|
||||
if(!*messages_count) break;
|
||||
@@ -151,7 +153,7 @@ static bool infrared_test_load_messages(
|
||||
success = true;
|
||||
} while(false);
|
||||
|
||||
string_clear(buf);
|
||||
furi_string_free(buf);
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -213,26 +215,26 @@ static void infrared_test_run_encoder(InfraredProtocol protocol, uint32_t test_i
|
||||
InfraredMessage* input_messages;
|
||||
uint32_t input_messages_count;
|
||||
|
||||
string_t buf;
|
||||
string_init(buf);
|
||||
FuriString* buf;
|
||||
buf = furi_string_alloc();
|
||||
|
||||
const char* protocol_name = infrared_get_protocol_name(protocol);
|
||||
mu_assert(infrared_test_prepare_file(protocol_name), "Failed to prepare test file");
|
||||
|
||||
string_printf(buf, "encoder_input%d", test_index);
|
||||
furi_string_printf(buf, "encoder_input%ld", test_index);
|
||||
mu_assert(
|
||||
infrared_test_load_messages(
|
||||
test->ff, string_get_cstr(buf), &input_messages, &input_messages_count),
|
||||
test->ff, furi_string_get_cstr(buf), &input_messages, &input_messages_count),
|
||||
"Failed to load messages from file");
|
||||
|
||||
string_printf(buf, "encoder_expected%d", test_index);
|
||||
furi_string_printf(buf, "encoder_expected%ld", test_index);
|
||||
mu_assert(
|
||||
infrared_test_load_raw_signal(
|
||||
test->ff, string_get_cstr(buf), &expected_timings, &expected_timings_count),
|
||||
test->ff, furi_string_get_cstr(buf), &expected_timings, &expected_timings_count),
|
||||
"Failed to load raw signal from file");
|
||||
|
||||
flipper_format_buffered_file_close(test->ff);
|
||||
string_clear(buf);
|
||||
furi_string_free(buf);
|
||||
|
||||
uint32_t j = 0;
|
||||
timings = malloc(sizeof(uint32_t) * timings_count);
|
||||
@@ -267,22 +269,22 @@ static void infrared_test_run_encoder_decoder(InfraredProtocol protocol, uint32_
|
||||
uint32_t input_messages_count;
|
||||
bool level = false;
|
||||
|
||||
string_t buf;
|
||||
string_init(buf);
|
||||
FuriString* buf;
|
||||
buf = furi_string_alloc();
|
||||
|
||||
timings = malloc(sizeof(uint32_t) * timings_count);
|
||||
|
||||
const char* protocol_name = infrared_get_protocol_name(protocol);
|
||||
mu_assert(infrared_test_prepare_file(protocol_name), "Failed to prepare test file");
|
||||
|
||||
string_printf(buf, "encoder_decoder_input%d", test_index);
|
||||
furi_string_printf(buf, "encoder_decoder_input%ld", test_index);
|
||||
mu_assert(
|
||||
infrared_test_load_messages(
|
||||
test->ff, string_get_cstr(buf), &input_messages, &input_messages_count),
|
||||
test->ff, furi_string_get_cstr(buf), &input_messages, &input_messages_count),
|
||||
"Failed to load messages from file");
|
||||
|
||||
flipper_format_buffered_file_close(test->ff);
|
||||
string_clear(buf);
|
||||
furi_string_free(buf);
|
||||
|
||||
for(uint32_t message_counter = 0; message_counter < input_messages_count; ++message_counter) {
|
||||
const InfraredMessage* message_encoded = &input_messages[message_counter];
|
||||
@@ -327,25 +329,27 @@ static void infrared_test_run_decoder(InfraredProtocol protocol, uint32_t test_i
|
||||
InfraredMessage* messages;
|
||||
uint32_t messages_count;
|
||||
|
||||
string_t buf;
|
||||
string_init(buf);
|
||||
FuriString* buf;
|
||||
buf = furi_string_alloc();
|
||||
|
||||
mu_assert(
|
||||
infrared_test_prepare_file(infrared_get_protocol_name(protocol)),
|
||||
"Failed to prepare test file");
|
||||
|
||||
string_printf(buf, "decoder_input%d", test_index);
|
||||
furi_string_printf(buf, "decoder_input%ld", test_index);
|
||||
mu_assert(
|
||||
infrared_test_load_raw_signal(test->ff, string_get_cstr(buf), &timings, &timings_count),
|
||||
infrared_test_load_raw_signal(
|
||||
test->ff, furi_string_get_cstr(buf), &timings, &timings_count),
|
||||
"Failed to load raw signal from file");
|
||||
|
||||
string_printf(buf, "decoder_expected%d", test_index);
|
||||
furi_string_printf(buf, "decoder_expected%ld", test_index);
|
||||
mu_assert(
|
||||
infrared_test_load_messages(test->ff, string_get_cstr(buf), &messages, &messages_count),
|
||||
infrared_test_load_messages(
|
||||
test->ff, furi_string_get_cstr(buf), &messages, &messages_count),
|
||||
"Failed to load messages from file");
|
||||
|
||||
flipper_format_buffered_file_close(test->ff);
|
||||
string_clear(buf);
|
||||
furi_string_free(buf);
|
||||
|
||||
InfraredMessage message_decoded_check_local;
|
||||
bool level = 0;
|
||||
@@ -420,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) {
|
||||
@@ -485,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);
|
||||
@@ -494,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) {
|
||||
@@ -511,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, \
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
#include <storage/storage.h>
|
||||
#include <lib/flipper_format/flipper_format.h>
|
||||
#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>
|
||||
@@ -15,6 +18,8 @@
|
||||
#define NFC_TEST_RESOURCES_DIR EXT_PATH("unit_tests/nfc/")
|
||||
#define NFC_TEST_SIGNAL_SHORT_FILE "nfc_nfca_signal_short.nfc"
|
||||
#define NFC_TEST_SIGNAL_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;
|
||||
@@ -52,14 +57,15 @@ static bool nfc_test_read_signal_from_file(const char* file_name) {
|
||||
bool success = false;
|
||||
|
||||
FlipperFormat* file = flipper_format_file_alloc(nfc_test->storage);
|
||||
string_t file_type;
|
||||
string_init(file_type);
|
||||
FuriString* file_type;
|
||||
file_type = furi_string_alloc();
|
||||
uint32_t file_version = 0;
|
||||
|
||||
do {
|
||||
if(!flipper_format_file_open_existing(file, file_name)) break;
|
||||
if(!flipper_format_read_header(file, file_type, &file_version)) break;
|
||||
if(string_cmp_str(file_type, nfc_test_file_type) || file_version != nfc_test_file_version)
|
||||
if(furi_string_cmp_str(file_type, nfc_test_file_type) ||
|
||||
file_version != nfc_test_file_version)
|
||||
break;
|
||||
if(!flipper_format_read_uint32(file, "Data length", &nfc_test->test_data_len, 1)) break;
|
||||
if(nfc_test->test_data_len > NFC_TEST_DATA_MAX_LEN) break;
|
||||
@@ -75,7 +81,7 @@ static bool nfc_test_read_signal_from_file(const char* file_name) {
|
||||
success = true;
|
||||
} while(false);
|
||||
|
||||
string_clear(file_type);
|
||||
furi_string_free(file_type);
|
||||
flipper_format_free(file);
|
||||
|
||||
return success;
|
||||
@@ -96,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();
|
||||
@@ -110,7 +119,7 @@ static bool nfc_test_digital_signal_test_encode(
|
||||
// Check timings
|
||||
if(time > encode_max_time) {
|
||||
FURI_LOG_E(
|
||||
TAG, "Encoding time: %d us while accepted value: %d us", time, encode_max_time);
|
||||
TAG, "Encoding time: %ld us while accepted value: %ld us", time, encode_max_time);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -130,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: %d, DUT: %d", 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;
|
||||
}
|
||||
@@ -141,16 +150,16 @@ static bool nfc_test_digital_signal_test_encode(
|
||||
if(sum_diff > timings_sum_tolerance) {
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
"Too big difference in timings sum. Ref: %d, DUT: %d",
|
||||
"Too big difference in timings sum. Ref: %ld, DUT: %ld",
|
||||
ref_timings_sum,
|
||||
dut_timings_sum);
|
||||
break;
|
||||
}
|
||||
|
||||
FURI_LOG_I(TAG, "Encoding time: %d us. Acceptable time: %d us", time, encode_max_time);
|
||||
FURI_LOG_I(TAG, "Encoding time: %ld us. Acceptable time: %ld us", time, encode_max_time);
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"Timings sum difference: %d [1/64MHZ]. Acceptable difference: %d [1/64MHz]",
|
||||
"Timings sum difference: %ld [1/64MHZ]. Acceptable difference: %ld [1/64MHz]",
|
||||
sum_diff,
|
||||
timings_sum_tolerance);
|
||||
success = true;
|
||||
@@ -170,10 +179,349 @@ MU_TEST(nfc_digital_signal_test) {
|
||||
"NFC long digital signal test failed\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(mf_classic_dict_test) {
|
||||
MfClassicDict* instance = NULL;
|
||||
uint64_t key = 0;
|
||||
FuriString* temp_str;
|
||||
temp_str = furi_string_alloc();
|
||||
|
||||
instance = mf_classic_dict_alloc(MfClassicDictTypeUnitTest);
|
||||
mu_assert(instance != NULL, "mf_classic_dict_alloc\r\n");
|
||||
|
||||
mu_assert(
|
||||
mf_classic_dict_get_total_keys(instance) == 0,
|
||||
"mf_classic_dict_get_total_keys == 0 assert failed\r\n");
|
||||
|
||||
furi_string_set(temp_str, "2196FAD8115B");
|
||||
mu_assert(
|
||||
mf_classic_dict_add_key_str(instance, temp_str),
|
||||
"mf_classic_dict_add_key == true assert failed\r\n");
|
||||
|
||||
mu_assert(
|
||||
mf_classic_dict_get_total_keys(instance) == 1,
|
||||
"mf_classic_dict_get_total_keys == 1 assert failed\r\n");
|
||||
|
||||
mu_assert(mf_classic_dict_rewind(instance), "mf_classic_dict_rewind == 1 assert failed\r\n");
|
||||
|
||||
mu_assert(
|
||||
mf_classic_dict_get_key_at_index_str(instance, temp_str, 0),
|
||||
"mf_classic_dict_get_key_at_index_str == true assert failed\r\n");
|
||||
mu_assert(
|
||||
furi_string_cmp(temp_str, "2196FAD8115B") == 0,
|
||||
"string_cmp(temp_str, \"2196FAD8115B\") == 0 assert failed\r\n");
|
||||
|
||||
mu_assert(mf_classic_dict_rewind(instance), "mf_classic_dict_rewind == 1 assert failed\r\n");
|
||||
|
||||
mu_assert(
|
||||
mf_classic_dict_get_key_at_index(instance, &key, 0),
|
||||
"mf_classic_dict_get_key_at_index == true assert failed\r\n");
|
||||
mu_assert(key == 0x2196FAD8115B, "key == 0x2196FAD8115B assert failed\r\n");
|
||||
|
||||
mu_assert(mf_classic_dict_rewind(instance), "mf_classic_dict_rewind == 1 assert failed\r\n");
|
||||
|
||||
mu_assert(
|
||||
mf_classic_dict_delete_index(instance, 0),
|
||||
"mf_classic_dict_delete_index == true assert failed\r\n");
|
||||
|
||||
mf_classic_dict_free(instance);
|
||||
furi_string_free(temp_str);
|
||||
}
|
||||
|
||||
MU_TEST(mf_classic_dict_load_test) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
mu_assert(storage != NULL, "storage != NULL assert failed\r\n");
|
||||
|
||||
// Delete unit test dict file if exists
|
||||
if(storage_file_exists(storage, NFC_TEST_DICT_PATH)) {
|
||||
mu_assert(
|
||||
storage_simply_remove(storage, NFC_TEST_DICT_PATH),
|
||||
"remove == true assert failed\r\n");
|
||||
}
|
||||
|
||||
// Create unit test dict file
|
||||
Stream* file_stream = file_stream_alloc(storage);
|
||||
mu_assert(file_stream != NULL, "file_stream != NULL assert failed\r\n");
|
||||
mu_assert(
|
||||
file_stream_open(file_stream, NFC_TEST_DICT_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS),
|
||||
"file_stream_open == true assert failed\r\n");
|
||||
|
||||
// Write unit test dict file
|
||||
char key_str[] = "a0a1a2a3a4a5";
|
||||
mu_assert(
|
||||
stream_write_cstring(file_stream, key_str) == strlen(key_str),
|
||||
"write == true assert failed\r\n");
|
||||
// Close unit test dict file
|
||||
mu_assert(file_stream_close(file_stream), "file_stream_close == true assert failed\r\n");
|
||||
|
||||
// Load unit test dict file
|
||||
MfClassicDict* instance = NULL;
|
||||
instance = mf_classic_dict_alloc(MfClassicDictTypeUnitTest);
|
||||
mu_assert(instance != NULL, "mf_classic_dict_alloc\r\n");
|
||||
uint32_t total_keys = mf_classic_dict_get_total_keys(instance);
|
||||
mu_assert(total_keys == 1, "total_keys == 1 assert failed\r\n");
|
||||
|
||||
// Read key
|
||||
uint64_t key_ref = 0xa0a1a2a3a4a5;
|
||||
uint64_t key_dut = 0;
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
mu_assert(
|
||||
mf_classic_dict_get_next_key_str(instance, temp_str),
|
||||
"get_next_key_str == true assert failed\r\n");
|
||||
mu_assert(furi_string_cmp_str(temp_str, key_str) == 0, "invalid key loaded\r\n");
|
||||
mu_assert(mf_classic_dict_rewind(instance), "mf_classic_dict_rewind == 1 assert failed\r\n");
|
||||
mu_assert(
|
||||
mf_classic_dict_get_next_key(instance, &key_dut),
|
||||
"get_next_key == true assert failed\r\n");
|
||||
mu_assert(key_dut == key_ref, "invalid key loaded\r\n");
|
||||
furi_string_free(temp_str);
|
||||
mf_classic_dict_free(instance);
|
||||
|
||||
// Check that MfClassicDict added new line to the end of the file
|
||||
mu_assert(
|
||||
file_stream_open(file_stream, NFC_TEST_DICT_PATH, FSAM_READ, FSOM_OPEN_EXISTING),
|
||||
"file_stream_open == true assert failed\r\n");
|
||||
mu_assert(stream_seek(file_stream, -1, StreamOffsetFromEnd), "seek == true assert failed\r\n");
|
||||
uint8_t last_char = 0;
|
||||
mu_assert(stream_read(file_stream, &last_char, 1) == 1, "read == true assert failed\r\n");
|
||||
mu_assert(last_char == '\n', "last_char == '\\n' assert failed\r\n");
|
||||
mu_assert(file_stream_close(file_stream), "file_stream_close == true assert failed\r\n");
|
||||
|
||||
// Delete unit test dict file
|
||||
mu_assert(
|
||||
storage_simply_remove(storage, NFC_TEST_DICT_PATH), "remove == true assert failed\r\n");
|
||||
stream_free(file_stream);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
MU_TEST(nfca_file_test) {
|
||||
NfcDevice* nfc = nfc_device_alloc();
|
||||
mu_assert(nfc != NULL, "nfc_device_data != NULL assert failed\r\n");
|
||||
nfc->format = NfcDeviceSaveFormatUid;
|
||||
|
||||
// Fill the UID, sak, ATQA and type
|
||||
uint8_t uid[7] = {0x04, 0x01, 0x23, 0x45, 0x67, 0x89, 0x00};
|
||||
memcpy(nfc->dev_data.nfc_data.uid, uid, 7);
|
||||
nfc->dev_data.nfc_data.uid_len = 7;
|
||||
|
||||
nfc->dev_data.nfc_data.sak = 0x08;
|
||||
nfc->dev_data.nfc_data.atqa[0] = 0x00;
|
||||
nfc->dev_data.nfc_data.atqa[1] = 0x04;
|
||||
nfc->dev_data.nfc_data.type = FuriHalNfcTypeA;
|
||||
|
||||
// Save the NFC device data to the file
|
||||
mu_assert(
|
||||
nfc_device_save(nfc, NFC_TEST_NFC_DEV_PATH), "nfc_device_save == true assert failed\r\n");
|
||||
nfc_device_free(nfc);
|
||||
|
||||
// Load the NFC device data from the file
|
||||
NfcDevice* nfc_validate = nfc_device_alloc();
|
||||
mu_assert(
|
||||
nfc_device_load(nfc_validate, NFC_TEST_NFC_DEV_PATH, true),
|
||||
"nfc_device_load == true assert failed\r\n");
|
||||
|
||||
// Check the UID, sak, ATQA and type
|
||||
mu_assert(memcmp(nfc_validate->dev_data.nfc_data.uid, uid, 7) == 0, "uid assert failed\r\n");
|
||||
mu_assert(nfc_validate->dev_data.nfc_data.sak == 0x08, "sak == 0x08 assert failed\r\n");
|
||||
mu_assert(
|
||||
nfc_validate->dev_data.nfc_data.atqa[0] == 0x00, "atqa[0] == 0x00 assert failed\r\n");
|
||||
mu_assert(
|
||||
nfc_validate->dev_data.nfc_data.atqa[1] == 0x04, "atqa[1] == 0x04 assert failed\r\n");
|
||||
mu_assert(
|
||||
nfc_validate->dev_data.nfc_data.type == FuriHalNfcTypeA,
|
||||
"type == FuriHalNfcTypeA assert failed\r\n");
|
||||
nfc_device_free(nfc_validate);
|
||||
}
|
||||
|
||||
static void mf_classic_generator_test(uint8_t uid_len, MfClassicType type) {
|
||||
NfcDevice* nfc_dev = nfc_device_alloc();
|
||||
mu_assert(nfc_dev != NULL, "nfc_device_data != NULL assert failed\r\n");
|
||||
nfc_dev->format = NfcDeviceSaveFormatMifareClassic;
|
||||
|
||||
// Create a test file
|
||||
nfc_generate_mf_classic(&nfc_dev->dev_data, uid_len, type);
|
||||
|
||||
// Get the uid from generated MFC
|
||||
uint8_t uid[7] = {0};
|
||||
memcpy(uid, nfc_dev->dev_data.nfc_data.uid, uid_len);
|
||||
uint8_t sak = nfc_dev->dev_data.nfc_data.sak;
|
||||
uint8_t atqa[2] = {};
|
||||
memcpy(atqa, nfc_dev->dev_data.nfc_data.atqa, 2);
|
||||
|
||||
MfClassicData* mf_data = &nfc_dev->dev_data.mf_classic_data;
|
||||
// Check the manufacturer block (should be uid[uid_len] + BCC (for 4byte only) + SAK + ATQA0 + ATQA1 + 0xFF[rest])
|
||||
uint8_t manufacturer_block[16] = {0};
|
||||
memcpy(manufacturer_block, nfc_dev->dev_data.mf_classic_data.block[0].value, 16);
|
||||
mu_assert(
|
||||
memcmp(manufacturer_block, uid, uid_len) == 0,
|
||||
"manufacturer_block uid doesn't match the file\r\n");
|
||||
|
||||
uint8_t position = 0;
|
||||
if(uid_len == 4) {
|
||||
position = uid_len;
|
||||
|
||||
uint8_t bcc = 0;
|
||||
|
||||
for(int i = 0; i < uid_len; i++) {
|
||||
bcc ^= uid[i];
|
||||
}
|
||||
|
||||
mu_assert(manufacturer_block[position] == bcc, "manufacturer_block bcc assert failed\r\n");
|
||||
} else {
|
||||
position = uid_len - 1;
|
||||
}
|
||||
|
||||
mu_assert(manufacturer_block[position + 1] == sak, "manufacturer_block sak assert failed\r\n");
|
||||
|
||||
mu_assert(
|
||||
manufacturer_block[position + 2] == atqa[0], "manufacturer_block atqa0 assert failed\r\n");
|
||||
|
||||
mu_assert(
|
||||
manufacturer_block[position + 3] == atqa[1], "manufacturer_block atqa1 assert failed\r\n");
|
||||
|
||||
for(uint8_t i = position + 4; i < 16; i++) {
|
||||
mu_assert(
|
||||
manufacturer_block[i] == 0xFF, "manufacturer_block[i] == 0xFF assert failed\r\n");
|
||||
}
|
||||
|
||||
// Reference sector trailers (should be 0xFF[6] + 0xFF + 0x07 + 0x80 + 0x69 + 0xFF[6])
|
||||
uint8_t sector_trailer[16] = {
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0x07,
|
||||
0x80,
|
||||
0x69,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xFF};
|
||||
// Reference block data
|
||||
uint8_t block_data[16] = {};
|
||||
memset(block_data, 0xff, sizeof(block_data));
|
||||
uint16_t total_blocks = mf_classic_get_total_block_num(type);
|
||||
for(size_t i = 1; i < total_blocks; i++) {
|
||||
if(mf_classic_is_sector_trailer(i)) {
|
||||
mu_assert(
|
||||
memcmp(mf_data->block[i].value, sector_trailer, 16) == 0,
|
||||
"Failed sector trailer compare");
|
||||
} else {
|
||||
mu_assert(memcmp(mf_data->block[i].value, block_data, 16) == 0, "Failed data compare");
|
||||
}
|
||||
}
|
||||
// Save the NFC device data to the file
|
||||
mu_assert(
|
||||
nfc_device_save(nfc_dev, NFC_TEST_NFC_DEV_PATH),
|
||||
"nfc_device_save == true assert failed\r\n");
|
||||
// Verify that key cache is saved
|
||||
FuriString* key_cache_name = furi_string_alloc();
|
||||
furi_string_set_str(key_cache_name, "/ext/nfc/.cache/");
|
||||
for(size_t i = 0; i < uid_len; i++) {
|
||||
furi_string_cat_printf(key_cache_name, "%02X", uid[i]);
|
||||
}
|
||||
furi_string_cat_printf(key_cache_name, ".keys");
|
||||
mu_assert(
|
||||
storage_common_stat(nfc_dev->storage, furi_string_get_cstr(key_cache_name), NULL) ==
|
||||
FSE_OK,
|
||||
"Key cache file save failed");
|
||||
nfc_device_free(nfc_dev);
|
||||
|
||||
// Load the NFC device data from the file
|
||||
NfcDevice* nfc_validate = nfc_device_alloc();
|
||||
mu_assert(nfc_validate, "Nfc device alloc assert");
|
||||
mu_assert(
|
||||
nfc_device_load(nfc_validate, NFC_TEST_NFC_DEV_PATH, false),
|
||||
"nfc_device_load == true assert failed\r\n");
|
||||
|
||||
// Check the UID, sak, ATQA and type
|
||||
mu_assert(
|
||||
memcmp(nfc_validate->dev_data.nfc_data.uid, uid, uid_len) == 0,
|
||||
"uid compare assert failed\r\n");
|
||||
mu_assert(nfc_validate->dev_data.nfc_data.sak == sak, "sak compare assert failed\r\n");
|
||||
mu_assert(
|
||||
memcmp(nfc_validate->dev_data.nfc_data.atqa, atqa, 2) == 0,
|
||||
"atqa compare assert failed\r\n");
|
||||
mu_assert(
|
||||
nfc_validate->dev_data.nfc_data.type == FuriHalNfcTypeA,
|
||||
"type == FuriHalNfcTypeA assert failed\r\n");
|
||||
|
||||
// Check the manufacturer block
|
||||
mu_assert(
|
||||
memcmp(nfc_validate->dev_data.mf_classic_data.block[0].value, manufacturer_block, 16) == 0,
|
||||
"manufacturer_block assert failed\r\n");
|
||||
// Check other blocks
|
||||
for(size_t i = 1; i < total_blocks; i++) {
|
||||
if(mf_classic_is_sector_trailer(i)) {
|
||||
mu_assert(
|
||||
memcmp(mf_data->block[i].value, sector_trailer, 16) == 0,
|
||||
"Failed sector trailer compare");
|
||||
} else {
|
||||
mu_assert(memcmp(mf_data->block[i].value, block_data, 16) == 0, "Failed data compare");
|
||||
}
|
||||
}
|
||||
nfc_device_free(nfc_validate);
|
||||
|
||||
// Check saved key cache
|
||||
NfcDevice* nfc_keys = nfc_device_alloc();
|
||||
mu_assert(nfc_validate, "Nfc device alloc assert");
|
||||
nfc_keys->dev_data.nfc_data.uid_len = uid_len;
|
||||
memcpy(nfc_keys->dev_data.nfc_data.uid, uid, uid_len);
|
||||
mu_assert(nfc_device_load_key_cache(nfc_keys), "Failed to load key cache");
|
||||
uint8_t total_sec = mf_classic_get_total_sectors_num(type);
|
||||
uint8_t default_key[6] = {};
|
||||
memset(default_key, 0xff, 6);
|
||||
for(size_t i = 0; i < total_sec; i++) {
|
||||
MfClassicSectorTrailer* sec_tr =
|
||||
mf_classic_get_sector_trailer_by_sector(&nfc_keys->dev_data.mf_classic_data, i);
|
||||
mu_assert(memcmp(sec_tr->key_a, default_key, 6) == 0, "Failed key compare");
|
||||
mu_assert(memcmp(sec_tr->key_b, default_key, 6) == 0, "Failed key compare");
|
||||
}
|
||||
|
||||
// Delete key cache file
|
||||
mu_assert(
|
||||
storage_common_remove(nfc_keys->storage, furi_string_get_cstr(key_cache_name)) == FSE_OK,
|
||||
"Failed to remove key cache file");
|
||||
furi_string_free(key_cache_name);
|
||||
nfc_device_free(nfc_keys);
|
||||
}
|
||||
|
||||
MU_TEST(mf_mini_file_test) {
|
||||
mf_classic_generator_test(4, MfClassicTypeMini);
|
||||
}
|
||||
|
||||
MU_TEST(mf_classic_1k_4b_file_test) {
|
||||
mf_classic_generator_test(4, MfClassicType1k);
|
||||
}
|
||||
|
||||
MU_TEST(mf_classic_4k_4b_file_test) {
|
||||
mf_classic_generator_test(4, MfClassicType4k);
|
||||
}
|
||||
|
||||
MU_TEST(mf_classic_1k_7b_file_test) {
|
||||
mf_classic_generator_test(7, MfClassicType1k);
|
||||
}
|
||||
|
||||
MU_TEST(mf_classic_4k_7b_file_test) {
|
||||
mf_classic_generator_test(7, MfClassicType4k);
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(nfc) {
|
||||
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);
|
||||
|
||||
nfc_test_free();
|
||||
}
|
||||
|
||||
62
applications/debug/unit_tests/power/power_test.c
Normal file
62
applications/debug/unit_tests/power/power_test.c
Normal file
@@ -0,0 +1,62 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "../minunit.h"
|
||||
|
||||
static void power_test_deinit(void) {
|
||||
// Try to reset to default charging voltage
|
||||
furi_hal_power_set_battery_charging_voltage(4.208f);
|
||||
}
|
||||
|
||||
MU_TEST(test_power_charge_voltage_exact) {
|
||||
// Power of 16mV charge voltages get applied exactly
|
||||
// (bq25896 charge controller works in 16mV increments)
|
||||
//
|
||||
// This test may need adapted if other charge controllers are used in the future.
|
||||
for(uint16_t charge_mv = 3840; charge_mv <= 4208; charge_mv += 16) {
|
||||
float charge_volt = (float)charge_mv / 1000.0f;
|
||||
furi_hal_power_set_battery_charging_voltage(charge_volt);
|
||||
mu_assert_double_eq(charge_volt, furi_hal_power_get_battery_charging_voltage());
|
||||
}
|
||||
}
|
||||
|
||||
MU_TEST(test_power_charge_voltage_floating_imprecision) {
|
||||
// 4.016f should act as 4.016 V, even with floating point imprecision
|
||||
furi_hal_power_set_battery_charging_voltage(4.016f);
|
||||
mu_assert_double_eq(4.016f, furi_hal_power_get_battery_charging_voltage());
|
||||
}
|
||||
|
||||
MU_TEST(test_power_charge_voltage_inexact) {
|
||||
// Charge voltages that are not power of 16mV get truncated down
|
||||
furi_hal_power_set_battery_charging_voltage(3.841f);
|
||||
mu_assert_double_eq(3.840, furi_hal_power_get_battery_charging_voltage());
|
||||
|
||||
furi_hal_power_set_battery_charging_voltage(3.900f);
|
||||
mu_assert_double_eq(3.888, furi_hal_power_get_battery_charging_voltage());
|
||||
|
||||
furi_hal_power_set_battery_charging_voltage(4.200f);
|
||||
mu_assert_double_eq(4.192, furi_hal_power_get_battery_charging_voltage());
|
||||
}
|
||||
|
||||
MU_TEST(test_power_charge_voltage_invalid_clamped) {
|
||||
// Out-of-range charge voltages get clamped to 3.840 V and 4.208 V
|
||||
furi_hal_power_set_battery_charging_voltage(3.808f);
|
||||
mu_assert_double_eq(3.840, furi_hal_power_get_battery_charging_voltage());
|
||||
|
||||
// NOTE: Intentionally picking a small increment above 4.208 V to reduce the risk of an
|
||||
// unhappy battery if this fails.
|
||||
furi_hal_power_set_battery_charging_voltage(4.240f);
|
||||
mu_assert_double_eq(4.208, furi_hal_power_get_battery_charging_voltage());
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(test_power_suite) {
|
||||
MU_RUN_TEST(test_power_charge_voltage_exact);
|
||||
MU_RUN_TEST(test_power_charge_voltage_floating_imprecision);
|
||||
MU_RUN_TEST(test_power_charge_voltage_inexact);
|
||||
MU_RUN_TEST(test_power_charge_voltage_invalid_clamped);
|
||||
power_test_deinit();
|
||||
}
|
||||
|
||||
int run_minunit_test_power() {
|
||||
MU_RUN_SUITE(test_power_suite);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <furi.h>
|
||||
#include "../minunit.h"
|
||||
#include <stdint.h>
|
||||
#include <stream_buffer.h>
|
||||
#include <pb.h>
|
||||
#include <pb_encode.h>
|
||||
#include <m-list.h>
|
||||
@@ -34,7 +33,7 @@ static uint32_t command_id = 0;
|
||||
|
||||
typedef struct {
|
||||
RpcSession* session;
|
||||
StreamBufferHandle_t output_stream;
|
||||
FuriStreamBuffer* output_stream;
|
||||
SemaphoreHandle_t close_session_semaphore;
|
||||
SemaphoreHandle_t terminate_semaphore;
|
||||
TickType_t timeout;
|
||||
@@ -90,7 +89,7 @@ static void test_rpc_setup(void) {
|
||||
}
|
||||
furi_check(rpc_session[0].session);
|
||||
|
||||
rpc_session[0].output_stream = xStreamBufferCreate(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();
|
||||
@@ -110,7 +109,7 @@ static void test_rpc_setup_second_session(void) {
|
||||
}
|
||||
furi_check(rpc_session[1].session);
|
||||
|
||||
rpc_session[1].output_stream = xStreamBufferCreate(1000, 1);
|
||||
rpc_session[1].output_stream = furi_stream_buffer_alloc(1000, 1);
|
||||
rpc_session_set_send_bytes_callback(rpc_session[1].session, output_bytes_callback);
|
||||
rpc_session[1].close_session_semaphore = xSemaphoreCreateBinary();
|
||||
rpc_session[1].terminate_semaphore = xSemaphoreCreateBinary();
|
||||
@@ -126,7 +125,7 @@ static void test_rpc_teardown(void) {
|
||||
rpc_session_close(rpc_session[0].session);
|
||||
furi_check(xSemaphoreTake(rpc_session[0].terminate_semaphore, portMAX_DELAY));
|
||||
furi_record_close(RECORD_RPC);
|
||||
vStreamBufferDelete(rpc_session[0].output_stream);
|
||||
furi_stream_buffer_free(rpc_session[0].output_stream);
|
||||
vSemaphoreDelete(rpc_session[0].close_session_semaphore);
|
||||
vSemaphoreDelete(rpc_session[0].terminate_semaphore);
|
||||
++command_id;
|
||||
@@ -141,7 +140,7 @@ static void test_rpc_teardown_second_session(void) {
|
||||
xSemaphoreTake(rpc_session[1].terminate_semaphore, 0);
|
||||
rpc_session_close(rpc_session[1].session);
|
||||
furi_check(xSemaphoreTake(rpc_session[1].terminate_semaphore, portMAX_DELAY));
|
||||
vStreamBufferDelete(rpc_session[1].output_stream);
|
||||
furi_stream_buffer_free(rpc_session[1].output_stream);
|
||||
vSemaphoreDelete(rpc_session[1].close_session_semaphore);
|
||||
vSemaphoreDelete(rpc_session[1].terminate_semaphore);
|
||||
++command_id;
|
||||
@@ -268,8 +267,8 @@ static PB_CommandStatus test_rpc_storage_get_file_error(File* file) {
|
||||
static void output_bytes_callback(void* ctx, uint8_t* got_bytes, size_t got_size) {
|
||||
RpcSessionContext* callbacks_context = ctx;
|
||||
|
||||
size_t bytes_sent =
|
||||
xStreamBufferSend(callbacks_context->output_stream, got_bytes, got_size, FuriWaitForever);
|
||||
size_t bytes_sent = furi_stream_buffer_send(
|
||||
callbacks_context->output_stream, got_bytes, got_size, FuriWaitForever);
|
||||
(void)bytes_sent;
|
||||
furi_check(bytes_sent == got_size);
|
||||
}
|
||||
@@ -534,7 +533,8 @@ static bool test_rpc_pb_stream_read(pb_istream_t* istream, pb_byte_t* buf, size_
|
||||
TickType_t now = xTaskGetTickCount();
|
||||
int32_t time_left = session_context->timeout - now;
|
||||
time_left = MAX(time_left, 0);
|
||||
bytes_received = xStreamBufferReceive(session_context->output_stream, buf, count, time_left);
|
||||
bytes_received =
|
||||
furi_stream_buffer_receive(session_context->output_stream, buf, count, time_left);
|
||||
return (count == bytes_received);
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ typedef struct {
|
||||
bool visited;
|
||||
} StorageTestPath;
|
||||
|
||||
DICT_DEF2(StorageTestPathDict, string_t, STRING_OPLIST, StorageTestPath, M_POD_OPLIST)
|
||||
DICT_DEF2(StorageTestPathDict, FuriString*, FURI_STRING_OPLIST, StorageTestPath, M_POD_OPLIST)
|
||||
|
||||
static StorageTestPathDict_t*
|
||||
storage_test_paths_alloc(const StorageTestPathDesc paths[], size_t paths_count) {
|
||||
@@ -83,15 +83,15 @@ static StorageTestPathDict_t*
|
||||
StorageTestPathDict_init(*data);
|
||||
|
||||
for(size_t i = 0; i < paths_count; i++) {
|
||||
string_t key;
|
||||
string_init_set(key, paths[i].path);
|
||||
FuriString* key;
|
||||
key = furi_string_alloc_set(paths[i].path);
|
||||
StorageTestPath value = {
|
||||
.is_dir = paths[i].is_dir,
|
||||
.visited = false,
|
||||
};
|
||||
|
||||
StorageTestPathDict_set_at(*data, key, value);
|
||||
string_clear(key);
|
||||
furi_string_free(key);
|
||||
}
|
||||
|
||||
return data;
|
||||
@@ -102,7 +102,7 @@ static void storage_test_paths_free(StorageTestPathDict_t* data) {
|
||||
free(data);
|
||||
}
|
||||
|
||||
static bool storage_test_paths_mark(StorageTestPathDict_t* data, string_t path, bool is_dir) {
|
||||
static bool storage_test_paths_mark(StorageTestPathDict_t* data, FuriString* path, bool is_dir) {
|
||||
bool found = false;
|
||||
|
||||
StorageTestPath* record = StorageTestPathDict_get(*data, path);
|
||||
@@ -148,27 +148,27 @@ static bool write_file_13DA(Storage* storage, const char* path) {
|
||||
}
|
||||
|
||||
static void storage_dirs_create(Storage* storage, const char* base) {
|
||||
string_t path;
|
||||
string_init(path);
|
||||
FuriString* path;
|
||||
path = furi_string_alloc();
|
||||
|
||||
storage_common_mkdir(storage, base);
|
||||
|
||||
for(size_t i = 0; i < COUNT_OF(storage_test_dirwalk_paths); i++) {
|
||||
string_printf(path, "%s/%s", base, storage_test_dirwalk_paths[i]);
|
||||
storage_common_mkdir(storage, string_get_cstr(path));
|
||||
furi_string_printf(path, "%s/%s", base, storage_test_dirwalk_paths[i]);
|
||||
storage_common_mkdir(storage, furi_string_get_cstr(path));
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < COUNT_OF(storage_test_dirwalk_files); i++) {
|
||||
string_printf(path, "%s/%s", base, storage_test_dirwalk_files[i]);
|
||||
write_file_13DA(storage, string_get_cstr(path));
|
||||
furi_string_printf(path, "%s/%s", base, storage_test_dirwalk_files[i]);
|
||||
write_file_13DA(storage, furi_string_get_cstr(path));
|
||||
}
|
||||
|
||||
string_clear(path);
|
||||
furi_string_free(path);
|
||||
}
|
||||
|
||||
MU_TEST_1(test_dirwalk_full, Storage* storage) {
|
||||
string_t path;
|
||||
string_init(path);
|
||||
FuriString* path;
|
||||
path = furi_string_alloc();
|
||||
FileInfo fileinfo;
|
||||
|
||||
StorageTestPathDict_t* paths =
|
||||
@@ -178,12 +178,12 @@ MU_TEST_1(test_dirwalk_full, Storage* storage) {
|
||||
mu_check(dir_walk_open(dir_walk, EXT_PATH("dirwalk")));
|
||||
|
||||
while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) {
|
||||
string_right(path, strlen(EXT_PATH("dirwalk/")));
|
||||
furi_string_right(path, strlen(EXT_PATH("dirwalk/")));
|
||||
mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY)));
|
||||
}
|
||||
|
||||
dir_walk_free(dir_walk);
|
||||
string_clear(path);
|
||||
furi_string_free(path);
|
||||
|
||||
mu_check(storage_test_paths_check(paths) == false);
|
||||
|
||||
@@ -191,8 +191,8 @@ MU_TEST_1(test_dirwalk_full, Storage* storage) {
|
||||
}
|
||||
|
||||
MU_TEST_1(test_dirwalk_no_recursive, Storage* storage) {
|
||||
string_t path;
|
||||
string_init(path);
|
||||
FuriString* path;
|
||||
path = furi_string_alloc();
|
||||
FileInfo fileinfo;
|
||||
|
||||
StorageTestPathDict_t* paths = storage_test_paths_alloc(
|
||||
@@ -203,12 +203,12 @@ MU_TEST_1(test_dirwalk_no_recursive, Storage* storage) {
|
||||
mu_check(dir_walk_open(dir_walk, EXT_PATH("dirwalk")));
|
||||
|
||||
while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) {
|
||||
string_right(path, strlen(EXT_PATH("dirwalk/")));
|
||||
furi_string_right(path, strlen(EXT_PATH("dirwalk/")));
|
||||
mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY)));
|
||||
}
|
||||
|
||||
dir_walk_free(dir_walk);
|
||||
string_clear(path);
|
||||
furi_string_free(path);
|
||||
|
||||
mu_check(storage_test_paths_check(paths) == false);
|
||||
|
||||
@@ -230,8 +230,8 @@ static bool test_dirwalk_filter_no_folder_ext(const char* name, FileInfo* filein
|
||||
}
|
||||
|
||||
MU_TEST_1(test_dirwalk_filter, Storage* storage) {
|
||||
string_t path;
|
||||
string_init(path);
|
||||
FuriString* path;
|
||||
path = furi_string_alloc();
|
||||
FileInfo fileinfo;
|
||||
|
||||
StorageTestPathDict_t* paths = storage_test_paths_alloc(
|
||||
@@ -242,12 +242,12 @@ MU_TEST_1(test_dirwalk_filter, Storage* storage) {
|
||||
mu_check(dir_walk_open(dir_walk, EXT_PATH("dirwalk")));
|
||||
|
||||
while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) {
|
||||
string_right(path, strlen(EXT_PATH("dirwalk/")));
|
||||
furi_string_right(path, strlen(EXT_PATH("dirwalk/")));
|
||||
mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY)));
|
||||
}
|
||||
|
||||
dir_walk_free(dir_walk);
|
||||
string_clear(path);
|
||||
furi_string_free(path);
|
||||
|
||||
mu_check(storage_test_paths_check(paths) == false);
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -58,7 +55,7 @@ MU_TEST(storage_file_open_lock) {
|
||||
storage_file_close(file);
|
||||
|
||||
// file_locker thread stop
|
||||
mu_check(furi_thread_join(locker_thread) == FuriStatusOk);
|
||||
mu_check(furi_thread_join(locker_thread));
|
||||
furi_thread_free(locker_thread);
|
||||
|
||||
// clean data
|
||||
@@ -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
|
||||
@@ -148,7 +142,7 @@ MU_TEST(storage_dir_open_lock) {
|
||||
storage_dir_close(file);
|
||||
|
||||
// file_locker thread stop
|
||||
mu_check(furi_thread_join(locker_thread) == FuriStatusOk);
|
||||
mu_check(furi_thread_join(locker_thread));
|
||||
furi_thread_free(locker_thread);
|
||||
|
||||
// clean data
|
||||
@@ -211,22 +205,22 @@ static bool check_file_13DA(Storage* storage, const char* path) {
|
||||
}
|
||||
|
||||
static void storage_dir_create(Storage* storage, const char* base) {
|
||||
string_t path;
|
||||
string_init(path);
|
||||
FuriString* path;
|
||||
path = furi_string_alloc();
|
||||
|
||||
storage_common_mkdir(storage, base);
|
||||
|
||||
for(size_t i = 0; i < COUNT_OF(storage_copy_test_paths); i++) {
|
||||
string_printf(path, "%s/%s", base, storage_copy_test_paths[i]);
|
||||
storage_common_mkdir(storage, string_get_cstr(path));
|
||||
furi_string_printf(path, "%s/%s", base, storage_copy_test_paths[i]);
|
||||
storage_common_mkdir(storage, furi_string_get_cstr(path));
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < COUNT_OF(storage_copy_test_files); i++) {
|
||||
string_printf(path, "%s/%s", base, storage_copy_test_files[i]);
|
||||
write_file_13DA(storage, string_get_cstr(path));
|
||||
furi_string_printf(path, "%s/%s", base, storage_copy_test_files[i]);
|
||||
write_file_13DA(storage, furi_string_get_cstr(path));
|
||||
}
|
||||
|
||||
string_clear(path);
|
||||
furi_string_free(path);
|
||||
}
|
||||
|
||||
static void storage_dir_remove(Storage* storage, const char* base) {
|
||||
@@ -235,15 +229,15 @@ static void storage_dir_remove(Storage* storage, const char* base) {
|
||||
|
||||
static bool storage_dir_rename_check(Storage* storage, const char* base) {
|
||||
bool result = false;
|
||||
string_t path;
|
||||
string_init(path);
|
||||
FuriString* path;
|
||||
path = furi_string_alloc();
|
||||
|
||||
result = (storage_common_stat(storage, base, NULL) == FSE_OK);
|
||||
|
||||
if(result) {
|
||||
for(size_t i = 0; i < COUNT_OF(storage_copy_test_paths); i++) {
|
||||
string_printf(path, "%s/%s", base, storage_copy_test_paths[i]);
|
||||
result = (storage_common_stat(storage, string_get_cstr(path), NULL) == FSE_OK);
|
||||
furi_string_printf(path, "%s/%s", base, storage_copy_test_paths[i]);
|
||||
result = (storage_common_stat(storage, furi_string_get_cstr(path), NULL) == FSE_OK);
|
||||
if(!result) {
|
||||
break;
|
||||
}
|
||||
@@ -252,15 +246,15 @@ static bool storage_dir_rename_check(Storage* storage, const char* base) {
|
||||
|
||||
if(result) {
|
||||
for(size_t i = 0; i < COUNT_OF(storage_copy_test_files); i++) {
|
||||
string_printf(path, "%s/%s", base, storage_copy_test_files[i]);
|
||||
result = check_file_13DA(storage, string_get_cstr(path));
|
||||
furi_string_printf(path, "%s/%s", base, storage_copy_test_files[i]);
|
||||
result = check_file_13DA(storage, furi_string_get_cstr(path));
|
||||
if(!result) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string_clear(path);
|
||||
furi_string_free(path);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ static const char* stream_test_right_data =
|
||||
MU_TEST_1(stream_composite_subtest, Stream* stream) {
|
||||
const size_t data_size = 128;
|
||||
uint8_t data[data_size];
|
||||
string_t string_lee;
|
||||
string_init_set(string_lee, "lee");
|
||||
FuriString* string_lee;
|
||||
string_lee = furi_string_alloc_set("lee");
|
||||
|
||||
// test that stream is empty
|
||||
// "" -> ""
|
||||
@@ -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));
|
||||
@@ -267,7 +291,7 @@ MU_TEST_1(stream_composite_subtest, Stream* stream) {
|
||||
mu_assert_int_eq(9, stream_tell(stream));
|
||||
mu_check(stream_eof(stream));
|
||||
|
||||
string_clear(string_lee);
|
||||
furi_string_free(string_lee);
|
||||
}
|
||||
|
||||
MU_TEST(stream_composite_test) {
|
||||
@@ -416,10 +440,10 @@ MU_TEST(stream_buffered_write_after_read_test) {
|
||||
}
|
||||
|
||||
MU_TEST(stream_buffered_large_file_test) {
|
||||
string_t input_data;
|
||||
string_t output_data;
|
||||
string_init(input_data);
|
||||
string_init(output_data);
|
||||
FuriString* input_data;
|
||||
FuriString* output_data;
|
||||
input_data = furi_string_alloc();
|
||||
output_data = furi_string_alloc();
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
@@ -429,7 +453,7 @@ MU_TEST(stream_buffered_large_file_test) {
|
||||
const size_t rep_count = data_size / line_size + 1;
|
||||
|
||||
for(size_t i = 0; i < rep_count; ++i) {
|
||||
string_cat_printf(input_data, "%s\n", stream_test_data);
|
||||
furi_string_cat_printf(input_data, "%s\n", stream_test_data);
|
||||
}
|
||||
|
||||
// write test data to file
|
||||
@@ -437,8 +461,8 @@ MU_TEST(stream_buffered_large_file_test) {
|
||||
mu_check(buffered_file_stream_open(
|
||||
stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
|
||||
mu_assert_int_eq(0, stream_size(stream));
|
||||
mu_assert_int_eq(string_size(input_data), stream_write_string(stream, input_data));
|
||||
mu_assert_int_eq(string_size(input_data), stream_size(stream));
|
||||
mu_assert_int_eq(furi_string_size(input_data), stream_write_string(stream, input_data));
|
||||
mu_assert_int_eq(furi_string_size(input_data), stream_size(stream));
|
||||
|
||||
const size_t substr_start = 8;
|
||||
const size_t substr_len = 11;
|
||||
@@ -475,23 +499,23 @@ MU_TEST(stream_buffered_large_file_test) {
|
||||
|
||||
// read the whole file
|
||||
mu_check(stream_rewind(stream));
|
||||
string_t tmp;
|
||||
string_init(tmp);
|
||||
FuriString* tmp;
|
||||
tmp = furi_string_alloc();
|
||||
while(stream_read_line(stream, tmp)) {
|
||||
string_cat(output_data, tmp);
|
||||
furi_string_cat(output_data, tmp);
|
||||
}
|
||||
string_clear(tmp);
|
||||
furi_string_free(tmp);
|
||||
|
||||
// check against generated data
|
||||
mu_assert_int_eq(string_size(input_data), string_size(output_data));
|
||||
mu_check(string_equal_p(input_data, output_data));
|
||||
mu_assert_int_eq(furi_string_size(input_data), furi_string_size(output_data));
|
||||
mu_check(furi_string_equal(input_data, output_data));
|
||||
mu_check(stream_eof(stream));
|
||||
|
||||
stream_free(stream);
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
string_clear(input_data);
|
||||
string_clear(output_data);
|
||||
furi_string_free(input_data);
|
||||
furi_string_free(output_data);
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(stream_suite) {
|
||||
|
||||
@@ -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 232
|
||||
#define TEST_RANDOM_COUNT_PARSE 329
|
||||
#define TEST_TIMEOUT 10000
|
||||
|
||||
static SubGhzEnvironment* environment_handler;
|
||||
@@ -28,12 +29,12 @@ static void subghz_test_rx_callback(
|
||||
void* context) {
|
||||
UNUSED(receiver);
|
||||
UNUSED(context);
|
||||
string_t text;
|
||||
string_init(text);
|
||||
FuriString* text;
|
||||
text = furi_string_alloc();
|
||||
subghz_protocol_decoder_base_get_string(decoder_base, text);
|
||||
subghz_receiver_reset(receiver_handler);
|
||||
FURI_LOG_T(TAG, "\r\n%s", string_get_cstr(text));
|
||||
string_clear(text);
|
||||
FURI_LOG_T(TAG, "\r\n%s", furi_string_get_cstr(text));
|
||||
furi_string_free(text);
|
||||
subghz_test_decoder_count++;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -141,8 +146,8 @@ static bool subghz_decode_random_test(const char* path) {
|
||||
static bool subghz_encoder_test(const char* path) {
|
||||
subghz_test_decoder_count = 0;
|
||||
uint32_t test_start = furi_get_tick();
|
||||
string_t temp_str;
|
||||
string_init(temp_str);
|
||||
FuriString* temp_str;
|
||||
temp_str = furi_string_alloc();
|
||||
bool file_load = false;
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
@@ -167,11 +172,11 @@ static bool subghz_encoder_test(const char* path) {
|
||||
} while(false);
|
||||
if(file_load) {
|
||||
SubGhzTransmitter* transmitter =
|
||||
subghz_transmitter_alloc_init(environment_handler, string_get_cstr(temp_str));
|
||||
subghz_transmitter_alloc_init(environment_handler, furi_string_get_cstr(temp_str));
|
||||
subghz_transmitter_deserialize(transmitter, fff_data_file);
|
||||
|
||||
SubGhzProtocolDecoderBase* decoder = subghz_receiver_search_decoder_base_by_name(
|
||||
receiver_handler, string_get_cstr(temp_str));
|
||||
receiver_handler, furi_string_get_cstr(temp_str));
|
||||
|
||||
if(decoder) {
|
||||
LevelDuration level_duration;
|
||||
@@ -192,10 +197,11 @@ static bool subghz_encoder_test(const char* path) {
|
||||
flipper_format_free(fff_data_file);
|
||||
FURI_LOG_T(TAG, "\r\n Decoder count parse \033[0;33m%d\033[0m ", subghz_test_decoder_count);
|
||||
if(furi_get_tick() - test_start > TEST_TIMEOUT) {
|
||||
printf("\033[0;31mTest encoder %s ERROR TimeOut\033[0m\r\n", string_get_cstr(temp_str));
|
||||
printf(
|
||||
"\033[0;31mTest encoder %s ERROR TimeOut\033[0m\r\n", furi_string_get_cstr(temp_str));
|
||||
subghz_test_decoder_count = 0;
|
||||
}
|
||||
string_clear(temp_str);
|
||||
furi_string_free(temp_str);
|
||||
|
||||
return subghz_test_decoder_count ? true : false;
|
||||
}
|
||||
@@ -206,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(
|
||||
@@ -340,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(
|
||||
@@ -412,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) {
|
||||
@@ -434,6 +594,57 @@ MU_TEST(subghz_decoder_clemsa_test) {
|
||||
"Test decoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_ansonic_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
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
|
||||
MU_TEST(subghz_encoder_princeton_test) {
|
||||
mu_assert(
|
||||
@@ -477,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")),
|
||||
@@ -537,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) {
|
||||
@@ -555,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");
|
||||
}
|
||||
@@ -563,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);
|
||||
@@ -582,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);
|
||||
@@ -592,9 +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_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);
|
||||
@@ -603,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);
|
||||
@@ -613,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();
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#include "m-string.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
@@ -11,18 +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)();
|
||||
|
||||
@@ -33,18 +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, string_t args, void* context) {
|
||||
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;
|
||||
@@ -91,34 +100,37 @@ void unit_tests_cli(Cli* cli, string_t args, void* context) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(string_size(args)) {
|
||||
if(string_cmp_str(args, unit_tests[i].name) == 0) {
|
||||
failed_tests += unit_tests[i].entry();
|
||||
if(furi_string_size(args)) {
|
||||
if(furi_string_cmp_str(args, unit_tests[i].name) == 0) {
|
||||
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,12 +1,12 @@
|
||||
App(
|
||||
appid="usb_mouse",
|
||||
appid="USB_Mouse",
|
||||
name="USB Mouse",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="usb_mouse_app",
|
||||
cdefines=["APP_USB_MOUSE"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=60,
|
||||
fap_icon="../../plugins/mousejacker/mouse_10px.png",
|
||||
fap_category="Misc",
|
||||
fap_category="Debug",
|
||||
)
|
||||
|
||||
5
applications/examples/application.fam
Normal file
5
applications/examples/application.fam
Normal file
@@ -0,0 +1,5 @@
|
||||
App(
|
||||
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`.
|
||||
10
applications/examples/example_images/application.fam
Normal file
10
applications/examples/example_images/application.fam
Normal file
@@ -0,0 +1,10 @@
|
||||
App(
|
||||
appid="example_images",
|
||||
name="Example: Images",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="example_images_main",
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
fap_category="Examples",
|
||||
fap_icon_assets="images",
|
||||
)
|
||||
81
applications/examples/example_images/example_images.c
Normal file
81
applications/examples/example_images/example_images.c
Normal file
@@ -0,0 +1,81 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <input/input.h>
|
||||
|
||||
/* Magic happens here -- this file is generated by fbt.
|
||||
* Just set fap_icon_assets in application.fam and #include {APPID}_icons.h */
|
||||
#include "example_images_icons.h"
|
||||
|
||||
typedef struct {
|
||||
uint8_t x, y;
|
||||
} ImagePosition;
|
||||
|
||||
static ImagePosition image_position = {.x = 0, .y = 0};
|
||||
|
||||
// Screen is 128x64 px
|
||||
static void app_draw_callback(Canvas* canvas, void* ctx) {
|
||||
UNUSED(ctx);
|
||||
|
||||
canvas_clear(canvas);
|
||||
canvas_draw_icon(canvas, image_position.x % 128, image_position.y % 64, &I_dolphin_71x25);
|
||||
}
|
||||
|
||||
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_images_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 InputKeyLeft:
|
||||
image_position.x -= 2;
|
||||
break;
|
||||
case InputKeyRight:
|
||||
image_position.x += 2;
|
||||
break;
|
||||
case InputKeyUp:
|
||||
image_position.y -= 2;
|
||||
break;
|
||||
case InputKeyDown:
|
||||
image_position.y += 2;
|
||||
break;
|
||||
default:
|
||||
running = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
view_port_update(view_port);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
BIN
applications/examples/example_images/images/dolphin_71x25.png
Normal file
BIN
applications/examples/example_images/images/dolphin_71x25.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
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 |
@@ -15,6 +15,23 @@ App(
|
||||
"archive",
|
||||
"clock",
|
||||
"unirfremix",
|
||||
"spectrum_analyzer",
|
||||
],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="main_apps_default",
|
||||
name="Basic applications for main menu",
|
||||
apptype=FlipperAppType.METAPACKAGE,
|
||||
provides=[
|
||||
"gpio",
|
||||
#"ibutton",
|
||||
"infrared",
|
||||
"lfrfid",
|
||||
"nfc",
|
||||
"subghz",
|
||||
#"bad_usb",
|
||||
#"u2f",
|
||||
"fap_loader",
|
||||
"archive",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -21,7 +21,7 @@ static void archive_tick_event_callback(void* context) {
|
||||
static ArchiveApp* archive_alloc() {
|
||||
ArchiveApp* archive = malloc(sizeof(ArchiveApp));
|
||||
|
||||
string_init(archive->fav_move_str);
|
||||
archive->fav_move_str = furi_string_alloc();
|
||||
|
||||
archive->scene_manager = scene_manager_alloc(&archive_scene_handlers, archive);
|
||||
archive->view_dispatcher = view_dispatcher_alloc();
|
||||
@@ -81,7 +81,7 @@ void archive_free(ArchiveApp* archive) {
|
||||
scene_manager_free(archive->scene_manager);
|
||||
|
||||
browser_free(archive->browser);
|
||||
string_clear(archive->fav_move_str);
|
||||
furi_string_free(archive->fav_move_str);
|
||||
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
archive->dialogs = NULL;
|
||||
|
||||
@@ -36,7 +36,7 @@ struct ArchiveApp {
|
||||
Loading* loading;
|
||||
FuriPubSubSubscription* loader_stop_subscription;
|
||||
|
||||
string_t fav_move_str;
|
||||
FuriString* fav_move_str;
|
||||
char text_store[MAX_NAME_LEN];
|
||||
char file_extension[MAX_EXT_LEN + 1];
|
||||
};
|
||||
|
||||
@@ -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 "m-string.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) {
|
||||
@@ -19,9 +21,11 @@ static void
|
||||
|
||||
if((item_cnt == 0) && (archive_is_home(browser)) && (tab != ArchiveTabBrowser)) {
|
||||
archive_switch_tab(browser, browser->last_tab_switch_dir);
|
||||
} else if(!string_start_with_str_p(browser->path, "/app:")) {
|
||||
} else if(!furi_string_start_with_str(browser->path, "/app:")) {
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
browser->view,
|
||||
ArchiveBrowserViewModel * model,
|
||||
{
|
||||
files_array_reset(model->files);
|
||||
model->item_cnt = item_cnt;
|
||||
model->item_idx = (file_idx > 0) ? file_idx : 0;
|
||||
@@ -31,8 +35,8 @@ static void
|
||||
model->list_offset = 0;
|
||||
model->list_loading = true;
|
||||
model->folder_loading = false;
|
||||
return false;
|
||||
});
|
||||
},
|
||||
false);
|
||||
archive_update_offset(browser);
|
||||
|
||||
file_browser_worker_load(browser->worker, load_offset, FILE_LIST_BUF_LEN);
|
||||
@@ -44,25 +48,38 @@ static void archive_list_load_cb(void* context, uint32_t list_load_offset) {
|
||||
ArchiveBrowserView* browser = (ArchiveBrowserView*)context;
|
||||
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
browser->view,
|
||||
ArchiveBrowserViewModel * model,
|
||||
{
|
||||
files_array_reset(model->files);
|
||||
model->array_offset = list_load_offset;
|
||||
return false;
|
||||
});
|
||||
},
|
||||
false);
|
||||
}
|
||||
|
||||
static void archive_list_item_cb(void* context, string_t 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, string_get_cstr(item_path));
|
||||
archive_add_file_item(browser, is_folder, furi_string_get_cstr(item_path));
|
||||
} else {
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
browser->view,
|
||||
ArchiveBrowserViewModel * model,
|
||||
{
|
||||
if(model->item_cnt <= BROWSER_SORT_THRESHOLD) {
|
||||
files_array_sort(model->files);
|
||||
}
|
||||
model->list_loading = false;
|
||||
return true;
|
||||
});
|
||||
},
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,20 +88,19 @@ static void archive_long_load_cb(void* context) {
|
||||
ArchiveBrowserView* browser = (ArchiveBrowserView*)context;
|
||||
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
model->folder_loading = true;
|
||||
return true;
|
||||
});
|
||||
browser->view, ArchiveBrowserViewModel * model, { model->folder_loading = true; }, true);
|
||||
}
|
||||
|
||||
static void archive_file_browser_set_path(
|
||||
ArchiveBrowserView* browser,
|
||||
string_t path,
|
||||
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);
|
||||
@@ -93,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +129,9 @@ void archive_update_offset(ArchiveBrowserView* browser) {
|
||||
furi_assert(browser);
|
||||
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
browser->view,
|
||||
ArchiveBrowserViewModel * model,
|
||||
{
|
||||
uint16_t bounds = model->item_cnt > 3 ? 2 : model->item_cnt;
|
||||
|
||||
if((model->item_cnt > 3u) && (model->item_idx >= ((int32_t)model->item_cnt - 1))) {
|
||||
@@ -124,33 +143,34 @@ void archive_update_offset(ArchiveBrowserView* browser) {
|
||||
model->list_offset =
|
||||
CLAMP(model->item_idx - 1, (int32_t)model->item_cnt - bounds, 0);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
void archive_update_focus(ArchiveBrowserView* browser, const char* target) {
|
||||
furi_assert(browser);
|
||||
furi_assert(target);
|
||||
|
||||
archive_get_items(browser, string_get_cstr(browser->path));
|
||||
archive_get_items(browser, furi_string_get_cstr(browser->path));
|
||||
|
||||
if(!archive_file_get_array_size(browser) && archive_is_home(browser)) {
|
||||
archive_switch_tab(browser, TAB_RIGHT);
|
||||
archive_switch_tab(browser, TAB_LEFT);
|
||||
} else {
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
browser->view,
|
||||
ArchiveBrowserViewModel * model,
|
||||
{
|
||||
uint16_t idx = 0;
|
||||
while(idx < files_array_size(model->files)) {
|
||||
ArchiveFile_t* current = files_array_get(model->files, idx);
|
||||
if(!string_search(current->path, target)) {
|
||||
if(!furi_string_search(current->path, target)) {
|
||||
model->item_idx = idx + model->array_offset;
|
||||
break;
|
||||
}
|
||||
++idx;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
},
|
||||
false);
|
||||
|
||||
archive_update_offset(browser);
|
||||
}
|
||||
@@ -161,10 +181,10 @@ size_t archive_file_get_array_size(ArchiveBrowserView* browser) {
|
||||
|
||||
uint16_t size = 0;
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
size = files_array_size(model->files);
|
||||
return false;
|
||||
});
|
||||
browser->view,
|
||||
ArchiveBrowserViewModel * model,
|
||||
{ size = files_array_size(model->files); },
|
||||
false);
|
||||
return size;
|
||||
}
|
||||
|
||||
@@ -172,11 +192,13 @@ void archive_set_item_count(ArchiveBrowserView* browser, uint32_t count) {
|
||||
furi_assert(browser);
|
||||
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
browser->view,
|
||||
ArchiveBrowserViewModel * model,
|
||||
{
|
||||
model->item_cnt = count;
|
||||
model->item_idx = CLAMP(model->item_idx, (int32_t)model->item_cnt - 1, 0);
|
||||
return false;
|
||||
});
|
||||
},
|
||||
false);
|
||||
archive_update_offset(browser);
|
||||
}
|
||||
|
||||
@@ -185,7 +207,9 @@ void archive_file_array_rm_selected(ArchiveBrowserView* browser) {
|
||||
uint32_t items_cnt = 0;
|
||||
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
browser->view,
|
||||
ArchiveBrowserViewModel * model,
|
||||
{
|
||||
files_array_remove_v(
|
||||
model->files,
|
||||
model->item_idx - model->array_offset,
|
||||
@@ -193,11 +217,11 @@ void archive_file_array_rm_selected(ArchiveBrowserView* browser) {
|
||||
model->item_cnt--;
|
||||
model->item_idx = CLAMP(model->item_idx, (int32_t)model->item_cnt - 1, 0);
|
||||
items_cnt = model->item_cnt;
|
||||
return false;
|
||||
});
|
||||
},
|
||||
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);
|
||||
@@ -207,7 +231,9 @@ void archive_file_array_swap(ArchiveBrowserView* browser, int8_t dir) {
|
||||
furi_assert(browser);
|
||||
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
browser->view,
|
||||
ArchiveBrowserViewModel * model,
|
||||
{
|
||||
ArchiveFile_t temp;
|
||||
size_t array_size = files_array_size(model->files) - 1;
|
||||
uint8_t swap_idx = CLAMP((size_t)(model->item_idx + dir), array_size, 0u);
|
||||
@@ -225,18 +251,18 @@ void archive_file_array_swap(ArchiveBrowserView* browser, int8_t dir) {
|
||||
} else {
|
||||
files_array_swap_at(model->files, model->item_idx, swap_idx);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
},
|
||||
false);
|
||||
}
|
||||
|
||||
void archive_file_array_rm_all(ArchiveBrowserView* browser) {
|
||||
furi_assert(browser);
|
||||
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
files_array_reset(model->files);
|
||||
return false;
|
||||
});
|
||||
browser->view,
|
||||
ArchiveBrowserViewModel * model,
|
||||
{ files_array_reset(model->files); },
|
||||
false);
|
||||
}
|
||||
|
||||
void archive_file_array_load(ArchiveBrowserView* browser, int8_t dir) {
|
||||
@@ -245,7 +271,9 @@ void archive_file_array_load(ArchiveBrowserView* browser, int8_t dir) {
|
||||
int32_t offset_new = 0;
|
||||
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
browser->view,
|
||||
ArchiveBrowserViewModel * model,
|
||||
{
|
||||
if(model->item_cnt > FILE_LIST_BUF_LEN) {
|
||||
if(dir < 0) {
|
||||
offset_new = model->item_idx - FILE_LIST_BUF_LEN / 4 * 3;
|
||||
@@ -255,14 +283,13 @@ 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;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
},
|
||||
false);
|
||||
|
||||
file_browser_worker_load(browser->worker, offset_new, FILE_LIST_BUF_LEN);
|
||||
}
|
||||
@@ -272,12 +299,14 @@ ArchiveFile_t* archive_get_current_file(ArchiveBrowserView* browser) {
|
||||
|
||||
ArchiveFile_t* selected = NULL;
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
browser->view,
|
||||
ArchiveBrowserViewModel * model,
|
||||
{
|
||||
selected = files_array_size(model->files) ?
|
||||
files_array_get(model->files, model->item_idx - model->array_offset) :
|
||||
NULL;
|
||||
return false;
|
||||
});
|
||||
},
|
||||
false);
|
||||
return selected;
|
||||
}
|
||||
|
||||
@@ -287,11 +316,13 @@ ArchiveFile_t* archive_get_file_at(ArchiveBrowserView* browser, size_t idx) {
|
||||
ArchiveFile_t* selected = NULL;
|
||||
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
browser->view,
|
||||
ArchiveBrowserViewModel * model,
|
||||
{
|
||||
idx = CLAMP(idx - model->array_offset, files_array_size(model->files), 0u);
|
||||
selected = files_array_size(model->files) ? files_array_get(model->files, idx) : NULL;
|
||||
return false;
|
||||
});
|
||||
},
|
||||
false);
|
||||
return selected;
|
||||
}
|
||||
|
||||
@@ -300,10 +331,7 @@ ArchiveTabEnum archive_get_tab(ArchiveBrowserView* browser) {
|
||||
|
||||
ArchiveTabEnum tab_id = 0;
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
tab_id = model->tab_idx;
|
||||
return false;
|
||||
});
|
||||
browser->view, ArchiveBrowserViewModel * model, { tab_id = model->tab_idx; }, false);
|
||||
return tab_id;
|
||||
}
|
||||
|
||||
@@ -315,22 +343,19 @@ bool archive_is_home(ArchiveBrowserView* browser) {
|
||||
}
|
||||
|
||||
const char* default_path = archive_get_default_path(archive_get_tab(browser));
|
||||
return (string_cmp_str(browser->path, default_path) == 0);
|
||||
return (furi_string_cmp_str(browser->path, default_path) == 0);
|
||||
}
|
||||
|
||||
const char* archive_get_name(ArchiveBrowserView* browser) {
|
||||
ArchiveFile_t* selected = archive_get_current_file(browser);
|
||||
return string_get_cstr(selected->path);
|
||||
return furi_string_get_cstr(selected->path);
|
||||
}
|
||||
|
||||
void archive_set_tab(ArchiveBrowserView* browser, ArchiveTabEnum tab) {
|
||||
furi_assert(browser);
|
||||
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
model->tab_idx = tab;
|
||||
return false;
|
||||
});
|
||||
browser->view, ArchiveBrowserViewModel * model, { model->tab_idx = tab; }, false);
|
||||
}
|
||||
|
||||
void archive_add_app_item(ArchiveBrowserView* browser, const char* name) {
|
||||
@@ -339,40 +364,60 @@ void archive_add_app_item(ArchiveBrowserView* browser, const char* name) {
|
||||
|
||||
ArchiveFile_t item;
|
||||
ArchiveFile_t_init(&item);
|
||||
string_set_str(item.path, name);
|
||||
furi_string_set(item.path, name);
|
||||
archive_set_file_type(&item, name, false, true);
|
||||
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
browser->view,
|
||||
ArchiveBrowserViewModel * model,
|
||||
{
|
||||
files_array_push_back(model->files, item);
|
||||
model->item_cnt = files_array_size(model->files);
|
||||
return false;
|
||||
});
|
||||
},
|
||||
false);
|
||||
ArchiveFile_t_clear(&item);
|
||||
}
|
||||
|
||||
static bool archive_get_fap_meta(FuriString* file_path, FuriString* fap_name, uint8_t** icon_ptr) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
bool success = false;
|
||||
if(fap_loader_load_name_and_icon(file_path, storage, icon_ptr, fap_name)) {
|
||||
success = true;
|
||||
}
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
return success;
|
||||
}
|
||||
|
||||
void archive_add_file_item(ArchiveBrowserView* browser, bool is_folder, const char* name) {
|
||||
furi_assert(browser);
|
||||
furi_assert(name);
|
||||
|
||||
ArchiveFile_t item;
|
||||
|
||||
ArchiveFile_t_init(&item);
|
||||
string_init_set_str(item.path, name);
|
||||
archive_set_file_type(&item, string_get_cstr(browser->path), is_folder, false);
|
||||
|
||||
furi_string_set(item.path, name);
|
||||
archive_set_file_type(&item, furi_string_get_cstr(browser->path), is_folder, false);
|
||||
if(item.type == ArchiveFileTypeApplication) {
|
||||
item.custom_icon_data = malloc(FAP_MANIFEST_MAX_ICON_SIZE);
|
||||
if(!archive_get_fap_meta(item.path, item.custom_name, &item.custom_icon_data)) {
|
||||
free(item.custom_icon_data);
|
||||
item.custom_icon_data = NULL;
|
||||
}
|
||||
}
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
files_array_push_back(model->files, item);
|
||||
return false;
|
||||
});
|
||||
browser->view,
|
||||
ArchiveBrowserViewModel * model,
|
||||
{ files_array_push_back(model->files, item); },
|
||||
false);
|
||||
ArchiveFile_t_clear(&item);
|
||||
}
|
||||
|
||||
void archive_show_file_menu(ArchiveBrowserView* browser, bool show) {
|
||||
furi_assert(browser);
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
browser->view,
|
||||
ArchiveBrowserViewModel * model,
|
||||
{
|
||||
if(show) {
|
||||
if(archive_is_item_in_array(model, model->item_idx)) {
|
||||
model->menu = true;
|
||||
@@ -380,35 +425,32 @@ void archive_show_file_menu(ArchiveBrowserView* browser, bool show) {
|
||||
menu_array_reset(model->context_menu);
|
||||
ArchiveFile_t* selected =
|
||||
files_array_get(model->files, model->item_idx - model->array_offset);
|
||||
selected->fav = archive_is_favorite("%s", string_get_cstr(selected->path));
|
||||
selected->fav =
|
||||
archive_is_favorite("%s", furi_string_get_cstr(selected->path));
|
||||
}
|
||||
} else {
|
||||
model->menu = false;
|
||||
model->menu_idx = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
void archive_favorites_move_mode(ArchiveBrowserView* browser, bool active) {
|
||||
furi_assert(browser);
|
||||
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
model->move_fav = active;
|
||||
return true;
|
||||
});
|
||||
browser->view, ArchiveBrowserViewModel * model, { model->move_fav = active; }, true);
|
||||
}
|
||||
|
||||
static bool archive_is_dir_exists(string_t path) {
|
||||
if(string_equal_str_p(path, STORAGE_ANY_PATH_PREFIX)) {
|
||||
static bool archive_is_dir_exists(FuriString* path) {
|
||||
if(furi_string_equal(path, STORAGE_ANY_PATH_PREFIX)) {
|
||||
return true;
|
||||
}
|
||||
bool state = false;
|
||||
FileInfo file_info;
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
if(storage_common_stat(storage, string_get_cstr(path), &file_info) == FSE_OK) {
|
||||
if(storage_common_stat(storage, furi_string_get_cstr(path), &file_info) == FSE_OK) {
|
||||
if(file_info.flags & FSF_DIRECTORY) {
|
||||
state = true;
|
||||
}
|
||||
@@ -428,20 +470,27 @@ 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);
|
||||
|
||||
string_set_str(browser->path, archive_get_default_path(tab));
|
||||
furi_string_set(browser->path, archive_get_default_path(tab));
|
||||
bool tab_empty = true;
|
||||
if(tab == ArchiveTabFavorites) {
|
||||
if(archive_favorites_count(browser) > 0) {
|
||||
tab_empty = false;
|
||||
}
|
||||
} else if(string_start_with_str_p(browser->path, "/app:")) {
|
||||
char* app_name = strchr(string_get_cstr(browser->path), ':');
|
||||
} else if(furi_string_start_with_str(browser->path, "/app:")) {
|
||||
char* app_name = strchr(furi_string_get_cstr(browser->path), ':');
|
||||
if(app_name != NULL) {
|
||||
if(archive_app_is_available(browser, string_get_cstr(browser->path))) {
|
||||
if(archive_app_is_available(browser, furi_string_get_cstr(browser->path))) {
|
||||
tab_empty = false;
|
||||
}
|
||||
}
|
||||
@@ -449,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
|
||||
}
|
||||
}
|
||||
@@ -459,29 +510,28 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) {
|
||||
archive_switch_tab(browser, key);
|
||||
} else {
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
browser->view,
|
||||
ArchiveBrowserViewModel * model,
|
||||
{
|
||||
model->item_idx = 0;
|
||||
model->array_offset = 0;
|
||||
return false;
|
||||
});
|
||||
archive_get_items(browser, string_get_cstr(browser->path));
|
||||
},
|
||||
false);
|
||||
archive_get_items(browser, furi_string_get_cstr(browser->path));
|
||||
archive_update_offset(browser);
|
||||
}
|
||||
}
|
||||
|
||||
void archive_enter_dir(ArchiveBrowserView* browser, string_t path) {
|
||||
void archive_enter_dir(ArchiveBrowserView* browser, FuriString* path) {
|
||||
furi_assert(browser);
|
||||
furi_assert(path);
|
||||
|
||||
int32_t idx_temp = 0;
|
||||
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
idx_temp = model->item_idx;
|
||||
return false;
|
||||
});
|
||||
browser->view, ArchiveBrowserViewModel * model, { idx_temp = model->item_idx; }, false);
|
||||
|
||||
string_set(browser->path, path);
|
||||
furi_string_set(browser->path, path);
|
||||
file_browser_worker_folder_enter(browser->worker, path, idx_temp);
|
||||
}
|
||||
|
||||
@@ -497,9 +547,6 @@ void archive_refresh_dir(ArchiveBrowserView* browser) {
|
||||
int32_t idx_temp = 0;
|
||||
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
idx_temp = model->item_idx;
|
||||
return false;
|
||||
});
|
||||
browser->view, ArchiveBrowserViewModel * model, { idx_temp = model->item_idx; }, false);
|
||||
file_browser_worker_folder_refresh(browser->worker, idx_temp);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user