mirror of
https://github.com/jomjol/AI-on-the-edge-device.git
synced 2025-12-07 20:16:55 +03:00
Compare commits
1699 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
019069cd16 | ||
|
|
4de1152cf3 | ||
|
|
0e0fb459dc | ||
|
|
8410df6144 | ||
|
|
4990858101 | ||
|
|
fc1f8ee242 | ||
|
|
252c399a76 | ||
|
|
eb7f2b3705 | ||
|
|
2ed6fb0f0d | ||
|
|
b5213b01af | ||
|
|
473e458b85 | ||
|
|
4f3f3d9af2 | ||
|
|
b5a4cfed96 | ||
|
|
6fca4d8d95 | ||
|
|
a11e19fb0c | ||
|
|
09fa94c95f | ||
|
|
f01b4dbd13 | ||
|
|
dbf1770016 | ||
|
|
006f3aa063 | ||
|
|
24c46c38b4 | ||
|
|
9ced147d9c | ||
|
|
0808895bd6 | ||
|
|
d2dec9fa59 | ||
|
|
7e7bc3dd68 | ||
|
|
5b09cd0d59 | ||
|
|
74d4f20858 | ||
|
|
e790a14caa | ||
|
|
f023a6b739 | ||
|
|
1e463188ea | ||
|
|
9991196961 | ||
|
|
ecece0f7fc | ||
|
|
1b3b7595c1 | ||
|
|
6d10f712d1 | ||
|
|
b84a2db050 | ||
|
|
374462a7d8 | ||
|
|
4facd7be05 | ||
|
|
3baf5865ad | ||
|
|
02138c44ac | ||
|
|
1094c8a0a8 | ||
|
|
75b15b8e9d | ||
|
|
36c12b400b | ||
|
|
35d90cd0ee | ||
|
|
999a8d9374 | ||
|
|
8a4269c6a0 | ||
|
|
8f5579cca5 | ||
|
|
e58b3a2cf8 | ||
|
|
222ee0921c | ||
|
|
e2bfcd26c9 | ||
|
|
18917b2d82 | ||
|
|
f0dea3abcb | ||
|
|
bd2d8b4a15 | ||
|
|
80e3f50a5b | ||
|
|
f6ca32d69f | ||
|
|
49e919c481 | ||
|
|
de768c4f44 | ||
|
|
002fc033aa | ||
|
|
f4af8de699 | ||
|
|
e4d6707a0b | ||
|
|
9f03e68690 | ||
|
|
b1df7df580 | ||
|
|
ebc9be4a28 | ||
|
|
5d0fc73c13 | ||
|
|
40a1aa0430 | ||
|
|
d7a733512f | ||
|
|
dc9f1aad27 | ||
|
|
cd3e641bcc | ||
|
|
ad72ffa37c | ||
|
|
2d45a0ed26 | ||
|
|
e8065ef414 | ||
|
|
33893eb566 | ||
|
|
3fbff0ad33 | ||
|
|
28cabea7f2 | ||
|
|
5d06130a88 | ||
|
|
3497a86084 | ||
|
|
84707fb27a | ||
|
|
0b81af7cb8 | ||
|
|
61efe1a6ef | ||
|
|
09ecd722cc | ||
|
|
5615fd8137 | ||
|
|
7ebf68411f | ||
|
|
34835dca84 | ||
|
|
697ff4c4b6 | ||
|
|
0f7c685933 | ||
|
|
3ace5aeff1 | ||
|
|
d47ed060b0 | ||
|
|
e7dbebffa1 | ||
|
|
def13d46af | ||
|
|
f82a6bf513 | ||
|
|
05deecee00 | ||
|
|
8339b6788f | ||
|
|
4e5b084932 | ||
|
|
62fcefee78 | ||
|
|
14128ca3a7 | ||
|
|
431551fb45 | ||
|
|
ff52d785cd | ||
|
|
f5a7c082d6 | ||
|
|
f3e90c6293 | ||
|
|
cbd14a267f | ||
|
|
ba7d6b3621 | ||
|
|
d4a492463b | ||
|
|
46589288e7 | ||
|
|
81a57d32a8 | ||
|
|
17994494c9 | ||
|
|
c581d64a26 | ||
|
|
70c2b88f8a | ||
|
|
13d01a6c96 | ||
|
|
331e3533cc | ||
|
|
5a00bdd7f6 | ||
|
|
db7ffb30c4 | ||
|
|
9a07f271ff | ||
|
|
6a45ab7693 | ||
|
|
9fc876853b | ||
|
|
c30881f73c | ||
|
|
e75e720869 | ||
|
|
43aae5a3cd | ||
|
|
54cd7e511a | ||
|
|
c5a82e839f | ||
|
|
9f97a2b223 | ||
|
|
8d06de5792 | ||
|
|
1326585a33 | ||
|
|
d7a507ca05 | ||
|
|
b24b7d5ce2 | ||
|
|
fc719da0ae | ||
|
|
9b1a83c8b4 | ||
|
|
6defcf8d4c | ||
|
|
c2a55e7c86 | ||
|
|
a636ce3679 | ||
|
|
a20fec1094 | ||
|
|
929796c87f | ||
|
|
e40ceb54ce | ||
|
|
eebdd7c6eb | ||
|
|
2a7f3b33a3 | ||
|
|
262d83ee6f | ||
|
|
de92c29245 | ||
|
|
19158c998f | ||
|
|
17ffd28c05 | ||
|
|
9ca02e0a7f | ||
|
|
7488d7bf23 | ||
|
|
e7bfba4b01 | ||
|
|
63ac38a52d | ||
|
|
e2cf8337d4 | ||
|
|
e995d6c498 | ||
|
|
0e3a50d0c1 | ||
|
|
df12deae00 | ||
|
|
b6bfeea936 | ||
|
|
f79e03faa2 | ||
|
|
de1dcc4d06 | ||
|
|
33bfef0af4 | ||
|
|
727b871fc5 | ||
|
|
03c84a1ff3 | ||
|
|
db36fe2522 | ||
|
|
9ffaf6e3f8 | ||
|
|
fa09680711 | ||
|
|
e2b66aa73a | ||
|
|
c4b990ada0 | ||
|
|
267782d083 | ||
|
|
e4a6fd33fe | ||
|
|
5db20d3687 | ||
|
|
58185a0569 | ||
|
|
b5e0d6ee66 | ||
|
|
d93c5daf14 | ||
|
|
6ff83445ac | ||
|
|
d944e8676b | ||
|
|
a8d7b29507 | ||
|
|
ab0fc72257 | ||
|
|
933215c116 | ||
|
|
e81a7eebe8 | ||
|
|
7d33c3ee5f | ||
|
|
863856ca5d | ||
|
|
eefc41d6ff | ||
|
|
d1807a1b3d | ||
|
|
dfc45772b7 | ||
|
|
4dd41c486f | ||
|
|
ff81fcbd7f | ||
|
|
5e5d2e2f72 | ||
|
|
53cee961f4 | ||
|
|
c81f901a19 | ||
|
|
512d7f95b4 | ||
|
|
6642f6f995 | ||
|
|
431df73e52 | ||
|
|
1f5d4de5f3 | ||
|
|
ce00192684 | ||
|
|
28f3ad0242 | ||
|
|
2dfd55e1c3 | ||
|
|
f84f20b2e8 | ||
|
|
806adcb4d0 | ||
|
|
4dc4752823 | ||
|
|
827423023c | ||
|
|
6ca7897fa0 | ||
|
|
39b8b5b07c | ||
|
|
5b98acaa32 | ||
|
|
d3d241c7b9 | ||
|
|
8c7e86fea4 | ||
|
|
98d85b2c6c | ||
|
|
f42e9c71f2 | ||
|
|
00be21f9e1 | ||
|
|
c694d9f363 | ||
|
|
a7dc37761b | ||
|
|
2dd2d03f6c | ||
|
|
4c407499d2 | ||
|
|
b97d808b54 | ||
|
|
9f2e91a9df | ||
|
|
18e96d62a6 | ||
|
|
a1a77ae5d9 | ||
|
|
c5b20f3680 | ||
|
|
add6cf5c33 | ||
|
|
493bd4df2f | ||
|
|
53ff190860 | ||
|
|
9a9aa68a65 | ||
|
|
a92cb69067 | ||
|
|
90fa44380c | ||
|
|
7a9f61a8d8 | ||
|
|
a8f8189543 | ||
|
|
2c4bda9e66 | ||
|
|
7b2a80a13d | ||
|
|
95d312b920 | ||
|
|
886cd4ffa5 | ||
|
|
10e0435383 | ||
|
|
b0de37b762 | ||
|
|
22e4b39f77 | ||
|
|
2c1a7f4c9e | ||
|
|
3960823439 | ||
|
|
d5ef08546a | ||
|
|
3d711f495e | ||
|
|
b21e3c6c9d | ||
|
|
23d2ae627d | ||
|
|
3f62abf878 | ||
|
|
025f4af9f2 | ||
|
|
3d92860c5e | ||
|
|
2ed9fb8eb5 | ||
|
|
598db004ae | ||
|
|
70332fe142 | ||
|
|
10da8c4f94 | ||
|
|
5bac1c68d9 | ||
|
|
008dba7e11 | ||
|
|
7283bfd506 | ||
|
|
56788652ae | ||
|
|
44e186e65b | ||
|
|
55be652dc1 | ||
|
|
1acd72d33e | ||
|
|
795bcd0d21 | ||
|
|
0b2e38935b | ||
|
|
a9c5bebb45 | ||
|
|
876adc51af | ||
|
|
bf090f3762 | ||
|
|
30a50720e5 | ||
|
|
6c891ab7fe | ||
|
|
6adcd90038 | ||
|
|
676b94f57c | ||
|
|
01df45f1e1 | ||
|
|
f7fc55960b | ||
|
|
3dd2b74226 | ||
|
|
4424337a4e | ||
|
|
3fa16c5624 | ||
|
|
b9134f923e | ||
|
|
06f4d417b5 | ||
|
|
55efc3b3f4 | ||
|
|
800e231301 | ||
|
|
34a3d6d6e3 | ||
|
|
4bfe5422c5 | ||
|
|
d63dc08f33 | ||
|
|
2ee85001eb | ||
|
|
1d2f920819 | ||
|
|
0ddcbbc847 | ||
|
|
bbd60c9d19 | ||
|
|
19d20f01de | ||
|
|
fa69272a57 | ||
|
|
69583db99e | ||
|
|
4986b1936f | ||
|
|
939271f2a0 | ||
|
|
d40d5ba0f6 | ||
|
|
a851fd393c | ||
|
|
2a8df9de1c | ||
|
|
634deb9f27 | ||
|
|
d795f30276 | ||
|
|
17f951d3cd | ||
|
|
f4f4fcd166 | ||
|
|
90d0827846 | ||
|
|
5a03bf82d7 | ||
|
|
391907b5a1 | ||
|
|
69af3d79c1 | ||
|
|
7b8a5e77a5 | ||
|
|
1a7ff9a4e2 | ||
|
|
70b8572d16 | ||
|
|
c8bfb338c6 | ||
|
|
e6bd4160e1 | ||
|
|
ab30bda586 | ||
|
|
813d49690c | ||
|
|
f5e2f5dd72 | ||
|
|
2029e07495 | ||
|
|
b780815a29 | ||
|
|
bb714acb0b | ||
|
|
ac27539442 | ||
|
|
27f64e9479 | ||
|
|
f0e79de1ba | ||
|
|
074136ec2b | ||
|
|
feb058ccf8 | ||
|
|
57dc02365b | ||
|
|
486b76de1b | ||
|
|
d4b2bec45f | ||
|
|
76ef3a80cc | ||
|
|
e63f98e8b3 | ||
|
|
c63819e9b4 | ||
|
|
a0d6509f42 | ||
|
|
b451e4ebb3 | ||
|
|
845a166d9e | ||
|
|
8b2d80c87e | ||
|
|
426068288d | ||
|
|
81b9b4ab76 | ||
|
|
18b6aee7c7 | ||
|
|
4abbbca212 | ||
|
|
3372359daa | ||
|
|
584f7b9471 | ||
|
|
88855c8f31 | ||
|
|
f70d7aed42 | ||
|
|
b40a42b02b | ||
|
|
f66ea4f01f | ||
|
|
bb79ab3d75 | ||
|
|
0b269e17f3 | ||
|
|
d55abf20c1 | ||
|
|
1fcbc65ee9 | ||
|
|
c595b402df | ||
|
|
1a34fe97e4 | ||
|
|
2c9a6a0f5c | ||
|
|
f8807ca4bc | ||
|
|
e0ec67a14d | ||
|
|
50c02a14f4 | ||
|
|
2f5ecf8722 | ||
|
|
2c65d51210 | ||
|
|
5c53858766 | ||
|
|
4c51a906a3 | ||
|
|
4395135e89 | ||
|
|
827aa48524 | ||
|
|
cf80aa929b | ||
|
|
245302c6ca | ||
|
|
426c24575f | ||
|
|
c3233dc159 | ||
|
|
2a0c61332e | ||
|
|
18d44a8556 | ||
|
|
5dc111e7a5 | ||
|
|
90d7c2e641 | ||
|
|
8bb9c6dc41 | ||
|
|
1da27a6c54 | ||
|
|
35fa2bd353 | ||
|
|
8aa9f48160 | ||
|
|
5dc7bb949a | ||
|
|
457a526792 | ||
|
|
eddf59cdb3 | ||
|
|
d0cf9e495d | ||
|
|
905b5a2ac5 | ||
|
|
14888bca3f | ||
|
|
2735a0862f | ||
|
|
782e1c229b | ||
|
|
287302ba7f | ||
|
|
3de4cc7c56 | ||
|
|
6c153cd376 | ||
|
|
d99c2ce920 | ||
|
|
17eb382b66 | ||
|
|
0df54d1c5b | ||
|
|
9ddc9a4f5a | ||
|
|
86304524e2 | ||
|
|
22ef80db37 | ||
|
|
5a8b39f246 | ||
|
|
284f7b1180 | ||
|
|
bf44745342 | ||
|
|
60ce08e331 | ||
|
|
52296b3cb6 | ||
|
|
17d85ae523 | ||
|
|
e760a38985 | ||
|
|
47d419e3ae | ||
|
|
f779bc8401 | ||
|
|
cb4e6a0a30 | ||
|
|
fd2d4408bf | ||
|
|
6ab2b42c8d | ||
|
|
b21c0ab15b | ||
|
|
adfe2d57d6 | ||
|
|
ec8de6287f | ||
|
|
656110ca02 | ||
|
|
1e08eaf4bf | ||
|
|
bb4f2e4d91 | ||
|
|
f15347598a | ||
|
|
defbd60ccf | ||
|
|
4a462fb79a | ||
|
|
c86cd905da | ||
|
|
184d1a70a2 | ||
|
|
decf72104e | ||
|
|
0ee3c0bb0a | ||
|
|
e05ae5170d | ||
|
|
1896c27dac | ||
|
|
58816275e5 | ||
|
|
a39092497e | ||
|
|
eefdc74e9a | ||
|
|
4b38c1ef00 | ||
|
|
2bb7552b85 | ||
|
|
798f1423c3 | ||
|
|
ee4832323d | ||
|
|
758238a82e | ||
|
|
e661e1d7f2 | ||
|
|
068f1b1817 | ||
|
|
26897ccb15 | ||
|
|
b1a38e0a6d | ||
|
|
7ee308dec6 | ||
|
|
da9f942857 | ||
|
|
8f018732d3 | ||
|
|
68ac6b0185 | ||
|
|
62a2f127b4 | ||
|
|
3a3e1dde90 | ||
|
|
6b84eb0290 | ||
|
|
4d823b354f | ||
|
|
485e55e0b8 | ||
|
|
56cfeb732e | ||
|
|
e6cb9d67ee | ||
|
|
809823a9b0 | ||
|
|
b9abbafefc | ||
|
|
eff713a2a9 | ||
|
|
33f357d8da | ||
|
|
58cbd680e8 | ||
|
|
4121416743 | ||
|
|
a2ec5cc355 | ||
|
|
06309088f4 | ||
|
|
03c99b2cca | ||
|
|
014dc88112 | ||
|
|
efb9830e0b | ||
|
|
1bda03ee04 | ||
|
|
4b23e0c6c3 | ||
|
|
f08e856dfd | ||
|
|
7a1154363b | ||
|
|
f2f117aea1 | ||
|
|
ee3b15990d | ||
|
|
516ebeb0b7 | ||
|
|
2ce25751a9 | ||
|
|
8d44ce4852 | ||
|
|
7fa0b87e6e | ||
|
|
fca37ee699 | ||
|
|
39a02c6d6d | ||
|
|
3a888c54fe | ||
|
|
6a9399cf0b | ||
|
|
14a2510e12 | ||
|
|
85030e39fa | ||
|
|
de223f51f6 | ||
|
|
f11fadf14b | ||
|
|
9b8923c998 | ||
|
|
45aeff3e0f | ||
|
|
603e968ec7 | ||
|
|
66eb1e8d9a | ||
|
|
9b200912b2 | ||
|
|
35a6d5a063 | ||
|
|
577417b054 | ||
|
|
443bd67f81 | ||
|
|
0feeede406 | ||
|
|
f6bf7e38c7 | ||
|
|
e54d914795 | ||
|
|
fc5fbd648e | ||
|
|
c4cd43b3d6 | ||
|
|
5890da10d2 | ||
|
|
f6369ff237 | ||
|
|
67ff06f64e | ||
|
|
295e505eef | ||
|
|
0e2ab869cb | ||
|
|
b0726b6c7e | ||
|
|
834fd60983 | ||
|
|
04f2f23931 | ||
|
|
c6cfe73e23 | ||
|
|
914cfceedf | ||
|
|
f1a836a407 | ||
|
|
c3dd59adde | ||
|
|
297301b701 | ||
|
|
0bd8ec7c98 | ||
|
|
21fa9431a2 | ||
|
|
892bf3e116 | ||
|
|
1b600d54c8 | ||
|
|
da06ebd1a5 | ||
|
|
0d48c526b8 | ||
|
|
6e48ec5fb7 | ||
|
|
9313d6e8c8 | ||
|
|
6083fe6151 | ||
|
|
4088e9ee75 | ||
|
|
a1f01bda0f | ||
|
|
504a2d4e0e | ||
|
|
72b50eaddb | ||
|
|
a39a839f41 | ||
|
|
6868bfe84a | ||
|
|
07913045e2 | ||
|
|
6c65b31fff | ||
|
|
2d89326362 | ||
|
|
b555d88aa7 | ||
|
|
5c43cf460f | ||
|
|
b3111235fc | ||
|
|
5084b438fc | ||
|
|
3d2fd2f70f | ||
|
|
27023547d2 | ||
|
|
2dcd771c15 | ||
|
|
f62f844134 | ||
|
|
149e8f0845 | ||
|
|
434e268d42 | ||
|
|
24f10b4683 | ||
|
|
6863e637aa | ||
|
|
b85e3b11a9 | ||
|
|
93f0f6b07d | ||
|
|
5e101f80b4 | ||
|
|
55f78dce6d | ||
|
|
304b9e0c32 | ||
|
|
db59c2e24a | ||
|
|
fd0ac182d2 | ||
|
|
286915b647 | ||
|
|
3f85f9b755 | ||
|
|
9d5846d0ce | ||
|
|
ba08b85225 | ||
|
|
774aa7d211 | ||
|
|
2997bf0911 | ||
|
|
af29756943 | ||
|
|
268a3024d8 | ||
|
|
2768667eb1 | ||
|
|
88bdcc9365 | ||
|
|
bb4fab8df5 | ||
|
|
39019e9c92 | ||
|
|
f4d086508d | ||
|
|
e23c89ae57 | ||
|
|
53a764abc8 | ||
|
|
4f07c88769 | ||
|
|
fa406d8989 | ||
|
|
1ca54f39f8 | ||
|
|
5867e90f69 | ||
|
|
e87b91f6eb | ||
|
|
4b52e61755 | ||
|
|
eb48212630 | ||
|
|
961662f483 | ||
|
|
306b1a75a5 | ||
|
|
7350864150 | ||
|
|
16d0758ea3 | ||
|
|
877a1b14e5 | ||
|
|
a122b37c81 | ||
|
|
ad137b329b | ||
|
|
85905a7045 | ||
|
|
9847f95c93 | ||
|
|
e9e13588f6 | ||
|
|
6c8a45f2dc | ||
|
|
e7d28f9bde | ||
|
|
33015351d6 | ||
|
|
0a461b2bd0 | ||
|
|
ecf1e78208 | ||
|
|
a8b9acf170 | ||
|
|
45a3138d28 | ||
|
|
7e997889aa | ||
|
|
6c51af7107 | ||
|
|
8be7beab9a | ||
|
|
62ec8d76c6 | ||
|
|
7a634797c4 | ||
|
|
575d59dad2 | ||
|
|
524d800a0a | ||
|
|
7b0e6200d6 | ||
|
|
65d011c9aa | ||
|
|
dc3604f144 | ||
|
|
1eb2dd4efa | ||
|
|
f4d345b902 | ||
|
|
a38837c61b | ||
|
|
b50be7e825 | ||
|
|
74f45bee28 | ||
|
|
47b3b0c708 | ||
|
|
9c390f3026 | ||
|
|
069aac5723 | ||
|
|
9335cd73d5 | ||
|
|
e90beffb44 | ||
|
|
55a06dbe1d | ||
|
|
ae07159afe | ||
|
|
1fff655ef1 | ||
|
|
3815f9cf0a | ||
|
|
e36490c89e | ||
|
|
d4f02f971f | ||
|
|
ec326d9a11 | ||
|
|
6e4df0ef87 | ||
|
|
3aa0411676 | ||
|
|
9f20c126be | ||
|
|
2707e8c9f4 | ||
|
|
0d467d8ad1 | ||
|
|
66be09c98e | ||
|
|
60e9a427a5 | ||
|
|
f72bdb7c45 | ||
|
|
a53188e697 | ||
|
|
0dd63b9b7a | ||
|
|
513e300676 | ||
|
|
dd28859a9f | ||
|
|
ecc43edbba | ||
|
|
bf8ab423e3 | ||
|
|
8116a546b5 | ||
|
|
5c512367e2 | ||
|
|
e15ea561bc | ||
|
|
9e4332314a | ||
|
|
880d9eae20 | ||
|
|
72fcd791db | ||
|
|
39960b15ed | ||
|
|
395b9a4c5b | ||
|
|
5a2753a50b | ||
|
|
57c5c19cca | ||
|
|
c64ed36fa4 | ||
|
|
754981c67f | ||
|
|
5c22986e2e | ||
|
|
1746920f61 | ||
|
|
d33380d2a0 | ||
|
|
889ed9e6be | ||
|
|
2b0e0f7d4e | ||
|
|
2314d7ef18 | ||
|
|
bec3a5ee3b | ||
|
|
c738a9a4da | ||
|
|
e7c8706b1b | ||
|
|
f725adf5ba | ||
|
|
7bb1b08bdb | ||
|
|
d1e7ef1fce | ||
|
|
08f7e1275e | ||
|
|
529690ec60 | ||
|
|
59431a7eaf | ||
|
|
23b5ffbb92 | ||
|
|
9b8594c040 | ||
|
|
3e082ed06e | ||
|
|
22fe50f80a | ||
|
|
885cd71b80 | ||
|
|
8c6805ec7c | ||
|
|
1fc0b41fca | ||
|
|
7e26744e2e | ||
|
|
1d9ef7e634 | ||
|
|
1be49a75b1 | ||
|
|
b6b7587f0a | ||
|
|
e3017c25a9 | ||
|
|
d914e69e7a | ||
|
|
edf9f11048 | ||
|
|
44afd138c5 | ||
|
|
d2b93a7110 | ||
|
|
ff726485ba | ||
|
|
de46cd899e | ||
|
|
37263bb239 | ||
|
|
d21a38f42f | ||
|
|
9b9a7537f1 | ||
|
|
56d8c65008 | ||
|
|
fc24db7d59 | ||
|
|
0867dcc6da | ||
|
|
b9f57edb92 | ||
|
|
7ebbba3cf2 | ||
|
|
5188734c8b | ||
|
|
4e476a75ca | ||
|
|
7a037a3254 | ||
|
|
f6b44ac905 | ||
|
|
deecc128be | ||
|
|
18f2b5824e | ||
|
|
45084bab70 | ||
|
|
6cdbe38717 | ||
|
|
fe0d0c2590 | ||
|
|
44f61c7c91 | ||
|
|
af8b7d6824 | ||
|
|
170583f8fe | ||
|
|
648a35e4d7 | ||
|
|
99849063d9 | ||
|
|
5b22007a1e | ||
|
|
80cf89c9d5 | ||
|
|
45d37ea957 | ||
|
|
f935c38571 | ||
|
|
bfc7c2b8b7 | ||
|
|
08f90de683 | ||
|
|
32748e3182 | ||
|
|
7a280bc7c6 | ||
|
|
f4ae688527 | ||
|
|
ca45d0a278 | ||
|
|
8f66fcf2a6 | ||
|
|
30549ac5af | ||
|
|
06ab14a6c9 | ||
|
|
72e3570dc4 | ||
|
|
724169b059 | ||
|
|
6e58f5eebb | ||
|
|
96b3d7cc2f | ||
|
|
74c9eada47 | ||
|
|
9d65889824 | ||
|
|
98451c8fc7 | ||
|
|
6b47eef4cd | ||
|
|
158ec65e57 | ||
|
|
c4bbd7c545 | ||
|
|
cb323eb715 | ||
|
|
1aa23aa14a | ||
|
|
41abc19a97 | ||
|
|
99e03dde06 | ||
|
|
9d73c5475e | ||
|
|
25ae9a045f | ||
|
|
dabe66819d | ||
|
|
4ad7b97764 | ||
|
|
083aeb42ec | ||
|
|
a13d1d0692 | ||
|
|
c12645ab4d | ||
|
|
116c99e55c | ||
|
|
43b65d6b2e | ||
|
|
bc7a0e46b7 | ||
|
|
455962bd94 | ||
|
|
86c515bcb8 | ||
|
|
f09067a142 | ||
|
|
8ce24df304 | ||
|
|
279a59d019 | ||
|
|
d88e286696 | ||
|
|
d07aa2a72d | ||
|
|
bf30872439 | ||
|
|
12dc4eb460 | ||
|
|
ab67d1ff35 | ||
|
|
86809d8bf8 | ||
|
|
85adf70394 | ||
|
|
290e7f3835 | ||
|
|
e9e0eed871 | ||
|
|
4ab0f632b7 | ||
|
|
1a33834b3f | ||
|
|
1d367a58d5 | ||
|
|
ec4ce66bcc | ||
|
|
4a0f5eadc2 | ||
|
|
f955f8786b | ||
|
|
38f6e49d00 | ||
|
|
4394d832b8 | ||
|
|
9e68380814 | ||
|
|
c8bb95a852 | ||
|
|
9c2de84ee4 | ||
|
|
d4b1c2c883 | ||
|
|
550d9e1c87 | ||
|
|
3f687a7233 | ||
|
|
c1369ca0ff | ||
|
|
76c8bce7b4 | ||
|
|
e0ae9b8e4f | ||
|
|
9de573c15e | ||
|
|
716c23fed3 | ||
|
|
8e22bd06e9 | ||
|
|
b78929745b | ||
|
|
6986f2186c | ||
|
|
3743ac18f5 | ||
|
|
13f1d40ca3 | ||
|
|
63f28097bd | ||
|
|
ff7fec1de4 | ||
|
|
f65b2850a2 | ||
|
|
3a999358b9 | ||
|
|
d3e195df9f | ||
|
|
dbf8e634d9 | ||
|
|
2b810ca32d | ||
|
|
0adfc45d36 | ||
|
|
2a54d9b889 | ||
|
|
4150c31b98 | ||
|
|
803e8f2bff | ||
|
|
b215345f11 | ||
|
|
98d35e0412 | ||
|
|
437e8e4d25 | ||
|
|
e05085ddf0 | ||
|
|
495b5de38c | ||
|
|
c2b89dd199 | ||
|
|
f15b1e5dfc | ||
|
|
e308a1b5d9 | ||
|
|
1e698440f9 | ||
|
|
98bf7e5387 | ||
|
|
e1f8ac59cb | ||
|
|
0a2b6b71ca | ||
|
|
5daae7b47c | ||
|
|
4951fc9b80 | ||
|
|
0bf8182728 | ||
|
|
a512d82793 | ||
|
|
4f305a8705 | ||
|
|
46daa4cb79 | ||
|
|
485363d7a2 | ||
|
|
c8d2d9d4fd | ||
|
|
0880213342 | ||
|
|
8d4fb74173 | ||
|
|
f10adb3383 | ||
|
|
3be33820d9 | ||
|
|
83f638c64f | ||
|
|
35d77a3925 | ||
|
|
a33da0c750 | ||
|
|
cd2350140d | ||
|
|
bd125b249b | ||
|
|
33b9a15f73 | ||
|
|
0e55fc7f7e | ||
|
|
fc0bbc57cb | ||
|
|
bc46149573 | ||
|
|
be8598bcaa | ||
|
|
ace1936a78 | ||
|
|
e5649d03b2 | ||
|
|
426d6bae3f | ||
|
|
5d35df65f3 | ||
|
|
f91f5e3cba | ||
|
|
1c66f8c6ca | ||
|
|
a42902af0d | ||
|
|
a871055d96 | ||
|
|
64fcb9595c | ||
|
|
9d9cff977d | ||
|
|
5724f37ea6 | ||
|
|
3ca93b49ca | ||
|
|
b2248818dd | ||
|
|
a702fbbe86 | ||
|
|
1002e502d5 | ||
|
|
17a8adae05 | ||
|
|
4fca050623 | ||
|
|
f371c176b6 | ||
|
|
1c6772f383 | ||
|
|
7d2f28084b | ||
|
|
923b8d7444 | ||
|
|
49e4eb3ef3 | ||
|
|
2c481c0d15 | ||
|
|
f2e854c935 | ||
|
|
8045e0bfaf | ||
|
|
40e176ec28 | ||
|
|
1ab135e989 | ||
|
|
99c14ae9e0 | ||
|
|
00d859734d | ||
|
|
de4efff558 | ||
|
|
2ed3f4aa40 | ||
|
|
ba59c8ee66 | ||
|
|
fb97e22dee | ||
|
|
b0e7283d2b | ||
|
|
e341564eee | ||
|
|
730006977c | ||
|
|
ea26a5722a | ||
|
|
62742d92d2 | ||
|
|
50b9983534 | ||
|
|
34ea8302ef | ||
|
|
a8fd0bbdef | ||
|
|
99e6243e25 | ||
|
|
0f6c767143 | ||
|
|
eb0b932c44 | ||
|
|
1e4be0f3fe | ||
|
|
47caa1b118 | ||
|
|
641609e187 | ||
|
|
006fb6a611 | ||
|
|
a0e4ee1d00 | ||
|
|
27d770c759 | ||
|
|
fe39b5120b | ||
|
|
74df27befd | ||
|
|
c92a1e430d | ||
|
|
642d22578f | ||
|
|
140ec1b380 | ||
|
|
17090c177e | ||
|
|
d2721cbd4b | ||
|
|
f80509c886 | ||
|
|
6c85796abe | ||
|
|
e82cbbf117 | ||
|
|
bacdc97d90 | ||
|
|
1e43c70adf | ||
|
|
16bb6e90a9 | ||
|
|
cf6882c579 | ||
|
|
858d0b3361 | ||
|
|
208a5433b7 | ||
|
|
9a7c9604fe | ||
|
|
f44fa4df31 | ||
|
|
68a985a09f | ||
|
|
6b329aaa58 | ||
|
|
80798ae0e6 | ||
|
|
cd61e2c92d | ||
|
|
c943510828 | ||
|
|
9291d330dd | ||
|
|
a856d9e2f2 | ||
|
|
41f9436465 | ||
|
|
5233c14725 | ||
|
|
3d42d949af | ||
|
|
aec1dc770b | ||
|
|
f335c17f0d | ||
|
|
dfe2c466bc | ||
|
|
bb924531ef | ||
|
|
6e24b6fa88 | ||
|
|
d9a7c197fc | ||
|
|
14d30bae9d | ||
|
|
97ef1904bb | ||
|
|
0856aea069 | ||
|
|
73e9253cb7 | ||
|
|
663e97db97 | ||
|
|
8b64266602 | ||
|
|
e697cc5ec8 | ||
|
|
2c4d5a42d8 | ||
|
|
2f2ec23a53 | ||
|
|
3ad72f39a6 | ||
|
|
88a074dfa9 | ||
|
|
8c5f956dfe | ||
|
|
505eed8767 | ||
|
|
cc9a3d3551 | ||
|
|
70b743e364 | ||
|
|
6307b7f278 | ||
|
|
ed2ee7ad90 | ||
|
|
26789f4187 | ||
|
|
923fc20ca5 | ||
|
|
6808a37b69 | ||
|
|
f996e65a4d | ||
|
|
0168f6b9b7 | ||
|
|
e6b8643124 | ||
|
|
69a92712ef | ||
|
|
93804cec3f | ||
|
|
8cd430efc7 | ||
|
|
8175c946f2 | ||
|
|
0eb70b6208 | ||
|
|
5ac3378479 | ||
|
|
356b208f33 | ||
|
|
924f03595b | ||
|
|
90595968b2 | ||
|
|
96a59ca429 | ||
|
|
b76d6cd987 | ||
|
|
98b7224c65 | ||
|
|
ece73f63c8 | ||
|
|
b281ae9583 | ||
|
|
6af7e1edd2 | ||
|
|
f04ca2bdde | ||
|
|
9007d8c484 | ||
|
|
d8a7e7a39d | ||
|
|
ee13581475 | ||
|
|
8c86a3013a | ||
|
|
8aab81acc7 | ||
|
|
8707190d16 | ||
|
|
aa72cab84f | ||
|
|
29607a8617 | ||
|
|
2239ab9019 | ||
|
|
86ea8a8e26 | ||
|
|
bfa8d8e691 | ||
|
|
dc788b14eb | ||
|
|
228a87038e | ||
|
|
d11ee2a4cf | ||
|
|
b38c940dd3 | ||
|
|
6a22d355eb | ||
|
|
a3a46fb0bb | ||
|
|
452339a1f0 | ||
|
|
5e93f1e8bd | ||
|
|
2e4023cfd6 | ||
|
|
12c748534e | ||
|
|
8275bdbc25 | ||
|
|
0ffaf99e10 | ||
|
|
b1b76feeb0 | ||
|
|
265616ff80 | ||
|
|
89c65f6226 | ||
|
|
6634b36f4a | ||
|
|
6c433d83fc | ||
|
|
a9d77a0005 | ||
|
|
efb35b1aa7 | ||
|
|
6f9d21fc21 | ||
|
|
f7df03f0c0 | ||
|
|
e92ff43e09 | ||
|
|
592a52ef0b | ||
|
|
4e19066e26 | ||
|
|
8e39e83c38 | ||
|
|
78900defad | ||
|
|
d0b78e7c73 | ||
|
|
d3675f269d | ||
|
|
64d25f416a | ||
|
|
3f8b38a1ce | ||
|
|
5979acc31e | ||
|
|
7397927b77 | ||
|
|
0d67282c00 | ||
|
|
f9939023c6 | ||
|
|
922d76dace | ||
|
|
02bf674539 | ||
|
|
4ad315b3f7 | ||
|
|
342fa0465c | ||
|
|
2866badc04 | ||
|
|
60194d8db8 | ||
|
|
165745bb7d | ||
|
|
fc4f3eebb6 | ||
|
|
8a6bd97ec2 | ||
|
|
c4ee48ad1e | ||
|
|
3e5aa77ff5 | ||
|
|
b45b0f6a2a | ||
|
|
fc77c0fdf1 | ||
|
|
bf1e96a303 | ||
|
|
86ba3e9bf4 | ||
|
|
d0fb1fc0f1 | ||
|
|
8bf4939ac1 | ||
|
|
75a653a5c7 | ||
|
|
e713ffa52d | ||
|
|
abc3efc16e | ||
|
|
25e20f9299 | ||
|
|
f2effc4253 | ||
|
|
fb03dba60c | ||
|
|
68e57d5ec4 | ||
|
|
a1691a77cf | ||
|
|
aa5651a464 | ||
|
|
0a79fb6196 | ||
|
|
7e108d707a | ||
|
|
fb850fb040 | ||
|
|
95292b8213 | ||
|
|
e59dca4bec | ||
|
|
86e47c722d | ||
|
|
5a1127d2b9 | ||
|
|
b1d4df4309 | ||
|
|
f6f70776d9 | ||
|
|
570777db7e | ||
|
|
b06b42f0e9 | ||
|
|
8a170a7e16 | ||
|
|
5e95634173 | ||
|
|
84cc3490a1 | ||
|
|
ee724d372d | ||
|
|
711578d0a2 | ||
|
|
1956416fb6 | ||
|
|
1ec1f4f75f | ||
|
|
0fb192d79e | ||
|
|
19847652a9 | ||
|
|
d807334141 | ||
|
|
a08a233484 | ||
|
|
7fc9676385 | ||
|
|
7d8cdc79f2 | ||
|
|
6938299b72 | ||
|
|
6794091919 | ||
|
|
6631ebc12a | ||
|
|
efc800c223 | ||
|
|
6756b6d741 | ||
|
|
3138e36137 | ||
|
|
b3b1c18ff5 | ||
|
|
1c9cd46d88 | ||
|
|
f4074fb939 | ||
|
|
7d286337ce | ||
|
|
81f92b8b0f | ||
|
|
01337ddcbf | ||
|
|
3fb545f869 | ||
|
|
04e59482b5 | ||
|
|
50acd26804 | ||
|
|
1bc8e698b4 | ||
|
|
46b8b67fed | ||
|
|
84a653b5d5 | ||
|
|
dee7212531 | ||
|
|
50b0593648 | ||
|
|
4c786406c3 | ||
|
|
6fed12e9ad | ||
|
|
11216350c4 | ||
|
|
577dbdeb7c | ||
|
|
b75f77b31c | ||
|
|
2776e1b127 | ||
|
|
562cc4352b | ||
|
|
013d5a99a8 | ||
|
|
fbe7bb90c2 | ||
|
|
660326af42 | ||
|
|
80aa2dfc73 | ||
|
|
316d8f252a | ||
|
|
28a39d8dfc | ||
|
|
3fc6c194e5 | ||
|
|
0e85b40eba | ||
|
|
d479c8d44e | ||
|
|
6b6e677f8b | ||
|
|
45c9914efa | ||
|
|
a4299cab89 | ||
|
|
67e4ee4aca | ||
|
|
85750c9453 | ||
|
|
d8b4b3f2cf | ||
|
|
fedaef07eb | ||
|
|
9ae9a82cca | ||
|
|
37fec1821f | ||
|
|
c4e69bc036 | ||
|
|
7dd14282c6 | ||
|
|
7e72cc3d1b | ||
|
|
8e2da52d82 | ||
|
|
e38c861cb5 | ||
|
|
b6a571a90c | ||
|
|
26949d8083 | ||
|
|
1c80c2108d | ||
|
|
e1fc44d44a | ||
|
|
32e5c484ab | ||
|
|
bf8589f9ec | ||
|
|
b65d9e5a46 | ||
|
|
5aa4714d05 | ||
|
|
dd8500052e | ||
|
|
2d68f44a40 | ||
|
|
39ec90895e | ||
|
|
3d1d41e36b | ||
|
|
46dfc75724 | ||
|
|
906915e058 | ||
|
|
85418c678d | ||
|
|
81e06c45d3 | ||
|
|
645fbf0231 | ||
|
|
5d10dba3c7 | ||
|
|
78806c081b | ||
|
|
3f659ccf7d | ||
|
|
6cd52a3cdc | ||
|
|
a1f675419a | ||
|
|
9087c1b4e1 | ||
|
|
9418d55a2f | ||
|
|
87076df23f | ||
|
|
aba9603021 | ||
|
|
086aa3134b | ||
|
|
010691f3e5 | ||
|
|
12d9099543 | ||
|
|
c9b7f8d901 | ||
|
|
0372f82f2e | ||
|
|
18be9afbe1 | ||
|
|
36f5618376 | ||
|
|
03542368d7 | ||
|
|
36a02d1658 | ||
|
|
c65182b568 | ||
|
|
e5bd824506 | ||
|
|
10fbb610c3 | ||
|
|
b8ab58a196 | ||
|
|
fa59b5ba6c | ||
|
|
67d47abde2 | ||
|
|
042ff18e65 | ||
|
|
3008952240 | ||
|
|
aef28c7128 | ||
|
|
9c5dfe65c9 | ||
|
|
b12cdd673b | ||
|
|
8247580372 | ||
|
|
d7c7537aa9 | ||
|
|
ad05a1d4fa | ||
|
|
7bd819ad96 | ||
|
|
64b472d6bc | ||
|
|
3d7a906cce | ||
|
|
8c4a5a957a | ||
|
|
21874473cc | ||
|
|
8aa99b34d6 | ||
|
|
6759164c82 | ||
|
|
2445377d0a | ||
|
|
99b6393fbc | ||
|
|
7e049f3270 | ||
|
|
79d1e6b932 | ||
|
|
3403e3ea5c | ||
|
|
c3a3aa9c0d | ||
|
|
89c9ff144c | ||
|
|
82e6334832 | ||
|
|
dd70aa8969 | ||
|
|
4cf190239e | ||
|
|
cb4ba51eee | ||
|
|
4d03867550 | ||
|
|
02294e3afc | ||
|
|
f68aa1d931 | ||
|
|
fb390359e1 | ||
|
|
764c64b615 | ||
|
|
a167770848 | ||
|
|
551743abec | ||
|
|
68a596707b | ||
|
|
47da2d657e | ||
|
|
43372c94a8 | ||
|
|
0643274c68 | ||
|
|
395b471700 | ||
|
|
a040d56fdc | ||
|
|
3c8a65c540 | ||
|
|
2f38643473 | ||
|
|
906a2e05eb | ||
|
|
3390e24534 | ||
|
|
dc28b892bc | ||
|
|
3023cd139d | ||
|
|
127135e1cb | ||
|
|
aa4e115f80 | ||
|
|
90f204b833 | ||
|
|
77427a8ec4 | ||
|
|
eafcdb01c2 | ||
|
|
3f58086aa1 | ||
|
|
d36a4dbdbe | ||
|
|
6879aa96e2 | ||
|
|
fc219cc7f3 | ||
|
|
787f9362eb | ||
|
|
231ac5305f | ||
|
|
4bd79e370f | ||
|
|
5c0cd63da0 | ||
|
|
5a31543538 | ||
|
|
1b88bf8690 | ||
|
|
6db01e67c9 | ||
|
|
f7cb6576b3 | ||
|
|
4a71749d1c | ||
|
|
d13f457344 | ||
|
|
ab41401643 | ||
|
|
592b93ce2b | ||
|
|
5dc7b90a1c | ||
|
|
b1e0203527 | ||
|
|
774dde5767 | ||
|
|
c322cd8b48 | ||
|
|
3dcdd52eb7 | ||
|
|
fe17d97a05 | ||
|
|
b71a34b476 | ||
|
|
ea71c86f69 | ||
|
|
550fe605bd | ||
|
|
109dd97016 | ||
|
|
715f0a10f3 | ||
|
|
d01caea53e | ||
|
|
a9cab31a1f | ||
|
|
d0968e87d7 | ||
|
|
6417f780e6 | ||
|
|
fd4d76de93 | ||
|
|
cdf8663bd3 | ||
|
|
bf9050757b | ||
|
|
bce99da6e5 | ||
|
|
234925c850 | ||
|
|
c9b7a5f84c | ||
|
|
cfaeaa370a | ||
|
|
8cd5a89d6a | ||
|
|
090f8d5cc9 | ||
|
|
29ae8cfa7f | ||
|
|
338184712d | ||
|
|
b2c510d73e | ||
|
|
e8b649d315 | ||
|
|
6a5d5511f1 | ||
|
|
ec99ac3a3b | ||
|
|
58cfd8bc42 | ||
|
|
f676f12e02 | ||
|
|
393fe43090 | ||
|
|
296a50a6d2 | ||
|
|
b1ee3d8793 | ||
|
|
d930fdae78 | ||
|
|
993fbfe5a8 | ||
|
|
af546ef0f1 | ||
|
|
2b60e81a52 | ||
|
|
11418459b8 | ||
|
|
3b8b8e47da | ||
|
|
ae302d49ef | ||
|
|
0153229d3c | ||
|
|
eeb74dd6fd | ||
|
|
ecc62a3ba9 | ||
|
|
aca60465f0 | ||
|
|
57bdca37fc | ||
|
|
6409397770 | ||
|
|
974044adf0 | ||
|
|
59aeeda786 | ||
|
|
b6bf8d992f | ||
|
|
82cb966863 | ||
|
|
9d31edc67a | ||
|
|
1b4f4bdd6d | ||
|
|
c9a879d329 | ||
|
|
ea69b1be00 | ||
|
|
2a8b3a87ea | ||
|
|
52783734ce | ||
|
|
0e1b390ec6 | ||
|
|
ab49bdf82f | ||
|
|
25e7051271 | ||
|
|
85c0a72ae8 | ||
|
|
7315f9adfc | ||
|
|
af1aee4ac3 | ||
|
|
d6ff7eef88 | ||
|
|
7706b4dbc3 | ||
|
|
3561ecd2b7 | ||
|
|
74c7ff7fdf | ||
|
|
a68ce353ad | ||
|
|
0d168f3445 | ||
|
|
073e04a3cc | ||
|
|
591dc048d4 | ||
|
|
bfe8d3b37a | ||
|
|
9695dba415 | ||
|
|
6a48f0502e | ||
|
|
4a8d6592ab | ||
|
|
434aebd641 | ||
|
|
c124c38e70 | ||
|
|
e6d60bb124 | ||
|
|
99afb88957 | ||
|
|
085ea2028c | ||
|
|
0e7c600cf7 | ||
|
|
3f3532defe | ||
|
|
a0ffc88e47 | ||
|
|
11bfaf0e91 | ||
|
|
189093d548 | ||
|
|
34eb89b1b6 | ||
|
|
b725d242d3 | ||
|
|
568e88314b | ||
|
|
70e94205d1 | ||
|
|
aaad952782 | ||
|
|
dc27911174 | ||
|
|
42678ae8e1 | ||
|
|
b6341992f6 | ||
|
|
17fd0f96df | ||
|
|
0b039e8d8c | ||
|
|
eab8a6d96b | ||
|
|
9f72bf117e | ||
|
|
058e9436f0 | ||
|
|
ebd8fe0e4f | ||
|
|
7970f5f691 | ||
|
|
0689ca86c5 | ||
|
|
5783e0f182 | ||
|
|
e190aa8d03 | ||
|
|
15bac02cbf | ||
|
|
7fdacce9a2 | ||
|
|
78ea179e9a | ||
|
|
2cfc3fadb2 | ||
|
|
60cdee2ea6 | ||
|
|
a17ac6860d | ||
|
|
ef24466702 | ||
|
|
de78027b0e | ||
|
|
28c40a0ad2 | ||
|
|
230bbb2239 | ||
|
|
bda2913a32 | ||
|
|
78daaf6e5b | ||
|
|
8939167a39 | ||
|
|
b48c6111d9 | ||
|
|
de1e919b96 | ||
|
|
35a96027f1 | ||
|
|
8e12d71580 | ||
|
|
7a36bfa2ff | ||
|
|
dfeac0cb7f | ||
|
|
dfec780157 | ||
|
|
4cc93324f8 | ||
|
|
bf8833eae6 | ||
|
|
00028010ee | ||
|
|
a681a76c4b | ||
|
|
cce812ff11 | ||
|
|
ccb4bd595c | ||
|
|
dc7eedcd8f | ||
|
|
40faa78929 | ||
|
|
eb53db00d0 | ||
|
|
658ef39736 | ||
|
|
0e90bcb2ef | ||
|
|
a020fce2d7 | ||
|
|
525ccf7aba | ||
|
|
ebcfc16f63 | ||
|
|
4b825efffb | ||
|
|
71871016d2 | ||
|
|
c07ef23d5b | ||
|
|
cbe884ad63 | ||
|
|
1dd703b337 | ||
|
|
bcb1d98191 | ||
|
|
1f5486e8cc | ||
|
|
1371be6f2c | ||
|
|
b0d8ed6248 | ||
|
|
379f4585e3 | ||
|
|
45a71981c8 | ||
|
|
ac3409f644 | ||
|
|
11c33f81dd | ||
|
|
641cc860d8 | ||
|
|
2029bd6e8a | ||
|
|
1ca5e1218d | ||
|
|
887c704f63 | ||
|
|
567dc74cd7 | ||
|
|
53606d5055 | ||
|
|
19a6c21c44 | ||
|
|
3a4b11e4b3 | ||
|
|
d981574ab2 | ||
|
|
eb817739b9 | ||
|
|
8972f17638 | ||
|
|
4d997d9473 | ||
|
|
e79c86c7b6 | ||
|
|
96bb86536f | ||
|
|
2daf6c8b3f | ||
|
|
24b46158de | ||
|
|
63d336ba28 | ||
|
|
8dd3a92671 | ||
|
|
8e8897c70d | ||
|
|
eac513411e | ||
|
|
98f9274085 | ||
|
|
884dd9fc3b | ||
|
|
04c564936a | ||
|
|
957138960a | ||
|
|
58a0297915 | ||
|
|
61bf536207 | ||
|
|
4136a7b372 | ||
|
|
b3afc9961d | ||
|
|
27e2e042da | ||
|
|
cc659bad30 | ||
|
|
776931c0ad | ||
|
|
e22b4b6fe1 | ||
|
|
cad534bffe | ||
|
|
3b44adb6fa | ||
|
|
cc0bd1473f | ||
|
|
58124d27bf | ||
|
|
9ad118814a | ||
|
|
270f8dd093 | ||
|
|
fde0ae4781 | ||
|
|
b87ce8c75d | ||
|
|
e372c87836 | ||
|
|
f8478d7742 | ||
|
|
a3d6923fec | ||
|
|
7bfa22187d | ||
|
|
7a8affae32 | ||
|
|
e48dd7c4c4 | ||
|
|
6d298c1746 | ||
|
|
4fe9ab92e0 | ||
|
|
f2ac4cdc64 | ||
|
|
be902401d1 | ||
|
|
020e93bca2 | ||
|
|
61dfdc2258 | ||
|
|
a98e3c8eab | ||
|
|
58a90ff706 | ||
|
|
d0bf12f3d4 | ||
|
|
af16785bbf | ||
|
|
18f6e83a2c | ||
|
|
147d97421b | ||
|
|
dcf2feb7aa | ||
|
|
e63e940b96 | ||
|
|
68b0fb83ee | ||
|
|
f15e5f060a | ||
|
|
e2a403441f | ||
|
|
9b3665b9c6 | ||
|
|
f4c8bf9206 | ||
|
|
c033db9c31 | ||
|
|
9300526f49 | ||
|
|
b6dd1f7f2d | ||
|
|
1e6eddca04 | ||
|
|
19ca0d7dd7 | ||
|
|
7fcb5d1c0c | ||
|
|
dd995ec28a | ||
|
|
af99de3535 | ||
|
|
3567cc2fb0 | ||
|
|
5e9d9bd264 | ||
|
|
62447c1bb9 | ||
|
|
a86434c9a2 | ||
|
|
b7b70299f7 | ||
|
|
eb02e0aec1 | ||
|
|
7816e53db7 | ||
|
|
7ae08e572a | ||
|
|
47d15d8adb | ||
|
|
0dac0e87e4 | ||
|
|
b290099d5b | ||
|
|
f6b1a41a0b | ||
|
|
e529af04cf | ||
|
|
6c365dd949 | ||
|
|
32f15fc557 | ||
|
|
6f06af1d5f | ||
|
|
a91f99faab | ||
|
|
17a87b23a1 | ||
|
|
d4b5ec2ae2 | ||
|
|
1bcaf09855 | ||
|
|
fa3842b2b4 | ||
|
|
ea72256e56 | ||
|
|
be5828cb3e | ||
|
|
104b72505c | ||
|
|
23728a0686 | ||
|
|
eaaa856b13 | ||
|
|
01e81d02b5 | ||
|
|
9ae8d0a512 | ||
|
|
da16322fb8 | ||
|
|
a6d39afc26 | ||
|
|
1b6a124f54 | ||
|
|
8aff6bf8f3 | ||
|
|
21115752aa | ||
|
|
025c2b88b9 | ||
|
|
655f9d7c97 | ||
|
|
03b5e36114 | ||
|
|
9c78c1e9ca | ||
|
|
136186a526 | ||
|
|
1f6b02a671 | ||
|
|
76a0518d52 | ||
|
|
a688a69af6 | ||
|
|
b890ffc5f3 | ||
|
|
b98558107e | ||
|
|
3e360ad0fb | ||
|
|
a7ced407f8 | ||
|
|
45a1e137d1 | ||
|
|
a44e0d81cc | ||
|
|
749fc6699c | ||
|
|
d7bb147a23 | ||
|
|
08b0b254f2 | ||
|
|
5414a4c3f1 | ||
|
|
7944ab329d | ||
|
|
8ca14a434c | ||
|
|
e24ba68fec | ||
|
|
b205326782 | ||
|
|
daa1960dff | ||
|
|
894c7f6972 | ||
|
|
737dcc76b8 | ||
|
|
b42f17916b | ||
|
|
2c6ce6fd07 | ||
|
|
f243f4b8ea | ||
|
|
02e881ebc0 | ||
|
|
7b8f10a14e | ||
|
|
d995c31b7b | ||
|
|
45154cb55c | ||
|
|
48067b10cd | ||
|
|
f24c40d780 | ||
|
|
f4edd36744 | ||
|
|
a202a6abdc | ||
|
|
c25adfe28a | ||
|
|
822c6cc45c | ||
|
|
c48b44d06a | ||
|
|
21a59fbd35 | ||
|
|
cdcf940d12 | ||
|
|
6cefc44fb6 | ||
|
|
8308f159ad | ||
|
|
e5ff8f2164 | ||
|
|
a000252c8a | ||
|
|
9a42c580cf | ||
|
|
6e0a7a742e | ||
|
|
026bac121f | ||
|
|
8a26b817f7 | ||
|
|
528a4435a9 | ||
|
|
9b791bb7a7 | ||
|
|
58eb0b1292 | ||
|
|
39eda4a4be | ||
|
|
87a6445ff6 | ||
|
|
b7e6d33d48 | ||
|
|
52e9cd20ee | ||
|
|
b34bd5d988 | ||
|
|
58d5e7bc58 | ||
|
|
1e09bfbb80 | ||
|
|
91fa1c066c | ||
|
|
10d49b55d1 | ||
|
|
67d0bf6a27 | ||
|
|
d36cbde7aa | ||
|
|
016f4088d4 | ||
|
|
c2d1bbb4be | ||
|
|
bc6a01444a | ||
|
|
24f0902194 | ||
|
|
19fd6a10dd | ||
|
|
a45a5296e4 | ||
|
|
1459bb15c1 | ||
|
|
ce5f3c463b | ||
|
|
04ebbf35e7 | ||
|
|
ba1d6e30e2 | ||
|
|
e9ac8933f9 | ||
|
|
ec96b7f878 | ||
|
|
ba7d429178 | ||
|
|
79be2089be | ||
|
|
ea2305de47 | ||
|
|
635b2c35a8 | ||
|
|
afdc4bb3f1 | ||
|
|
3d49ec72ba | ||
|
|
520f818adc | ||
|
|
20b054472e | ||
|
|
21a70c5655 | ||
|
|
08270f5d6d | ||
|
|
9923be2f1d | ||
|
|
5df57c95d4 | ||
|
|
d8c91466d0 | ||
|
|
37b2e370fe | ||
|
|
98dfba0640 | ||
|
|
574c9084c2 | ||
|
|
9862ae8e7a | ||
|
|
7bc4e63209 | ||
|
|
ad40150cfa | ||
|
|
970530d99f | ||
|
|
c6ae989b82 | ||
|
|
a0ebf354b1 | ||
|
|
97ecbc792e | ||
|
|
5934a59489 | ||
|
|
ee18046581 | ||
|
|
1e4e38c02f | ||
|
|
7a3038eceb | ||
|
|
7d2f86b72e | ||
|
|
3aaa319505 | ||
|
|
f4075f0a51 | ||
|
|
59643a8d52 | ||
|
|
baf2a880e4 | ||
|
|
d71e8320c7 | ||
|
|
3b3d924f40 | ||
|
|
60701bc007 | ||
|
|
5ca3e184e0 | ||
|
|
2903d1a0a6 | ||
|
|
5f0f1802a4 | ||
|
|
5be56d9b00 | ||
|
|
d3fd1b5045 | ||
|
|
4615e87483 | ||
|
|
fb9b72deea | ||
|
|
5cc873a6bb | ||
|
|
26745496a5 | ||
|
|
4537725852 | ||
|
|
676bda22ae | ||
|
|
87028e5f35 | ||
|
|
912083d20f | ||
|
|
4b6044dade | ||
|
|
871d3b537d | ||
|
|
01f8a514a1 | ||
|
|
7dbd77d2a4 | ||
|
|
94dde53c21 | ||
|
|
e47eaa3ac3 | ||
|
|
4060299204 | ||
|
|
70a99927cd | ||
|
|
688cee9463 | ||
|
|
fa3e300c37 | ||
|
|
9dbad050fd | ||
|
|
87202115d0 | ||
|
|
abc4cb444a | ||
|
|
78744840eb | ||
|
|
46cfe45cf6 | ||
|
|
91ff41a9d9 | ||
|
|
3869da9d06 | ||
|
|
2530691347 | ||
|
|
c65de27e9d | ||
|
|
9bb715fcb2 | ||
|
|
8c4f39ab49 | ||
|
|
e5206091dd | ||
|
|
e53c6fe64c | ||
|
|
b893b24d88 | ||
|
|
c7caa55223 | ||
|
|
c675019ef3 | ||
|
|
f2fea0a402 | ||
|
|
aeafd631d5 | ||
|
|
aa442632ea | ||
|
|
2a4b5f88a7 | ||
|
|
0e36010937 | ||
|
|
8a06825871 | ||
|
|
ce2f1bcde6 | ||
|
|
c59826471c | ||
|
|
9c8f64f602 | ||
|
|
d4342a77c8 | ||
|
|
054d2296c4 | ||
|
|
ae116981ef | ||
|
|
becb886ab7 | ||
|
|
3502ac9263 | ||
|
|
c64cb94b7d | ||
|
|
dffb28816d | ||
|
|
6e521f07c4 | ||
|
|
c05313aefc | ||
|
|
db0ca1cb9b | ||
|
|
584a73255a | ||
|
|
3a66385059 | ||
|
|
7e4f83c2f5 | ||
|
|
9971c82e99 | ||
|
|
b418525b3b | ||
|
|
f5c28107d4 | ||
|
|
793f928e6e | ||
|
|
a8fb2e37d5 | ||
|
|
11fed9394a | ||
|
|
bcdd0c66c0 | ||
|
|
e988215d8a | ||
|
|
f616643335 | ||
|
|
816f93222b | ||
|
|
9e85b1240a | ||
|
|
c5059b4568 | ||
|
|
2ae304dcf5 | ||
|
|
ffc15aa57a | ||
|
|
c9c02daff7 | ||
|
|
f6f3e2377e | ||
|
|
ed3226e3a0 | ||
|
|
8f5200e79d | ||
|
|
2753552997 | ||
|
|
a061ea7125 | ||
|
|
068d57f382 | ||
|
|
c76a635414 | ||
|
|
1b5f6b4683 | ||
|
|
891adf3397 | ||
|
|
190e7e76d3 | ||
|
|
eb47d5139f | ||
|
|
288910e67e | ||
|
|
1a0feb4f19 | ||
|
|
1cba7d3e1d | ||
|
|
0ff99b716c | ||
|
|
a537e785fb | ||
|
|
dbbc5ea127 | ||
|
|
3e6713d16c | ||
|
|
a86aa8034d | ||
|
|
c292ecd54b | ||
|
|
7bfdfd3c38 | ||
|
|
3f154e3a53 | ||
|
|
06ba8372d0 | ||
|
|
eae9b66eed | ||
|
|
4caca9b06a | ||
|
|
de772d7ddd | ||
|
|
707472ba27 | ||
|
|
46265debc3 | ||
|
|
14d221bf9c | ||
|
|
f4f871002b | ||
|
|
bb92d4aa54 | ||
|
|
acc7253ca1 | ||
|
|
f1002b5f9d | ||
|
|
84cea8e3d6 | ||
|
|
05a0f6fa62 | ||
|
|
2ab2f070b4 | ||
|
|
103de2011b | ||
|
|
3cec93e2f1 | ||
|
|
a23d7ee6e2 | ||
|
|
42afbcf655 | ||
|
|
c61167bdfa | ||
|
|
642cefb84f | ||
|
|
1223aa7c70 | ||
|
|
0d90977917 | ||
|
|
7e57e85e75 | ||
|
|
8f518954aa | ||
|
|
26144815d2 | ||
|
|
21d07be7df | ||
|
|
04f69f0853 | ||
|
|
0678c81959 | ||
|
|
70a88088f2 | ||
|
|
f8e8c756ab | ||
|
|
6e26fa6e3c | ||
|
|
b54d6e785d | ||
|
|
5d2e22cd86 | ||
|
|
ccd1d3f460 | ||
|
|
964486a819 | ||
|
|
9080f1d2f0 | ||
|
|
aab8dfcde5 | ||
|
|
1633b74ab2 | ||
|
|
bafd67be36 | ||
|
|
8f1d7d081d | ||
|
|
66bfcd1d45 | ||
|
|
d89438a15f | ||
|
|
d77fa5245d | ||
|
|
67115dd8d8 | ||
|
|
cefe125304 | ||
|
|
5a98b3d0bb | ||
|
|
480da7c38b | ||
|
|
d428abc12f | ||
|
|
a08144cc9f | ||
|
|
0868c22ac6 | ||
|
|
8ff17650dd | ||
|
|
5e037d7ab9 | ||
|
|
8d2ddc2f22 | ||
|
|
7963474bf0 | ||
|
|
a8aa6d6329 | ||
|
|
5b52b806ae | ||
|
|
b059713df5 | ||
|
|
48ec76ab38 | ||
|
|
fe446d60d7 | ||
|
|
9e2e0d4591 | ||
|
|
87935a23d4 | ||
|
|
2b178dcbd7 | ||
|
|
05cdc99079 | ||
|
|
51bca222c0 | ||
|
|
61bbe57018 | ||
|
|
9997539736 | ||
|
|
d142917afc | ||
|
|
41c1316575 | ||
|
|
d4be57f59e | ||
|
|
6a047d14a0 | ||
|
|
0d1b58542b | ||
|
|
bb05957d33 | ||
|
|
157b071a78 | ||
|
|
927b5e6e38 | ||
|
|
fb0fb551ff | ||
|
|
db02a306e9 | ||
|
|
7d84891813 | ||
|
|
e1a33003f0 | ||
|
|
64c7a171ae | ||
|
|
6a3cb6d9d9 | ||
|
|
efed040e9e | ||
|
|
2148f1031f | ||
|
|
6659456cb3 | ||
|
|
207cc585d9 | ||
|
|
df80124c57 | ||
|
|
dd1155dc89 | ||
|
|
4bbed42fb8 | ||
|
|
d70beb57fc |
67
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
67
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
name: 🐞 Bug Report
|
||||
description: Use this form if you think you found a bug / Verwende dieses Formular, wenn Du denkst, dass Du einen Fehler gefunden hast.
|
||||
labels: bug
|
||||
body:
|
||||
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you for taking your time to report a bug.
|
||||
|
||||
Before you proceed, please check:
|
||||
- [ ] Do you use the [latest version](https://github.com/jomjol/AI-on-the-edge-device/releases)?
|
||||
- [ ] Is there already another similar issue? Check for open and closed [Issue](https://github.com/jomjol/AI-on-the-edge-device/issues) and [Discussions](https://github.com/jomjol/AI-on-the-edge-device/discussions).
|
||||
- [ ] Are instructions in the [README](https://github.com/jomjol/AI-on-the-edge-device/tree/master#readme) solving your issue?
|
||||
- [ ] Are instructions in the [Wiki](https://github.com/jomjol/AI-on-the-edge-device/wiki) solving your issue?
|
||||
|
||||
Du darfst gerne auch in Deutsch schreiben!
|
||||
|
||||
|
||||
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: The Problem
|
||||
description: A clear and concise description of what the bug is.
|
||||
|
||||
|
||||
|
||||
- type: input
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Version
|
||||
description: Which version are you using? (See menu `System > Info`).
|
||||
|
||||
|
||||
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Logfile
|
||||
description: Add the logfile (See menu `System > Log Viewer`) to help explain your problem. This will be automatically formatted into code, so no need to format it on your side.
|
||||
render: shell
|
||||
|
||||
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: A clear and concise description of what you expected to happen.
|
||||
|
||||
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Screenshots
|
||||
description: If applicable, add screenshots to help explain your problem.
|
||||
|
||||
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional Context
|
||||
description: Add any other context about the problem here.
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 💬 Open a new Discussion
|
||||
url: https://github.com/jomjol/AI-on-the-edge-device/discussions/categories/q-a
|
||||
about: Use this form if you have a question or need help setting your AI-on-the-edge-device it up
|
||||
23
.github/ISSUE_TEMPLATE/feature.yaml
vendored
Normal file
23
.github/ISSUE_TEMPLATE/feature.yaml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
name: 💡 Feature Request
|
||||
description: Use this form if you have an idea or wish for a new feature
|
||||
labels: enhancement
|
||||
body:
|
||||
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you for taking your time to document your idea.
|
||||
|
||||
Before you proceed, please check:
|
||||
- [ ] Is there already another similar issue open or closed? Check for open and closed [Issue](https://github.com/jomjol/AI-on-the-edge-device/issues) and [Discussions](https://github.com/jomjol/AI-on-the-edge-device/discussions).
|
||||
- [ ] Is there already another similar [discussion](https://github.com/jomjol/AI-on-the-edge-device/discussions) ongoing?
|
||||
|
||||
Please be aware that we might decline your request if we have a reason for it!
|
||||
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: The Feature
|
||||
description: A clear and concise description of what the new feature should do. If possible, refer to other projects or add a mockup.
|
||||
19
.github/ISSUE_TEMPLATE/training.yaml
vendored
Normal file
19
.github/ISSUE_TEMPLATE/training.yaml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: 📖 New Training Data
|
||||
description: Use this form if you collected images and want to provide them for training the model
|
||||
title: New Training Data
|
||||
labels: new training data
|
||||
assignees: jomjol
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Before you proceed, please check:
|
||||
- [ ] Did you make sure we don't have similar data yet in the training data? (see [Digital Counters](https://jomjol.github.io/neural-network-digital-counter-readout/) resp. [Analog Needles](https://jomjol.github.io/neural-network-analog-needle-readout))
|
||||
- [ ] Did you follow the guideline as documented in the [Wiki](https://github.com/jomjol/AI-on-the-edge-device/wiki/ROI-Configuration)? We will only be able to accept the files if they fulfill the rules!
|
||||
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Files
|
||||
description: You can drag & drop your **zipped** images here
|
||||
8
.github/ISSUE_TEMPLATE/x_plain.yaml
vendored
Normal file
8
.github/ISSUE_TEMPLATE/x_plain.yaml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
name: 🗒️ Blank Form
|
||||
description: Use this form if none of the others fit. Please only use none of the others fit!
|
||||
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Issue
|
||||
|
||||
123
.github/label-commenter-config.yml
vendored
Normal file
123
.github/label-commenter-config.yml
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
# Reply Bot Configuration
|
||||
# See https://github.com/peaceiris/actions-label-commenter
|
||||
# Make sure to also add the response to .github/workflows/reply-bot.yml!
|
||||
# Due to the way it works, you have to add each response twice, once for the issue, once for the discussions!
|
||||
|
||||
labels:
|
||||
#######################################################################
|
||||
# Bot Response: Documentation
|
||||
#######################################################################
|
||||
- name: bot-reply Documentation
|
||||
labeled:
|
||||
issue:
|
||||
body: |
|
||||
Please have a look on our documentation: https://jomjol.github.io/AI-on-the-edge-device-docs
|
||||
discussion:
|
||||
body: |
|
||||
Please have a look on our documentation: https://jomjol.github.io/AI-on-the-edge-device-docs
|
||||
|
||||
#######################################################################
|
||||
# Bot Response: ROI setup
|
||||
#######################################################################
|
||||
- name: bot-reply ROI Setup
|
||||
labeled:
|
||||
issue:
|
||||
body: |
|
||||
Make sure to setup your ROIs properly. Have a look on our documentation: https://jomjol.github.io/AI-on-the-edge-device-docs/ROI-Configuration/#how-to-setup-the-digit-rois-perfectly
|
||||
discussion:
|
||||
body: |
|
||||
Make sure to setup your ROIs properly. Have a look on our documentation: https://jomjol.github.io/AI-on-the-edge-device-docs/ROI-Configuration/#how-to-setup-the-digit-rois-perfectly
|
||||
|
||||
|
||||
#######################################################################
|
||||
# Bot Response: Logfile
|
||||
#######################################################################
|
||||
- name: bot-reply Logfile
|
||||
labeled:
|
||||
issue:
|
||||
body: |
|
||||
Please provide a logfile!
|
||||
Make sure to first enable the `DEBUG` level in `Settings->Configuration->Debug->Logfile Log Level`!
|
||||
Then wait until the issue arises again.
|
||||
When you copy the log into here, please make sure to use **Fenced code blocks** by wrapping it into separate lines with ` ``` `, see https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#fenced-code-blocks
|
||||
discussion:
|
||||
body: |
|
||||
Please provide a logfile!
|
||||
Make sure to first enable the `DEBUG` level in `Settings->Configuration->Debug->Logfile Log Level`!
|
||||
Then wait until the issue arises again.
|
||||
When you copy the log into here, please make sure to use **Fenced code blocks** by wrapping it into separate lines with ` ``` `, see https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#fenced-code-blocks
|
||||
|
||||
|
||||
#######################################################################
|
||||
# Bot Response: Web Console
|
||||
#######################################################################
|
||||
- name: bot-reply Web Console
|
||||
labeled:
|
||||
issue:
|
||||
body: |
|
||||
You can use the [Web Console](https://jomjol.github.io/AI-on-the-edge-device/index.html) to get USB log from the device.
|
||||
The USB log contains more information about the startup and operation of the device than the normal Web UI log
|
||||
When you copy the log into herm, please make sure to use **Fenced code blocks** by wrapping it into separate lines with ` ``` `, see https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#fenced-code-blocks
|
||||
discussion:
|
||||
body: |
|
||||
You can use the [Web Console](https://jomjol.github.io/AI-on-the-edge-device/index.html) to get USB log from the device.
|
||||
The USB log contains more information about the startup and operation of the device than the normal Web UI log
|
||||
When you copy the log into herm, please make sure to use **Fenced code blocks** by wrapping it into separate lines with ` ``` `, see https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#fenced-code-blocks
|
||||
|
||||
|
||||
#######################################################################
|
||||
# Bot Response: Properly Format Code
|
||||
#######################################################################
|
||||
- name: bot-reply Properly Format Code
|
||||
labeled:
|
||||
issue:
|
||||
body: |
|
||||
Please make sure to use **Fenced code blocks** by wrapping it into separate lines with ` ``` `, see https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#fenced-code-blocks
|
||||
This makes your code or log much easier to read!
|
||||
discussion:
|
||||
body: |
|
||||
Please make sure to use **Fenced code blocks** by wrapping it into separate lines with ` ``` `, see https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#fenced-code-blocks
|
||||
This makes your code or log much easier to read!
|
||||
|
||||
|
||||
#######################################################################
|
||||
# Bot Response: Web Installer
|
||||
#######################################################################
|
||||
- name: bot-reply Web Installer
|
||||
labeled:
|
||||
issue:
|
||||
body: |
|
||||
You can use the [Web Installer](https://jomjol.github.io/AI-on-the-edge-device/index.html) install the firmware onto the ESP32.
|
||||
discussion:
|
||||
body: |
|
||||
You can use the [Web Installer](https://jomjol.github.io/AI-on-the-edge-device/index.html) install the firmware onto the ESP32.
|
||||
|
||||
|
||||
#######################################################################
|
||||
# Bot Response: Rolling Build
|
||||
#######################################################################
|
||||
- name: bot-reply Rolling Build
|
||||
labeled:
|
||||
issue:
|
||||
body: |
|
||||
You can try the latest [Automatic Build](https://github.com/jomjol/AI-on-the-edge-device/actions/workflows/build.yaml?query=branch%3Arolling+event%3Apush) of the the `rolling` or any other branch. It might already contain a fix for your issue.
|
||||
See the [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/rolling-installation) for additional information.
|
||||
discussion:
|
||||
body: |
|
||||
You can try the latest [Automatic Build](https://github.com/jomjol/AI-on-the-edge-device/actions/workflows/build.yaml?query=branch%3Arolling+event%3Apush) of the the `rolling` or any other branch. It might already contain a fix for your issue.
|
||||
See the [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/rolling-installation) for additional information.
|
||||
|
||||
|
||||
#######################################################################
|
||||
# Bot Response: Show Trained Digits/Pointers
|
||||
#######################################################################
|
||||
- name: bot-reply Show Trained Digits/Pointers
|
||||
labeled:
|
||||
issue:
|
||||
body: |
|
||||
See [Digital Digits](https://jomjol.github.io/neural-network-digital-counter-readout) resp. [Analogue Pointers](https://jomjol.github.io/neural-network-analog-needle-readout) for an overview of all trained data.
|
||||
If your type is not contained it can be added to our training material, see [here](https://jomjol.github.io/AI-on-the-edge-device-docs/collect-new-images/).
|
||||
discussion:
|
||||
body: |
|
||||
See [Digital Digits](https://jomjol.github.io/neural-network-digital-counter-readout) resp. [Analogue Pointers](https://jomjol.github.io/neural-network-analog-needle-readout) for an overview of all trained data.
|
||||
If your type is not contained it can be added to our training material, see [here](https://jomjol.github.io/AI-on-the-edge-device-docs/collect-new-images/).
|
||||
429
.github/workflows/build.yml
vendored
Normal file
429
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,429 @@
|
||||
name: Build and Pack
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
#########################################################################################
|
||||
## Build Firmware
|
||||
#########################################################################################
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- id: skip_check
|
||||
uses: fkirc/skip-duplicate-actions@v5
|
||||
with:
|
||||
concurrent_skipping: same_content_newer
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Set Variables
|
||||
id: vars
|
||||
run: |
|
||||
echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Update PIP cache on every commit
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: pip-${{ github.run_id }}
|
||||
restore-keys: pip # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||
|
||||
- name: Update PlatformIO cache on every commit
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: platformio-${{ github.run_id }}
|
||||
restore-keys: platformio # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||
|
||||
- name: Update Build cache on every commit
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ./code/.pio/
|
||||
key: build-${{ github.run_id }}
|
||||
restore-keys: build # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||
|
||||
- name: Update generated-files cache on every commit
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
./code/.pio/build/esp32cam/firmware.bin
|
||||
./code/.pio/build/esp32cam/partitions.bin
|
||||
./code/.pio/build/esp32cam/bootloader.bin
|
||||
./html/*
|
||||
key: generated-files-${{ github.run_id }}
|
||||
restore-keys: generated-files # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Install PlatformIO
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install --upgrade platformio
|
||||
|
||||
- name: Build Firmware
|
||||
#run: echo "Testing... ${{ github.ref_name }}, ${{ steps.vars.outputs.sha_short }}" > ./sd-card/html/version.txt; mkdir -p ./code/.pio/build/esp32cam/; cd ./code/.pio/build/esp32cam/; echo "${{ steps.vars.outputs.sha_short }}" > firmware.bin; cp firmware.bin partitions.bin; cp firmware.bin bootloader.bin # Testing
|
||||
run: cd code; platformio run --environment esp32cam
|
||||
|
||||
- name: Prepare Web UI (generate tooltip pages and update hashes in all files)
|
||||
run: |
|
||||
rm -rf ./html
|
||||
mkdir html
|
||||
cp -r ./sd-card/html/* ./html/
|
||||
|
||||
python -m pip install markdown
|
||||
mkdir html/param-tooltips
|
||||
cd tools/parameter-tooltip-generator
|
||||
python generate-param-doc-tooltips.py
|
||||
cd ../..
|
||||
|
||||
cp -r ./sd-card/html/* ./html/
|
||||
|
||||
echo "Replacing variables..."
|
||||
cd html; find . -type f -exec sed -i 's/$COMMIT_HASH/${{ steps.vars.outputs.sha_short }}/g' {} \;
|
||||
|
||||
|
||||
#########################################################################################
|
||||
## Pack for Update
|
||||
#########################################################################################
|
||||
pack-for-update:
|
||||
# New OTA concept
|
||||
# update__version.zip file with following content:
|
||||
# - /firmware.bin
|
||||
# - (optional) /html/* (inkl. subfolders)
|
||||
# - (optional) /config/*.tfl
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Update generated-files cache on every commit
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
./code/.pio/build/esp32cam/firmware.bin
|
||||
./code/.pio/build/esp32cam/partitions.bin
|
||||
./code/.pio/build/esp32cam/bootloader.bin
|
||||
./html/*
|
||||
key: generated-files-${{ github.run_id }}
|
||||
restore-keys: generated-files # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||
|
||||
- name: Update update cache on every commit
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: update
|
||||
key: update-${{ github.run_id }}
|
||||
restore-keys: update # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||
|
||||
- name: Set Variables
|
||||
id: vars
|
||||
run: |
|
||||
echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
echo "branch=$(echo ${{ github.ref_name }} | tr / __)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Prepare update__*.zip artifact
|
||||
run: |
|
||||
rm -rf ./update
|
||||
mkdir -p ./update
|
||||
cp "./code/.pio/build/esp32cam/firmware.bin" "update/firmware.bin"
|
||||
|
||||
- name: Add Web UI to update
|
||||
run: cp -r ./html ./update/
|
||||
|
||||
- name: Add CNN to update
|
||||
run: |
|
||||
rm -rf ./update/config/
|
||||
mkdir -p ./update/config/
|
||||
cp ./sd-card/config/*.tfl ./update/config/ 2>/dev/null || true
|
||||
cp ./sd-card/config/*.tflite ./update/config/ 2>/dev/null || true
|
||||
|
||||
- name: Upload update as update.zip artifact (Firmware + Web UI + CNN)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "AI-on-the-edge-device__update__${{ steps.vars.outputs.branch }}_(${{ steps.vars.outputs.sha_short }})"
|
||||
path: ./update/*
|
||||
|
||||
|
||||
|
||||
#########################################################################################
|
||||
## Pack for Remote Setup
|
||||
#########################################################################################
|
||||
pack-for-remote_setup:
|
||||
# New Remote Setup concept
|
||||
# remote_setup__version.zip file with following content:
|
||||
# - /firmware.bin
|
||||
# - /html/* (inkl. subfolders)
|
||||
# - /config/*
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Update generated-files cache on every commit
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
./code/.pio/build/esp32cam/firmware.bin
|
||||
./code/.pio/build/esp32cam/partitions.bin
|
||||
./code/.pio/build/esp32cam/bootloader.bin
|
||||
./html/*
|
||||
key: generated-files-${{ github.run_id }}
|
||||
restore-keys: generated-files # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||
|
||||
- name: Update remote_setup cache on every commit
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: remote_setup
|
||||
key: remote_setup-${{ github.run_id }}
|
||||
restore-keys: remote_setup # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||
|
||||
- name: Set Variables
|
||||
id: vars
|
||||
run: |
|
||||
echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
echo "branch=$(echo ${{ github.ref_name }} | tr / __)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Prepare remote_setup__*.zip artifact
|
||||
run: |
|
||||
rm -rf ./remote_setup
|
||||
mkdir -p ./remote_setup
|
||||
|
||||
- name: Add Web UI to remote_setup
|
||||
run: cp -r ./html ./remote_setup/
|
||||
|
||||
- name: Add whole config folder to remote_setup
|
||||
run: |
|
||||
rm -rf ./remote_setup/config/
|
||||
mkdir -p ./remote_setup/config/
|
||||
cp ./sd-card/config/* ./remote_setup/config/ 2>/dev/null || true
|
||||
|
||||
- name: Upload remote_setup as remote_setup.zip artifact (Firmware + Web UI + Config)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "AI-on-the-edge-device__remote-setup__${{ steps.vars.outputs.branch }}_(${{ steps.vars.outputs.sha_short }})"
|
||||
path: ./remote_setup/*
|
||||
|
||||
|
||||
#########################################################################################
|
||||
## Pack for a fresh install (USB flashing) (manual_setup)
|
||||
#########################################################################################
|
||||
pack-for-manual_setup:
|
||||
# creates old style binaries for fresh installation (backward compatible to wiki)
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Update generated-files cache on every commit
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
./code/.pio/build/esp32cam/firmware.bin
|
||||
./code/.pio/build/esp32cam/partitions.bin
|
||||
./code/.pio/build/esp32cam/bootloader.bin
|
||||
./html/*
|
||||
key: generated-files-${{ github.run_id }}
|
||||
restore-keys: generated-files # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||
|
||||
- name: Update manual_setup cache on every commit
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: manual_setup
|
||||
key: manual_setup-${{ github.run_id }}
|
||||
restore-keys: manual_setup # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||
|
||||
- name: Set Variables
|
||||
id: vars
|
||||
run: |
|
||||
echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
echo "branch=$(echo ${{ github.ref_name }} | tr / __)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Prepare manual_setup__*.zip artifact
|
||||
run: |
|
||||
rm -rf manual_setup
|
||||
mkdir -p manual_setup
|
||||
rm -rf manual_setup/*.zip
|
||||
rm -rf release
|
||||
mkdir -p release
|
||||
# copy builds to manual_setup folder
|
||||
cp -f "./code/.pio/build/esp32cam/firmware.bin" "manual_setup/firmware.bin"
|
||||
cp -f "./code/.pio/build/esp32cam/bootloader.bin" "manual_setup/bootloader.bin"
|
||||
cp -f "./code/.pio/build/esp32cam/partitions.bin" "manual_setup/partitions.bin"
|
||||
rm -rf ./sd-card/html
|
||||
cp -r ./html ./sd-card/ # Overwrite the Web UI with the preprocessed files
|
||||
cd sd-card; zip -r ../manual_setup/sd-card.zip *; cd ..
|
||||
cd ./manual_setup
|
||||
|
||||
- name: Upload manual_setup.zip artifact (Firmware + Bootloader + Partitions + Web UI)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "AI-on-the-edge-device__manual-setup__${{ steps.vars.outputs.branch }}_(${{ steps.vars.outputs.sha_short }})"
|
||||
path: ./manual_setup
|
||||
|
||||
|
||||
#########################################################################################
|
||||
## Prepare and create release
|
||||
#########################################################################################
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [pack-for-update, pack-for-manual_setup, pack-for-remote_setup]
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
|
||||
# Sets permissions of the GITHUB_TOKEN to allow updating the branches
|
||||
permissions:
|
||||
contents: write
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Update update cache on every commit
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: update
|
||||
key: update-${{ github.run_id }}
|
||||
restore-keys: update # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||
|
||||
- name: Update remote_setup cache on every commit
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: remote_setup
|
||||
key: remote_setup-${{ github.run_id }}
|
||||
restore-keys: remote_setup # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||
|
||||
- name: Update manual_setup cache on every commit
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: manual_setup
|
||||
key: manual_setup-${{ github.run_id }}
|
||||
restore-keys: manual_setup # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||
|
||||
- name: Set Variables
|
||||
id: vars
|
||||
run: |
|
||||
echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
echo "branch=$(echo ${{ github.ref_name }} | tr / __)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Prepare artifacts for release
|
||||
run: |
|
||||
rm -rf release
|
||||
mkdir -p release
|
||||
|
||||
# create AI-on-the-edge-device__update__*.zip like "AI-on-the-edge-device__update__v13.0.5.zip"
|
||||
cd ./update
|
||||
zip -r ../release/AI-on-the-edge-device__update__${{ steps.vars.outputs.branch }}.zip .
|
||||
|
||||
# create AI-on-the-edge-device__manual-setup__*.zip like "AI-on-the-edge-device__manual-setup__v13.0.5.zip"
|
||||
cd ../manual_setup
|
||||
zip -r ../release/AI-on-the-edge-device__manual-setup__${{ steps.vars.outputs.branch }}.zip .
|
||||
|
||||
# create AI-on-the-edge-device__remote-setup__*.zip like "AI-on-the-edge-device__remote-setup__v13.0.5.zip"
|
||||
cd ../remote_setup
|
||||
zip -r ../release/AI-on-the-edge-device__remote-setup__${{ steps.vars.outputs.branch }}.zip .
|
||||
|
||||
# extract the version used in next step
|
||||
- id: get_version
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
uses: Simply007/get-version-action@v2
|
||||
|
||||
# # the changelog [unreleased] will now be changed to the release version
|
||||
# - name: Update changelog
|
||||
# uses: thomaseizinger/keep-a-changelog-new-release@v1
|
||||
# if: startsWith(github.ref, 'refs/tags/')
|
||||
# with:
|
||||
# changelogPath: Changelog.md
|
||||
# version: ${{ steps.get_version.outputs.version-without-v }}
|
||||
|
||||
# # the release notes will be extracted from changelog
|
||||
# - name: Extract release notes
|
||||
# id: extract-release-notes
|
||||
# if: startsWith(github.ref, 'refs/tags/')
|
||||
# uses: ffurrer2/extract-release-notes@v1
|
||||
# with:
|
||||
# changelog_file: Changelog.md
|
||||
|
||||
# Releases should only be created on master by tagging the last commit.
|
||||
# all artifacts in firmware folder pushed to the release
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
# Note:
|
||||
# If you get the error "Resource not accessible by integration",
|
||||
# The access rights are not sufficient, see
|
||||
# https://github.com/softprops/action-gh-release/issues/232#issuecomment-1131379440
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
name: ${{ steps.get_version.outputs.version-without-v }}
|
||||
body: ${{ steps.extract-release-notes.outputs.release_notes }}
|
||||
files: |
|
||||
release/*
|
||||
|
||||
# # Commit&Push Changelog to master branch. Must be manually merged back to rolling
|
||||
# - name: Commit changes and push changes
|
||||
# if: startsWith(github.ref, 'refs/tags/')
|
||||
# run: |
|
||||
# git config user.name github-actions
|
||||
# git config user.email github-actions@github.com
|
||||
# git add Changelog.md
|
||||
# git commit Changelog.md -m "Update Changelog.md for ${{github.event.inputs.versionIncrement}} release"
|
||||
# git push origin HEAD:master
|
||||
|
||||
|
||||
#########################################################################################
|
||||
## Update the Web Installer on a release
|
||||
#########################################################################################
|
||||
# Make sure to also update update-webinstaller.yml!
|
||||
update-web-installer:
|
||||
needs: [release]
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Get version of last release
|
||||
id: last_release
|
||||
uses: mindojo/get-latest-release@0b8ef1434d7468d6bffcc8263baff5c777f72321
|
||||
with:
|
||||
myToken: ${{ github.token }}
|
||||
exclude_types: "draft|prerelease"
|
||||
view_top: 1
|
||||
|
||||
- name: Add binary to Web Installer and update manifest
|
||||
run: |
|
||||
echo "Updating Web installer to use firmware from ${{ steps.last_release.outputs.tag_name }}..."
|
||||
rm -f docs/binary/firmware.bin
|
||||
wget https://github.com/jomjol/AI-on-the-edge-device/releases/download/${{ steps.last_release.outputs.tag_name }}/AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
|
||||
unzip AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
|
||||
cp -f firmware.bin docs/binary/firmware.bin
|
||||
echo "Updating index and manifest file..."
|
||||
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/index.html
|
||||
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/manifest.json
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v4
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v2
|
||||
with:
|
||||
path: 'docs'
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
30
.github/workflows/clear_cache.yml
vendored
Normal file
30
.github/workflows/clear_cache.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
name: Clear cache
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
actions: write
|
||||
|
||||
jobs:
|
||||
clear-cache:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Clear cache
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
console.log("About to clear")
|
||||
const caches = await github.rest.actions.getActionsCacheList({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
})
|
||||
for (const cache of caches.data.actions_caches) {
|
||||
console.log(cache)
|
||||
github.rest.actions.deleteActionsCacheById({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
cache_id: cache.id,
|
||||
})
|
||||
}
|
||||
console.log("Clear completed")
|
||||
63
.github/workflows/manual-update-webinstaller.yml
vendored
Normal file
63
.github/workflows/manual-update-webinstaller.yml
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
# This updates the Web Installer with the files from the docs folder and the binary of the latest release
|
||||
# it only gets run on:
|
||||
# - Manually triggered
|
||||
# Make sure to also update the lower part of build.yml!
|
||||
|
||||
name: Manual Web Installer Update
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Run on manual trigger
|
||||
# push:
|
||||
# branches:
|
||||
# - rolling
|
||||
# paths:
|
||||
# - docs # The path filter somehow does not work, so lets run it on every change to rolling
|
||||
|
||||
jobs:
|
||||
manually-update-web-installer:
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Get version of last release
|
||||
id: last_release
|
||||
uses: mindojo/get-latest-release@0b8ef1434d7468d6bffcc8263baff5c777f72321
|
||||
with:
|
||||
myToken: ${{ github.token }}
|
||||
exclude_types: "draft|prerelease"
|
||||
view_top: 1
|
||||
|
||||
- name: Add binary to Web Installer and update manifest
|
||||
run: |
|
||||
echo "Updating Web installer to use firmware from ${{ steps.last_release.outputs.tag_name }}..."
|
||||
rm -f docs/binary/firmware.bin
|
||||
wget https://github.com/jomjol/AI-on-the-edge-device/releases/download/${{ steps.last_release.outputs.tag_name }}/AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
|
||||
unzip AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
|
||||
cp -f firmware.bin docs/binary/firmware.bin
|
||||
echo "Updating index and manifest file..."
|
||||
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/index.html
|
||||
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/manifest.json
|
||||
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v2
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v1
|
||||
with:
|
||||
path: 'docs'
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v1
|
||||
|
||||
79
.github/workflows/reply-bot.yml
vendored
Normal file
79
.github/workflows/reply-bot.yml
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
# Reply Bot
|
||||
# It uses the configuration in .github/label-commenter-config.yml
|
||||
# See https://github.com/peaceiris/actions-label-commenter
|
||||
|
||||
name: Reply-Bot
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [labeled]
|
||||
discussion:
|
||||
types: [labeled]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
discussions: write
|
||||
|
||||
jobs:
|
||||
comment:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
|
||||
####################################################################
|
||||
## Remove labels again (issues only)
|
||||
## Make sure to also add the reply message to .github/label-commenter-config.yml!
|
||||
## This currently seems no longer to work due to changes on the actions-cool/issues-helper!
|
||||
####################################################################
|
||||
# - name: Remove 'Logfile' label again (issues only)
|
||||
# if: github.event.label.name == 'bot-reply Logfile'
|
||||
# uses: actions-cool/issues-helper@v3
|
||||
# with:
|
||||
# actions: 'remove-labels'
|
||||
# labels: 'bot-reply Logfile'
|
||||
#
|
||||
# - name: Remove 'Web Console' label again (issues only)
|
||||
# if: github.event.label.name == 'bot-reply Web Console'
|
||||
# uses: actions-cool/issues-helper@v3
|
||||
# with:
|
||||
# actions: 'remove-labels'
|
||||
# labels: 'bot-reply Web Console'
|
||||
#
|
||||
# - name: Remove 'Properly Format Code' label again (issues only)
|
||||
# if: github.event.label.name == 'bot-reply Properly Format Code'
|
||||
# uses: actions-cool/issues-helper@v3
|
||||
# with:
|
||||
# actions: 'remove-labels'
|
||||
# labels: 'bot-reply Properly Format Code'
|
||||
#
|
||||
# - name: Remove 'Web Installer' label again (issues only)
|
||||
# if: github.event.label.name == 'bot-reply Web Installer'
|
||||
# uses: actions-cool/issues-helper@v3
|
||||
# with:
|
||||
# actions: 'remove-labels'
|
||||
# labels: 'bot-reply Web Installer'
|
||||
#
|
||||
# - name: Remove 'Rolling Build' label again (issues only)
|
||||
# if: github.event.label.name == 'bot-reply Rolling Build'
|
||||
# uses: actions-cool/issues-helper@v3
|
||||
# with:
|
||||
# actions: 'remove-labels'
|
||||
# labels: 'bot-reply Rolling Build'
|
||||
#
|
||||
# - name: Remove 'Show Trained Digits/Pointers' label again (issues only)
|
||||
# if: github.event.label.name == 'bot-reply Show Trained Digits/Pointers'
|
||||
# uses: actions-cool/issues-helper@v3
|
||||
# with:
|
||||
# actions: 'remove-labels'
|
||||
# labels: 'bot-reply Show Trained Digits/Pointers'
|
||||
|
||||
####################################################################
|
||||
## Write the response
|
||||
####################################################################
|
||||
- name: Write Response
|
||||
uses: peaceiris/actions-label-commenter@c2d00660c86f2b9ed0fb35b372c451558eba85b3
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
15
.gitignore
vendored
15
.gitignore
vendored
@@ -2,17 +2,26 @@
|
||||
.pio/
|
||||
.vscode/
|
||||
.code-workspace
|
||||
.helper/
|
||||
/sd-card/htm./.vscode/
|
||||
|
||||
/code/build
|
||||
/code/.helper
|
||||
/sd-card/html/debug/
|
||||
/firmware/
|
||||
version.txt
|
||||
/dist/
|
||||
/dist_release/
|
||||
/dist_old_ota
|
||||
CMakeLists.txt.user
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
CMakeScripts
|
||||
Testing
|
||||
Makefile
|
||||
cmake_install.cmake
|
||||
install_manifest.txt
|
||||
compile_commands.json
|
||||
CTestTestfile.cmake
|
||||
_deps
|
||||
code/edgeAI.code-workspace
|
||||
.DS_Store
|
||||
tools/parameter-tooltip-generator/html
|
||||
tools/parameter-tooltip-generator/AI-on-the-edge-device-docs
|
||||
|
||||
12
.gitmodules
vendored
Normal file
12
.gitmodules
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
[submodule "code/components/esp32-camera"]
|
||||
path = code/components/esp32-camera
|
||||
url = https://github.com/espressif/esp32-camera.git
|
||||
[submodule "code/components/esp-nn"]
|
||||
path = code/components/esp-nn
|
||||
url = https://github.com/espressif/esp-nn.git
|
||||
[submodule "code/components/esp-tflite-micro"]
|
||||
path = code/components/esp-tflite-micro
|
||||
url = https://github.com/espressif/esp-tflite-micro.git
|
||||
[submodule "code/components/stb"]
|
||||
path = code/components/stb
|
||||
url = https://github.com/nothings/stb.git
|
||||
1071
Changelog.md
1071
Changelog.md
File diff suppressed because it is too large
Load Diff
314
FeatureRequest.md
Normal file
314
FeatureRequest.md
Normal file
@@ -0,0 +1,314 @@
|
||||
## Feature Requests
|
||||
|
||||
**There are a lot of ideas for further improvements, but only limited capacity on side of the developer.** Therefore I have created this page as a collection of ideas.
|
||||
|
||||
1. Whoever has a new idea can put it here, so that it is not forgotten.
|
||||
|
||||
2. Whoever has the time, capacity and passion to support the project can take any of the ideas and implement them. I will provide support and help wherever I can!
|
||||
|
||||
|
||||
|
||||
____
|
||||
|
||||
|
||||
#### #40 Trigger with cron like exact time slot
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/2470
|
||||
|
||||
|
||||
|
||||
#### #39 upnp implementation to auto detect the device
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/2481
|
||||
|
||||
|
||||
|
||||
#### #38 Energy Saving
|
||||
|
||||
* Deep sleep between recognition
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/2486
|
||||
|
||||
|
||||
|
||||
#### #37 Auto init SD card
|
||||
|
||||
* Fully implement the SD card handling (including formatting) into the firmware
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/2488Demo
|
||||
|
||||
#### #36 Run demo without camera
|
||||
|
||||
Demo mode requires a working camera (if not, one receives a 'Cam bad' error). Would be nice to demo or play around on other ESP32 boards (or on ESP32-CAM boards when you broke the camera cable...).
|
||||
|
||||
#### #35 Use the same model, but provide the image from a Smartphone Camera
|
||||
as reading the Electricity or Water meter every few minutues only delivers apparent accuracy (DE: "Scheingenauigkeit") you could just as well take a picture with your Smartphone evey so often (e.g. once a week when you are in the Basement anyway), then with some "semi clever" tricks pass this image to the model developed here, and the values then on to who ever needs them e.g. via MQTT.
|
||||
IMO: It is not needed to have that many readings (datapoints) as our behaviour (Use of electricity or water) doesn't vary that much, say, over a weeks time. The interpolation between weekly readings will give sufficient information on the power and/or water usage.
|
||||
|
||||
|
||||
#### #34 implement state and Roi for water leak detection
|
||||
for example see Roi on the next picture..
|
||||

|
||||
in case of position change between the measurments set this state to true, if there is no change set it back to false.
|
||||
In a defined time window this movement can lead into an alarm state / water leak..
|
||||
haveing this state in the mqtt broker can trigger functions like closing the ater pipe walve and so on...
|
||||
|
||||
|
||||
|
||||
#### #33 Implement MATTER protocoll
|
||||
|
||||
* see [#1404](https://github.com/jomjol/AI-on-the-edge-device/issues/1404)
|
||||
|
||||
#### #32 Add feature to correct misinterpreted value
|
||||
|
||||
* If a value is misinterpreted, the user can manually correct the value.
|
||||
* The misinterpreted ROIs would be saved in a "training data" -folder on the SD-card
|
||||
* Stretch goal: make sending of saved training data as easy as pushing a button =)
|
||||
|
||||
#### #31 Implement InfluxDB v2.x interface
|
||||
|
||||
* Currently only InfluxDB v1.x is supportet, extend to v2.x
|
||||
* Remark: interface has changed
|
||||
* see [#1160](https://github.com/jomjol/AI-on-the-edge-device/issues/1160)
|
||||
|
||||
#### #30 Support meter clock over
|
||||
|
||||
* In case of meter clocking over, that is, reaching its max. value and starting over from 0,
|
||||
accept the new value and calculate correctly the difference.
|
||||
(see line 739 onwards in ClassFlowPostProcessing.cpp)
|
||||
|
||||
#### ~~#29 Add favicon and use the hostname for the website~~- implemented v11.3.1
|
||||
|
||||
* ~~https://github.com/jomjol/AI-on-the-edge-device/issues/927~~
|
||||
|
||||
#### #28 Improved error handling for ROIs
|
||||
|
||||
* In case a ROI is out of the image, there is no error message, but a non sense image is used
|
||||
* Implement a error message for wrong configuratioin of ROI
|
||||
|
||||
#### #27 Use Homie Spec for Mqtt binding
|
||||
|
||||
* Use the standardized Home Protocol for the Mqtt binding
|
||||
* https://homieiot.github.io/
|
||||
|
||||
#### #26 Changes behaviour for "N" replacement
|
||||
|
||||
* in case the higher digits has already increased by minium 1 - don't set the "N" to the last value, but to "0"
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/792
|
||||
|
||||
|
||||
#### #25 Trigger Measurement via MQTT
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/727
|
||||
|
||||
|
||||
#### #24 Show Mqtt state directly in Webserver
|
||||
|
||||
* Show MQTT log in Web page. E.g. connection established or failed to connect...
|
||||
|
||||
|
||||
|
||||
|
||||
#### #23 CPU Temp and Mqtt values
|
||||
|
||||
* Show the CPU Temp directly in Webpage. Also add the value to MQTT sending
|
||||
|
||||
|
||||
|
||||
#### ~~#22 Direct hint to the different neural network files in the other repositories~~- implemented >v11.3.1
|
||||
|
||||
* ~~https://github.com/jomjol/AI-on-the-edge-device/issues/644~~
|
||||
|
||||
|
||||
|
||||
#### #21 Extended "CheckDigitalConsistency" Logik
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/590
|
||||
|
||||
|
||||
|
||||
#### #20 Deep sleep and push mode
|
||||
|
||||
* Let the device be normally in deep sleep state, and wake it up periodically to collect data and push it via MQTT or HTTP post.
|
||||
* Support ESP-NOW to reduce the overhead of connecting to wifi and mqtt
|
||||
* the above should enable battery powered applications
|
||||
|
||||
* An other way to set deep sleep would be to enable it in a specific period (at night).
|
||||
|
||||
|
||||
#### #19 Extended log informations
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/580
|
||||
|
||||
|
||||
|
||||
#### ~~#18 Document WLAN-strength in web page~~
|
||||
|
||||
* ~~https://github.com/jomjol/AI-on-the-edge-device/issues/563~~
|
||||
|
||||
|
||||
|
||||
#### ~~#17 Direct InfluxDB connection~~
|
||||
|
||||
* ~~Done in v10.6.0~~
|
||||
|
||||
|
||||
#### #16 Serial Communication
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/512
|
||||
* Send the readout value via RX/TX interface with a dedicated TAG
|
||||
* Make dedicated communication FlowModule
|
||||
* Modification of RX/TX communication
|
||||
* Configuration interfache
|
||||
|
||||
|
||||
#### #15 Calibration for FishEye image
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/507
|
||||
|
||||
1. The development of such a correction algorithm with the libraries, that are available for the ESP32 environment.
|
||||
2. New module for integration of the flow into the image processing flow.
|
||||
3. Extension of the configuration (config.ini) and html-pages
|
||||
4. Parameter adjustment and testing for every different fish-eye module
|
||||
5. Maintenance for further updates / modules, ...
|
||||
|
||||
|
||||
|
||||
#### ~~#14 Backup and restore option for configuration~~- implemented v11.3.1
|
||||
|
||||
* ~~https://github.com/jomjol/AI-on-the-edge-device/issues/459~~
|
||||
|
||||
* ~~Implement a zip file compression for store and restore~~
|
||||
|
||||
* ~~Update the html to handle it~~
|
||||
|
||||
|
||||
|
||||
#### #13 Manage non linear gauge without CNN re-training
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/443
|
||||
|
||||
* Implement a look up table for non linear analog meters
|
||||
|
||||
|
||||
|
||||
#### ~~#12 Less reboots due to memory leakage~~
|
||||
|
||||
* ~~Issue: #414 & #425 #430~~
|
||||
|
||||
|
||||
|
||||
#### #11 MQTT - configurable payload
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/344
|
||||
|
||||
|
||||
|
||||
#### #10 Improve and bug fix logging of images
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/307
|
||||
|
||||
|
||||
|
||||
#### #9 Basic auth for the UI
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/283
|
||||
|
||||
* Implementation of an authentication mechanism.
|
||||
|
||||
#### #8 MQTT configurable readout intervall
|
||||
|
||||
Make the readout intervall configurable via MQTT.
|
||||
|
||||
* Change the mqtt part to receive and process input and not only sending
|
||||
|
||||
#### #7 Extended Error Handling
|
||||
|
||||
Check different types of error (e.g. tflite not availabe) and generate an error on the html page.
|
||||
|
||||
To do:
|
||||
|
||||
* Make a list of "important" errors
|
||||
* Implement a checking algo
|
||||
* Extend the firmware and html page for the error handling
|
||||
|
||||
#### ~~#6 Check for double ROI names~~ - implemented v8.0.0
|
||||
|
||||
~~Check during configuration, that ROI names are unique.~~
|
||||
|
||||
~~To do:~~
|
||||
|
||||
* ~~Implementation of ROI name checking in html code before saving analog or digital ROIs~~
|
||||
|
||||
|
||||
|
||||
#### #5 Configurable decimal separator (point or comma)
|
||||
|
||||
Decimal separator configurable for different systems
|
||||
|
||||
To do:
|
||||
|
||||
* Implementation of decimal point into postprocessing module
|
||||
* Extension of configuration
|
||||
* Adaption of the html configuration to implement shifting
|
||||
|
||||
|
||||
|
||||
#### ~~#4 Initial Shifting and Rotation~~ - implemented v7.0.0
|
||||
|
||||
* ~~https://github.com/jomjol/AI-on-the-edge-device/issues/123~~
|
||||
|
||||
~~Implementation of a shifting additional to the initial rotation of the raw camera input~~
|
||||
|
||||
~~To do:~~
|
||||
|
||||
* ~~Implementation of shifting~~
|
||||
* ~~Extension of configuration~~
|
||||
* ~~Adaption of the html configuration to implement shifting~~
|
||||
|
||||
|
||||
|
||||
#### ~~#3 Allow grouping of digits to multiple reading values~~ - implemented v8.0.0
|
||||
|
||||
* ~~https://github.com/jomjol/AI-on-the-edge-device/issues/123~~
|
||||
|
||||
~~Implementation of two different independent readouts in one setup~~
|
||||
|
||||
~~To do:~~
|
||||
|
||||
* ~~Extend the configuration, setting and processing flow for two independend readouts~~
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
____
|
||||
|
||||
#### #2 MQTT-controll with callback
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/105
|
||||
|
||||
Extend the MQTT client to also enable callbacks for configuration setting
|
||||
|
||||
To do:
|
||||
|
||||
* implement callback for receiving information and override `config.ini` settings
|
||||
|
||||
* change configuration management to handle online updates (currently changes need a restart)
|
||||
|
||||
* think about the startup, as there the default config is loaded
|
||||
|
||||
|
||||
|
||||
____
|
||||
|
||||
#### ~~#1 Optional GPIO for external flash/lighting~~ - implemented (v8.0.0)
|
||||
|
||||
* ~~https://github.com/jomjol/AI-on-the-edge-device/issues/133~~
|
||||
|
||||
~~Implementation of an an extrnal flash / lightning through GPIOs.~~
|
||||
|
||||
* ~~available GPIOs: 12 & 13 (currently in use for html switching)~~
|
||||
|
||||
~~To do:~~
|
||||
|
||||
* ~~Implementation of a software module for external light source (e.g. WS8132 LED controller, ...)~~
|
||||
* ~~Update of the camera module to use the external light instead of the internal flash light~~
|
||||
* ~~Adopt the configuration algorithm with a configurable light source~~
|
||||
146
README.md
146
README.md
@@ -1,92 +1,108 @@
|
||||
# AI-on-the-edge-device
|
||||
# Welcome to the AI-on-the-edge-device
|
||||
<img src="images/icon/watermeter.svg" width="100px">
|
||||
|
||||
This is an example of Artificial Intelligence (AI) calculations on a very cheap hardware.
|
||||
Artificial intelligence based systems have become established in our everyday lives. Just think of speech or image recognition. Most of the systems rely on either powerful processors or a direct connection to the cloud for doing the calculations there. With the increasing power of modern processors, the AI systems are coming closer to the end user – which is usually called **edge computing**.
|
||||
Here, this edge computing is put into a practically oriented example, where an AI network is implemented on an ESP32 device so: **AI on the edge**.
|
||||
|
||||
### Details on **function**, **installation** and **configuration** can be found on the **[Wiki Page](https://github.com/jomjol/AI-on-the-edge-device/wiki)**
|
||||
This project allows you to digitize your **analog** water, gas, power and other meters using cheap and easily available hardware.
|
||||
|
||||
A 3d-printable housing can be found here: https://www.thingiverse.com/thing:4571627
|
||||
All you need is an [ESP32 board with a supported camera](https://jomjol.github.io/AI-on-the-edge-device-docs/Hardware-Compatibility/) and something of a practical hand.
|
||||
|
||||
<img src="images/esp32-cam.png" width="200px">
|
||||
|
||||
## Key features
|
||||
- Tensorflow Lite (TFlite) integration – including easy-to-use wrapper
|
||||
- Inline image processing (feature detection, alignment, ROI extraction)
|
||||
- **Small** and **cheap** device (3 x 4.5 x 2 cm³, < 10 EUR)
|
||||
- Integrated camera and illumination
|
||||
- Web interface for administration and control
|
||||
- OTA interface for updating directly via the web interface
|
||||
- Full integration into Homeassistant
|
||||
- Support for Influx DB 1 and 2
|
||||
- MQTT
|
||||
- REST API
|
||||
|
||||
## Workflow
|
||||
The device takes a photo of your meter at a defined interval. It then extracts the Regions of Interest (ROIs) from the image and runs them through artificial intelligence. As a result, you get the digitized value of your meter.
|
||||
|
||||
There are several options for what to do with that value. Either send it to an MQTT broker, write it to an InfluxDb or simply provide access to it via a REST API.
|
||||
|
||||
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/idea.jpg" width="600">
|
||||
|
||||
## Impressions
|
||||
### AI-on-the-edge-device on a Water Meter
|
||||
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/watermeter_all.jpg" width="200"><img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/main.jpg" width="200"><img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/size.png" width="200">
|
||||
|
||||
### Web Interface (Water Meter)
|
||||
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/watermeter.jpg" width="600">
|
||||
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/edit_reference.jpg" width="600">
|
||||
|
||||
### AI-on-the-edge-device on a Electrical Power Meter
|
||||
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/powermeter.jpg" width="600">
|
||||
|
||||
|
||||
## Setup
|
||||
There is growing [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/) which provides you with a lot of information. Head there to get a start, set it up and configure it.
|
||||
|
||||
## Change log
|
||||
There are also articles in the German Heise magazine "make:" about the setup and technical background (behind a paywall): [DIY - Setup](https://www.heise.de/select/make/2021/2/2103513300897420296)
|
||||
|
||||
------
|
||||
A lot of people created useful Youtube videos which might help you getting started.
|
||||
Here a small selection:
|
||||
|
||||
### Known Issues
|
||||
- [youtube.com/watch?v=HKBofb1cnNc](https://www.youtube.com/watch?v=HKBofb1cnNc)
|
||||
- [youtube.com/watch?v=yyf0ORNLCk4](https://www.youtube.com/watch?v=yyf0ORNLCk4)
|
||||
- [youtube.com/watch?v=XxmTubGek6M](https://www.youtube.com/watch?v=XxmTubGek6M)
|
||||
- [youtube.com/watch?v=mDIJEyElkAU](https://www.youtube.com/watch?v=mDIJEyElkAU)
|
||||
- [youtube.com/watch?v=SssiPkyKVVs](https://www.youtube.com/watch?v=SssiPkyKVVs)
|
||||
- [youtube.com/watch?v=MAHE_QyHZFQ](https://www.youtube.com/watch?v=MAHE_QyHZFQ)
|
||||
- [youtube.com/watch?v=Uap_6bwtILQ](https://www.youtube.com/watch?v=Uap_6bwtILQ)
|
||||
|
||||
* Parts of the web page only works correctly in **Firefox** and Chrome!
|
||||
With **Edge** not all parts (especially the configuration) are **not full functional**.
|
||||
* spontaneous reboot, especially in case of intensive web server access (improved since v2.0.0)
|
||||
For further background information, head to [Neural Networks](https://www.heise.de/select/make/2021/6/2126410443385102621), [Training Neural Networks](https://www.heise.de/select/make/2022/1/2134114065999161585) and [Programming on the ESP32](https://www.heise.de/select/make/2022/2/2204010051597422030).
|
||||
|
||||
------
|
||||
### Download
|
||||
The latest available version can be found on the [Releases page](https://github.com/jomjol/AI-on-the-edge-device/releases).
|
||||
|
||||
**General remark:** Beside the `firmware.bin`, typically also the content of `/html` needs to be updated!
|
||||
### Flashing the ESP32
|
||||
Initially you will have to flash the ESP32 via a USB connection. Later updates are possible directly over the air (OTA using WIFI).
|
||||
|
||||
There are different ways to flash your ESP32:
|
||||
- The prefered way is the [Web Installer and Console](https://jomjol.github.io/AI-on-the-edge-device/index.html) which is a browser-based tool to flash the ESP32 and extract the log over USB:
|
||||

|
||||
- Flash Tool from Espressif
|
||||
- ESPtool (command-line tool)
|
||||
|
||||
See the [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/) for more information.
|
||||
|
||||
##### Rolling - (2020-09-12)
|
||||
### Flashing the SD Card
|
||||
The SD card can be setup automatically after the firmware got installed. See the [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#remote-setup-using-the-built-in-access-point) for details. For this to work, the SD card must be FAT formated (which is the default on a new SD card).
|
||||
Alternatively the SD card still can be setup manually, see the [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#3-sd-card) for details!
|
||||
|
||||
* based on v2.0.0 (2020-09-12)
|
||||
## Casing
|
||||
Various 3D-printable housing can be found here:
|
||||
- https://www.thingiverse.com/thing:4573481 (Water Meter)
|
||||
- https://www.thingiverse.com/thing:5028229 (Power Meter)
|
||||
- https://www.thingiverse.com/thing:5224101 (Gas Meter)
|
||||
- https://www.thingiverse.com/thing:4571627 (ESP32-cam housing only)
|
||||
|
||||
|
||||
## Donate
|
||||
If you would like to support the developer with a cup of coffee, you can do that via [PayPal](https://www.paypal.com/donate?hosted_button_id=8TRSVYNYKDSWL).
|
||||
|
||||
##### 2.0.0 Layout update (2020-09-12)
|
||||
<a href="https://www.paypal.com/donate?hosted_button_id=8TRSVYNYKDSWL"><img border="0" src="images/paypal.png" width="200px" target="_blank"></a>
|
||||
|
||||
* Update to **new and modern layout**
|
||||
* Support for Chrome improved
|
||||
* Improved robustness: improved error handling in auto flow reduces spontaneous reboots
|
||||
* File server: Option for "DELETE ALL"
|
||||
* WLan: support of spaces in SSID and password
|
||||
* Reference Image: Option for mirror image, option for image update on the fly
|
||||
* additional parameter in `wasserzaehler.html?noerror=true` to suppress an potential error message
|
||||
* bug fixing
|
||||
## Support
|
||||
If you have any technical problems please search the [discussions](https://github.com/jomjol/AI-on-the-edge-device/discussions). In case you found a ug or have a feature request, please open an [issue](https://github.com/jomjol/AI-on-the-edge-device/issues).
|
||||
|
||||
In other cases you can contact the developer via email: <img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/mail.jpg" height="25">
|
||||
|
||||
## Changes and History
|
||||
See [Changelog](Changelog.md).
|
||||
|
||||
##### 1.1.3 (2020-09-09)
|
||||
## Build It Yourself
|
||||
See [Build Instructions](code/README.md).
|
||||
|
||||
* **Bug in configuration of analog ROIs corrected** - correction in v.1.0.2 did not work properly
|
||||
* Improved update page for the web server (`/html` can be updated via a zip-file, which is provided in `/firmware/html.zip`)
|
||||
* Improved Chrome support
|
||||
## Tools
|
||||
* Logfile downloader and combiner (Thx to [reserve85](https://github.com/reserve85))
|
||||
* Files see ['/tools/logfile-tool'](tbd), how-to see [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/outdated--Gasmeter-Log-Downloader/)
|
||||
|
||||
##### 1.1.0 (2020-09-06)
|
||||
|
||||
* Implementation of "delete complete directory"
|
||||
**Attention: beside the `firmware.bin`, also the content of `/html` needs to be updated!**
|
||||
|
||||
|
||||
|
||||
##### 1.0.2 (2020-09-06)
|
||||
|
||||
* Bug in configuration of analog ROIs corrected
|
||||
* minor bug correction
|
||||
|
||||
##### 1.0.1 (2020-09-05)
|
||||
|
||||
* preValue.ini Bug corrected
|
||||
* minor bug correction
|
||||
|
||||
##### 1.0.0 (2020-09-04)
|
||||
|
||||
* **First usable version** - compatible to previous project (https://github.com/jomjol/water-meter-system-complete)
|
||||
* NEW:
|
||||
* no docker container for CNN calculation necessary
|
||||
* web based configuration editor on board
|
||||
|
||||
##### 0.1.0 (2020-08-07)
|
||||
|
||||
* Initial Version
|
||||
|
||||
|
||||
#### [Full Changelog](Changelog.md)
|
||||
|
||||
|
||||
|
||||
## Solved topics
|
||||
|
||||
* n.a.
|
||||
## Additional Ideas
|
||||
There are some ideas and feature requests which are not currently being pursued – mainly due to capacity reasons on the part of the developers.
|
||||
They features are collected in the [issues](https://github.com/jomjol/AI-on-the-edge-device/issues) and in [FeatureRequest.md](FeatureRequest.md).
|
||||
|
||||
12
code/.gitignore
vendored
12
code/.gitignore
vendored
@@ -1,6 +1,16 @@
|
||||
.pio
|
||||
.idea
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
||||
.helper
|
||||
version.cpp
|
||||
sdkconfig.esp32cam
|
||||
sdkconfig.esp32cam-dev
|
||||
sdkconfig.esp32cam-debug
|
||||
sdkconfig.esp32cam-board-rev3
|
||||
sdkconfig.esp32cam-cpu-freq-240
|
||||
sdkconfig.esp32cam-board-rev3-cpu-freq-240
|
||||
sdkconfig.esp32cam-dev-himem
|
||||
sdkconfig.esp32cam-dev-task-analysis
|
||||
sdkconfig.esp32cam-no-softap
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
copy "C:\Users\Muell\Documents\Programmieren\GitHub\AI-on-the-edge-device\code\.pio\build\esp32cam\firmware.bin" "C:\Users\Muell\Documents\Programmieren\GitHub\AI-on-the-edge-device\firmware\firmware.bin"
|
||||
copy "C:\Users\Muell\Documents\Programmieren\GitHub\AI-on-the-edge-device\code\.pio\build\esp32cam\bootloader.bin" "C:\Users\Muell\Documents\Programmieren\GitHub\AI-on-the-edge-device\firmware\bootloader.bin"
|
||||
copy "C:\Users\Muell\Documents\Programmieren\GitHub\AI-on-the-edge-device\code\.pio\build\esp32cam\partitions.bin" "C:\Users\Muell\Documents\Programmieren\GitHub\AI-on-the-edge-device\firmware\partitions.bin"
|
||||
copy "..\..\code\.pio\build\esp32cam\firmware.bin" "..\..\firmware\firmware.bin"
|
||||
copy "..\..\code\.pio\build\esp32cam\bootloader.bin" "..\..\firmware\bootloader.bin"
|
||||
copy "..\..\code\.pio\build\esp32cam\partitions.bin" "..\..\firmware\partitions.bin"
|
||||
1
code/.helper/makezip.bat
Normal file
1
code/.helper/makezip.bat
Normal file
@@ -0,0 +1 @@
|
||||
powershell Compress-Archive -Path "..\..\sd-card\html\*.*" -DestinationPath "..\..\firmware\html.zip"
|
||||
@@ -1,8 +1,22 @@
|
||||
cmake_minimum_required(VERSION 3.16.0)
|
||||
|
||||
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
|
||||
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common components/esp-tflite-micro)
|
||||
|
||||
set(PROJECT_VER "0.0.9.3")
|
||||
ADD_CUSTOM_COMMAND(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/version.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/_version.cpp
|
||||
COMMAND ${CMAKE_COMMAND} -P
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/version.cmake)
|
||||
|
||||
if(EXISTS "${SDKCONFIG}.defaults")
|
||||
if(EXISTS "sdkconfig.defaults")
|
||||
set(SDKCONFIG_DEFAULTS "${SDKCONFIG}.defaults;sdkconfig.defaults")
|
||||
message(STATUS "-- Using defaults: ${SDKCONFIG_DEFAULTS} + sdkconfig.defaults")
|
||||
else()
|
||||
set(SDKCONFIG_DEFAULTS "${SDKCONFIG}.defaults")
|
||||
endif()
|
||||
message(STATUS "-- Using defaults: ${SDKCONFIG_DEFAULTS}")
|
||||
endif()
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(esp32cam-server-only)
|
||||
project(AI-on-the-edge)
|
||||
|
||||
74
code/README.md
Normal file
74
code/README.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# Build
|
||||
|
||||
## Preparations
|
||||
```
|
||||
git clone https://github.com/jomjol/AI-on-the-edge-device.git
|
||||
cd AI-on-the-edge-device
|
||||
git checkout rolling
|
||||
git submodule update --init
|
||||
```
|
||||
|
||||
## Update Submodules
|
||||
```
|
||||
cd /components/submodule-name (e.g. tflite-micro-example)
|
||||
git checkout VERSION (e.g. HASH of latest tflite-micro-example build)
|
||||
cd ../../ (auf Ebene von code)
|
||||
git submodule update --init
|
||||
```
|
||||
Evt. muss man vorher noch einige Verzeichnisse in compenents von Hand löschen, da sie beim checkout nicht gelöscht wurden (vor update -- init)
|
||||
|
||||
## Build and Flash within terminal
|
||||
See further down to build it within an IDE.
|
||||
### Compile
|
||||
```
|
||||
cd code
|
||||
platformio run --environment esp32cam
|
||||
```
|
||||
|
||||
### Upload
|
||||
```
|
||||
pio run --target upload --upload-port /dev/ttyUSB0
|
||||
```
|
||||
|
||||
Alternatively you also can set the UART device in `platformio.ini`, eg. `upload_port = /dev/ttyUSB0`
|
||||
|
||||
### Monitor UART Log
|
||||
```
|
||||
pio device monitor -p /dev/ttyUSB0
|
||||
```
|
||||
|
||||
## Build and Flash with Visual Code IDE
|
||||
|
||||
- Download and install VS Code
|
||||
- https://code.visualstudio.com/Download
|
||||
- Install the VS Code platform io plugin
|
||||
- <img src="https://raw.githubusercontent.com/jomjol/ai-on-the-edge-device/master/images/platformio_plugin.jpg" width="200" align="middle">
|
||||
- Check for error messages, maybe you need to manually add some python libraries
|
||||
- e.g. in my Ubuntu a python3-env was missing: `sudo apt-get install python3-venv`
|
||||
- git clone this project
|
||||
- in Linux:
|
||||
|
||||
```
|
||||
git clone https://github.com/jomjol/AI-on-the-edge-device.git
|
||||
cd AI-on-the-edge-device
|
||||
git checkout rolling
|
||||
git submodule update --init
|
||||
```
|
||||
|
||||
- in VS code, open the `AI-on-the-edge-device/code`
|
||||
- from terminal: `cd AI-on-the-edge-device/code && code .`
|
||||
- open a pio terminal (click on the terminal sign in the bottom menu bar)
|
||||
- make sure you are in the `code` directory
|
||||
- To build, type `platformio run --environment esp32cam`
|
||||
- or use the graphical interface:
|
||||
<img src="https://raw.githubusercontent.com/jomjol/ai-on-the-edge-device/master/images/platformio_build.jpg" width="200" align="middle">
|
||||
- the build artifacts are stored in `code/.pio/build/esp32cam/`
|
||||
- Connect the device and type `pio device monitor`. There you will see your device and can copy the name to the next instruction
|
||||
- Add `upload_port = you_device_port` to the `platformio.ini` file
|
||||
- make sure an sd card with the contents of the `sd_card` folder is inserted and you have changed the wifi details
|
||||
- `pio run --target erase` to erase the flash
|
||||
- `pio run --target upload` this will upload the `bootloader.bin, partitions.bin,firmware.bin` from the `code/.pio/build/esp32cam/` folder.
|
||||
- `pio device monitor` to observe the logs via uart
|
||||
|
||||
# Update Parameters
|
||||
If you create or rename a parameter, make sure to update its documentation in `../param-docs/parameter-pages`! Check the `../param-docs/README.md` for more information.
|
||||
18
code/components/esp-fatfs/CMakeLists.txt
Normal file
18
code/components/esp-fatfs/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
set(srcs "diskio/diskio_mh.c"
|
||||
"diskio/diskio_rawflash_mh.c"
|
||||
"diskio/diskio_sdmmc_mh.c"
|
||||
"diskio/diskio_wl_mh.c"
|
||||
"src/ff_mh.c"
|
||||
"port/freertos/ffsystem_mh.c"
|
||||
"src/ffunicode_mh.c"
|
||||
"vfs/vfs_fat_mh.c"
|
||||
"vfs/vfs_fat_sdmmc_mh.c"
|
||||
"vfs/vfs_fat_spiflash_mh.c")
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
INCLUDE_DIRS "." diskio vfs src
|
||||
REQUIRES wear_levelling esp-sdmmc
|
||||
PRIV_REQUIRES vfs spi_flash
|
||||
)
|
||||
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")
|
||||
278
code/components/esp-fatfs/README.md
Normal file
278
code/components/esp-fatfs/README.md
Normal file
@@ -0,0 +1,278 @@
|
||||
# AIOTED related changes, see https://github.com/jomjol/AI-on-the-edge-device/pull/2781
|
||||
These files/folders were copied from `framework-espidf@3.50002.230601/components/` and adapted to our own needs.
|
||||
Since not every SD/MMC was recognized and this was due to the implementation of ATA trim support, this was revised.
|
||||
Furthermore, files that we don't need were deleted from it.
|
||||
|
||||
## The most relevant changes are:
|
||||
### fatfs/diskio/diskio_sdmmc.c
|
||||
DRESULT ff_sdmmc_ioctl (BYTE pdrv, BYTE cmd, void* buff), at lines 106 to 110 changed from:
|
||||
```c
|
||||
#if FF_USE_TRIM
|
||||
case CTRL_TRIM:
|
||||
return ff_sdmmc_trim (pdrv, *((DWORD*)buff), //start_sector
|
||||
(*((DWORD*)buff + 1) - *((DWORD*)buff) + 1)); //sector_count
|
||||
#endif //FF_USE_TRIM
|
||||
```
|
||||
to:
|
||||
```c
|
||||
#if (FF_USE_TRIM)
|
||||
case CTRL_TRIM:
|
||||
if(FF_CAN_TRIM){
|
||||
return ff_sdmmc_trim (pdrv, *((DWORD*)buff), //start_sector
|
||||
(*((DWORD*)buff + 1) - *((DWORD*)buff) + 1)); //sector_count
|
||||
}
|
||||
else{
|
||||
return RES_ERROR;
|
||||
}
|
||||
#endif //FF_USE_TRIM
|
||||
```
|
||||
|
||||
### fatfs/src/ff.c
|
||||
added:
|
||||
```c
|
||||
#include "sdmmc_cmd.h"
|
||||
```
|
||||
|
||||
static FRESULT remove_chain(FFOBJID* obj, DWORD clst, DWORD pclst), at lines 1437 to 1454 changed from:
|
||||
```c
|
||||
#if FF_FS_EXFAT || FF_USE_TRIM
|
||||
if (ecl + 1 == nxt) { /* Is next cluster contiguous? */
|
||||
ecl = nxt;
|
||||
} else { /* End of contiguous cluster block */
|
||||
#if FF_FS_EXFAT
|
||||
if (fs->fs_type == FS_EXFAT) {
|
||||
res = change_bitmap(fs, scl, ecl - scl + 1, 0); /* Mark the cluster block 'free' on the bitmap */
|
||||
if (res != FR_OK) return res;
|
||||
}
|
||||
#endif
|
||||
#if FF_USE_TRIM
|
||||
rt[0] = clst2sect(fs, scl); /* Start of data area to be freed */
|
||||
rt[1] = clst2sect(fs, ecl) + fs->csize - 1; /* End of data area to be freed */
|
||||
disk_ioctl(fs->pdrv, CTRL_TRIM, rt); /* Inform storage device that the data in the block may be erased */
|
||||
#endif
|
||||
scl = ecl = nxt;
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
to:
|
||||
```c
|
||||
#if FF_FS_EXFAT || FF_USE_TRIM
|
||||
if(FF_FS_EXFAT || FF_CAN_TRIM){
|
||||
if (ecl + 1 == nxt) { /* Is next cluster contiguous? */
|
||||
ecl = nxt;
|
||||
}
|
||||
else { /* End of contiguous cluster block */
|
||||
#if FF_FS_EXFAT
|
||||
if (fs->fs_type == FS_EXFAT) {
|
||||
res = change_bitmap(fs, scl, ecl - scl + 1, 0); /* Mark the cluster block 'free' on the bitmap */
|
||||
if (res != FR_OK) return res;
|
||||
}
|
||||
#endif
|
||||
#if FF_USE_TRIM
|
||||
if(FF_CAN_TRIM){
|
||||
rt[0] = clst2sect(fs, scl); /* Start of data area to be freed */
|
||||
rt[1] = clst2sect(fs, ecl) + fs->csize - 1; /* End of data area to be freed */
|
||||
disk_ioctl(fs->pdrv, CTRL_TRIM, rt); /* Inform storage device that the data in the block may be erased */
|
||||
}
|
||||
#endif
|
||||
scl = ecl = nxt;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
FRESULT f_mkfs(const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len), at lines 5946 to 5949 changed from:
|
||||
```c
|
||||
#if FF_USE_TRIM
|
||||
lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1; /* Inform storage device that the volume area may be erased */
|
||||
disk_ioctl(pdrv, CTRL_TRIM, lba);
|
||||
#endif
|
||||
```
|
||||
to:
|
||||
```c
|
||||
#if FF_USE_TRIM
|
||||
if(FF_CAN_TRIM){
|
||||
lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1; /* Inform storage device that the volume area may be erased */
|
||||
disk_ioctl(pdrv, CTRL_TRIM, lba);
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
FRESULT f_mkfs(const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len), at lines 6175 to 6178 changed from:
|
||||
```c
|
||||
#if FF_USE_TRIM
|
||||
lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1; /* Inform storage device that the volume area may be erased */
|
||||
disk_ioctl(pdrv, CTRL_TRIM, lba);
|
||||
#endif
|
||||
```
|
||||
to:
|
||||
```c
|
||||
#if FF_USE_TRIM
|
||||
if(FF_CAN_TRIM){
|
||||
lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1; /* Inform storage device that the volume area may be erased */
|
||||
disk_ioctl(pdrv, CTRL_TRIM, lba);
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
### sdmmc/sdmmc_cmd.c
|
||||
added:
|
||||
```c
|
||||
int FF_CAN_TRIM = 0;
|
||||
```
|
||||
|
||||
esp_err_t sdmmc_can_trim(sdmmc_card_t* card), at lines 630 to 636 changed from:
|
||||
```c
|
||||
esp_err_t sdmmc_can_trim(sdmmc_card_t* card)
|
||||
{
|
||||
if ((card->is_mmc) && (card->ext_csd.sec_feature & EXT_CSD_SEC_GB_CL_EN)) {
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
```
|
||||
to:
|
||||
```c
|
||||
esp_err_t sdmmc_can_trim(sdmmc_card_t* card)
|
||||
{
|
||||
if ((card->is_mmc) && (card->ext_csd.sec_feature & EXT_CSD_SEC_GB_CL_EN)) {
|
||||
FF_CAN_TRIM = 1;
|
||||
return ESP_OK;
|
||||
}
|
||||
FF_CAN_TRIM = 0;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
```
|
||||
|
||||
### sdmmc/include/sdmmc_cmd.h
|
||||
|
||||
added:
|
||||
```c
|
||||
extern int FF_CAN_TRIM;
|
||||
```
|
||||
|
||||
# Espressif IoT Development Framework
|
||||
|
||||
* [中文版](./README_CN.md)
|
||||
|
||||
ESP-IDF is the development framework for Espressif SoCs supported on Windows, Linux and macOS.
|
||||
|
||||
# ESP-IDF Release Support Schedule
|
||||
|
||||

|
||||
|
||||
- Please read [the support policy](SUPPORT_POLICY.md) and [the documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/versions.html) for more information about ESP-IDF versions.
|
||||
- Please see the [End-of-Life Advisories](https://www.espressif.com/en/support/documents/advisories?keys=&field_type_of_advisory_tid%5B%5D=817) for information about ESP-IDF releases with discontinued support.
|
||||
|
||||
# ESP-IDF Release and SoC Compatibility
|
||||
|
||||
The following table shows ESP-IDF support of Espressif SoCs where ![alt text][preview] and ![alt text][supported] denote preview status and support, respectively. The preview support is usually limited in time and intended for beta versions of chips. Please use an ESP-IDF release where the desired SoC is already supported.
|
||||
|
||||
|Chip | v4.1 | v4.2 | v4.3 | v4.4 | v5.0 | |
|
||||
|:----------- |:---------------------:| :---------------------:| :---------------------:| :---------------------:| :---------------------:|:------------------------------------------------------------------------------------ |
|
||||
|ESP32 |![alt text][supported] | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | |
|
||||
|ESP32-S2 | | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | |
|
||||
|ESP32-C3 | | | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | |
|
||||
|ESP32-S3 | | | | ![alt text][supported] | ![alt text][supported] | [Announcement](https://www.espressif.com/en/news/ESP32_S3) |
|
||||
|ESP32-C2 | | | | | ![alt text][supported] | [Announcement](https://blog.espressif.com/esp32-c2-and-why-it-matter-s-bcf4d7d0b2c6) |
|
||||
|ESP32-H2 | | | | ![alt text][preview] | ![alt text][preview] | [Announcement](https://www.espressif.com/en/news/ESP32_H2) |
|
||||
|
||||
[supported]: https://img.shields.io/badge/-supported-green "supported"
|
||||
[preview]: https://img.shields.io/badge/-preview-orange "preview"
|
||||
|
||||
Espressif SoCs released before 2016 (ESP8266 and ESP8285) are supported by [RTOS SDK](https://github.com/espressif/ESP8266_RTOS_SDK) instead.
|
||||
|
||||
# Developing With ESP-IDF
|
||||
|
||||
## Setting Up ESP-IDF
|
||||
|
||||
See https://idf.espressif.com/ for links to detailed instructions on how to set up the ESP-IDF depending on chip you use.
|
||||
|
||||
**Note:** Each SoC series and each ESP-IDF release has its own documentation. Please see Section [Versions](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/versions.html) on how to find documentation and how to checkout specific release of ESP-IDF.
|
||||
|
||||
### Non-GitHub forks
|
||||
|
||||
ESP-IDF uses relative locations as its submodules URLs ([.gitmodules](.gitmodules)). So they link to GitHub. If ESP-IDF is forked to a Git repository which is not on GitHub, you will need to run the script [tools/set-submodules-to-github.sh](tools/set-submodules-to-github.sh) after git clone.
|
||||
|
||||
The script sets absolute URLs for all submodules, allowing `git submodule update --init --recursive` to complete. If cloning ESP-IDF from GitHub, this step is not needed.
|
||||
|
||||
## Finding a Project
|
||||
|
||||
As well as the [esp-idf-template](https://github.com/espressif/esp-idf-template) project mentioned in Getting Started, ESP-IDF comes with some example projects in the [examples](examples) directory.
|
||||
|
||||
Once you've found the project you want to work with, change to its directory and you can configure and build it.
|
||||
|
||||
To start your own project based on an example, copy the example project directory outside of the ESP-IDF directory.
|
||||
|
||||
# Quick Reference
|
||||
|
||||
See the Getting Started guide links above for a detailed setup guide. This is a quick reference for common commands when working with ESP-IDF projects:
|
||||
|
||||
## Setup Build Environment
|
||||
|
||||
(See the Getting Started guide listed above for a full list of required steps with more details.)
|
||||
|
||||
* Install host build dependencies mentioned in the Getting Started guide.
|
||||
* Run the install script to set up the build environment. The options include `install.bat` or `install.ps1` for Windows, and `install.sh` or `install.fish` for Unix shells.
|
||||
* Run the export script on Windows (`export.bat`) or source it on Unix (`source export.sh`) in every shell environment before using ESP-IDF.
|
||||
|
||||
## Configuring the Project
|
||||
|
||||
* `idf.py set-target <chip_name>` sets the target of the project to `<chip_name>`. Run `idf.py set-target` without any arguments to see a list of supported targets.
|
||||
* `idf.py menuconfig` opens a text-based configuration menu where you can configure the project.
|
||||
|
||||
## Compiling the Project
|
||||
|
||||
`idf.py build`
|
||||
|
||||
... will compile app, bootloader and generate a partition table based on the config.
|
||||
|
||||
## Flashing the Project
|
||||
|
||||
When the build finishes, it will print a command line to use esptool.py to flash the chip. However you can also do this automatically by running:
|
||||
|
||||
`idf.py -p PORT flash`
|
||||
|
||||
Replace PORT with the name of your serial port (like `COM3` on Windows, `/dev/ttyUSB0` on Linux, or `/dev/cu.usbserial-X` on MacOS. If the `-p` option is left out, `idf.py flash` will try to flash the first available serial port.
|
||||
|
||||
This will flash the entire project (app, bootloader and partition table) to a new chip. The settings for serial port flashing can be configured with `idf.py menuconfig`.
|
||||
|
||||
You don't need to run `idf.py build` before running `idf.py flash`, `idf.py flash` will automatically rebuild anything which needs it.
|
||||
|
||||
## Viewing Serial Output
|
||||
|
||||
The `idf.py monitor` target uses the [idf_monitor tool](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/idf-monitor.html) to display serial output from Espressif SoCs. idf_monitor also has a range of features to decode crash output and interact with the device. [Check the documentation page for details](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/idf-monitor.html).
|
||||
|
||||
Exit the monitor by typing Ctrl-].
|
||||
|
||||
To build, flash and monitor output in one pass, you can run:
|
||||
|
||||
`idf.py flash monitor`
|
||||
|
||||
## Compiling & Flashing Only the App
|
||||
|
||||
After the initial flash, you may just want to build and flash just your app, not the bootloader and partition table:
|
||||
|
||||
* `idf.py app` - build just the app.
|
||||
* `idf.py app-flash` - flash just the app.
|
||||
|
||||
`idf.py app-flash` will automatically rebuild the app if any source files have changed.
|
||||
|
||||
(In normal development there's no downside to reflashing the bootloader and partition table each time, if they haven't changed.)
|
||||
|
||||
## Erasing Flash
|
||||
|
||||
The `idf.py flash` target does not erase the entire flash contents. However it is sometimes useful to set the device back to a totally erased state, particularly when making partition table changes or OTA app updates. To erase the entire flash, run `idf.py erase-flash`.
|
||||
|
||||
This can be combined with other targets, ie `idf.py -p PORT erase-flash flash` will erase everything and then re-flash the new app, bootloader and partition table.
|
||||
|
||||
# Resources
|
||||
|
||||
* Documentation for the latest version: https://docs.espressif.com/projects/esp-idf/. This documentation is built from the [docs directory](docs) of this repository.
|
||||
|
||||
* The [esp32.com forum](https://esp32.com/) is a place to ask questions and find community resources.
|
||||
|
||||
* [Check the Issues section on github](https://github.com/espressif/esp-idf/issues) if you find a bug or have a feature request. Please check existing Issues before opening a new one.
|
||||
|
||||
* If you're interested in contributing to ESP-IDF, please check the [Contributions Guide](https://docs.espressif.com/projects/esp-idf/en/latest/contribute/index.html).
|
||||
72
code/components/esp-fatfs/diskio/diskio_impl_mh.h
Normal file
72
code/components/esp-fatfs/diskio/diskio_impl_mh.h
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
typedef unsigned int UINT;
|
||||
typedef unsigned char BYTE;
|
||||
typedef uint32_t DWORD;
|
||||
|
||||
#define FF_DRV_NOT_USED 0xFF
|
||||
|
||||
#include "diskio_mh.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
/**
|
||||
* Structure of pointers to disk IO driver functions.
|
||||
*
|
||||
* See FatFs documentation for details about these functions
|
||||
*/
|
||||
typedef struct {
|
||||
DSTATUS (*init) (unsigned char pdrv); /*!< disk initialization function */
|
||||
DSTATUS (*status) (unsigned char pdrv); /*!< disk status check function */
|
||||
DRESULT (*read) (unsigned char pdrv, unsigned char* buff, uint32_t sector, unsigned count); /*!< sector read function */
|
||||
DRESULT (*write) (unsigned char pdrv, const unsigned char* buff, uint32_t sector, unsigned count); /*!< sector write function */
|
||||
DRESULT (*ioctl) (unsigned char pdrv, unsigned char cmd, void* buff); /*!< function to get info about disk and do some misc operations */
|
||||
} ff_diskio_impl_t;
|
||||
|
||||
/**
|
||||
* Register or unregister diskio driver for given drive number.
|
||||
*
|
||||
* When FATFS library calls one of disk_xxx functions for driver number pdrv,
|
||||
* corresponding function in discio_impl for given pdrv will be called.
|
||||
*
|
||||
* @param pdrv drive number
|
||||
* @param discio_impl pointer to ff_diskio_impl_t structure with diskio functions
|
||||
* or NULL to unregister and free previously registered drive
|
||||
*/
|
||||
void ff_diskio_register(BYTE pdrv, const ff_diskio_impl_t* discio_impl);
|
||||
|
||||
#define ff_diskio_unregister(pdrv_) ff_diskio_register(pdrv_, NULL)
|
||||
|
||||
|
||||
/**
|
||||
* Get next available drive number
|
||||
*
|
||||
* @param out_pdrv pointer to the byte to set if successful
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* ESP_ERR_NOT_FOUND if all drives are attached
|
||||
*/
|
||||
esp_err_t ff_diskio_get_drive(BYTE* out_pdrv);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
117
code/components/esp-fatfs/diskio/diskio_mh.c
Normal file
117
code/components/esp-fatfs/diskio/diskio_mh.c
Normal file
@@ -0,0 +1,117 @@
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2016 */
|
||||
/* ESP-IDF port Copyright 2016 Espressif Systems (Shanghai) PTE LTD */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* If a working storage control module is available, it should be */
|
||||
/* attached to the FatFs via a glue function rather than modifying it. */
|
||||
/* This is an example of glue functions to attach various exsisting */
|
||||
/* storage control modules to the FatFs module with a defined API. */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
#include "diskio_impl_mh.h"
|
||||
#include "ffconf_mh.h"
|
||||
#include "ff_mh.h"
|
||||
|
||||
static ff_diskio_impl_t * s_impls[FF_VOLUMES] = { NULL };
|
||||
|
||||
#if FF_MULTI_PARTITION /* Multiple partition configuration */
|
||||
const PARTITION VolToPart[FF_VOLUMES] = {
|
||||
{0, 0}, /* Logical drive 0 ==> Physical drive 0, auto detection */
|
||||
{1, 0}, /* Logical drive 1 ==> Physical drive 1, auto detection */
|
||||
#if FF_VOLUMES > 2
|
||||
{2, 0}, /* Logical drive 2 ==> Physical drive 2, auto detection */
|
||||
#endif
|
||||
#if FF_VOLUMES > 3
|
||||
{3, 0}, /* Logical drive 3 ==> Physical drive 3, auto detection */
|
||||
#endif
|
||||
#if FF_VOLUMES > 4
|
||||
{4, 0}, /* Logical drive 4 ==> Physical drive 4, auto detection */
|
||||
#endif
|
||||
#if FF_VOLUMES > 5
|
||||
{5, 0}, /* Logical drive 5 ==> Physical drive 5, auto detection */
|
||||
#endif
|
||||
#if FF_VOLUMES > 6
|
||||
{6, 0}, /* Logical drive 6 ==> Physical drive 6, auto detection */
|
||||
#endif
|
||||
#if FF_VOLUMES > 7
|
||||
{7, 0}, /* Logical drive 7 ==> Physical drive 7, auto detection */
|
||||
#endif
|
||||
#if FF_VOLUMES > 8
|
||||
{8, 0}, /* Logical drive 8 ==> Physical drive 8, auto detection */
|
||||
#endif
|
||||
#if FF_VOLUMES > 9
|
||||
{9, 0}, /* Logical drive 9 ==> Physical drive 9, auto detection */
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
esp_err_t ff_diskio_get_drive(BYTE* out_pdrv)
|
||||
{
|
||||
BYTE i;
|
||||
for(i=0; i<FF_VOLUMES; i++) {
|
||||
if (!s_impls[i]) {
|
||||
*out_pdrv = i;
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
void ff_diskio_register(BYTE pdrv, const ff_diskio_impl_t* discio_impl)
|
||||
{
|
||||
assert(pdrv < FF_VOLUMES);
|
||||
|
||||
if (s_impls[pdrv]) {
|
||||
ff_diskio_impl_t* im = s_impls[pdrv];
|
||||
s_impls[pdrv] = NULL;
|
||||
free(im);
|
||||
}
|
||||
|
||||
if (!discio_impl) {
|
||||
return;
|
||||
}
|
||||
|
||||
ff_diskio_impl_t * impl = (ff_diskio_impl_t *)malloc(sizeof(ff_diskio_impl_t));
|
||||
assert(impl != NULL);
|
||||
memcpy(impl, discio_impl, sizeof(ff_diskio_impl_t));
|
||||
s_impls[pdrv] = impl;
|
||||
}
|
||||
|
||||
DSTATUS ff_disk_initialize (BYTE pdrv)
|
||||
{
|
||||
return s_impls[pdrv]->init(pdrv);
|
||||
}
|
||||
DSTATUS ff_disk_status (BYTE pdrv)
|
||||
{
|
||||
return s_impls[pdrv]->status(pdrv);
|
||||
}
|
||||
DRESULT ff_disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count)
|
||||
{
|
||||
return s_impls[pdrv]->read(pdrv, buff, sector, count);
|
||||
}
|
||||
DRESULT ff_disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count)
|
||||
{
|
||||
return s_impls[pdrv]->write(pdrv, buff, sector, count);
|
||||
}
|
||||
DRESULT ff_disk_ioctl (BYTE pdrv, BYTE cmd, void* buff)
|
||||
{
|
||||
return s_impls[pdrv]->ioctl(pdrv, cmd, buff);
|
||||
}
|
||||
|
||||
DWORD get_fattime(void)
|
||||
{
|
||||
time_t t = time(NULL);
|
||||
struct tm tmr;
|
||||
localtime_r(&t, &tmr);
|
||||
int year = tmr.tm_year < 80 ? 0 : tmr.tm_year - 80;
|
||||
return ((DWORD)(year) << 25)
|
||||
| ((DWORD)(tmr.tm_mon + 1) << 21)
|
||||
| ((DWORD)tmr.tm_mday << 16)
|
||||
| (WORD)(tmr.tm_hour << 11)
|
||||
| (WORD)(tmr.tm_min << 5)
|
||||
| (WORD)(tmr.tm_sec >> 1);
|
||||
}
|
||||
99
code/components/esp-fatfs/diskio/diskio_rawflash_mh.c
Normal file
99
code/components/esp-fatfs/diskio/diskio_rawflash_mh.c
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "diskio_impl_mh.h"
|
||||
#include "ffconf_mh.h"
|
||||
#include "ff_mh.h"
|
||||
#include "esp_log.h"
|
||||
#include "diskio_rawflash_mh.h"
|
||||
#include "esp_compiler.h"
|
||||
#include "spi_flash_mmap.h"
|
||||
|
||||
|
||||
static const char* TAG = "diskio_rawflash";
|
||||
|
||||
const esp_partition_t* ff_raw_handles[FF_VOLUMES];
|
||||
|
||||
|
||||
DSTATUS ff_raw_initialize (BYTE pdrv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
DSTATUS ff_raw_status (BYTE pdrv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
DRESULT ff_raw_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count)
|
||||
{
|
||||
ESP_LOGV(TAG, "ff_raw_read - pdrv=%i, sector=%i, count=%in", (unsigned int)pdrv, (unsigned int)sector, (unsigned int)count);
|
||||
const esp_partition_t* part = ff_raw_handles[pdrv];
|
||||
assert(part);
|
||||
esp_err_t err = esp_partition_read(part, sector * SPI_FLASH_SEC_SIZE, buff, count * SPI_FLASH_SEC_SIZE);
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "esp_partition_read failed (0x%x)", err);
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
|
||||
DRESULT ff_raw_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count)
|
||||
{
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
DRESULT ff_raw_ioctl (BYTE pdrv, BYTE cmd, void *buff)
|
||||
{
|
||||
const esp_partition_t* part = ff_raw_handles[pdrv];
|
||||
ESP_LOGV(TAG, "ff_raw_ioctl: cmd=%in", cmd);
|
||||
assert(part);
|
||||
switch (cmd) {
|
||||
case CTRL_SYNC:
|
||||
return RES_OK;
|
||||
case GET_SECTOR_COUNT:
|
||||
*((DWORD *) buff) = part->size / SPI_FLASH_SEC_SIZE;
|
||||
return RES_OK;
|
||||
case GET_SECTOR_SIZE:
|
||||
*((WORD *) buff) = SPI_FLASH_SEC_SIZE;
|
||||
return RES_OK;
|
||||
case GET_BLOCK_SIZE:
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t ff_diskio_register_raw_partition(BYTE pdrv, const esp_partition_t* part_handle)
|
||||
{
|
||||
if (pdrv >= FF_VOLUMES) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
static const ff_diskio_impl_t raw_impl = {
|
||||
.init = &ff_raw_initialize,
|
||||
.status = &ff_raw_status,
|
||||
.read = &ff_raw_read,
|
||||
.write = &ff_raw_write,
|
||||
.ioctl = &ff_raw_ioctl
|
||||
};
|
||||
ff_diskio_register(pdrv, &raw_impl);
|
||||
ff_raw_handles[pdrv] = part_handle;
|
||||
return ESP_OK;
|
||||
|
||||
}
|
||||
|
||||
|
||||
BYTE ff_diskio_get_pdrv_raw(const esp_partition_t* part_handle)
|
||||
{
|
||||
for (int i = 0; i < FF_VOLUMES; i++) {
|
||||
if (part_handle == ff_raw_handles[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0xff;
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
@@ -11,19 +11,27 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#ifndef _CONVERSIONS_YUV_H_
|
||||
#define _CONVERSIONS_YUV_H_
|
||||
|
||||
#ifndef _DISKIO_RAWFLASH_DEFINED
|
||||
#define _DISKIO_RAWFLASH_DEFINED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include "esp_partition.h"
|
||||
|
||||
void yuv2rgb(uint8_t y, uint8_t u, uint8_t v, uint8_t *r, uint8_t *g, uint8_t *b);
|
||||
/**
|
||||
* Register spi flash partition
|
||||
*
|
||||
* @param pdrv drive number
|
||||
* @param part_handle pointer to raw flash partition.
|
||||
*/
|
||||
esp_err_t ff_diskio_register_raw_partition(unsigned char pdrv, const esp_partition_t* part_handle);
|
||||
unsigned char ff_diskio_get_pdrv_raw(const esp_partition_t* part_handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _CONVERSIONS_YUV_H_ */
|
||||
#endif // _DISKIO_RAWFLASH_DEFINED
|
||||
147
code/components/esp-fatfs/diskio/diskio_sdmmc_mh.c
Normal file
147
code/components/esp-fatfs/diskio/diskio_sdmmc_mh.c
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "diskio_impl_mh.h"
|
||||
#include "ffconf_mh.h"
|
||||
#include "ff_mh.h"
|
||||
#include "sdmmc_cmd_mh.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_compiler.h"
|
||||
|
||||
static sdmmc_card_t* s_cards[FF_VOLUMES] = { NULL };
|
||||
static bool s_disk_status_check_en[FF_VOLUMES] = { };
|
||||
|
||||
static const char* TAG = "diskio_sdmmc";
|
||||
|
||||
//Check if SD/MMC card is present
|
||||
static DSTATUS ff_sdmmc_card_available(BYTE pdrv)
|
||||
{
|
||||
sdmmc_card_t* card = s_cards[pdrv];
|
||||
assert(card);
|
||||
esp_err_t err = sdmmc_get_status(card);
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "Check status failed (0x%x)", err);
|
||||
return STA_NOINIT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ff_sdmmc_status() and ff_sdmmc_initialize() return STA_NOINIT when sdmmc_get_status()
|
||||
* fails. This error value is checked throughout the FATFS code.
|
||||
* Both functions return 0 on success.
|
||||
*/
|
||||
DSTATUS ff_sdmmc_initialize (BYTE pdrv)
|
||||
{
|
||||
return ff_sdmmc_card_available(pdrv);
|
||||
}
|
||||
|
||||
DSTATUS ff_sdmmc_status(BYTE pdrv)
|
||||
{
|
||||
if (s_disk_status_check_en[pdrv]) {
|
||||
return ff_sdmmc_card_available(pdrv);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
DRESULT ff_sdmmc_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count)
|
||||
{
|
||||
sdmmc_card_t* card = s_cards[pdrv];
|
||||
assert(card);
|
||||
esp_err_t err = sdmmc_read_sectors(card, buff, sector, count);
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "sdmmc_read_blocks failed (%d)", err);
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
DRESULT ff_sdmmc_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count)
|
||||
{
|
||||
sdmmc_card_t* card = s_cards[pdrv];
|
||||
assert(card);
|
||||
esp_err_t err = sdmmc_write_sectors(card, buff, sector, count);
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "sdmmc_write_blocks failed (%d)", err);
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
#if (FF_USE_TRIM)
|
||||
DRESULT ff_sdmmc_trim (BYTE pdrv, DWORD start_sector, DWORD sector_count)
|
||||
{
|
||||
sdmmc_card_t* card = s_cards[pdrv];
|
||||
assert(card);
|
||||
sdmmc_erase_arg_t arg;
|
||||
|
||||
arg = sdmmc_can_discard(card) == ESP_OK ? SDMMC_DISCARD_ARG : SDMMC_ERASE_ARG;
|
||||
esp_err_t err = sdmmc_erase_sectors(card, start_sector, sector_count, arg);
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "sdmmc_erase_sectors failed (%d)", err);
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_OK;
|
||||
}
|
||||
#endif //FF_USE_TRIM
|
||||
|
||||
DRESULT ff_sdmmc_ioctl (BYTE pdrv, BYTE cmd, void* buff)
|
||||
{
|
||||
sdmmc_card_t* card = s_cards[pdrv];
|
||||
assert(card);
|
||||
switch(cmd) {
|
||||
case CTRL_SYNC:
|
||||
return RES_OK;
|
||||
case GET_SECTOR_COUNT:
|
||||
*((DWORD*) buff) = card->csd.capacity;
|
||||
return RES_OK;
|
||||
case GET_SECTOR_SIZE:
|
||||
*((WORD*) buff) = card->csd.sector_size;
|
||||
return RES_OK;
|
||||
case GET_BLOCK_SIZE:
|
||||
return RES_ERROR;
|
||||
#if (FF_USE_TRIM)
|
||||
case CTRL_TRIM:
|
||||
if(FF_CAN_TRIM){
|
||||
return ff_sdmmc_trim (pdrv, *((DWORD*)buff), //start_sector
|
||||
(*((DWORD*)buff + 1) - *((DWORD*)buff) + 1)); //sector_count
|
||||
}
|
||||
else{
|
||||
return RES_ERROR;
|
||||
}
|
||||
#endif //FF_USE_TRIM
|
||||
}
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
void ff_sdmmc_set_disk_status_check(BYTE pdrv, bool enable)
|
||||
{
|
||||
s_disk_status_check_en[pdrv] = enable;
|
||||
}
|
||||
|
||||
void ff_diskio_register_sdmmc(BYTE pdrv, sdmmc_card_t* card)
|
||||
{
|
||||
static const ff_diskio_impl_t sdmmc_impl = {
|
||||
.init = &ff_sdmmc_initialize,
|
||||
.status = &ff_sdmmc_status,
|
||||
.read = &ff_sdmmc_read,
|
||||
.write = &ff_sdmmc_write,
|
||||
.ioctl = &ff_sdmmc_ioctl
|
||||
};
|
||||
s_cards[pdrv] = card;
|
||||
s_disk_status_check_en[pdrv] = false;
|
||||
ff_diskio_register(pdrv, &sdmmc_impl);
|
||||
}
|
||||
|
||||
BYTE ff_diskio_get_pdrv_card(const sdmmc_card_t* card)
|
||||
{
|
||||
for (int i = 0; i < FF_VOLUMES; i++) {
|
||||
if (card == s_cards[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0xff;
|
||||
}
|
||||
43
code/components/esp-fatfs/diskio/diskio_sdmmc_mh.h
Normal file
43
code/components/esp-fatfs/diskio/diskio_sdmmc_mh.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sdmmc_cmd_mh.h"
|
||||
|
||||
#include "driver/sdmmc_defs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enable/disable SD card status checking
|
||||
*
|
||||
* @param pdrv drive number
|
||||
* @param enable mock ff_sdmmc_status function (return 0)
|
||||
*/
|
||||
void ff_sdmmc_set_disk_status_check(BYTE pdrv, bool enable);
|
||||
|
||||
/**
|
||||
* Register SD/MMC diskio driver
|
||||
*
|
||||
* @param pdrv drive number
|
||||
* @param card pointer to sdmmc_card_t structure describing a card; card should be initialized before calling f_mount.
|
||||
*/
|
||||
void ff_diskio_register_sdmmc(unsigned char pdrv, sdmmc_card_t* card);
|
||||
|
||||
/**
|
||||
* @brief Get the driver number corresponding to a card
|
||||
*
|
||||
* @param card The card to get its driver
|
||||
* @return Driver number to the card
|
||||
*/
|
||||
BYTE ff_diskio_get_pdrv_card(const sdmmc_card_t* card);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
118
code/components/esp-fatfs/diskio/diskio_wl_mh.c
Normal file
118
code/components/esp-fatfs/diskio/diskio_wl_mh.c
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "diskio_impl_mh.h"
|
||||
#include "ffconf_mh.h"
|
||||
#include "ff_mh.h"
|
||||
#include "esp_log.h"
|
||||
#include "diskio_wl_mh.h"
|
||||
#include "wear_levelling.h"
|
||||
#include "esp_compiler.h"
|
||||
|
||||
static const char* TAG = "ff_diskio_spiflash";
|
||||
|
||||
wl_handle_t ff_wl_handles[FF_VOLUMES] = {
|
||||
[0 ... FF_VOLUMES - 1] = WL_INVALID_HANDLE
|
||||
};
|
||||
|
||||
DSTATUS ff_wl_initialize (BYTE pdrv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
DSTATUS ff_wl_status (BYTE pdrv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
DRESULT ff_wl_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count)
|
||||
{
|
||||
ESP_LOGV(TAG, "ff_wl_read - pdrv=%i, sector=%i, count=%i\n", (unsigned int)pdrv, (unsigned int)sector, (unsigned int)count);
|
||||
wl_handle_t wl_handle = ff_wl_handles[pdrv];
|
||||
assert(wl_handle + 1);
|
||||
esp_err_t err = wl_read(wl_handle, sector * wl_sector_size(wl_handle), buff, count * wl_sector_size(wl_handle));
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "wl_read failed (%d)", err);
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
DRESULT ff_wl_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count)
|
||||
{
|
||||
ESP_LOGV(TAG, "ff_wl_write - pdrv=%i, sector=%i, count=%i\n", (unsigned int)pdrv, (unsigned int)sector, (unsigned int)count);
|
||||
wl_handle_t wl_handle = ff_wl_handles[pdrv];
|
||||
assert(wl_handle + 1);
|
||||
esp_err_t err = wl_erase_range(wl_handle, sector * wl_sector_size(wl_handle), count * wl_sector_size(wl_handle));
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "wl_erase_range failed (%d)", err);
|
||||
return RES_ERROR;
|
||||
}
|
||||
err = wl_write(wl_handle, sector * wl_sector_size(wl_handle), buff, count * wl_sector_size(wl_handle));
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "wl_write failed (%d)", err);
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
DRESULT ff_wl_ioctl (BYTE pdrv, BYTE cmd, void *buff)
|
||||
{
|
||||
wl_handle_t wl_handle = ff_wl_handles[pdrv];
|
||||
ESP_LOGV(TAG, "ff_wl_ioctl: cmd=%i\n", cmd);
|
||||
assert(wl_handle + 1);
|
||||
switch (cmd) {
|
||||
case CTRL_SYNC:
|
||||
return RES_OK;
|
||||
case GET_SECTOR_COUNT:
|
||||
*((DWORD *) buff) = wl_size(wl_handle) / wl_sector_size(wl_handle);
|
||||
return RES_OK;
|
||||
case GET_SECTOR_SIZE:
|
||||
*((WORD *) buff) = wl_sector_size(wl_handle);
|
||||
return RES_OK;
|
||||
case GET_BLOCK_SIZE:
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t ff_diskio_register_wl_partition(BYTE pdrv, wl_handle_t flash_handle)
|
||||
{
|
||||
if (pdrv >= FF_VOLUMES) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
static const ff_diskio_impl_t wl_impl = {
|
||||
.init = &ff_wl_initialize,
|
||||
.status = &ff_wl_status,
|
||||
.read = &ff_wl_read,
|
||||
.write = &ff_wl_write,
|
||||
.ioctl = &ff_wl_ioctl
|
||||
};
|
||||
ff_wl_handles[pdrv] = flash_handle;
|
||||
ff_diskio_register(pdrv, &wl_impl);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
BYTE ff_diskio_get_pdrv_wl(wl_handle_t flash_handle)
|
||||
{
|
||||
for (int i = 0; i < FF_VOLUMES; i++) {
|
||||
if (flash_handle == ff_wl_handles[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
void ff_diskio_clear_pdrv_wl(wl_handle_t flash_handle)
|
||||
{
|
||||
for (int i = 0; i < FF_VOLUMES; i++) {
|
||||
if (flash_handle == ff_wl_handles[i]) {
|
||||
ff_wl_handles[i] = WL_INVALID_HANDLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
39
code/components/esp-fatfs/diskio/diskio_wl_mh.h
Normal file
39
code/components/esp-fatfs/diskio/diskio_wl_mh.h
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef _DISKIO_WL_DEFINED
|
||||
#define _DISKIO_WL_DEFINED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "wear_levelling.h"
|
||||
|
||||
|
||||
/**
|
||||
* Register spi flash partition
|
||||
*
|
||||
* @param pdrv drive number
|
||||
* @param flash_handle handle of the wear levelling partition.
|
||||
*/
|
||||
esp_err_t ff_diskio_register_wl_partition(unsigned char pdrv, wl_handle_t flash_handle);
|
||||
unsigned char ff_diskio_get_pdrv_wl(wl_handle_t flash_handle);
|
||||
void ff_diskio_clear_pdrv_wl(wl_handle_t flash_handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _DISKIO_WL_DEFINED
|
||||
108
code/components/esp-fatfs/port/freertos/ffsystem_mh.c
Normal file
108
code/components/esp-fatfs/port/freertos/ffsystem_mh.c
Normal file
@@ -0,0 +1,108 @@
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Sample Code of OS Dependent Functions for FatFs */
|
||||
/* (C)ChaN, 2017 */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "ff_mh.h"
|
||||
#include "sdkconfig.h"
|
||||
#ifdef CONFIG_FATFS_ALLOC_PREFER_EXTRAM
|
||||
#include "esp_heap_caps.h"
|
||||
#endif
|
||||
|
||||
void* ff_memalloc ( /* Returns pointer to the allocated memory block (null on not enough core) */
|
||||
unsigned msize /* Number of bytes to allocate */
|
||||
)
|
||||
{
|
||||
#ifdef CONFIG_FATFS_ALLOC_PREFER_EXTRAM
|
||||
return heap_caps_malloc_prefer(msize, 2, MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM,
|
||||
MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL);
|
||||
#else
|
||||
return malloc(msize);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Free a memory block */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void ff_memfree (
|
||||
void* mblock /* Pointer to the memory block to free (nothing to do for null) */
|
||||
)
|
||||
{
|
||||
free(mblock); /* Free the memory block with POSIX API */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#if FF_FS_REENTRANT /* Mutal exclusion */
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Create a Synchronization Object */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called in f_mount() function to create a new
|
||||
/ synchronization object for the volume, such as semaphore and mutex.
|
||||
/ When a 0 is returned, the f_mount() function fails with FR_INT_ERR.
|
||||
*/
|
||||
|
||||
|
||||
int ff_cre_syncobj ( /* 1:Function succeeded, 0:Could not create the sync object */
|
||||
BYTE vol, /* Corresponding volume (logical drive number) */
|
||||
FF_SYNC_t *sobj /* Pointer to return the created sync object */
|
||||
)
|
||||
{
|
||||
*sobj = xSemaphoreCreateMutex();
|
||||
return (*sobj != NULL) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Delete a Synchronization Object */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called in f_mount() function to delete a synchronization
|
||||
/ object that created with ff_cre_syncobj() function. When a 0 is returned,
|
||||
/ the f_mount() function fails with FR_INT_ERR.
|
||||
*/
|
||||
|
||||
int ff_del_syncobj ( /* 1:Function succeeded, 0:Could not delete due to an error */
|
||||
FF_SYNC_t sobj /* Sync object tied to the logical drive to be deleted */
|
||||
)
|
||||
{
|
||||
vSemaphoreDelete(sobj);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Request Grant to Access the Volume */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called on entering file functions to lock the volume.
|
||||
/ When a 0 is returned, the file function fails with FR_TIMEOUT.
|
||||
*/
|
||||
|
||||
int ff_req_grant ( /* 1:Got a grant to access the volume, 0:Could not get a grant */
|
||||
FF_SYNC_t sobj /* Sync object to wait */
|
||||
)
|
||||
{
|
||||
return (xSemaphoreTake(sobj, FF_FS_TIMEOUT) == pdTRUE) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Release Grant to Access the Volume */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called on leaving file functions to unlock the volume.
|
||||
*/
|
||||
|
||||
void ff_rel_grant (
|
||||
FF_SYNC_t sobj /* Sync object to be signaled */
|
||||
)
|
||||
{
|
||||
xSemaphoreGive(sobj);
|
||||
}
|
||||
|
||||
#endif // FF_FS_REENTRANT
|
||||
329
code/components/esp-fatfs/src/00history.txt
Normal file
329
code/components/esp-fatfs/src/00history.txt
Normal file
@@ -0,0 +1,329 @@
|
||||
----------------------------------------------------------------------------
|
||||
Revision history of FatFs module
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
R0.00 (February 26, 2006)
|
||||
|
||||
Prototype.
|
||||
|
||||
|
||||
|
||||
R0.01 (April 29, 2006)
|
||||
|
||||
The first release.
|
||||
|
||||
|
||||
|
||||
R0.02 (June 01, 2006)
|
||||
|
||||
Added FAT12 support.
|
||||
Removed unbuffered mode.
|
||||
Fixed a problem on small (<32M) partition.
|
||||
|
||||
|
||||
|
||||
R0.02a (June 10, 2006)
|
||||
|
||||
Added a configuration option (_FS_MINIMUM).
|
||||
|
||||
|
||||
|
||||
R0.03 (September 22, 2006)
|
||||
|
||||
Added f_rename().
|
||||
Changed option _FS_MINIMUM to _FS_MINIMIZE.
|
||||
|
||||
|
||||
|
||||
R0.03a (December 11, 2006)
|
||||
|
||||
Improved cluster scan algorithm to write files fast.
|
||||
Fixed f_mkdir() creates incorrect directory on FAT32.
|
||||
|
||||
|
||||
|
||||
R0.04 (February 04, 2007)
|
||||
|
||||
Added f_mkfs().
|
||||
Supported multiple drive system.
|
||||
Changed some interfaces for multiple drive system.
|
||||
Changed f_mountdrv() to f_mount().
|
||||
|
||||
|
||||
|
||||
R0.04a (April 01, 2007)
|
||||
|
||||
Supported multiple partitions on a physical drive.
|
||||
Added a capability of extending file size to f_lseek().
|
||||
Added minimization level 3.
|
||||
Fixed an endian sensitive code in f_mkfs().
|
||||
|
||||
|
||||
|
||||
R0.04b (May 05, 2007)
|
||||
|
||||
Added a configuration option _USE_NTFLAG.
|
||||
Added FSINFO support.
|
||||
Fixed DBCS name can result FR_INVALID_NAME.
|
||||
Fixed short seek (<= csize) collapses the file object.
|
||||
|
||||
|
||||
|
||||
R0.05 (August 25, 2007)
|
||||
|
||||
Changed arguments of f_read(), f_write() and f_mkfs().
|
||||
Fixed f_mkfs() on FAT32 creates incorrect FSINFO.
|
||||
Fixed f_mkdir() on FAT32 creates incorrect directory.
|
||||
|
||||
|
||||
|
||||
R0.05a (February 03, 2008)
|
||||
|
||||
Added f_truncate() and f_utime().
|
||||
Fixed off by one error at FAT sub-type determination.
|
||||
Fixed btr in f_read() can be mistruncated.
|
||||
Fixed cached sector is not flushed when create and close without write.
|
||||
|
||||
|
||||
|
||||
R0.06 (April 01, 2008)
|
||||
|
||||
Added fputc(), fputs(), fprintf() and fgets().
|
||||
Improved performance of f_lseek() on moving to the same or following cluster.
|
||||
|
||||
|
||||
|
||||
R0.07 (April 01, 2009)
|
||||
|
||||
Merged Tiny-FatFs as a configuration option. (_FS_TINY)
|
||||
Added long file name feature. (_USE_LFN)
|
||||
Added multiple code page feature. (_CODE_PAGE)
|
||||
Added re-entrancy for multitask operation. (_FS_REENTRANT)
|
||||
Added auto cluster size selection to f_mkfs().
|
||||
Added rewind option to f_readdir().
|
||||
Changed result code of critical errors.
|
||||
Renamed string functions to avoid name collision.
|
||||
|
||||
|
||||
|
||||
R0.07a (April 14, 2009)
|
||||
|
||||
Septemberarated out OS dependent code on reentrant cfg.
|
||||
Added multiple sector size feature.
|
||||
|
||||
|
||||
|
||||
R0.07c (June 21, 2009)
|
||||
|
||||
Fixed f_unlink() can return FR_OK on error.
|
||||
Fixed wrong cache control in f_lseek().
|
||||
Added relative path feature.
|
||||
Added f_chdir() and f_chdrive().
|
||||
Added proper case conversion to extended character.
|
||||
|
||||
|
||||
|
||||
R0.07e (November 03, 2009)
|
||||
|
||||
Septemberarated out configuration options from ff.h to ffconf.h.
|
||||
Fixed f_unlink() fails to remove a sub-directory on _FS_RPATH.
|
||||
Fixed name matching error on the 13 character boundary.
|
||||
Added a configuration option, _LFN_UNICODE.
|
||||
Changed f_readdir() to return the SFN with always upper case on non-LFN cfg.
|
||||
|
||||
|
||||
|
||||
R0.08 (May 15, 2010)
|
||||
|
||||
Added a memory configuration option. (_USE_LFN = 3)
|
||||
Added file lock feature. (_FS_SHARE)
|
||||
Added fast seek feature. (_USE_FASTSEEK)
|
||||
Changed some types on the API, XCHAR->TCHAR.
|
||||
Changed .fname in the FILINFO structure on Unicode cfg.
|
||||
String functions support UTF-8 encoding files on Unicode cfg.
|
||||
|
||||
|
||||
|
||||
R0.08a (August 16, 2010)
|
||||
|
||||
Added f_getcwd(). (_FS_RPATH = 2)
|
||||
Added sector erase feature. (_USE_ERASE)
|
||||
Moved file lock semaphore table from fs object to the bss.
|
||||
Fixed f_mkfs() creates wrong FAT32 volume.
|
||||
|
||||
|
||||
|
||||
R0.08b (January 15, 2011)
|
||||
|
||||
Fast seek feature is also applied to f_read() and f_write().
|
||||
f_lseek() reports required table size on creating CLMP.
|
||||
Extended format syntax of f_printf().
|
||||
Ignores duplicated directory separators in given path name.
|
||||
|
||||
|
||||
|
||||
R0.09 (September 06, 2011)
|
||||
|
||||
f_mkfs() supports multiple partition to complete the multiple partition feature.
|
||||
Added f_fdisk().
|
||||
|
||||
|
||||
|
||||
R0.09a (August 27, 2012)
|
||||
|
||||
Changed f_open() and f_opendir() reject null object pointer to avoid crash.
|
||||
Changed option name _FS_SHARE to _FS_LOCK.
|
||||
Fixed assertion failure due to OS/2 EA on FAT12/16 volume.
|
||||
|
||||
|
||||
|
||||
R0.09b (January 24, 2013)
|
||||
|
||||
Added f_setlabel() and f_getlabel().
|
||||
|
||||
|
||||
|
||||
R0.10 (October 02, 2013)
|
||||
|
||||
Added selection of character encoding on the file. (_STRF_ENCODE)
|
||||
Added f_closedir().
|
||||
Added forced full FAT scan for f_getfree(). (_FS_NOFSINFO)
|
||||
Added forced mount feature with changes of f_mount().
|
||||
Improved behavior of volume auto detection.
|
||||
Improved write throughput of f_puts() and f_printf().
|
||||
Changed argument of f_chdrive(), f_mkfs(), disk_read() and disk_write().
|
||||
Fixed f_write() can be truncated when the file size is close to 4GB.
|
||||
Fixed f_open(), f_mkdir() and f_setlabel() can return incorrect value on error.
|
||||
|
||||
|
||||
|
||||
R0.10a (January 15, 2014)
|
||||
|
||||
Added arbitrary strings as drive number in the path name. (_STR_VOLUME_ID)
|
||||
Added a configuration option of minimum sector size. (_MIN_SS)
|
||||
2nd argument of f_rename() can have a drive number and it will be ignored.
|
||||
Fixed f_mount() with forced mount fails when drive number is >= 1. (appeared at R0.10)
|
||||
Fixed f_close() invalidates the file object without volume lock.
|
||||
Fixed f_closedir() returns but the volume lock is left acquired. (appeared at R0.10)
|
||||
Fixed creation of an entry with LFN fails on too many SFN collisions. (appeared at R0.07)
|
||||
|
||||
|
||||
|
||||
R0.10b (May 19, 2014)
|
||||
|
||||
Fixed a hard error in the disk I/O layer can collapse the directory entry.
|
||||
Fixed LFN entry is not deleted when delete/rename an object with lossy converted SFN. (appeared at R0.07)
|
||||
|
||||
|
||||
|
||||
R0.10c (November 09, 2014)
|
||||
|
||||
Added a configuration option for the platforms without RTC. (_FS_NORTC)
|
||||
Changed option name _USE_ERASE to _USE_TRIM.
|
||||
Fixed volume label created by Mac OS X cannot be retrieved with f_getlabel(). (appeared at R0.09b)
|
||||
Fixed a potential problem of FAT access that can appear on disk error.
|
||||
Fixed null pointer dereference on attempting to delete the root direcotry. (appeared at R0.08)
|
||||
|
||||
|
||||
|
||||
R0.11 (February 09, 2015)
|
||||
|
||||
Added f_findfirst(), f_findnext() and f_findclose(). (_USE_FIND)
|
||||
Fixed f_unlink() does not remove cluster chain of the file. (appeared at R0.10c)
|
||||
Fixed _FS_NORTC option does not work properly. (appeared at R0.10c)
|
||||
|
||||
|
||||
|
||||
R0.11a (September 05, 2015)
|
||||
|
||||
Fixed wrong media change can lead a deadlock at thread-safe configuration.
|
||||
Added code page 771, 860, 861, 863, 864, 865 and 869. (_CODE_PAGE)
|
||||
Removed some code pages actually not exist on the standard systems. (_CODE_PAGE)
|
||||
Fixed errors in the case conversion teble of code page 437 and 850 (ff.c).
|
||||
Fixed errors in the case conversion teble of Unicode (cc*.c).
|
||||
|
||||
|
||||
|
||||
R0.12 (April 12, 2016)
|
||||
|
||||
Added support for exFAT file system. (_FS_EXFAT)
|
||||
Added f_expand(). (_USE_EXPAND)
|
||||
Changed some members in FINFO structure and behavior of f_readdir().
|
||||
Added an option _USE_CHMOD.
|
||||
Removed an option _WORD_ACCESS.
|
||||
Fixed errors in the case conversion table of Unicode (cc*.c).
|
||||
|
||||
|
||||
|
||||
R0.12a (July 10, 2016)
|
||||
|
||||
Added support for creating exFAT volume with some changes of f_mkfs().
|
||||
Added a file open method FA_OPEN_APPEND. An f_lseek() following f_open() is no longer needed.
|
||||
f_forward() is available regardless of _FS_TINY.
|
||||
Fixed f_mkfs() creates wrong volume. (appeared at R0.12)
|
||||
Fixed wrong memory read in create_name(). (appeared at R0.12)
|
||||
Fixed compilation fails at some configurations, _USE_FASTSEEK and _USE_FORWARD.
|
||||
|
||||
|
||||
|
||||
R0.12b (September 04, 2016)
|
||||
|
||||
Made f_rename() be able to rename objects with the same name but case.
|
||||
Fixed an error in the case conversion teble of code page 866. (ff.c)
|
||||
Fixed writing data is truncated at the file offset 4GiB on the exFAT volume. (appeared at R0.12)
|
||||
Fixed creating a file in the root directory of exFAT volume can fail. (appeared at R0.12)
|
||||
Fixed f_mkfs() creating exFAT volume with too small cluster size can collapse unallocated memory. (appeared at R0.12)
|
||||
Fixed wrong object name can be returned when read directory at Unicode cfg. (appeared at R0.12)
|
||||
Fixed large file allocation/removing on the exFAT volume collapses allocation bitmap. (appeared at R0.12)
|
||||
Fixed some internal errors in f_expand() and f_lseek(). (appeared at R0.12)
|
||||
|
||||
|
||||
|
||||
R0.12c (March 04, 2017)
|
||||
|
||||
Improved write throughput at the fragmented file on the exFAT volume.
|
||||
Made memory usage for exFAT be able to be reduced as decreasing _MAX_LFN.
|
||||
Fixed successive f_getfree() can return wrong count on the FAT12/16 volume. (appeared at R0.12)
|
||||
Fixed configuration option _VOLUMES cannot be set 10. (appeared at R0.10c)
|
||||
|
||||
|
||||
|
||||
R0.13 (May 21, 2017)
|
||||
|
||||
Changed heading character of configuration keywords "_" to "FF_".
|
||||
Removed ASCII-only configuration, FF_CODE_PAGE = 1. Use FF_CODE_PAGE = 437 instead.
|
||||
Added f_setcp(), run-time code page configuration. (FF_CODE_PAGE = 0)
|
||||
Improved cluster allocation time on stretch a deep buried cluster chain.
|
||||
Improved processing time of f_mkdir() with large cluster size by using FF_USE_LFN = 3.
|
||||
Improved NoFatChain flag of the fragmented file to be set after it is truncated and got contiguous.
|
||||
Fixed archive attribute is left not set when a file on the exFAT volume is renamed. (appeared at R0.12)
|
||||
Fixed exFAT FAT entry can be collapsed when write or lseek operation to the existing file is done. (appeared at R0.12c)
|
||||
Fixed creating a file can fail when a new cluster allocation to the exFAT directory occures. (appeared at R0.12c)
|
||||
|
||||
|
||||
|
||||
R0.13a (October 14, 2017)
|
||||
|
||||
Added support for UTF-8 encoding on the API. (FF_LFN_UNICODE = 2)
|
||||
Added options for file name output buffer. (FF_LFN_BUF, FF_SFN_BUF).
|
||||
Added dynamic memory allocation option for working buffer of f_mkfs() and f_fdisk().
|
||||
Fixed f_fdisk() and f_mkfs() create the partition table with wrong CHS parameters. (appeared at R0.09)
|
||||
Fixed f_unlink() can cause lost clusters at fragmented file on the exFAT volume. (appeared at R0.12c)
|
||||
Fixed f_setlabel() rejects some valid characters for exFAT volume. (appeared at R0.12)
|
||||
|
||||
|
||||
|
||||
R0.13b (April 07, 2018)
|
||||
|
||||
Added support for UTF-32 encoding on the API. (FF_LFN_UNICODE = 3)
|
||||
Added support for Unix style volume ID. (FF_STR_VOLUME_ID = 2)
|
||||
Fixed accesing any object on the exFAT root directory beyond the cluster boundary can fail. (appeared at R0.12c)
|
||||
Fixed f_setlabel() does not reject some invalid characters. (appeared at R0.09b)
|
||||
|
||||
|
||||
|
||||
R0.13c (October 14, 2018)
|
||||
Supported stdint.h for C99 and later. (integer.h was included in ff.h)
|
||||
Fixed reading a directory gets infinite loop when the last directory entry is not empty. (appeared at R0.12)
|
||||
Fixed creating a sub-directory in the fragmented sub-directory on the exFAT volume collapses FAT chain of the parent directory. (appeared at R0.12)
|
||||
Fixed f_getcwd() cause output buffer overrun when the buffer has a valid drive number. (appeared at R0.13b)
|
||||
20
code/components/esp-fatfs/src/00readme.txt
Normal file
20
code/components/esp-fatfs/src/00readme.txt
Normal file
@@ -0,0 +1,20 @@
|
||||
FatFs Module Source Files R0.13c
|
||||
|
||||
|
||||
FILES
|
||||
|
||||
00readme.txt This file.
|
||||
00history.txt Revision history.
|
||||
ff.c FatFs module.
|
||||
ffconf.h Configuration file of FatFs module.
|
||||
ff.h Common include file for FatFs and application module.
|
||||
diskio.h Common include file for FatFs and disk I/O module.
|
||||
diskio.c An example of glue function to attach existing disk I/O module to FatFs.
|
||||
ffunicode.c Optional Unicode utility functions.
|
||||
ffsystem.c An example of optional O/S related functions.
|
||||
|
||||
|
||||
Low level disk I/O module is not included in this archive because the FatFs
|
||||
module is only a generic file system layer and it does not depend on any specific
|
||||
storage device. You need to provide a low level disk I/O module written to
|
||||
control the storage device that attached to the target system.
|
||||
228
code/components/esp-fatfs/src/diskio_mh.c
Normal file
228
code/components/esp-fatfs/src/diskio_mh.c
Normal file
@@ -0,0 +1,228 @@
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Low level disk I/O module SKELETON for FatFs (C)ChaN, 2019 */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* If a working storage control module is available, it should be */
|
||||
/* attached to the FatFs via a glue function rather than modifying it. */
|
||||
/* This is an example of glue functions to attach various exsisting */
|
||||
/* storage control modules to the FatFs module with a defined API. */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#include "ff_mh.h" /* Obtains integer types */
|
||||
#include "diskio_mh.h" /* Declarations of disk functions */
|
||||
|
||||
/* Definitions of physical drive number for each drive */
|
||||
#define DEV_RAM 0 /* Example: Map Ramdisk to physical drive 0 */
|
||||
#define DEV_MMC 1 /* Example: Map MMC/SD card to physical drive 1 */
|
||||
#define DEV_USB 2 /* Example: Map USB MSD to physical drive 2 */
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Get Drive Status */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
DSTATUS disk_status (
|
||||
BYTE pdrv /* Physical drive nmuber to identify the drive */
|
||||
)
|
||||
{
|
||||
DSTATUS stat;
|
||||
int result;
|
||||
|
||||
switch (pdrv) {
|
||||
case DEV_RAM :
|
||||
result = RAM_disk_status();
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return stat;
|
||||
|
||||
case DEV_MMC :
|
||||
result = MMC_disk_status();
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return stat;
|
||||
|
||||
case DEV_USB :
|
||||
result = USB_disk_status();
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return stat;
|
||||
}
|
||||
return STA_NOINIT;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Inidialize a Drive */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
DSTATUS disk_initialize (
|
||||
BYTE pdrv /* Physical drive nmuber to identify the drive */
|
||||
)
|
||||
{
|
||||
DSTATUS stat;
|
||||
int result;
|
||||
|
||||
switch (pdrv) {
|
||||
case DEV_RAM :
|
||||
result = RAM_disk_initialize();
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return stat;
|
||||
|
||||
case DEV_MMC :
|
||||
result = MMC_disk_initialize();
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return stat;
|
||||
|
||||
case DEV_USB :
|
||||
result = USB_disk_initialize();
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return stat;
|
||||
}
|
||||
return STA_NOINIT;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Read Sector(s) */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
DRESULT disk_read (
|
||||
BYTE pdrv, /* Physical drive nmuber to identify the drive */
|
||||
BYTE *buff, /* Data buffer to store read data */
|
||||
LBA_t sector, /* Start sector in LBA */
|
||||
UINT count /* Number of sectors to read */
|
||||
)
|
||||
{
|
||||
DRESULT res;
|
||||
int result;
|
||||
|
||||
switch (pdrv) {
|
||||
case DEV_RAM :
|
||||
// translate the arguments here
|
||||
|
||||
result = RAM_disk_read(buff, sector, count);
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return res;
|
||||
|
||||
case DEV_MMC :
|
||||
// translate the arguments here
|
||||
|
||||
result = MMC_disk_read(buff, sector, count);
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return res;
|
||||
|
||||
case DEV_USB :
|
||||
// translate the arguments here
|
||||
|
||||
result = USB_disk_read(buff, sector, count);
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
return RES_PARERR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Write Sector(s) */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#if FF_FS_READONLY == 0
|
||||
|
||||
DRESULT disk_write (
|
||||
BYTE pdrv, /* Physical drive nmuber to identify the drive */
|
||||
const BYTE *buff, /* Data to be written */
|
||||
LBA_t sector, /* Start sector in LBA */
|
||||
UINT count /* Number of sectors to write */
|
||||
)
|
||||
{
|
||||
DRESULT res;
|
||||
int result;
|
||||
|
||||
switch (pdrv) {
|
||||
case DEV_RAM :
|
||||
// translate the arguments here
|
||||
|
||||
result = RAM_disk_write(buff, sector, count);
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return res;
|
||||
|
||||
case DEV_MMC :
|
||||
// translate the arguments here
|
||||
|
||||
result = MMC_disk_write(buff, sector, count);
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return res;
|
||||
|
||||
case DEV_USB :
|
||||
// translate the arguments here
|
||||
|
||||
result = USB_disk_write(buff, sector, count);
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
return RES_PARERR;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Miscellaneous Functions */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
DRESULT disk_ioctl (
|
||||
BYTE pdrv, /* Physical drive nmuber (0..) */
|
||||
BYTE cmd, /* Control code */
|
||||
void *buff /* Buffer to send/receive control data */
|
||||
)
|
||||
{
|
||||
DRESULT res;
|
||||
int result;
|
||||
|
||||
switch (pdrv) {
|
||||
case DEV_RAM :
|
||||
|
||||
// Process of the command for the RAM drive
|
||||
|
||||
return res;
|
||||
|
||||
case DEV_MMC :
|
||||
|
||||
// Process of the command for the MMC/SD card
|
||||
|
||||
return res;
|
||||
|
||||
case DEV_USB :
|
||||
|
||||
// Process of the command the USB drive
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
return RES_PARERR;
|
||||
}
|
||||
79
code/components/esp-fatfs/src/diskio_mh.h
Normal file
79
code/components/esp-fatfs/src/diskio_mh.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/*-----------------------------------------------------------------------/
|
||||
/ Low level disk interface modlue include file (C)ChaN, 2019 /
|
||||
/-----------------------------------------------------------------------*/
|
||||
|
||||
#ifndef _DISKIO_DEFINED
|
||||
#define _DISKIO_DEFINED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "ff_mh.h"
|
||||
|
||||
/* Status of Disk Functions */
|
||||
typedef BYTE DSTATUS;
|
||||
|
||||
/* Results of Disk Functions */
|
||||
typedef enum {
|
||||
RES_OK = 0, /* 0: Successful */
|
||||
RES_ERROR, /* 1: R/W Error */
|
||||
RES_WRPRT, /* 2: Write Protected */
|
||||
RES_NOTRDY, /* 3: Not Ready */
|
||||
RES_PARERR /* 4: Invalid Parameter */
|
||||
} DRESULT;
|
||||
|
||||
|
||||
/*---------------------------------------*/
|
||||
/* Prototypes for disk control functions */
|
||||
|
||||
|
||||
DSTATUS disk_initialize (BYTE pdrv);
|
||||
DSTATUS disk_status (BYTE pdrv);
|
||||
DRESULT disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count);
|
||||
DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count);
|
||||
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
|
||||
|
||||
|
||||
/* Disk Status Bits (DSTATUS) */
|
||||
|
||||
#define STA_NOINIT 0x01 /* Drive not initialized */
|
||||
#define STA_NODISK 0x02 /* No medium in the drive */
|
||||
#define STA_PROTECT 0x04 /* Write protected */
|
||||
|
||||
|
||||
/* Command code for disk_ioctrl fucntion */
|
||||
|
||||
/* Generic command (Used by FatFs) */
|
||||
#define CTRL_SYNC 0 /* Complete pending write process (needed at FF_FS_READONLY == 0) */
|
||||
#define GET_SECTOR_COUNT 1 /* Get media size (needed at FF_USE_MKFS == 1) */
|
||||
#define GET_SECTOR_SIZE 2 /* Get sector size (needed at FF_MAX_SS != FF_MIN_SS) */
|
||||
#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at FF_USE_MKFS == 1) */
|
||||
#define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at FF_USE_TRIM == 1) */
|
||||
|
||||
/* Generic command (Not used by FatFs) */
|
||||
#define CTRL_POWER 5 /* Get/Set power status */
|
||||
#define CTRL_LOCK 6 /* Lock/Unlock media removal */
|
||||
#define CTRL_EJECT 7 /* Eject media */
|
||||
#define CTRL_FORMAT 8 /* Create physical format on the media */
|
||||
|
||||
/* MMC/SDC specific ioctl command */
|
||||
#define MMC_GET_TYPE 10 /* Get card type */
|
||||
#define MMC_GET_CSD 11 /* Get CSD */
|
||||
#define MMC_GET_CID 12 /* Get CID */
|
||||
#define MMC_GET_OCR 13 /* Get OCR */
|
||||
#define MMC_GET_SDSTAT 14 /* Get SD status */
|
||||
#define ISDIO_READ 55 /* Read data form SD iSDIO register */
|
||||
#define ISDIO_WRITE 56 /* Write data to SD iSDIO register */
|
||||
#define ISDIO_MRITE 57 /* Masked write data to SD iSDIO register */
|
||||
|
||||
/* ATA/CF specific ioctl command */
|
||||
#define ATA_GET_REV 20 /* Get F/W revision */
|
||||
#define ATA_GET_MODEL 21 /* Get model name */
|
||||
#define ATA_GET_SN 22 /* Get serial number */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
6990
code/components/esp-fatfs/src/ff_mh.c
Normal file
6990
code/components/esp-fatfs/src/ff_mh.c
Normal file
File diff suppressed because it is too large
Load Diff
422
code/components/esp-fatfs/src/ff_mh.h
Normal file
422
code/components/esp-fatfs/src/ff_mh.h
Normal file
@@ -0,0 +1,422 @@
|
||||
/*----------------------------------------------------------------------------/
|
||||
/ FatFs - Generic FAT Filesystem module R0.14b /
|
||||
/-----------------------------------------------------------------------------/
|
||||
/
|
||||
/ Copyright (C) 2021, ChaN, all right reserved.
|
||||
/
|
||||
/ FatFs module is an open source software. Redistribution and use of FatFs in
|
||||
/ source and binary forms, with or without modification, are permitted provided
|
||||
/ that the following condition is met:
|
||||
|
||||
/ 1. Redistributions of source code must retain the above copyright notice,
|
||||
/ this condition and the following disclaimer.
|
||||
/
|
||||
/ This software is provided by the copyright holder and contributors "AS IS"
|
||||
/ and any warranties related to this software are DISCLAIMED.
|
||||
/ The copyright owner or contributors be NOT LIABLE for any damages caused
|
||||
/ by use of this software.
|
||||
/
|
||||
/----------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#ifndef FF_DEFINED
|
||||
#define FF_DEFINED 86631 /* Revision ID */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "ffconf_mh.h" /* FatFs configuration options */
|
||||
|
||||
#if FF_DEFINED != FFCONF_DEF
|
||||
#error Wrong configuration file (ffconf.h).
|
||||
#endif
|
||||
|
||||
|
||||
/* Integer types used for FatFs API */
|
||||
|
||||
#if defined(_WIN32) /* Windows VC++ (for development only) */
|
||||
#define FF_INTDEF 2
|
||||
#include <windows.h>
|
||||
typedef unsigned __int64 QWORD;
|
||||
#include <float.h>
|
||||
#define isnan(v) _isnan(v)
|
||||
#define isinf(v) (!_finite(v))
|
||||
|
||||
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) /* C99 or later */
|
||||
#define FF_INTDEF 2
|
||||
#include <stdint.h>
|
||||
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
|
||||
typedef unsigned char BYTE; /* char must be 8-bit */
|
||||
typedef uint16_t WORD; /* 16-bit unsigned integer */
|
||||
typedef uint32_t DWORD; /* 32-bit unsigned integer */
|
||||
typedef uint64_t QWORD; /* 64-bit unsigned integer */
|
||||
typedef WORD WCHAR; /* UTF-16 character type */
|
||||
|
||||
#else /* Earlier than C99 */
|
||||
#define FF_INTDEF 1
|
||||
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
|
||||
typedef unsigned char BYTE; /* char must be 8-bit */
|
||||
typedef unsigned short WORD; /* 16-bit unsigned integer */
|
||||
typedef unsigned long DWORD; /* 32-bit unsigned integer */
|
||||
typedef WORD WCHAR; /* UTF-16 character type */
|
||||
#endif
|
||||
|
||||
|
||||
/* Type of file size and LBA variables */
|
||||
|
||||
#if FF_FS_EXFAT
|
||||
#if FF_INTDEF != 2
|
||||
#error exFAT feature wants C99 or later
|
||||
#endif
|
||||
typedef QWORD FSIZE_t;
|
||||
#if FF_LBA64
|
||||
typedef QWORD LBA_t;
|
||||
#else
|
||||
typedef DWORD LBA_t;
|
||||
#endif
|
||||
#else
|
||||
#if FF_LBA64
|
||||
#error exFAT needs to be enabled when enable 64-bit LBA
|
||||
#endif
|
||||
typedef DWORD FSIZE_t;
|
||||
typedef DWORD LBA_t;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Type of path name strings on FatFs API (TCHAR) */
|
||||
|
||||
#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */
|
||||
typedef WCHAR TCHAR;
|
||||
#define _T(x) L ## x
|
||||
#define _TEXT(x) L ## x
|
||||
#elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */
|
||||
typedef char TCHAR;
|
||||
#define _T(x) u8 ## x
|
||||
#define _TEXT(x) u8 ## x
|
||||
#elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 encoding */
|
||||
typedef DWORD TCHAR;
|
||||
#define _T(x) U ## x
|
||||
#define _TEXT(x) U ## x
|
||||
#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3)
|
||||
#error Wrong FF_LFN_UNICODE setting
|
||||
#else /* ANSI/OEM code in SBCS/DBCS */
|
||||
typedef char TCHAR;
|
||||
#define _T(x) x
|
||||
#define _TEXT(x) x
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Definitions of volume management */
|
||||
|
||||
#if FF_MULTI_PARTITION /* Multiple partition configuration */
|
||||
typedef struct {
|
||||
BYTE pd; /* Physical drive number */
|
||||
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
|
||||
} PARTITION;
|
||||
extern const PARTITION VolToPart[]; /* Volume - Partition mapping table */
|
||||
#endif
|
||||
|
||||
#if FF_STR_VOLUME_ID
|
||||
#ifndef FF_VOLUME_STRS
|
||||
extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Filesystem object structure (FATFS) */
|
||||
|
||||
typedef struct {
|
||||
BYTE fs_type; /* Filesystem type (0:not mounted) */
|
||||
BYTE pdrv; /* Associated physical drive */
|
||||
BYTE n_fats; /* Number of FATs (1 or 2) */
|
||||
BYTE wflag; /* win[] flag (b0:dirty) */
|
||||
BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */
|
||||
WORD id; /* Volume mount ID */
|
||||
WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
|
||||
WORD csize; /* Cluster size [sectors] */
|
||||
#if FF_MAX_SS != FF_MIN_SS
|
||||
WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */
|
||||
#endif
|
||||
#if FF_USE_LFN
|
||||
WCHAR* lfnbuf; /* LFN working buffer */
|
||||
#endif
|
||||
#if FF_FS_EXFAT
|
||||
BYTE* dirbuf; /* Directory entry block scratchpad buffer for exFAT */
|
||||
#endif
|
||||
#if FF_FS_REENTRANT
|
||||
FF_SYNC_t sobj; /* Identifier of sync object */
|
||||
#endif
|
||||
#if !FF_FS_READONLY
|
||||
DWORD last_clst; /* Last allocated cluster */
|
||||
DWORD free_clst; /* Number of free clusters */
|
||||
#endif
|
||||
#if FF_FS_RPATH
|
||||
DWORD cdir; /* Current directory start cluster (0:root) */
|
||||
#if FF_FS_EXFAT
|
||||
DWORD cdc_scl; /* Containing directory start cluster (invalid when cdir is 0) */
|
||||
DWORD cdc_size; /* b31-b8:Size of containing directory, b7-b0: Chain status */
|
||||
DWORD cdc_ofs; /* Offset in the containing directory (invalid when cdir is 0) */
|
||||
#endif
|
||||
#endif
|
||||
DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */
|
||||
DWORD fsize; /* Size of an FAT [sectors] */
|
||||
LBA_t volbase; /* Volume base sector */
|
||||
LBA_t fatbase; /* FAT base sector */
|
||||
LBA_t dirbase; /* Root directory base sector/cluster */
|
||||
LBA_t database; /* Data base sector */
|
||||
#if FF_FS_EXFAT
|
||||
LBA_t bitbase; /* Allocation bitmap base sector */
|
||||
#endif
|
||||
LBA_t winsect; /* Current sector appearing in the win[] */
|
||||
BYTE win[FF_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */
|
||||
} FATFS;
|
||||
|
||||
|
||||
|
||||
/* Object ID and allocation information (FFOBJID) */
|
||||
|
||||
typedef struct {
|
||||
FATFS* fs; /* Pointer to the hosting volume of this object */
|
||||
WORD id; /* Hosting volume mount ID */
|
||||
BYTE attr; /* Object attribute */
|
||||
BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:fragmented in this session, b2:sub-directory stretched) */
|
||||
DWORD sclust; /* Object data start cluster (0:no cluster or root directory) */
|
||||
FSIZE_t objsize; /* Object size (valid when sclust != 0) */
|
||||
#if FF_FS_EXFAT
|
||||
DWORD n_cont; /* Size of first fragment - 1 (valid when stat == 3) */
|
||||
DWORD n_frag; /* Size of last fragment needs to be written to FAT (valid when not zero) */
|
||||
DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */
|
||||
DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */
|
||||
DWORD c_ofs; /* Offset in the containing directory (valid when file object and sclust != 0) */
|
||||
#endif
|
||||
#if FF_FS_LOCK
|
||||
UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */
|
||||
#endif
|
||||
} FFOBJID;
|
||||
|
||||
|
||||
|
||||
/* File object structure (FIL) */
|
||||
|
||||
typedef struct {
|
||||
FFOBJID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */
|
||||
BYTE flag; /* File status flags */
|
||||
BYTE err; /* Abort flag (error code) */
|
||||
FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */
|
||||
DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */
|
||||
LBA_t sect; /* Sector number appearing in buf[] (0:invalid) */
|
||||
#if !FF_FS_READONLY
|
||||
LBA_t dir_sect; /* Sector number containing the directory entry (not used at exFAT) */
|
||||
BYTE* dir_ptr; /* Pointer to the directory entry in the win[] (not used at exFAT) */
|
||||
#endif
|
||||
#if FF_USE_FASTSEEK
|
||||
DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */
|
||||
#endif
|
||||
#if !FF_FS_TINY
|
||||
BYTE buf[FF_MAX_SS]; /* File private data read/write window */
|
||||
#endif
|
||||
} FIL;
|
||||
|
||||
|
||||
|
||||
/* Directory object structure (FF_DIR) */
|
||||
|
||||
typedef struct {
|
||||
FFOBJID obj; /* Object identifier */
|
||||
DWORD dptr; /* Current read/write offset */
|
||||
DWORD clust; /* Current cluster */
|
||||
LBA_t sect; /* Current sector (0:Read operation has terminated) */
|
||||
BYTE* dir; /* Pointer to the directory item in the win[] */
|
||||
BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */
|
||||
#if FF_USE_LFN
|
||||
DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */
|
||||
#endif
|
||||
#if FF_USE_FIND
|
||||
const TCHAR* pat; /* Pointer to the name matching pattern */
|
||||
#endif
|
||||
} FF_DIR;
|
||||
|
||||
|
||||
|
||||
/* File information structure (FILINFO) */
|
||||
|
||||
typedef struct {
|
||||
FSIZE_t fsize; /* File size */
|
||||
WORD fdate; /* Modified date */
|
||||
WORD ftime; /* Modified time */
|
||||
BYTE fattrib; /* File attribute */
|
||||
#if FF_USE_LFN
|
||||
TCHAR altname[FF_SFN_BUF + 1];/* Altenative file name */
|
||||
TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */
|
||||
#else
|
||||
TCHAR fname[12 + 1]; /* File name */
|
||||
#endif
|
||||
} FILINFO;
|
||||
|
||||
|
||||
|
||||
/* Format parameter structure (MKFS_PARM) */
|
||||
|
||||
typedef struct {
|
||||
BYTE fmt; /* Format option (FM_FAT, FM_FAT32, FM_EXFAT and FM_SFD) */
|
||||
BYTE n_fat; /* Number of FATs */
|
||||
UINT align; /* Data area alignment (sector) */
|
||||
UINT n_root; /* Number of root directory entries */
|
||||
DWORD au_size; /* Cluster size (byte) */
|
||||
} MKFS_PARM;
|
||||
|
||||
|
||||
|
||||
/* File function return code (FRESULT) */
|
||||
|
||||
typedef enum {
|
||||
FR_OK = 0, /* (0) Succeeded */
|
||||
FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */
|
||||
FR_INT_ERR, /* (2) Assertion failed */
|
||||
FR_NOT_READY, /* (3) The physical drive cannot work */
|
||||
FR_NO_FILE, /* (4) Could not find the file */
|
||||
FR_NO_PATH, /* (5) Could not find the path */
|
||||
FR_INVALID_NAME, /* (6) The path name format is invalid */
|
||||
FR_DENIED, /* (7) Access denied due to prohibited access or directory full */
|
||||
FR_EXIST, /* (8) Access denied due to prohibited access */
|
||||
FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */
|
||||
FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */
|
||||
FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */
|
||||
FR_NOT_ENABLED, /* (12) The volume has no work area */
|
||||
FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */
|
||||
FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */
|
||||
FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */
|
||||
FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */
|
||||
FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */
|
||||
FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */
|
||||
FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
|
||||
} FRESULT;
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* FatFs module application interface */
|
||||
|
||||
FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */
|
||||
FRESULT f_close (FIL* fp); /* Close an open file object */
|
||||
FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */
|
||||
FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */
|
||||
FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */
|
||||
FRESULT f_truncate (FIL* fp); /* Truncate the file */
|
||||
FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */
|
||||
FRESULT f_opendir (FF_DIR* dp, const TCHAR* path); /* Open a directory */
|
||||
FRESULT f_closedir (FF_DIR* dp); /* Close an open directory */
|
||||
FRESULT f_readdir (FF_DIR* dp, FILINFO* fno); /* Read a directory item */
|
||||
FRESULT f_findfirst (FF_DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */
|
||||
FRESULT f_findnext (FF_DIR* dp, FILINFO* fno); /* Find next file */
|
||||
FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */
|
||||
FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */
|
||||
FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */
|
||||
FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */
|
||||
FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */
|
||||
FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */
|
||||
FRESULT f_chdir (const TCHAR* path); /* Change current directory */
|
||||
FRESULT f_chdrive (const TCHAR* path); /* Change current drive */
|
||||
FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */
|
||||
FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */
|
||||
FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */
|
||||
FRESULT f_setlabel (const TCHAR* label); /* Set volume label */
|
||||
FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
|
||||
FRESULT f_expand (FIL* fp, FSIZE_t fsz, BYTE opt); /* Allocate a contiguous block to the file */
|
||||
FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */
|
||||
FRESULT f_mkfs (const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len); /* Create a FAT volume */
|
||||
FRESULT f_fdisk (BYTE pdrv, const LBA_t ptbl[], void* work); /* Divide a physical drive into some partitions */
|
||||
FRESULT f_setcp (WORD cp); /* Set current code page */
|
||||
int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */
|
||||
int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */
|
||||
int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */
|
||||
TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */
|
||||
|
||||
#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize))
|
||||
#define f_error(fp) ((fp)->err)
|
||||
#define f_tell(fp) ((fp)->fptr)
|
||||
#define f_size(fp) ((fp)->obj.objsize)
|
||||
#define f_rewind(fp) f_lseek((fp), 0)
|
||||
#define f_rewinddir(dp) f_readdir((dp), 0)
|
||||
#define f_rmdir(path) f_unlink(path)
|
||||
#define f_unmount(path) f_mount(0, path, 0)
|
||||
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* Additional user defined functions */
|
||||
|
||||
/* RTC function */
|
||||
#if !FF_FS_READONLY && !FF_FS_NORTC
|
||||
DWORD get_fattime (void);
|
||||
#endif
|
||||
|
||||
/* LFN support functions */
|
||||
#if FF_USE_LFN >= 1 /* Code conversion (defined in unicode.c) */
|
||||
WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */
|
||||
WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code conversion */
|
||||
DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case conversion */
|
||||
#endif
|
||||
#if FF_USE_LFN == 3 /* Dynamic memory allocation */
|
||||
void* ff_memalloc (UINT msize); /* Allocate memory block */
|
||||
void ff_memfree (void* mblock); /* Free memory block */
|
||||
#endif
|
||||
|
||||
/* Sync functions */
|
||||
#if FF_FS_REENTRANT
|
||||
int ff_cre_syncobj (BYTE vol, FF_SYNC_t* sobj); /* Create a sync object */
|
||||
int ff_req_grant (FF_SYNC_t sobj); /* Lock sync object */
|
||||
void ff_rel_grant (FF_SYNC_t sobj); /* Unlock sync object */
|
||||
int ff_del_syncobj (FF_SYNC_t sobj); /* Delete a sync object */
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* Flags and offset address */
|
||||
|
||||
|
||||
/* File access mode and open method flags (3rd argument of f_open) */
|
||||
#define FA_READ 0x01
|
||||
#define FA_WRITE 0x02
|
||||
#define FA_OPEN_EXISTING 0x00
|
||||
#define FA_CREATE_NEW 0x04
|
||||
#define FA_CREATE_ALWAYS 0x08
|
||||
#define FA_OPEN_ALWAYS 0x10
|
||||
#define FA_OPEN_APPEND 0x30
|
||||
|
||||
/* Fast seek controls (2nd argument of f_lseek) */
|
||||
#define CREATE_LINKMAP ((FSIZE_t)0 - 1)
|
||||
|
||||
/* Format options (2nd argument of f_mkfs) */
|
||||
#define FM_FAT 0x01
|
||||
#define FM_FAT32 0x02
|
||||
#define FM_EXFAT 0x04
|
||||
#define FM_ANY 0x07
|
||||
#define FM_SFD 0x08
|
||||
|
||||
/* Filesystem type (FATFS.fs_type) */
|
||||
#define FS_FAT12 1
|
||||
#define FS_FAT16 2
|
||||
#define FS_FAT32 3
|
||||
#define FS_EXFAT 4
|
||||
|
||||
/* File attribute bits for directory entry (FILINFO.fattrib) */
|
||||
#define AM_RDO 0x01 /* Read only */
|
||||
#define AM_HID 0x02 /* Hidden */
|
||||
#define AM_SYS 0x04 /* System */
|
||||
#define AM_DIR 0x10 /* Directory */
|
||||
#define AM_ARC 0x20 /* Archive */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FF_DEFINED */
|
||||
309
code/components/esp-fatfs/src/ffconf_mh.h
Normal file
309
code/components/esp-fatfs/src/ffconf_mh.h
Normal file
@@ -0,0 +1,309 @@
|
||||
#include "sdkconfig.h"
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ FatFs Functional Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FFCONF_DEF 86631 /* Revision ID */
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Function Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_FS_READONLY 0
|
||||
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
|
||||
/ Read-only configuration removes writing API functions, f_write(), f_sync(),
|
||||
/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
|
||||
/ and optional writing functions as well. */
|
||||
|
||||
#define FF_FS_MINIMIZE 0
|
||||
/* This option defines minimization level to remove some basic API functions.
|
||||
/
|
||||
/ 0: Basic functions are fully enabled.
|
||||
/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
|
||||
/ are removed.
|
||||
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
|
||||
/ 3: f_lseek() function is removed in addition to 2. */
|
||||
|
||||
#define FF_USE_FIND 0
|
||||
/* This option switches filtered directory read functions, f_findfirst() and
|
||||
/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
|
||||
|
||||
#define FF_USE_MKFS 1
|
||||
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
|
||||
|
||||
#define FF_USE_FASTSEEK CONFIG_FATFS_USE_FASTSEEK
|
||||
/* This option switches fast seek function. (0:Disable or 1:Enable) */
|
||||
|
||||
#define FF_USE_EXPAND 0
|
||||
/* This option switches f_expand function. (0:Disable or 1:Enable) */
|
||||
|
||||
#define FF_USE_CHMOD 1
|
||||
/* This option switches attribute manipulation functions, f_chmod() and f_utime().
|
||||
/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */
|
||||
|
||||
#define FF_USE_LABEL 0
|
||||
/* This option switches volume label functions, f_getlabel() and f_setlabel().
|
||||
/ (0:Disable or 1:Enable) */
|
||||
|
||||
#define FF_USE_FORWARD 0
|
||||
/* This option switches f_forward() function. (0:Disable or 1:Enable) */
|
||||
|
||||
#define FF_USE_STRFUNC 0
|
||||
#define FF_PRINT_LLI 0
|
||||
#define FF_PRINT_FLOAT 0
|
||||
#define FF_STRF_ENCODE 3
|
||||
/* FF_USE_STRFUNC switches string functions, f_gets(), f_putc(), f_puts() and
|
||||
/ f_printf().
|
||||
/
|
||||
/ 0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect.
|
||||
/ 1: Enable without LF-CRLF conversion.
|
||||
/ 2: Enable with LF-CRLF conversion.
|
||||
/
|
||||
/ FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2
|
||||
makes f_printf() support floating point argument. These features want C99 or later.
|
||||
/ When FF_LFN_UNICODE >= 1 with LFN enabled, string functions convert the character
|
||||
/ encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE
|
||||
/ to be read/written via those functions.
|
||||
/
|
||||
/ 0: ANSI/OEM in current CP
|
||||
/ 1: Unicode in UTF-16LE
|
||||
/ 2: Unicode in UTF-16BE
|
||||
/ 3: Unicode in UTF-8
|
||||
*/
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Locale and Namespace Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_CODE_PAGE CONFIG_FATFS_CODEPAGE
|
||||
/* This option specifies the OEM code page to be used on the target system.
|
||||
/ Incorrect code page setting can cause a file open failure.
|
||||
/
|
||||
/ 437 - U.S.
|
||||
/ 720 - Arabic
|
||||
/ 737 - Greek
|
||||
/ 771 - KBL
|
||||
/ 775 - Baltic
|
||||
/ 850 - Latin 1
|
||||
/ 852 - Latin 2
|
||||
/ 855 - Cyrillic
|
||||
/ 857 - Turkish
|
||||
/ 860 - Portuguese
|
||||
/ 861 - Icelandic
|
||||
/ 862 - Hebrew
|
||||
/ 863 - Canadian French
|
||||
/ 864 - Arabic
|
||||
/ 865 - Nordic
|
||||
/ 866 - Russian
|
||||
/ 869 - Greek 2
|
||||
/ 932 - Japanese (DBCS)
|
||||
/ 936 - Simplified Chinese (DBCS)
|
||||
/ 949 - Korean (DBCS)
|
||||
/ 950 - Traditional Chinese (DBCS)
|
||||
/ 0 - Include all code pages above and configured by f_setcp()
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_FATFS_LFN_STACK)
|
||||
#define FF_USE_LFN 2
|
||||
#elif defined(CONFIG_FATFS_LFN_HEAP)
|
||||
#define FF_USE_LFN 3
|
||||
#else /* CONFIG_FATFS_LFN_NONE */
|
||||
#define FF_USE_LFN 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FATFS_MAX_LFN
|
||||
#define FF_MAX_LFN CONFIG_FATFS_MAX_LFN
|
||||
#endif
|
||||
|
||||
/* The FF_USE_LFN switches the support for LFN (long file name).
|
||||
/
|
||||
/ 0: Disable LFN. FF_MAX_LFN has no effect.
|
||||
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
|
||||
/ 2: Enable LFN with dynamic working buffer on the STACK.
|
||||
/ 3: Enable LFN with dynamic working buffer on the HEAP.
|
||||
/
|
||||
/ To enable the LFN, ffunicode.c needs to be added to the project. The LFN function
|
||||
/ requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
|
||||
/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
|
||||
/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
|
||||
/ be in range of 12 to 255. It is recommended to be set it 255 to fully support LFN
|
||||
/ specification.
|
||||
/ When use stack for the working buffer, take care on stack overflow. When use heap
|
||||
/ memory for the working buffer, memory management functions, ff_memalloc() and
|
||||
/ ff_memfree() exemplified in ffsystem.c, need to be added to the project. */
|
||||
|
||||
#ifdef CONFIG_FATFS_API_ENCODING_UTF_8
|
||||
#define FF_LFN_UNICODE 2
|
||||
#else /* CONFIG_FATFS_API_ENCODING_ANSI_OEM */
|
||||
#define FF_LFN_UNICODE 0
|
||||
#endif
|
||||
/* This option switches the character encoding on the API when LFN is enabled.
|
||||
/
|
||||
/ 0: ANSI/OEM in current CP (TCHAR = char)
|
||||
/ 1: Unicode in UTF-16 (TCHAR = WCHAR)
|
||||
/ 2: Unicode in UTF-8 (TCHAR = char)
|
||||
/ 3: Unicode in UTF-32 (TCHAR = DWORD)
|
||||
/
|
||||
/ Also behavior of string I/O functions will be affected by this option.
|
||||
/ When LFN is not enabled, this option has no effect. */
|
||||
|
||||
#define FF_LFN_BUF 255
|
||||
#define FF_SFN_BUF 12
|
||||
/* This set of options defines size of file name members in the FILINFO structure
|
||||
/ which is used to read out directory items. These values should be suffcient for
|
||||
/ the file names to read. The maximum possible length of the read file name depends
|
||||
/ on character encoding. When LFN is not enabled, these options have no effect. */
|
||||
|
||||
#define FF_FS_RPATH 0
|
||||
/* This option configures support for relative path.
|
||||
/
|
||||
/ 0: Disable relative path and remove related functions.
|
||||
/ 1: Enable relative path. f_chdir() and f_chdrive() are available.
|
||||
/ 2: f_getcwd() function is available in addition to 1.
|
||||
*/
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Drive/Volume Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_VOLUMES CONFIG_FATFS_VOLUME_COUNT
|
||||
/* Number of volumes (logical drives) to be used. (1-10) */
|
||||
|
||||
#define FF_STR_VOLUME_ID 0
|
||||
#define FF_VOLUME_STRS "RAM", "NAND", "CF", "SD", "SD2", "USB", "USB2", "USB3"
|
||||
/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
|
||||
/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
|
||||
/ number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
|
||||
/ logical drives. Number of items must not be less than FF_VOLUMES. Valid
|
||||
/ characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
|
||||
/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
|
||||
/ not defined, a user defined volume string table needs to be defined as:
|
||||
/
|
||||
/ const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
|
||||
*/
|
||||
|
||||
#define FF_MULTI_PARTITION 1
|
||||
/* This option switches support for multiple volumes on the physical drive.
|
||||
/ By default (0), each logical drive number is bound to the same physical drive
|
||||
/ number and only an FAT volume found on the physical drive will be mounted.
|
||||
/ When this function is enabled (1), each logical drive number can be bound to
|
||||
/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
|
||||
/ funciton will be available. */
|
||||
|
||||
/* SD card sector size */
|
||||
#define FF_SS_SDCARD 512
|
||||
/* wear_levelling library sector size */
|
||||
#define FF_SS_WL CONFIG_WL_SECTOR_SIZE
|
||||
|
||||
#define FF_MIN_SS MIN(FF_SS_SDCARD, FF_SS_WL)
|
||||
#define FF_MAX_SS MAX(FF_SS_SDCARD, FF_SS_WL)
|
||||
/* This set of options configures the range of sector size to be supported. (512,
|
||||
/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
|
||||
/ harddisk, but a larger value may be required for on-board flash memory and some
|
||||
/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured
|
||||
/ for variable sector size mode and disk_ioctl() function needs to implement
|
||||
/ GET_SECTOR_SIZE command. */
|
||||
|
||||
#define FF_LBA64 0
|
||||
/* This option switches support for 64-bit LBA. (0:Disable or 1:Enable)
|
||||
/ To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */
|
||||
|
||||
#define FF_MIN_GPT 0x10000000
|
||||
/* Minimum number of sectors to switch GPT as partitioning format in f_mkfs and
|
||||
/ f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */
|
||||
|
||||
#define FF_USE_TRIM 1
|
||||
/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
|
||||
/ To enable Trim function, also CTRL_TRIM command should be implemented to the
|
||||
/ disk_ioctl() function. */
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ System Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_FS_TINY (!CONFIG_FATFS_PER_FILE_CACHE)
|
||||
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
|
||||
/ At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes.
|
||||
/ Instead of private sector buffer eliminated from the file object, common sector
|
||||
/ buffer in the filesystem object (FATFS) is used for the file data transfer. */
|
||||
|
||||
#define FF_FS_EXFAT 0
|
||||
/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
|
||||
/ To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1)
|
||||
/ Note that enabling exFAT discards ANSI C (C89) compatibility. */
|
||||
|
||||
#define FF_FS_NORTC 0
|
||||
#define FF_NORTC_MON 1
|
||||
#define FF_NORTC_MDAY 1
|
||||
#define FF_NORTC_YEAR 2020
|
||||
/* The option FF_FS_NORTC switches timestamp functiton. If the system does not have
|
||||
/ any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable
|
||||
/ the timestamp function. Every object modified by FatFs will have a fixed timestamp
|
||||
/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
|
||||
/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be
|
||||
/ added to the project to read current time form real-time clock. FF_NORTC_MON,
|
||||
/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
|
||||
/ These options have no effect in read-only configuration (FF_FS_READONLY = 1). */
|
||||
|
||||
#define FF_FS_NOFSINFO 0
|
||||
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
|
||||
/ option, and f_getfree() function at first time after volume mount will force
|
||||
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
|
||||
/
|
||||
/ bit0=0: Use free cluster count in the FSINFO if available.
|
||||
/ bit0=1: Do not trust free cluster count in the FSINFO.
|
||||
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
|
||||
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
|
||||
*/
|
||||
|
||||
#define FF_FS_LOCK CONFIG_FATFS_FS_LOCK
|
||||
/* The option FF_FS_LOCK switches file lock function to control duplicated file open
|
||||
/ and illegal operation to open objects. This option must be 0 when FF_FS_READONLY
|
||||
/ is 1.
|
||||
/
|
||||
/ 0: Disable file lock function. To avoid volume corruption, application program
|
||||
/ should avoid illegal open, remove and rename to the open objects.
|
||||
/ >0: Enable file lock function. The value defines how many files/sub-directories
|
||||
/ can be opened simultaneously under file lock control. Note that the file
|
||||
/ lock control is independent of re-entrancy. */
|
||||
|
||||
/* #include <somertos.h> // O/S definitions */
|
||||
#define FF_FS_REENTRANT 1
|
||||
#define FF_FS_TIMEOUT (CONFIG_FATFS_TIMEOUT_MS / portTICK_PERIOD_MS)
|
||||
#define FF_SYNC_t SemaphoreHandle_t
|
||||
/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
|
||||
/ module itself. Note that regardless of this option, file access to different
|
||||
/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
|
||||
/ and f_fdisk() function, are always not re-entrant. Only file/directory access
|
||||
/ to the same volume is under control of this function.
|
||||
/
|
||||
/ 0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect.
|
||||
/ 1: Enable re-entrancy. Also user provided synchronization handlers,
|
||||
/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
|
||||
/ function, must be added to the project. Samples are available in
|
||||
/ option/syscall.c.
|
||||
/
|
||||
/ The FF_FS_TIMEOUT defines timeout period in unit of time tick.
|
||||
/ The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
|
||||
/ SemaphoreHandle_t and etc. A header file for O/S definitions needs to be
|
||||
/ included somewhere in the scope of ff.h. */
|
||||
|
||||
#include <sys/param.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
/* Some memory allocation functions are declared here in addition to ff.h, so that
|
||||
they can be used also by external code when LFN feature is disabled.
|
||||
*/
|
||||
void *ff_memalloc(unsigned msize);
|
||||
void ff_memfree(void *);
|
||||
|
||||
/*--- End of configuration options ---*/
|
||||
|
||||
/* Redefine names of disk IO functions to prevent name collisions */
|
||||
#define disk_initialize ff_disk_initialize
|
||||
#define disk_status ff_disk_status
|
||||
#define disk_read ff_disk_read
|
||||
#define disk_write ff_disk_write
|
||||
#define disk_ioctl ff_disk_ioctl
|
||||
169
code/components/esp-fatfs/src/ffsystem_mh.c
Normal file
169
code/components/esp-fatfs/src/ffsystem_mh.c
Normal file
@@ -0,0 +1,169 @@
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Sample Code of OS Dependent Functions for FatFs */
|
||||
/* (C)ChaN, 2018 */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include "ff_mh.h"
|
||||
|
||||
|
||||
#if FF_USE_LFN == 3 /* Dynamic memory allocation */
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Allocate a memory block */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void* ff_memalloc ( /* Returns pointer to the allocated memory block (null if not enough core) */
|
||||
UINT msize /* Number of bytes to allocate */
|
||||
)
|
||||
{
|
||||
return malloc(msize); /* Allocate a new memory block with POSIX API */
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Free a memory block */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void ff_memfree (
|
||||
void* mblock /* Pointer to the memory block to free (nothing to do if null) */
|
||||
)
|
||||
{
|
||||
free(mblock); /* Free the memory block with POSIX API */
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if FF_FS_REENTRANT /* Mutal exclusion */
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Create a Synchronization Object */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called in f_mount() function to create a new
|
||||
/ synchronization object for the volume, such as semaphore and mutex.
|
||||
/ When a 0 is returned, the f_mount() function fails with FR_INT_ERR.
|
||||
*/
|
||||
|
||||
//const osMutexDef_t Mutex[FF_VOLUMES]; /* Table of CMSIS-RTOS mutex */
|
||||
|
||||
|
||||
int ff_cre_syncobj ( /* 1:Function succeeded, 0:Could not create the sync object */
|
||||
BYTE vol, /* Corresponding volume (logical drive number) */
|
||||
FF_SYNC_t* sobj /* Pointer to return the created sync object */
|
||||
)
|
||||
{
|
||||
/* Win32 */
|
||||
*sobj = CreateMutex(NULL, FALSE, NULL);
|
||||
return (int)(*sobj != INVALID_HANDLE_VALUE);
|
||||
|
||||
/* uITRON */
|
||||
// T_CSEM csem = {TA_TPRI,1,1};
|
||||
// *sobj = acre_sem(&csem);
|
||||
// return (int)(*sobj > 0);
|
||||
|
||||
/* uC/OS-II */
|
||||
// OS_ERR err;
|
||||
// *sobj = OSMutexCreate(0, &err);
|
||||
// return (int)(err == OS_NO_ERR);
|
||||
|
||||
/* FreeRTOS */
|
||||
// *sobj = xSemaphoreCreateMutex();
|
||||
// return (int)(*sobj != NULL);
|
||||
|
||||
/* CMSIS-RTOS */
|
||||
// *sobj = osMutexCreate(&Mutex[vol]);
|
||||
// return (int)(*sobj != NULL);
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Delete a Synchronization Object */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called in f_mount() function to delete a synchronization
|
||||
/ object that created with ff_cre_syncobj() function. When a 0 is returned,
|
||||
/ the f_mount() function fails with FR_INT_ERR.
|
||||
*/
|
||||
|
||||
int ff_del_syncobj ( /* 1:Function succeeded, 0:Could not delete due to an error */
|
||||
FF_SYNC_t sobj /* Sync object tied to the logical drive to be deleted */
|
||||
)
|
||||
{
|
||||
/* Win32 */
|
||||
return (int)CloseHandle(sobj);
|
||||
|
||||
/* uITRON */
|
||||
// return (int)(del_sem(sobj) == E_OK);
|
||||
|
||||
/* uC/OS-II */
|
||||
// OS_ERR err;
|
||||
// OSMutexDel(sobj, OS_DEL_ALWAYS, &err);
|
||||
// return (int)(err == OS_NO_ERR);
|
||||
|
||||
/* FreeRTOS */
|
||||
// vSemaphoreDelete(sobj);
|
||||
// return 1;
|
||||
|
||||
/* CMSIS-RTOS */
|
||||
// return (int)(osMutexDelete(sobj) == osOK);
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Request Grant to Access the Volume */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called on entering file functions to lock the volume.
|
||||
/ When a 0 is returned, the file function fails with FR_TIMEOUT.
|
||||
*/
|
||||
|
||||
int ff_req_grant ( /* 1:Got a grant to access the volume, 0:Could not get a grant */
|
||||
FF_SYNC_t sobj /* Sync object to wait */
|
||||
)
|
||||
{
|
||||
/* Win32 */
|
||||
return (int)(WaitForSingleObject(sobj, FF_FS_TIMEOUT) == WAIT_OBJECT_0);
|
||||
|
||||
/* uITRON */
|
||||
// return (int)(wai_sem(sobj) == E_OK);
|
||||
|
||||
/* uC/OS-II */
|
||||
// OS_ERR err;
|
||||
// OSMutexPend(sobj, FF_FS_TIMEOUT, &err));
|
||||
// return (int)(err == OS_NO_ERR);
|
||||
|
||||
/* FreeRTOS */
|
||||
// return (int)(xSemaphoreTake(sobj, FF_FS_TIMEOUT) == pdTRUE);
|
||||
|
||||
/* CMSIS-RTOS */
|
||||
// return (int)(osMutexWait(sobj, FF_FS_TIMEOUT) == osOK);
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Release Grant to Access the Volume */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called on leaving file functions to unlock the volume.
|
||||
*/
|
||||
|
||||
void ff_rel_grant (
|
||||
FF_SYNC_t sobj /* Sync object to be signaled */
|
||||
)
|
||||
{
|
||||
/* Win32 */
|
||||
ReleaseMutex(sobj);
|
||||
|
||||
/* uITRON */
|
||||
// sig_sem(sobj);
|
||||
|
||||
/* uC/OS-II */
|
||||
// OSMutexPost(sobj);
|
||||
|
||||
/* FreeRTOS */
|
||||
// xSemaphoreGive(sobj);
|
||||
|
||||
/* CMSIS-RTOS */
|
||||
// osMutexRelease(sobj);
|
||||
}
|
||||
|
||||
#endif
|
||||
15593
code/components/esp-fatfs/src/ffunicode_mh.c
Normal file
15593
code/components/esp-fatfs/src/ffunicode_mh.c
Normal file
File diff suppressed because it is too large
Load Diff
321
code/components/esp-fatfs/vfs/esp_vfs_fat_mh.h
Normal file
321
code/components/esp-fatfs/vfs/esp_vfs_fat_mh.h
Normal file
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stddef.h>
|
||||
#include "esp_err.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/sdmmc_types.h"
|
||||
#include "driver/sdspi_host.h"
|
||||
#include "ff_mh.h"
|
||||
#include "wear_levelling.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Register FATFS with VFS component
|
||||
*
|
||||
* This function registers given FAT drive in VFS, at the specified base path.
|
||||
* If only one drive is used, fat_drive argument can be an empty string.
|
||||
* Refer to FATFS library documentation on how to specify FAT drive.
|
||||
* This function also allocates FATFS structure which should be used for f_mount
|
||||
* call.
|
||||
*
|
||||
* @note This function doesn't mount the drive into FATFS, it just connects
|
||||
* POSIX and C standard library IO function with FATFS. You need to mount
|
||||
* desired drive into FATFS separately.
|
||||
*
|
||||
* @param base_path path prefix where FATFS should be registered
|
||||
* @param fat_drive FATFS drive specification; if only one drive is used, can be an empty string
|
||||
* @param max_files maximum number of files which can be open at the same time
|
||||
* @param[out] out_fs pointer to FATFS structure which can be used for FATFS f_mount call is returned via this argument.
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_register was already called
|
||||
* - ESP_ERR_NO_MEM if not enough memory or too many VFSes already registered
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_register(const char* base_path, const char* fat_drive,
|
||||
size_t max_files, FATFS** out_fs);
|
||||
|
||||
/**
|
||||
* @brief Un-register FATFS from VFS
|
||||
*
|
||||
* @note FATFS structure returned by esp_vfs_fat_register is destroyed after
|
||||
* this call. Make sure to call f_mount function to unmount it before
|
||||
* calling esp_vfs_fat_unregister_ctx.
|
||||
* Difference between this function and the one above is that this one
|
||||
* will release the correct drive, while the one above will release
|
||||
* the last registered one
|
||||
*
|
||||
* @param base_path path prefix where FATFS is registered. This is the same
|
||||
* used when esp_vfs_fat_register was called
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if FATFS is not registered in VFS
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_unregister_path(const char* base_path);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Configuration arguments for esp_vfs_fat_sdmmc_mount and esp_vfs_fat_spiflash_mount_rw_wl functions
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* If FAT partition can not be mounted, and this parameter is true,
|
||||
* create partition table and format the filesystem.
|
||||
*/
|
||||
bool format_if_mount_failed;
|
||||
int max_files; ///< Max number of open files
|
||||
/**
|
||||
* If format_if_mount_failed is set, and mount fails, format the card
|
||||
* with given allocation unit size. Must be a power of 2, between sector
|
||||
* size and 128 * sector size.
|
||||
* For SD cards, sector size is always 512 bytes. For wear_levelling,
|
||||
* sector size is determined by CONFIG_WL_SECTOR_SIZE option.
|
||||
*
|
||||
* Using larger allocation unit size will result in higher read/write
|
||||
* performance and higher overhead when storing small files.
|
||||
*
|
||||
* Setting this field to 0 will result in allocation unit set to the
|
||||
* sector size.
|
||||
*/
|
||||
size_t allocation_unit_size;
|
||||
/**
|
||||
* Enables real ff_disk_status function implementation for SD cards
|
||||
* (ff_sdmmc_status). Possibly slows down IO performance.
|
||||
*
|
||||
* Try to enable if you need to handle situations when SD cards
|
||||
* are not unmounted properly before physical removal
|
||||
* or you are experiencing issues with SD cards.
|
||||
*
|
||||
* Doesn't do anything for other memory storage media.
|
||||
*/
|
||||
bool disk_status_check_enable;
|
||||
} esp_vfs_fat_mount_config_t;
|
||||
|
||||
// Compatibility definition
|
||||
typedef esp_vfs_fat_mount_config_t esp_vfs_fat_sdmmc_mount_config_t;
|
||||
|
||||
/**
|
||||
* @brief Convenience function to get FAT filesystem on SD card registered in VFS
|
||||
*
|
||||
* This is an all-in-one function which does the following:
|
||||
* - initializes SDMMC driver or SPI driver with configuration in host_config
|
||||
* - initializes SD card with configuration in slot_config
|
||||
* - mounts FAT partition on SD card using FATFS library, with configuration in mount_config
|
||||
* - registers FATFS library with VFS, with prefix given by base_prefix variable
|
||||
*
|
||||
* This function is intended to make example code more compact.
|
||||
* For real world applications, developers should implement the logic of
|
||||
* probing SD card, locating and mounting partition, and registering FATFS in VFS,
|
||||
* with proper error checking and handling of exceptional conditions.
|
||||
*
|
||||
* @note Use this API to mount a card through SDSPI is deprecated. Please call
|
||||
* `esp_vfs_fat_sdspi_mount()` instead for that case.
|
||||
*
|
||||
* @param base_path path where partition should be registered (e.g. "/sdcard")
|
||||
* @param host_config Pointer to structure describing SDMMC host. When using
|
||||
* SDMMC peripheral, this structure can be initialized using
|
||||
* SDMMC_HOST_DEFAULT() macro. When using SPI peripheral,
|
||||
* this structure can be initialized using SDSPI_HOST_DEFAULT()
|
||||
* macro.
|
||||
* @param slot_config Pointer to structure with slot configuration.
|
||||
* For SDMMC peripheral, pass a pointer to sdmmc_slot_config_t
|
||||
* structure initialized using SDMMC_SLOT_CONFIG_DEFAULT.
|
||||
* @param mount_config pointer to structure with extra parameters for mounting FATFS
|
||||
* @param[out] out_card if not NULL, pointer to the card information structure will be returned via this argument
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called
|
||||
* - ESP_ERR_NO_MEM if memory can not be allocated
|
||||
* - ESP_FAIL if partition can not be mounted
|
||||
* - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path,
|
||||
const sdmmc_host_t* host_config,
|
||||
const void* slot_config,
|
||||
const esp_vfs_fat_mount_config_t* mount_config,
|
||||
sdmmc_card_t** out_card);
|
||||
|
||||
/**
|
||||
* @brief Convenience function to get FAT filesystem on SD card registered in VFS
|
||||
*
|
||||
* This is an all-in-one function which does the following:
|
||||
* - initializes an SPI Master device based on the SPI Master driver with configuration in
|
||||
* slot_config, and attach it to an initialized SPI bus.
|
||||
* - initializes SD card with configuration in host_config_input
|
||||
* - mounts FAT partition on SD card using FATFS library, with configuration in mount_config
|
||||
* - registers FATFS library with VFS, with prefix given by base_prefix variable
|
||||
*
|
||||
* This function is intended to make example code more compact.
|
||||
* For real world applications, developers should implement the logic of
|
||||
* probing SD card, locating and mounting partition, and registering FATFS in VFS,
|
||||
* with proper error checking and handling of exceptional conditions.
|
||||
*
|
||||
* @note This function try to attach the new SD SPI device to the bus specified in host_config.
|
||||
* Make sure the SPI bus specified in `host_config->slot` have been initialized by
|
||||
* `spi_bus_initialize()` before.
|
||||
*
|
||||
* @param base_path path where partition should be registered (e.g. "/sdcard")
|
||||
* @param host_config_input Pointer to structure describing SDMMC host. This structure can be
|
||||
* initialized using SDSPI_HOST_DEFAULT() macro.
|
||||
* @param slot_config Pointer to structure with slot configuration.
|
||||
* For SPI peripheral, pass a pointer to sdspi_device_config_t
|
||||
* structure initialized using SDSPI_DEVICE_CONFIG_DEFAULT().
|
||||
* @param mount_config pointer to structure with extra parameters for mounting FATFS
|
||||
* @param[out] out_card If not NULL, pointer to the card information structure will be returned via
|
||||
* this argument. It is suggested to hold this handle and use it to unmount the card later if
|
||||
* needed. Otherwise it's not suggested to use more than one card at the same time and unmount one
|
||||
* of them in your application.
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called
|
||||
* - ESP_ERR_NO_MEM if memory can not be allocated
|
||||
* - ESP_FAIL if partition can not be mounted
|
||||
* - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_sdspi_mount(const char* base_path,
|
||||
const sdmmc_host_t* host_config_input,
|
||||
const sdspi_device_config_t* slot_config,
|
||||
const esp_vfs_fat_mount_config_t* mount_config,
|
||||
sdmmc_card_t** out_card);
|
||||
|
||||
/**
|
||||
* @brief Unmount FAT filesystem and release resources acquired using esp_vfs_fat_sdmmc_mount
|
||||
*
|
||||
* @deprecated Use `esp_vfs_fat_sdcard_unmount()` instead.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount hasn't been called
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_sdmmc_unmount(void);
|
||||
|
||||
/**
|
||||
* @brief Unmount an SD card from the FAT filesystem and release resources acquired using
|
||||
* `esp_vfs_fat_sdmmc_mount()` or `esp_vfs_fat_sdspi_mount()`
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG if the card argument is unregistered
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount hasn't been called
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_sdcard_unmount(const char* base_path, sdmmc_card_t *card);
|
||||
|
||||
/**
|
||||
* @brief Convenience function to initialize FAT filesystem in SPI flash and register it in VFS
|
||||
*
|
||||
* This is an all-in-one function which does the following:
|
||||
*
|
||||
* - finds the partition with defined partition_label. Partition label should be
|
||||
* configured in the partition table.
|
||||
* - initializes flash wear levelling library on top of the given partition
|
||||
* - mounts FAT partition using FATFS library on top of flash wear levelling
|
||||
* library
|
||||
* - registers FATFS library with VFS, with prefix given by base_prefix variable
|
||||
*
|
||||
* This function is intended to make example code more compact.
|
||||
*
|
||||
* @param base_path path where FATFS partition should be mounted (e.g. "/spiflash")
|
||||
* @param partition_label label of the partition which should be used
|
||||
* @param mount_config pointer to structure with extra parameters for mounting FATFS
|
||||
* @param[out] wl_handle wear levelling driver handle
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_NOT_FOUND if the partition table does not contain FATFS partition with given label
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_spiflash_mount_rw_wl was already called
|
||||
* - ESP_ERR_NO_MEM if memory can not be allocated
|
||||
* - ESP_FAIL if partition can not be mounted
|
||||
* - other error codes from wear levelling library, SPI flash driver, or FATFS drivers
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_spiflash_mount_rw_wl(const char* base_path,
|
||||
const char* partition_label,
|
||||
const esp_vfs_fat_mount_config_t* mount_config,
|
||||
wl_handle_t* wl_handle);
|
||||
|
||||
/**
|
||||
* @brief Unmount FAT filesystem and release resources acquired using esp_vfs_fat_spiflash_mount_rw_wl
|
||||
*
|
||||
* @param base_path path where partition should be registered (e.g. "/spiflash")
|
||||
* @param wl_handle wear levelling driver handle returned by esp_vfs_fat_spiflash_mount_rw_wl
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_spiflash_mount_rw_wl hasn't been called
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_spiflash_unmount_rw_wl(const char* base_path, wl_handle_t wl_handle);
|
||||
|
||||
/**
|
||||
* @brief Convenience function to initialize read-only FAT filesystem and register it in VFS
|
||||
*
|
||||
* This is an all-in-one function which does the following:
|
||||
*
|
||||
* - finds the partition with defined partition_label. Partition label should be
|
||||
* configured in the partition table.
|
||||
* - mounts FAT partition using FATFS library
|
||||
* - registers FATFS library with VFS, with prefix given by base_prefix variable
|
||||
*
|
||||
* @note Wear levelling is not used when FAT is mounted in read-only mode using this function.
|
||||
*
|
||||
* @param base_path path where FATFS partition should be mounted (e.g. "/spiflash")
|
||||
* @param partition_label label of the partition which should be used
|
||||
* @param mount_config pointer to structure with extra parameters for mounting FATFS
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_NOT_FOUND if the partition table does not contain FATFS partition with given label
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_spiflash_mount_ro was already called for the same partition
|
||||
* - ESP_ERR_NO_MEM if memory can not be allocated
|
||||
* - ESP_FAIL if partition can not be mounted
|
||||
* - other error codes from SPI flash driver, or FATFS drivers
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_spiflash_mount_ro(const char* base_path,
|
||||
const char* partition_label,
|
||||
const esp_vfs_fat_mount_config_t* mount_config);
|
||||
|
||||
/**
|
||||
* @brief Unmount FAT filesystem and release resources acquired using esp_vfs_fat_spiflash_mount_ro
|
||||
*
|
||||
* @param base_path path where partition should be registered (e.g. "/spiflash")
|
||||
* @param partition_label label of partition to be unmounted
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_spiflash_mount_rw_wl hasn't been called
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_spiflash_unmount_ro(const char* base_path, const char* partition_label);
|
||||
|
||||
esp_err_t esp_vfs_fat_spiflash_mount(const char* base_path,
|
||||
const char* partition_label,
|
||||
const esp_vfs_fat_mount_config_t* mount_config,
|
||||
wl_handle_t* wl_handle)
|
||||
__attribute__((deprecated("esp_vfs_fat_spiflash_mount is deprecated, please use esp_vfs_fat_spiflash_mount_rw_wl instead")));
|
||||
esp_err_t esp_vfs_fat_spiflash_unmount(const char* base_path, wl_handle_t wl_handle)
|
||||
__attribute__((deprecated("esp_vfs_fat_spiflash_unmount is deprecated, please use esp_vfs_fat_spiflash_unmount_rw_wl instead")));
|
||||
esp_err_t esp_vfs_fat_rawflash_mount(const char* base_path,
|
||||
const char* partition_label,
|
||||
const esp_vfs_fat_mount_config_t* mount_config)
|
||||
__attribute__((deprecated("esp_vfs_fat_rawflash_mount is deprecated, please use esp_vfs_fat_spiflash_mount_ro instead")));
|
||||
esp_err_t esp_vfs_fat_rawflash_unmount(const char* base_path, const char* partition_label)
|
||||
__attribute__((deprecated("esp_vfs_fat_rawflash_unmount is deprecated, please use esp_vfs_fat_spiflash_unmount_ro instead")));
|
||||
|
||||
/**
|
||||
* @brief Get information for FATFS partition
|
||||
*
|
||||
* @param base_path Path where partition should be registered (e.g. "/spiflash")
|
||||
* @param[out] out_total_bytes Size of the file system
|
||||
* @param[out] out_free_bytes Current used bytes in the file system
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if partition not found
|
||||
* - ESP_FAIL if another FRESULT error (saved in errno)
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_info(const char* base_path, uint64_t* out_total_bytes, uint64_t* out_free_bytes);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
30
code/components/esp-fatfs/vfs/vfs_fat_internal_mh.h
Normal file
30
code/components/esp-fatfs/vfs/vfs_fat_internal_mh.h
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_vfs_fat_mh.h"
|
||||
#include <sys/param.h>
|
||||
#include <stddef.h>
|
||||
|
||||
static inline size_t esp_vfs_fat_get_allocation_unit_size(
|
||||
size_t sector_size, size_t requested_size)
|
||||
{
|
||||
size_t alloc_unit_size = requested_size;
|
||||
const size_t max_sectors_per_cylinder = 128;
|
||||
const size_t max_size = sector_size * max_sectors_per_cylinder;
|
||||
alloc_unit_size = MAX(alloc_unit_size, sector_size);
|
||||
alloc_unit_size = MIN(alloc_unit_size, max_size);
|
||||
return alloc_unit_size;
|
||||
}
|
||||
1116
code/components/esp-fatfs/vfs/vfs_fat_mh.c
Normal file
1116
code/components/esp-fatfs/vfs/vfs_fat_mh.c
Normal file
File diff suppressed because it is too large
Load Diff
375
code/components/esp-fatfs/vfs/vfs_fat_sdmmc_mh.c
Normal file
375
code/components/esp-fatfs/vfs/vfs_fat_sdmmc_mh.c
Normal file
@@ -0,0 +1,375 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_vfs_fat_mh.h"
|
||||
#include "vfs_fat_internal_mh.h"
|
||||
#include "driver/sdspi_host.h"
|
||||
#include "sdmmc_cmd_mh.h"
|
||||
#include "diskio_impl_mh.h"
|
||||
#include "diskio_sdmmc_mh.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "driver/sdmmc_defs.h"
|
||||
|
||||
#if SOC_SDMMC_HOST_SUPPORTED
|
||||
#include "driver/sdmmc_host.h"
|
||||
#endif
|
||||
|
||||
static const char* TAG = "vfs_fat_sdmmc";
|
||||
static sdmmc_card_t* s_card = NULL;
|
||||
static uint8_t s_pdrv = FF_DRV_NOT_USED;
|
||||
static char * s_base_path = NULL;
|
||||
|
||||
#define CHECK_EXECUTE_RESULT(err, str) do { \
|
||||
if ((err) !=ESP_OK) { \
|
||||
ESP_LOGE(TAG, str" (0x%x).", err); \
|
||||
goto cleanup; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
static void call_host_deinit(const sdmmc_host_t *host_config);
|
||||
static esp_err_t partition_card(const esp_vfs_fat_mount_config_t *mount_config,
|
||||
const char *drv, sdmmc_card_t *card, BYTE pdrv);
|
||||
|
||||
static esp_err_t mount_prepare_mem(const char *base_path,
|
||||
BYTE *out_pdrv,
|
||||
char **out_dup_path,
|
||||
sdmmc_card_t** out_card)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
char* dup_path = NULL;
|
||||
sdmmc_card_t* card = NULL;
|
||||
|
||||
// connect SDMMC driver to FATFS
|
||||
BYTE pdrv = FF_DRV_NOT_USED;
|
||||
if (ff_diskio_get_drive(&pdrv) != ESP_OK || pdrv == FF_DRV_NOT_USED) {
|
||||
ESP_LOGD(TAG, "the maximum count of volumes is already mounted");
|
||||
return ESP_ERR_NO_MEM;
|
||||
|
||||
}
|
||||
|
||||
// not using ff_memalloc here, as allocation in internal RAM is preferred
|
||||
card = (sdmmc_card_t*)malloc(sizeof(sdmmc_card_t));
|
||||
if (card == NULL) {
|
||||
ESP_LOGD(TAG, "could not locate new sdmmc_card_t");
|
||||
err = ESP_ERR_NO_MEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
dup_path = strdup(base_path);
|
||||
if(!dup_path){
|
||||
ESP_LOGD(TAG, "could not copy base_path");
|
||||
err = ESP_ERR_NO_MEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
*out_card = card;
|
||||
*out_pdrv = pdrv;
|
||||
*out_dup_path = dup_path;
|
||||
return ESP_OK;
|
||||
cleanup:
|
||||
free(card);
|
||||
free(dup_path);
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t mount_to_vfs_fat(const esp_vfs_fat_mount_config_t *mount_config, sdmmc_card_t *card, uint8_t pdrv,
|
||||
const char *base_path)
|
||||
{
|
||||
FATFS* fs = NULL;
|
||||
esp_err_t err;
|
||||
ff_diskio_register_sdmmc(pdrv, card);
|
||||
ff_sdmmc_set_disk_status_check(pdrv, mount_config->disk_status_check_enable);
|
||||
ESP_LOGD(TAG, "using pdrv=%i", pdrv);
|
||||
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||
|
||||
// connect FATFS to VFS
|
||||
err = esp_vfs_fat_register(base_path, drv, mount_config->max_files, &fs);
|
||||
if (err == ESP_ERR_INVALID_STATE) {
|
||||
// it's okay, already registered with VFS
|
||||
} else if (err != ESP_OK) {
|
||||
ESP_LOGD(TAG, "esp_vfs_fat_register failed 0x(%x)", err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Try to mount partition
|
||||
FRESULT res = f_mount(fs, drv, 1);
|
||||
if (res != FR_OK) {
|
||||
err = ESP_FAIL;
|
||||
ESP_LOGW(TAG, "failed to mount card (%d)", res);
|
||||
if (!((res == FR_NO_FILESYSTEM || res == FR_INT_ERR)
|
||||
&& mount_config->format_if_mount_failed)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = partition_card(mount_config, drv, card, pdrv);
|
||||
if (err != ESP_OK) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ESP_LOGW(TAG, "mounting again");
|
||||
res = f_mount(fs, drv, 0);
|
||||
if (res != FR_OK) {
|
||||
err = ESP_FAIL;
|
||||
ESP_LOGD(TAG, "f_mount failed after formatting (%d)", res);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
|
||||
fail:
|
||||
if (fs) {
|
||||
f_mount(NULL, drv, 0);
|
||||
}
|
||||
esp_vfs_fat_unregister_path(base_path);
|
||||
ff_diskio_unregister(pdrv);
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t partition_card(const esp_vfs_fat_mount_config_t *mount_config,
|
||||
const char *drv, sdmmc_card_t *card, BYTE pdrv)
|
||||
{
|
||||
FRESULT res = FR_OK;
|
||||
esp_err_t err;
|
||||
const size_t workbuf_size = 4096;
|
||||
void* workbuf = NULL;
|
||||
ESP_LOGW(TAG, "partitioning card");
|
||||
|
||||
workbuf = ff_memalloc(workbuf_size);
|
||||
if (workbuf == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
LBA_t plist[] = {100, 0, 0, 0};
|
||||
res = f_fdisk(pdrv, plist, workbuf);
|
||||
if (res != FR_OK) {
|
||||
err = ESP_FAIL;
|
||||
ESP_LOGD(TAG, "f_fdisk failed (%d)", res);
|
||||
goto fail;
|
||||
}
|
||||
size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size(
|
||||
card->csd.sector_size,
|
||||
mount_config->allocation_unit_size);
|
||||
ESP_LOGW(TAG, "formatting card, allocation unit size=%d", alloc_unit_size);
|
||||
const MKFS_PARM opt = {(BYTE)FM_ANY, 0, 0, 0, alloc_unit_size};
|
||||
res = f_mkfs(drv, &opt, workbuf, workbuf_size);
|
||||
if (res != FR_OK) {
|
||||
err = ESP_FAIL;
|
||||
ESP_LOGD(TAG, "f_mkfs failed (%d)", res);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
free(workbuf);
|
||||
return ESP_OK;
|
||||
fail:
|
||||
free(workbuf);
|
||||
return err;
|
||||
}
|
||||
|
||||
#if SOC_SDMMC_HOST_SUPPORTED
|
||||
static esp_err_t init_sdmmc_host(int slot, const void *slot_config, int *out_slot)
|
||||
{
|
||||
*out_slot = slot;
|
||||
return sdmmc_host_init_slot(slot, (const sdmmc_slot_config_t*) slot_config);
|
||||
}
|
||||
|
||||
|
||||
esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path,
|
||||
const sdmmc_host_t* host_config,
|
||||
const void* slot_config,
|
||||
const esp_vfs_fat_mount_config_t* mount_config,
|
||||
sdmmc_card_t** out_card)
|
||||
{
|
||||
esp_err_t err;
|
||||
int card_handle = -1; //uninitialized
|
||||
sdmmc_card_t* card = NULL;
|
||||
BYTE pdrv = FF_DRV_NOT_USED;
|
||||
char* dup_path = NULL;
|
||||
bool host_inited = false;
|
||||
|
||||
err = mount_prepare_mem(base_path, &pdrv, &dup_path, &card);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "mount_prepare failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = (*host_config->init)();
|
||||
CHECK_EXECUTE_RESULT(err, "host init failed");
|
||||
//deinit() needs to be called to revert the init
|
||||
host_inited = true;
|
||||
//If this failed (indicated by card_handle != -1), slot deinit needs to called()
|
||||
//leave card_handle as is to indicate that (though slot deinit not implemented yet.
|
||||
err = init_sdmmc_host(host_config->slot, slot_config, &card_handle);
|
||||
CHECK_EXECUTE_RESULT(err, "slot init failed");
|
||||
|
||||
// probe and initialize card
|
||||
err = sdmmc_card_init(host_config, card);
|
||||
CHECK_EXECUTE_RESULT(err, "sdmmc_card_init failed");
|
||||
|
||||
err = mount_to_vfs_fat(mount_config, card, pdrv, dup_path);
|
||||
CHECK_EXECUTE_RESULT(err, "mount_to_vfs failed");
|
||||
|
||||
if (out_card != NULL) {
|
||||
*out_card = card;
|
||||
}
|
||||
if (s_card == NULL) {
|
||||
//store the ctx locally to be back-compatible
|
||||
s_card = card;
|
||||
s_pdrv = pdrv;
|
||||
s_base_path = dup_path;
|
||||
} else {
|
||||
free(dup_path);
|
||||
}
|
||||
return ESP_OK;
|
||||
cleanup:
|
||||
if (host_inited) {
|
||||
call_host_deinit(host_config);
|
||||
}
|
||||
free(card);
|
||||
free(dup_path);
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
static esp_err_t init_sdspi_host(int slot, const void *slot_config, int *out_slot)
|
||||
{
|
||||
esp_err_t err = sdspi_host_init_device((const sdspi_device_config_t*)slot_config, out_slot);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG,
|
||||
"Failed to attach sdspi device onto an SPI bus (rc=0x%x), please initialize the \
|
||||
bus first and check the device parameters."
|
||||
, err);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_fat_sdspi_mount(const char* base_path,
|
||||
const sdmmc_host_t* host_config_input,
|
||||
const sdspi_device_config_t* slot_config,
|
||||
const esp_vfs_fat_mount_config_t* mount_config,
|
||||
sdmmc_card_t** out_card)
|
||||
{
|
||||
const sdmmc_host_t* host_config = host_config_input;
|
||||
esp_err_t err;
|
||||
int card_handle = -1; //uninitialized
|
||||
bool host_inited = false;
|
||||
BYTE pdrv = FF_DRV_NOT_USED;
|
||||
sdmmc_card_t* card = NULL;
|
||||
char* dup_path = NULL;
|
||||
|
||||
err = mount_prepare_mem(base_path, &pdrv, &dup_path, &card);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "mount_prepare failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
//the init() function is usually empty, doesn't require any deinit to revert it
|
||||
err = (*host_config->init)();
|
||||
CHECK_EXECUTE_RESULT(err, "host init failed");
|
||||
|
||||
err = init_sdspi_host(host_config->slot, slot_config, &card_handle);
|
||||
CHECK_EXECUTE_RESULT(err, "slot init failed");
|
||||
//Set `host_inited` to true to indicate that host_config->deinit() needs
|
||||
//to be called to revert `init_sdspi_host`
|
||||
host_inited = true;
|
||||
|
||||
/*
|
||||
* The `slot` argument inside host_config should be replaced by the SD SPI handled returned
|
||||
* above. But the input pointer is const, so create a new variable.
|
||||
*/
|
||||
sdmmc_host_t new_config;
|
||||
if (card_handle != host_config->slot) {
|
||||
new_config = *host_config_input;
|
||||
host_config = &new_config;
|
||||
new_config.slot = card_handle;
|
||||
}
|
||||
|
||||
// probe and initialize card
|
||||
err = sdmmc_card_init(host_config, card);
|
||||
CHECK_EXECUTE_RESULT(err, "sdmmc_card_init failed");
|
||||
|
||||
err = mount_to_vfs_fat(mount_config, card, pdrv, dup_path);
|
||||
CHECK_EXECUTE_RESULT(err, "mount_to_vfs failed");
|
||||
|
||||
if (out_card != NULL) {
|
||||
*out_card = card;
|
||||
}
|
||||
if (s_card == NULL) {
|
||||
//store the ctx locally to be back-compatible
|
||||
s_card = card;
|
||||
s_pdrv = pdrv;
|
||||
s_base_path = dup_path;
|
||||
} else {
|
||||
free(dup_path);
|
||||
}
|
||||
return ESP_OK;
|
||||
|
||||
cleanup:
|
||||
if (host_inited) {
|
||||
call_host_deinit(host_config);
|
||||
}
|
||||
free(card);
|
||||
free(dup_path);
|
||||
return err;
|
||||
|
||||
}
|
||||
|
||||
static void local_card_remove(void)
|
||||
{
|
||||
s_card = NULL;
|
||||
free(s_base_path);
|
||||
s_base_path = NULL;
|
||||
s_pdrv = FF_DRV_NOT_USED;
|
||||
}
|
||||
|
||||
static void call_host_deinit(const sdmmc_host_t *host_config)
|
||||
{
|
||||
if (host_config->flags & SDMMC_HOST_FLAG_DEINIT_ARG) {
|
||||
host_config->deinit_p(host_config->slot);
|
||||
} else {
|
||||
host_config->deinit();
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t unmount_card_core(const char *base_path, sdmmc_card_t *card)
|
||||
{
|
||||
BYTE pdrv = ff_diskio_get_pdrv_card(card);
|
||||
if (pdrv == 0xff) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// unmount
|
||||
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||
f_mount(0, drv, 0);
|
||||
// release SD driver
|
||||
ff_diskio_unregister(pdrv);
|
||||
|
||||
call_host_deinit(&card->host);
|
||||
free(card);
|
||||
|
||||
esp_err_t err = esp_vfs_fat_unregister_path(base_path);
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_fat_sdmmc_unmount(void)
|
||||
{
|
||||
sdmmc_card_t* card = s_card;
|
||||
esp_err_t err = unmount_card_core(s_base_path, card);
|
||||
local_card_remove();
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_fat_sdcard_unmount(const char *base_path, sdmmc_card_t *card)
|
||||
{
|
||||
esp_err_t err = unmount_card_core(base_path, card);
|
||||
if (s_card == card) {
|
||||
local_card_remove();
|
||||
}
|
||||
return err;
|
||||
}
|
||||
218
code/components/esp-fatfs/vfs/vfs_fat_spiflash_mh.c
Normal file
218
code/components/esp-fatfs/vfs/vfs_fat_spiflash_mh.c
Normal file
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_vfs_fat_mh.h"
|
||||
#include "vfs_fat_internal_mh.h"
|
||||
#include "diskio_impl_mh.h"
|
||||
|
||||
#include "diskio_rawflash_mh.h"
|
||||
|
||||
#include "wear_levelling.h"
|
||||
#include "diskio_wl_mh.h"
|
||||
|
||||
static const char* TAG = "vfs_fat_spiflash";
|
||||
|
||||
esp_err_t esp_vfs_fat_spiflash_mount_rw_wl(const char* base_path,
|
||||
const char* partition_label,
|
||||
const esp_vfs_fat_mount_config_t* mount_config,
|
||||
wl_handle_t* wl_handle)
|
||||
{
|
||||
esp_err_t result = ESP_OK;
|
||||
const size_t workbuf_size = 4096;
|
||||
void *workbuf = NULL;
|
||||
|
||||
esp_partition_subtype_t subtype = partition_label ?
|
||||
ESP_PARTITION_SUBTYPE_ANY : ESP_PARTITION_SUBTYPE_DATA_FAT;
|
||||
const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
|
||||
subtype, partition_label);
|
||||
if (data_partition == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to find FATFS partition (type='data', subtype='fat', partition_label='%s'). Check the partition table.", partition_label);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
result = wl_mount(data_partition, wl_handle);
|
||||
if (result != ESP_OK) {
|
||||
ESP_LOGE(TAG, "failed to mount wear levelling layer. result = %i", result);
|
||||
return result;
|
||||
}
|
||||
// connect driver to FATFS
|
||||
BYTE pdrv = 0xFF;
|
||||
if (ff_diskio_get_drive(&pdrv) != ESP_OK) {
|
||||
ESP_LOGD(TAG, "the maximum count of volumes is already mounted");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
ESP_LOGD(TAG, "using pdrv=%i", pdrv);
|
||||
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||
|
||||
result = ff_diskio_register_wl_partition(pdrv, *wl_handle);
|
||||
if (result != ESP_OK) {
|
||||
ESP_LOGE(TAG, "ff_diskio_register_wl_partition failed pdrv=%i, error - 0x(%x)", pdrv, result);
|
||||
goto fail;
|
||||
}
|
||||
FATFS *fs;
|
||||
result = esp_vfs_fat_register(base_path, drv, mount_config->max_files, &fs);
|
||||
if (result == ESP_ERR_INVALID_STATE) {
|
||||
// it's okay, already registered with VFS
|
||||
} else if (result != ESP_OK) {
|
||||
ESP_LOGD(TAG, "esp_vfs_fat_register failed 0x(%x)", result);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Try to mount partition
|
||||
FRESULT fresult = f_mount(fs, drv, 1);
|
||||
if (fresult != FR_OK) {
|
||||
ESP_LOGW(TAG, "f_mount failed (%d)", fresult);
|
||||
if (!((fresult == FR_NO_FILESYSTEM || fresult == FR_INT_ERR)
|
||||
&& mount_config->format_if_mount_failed)) {
|
||||
result = ESP_FAIL;
|
||||
goto fail;
|
||||
}
|
||||
workbuf = ff_memalloc(workbuf_size);
|
||||
if (workbuf == NULL) {
|
||||
result = ESP_ERR_NO_MEM;
|
||||
goto fail;
|
||||
}
|
||||
size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size(
|
||||
CONFIG_WL_SECTOR_SIZE,
|
||||
mount_config->allocation_unit_size);
|
||||
ESP_LOGI(TAG, "Formatting FATFS partition, allocation unit size=%d", alloc_unit_size);
|
||||
const MKFS_PARM opt = {(BYTE)(FM_ANY | FM_SFD), 0, 0, 0, alloc_unit_size};
|
||||
fresult = f_mkfs(drv, &opt, workbuf, workbuf_size);
|
||||
if (fresult != FR_OK) {
|
||||
result = ESP_FAIL;
|
||||
ESP_LOGE(TAG, "f_mkfs failed (%d)", fresult);
|
||||
goto fail;
|
||||
}
|
||||
free(workbuf);
|
||||
workbuf = NULL;
|
||||
ESP_LOGI(TAG, "Mounting again");
|
||||
fresult = f_mount(fs, drv, 0);
|
||||
if (fresult != FR_OK) {
|
||||
result = ESP_FAIL;
|
||||
ESP_LOGE(TAG, "f_mount failed after formatting (%d)", fresult);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
|
||||
fail:
|
||||
free(workbuf);
|
||||
esp_vfs_fat_unregister_path(base_path);
|
||||
ff_diskio_unregister(pdrv);
|
||||
return result;
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_fat_spiflash_unmount_rw_wl(const char* base_path, wl_handle_t wl_handle)
|
||||
{
|
||||
BYTE pdrv = ff_diskio_get_pdrv_wl(wl_handle);
|
||||
if (pdrv == 0xff) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||
|
||||
f_mount(0, drv, 0);
|
||||
ff_diskio_unregister(pdrv);
|
||||
ff_diskio_clear_pdrv_wl(wl_handle);
|
||||
// release partition driver
|
||||
esp_err_t err_drv = wl_unmount(wl_handle);
|
||||
esp_err_t err = esp_vfs_fat_unregister_path(base_path);
|
||||
if (err == ESP_OK) err = err_drv;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t esp_vfs_fat_spiflash_mount_ro(const char* base_path,
|
||||
const char* partition_label,
|
||||
const esp_vfs_fat_mount_config_t* mount_config)
|
||||
{
|
||||
esp_err_t result = ESP_OK;
|
||||
|
||||
const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
|
||||
ESP_PARTITION_SUBTYPE_DATA_FAT, partition_label);
|
||||
if (data_partition == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to find FATFS partition (type='data', subtype='fat', partition_label='%s'). Check the partition table.", partition_label);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
// connect driver to FATFS
|
||||
BYTE pdrv = 0xFF;
|
||||
if (ff_diskio_get_drive(&pdrv) != ESP_OK) {
|
||||
ESP_LOGD(TAG, "the maximum count of volumes is already mounted");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
ESP_LOGD(TAG, "using pdrv=%i", pdrv);
|
||||
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||
|
||||
result = ff_diskio_register_raw_partition(pdrv, data_partition);
|
||||
if (result != ESP_OK) {
|
||||
ESP_LOGE(TAG, "ff_diskio_register_raw_partition failed pdrv=%i, error - 0x(%x)", pdrv, result);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
FATFS *fs;
|
||||
result = esp_vfs_fat_register(base_path, drv, mount_config->max_files, &fs);
|
||||
if (result == ESP_ERR_INVALID_STATE) {
|
||||
// it's okay, already registered with VFS
|
||||
} else if (result != ESP_OK) {
|
||||
ESP_LOGD(TAG, "esp_vfs_fat_register failed 0x(%x)", result);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Try to mount partition
|
||||
FRESULT fresult = f_mount(fs, drv, 1);
|
||||
if (fresult != FR_OK) {
|
||||
ESP_LOGW(TAG, "f_mount failed (%d)", fresult);
|
||||
result = ESP_FAIL;
|
||||
goto fail;
|
||||
}
|
||||
return ESP_OK;
|
||||
|
||||
fail:
|
||||
esp_vfs_fat_unregister_path(base_path);
|
||||
ff_diskio_unregister(pdrv);
|
||||
return result;
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_fat_spiflash_unmount_ro(const char* base_path, const char* partition_label)
|
||||
{
|
||||
const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
|
||||
ESP_PARTITION_SUBTYPE_DATA_FAT, partition_label);
|
||||
|
||||
if (data_partition == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to find FATFS partition (type='data', subtype='fat', partition_label='%s'). Check the partition table.", partition_label);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
BYTE pdrv = ff_diskio_get_pdrv_raw(data_partition);
|
||||
if (pdrv == 0xff) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||
|
||||
f_mount(0, drv, 0);
|
||||
ff_diskio_unregister(pdrv);
|
||||
esp_err_t err = esp_vfs_fat_unregister_path(base_path);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t esp_vfs_fat_spiflash_mount(const char* base_path,
|
||||
const char* partition_label,
|
||||
const esp_vfs_fat_mount_config_t* mount_config,
|
||||
wl_handle_t* wl_handle)
|
||||
__attribute__((alias("esp_vfs_fat_spiflash_mount_rw_wl")));
|
||||
esp_err_t esp_vfs_fat_spiflash_unmount(const char* base_path, wl_handle_t wl_handle)
|
||||
__attribute__((alias("esp_vfs_fat_spiflash_unmount_rw_wl")));
|
||||
esp_err_t esp_vfs_fat_rawflash_mount(const char* base_path,
|
||||
const char* partition_label,
|
||||
const esp_vfs_fat_mount_config_t* mount_config)
|
||||
__attribute__((alias("esp_vfs_fat_spiflash_mount_ro")));
|
||||
esp_err_t esp_vfs_fat_rawflash_unmount(const char* base_path, const char* partition_label)
|
||||
__attribute__((alias("esp_vfs_fat_spiflash_unmount_ro")));
|
||||
1
code/components/esp-nn
Submodule
1
code/components/esp-nn
Submodule
Submodule code/components/esp-nn added at 34e97138de
11
code/components/esp-sdmmc/CMakeLists.txt
Normal file
11
code/components/esp-sdmmc/CMakeLists.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
idf_component_register(SRCS "sdmmc_cmd_mh.c"
|
||||
"sdmmc_common_mh.c"
|
||||
"sdmmc_init_mh.c"
|
||||
"sdmmc_io_mh.c"
|
||||
"sdmmc_mmc_mh.c"
|
||||
"sdmmc_sd_mh.c"
|
||||
INCLUDE_DIRS "." "include"
|
||||
REQUIRES driver esp-fatfs
|
||||
PRIV_REQUIRES soc)
|
||||
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")
|
||||
279
code/components/esp-sdmmc/README.md
Normal file
279
code/components/esp-sdmmc/README.md
Normal file
@@ -0,0 +1,279 @@
|
||||
# AIOTED related changes, see https://github.com/jomjol/AI-on-the-edge-device/pull/2781
|
||||
These files/folders were copied from `framework-espidf@3.50002.230601/components/` and adapted to our own needs.
|
||||
Since not every SD/MMC was recognized and this was due to the implementation of ATA trim support, this was revised.
|
||||
Furthermore, files that we don't need were deleted from it.
|
||||
|
||||
## The most relevant changes are:
|
||||
### fatfs/diskio/diskio_sdmmc.c
|
||||
DRESULT ff_sdmmc_ioctl (BYTE pdrv, BYTE cmd, void* buff), at lines 106 to 110 changed from:
|
||||
```c
|
||||
#if FF_USE_TRIM
|
||||
case CTRL_TRIM:
|
||||
return ff_sdmmc_trim (pdrv, *((DWORD*)buff), //start_sector
|
||||
(*((DWORD*)buff + 1) - *((DWORD*)buff) + 1)); //sector_count
|
||||
#endif //FF_USE_TRIM
|
||||
```
|
||||
to:
|
||||
```c
|
||||
#if (FF_USE_TRIM)
|
||||
case CTRL_TRIM:
|
||||
if(FF_CAN_TRIM){
|
||||
return ff_sdmmc_trim (pdrv, *((DWORD*)buff), //start_sector
|
||||
(*((DWORD*)buff + 1) - *((DWORD*)buff) + 1)); //sector_count
|
||||
}
|
||||
else{
|
||||
return RES_ERROR;
|
||||
}
|
||||
#endif //FF_USE_TRIM
|
||||
```
|
||||
|
||||
### fatfs/src/ff.c
|
||||
added:
|
||||
```c
|
||||
#include "sdmmc_cmd.h"
|
||||
```
|
||||
|
||||
static FRESULT remove_chain(FFOBJID* obj, DWORD clst, DWORD pclst), at lines 1437 to 1454 changed from:
|
||||
```c
|
||||
#if FF_FS_EXFAT || FF_USE_TRIM
|
||||
if (ecl + 1 == nxt) { /* Is next cluster contiguous? */
|
||||
ecl = nxt;
|
||||
} else { /* End of contiguous cluster block */
|
||||
#if FF_FS_EXFAT
|
||||
if (fs->fs_type == FS_EXFAT) {
|
||||
res = change_bitmap(fs, scl, ecl - scl + 1, 0); /* Mark the cluster block 'free' on the bitmap */
|
||||
if (res != FR_OK) return res;
|
||||
}
|
||||
#endif
|
||||
#if FF_USE_TRIM
|
||||
rt[0] = clst2sect(fs, scl); /* Start of data area to be freed */
|
||||
rt[1] = clst2sect(fs, ecl) + fs->csize - 1; /* End of data area to be freed */
|
||||
disk_ioctl(fs->pdrv, CTRL_TRIM, rt); /* Inform storage device that the data in the block may be erased */
|
||||
#endif
|
||||
scl = ecl = nxt;
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
to:
|
||||
```c
|
||||
#if FF_FS_EXFAT || FF_USE_TRIM
|
||||
if(FF_FS_EXFAT || FF_CAN_TRIM){
|
||||
if (ecl + 1 == nxt) { /* Is next cluster contiguous? */
|
||||
ecl = nxt;
|
||||
}
|
||||
else { /* End of contiguous cluster block */
|
||||
#if FF_FS_EXFAT
|
||||
if (fs->fs_type == FS_EXFAT) {
|
||||
res = change_bitmap(fs, scl, ecl - scl + 1, 0); /* Mark the cluster block 'free' on the bitmap */
|
||||
if (res != FR_OK) return res;
|
||||
}
|
||||
#endif
|
||||
#if FF_USE_TRIM
|
||||
if(FF_CAN_TRIM){
|
||||
rt[0] = clst2sect(fs, scl); /* Start of data area to be freed */
|
||||
rt[1] = clst2sect(fs, ecl) + fs->csize - 1; /* End of data area to be freed */
|
||||
disk_ioctl(fs->pdrv, CTRL_TRIM, rt); /* Inform storage device that the data in the block may be erased */
|
||||
}
|
||||
#endif
|
||||
scl = ecl = nxt;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
FRESULT f_mkfs(const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len), at lines 5946 to 5949 changed from:
|
||||
```c
|
||||
#if FF_USE_TRIM
|
||||
lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1; /* Inform storage device that the volume area may be erased */
|
||||
disk_ioctl(pdrv, CTRL_TRIM, lba);
|
||||
#endif
|
||||
```
|
||||
to:
|
||||
```c
|
||||
#if FF_USE_TRIM
|
||||
if(FF_CAN_TRIM){
|
||||
lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1; /* Inform storage device that the volume area may be erased */
|
||||
disk_ioctl(pdrv, CTRL_TRIM, lba);
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
FRESULT f_mkfs(const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len), at lines 6175 to 6178 changed from:
|
||||
```c
|
||||
#if FF_USE_TRIM
|
||||
lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1; /* Inform storage device that the volume area may be erased */
|
||||
disk_ioctl(pdrv, CTRL_TRIM, lba);
|
||||
#endif
|
||||
```
|
||||
to:
|
||||
```c
|
||||
#if FF_USE_TRIM
|
||||
if(FF_CAN_TRIM){
|
||||
lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1; /* Inform storage device that the volume area may be erased */
|
||||
disk_ioctl(pdrv, CTRL_TRIM, lba);
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
### sdmmc/sdmmc_cmd.c
|
||||
added:
|
||||
```c
|
||||
int FF_CAN_TRIM = 0;
|
||||
```
|
||||
|
||||
esp_err_t sdmmc_can_trim(sdmmc_card_t* card), at lines 630 to 636 changed from:
|
||||
```c
|
||||
esp_err_t sdmmc_can_trim(sdmmc_card_t* card)
|
||||
{
|
||||
if ((card->is_mmc) && (card->ext_csd.sec_feature & EXT_CSD_SEC_GB_CL_EN)) {
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
```
|
||||
to:
|
||||
```c
|
||||
esp_err_t sdmmc_can_trim(sdmmc_card_t* card)
|
||||
{
|
||||
if ((card->is_mmc) && (card->ext_csd.sec_feature & EXT_CSD_SEC_GB_CL_EN)) {
|
||||
FF_CAN_TRIM = 1;
|
||||
return ESP_OK;
|
||||
}
|
||||
FF_CAN_TRIM = 0;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
```
|
||||
|
||||
### sdmmc/include/sdmmc_cmd.h
|
||||
|
||||
added:
|
||||
```c
|
||||
extern int FF_CAN_TRIM;
|
||||
```
|
||||
|
||||
|
||||
# Espressif IoT Development Framework
|
||||
|
||||
* [中文版](./README_CN.md)
|
||||
|
||||
ESP-IDF is the development framework for Espressif SoCs supported on Windows, Linux and macOS.
|
||||
|
||||
# ESP-IDF Release Support Schedule
|
||||
|
||||

|
||||
|
||||
- Please read [the support policy](SUPPORT_POLICY.md) and [the documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/versions.html) for more information about ESP-IDF versions.
|
||||
- Please see the [End-of-Life Advisories](https://www.espressif.com/en/support/documents/advisories?keys=&field_type_of_advisory_tid%5B%5D=817) for information about ESP-IDF releases with discontinued support.
|
||||
|
||||
# ESP-IDF Release and SoC Compatibility
|
||||
|
||||
The following table shows ESP-IDF support of Espressif SoCs where ![alt text][preview] and ![alt text][supported] denote preview status and support, respectively. The preview support is usually limited in time and intended for beta versions of chips. Please use an ESP-IDF release where the desired SoC is already supported.
|
||||
|
||||
|Chip | v4.1 | v4.2 | v4.3 | v4.4 | v5.0 | |
|
||||
|:----------- |:---------------------:| :---------------------:| :---------------------:| :---------------------:| :---------------------:|:------------------------------------------------------------------------------------ |
|
||||
|ESP32 |![alt text][supported] | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | |
|
||||
|ESP32-S2 | | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | |
|
||||
|ESP32-C3 | | | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | |
|
||||
|ESP32-S3 | | | | ![alt text][supported] | ![alt text][supported] | [Announcement](https://www.espressif.com/en/news/ESP32_S3) |
|
||||
|ESP32-C2 | | | | | ![alt text][supported] | [Announcement](https://blog.espressif.com/esp32-c2-and-why-it-matter-s-bcf4d7d0b2c6) |
|
||||
|ESP32-H2 | | | | ![alt text][preview] | ![alt text][preview] | [Announcement](https://www.espressif.com/en/news/ESP32_H2) |
|
||||
|
||||
[supported]: https://img.shields.io/badge/-supported-green "supported"
|
||||
[preview]: https://img.shields.io/badge/-preview-orange "preview"
|
||||
|
||||
Espressif SoCs released before 2016 (ESP8266 and ESP8285) are supported by [RTOS SDK](https://github.com/espressif/ESP8266_RTOS_SDK) instead.
|
||||
|
||||
# Developing With ESP-IDF
|
||||
|
||||
## Setting Up ESP-IDF
|
||||
|
||||
See https://idf.espressif.com/ for links to detailed instructions on how to set up the ESP-IDF depending on chip you use.
|
||||
|
||||
**Note:** Each SoC series and each ESP-IDF release has its own documentation. Please see Section [Versions](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/versions.html) on how to find documentation and how to checkout specific release of ESP-IDF.
|
||||
|
||||
### Non-GitHub forks
|
||||
|
||||
ESP-IDF uses relative locations as its submodules URLs ([.gitmodules](.gitmodules)). So they link to GitHub. If ESP-IDF is forked to a Git repository which is not on GitHub, you will need to run the script [tools/set-submodules-to-github.sh](tools/set-submodules-to-github.sh) after git clone.
|
||||
|
||||
The script sets absolute URLs for all submodules, allowing `git submodule update --init --recursive` to complete. If cloning ESP-IDF from GitHub, this step is not needed.
|
||||
|
||||
## Finding a Project
|
||||
|
||||
As well as the [esp-idf-template](https://github.com/espressif/esp-idf-template) project mentioned in Getting Started, ESP-IDF comes with some example projects in the [examples](examples) directory.
|
||||
|
||||
Once you've found the project you want to work with, change to its directory and you can configure and build it.
|
||||
|
||||
To start your own project based on an example, copy the example project directory outside of the ESP-IDF directory.
|
||||
|
||||
# Quick Reference
|
||||
|
||||
See the Getting Started guide links above for a detailed setup guide. This is a quick reference for common commands when working with ESP-IDF projects:
|
||||
|
||||
## Setup Build Environment
|
||||
|
||||
(See the Getting Started guide listed above for a full list of required steps with more details.)
|
||||
|
||||
* Install host build dependencies mentioned in the Getting Started guide.
|
||||
* Run the install script to set up the build environment. The options include `install.bat` or `install.ps1` for Windows, and `install.sh` or `install.fish` for Unix shells.
|
||||
* Run the export script on Windows (`export.bat`) or source it on Unix (`source export.sh`) in every shell environment before using ESP-IDF.
|
||||
|
||||
## Configuring the Project
|
||||
|
||||
* `idf.py set-target <chip_name>` sets the target of the project to `<chip_name>`. Run `idf.py set-target` without any arguments to see a list of supported targets.
|
||||
* `idf.py menuconfig` opens a text-based configuration menu where you can configure the project.
|
||||
|
||||
## Compiling the Project
|
||||
|
||||
`idf.py build`
|
||||
|
||||
... will compile app, bootloader and generate a partition table based on the config.
|
||||
|
||||
## Flashing the Project
|
||||
|
||||
When the build finishes, it will print a command line to use esptool.py to flash the chip. However you can also do this automatically by running:
|
||||
|
||||
`idf.py -p PORT flash`
|
||||
|
||||
Replace PORT with the name of your serial port (like `COM3` on Windows, `/dev/ttyUSB0` on Linux, or `/dev/cu.usbserial-X` on MacOS. If the `-p` option is left out, `idf.py flash` will try to flash the first available serial port.
|
||||
|
||||
This will flash the entire project (app, bootloader and partition table) to a new chip. The settings for serial port flashing can be configured with `idf.py menuconfig`.
|
||||
|
||||
You don't need to run `idf.py build` before running `idf.py flash`, `idf.py flash` will automatically rebuild anything which needs it.
|
||||
|
||||
## Viewing Serial Output
|
||||
|
||||
The `idf.py monitor` target uses the [idf_monitor tool](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/idf-monitor.html) to display serial output from Espressif SoCs. idf_monitor also has a range of features to decode crash output and interact with the device. [Check the documentation page for details](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/idf-monitor.html).
|
||||
|
||||
Exit the monitor by typing Ctrl-].
|
||||
|
||||
To build, flash and monitor output in one pass, you can run:
|
||||
|
||||
`idf.py flash monitor`
|
||||
|
||||
## Compiling & Flashing Only the App
|
||||
|
||||
After the initial flash, you may just want to build and flash just your app, not the bootloader and partition table:
|
||||
|
||||
* `idf.py app` - build just the app.
|
||||
* `idf.py app-flash` - flash just the app.
|
||||
|
||||
`idf.py app-flash` will automatically rebuild the app if any source files have changed.
|
||||
|
||||
(In normal development there's no downside to reflashing the bootloader and partition table each time, if they haven't changed.)
|
||||
|
||||
## Erasing Flash
|
||||
|
||||
The `idf.py flash` target does not erase the entire flash contents. However it is sometimes useful to set the device back to a totally erased state, particularly when making partition table changes or OTA app updates. To erase the entire flash, run `idf.py erase-flash`.
|
||||
|
||||
This can be combined with other targets, ie `idf.py -p PORT erase-flash flash` will erase everything and then re-flash the new app, bootloader and partition table.
|
||||
|
||||
# Resources
|
||||
|
||||
* Documentation for the latest version: https://docs.espressif.com/projects/esp-idf/. This documentation is built from the [docs directory](docs) of this repository.
|
||||
|
||||
* The [esp32.com forum](https://esp32.com/) is a place to ask questions and find community resources.
|
||||
|
||||
* [Check the Issues section on github](https://github.com/espressif/esp-idf/issues) if you find a bug or have a feature request. Please check existing Issues before opening a new one.
|
||||
|
||||
* If you're interested in contributing to ESP-IDF, please check the [Contributions Guide](https://docs.espressif.com/projects/esp-idf/en/latest/contribute/index.html).
|
||||
357
code/components/esp-sdmmc/include/sdmmc_cmd_mh.h
Normal file
357
code/components/esp-sdmmc/include/sdmmc_cmd_mh.h
Normal file
@@ -0,0 +1,357 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include "esp_err.h"
|
||||
#include "driver/sdmmc_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern int FF_CAN_TRIM;
|
||||
|
||||
/**
|
||||
* Probe and initialize SD/MMC card using given host
|
||||
*
|
||||
* @note Only SD cards (SDSC and SDHC/SDXC) are supported now.
|
||||
* Support for MMC/eMMC cards will be added later.
|
||||
*
|
||||
* @param host pointer to structure defining host controller
|
||||
* @param out_card pointer to structure which will receive information
|
||||
* about the card when the function completes
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - One of the error codes from SDMMC host controller
|
||||
*/
|
||||
esp_err_t sdmmc_card_init(const sdmmc_host_t* host,
|
||||
sdmmc_card_t* out_card);
|
||||
|
||||
/**
|
||||
* @brief Print information about the card to a stream
|
||||
* @param stream stream obtained using fopen or fdopen
|
||||
* @param card card information structure initialized using sdmmc_card_init
|
||||
*/
|
||||
void sdmmc_card_print_info(FILE* stream, const sdmmc_card_t* card);
|
||||
|
||||
/**
|
||||
* Get status of SD/MMC card
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - One of the error codes from SDMMC host controller
|
||||
*/
|
||||
esp_err_t sdmmc_get_status(sdmmc_card_t* card);
|
||||
|
||||
/**
|
||||
* Write given number of sectors to SD/MMC card
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @param src pointer to data buffer to read data from; data size must be
|
||||
* equal to sector_count * card->csd.sector_size
|
||||
* @param start_sector sector where to start writing
|
||||
* @param sector_count number of sectors to write
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - One of the error codes from SDMMC host controller
|
||||
*/
|
||||
esp_err_t sdmmc_write_sectors(sdmmc_card_t* card, const void* src,
|
||||
size_t start_sector, size_t sector_count);
|
||||
|
||||
/**
|
||||
* Read given number of sectors from the SD/MMC card
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @param dst pointer to data buffer to write into; buffer size must be
|
||||
* at least sector_count * card->csd.sector_size
|
||||
* @param start_sector sector where to start reading
|
||||
* @param sector_count number of sectors to read
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - One of the error codes from SDMMC host controller
|
||||
*/
|
||||
esp_err_t sdmmc_read_sectors(sdmmc_card_t* card, void* dst,
|
||||
size_t start_sector, size_t sector_count);
|
||||
|
||||
/**
|
||||
* Erase given number of sectors from the SD/MMC card
|
||||
*
|
||||
* @note When sdmmc_erase_sectors used with cards in SDSPI mode, it was
|
||||
* observed that card requires re-init after erase operation.
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @param start_sector sector where to start erase
|
||||
* @param sector_count number of sectors to erase
|
||||
* @param arg erase command (CMD38) argument
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - One of the error codes from SDMMC host controller
|
||||
*/
|
||||
esp_err_t sdmmc_erase_sectors(sdmmc_card_t* card, size_t start_sector,
|
||||
size_t sector_count, sdmmc_erase_arg_t arg);
|
||||
|
||||
/**
|
||||
* Check if SD/MMC card supports discard
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @return
|
||||
* - ESP_OK if supported by the card/device
|
||||
* - ESP_FAIL if not supported by the card/device
|
||||
*/
|
||||
esp_err_t sdmmc_can_discard(sdmmc_card_t* card);
|
||||
|
||||
/**
|
||||
* Check if SD/MMC card supports trim
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @return
|
||||
* - ESP_OK if supported by the card/device
|
||||
* - ESP_FAIL if not supported by the card/device
|
||||
*/
|
||||
esp_err_t sdmmc_can_trim(sdmmc_card_t* card);
|
||||
|
||||
/**
|
||||
* Check if SD/MMC card supports sanitize
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @return
|
||||
* - ESP_OK if supported by the card/device
|
||||
* - ESP_FAIL if not supported by the card/device
|
||||
*/
|
||||
esp_err_t sdmmc_mmc_can_sanitize(sdmmc_card_t* card);
|
||||
|
||||
/**
|
||||
* Sanitize the data that was unmapped by a Discard command
|
||||
*
|
||||
* @note Discard command has to precede sanitize operation. To discard, use
|
||||
* MMC_DICARD_ARG with sdmmc_erase_sectors argument
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @param timeout_ms timeout value in milliseconds required to sanitize the
|
||||
* selected range of sectors.
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - One of the error codes from SDMMC host controller
|
||||
*/
|
||||
esp_err_t sdmmc_mmc_sanitize(sdmmc_card_t* card, uint32_t timeout_ms);
|
||||
|
||||
/**
|
||||
* Erase complete SD/MMC card
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - One of the error codes from SDMMC host controller
|
||||
*/
|
||||
esp_err_t sdmmc_full_erase(sdmmc_card_t* card);
|
||||
|
||||
/**
|
||||
* Read one byte from an SDIO card using IO_RW_DIRECT (CMD52)
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @param function IO function number
|
||||
* @param reg byte address within IO function
|
||||
* @param[out] out_byte output, receives the value read from the card
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - One of the error codes from SDMMC host controller
|
||||
*/
|
||||
esp_err_t sdmmc_io_read_byte(sdmmc_card_t* card, uint32_t function,
|
||||
uint32_t reg, uint8_t *out_byte);
|
||||
|
||||
/**
|
||||
* Write one byte to an SDIO card using IO_RW_DIRECT (CMD52)
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @param function IO function number
|
||||
* @param reg byte address within IO function
|
||||
* @param in_byte value to be written
|
||||
* @param[out] out_byte if not NULL, receives new byte value read
|
||||
* from the card (read-after-write).
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - One of the error codes from SDMMC host controller
|
||||
*/
|
||||
esp_err_t sdmmc_io_write_byte(sdmmc_card_t* card, uint32_t function,
|
||||
uint32_t reg, uint8_t in_byte, uint8_t* out_byte);
|
||||
|
||||
/**
|
||||
* Read multiple bytes from an SDIO card using IO_RW_EXTENDED (CMD53)
|
||||
*
|
||||
* This function performs read operation using CMD53 in byte mode.
|
||||
* For block mode, see sdmmc_io_read_blocks.
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @param function IO function number
|
||||
* @param addr byte address within IO function where reading starts
|
||||
* @param dst buffer which receives the data read from card
|
||||
* @param size number of bytes to read
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_SIZE if size exceeds 512 bytes
|
||||
* - One of the error codes from SDMMC host controller
|
||||
*/
|
||||
esp_err_t sdmmc_io_read_bytes(sdmmc_card_t* card, uint32_t function,
|
||||
uint32_t addr, void* dst, size_t size);
|
||||
|
||||
/**
|
||||
* Write multiple bytes to an SDIO card using IO_RW_EXTENDED (CMD53)
|
||||
*
|
||||
* This function performs write operation using CMD53 in byte mode.
|
||||
* For block mode, see sdmmc_io_write_blocks.
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @param function IO function number
|
||||
* @param addr byte address within IO function where writing starts
|
||||
* @param src data to be written
|
||||
* @param size number of bytes to write
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_SIZE if size exceeds 512 bytes
|
||||
* - One of the error codes from SDMMC host controller
|
||||
*/
|
||||
esp_err_t sdmmc_io_write_bytes(sdmmc_card_t* card, uint32_t function,
|
||||
uint32_t addr, const void* src, size_t size);
|
||||
|
||||
/**
|
||||
* Read blocks of data from an SDIO card using IO_RW_EXTENDED (CMD53)
|
||||
*
|
||||
* This function performs read operation using CMD53 in block mode.
|
||||
* For byte mode, see sdmmc_io_read_bytes.
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @param function IO function number
|
||||
* @param addr byte address within IO function where writing starts
|
||||
* @param dst buffer which receives the data read from card
|
||||
* @param size number of bytes to read, must be divisible by the card block
|
||||
* size.
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_SIZE if size is not divisible by 512 bytes
|
||||
* - One of the error codes from SDMMC host controller
|
||||
*/
|
||||
esp_err_t sdmmc_io_read_blocks(sdmmc_card_t* card, uint32_t function,
|
||||
uint32_t addr, void* dst, size_t size);
|
||||
|
||||
/**
|
||||
* Write blocks of data to an SDIO card using IO_RW_EXTENDED (CMD53)
|
||||
*
|
||||
* This function performs write operation using CMD53 in block mode.
|
||||
* For byte mode, see sdmmc_io_write_bytes.
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @param function IO function number
|
||||
* @param addr byte address within IO function where writing starts
|
||||
* @param src data to be written
|
||||
* @param size number of bytes to read, must be divisible by the card block
|
||||
* size.
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_SIZE if size is not divisible by 512 bytes
|
||||
* - One of the error codes from SDMMC host controller
|
||||
*/
|
||||
esp_err_t sdmmc_io_write_blocks(sdmmc_card_t* card, uint32_t function,
|
||||
uint32_t addr, const void* src, size_t size);
|
||||
|
||||
/**
|
||||
* Enable SDIO interrupt in the SDMMC host
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_NOT_SUPPORTED if the host controller does not support
|
||||
* IO interrupts
|
||||
*/
|
||||
esp_err_t sdmmc_io_enable_int(sdmmc_card_t* card);
|
||||
|
||||
/**
|
||||
* Block until an SDIO interrupt is received
|
||||
*
|
||||
* Slave uses D1 line to signal interrupt condition to the host.
|
||||
* This function can be used to wait for the interrupt.
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @param timeout_ticks time to wait for the interrupt, in RTOS ticks
|
||||
* @return
|
||||
* - ESP_OK if the interrupt is received
|
||||
* - ESP_ERR_NOT_SUPPORTED if the host controller does not support
|
||||
* IO interrupts
|
||||
* - ESP_ERR_TIMEOUT if the interrupt does not happen in timeout_ticks
|
||||
*/
|
||||
esp_err_t sdmmc_io_wait_int(sdmmc_card_t* card, TickType_t timeout_ticks);
|
||||
|
||||
/**
|
||||
* Get the data of CIS region of an SDIO card.
|
||||
*
|
||||
* You may provide a buffer not sufficient to store all the CIS data. In this
|
||||
* case, this function stores as much data into your buffer as possible. Also,
|
||||
* this function will try to get and return the size required for you.
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @param out_buffer Output buffer of the CIS data
|
||||
* @param buffer_size Size of the buffer.
|
||||
* @param inout_cis_size Mandatory, pointer to a size, input and output.
|
||||
* - input: Limitation of maximum searching range, should be 0 or larger than
|
||||
* buffer_size. The function searches for CIS_CODE_END until this range. Set to
|
||||
* 0 to search infinitely.
|
||||
* - output: The size required to store all the CIS data, if CIS_CODE_END is found.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: on success
|
||||
* - ESP_ERR_INVALID_RESPONSE: if the card does not (correctly) support CIS.
|
||||
* - ESP_ERR_INVALID_SIZE: CIS_CODE_END found, but buffer_size is less than
|
||||
* required size, which is stored in the inout_cis_size then.
|
||||
* - ESP_ERR_NOT_FOUND: if the CIS_CODE_END not found. Increase input value of
|
||||
* inout_cis_size or set it to 0, if you still want to search for the end;
|
||||
* output value of inout_cis_size is invalid in this case.
|
||||
* - and other error code return from sdmmc_io_read_bytes
|
||||
*/
|
||||
esp_err_t sdmmc_io_get_cis_data(sdmmc_card_t* card, uint8_t* out_buffer, size_t buffer_size, size_t* inout_cis_size);
|
||||
|
||||
/**
|
||||
* Parse and print the CIS information of an SDIO card.
|
||||
*
|
||||
* @note Not all the CIS codes and all kinds of tuples are supported. If you
|
||||
* see some unresolved code, you can add the parsing of these code in
|
||||
* sdmmc_io.c and contribute to the IDF through the Github repository.
|
||||
*
|
||||
* using sdmmc_card_init
|
||||
* @param buffer Buffer to parse
|
||||
* @param buffer_size Size of the buffer.
|
||||
* @param fp File pointer to print to, set to NULL to print to stdout.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: on success
|
||||
* - ESP_ERR_NOT_SUPPORTED: if the value from the card is not supported to be parsed.
|
||||
* - ESP_ERR_INVALID_SIZE: if the CIS size fields are not correct.
|
||||
*/
|
||||
esp_err_t sdmmc_io_print_cis_info(uint8_t* buffer, size_t buffer_size, FILE* fp);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
701
code/components/esp-sdmmc/sdmmc_cmd_mh.c
Normal file
701
code/components/esp-sdmmc/sdmmc_cmd_mh.c
Normal file
@@ -0,0 +1,701 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "sdmmc_common_mh.h"
|
||||
|
||||
int FF_CAN_TRIM = 0;
|
||||
|
||||
static const char* TAG = "sdmmc_cmd";
|
||||
|
||||
|
||||
esp_err_t sdmmc_send_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd)
|
||||
{
|
||||
if (card->host.command_timeout_ms != 0) {
|
||||
cmd->timeout_ms = card->host.command_timeout_ms;
|
||||
} else if (cmd->timeout_ms == 0) {
|
||||
cmd->timeout_ms = SDMMC_DEFAULT_CMD_TIMEOUT_MS;
|
||||
}
|
||||
|
||||
int slot = card->host.slot;
|
||||
ESP_LOGV(TAG, "sending cmd slot=%d op=%d arg=%x flags=%x data=%p blklen=%d datalen=%d timeout=%d",
|
||||
slot, cmd->opcode, cmd->arg, cmd->flags, cmd->data, cmd->blklen, cmd->datalen, cmd->timeout_ms);
|
||||
esp_err_t err = (*card->host.do_transaction)(slot, cmd);
|
||||
if (err != 0) {
|
||||
ESP_LOGD(TAG, "cmd=%d, sdmmc_req_run returned 0x%x", cmd->opcode, err);
|
||||
return err;
|
||||
}
|
||||
int state = MMC_R1_CURRENT_STATE(cmd->response);
|
||||
ESP_LOGV(TAG, "cmd response %08x %08x %08x %08x err=0x%x state=%d",
|
||||
cmd->response[0],
|
||||
cmd->response[1],
|
||||
cmd->response[2],
|
||||
cmd->response[3],
|
||||
cmd->error,
|
||||
state);
|
||||
return cmd->error;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_send_app_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd)
|
||||
{
|
||||
sdmmc_command_t app_cmd = {
|
||||
.opcode = MMC_APP_CMD,
|
||||
.flags = SCF_CMD_AC | SCF_RSP_R1,
|
||||
.arg = MMC_ARG_RCA(card->rca),
|
||||
};
|
||||
esp_err_t err = sdmmc_send_cmd(card, &app_cmd);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
// Check APP_CMD status bit (only in SD mode)
|
||||
if (!host_is_spi(card) && !(MMC_R1(app_cmd.response) & MMC_R1_APP_CMD)) {
|
||||
ESP_LOGW(TAG, "card doesn't support APP_CMD");
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
return sdmmc_send_cmd(card, cmd);
|
||||
}
|
||||
|
||||
|
||||
esp_err_t sdmmc_send_cmd_go_idle_state(sdmmc_card_t* card)
|
||||
{
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = MMC_GO_IDLE_STATE,
|
||||
.flags = SCF_CMD_BC | SCF_RSP_R0,
|
||||
};
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (host_is_spi(card)) {
|
||||
/* To enter SPI mode, CMD0 needs to be sent twice (see figure 4-1 in
|
||||
* SD Simplified spec v4.10). Some cards enter SD mode on first CMD0,
|
||||
* so don't expect the above command to succeed.
|
||||
* SCF_RSP_R1 flag below tells the lower layer to expect correct R1
|
||||
* response (in SPI mode).
|
||||
*/
|
||||
(void) err;
|
||||
vTaskDelay(SDMMC_GO_IDLE_DELAY_MS / portTICK_PERIOD_MS);
|
||||
|
||||
cmd.flags |= SCF_RSP_R1;
|
||||
err = sdmmc_send_cmd(card, &cmd);
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
vTaskDelay(SDMMC_GO_IDLE_DELAY_MS / portTICK_PERIOD_MS);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t sdmmc_send_cmd_send_if_cond(sdmmc_card_t* card, uint32_t ocr)
|
||||
{
|
||||
const uint8_t pattern = 0xaa; /* any pattern will do here */
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = SD_SEND_IF_COND,
|
||||
.arg = (((ocr & SD_OCR_VOL_MASK) != 0) << 8) | pattern,
|
||||
.flags = SCF_CMD_BCR | SCF_RSP_R7,
|
||||
};
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
uint8_t response = cmd.response[0] & 0xff;
|
||||
if (response != pattern) {
|
||||
ESP_LOGD(TAG, "%s: received=0x%x expected=0x%x", __func__, response, pattern);
|
||||
return ESP_ERR_INVALID_RESPONSE;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_send_cmd_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp)
|
||||
{
|
||||
esp_err_t err;
|
||||
|
||||
sdmmc_command_t cmd = {
|
||||
.arg = ocr,
|
||||
.flags = SCF_CMD_BCR | SCF_RSP_R3,
|
||||
.opcode = SD_APP_OP_COND
|
||||
};
|
||||
int nretries = SDMMC_SEND_OP_COND_MAX_RETRIES;
|
||||
int err_cnt = SDMMC_SEND_OP_COND_MAX_ERRORS;
|
||||
for (; nretries != 0; --nretries) {
|
||||
bzero(&cmd, sizeof cmd);
|
||||
cmd.arg = ocr;
|
||||
cmd.flags = SCF_CMD_BCR | SCF_RSP_R3;
|
||||
if (!card->is_mmc) { /* SD mode */
|
||||
cmd.opcode = SD_APP_OP_COND;
|
||||
err = sdmmc_send_app_cmd(card, &cmd);
|
||||
} else { /* MMC mode */
|
||||
cmd.arg &= ~MMC_OCR_ACCESS_MODE_MASK;
|
||||
cmd.arg |= MMC_OCR_SECTOR_MODE;
|
||||
cmd.opcode = MMC_SEND_OP_COND;
|
||||
err = sdmmc_send_cmd(card, &cmd);
|
||||
}
|
||||
|
||||
if (err != ESP_OK) {
|
||||
if (--err_cnt == 0) {
|
||||
ESP_LOGD(TAG, "%s: sdmmc_send_app_cmd err=0x%x", __func__, err);
|
||||
return err;
|
||||
} else {
|
||||
ESP_LOGV(TAG, "%s: ignoring err=0x%x", __func__, err);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// In SD protocol, card sets MEM_READY bit in OCR when it is ready.
|
||||
// In SPI protocol, card clears IDLE_STATE bit in R1 response.
|
||||
if (!host_is_spi(card)) {
|
||||
if ((MMC_R3(cmd.response) & MMC_OCR_MEM_READY) ||
|
||||
ocr == 0) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if ((SD_SPI_R1(cmd.response) & SD_SPI_R1_IDLE_STATE) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
}
|
||||
if (nretries == 0) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
if (ocrp) {
|
||||
*ocrp = MMC_R3(cmd.response);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_send_cmd_read_ocr(sdmmc_card_t *card, uint32_t *ocrp)
|
||||
{
|
||||
assert(ocrp);
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = SD_READ_OCR,
|
||||
.flags = SCF_CMD_BCR | SCF_RSP_R2
|
||||
};
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
*ocrp = SD_SPI_R3(cmd.response);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t sdmmc_send_cmd_all_send_cid(sdmmc_card_t* card, sdmmc_response_t* out_raw_cid)
|
||||
{
|
||||
assert(out_raw_cid);
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = MMC_ALL_SEND_CID,
|
||||
.flags = SCF_CMD_BCR | SCF_RSP_R2
|
||||
};
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
memcpy(out_raw_cid, &cmd.response, sizeof(sdmmc_response_t));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_send_cmd_send_cid(sdmmc_card_t *card, sdmmc_cid_t *out_cid)
|
||||
{
|
||||
assert(out_cid);
|
||||
assert(host_is_spi(card) && "SEND_CID should only be used in SPI mode");
|
||||
assert(!card->is_mmc && "MMC cards are not supported in SPI mode");
|
||||
sdmmc_response_t buf;
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = MMC_SEND_CID,
|
||||
.flags = SCF_CMD_READ | SCF_CMD_ADTC,
|
||||
.arg = 0,
|
||||
.data = &buf[0],
|
||||
.datalen = sizeof(buf)
|
||||
};
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
sdmmc_flip_byte_order(buf, sizeof(buf));
|
||||
return sdmmc_decode_cid(buf, out_cid);
|
||||
}
|
||||
|
||||
|
||||
esp_err_t sdmmc_send_cmd_set_relative_addr(sdmmc_card_t* card, uint16_t* out_rca)
|
||||
{
|
||||
assert(out_rca);
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = SD_SEND_RELATIVE_ADDR,
|
||||
.flags = SCF_CMD_BCR | SCF_RSP_R6
|
||||
};
|
||||
|
||||
/* MMC cards expect us to set the RCA.
|
||||
* Set RCA to 1 since we don't support multiple cards on the same bus, for now.
|
||||
*/
|
||||
uint16_t mmc_rca = 1;
|
||||
if (card->is_mmc) {
|
||||
cmd.arg = MMC_ARG_RCA(mmc_rca);
|
||||
}
|
||||
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
*out_rca = (card->is_mmc) ? mmc_rca : SD_R6_RCA(cmd.response);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_send_cmd_set_blocklen(sdmmc_card_t* card, sdmmc_csd_t* csd)
|
||||
{
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = MMC_SET_BLOCKLEN,
|
||||
.arg = csd->sector_size,
|
||||
.flags = SCF_CMD_AC | SCF_RSP_R1
|
||||
};
|
||||
return sdmmc_send_cmd(card, &cmd);
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_send_cmd_send_csd(sdmmc_card_t* card, sdmmc_csd_t* out_csd)
|
||||
{
|
||||
/* The trick with SEND_CSD is that in SPI mode, it acts as a data read
|
||||
* command, while in SD mode it is an AC command with R2 response.
|
||||
*/
|
||||
sdmmc_response_t spi_buf;
|
||||
const bool is_spi = host_is_spi(card);
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = MMC_SEND_CSD,
|
||||
.arg = is_spi ? 0 : MMC_ARG_RCA(card->rca),
|
||||
.flags = is_spi ? (SCF_CMD_READ | SCF_CMD_ADTC | SCF_RSP_R1) :
|
||||
(SCF_CMD_AC | SCF_RSP_R2),
|
||||
.data = is_spi ? &spi_buf[0] : 0,
|
||||
.datalen = is_spi ? sizeof(spi_buf) : 0,
|
||||
};
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
uint32_t* ptr = cmd.response;
|
||||
if (is_spi) {
|
||||
sdmmc_flip_byte_order(spi_buf, sizeof(spi_buf));
|
||||
ptr = spi_buf;
|
||||
}
|
||||
if (card->is_mmc) {
|
||||
err = sdmmc_mmc_decode_csd(cmd.response, out_csd);
|
||||
} else {
|
||||
err = sdmmc_decode_csd(ptr, out_csd);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_send_cmd_select_card(sdmmc_card_t* card, uint32_t rca)
|
||||
{
|
||||
/* Don't expect to see a response when de-selecting a card */
|
||||
uint32_t response = (rca == 0) ? 0 : SCF_RSP_R1;
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = MMC_SELECT_CARD,
|
||||
.arg = MMC_ARG_RCA(rca),
|
||||
.flags = SCF_CMD_AC | response
|
||||
};
|
||||
return sdmmc_send_cmd(card, &cmd);
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_send_cmd_send_scr(sdmmc_card_t* card, sdmmc_scr_t *out_scr)
|
||||
{
|
||||
size_t datalen = 8;
|
||||
uint32_t* buf = (uint32_t*) heap_caps_malloc(datalen, MALLOC_CAP_DMA);
|
||||
if (buf == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
sdmmc_command_t cmd = {
|
||||
.data = buf,
|
||||
.datalen = datalen,
|
||||
.blklen = datalen,
|
||||
.flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1,
|
||||
.opcode = SD_APP_SEND_SCR
|
||||
};
|
||||
esp_err_t err = sdmmc_send_app_cmd(card, &cmd);
|
||||
if (err == ESP_OK) {
|
||||
err = sdmmc_decode_scr(buf, out_scr);
|
||||
}
|
||||
free(buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_send_cmd_set_bus_width(sdmmc_card_t* card, int width)
|
||||
{
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = SD_APP_SET_BUS_WIDTH,
|
||||
.flags = SCF_RSP_R1 | SCF_CMD_AC,
|
||||
.arg = (width == 4) ? SD_ARG_BUS_WIDTH_4 : SD_ARG_BUS_WIDTH_1,
|
||||
};
|
||||
|
||||
return sdmmc_send_app_cmd(card, &cmd);
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_send_cmd_crc_on_off(sdmmc_card_t* card, bool crc_enable)
|
||||
{
|
||||
assert(host_is_spi(card) && "CRC_ON_OFF can only be used in SPI mode");
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = SD_CRC_ON_OFF,
|
||||
.arg = crc_enable ? 1 : 0,
|
||||
.flags = SCF_CMD_AC | SCF_RSP_R1
|
||||
};
|
||||
return sdmmc_send_cmd(card, &cmd);
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_send_cmd_send_status(sdmmc_card_t* card, uint32_t* out_status)
|
||||
{
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = MMC_SEND_STATUS,
|
||||
.arg = MMC_ARG_RCA(card->rca),
|
||||
.flags = SCF_CMD_AC | SCF_RSP_R1
|
||||
};
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
if (out_status) {
|
||||
if (host_is_spi(card)) {
|
||||
*out_status = SD_SPI_R2(cmd.response);
|
||||
} else {
|
||||
*out_status = MMC_R1(cmd.response);
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_write_sectors(sdmmc_card_t* card, const void* src,
|
||||
size_t start_block, size_t block_count)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
size_t block_size = card->csd.sector_size;
|
||||
if (esp_ptr_dma_capable(src) && (intptr_t)src % 4 == 0) {
|
||||
err = sdmmc_write_sectors_dma(card, src, start_block, block_count);
|
||||
} else {
|
||||
// SDMMC peripheral needs DMA-capable buffers. Split the write into
|
||||
// separate single block writes, if needed, and allocate a temporary
|
||||
// DMA-capable buffer.
|
||||
void* tmp_buf = heap_caps_malloc(block_size, MALLOC_CAP_DMA);
|
||||
if (tmp_buf == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
const uint8_t* cur_src = (const uint8_t*) src;
|
||||
for (size_t i = 0; i < block_count; ++i) {
|
||||
memcpy(tmp_buf, cur_src, block_size);
|
||||
cur_src += block_size;
|
||||
err = sdmmc_write_sectors_dma(card, tmp_buf, start_block + i, 1);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGD(TAG, "%s: error 0x%x writing block %d+%d",
|
||||
__func__, err, start_block, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(tmp_buf);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_write_sectors_dma(sdmmc_card_t* card, const void* src,
|
||||
size_t start_block, size_t block_count)
|
||||
{
|
||||
if (start_block + block_count > card->csd.capacity) {
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
size_t block_size = card->csd.sector_size;
|
||||
sdmmc_command_t cmd = {
|
||||
.flags = SCF_CMD_ADTC | SCF_RSP_R1,
|
||||
.blklen = block_size,
|
||||
.data = (void*) src,
|
||||
.datalen = block_count * block_size,
|
||||
.timeout_ms = SDMMC_WRITE_CMD_TIMEOUT_MS
|
||||
};
|
||||
if (block_count == 1) {
|
||||
cmd.opcode = MMC_WRITE_BLOCK_SINGLE;
|
||||
} else {
|
||||
cmd.opcode = MMC_WRITE_BLOCK_MULTIPLE;
|
||||
}
|
||||
if (card->ocr & SD_OCR_SDHC_CAP) {
|
||||
cmd.arg = start_block;
|
||||
} else {
|
||||
cmd.arg = start_block * block_size;
|
||||
}
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
uint32_t status = 0;
|
||||
size_t count = 0;
|
||||
/* SD mode: wait for the card to become idle based on R1 status */
|
||||
while (!host_is_spi(card) && !(status & MMC_R1_READY_FOR_DATA)) {
|
||||
// TODO: add some timeout here
|
||||
err = sdmmc_send_cmd_send_status(card, &status);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
if (++count % 10 == 0) {
|
||||
ESP_LOGV(TAG, "waiting for card to become ready (%d)", count);
|
||||
}
|
||||
}
|
||||
/* SPI mode: although card busy indication is based on the busy token,
|
||||
* SD spec recommends that the host checks the results of programming by sending
|
||||
* SEND_STATUS command. Some of the conditions reported in SEND_STATUS are not
|
||||
* reported via a data error token.
|
||||
*/
|
||||
if (host_is_spi(card)) {
|
||||
err = sdmmc_send_cmd_send_status(card, &status);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
if (status & SD_SPI_R2_CARD_LOCKED) {
|
||||
ESP_LOGE(TAG, "%s: write failed, card is locked: r2=0x%04x",
|
||||
__func__, status);
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (status != 0) {
|
||||
ESP_LOGE(TAG, "%s: card status indicates an error after write operation: r2=0x%04x",
|
||||
__func__, status);
|
||||
return ESP_ERR_INVALID_RESPONSE;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_read_sectors(sdmmc_card_t* card, void* dst,
|
||||
size_t start_block, size_t block_count)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
size_t block_size = card->csd.sector_size;
|
||||
if (esp_ptr_dma_capable(dst) && (intptr_t)dst % 4 == 0) {
|
||||
err = sdmmc_read_sectors_dma(card, dst, start_block, block_count);
|
||||
} else {
|
||||
// SDMMC peripheral needs DMA-capable buffers. Split the read into
|
||||
// separate single block reads, if needed, and allocate a temporary
|
||||
// DMA-capable buffer.
|
||||
void* tmp_buf = heap_caps_malloc(block_size, MALLOC_CAP_DMA);
|
||||
if (tmp_buf == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
uint8_t* cur_dst = (uint8_t*) dst;
|
||||
for (size_t i = 0; i < block_count; ++i) {
|
||||
err = sdmmc_read_sectors_dma(card, tmp_buf, start_block + i, 1);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGD(TAG, "%s: error 0x%x writing block %d+%d",
|
||||
__func__, err, start_block, i);
|
||||
break;
|
||||
}
|
||||
memcpy(cur_dst, tmp_buf, block_size);
|
||||
cur_dst += block_size;
|
||||
}
|
||||
free(tmp_buf);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst,
|
||||
size_t start_block, size_t block_count)
|
||||
{
|
||||
if (start_block + block_count > card->csd.capacity) {
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
size_t block_size = card->csd.sector_size;
|
||||
sdmmc_command_t cmd = {
|
||||
.flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1,
|
||||
.blklen = block_size,
|
||||
.data = (void*) dst,
|
||||
.datalen = block_count * block_size
|
||||
};
|
||||
if (block_count == 1) {
|
||||
cmd.opcode = MMC_READ_BLOCK_SINGLE;
|
||||
} else {
|
||||
cmd.opcode = MMC_READ_BLOCK_MULTIPLE;
|
||||
}
|
||||
if (card->ocr & SD_OCR_SDHC_CAP) {
|
||||
cmd.arg = start_block;
|
||||
} else {
|
||||
cmd.arg = start_block * block_size;
|
||||
}
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
uint32_t status = 0;
|
||||
size_t count = 0;
|
||||
while (!host_is_spi(card) && !(status & MMC_R1_READY_FOR_DATA)) {
|
||||
// TODO: add some timeout here
|
||||
err = sdmmc_send_cmd_send_status(card, &status);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
if (++count % 10 == 0) {
|
||||
ESP_LOGV(TAG, "waiting for card to become ready (%d)", count);
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_erase_sectors(sdmmc_card_t* card, size_t start_sector,
|
||||
size_t sector_count, sdmmc_erase_arg_t arg)
|
||||
{
|
||||
if (start_sector + sector_count > card->csd.capacity) {
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
uint32_t cmd38_arg;
|
||||
if (arg == SDMMC_ERASE_ARG) {
|
||||
cmd38_arg = card->is_mmc ? SDMMC_MMC_TRIM_ARG : SDMMC_SD_ERASE_ARG;
|
||||
} else {
|
||||
cmd38_arg = card->is_mmc ? SDMMC_MMC_DISCARD_ARG : SDMMC_SD_DISCARD_ARG;
|
||||
}
|
||||
|
||||
/* validate the CMD38 argument against card supported features */
|
||||
if (card->is_mmc) {
|
||||
if ((cmd38_arg == SDMMC_MMC_TRIM_ARG) && (sdmmc_can_trim(card) != ESP_OK)) {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
if ((cmd38_arg == SDMMC_MMC_DISCARD_ARG) && (sdmmc_can_discard(card) != ESP_OK)) {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
} else { // SD card
|
||||
if ((cmd38_arg == SDMMC_SD_DISCARD_ARG) && (sdmmc_can_discard(card) != ESP_OK)) {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
/* default as block unit address */
|
||||
size_t addr_unit_mult = 1;
|
||||
|
||||
if (!(card->ocr & SD_OCR_SDHC_CAP)) {
|
||||
addr_unit_mult = card->csd.sector_size;
|
||||
}
|
||||
|
||||
/* prepare command to set the start address */
|
||||
sdmmc_command_t cmd = {
|
||||
.flags = SCF_CMD_AC | SCF_RSP_R1 | SCF_WAIT_BUSY,
|
||||
.opcode = card->is_mmc ? MMC_ERASE_GROUP_START :
|
||||
SD_ERASE_GROUP_START,
|
||||
.arg = (start_sector * addr_unit_mult),
|
||||
};
|
||||
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: sdmmc_send_cmd (ERASE_GROUP_START) returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* prepare command to set the end address */
|
||||
cmd.opcode = card->is_mmc ? MMC_ERASE_GROUP_END : SD_ERASE_GROUP_END;
|
||||
cmd.arg = ((start_sector + (sector_count - 1)) * addr_unit_mult);
|
||||
|
||||
err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: sdmmc_send_cmd (ERASE_GROUP_END) returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* issue erase command */
|
||||
memset((void *)&cmd, 0 , sizeof(sdmmc_command_t));
|
||||
cmd.flags = SCF_CMD_AC | SCF_RSP_R1B | SCF_WAIT_BUSY;
|
||||
cmd.opcode = MMC_ERASE;
|
||||
cmd.arg = cmd38_arg;
|
||||
cmd.timeout_ms = sdmmc_get_erase_timeout_ms(card, cmd38_arg, sector_count * card->csd.sector_size / 1024);
|
||||
|
||||
err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: sdmmc_send_cmd (ERASE) returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (host_is_spi(card)) {
|
||||
uint32_t status;
|
||||
err = sdmmc_send_cmd_send_status(card, &status);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
if (status != 0) {
|
||||
ESP_LOGE(TAG, "%s: card status indicates an error after erase operation: r2=0x%04x",
|
||||
__func__, status);
|
||||
return ESP_ERR_INVALID_RESPONSE;
|
||||
}
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_can_discard(sdmmc_card_t* card)
|
||||
{
|
||||
if ((card->is_mmc) && (card->ext_csd.rev >= EXT_CSD_REV_1_6)) {
|
||||
return ESP_OK;
|
||||
}
|
||||
// SD card
|
||||
if ((!card->is_mmc) && !host_is_spi(card) && (card->ssr.discard_support == 1)) {
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_can_trim(sdmmc_card_t* card)
|
||||
{
|
||||
if ((card->is_mmc) && (card->ext_csd.sec_feature & EXT_CSD_SEC_GB_CL_EN)) {
|
||||
FF_CAN_TRIM = 1;
|
||||
return ESP_OK;
|
||||
}
|
||||
FF_CAN_TRIM = 0;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_mmc_can_sanitize(sdmmc_card_t* card)
|
||||
{
|
||||
if ((card->is_mmc) && (card->ext_csd.sec_feature & EXT_CSD_SEC_SANITIZE)) {
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_mmc_sanitize(sdmmc_card_t* card, uint32_t timeout_ms)
|
||||
{
|
||||
esp_err_t err;
|
||||
uint8_t index = EXT_CSD_SANITIZE_START;
|
||||
uint8_t set = EXT_CSD_CMD_SET_NORMAL;
|
||||
uint8_t value = 0x01;
|
||||
|
||||
if (sdmmc_mmc_can_sanitize(card) != ESP_OK) {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
/*
|
||||
* A Sanitize operation is initiated by writing a value to the extended
|
||||
* CSD[165] SANITIZE_START. While the device is performing the sanitize
|
||||
* operation, the busy line is asserted.
|
||||
* SWITCH command is used to write the EXT_CSD register.
|
||||
*/
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = MMC_SWITCH,
|
||||
.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (index << 16) | (value << 8) | set,
|
||||
.flags = SCF_RSP_R1B | SCF_CMD_AC | SCF_WAIT_BUSY,
|
||||
.timeout_ms = timeout_ms,
|
||||
};
|
||||
err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err == ESP_OK) {
|
||||
//check response bit to see that switch was accepted
|
||||
if (MMC_R1(cmd.response) & MMC_R1_SWITCH_ERROR) {
|
||||
err = ESP_ERR_INVALID_RESPONSE;
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_full_erase(sdmmc_card_t* card)
|
||||
{
|
||||
sdmmc_erase_arg_t arg = SDMMC_SD_ERASE_ARG; // erase by default for SD card
|
||||
esp_err_t err;
|
||||
if (card->is_mmc) {
|
||||
arg = sdmmc_mmc_can_sanitize(card) == ESP_OK ? SDMMC_MMC_DISCARD_ARG: SDMMC_MMC_TRIM_ARG;
|
||||
}
|
||||
err = sdmmc_erase_sectors(card, 0, card->csd.capacity, arg);
|
||||
if ((err == ESP_OK) && (arg == SDMMC_MMC_DISCARD_ARG)) {
|
||||
uint32_t timeout_ms = sdmmc_get_erase_timeout_ms(card, SDMMC_MMC_DISCARD_ARG, card->csd.capacity * ((uint64_t) card->csd.sector_size) / 1024);
|
||||
return sdmmc_mmc_sanitize(card, timeout_ms);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_get_status(sdmmc_card_t* card)
|
||||
{
|
||||
uint32_t stat;
|
||||
return sdmmc_send_cmd_send_status(card, &stat);
|
||||
}
|
||||
332
code/components/esp-sdmmc/sdmmc_common_mh.c
Normal file
332
code/components/esp-sdmmc/sdmmc_common_mh.c
Normal file
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
|
||||
* Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "sdmmc_common_mh.h"
|
||||
|
||||
bool card_is_mmc = 0;
|
||||
|
||||
static const char* TAG = "sdmmc_common";
|
||||
|
||||
esp_err_t sdmmc_init_ocr(sdmmc_card_t* card)
|
||||
{
|
||||
esp_err_t err;
|
||||
/* In SPI mode, READ_OCR (CMD58) command is used to figure out which voltage
|
||||
* ranges the card can support. This step is skipped since 1.8V isn't
|
||||
* supported on the ESP32.
|
||||
*/
|
||||
|
||||
uint32_t host_ocr = get_host_ocr(card->host.io_voltage);
|
||||
if ((card->ocr & SD_OCR_SDHC_CAP) != 0) {
|
||||
host_ocr |= SD_OCR_SDHC_CAP;
|
||||
}
|
||||
/* Send SEND_OP_COND (ACMD41) command to the card until it becomes ready. */
|
||||
err = sdmmc_send_cmd_send_op_cond(card, host_ocr, &card->ocr);
|
||||
|
||||
/* If time-out, re-try send_op_cond as MMC */
|
||||
if (err == ESP_ERR_TIMEOUT && !host_is_spi(card)) {
|
||||
ESP_LOGD(TAG, "send_op_cond timeout, trying MMC");
|
||||
card->is_mmc = 1;
|
||||
card_is_mmc = 1;
|
||||
err = sdmmc_send_cmd_send_op_cond(card, host_ocr, &card->ocr);
|
||||
}
|
||||
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: send_op_cond (1) returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
if (host_is_spi(card)) {
|
||||
err = sdmmc_send_cmd_read_ocr(card, &card->ocr);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: read_ocr returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
ESP_LOGD(TAG, "host_ocr=0x%x card_ocr=0x%x", host_ocr, card->ocr);
|
||||
|
||||
/* Clear all voltage bits in host's OCR which the card doesn't support.
|
||||
* Don't touch CCS bit because in SPI mode cards don't report CCS in ACMD41
|
||||
* response.
|
||||
*/
|
||||
host_ocr &= (card->ocr | (~SD_OCR_VOL_MASK));
|
||||
ESP_LOGD(TAG, "sdmmc_card_init: host_ocr=%08x, card_ocr=%08x", host_ocr, card->ocr);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_init_cid(sdmmc_card_t* card)
|
||||
{
|
||||
esp_err_t err;
|
||||
sdmmc_response_t raw_cid;
|
||||
if (!host_is_spi(card)) {
|
||||
err = sdmmc_send_cmd_all_send_cid(card, &raw_cid);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: all_send_cid returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
if (!card->is_mmc) {
|
||||
err = sdmmc_decode_cid(raw_cid, &card->cid);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: decoding CID failed (0x%x)", __func__, err);
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
/* For MMC, need to know CSD to decode CID. But CSD can only be read
|
||||
* in data transfer mode, and it is not possible to read CID in data
|
||||
* transfer mode. We temporiliy store the raw cid and do the
|
||||
* decoding after the RCA is set and the card is in data transfer
|
||||
* mode.
|
||||
*/
|
||||
memcpy(card->raw_cid, raw_cid, sizeof(sdmmc_response_t));
|
||||
}
|
||||
} else {
|
||||
err = sdmmc_send_cmd_send_cid(card, &card->cid);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: send_cid returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_init_rca(sdmmc_card_t* card)
|
||||
{
|
||||
esp_err_t err;
|
||||
err = sdmmc_send_cmd_set_relative_addr(card, &card->rca);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: set_relative_addr returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_init_mmc_decode_cid(sdmmc_card_t* card)
|
||||
{
|
||||
esp_err_t err;
|
||||
sdmmc_response_t raw_cid;
|
||||
memcpy(raw_cid, card->raw_cid, sizeof(raw_cid));
|
||||
err = sdmmc_mmc_decode_cid(card->csd.mmc_ver, raw_cid, &card->cid);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: decoding CID failed (0x%x)", __func__, err);
|
||||
return err;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_init_csd(sdmmc_card_t* card)
|
||||
{
|
||||
assert(card->is_mem == 1);
|
||||
/* Get and decode the contents of CSD register. Determine card capacity. */
|
||||
esp_err_t err = sdmmc_send_cmd_send_csd(card, &card->csd);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: send_csd returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
const size_t max_sdsc_capacity = UINT32_MAX / card->csd.sector_size + 1;
|
||||
if (!(card->ocr & SD_OCR_SDHC_CAP) &&
|
||||
card->csd.capacity > max_sdsc_capacity) {
|
||||
ESP_LOGW(TAG, "%s: SDSC card reports capacity=%u. Limiting to %u.",
|
||||
__func__, card->csd.capacity, max_sdsc_capacity);
|
||||
card->csd.capacity = max_sdsc_capacity;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_init_select_card(sdmmc_card_t* card)
|
||||
{
|
||||
assert(!host_is_spi(card));
|
||||
esp_err_t err = sdmmc_send_cmd_select_card(card, card->rca);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: select_card returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_init_card_hs_mode(sdmmc_card_t* card)
|
||||
{
|
||||
esp_err_t err = ESP_ERR_NOT_SUPPORTED;
|
||||
if (card->is_mem && !card->is_mmc) {
|
||||
err = sdmmc_enable_hs_mode_and_check(card);
|
||||
} else if (card->is_sdio) {
|
||||
err = sdmmc_io_enable_hs_mode(card);
|
||||
} else if (card->is_mmc){
|
||||
err = sdmmc_mmc_enable_hs_mode(card);
|
||||
}
|
||||
if (err == ESP_ERR_NOT_SUPPORTED) {
|
||||
ESP_LOGD(TAG, "%s: host supports HS mode, but card doesn't", __func__);
|
||||
card->max_freq_khz = SDMMC_FREQ_DEFAULT;
|
||||
} else if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_init_host_bus_width(sdmmc_card_t* card)
|
||||
{
|
||||
int bus_width = 1;
|
||||
|
||||
if ((card->host.flags & SDMMC_HOST_FLAG_4BIT) &&
|
||||
(card->log_bus_width == 2)) {
|
||||
bus_width = 4;
|
||||
} else if ((card->host.flags & SDMMC_HOST_FLAG_8BIT) &&
|
||||
(card->log_bus_width == 3)) {
|
||||
bus_width = 8;
|
||||
}
|
||||
ESP_LOGD(TAG, "%s: using %d-bit bus", __func__, bus_width);
|
||||
if (bus_width > 1) {
|
||||
esp_err_t err = (*card->host.set_bus_width)(card->host.slot, bus_width);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "host.set_bus_width failed (0x%x)", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_init_host_frequency(sdmmc_card_t* card)
|
||||
{
|
||||
assert(card->max_freq_khz <= card->host.max_freq_khz);
|
||||
|
||||
/* Find highest frequency in the following list,
|
||||
* which is below card->max_freq_khz.
|
||||
*/
|
||||
const uint32_t freq_values[] = {
|
||||
SDMMC_FREQ_52M,
|
||||
SDMMC_FREQ_HIGHSPEED,
|
||||
SDMMC_FREQ_26M,
|
||||
SDMMC_FREQ_DEFAULT
|
||||
//NOTE: in sdspi mode, 20MHz may not work. in that case, add 10MHz here.
|
||||
};
|
||||
const int n_freq_values = sizeof(freq_values) / sizeof(freq_values[0]);
|
||||
|
||||
uint32_t selected_freq = SDMMC_FREQ_PROBING;
|
||||
for (int i = 0; i < n_freq_values; ++i) {
|
||||
uint32_t freq = freq_values[i];
|
||||
if (card->max_freq_khz >= freq) {
|
||||
selected_freq = freq;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "%s: using %d kHz bus frequency", __func__, selected_freq);
|
||||
if (selected_freq > SDMMC_FREQ_PROBING) {
|
||||
esp_err_t err = (*card->host.set_card_clk)(card->host.slot, selected_freq);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "failed to switch bus frequency (0x%x)", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (card->is_ddr) {
|
||||
if (card->host.set_bus_ddr_mode == NULL) {
|
||||
ESP_LOGE(TAG, "host doesn't support DDR mode or voltage switching");
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
esp_err_t err = (*card->host.set_bus_ddr_mode)(card->host.slot, true);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "failed to switch bus to DDR mode (0x%x)", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void sdmmc_flip_byte_order(uint32_t* response, size_t size)
|
||||
{
|
||||
assert(size % (2 * sizeof(uint32_t)) == 0);
|
||||
const size_t n_words = size / sizeof(uint32_t);
|
||||
for (int i = 0; i < n_words / 2; ++i) {
|
||||
uint32_t left = __builtin_bswap32(response[i]);
|
||||
uint32_t right = __builtin_bswap32(response[n_words - i - 1]);
|
||||
response[i] = right;
|
||||
response[n_words - i - 1] = left;
|
||||
}
|
||||
}
|
||||
|
||||
void sdmmc_card_print_info(FILE* stream, const sdmmc_card_t* card)
|
||||
{
|
||||
bool print_scr = false;
|
||||
bool print_csd = false;
|
||||
const char* type;
|
||||
fprintf(stream, "Name: %s\n", card->cid.name);
|
||||
if (card->is_sdio) {
|
||||
type = "SDIO";
|
||||
print_scr = true;
|
||||
print_csd = true;
|
||||
} else if (card->is_mmc) {
|
||||
type = "MMC";
|
||||
print_csd = true;
|
||||
} else {
|
||||
type = (card->ocr & SD_OCR_SDHC_CAP) ? "SDHC/SDXC" : "SDSC";
|
||||
print_csd = true;
|
||||
}
|
||||
fprintf(stream, "Type: %s\n", type);
|
||||
if (card->max_freq_khz < 1000) {
|
||||
fprintf(stream, "Speed: %d kHz\n", card->max_freq_khz);
|
||||
} else {
|
||||
fprintf(stream, "Speed: %d MHz%s\n", card->max_freq_khz / 1000,
|
||||
card->is_ddr ? ", DDR" : "");
|
||||
}
|
||||
fprintf(stream, "Size: %lluMB\n", ((uint64_t) card->csd.capacity) * card->csd.sector_size / (1024 * 1024));
|
||||
|
||||
if (print_csd) {
|
||||
fprintf(stream, "CSD: ver=%d, sector_size=%d, capacity=%d read_bl_len=%d\n",
|
||||
(card->is_mmc ? card->csd.csd_ver : card->csd.csd_ver + 1),
|
||||
card->csd.sector_size, card->csd.capacity, card->csd.read_block_len);
|
||||
if (card->is_mmc) {
|
||||
fprintf(stream, "EXT CSD: bus_width=%d\n", (1 << card->log_bus_width));
|
||||
} else if (!card->is_sdio){ // make sure card is SD
|
||||
fprintf(stream, "SSR: bus_width=%d\n", (card->ssr.cur_bus_width ? 4 : 1));
|
||||
}
|
||||
}
|
||||
if (print_scr) {
|
||||
fprintf(stream, "SCR: sd_spec=%d, bus_width=%d\n", card->scr.sd_spec, card->scr.bus_width);
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_fix_host_flags(sdmmc_card_t* card)
|
||||
{
|
||||
const uint32_t width_1bit = SDMMC_HOST_FLAG_1BIT;
|
||||
const uint32_t width_4bit = SDMMC_HOST_FLAG_4BIT;
|
||||
const uint32_t width_8bit = SDMMC_HOST_FLAG_8BIT;
|
||||
const uint32_t width_mask = width_1bit | width_4bit | width_8bit;
|
||||
|
||||
int slot_bit_width = card->host.get_bus_width(card->host.slot);
|
||||
if (slot_bit_width == 1 &&
|
||||
(card->host.flags & (width_4bit | width_8bit))) {
|
||||
card->host.flags &= ~width_mask;
|
||||
card->host.flags |= width_1bit;
|
||||
} else if (slot_bit_width == 4 && (card->host.flags & width_8bit)) {
|
||||
if ((card->host.flags & width_4bit) == 0) {
|
||||
ESP_LOGW(TAG, "slot width set to 4, but host flags don't have 4 line mode enabled; using 1 line mode");
|
||||
card->host.flags &= ~width_mask;
|
||||
card->host.flags |= width_1bit;
|
||||
} else {
|
||||
card->host.flags &= ~width_mask;
|
||||
card->host.flags |= width_4bit;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
uint32_t sdmmc_get_erase_timeout_ms(const sdmmc_card_t* card, int arg, size_t erase_size_kb)
|
||||
{
|
||||
if (card->is_mmc) {
|
||||
return sdmmc_mmc_get_erase_timeout_ms(card, arg, erase_size_kb);
|
||||
} else {
|
||||
return sdmmc_sd_get_erase_timeout_ms(card, arg, erase_size_kb);
|
||||
}
|
||||
}
|
||||
154
code/components/esp-sdmmc/sdmmc_common_mh.h
Normal file
154
code/components/esp-sdmmc/sdmmc_common_mh.h
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
|
||||
* Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/sdmmc_defs.h"
|
||||
#include "driver/sdmmc_types.h"
|
||||
#include "sdmmc_cmd_mh.h"
|
||||
#include "sys/param.h"
|
||||
#include "soc/soc_memory_layout.h"
|
||||
|
||||
extern bool card_is_mmc;
|
||||
|
||||
#define SDMMC_GO_IDLE_DELAY_MS 20
|
||||
#define SDMMC_IO_SEND_OP_COND_DELAY_MS 10
|
||||
|
||||
/* These delay values are mostly useful for cases when CD pin is not used, and
|
||||
* the card is removed. In this case, SDMMC peripheral may not always return
|
||||
* CMD_DONE / DATA_DONE interrupts after signaling the error. These timeouts work
|
||||
* as a safety net in such cases.
|
||||
*/
|
||||
#define SDMMC_DEFAULT_CMD_TIMEOUT_MS 1000 // Max timeout of ordinary commands
|
||||
#define SDMMC_WRITE_CMD_TIMEOUT_MS 5000 // Max timeout of write commands
|
||||
|
||||
|
||||
#define SDMMC_SD_DISCARD_TIMEOUT 250 // SD erase (discard) timeout
|
||||
|
||||
/* Maximum retry/error count for SEND_OP_COND (CMD1).
|
||||
* These are somewhat arbitrary, values originate from OpenBSD driver.
|
||||
*/
|
||||
#define SDMMC_SEND_OP_COND_MAX_RETRIES 100
|
||||
#define SDMMC_SEND_OP_COND_MAX_ERRORS 3
|
||||
|
||||
/* supported arguments for erase command 38 */
|
||||
#define SDMMC_SD_ERASE_ARG 0
|
||||
#define SDMMC_SD_DISCARD_ARG 1
|
||||
#define SDMMC_MMC_TRIM_ARG 1
|
||||
#define SDMMC_MMC_DISCARD_ARG 3
|
||||
|
||||
/* Functions to send individual commands */
|
||||
esp_err_t sdmmc_send_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd);
|
||||
esp_err_t sdmmc_send_app_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd);
|
||||
esp_err_t sdmmc_send_cmd_go_idle_state(sdmmc_card_t* card);
|
||||
esp_err_t sdmmc_send_cmd_send_if_cond(sdmmc_card_t* card, uint32_t ocr);
|
||||
esp_err_t sdmmc_send_cmd_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp);
|
||||
esp_err_t sdmmc_send_cmd_read_ocr(sdmmc_card_t *card, uint32_t *ocrp);
|
||||
esp_err_t sdmmc_send_cmd_send_cid(sdmmc_card_t *card, sdmmc_cid_t *out_cid);
|
||||
esp_err_t sdmmc_send_cmd_all_send_cid(sdmmc_card_t* card, sdmmc_response_t* out_raw_cid);
|
||||
esp_err_t sdmmc_send_cmd_set_relative_addr(sdmmc_card_t* card, uint16_t* out_rca);
|
||||
esp_err_t sdmmc_send_cmd_set_blocklen(sdmmc_card_t* card, sdmmc_csd_t* csd);
|
||||
esp_err_t sdmmc_send_cmd_switch_func(sdmmc_card_t* card,
|
||||
uint32_t mode, uint32_t group, uint32_t function,
|
||||
sdmmc_switch_func_rsp_t* resp);
|
||||
esp_err_t sdmmc_send_cmd_send_csd(sdmmc_card_t* card, sdmmc_csd_t* out_csd);
|
||||
esp_err_t sdmmc_send_cmd_select_card(sdmmc_card_t* card, uint32_t rca);
|
||||
esp_err_t sdmmc_send_cmd_send_scr(sdmmc_card_t* card, sdmmc_scr_t *out_scr);
|
||||
esp_err_t sdmmc_send_cmd_set_bus_width(sdmmc_card_t* card, int width);
|
||||
esp_err_t sdmmc_send_cmd_send_status(sdmmc_card_t* card, uint32_t* out_status);
|
||||
esp_err_t sdmmc_send_cmd_crc_on_off(sdmmc_card_t* card, bool crc_enable);
|
||||
|
||||
/* Higher level functions */
|
||||
esp_err_t sdmmc_enable_hs_mode(sdmmc_card_t* card);
|
||||
esp_err_t sdmmc_enable_hs_mode_and_check(sdmmc_card_t* card);
|
||||
esp_err_t sdmmc_write_sectors_dma(sdmmc_card_t* card, const void* src,
|
||||
size_t start_block, size_t block_count);
|
||||
esp_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst,
|
||||
size_t start_block, size_t block_count);
|
||||
uint32_t sdmmc_get_erase_timeout_ms(const sdmmc_card_t* card, int arg, size_t erase_size_kb);
|
||||
|
||||
/* SD specific */
|
||||
esp_err_t sdmmc_check_scr(sdmmc_card_t* card);
|
||||
esp_err_t sdmmc_decode_cid(sdmmc_response_t resp, sdmmc_cid_t* out_cid);
|
||||
esp_err_t sdmmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd);
|
||||
esp_err_t sdmmc_decode_scr(uint32_t *raw_scr, sdmmc_scr_t* out_scr);
|
||||
esp_err_t sdmmc_decode_ssr(uint32_t *raw_ssr, sdmmc_ssr_t* out_ssr);
|
||||
uint32_t sdmmc_sd_get_erase_timeout_ms(const sdmmc_card_t* card, int arg, size_t erase_size_kb);
|
||||
|
||||
/* SDIO specific */
|
||||
esp_err_t sdmmc_io_reset(sdmmc_card_t* card);
|
||||
esp_err_t sdmmc_io_enable_hs_mode(sdmmc_card_t* card);
|
||||
esp_err_t sdmmc_io_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp);
|
||||
esp_err_t sdmmc_io_rw_direct(sdmmc_card_t* card, int function,
|
||||
uint32_t reg, uint32_t arg, uint8_t *byte);
|
||||
esp_err_t sdmmc_io_rw_extended(sdmmc_card_t* card, int function,
|
||||
uint32_t reg, int arg, void *data, size_t size);
|
||||
|
||||
|
||||
/* MMC specific */
|
||||
esp_err_t sdmmc_mmc_send_ext_csd_data(sdmmc_card_t* card, void *out_data, size_t datalen);
|
||||
esp_err_t sdmmc_mmc_switch(sdmmc_card_t* card, uint8_t set, uint8_t index, uint8_t value);
|
||||
esp_err_t sdmmc_mmc_decode_cid(int mmc_ver, sdmmc_response_t resp, sdmmc_cid_t* out_cid);
|
||||
esp_err_t sdmmc_mmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd);
|
||||
esp_err_t sdmmc_mmc_enable_hs_mode(sdmmc_card_t* card);
|
||||
uint32_t sdmmc_mmc_get_erase_timeout_ms(const sdmmc_card_t* card, int arg, size_t erase_size_kb);
|
||||
|
||||
/* Parts of card initialization flow */
|
||||
esp_err_t sdmmc_init_sd_if_cond(sdmmc_card_t* card);
|
||||
esp_err_t sdmmc_init_select_card(sdmmc_card_t* card);
|
||||
esp_err_t sdmmc_init_csd(sdmmc_card_t* card);
|
||||
esp_err_t sdmmc_init_cid(sdmmc_card_t* card);
|
||||
esp_err_t sdmmc_init_rca(sdmmc_card_t* card);
|
||||
esp_err_t sdmmc_init_mmc_decode_cid(sdmmc_card_t* card);
|
||||
esp_err_t sdmmc_init_ocr(sdmmc_card_t* card);
|
||||
esp_err_t sdmmc_init_spi_crc(sdmmc_card_t* card);
|
||||
esp_err_t sdmmc_init_io(sdmmc_card_t* card);
|
||||
esp_err_t sdmmc_init_sd_blocklen(sdmmc_card_t* card);
|
||||
esp_err_t sdmmc_init_sd_scr(sdmmc_card_t* card);
|
||||
esp_err_t sdmmc_init_sd_ssr(sdmmc_card_t* card);
|
||||
esp_err_t sdmmc_init_sd_wait_data_ready(sdmmc_card_t* card);
|
||||
esp_err_t sdmmc_init_mmc_read_ext_csd(sdmmc_card_t* card);
|
||||
esp_err_t sdmmc_init_mmc_read_cid(sdmmc_card_t* card);
|
||||
esp_err_t sdmmc_init_host_bus_width(sdmmc_card_t* card);
|
||||
esp_err_t sdmmc_init_sd_bus_width(sdmmc_card_t* card);
|
||||
esp_err_t sdmmc_init_io_bus_width(sdmmc_card_t* card);
|
||||
esp_err_t sdmmc_init_mmc_bus_width(sdmmc_card_t* card);
|
||||
esp_err_t sdmmc_init_card_hs_mode(sdmmc_card_t* card);
|
||||
esp_err_t sdmmc_init_host_frequency(sdmmc_card_t* card);
|
||||
esp_err_t sdmmc_init_mmc_check_ext_csd(sdmmc_card_t* card);
|
||||
|
||||
/* Various helper functions */
|
||||
static inline bool host_is_spi(const sdmmc_card_t* card)
|
||||
{
|
||||
return (card->host.flags & SDMMC_HOST_FLAG_SPI) != 0;
|
||||
}
|
||||
|
||||
static inline uint32_t get_host_ocr(float voltage)
|
||||
{
|
||||
// TODO: report exact voltage to the card
|
||||
// For now tell that the host has 2.8-3.6V voltage range
|
||||
(void) voltage;
|
||||
return SD_OCR_VOL_MASK;
|
||||
}
|
||||
|
||||
void sdmmc_flip_byte_order(uint32_t* response, size_t size);
|
||||
|
||||
esp_err_t sdmmc_fix_host_flags(sdmmc_card_t* card);
|
||||
128
code/components/esp-sdmmc/sdmmc_init_mh.c
Normal file
128
code/components/esp-sdmmc/sdmmc_init_mh.c
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
|
||||
* Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "sdmmc_common_mh.h"
|
||||
|
||||
static const char* TAG = "sdmmc_init";
|
||||
|
||||
#define SDMMC_INIT_STEP(condition, function) \
|
||||
do { \
|
||||
if ((condition)) { \
|
||||
esp_err_t err = (function)(card); \
|
||||
if (err != ESP_OK) { \
|
||||
ESP_LOGD(TAG, "%s: %s returned 0x%x", __func__, #function, err); \
|
||||
return err; \
|
||||
} \
|
||||
} \
|
||||
} while(0);
|
||||
|
||||
|
||||
esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
|
||||
{
|
||||
memset(card, 0, sizeof(*card));
|
||||
memcpy(&card->host, config, sizeof(*config));
|
||||
const bool is_spi = host_is_spi(card);
|
||||
const bool always = true;
|
||||
const bool io_supported = true;
|
||||
|
||||
/* Check if host flags are compatible with slot configuration. */
|
||||
SDMMC_INIT_STEP(!is_spi, sdmmc_fix_host_flags);
|
||||
|
||||
/* Reset SDIO (CMD52, RES) before re-initializing IO (CMD5). */
|
||||
SDMMC_INIT_STEP(io_supported, sdmmc_io_reset);
|
||||
|
||||
/* GO_IDLE_STATE (CMD0) command resets the card */
|
||||
SDMMC_INIT_STEP(always, sdmmc_send_cmd_go_idle_state);
|
||||
|
||||
/* SEND_IF_COND (CMD8) command is used to identify SDHC/SDXC cards. */
|
||||
SDMMC_INIT_STEP(always, sdmmc_init_sd_if_cond);
|
||||
|
||||
/* IO_SEND_OP_COND(CMD5), Determine if the card is an IO card. */
|
||||
SDMMC_INIT_STEP(io_supported, sdmmc_init_io);
|
||||
|
||||
const bool is_mem = card->is_mem;
|
||||
const bool is_sdio = !is_mem;
|
||||
|
||||
/* Enable CRC16 checks for data transfers in SPI mode */
|
||||
SDMMC_INIT_STEP(is_spi, sdmmc_init_spi_crc);
|
||||
|
||||
/* Use SEND_OP_COND to set up card OCR */
|
||||
SDMMC_INIT_STEP(is_mem, sdmmc_init_ocr);
|
||||
|
||||
const bool is_mmc = is_mem && card->is_mmc;
|
||||
const bool is_sdmem = is_mem && !is_mmc;
|
||||
|
||||
ESP_LOGD(TAG, "%s: card type is %s", __func__,
|
||||
is_sdio ? "SDIO" : is_mmc ? "MMC" : "SD");
|
||||
|
||||
/* Read the contents of CID register*/
|
||||
SDMMC_INIT_STEP(is_mem, sdmmc_init_cid);
|
||||
|
||||
/* Assign RCA */
|
||||
SDMMC_INIT_STEP(!is_spi, sdmmc_init_rca);
|
||||
|
||||
/* Read and decode the contents of CSD register */
|
||||
SDMMC_INIT_STEP(is_mem, sdmmc_init_csd);
|
||||
|
||||
/* Decode the contents of mmc CID register */
|
||||
SDMMC_INIT_STEP(is_mmc && !is_spi, sdmmc_init_mmc_decode_cid);
|
||||
|
||||
/* Switch the card from stand-by mode to data transfer mode (not needed if
|
||||
* SPI interface is used). This is needed to issue SET_BLOCKLEN and
|
||||
* SEND_SCR commands.
|
||||
*/
|
||||
SDMMC_INIT_STEP(!is_spi, sdmmc_init_select_card);
|
||||
|
||||
/* SD memory cards:
|
||||
* Set block len for SDSC cards to 512 bytes (same as SDHC)
|
||||
* Read SCR
|
||||
* Wait to enter data transfer state
|
||||
*/
|
||||
SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_blocklen);
|
||||
SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_scr);
|
||||
SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_wait_data_ready);
|
||||
|
||||
/* MMC cards: read CXD */
|
||||
SDMMC_INIT_STEP(is_mmc, sdmmc_init_mmc_read_ext_csd);
|
||||
|
||||
/* Try to switch card to HS mode if the card supports it.
|
||||
* Set card->max_freq_khz value accordingly.
|
||||
*/
|
||||
SDMMC_INIT_STEP(always, sdmmc_init_card_hs_mode);
|
||||
|
||||
/* Set bus width. One call for every kind of card, then one for the host */
|
||||
if (!is_spi) {
|
||||
SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_bus_width);
|
||||
SDMMC_INIT_STEP(is_sdio, sdmmc_init_io_bus_width);
|
||||
SDMMC_INIT_STEP(is_mmc, sdmmc_init_mmc_bus_width);
|
||||
SDMMC_INIT_STEP(always, sdmmc_init_host_bus_width);
|
||||
}
|
||||
|
||||
/* SD card: read SD Status register */
|
||||
SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_ssr);
|
||||
|
||||
/* Switch to the host to use card->max_freq_khz frequency. */
|
||||
SDMMC_INIT_STEP(always, sdmmc_init_host_frequency);
|
||||
|
||||
/* Sanity check after switching the bus mode and frequency */
|
||||
SDMMC_INIT_STEP(is_sdmem, sdmmc_check_scr);
|
||||
/* Sanity check after eMMC switch to HS mode */
|
||||
SDMMC_INIT_STEP(is_mmc, sdmmc_init_mmc_check_ext_csd);
|
||||
/* TODO: add similar checks for SDIO */
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
636
code/components/esp-sdmmc/sdmmc_io_mh.c
Normal file
636
code/components/esp-sdmmc/sdmmc_io_mh.c
Normal file
@@ -0,0 +1,636 @@
|
||||
/*
|
||||
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
|
||||
* Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "sdmmc_common_mh.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_compiler.h"
|
||||
|
||||
|
||||
#define CIS_TUPLE(NAME) (cis_tuple_t) {.code=CISTPL_CODE_##NAME, .name=#NAME, .func=&cis_tuple_func_default, }
|
||||
#define CIS_TUPLE_WITH_FUNC(NAME, FUNC) (cis_tuple_t) {.code=CISTPL_CODE_##NAME, .name=#NAME, .func=&(FUNC), }
|
||||
|
||||
#define CIS_CHECK_SIZE(SIZE, MINIMAL) do {int store_size = (SIZE); if((store_size) < (MINIMAL)) return ESP_ERR_INVALID_SIZE;} while(0)
|
||||
#define CIS_CHECK_UNSUPPORTED(COND) do {if(!(COND)) return ESP_ERR_NOT_SUPPORTED;} while(0)
|
||||
#define CIS_GET_MINIMAL_SIZE 32
|
||||
|
||||
typedef esp_err_t (*cis_tuple_info_func_t)(const void* tuple_info, uint8_t* data, FILE* fp);
|
||||
|
||||
typedef struct {
|
||||
int code;
|
||||
const char *name;
|
||||
cis_tuple_info_func_t func;
|
||||
} cis_tuple_t;
|
||||
|
||||
static const char* TAG = "sdmmc_io";
|
||||
|
||||
static esp_err_t cis_tuple_func_default(const void* p, uint8_t* data, FILE* fp);
|
||||
static esp_err_t cis_tuple_func_manfid(const void* p, uint8_t* data, FILE* fp);
|
||||
static esp_err_t cis_tuple_func_cftable_entry(const void* p, uint8_t* data, FILE* fp);
|
||||
static esp_err_t cis_tuple_func_end(const void* p, uint8_t* data, FILE* fp);
|
||||
|
||||
static const cis_tuple_t cis_table[] = {
|
||||
CIS_TUPLE(NULL),
|
||||
CIS_TUPLE(DEVICE),
|
||||
CIS_TUPLE(CHKSUM),
|
||||
CIS_TUPLE(VERS1),
|
||||
CIS_TUPLE(ALTSTR),
|
||||
CIS_TUPLE(CONFIG),
|
||||
CIS_TUPLE_WITH_FUNC(CFTABLE_ENTRY, cis_tuple_func_cftable_entry),
|
||||
CIS_TUPLE_WITH_FUNC(MANFID, cis_tuple_func_manfid),
|
||||
CIS_TUPLE(FUNCID),
|
||||
CIS_TUPLE(FUNCE),
|
||||
CIS_TUPLE(VENDER_BEGIN),
|
||||
CIS_TUPLE(VENDER_END),
|
||||
CIS_TUPLE(SDIO_STD),
|
||||
CIS_TUPLE(SDIO_EXT),
|
||||
CIS_TUPLE_WITH_FUNC(END, cis_tuple_func_end),
|
||||
};
|
||||
|
||||
|
||||
esp_err_t sdmmc_io_reset(sdmmc_card_t* card)
|
||||
{
|
||||
uint8_t sdio_reset = CCCR_CTL_RES;
|
||||
esp_err_t err = sdmmc_io_rw_direct(card, 0, SD_IO_CCCR_CTL, SD_ARG_CMD52_WRITE, &sdio_reset);
|
||||
if (err == ESP_ERR_TIMEOUT || (host_is_spi(card) && err == ESP_ERR_NOT_SUPPORTED)) {
|
||||
/* Non-IO cards are allowed to time out (in SD mode) or
|
||||
* return "invalid command" error (in SPI mode).
|
||||
*/
|
||||
} else if (err == ESP_ERR_NOT_FOUND) {
|
||||
ESP_LOGD(TAG, "%s: card not present", __func__);
|
||||
return err;
|
||||
} else if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: unexpected return: 0x%x", __func__, err );
|
||||
return err;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_init_io(sdmmc_card_t* card)
|
||||
{
|
||||
/* IO_SEND_OP_COND(CMD5), Determine if the card is an IO card.
|
||||
* Non-IO cards will not respond to this command.
|
||||
*/
|
||||
esp_err_t err = sdmmc_io_send_op_cond(card, 0, &card->ocr);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGD(TAG, "%s: io_send_op_cond (1) returned 0x%x; not IO card", __func__, err);
|
||||
card->is_sdio = 0;
|
||||
card->is_mem = 1;
|
||||
} else {
|
||||
card->is_sdio = 1;
|
||||
|
||||
if (card->ocr & SD_IO_OCR_MEM_PRESENT) {
|
||||
ESP_LOGD(TAG, "%s: IO-only card", __func__);
|
||||
card->is_mem = 0;
|
||||
}
|
||||
card->num_io_functions = SD_IO_OCR_NUM_FUNCTIONS(card->ocr);
|
||||
ESP_LOGD(TAG, "%s: number of IO functions: %d", __func__, card->num_io_functions);
|
||||
if (card->num_io_functions == 0) {
|
||||
card->is_sdio = 0;
|
||||
}
|
||||
uint32_t host_ocr = get_host_ocr(card->host.io_voltage);
|
||||
host_ocr &= card->ocr;
|
||||
err = sdmmc_io_send_op_cond(card, host_ocr, &card->ocr);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: sdmmc_io_send_op_cond (1) returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
err = sdmmc_io_enable_int(card);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGD(TAG, "%s: sdmmc_enable_int failed (0x%x)", __func__, err);
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_init_io_bus_width(sdmmc_card_t* card)
|
||||
{
|
||||
esp_err_t err;
|
||||
card->log_bus_width = 0;
|
||||
if (card->host.flags & SDMMC_HOST_FLAG_4BIT) {
|
||||
uint8_t card_cap = 0;
|
||||
err = sdmmc_io_rw_direct(card, 0, SD_IO_CCCR_CARD_CAP,
|
||||
SD_ARG_CMD52_READ, &card_cap);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: sdmmc_io_rw_direct (read SD_IO_CCCR_CARD_CAP) returned 0x%0x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
ESP_LOGD(TAG, "IO card capabilities byte: %02x", card_cap);
|
||||
if (!(card_cap & CCCR_CARD_CAP_LSC) ||
|
||||
(card_cap & CCCR_CARD_CAP_4BLS)) {
|
||||
// This card supports 4-bit bus mode
|
||||
uint8_t bus_width = CCCR_BUS_WIDTH_4;
|
||||
err = sdmmc_io_rw_direct(card, 0, SD_IO_CCCR_BUS_WIDTH,
|
||||
SD_ARG_CMD52_WRITE, &bus_width);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: sdmmc_io_rw_direct (write SD_IO_CCCR_BUS_WIDTH) returned 0x%0x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
card->log_bus_width = 2;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t sdmmc_io_enable_hs_mode(sdmmc_card_t* card)
|
||||
{
|
||||
/* If the host is configured to use low frequency, don't attempt to switch */
|
||||
if (card->host.max_freq_khz < SDMMC_FREQ_DEFAULT) {
|
||||
card->max_freq_khz = card->host.max_freq_khz;
|
||||
return ESP_OK;
|
||||
} else if (card->host.max_freq_khz < SDMMC_FREQ_HIGHSPEED) {
|
||||
card->max_freq_khz = SDMMC_FREQ_DEFAULT;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* For IO cards, do write + read operation on "High Speed" register,
|
||||
* setting EHS bit. If both EHS and SHS read back as set, then HS mode
|
||||
* has been enabled.
|
||||
*/
|
||||
uint8_t val = CCCR_HIGHSPEED_ENABLE;
|
||||
esp_err_t err = sdmmc_io_rw_direct(card, 0, SD_IO_CCCR_HIGHSPEED,
|
||||
SD_ARG_CMD52_WRITE | SD_ARG_CMD52_EXCHANGE, &val);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGD(TAG, "%s: sdmmc_io_rw_direct returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "%s: CCCR_HIGHSPEED=0x%02x", __func__, val);
|
||||
const uint8_t hs_mask = CCCR_HIGHSPEED_ENABLE | CCCR_HIGHSPEED_SUPPORT;
|
||||
if ((val & hs_mask) != hs_mask) {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
card->max_freq_khz = SDMMC_FREQ_HIGHSPEED;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t sdmmc_io_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
sdmmc_command_t cmd = {
|
||||
.flags = SCF_CMD_BCR | SCF_RSP_R4,
|
||||
.arg = ocr,
|
||||
.opcode = SD_IO_SEND_OP_COND
|
||||
};
|
||||
for (size_t i = 0; i < 100; i++) {
|
||||
err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
break;
|
||||
}
|
||||
if ((MMC_R4(cmd.response) & SD_IO_OCR_MEM_READY) ||
|
||||
ocr == 0) {
|
||||
break;
|
||||
}
|
||||
err = ESP_ERR_TIMEOUT;
|
||||
vTaskDelay(SDMMC_IO_SEND_OP_COND_DELAY_MS / portTICK_PERIOD_MS);
|
||||
}
|
||||
if (err == ESP_OK && ocrp != NULL)
|
||||
*ocrp = MMC_R4(cmd.response);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_io_rw_direct(sdmmc_card_t* card, int func,
|
||||
uint32_t reg, uint32_t arg, uint8_t *byte)
|
||||
{
|
||||
esp_err_t err;
|
||||
sdmmc_command_t cmd = {
|
||||
.flags = SCF_CMD_AC | SCF_RSP_R5,
|
||||
.arg = 0,
|
||||
.opcode = SD_IO_RW_DIRECT
|
||||
};
|
||||
|
||||
arg |= (func & SD_ARG_CMD52_FUNC_MASK) << SD_ARG_CMD52_FUNC_SHIFT;
|
||||
arg |= (reg & SD_ARG_CMD52_REG_MASK) << SD_ARG_CMD52_REG_SHIFT;
|
||||
arg |= (*byte & SD_ARG_CMD52_DATA_MASK) << SD_ARG_CMD52_DATA_SHIFT;
|
||||
cmd.arg = arg;
|
||||
|
||||
err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGV(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
*byte = SD_R5_DATA(cmd.response);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t sdmmc_io_read_byte(sdmmc_card_t* card, uint32_t function,
|
||||
uint32_t addr, uint8_t *out_byte)
|
||||
{
|
||||
esp_err_t ret = sdmmc_io_rw_direct(card, function, addr, SD_ARG_CMD52_READ, out_byte);
|
||||
if (unlikely(ret != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "%s: sdmmc_io_rw_direct (read 0x%x) returned 0x%x", __func__, addr, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_io_write_byte(sdmmc_card_t* card, uint32_t function,
|
||||
uint32_t addr, uint8_t in_byte, uint8_t* out_byte)
|
||||
{
|
||||
uint8_t tmp_byte = in_byte;
|
||||
esp_err_t ret = sdmmc_io_rw_direct(card, function, addr,
|
||||
SD_ARG_CMD52_WRITE | SD_ARG_CMD52_EXCHANGE, &tmp_byte);
|
||||
if (unlikely(ret != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "%s: sdmmc_io_rw_direct (write 0x%x) returned 0x%x", __func__, addr, ret);
|
||||
return ret;
|
||||
}
|
||||
if (out_byte != NULL) {
|
||||
*out_byte = tmp_byte;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_io_rw_extended(sdmmc_card_t* card, int func,
|
||||
uint32_t reg, int arg, void *datap, size_t datalen)
|
||||
{
|
||||
esp_err_t err;
|
||||
const size_t max_byte_transfer_size = 512;
|
||||
sdmmc_command_t cmd = {
|
||||
.flags = SCF_CMD_AC | SCF_RSP_R5,
|
||||
.arg = 0,
|
||||
.opcode = SD_IO_RW_EXTENDED,
|
||||
.data = datap,
|
||||
.datalen = datalen,
|
||||
.blklen = max_byte_transfer_size /* TODO: read max block size from CIS */
|
||||
};
|
||||
|
||||
uint32_t count; /* number of bytes or blocks, depending on transfer mode */
|
||||
if (arg & SD_ARG_CMD53_BLOCK_MODE) {
|
||||
if (cmd.datalen % cmd.blklen != 0) {
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
count = cmd.datalen / cmd.blklen;
|
||||
} else {
|
||||
if (datalen > max_byte_transfer_size) {
|
||||
/* TODO: split into multiple operations? */
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
if (datalen == max_byte_transfer_size) {
|
||||
count = 0; // See 5.3.1 SDIO simplifed spec
|
||||
} else {
|
||||
count = datalen;
|
||||
}
|
||||
cmd.blklen = datalen;
|
||||
}
|
||||
|
||||
arg |= (func & SD_ARG_CMD53_FUNC_MASK) << SD_ARG_CMD53_FUNC_SHIFT;
|
||||
arg |= (reg & SD_ARG_CMD53_REG_MASK) << SD_ARG_CMD53_REG_SHIFT;
|
||||
arg |= (count & SD_ARG_CMD53_LENGTH_MASK) << SD_ARG_CMD53_LENGTH_SHIFT;
|
||||
cmd.arg = arg;
|
||||
|
||||
if ((arg & SD_ARG_CMD53_WRITE) == 0) {
|
||||
cmd.flags |= SCF_CMD_READ;
|
||||
}
|
||||
|
||||
err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_io_read_bytes(sdmmc_card_t* card, uint32_t function,
|
||||
uint32_t addr, void* dst, size_t size)
|
||||
{
|
||||
/* host quirk: SDIO transfer with length not divisible by 4 bytes
|
||||
* has to be split into two transfers: one with aligned length,
|
||||
* the other one for the remaining 1-3 bytes.
|
||||
*/
|
||||
uint8_t *pc_dst = dst;
|
||||
while (size > 0) {
|
||||
size_t size_aligned = size & (~3);
|
||||
size_t will_transfer = size_aligned > 0 ? size_aligned : size;
|
||||
|
||||
esp_err_t err = sdmmc_io_rw_extended(card, function, addr,
|
||||
SD_ARG_CMD53_READ | SD_ARG_CMD53_INCREMENT,
|
||||
pc_dst, will_transfer);
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
return err;
|
||||
}
|
||||
pc_dst += will_transfer;
|
||||
size -= will_transfer;
|
||||
addr += will_transfer;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_io_write_bytes(sdmmc_card_t* card, uint32_t function,
|
||||
uint32_t addr, const void* src, size_t size)
|
||||
{
|
||||
/* same host quirk as in sdmmc_io_read_bytes */
|
||||
const uint8_t *pc_src = (const uint8_t*) src;
|
||||
|
||||
while (size > 0) {
|
||||
size_t size_aligned = size & (~3);
|
||||
size_t will_transfer = size_aligned > 0 ? size_aligned : size;
|
||||
|
||||
esp_err_t err = sdmmc_io_rw_extended(card, function, addr,
|
||||
SD_ARG_CMD53_WRITE | SD_ARG_CMD53_INCREMENT,
|
||||
(void*) pc_src, will_transfer);
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
return err;
|
||||
}
|
||||
pc_src += will_transfer;
|
||||
size -= will_transfer;
|
||||
addr += will_transfer;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_io_read_blocks(sdmmc_card_t* card, uint32_t function,
|
||||
uint32_t addr, void* dst, size_t size)
|
||||
{
|
||||
if (unlikely(size % 4 != 0)) {
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
return sdmmc_io_rw_extended(card, function, addr,
|
||||
SD_ARG_CMD53_READ | SD_ARG_CMD53_INCREMENT | SD_ARG_CMD53_BLOCK_MODE,
|
||||
dst, size);
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_io_write_blocks(sdmmc_card_t* card, uint32_t function,
|
||||
uint32_t addr, const void* src, size_t size)
|
||||
{
|
||||
if (unlikely(size % 4 != 0)) {
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
return sdmmc_io_rw_extended(card, function, addr,
|
||||
SD_ARG_CMD53_WRITE | SD_ARG_CMD53_INCREMENT | SD_ARG_CMD53_BLOCK_MODE,
|
||||
(void*) src, size);
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_io_enable_int(sdmmc_card_t* card)
|
||||
{
|
||||
if (card->host.io_int_enable == NULL) {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
return (*card->host.io_int_enable)(card->host.slot);
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_io_wait_int(sdmmc_card_t* card, TickType_t timeout_ticks)
|
||||
{
|
||||
if (card->host.io_int_wait == NULL) {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
return (*card->host.io_int_wait)(card->host.slot, timeout_ticks);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Print the CIS information of a CIS card, currently only ESP slave supported.
|
||||
*/
|
||||
|
||||
static esp_err_t cis_tuple_func_default(const void* p, uint8_t* data, FILE* fp)
|
||||
{
|
||||
const cis_tuple_t* tuple = (const cis_tuple_t*)p;
|
||||
uint8_t code = *(data++);
|
||||
int size = *(data++);
|
||||
if (tuple) {
|
||||
fprintf(fp, "TUPLE: %s, size: %d: ", tuple->name, size);
|
||||
} else {
|
||||
fprintf(fp, "TUPLE: unknown(%02X), size: %d: ", code, size);
|
||||
}
|
||||
for (int i = 0; i < size; i++) fprintf(fp, "%02X ", *(data++));
|
||||
fprintf(fp, "\n");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t cis_tuple_func_manfid(const void* p, uint8_t* data, FILE* fp)
|
||||
{
|
||||
const cis_tuple_t* tuple = (const cis_tuple_t*)p;
|
||||
data++;
|
||||
int size = *(data++);
|
||||
fprintf(fp, "TUPLE: %s, size: %d\n", tuple->name, size);
|
||||
CIS_CHECK_SIZE(size, 4);
|
||||
fprintf(fp, " MANF: %04X, CARD: %04X\n", *(uint16_t*)(data), *(uint16_t*)(data+2));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t cis_tuple_func_end(const void* p, uint8_t* data, FILE* fp)
|
||||
{
|
||||
const cis_tuple_t* tuple = (const cis_tuple_t*)p;
|
||||
data++;
|
||||
fprintf(fp, "TUPLE: %s\n", tuple->name);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t cis_tuple_func_cftable_entry(const void* p, uint8_t* data, FILE* fp)
|
||||
{
|
||||
const cis_tuple_t* tuple = (const cis_tuple_t*)p;
|
||||
data++;
|
||||
int size = *(data++);
|
||||
fprintf(fp, "TUPLE: %s, size: %d\n", tuple->name, size);
|
||||
CIS_CHECK_SIZE(size, 2);
|
||||
|
||||
CIS_CHECK_SIZE(size--, 1);
|
||||
bool interface = data[0] & BIT(7);
|
||||
bool def = data[0] & BIT(6);
|
||||
int conf_ent_num = data[0] & 0x3F;
|
||||
fprintf(fp, " INDX: %02X, Intface: %d, Default: %d, Conf-Entry-Num: %d\n", *(data++), interface, def, conf_ent_num);
|
||||
|
||||
if (interface) {
|
||||
CIS_CHECK_SIZE(size--, 1);
|
||||
fprintf(fp, " IF: %02X\n", *(data++));
|
||||
}
|
||||
|
||||
CIS_CHECK_SIZE(size--, 1);
|
||||
bool misc = data[0] & BIT(7);
|
||||
int mem_space = (data[0] >> 5 )&(0x3);
|
||||
bool irq = data[0] & BIT(4);
|
||||
bool io_sp = data[0] & BIT(3);
|
||||
bool timing = data[0] & BIT(2);
|
||||
int power = data[0] & 3;
|
||||
fprintf(fp, " FS: %02X, misc: %d, mem_space: %d, irq: %d, io_space: %d, timing: %d, power: %d\n", *(data++), misc, mem_space, irq, io_sp, timing, power);
|
||||
|
||||
CIS_CHECK_UNSUPPORTED(power == 0); //power descriptor is not handled yet
|
||||
CIS_CHECK_UNSUPPORTED(!timing); //timing descriptor is not handled yet
|
||||
CIS_CHECK_UNSUPPORTED(!io_sp); //io space descriptor is not handled yet
|
||||
|
||||
if (irq) {
|
||||
CIS_CHECK_SIZE(size--, 1);
|
||||
bool mask = data[0] & BIT(4);
|
||||
fprintf(fp, " IR: %02X, mask: %d, ",*(data++), mask);
|
||||
if (mask) {
|
||||
CIS_CHECK_SIZE(size, 2);
|
||||
size-=2;
|
||||
fprintf(fp, " IRQ: %02X %02X\n", data[0], data[1]);
|
||||
data+=2;
|
||||
}
|
||||
}
|
||||
|
||||
if (mem_space) {
|
||||
CIS_CHECK_SIZE(size, 2);
|
||||
size-=2;
|
||||
CIS_CHECK_UNSUPPORTED(mem_space==1); //other cases not handled yet
|
||||
int len = *(uint16_t*)data;
|
||||
fprintf(fp, " LEN: %04X\n", len);
|
||||
data+=2;
|
||||
}
|
||||
|
||||
CIS_CHECK_UNSUPPORTED(misc==0); //misc descriptor is not handled yet
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static const cis_tuple_t* get_tuple(uint8_t code)
|
||||
{
|
||||
for (int i = 0; i < sizeof(cis_table)/sizeof(cis_tuple_t); i++) {
|
||||
if (code == cis_table[i].code) return &cis_table[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_io_print_cis_info(uint8_t* buffer, size_t buffer_size, FILE* fp)
|
||||
{
|
||||
ESP_LOG_BUFFER_HEXDUMP("CIS", buffer, buffer_size, ESP_LOG_DEBUG);
|
||||
if (!fp) fp = stdout;
|
||||
|
||||
uint8_t* cis = buffer;
|
||||
do {
|
||||
const cis_tuple_t* tuple = get_tuple(cis[0]);
|
||||
int size = cis[1];
|
||||
esp_err_t ret = ESP_OK;
|
||||
if (tuple) {
|
||||
ret = tuple->func(tuple, cis, fp);
|
||||
} else {
|
||||
ret = cis_tuple_func_default(NULL, cis, fp);
|
||||
}
|
||||
if (ret != ESP_OK) return ret;
|
||||
cis += 2 + size;
|
||||
if (tuple && tuple->code == CISTPL_CODE_END) break;
|
||||
} while (cis < buffer + buffer_size) ;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check tuples in the buffer.
|
||||
*
|
||||
* @param buf Buffer to check
|
||||
* @param buffer_size Size of the buffer
|
||||
* @param inout_cis_offset
|
||||
* - input: the last cis_offset, relative to the beginning of the buf. -1 if
|
||||
* this buffer begin with the tuple length, otherwise should be no smaller than
|
||||
* zero.
|
||||
* - output: when the end tuple found, output offset of the CISTPL_CODE_END
|
||||
* byte + 1 (relative to the beginning of the buffer; when not found, output
|
||||
* the address of next tuple code.
|
||||
*
|
||||
* @return true if found, false if haven't.
|
||||
*/
|
||||
static bool check_tuples_in_buffer(uint8_t* buf, int buffer_size, int* inout_cis_offset)
|
||||
{
|
||||
int cis_offset = *inout_cis_offset;
|
||||
if (cis_offset == -1) {
|
||||
//the CIS code is checked in the last buffer, skip to next tuple
|
||||
cis_offset += buf[0] + 2;
|
||||
}
|
||||
assert(cis_offset >= 0);
|
||||
while (1) {
|
||||
if (cis_offset < buffer_size) {
|
||||
//A CIS code in the buffer, check it
|
||||
if (buf[cis_offset] == CISTPL_CODE_END) {
|
||||
*inout_cis_offset = cis_offset + 1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (cis_offset + 1 < buffer_size) {
|
||||
cis_offset += buf[cis_offset+1] + 2;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
*inout_cis_offset = cis_offset;
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_io_get_cis_data(sdmmc_card_t* card, uint8_t* out_buffer, size_t buffer_size, size_t* inout_cis_size)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
WORD_ALIGNED_ATTR uint8_t buf[CIS_GET_MINIMAL_SIZE];
|
||||
|
||||
/* Pointer to size is a mandatory parameter */
|
||||
assert(inout_cis_size);
|
||||
|
||||
/*
|
||||
* CIS region exist in 0x1000~0x17FFF of FUNC 0, get the start address of it
|
||||
* from CCCR register.
|
||||
*/
|
||||
uint32_t addr;
|
||||
ret = sdmmc_io_read_bytes(card, 0, 9, &addr, 3);
|
||||
if (ret != ESP_OK) return ret;
|
||||
//the sdmmc_io driver reads 4 bytes, the most significant byte is not the address.
|
||||
addr &= 0xffffff;
|
||||
if (addr < 0x1000 || addr > 0x17FFF) {
|
||||
return ESP_ERR_INVALID_RESPONSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* To avoid reading too long, take the input value as limitation if
|
||||
* existing.
|
||||
*/
|
||||
size_t max_reading = UINT32_MAX;
|
||||
if (*inout_cis_size != 0) {
|
||||
max_reading = *inout_cis_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the length while reading. If find the end tuple, or reaches the
|
||||
* limitation, read no more and return both the data and the size already
|
||||
* read.
|
||||
*/
|
||||
int buffer_offset = 0;
|
||||
int cur_cis_offset = 0;
|
||||
bool end_tuple_found = false;
|
||||
do {
|
||||
ret = sdmmc_io_read_bytes(card, 0, addr + buffer_offset, &buf, CIS_GET_MINIMAL_SIZE);
|
||||
if (ret != ESP_OK) return ret;
|
||||
|
||||
//calculate relative to the beginning of the buffer
|
||||
int offset = cur_cis_offset - buffer_offset;
|
||||
bool finish = check_tuples_in_buffer(buf, CIS_GET_MINIMAL_SIZE, &offset);
|
||||
|
||||
int remain_size = buffer_size - buffer_offset;
|
||||
int copy_len;
|
||||
if (finish) {
|
||||
copy_len = MIN(offset, remain_size);
|
||||
end_tuple_found = true;
|
||||
} else {
|
||||
copy_len = MIN(CIS_GET_MINIMAL_SIZE, remain_size);
|
||||
}
|
||||
if (copy_len > 0) {
|
||||
memcpy(out_buffer + buffer_offset, buf, copy_len);
|
||||
}
|
||||
cur_cis_offset = buffer_offset + offset;
|
||||
buffer_offset += CIS_GET_MINIMAL_SIZE;
|
||||
} while (!end_tuple_found && buffer_offset < max_reading);
|
||||
|
||||
if (end_tuple_found) {
|
||||
*inout_cis_size = cur_cis_offset;
|
||||
if (cur_cis_offset > buffer_size) {
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
} else {
|
||||
return ESP_OK;
|
||||
}
|
||||
} else {
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
300
code/components/esp-sdmmc/sdmmc_mmc_mh.c
Normal file
300
code/components/esp-sdmmc/sdmmc_mmc_mh.c
Normal file
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
|
||||
* Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include "sdmmc_common_mh.h"
|
||||
|
||||
static const char* TAG = "sdmmc_mmc";
|
||||
|
||||
|
||||
esp_err_t sdmmc_init_mmc_read_ext_csd(sdmmc_card_t* card)
|
||||
{
|
||||
int card_type;
|
||||
esp_err_t err = ESP_OK;
|
||||
|
||||
uint8_t* ext_csd = heap_caps_malloc(EXT_CSD_MMC_SIZE, MALLOC_CAP_DMA);
|
||||
if (!ext_csd) {
|
||||
ESP_LOGE(TAG, "%s: could not allocate ext_csd", __func__);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
uint32_t sectors = 0;
|
||||
|
||||
ESP_LOGD(TAG, "MMC version: %d", card->csd.mmc_ver);
|
||||
if (card->csd.mmc_ver < MMC_CSD_MMCVER_4_0) {
|
||||
err = ESP_ERR_NOT_SUPPORTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* read EXT_CSD */
|
||||
err = sdmmc_mmc_send_ext_csd_data(card, ext_csd, EXT_CSD_MMC_SIZE);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: send_ext_csd_data error 0x%x", __func__, err);
|
||||
goto out;
|
||||
}
|
||||
card_type = ext_csd[EXT_CSD_CARD_TYPE];
|
||||
card->is_ddr = 0;
|
||||
if (card_type & EXT_CSD_CARD_TYPE_F_52M_1_8V) {
|
||||
card->max_freq_khz = SDMMC_FREQ_52M;
|
||||
if ((card->host.flags & SDMMC_HOST_FLAG_DDR) &&
|
||||
card->host.max_freq_khz >= SDMMC_FREQ_26M &&
|
||||
card->host.get_bus_width(card->host.slot) == 4) {
|
||||
ESP_LOGD(TAG, "card and host support DDR mode");
|
||||
card->is_ddr = 1;
|
||||
}
|
||||
} else if (card_type & EXT_CSD_CARD_TYPE_F_52M) {
|
||||
card->max_freq_khz = SDMMC_FREQ_52M;
|
||||
} else if (card_type & EXT_CSD_CARD_TYPE_F_26M) {
|
||||
card->max_freq_khz = SDMMC_FREQ_26M;
|
||||
} else {
|
||||
ESP_LOGW(TAG, "%s: unknown CARD_TYPE 0x%x", __func__, card_type);
|
||||
}
|
||||
/* For MMC cards, use speed value from EXT_CSD */
|
||||
card->csd.tr_speed = card->max_freq_khz * 1000;
|
||||
ESP_LOGD(TAG, "MMC card type %d, max_freq_khz=%d, is_ddr=%d", card_type, card->max_freq_khz, card->is_ddr);
|
||||
card->max_freq_khz = MIN(card->max_freq_khz, card->host.max_freq_khz);
|
||||
|
||||
if (card->host.flags & SDMMC_HOST_FLAG_8BIT) {
|
||||
card->ext_csd.power_class = ext_csd[(card->max_freq_khz > SDMMC_FREQ_26M) ?
|
||||
EXT_CSD_PWR_CL_52_360 : EXT_CSD_PWR_CL_26_360] >> 4;
|
||||
card->log_bus_width = 3;
|
||||
} else if (card->host.flags & SDMMC_HOST_FLAG_4BIT) {
|
||||
card->ext_csd.power_class = ext_csd[(card->max_freq_khz > SDMMC_FREQ_26M) ?
|
||||
EXT_CSD_PWR_CL_52_360 : EXT_CSD_PWR_CL_26_360] & 0x0f;
|
||||
card->log_bus_width = 2;
|
||||
} else {
|
||||
card->ext_csd.power_class = 0; //card must be able to do full rate at powerclass 0 in 1-bit mode
|
||||
card->log_bus_width = 0;
|
||||
}
|
||||
|
||||
sectors = ( ext_csd[EXT_CSD_SEC_COUNT + 0] << 0 )
|
||||
| ( ext_csd[EXT_CSD_SEC_COUNT + 1] << 8 )
|
||||
| ( ext_csd[EXT_CSD_SEC_COUNT + 2] << 16 )
|
||||
| ( ext_csd[EXT_CSD_SEC_COUNT + 3] << 24 );
|
||||
|
||||
if (sectors > (2u * 1024 * 1024 * 1024) / 512) {
|
||||
card->csd.capacity = sectors;
|
||||
}
|
||||
|
||||
/* erased state of a bit, if 1 byte value read is 0xFF else 0x00 */
|
||||
card->ext_csd.erase_mem_state = ext_csd[EXT_CSD_ERASED_MEM_CONT];
|
||||
card->ext_csd.rev = ext_csd[EXT_CSD_REV];
|
||||
card->ext_csd.sec_feature = ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT];
|
||||
|
||||
out:
|
||||
free(ext_csd);
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_init_mmc_bus_width(sdmmc_card_t* card)
|
||||
{
|
||||
esp_err_t err;
|
||||
if (card->ext_csd.power_class != 0) {
|
||||
err = sdmmc_mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_POWER_CLASS, card->ext_csd.power_class);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: can't change power class (%d bit), 0x%x"
|
||||
, __func__, card->ext_csd.power_class, err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (card->log_bus_width > 0) {
|
||||
int csd_bus_width_value = EXT_CSD_BUS_WIDTH_1;
|
||||
int bus_width = 1;
|
||||
if (card->log_bus_width == 2) {
|
||||
if (card->is_ddr) {
|
||||
csd_bus_width_value = EXT_CSD_BUS_WIDTH_4_DDR;
|
||||
} else {
|
||||
csd_bus_width_value = EXT_CSD_BUS_WIDTH_4;
|
||||
}
|
||||
bus_width = 4;
|
||||
} else if (card->log_bus_width == 3) {
|
||||
if (card->is_ddr) {
|
||||
csd_bus_width_value = EXT_CSD_BUS_WIDTH_8_DDR;
|
||||
} else {
|
||||
csd_bus_width_value = EXT_CSD_BUS_WIDTH_8;
|
||||
}
|
||||
bus_width = 8;
|
||||
}
|
||||
err = sdmmc_mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_BUS_WIDTH, csd_bus_width_value);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: can't change bus width (%d bit), 0x%x",
|
||||
__func__, bus_width, err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_mmc_enable_hs_mode(sdmmc_card_t* card)
|
||||
{
|
||||
esp_err_t err;
|
||||
if (card->max_freq_khz > SDMMC_FREQ_26M) {
|
||||
/* switch to high speed timing */
|
||||
err = sdmmc_mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HS_TIMING, EXT_CSD_HS_TIMING_HS);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: mmc_switch EXT_CSD_HS_TIMING_HS error 0x%x",
|
||||
__func__, err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_mmc_decode_cid(int mmc_ver, sdmmc_response_t resp, sdmmc_cid_t* out_cid)
|
||||
{
|
||||
if (mmc_ver == MMC_CSD_MMCVER_1_0 ||
|
||||
mmc_ver == MMC_CSD_MMCVER_1_4) {
|
||||
out_cid->mfg_id = MMC_CID_MID_V1(resp);
|
||||
out_cid->oem_id = 0;
|
||||
MMC_CID_PNM_V1_CPY(resp, out_cid->name);
|
||||
out_cid->revision = MMC_CID_REV_V1(resp);
|
||||
out_cid->serial = MMC_CID_PSN_V1(resp);
|
||||
out_cid->date = MMC_CID_MDT_V1(resp);
|
||||
} else if (mmc_ver == MMC_CSD_MMCVER_2_0 ||
|
||||
mmc_ver == MMC_CSD_MMCVER_3_1 ||
|
||||
mmc_ver == MMC_CSD_MMCVER_4_0) {
|
||||
out_cid->mfg_id = MMC_CID_MID_V2(resp);
|
||||
out_cid->oem_id = MMC_CID_OID_V2(resp);
|
||||
MMC_CID_PNM_V1_CPY(resp, out_cid->name);
|
||||
out_cid->revision = 0;
|
||||
out_cid->serial = MMC_CID_PSN_V1(resp);
|
||||
out_cid->date = 0;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_mmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd)
|
||||
{
|
||||
out_csd->csd_ver = MMC_CSD_CSDVER(response);
|
||||
if (out_csd->csd_ver == MMC_CSD_CSDVER_1_0 ||
|
||||
out_csd->csd_ver == MMC_CSD_CSDVER_2_0 ||
|
||||
out_csd->csd_ver == MMC_CSD_CSDVER_EXT_CSD) {
|
||||
out_csd->mmc_ver = MMC_CSD_MMCVER(response);
|
||||
out_csd->capacity = MMC_CSD_CAPACITY(response);
|
||||
out_csd->read_block_len = MMC_CSD_READ_BL_LEN(response);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "unknown MMC CSD structure version 0x%x\n", out_csd->csd_ver);
|
||||
return 1;
|
||||
}
|
||||
int read_bl_size = 1 << out_csd->read_block_len;
|
||||
out_csd->sector_size = MIN(read_bl_size, 512);
|
||||
if (out_csd->sector_size < read_bl_size) {
|
||||
out_csd->capacity *= read_bl_size / out_csd->sector_size;
|
||||
}
|
||||
/* tr_speed will be determined when reading CXD */
|
||||
out_csd->tr_speed = 0;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_mmc_send_ext_csd_data(sdmmc_card_t* card, void *out_data, size_t datalen)
|
||||
{
|
||||
assert(esp_ptr_dma_capable(out_data));
|
||||
sdmmc_command_t cmd = {
|
||||
.data = out_data,
|
||||
.datalen = datalen,
|
||||
.blklen = datalen,
|
||||
.opcode = MMC_SEND_EXT_CSD,
|
||||
.arg = 0,
|
||||
.flags = SCF_CMD_ADTC | SCF_RSP_R1 | SCF_CMD_READ
|
||||
};
|
||||
return sdmmc_send_cmd(card, &cmd);
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_mmc_switch(sdmmc_card_t* card, uint8_t set, uint8_t index, uint8_t value)
|
||||
{
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = MMC_SWITCH,
|
||||
.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (index << 16) | (value << 8) | set,
|
||||
.flags = SCF_RSP_R1B | SCF_CMD_AC | SCF_WAIT_BUSY,
|
||||
};
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err == ESP_OK) {
|
||||
//check response bit to see that switch was accepted
|
||||
if (MMC_R1(cmd.response) & MMC_R1_SWITCH_ERROR) {
|
||||
err = ESP_ERR_INVALID_RESPONSE;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_init_mmc_check_ext_csd(sdmmc_card_t* card)
|
||||
{
|
||||
assert(card->is_mem == 1 && card->rca != 0);
|
||||
|
||||
/*
|
||||
* Integrity check required if card switched to HS mode
|
||||
* card->max_freq_khz = MIN(card->max_freq_khz, card->host.max_freq_khz)
|
||||
* For 26MHz limit background see sdmmc_mmc_enable_hs_mode()
|
||||
*/
|
||||
if (card->max_freq_khz <= SDMMC_FREQ_26M) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* ensure EXT_CSD buffer is available before starting any SD-card operation */
|
||||
uint8_t* ext_csd = heap_caps_malloc(EXT_CSD_MMC_SIZE, MALLOC_CAP_DMA);
|
||||
if (!ext_csd) {
|
||||
ESP_LOGE(TAG, "%s: could not allocate ext_csd", __func__);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
/* ensure card is in transfer state before read ext_csd */
|
||||
uint32_t status;
|
||||
esp_err_t err = sdmmc_send_cmd_send_status(card, &status);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: send_status returned 0x%x", __func__, err);
|
||||
goto out;
|
||||
}
|
||||
status = ((status & MMC_R1_CURRENT_STATE_MASK) >> MMC_R1_CURRENT_STATE_POS);
|
||||
if (status != MMC_R1_CURRENT_STATE_TRAN) {
|
||||
ESP_LOGE(TAG, "%s: card not in transfer state", __func__);
|
||||
err = ESP_ERR_INVALID_STATE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* read EXT_CSD to ensure device works fine in HS mode */
|
||||
err = sdmmc_mmc_send_ext_csd_data(card, ext_csd, EXT_CSD_MMC_SIZE);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: send_ext_csd_data error 0x%x", __func__, err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* EXT_CSD static fields should match the previous read values in sdmmc_card_init */
|
||||
if ((card->ext_csd.rev != ext_csd[EXT_CSD_REV]) ||
|
||||
(card->ext_csd.sec_feature != ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT])) {
|
||||
ESP_LOGE(TAG, "%s: Data integrity test fail in HS mode", __func__);
|
||||
err = ESP_FAIL;
|
||||
}
|
||||
|
||||
out:
|
||||
free(ext_csd);
|
||||
return err;
|
||||
}
|
||||
|
||||
uint32_t sdmmc_mmc_get_erase_timeout_ms(const sdmmc_card_t* card, int arg, size_t erase_size_kb)
|
||||
{
|
||||
/* TODO: calculate erase timeout based on ext_csd (trim_timeout) */
|
||||
uint32_t timeout_ms = SDMMC_SD_DISCARD_TIMEOUT * erase_size_kb / card->csd.sector_size;
|
||||
timeout_ms = MAX(1000, timeout_ms);
|
||||
ESP_LOGD(TAG, "%s: erase timeout %u s (erasing %u kB, %ums per sector)",
|
||||
__func__, timeout_ms / 1000, erase_size_kb, SDMMC_SD_DISCARD_TIMEOUT);
|
||||
return timeout_ms;
|
||||
}
|
||||
449
code/components/esp-sdmmc/sdmmc_sd_mh.c
Normal file
449
code/components/esp-sdmmc/sdmmc_sd_mh.c
Normal file
@@ -0,0 +1,449 @@
|
||||
/*
|
||||
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
|
||||
* Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "sdmmc_common_mh.h"
|
||||
|
||||
static const char* TAG = "sdmmc_sd";
|
||||
|
||||
esp_err_t sdmmc_init_sd_if_cond(sdmmc_card_t* card)
|
||||
{
|
||||
/* SEND_IF_COND (CMD8) command is used to identify SDHC/SDXC cards.
|
||||
* SD v1 and non-SD cards will not respond to this command.
|
||||
*/
|
||||
uint32_t host_ocr = get_host_ocr(card->host.io_voltage);
|
||||
esp_err_t err = sdmmc_send_cmd_send_if_cond(card, host_ocr);
|
||||
if (err == ESP_OK) {
|
||||
ESP_LOGD(TAG, "SDHC/SDXC card");
|
||||
host_ocr |= SD_OCR_SDHC_CAP;
|
||||
} else if (err == ESP_ERR_TIMEOUT) {
|
||||
ESP_LOGD(TAG, "CMD8 timeout; not an SD v2.00 card");
|
||||
} else if (host_is_spi(card) && err == ESP_ERR_NOT_SUPPORTED) {
|
||||
ESP_LOGD(TAG, "CMD8 rejected; not an SD v2.00 card");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "%s: send_if_cond (1) returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
card->ocr = host_ocr;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_init_sd_blocklen(sdmmc_card_t* card)
|
||||
{
|
||||
/* SDSC cards support configurable data block lengths.
|
||||
* We don't use this feature and set the block length to 512 bytes,
|
||||
* same as the block length for SDHC cards.
|
||||
*/
|
||||
if ((card->ocr & SD_OCR_SDHC_CAP) == 0) {
|
||||
esp_err_t err = sdmmc_send_cmd_set_blocklen(card, &card->csd);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: set_blocklen returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_init_sd_scr(sdmmc_card_t* card)
|
||||
{
|
||||
esp_err_t err;
|
||||
/* Get the contents of SCR register: bus width and the version of SD spec
|
||||
* supported by the card.
|
||||
* In SD mode, this is the first command which uses D0 line. Errors at
|
||||
* this step usually indicate connection issue or lack of pull-up resistor.
|
||||
*/
|
||||
err = sdmmc_send_cmd_send_scr(card, &card->scr);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: send_scr (1) returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if ((card->scr.bus_width & SCR_SD_BUS_WIDTHS_4BIT)
|
||||
&& (card->host.flags & SDMMC_HOST_FLAG_4BIT)) {
|
||||
card->log_bus_width = 2;
|
||||
} else {
|
||||
card->log_bus_width = 0;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_init_sd_ssr(sdmmc_card_t* card)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
/* Get the contents of SSR register: SD additional information
|
||||
* ACMD13 to read 512byte SD status information
|
||||
*/
|
||||
uint32_t* sd_ssr = heap_caps_calloc(1, SD_SSR_SIZE, MALLOC_CAP_DMA);
|
||||
if (!sd_ssr) {
|
||||
ESP_LOGE(TAG, "%s: could not allocate sd_ssr", __func__);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
sdmmc_command_t cmd = {
|
||||
.data = sd_ssr,
|
||||
.datalen = SD_SSR_SIZE,
|
||||
.blklen = SD_SSR_SIZE,
|
||||
.opcode = SD_APP_SD_STATUS,
|
||||
.arg = 0,
|
||||
.flags = SCF_CMD_ADTC | SCF_RSP_R1 | SCF_CMD_READ
|
||||
};
|
||||
|
||||
// read SD status register
|
||||
err = sdmmc_send_app_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
free(sd_ssr);
|
||||
ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = sdmmc_decode_ssr(sd_ssr, &card->ssr);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: error sdmmc_decode_scr returned 0x%x", __func__, err);
|
||||
}
|
||||
free(sd_ssr);
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_init_sd_bus_width(sdmmc_card_t* card)
|
||||
{
|
||||
int width = 1;
|
||||
if (card->log_bus_width == 2) {
|
||||
width = 4;
|
||||
} else if (card->log_bus_width == 3) {
|
||||
width = 8;
|
||||
}
|
||||
esp_err_t err = sdmmc_send_cmd_set_bus_width(card, width);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "set_bus_width failed (0x%x)", err);
|
||||
return err;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_init_sd_wait_data_ready(sdmmc_card_t* card)
|
||||
{
|
||||
/* Wait for the card to be ready for data transfers */
|
||||
uint32_t status = 0;
|
||||
uint32_t count = 0;
|
||||
while (!host_is_spi(card) && !(status & MMC_R1_READY_FOR_DATA)) {
|
||||
// TODO: add some timeout here
|
||||
esp_err_t err = sdmmc_send_cmd_send_status(card, &status);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
if (++count % 16 == 0) {
|
||||
ESP_LOGV(TAG, "waiting for card to become ready (%d)", count);
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_send_cmd_switch_func(sdmmc_card_t* card,
|
||||
uint32_t mode, uint32_t group, uint32_t function,
|
||||
sdmmc_switch_func_rsp_t* resp)
|
||||
{
|
||||
if (card->scr.sd_spec < SCR_SD_SPEC_VER_1_10 ||
|
||||
((card->csd.card_command_class & SD_CSD_CCC_SWITCH) == 0)) {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
if (group == 0 ||
|
||||
group > SD_SFUNC_GROUP_MAX ||
|
||||
function > SD_SFUNC_FUNC_MAX) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (mode > 1) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
uint32_t group_shift = (group - 1) << 2;
|
||||
/* all functions which should not be affected are set to 0xf (no change) */
|
||||
uint32_t other_func_mask = (0x00ffffff & ~(0xf << group_shift));
|
||||
uint32_t func_val = (function << group_shift) | other_func_mask;
|
||||
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = MMC_SWITCH,
|
||||
.flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1,
|
||||
.blklen = sizeof(sdmmc_switch_func_rsp_t),
|
||||
.data = resp->data,
|
||||
.datalen = sizeof(sdmmc_switch_func_rsp_t),
|
||||
.arg = (!!mode << 31) | func_val
|
||||
};
|
||||
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
sdmmc_flip_byte_order(resp->data, sizeof(sdmmc_switch_func_rsp_t));
|
||||
uint32_t resp_ver = SD_SFUNC_VER(resp->data);
|
||||
if (resp_ver == 0) {
|
||||
/* busy response is never sent */
|
||||
} else if (resp_ver == 1) {
|
||||
if (SD_SFUNC_BUSY(resp->data, group) & (1 << function)) {
|
||||
ESP_LOGD(TAG, "%s: response indicates function %d:%d is busy",
|
||||
__func__, group, function);
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
} else {
|
||||
ESP_LOGD(TAG, "%s: got an invalid version of SWITCH_FUNC response: 0x%02x",
|
||||
__func__, resp_ver);
|
||||
return ESP_ERR_INVALID_RESPONSE;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_enable_hs_mode(sdmmc_card_t* card)
|
||||
{
|
||||
/* This will determine if the card supports SWITCH_FUNC command,
|
||||
* and high speed mode. If the cards supports both, this will enable
|
||||
* high speed mode at the card side.
|
||||
*/
|
||||
if (card->scr.sd_spec < SCR_SD_SPEC_VER_1_10 ||
|
||||
((card->csd.card_command_class & SD_CSD_CCC_SWITCH) == 0)) {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
sdmmc_switch_func_rsp_t* response = (sdmmc_switch_func_rsp_t*)
|
||||
heap_caps_malloc(sizeof(*response), MALLOC_CAP_DMA);
|
||||
if (response == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
esp_err_t err = sdmmc_send_cmd_switch_func(card, 0, SD_ACCESS_MODE, 0, response);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGD(TAG, "%s: sdmmc_send_cmd_switch_func (1) returned 0x%x", __func__, err);
|
||||
goto out;
|
||||
}
|
||||
uint32_t supported_mask = SD_SFUNC_SUPPORTED(response->data, 1);
|
||||
if ((supported_mask & BIT(SD_ACCESS_MODE_SDR25)) == 0) {
|
||||
err = ESP_ERR_NOT_SUPPORTED;
|
||||
goto out;
|
||||
}
|
||||
err = sdmmc_send_cmd_switch_func(card, 1, SD_ACCESS_MODE, SD_ACCESS_MODE_SDR25, response);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGD(TAG, "%s: sdmmc_send_cmd_switch_func (2) returned 0x%x", __func__, err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
free(response);
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_enable_hs_mode_and_check(sdmmc_card_t* card)
|
||||
{
|
||||
/* All cards should support at least default speed */
|
||||
card->max_freq_khz = SDMMC_FREQ_DEFAULT;
|
||||
if (card->host.max_freq_khz <= card->max_freq_khz) {
|
||||
/* Host is configured to use low frequency, don't attempt to switch */
|
||||
card->max_freq_khz = card->host.max_freq_khz;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* Try to enabled HS mode */
|
||||
esp_err_t err = sdmmc_enable_hs_mode(card);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
/* HS mode has been enabled on the card.
|
||||
* Read CSD again, it should now indicate that the card supports
|
||||
* 50MHz clock.
|
||||
* Since SEND_CSD is allowed only in standby mode, and the card is currently in data transfer
|
||||
* mode, deselect the card first, then get the CSD, then select the card again. This step is
|
||||
* not required in SPI mode, since CMD7 (select_card) is not supported.
|
||||
*/
|
||||
const bool is_spi = host_is_spi(card);
|
||||
if (!is_spi) {
|
||||
err = sdmmc_send_cmd_select_card(card, 0);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: select_card (1) returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
err = sdmmc_send_cmd_send_csd(card, &card->csd);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: send_csd returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
if (!is_spi) {
|
||||
err = sdmmc_send_cmd_select_card(card, card->rca);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: select_card (2) returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (card->csd.tr_speed != 50000000) {
|
||||
ESP_LOGW(TAG, "unexpected: after enabling HS mode, tr_speed=%d", card->csd.tr_speed);
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
card->max_freq_khz = SDMMC_FREQ_HIGHSPEED;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_check_scr(sdmmc_card_t* card)
|
||||
{
|
||||
/* If frequency switch has been performed, read SCR register one more time
|
||||
* and compare the result with the previous one. Use this simple check as
|
||||
* an indicator of potential signal integrity issues.
|
||||
*/
|
||||
sdmmc_scr_t scr_tmp = { 0 };
|
||||
esp_err_t err = sdmmc_send_cmd_send_scr(card, &scr_tmp);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: send_scr returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
if (memcmp(&card->scr, &scr_tmp, sizeof(scr_tmp)) != 0) {
|
||||
ESP_LOGE(TAG, "got corrupted data after increasing clock frequency");
|
||||
return ESP_ERR_INVALID_RESPONSE;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_init_spi_crc(sdmmc_card_t* card)
|
||||
{
|
||||
/* In SD mode, CRC checks of data transfers are mandatory and performed
|
||||
* by the hardware. In SPI mode, CRC16 of data transfers is optional and
|
||||
* needs to be enabled.
|
||||
*/
|
||||
assert(host_is_spi(card));
|
||||
esp_err_t err = sdmmc_send_cmd_crc_on_off(card, true);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: sdmmc_send_cmd_crc_on_off returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_decode_cid(sdmmc_response_t resp, sdmmc_cid_t* out_cid)
|
||||
{
|
||||
out_cid->mfg_id = SD_CID_MID(resp);
|
||||
out_cid->oem_id = SD_CID_OID(resp);
|
||||
SD_CID_PNM_CPY(resp, out_cid->name);
|
||||
out_cid->revision = SD_CID_REV(resp);
|
||||
out_cid->serial = SD_CID_PSN(resp);
|
||||
out_cid->date = SD_CID_MDT(resp);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd)
|
||||
{
|
||||
out_csd->csd_ver = SD_CSD_CSDVER(response);
|
||||
switch (out_csd->csd_ver) {
|
||||
case SD_CSD_CSDVER_2_0:
|
||||
out_csd->capacity = SD_CSD_V2_CAPACITY(response);
|
||||
out_csd->read_block_len = SD_CSD_V2_BL_LEN;
|
||||
break;
|
||||
case SD_CSD_CSDVER_1_0:
|
||||
out_csd->capacity = SD_CSD_CAPACITY(response);
|
||||
out_csd->read_block_len = SD_CSD_READ_BL_LEN(response);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "unknown SD CSD structure version 0x%x", out_csd->csd_ver);
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
out_csd->card_command_class = SD_CSD_CCC(response);
|
||||
int read_bl_size = 1 << out_csd->read_block_len;
|
||||
out_csd->sector_size = MIN(read_bl_size, 512);
|
||||
if (out_csd->sector_size < read_bl_size) {
|
||||
out_csd->capacity *= read_bl_size / out_csd->sector_size;
|
||||
}
|
||||
int speed = SD_CSD_SPEED(response);
|
||||
if (speed == SD_CSD_SPEED_50_MHZ) {
|
||||
out_csd->tr_speed = 50000000;
|
||||
} else {
|
||||
out_csd->tr_speed = 25000000;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_decode_scr(uint32_t *raw_scr, sdmmc_scr_t* out_scr)
|
||||
{
|
||||
sdmmc_response_t resp = { 0 };
|
||||
resp[1] = __builtin_bswap32(raw_scr[0]);
|
||||
resp[0] = __builtin_bswap32(raw_scr[1]);
|
||||
int ver = SCR_STRUCTURE(resp);
|
||||
if (ver != 0) {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
out_scr->sd_spec = SCR_SD_SPEC(resp);
|
||||
out_scr->erase_mem_state = SCR_DATA_STAT_AFTER_ERASE(resp);
|
||||
out_scr->bus_width = SCR_SD_BUS_WIDTHS(resp);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static const uint32_t s_au_to_size_kb[] = {
|
||||
0, 16, 32, 64,
|
||||
128, 256, 512, 1024,
|
||||
2 * 1024, 4 * 1024,
|
||||
8 * 1024, 12 * 1024,
|
||||
16 * 1024, 24 * 1024,
|
||||
32 * 1024, 64 * 1024
|
||||
};
|
||||
_Static_assert(sizeof(s_au_to_size_kb)/sizeof(s_au_to_size_kb[0]) == 16, "invalid number of elements in s_au_to_size_kb");
|
||||
|
||||
esp_err_t sdmmc_decode_ssr(uint32_t *raw_ssr, sdmmc_ssr_t* out_ssr)
|
||||
{
|
||||
uint32_t ssr[(SD_SSR_SIZE/sizeof(uint32_t))] = { 0 };
|
||||
size_t j = (SD_SSR_SIZE/sizeof(uint32_t) - 1);
|
||||
|
||||
for(size_t i = 0; i < (SD_SSR_SIZE/sizeof(uint32_t)); i++) {
|
||||
ssr[j - i] = __builtin_bswap32(raw_ssr[i]);
|
||||
}
|
||||
|
||||
out_ssr->cur_bus_width = SSR_DAT_BUS_WIDTH(ssr);
|
||||
out_ssr->discard_support = SSR_DISCARD_SUPPORT(ssr);
|
||||
out_ssr->fule_support = SSR_FULE_SUPPORT(ssr);
|
||||
uint32_t au = SSR_AU_SIZE(ssr);
|
||||
out_ssr->alloc_unit_kb = s_au_to_size_kb[au];
|
||||
out_ssr->erase_timeout = SSR_ERASE_TIMEOUT(ssr);
|
||||
out_ssr->erase_size_au = SSR_ERASE_SIZE(ssr);
|
||||
out_ssr->erase_offset = SSR_ERASE_OFFSET(ssr);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
uint32_t sdmmc_sd_get_erase_timeout_ms(const sdmmc_card_t* card, int arg, size_t erase_size_kb)
|
||||
{
|
||||
if (arg == SDMMC_SD_DISCARD_ARG) {
|
||||
return SDMMC_SD_DISCARD_TIMEOUT;
|
||||
} else if (arg == SDMMC_SD_ERASE_ARG) {
|
||||
if (card->ssr.alloc_unit_kb != 0 &&
|
||||
card->ssr.erase_size_au != 0 &&
|
||||
card->ssr.erase_timeout != 0 &&
|
||||
card->ssr.erase_offset != 0) {
|
||||
/* Card supports erase timeout estimation. See the erase timeout equation in SD spec. */
|
||||
uint32_t timeout_sec = card->ssr.erase_offset +
|
||||
card->ssr.erase_timeout * (erase_size_kb + card->ssr.alloc_unit_kb - 1) /
|
||||
(card->ssr.erase_size_au * card->ssr.alloc_unit_kb);
|
||||
ESP_LOGD(TAG, "%s: erase timeout %u s (erasing %u kB, ES=%u, ET=%u, EO=%u, AU=%u kB)",
|
||||
__func__, timeout_sec, erase_size_kb, card->ssr.erase_size_au,
|
||||
card->ssr.erase_timeout, card->ssr.erase_offset, card->ssr.alloc_unit_kb);
|
||||
return timeout_sec * 1000;
|
||||
} else {
|
||||
uint32_t timeout_ms = SDMMC_SD_DISCARD_TIMEOUT * erase_size_kb / card->csd.sector_size;
|
||||
timeout_ms = MAX(1000, timeout_ms);
|
||||
ESP_LOGD(TAG, "%s: erase timeout %u s (erasing %u kB, %ums per sector)",
|
||||
__func__, timeout_ms / 1000, erase_size_kb, SDMMC_SD_DISCARD_TIMEOUT);
|
||||
return timeout_ms;
|
||||
}
|
||||
} else {
|
||||
assert(false && "unexpected SD erase argument");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
1
code/components/esp-tflite-micro
Submodule
1
code/components/esp-tflite-micro
Submodule
Submodule code/components/esp-tflite-micro added at 13f26b8294
1
code/components/esp32-camera
Submodule
1
code/components/esp32-camera
Submodule
Submodule code/components/esp32-camera added at dba8da9898
7
code/components/jomjol_configfile/CMakeLists.txt
Normal file
7
code/components/jomjol_configfile/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES jomjol_logfile)
|
||||
|
||||
|
||||
86
code/components/jomjol_configfile/configFile.cpp
Normal file
86
code/components/jomjol_configfile/configFile.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "Helper.h"
|
||||
#include "configFile.h"
|
||||
#include <esp_log.h>
|
||||
|
||||
#include "../../include/defines.h"
|
||||
|
||||
static const char *TAG = "CONFIG";
|
||||
|
||||
ConfigFile::ConfigFile(std::string filePath)
|
||||
{
|
||||
std::string config = FormatFileName(filePath);
|
||||
pFile = fopen(config.c_str(), "r");
|
||||
}
|
||||
|
||||
ConfigFile::~ConfigFile()
|
||||
{
|
||||
fclose(pFile);
|
||||
}
|
||||
|
||||
bool ConfigFile::isNewParagraph(std::string input)
|
||||
{
|
||||
if ((input[0] == '[') || ((input[0] == ';') && (input[1] == '[')))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ConfigFile::GetNextParagraph(std::string& aktparamgraph, bool &disabled, bool &eof)
|
||||
{
|
||||
while (getNextLine(&aktparamgraph, disabled, eof) && !isNewParagraph(aktparamgraph));
|
||||
|
||||
if (isNewParagraph(aktparamgraph))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ConfigFile::getNextLine(std::string *rt, bool &disabled, bool &eof)
|
||||
{
|
||||
eof = false;
|
||||
char zw[1024] = "";
|
||||
if (pFile == NULL)
|
||||
{
|
||||
*rt = "";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fgets(zw, 1024, pFile))
|
||||
{
|
||||
ESP_LOGD(TAG, "%s", zw);
|
||||
if ((strlen(zw) == 0) && feof(pFile))
|
||||
{
|
||||
*rt = "";
|
||||
eof = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*rt = "";
|
||||
eof = true;
|
||||
return false;
|
||||
}
|
||||
*rt = zw;
|
||||
*rt = trim(*rt);
|
||||
while ((zw[0] == ';' || zw[0] == '#' || (rt->size() == 0)) && !(zw[1] == '['))
|
||||
{
|
||||
fgets(zw, 1024, pFile);
|
||||
ESP_LOGD(TAG, "%s", zw);
|
||||
if (feof(pFile))
|
||||
{
|
||||
*rt = "";
|
||||
eof = true;
|
||||
return false;
|
||||
}
|
||||
*rt = zw;
|
||||
*rt = trim(*rt);
|
||||
}
|
||||
|
||||
disabled = ((*rt)[0] == ';');
|
||||
return true;
|
||||
}
|
||||
23
code/components/jomjol_configfile/configFile.h
Normal file
23
code/components/jomjol_configfile/configFile.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef CONFIGFILE_H
|
||||
#define CONFIGFILE_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class ConfigFile {
|
||||
public:
|
||||
ConfigFile(std::string filePath);
|
||||
~ConfigFile();
|
||||
|
||||
bool isNewParagraph(std::string input);
|
||||
bool GetNextParagraph(std::string& aktparamgraph, bool &disabled, bool &eof);
|
||||
bool getNextLine(std::string* rt, bool &disabled, bool &eof);
|
||||
bool ConfigFileExists(){return pFile;};
|
||||
|
||||
private:
|
||||
FILE* pFile;
|
||||
};
|
||||
|
||||
#endif //CONFIGFILE_H
|
||||
9
code/components/jomjol_controlGPIO/CMakeLists.txt
Normal file
9
code/components/jomjol_controlGPIO/CMakeLists.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||
|
||||
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "." "../../include"
|
||||
REQUIRES esp_http_server jomjol_logfile jomjol_configfile jomjol_mqtt jomjol_flowcontroll)
|
||||
|
||||
|
||||
132
code/components/jomjol_controlGPIO/Color.cpp
Normal file
132
code/components/jomjol_controlGPIO/Color.cpp
Normal file
@@ -0,0 +1,132 @@
|
||||
#include "Color.h"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
|
||||
namespace {
|
||||
|
||||
// Int -> fixed point
|
||||
int up( int x ) { return x * 255; }
|
||||
|
||||
} // namespace
|
||||
|
||||
int iRgbSqrt( int num ) {
|
||||
// https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Binary_numeral_system_.28base_2.29
|
||||
assert( "sqrt input should be non-negative" && num >= 0 );
|
||||
assert( "sqrt input should no exceed 16 bits" && num <= 0xFFFF );
|
||||
int res = 0;
|
||||
int bit = 1 << 16;
|
||||
while ( bit > num )
|
||||
bit >>= 2;
|
||||
while ( bit != 0 ) {
|
||||
if ( num >= res + bit ) {
|
||||
num -= res + bit;
|
||||
res = ( res >> 1 ) + bit;
|
||||
} else
|
||||
res >>= 1;
|
||||
bit >>= 2;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Rgb::Rgb( Hsv y ) {
|
||||
// https://stackoverflow.com/questions/24152553/hsv-to-rgb-and-back-without-floating-point-math-in-python
|
||||
// greyscale
|
||||
if( y.s == 0 ) {
|
||||
r = g = b = y.v;
|
||||
return;
|
||||
}
|
||||
|
||||
const int region = y.h / 43;
|
||||
const int remainder = ( y.h - ( region * 43 ) ) * 6;
|
||||
|
||||
const int p = ( y.v * ( 255 - y.s ) ) >> 8;
|
||||
const int q = ( y.v * ( 255 - ( ( y.s * remainder ) >> 8 ) ) ) >> 8;
|
||||
const int t = ( y.v * ( 255 - ( ( y.s * (255 -remainder ) ) >> 8 ) ) ) >> 8;
|
||||
|
||||
switch( region ) {
|
||||
case 0: r = y.v; g = t; b = p; break;
|
||||
case 1: r = q; g = y.v; b = p; break;
|
||||
case 2: r = p; g = y.v; b = t; break;
|
||||
case 3: r = p; g = q; b = y.v; break;
|
||||
case 4: r = t; g = p; b = y.v; break;
|
||||
case 5: r = y.v; g = p; b = q; break;
|
||||
default: __builtin_trap();
|
||||
}
|
||||
|
||||
a = y.a;
|
||||
}
|
||||
|
||||
Rgb& Rgb::operator=( Hsv hsv ) {
|
||||
Rgb r{ hsv };
|
||||
swap( r );
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rgb Rgb::operator+( Rgb in ) const {
|
||||
auto copy = *this;
|
||||
copy += in;
|
||||
return copy;
|
||||
}
|
||||
|
||||
Rgb& Rgb::operator+=( Rgb in ) {
|
||||
unsigned int red = r + in.r;
|
||||
r = ( red < 255 ) ? red : 255;
|
||||
unsigned int green = g + in.g;
|
||||
g = ( green < 255 ) ? green : 255;
|
||||
unsigned int blue = b + in.b;
|
||||
b = ( blue < 255 ) ? blue : 255;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rgb& Rgb::blend( Rgb in ) {
|
||||
unsigned int inAlpha = in.a * ( 255 - a );
|
||||
unsigned int alpha = a + inAlpha;
|
||||
r = iRgbSqrt( ( ( r * r * a ) + ( in.r * in.r * inAlpha ) ) / alpha );
|
||||
g = iRgbSqrt( ( ( g * g * a ) + ( in.g * in.g * inAlpha ) ) / alpha );
|
||||
b = iRgbSqrt( ( ( b * b * a ) + ( in.b * in.b * inAlpha ) ) / alpha );
|
||||
a = alpha;
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint8_t IRAM_ATTR Rgb::getGrb( int idx ) {
|
||||
switch ( idx ) {
|
||||
case 0: return g;
|
||||
case 1: return r;
|
||||
case 2: return b;
|
||||
}
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
Hsv::Hsv( Rgb r ) {
|
||||
int min = std::min( r.r, std::min( r.g, r.b ) );
|
||||
int max = std::max( r.r, std::max( r.g, r.b ) );
|
||||
int chroma = max - min;
|
||||
|
||||
v = max;
|
||||
if ( chroma == 0 ) {
|
||||
h = s = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
s = up( chroma ) / max;
|
||||
int hh;
|
||||
if ( max == r.r )
|
||||
hh = ( up( int( r.g ) - int( r.b ) ) ) / chroma / 6;
|
||||
else if ( max == r.g )
|
||||
hh = 255 / 3 + ( up( int( r.b ) - int( r.r ) ) ) / chroma / 6;
|
||||
else
|
||||
hh = 2 * 255 / 3 + ( up( int( r.r ) - int( r.g ) ) ) / chroma / 6;
|
||||
|
||||
if ( hh < 0 )
|
||||
hh += 255;
|
||||
h = hh;
|
||||
|
||||
a = r.a;
|
||||
}
|
||||
|
||||
Hsv& Hsv::operator=( Rgb rgb ) {
|
||||
Hsv h{ rgb };
|
||||
swap( h );
|
||||
return *this;
|
||||
}
|
||||
74
code/components/jomjol_controlGPIO/Color.h
Normal file
74
code/components/jomjol_controlGPIO/Color.h
Normal file
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef COLOR_H
|
||||
#define COLOR_H
|
||||
|
||||
#include <cstdint>
|
||||
#include "esp_attr.h"
|
||||
union Hsv;
|
||||
|
||||
union Rgb {
|
||||
struct __attribute__ ((packed)) {
|
||||
uint8_t r, g, b, a;
|
||||
};
|
||||
uint32_t value;
|
||||
|
||||
Rgb( uint8_t r = 0, uint8_t g = 0, uint8_t b = 0, uint8_t a = 255 ) : r( r ), g( g ), b( b ), a( a ) {}
|
||||
Rgb( Hsv c );
|
||||
Rgb& operator=( Rgb rgb ) { swap( rgb ); return *this; }
|
||||
Rgb& operator=( Hsv hsv );
|
||||
Rgb operator+( Rgb in ) const;
|
||||
Rgb& operator+=( Rgb in );
|
||||
bool operator==( Rgb in ) const { return in.value == value; }
|
||||
Rgb& blend( Rgb in );
|
||||
void swap( Rgb& o ) { value = o.value; }
|
||||
void linearize() {
|
||||
r = channelGamma(r);
|
||||
g = channelGamma(g);
|
||||
b = channelGamma(b);
|
||||
}
|
||||
|
||||
uint8_t IRAM_ATTR getGrb( int idx );
|
||||
|
||||
void stretchChannels( uint8_t maxR, uint8_t maxG, uint8_t maxB ) {
|
||||
r = stretch( r, maxR );
|
||||
g = stretch( g, maxG );
|
||||
b = stretch( b, maxB );
|
||||
}
|
||||
|
||||
void stretchChannelsEvenly( uint8_t max ) {
|
||||
stretchChannels( max, max, max );
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t stretch( int value, uint8_t max ) {
|
||||
return ( value * max ) >> 8;
|
||||
}
|
||||
|
||||
uint8_t channelGamma( int channel ) {
|
||||
/* The optimal gamma correction is x^2.8. However, this is expensive to
|
||||
* compute. Therefore, we use x^3 for gamma correction. Also, we add a
|
||||
* bias as the WS2812 LEDs do not turn on for values less than 4. */
|
||||
if (channel == 0)
|
||||
return channel;
|
||||
channel = channel * channel * channel * 251;
|
||||
channel >>= 24;
|
||||
return static_cast< uint8_t >( 4 + channel );
|
||||
}
|
||||
};
|
||||
|
||||
union Hsv {
|
||||
struct __attribute__ ((packed)) {
|
||||
uint8_t h, s, v, a;
|
||||
};
|
||||
uint32_t value;
|
||||
|
||||
Hsv( uint8_t h, uint8_t s = 0, uint8_t v = 0, uint8_t a = 255 ) : h( h ), s( s ), v( v ), a( a ) {}
|
||||
Hsv( Rgb r );
|
||||
Hsv& operator=( Hsv h ) { swap( h ); return *this; }
|
||||
Hsv& operator=( Rgb rgb );
|
||||
bool operator==( Hsv in ) const { return in.value == value; }
|
||||
void swap( Hsv& o ) { value = o.value; }
|
||||
};
|
||||
|
||||
#endif //COLOR_H
|
||||
90
code/components/jomjol_controlGPIO/SmartLeds.cpp
Normal file
90
code/components/jomjol_controlGPIO/SmartLeds.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
#include "SmartLeds.h"
|
||||
|
||||
|
||||
/* PlatformIO 6 (ESP IDF 5) does no longer allow access to RMTMEM,
|
||||
see https://docs.espressif.com/projects/esp-idf/en/latest/esp32/migration-guides/release-5.x/5.0/peripherals.html?highlight=rmtmem#id5
|
||||
As a dirty workaround, we copy the needed structures from rmt_struct.h
|
||||
In the long run, this should be replaced! */
|
||||
typedef struct rmt_item32_s {
|
||||
union {
|
||||
struct {
|
||||
uint32_t duration0 :15;
|
||||
uint32_t level0 :1;
|
||||
uint32_t duration1 :15;
|
||||
uint32_t level1 :1;
|
||||
};
|
||||
uint32_t val;
|
||||
};
|
||||
} rmt_item32_t;
|
||||
|
||||
//Allow access to RMT memory using RMTMEM.chan[0].data32[8]
|
||||
typedef volatile struct rmt_mem_s {
|
||||
struct {
|
||||
rmt_item32_t data32[64];
|
||||
} chan[8];
|
||||
} rmt_mem_t;
|
||||
extern rmt_mem_t RMTMEM;
|
||||
|
||||
|
||||
|
||||
IsrCore SmartLed::_interruptCore = CoreCurrent;
|
||||
intr_handle_t SmartLed::_interruptHandle = NULL;
|
||||
|
||||
SmartLed*& IRAM_ATTR SmartLed::ledForChannel( int channel ) {
|
||||
static SmartLed* table[8] = { nullptr };
|
||||
assert( channel < 8 );
|
||||
return table[ channel ];
|
||||
}
|
||||
|
||||
void IRAM_ATTR SmartLed::interruptHandler(void*) {
|
||||
for (int channel = 0; channel != 8; channel++) {
|
||||
auto self = ledForChannel( channel );
|
||||
|
||||
if ( RMT.int_st.val & (1 << (24 + channel ) ) ) { // tx_thr_event
|
||||
if ( self )
|
||||
self->copyRmtHalfBlock();
|
||||
RMT.int_clr.val |= 1 << ( 24 + channel );
|
||||
} else if ( RMT.int_st.val & ( 1 << (3 * channel ) ) ) { // tx_end
|
||||
if ( self )
|
||||
xSemaphoreGiveFromISR( self->_finishedFlag, nullptr );
|
||||
RMT.int_clr.val |= 1 << ( 3 * channel );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR SmartLed::copyRmtHalfBlock() {
|
||||
int offset = detail::MAX_PULSES * _halfIdx;
|
||||
_halfIdx = !_halfIdx;
|
||||
int len = 3 - _componentPosition + 3 * ( _count - 1 );
|
||||
len = std::min( len, detail::MAX_PULSES / 8 );
|
||||
|
||||
if ( !len ) {
|
||||
for ( int i = 0; i < detail::MAX_PULSES; i++) {
|
||||
RMTMEM.chan[ _channel].data32[i + offset ].val = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int i;
|
||||
for ( i = 0; i != len && _pixelPosition != _count; i++ ) {
|
||||
uint8_t val = _buffer[ _pixelPosition ].getGrb( _componentPosition );
|
||||
for ( int j = 0; j != 8; j++, val <<= 1 ) {
|
||||
int bit = val >> 7;
|
||||
int idx = i * 8 + offset + j;
|
||||
RMTMEM.chan[ _channel ].data32[ idx ].val = _bitToRmt[ bit & 0x01 ].value;
|
||||
}
|
||||
if ( _pixelPosition == _count - 1 && _componentPosition == 2 ) {
|
||||
RMTMEM.chan[ _channel ].data32[ i * 8 + offset + 7 ].duration1 =
|
||||
_timing.TRS / ( detail::RMT_DURATION_NS * detail::DIVIDER );
|
||||
}
|
||||
|
||||
_componentPosition++;
|
||||
if ( _componentPosition == 3 ) {
|
||||
_componentPosition = 0;
|
||||
_pixelPosition++;
|
||||
}
|
||||
}
|
||||
|
||||
for ( i *= 8; i != detail::MAX_PULSES; i++ ) {
|
||||
RMTMEM.chan[ _channel ].data32[ i + offset ].val = 0;
|
||||
}
|
||||
}
|
||||
570
code/components/jomjol_controlGPIO/SmartLeds.h
Normal file
570
code/components/jomjol_controlGPIO/SmartLeds.h
Normal file
@@ -0,0 +1,570 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SMARTLEDS_H
|
||||
#define SMARTLEDS_H
|
||||
|
||||
/*
|
||||
* A C++ driver for the WS2812 LEDs using the RMT peripheral on the ESP32.
|
||||
*
|
||||
* Jan "yaqwsx" Mrázek <email@honzamrazek.cz>
|
||||
*
|
||||
* Based on the work by Martin F. Falatic - https://github.com/FozzTexx/ws2812-demo
|
||||
*/
|
||||
|
||||
/*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
#include "esp_idf_version.h"
|
||||
#if (ESP_IDF_VERSION_MAJOR >= 5)
|
||||
#include "soc/periph_defs.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
#include "soc/gpio_sig_map.h"
|
||||
#include "soc/gpio_periph.h"
|
||||
#include "soc/io_mux_reg.h"
|
||||
#include "esp_rom_gpio.h"
|
||||
#define gpio_pad_select_gpio esp_rom_gpio_pad_select_gpio
|
||||
#define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c)
|
||||
#define gpio_matrix_out(a,b,c,d) esp_rom_gpio_connect_out_signal(a,b,c,d)
|
||||
#define ets_delay_us(a) esp_rom_delay_us(a)
|
||||
#endif
|
||||
|
||||
#if defined ( ARDUINO )
|
||||
extern "C" { // ...someone forgot to put in the includes...
|
||||
#include "esp32-hal.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_ipc.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/periph_ctrl.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "soc/rmt_struct.h"
|
||||
#include <driver/spi_master.h>
|
||||
#include "esp_idf_version.h"
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL( 4, 0, 0 )
|
||||
#include "soc/dport_reg.h"
|
||||
#endif
|
||||
}
|
||||
#elif defined ( ESP_PLATFORM )
|
||||
extern "C" { // ...someone forgot to put in the includes...
|
||||
#include <esp_intr_alloc.h>
|
||||
#include <esp_ipc.h>
|
||||
#include <driver/gpio.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/semphr.h>
|
||||
#include <soc/dport_reg.h>
|
||||
#include <soc/gpio_sig_map.h>
|
||||
#include <soc/rmt_struct.h>
|
||||
#include <driver/spi_master.h>
|
||||
}
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#if (ESP_IDF_VERSION_MAJOR >= 4) && (ESP_IDF_VERSION_MINOR > 1)
|
||||
#include "hal/gpio_ll.h"
|
||||
#else
|
||||
#include "soc/gpio_periph.h"
|
||||
#define esp_rom_delay_us ets_delay_us
|
||||
static inline int gpio_ll_get_level(gpio_dev_t *hw, int gpio_num)
|
||||
{
|
||||
if (gpio_num < 32) {
|
||||
return (hw->in >> gpio_num) & 0x1;
|
||||
} else {
|
||||
return (hw->in1.data >> (gpio_num - 32)) & 0x1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0))
|
||||
#if !(configENABLE_BACKWARD_COMPATIBILITY == 1)
|
||||
#define xSemaphoreHandle SemaphoreHandle_t
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "Color.h"
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct TimingParams {
|
||||
uint32_t T0H;
|
||||
uint32_t T1H;
|
||||
uint32_t T0L;
|
||||
uint32_t T1L;
|
||||
uint32_t TRS;
|
||||
};
|
||||
|
||||
union RmtPulsePair {
|
||||
struct {
|
||||
int duration0:15;
|
||||
int level0:1;
|
||||
int duration1:15;
|
||||
int level1:1;
|
||||
};
|
||||
uint32_t value;
|
||||
};
|
||||
|
||||
static const int DIVIDER = 4; // 8 still seems to work, but timings become marginal
|
||||
static const int MAX_PULSES = 32; // A channel has a 64 "pulse" buffer - we use half per pass
|
||||
static const double RMT_DURATION_NS = 12.5; // minimum time of a single RMT duration based on clock ns
|
||||
|
||||
} // namespace detail
|
||||
|
||||
using LedType = detail::TimingParams;
|
||||
|
||||
static const LedType LED_WS2812 = { 350, 700, 800, 600, 50000 };
|
||||
static const LedType LED_WS2812B = { 400, 850, 850, 400, 50100 };
|
||||
static const LedType LED_SK6812 = { 300, 600, 900, 600, 80000 };
|
||||
static const LedType LED_WS2813 = { 350, 800, 350, 350, 300000 };
|
||||
|
||||
enum BufferType { SingleBuffer = 0, DoubleBuffer };
|
||||
|
||||
enum IsrCore { CoreFirst = 0, CoreSecond = 1, CoreCurrent = 2};
|
||||
|
||||
class SmartLed {
|
||||
public:
|
||||
// The RMT interrupt must not run on the same core as WiFi interrupts, otherwise SmartLeds
|
||||
// can't fill the RMT buffer fast enough, resulting in rendering artifacts.
|
||||
// Usually, that means you have to set isrCore == CoreSecond.
|
||||
//
|
||||
// If you use anything other than CoreCurrent, the FreeRTOS scheduler MUST be already running,
|
||||
// so you can't use it if you define SmartLed as global variable.
|
||||
SmartLed( const LedType& type, int count, int pin, int channel = 0, BufferType doubleBuffer = SingleBuffer, IsrCore isrCore = CoreCurrent)
|
||||
: _timing( type ),
|
||||
_channel( channel ),
|
||||
_count( count ),
|
||||
_firstBuffer( new Rgb[ count ] ),
|
||||
_secondBuffer( doubleBuffer ? new Rgb[ count ] : nullptr ),
|
||||
_finishedFlag( xSemaphoreCreateBinary() )
|
||||
{
|
||||
assert( channel >= 0 && channel < 8 );
|
||||
assert( ledForChannel( channel ) == nullptr );
|
||||
|
||||
xSemaphoreGive( _finishedFlag );
|
||||
|
||||
DPORT_SET_PERI_REG_MASK( DPORT_PERIP_CLK_EN_REG, DPORT_RMT_CLK_EN );
|
||||
DPORT_CLEAR_PERI_REG_MASK( DPORT_PERIP_RST_EN_REG, DPORT_RMT_RST );
|
||||
|
||||
PIN_FUNC_SELECT( GPIO_PIN_MUX_REG[ pin ], 2 );
|
||||
gpio_set_direction( static_cast< gpio_num_t >( pin ), GPIO_MODE_OUTPUT );
|
||||
gpio_matrix_out( static_cast< gpio_num_t >( pin ), RMT_SIG_OUT0_IDX + _channel, 0, 0 );
|
||||
initChannel( _channel );
|
||||
|
||||
RMT.tx_lim_ch[ _channel ].limit = detail::MAX_PULSES;
|
||||
RMT.int_ena.val |= 1 << ( 24 + _channel );
|
||||
RMT.int_ena.val |= 1 << ( 3 * _channel );
|
||||
|
||||
_bitToRmt[ 0 ].level0 = 1;
|
||||
_bitToRmt[ 0 ].level1 = 0;
|
||||
_bitToRmt[ 0 ].duration0 = _timing.T0H / ( detail::RMT_DURATION_NS * detail::DIVIDER );
|
||||
_bitToRmt[ 0 ].duration1 = _timing.T0L / ( detail::RMT_DURATION_NS * detail::DIVIDER );
|
||||
|
||||
_bitToRmt[ 1 ].level0 = 1;
|
||||
_bitToRmt[ 1 ].level1 = 0;
|
||||
_bitToRmt[ 1 ].duration0 = _timing.T1H / ( detail::RMT_DURATION_NS * detail::DIVIDER );
|
||||
_bitToRmt[ 1 ].duration1 = _timing.T1L / ( detail::RMT_DURATION_NS * detail::DIVIDER );
|
||||
|
||||
if ( !anyAlive() ) {
|
||||
_interruptCore = isrCore;
|
||||
if(isrCore != CoreCurrent) {
|
||||
ESP_ERROR_CHECK(esp_ipc_call_blocking(isrCore, registerInterrupt, NULL));
|
||||
} else {
|
||||
registerInterrupt(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
ledForChannel( channel ) = this;
|
||||
}
|
||||
|
||||
~SmartLed() {
|
||||
ledForChannel( _channel ) = nullptr;
|
||||
if ( !anyAlive() ) {
|
||||
if(_interruptCore != CoreCurrent) {
|
||||
ESP_ERROR_CHECK(esp_ipc_call_blocking(_interruptCore, unregisterInterrupt, NULL));
|
||||
} else {
|
||||
unregisterInterrupt(NULL);
|
||||
}
|
||||
}
|
||||
vSemaphoreDelete( _finishedFlag );
|
||||
}
|
||||
|
||||
Rgb& operator[]( int idx ) {
|
||||
return _firstBuffer[ idx ];
|
||||
}
|
||||
|
||||
const Rgb& operator[]( int idx ) const {
|
||||
return _firstBuffer[ idx ];
|
||||
}
|
||||
|
||||
void show() {
|
||||
_buffer = _firstBuffer.get();
|
||||
startTransmission();
|
||||
swapBuffers();
|
||||
}
|
||||
|
||||
bool wait( TickType_t timeout = portMAX_DELAY ) {
|
||||
if( xSemaphoreTake( _finishedFlag, timeout ) == pdTRUE ) {
|
||||
xSemaphoreGive( _finishedFlag );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int size() const {
|
||||
return _count;
|
||||
}
|
||||
|
||||
Rgb *begin() { return _firstBuffer.get(); }
|
||||
const Rgb *begin() const { return _firstBuffer.get(); }
|
||||
const Rgb *cbegin() const { return _firstBuffer.get(); }
|
||||
|
||||
Rgb *end() { return _firstBuffer.get() + _count; }
|
||||
const Rgb *end() const { return _firstBuffer.get() + _count; }
|
||||
const Rgb *cend() const { return _firstBuffer.get() + _count; }
|
||||
|
||||
private:
|
||||
static intr_handle_t _interruptHandle;
|
||||
static IsrCore _interruptCore;
|
||||
|
||||
static void initChannel( int channel ) {
|
||||
RMT.apb_conf.fifo_mask = 1; //enable memory access, instead of FIFO mode.
|
||||
RMT.apb_conf.mem_tx_wrap_en = 1; //wrap around when hitting end of buffer
|
||||
RMT.conf_ch[ channel ].conf0.div_cnt = detail::DIVIDER;
|
||||
RMT.conf_ch[ channel ].conf0.mem_size = 1;
|
||||
RMT.conf_ch[ channel ].conf0.carrier_en = 0;
|
||||
RMT.conf_ch[ channel ].conf0.carrier_out_lv = 1;
|
||||
RMT.conf_ch[ channel ].conf0.mem_pd = 0;
|
||||
|
||||
RMT.conf_ch[ channel ].conf1.rx_en = 0;
|
||||
RMT.conf_ch[ channel ].conf1.mem_owner = 0;
|
||||
RMT.conf_ch[ channel ].conf1.tx_conti_mode = 0; //loop back mode.
|
||||
RMT.conf_ch[ channel ].conf1.ref_always_on = 1; // use apb clock: 80M
|
||||
RMT.conf_ch[ channel ].conf1.idle_out_en = 1;
|
||||
RMT.conf_ch[ channel ].conf1.idle_out_lv = 0;
|
||||
}
|
||||
|
||||
static void registerInterrupt(void *) {
|
||||
ESP_ERROR_CHECK(esp_intr_alloc( ETS_RMT_INTR_SOURCE, 0, interruptHandler, nullptr, &_interruptHandle));
|
||||
}
|
||||
|
||||
static void unregisterInterrupt(void*) {
|
||||
esp_intr_free( _interruptHandle );
|
||||
}
|
||||
|
||||
static SmartLed*& IRAM_ATTR ledForChannel( int channel );
|
||||
static void IRAM_ATTR interruptHandler( void* );
|
||||
|
||||
void IRAM_ATTR copyRmtHalfBlock();
|
||||
|
||||
void swapBuffers() {
|
||||
if ( _secondBuffer )
|
||||
_firstBuffer.swap( _secondBuffer );
|
||||
}
|
||||
|
||||
void startTransmission() {
|
||||
// Invalid use of the library
|
||||
if( xSemaphoreTake( _finishedFlag, 0 ) != pdTRUE )
|
||||
abort();
|
||||
|
||||
_pixelPosition = _componentPosition = _halfIdx = 0;
|
||||
copyRmtHalfBlock();
|
||||
if ( _pixelPosition < _count )
|
||||
copyRmtHalfBlock();
|
||||
|
||||
RMT.conf_ch[ _channel ].conf1.mem_rd_rst = 1;
|
||||
RMT.conf_ch[ _channel ].conf1.tx_start = 1;
|
||||
}
|
||||
|
||||
static bool anyAlive() {
|
||||
for ( int i = 0; i != 8; i++ )
|
||||
if ( ledForChannel( i ) != nullptr ) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
const LedType& _timing;
|
||||
int _channel;
|
||||
detail::RmtPulsePair _bitToRmt[ 2 ];
|
||||
int _count;
|
||||
std::unique_ptr< Rgb[] > _firstBuffer;
|
||||
std::unique_ptr< Rgb[] > _secondBuffer;
|
||||
Rgb *_buffer;
|
||||
|
||||
xSemaphoreHandle _finishedFlag;
|
||||
|
||||
int _pixelPosition;
|
||||
int _componentPosition;
|
||||
int _halfIdx;
|
||||
};
|
||||
|
||||
class Apa102 {
|
||||
public:
|
||||
struct ApaRgb {
|
||||
ApaRgb( uint8_t r = 0, uint8_t g = 0, uint32_t b = 0, uint32_t v = 0xFF )
|
||||
: v( 0xE0 | v ), b( b ), g( g ), r( r )
|
||||
{}
|
||||
|
||||
ApaRgb& operator=( const Rgb& o ) {
|
||||
r = o.r;
|
||||
g = o.g;
|
||||
b = o.b;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ApaRgb& operator=( const Hsv& o ) {
|
||||
*this = Rgb{ o };
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint8_t v, b, g, r;
|
||||
};
|
||||
|
||||
static const int FINAL_FRAME_SIZE = 4;
|
||||
static const int TRANS_COUNT = 2 + 8;
|
||||
|
||||
Apa102( int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer )
|
||||
: _count( count ),
|
||||
_firstBuffer( new ApaRgb[ count ] ),
|
||||
_secondBuffer( doubleBuffer ? new ApaRgb[ count ] : nullptr ),
|
||||
_initFrame( 0 )
|
||||
{
|
||||
spi_bus_config_t buscfg;
|
||||
memset( &buscfg, 0, sizeof( buscfg ) );
|
||||
buscfg.mosi_io_num = datapin;
|
||||
buscfg.miso_io_num = -1;
|
||||
buscfg.sclk_io_num = clkpin;
|
||||
buscfg.quadwp_io_num = -1;
|
||||
buscfg.quadhd_io_num = -1;
|
||||
buscfg.max_transfer_sz = 65535;
|
||||
|
||||
spi_device_interface_config_t devcfg;
|
||||
memset( &devcfg, 0, sizeof( devcfg ) );
|
||||
devcfg.clock_speed_hz = 1000000;
|
||||
devcfg.mode = 0;
|
||||
devcfg.spics_io_num = -1;
|
||||
devcfg.queue_size = TRANS_COUNT;
|
||||
devcfg.pre_cb = nullptr;
|
||||
|
||||
auto ret = spi_bus_initialize( HSPI_HOST, &buscfg, 1 );
|
||||
assert( ret == ESP_OK );
|
||||
|
||||
ret = spi_bus_add_device( HSPI_HOST, &devcfg, &_spi );
|
||||
assert( ret == ESP_OK );
|
||||
|
||||
std::fill_n( _finalFrame, FINAL_FRAME_SIZE, 0xFFFFFFFF );
|
||||
}
|
||||
|
||||
~Apa102() {
|
||||
// ToDo
|
||||
}
|
||||
|
||||
ApaRgb& operator[]( int idx ) {
|
||||
return _firstBuffer[ idx ];
|
||||
}
|
||||
|
||||
const ApaRgb& operator[]( int idx ) const {
|
||||
return _firstBuffer[ idx ];
|
||||
}
|
||||
|
||||
void show() {
|
||||
_buffer = _firstBuffer.get();
|
||||
startTransmission();
|
||||
swapBuffers();
|
||||
}
|
||||
|
||||
void wait() {
|
||||
for ( int i = 0; i != _transCount; i++ ) {
|
||||
spi_transaction_t *t;
|
||||
spi_device_get_trans_result( _spi, &t, portMAX_DELAY );
|
||||
}
|
||||
}
|
||||
private:
|
||||
void swapBuffers() {
|
||||
if ( _secondBuffer )
|
||||
_firstBuffer.swap( _secondBuffer );
|
||||
}
|
||||
|
||||
void startTransmission() {
|
||||
for ( int i = 0; i != TRANS_COUNT; i++ ) {
|
||||
_transactions[ i ].cmd = 0;
|
||||
_transactions[ i ].addr = 0;
|
||||
_transactions[ i ].flags = 0;
|
||||
_transactions[ i ].rxlength = 0;
|
||||
_transactions[ i ].rx_buffer = nullptr;
|
||||
}
|
||||
// Init frame
|
||||
_transactions[ 0 ].length = 32;
|
||||
_transactions[ 0 ].tx_buffer = &_initFrame;
|
||||
spi_device_queue_trans( _spi, _transactions + 0, portMAX_DELAY );
|
||||
// Data
|
||||
_transactions[ 1 ].length = 32 * _count;
|
||||
_transactions[ 1 ].tx_buffer = _buffer;
|
||||
spi_device_queue_trans( _spi, _transactions + 1, portMAX_DELAY );
|
||||
_transCount = 2;
|
||||
// End frame
|
||||
for ( int i = 0; i != 1 + _count / 32 / FINAL_FRAME_SIZE; i++ ) {
|
||||
_transactions[ 2 + i ].length = 32 * FINAL_FRAME_SIZE;
|
||||
_transactions[ 2 + i ].tx_buffer = _finalFrame;
|
||||
spi_device_queue_trans( _spi, _transactions + 2 + i, portMAX_DELAY );
|
||||
_transCount++;
|
||||
}
|
||||
}
|
||||
|
||||
spi_device_handle_t _spi;
|
||||
int _count;
|
||||
std::unique_ptr< ApaRgb[] > _firstBuffer, _secondBuffer;
|
||||
ApaRgb *_buffer;
|
||||
|
||||
spi_transaction_t _transactions[ TRANS_COUNT ];
|
||||
int _transCount;
|
||||
|
||||
uint32_t _initFrame;
|
||||
uint32_t _finalFrame[ FINAL_FRAME_SIZE ];
|
||||
};
|
||||
|
||||
class LDP8806 {
|
||||
public:
|
||||
struct LDP8806_GRB {
|
||||
|
||||
LDP8806_GRB( uint8_t g_7bit = 0, uint8_t r_7bit = 0, uint32_t b_7bit = 0 )
|
||||
: g( g_7bit ), r( r_7bit ), b( b_7bit )
|
||||
{
|
||||
}
|
||||
|
||||
LDP8806_GRB& operator=( const Rgb& o ) {
|
||||
//Convert 8->7bit colour
|
||||
r = ( o.r * 127 / 256 ) | 0x80;
|
||||
g = ( o.g * 127 / 256 ) | 0x80;
|
||||
b = ( o.b * 127 / 256 ) | 0x80;
|
||||
return *this;
|
||||
}
|
||||
|
||||
LDP8806_GRB& operator=( const Hsv& o ) {
|
||||
*this = Rgb{ o };
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint8_t g, r, b;
|
||||
};
|
||||
|
||||
static const int LED_FRAME_SIZE_BYTES = sizeof( LDP8806_GRB );
|
||||
static const int LATCH_FRAME_SIZE_BYTES = 3;
|
||||
static const int TRANS_COUNT_MAX = 20;//Arbitrary, supports up to 600 LED
|
||||
|
||||
LDP8806( int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer, uint32_t clock_speed_hz = 2000000 )
|
||||
: _count( count ),
|
||||
_firstBuffer( new LDP8806_GRB[ count ] ),
|
||||
_secondBuffer( doubleBuffer ? new LDP8806_GRB[ count ] : nullptr ),
|
||||
// one 'latch'/start-of-data mark frame for every 32 leds
|
||||
_latchFrames( ( count + 31 ) / 32 )
|
||||
{
|
||||
spi_bus_config_t buscfg;
|
||||
memset( &buscfg, 0, sizeof( buscfg ) );
|
||||
buscfg.mosi_io_num = datapin;
|
||||
buscfg.miso_io_num = -1;
|
||||
buscfg.sclk_io_num = clkpin;
|
||||
buscfg.quadwp_io_num = -1;
|
||||
buscfg.quadhd_io_num = -1;
|
||||
buscfg.max_transfer_sz = 65535;
|
||||
|
||||
spi_device_interface_config_t devcfg;
|
||||
memset( &devcfg, 0, sizeof( devcfg ) );
|
||||
devcfg.clock_speed_hz = clock_speed_hz;
|
||||
devcfg.mode = 0;
|
||||
devcfg.spics_io_num = -1;
|
||||
devcfg.queue_size = TRANS_COUNT_MAX;
|
||||
devcfg.pre_cb = nullptr;
|
||||
|
||||
auto ret = spi_bus_initialize( HSPI_HOST, &buscfg, 1 );
|
||||
assert( ret == ESP_OK );
|
||||
|
||||
ret = spi_bus_add_device( HSPI_HOST, &devcfg, &_spi );
|
||||
assert( ret == ESP_OK );
|
||||
|
||||
std::fill_n( _latchBuffer, LATCH_FRAME_SIZE_BYTES, 0x0 );
|
||||
}
|
||||
|
||||
~LDP8806() {
|
||||
// noop
|
||||
}
|
||||
|
||||
LDP8806_GRB& operator[]( int idx ) {
|
||||
return _firstBuffer[ idx ];
|
||||
}
|
||||
|
||||
const LDP8806_GRB& operator[]( int idx ) const {
|
||||
return _firstBuffer[ idx ];
|
||||
}
|
||||
|
||||
void show() {
|
||||
_buffer = _firstBuffer.get();
|
||||
startTransmission();
|
||||
swapBuffers();
|
||||
}
|
||||
|
||||
void wait() {
|
||||
while ( _transCount-- ) {
|
||||
spi_transaction_t *t;
|
||||
spi_device_get_trans_result( _spi, &t, portMAX_DELAY );
|
||||
}
|
||||
}
|
||||
private:
|
||||
void swapBuffers() {
|
||||
if ( _secondBuffer )
|
||||
_firstBuffer.swap( _secondBuffer );
|
||||
}
|
||||
|
||||
void startTransmission() {
|
||||
_transCount = 0;
|
||||
for ( int i = 0; i != TRANS_COUNT_MAX; i++ ) {
|
||||
_transactions[ i ].cmd = 0;
|
||||
_transactions[ i ].addr = 0;
|
||||
_transactions[ i ].flags = 0;
|
||||
_transactions[ i ].rxlength = 0;
|
||||
_transactions[ i ].rx_buffer = nullptr;
|
||||
}
|
||||
// LED Data
|
||||
_transactions[ 0 ].length = ( LED_FRAME_SIZE_BYTES * 8 ) * _count;
|
||||
_transactions[ 0 ].tx_buffer = _buffer;
|
||||
spi_device_queue_trans( _spi, _transactions + _transCount, portMAX_DELAY );
|
||||
_transCount++;
|
||||
|
||||
// 'latch'/start-of-data marker frames
|
||||
for ( int i = 0; i < _latchFrames; i++ ) {
|
||||
_transactions[ _transCount ].length = ( LATCH_FRAME_SIZE_BYTES * 8 );
|
||||
_transactions[ _transCount ].tx_buffer = _latchBuffer;
|
||||
spi_device_queue_trans( _spi, _transactions + _transCount, portMAX_DELAY );
|
||||
_transCount++;
|
||||
}
|
||||
}
|
||||
|
||||
spi_device_handle_t _spi;
|
||||
int _count;
|
||||
std::unique_ptr< LDP8806_GRB[] > _firstBuffer, _secondBuffer;
|
||||
LDP8806_GRB *_buffer;
|
||||
|
||||
spi_transaction_t _transactions[ TRANS_COUNT_MAX ];
|
||||
int _transCount;
|
||||
|
||||
int _latchFrames;
|
||||
uint8_t _latchBuffer[ LATCH_FRAME_SIZE_BYTES ];
|
||||
};
|
||||
|
||||
#endif //SMARTLEDS_H
|
||||
705
code/components/jomjol_controlGPIO/server_GPIO.cpp
Normal file
705
code/components/jomjol_controlGPIO/server_GPIO.cpp
Normal file
@@ -0,0 +1,705 @@
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include "string.h"
|
||||
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_system.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <vector>
|
||||
|
||||
#include "../../include/defines.h"
|
||||
|
||||
#include "server_GPIO.h"
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
#include "configFile.h"
|
||||
#include "Helper.h"
|
||||
|
||||
#ifdef ENABLE_MQTT
|
||||
#include "interface_mqtt.h"
|
||||
#include "server_mqtt.h"
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
|
||||
static const char *TAG = "GPIO";
|
||||
QueueHandle_t gpio_queue_handle = NULL;
|
||||
|
||||
GpioPin::GpioPin(gpio_num_t gpio, const char* name, gpio_pin_mode_t mode, gpio_int_type_t interruptType, uint8_t dutyResolution, std::string mqttTopic, bool httpEnable)
|
||||
{
|
||||
_gpio = gpio;
|
||||
_name = name;
|
||||
_mode = mode;
|
||||
_interruptType = interruptType;
|
||||
_mqttTopic = mqttTopic;
|
||||
}
|
||||
|
||||
GpioPin::~GpioPin()
|
||||
{
|
||||
ESP_LOGD(TAG,"reset GPIO pin %d", _gpio);
|
||||
if (_interruptType != GPIO_INTR_DISABLE) {
|
||||
//hook isr handler for specific gpio pin
|
||||
gpio_isr_handler_remove(_gpio);
|
||||
}
|
||||
gpio_reset_pin(_gpio);
|
||||
}
|
||||
|
||||
static void IRAM_ATTR gpio_isr_handler(void* arg)
|
||||
{
|
||||
GpioResult gpioResult;
|
||||
gpioResult.gpio = *(gpio_num_t*) arg;
|
||||
gpioResult.value = gpio_get_level(gpioResult.gpio);
|
||||
BaseType_t ContextSwitchRequest = pdFALSE;
|
||||
|
||||
xQueueSendToBackFromISR(gpio_queue_handle,(void*)&gpioResult,&ContextSwitchRequest);
|
||||
|
||||
if(ContextSwitchRequest){
|
||||
taskYIELD();
|
||||
}
|
||||
}
|
||||
|
||||
static void gpioHandlerTask(void *arg) {
|
||||
ESP_LOGD(TAG,"start interrupt task");
|
||||
while(1){
|
||||
if(uxQueueMessagesWaiting(gpio_queue_handle)){
|
||||
while(uxQueueMessagesWaiting(gpio_queue_handle)){
|
||||
GpioResult gpioResult;
|
||||
xQueueReceive(gpio_queue_handle,(void*)&gpioResult,10);
|
||||
ESP_LOGD(TAG,"gpio: %d state: %d", gpioResult.gpio, gpioResult.value);
|
||||
((GpioHandler*)arg)->gpioInterrupt(&gpioResult);
|
||||
}
|
||||
}
|
||||
|
||||
((GpioHandler*)arg)->taskHandler();
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
}
|
||||
|
||||
void GpioPin::gpioInterrupt(int value) {
|
||||
#ifdef ENABLE_MQTT
|
||||
if (_mqttTopic.compare("") != 0) {
|
||||
ESP_LOGD(TAG, "gpioInterrupt %s %d", _mqttTopic.c_str(), value);
|
||||
|
||||
MQTTPublish(_mqttTopic, value ? "true" : "false", 1);
|
||||
}
|
||||
#endif //ENABLE_MQTT
|
||||
currentState = value;
|
||||
}
|
||||
|
||||
void GpioPin::init()
|
||||
{
|
||||
gpio_config_t io_conf;
|
||||
//set interrupt
|
||||
io_conf.intr_type = _interruptType;
|
||||
//set as output mode
|
||||
io_conf.mode = (_mode == GPIO_PIN_MODE_OUTPUT) || (_mode == GPIO_PIN_MODE_BUILT_IN_FLASH_LED) ? gpio_mode_t::GPIO_MODE_OUTPUT : gpio_mode_t::GPIO_MODE_INPUT;
|
||||
//bit mask of the pins that you want to set,e.g.GPIO18/19
|
||||
io_conf.pin_bit_mask = (1ULL << _gpio);
|
||||
//set pull-down mode
|
||||
io_conf.pull_down_en = _mode == GPIO_PIN_MODE_INPUT_PULLDOWN ? gpio_pulldown_t::GPIO_PULLDOWN_ENABLE : gpio_pulldown_t::GPIO_PULLDOWN_DISABLE;
|
||||
//set pull-up mode
|
||||
io_conf.pull_up_en = _mode == GPIO_PIN_MODE_INPUT_PULLDOWN ? gpio_pullup_t::GPIO_PULLUP_ENABLE : gpio_pullup_t::GPIO_PULLUP_DISABLE;
|
||||
//configure GPIO with the given settings
|
||||
gpio_config(&io_conf);
|
||||
|
||||
// if (_interruptType != GPIO_INTR_DISABLE) { // ohne GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X, wenn das genutzt wird, dann soll auch der Handler hier nicht initialisiert werden, da das dann über SmartLED erfolgt.
|
||||
if ((_interruptType != GPIO_INTR_DISABLE) && (_interruptType != GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X)) {
|
||||
//hook isr handler for specific gpio pin
|
||||
ESP_LOGD(TAG, "GpioPin::init add isr handler for GPIO %d", _gpio);
|
||||
gpio_isr_handler_add(_gpio, gpio_isr_handler, (void*)&_gpio);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_MQTT
|
||||
if ((_mqttTopic.compare("") != 0) && ((_mode == GPIO_PIN_MODE_OUTPUT) || (_mode == GPIO_PIN_MODE_OUTPUT_PWM) || (_mode == GPIO_PIN_MODE_BUILT_IN_FLASH_LED))) {
|
||||
std::function<bool(std::string, char*, int)> f = std::bind(&GpioPin::handleMQTT, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
|
||||
MQTTregisterSubscribeFunction(_mqttTopic, f);
|
||||
}
|
||||
#endif //ENABLE_MQTT
|
||||
}
|
||||
|
||||
bool GpioPin::getValue(std::string* errorText)
|
||||
{
|
||||
if ((_mode != GPIO_PIN_MODE_INPUT) && (_mode != GPIO_PIN_MODE_INPUT_PULLUP) && (_mode != GPIO_PIN_MODE_INPUT_PULLDOWN)) {
|
||||
(*errorText) = "GPIO is not in input mode";
|
||||
}
|
||||
|
||||
return gpio_get_level(_gpio) == 1;
|
||||
}
|
||||
|
||||
void GpioPin::setValue(bool value, gpio_set_source setSource, std::string* errorText)
|
||||
{
|
||||
ESP_LOGD(TAG, "GpioPin::setValue %d", value);
|
||||
|
||||
if ((_mode != GPIO_PIN_MODE_OUTPUT) && (_mode != GPIO_PIN_MODE_OUTPUT_PWM) && (_mode != GPIO_PIN_MODE_BUILT_IN_FLASH_LED)) {
|
||||
(*errorText) = "GPIO is not in output mode";
|
||||
} else {
|
||||
gpio_set_level(_gpio, value);
|
||||
|
||||
#ifdef ENABLE_MQTT
|
||||
if ((_mqttTopic.compare("") != 0) && (setSource != GPIO_SET_SOURCE_MQTT)) {
|
||||
MQTTPublish(_mqttTopic, value ? "true" : "false", 1);
|
||||
}
|
||||
#endif //ENABLE_MQTT
|
||||
}
|
||||
}
|
||||
|
||||
void GpioPin::publishState() {
|
||||
int newState = gpio_get_level(_gpio);
|
||||
if (newState != currentState) {
|
||||
ESP_LOGD(TAG,"publish state of GPIO %d new state %d", _gpio, newState);
|
||||
#ifdef ENABLE_MQTT
|
||||
if (_mqttTopic.compare("") != 0)
|
||||
MQTTPublish(_mqttTopic, newState ? "true" : "false", 1);
|
||||
#endif //ENABLE_MQTT
|
||||
currentState = newState;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_MQTT
|
||||
bool GpioPin::handleMQTT(std::string, char* data, int data_len) {
|
||||
ESP_LOGD(TAG, "GpioPin::handleMQTT data %.*s", data_len, data);
|
||||
|
||||
std::string dataStr(data, data_len);
|
||||
dataStr = toLower(dataStr);
|
||||
std::string errorText = "";
|
||||
if ((dataStr == "true") || (dataStr == "1")) {
|
||||
setValue(true, GPIO_SET_SOURCE_MQTT, &errorText);
|
||||
} else if ((dataStr == "false") || (dataStr == "0")) {
|
||||
setValue(false, GPIO_SET_SOURCE_MQTT, &errorText);
|
||||
} else {
|
||||
errorText = "wrong value ";
|
||||
errorText.append(data, data_len);
|
||||
}
|
||||
|
||||
if (errorText != "") {
|
||||
ESP_LOGE(TAG, "%s", errorText.c_str());
|
||||
}
|
||||
|
||||
return (errorText == "");
|
||||
}
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
esp_err_t callHandleHttpRequest(httpd_req_t *req)
|
||||
{
|
||||
ESP_LOGD(TAG,"callHandleHttpRequest");
|
||||
|
||||
GpioHandler *gpioHandler = (GpioHandler*)req->user_ctx;
|
||||
return gpioHandler->handleHttpRequest(req);
|
||||
}
|
||||
|
||||
void taskGpioHandler(void *pvParameter)
|
||||
{
|
||||
ESP_LOGD(TAG,"taskGpioHandler");
|
||||
((GpioHandler*)pvParameter)->init();
|
||||
}
|
||||
|
||||
GpioHandler::GpioHandler(std::string configFile, httpd_handle_t httpServer)
|
||||
{
|
||||
ESP_LOGI(TAG,"start GpioHandler");
|
||||
_configFile = configFile;
|
||||
_httpServer = httpServer;
|
||||
|
||||
ESP_LOGI(TAG, "register GPIO Uri");
|
||||
registerGpioUri();
|
||||
}
|
||||
|
||||
GpioHandler::~GpioHandler() {
|
||||
if (gpioMap != NULL) {
|
||||
clear();
|
||||
delete gpioMap;
|
||||
}
|
||||
}
|
||||
|
||||
void GpioHandler::init()
|
||||
{
|
||||
// TickType_t xDelay = 60000 / portTICK_PERIOD_MS;
|
||||
// ESP_LOGD(TAG, "wait before start %ldms", (long) xDelay);
|
||||
// vTaskDelay( xDelay );
|
||||
|
||||
ESP_LOGD(TAG, "*************** Start GPIOHandler_Init *****************");
|
||||
|
||||
if (gpioMap == NULL) {
|
||||
gpioMap = new std::map<gpio_num_t, GpioPin*>();
|
||||
} else {
|
||||
clear();
|
||||
}
|
||||
|
||||
|
||||
ESP_LOGI(TAG, "read GPIO config and init GPIO");
|
||||
if (!readConfig()) {
|
||||
clear();
|
||||
delete gpioMap;
|
||||
gpioMap = NULL;
|
||||
ESP_LOGI(TAG, "GPIO init completed, handler is disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for(std::map<gpio_num_t, GpioPin*>::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) {
|
||||
it->second->init();
|
||||
}
|
||||
|
||||
#ifdef ENABLE_MQTT
|
||||
std::function<void()> f = std::bind(&GpioHandler::handleMQTTconnect, this);
|
||||
MQTTregisterConnectFunction("gpio-handler", f);
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
if (xHandleTaskGpio == NULL) {
|
||||
gpio_queue_handle = xQueueCreate(10,sizeof(GpioResult));
|
||||
BaseType_t xReturned = xTaskCreate(&gpioHandlerTask, "gpio_int", 3 * 1024, (void *)this, tskIDLE_PRIORITY + 4, &xHandleTaskGpio);
|
||||
if(xReturned == pdPASS ) {
|
||||
ESP_LOGD(TAG, "xHandletaskGpioHandler started");
|
||||
} else {
|
||||
ESP_LOGD(TAG, "xHandletaskGpioHandler not started %d ", (int)xHandleTaskGpio);
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "GPIO init completed, is enabled");
|
||||
}
|
||||
|
||||
void GpioHandler::taskHandler() {
|
||||
if (gpioMap != NULL) {
|
||||
for(std::map<gpio_num_t, GpioPin*>::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) {
|
||||
if ((it->second->getInterruptType() == GPIO_INTR_DISABLE))
|
||||
it->second->publishState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_MQTT
|
||||
void GpioHandler::handleMQTTconnect()
|
||||
{
|
||||
if (gpioMap != NULL) {
|
||||
for(std::map<gpio_num_t, GpioPin*>::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) {
|
||||
if ((it->second->getMode() == GPIO_PIN_MODE_INPUT) || (it->second->getMode() == GPIO_PIN_MODE_INPUT_PULLDOWN) || (it->second->getMode() == GPIO_PIN_MODE_INPUT_PULLUP))
|
||||
it->second->publishState();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
void GpioHandler::deinit() {
|
||||
#ifdef ENABLE_MQTT
|
||||
MQTTunregisterConnectFunction("gpio-handler");
|
||||
#endif //ENABLE_MQTT
|
||||
clear();
|
||||
if (xHandleTaskGpio != NULL) {
|
||||
vTaskDelete(xHandleTaskGpio);
|
||||
xHandleTaskGpio = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void GpioHandler::gpioInterrupt(GpioResult* gpioResult) {
|
||||
if ((gpioMap != NULL) && (gpioMap->find(gpioResult->gpio) != gpioMap->end())) {
|
||||
(*gpioMap)[gpioResult->gpio]->gpioInterrupt(gpioResult->value);
|
||||
}
|
||||
}
|
||||
|
||||
bool GpioHandler::readConfig()
|
||||
{
|
||||
if (!gpioMap->empty())
|
||||
clear();
|
||||
|
||||
ConfigFile configFile = ConfigFile(_configFile);
|
||||
|
||||
std::vector<std::string> splitted;
|
||||
std::string line = "";
|
||||
bool disabledLine = false;
|
||||
bool eof = false;
|
||||
gpio_num_t gpioExtLED = (gpio_num_t) 0;
|
||||
|
||||
// ESP_LOGD(TAG, "readConfig - Start 1");
|
||||
|
||||
while ((!configFile.GetNextParagraph(line, disabledLine, eof) || (line.compare("[GPIO]") != 0)) && !eof) {}
|
||||
if (eof)
|
||||
return false;
|
||||
|
||||
// ESP_LOGD(TAG, "readConfig - Start 2 line: %s, disabbledLine: %d", line.c_str(), (int) disabledLine);
|
||||
|
||||
|
||||
_isEnabled = !disabledLine;
|
||||
|
||||
if (!_isEnabled)
|
||||
return false;
|
||||
|
||||
// ESP_LOGD(TAG, "readConfig - Start 3");
|
||||
|
||||
#ifdef ENABLE_MQTT
|
||||
// std::string mainTopicMQTT = "";
|
||||
std::string mainTopicMQTT = mqttServer_getMainTopic();
|
||||
if (mainTopicMQTT.length() > 0)
|
||||
{
|
||||
mainTopicMQTT = mainTopicMQTT + "/GPIO";
|
||||
ESP_LOGD(TAG, "MAINTOPICMQTT found");
|
||||
}
|
||||
#endif // ENABLE_MQTT
|
||||
bool registerISR = false;
|
||||
while (configFile.getNextLine(&line, disabledLine, eof) && !configFile.isNewParagraph(line))
|
||||
{
|
||||
splitted = ZerlegeZeile(line);
|
||||
// const std::regex pieces_regex("IO([0-9]{1,2})");
|
||||
// std::smatch pieces_match;
|
||||
// if (std::regex_match(splitted[0], pieces_match, pieces_regex) && (pieces_match.size() == 2))
|
||||
// {
|
||||
// std::string gpioStr = pieces_match[1];
|
||||
ESP_LOGD(TAG, "conf param %s", toUpper(splitted[0]).c_str());
|
||||
if (toUpper(splitted[0]) == "MAINTOPICMQTT") {
|
||||
// ESP_LOGD(TAG, "MAINTOPICMQTT found");
|
||||
// mainTopicMQTT = splitted[1];
|
||||
} else if ((splitted[0].rfind("IO", 0) == 0) && (splitted.size() >= 6))
|
||||
{
|
||||
ESP_LOGI(TAG,"Enable GP%s in %s mode", splitted[0].c_str(), splitted[1].c_str());
|
||||
std::string gpioStr = splitted[0].substr(2, 2);
|
||||
gpio_num_t gpioNr = (gpio_num_t)atoi(gpioStr.c_str());
|
||||
gpio_pin_mode_t pinMode = resolvePinMode(toLower(splitted[1]));
|
||||
gpio_int_type_t intType = resolveIntType(toLower(splitted[2]));
|
||||
uint16_t dutyResolution = (uint8_t)atoi(splitted[3].c_str());
|
||||
#ifdef ENABLE_MQTT
|
||||
bool mqttEnabled = (toLower(splitted[4]) == "true");
|
||||
#endif // ENABLE_MQTT
|
||||
bool httpEnabled = (toLower(splitted[5]) == "true");
|
||||
char gpioName[100];
|
||||
if (splitted.size() >= 7) {
|
||||
strcpy(gpioName, trim(splitted[6]).c_str());
|
||||
} else {
|
||||
sprintf(gpioName, "GPIO%d", gpioNr);
|
||||
}
|
||||
#ifdef ENABLE_MQTT
|
||||
std::string mqttTopic = mqttEnabled ? (mainTopicMQTT + "/" + gpioName) : "";
|
||||
#else // ENABLE_MQTT
|
||||
std::string mqttTopic = "";
|
||||
#endif // ENABLE_MQTT
|
||||
GpioPin* gpioPin = new GpioPin(gpioNr, gpioName, pinMode, intType,dutyResolution, mqttTopic, httpEnabled);
|
||||
(*gpioMap)[gpioNr] = gpioPin;
|
||||
|
||||
if (pinMode == GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X)
|
||||
{
|
||||
ESP_LOGD(TAG, "Set WS2812 to GPIO %d", gpioNr);
|
||||
gpioExtLED = gpioNr;
|
||||
}
|
||||
|
||||
if (intType != GPIO_INTR_DISABLE) {
|
||||
registerISR = true;
|
||||
}
|
||||
}
|
||||
if (toUpper(splitted[0]) == "LEDNUMBERS")
|
||||
{
|
||||
LEDNumbers = stoi(splitted[1]);
|
||||
}
|
||||
if (toUpper(splitted[0]) == "LEDCOLOR")
|
||||
{
|
||||
uint8_t _r, _g, _b;
|
||||
_r = stoi(splitted[1]);
|
||||
_g = stoi(splitted[2]);
|
||||
_b = stoi(splitted[3]);
|
||||
|
||||
LEDColor = Rgb{_r, _g, _b};
|
||||
}
|
||||
if (toUpper(splitted[0]) == "LEDTYPE")
|
||||
{
|
||||
if (splitted[1] == "WS2812")
|
||||
LEDType = LED_WS2812;
|
||||
if (splitted[1] == "WS2812B")
|
||||
LEDType = LED_WS2812B;
|
||||
if (splitted[1] == "SK6812")
|
||||
LEDType = LED_SK6812;
|
||||
if (splitted[1] == "WS2813")
|
||||
LEDType = LED_WS2813;
|
||||
}
|
||||
}
|
||||
|
||||
if (registerISR) {
|
||||
//install gpio isr service
|
||||
gpio_install_isr_service(ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM);
|
||||
}
|
||||
|
||||
if (gpioExtLED > 0)
|
||||
{
|
||||
// LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Startsequence 06"); // Nremove
|
||||
// vTaskDelay( xDelay );
|
||||
// xDelay = 5000 / portTICK_PERIOD_MS;
|
||||
// ESP_LOGD(TAG, "main: sleep for: %ldms", (long) xDelay);
|
||||
|
||||
// SmartLed leds( LED_WS2812, 2, GPIO_NUM_12, 0, DoubleBuffer );
|
||||
|
||||
|
||||
// leds[ 0 ] = Rgb{ 255, 0, 0 };
|
||||
// leds[ 1 ] = Rgb{ 255, 255, 255 };
|
||||
// leds.show();
|
||||
// SmartLed leds = new SmartLed(LEDType, LEDNumbers, gpioExtLED, 0, DoubleBuffer);
|
||||
// _SmartLED = new SmartLed( LED_WS2812, 2, GPIO_NUM_12, 0, DoubleBuffer );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GpioHandler::clear()
|
||||
{
|
||||
ESP_LOGD(TAG, "GpioHandler::clear");
|
||||
|
||||
if (gpioMap != NULL) {
|
||||
for(std::map<gpio_num_t, GpioPin*>::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) {
|
||||
delete it->second;
|
||||
}
|
||||
gpioMap->clear();
|
||||
}
|
||||
|
||||
// gpio_uninstall_isr_service(); can't uninstall, isr service is used by camera
|
||||
}
|
||||
|
||||
void GpioHandler::registerGpioUri()
|
||||
{
|
||||
ESP_LOGI(TAG, "server_GPIO - Registering URI handlers");
|
||||
|
||||
httpd_uri_t camuri = { };
|
||||
camuri.method = HTTP_GET;
|
||||
camuri.uri = "/GPIO";
|
||||
camuri.handler = callHandleHttpRequest;
|
||||
camuri.user_ctx = (void*)this;
|
||||
httpd_register_uri_handler(_httpServer, &camuri);
|
||||
}
|
||||
|
||||
esp_err_t GpioHandler::handleHttpRequest(httpd_req_t *req)
|
||||
{
|
||||
ESP_LOGD(TAG, "handleHttpRequest");
|
||||
|
||||
if (gpioMap == NULL) {
|
||||
std::string resp_str = "GPIO handler not initialized";
|
||||
httpd_resp_send(req, resp_str.c_str(), resp_str.length());
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_switch_GPIO - Start");
|
||||
#endif
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "handler_switch_GPIO");
|
||||
char _query[200];
|
||||
char _valueGPIO[30];
|
||||
char _valueStatus[30];
|
||||
std::string gpio, status;
|
||||
|
||||
if (httpd_req_get_url_query_str(req, _query, 200) == ESP_OK) {
|
||||
ESP_LOGD(TAG, "Query: %s", _query);
|
||||
|
||||
if (httpd_query_key_value(_query, "GPIO", _valueGPIO, 30) == ESP_OK)
|
||||
{
|
||||
ESP_LOGD(TAG, "GPIO is found %s", _valueGPIO);
|
||||
gpio = std::string(_valueGPIO);
|
||||
} else {
|
||||
std::string resp_str = "GPIO No is not defined";
|
||||
httpd_resp_send(req, resp_str.c_str(), resp_str.length());
|
||||
return ESP_OK;
|
||||
}
|
||||
if (httpd_query_key_value(_query, "Status", _valueStatus, 30) == ESP_OK)
|
||||
{
|
||||
ESP_LOGD(TAG, "Status is found %s", _valueStatus);
|
||||
status = std::string(_valueStatus);
|
||||
}
|
||||
} else {
|
||||
const char* resp_str = "Error in call. Use /GPIO?GPIO=12&Status=high";
|
||||
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
status = toUpper(status);
|
||||
if ((status != "HIGH") && (status != "LOW") && (status != "TRUE") && (status != "FALSE") && (status != "0") && (status != "1") && (status != ""))
|
||||
{
|
||||
std::string zw = "Status not valid: " + status;
|
||||
httpd_resp_sendstr_chunk(req, zw.c_str());
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
int gpionum = stoi(gpio);
|
||||
|
||||
// frei: 16; 12-15; 2; 4 // nur 12 und 13 funktionieren 2: reboot, 4: BlitzLED, 15: PSRAM, 14/15: DMA für SDKarte ???
|
||||
gpio_num_t gpio_num = resolvePinNr(gpionum);
|
||||
if (gpio_num == GPIO_NUM_NC)
|
||||
{
|
||||
std::string zw = "GPIO" + std::to_string(gpionum) + " unsupported - only 12 & 13 free";
|
||||
httpd_resp_sendstr_chunk(req, zw.c_str());
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
if (gpioMap->count(gpio_num) == 0) {
|
||||
char resp_str [30];
|
||||
sprintf(resp_str, "GPIO%d is not registred", gpio_num);
|
||||
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
if (status == "")
|
||||
{
|
||||
std::string resp_str = "";
|
||||
status = (*gpioMap)[gpio_num]->getValue(&resp_str) ? "HIGH" : "LOW";
|
||||
if (resp_str == "") {
|
||||
resp_str = status;
|
||||
}
|
||||
httpd_resp_sendstr_chunk(req, resp_str.c_str());
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string resp_str = "";
|
||||
(*gpioMap)[gpio_num]->setValue((status == "HIGH") || (status == "TRUE") || (status == "1"), GPIO_SET_SOURCE_HTTP, &resp_str);
|
||||
if (resp_str == "") {
|
||||
resp_str = "GPIO" + std::to_string(gpionum) + " switched to " + status;
|
||||
}
|
||||
httpd_resp_sendstr_chunk(req, resp_str.c_str());
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
};
|
||||
|
||||
void GpioHandler::flashLightEnable(bool value)
|
||||
{
|
||||
ESP_LOGD(TAG, "GpioHandler::flashLightEnable %s", value ? "true" : "false");
|
||||
|
||||
if (gpioMap != NULL) {
|
||||
for(std::map<gpio_num_t, GpioPin*>::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it)
|
||||
{
|
||||
if (it->second->getMode() == GPIO_PIN_MODE_BUILT_IN_FLASH_LED) //|| (it->second->getMode() == GPIO_PIN_MODE_EXTERNAL_FLASH_PWM) || (it->second->getMode() == GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X))
|
||||
{
|
||||
std::string resp_str = "";
|
||||
it->second->setValue(value, GPIO_SET_SOURCE_INTERNAL, &resp_str);
|
||||
|
||||
if (resp_str == "") {
|
||||
ESP_LOGD(TAG, "Flash light pin GPIO %d switched to %s", (int)it->first, (value ? "on" : "off"));
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Can't set flash light pin GPIO %d. Error: %s", (int)it->first, resp_str.c_str());
|
||||
}
|
||||
} else
|
||||
{
|
||||
if (it->second->getMode() == GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X)
|
||||
{
|
||||
#ifdef __LEDGLOBAL
|
||||
if (leds_global == NULL) {
|
||||
ESP_LOGI(TAG, "init SmartLed: LEDNumber=%d, GPIO=%d", LEDNumbers, (int)it->second->getGPIO());
|
||||
leds_global = new SmartLed( LEDType, LEDNumbers, it->second->getGPIO(), 0, DoubleBuffer );
|
||||
} else {
|
||||
// wait until we can update: https://github.com/RoboticsBrno/SmartLeds/issues/10#issuecomment-386921623
|
||||
leds_global->wait();
|
||||
}
|
||||
#else
|
||||
SmartLed leds( LEDType, LEDNumbers, it->second->getGPIO(), 0, DoubleBuffer );
|
||||
#endif
|
||||
|
||||
if (value)
|
||||
{
|
||||
for (int i = 0; i < LEDNumbers; ++i)
|
||||
#ifdef __LEDGLOBAL
|
||||
(*leds_global)[i] = LEDColor;
|
||||
#else
|
||||
leds[i] = LEDColor;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < LEDNumbers; ++i)
|
||||
#ifdef __LEDGLOBAL
|
||||
(*leds_global)[i] = Rgb{0, 0, 0};
|
||||
#else
|
||||
leds[i] = Rgb{0, 0, 0};
|
||||
#endif
|
||||
}
|
||||
#ifdef __LEDGLOBAL
|
||||
leds_global->show();
|
||||
#else
|
||||
leds.show();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gpio_num_t GpioHandler::resolvePinNr(uint8_t pinNr)
|
||||
{
|
||||
switch(pinNr) {
|
||||
case 0:
|
||||
return GPIO_NUM_0;
|
||||
case 1:
|
||||
return GPIO_NUM_1;
|
||||
case 3:
|
||||
return GPIO_NUM_3;
|
||||
case 4:
|
||||
return GPIO_NUM_4;
|
||||
case 12:
|
||||
return GPIO_NUM_12;
|
||||
case 13:
|
||||
return GPIO_NUM_13;
|
||||
default:
|
||||
return GPIO_NUM_NC;
|
||||
}
|
||||
}
|
||||
|
||||
gpio_pin_mode_t GpioHandler::resolvePinMode(std::string input)
|
||||
{
|
||||
if( input == "disabled" ) return GPIO_PIN_MODE_DISABLED;
|
||||
if( input == "input" ) return GPIO_PIN_MODE_INPUT;
|
||||
if( input == "input-pullup" ) return GPIO_PIN_MODE_INPUT_PULLUP;
|
||||
if( input == "input-pulldown" ) return GPIO_PIN_MODE_INPUT_PULLDOWN;
|
||||
if( input == "output" ) return GPIO_PIN_MODE_OUTPUT;
|
||||
if( input == "built-in-led" ) return GPIO_PIN_MODE_BUILT_IN_FLASH_LED;
|
||||
if( input == "output-pwm" ) return GPIO_PIN_MODE_OUTPUT_PWM;
|
||||
if( input == "external-flash-pwm" ) return GPIO_PIN_MODE_EXTERNAL_FLASH_PWM;
|
||||
if( input == "external-flash-ws281x" ) return GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X;
|
||||
|
||||
return GPIO_PIN_MODE_DISABLED;
|
||||
}
|
||||
|
||||
gpio_int_type_t GpioHandler::resolveIntType(std::string input)
|
||||
{
|
||||
if( input == "disabled" ) return GPIO_INTR_DISABLE;
|
||||
if( input == "rising-edge" ) return GPIO_INTR_POSEDGE;
|
||||
if( input == "falling-edge" ) return GPIO_INTR_NEGEDGE;
|
||||
if( input == "rising-and-falling" ) return GPIO_INTR_ANYEDGE ;
|
||||
if( input == "low-level-trigger" ) return GPIO_INTR_LOW_LEVEL;
|
||||
if( input == "high-level-trigger" ) return GPIO_INTR_HIGH_LEVEL;
|
||||
|
||||
|
||||
return GPIO_INTR_DISABLE;
|
||||
}
|
||||
|
||||
static GpioHandler *gpioHandler = NULL;
|
||||
|
||||
void gpio_handler_create(httpd_handle_t server)
|
||||
{
|
||||
if (gpioHandler == NULL)
|
||||
gpioHandler = new GpioHandler(CONFIG_FILE, server);
|
||||
}
|
||||
|
||||
void gpio_handler_init()
|
||||
{
|
||||
if (gpioHandler != NULL) {
|
||||
gpioHandler->init();
|
||||
}
|
||||
}
|
||||
|
||||
void gpio_handler_deinit() {
|
||||
if (gpioHandler != NULL) {
|
||||
gpioHandler->deinit();
|
||||
}
|
||||
}
|
||||
|
||||
void gpio_handler_destroy()
|
||||
{
|
||||
if (gpioHandler != NULL) {
|
||||
gpio_handler_deinit();
|
||||
delete gpioHandler;
|
||||
gpioHandler = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
GpioHandler* gpio_handler_get()
|
||||
{
|
||||
return gpioHandler;
|
||||
}
|
||||
|
||||
114
code/components/jomjol_controlGPIO/server_GPIO.h
Normal file
114
code/components/jomjol_controlGPIO/server_GPIO.h
Normal file
@@ -0,0 +1,114 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SERVER_GPIO_H
|
||||
#define SERVER_GPIO_H
|
||||
|
||||
#include <esp_log.h>
|
||||
|
||||
#include <esp_http_server.h>
|
||||
#include <map>
|
||||
#include "driver/gpio.h"
|
||||
|
||||
#include "SmartLeds.h"
|
||||
|
||||
typedef enum {
|
||||
GPIO_PIN_MODE_DISABLED = 0x0,
|
||||
GPIO_PIN_MODE_INPUT = 0x1,
|
||||
GPIO_PIN_MODE_INPUT_PULLUP = 0x2,
|
||||
GPIO_PIN_MODE_INPUT_PULLDOWN = 0x3,
|
||||
GPIO_PIN_MODE_OUTPUT = 0x4,
|
||||
GPIO_PIN_MODE_BUILT_IN_FLASH_LED = 0x5,
|
||||
GPIO_PIN_MODE_OUTPUT_PWM = 0x6,
|
||||
GPIO_PIN_MODE_EXTERNAL_FLASH_PWM = 0x7,
|
||||
GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X = 0x8,
|
||||
} gpio_pin_mode_t;
|
||||
|
||||
struct GpioResult {
|
||||
gpio_num_t gpio;
|
||||
int value;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
GPIO_SET_SOURCE_INTERNAL = 0,
|
||||
GPIO_SET_SOURCE_MQTT = 1,
|
||||
GPIO_SET_SOURCE_HTTP = 2,
|
||||
} gpio_set_source;
|
||||
|
||||
class GpioPin {
|
||||
public:
|
||||
GpioPin(gpio_num_t gpio, const char* name, gpio_pin_mode_t mode, gpio_int_type_t interruptType, uint8_t dutyResolution, std::string mqttTopic, bool httpEnable);
|
||||
~GpioPin();
|
||||
|
||||
void init();
|
||||
bool getValue(std::string* errorText);
|
||||
void setValue(bool value, gpio_set_source setSource, std::string* errorText);
|
||||
#ifdef ENABLE_MQTT
|
||||
bool handleMQTT(std::string, char* data, int data_len);
|
||||
#endif //ENABLE_MQTT
|
||||
void publishState();
|
||||
void gpioInterrupt(int value);
|
||||
gpio_int_type_t getInterruptType() { return _interruptType; }
|
||||
gpio_pin_mode_t getMode() { return _mode; }
|
||||
gpio_num_t getGPIO(){return _gpio;};
|
||||
|
||||
private:
|
||||
gpio_num_t _gpio;
|
||||
const char* _name;
|
||||
gpio_pin_mode_t _mode;
|
||||
gpio_int_type_t _interruptType;
|
||||
std::string _mqttTopic;
|
||||
int currentState = -1;
|
||||
};
|
||||
|
||||
esp_err_t callHandleHttpRequest(httpd_req_t *req);
|
||||
void taskGpioHandler(void *pvParameter);
|
||||
|
||||
class GpioHandler {
|
||||
public:
|
||||
GpioHandler(std::string configFile, httpd_handle_t httpServer);
|
||||
~GpioHandler();
|
||||
|
||||
void init();
|
||||
void deinit();
|
||||
void registerGpioUri();
|
||||
esp_err_t handleHttpRequest(httpd_req_t *req);
|
||||
void taskHandler();
|
||||
void gpioInterrupt(GpioResult* gpioResult);
|
||||
void flashLightEnable(bool value);
|
||||
bool isEnabled() { return _isEnabled; }
|
||||
#ifdef ENABLE_MQTT
|
||||
void handleMQTTconnect();
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
private:
|
||||
std::string _configFile;
|
||||
httpd_handle_t _httpServer;
|
||||
std::map<gpio_num_t, GpioPin*> *gpioMap = NULL;
|
||||
TaskHandle_t xHandleTaskGpio = NULL;
|
||||
bool _isEnabled = false;
|
||||
|
||||
int LEDNumbers = 2;
|
||||
Rgb LEDColor = Rgb{ 255, 255, 255 };
|
||||
LedType LEDType = LED_WS2812;
|
||||
#ifdef __LEDGLOBAL
|
||||
SmartLed *leds_global = NULL;
|
||||
#endif
|
||||
|
||||
bool readConfig();
|
||||
void clear();
|
||||
|
||||
gpio_num_t resolvePinNr(uint8_t pinNr);
|
||||
gpio_pin_mode_t resolvePinMode(std::string input);
|
||||
gpio_int_type_t resolveIntType(std::string input);
|
||||
};
|
||||
|
||||
void gpio_handler_create(httpd_handle_t server);
|
||||
void gpio_handler_init();
|
||||
void gpio_handler_deinit();
|
||||
void gpio_handler_destroy();
|
||||
GpioHandler* gpio_handler_get();
|
||||
|
||||
|
||||
|
||||
#endif //SERVER_GPIO_H
|
||||
|
||||
9
code/components/jomjol_controlcamera/CMakeLists.txt
Normal file
9
code/components/jomjol_controlcamera/CMakeLists.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||
|
||||
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES esp_timer esp32-camera esp_http_server jomjol_logfile jomjol_image_proc nvs_flash jomjol_fileserver_ota jomjol_controlGPIO)
|
||||
|
||||
|
||||
860
code/components/jomjol_controlcamera/ClassControllCamera.cpp
Normal file
860
code/components/jomjol_controlcamera/ClassControllCamera.cpp
Normal file
@@ -0,0 +1,860 @@
|
||||
#include "ClassControllCamera.h"
|
||||
#include "ClassLogFile.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "Helper.h"
|
||||
#include "statusled.h"
|
||||
#include "CImageBasis.h"
|
||||
|
||||
#include "server_ota.h"
|
||||
#include "server_GPIO.h"
|
||||
|
||||
#include "../../include/defines.h"
|
||||
|
||||
#include <esp_event.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_system.h>
|
||||
#include <nvs_flash.h>
|
||||
#include <sys/param.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "esp_camera.h"
|
||||
|
||||
#include "driver/ledc.h"
|
||||
#include "MainFlowControl.h"
|
||||
|
||||
#if (ESP_IDF_VERSION_MAJOR >= 5)
|
||||
#include "soc/periph_defs.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
#include "soc/gpio_sig_map.h"
|
||||
#include "soc/gpio_periph.h"
|
||||
#include "soc/io_mux_reg.h"
|
||||
#include "esp_rom_gpio.h"
|
||||
#define gpio_pad_select_gpio esp_rom_gpio_pad_select_gpio
|
||||
#define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c)
|
||||
#define gpio_matrix_out(a,b,c,d) esp_rom_gpio_connect_out_signal(a,b,c,d)
|
||||
#define ets_delay_us(a) esp_rom_delay_us(a)
|
||||
#endif
|
||||
|
||||
static const char *TAG = "CAM";
|
||||
|
||||
|
||||
/* Camera live stream */
|
||||
#define PART_BOUNDARY "123456789000000000000987654321"
|
||||
static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
|
||||
static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
|
||||
static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";
|
||||
|
||||
|
||||
static camera_config_t camera_config = {
|
||||
.pin_pwdn = CAM_PIN_PWDN,
|
||||
.pin_reset = CAM_PIN_RESET,
|
||||
.pin_xclk = CAM_PIN_XCLK,
|
||||
.pin_sscb_sda = CAM_PIN_SIOD,
|
||||
.pin_sscb_scl = CAM_PIN_SIOC,
|
||||
|
||||
.pin_d7 = CAM_PIN_D7,
|
||||
.pin_d6 = CAM_PIN_D6,
|
||||
.pin_d5 = CAM_PIN_D5,
|
||||
.pin_d4 = CAM_PIN_D4,
|
||||
.pin_d3 = CAM_PIN_D3,
|
||||
.pin_d2 = CAM_PIN_D2,
|
||||
.pin_d1 = CAM_PIN_D1,
|
||||
.pin_d0 = CAM_PIN_D0,
|
||||
.pin_vsync = CAM_PIN_VSYNC,
|
||||
.pin_href = CAM_PIN_HREF,
|
||||
.pin_pclk = CAM_PIN_PCLK,
|
||||
|
||||
//XCLK 20MHz or 10MHz for OV2640 double FPS (Experimental)
|
||||
.xclk_freq_hz = 20000000, // Orginal value
|
||||
// .xclk_freq_hz = 5000000, // Test to get rid of the image errors !!!! Hangs in version 9.2 !!!!
|
||||
.ledc_timer = LEDC_TIMER_0,
|
||||
.ledc_channel = LEDC_CHANNEL_0,
|
||||
|
||||
.pixel_format = PIXFORMAT_JPEG, //YUV422,GRAYSCALE,RGB565,JPEG
|
||||
.frame_size = FRAMESIZE_VGA, //QQVGA-UXGA Do not use sizes above QVGA when not JPEG
|
||||
// .frame_size = FRAMESIZE_UXGA, //QQVGA-UXGA Do not use sizes above QVGA when not JPEG
|
||||
.jpeg_quality = 12, //0-63 lower number means higher quality
|
||||
.fb_count = 1, //if more than one, i2s runs in continuous mode. Use only with JPEG
|
||||
.fb_location = CAMERA_FB_IN_PSRAM, /*!< The location where the frame buffer will be allocated */
|
||||
.grab_mode = CAMERA_GRAB_LATEST, // only from new esp32cam version
|
||||
};
|
||||
|
||||
|
||||
CCamera Camera;
|
||||
|
||||
uint8_t *demoImage = NULL; // Buffer holding the demo image in bytes
|
||||
|
||||
#define DEMO_IMAGE_SIZE 30000 // Max size of demo image in bytes
|
||||
|
||||
typedef struct {
|
||||
httpd_req_t *req;
|
||||
size_t len;
|
||||
} jpg_chunking_t;
|
||||
|
||||
|
||||
bool CCamera::testCamera(void) {
|
||||
bool success;
|
||||
camera_fb_t *fb = esp_camera_fb_get();
|
||||
if (fb) {
|
||||
success = true;
|
||||
}
|
||||
else {
|
||||
success = false;
|
||||
}
|
||||
|
||||
esp_camera_fb_return(fb);
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
void CCamera::ledc_init(void)
|
||||
{
|
||||
#ifdef USE_PWM_LEDFLASH
|
||||
|
||||
// Prepare and then apply the LEDC PWM timer configuration
|
||||
ledc_timer_config_t ledc_timer = { };
|
||||
|
||||
ledc_timer.speed_mode = LEDC_MODE;
|
||||
ledc_timer.timer_num = LEDC_TIMER;
|
||||
ledc_timer.duty_resolution = LEDC_DUTY_RES;
|
||||
ledc_timer.freq_hz = LEDC_FREQUENCY; // Set output frequency at 5 kHz
|
||||
ledc_timer.clk_cfg = LEDC_AUTO_CLK;
|
||||
|
||||
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
|
||||
|
||||
// Prepare and then apply the LEDC PWM channel configuration
|
||||
ledc_channel_config_t ledc_channel = { };
|
||||
|
||||
ledc_channel.speed_mode = LEDC_MODE;
|
||||
ledc_channel.channel = LEDC_CHANNEL;
|
||||
ledc_channel.timer_sel = LEDC_TIMER;
|
||||
ledc_channel.intr_type = LEDC_INTR_DISABLE;
|
||||
ledc_channel.gpio_num = LEDC_OUTPUT_IO;
|
||||
ledc_channel.duty = 0; // Set duty to 0%
|
||||
ledc_channel.hpoint = 0;
|
||||
|
||||
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static size_t jpg_encode_stream(void * arg, size_t index, const void* data, size_t len)
|
||||
{
|
||||
jpg_chunking_t *j = (jpg_chunking_t *)arg;
|
||||
|
||||
if(!index) {
|
||||
j->len = 0;
|
||||
}
|
||||
|
||||
if(httpd_resp_send_chunk(j->req, (const char *)data, len) != ESP_OK) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
j->len += len;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
bool CCamera::SetBrightnessContrastSaturation(int _brightness, int _contrast, int _saturation)
|
||||
{
|
||||
_brightness = min(2, max(-2, _brightness));
|
||||
_contrast = min(2, max(-2, _contrast));
|
||||
_saturation = min(2, max(-2, _saturation));
|
||||
|
||||
sensor_t * s = esp_camera_sensor_get();
|
||||
if (s) {
|
||||
s->set_saturation(s, _saturation);
|
||||
s->set_contrast(s, _contrast);
|
||||
s->set_brightness(s, _brightness);
|
||||
|
||||
/* Workaround - bug in cam library - enable bits are set without using bitwise OR logic -> only latest enable setting is used */
|
||||
/* Library version: https://github.com/espressif/esp32-camera/commit/5c8349f4cf169c8a61283e0da9b8cff10994d3f3 */
|
||||
/* Reference: https://esp32.com/viewtopic.php?f=19&t=14376#p93178 */
|
||||
/* The memory structure is as follows for
|
||||
byte_0 = enable_bits
|
||||
byte_0->bit0 = enable saturation and hue --> OK
|
||||
byte_0->bit1 = enable saturation --> OK
|
||||
byte_0->bit2 = enable brightness and contrast --> OK
|
||||
byte_0->bit3 = enable green -> blue spitial effect (Antique and blunish and greenish and readdish and b&w) enable
|
||||
byte_0->bit4 = anable gray -> read spitial effect (Antique and blunish and greenish and readdish and b&w) enable
|
||||
byte_0->bit5 = remove (UV) in YUV color system
|
||||
byte_0->bit6 = enable negative
|
||||
byte_0->bit7 = remove (Y) in YUV color system
|
||||
byte_1 = saturation1 0-255 --> ?
|
||||
byte_2 = hue 0-255 --> OK
|
||||
byte_3 = saturation2 0-255 --> OK
|
||||
byte_4 = reenter saturation2 in documents --> ?
|
||||
byte_5 = spital effect green -> blue 0-255 --> ?
|
||||
byte_6 = spital effect gray -> read 0-255 --> ?
|
||||
byte_7 = contrast lower byte 0-255 --> OK
|
||||
byte_8 = contrast higher byte 0-255 --> OK
|
||||
byte_9 = brightness 0-255 --> OK
|
||||
byte_10= if byte_10==4 contrast effective --> ?
|
||||
*/
|
||||
|
||||
//s->set_reg(s, 0x7C, 0xFF, 2); // Optional feature - hue setting: Select byte 2 in register 0x7C to set hue value
|
||||
//s->set_reg(s, 0x7D, 0xFF, 0); // Optional feature - hue setting: Hue value 0 - 255
|
||||
s->set_reg(s, 0xFF, 0x01, 0); // Select DSP bank
|
||||
s->set_reg(s, 0x7C, 0xFF, 0); // Select byte 0 in register 0x7C
|
||||
s->set_reg(s, 0x7D, 7, 7); // Set bit 0, 1, 2 in register 0x7D to enable saturation, contrast, brightness and hue control
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "SetBrightnessContrastSaturation: Failed to get control structure");
|
||||
}
|
||||
|
||||
if (((_brightness != brightness) || (_contrast != contrast) || (_saturation != saturation)) && isFixedExposure)
|
||||
EnableAutoExposure(waitbeforepicture_org);
|
||||
|
||||
brightness = _brightness;
|
||||
contrast = _contrast;
|
||||
saturation = _saturation;
|
||||
|
||||
ESP_LOGD(TAG, "brightness %d, contrast: %d, saturation %d", brightness, contrast, saturation);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void CCamera::SetQualitySize(int qual, framesize_t resol)
|
||||
{
|
||||
qual = min(63, max(8, qual)); // Limit quality from 8..63 (values lower than 8 tent to be unstable)
|
||||
|
||||
sensor_t * s = esp_camera_sensor_get();
|
||||
if (s) {
|
||||
s->set_quality(s, qual);
|
||||
s->set_framesize(s, resol);
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "SetQualitySize: Failed to get control structure");
|
||||
}
|
||||
|
||||
ActualResolution = resol;
|
||||
ActualQuality = qual;
|
||||
|
||||
if (resol == FRAMESIZE_QVGA)
|
||||
{
|
||||
image_height = 240;
|
||||
image_width = 320;
|
||||
}
|
||||
else if (resol == FRAMESIZE_VGA)
|
||||
{
|
||||
image_height = 480;
|
||||
image_width = 640;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CCamera::EnableAutoExposure(int flash_duration)
|
||||
{
|
||||
ESP_LOGD(TAG, "EnableAutoExposure");
|
||||
|
||||
LEDOnOff(true);
|
||||
if (flash_duration > 0) {
|
||||
LightOnOff(true);
|
||||
const TickType_t xDelay = flash_duration / portTICK_PERIOD_MS;
|
||||
vTaskDelay( xDelay );
|
||||
}
|
||||
|
||||
camera_fb_t * fb = esp_camera_fb_get();
|
||||
esp_camera_fb_return(fb);
|
||||
fb = esp_camera_fb_get();
|
||||
if (!fb) {
|
||||
LEDOnOff(false);
|
||||
LightOnOff(false);
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "EnableAutoExposure: Capture Failed. "
|
||||
"Check camera module and/or proper electrical connection");
|
||||
//doReboot();
|
||||
}
|
||||
esp_camera_fb_return(fb);
|
||||
|
||||
sensor_t * s = esp_camera_sensor_get();
|
||||
if (s) {
|
||||
s->set_gain_ctrl(s, 0);
|
||||
s->set_exposure_ctrl(s, 0);
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "EnableAutoExposure: Failed to get control structure to set gain+exposure");
|
||||
}
|
||||
|
||||
LEDOnOff(false);
|
||||
LightOnOff(false);
|
||||
isFixedExposure = true;
|
||||
waitbeforepicture_org = flash_duration;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("CaptureToBasisImage - Start");
|
||||
#endif
|
||||
|
||||
_Image->EmptyImage(); //Delete previous stored raw image -> black image
|
||||
|
||||
LEDOnOff(true);
|
||||
|
||||
if (delay > 0) {
|
||||
LightOnOff(true);
|
||||
const TickType_t xDelay = delay / portTICK_PERIOD_MS;
|
||||
vTaskDelay( xDelay );
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("CaptureToBasisImage - After LightOn");
|
||||
#endif
|
||||
|
||||
camera_fb_t * fb = esp_camera_fb_get();
|
||||
esp_camera_fb_return(fb);
|
||||
fb = esp_camera_fb_get();
|
||||
if (!fb) {
|
||||
LEDOnOff(false);
|
||||
LightOnOff(false);
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "is not working anymore (CaptureToBasisImage) - most probably caused "
|
||||
"by a hardware problem (instablility, ...). System will reboot.");
|
||||
doReboot();
|
||||
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (demoMode) { // Use images stored on SD-Card instead of camera image
|
||||
/* Replace Framebuffer with image from SD-Card */
|
||||
loadNextDemoImage(fb);
|
||||
}
|
||||
|
||||
CImageBasis* _zwImage = new CImageBasis("zwImage");
|
||||
if (_zwImage) {
|
||||
_zwImage->LoadFromMemory(fb->buf, fb->len);
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToBasisImage: Can't allocate _zwImage");
|
||||
}
|
||||
esp_camera_fb_return(fb);
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("CaptureToBasisImage - After fb_get");
|
||||
#endif
|
||||
|
||||
LEDOnOff(false);
|
||||
|
||||
if (delay > 0)
|
||||
LightOnOff(false);
|
||||
|
||||
// TickType_t xDelay = 1000 / portTICK_PERIOD_MS;
|
||||
// vTaskDelay( xDelay ); // wait for power to recover
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("CaptureToBasisImage - After LoadFromMemory");
|
||||
#endif
|
||||
|
||||
stbi_uc* p_target;
|
||||
stbi_uc* p_source;
|
||||
int channels = 3;
|
||||
int width = image_width;
|
||||
int height = image_height;
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
std::string _zw = "Targetimage: " + std::to_string((int) _Image->rgb_image) + " Size: " + std::to_string(_Image->width) + ", " + std::to_string(_Image->height);
|
||||
_zw = _zw + " _zwImage: " + std::to_string((int) _zwImage->rgb_image) + " Size: " + std::to_string(_zwImage->width) + ", " + std::to_string(_zwImage->height);
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, _zw);
|
||||
#endif
|
||||
|
||||
for (int x = 0; x < width; ++x)
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
p_target = _Image->rgb_image + (channels * (y * width + x));
|
||||
p_source = _zwImage->rgb_image + (channels * (y * width + x));
|
||||
p_target[0] = p_source[0];
|
||||
p_target[1] = p_source[1];
|
||||
p_target[2] = p_source[2];
|
||||
}
|
||||
|
||||
delete _zwImage;
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("CaptureToBasisImage - Done");
|
||||
#endif
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t CCamera::CaptureToFile(std::string nm, int delay)
|
||||
{
|
||||
string ftype;
|
||||
|
||||
LEDOnOff(true); // Switched off to save power !
|
||||
|
||||
if (delay > 0) {
|
||||
LightOnOff(true);
|
||||
const TickType_t xDelay = delay / portTICK_PERIOD_MS;
|
||||
vTaskDelay( xDelay );
|
||||
}
|
||||
|
||||
camera_fb_t * fb = esp_camera_fb_get();
|
||||
esp_camera_fb_return(fb);
|
||||
fb = esp_camera_fb_get();
|
||||
if (!fb) {
|
||||
LEDOnOff(false);
|
||||
LightOnOff(false);
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToFile: Capture Failed. "
|
||||
"Check camera module and/or proper electrical connection");
|
||||
//doReboot();
|
||||
|
||||
return ESP_FAIL;
|
||||
}
|
||||
LEDOnOff(false);
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "w %d, h %d, size %d", fb->width, fb->height, fb->len);
|
||||
#endif
|
||||
|
||||
nm = FormatFileName(nm);
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Save Camera to: %s", nm.c_str());
|
||||
#endif
|
||||
|
||||
ftype = toUpper(getFileType(nm));
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Filetype: %s", ftype.c_str());
|
||||
#endif
|
||||
|
||||
uint8_t * buf = NULL;
|
||||
size_t buf_len = 0;
|
||||
bool converted = false;
|
||||
|
||||
if (ftype.compare("BMP") == 0)
|
||||
{
|
||||
frame2bmp(fb, &buf, &buf_len);
|
||||
converted = true;
|
||||
}
|
||||
if (ftype.compare("JPG") == 0)
|
||||
{
|
||||
if(fb->format != PIXFORMAT_JPEG){
|
||||
bool jpeg_converted = frame2jpg(fb, ActualQuality, &buf, &buf_len);
|
||||
converted = true;
|
||||
if(!jpeg_converted){
|
||||
ESP_LOGE(TAG, "JPEG compression failed");
|
||||
}
|
||||
} else {
|
||||
buf_len = fb->len;
|
||||
buf = fb->buf;
|
||||
}
|
||||
}
|
||||
|
||||
FILE * fp = fopen(nm.c_str(), "wb");
|
||||
if (fp == NULL) { // If an error occurs during the file creation
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToFile: Failed to open file " + nm);
|
||||
}
|
||||
else {
|
||||
fwrite(buf, sizeof(uint8_t), buf_len, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
if (converted)
|
||||
free(buf);
|
||||
|
||||
esp_camera_fb_return(fb);
|
||||
|
||||
if (delay > 0)
|
||||
LightOnOff(false);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t CCamera::CaptureToHTTP(httpd_req_t *req, int delay)
|
||||
{
|
||||
esp_err_t res = ESP_OK;
|
||||
size_t fb_len = 0;
|
||||
int64_t fr_start = esp_timer_get_time();
|
||||
|
||||
LEDOnOff(true);
|
||||
|
||||
if (delay > 0) {
|
||||
LightOnOff(true);
|
||||
const TickType_t xDelay = delay / portTICK_PERIOD_MS;
|
||||
vTaskDelay( xDelay );
|
||||
}
|
||||
|
||||
camera_fb_t *fb = esp_camera_fb_get();
|
||||
esp_camera_fb_return(fb);
|
||||
fb = esp_camera_fb_get();
|
||||
if (!fb) {
|
||||
LEDOnOff(false);
|
||||
LightOnOff(false);
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToFile: Capture Failed. "
|
||||
"Check camera module and/or proper electrical connection");
|
||||
httpd_resp_send_500(req);
|
||||
// doReboot();
|
||||
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
LEDOnOff(false);
|
||||
|
||||
res = httpd_resp_set_type(req, "image/jpeg");
|
||||
if(res == ESP_OK){
|
||||
res = httpd_resp_set_hdr(req, "Content-Disposition", "inline; filename=raw.jpg");
|
||||
}
|
||||
|
||||
if(res == ESP_OK){
|
||||
if (demoMode) { // Use images stored on SD-Card instead of camera image
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Using Demo image!");
|
||||
/* Replace Framebuffer with image from SD-Card */
|
||||
loadNextDemoImage(fb);
|
||||
|
||||
res = httpd_resp_send(req, (const char *)fb->buf, fb->len);
|
||||
}
|
||||
else {
|
||||
if(fb->format == PIXFORMAT_JPEG){
|
||||
fb_len = fb->len;
|
||||
res = httpd_resp_send(req, (const char *)fb->buf, fb->len);
|
||||
} else {
|
||||
jpg_chunking_t jchunk = {req, 0};
|
||||
res = frame2jpg_cb(fb, 80, jpg_encode_stream, &jchunk)?ESP_OK:ESP_FAIL;
|
||||
httpd_resp_send_chunk(req, NULL, 0);
|
||||
fb_len = jchunk.len;
|
||||
}
|
||||
}
|
||||
}
|
||||
esp_camera_fb_return(fb);
|
||||
int64_t fr_end = esp_timer_get_time();
|
||||
|
||||
ESP_LOGI(TAG, "JPG: %dKB %dms", (int)(fb_len/1024), (int)((fr_end - fr_start)/1000));
|
||||
|
||||
if (delay > 0)
|
||||
LightOnOff(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t CCamera::CaptureToStream(httpd_req_t *req, bool FlashlightOn)
|
||||
{
|
||||
esp_err_t res = ESP_OK;
|
||||
size_t fb_len = 0;
|
||||
int64_t fr_start;
|
||||
char * part_buf[64];
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Live stream started");
|
||||
|
||||
if (FlashlightOn) {
|
||||
LEDOnOff(true);
|
||||
LightOnOff(true);
|
||||
}
|
||||
|
||||
//httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); //stream is blocking web interface, only serving to local
|
||||
|
||||
httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
|
||||
httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
|
||||
|
||||
while(1)
|
||||
{
|
||||
fr_start = esp_timer_get_time();
|
||||
camera_fb_t *fb = esp_camera_fb_get();
|
||||
esp_camera_fb_return(fb);
|
||||
fb = esp_camera_fb_get();
|
||||
if (!fb) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToStream: Camera framebuffer not available");
|
||||
break;
|
||||
}
|
||||
fb_len = fb->len;
|
||||
|
||||
if (res == ESP_OK){
|
||||
size_t hlen = snprintf((char *)part_buf, sizeof(part_buf), _STREAM_PART, fb_len);
|
||||
res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
|
||||
}
|
||||
if (res == ESP_OK){
|
||||
res = httpd_resp_send_chunk(req, (const char *)fb->buf, fb_len);
|
||||
}
|
||||
if (res == ESP_OK){
|
||||
res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
|
||||
}
|
||||
|
||||
esp_camera_fb_return(fb);
|
||||
|
||||
int64_t fr_end = esp_timer_get_time();
|
||||
ESP_LOGD(TAG, "JPG: %dKB %dms", (int)(fb_len/1024), (int)((fr_end - fr_start)/1000));
|
||||
|
||||
if (res != ESP_OK){ // Exit loop, e.g. also when closing the webpage
|
||||
break;
|
||||
}
|
||||
|
||||
int64_t fr_delta_ms = (fr_end - fr_start) / 1000;
|
||||
if (CAM_LIVESTREAM_REFRESHRATE > fr_delta_ms) {
|
||||
const TickType_t xDelay = (CAM_LIVESTREAM_REFRESHRATE - fr_delta_ms) / portTICK_PERIOD_MS;
|
||||
ESP_LOGD(TAG, "Stream: sleep for: %ldms", (long) xDelay*10);
|
||||
vTaskDelay(xDelay);
|
||||
}
|
||||
}
|
||||
|
||||
LEDOnOff(false);
|
||||
LightOnOff(false);
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Live stream stopped");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
void CCamera::LightOnOff(bool status)
|
||||
{
|
||||
GpioHandler* gpioHandler = gpio_handler_get();
|
||||
if ((gpioHandler != NULL) && (gpioHandler->isEnabled())) {
|
||||
ESP_LOGD(TAG, "Use gpioHandler to trigger flashlight");
|
||||
gpioHandler->flashLightEnable(status);
|
||||
}
|
||||
else {
|
||||
#ifdef USE_PWM_LEDFLASH
|
||||
if (status) {
|
||||
ESP_LOGD(TAG, "Internal Flash-LED turn on with PWM %d", led_intensity);
|
||||
ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, led_intensity));
|
||||
// Update duty to apply the new value
|
||||
ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL));
|
||||
}
|
||||
else {
|
||||
ESP_LOGD(TAG, "Internal Flash-LED turn off PWM");
|
||||
ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, 0));
|
||||
ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL));
|
||||
}
|
||||
#else
|
||||
// Init the GPIO
|
||||
gpio_pad_select_gpio(FLASH_GPIO);
|
||||
// Set the GPIO as a push/pull output
|
||||
gpio_set_direction(FLASH_GPIO, GPIO_MODE_OUTPUT);
|
||||
|
||||
if (status)
|
||||
gpio_set_level(FLASH_GPIO, 1);
|
||||
else
|
||||
gpio_set_level(FLASH_GPIO, 0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CCamera::LEDOnOff(bool status)
|
||||
{
|
||||
if (xHandle_task_StatusLED == NULL) {
|
||||
// Init the GPIO
|
||||
gpio_pad_select_gpio(BLINK_GPIO);
|
||||
/* Set the GPIO as a push/pull output */
|
||||
gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
|
||||
|
||||
if (!status)
|
||||
gpio_set_level(BLINK_GPIO, 1);
|
||||
else
|
||||
gpio_set_level(BLINK_GPIO, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CCamera::GetCameraParameter(httpd_req_t *req, int &qual, framesize_t &resol)
|
||||
{
|
||||
char _query[100];
|
||||
char _qual[10];
|
||||
char _size[10];
|
||||
|
||||
resol = ActualResolution;
|
||||
qual = ActualQuality;
|
||||
|
||||
|
||||
if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK)
|
||||
{
|
||||
ESP_LOGD(TAG, "Query: %s", _query);
|
||||
if (httpd_query_key_value(_query, "size", _size, 10) == ESP_OK)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Size: %s", _size);
|
||||
#endif
|
||||
if (strcmp(_size, "QVGA") == 0)
|
||||
resol = FRAMESIZE_QVGA; // 320x240
|
||||
else if (strcmp(_size, "VGA") == 0)
|
||||
resol = FRAMESIZE_VGA; // 640x480
|
||||
else if (strcmp(_size, "SVGA") == 0)
|
||||
resol = FRAMESIZE_SVGA; // 800x600
|
||||
else if (strcmp(_size, "XGA") == 0)
|
||||
resol = FRAMESIZE_XGA; // 1024x768
|
||||
else if (strcmp(_size, "SXGA") == 0)
|
||||
resol = FRAMESIZE_SXGA; // 1280x1024
|
||||
else if (strcmp(_size, "UXGA") == 0)
|
||||
resol = FRAMESIZE_UXGA; // 1600x1200
|
||||
}
|
||||
if (httpd_query_key_value(_query, "quality", _qual, 10) == ESP_OK)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Quality: %s", _qual);
|
||||
#endif
|
||||
qual = atoi(_qual);
|
||||
|
||||
if (qual > 63) // Limit to max. 63
|
||||
qual = 63;
|
||||
else if (qual < 8) // Limit to min. 8
|
||||
qual = 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
framesize_t CCamera::TextToFramesize(const char * _size)
|
||||
{
|
||||
if (strcmp(_size, "QVGA") == 0)
|
||||
return FRAMESIZE_QVGA; // 320x240
|
||||
else if (strcmp(_size, "VGA") == 0)
|
||||
return FRAMESIZE_VGA; // 640x480
|
||||
else if (strcmp(_size, "SVGA") == 0)
|
||||
return FRAMESIZE_SVGA; // 800x600
|
||||
else if (strcmp(_size, "XGA") == 0)
|
||||
return FRAMESIZE_XGA; // 1024x768
|
||||
else if (strcmp(_size, "SXGA") == 0)
|
||||
return FRAMESIZE_SXGA; // 1280x1024
|
||||
else if (strcmp(_size, "UXGA") == 0)
|
||||
return FRAMESIZE_UXGA; // 1600x1200
|
||||
|
||||
return ActualResolution;
|
||||
}
|
||||
|
||||
|
||||
CCamera::CCamera()
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "CreateClassCamera");
|
||||
#endif
|
||||
brightness = 0;
|
||||
contrast = 0;
|
||||
saturation = 0;
|
||||
isFixedExposure = false;
|
||||
|
||||
ledc_init();
|
||||
}
|
||||
|
||||
|
||||
esp_err_t CCamera::InitCam()
|
||||
{
|
||||
ESP_LOGD(TAG, "Init Camera");
|
||||
ActualQuality = camera_config.jpeg_quality;
|
||||
ActualResolution = camera_config.frame_size;
|
||||
//initialize the camera
|
||||
esp_camera_deinit(); // De-init in case it was already initialized
|
||||
esp_err_t err = esp_camera_init(&camera_config);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Camera Init Failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
CameraInitSuccessful = true;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
void CCamera::SetLEDIntensity(float _intrel)
|
||||
{
|
||||
_intrel = min(_intrel, (float) 100);
|
||||
_intrel = max(_intrel, (float) 0);
|
||||
_intrel = _intrel / 100;
|
||||
led_intensity = (int) (_intrel * 8191);
|
||||
ESP_LOGD(TAG, "Set led_intensity to %d of 8191", led_intensity);
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool CCamera::getCameraInitSuccessful()
|
||||
{
|
||||
return CameraInitSuccessful;
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::string> demoFiles;
|
||||
|
||||
void CCamera::useDemoMode()
|
||||
{
|
||||
char line[50];
|
||||
|
||||
FILE *fd = fopen("/sdcard/demo/files.txt", "r");
|
||||
if (!fd) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can not start Demo mode, the folder '/sdcard/demo/' does not contain the needed files!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "See Details on https://jomjol.github.io/AI-on-the-edge-device-docs/Demo-Mode!");
|
||||
return;
|
||||
}
|
||||
|
||||
demoImage = (uint8_t*)malloc(DEMO_IMAGE_SIZE);
|
||||
if (demoImage == NULL) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Unable to acquire required memory for demo image!");
|
||||
return;
|
||||
}
|
||||
|
||||
while (fgets(line, sizeof(line), fd) != NULL) {
|
||||
line[strlen(line) - 1] = '\0';
|
||||
demoFiles.push_back(line);
|
||||
}
|
||||
|
||||
fclose(fd);
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Using Demo mode (" + std::to_string(demoFiles.size()) +
|
||||
" files) instead of real camera image!");
|
||||
|
||||
for (auto file : demoFiles) {
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, file);
|
||||
}
|
||||
|
||||
demoMode = true;
|
||||
}
|
||||
|
||||
|
||||
bool CCamera::loadNextDemoImage(camera_fb_t *fb) {
|
||||
char filename[50];
|
||||
int readBytes;
|
||||
long fileSize;
|
||||
|
||||
snprintf(filename, sizeof(filename), "/sdcard/demo/%s", demoFiles[getCountFlowRounds() % demoFiles.size()].c_str());
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Using " + std::string(filename) + " as demo image");
|
||||
|
||||
/* Inject saved image */
|
||||
|
||||
FILE * fp = fopen(filename, "rb");
|
||||
if (!fp) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to read file: " + std::string(filename) +"!");
|
||||
return false;
|
||||
}
|
||||
|
||||
fileSize = GetFileSize(filename);
|
||||
if (fileSize > DEMO_IMAGE_SIZE) {
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof(buf), "Demo Image (%d bytes) is larger than provided buffer (%d bytes)!",
|
||||
(int)fileSize, DEMO_IMAGE_SIZE);
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, std::string(buf));
|
||||
return false;
|
||||
}
|
||||
|
||||
readBytes = fread(demoImage, 1, DEMO_IMAGE_SIZE, fp);
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "read " + std::to_string(readBytes) + " bytes");
|
||||
fclose(fp);
|
||||
|
||||
fb->buf = demoImage; // Update pointer
|
||||
fb->len = readBytes;
|
||||
// ToDo do we also need to set height, width, format and timestamp?
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
long CCamera::GetFileSize(std::string filename)
|
||||
{
|
||||
struct stat stat_buf;
|
||||
long rc = stat(filename.c_str(), &stat_buf);
|
||||
return rc == 0 ? stat_buf.st_size : -1;
|
||||
}
|
||||
63
code/components/jomjol_controlcamera/ClassControllCamera.h
Normal file
63
code/components/jomjol_controlcamera/ClassControllCamera.h
Normal file
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef CLASSCONTROLLCAMERA_H
|
||||
#define CLASSCONTROLLCAMERA_H
|
||||
|
||||
#include <string>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/event_groups.h"
|
||||
|
||||
#include "esp_camera.h"
|
||||
#include <string>
|
||||
#include <esp_http_server.h>
|
||||
#include "CImageBasis.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
class CCamera {
|
||||
protected:
|
||||
int ActualQuality;
|
||||
framesize_t ActualResolution;
|
||||
int brightness, contrast, saturation;
|
||||
bool isFixedExposure;
|
||||
int waitbeforepicture_org;
|
||||
int led_intensity = 4095;
|
||||
|
||||
void ledc_init(void);
|
||||
bool CameraInitSuccessful = false;
|
||||
bool demoMode = false;
|
||||
|
||||
bool loadNextDemoImage(camera_fb_t *fb);
|
||||
long GetFileSize(std::string filename);
|
||||
|
||||
public:
|
||||
int image_height, image_width;
|
||||
|
||||
CCamera();
|
||||
esp_err_t InitCam();
|
||||
|
||||
void LightOnOff(bool status);
|
||||
void LEDOnOff(bool status);
|
||||
esp_err_t CaptureToHTTP(httpd_req_t *req, int delay = 0);
|
||||
esp_err_t CaptureToStream(httpd_req_t *req, bool FlashlightOn);
|
||||
void SetQualitySize(int qual, framesize_t resol);
|
||||
bool SetBrightnessContrastSaturation(int _brightness, int _contrast, int _saturation);
|
||||
void GetCameraParameter(httpd_req_t *req, int &qual, framesize_t &resol);
|
||||
void SetLEDIntensity(float _intrel);
|
||||
bool testCamera(void);
|
||||
void EnableAutoExposure(int flash_duration);
|
||||
bool getCameraInitSuccessful();
|
||||
void useDemoMode(void);
|
||||
|
||||
|
||||
framesize_t TextToFramesize(const char * text);
|
||||
|
||||
esp_err_t CaptureToFile(std::string nm, int delay = 0);
|
||||
esp_err_t CaptureToBasisImage(CImageBasis *_Image, int delay = 0);
|
||||
};
|
||||
|
||||
|
||||
extern CCamera Camera;
|
||||
|
||||
#endif
|
||||
290
code/components/jomjol_controlcamera/server_camera.cpp
Normal file
290
code/components/jomjol_controlcamera/server_camera.cpp
Normal file
@@ -0,0 +1,290 @@
|
||||
#include "server_camera.h"
|
||||
|
||||
#include <string>
|
||||
#include "string.h"
|
||||
|
||||
#include "esp_camera.h"
|
||||
#include "ClassControllCamera.h"
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "../../include/defines.h"
|
||||
|
||||
static const char *TAG = "server_cam";
|
||||
|
||||
|
||||
void PowerResetCamera(){
|
||||
|
||||
ESP_LOGD(TAG, "Resetting camera by power down line");
|
||||
gpio_config_t conf;
|
||||
conf.intr_type = GPIO_INTR_DISABLE;
|
||||
conf.pin_bit_mask = 1LL << GPIO_NUM_32;
|
||||
conf.mode = GPIO_MODE_OUTPUT;
|
||||
conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||
conf.pull_up_en = GPIO_PULLUP_DISABLE;
|
||||
gpio_config(&conf);
|
||||
|
||||
// carefull, logic is inverted compared to reset pin
|
||||
gpio_set_level(GPIO_NUM_32, 1);
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
gpio_set_level(GPIO_NUM_32, 0);
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
|
||||
esp_err_t handler_lightOn(httpd_req_t *req)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_lightOn - Start");
|
||||
ESP_LOGD(TAG, "handler_lightOn uri: %s", req->uri);
|
||||
#endif
|
||||
|
||||
if (Camera.getCameraInitSuccessful())
|
||||
{
|
||||
Camera.LightOnOff(true);
|
||||
const char* resp_str = (const char*) req->user_ctx;
|
||||
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||
}
|
||||
else
|
||||
{
|
||||
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /lighton not available!");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_lightOn - Done");
|
||||
#endif
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t handler_lightOff(httpd_req_t *req)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_lightOff - Start");
|
||||
ESP_LOGD(TAG, "handler_lightOff uri: %s", req->uri);
|
||||
#endif
|
||||
|
||||
if (Camera.getCameraInitSuccessful())
|
||||
{
|
||||
Camera.LightOnOff(false);
|
||||
const char* resp_str = (const char*) req->user_ctx;
|
||||
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||
}
|
||||
else
|
||||
{
|
||||
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /lightoff not available!");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_lightOff - Done");
|
||||
#endif
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t handler_capture(httpd_req_t *req)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_capture - Start");
|
||||
#endif
|
||||
|
||||
if (Camera.getCameraInitSuccessful())
|
||||
{
|
||||
int quality;
|
||||
framesize_t res;
|
||||
|
||||
Camera.GetCameraParameter(req, quality, res);
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Size: %d, Quality: %d", res, quality);
|
||||
#endif
|
||||
|
||||
Camera.SetQualitySize(quality, res);
|
||||
|
||||
esp_err_t result;
|
||||
result = Camera.CaptureToHTTP(req);
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_capture - Done");
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /capture not available!");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
esp_err_t handler_capture_with_light(httpd_req_t *req)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_capture_with_light - Start");
|
||||
#endif
|
||||
|
||||
if (Camera.getCameraInitSuccessful())
|
||||
{
|
||||
char _query[100];
|
||||
char _delay[10];
|
||||
|
||||
int quality;
|
||||
framesize_t res;
|
||||
int delay = 2500;
|
||||
|
||||
if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK)
|
||||
{
|
||||
ESP_LOGD(TAG, "Query: %s", _query);
|
||||
if (httpd_query_key_value(_query, "delay", _delay, 10) == ESP_OK)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Delay: %s", _delay);
|
||||
#endif
|
||||
delay = atoi(_delay);
|
||||
|
||||
if (delay < 0)
|
||||
delay = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Camera.GetCameraParameter(req, quality, res);
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Size: %d, Quality: %d", res, quality);
|
||||
#endif
|
||||
|
||||
Camera.SetQualitySize(quality, res);
|
||||
Camera.LightOnOff(true);
|
||||
const TickType_t xDelay = delay / portTICK_PERIOD_MS;
|
||||
vTaskDelay( xDelay );
|
||||
|
||||
esp_err_t result;
|
||||
result = Camera.CaptureToHTTP(req);
|
||||
|
||||
Camera.LightOnOff(false);
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_capture_with_light - Done");
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /capture_with_flashlight not available!");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
esp_err_t handler_capture_save_to_file(httpd_req_t *req)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_capture_save_to_file - Start");
|
||||
#endif
|
||||
|
||||
if (Camera.getCameraInitSuccessful())
|
||||
{
|
||||
char _query[100];
|
||||
char _delay[10];
|
||||
int delay = 0;
|
||||
char filename[100];
|
||||
std::string fn = "/sdcard/";
|
||||
|
||||
|
||||
int quality;
|
||||
framesize_t res;
|
||||
|
||||
if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK)
|
||||
{
|
||||
ESP_LOGD(TAG, "Query: %s", _query);
|
||||
if (httpd_query_key_value(_query, "filename", filename, 100) == ESP_OK)
|
||||
{
|
||||
fn.append(filename);
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Filename: %s", fn.c_str());
|
||||
#endif
|
||||
}
|
||||
else
|
||||
fn.append("noname.jpg");
|
||||
|
||||
if (httpd_query_key_value(_query, "delay", _delay, 10) == ESP_OK)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Delay: %s", _delay);
|
||||
#endif
|
||||
delay = atoi(_delay);
|
||||
|
||||
if (delay < 0)
|
||||
delay = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
fn.append("noname.jpg");
|
||||
|
||||
Camera.GetCameraParameter(req, quality, res);
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Size: %d, Quality: %d", res, quality);
|
||||
#endif
|
||||
Camera.SetQualitySize(quality, res);
|
||||
|
||||
esp_err_t result;
|
||||
result = Camera.CaptureToFile(fn, delay);
|
||||
|
||||
const char* resp_str = (const char*) fn.c_str();
|
||||
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_capture_save_to_file - Done");
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /save not available!");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void register_server_camera_uri(httpd_handle_t server)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGI(TAG, "server_part_camera - Registering URI handlers");
|
||||
#endif
|
||||
|
||||
httpd_uri_t camuri = { };
|
||||
camuri.method = HTTP_GET;
|
||||
|
||||
camuri.uri = "/lighton";
|
||||
camuri.handler = handler_lightOn;
|
||||
camuri.user_ctx = (void*) "Light On";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/lightoff";
|
||||
camuri.handler = handler_lightOff;
|
||||
camuri.user_ctx = (void*) "Light Off";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/capture";
|
||||
camuri.handler = handler_capture;
|
||||
camuri.user_ctx = NULL;
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/capture_with_flashlight";
|
||||
camuri.handler = handler_capture_with_light;
|
||||
camuri.user_ctx = NULL;
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/save";
|
||||
camuri.handler = handler_capture_save_to_file;
|
||||
camuri.user_ctx = NULL;
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef JOMJOL_CONTROLCAMERA_H
|
||||
#define JOMJOL_CONTROLCAMERA_H
|
||||
|
||||
@@ -7,8 +9,6 @@
|
||||
|
||||
//#include "ClassControllCamera.h"
|
||||
|
||||
static const char *TAGPARTCAMERA = "server_camera";
|
||||
|
||||
void register_server_camera_uri(httpd_handle_t server);
|
||||
|
||||
void PowerResetCamera();
|
||||
7
code/components/jomjol_fileserver_ota/CMakeLists.txt
Normal file
7
code/components/jomjol_fileserver_ota/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "." "../../include" "miniz"
|
||||
REQUIRES vfs esp_http_server app_update esp_http_client nvs_flash jomjol_tfliteclass jomjol_flowcontroll spiffs jomjol_helper jomjol_controlGPIO)
|
||||
|
||||
|
||||
235
code/components/jomjol_fileserver_ota/miniz/ChangeLog.md
Normal file
235
code/components/jomjol_fileserver_ota/miniz/ChangeLog.md
Normal file
@@ -0,0 +1,235 @@
|
||||
## Changelog
|
||||
|
||||
### 3.0.1
|
||||
|
||||
- Fix compilation error with MINIZ_USE_UNALIGNED_LOADS_AND_STORES=1
|
||||
|
||||
### 3.0.0
|
||||
|
||||
- Reduce memory usage for inflate. This changes `struct tinfl_decompressor_tag` and therefore requires a major version bump (breaks ABI compatibility)
|
||||
- Add padding to structures so it continues to work if features differ. This also changes some structures
|
||||
- Use _ftelli64, _fseeki64 and stat with MinGW32 and OpenWatcom
|
||||
- Fix varios warnings with OpenWatcom compiler
|
||||
- Avoid using unaligned memory access in UBSan builds
|
||||
- Set MINIZ_LITTLE_ENDIAN only if not set
|
||||
- Add MINIZ_NO_DEFLATE_APIS and MINIZ_NO_INFLATE_APIS
|
||||
- Fix use of uninitialized memory in tinfl_decompress_mem_to_callback()
|
||||
- Use wfopen on windows
|
||||
- Use _wstat64 instead _stat64 on windows
|
||||
- Use level_and_flags after MZ_DEFAULT_COMPRESSION has been handled
|
||||
- Improve endianess detection
|
||||
- Don't use unaligned stores and loads per default
|
||||
- Fix function declaration if MINIZ_NO_STDIO is used
|
||||
- Fix MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 not being set
|
||||
- Remove total files check (its 32-bit uint)
|
||||
- tinfl_decompress: avoid NULL ptr arithmetic UB
|
||||
- miniz_zip: fix mz_zip_reader_extract_to_heap to read correct sizes
|
||||
- Eliminate 64-bit operations on 32-bit machines
|
||||
- Disable treating warnings as error with MSVC
|
||||
- Disable building shared lib via CMake by default
|
||||
- Fixed alignment problems on MacOS
|
||||
- Fixed get error string for MZ_ZIP_TOTAL_ERRORS
|
||||
- Write correct FLEVEL 2-bit value in zlib header
|
||||
- miniz.pc.in: fix include path not containing the "miniz" suffix
|
||||
- Fix compatibility with FreeBSD
|
||||
- pkg-config tweaks
|
||||
- Fix integer overflow in header corruption check
|
||||
- Fix some warnings
|
||||
- tdefl_compress_normal: Avoid NULL ptr arithmetic UB
|
||||
- replace use of stdint.h types with mz_ variants
|
||||
|
||||
|
||||
### 2.2.0
|
||||
|
||||
- Fix examples with amalgamation
|
||||
- Modified cmake script to support shared library mode and find_package
|
||||
- Fix for misleading doc comment on `mz_zip_reader_init_cfile` function
|
||||
- Add include location tolerance and stop forcing `_GNU_SOURCE`
|
||||
- Fix: mz_zip_reader_locate_file_v2 returns an mz_bool
|
||||
- Fix large file system checks
|
||||
- Add #elif to enable an external mz_crc32() to be linked in
|
||||
- Write with dynamic size (size of file/data to be added not known before adding)
|
||||
- Added uncompress2 for zlib compatibility
|
||||
- Add support for building as a Meson subproject
|
||||
- Added OSSFuzz support; Integrate with CIFuzz
|
||||
- Add pkg-config file
|
||||
- Fixed use-of-uninitialized value msan error when copying dist bytes with no output bytes written.
|
||||
- mz_zip_validate_file(): fix memory leak on errors
|
||||
- Fixed MSAN use-of-uninitialized in tinfl_decompress when invalid dist is decoded. In this instance dist was 31 which s_dist_base translates as 0
|
||||
- Add flag to set (compressed) size in local file header
|
||||
- avoid use of uninitialized value in tdefl_record_literal
|
||||
|
||||
### 2.1.0
|
||||
|
||||
- More instances of memcpy instead of cast and use memcpy per default
|
||||
- Remove inline for c90 support
|
||||
- New function to read files via callback functions when adding them
|
||||
- Fix out of bounds read while reading Zip64 extended information
|
||||
- guard memcpy when n == 0 because buffer may be NULL
|
||||
- Implement inflateReset() function
|
||||
- Move comp/decomp alloc/free prototypes under guarding #ifndef MZ_NO_MALLOC
|
||||
- Fix large file support under Windows
|
||||
- Don't warn if _LARGEFILE64_SOURCE is not defined to 1
|
||||
- Fixes for MSVC warnings
|
||||
- Remove check that path of file added to archive contains ':' or '\'
|
||||
- Add !defined check on MINIZ_USE_ALIGNED_LOADS_AND_STORES
|
||||
|
||||
### 2.0.8
|
||||
|
||||
- Remove unimplemented functions (mz_zip_locate_file and mz_zip_locate_file_v2)
|
||||
- Add license, changelog, readme and example files to release zip
|
||||
- Fix heap overflow to user buffer in tinfl_status tinfl_decompress
|
||||
- Fix corrupt archive if uncompressed file smaller than 4 byte and the file is added by mz_zip_writer_add_mem*
|
||||
|
||||
### 2.0.7
|
||||
|
||||
- Removed need in C++ compiler in cmake build
|
||||
- Fixed a lot of uninitialized value errors found with Valgrind by memsetting m_dict to 0 in tdefl_init
|
||||
- Fix resource leak in mz_zip_reader_init_file_v2
|
||||
- Fix assert with mz_zip_writer_add_mem* w/MZ_DEFAULT_COMPRESSION
|
||||
- cmake build: install library and headers
|
||||
- Remove _LARGEFILE64_SOURCE requirement from apple defines for large files
|
||||
|
||||
### 2.0.6
|
||||
|
||||
- Improve MZ_ZIP_FLAG_WRITE_ZIP64 documentation
|
||||
- Remove check for cur_archive_file_ofs > UINT_MAX because cur_archive_file_ofs is not used after this point
|
||||
- Add cmake debug configuration
|
||||
- Fix PNG height when creating png files
|
||||
- Add "iterative" file extraction method based on mz_zip_reader_extract_to_callback.
|
||||
- Option to use memcpy for unaligned data access
|
||||
- Define processor/arch macros as zero if not set to one
|
||||
|
||||
### 2.0.4/2.0.5
|
||||
|
||||
- Fix compilation with the various omission compile definitions
|
||||
|
||||
### 2.0.3
|
||||
|
||||
- Fix GCC/clang compile warnings
|
||||
- Added callback for periodic flushes (for ZIP file streaming)
|
||||
- Use UTF-8 for file names in ZIP files per default
|
||||
|
||||
### 2.0.2
|
||||
|
||||
- Fix source backwards compatibility with 1.x
|
||||
- Fix a ZIP bit not being set correctly
|
||||
|
||||
### 2.0.1
|
||||
|
||||
- Added some tests
|
||||
- Added CI
|
||||
- Make source code ANSI C compatible
|
||||
|
||||
### 2.0.0 beta
|
||||
|
||||
- Matthew Sitton merged miniz 1.x to Rich Geldreich's vogl ZIP64 changes. Miniz is now licensed as MIT since the vogl code base is MIT licensed
|
||||
- Miniz is now split into several files
|
||||
- Miniz does now not seek backwards when creating ZIP files. That is the ZIP files can be streamed
|
||||
- Miniz automatically switches to the ZIP64 format when the created ZIP files goes over ZIP file limits
|
||||
- Similar to [SQLite](https://www.sqlite.org/amalgamation.html) the Miniz source code is amalgamated into one miniz.c/miniz.h pair in a build step (amalgamate.sh). Please use miniz.c/miniz.h in your projects
|
||||
- Miniz 2 is only source back-compatible with miniz 1.x. It breaks binary compatibility because structures changed
|
||||
|
||||
### v1.16 BETA Oct 19, 2013
|
||||
|
||||
Still testing, this release is downloadable from [here](http://www.tenacioussoftware.com/miniz_v116_beta_r1.7z). Two key inflator-only robustness and streaming related changes. Also merged in tdefl_compressor_alloc(), tdefl_compressor_free() helpers to make script bindings easier for rustyzip. I would greatly appreciate any help with testing or any feedback.
|
||||
|
||||
The inflator in raw (non-zlib) mode is now usable on gzip or similar streams that have a bunch of bytes following the raw deflate data (problem discovered by rustyzip author williamw520). This version should never read beyond the last byte of the raw deflate data independent of how many bytes you pass into the input buffer.
|
||||
|
||||
The inflator now has a new failure status TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS (-4). Previously, if the inflator was starved of bytes and could not make progress (because the input buffer was empty and the caller did not set the TINFL_FLAG_HAS_MORE_INPUT flag - say on truncated or corrupted compressed data stream) it would append all 0's to the input and try to soldier on. This is scary behavior if the caller didn't know when to stop accepting output (because it didn't know how much uncompressed data was expected, or didn't enforce a sane maximum). v1.16 will instead return TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS immediately if it needs 1 or more bytes to make progress, the input buf is empty, and the caller has indicated that no more input is available. This is a "soft" failure, so you can call the inflator again with more input and it will try to continue, or you can give up and fail. This could be very useful in network streaming scenarios.
|
||||
|
||||
- The inflator coroutine func. is subtle and complex so I'm being cautious about this release. I would greatly appreciate any help with testing or any feedback.
|
||||
I feel good about these changes, and they've been through several hours of automated testing, but they will probably not fix anything for the majority of prev. users so I'm
|
||||
going to mark this release as beta for a few weeks and continue testing it at work/home on various things.
|
||||
- The inflator in raw (non-zlib) mode is now usable on gzip or similar data streams that have a bunch of bytes following the raw deflate data (problem discovered by rustyzip author williamw520).
|
||||
This version should *never* read beyond the last byte of the raw deflate data independent of how many bytes you pass into the input buffer. This issue was caused by the various Huffman bitbuffer lookahead optimizations, and
|
||||
would not be an issue if the caller knew and enforced the precise size of the raw compressed data *or* if the compressed data was in zlib format (i.e. always followed by the byte aligned zlib adler32).
|
||||
So in other words, you can now call the inflator on deflate streams that are followed by arbitrary amounts of data and it's guaranteed that decompression will stop exactly on the last byte.
|
||||
- The inflator now has a new failure status: TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS (-4). Previously, if the inflator was starved of bytes and could not make progress (because the input buffer was empty and the
|
||||
caller did not set the TINFL_FLAG_HAS_MORE_INPUT flag - say on truncated or corrupted compressed data stream) it would append all 0's to the input and try to soldier on.
|
||||
This is scary, because in the worst case, I believe it was possible for the prev. inflator to start outputting large amounts of literal data. If the caller didn't know when to stop accepting output
|
||||
(because it didn't know how much uncompressed data was expected, or didn't enforce a sane maximum) it could continue forever. v1.16 cannot fall into this failure mode, instead it'll return
|
||||
TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS immediately if it needs 1 or more bytes to make progress, the input buf is empty, and the caller has indicated that no more input is available. This is a "soft"
|
||||
failure, so you can call the inflator again with more input and it will try to continue, or you can give up and fail. This could be very useful in network streaming scenarios.
|
||||
- Added documentation to all the tinfl return status codes, fixed miniz_tester so it accepts double minus params for Linux, tweaked example1.c, added a simple "follower bytes" test to miniz_tester.cpp.
|
||||
### v1.15 r4 STABLE - Oct 13, 2013
|
||||
|
||||
Merged over a few very minor bug fixes that I fixed in the zip64 branch. This is downloadable from [here](http://code.google.com/p/miniz/downloads/list) and also in SVN head (as of 10/19/13).
|
||||
|
||||
|
||||
### v1.15 - Oct. 13, 2013
|
||||
|
||||
Interim bugfix release while I work on the next major release with zip64 and streaming compression/decompression support. Fixed the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com), which could cause the locate files func to not find files when this flag was specified. Also fixed a bug in mz_zip_reader_extract_to_mem_no_alloc() with user provided read buffers (thanks kymoon). I also merged lots of compiler fixes from various github repo branches and Google Code issue reports. I finally added cmake support (only tested under for Linux so far), compiled and tested with clang v3.3 and gcc 4.6 (under Linux), added defl_write_image_to_png_file_in_memory_ex() (supports Y flipping for OpenGL use, real-time compression), added a new PNG example (example6.c - Mandelbrot), and I added 64-bit file I/O support (stat64(), etc.) for glibc.
|
||||
|
||||
- Critical fix for the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com) which could cause locate files to not find files. This bug
|
||||
would only have occurred in earlier versions if you explicitly used this flag, OR if you used mz_zip_extract_archive_file_to_heap() or mz_zip_add_mem_to_archive_file_in_place()
|
||||
(which used this flag). If you can't switch to v1.15 but want to fix this bug, just remove the uses of this flag from both helper funcs (and of course don't use the flag).
|
||||
- Bugfix in mz_zip_reader_extract_to_mem_no_alloc() from kymoon when pUser_read_buf is not NULL and compressed size is > uncompressed size
|
||||
- Fixing mz_zip_reader_extract_*() funcs so they don't try to extract compressed data from directory entries, to account for weird zipfiles which contain zero-size compressed data on dir entries.
|
||||
Hopefully this fix won't cause any issues on weird zip archives, because it assumes the low 16-bits of zip external attributes are DOS attributes (which I believe they always are in practice).
|
||||
- Fixing mz_zip_reader_is_file_a_directory() so it doesn't check the internal attributes, just the filename and external attributes
|
||||
- mz_zip_reader_init_file() - missing MZ_FCLOSE() call if the seek failed
|
||||
- Added cmake support for Linux builds which builds all the examples, tested with clang v3.3 and gcc v4.6.
|
||||
- Clang fix for tdefl_write_image_to_png_file_in_memory() from toffaletti
|
||||
- Merged MZ_FORCEINLINE fix from hdeanclark
|
||||
- Fix <time.h> include before config #ifdef, thanks emil.brink
|
||||
- Added tdefl_write_image_to_png_file_in_memory_ex(): supports Y flipping (super useful for OpenGL apps), and explicit control over the compression level (so you can
|
||||
set it to 1 for real-time compression).
|
||||
- Merged in some compiler fixes from paulharris's github repro.
|
||||
- Retested this build under Windows (VS 2010, including static analysis), tcc 0.9.26, gcc v4.6 and clang v3.3.
|
||||
- Added example6.c, which dumps an image of the mandelbrot set to a PNG file.
|
||||
- Modified example2 to help test the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY flag more.
|
||||
- In r3: Bugfix to mz_zip_writer_add_file() found during merge: Fix possible src file fclose() leak if alignment bytes+local header file write faiiled
|
||||
- In r4: Minor bugfix to mz_zip_writer_add_from_zip_reader(): Was pushing the wrong central dir header offset, appears harmless in this release, but it became a problem in the zip64 branch
|
||||
|
||||
### v1.14 - May 20, 2012
|
||||
|
||||
(SVN Only) Minor tweaks to get miniz.c compiling with the Tiny C Compiler, added #ifndef MINIZ_NO_TIME guards around utime.h includes. Adding mz_free() function, so the caller can free heap blocks returned by miniz using whatever heap functions it has been configured to use, MSVC specific fixes to use "safe" variants of several functions (localtime_s, fopen_s, freopen_s).
|
||||
|
||||
MinGW32/64 GCC 4.6.1 compiler fixes: added MZ_FORCEINLINE, #include <time.h> (thanks fermtect).
|
||||
|
||||
Compiler specific fixes, some from fermtect. I upgraded to TDM GCC 4.6.1 and now static __forceinline is giving it fits, so I'm changing all usage of __forceinline to MZ_FORCEINLINE and forcing gcc to use __attribute__((__always_inline__)) (and MSVC to use __forceinline). Also various fixes from fermtect for MinGW32: added #include , 64-bit ftell/fseek fixes.
|
||||
|
||||
### v1.13 - May 19, 2012
|
||||
|
||||
From jason@cornsyrup.org and kelwert@mtu.edu - Most importantly, fixed mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bits. Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files. Other stuff:
|
||||
|
||||
Eliminated a bunch of warnings when compiling with GCC 32-bit/64. Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly "Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning).
|
||||
|
||||
Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64. Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test. Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives. Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.)
|
||||
|
||||
Fix ftell() usage in a few of the examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself). Fix fail logic handling in mz_zip_add_mem_to_archive_file_in_place() so it always calls mz_zip_writer_finalize_archive() and mz_zip_writer_end(), even if the file add fails.
|
||||
|
||||
- From jason@cornsyrup.org and kelwert@mtu.edu - Fix mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit.
|
||||
- Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files.
|
||||
- Eliminated a bunch of warnings when compiling with GCC 32-bit/64.
|
||||
- Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly
|
||||
"Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning).
|
||||
- Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64.
|
||||
- Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test.
|
||||
- Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives.
|
||||
- Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.)
|
||||
- Fix ftell() usage in examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself).
|
||||
|
||||
### v1.12 - 4/12/12
|
||||
|
||||
More comments, added low-level example5.c, fixed a couple minor level_and_flags issues in the archive API's.
|
||||
level_and_flags can now be set to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson <bruced@valvesoftware.com> for the feedback/bug report.
|
||||
|
||||
### v1.11 - 5/28/11
|
||||
|
||||
Added statement from unlicense.org
|
||||
|
||||
### v1.10 - 5/27/11
|
||||
|
||||
- Substantial compressor optimizations:
|
||||
- Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86).
|
||||
- Improved baseline L2-L9 compression perf. Also, greatly improved compression perf. issues on some file types.
|
||||
- Refactored the compression code for better readability and maintainability.
|
||||
- Added level 10 compression level (L10 has slightly better ratio than level 9, but could have a potentially large drop in throughput on some files).
|
||||
|
||||
### v1.09 - 5/15/11
|
||||
|
||||
Initial stable release.
|
||||
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 jomjol
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Copyright 2013-2014 RAD Game Tools and Valve Software
|
||||
Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
|
||||
|
||||
All Rights Reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
105
code/components/jomjol_fileserver_ota/miniz/examples/example1.c
Normal file
105
code/components/jomjol_fileserver_ota/miniz/examples/example1.c
Normal file
@@ -0,0 +1,105 @@
|
||||
// example1.c - Demonstrates miniz.c's compress() and uncompress() functions (same as zlib's).
|
||||
// Public domain, May 15 2011, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c.
|
||||
#include <stdio.h>
|
||||
#include "miniz.h"
|
||||
typedef unsigned char uint8;
|
||||
typedef unsigned short uint16;
|
||||
typedef unsigned int uint;
|
||||
|
||||
// The string to compress.
|
||||
static const char *s_pStr = "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \
|
||||
"Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \
|
||||
"Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \
|
||||
"Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \
|
||||
"Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \
|
||||
"Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \
|
||||
"Good morning Dr. Chandra. This is Hal. I am ready for my first lesson.";
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
uint step = 0;
|
||||
int cmp_status;
|
||||
uLong src_len = (uLong)strlen(s_pStr);
|
||||
uLong cmp_len = compressBound(src_len);
|
||||
uLong uncomp_len = src_len;
|
||||
uint8 *pCmp, *pUncomp;
|
||||
uint total_succeeded = 0;
|
||||
(void)argc, (void)argv;
|
||||
|
||||
printf("miniz.c version: %s\n", MZ_VERSION);
|
||||
|
||||
do
|
||||
{
|
||||
// Allocate buffers to hold compressed and uncompressed data.
|
||||
pCmp = (mz_uint8 *)malloc((size_t)cmp_len);
|
||||
pUncomp = (mz_uint8 *)malloc((size_t)src_len);
|
||||
if ((!pCmp) || (!pUncomp))
|
||||
{
|
||||
printf("Out of memory!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Compress the string.
|
||||
cmp_status = compress(pCmp, &cmp_len, (const unsigned char *)s_pStr, src_len);
|
||||
if (cmp_status != Z_OK)
|
||||
{
|
||||
printf("compress() failed!\n");
|
||||
free(pCmp);
|
||||
free(pUncomp);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("Compressed from %u to %u bytes\n", (mz_uint32)src_len, (mz_uint32)cmp_len);
|
||||
|
||||
if (step)
|
||||
{
|
||||
// Purposely corrupt the compressed data if fuzzy testing (this is a very crude fuzzy test).
|
||||
uint n = 1 + (rand() % 3);
|
||||
while (n--)
|
||||
{
|
||||
uint i = rand() % cmp_len;
|
||||
pCmp[i] ^= (rand() & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
// Decompress.
|
||||
cmp_status = uncompress(pUncomp, &uncomp_len, pCmp, cmp_len);
|
||||
total_succeeded += (cmp_status == Z_OK);
|
||||
|
||||
if (step)
|
||||
{
|
||||
printf("Simple fuzzy test: step %u total_succeeded: %u\n", step, total_succeeded);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cmp_status != Z_OK)
|
||||
{
|
||||
printf("uncompress failed!\n");
|
||||
free(pCmp);
|
||||
free(pUncomp);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("Decompressed from %u to %u bytes\n", (mz_uint32)cmp_len, (mz_uint32)uncomp_len);
|
||||
|
||||
// Ensure uncompress() returned the expected data.
|
||||
if ((uncomp_len != src_len) || (memcmp(pUncomp, s_pStr, (size_t)src_len)))
|
||||
{
|
||||
printf("Decompression failed!\n");
|
||||
free(pCmp);
|
||||
free(pUncomp);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
free(pCmp);
|
||||
free(pUncomp);
|
||||
|
||||
step++;
|
||||
|
||||
// Keep on fuzzy testing if there's a non-empty command line.
|
||||
} while (argc >= 2);
|
||||
|
||||
printf("Success.\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
164
code/components/jomjol_fileserver_ota/miniz/examples/example2.c
Normal file
164
code/components/jomjol_fileserver_ota/miniz/examples/example2.c
Normal file
@@ -0,0 +1,164 @@
|
||||
// example2.c - Simple demonstration of miniz.c's ZIP archive API's.
|
||||
// Note this test deletes the test archive file "__mz_example2_test__.zip" in the current directory, then creates a new one with test data.
|
||||
// Public domain, May 15 2011, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c.
|
||||
|
||||
#if defined(__GNUC__)
|
||||
// Ensure we get the 64-bit variants of the CRT's file I/O calls
|
||||
#ifndef _FILE_OFFSET_BITS
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#endif
|
||||
#ifndef _LARGEFILE64_SOURCE
|
||||
#define _LARGEFILE64_SOURCE 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include "miniz.h"
|
||||
|
||||
typedef unsigned char uint8;
|
||||
typedef unsigned short uint16;
|
||||
typedef unsigned int uint;
|
||||
|
||||
// The string to compress.
|
||||
static const char *s_pTest_str =
|
||||
"MISSION CONTROL I wouldn't worry too much about the computer. First of all, there is still a chance that he is right, despite your tests, and" \
|
||||
"if it should happen again, we suggest eliminating this possibility by allowing the unit to remain in place and seeing whether or not it" \
|
||||
"actually fails. If the computer should turn out to be wrong, the situation is still not alarming. The type of obsessional error he may be" \
|
||||
"guilty of is not unknown among the latest generation of HAL 9000 computers. It has almost always revolved around a single detail, such as" \
|
||||
"the one you have described, and it has never interfered with the integrity or reliability of the computer's performance in other areas." \
|
||||
"No one is certain of the cause of this kind of malfunctioning. It may be over-programming, but it could also be any number of reasons. In any" \
|
||||
"event, it is somewhat analogous to human neurotic behavior. Does this answer your query? Zero-five-three-Zero, MC, transmission concluded.";
|
||||
|
||||
static const char *s_pComment = "This is a comment";
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i, sort_iter;
|
||||
mz_bool status;
|
||||
size_t uncomp_size;
|
||||
mz_zip_archive zip_archive;
|
||||
void *p;
|
||||
const int N = 50;
|
||||
char data[2048];
|
||||
char archive_filename[64];
|
||||
static const char *s_Test_archive_filename = "__mz_example2_test__.zip";
|
||||
|
||||
assert((strlen(s_pTest_str) + 64) < sizeof(data));
|
||||
|
||||
printf("miniz.c version: %s\n", MZ_VERSION);
|
||||
|
||||
(void)argc, (void)argv;
|
||||
|
||||
// Delete the test archive, so it doesn't keep growing as we run this test
|
||||
remove(s_Test_archive_filename);
|
||||
|
||||
// Append a bunch of text files to the test archive
|
||||
for (i = (N - 1); i >= 0; --i)
|
||||
{
|
||||
sprintf(archive_filename, "%u.txt", i);
|
||||
sprintf(data, "%u %s %u", (N - 1) - i, s_pTest_str, i);
|
||||
|
||||
// Add a new file to the archive. Note this is an IN-PLACE operation, so if it fails your archive is probably hosed (its central directory may not be complete) but it should be recoverable using zip -F or -FF. So use caution with this guy.
|
||||
// A more robust way to add a file to an archive would be to read it into memory, perform the operation, then write a new archive out to a temp file and then delete/rename the files.
|
||||
// Or, write a new archive to disk to a temp file, then delete/rename the files. For this test this API is fine.
|
||||
status = mz_zip_add_mem_to_archive_file_in_place(s_Test_archive_filename, archive_filename, data, strlen(data) + 1, s_pComment, (uint16)strlen(s_pComment), MZ_BEST_COMPRESSION);
|
||||
if (!status)
|
||||
{
|
||||
printf("mz_zip_add_mem_to_archive_file_in_place failed!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
// Add a directory entry for testing
|
||||
status = mz_zip_add_mem_to_archive_file_in_place(s_Test_archive_filename, "directory/", NULL, 0, "no comment", (uint16)strlen("no comment"), MZ_BEST_COMPRESSION);
|
||||
if (!status)
|
||||
{
|
||||
printf("mz_zip_add_mem_to_archive_file_in_place failed!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Now try to open the archive.
|
||||
memset(&zip_archive, 0, sizeof(zip_archive));
|
||||
|
||||
status = mz_zip_reader_init_file(&zip_archive, s_Test_archive_filename, 0);
|
||||
if (!status)
|
||||
{
|
||||
printf("mz_zip_reader_init_file() failed!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Get and print information about each file in the archive.
|
||||
for (i = 0; i < (int)mz_zip_reader_get_num_files(&zip_archive); i++)
|
||||
{
|
||||
mz_zip_archive_file_stat file_stat;
|
||||
if (!mz_zip_reader_file_stat(&zip_archive, i, &file_stat))
|
||||
{
|
||||
printf("mz_zip_reader_file_stat() failed!\n");
|
||||
mz_zip_reader_end(&zip_archive);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("Filename: \"%s\", Comment: \"%s\", Uncompressed size: %u, Compressed size: %u, Is Dir: %u\n", file_stat.m_filename, file_stat.m_comment, (uint)file_stat.m_uncomp_size, (uint)file_stat.m_comp_size, mz_zip_reader_is_file_a_directory(&zip_archive, i));
|
||||
|
||||
if (!strcmp(file_stat.m_filename, "directory/"))
|
||||
{
|
||||
if (!mz_zip_reader_is_file_a_directory(&zip_archive, i))
|
||||
{
|
||||
printf("mz_zip_reader_is_file_a_directory() didn't return the expected results!\n");
|
||||
mz_zip_reader_end(&zip_archive);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close the archive, freeing any resources it was using
|
||||
mz_zip_reader_end(&zip_archive);
|
||||
|
||||
// Now verify the compressed data
|
||||
for (sort_iter = 0; sort_iter < 2; sort_iter++)
|
||||
{
|
||||
memset(&zip_archive, 0, sizeof(zip_archive));
|
||||
status = mz_zip_reader_init_file(&zip_archive, s_Test_archive_filename, sort_iter ? MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY : 0);
|
||||
if (!status)
|
||||
{
|
||||
printf("mz_zip_reader_init_file() failed!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
for (i = 0; i < N; i++)
|
||||
{
|
||||
sprintf(archive_filename, "%u.txt", i);
|
||||
sprintf(data, "%u %s %u", (N - 1) - i, s_pTest_str, i);
|
||||
|
||||
// Try to extract all the files to the heap.
|
||||
p = mz_zip_reader_extract_file_to_heap(&zip_archive, archive_filename, &uncomp_size, 0);
|
||||
if (!p)
|
||||
{
|
||||
printf("mz_zip_reader_extract_file_to_heap() failed!\n");
|
||||
mz_zip_reader_end(&zip_archive);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Make sure the extraction really succeeded.
|
||||
if ((uncomp_size != (strlen(data) + 1)) || (memcmp(p, data, strlen(data))))
|
||||
{
|
||||
printf("mz_zip_reader_extract_file_to_heap() failed to extract the proper data\n");
|
||||
mz_free(p);
|
||||
mz_zip_reader_end(&zip_archive);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("Successfully extracted file \"%s\", size %u\n", archive_filename, (uint)uncomp_size);
|
||||
printf("File data: \"%s\"\n", (const char *)p);
|
||||
|
||||
// We're done.
|
||||
mz_free(p);
|
||||
}
|
||||
|
||||
// Close the archive, freeing any resources it was using
|
||||
mz_zip_reader_end(&zip_archive);
|
||||
}
|
||||
|
||||
printf("Success.\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
269
code/components/jomjol_fileserver_ota/miniz/examples/example3.c
Normal file
269
code/components/jomjol_fileserver_ota/miniz/examples/example3.c
Normal file
@@ -0,0 +1,269 @@
|
||||
// example3.c - Demonstrates how to use miniz.c's deflate() and inflate() functions for simple file compression.
|
||||
// Public domain, May 15 2011, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c.
|
||||
// For simplicity, this example is limited to files smaller than 4GB, but this is not a limitation of miniz.c.
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include "miniz.h"
|
||||
|
||||
typedef unsigned char uint8;
|
||||
typedef unsigned short uint16;
|
||||
typedef unsigned int uint;
|
||||
|
||||
#define my_max(a,b) (((a) > (b)) ? (a) : (b))
|
||||
#define my_min(a,b) (((a) < (b)) ? (a) : (b))
|
||||
|
||||
#define BUF_SIZE (1024 * 1024)
|
||||
static uint8 s_inbuf[BUF_SIZE];
|
||||
static uint8 s_outbuf[BUF_SIZE];
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char *pMode;
|
||||
FILE *pInfile, *pOutfile;
|
||||
uint infile_size;
|
||||
int level = Z_BEST_COMPRESSION;
|
||||
z_stream stream;
|
||||
int p = 1;
|
||||
const char *pSrc_filename;
|
||||
const char *pDst_filename;
|
||||
long file_loc;
|
||||
|
||||
printf("miniz.c version: %s\n", MZ_VERSION);
|
||||
|
||||
if (argc < 4)
|
||||
{
|
||||
printf("Usage: example3 [options] [mode:c or d] infile outfile\n");
|
||||
printf("\nModes:\n");
|
||||
printf("c - Compresses file infile to a zlib stream in file outfile\n");
|
||||
printf("d - Decompress zlib stream in file infile to file outfile\n");
|
||||
printf("\nOptions:\n");
|
||||
printf("-l[0-10] - Compression level, higher values are slower.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
while ((p < argc) && (argv[p][0] == '-'))
|
||||
{
|
||||
switch (argv[p][1])
|
||||
{
|
||||
case 'l':
|
||||
{
|
||||
level = atoi(&argv[1][2]);
|
||||
if ((level < 0) || (level > 10))
|
||||
{
|
||||
printf("Invalid level!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
printf("Invalid option: %s\n", argv[p]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
if ((argc - p) < 3)
|
||||
{
|
||||
printf("Must specify mode, input filename, and output filename after options!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
else if ((argc - p) > 3)
|
||||
{
|
||||
printf("Too many filenames!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
pMode = argv[p++];
|
||||
if (!strchr("cCdD", pMode[0]))
|
||||
{
|
||||
printf("Invalid mode!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
pSrc_filename = argv[p++];
|
||||
pDst_filename = argv[p++];
|
||||
|
||||
printf("Mode: %c, Level: %u\nInput File: \"%s\"\nOutput File: \"%s\"\n", pMode[0], level, pSrc_filename, pDst_filename);
|
||||
|
||||
// Open input file.
|
||||
pInfile = fopen(pSrc_filename, "rb");
|
||||
if (!pInfile)
|
||||
{
|
||||
printf("Failed opening input file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Determine input file's size.
|
||||
fseek(pInfile, 0, SEEK_END);
|
||||
file_loc = ftell(pInfile);
|
||||
fseek(pInfile, 0, SEEK_SET);
|
||||
|
||||
if ((file_loc < 0) || ((mz_uint64)file_loc > INT_MAX))
|
||||
{
|
||||
// This is not a limitation of miniz or tinfl, but this example.
|
||||
printf("File is too large to be processed by this example.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
infile_size = (uint)file_loc;
|
||||
|
||||
// Open output file.
|
||||
pOutfile = fopen(pDst_filename, "wb");
|
||||
if (!pOutfile)
|
||||
{
|
||||
printf("Failed opening output file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("Input file size: %u\n", infile_size);
|
||||
|
||||
// Init the z_stream
|
||||
memset(&stream, 0, sizeof(stream));
|
||||
stream.next_in = s_inbuf;
|
||||
stream.avail_in = 0;
|
||||
stream.next_out = s_outbuf;
|
||||
stream.avail_out = BUF_SIZE;
|
||||
|
||||
if ((pMode[0] == 'c') || (pMode[0] == 'C'))
|
||||
{
|
||||
// Compression.
|
||||
uint infile_remaining = infile_size;
|
||||
|
||||
if (deflateInit(&stream, level) != Z_OK)
|
||||
{
|
||||
printf("deflateInit() failed!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
int status;
|
||||
if (!stream.avail_in)
|
||||
{
|
||||
// Input buffer is empty, so read more bytes from input file.
|
||||
uint n = my_min(BUF_SIZE, infile_remaining);
|
||||
|
||||
if (fread(s_inbuf, 1, n, pInfile) != n)
|
||||
{
|
||||
printf("Failed reading from input file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
stream.next_in = s_inbuf;
|
||||
stream.avail_in = n;
|
||||
|
||||
infile_remaining -= n;
|
||||
//printf("Input bytes remaining: %u\n", infile_remaining);
|
||||
}
|
||||
|
||||
status = deflate(&stream, infile_remaining ? Z_NO_FLUSH : Z_FINISH);
|
||||
|
||||
if ((status == Z_STREAM_END) || (!stream.avail_out))
|
||||
{
|
||||
// Output buffer is full, or compression is done, so write buffer to output file.
|
||||
uint n = BUF_SIZE - stream.avail_out;
|
||||
if (fwrite(s_outbuf, 1, n, pOutfile) != n)
|
||||
{
|
||||
printf("Failed writing to output file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
stream.next_out = s_outbuf;
|
||||
stream.avail_out = BUF_SIZE;
|
||||
}
|
||||
|
||||
if (status == Z_STREAM_END)
|
||||
break;
|
||||
else if (status != Z_OK)
|
||||
{
|
||||
printf("deflate() failed with status %i!\n", status);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (deflateEnd(&stream) != Z_OK)
|
||||
{
|
||||
printf("deflateEnd() failed!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
else if ((pMode[0] == 'd') || (pMode[0] == 'D'))
|
||||
{
|
||||
// Decompression.
|
||||
uint infile_remaining = infile_size;
|
||||
|
||||
if (inflateInit(&stream))
|
||||
{
|
||||
printf("inflateInit() failed!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
int status;
|
||||
if (!stream.avail_in)
|
||||
{
|
||||
// Input buffer is empty, so read more bytes from input file.
|
||||
uint n = my_min(BUF_SIZE, infile_remaining);
|
||||
|
||||
if (fread(s_inbuf, 1, n, pInfile) != n)
|
||||
{
|
||||
printf("Failed reading from input file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
stream.next_in = s_inbuf;
|
||||
stream.avail_in = n;
|
||||
|
||||
infile_remaining -= n;
|
||||
}
|
||||
|
||||
status = inflate(&stream, Z_SYNC_FLUSH);
|
||||
|
||||
if ((status == Z_STREAM_END) || (!stream.avail_out))
|
||||
{
|
||||
// Output buffer is full, or decompression is done, so write buffer to output file.
|
||||
uint n = BUF_SIZE - stream.avail_out;
|
||||
if (fwrite(s_outbuf, 1, n, pOutfile) != n)
|
||||
{
|
||||
printf("Failed writing to output file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
stream.next_out = s_outbuf;
|
||||
stream.avail_out = BUF_SIZE;
|
||||
}
|
||||
|
||||
if (status == Z_STREAM_END)
|
||||
break;
|
||||
else if (status != Z_OK)
|
||||
{
|
||||
printf("inflate() failed with status %i!\n", status);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (inflateEnd(&stream) != Z_OK)
|
||||
{
|
||||
printf("inflateEnd() failed!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Invalid mode!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
fclose(pInfile);
|
||||
if (EOF == fclose(pOutfile))
|
||||
{
|
||||
printf("Failed writing to output file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("Total input bytes: %u\n", (mz_uint32)stream.total_in);
|
||||
printf("Total output bytes: %u\n", (mz_uint32)stream.total_out);
|
||||
printf("Success.\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
102
code/components/jomjol_fileserver_ota/miniz/examples/example4.c
Normal file
102
code/components/jomjol_fileserver_ota/miniz/examples/example4.c
Normal file
@@ -0,0 +1,102 @@
|
||||
// example4.c - Uses tinfl.c to decompress a zlib stream in memory to an output file
|
||||
// Public domain, May 15 2011, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c.
|
||||
#include "miniz.h"
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
|
||||
typedef unsigned char uint8;
|
||||
typedef unsigned short uint16;
|
||||
typedef unsigned int uint;
|
||||
|
||||
#define my_max(a,b) (((a) > (b)) ? (a) : (b))
|
||||
#define my_min(a,b) (((a) < (b)) ? (a) : (b))
|
||||
|
||||
static int tinfl_put_buf_func(const void* pBuf, int len, void *pUser)
|
||||
{
|
||||
return len == (int)fwrite(pBuf, 1, len, (FILE*)pUser);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int status;
|
||||
FILE *pInfile, *pOutfile;
|
||||
uint infile_size, outfile_size;
|
||||
size_t in_buf_size;
|
||||
uint8 *pCmp_data;
|
||||
long file_loc;
|
||||
|
||||
if (argc != 3)
|
||||
{
|
||||
printf("Usage: example4 infile outfile\n");
|
||||
printf("Decompresses zlib stream in file infile to file outfile.\n");
|
||||
printf("Input file must be able to fit entirely in memory.\n");
|
||||
printf("example3 can be used to create compressed zlib streams.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Open input file.
|
||||
pInfile = fopen(argv[1], "rb");
|
||||
if (!pInfile)
|
||||
{
|
||||
printf("Failed opening input file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Determine input file's size.
|
||||
fseek(pInfile, 0, SEEK_END);
|
||||
file_loc = ftell(pInfile);
|
||||
fseek(pInfile, 0, SEEK_SET);
|
||||
|
||||
if ((file_loc < 0) || ((mz_uint64)file_loc > INT_MAX))
|
||||
{
|
||||
// This is not a limitation of miniz or tinfl, but this example.
|
||||
printf("File is too large to be processed by this example.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
infile_size = (uint)file_loc;
|
||||
|
||||
pCmp_data = (uint8 *)malloc(infile_size);
|
||||
if (!pCmp_data)
|
||||
{
|
||||
printf("Out of memory!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (fread(pCmp_data, 1, infile_size, pInfile) != infile_size)
|
||||
{
|
||||
printf("Failed reading input file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Open output file.
|
||||
pOutfile = fopen(argv[2], "wb");
|
||||
if (!pOutfile)
|
||||
{
|
||||
printf("Failed opening output file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("Input file size: %u\n", infile_size);
|
||||
|
||||
in_buf_size = infile_size;
|
||||
status = tinfl_decompress_mem_to_callback(pCmp_data, &in_buf_size, tinfl_put_buf_func, pOutfile, TINFL_FLAG_PARSE_ZLIB_HEADER);
|
||||
if (!status)
|
||||
{
|
||||
printf("tinfl_decompress_mem_to_callback() failed with status %i!\n", status);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
outfile_size = ftell(pOutfile);
|
||||
|
||||
fclose(pInfile);
|
||||
if (EOF == fclose(pOutfile))
|
||||
{
|
||||
printf("Failed writing to output file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("Total input bytes: %u\n", (uint)in_buf_size);
|
||||
printf("Total output bytes: %u\n", outfile_size);
|
||||
printf("Success.\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
327
code/components/jomjol_fileserver_ota/miniz/examples/example5.c
Normal file
327
code/components/jomjol_fileserver_ota/miniz/examples/example5.c
Normal file
@@ -0,0 +1,327 @@
|
||||
// example5.c - Demonstrates how to use miniz.c's low-level tdefl_compress() and tinfl_inflate() API's for simple file to file compression/decompression.
|
||||
// The low-level API's are the fastest, make no use of dynamic memory allocation, and are the most flexible functions exposed by miniz.c.
|
||||
// Public domain, April 11 2012, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c.
|
||||
// For simplicity, this example is limited to files smaller than 4GB, but this is not a limitation of miniz.c.
|
||||
|
||||
// Purposely disable a whole bunch of stuff this low-level example doesn't use.
|
||||
#define MINIZ_NO_STDIO
|
||||
#define MINIZ_NO_ARCHIVE_APIS
|
||||
#define MINIZ_NO_TIME
|
||||
#define MINIZ_NO_ZLIB_APIS
|
||||
#define MINIZ_NO_MALLOC
|
||||
#include "miniz.h"
|
||||
|
||||
// Now include stdio.h because this test uses fopen(), etc. (but we still don't want miniz.c's stdio stuff, for testing).
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
|
||||
typedef unsigned char uint8;
|
||||
typedef unsigned short uint16;
|
||||
typedef unsigned int uint;
|
||||
|
||||
#define my_max(a,b) (((a) > (b)) ? (a) : (b))
|
||||
#define my_min(a,b) (((a) < (b)) ? (a) : (b))
|
||||
|
||||
// IN_BUF_SIZE is the size of the file read buffer.
|
||||
// IN_BUF_SIZE must be >= 1
|
||||
#define IN_BUF_SIZE (1024*512)
|
||||
static uint8 s_inbuf[IN_BUF_SIZE];
|
||||
|
||||
// COMP_OUT_BUF_SIZE is the size of the output buffer used during compression.
|
||||
// COMP_OUT_BUF_SIZE must be >= 1 and <= OUT_BUF_SIZE
|
||||
#define COMP_OUT_BUF_SIZE (1024*512)
|
||||
|
||||
// OUT_BUF_SIZE is the size of the output buffer used during decompression.
|
||||
// OUT_BUF_SIZE must be a power of 2 >= TINFL_LZ_DICT_SIZE (because the low-level decompressor not only writes, but reads from the output buffer as it decompresses)
|
||||
//#define OUT_BUF_SIZE (TINFL_LZ_DICT_SIZE)
|
||||
#define OUT_BUF_SIZE (1024*512)
|
||||
static uint8 s_outbuf[OUT_BUF_SIZE];
|
||||
|
||||
// tdefl_compressor contains all the state needed by the low-level compressor so it's a pretty big struct (~300k).
|
||||
// This example makes it a global vs. putting it on the stack, of course in real-world usage you'll probably malloc() or new it.
|
||||
tdefl_compressor g_deflator;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char *pMode;
|
||||
FILE *pInfile, *pOutfile;
|
||||
uint infile_size;
|
||||
int level = 9;
|
||||
int p = 1;
|
||||
const char *pSrc_filename;
|
||||
const char *pDst_filename;
|
||||
const void *next_in = s_inbuf;
|
||||
size_t avail_in = 0;
|
||||
void *next_out = s_outbuf;
|
||||
size_t avail_out = OUT_BUF_SIZE;
|
||||
size_t total_in = 0, total_out = 0;
|
||||
long file_loc;
|
||||
|
||||
assert(COMP_OUT_BUF_SIZE <= OUT_BUF_SIZE);
|
||||
|
||||
printf("miniz.c example5 (demonstrates tinfl/tdefl)\n");
|
||||
|
||||
if (argc < 4)
|
||||
{
|
||||
printf("File to file compression/decompression using the low-level tinfl/tdefl API's.\n");
|
||||
printf("Usage: example5 [options] [mode:c or d] infile outfile\n");
|
||||
printf("\nModes:\n");
|
||||
printf("c - Compresses file infile to a zlib stream in file outfile\n");
|
||||
printf("d - Decompress zlib stream in file infile to file outfile\n");
|
||||
printf("\nOptions:\n");
|
||||
printf("-l[0-10] - Compression level, higher values are slower, 0 is none.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
while ((p < argc) && (argv[p][0] == '-'))
|
||||
{
|
||||
switch (argv[p][1])
|
||||
{
|
||||
case 'l':
|
||||
{
|
||||
level = atoi(&argv[1][2]);
|
||||
if ((level < 0) || (level > 10))
|
||||
{
|
||||
printf("Invalid level!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
printf("Invalid option: %s\n", argv[p]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
if ((argc - p) < 3)
|
||||
{
|
||||
printf("Must specify mode, input filename, and output filename after options!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
else if ((argc - p) > 3)
|
||||
{
|
||||
printf("Too many filenames!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
pMode = argv[p++];
|
||||
if (!strchr("cCdD", pMode[0]))
|
||||
{
|
||||
printf("Invalid mode!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
pSrc_filename = argv[p++];
|
||||
pDst_filename = argv[p++];
|
||||
|
||||
printf("Mode: %c, Level: %u\nInput File: \"%s\"\nOutput File: \"%s\"\n", pMode[0], level, pSrc_filename, pDst_filename);
|
||||
|
||||
// Open input file.
|
||||
pInfile = fopen(pSrc_filename, "rb");
|
||||
if (!pInfile)
|
||||
{
|
||||
printf("Failed opening input file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Determine input file's size.
|
||||
fseek(pInfile, 0, SEEK_END);
|
||||
file_loc = ftell(pInfile);
|
||||
fseek(pInfile, 0, SEEK_SET);
|
||||
|
||||
if ((file_loc < 0) || ((mz_uint64)file_loc > INT_MAX))
|
||||
{
|
||||
// This is not a limitation of miniz or tinfl, but this example.
|
||||
printf("File is too large to be processed by this example.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
infile_size = (uint)file_loc;
|
||||
|
||||
// Open output file.
|
||||
pOutfile = fopen(pDst_filename, "wb");
|
||||
if (!pOutfile)
|
||||
{
|
||||
printf("Failed opening output file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("Input file size: %u\n", infile_size);
|
||||
|
||||
if ((pMode[0] == 'c') || (pMode[0] == 'C'))
|
||||
{
|
||||
// The number of dictionary probes to use at each compression level (0-10). 0=implies fastest/minimal possible probing.
|
||||
static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 };
|
||||
|
||||
tdefl_status status;
|
||||
uint infile_remaining = infile_size;
|
||||
|
||||
// create tdefl() compatible flags (we have to compose the low-level flags ourselves, or use tdefl_create_comp_flags_from_zip_params() but that means MINIZ_NO_ZLIB_APIS can't be defined).
|
||||
mz_uint comp_flags = TDEFL_WRITE_ZLIB_HEADER | s_tdefl_num_probes[MZ_MIN(10, level)] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0);
|
||||
if (!level)
|
||||
comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS;
|
||||
|
||||
// Initialize the low-level compressor.
|
||||
status = tdefl_init(&g_deflator, NULL, NULL, comp_flags);
|
||||
if (status != TDEFL_STATUS_OKAY)
|
||||
{
|
||||
printf("tdefl_init() failed!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
avail_out = COMP_OUT_BUF_SIZE;
|
||||
|
||||
// Compression.
|
||||
for ( ; ; )
|
||||
{
|
||||
size_t in_bytes, out_bytes;
|
||||
|
||||
if (!avail_in)
|
||||
{
|
||||
// Input buffer is empty, so read more bytes from input file.
|
||||
uint n = my_min(IN_BUF_SIZE, infile_remaining);
|
||||
|
||||
if (fread(s_inbuf, 1, n, pInfile) != n)
|
||||
{
|
||||
printf("Failed reading from input file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
next_in = s_inbuf;
|
||||
avail_in = n;
|
||||
|
||||
infile_remaining -= n;
|
||||
//printf("Input bytes remaining: %u\n", infile_remaining);
|
||||
}
|
||||
|
||||
in_bytes = avail_in;
|
||||
out_bytes = avail_out;
|
||||
// Compress as much of the input as possible (or all of it) to the output buffer.
|
||||
status = tdefl_compress(&g_deflator, next_in, &in_bytes, next_out, &out_bytes, infile_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH);
|
||||
|
||||
next_in = (const char *)next_in + in_bytes;
|
||||
avail_in -= in_bytes;
|
||||
total_in += in_bytes;
|
||||
|
||||
next_out = (char *)next_out + out_bytes;
|
||||
avail_out -= out_bytes;
|
||||
total_out += out_bytes;
|
||||
|
||||
if ((status != TDEFL_STATUS_OKAY) || (!avail_out))
|
||||
{
|
||||
// Output buffer is full, or compression is done or failed, so write buffer to output file.
|
||||
uint n = COMP_OUT_BUF_SIZE - (uint)avail_out;
|
||||
if (fwrite(s_outbuf, 1, n, pOutfile) != n)
|
||||
{
|
||||
printf("Failed writing to output file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
next_out = s_outbuf;
|
||||
avail_out = COMP_OUT_BUF_SIZE;
|
||||
}
|
||||
|
||||
if (status == TDEFL_STATUS_DONE)
|
||||
{
|
||||
// Compression completed successfully.
|
||||
break;
|
||||
}
|
||||
else if (status != TDEFL_STATUS_OKAY)
|
||||
{
|
||||
// Compression somehow failed.
|
||||
printf("tdefl_compress() failed with status %i!\n", status);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((pMode[0] == 'd') || (pMode[0] == 'D'))
|
||||
{
|
||||
// Decompression.
|
||||
uint infile_remaining = infile_size;
|
||||
|
||||
tinfl_decompressor inflator;
|
||||
tinfl_init(&inflator);
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
size_t in_bytes, out_bytes;
|
||||
tinfl_status status;
|
||||
if (!avail_in)
|
||||
{
|
||||
// Input buffer is empty, so read more bytes from input file.
|
||||
uint n = my_min(IN_BUF_SIZE, infile_remaining);
|
||||
|
||||
if (fread(s_inbuf, 1, n, pInfile) != n)
|
||||
{
|
||||
printf("Failed reading from input file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
next_in = s_inbuf;
|
||||
avail_in = n;
|
||||
|
||||
infile_remaining -= n;
|
||||
}
|
||||
|
||||
in_bytes = avail_in;
|
||||
out_bytes = avail_out;
|
||||
status = tinfl_decompress(&inflator, (const mz_uint8 *)next_in, &in_bytes, s_outbuf, (mz_uint8 *)next_out, &out_bytes, (infile_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0) | TINFL_FLAG_PARSE_ZLIB_HEADER);
|
||||
|
||||
avail_in -= in_bytes;
|
||||
next_in = (const mz_uint8 *)next_in + in_bytes;
|
||||
total_in += in_bytes;
|
||||
|
||||
avail_out -= out_bytes;
|
||||
next_out = (mz_uint8 *)next_out + out_bytes;
|
||||
total_out += out_bytes;
|
||||
|
||||
if ((status <= TINFL_STATUS_DONE) || (!avail_out))
|
||||
{
|
||||
// Output buffer is full, or decompression is done, so write buffer to output file.
|
||||
uint n = OUT_BUF_SIZE - (uint)avail_out;
|
||||
if (fwrite(s_outbuf, 1, n, pOutfile) != n)
|
||||
{
|
||||
printf("Failed writing to output file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
next_out = s_outbuf;
|
||||
avail_out = OUT_BUF_SIZE;
|
||||
}
|
||||
|
||||
// If status is <= TINFL_STATUS_DONE then either decompression is done or something went wrong.
|
||||
if (status <= TINFL_STATUS_DONE)
|
||||
{
|
||||
if (status == TINFL_STATUS_DONE)
|
||||
{
|
||||
// Decompression completed successfully.
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Decompression failed.
|
||||
printf("tinfl_decompress() failed with status %i!\n", status);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Invalid mode!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
fclose(pInfile);
|
||||
if (EOF == fclose(pOutfile))
|
||||
{
|
||||
printf("Failed writing to output file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("Total input bytes: %u\n", (mz_uint32)total_in);
|
||||
printf("Total output bytes: %u\n", (mz_uint32)total_out);
|
||||
printf("Success.\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
166
code/components/jomjol_fileserver_ota/miniz/examples/example6.c
Normal file
166
code/components/jomjol_fileserver_ota/miniz/examples/example6.c
Normal file
@@ -0,0 +1,166 @@
|
||||
// example6.c - Demonstrates how to miniz's PNG writer func
|
||||
// Public domain, April 11 2012, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c.
|
||||
// Mandlebrot set code from http://rosettacode.org/wiki/Mandelbrot_set#C
|
||||
// Must link this example against libm on Linux.
|
||||
|
||||
// Purposely disable a whole bunch of stuff this low-level example doesn't use.
|
||||
#define MINIZ_NO_STDIO
|
||||
#define MINIZ_NO_TIME
|
||||
#define MINIZ_NO_ZLIB_APIS
|
||||
#include "miniz.h"
|
||||
|
||||
// Now include stdio.h because this test uses fopen(), etc. (but we still don't want miniz.c's stdio stuff, for testing).
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
|
||||
typedef unsigned char uint8;
|
||||
typedef unsigned short uint16;
|
||||
typedef unsigned int uint;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8 r, g, b;
|
||||
} rgb_t;
|
||||
|
||||
static void hsv_to_rgb(int hue, int min, int max, rgb_t *p)
|
||||
{
|
||||
const int invert = 0;
|
||||
const int saturation = 1;
|
||||
const int color_rotate = 0;
|
||||
|
||||
if (min == max) max = min + 1;
|
||||
if (invert) hue = max - (hue - min);
|
||||
if (!saturation) {
|
||||
p->r = p->g = p->b = 255 * (max - hue) / (max - min);
|
||||
return;
|
||||
} else {
|
||||
const double h_dbl = fmod(color_rotate + 1e-4 + 4.0 * (hue - min) / (max - min), 6);
|
||||
const double c_dbl = 255 * saturation;
|
||||
const double X_dbl = c_dbl * (1 - fabs(fmod(h_dbl, 2) - 1));
|
||||
const int h = (int)h_dbl;
|
||||
const int c = (int)c_dbl;
|
||||
const int X = (int)X_dbl;
|
||||
|
||||
p->r = p->g = p->b = 0;
|
||||
|
||||
switch(h) {
|
||||
case 0: p->r = c; p->g = X; return;
|
||||
case 1: p->r = X; p->g = c; return;
|
||||
case 2: p->g = c; p->b = X; return;
|
||||
case 3: p->g = X; p->b = c; return;
|
||||
case 4: p->r = X; p->b = c; return;
|
||||
default:p->r = c; p->b = X;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Image resolution
|
||||
const int iXmax = 4096;
|
||||
const int iYmax = 4096;
|
||||
|
||||
// Output filename
|
||||
static const char *pFilename = "mandelbrot.png";
|
||||
|
||||
int iX, iY;
|
||||
const double CxMin = -2.5;
|
||||
const double CxMax = 1.5;
|
||||
const double CyMin = -2.0;
|
||||
const double CyMax = 2.0;
|
||||
|
||||
double PixelWidth = (CxMax - CxMin) / iXmax;
|
||||
double PixelHeight = (CyMax - CyMin) / iYmax;
|
||||
|
||||
// Z=Zx+Zy*i ; Z0 = 0
|
||||
double Zx, Zy;
|
||||
double Zx2, Zy2; // Zx2=Zx*Zx; Zy2=Zy*Zy
|
||||
|
||||
int Iteration;
|
||||
const int IterationMax = 200;
|
||||
|
||||
// bail-out value , radius of circle
|
||||
const double EscapeRadius = 2;
|
||||
double ER2=EscapeRadius * EscapeRadius;
|
||||
|
||||
uint8 *pImage = (uint8 *)malloc(iXmax * 3 * iYmax);
|
||||
|
||||
// world ( double) coordinate = parameter plane
|
||||
double Cx,Cy;
|
||||
|
||||
int MinIter = 9999, MaxIter = 0;
|
||||
|
||||
(void)argc, (void)argv;
|
||||
|
||||
for(iY = 0; iY < iYmax; iY++)
|
||||
{
|
||||
Cy = CyMin + iY * PixelHeight;
|
||||
if (fabs(Cy) < PixelHeight/2)
|
||||
Cy = 0.0; // Main antenna
|
||||
|
||||
for(iX = 0; iX < iXmax; iX++)
|
||||
{
|
||||
uint8 *color = pImage + (iX * 3) + (iY * iXmax * 3);
|
||||
|
||||
Cx = CxMin + iX * PixelWidth;
|
||||
|
||||
// initial value of orbit = critical point Z= 0
|
||||
Zx = 0.0;
|
||||
Zy = 0.0;
|
||||
Zx2 = Zx * Zx;
|
||||
Zy2 = Zy * Zy;
|
||||
|
||||
for (Iteration=0;Iteration<IterationMax && ((Zx2+Zy2)<ER2);Iteration++)
|
||||
{
|
||||
Zy = 2 * Zx * Zy + Cy;
|
||||
Zx =Zx2 - Zy2 + Cx;
|
||||
Zx2 = Zx * Zx;
|
||||
Zy2 = Zy * Zy;
|
||||
};
|
||||
|
||||
color[0] = (uint8)Iteration;
|
||||
color[1] = (uint8)Iteration >> 8;
|
||||
color[2] = 0;
|
||||
|
||||
if (Iteration < MinIter)
|
||||
MinIter = Iteration;
|
||||
if (Iteration > MaxIter)
|
||||
MaxIter = Iteration;
|
||||
}
|
||||
}
|
||||
|
||||
for(iY = 0; iY < iYmax; iY++)
|
||||
{
|
||||
for(iX = 0; iX < iXmax; iX++)
|
||||
{
|
||||
uint8 *color = (uint8 *)(pImage + (iX * 3) + (iY * iXmax * 3));
|
||||
|
||||
uint Iterations = color[0] | (color[1] << 8U);
|
||||
|
||||
hsv_to_rgb((int)Iterations, MinIter, MaxIter, (rgb_t *)color);
|
||||
}
|
||||
}
|
||||
|
||||
// Now write the PNG image.
|
||||
{
|
||||
size_t png_data_size = 0;
|
||||
void *pPNG_data = tdefl_write_image_to_png_file_in_memory_ex(pImage, iXmax, iYmax, 3, &png_data_size, 6, MZ_FALSE);
|
||||
if (!pPNG_data)
|
||||
fprintf(stderr, "tdefl_write_image_to_png_file_in_memory_ex() failed!\n");
|
||||
else
|
||||
{
|
||||
FILE *pFile = fopen(pFilename, "wb");
|
||||
fwrite(pPNG_data, 1, png_data_size, pFile);
|
||||
fclose(pFile);
|
||||
printf("Wrote %s\n", pFilename);
|
||||
}
|
||||
|
||||
// mz_free() is by default just an alias to free() internally, but if you've overridden miniz's allocation funcs you'll probably need to call mz_free().
|
||||
mz_free(pPNG_data);
|
||||
}
|
||||
|
||||
free(pImage);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,7 @@
|
||||
/* miniz.c 2.1.0 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing
|
||||
#ifndef MINIZ_EXPORT
|
||||
#define MINIZ_EXPORT
|
||||
#endif
|
||||
/* miniz.c 3.0.0 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing
|
||||
See "unlicense" statement at the end of this file.
|
||||
Rich Geldreich <richgel99@gmail.com>, last updated Oct. 13, 2013
|
||||
Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt
|
||||
@@ -95,7 +98,7 @@
|
||||
possibility that the archive's central directory could be lost with this method if anything goes wrong, though.
|
||||
|
||||
- ZIP archive support limitations:
|
||||
No zip64 or spanning support. Extraction functions can only handle unencrypted, stored or deflated files.
|
||||
No spanning support. Extraction functions can only handle unencrypted, stored or deflated files.
|
||||
Requires streams capable of seeking.
|
||||
|
||||
* This is a header file library, like stb_image.c. To get only a header file, either cut and paste the
|
||||
@@ -114,10 +117,8 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* Defines to completely disable specific portions of miniz.c:
|
||||
If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. */
|
||||
If all macros here are defined the only functionality remaining will be CRC-32 and adler-32. */
|
||||
|
||||
/* Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. */
|
||||
/*#define MINIZ_NO_STDIO */
|
||||
@@ -127,6 +128,12 @@
|
||||
/* The current downside is the times written to your archives will be from 1979. */
|
||||
/*#define MINIZ_NO_TIME */
|
||||
|
||||
/* Define MINIZ_NO_DEFLATE_APIS to disable all compression API's. */
|
||||
/*#define MINIZ_NO_DEFLATE_APIS */
|
||||
|
||||
/* Define MINIZ_NO_INFLATE_APIS to disable all decompression API's. */
|
||||
/*#define MINIZ_NO_INFLATE_APIS */
|
||||
|
||||
/* Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. */
|
||||
/*#define MINIZ_NO_ARCHIVE_APIS */
|
||||
|
||||
@@ -145,6 +152,14 @@
|
||||
functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. */
|
||||
/*#define MINIZ_NO_MALLOC */
|
||||
|
||||
#ifdef MINIZ_NO_INFLATE_APIS
|
||||
//#define MINIZ_NO_ARCHIVE_APIS
|
||||
#endif
|
||||
|
||||
#ifdef MINIZ_NO_DEFLATE_APIS
|
||||
#define MINIZ_NO_ARCHIVE_WRITING_APIS
|
||||
#endif
|
||||
|
||||
#if defined(__TINYC__) && (defined(__linux) || defined(__linux__))
|
||||
/* TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux */
|
||||
#define MINIZ_NO_TIME
|
||||
@@ -163,18 +178,40 @@
|
||||
#define MINIZ_X86_OR_X64_CPU 0
|
||||
#endif
|
||||
|
||||
#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
|
||||
/* Set MINIZ_LITTLE_ENDIAN only if not set */
|
||||
#if !defined(MINIZ_LITTLE_ENDIAN)
|
||||
#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__)
|
||||
|
||||
#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
||||
/* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */
|
||||
#define MINIZ_LITTLE_ENDIAN 1
|
||||
#else
|
||||
#define MINIZ_LITTLE_ENDIAN 0
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#if MINIZ_X86_OR_X64_CPU
|
||||
#define MINIZ_LITTLE_ENDIAN 1
|
||||
#else
|
||||
#define MINIZ_LITTLE_ENDIAN 0
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Using unaligned loads and stores causes errors when using UBSan */
|
||||
#if defined(__has_feature)
|
||||
#if __has_feature(undefined_behavior_sanitizer)
|
||||
#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */
|
||||
#if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES)
|
||||
#if MINIZ_X86_OR_X64_CPU
|
||||
/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */
|
||||
#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
|
||||
#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0
|
||||
#define MINIZ_UNALIGNED_USE_MEMCPY
|
||||
#else
|
||||
#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0
|
||||
@@ -198,15 +235,15 @@ extern "C" {
|
||||
typedef unsigned long mz_ulong;
|
||||
|
||||
/* mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. */
|
||||
void mz_free(void *p);
|
||||
MINIZ_EXPORT void mz_free(void *p);
|
||||
|
||||
#define MZ_ADLER32_INIT (1)
|
||||
/* mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. */
|
||||
mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len);
|
||||
MINIZ_EXPORT mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len);
|
||||
|
||||
#define MZ_CRC32_INIT (0)
|
||||
/* mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. */
|
||||
mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len);
|
||||
MINIZ_EXPORT mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len);
|
||||
|
||||
/* Compression strategies. */
|
||||
enum
|
||||
@@ -222,7 +259,7 @@ enum
|
||||
#define MZ_DEFLATED 8
|
||||
|
||||
/* Heap allocation callbacks.
|
||||
Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long. */
|
||||
Note that mz_alloc_func parameter types purposely differ from zlib's: items/size is size_t, not unsigned long. */
|
||||
typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size);
|
||||
typedef void (*mz_free_func)(void *opaque, void *address);
|
||||
typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size);
|
||||
@@ -238,9 +275,9 @@ enum
|
||||
MZ_DEFAULT_COMPRESSION = -1
|
||||
};
|
||||
|
||||
#define MZ_VERSION "10.1.0"
|
||||
#define MZ_VERNUM 0xA100
|
||||
#define MZ_VER_MAJOR 10
|
||||
#define MZ_VERSION "11.0.1"
|
||||
#define MZ_VERNUM 0xB001
|
||||
#define MZ_VER_MAJOR 11
|
||||
#define MZ_VER_MINOR 1
|
||||
#define MZ_VER_REVISION 0
|
||||
#define MZ_VER_SUBREVISION 0
|
||||
@@ -304,7 +341,9 @@ typedef struct mz_stream_s
|
||||
typedef mz_stream *mz_streamp;
|
||||
|
||||
/* Returns the version string of miniz.c. */
|
||||
const char *mz_version(void);
|
||||
MINIZ_EXPORT const char *mz_version(void);
|
||||
|
||||
#ifndef MINIZ_NO_DEFLATE_APIS
|
||||
|
||||
/* mz_deflateInit() initializes a compressor with default options: */
|
||||
/* Parameters: */
|
||||
@@ -317,17 +356,17 @@ const char *mz_version(void);
|
||||
/* MZ_STREAM_ERROR if the stream is bogus. */
|
||||
/* MZ_PARAM_ERROR if the input parameters are bogus. */
|
||||
/* MZ_MEM_ERROR on out of memory. */
|
||||
int mz_deflateInit(mz_streamp pStream, int level);
|
||||
MINIZ_EXPORT int mz_deflateInit(mz_streamp pStream, int level);
|
||||
|
||||
/* mz_deflateInit2() is like mz_deflate(), except with more control: */
|
||||
/* Additional parameters: */
|
||||
/* method must be MZ_DEFLATED */
|
||||
/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) */
|
||||
/* mem_level must be between [1, 9] (it's checked but ignored by miniz.c) */
|
||||
int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy);
|
||||
MINIZ_EXPORT int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy);
|
||||
|
||||
/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). */
|
||||
int mz_deflateReset(mz_streamp pStream);
|
||||
MINIZ_EXPORT int mz_deflateReset(mz_streamp pStream);
|
||||
|
||||
/* mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. */
|
||||
/* Parameters: */
|
||||
@@ -339,34 +378,38 @@ int mz_deflateReset(mz_streamp pStream);
|
||||
/* MZ_STREAM_ERROR if the stream is bogus. */
|
||||
/* MZ_PARAM_ERROR if one of the parameters is invalid. */
|
||||
/* MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) */
|
||||
int mz_deflate(mz_streamp pStream, int flush);
|
||||
MINIZ_EXPORT int mz_deflate(mz_streamp pStream, int flush);
|
||||
|
||||
/* mz_deflateEnd() deinitializes a compressor: */
|
||||
/* Return values: */
|
||||
/* MZ_OK on success. */
|
||||
/* MZ_STREAM_ERROR if the stream is bogus. */
|
||||
int mz_deflateEnd(mz_streamp pStream);
|
||||
MINIZ_EXPORT int mz_deflateEnd(mz_streamp pStream);
|
||||
|
||||
/* mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. */
|
||||
mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len);
|
||||
MINIZ_EXPORT mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len);
|
||||
|
||||
/* Single-call compression functions mz_compress() and mz_compress2(): */
|
||||
/* Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. */
|
||||
int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len);
|
||||
int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level);
|
||||
MINIZ_EXPORT int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len);
|
||||
MINIZ_EXPORT int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level);
|
||||
|
||||
/* mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). */
|
||||
mz_ulong mz_compressBound(mz_ulong source_len);
|
||||
MINIZ_EXPORT mz_ulong mz_compressBound(mz_ulong source_len);
|
||||
|
||||
#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/
|
||||
|
||||
#ifndef MINIZ_NO_INFLATE_APIS
|
||||
|
||||
/* Initializes a decompressor. */
|
||||
int mz_inflateInit(mz_streamp pStream);
|
||||
MINIZ_EXPORT int mz_inflateInit(mz_streamp pStream);
|
||||
|
||||
/* mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: */
|
||||
/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). */
|
||||
int mz_inflateInit2(mz_streamp pStream, int window_bits);
|
||||
MINIZ_EXPORT int mz_inflateInit2(mz_streamp pStream, int window_bits);
|
||||
|
||||
/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_inflateEnd() followed by mz_inflateInit()/mz_inflateInit2(). */
|
||||
int mz_inflateReset(mz_streamp pStream);
|
||||
MINIZ_EXPORT int mz_inflateReset(mz_streamp pStream);
|
||||
|
||||
/* Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. */
|
||||
/* Parameters: */
|
||||
@@ -382,17 +425,19 @@ int mz_inflateReset(mz_streamp pStream);
|
||||
/* MZ_PARAM_ERROR if one of the parameters is invalid. */
|
||||
/* MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again */
|
||||
/* with more input data, or with more room in the output buffer (except when using single call decompression, described above). */
|
||||
int mz_inflate(mz_streamp pStream, int flush);
|
||||
MINIZ_EXPORT int mz_inflate(mz_streamp pStream, int flush);
|
||||
|
||||
/* Deinitializes a decompressor. */
|
||||
int mz_inflateEnd(mz_streamp pStream);
|
||||
MINIZ_EXPORT int mz_inflateEnd(mz_streamp pStream);
|
||||
|
||||
/* Single-call decompression. */
|
||||
/* Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. */
|
||||
int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len);
|
||||
MINIZ_EXPORT int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len);
|
||||
MINIZ_EXPORT int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len);
|
||||
#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/
|
||||
|
||||
/* Returns a string description of the specified error code, or NULL if the error code is invalid. */
|
||||
const char *mz_error(int err);
|
||||
MINIZ_EXPORT const char *mz_error(int err);
|
||||
|
||||
/* Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. */
|
||||
/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. */
|
||||
@@ -440,6 +485,8 @@ typedef void *const voidpc;
|
||||
#define free_func mz_free_func
|
||||
#define internal_state mz_internal_state
|
||||
#define z_stream mz_stream
|
||||
|
||||
#ifndef MINIZ_NO_DEFLATE_APIS
|
||||
#define deflateInit mz_deflateInit
|
||||
#define deflateInit2 mz_deflateInit2
|
||||
#define deflateReset mz_deflateReset
|
||||
@@ -449,12 +496,18 @@ typedef void *const voidpc;
|
||||
#define compress mz_compress
|
||||
#define compress2 mz_compress2
|
||||
#define compressBound mz_compressBound
|
||||
#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/
|
||||
|
||||
#ifndef MINIZ_NO_INFLATE_APIS
|
||||
#define inflateInit mz_inflateInit
|
||||
#define inflateInit2 mz_inflateInit2
|
||||
#define inflateReset mz_inflateReset
|
||||
#define inflate mz_inflate
|
||||
#define inflateEnd mz_inflateEnd
|
||||
#define uncompress mz_uncompress
|
||||
#define uncompress2 mz_uncompress2
|
||||
#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/
|
||||
|
||||
#define crc32 mz_crc32
|
||||
#define adler32 mz_adler32
|
||||
#define MAX_WBITS 15
|
||||
@@ -475,12 +528,19 @@ typedef void *const voidpc;
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
|
||||
/* ------------------- Types and macros */
|
||||
typedef unsigned char mz_uint8;
|
||||
typedef signed short mz_int16;
|
||||
@@ -511,7 +571,8 @@ typedef int mz_bool;
|
||||
#ifdef MINIZ_NO_TIME
|
||||
typedef struct mz_dummy_time_t_tag
|
||||
{
|
||||
int m_dummy;
|
||||
mz_uint32 m_dummy1;
|
||||
mz_uint32 m_dummy2;
|
||||
} mz_dummy_time_t;
|
||||
#define MZ_TIME_T mz_dummy_time_t
|
||||
#else
|
||||
@@ -533,6 +594,8 @@ typedef struct mz_dummy_time_t_tag
|
||||
#define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj))
|
||||
#define MZ_CLEAR_ARR(obj) memset((obj), 0, sizeof(obj))
|
||||
#define MZ_CLEAR_PTR(obj) memset((obj), 0, sizeof(*obj))
|
||||
|
||||
#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
|
||||
#define MZ_READ_LE16(p) *((const mz_uint16 *)(p))
|
||||
@@ -556,9 +619,9 @@ typedef struct mz_dummy_time_t_tag
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern void *miniz_def_alloc_func(void *opaque, size_t items, size_t size);
|
||||
extern void miniz_def_free_func(void *opaque, void *address);
|
||||
extern void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size);
|
||||
extern MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size);
|
||||
extern MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address);
|
||||
extern MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size);
|
||||
|
||||
#define MZ_UINT16_MAX (0xFFFFU)
|
||||
#define MZ_UINT32_MAX (0xFFFFFFFFU)
|
||||
@@ -566,9 +629,11 @@ extern void *miniz_def_realloc_func(void *opaque, void *address, size_t items, s
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
|
||||
#ifndef MINIZ_NO_DEFLATE_APIS
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -616,11 +681,11 @@ enum
|
||||
/* Function returns a pointer to the compressed data, or NULL on failure. */
|
||||
/* *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. */
|
||||
/* The caller must free() the returned block when it's no longer needed. */
|
||||
void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags);
|
||||
MINIZ_EXPORT void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags);
|
||||
|
||||
/* tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. */
|
||||
/* Returns 0 on failure. */
|
||||
size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags);
|
||||
MINIZ_EXPORT size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags);
|
||||
|
||||
/* Compresses an image to a compressed PNG file in memory. */
|
||||
/* On entry: */
|
||||
@@ -632,14 +697,14 @@ size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void
|
||||
/* Function returns a pointer to the compressed data, or NULL on failure. */
|
||||
/* *pLen_out will be set to the size of the PNG image file. */
|
||||
/* The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. */
|
||||
void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip);
|
||||
void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out);
|
||||
MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip);
|
||||
MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out);
|
||||
|
||||
/* Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. */
|
||||
typedef mz_bool (*tdefl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser);
|
||||
|
||||
/* tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. */
|
||||
mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
|
||||
MINIZ_EXPORT mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
|
||||
|
||||
enum
|
||||
{
|
||||
@@ -727,39 +792,43 @@ typedef struct
|
||||
/* pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. */
|
||||
/* If pBut_buf_func is NULL the user should always call the tdefl_compress() API. */
|
||||
/* flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) */
|
||||
tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
|
||||
MINIZ_EXPORT tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
|
||||
|
||||
/* Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. */
|
||||
tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush);
|
||||
MINIZ_EXPORT tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush);
|
||||
|
||||
/* tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. */
|
||||
/* tdefl_compress_buffer() always consumes the entire input buffer. */
|
||||
tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush);
|
||||
MINIZ_EXPORT tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush);
|
||||
|
||||
tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d);
|
||||
mz_uint32 tdefl_get_adler32(tdefl_compressor *d);
|
||||
MINIZ_EXPORT tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d);
|
||||
MINIZ_EXPORT mz_uint32 tdefl_get_adler32(tdefl_compressor *d);
|
||||
|
||||
/* Create tdefl_compress() flags given zlib-style compression parameters. */
|
||||
/* level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) */
|
||||
/* window_bits may be -15 (raw deflate) or 15 (zlib) */
|
||||
/* strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED */
|
||||
mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy);
|
||||
MINIZ_EXPORT mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy);
|
||||
|
||||
#ifndef MINIZ_NO_MALLOC
|
||||
/* Allocate the tdefl_compressor structure in C so that */
|
||||
/* non-C language bindings to tdefl_ API don't need to worry about */
|
||||
/* structure size and allocation mechanism. */
|
||||
tdefl_compressor *tdefl_compressor_alloc(void);
|
||||
void tdefl_compressor_free(tdefl_compressor *pComp);
|
||||
MINIZ_EXPORT tdefl_compressor *tdefl_compressor_alloc(void);
|
||||
MINIZ_EXPORT void tdefl_compressor_free(tdefl_compressor *pComp);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#pragma once
|
||||
|
||||
#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/
|
||||
#pragma once
|
||||
|
||||
/* ------------------- Low-level Decompression API Definitions */
|
||||
|
||||
#ifndef MINIZ_NO_INFLATE_APIS
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -784,17 +853,17 @@ enum
|
||||
/* Function returns a pointer to the decompressed data, or NULL on failure. */
|
||||
/* *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. */
|
||||
/* The caller must call mz_free() on the returned block when it's no longer needed. */
|
||||
void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags);
|
||||
MINIZ_EXPORT void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags);
|
||||
|
||||
/* tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. */
|
||||
/* Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. */
|
||||
#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1))
|
||||
size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags);
|
||||
MINIZ_EXPORT size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags);
|
||||
|
||||
/* tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. */
|
||||
/* Returns 1 on success or 0 on failure. */
|
||||
typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser);
|
||||
int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
|
||||
MINIZ_EXPORT int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
|
||||
|
||||
struct tinfl_decompressor_tag;
|
||||
typedef struct tinfl_decompressor_tag tinfl_decompressor;
|
||||
@@ -803,8 +872,8 @@ typedef struct tinfl_decompressor_tag tinfl_decompressor;
|
||||
/* Allocate the tinfl_decompressor structure in C so that */
|
||||
/* non-C language bindings to tinfl_ API don't need to worry about */
|
||||
/* structure size and allocation mechanism. */
|
||||
tinfl_decompressor *tinfl_decompressor_alloc(void);
|
||||
void tinfl_decompressor_free(tinfl_decompressor *pDecomp);
|
||||
MINIZ_EXPORT tinfl_decompressor *tinfl_decompressor_alloc(void);
|
||||
MINIZ_EXPORT void tinfl_decompressor_free(tinfl_decompressor *pDecomp);
|
||||
#endif
|
||||
|
||||
/* Max size of LZ dictionary. */
|
||||
@@ -855,7 +924,7 @@ typedef enum {
|
||||
|
||||
/* Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. */
|
||||
/* This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. */
|
||||
tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags);
|
||||
MINIZ_EXPORT tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags);
|
||||
|
||||
/* Internal/private bits follow. */
|
||||
enum
|
||||
@@ -868,12 +937,6 @@ enum
|
||||
TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0];
|
||||
mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2];
|
||||
} tinfl_huff_table;
|
||||
|
||||
#if MINIZ_HAS_64BIT_REGISTERS
|
||||
#define TINFL_USE_64BIT_BITBUF 1
|
||||
#else
|
||||
@@ -893,7 +956,13 @@ struct tinfl_decompressor_tag
|
||||
mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES];
|
||||
tinfl_bit_buf_t m_bit_buf;
|
||||
size_t m_dist_from_out_buf_start;
|
||||
tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES];
|
||||
mz_int16 m_look_up[TINFL_MAX_HUFF_TABLES][TINFL_FAST_LOOKUP_SIZE];
|
||||
mz_int16 m_tree_0[TINFL_MAX_HUFF_SYMBOLS_0 * 2];
|
||||
mz_int16 m_tree_1[TINFL_MAX_HUFF_SYMBOLS_1 * 2];
|
||||
mz_int16 m_tree_2[TINFL_MAX_HUFF_SYMBOLS_2 * 2];
|
||||
mz_uint8 m_code_size_0[TINFL_MAX_HUFF_SYMBOLS_0];
|
||||
mz_uint8 m_code_size_1[TINFL_MAX_HUFF_SYMBOLS_1];
|
||||
mz_uint8 m_code_size_2[TINFL_MAX_HUFF_SYMBOLS_2];
|
||||
mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137];
|
||||
};
|
||||
|
||||
@@ -901,6 +970,8 @@ struct tinfl_decompressor_tag
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
@@ -934,10 +1005,6 @@ typedef struct
|
||||
mz_uint16 m_bit_flag;
|
||||
mz_uint16 m_method;
|
||||
|
||||
#ifndef MINIZ_NO_TIME
|
||||
MZ_TIME_T m_time;
|
||||
#endif
|
||||
|
||||
/* CRC-32 of uncompressed data. */
|
||||
mz_uint32 m_crc32;
|
||||
|
||||
@@ -974,6 +1041,11 @@ typedef struct
|
||||
/* Guaranteed to be zero terminated, may be truncated to fit. */
|
||||
char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE];
|
||||
|
||||
#ifdef MINIZ_NO_TIME
|
||||
MZ_TIME_T m_padding;
|
||||
#else
|
||||
MZ_TIME_T m_time;
|
||||
#endif
|
||||
} mz_zip_archive_file_stat;
|
||||
|
||||
typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n);
|
||||
@@ -999,7 +1071,10 @@ typedef enum {
|
||||
MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY = 0x2000, /* validate the local headers, but don't decompress the entire file and check the crc32 */
|
||||
MZ_ZIP_FLAG_WRITE_ZIP64 = 0x4000, /* always use the zip64 file format, instead of the original zip file format with automatic switch to zip64. Use as flags parameter with mz_zip_writer_init*_v2 */
|
||||
MZ_ZIP_FLAG_WRITE_ALLOW_READING = 0x8000,
|
||||
MZ_ZIP_FLAG_ASCII_FILENAME = 0x10000
|
||||
MZ_ZIP_FLAG_ASCII_FILENAME = 0x10000,
|
||||
/*After adding a compressed file, seek back
|
||||
to local file header and set the correct sizes*/
|
||||
MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE = 0x20000
|
||||
} mz_zip_flags;
|
||||
|
||||
typedef enum {
|
||||
@@ -1082,9 +1157,7 @@ typedef struct
|
||||
mz_uint flags;
|
||||
|
||||
int status;
|
||||
#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS
|
||||
mz_uint file_crc32;
|
||||
#endif
|
||||
|
||||
mz_uint64 read_buf_size, read_buf_ofs, read_buf_avail, comp_remaining, out_buf_ofs, cur_file_ofs;
|
||||
mz_zip_archive_file_stat file_stat;
|
||||
void *pRead_buf;
|
||||
@@ -1094,149 +1167,157 @@ typedef struct
|
||||
|
||||
tinfl_decompressor inflator;
|
||||
|
||||
#ifdef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS
|
||||
mz_uint padding;
|
||||
#else
|
||||
mz_uint file_crc32;
|
||||
#endif
|
||||
|
||||
} mz_zip_reader_extract_iter_state;
|
||||
|
||||
/* -------- ZIP reading */
|
||||
|
||||
/* Inits a ZIP archive reader. */
|
||||
/* These functions read and validate the archive's central directory. */
|
||||
mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags);
|
||||
MINIZ_EXPORT mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags);
|
||||
|
||||
mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags);
|
||||
MINIZ_EXPORT mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags);
|
||||
|
||||
#ifndef MINIZ_NO_STDIO
|
||||
/* Read a archive from a disk file. */
|
||||
/* file_start_ofs is the file offset where the archive actually begins, or 0. */
|
||||
/* actual_archive_size is the true total size of the archive, which may be smaller than the file's actual size on disk. If zero the entire file is treated as the archive. */
|
||||
mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags);
|
||||
mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size);
|
||||
MINIZ_EXPORT mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags);
|
||||
MINIZ_EXPORT mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size);
|
||||
|
||||
/* Read an archive from an already opened FILE, beginning at the current file position. */
|
||||
/* The archive is assumed to be archive_size bytes long. If archive_size is < 0, then the entire rest of the file is assumed to contain the archive. */
|
||||
/* The archive is assumed to be archive_size bytes long. If archive_size is 0, then the entire rest of the file is assumed to contain the archive. */
|
||||
/* The FILE will NOT be closed when mz_zip_reader_end() is called. */
|
||||
mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags);
|
||||
MINIZ_EXPORT mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags);
|
||||
#endif
|
||||
|
||||
/* Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. */
|
||||
mz_bool mz_zip_reader_end(mz_zip_archive *pZip);
|
||||
MINIZ_EXPORT mz_bool mz_zip_reader_end(mz_zip_archive *pZip);
|
||||
|
||||
/* -------- ZIP reading or writing */
|
||||
|
||||
/* Clears a mz_zip_archive struct to all zeros. */
|
||||
/* Important: This must be done before passing the struct to any mz_zip functions. */
|
||||
void mz_zip_zero_struct(mz_zip_archive *pZip);
|
||||
MINIZ_EXPORT void mz_zip_zero_struct(mz_zip_archive *pZip);
|
||||
|
||||
mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip);
|
||||
mz_zip_type mz_zip_get_type(mz_zip_archive *pZip);
|
||||
MINIZ_EXPORT mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip);
|
||||
MINIZ_EXPORT mz_zip_type mz_zip_get_type(mz_zip_archive *pZip);
|
||||
|
||||
/* Returns the total number of files in the archive. */
|
||||
mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip);
|
||||
MINIZ_EXPORT mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip);
|
||||
|
||||
mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip);
|
||||
mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip);
|
||||
MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip);
|
||||
MINIZ_EXPORT mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip);
|
||||
MINIZ_EXPORT mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip);
|
||||
MINIZ_EXPORT MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip);
|
||||
|
||||
/* Reads n bytes of raw archive data, starting at file offset file_ofs, to pBuf. */
|
||||
size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n);
|
||||
MINIZ_EXPORT size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n);
|
||||
|
||||
/* All mz_zip funcs set the m_last_error field in the mz_zip_archive struct. These functions retrieve/manipulate this field. */
|
||||
/* Note that the m_last_error functionality is not thread safe. */
|
||||
mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num);
|
||||
mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip);
|
||||
mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip);
|
||||
mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip);
|
||||
const char *mz_zip_get_error_string(mz_zip_error mz_err);
|
||||
MINIZ_EXPORT mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num);
|
||||
MINIZ_EXPORT mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip);
|
||||
MINIZ_EXPORT mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip);
|
||||
MINIZ_EXPORT mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip);
|
||||
MINIZ_EXPORT const char *mz_zip_get_error_string(mz_zip_error mz_err);
|
||||
|
||||
/* MZ_TRUE if the archive file entry is a directory entry. */
|
||||
mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index);
|
||||
MINIZ_EXPORT mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index);
|
||||
|
||||
/* MZ_TRUE if the file is encrypted/strong encrypted. */
|
||||
mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index);
|
||||
MINIZ_EXPORT mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index);
|
||||
|
||||
/* MZ_TRUE if the compression method is supported, and the file is not encrypted, and the file is not a compressed patch file. */
|
||||
mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index);
|
||||
MINIZ_EXPORT mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index);
|
||||
|
||||
/* Retrieves the filename of an archive file entry. */
|
||||
/* Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. */
|
||||
mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size);
|
||||
MINIZ_EXPORT mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size);
|
||||
|
||||
/* Attempts to locates a file in the archive's central directory. */
|
||||
/* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */
|
||||
/* Returns -1 if the file cannot be found. */
|
||||
int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags);
|
||||
int mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *file_index);
|
||||
MINIZ_EXPORT int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags);
|
||||
MINIZ_EXPORT mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *file_index);
|
||||
|
||||
/* Returns detailed information about an archive file entry. */
|
||||
mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat);
|
||||
MINIZ_EXPORT mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat);
|
||||
|
||||
/* MZ_TRUE if the file is in zip64 format. */
|
||||
/* A file is considered zip64 if it contained a zip64 end of central directory marker, or if it contained any zip64 extended file information fields in the central directory. */
|
||||
mz_bool mz_zip_is_zip64(mz_zip_archive *pZip);
|
||||
MINIZ_EXPORT mz_bool mz_zip_is_zip64(mz_zip_archive *pZip);
|
||||
|
||||
/* Returns the total central directory size in bytes. */
|
||||
/* The current max supported size is <= MZ_UINT32_MAX. */
|
||||
size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip);
|
||||
MINIZ_EXPORT size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip);
|
||||
|
||||
/* Extracts a archive file to a memory buffer using no memory allocation. */
|
||||
/* There must be at least enough room on the stack to store the inflator's state (~34KB or so). */
|
||||
mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size);
|
||||
mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size);
|
||||
MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size);
|
||||
MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size);
|
||||
|
||||
/* Extracts a archive file to a memory buffer. */
|
||||
mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags);
|
||||
mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags);
|
||||
MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags);
|
||||
MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags);
|
||||
|
||||
/* Extracts a archive file to a dynamically allocated heap buffer. */
|
||||
/* The memory will be allocated via the mz_zip_archive's alloc/realloc functions. */
|
||||
/* Returns NULL and sets the last error on failure. */
|
||||
void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags);
|
||||
void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags);
|
||||
MINIZ_EXPORT void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags);
|
||||
MINIZ_EXPORT void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags);
|
||||
|
||||
/* Extracts a archive file using a callback function to output the file's data. */
|
||||
mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags);
|
||||
mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags);
|
||||
MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags);
|
||||
MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags);
|
||||
|
||||
/* Extract a file iteratively */
|
||||
mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags);
|
||||
mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags);
|
||||
size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size);
|
||||
mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState);
|
||||
MINIZ_EXPORT mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags);
|
||||
MINIZ_EXPORT mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags);
|
||||
MINIZ_EXPORT size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size);
|
||||
MINIZ_EXPORT mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState);
|
||||
|
||||
#ifndef MINIZ_NO_STDIO
|
||||
/* Extracts a archive file to a disk file and sets its last accessed and modified times. */
|
||||
/* This function only extracts files, not archive directory records. */
|
||||
mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags);
|
||||
mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags);
|
||||
MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags);
|
||||
MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags);
|
||||
|
||||
/* Extracts a archive file starting at the current position in the destination FILE stream. */
|
||||
mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *File, mz_uint flags);
|
||||
mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags);
|
||||
MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *File, mz_uint flags);
|
||||
MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
/* TODO */
|
||||
typedef void *mz_zip_streaming_extract_state_ptr;
|
||||
mz_zip_streaming_extract_state_ptr mz_zip_streaming_extract_begin(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags);
|
||||
uint64_t mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState);
|
||||
uint64_t mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState);
|
||||
mz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, uint64_t new_ofs);
|
||||
mz_uint64 mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState);
|
||||
mz_uint64 mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState);
|
||||
mz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, mz_uint64 new_ofs);
|
||||
size_t mz_zip_streaming_extract_read(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, void *pBuf, size_t buf_size);
|
||||
mz_bool mz_zip_streaming_extract_end(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState);
|
||||
#endif
|
||||
|
||||
/* This function compares the archive's local headers, the optional local zip64 extended information block, and the optional descriptor following the compressed data vs. the data in the central directory. */
|
||||
/* It also validates that each file can be successfully uncompressed unless the MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY is specified. */
|
||||
mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags);
|
||||
MINIZ_EXPORT mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags);
|
||||
|
||||
/* Validates an entire archive by calling mz_zip_validate_file() on each file. */
|
||||
mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags);
|
||||
MINIZ_EXPORT mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags);
|
||||
|
||||
/* Misc utils/helpers, valid for ZIP reading or writing */
|
||||
mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr);
|
||||
mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr);
|
||||
MINIZ_EXPORT mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr);
|
||||
#ifndef MINIZ_NO_STDIO
|
||||
MINIZ_EXPORT mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr);
|
||||
#endif
|
||||
|
||||
/* Universal end function - calls either mz_zip_reader_end() or mz_zip_writer_end(). */
|
||||
mz_bool mz_zip_end(mz_zip_archive *pZip);
|
||||
MINIZ_EXPORT mz_bool mz_zip_end(mz_zip_archive *pZip);
|
||||
|
||||
/* -------- ZIP writing */
|
||||
|
||||
@@ -1245,16 +1326,16 @@ mz_bool mz_zip_end(mz_zip_archive *pZip);
|
||||
/* Inits a ZIP archive writer. */
|
||||
/*Set pZip->m_pWrite (and pZip->m_pIO_opaque) before calling mz_zip_writer_init or mz_zip_writer_init_v2*/
|
||||
/*The output is streamable, i.e. file_ofs in mz_file_write_func always increases only by n*/
|
||||
mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size);
|
||||
mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags);
|
||||
MINIZ_EXPORT mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size);
|
||||
MINIZ_EXPORT mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags);
|
||||
|
||||
mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size);
|
||||
mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags);
|
||||
MINIZ_EXPORT mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size);
|
||||
MINIZ_EXPORT mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags);
|
||||
|
||||
#ifndef MINIZ_NO_STDIO
|
||||
mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning);
|
||||
mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags);
|
||||
mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags);
|
||||
MINIZ_EXPORT mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning);
|
||||
MINIZ_EXPORT mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags);
|
||||
MINIZ_EXPORT mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags);
|
||||
#endif
|
||||
|
||||
/* Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. */
|
||||
@@ -1263,56 +1344,57 @@ mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint f
|
||||
/* Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. */
|
||||
/* Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before */
|
||||
/* the archive is finalized the file's central directory will be hosed. */
|
||||
mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename);
|
||||
mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags);
|
||||
MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename);
|
||||
MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags);
|
||||
|
||||
/* Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. */
|
||||
/* To add a directory entry, call this method with an archive name ending in a forwardslash with an empty buffer. */
|
||||
/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */
|
||||
mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags);
|
||||
MINIZ_EXPORT mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags);
|
||||
|
||||
/* Like mz_zip_writer_add_mem(), except you can specify a file comment field, and optionally supply the function with already compressed data. */
|
||||
/* uncomp_size/uncomp_crc32 are only used if the MZ_ZIP_FLAG_COMPRESSED_DATA flag is specified. */
|
||||
mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags,
|
||||
mz_uint64 uncomp_size, mz_uint32 uncomp_crc32);
|
||||
MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags,
|
||||
mz_uint64 uncomp_size, mz_uint32 uncomp_crc32);
|
||||
|
||||
mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags,
|
||||
mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data_local, mz_uint user_extra_data_local_len,
|
||||
const char *user_extra_data_central, mz_uint user_extra_data_central_len);
|
||||
MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags,
|
||||
mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data_local, mz_uint user_extra_data_local_len,
|
||||
const char *user_extra_data_central, mz_uint user_extra_data_central_len);
|
||||
|
||||
/* Adds the contents of a file to an archive. This function also records the disk file's modified time into the archive. */
|
||||
/* File data is supplied via a read callback function. User mz_zip_writer_add_(c)file to add a file directly.*/
|
||||
mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 size_to_add,
|
||||
MINIZ_EXPORT mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 max_size,
|
||||
const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len,
|
||||
const char *user_extra_data_central, mz_uint user_extra_data_central_len);
|
||||
|
||||
|
||||
#ifndef MINIZ_NO_STDIO
|
||||
/* Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. */
|
||||
/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */
|
||||
mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);
|
||||
MINIZ_EXPORT mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);
|
||||
|
||||
/* Like mz_zip_writer_add_file(), except the file data is read from the specified FILE stream. */
|
||||
mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 size_to_add,
|
||||
MINIZ_EXPORT mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size,
|
||||
const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len,
|
||||
const char *user_extra_data_central, mz_uint user_extra_data_central_len);
|
||||
#endif
|
||||
|
||||
/* Adds a file to an archive by fully cloning the data from another archive. */
|
||||
/* This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data (it may add or modify the zip64 local header extra data field), and the optional descriptor following the compressed data. */
|
||||
mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index);
|
||||
MINIZ_EXPORT mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index);
|
||||
|
||||
/* Finalizes the archive by writing the central directory records followed by the end of central directory record. */
|
||||
/* After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). */
|
||||
/* An archive must be manually finalized by calling this function for it to be valid. */
|
||||
mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip);
|
||||
MINIZ_EXPORT mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip);
|
||||
|
||||
/* Finalizes a heap archive, returning a poiner to the heap block and its size. */
|
||||
/* Finalizes a heap archive, returning a pointer to the heap block and its size. */
|
||||
/* The heap block will be allocated using the mz_zip_archive's alloc/realloc callbacks. */
|
||||
mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize);
|
||||
MINIZ_EXPORT mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize);
|
||||
|
||||
/* Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. */
|
||||
/* Note for the archive to be valid, it *must* have been finalized before ending (this function will not do it for you). */
|
||||
mz_bool mz_zip_writer_end(mz_zip_archive *pZip);
|
||||
MINIZ_EXPORT mz_bool mz_zip_writer_end(mz_zip_archive *pZip);
|
||||
|
||||
/* -------- Misc. high-level helper functions: */
|
||||
|
||||
@@ -1320,14 +1402,16 @@ mz_bool mz_zip_writer_end(mz_zip_archive *pZip);
|
||||
/* Note this is NOT a fully safe operation. If it crashes or dies in some way your archive can be left in a screwed up state (without a central directory). */
|
||||
/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */
|
||||
/* TODO: Perhaps add an option to leave the existing central dir in place in case the add dies? We could then truncate the file (so the old central dir would be at the end) if something goes wrong. */
|
||||
mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);
|
||||
mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr);
|
||||
MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);
|
||||
MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr);
|
||||
|
||||
#ifndef MINIZ_NO_STDIO
|
||||
/* Reads a single file from an archive into a heap block. */
|
||||
/* If pComment is not NULL, only the file with the specified comment will be extracted. */
|
||||
/* Returns NULL on failure. */
|
||||
void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags);
|
||||
void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr);
|
||||
MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags);
|
||||
MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr);
|
||||
#endif
|
||||
|
||||
#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */
|
||||
|
||||
46
code/components/jomjol_fileserver_ota/miniz/readme.md
Normal file
46
code/components/jomjol_fileserver_ota/miniz/readme.md
Normal file
@@ -0,0 +1,46 @@
|
||||
## Miniz
|
||||
|
||||
Miniz is a lossless, high performance data compression library in a single source file that implements the zlib (RFC 1950) and Deflate (RFC 1951) compressed data format specification standards. It supports the most commonly used functions exported by the zlib library, but is a completely independent implementation so zlib's licensing requirements do not apply. Miniz also contains simple to use functions for writing .PNG format image files and reading/writing/appending .ZIP format archives. Miniz's compression speed has been tuned to be comparable to zlib's, and it also has a specialized real-time compressor function designed to compare well against fastlz/minilzo.
|
||||
|
||||
## Usage
|
||||
|
||||
Releases are available at the [releases page](https://github.com/richgel999/miniz/releases) as a pair of `miniz.c`/`miniz.h` files which can be simply added to a project. To create this file pair the different source and header files are [amalgamated](https://www.sqlite.org/amalgamation.html) during build. Alternatively use as cmake or meson module (or build system of your choice).
|
||||
|
||||
## Features
|
||||
|
||||
* MIT licensed
|
||||
* A portable, single source and header file library written in plain C. Tested with GCC, clang and Visual Studio.
|
||||
* Easily tuned and trimmed down by defines
|
||||
* A drop-in replacement for zlib's most used API's (tested in several open source projects that use zlib, such as libpng and libzip).
|
||||
* Fills a single threaded performance vs. compression ratio gap between several popular real-time compressors and zlib. For example, at level 1, miniz.c compresses around 5-9% better than minilzo, but is approx. 35% slower. At levels 2-9, miniz.c is designed to compare favorably against zlib's ratio and speed. See the miniz performance comparison page for example timings.
|
||||
* Not a block based compressor: miniz.c fully supports stream based processing using a coroutine-style implementation. The zlib-style API functions can be called a single byte at a time if that's all you've got.
|
||||
* Easy to use. The low-level compressor (tdefl) and decompressor (tinfl) have simple state structs which can be saved/restored as needed with simple memcpy's. The low-level codec API's don't use the heap in any way.
|
||||
* Entire inflater (including optional zlib header parsing and Adler-32 checking) is implemented in a single function as a coroutine, which is separately available in a small (~550 line) source file: miniz_tinfl.c
|
||||
* A fairly complete (but totally optional) set of .ZIP archive manipulation and extraction API's. The archive functionality is intended to solve common problems encountered in embedded, mobile, or game development situations. (The archive API's are purposely just powerful enough to write an entire archiver given a bit of additional higher-level logic.)
|
||||
|
||||
## Building miniz - Using vcpkg
|
||||
|
||||
You can download and install miniz using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager:
|
||||
|
||||
git clone https://github.com/Microsoft/vcpkg.git
|
||||
cd vcpkg
|
||||
./bootstrap-vcpkg.sh
|
||||
./vcpkg integrate install
|
||||
./vcpkg install miniz
|
||||
|
||||
The miniz port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
|
||||
|
||||
## Known Problems
|
||||
|
||||
* No support for encrypted archives. Not sure how useful this stuff is in practice.
|
||||
* Minimal documentation. The assumption is that the user is already familiar with the basic zlib API. I need to write an API wiki - for now I've tried to place key comments before each enum/API, and I've included 6 examples that demonstrate how to use the module's major features.
|
||||
|
||||
## Special Thanks
|
||||
|
||||
Thanks to Alex Evans for the PNG writer function. Also, thanks to Paul Holden and Thorsten Scheuermann for feedback and testing, Matt Pritchard for all his encouragement, and Sean Barrett's various public domain libraries for inspiration (and encouraging me to write miniz.c in C, which was much more enjoyable and less painful than I thought it would be considering I've been programming in C++ for so long).
|
||||
|
||||
Thanks to Bruce Dawson for reporting a problem with the level_and_flags archive API parameter (which is fixed in v1.12) and general feedback, and Janez Zemva for indirectly encouraging me into writing more examples.
|
||||
|
||||
## Patents
|
||||
|
||||
I was recently asked if miniz avoids patent issues. miniz purposely uses the same core algorithms as the ones used by zlib. The compressor uses vanilla hash chaining as described [here](https://datatracker.ietf.org/doc/html/rfc1951#section-4). Also see the [gzip FAQ](https://web.archive.org/web/20160308045258/http://www.gzip.org/#faq11). In my opinion, if miniz falls prey to a patent attack then zlib/gzip are likely to be at serious risk too.
|
||||
8
code/components/jomjol_fileserver_ota/miniz/readme2.md
Normal file
8
code/components/jomjol_fileserver_ota/miniz/readme2.md
Normal file
@@ -0,0 +1,8 @@
|
||||
The files in this folder are a direct extraction of the miniz release from https://github.com/richgel999/miniz/releases
|
||||
|
||||
It should be possible to include the repo directly as a submodule, how ever it causes various issues. See also
|
||||
- https://github.com/richgel999/miniz/issues/145
|
||||
- https://github.com/espressif/esptool/pull/500#issuecomment-574879468
|
||||
|
||||
For sumplicity we therefore use the release files as suggested in the readme.
|
||||
Additionally we added the CMakeLists.txt and this readme.
|
||||
1236
code/components/jomjol_fileserver_ota/server_file.cpp
Normal file
1236
code/components/jomjol_fileserver_ota/server_file.cpp
Normal file
File diff suppressed because it is too large
Load Diff
21
code/components/jomjol_fileserver_ota/server_file.h
Normal file
21
code/components/jomjol_fileserver_ota/server_file.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SERVERFILE_H
|
||||
#define SERVERFILE_H
|
||||
|
||||
#include <esp_http_server.h>
|
||||
#include <string>
|
||||
|
||||
void register_server_file_uri(httpd_handle_t server, const char *base_path);
|
||||
|
||||
void unzip(std::string _in_zip_file, std::string _target_directory);
|
||||
std::string unzip_new(std::string _in_zip_file, std::string _target_zip, std::string _target_bin, std::string _main = "/sdcard/", bool _initial_setup = false);
|
||||
|
||||
|
||||
void delete_all_in_directory(std::string _directory);
|
||||
|
||||
esp_err_t get_tflite_file_handler(httpd_req_t *req);
|
||||
esp_err_t get_data_file_handler(httpd_req_t *req);
|
||||
esp_err_t get_numbers_file_handler(httpd_req_t *req);
|
||||
|
||||
#endif //SERVERFILE_H
|
||||
@@ -5,35 +5,71 @@
|
||||
#include <sys/param.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <dirent.h>
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "Helper.h"
|
||||
|
||||
#include "esp_http_server.h"
|
||||
|
||||
|
||||
static const char *TAG = "serverhelp";
|
||||
|
||||
#define SCRATCH_BUFSIZE 8192
|
||||
char scratch[SCRATCH_BUFSIZE];
|
||||
#include "../../include/defines.h"
|
||||
|
||||
|
||||
#define IS_FILE_EXT(filename, ext) \
|
||||
(strcasecmp(&filename[strlen(filename) - sizeof(ext) + 1], ext) == 0)
|
||||
static const char *TAG = "SERVER HELP";
|
||||
|
||||
char scratch[SERVER_HELPER_SCRATCH_BUFSIZE];
|
||||
|
||||
|
||||
esp_err_t send_file(httpd_req_t *req, std::string filename, struct stat * file_stat)
|
||||
bool endsWith(std::string const &str, std::string const &suffix) {
|
||||
if (str.length() < suffix.length()) {
|
||||
return false;
|
||||
}
|
||||
return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t send_file(httpd_req_t *req, std::string filename)
|
||||
{
|
||||
FILE *fd = fopen(filename.c_str(), "r");
|
||||
if (!fd) {
|
||||
ESP_LOGE(TAG, "Failed to read existing file : %s", filename.c_str());
|
||||
/* Respond with 500 Internal Server Error */
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to read existing file");
|
||||
ESP_LOGE(TAG, "Failed to read file: %s", filename.c_str());
|
||||
/* Respond with 404 Error */
|
||||
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, get404());
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Sending file : %s (%ld bytes)...", filename.c_str(), file_stat->st_size);
|
||||
ESP_LOGD(TAG, "Sending file: %s ...", filename.c_str());
|
||||
// httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||
|
||||
/* For all files with the following file extention tell
|
||||
the webbrowser to cache them for 24h */
|
||||
if (endsWith(filename, ".html") ||
|
||||
endsWith(filename, ".htm") ||
|
||||
endsWith(filename, ".css") ||
|
||||
endsWith(filename, ".js") ||
|
||||
endsWith(filename, ".map") ||
|
||||
endsWith(filename, ".jpg") ||
|
||||
endsWith(filename, ".jpeg") ||
|
||||
endsWith(filename, ".ico") ||
|
||||
endsWith(filename, ".png")) {
|
||||
|
||||
if (filename == "/sdcard/html/setup.html") {
|
||||
httpd_resp_set_hdr(req, "Clear-Site-Data", "\"*\"");
|
||||
}
|
||||
else {
|
||||
httpd_resp_set_hdr(req, "Cache-Control", "max-age=86400");
|
||||
}
|
||||
}
|
||||
|
||||
set_content_type_from_file(req, filename.c_str());
|
||||
|
||||
/* Retrieve the pointer to scratch buffer for temporary storage */
|
||||
@@ -41,7 +77,7 @@ esp_err_t send_file(httpd_req_t *req, std::string filename, struct stat * file_
|
||||
size_t chunksize;
|
||||
do {
|
||||
/* Read file in chunks into the scratch buffer */
|
||||
chunksize = fread(chunk, 1, SCRATCH_BUFSIZE, fd);
|
||||
chunksize = fread(chunk, 1, SERVER_HELPER_SCRATCH_BUFSIZE, fd);
|
||||
|
||||
/* Send the buffer contents as HTTP response chunk */
|
||||
if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
|
||||
@@ -59,12 +95,13 @@ esp_err_t send_file(httpd_req_t *req, std::string filename, struct stat * file_
|
||||
|
||||
/* Close file after sending complete */
|
||||
fclose(fd);
|
||||
ESP_LOGI(TAG, "File sending complete");
|
||||
ESP_LOGD(TAG, "File sending complete");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* Copies the full path into destination buffer and returns
|
||||
* pointer to path (skipping the preceding base path) */
|
||||
const char* get_path_from_uri(char *dest, const char *base_path, const char *uri, size_t destsize)
|
||||
@@ -104,8 +141,14 @@ esp_err_t set_content_type_from_file(httpd_req_t *req, const char *filename)
|
||||
return httpd_resp_set_type(req, "text/html");
|
||||
} else if (IS_FILE_EXT(filename, ".jpeg")) {
|
||||
return httpd_resp_set_type(req, "image/jpeg");
|
||||
} else if (IS_FILE_EXT(filename, ".jpg")) {
|
||||
return httpd_resp_set_type(req, "image/jpeg");
|
||||
} else if (IS_FILE_EXT(filename, ".ico")) {
|
||||
return httpd_resp_set_type(req, "image/x-icon");
|
||||
} else if (IS_FILE_EXT(filename, ".js")) {
|
||||
return httpd_resp_set_type(req, "text/javascript");
|
||||
} else if (IS_FILE_EXT(filename, ".css")) {
|
||||
return httpd_resp_set_type(req, "text/css");
|
||||
}
|
||||
/* This is a limited set only */
|
||||
/* For any other type always set as plain text */
|
||||
@@ -1,3 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SERVERHELP_H
|
||||
#define SERVERHELP_H
|
||||
|
||||
#include <string>
|
||||
//#include <sys/param.h>
|
||||
#include "esp_http_server.h"
|
||||
@@ -5,6 +10,8 @@
|
||||
|
||||
const char* get_path_from_uri(char *dest, const char *base_path, const char *uri, size_t destsize);
|
||||
|
||||
esp_err_t send_file(httpd_req_t *req, std::string filename, struct stat * file_stat);
|
||||
esp_err_t send_file(httpd_req_t *req, std::string filename);
|
||||
|
||||
esp_err_t set_content_type_from_file(httpd_req_t *req, const char *filename);
|
||||
esp_err_t set_content_type_from_file(httpd_req_t *req, const char *filename);
|
||||
|
||||
#endif //SERVERHELP_H
|
||||
703
code/components/jomjol_fileserver_ota/server_ota.cpp
Normal file
703
code/components/jomjol_fileserver_ota/server_ota.cpp
Normal file
@@ -0,0 +1,703 @@
|
||||
#include "server_ota.h"
|
||||
|
||||
#include <string>
|
||||
#include "string.h"
|
||||
|
||||
/* TODO Rethink the usage of the int watchdog. It is no longer to be used, see
|
||||
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/migration-guides/release-5.x/5.0/system.html?highlight=esp_int_wdt */
|
||||
#include "esp_private/esp_int_wdt.h"
|
||||
|
||||
#include <esp_task_wdt.h>
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
#include <esp_ota_ops.h>
|
||||
#include "esp_http_client.h"
|
||||
#include "esp_flash_partitions.h"
|
||||
#include "esp_partition.h"
|
||||
#include <nvs.h>
|
||||
#include "esp_app_format.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "driver/gpio.h"
|
||||
// #include "protocol_examples_common.h"
|
||||
#include "errno.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "MainFlowControl.h"
|
||||
#include "server_file.h"
|
||||
#include "server_GPIO.h"
|
||||
#ifdef ENABLE_MQTT
|
||||
#include "interface_mqtt.h"
|
||||
#endif //ENABLE_MQTT
|
||||
#include "ClassControllCamera.h"
|
||||
#include "connect_wlan.h"
|
||||
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
|
||||
#include "Helper.h"
|
||||
#include "statusled.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
/*an ota data write buffer ready to write to the flash*/
|
||||
static char ota_write_data[SERVER_OTA_SCRATCH_BUFSIZE + 1] = { 0 };
|
||||
|
||||
static const char *TAG = "OTA";
|
||||
|
||||
esp_err_t handler_reboot(httpd_req_t *req);
|
||||
static bool ota_update_task(std::string fn);
|
||||
|
||||
std::string _file_name_update;
|
||||
bool initial_setup = false;
|
||||
|
||||
|
||||
static void infinite_loop(void)
|
||||
{
|
||||
int i = 0;
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "When a new firmware is available on the server, press the reset button to download it");
|
||||
while(1) {
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Waiting for a new firmware... (" + to_string(++i) + ")");
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void task_do_Update_ZIP(void *pvParameter)
|
||||
{
|
||||
StatusLED(AP_OR_OTA, 1, true); // Signaling an OTA update
|
||||
|
||||
std::string filetype = toUpper(getFileType(_file_name_update));
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "File: " + _file_name_update + " Filetype: " + filetype);
|
||||
|
||||
|
||||
if (filetype == "ZIP")
|
||||
{
|
||||
std::string in, out, outbin, zw, retfirmware;
|
||||
|
||||
out = "/sdcard/html";
|
||||
outbin = "/sdcard/firmware";
|
||||
|
||||
retfirmware = unzip_new(_file_name_update, out+"/", outbin+"/", "/sdcard/", initial_setup);
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Files unzipped.");
|
||||
|
||||
if (retfirmware.length() > 0)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Found firmware.bin");
|
||||
ota_update_task(retfirmware);
|
||||
}
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Trigger reboot due to firmware update");
|
||||
doRebootOTA();
|
||||
} else if (filetype == "BIN")
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Do firmware update - file: " + _file_name_update);
|
||||
ota_update_task(_file_name_update);
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Trigger reboot due to firmware update");
|
||||
doRebootOTA();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Only ZIP-Files support for update during startup!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CheckUpdate()
|
||||
{
|
||||
FILE *pfile;
|
||||
if ((pfile = fopen("/sdcard/update.txt", "r")) == NULL)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "No pending update");
|
||||
return;
|
||||
}
|
||||
|
||||
char zw[1024] = "";
|
||||
fgets(zw, 1024, pfile);
|
||||
_file_name_update = std::string(zw);
|
||||
if (fgets(zw, 1024, pfile))
|
||||
{
|
||||
std::string _szw = std::string(zw);
|
||||
if (_szw == "init")
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Inital Setup triggered");
|
||||
}
|
||||
}
|
||||
|
||||
fclose(pfile);
|
||||
DeleteFile("/sdcard/update.txt"); // Prevent Boot Loop!!!
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Start update process (" + _file_name_update + ")");
|
||||
|
||||
|
||||
xTaskCreate(&task_do_Update_ZIP, "task_do_Update_ZIP", configMINIMAL_STACK_SIZE * 35, NULL, tskIDLE_PRIORITY+1, NULL);
|
||||
while(1) { // wait until reboot within task_do_Update_ZIP
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool ota_update_task(std::string fn)
|
||||
{
|
||||
esp_err_t err;
|
||||
/* update handle : set by esp_ota_begin(), must be freed via esp_ota_end() */
|
||||
esp_ota_handle_t update_handle = 0 ;
|
||||
const esp_partition_t *update_partition = NULL;
|
||||
|
||||
ESP_LOGI(TAG, "Starting OTA update");
|
||||
|
||||
const esp_partition_t *configured = esp_ota_get_boot_partition();
|
||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||
|
||||
if (configured != running) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Configured OTA boot partition at offset " + to_string(configured->address) +
|
||||
", but running from offset " + to_string(running->address));
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "(This can happen if either the OTA boot data or preferred boot image become somehow corrupted.)");
|
||||
}
|
||||
ESP_LOGI(TAG, "Running partition type %d subtype %d (offset 0x%08x)",
|
||||
running->type, running->subtype, (unsigned int)running->address);
|
||||
|
||||
|
||||
update_partition = esp_ota_get_next_update_partition(NULL);
|
||||
ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%x",
|
||||
update_partition->subtype, (unsigned int)update_partition->address);
|
||||
// assert(update_partition != NULL);
|
||||
|
||||
int binary_file_length = 0;
|
||||
|
||||
// deal with all receive packet
|
||||
bool image_header_was_checked = false;
|
||||
|
||||
int data_read;
|
||||
|
||||
FILE* f = fopen(fn.c_str(), "rb"); // previously only "r
|
||||
|
||||
if (f == NULL) { // File does not exist
|
||||
return false;
|
||||
}
|
||||
|
||||
data_read = fread(ota_write_data, 1, SERVER_OTA_SCRATCH_BUFSIZE, f);
|
||||
|
||||
while (data_read > 0) {
|
||||
if (data_read < 0) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Error: SSL data read error");
|
||||
return false;
|
||||
} else if (data_read > 0) {
|
||||
if (image_header_was_checked == false) {
|
||||
esp_app_desc_t new_app_info;
|
||||
if (data_read > sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t)) {
|
||||
// check current version with downloading
|
||||
memcpy(&new_app_info, &ota_write_data[sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t)], sizeof(esp_app_desc_t));
|
||||
ESP_LOGI(TAG, "New firmware version: %s", new_app_info.version);
|
||||
|
||||
esp_app_desc_t running_app_info;
|
||||
if (esp_ota_get_partition_description(running, &running_app_info) == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Running firmware version: %s", running_app_info.version);
|
||||
}
|
||||
|
||||
const esp_partition_t* last_invalid_app = esp_ota_get_last_invalid_partition();
|
||||
esp_app_desc_t invalid_app_info;
|
||||
if (esp_ota_get_partition_description(last_invalid_app, &invalid_app_info) == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Last invalid firmware version: %s", invalid_app_info.version);
|
||||
}
|
||||
|
||||
// check current version with last invalid partition
|
||||
if (last_invalid_app != NULL) {
|
||||
if (memcmp(invalid_app_info.version, new_app_info.version, sizeof(new_app_info.version)) == 0) {
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "New version is the same as invalid version");
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Previously, there was an attempt to launch the firmware with " +
|
||||
string(invalid_app_info.version) + " version, but it failed");
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "The firmware has been rolled back to the previous version");
|
||||
infinite_loop();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if (memcmp(new_app_info.version, running_app_info.version, sizeof(new_app_info.version)) == 0) {
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Current running version is the same as a new. We will not continue the update");
|
||||
infinite_loop();
|
||||
}
|
||||
*/
|
||||
image_header_was_checked = true;
|
||||
|
||||
err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);
|
||||
if (err != ESP_OK) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_ota_begin failed (" + string(esp_err_to_name(err)) + ")");
|
||||
return false;
|
||||
}
|
||||
ESP_LOGI(TAG, "esp_ota_begin succeeded");
|
||||
} else {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "received package is not fit len");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
err = esp_ota_write( update_handle, (const void *)ota_write_data, data_read);
|
||||
if (err != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
binary_file_length += data_read;
|
||||
ESP_LOGD(TAG, "Written image length %d", binary_file_length);
|
||||
} else if (data_read == 0) {
|
||||
//
|
||||
// * As esp_http_client_read never returns negative error code, we rely on
|
||||
// * `errno` to check for underlying transport connectivity closure if any
|
||||
//
|
||||
if (errno == ECONNRESET || errno == ENOTCONN) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection closed, errno = " + to_string(errno));
|
||||
break;
|
||||
}
|
||||
}
|
||||
data_read = fread(ota_write_data, 1, SERVER_OTA_SCRATCH_BUFSIZE, f);
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
ESP_LOGI(TAG, "Total Write binary data length: %d", binary_file_length);
|
||||
|
||||
err = esp_ota_end(update_handle);
|
||||
if (err != ESP_OK) {
|
||||
if (err == ESP_ERR_OTA_VALIDATE_FAILED) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Image validation failed, image is corrupted");
|
||||
}
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_ota_end failed (" + string(esp_err_to_name(err)) + ")!");
|
||||
return false;
|
||||
}
|
||||
|
||||
err = esp_ota_set_boot_partition(update_partition);
|
||||
if (err != ESP_OK) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_ota_set_boot_partition failed (" + string(esp_err_to_name(err)) + ")!");
|
||||
|
||||
}
|
||||
// ESP_LOGI(TAG, "Prepare to restart system!");
|
||||
// esp_restart();
|
||||
|
||||
return true ;
|
||||
}
|
||||
|
||||
|
||||
static void print_sha256 (const uint8_t *image_hash, const char *label)
|
||||
{
|
||||
char hash_print[HASH_LEN * 2 + 1];
|
||||
hash_print[HASH_LEN * 2] = 0;
|
||||
for (int i = 0; i < HASH_LEN; ++i) {
|
||||
sprintf(&hash_print[i * 2], "%02x", image_hash[i]);
|
||||
}
|
||||
ESP_LOGI(TAG, "%s: %s", label, hash_print);
|
||||
}
|
||||
|
||||
|
||||
static bool diagnostic(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void CheckOTAUpdate(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Start CheckOTAUpdateCheck...");
|
||||
|
||||
uint8_t sha_256[HASH_LEN] = { 0 };
|
||||
esp_partition_t partition;
|
||||
|
||||
// get sha256 digest for the partition table
|
||||
partition.address = ESP_PARTITION_TABLE_OFFSET;
|
||||
partition.size = ESP_PARTITION_TABLE_MAX_LEN;
|
||||
partition.type = ESP_PARTITION_TYPE_DATA;
|
||||
esp_partition_get_sha256(&partition, sha_256);
|
||||
print_sha256(sha_256, "SHA-256 for the partition table: ");
|
||||
|
||||
// get sha256 digest for bootloader
|
||||
partition.address = ESP_BOOTLOADER_OFFSET;
|
||||
partition.size = ESP_PARTITION_TABLE_OFFSET;
|
||||
partition.type = ESP_PARTITION_TYPE_APP;
|
||||
esp_partition_get_sha256(&partition, sha_256);
|
||||
print_sha256(sha_256, "SHA-256 for bootloader: ");
|
||||
|
||||
// get sha256 digest for running partition
|
||||
esp_partition_get_sha256(esp_ota_get_running_partition(), sha_256);
|
||||
print_sha256(sha_256, "SHA-256 for current firmware: ");
|
||||
|
||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||
esp_ota_img_states_t ota_state;
|
||||
esp_err_t res_stat_partition = esp_ota_get_state_partition(running, &ota_state);
|
||||
switch (res_stat_partition)
|
||||
{
|
||||
case ESP_OK:
|
||||
ESP_LOGD(TAG, "CheckOTAUpdate Partition: ESP_OK");
|
||||
if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) {
|
||||
if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) {
|
||||
// run diagnostic function ...
|
||||
bool diagnostic_is_ok = diagnostic();
|
||||
if (diagnostic_is_ok) {
|
||||
ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution...");
|
||||
esp_ota_mark_app_valid_cancel_rollback();
|
||||
} else {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Diagnostics failed! Start rollback to the previous version...");
|
||||
esp_ota_mark_app_invalid_rollback_and_reboot();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ESP_ERR_INVALID_ARG:
|
||||
ESP_LOGD(TAG, "CheckOTAUpdate Partition: ESP_ERR_INVALID_ARG");
|
||||
break;
|
||||
case ESP_ERR_NOT_SUPPORTED:
|
||||
ESP_LOGD(TAG, "CheckOTAUpdate Partition: ESP_ERR_NOT_SUPPORTED");
|
||||
break;
|
||||
case ESP_ERR_NOT_FOUND:
|
||||
ESP_LOGD(TAG, "CheckOTAUpdate Partition: ESP_ERR_NOT_FOUND");
|
||||
break;
|
||||
}
|
||||
if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) {
|
||||
if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) {
|
||||
// run diagnostic function ...
|
||||
bool diagnostic_is_ok = diagnostic();
|
||||
if (diagnostic_is_ok) {
|
||||
ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution...");
|
||||
esp_ota_mark_app_valid_cancel_rollback();
|
||||
} else {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Diagnostics failed! Start rollback to the previous version...");
|
||||
esp_ota_mark_app_invalid_rollback_and_reboot();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
esp_err_t handler_ota_update(httpd_req_t *req)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_ota_update - Start");
|
||||
#endif
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "handler_ota_update");
|
||||
char _query[200];
|
||||
char _filename[100];
|
||||
char _valuechar[30];
|
||||
std::string fn = "/sdcard/firmware/";
|
||||
bool _file_del = false;
|
||||
std::string _task = "";
|
||||
|
||||
if (httpd_req_get_url_query_str(req, _query, 200) == ESP_OK)
|
||||
{
|
||||
ESP_LOGD(TAG, "Query: %s", _query);
|
||||
|
||||
if (httpd_query_key_value(_query, "task", _valuechar, 30) == ESP_OK)
|
||||
{
|
||||
ESP_LOGD(TAG, "task is found: %s", _valuechar);
|
||||
_task = std::string(_valuechar);
|
||||
}
|
||||
|
||||
if (httpd_query_key_value(_query, "file", _filename, 100) == ESP_OK)
|
||||
{
|
||||
fn.append(_filename);
|
||||
ESP_LOGD(TAG, "File: %s", fn.c_str());
|
||||
}
|
||||
if (httpd_query_key_value(_query, "delete", _filename, 100) == ESP_OK)
|
||||
{
|
||||
fn.append(_filename);
|
||||
_file_del = true;
|
||||
ESP_LOGD(TAG, "Delete Default File: %s", fn.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (_task.compare("emptyfirmwaredir") == 0)
|
||||
{
|
||||
ESP_LOGD(TAG, "Start empty directory /firmware");
|
||||
delete_all_in_directory("/sdcard/firmware");
|
||||
std::string zw = "firmware directory deleted - v2\n";
|
||||
ESP_LOGD(TAG, "%s", zw.c_str());
|
||||
printf("Ausgabe: %s\n", zw.c_str());
|
||||
|
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||
httpd_resp_send(req, zw.c_str(), strlen(zw.c_str()));
|
||||
/* Respond with an empty chunk to signal HTTP response completion */
|
||||
httpd_resp_send_chunk(req, NULL, 0);
|
||||
|
||||
ESP_LOGD(TAG, "Done empty directory /firmware");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
if (_task.compare("update") == 0)
|
||||
{
|
||||
std::string filetype = toUpper(getFileType(fn));
|
||||
if (filetype.length() == 0)
|
||||
{
|
||||
std::string zw = "Update failed - no file specified (zip, bin, tfl, tlite)";
|
||||
httpd_resp_sendstr_chunk(req, zw.c_str());
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
if ((filetype == "TFLITE") || (filetype == "TFL"))
|
||||
{
|
||||
std::string out = "/sdcard/config/" + getFileFullFileName(fn);
|
||||
DeleteFile(out);
|
||||
CopyFile(fn, out);
|
||||
DeleteFile(fn);
|
||||
|
||||
const char* resp_str = "Neural Network File copied.";
|
||||
httpd_resp_sendstr_chunk(req, resp_str);
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
if ((filetype == "ZIP") || (filetype == "BIN"))
|
||||
{
|
||||
FILE *pfile;
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Update for reboot");
|
||||
pfile = fopen("/sdcard/update.txt", "w");
|
||||
fwrite(fn.c_str(), fn.length(), 1, pfile);
|
||||
fclose(pfile);
|
||||
|
||||
std::string zw = "reboot\n";
|
||||
httpd_resp_sendstr_chunk(req, zw.c_str());
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
ESP_LOGD(TAG, "Send reboot");
|
||||
return ESP_OK;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
if (filetype == "BIN")
|
||||
{
|
||||
const char* resp_str;
|
||||
|
||||
DeleteMainFlowTask();
|
||||
gpio_handler_deinit();
|
||||
if (ota_update_task(fn))
|
||||
{
|
||||
std::string zw = "reboot\n";
|
||||
httpd_resp_sendstr_chunk(req, zw.c_str());
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
ESP_LOGD(TAG, "Send reboot");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
resp_str = "Error during Firmware Update!!!\nPlease check output of console.";
|
||||
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_ota_update - Done");
|
||||
#endif
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
*/
|
||||
|
||||
std::string zw = "Update failed - no valid file specified (zip, bin, tfl, tlite)!";
|
||||
httpd_resp_sendstr_chunk(req, zw.c_str());
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
if (_task.compare("unziphtml") == 0)
|
||||
{
|
||||
ESP_LOGD(TAG, "Task unziphtml");
|
||||
std::string in, out, zw;
|
||||
|
||||
in = "/sdcard/firmware/html.zip";
|
||||
out = "/sdcard/html";
|
||||
|
||||
delete_all_in_directory(out);
|
||||
|
||||
unzip(in, out+"/");
|
||||
zw = "Web Interface Update Successfull!\nNo reboot necessary";
|
||||
httpd_resp_send(req, zw.c_str(), strlen(zw.c_str()));
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
if (_file_del)
|
||||
{
|
||||
ESP_LOGD(TAG, "Delete !! _file_del: %s", fn.c_str());
|
||||
struct stat file_stat;
|
||||
int _result = stat(fn.c_str(), &file_stat);
|
||||
ESP_LOGD(TAG, "Ergebnis %d\n", _result);
|
||||
if (_result == 0) {
|
||||
ESP_LOGD(TAG, "Deleting file: %s", fn.c_str());
|
||||
/* Delete file */
|
||||
unlink(fn.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File does not exist: " + fn);
|
||||
}
|
||||
/* Respond with an empty chunk to signal HTTP response completion */
|
||||
std::string zw = "file deleted\n";
|
||||
ESP_LOGD(TAG, "%s", zw.c_str());
|
||||
httpd_resp_send(req, zw.c_str(), strlen(zw.c_str()));
|
||||
httpd_resp_send_chunk(req, NULL, 0);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
string zw = "ota without parameter - should not be the case!";
|
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||
httpd_resp_send(req, zw.c_str(), strlen(zw.c_str()));
|
||||
httpd_resp_send_chunk(req, NULL, 0);
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ota without parameter - should not be the case!");
|
||||
|
||||
/*
|
||||
const char* resp_str;
|
||||
|
||||
DeleteMainFlowTask();
|
||||
gpio_handler_deinit();
|
||||
if (ota_update_task(fn))
|
||||
{
|
||||
resp_str = "Firmware Update Successfull! You can restart now.";
|
||||
}
|
||||
else
|
||||
{
|
||||
resp_str = "Error during Firmware Update!!! Please check console output.";
|
||||
}
|
||||
|
||||
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||
*/
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_ota_update - Done");
|
||||
#endif
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
void hard_restart()
|
||||
{
|
||||
esp_task_wdt_config_t twdt_config = {
|
||||
.timeout_ms = 1,
|
||||
.idle_core_mask = (1 << portNUM_PROCESSORS) - 1, // Bitmask of all cores
|
||||
.trigger_panic = true,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_task_wdt_init(&twdt_config));
|
||||
|
||||
esp_task_wdt_add(NULL);
|
||||
while(true);
|
||||
}
|
||||
|
||||
|
||||
void task_reboot(void *DeleteMainFlow)
|
||||
{
|
||||
// write a reboot, to identify a reboot by purpouse
|
||||
FILE* pfile = fopen("/sdcard/reboot.txt", "w");
|
||||
std::string _s_zw= "reboot";
|
||||
fwrite(_s_zw.c_str(), strlen(_s_zw.c_str()), 1, pfile);
|
||||
fclose(pfile);
|
||||
|
||||
vTaskDelay(3000 / portTICK_PERIOD_MS);
|
||||
|
||||
if ((bool)DeleteMainFlow) {
|
||||
DeleteMainFlowTask(); // Kill autoflow task if executed in extra task, if not don't kill parent task
|
||||
}
|
||||
|
||||
Camera.LightOnOff(false);
|
||||
StatusLEDOff();
|
||||
|
||||
/* Stop service tasks */
|
||||
#ifdef ENABLE_MQTT
|
||||
MQTTdestroy_client(true);
|
||||
#endif //ENABLE_MQTT
|
||||
gpio_handler_destroy();
|
||||
esp_camera_deinit();
|
||||
WIFIDestroy();
|
||||
|
||||
vTaskDelay(3000 / portTICK_PERIOD_MS);
|
||||
esp_restart(); // Reset type: CPU reset (Reset both CPUs)
|
||||
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
hard_restart(); // Reset type: System reset (Triggered by watchdog), if esp_restart stalls (WDT needs to be activated)
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Reboot failed!");
|
||||
vTaskDelete(NULL); //Delete this task if it comes to this point
|
||||
}
|
||||
|
||||
|
||||
void doReboot()
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Reboot triggered by Software (5s)");
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Reboot in 5sec");
|
||||
|
||||
BaseType_t xReturned = xTaskCreate(&task_reboot, "task_reboot", configMINIMAL_STACK_SIZE * 4, (void*) true, 10, NULL);
|
||||
if( xReturned != pdPASS )
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "task_reboot not created -> force reboot without killing flow");
|
||||
task_reboot((void*) false);
|
||||
}
|
||||
vTaskDelay(10000 / portTICK_PERIOD_MS); // Prevent serving web client fetch response until system is shuting down
|
||||
}
|
||||
|
||||
|
||||
void doRebootOTA()
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Reboot in 5sec");
|
||||
|
||||
Camera.LightOnOff(false);
|
||||
StatusLEDOff();
|
||||
esp_camera_deinit();
|
||||
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
esp_restart(); // Reset type: CPU reset (Reset both CPUs)
|
||||
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
hard_restart(); // Reset type: System reset (Triggered by watchdog), if esp_restart stalls (WDT needs to be activated)
|
||||
}
|
||||
|
||||
|
||||
esp_err_t handler_reboot(httpd_req_t *req)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_reboot - Start");
|
||||
#endif
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "handler_reboot");
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "!!! System will restart within 5 sec!!!");
|
||||
|
||||
std::string response =
|
||||
"<html><head><script>"
|
||||
"function m(h) {"
|
||||
"document.getElementById('t').innerHTML=h;"
|
||||
"setInterval(function (){h +='.'; document.getElementById('t').innerHTML=h;"
|
||||
"fetch('reboot_page.html',{mode: 'no-cors'}).then(r=>{parent.location.href=('index.html');})}, 1000);"
|
||||
"}</script></head></html><body style='font-family: arial'><h3 id=t></h3>"
|
||||
"<script>m('Rebooting!<br>The page will automatically reload in around 25..60s.<br><br>');</script>"
|
||||
"</body></html>";
|
||||
|
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||
httpd_resp_send(req, response.c_str(), strlen(response.c_str()));
|
||||
|
||||
doReboot();
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_reboot - Done");
|
||||
#endif
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
void register_server_ota_sdcard_uri(httpd_handle_t server)
|
||||
{
|
||||
ESP_LOGI(TAG, "Registering URI handlers");
|
||||
|
||||
httpd_uri_t camuri = { };
|
||||
camuri.method = HTTP_GET;
|
||||
camuri.uri = "/ota";
|
||||
camuri.handler = handler_ota_update;
|
||||
camuri.user_ctx = (void*) "Do OTA";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.method = HTTP_GET;
|
||||
camuri.uri = "/reboot";
|
||||
camuri.handler = handler_reboot;
|
||||
camuri.user_ctx = (void*) "Reboot";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
}
|
||||
20
code/components/jomjol_fileserver_ota/server_ota.h
Normal file
20
code/components/jomjol_fileserver_ota/server_ota.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SERVEROTA_H
|
||||
#define SERVEROTA_H
|
||||
|
||||
#include <esp_log.h>
|
||||
|
||||
#include <esp_http_server.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
void register_server_ota_sdcard_uri(httpd_handle_t server);
|
||||
void CheckOTAUpdate();
|
||||
void doReboot();
|
||||
void doRebootOTA();
|
||||
void hard_restart();
|
||||
void CheckUpdate();
|
||||
|
||||
#endif //SERVEROTA_H
|
||||
7
code/components/jomjol_flowcontroll/CMakeLists.txt
Normal file
7
code/components/jomjol_flowcontroll/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES esp_timer esp_wifi jomjol_tfliteclass jomjol_helper jomjol_controlcamera jomjol_mqtt jomjol_influxdb jomjol_fileserver_ota jomjol_image_proc jomjol_wlan)
|
||||
|
||||
|
||||
119
code/components/jomjol_flowcontroll/ClassFlow.cpp
Normal file
119
code/components/jomjol_flowcontroll/ClassFlow.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
#include "ClassFlow.h"
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
static const char *TAG = "CLASS";
|
||||
|
||||
|
||||
void ClassFlow::SetInitialParameter(void)
|
||||
{
|
||||
ListFlowControll = NULL;
|
||||
previousElement = NULL;
|
||||
disabled = false;
|
||||
}
|
||||
|
||||
bool ClassFlow::isNewParagraph(string input)
|
||||
{
|
||||
if ((input[0] == '[') || ((input[0] == ';') && (input[1] == '[')))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ClassFlow::GetNextParagraph(FILE* pfile, string& aktparamgraph)
|
||||
{
|
||||
while (getNextLine(pfile, &aktparamgraph) && !isNewParagraph(aktparamgraph));
|
||||
|
||||
if (isNewParagraph(aktparamgraph))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
ClassFlow::ClassFlow(void)
|
||||
{
|
||||
SetInitialParameter();
|
||||
}
|
||||
|
||||
ClassFlow::ClassFlow(std::vector<ClassFlow*> * lfc)
|
||||
{
|
||||
SetInitialParameter();
|
||||
ListFlowControll = lfc;
|
||||
}
|
||||
|
||||
ClassFlow::ClassFlow(std::vector<ClassFlow*> * lfc, ClassFlow *_prev)
|
||||
{
|
||||
SetInitialParameter();
|
||||
ListFlowControll = lfc;
|
||||
previousElement = _prev;
|
||||
}
|
||||
|
||||
bool ClassFlow::ReadParameter(FILE* pfile, string &aktparamgraph)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ClassFlow::doFlow(string time)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string ClassFlow::getHTMLSingleStep(string host){
|
||||
return "";
|
||||
}
|
||||
|
||||
string ClassFlow::getReadout()
|
||||
{
|
||||
return string();
|
||||
}
|
||||
|
||||
std::string ClassFlow::GetParameterName(std::string _input)
|
||||
{
|
||||
string _param;
|
||||
int _pospunkt = _input.find_first_of(".");
|
||||
if (_pospunkt > -1)
|
||||
{
|
||||
_param = _input.substr(_pospunkt+1, _input.length() - _pospunkt - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
_param = _input;
|
||||
}
|
||||
// ESP_LOGD(TAG, "Parameter: %s, Pospunkt: %d", _param.c_str(), _pospunkt);
|
||||
return _param;
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlow::getNextLine(FILE* pfile, string *rt)
|
||||
{
|
||||
char zw[1024];
|
||||
if (pfile == NULL)
|
||||
{
|
||||
*rt = "";
|
||||
return false;
|
||||
}
|
||||
if (!fgets(zw, 1024, pfile))
|
||||
{
|
||||
*rt = "";
|
||||
ESP_LOGD(TAG, "END OF FILE");
|
||||
return false;
|
||||
}
|
||||
ESP_LOGD(TAG, "%s", zw);
|
||||
*rt = zw;
|
||||
*rt = trim(*rt);
|
||||
while ((zw[0] == ';' || zw[0] == '#' || (rt->size() == 0)) && !(zw[1] == '['))
|
||||
{
|
||||
*rt = "";
|
||||
if (!fgets(zw, 1024, pfile))
|
||||
return false;
|
||||
ESP_LOGD(TAG, "%s", zw);
|
||||
*rt = zw;
|
||||
*rt = trim(*rt);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1,36 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef CLASSFLOW_H
|
||||
#define CLASSFLOW_H
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Helper.h"
|
||||
#include "CFindTemplate.h"
|
||||
#include "CImageBasis.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
struct HTMLInfo
|
||||
{
|
||||
float val;
|
||||
CImageBasis *image = NULL;
|
||||
CImageBasis *image_org = NULL;
|
||||
std::string filename;
|
||||
std::string filename_org;
|
||||
};
|
||||
|
||||
|
||||
class ClassFlow
|
||||
{
|
||||
protected:
|
||||
std::vector<string> ZerlegeZeile(string input);
|
||||
bool isNewParagraph(string input);
|
||||
bool GetNextParagraph(FILE* pfile, string& aktparamgraph);
|
||||
bool getNextLine(FILE* pfile, string* rt);
|
||||
|
||||
std::vector<ClassFlow*>* ListFlowControll;
|
||||
ClassFlow *previousElement;
|
||||
|
||||
virtual void SetInitialParameter(void);
|
||||
|
||||
std::string GetParameterName(std::string _input);
|
||||
|
||||
bool disabled;
|
||||
|
||||
public:
|
||||
ClassFlow(void);
|
||||
ClassFlow(std::vector<ClassFlow*> * lfc);
|
||||
ClassFlow(std::vector<ClassFlow*> * lfc, ClassFlow *_prev);
|
||||
|
||||
virtual bool ReadParameter(FILE* pfile, string &aktparamgraph);
|
||||
virtual bool doFlow(string time);
|
||||
virtual string getHTMLSingleStep(string host);
|
||||
@@ -39,3 +51,4 @@ public:
|
||||
|
||||
};
|
||||
|
||||
#endif //CLASSFLOW_H
|
||||
393
code/components/jomjol_flowcontroll/ClassFlowAlignment.cpp
Normal file
393
code/components/jomjol_flowcontroll/ClassFlowAlignment.cpp
Normal file
@@ -0,0 +1,393 @@
|
||||
#include "ClassFlowAlignment.h"
|
||||
#include "ClassFlowTakeImage.h"
|
||||
#include "ClassFlow.h"
|
||||
#include "MainFlowControl.h"
|
||||
|
||||
#include "CRotateImage.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
#include "psram.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
|
||||
static const char *TAG = "ALIGN";
|
||||
|
||||
// #define DEBUG_DETAIL_ON
|
||||
|
||||
|
||||
void ClassFlowAlignment::SetInitialParameter(void)
|
||||
{
|
||||
initalrotate = 0;
|
||||
anz_ref = 0;
|
||||
initialmirror = false;
|
||||
use_antialiasing = false;
|
||||
initialflip = false;
|
||||
SaveAllFiles = false;
|
||||
namerawimage = "/sdcard/img_tmp/raw.jpg";
|
||||
FileStoreRefAlignment = "/sdcard/config/align.txt";
|
||||
ListFlowControll = NULL;
|
||||
AlignAndCutImage = NULL;
|
||||
ImageBasis = NULL;
|
||||
ImageTMP = NULL;
|
||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
||||
AlgROI = (ImageData*)malloc_psram_heap(std::string(TAG) + "->AlgROI", sizeof(ImageData), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
|
||||
#endif
|
||||
previousElement = NULL;
|
||||
disabled = false;
|
||||
SAD_criteria = 0.05;
|
||||
}
|
||||
|
||||
|
||||
ClassFlowAlignment::ClassFlowAlignment(std::vector<ClassFlow*>* lfc)
|
||||
{
|
||||
SetInitialParameter();
|
||||
ListFlowControll = lfc;
|
||||
|
||||
for (int i = 0; i < ListFlowControll->size(); ++i)
|
||||
{
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowTakeImage") == 0)
|
||||
{
|
||||
ImageBasis = ((ClassFlowTakeImage*) (*ListFlowControll)[i])->rawImage;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ImageBasis) // the function take pictures does not exist --> must be created first ONLY FOR TEST PURPOSES
|
||||
{
|
||||
ESP_LOGD(TAG, "CImageBasis had to be created");
|
||||
ImageBasis = new CImageBasis("ImageBasis", namerawimage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowAlignment::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
{
|
||||
std::vector<string> splitted;
|
||||
int suchex = 40;
|
||||
int suchey = 40;
|
||||
int alg_algo = 0; //default=0; 1 =HIGHACCURACY; 2= FAST; 3= OFF //add disable aligment algo |01.2023
|
||||
|
||||
|
||||
aktparamgraph = trim(aktparamgraph);
|
||||
|
||||
if (aktparamgraph.size() == 0)
|
||||
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||
return false;
|
||||
|
||||
if (aktparamgraph.compare("[Alignment]") != 0) //Paragraph does not fit Alignment
|
||||
return false;
|
||||
|
||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||
{
|
||||
splitted = ZerlegeZeile(aktparamgraph);
|
||||
if ((toUpper(splitted[0]) == "FLIPIMAGESIZE") && (splitted.size() > 1))
|
||||
{
|
||||
if (toUpper(splitted[1]) == "TRUE")
|
||||
initialflip = true;
|
||||
}
|
||||
if ((toUpper(splitted[0]) == "INITIALMIRROR") && (splitted.size() > 1))
|
||||
{
|
||||
if (toUpper(splitted[1]) == "TRUE")
|
||||
initialmirror = true;
|
||||
}
|
||||
if (((toUpper(splitted[0]) == "INITALROTATE") || (toUpper(splitted[0]) == "INITIALROTATE")) && (splitted.size() > 1))
|
||||
{
|
||||
this->initalrotate = std::stod(splitted[1]);
|
||||
}
|
||||
if ((toUpper(splitted[0]) == "SEARCHFIELDX") && (splitted.size() > 1))
|
||||
{
|
||||
suchex = std::stod(splitted[1]);
|
||||
}
|
||||
if ((toUpper(splitted[0]) == "SEARCHFIELDY") && (splitted.size() > 1))
|
||||
{
|
||||
suchey = std::stod(splitted[1]);
|
||||
}
|
||||
if ((toUpper(splitted[0]) == "ANTIALIASING") && (splitted.size() > 1))
|
||||
{
|
||||
if (toUpper(splitted[1]) == "TRUE")
|
||||
use_antialiasing = true;
|
||||
}
|
||||
if ((splitted.size() == 3) && (anz_ref < 2))
|
||||
{
|
||||
References[anz_ref].image_file = FormatFileName("/sdcard" + splitted[0]);
|
||||
References[anz_ref].target_x = std::stod(splitted[1]);
|
||||
References[anz_ref].target_y = std::stod(splitted[2]);
|
||||
anz_ref++;
|
||||
}
|
||||
|
||||
if ((toUpper(splitted[0]) == "SAVEALLFILES") && (splitted.size() > 1))
|
||||
{
|
||||
if (toUpper(splitted[1]) == "TRUE")
|
||||
SaveAllFiles = true;
|
||||
}
|
||||
if ((toUpper(splitted[0]) == "ALIGNMENTALGO") && (splitted.size() > 1))
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
std::string zw2 = "Alignment mode selected: " + splitted[1];
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, zw2);
|
||||
#endif
|
||||
if (toUpper(splitted[1]) == "HIGHACCURACY")
|
||||
alg_algo = 1;
|
||||
if (toUpper(splitted[1]) == "FAST")
|
||||
alg_algo = 2;
|
||||
if (toUpper(splitted[1]) == "OFF") //no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023
|
||||
alg_algo = 3;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < anz_ref; ++i)
|
||||
{
|
||||
References[i].search_x = suchex;
|
||||
References[i].search_y = suchey;
|
||||
References[i].fastalg_SAD_criteria = SAD_criteria;
|
||||
References[i].alignment_algo = alg_algo;
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
std::string zw2 = "Alignment mode written: " + std::to_string(alg_algo);
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, zw2);
|
||||
#endif
|
||||
}
|
||||
|
||||
//no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023
|
||||
if(References[0].alignment_algo != 3){
|
||||
LoadReferenceAlignmentValues();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowAlignment::getHTMLSingleStep(string host)
|
||||
{
|
||||
string result;
|
||||
|
||||
result = "<p>Rotated Image: </p> <p><img src=\"" + host + "/img_tmp/rot.jpg\"></p>\n";
|
||||
result = result + "<p>Found Alignment: </p> <p><img src=\"" + host + "/img_tmp/rot_roi.jpg\"></p>\n";
|
||||
result = result + "<p>Aligned Image: </p> <p><img src=\"" + host + "/img_tmp/alg.jpg\"></p>\n";
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowAlignment::doFlow(string time)
|
||||
{
|
||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
||||
if (!AlgROI) // AlgROI needs to be allocated before ImageTMP to avoid heap fragmentation
|
||||
{
|
||||
AlgROI = (ImageData*)heap_caps_realloc(AlgROI, sizeof(ImageData), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
|
||||
if (!AlgROI)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate AlgROI");
|
||||
LogFile.WriteHeapInfo("ClassFlowAlignment-doFlow");
|
||||
}
|
||||
}
|
||||
|
||||
if (AlgROI)
|
||||
{
|
||||
ImageBasis->writeToMemoryAsJPG((ImageData*)AlgROI, 90);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!ImageTMP)
|
||||
{
|
||||
ImageTMP = new CImageBasis("tmpImage", ImageBasis); // Make sure the name does not get change, it is relevant for the PSRAM allocation!
|
||||
if (!ImageTMP)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate tmpImage -> Exec this round aborted!");
|
||||
LogFile.WriteHeapInfo("ClassFlowAlignment-doFlow");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
delete AlignAndCutImage;
|
||||
AlignAndCutImage = new CAlignAndCutImage("AlignAndCutImage", ImageBasis, ImageTMP);
|
||||
if (!AlignAndCutImage)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate AlignAndCutImage -> Exec this round aborted!");
|
||||
LogFile.WriteHeapInfo("ClassFlowAlignment-doFlow");
|
||||
return false;
|
||||
}
|
||||
|
||||
CRotateImage rt("rawImage", AlignAndCutImage, ImageTMP, initialflip);
|
||||
if (initialflip)
|
||||
{
|
||||
int _zw = ImageBasis->height;
|
||||
ImageBasis->height = ImageBasis->width;
|
||||
ImageBasis->width = _zw;
|
||||
|
||||
_zw = ImageTMP->width;
|
||||
ImageTMP->width = ImageTMP->height;
|
||||
ImageTMP->height = _zw;
|
||||
}
|
||||
|
||||
if (initialmirror)
|
||||
{
|
||||
ESP_LOGD(TAG, "do mirror");
|
||||
rt.Mirror();
|
||||
|
||||
if (SaveAllFiles)
|
||||
AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/mirror.jpg"));
|
||||
}
|
||||
|
||||
if ((initalrotate != 0) || initialflip)
|
||||
{
|
||||
if (use_antialiasing)
|
||||
rt.RotateAntiAliasing(initalrotate);
|
||||
else
|
||||
rt.Rotate(initalrotate);
|
||||
|
||||
if (SaveAllFiles)
|
||||
AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/rot.jpg"));
|
||||
}
|
||||
|
||||
|
||||
//no align algo if set to 3 = off //add disable aligment algo |01.2023
|
||||
if(References[0].alignment_algo != 3){
|
||||
if (!AlignAndCutImage->Align(&References[0], &References[1]))
|
||||
{
|
||||
SaveReferenceAlignmentValues();
|
||||
}
|
||||
}// no align
|
||||
|
||||
|
||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
||||
if (AlgROI) {
|
||||
//no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023
|
||||
if(References[0].alignment_algo != 3){
|
||||
DrawRef(ImageTMP);
|
||||
}
|
||||
flowctrl.DigitalDrawROI(ImageTMP);
|
||||
flowctrl.AnalogDrawROI(ImageTMP);
|
||||
ImageTMP->writeToMemoryAsJPG((ImageData*)AlgROI, 90);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (SaveAllFiles)
|
||||
{
|
||||
AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/alg.jpg"));
|
||||
ImageTMP->SaveToFile(FormatFileName("/sdcard/img_tmp/alg_roi.jpg"));
|
||||
}
|
||||
|
||||
// must be deleted to have memory space for loading tflite
|
||||
delete ImageTMP;
|
||||
ImageTMP = NULL;
|
||||
|
||||
//no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023
|
||||
if(References[0].alignment_algo != 3){
|
||||
LoadReferenceAlignmentValues();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void ClassFlowAlignment::SaveReferenceAlignmentValues()
|
||||
{
|
||||
FILE* pFile;
|
||||
std::string zwtime, zwvalue;
|
||||
|
||||
pFile = fopen(FileStoreRefAlignment.c_str(), "w");
|
||||
|
||||
if (strlen(zwtime.c_str()) == 0)
|
||||
{
|
||||
time_t rawtime;
|
||||
struct tm* timeinfo;
|
||||
char buffer[80];
|
||||
|
||||
time(&rawtime);
|
||||
timeinfo = localtime(&rawtime);
|
||||
|
||||
strftime(buffer, 80, "%Y-%m-%dT%H:%M:%S", timeinfo);
|
||||
zwtime = std::string(buffer);
|
||||
}
|
||||
|
||||
fputs(zwtime.c_str(), pFile);
|
||||
fputs("\n", pFile);
|
||||
|
||||
zwvalue = std::to_string(References[0].fastalg_x) + "\t" + std::to_string(References[0].fastalg_y);
|
||||
zwvalue = zwvalue + "\t" +std::to_string(References[0].fastalg_SAD)+ "\t" +std::to_string(References[0].fastalg_min);
|
||||
zwvalue = zwvalue + "\t" +std::to_string(References[0].fastalg_max)+ "\t" +std::to_string(References[0].fastalg_avg);
|
||||
fputs(zwvalue.c_str(), pFile);
|
||||
fputs("\n", pFile);
|
||||
|
||||
zwvalue = std::to_string(References[1].fastalg_x) + "\t" + std::to_string(References[1].fastalg_y);
|
||||
zwvalue = zwvalue + "\t" +std::to_string(References[1].fastalg_SAD)+ "\t" +std::to_string(References[1].fastalg_min);
|
||||
zwvalue = zwvalue + "\t" +std::to_string(References[1].fastalg_max)+ "\t" +std::to_string(References[1].fastalg_avg);
|
||||
fputs(zwvalue.c_str(), pFile);
|
||||
fputs("\n", pFile);
|
||||
|
||||
fclose(pFile);
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowAlignment::LoadReferenceAlignmentValues(void)
|
||||
{
|
||||
FILE* pFile;
|
||||
char zw[1024];
|
||||
string zwvalue;
|
||||
std::vector<string> splitted;
|
||||
|
||||
|
||||
pFile = fopen(FileStoreRefAlignment.c_str(), "r");
|
||||
if (pFile == NULL)
|
||||
return false;
|
||||
|
||||
fgets(zw, 1024, pFile);
|
||||
ESP_LOGD(TAG, "%s", zw);
|
||||
|
||||
fgets(zw, 1024, pFile);
|
||||
splitted = ZerlegeZeile(std::string(zw), " \t");
|
||||
if (splitted.size() < 6)
|
||||
{
|
||||
fclose(pFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
References[0].fastalg_x = stoi(splitted[0]);
|
||||
References[0].fastalg_y = stoi(splitted[1]);
|
||||
References[0].fastalg_SAD = stof(splitted[2]);
|
||||
References[0].fastalg_min = stoi(splitted[3]);
|
||||
References[0].fastalg_max = stoi(splitted[4]);
|
||||
References[0].fastalg_avg = stof(splitted[5]);
|
||||
|
||||
fgets(zw, 1024, pFile);
|
||||
splitted = ZerlegeZeile(std::string(zw));
|
||||
if (splitted.size() < 6)
|
||||
{
|
||||
fclose(pFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
References[1].fastalg_x = stoi(splitted[0]);
|
||||
References[1].fastalg_y = stoi(splitted[1]);
|
||||
References[1].fastalg_SAD = stof(splitted[2]);
|
||||
References[1].fastalg_min = stoi(splitted[3]);
|
||||
References[1].fastalg_max = stoi(splitted[4]);
|
||||
References[1].fastalg_avg = stof(splitted[5]);
|
||||
|
||||
fclose(pFile);
|
||||
|
||||
|
||||
/*#ifdef DEBUG_DETAIL_ON
|
||||
std::string _zw = "\tLoadReferences[0]\tx,y:\t" + std::to_string(References[0].fastalg_x) + "\t" + std::to_string(References[0].fastalg_x);
|
||||
_zw = _zw + "\tSAD, min, max, avg:\t" + std::to_string(References[0].fastalg_SAD) + "\t" + std::to_string(References[0].fastalg_min);
|
||||
_zw = _zw + "\t" + std::to_string(References[0].fastalg_max) + "\t" + std::to_string(References[0].fastalg_avg);
|
||||
LogFile.WriteToDedicatedFile("/sdcard/alignment.txt", _zw);
|
||||
_zw = "\tLoadReferences[1]\tx,y:\t" + std::to_string(References[1].fastalg_x) + "\t" + std::to_string(References[1].fastalg_x);
|
||||
_zw = _zw + "\tSAD, min, max, avg:\t" + std::to_string(References[1].fastalg_SAD) + "\t" + std::to_string(References[1].fastalg_min);
|
||||
_zw = _zw + "\t" + std::to_string(References[1].fastalg_max) + "\t" + std::to_string(References[1].fastalg_avg);
|
||||
LogFile.WriteToDedicatedFile("/sdcard/alignment.txt", _zw);
|
||||
#endif*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void ClassFlowAlignment::DrawRef(CImageBasis *_zw)
|
||||
{
|
||||
if (_zw->ImageOkay())
|
||||
{
|
||||
_zw->drawRect(References[0].target_x, References[0].target_y, References[0].width, References[0].height, 255, 0, 0, 2);
|
||||
_zw->drawRect(References[1].target_x, References[1].target_y, References[1].width, References[1].height, 255, 0, 0, 2);
|
||||
}
|
||||
}
|
||||
54
code/components/jomjol_flowcontroll/ClassFlowAlignment.h
Normal file
54
code/components/jomjol_flowcontroll/ClassFlowAlignment.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef CLASSFLOWALIGNMENT_H
|
||||
#define CLASSFLOWALIGNMENT_H
|
||||
|
||||
#include "ClassFlow.h"
|
||||
#include "Helper.h"
|
||||
#include "CAlignAndCutImage.h"
|
||||
#include "CFindTemplate.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class ClassFlowAlignment :
|
||||
public ClassFlow
|
||||
{
|
||||
protected:
|
||||
float initalrotate;
|
||||
bool initialmirror;
|
||||
bool initialflip;
|
||||
bool use_antialiasing;
|
||||
RefInfo References[2];
|
||||
int anz_ref;
|
||||
string namerawimage;
|
||||
bool SaveAllFiles;
|
||||
CAlignAndCutImage *AlignAndCutImage;
|
||||
std::string FileStoreRefAlignment;
|
||||
float SAD_criteria;
|
||||
|
||||
void SetInitialParameter(void);
|
||||
bool LoadReferenceAlignmentValues(void);
|
||||
void SaveReferenceAlignmentValues();
|
||||
|
||||
public:
|
||||
CImageBasis *ImageBasis, *ImageTMP;
|
||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
||||
ImageData *AlgROI;
|
||||
#endif
|
||||
|
||||
ClassFlowAlignment(std::vector<ClassFlow*>* lfc);
|
||||
|
||||
CAlignAndCutImage* GetAlignAndCutImage(){return AlignAndCutImage;};
|
||||
|
||||
void DrawRef(CImageBasis *_zw);
|
||||
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
bool doFlow(string time);
|
||||
string getHTMLSingleStep(string host);
|
||||
string name(){return "ClassFlowAlignment";};
|
||||
};
|
||||
|
||||
|
||||
#endif //CLASSFLOWALIGNMENT_H
|
||||
989
code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.cpp
Normal file
989
code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.cpp
Normal file
@@ -0,0 +1,989 @@
|
||||
#include "ClassFlowCNNGeneral.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <iomanip>
|
||||
#include <sys/types.h>
|
||||
#include <sstream> // std::stringstream
|
||||
|
||||
#include "CTfLiteClass.h"
|
||||
#include "ClassLogFile.h"
|
||||
#include "esp_log.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
static const char* TAG = "CNN";
|
||||
|
||||
//#ifdef CONFIG_HEAP_TRACING_STANDALONE
|
||||
#ifdef HEAP_TRACING_CLASS_FLOW_CNN_GENERAL_DO_ALING_AND_CUT
|
||||
#include <esp_heap_trace.h>
|
||||
#define NUM_RECORDS 300
|
||||
static heap_trace_record_t trace_record[NUM_RECORDS]; // This buffer must be in internal RAM
|
||||
#endif
|
||||
|
||||
|
||||
ClassFlowCNNGeneral::ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNType _cnntype) : ClassFlowImage(NULL, TAG)
|
||||
{
|
||||
string cnnmodelfile = "";
|
||||
modelxsize = 1;
|
||||
modelysize = 1;
|
||||
CNNGoodThreshold = 0.0;
|
||||
ListFlowControll = NULL;
|
||||
previousElement = NULL;
|
||||
SaveAllFiles = false;
|
||||
disabled = false;
|
||||
isLogImageSelect = false;
|
||||
CNNType = AutoDetect;
|
||||
CNNType = _cnntype;
|
||||
flowpostalignment = _flowalign;
|
||||
imagesRetention = 5;
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution, int prev, float _before_narrow_Analog, float analogDigitalTransitionStart)
|
||||
{
|
||||
string result = "";
|
||||
|
||||
if (GENERAL[_analog]->ROI.size() == 0)
|
||||
return result;
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout _analog=" + std::to_string(_analog) + ", _extendedResolution=" + std::to_string(_extendedResolution) + ", prev=" + std::to_string(prev));
|
||||
|
||||
if (CNNType == Analogue || CNNType == Analogue100)
|
||||
{
|
||||
float number = GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float;
|
||||
int result_after_decimal_point = ((int) floor(number * 10) + 10) % 10;
|
||||
|
||||
prev = PointerEvalAnalogNew(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float, prev);
|
||||
// LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout(analog) number=" + std::to_string(number) + ", result_after_decimal_point=" + std::to_string(result_after_decimal_point) + ", prev=" + std::to_string(prev));
|
||||
result = std::to_string(prev);
|
||||
|
||||
if (_extendedResolution)
|
||||
result = result + std::to_string(result_after_decimal_point);
|
||||
|
||||
for (int i = GENERAL[_analog]->ROI.size() - 2; i >= 0; --i)
|
||||
{
|
||||
prev = PointerEvalAnalogNew(GENERAL[_analog]->ROI[i]->result_float, prev);
|
||||
result = std::to_string(prev) + result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (CNNType == Digital)
|
||||
{
|
||||
for (int i = 0; i < GENERAL[_analog]->ROI.size(); ++i)
|
||||
{
|
||||
if (GENERAL[_analog]->ROI[i]->result_klasse >= 10)
|
||||
result = result + "N";
|
||||
else
|
||||
result = result + std::to_string(GENERAL[_analog]->ROI[i]->result_klasse);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if ((CNNType == DoubleHyprid10) || (CNNType == Digital100))
|
||||
{
|
||||
|
||||
float number = GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float;
|
||||
if (number >= 0) // NaN?
|
||||
{
|
||||
if (_extendedResolution) // is only set if it is the first digit (no analogue before!)
|
||||
{
|
||||
int result_after_decimal_point = ((int) floor(number * 10)) % 10;
|
||||
int result_before_decimal_point = ((int) floor(number)) % 10;
|
||||
|
||||
result = std::to_string(result_before_decimal_point) + std::to_string(result_after_decimal_point);
|
||||
prev = result_before_decimal_point;
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout(dig100-ext) result_before_decimal_point=" + std::to_string(result_before_decimal_point) + ", result_after_decimal_point=" + std::to_string(result_after_decimal_point) + ", prev=" + std::to_string(prev));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_before_narrow_Analog >= 0)
|
||||
prev = PointerEvalHybridNew(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float, _before_narrow_Analog, prev, true, analogDigitalTransitionStart);
|
||||
else
|
||||
prev = PointerEvalHybridNew(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float, prev, prev);
|
||||
result = std::to_string(prev);
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout(dig100) prev=" + std::to_string(prev));
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = "N";
|
||||
if (_extendedResolution && (CNNType != Digital))
|
||||
result = "NN";
|
||||
}
|
||||
|
||||
for (int i = GENERAL[_analog]->ROI.size() - 2; i >= 0; --i)
|
||||
{
|
||||
if (GENERAL[_analog]->ROI[i]->result_float >= 0)
|
||||
{
|
||||
prev = PointerEvalHybridNew(GENERAL[_analog]->ROI[i]->result_float, GENERAL[_analog]->ROI[i+1]->result_float, prev);
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout#PointerEvalHybridNew()= " + std::to_string(prev));
|
||||
result = std::to_string(prev) + result;
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout#result= " + result);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
prev = -1;
|
||||
result = "N" + result;
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout(result_float<0 /'N') result_float=" + std::to_string(GENERAL[_analog]->ROI[i]->result_float));
|
||||
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determines the number of an ROI in connection with previous ROI results
|
||||
*
|
||||
* @param number: is the current ROI as float value from recognition
|
||||
* @param number_of_predecessors: is the last (lower) ROI as float from recognition
|
||||
* @param eval_predecessors: is the evaluated number. Sometimes a much lower value can change higer values
|
||||
* example: 9.8, 9.9, 0.1
|
||||
* 0.1 => 0 (eval_predecessors)
|
||||
* The 0 makes a 9.9 to 0 (eval_predecessors)
|
||||
* The 0 makes a 9.8 to 0
|
||||
* @param Analog_Predecessors false/true if the last ROI is an analog or digital ROI (default=false)
|
||||
* runs in special handling because analog is much less precise
|
||||
* @param digitalAnalogTransitionStart start of the transitionlogic begins on number_of_predecessor (default=9.2)
|
||||
*
|
||||
* @return int the determined number of the current ROI
|
||||
*/
|
||||
int ClassFlowCNNGeneral::PointerEvalHybridNew(float number, float number_of_predecessors, int eval_predecessors, bool Analog_Predecessors, float digitalAnalogTransitionStart)
|
||||
{
|
||||
int result;
|
||||
int result_after_decimal_point = ((int) floor(number * 10)) % 10;
|
||||
int result_before_decimal_point = ((int) floor(number) + 10) % 10;
|
||||
|
||||
if (eval_predecessors < 0)
|
||||
{
|
||||
// on first digit is no spezial logic for transition needed
|
||||
// we use the recognition as given. The result is the int value of the recognition
|
||||
// add precisition of 2 digits and round before trunc
|
||||
result = (int) ((int) trunc(round((number+10 % 10)*100)) ) / 100;
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalHybridNew - No predecessor - Result = " + std::to_string(result) +
|
||||
" number: " + std::to_string(number) + " number_of_predecessors = " + std::to_string(number_of_predecessors)+ " eval_predecessors = " + std::to_string(eval_predecessors) + " Digital_Uncertainty = " + std::to_string(Digital_Uncertainty));
|
||||
return result;
|
||||
}
|
||||
|
||||
if (Analog_Predecessors)
|
||||
{
|
||||
result = PointerEvalAnalogToDigitNew(number, number_of_predecessors, eval_predecessors, digitalAnalogTransitionStart);
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalHybridNew - Analog predecessor, evaluation over PointerEvalAnalogNew = " + std::to_string(result) +
|
||||
" number: " + std::to_string(number) + " number_of_predecessors = " + std::to_string(number_of_predecessors)+ " eval_predecessors = " + std::to_string(eval_predecessors) + " Digital_Uncertainty = " + std::to_string(Digital_Uncertainty));
|
||||
return result;
|
||||
}
|
||||
|
||||
if ((number_of_predecessors >= Digital_Transition_Area_Predecessor ) && (number_of_predecessors <= (10.0 - Digital_Transition_Area_Predecessor)))
|
||||
{
|
||||
// no digit change, because predecessor is far enough away (0+/-DigitalTransitionRangePredecessor) --> number is rounded
|
||||
if ((result_after_decimal_point <= DigitalBand) || (result_after_decimal_point >= (10-DigitalBand))) // Band around the digit --> Round off, as digit reaches inaccuracy in the frame
|
||||
result = ((int) round(number) + 10) % 10;
|
||||
else
|
||||
result = ((int) trunc(number) + 10) % 10;
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalHybridNew - NO analogue predecessor, no change of digits, as pre-decimal point far enough away = " + std::to_string(result) +
|
||||
" number: " + std::to_string(number) + " number_of_predecessors = " + std::to_string(number_of_predecessors)+ " eval_predecessors = " + std::to_string(eval_predecessors) + " Digital_Uncertainty = " + std::to_string(Digital_Uncertainty));
|
||||
return result;
|
||||
}
|
||||
|
||||
if (eval_predecessors <= 1) // Zero crossing at the predecessor has taken place (! evaluation via Prev_value and not number!) --> round up here (2.8 --> 3, but also 3.1 --> 3)
|
||||
{
|
||||
// We simply assume that the current digit after the zero crossing of the predecessor
|
||||
// has passed through at least half (x.5)
|
||||
if (result_after_decimal_point > 5)
|
||||
// The current digit does not yet have a zero crossing, but the predecessor does..
|
||||
result = (result_before_decimal_point + 1) % 10;
|
||||
else
|
||||
// Act. digit and predecessor have zero crossing
|
||||
result = result_before_decimal_point % 10;
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalHybridNew - NO analogue predecessor, zero crossing has taken placen = " + std::to_string(result) +
|
||||
" number: " + std::to_string(number) + " number_of_predecessors = " + std::to_string(number_of_predecessors)+ " eval_predecessors = " + std::to_string(eval_predecessors) + " Digital_Uncertainty = " + std::to_string(Digital_Uncertainty));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// remains only >= 9.x --> no zero crossing yet --> 2.8 --> 2,
|
||||
// and from 9.7(DigitalTransitionRangeLead) 3.1 --> 2
|
||||
// everything >=x.4 can be considered as current number in transition. With 9.x predecessor the current
|
||||
// number can still be x.6 - x.7.
|
||||
// Preceding (else - branch) does not already happen from 9.
|
||||
if (Digital_Transition_Area_Forward>=number_of_predecessors || result_after_decimal_point >= 4)
|
||||
// The current digit, like the previous digit, does not yet have a zero crossing.
|
||||
result = result_before_decimal_point % 10;
|
||||
else
|
||||
// current digit precedes the smaller digit (9.x). So already >=x.0 while the previous digit has not yet
|
||||
// has no zero crossing. Therefore, it is reduced by 1.
|
||||
result = (result_before_decimal_point - 1 + 10) % 10;
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalHybridNew - O analogue predecessor, >= 9.5 --> no zero crossing yet = " + std::to_string(result) +
|
||||
" number: " + std::to_string(number) + " number_of_predecessors = " + std::to_string(number_of_predecessors)+ " eval_predecessors = " + std::to_string(eval_predecessors) + " Digital_Uncertainty = " + std::to_string(Digital_Uncertainty) + " result_after_decimal_point = " + std::to_string(result_after_decimal_point));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int ClassFlowCNNGeneral::PointerEvalAnalogToDigitNew(float number, float numeral_preceder, int eval_predecessors, float analogDigitalTransitionStart)
|
||||
{
|
||||
int result;
|
||||
int result_after_decimal_point = ((int) floor(number * 10)) % 10;
|
||||
int result_before_decimal_point = ((int) floor(number) + 10) % 10;
|
||||
bool roundedUp = false;
|
||||
|
||||
// Within the digital inequalities
|
||||
if ((result_after_decimal_point >= (10-Digital_Uncertainty * 10)) // Band around the digit --> Round off, as digit reaches inaccuracy in the frame
|
||||
|| (eval_predecessors <= 4 && result_after_decimal_point>=6)) { // or digit runs after (analogue =0..4, digit >=6)
|
||||
result = (int) (round(number) + 10) % 10;
|
||||
roundedUp = true;
|
||||
// before/ after decimal point, because we adjust the number based on the uncertainty.
|
||||
result_after_decimal_point = ((int) floor(result * 10)) % 10;
|
||||
result_before_decimal_point = ((int) floor(result) + 10) % 10;
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalAnalogToDigitNew - Digital Uncertainty - Result = " + std::to_string(result) +
|
||||
" number: " + std::to_string(number) + " numeral_preceder: " + std::to_string(numeral_preceder) +
|
||||
" erg before comma: " + std::to_string(result_before_decimal_point) +
|
||||
" erg after comma: " + std::to_string(result_after_decimal_point));
|
||||
} else {
|
||||
result = (int) ((int) trunc(number) + 10) % 10;
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalAnalogToDigitNew - NO digital Uncertainty - Result = " + std::to_string(result) +
|
||||
" number: " + std::to_string(number) + " numeral_preceder = " + std::to_string(numeral_preceder));
|
||||
}
|
||||
|
||||
// No zero crossing has taken place.
|
||||
// Only eval_predecessors used because numeral_preceder could be wrong here.
|
||||
// numeral_preceder<=0.1 & eval_predecessors=9 corresponds to analogue was reset because of previous analogue that are not yet at 0.
|
||||
if ((eval_predecessors>=6 && (numeral_preceder>analogDigitalTransitionStart || numeral_preceder<=0.2) && roundedUp))
|
||||
{
|
||||
result = ((result_before_decimal_point+10) - 1) % 10;
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalAnalogToDigitNew - Nulldurchgang noch nicht stattgefunden = " + std::to_string(result) +
|
||||
" number: " + std::to_string(number) +
|
||||
" numeral_preceder = " + std::to_string(numeral_preceder) +
|
||||
" eerg after comma = " + std::to_string(result_after_decimal_point));
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
|
||||
int ClassFlowCNNGeneral::PointerEvalAnalogNew(float number, int numeral_preceder)
|
||||
{
|
||||
float number_min, number_max;
|
||||
int result;
|
||||
|
||||
if (numeral_preceder == -1)
|
||||
{
|
||||
result = (int) floor(number);
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalAnalogNew - No predecessor - Result = " + std::to_string(result) +
|
||||
" number: " + std::to_string(number) + " numeral_preceder = " + std::to_string(numeral_preceder) + " Analog_error = " + std::to_string(Analog_error));
|
||||
return result;
|
||||
}
|
||||
|
||||
number_min = number - Analog_error / 10.0;
|
||||
number_max = number + Analog_error / 10.0;
|
||||
|
||||
if ((int) floor(number_max) - (int) floor(number_min) != 0)
|
||||
{
|
||||
if (numeral_preceder <= Analog_error)
|
||||
{
|
||||
result = ((int) floor(number_max) + 10) % 10;
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalAnalogNew - number ambiguous, correction upwards - result = " + std::to_string(result) +
|
||||
" number: " + std::to_string(number) + " numeral_preceder = " + std::to_string(numeral_preceder) + " Analog_error = " + std::to_string(Analog_error));
|
||||
return result;
|
||||
}
|
||||
if (numeral_preceder >= 10 - Analog_error)
|
||||
{
|
||||
result = ((int) floor(number_min) + 10) % 10;
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalAnalogNew - number ambiguous, downward correction - result = " + std::to_string(result) +
|
||||
" number: " + std::to_string(number) + " numeral_preceder = " + std::to_string(numeral_preceder) + " Analog_error = " + std::to_string(Analog_error));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
result = ((int) floor(number) + 10) % 10;
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalAnalogNew - number unambiguous, no correction necessary - result = " + std::to_string(result) +
|
||||
" number: " + std::to_string(number) + " numeral_preceder = " + std::to_string(numeral_preceder) + " Analog_error = " + std::to_string(Analog_error));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
{
|
||||
std::vector<string> splitted;
|
||||
|
||||
aktparamgraph = trim(aktparamgraph);
|
||||
|
||||
if (aktparamgraph.size() == 0)
|
||||
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||
return false;
|
||||
|
||||
|
||||
if ((toUpper(aktparamgraph) != "[ANALOG]") && (toUpper(aktparamgraph) != ";[ANALOG]")
|
||||
&& (toUpper(aktparamgraph) != "[DIGIT]") && (toUpper(aktparamgraph) != ";[DIGIT]")
|
||||
&& (toUpper(aktparamgraph) != "[DIGITS]") && (toUpper(aktparamgraph) != ";[DIGITS]")
|
||||
) // Paragraph passt nicht
|
||||
return false;
|
||||
|
||||
if (aktparamgraph[0] == ';')
|
||||
{
|
||||
disabled = true;
|
||||
while (getNextLine(pfile, &aktparamgraph) && !isNewParagraph(aktparamgraph));
|
||||
ESP_LOGD(TAG, "[Analog/Digit] is disabled!");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||
{
|
||||
splitted = ZerlegeZeile(aktparamgraph);
|
||||
if ((toUpper(splitted[0]) == "ROIIMAGESLOCATION") && (splitted.size() > 1))
|
||||
{
|
||||
this->imagesLocation = "/sdcard" + splitted[1];
|
||||
this->isLogImage = true;
|
||||
}
|
||||
if ((toUpper(splitted[0]) == "LOGIMAGESELECT") && (splitted.size() > 1))
|
||||
{
|
||||
LogImageSelect = splitted[1];
|
||||
isLogImageSelect = true;
|
||||
}
|
||||
|
||||
if ((toUpper(splitted[0]) == "ROIIMAGESRETENTION") && (splitted.size() > 1))
|
||||
{
|
||||
this->imagesRetention = std::stoi(splitted[1]);
|
||||
}
|
||||
|
||||
if ((toUpper(splitted[0]) == "MODEL") && (splitted.size() > 1))
|
||||
{
|
||||
this->cnnmodelfile = splitted[1];
|
||||
}
|
||||
|
||||
if ((toUpper(splitted[0]) == "CNNGOODTHRESHOLD") && (splitted.size() > 1))
|
||||
{
|
||||
CNNGoodThreshold = std::stof(splitted[1]);
|
||||
}
|
||||
if (splitted.size() >= 5)
|
||||
{
|
||||
general* _analog = GetGENERAL(splitted[0], true);
|
||||
roi* neuroi = _analog->ROI[_analog->ROI.size()-1];
|
||||
neuroi->posx = std::stoi(splitted[1]);
|
||||
neuroi->posy = std::stoi(splitted[2]);
|
||||
neuroi->deltax = std::stoi(splitted[3]);
|
||||
neuroi->deltay = std::stoi(splitted[4]);
|
||||
neuroi->CCW = false;
|
||||
if (splitted.size() >= 6)
|
||||
{
|
||||
neuroi->CCW = toUpper(splitted[5]) == "TRUE";
|
||||
}
|
||||
neuroi->result_float = -1;
|
||||
neuroi->image = NULL;
|
||||
neuroi->image_org = NULL;
|
||||
}
|
||||
|
||||
if ((toUpper(splitted[0]) == "SAVEALLFILES") && (splitted.size() > 1))
|
||||
{
|
||||
if (toUpper(splitted[1]) == "TRUE")
|
||||
SaveAllFiles = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!getNetworkParameter()) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "An error occured on setting up the Network -> Disabling it!");
|
||||
disabled = true; // An error occured, disable this CNN!
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
|
||||
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
|
||||
{
|
||||
GENERAL[_ana]->ROI[i]->image = new CImageBasis("ROI " + GENERAL[_ana]->ROI[i]->name,
|
||||
modelxsize, modelysize, modelchannel);
|
||||
GENERAL[_ana]->ROI[i]->image_org = new CImageBasis("ROI " + GENERAL[_ana]->ROI[i]->name + " original",
|
||||
GENERAL[_ana]->ROI[i]->deltax, GENERAL[_ana]->ROI[i]->deltay, 3);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
general* ClassFlowCNNGeneral::FindGENERAL(string _name_number)
|
||||
{
|
||||
for (int i = 0; i < GENERAL.size(); ++i)
|
||||
if (GENERAL[i]->name == _name_number)
|
||||
return GENERAL[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
general* ClassFlowCNNGeneral::GetGENERAL(string _name, bool _create = true)
|
||||
{
|
||||
string _analog, _roi;
|
||||
int _pospunkt = _name.find_first_of(".");
|
||||
|
||||
if (_pospunkt > -1)
|
||||
{
|
||||
_analog = _name.substr(0, _pospunkt);
|
||||
_roi = _name.substr(_pospunkt+1, _name.length() - _pospunkt - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
_analog = "default";
|
||||
_roi = _name;
|
||||
}
|
||||
|
||||
general *_ret = NULL;
|
||||
|
||||
for (int i = 0; i < GENERAL.size(); ++i)
|
||||
if (GENERAL[i]->name == _analog)
|
||||
_ret = GENERAL[i];
|
||||
|
||||
if (!_create) // not found and should not be created
|
||||
return _ret;
|
||||
|
||||
if (_ret == NULL)
|
||||
{
|
||||
_ret = new general;
|
||||
_ret->name = _analog;
|
||||
GENERAL.push_back(_ret);
|
||||
}
|
||||
|
||||
roi* neuroi = new roi;
|
||||
neuroi->name = _roi;
|
||||
|
||||
_ret->ROI.push_back(neuroi);
|
||||
|
||||
ESP_LOGD(TAG, "GetGENERAL - GENERAL %s - roi %s - CCW: %d", _analog.c_str(), _roi.c_str(), neuroi->CCW);
|
||||
|
||||
return _ret;
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowCNNGeneral::getHTMLSingleStep(string host)
|
||||
{
|
||||
string result, zw;
|
||||
std::vector<HTMLInfo*> htmlinfo;
|
||||
|
||||
result = "<p>Found ROIs: </p> <p><img src=\"" + host + "/img_tmp/alg_roi.jpg\"></p>\n";
|
||||
result = result + "Analog Pointers: <p> ";
|
||||
|
||||
htmlinfo = GetHTMLInfo();
|
||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
||||
{
|
||||
std::stringstream stream;
|
||||
stream << std::fixed << std::setprecision(1) << htmlinfo[i]->val;
|
||||
zw = stream.str();
|
||||
|
||||
result = result + "<img src=\"" + host + "/img_tmp/" + htmlinfo[i]->filename + "\"> " + zw;
|
||||
delete htmlinfo[i];
|
||||
}
|
||||
htmlinfo.clear();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowCNNGeneral::doFlow(string time)
|
||||
{
|
||||
|
||||
#ifdef HEAP_TRACING_CLASS_FLOW_CNN_GENERAL_DO_ALING_AND_CUT
|
||||
//register a buffer to record the memory trace
|
||||
ESP_ERROR_CHECK( heap_trace_init_standalone(trace_record, NUM_RECORDS) );
|
||||
// start tracing
|
||||
ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_LEAKS) );
|
||||
#endif
|
||||
|
||||
if (disabled)
|
||||
return true;
|
||||
|
||||
if (!doAlignAndCut(time)){
|
||||
return false;
|
||||
}
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "doFlow after alignment");
|
||||
|
||||
doNeuralNetwork(time);
|
||||
|
||||
RemoveOldLogs();
|
||||
|
||||
#ifdef HEAP_TRACING_CLASS_FLOW_CNN_GENERAL_DO_ALING_AND_CUT
|
||||
ESP_ERROR_CHECK( heap_trace_stop() );
|
||||
heap_trace_dump();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowCNNGeneral::doAlignAndCut(string time)
|
||||
{
|
||||
if (disabled)
|
||||
return true;
|
||||
|
||||
CAlignAndCutImage *caic = flowpostalignment->GetAlignAndCutImage();
|
||||
|
||||
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
|
||||
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
|
||||
{
|
||||
ESP_LOGD(TAG, "General %d - Align&Cut", i);
|
||||
|
||||
caic->CutAndSave(GENERAL[_ana]->ROI[i]->posx, GENERAL[_ana]->ROI[i]->posy, GENERAL[_ana]->ROI[i]->deltax, GENERAL[_ana]->ROI[i]->deltay, GENERAL[_ana]->ROI[i]->image_org);
|
||||
if (SaveAllFiles)
|
||||
{
|
||||
if (GENERAL[_ana]->name == "default")
|
||||
GENERAL[_ana]->ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->ROI[i]->name + ".jpg"));
|
||||
else
|
||||
GENERAL[_ana]->ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".jpg"));
|
||||
}
|
||||
|
||||
GENERAL[_ana]->ROI[i]->image_org->Resize(modelxsize, modelysize, GENERAL[_ana]->ROI[i]->image);
|
||||
if (SaveAllFiles)
|
||||
{
|
||||
if (GENERAL[_ana]->name == "default")
|
||||
GENERAL[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->ROI[i]->name + ".jpg"));
|
||||
else
|
||||
GENERAL[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".jpg"));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void ClassFlowCNNGeneral::DrawROI(CImageBasis *_zw)
|
||||
{
|
||||
if (_zw->ImageOkay())
|
||||
{
|
||||
if (CNNType == Analogue || CNNType == Analogue100)
|
||||
{
|
||||
int r = 0;
|
||||
int g = 255;
|
||||
int b = 0;
|
||||
|
||||
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
|
||||
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
|
||||
{
|
||||
_zw->drawRect(GENERAL[_ana]->ROI[i]->posx, GENERAL[_ana]->ROI[i]->posy, GENERAL[_ana]->ROI[i]->deltax, GENERAL[_ana]->ROI[i]->deltay, r, g, b, 1);
|
||||
_zw->drawEllipse( (int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), (int) (GENERAL[_ana]->ROI[i]->deltax/2), (int) (GENERAL[_ana]->ROI[i]->deltay/2), r, g, b, 2);
|
||||
_zw->drawLine((int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) GENERAL[_ana]->ROI[i]->posy, (int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay), r, g, b, 2);
|
||||
_zw->drawLine((int) GENERAL[_ana]->ROI[i]->posx, (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), (int) GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax, (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), r, g, b, 2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int _dig = 0; _dig < GENERAL.size(); ++_dig)
|
||||
for (int i = 0; i < GENERAL[_dig]->ROI.size(); ++i)
|
||||
_zw->drawRect(GENERAL[_dig]->ROI[i]->posx, GENERAL[_dig]->ROI[i]->posy, GENERAL[_dig]->ROI[i]->deltax, GENERAL[_dig]->ROI[i]->deltay, 0, 0, (255 - _dig*100), 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowCNNGeneral::getNetworkParameter()
|
||||
{
|
||||
if (disabled)
|
||||
return true;
|
||||
|
||||
CTfLiteClass *tflite = new CTfLiteClass;
|
||||
string zwcnn = "/sdcard" + cnnmodelfile;
|
||||
zwcnn = FormatFileName(zwcnn);
|
||||
ESP_LOGD(TAG, "%s", zwcnn.c_str());
|
||||
if (!tflite->LoadModel(zwcnn)) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't load tflite model " + cnnmodelfile + " -> Init aborted!");
|
||||
LogFile.WriteHeapInfo("getNetworkParameter-LoadModel");
|
||||
delete tflite;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!tflite->MakeAllocate()) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate tflite model -> Init aborted!");
|
||||
LogFile.WriteHeapInfo("getNetworkParameter-MakeAllocate");
|
||||
delete tflite;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CNNType == AutoDetect)
|
||||
{
|
||||
tflite->GetInputDimension(false);
|
||||
modelxsize = tflite->ReadInputDimenstion(0);
|
||||
modelysize = tflite->ReadInputDimenstion(1);
|
||||
modelchannel = tflite->ReadInputDimenstion(2);
|
||||
|
||||
int _anzoutputdimensions = tflite->GetAnzOutPut();
|
||||
switch (_anzoutputdimensions)
|
||||
{
|
||||
case 2:
|
||||
CNNType = Analogue;
|
||||
ESP_LOGD(TAG, "TFlite-Type set to Analogue");
|
||||
break;
|
||||
case 10:
|
||||
CNNType = DoubleHyprid10;
|
||||
ESP_LOGD(TAG, "TFlite-Type set to DoubleHyprid10");
|
||||
break;
|
||||
case 11:
|
||||
CNNType = Digital;
|
||||
ESP_LOGD(TAG, "TFlite-Type set to Digital");
|
||||
break;
|
||||
/* case 20:
|
||||
CNNType = DigitalHyprid10;
|
||||
ESP_LOGD(TAG, "TFlite-Type set to DigitalHyprid10");
|
||||
break;
|
||||
*/
|
||||
// case 22:
|
||||
// CNNType = DigitalHyprid;
|
||||
// ESP_LOGD(TAG, "TFlite-Type set to DigitalHyprid");
|
||||
// break;
|
||||
case 100:
|
||||
if (modelxsize==32 && modelysize == 32) {
|
||||
CNNType = Analogue100;
|
||||
ESP_LOGD(TAG, "TFlite-Type set to Analogue100");
|
||||
} else {
|
||||
CNNType = Digital100;
|
||||
ESP_LOGD(TAG, "TFlite-Type set to Digital");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "tflite does not fit the firmware (outout_dimension=" + std::to_string(_anzoutputdimensions) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
delete tflite;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
|
||||
{
|
||||
if (disabled)
|
||||
return true;
|
||||
|
||||
string logPath = CreateLogFolder(time);
|
||||
|
||||
CTfLiteClass *tflite = new CTfLiteClass;
|
||||
string zwcnn = "/sdcard" + cnnmodelfile;
|
||||
zwcnn = FormatFileName(zwcnn);
|
||||
ESP_LOGD(TAG, "%s", zwcnn.c_str());
|
||||
|
||||
if (!tflite->LoadModel(zwcnn)) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't load tflite model " + cnnmodelfile + " -> Exec aborted this round!");
|
||||
LogFile.WriteHeapInfo("doNeuralNetwork-LoadModel");
|
||||
delete tflite;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!tflite->MakeAllocate()) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate tfilte model -> Exec aborted this round!");
|
||||
LogFile.WriteHeapInfo("doNeuralNetwork-MakeAllocate");
|
||||
delete tflite;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int n = 0; n < GENERAL.size(); ++n) // For each NUMBER
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Processing Number '" + GENERAL[n]->name + "'");
|
||||
for (int roi = 0; roi < GENERAL[n]->ROI.size(); ++roi) // For each ROI
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "ROI #" + std::to_string(roi) + " - TfLite");
|
||||
//ESP_LOGD(TAG, "General %d - TfLite", i);
|
||||
|
||||
switch (CNNType) {
|
||||
case Analogue:
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CNN Type: Analogue");
|
||||
{
|
||||
float f1, f2;
|
||||
f1 = 0; f2 = 0;
|
||||
|
||||
tflite->LoadInputImageBasis(GENERAL[n]->ROI[roi]->image);
|
||||
tflite->Invoke();
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "After Invoke");
|
||||
|
||||
f1 = tflite->GetOutputValue(0);
|
||||
f2 = tflite->GetOutputValue(1);
|
||||
float result = fmod(atan2(f1, f2) / (M_PI * 2) + 2, 1);
|
||||
|
||||
if(GENERAL[n]->ROI[roi]->CCW)
|
||||
GENERAL[n]->ROI[roi]->result_float = 10 - (result * 10);
|
||||
else
|
||||
GENERAL[n]->ROI[roi]->result_float = result * 10;
|
||||
|
||||
ESP_LOGD(TAG, "General result (Analog)%i - CCW: %d - %f", roi, GENERAL[n]->ROI[roi]->CCW, GENERAL[n]->ROI[roi]->result_float);
|
||||
if (isLogImage)
|
||||
LogImage(logPath, GENERAL[n]->ROI[roi]->name, &GENERAL[n]->ROI[roi]->result_float, NULL, time, GENERAL[n]->ROI[roi]->image_org);
|
||||
} break;
|
||||
|
||||
case Digital:
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CNN Type: Digital");
|
||||
{
|
||||
GENERAL[n]->ROI[roi]->result_klasse = 0;
|
||||
GENERAL[n]->ROI[roi]->result_klasse = tflite->GetClassFromImageBasis(GENERAL[n]->ROI[roi]->image);
|
||||
ESP_LOGD(TAG, "General result (Digit)%i: %d", roi, GENERAL[n]->ROI[roi]->result_klasse);
|
||||
|
||||
if (isLogImage)
|
||||
{
|
||||
string _imagename = GENERAL[n]->name + "_" + GENERAL[n]->ROI[roi]->name;
|
||||
if (isLogImageSelect)
|
||||
{
|
||||
if (LogImageSelect.find(GENERAL[n]->ROI[roi]->name) != std::string::npos)
|
||||
LogImage(logPath, _imagename, NULL, &GENERAL[n]->ROI[roi]->result_klasse, time, GENERAL[n]->ROI[roi]->image_org);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogImage(logPath, _imagename, NULL, &GENERAL[n]->ROI[roi]->result_klasse, time, GENERAL[n]->ROI[roi]->image_org);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
|
||||
case DoubleHyprid10:
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CNN Type: DoubleHyprid10");
|
||||
int _num, _numplus, _numminus;
|
||||
float _val, _valplus, _valminus;
|
||||
float _fit;
|
||||
float _result_save_file;
|
||||
|
||||
tflite->LoadInputImageBasis(GENERAL[n]->ROI[roi]->image);
|
||||
tflite->Invoke();
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "After Invoke");
|
||||
|
||||
_num = tflite->GetOutClassification(0, 9);
|
||||
_numplus = (_num + 1) % 10;
|
||||
_numminus = (_num - 1 + 10) % 10;
|
||||
|
||||
_val = tflite->GetOutputValue(_num);
|
||||
_valplus = tflite->GetOutputValue(_numplus);
|
||||
_valminus = tflite->GetOutputValue(_numminus);
|
||||
|
||||
float result = _num;
|
||||
|
||||
if (_valplus > _valminus)
|
||||
{
|
||||
result = result + _valplus / (_valplus + _val);
|
||||
_fit = _val + _valplus;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = result - _valminus / (_val + _valminus);
|
||||
_fit = _val + _valminus;
|
||||
|
||||
}
|
||||
if (result >= 10)
|
||||
result = result - 10;
|
||||
if (result < 0)
|
||||
result = result + 10;
|
||||
|
||||
string zw = "_num (p, m): " + to_string(_num) + " " + to_string(_numplus) + " " + to_string(_numminus);
|
||||
zw = zw + " _val (p, m): " + to_string(_val) + " " + to_string(_valplus) + " " + to_string(_valminus);
|
||||
zw = zw + " result: " + to_string(result) + " _fit: " + to_string(_fit);
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, zw);
|
||||
|
||||
|
||||
_result_save_file = result;
|
||||
|
||||
if (_fit < CNNGoodThreshold)
|
||||
{
|
||||
GENERAL[n]->ROI[roi]->isReject = true;
|
||||
result = -1;
|
||||
_result_save_file+= 100; // In case fit is not sufficient, the result should still be saved with "-10x.y".
|
||||
string zw = "Value Rejected due to Threshold (Fit: " + to_string(_fit) + ", Threshold: " + to_string(CNNGoodThreshold) + ")";
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, zw);
|
||||
}
|
||||
else
|
||||
{
|
||||
GENERAL[n]->ROI[roi]->isReject = false;
|
||||
}
|
||||
|
||||
|
||||
GENERAL[n]->ROI[roi]->result_float = result;
|
||||
ESP_LOGD(TAG, "Result General(Analog)%i: %f", roi, GENERAL[n]->ROI[roi]->result_float);
|
||||
|
||||
if (isLogImage)
|
||||
{
|
||||
string _imagename = GENERAL[n]->name + "_" + GENERAL[n]->ROI[roi]->name;
|
||||
if (isLogImageSelect)
|
||||
{
|
||||
if (LogImageSelect.find(GENERAL[n]->ROI[roi]->name) != std::string::npos)
|
||||
LogImage(logPath, _imagename, &_result_save_file, NULL, time, GENERAL[n]->ROI[roi]->image_org);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogImage(logPath, _imagename, &_result_save_file, NULL, time, GENERAL[n]->ROI[roi]->image_org);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Digital100:
|
||||
case Analogue100:
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CNN Type: Digital100 or Analogue100");
|
||||
int _num;
|
||||
float _result_save_file;
|
||||
|
||||
tflite->LoadInputImageBasis(GENERAL[n]->ROI[roi]->image);
|
||||
tflite->Invoke();
|
||||
|
||||
_num = tflite->GetOutClassification();
|
||||
|
||||
if(GENERAL[n]->ROI[roi]->CCW)
|
||||
GENERAL[n]->ROI[roi]->result_float = 10 - ((float)_num / 10.0);
|
||||
else
|
||||
GENERAL[n]->ROI[roi]->result_float = (float)_num / 10.0;
|
||||
|
||||
_result_save_file = GENERAL[n]->ROI[roi]->result_float;
|
||||
|
||||
|
||||
GENERAL[n]->ROI[roi]->isReject = false;
|
||||
|
||||
ESP_LOGD(TAG, "Result General(Analog)%i - CCW: %d - %f", roi, GENERAL[n]->ROI[roi]->CCW, GENERAL[n]->ROI[roi]->result_float);
|
||||
|
||||
if (isLogImage)
|
||||
{
|
||||
string _imagename = GENERAL[n]->name + "_" + GENERAL[n]->ROI[roi]->name;
|
||||
if (isLogImageSelect)
|
||||
{
|
||||
if (LogImageSelect.find(GENERAL[n]->ROI[roi]->name) != std::string::npos)
|
||||
LogImage(logPath, _imagename, &_result_save_file, NULL, time, GENERAL[n]->ROI[roi]->image_org);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogImage(logPath, _imagename, &_result_save_file, NULL, time, GENERAL[n]->ROI[roi]->image_org);
|
||||
}
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete tflite;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowCNNGeneral::isExtendedResolution(int _number)
|
||||
{
|
||||
if (CNNType == Digital)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
std::vector<HTMLInfo*> ClassFlowCNNGeneral::GetHTMLInfo()
|
||||
{
|
||||
std::vector<HTMLInfo*> result;
|
||||
|
||||
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
|
||||
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
|
||||
{
|
||||
ESP_LOGD(TAG, "Image: %d", (int) GENERAL[_ana]->ROI[i]->image);
|
||||
if (GENERAL[_ana]->ROI[i]->image)
|
||||
{
|
||||
if (GENERAL[_ana]->name == "default")
|
||||
GENERAL[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->ROI[i]->name + ".jpg"));
|
||||
else
|
||||
GENERAL[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".jpg"));
|
||||
}
|
||||
|
||||
HTMLInfo *zw = new HTMLInfo;
|
||||
if (GENERAL[_ana]->name == "default")
|
||||
{
|
||||
zw->filename = GENERAL[_ana]->ROI[i]->name + ".jpg";
|
||||
zw->filename_org = GENERAL[_ana]->ROI[i]->name + ".jpg";
|
||||
}
|
||||
else
|
||||
{
|
||||
zw->filename = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".jpg";
|
||||
zw->filename_org = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".jpg";
|
||||
}
|
||||
|
||||
if (CNNType == Digital)
|
||||
zw->val = GENERAL[_ana]->ROI[i]->result_klasse;
|
||||
else
|
||||
zw->val = GENERAL[_ana]->ROI[i]->result_float;
|
||||
zw->image = GENERAL[_ana]->ROI[i]->image;
|
||||
zw->image_org = GENERAL[_ana]->ROI[i]->image_org;
|
||||
|
||||
result.push_back(zw);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int ClassFlowCNNGeneral::getNumberGENERAL()
|
||||
{
|
||||
return GENERAL.size();
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowCNNGeneral::getNameGENERAL(int _analog)
|
||||
{
|
||||
if (_analog < GENERAL.size())
|
||||
return GENERAL[_analog]->name;
|
||||
|
||||
return "GENERAL DOES NOT EXIST";
|
||||
}
|
||||
|
||||
|
||||
general* ClassFlowCNNGeneral::GetGENERAL(int _analog)
|
||||
{
|
||||
if (_analog < GENERAL.size())
|
||||
return GENERAL[_analog];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void ClassFlowCNNGeneral::UpdateNameNumbers(std::vector<std::string> *_name_numbers)
|
||||
{
|
||||
for (int _dig = 0; _dig < GENERAL.size(); _dig++)
|
||||
{
|
||||
std::string _name = GENERAL[_dig]->name;
|
||||
bool found = false;
|
||||
for (int i = 0; i < (*_name_numbers).size(); ++i)
|
||||
{
|
||||
if ((*_name_numbers)[i] == _name)
|
||||
found = true;
|
||||
}
|
||||
if (!found)
|
||||
(*_name_numbers).push_back(_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowCNNGeneral::getReadoutRawString(int _analog)
|
||||
{
|
||||
string rt = "";
|
||||
|
||||
if (_analog >= GENERAL.size() || GENERAL[_analog]==NULL || GENERAL[_analog]->ROI.size() == 0)
|
||||
return rt;
|
||||
|
||||
for (int i = 0; i < GENERAL[_analog]->ROI.size(); ++i)
|
||||
{
|
||||
if (CNNType == Analogue || CNNType == Analogue100)
|
||||
{
|
||||
rt = rt + "," + RundeOutput(GENERAL[_analog]->ROI[i]->result_float, 1);
|
||||
}
|
||||
|
||||
if (CNNType == Digital)
|
||||
{
|
||||
if (GENERAL[_analog]->ROI[i]->result_klasse == 10)
|
||||
rt = rt + ",N";
|
||||
else
|
||||
rt = rt + "," + RundeOutput(GENERAL[_analog]->ROI[i]->result_klasse, 0);
|
||||
}
|
||||
|
||||
if ((CNNType == DoubleHyprid10) || (CNNType == Digital100))
|
||||
{
|
||||
rt = rt + "," + RundeOutput(GENERAL[_analog]->ROI[i]->result_float, 1);
|
||||
}
|
||||
}
|
||||
return rt;
|
||||
}
|
||||
|
||||
88
code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.h
Normal file
88
code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.h
Normal file
@@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef CLASSFLOWCNNGENERAL_H
|
||||
#define CLASSFLOWCNNGENERAL_H
|
||||
|
||||
#include"ClassFlowDefineTypes.h"
|
||||
#include "ClassFlowAlignment.h"
|
||||
|
||||
|
||||
enum t_CNNType {
|
||||
AutoDetect,
|
||||
Analogue,
|
||||
Analogue100,
|
||||
Digital,
|
||||
DigitalHyprid10,
|
||||
DoubleHyprid10,
|
||||
Digital100,
|
||||
None
|
||||
};
|
||||
|
||||
class ClassFlowCNNGeneral :
|
||||
public ClassFlowImage
|
||||
{
|
||||
protected:
|
||||
t_CNNType CNNType;
|
||||
std::vector<general*> GENERAL;
|
||||
float CNNGoodThreshold;
|
||||
|
||||
//moved to define.h
|
||||
//float Analog_error = 3.0;
|
||||
//float AnalogToDigtalFehler = 0.8;
|
||||
//float Digital_Uncertainty = 0.2;
|
||||
//int DigitalBand = 3;
|
||||
//float Digital_Transition_Range_Predecessor = 2;
|
||||
//float Digital_Transition_Area_Predecessor = 0.7; // 9.3 - 0.7
|
||||
//float Digital_Transition_Area_Forward = 9.7; // Pre-run zero crossing only happens from approx. 9.7 onwards
|
||||
|
||||
string cnnmodelfile;
|
||||
int modelxsize, modelysize, modelchannel;
|
||||
bool isLogImageSelect;
|
||||
string LogImageSelect;
|
||||
ClassFlowAlignment* flowpostalignment;
|
||||
|
||||
bool SaveAllFiles;
|
||||
|
||||
int PointerEvalAnalogNew(float zahl, int numeral_preceder);
|
||||
int PointerEvalAnalogToDigitNew(float zahl, float numeral_preceder, int eval_predecessors, float analogDigitalTransitionStart);
|
||||
int PointerEvalHybridNew(float zahl, float number_of_predecessors, int eval_predecessors, bool Analog_Predecessors = false, float analogDigitalTransitionStart=9.2);
|
||||
|
||||
|
||||
|
||||
bool doNeuralNetwork(string time);
|
||||
bool doAlignAndCut(string time);
|
||||
|
||||
bool getNetworkParameter();
|
||||
|
||||
public:
|
||||
ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNType _cnntype = AutoDetect);
|
||||
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
bool doFlow(string time);
|
||||
|
||||
string getHTMLSingleStep(string host);
|
||||
string getReadout(int _analog, bool _extendedResolution = false, int prev = -1, float _before_narrow_Analog = -1, float analogDigitalTransitionStart=9.2);
|
||||
|
||||
string getReadoutRawString(int _analog);
|
||||
|
||||
void DrawROI(CImageBasis *_zw);
|
||||
|
||||
std::vector<HTMLInfo*> GetHTMLInfo();
|
||||
|
||||
int getNumberGENERAL();
|
||||
general* GetGENERAL(int _analog);
|
||||
general* GetGENERAL(string _name, bool _create);
|
||||
general* FindGENERAL(string _name_number);
|
||||
string getNameGENERAL(int _analog);
|
||||
|
||||
bool isExtendedResolution(int _number = 0);
|
||||
|
||||
void UpdateNameNumbers(std::vector<std::string> *_name_numbers);
|
||||
|
||||
t_CNNType getCNNType(){return CNNType;};
|
||||
|
||||
string name(){return "ClassFlowCNNGeneral";};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
929
code/components/jomjol_flowcontroll/ClassFlowControll.cpp
Normal file
929
code/components/jomjol_flowcontroll/ClassFlowControll.cpp
Normal file
@@ -0,0 +1,929 @@
|
||||
#include "ClassFlowControll.h"
|
||||
|
||||
#include "connect_wlan.h"
|
||||
#include "read_wlanini.h"
|
||||
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <dirent.h>
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
#include "time_sntp.h"
|
||||
#include "Helper.h"
|
||||
#include "server_ota.h"
|
||||
#ifdef ENABLE_MQTT
|
||||
#include "interface_mqtt.h"
|
||||
#include "server_mqtt.h"
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
#include "server_help.h"
|
||||
#include "MainFlowControl.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
static const char* TAG = "FLOWCTRL";
|
||||
|
||||
//#define DEBUG_DETAIL_ON
|
||||
|
||||
|
||||
std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _host){
|
||||
std::string _classname = "";
|
||||
std::string result = "";
|
||||
|
||||
ESP_LOGD(TAG, "Step %s start", _stepname.c_str());
|
||||
|
||||
if ((_stepname.compare("[TakeImage]") == 0) || (_stepname.compare(";[TakeImage]") == 0)){
|
||||
_classname = "ClassFlowTakeImage";
|
||||
}
|
||||
if ((_stepname.compare("[Alignment]") == 0) || (_stepname.compare(";[Alignment]") == 0)){
|
||||
_classname = "ClassFlowAlignment";
|
||||
}
|
||||
if ((_stepname.compare(0, 7, "[Digits") == 0) || (_stepname.compare(0, 8, ";[Digits") == 0)) {
|
||||
_classname = "ClassFlowCNNGeneral";
|
||||
}
|
||||
if ((_stepname.compare("[Analog]") == 0) || (_stepname.compare(";[Analog]") == 0)){
|
||||
_classname = "ClassFlowCNNGeneral";
|
||||
}
|
||||
#ifdef ENABLE_MQTT
|
||||
if ((_stepname.compare("[MQTT]") == 0) || (_stepname.compare(";[MQTT]") == 0)){
|
||||
_classname = "ClassFlowMQTT";
|
||||
}
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
#ifdef ENABLE_INFLUXDB
|
||||
if ((_stepname.compare("[InfluxDB]") == 0) || (_stepname.compare(";[InfluxDB]") == 0)){
|
||||
_classname = "ClassFlowInfluxDB";
|
||||
}
|
||||
if ((_stepname.compare("[InfluxDBv2]") == 0) || (_stepname.compare(";[InfluxDBv2]") == 0)){
|
||||
_classname = "ClassFlowInfluxDBv2";
|
||||
}
|
||||
#endif //ENABLE_INFLUXDB
|
||||
|
||||
for (int i = 0; i < FlowControll.size(); ++i)
|
||||
if (FlowControll[i]->name().compare(_classname) == 0){
|
||||
if (!(FlowControll[i]->name().compare("ClassFlowTakeImage") == 0)) // if it is a TakeImage, the image does not need to be included, this happens automatically with the html query.
|
||||
FlowControll[i]->doFlow("");
|
||||
result = FlowControll[i]->getHTMLSingleStep(_host);
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Step %s end", _stepname.c_str());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
std::string ClassFlowControll::TranslateAktstatus(std::string _input)
|
||||
{
|
||||
if (_input.compare("ClassFlowTakeImage") == 0)
|
||||
return ("Take Image");
|
||||
if (_input.compare("ClassFlowAlignment") == 0)
|
||||
return ("Aligning");
|
||||
if (_input.compare("ClassFlowCNNGeneral") == 0)
|
||||
return ("Digitalization of ROIs");
|
||||
#ifdef ENABLE_MQTT
|
||||
if (_input.compare("ClassFlowMQTT") == 0)
|
||||
return ("Sending MQTT");
|
||||
#endif //ENABLE_MQTT
|
||||
#ifdef ENABLE_INFLUXDB
|
||||
if (_input.compare("ClassFlowInfluxDB") == 0)
|
||||
return ("Sending InfluxDB");
|
||||
if (_input.compare("ClassFlowInfluxDBv2") == 0)
|
||||
return ("Sending InfluxDBv2");
|
||||
#endif //ENABLE_INFLUXDB
|
||||
if (_input.compare("ClassFlowPostProcessing") == 0)
|
||||
return ("Post-Processing");
|
||||
|
||||
return "Unkown Status";
|
||||
}
|
||||
|
||||
|
||||
std::vector<HTMLInfo*> ClassFlowControll::GetAllDigital()
|
||||
{
|
||||
if (flowdigit)
|
||||
{
|
||||
ESP_LOGD(TAG, "ClassFlowControll::GetAllDigital - flowdigit != NULL");
|
||||
return flowdigit->GetHTMLInfo();
|
||||
}
|
||||
|
||||
std::vector<HTMLInfo*> empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
|
||||
std::vector<HTMLInfo*> ClassFlowControll::GetAllAnalog()
|
||||
{
|
||||
if (flowanalog)
|
||||
return flowanalog->GetHTMLInfo();
|
||||
|
||||
std::vector<HTMLInfo*> empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
|
||||
t_CNNType ClassFlowControll::GetTypeDigital()
|
||||
{
|
||||
if (flowdigit)
|
||||
return flowdigit->getCNNType();
|
||||
|
||||
return t_CNNType::None;
|
||||
}
|
||||
|
||||
|
||||
t_CNNType ClassFlowControll::GetTypeAnalog()
|
||||
{
|
||||
if (flowanalog)
|
||||
return flowanalog->getCNNType();
|
||||
|
||||
return t_CNNType::None;
|
||||
}
|
||||
|
||||
|
||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
||||
void ClassFlowControll::DigitalDrawROI(CImageBasis *_zw)
|
||||
{
|
||||
if (flowdigit)
|
||||
flowdigit->DrawROI(_zw);
|
||||
}
|
||||
|
||||
|
||||
void ClassFlowControll::AnalogDrawROI(CImageBasis *_zw)
|
||||
{
|
||||
if (flowanalog)
|
||||
flowanalog->DrawROI(_zw);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef ENABLE_MQTT
|
||||
bool ClassFlowControll::StartMQTTService()
|
||||
{
|
||||
/* Start the MQTT service */
|
||||
for (int i = 0; i < FlowControll.size(); ++i) {
|
||||
if (FlowControll[i]->name().compare("ClassFlowMQTT") == 0) {
|
||||
return ((ClassFlowMQTT*) (FlowControll[i]))->Start(AutoInterval);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
|
||||
void ClassFlowControll::SetInitialParameter(void)
|
||||
{
|
||||
AutoStart = false;
|
||||
SetupModeActive = false;
|
||||
AutoInterval = 10; // Minutes
|
||||
flowdigit = NULL;
|
||||
flowanalog = NULL;
|
||||
flowpostprocessing = NULL;
|
||||
disabled = false;
|
||||
aktRunNr = 0;
|
||||
aktstatus = "Flow task not yet created";
|
||||
aktstatusWithTime = aktstatus;
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowControll::getIsAutoStart(void)
|
||||
{
|
||||
return AutoStart;
|
||||
}
|
||||
|
||||
|
||||
void ClassFlowControll::setAutoStartInterval(long &_interval)
|
||||
{
|
||||
_interval = AutoInterval * 60 * 1000; // AutoInterval: minutes -> ms
|
||||
}
|
||||
|
||||
|
||||
ClassFlow* ClassFlowControll::CreateClassFlow(std::string _type)
|
||||
{
|
||||
ClassFlow* cfc = NULL;
|
||||
|
||||
_type = trim(_type);
|
||||
|
||||
if (toUpper(_type).compare("[TAKEIMAGE]") == 0)
|
||||
{
|
||||
cfc = new ClassFlowTakeImage(&FlowControll);
|
||||
flowtakeimage = (ClassFlowTakeImage*) cfc;
|
||||
}
|
||||
if (toUpper(_type).compare("[ALIGNMENT]") == 0)
|
||||
{
|
||||
cfc = new ClassFlowAlignment(&FlowControll);
|
||||
flowalignment = (ClassFlowAlignment*) cfc;
|
||||
}
|
||||
if (toUpper(_type).compare("[ANALOG]") == 0)
|
||||
{
|
||||
cfc = new ClassFlowCNNGeneral(flowalignment);
|
||||
flowanalog = (ClassFlowCNNGeneral*) cfc;
|
||||
}
|
||||
if (toUpper(_type).compare(0, 7, "[DIGITS") == 0)
|
||||
{
|
||||
cfc = new ClassFlowCNNGeneral(flowalignment);
|
||||
flowdigit = (ClassFlowCNNGeneral*) cfc;
|
||||
}
|
||||
#ifdef ENABLE_MQTT
|
||||
if (toUpper(_type).compare("[MQTT]") == 0)
|
||||
cfc = new ClassFlowMQTT(&FlowControll);
|
||||
#endif //ENABLE_MQTT
|
||||
#ifdef ENABLE_INFLUXDB
|
||||
if (toUpper(_type).compare("[INFLUXDB]") == 0)
|
||||
cfc = new ClassFlowInfluxDB(&FlowControll);
|
||||
if (toUpper(_type).compare("[INFLUXDBV2]") == 0)
|
||||
cfc = new ClassFlowInfluxDBv2(&FlowControll);
|
||||
#endif //ENABLE_INFLUXDB
|
||||
|
||||
if (toUpper(_type).compare("[POSTPROCESSING]") == 0)
|
||||
{
|
||||
cfc = new ClassFlowPostProcessing(&FlowControll, flowanalog, flowdigit);
|
||||
flowpostprocessing = (ClassFlowPostProcessing*) cfc;
|
||||
}
|
||||
|
||||
if (cfc) // Attached only if it is not [AutoTimer], because this is for FlowControll
|
||||
FlowControll.push_back(cfc);
|
||||
|
||||
if (toUpper(_type).compare("[AUTOTIMER]") == 0)
|
||||
cfc = this;
|
||||
|
||||
if (toUpper(_type).compare("[DATALOGGING]") == 0)
|
||||
cfc = this;
|
||||
|
||||
if (toUpper(_type).compare("[DEBUG]") == 0)
|
||||
cfc = this;
|
||||
|
||||
if (toUpper(_type).compare("[SYSTEM]") == 0)
|
||||
cfc = this;
|
||||
|
||||
return cfc;
|
||||
}
|
||||
|
||||
|
||||
void ClassFlowControll::InitFlow(std::string config)
|
||||
{
|
||||
aktstatus = "Initialization";
|
||||
aktstatusWithTime = aktstatus;
|
||||
|
||||
//#ifdef ENABLE_MQTT
|
||||
//MQTTPublish(mqttServer_getMainTopic() + "/" + "status", "Initialization", 1, false); // Right now, not possible -> MQTT Service is going to be started later
|
||||
//#endif //ENABLE_MQTT
|
||||
|
||||
string line;
|
||||
flowpostprocessing = NULL;
|
||||
|
||||
ClassFlow* cfc;
|
||||
FILE* pFile;
|
||||
config = FormatFileName(config);
|
||||
pFile = fopen(config.c_str(), "r");
|
||||
|
||||
line = "";
|
||||
|
||||
char zw[1024];
|
||||
if (pFile != NULL)
|
||||
{
|
||||
fgets(zw, 1024, pFile);
|
||||
ESP_LOGD(TAG, "%s", zw);
|
||||
line = std::string(zw);
|
||||
}
|
||||
|
||||
while ((line.size() > 0) && !(feof(pFile)))
|
||||
{
|
||||
cfc = CreateClassFlow(line);
|
||||
// printf("Name: %s\n", cfc->name().c_str());
|
||||
if (cfc)
|
||||
{
|
||||
ESP_LOGD(TAG, "Start ReadParameter (%s)", line.c_str());
|
||||
cfc->ReadParameter(pFile, line);
|
||||
}
|
||||
else
|
||||
{
|
||||
line = "";
|
||||
if (fgets(zw, 1024, pFile) && !feof(pFile))
|
||||
{
|
||||
ESP_LOGD(TAG, "Read: %s", zw);
|
||||
line = std::string(zw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(pFile);
|
||||
}
|
||||
|
||||
|
||||
std::string* ClassFlowControll::getActStatusWithTime()
|
||||
{
|
||||
return &aktstatusWithTime;
|
||||
}
|
||||
|
||||
|
||||
std::string* ClassFlowControll::getActStatus()
|
||||
{
|
||||
return &aktstatus;
|
||||
}
|
||||
|
||||
|
||||
void ClassFlowControll::setActStatus(std::string _aktstatus)
|
||||
{
|
||||
aktstatus = _aktstatus;
|
||||
aktstatusWithTime = aktstatus;
|
||||
}
|
||||
|
||||
|
||||
void ClassFlowControll::doFlowTakeImageOnly(string time)
|
||||
{
|
||||
std::string zw_time;
|
||||
|
||||
for (int i = 0; i < FlowControll.size(); ++i)
|
||||
{
|
||||
if (FlowControll[i]->name() == "ClassFlowTakeImage") {
|
||||
zw_time = getCurrentTimeString("%H:%M:%S");
|
||||
aktstatus = TranslateAktstatus(FlowControll[i]->name());
|
||||
aktstatusWithTime = aktstatus + " (" + zw_time + ")";
|
||||
#ifdef ENABLE_MQTT
|
||||
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", aktstatus, 1, false);
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
FlowControll[i]->doFlow(time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowControll::doFlow(string time)
|
||||
{
|
||||
bool result = true;
|
||||
std::string zw_time;
|
||||
int repeat = 0;
|
||||
int qos = 1;
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("ClassFlowControll::doFlow - Start");
|
||||
#endif
|
||||
|
||||
/* Check if we have a valid date/time and if not restart the NTP client */
|
||||
/* if (! getTimeIsSet()) {
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Time not set, restarting NTP Client!");
|
||||
restartNtpClient();
|
||||
}*/
|
||||
|
||||
//checkNtpStatus(0);
|
||||
|
||||
for (int i = 0; i < FlowControll.size(); ++i)
|
||||
{
|
||||
zw_time = getCurrentTimeString("%H:%M:%S");
|
||||
aktstatus = TranslateAktstatus(FlowControll[i]->name());
|
||||
aktstatusWithTime = aktstatus + " (" + zw_time + ")";
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Status: " + aktstatusWithTime);
|
||||
#ifdef ENABLE_MQTT
|
||||
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", aktstatus, qos, false);
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
string zw = "FlowControll.doFlow - " + FlowControll[i]->name();
|
||||
LogFile.WriteHeapInfo(zw);
|
||||
#endif
|
||||
|
||||
if (!FlowControll[i]->doFlow(time)){
|
||||
repeat++;
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Fehler im vorheriger Schritt - wird zum " + to_string(repeat) + ". Mal wiederholt");
|
||||
if (i) i -= 1; // vPrevious step must be repeated (probably take pictures)
|
||||
result = false;
|
||||
if (repeat > 5) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Wiederholung 5x nicht erfolgreich --> reboot");
|
||||
doReboot();
|
||||
//Step was repeated 5x --> reboot
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("ClassFlowControll::doFlow");
|
||||
#endif
|
||||
}
|
||||
|
||||
zw_time = getCurrentTimeString("%H:%M:%S");
|
||||
aktstatus = "Flow finished";
|
||||
aktstatusWithTime = aktstatus + " (" + zw_time + ")";
|
||||
//LogFile.WriteToFile(ESP_LOG_INFO, TAG, aktstatusWithTime);
|
||||
#ifdef ENABLE_MQTT
|
||||
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", aktstatus, qos, false);
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowControll::getReadoutAll(int _type)
|
||||
{
|
||||
std::string out = "";
|
||||
if (flowpostprocessing)
|
||||
{
|
||||
std::vector<NumberPost*> *numbers = flowpostprocessing->GetNumbers();
|
||||
|
||||
for (int i = 0; i < (*numbers).size(); ++i)
|
||||
{
|
||||
out = out + (*numbers)[i]->name + "\t";
|
||||
switch (_type) {
|
||||
case READOUT_TYPE_VALUE:
|
||||
out = out + (*numbers)[i]->ReturnValue;
|
||||
break;
|
||||
case READOUT_TYPE_PREVALUE:
|
||||
if (flowpostprocessing->PreValueUse)
|
||||
{
|
||||
if ((*numbers)[i]->PreValueOkay)
|
||||
out = out + (*numbers)[i]->ReturnPreValue;
|
||||
else
|
||||
out = out + "PreValue too old";
|
||||
}
|
||||
else
|
||||
out = out + "PreValue deactivated";
|
||||
break;
|
||||
case READOUT_TYPE_RAWVALUE:
|
||||
out = out + (*numbers)[i]->ReturnRawValue;
|
||||
break;
|
||||
case READOUT_TYPE_ERROR:
|
||||
out = out + (*numbers)[i]->ErrorMessageText;
|
||||
break;
|
||||
}
|
||||
if (i < (*numbers).size()-1)
|
||||
out = out + "\r\n";
|
||||
}
|
||||
// ESP_LOGD(TAG, "OUT: %s", out.c_str());
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowControll::getReadout(bool _rawvalue = false, bool _noerror = false, int _number = 0)
|
||||
{
|
||||
if (flowpostprocessing)
|
||||
return flowpostprocessing->getReadoutParam(_rawvalue, _noerror, _number);
|
||||
|
||||
string zw = "";
|
||||
string result = "";
|
||||
|
||||
for (int i = 0; i < FlowControll.size(); ++i)
|
||||
{
|
||||
zw = FlowControll[i]->getReadout();
|
||||
if (zw.length() > 0)
|
||||
{
|
||||
if (result.length() == 0)
|
||||
result = zw;
|
||||
else
|
||||
result = result + "\t" + zw;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowControll::GetPrevalue(std::string _number)
|
||||
{
|
||||
if (flowpostprocessing)
|
||||
{
|
||||
return flowpostprocessing->GetPreValue(_number);
|
||||
}
|
||||
|
||||
|
||||
return std::string("");
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowControll::UpdatePrevalue(std::string _newvalue, std::string _numbers, bool _extern)
|
||||
{
|
||||
double newvalueAsDouble;
|
||||
char* p;
|
||||
|
||||
_newvalue = trim(_newvalue);
|
||||
//ESP_LOGD(TAG, "Input UpdatePreValue: %s", _newvalue.c_str());
|
||||
|
||||
if (_newvalue.substr(0,8).compare("0.000000") == 0 || _newvalue.compare("0.0") == 0 || _newvalue.compare("0") == 0) {
|
||||
newvalueAsDouble = 0; // preset to value = 0
|
||||
}
|
||||
else {
|
||||
newvalueAsDouble = strtod(_newvalue.c_str(), &p);
|
||||
if (newvalueAsDouble == 0) {
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "UpdatePrevalue: No valid value for processing: " + _newvalue);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (flowpostprocessing) {
|
||||
if (flowpostprocessing->SetPreValue(newvalueAsDouble, _numbers, _extern))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "UpdatePrevalue: ERROR - Class Post-Processing not initialized");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowControll::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
{
|
||||
std::vector<string> splitted;
|
||||
|
||||
aktparamgraph = trim(aktparamgraph);
|
||||
|
||||
if (aktparamgraph.size() == 0)
|
||||
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||
return false;
|
||||
|
||||
|
||||
if ((toUpper(aktparamgraph).compare("[AUTOTIMER]") != 0) && (toUpper(aktparamgraph).compare("[DEBUG]") != 0) &&
|
||||
(toUpper(aktparamgraph).compare("[SYSTEM]") != 0 && (toUpper(aktparamgraph).compare("[DATALOGGING]") != 0))) // Paragraph passt nicht zu Debug oder DataLogging
|
||||
return false;
|
||||
|
||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||
{
|
||||
splitted = ZerlegeZeile(aktparamgraph, " =");
|
||||
if ((toUpper(splitted[0]) == "AUTOSTART") && (splitted.size() > 1))
|
||||
{
|
||||
if (toUpper(splitted[1]) == "TRUE")
|
||||
{
|
||||
AutoStart = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((toUpper(splitted[0]) == "INTERVAL") && (splitted.size() > 1))
|
||||
{
|
||||
AutoInterval = std::stof(splitted[1]);
|
||||
}
|
||||
|
||||
if ((toUpper(splitted[0]) == "DATALOGACTIVE") && (splitted.size() > 1))
|
||||
{
|
||||
if (toUpper(splitted[1]) == "TRUE")
|
||||
{
|
||||
LogFile.SetDataLogToSD(true);
|
||||
}
|
||||
else {
|
||||
LogFile.SetDataLogToSD(false);
|
||||
}
|
||||
}
|
||||
|
||||
if ((toUpper(splitted[0]) == "DATAFILESRETENTION") && (splitted.size() > 1))
|
||||
{
|
||||
LogFile.SetDataLogRetention(std::stoi(splitted[1]));
|
||||
}
|
||||
|
||||
if ((toUpper(splitted[0]) == "LOGLEVEL") && (splitted.size() > 1))
|
||||
{
|
||||
/* matches esp_log_level_t */
|
||||
if ((toUpper(splitted[1]) == "TRUE") || (toUpper(splitted[1]) == "2"))
|
||||
{
|
||||
LogFile.setLogLevel(ESP_LOG_WARN);
|
||||
}
|
||||
else if ((toUpper(splitted[1]) == "FALSE") || (toUpper(splitted[1]) == "0") || (toUpper(splitted[1]) == "1"))
|
||||
{
|
||||
LogFile.setLogLevel(ESP_LOG_ERROR);
|
||||
}
|
||||
else if (toUpper(splitted[1]) == "3")
|
||||
{
|
||||
LogFile.setLogLevel(ESP_LOG_INFO);
|
||||
}
|
||||
else if (toUpper(splitted[1]) == "4")
|
||||
{
|
||||
LogFile.setLogLevel(ESP_LOG_DEBUG);
|
||||
}
|
||||
|
||||
/* If system reboot was not triggered by user and reboot was caused by execption -> keep log level to DEBUG */
|
||||
if (!getIsPlannedReboot() && (esp_reset_reason() == ESP_RST_PANIC))
|
||||
LogFile.setLogLevel(ESP_LOG_DEBUG);
|
||||
}
|
||||
if ((toUpper(splitted[0]) == "LOGFILESRETENTION") && (splitted.size() > 1))
|
||||
{
|
||||
LogFile.SetLogFileRetention(std::stoi(splitted[1]));
|
||||
}
|
||||
|
||||
/* TimeServer and TimeZone got already read from the config, see setupTime () */
|
||||
|
||||
#if (defined WLAN_USE_ROAMING_BY_SCANNING || (defined WLAN_USE_MESH_ROAMING && defined WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES))
|
||||
if ((toUpper(splitted[0]) == "RSSITHRESHOLD") && (splitted.size() > 1))
|
||||
{
|
||||
int RSSIThresholdTMP = atoi(splitted[1].c_str());
|
||||
RSSIThresholdTMP = min(0, max(-100, RSSIThresholdTMP)); // Verify input limits (-100 - 0)
|
||||
|
||||
if (ChangeRSSIThreshold(WLAN_CONFIG_FILE, RSSIThresholdTMP))
|
||||
{
|
||||
// reboot necessary so that the new wlan.ini is also used !!!
|
||||
fclose(pfile);
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Rebooting to activate new RSSITHRESHOLD ...");
|
||||
doReboot();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((toUpper(splitted[0]) == "HOSTNAME") && (splitted.size() > 1))
|
||||
{
|
||||
if (ChangeHostName(WLAN_CONFIG_FILE, splitted[1]))
|
||||
{
|
||||
// reboot necessary so that the new wlan.ini is also used !!!
|
||||
fclose(pfile);
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Rebooting to activate new HOSTNAME...");
|
||||
doReboot();
|
||||
}
|
||||
}
|
||||
|
||||
if ((toUpper(splitted[0]) == "SETUPMODE") && (splitted.size() > 1))
|
||||
{
|
||||
if (toUpper(splitted[1]) == "TRUE")
|
||||
{
|
||||
SetupModeActive = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int ClassFlowControll::CleanTempFolder() {
|
||||
const char* folderPath = "/sdcard/img_tmp";
|
||||
|
||||
ESP_LOGD(TAG, "Clean up temporary folder to avoid damage of sdcard sectors: %s", folderPath);
|
||||
DIR *dir = opendir(folderPath);
|
||||
if (!dir) {
|
||||
ESP_LOGE(TAG, "Failed to stat dir: %s", folderPath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct dirent *entry;
|
||||
int deleted = 0;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
std::string path = string(folderPath) + "/" + entry->d_name;
|
||||
if (entry->d_type == DT_REG) {
|
||||
if (unlink(path.c_str()) == 0) {
|
||||
deleted ++;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "can't delete file: %s", path.c_str());
|
||||
}
|
||||
} else if (entry->d_type == DT_DIR) {
|
||||
deleted += removeFolder(path.c_str(), TAG);
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
ESP_LOGD(TAG, "%d files deleted", deleted);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t ClassFlowControll::SendRawJPG(httpd_req_t *req)
|
||||
{
|
||||
return flowtakeimage != NULL ? flowtakeimage->SendRawJPG(req) : ESP_FAIL;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t ClassFlowControll::GetJPGStream(std::string _fn, httpd_req_t *req)
|
||||
{
|
||||
ESP_LOGD(TAG, "ClassFlowControll::GetJPGStream %s", _fn.c_str());
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("ClassFlowControll::GetJPGStream - Start");
|
||||
#endif
|
||||
|
||||
CImageBasis *_send = NULL;
|
||||
esp_err_t result = ESP_FAIL;
|
||||
bool _sendDelete = false;
|
||||
|
||||
if (_fn == "alg.jpg") {
|
||||
if (flowalignment && flowalignment->ImageBasis->ImageOkay()) {
|
||||
_send = flowalignment->ImageBasis;
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ClassFlowControll::GetJPGStream: alg.jpg cannot be served");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
else if (_fn == "alg_roi.jpg") {
|
||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG // no CImageBasis needed to create alg_roi.jpg (ca. 790kB less RAM)
|
||||
if (aktstatus.find("Initialization (delayed)") != -1) {
|
||||
FILE* file = fopen("/sdcard/html/Flowstate_initialization_delayed.jpg", "rb");
|
||||
|
||||
if (!file) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File /sdcard/html/Flowstate_initialization_delayed.jpg not found");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
long fileSize = ftell(file); /* how long is the file ? */
|
||||
fseek(file, 0, SEEK_SET); /* reset */
|
||||
|
||||
unsigned char* fileBuffer = (unsigned char*) malloc(fileSize);
|
||||
|
||||
if (!fileBuffer) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ClassFlowControll::GetJPGStream: Not enough memory to create fileBuffer: " + std::to_string(fileSize));
|
||||
fclose(file);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
fread(fileBuffer, fileSize, 1, file);
|
||||
fclose(file);
|
||||
|
||||
httpd_resp_set_type(req, "image/jpeg");
|
||||
result = httpd_resp_send(req, (const char *)fileBuffer, fileSize);
|
||||
free(fileBuffer);
|
||||
}
|
||||
else if (aktstatus.find("Initialization") != -1) {
|
||||
FILE* file = fopen("/sdcard/html/Flowstate_initialization.jpg", "rb");
|
||||
|
||||
if (!file) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File /sdcard/html/Flowstate_initialization.jpg not found");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
long fileSize = ftell(file); /* how long is the file ? */
|
||||
fseek(file, 0, SEEK_SET); /* reset */
|
||||
|
||||
unsigned char* fileBuffer = (unsigned char*) malloc(fileSize);
|
||||
|
||||
if (!fileBuffer) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ClassFlowControll::GetJPGStream: Not enough memory to create fileBuffer: " + std::to_string(fileSize));
|
||||
fclose(file);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
fread(fileBuffer, fileSize, 1, file);
|
||||
fclose(file);
|
||||
|
||||
httpd_resp_set_type(req, "image/jpeg");
|
||||
result = httpd_resp_send(req, (const char *)fileBuffer, fileSize);
|
||||
free(fileBuffer);
|
||||
}
|
||||
else if (aktstatus.find("Take Image") != -1) {
|
||||
if (flowalignment && flowalignment->AlgROI) {
|
||||
FILE* file = fopen("/sdcard/html/Flowstate_take_image.jpg", "rb");
|
||||
|
||||
if (!file) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File /sdcard/html/Flowstate_take_image.jpg not found");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
flowalignment->AlgROI->size = ftell(file); /* how long is the file ? */
|
||||
fseek(file, 0, SEEK_SET); /* reset */
|
||||
|
||||
if (flowalignment->AlgROI->size > MAX_JPG_SIZE) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File /sdcard/html/Flowstate_take_image.jpg (" + std::to_string(flowalignment->AlgROI->size) +
|
||||
") > allocated buffer (" + std::to_string(MAX_JPG_SIZE) + ")");
|
||||
fclose(file);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
fread(flowalignment->AlgROI->data, flowalignment->AlgROI->size, 1, file);
|
||||
fclose(file);
|
||||
|
||||
httpd_resp_set_type(req, "image/jpeg");
|
||||
result = httpd_resp_send(req, (const char *)flowalignment->AlgROI->data, flowalignment->AlgROI->size);
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ClassFlowControll::GetJPGStream: alg_roi.jpg cannot be served -> alg.jpg is going to be served!");
|
||||
if (flowalignment && flowalignment->ImageBasis->ImageOkay()) {
|
||||
_send = flowalignment->ImageBasis;
|
||||
}
|
||||
else {
|
||||
httpd_resp_send(req, NULL, 0);
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (flowalignment && flowalignment->AlgROI) {
|
||||
httpd_resp_set_type(req, "image/jpeg");
|
||||
result = httpd_resp_send(req, (const char *)flowalignment->AlgROI->data, flowalignment->AlgROI->size);
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ClassFlowControll::GetJPGStream: alg_roi.jpg cannot be served -> alg.jpg is going to be served!");
|
||||
if (flowalignment && flowalignment->ImageBasis->ImageOkay()) {
|
||||
_send = flowalignment->ImageBasis;
|
||||
}
|
||||
else {
|
||||
httpd_resp_send(req, NULL, 0);
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (!flowalignment) {
|
||||
ESP_LOGD(TAG, "ClassFloDControll::GetJPGStream: FlowAlignment is not (yet) initialized. Interrupt serving!");
|
||||
httpd_resp_send(req, NULL, 0);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
_send = new CImageBasis("alg_roi", flowalignment->ImageBasis);
|
||||
|
||||
if (_send->ImageOkay()) {
|
||||
if (flowalignment) flowalignment->DrawRef(_send);
|
||||
if (flowdigit) flowdigit->DrawROI(_send);
|
||||
if (flowanalog) flowanalog->DrawROI(_send);
|
||||
_sendDelete = true; // delete temporary _send element after sending
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "ClassFlowControll::GetJPGStream: Not enough memory to create alg_roi.jpg -> alg.jpg is going to be served!");
|
||||
|
||||
if (flowalignment && flowalignment->ImageBasis->ImageOkay()) {
|
||||
_send = flowalignment->ImageBasis;
|
||||
}
|
||||
else {
|
||||
httpd_resp_send(req, NULL, 0);
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
std::vector<HTMLInfo*> htmlinfo;
|
||||
|
||||
htmlinfo = GetAllDigital();
|
||||
ESP_LOGD(TAG, "After getClassFlowControll::GetAllDigital");
|
||||
|
||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
||||
{
|
||||
if (_fn == htmlinfo[i]->filename)
|
||||
{
|
||||
if (htmlinfo[i]->image)
|
||||
_send = htmlinfo[i]->image;
|
||||
}
|
||||
|
||||
if (_fn == htmlinfo[i]->filename_org)
|
||||
{
|
||||
if (htmlinfo[i]->image_org)
|
||||
_send = htmlinfo[i]->image_org;
|
||||
}
|
||||
delete htmlinfo[i];
|
||||
}
|
||||
htmlinfo.clear();
|
||||
|
||||
if (!_send)
|
||||
{
|
||||
htmlinfo = GetAllAnalog();
|
||||
ESP_LOGD(TAG, "After getClassFlowControll::GetAllAnalog");
|
||||
|
||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
||||
{
|
||||
if (_fn == htmlinfo[i]->filename)
|
||||
{
|
||||
if (htmlinfo[i]->image)
|
||||
_send = htmlinfo[i]->image;
|
||||
}
|
||||
|
||||
if (_fn == htmlinfo[i]->filename_org)
|
||||
{
|
||||
if (htmlinfo[i]->image_org)
|
||||
_send = htmlinfo[i]->image_org;
|
||||
}
|
||||
delete htmlinfo[i];
|
||||
}
|
||||
htmlinfo.clear();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("ClassFlowControll::GetJPGStream - before send");
|
||||
#endif
|
||||
|
||||
if (_send)
|
||||
{
|
||||
ESP_LOGD(TAG, "Sending file: %s ...", _fn.c_str());
|
||||
set_content_type_from_file(req, _fn.c_str());
|
||||
result = _send->SendJPGtoHTTP(req);
|
||||
/* Respond with an empty chunk to signal HTTP response completion */
|
||||
httpd_resp_send_chunk(req, NULL, 0);
|
||||
ESP_LOGD(TAG, "File sending complete");
|
||||
|
||||
if (_sendDelete)
|
||||
delete _send;
|
||||
|
||||
_send = NULL;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("ClassFlowControll::GetJPGStream - done");
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowControll::getNumbersName()
|
||||
{
|
||||
return flowpostprocessing->getNumbersName();
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowControll::getJSON()
|
||||
{
|
||||
return flowpostprocessing->GetJSON();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user