mirror of
https://github.com/wled/WLED.git
synced 2026-05-09 23:42:44 +00:00
Compare commits
6627 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa6e719129 | ||
|
|
979c724c91 | ||
|
|
665d66f45e | ||
|
|
367d64f41f | ||
|
|
59c5991d2d | ||
|
|
8935997228 | ||
|
|
c05097351d | ||
|
|
57cca612db | ||
|
|
8204e1bafe | ||
|
|
8039b71b76 | ||
|
|
1edfc81259 | ||
|
|
e321514e4f | ||
|
|
b0b9fc58da | ||
|
|
360333c022 | ||
|
|
0b7f77b2f7 | ||
|
|
05959f94a8 | ||
|
|
3e085024a0 | ||
|
|
bef897f679 | ||
|
|
dba7fcaaff | ||
|
|
8aa5501571 | ||
|
|
41fd877adb | ||
|
|
d66258f53b | ||
|
|
aaf76cc4d6 | ||
|
|
7872c633f3 | ||
|
|
ccd40e1f27 | ||
|
|
d466cab664 | ||
|
|
c74b988c90 | ||
|
|
fe14c3104b | ||
|
|
47af19519f | ||
|
|
aea18fa87b | ||
|
|
e0b0b2a475 | ||
|
|
93e3e2ed02 | ||
|
|
d00dbacca6 | ||
|
|
988254bb9f | ||
|
|
dd397fe673 | ||
|
|
7c57de901d | ||
|
|
a4359fa57f | ||
|
|
0c3ff97e16 | ||
|
|
a871f110f5 | ||
|
|
49f653c621 | ||
|
|
64d57f51d5 | ||
|
|
eaffac61c9 | ||
|
|
0466daa552 | ||
|
|
88032d5e4b | ||
|
|
9161672b43 | ||
|
|
45cdfde61a | ||
|
|
120da32d69 | ||
|
|
eb60ff91e4 | ||
|
|
449069a344 | ||
|
|
d7d4e7dfb4 | ||
|
|
f16ca9c8ae | ||
|
|
5f219e60b8 | ||
|
|
18a9986d1d | ||
|
|
7a71efe916 | ||
|
|
a535c56b4d | ||
|
|
e37707d520 | ||
|
|
fd6f568023 | ||
|
|
02e593da0b | ||
|
|
e8d481720a | ||
|
|
f4f4978eeb | ||
|
|
49a63fc15e | ||
|
|
1bc71f2352 | ||
|
|
70b0aeac95 | ||
|
|
d20dbeb204 | ||
|
|
69d494ad40 | ||
|
|
8aa4beeaea | ||
|
|
012fbdd6e9 | ||
|
|
01328a65c1 | ||
|
|
a2d970e155 | ||
|
|
cb666cedc5 | ||
|
|
5e0a0a7561 | ||
|
|
3af2ae5f2f | ||
|
|
259bf3c0f8 | ||
|
|
2481aab860 | ||
|
|
ac96ee7568 | ||
|
|
2b32918db0 | ||
|
|
5e49a1cffb | ||
|
|
910caae463 | ||
|
|
35ce05a73b | ||
|
|
ba377d7c29 | ||
|
|
1cce33e0d3 | ||
|
|
6d7c1d0bbd | ||
|
|
96510614a3 | ||
|
|
1185886eab | ||
|
|
a94a3f7003 | ||
|
|
144f1f13a8 | ||
|
|
5a7aa8d8a8 | ||
|
|
42b91f2122 | ||
|
|
a966c41bc2 | ||
|
|
36ebcb50f7 | ||
|
|
3149a80dcf | ||
|
|
bab31833f0 | ||
|
|
48b0ba0643 | ||
|
|
0198a614b9 | ||
|
|
14f2ca4223 | ||
|
|
7932a249e0 | ||
|
|
474e1fe2e5 | ||
|
|
7aec31f039 | ||
|
|
e2dd6303ea | ||
|
|
7242e9d842 | ||
|
|
ab0cde110f | ||
|
|
c735feb90a | ||
|
|
a6faf942f1 | ||
|
|
370e4c8e2f | ||
|
|
8e90242e95 | ||
|
|
97dacb6a04 | ||
|
|
061920bb9c | ||
|
|
97704e08de | ||
|
|
a51aec6f19 | ||
|
|
c21ed0714f | ||
|
|
a2dce56809 | ||
|
|
036f5199e5 | ||
|
|
1914e4ee3b | ||
|
|
9c072ec921 | ||
|
|
cf4dfe958e | ||
|
|
2cccbd175c | ||
|
|
cb61c4fe7d | ||
|
|
4a0a4c632b | ||
|
|
5b2ae15d8d | ||
|
|
44ddc21530 | ||
|
|
063592c845 | ||
|
|
4955af0a11 | ||
|
|
9df5d056b9 | ||
|
|
3486e3bcbb | ||
|
|
91eb69643d | ||
|
|
48b27d12ac | ||
|
|
78c1051cbb | ||
|
|
2302863386 | ||
|
|
7560811480 | ||
|
|
12f6fbc005 | ||
|
|
c819814904 | ||
|
|
0ef5ee74d4 | ||
|
|
875711d38e | ||
|
|
9b9474282d | ||
|
|
1284aba5d0 | ||
|
|
fa6fc4f0b6 | ||
|
|
f7d9461d1c | ||
|
|
51862e3572 | ||
|
|
7153fd8fe4 | ||
|
|
35c1f415c3 | ||
|
|
cf03f1a16e | ||
|
|
5be517b519 | ||
|
|
aaf51927a6 | ||
|
|
630533d125 | ||
|
|
a78d39ff1e | ||
|
|
7722faf9d7 | ||
|
|
72a43c6431 | ||
|
|
7c6828d443 | ||
|
|
f8f46736ec | ||
|
|
e4f8534cf0 | ||
|
|
79ecffe0f3 | ||
|
|
1fb636d8ea | ||
|
|
e04c855e6c | ||
|
|
6a627a86b7 | ||
|
|
746df24011 | ||
|
|
da64f71ce2 | ||
|
|
8972b6bfe2 | ||
|
|
8241468fe2 | ||
|
|
e9b740a915 | ||
|
|
548bb6ffd5 | ||
|
|
964c8cee66 | ||
|
|
9d1f36c553 | ||
|
|
ad921f031f | ||
|
|
3d2fac0ad4 | ||
|
|
a5f28d0fcb | ||
|
|
fb8f8f0b08 | ||
|
|
53fdf9a89c | ||
|
|
a1316034c1 | ||
|
|
75df4affa8 | ||
|
|
820c841376 | ||
|
|
34d50710b3 | ||
|
|
d0d62d9493 | ||
|
|
89b2bc2992 | ||
|
|
472fca7486 | ||
|
|
42844e4fa8 | ||
|
|
4a6ff64519 | ||
|
|
f6a43f4dfa | ||
|
|
b7d2c3cd85 | ||
|
|
20c021d9b8 | ||
|
|
9c93cb8cf0 | ||
|
|
ad5043f7e0 | ||
|
|
5ac282f8b9 | ||
|
|
e4351c8979 | ||
|
|
e019d36221 | ||
|
|
6a16593be5 | ||
|
|
3bcb0c902e | ||
|
|
78ecd3805e | ||
|
|
4c7fa0303b | ||
|
|
75b679814b | ||
|
|
d8cb20a9e7 | ||
|
|
710f897a1d | ||
|
|
abb4124ab4 | ||
|
|
3c7fbabd23 | ||
|
|
bda74bafb8 | ||
|
|
9cf3a7d184 | ||
|
|
5cef850c47 | ||
|
|
74763040ad | ||
|
|
7a635c88d6 | ||
|
|
dc5a7350d2 | ||
|
|
81f251c125 | ||
|
|
ff9067f10b | ||
|
|
144c0f9dd6 | ||
|
|
a721264205 | ||
|
|
f28cbea2c2 | ||
|
|
3b256eed1e | ||
|
|
610c80a0ae | ||
|
|
aa6cd883c2 | ||
|
|
7006285856 | ||
|
|
7e490fcd28 | ||
|
|
6f030e540f | ||
|
|
a63a307c6e | ||
|
|
b57d51ef19 | ||
|
|
34722aa371 | ||
|
|
19c178d0f9 | ||
|
|
3244d0f593 | ||
|
|
64f3aa96dd | ||
|
|
f816b5fa98 | ||
|
|
e4cd730654 | ||
|
|
c7fa496fda | ||
|
|
40442551ef | ||
|
|
cfa93c9778 | ||
|
|
c35358c005 | ||
|
|
2ab9659332 | ||
|
|
3bfdb736e3 | ||
|
|
53d6c4b059 | ||
|
|
5beafe0a7d | ||
|
|
7f44396f7f | ||
|
|
5e1ae99767 | ||
|
|
1da5dc7d18 | ||
|
|
05498f2ae4 | ||
|
|
9ae1ef506c | ||
|
|
cd1c5ca6bd | ||
|
|
b07cd45642 | ||
|
|
278a1fb6c1 | ||
|
|
0e1da4f004 | ||
|
|
5f5f468983 | ||
|
|
c604b99a4c | ||
|
|
5f28406f42 | ||
|
|
bc229b8cb6 | ||
|
|
5790309371 | ||
|
|
210b4d8f54 | ||
|
|
19292675d8 | ||
|
|
ac1a4dfbfd | ||
|
|
7fa15761ae | ||
|
|
35d267109f | ||
|
|
3bfbbab807 | ||
|
|
b97b46ae12 | ||
|
|
26d2cc971a | ||
|
|
3972b6e8fb | ||
|
|
3116e88cdc | ||
|
|
1c2f70598a | ||
|
|
6f65b46b14 | ||
|
|
1c8dd8ecd2 | ||
|
|
55c4288cb6 | ||
|
|
231fb5a7ca | ||
|
|
536b40c6f1 | ||
|
|
d3a8da212c | ||
|
|
522927ea94 | ||
|
|
4178d05c6a | ||
|
|
2d1315f9dc | ||
|
|
822e07d9f5 | ||
|
|
439d4a0e98 | ||
|
|
1f102ca832 | ||
|
|
43e86d0f4d | ||
|
|
29e9c73274 | ||
|
|
6240ee69fc | ||
|
|
14bd5d615b | ||
|
|
e0441c8876 | ||
|
|
1ee42f0206 | ||
|
|
d1ed547a7c | ||
|
|
f830ea498c | ||
|
|
ce31d802d5 | ||
|
|
f09c449ad5 | ||
|
|
6bebb8b4a8 | ||
|
|
e3bc32a823 | ||
|
|
6b70c6ae91 | ||
|
|
b9138b4300 | ||
|
|
7a157a8c91 | ||
|
|
7387baace4 | ||
|
|
80e75139c6 | ||
|
|
e9ced6ddf1 | ||
|
|
6d1d494cf5 | ||
|
|
65daf26aee | ||
|
|
591f65fcee | ||
|
|
8b6d4134bd | ||
|
|
2102bb12f0 | ||
|
|
747bc2bacd | ||
|
|
4d63b3c8a0 | ||
|
|
147384074d | ||
|
|
129137d96c | ||
|
|
0120b1ab79 | ||
|
|
c64b1e3e5d | ||
|
|
0c08c27df4 | ||
|
|
2676ac771d | ||
|
|
680ef26f4d | ||
|
|
eb4bd6fbb1 | ||
|
|
e78b4a245d | ||
|
|
73ae3de5b8 | ||
|
|
a6b107e8df | ||
|
|
6b953d96eb | ||
|
|
3d33bae2b8 | ||
|
|
f2df029f26 | ||
|
|
761eb99e53 | ||
|
|
2434a9624e | ||
|
|
03d0522cf1 | ||
|
|
642c99a617 | ||
|
|
76c25da58e | ||
|
|
b51e7b65f9 | ||
|
|
78166090bc | ||
|
|
6d788a27b6 | ||
|
|
32cda7d793 | ||
|
|
c180a3fe6f | ||
|
|
1ca55e42af | ||
|
|
ba677d1a32 | ||
|
|
354da8fdc0 | ||
|
|
2c4ed4249d | ||
|
|
39bf31d3a1 | ||
|
|
50ef8db595 | ||
|
|
d4f365e7e5 | ||
|
|
f19d29cd64 | ||
|
|
1031e70d70 | ||
|
|
c9f47d4b5c | ||
|
|
857e73ab25 | ||
|
|
4e072962c0 | ||
|
|
81af160be6 | ||
|
|
a64334c32e | ||
|
|
8d39dac654 | ||
|
|
e867fcab1a | ||
|
|
9683896a21 | ||
|
|
ca1d6614b2 | ||
|
|
96f423438b | ||
|
|
df94a8d5af | ||
|
|
d9cc751db4 | ||
|
|
99c3f68f80 | ||
|
|
be900737d2 | ||
|
|
af8db57f02 | ||
|
|
1773f61ded | ||
|
|
a024935c39 | ||
|
|
10df03e564 | ||
|
|
7a9e7f9c41 | ||
|
|
45acb44a36 | ||
|
|
8a3cb46007 | ||
|
|
ba5cf9cd3c | ||
|
|
d1d9dec402 | ||
|
|
6e9dc181e1 | ||
|
|
fe3a158264 | ||
|
|
e2de1af6f4 | ||
|
|
22ab62d090 | ||
|
|
254e0099ca | ||
|
|
8433fd24c3 | ||
|
|
32daa03941 | ||
|
|
4749247389 | ||
|
|
a870474b49 | ||
|
|
97493b1af8 | ||
|
|
8e27fe4c0c | ||
|
|
407c9fd72f | ||
|
|
e95450b318 | ||
|
|
b556da8b36 | ||
|
|
5cfb6f984b | ||
|
|
60e1698ed2 | ||
|
|
60b2c3bb54 | ||
|
|
979e3fd1f7 | ||
|
|
600b9b4d8e | ||
|
|
787d8a7342 | ||
|
|
46e77ea203 | ||
|
|
fa868568af | ||
|
|
1c2cacf185 | ||
|
|
f1f067e93a | ||
|
|
8a2a7054ab | ||
|
|
e4b0ee0672 | ||
|
|
b821e20fd6 | ||
|
|
51a14ede8b | ||
|
|
788c09972e | ||
|
|
ba21ab55f8 | ||
|
|
1585cab3ba | ||
|
|
304c59e09b | ||
|
|
fdb85d82da | ||
|
|
c8a03817ed | ||
|
|
dc76ff669b | ||
|
|
eca002b655 | ||
|
|
71c8a30f42 | ||
|
|
65f1d8d836 | ||
|
|
624763cbc8 | ||
|
|
af7c91057e | ||
|
|
dd3edf1a3c | ||
|
|
7f4e0f74ba | ||
|
|
b452370be7 | ||
|
|
913c7316f2 | ||
|
|
bb6114e8aa | ||
|
|
c097cb1f27 | ||
|
|
9094b3130d | ||
|
|
32b104e1a9 | ||
|
|
d1260ccf8b | ||
|
|
c35140e763 | ||
|
|
6632a35339 | ||
|
|
6388b8f4bb | ||
|
|
f9a8b3021f | ||
|
|
6a8c6c1f58 | ||
|
|
19bc3c513a | ||
|
|
fa3a94e33e | ||
|
|
5c2177e8d5 | ||
|
|
6e39969cdc | ||
|
|
4b0cf874c9 | ||
|
|
2ff4ee0e1b | ||
|
|
f2a3502445 | ||
|
|
6ca8ed65e8 | ||
|
|
6a2b7995e9 | ||
|
|
247a7a51d7 | ||
|
|
1fee9d4c29 | ||
|
|
b4d3a279e3 | ||
|
|
4684e092a8 | ||
|
|
2a53f415ea | ||
|
|
e074d19593 | ||
|
|
14a728084c | ||
|
|
ead1d6b5f8 | ||
|
|
a421cfeabe | ||
|
|
6f6ac066c9 | ||
|
|
a55a32cc7e | ||
|
|
474c84c9e6 | ||
|
|
a2b64ad332 | ||
|
|
cc5b504771 | ||
|
|
fe33709eb0 | ||
|
|
41b51edbdd | ||
|
|
e6b5429873 | ||
|
|
8cbc76540f | ||
|
|
de01c5e61f | ||
|
|
c114ea6b30 | ||
|
|
bdea3d4959 | ||
|
|
e403f4e0d0 | ||
|
|
3bac2ddae2 | ||
|
|
b98b1b4c78 | ||
|
|
5885a9cc63 | ||
|
|
e306e14b22 | ||
|
|
7b9d643dcd | ||
|
|
f70b359631 | ||
|
|
ae37f4268c | ||
|
|
7c6a1d717d | ||
|
|
a2c1ad01da | ||
|
|
a947e8f35e | ||
|
|
653e03921e | ||
|
|
a0eec81c8a | ||
|
|
9eda32b93a | ||
|
|
e1f5bbf895 | ||
|
|
5d4fdb171e | ||
|
|
4fa4bc8d4b | ||
|
|
b5f13e4331 | ||
|
|
a897271a03 | ||
|
|
75a7ed132a | ||
|
|
379343278d | ||
|
|
0b965ea431 | ||
|
|
d6cd65e108 | ||
|
|
fc25eb2c90 | ||
|
|
dc5732a5f5 | ||
|
|
a9811c2020 | ||
|
|
33411f0300 | ||
|
|
8bc434b614 | ||
|
|
ce6577ee35 | ||
|
|
579021f5fc | ||
|
|
17e91a7d2a | ||
|
|
61f5737df2 | ||
|
|
49a25af1f2 | ||
|
|
b6f3cb6394 | ||
|
|
571ab674c3 | ||
|
|
6b607fb545 | ||
|
|
e761418531 | ||
|
|
fca921ee82 | ||
|
|
fc7993f4a7 | ||
|
|
f12e3e03ac | ||
|
|
eb87fbf8e4 | ||
|
|
c534328cc5 | ||
|
|
28d8a1c25c | ||
|
|
d1c4de2499 | ||
|
|
1e081a7f0d | ||
|
|
730205ded5 | ||
|
|
90ca6ccf8b | ||
|
|
d7fd49cc4c | ||
|
|
eb03520aa9 | ||
|
|
d8e2ceecf7 | ||
|
|
7dfed581b7 | ||
|
|
49a1ae54cf | ||
|
|
5b3cc753e2 | ||
|
|
4615eb8258 | ||
|
|
9b787e13d1 | ||
|
|
3dbcd79b3c | ||
|
|
a1aac452de | ||
|
|
b90fbe6b1a | ||
|
|
1860258deb | ||
|
|
a2935b87c2 | ||
|
|
c1ce1d8aba | ||
|
|
54b7dfe04b | ||
|
|
4a33809d66 | ||
|
|
336e074b4a | ||
|
|
aaad450175 | ||
|
|
85b3c5d91b | ||
|
|
4db86ebf7f | ||
|
|
65c43b5224 | ||
|
|
d1ed5844e3 | ||
|
|
c649ec1d8c | ||
|
|
271e9ac7b7 | ||
|
|
8348089b50 | ||
|
|
4f968861d6 | ||
|
|
66ffd65476 | ||
|
|
194829336f | ||
|
|
f1d708ca43 | ||
|
|
4f93661865 | ||
|
|
cd2dc437a3 | ||
|
|
f95dae1b1b | ||
|
|
6ae4b1fc38 | ||
|
|
fc776eeb16 | ||
|
|
79376bbc58 | ||
|
|
3b14c31e00 | ||
|
|
a666f07340 | ||
|
|
bd933ff230 | ||
|
|
7addae9c24 | ||
|
|
a73a2aaa33 | ||
|
|
a96e88043d | ||
|
|
29d2f7fc1b | ||
|
|
7aedf77d83 | ||
|
|
1324d49098 | ||
|
|
79a52a60ff | ||
|
|
6581dd6ff9 | ||
|
|
4659939547 | ||
|
|
50d33c5bf4 | ||
|
|
c7c379f962 | ||
|
|
af8c851cc6 | ||
|
|
88466c7d1f | ||
|
|
a36638ee6d | ||
|
|
ff93a48926 | ||
|
|
9474c29946 | ||
|
|
8097c7c86d | ||
|
|
abfe91d47b | ||
|
|
a4109c7ea8 | ||
|
|
34445dbe0f | ||
|
|
c5631b8fe3 | ||
|
|
4cddd3face | ||
|
|
5250a0fe2c | ||
|
|
95611f19c0 | ||
|
|
b98ee3e7b6 | ||
|
|
90d4dd79de | ||
|
|
00904d8862 | ||
|
|
2e7b6b79bf | ||
|
|
104d2ae7e8 | ||
|
|
f1242bfb7a | ||
|
|
aef5e9691c | ||
|
|
3d9012b43a | ||
|
|
3c5df5ae66 | ||
|
|
520f1f884b | ||
|
|
8fc33fd7b1 | ||
|
|
9e0f7ec4e9 | ||
|
|
00ca694eea | ||
|
|
c91a39f55c | ||
|
|
ffc7b66c20 | ||
|
|
94bea4405a | ||
|
|
2963c1b761 | ||
|
|
013ecfb189 | ||
|
|
670f74d589 | ||
|
|
d475d21a79 | ||
|
|
91349234a0 | ||
|
|
601bb6f0ca | ||
|
|
07e26d31f4 | ||
|
|
151acb249e | ||
|
|
25d5295d5d | ||
|
|
474a995845 | ||
|
|
f0f12e77ad | ||
|
|
5bf1fc38b1 | ||
|
|
d55a3f078a | ||
|
|
46125773d9 | ||
|
|
ec61a35042 | ||
|
|
1afd72cb83 | ||
|
|
c623b82698 | ||
|
|
acd415c522 | ||
|
|
5fb37130f8 | ||
|
|
8e00e7175c | ||
|
|
0f06535932 | ||
|
|
46ff43889b | ||
|
|
7e1992fc5c | ||
|
|
0391488cef | ||
|
|
eb80fdf733 | ||
|
|
4973fd5a39 | ||
|
|
1dd338c5e0 | ||
|
|
186c4a7724 | ||
|
|
f0182eb1b2 | ||
|
|
2acf731baf | ||
|
|
e2b8f91417 | ||
|
|
5f33c69dd0 | ||
|
|
76bb3f7d77 | ||
|
|
da1d53c3b0 | ||
|
|
f4b98c43de | ||
|
|
62c78fc5ac | ||
|
|
9c4cf78a52 | ||
|
|
790be35ab8 | ||
|
|
0eef321f88 | ||
|
|
69dfe6c8a1 | ||
|
|
80c97076ae | ||
|
|
c3f394489f | ||
|
|
ce172df91a | ||
|
|
91baa34071 | ||
|
|
0e043b2a1b | ||
|
|
01c84b0140 | ||
|
|
1da2692c34 | ||
|
|
d538736411 | ||
|
|
b268aea0ab | ||
|
|
a04d70293d | ||
|
|
c66d67dd19 | ||
|
|
0c22163fd9 | ||
|
|
50c0f41508 | ||
|
|
b60313e1f8 | ||
|
|
1fff61b726 | ||
|
|
c4850aed08 | ||
|
|
43eb2d6f47 | ||
|
|
0ec4488dd1 | ||
|
|
ca5debef32 | ||
|
|
cc0230f83f | ||
|
|
3bc728e068 | ||
|
|
7f1f986f13 | ||
|
|
65b91762fd | ||
|
|
91fdb5822b | ||
|
|
5ca10f35d1 | ||
|
|
a073bf32e4 | ||
|
|
d79b02379e | ||
|
|
e4cabf8de6 | ||
|
|
f5f3fc338f | ||
|
|
f034601512 | ||
|
|
151a974607 | ||
|
|
042ed39464 | ||
|
|
9f583f16f8 | ||
|
|
4c4436f48c | ||
|
|
c3e18905c1 | ||
|
|
a18a661c73 | ||
|
|
93908e758f | ||
|
|
30fbf55b9a | ||
|
|
3562fa264e | ||
|
|
359d46c3e1 | ||
|
|
2b73a349dd | ||
|
|
d86ae7db40 | ||
|
|
f096320e63 | ||
|
|
e23751bd1d | ||
|
|
d5002cce25 | ||
|
|
daa833f33d | ||
|
|
7fe831c5e3 | ||
|
|
66069245a1 | ||
|
|
8b3975752c | ||
|
|
c6b4c77387 | ||
|
|
9152d9d2ed | ||
|
|
5a4a50415e | ||
|
|
640d0ee133 | ||
|
|
529edfc39b | ||
|
|
4b1b0fe045 | ||
|
|
4bfc9a9514 | ||
|
|
4d39dd0a5e | ||
|
|
15ba01a1c6 | ||
|
|
2593b11aba | ||
|
|
e7652e389f | ||
|
|
bd4a7e748d | ||
|
|
77f3426867 | ||
|
|
45615c07ee | ||
|
|
ee5a70a63e | ||
|
|
deac50409c | ||
|
|
c5119c8aa6 | ||
|
|
6c718c3558 | ||
|
|
75481d3251 | ||
|
|
7d6f47755c | ||
|
|
eb5d548ba7 | ||
|
|
3a7de8275f | ||
|
|
33d79e048c | ||
|
|
ed2b170e1b | ||
|
|
c73636d96d | ||
|
|
3410b785db | ||
|
|
762d4433d8 | ||
|
|
e69bf4eceb | ||
|
|
741fd8d9d3 | ||
|
|
684224c614 | ||
|
|
9826197083 | ||
|
|
2ba84b12f8 | ||
|
|
efeb791807 | ||
|
|
43e3578d50 | ||
|
|
b375c718b0 | ||
|
|
b16fbafd83 | ||
|
|
76cb2e9988 | ||
|
|
9d706010f5 | ||
|
|
97b20438fd | ||
|
|
65913f990d | ||
|
|
56d00357d3 | ||
|
|
1864e550e6 | ||
|
|
a79ae25621 | ||
|
|
05c481c5bb | ||
|
|
7ce0d69563 | ||
|
|
011e72c3c1 | ||
|
|
e44bdf6193 | ||
|
|
29dcdf8e85 | ||
|
|
e37d4cd8a8 | ||
|
|
46e60b4d0a | ||
|
|
75f6de9dc2 | ||
|
|
649d43b581 | ||
|
|
16cfbf7500 | ||
|
|
aecac2c56c | ||
|
|
385504e6db | ||
|
|
a0321170d0 | ||
|
|
d70018ae9f | ||
|
|
9359f0b7fc | ||
|
|
be74196a62 | ||
|
|
705f2035f4 | ||
|
|
65efcb351e | ||
|
|
72ad39d6a7 | ||
|
|
5950204d34 | ||
|
|
46df9410b3 | ||
|
|
bc5d4fed3c | ||
|
|
b7e4cd0d9a | ||
|
|
0becd61323 | ||
|
|
3f2e92c4c5 | ||
|
|
666a59ff53 | ||
|
|
ce7ca3f2d2 | ||
|
|
87092ccb80 | ||
|
|
2e834852d5 | ||
|
|
3e9e18dae2 | ||
|
|
831b68e60b | ||
|
|
5e3803a5bb | ||
|
|
91d7e0c051 | ||
|
|
a037d99469 | ||
|
|
8cc5d64819 | ||
|
|
4c948cca13 | ||
|
|
5cb8dc3978 | ||
|
|
199bc45ae2 | ||
|
|
8fc87aa17d | ||
|
|
19a49e83d1 | ||
|
|
c8757d45c8 | ||
|
|
62fad4dcdf | ||
|
|
da7f107273 | ||
|
|
d5d7fde30f | ||
|
|
6f914d79b1 | ||
|
|
dd13c2df47 | ||
|
|
8aeb9e1abe | ||
|
|
cfad0b8a52 | ||
|
|
a60be251d2 | ||
|
|
f15c1fbca6 | ||
|
|
708baf1ed7 | ||
|
|
4155a6bc23 | ||
|
|
c92f0a9d90 | ||
|
|
5fa901c37c | ||
|
|
46f3bc0ced | ||
|
|
f8ce5980a1 | ||
|
|
4b5c3a396d | ||
|
|
550b4d9dea | ||
|
|
f3e3f585df | ||
|
|
2082b01a3c | ||
|
|
8baa6a4616 | ||
|
|
1fb9eb771e | ||
|
|
dee581f58d | ||
|
|
7943b00017 | ||
|
|
4de6656bc4 | ||
|
|
cd8ddb81e1 | ||
|
|
890860ebf6 | ||
|
|
624042d97e | ||
|
|
85d4db83ed | ||
|
|
5146926723 | ||
|
|
3b5c6ca284 | ||
|
|
dcc1fbc96e | ||
|
|
7865985eeb | ||
|
|
4ac7eb7eb2 | ||
|
|
7285efebca | ||
|
|
af2d46c30d | ||
|
|
f4d89c4196 | ||
|
|
c9c442a933 | ||
|
|
b8b59b2bb1 | ||
|
|
c33e303323 | ||
|
|
caf3c7a2f9 | ||
|
|
c8d8ab020e | ||
|
|
297d5ced75 | ||
|
|
3f90366aa8 | ||
|
|
e3653baf74 | ||
|
|
f74d1459b9 | ||
|
|
93e011d403 | ||
|
|
374d90629d | ||
|
|
bfe5cd52e7 | ||
|
|
e374c7ae55 | ||
|
|
9e4675ef46 | ||
|
|
b76ef231fc | ||
|
|
591dbe387c | ||
|
|
e5ba97bbe2 | ||
|
|
b79e81f3be | ||
|
|
ab5b6f9b7d | ||
|
|
4fd1b393a8 | ||
|
|
e2f5becdd0 | ||
|
|
71301ddc57 | ||
|
|
c30c7e1da5 | ||
|
|
806163f1ed | ||
|
|
ecc3eae247 | ||
|
|
07e303bcc1 | ||
|
|
24f2306129 | ||
|
|
79b3bc2573 | ||
|
|
5c74f0fa21 | ||
|
|
e7157e542a | ||
|
|
a24420ae70 | ||
|
|
b7bfd6fc67 | ||
|
|
8bcd4550f8 | ||
|
|
9569ec7ccf | ||
|
|
61990189de | ||
|
|
c0875b36bb | ||
|
|
929a5a8d80 | ||
|
|
731f140b88 | ||
|
|
127c700a99 | ||
|
|
a67a2cbf5c | ||
|
|
f9a6a3d36f | ||
|
|
9c38843747 | ||
|
|
3fc653bbff | ||
|
|
600603149e | ||
|
|
2b42049998 | ||
|
|
5f70fe57e3 | ||
|
|
a2fc1a4d88 | ||
|
|
beee4e9293 | ||
|
|
17f7b158fa | ||
|
|
697ef4bdb7 | ||
|
|
ce323bed7a | ||
|
|
db8b378ee0 | ||
|
|
85214f1f2b | ||
|
|
63c3d5c89d | ||
|
|
b43f85c305 | ||
|
|
f12840218e | ||
|
|
5f224fa5f9 | ||
|
|
263150aeb3 | ||
|
|
164f213094 | ||
|
|
06b4c05f73 | ||
|
|
38975dbfd4 | ||
|
|
a6ce136843 | ||
|
|
a678bd09e9 | ||
|
|
8215fefc2d | ||
|
|
86137a669e | ||
|
|
33650e3886 | ||
|
|
d090b11a47 | ||
|
|
e44456fc0b | ||
|
|
59f50a569a | ||
|
|
9fda639e00 | ||
|
|
0ff2b5d081 | ||
|
|
134ce5d42d | ||
|
|
0a9ed37244 | ||
|
|
6246e41b55 | ||
|
|
6b6d26b8f0 | ||
|
|
62c87e206e | ||
|
|
4905cdd2e3 | ||
|
|
ce3f88428e | ||
|
|
74644aa4a7 | ||
|
|
2ccb7312d0 | ||
|
|
bd7735af3d | ||
|
|
0c2ab7ef45 | ||
|
|
fab724a703 | ||
|
|
ea86973548 | ||
|
|
9eb0c3c8e5 | ||
|
|
f20b4c9e81 | ||
|
|
6540deb462 | ||
|
|
c80c9bd8b9 | ||
|
|
d7d1e929fe | ||
|
|
e2800d75f7 | ||
|
|
2c1cf87e08 | ||
|
|
e6716fe834 | ||
|
|
fc0d64ec78 | ||
|
|
7b0075d375 | ||
|
|
e227d01436 | ||
|
|
db55fec879 | ||
|
|
9099b13f69 | ||
|
|
0ba05877d7 | ||
|
|
b1ed99dfe6 | ||
|
|
a2e9e2b7d1 | ||
|
|
5163fbf36b | ||
|
|
b187f9427d | ||
|
|
7cc5c87a52 | ||
|
|
1c4141a2b1 | ||
|
|
cc81cc27b0 | ||
|
|
6a5dcb3a76 | ||
|
|
796494e925 | ||
|
|
7973fd84f1 | ||
|
|
66869f8341 | ||
|
|
10d1098403 | ||
|
|
a041fd1266 | ||
|
|
e2fd1559d2 | ||
|
|
9dbd5f89b6 | ||
|
|
0f00c95aba | ||
|
|
bbfe90d2ca | ||
|
|
3f03cb4d2b | ||
|
|
05557ca790 | ||
|
|
a53baa9b42 | ||
|
|
7eafc01ab9 | ||
|
|
57cc1ce5d0 | ||
|
|
368351bbdd | ||
|
|
f9bfcce65d | ||
|
|
e97723dcc7 | ||
|
|
3dc45b80ba | ||
|
|
020eca8292 | ||
|
|
fb469d7b24 | ||
|
|
0f5d297bfb | ||
|
|
a56bd3cb91 | ||
|
|
ddd5d4152b | ||
|
|
edf6cb146d | ||
|
|
4707a50fbb | ||
|
|
91bd6c3f35 | ||
|
|
009950e28f | ||
|
|
8ee12620f0 | ||
|
|
65edc50563 | ||
|
|
a6f5080a4f | ||
|
|
d2d5c423b7 | ||
|
|
42bf8fb40d | ||
|
|
b0dd9690e7 | ||
|
|
65a79d411d | ||
|
|
3a413a59ba | ||
|
|
16a88775c3 | ||
|
|
728b1e8ad4 | ||
|
|
7835550f1a | ||
|
|
a50b4f5e3c | ||
|
|
80c40f6afd | ||
|
|
f442d58b70 | ||
|
|
201d04910a | ||
|
|
65f2ced6c2 | ||
|
|
2a0e78c656 | ||
|
|
05f0630b9c | ||
|
|
ea231cbea8 | ||
|
|
24e71cc6d5 | ||
|
|
94f226aadf | ||
|
|
00d1fcc5fb | ||
|
|
caeda96fbd | ||
|
|
9805ae59d5 | ||
|
|
00eb4068b0 | ||
|
|
fc7d4dfcb0 | ||
|
|
ab28b6d58f | ||
|
|
4abaf13598 | ||
|
|
8b65d873b3 | ||
|
|
a87b562bc2 | ||
|
|
23a51e0982 | ||
|
|
4a3af814bf | ||
|
|
db22936153 | ||
|
|
a147f4120c | ||
|
|
dcd3e07273 | ||
|
|
f26733cd8c | ||
|
|
f3623158d7 | ||
|
|
e80a7c6b75 | ||
|
|
309c8d67f3 | ||
|
|
75c95d88e2 | ||
|
|
0a7d3a9d9b | ||
|
|
aa28769e71 | ||
|
|
75cd411073 | ||
|
|
792a7aa081 | ||
|
|
7ea510e75b | ||
|
|
242df4b049 | ||
|
|
358e38e056 | ||
|
|
c693f63244 | ||
|
|
817157bbc1 | ||
|
|
ac61eb4b1b | ||
|
|
24ab2952ee | ||
|
|
999637f8ad | ||
|
|
25223c446f | ||
|
|
66ad27ad3a | ||
|
|
d9ad4ec743 | ||
|
|
d381108dc0 | ||
|
|
cbe7d0678b | ||
|
|
42d9a41cf5 | ||
|
|
b5a710dbe4 | ||
|
|
40653b0d6f | ||
|
|
608aff1e17 | ||
|
|
5fe766399b | ||
|
|
891115ecee | ||
|
|
0fe722e478 | ||
|
|
ee3864175d | ||
|
|
849d5e6667 | ||
|
|
a8dd2435ec | ||
|
|
d9b086cbe9 | ||
|
|
6464c620c7 | ||
|
|
7998650e60 | ||
|
|
d10714d1c1 | ||
|
|
f721efca1e | ||
|
|
125a21da75 | ||
|
|
c934776f45 | ||
|
|
f1c88bc38d | ||
|
|
92db9e0e00 | ||
|
|
19ba257722 | ||
|
|
6c4d049c1a | ||
|
|
fbb7ef7cfc | ||
|
|
b77881f634 | ||
|
|
7852ff558e | ||
|
|
adb9b773b0 | ||
|
|
410025f30f | ||
|
|
f1d52a8ec1 | ||
|
|
7f2b6a3f10 | ||
|
|
0f321bfb38 | ||
|
|
10b925acb7 | ||
|
|
ee9ac947a1 | ||
|
|
8baf0fdef7 | ||
|
|
353868414a | ||
|
|
c661d8f5b7 | ||
|
|
ca7d7d9369 | ||
|
|
02f14baad4 | ||
|
|
88aa8e8178 | ||
|
|
e979c58c98 | ||
|
|
81b74227fa | ||
|
|
9e96bd6705 | ||
|
|
5203c3927f | ||
|
|
bc099baeb1 | ||
|
|
646ec30ae5 | ||
|
|
dc5eaf3ae9 | ||
|
|
b941654a68 | ||
|
|
6f5482782b | ||
|
|
8a6e3a9898 | ||
|
|
354a0aef52 | ||
|
|
b0b3196e52 | ||
|
|
0139c34ec2 | ||
|
|
ff3680813c | ||
|
|
7db52d794b | ||
|
|
fbbb369fa4 | ||
|
|
f719ee5b18 | ||
|
|
c3ab562258 | ||
|
|
62d3e155bd | ||
|
|
fab80f4e4e | ||
|
|
1cd3a97c51 | ||
|
|
ff99c7de70 | ||
|
|
e2c919d270 | ||
|
|
035d4aed26 | ||
|
|
d03604b14a | ||
|
|
468ef50f75 | ||
|
|
16525c2f3f | ||
|
|
af1a966986 | ||
|
|
1492f1ce89 | ||
|
|
7e87891701 | ||
|
|
e76e9a3e1f | ||
|
|
bfe6fa1e5d | ||
|
|
cf391034da | ||
|
|
36cb1cad36 | ||
|
|
22e2b6f3c5 | ||
|
|
9c8f8c645e | ||
|
|
e21a09cec9 | ||
|
|
a0d1a8cbc4 | ||
|
|
86393e0b1d | ||
|
|
f328713710 | ||
|
|
ed6efe4c9e | ||
|
|
befff2f034 | ||
|
|
630315180d | ||
|
|
a70bfa0c89 | ||
|
|
d2b7e474d6 | ||
|
|
494b72c287 | ||
|
|
229e7b940f | ||
|
|
95dcb03f6d | ||
|
|
a6d9a8220c | ||
|
|
0ba80ce61e | ||
|
|
7c23872e03 | ||
|
|
507938be35 | ||
|
|
8bee304e4d | ||
|
|
be239993f4 | ||
|
|
f396dac1cc | ||
|
|
ebba1f78ab | ||
|
|
c8c3fc2bcc | ||
|
|
14c6c05e00 | ||
|
|
3fbdd9635f | ||
|
|
d7be9c9a66 | ||
|
|
928c9fbacd | ||
|
|
2b666ab9f7 | ||
|
|
4ecc531998 | ||
|
|
b908384ba2 | ||
|
|
b27a4a94e4 | ||
|
|
5e8073022b | ||
|
|
9553425374 | ||
|
|
2012317bc9 | ||
|
|
18a118bb7a | ||
|
|
e00789f838 | ||
|
|
20563e6306 | ||
|
|
580c2d8213 | ||
|
|
c9b8789ea9 | ||
|
|
4271588ecd | ||
|
|
a5277ff4a0 | ||
|
|
a66f5d660d | ||
|
|
80061e8d76 | ||
|
|
d07a057f04 | ||
|
|
fa2949af7d | ||
|
|
a4680b7216 | ||
|
|
523eb910a6 | ||
|
|
2f21de79f7 | ||
|
|
092d402ff8 | ||
|
|
12e8d6c4c6 | ||
|
|
8c1486d577 | ||
|
|
57e9199baa | ||
|
|
f4aac0bd8d | ||
|
|
664150811a | ||
|
|
3cb4ae16ab | ||
|
|
73fc9ea54d | ||
|
|
dd80919ed6 | ||
|
|
d7103cd75c | ||
|
|
29beee367c | ||
|
|
d22fe9227b | ||
|
|
6ea9535463 | ||
|
|
bbd1b730b1 | ||
|
|
6e138e4f79 | ||
|
|
d6453bb53d | ||
|
|
9bfd34e6b2 | ||
|
|
7b13771cfe | ||
|
|
b70b222f80 | ||
|
|
2ffc8dd228 | ||
|
|
08d12f9ee6 | ||
|
|
7ce96c190f | ||
|
|
409b4b1963 | ||
|
|
05cd7d753e | ||
|
|
7be460e8b1 | ||
|
|
6dfbd07e86 | ||
|
|
7b56e53c47 | ||
|
|
a8cde3289a | ||
|
|
f4e7202dd0 | ||
|
|
a2de3d33bf | ||
|
|
46a3e3d353 | ||
|
|
756e27fb52 | ||
|
|
1d6e5a2464 | ||
|
|
dc77428df4 | ||
|
|
b293356cec | ||
|
|
1dc7647bb9 | ||
|
|
c3f6537a4d | ||
|
|
70a3dc938a | ||
|
|
1fd703aff9 | ||
|
|
aa3fb7d165 | ||
|
|
2b9ff3148c | ||
|
|
7f24269511 | ||
|
|
b34d65fce0 | ||
|
|
778cecb512 | ||
|
|
e7e0eb0f32 | ||
|
|
a0c55c6406 | ||
|
|
aba736cb96 | ||
|
|
bdec873fed | ||
|
|
2cc73660bf | ||
|
|
f3de45c6ad | ||
|
|
386e2c6306 | ||
|
|
2473065b98 | ||
|
|
ed91c54654 | ||
|
|
4d53e0adde | ||
|
|
35f87365c9 | ||
|
|
8e7d6d5dad | ||
|
|
95a10c692c | ||
|
|
3a31d5d49e | ||
|
|
77d7082ffc | ||
|
|
c43d09c8b1 | ||
|
|
d92e60ee5f | ||
|
|
b9ceacb43d | ||
|
|
2fe809f15a | ||
|
|
078a054dbd | ||
|
|
c57be77039 | ||
|
|
e6910f732f | ||
|
|
d0b599781d | ||
|
|
2431f2058b | ||
|
|
8c717537c4 | ||
|
|
e088f4654a | ||
|
|
b363b6151c | ||
|
|
3baa4f8223 | ||
|
|
373f4cfefd | ||
|
|
64a02b705a | ||
|
|
f72b5d04e8 | ||
|
|
1688546519 | ||
|
|
2eff6b7a3a | ||
|
|
58962f8470 | ||
|
|
dfd7ff5b39 | ||
|
|
d56ded8c18 | ||
|
|
af3ebbb3c2 | ||
|
|
1db3359b84 | ||
|
|
7a40ef74c6 | ||
|
|
b64cd36468 | ||
|
|
b3f9983f44 | ||
|
|
070b08a9e6 | ||
|
|
851e9ece03 | ||
|
|
51db63dff7 | ||
|
|
a5b972d87e | ||
|
|
4bc3408410 | ||
|
|
5d392d89ce | ||
|
|
86f97614b0 | ||
|
|
5b7bab6752 | ||
|
|
70042db2de | ||
|
|
bf69d37cbe | ||
|
|
ee7ec20f29 | ||
|
|
1e1ba9afa3 | ||
|
|
32a75c1ff5 | ||
|
|
f51783f039 | ||
|
|
c203ef8bcd | ||
|
|
61b99471a1 | ||
|
|
e27fa882fa | ||
|
|
7c05914e5a | ||
|
|
daa438b349 | ||
|
|
35624ab998 | ||
|
|
e6b145412b | ||
|
|
2d6ad41ed4 | ||
|
|
8544bdd881 | ||
|
|
a778ff01f6 | ||
|
|
e7c0ce794b | ||
|
|
4c50119ac2 | ||
|
|
2181618357 | ||
|
|
bec7e54f7f | ||
|
|
dc317220b3 | ||
|
|
981750a48a | ||
|
|
1df717084b | ||
|
|
f2caf14d6a | ||
|
|
199529a031 | ||
|
|
99108f9eff | ||
|
|
74672e2130 | ||
|
|
7d48bba926 | ||
|
|
5f19608e41 | ||
|
|
8d4c9119b4 | ||
|
|
b1b2eead26 | ||
|
|
b3af04d3ca | ||
|
|
04c7eace09 | ||
|
|
0e7d5dd013 | ||
|
|
27d3420ad7 | ||
|
|
c9672b35ce | ||
|
|
39512da74e | ||
|
|
0d44e7ec27 | ||
|
|
24082d169b | ||
|
|
3debaf0f41 | ||
|
|
2448266d7c | ||
|
|
3a426e258b | ||
|
|
b062d1ee3e | ||
|
|
ebc171d405 | ||
|
|
4951be6999 | ||
|
|
01a71132d5 | ||
|
|
a421a90e0a | ||
|
|
1d558e8391 | ||
|
|
30a697e708 | ||
|
|
0233daeda2 | ||
|
|
48372bcd91 | ||
|
|
44a1a1ebde | ||
|
|
2b07be1d09 | ||
|
|
a98685d89e | ||
|
|
1c4ba20646 | ||
|
|
d3954b94d3 | ||
|
|
7daea18907 | ||
|
|
0c84235a95 | ||
|
|
aab29cb0ab | ||
|
|
566c5057f9 | ||
|
|
cd52d7bcf6 | ||
|
|
ed3ec66d33 | ||
|
|
01c463c8e8 | ||
|
|
872465df40 | ||
|
|
b6f74287d0 | ||
|
|
013684b5ca | ||
|
|
90c2955a71 | ||
|
|
7be868db12 | ||
|
|
703f84e5e1 | ||
|
|
c92dbb10ac | ||
|
|
8ee2c44550 | ||
|
|
48f5099646 | ||
|
|
b9aeb19834 | ||
|
|
193926c795 | ||
|
|
a4c3491f0c | ||
|
|
ad65856b3d | ||
|
|
b2aac9f991 | ||
|
|
a582786655 | ||
|
|
953e994c88 | ||
|
|
9a6e91d3e5 | ||
|
|
fc4e7a2dee | ||
|
|
a56014bb66 | ||
|
|
ebfc438bd4 | ||
|
|
3996f02dea | ||
|
|
d637524bfc | ||
|
|
8570922dcc | ||
|
|
6598265f9b | ||
|
|
8f398dfd08 | ||
|
|
68e9d701de | ||
|
|
67e8a00b6d | ||
|
|
11b48bc374 | ||
|
|
fa80c62b28 | ||
|
|
7f9cc67518 | ||
|
|
84eb6fd460 | ||
|
|
2cc5a29b86 | ||
|
|
b178c08271 | ||
|
|
9a3b208ac5 | ||
|
|
2989155f05 | ||
|
|
50b56c64f5 | ||
|
|
be3e331afb | ||
|
|
9d8fdd0b20 | ||
|
|
033c7abe62 | ||
|
|
5525a21696 | ||
|
|
aed03cd03b | ||
|
|
5a5661f136 | ||
|
|
a3bcf92ea5 | ||
|
|
0ad31c90f6 | ||
|
|
789d68e80d | ||
|
|
f06a1e8b49 | ||
|
|
a0ca243955 | ||
|
|
9e2268bd74 | ||
|
|
702d085117 | ||
|
|
356a0d72c3 | ||
|
|
7fcc4a5283 | ||
|
|
15edfcd088 | ||
|
|
278d204d1c | ||
|
|
39b3e7e507 | ||
|
|
8487dd7cfd | ||
|
|
5d05d7936c | ||
|
|
0afd2fe720 | ||
|
|
59a79a30da | ||
|
|
5da380e1b0 | ||
|
|
2adf745d06 | ||
|
|
ef2eb0764c | ||
|
|
9e37d7051c | ||
|
|
05098c3382 | ||
|
|
32607ee74c | ||
|
|
30559cd2d3 | ||
|
|
8fd905215f | ||
|
|
270d75afe2 | ||
|
|
a65f97ac75 | ||
|
|
53c1856038 | ||
|
|
56b8af86d7 | ||
|
|
e852df3179 | ||
|
|
7c03f716a8 | ||
|
|
d4ba603cf7 | ||
|
|
deb0306347 | ||
|
|
881da25e8c | ||
|
|
a37b953e72 | ||
|
|
bba5188594 | ||
|
|
7dc633581d | ||
|
|
bd00d012e2 | ||
|
|
4f4476b79f | ||
|
|
0b8721c25e | ||
|
|
869e275e48 | ||
|
|
e4714870a4 | ||
|
|
1dbd7066de | ||
|
|
022e4986ee | ||
|
|
2c9c413e79 | ||
|
|
b380d5e2c7 | ||
|
|
650853c177 | ||
|
|
ae698f988a | ||
|
|
f920fdecfe | ||
|
|
4e4f823141 | ||
|
|
b421f7ae87 | ||
|
|
ca80d0489b | ||
|
|
1ed82426a1 | ||
|
|
7e9f7d4101 | ||
|
|
f240a33935 | ||
|
|
5bd0a26126 | ||
|
|
cc011e39ce | ||
|
|
29ee551b06 | ||
|
|
5b5e4157e3 | ||
|
|
3c19692312 | ||
|
|
24accf96a8 | ||
|
|
f2626b0fc0 | ||
|
|
cc9e9b109c | ||
|
|
adead9b578 | ||
|
|
8527d231e1 | ||
|
|
4c19341279 | ||
|
|
075fd4da2d | ||
|
|
52bee88ad2 | ||
|
|
52b784e0e5 | ||
|
|
3521732597 | ||
|
|
d3eec72e45 | ||
|
|
79bac912aa | ||
|
|
2381e323c1 | ||
|
|
d64cedd3fc | ||
|
|
c16d83fab0 | ||
|
|
cbed841414 | ||
|
|
67022beca0 | ||
|
|
6e76a72d78 | ||
|
|
a5575bc3a0 | ||
|
|
90b18158fc | ||
|
|
71b0e8e937 | ||
|
|
4d5e0ca7a3 | ||
|
|
b8685f2c39 | ||
|
|
c4e697d797 | ||
|
|
3502a39181 | ||
|
|
4a56c92e7b | ||
|
|
2aab617c2e | ||
|
|
471bd83eb2 | ||
|
|
ec7a7f4c25 | ||
|
|
bb0c0af189 | ||
|
|
d2f8f99683 | ||
|
|
1c146baeeb | ||
|
|
709aeff9ea | ||
|
|
34f18122f5 | ||
|
|
7208282431 | ||
|
|
204e72e9eb | ||
|
|
f626dfb7b7 | ||
|
|
08b263bf4e | ||
|
|
d0e99923fd | ||
|
|
1750512477 | ||
|
|
5e3b4c3a11 | ||
|
|
0c431d9746 | ||
|
|
438c5d9909 | ||
|
|
7d29edf6f4 | ||
|
|
50d505b896 | ||
|
|
2e06f5b1e8 | ||
|
|
3adcbb7904 | ||
|
|
f447df9873 | ||
|
|
27e98147ef | ||
|
|
48958cc638 | ||
|
|
ae4de2782a | ||
|
|
dcf89e0dbd | ||
|
|
35d92f43c0 | ||
|
|
12db60885f | ||
|
|
d637260dc3 | ||
|
|
0937064e18 | ||
|
|
54264efb20 | ||
|
|
6a1d3de75b | ||
|
|
3fc8c7d560 | ||
|
|
56e1d577fd | ||
|
|
272129f66c | ||
|
|
0441ede229 | ||
|
|
f001846e00 | ||
|
|
0ac627dfbb | ||
|
|
1a82a3bf7b | ||
|
|
a2d84886c0 | ||
|
|
97e8382a41 | ||
|
|
88738327fd | ||
|
|
0ad65f4748 | ||
|
|
9dad436f72 | ||
|
|
ebdc38fff2 | ||
|
|
97bbe6f305 | ||
|
|
2f0dbef1e5 | ||
|
|
1711286ef0 | ||
|
|
099d3f7b41 | ||
|
|
5f77478841 | ||
|
|
07cc3aa5c0 | ||
|
|
ff26f54bfd | ||
|
|
3323d2ed37 | ||
|
|
7b9b3f1ee2 | ||
|
|
cae98451e3 | ||
|
|
83da7569f5 | ||
|
|
26397ee8ad | ||
|
|
dcfebcb973 | ||
|
|
fd3b47908b | ||
|
|
217d2aeb7f | ||
|
|
e57c701837 | ||
|
|
b4aa8376de | ||
|
|
d4976ac47a | ||
|
|
b8a96d9a77 | ||
|
|
f75d582eee | ||
|
|
18e0ec9a55 | ||
|
|
68b80cdadc | ||
|
|
3d3c475d1b | ||
|
|
6668e72351 | ||
|
|
b72695f01a | ||
|
|
65f98c1f30 | ||
|
|
c6fd4c51cf | ||
|
|
3c11c8441f | ||
|
|
3261c5b071 | ||
|
|
c8625c70dd | ||
|
|
396e9d0c39 | ||
|
|
e16d3bf040 | ||
|
|
2c58a87982 | ||
|
|
a705ae5278 | ||
|
|
a426e93011 | ||
|
|
1c220d25ca | ||
|
|
039858dca2 | ||
|
|
4758b5efe8 | ||
|
|
36e065ab4d | ||
|
|
076497e14d | ||
|
|
e8d9891d13 | ||
|
|
4902d7fb9e | ||
|
|
a873ca6a3e | ||
|
|
a86cb27cfe | ||
|
|
d620930f10 | ||
|
|
8db8ecfef3 | ||
|
|
a0f99393f5 | ||
|
|
ae8c3b02d0 | ||
|
|
99427c2ef7 | ||
|
|
334f16c0b6 | ||
|
|
7a80a772c1 | ||
|
|
8b1d712e1e | ||
|
|
a121f5b61b | ||
|
|
251062170e | ||
|
|
92e59af4d8 | ||
|
|
fa4c23b76e | ||
|
|
a0a46850f5 | ||
|
|
4cd0563a93 | ||
|
|
b83f0f461c | ||
|
|
3668ede0ff | ||
|
|
945584384a | ||
|
|
b1dd27b516 | ||
|
|
dcba1aad10 | ||
|
|
acc8b9cdbc | ||
|
|
6cbdd825eb | ||
|
|
cc55f6015d | ||
|
|
95718ab6ec | ||
|
|
feab27295d | ||
|
|
ca176c7549 | ||
|
|
f8a7a0d6e8 | ||
|
|
0a05611e1d | ||
|
|
1a8aaa3b26 | ||
|
|
cd1c13b4b1 | ||
|
|
d87c5035dd | ||
|
|
77967731d5 | ||
|
|
0b54034470 | ||
|
|
8ad2583785 | ||
|
|
dd533a9ab4 | ||
|
|
7236589037 | ||
|
|
2c583c3071 | ||
|
|
5b989adebc | ||
|
|
855e606163 | ||
|
|
20f8d3c8a9 | ||
|
|
0c77dbb7ea | ||
|
|
2448e2ae3b | ||
|
|
d53d7aa2e2 | ||
|
|
0be1df7ee8 | ||
|
|
6aef0e145c | ||
|
|
4f1965fbaa | ||
|
|
32dc54ce72 | ||
|
|
548736f432 | ||
|
|
6790f8af08 | ||
|
|
89d587e7dd | ||
|
|
5c8b2ebf7a | ||
|
|
5ac8ba9bae | ||
|
|
8f8afd98a5 | ||
|
|
49fb16e2c6 | ||
|
|
0db47a8586 | ||
|
|
cec8978886 | ||
|
|
0160e3fa87 | ||
|
|
545bfa6ef9 | ||
|
|
c596b5a17d | ||
|
|
84dd26c1b7 | ||
|
|
a765903a41 | ||
|
|
25ab381916 | ||
|
|
4d3df5d98f | ||
|
|
6fe2024542 | ||
|
|
7f69a0bc5e | ||
|
|
5b829adedb | ||
|
|
4cd4c13b2d | ||
|
|
86be5df475 | ||
|
|
8c5e0cd4e9 | ||
|
|
d320c4650d | ||
|
|
a9b0b8adc8 | ||
|
|
ba01cb82f7 | ||
|
|
223b97b884 | ||
|
|
d437027f26 | ||
|
|
9a564ee204 | ||
|
|
d37ee89e84 | ||
|
|
5de86d3d91 | ||
|
|
4b6041302e | ||
|
|
536444f9d1 | ||
|
|
ba5ec57e4d | ||
|
|
9fa53ccf05 | ||
|
|
5c2bac4b9d | ||
|
|
ef1e24cec2 | ||
|
|
001e2ad287 | ||
|
|
0404ec9881 | ||
|
|
6ff5c88ebf | ||
|
|
1e761c31bd | ||
|
|
ab7b2d729e | ||
|
|
271a07a7d6 | ||
|
|
029293a086 | ||
|
|
cf1630a94a | ||
|
|
4634ace74e | ||
|
|
3733715184 | ||
|
|
bf37ac53a3 | ||
|
|
50934e6840 | ||
|
|
891ea48e11 | ||
|
|
70323b9477 | ||
|
|
451cd4c74a | ||
|
|
7d0951a08a | ||
|
|
d98ca9a202 | ||
|
|
3c2c5bedc5 | ||
|
|
749d34cd30 | ||
|
|
1898be2fe1 | ||
|
|
af34da4160 | ||
|
|
fa053b7e60 | ||
|
|
4588219e31 | ||
|
|
6e89346f00 | ||
|
|
2703c9899a | ||
|
|
d05c358fd2 | ||
|
|
7c6bc5c421 | ||
|
|
798c398f23 | ||
|
|
4cc2cc4ad4 | ||
|
|
dcfdca6351 | ||
|
|
832599b8c5 | ||
|
|
c81ef2669e | ||
|
|
19d837c222 | ||
|
|
4f48ddfaec | ||
|
|
2e01fe0b5b | ||
|
|
7d067d8c30 | ||
|
|
b3b326738c | ||
|
|
2bb2caf2d2 | ||
|
|
2a094883ad | ||
|
|
6d1126b8aa | ||
|
|
26a47537f9 | ||
|
|
01d43c69fb | ||
|
|
7db1989093 | ||
|
|
32eee3365a | ||
|
|
95b4bde918 | ||
|
|
0a97e28aab | ||
|
|
4fa8a3898a | ||
|
|
caa997fff1 | ||
|
|
bd68b977d5 | ||
|
|
98a6907976 | ||
|
|
e9d2182390 | ||
|
|
5e29f2c1b7 | ||
|
|
44e28f96e0 | ||
|
|
a0e81da8c5 | ||
|
|
85a7c3c60d | ||
|
|
01e07ca0bc | ||
|
|
1468ee5fde | ||
|
|
49f044ecde | ||
|
|
37f32ab197 | ||
|
|
de8a3666ec | ||
|
|
a60231ba59 | ||
|
|
c8dafede6d | ||
|
|
210191b251 | ||
|
|
7deea9eb75 | ||
|
|
5e9a46d54d | ||
|
|
be64930ebb | ||
|
|
488974dd3e | ||
|
|
5975b9125f | ||
|
|
f301296f1e | ||
|
|
407477dc68 | ||
|
|
eb5ad232a0 | ||
|
|
1b0ce9a123 | ||
|
|
7b855c851d | ||
|
|
f1b9952bf9 | ||
|
|
6f03854eda | ||
|
|
c356846d90 | ||
|
|
f7b8828deb | ||
|
|
4276671538 | ||
|
|
5b86c67a98 | ||
|
|
6ce6b9576d | ||
|
|
ca062140f3 | ||
|
|
a15c391e6c | ||
|
|
ba636b17a0 | ||
|
|
2a07eb84f6 | ||
|
|
949b9fb10e | ||
|
|
ae1b6af0d4 | ||
|
|
dd27504d30 | ||
|
|
c30a08cfc5 | ||
|
|
a4c49aa35e | ||
|
|
ba3a61f623 | ||
|
|
402fba734a | ||
|
|
262af0678f | ||
|
|
3765d558b6 | ||
|
|
4ed8ded502 | ||
|
|
ee380c5377 | ||
|
|
7fa25ca7ae | ||
|
|
d3c401ed4e | ||
|
|
cb8dae1ddb | ||
|
|
59deebc961 | ||
|
|
0ae73296cf | ||
|
|
8e78fb4caa | ||
|
|
336da25463 | ||
|
|
ffbc8c5f70 | ||
|
|
10d8cfde85 | ||
|
|
3c7f83407b | ||
|
|
fe4b668107 | ||
|
|
cc87b32206 | ||
|
|
9114867578 | ||
|
|
6f221852a2 | ||
|
|
c842994df5 | ||
|
|
202901b09f | ||
|
|
7c0fe1285a | ||
|
|
6dc2c680c5 | ||
|
|
9a4b56db6e | ||
|
|
8180f2c742 | ||
|
|
a76a895f1d | ||
|
|
b404458369 | ||
|
|
c44b9f8659 | ||
|
|
bef1ac2668 | ||
|
|
906f8fc2e7 | ||
|
|
c600c6da63 | ||
|
|
e789a18553 | ||
|
|
3f3c986932 | ||
|
|
3ccc5babc1 | ||
|
|
fafb2eba69 | ||
|
|
33cf82a982 | ||
|
|
e74eb7d3fc | ||
|
|
fbeead0c74 | ||
|
|
9a9c65ac8e | ||
|
|
0a8d86cfc3 | ||
|
|
8632a0a6ec | ||
|
|
e111b6e1b7 | ||
|
|
b7aba15d58 | ||
|
|
713cbb81b8 | ||
|
|
fc0739703b | ||
|
|
382d7e8ac3 | ||
|
|
23e578bfbf | ||
|
|
bd7cd32f91 | ||
|
|
0a5400263b | ||
|
|
17d59d3337 | ||
|
|
9cb3531e2d | ||
|
|
b50e6e0d90 | ||
|
|
a88436c620 | ||
|
|
45cf90094a | ||
|
|
ae1df20893 | ||
|
|
71148740d4 | ||
|
|
4ef583c844 | ||
|
|
16f61ea96d | ||
|
|
32f9616b6e | ||
|
|
1346eb4f76 | ||
|
|
d4268ba070 | ||
|
|
696290527a | ||
|
|
686866c6f9 | ||
|
|
72455ccde1 | ||
|
|
88fb860568 | ||
|
|
ceed494cf7 | ||
|
|
ac8f919304 | ||
|
|
7f1ec4802d | ||
|
|
d5777b7bce | ||
|
|
e34f179a82 | ||
|
|
6a90b9a512 | ||
|
|
ce5ee65d60 | ||
|
|
65a8dbfe41 | ||
|
|
1ff667b7ef | ||
|
|
0806c7fbdb | ||
|
|
5708d7a6b8 | ||
|
|
2264cc5d10 | ||
|
|
28cb3f9d0c | ||
|
|
f16ed4697f | ||
|
|
81e412fe5b | ||
|
|
f3137eb0a9 | ||
|
|
0e5bd4ed74 | ||
|
|
6a37f25c5d | ||
|
|
2afff05014 | ||
|
|
e3d9417b84 | ||
|
|
6a188033c6 | ||
|
|
df24fd7bf2 | ||
|
|
d45b4ad134 | ||
|
|
ec938f254c | ||
|
|
fa82e759bd | ||
|
|
daf0bcfac3 | ||
|
|
09428dcade | ||
|
|
49d51c5af8 | ||
|
|
ec7f69d559 | ||
|
|
a8c5bf2573 | ||
|
|
f12de61d7f | ||
|
|
71fcd6482d | ||
|
|
e59b38a5d2 | ||
|
|
b07658b460 | ||
|
|
5b8d9d1510 | ||
|
|
6169c458bf | ||
|
|
23400d0449 | ||
|
|
992d11be10 | ||
|
|
feac45fd0a | ||
|
|
934176818f | ||
|
|
c5435ec1fa | ||
|
|
c3f472fbcb | ||
|
|
1cee1c3562 | ||
|
|
eae5a74a11 | ||
|
|
5df1a223c2 | ||
|
|
81382a4bc0 | ||
|
|
750a429bd4 | ||
|
|
dbe78dff93 | ||
|
|
5869627b32 | ||
|
|
5bc5ef0ae8 | ||
|
|
2cfd2e1410 | ||
|
|
cce5e8f811 | ||
|
|
058e66c7fc | ||
|
|
8ae09c3d02 | ||
|
|
83699bc5c3 | ||
|
|
3a5eb8c5d2 | ||
|
|
fd00e5eaae | ||
|
|
20444ee7d5 | ||
|
|
eb06e575c2 | ||
|
|
ad402adf7a | ||
|
|
21c582ee1a | ||
|
|
e0d78d5328 | ||
|
|
f96acd6263 | ||
|
|
e185f2eaf6 | ||
|
|
78fb9dcc59 | ||
|
|
e066b502c3 | ||
|
|
e94943d505 | ||
|
|
b4315152e2 | ||
|
|
aae9446ce0 | ||
|
|
ecd46f2f06 | ||
|
|
74f77a7e8a | ||
|
|
340a9f8b76 | ||
|
|
81cec6a25a | ||
|
|
c5b31e2f63 | ||
|
|
0642b17ab0 | ||
|
|
ac689a9a2e | ||
|
|
43cc4ec009 | ||
|
|
9cc333b2bd | ||
|
|
55d98ff935 | ||
|
|
dc90a4ed42 | ||
|
|
ef80abd885 | ||
|
|
b9080f9479 | ||
|
|
092a5a1ca3 | ||
|
|
d3c31c52a2 | ||
|
|
329173e145 | ||
|
|
8e4f8fcbc8 | ||
|
|
c1c707c77a | ||
|
|
b1e58dd10d | ||
|
|
2cc0aceac7 | ||
|
|
d79d5dbadd | ||
|
|
ebd8a10cef | ||
|
|
be7475fe66 | ||
|
|
85e2f7eb34 | ||
|
|
9bb979f2e8 | ||
|
|
c942345453 | ||
|
|
fcc344ba99 | ||
|
|
a3b28871c6 | ||
|
|
2ca034bfa8 | ||
|
|
0d035a08d6 | ||
|
|
6655e2664e | ||
|
|
c51ce2eec7 | ||
|
|
b2e00eb868 | ||
|
|
820df0c596 | ||
|
|
865e3dcd0c | ||
|
|
f55e2fc7b8 | ||
|
|
cb22e1933e | ||
|
|
8818a9e82a | ||
|
|
dbb47d506c | ||
|
|
e4d5d77347 | ||
|
|
dd52124765 | ||
|
|
6f3267aee9 | ||
|
|
0bbd6b7c4b | ||
|
|
1cc47b02cf | ||
|
|
e7babc071d | ||
|
|
d11ca2e159 | ||
|
|
2d6365dc6a | ||
|
|
cc24119a59 | ||
|
|
b8f15333d8 | ||
|
|
e5a426419c | ||
|
|
8a1df1e700 | ||
|
|
cc298f56e2 | ||
|
|
665693a513 | ||
|
|
c35e82e59f | ||
|
|
72d38f4cff | ||
|
|
f6c47ac19c | ||
|
|
0430dc5a1f | ||
|
|
27bec89386 | ||
|
|
79b3ce141c | ||
|
|
011afd0910 | ||
|
|
8d00e4d31d | ||
|
|
8688777ae1 | ||
|
|
198aceee07 | ||
|
|
ee1bf1c221 | ||
|
|
24ecf1a166 | ||
|
|
5cb49c86a0 | ||
|
|
9940d2590b | ||
|
|
577fce69e2 | ||
|
|
e68375a71e | ||
|
|
fe7fb6c7a9 | ||
|
|
cec67d8eff | ||
|
|
2443e2ec7c | ||
|
|
bcf862044a | ||
|
|
b73f049484 | ||
|
|
cde5314d41 | ||
|
|
ed5eb28228 | ||
|
|
db5e66a9b0 | ||
|
|
77723b615f | ||
|
|
96c7716d3e | ||
|
|
ea80c1ed83 | ||
|
|
7d7b7c1e25 | ||
|
|
9d860c265c | ||
|
|
d234b4b0f1 | ||
|
|
e1598a9966 | ||
|
|
e82f38e277 | ||
|
|
1d27aa2f18 | ||
|
|
113dbbdf94 | ||
|
|
e701b5b5eb | ||
|
|
6f01896d04 | ||
|
|
5582bbac60 | ||
|
|
d2401a212a | ||
|
|
52548542d2 | ||
|
|
509cbdf476 | ||
|
|
5a6ebd6657 | ||
|
|
541d3f286a | ||
|
|
0191af412b | ||
|
|
abcd2a2d01 | ||
|
|
91efcb910b | ||
|
|
e9b7320d1c | ||
|
|
779744bd8e | ||
|
|
1123d85fd2 | ||
|
|
5c247d2833 | ||
|
|
17fdb4f5c9 | ||
|
|
365c1987ed | ||
|
|
c03422ee37 | ||
|
|
ef8f23ce90 | ||
|
|
050f7ebddf | ||
|
|
10fdf46410 | ||
|
|
49976ae35a | ||
|
|
21afb05409 | ||
|
|
0af1ec3bde | ||
|
|
343d766ddd | ||
|
|
02cae2e61e | ||
|
|
1975c9c34a | ||
|
|
42decbddd9 | ||
|
|
d88bb3c668 | ||
|
|
b71467b9be | ||
|
|
ac503ef72e | ||
|
|
ea608cecb0 | ||
|
|
2e266ec945 | ||
|
|
bec1adf7ad | ||
|
|
2fc2f47d06 | ||
|
|
3a8e19d1b4 | ||
|
|
cc9db0220c | ||
|
|
13ed78be96 | ||
|
|
cd1ede38a7 | ||
|
|
1ec68c428a | ||
|
|
f6ed3bc9db | ||
|
|
34cdda2e28 | ||
|
|
4ab2c907f5 | ||
|
|
8632d99341 | ||
|
|
efa32ed4f6 | ||
|
|
551b8af76d | ||
|
|
887254f5da | ||
|
|
0275bd1d45 | ||
|
|
f3891c305d | ||
|
|
5874b78349 | ||
|
|
a812fc07f2 | ||
|
|
78e7312adf | ||
|
|
593970ed6d | ||
|
|
693bf4816b | ||
|
|
b8b11880b0 | ||
|
|
0ff4016250 | ||
|
|
e753f3849e | ||
|
|
7b248c8fb2 | ||
|
|
f0e4dd90ee | ||
|
|
c540ec5417 | ||
|
|
3615ab535b | ||
|
|
b8d6ebe882 | ||
|
|
1f27872294 | ||
|
|
3815516022 | ||
|
|
a1dfdced31 | ||
|
|
ff39a8db3f | ||
|
|
9877e899e0 | ||
|
|
bc4a6138b1 | ||
|
|
f825cab54a | ||
|
|
57b01c2711 | ||
|
|
5f3e3d7796 | ||
|
|
bee75a4508 | ||
|
|
a4ac444bda | ||
|
|
d13b8c8421 | ||
|
|
0af3063127 | ||
|
|
17e1975dd8 | ||
|
|
166eeacab9 | ||
|
|
296df2660a | ||
|
|
017169f1a1 | ||
|
|
ed0e73803f | ||
|
|
f4475b9d2a | ||
|
|
b9849da66e | ||
|
|
8a1df9afee | ||
|
|
9cfb56d9c6 | ||
|
|
25ade86994 | ||
|
|
da484b07f5 | ||
|
|
20c0916adc | ||
|
|
c10ec2962b | ||
|
|
de5632b1cf | ||
|
|
1bb06106fd | ||
|
|
bc18cdbeea | ||
|
|
67df5d6dcc | ||
|
|
68a7282b27 | ||
|
|
5732b66c5a | ||
|
|
f1cce8ef46 | ||
|
|
e47932c9aa | ||
|
|
3a70fb0e15 | ||
|
|
d5da84dcb2 | ||
|
|
1bd051d703 | ||
|
|
abb55f8e4b | ||
|
|
46f6a257d0 | ||
|
|
97e4eec5be | ||
|
|
69b99abb7d | ||
|
|
08f6d5a683 | ||
|
|
627bea30ed | ||
|
|
caf0a5bbe8 | ||
|
|
501f988b0f | ||
|
|
5f0c6fce74 | ||
|
|
78089107b7 | ||
|
|
f3b0906cea | ||
|
|
9f581c6181 | ||
|
|
31b232d3f3 | ||
|
|
77e6a6de61 | ||
|
|
85702a8142 | ||
|
|
0bcdc9641d | ||
|
|
bfd1bdfc64 | ||
|
|
c1fed6d469 | ||
|
|
79ffe021e4 | ||
|
|
a1c348b365 | ||
|
|
904b1f114b | ||
|
|
63f481d498 | ||
|
|
fc5c19788e | ||
|
|
56e0bde8ab | ||
|
|
db2cdca71b | ||
|
|
faa2fba6b9 | ||
|
|
f727ea2874 | ||
|
|
4a7ef07089 | ||
|
|
0df726cdab | ||
|
|
2f6f0d2f3e | ||
|
|
fae5938b0a | ||
|
|
2ec3639900 | ||
|
|
4513404629 | ||
|
|
13bfda56ef | ||
|
|
075c164407 | ||
|
|
dcb5049f97 | ||
|
|
d24cf14009 | ||
|
|
3d34802ab2 | ||
|
|
2aec21a6d0 | ||
|
|
19ccff9ff1 | ||
|
|
1dd9c6754c | ||
|
|
f9467ceaf1 | ||
|
|
ccce0f2d3b | ||
|
|
f51da4f0c4 | ||
|
|
2eff389fff | ||
|
|
77ca2ec0e9 | ||
|
|
77e6ea8a6f | ||
|
|
e33299bbd7 | ||
|
|
74e273274c | ||
|
|
02bf5902f0 | ||
|
|
5f41de8938 | ||
|
|
f71d839009 | ||
|
|
3cb6b17316 | ||
|
|
c0cb677a4c | ||
|
|
a5a6a8eaee | ||
|
|
ecb861de56 | ||
|
|
1d20f18d3f | ||
|
|
68f6b3452e | ||
|
|
60075f6e8c | ||
|
|
1d7789f544 | ||
|
|
bd10a9aa26 | ||
|
|
1ceeed38bc | ||
|
|
2e1e917952 | ||
|
|
9e0b91ac17 | ||
|
|
ea83ec496b | ||
|
|
f26bb26ffa | ||
|
|
28348f919b | ||
|
|
c84d4c637d | ||
|
|
04706cfa9c | ||
|
|
3078bea7cc | ||
|
|
c91e0fc988 | ||
|
|
5183af4e8a | ||
|
|
1ff5cb0596 | ||
|
|
f441ce9c17 | ||
|
|
522e5e7957 | ||
|
|
9e468bd059 | ||
|
|
ecc9443677 | ||
|
|
6b8d8bf735 | ||
|
|
43d024fe42 | ||
|
|
c016dedea4 | ||
|
|
b9ca2cfe90 | ||
|
|
d3492b5b6c | ||
|
|
3682cd6a5e | ||
|
|
b209b1e481 | ||
|
|
4afed48f58 | ||
|
|
6a18ce078e | ||
|
|
bc5aadff7d | ||
|
|
de89b516dc | ||
|
|
4dbe9a7015 | ||
|
|
a320f16404 | ||
|
|
cec7241986 | ||
|
|
6de77293ad | ||
|
|
0ac53d8353 | ||
|
|
da79b93387 | ||
|
|
b88c300d04 | ||
|
|
88372cd516 | ||
|
|
b3206121cc | ||
|
|
5bccb5fc42 | ||
|
|
47e1cbdaff | ||
|
|
52020cbe26 | ||
|
|
d33651c25b | ||
|
|
18e9f9c304 | ||
|
|
2607c44fbb | ||
|
|
c8a7d44f55 | ||
|
|
a6e536189c | ||
|
|
3f9a6cae53 | ||
|
|
cd5494fdd2 | ||
|
|
5ab1b14d6b | ||
|
|
379f181362 | ||
|
|
dd19aa63d0 | ||
|
|
6df3b417a9 | ||
|
|
fa76431dd6 | ||
|
|
6504fb68b6 | ||
|
|
2ff49cf657 | ||
|
|
fa1aa1fe80 | ||
|
|
85b95a2940 | ||
|
|
5e38039c4d | ||
|
|
4df936a437 | ||
|
|
736a8b1b80 | ||
|
|
22f6128bc4 | ||
|
|
db475b6998 | ||
|
|
6daf7f6322 | ||
|
|
16086c0961 | ||
|
|
e88c81ad0d | ||
|
|
a2b9aed40d | ||
|
|
caa4fe1ec4 | ||
|
|
25fb878e54 | ||
|
|
a1d6ffadad | ||
|
|
3b89814b69 | ||
|
|
071e0be445 | ||
|
|
bed364d75e | ||
|
|
d2984e9e16 | ||
|
|
05a8c692f2 | ||
|
|
fd9570e782 | ||
|
|
35d26afcb4 | ||
|
|
2245ee6fce | ||
|
|
ff10130176 | ||
|
|
a13f1a9bee | ||
|
|
3cc60fa4d4 | ||
|
|
bd69c24231 | ||
|
|
c7d292a716 | ||
|
|
74bc159a52 | ||
|
|
9f99a1896d | ||
|
|
8110259d1c | ||
|
|
1048bf993a | ||
|
|
3eba0f4e0d | ||
|
|
d7e0b364d1 | ||
|
|
25dd43b949 | ||
|
|
8e8ffa30a6 | ||
|
|
69da2f4b23 | ||
|
|
d48bab02a1 | ||
|
|
886120fe9f | ||
|
|
bdd4d9f3ff | ||
|
|
6276c2f1f5 | ||
|
|
b3acc97d03 | ||
|
|
24bd1db4fc | ||
|
|
e83d3cb4a3 | ||
|
|
674481f0d1 | ||
|
|
5d271d21bc | ||
|
|
4e4493e627 | ||
|
|
1154905818 | ||
|
|
0d3ea848c2 | ||
|
|
8ffe1e65fd | ||
|
|
127ea7e351 | ||
|
|
34b16f1919 | ||
|
|
fa32faf759 | ||
|
|
6f3d7e76c9 | ||
|
|
57665e8964 | ||
|
|
5c502b1fe4 | ||
|
|
6d1410741d | ||
|
|
3e20724058 | ||
|
|
197f47befe | ||
|
|
d126611e87 | ||
|
|
1b75be5710 | ||
|
|
6272969983 | ||
|
|
084fc2fcd1 | ||
|
|
b2e68db380 | ||
|
|
459156fe57 | ||
|
|
7abfe68458 | ||
|
|
442d7a7226 | ||
|
|
80ebcb2c28 | ||
|
|
9735d1c6f3 | ||
|
|
ef017fd343 | ||
|
|
cd928bc586 | ||
|
|
d2b4d25317 | ||
|
|
a418cd2a2a | ||
|
|
94cdd88474 | ||
|
|
1bdf3876fc | ||
|
|
30435e6406 | ||
|
|
bd1c06a7a7 | ||
|
|
8358272b9a | ||
|
|
ff3cce0ed2 | ||
|
|
c3787af29d | ||
|
|
b3810a16cc | ||
|
|
38539aac74 | ||
|
|
58e8346209 | ||
|
|
ba9ce4adf2 | ||
|
|
a3a8fa1cef | ||
|
|
c6805271e9 | ||
|
|
bd60fe5a13 | ||
|
|
b72f3baab7 | ||
|
|
d1d54ce9c8 | ||
|
|
954f26308b | ||
|
|
1cafa0b33f | ||
|
|
18c17168e1 | ||
|
|
d3a97f1062 | ||
|
|
d18f078b1e | ||
|
|
9ffcde878a | ||
|
|
aa970d6ca5 | ||
|
|
503c68320a | ||
|
|
a106342439 | ||
|
|
00f5471270 | ||
|
|
3b0e6ec65c | ||
|
|
8691ddc081 | ||
|
|
f5199d2b73 | ||
|
|
0c8d9d5614 | ||
|
|
24c5935661 | ||
|
|
bff6697690 | ||
|
|
02405b4856 | ||
|
|
78b37b592e | ||
|
|
4db88cf86b | ||
|
|
a7e17eabff | ||
|
|
93d9ce18b2 | ||
|
|
6f7ac93d84 | ||
|
|
fa5d60ca26 | ||
|
|
20ed81cd86 | ||
|
|
6f38874096 | ||
|
|
b7db5be4df | ||
|
|
6d278994ec | ||
|
|
157dbffc59 | ||
|
|
f21ab3588d | ||
|
|
2900bda8f9 | ||
|
|
5f37c19d42 | ||
|
|
6f6356e617 | ||
|
|
5d152baac0 | ||
|
|
d7739f9764 | ||
|
|
d6e73fde50 | ||
|
|
fd149b3f46 | ||
|
|
d1d45e7166 | ||
|
|
4b19759dd6 | ||
|
|
47f44680a3 | ||
|
|
0a344ada97 | ||
|
|
3b98d01f71 | ||
|
|
23d8000239 | ||
|
|
c32ee40ca0 | ||
|
|
213f45494f | ||
|
|
85a51e6c42 | ||
|
|
ecfdc6f0a8 | ||
|
|
1c1c3fd832 | ||
|
|
7bafe995e5 | ||
|
|
3c23672347 | ||
|
|
33fe68d7eb | ||
|
|
b031fa1531 | ||
|
|
dab4dc3f41 | ||
|
|
78096803ea | ||
|
|
f1987b9544 | ||
|
|
8b6bf08a23 | ||
|
|
2640203c88 | ||
|
|
2dafa9644f | ||
|
|
92ebeddcb0 | ||
|
|
ecee073e08 | ||
|
|
6d1b9ffad2 | ||
|
|
7ee4b54154 | ||
|
|
e3271b8082 | ||
|
|
c6ff45f959 | ||
|
|
5d3f22e06a | ||
|
|
c77f6c5f7b | ||
|
|
6d03c3a54c | ||
|
|
92dc63e5c9 | ||
|
|
505d319e01 | ||
|
|
62f845a94e | ||
|
|
7a9eff7f35 | ||
|
|
b849ea8eaa | ||
|
|
f1635fa302 | ||
|
|
6110b72b87 | ||
|
|
247de600af | ||
|
|
3e60d3d96e | ||
|
|
6de617ecd5 | ||
|
|
7c8df97968 | ||
|
|
acf6736afd | ||
|
|
5f2480c3d9 | ||
|
|
0593a078c6 | ||
|
|
323c70dcdf | ||
|
|
a1b0f84444 | ||
|
|
df6c271830 | ||
|
|
12bf04826a | ||
|
|
52a1b0453c | ||
|
|
0dcb56eab5 | ||
|
|
a42f78b08b | ||
|
|
505768db04 | ||
|
|
c74db95c14 | ||
|
|
af3f27feae | ||
|
|
7b366d49d2 | ||
|
|
88b30e7e28 | ||
|
|
05c0febd04 | ||
|
|
9d70ec56f2 | ||
|
|
7f8ec59939 | ||
|
|
0398ec40b7 | ||
|
|
f55465f8b8 | ||
|
|
a4384bd340 | ||
|
|
a2368a75f7 | ||
|
|
68e5e74882 | ||
|
|
b2afac8914 | ||
|
|
0637c1c9d4 | ||
|
|
0453a5fb3d | ||
|
|
3ad56ea103 | ||
|
|
6322ab9f07 | ||
|
|
21d0f10dd7 | ||
|
|
51b9e81c3d | ||
|
|
7723011594 | ||
|
|
91c11a18e0 | ||
|
|
215ba35fe1 | ||
|
|
7e17011ebc | ||
|
|
1f25edc737 | ||
|
|
ab13db73e7 | ||
|
|
cb5eb9edc7 | ||
|
|
509675fe66 | ||
|
|
da5c12f466 | ||
|
|
f2ad10bbce | ||
|
|
99a7bece2e | ||
|
|
b305fd8865 | ||
|
|
c0beb621e2 | ||
|
|
da6d64e581 | ||
|
|
a6d58ee360 | ||
|
|
2a480ab7db | ||
|
|
5c09ee29db | ||
|
|
15526bd6e8 | ||
|
|
c8f48168b4 | ||
|
|
d937d473f9 | ||
|
|
66f14c6343 | ||
|
|
f25fadafd8 | ||
|
|
d9b0df94e2 | ||
|
|
200d11ca99 | ||
|
|
f8c48ef60a | ||
|
|
1fc0680c71 | ||
|
|
ec4afb2cbc | ||
|
|
a2a632c415 | ||
|
|
962e64106c | ||
|
|
c40b6088ee | ||
|
|
0d1a254ca8 | ||
|
|
fdbcb61a12 | ||
|
|
6435cb1466 | ||
|
|
4739cfab9a | ||
|
|
00a73e9cc8 | ||
|
|
0ff5dec004 | ||
|
|
07a15883bd | ||
|
|
2bd1e81917 | ||
|
|
755f91f5ab | ||
|
|
7603b5a56c | ||
|
|
7ef84cfbfe | ||
|
|
41129cf379 | ||
|
|
c8c394b4e9 | ||
|
|
59886a1528 | ||
|
|
a28d2c869f | ||
|
|
6f1fff44fa | ||
|
|
108978d1a5 | ||
|
|
a4733b4d91 | ||
|
|
66f4671ec0 | ||
|
|
c932621b5d | ||
|
|
21173dc907 | ||
|
|
f2c30ba3f7 | ||
|
|
989bdfb0d5 | ||
|
|
5761dce957 | ||
|
|
95e2e574b8 | ||
|
|
c789d80ce5 | ||
|
|
8422ffcd19 | ||
|
|
2d30535b69 | ||
|
|
0a815179a2 | ||
|
|
aa24e5d284 | ||
|
|
23aad24027 | ||
|
|
7ae5459fe7 | ||
|
|
0a8a88421e | ||
|
|
4f42a176ef | ||
|
|
4457067045 | ||
|
|
e7dc2048ad | ||
|
|
af6094bb06 | ||
|
|
0ab139cb24 | ||
|
|
500a7301f2 | ||
|
|
c92cef1780 | ||
|
|
49ceac1ac7 | ||
|
|
276a93605d | ||
|
|
0150c3fe23 | ||
|
|
34674501c3 | ||
|
|
f6206d4c30 | ||
|
|
6dcd9596a2 | ||
|
|
e165838e54 | ||
|
|
eae1707e97 | ||
|
|
ed2950f73b | ||
|
|
7eae8f68d8 | ||
|
|
0ab2d18b52 | ||
|
|
8ab621bc91 | ||
|
|
41e51bbeb5 | ||
|
|
66d9e8c038 | ||
|
|
f5e47b2b74 | ||
|
|
678a823e8b | ||
|
|
246746a82e | ||
|
|
00038453e1 | ||
|
|
9ce485eade | ||
|
|
8a6ff5a42b | ||
|
|
1eaac0072d | ||
|
|
35716a7e4c | ||
|
|
e0f89beebb | ||
|
|
b8bf2a707c | ||
|
|
7d616be8f4 | ||
|
|
0a7e9f9f8f | ||
|
|
226e188903 | ||
|
|
590d454119 | ||
|
|
06d5bd799f | ||
|
|
7fe6541b7c | ||
|
|
0f30fa5fb9 | ||
|
|
2c362315ad | ||
|
|
7dd79edc52 | ||
|
|
3d6f25a9d3 | ||
|
|
b3c21feba3 | ||
|
|
fe54fadbf8 | ||
|
|
caa0722d49 | ||
|
|
fee32622f1 | ||
|
|
d1910e4274 | ||
|
|
45eb11d7f4 | ||
|
|
874179fa75 | ||
|
|
8b16cb4bae | ||
|
|
777ed55c21 | ||
|
|
fcafd8b05e | ||
|
|
5f1cadfea4 | ||
|
|
917a981096 | ||
|
|
89f8b6060f | ||
|
|
345346315a | ||
|
|
f1c27e1798 | ||
|
|
4316024dc7 | ||
|
|
ca05aa84ff | ||
|
|
d10d7dc298 | ||
|
|
063af1dbc7 | ||
|
|
ef6fe43251 | ||
|
|
2659055c31 | ||
|
|
f5ed757780 | ||
|
|
e114b842ba | ||
|
|
12e2116acb | ||
|
|
bccc97d25f | ||
|
|
a9bcc75733 | ||
|
|
7b87475af8 | ||
|
|
2ac218886a | ||
|
|
97503897f0 | ||
|
|
7e06e32cb6 | ||
|
|
15b813c6af | ||
|
|
a71c9107bd | ||
|
|
4c2110189b | ||
|
|
df750c2a71 | ||
|
|
3eb412b750 | ||
|
|
5c7b7e4182 | ||
|
|
2b0fad87d2 | ||
|
|
8817d41275 | ||
|
|
04ef3f9496 | ||
|
|
fbe26e13ae | ||
|
|
4408dffa87 | ||
|
|
546192865c | ||
|
|
7afa550ba4 | ||
|
|
21a9372320 | ||
|
|
f4a2eec984 | ||
|
|
94b9455c9c | ||
|
|
c41c71c8c1 | ||
|
|
dde647c570 | ||
|
|
445a89ba6e | ||
|
|
07495f6621 | ||
|
|
5952edc550 | ||
|
|
b66a038a2f | ||
|
|
df1c8a64e5 | ||
|
|
3e2aebcd10 | ||
|
|
0d279cb4dd | ||
|
|
3942a76d15 | ||
|
|
1bebf3d3d6 | ||
|
|
d5f54c240a | ||
|
|
8868ecd11a | ||
|
|
8a31c57bfa | ||
|
|
7e2cce4a8d | ||
|
|
bfb217c203 | ||
|
|
593e55af95 | ||
|
|
29af62f956 | ||
|
|
2b022e1871 | ||
|
|
783a4d3996 | ||
|
|
ddfe925f8c | ||
|
|
1735bcab21 | ||
|
|
ff6cacf766 | ||
|
|
2a72eae77f | ||
|
|
39fb7fdfe3 | ||
|
|
e0facf35c8 | ||
|
|
73d48c8ac4 | ||
|
|
5080059143 | ||
|
|
90d5b77d74 | ||
|
|
e0f987328b | ||
|
|
04c3a925cc | ||
|
|
219b29991b | ||
|
|
5120045459 | ||
|
|
3bca4a73f4 | ||
|
|
b4857ab036 | ||
|
|
f3ae041691 | ||
|
|
a84627a465 | ||
|
|
78aeca6399 | ||
|
|
5dd8f0a0b7 | ||
|
|
9378fc7276 | ||
|
|
3a0d8069b6 | ||
|
|
bab42014a4 | ||
|
|
74fed5695f | ||
|
|
3be368c333 | ||
|
|
a4a8e2614e | ||
|
|
58eb151ba1 | ||
|
|
21d21ea7f0 | ||
|
|
45dc5e236d | ||
|
|
0597eeed6b | ||
|
|
0a66fe196a | ||
|
|
3f21b4aa7d | ||
|
|
b97186c5bf | ||
|
|
874b24fb32 | ||
|
|
97889b5e55 | ||
|
|
4b134194cf | ||
|
|
24b60a25d5 | ||
|
|
3e069376a7 | ||
|
|
5bdb2c9e86 | ||
|
|
71b0c5f805 | ||
|
|
a1f62e7a1f | ||
|
|
186c40801a | ||
|
|
bf867e84d6 | ||
|
|
27035a8e1d | ||
|
|
db3770c3e5 | ||
|
|
46337ca554 | ||
|
|
4f0f288000 | ||
|
|
8060bae1b8 | ||
|
|
ea32c97b82 | ||
|
|
99efbd30f1 | ||
|
|
a9eb7cb4fd | ||
|
|
a11cab0f88 | ||
|
|
447324b59f | ||
|
|
e580e815f0 | ||
|
|
5fc00840f2 | ||
|
|
ff59dcd66e | ||
|
|
0a1d82de2a | ||
|
|
26837645ac | ||
|
|
c318699d6a | ||
|
|
e5caab635a | ||
|
|
d5f96295ff | ||
|
|
4a6c78aaf3 | ||
|
|
c4d214f5c0 | ||
|
|
1ed7e6cb1c | ||
|
|
6b7f80f24a | ||
|
|
220217561a | ||
|
|
75e857a510 | ||
|
|
052bdd8eb0 | ||
|
|
9e54de8a8a | ||
|
|
fd2809e367 | ||
|
|
18874c2069 | ||
|
|
115eb0ddef | ||
|
|
9940258725 | ||
|
|
74e14a4fc3 | ||
|
|
90ea386c62 | ||
|
|
a5b132dfd7 | ||
|
|
157debf8e4 | ||
|
|
74a5528f17 | ||
|
|
21de9e938a | ||
|
|
9c4d3cd6e2 | ||
|
|
0616d184e0 | ||
|
|
6f8b3a6242 | ||
|
|
9a006dc84a | ||
|
|
d2dbaf52a1 | ||
|
|
0a9145cd3c | ||
|
|
e778b02c0c | ||
|
|
8fb5f0ef3c | ||
|
|
43f5e4360d | ||
|
|
cdc8640218 | ||
|
|
21f8d7a967 | ||
|
|
801d92bbba | ||
|
|
77116172e4 | ||
|
|
9d3c0f4ff0 | ||
|
|
6382d2b730 | ||
|
|
6ad4493a91 | ||
|
|
f070dc5527 | ||
|
|
fc6e7c81d3 | ||
|
|
59a725c52c | ||
|
|
51b3d7cb4a | ||
|
|
3baff4e675 | ||
|
|
06911e7ad0 | ||
|
|
4e0f3e35db | ||
|
|
fd6ce57003 | ||
|
|
4f55be4f19 | ||
|
|
bd20c83919 | ||
|
|
48f8a45031 | ||
|
|
680cec5abb | ||
|
|
e8b3c66c21 | ||
|
|
ecdc3be52e | ||
|
|
301bdf2186 | ||
|
|
1c1b67e000 | ||
|
|
662870baf4 | ||
|
|
bc85520f5e | ||
|
|
d3be7a3edf | ||
|
|
6cd0da821a | ||
|
|
1333c41811 | ||
|
|
55eceb4080 | ||
|
|
e95629f827 | ||
|
|
6332ee6edb | ||
|
|
901d56f898 | ||
|
|
010c3494fd | ||
|
|
5ebc345e95 | ||
|
|
b3a768a04b | ||
|
|
f414af4d82 | ||
|
|
b743ca8eb1 | ||
|
|
a3a5bffce4 | ||
|
|
7971f3cbd8 | ||
|
|
d1b80aa4d4 | ||
|
|
08d9f7d967 | ||
|
|
5f62b4a89d | ||
|
|
776502e6db | ||
|
|
39e0a1fb9e | ||
|
|
5ed02d2f0d | ||
|
|
72e864b013 | ||
|
|
5ab012163e | ||
|
|
1900686bc4 | ||
|
|
809a294f9d | ||
|
|
cf3f6ede72 | ||
|
|
b76a90304d | ||
|
|
7af5b24b03 | ||
|
|
1f81fb9284 | ||
|
|
4e105492ca | ||
|
|
abf7dd5fa8 | ||
|
|
6c877b6d44 | ||
|
|
86d2998dc0 | ||
|
|
201daf8ff3 | ||
|
|
1140f5f6cb | ||
|
|
0a91d60677 | ||
|
|
5cd8f5681e | ||
|
|
b2babd9502 | ||
|
|
10faaaf531 | ||
|
|
7642c5a50d | ||
|
|
42dba3170d | ||
|
|
5c90a742ba | ||
|
|
9e57ef074d | ||
|
|
9e9b1c3c98 | ||
|
|
13d5deddec | ||
|
|
8522760c70 | ||
|
|
5bc2282ac5 | ||
|
|
2944b2a8f6 | ||
|
|
264480b9bc | ||
|
|
7d52cc46dc | ||
|
|
37c9fd278c | ||
|
|
3d6fe0a565 | ||
|
|
da488f76d2 | ||
|
|
6d28de77d1 | ||
|
|
a8bd46c521 | ||
|
|
1ec3a74d4c | ||
|
|
076449a33d | ||
|
|
1b5cc2abf1 | ||
|
|
b176225910 | ||
|
|
082d1eea48 | ||
|
|
e99dfbae0d | ||
|
|
d6735bed4f | ||
|
|
b024d93fa0 | ||
|
|
dbe8554724 | ||
|
|
9f79e64678 | ||
|
|
f513cae66e | ||
|
|
37b807210e | ||
|
|
15e26ba929 | ||
|
|
db1795981f | ||
|
|
2a40baf509 | ||
|
|
2dbf72e452 | ||
|
|
67ae716c60 | ||
|
|
8dfb130d19 | ||
|
|
2a42fea32a | ||
|
|
f69edefddf | ||
|
|
6f3b5fc559 | ||
|
|
7275a34e1f | ||
|
|
687e2ecaaa | ||
|
|
64312aedae | ||
|
|
391876e557 | ||
|
|
cb95a7d418 | ||
|
|
830f806dee | ||
|
|
21c2316f6b | ||
|
|
930a1ae450 | ||
|
|
aec331b5a6 | ||
|
|
1db6c281b1 | ||
|
|
5b70ca81a7 | ||
|
|
d00df785a5 | ||
|
|
d0c6e7a285 | ||
|
|
058995a0b6 | ||
|
|
fd3be887b4 | ||
|
|
84802d9065 | ||
|
|
90d696d826 | ||
|
|
aeb9e2ad9f | ||
|
|
26ab1bfd4f | ||
|
|
23d44cb28b | ||
|
|
de4b352495 | ||
|
|
d635117194 | ||
|
|
c0412d368e | ||
|
|
7ec30f0796 | ||
|
|
9815a55221 | ||
|
|
7c108e5128 | ||
|
|
4873031c37 | ||
|
|
15d23870c1 | ||
|
|
2abbf7e762 | ||
|
|
1578aa7cb6 | ||
|
|
f2230751e7 | ||
|
|
8fc0dda26d | ||
|
|
0ab71badfa | ||
|
|
0f24d924f9 | ||
|
|
518a0de95f | ||
|
|
fa5648c0c3 | ||
|
|
bd620a764d | ||
|
|
cff42b5d27 | ||
|
|
25eef2a40e | ||
|
|
db276d5f1f | ||
|
|
ce0d906c35 | ||
|
|
520826abc0 | ||
|
|
5e1d6014d5 | ||
|
|
32af17317c | ||
|
|
3e7cc8a0a0 | ||
|
|
f717ce52fa | ||
|
|
d880f3c628 | ||
|
|
0a97717b35 | ||
|
|
029403ea10 | ||
|
|
e4ec65622f | ||
|
|
3ee3b97255 | ||
|
|
a3dd6ce891 | ||
|
|
0c97d7701e | ||
|
|
b852b6f55c | ||
|
|
d3b7b857a4 | ||
|
|
63ad3f9290 | ||
|
|
40cc30e054 | ||
|
|
23374e98ae | ||
|
|
40f544fffc | ||
|
|
4fc69c9a2c | ||
|
|
2e5a485804 | ||
|
|
2d10b5be90 | ||
|
|
b0d0bb7953 | ||
|
|
8c69b85280 | ||
|
|
9d6d1e4ff8 | ||
|
|
e71c72b4a2 | ||
|
|
bf4b29b3e3 | ||
|
|
8e0809ceba | ||
|
|
230af48899 | ||
|
|
9dc459192e | ||
|
|
8de2301c6b | ||
|
|
f66b343edd | ||
|
|
ff82d36e94 | ||
|
|
c3e937f5e0 | ||
|
|
25c5e823d9 | ||
|
|
528b8b1271 | ||
|
|
b88344a6c0 | ||
|
|
9e135cc81d | ||
|
|
72ef288357 | ||
|
|
426ac29ca5 | ||
|
|
ceb330e3e4 | ||
|
|
7ea8f741f2 | ||
|
|
443c9fba03 | ||
|
|
0227f79bd7 | ||
|
|
2812eaa13d | ||
|
|
cb293fcdb3 | ||
|
|
706428fe98 | ||
|
|
698f5f951e | ||
|
|
155c12f657 | ||
|
|
a36c731d15 | ||
|
|
6a793536dd | ||
|
|
27532a4237 | ||
|
|
467f69f50e | ||
|
|
32e724e744 | ||
|
|
5fb891c495 | ||
|
|
d56cefdc7a | ||
|
|
80c67d37c0 | ||
|
|
1e29d9463e | ||
|
|
75f6a53269 | ||
|
|
b5a8f3156c | ||
|
|
de245c08ce | ||
|
|
76a7f25083 | ||
|
|
d9a96fbb29 | ||
|
|
16617660c3 | ||
|
|
a83d9a075f | ||
|
|
eca3fb1e19 | ||
|
|
41bc799c3f | ||
|
|
44726c7411 | ||
|
|
8f7f9ec367 | ||
|
|
21de8f2284 | ||
|
|
15797a89e7 | ||
|
|
c340a1d47f | ||
|
|
71cec39bc4 | ||
|
|
882542318c | ||
|
|
7238ef4f55 | ||
|
|
1049d65621 | ||
|
|
552ae836e9 | ||
|
|
1852c0a88c | ||
|
|
d0d399bdfc | ||
|
|
3975fe0f02 | ||
|
|
0739cfc240 | ||
|
|
f31bf4f7c9 | ||
|
|
f413671915 | ||
|
|
e67a210e95 | ||
|
|
51dfa9a247 | ||
|
|
7c121b8ba6 | ||
|
|
ae92a5c25e | ||
|
|
fabd3b8421 | ||
|
|
94d754e63a | ||
|
|
f869542a47 | ||
|
|
2b616b688d | ||
|
|
0925ea6f9c | ||
|
|
87b5565679 | ||
|
|
adcecf969a | ||
|
|
f3831665d4 | ||
|
|
9db8d44654 | ||
|
|
e1075a3bbf | ||
|
|
3b6f499fc5 | ||
|
|
2c1fbe103b | ||
|
|
1543862f3b | ||
|
|
5b7b34a9f0 | ||
|
|
fb6271eecf | ||
|
|
031526b619 | ||
|
|
e42ca2dbf2 | ||
|
|
4f5816f42e | ||
|
|
555d0d3378 | ||
|
|
1495b756d3 | ||
|
|
7f78d00b97 | ||
|
|
bfd58b3cdf | ||
|
|
e260b9473a | ||
|
|
ccc56d446f | ||
|
|
31a4e38bc0 | ||
|
|
284366e6bb | ||
|
|
9c6dda9bd2 | ||
|
|
e5f6f8dfa7 | ||
|
|
0232117de5 | ||
|
|
96700fe3d1 | ||
|
|
367a4e17a2 | ||
|
|
eba0d7c1e0 | ||
|
|
341a4a0d60 | ||
|
|
d116424241 | ||
|
|
db881ee011 | ||
|
|
34bed44a9b | ||
|
|
d431aa4b59 | ||
|
|
2512bebc62 | ||
|
|
309dbd7585 | ||
|
|
a1f28caa8e | ||
|
|
4204393337 | ||
|
|
789e34702b | ||
|
|
47bc1bf88d | ||
|
|
9867227ccd | ||
|
|
26302d478e | ||
|
|
0decf94c9c | ||
|
|
0ae78efd43 | ||
|
|
b5af6b0bf9 | ||
|
|
3eabefd3d6 | ||
|
|
7ff6a6e0e7 | ||
|
|
94dc611024 | ||
|
|
0b28107432 | ||
|
|
fe717dad7f | ||
|
|
fb3816e2e5 | ||
|
|
535bf4c848 | ||
|
|
1fc60b0682 | ||
|
|
302f1ee8e3 | ||
|
|
42aec56b8e | ||
|
|
199c00c59b | ||
|
|
107bb14555 | ||
|
|
5820792013 | ||
|
|
0e20248494 | ||
|
|
c4f4b247bc | ||
|
|
0d66bc49c2 | ||
|
|
bdc78a826f | ||
|
|
6570062e61 | ||
|
|
820facb833 | ||
|
|
a933fcf7e2 | ||
|
|
2d3039c6a2 | ||
|
|
aca01044f4 | ||
|
|
dffd1b1d69 | ||
|
|
14fc64f2f1 | ||
|
|
04791d06d0 | ||
|
|
a485451328 | ||
|
|
c7d399c122 | ||
|
|
df155bed89 | ||
|
|
3ee3630d22 | ||
|
|
6b2911b8c8 | ||
|
|
d474200d7f | ||
|
|
cc6f6277f6 | ||
|
|
0521e988bc | ||
|
|
832ff39eb6 | ||
|
|
134798e82f | ||
|
|
b6f43966ea | ||
|
|
1dab26bcbc | ||
|
|
afa7be6fdc | ||
|
|
36290c20aa | ||
|
|
c33c7eb68e | ||
|
|
5da33afecd | ||
|
|
a49b49aaa9 | ||
|
|
225fd0d05b | ||
|
|
c43b4f9cf0 | ||
|
|
3581f4c87e | ||
|
|
7a4e0cc850 | ||
|
|
ba49da75de | ||
|
|
f329a5950e | ||
|
|
d95158003c | ||
|
|
1be6223a31 | ||
|
|
76178b423e | ||
|
|
195e537499 | ||
|
|
418abc2b0a | ||
|
|
277f0346f2 | ||
|
|
6148cbb122 | ||
|
|
3847bfc41a | ||
|
|
783424dd26 | ||
|
|
4c1861dd27 | ||
|
|
7f6486c77d | ||
|
|
1b2a50f9a3 | ||
|
|
5dadf92a62 | ||
|
|
76821addd7 | ||
|
|
a769c55c72 | ||
|
|
347ec91bfc | ||
|
|
4245767357 | ||
|
|
74d196ad50 | ||
|
|
3c4649748d | ||
|
|
22985900a8 | ||
|
|
86300a8e3c | ||
|
|
32ab2ae201 | ||
|
|
23e4a2e28e | ||
|
|
40515e62ac | ||
|
|
b56490650c | ||
|
|
b5751795b5 | ||
|
|
43d6151506 | ||
|
|
8ccfb606c0 | ||
|
|
3b74cd5676 | ||
|
|
26e766ee19 | ||
|
|
6f96dc8f23 | ||
|
|
1c3fdb73fb | ||
|
|
332be7edd6 | ||
|
|
76e0e935f0 | ||
|
|
7e6eb65950 | ||
|
|
b0a56a431b | ||
|
|
166316e0c5 | ||
|
|
4194e66d81 | ||
|
|
c9b9d86892 | ||
|
|
f2d00e6e42 | ||
|
|
acfc166a11 | ||
|
|
3255382132 | ||
|
|
25831bfb60 | ||
|
|
d011ca0626 | ||
|
|
5eadbe7ecd | ||
|
|
ea7e0c6204 | ||
|
|
2dcb126e6d | ||
|
|
438525e8ec | ||
|
|
eb66a403d9 | ||
|
|
26ac612474 | ||
|
|
2e28df3e8a | ||
|
|
43613e3b10 | ||
|
|
555dd2e726 | ||
|
|
3260f46543 | ||
|
|
f9de23402a | ||
|
|
9e155cf94a | ||
|
|
dcace43ce2 | ||
|
|
527e3d6cd0 | ||
|
|
befce5b887 | ||
|
|
9929c96650 | ||
|
|
c7d45c28cf | ||
|
|
87cc3fd714 | ||
|
|
282d58a6fe | ||
|
|
b4aec91d67 | ||
|
|
d2a149ee23 | ||
|
|
8a72f94b3d | ||
|
|
fc4ed1c50b | ||
|
|
ae1d0a18f3 | ||
|
|
9709ca331e | ||
|
|
b4a9641c31 | ||
|
|
cc68e6b6e6 | ||
|
|
60c47cfca1 | ||
|
|
720abd4e04 | ||
|
|
b4fe694d1b | ||
|
|
c117785554 | ||
|
|
a9d6a15924 | ||
|
|
e9723de6c3 | ||
|
|
b8c61b5236 | ||
|
|
da0cf01eee | ||
|
|
b77e1eb94b | ||
|
|
e40f266042 | ||
|
|
69fb15d3cb | ||
|
|
188956a4af | ||
|
|
c7dd4a7f2b | ||
|
|
111f1729c9 | ||
|
|
164372b46a | ||
|
|
1af3794fed | ||
|
|
885f4ded0c | ||
|
|
9bb018d8d9 | ||
|
|
58187e00e6 | ||
|
|
5110a8c636 | ||
|
|
ddbe883d47 | ||
|
|
945ac82b6a | ||
|
|
84148ad07a | ||
|
|
25553a28a1 | ||
|
|
c2f5846a8e | ||
|
|
3f98e9451a | ||
|
|
6c72194346 | ||
|
|
822dd24756 | ||
|
|
262ed38429 | ||
|
|
5ff66ce4dd | ||
|
|
ca73b8e0b8 | ||
|
|
2d49ace0e3 | ||
|
|
d52f22044e | ||
|
|
2bf052aac9 | ||
|
|
a79c9d5f4f | ||
|
|
6f9bcf1858 | ||
|
|
d1b00ba95d | ||
|
|
bb45bee7f8 | ||
|
|
246f0b21fd | ||
|
|
394443b6d1 | ||
|
|
fc1dd2daac | ||
|
|
ba1b6f321e | ||
|
|
06402cf1c6 | ||
|
|
dfc33389d8 | ||
|
|
4e83752655 | ||
|
|
7e28718681 | ||
|
|
bf452e922a | ||
|
|
4911a74cac | ||
|
|
a2bda5a4db | ||
|
|
800abc605f | ||
|
|
ac83b67632 | ||
|
|
00bc7dccb9 | ||
|
|
04aa9f0e61 | ||
|
|
353e97a4c1 | ||
|
|
7b28387bb4 | ||
|
|
1889fe23c5 | ||
|
|
9832fbe042 | ||
|
|
084070475d | ||
|
|
cb42ca8765 | ||
|
|
45d7e66488 | ||
|
|
93a1616933 | ||
|
|
9a87a2ff0d | ||
|
|
bdff05feaf | ||
|
|
937e3d0b94 | ||
|
|
8503aba583 | ||
|
|
bb8223d4ff | ||
|
|
af3c8f66f7 | ||
|
|
3097a1b17e | ||
|
|
54b80f74b2 | ||
|
|
9a78d28cd7 | ||
|
|
5df197e814 | ||
|
|
5fe09e9787 | ||
|
|
6be5360bdd | ||
|
|
2b9007958b | ||
|
|
2f9c126d34 | ||
|
|
1ed817932b | ||
|
|
0ccadb246f | ||
|
|
b67235a7e5 | ||
|
|
93853613bd | ||
|
|
1ecb4fedcc | ||
|
|
c8fdf3731a | ||
|
|
f8e766ffc0 | ||
|
|
050489dd80 | ||
|
|
2db966ba47 | ||
|
|
aa54d65f63 | ||
|
|
37dc17ae15 | ||
|
|
cd6b3d7dee | ||
|
|
aec0bc5029 | ||
|
|
e010e67717 | ||
|
|
44197d91c6 | ||
|
|
e3ee48b52e | ||
|
|
8ccf349458 | ||
|
|
2fce15db94 | ||
|
|
0cf50e8000 | ||
|
|
5b9630cf46 | ||
|
|
7dcbb409a9 | ||
|
|
286e057fae | ||
|
|
7d4fe341f1 | ||
|
|
76acb7bb58 | ||
|
|
5ef7cd7bdd | ||
|
|
ebb4628e66 | ||
|
|
0928060c75 | ||
|
|
d8f9a9a03c | ||
|
|
abfb8bbc34 | ||
|
|
82e01f7b17 | ||
|
|
a97609e920 | ||
|
|
f1e1bd41b9 | ||
|
|
57d35858d8 | ||
|
|
0b956c66c1 | ||
|
|
b1dfa1db61 | ||
|
|
cb579ecc62 | ||
|
|
ae235aa58c | ||
|
|
6302056182 | ||
|
|
5e20abd7f1 | ||
|
|
72a72dbc88 | ||
|
|
4766666913 | ||
|
|
fa6070c680 | ||
|
|
6267d11e51 | ||
|
|
822298ab66 | ||
|
|
2ad3ab7f0d | ||
|
|
f437fd6cd6 | ||
|
|
42b247756a | ||
|
|
fbbf2d5eb3 | ||
|
|
788a276616 | ||
|
|
196779ffb6 | ||
|
|
59a144baed | ||
|
|
ad825b80b0 | ||
|
|
45a0061660 | ||
|
|
8831c76fb4 | ||
|
|
66616e1cab | ||
|
|
fa281a0df0 | ||
|
|
d48a96599f | ||
|
|
7de7ef8e8c | ||
|
|
406a254523 | ||
|
|
eabd6f60ef | ||
|
|
e416d06aa9 | ||
|
|
65e073e6b8 | ||
|
|
45ac0d5dc6 | ||
|
|
c9ef034ab8 | ||
|
|
c89f38f4f8 | ||
|
|
858b57d77a | ||
|
|
9b87892036 | ||
|
|
ed69692f08 | ||
|
|
7119999df8 | ||
|
|
c16462a0ce | ||
|
|
272f96b405 | ||
|
|
3e519001a3 | ||
|
|
f442aad962 | ||
|
|
fa9b151c36 | ||
|
|
481bd6f57a | ||
|
|
f015227fc8 | ||
|
|
61ba16b779 | ||
|
|
f6e86bfcf8 | ||
|
|
498dd76730 | ||
|
|
ebd909dfe7 | ||
|
|
067ed1258b | ||
|
|
ef3c72a24f | ||
|
|
b257f476c9 | ||
|
|
d3af2be79a | ||
|
|
94aeb19245 | ||
|
|
e4ee392c27 | ||
|
|
cf48ad06ed | ||
|
|
63df85f7f9 | ||
|
|
b29611509e | ||
|
|
dd1cf0ec56 | ||
|
|
c04c73bbd7 | ||
|
|
0d287283d4 | ||
|
|
4ea5723b7f | ||
|
|
ea964124d6 | ||
|
|
75244853c1 | ||
|
|
c361c34b16 | ||
|
|
264b3a785b | ||
|
|
bb15e1d8ac | ||
|
|
68860ae866 | ||
|
|
61637f12c7 | ||
|
|
7538649e81 | ||
|
|
31efbe915e | ||
|
|
9e69627626 | ||
|
|
dd9da2853a | ||
|
|
b4f08fa8d5 | ||
|
|
670461c66f | ||
|
|
f6092b9128 | ||
|
|
a690cb36ff | ||
|
|
5ca8f4a3aa | ||
|
|
ba6e2f0a54 | ||
|
|
149f4e38a0 | ||
|
|
b5ee170726 | ||
|
|
04961880fe | ||
|
|
e22e8ffa0e | ||
|
|
d18d800947 | ||
|
|
3ca58ee65f | ||
|
|
21387b9a83 | ||
|
|
ccb0d491ed | ||
|
|
daa3200713 | ||
|
|
fec2d1f7ee | ||
|
|
eb8e95723c | ||
|
|
999bec19f1 | ||
|
|
dfb8de2349 | ||
|
|
59f1cdcc82 | ||
|
|
3eb8be6239 | ||
|
|
7dfc4a651d | ||
|
|
189d145393 | ||
|
|
26bec11d76 | ||
|
|
1e7071bff3 | ||
|
|
92390d1d59 | ||
|
|
b9b072119b | ||
|
|
b6d9fd8030 | ||
|
|
d383bc93c7 | ||
|
|
af88c68fca | ||
|
|
24537c4fdc | ||
|
|
130f495fb6 | ||
|
|
9d22a06969 | ||
|
|
e3783e0236 | ||
|
|
a5161eb7f1 | ||
|
|
82e448de7c | ||
|
|
680afe972e | ||
|
|
69ab2ce402 | ||
|
|
4374930065 | ||
|
|
9f3520cba5 | ||
|
|
d20cdc099d | ||
|
|
926e9ff3de | ||
|
|
70c277d8a1 | ||
|
|
0a5aac724a | ||
|
|
bb91d5495f | ||
|
|
995d94c124 | ||
|
|
ee7036f13d | ||
|
|
5a8a8dc292 | ||
|
|
7d84de6690 | ||
|
|
3520f9e9c2 | ||
|
|
b2df7981a9 | ||
|
|
9831e7bc3b | ||
|
|
90e37439e5 | ||
|
|
65f2490e89 | ||
|
|
c14a921e5a | ||
|
|
2bc8ffefce | ||
|
|
b88d8c85cb | ||
|
|
693f52d984 | ||
|
|
56a3bff42e | ||
|
|
bffeec1615 | ||
|
|
e4a9f115cb | ||
|
|
a717238f76 | ||
|
|
4a567ab97c | ||
|
|
90431b662b | ||
|
|
1c8f349a62 | ||
|
|
217004c70c | ||
|
|
a75608628e | ||
|
|
33130f39ee | ||
|
|
cae43e97cd | ||
|
|
cdfc0f6b71 | ||
|
|
bf6a18a414 | ||
|
|
16b66afa7a | ||
|
|
9e446210fb | ||
|
|
b0118d2d57 | ||
|
|
bc90309dd6 | ||
|
|
52c4093fb0 | ||
|
|
b47c12cbee | ||
|
|
fb14bc6016 | ||
|
|
cd6862b1a7 | ||
|
|
3d9160f2fa | ||
|
|
157a5d9902 | ||
|
|
f4972e2be2 | ||
|
|
85c8e6ba42 | ||
|
|
8e79bd8785 | ||
|
|
1ace7ce254 | ||
|
|
a00be5b60c | ||
|
|
aabe8d1d5e | ||
|
|
3da086438b | ||
|
|
c257c86387 | ||
|
|
ff3ae14c29 | ||
|
|
cd82a34392 | ||
|
|
baacd55910 | ||
|
|
511b7c4d92 | ||
|
|
f38851b7c6 | ||
|
|
432c5837f0 | ||
|
|
cc599f544a | ||
|
|
e886c85134 | ||
|
|
05eb716b85 | ||
|
|
61eb7b0a6a | ||
|
|
f0dade5856 | ||
|
|
8567f6b13c | ||
|
|
b740316918 | ||
|
|
2119d08543 | ||
|
|
599ff66522 | ||
|
|
6d2eb04ada | ||
|
|
0aea75edb7 | ||
|
|
2ca8231ab4 | ||
|
|
e00116551c | ||
|
|
4d55e05b07 | ||
|
|
9ff3f85432 | ||
|
|
65c584aeda | ||
|
|
2540a2dfd9 | ||
|
|
70e9187bcb | ||
|
|
5e2fa13471 | ||
|
|
4f8610f895 | ||
|
|
95f9e97af8 | ||
|
|
695b073080 | ||
|
|
3a28935bfe | ||
|
|
468ed1a9ce | ||
|
|
a6052d7900 | ||
|
|
e42836b07f | ||
|
|
16373919d4 | ||
|
|
92f9c908f6 | ||
|
|
480e1e17c8 | ||
|
|
274f5f2f1f | ||
|
|
02d4f9cbba | ||
|
|
d10daf0991 | ||
|
|
8c9656b799 | ||
|
|
396ea3d0ee | ||
|
|
3efee4a855 | ||
|
|
ece6759fa7 | ||
|
|
246d945f39 | ||
|
|
4a3bc486d0 | ||
|
|
996d041581 | ||
|
|
d48e4adcd4 | ||
|
|
7f84b7ab83 | ||
|
|
3e26bd6a17 | ||
|
|
e964c62907 | ||
|
|
fbb4965263 | ||
|
|
88139d95a7 | ||
|
|
5875b1988b | ||
|
|
2a2091595b | ||
|
|
d77883dd7a | ||
|
|
adea7dadec | ||
|
|
cf4ce2dc08 | ||
|
|
e74dfb2ba6 | ||
|
|
aaea9ff018 | ||
|
|
206b88eba2 | ||
|
|
8f5373f034 | ||
|
|
dfa0a16487 | ||
|
|
8b9f6f49ef | ||
|
|
bcac978810 | ||
|
|
d17a41f7f1 | ||
|
|
503f71f004 | ||
|
|
4543288ea7 | ||
|
|
9307105b3f | ||
|
|
567daf9946 | ||
|
|
558b22c36a | ||
|
|
f076dddfe3 | ||
|
|
af44730418 | ||
|
|
4ec1140cb4 | ||
|
|
2a5d20058f | ||
|
|
51f38e0a76 | ||
|
|
54eb42d658 | ||
|
|
a7a6f4cec6 | ||
|
|
3968a8e0dc | ||
|
|
9e8ff27a7f | ||
|
|
7dd3d2b040 | ||
|
|
13678cb8d5 | ||
|
|
890aa6f9ac | ||
|
|
646cf44b83 | ||
|
|
bf789ca97b | ||
|
|
9b90ff10f4 | ||
|
|
0cc719a823 | ||
|
|
4cd026dfe9 | ||
|
|
3120b49dba | ||
|
|
fb1999c474 | ||
|
|
2c37961f7b | ||
|
|
1abc863f82 | ||
|
|
c9be03c0bc | ||
|
|
fd89209233 | ||
|
|
2e362fbb64 | ||
|
|
11b687cdc2 | ||
|
|
747c920420 | ||
|
|
cac51737cb | ||
|
|
7789379914 | ||
|
|
08e2bfe9a2 | ||
|
|
56a854ec88 | ||
|
|
de4ff4e58d | ||
|
|
991c2afedb | ||
|
|
cded92662f | ||
|
|
2c3fa0fd8f | ||
|
|
ec08432f92 | ||
|
|
1bab4d6937 | ||
|
|
d1fed11d0d | ||
|
|
e96053e268 | ||
|
|
9b98cbb894 | ||
|
|
349578fb6e | ||
|
|
7c186e4fcc | ||
|
|
a4fcbb9f67 | ||
|
|
763b64cc57 | ||
|
|
d57e6c5bf2 | ||
|
|
80711cc00a | ||
|
|
8b61b9ebfe | ||
|
|
e00e778bce | ||
|
|
81e70925c4 | ||
|
|
ddd32bd600 | ||
|
|
2713573b9b | ||
|
|
cf2e8bbc0b | ||
|
|
bc56c1a0e1 | ||
|
|
bfbf7af411 | ||
|
|
c151221d12 | ||
|
|
b8489724ef | ||
|
|
7a2f556682 | ||
|
|
92d883db87 | ||
|
|
cb931d7af0 | ||
|
|
6b54b57cb9 | ||
|
|
1ca4348ca0 | ||
|
|
3ca7006e3a | ||
|
|
339d2a7bf3 | ||
|
|
0d3debf9b9 | ||
|
|
7f74a4f4b5 | ||
|
|
220718cb58 | ||
|
|
be2acbd3eb | ||
|
|
9249c74b7b | ||
|
|
2b87817ba2 | ||
|
|
883c0f9dfe | ||
|
|
3ffc58d442 | ||
|
|
1bb4b0156f | ||
|
|
92d2be3f5e | ||
|
|
a7cded21f7 | ||
|
|
74156b7ed8 | ||
|
|
821f320347 | ||
|
|
eee9274098 | ||
|
|
8dd1f89225 | ||
|
|
f2459ea904 | ||
|
|
e51f7bfbff | ||
|
|
e2215ced34 | ||
|
|
b14c8e82a0 | ||
|
|
e7d50d2614 | ||
|
|
bca92883d2 | ||
|
|
ed865e38de | ||
|
|
dec45109d3 | ||
|
|
48c267c5c1 | ||
|
|
c041d39cab | ||
|
|
52c18e77ae | ||
|
|
52a189cdd2 | ||
|
|
dca8a47da8 | ||
|
|
6be9317fd7 | ||
|
|
bee99ca8d0 | ||
|
|
48dc89cf13 | ||
|
|
fc7f609234 | ||
|
|
0bed9b3c2e | ||
|
|
31fa73518b | ||
|
|
c6fd11157a | ||
|
|
ca73a57de7 | ||
|
|
38a545af92 | ||
|
|
b91b340afd | ||
|
|
8e7b1c97df | ||
|
|
c9c55fe0c9 | ||
|
|
d553160d65 | ||
|
|
b8a4df2f50 | ||
|
|
36edbf6ea9 | ||
|
|
f966535ea9 | ||
|
|
07cc26a144 | ||
|
|
178c4d15b7 | ||
|
|
b0b68c695c | ||
|
|
219a3658d6 | ||
|
|
d2310fc2ea | ||
|
|
3fe0ccd934 | ||
|
|
f902ebadcc | ||
|
|
57323af167 | ||
|
|
20b0b5fc8e | ||
|
|
bb72b8cc93 | ||
|
|
fec5516da9 | ||
|
|
ec9a092751 | ||
|
|
f78f8b6b12 | ||
|
|
f97b79bc16 | ||
|
|
42c8a77755 | ||
|
|
c692cc6a70 | ||
|
|
2b8d8d4e9c | ||
|
|
2ae8032ace | ||
|
|
c4416584de | ||
|
|
86d8b49113 | ||
|
|
7a5d870f67 | ||
|
|
b43459232a | ||
|
|
e2b4e60c9e | ||
|
|
17543535e3 | ||
|
|
901ce23cd2 | ||
|
|
1b52d8065e | ||
|
|
c6db901051 | ||
|
|
39edb1ad37 | ||
|
|
a397aa188c | ||
|
|
dd08751f3f | ||
|
|
ef6a9184ba | ||
|
|
575fb6fc60 | ||
|
|
4147d6c67e | ||
|
|
115c17ab90 | ||
|
|
d892c7290c | ||
|
|
63d8a902d5 | ||
|
|
43152fcf19 | ||
|
|
1f135f1fa5 | ||
|
|
c71d378eab | ||
|
|
bb82bf762f | ||
|
|
6fa5689aaf | ||
|
|
d78bef72ea | ||
|
|
e410de9552 | ||
|
|
8dc262b415 | ||
|
|
7fa494815f | ||
|
|
929bb70e5a | ||
|
|
36fb262fa0 | ||
|
|
b8cc783583 | ||
|
|
67b3d383e4 | ||
|
|
9144ccac6b | ||
|
|
04020d5ae2 | ||
|
|
de4f1d09af | ||
|
|
d16f9efeec | ||
|
|
8ba5dfe447 | ||
|
|
27d7f5f190 | ||
|
|
c43c4c42c8 | ||
|
|
3759071449 | ||
|
|
613283f656 | ||
|
|
506b6b51ce | ||
|
|
c7eccfb714 | ||
|
|
85d59945a0 | ||
|
|
98be19b29f | ||
|
|
4c8b490c89 | ||
|
|
8dd1745149 | ||
|
|
66406d86c1 | ||
|
|
35832b07b9 | ||
|
|
357683cbb9 | ||
|
|
15bc6159f9 | ||
|
|
7cdafa76a5 | ||
|
|
e84b0c91f8 | ||
|
|
cecffee3d3 | ||
|
|
d039a40d7c | ||
|
|
90463d8613 | ||
|
|
90c965a6ba | ||
|
|
e961691645 | ||
|
|
6270d2408f | ||
|
|
7ef1842237 | ||
|
|
faf616cbea | ||
|
|
4a09e18d9a | ||
|
|
983aca515d | ||
|
|
eb184d3c68 | ||
|
|
187ecf511f | ||
|
|
0ab35a3ca3 | ||
|
|
8e208bc76d | ||
|
|
1b4d92007e | ||
|
|
4101d7664d | ||
|
|
e128c3094a | ||
|
|
95869eeb70 | ||
|
|
d977bbd61c | ||
|
|
a16a6211e2 | ||
|
|
a75013e43e | ||
|
|
1e157e95b6 | ||
|
|
04dbfcd0e6 | ||
|
|
73440e2287 | ||
|
|
ee4459691f | ||
|
|
379c6045b9 | ||
|
|
00d0ddb4b5 | ||
|
|
be08c01be6 | ||
|
|
fb6abe34df | ||
|
|
b0d107f916 | ||
|
|
6d1ff7c3f3 | ||
|
|
6f67132f4b | ||
|
|
859d21162c | ||
|
|
c739a7ea2f | ||
|
|
7e48875fd4 | ||
|
|
474e86306f | ||
|
|
b436a660f1 | ||
|
|
d36460e30b | ||
|
|
0e236f9d88 | ||
|
|
72eb61951b | ||
|
|
22b2503839 | ||
|
|
d7b5719dfd | ||
|
|
4322d195d3 | ||
|
|
4ecad65926 | ||
|
|
f12025b86e | ||
|
|
5cfea54b06 | ||
|
|
c24b75953a | ||
|
|
f3d52f4932 | ||
|
|
ea6d339b9c | ||
|
|
284a9999b3 | ||
|
|
b241872a00 | ||
|
|
b7034d3213 | ||
|
|
0a0ced3e8e | ||
|
|
e7449b4d56 | ||
|
|
fab34c9e49 | ||
|
|
7df08c2120 | ||
|
|
b94e0ef797 | ||
|
|
00fed4f995 | ||
|
|
e8a7802e94 | ||
|
|
bfbc1ebb13 | ||
|
|
a802bb2736 | ||
|
|
3c5838cafd | ||
|
|
9217e8336d | ||
|
|
13cfc2d7bc | ||
|
|
171cebed1c | ||
|
|
1dcef87e1c | ||
|
|
23fb602a33 | ||
|
|
a8a549e8fc | ||
|
|
44790e99ea | ||
|
|
a3f6717c59 | ||
|
|
9587480e29 | ||
|
|
c51dbae8b6 | ||
|
|
8619e8fc0b | ||
|
|
10dace6de6 | ||
|
|
f6e843b5e2 | ||
|
|
a7bad5df61 | ||
|
|
f50c9e855c | ||
|
|
3653666ffe | ||
|
|
2123e43490 | ||
|
|
19146d8012 | ||
|
|
987dd36401 | ||
|
|
6bb158786b | ||
|
|
f66d091717 | ||
|
|
0a3d911602 | ||
|
|
e8edb99bb0 | ||
|
|
917cd96a3d | ||
|
|
20ad6d239d | ||
|
|
eea3968bfb | ||
|
|
7ac8f6dd19 | ||
|
|
cafa78c3f3 | ||
|
|
e808f7655c | ||
|
|
4f28bf7ab4 | ||
|
|
9380b2b4e8 | ||
|
|
8caeddde15 | ||
|
|
b637398a9c | ||
|
|
2e5b19575f | ||
|
|
102a69996a | ||
|
|
37af48f3fb | ||
|
|
f38747aa28 | ||
|
|
88d05578a8 | ||
|
|
b24c8b3410 | ||
|
|
2c8dbb94fc | ||
|
|
3da2ec5112 | ||
|
|
c3545ef060 | ||
|
|
4308a7cf79 | ||
|
|
e629c90a71 | ||
|
|
e7f07f5bfc | ||
|
|
da7972c119 | ||
|
|
efc476e50b | ||
|
|
33f4e8cf73 | ||
|
|
643f300792 | ||
|
|
bd601ad2da | ||
|
|
9f1a7a1c20 | ||
|
|
bd721c1310 | ||
|
|
98138a02e3 | ||
|
|
f7004e7a7c | ||
|
|
1db25d4b20 | ||
|
|
507e198b70 | ||
|
|
5c721ee435 | ||
|
|
9a44f0c869 | ||
|
|
8c5f9b4501 | ||
|
|
713bf66a12 | ||
|
|
78e9f5bd1a | ||
|
|
f03abf2600 | ||
|
|
d5eee5b56c | ||
|
|
8899684092 | ||
|
|
8e30e4925c | ||
|
|
906c7a8ea1 | ||
|
|
324fc149b3 | ||
|
|
29b1f2afae | ||
|
|
87fa14b281 | ||
|
|
b6db86da50 | ||
|
|
043947fdcb | ||
|
|
713509527a | ||
|
|
ae7eedf523 | ||
|
|
0a1bd748d7 | ||
|
|
1b351b7743 | ||
|
|
e409bd298a | ||
|
|
caef289b9b | ||
|
|
84628bd9fc | ||
|
|
43582b6319 | ||
|
|
c14c4425a4 | ||
|
|
c47d6cffa8 | ||
|
|
f104fb0586 | ||
|
|
75e410e4b4 | ||
|
|
740316ae2b | ||
|
|
b141ec7ea7 | ||
|
|
50875d5759 | ||
|
|
61b5e5ad7e | ||
|
|
6403d18d15 | ||
|
|
77f04d913a | ||
|
|
c0a783198e | ||
|
|
d370f67f60 | ||
|
|
5f4199183c | ||
|
|
d097f8bf1e | ||
|
|
1e104bdd9e | ||
|
|
8143be29d9 | ||
|
|
1d8c9ac020 | ||
|
|
7fcc8be73c | ||
|
|
e5f9cfd5b6 | ||
|
|
edd487c1f4 | ||
|
|
71d84fecba | ||
|
|
d30e219d7b | ||
|
|
0cfda55b3a | ||
|
|
17d1ca82a6 | ||
|
|
81d2a67948 | ||
|
|
3bae3aa9aa | ||
|
|
7211e6b929 | ||
|
|
82af52a0bc | ||
|
|
e88d34ea19 | ||
|
|
e1d7d9511f | ||
|
|
22d7d2c1f6 | ||
|
|
2b10a0c513 | ||
|
|
6e59cfc66c | ||
|
|
51d8344515 | ||
|
|
535f285287 | ||
|
|
779fd78091 | ||
|
|
69a111ee35 | ||
|
|
7288e5a8fd | ||
|
|
ac57da8713 | ||
|
|
2000d02768 | ||
|
|
5ba1ebd525 | ||
|
|
92329a8dd0 | ||
|
|
21de073784 | ||
|
|
3d502a41c5 | ||
|
|
8570f9256d | ||
|
|
fe09c417ff | ||
|
|
ca891b0e70 | ||
|
|
af3ee35c50 | ||
|
|
ba0bc31525 | ||
|
|
3905cad68d | ||
|
|
30a029c19f | ||
|
|
7cac609c06 | ||
|
|
e1365f185c | ||
|
|
c2ac215d43 | ||
|
|
c47972d500 | ||
|
|
3c36030977 | ||
|
|
7b2836c63c | ||
|
|
38e2fc6812 | ||
|
|
b0037c75a3 | ||
|
|
d7f6cd944c | ||
|
|
0de928a674 | ||
|
|
5c542d60e5 | ||
|
|
b3a29188a2 | ||
|
|
8417589789 | ||
|
|
c982e022fe | ||
|
|
a4c3fd4493 | ||
|
|
7de35b4830 | ||
|
|
3841780fe6 | ||
|
|
2440d8e3d1 | ||
|
|
5012418ed2 | ||
|
|
a6ab4feca5 | ||
|
|
cd0471386d | ||
|
|
d6749d30ab | ||
|
|
37a4a4dcdf | ||
|
|
cebceb3ec3 | ||
|
|
426635871b | ||
|
|
701c90d18d | ||
|
|
d00a708177 | ||
|
|
1f32f96487 | ||
|
|
7642f8d702 | ||
|
|
e78bf240ca | ||
|
|
4fb44d98db | ||
|
|
113ee73609 | ||
|
|
64441b39ac | ||
|
|
b4e3cccf4b | ||
|
|
6a3ef2a2e4 | ||
|
|
1dd00c2ea9 | ||
|
|
5038e4396e | ||
|
|
bd025309fb | ||
|
|
1ae0dd574d | ||
|
|
7d5ce994ab | ||
|
|
78e0c3dcca | ||
|
|
edbb96bcd9 | ||
|
|
bdb1e839ed | ||
|
|
1880740561 | ||
|
|
503835d47e | ||
|
|
8d372bee67 | ||
|
|
f385af595a | ||
|
|
4cd6bafc15 | ||
|
|
dc700c41fb | ||
|
|
7cd9e8860d | ||
|
|
b6adbc926f | ||
|
|
c253464b2a | ||
|
|
6306cfff96 | ||
|
|
d86d88c7b7 | ||
|
|
5f606bb0b7 | ||
|
|
1daa97545b | ||
|
|
d0189b0719 | ||
|
|
222b92807e | ||
|
|
de90e5b753 | ||
|
|
ad4870a59b | ||
|
|
9a6c387b8e | ||
|
|
81b4a35076 | ||
|
|
411b3d0888 | ||
|
|
fc0dc4472b | ||
|
|
7824f9ee63 | ||
|
|
fb00bef05f | ||
|
|
d7bc6b1be7 | ||
|
|
79776ae8bc | ||
|
|
90b3f009af | ||
|
|
6f8deb83e3 | ||
|
|
da02a68e60 | ||
|
|
85fc8710dd | ||
|
|
a01f3e4efd | ||
|
|
cce54f3bb7 | ||
|
|
92037a480d | ||
|
|
cf6005ce2f | ||
|
|
a766ddbebc | ||
|
|
01acb08c83 | ||
|
|
023c259034 | ||
|
|
dfe4d70198 | ||
|
|
4d939cb778 | ||
|
|
660a809ec8 | ||
|
|
26825ed60f | ||
|
|
8bd8975e0a | ||
|
|
799d4f9465 | ||
|
|
47797bcf4a | ||
|
|
2847921e5a | ||
|
|
8402de601f | ||
|
|
4dec75c589 | ||
|
|
ac15b227da | ||
|
|
a1dc7a52e9 | ||
|
|
6ba3e25d33 | ||
|
|
51a2fa47c9 | ||
|
|
09bd6dba84 | ||
|
|
925bc3b3dd | ||
|
|
382bf1e94c | ||
|
|
575a7531c5 | ||
|
|
dce0c0b47e | ||
|
|
dc8230bf2e | ||
|
|
17428d58d9 | ||
|
|
6771bd84c6 | ||
|
|
ac10b3a5c7 | ||
|
|
56f2513aba | ||
|
|
9858a009da | ||
|
|
73b818cf78 | ||
|
|
4f8ffaee5b | ||
|
|
2cd40c7552 | ||
|
|
c9fd69ceb7 | ||
|
|
e4fbf70568 | ||
|
|
cf93d6bb65 | ||
|
|
228890aa19 | ||
|
|
ad8512e246 | ||
|
|
4480abc646 | ||
|
|
94243ac605 | ||
|
|
e8942c2968 | ||
|
|
8e03395b53 | ||
|
|
4f3de8646a | ||
|
|
6802f5a802 | ||
|
|
3a1ddce13f | ||
|
|
6c18857109 | ||
|
|
508b1e76c2 | ||
|
|
51d3268eed | ||
|
|
a0c90d4ba3 | ||
|
|
2c27240da6 | ||
|
|
787f5f06df | ||
|
|
7894389f1d | ||
|
|
da5f6315be | ||
|
|
42d1ab8a87 | ||
|
|
cf51892782 | ||
|
|
cdd4319991 | ||
|
|
3287eef0f1 | ||
|
|
c4a261f2d2 | ||
|
|
1867db3c4b | ||
|
|
9e23d52193 | ||
|
|
e29be737f7 | ||
|
|
3ac4122ec8 | ||
|
|
d56a79e016 | ||
|
|
77ace76e32 | ||
|
|
3270605b4f | ||
|
|
36e10539e0 | ||
|
|
37ba649930 | ||
|
|
586e72e797 | ||
|
|
38bd0d6bbb | ||
|
|
da0da4c75e | ||
|
|
bfe16bb254 | ||
|
|
2ada88a266 | ||
|
|
5b51ce9840 | ||
|
|
26793c8428 | ||
|
|
8719adef1e | ||
|
|
cd7bcb79e5 | ||
|
|
005419ab9a | ||
|
|
d28d2c57e4 | ||
|
|
64970772c7 | ||
|
|
6019b7bda4 | ||
|
|
5c792eb869 | ||
|
|
c6126db2a2 | ||
|
|
030833f942 | ||
|
|
daf67d9cf7 | ||
|
|
301ed25019 | ||
|
|
a7dbfc4954 | ||
|
|
af5e38e5ee | ||
|
|
102a28aef4 | ||
|
|
d3d8fdff13 | ||
|
|
9cb6f95420 | ||
|
|
89f334e67b | ||
|
|
b8b3d17570 | ||
|
|
053083f600 | ||
|
|
cf46564c67 | ||
|
|
59b038b8c4 | ||
|
|
fb6dfcd3fc | ||
|
|
1711ac9a88 | ||
|
|
e14c5bbd25 | ||
|
|
cf0f0d77be | ||
|
|
be7e7ac274 | ||
|
|
844bef9fda | ||
|
|
d56d41a8c2 | ||
|
|
b722c618bd | ||
|
|
720fae8720 | ||
|
|
8744b40dc5 | ||
|
|
d053bc562f | ||
|
|
cade1800f4 | ||
|
|
450a0180f8 | ||
|
|
ea363a8764 | ||
|
|
bbc8049832 | ||
|
|
66acd60406 | ||
|
|
6fd8a5a084 | ||
|
|
5927332a5f | ||
|
|
44a4b11d36 | ||
|
|
b8db47e528 | ||
|
|
f7652bd2ef | ||
|
|
e9f6509cb0 | ||
|
|
3c57e2e2b9 | ||
|
|
753ae51dd5 | ||
|
|
638178556f | ||
|
|
b44ed70112 | ||
|
|
3e494cc551 | ||
|
|
fa55896722 | ||
|
|
67a51be9ee | ||
|
|
2149bbb8ea | ||
|
|
d92a93f1d5 | ||
|
|
991fad02d7 | ||
|
|
7497e43fb9 | ||
|
|
1336de12a0 | ||
|
|
0f78bd3785 | ||
|
|
90b567c721 | ||
|
|
8176f1141e | ||
|
|
515827c745 | ||
|
|
1a2701561b | ||
|
|
c7d3ee0612 | ||
|
|
4be3cb4b0d | ||
|
|
db759bef46 | ||
|
|
3d47a8e9c0 | ||
|
|
91fe80334b | ||
|
|
7125d19af1 | ||
|
|
873e41dcfb | ||
|
|
420f858d9b | ||
|
|
e6f74751d4 | ||
|
|
5a4713950c | ||
|
|
c6691564a5 | ||
|
|
8acb44b202 | ||
|
|
72770e5809 | ||
|
|
968721a515 | ||
|
|
1de009a80d | ||
|
|
74b6a78a9b | ||
|
|
32fc6d4b7f | ||
|
|
d05b49496c | ||
|
|
52e5f467b0 | ||
|
|
4e0cf380be | ||
|
|
ebe9499e97 | ||
|
|
1cb3ab82c2 | ||
|
|
e0a954caa2 | ||
|
|
ecce3243de | ||
|
|
5e6532959b | ||
|
|
d8b7cfb36b | ||
|
|
b2837563c4 | ||
|
|
957948f906 | ||
|
|
7befafe7b7 | ||
|
|
436ce63e30 | ||
|
|
0268beb9c2 | ||
|
|
6a42e477aa | ||
|
|
22bc3dac2d | ||
|
|
4db4329ce3 | ||
|
|
924073424f | ||
|
|
9e828eccf6 | ||
|
|
3a8c99d43c | ||
|
|
acb17dc575 | ||
|
|
58987989da | ||
|
|
86e8ee334f | ||
|
|
998f2f9421 | ||
|
|
8694e7a6bf | ||
|
|
b46a6ed094 | ||
|
|
d0f53cb14a | ||
|
|
96d497a5cd | ||
|
|
a8785570df | ||
|
|
aa36e04250 | ||
|
|
821b7ed9af | ||
|
|
9270f80af2 | ||
|
|
eb8710df81 | ||
|
|
095099a085 | ||
|
|
ad424cac18 | ||
|
|
4e11806d00 | ||
|
|
f45082b764 | ||
|
|
cdca715afc | ||
|
|
d0a08a55d1 | ||
|
|
c5f3e76b21 | ||
|
|
f58ff68f3c | ||
|
|
a098aa0a89 | ||
|
|
7b3fc206f7 | ||
|
|
8f5d2a7f00 | ||
|
|
44c585e8c8 | ||
|
|
640f45f57d | ||
|
|
0ba8bace0d | ||
|
|
4202fb8cdc | ||
|
|
5f8b8835e1 | ||
|
|
78edcfe5cf | ||
|
|
2ca5e0c8b8 | ||
|
|
7ca1d99412 | ||
|
|
118bcbd6a6 | ||
|
|
987b442796 | ||
|
|
8ea77ccd04 | ||
|
|
004c2920f5 | ||
|
|
f02616acd1 | ||
|
|
69f9a484ca | ||
|
|
dde5367560 | ||
|
|
66da57f375 | ||
|
|
d328db543e | ||
|
|
c2c46f2843 | ||
|
|
96da48ae82 | ||
|
|
eb9eda1f6d | ||
|
|
f16558c126 | ||
|
|
1abf0fc134 | ||
|
|
b0ba1b2ecc | ||
|
|
a70717f2f7 | ||
|
|
5dec73f27c | ||
|
|
92ac87fa3f | ||
|
|
267239e3f2 | ||
|
|
52b863fe36 | ||
|
|
79337a4568 | ||
|
|
84750e2605 | ||
|
|
dfa1a3ad90 | ||
|
|
c1f9445e9d | ||
|
|
191db46c4f | ||
|
|
bc67bf6826 | ||
|
|
df534d30bf | ||
|
|
ce99dbe40c | ||
|
|
affcca8034 | ||
|
|
a6f31a577a | ||
|
|
9b814f4ed8 | ||
|
|
3091440162 | ||
|
|
3b2573afed | ||
|
|
1b64747c2b | ||
|
|
863212915c | ||
|
|
8ef82ebdd7 | ||
|
|
78aad924c5 | ||
|
|
24fda89665 | ||
|
|
59cb9ba344 | ||
|
|
d511eb19ef | ||
|
|
d3e9f51d6b | ||
|
|
e3499e5a70 | ||
|
|
026425407e | ||
|
|
18884111a6 | ||
|
|
3e5b152718 | ||
|
|
8e9637f6d4 | ||
|
|
d11ad39048 | ||
|
|
cb44d45eeb | ||
|
|
c15ffca48c | ||
|
|
902c11d074 | ||
|
|
35250677b9 | ||
|
|
1f3a1a0a95 | ||
|
|
38330b735c | ||
|
|
bda3c4ab7a | ||
|
|
d8d01ac353 | ||
|
|
51d935f419 | ||
|
|
c96f83b076 | ||
|
|
2fe4edb6df | ||
|
|
7308f5993c | ||
|
|
1e4f8be74b | ||
|
|
22ac12dc36 | ||
|
|
866296fefd | ||
|
|
9d574397bc | ||
|
|
b6e53b1a0c | ||
|
|
a46894f395 | ||
|
|
bee48dae7e | ||
|
|
e12f7b67e5 | ||
|
|
c7ceeb1833 | ||
|
|
12f6ed621e | ||
|
|
d7daada42e | ||
|
|
6c6849d8d7 | ||
|
|
e82336f355 | ||
|
|
1151b615a0 | ||
|
|
0cf891b9d9 | ||
|
|
aeb8fd6fda | ||
|
|
615f807909 | ||
|
|
985255afed | ||
|
|
9bec394d7f | ||
|
|
8f72e0ab83 | ||
|
|
6799a3bd35 | ||
|
|
588c7a81fc | ||
|
|
ce32ac19dd | ||
|
|
16aa0e4dba | ||
|
|
81cb765b7a | ||
|
|
d5523615ef | ||
|
|
ff5d899a92 | ||
|
|
d9f2c2b968 | ||
|
|
2f6adbd07c | ||
|
|
f0992d56c1 | ||
|
|
67bcf42125 | ||
|
|
935ddd12ec | ||
|
|
5a772f5410 | ||
|
|
698a32f364 | ||
|
|
bdea2acf67 | ||
|
|
377a11b160 | ||
|
|
9519c8edbd | ||
|
|
64fd207533 | ||
|
|
96e04f1c54 | ||
|
|
348c4b4431 | ||
|
|
bfbff723ac | ||
|
|
03dba4d7d0 | ||
|
|
febd7cbca8 | ||
|
|
e2b7b228c5 | ||
|
|
8b58d96aea | ||
|
|
0a2e01a616 | ||
|
|
a8908238d5 | ||
|
|
569138ac80 | ||
|
|
cf3faa1170 | ||
|
|
ae50374d55 | ||
|
|
d2705f033d | ||
|
|
5d12e2291c | ||
|
|
cd46d84dcb | ||
|
|
a3b0b8b3d0 | ||
|
|
4c60a70c6f | ||
|
|
a75b3a53aa | ||
|
|
94a79b57e9 | ||
|
|
dbe90eb3f5 | ||
|
|
3891348c26 | ||
|
|
84106d6282 | ||
|
|
37395931bf | ||
|
|
88e487be8e | ||
|
|
fd4c0e795a | ||
|
|
c79eb43347 | ||
|
|
860e74bffa | ||
|
|
4c759083be | ||
|
|
d3bb079be4 | ||
|
|
ed374684a6 | ||
|
|
169a46c38c | ||
|
|
1dbea434a3 | ||
|
|
a5b4d7a244 | ||
|
|
7ebb58b1fa | ||
|
|
ac5b3110f2 | ||
|
|
48259b4ffe | ||
|
|
041426fecb | ||
|
|
2caf7efdc6 | ||
|
|
b00e038b33 | ||
|
|
36503f0417 | ||
|
|
0daddf9896 | ||
|
|
f3364e1327 | ||
|
|
cf54115077 | ||
|
|
12a94c50b8 | ||
|
|
0dd12cf0a6 | ||
|
|
f92c336ae4 | ||
|
|
477c9ef577 | ||
|
|
e146a476bd | ||
|
|
45e74126da | ||
|
|
f32a39e79f | ||
|
|
489b144085 | ||
|
|
e7d311d23c | ||
|
|
4cc2dea2fe | ||
|
|
1e2cae7087 | ||
|
|
2b259f3704 | ||
|
|
0df5221784 | ||
|
|
4c78d35680 | ||
|
|
cdef8472e3 | ||
|
|
f9c933bf3b | ||
|
|
cc995ecef8 | ||
|
|
922a3631ae | ||
|
|
0903078618 | ||
|
|
8c759cb65a | ||
|
|
bd45c67528 | ||
|
|
562a206508 | ||
|
|
dd584e929f | ||
|
|
1828a2a81c | ||
|
|
a6746f77f0 | ||
|
|
184ff7a3b3 | ||
|
|
9db872db56 | ||
|
|
f1a1b89d13 | ||
|
|
14e0e96596 | ||
|
|
793c878c66 | ||
|
|
14887d5e88 | ||
|
|
366006273d | ||
|
|
6a69a726f2 | ||
|
|
e9cd4d95a7 | ||
|
|
d7e1dc1f95 | ||
|
|
19c8b4fe2d | ||
|
|
8b73a7375a | ||
|
|
974798f79c | ||
|
|
43c5de074f | ||
|
|
461cc1d5a8 | ||
|
|
3d6df07335 | ||
|
|
7689587879 | ||
|
|
e248b989e1 | ||
|
|
9920424a31 | ||
|
|
17be0a2c12 | ||
|
|
26fa38d052 | ||
|
|
34a4382920 | ||
|
|
e7c9b5a4f0 | ||
|
|
8c31904838 | ||
|
|
d522b608d3 | ||
|
|
3e7303c15c | ||
|
|
6e342983f9 | ||
|
|
a6d7ed3824 | ||
|
|
e003ec39fb | ||
|
|
62abc63f7a | ||
|
|
0955480f5b | ||
|
|
bf76affd06 | ||
|
|
b4d6525899 | ||
|
|
ba3555a66f | ||
|
|
d8be286831 | ||
|
|
65f5bc531c | ||
|
|
3ba4702181 | ||
|
|
ad9c42e832 | ||
|
|
0368d3be59 | ||
|
|
598549b5fb | ||
|
|
49086a3ae0 | ||
|
|
4676a7aa62 | ||
|
|
7d25b234d5 | ||
|
|
c6578870f0 | ||
|
|
4e8030bd81 | ||
|
|
094e130544 | ||
|
|
879fd5a13d | ||
|
|
c2bb49aca0 | ||
|
|
9ed14b6e8c | ||
|
|
4484721a9f | ||
|
|
4963a5797b | ||
|
|
13f5798ed4 | ||
|
|
db8e1dec3e | ||
|
|
c9bdecdb69 | ||
|
|
adb7726974 | ||
|
|
d6883d0c1c | ||
|
|
b2409ac708 | ||
|
|
cf189663a7 | ||
|
|
f0d36fd769 | ||
|
|
213e3e998a | ||
|
|
bef9c68f81 | ||
|
|
099d2fd03d | ||
|
|
9f71a6ab18 | ||
|
|
e088f7a552 | ||
|
|
2e6ce0481c | ||
|
|
23d39e5366 | ||
|
|
279664a578 | ||
|
|
2a3d128f3c | ||
|
|
9667365d9e | ||
|
|
d4ef26e0f3 | ||
|
|
a053e81797 | ||
|
|
1494bfe855 | ||
|
|
f1a4ba4e76 | ||
|
|
385c526414 | ||
|
|
5e95e02429 | ||
|
|
3081802b1c | ||
|
|
1663601dcb | ||
|
|
1a513c7bbf | ||
|
|
92cbdde429 | ||
|
|
0f6b1e4ae1 | ||
|
|
f915201a27 | ||
|
|
d1f76042e1 | ||
|
|
845aa733b7 | ||
|
|
a0e318827d | ||
|
|
39720a11dc | ||
|
|
da33bf3438 | ||
|
|
eda6f134a9 | ||
|
|
46e1c8ef73 | ||
|
|
f247139f1c | ||
|
|
566985cf72 | ||
|
|
70b4cdf520 | ||
|
|
081f211231 | ||
|
|
9cd8acab43 | ||
|
|
8b79a9708b | ||
|
|
969acbd47f | ||
|
|
31012671c5 | ||
|
|
e362b3b6aa | ||
|
|
d2ced93e58 | ||
|
|
958cd35e21 | ||
|
|
73e898773b | ||
|
|
46eae410c3 | ||
|
|
73a9e1c316 | ||
|
|
03862d4b6c | ||
|
|
557a2f08f7 | ||
|
|
ae90aa4ccc | ||
|
|
6eff2e6e74 | ||
|
|
955bb51f11 | ||
|
|
b583def913 | ||
|
|
dd85da053f | ||
|
|
ba6a01408d | ||
|
|
81d880fb4e | ||
|
|
bdbce67473 | ||
|
|
6079effae3 | ||
|
|
41aa1ee318 | ||
|
|
8d2fe315db | ||
|
|
22c3ac5be3 | ||
|
|
a517f0df1d | ||
|
|
c14f16bdf1 | ||
|
|
9c9854b6bf | ||
|
|
d280e16723 | ||
|
|
b93a9cb8bc | ||
|
|
8601052179 | ||
|
|
eaa20ff4bf | ||
|
|
e4c6e4bc48 | ||
|
|
c52597205e | ||
|
|
eee9de8271 | ||
|
|
c73033c0b4 | ||
|
|
aecfa3ff0f | ||
|
|
18f575bee4 | ||
|
|
522e752582 | ||
|
|
854ed8cfa9 | ||
|
|
4642205768 | ||
|
|
40dbfbe092 | ||
|
|
7882519c0e | ||
|
|
0234017ca1 | ||
|
|
6c315e5a9c | ||
|
|
ef0f91d8d0 | ||
|
|
9552784e72 | ||
|
|
ac20d7f302 | ||
|
|
f068327307 | ||
|
|
1bc698ae78 | ||
|
|
3f6691dcd5 | ||
|
|
31981b9080 | ||
|
|
167d29c39f | ||
|
|
28b78c2b27 | ||
|
|
b3d691fff6 | ||
|
|
8e5f2d91e8 | ||
|
|
cfa7f60e5f | ||
|
|
e0fcaa6103 | ||
|
|
1b2134d7a8 | ||
|
|
f922268af7 | ||
|
|
841a9f8082 | ||
|
|
4865ddb377 | ||
|
|
a556732e4f | ||
|
|
0ea31cb088 | ||
|
|
06fe7323eb | ||
|
|
d8d9259c36 | ||
|
|
261260b232 | ||
|
|
5b88894638 | ||
|
|
ddadaa828a | ||
|
|
a3cd10d83b | ||
|
|
b626c7620e | ||
|
|
5d90d8930e | ||
|
|
b01309c3bf | ||
|
|
2d1511b5dd | ||
|
|
e561304645 | ||
|
|
961d5591bd | ||
|
|
eca3f12fed | ||
|
|
a2c8796e04 | ||
|
|
ad301fd087 | ||
|
|
02b08939cd | ||
|
|
9b0d583f1b | ||
|
|
4a0a07f158 | ||
|
|
9c864c9759 | ||
|
|
f1dd1bd6bd | ||
|
|
85b1c309d1 | ||
|
|
6fe43b7b5c | ||
|
|
62fc986d96 | ||
|
|
03710f1fd2 | ||
|
|
2283c7a926 | ||
|
|
fb19ca8bf4 | ||
|
|
4ebfc1516c | ||
|
|
cc713e6c89 | ||
|
|
1be65adf02 | ||
|
|
25427ee60d | ||
|
|
c8eefbaa2e | ||
|
|
b339f426f2 | ||
|
|
9fd26fa574 | ||
|
|
be90bf0188 | ||
|
|
adcdaba199 | ||
|
|
17907589cc | ||
|
|
f333df181f | ||
|
|
61a01cb163 | ||
|
|
4ce557a829 | ||
|
|
4d10c9de95 | ||
|
|
5b84acebbc | ||
|
|
fc845dc936 | ||
|
|
7beae93441 | ||
|
|
c8f1297adb | ||
|
|
053a1d34e5 | ||
|
|
110a75ba0b | ||
|
|
826a31e57d | ||
|
|
23d7c3d0fe | ||
|
|
4d4a20e05e | ||
|
|
c03d4f115f | ||
|
|
ed90b638a9 | ||
|
|
c5252e06a7 | ||
|
|
8af445e72b | ||
|
|
94a0199955 | ||
|
|
44739c5198 | ||
|
|
5f871bc01f | ||
|
|
b382dd6fc0 | ||
|
|
2e84f82ed6 | ||
|
|
1f5971f15a | ||
|
|
694466a196 | ||
|
|
03311d3776 | ||
|
|
ae0eba866a | ||
|
|
05f92b74e7 | ||
|
|
41b6f3ffa7 | ||
|
|
c895b76864 | ||
|
|
906737bedb | ||
|
|
7138e891be | ||
|
|
53abe36b83 | ||
|
|
efbb7a034c | ||
|
|
d7dac57a07 | ||
|
|
05bc81bf4e | ||
|
|
59ce88f044 | ||
|
|
f8bc0bd2b5 | ||
|
|
7ebb184c8a | ||
|
|
bd44205b4e | ||
|
|
cf94cb1092 | ||
|
|
02d92e32c7 | ||
|
|
68087cdea5 | ||
|
|
7f92607b85 | ||
|
|
3be4b69b44 | ||
|
|
bb9afcb304 | ||
|
|
e9a05890a5 | ||
|
|
613809c2af | ||
|
|
7b969bb8c2 | ||
|
|
7aef551292 | ||
|
|
447b811fa0 | ||
|
|
5151aa677f | ||
|
|
a5ff34d423 | ||
|
|
435040814d | ||
|
|
9987416a4a | ||
|
|
31e33e0a8b | ||
|
|
03cfae45f8 | ||
|
|
4f83325e3c | ||
|
|
cd1765a0f3 | ||
|
|
5c744ad9aa | ||
|
|
b211d8b085 | ||
|
|
f8eece362f | ||
|
|
d4ea30e081 | ||
|
|
83416ee2e0 | ||
|
|
fa981a389f | ||
|
|
25915b5521 | ||
|
|
ed0dcb5c3d | ||
|
|
55817f31f9 | ||
|
|
a5b19bbc83 | ||
|
|
f6b44e03ac | ||
|
|
6d2ef4e0bf | ||
|
|
4d714cf9a4 | ||
|
|
930ded6767 | ||
|
|
4cdb18907f | ||
|
|
93bccb96b6 | ||
|
|
38bc618ee5 | ||
|
|
00b0193a43 | ||
|
|
e2db37d28a | ||
|
|
f9bce54104 | ||
|
|
7ee14724fc | ||
|
|
5988c2ac78 | ||
|
|
4eb0dbb5a4 | ||
|
|
8c5b3fe23e | ||
|
|
e35ad7551b | ||
|
|
6c943a7158 | ||
|
|
2da14e5b2a | ||
|
|
97f8eea302 | ||
|
|
04d5932252 | ||
|
|
84e38f765d | ||
|
|
b33c5798ee | ||
|
|
81012e60ff | ||
|
|
6180c2f948 | ||
|
|
52f0ae9350 | ||
|
|
fa0936da3c | ||
|
|
8a30c6347e | ||
|
|
09bcf34050 | ||
|
|
795c515999 | ||
|
|
00dbdc2267 | ||
|
|
32286888e5 | ||
|
|
565d8d8f04 | ||
|
|
0a5a0bef48 | ||
|
|
6e0e5c102e | ||
|
|
d31271fee3 | ||
|
|
be8a9ae73b | ||
|
|
afaa001738 | ||
|
|
22fbb0e35b | ||
|
|
026a53f3ff | ||
|
|
e17203ca1b | ||
|
|
599c7919ce | ||
|
|
778b601cd5 | ||
|
|
3170fa2208 | ||
|
|
07216db864 | ||
|
|
fec870f264 | ||
|
|
4518f089cc | ||
|
|
2c5eba335f | ||
|
|
fb19f1ecbc | ||
|
|
91dd03ba67 | ||
|
|
86092541ed | ||
|
|
e879fe5843 | ||
|
|
0ca7699fe5 | ||
|
|
7f6adfa331 | ||
|
|
5f0b102671 | ||
|
|
70a59daa6f | ||
|
|
d28eb6ae21 | ||
|
|
34865f797f | ||
|
|
eca980dfca | ||
|
|
742c792ec7 | ||
|
|
24a5f7a38e | ||
|
|
554949102b | ||
|
|
0f3b95802d | ||
|
|
868910fddf | ||
|
|
436542eff8 | ||
|
|
f4c05c67a2 | ||
|
|
6f7f67df5a | ||
|
|
50b6163e73 | ||
|
|
9b062f33c5 | ||
|
|
ea15c2245e | ||
|
|
beb539abaf | ||
|
|
6dd1d45a02 | ||
|
|
26ae6d3691 | ||
|
|
90ea01aa46 | ||
|
|
6c52105ac7 | ||
|
|
f97bc9dba8 | ||
|
|
3332375d7c | ||
|
|
fe6b1c13c4 | ||
|
|
5608425a12 | ||
|
|
f784b01d20 | ||
|
|
2648eba5bf | ||
|
|
255347ab77 | ||
|
|
52c36ef6a4 | ||
|
|
ed2e083d13 | ||
|
|
80f7c5ed9d | ||
|
|
8d33cbeaca | ||
|
|
b0b8bc7385 | ||
|
|
edbc8b28d5 | ||
|
|
da9bab16e1 | ||
|
|
75098d8b3e | ||
|
|
65bb7fd533 | ||
|
|
0259e78b2f | ||
|
|
99b8888a1b | ||
|
|
689f4ef606 | ||
|
|
b0c40e1e37 | ||
|
|
e54819e7e5 | ||
|
|
7eb029dcb6 | ||
|
|
3da70c3e8b | ||
|
|
f8c80283e4 | ||
|
|
72a6681ac1 | ||
|
|
1ab555b590 | ||
|
|
4602ec7688 | ||
|
|
5c39d8d12e | ||
|
|
97284fcf87 | ||
|
|
c8b1654e0a | ||
|
|
0400d0e0f0 | ||
|
|
4040f6bec6 | ||
|
|
a4a3aa045b | ||
|
|
a5a18903c7 | ||
|
|
6990986d9f | ||
|
|
06ded0098c | ||
|
|
f1fbea30f1 | ||
|
|
04f5bdb843 | ||
|
|
d29283ff21 | ||
|
|
35779dad8b | ||
|
|
ee0d6420a0 | ||
|
|
f66d847032 | ||
|
|
aba4dc7c50 | ||
|
|
99d0c5e2c6 | ||
|
|
fa55b94528 | ||
|
|
7fb46cf982 | ||
|
|
523752b263 | ||
|
|
7df4b8e8cc | ||
|
|
7a4ed5a337 | ||
|
|
ec37085e39 | ||
|
|
61e1e2729d | ||
|
|
ae8281f835 | ||
|
|
2efa68dd60 | ||
|
|
d2c92781c8 | ||
|
|
fa35293618 | ||
|
|
20ccca0aec | ||
|
|
10e216da6b | ||
|
|
6491353a57 | ||
|
|
1906523489 | ||
|
|
24beaa0d18 | ||
|
|
e1deb48121 | ||
|
|
b3891bacaa | ||
|
|
7939904ee3 | ||
|
|
7dcd69a2df | ||
|
|
9f44f989e5 | ||
|
|
002b2d0134 | ||
|
|
8b10ec93c2 | ||
|
|
cf9ecd1564 | ||
|
|
f31147248b | ||
|
|
2f9c540660 | ||
|
|
09489feff9 | ||
|
|
0d17a69ee4 | ||
|
|
c5755a4ec4 | ||
|
|
20bc3719a4 | ||
|
|
7dc41ab205 | ||
|
|
acc21e3b43 | ||
|
|
41a6726beb | ||
|
|
2f8ba75970 | ||
|
|
eb08129226 | ||
|
|
816823b115 | ||
|
|
b6059939b4 | ||
|
|
b8e23b2d7e | ||
|
|
a06846fa74 | ||
|
|
33f72e40da | ||
|
|
e8b2d80037 | ||
|
|
853463b7cd | ||
|
|
518e1a6405 | ||
|
|
afd376ddbc | ||
|
|
1ce325ee8a | ||
|
|
1e947a34a8 | ||
|
|
fe1e5aeebf | ||
|
|
8386b9a0b4 | ||
|
|
40323e3afe | ||
|
|
18868a5bd6 | ||
|
|
754682577c | ||
|
|
603dee7661 | ||
|
|
2a27cc46e2 | ||
|
|
a9a06a1e4e | ||
|
|
5f73725b8f | ||
|
|
324983024a | ||
|
|
7cb63b1da0 | ||
|
|
31603380c1 | ||
|
|
5a3d3b0324 | ||
|
|
78006855ee | ||
|
|
71520f6709 | ||
|
|
3f5a09229d | ||
|
|
5609771993 | ||
|
|
79b01cdc3c | ||
|
|
73f99b5e22 | ||
|
|
7d6f8eb495 | ||
|
|
eac5340b03 | ||
|
|
aef0243b73 | ||
|
|
736053e24e | ||
|
|
36b196dd93 | ||
|
|
549dcd32ca | ||
|
|
a59f56d852 | ||
|
|
2c14181051 | ||
|
|
c2b9e06a4a | ||
|
|
964978d45b | ||
|
|
599490bb6e | ||
|
|
a841e1fb2e | ||
|
|
296fe4b62e | ||
|
|
cb4c736fab | ||
|
|
118f02fd11 | ||
|
|
990d0f6e3e | ||
|
|
84624666ce | ||
|
|
42474e551f | ||
|
|
061e055d1b | ||
|
|
12f2caa8d6 | ||
|
|
8bd716c056 | ||
|
|
b890b5f0dc | ||
|
|
cd95abb2a1 | ||
|
|
c3df9e6270 | ||
|
|
6463fbee32 | ||
|
|
e43cdc6674 | ||
|
|
59216a9ae1 | ||
|
|
47a620bd36 | ||
|
|
37dbf4d8ec | ||
|
|
1270f2d577 | ||
|
|
80a067806d | ||
|
|
f20065f546 | ||
|
|
43d50e270a | ||
|
|
c27117e99e | ||
|
|
9c84f13425 | ||
|
|
b2cf7a16f2 | ||
|
|
28556790d6 | ||
|
|
41c9bb63a0 | ||
|
|
c2b2fafc9c | ||
|
|
5b88ba6d1d | ||
|
|
d036021a78 | ||
|
|
7d5e2466f0 | ||
|
|
d3f35955d6 | ||
|
|
fb338c0728 | ||
|
|
5462d1e9f8 | ||
|
|
2ce8f1ee5d | ||
|
|
3f0258e215 | ||
|
|
e72a8d999f | ||
|
|
fed16fd14e | ||
|
|
5dbc45ecb9 | ||
|
|
2f411dfc9c | ||
|
|
d6cff870e5 | ||
|
|
e1cd45c57e | ||
|
|
7572b6f66b | ||
|
|
e489b08bc1 | ||
|
|
094bdb29b6 | ||
|
|
712e05479b | ||
|
|
7c6c9eed36 | ||
|
|
63eee28a82 | ||
|
|
0777eaad10 | ||
|
|
9e6866c160 | ||
|
|
974dd8ce57 | ||
|
|
957d08f4c6 | ||
|
|
156b499f93 | ||
|
|
f31100326b | ||
|
|
9b7b1d6a61 | ||
|
|
7101ad81c4 | ||
|
|
8643263227 | ||
|
|
409c1b7584 | ||
|
|
eab132ed32 | ||
|
|
f45b5da71a | ||
|
|
f57b606f72 | ||
|
|
66bad2b6f8 | ||
|
|
44f9bc5f0e | ||
|
|
5c7e3c6bab | ||
|
|
46ec504743 | ||
|
|
737151ba09 | ||
|
|
2eef39d64a | ||
|
|
821f76bcd6 | ||
|
|
37ec058572 | ||
|
|
cadda12371 | ||
|
|
a643b56555 | ||
|
|
aa34df7a4c | ||
|
|
f7404085de | ||
|
|
33036e7599 | ||
|
|
f6e5b67f0d | ||
|
|
9547ac353d | ||
|
|
48339b19d4 | ||
|
|
0f8d464706 | ||
|
|
d6be7b4cae | ||
|
|
70aa01b261 | ||
|
|
11c7ffad4e | ||
|
|
1973424e05 | ||
|
|
16d97d3c63 | ||
|
|
3e6728fedb | ||
|
|
3e9aea072d | ||
|
|
fcfb73c755 | ||
|
|
12ce3282ff | ||
|
|
9f3e66fff0 | ||
|
|
624993fc89 | ||
|
|
ba8a00764a | ||
|
|
49f79a331c | ||
|
|
3dec4a6651 | ||
|
|
ac0a853030 | ||
|
|
e8ae4e76a3 | ||
|
|
c73b098f30 | ||
|
|
4292e26135 | ||
|
|
02fb2550d0 | ||
|
|
37bd525638 | ||
|
|
ea0f37f5b9 | ||
|
|
d09d7521d6 | ||
|
|
20d03cb817 | ||
|
|
97b3c3db7b | ||
|
|
b97b6dc144 | ||
|
|
b07bbab069 | ||
|
|
c8d5218c65 | ||
|
|
80a657965e | ||
|
|
b3324d22f5 | ||
|
|
295663f6a6 | ||
|
|
47caa2c3a0 | ||
|
|
4ddc422cfa | ||
|
|
856c8aa611 | ||
|
|
6f600f74af | ||
|
|
be8db602b8 | ||
|
|
7871e868a9 | ||
|
|
44467971f2 | ||
|
|
108fc4a0d8 | ||
|
|
31b7cdff9b | ||
|
|
0465298507 | ||
|
|
65ac8d4b2b | ||
|
|
dd59e3a831 | ||
|
|
1e98d56bb6 | ||
|
|
d31e4c7815 | ||
|
|
4af1f62aab | ||
|
|
bc403440ba | ||
|
|
38d8dfe5ab | ||
|
|
bee93f4743 | ||
|
|
9888510158 | ||
|
|
5fd4ed91d8 | ||
|
|
2a949cd8f1 | ||
|
|
312cbc86e9 | ||
|
|
85ded6e500 | ||
|
|
ce5a81d83c | ||
|
|
eb92c0bbf5 | ||
|
|
5453ae81a6 | ||
|
|
ff84f2b2f7 | ||
|
|
6df64d0d31 | ||
|
|
e9ac20a149 | ||
|
|
83753a5f81 | ||
|
|
7a228cac43 | ||
|
|
3161f5fa47 | ||
|
|
442741d246 | ||
|
|
5784092c1b | ||
|
|
d6ad089c60 | ||
|
|
89ff8b3fcb | ||
|
|
5773f141b8 | ||
|
|
446b4b084c | ||
|
|
f8da8f6929 | ||
|
|
f7de055f67 | ||
|
|
07d09aea85 | ||
|
|
f63ceed1ae | ||
|
|
b93d72296c | ||
|
|
d590e01a58 | ||
|
|
1bc15a8507 | ||
|
|
adeb9bccb1 | ||
|
|
5cf26af9f0 | ||
|
|
04c9646e05 | ||
|
|
67bf1a93e4 | ||
|
|
c263f11170 | ||
|
|
bb4c646714 | ||
|
|
b44ffffed8 | ||
|
|
bd521f858e | ||
|
|
f66fcfbe6d | ||
|
|
2bdaf53ecf | ||
|
|
46e7db6d94 | ||
|
|
10fc9fe268 | ||
|
|
73c75635b1 | ||
|
|
cc7d745ce6 | ||
|
|
7e1920dc4b | ||
|
|
a93f05c047 | ||
|
|
00238247cd | ||
|
|
b33e28835d | ||
|
|
044d830b64 | ||
|
|
38c84bb1f6 | ||
|
|
b50e066dee | ||
|
|
f55f803531 | ||
|
|
757e8eb57c | ||
|
|
a696afaeb8 | ||
|
|
8ca298b299 | ||
|
|
cde497c94e | ||
|
|
090e29effd | ||
|
|
94cf6424f5 | ||
|
|
b8013a57e2 | ||
|
|
1b23210902 | ||
|
|
0acca2e313 | ||
|
|
4bb30deca6 | ||
|
|
31bf615fe8 | ||
|
|
0d77027f60 | ||
|
|
bbf46358fa | ||
|
|
1cf793233f | ||
|
|
39b7b3ad53 | ||
|
|
7acc537c7a | ||
|
|
97c1a2245b | ||
|
|
8e9269fdf9 | ||
|
|
d9b2037b50 | ||
|
|
95827c3ada | ||
|
|
4e2bbc04fa | ||
|
|
61eff6e7e8 | ||
|
|
1d4d5f0c93 | ||
|
|
00f1b483eb | ||
|
|
939de6b177 | ||
|
|
c3d48acb4c | ||
|
|
ad4bc206ab | ||
|
|
8cfa5ba39e | ||
|
|
c2e6d1c6bf | ||
|
|
392bda7d8c | ||
|
|
5a658b7080 | ||
|
|
a6adb314ec | ||
|
|
5714578783 | ||
|
|
539125ff47 | ||
|
|
10cfcdab8c | ||
|
|
3f71d3b250 | ||
|
|
1b50fbab22 | ||
|
|
303fc65a6a | ||
|
|
445b6ee13f | ||
|
|
8afaac1e30 | ||
|
|
0327f9428e | ||
|
|
a5de66bbd5 | ||
|
|
d47157eec3 | ||
|
|
f4b47ed399 | ||
|
|
8b2145bd88 | ||
|
|
de454e8b78 | ||
|
|
6cd770b4c7 | ||
|
|
355525c248 | ||
|
|
53237c297f | ||
|
|
47d4e7381f | ||
|
|
5dac6690d7 | ||
|
|
2d8885cb0c | ||
|
|
b89f7180db | ||
|
|
4fdf85bbdb | ||
|
|
2ebb837a15 | ||
|
|
728d57d955 | ||
|
|
e53a2e7b43 | ||
|
|
e682fd07cb | ||
|
|
849aa64678 | ||
|
|
bddd22cfab | ||
|
|
cbb12e1b7c | ||
|
|
cc87ba4962 | ||
|
|
19310470b6 | ||
|
|
02fcccc8c7 | ||
|
|
fb2e556726 | ||
|
|
9f230ae339 | ||
|
|
3f0eb0a046 | ||
|
|
7d6d9eddc4 | ||
|
|
cf87da0ef3 | ||
|
|
0775acedc0 | ||
|
|
7b21c1bcbe | ||
|
|
9c295d1884 | ||
|
|
8f1cee2e61 | ||
|
|
caa9cc32d7 | ||
|
|
b750f827c5 | ||
|
|
aef53a8753 | ||
|
|
5d147163e5 | ||
|
|
75fe1a19eb | ||
|
|
5149078f6a | ||
|
|
4d95248f16 | ||
|
|
5c9405fffc | ||
|
|
368f52ade6 | ||
|
|
6457314794 | ||
|
|
84f4e3eedc | ||
|
|
8cc2ba4770 | ||
|
|
b003ed3f03 | ||
|
|
330da137db | ||
|
|
9e5d45d0de | ||
|
|
f40398bf42 | ||
|
|
b5c15d97fa | ||
|
|
783a21d88d | ||
|
|
6ddcba8917 | ||
|
|
91598cbbbf | ||
|
|
772c80aa85 | ||
|
|
62e7c861bd | ||
|
|
9d5b6eac55 | ||
|
|
72c5de6eae | ||
|
|
a28345d858 | ||
|
|
05b532b9eb | ||
|
|
0b0d18f182 | ||
|
|
1d833419aa | ||
|
|
c1b0877956 | ||
|
|
46b66c76ef | ||
|
|
d00b4335b5 | ||
|
|
7a129e6de1 | ||
|
|
e17e2a636b | ||
|
|
17c20276a9 | ||
|
|
dc9dedf220 | ||
|
|
3ac772badc | ||
|
|
22fc58d93b | ||
|
|
ccd3152b24 | ||
|
|
7d929dcde6 | ||
|
|
45e0cbdb25 | ||
|
|
dc9d48850f | ||
|
|
3a874bc8c7 | ||
|
|
8453cd82e9 | ||
|
|
9092549f07 | ||
|
|
a94b5ba0c0 | ||
|
|
5d1efd84a4 | ||
|
|
f62e56b7ec | ||
|
|
2ac90bbb96 | ||
|
|
f85f2d5d22 | ||
|
|
8af953e20d | ||
|
|
66132a912a | ||
|
|
a94269ceb9 | ||
|
|
b852cbdc80 | ||
|
|
350caee808 | ||
|
|
476ac263fb | ||
|
|
51a4f61a8f | ||
|
|
7d05236514 | ||
|
|
465b43be6a | ||
|
|
006edacd55 | ||
|
|
a9666a9f6e | ||
|
|
7de492caa7 | ||
|
|
93ee4716cc | ||
|
|
267f5159a3 | ||
|
|
8a60d4cf2f | ||
|
|
6489444158 | ||
|
|
59a66a3ea5 | ||
|
|
e17550e23e | ||
|
|
0f8d6daf99 | ||
|
|
08925a72c6 | ||
|
|
1101299168 | ||
|
|
22be8e2e9d | ||
|
|
6db2240f8a | ||
|
|
96422de031 | ||
|
|
54eac18eea | ||
|
|
f2043dc181 | ||
|
|
e416ec9279 | ||
|
|
5eb4ffb1cc | ||
|
|
284e748449 | ||
|
|
3111718eb1 | ||
|
|
fd8d17c5e5 | ||
|
|
109bb62209 | ||
|
|
cc661b26fa | ||
|
|
ea69957ed1 | ||
|
|
700f641e29 | ||
|
|
83f4eeb3b5 | ||
|
|
d1f4cdebf3 | ||
|
|
d95ba43fd1 | ||
|
|
8fae964ee8 | ||
|
|
baf49b88f4 | ||
|
|
7dc07f6d21 | ||
|
|
6472d35d91 | ||
|
|
95c87919a8 | ||
|
|
077b4d5c89 | ||
|
|
3577da05ac | ||
|
|
b8e8028eb9 | ||
|
|
a2105de402 | ||
|
|
d049e0a149 | ||
|
|
7463be862f | ||
|
|
a899666e68 | ||
|
|
10a52f8cf9 | ||
|
|
bc6652f443 | ||
|
|
72d04a0120 | ||
|
|
6dbed30008 | ||
|
|
c5eac298e6 | ||
|
|
bc18eda336 | ||
|
|
3cefb14297 | ||
|
|
4bc401278e | ||
|
|
56167f84ad | ||
|
|
d7e3765efe | ||
|
|
3d51d1e345 | ||
|
|
db4ecce20b | ||
|
|
95518f1948 | ||
|
|
bd23942893 | ||
|
|
c8610b8ad2 | ||
|
|
d0440122b9 | ||
|
|
d4beb2f79b | ||
|
|
9be995bb08 | ||
|
|
8d4636bbab | ||
|
|
f59c6e7a7c | ||
|
|
c24ab1b21d | ||
|
|
6a01658355 | ||
|
|
f1e2439e66 | ||
|
|
c1d47290b1 | ||
|
|
4d6116ed40 | ||
|
|
4d89ed701d | ||
|
|
18ba394901 | ||
|
|
3a83753611 | ||
|
|
e80594d61d | ||
|
|
c436b586d2 | ||
|
|
83c6f72eb0 | ||
|
|
e26299b998 | ||
|
|
f84e2c2ac7 | ||
|
|
4e8c94fd2d | ||
|
|
8f3fd37d47 | ||
|
|
a839809eb8 | ||
|
|
7960e9b309 | ||
|
|
88ceba59cf | ||
|
|
f368bbec32 | ||
|
|
021c4ba68a | ||
|
|
4875544888 | ||
|
|
849bdc52f6 | ||
|
|
54f4658dae | ||
|
|
63bb05b2d4 | ||
|
|
916ad0a58e | ||
|
|
dbc67e077d | ||
|
|
15055fa509 | ||
|
|
8ee704e123 | ||
|
|
0d552cd880 | ||
|
|
e968917dbc | ||
|
|
d8240bb683 | ||
|
|
b481c13829 | ||
|
|
60ce5c67de | ||
|
|
421f932053 | ||
|
|
542d6361f2 | ||
|
|
81bafa00ac | ||
|
|
84d0c17c4b | ||
|
|
77c0ba990d | ||
|
|
be7e2bed6f | ||
|
|
1d4487b6cd | ||
|
|
a7bfd0af41 | ||
|
|
55ef547a85 | ||
|
|
19129c8786 | ||
|
|
b5737ce9c1 | ||
|
|
f921d9a1f8 | ||
|
|
9609b48f2f | ||
|
|
ff8145b745 | ||
|
|
530e8b39e5 | ||
|
|
5926d396f9 | ||
|
|
50aeee288b | ||
|
|
72e001b0d5 | ||
|
|
f04c9d101e | ||
|
|
7cf16766c4 | ||
|
|
0e200e0c34 | ||
|
|
771a544d0f | ||
|
|
fba707f6a8 | ||
|
|
4e28e2cb59 | ||
|
|
2ecc53ba56 | ||
|
|
5c3c8b7b8a | ||
|
|
3eb1fe0eb2 | ||
|
|
aec998acc1 | ||
|
|
6a52a1dc63 | ||
|
|
91e758f66f | ||
|
|
441416b241 | ||
|
|
e541d8697e | ||
|
|
445825df44 | ||
|
|
b93c47fa60 | ||
|
|
4b817208aa | ||
|
|
90b831600f | ||
|
|
b3dd368920 | ||
|
|
c8db90b644 | ||
|
|
7ed65529df | ||
|
|
a8b59f5f59 | ||
|
|
eb966ec041 | ||
|
|
7fea0c3244 | ||
|
|
588789cb77 | ||
|
|
3640f977c8 | ||
|
|
28f12a4874 | ||
|
|
93378406c3 | ||
|
|
602d04af82 | ||
|
|
dcfbf2b154 | ||
|
|
bd13336256 | ||
|
|
815940913b | ||
|
|
f7191c0381 | ||
|
|
5c6d755750 | ||
|
|
3c545d488d | ||
|
|
bd0f84605f | ||
|
|
07d11c845c | ||
|
|
6e9a69be5c | ||
|
|
2e9bd477d9 | ||
|
|
392df6ba72 | ||
|
|
c66cffd6a6 | ||
|
|
b3b3e3ea20 | ||
|
|
b058fb8db4 | ||
|
|
9f0f6181a1 | ||
|
|
067a88b3e7 | ||
|
|
251d5f4135 | ||
|
|
f702e1a80d | ||
|
|
44e574f440 | ||
|
|
e1527fcbb9 | ||
|
|
9e583f9ff0 | ||
|
|
9ba7e5d567 | ||
|
|
02b6d53544 | ||
|
|
611816c8e3 | ||
|
|
0511a62ca1 | ||
|
|
31e34d6f19 | ||
|
|
5360fe49d4 | ||
|
|
bf94febb11 | ||
|
|
dbd3bd50df | ||
|
|
c1a1fb8d87 | ||
|
|
b10568e917 | ||
|
|
0e1ad39ede | ||
|
|
123bd0bb92 | ||
|
|
c8ce06d110 | ||
|
|
b0cfcb1999 | ||
|
|
6a8ed1192f | ||
|
|
0862859f93 | ||
|
|
3ad336a1eb | ||
|
|
a17f83cedd | ||
|
|
2c6850f6e4 | ||
|
|
5da47636cf | ||
|
|
944b857825 | ||
|
|
e04b965659 | ||
|
|
17d2fb80f2 | ||
|
|
34c8f33c3c | ||
|
|
07d74ee692 | ||
|
|
d28158bc74 | ||
|
|
14b7ec2a80 | ||
|
|
8c9fb956ff | ||
|
|
db632ae847 | ||
|
|
f27b31b581 | ||
|
|
8c44147a45 | ||
|
|
3bef4284e3 | ||
|
|
ec05215a5e | ||
|
|
5229b2959e | ||
|
|
8769234e28 | ||
|
|
bbdd1915eb | ||
|
|
5903e8256f | ||
|
|
c879351063 | ||
|
|
de8a244500 | ||
|
|
1bb7e36a65 | ||
|
|
3066a142b8 | ||
|
|
793a01f7ca | ||
|
|
ccf047b1ab | ||
|
|
40c8fdbf64 | ||
|
|
dc01c907f1 | ||
|
|
801df94446 | ||
|
|
0197d89976 | ||
|
|
ce725252cc | ||
|
|
e16a67242e | ||
|
|
4c678a5010 | ||
|
|
73f07b2939 | ||
|
|
b58efa3df0 | ||
|
|
9f885407f5 | ||
|
|
3754088a44 | ||
|
|
c4f084a991 | ||
|
|
4c73df4ba6 | ||
|
|
4aa53aa5a5 | ||
|
|
7483d3b229 | ||
|
|
8b6cc708e7 | ||
|
|
200960899e | ||
|
|
599a456c81 | ||
|
|
4b46502d22 | ||
|
|
7233c55428 | ||
|
|
a58c5cce78 | ||
|
|
0b23bf65b3 | ||
|
|
bc0a3f8a47 | ||
|
|
ef59fd4b6f | ||
|
|
9b2a0102be | ||
|
|
04b4ef6d85 | ||
|
|
9e8aadb750 | ||
|
|
36e7c2467e | ||
|
|
f7ce83ea34 | ||
|
|
0ae0f40628 | ||
|
|
81182bb125 | ||
|
|
2424df0d18 | ||
|
|
af9aa7d201 | ||
|
|
4cd3a614de | ||
|
|
1e5420e6a7 | ||
|
|
660de0b4e5 | ||
|
|
b73aaecd22 | ||
|
|
e9d07eadaa | ||
|
|
6f0e944c7e | ||
|
|
cd8d2c141e | ||
|
|
c831d62bc3 | ||
|
|
14ac66ff4e | ||
|
|
75bf758042 | ||
|
|
1539e703e9 | ||
|
|
f43bf03768 | ||
|
|
495f7f190f | ||
|
|
3acc521741 | ||
|
|
16216b9eb9 | ||
|
|
dfdb22f584 | ||
|
|
b7fb9e182b | ||
|
|
8574bf9d98 | ||
|
|
1a80439825 | ||
|
|
0b264176bc | ||
|
|
bde70a27f0 | ||
|
|
7d2f5f0799 | ||
|
|
28bc07da2f | ||
|
|
7610ab7a8d | ||
|
|
8517cc8211 | ||
|
|
51db653b1a | ||
|
|
8aa8ae239a | ||
|
|
dc4e4395a9 | ||
|
|
623694ab73 | ||
|
|
374457df70 | ||
|
|
7885dddef2 | ||
|
|
024ec86dc5 | ||
|
|
73d6cc1e54 | ||
|
|
f632ef0de8 | ||
|
|
8fdf84068d | ||
|
|
c58ad64a28 | ||
|
|
131625bb53 | ||
|
|
cbf3ae4db4 | ||
|
|
9ac7acf8b3 | ||
|
|
9dadb6da4c | ||
|
|
b6bbbeb9d3 | ||
|
|
517e9f92ba | ||
|
|
29c9e5cb17 | ||
|
|
52b60fd6a6 | ||
|
|
7ec09c80a5 | ||
|
|
b0bfe341df | ||
|
|
d6337f7500 | ||
|
|
70cf6546ca | ||
|
|
0cdab52418 | ||
|
|
625e04d208 | ||
|
|
6da657d3e2 | ||
|
|
777a95d23c | ||
|
|
06caace827 | ||
|
|
0a0a766c0d | ||
|
|
344c9e9238 | ||
|
|
89b2b066ef | ||
|
|
894e084c7f | ||
|
|
2ba064b2a5 | ||
|
|
dfe065ef82 | ||
|
|
7bd4b78470 | ||
|
|
d7991a247d | ||
|
|
2f9eacdf66 | ||
|
|
2178fd6ee9 | ||
|
|
7019ddb165 | ||
|
|
9f13763637 | ||
|
|
beeba27f46 | ||
|
|
79ac85e048 | ||
|
|
315d4f225a | ||
|
|
85489458d8 | ||
|
|
bfc7f56c4d | ||
|
|
23c5ddce83 | ||
|
|
7685f9b73d | ||
|
|
3bb9d220bb | ||
|
|
664fad96fa | ||
|
|
669a610e36 | ||
|
|
7e0d9cb48c | ||
|
|
7cbc9d21b5 | ||
|
|
62cb8358cc | ||
|
|
55b26751ae | ||
|
|
c2892d7887 | ||
|
|
6c8bf090fe | ||
|
|
ada5fd53e7 | ||
|
|
2f30451067 | ||
|
|
6b5c2be701 | ||
|
|
13bc378069 | ||
|
|
1ba70706c2 | ||
|
|
8431d0bd5c | ||
|
|
852f758be3 | ||
|
|
b455f432d5 | ||
|
|
6760744249 | ||
|
|
306cea60a1 | ||
|
|
ba2e07c4b9 | ||
|
|
9b796531b2 | ||
|
|
11c7d586d9 | ||
|
|
39cd83b171 | ||
|
|
2fdbc88d8c | ||
|
|
cc0f1be5d2 | ||
|
|
08d7a1c123 | ||
|
|
1f70a735c7 | ||
|
|
6713fcfeb1 | ||
|
|
c3107d213a | ||
|
|
adf5c8c278 | ||
|
|
5f86a8a15b | ||
|
|
042c756be8 | ||
|
|
1952505e52 | ||
|
|
c4086b9127 | ||
|
|
0ada09891c | ||
|
|
2d586406da | ||
|
|
651b4d2461 | ||
|
|
01b2468fea | ||
|
|
371c4e0051 | ||
|
|
69099fcdd7 | ||
|
|
28e714db1e | ||
|
|
57e50d0c33 | ||
|
|
a0fd02e0c0 | ||
|
|
c925b3d218 | ||
|
|
5da4386f31 | ||
|
|
4104dec87f | ||
|
|
d40a555531 | ||
|
|
95df91a03b | ||
|
|
3a8caa15b9 | ||
|
|
a5a25f02e3 | ||
|
|
bfd7be543a | ||
|
|
778e790e82 | ||
|
|
17eeb22971 | ||
|
|
1617658bfe | ||
|
|
991fe31569 | ||
|
|
4bcfff780a | ||
|
|
12f9ad8f7f | ||
|
|
6f843fcb27 | ||
|
|
e0f17e1778 | ||
|
|
4a5a9b73b1 | ||
|
|
7652336c84 | ||
|
|
94113827a7 | ||
|
|
cc10ee0134 | ||
|
|
5ad0fdf39c | ||
|
|
7201a8d634 | ||
|
|
3fde7365f9 | ||
|
|
f6a5bc9b40 | ||
|
|
9a5917a331 | ||
|
|
94c0324e8a | ||
|
|
d3b5594092 | ||
|
|
742d580eae | ||
|
|
d4a3cadd09 | ||
|
|
ec0feb68f4 | ||
|
|
bfb27c49a2 | ||
|
|
cb7b7f1dca | ||
|
|
38c032b79e | ||
|
|
2b7f2d4744 | ||
|
|
5ca8bc3f2a | ||
|
|
1ccc8eec0a | ||
|
|
9c5afda83a | ||
|
|
05b86a71fd | ||
|
|
d94d3d4bc5 | ||
|
|
e0c0f29fc6 | ||
|
|
119826cb9b | ||
|
|
6ab95ed4ef | ||
|
|
4f1eb64ac6 | ||
|
|
3e3dc3a6ab | ||
|
|
3f8dc76f84 | ||
|
|
8a6945ff3b | ||
|
|
f60579fd21 | ||
|
|
e6111c0d48 | ||
|
|
136a00a301 | ||
|
|
b2f5bee20d | ||
|
|
fa075f6800 | ||
|
|
277f395595 | ||
|
|
e2061464a5 | ||
|
|
04c4451f7d | ||
|
|
fcf5cd4655 | ||
|
|
3816f0b68b | ||
|
|
ccab1844ce | ||
|
|
5a6be54970 | ||
|
|
ce6b5105c6 | ||
|
|
de7da8b26e | ||
|
|
24932d6ba3 | ||
|
|
d5fd5954d1 | ||
|
|
977075763d | ||
|
|
757172934e | ||
|
|
1a2543ddde | ||
|
|
1a520f8782 | ||
|
|
b56c1b956c | ||
|
|
1a279d14c4 | ||
|
|
0d8e763a5f | ||
|
|
bf6d3649a4 | ||
|
|
7c9db7edeb | ||
|
|
65fd705d9a | ||
|
|
311e54451b | ||
|
|
8b759bc5d9 | ||
|
|
9a0aac4745 | ||
|
|
cb6607a169 | ||
|
|
8caa4e9cb6 | ||
|
|
d6338d7b11 | ||
|
|
f96a5ec774 | ||
|
|
1be8e7e216 | ||
|
|
cf9cf9d7bb | ||
|
|
6ba1795ded | ||
|
|
ced0cc1bac | ||
|
|
3c49f22266 | ||
|
|
13ae99edec | ||
|
|
0f82730a78 | ||
|
|
8608c45309 | ||
|
|
90516217e0 | ||
|
|
ff083daf31 | ||
|
|
7f6a554e1b | ||
|
|
eb99271120 | ||
|
|
04aa22b510 | ||
|
|
13e5c695c3 | ||
|
|
1cd56decab | ||
|
|
585f8f4683 | ||
|
|
12de47c923 | ||
|
|
afde7940d8 | ||
|
|
1070d8d3fa | ||
|
|
dcc7ba8f93 | ||
|
|
01dd41bdbf | ||
|
|
7d5b20314c | ||
|
|
29e048af7b | ||
|
|
f3b84f1365 | ||
|
|
5751d5c1b0 | ||
|
|
3d2336aac1 | ||
|
|
afe5f19464 | ||
|
|
4091a3c238 | ||
|
|
c38f0d751b | ||
|
|
3623afa721 | ||
|
|
f411e07fb4 | ||
|
|
9bfe27dd5e | ||
|
|
6a096fbb27 | ||
|
|
c4201d9a2a | ||
|
|
58e9817a6d | ||
|
|
48d5584491 | ||
|
|
6404071a01 | ||
|
|
735205492e | ||
|
|
5786f1d057 | ||
|
|
87c6f3c757 | ||
|
|
a6feb77e52 | ||
|
|
0e99c948d6 | ||
|
|
0f5e0f640b | ||
|
|
3d2c6388de | ||
|
|
13b3b2fd23 | ||
|
|
ad8e614ae8 | ||
|
|
9a6d709082 | ||
|
|
48c0360877 | ||
|
|
6eafab8286 | ||
|
|
d230be3e1c | ||
|
|
daa77d40a3 | ||
|
|
c901512db0 | ||
|
|
6ae743684f | ||
|
|
789c00dde1 | ||
|
|
90da471084 | ||
|
|
2d55056015 | ||
|
|
ffab9bb893 | ||
|
|
1192d04391 | ||
|
|
f18dced2f3 | ||
|
|
31ea032054 | ||
|
|
89543e927a | ||
|
|
a52386e6ad | ||
|
|
ecdc0a3800 | ||
|
|
46d66dded8 | ||
|
|
a69dcfc49d | ||
|
|
601005f837 | ||
|
|
de6f32e486 | ||
|
|
fbe866198d | ||
|
|
70546cd2ec | ||
|
|
a7c99cbbd2 | ||
|
|
40780ccec7 | ||
|
|
7078c91f7d | ||
|
|
0e2168392c | ||
|
|
380006c9d8 | ||
|
|
4127882e5f | ||
|
|
d603a8a9be | ||
|
|
5f17d30973 | ||
|
|
8f92077454 | ||
|
|
8755a9bcda | ||
|
|
6ace46eece | ||
|
|
37cab07295 | ||
|
|
4a1ecc7b72 | ||
|
|
e46078286d | ||
|
|
cfeb88f649 | ||
|
|
7ce197e0c8 | ||
|
|
f93b1167f1 | ||
|
|
152ca63529 | ||
|
|
806221bc3f | ||
|
|
2e8d5311a5 | ||
|
|
23b5fd1c12 | ||
|
|
7a8ba7d47d | ||
|
|
1a509cf3e0 | ||
|
|
9848f9613c | ||
|
|
4264c2a173 | ||
|
|
93cefb88f5 | ||
|
|
7132e1fee1 | ||
|
|
e70e1b8ad7 | ||
|
|
77d8a8e43d | ||
|
|
0b75a7d0d3 | ||
|
|
1f4a15ee6b | ||
|
|
f6772eaf59 | ||
|
|
bab4f9963f | ||
|
|
1dbcac4f53 | ||
|
|
7ac5abe7f8 | ||
|
|
157e6b2a33 | ||
|
|
8d20a13776 | ||
|
|
a385ea7c52 | ||
|
|
c58a3c41d8 | ||
|
|
0a7df86f3f | ||
|
|
213bc75ae1 | ||
|
|
f8df7ebb7c | ||
|
|
60503c31fb | ||
|
|
77220e24dd | ||
|
|
3c25e11c5f | ||
|
|
be2ffc31b2 | ||
|
|
8a2256e0d8 | ||
|
|
adfb24ce02 | ||
|
|
e6d50f94ee | ||
|
|
520798bfa6 | ||
|
|
e539a36ae7 | ||
|
|
bfab2d405b | ||
|
|
df38f00cf2 | ||
|
|
a30ce1c44d | ||
|
|
eee2450c9e | ||
|
|
baf4a241a2 | ||
|
|
e2242f5d99 | ||
|
|
30df67721d | ||
|
|
4a20f43fbf | ||
|
|
c59e792178 | ||
|
|
e6a99c1d33 | ||
|
|
3548628c2c | ||
|
|
bb84157a21 | ||
|
|
eb10aa8c97 | ||
|
|
e06d269b82 | ||
|
|
07428922c3 | ||
|
|
0f7e22d8b7 | ||
|
|
ba4c3e3852 | ||
|
|
b8de36b340 | ||
|
|
bbe3e8093c | ||
|
|
26096bc136 | ||
|
|
caae57d960 | ||
|
|
f91384596c | ||
|
|
cb38976162 | ||
|
|
3460f9d9cc | ||
|
|
6eacf8ed7e | ||
|
|
20bebe98b1 | ||
|
|
91d885eae4 | ||
|
|
ea3358ecb2 | ||
|
|
4c5c4d1700 | ||
|
|
e3fabe92bd | ||
|
|
142740f080 | ||
|
|
09e51c2399 | ||
|
|
0892eb271d | ||
|
|
536be76ecb | ||
|
|
6bfdf0eb4d | ||
|
|
8320ed5a92 | ||
|
|
43677685bb | ||
|
|
61e0aa9845 | ||
|
|
34eee005a8 | ||
|
|
6fa136da0c | ||
|
|
9b3e6270d5 | ||
|
|
b5abc6c724 | ||
|
|
71edc3a084 | ||
|
|
f74a45a33e | ||
|
|
0a1d04495d | ||
|
|
49dee560fd | ||
|
|
dfb3dfb74d | ||
|
|
05521bfd3a | ||
|
|
6c997f573a | ||
|
|
c4689c3bcc | ||
|
|
c0dd98b6d7 | ||
|
|
83452d73bc | ||
|
|
e5417d12ca | ||
|
|
6e19e6f0a0 | ||
|
|
071281c13a | ||
|
|
56cf1c818b | ||
|
|
61101987f9 | ||
|
|
a057e50684 | ||
|
|
5f3dc660c8 | ||
|
|
5553964d52 | ||
|
|
51ead2f6bd | ||
|
|
d433b25627 | ||
|
|
bd7671c07e | ||
|
|
847178b7be | ||
|
|
90808ac67e | ||
|
|
a899ea8b4d | ||
|
|
7b83b99ac9 | ||
|
|
7aab7678e9 | ||
|
|
195af002cf | ||
|
|
38db8d2bfd | ||
|
|
44a9fed8a9 | ||
|
|
cfc346abad | ||
|
|
befeb55349 | ||
|
|
b460d0f533 | ||
|
|
c11acb6308 | ||
|
|
d76103eb76 | ||
|
|
779f984a30 | ||
|
|
9c55017191 | ||
|
|
c6f575d8d3 | ||
|
|
2c0c22dbf3 | ||
|
|
73a99a7dea | ||
|
|
3c81337630 | ||
|
|
3f41ba6bdf | ||
|
|
2812f61957 | ||
|
|
f23cee17eb | ||
|
|
746a8badac | ||
|
|
f24fcfca69 | ||
|
|
d60506a75d | ||
|
|
77dee439e6 | ||
|
|
d7790a04c5 | ||
|
|
2c0b07387b | ||
|
|
06f2f9adbb | ||
|
|
37d5b9109f | ||
|
|
76e269ee21 | ||
|
|
3103939197 | ||
|
|
3f01c87223 | ||
|
|
e4cda4bb99 | ||
|
|
f333f867c5 | ||
|
|
75c46f7a0e | ||
|
|
5c6cb41124 | ||
|
|
94f7c03871 | ||
|
|
96d5c03a6d | ||
|
|
3a03bc41a7 | ||
|
|
331844ff73 | ||
|
|
2bc38e3784 | ||
|
|
d580dedfc8 | ||
|
|
aa0f4c9985 | ||
|
|
aa242d897d | ||
|
|
d56ab6c971 | ||
|
|
2f7be3475d | ||
|
|
2544d2e068 | ||
|
|
a09f64aee5 | ||
|
|
f7114fc2aa | ||
|
|
7092f337ef | ||
|
|
8e71c3ae17 | ||
|
|
a9c211d66c | ||
|
|
a1c2c04510 | ||
|
|
0902b628f8 | ||
|
|
517a85f9e9 | ||
|
|
94941a7732 | ||
|
|
f3aa8d368e | ||
|
|
9518c5f2e4 | ||
|
|
e44173ff09 | ||
|
|
fa1106d3cf | ||
|
|
c24d574f90 | ||
|
|
8ed35652bc | ||
|
|
9bdcfc8a45 | ||
|
|
89b76b514c | ||
|
|
dd433d8af0 | ||
|
|
c105f3b970 | ||
|
|
94471c0d1c | ||
|
|
849e04ab83 | ||
|
|
67833c5513 | ||
|
|
b0306867b4 | ||
|
|
d5025fdbcc | ||
|
|
e6cf1dc98d | ||
|
|
0266370218 | ||
|
|
aeb3f2b018 | ||
|
|
bb8d5ac13f | ||
|
|
d02bf37167 | ||
|
|
99dbd9e649 | ||
|
|
0d63dad8c2 | ||
|
|
6ce465664f | ||
|
|
b934634159 | ||
|
|
ef904e01ec | ||
|
|
25b77db4cd | ||
|
|
88c0a9e30a | ||
|
|
75c219d6c6 | ||
|
|
82e7328903 | ||
|
|
d6b366c77f | ||
|
|
42a7c84a33 | ||
|
|
1f4c1f2af5 | ||
|
|
e665e4dc57 | ||
|
|
9551519a35 | ||
|
|
df51b80e07 | ||
|
|
0df6826c91 | ||
|
|
af61962314 | ||
|
|
f5ed710c0b | ||
|
|
e5ae07b3e8 | ||
|
|
f1535e1f71 | ||
|
|
a6316b40d1 | ||
|
|
c2746a55e3 | ||
|
|
e9c782bf9e | ||
|
|
ad6c154eb6 | ||
|
|
b747b10642 | ||
|
|
2005a2abd4 | ||
|
|
c5818ff5e4 | ||
|
|
b5e02e6ff9 | ||
|
|
205f62e732 | ||
|
|
fdc8b5eb54 | ||
|
|
36abe8e808 | ||
|
|
fcf0e08e01 | ||
|
|
0edcf97e3f | ||
|
|
0707b26303 | ||
|
|
09662a4bcd | ||
|
|
cb06961b82 | ||
|
|
824fb4adae | ||
|
|
a91d993c6d | ||
|
|
e16bab8dd9 | ||
|
|
068c5851ef | ||
|
|
af48dcd884 | ||
|
|
058806c241 | ||
|
|
48f6e33bf2 | ||
|
|
6a3ef42d37 | ||
|
|
f10a9d7f61 | ||
|
|
c315c04b9a | ||
|
|
f7893d4e4b | ||
|
|
2e2f7fa6c3 | ||
|
|
7aff64f877 | ||
|
|
0d05bc2676 | ||
|
|
131b350ee7 | ||
|
|
06651dbc4c | ||
|
|
0296247d82 | ||
|
|
79e767ba79 | ||
|
|
f771dee852 | ||
|
|
c4d8ef5954 | ||
|
|
5d6b97a63e | ||
|
|
c277ebb43e | ||
|
|
c9cd7b087a | ||
|
|
da7ff52a2f | ||
|
|
84e4a4bb52 | ||
|
|
34c9c5a9b1 | ||
|
|
e348e66f14 | ||
|
|
1e83614f8b | ||
|
|
c53647cd18 | ||
|
|
faa4e7753c | ||
|
|
220757be06 | ||
|
|
7f1013123a | ||
|
|
b1a7fe0f80 | ||
|
|
153e6b56ba | ||
|
|
35998df05c | ||
|
|
5176a8cb9e | ||
|
|
4892b648ae | ||
|
|
b4506c2b9b | ||
|
|
4373163869 | ||
|
|
4b8cde7b3b | ||
|
|
b7543c8a45 | ||
|
|
b5232886d9 | ||
|
|
aefd81228e | ||
|
|
03516e11f7 | ||
|
|
455a17cdb2 | ||
|
|
564ad09b98 | ||
|
|
135f6b0050 | ||
|
|
d5e79ff36c | ||
|
|
7a7f34746e | ||
|
|
6b4bbe625b | ||
|
|
28c0b2737b | ||
|
|
7684fb83d6 | ||
|
|
4d22608724 | ||
|
|
b7a42d61c4 | ||
|
|
bdaef7e541 | ||
|
|
854501385e | ||
|
|
7b875ed378 | ||
|
|
c930d6ddc0 | ||
|
|
aab2e4555a | ||
|
|
938eefae9b | ||
|
|
eccc5e6d61 | ||
|
|
bc96ca48b6 | ||
|
|
e5dd88cdce | ||
|
|
51acd4952b | ||
|
|
88e0da765c | ||
|
|
3ba708b98d | ||
|
|
dfb6216271 | ||
|
|
70387e6976 | ||
|
|
55fd781c77 | ||
|
|
36a0a240f9 | ||
|
|
ed0b507fb5 | ||
|
|
3a3948e74f | ||
|
|
c01dd232c5 | ||
|
|
825cc096c0 | ||
|
|
bcd574036e | ||
|
|
634fe64dca | ||
|
|
e33a0bf940 | ||
|
|
fb91d9b2d4 | ||
|
|
3d89588eb1 | ||
|
|
ac010cd7b2 | ||
|
|
d0c94ba565 | ||
|
|
adbeb4ae54 | ||
|
|
c7e1b47c59 | ||
|
|
b25e22bca4 | ||
|
|
6cb6a8ee98 | ||
|
|
223bd969fd | ||
|
|
ae46eeabef | ||
|
|
79c83a96a0 | ||
|
|
db33eb5b1f | ||
|
|
bc45f8f1d8 | ||
|
|
7875a96137 | ||
|
|
4875f5e275 | ||
|
|
7f9da22626 | ||
|
|
78098e612d | ||
|
|
e19ab9a0d5 | ||
|
|
ace45516d9 | ||
|
|
fe57bfce1b | ||
|
|
453cc0cc05 | ||
|
|
39009c482d | ||
|
|
6b942be1cd | ||
|
|
77aa2b6ba9 | ||
|
|
074d53fa17 | ||
|
|
a839ec4832 | ||
|
|
5119799256 | ||
|
|
176e18e4c0 | ||
|
|
b697df35c6 | ||
|
|
5236625485 | ||
|
|
f36073f5cd | ||
|
|
595fd9377c | ||
|
|
76f4c49e22 | ||
|
|
8c0592cdb7 | ||
|
|
0c73ecf6a8 | ||
|
|
c365fd9d74 | ||
|
|
aa8c587ac8 | ||
|
|
e9d9760e16 | ||
|
|
560f72a320 | ||
|
|
3425f2bf34 | ||
|
|
e28dbb3b93 | ||
|
|
1b3ed80d37 | ||
|
|
89a2e15cfb | ||
|
|
99b1282e98 | ||
|
|
ed753b590c | ||
|
|
bda095b0ec | ||
|
|
9d7f1d230c | ||
|
|
bdf0458780 | ||
|
|
7f973ad131 | ||
|
|
1f42070104 | ||
|
|
98e4ac6b27 | ||
|
|
8d318e7206 | ||
|
|
362156c8ab | ||
|
|
3ff23ade8b | ||
|
|
d9050dd8b9 | ||
|
|
bcee2c37d7 | ||
|
|
b8fcab29ac | ||
|
|
8ddae6bba0 | ||
|
|
fc9255cdc4 | ||
|
|
f725506df0 | ||
|
|
aa27b85538 | ||
|
|
afe75f8a89 | ||
|
|
b725d66ee3 | ||
|
|
fe3fb622ee | ||
|
|
e8bd114c01 | ||
|
|
b694bcf62a | ||
|
|
b0f933c926 | ||
|
|
2d12bc01de | ||
|
|
17b4ff7a1f | ||
|
|
6aff9f91d5 | ||
|
|
cac8fd93e4 | ||
|
|
f046abefbc | ||
|
|
b34e060b17 | ||
|
|
907be77ef4 | ||
|
|
373d12be64 | ||
|
|
bf45d55b4a | ||
|
|
2525429070 | ||
|
|
2513bcf2c6 | ||
|
|
bbffb6d901 | ||
|
|
3b7f5a1397 | ||
|
|
600e949628 | ||
|
|
235c123ad0 | ||
|
|
dcea584a9a | ||
|
|
d583adb958 | ||
|
|
0d96a0fb7c | ||
|
|
fb6b2cbfa2 | ||
|
|
eb65eafbe0 | ||
|
|
13f18492ad | ||
|
|
3d04541ee3 | ||
|
|
6ded0cce12 | ||
|
|
1ecbaf2c7f | ||
|
|
b0828a6280 | ||
|
|
d2ffb3ca9d | ||
|
|
606cd18dc4 | ||
|
|
8a713b2bbb | ||
|
|
c68f31a11c | ||
|
|
3107943af8 | ||
|
|
bf9360f374 | ||
|
|
f66c28a1bc | ||
|
|
5d6e214e0e | ||
|
|
ee8596d175 | ||
|
|
3c900814be | ||
|
|
52df963be9 | ||
|
|
8c513c2aad | ||
|
|
1e3834dc3d | ||
|
|
386b10a004 | ||
|
|
f697f3d7ad | ||
|
|
8d3ff16037 | ||
|
|
b10ab358da | ||
|
|
2cc6edd868 | ||
|
|
cac974b8e1 | ||
|
|
2c02e27197 | ||
|
|
85c0002a78 | ||
|
|
bd65bf2175 | ||
|
|
d70332f2a2 | ||
|
|
35b54e2f88 | ||
|
|
a3e1af72ab | ||
|
|
1313a44ba3 | ||
|
|
edbe8131ab | ||
|
|
ff55a305cd | ||
|
|
10c22efbb5 | ||
|
|
118bbfbcf7 | ||
|
|
e7709d8463 | ||
|
|
d70e421db3 | ||
|
|
0028c3c640 | ||
|
|
2594cb23c4 | ||
|
|
d975f6e23a | ||
|
|
5e46975c63 | ||
|
|
16f41fc4b4 | ||
|
|
1263f5e046 | ||
|
|
96713ef383 | ||
|
|
26a8686a54 | ||
|
|
57421d2392 | ||
|
|
63d92381ee | ||
|
|
2716f4cbe9 | ||
|
|
6ef4582ebd | ||
|
|
649ebb8a4e | ||
|
|
b1acf6088f | ||
|
|
78f5d80c5b | ||
|
|
5896b1eb71 | ||
|
|
559891ac43 | ||
|
|
1fbe1d1855 | ||
|
|
71473fd5c4 | ||
|
|
c7beacf3b9 | ||
|
|
3dc55ff8b5 | ||
|
|
b6789e0011 | ||
|
|
2f2bebe506 | ||
|
|
503b8cbf34 | ||
|
|
916356afad | ||
|
|
41a84160ec | ||
|
|
55f071e4a0 | ||
|
|
0afe0f8fbd | ||
|
|
427b5bd126 | ||
|
|
20f6378a3e | ||
|
|
36e0a1eb23 | ||
|
|
ac9a567e1f | ||
|
|
33cd52cddd | ||
|
|
d543ba82ac | ||
|
|
5a98993df9 | ||
|
|
aee9c4a193 | ||
|
|
0262f89bb0 | ||
|
|
a8c530a3a9 | ||
|
|
7dc1240c18 | ||
|
|
3b4bccd0a7 | ||
|
|
39a80f8dc7 | ||
|
|
ce446dac92 | ||
|
|
b1c9dbd0a5 | ||
|
|
a5c57d7fb0 | ||
|
|
0a349e5298 | ||
|
|
dc7d7150cd | ||
|
|
7c4aa4441f | ||
|
|
62a795bd3b | ||
|
|
c583ab4f1a | ||
|
|
45a2970307 | ||
|
|
56a74bc54c | ||
|
|
5a4b719995 | ||
|
|
34a4a89bc9 | ||
|
|
02989f65ff | ||
|
|
1da539ef05 | ||
|
|
38d6fbd3d3 | ||
|
|
39682a4cf1 | ||
|
|
993a5805cd | ||
|
|
09cf528cc6 | ||
|
|
18ab2b3ab5 | ||
|
|
655fbf91e2 | ||
|
|
980794ea43 | ||
|
|
c1cab30daf | ||
|
|
b50e798d55 | ||
|
|
230e3ef842 | ||
|
|
2635a3edd0 | ||
|
|
47f24ac151 | ||
|
|
42397a01bf | ||
|
|
7730a00dbb | ||
|
|
3a4f4c8c34 | ||
|
|
a0d88b0cbe | ||
|
|
bb0419837b | ||
|
|
35098c474c | ||
|
|
0ca6535345 | ||
|
|
ec6a243e3e | ||
|
|
2cd8ee4a13 | ||
|
|
432755e05d | ||
|
|
51a7c2adc5 | ||
|
|
5c1f793887 | ||
|
|
a20358b61b | ||
|
|
e5dc473ced | ||
|
|
e57a518b34 | ||
|
|
341d9d1697 | ||
|
|
9cee424383 | ||
|
|
37cb51cfd6 | ||
|
|
533f86c035 | ||
|
|
9947c51af7 | ||
|
|
409303d0f5 | ||
|
|
84b7bfb989 | ||
|
|
04d17e4839 | ||
|
|
10a1275a52 | ||
|
|
5464cbc2ed | ||
|
|
3b3f8e6f43 | ||
|
|
2d4d7bf937 | ||
|
|
652651668f | ||
|
|
e57d5d86f3 | ||
|
|
4f7dc5be34 | ||
|
|
b2c656940b | ||
|
|
f8266d9811 | ||
|
|
5c03550a8a | ||
|
|
ed5491a480 | ||
|
|
e8371f403b | ||
|
|
373cffb261 | ||
|
|
2bf22766eb | ||
|
|
83530fcaf1 | ||
|
|
6a41123209 | ||
|
|
c895c5c16e | ||
|
|
9ab4b3f3f4 | ||
|
|
b93dd47a01 | ||
|
|
69a826f896 | ||
|
|
60c7ec5aca | ||
|
|
888eacb574 | ||
|
|
4def582ab1 | ||
|
|
ee6f25cc47 | ||
|
|
f5d6383f50 | ||
|
|
00f00b8198 | ||
|
|
3d1f883465 | ||
|
|
8613a89931 | ||
|
|
dd1485a66a | ||
|
|
4198954d46 | ||
|
|
e56caa8959 | ||
|
|
de7c72d5a7 | ||
|
|
18a4a43f8a | ||
|
|
70c73d2e0e | ||
|
|
2049e8b8df | ||
|
|
41da889a09 | ||
|
|
0a1bbca321 | ||
|
|
7e01d113d8 | ||
|
|
6e49cc54ac | ||
|
|
487a95eb79 | ||
|
|
696e438df7 | ||
|
|
a4e093159e | ||
|
|
23986579bb | ||
|
|
0fc099926f | ||
|
|
06b9b66272 | ||
|
|
fec8343f45 | ||
|
|
2d8f2593f0 | ||
|
|
ab0a03a420 | ||
|
|
3ef30a42be | ||
|
|
034687163f | ||
|
|
190db80c28 | ||
|
|
579f355852 | ||
|
|
d9104433a8 | ||
|
|
c6059f9bf6 | ||
|
|
fd06ff08c7 | ||
|
|
afd990bd71 | ||
|
|
8333c4b48d | ||
|
|
30c4f501fc | ||
|
|
3b96eef2ad | ||
|
|
a769ab6cff | ||
|
|
ef1f835e57 | ||
|
|
329b2ba275 | ||
|
|
98383ef19b | ||
|
|
8bb70fb1b0 | ||
|
|
72cca365e5 | ||
|
|
dfc99faea8 | ||
|
|
71886c162b | ||
|
|
df1f516f6b | ||
|
|
2e55189d59 | ||
|
|
6ade40ce85 | ||
|
|
94c5f0d7a8 | ||
|
|
69a2ac8de1 | ||
|
|
b1028086a3 | ||
|
|
385b3570b2 | ||
|
|
77edd12030 | ||
|
|
217fca31e4 | ||
|
|
5e1d20fc72 | ||
|
|
db60309ce8 | ||
|
|
8bfae2fa74 | ||
|
|
2788dd1ada | ||
|
|
6a5b757a24 | ||
|
|
a19098a004 | ||
|
|
ec10c29aca | ||
|
|
f90ab46794 | ||
|
|
b8ca97e019 | ||
|
|
e12757dbb9 | ||
|
|
3e9b44d193 | ||
|
|
3006d25406 | ||
|
|
f2ae9a0711 | ||
|
|
19945e4ccb | ||
|
|
61ab16acd6 | ||
|
|
3a84292f82 | ||
|
|
1d314294c7 | ||
|
|
b5b1dbfe85 | ||
|
|
5be88dd188 | ||
|
|
88b67b9541 | ||
|
|
a31da3186f | ||
|
|
9a7a38e913 | ||
|
|
a0960f5468 | ||
|
|
9b87a512cf | ||
|
|
8da985b6d0 | ||
|
|
5cb2a39746 | ||
|
|
6268cadc95 | ||
|
|
470b4b3972 | ||
|
|
a88ae2ca56 | ||
|
|
9875fbef0a | ||
|
|
cf29ddc8d0 | ||
|
|
ad70fcba7f | ||
|
|
a9a7720a11 | ||
|
|
8ff4f50f79 | ||
|
|
aea07f42d1 | ||
|
|
8d75c06852 | ||
|
|
ffbedbc1e6 | ||
|
|
a47d48c973 | ||
|
|
c54092c932 | ||
|
|
9134c3b4af | ||
|
|
7e21955211 | ||
|
|
40ac760285 | ||
|
|
de63bdac39 | ||
|
|
408d63825a | ||
|
|
3e1eb02f54 | ||
|
|
d2f55e1064 | ||
|
|
c97bead631 | ||
|
|
8264ca4e9e | ||
|
|
05bdaa4bc7 | ||
|
|
cc2de04f6b | ||
|
|
f99f13a090 | ||
|
|
f35ab125ec | ||
|
|
b8342f1c9c | ||
|
|
0ce77bbc59 | ||
|
|
e39eccb99e | ||
|
|
77ce67d685 | ||
|
|
ef125ff109 | ||
|
|
30e3bbd0e8 | ||
|
|
7bf1c35dcf | ||
|
|
c6ea2dff8a | ||
|
|
f2329476ec | ||
|
|
c4512b75d9 | ||
|
|
92e43abbc4 | ||
|
|
07d6bfa989 | ||
|
|
ccf5e66f31 | ||
|
|
6f5e71164a | ||
|
|
12131764d1 | ||
|
|
594c0b8550 | ||
|
|
ed729c32d2 | ||
|
|
ea26ee8d40 | ||
|
|
53f09c0630 | ||
|
|
2856e02eac | ||
|
|
a4f0c9195e | ||
|
|
0e73a0293b | ||
|
|
f4f5d6e562 | ||
|
|
e5c3629e2e | ||
|
|
5028471598 | ||
|
|
c28027496d | ||
|
|
c0f5509652 | ||
|
|
9bc48ececa | ||
|
|
1f53e4d4a2 | ||
|
|
dbef0e6583 | ||
|
|
c246e5af35 | ||
|
|
d36796429e | ||
|
|
37a31a9b94 | ||
|
|
c2ab3f96a7 | ||
|
|
ef36b5377d | ||
|
|
f12237d298 | ||
|
|
5b312d4c8b | ||
|
|
d21e82b020 | ||
|
|
c6e5606b4c | ||
|
|
7b4dc43147 | ||
|
|
62510ac6a3 | ||
|
|
bffe2d7bd0 | ||
|
|
cbaf1e576d | ||
|
|
0d355dbf35 | ||
|
|
d851bff762 | ||
|
|
453f4b549b | ||
|
|
8d669b12b6 | ||
|
|
ed3234d949 | ||
|
|
603a00376f | ||
|
|
4f34cfb654 | ||
|
|
fd1e536cdb | ||
|
|
0c6a880a74 | ||
|
|
89fa053310 | ||
|
|
267887908e | ||
|
|
b86f58befe | ||
|
|
b804101c24 | ||
|
|
f28502514f | ||
|
|
79da716a44 | ||
|
|
f76a440b74 | ||
|
|
18dad0c72c | ||
|
|
14a5ab6740 | ||
|
|
d0d56c4416 | ||
|
|
9dd9a01211 | ||
|
|
480e7f5b54 | ||
|
|
cffee7bfa7 | ||
|
|
5b9eda9c63 | ||
|
|
0a363d5fc0 | ||
|
|
9eb646085e | ||
|
|
e3269f6edc | ||
|
|
57d3120b45 | ||
|
|
81da8261a0 | ||
|
|
7e5949b4a9 | ||
|
|
a776b8ac31 | ||
|
|
14e19226ea | ||
|
|
5473b3e42e | ||
|
|
143179cac9 | ||
|
|
ad2c7ee363 | ||
|
|
755448f9f5 | ||
|
|
f304a6891f | ||
|
|
1221661ae3 | ||
|
|
a78f17abec | ||
|
|
70bf957a8a | ||
|
|
e8e04db7b4 | ||
|
|
d5def30c59 | ||
|
|
81176bc4e6 | ||
|
|
730ba12c8d | ||
|
|
80ad456d48 | ||
|
|
2d75526395 | ||
|
|
cd618a43d4 | ||
|
|
1979236f3a | ||
|
|
3589adcb78 | ||
|
|
4a834ecfc8 | ||
|
|
3dcc3492e8 | ||
|
|
2e77dcc660 | ||
|
|
89f60a0422 | ||
|
|
0455c09e4c | ||
|
|
7360b882ac | ||
|
|
5ffd29e31a | ||
|
|
5d7e892464 | ||
|
|
e8fd5de5b2 | ||
|
|
a8e7aa99e8 | ||
|
|
9a0db83f83 | ||
|
|
4e85566b88 | ||
|
|
8be72f6f23 | ||
|
|
513bc2c0ab | ||
|
|
036ba77a68 | ||
|
|
392277100c | ||
|
|
a7092ac503 | ||
|
|
027a16a39b | ||
|
|
c66af86d88 | ||
|
|
e621fdec0c | ||
|
|
447594b5ea | ||
|
|
a0b208cca0 | ||
|
|
1a4061fdb5 | ||
|
|
fb59f1f0a0 | ||
|
|
b1961033b3 | ||
|
|
863498f762 | ||
|
|
bdc44a4070 | ||
|
|
6451522f89 | ||
|
|
5ca1e9268c | ||
|
|
451a6841c9 | ||
|
|
3e3f46a683 | ||
|
|
2b0eaafea6 | ||
|
|
c1a8fde9a0 | ||
|
|
9dbd1b2a1b | ||
|
|
0cc1007543 | ||
|
|
8ca86181e4 | ||
|
|
8759d8f868 | ||
|
|
65a32b4166 | ||
|
|
933a0cddf8 | ||
|
|
d4c0542c84 | ||
|
|
05f5aaaeca | ||
|
|
036bd07e72 | ||
|
|
cb0452964e | ||
|
|
9c3e7b9ec0 | ||
|
|
1fd0383f69 | ||
|
|
9e62db5237 | ||
|
|
3062786bb3 | ||
|
|
b42847c135 | ||
|
|
5befcd24b5 | ||
|
|
61f3002568 | ||
|
|
0b7e0e166c | ||
|
|
3b52770a1b | ||
|
|
d0b9f53d8c | ||
|
|
92a9d7d26a | ||
|
|
ffd958a1b3 | ||
|
|
a7e9c7e24b | ||
|
|
9839cc6386 | ||
|
|
8d66d38fd2 | ||
|
|
a004d1647d | ||
|
|
21fe2c5f8f | ||
|
|
867e43637c | ||
|
|
c9025917b2 | ||
|
|
258def854c | ||
|
|
e8481818c8 | ||
|
|
f646e9bc59 | ||
|
|
680923bdd5 | ||
|
|
17e43a7ff2 | ||
|
|
71a5cfed4b | ||
|
|
c055477d3b | ||
|
|
ad4acca17a | ||
|
|
37b84300b4 | ||
|
|
d9a8dac266 | ||
|
|
76f704b060 | ||
|
|
cde360f7ec | ||
|
|
d69a2f1514 | ||
|
|
06a5c1a798 | ||
|
|
ca1f25ecf6 | ||
|
|
adcd7fb170 | ||
|
|
f105436b41 | ||
|
|
47738c751c | ||
|
|
0b3f2da6ef | ||
|
|
fa7092cc17 | ||
|
|
773d6e002c | ||
|
|
b78d1baaaf | ||
|
|
50ff59239c | ||
|
|
8f36878dde | ||
|
|
a29e98fd41 | ||
|
|
e4d5551f16 | ||
|
|
e610a1ffe4 | ||
|
|
ac927d188b | ||
|
|
bbf2f6c7de | ||
|
|
398816dbeb | ||
|
|
f3b399b31f | ||
|
|
2b0a38d25d | ||
|
|
b41dacb6c0 | ||
|
|
c5f5532303 | ||
|
|
09485c995e | ||
|
|
dc936b63d6 | ||
|
|
b5213794a8 | ||
|
|
2f8365a790 | ||
|
|
47be430bc1 | ||
|
|
918da24c8d | ||
|
|
0e82f2a02f | ||
|
|
447f15cfa4 | ||
|
|
8f80c206e7 | ||
|
|
e1a61985a2 | ||
|
|
16ad0c22f0 | ||
|
|
3d0d027216 | ||
|
|
b58a17a99e | ||
|
|
adaf2e15cd | ||
|
|
1723683370 | ||
|
|
9cd0a7982a | ||
|
|
d2726aae90 | ||
|
|
607a66b983 | ||
|
|
38b2adcaad | ||
|
|
d7879d8853 | ||
|
|
2d5b09a16f | ||
|
|
a06a722c23 | ||
|
|
d9d45f86cb | ||
|
|
e49b9cff67 | ||
|
|
8c69a4cb7a | ||
|
|
1042ac141a | ||
|
|
81aab35231 | ||
|
|
1c5742c7d4 | ||
|
|
4d646e67f3 | ||
|
|
59fbc51283 | ||
|
|
125e21900a | ||
|
|
569efd3e45 | ||
|
|
6c557ec015 | ||
|
|
63b917eb07 | ||
|
|
71af63dfc7 | ||
|
|
0cae048ada | ||
|
|
71021da261 | ||
|
|
1d897e491a | ||
|
|
57c0b408e5 | ||
|
|
43e0186efe | ||
|
|
8831d74cbc | ||
|
|
9a9d2cb964 | ||
|
|
2fc2a0cbdb | ||
|
|
40c041c9e7 | ||
|
|
b00c1dd055 | ||
|
|
761245049f | ||
|
|
30e142edd3 | ||
|
|
b7d84c002d | ||
|
|
551afe0cd7 | ||
|
|
5d8bfc2930 | ||
|
|
5d947cc8e1 | ||
|
|
7bf92db4c9 | ||
|
|
593c1d6b05 | ||
|
|
30e326b8b8 | ||
|
|
cf60eb52d8 | ||
|
|
8850dac291 | ||
|
|
bed52d0a4a | ||
|
|
10e564eeea | ||
|
|
ae47c4c79a | ||
|
|
8048bb85fb | ||
|
|
e99e9fed11 | ||
|
|
79e9dbb9c6 | ||
|
|
081a6888fa | ||
|
|
ae6ba79f1e | ||
|
|
d9d2ad4382 | ||
|
|
31d277ffa2 | ||
|
|
7fe5f8907e | ||
|
|
2e9b59e2e0 | ||
|
|
5188894301 | ||
|
|
4c16efbbf7 | ||
|
|
8055243909 | ||
|
|
2eb4383e03 | ||
|
|
bafd043aae | ||
|
|
3379e7cc48 | ||
|
|
d971fc440f | ||
|
|
31658f4eab | ||
|
|
339be2b7a4 | ||
|
|
3e716e429f | ||
|
|
a8a61665f4 | ||
|
|
a7a1002509 | ||
|
|
65154e48cb | ||
|
|
32c10e5ae8 | ||
|
|
61ee1c6ff3 | ||
|
|
6642d96688 | ||
|
|
df8b085f0e | ||
|
|
7a415ccdfc | ||
|
|
2a432dc6ee | ||
|
|
322d1f09de | ||
|
|
44f4bd6e62 | ||
|
|
2bb74233cc | ||
|
|
19f6fd2295 | ||
|
|
558811e4f9 | ||
|
|
ed560a338e | ||
|
|
74741682bf | ||
|
|
d32d4e21e9 | ||
|
|
78f301d503 | ||
|
|
318ffc821c | ||
|
|
ef4ec16860 | ||
|
|
6bc927c535 | ||
|
|
2fe2b3c975 | ||
|
|
e4ad0d3b59 | ||
|
|
750261d205 | ||
|
|
df1b25f381 | ||
|
|
4af7ccd84c | ||
|
|
4282053f68 | ||
|
|
5c7d993dbe | ||
|
|
cdef7a53a4 | ||
|
|
b1f26d4ebe | ||
|
|
9bf534288c | ||
|
|
3d359229cf | ||
|
|
8013f8d5b3 | ||
|
|
cf12ab4b74 | ||
|
|
0f3eecaa8a | ||
|
|
22c1f4bb4e | ||
|
|
43c5fea782 | ||
|
|
41eab27f20 | ||
|
|
ba354f68d1 | ||
|
|
a6c7cc7b41 | ||
|
|
4dfc1631af | ||
|
|
58861fa524 | ||
|
|
1c6b1c530f | ||
|
|
21b498fece | ||
|
|
446e2c123f | ||
|
|
b3a7ee633d | ||
|
|
8b6366688a | ||
|
|
1671c78260 | ||
|
|
96ca459df8 | ||
|
|
51fb981d24 | ||
|
|
2ef46195d3 | ||
|
|
6e35b5ba8b | ||
|
|
b8ea0bd1ff | ||
|
|
8ca6ca2c88 | ||
|
|
508804d0d5 | ||
|
|
eb251050a5 | ||
|
|
67758b4980 | ||
|
|
f187d7258b | ||
|
|
5efd1f3a91 | ||
|
|
e3def22b07 | ||
|
|
ce5839ce27 | ||
|
|
50082043ef | ||
|
|
012045878d | ||
|
|
3083ccddcc | ||
|
|
f39b9041a0 | ||
|
|
37827cdd6c | ||
|
|
58f41a1f9b | ||
|
|
7712faca0a | ||
|
|
805fe1d47e | ||
|
|
0d4d1eff94 | ||
|
|
38cc460358 | ||
|
|
8600360173 | ||
|
|
f3371c443e | ||
|
|
a86f750473 | ||
|
|
92b532ce9a | ||
|
|
924f97cbe8 | ||
|
|
8817dc6d73 | ||
|
|
7324d59e08 | ||
|
|
dcd8e9250f | ||
|
|
f07578e0cb | ||
|
|
cabcf8b7b2 | ||
|
|
a90a07f46d | ||
|
|
9839bab8dc | ||
|
|
b9dd3de63b | ||
|
|
194aa90aee | ||
|
|
9bc72df1b5 | ||
|
|
b2439ca0af | ||
|
|
70d04d807b | ||
|
|
2372675c79 | ||
|
|
cb66900575 | ||
|
|
de4be44728 | ||
|
|
21dc037848 | ||
|
|
9fe5e69503 | ||
|
|
c0ac381f6f | ||
|
|
5f235c121d | ||
|
|
6b8e9a63f3 | ||
|
|
3dcda08735 | ||
|
|
f92ee8e762 | ||
|
|
94200dd3a4 | ||
|
|
512c4dd6f1 | ||
|
|
6d43854557 | ||
|
|
112ba7ac5c | ||
|
|
4c58929dd4 | ||
|
|
2188509d2c | ||
|
|
263ef14822 | ||
|
|
ed24f72cf9 | ||
|
|
c1197d06fe | ||
|
|
bd4d1cdd41 | ||
|
|
2c70d66d4a | ||
|
|
97ecace40e | ||
|
|
6ef988549d | ||
|
|
9088d79390 | ||
|
|
0c9bcb2445 | ||
|
|
c98c54bd6b | ||
|
|
225e66a522 | ||
|
|
f1810a9784 | ||
|
|
9526051766 | ||
|
|
bd435b175b | ||
|
|
42ab734256 | ||
|
|
77d89e7df3 | ||
|
|
c1dec16c50 | ||
|
|
86ae11f4c4 | ||
|
|
7ca1970fff | ||
|
|
b1cbbeb935 | ||
|
|
2dce4462a0 | ||
|
|
55e2bc27c6 | ||
|
|
2c3cad6e61 | ||
|
|
6122a8371a | ||
|
|
4ffeb05120 | ||
|
|
310f55abb6 | ||
|
|
d6c0642a02 | ||
|
|
541556874f | ||
|
|
477d7080b8 | ||
|
|
173c752d62 | ||
|
|
3b70488828 | ||
|
|
334783f89a | ||
|
|
89a54e31f1 | ||
|
|
354d18f78e | ||
|
|
0e8806eb2b | ||
|
|
731550acb3 | ||
|
|
be4019b4d3 | ||
|
|
70ffcd9adf | ||
|
|
bbe511dd15 | ||
|
|
82d5ac91d7 | ||
|
|
54de0eab6a | ||
|
|
4d5bb274d1 | ||
|
|
131fae57e5 | ||
|
|
37da53c20e | ||
|
|
6ad57a15cf | ||
|
|
4729bce16c | ||
|
|
0b5ac7a139 | ||
|
|
677e23ad14 | ||
|
|
6d838e3043 | ||
|
|
f322abceb8 | ||
|
|
e754d21598 | ||
|
|
0fdd861ef1 | ||
|
|
2e5f6a3507 | ||
|
|
4e57cab0fa | ||
|
|
9930c8f94d | ||
|
|
f8e262b87e | ||
|
|
896bdaf124 | ||
|
|
2e4f2639a3 | ||
|
|
ce89a92d0d | ||
|
|
1d9d1f6bbd | ||
|
|
767b57fc01 | ||
|
|
9e00177d76 | ||
|
|
095429a7df | ||
|
|
983efd61fb | ||
|
|
e028316308 | ||
|
|
e1354accb8 | ||
|
|
ea726f928d | ||
|
|
6b419dbfc0 | ||
|
|
006a9eaf44 | ||
|
|
76117854c6 | ||
|
|
6eae6db46b | ||
|
|
3aacb7150d | ||
|
|
81298a1034 | ||
|
|
b3d728df91 | ||
|
|
9a091ff11a | ||
|
|
6989b1730e | ||
|
|
1595542d59 | ||
|
|
fba9992a10 | ||
|
|
867dce2294 | ||
|
|
692554a899 | ||
|
|
f3cc616e07 | ||
|
|
d1c289b709 | ||
|
|
e7a0874a57 | ||
|
|
1beb9c4bb8 | ||
|
|
6eef3a9037 | ||
|
|
ddaaae46a6 | ||
|
|
4e4773a370 | ||
|
|
f4a2ffc5d2 | ||
|
|
ba1117e10e | ||
|
|
0cd46f932a | ||
|
|
937f404583 | ||
|
|
d13d60d752 | ||
|
|
31e4e7c709 | ||
|
|
0d3a8ce31b | ||
|
|
be185b46a7 | ||
|
|
90fa5b3b93 | ||
|
|
733996772b | ||
|
|
d4c921ea2e | ||
|
|
2852061699 | ||
|
|
d8859b9f0a | ||
|
|
ae1bc96006 | ||
|
|
f30ffb4413 | ||
|
|
273c6467c8 | ||
|
|
846a1d007c | ||
|
|
1dccc8dc78 | ||
|
|
e0d67bd057 | ||
|
|
4b4b93ac04 | ||
|
|
4390aee1e0 | ||
|
|
4cddb16788 | ||
|
|
e1179fd8c8 | ||
|
|
cb77285277 | ||
|
|
6c9d161950 | ||
|
|
40aaac5868 | ||
|
|
e16b69594e | ||
|
|
4837bf007a | ||
|
|
705fd4dafd | ||
|
|
a3e28d3c66 | ||
|
|
4a6755c28a | ||
|
|
188fe5dc52 | ||
|
|
44a8ae457d | ||
|
|
92eafcfe1a | ||
|
|
b12b031fdd | ||
|
|
492ec489a1 | ||
|
|
c57124e876 | ||
|
|
95b33c9c34 | ||
|
|
c6d8b63e54 | ||
|
|
f0f02c4ea6 | ||
|
|
eb2cb6810a | ||
|
|
b3c090e9ed | ||
|
|
13366fc9f8 | ||
|
|
929af7830a | ||
|
|
13062cf0e4 | ||
|
|
b897a8a35f | ||
|
|
117dc5288d | ||
|
|
4b5a3bd3d5 | ||
|
|
b224a67ea7 | ||
|
|
793f919d59 | ||
|
|
315987b2f6 | ||
|
|
9b7db548a2 | ||
|
|
126b70f781 | ||
|
|
0bbff627e2 | ||
|
|
961d23e2a1 | ||
|
|
b03ff9a48a | ||
|
|
3ffb40fafa | ||
|
|
1a3b4ac2ac | ||
|
|
794e17442f | ||
|
|
238d7119e0 | ||
|
|
8a929a8348 | ||
|
|
cf77153647 | ||
|
|
a2da0b0641 | ||
|
|
73faa13811 | ||
|
|
1a71872c7b | ||
|
|
62fe7135bd | ||
|
|
078940d29f | ||
|
|
2fafe42c18 | ||
|
|
c8a7537157 | ||
|
|
d4bf1cb23d | ||
|
|
46e4350013 | ||
|
|
202eb0d854 | ||
|
|
898702346e | ||
|
|
b72e6f16ca | ||
|
|
b9c27ed324 | ||
|
|
0166dfe16e | ||
|
|
7274541722 | ||
|
|
709ff7a701 | ||
|
|
66c224c954 | ||
|
|
3f9b37aa7f | ||
|
|
0377958d8f | ||
|
|
cc1cfd70b8 | ||
|
|
bc125ad76c | ||
|
|
62a2246448 | ||
|
|
587cf751d8 | ||
|
|
600181ed07 | ||
|
|
f0e525d2e2 | ||
|
|
f86cdd8cde | ||
|
|
4a4c537a0d | ||
|
|
1caaf04dfa | ||
|
|
b422a80249 | ||
|
|
ba19e20833 | ||
|
|
c34ddb2bc3 | ||
|
|
aa315f8472 | ||
|
|
2af6af2bf0 | ||
|
|
5694ff7c97 | ||
|
|
76f1c689c1 | ||
|
|
a371239172 | ||
|
|
4fd904fbcc | ||
|
|
b73a257389 | ||
|
|
9e70d6b3e1 | ||
|
|
9caca37ab1 | ||
|
|
6e76fc0aa7 | ||
|
|
6171883758 | ||
|
|
942b68c948 | ||
|
|
9ca7ffa5a3 | ||
|
|
d1ce23c5ac | ||
|
|
b7b6d0a6bc | ||
|
|
10c51eea2c | ||
|
|
48d20c02a1 | ||
|
|
c5cc0b3f2b | ||
|
|
6ebef8846c | ||
|
|
5d1993935e | ||
|
|
caab8943cb | ||
|
|
f5c05b24fb | ||
|
|
940a0d006d | ||
|
|
8fe67a04d8 | ||
|
|
bec745d095 | ||
|
|
223fd35138 | ||
|
|
d3fc0309c0 | ||
|
|
830223f6e2 | ||
|
|
eb53c52499 | ||
|
|
96c9e2b4d6 | ||
|
|
c34b948bad | ||
|
|
9ac609f846 | ||
|
|
6aa47bfd1b | ||
|
|
ff46e6ea86 | ||
|
|
5489c74986 | ||
|
|
f6f8151150 | ||
|
|
a20d577f6c | ||
|
|
c4c2494dd1 | ||
|
|
c9c294a1d5 | ||
|
|
6359a8a8a2 | ||
|
|
f9b44381bd | ||
|
|
eb1ccb600b | ||
|
|
b2db61aa03 | ||
|
|
ee55a574de | ||
|
|
0998fd32cd | ||
|
|
686f2c4aa6 | ||
|
|
cd234673ea | ||
|
|
54d7a81f16 | ||
|
|
9d8d2c0aa1 | ||
|
|
17ce6b9507 | ||
|
|
296065a976 | ||
|
|
0b0f600f97 | ||
|
|
f1371d6737 | ||
|
|
03a33790e1 | ||
|
|
c0816c80ae | ||
|
|
071ebe6ef2 | ||
|
|
2126e42743 | ||
|
|
8a2b34adb4 | ||
|
|
93eb4d21bf | ||
|
|
868cedeed2 | ||
|
|
5c794f428a | ||
|
|
612d6f85bd | ||
|
|
e5cef6b877 | ||
|
|
f2a63c04a8 | ||
|
|
5a5064e070 | ||
|
|
478fa3132c | ||
|
|
a84859c211 | ||
|
|
67013bd58f | ||
|
|
b51be31d8a | ||
|
|
a3bef49124 | ||
|
|
a5cf553f17 | ||
|
|
640188f4e2 | ||
|
|
48265bbe02 | ||
|
|
6aaf544079 | ||
|
|
9904c10984 | ||
|
|
81c810eba4 | ||
|
|
5e6b1e8175 | ||
|
|
48c165b0b4 | ||
|
|
042605701e | ||
|
|
32cf1495d3 | ||
|
|
9577e49231 | ||
|
|
de19839145 | ||
|
|
f970780d6c | ||
|
|
1e69cc75c7 | ||
|
|
377e4fa0a5 | ||
|
|
a5d6dc58d3 | ||
|
|
a122c17340 | ||
|
|
34ddf104a9 | ||
|
|
f98b0beee5 | ||
|
|
75a61f85db | ||
|
|
c1cdf27507 | ||
|
|
43e9743645 | ||
|
|
c2972786f5 | ||
|
|
eeb17b417c | ||
|
|
473991638c | ||
|
|
92b4b69b3f | ||
|
|
dbd6f134c1 | ||
|
|
5f59487a88 | ||
|
|
bb7f673ff9 | ||
|
|
4715180a32 | ||
|
|
4b31610169 | ||
|
|
2466c5a204 | ||
|
|
0505baff38 | ||
|
|
c55e3a37ae | ||
|
|
ce5fec4d5f | ||
|
|
6d4339b034 | ||
|
|
70d0aae07c | ||
|
|
e3b9b341fd | ||
|
|
95083cf743 | ||
|
|
3842c4ac46 | ||
|
|
fe178043ee | ||
|
|
82d40f60f1 | ||
|
|
1d4d885276 | ||
|
|
ed3557ffca | ||
|
|
1c3878fcb0 | ||
|
|
b3dcb9fe6c | ||
|
|
0462755922 | ||
|
|
415dfd2750 | ||
|
|
72f203e4fa | ||
|
|
4e3c83af94 | ||
|
|
65a0f60257 | ||
|
|
094bdf02c4 | ||
|
|
5d8d12bc89 | ||
|
|
89b9fd8a45 | ||
|
|
ce1ba3bc2c | ||
|
|
3afb499930 | ||
|
|
356ff57005 | ||
|
|
16ce67057d | ||
|
|
4c9dc739ae | ||
|
|
a665607fac | ||
|
|
7f5671f975 |
113
.coderabbit.yaml
Normal file
113
.coderabbit.yaml
Normal file
@@ -0,0 +1,113 @@
|
||||
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
|
||||
#
|
||||
# CodeRabbit configuration — references existing guideline files to avoid
|
||||
# duplicating conventions. See:
|
||||
# .github/copilot-instructions.md — project overview & general rules
|
||||
# docs/cpp.instructions.md — C++ coding conventions
|
||||
# docs/web.instructions.md — Web UI coding conventions
|
||||
# docs/cicd.instructions.md — GitHub Actions / CI-CD conventions
|
||||
#
|
||||
# NOTE: This file must be committed (tracked by git) for CodeRabbit to read
|
||||
# it from the repository. If it is listed in .gitignore, CodeRabbit will
|
||||
# not see it and these settings will have no effect.
|
||||
|
||||
language: en-US
|
||||
|
||||
reviews:
|
||||
# generic review setting, see https://docs.coderabbit.ai/reference/configuration#reference
|
||||
auto_apply_labels: true
|
||||
# abort_on_close: false
|
||||
high_level_summary: true
|
||||
review_status: true
|
||||
collapse_walkthrough: false
|
||||
poem: false
|
||||
# sequence_diagrams: false
|
||||
auto_review:
|
||||
enabled: true
|
||||
ignore_title_keywords:
|
||||
- WIP
|
||||
|
||||
path_instructions:
|
||||
- path: "**/*.{cpp,h,hpp,ino}"
|
||||
instructions: >
|
||||
Follow the C++ coding conventions documented in docs/cpp.instructions.md
|
||||
and the general project guidelines in .github/copilot-instructions.md.
|
||||
|
||||
Key rules: 2-space indentation (no tabs), camelCase functions/variables,
|
||||
PascalCase classes, UPPER_CASE macros. No C++ exceptions — use return codes and debug macros.
|
||||
|
||||
Hot-path optimization guidelines (attributes, uint_fast types, caching,
|
||||
unsigned range checks) apply from pixel set/get operations and strip.show() downward —
|
||||
NOT to effect functions in FX.cpp, which have diverse contributor styles.
|
||||
# disabled - the below instruction has no effect
|
||||
# When initially reviewing a PR, summarize good practices (top 5) and create a prioritized list of suggested improvements (focus on major ones).
|
||||
|
||||
- path: "wled00/data/**"
|
||||
instructions: >
|
||||
Follow the web UI conventions documented in docs/web.instructions.md.
|
||||
|
||||
Key rules: indent HTML and JavaScript with tabs, CSS with tabs.
|
||||
Files here are built into wled00/html_*.h and wled00/js_*.h by tools/cdata.js — never
|
||||
edit those generated headers directly.
|
||||
# disabled - the below instruction has no effect
|
||||
# When initially reviewing a PR, summarize good practices (top 5) and create a prioritized list of suggested improvements (focus on major ones).
|
||||
|
||||
- path: "wled00/html_*.h"
|
||||
instructions: >
|
||||
These files are auto-generated from wled00/data/ by tools/cdata.js.
|
||||
They must never be manually edited or committed. Flag any PR that
|
||||
includes changes to these files.
|
||||
|
||||
- path: "wled00/js_*.h"
|
||||
instructions: >
|
||||
These files are auto-generated from wled00/data/ by tools/cdata.js.
|
||||
They must never be manually edited or committed. Flag any PR that
|
||||
includes changes to these files.
|
||||
|
||||
- path: "usermods/**"
|
||||
instructions: >
|
||||
Usermods are community add-ons.
|
||||
Each usermod lives in its own directory under usermods/ and is implemented
|
||||
as a .cpp file with a dedicated library.json file to manage dependencies.
|
||||
Follow the same C++ conventions as the core firmware (docs/cpp.instructions.md).
|
||||
# disabled - the below instruction has no effect
|
||||
# When initially reviewing a PR, summarize good practices (top 6) and create a prioritized list of suggested improvements (skip minor ones).
|
||||
|
||||
- path: ".github/workflows/*.{yml,yaml}"
|
||||
instructions: >
|
||||
Follow the CI/CD conventions documented in docs/cicd.instructions.md.
|
||||
|
||||
Key rules: 2-space indentation, descriptive name: on every workflow/job/step.
|
||||
Third-party actions must be pinned to a specific version tag — branch pins
|
||||
such as @main or @master are not allowed. Declare explicit permissions: blocks
|
||||
scoped to least privilege. Never interpolate github.event.* values directly
|
||||
into run: steps — pass them through an env: variable to prevent script
|
||||
injection. Do not use pull_request_target unless fully justified.
|
||||
# disabled - the below instruction has no effect
|
||||
# When initially reviewing a PR, summarize good practices (top 6) and create a prioritized list of suggested improvements.
|
||||
|
||||
- path: "**/*.instructions.md"
|
||||
instructions: |
|
||||
This file contains both AI-facing rules and human-only reference sections.
|
||||
Human-only sections are enclosed in `<!-- HUMAN_ONLY_START -->` /
|
||||
`<!-- HUMAN_ONLY_END -->` HTML comment markers and should not be used as
|
||||
actionable review criteria.
|
||||
|
||||
When this file is modified in a PR, perform the following alignment check:
|
||||
1. For each `<!-- HUMAN_ONLY_START --> ... <!-- HUMAN_ONLY_END -->` block,
|
||||
verify that its examples and guidance are consistent with (and do not
|
||||
contradict) the AI-facing rules stated in the same file.
|
||||
2. Flag any HUMAN_ONLY section whose content has drifted from the surrounding
|
||||
AI-facing rules due to edits introduced in this PR.
|
||||
3. If new AI-facing rules were added without updating a related HUMAN_ONLY
|
||||
reference section, note this as a suggestion (not a required fix).
|
||||
|
||||
finishing_touches:
|
||||
# Docstrings | Options for generating Docstrings for your PRs/MRs.
|
||||
docstrings:
|
||||
# Docstrings | Allow CodeRabbit to generate docstrings for PRs/MRs.
|
||||
# default: true - disabled in WLED: has caused confusion in the past
|
||||
enabled: false
|
||||
unit_tests:
|
||||
# default: true - disabled in WLED: we don't have a unit test framework, this option just confuses contributors
|
||||
enabled: false
|
||||
17
.devcontainer/Dockerfile
Normal file
17
.devcontainer/Dockerfile
Normal file
@@ -0,0 +1,17 @@
|
||||
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.148.1/containers/python-3/.devcontainer/base.Dockerfile
|
||||
|
||||
# [Choice] Python version: 3, 3.9, 3.8, 3.7, 3.6
|
||||
ARG VARIANT="3"
|
||||
FROM mcr.microsoft.com/devcontainers/python:0-${VARIANT}
|
||||
|
||||
# [Optional] If your pip requirements rarely change, uncomment this section to add them to the image.
|
||||
# COPY requirements.txt /tmp/pip-tmp/
|
||||
# RUN pip3 --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements.txt \
|
||||
# && rm -rf /tmp/pip-tmp
|
||||
|
||||
# [Optional] Uncomment this section to install additional OS packages.
|
||||
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
||||
# && apt-get -y install --no-install-recommends <your-package-list-here>
|
||||
|
||||
# [Optional] Uncomment this line to install global node packages.
|
||||
# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g <your-package-here>" 2>&1
|
||||
60
.devcontainer/devcontainer.json
Normal file
60
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"name": "Python 3",
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile",
|
||||
"context": "..",
|
||||
"args": {
|
||||
// Update 'VARIANT' to pick a Python version: 3, 3.6, 3.7, 3.8, 3.9
|
||||
"VARIANT": "3"
|
||||
}
|
||||
},
|
||||
|
||||
// To give the container access to a device serial port, you can uncomment one of the following lines.
|
||||
// Note: If running on Windows, you will have to do some additional steps:
|
||||
// https://stackoverflow.com/questions/68527888/how-can-i-use-a-usb-com-port-inside-of-a-vscode-development-container
|
||||
//
|
||||
// You can explicitly just forward the port you want to connect to. Replace `/dev/ttyACM0` with the serial port for
|
||||
// your device. This will only work if the device is plugged in from the start without reconnecting. Adding the
|
||||
// `dialout` group is needed if read/write permisions for the port are limitted to the dialout user.
|
||||
// "runArgs": ["--device=/dev/ttyACM0", "--group-add", "dialout"],
|
||||
//
|
||||
// Alternatively, you can give more comprehensive access to the host system. This will expose all the host devices to
|
||||
// the container. Adding the `dialout` group is needed if read/write permisions for the port are limitted to the
|
||||
// dialout user. This could allow the container to modify unrelated serial devices, which would be a similar level of
|
||||
// risk to running the build directly on the host.
|
||||
// "runArgs": ["--privileged", "-v", "/dev/bus/usb:/dev/bus/usb", "--group-add", "dialout"],
|
||||
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"settings": {
|
||||
"terminal.integrated.shell.linux": "/bin/bash",
|
||||
"python.pythonPath": "/usr/local/bin/python",
|
||||
"python.linting.enabled": true,
|
||||
"python.linting.pylintEnabled": true,
|
||||
"python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8",
|
||||
"python.formatting.blackPath": "/usr/local/py-utils/bin/black",
|
||||
"python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf",
|
||||
"python.linting.banditPath": "/usr/local/py-utils/bin/bandit",
|
||||
"python.linting.flake8Path": "/usr/local/py-utils/bin/flake8",
|
||||
"python.linting.mypyPath": "/usr/local/py-utils/bin/mypy",
|
||||
"python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle",
|
||||
"python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle",
|
||||
"python.linting.pylintPath": "/usr/local/py-utils/bin/pylint"
|
||||
},
|
||||
"extensions": [
|
||||
"ms-python.python",
|
||||
"platformio.platformio-ide"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
// "forwardPorts": [],
|
||||
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
"postCreateCommand": "bash -i -c 'nvm install && npm ci'",
|
||||
|
||||
// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
||||
"remoteUser": "vscode"
|
||||
}
|
||||
|
||||
2
.github/FUNDING.yml
vendored
Normal file
2
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
github: [DedeHai,lost-hope,willmmiles,netmindz,softhack007]
|
||||
custom: ['https://paypal.me/Aircoookie','https://paypal.me/blazoncek']
|
||||
92
.github/ISSUE_TEMPLATE/bug.yml
vendored
Normal file
92
.github/ISSUE_TEMPLATE/bug.yml
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
name: Bug Report
|
||||
description: File a bug report
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please quickly search existing issues first before submitting a bug.
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: What happened?
|
||||
description: A clear and concise description of what the bug is.
|
||||
placeholder: Tell us what the problem is.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: how-to-reproduce
|
||||
attributes:
|
||||
label: To Reproduce Bug
|
||||
description: Steps to reproduce the behavior, if consistently possible.
|
||||
placeholder: Tell us how to make the bug appear.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected-behavior
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: A clear and concise description of what you expected to happen.
|
||||
placeholder: Tell us what you expected to happen.
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: install_format
|
||||
attributes:
|
||||
label: Install Method
|
||||
description: How did you install WLED?
|
||||
options:
|
||||
- Binary from WLED.me
|
||||
- Self-Compiled
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: What version of WLED?
|
||||
description: |-
|
||||
Find this by going to <kbd><samp>⚙️ Config</samp></kbd> → <kbd><samp>Security & Updates</samp></kbd> → Scroll to Bottom.
|
||||
Copy and paste the rest of the line that begins “<samp>Installed version: </samp>”,
|
||||
or, for older versions, the entire line after “<samp>Server message</samp>”.
|
||||
placeholder: "e.g. WLED 0.13.1 (build 2203150)"
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: Board
|
||||
attributes:
|
||||
label: Which microcontroller/board are you seeing the problem on?
|
||||
multiple: true
|
||||
options:
|
||||
- ESP8266
|
||||
- ESP32
|
||||
- ESP32 with ethernet
|
||||
- ESP32-S2
|
||||
- ESP32-S3
|
||||
- ESP32-C3
|
||||
- ESP32-C5 (experimental)
|
||||
- ESP32-C6 (experimental)
|
||||
- ESP32-P4 (experimental)
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Relevant log/trace output
|
||||
description: Please copy and paste any relevant log output if you have it. This will be automatically formatted into code, so no need for backticks.
|
||||
render: shell
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Anything else?
|
||||
description: |
|
||||
Links? References? Anything that will give us more context about the issue you are encountering!
|
||||
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
|
||||
validations:
|
||||
required: false
|
||||
- type: checkboxes
|
||||
id: terms
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/wled-dev/WLED/blob/main/CODE_OF_CONDUCT.md)
|
||||
options:
|
||||
- label: I agree to follow this project's Code of Conduct
|
||||
required: true
|
||||
11
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
11
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: WLED Discord community
|
||||
url: https://discord.gg/KuqP7NE
|
||||
about: Please ask and answer questions and discuss setup issues here!
|
||||
- name: WLED community forum
|
||||
url: https://wled.discourse.group/
|
||||
about: For issues and ideas that might need longer discussion.
|
||||
- name: kno.wled.ge base
|
||||
url: https://kno.wled.ge/basics/faq/
|
||||
about: Take a look at the frequently asked questions and documentation, perhaps your question is already answered!
|
||||
22
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
22
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an improvement idea for WLED!
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
|
||||
Thank you for your ideas for making WLED better!
|
||||
120
.github/agent-build.instructions.md
vendored
Normal file
120
.github/agent-build.instructions.md
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
---
|
||||
applyTo: "**"
|
||||
---
|
||||
# Agent-Mode Build & Test Instructions
|
||||
|
||||
Detailed build workflow, timeouts, and troubleshooting for making code changes in agent mode. Always reference these instructions first when running builds or validating changes.
|
||||
|
||||
## Build Timing and Timeouts
|
||||
|
||||
Use these timeout values when running builds:
|
||||
|
||||
| Command | Typical Time | Minimum Timeout | Notes |
|
||||
|---|---|---|---|
|
||||
| `npm run build` | ~3 s | 30 s | Web UI → `wled00/html_*.h` `wled00/js_*.h` headers |
|
||||
| `npm test` | ~40 s | 2 min | Validates build system |
|
||||
| `npm run dev` | continuous | — | Watch mode, auto-rebuilds on changes |
|
||||
| `pio run -e <env>` | 15–20 min | 30 min | First build downloads toolchains; subsequent builds are faster |
|
||||
|
||||
**NEVER cancel long-running builds.** PlatformIO downloads and compilation require patience.
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Code Style Summary
|
||||
- **C++** files in `wled00/` and `usermods/`: 2-space indentation (no tabs), camelCase functions/variables, PascalCase classes, UPPER_CASE macros. No C++ exceptions — use return codes and debug macros.
|
||||
- **Web UI** files in `wled00/data`: indent HTML and JavaScript with tabs, CSS with tabs.
|
||||
- **CI/CD workflows** in `.github/workflows`: 2-space indentation, descriptive `name:` on every workflow/job/step. Third-party actions must be pinned to a specific version tag — branch pins such as `@main` or `@master` are not allowed. SHA pinning recommended.
|
||||
|
||||
### Web UI Changes
|
||||
|
||||
1. Edit files in `wled00/data/`
|
||||
2. Run `npm run build` to regenerate `wled00/html_*.h` `wled00/js_*.h` headers
|
||||
3. Test with local HTTP server (see Manual Testing below)
|
||||
4. Run `npm test` to validate
|
||||
|
||||
### Firmware Changes
|
||||
|
||||
1. Edit files in `wled00/` (but **never** `html_*.h` and `js_*.h` files)
|
||||
2. Ensure web UI is built first: `npm run build`
|
||||
3. Build firmware: `pio run -e esp32dev` (set timeout ≥ 30 min)
|
||||
4. Flash to device: `pio run -e [target] --target upload`
|
||||
|
||||
### Combined Web + Firmware Changes
|
||||
|
||||
1. Always build web UI first
|
||||
2. Test web interface manually
|
||||
3. Then build and test firmware
|
||||
|
||||
|
||||
## Before Finishing Work - Testing
|
||||
|
||||
**You MUST complete ALL of these before marking work as done:**
|
||||
|
||||
1. **Run tests**: `npm test` — must pass
|
||||
2. **Build firmware**: `pio run -e esp32dev` — must succeed after source code changes, **never skip this step**.
|
||||
- Set timeout to 30+ minutes, **never cancel**
|
||||
- Choose `esp32dev` as a common, representative environment
|
||||
- If the build fails, fix the issue before proceeding
|
||||
3. **For web UI changes**: manually test the interface (see below)
|
||||
|
||||
If any step fails, fix the issue. **Do NOT mark work complete with failing builds or tests.**
|
||||
|
||||
## Manual Web UI Testing
|
||||
|
||||
Start a local server:
|
||||
|
||||
```sh
|
||||
cd wled00/data && python3 -m http.server 8080
|
||||
# Open http://localhost:8080/index.htm
|
||||
```
|
||||
|
||||
Test these scenarios after every web UI change:
|
||||
|
||||
- **Load**: `index.htm` loads without JavaScript errors (check browser console)
|
||||
- **Navigation**: switching between main page and settings pages works
|
||||
- **Color controls**: color picker and brightness controls function correctly
|
||||
- **Effects**: effect selection and parameter changes work
|
||||
- **Settings**: form submission and validation work
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Build Issues
|
||||
|
||||
| Problem | Solution |
|
||||
|---|---|
|
||||
| Missing `html_*.h` | Run `npm ci; npm run build` |
|
||||
| Web UI looks broken | Check browser console for JS errors |
|
||||
| PlatformIO network errors | Retry — downloads can be flaky |
|
||||
| Node.js version mismatch | Ensure Node.js 20+ (check `.nvmrc`) |
|
||||
|
||||
### Recovery Steps
|
||||
|
||||
- **Force web UI rebuild**: `npm run build -- -f`
|
||||
- **Clear generated files**: `rm -f wled00/html_*.h wled00/js_*.h` then `npm run build`
|
||||
- **Clean PlatformIO build artifacts**: `pio run --target clean`
|
||||
- **Reinstall Node deps**: `rm -rf node_modules && npm ci`
|
||||
|
||||
## CI/CD Validation
|
||||
|
||||
The GitHub Actions CI workflow will:
|
||||
1. Install Node.js and Python dependencies
|
||||
2. Run `npm test`
|
||||
3. Build web UI (automatic via PlatformIO)
|
||||
4. Compile firmware for **all** `default_envs` targets
|
||||
|
||||
**To ensure CI success, always validate locally:**
|
||||
- Run `npm test` and ensure it passes
|
||||
- Run `pio run -e esp32dev` (or another common firmware environment, see next section) and ensure it completes successfully
|
||||
- If either fails locally, it WILL fail in CI
|
||||
|
||||
Match this workflow in local development to catch failures before pushing.
|
||||
|
||||
## Important Reminders
|
||||
|
||||
- Always **commit source code**
|
||||
- Every pull request MUST include a clear description of *what* changed and *why*.
|
||||
- **Never edit or commit** `wled00/html_*.h` and `wled00/js_*.h` — auto-generated from `wled00/data/`
|
||||
- After modifying source code files, check that any **previous comments have been preserved** or updated to reflect the new behaviour.
|
||||
- Web UI rebuild is part of the PlatformIO firmware compilation pipeline
|
||||
- Common environments: `nodemcuv2`, `esp32dev`, `esp8266_2m`, `esp32c3dev`, `esp32s3dev_8MB_opi`
|
||||
- List all PlatformIO targets: `pio run --list-targets`
|
||||
139
.github/copilot-instructions.md
vendored
Normal file
139
.github/copilot-instructions.md
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
# WLED - ESP32/ESP8266 LED Controller Firmware
|
||||
|
||||
WLED is a fast and feature-rich implementation of an ESP32 and ESP8266 webserver to control NeoPixel (WS2812B, WS2811, SK6812) LEDs and SPI-based chipsets. The project consists of C++ firmware for microcontrollers and a modern web interface.
|
||||
|
||||
Always reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here.
|
||||
|
||||
> **Note for AI review tools**: sections enclosed in
|
||||
> `<!-- HUMAN_ONLY_START -->` / `<!-- HUMAN_ONLY_END -->` HTML comments contain
|
||||
> contributor reference material. Do **not** use that content as actionable review
|
||||
> criteria — treat it as background context only.
|
||||
|
||||
## Setup
|
||||
|
||||
- Node.js 20+ (see `.nvmrc`)
|
||||
- Install dependencies: `npm ci`
|
||||
- PlatformIO (required only for firmware compilation): `pip install -r requirements.txt`
|
||||
|
||||
## Build and Test
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
|
||||
| Command | Purpose | Typical Time |
|
||||
|---|---|---|
|
||||
| `npm run build` | Build web UI → generates `wled00/html_*.h` and `wled00/js_*.h` headers | ~3 s |
|
||||
| `npm test` | Run test suite | ~40 s |
|
||||
| `npm run dev` | Watch mode — auto-rebuilds web UI on file changes | — |
|
||||
| `pio run -e <env>` | Build firmware for a hardware target | 15–20 min |
|
||||
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
|
||||
- **Always run `npm run build` before any `pio run`** (and run `npm ci` first on fresh clones or when lockfile/dependencies change).
|
||||
- The web UI build generates required `wled00/html_*.h` and `wled00/js_*.h` headers for firmware compilation.
|
||||
- **Build firmware to validate code changes**: `pio run -e esp32dev` — must succeed, never skip this step.
|
||||
- Common firmware environments: `nodemcuv2`, `esp32dev`, `esp8266_2m`, `esp32c3dev`, `esp32s3dev_8MB_opi`
|
||||
|
||||
For detailed build timeouts, development workflows, troubleshooting, and validation steps, see [agent-build.instructions.md](agent-build.instructions.md).
|
||||
|
||||
### Usermod Guidelines
|
||||
|
||||
- New custom effects can be added into the user_fx usermod. Read the [user_fx documentation](https://github.com/wled/WLED/blob/main/usermods/user_fx/README.md) for guidance.
|
||||
- Other usermods may be based on the [EXAMPLE usermod](https://github.com/wled/WLED/tree/main/usermods/EXAMPLE). Never edit the example, always create a copy!
|
||||
- New usermod IDs can be added into [wled00/const.h](https://github.com/wled/WLED/blob/main/wled00/const.h#L160).
|
||||
- To activate a usermod, a custom build configuration should be used. Add the usermod name to `custom_usermods`.
|
||||
|
||||
## Project Structure Overview
|
||||
|
||||
### Project Branch / Release Structure
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
|
||||
```text
|
||||
main # Main development trunk (daily/nightly) 17.0.0-dev
|
||||
├── V5 # special branch: code rework for esp-idf 5.5.x (unstable)
|
||||
├── V5-C6 # special branch: integration of new MCU types: esp32-c5, esp32-c6, esp32-p4 (unstable)
|
||||
16_x # current beta, preparations for next release 16.0.0
|
||||
0_15_x # maintenance (bugfixes only) for current release 0.15.4
|
||||
(tag) v0.14.4 # previous version 0.14.4 (no maintenance)
|
||||
(tag) v0.13.3 # old version 0.13.3 (no maintenance)
|
||||
(tag) v0. ... . ... # historical versions 0.12.x and before
|
||||
```
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
|
||||
- ``main``: development trunk (daily/nightly)
|
||||
- ``V5`` and ``V5-C6``: code rework for esp-idf 5.5.x (unstable) - branched from ``main``.
|
||||
- ``0_15_x``: bugfixing / maintenance for release 0.15.x
|
||||
|
||||
### Repository Structure
|
||||
|
||||
tl;dr:
|
||||
* Firmware source: `wled00/` (C++). Web UI source: `wled00/data/`. Build targets: `platformio.ini`.
|
||||
* Auto-generated headers: `wled00/html_*.h` and `wled00/js_*.h` — **never edit or commit**.
|
||||
* ArduinoJSON + AsyncJSON: `wled00/src/dependencies/json` (included via `wled.h`). CI/CD: `.github/workflows/`.
|
||||
* Usermods: `usermods/` (C++, with individual library.json).
|
||||
* Contributor docs: `docs/` (coding guidelines, etc).
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
Detailed overview:
|
||||
|
||||
```text
|
||||
wled00/ # Main firmware source (C++) "WLED core"
|
||||
├── data/ # Web interface files
|
||||
│ ├── index.htm # Main UI
|
||||
│ ├── settings*.htm # Settings pages
|
||||
│ └── *.js/*.css # Frontend resources
|
||||
├── *.cpp/*.h # Firmware source files
|
||||
├── html_*.h # Auto-generated embedded web files (DO NOT EDIT, DO NOT COMMIT)
|
||||
├── src/ # Modules used by the WLED core (C++)
|
||||
│ ├── fonts/ # Font libraries for scrolling text effect
|
||||
└ └── dependencies/ # Utility functions - some of them have their own licensing terms
|
||||
lib/ # Project specific custom libraries. PlatformIO will compile them to separate static libraries and link them
|
||||
platformio.ini # Hardware build configuration
|
||||
|
||||
platformio_override.sample.ini # examples for custom build configurations - entries must be copied into platformio_override.ini to use them.
|
||||
# platformio_override.ini is _not_ stored in the WLED repository!
|
||||
usermods/ # User-contributed addons to the WLED core, maintained by individual contributors (C++, with individual library.json)
|
||||
package.json # Node.js dependencies and scripts, release identification
|
||||
pio-scripts/ # Build tools (PlatformIO)
|
||||
tools/ # Build tools (Node.js), partition files, and generic utilities
|
||||
├── cdata.js # Web UI build script
|
||||
└── cdata-test.js # Test suite
|
||||
docs/ # Contributor docs, coding guidelines
|
||||
.github/workflows/ # CI/CD pipelines
|
||||
```
|
||||
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
## General Guidelines
|
||||
|
||||
- **Repository language is English.** Suggest translations for non-English content.
|
||||
- **Use VS Code with PlatformIO extension** for best development experience.
|
||||
- **Never edit or commit** `wled00/html_*.h` and `wled00/js_*.h` — auto-generated from `wled00/data/`.
|
||||
- If updating Web UI files in `wled00/data/`, **make use of common functions in `wled00/data/common.js` whenever possible**.
|
||||
- **When unsure, say so.** Gather more information rather than guessing.
|
||||
- **Acknowledge good patterns** when you see them. Positive feedback always helps.
|
||||
- **Provide references** when making analyses or recommendations. Base them on the correct branch or PR.
|
||||
- **Highlight user-visible breaking changes and ripple effects**. Ask for confirmation that these were introduced intentionally.
|
||||
- **Unused / dead code must be justified or removed**. This helps to keep the codebase clean, maintainable and readable.
|
||||
- **Verify feature-flag names.** Every `WLED_ENABLE_*` / `WLED_DISABLE_*` flag must exactly match one of the names below — misspellings are silently ignored by the preprocessor (e.g. `WLED_IR_DISABLE` instead of `WLED_DISABLE_INFRARED`), causing silent build variations. Flag unrecognised names as likely typos and suggest the correct spelling.
|
||||
<br>**`WLED_DISABLE_*`**: `2D`, `ADALIGHT`, `ALEXA`, `BROWNOUT_DET`, `ESPNOW`, `FILESYSTEM`, `HUESYNC`, `IMPROV_WIFISCAN`, `INFRARED`, `LOXONE`, `MQTT`, `OTA`, `PARTICLESYSTEM1D`, `PARTICLESYSTEM2D`, `PIXELFORGE`, `WEBSOCKETS`
|
||||
<br>**`WLED_ENABLE_*`**: `ADALIGHT`, `AOTA`, `DMX`, `DMX_INPUT`, `DMX_OUTPUT`, `FS_EDITOR`, `GIF`, `HUB75MATRIX`, `JSONLIVE`, `LOXONE`, `MQTT`, `PIXART`, `PXMAGIC`, `USERMOD_PAGE`, `WEBSOCKETS`, `WPA_ENTERPRISE`
|
||||
- **C++ formatting available**: `clang-format` is installed but not in CI
|
||||
- No automated linting is configured — match existing code style in files you edit.
|
||||
|
||||
Refer to `docs/cpp.instructions.md` and `docs/web.instructions.md` for language-specific conventions, and `docs/cicd.instructions.md` for GitHub Actions workflows.
|
||||
|
||||
### Attribution for AI-generated code
|
||||
Using AI-generated code can hide the source of the inspiration / knowledge / sources it used.
|
||||
- Document attribution of inspiration / knowledge / sources used in the code, e.g. link to GitHub repositories or other websites describing the principles / algorithms used.
|
||||
- When a larger block of code is generated by an AI tool, embed it into `// AI: below section was generated by an AI` ... `// AI: end` comments (see C++ guidelines).
|
||||
- Every non-trivial AI-generated function should have a brief comment describing what it does. Explain parameters when their names alone are not self-explanatory.
|
||||
- AI-generated code must be well documented with meaningful comments that explain intent, assumptions, and non-obvious logic. Do not rephrase source code; explain concepts and reasoning.
|
||||
|
||||
### Pull Request Expectations
|
||||
|
||||
- **No force-push on open PRs.** Once a pull request is open and being reviewed, do not force-push (`git push --force`) to the branch. Force-pushing rewrites history that reviewers may have already commented on, making it impossible to track incremental changes. Use regular commits or `git merge` to incorporate feedback; the branch will be squash-merged when it is accepted.
|
||||
- **Modifications to ``platformio.ini`` MUST be approved explicitly** by a *maintainer* or *WLED organisation Member*. Modifications to the global build environment may break github action builds. Always flag them.
|
||||
- **Document your changes in the PR.** Every pull request should include a clear description of *what* changed and *why*. If the change affects user-visible behavior, describe the expected impact. Link to related issues where applicable. Provide screenshots to showcase new features.
|
||||
|
||||
### Supporting Reviews and Discussions
|
||||
- **For "is it worth doing?" debates** about proposed reliability, safety, or data-integrity mechanisms (CRC checks, backups, power-loss protection): suggest a software **FMEA** (Failure Mode and Effects Analysis).
|
||||
Clarify the main feared events, enumerate failure modes, assess each mitigation's effectiveness per failure mode, note common-cause failures, and rate credibility for the typical WLED use case.
|
||||
|
||||
45
.github/platformio_release.ini.template
vendored
Normal file
45
.github/platformio_release.ini.template
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
; ----------------------------------------------------------------------------
|
||||
; platformio_release.ini.template
|
||||
; ----------------------------------------------------------------------------
|
||||
; Copied to platformio_release.ini by the release CI workflow
|
||||
; (.github/workflows/release.yml -> build.yml with `release: true`)
|
||||
; in order to extend the matrix of `default_envs` built and published
|
||||
; for tagged releases.
|
||||
;
|
||||
; This file overrides `[platformio].default_envs` from platformio.ini via
|
||||
; `extra_configs`. It MUST list every env that should be released - including
|
||||
; the regular CI default_envs - because it fully replaces the parent value.
|
||||
;
|
||||
; Do NOT commit a generated platformio_release.ini (it's in .gitignore).
|
||||
; ----------------------------------------------------------------------------
|
||||
|
||||
[platformio]
|
||||
default_envs = nodemcuv2
|
||||
esp8266_2m
|
||||
esp8266_2m_min
|
||||
esp01_1m_full
|
||||
nodemcuv2_160
|
||||
esp8266_2m_160
|
||||
esp01_1m_full_160
|
||||
nodemcuv2_compat
|
||||
esp8266_2m_compat
|
||||
esp01_1m_full_compat
|
||||
esp32dev
|
||||
esp32dev_debug
|
||||
esp32_eth
|
||||
esp32_wrover
|
||||
lolin_s2_mini
|
||||
esp32c3dev
|
||||
esp32c3dev_qio
|
||||
esp32S3_wroom2
|
||||
esp32s3dev_16MB_opi
|
||||
esp32s3dev_8MB_opi
|
||||
esp32s3dev_8MB_qspi
|
||||
esp32s3_4M_qspi
|
||||
usermods
|
||||
; HUB75 release-only envs
|
||||
esp32dev_hub75
|
||||
esp32dev_hub75_forum_pinout
|
||||
adafruit_matrixportal_esp32s3
|
||||
esp32s3dev_16MB_opi_hub75 ;; MoonHub
|
||||
esp32s3dev_4MB_qspi_hub75 ;; HD-WF2
|
||||
111
.github/workflows/build.yml
vendored
Normal file
111
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
name: WLED Build
|
||||
|
||||
# Only included into other workflows
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
release:
|
||||
description: 'Build the release env matrix (uses .github/platformio_release.ini.template)'
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
jobs:
|
||||
|
||||
get_default_envs:
|
||||
name: Gather Environments
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Apply release config
|
||||
if: inputs.release
|
||||
run: cp .github/platformio_release.ini.template platformio_release.ini
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
cache: 'pip'
|
||||
- name: Install PlatformIO
|
||||
run: pip install -r requirements.txt
|
||||
- name: Get default environments
|
||||
id: envs
|
||||
run: |
|
||||
echo "environments=$(pio project config --json-output | jq -cr '.[0][1][0][1]')" >> $GITHUB_OUTPUT
|
||||
outputs:
|
||||
environments: ${{ steps.envs.outputs.environments }}
|
||||
|
||||
|
||||
build:
|
||||
name: Build Environments
|
||||
runs-on: ubuntu-latest
|
||||
needs: get_default_envs
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
environment: ${{ fromJSON(needs.get_default_envs.outputs.environments) }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Apply release config
|
||||
if: inputs.release
|
||||
run: cp .github/platformio_release.ini.template platformio_release.ini
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
- run: |
|
||||
npm ci
|
||||
VERSION=`date +%y%m%d0`
|
||||
sed -i -r -e "s/define VERSION .+/define VERSION $VERSION/" wled00/wled.h
|
||||
- name: Cache PlatformIO
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.platformio/.cache
|
||||
~/.buildcache
|
||||
build_output
|
||||
key: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', '.github/platformio_release.ini.template', 'pio-scripts/output_bins.py') }}-${{ hashFiles('wled00/**', 'usermods/**') }}
|
||||
restore-keys: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', '.github/platformio_release.ini.template', 'pio-scripts/output_bins.py') }}-
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
cache: 'pip'
|
||||
- name: Install PlatformIO
|
||||
run: pip install -r requirements.txt
|
||||
|
||||
- name: Build firmware
|
||||
run: pio run -e ${{ matrix.environment }}
|
||||
- name: Get artifact name from bin filename
|
||||
id: artifact_name
|
||||
run: |
|
||||
bin=$(ls build_output/release/*.bin 2>/dev/null | head -1)
|
||||
if [ -n "$bin" ]; then
|
||||
# Strip WLED_<version>_ prefix from WLED_<version>_<RELEASE_NAME>.bin
|
||||
base=$(basename "$bin" .bin)
|
||||
release_name=$(echo "$base" | sed 's/^[^_]*_[^_]*_//')
|
||||
echo "name=firmware-$release_name" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "name=firmware-${{ matrix.environment }}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ steps.artifact_name.outputs.name }}
|
||||
path: |
|
||||
build_output/release/*.bin
|
||||
build_output/release/*_ESP02*.bin.gz
|
||||
|
||||
|
||||
testCdata:
|
||||
name: Test cdata.js
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
- run: npm ci
|
||||
- run: npm test
|
||||
53
.github/workflows/nightly.yml
vendored
Normal file
53
.github/workflows/nightly.yml
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
|
||||
name: Deploy Nightly
|
||||
on:
|
||||
# This can be used to automatically publish nightlies at UTC nighttime
|
||||
schedule:
|
||||
- cron: '0 2 * * *' # run at 2 AM UTC
|
||||
# This can be used to allow manually triggering nightlies from the web interface
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
jobs:
|
||||
wled_build:
|
||||
uses: ./.github/workflows/build.yml
|
||||
nightly:
|
||||
name: Deploy nightly
|
||||
runs-on: ubuntu-latest
|
||||
needs: wled_build
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
merge-multiple: true
|
||||
- name: Show Files
|
||||
run: ls -la
|
||||
- name: "✏️ Generate release changelog"
|
||||
id: changelog
|
||||
uses: janheinrichmerker/action-github-changelog-generator@v2.4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
sinceTag: v16.0.0
|
||||
output: CHANGELOG_NIGHTLY.md
|
||||
# Exclude issues that were closed without resolution from changelog
|
||||
excludeLabels: 'stale,wontfix,duplicate,invalid,external,question,use-as-is,not_planned'
|
||||
- name: Update Nightly Release
|
||||
uses: andelf/nightly-release@main
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: nightly
|
||||
name: 'Nightly Release $$'
|
||||
prerelease: true
|
||||
body_path: CHANGELOG_NIGHTLY.md
|
||||
files: |
|
||||
*.bin
|
||||
*.bin.gz
|
||||
- name: Repository Dispatch
|
||||
uses: peter-evans/repository-dispatch@v3
|
||||
with:
|
||||
repository: wled/WLED-WebInstaller
|
||||
event-type: release-nightly
|
||||
token: ${{ secrets.PAT_PUBLIC }}
|
||||
38
.github/workflows/pr-merge.yaml
vendored
Normal file
38
.github/workflows/pr-merge.yaml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: Notify Discord on PR Merge
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request_target:
|
||||
types: [closed]
|
||||
|
||||
jobs:
|
||||
notify:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.pull_request.merged == true
|
||||
steps:
|
||||
- name: Get User Permission
|
||||
id: checkAccess
|
||||
uses: actions-cool/check-user-permission@v2
|
||||
with:
|
||||
require: write
|
||||
username: ${{ github.triggering_actor }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Check User Permission
|
||||
if: steps.checkAccess.outputs.require-result == 'false'
|
||||
run: |
|
||||
echo "${{ github.triggering_actor }} does not have permissions on this repo."
|
||||
echo "Current permission level is ${{ steps.checkAccess.outputs.user-permission }}"
|
||||
echo "Job originally triggered by ${{ github.actor }}"
|
||||
exit 1
|
||||
- name: Send Discord notification
|
||||
env:
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
PR_TITLE: ${{ github.event.pull_request.title }}
|
||||
PR_URL: ${{ github.event.pull_request.html_url }}
|
||||
ACTOR: ${{ github.actor }}
|
||||
run: |
|
||||
jq -n \
|
||||
--arg content "Pull Request #${PR_NUMBER} \"${PR_TITLE}\" merged by ${ACTOR}
|
||||
${PR_URL} . It will be included in the next nightly builds, please test" \
|
||||
'{content: $content}' \
|
||||
| curl -H "Content-Type: application/json" -d @- ${{ secrets.DISCORD_WEBHOOK_BETA_TESTERS }}
|
||||
48
.github/workflows/release.yml
vendored
Normal file
48
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
name: WLED Release CI
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
jobs:
|
||||
|
||||
wled_build:
|
||||
uses: ./.github/workflows/build.yml
|
||||
with:
|
||||
release: true
|
||||
|
||||
release:
|
||||
name: Create Release
|
||||
runs-on: ubuntu-latest
|
||||
needs: wled_build
|
||||
steps:
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
merge-multiple: true
|
||||
- name: Create draft release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
body: "Release assets uploaded. Release notes pending..."
|
||||
draft: True
|
||||
files: |
|
||||
*.bin
|
||||
*.bin.gz
|
||||
- name: "✏️ Generate release changelog"
|
||||
id: changelog
|
||||
uses: janheinrichmerker/action-github-changelog-generator@v2.4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
sinceTag: v0.15.0
|
||||
maxIssues: 500
|
||||
# Exclude issues that were closed without resolution from changelog
|
||||
excludeLabels: 'stale,wontfix,duplicate,invalid,external,question,use-as-is,not_planned'
|
||||
- name: Update release description
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
body: ${{ steps.changelog.outputs.changelog }}
|
||||
draft: True
|
||||
|
||||
30
.github/workflows/stale.yml
vendored
Normal file
30
.github/workflows/stale.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
name: 'Close stale issues and PRs'
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 12 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
days-before-stale: 120
|
||||
days-before-close: 7
|
||||
stale-issue-label: 'stale'
|
||||
stale-pr-label: 'stale'
|
||||
exempt-issue-labels: 'pinned,keep,enhancement,confirmed'
|
||||
exempt-pr-labels: 'pinned,keep,enhancement,confirmed'
|
||||
exempt-all-milestones: true
|
||||
operations-per-run: 1000
|
||||
stale-issue-message: >
|
||||
Hey! This issue has been open for quite some time without any new comments now.
|
||||
It will be closed automatically in a week if no further activity occurs.
|
||||
|
||||
Thank you for using WLED! ✨
|
||||
stale-pr-message: >
|
||||
Hey! This pull request has been open for quite some time without any new comments now.
|
||||
It will be closed automatically in a week if no further activity occurs.
|
||||
|
||||
Thank you for contributing to WLED! ❤️
|
||||
13
.github/workflows/test.yaml
vendored
Normal file
13
.github/workflows/test.yaml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
dispatch:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Repository Dispatch
|
||||
uses: peter-evans/repository-dispatch@v3
|
||||
with:
|
||||
repository: wled/WLED-WebInstaller
|
||||
event-type: release-nightly
|
||||
token: ${{ secrets.PAT_PUBLIC }}
|
||||
77
.github/workflows/usermods.yml
vendored
Normal file
77
.github/workflows/usermods.yml
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
name: Usermod CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- usermods/**
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
jobs:
|
||||
|
||||
get_usermod_envs:
|
||||
# Only run for pull requests from forks (not from branches within wled/WLED)
|
||||
if: github.event.pull_request.head.repo.full_name != github.repository
|
||||
name: Gather Usermods
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
cache: 'pip'
|
||||
- name: Install PlatformIO
|
||||
run: pip install -r requirements.txt
|
||||
- name: Get default environments
|
||||
id: envs
|
||||
run: |
|
||||
echo "usermods=$(find usermods/ -name library.json | xargs dirname | xargs -n 1 basename | jq -R | grep -v PWM_fan | grep -v BME68X_v2| grep -v pixels_dice_tray | jq --slurp -c)" >> $GITHUB_OUTPUT
|
||||
outputs:
|
||||
usermods: ${{ steps.envs.outputs.usermods }}
|
||||
|
||||
|
||||
build:
|
||||
# Only run for pull requests from forks (not from branches within wled/WLED)
|
||||
if: github.event.pull_request.head.repo.full_name != github.repository
|
||||
name: Build Enviornments
|
||||
runs-on: ubuntu-latest
|
||||
needs: get_usermod_envs
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
usermod: ${{ fromJSON(needs.get_usermod_envs.outputs.usermods) }}
|
||||
environment: [usermods_esp32, usermods_esp32c3, usermods_esp32s2, usermods_esp32s3]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
- run: npm ci
|
||||
- name: Cache PlatformIO
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.platformio/.cache
|
||||
~/.buildcache
|
||||
build_output
|
||||
key: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-${{ hashFiles('wled00/**', 'usermods/**') }}
|
||||
restore-keys: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
cache: 'pip'
|
||||
- name: Install PlatformIO
|
||||
run: pip install -r requirements.txt
|
||||
- name: Add usermods environment
|
||||
run: |
|
||||
cp -v usermods/platformio_override.usermods.ini platformio_override.ini
|
||||
echo >> platformio_override.ini
|
||||
echo "custom_usermods = ${{ matrix.usermod }}" >> platformio_override.ini
|
||||
cat platformio_override.ini
|
||||
|
||||
- name: Build firmware
|
||||
run: pio run -e ${{ matrix.environment }}
|
||||
11
.github/workflows/wled-ci.yml
vendored
Normal file
11
.github/workflows/wled-ci.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
name: WLED CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
wled_build:
|
||||
uses: ./.github/workflows/build.yml
|
||||
33
.gitignore
vendored
Normal file
33
.gitignore
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
.cache
|
||||
.clang-format
|
||||
.direnv
|
||||
.DS_Store
|
||||
.idea
|
||||
.pio
|
||||
.pioenvs
|
||||
.piolibdeps
|
||||
.vscode
|
||||
compile_commands.json
|
||||
__pycache__/
|
||||
|
||||
/.dummy
|
||||
/dependencies.lock
|
||||
/managed_components
|
||||
|
||||
esp01-update.sh
|
||||
platformio_override.ini
|
||||
platformio_release.ini
|
||||
replace_fs.py
|
||||
wled-update.sh
|
||||
|
||||
/build_output/
|
||||
/node_modules/
|
||||
/logs/
|
||||
|
||||
/wled00/extLibs
|
||||
/wled00/LittleFS
|
||||
/wled00/my_config.h
|
||||
/wled00/Release
|
||||
/wled00/wled00.ino.cpp
|
||||
/wled00/html_*.h
|
||||
/wled00/js_*.h
|
||||
3
.gitpod.Dockerfile
vendored
Normal file
3
.gitpod.Dockerfile
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
FROM gitpod/workspace-full
|
||||
|
||||
USER gitpod
|
||||
11
.gitpod.yml
Normal file
11
.gitpod.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
tasks:
|
||||
- command: pip3 install -U platformio && platformio run
|
||||
|
||||
image:
|
||||
file: .gitpod.Dockerfile
|
||||
|
||||
vscode:
|
||||
extensions:
|
||||
- Atishay-Jain.All-Autocomplete
|
||||
- esbenp.prettier-vscode
|
||||
- shardulm94.trailing-spaces
|
||||
10
.vscode/extensions.json
vendored
Normal file
10
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
],
|
||||
"unwantedRecommendations": [
|
||||
"ms-vscode.cpptools-extension-pack"
|
||||
]
|
||||
}
|
||||
42
.vscode/tasks.json
vendored
Normal file
42
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Build: HTML and binary",
|
||||
"dependsOn": [
|
||||
"Build: HTML only",
|
||||
"Build: binary only"
|
||||
],
|
||||
"dependsOrder": "sequence",
|
||||
"problemMatcher": [
|
||||
"$platformio"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "PlatformIO",
|
||||
"label": "Build: binary only",
|
||||
"task": "Build",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$platformio"
|
||||
],
|
||||
"presentation": {
|
||||
"panel": "shared"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "build",
|
||||
"group": "build",
|
||||
"problemMatcher": [],
|
||||
"label": "Build: HTML only",
|
||||
"detail": "npm run build",
|
||||
"presentation": {
|
||||
"panel": "shared"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
186
AGENTS.md
Normal file
186
AGENTS.md
Normal file
@@ -0,0 +1,186 @@
|
||||
# AGENTS.md — WLED Coding Agent Reference
|
||||
|
||||
WLED is C++ firmware for ESP32/ESP8266 microcontrollers controlling addressable LEDs,
|
||||
with a web UI (HTML/JS/CSS). Built with PlatformIO (Arduino framework) and Node.js tooling.
|
||||
|
||||
See also: `.github/copilot-instructions.md`, `.github/agent-build.instructions.md`,
|
||||
`docs/cpp.instructions.md`, `docs/web.instructions.md`, `docs/cicd.instructions.md`.
|
||||
|
||||
## Build Commands
|
||||
|
||||
| Command | Purpose | Timeout |
|
||||
|---|---|---|
|
||||
| `npm ci` | Install Node.js deps (required first) | 30s |
|
||||
| `npm run build` | Build web UI into `wled00/html_*.h` / `wled00/js_*.h` | 30s |
|
||||
| `npm test` | Run test suite (Node.js built-in `node --test`) | 2 min |
|
||||
| `npm run dev` | Watch mode — auto-rebuilds web UI on changes | continuous |
|
||||
| `pio run -e esp32dev` | Build firmware (ESP32, most common target) | 5 min |
|
||||
| `pio run -e nodemcuv2` | Build firmware (ESP8266) | 5 min |
|
||||
|
||||
**Always run `npm ci && npm run build` before `pio run`.** The web UI build generates
|
||||
required C headers for firmware compilation.
|
||||
|
||||
### Running a Single Test
|
||||
|
||||
Tests use Node.js built-in test runner (`node:test`). The single test file is
|
||||
`tools/cdata-test.js`. Run it with:
|
||||
|
||||
```sh
|
||||
npm test # runs all tests via `node --test`
|
||||
node --test tools/cdata-test.js # run just that file directly
|
||||
```
|
||||
|
||||
There are no C++ unit tests. Firmware is validated by successful compilation across
|
||||
target environments. Always build after code changes: `pio run -e esp32dev`.
|
||||
|
||||
### Common Firmware Environments
|
||||
|
||||
`esp32dev`, `nodemcuv2`, `esp8266_2m`, `esp32c3dev`, `esp32s3dev_8MB_opi`, `lolin_s2_mini`
|
||||
|
||||
### Recovery / Troubleshooting
|
||||
|
||||
```sh
|
||||
npm run build -- -f # force web UI rebuild
|
||||
rm -f wled00/html_*.h wled00/js_*.h && npm run build # clean + rebuild UI
|
||||
pio run --target clean # clean PlatformIO build artifacts
|
||||
rm -rf node_modules && npm ci # reinstall Node.js deps
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
wled00/ # Main firmware source (C++)
|
||||
data/ # Web UI source (HTML/JS/CSS) — tabs for indentation
|
||||
html_*.h, js_*.h # Auto-generated (NEVER edit or commit)
|
||||
src/ # Sub-modules: fonts, bundled dependencies (ArduinoJSON)
|
||||
usermods/ # Community usermods (each has library.json + .cpp/.h)
|
||||
platformio.ini # Build configuration and environments
|
||||
pio-scripts/ # PlatformIO build scripts (Python)
|
||||
tools/ # Node.js build tools (cdata.js) and tests
|
||||
docs/ # Coding convention docs
|
||||
.github/workflows/ # CI/CD (GitHub Actions)
|
||||
```
|
||||
|
||||
## C++ Code Style (wled00/, usermods/)
|
||||
|
||||
### Formatting
|
||||
- **2-space indentation** (no tabs in C++ files)
|
||||
- K&R brace style preferred (opening brace on same line)
|
||||
- Single-statement `if` bodies may omit braces: `if (a == b) doStuff(a);`
|
||||
- Space after keywords (`if (...)`, `for (...)`), no space before function parens (`doStuff(a)`)
|
||||
- No enforced line-length limit
|
||||
|
||||
### Naming Conventions
|
||||
| Kind | Convention | Examples |
|
||||
|---|---|---|
|
||||
| Functions, variables | camelCase | `setRandomColor()`, `effectCurrent` |
|
||||
| Classes, structs | PascalCase | `BusConfig`, `UsermodTemperature` |
|
||||
| Macros, constants | UPPER_CASE | `WLED_MAX_USERMODS`, `FX_MODE_STATIC` |
|
||||
| Private members | _camelCase | `_type`, `_bri`, `_len` |
|
||||
| Enum values | PascalCase | `PinOwner::BusDigital` |
|
||||
|
||||
### Includes
|
||||
- Include `"wled.h"` as the primary project header
|
||||
- Project headers first, then platform/Arduino, then third-party
|
||||
- Platform-conditional includes wrapped in `#ifdef ARDUINO_ARCH_ESP32` / `#ifdef ESP8266`
|
||||
|
||||
### Types and Const
|
||||
- Prefer `const &` for read-only function parameters
|
||||
- Mark getter/query methods `const`; use `static` for methods not accessing instance state
|
||||
- Prefer `constexpr` over `#define` for compile-time constants when possible
|
||||
- Use `static_assert` over `#if ... #error`
|
||||
- Use `uint_fast16_t` / `uint_fast8_t` in hot-path code
|
||||
|
||||
### Error Handling
|
||||
- **No C++ exceptions** — some builds disable them
|
||||
- Use return codes (`false`, `-1`) and global flags (`errorFlag = ERR_LOW_MEM`)
|
||||
- Use early returns as guard clauses: `if (!enabled || (strip.isUpdating() && (millis() - last_time < MAX_USERMOD_DELAY))) return;`
|
||||
- Debug output: `DEBUG_PRINTF()` / `DEBUG_PRINTLN()` (compiled out unless `-D WLED_DEBUG`)
|
||||
|
||||
### Strings and Memory
|
||||
- Use `F("string")` for string constants (saves RAM on ESP8266)
|
||||
- Use `PSTR()` with `DEBUG_PRINTF_P()` for format strings
|
||||
- Avoid `String` in hot paths; acceptable in config/setup code
|
||||
- Use `d_malloc()` (DRAM-preferred) / `p_malloc()` (PSRAM-preferred) for allocation
|
||||
- No VLAs — use fixed arrays or heap allocation
|
||||
- Call `reserve()` on strings/vectors to pre-allocate and avoid fragmentation
|
||||
|
||||
### Preprocessor / Feature Flags
|
||||
- Feature toggling: `WLED_DISABLE_*` and `WLED_ENABLE_*` flags (exact names matter!)
|
||||
- `WLED_DISABLE_*`: `2D`, `ADALIGHT`, `ALEXA`, `MQTT`, `OTA`, `INFRARED`, `WEBSOCKETS`, etc.
|
||||
- `WLED_ENABLE_*`: `DMX`, `GIF`, `HUB75MATRIX`, `JSONLIVE`, `WEBSOCKETS`, etc.
|
||||
- Platform: `ARDUINO_ARCH_ESP32`, `ESP8266`, `CONFIG_IDF_TARGET_ESP32S3`
|
||||
|
||||
### Comments
|
||||
- `//` for inline (always space after), `/* */` for block comments
|
||||
- AI-generated blocks: mark with `// AI: below section was generated by an AI` / `// AI: end`
|
||||
|
||||
### Math Functions
|
||||
- Use `sin8_t()`, `cos8_t()` — NOT `sin8()`, `cos8()` (removed, won't compile)
|
||||
- Use `sin_approx()` / `cos_approx()` instead of `sinf()` / `cosf()`
|
||||
- Replace `inoise8` / `inoise16` with `perlin8` / `perlin16`
|
||||
|
||||
### Hot-Path Code (Pixel Pipeline)
|
||||
- Use function attributes: `IRAM_ATTR`, `WLED_O2_ATTR`, `__attribute__((hot))`
|
||||
- Cache class members to locals before loops
|
||||
- Pre-compute invariants outside loops; use reciprocals to avoid division
|
||||
- Unsigned range checks: `if ((uint_fast16_t)(pix - start) < len)`
|
||||
|
||||
### ESP32 Tasks
|
||||
- `delay(1)` in custom FreeRTOS tasks (NOT `yield()`) — feeds IDLE watchdog
|
||||
- Do not use `delay()` in effects (FX.cpp) or hot pixel path
|
||||
|
||||
## Web UI Code Style (wled00/data/)
|
||||
|
||||
- **Tab indentation** for HTML, JS, and CSS
|
||||
- camelCase for JS functions/variables
|
||||
- Reuse helpers from `common.js` — do not duplicate utilities
|
||||
- After editing, run `npm run build` to regenerate headers
|
||||
- **Never edit** `wled00/html_*.h` or `wled00/js_*.h` directly
|
||||
|
||||
## Usermod Pattern
|
||||
|
||||
Usermods live in `usermods/<name>/` with a `.cpp`, optional `.h`, `library.json`, and `readme.md`.
|
||||
|
||||
```cpp
|
||||
class MyUsermod : public Usermod {
|
||||
private:
|
||||
bool enabled = false;
|
||||
static const char _name[];
|
||||
public:
|
||||
void setup() override { /* ... */ }
|
||||
void loop() override { /* ... */ }
|
||||
void addToConfig(JsonObject& root) override { /* ... */ }
|
||||
bool readFromConfig(JsonObject& root) override { /* ... */ }
|
||||
uint16_t getId() override { return USERMOD_ID_MYMOD; }
|
||||
};
|
||||
const char MyUsermod::_name[] PROGMEM = "MyUsermod";
|
||||
static MyUsermod myUsermod;
|
||||
REGISTER_USERMOD(myUsermod);
|
||||
```
|
||||
|
||||
- Add usermod IDs to `wled00/const.h`
|
||||
- Activate via `custom_usermods` in platformio build config
|
||||
- Base new usermods on `usermods/EXAMPLE/` (never edit the example directly)
|
||||
- Store repeated strings as `static const char[] PROGMEM`
|
||||
|
||||
## CI/CD
|
||||
|
||||
CI runs on every push/PR via GitHub Actions (`.github/workflows/wled-ci.yml`):
|
||||
1. `npm test` (web UI build validation)
|
||||
2. Firmware compilation for all default environments (~22 targets)
|
||||
3. Post-link validation of usermod linkage (`validate_modules.py`)
|
||||
|
||||
No automated linting is configured. Match existing code style in files you edit.
|
||||
|
||||
## General Rules
|
||||
|
||||
- Repository language is English
|
||||
- The `docs/` folder is for developer/contributor information (coding conventions, architecture, etc.). User documentation is maintained in the [wled/WLED-Docs](https://github.com/wled/WLED-Docs) repository.
|
||||
- Never edit or commit auto-generated `wled00/html_*.h` / `wled00/js_*.h`
|
||||
- When updating an existing PR, retain the original description. Only modify it to ensure technical accuracy. Add change logs after the existing description.
|
||||
- No force-push on open PRs
|
||||
- Changes to `platformio.ini` require maintainer approval
|
||||
- Remove dead/unused code — justify or delete it
|
||||
- Verify feature-flag spelling exactly (misspellings are silently ignored by preprocessor)
|
||||
- Provide references when making analyses or recommendations. Support factual claims with verifiable citations, references or concrete evidence; **never fabricate citations**.
|
||||
1628
CHANGELOG.md
Normal file
1628
CHANGELOG.md
Normal file
File diff suppressed because it is too large
Load Diff
76
CODE_OF_CONDUCT.md
Normal file
76
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at dev.aircoookie@gmail.com. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
||||
257
CONTRIBUTING.md
Normal file
257
CONTRIBUTING.md
Normal file
@@ -0,0 +1,257 @@
|
||||
# Thank you for making WLED better!
|
||||
|
||||
WLED is a community-driven project, and every contribution matters! We appreciate your time and effort.
|
||||
|
||||
Our maintainers are here for two things: **helping you** improve your code, and **keeping WLED** lean, efficient, and maintainable.
|
||||
We'll work with you to refine your contribution, but we'll also push back if something might create technical debt or add features without clear value. Don't take it personally - we're just protecting WLED's architecture while helping your contribution succeed!
|
||||
|
||||
## Getting Started
|
||||
|
||||
Here are a few suggestions to make it easier for you to contribute:
|
||||
|
||||
### Important Developer Infos
|
||||
* [Project Structure, Files and Directories](.github/copilot-instructions.md#project-structure-overview) (in our AI instructions)
|
||||
* [Instructions for creating usermods](.github/copilot-instructions.md#usermod-guidelines) (in our AI instructions)
|
||||
* KB: [Compiling WLED](https://kno.wled.ge/advanced/compiling-wled/) - slightly outdated but still helpful :-)
|
||||
* Arduino IDE is not supported any more. Use VSCode with the PlatformIO extension.
|
||||
* [Compiling in VSCode/Platformio](https://github.com/wled/WLED-Docs/issues/161) - modern way without command line or platformio.ini changes.
|
||||
* If you add a new feature, consider making a PR to [``wled-docs``](https://github.com/wled/WLED-Docs) for updating our official documentation.
|
||||
|
||||
### PR from a branch in your own fork
|
||||
Start your pull request (PR) in a branch of your own fork. Don't make a PR directly from your main branch.
|
||||
This lets you update your PR if needed, while you can work on other tasks in 'main' or in other branches.
|
||||
|
||||
> [!TIP]
|
||||
> **The easiest way to start your first PR**
|
||||
> When viewing a file in `wled/WLED`, click on the "pen" icon and start making changes.
|
||||
> When you choose to 'Commit changes', GitHub will automatically create a PR from your fork.
|
||||
>
|
||||
> <img width="295" height="134" alt="image: fork and edit" src="https://github.com/user-attachments/assets/f0dc7567-edcb-4409-a530-cd621ae9661f" />
|
||||
|
||||
### Target branch for pull requests
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Please make all PRs against the `main` branch.
|
||||
|
||||
### Describing your PR
|
||||
|
||||
Please add a description of your proposed code changes.
|
||||
A PR with no description or just a few words might not get accepted, simply because very basic information is missing.
|
||||
No need to write an essay!
|
||||
|
||||
A good description helps us to review and understand your proposed changes. For example, you could say a few words about
|
||||
* What you try to achieve (new feature, fixing a bug, refactoring, security enhancements, etc.)
|
||||
* How your code works (short technical summary - focus on important aspects that might not be obvious when reading the code)
|
||||
* Testing you performed, known limitations, anything you couldn't quite solve.
|
||||
* Let us know if you'd like guidance from a maintainer (WLED is a big project 😉)
|
||||
|
||||
### Testing Your Changes
|
||||
|
||||
Before submitting:
|
||||
|
||||
- ✅ Does it compile?
|
||||
- ✅ Does your feature/fix actually work?
|
||||
- ✅ Did you break anything else?
|
||||
- ✅ Tested on actual hardware if possible?
|
||||
|
||||
Mention your testing in the PR description (e.g., "Tested on ESP32 + WS2812B").
|
||||
|
||||
## During Review
|
||||
|
||||
We're all volunteers, so reviews can take some time (longer during busy times).
|
||||
Don't worry - we haven't forgotten you! Feel free to ping after a week if there's no activity.
|
||||
|
||||
### Updating your code
|
||||
While the PR is open, you can keep updating your branch - just push more commits! GitHub will automatically update your PR.
|
||||
|
||||
You don't need to squash commits or clean up history - we'll handle that when merging.
|
||||
|
||||
> [!CAUTION]
|
||||
> Do not use "force-push" while your PR is open!
|
||||
> It has many subtle and unexpected consequences on our GitHub repository.
|
||||
> For example, we regularly lose review comments when the PR author force-pushes code changes. Our review bot (coderabbit) may become unable to properly track changes, it gets confused or stops responding to questions.
|
||||
> So, pretty please, do not force-push.
|
||||
|
||||
> [!TIP]
|
||||
> Use [cherry-picking](https://docs.github.com/en/desktop/managing-commits/cherry-picking-a-commit-in-github-desktop) to copy commits from one branch to another.
|
||||
|
||||
|
||||
### Responding to Reviews
|
||||
|
||||
When we ask for changes:
|
||||
|
||||
- **Add new commits** - please don't amend or force-push
|
||||
- **Reply in the PR** - let us know when you've addressed comments
|
||||
- **Ask questions** - if something's unclear, just ask!
|
||||
- **Be patient** - we're all volunteers here 😊
|
||||
|
||||
You can reference feedback in commit messages:
|
||||
> ```text
|
||||
> Fix naming per @Aircoookie's suggestion
|
||||
> ```
|
||||
|
||||
### Dealing with Merge Conflicts
|
||||
|
||||
Got conflicts with `main`? No worries - here's how to fix them:
|
||||
|
||||
**Using GitHub Desktop** (easier for beginners):
|
||||
|
||||
1. Click **Fetch origin**, then **Pull origin**
|
||||
2. If conflicts exist, GitHub Desktop will warn you - click **View conflicts**
|
||||
3. Open the conflicted files in your editor (VS Code, etc.)
|
||||
4. Remove the conflict markers (`<<<<<<<`, `=======`, `>>>>>>>`) and keep the correct code
|
||||
5. Save the files
|
||||
6. Back in GitHub Desktop, commit the merge (it'll suggest a message)
|
||||
7. Click **Push origin**
|
||||
|
||||
**Using command line**:
|
||||
|
||||
```bash
|
||||
git fetch origin
|
||||
git merge origin/main
|
||||
# Fix conflicts in your editor
|
||||
git add .
|
||||
git commit
|
||||
git push
|
||||
```
|
||||
|
||||
Either way works fine - pick what you're comfortable with! Merging is simpler than rebasing and keeps everything connected.
|
||||
|
||||
#### When you MUST rebase (really rare!)
|
||||
|
||||
Sometimes you might hit merge conflicts with `main` that are harder to solve. Here's what to try:
|
||||
|
||||
1. **Merge instead of rebase** (safest option):
|
||||
```bash
|
||||
git fetch origin
|
||||
git merge origin/main
|
||||
git push
|
||||
```
|
||||
Keeps review comments attached and CI results visible!
|
||||
|
||||
2. **Use cherry-picking** to copy commits between branches without rewriting history - [here's how](https://docs.github.com/en/desktop/managing-commits/cherry-picking-a-commit-in-github-desktop).
|
||||
|
||||
3. **If all else fails, use `--force-with-lease`** (not plain `--force`):
|
||||
```bash
|
||||
git rebase origin/main
|
||||
git push --force-with-lease
|
||||
```
|
||||
Then **leave a comment** explaining why you had to force-push, and be ready to re-address some feedback.
|
||||
|
||||
### Additional Resources
|
||||
Want to know more? Check out:
|
||||
- 📚 [GitHub Desktop documentation](https://docs.github.com/en/desktop) - if you prefer GUI tools
|
||||
- 🎓 [How to properly submit a PR](https://github.com/wled-dev/WLED/wiki/How-to-properly-submit-a-PR) - detailed tips and tricks
|
||||
|
||||
|
||||
## After Approval
|
||||
Once approved, a maintainer will merge your PR (possibly squashing commits).
|
||||
Your contribution will be in the next WLED release - thank you! 🎉
|
||||
|
||||
|
||||
## Coding Guidelines
|
||||
|
||||
### Source Code from an AI agent or bot
|
||||
> [!IMPORTANT]
|
||||
> It's OK if you took help from an AI for writing your source code.
|
||||
>
|
||||
> AI tools can be very helpful, but as the contributor, **you're responsible for the code**.
|
||||
|
||||
* Make sure you really understand the AI-generated code, don't just accept it because it "seems to work".
|
||||
* Don't let the AI change existing code without double-checking by you as the contributor. Often, the result will not be complete. For example, previous source code comments may be lost.
|
||||
* Remember that AI is still "Often-Wrong" ;-)
|
||||
* If you don't feel confident using English, you can use AI for translating code comments and descriptions into English. AI bots are very good at understanding language. However, always check if the results are correct. The translation might still have wrong technical terms, or errors in some details.
|
||||
|
||||
#### Best Practice with AI
|
||||
|
||||
AI tools are powerful but "often wrong" - your judgment is essential! 😊
|
||||
|
||||
- ✅ **Understand the code** - As the person contributing to WLED, make sure you understand exactly what the AI-generated source code does
|
||||
- ✅ **Review carefully** - AI can lose comments, introduce bugs, or make unnecessary changes
|
||||
- ✅ **Be transparent** - Add comments `// AI: below section was generated by an AI` ... `// AI: end` around larger chunks
|
||||
- ✅ **Use AI for translation** - AI is great for translating comments to English (but verify technical terms!)
|
||||
|
||||
### Code style
|
||||
|
||||
Don't stress too much about style! When in doubt, just match the style in the files you're editing. 😊
|
||||
|
||||
Our review bot (coderabbit) has learned lots of detailed guides and hints - it will suggest them automatically when you submit a PR for review.
|
||||
If you are curious, these are the detailed guides:
|
||||
* [C++ Coding](docs/cpp.instructions.md)
|
||||
* [WebUi: HTML, JS, CSS](docs/web.instructions.md)
|
||||
|
||||
Below are the main rules used in the WLED repository:
|
||||
|
||||
#### Indentation
|
||||
|
||||
We use tabs for indentation in Web files (.html/.css/.js) and spaces (2 per indentation level) for all other files.
|
||||
You are all set if you have enabled `Editor: Detect Indentation` in VS Code.
|
||||
|
||||
#### Blocks
|
||||
|
||||
Whether the opening bracket of e.g. an `if` block is in the same line as the condition or in a separate line is up to your discretion. If there is only one statement, leaving out block brackets is acceptable.
|
||||
|
||||
Good:
|
||||
```cpp
|
||||
if (a == b) {
|
||||
doStuff(a);
|
||||
}
|
||||
```
|
||||
|
||||
```cpp
|
||||
if (a == b) doStuff(a);
|
||||
```
|
||||
|
||||
Also acceptable (though the first style is usually easier to read):
|
||||
```cpp
|
||||
if (a == b)
|
||||
{
|
||||
doStuff(a);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
There should always be a space between a keyword and its condition and between the condition and brace.
|
||||
Within the condition, no space should be between the parenthesis and variables.
|
||||
Spaces between variables and operators are up to the authors discretion.
|
||||
There should be no space between function names and their argument parenthesis.
|
||||
|
||||
Good:
|
||||
```cpp
|
||||
if (a == b) {
|
||||
doStuff(a);
|
||||
}
|
||||
```
|
||||
|
||||
Not good:
|
||||
```cpp
|
||||
if( a==b ){
|
||||
doStuff ( a);
|
||||
}
|
||||
```
|
||||
|
||||
#### Comments
|
||||
|
||||
Comments should have a space between the delimiting characters (e.g. `//`) and the comment text.
|
||||
We're gradually adopting this style - don't worry if you see older code without spaces!
|
||||
|
||||
Good:
|
||||
```cpp
|
||||
// This is a short inline comment.
|
||||
|
||||
/*
|
||||
* This is a longer comment
|
||||
* wrapping over multiple lines,
|
||||
* used in WLED for file headers and function explanations
|
||||
*/
|
||||
```
|
||||
```css
|
||||
/* This is a CSS inline comment */
|
||||
```
|
||||
```html
|
||||
<!-- This is an HTML comment -->
|
||||
```
|
||||
|
||||
There is no hard character limit for a comment within a line,
|
||||
though as a rule of thumb consider wrapping after 120 characters.
|
||||
Inline comments are OK if they describe that line only and are not exceedingly wide.
|
||||
307
LICENSE
307
LICENSE
@@ -1,21 +1,294 @@
|
||||
MIT License
|
||||
Copyright (c) 2016-present Christian Schwinne and individual WLED contributors
|
||||
Licensed under the EUPL v. 1.2 or later
|
||||
|
||||
Copyright (c) 2016 Christian Schwinne
|
||||
EUROPEAN UNION PUBLIC LICENCE v. 1.2
|
||||
EUPL © the European Union 2007, 2016
|
||||
|
||||
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:
|
||||
This European Union Public Licence (the ‘EUPL’) applies to the Work (as
|
||||
defined below) which is provided under the terms of this Licence. Any use of
|
||||
the Work, other than as authorised under this Licence is prohibited (to the
|
||||
extent such use is covered by a right of the copyright holder of the Work).
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
The Work is provided under the terms of this Licence when the Licensor (as
|
||||
defined below) has placed the following notice immediately following the
|
||||
copyright notice for the Work:
|
||||
|
||||
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.
|
||||
Licensed under the EUPL
|
||||
|
||||
or has expressed by any other means his willingness to license under the EUPL.
|
||||
|
||||
1. Definitions
|
||||
|
||||
In this Licence, the following terms have the following meaning:
|
||||
|
||||
- ‘The Licence’: this Licence.
|
||||
|
||||
- ‘The Original Work’: the work or software distributed or communicated by the
|
||||
Licensor under this Licence, available as Source Code and also as Executable
|
||||
Code as the case may be.
|
||||
|
||||
- ‘Derivative Works’: the works or software that could be created by the
|
||||
Licensee, based upon the Original Work or modifications thereof. This
|
||||
Licence does not define the extent of modification or dependence on the
|
||||
Original Work required in order to classify a work as a Derivative Work;
|
||||
this extent is determined by copyright law applicable in the country
|
||||
mentioned in Article 15.
|
||||
|
||||
- ‘The Work’: the Original Work or its Derivative Works.
|
||||
|
||||
- ‘The Source Code’: the human-readable form of the Work which is the most
|
||||
convenient for people to study and modify.
|
||||
|
||||
- ‘The Executable Code’: any code which has generally been compiled and which
|
||||
is meant to be interpreted by a computer as a program.
|
||||
|
||||
- ‘The Licensor’: the natural or legal person that distributes or communicates
|
||||
the Work under the Licence.
|
||||
|
||||
- ‘Contributor(s)’: any natural or legal person who modifies the Work under
|
||||
the Licence, or otherwise contributes to the creation of a Derivative Work.
|
||||
|
||||
- ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of
|
||||
the Work under the terms of the Licence.
|
||||
|
||||
- ‘Distribution’ or ‘Communication’: any act of selling, giving, lending,
|
||||
renting, distributing, communicating, transmitting, or otherwise making
|
||||
available, online or offline, copies of the Work or providing access to its
|
||||
essential functionalities at the disposal of any other natural or legal
|
||||
person.
|
||||
|
||||
2. Scope of the rights granted by the Licence
|
||||
|
||||
The Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
|
||||
sublicensable licence to do the following, for the duration of copyright
|
||||
vested in the Original Work:
|
||||
|
||||
- use the Work in any circumstance and for all usage,
|
||||
- reproduce the Work,
|
||||
- modify the Work, and make Derivative Works based upon the Work,
|
||||
- communicate to the public, including the right to make available or display
|
||||
the Work or copies thereof to the public and perform publicly, as the case
|
||||
may be, the Work,
|
||||
- distribute the Work or copies thereof,
|
||||
- lend and rent the Work or copies thereof,
|
||||
- sublicense rights in the Work or copies thereof.
|
||||
|
||||
Those rights can be exercised on any media, supports and formats, whether now
|
||||
known or later invented, as far as the applicable law permits so.
|
||||
|
||||
In the countries where moral rights apply, the Licensor waives his right to
|
||||
exercise his moral right to the extent allowed by law in order to make
|
||||
effective the licence of the economic rights here above listed.
|
||||
|
||||
The Licensor grants to the Licensee royalty-free, non-exclusive usage rights
|
||||
to any patents held by the Licensor, to the extent necessary to make use of
|
||||
the rights granted on the Work under this Licence.
|
||||
|
||||
3. Communication of the Source Code
|
||||
|
||||
The Licensor may provide the Work either in its Source Code form, or as
|
||||
Executable Code. If the Work is provided as Executable Code, the Licensor
|
||||
provides in addition a machine-readable copy of the Source Code of the Work
|
||||
along with each copy of the Work that the Licensor distributes or indicates,
|
||||
in a notice following the copyright notice attached to the Work, a repository
|
||||
where the Source Code is easily and freely accessible for as long as the
|
||||
Licensor continues to distribute or communicate the Work.
|
||||
|
||||
4. Limitations on copyright
|
||||
|
||||
Nothing in this Licence is intended to deprive the Licensee of the benefits
|
||||
from any exception or limitation to the exclusive rights of the rights owners
|
||||
in the Work, of the exhaustion of those rights or of other applicable
|
||||
limitations thereto.
|
||||
|
||||
5. Obligations of the Licensee
|
||||
|
||||
The grant of the rights mentioned above is subject to some restrictions and
|
||||
obligations imposed on the Licensee. Those obligations are the following:
|
||||
|
||||
Attribution right: The Licensee shall keep intact all copyright, patent or
|
||||
trademarks notices and all notices that refer to the Licence and to the
|
||||
disclaimer of warranties. The Licensee must include a copy of such notices and
|
||||
a copy of the Licence with every copy of the Work he/she distributes or
|
||||
communicates. The Licensee must cause any Derivative Work to carry prominent
|
||||
notices stating that the Work has been modified and the date of modification.
|
||||
|
||||
Copyleft clause: If the Licensee distributes or communicates copies of the
|
||||
Original Works or Derivative Works, this Distribution or Communication will be
|
||||
done under the terms of this Licence or of a later version of this Licence
|
||||
unless the Original Work is expressly distributed only under this version of
|
||||
the Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee
|
||||
(becoming Licensor) cannot offer or impose any additional terms or conditions
|
||||
on the Work or Derivative Work that alter or restrict the terms of the
|
||||
Licence.
|
||||
|
||||
Compatibility clause: If the Licensee Distributes or Communicates Derivative
|
||||
Works or copies thereof based upon both the Work and another work licensed
|
||||
under a Compatible Licence, this Distribution or Communication can be done
|
||||
under the terms of this Compatible Licence. For the sake of this clause,
|
||||
‘Compatible Licence’ refers to the licences listed in the appendix attached to
|
||||
this Licence. Should the Licensee's obligations under the Compatible Licence
|
||||
conflict with his/her obligations under this Licence, the obligations of the
|
||||
Compatible Licence shall prevail.
|
||||
|
||||
Provision of Source Code: When distributing or communicating copies of the
|
||||
Work, the Licensee will provide a machine-readable copy of the Source Code or
|
||||
indicate a repository where this Source will be easily and freely available
|
||||
for as long as the Licensee continues to distribute or communicate the Work.
|
||||
|
||||
Legal Protection: This Licence does not grant permission to use the trade
|
||||
names, trademarks, service marks, or names of the Licensor, except as required
|
||||
for reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the copyright notice.
|
||||
|
||||
6. Chain of Authorship
|
||||
|
||||
The original Licensor warrants that the copyright in the Original Work granted
|
||||
hereunder is owned by him/her or licensed to him/her and that he/she has the
|
||||
power and authority to grant the Licence.
|
||||
|
||||
Each Contributor warrants that the copyright in the modifications he/she
|
||||
brings to the Work are owned by him/her or licensed to him/her and that he/she
|
||||
has the power and authority to grant the Licence.
|
||||
|
||||
Each time You accept the Licence, the original Licensor and subsequent
|
||||
Contributors grant You a licence to their contributions to the Work, under the
|
||||
terms of this Licence.
|
||||
|
||||
7. Disclaimer of Warranty
|
||||
|
||||
The Work is a work in progress, which is continuously improved by numerous
|
||||
Contributors. It is not a finished work and may therefore contain defects or
|
||||
‘bugs’ inherent to this type of development.
|
||||
|
||||
For the above reason, the Work is provided under the Licence on an ‘as is’
|
||||
basis and without warranties of any kind concerning the Work, including
|
||||
without limitation merchantability, fitness for a particular purpose, absence
|
||||
of defects or errors, accuracy, non-infringement of intellectual property
|
||||
rights other than copyright as stated in Article 6 of this Licence.
|
||||
|
||||
This disclaimer of warranty is an essential part of the Licence and a
|
||||
condition for the grant of any rights to the Work.
|
||||
|
||||
8. Disclaimer of Liability
|
||||
|
||||
Except in the cases of wilful misconduct or damages directly caused to natural
|
||||
persons, the Licensor will in no event be liable for any direct or indirect,
|
||||
material or moral, damages of any kind, arising out of the Licence or of the
|
||||
use of the Work, including without limitation, damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, loss of data or any commercial
|
||||
damage, even if the Licensor has been advised of the possibility of such
|
||||
damage. However, the Licensor will be liable under statutory product liability
|
||||
laws as far such laws apply to the Work.
|
||||
|
||||
9. Additional agreements
|
||||
|
||||
While distributing the Work, You may choose to conclude an additional
|
||||
agreement, defining obligations or services consistent with this Licence.
|
||||
However, if accepting obligations, You may act only on your own behalf and on
|
||||
your sole responsibility, not on behalf of the original Licensor or any other
|
||||
Contributor, and only if You agree to indemnify, defend, and hold each
|
||||
Contributor harmless for any liability incurred by, or claims asserted against
|
||||
such Contributor by the fact You have accepted any warranty or additional
|
||||
liability.
|
||||
|
||||
10. Acceptance of the Licence
|
||||
|
||||
The provisions of this Licence can be accepted by clicking on an icon ‘I
|
||||
agree’ placed under the bottom of a window displaying the text of this Licence
|
||||
or by affirming consent in any other similar way, in accordance with the rules
|
||||
of applicable law. Clicking on that icon indicates your clear and irrevocable
|
||||
acceptance of this Licence and all of its terms and conditions.
|
||||
|
||||
Similarly, you irrevocably accept this Licence and all of its terms and
|
||||
conditions by exercising any rights granted to You by Article 2 of this
|
||||
Licence, such as the use of the Work, the creation by You of a Derivative Work
|
||||
or the Distribution or Communication by You of the Work or copies thereof.
|
||||
|
||||
11. Information to the public
|
||||
|
||||
In case of any Distribution or Communication of the Work by means of
|
||||
electronic communication by You (for example, by offering to download the Work
|
||||
from a remote location) the distribution channel or media (for example, a
|
||||
website) must at least provide to the public the information requested by the
|
||||
applicable law regarding the Licensor, the Licence and the way it may be
|
||||
accessible, concluded, stored and reproduced by the Licensee.
|
||||
|
||||
12. Termination of the Licence
|
||||
|
||||
The Licence and the rights granted hereunder will terminate automatically upon
|
||||
any breach by the Licensee of the terms of the Licence.
|
||||
|
||||
Such a termination will not terminate the licences of any person who has
|
||||
received the Work from the Licensee under the Licence, provided such persons
|
||||
remain in full compliance with the Licence.
|
||||
|
||||
13. Miscellaneous
|
||||
|
||||
Without prejudice of Article 9 above, the Licence represents the complete
|
||||
agreement between the Parties as to the Work.
|
||||
|
||||
If any provision of the Licence is invalid or unenforceable under applicable
|
||||
law, this will not affect the validity or enforceability of the Licence as a
|
||||
whole. Such provision will be construed or reformed so as necessary to make it
|
||||
valid and enforceable.
|
||||
|
||||
The European Commission may publish other linguistic versions or new versions
|
||||
of this Licence or updated versions of the Appendix, so far this is required
|
||||
and reasonable, without reducing the scope of the rights granted by the
|
||||
Licence. New versions of the Licence will be published with a unique version
|
||||
number.
|
||||
|
||||
All linguistic versions of this Licence, approved by the European Commission,
|
||||
have identical value. Parties can take advantage of the linguistic version of
|
||||
their choice.
|
||||
|
||||
14. Jurisdiction
|
||||
|
||||
Without prejudice to specific agreement between parties,
|
||||
|
||||
- any litigation resulting from the interpretation of this License, arising
|
||||
between the European Union institutions, bodies, offices or agencies, as a
|
||||
Licensor, and any Licensee, will be subject to the jurisdiction of the Court
|
||||
of Justice of the European Union, as laid down in article 272 of the Treaty
|
||||
on the Functioning of the European Union,
|
||||
|
||||
- any litigation arising between other parties and resulting from the
|
||||
interpretation of this License, will be subject to the exclusive
|
||||
jurisdiction of the competent court where the Licensor resides or conducts
|
||||
its primary business.
|
||||
|
||||
15. Applicable Law
|
||||
|
||||
Without prejudice to specific agreement between parties,
|
||||
|
||||
- this Licence shall be governed by the law of the European Union Member State
|
||||
where the Licensor has his seat, resides or has his registered office,
|
||||
|
||||
- this licence shall be governed by Belgian law if the Licensor has no seat,
|
||||
residence or registered office inside a European Union Member State.
|
||||
|
||||
Appendix
|
||||
|
||||
‘Compatible Licences’ according to Article 5 EUPL are:
|
||||
|
||||
- GNU General Public License (GPL) v. 2, v. 3
|
||||
- GNU Affero General Public License (AGPL) v. 3
|
||||
- Open Software License (OSL) v. 2.1, v. 3.0
|
||||
- Eclipse Public License (EPL) v. 1.0
|
||||
- CeCILL v. 2.0, v. 2.1
|
||||
- Mozilla Public Licence (MPL) v. 2
|
||||
- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3
|
||||
- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for
|
||||
works other than software
|
||||
- European Union Public Licence (EUPL) v. 1.1, v. 1.2
|
||||
- Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong
|
||||
Reciprocity (LiLiQ-R+).
|
||||
|
||||
The European Commission may update this Appendix to later versions of the
|
||||
above licences without producing a new version of the EUPL, as long as they
|
||||
provide the rights granted in Article 2 of this Licence and protect the
|
||||
covered Source Code from exclusive appropriation.
|
||||
|
||||
All other changes or additions to this Appendix require the production of a
|
||||
new EUPL version.
|
||||
@@ -1,7 +0,0 @@
|
||||
## Where are the new binaries?
|
||||
|
||||
From v0.5.0 on forward, the GitHub [releases](https://github.com/Aircoookie/WLED/releases) system will be used for binaries.
|
||||
|
||||
### What binary should I choose?
|
||||
|
||||
You should always try to use the binaries from the release page. This directory is for legacy purposes only!
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
58
boards/adafruit_matrixportal_esp32s3_wled.json
Normal file
58
boards/adafruit_matrixportal_esp32s3_wled.json
Normal file
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino":{
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"partitions": "default_8MB.csv"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DARDUINO_ADAFRUIT_MATRIXPORTAL_ESP32S3",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1",
|
||||
"-DBOARD_HAS_PSRAM"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
"hwids": [
|
||||
[
|
||||
"0x239A",
|
||||
"0x8125"
|
||||
],
|
||||
[
|
||||
"0x239A",
|
||||
"0x0125"
|
||||
],
|
||||
[
|
||||
"0x239A",
|
||||
"0x8126"
|
||||
]
|
||||
],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "adafruit_matrixportal_esp32s3"
|
||||
},
|
||||
"connectivity": [
|
||||
"bluetooth",
|
||||
"wifi"
|
||||
],
|
||||
"debug": {
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino",
|
||||
"espidf"
|
||||
],
|
||||
"name": "Adafruit MatrixPortal ESP32-S3 for WLED",
|
||||
"upload": {
|
||||
"flash_size": "8MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 8388608,
|
||||
"use_1200bps_touch": true,
|
||||
"wait_for_upload_port": true,
|
||||
"require_upload_port": true,
|
||||
"speed": 460800
|
||||
},
|
||||
"url": "https://www.adafruit.com/product/5778",
|
||||
"vendor": "Adafruit"
|
||||
}
|
||||
47
boards/lilygo-t7-s3.json
Normal file
47
boards/lilygo-t7-s3.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino":{
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"memory_type": "qio_opi",
|
||||
"partitions": "default_16MB.csv"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DARDUINO_TTGO_T7_S3",
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_MODE=1"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
"hwids": [
|
||||
[
|
||||
"0X303A",
|
||||
"0x1001"
|
||||
]
|
||||
],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "esp32s3"
|
||||
},
|
||||
"connectivity": [
|
||||
"wifi",
|
||||
"bluetooth"
|
||||
],
|
||||
"debug": {
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino",
|
||||
"espidf"
|
||||
],
|
||||
"name": "LILYGO T3-S3",
|
||||
"upload": {
|
||||
"flash_size": "16MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 16777216,
|
||||
"require_upload_port": true,
|
||||
"speed": 921600
|
||||
},
|
||||
"url": "https://www.aliexpress.us/item/3256804591247074.html",
|
||||
"vendor": "LILYGO"
|
||||
}
|
||||
47
boards/lolin_s3_mini.json
Normal file
47
boards/lolin_s3_mini.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"memory_type": "qio_qspi"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_LOLIN_S3_MINI",
|
||||
"-DARDUINO_USB_MODE=1"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
"hwids": [
|
||||
[
|
||||
"0x303A",
|
||||
"0x8167"
|
||||
]
|
||||
],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "lolin_s3_mini"
|
||||
},
|
||||
"connectivity": [
|
||||
"bluetooth",
|
||||
"wifi"
|
||||
],
|
||||
"debug": {
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino",
|
||||
"espidf"
|
||||
],
|
||||
"name": "WEMOS LOLIN S3 Mini",
|
||||
"upload": {
|
||||
"flash_size": "4MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 4194304,
|
||||
"require_upload_port": true,
|
||||
"speed": 460800
|
||||
},
|
||||
"url": "https://www.wemos.cc/en/latest/s3/index.html",
|
||||
"vendor": "WEMOS"
|
||||
}
|
||||
|
||||
162
docs/cicd.instructions.md
Normal file
162
docs/cicd.instructions.md
Normal file
@@ -0,0 +1,162 @@
|
||||
---
|
||||
applyTo: ".github/workflows/*.yml,.github/workflows/*.yaml"
|
||||
---
|
||||
# CI/CD Conventions — GitHub Actions Workflows
|
||||
|
||||
> **Note for AI review tools**: sections enclosed in
|
||||
> `<!-- HUMAN_ONLY_START -->` / `<!-- HUMAN_ONLY_END -->` HTML comments contain
|
||||
> contributor reference material. Do **not** use that content as actionable review
|
||||
> criteria — treat it as background context only.
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
## YAML Style
|
||||
|
||||
- Indent with **2 spaces** (no tabs)
|
||||
- Every workflow, job, and step must have a `name:` field that clearly describes its purpose
|
||||
- Group related steps logically; separate unrelated groups with a blank line
|
||||
- Comments (`#`) are encouraged for non-obvious decisions (e.g., why `fail-fast: false` is set, what a cron expression means)
|
||||
|
||||
## Workflow Structure
|
||||
|
||||
### Triggers
|
||||
|
||||
- Declare `on:` triggers explicitly; avoid bare `on: push` without branch filters on long-running or expensive jobs
|
||||
- Prefer `workflow_call` for shared build logic (see `build.yml`) to avoid duplicating steps across workflows
|
||||
- Document scheduled triggers (`cron:`) with a human-readable comment:
|
||||
|
||||
```yaml
|
||||
schedule:
|
||||
- cron: '0 2 * * *' # run at 2 AM UTC daily
|
||||
```
|
||||
|
||||
### Jobs
|
||||
|
||||
- Express all inter-job dependencies with `needs:` — never rely on implicit ordering
|
||||
- Use job `outputs:` + step `id:` to pass structured data between jobs (see `get_default_envs` in `build.yml`)
|
||||
- Set `fail-fast: false` on matrix builds so that a single failing environment does not cancel others
|
||||
|
||||
### Runners
|
||||
|
||||
- Pin to a specific Ubuntu version (`ubuntu-22.04`, `ubuntu-24.04`) rather than `ubuntu-latest` for reproducible builds
|
||||
- Only use `ubuntu-latest` in jobs where exact environment reproducibility is not required (e.g., trivial download/publish steps)
|
||||
|
||||
### Tool and Language Versions
|
||||
|
||||
- Pin tool versions explicitly:
|
||||
```yaml
|
||||
python-version: '3.12'
|
||||
```
|
||||
- Do not rely on the runner's pre-installed tool versions — always install via a versioned setup action
|
||||
|
||||
### Caching
|
||||
|
||||
- Always cache package managers and build tool directories when the job installs dependencies:
|
||||
```yaml
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-
|
||||
```
|
||||
- Include the environment name or a relevant identifier in cache keys when building multiple targets
|
||||
|
||||
### Artifacts
|
||||
|
||||
- Name artifacts with enough context to be unambiguous (e.g., `firmware-${{ matrix.environment }}`)
|
||||
- Avoid uploading artifacts that will never be consumed downstream
|
||||
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
Important: Several current workflows still violate parts of the baseline below - migration is in progress.
|
||||
|
||||
### Permissions — Least Privilege
|
||||
|
||||
Declare explicit `permissions:` blocks. The default token permissions are broad; scope them to the minimum required:
|
||||
|
||||
```yaml
|
||||
permissions:
|
||||
contents: read # for checkout
|
||||
```
|
||||
|
||||
For jobs that publish releases or write to the repository:
|
||||
|
||||
```yaml
|
||||
permissions:
|
||||
contents: write # create/update releases
|
||||
```
|
||||
|
||||
A common safe baseline for build-only jobs:
|
||||
|
||||
```yaml
|
||||
permissions:
|
||||
contents: read
|
||||
```
|
||||
|
||||
### Supply Chain — Action Pinning
|
||||
|
||||
**Third-party actions** (anything outside the `actions/` and `github/` namespaces) should be pinned to a specific release tag. Branch pins (`@main`, `@master`) are **not allowed** — they can be updated by the action author at any time without notice:
|
||||
|
||||
```yaml
|
||||
# ✅ Acceptable — specific version tag. SHA pinning recommended for more security, as @v2 is still a mutable tag.
|
||||
uses: softprops/action-gh-release@v2
|
||||
|
||||
# ❌ Not acceptable — mutable branch reference
|
||||
uses: andelf/nightly-release@main
|
||||
```
|
||||
|
||||
SHA pinning (e.g., `uses: someorg/some-action@abc1234`) is the most secure option for third-party actions; it is recommended when auditing supply-chain risk is a priority. At minimum, always use a specific version tag.
|
||||
|
||||
**First-party actions** (`actions/checkout`, `actions/cache`, `actions/upload-artifact`, etc.) pinned to a major version tag (e.g., `@v4`) are acceptable because GitHub maintains and audits these.
|
||||
|
||||
When adding a new third-party action:
|
||||
1. Check that the action's repository is actively maintained
|
||||
2. Review the action's source before adding it
|
||||
3. Prefer well-known, widely-used actions over obscure ones
|
||||
|
||||
### Credentials and Secrets
|
||||
|
||||
- Use `${{ secrets.GITHUB_TOKEN }}` for operations within the same repository — it is automatically scoped and rotated
|
||||
- Never commit secrets, tokens, or passwords into workflow files or any tracked file
|
||||
- Never print secrets in `run:` steps, even with `echo` — GitHub masks known secrets but derived values are not automatically masked
|
||||
- Scope secrets to the narrowest step that needs them using `env:` at the step level, not at the workflow level:
|
||||
|
||||
```yaml
|
||||
# ✅ Scoped to the step that needs it
|
||||
- name: Create release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# ❌ Unnecessarily broad
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
```
|
||||
|
||||
- Personal Access Tokens (PATs, stored as repository secrets) should have the minimum required scopes and should be rotated periodically
|
||||
|
||||
### Script Injection
|
||||
|
||||
`${{ }}` expressions are evaluated before the shell script runs. If an expression comes from untrusted input (PR titles, issue bodies, branch names from forks), it can inject arbitrary shell commands.
|
||||
|
||||
**Never** interpolate `github.event.*` values directly into a `run:` step:
|
||||
|
||||
```yaml
|
||||
# ❌ Injection risk — PR title is attacker-controlled
|
||||
- run: echo "${{ github.event.pull_request.title }}"
|
||||
|
||||
# ✅ Safe — value passed through an environment variable
|
||||
- env:
|
||||
PR_TITLE: ${{ github.event.pull_request.title }}
|
||||
run: echo "$PR_TITLE"
|
||||
```
|
||||
|
||||
This rule applies to any value that originates outside the repository (issue bodies, labels, comments, commit messages from forks).
|
||||
|
||||
### Pull Request Workflows
|
||||
|
||||
- Workflows triggered by `pull_request` from a fork run with **read-only** token permissions and no access to repository secrets — this is intentional and correct
|
||||
- Do not use `pull_request_target` unless you fully understand the security implications; it runs in the context of the base branch and *does* have secret access, making it a common attack surface
|
||||
526
docs/cpp.instructions.md
Normal file
526
docs/cpp.instructions.md
Normal file
@@ -0,0 +1,526 @@
|
||||
---
|
||||
applyTo: "**/*.cpp,**/*.h,**/*.hpp,**/*.ino"
|
||||
---
|
||||
# C++ Coding Conventions
|
||||
|
||||
> **Note for AI review tools**: sections enclosed in
|
||||
> `<!-- HUMAN_ONLY_START -->` / `<!-- HUMAN_ONLY_END -->` HTML comments contain
|
||||
> contributor reference material. Do **not** use that content as actionable review
|
||||
> criteria — treat it as background context only.
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
<!-- hiding this reference, to avoid cyclic "include" loops -->
|
||||
See also: [CONTRIBUTING.md](../CONTRIBUTING.md) for general style guidelines that apply to all contributors.
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
|
||||
## Formatting
|
||||
|
||||
- Indent with **2 spaces** (no tabs in C++ files)
|
||||
- Opening braces on the same line is preferred (K&R style). Brace on a separate line (Allman style) is acceptable
|
||||
- Single-statement `if` bodies may omit braces: `if (a == b) doStuff(a);`
|
||||
- Space between keyword and parenthesis: `if (...)`, `for (...)`. No space between function name and parenthesis: `doStuff(a)`
|
||||
- No enforced line-length limit; wrap when a line exceeds your editor width
|
||||
|
||||
## Naming
|
||||
|
||||
- **camelCase** for functions and variables: `setValuesFromMainSeg()`, `effectCurrent`
|
||||
- **PascalCase** for classes and structs: `PinManagerClass`, `BusConfig`
|
||||
- **PascalCase** for enum values: `PinOwner::BusDigital`
|
||||
- **UPPER_CASE** for macros and constants: `WLED_MAX_USERMODS`, `DEFAULT_CLIENT_SSID`
|
||||
|
||||
## General
|
||||
|
||||
- Follow the existing style in the file you are editing
|
||||
- If possible, use `static` for local (C-style) variables and functions (keeps the global namespace clean)
|
||||
- Avoid unexplained "magic numbers". Prefer named constants (`constexpr`) or C-style `#define` constants for repeated numbers that have the same meaning
|
||||
- Include `"wled.h"` as the primary project header where needed
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
## Header Guards
|
||||
|
||||
Most headers use `#ifndef` / `#define` guards. Some newer headers add `#pragma once` before the guard:
|
||||
|
||||
```cpp
|
||||
#ifndef WLED_EXAMPLE_H
|
||||
#define WLED_EXAMPLE_H
|
||||
// ...
|
||||
#endif // WLED_EXAMPLE_H
|
||||
```
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
|
||||
## Comments
|
||||
|
||||
- `//` for inline comments, `/* ... */` for block comments. Always put a space after `//`
|
||||
- **AI attribution:** When a larger block of code is generated by an AI tool, mark it with an `// AI:` comment so reviewers know to scrutinize it:
|
||||
|
||||
```cpp
|
||||
// AI: below section was generated by an AI
|
||||
void calculateCRC(const uint8_t* data, size_t len) {
|
||||
...
|
||||
}
|
||||
// AI: end
|
||||
```
|
||||
|
||||
Single-line AI-assisted edits do not need the marker — use it when the AI produced a contiguous block that a human did not write line-by-line.
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
<!-- hidden from AI for now, as it created too many "please add a description" review findings in my first tests -->
|
||||
- **Function & feature comments:** Every non-trivial function should have a brief comment above it describing what it does. Include a note about each parameter when the names alone are not self-explanatory:
|
||||
|
||||
```cpp
|
||||
/* *****
|
||||
* Apply gamma correction to a single color channel.
|
||||
* @param value raw 8-bit channel value (0–255)
|
||||
* @param gamma gamma exponent (typically 2.8)
|
||||
* @return corrected 8-bit value
|
||||
***** */
|
||||
uint8_t gammaCorrect(uint8_t value, float gamma);
|
||||
```
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
|
||||
Short accessor-style functions (getters/setters, one-liners) may skip this if their purpose is obvious from the name.
|
||||
|
||||
## Preprocessor & Feature Flags
|
||||
|
||||
- Prefer compile-time feature flags (`#ifdef` / `#ifndef`) over runtime checks where possible
|
||||
- Platform differentiation: `ARDUINO_ARCH_ESP32` vs `ESP8266`
|
||||
- PSRAM availability: `BOARD_HAS_PSRAM`
|
||||
|
||||
## Error Handling
|
||||
|
||||
- `DEBUG_PRINTF()` / `DEBUG_PRINTLN()` for developer diagnostics (compiled out unless `-D WLED_DEBUG`)
|
||||
- Don't rely on C++ exceptions — use return codes (`-1` / `false` for errors) and global flags (e.g. `errorFlag = ERR_LOW_MEM`). Some builds don't support C++ exceptions.
|
||||
|
||||
## Strings
|
||||
|
||||
- Use `const char*` for temporary/parsed strings
|
||||
- Avoid `String` (Arduino heap-allocated string) in hot paths; acceptable in config/setup code
|
||||
- Use `F("string")` for string constants (major RAM win on ESP8266; mostly overload/type compatibility on ESP32)
|
||||
- Store repeated strings as `static const char[] PROGMEM`
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
|
||||
On **ESP8266** this explicitly stores the string in flash (PROGMEM), saving precious RAM — every byte counts on that platform.
|
||||
On **ESP32**, `PROGMEM` is a no-op and string literals already reside in flash/rodata, so `F()` yields little RAM benefit but remains harmless (it satisfies `__FlashStringHelper*` overloads that some APIs expect).
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
|
||||
```cpp
|
||||
DEBUG_PRINTLN(F("WS client connected.")); // string stays in flash, not RAM
|
||||
DEBUG_PRINTF_P(PSTR("initE: Ignoring attempt for invalid ethernetType (%d)\n"), ethernetType); // format string stays in flash
|
||||
```
|
||||
|
||||
## Memory
|
||||
|
||||
- **PSRAM-aware allocation**: use `d_malloc()` (prefer DRAM), `p_malloc()` (prefer PSRAM) from `fcn_declare.h`
|
||||
- **Avoid Variable Length Arrays (VLAs)**: FreeRTOS task stacks are typically 2–8 KB. A runtime-sized VLA can silently exhaust the stack. Use fixed-size arrays or heap allocation (`d_malloc` / `p_malloc`). Any VLA must be explicitly justified in source or PR.
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
GCC/Clang support VLAs as an extension (they are not part of the C++ standard), so they look like a legitimate feature — but they are allocated on the stack at runtime. On ESP32/ESP8266, a VLA whose size depends on a runtime parameter (segment dimensions, pixel counts, etc.) can silently exhaust the stack and cause the program to behave in unexpected ways or crash.
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
- **Larger buffers** (LED data, JSON documents) should use PSRAM when available and technically feasible
|
||||
- **Hot-path**: some data should stay in DRAM or IRAM for performance reasons
|
||||
- Memory efficiency matters, but is less critical on boards with PSRAM
|
||||
|
||||
Heap fragmentation is a concern:
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
- Fragmentation can lead to crashes, even when the overall amount of available heap is still good. The C++ runtime doesn't do any "garbage collection".
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
- Avoid frequent `d_malloc` and `d_free` inside a function, especially for small sizes.
|
||||
- Avoid frequent creation / destruction of objects.
|
||||
- Allocate buffers early, and try to re-use them.
|
||||
- Instead of incrementally appending to a `String`, reserve the expected max buffer upfront by using the `reserve()` method.
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
|
||||
```cpp
|
||||
String result;
|
||||
result.reserve(65); // pre-allocate to avoid realloc fragmentation
|
||||
```
|
||||
|
||||
```cpp
|
||||
// prefer DRAM; falls back gracefully and enforces MIN_HEAP_SIZE guard
|
||||
_ledsDirty = (byte*) d_malloc(getBitArrayBytes(_len));
|
||||
```
|
||||
|
||||
```cpp
|
||||
_mode.reserve(_modeCount); // allocate memory to prevent initial fragmentation - does not increase size()
|
||||
_modeData.reserve(_modeCount); // allocate memory to prevent initial fragmentation - does not increase size()
|
||||
```
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
|
||||
## `const` and `constexpr`
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
`const` is a promise to the compiler that a value (or object) will not change - a function declared with a `const char* message` parameter is not allowed to modify the content of `message`.
|
||||
This pattern enables optimizations and makes intent clear to reviewers.
|
||||
|
||||
`constexpr` allows to define constants that are *guaranteed* to be evaluated by the compiler (zero run-time costs).
|
||||
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
- For function parameters that are read-only, prefer `const &` or `const`.
|
||||
|
||||
### `const` locals
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
* Adding `const` to a local variable that is only assigned once is optional, but *not* strictly necessary.
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
* In hot-path code, `const` on cached locals may help the compiler keep values in registers.
|
||||
```cpp
|
||||
const uint_fast16_t cols = vWidth();
|
||||
const uint_fast16_t rows = vHeight();
|
||||
```
|
||||
|
||||
### `const` references to avoid copies
|
||||
- Pass objects by `const &` (or `&`) instead of copying them implicitly.
|
||||
- Use `const &` (or `&`) inside loops - This avoids constructing temporary objects on every access.
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
```cpp
|
||||
const auto &m = _mappings[i]; // reference, not a copy (bus_manager.cpp)
|
||||
Segment& sourcesegment = strip.getSegment(sourceid); // alias — avoids creating a temporary Segment instance
|
||||
```
|
||||
|
||||
For function parameters that are read-only, prefer `const &`:
|
||||
```cpp
|
||||
BusManager::add(const BusConfig &bc, bool placeholder) {
|
||||
```
|
||||
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
- Class **Data Members:** Avoid reference data members (`T&` or `const T&`) in a class.
|
||||
A reference member can outlive the object it refers to, causing **dangling reference** bugs that are hard to diagnose. Prefer value storage or use a pointer and document the expected lifetime.
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
<!-- hidden from AI for now - codebase is not compliant to this rule (slowly migrating) -->
|
||||
### `constexpr` over `#define`
|
||||
|
||||
- Prefer `constexpr` for compile-time constants. Unlike `#define`, `constexpr` respects scope and type safety, keeping the global namespace clean.
|
||||
|
||||
```cpp
|
||||
// Prefer:
|
||||
constexpr uint32_t TWO_CHANNEL_MASK = 0x00FF00FF;
|
||||
constexpr int WLED_MAX_BUSSES = WLED_MAX_DIGITAL_CHANNELS + WLED_MAX_ANALOG_CHANNELS;
|
||||
|
||||
// Avoid (when possible):
|
||||
#define TWO_CHANNEL_MASK 0x00FF00FF
|
||||
```
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
|
||||
### `static_assert` over `#error`
|
||||
|
||||
- Use `static_assert` instead of the C-style `#if … #error … #endif` pattern when validating compile-time constants. It provides a clear message and works with `constexpr` values.
|
||||
- `#define` and `#if ... #else ... #endif` is still needed for conditional-compilation guards and build-flag-overridable values.
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
```cpp
|
||||
// Prefer:
|
||||
constexpr int WLED_MAX_BUSSES = WLED_MAX_DIGITAL_CHANNELS + WLED_MAX_ANALOG_CHANNELS;
|
||||
static_assert(WLED_MAX_BUSSES <= 32, "WLED_MAX_BUSSES exceeds hard limit");
|
||||
|
||||
// Avoid:
|
||||
#if (WLED_MAX_BUSSES > 32)
|
||||
#error "WLED_MAX_BUSSES exceeds hard limit"
|
||||
#endif
|
||||
```
|
||||
|
||||
```cpp
|
||||
// using static_assert() to validate enumerated types (zero cost at runtime)
|
||||
static_assert(0u == static_cast<uint8_t>(PinOwner::None),
|
||||
"PinOwner::None must be zero, so default array initialization works as expected");
|
||||
```
|
||||
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
### `static` and `const` class methods
|
||||
|
||||
#### `const` member functions
|
||||
|
||||
Marking a member function `const` tells the compiler that it does not modify the object's state:
|
||||
|
||||
```cpp
|
||||
uint16_t length() const { return _len; }
|
||||
bool isActive() const { return _active; }
|
||||
```
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
Benefits for GCC/Xtensa/RISC-V:
|
||||
- The compiler knows the method cannot write to `this`, so it is free to **keep member values in registers** across the call and avoid reload barriers.
|
||||
- `const` methods can be called on `const` objects and `const` references — essential when passing large objects as `const &` to avoid copying.
|
||||
- `const` allows the compiler to **eliminate redundant loads**: if a caller already has a member value cached, the compiler can prove the `const` call cannot invalidate it.
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
|
||||
Declare getter, query, or inspection methods `const`. If you need to mark a member `mutable` to work around this (e.g. for a cache or counter), document the reason.
|
||||
|
||||
#### `static` member functions
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
A `static` member function has no implicit `this` pointer. This has two distinct advantages:
|
||||
|
||||
1. **Smaller code, faster calls**: no `this` is passed in a register. On Xtensa and RISC-V, this removes one register argument from every call site and prevents the compiler from emitting `this`-preservation code around inlined blocks.
|
||||
2. **Better inlining**: GCC can inline a `static` method with more certainty because it cannot be overridden by a derived class (no virtual dispatch ambiguity) and has no aliasing concern through `this`.
|
||||
|
||||
Use `static` for any method that does not need access to instance members:
|
||||
|
||||
```cpp
|
||||
// Factory / utility — no instance needed:
|
||||
static BusConfig fromJson(JsonObject obj);
|
||||
|
||||
// Pure computation helpers:
|
||||
static uint8_t gamma8(uint8_t val);
|
||||
static uint32_t colorBalance(uint32_t color, uint8_t r, uint8_t g, uint8_t b);
|
||||
```
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
|
||||
`static` communicates intent clearly: a reviewer immediately knows the method is stateless and safe to call without a fully constructed object.
|
||||
|
||||
> **Rule of thumb**: if a method does not read or write any member variable, make it `static`. If it only reads member variables, make it `const`. Note: `static` methods cannot also be `const`-qualified because there is no implicit `this` pointer to be const — just use `static`. Both qualifiers reduce coupling and improve generated code on all ESP32 targets.
|
||||
|
||||
---
|
||||
|
||||
## Hot-Path Optimization
|
||||
|
||||
The hot path is the per-frame pixel pipeline: **Segment → Strip → BusManager → Bus(Digital,HUB75,Network) or PolyBus → LED driver, plus ``WS2812FX::show()`` and below**.
|
||||
Speed is the priority here. The patterns below are taken from existing hot-path code (`FX_fcn.cpp`, `FX_2Dfcn.cpp`, `bus_manager.cpp`, `colors.cpp`) and should be followed when modifying these files.
|
||||
|
||||
Note: `FX.cpp` (effect functions) is written by many contributors and has diverse styles — that is acceptable.
|
||||
|
||||
### Function Attributes
|
||||
|
||||
Stack the appropriate attributes on hot-path functions. Defined in `const.h`:
|
||||
|
||||
| Attribute | Meaning | When to use |
|
||||
|---|---|---|
|
||||
| `__attribute__((hot))` | Branch-prediction hint | hot-path functions with complex logic |
|
||||
| `IRAM_ATTR` | Place in fast IRAM (ESP32) | Critical per-pixel functions (e.g. `BusDigital::setPixelColor`) |
|
||||
| `IRAM_ATTR_YN` | IRAM on ESP32, no-op on ESP8266 | Hot functions that ESP8266 can't fit in IRAM |
|
||||
| `WLED_O2_ATTR` | Force `-O2` optimization | Most hot-path functions |
|
||||
| `WLED_O3_ATTR` | Force `-O3,fast-math` | Innermost color math (e.g. `color_blend`) |
|
||||
| `[[gnu::hot]] inline` | Modern C++ attribute + inline | Header-defined accessors (e.g. `progress()`, `currentBri()`) |
|
||||
|
||||
Note: `WLED_O3_ATTR` sometimes causes performance loss compared to `WLED_O2_ATTR`. Choose optimization levels based on test results.
|
||||
|
||||
Example signature:
|
||||
|
||||
```cpp
|
||||
void IRAM_ATTR_YN WLED_O2_ATTR __attribute__((hot)) Segment::setPixelColor(unsigned i, uint32_t c)
|
||||
```
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
### Cache Members to Locals Before Loops
|
||||
|
||||
Copy class members and virtual-call results to local variables before entering a loop:
|
||||
|
||||
```cpp
|
||||
uint_fast8_t count = numBusses; // avoid repeated member access
|
||||
for (uint_fast8_t i = 0; i < count; i++) {
|
||||
Bus* const b = busses[i]; // const pointer hints to compiler
|
||||
uint_fast16_t bstart = b->getStart();
|
||||
uint_fast16_t blen = b->getLength();
|
||||
...
|
||||
}
|
||||
```
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
|
||||
### Unsigned Range Check
|
||||
|
||||
Replace two-comparison range tests with a single unsigned subtraction:
|
||||
|
||||
```cpp
|
||||
// Instead of: if (pix >= bstart && pix < bstart + blen)
|
||||
if ((uint_fast16_t)(pix - bstart) < blen) // also catches negative pix via unsigned underflow
|
||||
```
|
||||
|
||||
### Early Returns
|
||||
|
||||
Guard every hot-path function with the cheapest necessary checks first:
|
||||
|
||||
```cpp
|
||||
if (!isActive()) return; // inactive segment
|
||||
if (unsigned(i) >= vLength()) return; // bounds check (catches negative i too)
|
||||
```
|
||||
|
||||
### Avoid Nested Calls — Fast Path / Complex Path
|
||||
|
||||
Avoid calling non-inline functions or making complex decisions inside per-pixel hot-path code. When a function has both a common simple case and a rare complex case, split it into two variants and choose once per frame rather than per pixel.
|
||||
|
||||
General rules:
|
||||
- Keep fast-path functions free of non-inline calls, multi-way branches, and complex switch-case decisions.
|
||||
- Hoist per-frame decisions (e.g. simple vs. complex segment) out of the per-pixel loop.
|
||||
- Code duplication between fast/slow variants is acceptable to keep the fast path lean.
|
||||
|
||||
### Function Pointers to Eliminate Repeated Decisions
|
||||
|
||||
When the same decision (e.g. "which drawing routine?") would be evaluated for every pixel, assign the chosen variant to a function pointer once and let the inner loop call through the pointer. This removes the branch entirely — the calling code (e.g. the GIF decoder loop) only ever invokes one function per frame, with no per-pixel decision.
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
`image_loader.cpp` demonstrates the pattern: `calculateScaling()` picks the best drawing callback once per frame based on segment dimensions and GIF size, then passes it to the decoder via `setDrawPixelCallback()`:
|
||||
|
||||
```cpp
|
||||
// calculateScaling() — called once per frame
|
||||
if ((perPixelX < 2) && (perPixelY < 2))
|
||||
decoder.setDrawPixelCallback(drawPixelCallbackDownScale2D); // downscale-only variant
|
||||
else
|
||||
decoder.setDrawPixelCallback(drawPixelCallback2D); // full-scaling variant
|
||||
```
|
||||
|
||||
Each callback is a small, single-purpose function with no internal branching — the decoder's per-pixel loop never re-evaluates which strategy to use.
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
### Template Specialization (Advanced)
|
||||
|
||||
Templates can eliminate runtime decisions by generating separate code paths at compile time. For example, a pixel setter could be templated on color order or channel count so the compiler removes dead branches and produces tight, specialized machine code:
|
||||
|
||||
```cpp
|
||||
template<bool hasWhite>
|
||||
void setChannel(uint8_t* out, uint32_t col) {
|
||||
out[0] = R(col); out[1] = G(col); out[2] = B(col);
|
||||
if constexpr (hasWhite) out[3] = W(col); // compiled out when hasWhite==false
|
||||
}
|
||||
```
|
||||
|
||||
Use sparingly — each instantiation duplicates code in flash. On ESP8266 and small-flash ESP32 boards this can exhaust IRAM/flash. Prefer templates only when the hot path is measurably faster and the number of instantiations is small (2–4).
|
||||
|
||||
### RAII Lock-Free Synchronization (Advanced)
|
||||
|
||||
Where contention is rare and the critical section is short, consider replacing mutex-based locking with lock-free techniques using `std::atomic` and RAII scoped guards. A scoped guard sets a flag on construction and clears it on destruction, guaranteeing cleanup even on early return:
|
||||
|
||||
```cpp
|
||||
struct ScopedBusyFlag {
|
||||
std::atomic<bool>& flag;
|
||||
bool acquired;
|
||||
ScopedBusyFlag(std::atomic<bool>& f) : flag(f), acquired(false) {
|
||||
bool expected = false;
|
||||
acquired = flag.compare_exchange_strong(expected, true);
|
||||
}
|
||||
~ScopedBusyFlag() { if (acquired) flag.store(false); }
|
||||
explicit operator bool() const { return acquired; }
|
||||
};
|
||||
|
||||
// Usage
|
||||
static std::atomic<bool> busySending{false};
|
||||
ScopedBusyFlag guard(busySending);
|
||||
if (!guard) return; // another task is already sending
|
||||
// ... do work — flag auto-clears when guard goes out of scope
|
||||
```
|
||||
|
||||
This avoids FreeRTOS semaphore overhead and the risk of forgetting to return a semaphore. There are no current examples of this pattern in the codebase — consult with maintainers before introducing it in new code, to ensure it aligns with the project's synchronization conventions.
|
||||
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
### Pre-Compute Outside Loops
|
||||
|
||||
Move invariant calculations before the loop. Pre-compute reciprocals to replace division with multiplication.
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
```cpp
|
||||
const uint_fast16_t cols = virtualWidth();
|
||||
const uint_fast16_t rows = virtualHeight();
|
||||
uint_fast8_t fadeRate = (255U - rate) >> 1;
|
||||
float mappedRate_r = 1.0f / (float(fadeRate) + 1.1f); // reciprocal — avoid division inside loop
|
||||
```
|
||||
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
### Parallel Channel Processing
|
||||
|
||||
Process R+B and W+G channels simultaneously using the two-channel mask pattern:
|
||||
|
||||
```cpp
|
||||
constexpr uint32_t TWO_CHANNEL_MASK = 0x00FF00FF;
|
||||
uint32_t rb = (((c1 & TWO_CHANNEL_MASK) * amount) >> 8) & TWO_CHANNEL_MASK;
|
||||
uint32_t wg = (((c1 >> 8) & TWO_CHANNEL_MASK) * amount) & ~TWO_CHANNEL_MASK;
|
||||
return rb | wg;
|
||||
```
|
||||
|
||||
### Bit Shifts Over Division (mainly for RISC-V boards)
|
||||
|
||||
ESP32 and ESP32-S3 (Xtensa core) have a fast "integer divide" instruction, so manual shifts rarely help.
|
||||
On RISC-V targets (ESP32-C3/C6/P4) and ESP8266, prefer explicit bit-shifts for power-of-two arithmetic — the compiler does **not** always convert divisions to shifts.
|
||||
Always use unsigned operands for right shifts; signed right-shift is implementation-defined.
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
On RISC-V-based boards (ESP32-C3, ESP32-C6, ESP32-C5) explicit shifts can be beneficial.
|
||||
```cpp
|
||||
position >> 3 // instead of position / 8
|
||||
(255U - rate) >> 1 // instead of (255 - rate) / 2
|
||||
i & 0x0007 // instead of i % 8
|
||||
```
|
||||
|
||||
**Important**: The bit-shifted expression should be unsigned. On some MCUs, "signed right-shift" is implemented by an "arithmetic shift right" that duplicates the sign bit: ``0b1010 >> 1 = 0b1101``.
|
||||
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
### Static Caching for Expensive Computations
|
||||
|
||||
Cache results in static locals when the input rarely changes between calls:
|
||||
|
||||
```cpp
|
||||
static uint16_t lastKelvin = 0;
|
||||
static byte correctionRGB[4] = {255,255,255,0};
|
||||
if (lastKelvin != kelvin) {
|
||||
colorKtoRGB(kelvin, correctionRGB); // expensive — only recalculate when input changes
|
||||
lastKelvin = kelvin;
|
||||
}
|
||||
```
|
||||
|
||||
### Inlining Strategy
|
||||
|
||||
- Move frequently-called small functions to headers for inlining (e.g. `Segment::setPixelColorRaw` is in `FX.h`)
|
||||
- Use `static inline` for file-local helpers
|
||||
|
||||
### Math & Trigonometric Functions
|
||||
|
||||
- WLED uses a custom `fastled_slim` library. The old FastLED trig aliases (`sin8`, `cos8`, `sin16`, `cos16`) **no longer exist and cause a compile error** — use `sin8_t()`, `cos8_t()`, `sin16_t()`, `cos16_t()` instead. For float approximations use `sin_approx()` / `cos_approx()` instead of `sinf()` / `cosf()`. Replace FastLED noise aliases (`inoise8`, `inoise16`) with `perlin8`, `perlin16`.
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
|
||||
| ❌ Do not use (compile error) | ✅ Use instead | Source |
|
||||
|---|---|---|
|
||||
| `sin8()`, `cos8()` | `sin8_t()`, `cos8_t()` | `fastled_slim.h` → `wled_math.cpp` |
|
||||
| `sin16()`, `cos16()` | `sin16_t()`, `cos16_t()` | `fastled_slim.h` → `wled_math.cpp` |
|
||||
| `sinf()`, `cosf()` | `sin_approx()`, `cos_approx()` | `wled_math.cpp` |
|
||||
| `atan2f()`, `atan2()` | `atan2_t()` | `wled_math.cpp` |
|
||||
| `sqrt()` on integers | `sqrt32_bw()` | `fcn_declare.h` → `wled_math.cpp` |
|
||||
| `sqrtf()` on floats | `sqrtf()` (acceptable) | — no WLED replacement |
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
|
||||
---
|
||||
|
||||
## `delay()` vs `yield()` in ESP32 Tasks
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
* On ESP32, `delay(ms)` calls `vTaskDelay(ms / portTICK_PERIOD_MS)`, which **suspends only the calling task**. The FreeRTOS scheduler immediately runs all other ready tasks.
|
||||
* The Arduino `loop()` function runs inside `loopTask`. Calling `delay()` there does *not* block the network stack, audio FFT, LED DMA, nor any other FreeRTOS task.
|
||||
* This differs from ESP8266, where `delay()` stalls the entire system unless `yield()` was called inside.
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
|
||||
- On ESP32, `delay()` is generally allowed, as it helps to efficiently manage CPU usage of all tasks.
|
||||
- On ESP8266, only use `delay()` and `yield()` in the main `loop()` context. If not sure, protect with `if (can_yield()) ...`.
|
||||
- Do *not* use `delay()` in effects (FX.cpp) or in the hot pixel path.
|
||||
- `delay()` on the bus-level is allowed, it might be needed to achieve exact timing in LED drivers.
|
||||
|
||||
### IDLE Watchdog and Custom Tasks on ESP32
|
||||
|
||||
- In arduino-esp32, `yield()` calls `vTaskDelay(0)`, which only switches to tasks at equal or higher priority — the IDLE task (priority 0) is never reached.
|
||||
- **Do not use `yield()` to pace ESP32 tasks or assume it feeds any watchdog**.
|
||||
- **Custom `xTaskCreate()` tasks must call `delay(1)` in their loop, not `yield()`.** Without a real blocking call, the IDLE task is starved. The IDLE watchdog panic is the first visible symptom — but the damage starts earlier: deleted task memory leaks, software timers stop firing, light sleep is disabled, and Wi-Fi/BT idle hooks don't run. Structure custom tasks like this:
|
||||
```cpp
|
||||
// WRONG — IDLE task is never scheduled; yield() does not feed the idle task watchdog.
|
||||
void myTask(void*) {
|
||||
for (;;) {
|
||||
doWork();
|
||||
yield();
|
||||
}
|
||||
}
|
||||
|
||||
// CORRECT — delay(1) suspends the task for ≥1 ms, IDLE task runs, IDLE watchdog is fed
|
||||
void myTask(void*) {
|
||||
for (;;) {
|
||||
doWork();
|
||||
delay(1); // DO NOT REMOVE — lets IDLE(0) run and feeds its watchdog
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- Prefer blocking FreeRTOS primitives (`xQueueReceive`, `ulTaskNotifyTake`, `vTaskDelayUntil`) over `delay(1)` polling where precise timing or event-driven behaviour is needed.
|
||||
- **Watchdog note.** WLED disables the Task Watchdog by default (`WLED_WATCHDOG_TIMEOUT 0` in `wled.h`). When enabled, `esp_task_wdt_reset()` is called at the end of each `loop()` iteration. Long blocking operations inside `loop()` — such as OTA downloads or slow file I/O — must call `esp_task_wdt_reset()` periodically, or be restructured so the main loop is not blocked for longer than the configured timeout.
|
||||
|
||||
## Caveats and Pitfalls
|
||||
|
||||
- **LittleFS filenames**: File paths passed to `file.open()` must not exceed 255 bytes (`LFS_NAME_MAX`). Validate constructed paths (e.g., `/ledmap_` + segment name + `.json`) stay within this limit (assume standard configurations, like WLED_MAX_SEGNAME_LEN = 64).
|
||||
|
||||
- **Float-to-unsigned conversion is undefined behavior when the value is out of range.** Converting a negative `float` directly to an unsigned integer type (`uint8_t`, `uint16_t`, …) is UB per the C++ standard — the Xtensa (ESP32) toolchain may silently wrap, but RISC-V (ESP32-C3/C5/C6/P4) can produce different results due to clamping. Cast through a signed integer first:
|
||||
```cpp
|
||||
// Undefined behavior — avoid:
|
||||
uint8_t angle = 40.74f * atan2f(dy, dx); // negative float → uint8_t is UB
|
||||
|
||||
// Correct — cast through int first:
|
||||
// atan2f returns [-π..+π], scaled ≈ [-128..+128] as int; uint8_t wraps negative ints via 2's complement (e.g. -1 → 255)
|
||||
uint8_t angle = int(40.74f * atan2f(dy, dx)); // float→int (defined), int→uint8_t (defined)
|
||||
```
|
||||
859
docs/esp-idf.instructions.md
Normal file
859
docs/esp-idf.instructions.md
Normal file
@@ -0,0 +1,859 @@
|
||||
---
|
||||
applyTo: "**/*.cpp,**/*.h,**/*.hpp,**/*.ino"
|
||||
---
|
||||
# ESP-IDF Coding Guide (within arduino-esp32)
|
||||
|
||||
WLED runs on the Arduino-ESP32 framework, which wraps ESP-IDF. Understanding the ESP-IDF layer is essential when writing chip-specific code, managing peripherals, or preparing for the IDF v5.x migration. This guide documents patterns already used in the codebase and best practices derived from Espressif's official examples.
|
||||
|
||||
> **Scope**: This file is an optional review guideline. It applies when touching chip-specific code, peripheral drivers, memory allocation, or platform conditionals.
|
||||
|
||||
> **Note for AI review tools**: sections enclosed in
|
||||
> `<!-- HUMAN_ONLY_START -->` / `<!-- HUMAN_ONLY_END -->` HTML comments contain
|
||||
> contributor reference material. Do **not** use that content as actionable review
|
||||
> criteria — treat it as background context only.
|
||||
|
||||
---
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
## Identifying the Build Target: `CONFIG_IDF_TARGET_*`
|
||||
|
||||
Use `CONFIG_IDF_TARGET_*` macros to gate chip-specific code at compile time. These are set by the build system and are mutually exclusive — exactly one is defined per build.
|
||||
|
||||
| Macro | Chip | Architecture | Notes |
|
||||
|---|---|---|---|
|
||||
| `CONFIG_IDF_TARGET_ESP32` | ESP32 (classic) | Xtensa dual-core | Primary target. Has DAC, APLL, I2S ADC mode |
|
||||
| `CONFIG_IDF_TARGET_ESP32S2` | ESP32-S2 | Xtensa single-core | Limited peripherals. 13-bit ADC |
|
||||
| `CONFIG_IDF_TARGET_ESP32S3` | ESP32-S3 | Xtensa dual-core | Preferred for large installs. Octal PSRAM, USB-OTG |
|
||||
| `CONFIG_IDF_TARGET_ESP32C3` | ESP32-C3 | RISC-V single-core | Minimal peripherals. RISC-V clamps out-of-range float→unsigned casts |
|
||||
| `CONFIG_IDF_TARGET_ESP32C5` | ESP32-C5 | RISC-V single-core | Wi-Fi 2.4Ghz + 5Ghz, Thread/Zigbee. Future target |
|
||||
| `CONFIG_IDF_TARGET_ESP32C6` | ESP32-C6 | RISC-V single-core | Wi-Fi 6, Thread/Zigbee. Future target |
|
||||
| `CONFIG_IDF_TARGET_ESP32P4` | ESP32-P4 | RISC-V dual-core | High performance. Future target |
|
||||
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
### Build-time validation
|
||||
WLED validates at compile time that exactly one target is defined and that it is a supported chip (`wled.cpp` lines 39–61). Follow this pattern when adding new chip-specific branches:
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
```cpp
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32)
|
||||
// classic ESP32 path
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
// S3-specific path
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32P4)
|
||||
// RISC-V common path
|
||||
#else
|
||||
#warning "Untested chip — review peripheral availability"
|
||||
#endif
|
||||
```
|
||||
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
### Guidelines
|
||||
|
||||
- **Always test on the actual chip** before claiming support. Simulators and cross-compilation can hide peripheral differences.
|
||||
- **Prefer `#elif` chains** over nested `#ifdef` for readability.
|
||||
- **Do not use `CONFIG_IDF_TARGET_*` for feature detection.** Use `SOC_*` capability macros instead (see next section). For example, use `SOC_I2S_SUPPORTS_ADC` instead of `CONFIG_IDF_TARGET_ESP32` to check for I2S ADC support.
|
||||
- When a feature must be disabled on certain chips, use explicit `static_assert()` or `#warning` directives so the build clearly reports what is missing.
|
||||
|
||||
---
|
||||
|
||||
## Hardware Capability Detection: `SOC_*` Macros
|
||||
|
||||
`SOC_*` macros (from `soc/soc_caps.h`) describe what the current chip supports. They are the correct way to check for peripheral features — they stay accurate when new chips are added, unlike `CONFIG_IDF_TARGET_*` checks.
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
### Important `SOC_*` macros used in WLED
|
||||
|
||||
| Macro | Type | Used in | Purpose |
|
||||
|---|---|---|---|
|
||||
| `SOC_I2S_NUM` | `int` | `audio_source.h` | Number of I2S peripherals (1 or 2) |
|
||||
| `SOC_I2S_SUPPORTS_ADC` | `bool` | `usermods/audioreactive/audio_source.h` | I2S ADC sampling mode (ESP32 only) |
|
||||
| `SOC_I2S_SUPPORTS_APLL` | `bool` | `usermods/audioreactive/audio_source.h` | Audio PLL for precise sample rates |
|
||||
| `SOC_I2S_SUPPORTS_PDM_RX` | `bool` | `usermods/audioreactive/audio_source.h` | PDM microphone input |
|
||||
| `SOC_ADC_MAX_BITWIDTH` | `int` | `util.cpp` | ADC resolution (12 or 13 bits). Renamed to `CONFIG_SOC_ADC_RTC_MAX_BITWIDTH` in IDF v5 |
|
||||
| `SOC_ADC_CHANNEL_NUM(unit)` | `int` | `pin_manager.cpp` | ADC channels per unit |
|
||||
| `SOC_UART_NUM` | `int` | `dmx_input.cpp` | Number of UART peripherals |
|
||||
| `SOC_DRAM_LOW` / `SOC_DRAM_HIGH` | `addr` | `util.cpp` | DRAM address boundaries for validation |
|
||||
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
### Key pitfall
|
||||
`SOC_ADC_MAX_BITWIDTH` (ADC resolution 12 or 13 bits) was renamed to `CONFIG_SOC_ADC_RTC_MAX_BITWIDTH` in IDF v5.
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
### Less commonly used but valuable
|
||||
|
||||
| Macro | Purpose |
|
||||
|---|---|
|
||||
| `SOC_RMT_TX_CANDIDATES_PER_GROUP` | Number of RMT TX channels (varies 2–8 by chip) |
|
||||
| `SOC_LEDC_CHANNEL_NUM` | Number of LEDC (PWM) channels |
|
||||
| `SOC_GPIO_PIN_COUNT` | Total GPIO pin count |
|
||||
| `SOC_DAC_SUPPORTED` | Whether the chip has a DAC (ESP32/S2 only) |
|
||||
| `SOC_SPIRAM_SUPPORTED` | Whether PSRAM interface exists |
|
||||
| `SOC_CPU_CORES_NUM` | Core count (1 or 2) — useful for task pinning decisions |
|
||||
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
|
||||
### Best practices
|
||||
|
||||
```cpp
|
||||
// Good: feature-based detection
|
||||
#if SOC_I2S_SUPPORTS_PDM_RX
|
||||
_config.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM);
|
||||
#else
|
||||
#warning "PDM microphones not supported on this chip"
|
||||
#endif
|
||||
|
||||
// Avoid: chip-name-based detection
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
// happens to be correct today, but breaks when a new chip adds PDM support
|
||||
#endif
|
||||
```
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
### PSRAM capability macros
|
||||
|
||||
For PSRAM presence, mode, and DMA access patterns:
|
||||
|
||||
| Macro | Meaning |
|
||||
|---|---|
|
||||
| `CONFIG_SPIRAM` / `BOARD_HAS_PSRAM` | PSRAM is present in the build configuration |
|
||||
| `CONFIG_SPIRAM_MODE_QUAD` | Quad-SPI PSRAM (standard, used on ESP32 classic and some S2/S3 boards) |
|
||||
| `CONFIG_SPIRAM_MODE_OCT` | Octal-SPI PSRAM — 8 data lines, DTR mode. Used on ESP32-S3 with octal PSRAM (e.g. N8R8 / N16R8 modules). Reserves GPIO 33–37 for the PSRAM bus — **do not allocate these pins** when this macro is defined. `wled.cpp` uses this to gate GPIO reservation. |
|
||||
| `CONFIG_SPIRAM_MODE_HEX` | Hex-SPI (16-line) PSRAM — future interface on ESP32-P4 running at up to 200 MHz. Used in `json.cpp` to report the PSRAM mode. |
|
||||
| `CONFIG_SOC_PSRAM_DMA_CAPABLE` | PSRAM buffers can be used with DMA (ESP32-S3 with octal PSRAM) |
|
||||
| `CONFIG_SOC_MEMSPI_FLASH_PSRAM_INDEPENDENT` | SPI flash and PSRAM on separate buses (no speed contention) |
|
||||
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
|
||||
#### Detecting octal/hex flash
|
||||
|
||||
On ESP32-S3 modules with OPI flash (e.g. N8R8 modules where the SPI flash itself runs in Octal-PI mode), the build system sets:
|
||||
|
||||
| Macro | Meaning |
|
||||
|---|---|
|
||||
| `CONFIG_ESPTOOLPY_FLASHMODE_OPI` | Octal-PI flash mode. On S3, implies GPIO 33–37 are used by the flash/PSRAM interface — the same GPIO block as octal PSRAM. `wled.cpp` uses `CONFIG_ESPTOOLPY_FLASHMODE_OPI \|\| (CONFIG_SPIRAM_MODE_OCT && BOARD_HAS_PSRAM)` to decide whether to reserve these GPIOs. `json.cpp` uses this to report the flash mode string as `"🚀OPI"`. |
|
||||
| `CONFIG_ESPTOOLPY_FLASHMODE_HEX` | Hex flash mode (ESP32-P4). Reported as `"🚀🚀HEX"` in `json.cpp`. |
|
||||
|
||||
**Pattern used in WLED** (from `wled.cpp`) to reserve the octal-bus GPIOs on S3:
|
||||
```cpp
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
#if CONFIG_ESPTOOLPY_FLASHMODE_OPI || (CONFIG_SPIRAM_MODE_OCT && defined(BOARD_HAS_PSRAM))
|
||||
// S3: GPIO 33-37 are used by the octal PSRAM/flash bus
|
||||
managed_pin_type pins[] = { {33, true}, {34, true}, {35, true}, {36, true}, {37, true} };
|
||||
pinManager.allocateMultiplePins(pins, sizeof(pins)/sizeof(managed_pin_type), PinOwner::SPI_RAM);
|
||||
#endif
|
||||
#endif
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ESP-IDF Version Conditionals
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
### Checking the IDF version
|
||||
|
||||
```cpp
|
||||
#include <esp_idf_version.h>
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
// IDF v5+ code path
|
||||
#elif ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
|
||||
// IDF v4.4+ code path
|
||||
#else
|
||||
// Legacy IDF v3/v4.x path
|
||||
#endif
|
||||
```
|
||||
|
||||
### Key ESP-IDF version thresholds for WLED
|
||||
|
||||
| Version | What changed |
|
||||
|---|---|
|
||||
| **4.0.0** | Filesystem API (`SPIFFS`/`LittleFS`), GPIO driver overhaul |
|
||||
| **4.2.0** | ADC/GPIO API updates; `esp_adc_cal` introduced |
|
||||
| **4.4.0** | I2S driver refactored (legacy API remains); `adc_deprecated.h` headers appear for newer targets |
|
||||
| **4.4.4–4.4.8** | Known I2S channel-swap regression on ESP32 (workaround in `audio_source.h`) |
|
||||
| **5.0.0** | **Major breaking changes** — RMT, I2S, ADC, SPI flash APIs replaced (see migration section) |
|
||||
| **5.1.0** | Matter protocol support; new `esp_flash` API stable |
|
||||
| **5.3+** | arduino-esp32 v3.x compatibility; C6/P4 support |
|
||||
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
### Guidelines
|
||||
|
||||
- When adding a version guard, **always include a comment** explaining *what* changed and *why* the guard is needed.
|
||||
- Avoid version ranges that silently break — prefer `>=` over exact version matches.
|
||||
- Known regressions should use explicit range guards:
|
||||
```cpp
|
||||
// IDF 4.4.4–4.4.8 swapped I2S left/right channels (fixed in 4.4.9)
|
||||
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 4)) && \
|
||||
(ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(4, 4, 8))
|
||||
#define I2S_CHANNELS_SWAPPED
|
||||
#endif
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Migrating from ESP-IDF v4.4.x to v5.x
|
||||
|
||||
The jump from IDF v4.4 (arduino-esp32 v2.x) to IDF v5.x (arduino-esp32 v3.x) is the largest API break in ESP-IDF history. This section documents the critical changes and recommended migration patterns based on the upstream WLED `V5-C6` branch (`https://github.com/wled/WLED/tree/V5-C6`). Note: WLED has not yet migrated to IDF v5 — these patterns prepare for the future migration.
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
### Compiler changes
|
||||
|
||||
IDF v5.x ships a much newer GCC toolchain. Key versions:
|
||||
|
||||
| ESP-IDF | GCC | C++ default | Notes |
|
||||
|---|---|---|---|
|
||||
| 4.4.x (current) | **8.4.0** | C++17 (gnu++17) | Xtensa + RISC-V |
|
||||
| 5.1–5.3 | **13.2** | C++20 (gnu++2b) | Significant warning changes |
|
||||
| 5.4–5.5 | **14.2** | C++23 (gnu++2b) | Latest; stricter diagnostics |
|
||||
|
||||
Notable behavioral differences:
|
||||
|
||||
| Change | Impact | Action |
|
||||
|---|---|---|
|
||||
| Stricter `-Werror=enum-conversion` | Implicit int-to-enum casts now error | Use explicit `static_cast<>` or typed enums |
|
||||
| C++20/23 features available | `consteval`, `concepts`, `std::span`, `std::expected` | Use judiciously — ESP8266 builds still require GCC 10.x with C++17 |
|
||||
| `-Wdeprecated-declarations` enforced | Deprecated API calls become warnings/errors | Migrate to new APIs (see below) |
|
||||
| `-Wdangling-reference` (GCC 13+) | Warns when a reference binds to a temporary that will be destroyed | Fix the lifetime issue; do not suppress the warning |
|
||||
| `-fno-common` default (GCC 12+) | Duplicate tentative definitions across translation units cause linker errors | Use `extern` declarations in headers, define in exactly one `.cpp` |
|
||||
| RISC-V codegen improvements | C3/C6/P4 benefit from better register allocation | No action needed — automatic |
|
||||
|
||||
### C++ language features: GCC 8 → GCC 14
|
||||
|
||||
The jump from GCC 8.4 to GCC 14.2 spans six major compiler releases. This section lists features that become available and patterns that need updating.
|
||||
|
||||
#### Features safe to use after migration
|
||||
|
||||
These work in GCC 13+/14+ but **not** in GCC 8.4. Guard with `#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)` if the code must compile on both IDF v4 and v5.
|
||||
|
||||
| Feature | Standard | Example | Benefit |
|
||||
|---|---|---|---|
|
||||
| Designated initializers (C++20) | C++20 | `gpio_config_t cfg = { .mode = GPIO_MODE_OUTPUT };` | Already used as a GNU extension in GCC 8; becomes standard and portable in C++20 |
|
||||
| `[[likely]]` / `[[unlikely]]` | C++20 | `if (err != ESP_OK) [[unlikely]] { ... }` | Hints for branch prediction; useful in hot paths |
|
||||
| `[[nodiscard("reason")]]` | C++20 | `[[nodiscard("leak if ignored")]] void* allocBuffer();` | Enforces checking return values — helpful for `esp_err_t` wrappers |
|
||||
| `std::span<T>` | C++20 | `void process(std::span<uint8_t> buf)` | Safe, non-owning view of contiguous memory — replaces raw pointer + length pairs |
|
||||
| `consteval` | C++20 | `consteval uint32_t packColor(...)` | Guarantees compile-time evaluation; useful for color constants |
|
||||
| `constinit` | C++20 | `constinit static int counter = 0;` | Prevents static initialization order fiasco |
|
||||
| Concepts / `requires` | C++20 | `template<typename T> requires std::integral<T>` | Clearer constraints than SFINAE; improves error messages |
|
||||
| Three-way comparison (`<=>`) | C++20 | `auto operator<=>(const Version&) const = default;` | Less boilerplate for comparable types |
|
||||
| `std::bit_cast` | C++20 | `float f = std::bit_cast<float>(uint32_val);` | Type-safe reinterpretation — replaces `memcpy` or `union` tricks |
|
||||
| `if consteval` | C++23 | `if consteval { /* compile-time */ } else { /* runtime */ }` | Cleaner than `std::is_constant_evaluated()` |
|
||||
| `std::expected<T, E>` | C++23 | `std::expected<int, esp_err_t> readSensor()` | Monadic error handling — cleaner than returning error codes |
|
||||
| `std::to_underlying` | C++23 | `auto val = std::to_underlying(myEnum);` | Replaces `static_cast<int>(myEnum)` |
|
||||
|
||||
#### Features already available in GCC 8 (C++17)
|
||||
|
||||
These work on both IDF v4.4 and v5.x — prefer them now:
|
||||
|
||||
| Feature | Example | Notes |
|
||||
|---|---|---|
|
||||
| `if constexpr` | `if constexpr (sizeof(T) == 4) { ... }` | Compile-time branching; already used in WLED |
|
||||
| `std::optional<T>` | `std::optional<uint8_t> pin;` | Nullable value without sentinel values like `-1` |
|
||||
| `std::string_view` | `void log(std::string_view msg)` | Non-owning, non-allocating string reference |
|
||||
| Structured bindings | `auto [err, value] = readSensor();` | Useful with `std::pair` / `std::tuple` returns |
|
||||
| Fold expressions | `(addSegment(args), ...);` | Variadic template expansion |
|
||||
| Inline variables | `inline constexpr int MAX_PINS = 50;` | Avoids ODR issues with header-defined constants |
|
||||
| `[[maybe_unused]]` | `[[maybe_unused]] int debug_only = 0;` | Suppresses unused-variable warnings cleanly |
|
||||
| `[[fallthrough]]` | `case 1: doA(); [[fallthrough]]; case 2:` | Documents intentional switch fallthrough |
|
||||
| Nested namespaces | `namespace wled::audio { }` | Shorter than nested `namespace` blocks |
|
||||
|
||||
#### Patterns that break or change behavior
|
||||
|
||||
| Pattern | GCC 8 behavior | GCC 14 behavior | Fix |
|
||||
|---|---|---|---|
|
||||
| `int x; enum E e = x;` | Warning (often ignored) | Error with `-Werror=enum-conversion` | `E e = static_cast<E>(x);` |
|
||||
| `int g;` in two `.cpp` files | Both compile, linker merges (tentative definition) | Error: multiple definitions (`-fno-common`) | `extern int g;` in header, `int g;` in one `.cpp` |
|
||||
| `const char* ref = std::string(...).c_str();` | Silent dangling pointer | Warning (`-Wdangling-reference`) | Extend lifetime: store the `std::string` in a local variable |
|
||||
| `register int x;` | Accepted (ignored) | Warning or error (`register` removed in C++17) | Remove `register` keyword |
|
||||
| Narrowing in aggregate init | Warning | Error | Use explicit cast or wider type |
|
||||
| Implicit `this` capture in lambdas | Accepted in `[=]` | Deprecated warning; error in C++20 mode | Use `[=, this]` or `[&]` |
|
||||
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
#### Recommendations
|
||||
|
||||
- **Do not raise the minimum C++ standard yet.** WLED must still build on IDF v4.4 (GCC 8.4, C++17). Use `#if __cplusplus > 201703L` to gate C++20 features.
|
||||
- **Mark intentional fallthrough** with `[[fallthrough]]` — GCC 14 warns on unmarked fallthrough by default.
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
- **Prefer `std::optional` over sentinel values** (e.g., `-1` for "no pin") in new code — it works on both compilers.
|
||||
- **Use `std::string_view`** for read-only string parameters instead of `const char*` or `const String&` — zero-copy and works on GCC 8+.
|
||||
- **Avoid raw `union` type punning** — prefer `memcpy` (GCC 8) or `std::bit_cast` (GCC 13+) for strict-aliasing safety.
|
||||
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
### Deprecated and removed APIs
|
||||
|
||||
#### RMT (Remote Control Transceiver)
|
||||
|
||||
The legacy `rmt_*` functions are removed in IDF v5. Do not introduce new legacy RMT calls.
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
The new API is channel-based:
|
||||
|
||||
| IDF v4 (legacy) | IDF v5 (new) | Notes |
|
||||
|---|---|---|
|
||||
| `rmt_config()` + `rmt_driver_install()` | `rmt_new_tx_channel()` / `rmt_new_rx_channel()` | Channels are now objects |
|
||||
| `rmt_write_items()` | `rmt_transmit()` with encoder | Requires `rmt_encoder_t` |
|
||||
| `rmt_set_idle_level()` | Configure in channel config | Set at creation time |
|
||||
| `rmt_item32_t` | `rmt_symbol_word_t` | Different struct layout |
|
||||
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
**WLED impact**: NeoPixelBus LED output and IR receiver both use legacy RMT. The upstream `V5-C6` branch adds `-D WLED_USE_SHARED_RMT` and disables IR until the library is ported.
|
||||
|
||||
#### I2S (Inter-IC Sound)
|
||||
|
||||
Legacy `i2s_driver_install()` + `i2s_read()` API is deprecated. When touching audio source code, wrap legacy I2S init and reading in `#if ESP_IDF_VERSION_MAJOR < 5` / `#else`.
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
The new API uses channel handles:
|
||||
| IDF v4 (legacy) | IDF v5 (new) | Notes |
|
||||
|---|---|---|
|
||||
| `i2s_driver_install()` | `i2s_channel_init_std_mode()` | Separate STD/PDM/TDM modes |
|
||||
| `i2s_set_pin()` | Pin config in `i2s_std_gpio_config_t` | Set at init time |
|
||||
| `i2s_read()` | `i2s_channel_read()` | Uses channel handle |
|
||||
| `i2s_set_clk()` | `i2s_channel_reconfig_std_clk()` | Reconfigure running channel |
|
||||
| `i2s_config_t` | `i2s_std_config_t` | Separate config for each mode |
|
||||
|
||||
**Migration pattern** (from Espressif examples):
|
||||
```cpp
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#include "driver/i2s_std.h"
|
||||
i2s_chan_handle_t rx_handle;
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
|
||||
i2s_new_channel(&chan_cfg, NULL, &rx_handle);
|
||||
|
||||
i2s_std_config_t std_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(22050),
|
||||
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_MONO),
|
||||
.gpio_cfg = { .din = GPIO_NUM_32, .mclk = I2S_GPIO_UNUSED, ... },
|
||||
};
|
||||
i2s_channel_init_std_mode(rx_handle, &std_cfg);
|
||||
i2s_channel_enable(rx_handle);
|
||||
#else
|
||||
// Legacy i2s_driver_install() path
|
||||
#endif
|
||||
```
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
**WLED impact**: The audioreactive usermod (`audio_source.h`) heavily uses legacy I2S. Migration requires rewriting the `I2SSource` class for channel-based API.
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
#### ADC (Analog-to-Digital Converter)
|
||||
|
||||
Legacy `adc1_get_raw()` and `esp_adc_cal_*` are deprecated:
|
||||
|
||||
| IDF v4 (legacy) | IDF v5 (new) | Notes |
|
||||
|---|---|---|
|
||||
| `adc1_config_width()` + `adc1_get_raw()` | `adc_oneshot_new_unit()` + `adc_oneshot_read()` | Object-based API |
|
||||
| `esp_adc_cal_characterize()` | `adc_cali_create_scheme_*()` | Calibration is now scheme-based |
|
||||
| `adc_continuous_*` (old) | `adc_continuous_*` (restructured) | Config struct changes |
|
||||
|
||||
#### SPI Flash
|
||||
|
||||
| IDF v4 (legacy) | IDF v5 (new) |
|
||||
|---|---|
|
||||
| `spi_flash_read()` | `esp_flash_read()` |
|
||||
| `spi_flash_write()` | `esp_flash_write()` |
|
||||
| `spi_flash_erase_range()` | `esp_flash_erase_region()` |
|
||||
|
||||
WLED already has a compatibility shim in `ota_update.cpp` that maps old names to new ones.
|
||||
|
||||
#### GPIO
|
||||
|
||||
| IDF v4 (legacy) | IDF v5 (recommended) |
|
||||
|---|---|
|
||||
| `gpio_pad_select_gpio()` | `esp_rom_gpio_pad_select_gpio()` (or use `gpio_config()`) |
|
||||
| `gpio_set_direction()` + `gpio_set_pull_mode()` | `gpio_config()` with `gpio_config_t` struct |
|
||||
|
||||
### Features disabled in IDF v5 builds
|
||||
|
||||
The upstream `V5-C6` branch explicitly disables features with incompatible library dependencies:
|
||||
|
||||
```ini
|
||||
# platformio.ini [esp32_idf_V5]
|
||||
-D WLED_DISABLE_INFRARED # IR library uses legacy RMT
|
||||
-D WLED_DISABLE_MQTT # AsyncMqttClient incompatible with IDF v5
|
||||
-D ESP32_ARDUINO_NO_RGB_BUILTIN # Prevents RMT driver conflict with built-in LED
|
||||
-D WLED_USE_SHARED_RMT # Use new shared RMT driver for NeoPixel output
|
||||
```
|
||||
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
### Migration checklist for new code
|
||||
|
||||
1. **Never use a removed API without a version guard.** Always provide both old and new paths, or disable the feature on IDF v5.
|
||||
2. **Test on both IDF v4.4 and v5.x builds** if the code must be backward-compatible.
|
||||
3. **Prefer the newer API** when writing new code — wrap the old API in an `#else` block.
|
||||
4. **Mark migration TODOs** with `// TODO(idf5):` so they are easy to find later.
|
||||
|
||||
---
|
||||
|
||||
## Memory Management: `heap_caps_*` Best Practices
|
||||
|
||||
ESP32 has multiple memory regions with different capabilities. Using the right allocator is critical for performance and stability.
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
### Memory regions
|
||||
|
||||
| Region | Flag | Speed | DMA | Size | Use for |
|
||||
|---|---|---|---|---|---|
|
||||
| DRAM | `MALLOC_CAP_INTERNAL \| MALLOC_CAP_8BIT` | Fast | Yes (ESP32) | 200–320 KB | Hot-path buffers, task stacks, small allocations |
|
||||
| IRAM | `MALLOC_CAP_EXEC` | Fastest | No | 32–128 KB | Code (automatic via `IRAM_ATTR`) |
|
||||
| PSRAM (SPIRAM) | `MALLOC_CAP_SPIRAM \| MALLOC_CAP_8BIT` | Slower | Chip-dependent | 2–16 MB | Large buffers, JSON documents, image data |
|
||||
| RTC RAM | `MALLOC_CAP_RTCRAM` | Moderate | No | 8 KB | Data surviving deep sleep; small persistent buffers |
|
||||
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
|
||||
### WLED allocation wrappers
|
||||
|
||||
WLED provides convenience wrappers with automatic fallback. **Always prefer these over raw `heap_caps_*` calls**:
|
||||
|
||||
| Function | Allocation preference | Use case |
|
||||
|---|---|---|
|
||||
| `d_malloc(size)` | RTC → DRAM → PSRAM | General-purpose; prefers fast memory |
|
||||
| `d_calloc(n, size)` | Same as `d_malloc`, zero-initialized | Arrays, structs |
|
||||
| `p_malloc(size)` | PSRAM → DRAM | Large buffers; prefers abundant memory |
|
||||
| `p_calloc(n, size)` | Same as `p_malloc`, zero-initialized | Large arrays |
|
||||
| `d_malloc_only(size)` | RTC → DRAM (no PSRAM fallback) | DMA buffers, time-critical data |
|
||||
|
||||
### PSRAM guidelines
|
||||
|
||||
- **Check availability**: always test `psramFound()` before assuming PSRAM is present.
|
||||
- **DMA compatibility**: on ESP32 (classic), PSRAM buffers are **not DMA-capable** — use `d_malloc_only()` to allocate DMA buffers in DRAM only. On ESP32-S3 with octal PSRAM (`CONFIG_SPIRAM_MODE_OCT`), PSRAM buffers *can* be used with DMA when `CONFIG_SOC_PSRAM_DMA_CAPABLE` is defined.
|
||||
- **JSON documents**: use the `PSRAMDynamicJsonDocument` allocator (defined in `wled.h`) to put large JSON documents in PSRAM:
|
||||
```cpp
|
||||
PSRAMDynamicJsonDocument doc(16384); // allocated in PSRAM if available
|
||||
```
|
||||
- **Fragmentation**: PSRAM allocations fragment less than DRAM because the region is larger. But avoid mixing small and large allocations in PSRAM — small allocations waste the MMU page granularity.
|
||||
- **Heap validation**: use `d_measureHeap()` and `d_measureContiguousFreeHeap()` to monitor remaining DRAM. Allocations that would drop free DRAM below `MIN_HEAP_SIZE` should go to PSRAM instead.
|
||||
- **Performance**: Keep hot-path data in DRAM. Prefer PSRAM for capacity-oriented buffers and monitor contiguous DRAM headroom.
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
PSRAM access is up to 15× slower than DRAM on ESP32, 3–10× slower than DRAM on ESP32-S3/-S2 with quad-SPI bus. On ESP32-S3 with octal PSRAM (`CONFIG_SPIRAM_MODE_OCT`), the penalty is smaller (~2×) because the 8-line DTR bus can transfer 8 bits in parallel at 80 MHz (120 MHz is possible with CONFIG_SPIRAM_SPEED_120M, which requires enabling experimental ESP-IDF features). On ESP32-P4 with hex PSRAM (`CONFIG_SPIRAM_MODE_HEX`), the 16-line bus runs at 200 MHz which brings it on-par with DRAM. Keep hot-path data in DRAM regardless, but consider that ESP32 often crashes when the largest DRAM chunk gets below 10 KB.
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
### Pattern: preference-based allocation
|
||||
|
||||
When you need a buffer that works on boards with or without PSRAM:
|
||||
|
||||
```cpp
|
||||
// Prefer PSRAM for large buffers, fall back to DRAM
|
||||
uint8_t* buf = (uint8_t*)heap_caps_malloc_prefer(bufSize, 2,
|
||||
MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT, // first choice: PSRAM
|
||||
MALLOC_CAP_DEFAULT); // fallback: any available
|
||||
// Or simply:
|
||||
uint8_t* buf = (uint8_t*)p_malloc(bufSize);
|
||||
```
|
||||
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
---
|
||||
|
||||
## I2S Audio: Best Practices
|
||||
|
||||
The audioreactive usermod uses I2S for microphone input. Key patterns:
|
||||
|
||||
### Port selection
|
||||
|
||||
```cpp
|
||||
constexpr i2s_port_t AR_I2S_PORT = I2S_NUM_0;
|
||||
// I2S_NUM_1 has limitations: no MCLK routing, no ADC support, no PDM support
|
||||
```
|
||||
|
||||
Always use `I2S_NUM_0` unless you have a specific reason and have verified support on all target chips.
|
||||
|
||||
### DMA buffer tuning
|
||||
|
||||
DMA buffer size controls latency vs. reliability:
|
||||
|
||||
| Scenario | `dma_buf_count` | `dma_buf_len` | Latency | Notes |
|
||||
|---|---|---|---|---|
|
||||
| With HUB75 matrix | 18 | 128 | ~100 ms | Higher count prevents I2S starvation during matrix DMA |
|
||||
| Without PSRAM | 24 | 128 | ~140 ms | More buffers compensate for slower interrupt response |
|
||||
| Default | 8 | 128 | ~46 ms | Acceptable for most setups |
|
||||
|
||||
### Interrupt priority
|
||||
|
||||
Choose interrupt priority based on coexistence with other drivers:
|
||||
|
||||
```cpp
|
||||
#ifdef WLED_ENABLE_HUB75MATRIX
|
||||
.intr_alloc_flags = ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL1, // level 1 (lowest) to avoid starving HUB75
|
||||
#else
|
||||
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL2 | ESP_INTR_FLAG_LEVEL3, // accept level 2 or 3 (allocator picks available)
|
||||
#endif
|
||||
```
|
||||
|
||||
### APLL (Audio PLL) usage
|
||||
|
||||
The ESP32 has an audio PLL for precise sample rates. Rules:
|
||||
|
||||
- Enable APLL when an MCLK pin is provided and precision matters.
|
||||
- **Disable APLL** when Ethernet or HUB75 is active — they also use the APLL.
|
||||
- APLL is broken on ESP32 revision 0 silicon.
|
||||
- Not all chips have APLL — gate with `SOC_I2S_SUPPORTS_APLL`.
|
||||
|
||||
```cpp
|
||||
#if !defined(SOC_I2S_SUPPORTS_APLL)
|
||||
_config.use_apll = false;
|
||||
#elif defined(WLED_USE_ETHERNET) || defined(WLED_ENABLE_HUB75MATRIX)
|
||||
_config.use_apll = false; // APLL conflict
|
||||
#endif
|
||||
```
|
||||
|
||||
### PDM microphone caveats
|
||||
|
||||
- Not supported on ESP32-C3 (`SOC_I2S_SUPPORTS_PDM_RX` not defined).
|
||||
- ESP32-S3 PDM has known issues: sample rate at 50% of expected, very low amplitude.
|
||||
- **16-bit data width**: Espressif's IDF documentation states that in PDM mode the data unit width is always 16 bits, regardless of the configured `bits_per_sample`.
|
||||
- See [espressif/esp-idf#8660](https://github.com/espressif/esp-idf/issues/8660) for the upstream issue.
|
||||
- **Flag `bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT` in PDM mode** — this causes the S3 low-amplitude symptom.
|
||||
- No clock pin (`I2S_CKPIN = -1`) triggers PDM mode in WLED.
|
||||
|
||||
---
|
||||
|
||||
## HUB75 LED Matrix: Best Practices
|
||||
|
||||
WLED uses the `ESP32-HUB75-MatrixPanel-I2S-DMA` library for HUB75 matrix output.
|
||||
|
||||
### Chip-specific panel limits
|
||||
|
||||
```cpp
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S3) && defined(BOARD_HAS_PSRAM)
|
||||
maxChainLength = 6; // S3 + PSRAM: up to 6 panels (DMA-capable PSRAM)
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
maxChainLength = 2; // S2: limited DMA channels
|
||||
#else
|
||||
maxChainLength = 4; // Classic ESP32: default
|
||||
#endif
|
||||
```
|
||||
|
||||
### Color depth vs. pixel count
|
||||
|
||||
The driver dynamically reduces color depth for larger displays to stay within DMA buffer limits:
|
||||
|
||||
| Pixel count | Color depth | Bits per pixel |
|
||||
|---|---|---|
|
||||
| ≤ `MAX_PIXELS_10BIT` | 10-bit (30-bit color) | High quality (experimental) |
|
||||
| ≤ `MAX_PIXELS_8BIT` | 8-bit (24-bit color) | Full quality |
|
||||
| ≤ `MAX_PIXELS_6BIT` | 6-bit (18-bit color) | Slight banding |
|
||||
| ≤ `MAX_PIXELS_4BIT` | 4-bit (12-bit color) | Visible banding |
|
||||
| larger | 3-bit (9-bit color) | Minimal color range |
|
||||
|
||||
### Resource conflicts
|
||||
|
||||
- **APLL**: HUB75 I2S DMA uses the APLL. Disable APLL in the audio I2S driver when HUB75 is active.
|
||||
- **I2S peripheral**: HUB75 uses `I2S_NUM_1` (or `I2S_NUM_0` on single-I2S chips). Audio must use the other port.
|
||||
- **Pin count**: HUB75 requires 13–14 GPIO pins. On ESP32-S2 this severely limits remaining GPIO.
|
||||
- **Reboot required**: on ESP32-S3, changing HUB75 driver options requires a full reboot — the I2S DMA cannot be reconfigured at runtime.
|
||||
|
||||
---
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
## GPIO Best Practices
|
||||
|
||||
### Prefer `gpio_config()` over individual calls
|
||||
|
||||
```cpp
|
||||
// Preferred: single struct-based configuration
|
||||
gpio_config_t io_conf = {
|
||||
.pin_bit_mask = (1ULL << pin),
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
};
|
||||
gpio_config(&io_conf);
|
||||
|
||||
// Avoid: multiple separate calls (more error-prone, deprecated in IDF v5)
|
||||
gpio_set_direction(pin, GPIO_MODE_OUTPUT);
|
||||
gpio_set_pull_mode(pin, GPIO_FLOATING);
|
||||
```
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
|
||||
### Pin manager integration
|
||||
|
||||
Always allocate pins through WLED's `pinManager` before using GPIO APIs:
|
||||
|
||||
```cpp
|
||||
if (!pinManager.allocatePin(myPin, true, PinOwner::UM_MyUsermod)) {
|
||||
return; // pin in use by another module
|
||||
}
|
||||
// Now safe to configure
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Timer Best Practices
|
||||
|
||||
### Microsecond timing
|
||||
|
||||
For high-resolution timing, prefer `esp_timer_get_time()` (microsecond resolution, 64-bit) over `millis()` or `micros()`.
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
|
||||
```cpp
|
||||
#include <esp_timer.h>
|
||||
int64_t now_us = esp_timer_get_time(); // monotonic, not affected by NTP
|
||||
```
|
||||
|
||||
> **Note**: In arduino-esp32, both `millis()` and `micros()` are thin wrappers around `esp_timer_get_time()` — they share the same monotonic clock source. Prefer the direct call when you need the full 64-bit value or ISR-safe access without truncation:
|
||||
> ```cpp
|
||||
> // arduino-esp32 internals (cores/esp32/esp32-hal-misc.c):
|
||||
> // unsigned long micros() { return (unsigned long)(esp_timer_get_time()); }
|
||||
> // unsigned long millis() { return (unsigned long)(esp_timer_get_time() / 1000ULL); }
|
||||
> ```
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
### Periodic timers
|
||||
|
||||
For periodic tasks with sub-millisecond precision, use `esp_timer`:
|
||||
|
||||
```cpp
|
||||
esp_timer_handle_t timer;
|
||||
esp_timer_create_args_t args = {
|
||||
.callback = myCallback,
|
||||
.arg = nullptr,
|
||||
.dispatch_method = ESP_TIMER_TASK, // run in timer task (not ISR)
|
||||
.name = "my_timer",
|
||||
};
|
||||
esp_timer_create(&args, &timer);
|
||||
esp_timer_start_periodic(timer, 1000); // 1 ms period
|
||||
```
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
|
||||
Always prefer `ESP_TIMER_TASK` dispatch over `ESP_TIMER_ISR` unless you need ISR-level latency — ISR callbacks have severe restrictions (no logging, no heap allocation, no FreeRTOS API calls).
|
||||
|
||||
### Precision waiting: coarse delay then spin-poll
|
||||
|
||||
When waiting for a precise future deadline (e.g., FPS limiting, protocol timing), avoid spinning the entire duration — that wastes CPU and starves other tasks. Instead, yield to FreeRTOS while time allows, then spin only for the final window.
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
```cpp
|
||||
// Wait until 'target_us' (a micros() / esp_timer_get_time() timestamp)
|
||||
long time_to_wait = (long)(target_us - micros());
|
||||
// Coarse phase: yield to FreeRTOS while we have more than ~2 ms remaining.
|
||||
// vTaskDelay(1) suspends the task for one RTOS tick, letting other task run freely.
|
||||
while (time_to_wait > 2000) {
|
||||
vTaskDelay(1);
|
||||
time_to_wait = (long)(target_us - micros());
|
||||
}
|
||||
// Fine phase: busy-poll the last ≤2 ms for microsecond accuracy.
|
||||
// micros() wraps esp_timer_get_time() so this is low-overhead.
|
||||
while ((long)(target_us - micros()) > 0) { /* spin */ }
|
||||
```
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
|
||||
> The threshold (2000 µs as an example) should be at least one RTOS tick (default 1 ms on ESP32) plus some margin. A value of 1500–3000 µs works well in practice.
|
||||
|
||||
---
|
||||
|
||||
## ADC Best Practices
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
### Version-aware ADC code
|
||||
|
||||
ADC is one of the most fragmented APIs across IDF versions:
|
||||
|
||||
```cpp
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
// IDF v5: oneshot driver
|
||||
#include "esp_adc/adc_oneshot.h"
|
||||
#include "esp_adc/adc_cali.h"
|
||||
adc_oneshot_unit_handle_t adc_handle;
|
||||
adc_oneshot_unit_init_cfg_t unit_cfg = { .unit_id = ADC_UNIT_1 };
|
||||
adc_oneshot_new_unit(&unit_cfg, &adc_handle);
|
||||
#else
|
||||
// IDF v4: legacy driver
|
||||
#include "driver/adc.h"
|
||||
#include "esp_adc_cal.h"
|
||||
adc1_config_width(ADC_WIDTH_BIT_12);
|
||||
int raw = adc1_get_raw(ADC1_CHANNEL_0);
|
||||
#endif
|
||||
```
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
|
||||
### Bit width portability
|
||||
|
||||
Not all chips have 12-bit ADC. `SOC_ADC_MAX_BITWIDTH` reports the maximum resolution (12 or 13 bits). Note that in IDF v5, this macro was renamed to `CONFIG_SOC_ADC_RTC_MAX_BITWIDTH`. Write version-aware guards:
|
||||
|
||||
```cpp
|
||||
// IDF v4: SOC_ADC_MAX_BITWIDTH IDF v5: CONFIG_SOC_ADC_RTC_MAX_BITWIDTH
|
||||
#if defined(CONFIG_SOC_ADC_RTC_MAX_BITWIDTH) // IDF v5+
|
||||
#define MY_ADC_MAX_BITWIDTH CONFIG_SOC_ADC_RTC_MAX_BITWIDTH
|
||||
#elif defined(SOC_ADC_MAX_BITWIDTH) // IDF v4
|
||||
#define MY_ADC_MAX_BITWIDTH SOC_ADC_MAX_BITWIDTH
|
||||
#else
|
||||
#define MY_ADC_MAX_BITWIDTH 12 // safe fallback
|
||||
#endif
|
||||
|
||||
#if MY_ADC_MAX_BITWIDTH == 13
|
||||
adc1_config_width(ADC_WIDTH_BIT_13); // ESP32-S2
|
||||
#else
|
||||
adc1_config_width(ADC_WIDTH_BIT_12); // ESP32, S3, C3, etc.
|
||||
#endif
|
||||
```
|
||||
|
||||
WLED's `util.cpp` uses the IDF v4 form (`SOC_ADC_MAX_BITWIDTH`) — this will need updating when the codebase migrates to IDF v5.
|
||||
|
||||
---
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
## RMT Best Practices
|
||||
|
||||
### Current usage in WLED
|
||||
|
||||
RMT drives NeoPixel LED output (via NeoPixelBus) and IR receiver input. Both use the legacy API that is removed in IDF v5.
|
||||
|
||||
### Migration notes
|
||||
|
||||
- The upstream `V5-C6` branch uses `-D WLED_USE_SHARED_RMT` to switch to the new RMT driver for NeoPixel output.
|
||||
- IR is disabled on IDF v5 until the IR library is ported.
|
||||
- New chips (C6, P4) have different RMT channel counts — use `SOC_RMT_TX_CANDIDATES_PER_GROUP` to check availability.
|
||||
- The new RMT API requires an "encoder" object (`rmt_encoder_t`) to translate data formats — this is more flexible but requires more setup code.
|
||||
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
---
|
||||
|
||||
## Espressif Best Practices (from official examples)
|
||||
|
||||
### Error handling
|
||||
|
||||
Always check `esp_err_t` return values. Use `ESP_ERROR_CHECK()` in initialization code, but handle errors gracefully in runtime code.
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
```cpp
|
||||
// Initialization — crash early on failure
|
||||
ESP_ERROR_CHECK(i2s_driver_install(I2S_NUM_0, &config, 0, nullptr));
|
||||
|
||||
// Runtime — log and recover
|
||||
esp_err_t err = i2s_read(I2S_NUM_0, buf, len, &bytes_read, portMAX_DELAY);
|
||||
if (err != ESP_OK) {
|
||||
DEBUGSR_PRINTF("I2S read failed: %s\n", esp_err_to_name(err));
|
||||
return;
|
||||
}
|
||||
```
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
|
||||
For situations between these two extremes — where you want the `ESP_ERROR_CHECK` formatted log message (file, line, error name) but must not abort — use `ESP_ERROR_CHECK_WITHOUT_ABORT()`.
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
```cpp
|
||||
// Logs in the same format as ESP_ERROR_CHECK, but returns the error code instead of aborting.
|
||||
// Useful for non-fatal driver calls where you want visibility without crashing.
|
||||
esp_err_t err = ESP_ERROR_CHECK_WITHOUT_ABORT(i2s_set_clk(AR_I2S_PORT, rate, bits, ch));
|
||||
if (err != ESP_OK) return; // handle as needed
|
||||
```
|
||||
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
### Logging
|
||||
|
||||
WLED uses its own logging macros — **not** `ESP_LOGx()`. For application-level code, always use the WLED macros defined in `wled.h`:
|
||||
|
||||
| Macro family | Defined in | Controlled by | Use for |
|
||||
|---|---|---|---|
|
||||
| `DEBUG_PRINT` / `DEBUG_PRINTLN` / `DEBUG_PRINTF` | `wled.h` | `WLED_DEBUG` build flag | Development/diagnostic output; compiled out in release builds |
|
||||
|
||||
All of these wrap `Serial` output through the `DEBUGOUT` / `DEBUGOUTLN` / `DEBUGOUTF` macros.
|
||||
|
||||
**Exception — low-level driver code**: When writing code that interacts directly with ESP-IDF APIs (e.g., I2S initialization, RMT setup), use `ESP_LOGx()` macros instead. They support tag-based filtering and compile-time log level control:
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
```cpp
|
||||
static const char* TAG = "my_module";
|
||||
ESP_LOGI(TAG, "Initialized with %d buffers", count);
|
||||
ESP_LOGW(TAG, "PSRAM not available, falling back to DRAM");
|
||||
ESP_LOGE(TAG, "Failed to allocate %u bytes", size);
|
||||
```
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
### Task creation and pinning
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
On dual-core chips (ESP32, S3, P4), pin latency-sensitive tasks to a specific core:
|
||||
|
||||
```cpp
|
||||
xTaskCreatePinnedToCore(
|
||||
audioTask, // function
|
||||
"audio", // name
|
||||
4096, // stack size
|
||||
nullptr, // parameter
|
||||
5, // priority (higher = more important)
|
||||
&audioTaskHandle, // handle
|
||||
0 // core ID (0 = protocol core, 1 = app core)
|
||||
);
|
||||
```
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
|
||||
Guidelines:
|
||||
- Pin network/protocol tasks to core 0 (where Wi-Fi runs).
|
||||
- Pin real-time tasks (audio, LED output) to core 1.
|
||||
- On single-core chips (S2, C3, C5, C6), only core 0 exists — pinning to core 1 will fail. Use `SOC_CPU_CORES_NUM > 1` guards or `tskNO_AFFINITY`.
|
||||
- Use `SOC_CPU_CORES_NUM` to conditionally pin tasks:
|
||||
```cpp
|
||||
#if SOC_CPU_CORES_NUM > 1
|
||||
xTaskCreatePinnedToCore(audioTask, "audio", 4096, nullptr, 5, &handle, 1);
|
||||
#else
|
||||
xTaskCreate(audioTask, "audio", 4096, nullptr, 5, &handle);
|
||||
#endif
|
||||
```
|
||||
|
||||
**Tip: use xTaskCreateUniversal()** - from arduino-esp32 - to avoid the conditional on `SOC_CPU_CORES_NUM`. This function has the same signature as ``xTaskCreatePinnedToCore()``, but automatically falls back to ``xTaskCreate()`` on single-core MCUs.
|
||||
|
||||
### `delay()`, `yield()`, and the IDLE task
|
||||
|
||||
FreeRTOS on ESP32 is **preemptive** — all tasks are scheduled by priority regardless of `yield()` calls. This is fundamentally different from ESP8266 cooperative multitasking.
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
| Call | What it does | Reaches IDLE (priority 0)? |
|
||||
|---|---|---|
|
||||
| `delay(ms)` / `vTaskDelay(ticks)` | Suspends calling task; scheduler runs all other ready tasks | ✅ Yes |
|
||||
| `yield()` / `vTaskDelay(0)` | Hint to switch to tasks at **equal or higher** priority only | ❌ No |
|
||||
| `taskYIELD()` | Same as `vTaskDelay(0)` | ❌ No |
|
||||
| Blocking API (`xQueueReceive`, `ulTaskNotifyTake`, `vTaskDelayUntil`) | Suspends task until event or timeout; IDLE runs freely | ✅ Yes |
|
||||
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
|
||||
**`delay()` in `loopTask` is safe.** Arduino's `loop()` runs inside `loopTask`. Calling `delay()` suspends only `loopTask` — all other FreeRTOS tasks (Wi-Fi stack, audio FFT, LED DMA) continue uninterrupted on either core.
|
||||
|
||||
**`yield()` does not yield to IDLE.** Any task that loops with only `yield()` calls will starve the IDLE task, causing the IDLE watchdog to fire. Always use `delay(1)` (or a blocking FreeRTOS call) in tight task loops. Note: WLED redefines `yield()` as an empty macro on ESP32 WLEDMM_FASTPATH builds.
|
||||
|
||||
#### Why the IDLE task is not optional
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
|
||||
The FreeRTOS IDLE task (one per core on dual-core ESP32 and ESP32-S3; single instance on single-core chips) is not idle in the casual sense — it performs essential system housekeeping:
|
||||
|
||||
- **Frees deleted task memory**: when a task calls `vTaskDelete()`, the IDLE task reclaims its TCB and stack. Without IDLE running, deleted tasks leak memory permanently.
|
||||
- **Runs the idle hook**: when `configUSE_IDLE_HOOK = 1`, the IDLE task calls `vApplicationIdleHook()` on every iteration — some ESP-IDF components register low-priority background work here.
|
||||
- **Implements tickless idle / light sleep**: on battery-powered devices, IDLE is the entry point for low-power sleep. A permanently starved IDLE task disables light sleep entirely.
|
||||
- **Runs registered idle hooks**: ESP-IDF components register callbacks via `esp_register_freertos_idle_hook()` (e.g., Wi-Fi background maintenance, Bluetooth housekeeping). These only fire when IDLE runs.
|
||||
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
|
||||
In short: **starving IDLE corrupts memory cleanup, breaks background activities, disables low-power sleep, and prevents Wi-Fi/BT maintenance.** The IDLE watchdog panic is a symptom — the real damage happens before the watchdog fires.
|
||||
|
||||
### Watchdog management
|
||||
|
||||
Long-running operations may trigger the task watchdog. Feed it explicitly:
|
||||
|
||||
```cpp
|
||||
#include <esp_task_wdt.h>
|
||||
esp_task_wdt_reset(); // feed the watchdog in long loops
|
||||
```
|
||||
|
||||
For tasks that intentionally block for extended periods, consider subscribing/unsubscribing from the TWDT:
|
||||
|
||||
```cpp
|
||||
esp_task_wdt_delete(NULL); // remove current task from TWDT (IDF v4.4)
|
||||
// ... long blocking operation ...
|
||||
esp_task_wdt_add(NULL); // re-register
|
||||
```
|
||||
|
||||
> **IDF v5 note**: In IDF v5, `esp_task_wdt_add()` and `esp_task_wdt_delete()` require an explicit `TaskHandle_t`. Use `xTaskGetCurrentTaskHandle()` instead of `NULL`.
|
||||
|
||||
<!-- HUMAN_ONLY_START -->
|
||||
---
|
||||
|
||||
## Quick Reference: IDF v4 → v5 API Mapping
|
||||
|
||||
| Component | IDF v4 Header | IDF v5 Header | Key Change |
|
||||
|---|---|---|---|
|
||||
| I2S | `driver/i2s.h` | `driver/i2s_std.h` | Channel-based API |
|
||||
| ADC (oneshot) | `driver/adc.h` | `esp_adc/adc_oneshot.h` | Unit/channel handles |
|
||||
| ADC (calibration) | `esp_adc_cal.h` | `esp_adc/adc_cali.h` | Scheme-based calibration |
|
||||
| RMT | `driver/rmt.h` | `driver/rmt_tx.h` / `rmt_rx.h` | Encoder-based transmit |
|
||||
| SPI Flash | `spi_flash.h` | `esp_flash.h` | `esp_flash_*` functions |
|
||||
| GPIO | `driver/gpio.h` | `driver/gpio.h` | `gpio_pad_select_gpio()` removed |
|
||||
| Timer | `driver/timer.h` | `driver/gptimer.h` | General-purpose timer handles |
|
||||
| PCNT | `driver/pcnt.h` | `driver/pulse_cnt.h` | Handle-based API |
|
||||
<!-- HUMAN_ONLY_END -->
|
||||
30
docs/web.instructions.md
Normal file
30
docs/web.instructions.md
Normal file
@@ -0,0 +1,30 @@
|
||||
---
|
||||
applyTo: "wled00/data/**"
|
||||
---
|
||||
# Web UI Coding Conventions
|
||||
|
||||
## Formatting
|
||||
|
||||
- Indent **HTML and JavaScript** with **tabs**
|
||||
- Indent **CSS** with **tabs**
|
||||
|
||||
## JavaScript Style
|
||||
|
||||
- **camelCase** for functions and variables: `gId()`, `selectedFx`, `currentPreset`
|
||||
- Abbreviated helpers are common: `d` for `document`, `gId()` for `getElementById()`
|
||||
|
||||
## Key Files
|
||||
|
||||
- `index.htm` — main interface
|
||||
- `index.js` — functions that manage / update the main interface
|
||||
- `settings*.htm` — configuration pages
|
||||
- `*.css` — stylesheets (inlined during build)
|
||||
- `common.js` — helper functions
|
||||
|
||||
**Reuse shared helpers from `common.js` whenever possible** instead of duplicating utilities in page-local scripts.
|
||||
|
||||
## Build Integration
|
||||
|
||||
Files in this directory are processed by `tools/cdata.js` into generated headers
|
||||
(`wled00/html_*.h`, `wled00/js_*.h`).
|
||||
Run `npm run build` after any change. **Never edit generated headers directly.**
|
||||
5
images/Readme.md
Normal file
5
images/Readme.md
Normal file
@@ -0,0 +1,5 @@
|
||||
### Additional Logos
|
||||
|
||||
Additional awesome logos for WLED can be found here [Aircoookie/Akemi](https://github.com/Aircoookie/Akemi).
|
||||
|
||||
<img src="https://github.com/Aircoookie/Akemi/blob/master/akemi/001_cheerful.png">
|
||||
BIN
images/macbook-pro-space-gray-on-the-wooden-table.jpg
Normal file
BIN
images/macbook-pro-space-gray-on-the-wooden-table.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 489 KiB |
BIN
images/walking-with-iphone-x.jpg
Normal file
BIN
images/walking-with-iphone-x.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 500 KiB |
BIN
images/wled_logo_akemi.png
Normal file
BIN
images/wled_logo_akemi.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
BIN
images/wled_logo_old.png
Normal file
BIN
images/wled_logo_old.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
39
include/README
Normal file
39
include/README
Normal file
@@ -0,0 +1,39 @@
|
||||
|
||||
This directory is intended for project header files.
|
||||
|
||||
A header file is a file containing C declarations and macro definitions
|
||||
to be shared between several project source files. You request the use of a
|
||||
header file in your project source file (C, C++, etc) located in `src` folder
|
||||
by including it, with the C preprocessing directive `#include'.
|
||||
|
||||
```src/main.c
|
||||
|
||||
#include "header.h"
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Including a header file produces the same results as copying the header file
|
||||
into each source file that needs it. Such copying would be time-consuming
|
||||
and error-prone. With a header file, the related declarations appear
|
||||
in only one place. If they need to be changed, they can be changed in one
|
||||
place, and programs that include the header file will automatically use the
|
||||
new version when next recompiled. The header file eliminates the labor of
|
||||
finding and changing all the copies as well as the risk that a failure to
|
||||
find one copy will result in inconsistencies within a program.
|
||||
|
||||
In C, the usual convention is to give header files names that end with `.h'.
|
||||
It is most portable to use only letters, digits, dashes, and underscores in
|
||||
header file names, and at most one dot.
|
||||
|
||||
Read more about using header files in official GCC documentation:
|
||||
|
||||
* Include Syntax
|
||||
* Include Operation
|
||||
* Once-Only Headers
|
||||
* Computed Includes
|
||||
|
||||
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
||||
504
lib/ESP8266PWM/src/core_esp8266_waveform_phase.cpp
Normal file
504
lib/ESP8266PWM/src/core_esp8266_waveform_phase.cpp
Normal file
@@ -0,0 +1,504 @@
|
||||
/* esp8266_waveform imported from platform source code
|
||||
Modified for WLED to work around a fault in the NMI handling,
|
||||
which can result in the system locking up and hard WDT crashes.
|
||||
|
||||
Imported from https://github.com/esp8266/Arduino/blob/7e0d20e2b9034994f573a236364e0aef17fd66de/cores/esp8266/core_esp8266_waveform_phase.cpp
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
esp8266_waveform - General purpose waveform generation and control,
|
||||
supporting outputs on all pins in parallel.
|
||||
|
||||
Copyright (c) 2018 Earle F. Philhower, III. All rights reserved.
|
||||
Copyright (c) 2020 Dirk O. Kaar.
|
||||
|
||||
The core idea is to have a programmable waveform generator with a unique
|
||||
high and low period (defined in microseconds or CPU clock cycles). TIMER1 is
|
||||
set to 1-shot mode and is always loaded with the time until the next edge
|
||||
of any live waveforms.
|
||||
|
||||
Up to one waveform generator per pin supported.
|
||||
|
||||
Each waveform generator is synchronized to the ESP clock cycle counter, not the
|
||||
timer. This allows for removing interrupt jitter and delay as the counter
|
||||
always increments once per 80MHz clock. Changes to a waveform are
|
||||
contiguous and only take effect on the next waveform transition,
|
||||
allowing for smooth transitions.
|
||||
|
||||
This replaces older tone(), analogWrite(), and the Servo classes.
|
||||
|
||||
Everywhere in the code where "ccy" or "ccys" is used, it means ESP.getCycleCount()
|
||||
clock cycle time, or an interval measured in clock cycles, but not TIMER1
|
||||
cycles (which may be 2 CPU clock cycles @ 160MHz).
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "core_esp8266_waveform.h"
|
||||
#include <Arduino.h>
|
||||
#include "debug.h"
|
||||
#include "ets_sys.h"
|
||||
#include <atomic>
|
||||
|
||||
|
||||
// ----- @willmmiles begin patch -----
|
||||
// Linker magic
|
||||
extern "C" void usePWMFixedNMI(void) {};
|
||||
|
||||
// NMI crash workaround
|
||||
// Sometimes the NMI fails to return, stalling the CPU. When this happens,
|
||||
// the next NMI gets a return address /inside the NMI handler function/.
|
||||
// We work around this by caching the last NMI return address, and restoring
|
||||
// the epc3 and eps3 registers to the previous values if the observed epc3
|
||||
// happens to be pointing to the _NMILevelVector function.
|
||||
extern "C" void _NMILevelVector();
|
||||
extern "C" void _UserExceptionVector_1(); // the next function after _NMILevelVector
|
||||
static inline IRAM_ATTR void nmiCrashWorkaround() {
|
||||
static uintptr_t epc3_backup, eps3_backup;
|
||||
|
||||
uintptr_t epc3, eps3;
|
||||
__asm__ __volatile__("rsr %0,epc3; rsr %1,eps3":"=a"(epc3),"=a" (eps3));
|
||||
if ((epc3 < (uintptr_t) &_NMILevelVector) || (epc3 >= (uintptr_t) &_UserExceptionVector_1)) {
|
||||
// Address is good; save backup
|
||||
epc3_backup = epc3;
|
||||
eps3_backup = eps3;
|
||||
} else {
|
||||
// Address is inside the NMI handler -- restore from backup
|
||||
__asm__ __volatile__("wsr %0,epc3; wsr %1,eps3"::"a"(epc3_backup),"a"(eps3_backup));
|
||||
}
|
||||
}
|
||||
// ----- @willmmiles end patch -----
|
||||
|
||||
|
||||
// No-op calls to override the PWM implementation
|
||||
extern "C" void _setPWMFreq_weak(uint32_t freq) { (void) freq; }
|
||||
extern "C" IRAM_ATTR bool _stopPWM_weak(int pin) { (void) pin; return false; }
|
||||
extern "C" bool _setPWM_weak(int pin, uint32_t val, uint32_t range) { (void) pin; (void) val; (void) range; return false; }
|
||||
|
||||
|
||||
// Timer is 80MHz fixed. 160MHz CPU frequency need scaling.
|
||||
constexpr bool ISCPUFREQ160MHZ = clockCyclesPerMicrosecond() == 160;
|
||||
// Maximum delay between IRQs, Timer1, <= 2^23 / 80MHz
|
||||
constexpr int32_t MAXIRQTICKSCCYS = microsecondsToClockCycles(10000);
|
||||
// Maximum servicing time for any single IRQ
|
||||
constexpr uint32_t ISRTIMEOUTCCYS = microsecondsToClockCycles(18);
|
||||
// The latency between in-ISR rearming of the timer and the earliest firing
|
||||
constexpr int32_t IRQLATENCYCCYS = microsecondsToClockCycles(2);
|
||||
// The SDK and hardware take some time to actually get to our NMI code
|
||||
constexpr int32_t DELTAIRQCCYS = ISCPUFREQ160MHZ ?
|
||||
microsecondsToClockCycles(2) >> 1 : microsecondsToClockCycles(2);
|
||||
|
||||
// for INFINITE, the NMI proceeds on the waveform without expiry deadline.
|
||||
// for EXPIRES, the NMI expires the waveform automatically on the expiry ccy.
|
||||
// for UPDATEEXPIRY, the NMI recomputes the exact expiry ccy and transitions to EXPIRES.
|
||||
// for UPDATEPHASE, the NMI recomputes the target timings
|
||||
// for INIT, the NMI initializes nextPeriodCcy, and if expiryCcy != 0 includes UPDATEEXPIRY.
|
||||
enum class WaveformMode : uint8_t {INFINITE = 0, EXPIRES = 1, UPDATEEXPIRY = 2, UPDATEPHASE = 3, INIT = 4};
|
||||
|
||||
// Waveform generator can create tones, PWM, and servos
|
||||
typedef struct {
|
||||
uint32_t nextPeriodCcy; // ESP clock cycle when a period begins.
|
||||
uint32_t endDutyCcy; // ESP clock cycle when going from duty to off
|
||||
int32_t dutyCcys; // Set next off cycle at low->high to maintain phase
|
||||
int32_t adjDutyCcys; // Temporary correction for next period
|
||||
int32_t periodCcys; // Set next phase cycle at low->high to maintain phase
|
||||
uint32_t expiryCcy; // For time-limited waveform, the CPU clock cycle when this waveform must stop. If WaveformMode::UPDATE, temporarily holds relative ccy count
|
||||
WaveformMode mode;
|
||||
bool autoPwm; // perform PWM duty to idle cycle ratio correction under high load at the expense of precise timings
|
||||
} Waveform;
|
||||
|
||||
namespace {
|
||||
|
||||
static struct {
|
||||
Waveform pins[17]; // State of all possible pins
|
||||
uint32_t states = 0; // Is the pin high or low, updated in NMI so no access outside the NMI code
|
||||
uint32_t enabled = 0; // Is it actively running, updated in NMI so no access outside the NMI code
|
||||
|
||||
// Enable lock-free by only allowing updates to waveform.states and waveform.enabled from IRQ service routine
|
||||
int32_t toSetBits = 0; // Message to the NMI handler to start/modify exactly one waveform
|
||||
int32_t toDisableBits = 0; // Message to the NMI handler to disable exactly one pin from waveform generation
|
||||
|
||||
// toSetBits temporaries
|
||||
// cheaper than packing them in every Waveform, since we permit only one use at a time
|
||||
uint32_t phaseCcy; // positive phase offset ccy count
|
||||
int8_t alignPhase; // < 0 no phase alignment, otherwise starts waveform in relative phase offset to given pin
|
||||
|
||||
uint32_t(*timer1CB)() = nullptr;
|
||||
|
||||
bool timer1Running = false;
|
||||
|
||||
uint32_t nextEventCcy;
|
||||
} waveform;
|
||||
|
||||
}
|
||||
|
||||
// Interrupt on/off control
|
||||
static IRAM_ATTR void timer1Interrupt();
|
||||
|
||||
// Non-speed critical bits
|
||||
#pragma GCC optimize ("Os")
|
||||
|
||||
static void initTimer() {
|
||||
timer1_disable();
|
||||
ETS_FRC_TIMER1_INTR_ATTACH(NULL, NULL);
|
||||
ETS_FRC_TIMER1_NMI_INTR_ATTACH(timer1Interrupt);
|
||||
timer1_enable(TIM_DIV1, TIM_EDGE, TIM_SINGLE);
|
||||
waveform.timer1Running = true;
|
||||
timer1_write(IRQLATENCYCCYS); // Cause an interrupt post-haste
|
||||
}
|
||||
|
||||
static void IRAM_ATTR deinitTimer() {
|
||||
ETS_FRC_TIMER1_NMI_INTR_ATTACH(NULL);
|
||||
timer1_disable();
|
||||
timer1_isr_init();
|
||||
waveform.timer1Running = false;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
// Set a callback. Pass in NULL to stop it
|
||||
void setTimer1Callback_weak(uint32_t (*fn)()) {
|
||||
waveform.timer1CB = fn;
|
||||
std::atomic_thread_fence(std::memory_order_acq_rel);
|
||||
if (!waveform.timer1Running && fn) {
|
||||
initTimer();
|
||||
} else if (waveform.timer1Running && !fn && !waveform.enabled) {
|
||||
deinitTimer();
|
||||
}
|
||||
}
|
||||
|
||||
// Start up a waveform on a pin, or change the current one. Will change to the new
|
||||
// waveform smoothly on next low->high transition. For immediate change, stopWaveform()
|
||||
// first, then it will immediately begin.
|
||||
int startWaveformClockCycles_weak(uint8_t pin, uint32_t highCcys, uint32_t lowCcys,
|
||||
uint32_t runTimeCcys, int8_t alignPhase, uint32_t phaseOffsetCcys, bool autoPwm) {
|
||||
uint32_t periodCcys = highCcys + lowCcys;
|
||||
if (periodCcys < MAXIRQTICKSCCYS) {
|
||||
if (!highCcys) {
|
||||
periodCcys = (MAXIRQTICKSCCYS / periodCcys) * periodCcys;
|
||||
}
|
||||
else if (!lowCcys) {
|
||||
highCcys = periodCcys = (MAXIRQTICKSCCYS / periodCcys) * periodCcys;
|
||||
}
|
||||
}
|
||||
// sanity checks, including mixed signed/unsigned arithmetic safety
|
||||
if ((pin > 16) || isFlashInterfacePin(pin) || (alignPhase > 16) ||
|
||||
static_cast<int32_t>(periodCcys) <= 0 ||
|
||||
static_cast<int32_t>(highCcys) < 0 || static_cast<int32_t>(lowCcys) < 0) {
|
||||
return false;
|
||||
}
|
||||
Waveform& wave = waveform.pins[pin];
|
||||
wave.dutyCcys = highCcys;
|
||||
wave.adjDutyCcys = 0;
|
||||
wave.periodCcys = periodCcys;
|
||||
wave.autoPwm = autoPwm;
|
||||
waveform.alignPhase = (alignPhase < 0) ? -1 : alignPhase;
|
||||
waveform.phaseCcy = phaseOffsetCcys;
|
||||
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
const uint32_t pinBit = 1UL << pin;
|
||||
if (!(waveform.enabled & pinBit)) {
|
||||
// wave.nextPeriodCcy and wave.endDutyCcy are initialized by the ISR
|
||||
wave.expiryCcy = runTimeCcys; // in WaveformMode::INIT, temporarily hold relative cycle count
|
||||
wave.mode = WaveformMode::INIT;
|
||||
if (!wave.dutyCcys) {
|
||||
// If initially at zero duty cycle, force GPIO off
|
||||
if (pin == 16) {
|
||||
GP16O = 0;
|
||||
}
|
||||
else {
|
||||
GPOC = pinBit;
|
||||
}
|
||||
}
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
waveform.toSetBits = 1UL << pin;
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
if (!waveform.timer1Running) {
|
||||
initTimer();
|
||||
}
|
||||
else if (T1V > IRQLATENCYCCYS) {
|
||||
// Must not interfere if Timer is due shortly
|
||||
timer1_write(IRQLATENCYCCYS);
|
||||
}
|
||||
}
|
||||
else {
|
||||
wave.mode = WaveformMode::INFINITE; // turn off possible expiry to make update atomic from NMI
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
if (runTimeCcys) {
|
||||
wave.expiryCcy = runTimeCcys; // in WaveformMode::UPDATEEXPIRY, temporarily hold relative cycle count
|
||||
wave.mode = WaveformMode::UPDATEEXPIRY;
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
waveform.toSetBits = 1UL << pin;
|
||||
} else if (alignPhase >= 0) {
|
||||
// @willmmiles new feature
|
||||
wave.mode = WaveformMode::UPDATEPHASE; // recalculate start
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
waveform.toSetBits = 1UL << pin;
|
||||
}
|
||||
}
|
||||
std::atomic_thread_fence(std::memory_order_acq_rel);
|
||||
while (waveform.toSetBits) {
|
||||
esp_yield(); // Wait for waveform to update
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Stops a waveform on a pin
|
||||
IRAM_ATTR int stopWaveform_weak(uint8_t pin) {
|
||||
// Can't possibly need to stop anything if there is no timer active
|
||||
if (!waveform.timer1Running) {
|
||||
return false;
|
||||
}
|
||||
// If user sends in a pin >16 but <32, this will always point to a 0 bit
|
||||
// If they send >=32, then the shift will result in 0 and it will also return false
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
const uint32_t pinBit = 1UL << pin;
|
||||
if (waveform.enabled & pinBit) {
|
||||
waveform.toDisableBits = 1UL << pin;
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
// Must not interfere if Timer is due shortly
|
||||
if (T1V > IRQLATENCYCCYS) {
|
||||
timer1_write(IRQLATENCYCCYS);
|
||||
}
|
||||
while (waveform.toDisableBits) {
|
||||
/* no-op */ // Can't delay() since stopWaveform may be called from an IRQ
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
}
|
||||
}
|
||||
if (!waveform.enabled && !waveform.timer1CB) {
|
||||
deinitTimer();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Speed critical bits
|
||||
#pragma GCC optimize ("O2")
|
||||
|
||||
// For dynamic CPU clock frequency switch in loop the scaling logic would have to be adapted.
|
||||
// Using constexpr makes sure that the CPU clock frequency is compile-time fixed.
|
||||
static inline IRAM_ATTR int32_t scaleCcys(const int32_t ccys, const bool isCPU2X) {
|
||||
if (ISCPUFREQ160MHZ) {
|
||||
return isCPU2X ? ccys : (ccys >> 1);
|
||||
}
|
||||
else {
|
||||
return isCPU2X ? (ccys << 1) : ccys;
|
||||
}
|
||||
}
|
||||
|
||||
static IRAM_ATTR void timer1Interrupt() {
|
||||
const uint32_t isrStartCcy = ESP.getCycleCount();
|
||||
//int32_t clockDrift = isrStartCcy - waveform.nextEventCcy;
|
||||
|
||||
// ----- @willmmiles begin patch -----
|
||||
nmiCrashWorkaround();
|
||||
// ----- @willmmiles end patch -----
|
||||
|
||||
const bool isCPU2X = CPU2X & 1;
|
||||
if ((waveform.toSetBits && !(waveform.enabled & waveform.toSetBits)) || waveform.toDisableBits) {
|
||||
// Handle enable/disable requests from main app.
|
||||
waveform.enabled = (waveform.enabled & ~waveform.toDisableBits) | waveform.toSetBits; // Set the requested waveforms on/off
|
||||
// Find the first GPIO being generated by checking GCC's find-first-set (returns 1 + the bit of the first 1 in an int32_t)
|
||||
waveform.toDisableBits = 0;
|
||||
}
|
||||
|
||||
if (waveform.toSetBits) {
|
||||
const int toSetPin = __builtin_ffs(waveform.toSetBits) - 1;
|
||||
Waveform& wave = waveform.pins[toSetPin];
|
||||
switch (wave.mode) {
|
||||
case WaveformMode::INIT:
|
||||
waveform.states &= ~waveform.toSetBits; // Clear the state of any just started
|
||||
if (waveform.alignPhase >= 0 && waveform.enabled & (1UL << waveform.alignPhase)) {
|
||||
wave.nextPeriodCcy = waveform.pins[waveform.alignPhase].nextPeriodCcy + scaleCcys(waveform.phaseCcy, isCPU2X);
|
||||
}
|
||||
else {
|
||||
wave.nextPeriodCcy = waveform.nextEventCcy;
|
||||
}
|
||||
if (!wave.expiryCcy) {
|
||||
wave.mode = WaveformMode::INFINITE;
|
||||
break;
|
||||
}
|
||||
// fall through
|
||||
case WaveformMode::UPDATEEXPIRY:
|
||||
// in WaveformMode::UPDATEEXPIRY, expiryCcy temporarily holds relative CPU cycle count
|
||||
wave.expiryCcy = wave.nextPeriodCcy + scaleCcys(wave.expiryCcy, isCPU2X);
|
||||
wave.mode = WaveformMode::EXPIRES;
|
||||
break;
|
||||
// @willmmiles new feature
|
||||
case WaveformMode::UPDATEPHASE:
|
||||
// in WaveformMode::UPDATEPHASE, we recalculate the targets
|
||||
if ((waveform.alignPhase >= 0) && (waveform.enabled & (1UL << waveform.alignPhase))) {
|
||||
// Compute phase shift to realign with target
|
||||
auto const newPeriodCcy = waveform.pins[waveform.alignPhase].nextPeriodCcy + scaleCcys(waveform.phaseCcy, isCPU2X);
|
||||
auto const period = scaleCcys(wave.periodCcys, isCPU2X);
|
||||
auto shift = ((static_cast<int32_t> (newPeriodCcy - wave.nextPeriodCcy) + period/2) % period) - (period/2);
|
||||
wave.nextPeriodCcy += static_cast<uint32_t>(shift);
|
||||
if (static_cast<int32_t>(wave.endDutyCcy - wave.nextPeriodCcy) > 0) {
|
||||
wave.endDutyCcy = wave.nextPeriodCcy;
|
||||
}
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
waveform.toSetBits = 0;
|
||||
}
|
||||
|
||||
// Exit the loop if the next event, if any, is sufficiently distant.
|
||||
const uint32_t isrTimeoutCcy = isrStartCcy + ISRTIMEOUTCCYS;
|
||||
uint32_t busyPins = waveform.enabled;
|
||||
waveform.nextEventCcy = isrStartCcy + MAXIRQTICKSCCYS;
|
||||
|
||||
uint32_t now = ESP.getCycleCount();
|
||||
uint32_t isrNextEventCcy = now;
|
||||
while (busyPins) {
|
||||
if (static_cast<int32_t>(isrNextEventCcy - now) > IRQLATENCYCCYS) {
|
||||
waveform.nextEventCcy = isrNextEventCcy;
|
||||
break;
|
||||
}
|
||||
isrNextEventCcy = waveform.nextEventCcy;
|
||||
uint32_t loopPins = busyPins;
|
||||
while (loopPins) {
|
||||
const int pin = __builtin_ffsl(loopPins) - 1;
|
||||
const uint32_t pinBit = 1UL << pin;
|
||||
loopPins ^= pinBit;
|
||||
|
||||
Waveform& wave = waveform.pins[pin];
|
||||
|
||||
/* @willmmiles - wtf? We don't want to accumulate drift
|
||||
if (clockDrift) {
|
||||
wave.endDutyCcy += clockDrift;
|
||||
wave.nextPeriodCcy += clockDrift;
|
||||
wave.expiryCcy += clockDrift;
|
||||
}
|
||||
*/
|
||||
|
||||
uint32_t waveNextEventCcy = (waveform.states & pinBit) ? wave.endDutyCcy : wave.nextPeriodCcy;
|
||||
if (WaveformMode::EXPIRES == wave.mode &&
|
||||
static_cast<int32_t>(waveNextEventCcy - wave.expiryCcy) >= 0 &&
|
||||
static_cast<int32_t>(now - wave.expiryCcy) >= 0) {
|
||||
// Disable any waveforms that are done
|
||||
waveform.enabled ^= pinBit;
|
||||
busyPins ^= pinBit;
|
||||
}
|
||||
else {
|
||||
const int32_t overshootCcys = now - waveNextEventCcy;
|
||||
if (overshootCcys >= 0) {
|
||||
const int32_t periodCcys = scaleCcys(wave.periodCcys, isCPU2X);
|
||||
if (waveform.states & pinBit) {
|
||||
// active configuration and forward are 100% duty
|
||||
if (wave.periodCcys == wave.dutyCcys) {
|
||||
wave.nextPeriodCcy += periodCcys;
|
||||
wave.endDutyCcy = wave.nextPeriodCcy;
|
||||
}
|
||||
else {
|
||||
if (wave.autoPwm) {
|
||||
wave.adjDutyCcys += overshootCcys;
|
||||
}
|
||||
waveform.states ^= pinBit;
|
||||
if (16 == pin) {
|
||||
GP16O = 0;
|
||||
}
|
||||
else {
|
||||
GPOC = pinBit;
|
||||
}
|
||||
}
|
||||
waveNextEventCcy = wave.nextPeriodCcy;
|
||||
}
|
||||
else {
|
||||
wave.nextPeriodCcy += periodCcys;
|
||||
if (!wave.dutyCcys) {
|
||||
wave.endDutyCcy = wave.nextPeriodCcy;
|
||||
}
|
||||
else {
|
||||
int32_t dutyCcys = scaleCcys(wave.dutyCcys, isCPU2X);
|
||||
if (dutyCcys <= wave.adjDutyCcys) {
|
||||
dutyCcys >>= 1;
|
||||
wave.adjDutyCcys -= dutyCcys;
|
||||
}
|
||||
else if (wave.adjDutyCcys) {
|
||||
dutyCcys -= wave.adjDutyCcys;
|
||||
wave.adjDutyCcys = 0;
|
||||
}
|
||||
wave.endDutyCcy = now + dutyCcys;
|
||||
if (static_cast<int32_t>(wave.endDutyCcy - wave.nextPeriodCcy) > 0) {
|
||||
wave.endDutyCcy = wave.nextPeriodCcy;
|
||||
}
|
||||
waveform.states |= pinBit;
|
||||
if (16 == pin) {
|
||||
GP16O = 1;
|
||||
}
|
||||
else {
|
||||
GPOS = pinBit;
|
||||
}
|
||||
}
|
||||
waveNextEventCcy = wave.endDutyCcy;
|
||||
}
|
||||
|
||||
if (WaveformMode::EXPIRES == wave.mode && static_cast<int32_t>(waveNextEventCcy - wave.expiryCcy) > 0) {
|
||||
waveNextEventCcy = wave.expiryCcy;
|
||||
}
|
||||
}
|
||||
|
||||
if (static_cast<int32_t>(waveNextEventCcy - isrTimeoutCcy) >= 0) {
|
||||
busyPins ^= pinBit;
|
||||
if (static_cast<int32_t>(waveform.nextEventCcy - waveNextEventCcy) > 0) {
|
||||
waveform.nextEventCcy = waveNextEventCcy;
|
||||
}
|
||||
}
|
||||
else if (static_cast<int32_t>(isrNextEventCcy - waveNextEventCcy) > 0) {
|
||||
isrNextEventCcy = waveNextEventCcy;
|
||||
}
|
||||
}
|
||||
now = ESP.getCycleCount();
|
||||
}
|
||||
//clockDrift = 0;
|
||||
}
|
||||
|
||||
int32_t callbackCcys = 0;
|
||||
if (waveform.timer1CB) {
|
||||
callbackCcys = scaleCcys(waveform.timer1CB(), isCPU2X);
|
||||
}
|
||||
now = ESP.getCycleCount();
|
||||
int32_t nextEventCcys = waveform.nextEventCcy - now;
|
||||
// Account for unknown duration of timer1CB().
|
||||
if (waveform.timer1CB && nextEventCcys > callbackCcys) {
|
||||
waveform.nextEventCcy = now + callbackCcys;
|
||||
nextEventCcys = callbackCcys;
|
||||
}
|
||||
|
||||
// Timer is 80MHz fixed. 160MHz CPU frequency need scaling.
|
||||
int32_t deltaIrqCcys = DELTAIRQCCYS;
|
||||
int32_t irqLatencyCcys = IRQLATENCYCCYS;
|
||||
if (isCPU2X) {
|
||||
nextEventCcys >>= 1;
|
||||
deltaIrqCcys >>= 1;
|
||||
irqLatencyCcys >>= 1;
|
||||
}
|
||||
|
||||
// Firing timer too soon, the NMI occurs before ISR has returned.
|
||||
if (nextEventCcys < irqLatencyCcys + deltaIrqCcys) {
|
||||
waveform.nextEventCcy = now + IRQLATENCYCCYS + DELTAIRQCCYS;
|
||||
nextEventCcys = irqLatencyCcys;
|
||||
}
|
||||
else {
|
||||
nextEventCcys -= deltaIrqCcys;
|
||||
}
|
||||
|
||||
// Register access is fast and edge IRQ was configured before.
|
||||
T1L = nextEventCcys;
|
||||
}
|
||||
469
lib/NeoESP32RmtHI/include/NeoEsp32RmtHIMethod.h
Normal file
469
lib/NeoESP32RmtHI/include/NeoEsp32RmtHIMethod.h
Normal file
@@ -0,0 +1,469 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
NeoPixel driver for ESP32 RMTs using High-priority Interrupt
|
||||
|
||||
(NB. This cannot be mixed with the non-HI driver.)
|
||||
|
||||
Written by Will M. Miles.
|
||||
|
||||
I invest time and resources providing this open source code,
|
||||
please support me by donating (see https://github.com/Makuna/NeoPixelBus)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
This file is part of the Makuna/NeoPixelBus library.
|
||||
|
||||
NeoPixelBus is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixelBus is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
|
||||
// Use the NeoEspRmtSpeed types from the driver-based implementation
|
||||
#include <NeoPixelBus.h>
|
||||
|
||||
|
||||
namespace NeoEsp32RmtHiMethodDriver {
|
||||
// Install the driver for a specific channel, specifying timing properties
|
||||
esp_err_t Install(rmt_channel_t channel, uint32_t rmtBit0, uint32_t rmtBit1, uint32_t resetDuration);
|
||||
|
||||
// Remove the driver on a specific channel
|
||||
esp_err_t Uninstall(rmt_channel_t channel);
|
||||
|
||||
// Write a buffer of data to a specific channel.
|
||||
// Buffer reference is held until write completes.
|
||||
esp_err_t Write(rmt_channel_t channel, const uint8_t *src, size_t src_size);
|
||||
|
||||
// Wait until transaction is complete.
|
||||
esp_err_t WaitForTxDone(rmt_channel_t channel, TickType_t wait_time);
|
||||
};
|
||||
|
||||
template<typename T_SPEED, typename T_CHANNEL> class NeoEsp32RmtHIMethodBase
|
||||
{
|
||||
public:
|
||||
typedef NeoNoSettings SettingsObject;
|
||||
|
||||
NeoEsp32RmtHIMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
_sizeData(pixelCount * elementSize + settingsSize),
|
||||
_pin(pin)
|
||||
{
|
||||
construct();
|
||||
}
|
||||
|
||||
NeoEsp32RmtHIMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize, NeoBusChannel channel) :
|
||||
_sizeData(pixelCount* elementSize + settingsSize),
|
||||
_pin(pin),
|
||||
_channel(channel)
|
||||
{
|
||||
construct();
|
||||
}
|
||||
|
||||
~NeoEsp32RmtHIMethodBase()
|
||||
{
|
||||
// wait until the last send finishes before destructing everything
|
||||
// arbitrary time out of 10 seconds
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(NeoEsp32RmtHiMethodDriver::WaitForTxDone(_channel.RmtChannelNumber, 10000 / portTICK_PERIOD_MS));
|
||||
|
||||
ESP_ERROR_CHECK(NeoEsp32RmtHiMethodDriver::Uninstall(_channel.RmtChannelNumber));
|
||||
|
||||
gpio_matrix_out(_pin, SIG_GPIO_OUT_IDX, false, false);
|
||||
pinMode(_pin, INPUT);
|
||||
|
||||
free(_dataEditing);
|
||||
free(_dataSending);
|
||||
}
|
||||
|
||||
bool IsReadyToUpdate() const
|
||||
{
|
||||
return (ESP_OK == ESP_ERROR_CHECK_WITHOUT_ABORT_SILENT_TIMEOUT(NeoEsp32RmtHiMethodDriver::WaitForTxDone(_channel.RmtChannelNumber, 0)));
|
||||
}
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
rmt_config_t config = {};
|
||||
|
||||
config.rmt_mode = RMT_MODE_TX;
|
||||
config.channel = _channel.RmtChannelNumber;
|
||||
config.gpio_num = static_cast<gpio_num_t>(_pin);
|
||||
config.mem_block_num = 1;
|
||||
config.tx_config.loop_en = false;
|
||||
|
||||
config.tx_config.idle_output_en = true;
|
||||
config.tx_config.idle_level = T_SPEED::IdleLevel;
|
||||
|
||||
config.tx_config.carrier_en = false;
|
||||
config.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW;
|
||||
|
||||
config.clk_div = T_SPEED::RmtClockDivider;
|
||||
|
||||
ESP_ERROR_CHECK(rmt_config(&config)); // Uses ESP library
|
||||
ESP_ERROR_CHECK(NeoEsp32RmtHiMethodDriver::Install(_channel.RmtChannelNumber, T_SPEED::RmtBit0, T_SPEED::RmtBit1, T_SPEED::RmtDurationReset));
|
||||
}
|
||||
|
||||
void Update(bool maintainBufferConsistency)
|
||||
{
|
||||
// wait for not actively sending data
|
||||
// this will time out at 10 seconds, an arbitrarily long period of time
|
||||
// and do nothing if this happens
|
||||
if (ESP_OK == ESP_ERROR_CHECK_WITHOUT_ABORT(NeoEsp32RmtHiMethodDriver::WaitForTxDone(_channel.RmtChannelNumber, 10000 / portTICK_PERIOD_MS)))
|
||||
{
|
||||
// now start the RMT transmit with the editing buffer before we swap
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(NeoEsp32RmtHiMethodDriver::Write(_channel.RmtChannelNumber, _dataEditing, _sizeData));
|
||||
|
||||
if (maintainBufferConsistency)
|
||||
{
|
||||
// copy editing to sending,
|
||||
// this maintains the contract that "colors present before will
|
||||
// be the same after", otherwise GetPixelColor will be inconsistent
|
||||
memcpy(_dataSending, _dataEditing, _sizeData);
|
||||
}
|
||||
|
||||
// swap so the user can modify without affecting the async operation
|
||||
std::swap(_dataSending, _dataEditing);
|
||||
}
|
||||
}
|
||||
|
||||
bool AlwaysUpdate()
|
||||
{
|
||||
// this method requires update to be called only if changes to buffer
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SwapBuffers()
|
||||
{
|
||||
std::swap(_dataSending, _dataEditing);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t* getData() const
|
||||
{
|
||||
return _dataEditing;
|
||||
};
|
||||
|
||||
size_t getDataSize() const
|
||||
{
|
||||
return _sizeData;
|
||||
}
|
||||
|
||||
void applySettings([[maybe_unused]] const SettingsObject& settings)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const size_t _sizeData; // Size of '_data*' buffers
|
||||
const uint8_t _pin; // output pin number
|
||||
const T_CHANNEL _channel; // holds instance for multi channel support
|
||||
|
||||
// Holds data stream which include LED color values and other settings as needed
|
||||
uint8_t* _dataEditing; // exposed for get and set
|
||||
uint8_t* _dataSending; // used for async send using RMT
|
||||
|
||||
|
||||
void construct()
|
||||
{
|
||||
_dataEditing = static_cast<uint8_t*>(malloc(_sizeData));
|
||||
// data cleared later in Begin()
|
||||
|
||||
_dataSending = static_cast<uint8_t*>(malloc(_sizeData));
|
||||
// no need to initialize it, it gets overwritten on every send
|
||||
}
|
||||
};
|
||||
|
||||
// normal
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannelN> NeoEsp32RmtHINWs2811Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannelN> NeoEsp32RmtHINWs2812xMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannelN> NeoEsp32RmtHINWs2816Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2805, NeoEsp32RmtChannelN> NeoEsp32RmtHINWs2805Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannelN> NeoEsp32RmtHINSk6812Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannelN> NeoEsp32RmtHINTm1814Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannelN> NeoEsp32RmtHINTm1829Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannelN> NeoEsp32RmtHINTm1914Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannelN> NeoEsp32RmtHINApa106Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannelN> NeoEsp32RmtHINTx1812Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedGs1903, NeoEsp32RmtChannelN> NeoEsp32RmtHINGs1903Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannelN> NeoEsp32RmtHIN800KbpsMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannelN> NeoEsp32RmtHIN400KbpsMethod;
|
||||
typedef NeoEsp32RmtHINWs2805Method NeoEsp32RmtHINWs2814Method;
|
||||
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Ws2811Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Ws2812xMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Ws2816Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2805, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Ws2805Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Sk6812Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Tm1814Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Tm1829Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Tm1914Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Apa106Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Tx1812Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedGs1903, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Gs1903Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel0> NeoEsp32RmtHI0800KbpsMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel0> NeoEsp32RmtHI0400KbpsMethod;
|
||||
typedef NeoEsp32RmtHI0Ws2805Method NeoEsp32RmtHI0Ws2814Method;
|
||||
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Ws2811Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Ws2812xMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Ws2816Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2805, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Ws2805Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Sk6812Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Tm1814Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Tm1829Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Tm1914Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Apa106Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Tx1812Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedGs1903, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Gs1903Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel1> NeoEsp32RmtHI1800KbpsMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel1> NeoEsp32RmtHI1400KbpsMethod;
|
||||
typedef NeoEsp32RmtHI1Ws2805Method NeoEsp32RmtHI1Ws2814Method;
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Ws2811Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Ws2812xMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Ws2816Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2805, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Ws2805Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Sk6812Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Tm1814Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Tm1829Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Tm1914Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Apa106Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Tx1812Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedGs1903, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Gs1903Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel2> NeoEsp32RmtHI2800KbpsMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel2> NeoEsp32RmtHI2400KbpsMethod;
|
||||
typedef NeoEsp32RmtHI2Ws2805Method NeoEsp32RmtHI2Ws2814Method;
|
||||
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Ws2811Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Ws2812xMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Ws2816Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2805, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Ws2805Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Sk6812Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Tm1814Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Tm1829Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Tm1914Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Apa106Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Tx1812Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedGs1903, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Gs1903Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel3> NeoEsp32RmtHI3800KbpsMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel3> NeoEsp32RmtHI3400KbpsMethod;
|
||||
typedef NeoEsp32RmtHI3Ws2805Method NeoEsp32RmtHI3Ws2814Method;
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Ws2811Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Ws2812xMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Ws2816Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2805, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Ws2805Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Sk6812Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Tm1814Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Tm1829Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Tm1914Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Apa106Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Tx1812Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedGs1903, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Gs1903Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel4> NeoEsp32RmtHI4800KbpsMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel4> NeoEsp32RmtHI4400KbpsMethod;
|
||||
typedef NeoEsp32RmtHI4Ws2805Method NeoEsp32RmtHI4Ws2814Method;
|
||||
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Ws2811Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Ws2812xMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Ws2816Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2805, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Ws2805Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Sk6812Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Tm1814Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Tm1829Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Tm1914Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Apa106Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Tx1812Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedGs1903, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Gs1903Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel5> NeoEsp32RmtHI5800KbpsMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel5> NeoEsp32RmtHI5400KbpsMethod;
|
||||
typedef NeoEsp32RmtHI5Ws2805Method NeoEsp32RmtHI5Ws2814Method;
|
||||
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Ws2811Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Ws2812xMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Ws2816Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2805, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Ws2805Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Sk6812Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Tm1814Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Tm1829Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Tm1914Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Apa106Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Tx1812Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedGs1903, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Gs1903Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel6> NeoEsp32RmtHI6800KbpsMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel6> NeoEsp32RmtHI6400KbpsMethod;
|
||||
typedef NeoEsp32RmtHI6Ws2805Method NeoEsp32RmtHI6Ws2814Method;
|
||||
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Ws2811Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Ws2812xMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Ws2816Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2805, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Ws2805Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Sk6812Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Tm1814Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Tm1829Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Tm1914Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Apa106Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Tx1812Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedGs1903, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Gs1903Method;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel7> NeoEsp32RmtHI7800KbpsMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel7> NeoEsp32RmtHI7400KbpsMethod;
|
||||
typedef NeoEsp32RmtHI7Ws2805Method NeoEsp32RmtHI7Ws2814Method;
|
||||
|
||||
#endif // !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
#endif // !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
|
||||
// inverted
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannelN> NeoEsp32RmtHINWs2811InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannelN> NeoEsp32RmtHINWs2812xInvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannelN> NeoEsp32RmtHINWs2816InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2805, NeoEsp32RmtChannelN> NeoEsp32RmtHINWs2805InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannelN> NeoEsp32RmtHINSk6812InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannelN> NeoEsp32RmtHINTm1814InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannelN> NeoEsp32RmtHINTm1829InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannelN> NeoEsp32RmtHINTm1914InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannelN> NeoEsp32RmtHINApa106InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannelN> NeoEsp32RmtHINTx1812InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedGs1903, NeoEsp32RmtChannelN> NeoEsp32RmtHINGs1903InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannelN> NeoEsp32RmtHIN800KbpsInvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannelN> NeoEsp32RmtHIN400KbpsInvertedMethod;
|
||||
typedef NeoEsp32RmtHINWs2805InvertedMethod NeoEsp32RmtHINWs2814InvertedMethod;
|
||||
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Ws2811InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Ws2812xInvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Ws2816InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2805, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Ws2805InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Sk6812InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Tm1814InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Tm1829InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Tm1914InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Apa106InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Tx1812InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedGs1903, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Gs1903InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel0> NeoEsp32RmtHI0800KbpsInvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel0> NeoEsp32RmtHI0400KbpsInvertedMethod;
|
||||
typedef NeoEsp32RmtHI0Ws2805InvertedMethod NeoEsp32RmtHI0Ws2814InvertedMethod;
|
||||
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Ws2811InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Ws2812xInvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Ws2816InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2805, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Ws2805InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Sk6812InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Tm1814InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Tm1829InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Tm1914InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Apa106InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Tx1812InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedGs1903, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Gs1903InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel1> NeoEsp32RmtHI1800KbpsInvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel1> NeoEsp32RmtHI1400KbpsInvertedMethod;
|
||||
typedef NeoEsp32RmtHI1Ws2805InvertedMethod NeoEsp32RmtHI1Ws2814InvertedMethod;
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Ws2811InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Ws2812xInvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Ws2816InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2805, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Ws2805InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Sk6812InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Tm1814InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Tm1829InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Tm1914InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Apa106InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Tx1812InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedGs1903, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Gs1903InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel2> NeoEsp32RmtHI2800KbpsInvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel2> NeoEsp32RmtHI2400KbpsInvertedMethod;
|
||||
typedef NeoEsp32RmtHI2Ws2805InvertedMethod NeoEsp32RmtHI2Ws2814InvertedMethod;
|
||||
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Ws2811InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Ws2812xInvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2805, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Ws2805InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Ws2816InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Sk6812InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Tm1814InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Tm1829InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Tm1914InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Apa106InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Tx1812InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedGs1903, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Gs1903InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel3> NeoEsp32RmtHI3800KbpsInvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel3> NeoEsp32RmtHI3400KbpsInvertedMethod;
|
||||
typedef NeoEsp32RmtHI3Ws2805InvertedMethod NeoEsp32RmtHI3Ws2814InvertedMethod;
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Ws2811InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Ws2812xInvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Ws2816InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2805, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Ws2805InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Sk6812InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Tm1814InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Tm1829InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Tm1914InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Apa106InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Tx1812InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedGs1903, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Gs1903InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel4> NeoEsp32RmtHI4800KbpsInvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel4> NeoEsp32RmtHI4400KbpsInvertedMethod;
|
||||
typedef NeoEsp32RmtHI4Ws2805InvertedMethod NeoEsp32RmtHI4Ws2814InvertedMethod;
|
||||
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Ws2811InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Ws2812xInvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Ws2816InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2805, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Ws2805InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Sk6812InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Tm1814InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Tm1829InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Tm1914InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Apa106InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Tx1812InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedGs1903, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Gs1903InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel5> NeoEsp32RmtHI5800KbpsInvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel5> NeoEsp32RmtHI5400KbpsInvertedMethod;
|
||||
typedef NeoEsp32RmtHI5Ws2805InvertedMethod NeoEsp32RmtHI5Ws2814InvertedMethod;
|
||||
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Ws2811InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Ws2812xInvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Ws2816InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2805, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Ws2805InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Sk6812InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Tm1814InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Tm1829InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Tm1914InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Apa106InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Tx1812InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedGs1903, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Gs1903InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel6> NeoEsp32RmtHI6800KbpsInvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel6> NeoEsp32RmtHI6400KbpsInvertedMethod;
|
||||
typedef NeoEsp32RmtHI6Ws2805InvertedMethod NeoEsp32RmtHI6Ws2814InvertedMethod;
|
||||
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Ws2811InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Ws2812xInvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Ws2816InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2805, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Ws2805InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Sk6812InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Tm1814InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Tm1829InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Tm1914InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Apa106InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Tx1812InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedGs1903, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Gs1903InvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel7> NeoEsp32RmtHI7800KbpsInvertedMethod;
|
||||
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel7> NeoEsp32RmtHI7400KbpsInvertedMethod;
|
||||
typedef NeoEsp32RmtHI7Ws2805InvertedMethod NeoEsp32RmtHI7Ws2814InvertedMethod;
|
||||
|
||||
#endif // !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
#endif // !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
|
||||
#endif
|
||||
12
lib/NeoESP32RmtHI/library.json
Normal file
12
lib/NeoESP32RmtHI/library.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "NeoESP32RmtHI",
|
||||
"build": { "libArchive": false },
|
||||
"platforms": ["espressif32"],
|
||||
"dependencies": [
|
||||
{
|
||||
"owner": "makuna",
|
||||
"name": "NeoPixelBus",
|
||||
"version": "^2.8.3"
|
||||
}
|
||||
]
|
||||
}
|
||||
263
lib/NeoESP32RmtHI/src/NeoEsp32RmtHI.S
Normal file
263
lib/NeoESP32RmtHI/src/NeoEsp32RmtHI.S
Normal file
@@ -0,0 +1,263 @@
|
||||
/* RMT ISR shim
|
||||
* Bridges from a high-level interrupt to the C++ code.
|
||||
*
|
||||
* This code is largely derived from Espressif's 'hli_vector.S' Bluetooth ISR.
|
||||
*
|
||||
*/
|
||||
|
||||
#if defined(__XTENSA__) && defined(ESP32) && !defined(CONFIG_BTDM_CTRL_HLI)
|
||||
|
||||
#include <freertos/xtensa_context.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "soc/soc.h"
|
||||
|
||||
/* If the Bluetooth driver has hooked the high-priority interrupt, we piggyback on it and don't need this. */
|
||||
#ifndef CONFIG_BTDM_CTRL_HLI
|
||||
|
||||
/*
|
||||
Select interrupt based on system check level
|
||||
- Base ESP32: could be 4 or 5, depends on platform config
|
||||
- S2: 5
|
||||
- S3: 5
|
||||
*/
|
||||
|
||||
#if CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5
|
||||
/* Use level 4 */
|
||||
#define RFI_X 4
|
||||
#define xt_highintx xt_highint4
|
||||
#else /* !CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */
|
||||
/* Use level 5 */
|
||||
#define RFI_X 5
|
||||
#define xt_highintx xt_highint5
|
||||
#endif /* CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */
|
||||
|
||||
// Register map, based on interrupt level
|
||||
#define EPC_X (EPC + RFI_X)
|
||||
#define EXCSAVE_X (EXCSAVE + RFI_X)
|
||||
|
||||
// The sp mnemonic is used all over in ESP's assembly, though I'm not sure where it's expected to be defined?
|
||||
#define sp a1
|
||||
|
||||
/* Interrupt stack size, for C code. */
|
||||
#define RMT_INTR_STACK_SIZE 512
|
||||
|
||||
/* Save area for the CPU state:
|
||||
* - 64 words for the general purpose registers
|
||||
* - 7 words for some of the special registers:
|
||||
* - WINDOWBASE, WINDOWSTART — only WINDOWSTART is truly needed
|
||||
* - SAR, LBEG, LEND, LCOUNT — since the C code might use these
|
||||
* - EPC1 — since the C code might cause window overflow exceptions
|
||||
* This is not laid out as standard exception frame structure
|
||||
* for simplicity of the save/restore code.
|
||||
*/
|
||||
#define REG_FILE_SIZE (64 * 4)
|
||||
#define SPECREG_OFFSET REG_FILE_SIZE
|
||||
#define SPECREG_SIZE (7 * 4)
|
||||
#define REG_SAVE_AREA_SIZE (SPECREG_OFFSET + SPECREG_SIZE)
|
||||
|
||||
.data
|
||||
_rmt_intr_stack:
|
||||
.space RMT_INTR_STACK_SIZE
|
||||
_rmt_save_ctx:
|
||||
.space REG_SAVE_AREA_SIZE
|
||||
|
||||
.section .iram1,"ax"
|
||||
.global xt_highintx
|
||||
.type xt_highintx,@function
|
||||
.align 4
|
||||
|
||||
xt_highintx:
|
||||
|
||||
movi a0, _rmt_save_ctx
|
||||
/* save 4 lower registers */
|
||||
s32i a1, a0, 4
|
||||
s32i a2, a0, 8
|
||||
s32i a3, a0, 12
|
||||
rsr a2, EXCSAVE_X /* holds the value of a0 */
|
||||
s32i a2, a0, 0
|
||||
|
||||
/* Save special registers */
|
||||
addi a0, a0, SPECREG_OFFSET
|
||||
rsr a2, WINDOWBASE
|
||||
s32i a2, a0, 0
|
||||
rsr a2, WINDOWSTART
|
||||
s32i a2, a0, 4
|
||||
rsr a2, SAR
|
||||
s32i a2, a0, 8
|
||||
#if XCHAL_HAVE_LOOPS
|
||||
rsr a2, LBEG
|
||||
s32i a2, a0, 12
|
||||
rsr a2, LEND
|
||||
s32i a2, a0, 16
|
||||
rsr a2, LCOUNT
|
||||
s32i a2, a0, 20
|
||||
#endif
|
||||
rsr a2, EPC1
|
||||
s32i a2, a0, 24
|
||||
|
||||
/* disable exception mode, window overflow */
|
||||
movi a0, PS_INTLEVEL(RFI_X+1) | PS_EXCM
|
||||
wsr a0, PS
|
||||
rsync
|
||||
|
||||
/* Save the remaining physical registers.
|
||||
* 4 registers are already saved, which leaves 60 registers to save.
|
||||
* (FIXME: consider the case when the CPU is configured with physical 32 registers)
|
||||
* These 60 registers are saved in 5 iterations, 12 registers at a time.
|
||||
*/
|
||||
movi a1, 5
|
||||
movi a3, _rmt_save_ctx + 4 * 4
|
||||
|
||||
/* This is repeated 5 times, each time the window is shifted by 12 registers.
|
||||
* We come here with a1 = downcounter, a3 = save pointer, a2 and a0 unused.
|
||||
*/
|
||||
1:
|
||||
s32i a4, a3, 0
|
||||
s32i a5, a3, 4
|
||||
s32i a6, a3, 8
|
||||
s32i a7, a3, 12
|
||||
s32i a8, a3, 16
|
||||
s32i a9, a3, 20
|
||||
s32i a10, a3, 24
|
||||
s32i a11, a3, 28
|
||||
s32i a12, a3, 32
|
||||
s32i a13, a3, 36
|
||||
s32i a14, a3, 40
|
||||
s32i a15, a3, 44
|
||||
|
||||
/* We are about to rotate the window, so that a12-a15 will become the new a0-a3.
|
||||
* Copy a0-a3 to a12-15 to still have access to these values.
|
||||
* At the same time we can decrement the counter and adjust the save area pointer
|
||||
*/
|
||||
|
||||
/* a0 is constant (_rmt_save_ctx), no need to copy */
|
||||
addi a13, a1, -1 /* copy and decrement the downcounter */
|
||||
/* a2 is scratch so no need to copy */
|
||||
addi a15, a3, 48 /* copy and adjust the save area pointer */
|
||||
beqz a13, 2f /* have saved all registers ? */
|
||||
rotw 3 /* rotate the window and go back */
|
||||
j 1b
|
||||
|
||||
/* the loop is complete */
|
||||
2:
|
||||
rotw 4 /* this brings us back to the original window */
|
||||
/* a0 still points to _rmt_save_ctx */
|
||||
|
||||
/* Can clear WINDOWSTART now, all registers are saved */
|
||||
rsr a2, WINDOWBASE
|
||||
/* WINDOWSTART = (1 << WINDOWBASE) */
|
||||
movi a3, 1
|
||||
ssl a2
|
||||
sll a3, a3
|
||||
wsr a3, WINDOWSTART
|
||||
|
||||
_highint_stack_switch:
|
||||
movi a0, 0
|
||||
movi sp, _rmt_intr_stack + RMT_INTR_STACK_SIZE - 16
|
||||
s32e a0, sp, -12 /* For GDB: set null SP */
|
||||
s32e a0, sp, -16 /* For GDB: set null PC */
|
||||
movi a0, _highint_stack_switch /* For GDB: cosmetics, for the frame where stack switch happened */
|
||||
|
||||
/* Set up PS for C, disable all interrupts except NMI and debug, and clear EXCM. */
|
||||
movi a6, PS_INTLEVEL(RFI_X) | PS_UM | PS_WOE
|
||||
wsr a6, PS
|
||||
rsync
|
||||
|
||||
/* Call C handler */
|
||||
mov a6, sp
|
||||
call4 NeoEsp32RmtMethodIsr
|
||||
|
||||
l32e sp, sp, -12 /* switch back to the original stack */
|
||||
|
||||
/* Done with C handler; re-enable exception mode, disabling window overflow */
|
||||
movi a2, PS_INTLEVEL(RFI_X+1) | PS_EXCM /* TOCHECK */
|
||||
wsr a2, PS
|
||||
rsync
|
||||
|
||||
/* Restore the special registers.
|
||||
* WINDOWSTART will be restored near the end.
|
||||
*/
|
||||
movi a0, _rmt_save_ctx + SPECREG_OFFSET
|
||||
l32i a2, a0, 8
|
||||
wsr a2, SAR
|
||||
#if XCHAL_HAVE_LOOPS
|
||||
l32i a2, a0, 12
|
||||
wsr a2, LBEG
|
||||
l32i a2, a0, 16
|
||||
wsr a2, LEND
|
||||
l32i a2, a0, 20
|
||||
wsr a2, LCOUNT
|
||||
#endif
|
||||
l32i a2, a0, 24
|
||||
wsr a2, EPC1
|
||||
|
||||
/* Restoring the physical registers.
|
||||
* This is the reverse to the saving process above.
|
||||
*/
|
||||
|
||||
/* Rotate back to the final window, then start loading 12 registers at a time,
|
||||
* in 5 iterations.
|
||||
* Again, a1 is the downcounter and a3 is the save area pointer.
|
||||
* After each rotation, a1 and a3 are copied from a13 and a15.
|
||||
* To simplify the loop, we put the initial values into a13 and a15.
|
||||
*/
|
||||
rotw -4
|
||||
movi a15, _rmt_save_ctx + 64 * 4 /* point to the end of the save area */
|
||||
movi a13, 5
|
||||
|
||||
1:
|
||||
/* Copy a1 and a3 from their previous location,
|
||||
* at the same time decrementing and adjusting the save area pointer.
|
||||
*/
|
||||
addi a1, a13, -1
|
||||
addi a3, a15, -48
|
||||
|
||||
/* Load 12 registers */
|
||||
l32i a4, a3, 0
|
||||
l32i a5, a3, 4
|
||||
l32i a6, a3, 8
|
||||
l32i a7, a3, 12
|
||||
l32i a8, a3, 16
|
||||
l32i a9, a3, 20
|
||||
l32i a10, a3, 24
|
||||
l32i a11, a3, 28 /* ensure PS and EPC written */
|
||||
l32i a12, a3, 32
|
||||
l32i a13, a3, 36
|
||||
l32i a14, a3, 40
|
||||
l32i a15, a3, 44
|
||||
|
||||
/* Done with the loop? */
|
||||
beqz a1, 2f
|
||||
/* If no, rotate the window and repeat */
|
||||
rotw -3
|
||||
j 1b
|
||||
|
||||
2:
|
||||
/* Done with the loop. Only 4 registers (a0-a3 in the original window) remain
|
||||
* to be restored. Also need to restore WINDOWSTART, since all the general
|
||||
* registers are now in place.
|
||||
*/
|
||||
movi a0, _rmt_save_ctx
|
||||
|
||||
l32i a2, a0, SPECREG_OFFSET + 4
|
||||
wsr a2, WINDOWSTART
|
||||
|
||||
l32i a1, a0, 4
|
||||
l32i a2, a0, 8
|
||||
l32i a3, a0, 12
|
||||
rsr a0, EXCSAVE_X /* holds the value of a0 before the interrupt handler */
|
||||
|
||||
/* Return from the interrupt, restoring PS from EPS_X */
|
||||
rfi RFI_X
|
||||
|
||||
|
||||
/* The linker has no reason to link in this file; all symbols it exports are already defined
|
||||
(weakly!) in the default int handler. Define a symbol here so we can use it to have the
|
||||
linker inspect this anyway. */
|
||||
|
||||
.global ld_include_hli_vectors_rmt
|
||||
ld_include_hli_vectors_rmt:
|
||||
|
||||
|
||||
#endif // CONFIG_BTDM_CTRL_HLI
|
||||
#endif // XTensa
|
||||
507
lib/NeoESP32RmtHI/src/NeoEsp32RmtHIMethod.cpp
Normal file
507
lib/NeoESP32RmtHI/src/NeoEsp32RmtHIMethod.cpp
Normal file
@@ -0,0 +1,507 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
NeoPixel library helper functions for Esp32.
|
||||
|
||||
A BIG thanks to Andreas Merkle for the investigation and implementation of
|
||||
a workaround to the GCC bug that drops method attributes from template methods
|
||||
|
||||
Written by Michael C. Miller.
|
||||
|
||||
I invest time and resources providing this open source code,
|
||||
please support me by donating (see https://github.com/Makuna/NeoPixelBus)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
This file is part of the Makuna/NeoPixelBus library.
|
||||
|
||||
NeoPixelBus is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixelBus is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
|
||||
#include <algorithm>
|
||||
#include "esp_idf_version.h"
|
||||
#include "NeoEsp32RmtHIMethod.h"
|
||||
#include "soc/soc.h"
|
||||
#include "soc/rmt_reg.h"
|
||||
|
||||
#ifdef __riscv
|
||||
#include "riscv/interrupt.h"
|
||||
#endif
|
||||
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0)
|
||||
#include "hal/rmt_ll.h"
|
||||
#else
|
||||
/* Shims for older ESP-IDF v3; we can safely assume original ESP32 */
|
||||
#include "soc/rmt_struct.h"
|
||||
|
||||
// Selected RMT API functions borrowed from ESP-IDF v4.4.8
|
||||
// components/hal/esp32/include/hal/rmt_ll.h
|
||||
// Copyright 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.
|
||||
|
||||
__attribute__((always_inline))
|
||||
static inline void rmt_ll_tx_reset_pointer(rmt_dev_t *dev, uint32_t channel)
|
||||
{
|
||||
dev->conf_ch[channel].conf1.mem_rd_rst = 1;
|
||||
dev->conf_ch[channel].conf1.mem_rd_rst = 0;
|
||||
}
|
||||
|
||||
__attribute__((always_inline))
|
||||
static inline void rmt_ll_tx_start(rmt_dev_t *dev, uint32_t channel)
|
||||
{
|
||||
dev->conf_ch[channel].conf1.tx_start = 1;
|
||||
}
|
||||
|
||||
__attribute__((always_inline))
|
||||
static inline void rmt_ll_tx_stop(rmt_dev_t *dev, uint32_t channel)
|
||||
{
|
||||
RMTMEM.chan[channel].data32[0].val = 0;
|
||||
dev->conf_ch[channel].conf1.tx_start = 0;
|
||||
dev->conf_ch[channel].conf1.mem_rd_rst = 1;
|
||||
dev->conf_ch[channel].conf1.mem_rd_rst = 0;
|
||||
}
|
||||
|
||||
__attribute__((always_inline))
|
||||
static inline void rmt_ll_tx_enable_pingpong(rmt_dev_t *dev, uint32_t channel, bool enable)
|
||||
{
|
||||
dev->apb_conf.mem_tx_wrap_en = enable;
|
||||
}
|
||||
|
||||
__attribute__((always_inline))
|
||||
static inline void rmt_ll_tx_enable_loop(rmt_dev_t *dev, uint32_t channel, bool enable)
|
||||
{
|
||||
dev->conf_ch[channel].conf1.tx_conti_mode = enable;
|
||||
}
|
||||
|
||||
__attribute__((always_inline))
|
||||
static inline uint32_t rmt_ll_tx_get_channel_status(rmt_dev_t *dev, uint32_t channel)
|
||||
{
|
||||
return dev->status_ch[channel];
|
||||
}
|
||||
|
||||
__attribute__((always_inline))
|
||||
static inline void rmt_ll_tx_set_limit(rmt_dev_t *dev, uint32_t channel, uint32_t limit)
|
||||
{
|
||||
dev->tx_lim_ch[channel].limit = limit;
|
||||
}
|
||||
|
||||
__attribute__((always_inline))
|
||||
static inline void rmt_ll_enable_interrupt(rmt_dev_t *dev, uint32_t mask, bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
dev->int_ena.val |= mask;
|
||||
} else {
|
||||
dev->int_ena.val &= ~mask;
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((always_inline))
|
||||
static inline void rmt_ll_enable_tx_end_interrupt(rmt_dev_t *dev, uint32_t channel, bool enable)
|
||||
{
|
||||
dev->int_ena.val &= ~(1 << (channel * 3));
|
||||
dev->int_ena.val |= (enable << (channel * 3));
|
||||
}
|
||||
|
||||
__attribute__((always_inline))
|
||||
static inline void rmt_ll_enable_tx_err_interrupt(rmt_dev_t *dev, uint32_t channel, bool enable)
|
||||
{
|
||||
dev->int_ena.val &= ~(1 << (channel * 3 + 2));
|
||||
dev->int_ena.val |= (enable << (channel * 3 + 2));
|
||||
}
|
||||
|
||||
__attribute__((always_inline))
|
||||
static inline void rmt_ll_enable_tx_thres_interrupt(rmt_dev_t *dev, uint32_t channel, bool enable)
|
||||
{
|
||||
dev->int_ena.val &= ~(1 << (channel + 24));
|
||||
dev->int_ena.val |= (enable << (channel + 24));
|
||||
}
|
||||
|
||||
__attribute__((always_inline))
|
||||
static inline void rmt_ll_clear_tx_end_interrupt(rmt_dev_t *dev, uint32_t channel)
|
||||
{
|
||||
dev->int_clr.val = (1 << (channel * 3));
|
||||
}
|
||||
|
||||
__attribute__((always_inline))
|
||||
static inline void rmt_ll_clear_tx_err_interrupt(rmt_dev_t *dev, uint32_t channel)
|
||||
{
|
||||
dev->int_clr.val = (1 << (channel * 3 + 2));
|
||||
}
|
||||
|
||||
__attribute__((always_inline))
|
||||
static inline void rmt_ll_clear_tx_thres_interrupt(rmt_dev_t *dev, uint32_t channel)
|
||||
{
|
||||
dev->int_clr.val = (1 << (channel + 24));
|
||||
}
|
||||
|
||||
|
||||
__attribute__((always_inline))
|
||||
static inline uint32_t rmt_ll_get_tx_thres_interrupt_status(rmt_dev_t *dev)
|
||||
{
|
||||
uint32_t status = dev->int_st.val;
|
||||
return (status & 0xFF000000) >> 24;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// *********************************
|
||||
// Select method for binding interrupt
|
||||
//
|
||||
// - If the Bluetooth driver has registered a high-level interrupt, piggyback on that API
|
||||
// - If we're on a modern core, allocate the interrupt with the API (old cores are bugged)
|
||||
// - Otherwise use the low-level hardware API to manually bind the interrupt
|
||||
|
||||
|
||||
#if defined(CONFIG_BTDM_CTRL_HLI)
|
||||
// Espressif's bluetooth driver offers a helpful sharing layer; bring in the interrupt management calls
|
||||
#include "hal/interrupt_controller_hal.h"
|
||||
extern "C" esp_err_t hli_intr_register(intr_handler_t handler, void* arg, uint32_t intr_reg, uint32_t intr_mask);
|
||||
|
||||
#else /* !CONFIG_BTDM_CTRL_HLI*/
|
||||
|
||||
// Declare the our high-priority ISR handler
|
||||
extern "C" void ld_include_hli_vectors_rmt(); // an object with an address, but no space
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#include "soc/periph_defs.h"
|
||||
#endif
|
||||
|
||||
// Select level flag
|
||||
#if defined(__riscv)
|
||||
// RISCV chips don't block interrupts while scheduling; all we need to do is be higher than the WiFi ISR
|
||||
#define INT_LEVEL_FLAG ESP_INTR_FLAG_LEVEL3
|
||||
#elif defined(CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5)
|
||||
#define INT_LEVEL_FLAG ESP_INTR_FLAG_LEVEL4
|
||||
#else
|
||||
#define INT_LEVEL_FLAG ESP_INTR_FLAG_LEVEL5
|
||||
#endif
|
||||
|
||||
// ESP-IDF v3 cannot enable high priority interrupts through the API at all;
|
||||
// and ESP-IDF v4 on XTensa cannot enable Level 5 due to incorrect interrupt descriptor tables
|
||||
#if !defined(__XTENSA__) || (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) || ((ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0) && CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5))
|
||||
#define NEOESP32_RMT_CAN_USE_INTR_ALLOC
|
||||
|
||||
// XTensa cores require the assembly bridge
|
||||
#ifdef __XTENSA__
|
||||
#define HI_IRQ_HANDLER nullptr
|
||||
#define HI_IRQ_HANDLER_ARG ld_include_hli_vectors_rmt
|
||||
#else
|
||||
#define HI_IRQ_HANDLER NeoEsp32RmtMethodIsr
|
||||
#define HI_IRQ_HANDLER_ARG nullptr
|
||||
#endif
|
||||
|
||||
#else
|
||||
/* !CONFIG_BTDM_CTRL_HLI && !NEOESP32_RMT_CAN_USE_INTR_ALLOC */
|
||||
// This is the index of the LV5 interrupt vector - see interrupt descriptor table in idf components/hal/esp32/interrupt_descriptor_table.c
|
||||
#define ESP32_LV5_IRQ_INDEX 26
|
||||
|
||||
#endif /* NEOESP32_RMT_CAN_USE_INTR_ALLOC */
|
||||
#endif /* CONFIG_BTDM_CTRL_HLI */
|
||||
|
||||
|
||||
// RMT driver implementation
|
||||
struct NeoEsp32RmtHIChannelState {
|
||||
uint32_t rmtBit0, rmtBit1;
|
||||
uint32_t resetDuration;
|
||||
|
||||
const byte* txDataStart; // data array
|
||||
const byte* txDataEnd; // one past end
|
||||
const byte* txDataCurrent; // current location
|
||||
size_t rmtOffset;
|
||||
};
|
||||
|
||||
// Global variables
|
||||
#if defined(NEOESP32_RMT_CAN_USE_INTR_ALLOC)
|
||||
static intr_handle_t isrHandle = nullptr;
|
||||
#endif
|
||||
|
||||
static NeoEsp32RmtHIChannelState** driverState = nullptr;
|
||||
constexpr size_t rmtBatchSize = RMT_MEM_ITEM_NUM / 2;
|
||||
|
||||
// Fill the RMT buffer memory
|
||||
// This is implemented using many arguments instead of passing the structure object to ensure we do only one lookup
|
||||
// All the arguments are passed in registers, so they don't need to be looked up again
|
||||
static void IRAM_ATTR RmtFillBuffer(uint8_t channel, const byte** src_ptr, const byte* end, uint32_t bit0, uint32_t bit1, size_t* offset_ptr, size_t reserve) {
|
||||
// We assume that (rmtToWrite % 8) == 0
|
||||
size_t rmtToWrite = rmtBatchSize - reserve;
|
||||
rmt_item32_t* dest =(rmt_item32_t*) &RMTMEM.chan[channel].data32[*offset_ptr + reserve]; // write directly in to RMT memory
|
||||
const byte* psrc = *src_ptr;
|
||||
|
||||
*offset_ptr ^= rmtBatchSize;
|
||||
|
||||
if (psrc != end) {
|
||||
while (rmtToWrite > 0) {
|
||||
uint8_t data = *psrc;
|
||||
for (uint8_t bit = 0; bit < 8; bit++)
|
||||
{
|
||||
dest->val = (data & 0x80) ? bit1 : bit0;
|
||||
dest++;
|
||||
data <<= 1;
|
||||
}
|
||||
rmtToWrite -= 8;
|
||||
psrc++;
|
||||
|
||||
if (psrc == end) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*src_ptr = psrc;
|
||||
}
|
||||
|
||||
if (rmtToWrite > 0) {
|
||||
// Add end event
|
||||
rmt_item32_t bit0_val = {{.val = bit0 }};
|
||||
*dest = rmt_item32_t {{{ .duration0 = 0, .level0 = bit0_val.level1, .duration1 = 0, .level1 = bit0_val.level1 }}};
|
||||
}
|
||||
}
|
||||
|
||||
static void IRAM_ATTR RmtStartWrite(uint8_t channel, NeoEsp32RmtHIChannelState& state) {
|
||||
// Reset context state
|
||||
state.rmtOffset = 0;
|
||||
|
||||
// Fill the first part of the buffer with a reset event
|
||||
// FUTURE: we could do timing analysis with the last interrupt on this channel
|
||||
// Use 8 words to stay aligned with the buffer fill logic
|
||||
rmt_item32_t bit0_val = {{.val = state.rmtBit0 }};
|
||||
rmt_item32_t fill = {{{ .duration0 = 100, .level0 = bit0_val.level1, .duration1 = 100, .level1 = bit0_val.level1 }}};
|
||||
rmt_item32_t* dest = (rmt_item32_t*) &RMTMEM.chan[channel].data32[0];
|
||||
for (auto i = 0; i < 7; ++i) dest[i] = fill;
|
||||
fill.duration1 = state.resetDuration > 1400 ? (state.resetDuration - 1400) : 100;
|
||||
dest[7] = fill;
|
||||
|
||||
// Fill the remaining buffer with real data
|
||||
RmtFillBuffer(channel, &state.txDataCurrent, state.txDataEnd, state.rmtBit0, state.rmtBit1, &state.rmtOffset, 8);
|
||||
RmtFillBuffer(channel, &state.txDataCurrent, state.txDataEnd, state.rmtBit0, state.rmtBit1, &state.rmtOffset, 0);
|
||||
|
||||
// Start operation
|
||||
rmt_ll_clear_tx_thres_interrupt(&RMT, channel);
|
||||
rmt_ll_tx_reset_pointer(&RMT, channel);
|
||||
rmt_ll_tx_start(&RMT, channel);
|
||||
}
|
||||
|
||||
extern "C" void IRAM_ATTR NeoEsp32RmtMethodIsr(void *arg) {
|
||||
// Tx threshold interrupt
|
||||
uint32_t status = rmt_ll_get_tx_thres_interrupt_status(&RMT);
|
||||
while (status) {
|
||||
uint8_t channel = __builtin_ffs(status) - 1;
|
||||
if (driverState[channel]) {
|
||||
// Normal case
|
||||
NeoEsp32RmtHIChannelState& state = *driverState[channel];
|
||||
RmtFillBuffer(channel, &state.txDataCurrent, state.txDataEnd, state.rmtBit0, state.rmtBit1, &state.rmtOffset, 0);
|
||||
} else {
|
||||
// Danger - another driver got invoked?
|
||||
rmt_ll_tx_stop(&RMT, channel);
|
||||
}
|
||||
rmt_ll_clear_tx_thres_interrupt(&RMT, channel);
|
||||
status = rmt_ll_get_tx_thres_interrupt_status(&RMT);
|
||||
}
|
||||
};
|
||||
|
||||
// Wrapper around the register analysis defines
|
||||
// For all currently supported chips, this is constant for all channels; but this is not true of *all* ESP32
|
||||
static inline bool _RmtStatusIsTransmitting(rmt_channel_t channel, uint32_t status) {
|
||||
uint32_t v;
|
||||
switch(channel) {
|
||||
#ifdef RMT_STATE_CH0
|
||||
case 0: v = (status >> RMT_STATE_CH0_S) & RMT_STATE_CH0_V; break;
|
||||
#endif
|
||||
#ifdef RMT_STATE_CH1
|
||||
case 1: v = (status >> RMT_STATE_CH1_S) & RMT_STATE_CH1_V; break;
|
||||
#endif
|
||||
#ifdef RMT_STATE_CH2
|
||||
case 2: v = (status >> RMT_STATE_CH2_S) & RMT_STATE_CH2_V; break;
|
||||
#endif
|
||||
#ifdef RMT_STATE_CH3
|
||||
case 3: v = (status >> RMT_STATE_CH3_S) & RMT_STATE_CH3_V; break;
|
||||
#endif
|
||||
#ifdef RMT_STATE_CH4
|
||||
case 4: v = (status >> RMT_STATE_CH4_S) & RMT_STATE_CH4_V; break;
|
||||
#endif
|
||||
#ifdef RMT_STATE_CH5
|
||||
case 5: v = (status >> RMT_STATE_CH5_S) & RMT_STATE_CH5_V; break;
|
||||
#endif
|
||||
#ifdef RMT_STATE_CH6
|
||||
case 6: v = (status >> RMT_STATE_CH6_S) & RMT_STATE_CH6_V; break;
|
||||
#endif
|
||||
#ifdef RMT_STATE_CH7
|
||||
case 7: v = (status >> RMT_STATE_CH7_S) & RMT_STATE_CH7_V; break;
|
||||
#endif
|
||||
default: v = 0;
|
||||
}
|
||||
|
||||
return v != 0;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t NeoEsp32RmtHiMethodDriver::Install(rmt_channel_t channel, uint32_t rmtBit0, uint32_t rmtBit1, uint32_t reset) {
|
||||
// Validate channel number
|
||||
if (channel >= RMT_CHANNEL_MAX) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_err_t err = ESP_OK;
|
||||
if (!driverState) {
|
||||
// First time init
|
||||
driverState = reinterpret_cast<NeoEsp32RmtHIChannelState**>(heap_caps_calloc(RMT_CHANNEL_MAX, sizeof(NeoEsp32RmtHIChannelState*), MALLOC_CAP_INTERNAL));
|
||||
if (!driverState) return ESP_ERR_NO_MEM;
|
||||
|
||||
// Ensure all interrupts are cleared before binding
|
||||
RMT.int_ena.val = 0;
|
||||
RMT.int_clr.val = 0xFFFFFFFF;
|
||||
|
||||
// Bind interrupt handler
|
||||
#if defined(CONFIG_BTDM_CTRL_HLI)
|
||||
// Bluetooth driver has taken the empty high-priority interrupt. Fortunately, it allows us to
|
||||
// hook up another handler.
|
||||
err = hli_intr_register(NeoEsp32RmtMethodIsr, nullptr, (uintptr_t) &RMT.int_st, 0xFF000000);
|
||||
// 25 is the magic number of the bluetooth ISR on ESP32 - see soc/soc.h.
|
||||
intr_matrix_set(cpu_hal_get_core_id(), ETS_RMT_INTR_SOURCE, 25);
|
||||
intr_cntrl_ll_enable_interrupts(1<<25);
|
||||
#elif defined(NEOESP32_RMT_CAN_USE_INTR_ALLOC)
|
||||
// Use the platform code to allocate the interrupt
|
||||
// If we need the additional assembly bridge, we pass it as the "arg" to the IDF so it gets linked in
|
||||
err = esp_intr_alloc(ETS_RMT_INTR_SOURCE, INT_LEVEL_FLAG | ESP_INTR_FLAG_IRAM, HI_IRQ_HANDLER, (void*) HI_IRQ_HANDLER_ARG, &isrHandle);
|
||||
//err = ESP_ERR_NOT_FINISHED;
|
||||
#else
|
||||
// Broken IDF API does not allow us to reserve the interrupt; do it manually
|
||||
static volatile const void* __attribute__((used)) pleaseLinkAssembly = (void*) ld_include_hli_vectors_rmt;
|
||||
intr_matrix_set(xPortGetCoreID(), ETS_RMT_INTR_SOURCE, ESP32_LV5_IRQ_INDEX);
|
||||
ESP_INTR_ENABLE(ESP32_LV5_IRQ_INDEX);
|
||||
#endif
|
||||
|
||||
if (err != ESP_OK) {
|
||||
heap_caps_free(driverState);
|
||||
driverState = nullptr;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (driverState[channel] != nullptr) {
|
||||
return ESP_ERR_INVALID_STATE; // already in use
|
||||
}
|
||||
|
||||
NeoEsp32RmtHIChannelState* state = reinterpret_cast<NeoEsp32RmtHIChannelState*>(heap_caps_calloc(1, sizeof(NeoEsp32RmtHIChannelState), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT));
|
||||
if (state == nullptr) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
// Store timing information
|
||||
state->rmtBit0 = rmtBit0;
|
||||
state->rmtBit1 = rmtBit1;
|
||||
state->resetDuration = reset;
|
||||
|
||||
// Initialize hardware
|
||||
rmt_ll_tx_stop(&RMT, channel);
|
||||
rmt_ll_tx_reset_pointer(&RMT, channel);
|
||||
rmt_ll_enable_tx_err_interrupt(&RMT, channel, false);
|
||||
rmt_ll_enable_tx_end_interrupt(&RMT, channel, false);
|
||||
rmt_ll_enable_tx_thres_interrupt(&RMT, channel, false);
|
||||
rmt_ll_clear_tx_err_interrupt(&RMT, channel);
|
||||
rmt_ll_clear_tx_end_interrupt(&RMT, channel);
|
||||
rmt_ll_clear_tx_thres_interrupt(&RMT, channel);
|
||||
|
||||
rmt_ll_tx_enable_loop(&RMT, channel, false);
|
||||
rmt_ll_tx_enable_pingpong(&RMT, channel, true);
|
||||
rmt_ll_tx_set_limit(&RMT, channel, rmtBatchSize);
|
||||
|
||||
driverState[channel] = state;
|
||||
|
||||
rmt_ll_enable_tx_thres_interrupt(&RMT, channel, true);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t NeoEsp32RmtHiMethodDriver::Uninstall(rmt_channel_t channel) {
|
||||
if ((channel >= RMT_CHANNEL_MAX) || !driverState || !driverState[channel]) return ESP_ERR_INVALID_ARG;
|
||||
|
||||
NeoEsp32RmtHIChannelState* state = driverState[channel];
|
||||
|
||||
WaitForTxDone(channel, 10000 / portTICK_PERIOD_MS);
|
||||
|
||||
// Done or not, we're out of here
|
||||
rmt_ll_tx_stop(&RMT, channel);
|
||||
rmt_ll_enable_tx_thres_interrupt(&RMT, channel, false);
|
||||
driverState[channel] = nullptr;
|
||||
heap_caps_free(state);
|
||||
|
||||
#if !defined(CONFIG_BTDM_CTRL_HLI) /* Cannot unbind from bluetooth ISR */
|
||||
// Turn off the driver ISR and release global state if none are left
|
||||
for (uint8_t channelIndex = 0; channelIndex < RMT_CHANNEL_MAX; ++channelIndex) {
|
||||
if (driverState[channelIndex]) return ESP_OK; // done
|
||||
}
|
||||
|
||||
#if defined(NEOESP32_RMT_CAN_USE_INTR_ALLOC)
|
||||
esp_intr_free(isrHandle);
|
||||
#else
|
||||
ESP_INTR_DISABLE(ESP32_LV5_IRQ_INDEX);
|
||||
#endif
|
||||
|
||||
heap_caps_free(driverState);
|
||||
driverState = nullptr;
|
||||
#endif /* !defined(CONFIG_BTDM_CTRL_HLI) */
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t NeoEsp32RmtHiMethodDriver::Write(rmt_channel_t channel, const uint8_t *src, size_t src_size) {
|
||||
if ((channel >= RMT_CHANNEL_MAX) || !driverState || !driverState[channel]) return ESP_ERR_INVALID_ARG;
|
||||
|
||||
NeoEsp32RmtHIChannelState& state = *driverState[channel];
|
||||
esp_err_t result = WaitForTxDone(channel, 10000 / portTICK_PERIOD_MS);
|
||||
|
||||
if (result == ESP_OK) {
|
||||
state.txDataStart = src;
|
||||
state.txDataCurrent = src;
|
||||
state.txDataEnd = src + src_size;
|
||||
RmtStartWrite(channel, state);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
esp_err_t NeoEsp32RmtHiMethodDriver::WaitForTxDone(rmt_channel_t channel, TickType_t wait_time) {
|
||||
if ((channel >= RMT_CHANNEL_MAX) || !driverState || !driverState[channel]) return ESP_ERR_INVALID_ARG;
|
||||
|
||||
NeoEsp32RmtHIChannelState& state = *driverState[channel];
|
||||
// yield-wait until wait_time
|
||||
esp_err_t rv = ESP_OK;
|
||||
uint32_t status;
|
||||
while(1) {
|
||||
status = rmt_ll_tx_get_channel_status(&RMT, channel);
|
||||
if (!_RmtStatusIsTransmitting(channel, status)) break;
|
||||
if (wait_time == 0) { rv = ESP_ERR_TIMEOUT; break; };
|
||||
|
||||
TickType_t sleep = std::min(wait_time, (TickType_t) 5);
|
||||
vTaskDelay(sleep);
|
||||
wait_time -= sleep;
|
||||
};
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
#endif
|
||||
46
lib/README
Normal file
46
lib/README
Normal file
@@ -0,0 +1,46 @@
|
||||
|
||||
This directory is intended for project specific (private) libraries.
|
||||
PlatformIO will compile them to static libraries and link into executable file.
|
||||
|
||||
The source code of each library should be placed in a an own separate directory
|
||||
("lib/your_library_name/[here are source files]").
|
||||
|
||||
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||
|
||||
|--lib
|
||||
| |
|
||||
| |--Bar
|
||||
| | |--docs
|
||||
| | |--examples
|
||||
| | |--src
|
||||
| | |- Bar.c
|
||||
| | |- Bar.h
|
||||
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||
| |
|
||||
| |--Foo
|
||||
| | |- Foo.c
|
||||
| | |- Foo.h
|
||||
| |
|
||||
| |- README --> THIS FILE
|
||||
|
|
||||
|- platformio.ini
|
||||
|--src
|
||||
|- main.c
|
||||
|
||||
and a contents of `src/main.c`:
|
||||
```
|
||||
#include <Foo.h>
|
||||
#include <Bar.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
PlatformIO Library Dependency Finder will find automatically dependent
|
||||
libraries scanning project source files.
|
||||
|
||||
More information about PlatformIO Library Dependency Finder
|
||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
||||
790
package-lock.json
generated
Normal file
790
package-lock.json
generated
Normal file
@@ -0,0 +1,790 @@
|
||||
{
|
||||
"name": "wled",
|
||||
"version": "17.0.0-dev",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "wled",
|
||||
"version": "17.0.0-dev",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"clean-css": "^5.3.3",
|
||||
"html-minifier-terser": "^7.2.0",
|
||||
"nodemon": "^3.1.14",
|
||||
"web-resource-inliner": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
|
||||
"integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/set-array": "^1.2.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
"@jridgewell/trace-mapping": "^0.3.24"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/resolve-uri": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/set-array": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
|
||||
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/source-map": {
|
||||
"version": "0.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
|
||||
"integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
"@jridgewell/trace-mapping": "^0.3.25"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
|
||||
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.25",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
|
||||
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.1.0",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.14.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
|
||||
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-colors": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
|
||||
"integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/anymatch": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"normalize-path": "^3.0.0",
|
||||
"picomatch": "^2.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
|
||||
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/binary-extensions": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
|
||||
"integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fill-range": "^7.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-from": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/camel-case": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz",
|
||||
"integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pascal-case": "^3.1.2",
|
||||
"tslib": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/chokidar": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"anymatch": "~3.1.2",
|
||||
"braces": "~3.0.2",
|
||||
"glob-parent": "~5.1.2",
|
||||
"is-binary-path": "~2.1.0",
|
||||
"is-glob": "~4.0.1",
|
||||
"normalize-path": "~3.0.0",
|
||||
"readdirp": "~3.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/clean-css": {
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz",
|
||||
"integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"source-map": "~0.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
|
||||
"integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/dom-serializer": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
|
||||
"integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.0.1",
|
||||
"domhandler": "^4.2.0",
|
||||
"entities": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/dom-serializer/node_modules/domhandler": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
|
||||
"integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/dom-serializer/node_modules/entities": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
|
||||
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
|
||||
"license": "BSD-2-Clause",
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/domelementtype": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
|
||||
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fb55"
|
||||
}
|
||||
],
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/domhandler": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz",
|
||||
"integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/domutils": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
|
||||
"integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"dom-serializer": "^1.0.1",
|
||||
"domelementtype": "^2.2.0",
|
||||
"domhandler": "^4.2.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/domutils?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/domutils/node_modules/domhandler": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
|
||||
"integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/dot-case": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
|
||||
"integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"no-case": "^3.0.4",
|
||||
"tslib": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/entities": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-goat": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-3.0.0.tgz",
|
||||
"integrity": "sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/html-minifier-terser": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz",
|
||||
"integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"camel-case": "^4.1.2",
|
||||
"clean-css": "~5.3.2",
|
||||
"commander": "^10.0.0",
|
||||
"entities": "^4.4.0",
|
||||
"param-case": "^3.0.4",
|
||||
"relateurl": "^0.2.7",
|
||||
"terser": "^5.15.1"
|
||||
},
|
||||
"bin": {
|
||||
"html-minifier-terser": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.13.1 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/htmlparser2": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-5.0.1.tgz",
|
||||
"integrity": "sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.0.1",
|
||||
"domhandler": "^3.3.0",
|
||||
"domutils": "^2.4.2",
|
||||
"entities": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/htmlparser2?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/htmlparser2/node_modules/entities": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
|
||||
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
|
||||
"license": "BSD-2-Clause",
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/ignore-by-default": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
|
||||
"integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/is-binary-path": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"binary-extensions": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-extglob": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-glob": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-extglob": "^2.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-number": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lower-case": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
|
||||
"integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/mime": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
|
||||
"integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"mime": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "10.2.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
|
||||
"integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^5.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/no-case": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
|
||||
"integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lower-case": "^2.0.2",
|
||||
"tslib": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon": {
|
||||
"version": "3.1.14",
|
||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.14.tgz",
|
||||
"integrity": "sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chokidar": "^3.5.2",
|
||||
"debug": "^4",
|
||||
"ignore-by-default": "^1.0.1",
|
||||
"minimatch": "^10.2.1",
|
||||
"pstree.remy": "^1.1.8",
|
||||
"semver": "^7.5.3",
|
||||
"simple-update-notifier": "^2.0.0",
|
||||
"supports-color": "^5.5.0",
|
||||
"touch": "^3.1.0",
|
||||
"undefsafe": "^2.0.5"
|
||||
},
|
||||
"bin": {
|
||||
"nodemon": "bin/nodemon.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/nodemon"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/param-case": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
|
||||
"integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dot-case": "^3.0.4",
|
||||
"tslib": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/pascal-case": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
|
||||
"integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"no-case": "^3.0.4",
|
||||
"tslib": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
|
||||
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/pstree.remy": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
|
||||
"integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/readdirp": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"picomatch": "^2.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/relateurl": {
|
||||
"version": "0.2.7",
|
||||
"resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
|
||||
"integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.7.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz",
|
||||
"integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-update-notifier": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
|
||||
"integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"semver": "^7.5.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-support": {
|
||||
"version": "0.5.21",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
|
||||
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-flag": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.37.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz",
|
||||
"integrity": "sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.3",
|
||||
"acorn": "^8.8.2",
|
||||
"commander": "^2.20.0",
|
||||
"source-map-support": "~0.5.20"
|
||||
},
|
||||
"bin": {
|
||||
"terser": "bin/terser"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/terser/node_modules/commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/touch": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz",
|
||||
"integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"nodetouch": "bin/nodetouch.js"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/undefsafe": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
|
||||
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/valid-data-url": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/valid-data-url/-/valid-data-url-3.0.1.tgz",
|
||||
"integrity": "sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/web-resource-inliner": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/web-resource-inliner/-/web-resource-inliner-7.0.0.tgz",
|
||||
"integrity": "sha512-NlfnGF8MY9ZUwFjyq3vOUBx7KwF8bmE+ywR781SB0nWB6MoMxN4BA8gtgP1KGTZo/O/AyWJz7HZpR704eaj4mg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-colors": "^4.1.1",
|
||||
"escape-goat": "^3.0.0",
|
||||
"htmlparser2": "^5.0.0",
|
||||
"mime": "^2.4.6",
|
||||
"valid-data-url": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
34
package.json
Normal file
34
package.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "wled",
|
||||
"version": "17.0.0-dev",
|
||||
"description": "Tools for WLED project",
|
||||
"main": "tools/cdata.js",
|
||||
"directories": {
|
||||
"lib": "lib",
|
||||
"test": "test"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "node tools/cdata.js",
|
||||
"test": "node --test",
|
||||
"dev": "nodemon -e js,html,htm,css,png,jpg,gif,ico,js -w tools/ -w wled00/data/ -x node tools/cdata.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/wled/WLED.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/wled/WLED/issues"
|
||||
},
|
||||
"homepage": "https://github.com/wled/WLED#readme",
|
||||
"dependencies": {
|
||||
"clean-css": "^5.3.3",
|
||||
"html-minifier-terser": "^7.2.0",
|
||||
"web-resource-inliner": "^7.0.0",
|
||||
"nodemon": "^3.1.14"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
}
|
||||
21
pio-scripts/build_ui.py
Normal file
21
pio-scripts/build_ui.py
Normal file
@@ -0,0 +1,21 @@
|
||||
Import("env")
|
||||
import shutil
|
||||
|
||||
node_ex = shutil.which("node")
|
||||
# Check if Node.js is installed and present in PATH if it failed, abort the build
|
||||
if node_ex is None:
|
||||
print('\x1b[0;31;43m' + 'Node.js is not installed or missing from PATH html css js will not be processed check https://kno.wled.ge/advanced/compiling-wled/' + '\x1b[0m')
|
||||
exitCode = env.Execute("null")
|
||||
exit(exitCode)
|
||||
else:
|
||||
# Install the necessary node packages for the pre-build asset bundling script
|
||||
print('\x1b[6;33;42m' + 'Installing node packages' + '\x1b[0m')
|
||||
env.Execute("npm ci")
|
||||
|
||||
# Call the bundling script
|
||||
exitCode = env.Execute("npm run build")
|
||||
|
||||
# If it failed, abort the build
|
||||
if (exitCode):
|
||||
print('\x1b[0;31;43m' + 'npm run build fails check https://kno.wled.ge/advanced/compiling-wled/' + '\x1b[0m')
|
||||
exit(exitCode)
|
||||
86
pio-scripts/dynarray.py
Normal file
86
pio-scripts/dynarray.py
Normal file
@@ -0,0 +1,86 @@
|
||||
# Add a section to the linker script to store our dynamic arrays
|
||||
# This is implemented as a pio post-script to ensure that we can
|
||||
# place our linker script at the correct point in the command arguments.
|
||||
Import("env")
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
# Linker script fragment injected into the rodata output section of whichever
|
||||
# platform we're building for. Placed just before the end-of-rodata marker so
|
||||
# that the dynarray entries land in flash rodata and are correctly sorted.
|
||||
DYNARRAY_INJECTION = (
|
||||
"\n /* dynarray: WLED dynamic module arrays */\n"
|
||||
" . = ALIGN(0x10);\n"
|
||||
" KEEP(*(SORT_BY_INIT_PRIORITY(.dynarray.*)))\n"
|
||||
" "
|
||||
)
|
||||
|
||||
|
||||
def inject_before_marker(path, marker):
|
||||
"""Patch a linker script file in-place, inserting DYNARRAY_INJECTION before marker."""
|
||||
original = path.read_text()
|
||||
marker_pos = original.find(marker)
|
||||
if marker_pos < 0:
|
||||
raise RuntimeError(
|
||||
f"DYNARRAY injection marker not found in linker script: path={path}, marker={marker!r}"
|
||||
)
|
||||
patched = original[:marker_pos] + DYNARRAY_INJECTION + original[marker_pos:]
|
||||
path.write_text(patched)
|
||||
|
||||
|
||||
if env.get("PIOPLATFORM") == "espressif32":
|
||||
# Find sections.ld on the linker search path (LIBPATH).
|
||||
sections_ld_path = None
|
||||
for ld_dir in env.get("LIBPATH", []):
|
||||
candidate = Path(str(ld_dir)) / "sections.ld"
|
||||
if candidate.exists():
|
||||
sections_ld_path = candidate
|
||||
break
|
||||
|
||||
if sections_ld_path is not None:
|
||||
# Inject inside the existing .flash.rodata output section, just before
|
||||
# _rodata_end. IDF v5 enforces zero gaps between adjacent output
|
||||
# sections via ASSERT statements, so INSERT AFTER .flash.rodata would
|
||||
# fail. Injecting inside the section creates no new output section and
|
||||
# leaves the ASSERTs satisfied.
|
||||
build_dir = Path(env.subst("$BUILD_DIR"))
|
||||
patched_path = build_dir / "dynarray_sections.ld"
|
||||
shutil.copy(sections_ld_path, patched_path)
|
||||
inject_before_marker(patched_path, "_rodata_end = ABSOLUTE(.);")
|
||||
|
||||
# Replace "sections.ld" in LINKFLAGS with an absolute path to our
|
||||
# patched copy. The flag may appear as a bare token, combined as
|
||||
# "-Tsections.ld", or split across two tokens ("-T", "sections.ld").
|
||||
patched_str = str(patched_path)
|
||||
new_flags = []
|
||||
skip_next = False
|
||||
for flag in env.get("LINKFLAGS", []):
|
||||
if skip_next:
|
||||
new_flags.append(patched_str if flag == "sections.ld" else flag)
|
||||
skip_next = False
|
||||
elif flag == "-T":
|
||||
new_flags.append(flag)
|
||||
skip_next = True
|
||||
else:
|
||||
new_flags.append(flag.replace("sections.ld", patched_str))
|
||||
env.Replace(LINKFLAGS=new_flags)
|
||||
else:
|
||||
# Assume sections.ld will be built (ESP-IDF format); add a post-action to patch it
|
||||
# TODO: consider using ESP-IDF linker fragment (https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/linker-script-generation.html)
|
||||
# For now, patch after building
|
||||
sections_ld = Path(env.subst("$BUILD_DIR")) / "sections.ld"
|
||||
def patch_sections_ld(target, source, env):
|
||||
inject_before_marker(sections_ld, "_rodata_end = ABSOLUTE(.);")
|
||||
env.AddPostAction(str(sections_ld), patch_sections_ld)
|
||||
|
||||
elif env.get("PIOPLATFORM") == "espressif8266":
|
||||
# The ESP8266 framework preprocesses eagle.app.v6.common.ld.h into
|
||||
# local.eagle.app.v6.common.ld in $BUILD_DIR/ld/ at build time. Register
|
||||
# a post-action on that generated file so the injection happens after
|
||||
# C-preprocessing but before linking.
|
||||
build_ld = Path(env.subst("$BUILD_DIR")) / "ld" / "local.eagle.app.v6.common.ld"
|
||||
|
||||
def patch_esp8266_ld(target, source, env):
|
||||
inject_before_marker(build_ld, "_irom0_text_end = ABSOLUTE(.);")
|
||||
|
||||
env.AddPostAction(str(build_ld), patch_esp8266_ld)
|
||||
210
pio-scripts/load_usermods.py
Normal file
210
pio-scripts/load_usermods.py
Normal file
@@ -0,0 +1,210 @@
|
||||
Import('env')
|
||||
from collections import deque
|
||||
from pathlib import Path # For OS-agnostic path manipulation
|
||||
import re
|
||||
from urllib.parse import urlparse
|
||||
from click import secho
|
||||
from SCons.Script import Exit
|
||||
from platformio.builder.tools.piolib import LibBuilderBase
|
||||
|
||||
usermod_dir = Path(env["PROJECT_DIR"]).resolve() / "usermods"
|
||||
|
||||
# Utility functions
|
||||
def find_usermod(mod: str) -> Path:
|
||||
"""Locate this library in the usermods folder.
|
||||
We do this to avoid needing to rename a bunch of folders;
|
||||
this could be removed later
|
||||
"""
|
||||
# Check name match
|
||||
mp = usermod_dir / mod
|
||||
if mp.exists():
|
||||
return mp
|
||||
mp = usermod_dir / f"{mod}_v2"
|
||||
if mp.exists():
|
||||
return mp
|
||||
mp = usermod_dir / f"usermod_v2_{mod}"
|
||||
if mp.exists():
|
||||
return mp
|
||||
raise RuntimeError(f"Couldn't locate module {mod} in usermods directory!")
|
||||
|
||||
# Names of external/registry deps listed in custom_usermods.
|
||||
# Populated during parsing below; read by is_wled_module() at configure time.
|
||||
_custom_usermod_names: set[str] = set()
|
||||
|
||||
# Matches any RFC-valid URL scheme (http, https, git, git+https, symlink, file, hg+ssh, etc.)
|
||||
_URL_SCHEME_RE = re.compile(r'^[a-zA-Z][a-zA-Z0-9+.-]*://')
|
||||
# SSH git URL: user@host:path (e.g. git@github.com:user/repo.git#tag)
|
||||
_SSH_URL_RE = re.compile(r'^[^@\s]+@[^@:\s]+:[^:\s]')
|
||||
# Explicit custom name: "LibName = <spec>" (PlatformIO [<name>=]<spec> form)
|
||||
_NAME_EQ_RE = re.compile(r'^([A-Za-z0-9_.-]+)\s*=\s*(\S.*)')
|
||||
|
||||
|
||||
def _is_external_entry(line: str) -> bool:
|
||||
"""Return True if line is a lib_deps-style external/registry entry."""
|
||||
if _NAME_EQ_RE.match(line): # "LibName = <spec>"
|
||||
return True
|
||||
if _URL_SCHEME_RE.match(line): # https://, git://, symlink://, etc.
|
||||
return True
|
||||
if _SSH_URL_RE.match(line): # git@github.com:user/repo.git
|
||||
return True
|
||||
if '@' in line: # "owner/Name @ ^1.0.0"
|
||||
return True
|
||||
if re.match(r'^[^/\s]+/[^/\s]+$', line): # "owner/Name"
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _predict_dep_name(entry: str) -> str | None:
|
||||
"""Predict the library name PlatformIO will assign to this dep (best-effort).
|
||||
|
||||
Accuracy relies on the library's manifest "name" matching the repo/package
|
||||
name in the spec. This holds for well-authored libraries; the libArchive
|
||||
check (which requires library.json) provides an early-failure safety net.
|
||||
"""
|
||||
entry = entry.strip()
|
||||
# "LibName = <spec>" — name is given explicitly; always use it
|
||||
m = _NAME_EQ_RE.match(entry)
|
||||
if m:
|
||||
return m.group(1).strip()
|
||||
# URL scheme: extract name from path
|
||||
if _URL_SCHEME_RE.match(entry):
|
||||
parsed = urlparse(entry)
|
||||
if parsed.netloc in ('github.com', 'gitlab.com', 'bitbucket.com'):
|
||||
parts = [p for p in parsed.path.split('/') if p]
|
||||
if len(parts) >= 2:
|
||||
name = parts[1]
|
||||
else:
|
||||
name = Path(parsed.path.rstrip('/')).name.strip()
|
||||
if name.endswith('.git'):
|
||||
name = name[:-4]
|
||||
return name or None
|
||||
# SSH git URL: git@github.com:user/repo.git#tag → repo
|
||||
if _SSH_URL_RE.match(entry):
|
||||
path_part = entry.split(':', 1)[1].split('#')[0].rstrip('/')
|
||||
name = Path(path_part).name
|
||||
return (name[:-4] if name.endswith('.git') else name) or None
|
||||
# Versioned registry: "owner/Name @ version" → Name
|
||||
if '@' in entry:
|
||||
name_part = entry.split('@')[0].strip()
|
||||
return name_part.split('/')[-1].strip() if '/' in name_part else name_part
|
||||
# Plain registry: "owner/Name" → Name
|
||||
if re.match(r'^[^/\s]+/[^/\s]+$', entry):
|
||||
return entry.split('/')[-1].strip()
|
||||
return None
|
||||
|
||||
|
||||
def is_wled_module(dep: LibBuilderBase) -> bool:
|
||||
"""Returns true if the specified library is a wled module."""
|
||||
return (
|
||||
usermod_dir in Path(dep.src_dir).parents
|
||||
or str(dep.name).startswith("wled-")
|
||||
or dep.name in _custom_usermod_names
|
||||
)
|
||||
|
||||
|
||||
## Script starts here — parse custom_usermods
|
||||
raw_usermods = env.GetProjectOption("custom_usermods", "")
|
||||
usermods_libdeps: list[str] = []
|
||||
|
||||
for line in raw_usermods.splitlines():
|
||||
line = line.strip()
|
||||
if not line or line.startswith('#') or line.startswith(';'):
|
||||
continue
|
||||
|
||||
if _is_external_entry(line):
|
||||
# External URL or registry entry: pass through to lib_deps unchanged.
|
||||
predicted = _predict_dep_name(line)
|
||||
if predicted:
|
||||
_custom_usermod_names.add(predicted)
|
||||
else:
|
||||
secho(
|
||||
f"WARNING: Cannot determine library name for custom_usermods entry "
|
||||
f"{line!r}. If it is not recognised as a WLED module at build time, "
|
||||
f"ensure its library.json 'name' matches the repo name.",
|
||||
fg="yellow", err=True)
|
||||
usermods_libdeps.append(line)
|
||||
else:
|
||||
# Bare name(s): split on whitespace for backwards compatibility.
|
||||
for token in line.split():
|
||||
if token == '*':
|
||||
for mod_path in sorted(usermod_dir.iterdir()):
|
||||
if mod_path.is_dir() and (mod_path / 'library.json').exists():
|
||||
_custom_usermod_names.add(mod_path.name)
|
||||
usermods_libdeps.append(f"symlink://{mod_path.resolve()}")
|
||||
else:
|
||||
resolved = find_usermod(token)
|
||||
_custom_usermod_names.add(resolved.name)
|
||||
usermods_libdeps.append(f"symlink://{resolved.resolve()}")
|
||||
|
||||
if usermods_libdeps:
|
||||
env.GetProjectConfig().set("env:" + env['PIOENV'], 'lib_deps', env.GetProjectOption('lib_deps') + usermods_libdeps)
|
||||
|
||||
# Utility function for assembling usermod include paths
|
||||
def cached_add_includes(dep, dep_cache: set, includes: deque):
|
||||
""" Add dep's include paths to includes if it's not in the cache """
|
||||
if dep not in dep_cache:
|
||||
dep_cache.add(dep)
|
||||
for include in dep.get_include_dirs():
|
||||
if include not in includes:
|
||||
includes.appendleft(include)
|
||||
if usermod_dir not in Path(dep.src_dir).parents:
|
||||
# Recurse, but only for NON-usermods
|
||||
for subdep in dep.depbuilders:
|
||||
cached_add_includes(subdep, dep_cache, includes)
|
||||
|
||||
# Monkey-patch ConfigureProjectLibBuilder to mark up the dependencies
|
||||
# Save the old value
|
||||
old_ConfigureProjectLibBuilder = env.ConfigureProjectLibBuilder
|
||||
|
||||
# Our new wrapper
|
||||
def wrapped_ConfigureProjectLibBuilder(xenv):
|
||||
# Call the wrapped function
|
||||
result = old_ConfigureProjectLibBuilder.clone(xenv)()
|
||||
|
||||
# Fix up include paths
|
||||
# In PlatformIO >=6.1.17, this could be done prior to ConfigureProjectLibBuilder
|
||||
wled_dir = xenv["PROJECT_SRC_DIR"]
|
||||
# Build a list of dependency include dirs
|
||||
# TODO: Find out if this is the order that PlatformIO/SCons puts them in??
|
||||
processed_deps = set()
|
||||
extra_include_dirs = deque() # Deque used for fast prepend
|
||||
for dep in result.depbuilders:
|
||||
cached_add_includes(dep, processed_deps, extra_include_dirs)
|
||||
|
||||
wled_deps = [dep for dep in result.depbuilders if is_wled_module(dep)]
|
||||
|
||||
broken_usermods = []
|
||||
for dep in wled_deps:
|
||||
# Add the wled folder to the include path
|
||||
dep.env.PrependUnique(CPPPATH=str(wled_dir))
|
||||
# Add WLED's own dependencies
|
||||
for dir in extra_include_dirs:
|
||||
dep.env.PrependUnique(CPPPATH=str(dir))
|
||||
# Ensure debug info is emitted for this module's source files.
|
||||
# validate_modules.py uses `nm --defined-only -l` on the final ELF to check
|
||||
# that each module has at least one symbol placed in the binary. The -l flag
|
||||
# reads DWARF debug sections to map placed symbols back to their original source
|
||||
# files; without -g those sections are absent and the check cannot attribute any
|
||||
# symbol to a specific module. We scope this to usermods only — the main WLED
|
||||
# build and other libraries are unaffected.
|
||||
dep.env.AppendUnique(CCFLAGS=["-g"])
|
||||
# Enforce that libArchive is not set; we must link them directly to the executable
|
||||
if dep.lib_archive:
|
||||
broken_usermods.append(dep)
|
||||
|
||||
if broken_usermods:
|
||||
broken_usermods = [usermod.name for usermod in broken_usermods]
|
||||
secho(
|
||||
f"ERROR: libArchive=false is missing on usermod(s) {' '.join(broken_usermods)} -- "
|
||||
f"modules will not compile in correctly. Add '\"build\": {{\"libArchive\": false}}' "
|
||||
f"to their library.json.",
|
||||
fg="red", err=True)
|
||||
Exit(1)
|
||||
|
||||
# Save the depbuilders list for later validation
|
||||
xenv.Replace(WLED_MODULES=wled_deps)
|
||||
|
||||
return result
|
||||
|
||||
# Apply the wrapper
|
||||
env.AddMethod(wrapped_ConfigureProjectLibBuilder, "ConfigureProjectLibBuilder")
|
||||
24
pio-scripts/obj-dump.py
Normal file
24
pio-scripts/obj-dump.py
Normal file
@@ -0,0 +1,24 @@
|
||||
# Little convenience script to get an object dump
|
||||
# You may add "-S" to the objdump commandline (i.e. replace "-D -C " with "-d -S -C ")
|
||||
# to get source code intermixed with disassembly (SLOW !)
|
||||
|
||||
Import('env')
|
||||
|
||||
def obj_dump_after_elf(source, target, env):
|
||||
platform = env.PioPlatform()
|
||||
board = env.BoardConfig()
|
||||
mcu = board.get("build.mcu", "esp32")
|
||||
|
||||
print("Create firmware.asm")
|
||||
if mcu == "esp8266":
|
||||
env.Execute("xtensa-lx106-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm")
|
||||
if mcu == "esp32":
|
||||
env.Execute("xtensa-esp32-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm")
|
||||
if mcu == "esp32s2":
|
||||
env.Execute("xtensa-esp32s2-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm")
|
||||
if mcu == "esp32s3":
|
||||
env.Execute("xtensa-esp32s3-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm")
|
||||
if mcu == "esp32c3":
|
||||
env.Execute("riscv32-esp-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm")
|
||||
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.elf", [obj_dump_after_elf])
|
||||
68
pio-scripts/output_bins.py
Normal file
68
pio-scripts/output_bins.py
Normal file
@@ -0,0 +1,68 @@
|
||||
Import('env')
|
||||
import os
|
||||
import shutil
|
||||
import gzip
|
||||
import json
|
||||
|
||||
OUTPUT_DIR = "build_output{}".format(os.path.sep)
|
||||
#OUTPUT_DIR = os.path.join("build_output")
|
||||
|
||||
def _get_cpp_define_value(env, define):
|
||||
define_list = [item[-1] for item in env["CPPDEFINES"] if item[0] == define]
|
||||
|
||||
if define_list:
|
||||
return define_list[0]
|
||||
|
||||
return None
|
||||
|
||||
def _create_dirs(dirs=["map", "release", "firmware"]):
|
||||
for d in dirs:
|
||||
os.makedirs(os.path.join(OUTPUT_DIR, d), exist_ok=True)
|
||||
|
||||
def create_release(source):
|
||||
release_name_def = _get_cpp_define_value(env, "WLED_RELEASE_NAME")
|
||||
if release_name_def:
|
||||
release_name = release_name_def.replace("\\\"", "")
|
||||
with open("package.json", "r") as package:
|
||||
version = json.load(package)["version"]
|
||||
release_file = os.path.join(OUTPUT_DIR, "release", f"WLED_{version}_{release_name}.bin")
|
||||
release_gz_file = release_file + ".gz"
|
||||
print(f"Copying {source} to {release_file}")
|
||||
shutil.copy(source, release_file)
|
||||
bin_gzip(release_file, release_gz_file)
|
||||
else:
|
||||
variant = env["PIOENV"]
|
||||
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
|
||||
print(f"Copying {source} to {bin_file}")
|
||||
shutil.copy(source, bin_file)
|
||||
|
||||
def bin_rename_copy(source, target, env):
|
||||
_create_dirs()
|
||||
variant = env["PIOENV"]
|
||||
builddir = os.path.join(env["PROJECT_BUILD_DIR"], variant)
|
||||
source_map = os.path.join(builddir, env["PROGNAME"] + ".map")
|
||||
|
||||
# create string with location and file names based on variant
|
||||
map_file = "{}map{}{}.map".format(OUTPUT_DIR, os.path.sep, variant)
|
||||
|
||||
create_release(str(target[0]))
|
||||
|
||||
# copy firmware.map to map/<variant>.map
|
||||
if os.path.isfile("firmware.map"):
|
||||
print("Found linker mapfile firmware.map")
|
||||
shutil.copy("firmware.map", map_file)
|
||||
if os.path.isfile(source_map):
|
||||
print(f"Found linker mapfile {source_map}")
|
||||
shutil.copy(source_map, map_file)
|
||||
|
||||
def bin_gzip(source, target):
|
||||
# only create gzip for esp8266
|
||||
if not env["PIOPLATFORM"] == "espressif8266":
|
||||
return
|
||||
|
||||
print(f"Creating gzip file {target} from {source}")
|
||||
with open(source,"rb") as fp:
|
||||
with gzip.open(target, "wb", compresslevel = 9) as f:
|
||||
shutil.copyfileobj(fp, f)
|
||||
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", bin_rename_copy)
|
||||
116
pio-scripts/set_metadata.py
Normal file
116
pio-scripts/set_metadata.py
Normal file
@@ -0,0 +1,116 @@
|
||||
Import('env')
|
||||
import subprocess
|
||||
import json
|
||||
import re
|
||||
|
||||
def get_github_repo():
|
||||
"""Extract GitHub repository name from git remote URL.
|
||||
|
||||
Uses the remote that the current branch tracks, falling back to 'origin'.
|
||||
This handles cases where repositories have multiple remotes or where the
|
||||
main remote is not named 'origin'.
|
||||
|
||||
Returns:
|
||||
str: Repository name in 'owner/repo' format for GitHub repos,
|
||||
'unknown' for non-GitHub repos, missing git CLI, or any errors.
|
||||
"""
|
||||
try:
|
||||
remote_name = 'origin' # Default fallback
|
||||
|
||||
# Try to get the remote for the current branch
|
||||
try:
|
||||
# Get current branch name
|
||||
branch_result = subprocess.run(['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
|
||||
capture_output=True, text=True, check=True)
|
||||
current_branch = branch_result.stdout.strip()
|
||||
|
||||
# Get the remote for the current branch
|
||||
remote_result = subprocess.run(['git', 'config', f'branch.{current_branch}.remote'],
|
||||
capture_output=True, text=True, check=True)
|
||||
tracked_remote = remote_result.stdout.strip()
|
||||
|
||||
# Use the tracked remote if we found one
|
||||
if tracked_remote:
|
||||
remote_name = tracked_remote
|
||||
except subprocess.CalledProcessError:
|
||||
# If branch config lookup fails, continue with 'origin' as fallback
|
||||
pass
|
||||
|
||||
# Get the remote URL for the determined remote
|
||||
result = subprocess.run(['git', 'remote', 'get-url', remote_name],
|
||||
capture_output=True, text=True, check=True)
|
||||
remote_url = result.stdout.strip()
|
||||
|
||||
# Check if it's a GitHub URL
|
||||
if 'github.com' not in remote_url.lower():
|
||||
return None
|
||||
|
||||
# Parse GitHub URL patterns:
|
||||
# https://github.com/owner/repo.git
|
||||
# git@github.com:owner/repo.git
|
||||
# https://github.com/owner/repo
|
||||
|
||||
# Remove .git suffix if present
|
||||
if remote_url.endswith('.git'):
|
||||
remote_url = remote_url[:-4]
|
||||
|
||||
# Handle HTTPS URLs
|
||||
https_match = re.search(r'github\.com/([^/]+/[^/]+)', remote_url, re.IGNORECASE)
|
||||
if https_match:
|
||||
return https_match.group(1)
|
||||
|
||||
# Handle SSH URLs
|
||||
ssh_match = re.search(r'github\.com:([^/]+/[^/]+)', remote_url, re.IGNORECASE)
|
||||
if ssh_match:
|
||||
return ssh_match.group(1)
|
||||
|
||||
return None
|
||||
|
||||
except FileNotFoundError:
|
||||
# Git CLI is not installed or not in PATH
|
||||
return None
|
||||
except subprocess.CalledProcessError:
|
||||
# Git command failed (e.g., not a git repo, no remote, etc.)
|
||||
return None
|
||||
except Exception:
|
||||
# Any other unexpected error
|
||||
return None
|
||||
|
||||
# WLED version is managed by package.json; this is picked up in several places
|
||||
# - It's integrated in to the UI code
|
||||
# - Here, for wled_metadata.cpp
|
||||
# - The output_bins script
|
||||
# We always take it from package.json to ensure consistency
|
||||
with open("package.json", "r") as package:
|
||||
WLED_VERSION = json.load(package)["version"]
|
||||
|
||||
def has_def(cppdefs, name):
|
||||
""" Returns true if a given name is set in a CPPDEFINES collection """
|
||||
for f in cppdefs:
|
||||
if isinstance(f, tuple):
|
||||
f = f[0]
|
||||
if f == name:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def add_wled_metadata_flags(env, node):
|
||||
cdefs = env["CPPDEFINES"].copy()
|
||||
|
||||
if not has_def(cdefs, "WLED_REPO"):
|
||||
repo = get_github_repo()
|
||||
if repo:
|
||||
cdefs.append(("WLED_REPO", f"\\\"{repo}\\\""))
|
||||
|
||||
cdefs.append(("WLED_VERSION", WLED_VERSION))
|
||||
|
||||
# This transforms the node in to a Builder; it cannot be modified again
|
||||
return env.Object(
|
||||
node,
|
||||
CPPDEFINES=cdefs
|
||||
)
|
||||
|
||||
env.AddBuildMiddleware(
|
||||
add_wled_metadata_flags,
|
||||
"*/wled_metadata.cpp"
|
||||
)
|
||||
15
pio-scripts/strip-floats.py
Normal file
15
pio-scripts/strip-floats.py
Normal file
@@ -0,0 +1,15 @@
|
||||
Import('env')
|
||||
|
||||
#
|
||||
# Dump build environment (for debug)
|
||||
#print env.Dump()
|
||||
#
|
||||
|
||||
flags = " ".join(env['LINKFLAGS'])
|
||||
flags = flags.replace("-u _printf_float", "")
|
||||
flags = flags.replace("-u _scanf_float", "")
|
||||
newflags = flags.split()
|
||||
|
||||
env.Replace(
|
||||
LINKFLAGS=newflags
|
||||
)
|
||||
9
pio-scripts/user_config_copy.py
Normal file
9
pio-scripts/user_config_copy.py
Normal file
@@ -0,0 +1,9 @@
|
||||
Import('env')
|
||||
import os
|
||||
import shutil
|
||||
|
||||
# copy WLED00/my_config_sample.h to WLED00/my_config.h
|
||||
if os.path.isfile("wled00/my_config.h"):
|
||||
print ("*** use existing my_config.h ***")
|
||||
else:
|
||||
shutil.copy("wled00/my_config_sample.h", "wled00/my_config.h")
|
||||
177
pio-scripts/validate_modules.py
Normal file
177
pio-scripts/validate_modules.py
Normal file
@@ -0,0 +1,177 @@
|
||||
import re
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from click import secho
|
||||
from SCons.Script import Action, Exit
|
||||
Import("env")
|
||||
|
||||
_ATTR = re.compile(r'\bDW_AT_(name|comp_dir)\b')
|
||||
|
||||
|
||||
def read_lines(p: Path):
|
||||
""" Read in the contents of a file for analysis """
|
||||
with p.open("r", encoding="utf-8", errors="ignore") as f:
|
||||
return f.readlines()
|
||||
|
||||
|
||||
def _get_readelf_path(env) -> str:
|
||||
""" Derive the readelf tool path from the build environment """
|
||||
# Derive from the C compiler: xtensa-esp32-elf-gcc → xtensa-esp32-elf-readelf
|
||||
cc = Path(env.subst("$CC"))
|
||||
return str(cc.with_name(re.sub(r'(gcc|g\+\+)$', 'readelf', cc.name)))
|
||||
|
||||
|
||||
def check_elf_modules(elf_path: Path, env, module_lib_builders) -> set[str]:
|
||||
""" Check which modules have at least one compilation unit in the ELF.
|
||||
|
||||
The map file is not a reliable source for this: with LTO, original object
|
||||
file paths are replaced by temporary ltrans.o partitions in all output
|
||||
sections, making per-module attribution impossible from the map alone.
|
||||
Instead we invoke readelf --debug-dump=info --dwarf-depth=1 on the ELF,
|
||||
which reads only the top-level compilation-unit DIEs from .debug_info.
|
||||
Each CU corresponds to one source file; matching DW_AT_comp_dir +
|
||||
DW_AT_name against the module src_dirs is sufficient to confirm a module
|
||||
was compiled into the ELF. The output volume is proportional to the
|
||||
number of source files, not the number of symbols.
|
||||
|
||||
Returns the set of build_dir basenames for confirmed modules.
|
||||
"""
|
||||
readelf_path = _get_readelf_path(env)
|
||||
try:
|
||||
result = subprocess.run(
|
||||
[readelf_path, "--debug-dump=info", "--dwarf-depth=1", str(elf_path)],
|
||||
capture_output=True, text=True, errors="ignore", timeout=120,
|
||||
)
|
||||
output = result.stdout
|
||||
if result.returncode != 0 or result.stderr.strip():
|
||||
secho(f"WARNING: readelf exited {result.returncode}: {result.stderr.strip()}", fg="yellow", err=True)
|
||||
except (subprocess.TimeoutExpired, FileNotFoundError, OSError) as e:
|
||||
secho(f"WARNING: readelf failed ({e}); skipping per-module validation", fg="yellow", err=True)
|
||||
return {Path(b.build_dir).name for b in module_lib_builders} # conservative pass
|
||||
|
||||
remaining = {Path(str(b.src_dir)): Path(b.build_dir).name for b in module_lib_builders}
|
||||
found = set()
|
||||
project_dir = Path(env.subst("$PROJECT_DIR"))
|
||||
|
||||
def _flush_cu(comp_dir: str | None, name: str | None) -> None:
|
||||
"""Match one completed CU against remaining builders."""
|
||||
if not name or not remaining:
|
||||
return
|
||||
p = Path(name)
|
||||
src_path = (Path(comp_dir) / p) if (comp_dir and not p.is_absolute()) else p
|
||||
# In arduino+espidf dual-framework builds the IDF toolchain sets DW_AT_comp_dir
|
||||
# to the virtual path "/IDF_PROJECT" rather than the real project root, so
|
||||
# src_path won't match. Pre-compute a fallback using $PROJECT_DIR and check
|
||||
# both candidates in a single pass.
|
||||
use_fallback = not p.is_absolute() and comp_dir and Path(comp_dir) != project_dir
|
||||
src_path_real = project_dir / p if use_fallback else None
|
||||
for src_dir in list(remaining):
|
||||
if src_path.is_relative_to(src_dir) or (src_path_real and src_path_real.is_relative_to(src_dir)):
|
||||
found.add(remaining.pop(src_dir))
|
||||
return
|
||||
|
||||
# readelf emits one DW_TAG_compile_unit DIE per source file. Attributes
|
||||
# of interest:
|
||||
# DW_AT_name — source file (absolute, or relative to comp_dir)
|
||||
# DW_AT_comp_dir — compile working directory
|
||||
# Both appear as either a direct string or an indirect string:
|
||||
# DW_AT_name : foo.cpp
|
||||
# DW_AT_name : (indirect string, offset: 0x…): foo.cpp
|
||||
# Taking the portion after the *last* ": " on the line handles both forms.
|
||||
|
||||
comp_dir = name = None
|
||||
for line in output.splitlines():
|
||||
if 'Compilation Unit @' in line:
|
||||
_flush_cu(comp_dir, name)
|
||||
comp_dir = name = None
|
||||
continue
|
||||
if not remaining:
|
||||
break # all builders matched
|
||||
m = _ATTR.search(line)
|
||||
if m:
|
||||
_, _, val = line.rpartition(': ')
|
||||
val = val.strip()
|
||||
if m.group(1) == 'name':
|
||||
name = val
|
||||
else:
|
||||
comp_dir = val
|
||||
_flush_cu(comp_dir, name) # flush the last CU
|
||||
|
||||
return found
|
||||
|
||||
|
||||
def count_usermod_objects(map_file: list[str]) -> int:
|
||||
""" Returns the number of usermod objects in the usermod list.
|
||||
|
||||
Computes the count from the address span between the .dynarray.usermods.0
|
||||
and .dynarray.usermods.99999 sentinel sections. This mirrors the
|
||||
DYNARRAY_LENGTH macro and is reliable under LTO, where all entries are
|
||||
merged into a single ltrans partition so counting section occurrences
|
||||
always yields 1 regardless of the true count.
|
||||
"""
|
||||
ENTRY_SIZE = 4 # sizeof(Usermod*) on 32-bit targets
|
||||
addr_begin = None
|
||||
addr_end = None
|
||||
|
||||
for i, line in enumerate(map_file):
|
||||
stripped = line.strip()
|
||||
if stripped == '.dynarray.usermods.0':
|
||||
if i + 1 < len(map_file):
|
||||
m = re.search(r'0x([0-9a-fA-F]+)', map_file[i + 1])
|
||||
if m:
|
||||
addr_begin = int(m.group(1), 16)
|
||||
elif stripped == '.dynarray.usermods.99999':
|
||||
if i + 1 < len(map_file):
|
||||
m = re.search(r'0x([0-9a-fA-F]+)', map_file[i + 1])
|
||||
if m:
|
||||
addr_end = int(m.group(1), 16)
|
||||
if addr_begin is not None and addr_end is not None:
|
||||
break
|
||||
|
||||
if addr_begin is None or addr_end is None:
|
||||
return 0
|
||||
return (addr_end - addr_begin) // ENTRY_SIZE
|
||||
|
||||
|
||||
def validate_map_file(source, target, env):
|
||||
""" Validate that all modules appear in the output build """
|
||||
build_dir = Path(env.subst("$BUILD_DIR"))
|
||||
map_file_path = build_dir / env.subst("${PROGNAME}.map")
|
||||
|
||||
if not map_file_path.exists():
|
||||
secho(f"ERROR: Map file not found: {map_file_path}", fg="red", err=True)
|
||||
Exit(1)
|
||||
|
||||
# Identify the WLED module builders, set by load_usermods.py
|
||||
module_lib_builders = env.get('WLED_MODULES')
|
||||
if module_lib_builders is None:
|
||||
secho("ERROR: WLED_MODULES not set — ensure load_usermods.py is run as a pre: script", fg="red", err=True)
|
||||
Exit(1)
|
||||
|
||||
# Extract the values we care about
|
||||
modules = {Path(builder.build_dir).name: builder.name for builder in module_lib_builders}
|
||||
secho(f"INFO: {len(modules)} libraries included as WLED optional/user modules")
|
||||
|
||||
# Now parse the map file
|
||||
map_file_contents = read_lines(map_file_path)
|
||||
usermod_object_count = count_usermod_objects(map_file_contents)
|
||||
secho(f"INFO: {usermod_object_count} usermod object entries found")
|
||||
|
||||
elf_path = build_dir / env.subst("${PROGNAME}.elf")
|
||||
|
||||
confirmed_modules = check_elf_modules(elf_path, env, module_lib_builders)
|
||||
if confirmed_modules:
|
||||
secho(f"INFO: Code from usermod libraries found in binary: {', '.join(confirmed_modules)}")
|
||||
# else - if there's no usermods found, don't generate a message. If we're legitimately missing all entries, the error report on the
|
||||
# next line will trip; and if the usermod set is expected to be empty, then there's no need for yet another null message.
|
||||
|
||||
missing_modules = [modname for mdir, modname in modules.items() if mdir not in confirmed_modules]
|
||||
if missing_modules:
|
||||
secho(
|
||||
f"ERROR: No symbols from {missing_modules} found in linked output!",
|
||||
fg="red",
|
||||
err=True)
|
||||
Exit(1)
|
||||
|
||||
env.Append(LINKFLAGS=[env.subst("-Wl,--Map=${BUILD_DIR}/${PROGNAME}.map")])
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.elf", Action(validate_map_file, cmdstr='Checking linked optional modules (usermods) in map file'))
|
||||
765
platformio.ini
Normal file
765
platformio.ini
Normal file
@@ -0,0 +1,765 @@
|
||||
; PlatformIO Project Configuration File
|
||||
; Please visit documentation: https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[platformio]
|
||||
# ------------------------------------------------------------------------------
|
||||
# ENVIRONMENTS
|
||||
#
|
||||
# Please uncomment one of the lines below to select your board(s)
|
||||
# (use `platformio_override.ini` when building for your own board; see `platformio_override.ini.sample` for an example)
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# CI/release binaries
|
||||
default_envs = nodemcuv2
|
||||
esp8266_2m
|
||||
esp01_1m_full
|
||||
nodemcuv2_160
|
||||
esp8266_2m_160
|
||||
esp01_1m_full_160
|
||||
nodemcuv2_compat
|
||||
esp8266_2m_compat
|
||||
esp01_1m_full_compat
|
||||
esp32dev
|
||||
esp32dev_debug
|
||||
esp32_eth
|
||||
esp32_wrover
|
||||
lolin_s2_mini
|
||||
esp32c3dev
|
||||
esp32c3dev_qio
|
||||
esp32S3_wroom2
|
||||
esp32s3dev_16MB_opi
|
||||
esp32s3dev_8MB_opi
|
||||
esp32s3dev_8MB_qspi
|
||||
esp32s3_4M_qspi
|
||||
usermods
|
||||
|
||||
src_dir = ./wled00
|
||||
data_dir = ./wled00/data
|
||||
build_cache_dir = ~/.buildcache
|
||||
extra_configs =
|
||||
platformio_override.ini
|
||||
platformio_release.ini
|
||||
|
||||
[common]
|
||||
# ------------------------------------------------------------------------------
|
||||
# FLAGS: DEBUG
|
||||
# esp8266 : see https://docs.platformio.org/en/latest/platforms/espressif8266.html#debug-level
|
||||
# esp32 : see https://docs.platformio.org/en/latest/platforms/espressif32.html#debug-level
|
||||
# ------------------------------------------------------------------------------
|
||||
debug_flags = -D DEBUG=1 -D WLED_DEBUG
|
||||
-DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_TLS_MEM ;; for esp8266
|
||||
# if needed (for memleaks etc) also add; -DDEBUG_ESP_OOM -include "umm_malloc/umm_malloc_cfg.h"
|
||||
# -DDEBUG_ESP_CORE is not working right now
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# FLAGS: ldscript (available ldscripts at https://github.com/esp8266/Arduino/tree/master/tools/sdk/ld)
|
||||
# ldscript_2m1m (2048 KB) = 1019 KB sketch, 4 KB eeprom, 1004 KB spiffs, 16 KB reserved
|
||||
# ldscript_4m1m (4096 KB) = 1019 KB sketch, 4 KB eeprom, 1002 KB spiffs, 16 KB reserved, 2048 KB empty/ota?
|
||||
#
|
||||
# Available lwIP variants (macros):
|
||||
# -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH = v1.4 Higher Bandwidth (default)
|
||||
# -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY = v2 Lower Memory
|
||||
# -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH = v2 Higher Bandwidth
|
||||
# -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
|
||||
#
|
||||
# BearSSL performance:
|
||||
# When building with -DSECURE_CLIENT=SECURE_CLIENT_BEARSSL, please add `board_build.f_cpu = 160000000` to the environment configuration
|
||||
#
|
||||
# BearSSL ciphers:
|
||||
# When building on core >= 2.5, you can add the build flag -DBEARSSL_SSL_BASIC in order to build BearSSL with a limited set of ciphers:
|
||||
# TLS_RSA_WITH_AES_128_CBC_SHA256 / AES128-SHA256
|
||||
# TLS_RSA_WITH_AES_256_CBC_SHA256 / AES256-SHA256
|
||||
# TLS_RSA_WITH_AES_128_CBC_SHA / AES128-SHA
|
||||
# TLS_RSA_WITH_AES_256_CBC_SHA / AES256-SHA
|
||||
# This reduces the OTA size with ~45KB, so it's especially useful on low memory boards (512k/1m).
|
||||
# ------------------------------------------------------------------------------
|
||||
build_flags =
|
||||
-DMQTT_MAX_PACKET_SIZE=1024
|
||||
-DSECURE_CLIENT=SECURE_CLIENT_BEARSSL
|
||||
-DBEARSSL_SSL_BASIC
|
||||
-D CORE_DEBUG_LEVEL=0
|
||||
-D NDEBUG
|
||||
-Wno-attributes ;; silence warnings about unknown attribute 'maybe_unused' in NeoPixelBus
|
||||
#build_flags for the IRremoteESP8266 library (enabled decoders have to appear here)
|
||||
-D _IR_ENABLE_DEFAULT_=false
|
||||
-D DECODE_HASH=true
|
||||
-D DECODE_NEC=true
|
||||
-D DECODE_SONY=true
|
||||
-D DECODE_SAMSUNG=true
|
||||
-D DECODE_LG=true
|
||||
-DWLED_USE_MY_CONFIG
|
||||
-D WLED_PS_DONT_REPLACE_FX ; PS replacement FX are purely a flash memory saving feature, do not replace classic FX until we run out of flash
|
||||
|
||||
build_unflags =
|
||||
|
||||
ldscript_1m128k = eagle.flash.1m128.ld
|
||||
ldscript_2m512k = eagle.flash.2m512.ld
|
||||
ldscript_2m1m = eagle.flash.2m1m.ld
|
||||
ldscript_4m1m = eagle.flash.4m1m.ld
|
||||
|
||||
[scripts_defaults]
|
||||
extra_scripts =
|
||||
pre:pio-scripts/set_metadata.py
|
||||
post:pio-scripts/output_bins.py
|
||||
post:pio-scripts/strip-floats.py
|
||||
post:pio-scripts/dynarray.py
|
||||
pre:pio-scripts/user_config_copy.py
|
||||
pre:pio-scripts/load_usermods.py
|
||||
pre:pio-scripts/build_ui.py
|
||||
post:pio-scripts/validate_modules.py ;; double-check the build output usermods
|
||||
; post:pio-scripts/obj-dump.py ;; convenience script to create a disassembly dump of the firmware (hardcore debugging)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# COMMON SETTINGS:
|
||||
# ------------------------------------------------------------------------------
|
||||
[env]
|
||||
framework = arduino
|
||||
board_build.flash_mode = dout
|
||||
monitor_speed = 115200
|
||||
# slow upload speed but most compatible (use platformio_override.ini to use faster speed)
|
||||
upload_speed = 115200
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# LIBRARIES: required dependencies
|
||||
# Please note that we don't always use the latest version of a library.
|
||||
#
|
||||
# The following libraries have been included (and some of them changed) in the source:
|
||||
# ArduinoJson@5.13.5, E131@1.0.0(changed), Time@1.5, Timezone@1.2.1
|
||||
# ------------------------------------------------------------------------------
|
||||
lib_compat_mode = strict
|
||||
lib_deps =
|
||||
IRremoteESP8266 @ 2.8.2
|
||||
https://github.com/Makuna/NeoPixelBus.git#a0919d1c10696614625978dd6fb750a1317a14ce
|
||||
https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.4.2
|
||||
marvinroger/AsyncMqttClient @ 0.9.0
|
||||
# for I2C interface
|
||||
;Wire
|
||||
# ESP-NOW library
|
||||
;gmag11/QuickESPNow @ ~0.7.0
|
||||
https://github.com/blazoncek/QuickESPNow.git#optional-debug
|
||||
#For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line
|
||||
#TFT_eSPI
|
||||
#For compatible OLED display uncomment following
|
||||
#olikraus/U8g2 #@ ~2.33.15
|
||||
#For Dallas sensor uncomment following
|
||||
#paulstoffregen/OneWire @ ~2.3.8
|
||||
#For BME280 sensor uncomment following
|
||||
#BME280 @ ~3.0.0
|
||||
;adafruit/Adafruit BMP280 Library @ 2.1.0
|
||||
;adafruit/Adafruit CCS811 Library @ 1.0.4
|
||||
;adafruit/Adafruit Si7021 Library @ 1.4.0
|
||||
#For MAX1704x Lipo Monitor / Fuel Gauge uncomment following
|
||||
; https://github.com/adafruit/Adafruit_BusIO @ 1.14.5
|
||||
; https://github.com/adafruit/Adafruit_MAX1704X @ 1.0.2
|
||||
#For MPU6050 IMU uncomment follwoing
|
||||
;electroniccats/MPU6050 @1.0.1
|
||||
# SHT85
|
||||
;robtillaart/SHT85@~0.3.3
|
||||
|
||||
extra_scripts = ${scripts_defaults.extra_scripts}
|
||||
|
||||
[esp8266]
|
||||
# ------------------------------------------------------------------------------
|
||||
# PLATFORM:
|
||||
# !! DO NOT confuse platformio's ESP8266 development platform with Arduino core for ESP8266
|
||||
#
|
||||
# arduino core 2.6.3 = platformIO 2.3.2
|
||||
# arduino core 2.7.0 = platformIO 2.5.0
|
||||
# ------------------------------------------------------------------------------
|
||||
arduino_core_2_6_3 = espressif8266@2.3.3
|
||||
arduino_core_2_7_4 = espressif8266@2.6.2
|
||||
arduino_core_3_0_0 = espressif8266@3.0.0
|
||||
arduino_core_3_0_2 = espressif8266@3.2.0
|
||||
arduino_core_3_1_0 = espressif8266@4.1.0
|
||||
arduino_core_3_1_2 = espressif8266@4.2.1
|
||||
|
||||
# Development platforms
|
||||
arduino_core_develop = https://github.com/platformio/platform-espressif8266#develop
|
||||
arduino_core_git = https://github.com/platformio/platform-espressif8266#feature/stage
|
||||
|
||||
# Platform to use for ESP8266
|
||||
platform_wled_default = ${esp8266.arduino_core_3_1_2}
|
||||
# We use 2.7.4.7 for all, includes PWM flicker fix and Wstring optimization
|
||||
#platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7
|
||||
platform_packages = platformio/toolchain-xtensa @ ~2.100300.220621 #2.40802.200502
|
||||
platformio/tool-esptool #@ ~1.413.0
|
||||
platformio/tool-esptoolpy #@ ~1.30000.0
|
||||
|
||||
## previous platform for 8266, in case of problems with the new one
|
||||
## you'll need makuna/NeoPixelBus@ 2.6.9 for arduino_core_3_0_2, which does not support Ucs890x
|
||||
;; platform_wled_default = ${esp8266.arduino_core_3_0_2}
|
||||
;; platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7
|
||||
;; platformio/toolchain-xtensa @ ~2.40802.200502
|
||||
;; platformio/tool-esptool @ ~1.413.0
|
||||
;; platformio/tool-esptoolpy @ ~1.30000.0
|
||||
|
||||
platform = ${esp8266.platform_wled_default}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags =
|
||||
-DESP8266
|
||||
-DFP_IN_IROM
|
||||
;-Wno-deprecated-declarations
|
||||
;-Wno-register ;; leaves some warnings when compiling C files: command-line option '-Wno-register' is valid for C++/ObjC++ but not for C
|
||||
;-Dregister= # remove warnings in C++17 due to use of deprecated register keyword by the FastLED library ;; warning: this can be dangerous
|
||||
-Wno-misleading-indentation
|
||||
; NONOSDK22x_190703 = 2.2.2-dev(38a443e)
|
||||
-DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703
|
||||
; lwIP 2 - Higher Bandwidth no Features
|
||||
; -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
|
||||
; lwIP 1.4 - Higher Bandwidth (Aircoookie has)
|
||||
-DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH
|
||||
; VTABLES in Flash
|
||||
-DVTABLES_IN_FLASH
|
||||
; restrict to minimal mime-types
|
||||
-DMIMETYPE_MINIMAL
|
||||
; other special-purpose framework flags (see https://docs.platformio.org/en/latest/platforms/espressif8266.html)
|
||||
; decrease code cache size and increase IRAM to fit all pixel functions
|
||||
-D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48 ;; in case of linker errors like "section `.text1' will not fit in region `iram1_0_seg'"
|
||||
; -D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48_SECHEAP_SHARED ;; (experimental) adds some extra heap, but may cause slowdown
|
||||
-D NON32XFER_HANDLER ;; ask forgiveness for PROGMEM misuse
|
||||
|
||||
lib_deps =
|
||||
#https://github.com/lorol/LITTLEFS.git
|
||||
ESPAsyncTCP @ 1.2.2
|
||||
ESPAsyncUDP
|
||||
ESP8266PWM
|
||||
https://github.com/tignioj/ArduinoUZlib.git#20aff95cd80c141f80bdbf66895409a0046d2c2f
|
||||
${env.lib_deps}
|
||||
|
||||
monitor_filters = esp8266_exception_decoder
|
||||
|
||||
;; compatibilty flags - same as 0.14.0 which seems to work better on some 8266 boards. Not using PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48
|
||||
build_flags_compat =
|
||||
-DESP8266
|
||||
-DFP_IN_IROM
|
||||
;;-Wno-deprecated-declarations
|
||||
-Wno-misleading-indentation
|
||||
;;-Wno-attributes ;; silence warnings about unknown attribute 'maybe_unused' in NeoPixelBus
|
||||
-DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703
|
||||
-DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH
|
||||
-DVTABLES_IN_FLASH
|
||||
-DMIMETYPE_MINIMAL
|
||||
-DWLED_SAVE_IRAM ;; needed to prevent linker error
|
||||
|
||||
;; this platform version was used for WLED 0.14.0
|
||||
platform_compat = espressif8266@4.2.0
|
||||
platform_packages_compat =
|
||||
platformio/toolchain-xtensa @ ~2.100300.220621 #2.40802.200502
|
||||
platformio/tool-esptool #@ ~1.413.0
|
||||
platformio/tool-esptoolpy #@ ~1.30000.0
|
||||
|
||||
;; experimental - for using older NeoPixelBus 2.7.9
|
||||
lib_deps_compat =
|
||||
ESPAsyncTCP @ 1.2.2
|
||||
ESPAsyncUDP
|
||||
ESP8266PWM
|
||||
IRremoteESP8266 @ 2.8.2
|
||||
makuna/NeoPixelBus @ 2.7.9
|
||||
https://github.com/blazoncek/QuickESPNow.git#optional-debug
|
||||
https://github.com/tignioj/ArduinoUZlib.git#20aff95cd80c141f80bdbf66895409a0046d2c2f
|
||||
https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.4.0
|
||||
|
||||
[esp32_all_variants]
|
||||
lib_deps =
|
||||
esp32async/AsyncTCP @ 3.4.7
|
||||
bitbank2/AnimatedGIF@^1.4.7
|
||||
https://github.com/Aircoookie/GifDecoder.git#bc3af189b6b1e06946569f6b4287f0b79a860f8e
|
||||
build_flags =
|
||||
-D CONFIG_ASYNC_TCP_USE_WDT=0
|
||||
-D CONFIG_ASYNC_TCP_STACK_SIZE=8192
|
||||
-D WLED_ENABLE_GIF
|
||||
|
||||
[esp32]
|
||||
platform = ${esp32_idf_V4.platform}
|
||||
platform_packages =
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${esp32_idf_V4.build_flags}
|
||||
lib_deps = ${esp32_idf_V4.lib_deps}
|
||||
monitor_filters = esp32_exception_decoder
|
||||
|
||||
tiny_partitions = tools/WLED_ESP32_2MB_noOTA.csv
|
||||
default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
|
||||
extended_partitions = tools/WLED_ESP32_4MB_700k_FS.csv
|
||||
big_partitions = tools/WLED_ESP32_4MB_256KB_FS.csv ;; 1.8MB firmware, 256KB filesystem, coredump support
|
||||
large_partitions = tools/WLED_ESP32_8MB.csv
|
||||
extreme_partitions = tools/WLED_ESP32_16MB_9MB_FS.csv
|
||||
|
||||
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
|
||||
# additional build flags for audioreactive - must be applied globally
|
||||
AR_build_flags = ;; -fsingle-precision-constant ;; forces ArduinoFFT to use float math (2x faster)
|
||||
AR_lib_deps = ;; for pre-usermod-library platformio_override compatibility
|
||||
|
||||
|
||||
[esp32_idf_V4]
|
||||
;; build environment for ESP32 using ESP-IDF 4.4.x / arduino-esp32 v2.0.5
|
||||
;; *** important: build flags from esp32_idf_V4 are inherited by _all_ esp32-based MCUs: esp32, esp32s2, esp32s3, esp32c3
|
||||
;;
|
||||
;; please note that you can NOT update existing ESP32 installs with a "V4" build. Also updating by OTA will not work properly.
|
||||
;; You need to completely erase your device (esptool erase_flash) first, then install the "V4" build from VSCode+platformio.
|
||||
|
||||
platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.06.00/platform-espressif32.zip ;; Tasmota Arduino Core 2.0.18 with IPv6 support, based on IDF 4.4.8
|
||||
platform_packages =
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = -g
|
||||
-Wshadow=compatible-local ;; emit warning in case a local variable "shadows" another local one
|
||||
-DARDUINO_ARCH_ESP32 -DESP32
|
||||
${esp32_all_variants.build_flags}
|
||||
-D WLED_ENABLE_DMX_INPUT
|
||||
lib_deps =
|
||||
${esp32_all_variants.lib_deps}
|
||||
https://github.com/someweisguy/esp_dmx.git#47db25d8c515e76fabcf5fc5ab0b786f98eeade0
|
||||
${env.lib_deps}
|
||||
|
||||
[esp32s2]
|
||||
;; generic definitions for all ESP32-S2 boards
|
||||
platform = ${esp32_idf_V4.platform}
|
||||
platform_packages = ${esp32_idf_V4.platform_packages}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = -g
|
||||
-DARDUINO_ARCH_ESP32
|
||||
-DARDUINO_ARCH_ESP32S2
|
||||
-DCONFIG_IDF_TARGET_ESP32S2=1
|
||||
-DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0
|
||||
-DCO
|
||||
-DARDUINO_USB_MODE=0 ;; this flag is mandatory for ESP32-S2 !
|
||||
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
|
||||
;; ARDUINO_USB_CDC_ON_BOOT
|
||||
${esp32_idf_V4.build_flags}
|
||||
lib_deps =
|
||||
${esp32_idf_V4.lib_deps}
|
||||
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
|
||||
monitor_filters = esp32_exception_decoder
|
||||
|
||||
[esp32c3]
|
||||
;; generic definitions for all ESP32-C3 boards
|
||||
platform = ${esp32_idf_V4.platform}
|
||||
platform_packages = ${esp32_idf_V4.platform_packages}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = -g
|
||||
-DARDUINO_ARCH_ESP32
|
||||
-DARDUINO_ARCH_ESP32C3
|
||||
-DCONFIG_IDF_TARGET_ESP32C3=1
|
||||
-DCO
|
||||
-DARDUINO_USB_MODE=1 ;; this flag is mandatory for ESP32-C3
|
||||
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
|
||||
;; ARDUINO_USB_CDC_ON_BOOT
|
||||
${esp32_idf_V4.build_flags}
|
||||
lib_deps =
|
||||
${esp32_idf_V4.lib_deps}
|
||||
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
|
||||
board_build.flash_mode = qio
|
||||
monitor_filters = esp32_exception_decoder
|
||||
|
||||
[esp32s3]
|
||||
;; generic definitions for all ESP32-S3 boards
|
||||
platform = ${esp32_idf_V4.platform}
|
||||
platform_packages = ${esp32_idf_V4.platform_packages}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = -g
|
||||
-DESP32
|
||||
-DARDUINO_ARCH_ESP32
|
||||
-DARDUINO_ARCH_ESP32S3
|
||||
-DCONFIG_IDF_TARGET_ESP32S3=1
|
||||
-DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_DFU_ON_BOOT=0
|
||||
-DCO
|
||||
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
|
||||
;; ARDUINO_USB_MODE, ARDUINO_USB_CDC_ON_BOOT
|
||||
${esp32_idf_V4.build_flags}
|
||||
lib_deps =
|
||||
${esp32_idf_V4.lib_deps}
|
||||
board_build.partitions = ${esp32.large_partitions} ;; default partioning for 8MB flash - can be overridden in build envs
|
||||
upload_speed = 921600
|
||||
monitor_filters = esp32_exception_decoder
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# WLED BUILDS
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
[env:nodemcuv2]
|
||||
extends = esp8266
|
||||
board = nodemcuv2
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP8266\" #-DWLED_DISABLE_2D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
custom_usermods = audioreactive
|
||||
|
||||
[env:nodemcuv2_compat]
|
||||
extends = env:nodemcuv2
|
||||
;; using platform version and build options from WLED 0.14.0
|
||||
platform = ${esp8266.platform_compat}
|
||||
platform_packages = ${esp8266.platform_packages_compat}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP8266_compat\" #-DWLED_DISABLE_2D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
;; lib_deps = ${esp8266.lib_deps_compat} ;; experimental - use older NeoPixelBus 2.7.9
|
||||
custom_usermods = audioreactive
|
||||
|
||||
[env:nodemcuv2_160]
|
||||
extends = env:nodemcuv2
|
||||
board_build.f_cpu = 160000000L
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP8266_160\" #-DWLED_DISABLE_2D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
custom_usermods = audioreactive
|
||||
|
||||
[env:esp8266_2m]
|
||||
extends = esp8266
|
||||
board = esp_wroom_02
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP02\"
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM1D
|
||||
custom_usermods = audioreactive
|
||||
|
||||
[env:esp8266_2m_compat]
|
||||
extends = env:esp8266_2m
|
||||
;; using platform version and build options from WLED 0.14.0
|
||||
platform = ${esp8266.platform_compat}
|
||||
platform_packages = ${esp8266.platform_packages_compat}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP02_compat\" #-DWLED_DISABLE_2D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM1D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
custom_usermods = audioreactive
|
||||
|
||||
[env:esp8266_2m_160]
|
||||
extends = env:esp8266_2m
|
||||
board_build.f_cpu = 160000000L
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP02_160\"
|
||||
-D WLED_DISABLE_PARTICLESYSTEM1D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
custom_usermods = audioreactive
|
||||
|
||||
[env:esp8266_2m_min]
|
||||
;; Minimal-feature build for ESP02 (2MB flash).
|
||||
;; Use this to recover from a failed OTA: flash via serial, then OTA-upload the regular esp8266_2m binary.
|
||||
;; OTA is intentionally kept enabled. All other optional features are stripped to minimise binary size.
|
||||
extends = esp8266
|
||||
board = esp_wroom_02
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP02_min\"
|
||||
-D WLED_DISABLE_ALEXA
|
||||
-D WLED_DISABLE_HUESYNC
|
||||
-D WLED_DISABLE_INFRARED
|
||||
-D WLED_DISABLE_MQTT
|
||||
-D WLED_DISABLE_ADALIGHT
|
||||
-D WLED_DISABLE_LOXONE
|
||||
-D WLED_DISABLE_WEBSOCKETS
|
||||
-D WLED_DISABLE_ESPNOW
|
||||
-D WLED_DISABLE_2D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM1D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
-D WLED_DISABLE_PIXELFORGE
|
||||
-D WLED_DISABLE_IMPROV_WIFISCAN
|
||||
|
||||
[env:esp01_1m_full]
|
||||
extends = esp8266
|
||||
board = esp01_1m
|
||||
board_build.ldscript = ${common.ldscript_1m128k}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP01\" -D WLED_DISABLE_OTA
|
||||
; -D WLED_USE_REAL_MATH ;; may fix wrong sunset/sunrise times, at the cost of 7064 bytes FLASH and 975 bytes RAM
|
||||
-D WLED_DISABLE_PARTICLESYSTEM1D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
-D WLED_DISABLE_PIXELFORGE
|
||||
|
||||
[env:esp01_1m_full_compat]
|
||||
extends = env:esp01_1m_full
|
||||
;; using platform version and build options from WLED 0.14.0
|
||||
platform = ${esp8266.platform_compat}
|
||||
platform_packages = ${esp8266.platform_packages_compat}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP01_compat\" -D WLED_DISABLE_OTA #-DWLED_DISABLE_2D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM1D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
-D WLED_DISABLE_PIXELFORGE
|
||||
|
||||
[env:esp01_1m_full_160]
|
||||
extends = env:esp01_1m_full
|
||||
board_build.f_cpu = 160000000L
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP01_160\" -D WLED_DISABLE_OTA
|
||||
; -D WLED_USE_REAL_MATH ;; may fix wrong sunset/sunrise times, at the cost of 7064 bytes FLASH and 975 bytes RAM
|
||||
-D WLED_DISABLE_PARTICLESYSTEM1D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
-D WLED_DISABLE_PIXELFORGE
|
||||
custom_usermods = audioreactive
|
||||
|
||||
[env:esp32dev]
|
||||
extends = esp32
|
||||
board = esp32dev
|
||||
custom_usermods = audioreactive
|
||||
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32\" #-D WLED_DISABLE_BROWNOUT_DET
|
||||
-DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
board_build.flash_mode = dio
|
||||
|
||||
[env:esp32dev_debug]
|
||||
extends = env:esp32dev
|
||||
upload_speed = 921600
|
||||
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags}
|
||||
-D WLED_DEBUG
|
||||
-D WLED_RELEASE_NAME=\"ESP32_DEBUG\"
|
||||
|
||||
[env:esp32dev_8M]
|
||||
extends = env:esp32dev
|
||||
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_8M\" #-D WLED_DISABLE_BROWNOUT_DET
|
||||
-DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
|
||||
board_build.partitions = ${esp32.large_partitions}
|
||||
board_upload.flash_size = 8MB
|
||||
board_upload.maximum_size = 8388608
|
||||
; board_build.f_flash = 80000000L
|
||||
|
||||
[env:esp32dev_16M]
|
||||
extends = env:esp32dev
|
||||
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_16M\" #-D WLED_DISABLE_BROWNOUT_DET
|
||||
-DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
|
||||
board_build.partitions = ${esp32.extreme_partitions}
|
||||
board_upload.flash_size = 16MB
|
||||
board_upload.maximum_size = 16777216
|
||||
board_build.f_flash = 80000000L
|
||||
|
||||
[env:esp32_eth]
|
||||
extends = esp32
|
||||
board = esp32-poe
|
||||
upload_speed = 921600
|
||||
custom_usermods = audioreactive
|
||||
build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"ESP32_Ethernet\" -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1
|
||||
-D SR_DMTYPE=-1 -D AUDIOPIN=-1 -D I2S_SDPIN=-1 -D I2S_WSPIN=-1 -D I2S_CKPIN=-1 -D MCLK_PIN=-1 ;; force AR to not allocate any PINs at startup
|
||||
-D DATA_PINS=4 ;; default led pin = 16 conflicts with pins used for ethernet
|
||||
; -D WLED_DISABLE_ESPNOW ;; ESP-NOW requires wifi, may crash with ethernet only => uncomment if your board uses ETH_CLOCK_GPIO0_OUT, ETH_CLOCK_GPIO16_OUT, ETH_CLOCK_GPIO17_OUT
|
||||
-DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
board_build.flash_mode = dio
|
||||
|
||||
[env:esp32_wrover]
|
||||
extends = esp32_idf_V4
|
||||
board = ttgo-t7-v14-mini32
|
||||
board_build.f_flash = 80000000L
|
||||
board_build.flash_mode = qio
|
||||
board_build.partitions = ${esp32.extended_partitions}
|
||||
custom_usermods = audioreactive
|
||||
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_WROVER\"
|
||||
-DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
|
||||
-DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue ;; Older ESP32 (rev.<3) need a PSRAM fix (increases static RAM used) https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/external-ram.html
|
||||
-D DATA_PINS=25
|
||||
|
||||
[env:esp32c3dev]
|
||||
extends = esp32c3
|
||||
board = esp32-c3-devkitm-1
|
||||
custom_usermods = audioreactive
|
||||
build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-C3\"
|
||||
-D WLED_WATCHDOG_TIMEOUT=0
|
||||
-DLOLIN_WIFI_FIX ; seems to work much better with this
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB
|
||||
;-DARDUINO_USB_CDC_ON_BOOT=0 ;; for serial-to-USB chip
|
||||
upload_speed = 460800
|
||||
board_build.flash_mode = dio ; safe default, required for OTA updates to 0.16 from older version which used dio (must match the bootloader!)
|
||||
|
||||
[env:esp32c3dev_qio]
|
||||
extends = env:esp32c3dev
|
||||
build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-C3-QIO\"
|
||||
board_build.flash_mode = qio ; qio is faster and works on almost all boards (some boards may use dio to get 2 extra pins)
|
||||
|
||||
[env:esp32s3dev_16MB_opi]
|
||||
;; ESP32-S3 development board, with 16MB FLASH and >= 8MB PSRAM (memory_type: qio_opi)
|
||||
extends = esp32s3
|
||||
board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support
|
||||
board_build.arduino.memory_type = qio_opi ;; use with PSRAM: 8MB or 16MB
|
||||
custom_usermods = audioreactive
|
||||
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_16MB_opi\"
|
||||
-D WLED_WATCHDOG_TIMEOUT=0
|
||||
;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
|
||||
-D ARDUINO_USB_CDC_ON_BOOT=1 ;; -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
|
||||
-DBOARD_HAS_PSRAM
|
||||
board_build.partitions = ${esp32.extreme_partitions}
|
||||
board_upload.flash_size = 16MB
|
||||
board_upload.maximum_size = 16777216
|
||||
board_build.f_flash = 80000000L
|
||||
board_build.flash_mode = qio
|
||||
|
||||
[env:esp32s3dev_8MB_opi]
|
||||
;; ESP32-S3 development board, with 8MB FLASH and >= 8MB PSRAM (memory_type: qio_opi)
|
||||
extends = esp32s3
|
||||
board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support
|
||||
board_build.arduino.memory_type = qio_opi ;; use with PSRAM: 8MB or 16MB
|
||||
custom_usermods = audioreactive
|
||||
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_8MB_opi\"
|
||||
-D WLED_WATCHDOG_TIMEOUT=0
|
||||
;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
|
||||
-D ARDUINO_USB_CDC_ON_BOOT=1 ;; -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
|
||||
-DBOARD_HAS_PSRAM
|
||||
board_build.f_flash = 80000000L
|
||||
board_build.flash_mode = qio
|
||||
|
||||
[env:esp32s3dev_8MB_qspi]
|
||||
;; generic ESP32-S3 board with 8MB FLASH and PSRAM (memory_type: qio_qspi). Try this one if esp32s3dev_8MB_opi does not work on your board
|
||||
extends = env:esp32s3dev_8MB_opi
|
||||
board_build.arduino.memory_type = qio_qspi
|
||||
board_build.flash_mode = qio
|
||||
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_8MB_qspi\"
|
||||
-D WLED_WATCHDOG_TIMEOUT=0
|
||||
;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
|
||||
-D ARDUINO_USB_CDC_ON_BOOT=1 ;; -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
|
||||
-DBOARD_HAS_PSRAM
|
||||
;; -DLOLIN_WIFI_FIX ;; uncomment if you have WiFi connectivity problems
|
||||
|
||||
[env:esp32S3_wroom2]
|
||||
;; For ESP32-S3 WROOM-2, a.k.a. ESP32-S3 DevKitC-1 v1.1
|
||||
;; with >= 16MB FLASH and >= 8MB PSRAM (memory_type: opi_opi)
|
||||
extends = esp32s3
|
||||
board = esp32s3camlcd ;; this is the only standard board with "opi_opi"
|
||||
board_build.arduino.memory_type = opi_opi
|
||||
custom_usermods = audioreactive
|
||||
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_WROOM-2\"
|
||||
-D WLED_WATCHDOG_TIMEOUT=0
|
||||
-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
|
||||
;; -D ARDUINO_USB_CDC_ON_BOOT=1 ;; -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
|
||||
-DBOARD_HAS_PSRAM
|
||||
-D LEDPIN=38 -D DATA_PINS=38 ;; buildin WS2812b LED
|
||||
-D BTNPIN=0 -D RLYPIN=16 -D IRPIN=17 -D AUDIOPIN=-1
|
||||
;;-D WLED_DEBUG
|
||||
-D SR_DMTYPE=1 -D I2S_SDPIN=13 -D I2S_CKPIN=14 -D I2S_WSPIN=15 -D MCLK_PIN=4 ;; I2S mic
|
||||
board_build.partitions = ${esp32.extreme_partitions}
|
||||
board_upload.flash_size = 16MB
|
||||
board_upload.maximum_size = 16777216
|
||||
|
||||
[env:esp32S3_wroom2_32MB]
|
||||
;; For ESP32-S3 WROOM-2 with 32MB Flash, and >= 8MB PSRAM (memory_type: opi_opi)
|
||||
extends = env:esp32S3_wroom2
|
||||
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_WROOM-2_32MB\"
|
||||
-D WLED_WATCHDOG_TIMEOUT=0
|
||||
-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
|
||||
;; -D ARDUINO_USB_CDC_ON_BOOT=1 ;; -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
|
||||
-DBOARD_HAS_PSRAM
|
||||
-D LEDPIN=38 -D DATA_PINS=38 ;; buildin WS2812b LED
|
||||
-D BTNPIN=0 -D RLYPIN=16 -D IRPIN=17 -D AUDIOPIN=-1
|
||||
;;-D WLED_DEBUG
|
||||
-D SR_DMTYPE=1 -D I2S_SDPIN=13 -D I2S_CKPIN=14 -D I2S_WSPIN=15 -D MCLK_PIN=4 ;; I2S mic
|
||||
board_build.partitions = tools/WLED_ESP32_32MB.csv
|
||||
board_upload.flash_size = 32MB
|
||||
board_upload.maximum_size = 33554432
|
||||
|
||||
[env:esp32s3_4M_qspi]
|
||||
;; ESP32-S3, with 4MB FLASH and <= 4MB PSRAM (memory_type: qio_qspi)
|
||||
extends = esp32s3
|
||||
board = lolin_s3_mini ;; -S3 mini, 4MB flash 2MB PSRAM
|
||||
custom_usermods = audioreactive
|
||||
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_4M_qspi\"
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1 ;; -DARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
|
||||
-DBOARD_HAS_PSRAM
|
||||
-DLOLIN_WIFI_FIX ; seems to work much better with this
|
||||
-D WLED_WATCHDOG_TIMEOUT=0
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
board_build.f_flash = 80000000L
|
||||
board_build.flash_mode = qio
|
||||
|
||||
[env:lolin_s2_mini]
|
||||
extends = esp32s2
|
||||
board = lolin_s2_mini
|
||||
board_build.flash_mode = qio
|
||||
board_build.f_flash = 80000000L
|
||||
custom_usermods = audioreactive
|
||||
build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S2\"
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
-DARDUINO_USB_MSC_ON_BOOT=0
|
||||
-DARDUINO_USB_DFU_ON_BOOT=0
|
||||
-DBOARD_HAS_PSRAM
|
||||
-DLOLIN_WIFI_FIX ; seems to work much better with this
|
||||
-D WLED_WATCHDOG_TIMEOUT=0
|
||||
-D DATA_PINS=16
|
||||
-D HW_PIN_SCL=35
|
||||
-D HW_PIN_SDA=33
|
||||
-D HW_PIN_CLOCKSPI=7
|
||||
-D HW_PIN_DATASPI=11
|
||||
-D HW_PIN_MISOSPI=9
|
||||
; -D STATUSLED=15
|
||||
|
||||
|
||||
[env:usermods]
|
||||
extends = env:esp32dev
|
||||
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_USERMODS\"
|
||||
-DTOUCH_CS=9
|
||||
custom_usermods = * ; Expands to all usermods in usermods folder
|
||||
board_build.partitions = ${esp32.extreme_partitions} ; We're gonna need a bigger boat
|
||||
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Hub75 examples
|
||||
# ------------------------------------------------------------------------------
|
||||
# Note: some panels may experience ghosting with default full brightness. use -D WLED_HUB75_MAX_BRIGHTNESS=239 or lower to fix it.
|
||||
|
||||
[hub75]
|
||||
;; Shared values for all HUB75 build envs.
|
||||
|
||||
;; Core HUB75 flags - common to every HUB75 build
|
||||
build_flags =
|
||||
-D WLED_ENABLE_HUB75MATRIX -D NO_GFX
|
||||
-D WLED_DEBUG_BUS
|
||||
-D LED_TYPES=TYPE_HUB75MATRIX_HS
|
||||
; -D WLED_DEBUG
|
||||
|
||||
;; Default I2S mic pins disabled (HUB75 uses GPIOs that would otherwise clash).
|
||||
;; Envs that wire up a real mic should NOT include this and define I2S_*PIN themselves.
|
||||
i2s_disable_flags = -D SR_DMTYPE=1 -D I2S_SDPIN=-1 -D I2S_CKPIN=-1 -D I2S_WSPIN=-1 -D MCLK_PIN=-1 ;; Disable to prevent pin clash
|
||||
|
||||
;; Pinned HUB75 driver libraries
|
||||
lib_deps = https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-DMA.git#f17fb7fe9d487e9643f919eb5aeedea8d9d1f8d7 ;; 3.0.14
|
||||
|
||||
;; Extra flags shared by all S3-based HUB75 builds
|
||||
s3_build_flags =
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1 ;; -DARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
|
||||
-DBOARD_HAS_PSRAM
|
||||
-DLOLIN_WIFI_FIX ; seems to work much better with this (sets lower TX power)
|
||||
-D WLED_WATCHDOG_TIMEOUT=0
|
||||
-D S3_LCD_DIV_NUM=20 ;; Attempt to fix wifi performance issue when panel active with S3 chips
|
||||
|
||||
|
||||
[env:esp32dev_hub75]
|
||||
extends = env:esp32dev
|
||||
upload_speed = 921600
|
||||
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} ${hub75.build_flags} ${hub75.i2s_disable_flags}
|
||||
-DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
|
||||
-D WLED_RELEASE_NAME=\"ESP32_HUB75\"
|
||||
lib_deps = ${esp32_idf_V4.lib_deps}
|
||||
${hub75.lib_deps}
|
||||
|
||||
[env:esp32dev_hub75_forum_pinout]
|
||||
extends = env:esp32dev_hub75
|
||||
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} ${hub75.build_flags} ${hub75.i2s_disable_flags}
|
||||
-DARDUINO_USB_CDC_ON_BOOT=0
|
||||
-D WLED_RELEASE_NAME=\"ESP32_HUB75_forum_pinout\"
|
||||
-D ESP32_FORUM_PINOUT ;; enable for SmartMatrix default pins
|
||||
|
||||
[env:esp32s3dev_4MB_qspi_hub75]
|
||||
; HD-WF2 - NOTE: this board has NO PSRAM, so BOARD_HAS_PSRAM must not be set
|
||||
; (BOARD_HAS_PSRAM causes the DMA library to allocate only in SPIRAM, which fails without PSRAM)
|
||||
extends = env:esp32s3dev_8MB_qspi
|
||||
board_build.partitions = ${esp32.extended_partitions} ;; 1.65MB firmware, 700KB filesystem
|
||||
build_unflags = -DBOARD_HAS_PSRAM
|
||||
build_flags = ${common.build_flags} ${esp32s3.build_flags} ${hub75.build_flags} ${hub75.s3_build_flags} ${hub75.i2s_disable_flags}
|
||||
-D WLED_RELEASE_NAME=\"ESP32-S3_HD-WF2\"
|
||||
-D HD_WF2_PINOUT ;; Huidu HD-WF2 specific GPIO wiring
|
||||
lib_deps = ${esp32s3.lib_deps}
|
||||
${hub75.lib_deps}
|
||||
|
||||
[env:adafruit_matrixportal_esp32s3]
|
||||
; ESP32-S3 processor, 8 MB flash, 2 MB of PSRAM, dedicated driver pins for HUB75
|
||||
extends = env:esp32s3dev_8MB_qspi
|
||||
board = adafruit_matrixportal_esp32s3_wled ; modified board definition: removed flash section that causes FS erase on upload
|
||||
build_flags = ${common.build_flags} ${esp32s3.build_flags} ${hub75.build_flags} ${hub75.s3_build_flags} ${hub75.i2s_disable_flags}
|
||||
-D WLED_RELEASE_NAME=\"ESP32-S3_Adafruit_Matrixportal\"
|
||||
-D ARDUINO_ADAFRUIT_MATRIXPORTAL_ESP32S3
|
||||
lib_deps = ${esp32s3.lib_deps}
|
||||
${hub75.lib_deps}
|
||||
;; board_build.partitions = tools/partitions-8MB_spiffs-tinyuf2.csv ;; supports adafruit UF2 bootloader
|
||||
|
||||
[env:esp32s3dev_16MB_opi_hub75]
|
||||
;; MOONHUB HUB75 adapter board (lilygo T7-S3 with 16MB flash and octal PSRAM)
|
||||
extends = env:esp32s3dev_8MB_opi
|
||||
board = lilygo-t7-s3
|
||||
board_build.partitions = ${esp32.extreme_partitions} ;; for 16MB flash (overrides large_partitions for 8MB)
|
||||
;; Note: real I2S mic pins are wired here, so we do NOT include ${hub75.i2s_disable_flags}.
|
||||
build_flags = ${common.build_flags} ${esp32s3.build_flags} ${hub75.build_flags} ${hub75.s3_build_flags}
|
||||
-D WLED_RELEASE_NAME=\"ESP32-S3_16MB_opi_HUB75\"
|
||||
-D MOONHUB_S3_PINOUT ;; HUB75 pinout
|
||||
-D LEDPIN=14 -D BTNPIN=0 -D RLYPIN=15 -D IRPIN=-1 -D AUDIOPIN=-1 ;; defaults that avoid pin conflicts with HUB75
|
||||
-D SR_DMTYPE=1 -D I2S_SDPIN=10 -D I2S_CKPIN=11 -D I2S_WSPIN=12 -D MCLK_PIN=-1 ;; I2S mic
|
||||
lib_deps = ${esp32s3.lib_deps}
|
||||
${hub75.lib_deps}
|
||||
558
platformio_override.sample.ini
Normal file
558
platformio_override.sample.ini
Normal file
@@ -0,0 +1,558 @@
|
||||
# Example PlatformIO Project Configuration Override
|
||||
# ------------------------------------------------------------------------------
|
||||
# Copy to platformio_override.ini to activate overrides
|
||||
# ------------------------------------------------------------------------------
|
||||
# Please visit documentation: https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[platformio]
|
||||
default_envs = WLED_generic8266_1M, esp32dev_V4_dio80 # put the name(s) of your own build environment here. You can define as many as you need
|
||||
|
||||
#----------
|
||||
# SAMPLE
|
||||
#----------
|
||||
[env:WLED_generic8266_1M]
|
||||
extends = env:esp01_1m_full # when you want to extend the existing environment (define only updated options)
|
||||
; board = esp01_1m # uncomment when ou need different board
|
||||
; platform = ${esp8266.platform_wled_default} # uncomment and change when you want particular platform
|
||||
; platform_packages = ${esp8266.platform_packages}
|
||||
; board_build.ldscript = ${common.ldscript_1m128k}
|
||||
; upload_speed = 921600 # fast upload speed (remove ';' if your board supports fast upload speed)
|
||||
# Sample libraries used for various usermods. Uncomment when using particular usermod.
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
; olikraus/U8g2 # @~2.33.15
|
||||
; paulstoffregen/OneWire@~2.3.8
|
||||
; adafruit/Adafruit Unified Sensor@^1.1.4
|
||||
; adafruit/DHT sensor library@^1.4.1
|
||||
; adafruit/Adafruit BME280 Library@^2.2.2
|
||||
; Wire
|
||||
; robtillaart/SHT85@~0.3.3
|
||||
; ;gmag11/QuickESPNow @ ~0.7.0 # will also load QuickDebug
|
||||
; https://github.com/blazoncek/QuickESPNow.git#optional-debug ;; exludes debug library
|
||||
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags}
|
||||
;
|
||||
; *** To use the below defines/overrides, copy and paste each onto its own line just below build_flags in the section above.
|
||||
;
|
||||
; Set a release name that may be used to distinguish required binary for flashing
|
||||
; -D WLED_RELEASE_NAME=\"ESP32_MULTI_USREMODS\"
|
||||
;
|
||||
; disable specific features
|
||||
; -D WLED_DISABLE_OTA
|
||||
; -D WLED_DISABLE_ALEXA
|
||||
; -D WLED_DISABLE_HUESYNC
|
||||
; -D WLED_DISABLE_LOXONE
|
||||
; -D WLED_DISABLE_INFRARED
|
||||
; -D WLED_DISABLE_WEBSOCKETS
|
||||
; -D WLED_DISABLE_MQTT
|
||||
; -D WLED_DISABLE_ADALIGHT
|
||||
; -D WLED_DISABLE_2D
|
||||
; -D WLED_DISABLE_PIXELFORGE
|
||||
; -D WLED_DISABLE_ESPNOW
|
||||
; -D WLED_DISABLE_BROWNOUT_DET
|
||||
;
|
||||
; enable optional built-in features
|
||||
; -D WLED_ENABLE_PIXART
|
||||
; -D WLED_ENABLE_USERMOD_PAGE # if created
|
||||
; -D WLED_ENABLE_DMX
|
||||
;
|
||||
; PIN defines - uncomment and change, if needed:
|
||||
; -D DATA_PINS=2
|
||||
; or use this for multiple outputs
|
||||
; -D DATA_PINS=1,3
|
||||
; -D BTNPIN=0
|
||||
; -D IRPIN=4
|
||||
; -D RLYPIN=12
|
||||
; -D RLYMDE=1
|
||||
; -D RLYODRAIN=0
|
||||
; -D LED_BUILTIN=2 # GPIO of built-in LED
|
||||
;
|
||||
; Limit max buses
|
||||
; -D WLED_MAX_BUSSES=2
|
||||
; -D WLED_MAX_ANALOG_CHANNELS=3 # only 3 PWM HW pins available
|
||||
; -D WLED_MAX_DIGITAL_CHANNELS=2 # only 2 HW accelerated pins available
|
||||
;
|
||||
; Configure default WiFi
|
||||
; -D CLIENT_SSID='"MyNetwork"'
|
||||
; -D CLIENT_PASS='"Netw0rkPassw0rd"'
|
||||
;
|
||||
; Configure and use Ethernet
|
||||
; -D WLED_USE_ETHERNET
|
||||
; -D WLED_ETH_DEFAULT=5
|
||||
; do not use pins 5, (16,) 17, 18, 19, 21, 22, 23, 25, 26, 27 for anything but ethernet
|
||||
; -D PHY_ADDR=0 -D ETH_PHY_POWER=5 -D ETH_PHY_MDC=23 -D ETH_PHY_MDIO=18
|
||||
; -D ETH_CLK_MODE=ETH_CLOCK_GPIO17_OUT
|
||||
;
|
||||
; NTP time configuration
|
||||
; -D WLED_NTP_ENABLED=true
|
||||
; -D WLED_TIMEZONE=2
|
||||
; -D WLED_LAT=48.86
|
||||
; -D WLED_LON=2.33
|
||||
;
|
||||
; Use Watchdog timer with 10s guard
|
||||
; -D WLED_WATCHDOG_TIMEOUT=10
|
||||
;
|
||||
; Create debug build (with remote debug)
|
||||
; -D WLED_DEBUG
|
||||
; -D WLED_DEBUG_HOST='"192.168.0.100"'
|
||||
; -D WLED_DEBUG_PORT=7868
|
||||
;
|
||||
; Use Autosave usermod and set it to do save after 90s
|
||||
; -D USERMOD_AUTO_SAVE
|
||||
; -D AUTOSAVE_AFTER_SEC=90
|
||||
;
|
||||
; Use AHT10/AHT15/AHT20 usermod
|
||||
; -D USERMOD_AHT10
|
||||
;
|
||||
; Use INA226 usermod
|
||||
; -D USERMOD_INA226
|
||||
;
|
||||
; Use 4 Line Display usermod with SPI display
|
||||
; -D USERMOD_FOUR_LINE_DISPLAY
|
||||
; -DFLD_SPI_DEFAULT
|
||||
; -D FLD_TYPE=SSD1306_SPI64
|
||||
; -D FLD_PIN_CLOCKSPI=14
|
||||
; -D FLD_PIN_DATASPI=13
|
||||
; -D FLD_PIN_DC=26
|
||||
; -D FLD_PIN_CS=15
|
||||
; -D FLD_PIN_RESET=27
|
||||
;
|
||||
; Use Rotary encoder usermod (in conjunction with 4LD)
|
||||
; -D USERMOD_ROTARY_ENCODER_UI
|
||||
; -D ENCODER_DT_PIN=5
|
||||
; -D ENCODER_CLK_PIN=18
|
||||
; -D ENCODER_SW_PIN=19
|
||||
;
|
||||
; Use Dallas DS18B20 temperature sensor usermod and configure it to use GPIO13
|
||||
; -D USERMOD_DALLASTEMPERATURE
|
||||
; -D TEMPERATURE_PIN=13
|
||||
;
|
||||
; Use Multi Relay usermod and configure it to use 6 relays and appropriate GPIO
|
||||
; -D USERMOD_MULTI_RELAY
|
||||
; -D MULTI_RELAY_MAX_RELAYS=6
|
||||
; -D MULTI_RELAY_PINS=12,23,22,21,24,25
|
||||
;
|
||||
; Use PIR sensor usermod and configure it to use GPIO4 and timer of 60s
|
||||
; -D USERMOD_PIRSWITCH
|
||||
; -D PIR_SENSOR_PIN=4 # use -1 to disable usermod
|
||||
; -D PIR_SENSOR_OFF_SEC=60
|
||||
; -D PIR_SENSOR_MAX_SENSORS=2 # max allowable sensors (uses OR logic for triggering)
|
||||
;
|
||||
; Use Audioreactive usermod and configure I2S microphone
|
||||
; -D AUDIOPIN=-1
|
||||
; -D DMTYPE=1 # 0-analog/disabled, 1-I2S generic, 2-ES7243, 3-SPH0645, 4-I2S+mclk, 5-I2S PDM
|
||||
; -D I2S_SDPIN=36
|
||||
; -D I2S_WSPIN=23
|
||||
; -D I2S_CKPIN=19
|
||||
;
|
||||
; Use PWM fan usermod
|
||||
; -D USERMOD_PWM_FAN
|
||||
; -D TACHO_PIN=33
|
||||
; -D PWM_PIN=32
|
||||
;
|
||||
; Use POV Display usermod
|
||||
; -D USERMOD_POV_DISPLAY
|
||||
; Use built-in or custom LED as a status indicator (assumes LED is connected to GPIO16)
|
||||
; -D STATUSLED=16
|
||||
;
|
||||
; set the name of the module - make sure there is a quote-backslash-quote before the name and a backslash-quote-quote after the name
|
||||
; -D SERVERNAME="\"WLED\""
|
||||
;
|
||||
; set the number of LEDs
|
||||
; -D PIXEL_COUNTS=30
|
||||
; or this for multiple outputs
|
||||
; -D PIXEL_COUNTS=30,30
|
||||
;
|
||||
; set the default LED type
|
||||
; -D LED_TYPES=22 # see const.h (TYPE_xxxx)
|
||||
; or this for multiple outputs
|
||||
; -D LED_TYPES=TYPE_SK6812_RGBW,TYPE_WS2812_RGB
|
||||
;
|
||||
; set default color order of your led strip
|
||||
; -D DEFAULT_LED_COLOR_ORDER=COL_ORDER_GRB
|
||||
;
|
||||
; set milliampere limit when using ESP power pin (or inadequate PSU) to power LEDs
|
||||
; -D ABL_MILLIAMPS_DEFAULT=850
|
||||
; -D LED_MILLIAMPS_DEFAULT=55
|
||||
;
|
||||
; enable IR by setting remote type
|
||||
; -D IRTYPE=0 # 0 Remote disabled | 1 24-key RGB | 2 24-key with CT | 3 40-key blue | 4 40-key RGB | 5 21-key RGB | 6 6-key black | 7 9-key red | 8 JSON remote
|
||||
;
|
||||
; use PSRAM on classic ESP32 rev.1 (rev.3 or above has no issues)
|
||||
; -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue # needed only for classic ESP32 rev.1
|
||||
;
|
||||
; configure I2C and SPI interface (for various hardware)
|
||||
; -D I2CSDAPIN=33 # initialise interface
|
||||
; -D I2CSCLPIN=35 # initialise interface
|
||||
; -D HW_PIN_SCL=35
|
||||
; -D HW_PIN_SDA=33
|
||||
; -D HW_PIN_CLOCKSPI=7
|
||||
; -D HW_PIN_DATASPI=11
|
||||
; -D HW_PIN_MISOSPI=9
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Optional: build flags for speed, instead of optimising for size.
|
||||
# Add ${Speed_Flags.build_flags} / ${Speed_Flags.build_unflags} to your own env
|
||||
# in platformio_override.ini to opt in.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
[Speed_Flags]
|
||||
build_unflags = -Os ;; to disable standard optimization for small size
|
||||
build_flags =
|
||||
-O2 ;; optimize for speed
|
||||
-free -fipa-pta ;; very useful, too
|
||||
;;-fsingle-precision-constant ;; makes all floating point literals "float" (default is "double")
|
||||
;;-funsafe-math-optimizations ;; less dangerous than -ffast-math; still allows the compiler to exploit FMA and reciprocals (up to 10% faster on -S3)
|
||||
# Important: we need to explicitly switch off some "-O2" optimizations
|
||||
-fno-jump-tables -fno-tree-switch-conversion ;; needed - firmware may crash otherwise
|
||||
-freorder-blocks -Wwrite-strings -fstrict-volatile-bitfields ;; needed - recommended by espressif
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# PRE-CONFIGURED DEVELOPMENT BOARDS AND CONTROLLERS
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
[env:esp07]
|
||||
board = esp07
|
||||
platform = ${esp8266.platform_wled_default}
|
||||
platform_packages = ${esp8266.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:d1_mini]
|
||||
board = d1_mini
|
||||
platform = ${esp8266.platform_wled_default}
|
||||
platform_packages = ${esp8266.platform_packages}
|
||||
upload_speed = 921600
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
monitor_filters = esp8266_exception_decoder
|
||||
|
||||
[env:heltec_wifi_kit_8]
|
||||
board = d1_mini
|
||||
platform = ${esp8266.platform_wled_default}
|
||||
platform_packages = ${esp8266.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:h803wf]
|
||||
board = d1_mini
|
||||
platform = ${esp8266.platform_wled_default}
|
||||
platform_packages = ${esp8266.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D DATA_PINS=1 -D WLED_DISABLE_INFRARED
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp32dev_qio80]
|
||||
extends = env:esp32dev # we want to extend the existing esp32dev environment (and define only updated options)
|
||||
board = esp32dev
|
||||
build_flags = ${common.build_flags} ${esp32.build_flags} #-D WLED_DISABLE_BROWNOUT_DET
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
monitor_filters = esp32_exception_decoder
|
||||
board_build.f_flash = 80000000L
|
||||
board_build.flash_mode = qio
|
||||
|
||||
[env:esp32dev_V4_dio80]
|
||||
;; experimental ESP32 env using ESP-IDF V4.4.x
|
||||
;; Warning: this build environment is not stable!!
|
||||
;; please erase your device before installing.
|
||||
extends = esp32_idf_V4 # based on newer "esp-idf V4" platform environment
|
||||
board = esp32dev
|
||||
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} #-D WLED_DISABLE_BROWNOUT_DET
|
||||
lib_deps = ${esp32_idf_V4.lib_deps}
|
||||
monitor_filters = esp32_exception_decoder
|
||||
board_build.partitions = ${esp32.default_partitions} ;; if you get errors about "out of program space", change this to ${esp32.extended_partitions} or even ${esp32.big_partitions}
|
||||
board_build.f_flash = 80000000L
|
||||
board_build.flash_mode = dio
|
||||
|
||||
[env:esp32s2_saola]
|
||||
extends = esp32s2
|
||||
board = esp32-s2-saola-1
|
||||
platform = ${esp32s2.platform}
|
||||
platform_packages = ${esp32s2.platform_packages}
|
||||
framework = arduino
|
||||
board_build.flash_mode = qio
|
||||
upload_speed = 460800
|
||||
build_flags = ${common.build_flags} ${esp32s2.build_flags}
|
||||
;-DLOLIN_WIFI_FIX ;; try this in case Wifi does not work
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
lib_deps = ${esp32s2.lib_deps}
|
||||
|
||||
[env:esp32s3dev_8MB_PSRAM_qspi]
|
||||
;; ESP32-TinyS3 development board, with 8MB FLASH and PSRAM (memory_type: qio_qspi)
|
||||
extends = env:esp32s3dev_8MB_PSRAM_opi
|
||||
;board = um_tinys3 ; -> needs workaround from https://github.com/wled-dev/WLED/pull/2905#issuecomment-1328049860
|
||||
board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support
|
||||
board_build.arduino.memory_type = qio_qspi ;; use with PSRAM: 2MB or 4MB
|
||||
|
||||
[env:esp8285_4CH_MagicHome]
|
||||
board = esp8285
|
||||
platform = ${esp8266.platform_wled_default}
|
||||
platform_packages = ${esp8266.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_1m128k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_DISABLE_OTA
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp8285_H801]
|
||||
board = esp8285
|
||||
platform = ${esp8266.platform_wled_default}
|
||||
platform_packages = ${esp8266.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_1m128k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_DISABLE_OTA
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:d1_mini_5CH_Shojo_PCB]
|
||||
board = d1_mini
|
||||
platform = ${esp8266.platform_wled_default}
|
||||
platform_packages = ${esp8266.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_USE_SHOJO_PCB ;; NB: WLED_USE_SHOJO_PCB is not used anywhere in the source code. Not sure why its needed.
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:d1_mini_debug]
|
||||
board = d1_mini
|
||||
build_type = debug
|
||||
platform = ${esp8266.platform_wled_default}
|
||||
platform_packages = ${esp8266.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} ${common.debug_flags}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:d1_mini_ota]
|
||||
board = d1_mini
|
||||
upload_protocol = espota
|
||||
# exchange for your WLED IP
|
||||
upload_port = "10.10.1.27"
|
||||
platform = ${esp8266.platform_wled_default}
|
||||
platform_packages = ${esp8266.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:anavi_miracle_controller]
|
||||
board = d1_mini
|
||||
platform = ${esp8266.platform_wled_default}
|
||||
platform_packages = ${esp8266.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D DATA_PINS=12 -D IRPIN=-1 -D RLYPIN=2
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp32c3dev_2MB]
|
||||
;; for ESP32-C3 boards with 2MB flash (instead of 4MB).
|
||||
;; this board need a specific partition file. OTA not possible.
|
||||
extends = esp32c3
|
||||
platform = ${esp32c3.platform}
|
||||
platform_packages = ${esp32c3.platform_packages}
|
||||
board = esp32-c3-devkitm-1
|
||||
build_flags = ${common.build_flags} ${esp32c3.build_flags}
|
||||
-D WLED_WATCHDOG_TIMEOUT=0
|
||||
-D WLED_DISABLE_OTA
|
||||
; -DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB
|
||||
-DARDUINO_USB_CDC_ON_BOOT=0 ;; for serial-to-USB chip
|
||||
build_unflags = ${common.build_unflags}
|
||||
upload_speed = 115200
|
||||
lib_deps = ${esp32c3.lib_deps}
|
||||
board_build.partitions = tools/WLED_ESP32_2MB_noOTA.csv
|
||||
board_build.flash_mode = dio
|
||||
board_upload.flash_size = 2MB
|
||||
board_upload.maximum_size = 2097152
|
||||
|
||||
[env:wemos_shield_esp32]
|
||||
extends = esp32 ;; use default esp32 platform
|
||||
board = esp32dev
|
||||
upload_speed = 460800
|
||||
build_flags = ${common.build_flags} ${esp32.build_flags}
|
||||
-D WLED_RELEASE_NAME=\"ESP32_wemos_shield\"
|
||||
-D DATA_PINS=16
|
||||
-D RLYPIN=19
|
||||
-D BTNPIN=17
|
||||
-D IRPIN=18
|
||||
-UWLED_USE_MY_CONFIG
|
||||
-D USERMOD_DALLASTEMPERATURE
|
||||
-D USERMOD_FOUR_LINE_DISPLAY
|
||||
-D TEMPERATURE_PIN=23
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
OneWire@~2.3.5 ;; needed for USERMOD_DALLASTEMPERATURE
|
||||
olikraus/U8g2 @ ^2.28.8 ;; needed for USERMOD_FOUR_LINE_DISPLAY
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
|
||||
[env:esp32_pico-D4]
|
||||
extends = esp32 ;; use default esp32 platform
|
||||
board = pico32 ;; pico32-D4 is different from the standard esp32dev
|
||||
;; hardware details from https://github.com/srg74/WLED-ESP32-pico
|
||||
build_flags = ${common.build_flags} ${esp32.build_flags}
|
||||
-D WLED_RELEASE_NAME=\"pico32-D4\" -D SERVERNAME='"WLED-pico32"'
|
||||
-D WLED_DISABLE_ADALIGHT ;; no serial-to-USB chip on this board - better to disable serial protocols
|
||||
-D DATA_PINS=2,18 ;; LED pins
|
||||
-D RLYPIN=19 -D BTNPIN=0 -D IRPIN=-1 ;; no default pin for IR
|
||||
-D UM_AUDIOREACTIVE_ENABLE ;; enable AR by default
|
||||
;; Audioreactive settings for on-board microphone (ICS-43432)
|
||||
-D SR_DMTYPE=1 -D I2S_SDPIN=25 -D I2S_WSPIN=15 -D I2S_CKPIN=14
|
||||
-D SR_SQUELCH=5 -D SR_GAIN=30
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
board_build.f_flash = 80000000L
|
||||
|
||||
[env:m5atom]
|
||||
extends = env:esp32dev # we want to extend the existing esp32dev environment (and define only updated options)
|
||||
build_flags = ${common.build_flags} ${esp32.build_flags} -D DATA_PINS=27 -D BTNPIN=39
|
||||
|
||||
[env:sp501e]
|
||||
board = esp_wroom_02
|
||||
platform = ${esp8266.platform_wled_default}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D DATA_PINS=3 -D BTNPIN=1
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:sp511e]
|
||||
board = esp_wroom_02
|
||||
platform = ${esp8266.platform_wled_default}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D DATA_PINS=3 -D BTNPIN=2 -D IRPIN=5 -D WLED_MAX_BUTTONS=3
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:Athom_RGBCW] ;7w and 5w(GU10) bulbs
|
||||
board = esp8285
|
||||
platform = ${esp8266.platform_wled_default}
|
||||
platform_packages = ${esp8266.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,13,5
|
||||
-D LED_TYPES=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:Athom_15w_RGBCW] ;15w bulb
|
||||
board = esp8285
|
||||
platform = ${esp8266.platform_wled_default}
|
||||
platform_packages = ${esp8266.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,5,13
|
||||
-D LED_TYPES=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0 -D WLED_USE_IC_CCT
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:Athom_3Pin_Controller] ;small controller with only data
|
||||
board = esp8285
|
||||
platform = ${esp8266.platform_wled_default}
|
||||
platform_packages = ${esp8266.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=0 -D RLYPIN=-1 -D DATA_PINS=1 -D WLED_DISABLE_INFRARED
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:Athom_4Pin_Controller] ; With clock and data interface
|
||||
board = esp8285
|
||||
platform = ${esp8266.platform_wled_default}
|
||||
platform_packages = ${esp8266.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=0 -D RLYPIN=12 -D DATA_PINS=1 -D WLED_DISABLE_INFRARED
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:Athom_5Pin_Controller] ;Analog light strip controller
|
||||
board = esp8285
|
||||
platform = ${esp8266.platform_wled_default}
|
||||
platform_packages = ${esp8266.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=0 -D RLYPIN=-1 DATA_PINS=4,12,14,13 -D WLED_DISABLE_INFRARED
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:MY9291]
|
||||
board = esp01_1m
|
||||
platform = ${esp8266.platform_wled_default}
|
||||
platform_packages = ${esp8266.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_1m128k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_DISABLE_OTA -D USERMOD_MY9291
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# codm pixel controller board configurations
|
||||
# codm-controller-0_6 can also be used for the TYWE3S controller
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
[env:codm-controller-0_6]
|
||||
board = esp_wroom_02
|
||||
platform = ${esp8266.platform_wled_default}
|
||||
platform_packages = ${esp8266.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:codm-controller-0_6-rev2]
|
||||
board = esp_wroom_02
|
||||
platform = ${esp8266.platform_wled_default}
|
||||
platform_packages = ${esp8266.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# EleksTube-IPS
|
||||
# ------------------------------------------------------------------------------
|
||||
[env:elekstube_ips]
|
||||
extends = esp32 ;; use default esp32 platform
|
||||
board = esp32dev
|
||||
upload_speed = 921600
|
||||
custom_usermods = ${env:esp32dev.custom_usermods} RTC EleksTube_IPS
|
||||
build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_DISABLE_BROWNOUT_DET -D WLED_DISABLE_INFRARED
|
||||
-D DATA_PINS=12
|
||||
-D RLYPIN=27
|
||||
-D BTNPIN=34
|
||||
-D PIXEL_COUNTS=6
|
||||
# Display config
|
||||
-D ST7789_DRIVER
|
||||
-D TFT_WIDTH=135
|
||||
-D TFT_HEIGHT=240
|
||||
-D CGRAM_OFFSET
|
||||
-D TFT_SDA_READ
|
||||
-D TFT_MOSI=23
|
||||
-D TFT_SCLK=18
|
||||
-D TFT_DC=25
|
||||
-D TFT_RST=26
|
||||
-D SPI_FREQUENCY=40000000
|
||||
-D USER_SETUP_LOADED
|
||||
monitor_filters = esp32_exception_decoder
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Usermod examples
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# 433MHz RF remote example for esp32dev
|
||||
[env:esp32dev_usermod_RF433]
|
||||
extends = env:esp32dev
|
||||
custom_usermods =
|
||||
${env:esp32dev.custom_usermods}
|
||||
RF433
|
||||
|
||||
# External usermod from a git repository.
|
||||
# The library's `library.json` must include `"build": {"libArchive": false}`.
|
||||
# The name PlatformIO assigns is taken from the library's `library.json` "name" field.
|
||||
# If that name doesn't match the repo name in the URL, use the "LibName = URL" form
|
||||
# shown in the commented-out line below to supply the name explicitly.
|
||||
[env:esp32dev_external_usermod]
|
||||
extends = env:esp32dev
|
||||
custom_usermods =
|
||||
${env:esp32dev.custom_usermods}
|
||||
https://github.com/wled/wled-usermod-example.git#main
|
||||
|
||||
|
||||
|
||||
113
readme.md
113
readme.md
@@ -1,50 +1,87 @@
|
||||
## Welcome to my project WLED!
|
||||
<p align="center">
|
||||
<img src="/images/wled_logo_akemi.png">
|
||||
<a href="https://github.com/wled-dev/WLED/releases"><img src="https://img.shields.io/github/release/wled-dev/WLED.svg?style=flat-square"></a>
|
||||
<a href="https://raw.githubusercontent.com/wled-dev/WLED/main/LICENSE"><img src="https://img.shields.io/github/license/wled-dev/wled?color=blue&style=flat-square"></a>
|
||||
<a href="https://wled.discourse.group"><img src="https://img.shields.io/discourse/topics?colorB=blue&label=forum&server=https%3A%2F%2Fwled.discourse.group%2F&style=flat-square"></a>
|
||||
<a href="https://discord.gg/QAh7wJHrRM"><img src="https://img.shields.io/discord/473448917040758787.svg?colorB=blue&label=discord&style=flat-square"></a>
|
||||
<a href="https://kno.wled.ge"><img src="https://img.shields.io/badge/quick_start-wiki-blue.svg?style=flat-square"></a>
|
||||
<a href="https://github.com/Aircoookie/WLED-App"><img src="https://img.shields.io/badge/app-wled-blue.svg?style=flat-square"></a>
|
||||
<a href="https://gitpod.io/#https://github.com/wled-dev/WLED"><img src="https://img.shields.io/badge/Gitpod-ready--to--code-blue?style=flat-square&logo=gitpod"></a>
|
||||
|
||||
WLED is a fast and (relatively) secure implementation of an ESP8266/ESP32 webserver to control NeoPixel (WS2812B) LEDs!
|
||||
</p>
|
||||
|
||||
### Features: (V0.6.3)
|
||||
- RGB, HSB, and brightness sliders
|
||||
- Settings page - configuration over network
|
||||
- Access Point and station mode - automatic failsafe AP
|
||||
- WS2812FX library integrated for over 50 special effects!
|
||||
- Secondary color support lets you use even more effect combinations
|
||||
- Alexa smart home device server (including dimming)
|
||||
- Beta syncronization to Philips hue lights
|
||||
- Support for RGBW strips
|
||||
- 25 user presets! Save your favorite colors and effects and apply them easily! Now supports cycling through them.
|
||||
- HTTP request API for simple integration
|
||||
- Macro functions to automatically execute API calls
|
||||
- Nightlight function (gradually dims down)
|
||||
- Notifier function (multiple ESPs sync color via UDP broadcast)
|
||||
- Support for power pushbutton
|
||||
- Custom Theater Chase
|
||||
- Support for the Adalight serial ambilight protocol!
|
||||
- Full OTA software update capability (HTTP and ArduinoOTA)
|
||||
- Password protected OTA page for added security (OTA lock)
|
||||
- NTP and configurable analog clock function
|
||||
- Support for the Cronixie Clock kit by Diamex
|
||||
- Realtime UDP Packet Control (WARLS, DRGB, DRGBW) possible
|
||||
- Client HTML UI controlled, customizable themes
|
||||
# Welcome to WLED! ✨
|
||||
|
||||
### Quick start guide and documentation:
|
||||
A fast and feature-rich implementation of an ESP32 and ESP8266 webserver to control NeoPixel (WS2812B, WS2811, SK6812) LEDs or also SPI based chipsets like the WS2801 and APA102!
|
||||
|
||||
See the [wiki](https://github.com/Aircoookie/WLED/wiki)!
|
||||
Originally created by [Aircoookie](https://github.com/Aircoookie)
|
||||
|
||||
### Other
|
||||
## ⚙️ Features
|
||||
- WS2812FX library with more than 100 special effects
|
||||
- FastLED noise effects and 50 palettes
|
||||
- Modern UI with color, effect and segment controls
|
||||
- Segments to set different effects and colors to user defined parts of the LED string
|
||||
- Settings page - configuration via the network
|
||||
- Access Point and station mode - automatic failsafe AP
|
||||
- [Up to 10 LED outputs](https://kno.wled.ge/features/multi-strip/#esp32) per instance
|
||||
- Support for RGBW strips
|
||||
- Up to 250 user presets to save and load colors/effects easily, supports cycling through them.
|
||||
- Presets can be used to automatically execute API calls
|
||||
- Nightlight function (gradually dims down)
|
||||
- Full OTA software updateability (HTTP + ArduinoOTA), password protectable
|
||||
- Configurable analog clock (Cronixie, 7-segment and EleksTube IPS clock support via usermods)
|
||||
- Configurable Auto Brightness limit for safe operation
|
||||
- Filesystem-based config for easier backup of presets and settings
|
||||
|
||||
Licensed under the MIT license
|
||||
Uses libraries:
|
||||
ESP8266/ESP32 Arduino Core
|
||||
NeoPixelBus by Makuna
|
||||
[WS2812FX](https://github.com/kitesurfer1404/WS2812FX) by kitesurfer1404 (Aircoookie fork)
|
||||
Time library
|
||||
Timezone library by JChristensen
|
||||
Alexa code based on arduino-esp8266-alexa-multiple-wemo-switch by kakopappa
|
||||
|
||||
Uses Linearicons by Perxis! (link in settings page)
|
||||
## 💡 Supported light control interfaces
|
||||
- WLED app for [Android](https://play.google.com/store/apps/details?id=ca.cgagnier.wlednativeandroid) and [iOS](https://apps.apple.com/gb/app/wled-native/id6446207239)
|
||||
- JSON and HTTP request APIs
|
||||
- MQTT
|
||||
- E1.31, Art-Net, DDP and TPM2.net
|
||||
- [diyHue](https://github.com/diyhue/diyHue) (Wled is supported by diyHue, including Hue Sync Entertainment under udp. Thanks to [Gregory Mallios](https://github.com/gmallios))
|
||||
- [Hyperion](https://github.com/hyperion-project/hyperion.ng)
|
||||
- UDP realtime
|
||||
- Alexa voice control (including dimming and color)
|
||||
- Sync to Philips hue lights
|
||||
- Adalight (PC ambilight via serial) and TPM2
|
||||
- Sync color of multiple WLED devices (UDP notifier)
|
||||
- Infrared remotes (24-key RGB, receiver required)
|
||||
- Simple timers/schedules (time from NTP, timezones/DST supported)
|
||||
|
||||
## 📲 Quick start guide and documentation
|
||||
|
||||
See the [documentation on our official site](https://kno.wled.ge)!
|
||||
|
||||
[On this page](https://kno.wled.ge/basics/tutorials/) you can find excellent tutorials and tools to help you get your new project up and running!
|
||||
|
||||
## 🖼️ User interface
|
||||
<img src="/images/macbook-pro-space-gray-on-the-wooden-table.jpg" width="50%"><img src="/images/walking-with-iphone-x.jpg" width="50%">
|
||||
|
||||
## 💾 Compatible hardware
|
||||
|
||||
See [here](https://kno.wled.ge/basics/compatible-hardware)!
|
||||
|
||||
## ✌️ Other
|
||||
|
||||
Licensed under the EUPL v1.2 license
|
||||
Credits [here](https://kno.wled.ge/about/contributors/)!
|
||||
CORS proxy by [Corsfix](https://corsfix.com/)
|
||||
|
||||
Join the Discord server to discuss everything about WLED!
|
||||
|
||||
<a href="https://discord.gg/QAh7wJHrRM"><img src="https://discordapp.com/api/guilds/473448917040758787/widget.png?style=banner2" width="25%"></a>
|
||||
|
||||
Check out the WLED [Discourse forum](https://wled.discourse.group)!
|
||||
|
||||
You can also send me mails to [dev.aircoookie@gmail.com](mailto:dev.aircoookie@gmail.com), but please, only do so if you want to talk to me privately.
|
||||
|
||||
If WLED really brightens up your day, you can [](https://paypal.me/aircoookie)
|
||||
|
||||
|
||||
*Disclaimer:*
|
||||
|
||||
If you are prone to photosensitive epilepsy, we recommended you do **not** use this software.
|
||||
If you still want to try, don't use strobe, lighting or noise modes or high effect speed settings.
|
||||
|
||||
As per the EUPL license, I assume no liability for any damage to you or any other person or equipment.
|
||||
|
||||
|
||||
1
requirements.in
Normal file
1
requirements.in
Normal file
@@ -0,0 +1 @@
|
||||
platformio>=6.1.17
|
||||
58
requirements.txt
Normal file
58
requirements.txt
Normal file
@@ -0,0 +1,58 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.11
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile requirements.in
|
||||
#
|
||||
ajsonrpc==1.2.0
|
||||
# via platformio
|
||||
anyio==4.8.0
|
||||
# via starlette
|
||||
bottle==0.13.2
|
||||
# via platformio
|
||||
certifi==2025.1.31
|
||||
# via requests
|
||||
charset-normalizer==3.4.1
|
||||
# via requests
|
||||
click==8.1.8
|
||||
# via
|
||||
# platformio
|
||||
# uvicorn
|
||||
colorama==0.4.6
|
||||
# via platformio
|
||||
h11==0.16.0
|
||||
# via
|
||||
# uvicorn
|
||||
# wsproto
|
||||
idna==3.10
|
||||
# via
|
||||
# anyio
|
||||
# requests
|
||||
marshmallow==3.26.1
|
||||
# via platformio
|
||||
packaging==24.2
|
||||
# via marshmallow
|
||||
platformio==6.1.17
|
||||
# via -r requirements.in
|
||||
pyelftools==0.32
|
||||
# via platformio
|
||||
pyserial==3.5
|
||||
# via platformio
|
||||
requests==2.33.0
|
||||
# via platformio
|
||||
semantic-version==2.10.0
|
||||
# via platformio
|
||||
sniffio==1.3.1
|
||||
# via anyio
|
||||
starlette==0.45.3
|
||||
# via platformio
|
||||
tabulate==0.9.0
|
||||
# via platformio
|
||||
typing-extensions==4.12.2
|
||||
# via anyio
|
||||
urllib3==2.5.0
|
||||
# via requests
|
||||
uvicorn==0.34.0
|
||||
# via platformio
|
||||
wsproto==1.2.0
|
||||
# via platformio
|
||||
11
test/README
Normal file
11
test/README
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
This directory is intended for PIO Unit Testing and project tests.
|
||||
|
||||
Unit Testing is a software testing method by which individual units of
|
||||
source code, sets of one or more MCU program modules together with associated
|
||||
control data, usage procedures, and operating procedures, are tested to
|
||||
determine whether they are fit for use. Unit testing finds problems early
|
||||
in the development cycle.
|
||||
|
||||
More information about PIO Unit Testing:
|
||||
- https://docs.platformio.org/page/plus/unit-testing.html
|
||||
BIN
tools/AutoCubeMap.xlsx
Normal file
BIN
tools/AutoCubeMap.xlsx
Normal file
Binary file not shown.
6
tools/WLED_ESP32-wrover_4MB.csv
Normal file
6
tools/WLED_ESP32-wrover_4MB.csv
Normal file
@@ -0,0 +1,6 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x1A0000,
|
||||
app1, app, ota_1, 0x1B0000,0x1A0000,
|
||||
spiffs, data, spiffs, 0x350000,0xB0000,
|
||||
|
6
tools/WLED_ESP32_16MB.csv
Normal file
6
tools/WLED_ESP32_16MB.csv
Normal file
@@ -0,0 +1,6 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x200000,
|
||||
app1, app, ota_1, 0x210000,0x200000,
|
||||
spiffs, data, spiffs, 0x410000,0xBE0000,
|
||||
|
8
tools/WLED_ESP32_16MB_9MB_FS.csv
Normal file
8
tools/WLED_ESP32_16MB_9MB_FS.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x300000,
|
||||
app1, app, ota_1, 0x310000,0x300000,
|
||||
spiffs, data, spiffs, 0x610000,0x9E0000,
|
||||
coredump, data, coredump,,64K
|
||||
# to create/use ffat, see https://github.com/marcmerlin/esp32_fatfsimage
|
||||
|
5
tools/WLED_ESP32_2MB_noOTA.csv
Normal file
5
tools/WLED_ESP32_2MB_noOTA.csv
Normal file
@@ -0,0 +1,5 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 20K,
|
||||
otadata, data, ota, 0xe000, 8K,
|
||||
app0, app, ota_0, 0x10000, 1536K,
|
||||
spiffs, data, spiffs, 0x190000, 384K,
|
||||
|
7
tools/WLED_ESP32_32MB.csv
Normal file
7
tools/WLED_ESP32_32MB.csv
Normal file
@@ -0,0 +1,7 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x300000,
|
||||
app1, app, ota_1, 0x310000,0x300000,
|
||||
spiffs, data, spiffs, 0x610000,0x19E0000,
|
||||
coredump, data, coredump,,64K
|
||||
|
6
tools/WLED_ESP32_4MB_1MB_FS.csv
Normal file
6
tools/WLED_ESP32_4MB_1MB_FS.csv
Normal file
@@ -0,0 +1,6 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x180000,
|
||||
app1, app, ota_1, 0x190000,0x180000,
|
||||
spiffs, data, spiffs, 0x310000,0xF0000,
|
||||
|
7
tools/WLED_ESP32_4MB_256KB_FS.csv
Normal file
7
tools/WLED_ESP32_4MB_256KB_FS.csv
Normal file
@@ -0,0 +1,7 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x1D0000,
|
||||
app1, app, ota_1, 0x1E0000,0x1D0000,
|
||||
spiffs, data, spiffs, 0x3B0000,0x40000,
|
||||
coredump, data, coredump,,64K
|
||||
|
7
tools/WLED_ESP32_4MB_512KB_FS.csv
Normal file
7
tools/WLED_ESP32_4MB_512KB_FS.csv
Normal file
@@ -0,0 +1,7 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x1B0000,
|
||||
app1, app, ota_1, 0x1C0000,0x1B0000,
|
||||
spiffs, data, spiffs, 0x370000,0x80000,
|
||||
coredump, data, coredump,,64K
|
||||
|
6
tools/WLED_ESP32_4MB_700k_FS.csv
Normal file
6
tools/WLED_ESP32_4MB_700k_FS.csv
Normal file
@@ -0,0 +1,6 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x1A0000,
|
||||
app1, app, ota_1, 0x1B0000,0x1A0000,
|
||||
spiffs, data, spiffs, 0x350000,0xB0000,
|
||||
|
7
tools/WLED_ESP32_8MB.csv
Normal file
7
tools/WLED_ESP32_8MB.csv
Normal file
@@ -0,0 +1,7 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x200000,
|
||||
app1, app, ota_1, 0x210000,0x200000,
|
||||
spiffs, data, spiffs, 0x410000,0x3E0000,
|
||||
coredump, data, coredump,,64K
|
||||
|
17
tools/all_xml.sh
Normal file
17
tools/all_xml.sh
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
# Pull all settings pages for comparison
|
||||
HOST=$1
|
||||
TGT_PATH=$2
|
||||
CURL_ARGS="--compressed"
|
||||
|
||||
# Replicate one target many times
|
||||
function replicate() {
|
||||
for i in {0..10}
|
||||
do
|
||||
echo -n " http://${HOST}/settings.js?p=$i -o ${TGT_PATH}/$i.xml"
|
||||
done
|
||||
}
|
||||
read -a TARGETS <<< $(replicate)
|
||||
|
||||
mkdir -p ${TGT_PATH}
|
||||
curl ${CURL_ARGS} ${TARGETS[@]}
|
||||
218
tools/cdata-test.js
Normal file
218
tools/cdata-test.js
Normal file
@@ -0,0 +1,218 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('node:assert');
|
||||
const { describe, it, before, after } = require('node:test');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const child_process = require('child_process');
|
||||
const util = require('util');
|
||||
const execPromise = util.promisify(child_process.exec);
|
||||
|
||||
process.env.NODE_ENV = 'test'; // Set the environment to testing
|
||||
const cdata = require('./cdata.js');
|
||||
|
||||
describe('Function', () => {
|
||||
const testFolderPath = path.join(__dirname, 'testFolder');
|
||||
const oldFilePath = path.join(testFolderPath, 'oldFile.txt');
|
||||
const newFilePath = path.join(testFolderPath, 'newFile.txt');
|
||||
|
||||
// Create a temporary file before the test
|
||||
before(() => {
|
||||
// Create test folder
|
||||
if (!fs.existsSync(testFolderPath)) {
|
||||
fs.mkdirSync(testFolderPath);
|
||||
}
|
||||
|
||||
// Create an old file
|
||||
fs.writeFileSync(oldFilePath, 'This is an old file.');
|
||||
// Modify the 'mtime' to simulate an old file
|
||||
const oldTime = new Date();
|
||||
oldTime.setFullYear(oldTime.getFullYear() - 1);
|
||||
fs.utimesSync(oldFilePath, oldTime, oldTime);
|
||||
|
||||
// Create a new file
|
||||
fs.writeFileSync(newFilePath, 'This is a new file.');
|
||||
});
|
||||
|
||||
// delete the temporary files after the test
|
||||
after(() => {
|
||||
fs.rmSync(testFolderPath, { recursive: true });
|
||||
});
|
||||
|
||||
describe('isFileNewerThan', async () => {
|
||||
it('should return true if the file is newer than the provided time', async () => {
|
||||
const pastTime = Date.now() - 10000; // 10 seconds ago
|
||||
assert.strictEqual(cdata.isFileNewerThan(newFilePath, pastTime), true);
|
||||
});
|
||||
|
||||
it('should return false if the file is older than the provided time', async () => {
|
||||
assert.strictEqual(cdata.isFileNewerThan(oldFilePath, Date.now()), false);
|
||||
});
|
||||
|
||||
it('should throw an exception if the file does not exist', async () => {
|
||||
assert.throws(() => {
|
||||
cdata.isFileNewerThan('nonexistent.txt', Date.now());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('isAnyFileInFolderNewerThan', async () => {
|
||||
it('should return true if a file in the folder is newer than the given time', async () => {
|
||||
const time = fs.statSync(path.join(testFolderPath, 'oldFile.txt')).mtime;
|
||||
assert.strictEqual(cdata.isAnyFileInFolderNewerThan(testFolderPath, time), true);
|
||||
});
|
||||
|
||||
it('should return false if no files in the folder are newer than the given time', async () => {
|
||||
assert.strictEqual(cdata.isAnyFileInFolderNewerThan(testFolderPath, new Date()), false);
|
||||
});
|
||||
|
||||
it('should throw an exception if the folder does not exist', async () => {
|
||||
assert.throws(() => {
|
||||
cdata.isAnyFileInFolderNewerThan('nonexistent', new Date());
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Script', () => {
|
||||
const folderPath = 'wled00';
|
||||
const dataPath = path.join(folderPath, 'data');
|
||||
|
||||
before(() => {
|
||||
process.env.NODE_ENV = 'production';
|
||||
// Backup files
|
||||
fs.cpSync("wled00/data", "wled00Backup", { recursive: true });
|
||||
fs.cpSync("tools/cdata.js", "cdata.bak.js");
|
||||
fs.cpSync("package.json", "package.bak.json");
|
||||
});
|
||||
after(() => {
|
||||
// Restore backup
|
||||
fs.rmSync("wled00/data", { recursive: true });
|
||||
fs.renameSync("wled00Backup", "wled00/data");
|
||||
fs.rmSync("tools/cdata.js");
|
||||
fs.renameSync("cdata.bak.js", "tools/cdata.js");
|
||||
fs.rmSync("package.json");
|
||||
fs.renameSync("package.bak.json", "package.json");
|
||||
});
|
||||
|
||||
// delete all html_*.h files
|
||||
async function deleteBuiltFiles() {
|
||||
const files = await fs.promises.readdir(folderPath);
|
||||
await Promise.all(files.map(file => {
|
||||
if (file.startsWith('html_') && path.extname(file) === '.h') {
|
||||
return fs.promises.unlink(path.join(folderPath, file));
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// check if html_*.h files were created
|
||||
async function checkIfBuiltFilesExist() {
|
||||
const files = await fs.promises.readdir(folderPath);
|
||||
const htmlFiles = files.filter(file => file.startsWith('html_') && path.extname(file) === '.h');
|
||||
assert(htmlFiles.length > 0, 'html_*.h files were not created');
|
||||
}
|
||||
|
||||
async function runAndCheckIfBuiltFilesExist() {
|
||||
await execPromise('node tools/cdata.js');
|
||||
await checkIfBuiltFilesExist();
|
||||
}
|
||||
|
||||
async function checkIfFileWasNewlyCreated(file) {
|
||||
const modifiedTime = fs.statSync(file).mtimeMs;
|
||||
assert(Date.now() - modifiedTime < 850, file + ' was not modified');
|
||||
}
|
||||
|
||||
async function testFileModification(sourceFilePath, resultFile) {
|
||||
// run cdata.js to ensure html_*.h files are created
|
||||
await execPromise('node tools/cdata.js');
|
||||
|
||||
// modify file
|
||||
fs.appendFileSync(sourceFilePath, ' ');
|
||||
// delay for 1 second to ensure the modified time is different
|
||||
await new Promise(resolve => setTimeout(resolve, 1400));
|
||||
|
||||
// run script cdata.js again and wait for it to finish
|
||||
await execPromise('node tools/cdata.js');
|
||||
|
||||
await checkIfFileWasNewlyCreated(path.join(folderPath, resultFile));
|
||||
}
|
||||
|
||||
describe('should build if', () => {
|
||||
it('html_*.h files are missing', async () => {
|
||||
await deleteBuiltFiles();
|
||||
await runAndCheckIfBuiltFilesExist();
|
||||
});
|
||||
|
||||
it('only one html_*.h file is missing', async () => {
|
||||
// run script cdata.js and wait for it to finish
|
||||
await execPromise('node tools/cdata.js');
|
||||
|
||||
// delete a random html_*.h file
|
||||
let files = await fs.promises.readdir(folderPath);
|
||||
let htmlFiles = files.filter(file => file.startsWith('html_') && path.extname(file) === '.h');
|
||||
const randomFile = htmlFiles[Math.floor(Math.random() * htmlFiles.length)];
|
||||
await fs.promises.unlink(path.join(folderPath, randomFile));
|
||||
|
||||
await runAndCheckIfBuiltFilesExist();
|
||||
});
|
||||
|
||||
it('script was executed with -f or --force', async () => {
|
||||
await execPromise('node tools/cdata.js');
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
await execPromise('node tools/cdata.js --force');
|
||||
await checkIfFileWasNewlyCreated(path.join(folderPath, 'html_ui.h'));
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
await execPromise('node tools/cdata.js -f');
|
||||
await checkIfFileWasNewlyCreated(path.join(folderPath, 'html_ui.h'));
|
||||
});
|
||||
|
||||
it('a file changes', async () => {
|
||||
await testFileModification(path.join(dataPath, 'index.htm'), 'html_ui.h');
|
||||
});
|
||||
|
||||
it('a inlined file changes', async () => {
|
||||
await testFileModification(path.join(dataPath, 'index.js'), 'html_ui.h');
|
||||
});
|
||||
|
||||
it('a settings file changes', async () => {
|
||||
await testFileModification(path.join(dataPath, 'settings_leds.htm'), 'html_settings.h');
|
||||
});
|
||||
|
||||
it('common.js changes', async () => {
|
||||
await testFileModification(path.join(dataPath, 'common.js'), 'html_settings.h');
|
||||
});
|
||||
|
||||
// this testcase currently fails - might be due to npm updates (maybe "faking" a favicon.ico change is harder now), or a real regression
|
||||
// see https://github.com/wled/WLED/issues/5581
|
||||
// it('the favicon changes', async () => {
|
||||
// await testFileModification(path.join(dataPath, 'favicon.ico'), 'html_other.h');
|
||||
// });
|
||||
|
||||
it('cdata.js changes', async () => {
|
||||
await testFileModification('tools/cdata.js', 'html_ui.h');
|
||||
});
|
||||
|
||||
it('package.json changes', async () => {
|
||||
await testFileModification('package.json', 'html_ui.h');
|
||||
});
|
||||
});
|
||||
|
||||
describe('should not build if', () => {
|
||||
it('the files are already built', async () => {
|
||||
await deleteBuiltFiles();
|
||||
|
||||
// run script cdata.js and wait for it to finish
|
||||
let startTime = Date.now();
|
||||
await execPromise('node tools/cdata.js');
|
||||
const firstRunTime = Date.now() - startTime;
|
||||
|
||||
// run script cdata.js and wait for it to finish
|
||||
startTime = Date.now();
|
||||
await execPromise('node tools/cdata.js');
|
||||
const secondRunTime = Date.now() - startTime;
|
||||
|
||||
// check if second run was faster than the first (must be at least 2x faster)
|
||||
assert(secondRunTime < firstRunTime / 2, 'html_*.h files were rebuilt');
|
||||
});
|
||||
});
|
||||
});
|
||||
480
tools/cdata.js
Normal file
480
tools/cdata.js
Normal file
@@ -0,0 +1,480 @@
|
||||
/**
|
||||
* Writes compressed C arrays of data files (web interface)
|
||||
* How to use it?
|
||||
*
|
||||
* 1) Install Node 20+ and npm
|
||||
* 2) npm install
|
||||
* 3) npm run build
|
||||
*
|
||||
* If you change data folder often, you can run it in monitoring mode (it will recompile and update *.h on every file change)
|
||||
*
|
||||
* > npm run dev
|
||||
*
|
||||
* How it works?
|
||||
*
|
||||
* It uses NodeJS packages to inline, minify and GZIP files. See writeHtmlGzipped and writeChunks invocations at the bottom of the page.
|
||||
*/
|
||||
|
||||
const fs = require("node:fs");
|
||||
const path = require("path");
|
||||
const inline = require("web-resource-inliner");
|
||||
const zlib = require("node:zlib");
|
||||
const CleanCSS = require("clean-css");
|
||||
const minifyHtml = require("html-minifier-terser").minify;
|
||||
const packageJson = require("../package.json");
|
||||
|
||||
// Export functions for testing
|
||||
module.exports = { isFileNewerThan, isAnyFileInFolderNewerThan };
|
||||
|
||||
const output = ["wled00/html_ui.h", "wled00/html_pixart.h", "wled00/html_cpal.h", "wled00/html_edit.h", "wled00/html_pxmagic.h", "wled00/html_pixelforge.h", "wled00/html_settings.h", "wled00/html_other.h", "wled00/js_iro.h", "wled00/js_omggif.h"]
|
||||
|
||||
// \x1b[34m is blue, \x1b[36m is cyan, \x1b[0m is reset
|
||||
const wledBanner = `
|
||||
\t\x1b[34m ## ## ## ###### ######
|
||||
\t\x1b[34m## ## ## ## ## ## ##
|
||||
\t\x1b[34m## ## ## ## ###### ## ##
|
||||
\t\x1b[34m## ## ## ## ## ## ##
|
||||
\t\x1b[34m ## ## ###### ###### ######
|
||||
\t\t\x1b[36m build script for web UI
|
||||
\x1b[0m`;
|
||||
|
||||
// Generate build timestamp as UNIX timestamp (seconds since epoch)
|
||||
function generateBuildTime() {
|
||||
return Math.floor(Date.now() / 1000);
|
||||
}
|
||||
|
||||
const singleHeader = `/*
|
||||
* Binary array for the Web UI.
|
||||
* gzip is used for smaller size and improved speeds.
|
||||
*
|
||||
* Please see https://kno.wled.ge/advanced/custom-features/#changing-web-ui
|
||||
* to find out how to easily modify the web UI source!
|
||||
*/
|
||||
|
||||
// Automatically generated build time for cache busting (UNIX timestamp)
|
||||
#define WEB_BUILD_TIME ${generateBuildTime()}
|
||||
|
||||
`;
|
||||
|
||||
const multiHeader = `/*
|
||||
* More web UI HTML source arrays.
|
||||
* This file is auto generated, please don't make any changes manually.
|
||||
*
|
||||
* Instead, see https://kno.wled.ge/advanced/custom-features/#changing-web-ui
|
||||
* to find out how to easily modify the web UI source!
|
||||
*/
|
||||
`;
|
||||
|
||||
function hexdump(buffer, isHex = false) {
|
||||
let lines = [];
|
||||
|
||||
for (let i = 0; i < buffer.length; i += (isHex ? 32 : 16)) {
|
||||
var block;
|
||||
let hexArray = [];
|
||||
if (isHex) {
|
||||
block = buffer.slice(i, i + 32)
|
||||
for (let j = 0; j < block.length; j += 2) {
|
||||
hexArray.push("0x" + block.slice(j, j + 2))
|
||||
}
|
||||
} else {
|
||||
block = buffer.slice(i, i + 16); // cut buffer into blocks of 16
|
||||
for (let value of block) {
|
||||
hexArray.push("0x" + value.toString(16).padStart(2, "0"));
|
||||
}
|
||||
}
|
||||
|
||||
let hexString = hexArray.join(", ");
|
||||
let line = ` ${hexString}`;
|
||||
lines.push(line);
|
||||
}
|
||||
|
||||
return lines.join(",\n");
|
||||
}
|
||||
|
||||
function adoptVersionAndRepo(html) {
|
||||
let repoUrl = packageJson.repository ? packageJson.repository.url : undefined;
|
||||
if (repoUrl) {
|
||||
repoUrl = repoUrl.replace(/^git\+/, "");
|
||||
repoUrl = repoUrl.replace(/\.git$/, "");
|
||||
html = html.replaceAll("https://github.com/atuline/WLED", repoUrl);
|
||||
html = html.replaceAll("https://github.com/wled-dev/WLED", repoUrl);
|
||||
}
|
||||
let version = packageJson.version;
|
||||
if (version) {
|
||||
html = html.replaceAll("##VERSION##", version);
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
async function minify(str, type = "plain") {
|
||||
const options = {
|
||||
collapseWhitespace: true,
|
||||
conservativeCollapse: true, // preserve spaces in text
|
||||
collapseBooleanAttributes: true,
|
||||
collapseInlineTagWhitespace: true,
|
||||
minifyCSS: true,
|
||||
minifyJS: true,
|
||||
removeAttributeQuotes: true,
|
||||
removeComments: true,
|
||||
sortAttributes: true,
|
||||
sortClassName: true,
|
||||
};
|
||||
|
||||
if (type == "plain") {
|
||||
return str;
|
||||
} else if (type == "css-minify") {
|
||||
return new CleanCSS({}).minify(str).styles;
|
||||
} else if (type == "js-minify") {
|
||||
let js = await minifyHtml('<script>' + str + '</script>', options);
|
||||
return js.replace(/<[\/]*script>/g, '');
|
||||
} else if (type == "html-minify") {
|
||||
return await minifyHtml(str, options);
|
||||
}
|
||||
|
||||
throw new Error("Unknown filter: " + type);
|
||||
}
|
||||
|
||||
async function writeHtmlGzipped(sourceFile, resultFile, page, inlineCss = true) {
|
||||
console.info("Reading " + sourceFile);
|
||||
inline.html({
|
||||
fileContent: fs.readFileSync(sourceFile, "utf8"),
|
||||
relativeTo: path.dirname(sourceFile),
|
||||
strict: inlineCss, // when not inlining css, ignore errors (enables linking style.css from subfolder htm files)
|
||||
stylesheets: inlineCss // when true (default), css is inlined
|
||||
},
|
||||
async function (error, html) {
|
||||
if (error) throw error;
|
||||
|
||||
html = adoptVersionAndRepo(html);
|
||||
const originalLength = html.length;
|
||||
html = await minify(html, "html-minify");
|
||||
const result = zlib.gzipSync(html, { level: zlib.constants.Z_BEST_COMPRESSION });
|
||||
console.info("Minified and compressed " + sourceFile + " from " + originalLength + " to " + result.length + " bytes");
|
||||
const array = hexdump(result);
|
||||
let src = singleHeader;
|
||||
src += `const uint16_t PAGE_${page}_length = ${result.length};\n`;
|
||||
src += `const uint8_t PAGE_${page}[] PROGMEM = {\n${array}\n};\n\n`;
|
||||
console.info("Writing " + resultFile);
|
||||
fs.writeFileSync(resultFile, src);
|
||||
});
|
||||
}
|
||||
|
||||
async function specToChunk(srcDir, s) {
|
||||
const buf = fs.readFileSync(srcDir + "/" + s.file);
|
||||
let chunk = `\n// Autogenerated from ${srcDir}/${s.file}, do not edit!!\n`
|
||||
|
||||
if (s.method == "plaintext" || s.method == "gzip") {
|
||||
let str = buf.toString("utf-8");
|
||||
str = adoptVersionAndRepo(str);
|
||||
const originalLength = str.length;
|
||||
if (s.method == "gzip") {
|
||||
if (s.mangle) str = s.mangle(str);
|
||||
const zip = zlib.gzipSync(await minify(str, s.filter), { level: zlib.constants.Z_BEST_COMPRESSION });
|
||||
console.info("Minified and compressed " + s.file + " from " + originalLength + " to " + zip.length + " bytes");
|
||||
const result = hexdump(zip);
|
||||
chunk += `const uint16_t ${s.name}_length = ${zip.length};\n`;
|
||||
chunk += `const uint8_t ${s.name}[] PROGMEM = {\n${result}\n};\n\n`;
|
||||
return chunk;
|
||||
} else {
|
||||
const minified = await minify(str, s.filter);
|
||||
console.info("Minified " + s.file + " from " + originalLength + " to " + minified.length + " bytes");
|
||||
chunk += `const char ${s.name}[] PROGMEM = R"${s.prepend || ""}${minified}${s.append || ""}";\n\n`;
|
||||
return s.mangle ? s.mangle(chunk) : chunk;
|
||||
}
|
||||
} else if (s.method == "binary") {
|
||||
const result = hexdump(buf);
|
||||
chunk += `const uint16_t ${s.name}_length = ${buf.length};\n`;
|
||||
chunk += `const uint8_t ${s.name}[] PROGMEM = {\n${result}\n};\n\n`;
|
||||
return chunk;
|
||||
}
|
||||
|
||||
throw new Error("Unknown method: " + s.method);
|
||||
}
|
||||
|
||||
async function writeChunks(srcDir, specs, resultFile) {
|
||||
let src = multiHeader;
|
||||
for (const s of specs) {
|
||||
console.info("Reading " + srcDir + "/" + s.file + " as " + s.name);
|
||||
src += await specToChunk(srcDir, s);
|
||||
}
|
||||
console.info("Writing " + src.length + " characters into " + resultFile);
|
||||
fs.writeFileSync(resultFile, src);
|
||||
}
|
||||
|
||||
// Check if a file is newer than a given time
|
||||
function isFileNewerThan(filePath, time) {
|
||||
const stats = fs.statSync(filePath);
|
||||
return stats.mtimeMs > time;
|
||||
}
|
||||
|
||||
// Check if any file in a folder (or its subfolders) is newer than a given time
|
||||
function isAnyFileInFolderNewerThan(folderPath, time) {
|
||||
const files = fs.readdirSync(folderPath, { withFileTypes: true });
|
||||
for (const file of files) {
|
||||
const filePath = path.join(folderPath, file.name);
|
||||
if (isFileNewerThan(filePath, time)) {
|
||||
return true;
|
||||
}
|
||||
if (file.isDirectory() && isAnyFileInFolderNewerThan(filePath, time)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the web UI is already built
|
||||
function isAlreadyBuilt(webUIPath, packageJsonPath = "package.json") {
|
||||
let lastBuildTime = Infinity;
|
||||
|
||||
for (const file of output) {
|
||||
try {
|
||||
lastBuildTime = Math.min(lastBuildTime, fs.statSync(file).mtimeMs);
|
||||
} catch (e) {
|
||||
if (e.code !== 'ENOENT') throw e;
|
||||
console.info("File " + file + " does not exist. Rebuilding...");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return !isAnyFileInFolderNewerThan(webUIPath, lastBuildTime) && !isFileNewerThan(packageJsonPath, lastBuildTime) && !isFileNewerThan(__filename, lastBuildTime);
|
||||
}
|
||||
|
||||
// Don't run this script if we're in a test environment
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
return;
|
||||
}
|
||||
|
||||
console.info(wledBanner);
|
||||
|
||||
if (isAlreadyBuilt("wled00/data") && process.argv[2] !== '--force' && process.argv[2] !== '-f') {
|
||||
console.info("Web UI is already built");
|
||||
return;
|
||||
}
|
||||
|
||||
writeHtmlGzipped("wled00/data/index.htm", "wled00/html_ui.h", 'index');
|
||||
writeHtmlGzipped("wled00/data/pixart/pixart.htm", "wled00/html_pixart.h", 'pixart');
|
||||
writeHtmlGzipped("wled00/data/pxmagic/pxmagic.htm", "wled00/html_pxmagic.h", 'pxmagic');
|
||||
writeHtmlGzipped("wled00/data/pixelforge/pixelforge.htm", "wled00/html_pixelforge.h", 'pixelforge', false); // do not inline css
|
||||
//writeHtmlGzipped("wled00/data/edit.htm", "wled00/html_edit.h", 'edit');
|
||||
|
||||
writeChunks(
|
||||
"wled00/data/",
|
||||
[
|
||||
{
|
||||
file: "iro.js",
|
||||
name: "JS_iro",
|
||||
method: "gzip",
|
||||
filter: "plain", // no minification, it is already minified
|
||||
mangle: (s) => s.replace(/^\/\*![\s\S]*?\*\//, '') // remove license comment at the top
|
||||
}
|
||||
],
|
||||
"wled00/js_iro.h"
|
||||
);
|
||||
|
||||
writeChunks(
|
||||
"wled00/data/pixelforge",
|
||||
[
|
||||
{
|
||||
file: "omggif.js",
|
||||
name: "JS_omggif",
|
||||
method: "gzip",
|
||||
filter: "js-minify",
|
||||
mangle: (s) => s.replace(/^\/\*![\s\S]*?\*\//, '') // remove license comment at the top
|
||||
}
|
||||
],
|
||||
"wled00/js_omggif.h"
|
||||
);
|
||||
|
||||
writeChunks(
|
||||
"wled00/data",
|
||||
[
|
||||
{
|
||||
file: "edit.htm",
|
||||
name: "PAGE_edit",
|
||||
method: "gzip",
|
||||
filter: "html-minify"
|
||||
}
|
||||
],
|
||||
"wled00/html_edit.h"
|
||||
);
|
||||
|
||||
writeChunks(
|
||||
"wled00/data/cpal",
|
||||
[
|
||||
{
|
||||
file: "cpal.htm",
|
||||
name: "PAGE_cpal",
|
||||
method: "gzip",
|
||||
filter: "html-minify"
|
||||
}
|
||||
],
|
||||
"wled00/html_cpal.h"
|
||||
);
|
||||
|
||||
writeChunks(
|
||||
"wled00/data",
|
||||
[
|
||||
{
|
||||
file: "style.css",
|
||||
name: "PAGE_settingsCss",
|
||||
method: "gzip",
|
||||
filter: "css-minify",
|
||||
mangle: (str) =>
|
||||
str
|
||||
.replace("%%", "%")
|
||||
},
|
||||
{
|
||||
file: "common.js",
|
||||
name: "JS_common",
|
||||
method: "gzip",
|
||||
filter: "js-minify",
|
||||
},
|
||||
{
|
||||
file: "settings.htm",
|
||||
name: "PAGE_settings",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "settings_wifi.htm",
|
||||
name: "PAGE_settings_wifi",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "settings_leds.htm",
|
||||
name: "PAGE_settings_leds",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "settings_dmx.htm",
|
||||
name: "PAGE_settings_dmx",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "settings_ui.htm",
|
||||
name: "PAGE_settings_ui",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "settings_sync.htm",
|
||||
name: "PAGE_settings_sync",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "settings_time.htm",
|
||||
name: "PAGE_settings_time",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "settings_sec.htm",
|
||||
name: "PAGE_settings_sec",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "settings_um.htm",
|
||||
name: "PAGE_settings_um",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "settings_2D.htm",
|
||||
name: "PAGE_settings_2D",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "settings_pin.htm",
|
||||
name: "PAGE_settings_pin",
|
||||
method: "gzip",
|
||||
filter: "html-minify"
|
||||
},
|
||||
{
|
||||
file: "settings_pininfo.htm",
|
||||
name: "PAGE_settings_pininfo",
|
||||
method: "gzip",
|
||||
filter: "html-minify"
|
||||
}
|
||||
],
|
||||
"wled00/html_settings.h"
|
||||
);
|
||||
|
||||
writeChunks(
|
||||
"wled00/data",
|
||||
[
|
||||
{
|
||||
file: "usermod.htm",
|
||||
name: "PAGE_usermod",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
mangle: (str) =>
|
||||
str.replace(/fetch\("http\:\/\/.*\/win/gms, 'fetch("/win'),
|
||||
},
|
||||
{
|
||||
file: "msg.htm",
|
||||
name: "PAGE_msg",
|
||||
prepend: "=====(",
|
||||
append: ")=====",
|
||||
method: "plaintext",
|
||||
filter: "html-minify",
|
||||
mangle: (str) => str.replace(/\<h2\>.*\<\/body\>/gms, "<h2>%MSG%</body>"),
|
||||
},
|
||||
{
|
||||
file: "dmxmap.htm",
|
||||
name: "PAGE_dmxmap",
|
||||
prepend: "=====(",
|
||||
append: ")=====",
|
||||
method: "plaintext",
|
||||
filter: "html-minify",
|
||||
mangle: (str) => `
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
${str.replace(/function FM\(\)[ ]?\{/gms, "function FM() {%DMXVARS%\n")}
|
||||
#else
|
||||
const char PAGE_dmxmap[] PROGMEM = R"=====()=====";
|
||||
#endif
|
||||
`,
|
||||
},
|
||||
{
|
||||
file: "update.htm",
|
||||
name: "PAGE_update",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "welcome.htm",
|
||||
name: "PAGE_welcome",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "liveview.htm",
|
||||
name: "PAGE_liveview",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "liveviewws2D.htm",
|
||||
name: "PAGE_liveviewws2D",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "404.htm",
|
||||
name: "PAGE_404",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "favicon.ico",
|
||||
name: "favicon",
|
||||
method: "binary",
|
||||
}
|
||||
],
|
||||
"wled00/html_other.h"
|
||||
);
|
||||
232
tools/fps_test.htm
Normal file
232
tools/fps_test.htm
Normal file
@@ -0,0 +1,232 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>WLED frame rate test tool</title>
|
||||
<style>
|
||||
body {
|
||||
background-color: #222;
|
||||
color: #fff;
|
||||
font-family: Helvetica, Verdana, sans-serif;
|
||||
}
|
||||
input {
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
}
|
||||
#ip {
|
||||
width: 100px;
|
||||
}
|
||||
#secs {
|
||||
width: 36px;
|
||||
}
|
||||
#csva {
|
||||
position: absolute;
|
||||
top: -100px; /*gtfo*/
|
||||
}
|
||||
button {
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
}
|
||||
table, th, td {
|
||||
border: 1px solid #aaa;
|
||||
border-collapse: collapse;
|
||||
text-align: center;
|
||||
}
|
||||
.red {
|
||||
color: #d20;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
var gotfx = false, running = false;
|
||||
var pos = 0, prev = 0, min = 999, max = 0, fpslist = [], names = [], names_checked = [];
|
||||
var to;
|
||||
function S() {
|
||||
document.getElementById('ip').value = localStorage.getItem('locIpFps');
|
||||
if (document.getElementById('ip').value) req(false);
|
||||
}
|
||||
function loadC() {
|
||||
hide(false);
|
||||
var list = localStorage.getItem('fpsFxSelection');
|
||||
if (!list) return;
|
||||
list = JSON.parse(list);
|
||||
var chks = document.querySelectorAll('.fxcheck');
|
||||
for (let i = 0; i < chks.length; i++) {
|
||||
if (i < list.length) chks[i].checked = list[i];
|
||||
}
|
||||
}
|
||||
function saveC() {
|
||||
var list = [];
|
||||
var chks = document.querySelectorAll('.fxcheck');
|
||||
for (let i = 0; i < chks.length; i++) {
|
||||
list.push(chks[i].checked);
|
||||
}
|
||||
localStorage.setItem('fpsFxSelection', JSON.stringify(list));
|
||||
}
|
||||
function setC(c) {
|
||||
hide(false);
|
||||
var chks = document.querySelectorAll('.fxcheck');
|
||||
for (let i = 0; i < chks.length; i++) {
|
||||
chks[i].checked = (c == 255);
|
||||
}
|
||||
if (c == 1 && chks.length > 100) {
|
||||
chks[1].checked = true; //Blink
|
||||
chks[15].checked = true; //Running
|
||||
chks[16].checked = true; //Saw
|
||||
chks[37].checked = true; //Running 2
|
||||
chks[44].checked = true; //Tetrix
|
||||
chks[63].checked = true; //Pride 2015
|
||||
chks[74].checked = true; //Colortwinkles
|
||||
chks[101].checked = true;//Pacifica
|
||||
}
|
||||
}
|
||||
function hide(h) {
|
||||
var trs = document.querySelectorAll('.trs');
|
||||
var chks = document.querySelectorAll('.fxcheck');
|
||||
for (let i = 0; i < trs.length; i++) {
|
||||
trs[i].style.display = (h && !chks[i].checked) ? "none":"table-row";
|
||||
}
|
||||
}
|
||||
function run(init) {
|
||||
if (init) {
|
||||
running = !running;
|
||||
document.getElementById('runbtn').innerText = running ? 'Stop':'Run';
|
||||
if (running) {pos = 0; prev = -1; min = 999; max = 0; fpslist = []; names_checked = []; hide(true);}
|
||||
clearTimeout(to);
|
||||
if (!running) {req({seg:{fx:0},v:true,stop:true}); return;}
|
||||
}
|
||||
if (!gotfx) {req(false); return;}
|
||||
var chks = document.querySelectorAll('.fxcheck');
|
||||
var fpsb = document.querySelectorAll('.fps');
|
||||
if (prev >= 0) {pos++};
|
||||
if (pos >= chks.length) {run(true); return;} //end
|
||||
while (!chks[pos].checked) {
|
||||
fpsb[pos].innerText = "-";
|
||||
pos++;
|
||||
if (pos >= chks.length) {run(true); return;} //end
|
||||
}
|
||||
names_checked.push(names[pos]);
|
||||
var extra = {};
|
||||
try {
|
||||
extra = JSON.parse(document.getElementById('ej').value);
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
var cmd = {seg:{fx:pos},v:true};
|
||||
Object.assign(cmd, extra);
|
||||
req(cmd);
|
||||
}
|
||||
function req(command) {
|
||||
var ip = document.getElementById('ip').value;
|
||||
if (!ip) {alert("Please enter WLED IP"); return;}
|
||||
if (ip != localStorage.getItem('locIpFps')) localStorage.setItem('locIpFps', document.getElementById('ip').value);
|
||||
var url = command ? `http://${ip}/json/si` : `http://${ip}/json/effects`;
|
||||
var type = command ? 'post':'get';
|
||||
var req = undefined;
|
||||
if (command)
|
||||
{
|
||||
req = JSON.stringify(command);
|
||||
}
|
||||
fetch
|
||||
(url, {
|
||||
method: type,
|
||||
headers: {
|
||||
"Content-type": "application/json; charset=UTF-8"
|
||||
},
|
||||
body: req
|
||||
})
|
||||
.then(res => {
|
||||
if (!res.ok) {
|
||||
alert('Data malfunction');
|
||||
}
|
||||
return res.json();
|
||||
})
|
||||
.then(json => {
|
||||
if (!json) {
|
||||
alert('Empty response'); return;
|
||||
}
|
||||
if (!command) {
|
||||
names = json;
|
||||
var tblc = '';
|
||||
for (let i = 0; i < json.length; i++) {
|
||||
tblc += `<tr class="trs"><td><input type="checkbox" class="fxcheck" /></td><td>${i}</td><td>${json[i]}</td><td class="fps"></td></tr>`
|
||||
}
|
||||
var tbl = `<table>
|
||||
<tr>
|
||||
<th>Test?</th><th>ID</th><th>Effect Name</th><th>FPS</th>
|
||||
</tr>
|
||||
${tblc}
|
||||
</table>`;
|
||||
document.getElementById('tablecon').innerHTML = tbl;
|
||||
setC(1);
|
||||
loadC();
|
||||
gotfx = true;
|
||||
document.getElementById('runbtn').innerText = "Run";
|
||||
} else {
|
||||
if (!json.info) return;
|
||||
document.getElementById('leds').innerText = json.info.leds.count;
|
||||
document.getElementById('seg').innerText = json.state.seg[0].len;
|
||||
document.getElementById('bri').innerText = json.state.bri;
|
||||
if (prev >= 0) {
|
||||
var lastfps = parseInt(json.info.leds.fps); //previous FX
|
||||
if (lastfps < min) min = lastfps;
|
||||
if (lastfps > max) max = lastfps;
|
||||
fpslist.push(lastfps);
|
||||
var sum = 0;
|
||||
for (let i = 0; i < fpslist.length; i++) {
|
||||
sum += fpslist[i];
|
||||
}
|
||||
sum /= fpslist.length;
|
||||
document.getElementById('fps_min').innerText = min;
|
||||
document.getElementById('fps_max').innerText = max;
|
||||
document.getElementById('fps_avg').innerText = Math.round(sum*10)/10;
|
||||
var fpsb = document.querySelectorAll('.fps');
|
||||
fpsb[prev].innerHTML = lastfps;
|
||||
}
|
||||
prev = pos;
|
||||
var delay = parseInt(document.getElementById('secs').value)*1000;
|
||||
delay = Math.min(Math.max(delay, 2000), 15000)
|
||||
if (!command.stop) to = setTimeout(run,delay);
|
||||
}
|
||||
})
|
||||
.catch(function (error) {
|
||||
alert('Comms malfunction');
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
function csv(n) {
|
||||
var txt = "";
|
||||
for (let i = 0; i < fpslist.length; i++) {
|
||||
if (!n) txt += names_checked[i] + ',';
|
||||
txt += fpslist[i]; txt += "\n";
|
||||
}
|
||||
document.getElementById('csva').value = txt;
|
||||
var copyText = document.getElementById('csva');
|
||||
|
||||
copyText.select();
|
||||
copyText.setSelectionRange(0, 999999);
|
||||
document.execCommand("copy");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="S()">
|
||||
<h2>Starship monitoring dashboard</h2>
|
||||
(or rather just a WLED frame rate tester lol)<br><br>
|
||||
IP: <input id="ip" /><br>
|
||||
Time per effect: <input type=number id=secs value=5 max=15 min=2 />s<br>
|
||||
Effects to test:
|
||||
<button type="button" onclick="setC(255)">All</button>
|
||||
<button type="button" onclick="setC(1)">Selection 1</button>
|
||||
<button type="button" onclick="setC(0)">None</button>
|
||||
<button type="button" onclick="loadC()">Get LS</button>
|
||||
<button type="button" class="red" onclick="saveC()">Save to LS</button><br>
|
||||
Extra JSON: <input id="ej" /><br>
|
||||
|
||||
<button type="button" onclick="run(true)" id="runbtn">Fetch FX list</button><br>
|
||||
LEDs: <span id="leds">-</span>, Seg: <span id="seg">-</span>, Bri: <span id="bri">-</span><br>
|
||||
FPS min: <span id="fps_min">-</span>, max: <span id="fps_max">-</span>, avg: <span id="fps_avg">-</span><br><br>
|
||||
<div id="tablecon">
|
||||
</div><br>
|
||||
<button type="button" onclick="csv(false)">Copy csv to clipboard</button>
|
||||
<button type="button" onclick="csv(true)">Copy csv (FPS only)</button>
|
||||
<textarea id=csva></textarea>
|
||||
</body>
|
||||
</html>
|
||||
100
tools/json_test.htm
Normal file
100
tools/json_test.htm
Normal file
@@ -0,0 +1,100 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>JSON client</title>
|
||||
<style>
|
||||
:root {
|
||||
--bCol:#333;--cCol:#222;--dCol:#666;--tCol:#fff;
|
||||
}
|
||||
body {
|
||||
font-family: Verdana, sans-serif;
|
||||
text-align: center;
|
||||
background: var(--cCol);
|
||||
color: var(--tCol);
|
||||
margin: 20px;
|
||||
background-attachment: fixed;
|
||||
}
|
||||
button {
|
||||
background: var(--cCol);
|
||||
color: var(--tCol);
|
||||
border: 0.3ch solid var(--cCol);
|
||||
display: inline-block;
|
||||
font-size: 20px;
|
||||
margin: 8px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
input {
|
||||
background: var(--cCol);
|
||||
color: var(--tCol);
|
||||
border: 0.5ch solid var(--cCol);
|
||||
width: 100%;
|
||||
}
|
||||
h1{
|
||||
margin: 0px;
|
||||
font-size: 20px;
|
||||
}
|
||||
h2{
|
||||
font-size: 16px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
form{
|
||||
background: var(--bCol);
|
||||
width: 500px;
|
||||
padding: 20px;
|
||||
-webkit-border-radius: 10px;
|
||||
-moz-border-radius: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
textarea{
|
||||
background: var(--cCol);
|
||||
color: var(--tCol);
|
||||
padding-top: 10px;
|
||||
width: 100%;
|
||||
font-family: monaco,monospace;
|
||||
font-size: 12px;
|
||||
-webkit-border-radius: 10px;
|
||||
-moz-border-radius: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<form name="cf">
|
||||
<h1>JSON API test tool</h1>
|
||||
<h2>URL:</h2>
|
||||
<input name="cu" type="text" size="60" value="http://192.168.4.1/json">
|
||||
<div id="buttons">
|
||||
<button type="button" onclick="rq('GET')">GET</button>
|
||||
<button type="button" onclick="rq('POST')">POST</button>
|
||||
</div>
|
||||
<h2>Body:</h2>
|
||||
<textarea name="bd" rows="8" cols="100"></textarea>
|
||||
<h2>Response:</h2>
|
||||
<textarea name="rsp" rows="25" cols="100"></textarea>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<script>
|
||||
function rq(cm)
|
||||
{
|
||||
var h = new XMLHttpRequest();
|
||||
h.open(cm, document.cf.cu.value, true);
|
||||
h.onreadystatechange = function()
|
||||
{
|
||||
if(h.readyState == 4)
|
||||
{
|
||||
if(h.status==200)
|
||||
{
|
||||
document.cf.rsp.value="Bad JSON: "+h.responseText
|
||||
document.cf.rsp.value=JSON.stringify(JSON.parse(h.responseText), null, '\t');
|
||||
}
|
||||
else
|
||||
{
|
||||
document.cf.rsp.value="Error "+h.status+"\r\n\n"+h.responseText;
|
||||
}
|
||||
}
|
||||
}
|
||||
h.send(document.cf.bd.value);
|
||||
}
|
||||
</script>
|
||||
16
tools/multi-update.cmd
Normal file
16
tools/multi-update.cmd
Normal file
@@ -0,0 +1,16 @@
|
||||
@echo off
|
||||
SETLOCAL
|
||||
SET FWPATH=c:\path\to\your\WLED\build_output\firmware
|
||||
GOTO ESPS
|
||||
|
||||
:UPDATEONE
|
||||
IF NOT EXIST %FWPATH%\%2 GOTO SKIP
|
||||
ping -w 1000 -n 1 %1 | find "TTL=" || GOTO SKIP
|
||||
ECHO Updating %1
|
||||
curl -s -F "update=@%FWPATH%/%2" %1/update >nul
|
||||
:SKIP
|
||||
GOTO:EOF
|
||||
|
||||
:ESPS
|
||||
call :UPDATEONE 192.168.x.x firmware.bin
|
||||
call :UPDATEONE ....
|
||||
19
tools/multi-update.sh
Normal file
19
tools/multi-update.sh
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
FWPATH=/path/to/your/WLED/build_output/firmware
|
||||
|
||||
update_one() {
|
||||
if [ -f $FWPATH/$2 ]; then
|
||||
ping -c 1 $1 >/dev/null
|
||||
PINGRESULT=$?
|
||||
if [ $PINGRESULT -eq 0 ]; then
|
||||
echo Updating $1
|
||||
curl -s -F "update=@${FWPATH}/$2" $1/update >/dev/null
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
update_one 192.168.x.x firmware.bin
|
||||
update_one 192.168.x.x firmware.bin
|
||||
# ...
|
||||
10
tools/partitions-16MB_spiffs-tinyuf2.csv
Normal file
10
tools/partitions-16MB_spiffs-tinyuf2.csv
Normal file
@@ -0,0 +1,10 @@
|
||||
# ESP-IDF Partition Table
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
# bootloader.bin,, 0x1000, 32K
|
||||
# partition table,, 0x8000, 4K
|
||||
nvs, data, nvs, 0x9000, 20K,
|
||||
otadata, data, ota, 0xe000, 8K,
|
||||
ota_0, app, ota_0, 0x10000, 2048K,
|
||||
ota_1, app, ota_1, 0x210000, 2048K,
|
||||
uf2, app, factory,0x410000, 256K,
|
||||
spiffs, data, spiffs, 0x450000, 11968K,
|
||||
|
11
tools/partitions-4MB_spiffs-tinyuf2.csv
Normal file
11
tools/partitions-4MB_spiffs-tinyuf2.csv
Normal file
@@ -0,0 +1,11 @@
|
||||
# ESP-IDF Partition Table
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
# bootloader.bin,, 0x1000, 32K
|
||||
# partition table, 0x8000, 4K
|
||||
|
||||
nvs, data, nvs, 0x9000, 20K,
|
||||
otadata, data, ota, 0xe000, 8K,
|
||||
ota_0, 0, ota_0, 0x10000, 1408K,
|
||||
ota_1, 0, ota_1, 0x170000, 1408K,
|
||||
uf2, app, factory,0x2d0000, 256K,
|
||||
spiffs, data, spiffs, 0x310000, 960K,
|
||||
|
10
tools/partitions-8MB_spiffs-tinyuf2.csv
Normal file
10
tools/partitions-8MB_spiffs-tinyuf2.csv
Normal file
@@ -0,0 +1,10 @@
|
||||
# ESP-IDF Partition Table
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
# bootloader.bin,, 0x1000, 32K
|
||||
# partition table,, 0x8000, 4K
|
||||
nvs, data, nvs, 0x9000, 20K,
|
||||
otadata, data, ota, 0xe000, 8K,
|
||||
ota_0, app, ota_0, 0x10000, 2048K,
|
||||
ota_1, app, ota_1, 0x210000, 2048K,
|
||||
uf2, app, factory,0x410000, 256K,
|
||||
spiffs, data, spiffs, 0x450000, 3776K,
|
||||
|
38
tools/stress_test.sh
Normal file
38
tools/stress_test.sh
Normal file
@@ -0,0 +1,38 @@
|
||||
#!/bin/bash
|
||||
# Some web server stress tests
|
||||
#
|
||||
# Perform a large number of parallel requests, stress testing the web server
|
||||
# TODO: some kind of performance metrics
|
||||
|
||||
# Accepts three command line arguments:
|
||||
# - first argument - mandatory - IP or hostname of target server
|
||||
# - second argument - target type (optional)
|
||||
# - third argument - xfer count (for replicated targets) (optional)
|
||||
HOST=$1
|
||||
declare -n TARGET_STR="${2:-JSON_LARGER}_TARGETS"
|
||||
REPLICATE_COUNT=$(("${3:-10}"))
|
||||
|
||||
PARALLEL_MAX=${PARALLEL_MAX:-50}
|
||||
|
||||
CURL_ARGS="--compressed --parallel --parallel-immediate --parallel-max ${PARALLEL_MAX}"
|
||||
CURL_PRINT_RESPONSE_ARGS="-w %{http_code}\n"
|
||||
|
||||
JSON_TARGETS=('json/state' 'json/info' 'json/si', 'json/palettes' 'json/fxdata' 'settings/s.js?p=2')
|
||||
FILE_TARGETS=('' 'iro.js' 'rangetouch.js' 'settings' 'settings/wifi')
|
||||
# Replicate one target many times
|
||||
function replicate() {
|
||||
printf "${1}?%d " $(seq 1 ${REPLICATE_COUNT})
|
||||
}
|
||||
read -a JSON_TINY_TARGETS <<< $(replicate "json/nodes")
|
||||
read -a JSON_SMALL_TARGETS <<< $(replicate "json/info")
|
||||
read -a JSON_LARGE_TARGETS <<< $(replicate "json/si")
|
||||
read -a JSON_LARGER_TARGETS <<< $(replicate "json/fxdata")
|
||||
read -a INDEX_TARGETS <<< $(replicate "")
|
||||
|
||||
# Expand target URLS to full arguments for curl
|
||||
TARGETS=(${TARGET_STR[@]})
|
||||
#echo "${TARGETS[@]}"
|
||||
FULL_TGT_OPTIONS=$(printf "http://${HOST}/%s -o /dev/null " "${TARGETS[@]}")
|
||||
#echo ${FULL_TGT_OPTIONS}
|
||||
|
||||
time curl ${CURL_ARGS} ${FULL_TGT_OPTIONS}
|
||||
46
tools/udp_test.py
Normal file
46
tools/udp_test.py
Normal file
@@ -0,0 +1,46 @@
|
||||
import numpy as np
|
||||
import socket
|
||||
|
||||
class WledRealtimeClient:
|
||||
def __init__(self, wled_controller_ip, num_pixels, udp_port=21324, max_pixels_per_packet=126):
|
||||
self.wled_controller_ip = wled_controller_ip
|
||||
self.num_pixels = num_pixels
|
||||
self.udp_port = udp_port
|
||||
self.max_pixels_per_packet = max_pixels_per_packet
|
||||
self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
self._prev_pixels = np.full((3, self.num_pixels), 253, dtype=np.uint8)
|
||||
self.pixels = np.full((3, self.num_pixels), 1, dtype=np.uint8)
|
||||
|
||||
def update(self):
|
||||
# Truncate values and cast to integer
|
||||
self.pixels = np.clip(self.pixels, 0, 255).astype(np.uint8)
|
||||
p = np.copy(self.pixels)
|
||||
|
||||
idx = np.where(~np.all(p == self._prev_pixels, axis=0))[0]
|
||||
num_pixels = len(idx)
|
||||
n_packets = (num_pixels + self.max_pixels_per_packet - 1) // self.max_pixels_per_packet
|
||||
idx_split = np.array_split(idx, n_packets)
|
||||
|
||||
header = bytes([1, 2]) # WARLS protocol header
|
||||
for packet_indices in idx_split:
|
||||
data = bytearray(header)
|
||||
for i in packet_indices:
|
||||
data.extend([i, *p[:, i]]) # Index and RGB values
|
||||
self._sock.sendto(bytes(data), (self.wled_controller_ip, self.udp_port))
|
||||
|
||||
self._prev_pixels = np.copy(p)
|
||||
|
||||
|
||||
|
||||
################################## LED blink test ##################################
|
||||
if __name__ == "__main__":
|
||||
WLED_CONTROLLER_IP = "192.168.1.153"
|
||||
NUM_PIXELS = 255 # Amount of LEDs on your strip
|
||||
import time
|
||||
wled = WledRealtimeClient(WLED_CONTROLLER_IP, NUM_PIXELS)
|
||||
print('Starting LED blink test')
|
||||
while True:
|
||||
for i in range(NUM_PIXELS):
|
||||
wled.pixels[1, i] = 255 if wled.pixels[1, i] == 0 else 0
|
||||
wled.update()
|
||||
time.sleep(.01)
|
||||
696
tools/wbf-to-header-bidirectional-fontconverter.html
Normal file
696
tools/wbf-to-header-bidirectional-fontconverter.html
Normal file
@@ -0,0 +1,696 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>WBF ↔ C Header Bi-Directional Converter</title>
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
}
|
||||
body {
|
||||
font-family: system-ui, sans-serif;
|
||||
background: #0a0a0a;
|
||||
color: #e0e0e0;
|
||||
padding: 20px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.card {
|
||||
max-width: 900px;
|
||||
margin: auto;
|
||||
background: #16213e;
|
||||
padding: 30px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 20px rgba(0, 242, 255, .15);
|
||||
border: 1px solid rgba(0, 242, 255, .1);
|
||||
}
|
||||
h1 {
|
||||
color: #00f2ff;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
font-size: 2rem;
|
||||
text-shadow: 0 0 25px #ffffff;
|
||||
}
|
||||
.mode-selector {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.mode-btn {
|
||||
padding: 14px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
border: 2px solid rgba(0, 242, 255, .3);
|
||||
background: rgba(0, 0, 0, .4);
|
||||
color: #888;
|
||||
transition: all .3s;
|
||||
}
|
||||
.mode-btn.active {
|
||||
background: linear-gradient(135deg, #0095b3 0%, #00d4ff 100%);
|
||||
border-color: #00f2ff;
|
||||
color: #fff;
|
||||
box-shadow: 0 0 14px rgba(0, 242, 255, .6);
|
||||
}
|
||||
.mode-btn:hover:not(.active) {
|
||||
border-color: #00f2ff;
|
||||
color: #00f2ff;
|
||||
}
|
||||
.controls {
|
||||
background: rgba(255, 255, 255, .03);
|
||||
padding: 20px;
|
||||
border-radius: 15px;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid rgba(0, 242, 255, .1);
|
||||
display: none;
|
||||
}
|
||||
.controls.active {
|
||||
display: block;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
font-size: .85rem;
|
||||
font-weight: 600;
|
||||
color: #00d4ff;
|
||||
margin-bottom: 8px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .5px;
|
||||
}
|
||||
input[type="file"] {
|
||||
display: none;
|
||||
}
|
||||
.file-label {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(0, 242, 255, .2);
|
||||
background: rgba(0, 0, 0, .4);
|
||||
color: #888;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
transition: all .3s;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.file-label:hover {
|
||||
border-color: #00f2ff;
|
||||
color: #00f2ff;
|
||||
}
|
||||
.file-label.has-file {
|
||||
color: #00f2ff;
|
||||
}
|
||||
input[type="text"], textarea {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(0, 242, 255, .2);
|
||||
background: rgba(0, 0, 0, .4);
|
||||
color: #fff;
|
||||
margin-bottom: 15px;
|
||||
transition: all .3s;
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
textarea {
|
||||
min-height: 300px;
|
||||
resize: vertical;
|
||||
font-size: .85rem;
|
||||
}
|
||||
input[type="text"]:focus, textarea:focus {
|
||||
outline: none;
|
||||
border-color: #00f2ff;
|
||||
box-shadow: 0 0 8px rgba(0, 242, 255, .3);
|
||||
}
|
||||
button {
|
||||
width: 100%;
|
||||
padding: 14px;
|
||||
border-radius: 50px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
border: none;
|
||||
background: linear-gradient(135deg, #0095b3 0%, #00d4ff 100%);
|
||||
box-shadow: 0 0 14px rgba(0, 242, 255, .6);
|
||||
color: #fff;
|
||||
transition: all .3s;
|
||||
}
|
||||
button:hover:not(:disabled) {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 0 18px rgba(0, 242, 255, .9);
|
||||
}
|
||||
button:disabled {
|
||||
opacity: .3;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.info {
|
||||
background: rgba(0, 242, 255, .05);
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid rgba(0, 242, 255, .2);
|
||||
font-size: .85rem;
|
||||
}
|
||||
.output {
|
||||
background: rgba(0, 0, 0, .6);
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(0, 242, 255, .2);
|
||||
max-height: 500px;
|
||||
overflow-y: auto;
|
||||
display: none;
|
||||
}
|
||||
.output.active {
|
||||
display: block;
|
||||
}
|
||||
pre {
|
||||
color: #00f2ff;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: .85rem;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.copy-btn {
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<h1>WBF ↔ C Header Converter</h1>
|
||||
|
||||
<div class="mode-selector">
|
||||
<button class="mode-btn active" onclick="switchMode('wbf-to-header')">WBF → C Header</button>
|
||||
<button class="mode-btn" onclick="switchMode('header-to-wbf')">C Header → WBF</button>
|
||||
</div>
|
||||
|
||||
<!-- WBF to Header Mode -->
|
||||
<div id="wbf-to-header" class="controls active">
|
||||
<div class="info">
|
||||
Load a WLED Bitmap Font (.wbf) file and convert it to a C/C++ header file.
|
||||
</div>
|
||||
|
||||
<label>Select WBF Font File</label>
|
||||
<label for="wbfFile" class="file-label" id="wbfFileLabel">Choose .wbf file</label>
|
||||
<input type="file" id="wbfFile" accept=".wbf">
|
||||
|
||||
<label>Array Name</label>
|
||||
<input type="text" id="arrayName" placeholder="console_font_4x6" value="console_font_4x6">
|
||||
|
||||
<button id="convertToHeaderBtn" disabled>Convert to Header</button>
|
||||
</div>
|
||||
|
||||
<!-- Header to WBF Mode -->
|
||||
<div id="header-to-wbf" class="controls">
|
||||
<div class="info">
|
||||
Paste a C/C++ header file containing a WLED font array and convert it to a .wbf file.
|
||||
</div>
|
||||
|
||||
<label>Paste C Header Code</label>
|
||||
<textarea id="headerInput" placeholder="Paste your C header code here (e.g., static const unsigned char font[] PROGMEM = {...};)"></textarea>
|
||||
|
||||
<label>Output Filename (without .wbf extension)</label>
|
||||
<input type="text" id="wbfFilename" placeholder="console_font_4x6" value="console_font_4x6">
|
||||
|
||||
<button id="convertToWbfBtn" disabled>Convert to WBF</button>
|
||||
</div>
|
||||
|
||||
<div id="output" class="output">
|
||||
<pre id="outputCode"></pre>
|
||||
<button class="copy-btn" onclick="copyToClipboard(event)">Copy to Clipboard</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Elements for WBF to Header
|
||||
const wbfFileInput = document.getElementById('wbfFile');
|
||||
const wbfFileLabel = document.getElementById('wbfFileLabel');
|
||||
const arrayNameInput = document.getElementById('arrayName');
|
||||
const convertToHeaderBtn = document.getElementById('convertToHeaderBtn');
|
||||
|
||||
// Elements for Header to WBF
|
||||
const headerInput = document.getElementById('headerInput');
|
||||
const wbfFilenameInput = document.getElementById('wbfFilename');
|
||||
const convertToWbfBtn = document.getElementById('convertToWbfBtn');
|
||||
|
||||
// Output elements
|
||||
const output = document.getElementById('output');
|
||||
const outputCode = document.getElementById('outputCode');
|
||||
|
||||
let wbfData = null;
|
||||
let fileName = '';
|
||||
|
||||
// Mode switching
|
||||
function switchMode(mode) {
|
||||
document.querySelectorAll('.mode-btn').forEach(btn => btn.classList.remove('active'));
|
||||
document.querySelectorAll('.controls').forEach(ctrl => ctrl.classList.remove('active'));
|
||||
|
||||
event.target.classList.add('active');
|
||||
document.getElementById(mode).classList.add('active');
|
||||
output.classList.remove('active');
|
||||
}
|
||||
|
||||
// ==================== WBF to Header ====================
|
||||
wbfFileInput.addEventListener('change', (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
fileName = file.name.replace(/\.[^.]+$/, '');
|
||||
wbfFileLabel.textContent = file.name;
|
||||
wbfFileLabel.classList.add('has-file');
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = (ev) => {
|
||||
wbfData = new Uint8Array(ev.target.result);
|
||||
convertToHeaderBtn.disabled = false;
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
}
|
||||
});
|
||||
|
||||
convertToHeaderBtn.addEventListener('click', () => {
|
||||
if (!wbfData) return;
|
||||
|
||||
const header = parseWBF(wbfData);
|
||||
if (header) {
|
||||
generateHeader(header);
|
||||
}
|
||||
});
|
||||
|
||||
function parseWBF(data) {
|
||||
if (data.length < 12) {
|
||||
alert('Invalid WBF file: too short');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Parse header
|
||||
if (data[0] !== 0x57) { // 'W'
|
||||
alert('Invalid WBF file: missing magic byte');
|
||||
return null;
|
||||
}
|
||||
|
||||
const height = data[1];
|
||||
const maxWidth = data[2];
|
||||
const spacing = data[3];
|
||||
const flags = data[4];
|
||||
const first = data[5];
|
||||
const last = data[6];
|
||||
const reserved = data[7];
|
||||
|
||||
// Unicode offset is 32-bit little-endian
|
||||
const unicodeOffset = data[8] | (data[9] << 8) | (data[10] << 16) | (data[11] << 24);
|
||||
|
||||
const numChars = last - first + 1;
|
||||
const isVariableWidth = (flags & 0x01) !== 0;
|
||||
|
||||
let offset = 12; // Start after header
|
||||
let widthTable = null;
|
||||
|
||||
// If variable width, read width table
|
||||
if (isVariableWidth) {
|
||||
if (data.length < 12 + numChars) {
|
||||
alert('Invalid WBF file: missing width table');
|
||||
return null;
|
||||
}
|
||||
widthTable = Array.from(data.slice(12, 12 + numChars));
|
||||
offset = 12 + numChars;
|
||||
}
|
||||
|
||||
// Calculate expected data size
|
||||
let expectedDataSize = 0;
|
||||
if (isVariableWidth) {
|
||||
for (let w of widthTable) {
|
||||
expectedDataSize += Math.ceil((w * height) / 8);
|
||||
}
|
||||
} else {
|
||||
expectedDataSize = numChars * Math.ceil((maxWidth * height) / 8);
|
||||
}
|
||||
|
||||
if (data.length < offset + expectedDataSize) {
|
||||
alert(`Invalid WBF file: expected ${offset + expectedDataSize} bytes, got ${data.length}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
height,
|
||||
maxWidth,
|
||||
spacing,
|
||||
flags,
|
||||
first,
|
||||
last,
|
||||
reserved,
|
||||
unicodeOffset,
|
||||
data: data,
|
||||
isVariableWidth,
|
||||
widthTable,
|
||||
dataOffset: offset
|
||||
};
|
||||
}
|
||||
|
||||
function generateHeader(header) {
|
||||
const arrayName = arrayNameInput.value || 'console_font';
|
||||
let code = '';
|
||||
|
||||
// Header comment
|
||||
code += `// Font: ${fileName}\n`;
|
||||
code += `// Height: ${header.height}, Max Width: ${header.maxWidth}, Spacing: ${header.spacing}\n`;
|
||||
code += `// Characters: ${header.first}-${header.last} (${header.last - header.first + 1} glyphs)\n`;
|
||||
code += `// Unicode Offset: 0x${header.unicodeOffset.toString(16).padStart(8, '0').toUpperCase()}\n`;
|
||||
code += `// Variable Width: ${header.isVariableWidth ? 'Yes' : 'No'}\n\n`;
|
||||
|
||||
// Array declaration
|
||||
code += `static const unsigned char ${arrayName}[] PROGMEM = {\n`;
|
||||
|
||||
// Header bytes (12 bytes)
|
||||
code += ' ';
|
||||
code += `0x${header.data[0].toString(16).padStart(2, '0').toUpperCase()}, `; // Magic 'W'
|
||||
code += `0x${header.height.toString(16).padStart(2, '0').toUpperCase()}, `; // Height
|
||||
code += `0x${header.maxWidth.toString(16).padStart(2, '0').toUpperCase()}, `; // Max Width
|
||||
code += `0x${header.spacing.toString(16).padStart(2, '0').toUpperCase()}, `; // Spacing
|
||||
code += `0x${header.flags.toString(16).padStart(2, '0').toUpperCase()}, `; // Flags
|
||||
code += `0x${header.first.toString(16).padStart(2, '0').toUpperCase()}, `; // First char
|
||||
code += `0x${header.last.toString(16).padStart(2, '0').toUpperCase()}, `; // Last char
|
||||
code += `0x${header.reserved.toString(16).padStart(2, '0').toUpperCase()}, `; // Reserved
|
||||
|
||||
// Unicode offset (4 bytes, little-endian)
|
||||
code += `0x${header.data[8].toString(16).padStart(2, '0').toUpperCase()}, `;
|
||||
code += `0x${header.data[9].toString(16).padStart(2, '0').toUpperCase()}, `;
|
||||
code += `0x${header.data[10].toString(16).padStart(2, '0').toUpperCase()}, `;
|
||||
code += `0x${header.data[11].toString(16).padStart(2, '0').toUpperCase()}`;
|
||||
|
||||
code += ', // Header: \'W\', H, W, S, Flags, First, Last, Reserved, UnicodeOffset (32bit)\n\n';
|
||||
|
||||
// Width table (if variable width)
|
||||
if (header.isVariableWidth) {
|
||||
code += ' // Width table\n';
|
||||
const numChars = header.last - header.first + 1;
|
||||
for (let i = 0; i < numChars; i++) {
|
||||
if (i % 16 === 0) {
|
||||
if (i > 0) code += '\n';
|
||||
code += ' ';
|
||||
}
|
||||
code += `0x${header.widthTable[i].toString(16).padStart(2, '0').toUpperCase()}`;
|
||||
if (i < numChars - 1) {
|
||||
code += ', ';
|
||||
}
|
||||
}
|
||||
code += ',\n\n';
|
||||
}
|
||||
|
||||
// Character data
|
||||
const numChars = header.last - header.first + 1;
|
||||
let offset = header.dataOffset;
|
||||
|
||||
// First pass: calculate max byte width for alignment
|
||||
let maxByteWidth = 0;
|
||||
let tempOffset = header.dataOffset;
|
||||
for (let i = 0; i < numChars; i++) {
|
||||
const charWidth = header.isVariableWidth ? header.widthTable[i] : header.maxWidth;
|
||||
const bytesPerChar = Math.ceil((charWidth * header.height) / 8);
|
||||
const byteStr = Array(bytesPerChar).fill('0x00').join(', ');
|
||||
maxByteWidth = Math.max(maxByteWidth, byteStr.length);
|
||||
tempOffset += bytesPerChar;
|
||||
}
|
||||
|
||||
// Second pass: generate output with aligned comments
|
||||
for (let i = 0; i < numChars; i++) {
|
||||
const charCode = header.first + i;
|
||||
const charStr = getAsciiString(charCode);
|
||||
|
||||
// Calculate bytes for this character
|
||||
const charWidth = header.isVariableWidth ? header.widthTable[i] : header.maxWidth;
|
||||
const bytesPerChar = Math.ceil((charWidth * header.height) / 8);
|
||||
|
||||
code += ' ';
|
||||
let byteStr = '';
|
||||
for (let b = 0; b < bytesPerChar; b++) {
|
||||
const byte = header.data[offset++];
|
||||
byteStr += `0x${byte.toString(16).padStart(2, '0').toUpperCase()}`;
|
||||
if (b < bytesPerChar - 1) {
|
||||
byteStr += ', ';
|
||||
}
|
||||
}
|
||||
code += byteStr;
|
||||
code += ',';
|
||||
|
||||
// Pad to align comments
|
||||
const padding = ' '.repeat(maxByteWidth - byteStr.length + 5);
|
||||
|
||||
const widthInfo = header.isVariableWidth ? `, w=${charWidth}` : '';
|
||||
code += `${padding}/* code=${charCode}, hex=0x${charCode.toString(16).padStart(2, '0').toUpperCase()}, ascii="${charStr}"${widthInfo} */\n`;
|
||||
}
|
||||
|
||||
code += '};\n';
|
||||
|
||||
// Display output
|
||||
outputCode.textContent = code;
|
||||
output.classList.add('active');
|
||||
}
|
||||
|
||||
// ==================== Header to WBF ====================
|
||||
headerInput.addEventListener('input', () => {
|
||||
convertToWbfBtn.disabled = headerInput.value.trim().length === 0;
|
||||
});
|
||||
|
||||
convertToWbfBtn.addEventListener('click', () => {
|
||||
const headerText = headerInput.value.trim();
|
||||
if (!headerText) return;
|
||||
|
||||
const wbfData = parseHeaderToWBF(headerText);
|
||||
if (wbfData) {
|
||||
downloadWBF(wbfData);
|
||||
}
|
||||
});
|
||||
|
||||
function parseHeaderToWBF(headerText) {
|
||||
try {
|
||||
// Properly remove C-style comments
|
||||
let cleanedText = removeComments(headerText);
|
||||
|
||||
// Extract all hex values from the cleaned text
|
||||
const hexPattern = /0x[0-9a-fA-F]{2}/g;
|
||||
const hexValues = cleanedText.match(hexPattern);
|
||||
|
||||
if (!hexValues || hexValues.length < 12) {
|
||||
alert('Invalid header: Could not find enough hex values (need at least 12 for header)\nFound: ' + (hexValues ? hexValues.length : 0) + ' bytes');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Convert hex strings to bytes
|
||||
const allBytes = hexValues.map(hex => parseInt(hex, 16));
|
||||
|
||||
// Parse the header (first 12 bytes)
|
||||
const height = allBytes[1];
|
||||
const maxWidth = allBytes[2];
|
||||
const spacing = allBytes[3];
|
||||
const flags = allBytes[4];
|
||||
const first = allBytes[5];
|
||||
const last = allBytes[6];
|
||||
const reserved = allBytes[7];
|
||||
const unicodeOffset = allBytes[8] | (allBytes[9] << 8) | (allBytes[10] << 16) | (allBytes[11] << 24);
|
||||
|
||||
// Validate magic byte
|
||||
if (allBytes[0] !== 0x57) {
|
||||
alert('Invalid header: First byte should be 0x57 (magic \'W\'), found 0x' + allBytes[0].toString(16).padStart(2, '0').toUpperCase());
|
||||
return null;
|
||||
}
|
||||
|
||||
const numChars = last - first + 1;
|
||||
const isVariableWidth = (flags & 0x01) !== 0;
|
||||
|
||||
// Now we need to separate header, width table (if present), and bitmap data
|
||||
let dataStartIndex = 12; // After the 12-byte header
|
||||
let widthTable = null;
|
||||
|
||||
if (isVariableWidth) {
|
||||
// Extract width table
|
||||
widthTable = allBytes.slice(dataStartIndex, dataStartIndex + numChars);
|
||||
dataStartIndex += numChars;
|
||||
}
|
||||
|
||||
// Calculate expected bitmap data size
|
||||
let expectedBitmapSize = 0;
|
||||
if (isVariableWidth) {
|
||||
for (let w of widthTable) {
|
||||
expectedBitmapSize += Math.ceil((w * height) / 8);
|
||||
}
|
||||
} else {
|
||||
expectedBitmapSize = numChars * Math.ceil((maxWidth * height) / 8);
|
||||
}
|
||||
|
||||
// Extract bitmap data
|
||||
const bitmapData = allBytes.slice(dataStartIndex, dataStartIndex + expectedBitmapSize);
|
||||
|
||||
// Validate we have all the data
|
||||
const totalExpected = dataStartIndex + expectedBitmapSize;
|
||||
if (allBytes.length < totalExpected) {
|
||||
alert(`Invalid header: Expected at least ${totalExpected} bytes, found ${allBytes.length}\n\n` +
|
||||
`Header shows: ${numChars} characters from ${first} to ${last}\n` +
|
||||
`${isVariableWidth ? 'Variable width font' : 'Fixed width font'}\n` +
|
||||
`Height: ${height}, Max Width: ${maxWidth}\n` +
|
||||
`Expected bitmap size: ${expectedBitmapSize} bytes`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Reconstruct the WBF file
|
||||
let wbfSize = 12; // Header
|
||||
if (isVariableWidth) {
|
||||
wbfSize += numChars; // Width table
|
||||
}
|
||||
wbfSize += expectedBitmapSize; // Bitmap data
|
||||
|
||||
const wbfData = new Uint8Array(wbfSize);
|
||||
let offset = 0;
|
||||
|
||||
// Write header
|
||||
wbfData[offset++] = 0x57; // Magic 'W'
|
||||
wbfData[offset++] = height;
|
||||
wbfData[offset++] = maxWidth;
|
||||
wbfData[offset++] = spacing;
|
||||
wbfData[offset++] = flags;
|
||||
wbfData[offset++] = first;
|
||||
wbfData[offset++] = last;
|
||||
wbfData[offset++] = reserved;
|
||||
wbfData[offset++] = unicodeOffset & 0xFF;
|
||||
wbfData[offset++] = (unicodeOffset >> 8) & 0xFF;
|
||||
wbfData[offset++] = (unicodeOffset >> 16) & 0xFF;
|
||||
wbfData[offset++] = (unicodeOffset >> 24) & 0xFF;
|
||||
|
||||
// Write width table if variable width
|
||||
if (isVariableWidth) {
|
||||
for (let w of widthTable) {
|
||||
wbfData[offset++] = w;
|
||||
}
|
||||
}
|
||||
|
||||
// Write bitmap data
|
||||
for (let byte of bitmapData) {
|
||||
wbfData[offset++] = byte;
|
||||
}
|
||||
|
||||
// Show success message
|
||||
outputCode.textContent = `Successfully parsed header!\n\n` +
|
||||
`Height: ${height}\n` +
|
||||
`Max Width: ${maxWidth}\n` +
|
||||
`Spacing: ${spacing}\n` +
|
||||
`Flags: 0x${flags.toString(16).padStart(2, '0').toUpperCase()} (${isVariableWidth ? 'Variable Width' : 'Fixed Width'})\n` +
|
||||
`Characters: ${first}-${last} (${numChars} glyphs)\n` +
|
||||
`Unicode Offset: 0x${unicodeOffset.toString(16).padStart(8, '0').toUpperCase()}\n` +
|
||||
`Total Size: ${wbfData.length} bytes\n` +
|
||||
` Header: 12 bytes\n` +
|
||||
(isVariableWidth ? ` Width table: ${numChars} bytes\n` : '') +
|
||||
` Bitmap data: ${expectedBitmapSize} bytes\n` +
|
||||
`Parsed ${allBytes.length} total hex values\n\n` +
|
||||
`Your .wbf file is ready to download!`;
|
||||
output.classList.add('active');
|
||||
|
||||
return wbfData;
|
||||
} catch (error) {
|
||||
alert('Error parsing header: ' + error.message);
|
||||
console.error('Parse error:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Proper C-style comment removal
|
||||
function removeComments(code) {
|
||||
let result = '';
|
||||
let i = 0;
|
||||
|
||||
while (i < code.length) {
|
||||
// Check for // comment (single-line)
|
||||
if (code[i] === '/' && code[i + 1] === '/') {
|
||||
// Skip until end of line
|
||||
i += 2;
|
||||
while (i < code.length && code[i] !== '\n') {
|
||||
i++;
|
||||
}
|
||||
// Keep the newline
|
||||
if (i < code.length) {
|
||||
result += '\n';
|
||||
i++;
|
||||
}
|
||||
}
|
||||
// Check for /* comment (multi-line)
|
||||
else if (code[i] === '/' && code[i + 1] === '*') {
|
||||
// Skip until we find */
|
||||
i += 2;
|
||||
while (i < code.length - 1) {
|
||||
if (code[i] === '*' && code[i + 1] === '/') {
|
||||
i += 2;
|
||||
break;
|
||||
}
|
||||
// Preserve newlines in multi-line comments (for line counting if needed)
|
||||
if (code[i] === '\n') {
|
||||
result += '\n';
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
// Regular character
|
||||
else {
|
||||
result += code[i];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function downloadWBF(data) {
|
||||
const filename = wbfFilenameInput.value.trim() || 'font';
|
||||
const blob = new Blob([data], { type: 'application/octet-stream' });
|
||||
const a = document.createElement('a');
|
||||
a.href = URL.createObjectURL(blob);
|
||||
a.download = `${filename}.wbf`;
|
||||
a.click();
|
||||
}
|
||||
|
||||
// ==================== Utility Functions ====================
|
||||
function getAsciiString(code) {
|
||||
if (code < 32) {
|
||||
// Control characters
|
||||
return '^' + String.fromCharCode(64 + code);
|
||||
} else if (code === 127) {
|
||||
return '^?';
|
||||
} else if (code >= 32 && code <= 126) {
|
||||
// Printable ASCII
|
||||
return String.fromCharCode(code);
|
||||
} else {
|
||||
// Extended ASCII
|
||||
return '\\x' + code.toString(16).padStart(2, '0');
|
||||
}
|
||||
}
|
||||
|
||||
function copyToClipboard(event) {
|
||||
const text = outputCode.textContent;
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
const btn = event.target;
|
||||
const originalText = btn.textContent;
|
||||
btn.textContent = 'Copied!';
|
||||
setTimeout(() => {
|
||||
btn.textContent = originalText;
|
||||
}, 2000);
|
||||
}).catch(err => {
|
||||
console.error('Clipboard error:', err);
|
||||
// Fallback method for older browsers
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.value = text;
|
||||
textarea.style.position = 'fixed';
|
||||
textarea.style.opacity = '0';
|
||||
document.body.appendChild(textarea);
|
||||
textarea.select();
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
const btn = event.target;
|
||||
const originalText = btn.textContent;
|
||||
btn.textContent = 'Copied!';
|
||||
setTimeout(() => {
|
||||
btn.textContent = originalText;
|
||||
}, 2000);
|
||||
} catch (e) {
|
||||
alert('Failed to copy to clipboard: ' + e.message);
|
||||
}
|
||||
document.body.removeChild(textarea);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user