mirror of
https://github.com/wled/WLED.git
synced 2025-08-30 21:10:24 +00:00
Compare commits
1510 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6c315e5a9c | ||
![]() |
ef0f91d8d0 | ||
![]() |
9552784e72 | ||
![]() |
f068327307 | ||
![]() |
1bc698ae78 | ||
![]() |
1b2134d7a8 | ||
![]() |
f922268af7 | ||
![]() |
4865ddb377 | ||
![]() |
a556732e4f | ||
![]() |
0ea31cb088 | ||
![]() |
b626c7620e | ||
![]() |
5d90d8930e | ||
![]() |
b01309c3bf | ||
![]() |
961d5591bd | ||
![]() |
eca3f12fed | ||
![]() |
a2c8796e04 | ||
![]() |
ad301fd087 | ||
![]() |
02b08939cd | ||
![]() |
9b0d583f1b | ||
![]() |
4a0a07f158 | ||
![]() |
9c864c9759 | ||
![]() |
85b1c309d1 | ||
![]() |
6fe43b7b5c | ||
![]() |
25427ee60d | ||
![]() |
be90bf0188 | ||
![]() |
adcdaba199 | ||
![]() |
17907589cc | ||
![]() |
f333df181f | ||
![]() |
4ce557a829 | ||
![]() |
fc845dc936 | ||
![]() |
7beae93441 | ||
![]() |
4d4a20e05e | ||
![]() |
c03d4f115f | ||
![]() |
ed90b638a9 | ||
![]() |
94a0199955 | ||
![]() |
44739c5198 | ||
![]() |
5f871bc01f | ||
![]() |
1f5971f15a | ||
![]() |
694466a196 | ||
![]() |
03311d3776 | ||
![]() |
ae0eba866a | ||
![]() |
906737bedb | ||
![]() |
7138e891be | ||
![]() |
53abe36b83 | ||
![]() |
efbb7a034c | ||
![]() |
05bc81bf4e | ||
![]() |
f8bc0bd2b5 | ||
![]() |
cf94cb1092 | ||
![]() |
02d92e32c7 | ||
![]() |
7f92607b85 | ||
![]() |
3be4b69b44 | ||
![]() |
bb9afcb304 | ||
![]() |
e9a05890a5 | ||
![]() |
613809c2af | ||
![]() |
7b969bb8c2 | ||
![]() |
7aef551292 | ||
![]() |
447b811fa0 | ||
![]() |
435040814d | ||
![]() |
9987416a4a | ||
![]() |
31e33e0a8b | ||
![]() |
b211d8b085 | ||
![]() |
83416ee2e0 | ||
![]() |
fa981a389f | ||
![]() |
55817f31f9 | ||
![]() |
6d2ef4e0bf | ||
![]() |
4d714cf9a4 | ||
![]() |
930ded6767 | ||
![]() |
4cdb18907f | ||
![]() |
38bc618ee5 | ||
![]() |
00b0193a43 | ||
![]() |
f9bce54104 | ||
![]() |
7ee14724fc | ||
![]() |
4eb0dbb5a4 | ||
![]() |
8c5b3fe23e | ||
![]() |
97f8eea302 | ||
![]() |
04d5932252 | ||
![]() |
b33c5798ee | ||
![]() |
6180c2f948 | ||
![]() |
795c515999 | ||
![]() |
00dbdc2267 | ||
![]() |
32286888e5 | ||
![]() |
565d8d8f04 | ||
![]() |
0a5a0bef48 | ||
![]() |
6e0e5c102e | ||
![]() |
be8a9ae73b | ||
![]() |
22fbb0e35b | ||
![]() |
e17203ca1b | ||
![]() |
3170fa2208 | ||
![]() |
07216db864 | ||
![]() |
fec870f264 | ||
![]() |
2c5eba335f | ||
![]() |
fb19f1ecbc | ||
![]() |
e879fe5843 | ||
![]() |
0ca7699fe5 | ||
![]() |
7f6adfa331 | ||
![]() |
5f0b102671 | ||
![]() |
d28eb6ae21 | ||
![]() |
eca980dfca | ||
![]() |
742c792ec7 | ||
![]() |
9b062f33c5 | ||
![]() |
ea15c2245e | ||
![]() |
26ae6d3691 | ||
![]() |
f97bc9dba8 | ||
![]() |
fe6b1c13c4 | ||
![]() |
5608425a12 | ||
![]() |
f784b01d20 | ||
![]() |
2648eba5bf | ||
![]() |
255347ab77 | ||
![]() |
52c36ef6a4 | ||
![]() |
e54819e7e5 | ||
![]() |
7eb029dcb6 | ||
![]() |
f8c80283e4 | ||
![]() |
04f5bdb843 | ||
![]() |
aba4dc7c50 | ||
![]() |
7fb46cf982 | ||
![]() |
ae8281f835 | ||
![]() |
fa35293618 | ||
![]() |
20ccca0aec | ||
![]() |
10e216da6b | ||
![]() |
6491353a57 | ||
![]() |
9f44f989e5 | ||
![]() |
33f72e40da | ||
![]() |
18868a5bd6 | ||
![]() |
754682577c | ||
![]() |
aef0243b73 | ||
![]() |
736053e24e | ||
![]() |
2c14181051 | ||
![]() |
296fe4b62e | ||
![]() |
118f02fd11 | ||
![]() |
990d0f6e3e | ||
![]() |
84624666ce | ||
![]() |
8bd716c056 | ||
![]() |
cd95abb2a1 | ||
![]() |
1270f2d577 | ||
![]() |
c27117e99e | ||
![]() |
28556790d6 | ||
![]() |
41c9bb63a0 | ||
![]() |
7d5e2466f0 | ||
![]() |
d3f35955d6 | ||
![]() |
fb338c0728 | ||
![]() |
2ce8f1ee5d | ||
![]() |
3f0258e215 | ||
![]() |
e72a8d999f | ||
![]() |
fed16fd14e | ||
![]() |
5dbc45ecb9 | ||
![]() |
094bdb29b6 | ||
![]() |
9e6866c160 | ||
![]() |
7101ad81c4 | ||
![]() |
8643263227 | ||
![]() |
eab132ed32 | ||
![]() |
66bad2b6f8 | ||
![]() |
46ec504743 | ||
![]() |
cadda12371 | ||
![]() |
a643b56555 | ||
![]() |
f7404085de | ||
![]() |
33036e7599 | ||
![]() |
f6e5b67f0d | ||
![]() |
9547ac353d | ||
![]() |
48339b19d4 | ||
![]() |
11c7ffad4e | ||
![]() |
1973424e05 | ||
![]() |
16d97d3c63 | ||
![]() |
3e6728fedb | ||
![]() |
3e9aea072d | ||
![]() |
9f3e66fff0 | ||
![]() |
624993fc89 | ||
![]() |
ba8a00764a | ||
![]() |
3dec4a6651 | ||
![]() |
02fb2550d0 | ||
![]() |
37bd525638 | ||
![]() |
ea0f37f5b9 | ||
![]() |
97b3c3db7b | ||
![]() |
b97b6dc144 | ||
![]() |
c8d5218c65 | ||
![]() |
80a657965e | ||
![]() |
b3324d22f5 | ||
![]() |
31b7cdff9b | ||
![]() |
0465298507 | ||
![]() |
d31e4c7815 | ||
![]() |
4af1f62aab | ||
![]() |
bc403440ba | ||
![]() |
38d8dfe5ab | ||
![]() |
eb92c0bbf5 | ||
![]() |
6df64d0d31 | ||
![]() |
83753a5f81 | ||
![]() |
3161f5fa47 | ||
![]() |
5784092c1b | ||
![]() |
d6ad089c60 | ||
![]() |
446b4b084c | ||
![]() |
d590e01a58 | ||
![]() |
adeb9bccb1 | ||
![]() |
b44ffffed8 | ||
![]() |
2bdaf53ecf | ||
![]() |
46e7db6d94 | ||
![]() |
7e1920dc4b | ||
![]() |
a93f05c047 | ||
![]() |
00238247cd | ||
![]() |
b33e28835d | ||
![]() |
f55f803531 | ||
![]() |
8ca298b299 | ||
![]() |
090e29effd | ||
![]() |
0acca2e313 | ||
![]() |
0d77027f60 | ||
![]() |
39b7b3ad53 | ||
![]() |
00f1b483eb | ||
![]() |
c3d48acb4c | ||
![]() |
392bda7d8c | ||
![]() |
10cfcdab8c | ||
![]() |
3f71d3b250 | ||
![]() |
1b50fbab22 | ||
![]() |
303fc65a6a | ||
![]() |
445b6ee13f | ||
![]() |
8afaac1e30 | ||
![]() |
0327f9428e | ||
![]() |
a5de66bbd5 | ||
![]() |
d47157eec3 | ||
![]() |
f4b47ed399 | ||
![]() |
8b2145bd88 | ||
![]() |
de454e8b78 | ||
![]() |
6cd770b4c7 | ||
![]() |
355525c248 | ||
![]() |
47d4e7381f | ||
![]() |
5dac6690d7 | ||
![]() |
b89f7180db | ||
![]() |
2ebb837a15 | ||
![]() |
849aa64678 | ||
![]() |
cbb12e1b7c | ||
![]() |
cc87ba4962 | ||
![]() |
fb2e556726 | ||
![]() |
3f0eb0a046 | ||
![]() |
7d6d9eddc4 | ||
![]() |
cf87da0ef3 | ||
![]() |
0775acedc0 | ||
![]() |
8f1cee2e61 | ||
![]() |
caa9cc32d7 | ||
![]() |
b750f827c5 | ||
![]() |
5d147163e5 | ||
![]() |
75fe1a19eb | ||
![]() |
5c9405fffc | ||
![]() |
6457314794 | ||
![]() |
84f4e3eedc | ||
![]() |
b003ed3f03 | ||
![]() |
330da137db | ||
![]() |
9e5d45d0de | ||
![]() |
b5c15d97fa | ||
![]() |
6ddcba8917 | ||
![]() |
91598cbbbf | ||
![]() |
772c80aa85 | ||
![]() |
a28345d858 | ||
![]() |
05b532b9eb | ||
![]() |
0b0d18f182 | ||
![]() |
c1b0877956 | ||
![]() |
46b66c76ef | ||
![]() |
d00b4335b5 | ||
![]() |
7a129e6de1 | ||
![]() |
17c20276a9 | ||
![]() |
dc9dedf220 | ||
![]() |
3ac772badc | ||
![]() |
22fc58d93b | ||
![]() |
ccd3152b24 | ||
![]() |
7d929dcde6 | ||
![]() |
3a874bc8c7 | ||
![]() |
8453cd82e9 | ||
![]() |
f62e56b7ec | ||
![]() |
2ac90bbb96 | ||
![]() |
f85f2d5d22 | ||
![]() |
a94269ceb9 | ||
![]() |
476ac263fb | ||
![]() |
51a4f61a8f | ||
![]() |
267f5159a3 | ||
![]() |
96422de031 | ||
![]() |
f2043dc181 | ||
![]() |
e416ec9279 | ||
![]() |
5eb4ffb1cc | ||
![]() |
8fae964ee8 | ||
![]() |
baf49b88f4 | ||
![]() |
3577da05ac | ||
![]() |
b8e8028eb9 | ||
![]() |
a899666e68 | ||
![]() |
10a52f8cf9 | ||
![]() |
72d04a0120 | ||
![]() |
6dbed30008 | ||
![]() |
c5eac298e6 | ||
![]() |
bc18eda336 | ||
![]() |
3cefb14297 | ||
![]() |
4bc401278e | ||
![]() |
d7e3765efe | ||
![]() |
3d51d1e345 | ||
![]() |
bd23942893 | ||
![]() |
c8610b8ad2 | ||
![]() |
d0440122b9 | ||
![]() |
8d4636bbab | ||
![]() |
f59c6e7a7c | ||
![]() |
c24ab1b21d | ||
![]() |
6a01658355 | ||
![]() |
f1e2439e66 | ||
![]() |
4d89ed701d | ||
![]() |
e80594d61d | ||
![]() |
83c6f72eb0 | ||
![]() |
e26299b998 | ||
![]() |
a839809eb8 | ||
![]() |
88ceba59cf | ||
![]() |
f368bbec32 | ||
![]() |
021c4ba68a | ||
![]() |
54f4658dae | ||
![]() |
dbc67e077d | ||
![]() |
e968917dbc | ||
![]() |
d8240bb683 | ||
![]() |
b481c13829 | ||
![]() |
77c0ba990d | ||
![]() |
1d4487b6cd | ||
![]() |
ff8145b745 | ||
![]() |
530e8b39e5 | ||
![]() |
50aeee288b | ||
![]() |
72e001b0d5 | ||
![]() |
f04c9d101e | ||
![]() |
2ecc53ba56 | ||
![]() |
3eb1fe0eb2 | ||
![]() |
aec998acc1 | ||
![]() |
91e758f66f | ||
![]() |
441416b241 | ||
![]() |
e541d8697e | ||
![]() |
4b817208aa | ||
![]() |
7fea0c3244 | ||
![]() |
bd13336256 | ||
![]() |
815940913b | ||
![]() |
f7191c0381 | ||
![]() |
07d11c845c | ||
![]() |
2e9bd477d9 | ||
![]() |
b058fb8db4 | ||
![]() |
9f0f6181a1 | ||
![]() |
f702e1a80d | ||
![]() |
e1527fcbb9 | ||
![]() |
9ba7e5d567 | ||
![]() |
02b6d53544 | ||
![]() |
123bd0bb92 | ||
![]() |
6a8ed1192f | ||
![]() |
0862859f93 | ||
![]() |
3ad336a1eb | ||
![]() |
a17f83cedd | ||
![]() |
2c6850f6e4 | ||
![]() |
5da47636cf | ||
![]() |
e04b965659 | ||
![]() |
17d2fb80f2 | ||
![]() |
14b7ec2a80 | ||
![]() |
f27b31b581 | ||
![]() |
8c44147a45 | ||
![]() |
ec05215a5e | ||
![]() |
5903e8256f | ||
![]() |
c879351063 | ||
![]() |
1bb7e36a65 | ||
![]() |
793a01f7ca | ||
![]() |
40c8fdbf64 | ||
![]() |
dc01c907f1 | ||
![]() |
801df94446 | ||
![]() |
0197d89976 | ||
![]() |
e16a67242e | ||
![]() |
4c678a5010 | ||
![]() |
3754088a44 | ||
![]() |
c4f084a991 | ||
![]() |
4c73df4ba6 | ||
![]() |
4aa53aa5a5 | ||
![]() |
7483d3b229 | ||
![]() |
8b6cc708e7 | ||
![]() |
200960899e | ||
![]() |
599a456c81 | ||
![]() |
4b46502d22 | ||
![]() |
7233c55428 | ||
![]() |
a58c5cce78 | ||
![]() |
0b23bf65b3 | ||
![]() |
bc0a3f8a47 | ||
![]() |
9b2a0102be | ||
![]() |
04b4ef6d85 | ||
![]() |
9e8aadb750 | ||
![]() |
0ae0f40628 | ||
![]() |
af9aa7d201 | ||
![]() |
4cd3a614de | ||
![]() |
1e5420e6a7 | ||
![]() |
660de0b4e5 | ||
![]() |
b73aaecd22 | ||
![]() |
c831d62bc3 | ||
![]() |
1539e703e9 | ||
![]() |
f43bf03768 | ||
![]() |
495f7f190f | ||
![]() |
16216b9eb9 | ||
![]() |
dfdb22f584 | ||
![]() |
0b264176bc | ||
![]() |
bde70a27f0 | ||
![]() |
7d2f5f0799 | ||
![]() |
7610ab7a8d | ||
![]() |
51db653b1a | ||
![]() |
dc4e4395a9 | ||
![]() |
623694ab73 | ||
![]() |
374457df70 | ||
![]() |
7885dddef2 | ||
![]() |
73d6cc1e54 | ||
![]() |
8fdf84068d | ||
![]() |
131625bb53 | ||
![]() |
29c9e5cb17 | ||
![]() |
52b60fd6a6 | ||
![]() |
d6337f7500 | ||
![]() |
625e04d208 | ||
![]() |
6da657d3e2 | ||
![]() |
344c9e9238 | ||
![]() |
89b2b066ef | ||
![]() |
894e084c7f | ||
![]() |
2ba064b2a5 | ||
![]() |
dfe065ef82 | ||
![]() |
7bd4b78470 | ||
![]() |
d7991a247d | ||
![]() |
2178fd6ee9 | ||
![]() |
7019ddb165 | ||
![]() |
9f13763637 | ||
![]() |
beeba27f46 | ||
![]() |
315d4f225a | ||
![]() |
85489458d8 | ||
![]() |
bfc7f56c4d | ||
![]() |
7685f9b73d | ||
![]() |
664fad96fa | ||
![]() |
669a610e36 | ||
![]() |
7e0d9cb48c | ||
![]() |
7cbc9d21b5 | ||
![]() |
55b26751ae | ||
![]() |
c2892d7887 | ||
![]() |
6c8bf090fe | ||
![]() |
13bc378069 | ||
![]() |
8431d0bd5c | ||
![]() |
852f758be3 | ||
![]() |
b455f432d5 | ||
![]() |
306cea60a1 | ||
![]() |
ba2e07c4b9 | ||
![]() |
9b796531b2 | ||
![]() |
08d7a1c123 | ||
![]() |
1f70a735c7 | ||
![]() |
6713fcfeb1 | ||
![]() |
c3107d213a | ||
![]() |
adf5c8c278 | ||
![]() |
5f86a8a15b | ||
![]() |
042c756be8 | ||
![]() |
2d586406da | ||
![]() |
371c4e0051 | ||
![]() |
69099fcdd7 | ||
![]() |
57e50d0c33 | ||
![]() |
1617658bfe | ||
![]() |
4bcfff780a | ||
![]() |
12f9ad8f7f | ||
![]() |
6f843fcb27 | ||
![]() |
e0f17e1778 | ||
![]() |
bfb27c49a2 | ||
![]() |
cb7b7f1dca | ||
![]() |
5ca8bc3f2a | ||
![]() |
1ccc8eec0a | ||
![]() |
9c5afda83a | ||
![]() |
d94d3d4bc5 | ||
![]() |
119826cb9b | ||
![]() |
6ab95ed4ef | ||
![]() |
4f1eb64ac6 | ||
![]() |
3f8dc76f84 | ||
![]() |
f60579fd21 | ||
![]() |
136a00a301 | ||
![]() |
fa075f6800 | ||
![]() |
277f395595 | ||
![]() |
e2061464a5 | ||
![]() |
fcf5cd4655 | ||
![]() |
3816f0b68b | ||
![]() |
1a2543ddde | ||
![]() |
7c9db7edeb | ||
![]() |
8b759bc5d9 | ||
![]() |
9a0aac4745 | ||
![]() |
ced0cc1bac | ||
![]() |
3c49f22266 | ||
![]() |
13ae99edec | ||
![]() |
0f82730a78 | ||
![]() |
ff083daf31 | ||
![]() |
7f6a554e1b | ||
![]() |
eb99271120 | ||
![]() |
13e5c695c3 | ||
![]() |
12de47c923 | ||
![]() |
afde7940d8 | ||
![]() |
01dd41bdbf | ||
![]() |
f3b84f1365 | ||
![]() |
5751d5c1b0 | ||
![]() |
3d2336aac1 | ||
![]() |
afe5f19464 | ||
![]() |
4091a3c238 | ||
![]() |
f411e07fb4 | ||
![]() |
9bfe27dd5e | ||
![]() |
c4201d9a2a | ||
![]() |
58e9817a6d | ||
![]() |
48d5584491 | ||
![]() |
5786f1d057 | ||
![]() |
87c6f3c757 | ||
![]() |
0e99c948d6 | ||
![]() |
0f5e0f640b | ||
![]() |
3d2c6388de | ||
![]() |
ad8e614ae8 | ||
![]() |
48c0360877 | ||
![]() |
d230be3e1c | ||
![]() |
daa77d40a3 | ||
![]() |
6ae743684f | ||
![]() |
789c00dde1 | ||
![]() |
90da471084 | ||
![]() |
2d55056015 | ||
![]() |
ffab9bb893 | ||
![]() |
1192d04391 | ||
![]() |
f18dced2f3 | ||
![]() |
ecdc0a3800 | ||
![]() |
46d66dded8 | ||
![]() |
a69dcfc49d | ||
![]() |
601005f837 | ||
![]() |
de6f32e486 | ||
![]() |
fbe866198d | ||
![]() |
d603a8a9be | ||
![]() |
8f92077454 | ||
![]() |
8755a9bcda | ||
![]() |
4a1ecc7b72 | ||
![]() |
e46078286d | ||
![]() |
806221bc3f | ||
![]() |
4264c2a173 | ||
![]() |
1f4a15ee6b | ||
![]() |
bab4f9963f | ||
![]() |
1dbcac4f53 | ||
![]() |
8d20a13776 | ||
![]() |
213bc75ae1 | ||
![]() |
8a2256e0d8 | ||
![]() |
eee2450c9e | ||
![]() |
e2242f5d99 | ||
![]() |
30df67721d | ||
![]() |
4a20f43fbf | ||
![]() |
e06d269b82 | ||
![]() |
ba4c3e3852 | ||
![]() |
b8de36b340 | ||
![]() |
bbe3e8093c | ||
![]() |
26096bc136 | ||
![]() |
3460f9d9cc | ||
![]() |
6eacf8ed7e | ||
![]() |
20bebe98b1 | ||
![]() |
91d885eae4 | ||
![]() |
ea3358ecb2 | ||
![]() |
9b3e6270d5 | ||
![]() |
71edc3a084 | ||
![]() |
05521bfd3a | ||
![]() |
6c997f573a | ||
![]() |
c0dd98b6d7 | ||
![]() |
56cf1c818b | ||
![]() |
5553964d52 | ||
![]() |
90808ac67e | ||
![]() |
7aab7678e9 | ||
![]() |
195af002cf | ||
![]() |
38db8d2bfd | ||
![]() |
44a9fed8a9 | ||
![]() |
cfc346abad | ||
![]() |
befeb55349 | ||
![]() |
b460d0f533 | ||
![]() |
746a8badac | ||
![]() |
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 | ||
![]() |
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 | ||
![]() |
e5dc473ced | ||
![]() |
e57a518b34 | ||
![]() |
533f86c035 | ||
![]() |
9947c51af7 | ||
![]() |
409303d0f5 | ||
![]() |
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 |
22
.devcontainer/Dockerfile
Normal file
22
.devcontainer/Dockerfile
Normal file
@@ -0,0 +1,22 @@
|
||||
# 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/vscode/devcontainers/python:0-${VARIANT}
|
||||
|
||||
# [Option] Install Node.js
|
||||
ARG INSTALL_NODE="true"
|
||||
ARG NODE_VERSION="lts/*"
|
||||
RUN if [ "${INSTALL_NODE}" = "true" ]; then su vscode -c "source /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi
|
||||
|
||||
# [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
|
47
.devcontainer/devcontainer.json
Normal file
47
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"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",
|
||||
// Options
|
||||
"INSTALL_NODE": "true",
|
||||
"NODE_VERSION": "lts/*"
|
||||
}
|
||||
},
|
||||
|
||||
// Set *default* container specific settings.json values on container create.
|
||||
"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"
|
||||
},
|
||||
|
||||
// Add the IDs of extensions you want installed when the container is created.
|
||||
"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": "npm install",
|
||||
|
||||
// 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: [Aircoookie]
|
||||
custom: ['https://paypal.me/Aircoookie']
|
83
.github/ISSUE_TEMPLATE/bug.yml
vendored
Normal file
83
.github/ISSUE_TEMPLATE/bug.yml
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
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: You can find this in by going to Config -> Security & Updates -> Scroll to Bottom. Copy and paste the entire line after "Server message"
|
||||
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
|
||||
- Other
|
||||
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/Aircoookie/WLED/blob/master/CODE_OF_CONDUCT.md)
|
||||
options:
|
||||
- label: I agree to follow this project's Code of Conduct
|
||||
required: true
|
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!
|
19
.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
19
.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
name: Question
|
||||
about: Have a question about using WLED?
|
||||
title: ''
|
||||
labels: question
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Take a look at the wiki and FAQ, perhaps your question is already answered!**
|
||||
[FAQ](https://github.com/Aircoookie/WLED/wiki/FAQ)
|
||||
|
||||
**Please consider asking your question on the WLED forum or Discord**
|
||||
[Forum](https://wled.discourse.group/)
|
||||
[Discord](https://discord.gg/KuqP7NE)
|
||||
[What to post where?](https://github.com/Aircoookie/WLED/issues/658)
|
||||
|
||||
**If you do not like to use these platforms, delete this template and ask away!**
|
||||
Please keep in mind though that the issue section is generally not the preferred place for general questions.
|
20
.github/stale.yml
vendored
Normal file
20
.github/stale.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 120
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- pinned
|
||||
- keep
|
||||
- enhancement
|
||||
- confirmed
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
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!
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
86
.github/workflows/wled-ci.yml
vendored
Normal file
86
.github/workflows/wled-ci.yml
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
name: PlatformIO CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
|
||||
get_default_envs:
|
||||
name: Gather Environments
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Cache pip
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-
|
||||
- uses: actions/setup-python@v2
|
||||
- name: Install PlatformIO
|
||||
run: pip install -r requirements.txt
|
||||
- name: Get default environments
|
||||
id: envs
|
||||
run: |
|
||||
echo "::set-output name=environments::$(pio project config --json-output | jq -cr '.[0][1][0][1]')"
|
||||
outputs:
|
||||
environments: ${{ steps.envs.outputs.environments }}
|
||||
|
||||
|
||||
build:
|
||||
name: Build Enviornments
|
||||
runs-on: ubuntu-latest
|
||||
needs: get_default_envs
|
||||
strategy:
|
||||
matrix:
|
||||
environment: ${{ fromJSON(needs.get_default_envs.outputs.environments) }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Cache pip
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-
|
||||
- name: Cache PlatformIO
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
- name: Install PlatformIO
|
||||
run: pip install -r requirements.txt
|
||||
- name: Build firmware
|
||||
env:
|
||||
WLED_RELEASE: True
|
||||
run: pio run -e ${{ matrix.environment }}
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: firmware-${{ matrix.environment }}
|
||||
path: |
|
||||
build_output/firmware/*.bin
|
||||
build_output/firmware/*.gz
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
name: firmware-release
|
||||
path: build_output/release/*.bin
|
||||
release:
|
||||
name: Create Release
|
||||
runs-on: ubuntu-latest
|
||||
needs: [get_default_envs, build]
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
steps:
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: firmware-release
|
||||
- name: Create draft release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: True
|
||||
files: |
|
||||
*.bin
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
10
.gitignore
vendored
10
.gitignore
vendored
@@ -1,7 +1,17 @@
|
||||
.pio
|
||||
.cache
|
||||
.pioenvs
|
||||
.piolibdeps
|
||||
.vscode
|
||||
!.vscode/extensions.json
|
||||
/wled00/Release
|
||||
/wled00/extLibs
|
||||
/platformio_override.ini
|
||||
/wled00/my_config.h
|
||||
/build_output
|
||||
.DS_Store
|
||||
.gitignore
|
||||
.clang-format
|
||||
node_modules
|
||||
.idea
|
||||
.direnv
|
||||
|
5
.gitpod.Dockerfile
vendored
Normal file
5
.gitpod.Dockerfile
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
FROM gitpod/workspace-full
|
||||
|
||||
USER gitpod
|
||||
|
||||
RUN pip3 install -U platformio
|
12
.gitpod.yml
Normal file
12
.gitpod.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
tasks:
|
||||
- command: platformio run
|
||||
|
||||
image:
|
||||
file: .gitpod.Dockerfile
|
||||
|
||||
vscode:
|
||||
extensions:
|
||||
- ms-vscode.cpptools@0.26.3:u3GsZ5PK12Ddr79vh4TWgQ==
|
||||
- eamodio.gitlens@10.2.1:e0IYyp0efFqVsrZwsIe8CA==
|
||||
- Atishay-Jain.All-Autocomplete@0.0.23:fbZNfSpnd8XkAHGfAPS2rA==
|
||||
- 2gua.rainbow-brackets@0.0.6:Tbu8dTz0i+/bgcKQTQ5b8g==
|
35
.travis.yml
35
.travis.yml
@@ -1,35 +0,0 @@
|
||||
# Continuous Integration (CI) is the practice, in software
|
||||
# engineering, of merging all developer working copies with a shared mainline
|
||||
# several times a day < https://docs.platformio.org/page/ci/index.html >
|
||||
#
|
||||
# Documentation:
|
||||
#
|
||||
# * Travis CI Embedded Builds with PlatformIO
|
||||
# < https://docs.travis-ci.com/user/integration/platformio/ >
|
||||
#
|
||||
# * PlatformIO integration with Travis CI
|
||||
# < https://docs.platformio.org/page/ci/travis.html >
|
||||
#
|
||||
# * User Guide for `platformio ci` command
|
||||
# < https://docs.platformio.org/page/userguide/cmd_ci.html >
|
||||
#
|
||||
#
|
||||
# Please choose one of the following templates (proposed below) and uncomment
|
||||
# it (remove "# " before each line) or use own configuration according to the
|
||||
# Travis CI documentation (see above).
|
||||
#
|
||||
|
||||
language: python
|
||||
python:
|
||||
- "2.7"
|
||||
sudo: false
|
||||
cache:
|
||||
directories:
|
||||
- "~/.platformio"
|
||||
env:
|
||||
- PLATFORMIO_CI_SRC=wled00
|
||||
install:
|
||||
- pip install -U platformio
|
||||
- platformio update
|
||||
script:
|
||||
- platformio ci --project-conf=./platformio.ini -v
|
Binary file not shown.
17
.vscode/extensions.json
vendored
17
.vscode/extensions.json
vendored
@@ -1,7 +1,10 @@
|
||||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
]
|
||||
}
|
||||
{
|
||||
// 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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
1014
CHANGELOG.md
Normal file
1014
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
|
78
CONTRIBUTING.md
Normal file
78
CONTRIBUTING.md
Normal file
@@ -0,0 +1,78 @@
|
||||
## Thank you for making WLED better!
|
||||
|
||||
Here are a few suggestions to make it easier for you to contribute!
|
||||
|
||||
### Code style
|
||||
|
||||
When in doubt, it is easiest to replicate the code style you find in the files you want to edit :)
|
||||
Below are the guidelines we use 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 braches is acceptable.
|
||||
|
||||
Good:
|
||||
```cpp
|
||||
if (a == b) {
|
||||
doStuff(a);
|
||||
}
|
||||
```
|
||||
|
||||
```cpp
|
||||
if (a == b)
|
||||
{
|
||||
doStuff(a);
|
||||
}
|
||||
```
|
||||
|
||||
```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 paranthesis 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.
|
||||
Note: This is a recent change, the majority of the codebase still has comments without spaces.
|
||||
|
||||
Good:
|
||||
```
|
||||
// This is a comment.
|
||||
|
||||
/* This is a CSS inline comment */
|
||||
|
||||
/*
|
||||
* This is a comment
|
||||
* wrapping over multiple lines,
|
||||
* used in WLED for file headers and function explanations
|
||||
*/
|
||||
|
||||
<!-- This is an HTML comment -->
|
||||
```
|
||||
|
||||
There is no set character limit for a comment within a line,
|
||||
though as a rule of thumb you should wrap your comment if it exceeds the width of your editor window.
|
||||
Inline comments are OK if they describe that line only and are not exceedingly wide.
|
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 |
2312
package-lock.json
generated
Normal file
2312
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
31
package.json
Normal file
31
package.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "wled",
|
||||
"version": "0.13.1",
|
||||
"description": "Tools for WLED project",
|
||||
"main": "tools/cdata.js",
|
||||
"directories": {
|
||||
"lib": "lib",
|
||||
"test": "test"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "node tools/cdata.js",
|
||||
"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/Aircoookie/WLED.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/Aircoookie/WLED/issues"
|
||||
},
|
||||
"homepage": "https://github.com/Aircoookie/WLED#readme",
|
||||
"dependencies": {
|
||||
"clean-css": "^4.2.3",
|
||||
"html-minifier-terser": "^5.1.1",
|
||||
"inliner": "^1.13.1",
|
||||
"nodemon": "^2.0.4",
|
||||
"zlib": "^1.0.5"
|
||||
}
|
||||
}
|
9
pio-scripts/obj-dump.py
Normal file
9
pio-scripts/obj-dump.py
Normal file
@@ -0,0 +1,9 @@
|
||||
# Little convenience script to get an object dump
|
||||
|
||||
Import('env')
|
||||
|
||||
def obj_dump_after_elf(source, target, env):
|
||||
print("Create firmware.asm")
|
||||
env.Execute("xtensa-lx106-elf-objdump "+ "-D " + str(target[0]) + " > "+ "${PROGNAME}.asm")
|
||||
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.elf", [obj_dump_after_elf])
|
69
pio-scripts/output_bins.py
Normal file
69
pio-scripts/output_bins.py
Normal file
@@ -0,0 +1,69 @@
|
||||
Import('env')
|
||||
import os
|
||||
import shutil
|
||||
import gzip
|
||||
|
||||
OUTPUT_DIR = "build_output{}".format(os.path.sep)
|
||||
|
||||
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=["firmware", "map"]):
|
||||
# check if output directories exist and create if necessary
|
||||
if not os.path.isdir(OUTPUT_DIR):
|
||||
os.mkdir(OUTPUT_DIR)
|
||||
|
||||
for d in dirs:
|
||||
if not os.path.isdir("{}{}".format(OUTPUT_DIR, d)):
|
||||
os.mkdir("{}{}".format(OUTPUT_DIR, d))
|
||||
|
||||
def bin_rename_copy(source, target, env):
|
||||
_create_dirs()
|
||||
variant = env["PIOENV"]
|
||||
|
||||
# create string with location and file names based on variant
|
||||
map_file = "{}map{}{}.map".format(OUTPUT_DIR, os.path.sep, variant)
|
||||
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
|
||||
|
||||
release_name = _get_cpp_define_value(env, "WLED_RELEASE_NAME")
|
||||
|
||||
if release_name:
|
||||
_create_dirs(["release"])
|
||||
version = _get_cpp_define_value(env, "WLED_VERSION")
|
||||
release_file = "{}release{}WLED_{}_{}.bin".format(OUTPUT_DIR, os.path.sep, version, release_name)
|
||||
shutil.copy(str(target[0]), release_file)
|
||||
|
||||
# check if new target files exist and remove if necessary
|
||||
for f in [map_file, bin_file]:
|
||||
if os.path.isfile(f):
|
||||
os.remove(f)
|
||||
|
||||
# copy firmware.bin to firmware/<variant>.bin
|
||||
shutil.copy(str(target[0]), bin_file)
|
||||
|
||||
# copy firmware.map to map/<variant>.map
|
||||
if os.path.isfile("firmware.map"):
|
||||
shutil.move("firmware.map", map_file)
|
||||
|
||||
def bin_gzip(source, target, env):
|
||||
_create_dirs()
|
||||
variant = env["PIOENV"]
|
||||
|
||||
# create string with location and file names based on variant
|
||||
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
|
||||
gzip_file = "{}firmware{}{}.bin.gz".format(OUTPUT_DIR, os.path.sep, variant)
|
||||
|
||||
# check if new target files exist and remove if necessary
|
||||
if os.path.isfile(gzip_file): os.remove(gzip_file)
|
||||
|
||||
# write gzip firmware file
|
||||
with open(bin_file,"rb") as fp:
|
||||
with gzip.open(gzip_file, "wb", compresslevel = 9) as f:
|
||||
shutil.copyfileobj(fp, f)
|
||||
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_rename_copy, bin_gzip])
|
8
pio-scripts/set_version.py
Normal file
8
pio-scripts/set_version.py
Normal file
@@ -0,0 +1,8 @@
|
||||
Import('env')
|
||||
import json
|
||||
|
||||
PACKAGE_FILE = "package.json"
|
||||
|
||||
with open(PACKAGE_FILE, "r") as package:
|
||||
version = json.load(package)["version"]
|
||||
env.Append(BUILD_FLAGS=[f"-DWLED_VERSION={version}"])
|
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")
|
663
platformio.ini
663
platformio.ini
@@ -2,162 +2,553 @@
|
||||
; Please visit documentation: https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[platformio]
|
||||
src_dir = ./wled00
|
||||
data_dir = ./wled00/data
|
||||
lib_extra_dirs = ./wled00/src
|
||||
; Please uncomment one of the 5 lines below to select your board
|
||||
; env_default = nodemcuv2
|
||||
; env_default = esp01
|
||||
; env_default = esp01_1m
|
||||
; env_default = d1_mini
|
||||
; env_default = esp32dev
|
||||
# ------------------------------------------------------------------------------
|
||||
# ENVIRONMENTS
|
||||
#
|
||||
# Please uncomment one of the lines below to select your board(s)
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Travis CI binaries (use `platformio_override.ini` when building for your own board; see `platformio_override.ini.sample` for an example)
|
||||
; default_envs = travis_esp8266, travis_esp32
|
||||
|
||||
# Release binaries
|
||||
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, esp32s2_saola, esp32c3
|
||||
|
||||
# Build everything
|
||||
; default_envs = esp32dev, esp8285_4CH_MagicHome, codm-controller-0.6-rev2, codm-controller-0.6, esp32s2_saola, d1_mini_5CH_Shojo_PCB, d1_mini, sp501e, travis_esp8266, travis_esp32, nodemcuv2, esp32_eth, anavi_miracle_controller, esp07, esp01_1m_full, m5atom, h803wf, d1_mini_ota, heltec_wifi_kit_8, esp8285_H801, d1_mini_debug, wemos_shield_esp32, elekstube_ips
|
||||
|
||||
# Single binaries (uncomment your board)
|
||||
; default_envs = elekstube_ips
|
||||
; default_envs = nodemcuv2
|
||||
; default_envs = esp8266_2m
|
||||
; default_envs = esp01_1m_full
|
||||
; default_envs = esp07
|
||||
; default_envs = d1_mini
|
||||
; default_envs = heltec_wifi_kit_8
|
||||
; default_envs = h803wf
|
||||
; default_envs = d1_mini_debug
|
||||
; default_envs = d1_mini_ota
|
||||
; default_envs = esp32dev
|
||||
; default_envs = esp8285_4CH_MagicHome
|
||||
; default_envs = esp8285_H801
|
||||
; default_envs = d1_mini_5CH_Shojo_PCB
|
||||
; default_envs = wemos_shield_esp32
|
||||
; default_envs = m5atom
|
||||
; default_envs = esp32_eth
|
||||
; default_envs = esp32_eth_ota1mapp
|
||||
; default_envs = esp32s2_saola
|
||||
|
||||
src_dir = ./wled00
|
||||
data_dir = ./wled00/data
|
||||
build_cache_dir = ~/.buildcache
|
||||
extra_configs =
|
||||
platformio_override.ini
|
||||
|
||||
[common]
|
||||
framework = arduino
|
||||
monitor_speed = 115200
|
||||
board_build.flash_mode = dout
|
||||
upload_speed = 115200
|
||||
upload_speed_fast = 921600
|
||||
build_flags =
|
||||
-w ; supresses all C/C++ warnings
|
||||
; -D VERSION=0.8.5
|
||||
; -D DEBUG
|
||||
# TODO replace libs in /lib with managed libs in here if possible.
|
||||
# If they are not changed it's just a matter of setting the correct version and change the import statement
|
||||
lib_deps_external =
|
||||
#Blynk@0.5.4(changed)
|
||||
#E131@1.0.0(changed)
|
||||
FastLED@3.3.2
|
||||
NeoPixelBus@2.5.1
|
||||
ESPAsyncTCP@1.2.0
|
||||
AsyncTCP@1.0.3
|
||||
Esp Async WebServer@1.2.0
|
||||
#ArduinoJson@5.13.5
|
||||
IRremoteESP8266@2.5.5
|
||||
#Time@1.5
|
||||
#Timezone@1.2.1
|
||||
|
||||
[common:esp8266]
|
||||
# ------------------------------------------------------------------------------
|
||||
# PLATFORM:
|
||||
# !! DO NOT confuse platformio's ESP8266 development platform with Arduino core for ESP8266
|
||||
# We use Arduino Core 2.5.0 (platformIO 2.0.4) as default
|
||||
#
|
||||
# arduino core 2.3.0 = platformIO 1.5.0
|
||||
# arduino core 2.4.0 = platformIO 1.6.0
|
||||
# arduino core 2.4.1 = platformIO 1.7.3
|
||||
# arduino core 2.4.2 = platformIO 1.8.0
|
||||
# arduino core 2.5.0 = platformIO 2.0.4
|
||||
# arduino core stage = platformIO feature#stage
|
||||
# arduino core 2.6.3 = platformIO 2.3.2
|
||||
# arduino core 2.7.0 = platformIO 2.5.0
|
||||
# ------------------------------------------------------------------------------
|
||||
arduino_core_2_3_0 = espressif8266@1.5.0
|
||||
arduino_core_2_4_0 = espressif8266@1.6.0
|
||||
arduino_core_2_4_1 = espressif8266@1.7.3
|
||||
arduino_core_2_4_2 = espressif8266@1.8.0
|
||||
arduino_core_2_5_0 = espressif8266@2.0.4
|
||||
arduino_core_2_5_2 = espressif8266@2.2.3
|
||||
arduino_core_stage = https://github.com/platformio/platform-espressif8266.git#feature/stage
|
||||
platform = ${common:esp8266.arduino_core_2_5_2}
|
||||
build_flags =
|
||||
-D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH
|
||||
-Wl,-Teagle.flash.4m1m.ld ;;;; Required for core > v2.5.0 or staging version 4MB Flash 3MB SPIFFs
|
||||
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
|
||||
|
||||
[common:esp8266_1M]
|
||||
platform = espressif8266@1.8.0
|
||||
build_flags =
|
||||
-D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
|
||||
-Wl,-Teagle.flash.1m0.ld ;;;; Compile with no SPIFFS to leave space for OTA
|
||||
; -D WLED_DISABLE_MOBILE_UI
|
||||
; -D WLED_DISABLE_OTA
|
||||
; -D WLED_DISABLE_ALEXA
|
||||
-D WLED_DISABLE_BLYNK
|
||||
-D WLED_DISABLE_CRONIXIE
|
||||
; -D WLED_DISABLE_HUESYNC
|
||||
-D WLED_DISABLE_INFRARED
|
||||
# Development platforms
|
||||
arduino_core_develop = https://github.com/platformio/platform-espressif8266#develop
|
||||
arduino_core_git = https://github.com/platformio/platform-espressif8266#feature/stage
|
||||
|
||||
[common:esp8266_512k]
|
||||
platform = espressif8266@1.8.0
|
||||
build_flags =
|
||||
-D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
|
||||
-Wl,-Teagle.flash.512k0.ld ;;;; Compile with no SPIFFS
|
||||
; -D WLED_DISABLE_MOBILE_UI
|
||||
-D WLED_DISABLE_OTA
|
||||
; -D WLED_DISABLE_ALEXA
|
||||
-D WLED_DISABLE_BLYNK
|
||||
-D WLED_DISABLE_CRONIXIE
|
||||
; -D WLED_DISABLE_HUESYNC
|
||||
-D WLED_DISABLE_INFRARED
|
||||
# Platform to use for ESP8266
|
||||
platform_wled_default = ${common.arduino_core_2_7_4}
|
||||
# We use 2.7.4.7 for all, includes PWM flicker fix and Wstring optimization
|
||||
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
|
||||
|
||||
[common:esp32]
|
||||
platform = espressif32@1.7.0
|
||||
build_flags =
|
||||
-D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH
|
||||
-D ARDUINO_ARCH_ESP32
|
||||
-D WLED_DISABLE_INFRARED
|
||||
# ------------------------------------------------------------------------------
|
||||
# FLAGS: DEBUG
|
||||
#
|
||||
# ------------------------------------------------------------------------------
|
||||
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
|
||||
#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
|
||||
#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 USERMOD_SENSORSTOMQTT
|
||||
|
||||
build_unflags =
|
||||
|
||||
# enables all features for travis CI
|
||||
build_flags_all_features =
|
||||
-D WLED_ENABLE_ADALIGHT
|
||||
-D WLED_ENABLE_DMX
|
||||
-D WLED_ENABLE_MQTT
|
||||
-D WLED_ENABLE_WEBSOCKETS
|
||||
|
||||
build_flags_esp8266 = ${common.build_flags} ${esp8266.build_flags}
|
||||
build_flags_esp32 = ${common.build_flags} ${esp32.build_flags}
|
||||
|
||||
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_version.py
|
||||
post:pio-scripts/output_bins.py
|
||||
post:pio-scripts/strip-floats.py
|
||||
pre:pio-scripts/user_config_copy.py
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# COMMON SETTINGS:
|
||||
# ------------------------------------------------------------------------------
|
||||
[env]
|
||||
framework = arduino
|
||||
board_build.flash_mode = dout
|
||||
monitor_speed = 115200
|
||||
# slow upload speed (comment this out with a ';' when building for development use)
|
||||
upload_speed = 115200
|
||||
# fast upload speed (remove ';' when building for development use)
|
||||
; upload_speed = 921600
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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 changd) in the source:
|
||||
# ArduinoJson@5.13.5, Blynk@0.5.4(changed), E131@1.0.0(changed), Time@1.5, Timezone@1.2.1
|
||||
# ------------------------------------------------------------------------------
|
||||
lib_compat_mode = strict
|
||||
lib_deps =
|
||||
fastled/FastLED @ 3.5.0
|
||||
IRremoteESP8266 @ 2.8.1
|
||||
https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.4
|
||||
#For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line
|
||||
#TFT_eSPI
|
||||
#For use SSD1306 OLED display uncomment following
|
||||
#U8g2@~2.27.2
|
||||
#For Dallas sensor uncomment following 2 lines
|
||||
#OneWire@~2.3.5
|
||||
#milesburton/DallasTemperature@^3.9.0
|
||||
#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
|
||||
|
||||
extra_scripts = ${scripts_defaults.extra_scripts}
|
||||
|
||||
[esp8266]
|
||||
build_flags =
|
||||
-DESP8266
|
||||
-DFP_IN_IROM
|
||||
;-Wno-deprecated-declarations
|
||||
-Wno-register
|
||||
-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
|
||||
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
#https://github.com/lorol/LITTLEFS.git
|
||||
# ESPAsyncTCP @ 1.2.0
|
||||
ESPAsyncUDP
|
||||
makuna/NeoPixelBus @ 2.6.7 # 2.6.5/2.6.6 and newer do not compile on ESP core < 3.0.0
|
||||
|
||||
[esp32]
|
||||
#platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.3/platform-espressif32-2.0.2.3.zip
|
||||
platform = espressif32@3.5.0
|
||||
|
||||
platform_packages = framework-arduinoespressif32 @ https://github.com/Aircoookie/arduino-esp32.git#1.0.6.4
|
||||
|
||||
build_flags = -g
|
||||
-DARDUINO_ARCH_ESP32
|
||||
#-DCONFIG_LITTLEFS_FOR_IDF_3_2
|
||||
-D CONFIG_ASYNC_TCP_USE_WDT=0
|
||||
#use LITTLEFS library by lorol in ESP32 core 1.x.x instead of built-in in 2.x.x
|
||||
-D LOROL_LITTLEFS
|
||||
|
||||
default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
|
||||
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
https://github.com/lorol/LITTLEFS.git
|
||||
makuna/NeoPixelBus @ 2.6.7
|
||||
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
|
||||
|
||||
[esp32s2]
|
||||
build_flags = -g
|
||||
-DARDUINO_ARCH_ESP32
|
||||
-DARDUINO_ARCH_ESP32S2
|
||||
-DCONFIG_IDF_TARGET_ESP32S2
|
||||
-D CONFIG_ASYNC_TCP_USE_WDT=0
|
||||
-DCO
|
||||
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
makuna/NeoPixelBus @ 2.6.7
|
||||
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
|
||||
|
||||
[esp32c3]
|
||||
build_flags = -g
|
||||
-DARDUINO_ARCH_ESP32
|
||||
-DARDUINO_ARCH_ESP32C3
|
||||
-DCONFIG_IDF_TARGET_ESP32C3
|
||||
-D CONFIG_ASYNC_TCP_USE_WDT=0
|
||||
-DCO
|
||||
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
makuna/NeoPixelBus @ 2.6.7
|
||||
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# WLED BUILDS
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# see: http://docs.platformio.org/en/latest/platforms/espressif8266.html
|
||||
[env:nodemcuv2]
|
||||
board = nodemcuv2
|
||||
platform = ${common:esp8266.platform}
|
||||
monitor_speed = ${common.monitor_speed}
|
||||
upload_speed = ${common.upload_speed}
|
||||
framework = ${common.framework}
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
${common:esp8266.build_flags}
|
||||
lib_deps =
|
||||
${common.lib_deps_external}
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp8266_2m]
|
||||
board = esp_wroom_02
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP02
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp01_1m_full]
|
||||
board = esp01_1m
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_1m128k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP01 -D WLED_DISABLE_OTA
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp07]
|
||||
board = esp07
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:d1_mini]
|
||||
board = d1_mini
|
||||
platform = ${common:esp8266.platform}
|
||||
monitor_speed = ${common.monitor_speed}
|
||||
upload_speed = ${common.upload_speed}
|
||||
framework = ${common.framework}
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
${common:esp8266.build_flags}
|
||||
lib_deps =
|
||||
${common.lib_deps_external}
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
upload_speed = 921600
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
monitor_filters = esp8266_exception_decoder
|
||||
|
||||
[env:esp01_1m]
|
||||
board = esp01_1m
|
||||
platform = ${common:esp8266_1M.platform}
|
||||
monitor_speed = ${common.monitor_speed}
|
||||
upload_speed = ${common.upload_speed}
|
||||
framework = ${common.framework}
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
${common:esp8266_1M.build_flags}
|
||||
# disable IR because there is no pin for it
|
||||
-D WLED_DISABLE_INFRARED
|
||||
lib_deps =
|
||||
${common.lib_deps_external}
|
||||
[env:heltec_wifi_kit_8]
|
||||
board = d1_mini
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp01]
|
||||
board = esp01
|
||||
platform = ${common:esp8266_512k.platform}
|
||||
monitor_speed = ${common.monitor_speed}
|
||||
upload_speed = ${common.upload_speed}
|
||||
framework = ${common.framework}
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
${common:esp8266_512k.build_flags}
|
||||
lib_deps =
|
||||
${common.lib_deps_external}
|
||||
[env:h803wf]
|
||||
board = d1_mini
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D LEDPIN=1 -D WLED_DISABLE_INFRARED
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
# see: http://docs.platformio.org/en/latest/platforms/espressif32.html
|
||||
[env:esp32dev]
|
||||
board = esp32dev
|
||||
platform = ${common:esp32.platform}
|
||||
monitor_speed = ${common.monitor_speed}
|
||||
upload_speed = ${common.upload_speed_fast}
|
||||
framework = ${common.framework}
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
${common:esp32.build_flags}
|
||||
platform = ${esp32.platform}
|
||||
platform_packages = ${esp32.platform_packages}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32 #-D WLED_DISABLE_BLYNK #-D WLED_DISABLE_BROWNOUT_DET
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
monitor_filters = esp32_exception_decoder
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
|
||||
[env:esp32_eth]
|
||||
board = esp32-poe
|
||||
platform = ${esp32.platform}
|
||||
platform_packages = ${esp32.platform_packages}
|
||||
upload_speed = 921600
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_Ethernet -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1 -D WLED_DISABLE_BLYNK
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
|
||||
[env:esp32s2_saola]
|
||||
board = esp32-s2-saola-1
|
||||
platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.2/platform-tasmota-espressif32-2.0.2.zip
|
||||
platform_packages =
|
||||
framework = arduino
|
||||
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
|
||||
board_build.flash_mode = qio
|
||||
upload_speed = 460800
|
||||
build_unflags = ${common.build_unflags}
|
||||
lib_deps = ${esp32s2.lib_deps}
|
||||
|
||||
[env:esp32c3]
|
||||
board = esp32-c3-devkitm-1
|
||||
platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.2/platform-tasmota-espressif32-2.0.2.zip
|
||||
platform_packages =
|
||||
framework = arduino
|
||||
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
|
||||
upload_speed = 460800
|
||||
build_unflags = ${common.build_unflags}
|
||||
lib_deps = ${esp32c3.lib_deps}
|
||||
|
||||
[env:esp8285_4CH_MagicHome]
|
||||
board = esp8285
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_1m128k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp8285_H801]
|
||||
board = esp8285
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_1m128k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:d1_mini_5CH_Shojo_PCB]
|
||||
board = d1_mini
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_USE_SHOJO_PCB
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# DEVELOPMENT BOARDS
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
[env:d1_mini_debug]
|
||||
board = d1_mini
|
||||
build_type = debug
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} ${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 = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:anavi_miracle_controller]
|
||||
board = d1_mini
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D LEDPIN=12 -D IRPIN=-1 -D RLYPIN=2
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# custom board configurations
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
[env:wemos_shield_esp32]
|
||||
board = esp32dev
|
||||
platform = espressif32@3.2
|
||||
upload_speed = 460800
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp32}
|
||||
-D LEDPIN=16
|
||||
-D RLYPIN=19
|
||||
-D BTNPIN=17
|
||||
-D IRPIN=18
|
||||
-D 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
|
||||
olikraus/U8g2 @ ^2.28.8
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
|
||||
[env:m5atom]
|
||||
board = esp32dev
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp32} -D LEDPIN=27 -D BTNPIN=39
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
platform = espressif32@3.2
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
|
||||
[env:sp501e]
|
||||
board = esp_wroom_02
|
||||
platform = ${common.platform_wled_default}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_flags = ${common.build_flags_esp8266} -D LEDPIN=3 -D BTNPIN=1
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:sp511e]
|
||||
board = esp_wroom_02
|
||||
platform = ${common.platform_wled_default}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_flags = ${common.build_flags_esp8266} -D LEDPIN=3 -D BTNPIN=2 -D IRPIN=5 -D WLED_MAX_BUTTONS=3
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:athom7w]
|
||||
board = esp_wroom_02
|
||||
platform = ${common.platform_wled_default}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_MAX_CCT_BLEND=0 -D BTNPIN=-1 -D IRPIN=-1 -D WLED_DISABLE_INFRARED
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:athom15w]
|
||||
board = esp_wroom_02
|
||||
platform = ${common.platform_wled_default}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_USE_IC_CCT -D BTNPIN=-1 -D IRPIN=-1 -D WLED_DISABLE_INFRARED
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# travis test board configurations
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
[env:travis_esp8266]
|
||||
extends = env:d1_mini
|
||||
build_type = debug
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} ${common.debug_flags} ${common.build_flags_all_features}
|
||||
|
||||
[env:travis_esp32]
|
||||
extends = env:esp32dev
|
||||
; build_type = debug
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp32} ${common.debug_flags} ${common.build_flags_all_features}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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 = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:codm-controller-0.6-rev2]
|
||||
board = esp_wroom_02
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# EleksTube-IPS
|
||||
# ------------------------------------------------------------------------------
|
||||
[env:elekstube_ips]
|
||||
board = esp32dev
|
||||
platform = espressif32@3.2
|
||||
upload_speed = 921600
|
||||
build_flags = ${common.build_flags_esp32} -D WLED_DISABLE_BROWNOUT_DET -D WLED_DISABLE_INFRARED
|
||||
-D USERMOD_RTC
|
||||
-D USERMOD_ELEKSTUBE_IPS
|
||||
-D LEDPIN=12
|
||||
-D RLYPIN=27
|
||||
-D BTNPIN=34
|
||||
-D WLED_DISABLE_BLYNK
|
||||
-D DEFAULT_LED_COUNT=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
|
||||
lib_deps =
|
||||
${common.lib_deps_external}
|
||||
lib_ignore =
|
||||
IRremoteESP8266
|
||||
${esp32.lib_deps}
|
||||
TFT_eSPI @ ^2.3.70
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
|
63
platformio_override.ini.sample
Normal file
63
platformio_override.ini.sample
Normal file
@@ -0,0 +1,63 @@
|
||||
# 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_tasmota_1M
|
||||
|
||||
[env:WLED_tasmota_1M]
|
||||
board = esp01_1m
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_1m128k}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
; *********************************************************************
|
||||
; *** Use custom settings from file my_config.h
|
||||
-DWLED_USE_MY_CONFIG
|
||||
; *********************************************************************
|
||||
;
|
||||
;
|
||||
; *** To use the below defines/overrides, copy and paste each onto it's own line just below build_flags in the section above.
|
||||
;
|
||||
; disable specific features
|
||||
; -D WLED_DISABLE_OTA
|
||||
; -D WLED_DISABLE_ALEXA
|
||||
; -D WLED_DISABLE_BLYNK
|
||||
; -D WLED_DISABLE_HUESYNC
|
||||
; -D WLED_DISABLE_INFRARED
|
||||
; -D WLED_DISABLE_WEBSOCKETS
|
||||
; PIN defines - uncomment and change, if needed:
|
||||
; -D LEDPIN=2
|
||||
; -D BTNPIN=0
|
||||
; -D TOUCHPIN=T0
|
||||
; -D IRPIN=4
|
||||
; -D RLYPIN=12
|
||||
; -D RLYMDE=1
|
||||
; digital LED strip types - uncomment only one ! - this will disable WS281x / SK681x support
|
||||
; -D USE_APA102
|
||||
; -D USE_WS2801
|
||||
; -D USE_LPD8806
|
||||
; PIN defines for 2 wire LEDs
|
||||
-D CLKPIN=0
|
||||
-D DATAPIN=2
|
||||
; to drive analog LED strips (aka 5050) hardware configuration is no longer necessary
|
||||
; configure the settings in the UI as follows (hard):
|
||||
; for the Magic Home LED Controller use PWM pins 5,12,13,15
|
||||
; for the H801 controller use PINs 15,13,12,14 (W2 = 04)
|
||||
; for the BW-LT11 controller use PINs 12,4,14,5
|
||||
;
|
||||
; 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 DEFAULT_LED_COUNT=30
|
||||
;
|
||||
; set milliampere limit when using ESP pin to power leds
|
||||
; -D ABL_MILLIAMPS_DEFAULT =850
|
||||
;
|
||||
; 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
|
79
readme.md
79
readme.md
@@ -1,54 +1,81 @@
|
||||

|
||||
<p align="center">
|
||||
<img src="/images/wled_logo_akemi.png">
|
||||
<a href="https://github.com/Aircoookie/WLED/releases"><img src="https://img.shields.io/github/release/Aircoookie/WLED.svg?style=flat-square"></a>
|
||||
<a href="https://raw.githubusercontent.com/Aircoookie/WLED/master/LICENSE"><img src="https://img.shields.io/github/license/Aircoookie/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/KuqP7NE"><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/Aircoookie/WLED"><img src="https://img.shields.io/badge/Gitpod-ready--to--code-blue?style=flat-square&logo=gitpod"></a>
|
||||
|
||||
[](https://github.com/Aircoookie/WLED/releases)
|
||||
[](https://discord.gg/KuqP7NE)
|
||||
[](https://github.com/Aircoookie/WLED/wiki)
|
||||
[](https://github.com/Aircoookie/WLED-App)
|
||||
</p>
|
||||
|
||||
## Welcome to my project WLED!
|
||||
# Welcome to my project WLED! ✨
|
||||
|
||||
A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control NeoPixel (WS2812B, WS2811, SK6812, APA102) LEDs!
|
||||
A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control NeoPixel (WS2812B, WS2811, SK6812) LEDs or also SPI based chipsets like the WS2801 and APA102!
|
||||
|
||||
### Features:
|
||||
- WS2812FX library integrated for 80 special effects
|
||||
- FastLED noise effects and palettes
|
||||
- Customizable Mobile and desktop UI with color and effect controls
|
||||
## ⚙️ Features
|
||||
- WS2812FX library integrated for over 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 parts of the LEDs
|
||||
- Settings page - configuration over network
|
||||
- Access Point and station mode - automatic failsafe AP
|
||||
- Up to 10 LED outputs per instance
|
||||
- Support for RGBW strips
|
||||
- 25 user presets to save and load colors/effects easily, supports cycling through them.
|
||||
- Macro functions to automatically execute API calls
|
||||
- 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 updatability (HTTP + ArduinoOTA), password protectable
|
||||
- Configurable analog clock + support for the Cronixie kit by Diamex
|
||||
- Configurable analog clock (Cronixie, 7-segment and EleksTube IPS clock support via usermods)
|
||||
- Configurable Auto Brightness limit for safer operation
|
||||
- Filesystem-based config for easier backup of presets and settings
|
||||
|
||||
### Supported light control interfaces:
|
||||
- WLED app for Android and iOS
|
||||
## 💡 Supported light control interfaces
|
||||
- WLED app for [Android](https://play.google.com/store/apps/details?id=com.aircoookie.WLED) and [iOS](https://apps.apple.com/us/app/wled/id1475695033)
|
||||
- JSON and HTTP request APIs
|
||||
- MQTT
|
||||
- Blynk IoT
|
||||
- E1.31
|
||||
- Hyperion
|
||||
- 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)
|
||||
- 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:
|
||||
## 📲 Quick start guide and documentation
|
||||
|
||||
See the [wiki](https://github.com/Aircoookie/WLED/wiki)!
|
||||
See the [documentation on our official site](https://kno.wled.ge)!
|
||||
|
||||
### Other
|
||||
[On this page](https://kno.wled.ge/basics/tutorials/) you can find excellent tutorials made by the community and helpful tools to help you get your new lamp 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 MIT license
|
||||
Credits [here](https://github.com/Aircoookie/WLED/wiki/Contributors-&-About)!
|
||||
Credits [here](https://kno.wled.ge/about/contributors/)!
|
||||
|
||||
Uses Linearicons by Perxis!
|
||||
Join the Discord server to discuss everything about WLED!
|
||||
|
||||
Join the Discord [server](https://discord.gg/KuqP7NE) to discuss everything about WLED!
|
||||
You can also send me mails to [dev.aircoookie@gmail.com](mailto:dev.aircoookie@gmail.com).
|
||||
<a href="https://discord.gg/KuqP7NE"><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 every day, you can [](https://paypal.me/aircoookie)
|
||||
|
||||
|
||||
*Disclaimer:*
|
||||
If you are sensitive to photosensitive epilepsy it is not recommended that you use this software.
|
||||
In case you still want to try, don't use strobe, lighting or noise modes or high effect speed settings.
|
||||
As per the MIT 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
|
54
requirements.txt
Normal file
54
requirements.txt
Normal file
@@ -0,0 +1,54 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile
|
||||
# To update, run:
|
||||
#
|
||||
# pip-compile
|
||||
#
|
||||
aiofiles==0.6.0
|
||||
# via platformio
|
||||
ajsonrpc==1.1.0
|
||||
# via platformio
|
||||
bottle==0.12.19
|
||||
# via platformio
|
||||
certifi==2020.12.5
|
||||
# via requests
|
||||
chardet==4.0.0
|
||||
# via requests
|
||||
click==7.1.2
|
||||
# via
|
||||
# platformio
|
||||
# uvicorn
|
||||
colorama==0.4.4
|
||||
# via platformio
|
||||
h11==0.12.0
|
||||
# via
|
||||
# uvicorn
|
||||
# wsproto
|
||||
idna==2.10
|
||||
# via requests
|
||||
ifaddr==0.1.7
|
||||
# via zeroconf
|
||||
marshmallow==3.11.1
|
||||
# via platformio
|
||||
platformio==5.1.1
|
||||
# via -r requirements.in
|
||||
pyelftools==0.27
|
||||
# via platformio
|
||||
pyserial==3.5
|
||||
# via platformio
|
||||
requests==2.25.1
|
||||
# via platformio
|
||||
semantic-version==2.8.5
|
||||
# via platformio
|
||||
starlette==0.14.2
|
||||
# via platformio
|
||||
tabulate==0.8.9
|
||||
# via platformio
|
||||
urllib3==1.26.5
|
||||
# via requests
|
||||
uvicorn==0.13.4
|
||||
# via platformio
|
||||
wsproto==1.0.0
|
||||
# via platformio
|
||||
zeroconf==0.28.8
|
||||
# via platformio
|
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,
|
|
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,
|
|
6
tools/WLED_ESP32_8MB.csv
Normal file
6
tools/WLED_ESP32_8MB.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,0x3F0000,
|
|
440
tools/cdata.js
Normal file
440
tools/cdata.js
Normal file
@@ -0,0 +1,440 @@
|
||||
/**
|
||||
* Writes compressed C arrays of data files (web interface)
|
||||
* How to use it?
|
||||
*
|
||||
* 1) Install Node 11+ 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("fs");
|
||||
const packageJson = require("../package.json");
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function hexdump(buffer) {
|
||||
let lines = [];
|
||||
|
||||
for (let i = 0; i < buffer.length; i += 16) {
|
||||
let block = buffer.slice(i, i + 16); // cut buffer into blocks of 16
|
||||
let hexArray = [];
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
const inliner = require("inliner");
|
||||
const zlib = require("zlib");
|
||||
|
||||
function strReplace(str, search, replacement) {
|
||||
return str.split(search).join(replacement);
|
||||
}
|
||||
|
||||
function adoptVersionAndRepo(html) {
|
||||
let repoUrl = packageJson.repository ? packageJson.repository.url : undefined;
|
||||
if (repoUrl) {
|
||||
repoUrl = repoUrl.replace(/^git\+/, "");
|
||||
repoUrl = repoUrl.replace(/\.git$/, "");
|
||||
// Replace we
|
||||
html = strReplace(html, "https://github.com/atuline/WLED", repoUrl);
|
||||
html = strReplace(html, "https://github.com/Aircoookie/WLED", repoUrl);
|
||||
}
|
||||
|
||||
let version = packageJson.version;
|
||||
if (version) {
|
||||
html = strReplace(html, "##VERSION##", version);
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function writeHtmlGzipped(sourceFile, resultFile) {
|
||||
console.info("Reading " + sourceFile);
|
||||
new inliner(sourceFile, function (error, html) {
|
||||
console.info("Inlined " + html.length + " characters");
|
||||
html = filter(html, "html-minify-ui");
|
||||
console.info("Minified to " + html.length + " characters");
|
||||
|
||||
if (error) {
|
||||
console.warn(error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
html = adoptVersionAndRepo(html);
|
||||
zlib.gzip(html, { level: zlib.constants.Z_BEST_COMPRESSION }, function (error, result) {
|
||||
if (error) {
|
||||
console.warn(error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
console.info("Compressed " + result.length + " bytes");
|
||||
const array = hexdump(result);
|
||||
const src = `/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
// Autogenerated from ${sourceFile}, do not edit!!
|
||||
const uint16_t PAGE_index_L = ${result.length};
|
||||
const uint8_t PAGE_index[] PROGMEM = {
|
||||
${array}
|
||||
};
|
||||
`;
|
||||
console.info("Writing " + resultFile);
|
||||
fs.writeFileSync(resultFile, src);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const CleanCSS = require("clean-css");
|
||||
const MinifyHTML = require("html-minifier-terser").minify;
|
||||
|
||||
function filter(str, type) {
|
||||
str = adoptVersionAndRepo(str);
|
||||
|
||||
if (type === undefined) {
|
||||
return str;
|
||||
} else if (type == "css-minify") {
|
||||
return new CleanCSS({}).minify(str).styles;
|
||||
} else if (type == "html-minify") {
|
||||
return MinifyHTML(str, {
|
||||
collapseWhitespace: true,
|
||||
maxLineLength: 80,
|
||||
minifyCSS: true,
|
||||
minifyJS: true,
|
||||
continueOnParseError: false,
|
||||
removeComments: true,
|
||||
});
|
||||
} else if (type == "html-minify-ui") {
|
||||
return MinifyHTML(str, {
|
||||
collapseWhitespace: true,
|
||||
conservativeCollapse: true,
|
||||
maxLineLength: 80,
|
||||
minifyCSS: true,
|
||||
minifyJS: true,
|
||||
continueOnParseError: false,
|
||||
removeComments: true,
|
||||
});
|
||||
} else {
|
||||
console.warn("Unknown filter: " + type);
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
function specToChunk(srcDir, s) {
|
||||
if (s.method == "plaintext") {
|
||||
const buf = fs.readFileSync(srcDir + "/" + s.file);
|
||||
const str = buf.toString("utf-8");
|
||||
const chunk = `
|
||||
// Autogenerated from ${srcDir}/${s.file}, do not edit!!
|
||||
const char ${s.name}[] PROGMEM = R"${s.prepend || ""}${filter(str, s.filter)}${
|
||||
s.append || ""
|
||||
}";
|
||||
|
||||
`;
|
||||
return s.mangle ? s.mangle(chunk) : chunk;
|
||||
} else if (s.method == "binary") {
|
||||
const buf = fs.readFileSync(srcDir + "/" + s.file);
|
||||
const result = hexdump(buf);
|
||||
const chunk = `
|
||||
// Autogenerated from ${srcDir}/${s.file}, do not edit!!
|
||||
const uint16_t ${s.name}_length = ${result.length};
|
||||
const uint8_t ${s.name}[] PROGMEM = {
|
||||
${result}
|
||||
};
|
||||
|
||||
`;
|
||||
return s.mangle ? s.mangle(chunk) : chunk;
|
||||
} else {
|
||||
console.warn("Unknown method: " + s.method);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function writeChunks(srcDir, specs, resultFile) {
|
||||
let src = `/*
|
||||
* 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!
|
||||
*/
|
||||
`;
|
||||
specs.forEach((s) => {
|
||||
try {
|
||||
console.info("Reading " + srcDir + "/" + s.file + " as " + s.name);
|
||||
src += specToChunk(srcDir, s);
|
||||
} catch (e) {
|
||||
console.warn(
|
||||
"Failed " + s.name + " from " + srcDir + "/" + s.file,
|
||||
e.message.length > 60 ? e.message.substring(0, 60) : e.message
|
||||
);
|
||||
}
|
||||
});
|
||||
console.info("Writing " + src.length + " characters into " + resultFile);
|
||||
fs.writeFileSync(resultFile, src);
|
||||
}
|
||||
|
||||
writeHtmlGzipped("wled00/data/index.htm", "wled00/html_ui.h");
|
||||
|
||||
writeChunks(
|
||||
"wled00/data",
|
||||
[
|
||||
{
|
||||
file: "style.css",
|
||||
name: "PAGE_settingsCss",
|
||||
prepend: "=====(<style>",
|
||||
append: "</style>)=====",
|
||||
method: "plaintext",
|
||||
filter: "css-minify",
|
||||
},
|
||||
{
|
||||
file: "settings.htm",
|
||||
name: "PAGE_settings",
|
||||
prepend: "=====(",
|
||||
append: ")=====",
|
||||
method: "plaintext",
|
||||
filter: "html-minify",
|
||||
mangle: (str) =>
|
||||
str
|
||||
.replace("%", "%%")
|
||||
.replace(/Usermods\<\/button\>\<\/form\>/gms, "Usermods\<\/button\>\<\/form\>%DMXMENU%"),
|
||||
},
|
||||
{
|
||||
file: "settings_wifi.htm",
|
||||
name: "PAGE_settings_wifi",
|
||||
prepend: "=====(",
|
||||
append: ")=====",
|
||||
method: "plaintext",
|
||||
filter: "html-minify",
|
||||
mangle: (str) =>
|
||||
str
|
||||
.replace(/\<link rel="stylesheet".*\>/gms, "")
|
||||
.replace(/\<style\>.*\<\/style\>/gms, "%CSS%%SCSS%")
|
||||
.replace(
|
||||
/function GetV().*\<\/script\>/gms,
|
||||
"function GetV() {var d=document;\n"
|
||||
),
|
||||
},
|
||||
{
|
||||
file: "settings_leds.htm",
|
||||
name: "PAGE_settings_leds",
|
||||
prepend: "=====(",
|
||||
append: ")=====",
|
||||
method: "plaintext",
|
||||
filter: "html-minify",
|
||||
mangle: (str) =>
|
||||
str
|
||||
.replace(/\<link rel="stylesheet".*\>/gms, "")
|
||||
.replace(/\<style\>.*\<\/style\>/gms, "%CSS%%SCSS%")
|
||||
.replace(
|
||||
/function GetV().*\<\/script\>/gms,
|
||||
"function GetV() {var d=document;\n"
|
||||
),
|
||||
},
|
||||
{
|
||||
file: "settings_dmx.htm",
|
||||
name: "PAGE_settings_dmx",
|
||||
prepend: "=====(",
|
||||
append: ")=====",
|
||||
method: "plaintext",
|
||||
filter: "html-minify",
|
||||
mangle: (str) => {
|
||||
const nocss = str
|
||||
.replace(/\<link rel="stylesheet".*\>/gms, "")
|
||||
.replace(/\<style\>.*\<\/style\>/gms, "%CSS%%SCSS%")
|
||||
.replace(
|
||||
/function GetV().*\<\/script\>/gms,
|
||||
"function GetV() {var d=document;\n"
|
||||
);
|
||||
return `
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
${nocss}
|
||||
#else
|
||||
const char PAGE_settings_dmx[] PROGMEM = R"=====()=====";
|
||||
#endif
|
||||
`;
|
||||
},
|
||||
},
|
||||
{
|
||||
file: "settings_ui.htm",
|
||||
name: "PAGE_settings_ui",
|
||||
prepend: "=====(",
|
||||
append: ")=====",
|
||||
method: "plaintext",
|
||||
filter: "html-minify",
|
||||
mangle: (str) =>
|
||||
str
|
||||
.replace(/\<link rel="stylesheet".*\>/gms, "")
|
||||
.replace(/\<style\>.*\<\/style\>/gms, "%CSS%%SCSS%")
|
||||
.replace(
|
||||
/function GetV().*\<\/script\>/gms,
|
||||
"function GetV() {var d=document;\n"
|
||||
),
|
||||
},
|
||||
{
|
||||
file: "settings_sync.htm",
|
||||
name: "PAGE_settings_sync",
|
||||
prepend: "=====(",
|
||||
append: ")=====",
|
||||
method: "plaintext",
|
||||
filter: "html-minify",
|
||||
mangle: (str) =>
|
||||
str
|
||||
.replace(/\<link rel="stylesheet".*\>/gms, "")
|
||||
.replace(/\<style\>.*\<\/style\>/gms, "%CSS%%SCSS%")
|
||||
.replace(/function GetV().*\<\/script\>/gms, "function GetV() {\n"),
|
||||
},
|
||||
{
|
||||
file: "settings_time.htm",
|
||||
name: "PAGE_settings_time",
|
||||
prepend: "=====(",
|
||||
append: ")=====",
|
||||
method: "plaintext",
|
||||
filter: "html-minify",
|
||||
mangle: (str) =>
|
||||
str
|
||||
.replace(/\<link rel="stylesheet".*\>/gms, "")
|
||||
.replace(/\<style\>.*\<\/style\>/gms, "%CSS%%SCSS%")
|
||||
.replace(/function GetV().*\<\/script\>/gms, "function GetV() {\n"),
|
||||
},
|
||||
{
|
||||
file: "settings_sec.htm",
|
||||
name: "PAGE_settings_sec",
|
||||
prepend: "=====(",
|
||||
append: ")=====",
|
||||
method: "plaintext",
|
||||
filter: "html-minify",
|
||||
mangle: (str) =>
|
||||
str
|
||||
.replace(/\<link rel="stylesheet".*\>/gms, "")
|
||||
.replace(/\<style\>.*\<\/style\>/gms, "%CSS%%SCSS%")
|
||||
.replace(
|
||||
/function GetV().*\<\/script\>/gms,
|
||||
"function GetV() {var d=document;\n"
|
||||
),
|
||||
},
|
||||
{
|
||||
file: "settings_um.htm",
|
||||
name: "PAGE_settings_um",
|
||||
prepend: "=====(",
|
||||
append: ")=====",
|
||||
method: "plaintext",
|
||||
filter: "html-minify",
|
||||
mangle: (str) =>
|
||||
str
|
||||
.replace(/\<link rel="stylesheet".*\>/gms, "")
|
||||
.replace(/\<style\>.*\<\/style\>/gms, "%CSS%%SCSS%")
|
||||
.replace(
|
||||
/function GetV().*\<\/script\>/gms,
|
||||
"function GetV() {var d=document;\n"
|
||||
),
|
||||
}
|
||||
],
|
||||
"wled00/html_settings.h"
|
||||
);
|
||||
|
||||
writeChunks(
|
||||
"wled00/data",
|
||||
[
|
||||
{
|
||||
file: "usermod.htm",
|
||||
name: "PAGE_usermod",
|
||||
prepend: "=====(",
|
||||
append: ")=====",
|
||||
method: "plaintext",
|
||||
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",
|
||||
prepend: "=====(",
|
||||
append: ")=====",
|
||||
method: "plaintext",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "welcome.htm",
|
||||
name: "PAGE_welcome",
|
||||
prepend: "=====(",
|
||||
append: ")=====",
|
||||
method: "plaintext",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "liveview.htm",
|
||||
name: "PAGE_liveview",
|
||||
prepend: "=====(",
|
||||
append: ")=====",
|
||||
method: "plaintext",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "liveviewws.htm",
|
||||
name: "PAGE_liveviewws",
|
||||
prepend: "=====(",
|
||||
append: ")=====",
|
||||
method: "plaintext",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "404.htm",
|
||||
name: "PAGE_404",
|
||||
prepend: "=====(",
|
||||
append: ")=====",
|
||||
method: "plaintext",
|
||||
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>
|
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
|
||||
# ...
|
534
usermods/Animated_Staircase/Animated_Staircase.h
Normal file
534
usermods/Animated_Staircase/Animated_Staircase.h
Normal file
@@ -0,0 +1,534 @@
|
||||
/*
|
||||
* Usermod for detecting people entering/leaving a staircase and switching the
|
||||
* staircase on/off.
|
||||
*
|
||||
* Edit the Animated_Staircase_config.h file to compile this usermod for your
|
||||
* specific configuration.
|
||||
*
|
||||
* See the accompanying README.md file for more info.
|
||||
*/
|
||||
#pragma once
|
||||
#include "wled.h"
|
||||
|
||||
class Animated_Staircase : public Usermod {
|
||||
private:
|
||||
|
||||
/* configuration (available in API and stored in flash) */
|
||||
bool enabled = false; // Enable this usermod
|
||||
unsigned long segment_delay_ms = 150; // Time between switching each segment
|
||||
unsigned long on_time_ms = 30000; // The time for the light to stay on
|
||||
int8_t topPIRorTriggerPin = -1; // disabled
|
||||
int8_t bottomPIRorTriggerPin = -1; // disabled
|
||||
int8_t topEchoPin = -1; // disabled
|
||||
int8_t bottomEchoPin = -1; // disabled
|
||||
bool useUSSensorTop = false; // using PIR or UltraSound sensor?
|
||||
bool useUSSensorBottom = false; // using PIR or UltraSound sensor?
|
||||
unsigned int topMaxDist = 50; // default maximum measured distance in cm, top
|
||||
unsigned int bottomMaxDist = 50; // default maximum measured distance in cm, bottom
|
||||
|
||||
/* runtime variables */
|
||||
bool initDone = false;
|
||||
|
||||
// Time between checking of the sensors
|
||||
const unsigned int scanDelay = 100;
|
||||
|
||||
// Lights on or off.
|
||||
// Flipping this will start a transition.
|
||||
bool on = false;
|
||||
|
||||
// Swipe direction for current transition
|
||||
#define SWIPE_UP true
|
||||
#define SWIPE_DOWN false
|
||||
bool swipe = SWIPE_UP;
|
||||
|
||||
// Indicates which Sensor was seen last (to determine
|
||||
// the direction when swiping off)
|
||||
#define LOWER false
|
||||
#define UPPER true
|
||||
bool lastSensor = LOWER;
|
||||
|
||||
// Time of the last transition action
|
||||
unsigned long lastTime = 0;
|
||||
|
||||
// Time of the last sensor check
|
||||
unsigned long lastScanTime = 0;
|
||||
|
||||
// Last time the lights were switched on or off
|
||||
unsigned long lastSwitchTime = 0;
|
||||
|
||||
// segment id between onIndex and offIndex are on.
|
||||
// controll the swipe by setting/moving these indices around.
|
||||
// onIndex must be less than or equal to offIndex
|
||||
byte onIndex = 0;
|
||||
byte offIndex = 0;
|
||||
|
||||
// The maximum number of configured segments.
|
||||
// Dynamically updated based on user configuration.
|
||||
byte maxSegmentId = 1;
|
||||
byte mainSegmentId = 0;
|
||||
|
||||
// These values are used by the API to read the
|
||||
// last sensor state, or trigger a sensor
|
||||
// through the API
|
||||
bool topSensorRead = false;
|
||||
bool topSensorWrite = false;
|
||||
bool bottomSensorRead = false;
|
||||
bool bottomSensorWrite = false;
|
||||
bool topSensorState = false;
|
||||
bool bottomSensorState = false;
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
static const char _name[];
|
||||
static const char _enabled[];
|
||||
static const char _segmentDelay[];
|
||||
static const char _onTime[];
|
||||
static const char _useTopUltrasoundSensor[];
|
||||
static const char _topPIRorTrigger_pin[];
|
||||
static const char _topEcho_pin[];
|
||||
static const char _useBottomUltrasoundSensor[];
|
||||
static const char _bottomPIRorTrigger_pin[];
|
||||
static const char _bottomEcho_pin[];
|
||||
static const char _topEchoCm[];
|
||||
static const char _bottomEchoCm[];
|
||||
|
||||
void publishMqtt(bool bottom, const char* state)
|
||||
{
|
||||
//Check if MQTT Connected, otherwise it will crash the 8266
|
||||
if (WLED_MQTT_CONNECTED){
|
||||
char subuf[64];
|
||||
sprintf_P(subuf, PSTR("%s/motion/%d"), mqttDeviceTopic, (int)bottom);
|
||||
mqtt->publish(subuf, 0, false, state);
|
||||
}
|
||||
}
|
||||
|
||||
void updateSegments() {
|
||||
mainSegmentId = strip.getMainSegmentId();
|
||||
WS2812FX::Segment* segments = strip.getSegments();
|
||||
for (int i = 0; i < MAX_NUM_SEGMENTS; i++, segments++) {
|
||||
if (!segments->isActive()) {
|
||||
maxSegmentId = i - 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= onIndex && i < offIndex) {
|
||||
segments->setOption(SEG_OPTION_ON, 1, i);
|
||||
|
||||
// We may need to copy mode and colors from segment 0 to make sure
|
||||
// changes are propagated even when the config is changed during a wipe
|
||||
// segments->mode = mainsegment.mode;
|
||||
// segments->colors[0] = mainsegment.colors[0];
|
||||
} else {
|
||||
segments->setOption(SEG_OPTION_ON, 0, i);
|
||||
}
|
||||
// Always mark segments as "transitional", we are animating the staircase
|
||||
segments->setOption(SEG_OPTION_TRANSITIONAL, 1, i);
|
||||
}
|
||||
colorUpdated(CALL_MODE_DIRECT_CHANGE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Detects if an object is within ultrasound range.
|
||||
* signalPin: The pin where the pulse is sent
|
||||
* echoPin: The pin where the echo is received
|
||||
* maxTimeUs: Detection timeout in microseconds. If an echo is
|
||||
* received within this time, an object is detected
|
||||
* and the function will return true.
|
||||
*
|
||||
* The speed of sound is 343 meters per second at 20 degress Celcius.
|
||||
* Since the sound has to travel back and forth, the detection
|
||||
* distance for the sensor in cm is (0.0343 * maxTimeUs) / 2.
|
||||
*
|
||||
* For practical reasons, here are some useful distances:
|
||||
*
|
||||
* Distance = maxtime
|
||||
* 5 cm = 292 uS
|
||||
* 10 cm = 583 uS
|
||||
* 20 cm = 1166 uS
|
||||
* 30 cm = 1749 uS
|
||||
* 50 cm = 2915 uS
|
||||
* 100 cm = 5831 uS
|
||||
*/
|
||||
bool ultrasoundRead(int8_t signalPin, int8_t echoPin, unsigned int maxTimeUs) {
|
||||
if (signalPin<0 || echoPin<0) return false;
|
||||
digitalWrite(signalPin, LOW);
|
||||
delayMicroseconds(2);
|
||||
digitalWrite(signalPin, HIGH);
|
||||
delayMicroseconds(10);
|
||||
digitalWrite(signalPin, LOW);
|
||||
return pulseIn(echoPin, HIGH, maxTimeUs) > 0;
|
||||
}
|
||||
|
||||
bool checkSensors() {
|
||||
bool sensorChanged = false;
|
||||
|
||||
if ((millis() - lastScanTime) > scanDelay) {
|
||||
lastScanTime = millis();
|
||||
|
||||
bottomSensorRead = bottomSensorWrite ||
|
||||
(!useUSSensorBottom ?
|
||||
(bottomPIRorTriggerPin<0 ? false : digitalRead(bottomPIRorTriggerPin)) :
|
||||
ultrasoundRead(bottomPIRorTriggerPin, bottomEchoPin, bottomMaxDist*59) // cm to us
|
||||
);
|
||||
topSensorRead = topSensorWrite ||
|
||||
(!useUSSensorTop ?
|
||||
(topPIRorTriggerPin<0 ? false : digitalRead(topPIRorTriggerPin)) :
|
||||
ultrasoundRead(topPIRorTriggerPin, topEchoPin, topMaxDist*59) // cm to us
|
||||
);
|
||||
|
||||
if (bottomSensorRead != bottomSensorState) {
|
||||
bottomSensorState = bottomSensorRead; // change previous state
|
||||
sensorChanged = true;
|
||||
publishMqtt(true, bottomSensorState ? "on" : "off");
|
||||
DEBUG_PRINTLN(F("Bottom sensor changed."));
|
||||
}
|
||||
|
||||
if (topSensorRead != topSensorState) {
|
||||
topSensorState = topSensorRead; // change previous state
|
||||
sensorChanged = true;
|
||||
publishMqtt(false, topSensorState ? "on" : "off");
|
||||
DEBUG_PRINTLN(F("Top sensor changed."));
|
||||
}
|
||||
|
||||
// Values read, reset the flags for next API call
|
||||
topSensorWrite = false;
|
||||
bottomSensorWrite = false;
|
||||
|
||||
if (topSensorRead != bottomSensorRead) {
|
||||
lastSwitchTime = millis();
|
||||
|
||||
if (on) {
|
||||
lastSensor = topSensorRead;
|
||||
} else {
|
||||
// If the bottom sensor triggered, we need to swipe up, ON
|
||||
swipe = bottomSensorRead;
|
||||
|
||||
DEBUG_PRINT(F("ON -> Swipe "));
|
||||
DEBUG_PRINTLN(swipe ? F("up.") : F("down."));
|
||||
|
||||
if (onIndex == offIndex) {
|
||||
// Position the indices for a correct on-swipe
|
||||
if (swipe == SWIPE_UP) {
|
||||
onIndex = mainSegmentId;
|
||||
} else {
|
||||
onIndex = maxSegmentId+1;
|
||||
}
|
||||
offIndex = onIndex;
|
||||
}
|
||||
on = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return sensorChanged;
|
||||
}
|
||||
|
||||
void autoPowerOff() {
|
||||
if (on && ((millis() - lastSwitchTime) > on_time_ms)) {
|
||||
// if sensors are still on, do nothing
|
||||
if (bottomSensorState || topSensorState) return;
|
||||
|
||||
// Swipe OFF in the direction of the last sensor detection
|
||||
swipe = lastSensor;
|
||||
on = false;
|
||||
|
||||
DEBUG_PRINT(F("OFF -> Swipe "));
|
||||
DEBUG_PRINTLN(swipe ? F("up.") : F("down."));
|
||||
}
|
||||
}
|
||||
|
||||
void updateSwipe() {
|
||||
if ((millis() - lastTime) > segment_delay_ms) {
|
||||
lastTime = millis();
|
||||
|
||||
if (on) {
|
||||
// Turn on all segments
|
||||
onIndex = MAX(mainSegmentId, onIndex - 1);
|
||||
offIndex = MIN(maxSegmentId + 1, offIndex + 1);
|
||||
} else {
|
||||
if (swipe == SWIPE_UP) {
|
||||
onIndex = MIN(offIndex, onIndex + 1);
|
||||
} else {
|
||||
offIndex = MAX(onIndex, offIndex - 1);
|
||||
}
|
||||
}
|
||||
updateSegments();
|
||||
}
|
||||
}
|
||||
|
||||
// send sesnor values to JSON API
|
||||
void writeSensorsToJson(JsonObject& staircase) {
|
||||
staircase[F("top-sensor")] = topSensorRead;
|
||||
staircase[F("bottom-sensor")] = bottomSensorRead;
|
||||
}
|
||||
|
||||
// allow overrides from JSON API
|
||||
void readSensorsFromJson(JsonObject& staircase) {
|
||||
bottomSensorWrite = bottomSensorState || (staircase[F("bottom-sensor")].as<bool>());
|
||||
topSensorWrite = topSensorState || (staircase[F("top-sensor")].as<bool>());
|
||||
}
|
||||
|
||||
void enable(bool enable) {
|
||||
if (enable) {
|
||||
DEBUG_PRINTLN(F("Animated Staircase enabled."));
|
||||
DEBUG_PRINT(F("Delay between steps: "));
|
||||
DEBUG_PRINT(segment_delay_ms);
|
||||
DEBUG_PRINT(F(" milliseconds.\nStairs switch off after: "));
|
||||
DEBUG_PRINT(on_time_ms / 1000);
|
||||
DEBUG_PRINTLN(F(" seconds."));
|
||||
|
||||
if (!useUSSensorBottom)
|
||||
pinMode(bottomPIRorTriggerPin, INPUT_PULLUP);
|
||||
else {
|
||||
pinMode(bottomPIRorTriggerPin, OUTPUT);
|
||||
pinMode(bottomEchoPin, INPUT);
|
||||
}
|
||||
|
||||
if (!useUSSensorTop)
|
||||
pinMode(topPIRorTriggerPin, INPUT_PULLUP);
|
||||
else {
|
||||
pinMode(topPIRorTriggerPin, OUTPUT);
|
||||
pinMode(topEchoPin, INPUT);
|
||||
}
|
||||
} else {
|
||||
// Restore segment options
|
||||
WS2812FX::Segment* segments = strip.getSegments();
|
||||
for (int i = 0; i < MAX_NUM_SEGMENTS; i++, segments++) {
|
||||
if (!segments->isActive()) {
|
||||
maxSegmentId = i - 1;
|
||||
break;
|
||||
}
|
||||
segments->setOption(SEG_OPTION_ON, 1, i);
|
||||
}
|
||||
colorUpdated(CALL_MODE_DIRECT_CHANGE);
|
||||
DEBUG_PRINTLN(F("Animated Staircase disabled."));
|
||||
}
|
||||
enabled = enable;
|
||||
}
|
||||
|
||||
public:
|
||||
void setup() {
|
||||
// standardize invalid pin numbers to -1
|
||||
if (topPIRorTriggerPin < 0) topPIRorTriggerPin = -1;
|
||||
if (topEchoPin < 0) topEchoPin = -1;
|
||||
if (bottomPIRorTriggerPin < 0) bottomPIRorTriggerPin = -1;
|
||||
if (bottomEchoPin < 0) bottomEchoPin = -1;
|
||||
// allocate pins
|
||||
PinManagerPinType pins[4] = {
|
||||
{ topPIRorTriggerPin, useUSSensorTop },
|
||||
{ topEchoPin, false },
|
||||
{ bottomPIRorTriggerPin, useUSSensorBottom },
|
||||
{ bottomEchoPin, false },
|
||||
};
|
||||
// NOTE: this *WILL* return TRUE if all the pins are set to -1.
|
||||
// this is *BY DESIGN*.
|
||||
if (!pinManager.allocateMultiplePins(pins, 4, PinOwner::UM_AnimatedStaircase)) {
|
||||
topPIRorTriggerPin = -1;
|
||||
topEchoPin = -1;
|
||||
bottomPIRorTriggerPin = -1;
|
||||
bottomEchoPin = -1;
|
||||
enabled = false;
|
||||
}
|
||||
enable(enabled);
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (!enabled || strip.isUpdating()) return;
|
||||
checkSensors();
|
||||
autoPowerOff();
|
||||
updateSwipe();
|
||||
}
|
||||
|
||||
uint16_t getId() { return USERMOD_ID_ANIMATED_STAIRCASE; }
|
||||
|
||||
/**
|
||||
* handling of MQTT message
|
||||
* topic only contains stripped topic (part after /wled/MAC)
|
||||
* topic should look like: /swipe with amessage of [up|down]
|
||||
*/
|
||||
bool onMqttMessage(char* topic, char* payload) {
|
||||
if (strlen(topic) == 6 && strncmp_P(topic, PSTR("/swipe"), 6) == 0) {
|
||||
String action = payload;
|
||||
if (action == "up") {
|
||||
bottomSensorWrite = true;
|
||||
return true;
|
||||
} else if (action == "down") {
|
||||
topSensorWrite = true;
|
||||
return true;
|
||||
} else if (action == "on") {
|
||||
enable(true);
|
||||
return true;
|
||||
} else if (action == "off") {
|
||||
enable(false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* subscribe to MQTT topic for controlling usermod
|
||||
*/
|
||||
void onMqttConnect(bool sessionPresent) {
|
||||
//(re)subscribe to required topics
|
||||
char subuf[64];
|
||||
if (mqttDeviceTopic[0] != 0) {
|
||||
strcpy(subuf, mqttDeviceTopic);
|
||||
strcat_P(subuf, PSTR("/swipe"));
|
||||
mqtt->subscribe(subuf, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void addToJsonState(JsonObject& root) {
|
||||
JsonObject staircase = root[FPSTR(_name)];
|
||||
if (staircase.isNull()) {
|
||||
staircase = root.createNestedObject(FPSTR(_name));
|
||||
}
|
||||
writeSensorsToJson(staircase);
|
||||
DEBUG_PRINTLN(F("Staircase sensor state exposed in API."));
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads configuration settings from the json API.
|
||||
* See void addToJsonState(JsonObject& root)
|
||||
*/
|
||||
void readFromJsonState(JsonObject& root) {
|
||||
if (!initDone) return; // prevent crash on boot applyPreset()
|
||||
JsonObject staircase = root[FPSTR(_name)];
|
||||
if (!staircase.isNull()) {
|
||||
if (staircase[FPSTR(_enabled)].is<bool>()) {
|
||||
enabled = staircase[FPSTR(_enabled)].as<bool>();
|
||||
} else {
|
||||
String str = staircase[FPSTR(_enabled)]; // checkbox -> off or on
|
||||
enabled = (bool)(str!="off"); // off is guaranteed to be present
|
||||
}
|
||||
readSensorsFromJson(staircase);
|
||||
DEBUG_PRINTLN(F("Staircase sensor state read from API."));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes the configuration to internal flash memory.
|
||||
*/
|
||||
void addToConfig(JsonObject& root) {
|
||||
JsonObject staircase = root[FPSTR(_name)];
|
||||
if (staircase.isNull()) {
|
||||
staircase = root.createNestedObject(FPSTR(_name));
|
||||
}
|
||||
staircase[FPSTR(_enabled)] = enabled;
|
||||
staircase[FPSTR(_segmentDelay)] = segment_delay_ms;
|
||||
staircase[FPSTR(_onTime)] = on_time_ms / 1000;
|
||||
staircase[FPSTR(_useTopUltrasoundSensor)] = useUSSensorTop;
|
||||
staircase[FPSTR(_topPIRorTrigger_pin)] = topPIRorTriggerPin;
|
||||
staircase[FPSTR(_topEcho_pin)] = useUSSensorTop ? topEchoPin : -1;
|
||||
staircase[FPSTR(_useBottomUltrasoundSensor)] = useUSSensorBottom;
|
||||
staircase[FPSTR(_bottomPIRorTrigger_pin)] = bottomPIRorTriggerPin;
|
||||
staircase[FPSTR(_bottomEcho_pin)] = useUSSensorBottom ? bottomEchoPin : -1;
|
||||
staircase[FPSTR(_topEchoCm)] = topMaxDist;
|
||||
staircase[FPSTR(_bottomEchoCm)] = bottomMaxDist;
|
||||
DEBUG_PRINTLN(F("Staircase config saved."));
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads the configuration to internal flash memory before setup() is called.
|
||||
*
|
||||
* The function should return true if configuration was successfully loaded or false if there was no configuration.
|
||||
*/
|
||||
bool readFromConfig(JsonObject& root) {
|
||||
bool oldUseUSSensorTop = useUSSensorTop;
|
||||
bool oldUseUSSensorBottom = useUSSensorBottom;
|
||||
int8_t oldTopAPin = topPIRorTriggerPin;
|
||||
int8_t oldTopBPin = topEchoPin;
|
||||
int8_t oldBottomAPin = bottomPIRorTriggerPin;
|
||||
int8_t oldBottomBPin = bottomEchoPin;
|
||||
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
enabled = top[FPSTR(_enabled)] | enabled;
|
||||
|
||||
segment_delay_ms = top[FPSTR(_segmentDelay)] | segment_delay_ms;
|
||||
segment_delay_ms = (unsigned long) min((unsigned long)10000,max((unsigned long)10,(unsigned long)segment_delay_ms)); // max delay 10s
|
||||
|
||||
on_time_ms = top[FPSTR(_onTime)] | on_time_ms/1000;
|
||||
on_time_ms = min(900,max(10,(int)on_time_ms)) * 1000; // min 10s, max 15min
|
||||
|
||||
useUSSensorTop = top[FPSTR(_useTopUltrasoundSensor)] | useUSSensorTop;
|
||||
topPIRorTriggerPin = top[FPSTR(_topPIRorTrigger_pin)] | topPIRorTriggerPin;
|
||||
topEchoPin = top[FPSTR(_topEcho_pin)] | topEchoPin;
|
||||
|
||||
useUSSensorBottom = top[FPSTR(_useBottomUltrasoundSensor)] | useUSSensorBottom;
|
||||
bottomPIRorTriggerPin = top[FPSTR(_bottomPIRorTrigger_pin)] | bottomPIRorTriggerPin;
|
||||
bottomEchoPin = top[FPSTR(_bottomEcho_pin)] | bottomEchoPin;
|
||||
|
||||
topMaxDist = top[FPSTR(_topEchoCm)] | topMaxDist;
|
||||
topMaxDist = min(150,max(30,(int)topMaxDist)); // max distnace ~1.5m (a lag of 9ms may be expected)
|
||||
bottomMaxDist = top[FPSTR(_bottomEchoCm)] | bottomMaxDist;
|
||||
bottomMaxDist = min(150,max(30,(int)bottomMaxDist)); // max distance ~1.5m (a lag of 9ms may be expected)
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
if (!initDone) {
|
||||
// first run: reading from cfg.json
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
// changing parameters from settings page
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
bool changed = false;
|
||||
if ((oldUseUSSensorTop != useUSSensorTop) ||
|
||||
(oldUseUSSensorBottom != useUSSensorBottom) ||
|
||||
(oldTopAPin != topPIRorTriggerPin) ||
|
||||
(oldTopBPin != topEchoPin) ||
|
||||
(oldBottomAPin != bottomPIRorTriggerPin) ||
|
||||
(oldBottomBPin != bottomEchoPin)) {
|
||||
changed = true;
|
||||
pinManager.deallocatePin(oldTopAPin, PinOwner::UM_AnimatedStaircase);
|
||||
pinManager.deallocatePin(oldTopBPin, PinOwner::UM_AnimatedStaircase);
|
||||
pinManager.deallocatePin(oldBottomAPin, PinOwner::UM_AnimatedStaircase);
|
||||
pinManager.deallocatePin(oldBottomBPin, PinOwner::UM_AnimatedStaircase);
|
||||
}
|
||||
if (changed) setup();
|
||||
}
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Shows the delay between steps and power-off time in the "info"
|
||||
* tab of the web-UI.
|
||||
*/
|
||||
void addToJsonInfo(JsonObject& root) {
|
||||
JsonObject staircase = root["u"];
|
||||
if (staircase.isNull()) {
|
||||
staircase = root.createNestedObject("u");
|
||||
}
|
||||
|
||||
JsonArray usermodEnabled = staircase.createNestedArray(F("Staircase")); // name
|
||||
String btn = F("<button class=\"btn infobtn\" onclick=\"requestJson({staircase:{enabled:");
|
||||
if (enabled) {
|
||||
btn += F("false}});\">");
|
||||
btn += F("enabled");
|
||||
} else {
|
||||
btn += F("true}});\">");
|
||||
btn += F("disabled");
|
||||
}
|
||||
btn += F("</button>");
|
||||
usermodEnabled.add(btn); // value
|
||||
}
|
||||
};
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
const char Animated_Staircase::_name[] PROGMEM = "staircase";
|
||||
const char Animated_Staircase::_enabled[] PROGMEM = "enabled";
|
||||
const char Animated_Staircase::_segmentDelay[] PROGMEM = "segment-delay-ms";
|
||||
const char Animated_Staircase::_onTime[] PROGMEM = "on-time-s";
|
||||
const char Animated_Staircase::_useTopUltrasoundSensor[] PROGMEM = "useTopUltrasoundSensor";
|
||||
const char Animated_Staircase::_topPIRorTrigger_pin[] PROGMEM = "topPIRorTrigger_pin";
|
||||
const char Animated_Staircase::_topEcho_pin[] PROGMEM = "topEcho_pin";
|
||||
const char Animated_Staircase::_useBottomUltrasoundSensor[] PROGMEM = "useBottomUltrasoundSensor";
|
||||
const char Animated_Staircase::_bottomPIRorTrigger_pin[] PROGMEM = "bottomPIRorTrigger_pin";
|
||||
const char Animated_Staircase::_bottomEcho_pin[] PROGMEM = "bottomEcho_pin";
|
||||
const char Animated_Staircase::_topEchoCm[] PROGMEM = "top-dist-cm";
|
||||
const char Animated_Staircase::_bottomEchoCm[] PROGMEM = "bottom-dist-cm";
|
131
usermods/Animated_Staircase/README.md
Normal file
131
usermods/Animated_Staircase/README.md
Normal file
@@ -0,0 +1,131 @@
|
||||
# Usermod Animated Staircase
|
||||
This usermod makes your staircase look cool by switching it on with an animation. It uses
|
||||
PIR or ultrasonic sensors at the top and bottom of your stairs to:
|
||||
|
||||
- Light up the steps in your walking direction, leading the way.
|
||||
- Switch off the steps after you, in the direction of the last detected movement.
|
||||
- Always switch on when one of the sensors detects movement, even if an effect
|
||||
is still running. It can therewith handle multiple people on the stairs gracefully.
|
||||
|
||||
The Animated Staircase can be controlled by the WLED API. Change settings such as
|
||||
speed, on/off time and distance settings by sending an HTTP request, see below.
|
||||
|
||||
## WLED integration
|
||||
To include this usermod in your WLED setup, you have to be able to [compile WLED from source](https://github.com/Aircoookie/WLED/wiki/Compiling-WLED).
|
||||
|
||||
Before compiling, you have to make the following modifications:
|
||||
|
||||
Edit `usermods_list.cpp`:
|
||||
1. Open `wled00/usermods_list.cpp`
|
||||
2. add `#include "../usermods/Animated_Staircase/Animated_Staircase.h"` to the top of the file
|
||||
3. add `usermods.add(new Animated_Staircase());` to the end of the `void registerUsermods()` function.
|
||||
|
||||
You can configure usermod using Usermods settings page.
|
||||
Please enter GPIO pins for PIR sensors or ultrasonic sensor (trigger and echo).
|
||||
If you use PIR sensor enter -1 for echo pin.
|
||||
Maximum distance for ultrasonic sensor can be configured as a time needed for echo (see below).
|
||||
|
||||
## Hardware installation
|
||||
1. Stick the LED strip under each step of the stairs.
|
||||
2. Connect the ESP8266 pin D4 or ESP32 pin D2 to the first LED data pin at the bottom step
|
||||
of your stairs.
|
||||
3. Connect the data-out pin at the end of each strip per step to the data-in pin on the
|
||||
other end of the next step, creating one large virtual LED strip.
|
||||
4. Mount sensors of choice at the bottom and top of the stairs and connect them to the ESP.
|
||||
5. To make sure all LEDs get enough power and have your staircase lighted evenly, power each
|
||||
step from one side, using at least AWG14 or 2.5mm^2 cable. Don't connect them serial as you
|
||||
do for the datacable!
|
||||
|
||||
You _may_ need to use 10k pull-down resistors on the selected PIR pins, depending on the sensor.
|
||||
|
||||
## WLED configuration
|
||||
1. In the WLED UI, confgure a segment for each step. The lowest step of the stairs is the
|
||||
lowest segment id.
|
||||
2. Save your segments into a preset.
|
||||
3. Ideally, add the preset in the config > LED setup menu to the "apply
|
||||
preset **n** at boot" setting.
|
||||
|
||||
## Changing behavior through API
|
||||
The Staircase settings can be changed through the WLED JSON api.
|
||||
|
||||
**NOTE:** We are using [curl](https://curl.se/) to send HTTP POSTs to the WLED API.
|
||||
If you're using Windows and want to use the curl commands, replace the `\` with a `^`
|
||||
or remove them and put everything on one line.
|
||||
|
||||
|
||||
| Setting | Description | Default |
|
||||
|------------------|---------------------------------------------------------------|---------|
|
||||
| enabled | Enable or disable the usermod | true |
|
||||
| bottom-sensor | Manually trigger a down to up animation via API | false |
|
||||
| top-sensor | Manually trigger an up to down animation via API | false |
|
||||
|
||||
|
||||
To read the current settings, open a browser to `http://xxx.xxx.xxx.xxx/json/state` (use your WLED
|
||||
device IP address). The device will respond with a json object containing all WLED settings.
|
||||
The staircase settings and sensor states are inside the WLED status element:
|
||||
|
||||
```json
|
||||
{
|
||||
"state": {
|
||||
"staircase": {
|
||||
"enabled": true,
|
||||
"bottom-sensor": false,
|
||||
"tops-ensor": false
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### Enable/disable the usermod
|
||||
By disabling the usermod you will be able to keep the LED's on, independent from the sensor
|
||||
activity. This enables to play with the lights without the usermod switching them on or off.
|
||||
|
||||
To disable the usermod:
|
||||
|
||||
```bash
|
||||
curl -X POST -H "Content-Type: application/json" \
|
||||
-d {"staircase":{"enabled":false}} \
|
||||
xxx.xxx.xxx.xxx/json/state
|
||||
```
|
||||
|
||||
To enable the usermod again, use `"enabled":true`.
|
||||
|
||||
Alternatively you can use _Usermod_ Settings page where you can change other parameters as well.
|
||||
|
||||
### Changing animation parameters and detection range of the ultrasonic HC-SR04 sensor
|
||||
Using _Usermod_ Settings page you can define different usermod parameters, includng sensor pins, delay between segment activation and so on.
|
||||
|
||||
When an ultrasonic sensor is enabled you can enter maximum detection distance in centimeters separately for top and bottom sensors.
|
||||
|
||||
**Please note:** that using an HC-SR04 sensor, particularly when detecting echos at longer
|
||||
distances creates delays in the WLED software, and _might_ introduce timing hickups in your animations or
|
||||
a less responsive web interface. It is therefore advised to keep the detection distance as short as possible.
|
||||
|
||||
### Animation triggering through the API
|
||||
Instead of stairs activation by one of the sensors, you can also trigger the animation through
|
||||
the API. To simulate triggering the bottom sensor, use:
|
||||
|
||||
```bash
|
||||
curl -X POST -H "Content-Type: application/json" \
|
||||
-d '{"staircase":{"bottom-sensor":true}}' \
|
||||
xxx.xxx.xxx.xxx/json/state
|
||||
```
|
||||
|
||||
Likewise, to trigger the top sensor, use:
|
||||
|
||||
```bash
|
||||
curl -X POST -H "Content-Type: application/json" \
|
||||
-d '{"staircase":{"top-sensor":true}}' \
|
||||
xxx.xxx.xxx.xxx/json/state
|
||||
```
|
||||
**MQTT**
|
||||
You can publish a message with either `up` or `down` on topic `/swipe` to trigger animation.
|
||||
You can also use `on` or `off` for enabling or disabling usermod.
|
||||
|
||||
Have fun with this usermod.<br/>
|
||||
www.rolfje.com
|
||||
|
||||
Modifications @blazoncek
|
||||
|
||||
## Change log
|
||||
2021-04
|
||||
* Adaptation for runtime configuration.
|
5
usermods/Artemis_reciever/readme.md
Normal file
5
usermods/Artemis_reciever/readme.md
Normal file
@@ -0,0 +1,5 @@
|
||||
Usermod to allow WLED to receive via UDP port from RGB.NET (and therefore add as a device to be controlled within artemis on PC)
|
||||
|
||||
This is only a very simple code to support a single led strip, it does not support the full function of the RGB.NET sketch for esp8266 only what is needed to be used with Artemis. It will show as a ws281x device in artemis when you provide the correct hostname or ip. Artemis queries the number of LEDs via the web interface (/config) but communication to set the LEDs is all done via the UDP interface.
|
||||
|
||||
To install, copy the usermod.cpp file to wled00 folder and recompile
|
93
usermods/Artemis_reciever/usermod.cpp
Normal file
93
usermods/Artemis_reciever/usermod.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* RGB.NET (artemis) receiver
|
||||
*
|
||||
* This works via the UDP, http is not supported apart from reporting LED count
|
||||
*
|
||||
*
|
||||
*/
|
||||
#include "wled.h"
|
||||
#include <WiFiUdp.h>
|
||||
|
||||
WiFiUDP UDP;
|
||||
const unsigned int RGBNET_localUdpPort = 1872; // local port to listen on
|
||||
unsigned char RGBNET_packet[770];
|
||||
long lastTime = 0;
|
||||
int delayMs = 10;
|
||||
bool isRGBNETUDPEnabled;
|
||||
|
||||
void RGBNET_readValues() {
|
||||
|
||||
int RGBNET_packetSize = UDP.parsePacket();
|
||||
if (RGBNET_packetSize) {
|
||||
// receive incoming UDP packets
|
||||
int sequenceNumber = UDP.read();
|
||||
int channel = UDP.read();
|
||||
|
||||
//channel data is not used we only supports one channel
|
||||
int len = UDP.read(RGBNET_packet, strip.getLengthTotal()*3);
|
||||
if(len==0){
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < len; i=i+3) {
|
||||
strip.setPixelColor(i/3, RGBNET_packet[i], RGBNET_packet[i+1], RGBNET_packet[i+2], 0);
|
||||
}
|
||||
//strip.show();
|
||||
}
|
||||
}
|
||||
|
||||
//update LED strip
|
||||
void RGBNET_show() {
|
||||
strip.show();
|
||||
lastTime = millis();
|
||||
}
|
||||
|
||||
//This function provides a json with info on the number of LEDs connected
|
||||
// it is needed by artemis to know how many LEDs to display on the surface
|
||||
void handleConfig(AsyncWebServerRequest *request)
|
||||
{
|
||||
String config = (String)"{\
|
||||
\"channels\": [\
|
||||
{\
|
||||
\"channel\": 1,\
|
||||
\"leds\": " + strip.getLengthTotal() + "\
|
||||
},\
|
||||
{\
|
||||
\"channel\": 2,\
|
||||
\"leds\": " + "0" + "\
|
||||
},\
|
||||
{\
|
||||
\"channel\": 3,\
|
||||
\"leds\": " + "0" + "\
|
||||
},\
|
||||
{\
|
||||
\"channel\": 4,\
|
||||
\"leds\": " + "0" + "\
|
||||
}\
|
||||
]\
|
||||
}";
|
||||
request->send(200, "application/json", config);
|
||||
}
|
||||
|
||||
|
||||
void userSetup()
|
||||
{
|
||||
server.on("/config", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
handleConfig(request);
|
||||
});
|
||||
}
|
||||
|
||||
void userConnected()
|
||||
{
|
||||
// new wifi, who dis?
|
||||
UDP.begin(RGBNET_localUdpPort);
|
||||
isRGBNETUDPEnabled = true;
|
||||
}
|
||||
|
||||
void userLoop()
|
||||
{
|
||||
RGBNET_readValues();
|
||||
if (millis()-lastTime > delayMs) {
|
||||
RGBNET_show();
|
||||
}
|
||||
}
|
16
usermods/BH1750_v2/platformio_override.ini
Normal file
16
usermods/BH1750_v2/platformio_override.ini
Normal file
@@ -0,0 +1,16 @@
|
||||
; Options
|
||||
; -------
|
||||
; USERMOD_BH1750 - define this to have this user mod included wled00\usermods_list.cpp
|
||||
; USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL - the max number of milliseconds between measurements, defaults to 10000ms
|
||||
; USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL - the min number of milliseconds between measurements, defaults to 500ms
|
||||
; USERMOD_BH1750_FIRST_MEASUREMENT_AT - the number of milliseconds after boot to take first measurement, defaults to 10 seconds
|
||||
; USERMOD_BH1750_OFFSET_VALUE - the offset value to report on, defaults to 1
|
||||
;
|
||||
[env:usermod_BH1750_d1_mini]
|
||||
extends = env:d1_mini
|
||||
build_flags =
|
||||
${common.build_flags_esp8266}
|
||||
-D USERMOD_BH1750
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
claws/BH1750 @ ^1.2.0
|
24
usermods/BH1750_v2/readme.md
Normal file
24
usermods/BH1750_v2/readme.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# BH1750 usermod
|
||||
|
||||
This usermod will read from an ambient light sensor like the BH1750 sensor.
|
||||
The luminance is displayed both in the Info section of the web UI as well as published to the `/luminance` MQTT topic if enabled.
|
||||
|
||||
## Installation
|
||||
|
||||
Copy the example `platformio_override.ini` to the root directory. This file should be placed in the same directory as `platformio.ini`.
|
||||
|
||||
### Define Your Options
|
||||
|
||||
* `USERMOD_BH1750` - define this to have this user mod included wled00\usermods_list.cpp
|
||||
* `USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL` - the max number of milliseconds between measurements, defaults to 10000ms
|
||||
* `USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL` - the min number of milliseconds between measurements, defaults to 500ms
|
||||
* `USERMOD_BH1750_FIRST_MEASUREMENT_AT` - the number of milliseconds after boot to take first measurement, defaults to 10 seconds
|
||||
* `USERMOD_BH1750_OFFSET_VALUE` - the offset value to report on, defaults to 1
|
||||
|
||||
All parameters can be configured at runtime using Usermods settings page.
|
||||
|
||||
### PlatformIO requirements
|
||||
|
||||
If you are using `platformio_override.ini`, you should be able to refresh the task list and see your custom task, for example `env:usermod_BH1750_d1_mini`.
|
||||
|
||||
## Change Log
|
177
usermods/BH1750_v2/usermod_bh1750.h
Normal file
177
usermods/BH1750_v2/usermod_bh1750.h
Normal file
@@ -0,0 +1,177 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
#include <Wire.h>
|
||||
#include <BH1750.h>
|
||||
|
||||
// the max frequency to check photoresistor, 10 seconds
|
||||
#ifndef USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL
|
||||
#define USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL 10000
|
||||
#endif
|
||||
|
||||
// the min frequency to check photoresistor, 500 ms
|
||||
#ifndef USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL
|
||||
#define USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL 500
|
||||
#endif
|
||||
|
||||
// how many seconds after boot to take first measurement, 10 seconds
|
||||
#ifndef USERMOD_BH1750_FIRST_MEASUREMENT_AT
|
||||
#define USERMOD_BH1750_FIRST_MEASUREMENT_AT 10000
|
||||
#endif
|
||||
|
||||
// only report if differance grater than offset value
|
||||
#ifndef USERMOD_BH1750_OFFSET_VALUE
|
||||
#define USERMOD_BH1750_OFFSET_VALUE 1
|
||||
#endif
|
||||
|
||||
class Usermod_BH1750 : public Usermod
|
||||
{
|
||||
private:
|
||||
int8_t offset = USERMOD_BH1750_OFFSET_VALUE;
|
||||
|
||||
unsigned long maxReadingInterval = USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL;
|
||||
unsigned long minReadingInterval = USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL;
|
||||
unsigned long lastMeasurement = UINT32_MAX - (USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL - USERMOD_BH1750_FIRST_MEASUREMENT_AT);
|
||||
unsigned long lastSend = UINT32_MAX - (USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL - USERMOD_BH1750_FIRST_MEASUREMENT_AT);
|
||||
// flag to indicate we have finished the first readLightLevel call
|
||||
// allows this library to report to the user how long until the first
|
||||
// measurement
|
||||
bool getLuminanceComplete = false;
|
||||
|
||||
// flag set at startup
|
||||
bool disabled = false;
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
static const char _name[];
|
||||
static const char _enabled[];
|
||||
static const char _maxReadInterval[];
|
||||
static const char _minReadInterval[];
|
||||
static const char _offset[];
|
||||
|
||||
BH1750 lightMeter;
|
||||
float lastLux = -1000;
|
||||
|
||||
bool checkBoundSensor(float newValue, float prevValue, float maxDiff)
|
||||
{
|
||||
return isnan(prevValue) || newValue <= prevValue - maxDiff || newValue >= prevValue + maxDiff || (newValue == 0.0 && prevValue > 0.0);
|
||||
}
|
||||
|
||||
public:
|
||||
void setup()
|
||||
{
|
||||
Wire.begin();
|
||||
lightMeter.begin();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (disabled || strip.isUpdating())
|
||||
return;
|
||||
|
||||
unsigned long now = millis();
|
||||
|
||||
// check to see if we are due for taking a measurement
|
||||
// lastMeasurement will not be updated until the conversion
|
||||
// is complete the the reading is finished
|
||||
if (now - lastMeasurement < minReadingInterval)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool shouldUpdate = now - lastSend > maxReadingInterval;
|
||||
|
||||
float lux = lightMeter.readLightLevel();
|
||||
lastMeasurement = millis();
|
||||
getLuminanceComplete = true;
|
||||
|
||||
if (shouldUpdate || checkBoundSensor(lux, lastLux, offset))
|
||||
{
|
||||
lastLux = lux;
|
||||
lastSend = millis();
|
||||
if (WLED_MQTT_CONNECTED)
|
||||
{
|
||||
char subuf[45];
|
||||
strcpy(subuf, mqttDeviceTopic);
|
||||
strcat_P(subuf, PSTR("/luminance"));
|
||||
mqtt->publish(subuf, 0, true, String(lux).c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_PRINTLN("Missing MQTT connection. Not publishing data");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addToJsonInfo(JsonObject &root)
|
||||
{
|
||||
JsonObject user = root[F("u")];
|
||||
if (user.isNull())
|
||||
user = root.createNestedObject(F("u"));
|
||||
|
||||
JsonArray lux_json = user.createNestedArray(F("Luminance"));
|
||||
|
||||
if (!getLuminanceComplete)
|
||||
{
|
||||
// if we haven't read the sensor yet, let the user know
|
||||
// that we are still waiting for the first measurement
|
||||
lux_json.add((USERMOD_BH1750_FIRST_MEASUREMENT_AT - millis()) / 1000);
|
||||
lux_json.add(F(" sec until read"));
|
||||
return;
|
||||
}
|
||||
|
||||
lux_json.add(lastLux);
|
||||
lux_json.add(F(" lx"));
|
||||
}
|
||||
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_BH1750;
|
||||
}
|
||||
|
||||
/**
|
||||
* addToConfig() (called from set.cpp) stores persistent properties to cfg.json
|
||||
*/
|
||||
void addToConfig(JsonObject &root)
|
||||
{
|
||||
// we add JSON object.
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
|
||||
top[FPSTR(_enabled)] = !disabled;
|
||||
top[FPSTR(_maxReadInterval)] = maxReadingInterval;
|
||||
top[FPSTR(_minReadInterval)] = minReadingInterval;
|
||||
top[FPSTR(_offset)] = offset;
|
||||
|
||||
DEBUG_PRINTLN(F("Photoresistor config saved."));
|
||||
}
|
||||
|
||||
/**
|
||||
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
|
||||
*/
|
||||
bool readFromConfig(JsonObject &root)
|
||||
{
|
||||
// we look for JSON object.
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull())
|
||||
{
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
disabled = !(top[FPSTR(_enabled)] | !disabled);
|
||||
maxReadingInterval = (top[FPSTR(_maxReadInterval)] | maxReadingInterval); // ms
|
||||
minReadingInterval = (top[FPSTR(_minReadInterval)] | minReadingInterval); // ms
|
||||
offset = top[FPSTR(_offset)] | offset;
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
const char Usermod_BH1750::_name[] PROGMEM = "BH1750";
|
||||
const char Usermod_BH1750::_enabled[] PROGMEM = "enabled";
|
||||
const char Usermod_BH1750::_maxReadInterval[] PROGMEM = "max-read-interval-ms";
|
||||
const char Usermod_BH1750::_minReadInterval[] PROGMEM = "min-read-interval-ms";
|
||||
const char Usermod_BH1750::_offset[] PROGMEM = "offset-lx";
|
14
usermods/BH1750_v2/usermods_list.cpp
Normal file
14
usermods/BH1750_v2/usermods_list.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#include "wled.h"
|
||||
/*
|
||||
* Register your v2 usermods here!
|
||||
*/
|
||||
#ifdef USERMOD_BH1750
|
||||
#include "../usermods/BH1750_v2/usermod_BH1750.h"
|
||||
#endif
|
||||
|
||||
void registerUsermods()
|
||||
{
|
||||
#ifdef USERMOD_BH1750
|
||||
usermods.add(new Usermod_BH1750());
|
||||
#endif
|
||||
}
|
40
usermods/BME280_v2/README.md
Normal file
40
usermods/BME280_v2/README.md
Normal file
@@ -0,0 +1,40 @@
|
||||
Hello! I have written a v2 usermod for the BME280/BMP280 sensor based on the [existing v1 usermod](https://github.com/Aircoookie/WLED/blob/master/usermods/Wemos_D1_mini%2BWemos32_mini_shield/usermod_bme280.cpp). It is not just a refactor, there are many changes which I made to fit my use case, and I hope they will fit the use cases of others as well! Most notably, this usermod is *just* for the BME280 and does not control a display like in the v1 usermod designed for the WeMos shield.
|
||||
|
||||
- Requires libraries `BME280@~3.0.0` (by [finitespace](https://github.com/finitespace/BME280)) and `Wire`. Please add these under `lib_deps` in your `platform.ini` (or `platform_override.ini`).
|
||||
- Data is published over MQTT so make sure you've enabled the MQTT sync interface.
|
||||
- This usermod also writes to serial (GPIO1 on ESP8266). Please make sure nothing else listening on the serial TX pin of your board will get confused by log messages!
|
||||
|
||||
To enable, compile with `USERMOD_BME280` defined (i.e. `platformio_override.ini`)
|
||||
```ini
|
||||
build_flags =
|
||||
${common.build_flags_esp8266}
|
||||
-D USERMOD_BME280
|
||||
```
|
||||
or define `USERMOD_BME280` in `my_config.h`
|
||||
```c++
|
||||
#define USERMOD_BME280
|
||||
```
|
||||
|
||||
Changes include:
|
||||
- Adjustable measure intervals
|
||||
- Temperature and pressure have separate intervals due to pressure not frequently changing at any constant altitude
|
||||
- Adjustment of number of decimal places in published sensor values
|
||||
- Separate adjustment for temperature, humidity and pressure values
|
||||
- Values are rounded to the specified number of decimal places
|
||||
- Pressure measured in units of hPa instead of Pa
|
||||
- Calculation of heat index (apparent temperature) and dew point
|
||||
- These, along with humidity measurements, are disabled if the sensor is a BMP280
|
||||
- 16x oversampling of sensor during measurement
|
||||
- Values are only published if they are different from the previous value
|
||||
- Values are published on startup (continually until the MQTT broker acknowledges a successful publication)
|
||||
|
||||
Adjustments are made through preprocessor definitions at the start of the class definition.
|
||||
|
||||
MQTT topics are as follows:
|
||||
Measurement type | MQTT topic
|
||||
--- | ---
|
||||
Temperature | `<deviceTopic>/temperature`
|
||||
Humidity | `<deviceTopic>/humidity`
|
||||
Pressure | `<deviceTopic>/pressure`
|
||||
Heat index | `<deviceTopic>/heat_index`
|
||||
Dew point | `<deviceTopic>/dew_point`
|
216
usermods/BME280_v2/usermod_bme280.h
Normal file
216
usermods/BME280_v2/usermod_bme280.h
Normal file
@@ -0,0 +1,216 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include <BME280I2C.h> // BME280 sensor
|
||||
#include <EnvironmentCalculations.h> // BME280 extended measurements
|
||||
|
||||
class UsermodBME280 : public Usermod
|
||||
{
|
||||
private:
|
||||
// User-defined configuration
|
||||
#define Celsius // Show temperature mesaurement in Celcius. Comment out for Fahrenheit
|
||||
#define TemperatureDecimals 1 // Number of decimal places in published temperaure values
|
||||
#define HumidityDecimals 2 // Number of decimal places in published humidity values
|
||||
#define PressureDecimals 2 // Number of decimal places in published pressure values
|
||||
#define TemperatureInterval 5 // Interval to measure temperature (and humidity, dew point if available) in seconds
|
||||
#define PressureInterval 300 // Interval to measure pressure in seconds
|
||||
#define PublishAlways 0 // Publish values even when they have not changed
|
||||
|
||||
// Sanity checks
|
||||
#if !defined(TemperatureDecimals) || TemperatureDecimals < 0
|
||||
#define TemperatureDecimals 0
|
||||
#endif
|
||||
#if !defined(HumidityDecimals) || HumidityDecimals < 0
|
||||
#define HumidityDecimals 0
|
||||
#endif
|
||||
#if !defined(PressureDecimals) || PressureDecimals < 0
|
||||
#define PressureDecimals 0
|
||||
#endif
|
||||
#if !defined(TemperatureInterval) || TemperatureInterval < 0
|
||||
#define TemperatureInterval 1
|
||||
#endif
|
||||
#if !defined(PressureInterval) || PressureInterval < 0
|
||||
#define PressureInterval TemperatureInterval
|
||||
#endif
|
||||
#if !defined(PublishAlways)
|
||||
#define PublishAlways 0
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32 // ESP32 boards
|
||||
uint8_t SCL_PIN = 22;
|
||||
uint8_t SDA_PIN = 21;
|
||||
#else // ESP8266 boards
|
||||
uint8_t SCL_PIN = 5;
|
||||
uint8_t SDA_PIN = 4;
|
||||
//uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8
|
||||
#endif
|
||||
|
||||
// BME280 sensor settings
|
||||
BME280I2C::Settings settings{
|
||||
BME280::OSR_X16, // Temperature oversampling x16
|
||||
BME280::OSR_X16, // Humidity oversampling x16
|
||||
BME280::OSR_X16, // Pressure oversampling x16
|
||||
// Defaults
|
||||
BME280::Mode_Forced,
|
||||
BME280::StandbyTime_1000ms,
|
||||
BME280::Filter_Off,
|
||||
BME280::SpiEnable_False,
|
||||
BME280I2C::I2CAddr_0x76 // I2C address. I2C specific. Default 0x76
|
||||
};
|
||||
|
||||
BME280I2C bme{settings};
|
||||
|
||||
uint8_t sensorType;
|
||||
|
||||
// Measurement timers
|
||||
long timer;
|
||||
long lastTemperatureMeasure = 0;
|
||||
long lastPressureMeasure = 0;
|
||||
|
||||
// Current sensor values
|
||||
float sensorTemperature;
|
||||
float sensorHumidity;
|
||||
float sensorHeatIndex;
|
||||
float sensorDewPoint;
|
||||
float sensorPressure;
|
||||
// Track previous sensor values
|
||||
float lastTemperature;
|
||||
float lastHumidity;
|
||||
float lastHeatIndex;
|
||||
float lastDewPoint;
|
||||
float lastPressure;
|
||||
|
||||
// Store packet IDs of MQTT publications
|
||||
uint16_t mqttTemperaturePub = 0;
|
||||
uint16_t mqttPressurePub = 0;
|
||||
|
||||
void UpdateBME280Data(int SensorType)
|
||||
{
|
||||
float _temperature, _humidity, _pressure;
|
||||
#ifdef Celsius
|
||||
BME280::TempUnit tempUnit(BME280::TempUnit_Celsius);
|
||||
EnvironmentCalculations::TempUnit envTempUnit(EnvironmentCalculations::TempUnit_Celsius);
|
||||
#else
|
||||
BME280::TempUnit tempUnit(BME280::TempUnit_Fahrenheit);
|
||||
EnvironmentCalculations::TempUnit envTempUnit(EnvironmentCalculations::TempUnit_Fahrenheit);
|
||||
#endif
|
||||
BME280::PresUnit presUnit(BME280::PresUnit_hPa);
|
||||
|
||||
bme.read(_pressure, _temperature, _humidity, tempUnit, presUnit);
|
||||
|
||||
sensorTemperature = _temperature;
|
||||
sensorHumidity = _humidity;
|
||||
sensorPressure = _pressure;
|
||||
if (sensorType == 1)
|
||||
{
|
||||
sensorHeatIndex = EnvironmentCalculations::HeatIndex(_temperature, _humidity, envTempUnit);
|
||||
sensorDewPoint = EnvironmentCalculations::DewPoint(_temperature, _humidity, envTempUnit);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void setup()
|
||||
{
|
||||
Wire.begin(SDA_PIN, SCL_PIN);
|
||||
|
||||
if (!bme.begin())
|
||||
{
|
||||
sensorType = 0;
|
||||
Serial.println("Could not find BME280I2C sensor!");
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (bme.chipModel())
|
||||
{
|
||||
case BME280::ChipModel_BME280:
|
||||
sensorType = 1;
|
||||
Serial.println("Found BME280 sensor! Success.");
|
||||
break;
|
||||
case BME280::ChipModel_BMP280:
|
||||
sensorType = 2;
|
||||
Serial.println("Found BMP280 sensor! No Humidity available.");
|
||||
break;
|
||||
default:
|
||||
sensorType = 0;
|
||||
Serial.println("Found UNKNOWN sensor! Error!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// BME280 sensor MQTT publishing
|
||||
// Check if sensor present and MQTT Connected, otherwise it will crash the MCU
|
||||
if (sensorType != 0 && mqtt != nullptr)
|
||||
{
|
||||
// Timer to fetch new temperature, humidity and pressure data at intervals
|
||||
timer = millis();
|
||||
|
||||
if (timer - lastTemperatureMeasure >= TemperatureInterval * 1000 || mqttTemperaturePub == 0)
|
||||
{
|
||||
lastTemperatureMeasure = timer;
|
||||
|
||||
UpdateBME280Data(sensorType);
|
||||
|
||||
float temperature = roundf(sensorTemperature * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals);
|
||||
float humidity, heatIndex, dewPoint;
|
||||
|
||||
// If temperature has changed since last measure, create string populated with device topic
|
||||
// from the UI and values read from sensor, then publish to broker
|
||||
if (temperature != lastTemperature || PublishAlways)
|
||||
{
|
||||
String topic = String(mqttDeviceTopic) + "/temperature";
|
||||
mqttTemperaturePub = mqtt->publish(topic.c_str(), 0, false, String(temperature, TemperatureDecimals).c_str());
|
||||
}
|
||||
|
||||
lastTemperature = temperature; // Update last sensor temperature for next loop
|
||||
|
||||
if (sensorType == 1) // Only if sensor is a BME280
|
||||
{
|
||||
humidity = roundf(sensorHumidity * pow(10, HumidityDecimals)) / pow(10, HumidityDecimals);
|
||||
heatIndex = roundf(sensorHeatIndex * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals);
|
||||
dewPoint = roundf(sensorDewPoint * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals);
|
||||
|
||||
if (humidity != lastHumidity || PublishAlways)
|
||||
{
|
||||
String topic = String(mqttDeviceTopic) + "/humidity";
|
||||
mqtt->publish(topic.c_str(), 0, false, String(humidity, HumidityDecimals).c_str());
|
||||
}
|
||||
|
||||
if (heatIndex != lastHeatIndex || PublishAlways)
|
||||
{
|
||||
String topic = String(mqttDeviceTopic) + "/heat_index";
|
||||
mqtt->publish(topic.c_str(), 0, false, String(heatIndex, TemperatureDecimals).c_str());
|
||||
}
|
||||
|
||||
if (dewPoint != lastDewPoint || PublishAlways)
|
||||
{
|
||||
String topic = String(mqttDeviceTopic) + "/dew_point";
|
||||
mqtt->publish(topic.c_str(), 0, false, String(dewPoint, TemperatureDecimals).c_str());
|
||||
}
|
||||
|
||||
lastHumidity = humidity;
|
||||
lastHeatIndex = heatIndex;
|
||||
lastDewPoint = dewPoint;
|
||||
}
|
||||
}
|
||||
|
||||
if (timer - lastPressureMeasure >= PressureInterval * 1000 || mqttPressurePub == 0)
|
||||
{
|
||||
lastPressureMeasure = timer;
|
||||
|
||||
float pressure = roundf(sensorPressure * pow(10, PressureDecimals)) / pow(10, PressureDecimals);
|
||||
|
||||
if (pressure != lastPressure || PublishAlways)
|
||||
{
|
||||
String topic = String(mqttDeviceTopic) + "/pressure";
|
||||
mqttPressurePub = mqtt->publish(topic.c_str(), 0, true, String(pressure, PressureDecimals).c_str());
|
||||
}
|
||||
|
||||
lastPressure = pressure;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
8
usermods/Cronixie/readme.md
Normal file
8
usermods/Cronixie/readme.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Cronixie clock usermod
|
||||
|
||||
This usermod supports driving the Cronixie M and L clock kits by Diamex.
|
||||
|
||||
## Installation
|
||||
|
||||
Compile and upload after adding `-D USERMOD_CRONIXIE` to `build_flags` of your PlatformIO environment.
|
||||
Make sure the Auto Brightness Limiter is enabled at 420mA (!) and configure 60 WS281x LEDs.
|
301
usermods/Cronixie/usermod_cronixie.h
Normal file
301
usermods/Cronixie/usermod_cronixie.h
Normal file
@@ -0,0 +1,301 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
class UsermodCronixie : public Usermod {
|
||||
private:
|
||||
unsigned long lastTime = 0;
|
||||
char cronixieDisplay[7] = "HHMMSS";
|
||||
byte _digitOut[6] = {10,10,10,10,10,10};
|
||||
byte dP[6] = {255, 255, 255, 255, 255, 255};
|
||||
|
||||
// set your config variables to their boot default value (this can also be done in readFromConfig() or a constructor if you prefer)
|
||||
bool backlight = true;
|
||||
|
||||
public:
|
||||
void initCronixie()
|
||||
{
|
||||
if (dP[0] == 255) // if dP[0] is 255, cronixie is not yet init'ed
|
||||
{
|
||||
setCronixie();
|
||||
strip.getSegment(0).grouping = 10; // 10 LEDs per digit
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (!toki.isTick()) return;
|
||||
initCronixie();
|
||||
_overlayCronixie();
|
||||
strip.trigger();
|
||||
}
|
||||
|
||||
byte getSameCodeLength(char code, int index, char const cronixieDisplay[])
|
||||
{
|
||||
byte counter = 0;
|
||||
|
||||
for (int i = index+1; i < 6; i++)
|
||||
{
|
||||
if (cronixieDisplay[i] == code)
|
||||
{
|
||||
counter++;
|
||||
} else {
|
||||
return counter;
|
||||
}
|
||||
}
|
||||
return counter;
|
||||
}
|
||||
|
||||
void setCronixie()
|
||||
{
|
||||
/*
|
||||
* digit purpose index
|
||||
* 0-9 | 0-9 (incl. random)
|
||||
* 10 | blank
|
||||
* 11 | blank, bg off
|
||||
* 12 | test upw.
|
||||
* 13 | test dnw.
|
||||
* 14 | binary AM/PM
|
||||
* 15 | BB upper +50 for no trailing 0
|
||||
* 16 | BBB
|
||||
* 17 | BBBB
|
||||
* 18 | BBBBB
|
||||
* 19 | BBBBBB
|
||||
* 20 | H
|
||||
* 21 | HH
|
||||
* 22 | HHH
|
||||
* 23 | HHHH
|
||||
* 24 | M
|
||||
* 25 | MM
|
||||
* 26 | MMM
|
||||
* 27 | MMMM
|
||||
* 28 | MMMMM
|
||||
* 29 | MMMMMM
|
||||
* 30 | S
|
||||
* 31 | SS
|
||||
* 32 | SSS
|
||||
* 33 | SSSS
|
||||
* 34 | SSSSS
|
||||
* 35 | SSSSSS
|
||||
* 36 | Y
|
||||
* 37 | YY
|
||||
* 38 | YYYY
|
||||
* 39 | I
|
||||
* 40 | II
|
||||
* 41 | W
|
||||
* 42 | WW
|
||||
* 43 | D
|
||||
* 44 | DD
|
||||
* 45 | DDD
|
||||
* 46 | V
|
||||
* 47 | VV
|
||||
* 48 | VVV
|
||||
* 49 | VVVV
|
||||
* 50 | VVVVV
|
||||
* 51 | VVVVVV
|
||||
* 52 | v
|
||||
* 53 | vv
|
||||
* 54 | vvv
|
||||
* 55 | vvvv
|
||||
* 56 | vvvvv
|
||||
* 57 | vvvvvv
|
||||
*/
|
||||
|
||||
//H HourLower | HH - Hour 24. | AH - Hour 12. | HHH Hour of Month | HHHH Hour of Year
|
||||
//M MinuteUpper | MM Minute of Hour | MMM Minute of 12h | MMMM Minute of Day | MMMMM Minute of Month | MMMMMM Minute of Year
|
||||
//S SecondUpper | SS Second of Minute | SSS Second of 10 Minute | SSSS Second of Hour | SSSSS Second of Day | SSSSSS Second of Week
|
||||
//B AM/PM | BB 0-6/6-12/12-18/18-24 | BBB 0-3... | BBBB 0-1.5... | BBBBB 0-1 | BBBBBB 0-0.5
|
||||
|
||||
//Y YearLower | YY - Year LU | YYYY - Std.
|
||||
//I MonthLower | II - Month of Year
|
||||
//W Week of Month | WW Week of Year
|
||||
//D Day of Week | DD Day Of Month | DDD Day Of Year
|
||||
|
||||
DEBUG_PRINT("cset ");
|
||||
DEBUG_PRINTLN(cronixieDisplay);
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
dP[i] = 10;
|
||||
switch (cronixieDisplay[i])
|
||||
{
|
||||
case '_': dP[i] = 10; break;
|
||||
case '-': dP[i] = 11; break;
|
||||
case 'r': dP[i] = random(1,7); break; //random btw. 1-6
|
||||
case 'R': dP[i] = random(0,10); break; //random btw. 0-9
|
||||
//case 't': break; //Test upw.
|
||||
//case 'T': break; //Test dnw.
|
||||
case 'b': dP[i] = 14 + getSameCodeLength('b',i,cronixieDisplay); i = i+dP[i]-14; break;
|
||||
case 'B': dP[i] = 14 + getSameCodeLength('B',i,cronixieDisplay); i = i+dP[i]-14; break;
|
||||
case 'h': dP[i] = 70 + getSameCodeLength('h',i,cronixieDisplay); i = i+dP[i]-70; break;
|
||||
case 'H': dP[i] = 20 + getSameCodeLength('H',i,cronixieDisplay); i = i+dP[i]-20; break;
|
||||
case 'A': dP[i] = 108; i++; break;
|
||||
case 'a': dP[i] = 58; i++; break;
|
||||
case 'm': dP[i] = 74 + getSameCodeLength('m',i,cronixieDisplay); i = i+dP[i]-74; break;
|
||||
case 'M': dP[i] = 24 + getSameCodeLength('M',i,cronixieDisplay); i = i+dP[i]-24; break;
|
||||
case 's': dP[i] = 80 + getSameCodeLength('s',i,cronixieDisplay); i = i+dP[i]-80; break; //refresh more often bc. of secs
|
||||
case 'S': dP[i] = 30 + getSameCodeLength('S',i,cronixieDisplay); i = i+dP[i]-30; break;
|
||||
case 'Y': dP[i] = 36 + getSameCodeLength('Y',i,cronixieDisplay); i = i+dP[i]-36; break;
|
||||
case 'y': dP[i] = 86 + getSameCodeLength('y',i,cronixieDisplay); i = i+dP[i]-86; break;
|
||||
case 'I': dP[i] = 39 + getSameCodeLength('I',i,cronixieDisplay); i = i+dP[i]-39; break; //Month. Don't ask me why month and minute both start with M.
|
||||
case 'i': dP[i] = 89 + getSameCodeLength('i',i,cronixieDisplay); i = i+dP[i]-89; break;
|
||||
//case 'W': break;
|
||||
//case 'w': break;
|
||||
case 'D': dP[i] = 43 + getSameCodeLength('D',i,cronixieDisplay); i = i+dP[i]-43; break;
|
||||
case 'd': dP[i] = 93 + getSameCodeLength('d',i,cronixieDisplay); i = i+dP[i]-93; break;
|
||||
case '0': dP[i] = 0; break;
|
||||
case '1': dP[i] = 1; break;
|
||||
case '2': dP[i] = 2; break;
|
||||
case '3': dP[i] = 3; break;
|
||||
case '4': dP[i] = 4; break;
|
||||
case '5': dP[i] = 5; break;
|
||||
case '6': dP[i] = 6; break;
|
||||
case '7': dP[i] = 7; break;
|
||||
case '8': dP[i] = 8; break;
|
||||
case '9': dP[i] = 9; break;
|
||||
//case 'V': break; //user var0
|
||||
//case 'v': break; //user var1
|
||||
}
|
||||
}
|
||||
DEBUG_PRINT("result ");
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
DEBUG_PRINT((int)dP[i]);
|
||||
DEBUG_PRINT(" ");
|
||||
}
|
||||
DEBUG_PRINTLN((int)dP[5]);
|
||||
|
||||
_overlayCronixie(); // refresh
|
||||
}
|
||||
|
||||
void _overlayCronixie()
|
||||
{
|
||||
byte h = hour(localTime);
|
||||
byte h0 = h;
|
||||
byte m = minute(localTime);
|
||||
byte s = second(localTime);
|
||||
byte d = day(localTime);
|
||||
byte mi = month(localTime);
|
||||
int y = year(localTime);
|
||||
//this has to be changed in time for 22nd century
|
||||
y -= 2000; if (y<0) y += 30; //makes countdown work
|
||||
|
||||
if (useAMPM && !countdownMode)
|
||||
{
|
||||
if (h>12) h-=12;
|
||||
else if (h==0) h+=12;
|
||||
}
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
if (dP[i] < 12) _digitOut[i] = dP[i];
|
||||
else {
|
||||
if (dP[i] < 65)
|
||||
{
|
||||
switch(dP[i])
|
||||
{
|
||||
case 21: _digitOut[i] = h/10; _digitOut[i+1] = h- _digitOut[i]*10; i++; break; //HH
|
||||
case 25: _digitOut[i] = m/10; _digitOut[i+1] = m- _digitOut[i]*10; i++; break; //MM
|
||||
case 31: _digitOut[i] = s/10; _digitOut[i+1] = s- _digitOut[i]*10; i++; break; //SS
|
||||
|
||||
case 20: _digitOut[i] = h- (h/10)*10; break; //H
|
||||
case 24: _digitOut[i] = m/10; break; //M
|
||||
case 30: _digitOut[i] = s/10; break; //S
|
||||
|
||||
case 43: _digitOut[i] = weekday(localTime); _digitOut[i]--; if (_digitOut[i]<1) _digitOut[i]= 7; break; //D
|
||||
case 44: _digitOut[i] = d/10; _digitOut[i+1] = d- _digitOut[i]*10; i++; break; //DD
|
||||
case 40: _digitOut[i] = mi/10; _digitOut[i+1] = mi- _digitOut[i]*10; i++; break; //II
|
||||
case 37: _digitOut[i] = y/10; _digitOut[i+1] = y- _digitOut[i]*10; i++; break; //YY
|
||||
case 39: _digitOut[i] = 2; _digitOut[i+1] = 0; _digitOut[i+2] = y/10; _digitOut[i+3] = y- _digitOut[i+2]*10; i+=3; break; //YYYY
|
||||
|
||||
//case 16: _digitOut[i+2] = ((h0/3)&1)?1:0; i++; //BBB (BBBB NI)
|
||||
//case 15: _digitOut[i+1] = (h0>17 || (h0>5 && h0<12))?1:0; i++; //BB
|
||||
case 14: _digitOut[i] = (h0>11)?1:0; break; //B
|
||||
}
|
||||
} else
|
||||
{
|
||||
switch(dP[i])
|
||||
{
|
||||
case 71: _digitOut[i] = h/10; _digitOut[i+1] = h- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //hh
|
||||
case 75: _digitOut[i] = m/10; _digitOut[i+1] = m- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //mm
|
||||
case 81: _digitOut[i] = s/10; _digitOut[i+1] = s- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //ss
|
||||
//case 66: _digitOut[i+2] = ((h0/3)&1)?1:10; i++; //bbb (bbbb NI)
|
||||
//case 65: _digitOut[i+1] = (h0>17 || (h0>5 && h0<12))?1:10; i++; //bb
|
||||
case 64: _digitOut[i] = (h0>11)?1:10; break; //b
|
||||
|
||||
case 93: _digitOut[i] = weekday(localTime); _digitOut[i]--; if (_digitOut[i]<1) _digitOut[i]= 7; break; //d
|
||||
case 94: _digitOut[i] = d/10; _digitOut[i+1] = d- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //dd
|
||||
case 90: _digitOut[i] = mi/10; _digitOut[i+1] = mi- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //ii
|
||||
case 87: _digitOut[i] = y/10; _digitOut[i+1] = y- _digitOut[i]*10; i++; break; //yy
|
||||
case 89: _digitOut[i] = 2; _digitOut[i+1] = 0; _digitOut[i+2] = y/10; _digitOut[i+3] = y- _digitOut[i+2]*10; i+=3; break; //yyyy
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleOverlayDraw()
|
||||
{
|
||||
byte offsets[] = {5, 0, 6, 1, 7, 2, 8, 3, 9, 4};
|
||||
|
||||
for (uint16_t i = 0; i < 6; i++)
|
||||
{
|
||||
byte o = 10*i;
|
||||
byte excl = 10;
|
||||
if(_digitOut[i] < 10) excl = offsets[_digitOut[i]];
|
||||
excl += o;
|
||||
|
||||
if (backlight && _digitOut[i] <11)
|
||||
{
|
||||
uint32_t col = strip.gamma32(strip.getSegment(0).colors[1]);
|
||||
for (uint16_t j=o; j< o+10; j++) {
|
||||
if (j != excl) strip.setPixelColor(j, col);
|
||||
}
|
||||
} else
|
||||
{
|
||||
for (uint16_t j=o; j< o+10; j++) {
|
||||
if (j != excl) strip.setPixelColor(j, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addToJsonState(JsonObject& root)
|
||||
{
|
||||
root["nx"] = cronixieDisplay;
|
||||
}
|
||||
|
||||
void readFromJsonState(JsonObject& root)
|
||||
{
|
||||
if (root["nx"].is<const char*>()) {
|
||||
strncpy(cronixieDisplay, root["nx"], 6);
|
||||
}
|
||||
}
|
||||
|
||||
void addToConfig(JsonObject& root)
|
||||
{
|
||||
JsonObject top = root.createNestedObject(F("Cronixie"));
|
||||
top["backlight"] = backlight;
|
||||
}
|
||||
|
||||
bool readFromConfig(JsonObject& root)
|
||||
{
|
||||
// default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor
|
||||
// setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed)
|
||||
|
||||
JsonObject top = root[F("Cronixie")];
|
||||
|
||||
bool configComplete = !top.isNull();
|
||||
|
||||
configComplete &= getJsonValue(top["backlight"], backlight);
|
||||
|
||||
return configComplete;
|
||||
}
|
||||
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_CRONIXIE;
|
||||
}
|
||||
};
|
22
usermods/DHT/platformio_override.ini
Normal file
22
usermods/DHT/platformio_override.ini
Normal file
@@ -0,0 +1,22 @@
|
||||
; Options
|
||||
; -------
|
||||
; USERMOD_DHT - define this to have this user mod included wled00\usermods_list.cpp
|
||||
; USERMOD_DHT_DHTTYPE - DHT model: 11, 21, 22 for DHT11, DHT21, or DHT22, defaults to 22/DHT22
|
||||
; USERMOD_DHT_PIN - pin to which DTH is connected, defaults to Q2 pin on QuinLed Dig-Uno's board
|
||||
; USERMOD_DHT_CELSIUS - define this to report temperatures in degrees celsious, otherwise fahrenheit will be reported
|
||||
; USERMOD_DHT_MEASUREMENT_INTERVAL - the number of milliseconds between measurements, defaults to 60 seconds
|
||||
; USERMOD_DHT_FIRST_MEASUREMENT_AT - the number of milliseconds after boot to take first measurement, defaults to 90 seconds
|
||||
; USERMOD_DHT_STATS - For debug, report delay stats
|
||||
|
||||
[env:d1_mini_usermod_dht_C]
|
||||
extends = env:d1_mini
|
||||
build_flags = ${env:d1_mini.build_flags} -D USERMOD_DHT -D USERMOD_DHT_CELSIUS
|
||||
lib_deps = ${env.lib_deps}
|
||||
https://github.com/alwynallan/DHT_nonblocking
|
||||
|
||||
[env:custom32_LEDPIN_16_usermod_dht_C]
|
||||
extends = env:custom32_LEDPIN_16
|
||||
build_flags = ${env:custom32_LEDPIN_16.build_flags} -D USERMOD_DHT -D USERMOD_DHT_CELSIUS -D USERMOD_DHT_STATS
|
||||
lib_deps = ${env.lib_deps}
|
||||
https://github.com/alwynallan/DHT_nonblocking
|
||||
|
41
usermods/DHT/readme.md
Normal file
41
usermods/DHT/readme.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# DHT Temperature/Humidity sensor usermod
|
||||
|
||||
This usermod will read from an attached DHT22 or DHT11 humidity and temperature sensor.
|
||||
The sensor readings are displayed in the Info section of the web UI.
|
||||
|
||||
If sensor is not detected after a while (10 update intervals), this usermod will be disabled.
|
||||
|
||||
## Installation
|
||||
|
||||
Copy the example `platformio_override.ini` to the root directory. This file should be placed in the same directory as `platformio.ini`.
|
||||
|
||||
### Define Your Options
|
||||
|
||||
* `USERMOD_DHT` - define this to have this user mod included wled00\usermods_list.cpp
|
||||
* `USERMOD_DHT_DHTTYPE` - DHT model: 11, 21, 22 for DHT11, DHT21, or DHT22, defaults to 22/DHT22
|
||||
* `USERMOD_DHT_PIN` - pin to which DTH is connected, defaults to Q2 pin on QuinLed Dig-Uno's board
|
||||
* `USERMOD_DHT_CELSIUS` - define this to report temperatures in degrees celsious, otherwise fahrenheit will be reported
|
||||
* `USERMOD_DHT_MEASUREMENT_INTERVAL` - the number of milliseconds between measurements, defaults to 60 seconds
|
||||
* `USERMOD_DHT_FIRST_MEASUREMENT_AT` - the number of milliseconds after boot to take first measurement, defaults to 90 seconds
|
||||
* `USERMOD_DHT_STATS` - For debug, report delay stats
|
||||
|
||||
## Project link
|
||||
|
||||
* [QuinLED-Dig-Uno](https://quinled.info/2018/09/15/quinled-dig-uno/) - Project link
|
||||
|
||||
### PlatformIO requirements
|
||||
|
||||
If you are using `platformio_override.ini`, you should be able to refresh the task list and see your custom task, for example `env:d1_mini_usermod_dht_C`. If not, you can add the libraries and dependencies into `platformio.ini` as you see fit.
|
||||
|
||||
|
||||
## Change Log
|
||||
|
||||
2020-02-04
|
||||
* Change default QuinLed pin to Q2
|
||||
* Instead of trying to keep updates at constant cadence, space readings out by measurement interval; hope this helps to avoid occasional bursts of readings with errors
|
||||
* Add some more (optional) stats
|
||||
2020-02-03
|
||||
* Due to poor readouts on ESP32 with previous DHT library, rewrote to use https://github.com/alwynallan/DHT_nonblocking
|
||||
* The new library serializes/delays up to 5ms for the sensor readout
|
||||
2020-02-02
|
||||
* Created
|
216
usermods/DHT/usermod_dht.h
Normal file
216
usermods/DHT/usermod_dht.h
Normal file
@@ -0,0 +1,216 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
#include <dht_nonblocking.h>
|
||||
|
||||
// USERMOD_DHT_DHTTYPE:
|
||||
// 11 // DHT 11
|
||||
// 21 // DHT 21
|
||||
// 22 // DHT 22 (AM2302), AM2321 *** default
|
||||
#ifndef USERMOD_DHT_DHTTYPE
|
||||
#define USERMOD_DHT_DHTTYPE 22
|
||||
#endif
|
||||
|
||||
#if USERMOD_DHT_DHTTYPE == 11
|
||||
#define DHTTYPE DHT_TYPE_11
|
||||
#elif USERMOD_DHT_DHTTYPE == 21
|
||||
#define DHTTYPE DHT_TYPE_21
|
||||
#elif USERMOD_DHT_DHTTYPE == 22
|
||||
#define DHTTYPE DHT_TYPE_22
|
||||
#endif
|
||||
|
||||
// Connect pin 1 (on the left) of the sensor to +5V
|
||||
// NOTE: If using a board with 3.3V logic like an Arduino Due connect pin 1
|
||||
// to 3.3V instead of 5V!
|
||||
// Connect pin 2 of the sensor to whatever your DHTPIN is
|
||||
// NOTE: Pin defaults below are for QuinLed Dig-Uno's Q2 on the board
|
||||
// Connect pin 4 (on the right) of the sensor to GROUND
|
||||
// NOTE: If using a bare sensor (AM*), Connect a 10K resistor from pin 2
|
||||
// (data) to pin 1 (power) of the sensor. DHT* boards have the pullup already
|
||||
|
||||
#ifdef USERMOD_DHT_PIN
|
||||
#define DHTPIN USERMOD_DHT_PIN
|
||||
#else
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#define DHTPIN 21
|
||||
#else //ESP8266 boards
|
||||
#define DHTPIN 4
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// the frequency to check sensor, 1 minute
|
||||
#ifndef USERMOD_DHT_MEASUREMENT_INTERVAL
|
||||
#define USERMOD_DHT_MEASUREMENT_INTERVAL 60000
|
||||
#endif
|
||||
|
||||
// how many seconds after boot to take first measurement, 90 seconds
|
||||
// 90 gives enough time to OTA update firmware if this crashses
|
||||
#ifndef USERMOD_DHT_FIRST_MEASUREMENT_AT
|
||||
#define USERMOD_DHT_FIRST_MEASUREMENT_AT 90000
|
||||
#endif
|
||||
|
||||
// from COOLDOWN_TIME in dht_nonblocking.cpp
|
||||
#define DHT_TIMEOUT_TIME 10000
|
||||
|
||||
DHT_nonblocking dht_sensor(DHTPIN, DHTTYPE);
|
||||
|
||||
class UsermodDHT : public Usermod {
|
||||
private:
|
||||
unsigned long nextReadTime = 0;
|
||||
unsigned long lastReadTime = 0;
|
||||
float humidity, temperature = 0;
|
||||
bool initializing = true;
|
||||
bool disabled = false;
|
||||
#ifdef USERMOD_DHT_STATS
|
||||
unsigned long nextResetStatsTime = 0;
|
||||
uint16_t updates = 0;
|
||||
uint16_t clean_updates = 0;
|
||||
uint16_t errors = 0;
|
||||
unsigned long maxDelay = 0;
|
||||
unsigned long currentIteration = 0;
|
||||
unsigned long maxIteration = 0;
|
||||
#endif
|
||||
|
||||
public:
|
||||
void setup() {
|
||||
nextReadTime = millis() + USERMOD_DHT_FIRST_MEASUREMENT_AT;
|
||||
lastReadTime = millis();
|
||||
#ifdef USERMOD_DHT_STATS
|
||||
nextResetStatsTime = millis() + 60*60*1000;
|
||||
#endif
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
if (millis() < nextReadTime) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef USERMOD_DHT_STATS
|
||||
if (millis() >= nextResetStatsTime) {
|
||||
nextResetStatsTime += 60*60*1000;
|
||||
errors = 0;
|
||||
updates = 0;
|
||||
clean_updates = 0;
|
||||
}
|
||||
unsigned long dcalc = millis();
|
||||
if (currentIteration == 0) {
|
||||
currentIteration = millis();
|
||||
}
|
||||
#endif
|
||||
|
||||
float tempC;
|
||||
if (dht_sensor.measure(&tempC, &humidity)) {
|
||||
#ifdef USERMOD_DHT_CELSIUS
|
||||
temperature = tempC;
|
||||
#else
|
||||
temperature = tempC * 9 / 5 + 32;
|
||||
#endif
|
||||
|
||||
nextReadTime = millis() + USERMOD_DHT_MEASUREMENT_INTERVAL;
|
||||
lastReadTime = millis();
|
||||
initializing = false;
|
||||
|
||||
#ifdef USERMOD_DHT_STATS
|
||||
unsigned long icalc = millis() - currentIteration;
|
||||
if (icalc > maxIteration) {
|
||||
maxIteration = icalc;
|
||||
}
|
||||
if (icalc > DHT_TIMEOUT_TIME) {
|
||||
errors += icalc/DHT_TIMEOUT_TIME;
|
||||
} else {
|
||||
clean_updates += 1;
|
||||
}
|
||||
updates += 1;
|
||||
currentIteration = 0;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USERMOD_DHT_STATS
|
||||
dcalc = millis() - dcalc;
|
||||
if (dcalc > maxDelay) {
|
||||
maxDelay = dcalc;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (((millis() - lastReadTime) > 10*USERMOD_DHT_MEASUREMENT_INTERVAL)) {
|
||||
disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
void addToJsonInfo(JsonObject& root) {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
JsonObject user = root["u"];
|
||||
if (user.isNull()) user = root.createNestedObject("u");
|
||||
|
||||
JsonArray temp = user.createNestedArray("Temperature");
|
||||
JsonArray hum = user.createNestedArray("Humidity");
|
||||
|
||||
#ifdef USERMOD_DHT_STATS
|
||||
JsonArray next = user.createNestedArray("next");
|
||||
if (nextReadTime >= millis()) {
|
||||
next.add((nextReadTime - millis()) / 1000);
|
||||
next.add(" sec until read");
|
||||
} else {
|
||||
next.add((millis() - nextReadTime) / 1000);
|
||||
next.add(" sec active reading");
|
||||
}
|
||||
|
||||
JsonArray last = user.createNestedArray("last");
|
||||
last.add((millis() - lastReadTime) / 60000);
|
||||
last.add(" min since read");
|
||||
|
||||
JsonArray err = user.createNestedArray("errors");
|
||||
err.add(errors);
|
||||
err.add(" Errors");
|
||||
|
||||
JsonArray upd = user.createNestedArray("updates");
|
||||
upd.add(updates);
|
||||
upd.add(" Updates");
|
||||
|
||||
JsonArray cupd = user.createNestedArray("cleanUpdates");
|
||||
cupd.add(clean_updates);
|
||||
cupd.add(" Updates");
|
||||
|
||||
JsonArray iter = user.createNestedArray("maxIter");
|
||||
iter.add(maxIteration);
|
||||
iter.add(" ms");
|
||||
|
||||
JsonArray delay = user.createNestedArray("maxDelay");
|
||||
delay.add(maxDelay);
|
||||
delay.add(" ms");
|
||||
#endif
|
||||
|
||||
if (initializing) {
|
||||
// if we haven't read the sensor yet, let the user know
|
||||
// that we are still waiting for the first measurement
|
||||
temp.add((nextReadTime - millis()) / 1000);
|
||||
temp.add(" sec until read");
|
||||
hum.add((nextReadTime - millis()) / 1000);
|
||||
hum.add(" sec until read");
|
||||
return;
|
||||
}
|
||||
|
||||
hum.add(humidity);
|
||||
hum.add("%");
|
||||
|
||||
temp.add(temperature);
|
||||
#ifdef USERMOD_DHT_CELSIUS
|
||||
temp.add("°C");
|
||||
#else
|
||||
temp.add("°F");
|
||||
#endif
|
||||
}
|
||||
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_DHT;
|
||||
}
|
||||
|
||||
};
|
10
usermods/EXAMPLE_v2/readme.md
Normal file
10
usermods/EXAMPLE_v2/readme.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Usermods API v2 example usermod
|
||||
|
||||
In this usermod file you can find the documentation on how to take advantage of the new version 2 usermods!
|
||||
|
||||
## Installation
|
||||
|
||||
Copy `usermod_v2_example.h` to the wled00 directory.
|
||||
Uncomment the corresponding lines in `usermods_list.cpp` and compile!
|
||||
_(You shouldn't need to actually install this, it does nothing useful)_
|
||||
|
222
usermods/EXAMPLE_v2/usermod_v2_example.h
Normal file
222
usermods/EXAMPLE_v2/usermod_v2_example.h
Normal file
@@ -0,0 +1,222 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
/*
|
||||
* Usermods allow you to add own functionality to WLED more easily
|
||||
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
|
||||
*
|
||||
* This is an example for a v2 usermod.
|
||||
* v2 usermods are class inheritance based and can (but don't have to) implement more functions, each of them is shown in this example.
|
||||
* Multiple v2 usermods can be added to one compilation easily.
|
||||
*
|
||||
* Creating a usermod:
|
||||
* This file serves as an example. If you want to create a usermod, it is recommended to use usermod_v2_empty.h from the usermods folder as a template.
|
||||
* Please remember to rename the class and file to a descriptive name.
|
||||
* You may also use multiple .h and .cpp files.
|
||||
*
|
||||
* Using a usermod:
|
||||
* 1. Copy the usermod into the sketch folder (same folder as wled00.ino)
|
||||
* 2. Register the usermod by adding #include "usermod_filename.h" in the top and registerUsermod(new MyUsermodClass()) in the bottom of usermods_list.cpp
|
||||
*/
|
||||
|
||||
//class name. Use something descriptive and leave the ": public Usermod" part :)
|
||||
class MyExampleUsermod : public Usermod {
|
||||
private:
|
||||
//Private class members. You can declare variables and functions only accessible to your usermod here
|
||||
unsigned long lastTime = 0;
|
||||
|
||||
// set your config variables to their boot default value (this can also be done in readFromConfig() or a constructor if you prefer)
|
||||
bool testBool = false;
|
||||
unsigned long testULong = 42424242;
|
||||
float testFloat = 42.42;
|
||||
String testString = "Forty-Two";
|
||||
|
||||
// These config variables have defaults set inside readFromConfig()
|
||||
int testInt;
|
||||
long testLong;
|
||||
int8_t testPins[2];
|
||||
|
||||
public:
|
||||
//Functions called by WLED
|
||||
|
||||
/*
|
||||
* setup() is called once at boot. WiFi is not yet connected at this point.
|
||||
* You can use it to initialize variables, sensors or similar.
|
||||
*/
|
||||
void setup() {
|
||||
//Serial.println("Hello from my usermod!");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* connected() is called every time the WiFi is (re)connected
|
||||
* Use it to initialize network interfaces
|
||||
*/
|
||||
void connected() {
|
||||
//Serial.println("Connected to WiFi!");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* loop() is called continuously. Here you can check for events, read sensors, etc.
|
||||
*
|
||||
* Tips:
|
||||
* 1. You can use "if (WLED_CONNECTED)" to check for a successful network connection.
|
||||
* Additionally, "if (WLED_MQTT_CONNECTED)" is available to check for a connection to an MQTT broker.
|
||||
*
|
||||
* 2. Try to avoid using the delay() function. NEVER use delays longer than 10 milliseconds.
|
||||
* Instead, use a timer check as shown here.
|
||||
*/
|
||||
void loop() {
|
||||
if (millis() - lastTime > 1000) {
|
||||
//Serial.println("I'm alive!");
|
||||
lastTime = millis();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
|
||||
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
|
||||
* Below it is shown how this could be used for e.g. a light sensor
|
||||
*/
|
||||
/*
|
||||
void addToJsonInfo(JsonObject& root)
|
||||
{
|
||||
int reading = 20;
|
||||
//this code adds "u":{"Light":[20," lux"]} to the info object
|
||||
JsonObject user = root["u"];
|
||||
if (user.isNull()) user = root.createNestedObject("u");
|
||||
|
||||
JsonArray lightArr = user.createNestedArray("Light"); //name
|
||||
lightArr.add(reading); //value
|
||||
lightArr.add(" lux"); //unit
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
|
||||
* Values in the state object may be modified by connected clients
|
||||
*/
|
||||
void addToJsonState(JsonObject& root)
|
||||
{
|
||||
//root["user0"] = userVar0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
|
||||
* Values in the state object may be modified by connected clients
|
||||
*/
|
||||
void readFromJsonState(JsonObject& root)
|
||||
{
|
||||
userVar0 = root["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value
|
||||
//if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!"));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
|
||||
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
|
||||
* If you want to force saving the current state, use serializeConfig() in your loop().
|
||||
*
|
||||
* CAUTION: serializeConfig() will initiate a filesystem write operation.
|
||||
* It might cause the LEDs to stutter and will cause flash wear if called too often.
|
||||
* Use it sparingly and always in the loop, never in network callbacks!
|
||||
*
|
||||
* addToConfig() will make your settings editable through the Usermod Settings page automatically.
|
||||
*
|
||||
* Usermod Settings Overview:
|
||||
* - Numeric values are treated as floats in the browser.
|
||||
* - If the numeric value entered into the browser contains a decimal point, it will be parsed as a C float
|
||||
* before being returned to the Usermod. The float data type has only 6-7 decimal digits of precision, and
|
||||
* doubles are not supported, numbers will be rounded to the nearest float value when being parsed.
|
||||
* The range accepted by the input field is +/- 1.175494351e-38 to +/- 3.402823466e+38.
|
||||
* - If the numeric value entered into the browser doesn't contain a decimal point, it will be parsed as a
|
||||
* C int32_t (range: -2147483648 to 2147483647) before being returned to the usermod.
|
||||
* Overflows or underflows are truncated to the max/min value for an int32_t, and again truncated to the type
|
||||
* used in the Usermod when reading the value from ArduinoJson.
|
||||
* - Pin values can be treated differently from an integer value by using the key name "pin"
|
||||
* - "pin" can contain a single or array of integer values
|
||||
* - On the Usermod Settings page there is simple checking for pin conflicts and warnings for special pins
|
||||
* - Red color indicates a conflict. Yellow color indicates a pin with a warning (e.g. an input-only pin)
|
||||
* - Tip: use int8_t to store the pin value in the Usermod, so a -1 value (pin not set) can be used
|
||||
*
|
||||
* See usermod_v2_auto_save.h for an example that saves Flash space by reusing ArduinoJson key name strings
|
||||
*
|
||||
* If you need a dedicated settings page with custom layout for your Usermod, that takes a lot more work.
|
||||
* You will have to add the setting to the HTML, xml.cpp and set.cpp manually.
|
||||
* See the WLED Soundreactive fork (code and wiki) for reference. https://github.com/atuline/WLED
|
||||
*
|
||||
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
|
||||
*/
|
||||
void addToConfig(JsonObject& root)
|
||||
{
|
||||
JsonObject top = root.createNestedObject("exampleUsermod");
|
||||
top["great"] = userVar0; //save these vars persistently whenever settings are saved
|
||||
top["testBool"] = testBool;
|
||||
top["testInt"] = testInt;
|
||||
top["testLong"] = testLong;
|
||||
top["testULong"] = testULong;
|
||||
top["testFloat"] = testFloat;
|
||||
top["testString"] = testString;
|
||||
JsonArray pinArray = top.createNestedArray("pin");
|
||||
pinArray.add(testPins[0]);
|
||||
pinArray.add(testPins[1]);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* readFromConfig() can be used to read back the custom settings you added with addToConfig().
|
||||
* This is called by WLED when settings are loaded (currently this only happens immediately after boot, or after saving on the Usermod Settings page)
|
||||
*
|
||||
* readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes),
|
||||
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
|
||||
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
|
||||
*
|
||||
* Return true in case the config values returned from Usermod Settings were complete, or false if you'd like WLED to save your defaults to disk (so any missing values are editable in Usermod Settings)
|
||||
*
|
||||
* getJsonValue() returns false if the value is missing, or copies the value into the variable provided and returns true if the value is present
|
||||
* The configComplete variable is true only if the "exampleUsermod" object and all values are present. If any values are missing, WLED will know to call addToConfig() to save them
|
||||
*
|
||||
* This function is guaranteed to be called on boot, but could also be called every time settings are updated
|
||||
*/
|
||||
bool readFromConfig(JsonObject& root)
|
||||
{
|
||||
// default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor
|
||||
// setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed)
|
||||
|
||||
JsonObject top = root["exampleUsermod"];
|
||||
|
||||
bool configComplete = !top.isNull();
|
||||
|
||||
configComplete &= getJsonValue(top["great"], userVar0);
|
||||
configComplete &= getJsonValue(top["testBool"], testBool);
|
||||
configComplete &= getJsonValue(top["testULong"], testULong);
|
||||
configComplete &= getJsonValue(top["testFloat"], testFloat);
|
||||
configComplete &= getJsonValue(top["testString"], testString);
|
||||
|
||||
// A 3-argument getJsonValue() assigns the 3rd argument as a default value if the Json value is missing
|
||||
configComplete &= getJsonValue(top["testInt"], testInt, 42);
|
||||
configComplete &= getJsonValue(top["testLong"], testLong, -42424242);
|
||||
configComplete &= getJsonValue(top["pin"][0], testPins[0], -1);
|
||||
configComplete &= getJsonValue(top["pin"][1], testPins[1], -1);
|
||||
|
||||
return configComplete;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
|
||||
* This could be used in the future for the system to determine whether your usermod is installed.
|
||||
*/
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_EXAMPLE;
|
||||
}
|
||||
|
||||
//More methods can be added in the future, this example will then be extended.
|
||||
//Your usermod will remain compatible as it does not need to implement all methods from the Usermod base class!
|
||||
};
|
70
usermods/EleksTube_IPS/ChipSelect.h
Normal file
70
usermods/EleksTube_IPS/ChipSelect.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#ifndef CHIP_SELECT_H
|
||||
#define CHIP_SELECT_H
|
||||
|
||||
#include "Hardware.h"
|
||||
|
||||
/*
|
||||
* `digit`s are as defined in Hardware.h, 0 == seconds ones, 5 == hours tens.
|
||||
*/
|
||||
|
||||
class ChipSelect {
|
||||
private:
|
||||
uint8_t digits_map;
|
||||
const uint8_t all_on = 0x3F;
|
||||
const uint8_t all_off = 0x00;
|
||||
public:
|
||||
ChipSelect() : digits_map(all_off) {}
|
||||
|
||||
void update() {
|
||||
// Documented in README.md. Q7 and Q6 are unused. Q5 is Seconds Ones, Q0 is Hours Tens.
|
||||
// Q7 is the first bit written, Q0 is the last. So we push two dummy bits, then start with
|
||||
// Seconds Ones and end with Hours Tens.
|
||||
// CS is Active Low, but digits_map is 1 for enable, 0 for disable. So we bit-wise NOT first.
|
||||
|
||||
uint8_t to_shift = (~digits_map) << 2;
|
||||
|
||||
digitalWrite(CSSR_LATCH_PIN, LOW);
|
||||
shiftOut(CSSR_DATA_PIN, CSSR_CLOCK_PIN, LSBFIRST, to_shift);
|
||||
digitalWrite(CSSR_LATCH_PIN, HIGH);
|
||||
}
|
||||
|
||||
void begin()
|
||||
{
|
||||
pinMode(CSSR_LATCH_PIN, OUTPUT);
|
||||
pinMode(CSSR_DATA_PIN, OUTPUT);
|
||||
pinMode(CSSR_CLOCK_PIN, OUTPUT);
|
||||
|
||||
digitalWrite(CSSR_DATA_PIN, LOW);
|
||||
digitalWrite(CSSR_CLOCK_PIN, LOW);
|
||||
digitalWrite(CSSR_LATCH_PIN, LOW);
|
||||
update();
|
||||
}
|
||||
|
||||
// These speak the indexes defined in Hardware.h.
|
||||
// So 0 is disabled, 1 is enabled (even though CS is active low, this gets mapped.)
|
||||
// So bit 0 (LSB), is index 0, is SECONDS_ONES
|
||||
// Translation to what the 74HC595 uses is done in update()
|
||||
void setDigitMap(uint8_t map, bool update_=true) { digits_map = map; if (update_) update(); }
|
||||
uint8_t getDigitMap() { return digits_map; }
|
||||
|
||||
// Helper functions
|
||||
// Sets just the one digit by digit number
|
||||
void setDigit(uint8_t digit, bool update_=true) { setDigitMap(0x01 << digit, update_); }
|
||||
void setAll(bool update_=true) { setDigitMap(all_on, update_); }
|
||||
void clear(bool update_=true) { setDigitMap(all_off, update_); }
|
||||
void setSecondsOnes() { setDigit(SECONDS_ONES); }
|
||||
void setSecondsTens() { setDigit(SECONDS_TENS); }
|
||||
void setMinutesOnes() { setDigit(MINUTES_ONES); }
|
||||
void setMinutesTens() { setDigit(MINUTES_TENS); }
|
||||
void setHoursOnes() { setDigit(HOURS_ONES); }
|
||||
void setHoursTens() { setDigit(HOURS_TENS); }
|
||||
bool isSecondsOnes() { return ((digits_map & SECONDS_ONES_MAP) > 0); }
|
||||
bool isSecondsTens() { return ((digits_map & SECONDS_TENS_MAP) > 0); }
|
||||
bool isMinutesOnes() { return ((digits_map & MINUTES_ONES_MAP) > 0); }
|
||||
bool isMinutesTens() { return ((digits_map & MINUTES_TENS_MAP) > 0); }
|
||||
bool isHoursOnes() { return ((digits_map & HOURS_ONES_MAP) > 0); }
|
||||
bool isHoursTens() { return ((digits_map & HOURS_TENS_MAP) > 0); }
|
||||
};
|
||||
|
||||
|
||||
#endif // CHIP_SELECT_H
|
52
usermods/EleksTube_IPS/Hardware.h
Normal file
52
usermods/EleksTube_IPS/Hardware.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Define the hardware for the EleksTube IPS clock. Mostly pin definitions
|
||||
*/
|
||||
#ifndef ELEKSTUBEHAX_HARDWARE_H
|
||||
#define ELEKSTUBEHAX_HARDWARE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <Arduino.h> // for HIGH and LOW
|
||||
|
||||
// Common indexing scheme, used to identify the digit
|
||||
#define SECONDS_ONES (0)
|
||||
#define SECONDS_TENS (1)
|
||||
#define MINUTES_ONES (2)
|
||||
#define MINUTES_TENS (3)
|
||||
#define HOURS_ONES (4)
|
||||
#define HOURS_TENS (5)
|
||||
#define NUM_DIGITS (6)
|
||||
|
||||
#define SECONDS_ONES_MAP (0x01 << SECONDS_ONES)
|
||||
#define SECONDS_TENS_MAP (0x01 << SECONDS_TENS)
|
||||
#define MINUTES_ONES_MAP (0x01 << MINUTES_ONES)
|
||||
#define MINUTES_TENS_MAP (0x01 << MINUTES_TENS)
|
||||
#define HOURS_ONES_MAP (0x01 << HOURS_ONES)
|
||||
#define HOURS_TENS_MAP (0x01 << HOURS_TENS)
|
||||
|
||||
// WS2812 (or compatible) LEDs on the back of the display modules.
|
||||
#define BACKLIGHTS_PIN (12)
|
||||
|
||||
// Buttons, active low, externally pulled up (with actual resistors!)
|
||||
#define BUTTON_LEFT_PIN (33)
|
||||
#define BUTTON_MODE_PIN (32)
|
||||
#define BUTTON_RIGHT_PIN (35)
|
||||
#define BUTTON_POWER_PIN (34)
|
||||
|
||||
// I2C to DS3231 RTC.
|
||||
#define RTC_SCL_PIN (22)
|
||||
#define RTC_SDA_PIN (21)
|
||||
|
||||
// Chip Select shift register, to select the display
|
||||
#define CSSR_DATA_PIN (14)
|
||||
#define CSSR_CLOCK_PIN (16)
|
||||
#define CSSR_LATCH_PIN (17)
|
||||
|
||||
// SPI to displays
|
||||
// DEFINED IN User_Setup.h
|
||||
// Look for: TFT_MOSI, TFT_SCLK, TFT_CS, TFT_DC, and TFT_RST
|
||||
|
||||
// Power for all TFT displays are grounded through a MOSFET so they can all be turned off.
|
||||
// Active HIGH.
|
||||
#define TFT_ENABLE_PIN (27)
|
||||
|
||||
#endif // ELEKSTUBEHAX_HARDWARE_H
|
379
usermods/EleksTube_IPS/TFTs.h
Normal file
379
usermods/EleksTube_IPS/TFTs.h
Normal file
@@ -0,0 +1,379 @@
|
||||
#ifndef TFTS_H
|
||||
#define TFTS_H
|
||||
|
||||
#include "wled.h"
|
||||
#include <FS.h>
|
||||
|
||||
#include <TFT_eSPI.h>
|
||||
#include "Hardware.h"
|
||||
#include "ChipSelect.h"
|
||||
|
||||
class TFTs : public TFT_eSPI {
|
||||
private:
|
||||
uint8_t digits[NUM_DIGITS];
|
||||
|
||||
|
||||
// These read 16- and 32-bit types from the SD card file.
|
||||
// BMP data is stored little-endian, Arduino is little-endian too.
|
||||
// May need to reverse subscript order if porting elsewhere.
|
||||
|
||||
uint16_t read16(fs::File &f) {
|
||||
uint16_t result;
|
||||
((uint8_t *)&result)[0] = f.read(); // LSB
|
||||
((uint8_t *)&result)[1] = f.read(); // MSB
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t read32(fs::File &f) {
|
||||
uint32_t result;
|
||||
((uint8_t *)&result)[0] = f.read(); // LSB
|
||||
((uint8_t *)&result)[1] = f.read();
|
||||
((uint8_t *)&result)[2] = f.read();
|
||||
((uint8_t *)&result)[3] = f.read(); // MSB
|
||||
return result;
|
||||
}
|
||||
|
||||
uint16_t output_buffer[TFT_HEIGHT][TFT_WIDTH];
|
||||
int16_t w = 135, h = 240, x = 0, y = 0, bufferedDigit = 255;
|
||||
uint16_t digitR, digitG, digitB, dimming = 255;
|
||||
uint32_t digitColor = 0;
|
||||
|
||||
void drawBuffer() {
|
||||
bool oldSwapBytes = getSwapBytes();
|
||||
setSwapBytes(true);
|
||||
pushImage(x, y, w, h, (uint16_t *)output_buffer);
|
||||
setSwapBytes(oldSwapBytes);
|
||||
}
|
||||
|
||||
// These BMP functions are stolen directly from the TFT_SPIFFS_BMP example in the TFT_eSPI library.
|
||||
// Unfortunately, they aren't part of the library itself, so I had to copy them.
|
||||
// I've modified drawBmp to buffer the whole image at once instead of doing it line-by-line.
|
||||
|
||||
//// BEGIN STOLEN CODE
|
||||
|
||||
// Draw directly from file stored in RGB565 format. Fastest
|
||||
bool drawBin(const char *filename) {
|
||||
fs::File bmpFS;
|
||||
|
||||
// Open requested file on SD card
|
||||
bmpFS = WLED_FS.open(filename, "r");
|
||||
|
||||
size_t sz = bmpFS.size();
|
||||
if (sz > 64800) {
|
||||
bmpFS.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t r, g, b, dimming = 255;
|
||||
int16_t row, col;
|
||||
|
||||
//draw img that is shorter than 240pix into the center
|
||||
w = 135;
|
||||
h = sz / (w * 2);
|
||||
x = 0;
|
||||
y = (height() - h) /2;
|
||||
|
||||
uint8_t lineBuffer[w * 2];
|
||||
|
||||
if (!realtimeMode || realtimeOverride) strip.service();
|
||||
|
||||
// 0,0 coordinates are top left
|
||||
for (row = 0; row < h; row++) {
|
||||
|
||||
bmpFS.read(lineBuffer, sizeof(lineBuffer));
|
||||
uint8_t PixM, PixL;
|
||||
|
||||
// Colors are already in 16-bit R5, G6, B5 format
|
||||
for (col = 0; col < w; col++)
|
||||
{
|
||||
if (dimming == 255 && !digitColor) { // not needed, copy directly
|
||||
output_buffer[row][col] = (lineBuffer[col*2+1] << 8) | (lineBuffer[col*2]);
|
||||
} else {
|
||||
// 16 BPP pixel format: R5, G6, B5 ; bin: RRRR RGGG GGGB BBBB
|
||||
PixM = lineBuffer[col*2+1];
|
||||
PixL = lineBuffer[col*2];
|
||||
// align to 8-bit value (MSB left aligned)
|
||||
r = (PixM) & 0xF8;
|
||||
g = ((PixM << 5) | (PixL >> 3)) & 0xFC;
|
||||
b = (PixL << 3) & 0xF8;
|
||||
r *= dimming; g *= dimming; b *= dimming;
|
||||
r = r >> 8; g = g >> 8; b = b >> 8;
|
||||
if (digitColor) { // grayscale pixel coloring
|
||||
uint8_t l = (r > g) ? ((r > b) ? r:b) : ((g > b) ? g:b);
|
||||
r = g = b = l;
|
||||
r *= digitR; g *= digitG; b *= digitB;
|
||||
r = r >> 8; g = g >> 8; b = b >> 8;
|
||||
}
|
||||
output_buffer[row][col] = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drawBuffer();
|
||||
|
||||
bmpFS.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool drawBmp(const char *filename) {
|
||||
fs::File bmpFS;
|
||||
|
||||
// Open requested file on SD card
|
||||
bmpFS = WLED_FS.open(filename, "r");
|
||||
|
||||
uint32_t seekOffset, headerSize, paletteSize = 0;
|
||||
int16_t row;
|
||||
uint16_t r, g, b, dimming = 255, bitDepth;
|
||||
|
||||
uint16_t magic = read16(bmpFS);
|
||||
if (magic != ('B' | ('M' << 8))) { // File not found or not a BMP
|
||||
Serial.println(F("BMP not found!"));
|
||||
bmpFS.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
read32(bmpFS); // filesize in bytes
|
||||
read32(bmpFS); // reserved
|
||||
seekOffset = read32(bmpFS); // start of bitmap
|
||||
headerSize = read32(bmpFS); // header size
|
||||
w = read32(bmpFS); // width
|
||||
h = read32(bmpFS); // height
|
||||
read16(bmpFS); // color planes (must be 1)
|
||||
bitDepth = read16(bmpFS);
|
||||
|
||||
if (read32(bmpFS) != 0 || (bitDepth != 24 && bitDepth != 1 && bitDepth != 4 && bitDepth != 8)) {
|
||||
Serial.println(F("BMP format not recognized."));
|
||||
bmpFS.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t palette[256];
|
||||
if (bitDepth <= 8) // 1,4,8 bit bitmap: read color palette
|
||||
{
|
||||
read32(bmpFS); read32(bmpFS); read32(bmpFS); // size, w resolution, h resolution
|
||||
paletteSize = read32(bmpFS);
|
||||
if (paletteSize == 0) paletteSize = bitDepth * bitDepth; //if 0, size is 2^bitDepth
|
||||
bmpFS.seek(14 + headerSize); // start of color palette
|
||||
for (uint16_t i = 0; i < paletteSize; i++) {
|
||||
palette[i] = read32(bmpFS);
|
||||
}
|
||||
}
|
||||
|
||||
// draw img that is shorter than 240pix into the center
|
||||
x = (width() - w) /2;
|
||||
y = (height() - h) /2;
|
||||
|
||||
bmpFS.seek(seekOffset);
|
||||
|
||||
uint32_t lineSize = ((bitDepth * w +31) >> 5) * 4;
|
||||
uint8_t lineBuffer[lineSize];
|
||||
|
||||
uint8_t serviceStrip = (!realtimeMode || realtimeOverride) ? 7 : 0;
|
||||
// row is decremented as the BMP image is drawn bottom up
|
||||
for (row = h-1; row >= 0; row--) {
|
||||
if ((row & 0b00000111) == serviceStrip) strip.service(); //still refresh backlight to mitigate stutter every few rows
|
||||
bmpFS.read(lineBuffer, sizeof(lineBuffer));
|
||||
uint8_t* bptr = lineBuffer;
|
||||
|
||||
// Convert 24 to 16 bit colors while copying to output buffer.
|
||||
for (uint16_t col = 0; col < w; col++)
|
||||
{
|
||||
if (bitDepth == 24) {
|
||||
b = *bptr++;
|
||||
g = *bptr++;
|
||||
r = *bptr++;
|
||||
} else {
|
||||
uint32_t c = 0;
|
||||
if (bitDepth == 8) {
|
||||
c = palette[*bptr++];
|
||||
}
|
||||
else if (bitDepth == 4) {
|
||||
c = palette[(*bptr >> ((col & 0x01)?0:4)) & 0x0F];
|
||||
if (col & 0x01) bptr++;
|
||||
}
|
||||
else { // bitDepth == 1
|
||||
c = palette[(*bptr >> (7 - (col & 0x07))) & 0x01];
|
||||
if ((col & 0x07) == 0x07) bptr++;
|
||||
}
|
||||
b = c; g = c >> 8; r = c >> 16;
|
||||
}
|
||||
if (dimming != 255) { // only dimm when needed
|
||||
r *= dimming; g *= dimming; b *= dimming;
|
||||
r = r >> 8; g = g >> 8; b = b >> 8;
|
||||
}
|
||||
if (digitColor) { // grayscale pixel coloring
|
||||
uint8_t l = (r > g) ? ((r > b) ? r:b) : ((g > b) ? g:b);
|
||||
r = g = b = l;
|
||||
|
||||
r *= digitR; g *= digitG; b *= digitB;
|
||||
r = r >> 8; g = g >> 8; b = b >> 8;
|
||||
}
|
||||
output_buffer[row][col] = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xFF) >> 3);
|
||||
}
|
||||
}
|
||||
|
||||
drawBuffer();
|
||||
|
||||
bmpFS.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool drawClk(const char *filename) {
|
||||
fs::File bmpFS;
|
||||
|
||||
// Open requested file on SD card
|
||||
bmpFS = WLED_FS.open(filename, "r");
|
||||
|
||||
if (!bmpFS)
|
||||
{
|
||||
Serial.print("File not found: ");
|
||||
Serial.println(filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t r, g, b, dimming = 255, magic;
|
||||
int16_t row, col;
|
||||
|
||||
magic = read16(bmpFS);
|
||||
if (magic != 0x4B43) { // look for "CK" header
|
||||
Serial.print(F("File not a CLK. Magic: "));
|
||||
Serial.println(magic);
|
||||
bmpFS.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
w = read16(bmpFS);
|
||||
h = read16(bmpFS);
|
||||
x = (width() - w) / 2;
|
||||
y = (height() - h) / 2;
|
||||
|
||||
uint8_t lineBuffer[w * 2];
|
||||
|
||||
if (!realtimeMode || realtimeOverride) strip.service();
|
||||
|
||||
// 0,0 coordinates are top left
|
||||
for (row = 0; row < h; row++) {
|
||||
|
||||
bmpFS.read(lineBuffer, sizeof(lineBuffer));
|
||||
uint8_t PixM, PixL;
|
||||
|
||||
// Colors are already in 16-bit R5, G6, B5 format
|
||||
for (col = 0; col < w; col++)
|
||||
{
|
||||
if (dimming == 255 && !digitColor) { // not needed, copy directly
|
||||
output_buffer[row][col+x] = (lineBuffer[col*2+1] << 8) | (lineBuffer[col*2]);
|
||||
} else {
|
||||
// 16 BPP pixel format: R5, G6, B5 ; bin: RRRR RGGG GGGB BBBB
|
||||
PixM = lineBuffer[col*2+1];
|
||||
PixL = lineBuffer[col*2];
|
||||
// align to 8-bit value (MSB left aligned)
|
||||
r = (PixM) & 0xF8;
|
||||
g = ((PixM << 5) | (PixL >> 3)) & 0xFC;
|
||||
b = (PixL << 3) & 0xF8;
|
||||
r *= dimming; g *= dimming; b *= dimming;
|
||||
r = r >> 8; g = g >> 8; b = b >> 8;
|
||||
if (digitColor) { // grayscale pixel coloring
|
||||
uint8_t l = (r > g) ? ((r > b) ? r:b) : ((g > b) ? g:b);
|
||||
r = g = b = l;
|
||||
r *= digitR; g *= digitG; b *= digitB;
|
||||
r = r >> 8; g = g >> 8; b = b >> 8;
|
||||
}
|
||||
output_buffer[row][col+x] = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drawBuffer();
|
||||
|
||||
bmpFS.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
TFTs() : TFT_eSPI(), chip_select()
|
||||
{ for (uint8_t digit=0; digit < NUM_DIGITS; digit++) digits[digit] = 0; }
|
||||
|
||||
// no == Do not send to TFT. yes == Send to TFT if changed. force == Send to TFT.
|
||||
enum show_t { no, yes, force };
|
||||
// A digit of 0xFF means blank the screen.
|
||||
const static uint8_t blanked = 255;
|
||||
|
||||
uint8_t tubeSegment = 1;
|
||||
uint8_t digitOffset = 0;
|
||||
|
||||
void begin() {
|
||||
pinMode(TFT_ENABLE_PIN, OUTPUT);
|
||||
digitalWrite(TFT_ENABLE_PIN, HIGH); //enable displays on boot
|
||||
|
||||
// Start with all displays selected.
|
||||
chip_select.begin();
|
||||
chip_select.setAll();
|
||||
|
||||
// Initialize the super class.
|
||||
init();
|
||||
}
|
||||
|
||||
void showDigit(uint8_t digit) {
|
||||
chip_select.setDigit(digit);
|
||||
uint8_t digitToDraw = digits[digit];
|
||||
if (digitToDraw < 10) digitToDraw += digitOffset;
|
||||
|
||||
if (digitToDraw == blanked) {
|
||||
fillScreen(TFT_BLACK); return;
|
||||
}
|
||||
|
||||
// if last digit was the same, skip loading from FS to buffer
|
||||
if (!digitColor && digitToDraw == bufferedDigit) drawBuffer();
|
||||
digitR = R(digitColor); digitG = G(digitColor); digitB = B(digitColor);
|
||||
|
||||
// Filenames are no bigger than "254.bmp\0"
|
||||
char file_name[10];
|
||||
// Fastest, raw RGB565
|
||||
sprintf(file_name, "/%d.bin", digitToDraw);
|
||||
if (WLED_FS.exists(file_name)) {
|
||||
if (drawBin(file_name)) bufferedDigit = digitToDraw;
|
||||
return;
|
||||
}
|
||||
// Fast, raw RGB565, see https://github.com/aly-fly/EleksTubeHAX on how to create this clk format
|
||||
sprintf(file_name, "/%d.clk", digitToDraw);
|
||||
if (WLED_FS.exists(file_name)) {
|
||||
if (drawClk(file_name)) bufferedDigit = digitToDraw;
|
||||
return;
|
||||
}
|
||||
// Slow, regular RGB888 or 1,4,8 bit palette BMP
|
||||
sprintf(file_name, "/%d.bmp", digitToDraw);
|
||||
if (drawBmp(file_name)) bufferedDigit = digitToDraw;
|
||||
return;
|
||||
}
|
||||
|
||||
void setDigit(uint8_t digit, uint8_t value, show_t show=yes) {
|
||||
uint8_t old_value = digits[digit];
|
||||
digits[digit] = value;
|
||||
|
||||
// Color in grayscale bitmaps if Segment 1 exists
|
||||
// TODO If secondary and tertiary are black, color all in primary,
|
||||
// else color first three from Seg 1 color slots and last three from Seg 2 color slots
|
||||
WS2812FX::Segment& seg1 = strip.getSegment(tubeSegment);
|
||||
if (seg1.isActive()) {
|
||||
digitColor = strip.getPixelColor(seg1.start + digit);
|
||||
dimming = seg1.opacity;
|
||||
} else {
|
||||
digitColor = 0;
|
||||
dimming = 255;
|
||||
}
|
||||
|
||||
if (show != no && (old_value != value || show == force)) {
|
||||
showDigit(digit);
|
||||
}
|
||||
}
|
||||
uint8_t getDigit(uint8_t digit) {return digits[digit];}
|
||||
|
||||
void showAllDigits() {for (uint8_t digit=0; digit < NUM_DIGITS; digit++) showDigit(digit);}
|
||||
|
||||
// Making chip_select public so we don't have to proxy all methods, and the caller can just use it directly.
|
||||
ChipSelect chip_select;
|
||||
};
|
||||
|
||||
#endif // TFTS_H
|
47
usermods/EleksTube_IPS/User_Setup.h
Normal file
47
usermods/EleksTube_IPS/User_Setup.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* This is intended to over-ride `User_Setup.h` that comes with the TFT_eSPI library.
|
||||
* I hate having to modify the library code.
|
||||
*/
|
||||
|
||||
// ST7789 135 x 240 display with no chip select line
|
||||
|
||||
#define ST7789_DRIVER // Configure all registers
|
||||
|
||||
#define TFT_WIDTH 135
|
||||
#define TFT_HEIGHT 240
|
||||
|
||||
#define CGRAM_OFFSET // Library will add offsets required
|
||||
|
||||
//#define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue
|
||||
//#define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red
|
||||
|
||||
//#define TFT_INVERSION_ON
|
||||
//#define TFT_INVERSION_OFF
|
||||
|
||||
// EleksTube IPS
|
||||
#define TFT_SDA_READ // Read and write on the MOSI/SDA pin, no separate MISO pin
|
||||
#define TFT_MOSI 23
|
||||
#define TFT_SCLK 18
|
||||
//#define TFT_CS -1 // Not connected
|
||||
#define TFT_DC 25 // Data Command, aka Register Select or RS
|
||||
#define TFT_RST 26 // Connect reset to ensure display initialises
|
||||
|
||||
#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
|
||||
//#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
|
||||
//#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
|
||||
//#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
|
||||
//#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:.
|
||||
//#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
|
||||
//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT
|
||||
//#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts
|
||||
|
||||
//#define SMOOTH_FONT
|
||||
|
||||
|
||||
//#define SPI_FREQUENCY 27000000
|
||||
#define SPI_FREQUENCY 40000000
|
||||
|
||||
/*
|
||||
* To make the Library not over-write all this:
|
||||
*/
|
||||
#define USER_SETUP_LOADED
|
45
usermods/EleksTube_IPS/readme.md
Normal file
45
usermods/EleksTube_IPS/readme.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# EleksTube IPS Clock usermod
|
||||
|
||||
This usermod allows WLED to run on the EleksTube IPS clock.
|
||||
It enables running all WLED effects on the background SK6812 lighting, while displaying digit bitmaps on the 6 IPS screens.
|
||||
Code is largely based on https://github.com/SmittyHalibut/EleksTubeHAX by Mark Smith!
|
||||
|
||||
Supported:
|
||||
- Display with custom bitmaps (.bmp) or raw RGB565 images (.bin) from filesystem
|
||||
- Background lighting
|
||||
- All 4 hardware buttons
|
||||
- RTC (with RTC usermod)
|
||||
- Standard WLED time features (NTP, DST, timezones)
|
||||
|
||||
Not supported:
|
||||
- On-device setup with buttons (WiFi setup only)
|
||||
|
||||
Your images must be 1-135 pixels wide and 1-240 pixels high.
|
||||
For BMP, 1, 4, 8, and 24 bits per pixel formats are supported.
|
||||
|
||||
## Installation
|
||||
|
||||
Compile and upload to clock using the `elekstube_ips` PlatformIO environment
|
||||
Once uploaded (the clock can be flashed like any ESP32 module), go to `[WLED-IP]/edit` and upload the 0-9.bin files from [here](https://github.com/Aircoookie/NixieThemes/tree/master/themes/RealisticNixie/bin).
|
||||
You can find more clockfaces in the [NixieThemes](https://github.com/Aircoookie/NixieThemes/) repo.
|
||||
Use LED pin 12, relay pin 27 and button pin 34.
|
||||
|
||||
## Use of RGB565 images
|
||||
|
||||
Binary 16-bit per pixel RGB565 format `.bin` and `.clk` images are now supported. This has the benefit of only using 2/3rds of the file size a 24 BPP `.bmp` has.
|
||||
The drawback is that this format cannot be handled by common image programs and that an extra conversion step is needed.
|
||||
You can use https://lvgl.io/tools/imageconverter to convert your .bmp to a .bin file (settings `True color` and `Binary RGB565`).
|
||||
Thank you to @RedNax67 for adding .bin and .clk support.
|
||||
For most clockface designs, using 4 or 8 BPP BMP formats will save even more file size:
|
||||
|
||||
| Bits per pixel | File size in kB (for 135x240 img) | % of 24 BPP BMP | Max unique colors
|
||||
| --- | --- | --- | --- |
|
||||
24 | 98 | 100% | 16M (66K)
|
||||
16 (.clk) | 64.8 | 66% | 66K
|
||||
8 | 33.7 | 34% | 256
|
||||
4 | 16.4 | 17% | 16
|
||||
1 | 4.9 | 5% | 2
|
||||
|
||||
Comparison 1 vs. 4 vs. 8 vs. 24 BPP. With this clockface on the actual clock, 4 bit looks good, and 8 bit is almost indistinguishable from 24 bit.
|
||||
|
||||

|
158
usermods/EleksTube_IPS/usermod_elekstube_ips.h
Normal file
158
usermods/EleksTube_IPS/usermod_elekstube_ips.h
Normal file
@@ -0,0 +1,158 @@
|
||||
#pragma once
|
||||
#include "TFTs.h"
|
||||
#include "wled.h"
|
||||
|
||||
//Large parts of the code are from https://github.com/SmittyHalibut/EleksTubeHAX
|
||||
|
||||
class ElekstubeIPSUsermod : public Usermod {
|
||||
private:
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
static const char _name[];
|
||||
static const char _tubeSeg[];
|
||||
static const char _digitOffset[];
|
||||
|
||||
char cronixieDisplay[7] = "HHMMSS";
|
||||
|
||||
TFTs tfts;
|
||||
void updateClockDisplay(TFTs::show_t show=TFTs::yes) {
|
||||
bool set[6] = {false};
|
||||
for (uint8_t i = 0; i<6; i++) {
|
||||
char c = cronixieDisplay[i];
|
||||
if (c >= '0' && c <= '9') {
|
||||
tfts.setDigit(5-i, c - '0', show); set[i] = true;
|
||||
} else if (c >= 'A' && c <= 'G') {
|
||||
tfts.setDigit(5-i, c - 'A' + 10, show); set[i] = true; //10.bmp to 16.bmp static display
|
||||
} else if (c == '-' || c == '_' || c == ' ') {
|
||||
tfts.setDigit(5-i, 255, show); set[i] = true; //blank
|
||||
} else {
|
||||
set[i] = false; //display HHMMSS time
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint8_t hr = hour(localTime);
|
||||
uint8_t hrTens = hr/10;
|
||||
uint8_t mi = minute(localTime);
|
||||
uint8_t mittens = mi/10;
|
||||
uint8_t s = second(localTime);
|
||||
uint8_t sTens = s/10;
|
||||
if (!set[0]) tfts.setDigit(HOURS_TENS, hrTens, show);
|
||||
if (!set[1]) tfts.setDigit(HOURS_ONES, hr - hrTens*10, show);
|
||||
if (!set[2]) tfts.setDigit(MINUTES_TENS, mittens, show);
|
||||
if (!set[3]) tfts.setDigit(MINUTES_ONES, mi - mittens*10, show);
|
||||
if (!set[4]) tfts.setDigit(SECONDS_TENS, sTens, show);
|
||||
if (!set[5]) tfts.setDigit(SECONDS_ONES, s - sTens*10, show);
|
||||
}
|
||||
unsigned long lastTime = 0;
|
||||
public:
|
||||
|
||||
uint8_t lastBri;
|
||||
uint32_t lastCols[6];
|
||||
TFTs::show_t fshow=TFTs::yes;
|
||||
|
||||
void setup() {
|
||||
tfts.begin();
|
||||
tfts.fillScreen(TFT_BLACK);
|
||||
|
||||
for (int8_t i = 5; i >= 0; i--) {
|
||||
tfts.setDigit(i, 255, TFTs::force); //turn all off
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (!toki.isTick()) return;
|
||||
updateLocalTime();
|
||||
|
||||
WS2812FX::Segment& seg1 = strip.getSegment(tfts.tubeSegment);
|
||||
if (seg1.isActive()) {
|
||||
bool update = false;
|
||||
if (seg1.opacity != lastBri) update = true;
|
||||
lastBri = seg1.opacity;
|
||||
for (uint8_t i = 0; i < 6; i++) {
|
||||
uint32_t c = strip.getPixelColor(seg1.start + i);
|
||||
if (c != lastCols[i]) update = true;
|
||||
lastCols[i] = c;
|
||||
}
|
||||
if (update) fshow=TFTs::force;
|
||||
} else if (lastCols[0] != 0) { // Segment 1 deleted
|
||||
fshow=TFTs::force;
|
||||
lastCols[0] = 0;
|
||||
}
|
||||
|
||||
updateClockDisplay(fshow);
|
||||
fshow=TFTs::yes;
|
||||
}
|
||||
|
||||
/**
|
||||
* addToConfig() (called from set.cpp) stores persistent properties to cfg.json
|
||||
*/
|
||||
void addToConfig(JsonObject &root) {
|
||||
// we add JSON object: {"EleksTubeIPS": {"tubeSegment": 1, "digitOffset": 0}}
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
|
||||
top[FPSTR(_tubeSeg)] = tfts.tubeSegment;
|
||||
top[FPSTR(_digitOffset)] = tfts.digitOffset;
|
||||
DEBUG_PRINTLN(F("EleksTube config saved."));
|
||||
}
|
||||
|
||||
/**
|
||||
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
|
||||
*
|
||||
* The function should return true if configuration was successfully loaded or false if there was no configuration.
|
||||
*/
|
||||
bool readFromConfig(JsonObject &root) {
|
||||
// we look for JSON object: {"EleksTubeIPS": {"tubeSegment": 1, "digitOffset": 0}}
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
tfts.tubeSegment = top[FPSTR(_tubeSeg)] | tfts.tubeSegment;
|
||||
uint8_t digitOffsetPrev = tfts.digitOffset;
|
||||
tfts.digitOffset = top[FPSTR(_digitOffset)] | tfts.digitOffset;
|
||||
if (tfts.digitOffset > 240) tfts.digitOffset = 240;
|
||||
if (tfts.digitOffset != digitOffsetPrev) fshow=TFTs::force;
|
||||
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return !top[FPSTR(_digitOffset)].isNull();
|
||||
}
|
||||
|
||||
/*
|
||||
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
|
||||
* Values in the state object may be modified by connected clients
|
||||
*/
|
||||
void addToJsonState(JsonObject& root)
|
||||
{
|
||||
root["nx"] = cronixieDisplay;
|
||||
root[FPSTR(_digitOffset)] = tfts.digitOffset;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
|
||||
* Values in the state object may be modified by connected clients
|
||||
*/
|
||||
void readFromJsonState(JsonObject& root)
|
||||
{
|
||||
if (root["nx"].is<const char*>()) {
|
||||
strncpy(cronixieDisplay, root["nx"], 6);
|
||||
}
|
||||
|
||||
uint8_t digitOffsetPrev = tfts.digitOffset;
|
||||
tfts.digitOffset = root[FPSTR(_digitOffset)] | tfts.digitOffset;
|
||||
if (tfts.digitOffset > 240) tfts.digitOffset = 240;
|
||||
if (tfts.digitOffset != digitOffsetPrev) fshow=TFTs::force;
|
||||
}
|
||||
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_ELEKSTUBE_IPS;
|
||||
}
|
||||
};
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
const char ElekstubeIPSUsermod::_name[] PROGMEM = "EleksTubeIPS";
|
||||
const char ElekstubeIPSUsermod::_tubeSeg[] PROGMEM = "tubeSegment";
|
||||
const char ElekstubeIPSUsermod::_digitOffset[] PROGMEM = "digitOffset";
|
BIN
usermods/Enclosure_with_OLED_temp_ESP07/assets/controller.jpg
Normal file
BIN
usermods/Enclosure_with_OLED_temp_ESP07/assets/controller.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 136 KiB |
BIN
usermods/Enclosure_with_OLED_temp_ESP07/assets/pcb.png
Normal file
BIN
usermods/Enclosure_with_OLED_temp_ESP07/assets/pcb.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 201 KiB |
7
usermods/Enclosure_with_OLED_temp_ESP07/assets/readme.md
Normal file
7
usermods/Enclosure_with_OLED_temp_ESP07/assets/readme.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Enclosure and PCB
|
||||
|
||||
## IP67 rated enclosure
|
||||

|
||||
|
||||
## PCB
|
||||

|
68
usermods/Enclosure_with_OLED_temp_ESP07/readme.md
Normal file
68
usermods/Enclosure_with_OLED_temp_ESP07/readme.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# Almost universal controller board for outdoor applications
|
||||
This usermod is using ideas from @mrVanboy and @400killer
|
||||
|
||||
Installation of file: Copy and replace file in wled00 directory.
|
||||
|
||||
For BME280 sensor use usermod_bme280.cpp. Copy to wled00 and rename to usermod.cpp
|
||||
|
||||
## Project repository
|
||||
- [Original repository](https://github.com/srg74/Controller-for-WLED-firmware) - Main controller repository
|
||||
## Features
|
||||
- SSD1306 128x32 and 128x64 I2C OLED display
|
||||
- On screen IP address, SSID and controller status (e.g. ON or OFF, recent effect)
|
||||
- Auto display shutoff for saving display lifetime
|
||||
- Dallas temperature sensor
|
||||
- Reporting temperature to MQTT broker
|
||||
|
||||
## Hardware
|
||||

|
||||
|
||||
## Functionality checked with
|
||||
- ESP-07S
|
||||
- PlatformIO
|
||||
- SSD1306 128x32 I2C OLED display
|
||||
- DS18B20 (temperature sensor)
|
||||
- BME280 (temperature, humidity and pressure sensor)
|
||||
- KY-022 (infrared receiver)
|
||||
- Push button (N.O. momentary switch)
|
||||
|
||||
For Dallas sensor uncomment `U8g2@~2.27.3`,`DallasTemperature@~3.8.0`,`OneWire@~2.3.5 under` `[common]` section in `platformio.ini`:
|
||||
```ini
|
||||
# platformio.ini
|
||||
...
|
||||
[platformio]
|
||||
...
|
||||
default_envs = esp07
|
||||
; default_envs = d1_mini
|
||||
...
|
||||
[common]
|
||||
...
|
||||
lib_deps_external =
|
||||
...
|
||||
#For use SSD1306 OLED display uncomment following
|
||||
U8g2@~2.27.3
|
||||
#For Dallas sensor uncomment following 2 lines
|
||||
DallasTemperature@~3.8.0
|
||||
OneWire@~2.3.5
|
||||
...
|
||||
```
|
||||
|
||||
For BME280 sensor uncomment `U8g2@~2.27.3`,`BME280@~3.0.0 under` `[common]` section in `platformio.ini`:
|
||||
```ini
|
||||
# platformio.ini
|
||||
...
|
||||
[platformio]
|
||||
...
|
||||
default_envs = esp07
|
||||
; default_envs = d1_mini
|
||||
...
|
||||
[common]
|
||||
...
|
||||
lib_deps_external =
|
||||
...
|
||||
#For use SSD1306 OLED display uncomment following
|
||||
U8g2@~2.27.3
|
||||
#For BME280 sensor uncomment following
|
||||
BME280@~3.0.0
|
||||
...
|
||||
```
|
209
usermods/Enclosure_with_OLED_temp_ESP07/usermod.cpp
Normal file
209
usermods/Enclosure_with_OLED_temp_ESP07/usermod.cpp
Normal file
@@ -0,0 +1,209 @@
|
||||
#include "wled.h"
|
||||
#include <Arduino.h>
|
||||
#include <U8x8lib.h> // from https://github.com/olikraus/u8g2/
|
||||
#include <DallasTemperature.h> //Dallastemperature sensor
|
||||
//The SCL and SDA pins are defined here.
|
||||
//Lolin32 boards use SCL=5 SDA=4
|
||||
#define U8X8_PIN_SCL 5
|
||||
#define U8X8_PIN_SDA 4
|
||||
// Dallas sensor
|
||||
OneWire oneWire(13);
|
||||
DallasTemperature sensor(&oneWire);
|
||||
long temptimer = millis();
|
||||
long lastMeasure = 0;
|
||||
#define Celsius // Show temperature mesaurement in Celcius otherwise is in Fahrenheit
|
||||
|
||||
// If display does not work or looks corrupted check the
|
||||
// constructor reference:
|
||||
// https://github.com/olikraus/u8g2/wiki/u8x8setupcpp
|
||||
// or check the gallery:
|
||||
// https://github.com/olikraus/u8g2/wiki/gallery
|
||||
// --> First choise of cheap I2C OLED 128X32 0.91"
|
||||
U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA
|
||||
// --> Second choise of cheap I2C OLED 128X64 0.96" or 1.3"
|
||||
//U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA
|
||||
// gets called once at boot. Do all initialization that doesn't depend on
|
||||
// network here
|
||||
void userSetup() {
|
||||
sensor.begin(); //Start Dallas temperature sensor
|
||||
u8x8.begin();
|
||||
//u8x8.setFlipMode(1); //Uncoment if using WLED Wemos shield
|
||||
u8x8.setPowerSave(0);
|
||||
u8x8.setContrast(10); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255
|
||||
u8x8.setFont(u8x8_font_chroma48medium8_r);
|
||||
u8x8.drawString(0, 0, "Loading...");
|
||||
}
|
||||
|
||||
// gets called every time WiFi is (re-)connected. Initialize own network
|
||||
// interfaces here
|
||||
void userConnected() {}
|
||||
|
||||
// needRedraw marks if redraw is required to prevent often redrawing.
|
||||
bool needRedraw = true;
|
||||
|
||||
// Next variables hold the previous known values to determine if redraw is
|
||||
// required.
|
||||
String knownSsid = "";
|
||||
IPAddress knownIp;
|
||||
uint8_t knownBrightness = 0;
|
||||
uint8_t knownMode = 0;
|
||||
uint8_t knownPalette = 0;
|
||||
|
||||
long lastUpdate = 0;
|
||||
long lastRedraw = 0;
|
||||
bool displayTurnedOff = false;
|
||||
// How often we are redrawing screen
|
||||
#define USER_LOOP_REFRESH_RATE_MS 5000
|
||||
|
||||
void userLoop() {
|
||||
|
||||
//----> Dallas temperature sensor MQTT publishing
|
||||
temptimer = millis();
|
||||
// Timer to publishe new temperature every 60 seconds
|
||||
if (temptimer - lastMeasure > 60000)
|
||||
{
|
||||
lastMeasure = temptimer;
|
||||
//Check if MQTT Connected, otherwise it will crash the 8266
|
||||
if (mqtt != nullptr)
|
||||
{
|
||||
sensor.requestTemperatures();
|
||||
//Gets prefered temperature scale based on selection in definitions section
|
||||
#ifdef Celsius
|
||||
float board_temperature = sensor.getTempCByIndex(0);
|
||||
#else
|
||||
float board_temperature = sensor.getTempFByIndex(0);
|
||||
#endif
|
||||
//Create character string populated with user defined device topic from the UI, and the read temperature. Then publish to MQTT server.
|
||||
char subuf[38];
|
||||
strcpy(subuf, mqttDeviceTopic);
|
||||
strcat(subuf, "/temperature");
|
||||
mqtt->publish(subuf, 0, true, String(board_temperature).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we time interval for redrawing passes.
|
||||
if (millis() - lastUpdate < USER_LOOP_REFRESH_RATE_MS) {
|
||||
return;
|
||||
}
|
||||
lastUpdate = millis();
|
||||
|
||||
// Turn off display after 3 minutes with no change.
|
||||
if(!displayTurnedOff && millis() - lastRedraw > 3*60*1000) {
|
||||
u8x8.setPowerSave(1);
|
||||
displayTurnedOff = true;
|
||||
}
|
||||
|
||||
// Check if values which are shown on display changed from the last time.
|
||||
if (((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid) {
|
||||
needRedraw = true;
|
||||
} else if (knownIp != (apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP())) {
|
||||
needRedraw = true;
|
||||
} else if (knownBrightness != bri) {
|
||||
needRedraw = true;
|
||||
} else if (knownMode != strip.getMainSegment().mode) {
|
||||
needRedraw = true;
|
||||
} else if (knownPalette != strip.getMainSegment().palette) {
|
||||
needRedraw = true;
|
||||
}
|
||||
|
||||
if (!needRedraw) {
|
||||
return;
|
||||
}
|
||||
needRedraw = false;
|
||||
|
||||
if (displayTurnedOff)
|
||||
{
|
||||
u8x8.setPowerSave(0);
|
||||
displayTurnedOff = false;
|
||||
}
|
||||
lastRedraw = millis();
|
||||
|
||||
// Update last known values.
|
||||
#if defined(ESP8266)
|
||||
knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID();
|
||||
#else
|
||||
knownSsid = WiFi.SSID();
|
||||
#endif
|
||||
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
|
||||
knownBrightness = bri;
|
||||
knownMode = strip.getMainSegment().mode;
|
||||
knownPalette = strip.getMainSegment().palette;
|
||||
u8x8.clear();
|
||||
u8x8.setFont(u8x8_font_chroma48medium8_r);
|
||||
|
||||
// First row with Wifi name
|
||||
u8x8.setCursor(1, 0);
|
||||
u8x8.print(knownSsid.substring(0, u8x8.getCols() > 1 ? u8x8.getCols() - 2 : 0));
|
||||
// Print `~` char to indicate that SSID is longer, than owr dicplay
|
||||
if (knownSsid.length() > u8x8.getCols())
|
||||
u8x8.print("~");
|
||||
|
||||
// Second row with IP or Psssword
|
||||
u8x8.setCursor(1, 1);
|
||||
// Print password in AP mode and if led is OFF.
|
||||
if (apActive && bri == 0)
|
||||
u8x8.print(apPass);
|
||||
else
|
||||
u8x8.print(knownIp);
|
||||
|
||||
// Third row with mode name
|
||||
u8x8.setCursor(2, 2);
|
||||
uint8_t qComma = 0;
|
||||
bool insideQuotes = false;
|
||||
uint8_t printedChars = 0;
|
||||
char singleJsonSymbol;
|
||||
|
||||
// Find the mode name in JSON
|
||||
for (size_t i = 0; i < strlen_P(JSON_mode_names); i++) {
|
||||
singleJsonSymbol = pgm_read_byte_near(JSON_mode_names + i);
|
||||
switch (singleJsonSymbol) {
|
||||
case '"':
|
||||
insideQuotes = !insideQuotes;
|
||||
break;
|
||||
case '[':
|
||||
case ']':
|
||||
break;
|
||||
case ',':
|
||||
qComma++;
|
||||
default:
|
||||
if (!insideQuotes || (qComma != knownMode))
|
||||
break;
|
||||
u8x8.print(singleJsonSymbol);
|
||||
printedChars++;
|
||||
}
|
||||
if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2))
|
||||
break;
|
||||
}
|
||||
// Fourth row with palette name
|
||||
u8x8.setCursor(2, 3);
|
||||
qComma = 0;
|
||||
insideQuotes = false;
|
||||
printedChars = 0;
|
||||
// Looking for palette name in JSON.
|
||||
for (size_t i = 0; i < strlen_P(JSON_palette_names); i++) {
|
||||
singleJsonSymbol = pgm_read_byte_near(JSON_palette_names + i);
|
||||
switch (singleJsonSymbol) {
|
||||
case '"':
|
||||
insideQuotes = !insideQuotes;
|
||||
break;
|
||||
case '[':
|
||||
case ']':
|
||||
break;
|
||||
case ',':
|
||||
qComma++;
|
||||
default:
|
||||
if (!insideQuotes || (qComma != knownPalette))
|
||||
break;
|
||||
u8x8.print(singleJsonSymbol);
|
||||
printedChars++;
|
||||
}
|
||||
if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2))
|
||||
break;
|
||||
}
|
||||
|
||||
u8x8.setFont(u8x8_font_open_iconic_embedded_1x1);
|
||||
u8x8.drawGlyph(0, 0, 80); // wifi icon
|
||||
u8x8.drawGlyph(0, 1, 68); // home icon
|
||||
u8x8.setFont(u8x8_font_open_iconic_weather_2x2);
|
||||
u8x8.drawGlyph(0, 2, 66 + (bri > 0 ? 3 : 0)); // sun/moon icon
|
||||
}
|
266
usermods/Enclosure_with_OLED_temp_ESP07/usermod_bme280.cpp
Normal file
266
usermods/Enclosure_with_OLED_temp_ESP07/usermod_bme280.cpp
Normal file
@@ -0,0 +1,266 @@
|
||||
#include "wled.h"
|
||||
#include <Arduino.h>
|
||||
#include <U8x8lib.h> // from https://github.com/olikraus/u8g2/
|
||||
#include <Wire.h>
|
||||
#include <BME280I2C.h> //BME280 sensor
|
||||
|
||||
void UpdateBME280Data();
|
||||
|
||||
#define Celsius // Show temperature mesaurement in Celcius otherwise is in Fahrenheit
|
||||
BME280I2C bme; // Default : forced mode, standby time = 1000 ms
|
||||
// Oversampling = pressure ×1, temperature ×1, humidity ×1, filter off,
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32 //ESP32 boards
|
||||
uint8_t SCL_PIN = 22;
|
||||
uint8_t SDA_PIN = 21;
|
||||
#else //ESP8266 boards
|
||||
uint8_t SCL_PIN = 5;
|
||||
uint8_t SDA_PIN = 4;
|
||||
// uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8
|
||||
#endif
|
||||
|
||||
//The SCL and SDA pins are defined here.
|
||||
//ESP8266 Wemos D1 mini board use SCL=5 SDA=4 while ESP32 Wemos32 mini board use SCL=22 SDA=21
|
||||
#define U8X8_PIN_SCL SCL_PIN
|
||||
#define U8X8_PIN_SDA SDA_PIN
|
||||
//#define U8X8_PIN_RESET RST_PIN // Uncoment for Heltec WiFi-Kit-8
|
||||
|
||||
// If display does not work or looks corrupted check the
|
||||
// constructor reference:
|
||||
// https://github.com/olikraus/u8g2/wiki/u8x8setupcpp
|
||||
// or check the gallery:
|
||||
// https://github.com/olikraus/u8g2/wiki/gallery
|
||||
// --> First choise of cheap I2C OLED 128X32 0.91"
|
||||
U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA
|
||||
// --> Second choise of cheap I2C OLED 128X64 0.96" or 1.3"
|
||||
//U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA
|
||||
// --> Third choise of Heltec WiFi-Kit-8 OLED 128X32 0.91"
|
||||
//U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_RESET, U8X8_PIN_SCL, U8X8_PIN_SDA); // Constructor for Heltec WiFi-Kit-8
|
||||
// gets called once at boot. Do all initialization that doesn't depend on network here
|
||||
|
||||
// BME280 sensor timer
|
||||
long tempTimer = millis();
|
||||
long lastMeasure = 0;
|
||||
|
||||
float SensorPressure(NAN);
|
||||
float SensorTemperature(NAN);
|
||||
float SensorHumidity(NAN);
|
||||
|
||||
void userSetup() {
|
||||
u8x8.begin();
|
||||
u8x8.setPowerSave(0);
|
||||
u8x8.setFlipMode(1);
|
||||
u8x8.setContrast(10); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255
|
||||
u8x8.setFont(u8x8_font_chroma48medium8_r);
|
||||
u8x8.drawString(0, 0, "Loading...");
|
||||
Wire.begin(SDA_PIN,SCL_PIN);
|
||||
|
||||
while(!bme.begin())
|
||||
{
|
||||
Serial.println("Could not find BME280I2C sensor!");
|
||||
delay(1000);
|
||||
}
|
||||
switch(bme.chipModel())
|
||||
{
|
||||
case BME280::ChipModel_BME280:
|
||||
Serial.println("Found BME280 sensor! Success.");
|
||||
break;
|
||||
case BME280::ChipModel_BMP280:
|
||||
Serial.println("Found BMP280 sensor! No Humidity available.");
|
||||
break;
|
||||
default:
|
||||
Serial.println("Found UNKNOWN sensor! Error!");
|
||||
}
|
||||
}
|
||||
|
||||
// gets called every time WiFi is (re-)connected. Initialize own network
|
||||
// interfaces here
|
||||
void userConnected() {}
|
||||
|
||||
// needRedraw marks if redraw is required to prevent often redrawing.
|
||||
bool needRedraw = true;
|
||||
|
||||
// Next variables hold the previous known values to determine if redraw is
|
||||
// required.
|
||||
String knownSsid = "";
|
||||
IPAddress knownIp;
|
||||
uint8_t knownBrightness = 0;
|
||||
uint8_t knownMode = 0;
|
||||
uint8_t knownPalette = 0;
|
||||
|
||||
long lastUpdate = 0;
|
||||
long lastRedraw = 0;
|
||||
bool displayTurnedOff = false;
|
||||
// How often we are redrawing screen
|
||||
#define USER_LOOP_REFRESH_RATE_MS 5000
|
||||
|
||||
void userLoop() {
|
||||
|
||||
// BME280 sensor MQTT publishing
|
||||
tempTimer = millis();
|
||||
// Timer to publish new sensor data every 60 seconds
|
||||
if (tempTimer - lastMeasure > 60000)
|
||||
{
|
||||
lastMeasure = tempTimer;
|
||||
|
||||
// Check if MQTT Connected, otherwise it will crash the 8266
|
||||
if (mqtt != nullptr)
|
||||
{
|
||||
UpdateBME280Data();
|
||||
float board_temperature = SensorTemperature;
|
||||
float board_pressure = SensorPressure;
|
||||
float board_humidity = SensorHumidity;
|
||||
|
||||
// Create string populated with user defined device topic from the UI, and the read temperature, humidity and pressure. Then publish to MQTT server.
|
||||
String t = String(mqttDeviceTopic);
|
||||
t += "/temperature";
|
||||
mqtt->publish(t.c_str(), 0, true, String(board_temperature).c_str());
|
||||
String p = String(mqttDeviceTopic);
|
||||
p += "/pressure";
|
||||
mqtt->publish(p.c_str(), 0, true, String(board_pressure).c_str());
|
||||
String h = String(mqttDeviceTopic);
|
||||
h += "/humidity";
|
||||
mqtt->publish(h.c_str(), 0, true, String(board_humidity).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we time interval for redrawing passes.
|
||||
if (millis() - lastUpdate < USER_LOOP_REFRESH_RATE_MS) {
|
||||
return;
|
||||
}
|
||||
lastUpdate = millis();
|
||||
|
||||
// Turn off display after 3 minutes with no change.
|
||||
if(!displayTurnedOff && millis() - lastRedraw > 3*60*1000) {
|
||||
u8x8.setPowerSave(1);
|
||||
displayTurnedOff = true;
|
||||
}
|
||||
|
||||
// Check if values which are shown on display changed from the last time.
|
||||
if (((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid) {
|
||||
needRedraw = true;
|
||||
} else if (knownIp != (apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP())) {
|
||||
needRedraw = true;
|
||||
} else if (knownBrightness != bri) {
|
||||
needRedraw = true;
|
||||
} else if (knownMode != strip.getMainSegment().mode) {
|
||||
needRedraw = true;
|
||||
} else if (knownPalette != strip.getMainSegment().palette) {
|
||||
needRedraw = true;
|
||||
}
|
||||
|
||||
if (!needRedraw) {
|
||||
return;
|
||||
}
|
||||
needRedraw = false;
|
||||
|
||||
if (displayTurnedOff)
|
||||
{
|
||||
u8x8.setPowerSave(0);
|
||||
displayTurnedOff = false;
|
||||
}
|
||||
lastRedraw = millis();
|
||||
|
||||
// Update last known values.
|
||||
#if defined(ESP8266)
|
||||
knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID();
|
||||
#else
|
||||
knownSsid = WiFi.SSID();
|
||||
#endif
|
||||
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
|
||||
knownBrightness = bri;
|
||||
knownMode = strip.getMainSegment().mode;
|
||||
knownPalette = strip.getMainSegment().palette;
|
||||
u8x8.clear();
|
||||
u8x8.setFont(u8x8_font_chroma48medium8_r);
|
||||
|
||||
// First row with Wifi name
|
||||
u8x8.setCursor(1, 0);
|
||||
u8x8.print(knownSsid.substring(0, u8x8.getCols() > 1 ? u8x8.getCols() - 2 : 0));
|
||||
// Print `~` char to indicate that SSID is longer, than owr dicplay
|
||||
if (knownSsid.length() > u8x8.getCols())
|
||||
u8x8.print("~");
|
||||
|
||||
// Second row with IP or Psssword
|
||||
u8x8.setCursor(1, 1);
|
||||
// Print password in AP mode and if led is OFF.
|
||||
if (apActive && bri == 0)
|
||||
u8x8.print(apPass);
|
||||
else
|
||||
u8x8.print(knownIp);
|
||||
|
||||
// Third row with mode name
|
||||
u8x8.setCursor(2, 2);
|
||||
uint8_t qComma = 0;
|
||||
bool insideQuotes = false;
|
||||
uint8_t printedChars = 0;
|
||||
char singleJsonSymbol;
|
||||
|
||||
// Find the mode name in JSON
|
||||
for (size_t i = 0; i < strlen_P(JSON_mode_names); i++) {
|
||||
singleJsonSymbol = pgm_read_byte_near(JSON_mode_names + i);
|
||||
switch (singleJsonSymbol) {
|
||||
case '"':
|
||||
insideQuotes = !insideQuotes;
|
||||
break;
|
||||
case '[':
|
||||
case ']':
|
||||
break;
|
||||
case ',':
|
||||
qComma++;
|
||||
default:
|
||||
if (!insideQuotes || (qComma != knownMode))
|
||||
break;
|
||||
u8x8.print(singleJsonSymbol);
|
||||
printedChars++;
|
||||
}
|
||||
if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2))
|
||||
break;
|
||||
}
|
||||
// Fourth row with palette name
|
||||
u8x8.setCursor(2, 3);
|
||||
qComma = 0;
|
||||
insideQuotes = false;
|
||||
printedChars = 0;
|
||||
// Looking for palette name in JSON.
|
||||
for (size_t i = 0; i < strlen_P(JSON_palette_names); i++) {
|
||||
singleJsonSymbol = pgm_read_byte_near(JSON_palette_names + i);
|
||||
switch (singleJsonSymbol) {
|
||||
case '"':
|
||||
insideQuotes = !insideQuotes;
|
||||
break;
|
||||
case '[':
|
||||
case ']':
|
||||
break;
|
||||
case ',':
|
||||
qComma++;
|
||||
default:
|
||||
if (!insideQuotes || (qComma != knownPalette))
|
||||
break;
|
||||
u8x8.print(singleJsonSymbol);
|
||||
printedChars++;
|
||||
}
|
||||
if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2))
|
||||
break;
|
||||
}
|
||||
|
||||
u8x8.setFont(u8x8_font_open_iconic_embedded_1x1);
|
||||
u8x8.drawGlyph(0, 0, 80); // wifi icon
|
||||
u8x8.drawGlyph(0, 1, 68); // home icon
|
||||
u8x8.setFont(u8x8_font_open_iconic_weather_2x2);
|
||||
u8x8.drawGlyph(0, 2, 66 + (bri > 0 ? 3 : 0)); // sun/moon icon
|
||||
}
|
||||
|
||||
void UpdateBME280Data() {
|
||||
float temp(NAN), hum(NAN), pres(NAN);
|
||||
#ifdef Celsius
|
||||
BME280::TempUnit tempUnit(BME280::TempUnit_Celsius);
|
||||
#else
|
||||
BME280::TempUnit tempUnit(BME280::TempUnit_Fahrenheit);
|
||||
#endif
|
||||
BME280::PresUnit presUnit(BME280::PresUnit_Pa);
|
||||
bme.read(pres, temp, hum, tempUnit, presUnit);
|
||||
SensorTemperature=temp;
|
||||
SensorHumidity=hum;
|
||||
SensorPressure=pres;
|
||||
}
|
69
usermods/Fix_unreachable_netservices_v2/readme.md
Normal file
69
usermods/Fix_unreachable_netservices_v2/readme.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# Fix unreachable net services V2
|
||||
|
||||
**Attention: This usermod compiles only for ESP8266**
|
||||
|
||||
This usermod-v2 modification performs a ping request to the local IP address every 60 seconds. By this procedure the net services of WLED remains accessible in some problematic WLAN environments.
|
||||
|
||||
The modification works with static or DHCP IP address configuration.
|
||||
|
||||
_Story:_
|
||||
|
||||
Unfortunately, with all ESP projects where a web server or other network services are running, I have the problem that after some time the web server is no longer accessible. Now I found out that the connection is at least reestablished when a ping request is executed by the device.
|
||||
|
||||
With this modification, in the worst case, the network functions are not available for 60 seconds until the next ping request.
|
||||
|
||||
## Webinterface
|
||||
|
||||
The number of pings and reconnects is displayed on the info page in the web interface.
|
||||
The ping delay can be changed. Changes persist after a reboot.
|
||||
|
||||
## JSON API
|
||||
|
||||
The usermod supports the following state changes:
|
||||
|
||||
| JSON key | Value range | Description |
|
||||
|-------------|------------------|---------------------------------|
|
||||
| PingDelayMs | 5000 to 18000000 | Deactivdate/activate the sensor |
|
||||
|
||||
Changes also persist after a reboot.
|
||||
|
||||
## Installation
|
||||
|
||||
1. Copy the file `usermod_Fix_unreachable_netservices.h` to the `wled00` directory.
|
||||
2. Register the usermod by adding `#include "usermod_Fix_unreachable_netservices.h"` in the top and `registerUsermod(new FixUnreachableNetServices());` in the bottom of `usermods_list.cpp`.
|
||||
|
||||
Example **usermods_list.cpp**:
|
||||
|
||||
```cpp
|
||||
#include "wled.h"
|
||||
/*
|
||||
* Register your v2 usermods here!
|
||||
* (for v1 usermods using just usermod.cpp, you can ignore this file)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Add/uncomment your usermod filename here (and once more below)
|
||||
* || || ||
|
||||
* \/ \/ \/
|
||||
*/
|
||||
//#include "usermod_v2_example.h"
|
||||
//#include "usermod_temperature.h"
|
||||
//#include "usermod_v2_empty.h"
|
||||
#include "usermod_Fix_unreachable_netservices.h"
|
||||
|
||||
void registerUsermods()
|
||||
{
|
||||
/*
|
||||
* Add your usermod class name here
|
||||
* || || ||
|
||||
* \/ \/ \/
|
||||
*/
|
||||
//usermods.add(new MyExampleUsermod());
|
||||
//usermods.add(new UsermodTemperature());
|
||||
//usermods.add(new UsermodRenameMe());
|
||||
usermods.add(new FixUnreachableNetServices());
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Hopefully I can help someone with that - @gegu
|
@@ -0,0 +1,171 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
#if defined(ESP32)
|
||||
#warning "Usermod FixUnreachableNetServices works only with ESP8266 builds"
|
||||
class FixUnreachableNetServices : public Usermod
|
||||
{
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(ESP8266)
|
||||
#include <ping.h>
|
||||
|
||||
/*
|
||||
* This usermod performs a ping request to the local IP address every 60 seconds.
|
||||
* By this procedure the net services of WLED remains accessible in some problematic WLAN environments.
|
||||
*
|
||||
* Usermods allow you to add own functionality to WLED more easily
|
||||
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
|
||||
*
|
||||
* v2 usermods are class inheritance based and can (but don't have to) implement more functions, each of them is shown in this example.
|
||||
* Multiple v2 usermods can be added to one compilation easily.
|
||||
*
|
||||
* Creating a usermod:
|
||||
* This file serves as an example. If you want to create a usermod, it is recommended to use usermod_v2_empty.h from the usermods folder as a template.
|
||||
* Please remember to rename the class and file to a descriptive name.
|
||||
* You may also use multiple .h and .cpp files.
|
||||
*
|
||||
* Using a usermod:
|
||||
* 1. Copy the usermod into the sketch folder (same folder as wled00.ino)
|
||||
* 2. Register the usermod by adding #include "usermod_filename.h" in the top and registerUsermod(new MyUsermodClass()) in the bottom of usermods_list.cpp
|
||||
*/
|
||||
|
||||
class FixUnreachableNetServices : public Usermod
|
||||
{
|
||||
private:
|
||||
//Private class members. You can declare variables and functions only accessible to your usermod here
|
||||
unsigned long m_lastTime = 0;
|
||||
|
||||
// declare required variables
|
||||
unsigned long m_pingDelayMs = 60000;
|
||||
unsigned long m_connectedWiFi = 0;
|
||||
ping_option m_pingOpt;
|
||||
unsigned int m_pingCount = 0;
|
||||
bool m_updateConfig = false;
|
||||
|
||||
public:
|
||||
//Functions called by WLED
|
||||
|
||||
/**
|
||||
* setup() is called once at boot. WiFi is not yet connected at this point.
|
||||
* You can use it to initialize variables, sensors or similar.
|
||||
*/
|
||||
void setup()
|
||||
{
|
||||
//Serial.println("Hello from my usermod!");
|
||||
}
|
||||
|
||||
/**
|
||||
* connected() is called every time the WiFi is (re)connected
|
||||
* Use it to initialize network interfaces
|
||||
*/
|
||||
void connected()
|
||||
{
|
||||
//Serial.println("Connected to WiFi!");
|
||||
|
||||
++m_connectedWiFi;
|
||||
|
||||
// initialize ping_options structure
|
||||
memset(&m_pingOpt, 0, sizeof(struct ping_option));
|
||||
m_pingOpt.count = 1;
|
||||
m_pingOpt.ip = WiFi.localIP();
|
||||
}
|
||||
|
||||
/**
|
||||
* loop
|
||||
*/
|
||||
void loop()
|
||||
{
|
||||
if (m_connectedWiFi > 0 && millis() - m_lastTime > m_pingDelayMs)
|
||||
{
|
||||
ping_start(&m_pingOpt);
|
||||
m_lastTime = millis();
|
||||
++m_pingCount;
|
||||
}
|
||||
if (m_updateConfig)
|
||||
{
|
||||
serializeConfig();
|
||||
m_updateConfig = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
|
||||
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
|
||||
* Below it is shown how this could be used for e.g. a light sensor
|
||||
*/
|
||||
void addToJsonInfo(JsonObject &root)
|
||||
{
|
||||
//this code adds "u":{"⚡ Ping fix pings": m_pingCount} to the info object
|
||||
JsonObject user = root["u"];
|
||||
if (user.isNull())
|
||||
user = root.createNestedObject("u");
|
||||
|
||||
String uiDomString = "⚡ Ping fix pings<span style=\"display:block;padding-left:25px;\">\
|
||||
Delay <input type=\"number\" min=\"5\" max=\"300\" value=\"";
|
||||
uiDomString += (unsigned long)(m_pingDelayMs / 1000);
|
||||
uiDomString += "\" onchange=\"requestJson({PingDelay:parseInt(this.value)});\">sec</span>";
|
||||
|
||||
JsonArray infoArr = user.createNestedArray(uiDomString); //name
|
||||
infoArr.add(m_pingCount); //value
|
||||
|
||||
//this code adds "u":{"⚡ Reconnects": m_connectedWiFi - 1} to the info object
|
||||
infoArr = user.createNestedArray("⚡ Reconnects"); //name
|
||||
infoArr.add(m_connectedWiFi - 1); //value
|
||||
}
|
||||
|
||||
/**
|
||||
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
|
||||
* Values in the state object may be modified by connected clients
|
||||
*/
|
||||
void addToJsonState(JsonObject &root)
|
||||
{
|
||||
root["PingDelay"] = (m_pingDelayMs/1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
|
||||
* Values in the state object may be modified by connected clients
|
||||
*/
|
||||
void readFromJsonState(JsonObject &root)
|
||||
{
|
||||
if (root["PingDelay"] != nullptr)
|
||||
{
|
||||
m_pingDelayMs = (1000 * max(1UL, min(300UL, root["PingDelay"].as<unsigned long>())));
|
||||
m_updateConfig = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* provide the changeable values
|
||||
*/
|
||||
void addToConfig(JsonObject &root)
|
||||
{
|
||||
JsonObject top = root.createNestedObject("FixUnreachableNetServices");
|
||||
top["PingDelayMs"] = m_pingDelayMs;
|
||||
}
|
||||
|
||||
/**
|
||||
* restore the changeable values
|
||||
*/
|
||||
bool readFromConfig(JsonObject &root)
|
||||
{
|
||||
JsonObject top = root["FixUnreachableNetServices"];
|
||||
if (top.isNull()) return false;
|
||||
m_pingDelayMs = top["PingDelayMs"] | m_pingDelayMs;
|
||||
m_pingDelayMs = max(5000UL, min(18000000UL, m_pingDelayMs));
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
|
||||
* This could be used in the future for the system to determine whether your usermod is installed.
|
||||
*/
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_FIXNETSERVICES;
|
||||
}
|
||||
};
|
||||
#endif
|
119
usermods/JSON_IR_remote/21-key_ir.json
Normal file
119
usermods/JSON_IR_remote/21-key_ir.json
Normal file
@@ -0,0 +1,119 @@
|
||||
{
|
||||
"desc": "21-key",
|
||||
"0xFFA25D": {
|
||||
"label": "On",
|
||||
"pos": "1x1",
|
||||
"cmd": "T=1"
|
||||
},
|
||||
"0xFF629D": {
|
||||
"label": "Off",
|
||||
"pos": "1x2",
|
||||
"cmd": "T=0"
|
||||
},
|
||||
"0xFFE21D": {
|
||||
"label": "Flash",
|
||||
"pos": "1x3",
|
||||
"cmnt": "Cycle Effects",
|
||||
"cmd": "CY=0&FX=~"
|
||||
},
|
||||
"0xFF22DD": {
|
||||
"label": "Strobe",
|
||||
"pos": "2x1",
|
||||
"cmnt": "Sinelon Dual",
|
||||
"cmd": "CY=0&FX=93"
|
||||
},
|
||||
"0xFF02FD": {
|
||||
"label": "Fade",
|
||||
"pos": "2x2",
|
||||
"cmnt": "Rain",
|
||||
"cmd": "CY=0&FX=43"
|
||||
},
|
||||
"0xFFC23D": {
|
||||
"label": "Smooth",
|
||||
"pos": "2x3",
|
||||
"cmnt": "Aurora",
|
||||
"cmd": "CY=0&FX=38"
|
||||
},
|
||||
"0xFFE01F": {
|
||||
"label": "Bright +",
|
||||
"pos": "3x1",
|
||||
"cmd": "A=~16"
|
||||
},
|
||||
"0xFFA857": {
|
||||
"label": "Bright -",
|
||||
"pos": "3x2",
|
||||
"cmd": "A=~-16"
|
||||
},
|
||||
"0xFF906F": {
|
||||
"label": "White",
|
||||
"pos": "3x3",
|
||||
"cmd": "FP=5&CL=hFFFFFF&C2=hFFFFFF&C3=hA8A8A8"
|
||||
},
|
||||
"0xFF6897": {
|
||||
"label": "Red",
|
||||
"pos": "4x1",
|
||||
"cmnt": "Lava",
|
||||
"cmd": "FP=8"
|
||||
},
|
||||
"0xFF9867": {
|
||||
"label": "Green",
|
||||
"pos": "4x2",
|
||||
"cmnt": "Forest",
|
||||
"cmd": "FP=10"
|
||||
},
|
||||
"0xFFB04F": {
|
||||
"label": "Blue",
|
||||
"pos": "4x3",
|
||||
"cmnt": "Breeze",
|
||||
"cmd": "FP=15"
|
||||
},
|
||||
"0xFF30CF": {
|
||||
"label": "Tomato",
|
||||
"pos": "5x1",
|
||||
"cmd": "FP=5&CL=hFF6347&C2=hFFBF47&C3=hA85859"
|
||||
},
|
||||
"0xFF18E7": {
|
||||
"label": "LightGreen",
|
||||
"pos": "5x2",
|
||||
"cmnt": "Rivendale",
|
||||
"cmd": "FP=14"
|
||||
},
|
||||
"0xFF7A85": {
|
||||
"label": "SkyBlue",
|
||||
"pos": "5x3",
|
||||
"cmnt": "Ocean",
|
||||
"cmd": "FP=9"
|
||||
},
|
||||
"0xFF10EF": {
|
||||
"label": "Orange",
|
||||
"pos": "6x1",
|
||||
"cmnt": "Orangery",
|
||||
"cmd": "FP=47"
|
||||
},
|
||||
"0xFF38C7": {
|
||||
"label": "Aqua",
|
||||
"pos": "6x2",
|
||||
"cmd": "FP=5&CL=hFFFF&C2=h7FFF&C3=h39A895"
|
||||
},
|
||||
"0xFF5AA5": {
|
||||
"label": "Purple",
|
||||
"pos": "6x3",
|
||||
"cmd": "FP=5&CL=h663399&C2=h993399&C3=h473864"
|
||||
},
|
||||
"0xFF42BD": {
|
||||
"label": "Yellow",
|
||||
"pos": "7x1",
|
||||
"cmd": "FP=5&CL=hFFFF00&C2=hFFC800&C3=hFDFFDE"
|
||||
},
|
||||
"0xFF4AB5": {
|
||||
"label": "Cyan",
|
||||
"pos": "7x2",
|
||||
"cmnt": "Beech",
|
||||
"cmd": "FP=22"
|
||||
},
|
||||
"0xFF52AD": {
|
||||
"label": "Pink",
|
||||
"pos": "7x3",
|
||||
"cmd": "FP=5&CL=hFFC0CB&C2=hFFD4C0&C3=hA88C96"
|
||||
}
|
||||
}
|
147
usermods/JSON_IR_remote/24-key_ir.json
Normal file
147
usermods/JSON_IR_remote/24-key_ir.json
Normal file
@@ -0,0 +1,147 @@
|
||||
{
|
||||
"desc": "24-key",
|
||||
"0xF700FF": {
|
||||
"label": "+",
|
||||
"pos": "1x1",
|
||||
"cmnt": "Speed +",
|
||||
"cmd": "SX=~16"
|
||||
},
|
||||
"0xF7807F": {
|
||||
"label": "-",
|
||||
"pos": "1x2",
|
||||
"cmnt": "Speed -",
|
||||
"cmd": "SX=~-16"
|
||||
},
|
||||
"0xF740BF": {
|
||||
"label": "On/Off",
|
||||
"pos": "1x3",
|
||||
"cmnt": "Toggle On/Off",
|
||||
"cmd": "T=2"
|
||||
},
|
||||
"0xF7C03F": {
|
||||
"label": "W",
|
||||
"pos": "1x4",
|
||||
"cmnt": "Cycle color palette",
|
||||
"cmd": "FP=~"
|
||||
},
|
||||
"0xF720DF": {
|
||||
"label": "R",
|
||||
"pos": "2x1",
|
||||
"cmnt": "Lava",
|
||||
"cmd": "FP=8"
|
||||
},
|
||||
"0xF7A05F": {
|
||||
"label": "G",
|
||||
"pos": "2x2",
|
||||
"cmnt": "Forest",
|
||||
"cmd": "FP=10"
|
||||
},
|
||||
"0xF7609F": {
|
||||
"label": "B",
|
||||
"pos": "2x3",
|
||||
"cmnt": "Breeze",
|
||||
"cmd": "FP=15"
|
||||
},
|
||||
"0xF7E01F": {
|
||||
"label": "Bright -",
|
||||
"pos": "2x4",
|
||||
"cmnt": "Bright -",
|
||||
"cmd": "A=~-16"
|
||||
},
|
||||
"0xF710EF": {
|
||||
"label": "Timer1H",
|
||||
"pos": "3x1",
|
||||
"cmnt": "Timer 60 min",
|
||||
"cmd": "NL=60&NT=0"
|
||||
},
|
||||
"0xF7906F": {
|
||||
"label": "Timer4H",
|
||||
"pos": "3x2",
|
||||
"cmnt": "Timer 30 min",
|
||||
"cmd": "NL=30&NT=0"
|
||||
},
|
||||
"0xF750AF": {
|
||||
"label": "Timer8H",
|
||||
"pos": "3x3",
|
||||
"cmnt": "Timer 15 min",
|
||||
"cmd": "NL=15&NT=0"
|
||||
},
|
||||
"0xF7D02F": {
|
||||
"label": "Bright128",
|
||||
"pos": "3x4",
|
||||
"cmnt": "Bright 128",
|
||||
"cmd": "A=128"
|
||||
},
|
||||
"0xF730CF": {
|
||||
"label": "Music1",
|
||||
"pos": "4x1",
|
||||
"cmnt": "Cycle FX +",
|
||||
"cmd": "FX=~"
|
||||
},
|
||||
"0xF7B04F": {
|
||||
"label": "Music2",
|
||||
"pos": "4x2",
|
||||
"cmnt": "Cycle FX -",
|
||||
"cmd": "FX=~-1"
|
||||
},
|
||||
"0xF7708F": {
|
||||
"label": "Music3",
|
||||
"pos": "4x3",
|
||||
"cmnt": "Reset FX and FP",
|
||||
"cmd": "FX=1&PF=6"
|
||||
},
|
||||
"0xF7F00F": {
|
||||
"label": "Bright +",
|
||||
"pos": "4x4",
|
||||
"cmnt": "Bright +",
|
||||
"cmd": "A=~16"
|
||||
},
|
||||
"0xF708F7": {
|
||||
"label": "Mode1",
|
||||
"pos": "5x1",
|
||||
"cmnt": "Preset 1",
|
||||
"cmd": "PL=1"
|
||||
},
|
||||
"0xF78877": {
|
||||
"label": "Mode2",
|
||||
"pos": "5x2",
|
||||
"cmnt": "Preset 2",
|
||||
"cmd": "PL=2"
|
||||
},
|
||||
"0xF748B7": {
|
||||
"label": "Mode3",
|
||||
"pos": "5x3",
|
||||
"cmnt": "Preset 3",
|
||||
"cmd": "PL=3"
|
||||
},
|
||||
"0xF7C837": {
|
||||
"label": "Up",
|
||||
"pos": "5x4",
|
||||
"cmnt": "Intensity +",
|
||||
"cmd": "IX=~16"
|
||||
},
|
||||
"0xF728D7": {
|
||||
"label": "Mode4",
|
||||
"pos": "6x1",
|
||||
"cmnt": "Preset 4",
|
||||
"cmd": "PL=4"
|
||||
},
|
||||
"0xF7A857": {
|
||||
"label": "Mode5",
|
||||
"pos": "6x2",
|
||||
"cmnt": "Preset 5",
|
||||
"cmd": "PL=5"
|
||||
},
|
||||
"0xF76897": {
|
||||
"label": "Cycle",
|
||||
"pos": "6x3",
|
||||
"cmnt": "Toggle preset cycle",
|
||||
"cmd": "CY=1&PT=60000"
|
||||
},
|
||||
"0xF7E817": {
|
||||
"label": "Down",
|
||||
"pos": "6x4",
|
||||
"cmnt": "Intensity -",
|
||||
"cmd": "IX=~-16"
|
||||
}
|
||||
}
|
185
usermods/JSON_IR_remote/32-key_ir.json
Normal file
185
usermods/JSON_IR_remote/32-key_ir.json
Normal file
@@ -0,0 +1,185 @@
|
||||
{
|
||||
"desc": "32-key",
|
||||
"0xFF08F7": {
|
||||
"label": "On",
|
||||
"pos": "1x1",
|
||||
"cmd": "T=1"
|
||||
},
|
||||
"0xFFC03F": {
|
||||
"label": "Off",
|
||||
"pos": "1x2",
|
||||
"cmd": "T=0"
|
||||
},
|
||||
"0xFF807F": {
|
||||
"label": "Auto",
|
||||
"pos": "1x3",
|
||||
"cmnt": "Toggle preset cycle",
|
||||
"cmd": "CY=2"
|
||||
},
|
||||
"0xFF609F": {
|
||||
"label": "Mode",
|
||||
"pos": "1x4",
|
||||
"cmnt": "Cycle effects",
|
||||
"cmd": "FX=~&CY=0"
|
||||
},
|
||||
"0xFF906F": {
|
||||
"label": "4H",
|
||||
"pos": "2x1",
|
||||
"cmnt": "Timer 60min",
|
||||
"cmd": "NL=60&NT=0"
|
||||
},
|
||||
"0xFFB847": {
|
||||
"label": "6H",
|
||||
"pos": "2x2",
|
||||
"cmnt": "Timer 90min",
|
||||
"cmd": "NL=90&NT=0"
|
||||
},
|
||||
"0xFFF807": {
|
||||
"label": "8H",
|
||||
"pos": "2x3",
|
||||
"cmnt": "Timer 120min",
|
||||
"cmd": "NL=120&NT=0"
|
||||
},
|
||||
"0xFFB04F": {
|
||||
"label": "Timer Off",
|
||||
"pos": "2x4",
|
||||
"cmd": "NL=0"
|
||||
},
|
||||
"0xFF9867": {
|
||||
"label": "Red",
|
||||
"pos": "3x1",
|
||||
"cmnt": "Lava",
|
||||
"cmd": "FP=8"
|
||||
},
|
||||
"0xFFD827": {
|
||||
"label": "Green",
|
||||
"pos": "3x2",
|
||||
"cmnt": "Forest",
|
||||
"cmd": "FP=10"
|
||||
},
|
||||
"0xFF8877": {
|
||||
"label": "Blue",
|
||||
"pos": "3x3",
|
||||
"cmnt": "Breeze",
|
||||
"cmd": "FP=15"
|
||||
},
|
||||
"0xFFA857": {
|
||||
"label": "White",
|
||||
"pos": "3x4",
|
||||
"cmd": "FP=5&CL=hFFFFFF&C2=hFFE4CD&C3=hE4E4FF"
|
||||
},
|
||||
"0xFFE817": {
|
||||
"label": "OrangeRed",
|
||||
"pos": "4x1",
|
||||
"cmnt": "Sakura",
|
||||
"cmd": "FP=49"
|
||||
},
|
||||
"0xFF48B7": {
|
||||
"label": "SeaGreen",
|
||||
"pos": "4x2",
|
||||
"cmnt": "Rivendale",
|
||||
"cmd": "FP=14"
|
||||
},
|
||||
"0xFF6897": {
|
||||
"label": "RoyalBlue",
|
||||
"pos": "4x3",
|
||||
"cmnt": "Ocean",
|
||||
"cmd": "FP=9"
|
||||
},
|
||||
"0xFFB24D": {
|
||||
"label": "DarkBlue",
|
||||
"pos": "4x4",
|
||||
"cmnt": "Breeze",
|
||||
"cmd": "FP=15"
|
||||
},
|
||||
"0xFF02FD": {
|
||||
"label": "Orange",
|
||||
"pos": "5x1",
|
||||
"cmnt": "Orangery",
|
||||
"cmd": "FP=47"
|
||||
},
|
||||
"0xFF32CD": {
|
||||
"label": "YellowGreen",
|
||||
"pos": "5x2",
|
||||
"cmnt": "Aurora",
|
||||
"cmd": "FP=37"
|
||||
},
|
||||
"0xFF20DF": {
|
||||
"label": "SkyBlue",
|
||||
"pos": "5x3",
|
||||
"cmnt": "Beech",
|
||||
"cmd": "FP=22"
|
||||
},
|
||||
"0xFF00FF": {
|
||||
"label": "Orchid",
|
||||
"pos": "5x4",
|
||||
"cmd": "FP=5&CL=hDA70D6&C2=hDA70A0&C3=h89618F"
|
||||
},
|
||||
"0xFF50AF": {
|
||||
"label": "Yellow",
|
||||
"pos": "6x1",
|
||||
"cmd": "FP=5&CL=hFFFF00&C2=hFFC800&C3=hFDFFDE"
|
||||
},
|
||||
"0xFF7887": {
|
||||
"label": "DarkGreen",
|
||||
"pos": "6x2",
|
||||
"cmnt": "Orange and Teal",
|
||||
"cmd": "FP=44"
|
||||
},
|
||||
"0xFF708F": {
|
||||
"label": "RebeccaPurple",
|
||||
"pos": "6x3",
|
||||
"cmd": "FP=5&CL=h800080&C2=h800040&C3=h4B1C54"
|
||||
},
|
||||
"0xFF58A7": {
|
||||
"label": "Plum",
|
||||
"pos": "6x4",
|
||||
"cmd": "FP=5&CL=hDDA0DD&C2=hDDA0BE&C3=h8D7791"
|
||||
},
|
||||
"0xFF38C7": {
|
||||
"label": "Strobe",
|
||||
"pos": "7x1",
|
||||
"cmnt": "Dancing Shadows",
|
||||
"cmd": "FX=112&CY=0"
|
||||
},
|
||||
"0xFF28D7": {
|
||||
"label": "In Waves",
|
||||
"pos": "7x2",
|
||||
"cmnt": "Noise 1",
|
||||
"cmd": "FX=70&CY=0"
|
||||
},
|
||||
"0xFFF00F": {
|
||||
"label": "Speed +",
|
||||
"pos": "7x3",
|
||||
"cmd": "SX=~16"
|
||||
},
|
||||
"0xFF30CF": {
|
||||
"label": "Speed -",
|
||||
"pos": "7x4",
|
||||
"cmd": "SX=~-16"
|
||||
},
|
||||
"0xFF40BF": {
|
||||
"label": "Jump",
|
||||
"pos": "8x1",
|
||||
"cmnt": "Colortwinkles",
|
||||
"cmd": "FX=74&CY=0"
|
||||
},
|
||||
"0xFF12ED": {
|
||||
"label": "Fade",
|
||||
"pos": "8x2",
|
||||
"cmnt": "Sunrise",
|
||||
"cmd": "FX=104&CY=0"
|
||||
},
|
||||
"0xFF2AD5": {
|
||||
"label": "Flash",
|
||||
"pos": "8x3",
|
||||
"cmnt": "Railway",
|
||||
"cmd": "FX=78&CY=0"
|
||||
},
|
||||
"0xFFA05F": {
|
||||
"label": "Chase Flash",
|
||||
"pos": "8x4",
|
||||
"cmnt": "Washing Machine",
|
||||
"cmd": "FX=113&CY=0"
|
||||
}
|
||||
}
|
233
usermods/JSON_IR_remote/40-key-black_ir.json
Normal file
233
usermods/JSON_IR_remote/40-key-black_ir.json
Normal file
@@ -0,0 +1,233 @@
|
||||
{
|
||||
"desc": "40-key-black",
|
||||
"0xFF3AC5": {
|
||||
"label": "Bright +",
|
||||
"pos": "1x1",
|
||||
"cmd": "A=~16"
|
||||
},
|
||||
"0xFFBA45": {
|
||||
"label": "Bright -",
|
||||
"pos": "1x2",
|
||||
"cmd": "A=~-16"
|
||||
},
|
||||
"0xFF827D": {
|
||||
"label": "Off",
|
||||
"pos": "1x3",
|
||||
"cmd": "T=0"
|
||||
},
|
||||
"0xFF02FD": {
|
||||
"label": "On",
|
||||
"pos": "1x4",
|
||||
"cmd": "T=1"
|
||||
},
|
||||
"0xFF1AE5": {
|
||||
"label": "Red",
|
||||
"pos": "2x1",
|
||||
"cmnt": "Lava",
|
||||
"cmd": "FP=8"
|
||||
},
|
||||
"0xFF9A65": {
|
||||
"label": "Green",
|
||||
"pos": "2x2",
|
||||
"cmnt": "Forest",
|
||||
"cmd": "FP=10"
|
||||
},
|
||||
"0xFFA25D": {
|
||||
"label": "Blue",
|
||||
"pos": "2x3",
|
||||
"cmnt": "Breeze",
|
||||
"cmd": "FP=15"
|
||||
},
|
||||
"0xFF22DD": {
|
||||
"label": "White",
|
||||
"pos": "2x4",
|
||||
"cmd": "FP=5&CL=hFFFFFF&C2=hFFFFFF&C3=hA8A8A8"
|
||||
},
|
||||
"0xFF2AD5": {
|
||||
"label": "Tomato",
|
||||
"pos": "3x1",
|
||||
"cmnt": "Yelmag",
|
||||
"cmd": "FP=5&CL=hFF6347&C2=hFFBF47&C3=hA85859"
|
||||
},
|
||||
"0xFFAA55": {
|
||||
"label": "LightGreen",
|
||||
"pos": "3x2",
|
||||
"cmnt": "Rivendale",
|
||||
"cmd": "FP=14"
|
||||
},
|
||||
"0xFF926D": {
|
||||
"label": "SkyBlue",
|
||||
"pos": "3x3",
|
||||
"cmnt": "Ocean",
|
||||
"cmd": "FP=9"
|
||||
},
|
||||
"0xFF12ED": {
|
||||
"label": "WarmWhite",
|
||||
"pos": "3x4",
|
||||
"cmnt": "Warm White",
|
||||
"cmd": "FP=5&CL=hFFE4CD&C2=hFFFCCD&C3=hA89892"
|
||||
},
|
||||
"0xFF0AF5": {
|
||||
"label": "OrangeRed",
|
||||
"pos": "4x1",
|
||||
"cmnt": "Sakura",
|
||||
"cmd": "FP=49"
|
||||
},
|
||||
"0xFF8A75": {
|
||||
"label": "Cyan",
|
||||
"pos": "4x2",
|
||||
"cmnt": "Beech",
|
||||
"cmd": "FP=22"
|
||||
},
|
||||
"0xFFB24D": {
|
||||
"label": "RebeccaPurple",
|
||||
"pos": "4x3",
|
||||
"cmd": "FP=5&CL=h663399&C2=h993399&C3=h473864"
|
||||
},
|
||||
"0xFF32CD": {
|
||||
"label": "CoolWhite",
|
||||
"pos": "4x4",
|
||||
"cmnt": "Cool White",
|
||||
"cmd": "FP=5&CL=hE4E4FF&C2=hF1E4FF&C3=h9C9EA8"
|
||||
},
|
||||
"0xFF38C7": {
|
||||
"label": "Orange",
|
||||
"pos": "5x1",
|
||||
"cmnt": "Orangery",
|
||||
"cmd": "FP=47"
|
||||
},
|
||||
"0xFFB847": {
|
||||
"label": "Turquoise",
|
||||
"pos": "5x2",
|
||||
"cmd": "FP=5&CL=h40E0D0&C2=h40A0E0&C3=h4E9381"
|
||||
},
|
||||
"0xFF7887": {
|
||||
"label": "Purple",
|
||||
"pos": "5x3",
|
||||
"cmd": "FP=5&CL=h800080&C2=h800040&C3=h4B1C54"
|
||||
},
|
||||
"0xFFF807": {
|
||||
"label": "MedGray",
|
||||
"pos": "5x4",
|
||||
"cmnt": "Cycle palette +",
|
||||
"cmd": "FP=~"
|
||||
},
|
||||
"0xFF18E7": {
|
||||
"label": "Yellow",
|
||||
"pos": "6x1",
|
||||
"cmd": "FP=5&CL=hFFFF00&C2=h7FFF00&C3=hA89539"
|
||||
},
|
||||
"0xFF9867": {
|
||||
"label": "DarkCyan",
|
||||
"pos": "6x2",
|
||||
"cmd": "FP=5&CL=h8B8B&C2=h458B&C3=h1F5B51"
|
||||
},
|
||||
"0xFF58A7": {
|
||||
"label": "Plum",
|
||||
"pos": "6x3",
|
||||
"cmnt": "Magenta",
|
||||
"cmd": "FP=40"
|
||||
},
|
||||
"0xFFD827": {
|
||||
"label": "DarkGray",
|
||||
"pos": "6x4",
|
||||
"cmnt": "Cycle palette -",
|
||||
"cmd": "FP=~-"
|
||||
},
|
||||
"0xFF28D7": {
|
||||
"label": "Jump3",
|
||||
"pos": "7x1",
|
||||
"cmnt": "Colortwinkles",
|
||||
"cmd": "CY=0&FX=74"
|
||||
},
|
||||
"0xFFA857": {
|
||||
"label": "Fade3",
|
||||
"pos": "7x2",
|
||||
"cmnt": "Rain",
|
||||
"cmd": "CY=0&FX=43"
|
||||
},
|
||||
"0xFF6897": {
|
||||
"label": "Flash",
|
||||
"pos": "7x3",
|
||||
"cmnt": "Cycle Effects",
|
||||
"cmd": "CY=0&FX=~"
|
||||
},
|
||||
"0xFFE817": {
|
||||
"label": "Quick",
|
||||
"pos": "7x4",
|
||||
"cmnt": "Fx speed +16",
|
||||
"cmd": "SX=~16"
|
||||
},
|
||||
"0xFF08F7": {
|
||||
"label": "Jump7",
|
||||
"pos": "8x1",
|
||||
"cmnt": "Sinelon Dual",
|
||||
"cmd": "CY=0&FX=93"
|
||||
},
|
||||
"0xFF8877": {
|
||||
"label": "Fade7",
|
||||
"pos": "8x2",
|
||||
"cmnt": "Lighthouse",
|
||||
"cmd": "CY=0&FX=41"
|
||||
},
|
||||
"0xFF48B7": {
|
||||
"label": "Auto",
|
||||
"pos": "8x3",
|
||||
"cmnt": "Toggle preset cycle",
|
||||
"cmd": "CY=2"
|
||||
},
|
||||
"0xFFC837": {
|
||||
"label": "Slow",
|
||||
"pos": "8x4",
|
||||
"cmnt": "FX speed -16",
|
||||
"cmd": "SX=~-16"
|
||||
},
|
||||
"0xFF30CF": {
|
||||
"label": "Custom1",
|
||||
"pos": "9x1",
|
||||
"cmnt": "Noise 1",
|
||||
"cmd": "CY=0&FX=70"
|
||||
},
|
||||
"0xFFB04F": {
|
||||
"label": "Custom2",
|
||||
"pos": "9x2",
|
||||
"cmnt": "Dancing Shadows",
|
||||
"cmd": "CY=0&FX=112"
|
||||
},
|
||||
"0xFF708F": {
|
||||
"label": "Music +",
|
||||
"pos": "9x3",
|
||||
"cmnt": "FX Intensity +16",
|
||||
"cmd": "IX=~16"
|
||||
},
|
||||
"0xFFF00F": {
|
||||
"label": "Timer60",
|
||||
"pos": "9x4",
|
||||
"cmnt": "Timer 60 min",
|
||||
"cmd": "NL=60&NT=0"
|
||||
},
|
||||
"0xFF10EF": {
|
||||
"label": "Custom3",
|
||||
"pos": "10x1",
|
||||
"cmnt": "Twinklefox",
|
||||
"cmd": "CY=0&FX=80"
|
||||
},
|
||||
"0xFF906F": {
|
||||
"label": "Custom4",
|
||||
"pos": "10x2",
|
||||
"cmnt": "Twinklecat",
|
||||
"cmd": "CY=0&FX=81"
|
||||
},
|
||||
"0xFF50AF": {
|
||||
"label": "Music -",
|
||||
"pos": "10x3",
|
||||
"cmnt": "FX Intesity -16",
|
||||
"cmd": "IX=~-16"
|
||||
},
|
||||
"0xFFD02F": {
|
||||
"label": "Timer120",
|
||||
"pos": "10x4",
|
||||
"cmnt": "Timer 120 min",
|
||||
"cmd": "NL=120&NT=0"
|
||||
}
|
||||
}
|
217
usermods/JSON_IR_remote/40-key-blue_ir.json
Normal file
217
usermods/JSON_IR_remote/40-key-blue_ir.json
Normal file
@@ -0,0 +1,217 @@
|
||||
{
|
||||
"desc": "40-key-blue",
|
||||
"0xFF3AC5": {
|
||||
"label": "Bright +",
|
||||
"pos": "1x1",
|
||||
"cmd": "A=~16"
|
||||
},
|
||||
"0xFFBA45": {
|
||||
"label": "Bright -",
|
||||
"pos": "1x2",
|
||||
"cmd": "A=~-16"
|
||||
},
|
||||
"0xFF827D": {
|
||||
"label": "Off",
|
||||
"pos": "1x3",
|
||||
"cmd": "T=0"
|
||||
},
|
||||
"0xFF02FD": {
|
||||
"label": "On",
|
||||
"pos": "1x4",
|
||||
"cmd": "T=1"
|
||||
},
|
||||
"0xFF1AE5": {
|
||||
"label": "Red",
|
||||
"pos": "2x1",
|
||||
"cmnt": "Lava",
|
||||
"cmd": "FP=8"
|
||||
},
|
||||
"0xFF9A65": {
|
||||
"label": "Green",
|
||||
"pos": "2x2",
|
||||
"cmnt": "Forest",
|
||||
"cmd": "FP=10"
|
||||
},
|
||||
"0xFFA25D": {
|
||||
"label": "Blue",
|
||||
"pos": "2x3",
|
||||
"cmnt": "Breeze",
|
||||
"cmd": "FP=15"
|
||||
},
|
||||
"0xFF22DD": {
|
||||
"label": "White",
|
||||
"pos": "2x4",
|
||||
"cmd": "FP=5&CL=hFFFFFF&C2=hFFFFFF&C3=hA8A8A8"
|
||||
},
|
||||
"0xFF2AD5": {
|
||||
"label": "Tomato",
|
||||
"pos": "3x1",
|
||||
"cmnt": "Yelmag",
|
||||
"cmd": "FP=5&CL=hFF6347&C2=hFFBF47&C3=hA85859"
|
||||
},
|
||||
"0xFFAA55": {
|
||||
"label": "LightGreen",
|
||||
"pos": "3x2",
|
||||
"cmnt": "Rivendale",
|
||||
"cmd": "FP=14"
|
||||
},
|
||||
"0xFF926D": {
|
||||
"label": "SkyBlue",
|
||||
"pos": "3x3",
|
||||
"cmnt": "Ocean",
|
||||
"cmd": "FP=9"
|
||||
},
|
||||
"0xFF12ED": {
|
||||
"label": "WarmWhite",
|
||||
"pos": "3x4",
|
||||
"cmnt": "Warm White",
|
||||
"cmd": "FP=5&CL=hFFE4CD&C2=hFFFCCD&C3=hA89892"
|
||||
},
|
||||
"0xFF0AF5": {
|
||||
"label": "OrangeRed",
|
||||
"pos": "4x1",
|
||||
"cmnt": "Sakura",
|
||||
"cmd": "FP=49"
|
||||
},
|
||||
"0xFF8A75": {
|
||||
"label": "Cyan",
|
||||
"pos": "4x2",
|
||||
"cmnt": "Beech",
|
||||
"cmd": "FP=22"
|
||||
},
|
||||
"0xFFB24D": {
|
||||
"label": "RebeccaPurple",
|
||||
"pos": "4x3",
|
||||
"cmd": "FP=5&CL=h663399&C2=h993399&C3=h473864"
|
||||
},
|
||||
"0xFF32CD": {
|
||||
"label": "CoolWhite",
|
||||
"pos": "4x4",
|
||||
"cmnt": "Cool White",
|
||||
"cmd": "FP=5&CL=hE4E4FF&C2=hF1E4FF&C3=h9C9EA8"
|
||||
},
|
||||
"0xFF38C7": {
|
||||
"label": "Orange",
|
||||
"pos": "5x1",
|
||||
"cmnt": "Orangery",
|
||||
"cmd": "FP=47"
|
||||
},
|
||||
"0xFFB847": {
|
||||
"label": "Turquoise",
|
||||
"pos": "5x2",
|
||||
"cmd": "FP=5&CL=h40E0D0&C2=h40A0E0&C3=h4E9381"
|
||||
},
|
||||
"0xFF7887": {
|
||||
"label": "Purple",
|
||||
"pos": "5x3",
|
||||
"cmd": "FP=5&CL=h800080&C2=h800040&C3=h4B1C54"
|
||||
},
|
||||
"0xFFF807": {
|
||||
"label": "MedGray",
|
||||
"pos": "5x4",
|
||||
"cmnt": "Cycle palette +",
|
||||
"cmd": "FP=~"
|
||||
},
|
||||
"0xFF18E7": {
|
||||
"label": "Yellow",
|
||||
"pos": "6x1",
|
||||
"cmd": "FP=5&CL=hFFFF00&C2=h7FFF00&C3=hA89539"
|
||||
},
|
||||
"0xFF9867": {
|
||||
"label": "DarkCyan",
|
||||
"pos": "6x2",
|
||||
"cmd": "FP=5&CL=h8B8B&C2=h458B&C3=h1F5B51"
|
||||
},
|
||||
"0xFF58A7": {
|
||||
"label": "Plum",
|
||||
"pos": "6x3",
|
||||
"cmnt": "Magenta",
|
||||
"cmd": "FP=40"
|
||||
},
|
||||
"0xFFD827": {
|
||||
"label": "DarkGray",
|
||||
"pos": "6x4",
|
||||
"cmnt": "Cycle palette -",
|
||||
"cmd": "FP=~-"
|
||||
},
|
||||
"0xFF28D7": {
|
||||
"label": "W +",
|
||||
"pos": "7x1"
|
||||
},
|
||||
"0xFFA857": {
|
||||
"label": "W -",
|
||||
"pos": "7x2"
|
||||
},
|
||||
"0xFF6897": {
|
||||
"label": "W On",
|
||||
"pos": "7x3"
|
||||
},
|
||||
"0xFFE817": {
|
||||
"label": "W Off",
|
||||
"pos": "7x4"
|
||||
},
|
||||
"0xFF08F7": {
|
||||
"label": "W25",
|
||||
"pos": "8x1"
|
||||
},
|
||||
"0xFF8877": {
|
||||
"label": "W50",
|
||||
"pos": "8x2"
|
||||
},
|
||||
"0xFF48B7": {
|
||||
"label": "W75",
|
||||
"pos": "8x3"
|
||||
},
|
||||
"0xFFC837": {
|
||||
"label": "W100",
|
||||
"pos": "8x4"
|
||||
},
|
||||
"0xFF30CF": {
|
||||
"label": "Jump3",
|
||||
"pos": "9x1",
|
||||
"cmnt": "Colortwinkles",
|
||||
"cmd": "CY=0&FX=74"
|
||||
},
|
||||
"0xFFB04F": {
|
||||
"label": "Fade3",
|
||||
"pos": "9x2",
|
||||
"cmnt": "Rain",
|
||||
"cmd": "CY=0&FX=43"
|
||||
},
|
||||
"0xFF708F": {
|
||||
"label": "Jump7",
|
||||
"pos": "9x3",
|
||||
"cmnt": "Sinelon Dual",
|
||||
"cmd": "CY=0&FX=93"
|
||||
},
|
||||
"0xFFF00F": {
|
||||
"label": "Quick",
|
||||
"pos": "9x4",
|
||||
"cmnt": "Fx speed +16",
|
||||
"cmd": "SX=~16"
|
||||
},
|
||||
"0xFF10EF": {
|
||||
"label": "Fade",
|
||||
"pos": "10x1",
|
||||
"cmnt": "Lighthouse",
|
||||
"cmd": "CY=0&FX=41"
|
||||
},
|
||||
"0xFF906F": {
|
||||
"label": "Flash",
|
||||
"pos": "10x2",
|
||||
"cmnt": "Cycle Effects",
|
||||
"cmd": "CY=0&FX=~"
|
||||
},
|
||||
"0xFF50AF": {
|
||||
"label": "Auto",
|
||||
"pos": "10x3",
|
||||
"cmnt": "Toggle preset cycle",
|
||||
"cmd": "CY=2"
|
||||
},
|
||||
"0xFFD02F": {
|
||||
"label": "Slow",
|
||||
"pos": "10x4",
|
||||
"cmnt": "Sinelon Dual",
|
||||
"cmd": "CY=0&FX=93"
|
||||
}
|
||||
}
|
241
usermods/JSON_IR_remote/44-key_ir.json
Normal file
241
usermods/JSON_IR_remote/44-key_ir.json
Normal file
@@ -0,0 +1,241 @@
|
||||
{
|
||||
"desc": "44-key",
|
||||
"0xFF3AC5": {
|
||||
"label": "Bright +",
|
||||
"pos": "1x1",
|
||||
"cmd": "A=~16"
|
||||
},
|
||||
"0xFFBA45": {
|
||||
"label": "Bright -",
|
||||
"pos": "1x2",
|
||||
"cmd": "A=~-16"
|
||||
},
|
||||
"0xFF827D": {
|
||||
"label": "Off",
|
||||
"pos": "1x3",
|
||||
"cmd": "T=0"
|
||||
},
|
||||
"0xFF02FD": {
|
||||
"label": "On",
|
||||
"pos": "1x4",
|
||||
"cmd": "T=1"
|
||||
},
|
||||
"0xFF1AE5": {
|
||||
"label": "Red",
|
||||
"pos": "2x1",
|
||||
"cmnt": "Lava",
|
||||
"cmd": "FP=8"
|
||||
},
|
||||
"0xFF9A65": {
|
||||
"label": "Green",
|
||||
"pos": "2x2",
|
||||
"cmnt": "Forest",
|
||||
"cmd": "FP=10"
|
||||
},
|
||||
"0xFFA25D": {
|
||||
"label": "Blue",
|
||||
"pos": "2x3",
|
||||
"cmnt": "Breeze",
|
||||
"cmd": "FP=15"
|
||||
},
|
||||
"0xFF22DD": {
|
||||
"label": "White",
|
||||
"pos": "2x4",
|
||||
"cmd": "FP=5&CL=hFFFFFF&C2=hFFFFFF&C3=hA8A8A8"
|
||||
},
|
||||
"0xFF2AD5": {
|
||||
"label": "Tomato",
|
||||
"pos": "3x1",
|
||||
"cmd": "FP=5&CL=hFF6347&C2=hFFBF47&C3=hA85859"
|
||||
},
|
||||
"0xFFAA55": {
|
||||
"label": "LightGreen",
|
||||
"pos": "3x2",
|
||||
"cmnt": "Rivendale",
|
||||
"cmd": "FP=14"
|
||||
},
|
||||
"0xFF926D": {
|
||||
"label": "DeepBlue",
|
||||
"pos": "3x3",
|
||||
"cmnt": "Ocean",
|
||||
"cmd": "FP=9"
|
||||
},
|
||||
"0xFF12ED": {
|
||||
"label": "Warmwhite2",
|
||||
"pos": "3x4",
|
||||
"cmnt": "Warm White",
|
||||
"cmd": "FP=5&CL=hFFE4CD&C2=hFFFCCD&C3=hA89892"
|
||||
},
|
||||
"0xFF0AF5": {
|
||||
"label": "Orange",
|
||||
"pos": "4x1",
|
||||
"cmnt": "Sakura",
|
||||
"cmd": "FP=49"
|
||||
},
|
||||
"0xFF8A75": {
|
||||
"label": "Turquoise",
|
||||
"pos": "4x2",
|
||||
"cmnt": "Beech",
|
||||
"cmd": "FP=22"
|
||||
},
|
||||
"0xFFB24D": {
|
||||
"label": "Purple",
|
||||
"pos": "4x3",
|
||||
"cmd": "FP=5&CL=h663399&C2=h993399&C3=h473864"
|
||||
},
|
||||
"0xFF32CD": {
|
||||
"label": "WarmWhite",
|
||||
"pos": "4x4",
|
||||
"cmd": "FP=5&CL=hE4E4FF&C2=hF1E4FF&C3=h9C9EA8"
|
||||
},
|
||||
"0xFF38C7": {
|
||||
"label": "Yellowish",
|
||||
"pos": "5x1",
|
||||
"cmnt": "Orangery",
|
||||
"cmd": "FP=47"
|
||||
},
|
||||
"0xFFB847": {
|
||||
"label": "Cyan",
|
||||
"pos": "5x2",
|
||||
"cmnt": "Beech",
|
||||
"cmd": "FP=22"
|
||||
},
|
||||
"0xFF7887": {
|
||||
"label": "Magenta",
|
||||
"pos": "5x3",
|
||||
"cmd": "FP=5&CL=hFF00FF&C2=hFF007F&C3=h9539A8"
|
||||
},
|
||||
"0xFFF807": {
|
||||
"label": "ColdWhite",
|
||||
"pos": "5x4",
|
||||
"cmd": "FP=5&CL=hE4E4FF&C2=hF1E4FF&C3=h9C9EA8"
|
||||
},
|
||||
"0xFF18E7": {
|
||||
"label": "Yellow",
|
||||
"pos": "6x1",
|
||||
"cmd": "FP=5&CL=hFFFF00&C2=hFFC800&C3=hFDFFDE"
|
||||
},
|
||||
"0xFF9867": {
|
||||
"label": "Aqua",
|
||||
"pos": "6x2",
|
||||
"cmd": "FP=5&CL=hFFFF&C2=h7FFF&C3=h39A895"
|
||||
},
|
||||
"0xFF58A7": {
|
||||
"label": "Pink",
|
||||
"pos": "6x3",
|
||||
"cmd": "FP=5&CL=hFFC0CB&C2=hFFD4C0&C3=hA88C96"
|
||||
},
|
||||
"0xFFD827": {
|
||||
"label": "ColdWhite2",
|
||||
"pos": "6x4",
|
||||
"cmd": "FP=5&CL=hE4E4FF&C2=hF1E4FF&C3=h9C9EA8"
|
||||
},
|
||||
"0xFF28D7": {
|
||||
"label": "Red +",
|
||||
"pos": "7x1",
|
||||
"cmd": "FP=5&R=~16"
|
||||
},
|
||||
"0xFFA857": {
|
||||
"label": "Green +",
|
||||
"pos": "7x2",
|
||||
"cmd": "FP=5&G=~16"
|
||||
},
|
||||
"0xFF6897": {
|
||||
"label": "Blue +",
|
||||
"pos": "7x3",
|
||||
"cmd": "FP=5&B=~16"
|
||||
},
|
||||
"0xFFE817": {
|
||||
"label": "Quick",
|
||||
"pos": "7x4",
|
||||
"cmnt": "Fx speed +16",
|
||||
"cmd": "SX=~16"
|
||||
},
|
||||
"0xFF08F7": {
|
||||
"label": "Red -",
|
||||
"pos": "8x1",
|
||||
"cmd": "FP=5&R=~-16"
|
||||
},
|
||||
"0xFF8877": {
|
||||
"label": "Green -",
|
||||
"pos": "8x2",
|
||||
"cmd": "FP=5&G=~-16"
|
||||
},
|
||||
"0xFF48B7": {
|
||||
"label": "Blue -",
|
||||
"pos": "8x3",
|
||||
"cmd": "FP=5&B=~-16"
|
||||
},
|
||||
"0xFFC837": {
|
||||
"label": "Slow",
|
||||
"pos": "8x4",
|
||||
"cmnt": "FX speed -16",
|
||||
"cmd": "SX=~-16"
|
||||
},
|
||||
"0xFF30CF": {
|
||||
"label": "Diy1",
|
||||
"pos": "9x1",
|
||||
"cmd": "CY=0&PL=1"
|
||||
},
|
||||
"0xFFB04F": {
|
||||
"label": "Diy2",
|
||||
"pos": "9x2",
|
||||
"cmd": "CY=0&PL=2"
|
||||
},
|
||||
"0xFF708F": {
|
||||
"label": "Diy3",
|
||||
"pos": "9x3",
|
||||
"cmd": "CY=0&PL=3"
|
||||
},
|
||||
"0xFFF00F": {
|
||||
"label": "Auto",
|
||||
"pos": "9x4",
|
||||
"cmnt": "Toggle preset cycle",
|
||||
"cmd": "CY=2"
|
||||
},
|
||||
"0xFF10EF": {
|
||||
"label": "Diy4",
|
||||
"pos": "10x1",
|
||||
"cmd": "CY=0&PL=4"
|
||||
},
|
||||
"0xFF906F": {
|
||||
"label": "Diy5",
|
||||
"pos": "10x2",
|
||||
"cmd": "CY=0&PL=5"
|
||||
},
|
||||
"0xFF50AF": {
|
||||
"label": "Diy6",
|
||||
"pos": "10x3",
|
||||
"cmd": "CY=0&PL=6"
|
||||
},
|
||||
"0xFFD02F": {
|
||||
"label": "Flash",
|
||||
"pos": "10x4",
|
||||
"cmnt": "Cycle Effects",
|
||||
"cmd": "CY=0&FX=~"
|
||||
},
|
||||
"0xFF20DF": {
|
||||
"label": "Jump3",
|
||||
"pos": "11x1",
|
||||
"cmnt": "Colortwinkles",
|
||||
"cmd": "CY=0&FX=74"
|
||||
},
|
||||
"0xFFA05F": {
|
||||
"label": "Jump7",
|
||||
"pos": "11x2",
|
||||
"cmnt": "Sinelon Dual",
|
||||
"cmd": "CY=0&FX=93"
|
||||
},
|
||||
"0xFF609F": {
|
||||
"label": "Fade3",
|
||||
"pos": "11x3",
|
||||
"cmnt": "Rain",
|
||||
"cmd": "CY=0&FX=43"
|
||||
},
|
||||
"0xFFE01F": {
|
||||
"label": "Fade7",
|
||||
"pos": "11x4",
|
||||
"cmnt": "Lighthouse",
|
||||
"cmd": "CY=0&FX=41"
|
||||
}
|
||||
}
|
38
usermods/JSON_IR_remote/6-key_ir.json
Normal file
38
usermods/JSON_IR_remote/6-key_ir.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"desc": "6-key",
|
||||
"0xFF0FF0": {
|
||||
"label": "Power",
|
||||
"pos": "1x1",
|
||||
"cmd": "T=2"
|
||||
},
|
||||
"0xFF8F70": {
|
||||
"label": "Channel +",
|
||||
"pos": "2x1",
|
||||
"cmnt": "Cycle palette up",
|
||||
"cmd": "FP=~"
|
||||
},
|
||||
"0xFF4FB0": {
|
||||
"label": "Channel -",
|
||||
"pos": "3x1",
|
||||
"cmnt": "Cycle palette down",
|
||||
"cmd": "FP=~-"
|
||||
},
|
||||
"0xFFCF30": {
|
||||
"label": "Volume +",
|
||||
"pos": "4x1",
|
||||
"cmnt": "Brighten",
|
||||
"cmd": "A=~16"
|
||||
},
|
||||
"0xFF2FD0": {
|
||||
"label": "Volume -",
|
||||
"pos": "5x1",
|
||||
"cmnt": "Dim",
|
||||
"cmd": "A=~-16"
|
||||
},
|
||||
"0xFFAF50": {
|
||||
"label": "Mute",
|
||||
"pos": "6x1",
|
||||
"cmnt": "Cycle effects",
|
||||
"cmd": "CY=0&FX=~"
|
||||
}
|
||||
}
|
47
usermods/JSON_IR_remote/9-key_ir.json
Normal file
47
usermods/JSON_IR_remote/9-key_ir.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"desc": "9-key",
|
||||
"0xFF629D": {
|
||||
"label": "Power",
|
||||
"cmd": "T=2"
|
||||
},
|
||||
"0xFF22DD": {
|
||||
"label": "A",
|
||||
"cmnt": "Preset 1",
|
||||
"cmd": "PL=1"
|
||||
},
|
||||
"0xFF02FD": {
|
||||
"label": "B",
|
||||
"cmnt": "Preset 2",
|
||||
"cmd": "PL=2"
|
||||
},
|
||||
"0xFFC23D": {
|
||||
"label": "C",
|
||||
"cmnt": "Preset 3",
|
||||
"cmd": "PL=3"
|
||||
},
|
||||
"0xFF30CF": {
|
||||
"label": "Left",
|
||||
"cmnt": "Speed -",
|
||||
"cmd": "SI=~-16"
|
||||
},
|
||||
"0xFF7A85": {
|
||||
"label": "Right",
|
||||
"cmnt": "Speed +",
|
||||
"cmd": "SI=~16"
|
||||
},
|
||||
"0xFF9867": {
|
||||
"label": "Up",
|
||||
"cmnt": "Bright +",
|
||||
"cmd": "A=~16"
|
||||
},
|
||||
"0xFF38C7": {
|
||||
"label": "Down",
|
||||
"cmnt": "Bright -",
|
||||
"cmd": "A=~-16"
|
||||
},
|
||||
"0xFF18E7": {
|
||||
"label": "Select",
|
||||
"cmnt": "Cycle effects",
|
||||
"cmd": "CY=0&FX=~"
|
||||
}
|
||||
}
|
BIN
usermods/JSON_IR_remote/IR_Remote_Codes.xlsx
Normal file
BIN
usermods/JSON_IR_remote/IR_Remote_Codes.xlsx
Normal file
Binary file not shown.
108
usermods/JSON_IR_remote/ir_json_maker.py
Normal file
108
usermods/JSON_IR_remote/ir_json_maker.py
Normal file
@@ -0,0 +1,108 @@
|
||||
import colorsys
|
||||
import json
|
||||
import openpyxl
|
||||
|
||||
named_colors = {'AliceBlue': '0xF0F8FF', 'AntiqueWhite': '0xFAEBD7', 'Aqua': '0x00FFFF',
|
||||
'Aquamarine': '0x7FFFD4', 'Azure': '0xF0FFFF', 'Beige': '0xF5F5DC', 'Bisque': '0xFFE4C4',
|
||||
'Black': '0x000000', 'BlanchedAlmond': '0xFFEBCD', 'Blue': '0x0000FF',
|
||||
'BlueViolet': '0x8A2BE2', 'Brown': '0xA52A2A', 'BurlyWood': '0xDEB887',
|
||||
'CadetBlue': '0x5F9EA0', 'Chartreuse': '0x7FFF00', 'Chocolate': '0xD2691E',
|
||||
'Coral': '0xFF7F50', 'CornflowerBlue': '0x6495ED', 'Cornsilk': '0xFFF8DC',
|
||||
'Crimson': '0xDC143C', 'Cyan': '0x00FFFF', 'DarkBlue': '0x00008B', 'DarkCyan': '0x008B8B',
|
||||
'DarkGoldenRod': '0xB8860B', 'DarkGray': '0xA9A9A9', 'DarkGrey': '0xA9A9A9',
|
||||
'DarkGreen': '0x006400', 'DarkKhaki': '0xBDB76B', 'DarkMagenta': '0x8B008B',
|
||||
'DarkOliveGreen': '0x556B2F', 'DarkOrange': '0xFF8C00', 'DarkOrchid': '0x9932CC',
|
||||
'DarkRed': '0x8B0000', 'DarkSalmon': '0xE9967A', 'DarkSeaGreen': '0x8FBC8F',
|
||||
'DarkSlateBlue': '0x483D8B', 'DarkSlateGray': '0x2F4F4F', 'DarkSlateGrey': '0x2F4F4F',
|
||||
'DarkTurquoise': '0x00CED1', 'DarkViolet': '0x9400D3', 'DeepPink': '0xFF1493',
|
||||
'DeepSkyBlue': '0x00BFFF', 'DimGray': '0x696969', 'DimGrey': '0x696969',
|
||||
'DodgerBlue': '0x1E90FF', 'FireBrick': '0xB22222', 'FloralWhite': '0xFFFAF0',
|
||||
'ForestGreen': '0x228B22', 'Fuchsia': '0xFF00FF', 'Gainsboro': '0xDCDCDC',
|
||||
'GhostWhite': '0xF8F8FF', 'Gold': '0xFFD700', 'GoldenRod': '0xDAA520', 'Gray': '0x808080',
|
||||
'Grey': '0x808080', 'Green': '0x008000', 'GreenYellow': '0xADFF2F', 'HoneyDew': '0xF0FFF0',
|
||||
'HotPink': '0xFF69B4', 'IndianRed': '0xCD5C5C', 'Indigo': '0x4B0082', 'Ivory': '0xFFFFF0',
|
||||
'Khaki': '0xF0E68C', 'Lavender': '0xE6E6FA', 'LavenderBlush': '0xFFF0F5',
|
||||
'LawnGreen': '0x7CFC00', 'LemonChiffon': '0xFFFACD', 'LightBlue': '0xADD8E6',
|
||||
'LightCoral': '0xF08080', 'LightCyan': '0xE0FFFF', 'LightGoldenRodYellow': '0xFAFAD2',
|
||||
'LightGray': '0xD3D3D3', 'LightGrey': '0xD3D3D3', 'LightGreen': '0x90EE90',
|
||||
'LightPink': '0xFFB6C1', 'LightSalmon': '0xFFA07A', 'LightSeaGreen': '0x20B2AA',
|
||||
'LightSkyBlue': '0x87CEFA', 'LightSlateGray': '0x778899', 'LightSlateGrey': '0x778899',
|
||||
'LightSteelBlue': '0xB0C4DE', 'LightYellow': '0xFFFFE0', 'Lime': '0x00FF00',
|
||||
'LimeGreen': '0x32CD32', 'Linen': '0xFAF0E6', 'Magenta': '0xFF00FF', 'Maroon': '0x800000',
|
||||
'MediumAquaMarine': '0x66CDAA', 'MediumBlue': '0x0000CD', 'MediumOrchid': '0xBA55D3',
|
||||
'MediumPurple': '0x9370DB', 'MediumSeaGreen': '0x3CB371', 'MediumSlateBlue': '0x7B68EE',
|
||||
'MediumSpringGreen': '0x00FA9A', 'MediumTurquoise': '0x48D1CC', 'MediumVioletRed': '0xC71585',
|
||||
'MidnightBlue': '0x191970', 'MintCream': '0xF5FFFA', 'MistyRose': '0xFFE4E1',
|
||||
'Moccasin': '0xFFE4B5', 'NavajoWhite': '0xFFDEAD', 'Navy': '0x000080', 'OldLace': '0xFDF5E6',
|
||||
'Olive': '0x808000', 'OliveDrab': '0x6B8E23', 'Orange': '0xFFA500', 'OrangeRed': '0xFF4500',
|
||||
'Orchid': '0xDA70D6', 'PaleGoldenRod': '0xEEE8AA', 'PaleGreen': '0x98FB98',
|
||||
'PaleTurquoise': '0xAFEEEE', 'PaleVioletRed': '0xDB7093', 'PapayaWhip': '0xFFEFD5',
|
||||
'PeachPuff': '0xFFDAB9', 'Peru': '0xCD853F', 'Pink': '0xFFC0CB', 'Plum': '0xDDA0DD',
|
||||
'PowderBlue': '0xB0E0E6', 'Purple': '0x800080', 'RebeccaPurple': '0x663399', 'Red': '0xFF0000',
|
||||
'RosyBrown': '0xBC8F8F', 'RoyalBlue': '0x4169E1', 'SaddleBrown': '0x8B4513', 'Salmon': '0xFA8072',
|
||||
'SandyBrown': '0xF4A460', 'SeaGreen': '0x2E8B57', 'SeaShell': '0xFFF5EE', 'Sienna': '0xA0522D',
|
||||
'Silver': '0xC0C0C0', 'SkyBlue': '0x87CEEB', 'SlateBlue': '0x6A5ACD', 'SlateGray': '0x708090',
|
||||
'SlateGrey': '0x708090', 'Snow': '0xFFFAFA', 'SpringGreen': '0x00FF7F', 'SteelBlue': '0x4682B4',
|
||||
'Tan': '0xD2B48C', 'Teal': '0x008080', 'Thistle': '0xD8BFD8', 'Tomato': '0xFF6347',
|
||||
'Turquoise': '0x40E0D0', 'Violet': '0xEE82EE', 'Wheat': '0xF5DEB3', 'White': '0xFFFFFF',
|
||||
'WhiteSmoke': '0xF5F5F5', 'Yellow': '0xFFFF00', 'YellowGreen': '0x9ACD32'}
|
||||
|
||||
def shift_color(col, shift=30, sat=1.0, val=1.0):
|
||||
r = (col & (255 << 16)) >> 16
|
||||
g = (col & (255 << 8)) >> 8
|
||||
b = col & 255
|
||||
hsv = colorsys.rgb_to_hsv(r, g, b)
|
||||
h = (((hsv[0] * 360) + shift) % 360) / 360
|
||||
rgb = colorsys.hsv_to_rgb(h, hsv[1] * sat, hsv[2] * val)
|
||||
return (int(rgb[0]) << 16) + (int(rgb[1]) << 8) + int(rgb[2])
|
||||
|
||||
def parse_sheet(ws):
|
||||
print(f'Parsing worksheet {ws.title}')
|
||||
ir = {"desc": ws.title}
|
||||
rows = ws.rows
|
||||
keys = [col.value.lower() for col in next(rows)]
|
||||
for row in rows:
|
||||
rec = dict(zip(keys, [col.value for col in row]))
|
||||
if rec.get('code') is None:
|
||||
continue
|
||||
cd = {"label": rec.get('label')}
|
||||
if rec.get('row'):
|
||||
cd['pos'] = f'{rec["row"]}x{rec["col"]}'
|
||||
if rec.get('comment'):
|
||||
cd['cmnt'] = rec.get('comment')
|
||||
if rec.get('rpt'):
|
||||
cd['rpt'] = bool(rec['rpt'])
|
||||
|
||||
if rec.get('cmd'):
|
||||
cd['cmd'] = rec['cmd']
|
||||
elif all((rec.get('primary'), rec.get('secondary'), rec.get('tertiary'))):
|
||||
c1 = int(rec.get('primary'), 16)
|
||||
c2 = int(rec.get('secondary'), 16)
|
||||
c3 = int(rec.get('tertiary'), 16)
|
||||
cd['cmd'] = f'FP=5&CL=h{c1:X}&C2=h{c2:X}&C3=h{c3:X}'
|
||||
elif all((rec.get('primary'), rec.get('secondary'))):
|
||||
c1 = int(rec.get('primary'), 16)
|
||||
c2 = int(rec.get('secondary'), 16)
|
||||
c3 = shift_color(c1, -1, sat=0.66, val=0.66)
|
||||
cd['cmd'] = f'FP=5&CL=h{c1:X}&C2=h{c2:X}&C3=h{c3:X}'
|
||||
elif rec.get('primary'):
|
||||
c1 = int(rec.get('primary'), 16)
|
||||
c2 = shift_color(c1, 30)
|
||||
c3 = shift_color(c1, -10, sat=0.66, val=0.66)
|
||||
cd['cmd'] = f'FP=5&CL=h{c1:X}&C2=h{c2:X}&C3=h{c3:X}'
|
||||
elif rec.get('label') in named_colors:
|
||||
c1 = int(named_colors[rec.get('label')], 16)
|
||||
c2 = shift_color(c1, 30)
|
||||
c3 = shift_color(c1, -10, sat=0.66, val=0.66)
|
||||
cd['cmd'] = f'FP=5&CL=h{c1:X}&C2=h{c2:X}&C3=h{c3:X}'
|
||||
else:
|
||||
print(f'Did not find a command or color for {rec["label"]}. Hint use named CSS colors as labels')
|
||||
ir[rec['code']] = cd
|
||||
|
||||
with open(f'{ws.title}_ir.json', 'w') as fp:
|
||||
json.dump(ir, fp, indent=2)
|
||||
|
||||
if __name__ == '__main__':
|
||||
wb = openpyxl.load_workbook('IR_Remote_Codes.xlsx')
|
||||
for ws in wb.worksheets:
|
||||
parse_sheet(ws)
|
33
usermods/JSON_IR_remote/readme.md
Normal file
33
usermods/JSON_IR_remote/readme.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# JSON IR remote
|
||||
|
||||
## Purpose
|
||||
|
||||
The JSON IR remote allows users to customize IR remote behavior without writing custom code and compiling.
|
||||
It also enables using any remote that is compatible with your IR receiver. Using the JSON IR remote, you can
|
||||
map buttons from any remote to any HTTP request API or JSON API command.
|
||||
|
||||
## Usage
|
||||
|
||||
* Upload the IR config file, named _ir.json_ to your board using the [ip address]/edit url. Pick from one of the included files or create your own.
|
||||
* On the config > LED settings page, set the correct IR pin.
|
||||
* On the config > Sync Interfaces page, select "JSON Remote" as the Infrared remote.
|
||||
|
||||
## Modification
|
||||
|
||||
* See if there is a json file with the same number of buttons as your remote. Many remotes will have the same internals and emit the same codes but have different labels.
|
||||
* In the ir.json file, each key will be the hex encoded IR code.
|
||||
* The "cmd" property will be the HTTP Request API or JSON API to execute when that button is pressed.
|
||||
* A limited number of c functions are supported (!incBrightness, !decBrightness, !presetFallback)
|
||||
* When using !presetFallback, include properties PL (preset to load), FX (effect to fall back to) and FP (palette to fall back to)
|
||||
* If the command is _repeatable_ and does not contain the "~" character, add a "rpt": true property.
|
||||
* Other properties are ignored, but having a label property may help when editing.
|
||||
|
||||
|
||||
Sample:
|
||||
{
|
||||
"0xFF629D": {"cmd": "T=2", "rpt": true, "label": "Toggle on/off"}, // HTTP command
|
||||
"0xFF9867": {"cmd": "A=~16", "label": "Inc brightness"}, // HTTP command with incrementing
|
||||
"0xFF38C7": {"cmd": {"bri": 10}, "label": "Dim to 10"}, // JSON command
|
||||
"0xFF22DD": {"cmd": "!presetFallback", "PL": 1, "FX": 16, "FP": 6,
|
||||
"label": "Preset 1 or fallback to Saw - Party"}, // c function
|
||||
}
|
347
usermods/PIR_sensor_switch/PIR_Highlight_Standby
Normal file
347
usermods/PIR_sensor_switch/PIR_Highlight_Standby
Normal file
@@ -0,0 +1,347 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
/*
|
||||
* --------------------
|
||||
* Rawframe edit:
|
||||
* - TESTED ON WLED VS.0.10.1 - WHERE ONLY PRESET 16 SAVES SEGMENTS - some macros may not be needed if this changes.
|
||||
* - Code has been modified as my usage changed, as such it has poor use of functions vs if thens, but feel free to change it for me :)
|
||||
*
|
||||
* Edited to SWITCH between two lighting scenes/modes : STANDBY and HIGHLIGHT
|
||||
*
|
||||
* Usage:
|
||||
* - Standby is the default mode and Highlight is activated when the PIR detects activity.
|
||||
* - PIR delay now set to same value as Nightlight feature on boot but otherwise controlled as normal.
|
||||
* - Standby and Highlight brightness can be set on the fly (default values set on boot via macros calling presets).
|
||||
* - Macros are used to set Standby and Highlight states (macros can load saved presets etc).
|
||||
*
|
||||
* - Macro short button press = Highlight state default (used on boot only and sets default brightness).
|
||||
* - Macro double button press = Standby state default (used on boot only and sets default brightness).
|
||||
* - Macro long button press = Highlight state (after boot).
|
||||
* - Macro 16 = Standby state (after boot).
|
||||
*
|
||||
* ! It is advised not to set 'Apply preset at boot' or a boot macro (that activates a preset) as we will call our own macros on boot.
|
||||
*
|
||||
* - When the strip is off before PIR activates the strip will return to off for Standby mode, and vice versa.
|
||||
* - When the strip is turned off while in Highlight mode, it will return to standby mode. (This behaviour could be changed easily if for some reason you wanted the lights to go out when the pir is activated).
|
||||
* - Macros can be chained so you could do almost anything, such as have standby mode also turn on the nightlight function with a new time delay.
|
||||
*
|
||||
* Segment Notes:
|
||||
* - It's easier to save the segment selections in preset than apply via macro while we a limited to preset 16. (Ie, instead of selecting sections at the point of activating standby/highlight modes).
|
||||
* - Because only preset 16 saves segments, for now we are having to use addiotional macros to control segments where they are involved. Macros can be chained so this works but it would be better if macros also accepted json-api commands. (Testing http api segement behaviour of SS with SB left me a little confused).
|
||||
*
|
||||
* Future:
|
||||
* - Maybe a second timer/timetable that turns on/off standby mode also after set inactivity period / date & times. For now this can be achieved others ways so may not be worth eating more processing power.
|
||||
*
|
||||
* --------------------
|
||||
*
|
||||
* This usermod handles PIR sensor states.
|
||||
* The strip will be switched on and the off timer will be resetted when the sensor goes HIGH.
|
||||
* When the sensor state goes LOW, the off timer is started and when it expires, the strip is switched off.
|
||||
*
|
||||
*
|
||||
* Usermods allow you to add own functionality to WLED more easily
|
||||
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
|
||||
*
|
||||
* v2 usermods are class inheritance based and can (but don't have to) implement more functions, each of them is shown in this example.
|
||||
* Multiple v2 usermods can be added to one compilation easily.
|
||||
*
|
||||
* Creating a usermod:
|
||||
* This file serves as an example. If you want to create a usermod, it is recommended to use usermod_v2_empty.h from the usermods folder as a template.
|
||||
* Please remember to rename the class and file to a descriptive name.
|
||||
* You may also use multiple .h and .cpp files.
|
||||
*
|
||||
* Using a usermod:
|
||||
* 1. Copy the usermod into the sketch folder (same folder as wled00.ino)
|
||||
* 2. Register the usermod by adding #include "usermod_filename.h" in the top and registerUsermod(new MyUsermodClass()) in the bottom of usermods_list.cpp
|
||||
*/
|
||||
|
||||
class PIRsensorSwitch : public Usermod {
|
||||
private:
|
||||
// PIR sensor pin
|
||||
const uint8_t PIRsensorPin = 13; // D7 on D1 mini
|
||||
// notification mode for stateUpdated()
|
||||
const byte NotifyUpdateMode = CALL_MODE_NO_NOTIFY; // CALL_MODE_DIRECT_CHANGE
|
||||
// 1 min delay before switch off after the sensor state goes LOW
|
||||
uint32_t m_switchOffDelay = 60000;
|
||||
// off timer start time
|
||||
uint32_t m_offTimerStart = 0;
|
||||
// current PIR sensor pin state
|
||||
byte m_PIRsensorPinState = LOW;
|
||||
// PIR sensor enabled - ISR attached
|
||||
bool m_PIRenabled = true;
|
||||
// temp standby brightness store. initial value set as nightlight default target brightness
|
||||
byte briStandby _INIT(nightlightTargetBri);
|
||||
// temp hightlight brightness store. initial value set as current brightness
|
||||
byte briHighlight _INIT(bri);
|
||||
// highlight active/deactive monitor
|
||||
bool highlightActive = false;
|
||||
// wled on/off state in standby mode
|
||||
bool standbyoff = false;
|
||||
|
||||
/*
|
||||
* return or change if new PIR sensor state is available
|
||||
*/
|
||||
static volatile bool newPIRsensorState(bool changeState = false, bool newState = false) {
|
||||
static volatile bool s_PIRsensorState = false;
|
||||
if (changeState) {
|
||||
s_PIRsensorState = newState;
|
||||
}
|
||||
return s_PIRsensorState;
|
||||
}
|
||||
|
||||
/*
|
||||
* PIR sensor state has changed
|
||||
*/
|
||||
static void IRAM_ATTR ISR_PIRstateChange() {
|
||||
newPIRsensorState(true, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* switch strip on/off
|
||||
*/
|
||||
// now allowing adjustable standby and highlight brightness
|
||||
void switchStrip(bool switchOn) {
|
||||
//if (switchOn && bri == 0) {
|
||||
if (switchOn) { // **pir sensor is on and activated**
|
||||
//bri = briLast;
|
||||
if (bri != 0) { // is WLED currently on
|
||||
if (highlightActive) { // and is Highlight already on
|
||||
briHighlight = bri; // then update highlight brightness with current brightness
|
||||
}
|
||||
else {
|
||||
briStandby = bri; // else update standby brightness with current brightness
|
||||
}
|
||||
}
|
||||
else { // WLED is currently off
|
||||
if (!highlightActive) { // and Highlight is not already on
|
||||
briStandby = briLast; // then update standby brightness with last active brightness (before turned off)
|
||||
standbyoff = true;
|
||||
}
|
||||
else { // and Highlight is already on
|
||||
briHighlight = briLast; // then set hightlight brightness to last active brightness (before turned off)
|
||||
}
|
||||
}
|
||||
applyMacro(16); // apply highlight lighting without brightness
|
||||
if (bri != briHighlight) {
|
||||
bri = briHighlight; // set current highlight brightness to last set highlight brightness
|
||||
}
|
||||
stateUpdated(NotifyUpdateMode);
|
||||
highlightActive = true; // flag highlight is on
|
||||
}
|
||||
else { // **pir timer has elapsed**
|
||||
//briLast = bri;
|
||||
//bri = 0;
|
||||
if (bri != 0) { // is WLED currently on
|
||||
briHighlight = bri; // update highlight brightness with current brightness
|
||||
if (!standbyoff) { //
|
||||
bri = briStandby; // set standby brightness to last set standby brightness
|
||||
}
|
||||
else { //
|
||||
briLast = briStandby; // set standby off brightness
|
||||
bri = 0; // set power off in standby
|
||||
standbyoff = false; // turn off flag
|
||||
}
|
||||
applyMacro(macroLongPress); // apply standby lighting without brightness
|
||||
}
|
||||
else { // WLED is currently off
|
||||
briHighlight = briLast; // set last active brightness (before turned off) to highlight lighting brightness
|
||||
if (!standbyoff) { //
|
||||
bri = briStandby; // set standby brightness to last set standby brightness
|
||||
}
|
||||
else { //
|
||||
briLast = briStandby; // set standby off brightness
|
||||
bri = 0; // set power off in standby
|
||||
standbyoff = false; // turn off flag
|
||||
}
|
||||
applyMacro(macroLongPress); // apply standby lighting without brightness
|
||||
}
|
||||
stateUpdated(NotifyUpdateMode);
|
||||
highlightActive = false; // flag highlight is off
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read and update PIR sensor state.
|
||||
* Initilize/reset switch off timer
|
||||
*/
|
||||
bool updatePIRsensorState() {
|
||||
if (newPIRsensorState()) {
|
||||
m_PIRsensorPinState = digitalRead(PIRsensorPin);
|
||||
|
||||
if (m_PIRsensorPinState == HIGH) {
|
||||
m_offTimerStart = 0;
|
||||
switchStrip(true);
|
||||
}
|
||||
else if (bri != 0) {
|
||||
// start switch off timer
|
||||
m_offTimerStart = millis();
|
||||
}
|
||||
newPIRsensorState(true, false);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* switch off the strip if the delay has elapsed
|
||||
*/
|
||||
bool handleOffTimer() {
|
||||
if (m_offTimerStart > 0) {
|
||||
if ((millis() - m_offTimerStart > m_switchOffDelay) || bri == 0 ) { // now also checking for manual power off during highlight mode
|
||||
switchStrip(false);
|
||||
m_offTimerStart = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
//Functions called by WLED
|
||||
|
||||
/*
|
||||
* setup() is called once at boot. WiFi is not yet connected at this point.
|
||||
* You can use it to initialize variables, sensors or similar.
|
||||
*/
|
||||
void setup() {
|
||||
// PIR Sensor mode INPUT_PULLUP
|
||||
pinMode(PIRsensorPin, INPUT_PULLUP);
|
||||
// assign interrupt function and set CHANGE mode
|
||||
attachInterrupt(digitalPinToInterrupt(PIRsensorPin), ISR_PIRstateChange, CHANGE);
|
||||
// set delay to nightlight default duration on boot (after which json PIRoffSec overides if needed)
|
||||
m_switchOffDelay = (nightlightDelayMins*60000);
|
||||
applyMacro(macroButton); // apply default highlight lighting
|
||||
briHighlight = bri;
|
||||
applyMacro(macroDoublePress); // apply default standby lighting with brightness
|
||||
briStandby = bri;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* connected() is called every time the WiFi is (re)connected
|
||||
* Use it to initialize network interfaces
|
||||
*/
|
||||
void connected() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* loop() is called continuously. Here you can check for events, read sensors, etc.
|
||||
*/
|
||||
void loop() {
|
||||
if (!updatePIRsensorState()) {
|
||||
handleOffTimer();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
|
||||
*
|
||||
* Add PIR sensor state and switch off timer duration to jsoninfo
|
||||
*/
|
||||
void addToJsonInfo(JsonObject& root)
|
||||
{
|
||||
//this code adds "u":{"⏲ PIR sensor state":uiDomString} to the info object
|
||||
// the value contains a button to toggle the sensor enabled/disabled
|
||||
JsonObject user = root["u"];
|
||||
if (user.isNull()) user = root.createNestedObject("u");
|
||||
|
||||
JsonArray infoArr = user.createNestedArray("⏲ PIR sensor state"); //name
|
||||
String uiDomString = "<button class=\"btn infobtn\" onclick=\"requestJson({PIRenabled:";
|
||||
String sensorStateInfo;
|
||||
|
||||
// PIR sensor state
|
||||
if (m_PIRenabled) {
|
||||
uiDomString += "false";
|
||||
sensorStateInfo = (m_PIRsensorPinState != LOW ? "active" : "inactive"); //value
|
||||
} else {
|
||||
uiDomString += "true";
|
||||
sensorStateInfo = "Disabled !";
|
||||
}
|
||||
uiDomString += "});return false;\">";
|
||||
uiDomString += sensorStateInfo;
|
||||
uiDomString += "</button>";
|
||||
infoArr.add(uiDomString); //value
|
||||
|
||||
//this code adds "u":{"⏲ switch off timer":uiDomString} to the info object
|
||||
infoArr = user.createNestedArray("⏲ switch off timer"); //name
|
||||
|
||||
// off timer
|
||||
if (m_offTimerStart > 0) {
|
||||
uiDomString = "";
|
||||
unsigned int offSeconds = (m_switchOffDelay - (millis() - m_offTimerStart)) / 1000;
|
||||
if (offSeconds >= 3600) {
|
||||
uiDomString += (offSeconds / 3600);
|
||||
uiDomString += " hours ";
|
||||
offSeconds %= 3600;
|
||||
}
|
||||
if (offSeconds >= 60) {
|
||||
uiDomString += (offSeconds / 60);
|
||||
offSeconds %= 60;
|
||||
} else if (uiDomString.length() > 0){
|
||||
uiDomString += 0;
|
||||
}
|
||||
if (uiDomString.length() > 0){
|
||||
uiDomString += " min ";
|
||||
}
|
||||
uiDomString += (offSeconds);
|
||||
infoArr.add(uiDomString + " sec");
|
||||
} else {
|
||||
infoArr.add("inactive");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
|
||||
* Values in the state object may be modified by connected clients
|
||||
* Add "PIRenabled" to json state. This can be used to disable/enable the sensor.
|
||||
* Add "PIRoffSec" to json state. This can be used to adjust <m_switchOffDelay> milliseconds .
|
||||
*/
|
||||
void addToJsonState(JsonObject& root)
|
||||
{
|
||||
root["PIRenabled"] = m_PIRenabled;
|
||||
root["PIRoffSec"] = (m_switchOffDelay / 1000);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
|
||||
* Values in the state object may be modified by connected clients
|
||||
* Read "PIRenabled" from json state and switch enable/disable the PIR sensor.
|
||||
* Read "PIRoffSec" from json state and adjust <m_switchOffDelay> milliseconds .
|
||||
*/
|
||||
void readFromJsonState(JsonObject& root)
|
||||
{
|
||||
if (root["PIRoffSec"] != nullptr) {
|
||||
m_switchOffDelay = (1000 * max(60UL, min(43200UL, root["PIRoffSec"].as<unsigned long>())));
|
||||
}
|
||||
|
||||
if (root["PIRenabled"] != nullptr) {
|
||||
if (root["PIRenabled"] && !m_PIRenabled) {
|
||||
attachInterrupt(digitalPinToInterrupt(PIRsensorPin), ISR_PIRstateChange, CHANGE);
|
||||
newPIRsensorState(true, true);
|
||||
}
|
||||
else if(m_PIRenabled) {
|
||||
detachInterrupt(PIRsensorPin);
|
||||
}
|
||||
m_PIRenabled = root["PIRenabled"];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
|
||||
* This could be used in the future for the system to determine whether your usermod is installed.
|
||||
*/
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_PIRSWITCH;
|
||||
}
|
||||
|
||||
//More methods can be added in the future, this example will then be extended.
|
||||
//Your usermod will remain compatible as it does not need to implement all methods from the Usermod base class!
|
||||
};
|
124
usermods/PIR_sensor_switch/readme.md
Normal file
124
usermods/PIR_sensor_switch/readme.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# PIR sensor switch
|
||||
|
||||
This usermod-v2 modification allows the connection of a PIR sensor to switch on the LED strip when motion is detected. The switch-off occurs ten minutes after no more motion is detected.
|
||||
|
||||
_Story:_
|
||||
|
||||
I use the PIR Sensor to automatically turn on the WLED analog clock in my home office room when I am there.
|
||||
The LED strip is switched [using a relay](https://github.com/Aircoookie/WLED/wiki/Control-a-relay-with-WLED) to keep the power consumption low when it is switched off.
|
||||
|
||||
## Webinterface
|
||||
|
||||
The info page in the web interface shows the remaining time of the off timer. Usermod can also be temporarily disbled/enabled from the info page by clicking PIR button.
|
||||
|
||||
## Sensor connection
|
||||
|
||||
My setup uses an HC-SR501 or HC-SR602 sensor, a HC-SR505 should also work.
|
||||
|
||||
The usermod uses GPIO13 (D1 mini pin D7) by default for the sensor signal but can be changed in the Usermod settings page.
|
||||
[This example page](http://www.esp8266learning.com/wemos-mini-pir-sensor-example.php) describes how to connect the sensor.
|
||||
|
||||
Use the potentiometers on the sensor to set the time-delay to the minimum and the sensitivity to about half, or slightly above.
|
||||
You can also use usermod's off timer instead of sensor's. In such case rotate the potentiometer to its shortest time possible (or use SR602 which lacks such potentiometer).
|
||||
|
||||
## Usermod installation
|
||||
|
||||
1. Copy the file `usermod_PIR_sensor_switch.h` to the `wled00` directory.
|
||||
2. Register the usermod by adding `#include "usermod_PIR_sensor_switch.h"` in the top and `registerUsermod(new PIRsensorSwitch());` in the bottom of `usermods_list.cpp`.
|
||||
|
||||
Example **usermods_list.cpp**:
|
||||
|
||||
```cpp
|
||||
#include "wled.h"
|
||||
/*
|
||||
* Register your v2 usermods here!
|
||||
* (for v1 usermods using just usermod.cpp, you can ignore this file)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Add/uncomment your usermod filename here (and once more below)
|
||||
* || || ||
|
||||
* \/ \/ \/
|
||||
*/
|
||||
//#include "usermod_v2_example.h"
|
||||
//#include "usermod_temperature.h"
|
||||
//#include "usermod_v2_empty.h"
|
||||
#include "usermod_PIR_sensor_switch.h"
|
||||
|
||||
void registerUsermods()
|
||||
{
|
||||
/*
|
||||
* Add your usermod class name here
|
||||
* || || ||
|
||||
* \/ \/ \/
|
||||
*/
|
||||
//usermods.add(new MyExampleUsermod());
|
||||
//usermods.add(new UsermodTemperature());
|
||||
//usermods.add(new UsermodRenameMe());
|
||||
usermods.add(new PIRsensorSwitch());
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
**NOTE:** Usermod has been included in master branch of WLED so it can be compiled in directly just by defining `-D USERMOD_PIRSWITCH` and optionaly `-D PIR_SENSOR_PIN=16` to override default pin.
|
||||
|
||||
## API to enable/disable the PIR sensor from outside. For example from another usermod.
|
||||
|
||||
To query or change the PIR sensor state the methods `bool PIRsensorEnabled()` and `void EnablePIRsensor(bool enable)` are available.
|
||||
|
||||
When the PIR sensor state changes an MQTT message is broadcasted with topic `wled/deviceMAC/motion` and message `on` or `off`.
|
||||
Usermod can also be configured to just send MQTT message and not change WLED state using settings page as well as responding to motion only during nighttime (assuming NTP and lattitude/longitude are set to determine sunrise/sunset times).
|
||||
|
||||
### There are two options to get access to the usermod instance:
|
||||
|
||||
1. Include `usermod_PIR_sensor_switch.h` **before** you include the other usermod in `usermods_list.cpp'
|
||||
|
||||
or
|
||||
|
||||
2. Use `#include "usermod_PIR_sensor_switch.h"` at the top of the `usermod.h` where you need it.
|
||||
|
||||
**Example usermod.h :**
|
||||
```cpp
|
||||
#include "wled.h"
|
||||
|
||||
#include "usermod_PIR_sensor_switch.h"
|
||||
|
||||
class MyUsermod : public Usermod {
|
||||
//...
|
||||
|
||||
void togglePIRSensor() {
|
||||
#ifdef USERMOD_PIR_SENSOR_SWITCH
|
||||
PIRsensorSwitch *PIRsensor = (PIRsensorSwitch::*) usermods.lookup(USERMOD_ID_PIRSWITCH);
|
||||
if (PIRsensor != nullptr) {
|
||||
PIRsensor->EnablePIRsensor(!PIRsensor->PIRsensorEnabled());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
//...
|
||||
};
|
||||
```
|
||||
|
||||
### Configuration options
|
||||
|
||||
Usermod can be configured in Usermods settings page.
|
||||
|
||||
* `PIRenabled` - enable/disable usermod
|
||||
* `pin` - dynamically change GPIO pin where PIR sensor is attached to ESP
|
||||
* `PIRoffSec` - number of seconds after PIR sensor deactivates when usermod triggers Off preset (or turns WLED off)
|
||||
* `on-preset` - preset triggered when PIR activates (if this is 0 it will just turn WLED on)
|
||||
* `off-preset` - preset triggered when PIR deactivates (if this is 0 it will just turn WLED off)
|
||||
* `nighttime-only` - enable triggering only between sunset and sunrise (you will need to set up _NTP_, _Lat_ & _Lon_ in Time & Macro settings)
|
||||
* `mqtt-only` - only send MQTT messages, do not interact with WLED
|
||||
* `off-only` - only trigger presets or turn WLED on/off in WLED is not already on (displaying effect)
|
||||
* `notifications` - enable or disable sending notifications to other WLED instances using Sync button
|
||||
|
||||
|
||||
Have fun - @gegu & @blazoncek
|
||||
|
||||
## Change log
|
||||
2021-04
|
||||
* Adaptation for runtime configuration.
|
||||
|
||||
2021-11
|
||||
* Added information about dynamic configuration options
|
||||
* Added option to temporary enable/disble usermod from WLED UI (Info dialog)
|
452
usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h
Normal file
452
usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h
Normal file
@@ -0,0 +1,452 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
#ifndef PIR_SENSOR_PIN
|
||||
// compatible with QuinLED-Dig-Uno
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#define PIR_SENSOR_PIN 23 // Q4
|
||||
#else //ESP8266 boards
|
||||
#define PIR_SENSOR_PIN 13 // Q4 (D7 on D1 mini)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This usermod handles PIR sensor states.
|
||||
* The strip will be switched on and the off timer will be resetted when the sensor goes HIGH.
|
||||
* When the sensor state goes LOW, the off timer is started and when it expires, the strip is switched off.
|
||||
*
|
||||
*
|
||||
* Usermods allow you to add own functionality to WLED more easily
|
||||
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
|
||||
*
|
||||
* v2 usermods are class inheritance based and can (but don't have to) implement more functions, each of them is shown in this example.
|
||||
* Multiple v2 usermods can be added to one compilation easily.
|
||||
*
|
||||
* Creating a usermod:
|
||||
* This file serves as an example. If you want to create a usermod, it is recommended to use usermod_v2_empty.h from the usermods folder as a template.
|
||||
* Please remember to rename the class and file to a descriptive name.
|
||||
* You may also use multiple .h and .cpp files.
|
||||
*
|
||||
* Using a usermod:
|
||||
* 1. Copy the usermod into the sketch folder (same folder as wled00.ino)
|
||||
* 2. Register the usermod by adding #include "usermod_filename.h" in the top and registerUsermod(new MyUsermodClass()) in the bottom of usermods_list.cpp
|
||||
*/
|
||||
|
||||
class PIRsensorSwitch : public Usermod
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* constructor
|
||||
*/
|
||||
PIRsensorSwitch() {}
|
||||
/**
|
||||
* desctructor
|
||||
*/
|
||||
~PIRsensorSwitch() {}
|
||||
|
||||
/**
|
||||
* Enable/Disable the PIR sensor
|
||||
*/
|
||||
void EnablePIRsensor(bool en) { enabled = en; }
|
||||
/**
|
||||
* Get PIR sensor enabled/disabled state
|
||||
*/
|
||||
bool PIRsensorEnabled() { return enabled; }
|
||||
|
||||
private:
|
||||
|
||||
byte prevPreset = 0;
|
||||
byte prevPlaylist = 0;
|
||||
bool savedState = false;
|
||||
|
||||
uint32_t offTimerStart = 0; // off timer start time
|
||||
byte NotifyUpdateMode = CALL_MODE_NO_NOTIFY; // notification mode for stateUpdated(): CALL_MODE_NO_NOTIFY or CALL_MODE_DIRECT_CHANGE
|
||||
byte sensorPinState = LOW; // current PIR sensor pin state
|
||||
bool initDone = false; // status of initialization
|
||||
bool PIRtriggered = false;
|
||||
unsigned long lastLoop = 0;
|
||||
|
||||
// configurable parameters
|
||||
bool enabled = true; // PIR sensor enabled
|
||||
int8_t PIRsensorPin = PIR_SENSOR_PIN; // PIR sensor pin
|
||||
uint32_t m_switchOffDelay = 600000; // delay before switch off after the sensor state goes LOW (10min)
|
||||
uint8_t m_onPreset = 0; // on preset
|
||||
uint8_t m_offPreset = 0; // off preset
|
||||
bool m_nightTimeOnly = false; // flag to indicate that PIR sensor should activate WLED during nighttime only
|
||||
bool m_mqttOnly = false; // flag to send MQTT message only (assuming it is enabled)
|
||||
// flag to enable triggering only if WLED is initially off (LEDs are not on, preventing running effect being overwritten by PIR)
|
||||
bool m_offOnly = false;
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
static const char _name[];
|
||||
static const char _switchOffDelay[];
|
||||
static const char _enabled[];
|
||||
static const char _onPreset[];
|
||||
static const char _offPreset[];
|
||||
static const char _nightTime[];
|
||||
static const char _mqttOnly[];
|
||||
static const char _offOnly[];
|
||||
static const char _notify[];
|
||||
|
||||
/**
|
||||
* check if it is daytime
|
||||
* if sunrise/sunset is not defined (no NTP or lat/lon) default to nighttime
|
||||
*/
|
||||
bool isDayTime() {
|
||||
updateLocalTime();
|
||||
uint8_t hr = hour(localTime);
|
||||
uint8_t mi = minute(localTime);
|
||||
|
||||
if (sunrise && sunset) {
|
||||
if (hour(sunrise)<hr && hour(sunset)>hr) {
|
||||
return true;
|
||||
} else {
|
||||
if (hour(sunrise)==hr && minute(sunrise)<mi) {
|
||||
return true;
|
||||
}
|
||||
if (hour(sunset)==hr && minute(sunset)>mi) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* switch strip on/off
|
||||
*/
|
||||
void switchStrip(bool switchOn)
|
||||
{
|
||||
if (m_offOnly && bri && (switchOn || (!PIRtriggered && !switchOn))) return;
|
||||
PIRtriggered = switchOn;
|
||||
if (switchOn) {
|
||||
if (m_onPreset) {
|
||||
if (currentPlaylist>0) prevPlaylist = currentPlaylist;
|
||||
else if (currentPreset>0) prevPreset = currentPreset;
|
||||
else {
|
||||
saveTemporaryPreset();
|
||||
savedState = true;
|
||||
prevPlaylist = 0;
|
||||
prevPreset = 0;
|
||||
}
|
||||
applyPreset(m_onPreset, NotifyUpdateMode);
|
||||
return;
|
||||
}
|
||||
// preset not assigned
|
||||
if (bri == 0) {
|
||||
bri = briLast;
|
||||
stateUpdated(NotifyUpdateMode);
|
||||
}
|
||||
} else {
|
||||
if (m_offPreset) {
|
||||
applyPreset(m_offPreset, NotifyUpdateMode);
|
||||
return;
|
||||
} else if (prevPlaylist) {
|
||||
applyPreset(prevPlaylist, NotifyUpdateMode);
|
||||
prevPlaylist = 0;
|
||||
return;
|
||||
} else if (prevPreset) {
|
||||
applyPreset(prevPreset, NotifyUpdateMode);
|
||||
prevPreset = 0;
|
||||
return;
|
||||
} else if (savedState) {
|
||||
applyTemporaryPreset();
|
||||
savedState = false;
|
||||
return;
|
||||
}
|
||||
// preset not assigned
|
||||
if (bri != 0) {
|
||||
briLast = bri;
|
||||
bri = 0;
|
||||
stateUpdated(NotifyUpdateMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void publishMqtt(const char* state)
|
||||
{
|
||||
//Check if MQTT Connected, otherwise it will crash the 8266
|
||||
if (WLED_MQTT_CONNECTED){
|
||||
char subuf[64];
|
||||
strcpy(subuf, mqttDeviceTopic);
|
||||
strcat_P(subuf, PSTR("/motion"));
|
||||
mqtt->publish(subuf, 0, false, state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and update PIR sensor state.
|
||||
* Initilize/reset switch off timer
|
||||
*/
|
||||
bool updatePIRsensorState()
|
||||
{
|
||||
bool pinState = digitalRead(PIRsensorPin);
|
||||
if (pinState != sensorPinState) {
|
||||
sensorPinState = pinState; // change previous state
|
||||
|
||||
if (sensorPinState == HIGH) {
|
||||
offTimerStart = 0;
|
||||
if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(true);
|
||||
publishMqtt("on");
|
||||
} else /*if (bri != 0)*/ {
|
||||
// start switch off timer
|
||||
offTimerStart = millis();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* switch off the strip if the delay has elapsed
|
||||
*/
|
||||
bool handleOffTimer()
|
||||
{
|
||||
if (offTimerStart > 0 && millis() - offTimerStart > m_switchOffDelay)
|
||||
{
|
||||
if (enabled == true)
|
||||
{
|
||||
if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(false);
|
||||
publishMqtt("off");
|
||||
}
|
||||
offTimerStart = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
//Functions called by WLED
|
||||
|
||||
/**
|
||||
* setup() is called once at boot. WiFi is not yet connected at this point.
|
||||
* You can use it to initialize variables, sensors or similar.
|
||||
*/
|
||||
void setup()
|
||||
{
|
||||
if (enabled) {
|
||||
// pin retrieved from cfg.json (readFromConfig()) prior to running setup()
|
||||
if (PIRsensorPin >= 0 && pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) {
|
||||
// PIR Sensor mode INPUT_PULLUP
|
||||
pinMode(PIRsensorPin, INPUT_PULLUP);
|
||||
sensorPinState = digitalRead(PIRsensorPin);
|
||||
} else {
|
||||
if (PIRsensorPin >= 0) {
|
||||
DEBUG_PRINTLN(F("PIRSensorSwitch pin allocation failed."));
|
||||
}
|
||||
PIRsensorPin = -1; // allocation failed
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* connected() is called every time the WiFi is (re)connected
|
||||
* Use it to initialize network interfaces
|
||||
*/
|
||||
void connected()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* loop() is called continuously. Here you can check for events, read sensors, etc.
|
||||
*/
|
||||
void loop()
|
||||
{
|
||||
// only check sensors 4x/s
|
||||
if (!enabled || millis() - lastLoop < 250 || strip.isUpdating()) return;
|
||||
lastLoop = millis();
|
||||
|
||||
if (!updatePIRsensorState()) {
|
||||
handleOffTimer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
|
||||
*
|
||||
* Add PIR sensor state and switch off timer duration to jsoninfo
|
||||
*/
|
||||
void addToJsonInfo(JsonObject &root)
|
||||
{
|
||||
JsonObject user = root["u"];
|
||||
if (user.isNull()) user = root.createNestedObject("u");
|
||||
|
||||
String uiDomString = F("<button class=\"btn\" onclick=\"requestJson({");
|
||||
uiDomString += FPSTR(_name);
|
||||
uiDomString += F(":{");
|
||||
uiDomString += FPSTR(_enabled);
|
||||
if (enabled) {
|
||||
uiDomString += F(":false}});\">");
|
||||
uiDomString += F("PIR <i class=\"icons\"></i>");
|
||||
} else {
|
||||
uiDomString += F(":true}});\">");
|
||||
uiDomString += F("PIR <i class=\"icons\"></i>");
|
||||
}
|
||||
uiDomString += F("</button>");
|
||||
JsonArray infoArr = user.createNestedArray(uiDomString); // timer value
|
||||
|
||||
if (enabled) {
|
||||
if (offTimerStart > 0)
|
||||
{
|
||||
uiDomString = "";
|
||||
unsigned int offSeconds = (m_switchOffDelay - (millis() - offTimerStart)) / 1000;
|
||||
if (offSeconds >= 3600)
|
||||
{
|
||||
uiDomString += (offSeconds / 3600);
|
||||
uiDomString += F("h ");
|
||||
offSeconds %= 3600;
|
||||
}
|
||||
if (offSeconds >= 60)
|
||||
{
|
||||
uiDomString += (offSeconds / 60);
|
||||
offSeconds %= 60;
|
||||
}
|
||||
else if (uiDomString.length() > 0)
|
||||
{
|
||||
uiDomString += 0;
|
||||
}
|
||||
if (uiDomString.length() > 0)
|
||||
{
|
||||
uiDomString += F("min ");
|
||||
}
|
||||
uiDomString += (offSeconds);
|
||||
infoArr.add(uiDomString + F("s"));
|
||||
} else {
|
||||
infoArr.add(sensorPinState ? F("sensor on") : F("inactive"));
|
||||
}
|
||||
} else {
|
||||
infoArr.add(F("disabled"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
|
||||
* Values in the state object may be modified by connected clients
|
||||
*/
|
||||
/*
|
||||
void addToJsonState(JsonObject &root)
|
||||
{
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
|
||||
* Values in the state object may be modified by connected clients
|
||||
*/
|
||||
|
||||
void readFromJsonState(JsonObject &root)
|
||||
{
|
||||
if (!initDone) return; // prevent crash on boot applyPreset()
|
||||
JsonObject usermod = root[FPSTR(_name)];
|
||||
if (!usermod.isNull()) {
|
||||
if (usermod[FPSTR(_enabled)].is<bool>()) {
|
||||
enabled = usermod[FPSTR(_enabled)].as<bool>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* provide the changeable values
|
||||
*/
|
||||
void addToConfig(JsonObject &root)
|
||||
{
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name));
|
||||
top[FPSTR(_enabled)] = enabled;
|
||||
top[FPSTR(_switchOffDelay)] = m_switchOffDelay / 1000;
|
||||
top["pin"] = PIRsensorPin;
|
||||
top[FPSTR(_onPreset)] = m_onPreset;
|
||||
top[FPSTR(_offPreset)] = m_offPreset;
|
||||
top[FPSTR(_nightTime)] = m_nightTimeOnly;
|
||||
top[FPSTR(_mqttOnly)] = m_mqttOnly;
|
||||
top[FPSTR(_offOnly)] = m_offOnly;
|
||||
top[FPSTR(_notify)] = (NotifyUpdateMode != CALL_MODE_NO_NOTIFY);
|
||||
DEBUG_PRINTLN(F("PIR config saved."));
|
||||
}
|
||||
|
||||
/**
|
||||
* restore the changeable values
|
||||
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
|
||||
*
|
||||
* The function should return true if configuration was successfully loaded or false if there was no configuration.
|
||||
*/
|
||||
bool readFromConfig(JsonObject &root)
|
||||
{
|
||||
bool oldEnabled = enabled;
|
||||
int8_t oldPin = PIRsensorPin;
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
PIRsensorPin = top["pin"] | PIRsensorPin;
|
||||
|
||||
enabled = top[FPSTR(_enabled)] | enabled;
|
||||
|
||||
m_switchOffDelay = (top[FPSTR(_switchOffDelay)] | m_switchOffDelay/1000) * 1000;
|
||||
|
||||
m_onPreset = top[FPSTR(_onPreset)] | m_onPreset;
|
||||
m_onPreset = max(0,min(250,(int)m_onPreset));
|
||||
m_offPreset = top[FPSTR(_offPreset)] | m_offPreset;
|
||||
m_offPreset = max(0,min(250,(int)m_offPreset));
|
||||
|
||||
m_nightTimeOnly = top[FPSTR(_nightTime)] | m_nightTimeOnly;
|
||||
m_mqttOnly = top[FPSTR(_mqttOnly)] | m_mqttOnly;
|
||||
m_offOnly = top[FPSTR(_offOnly)] | m_offOnly;
|
||||
|
||||
NotifyUpdateMode = top[FPSTR(_notify)] ? CALL_MODE_DIRECT_CHANGE : CALL_MODE_NO_NOTIFY;
|
||||
|
||||
if (!initDone) {
|
||||
// reading config prior to setup()
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
if (oldPin != PIRsensorPin || oldEnabled != enabled) {
|
||||
// check if pin is OK
|
||||
if (oldPin != PIRsensorPin && oldPin >= 0) {
|
||||
// if we are changing pin in settings page
|
||||
// deallocate old pin
|
||||
pinManager.deallocatePin(oldPin, PinOwner::UM_PIR);
|
||||
if (pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) {
|
||||
pinMode(PIRsensorPin, INPUT_PULLUP);
|
||||
} else {
|
||||
// allocation failed
|
||||
PIRsensorPin = -1;
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
if (enabled) {
|
||||
sensorPinState = digitalRead(PIRsensorPin);
|
||||
}
|
||||
}
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
}
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return !top[FPSTR(_notify)].isNull();
|
||||
}
|
||||
|
||||
/**
|
||||
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
|
||||
* This could be used in the future for the system to determine whether your usermod is installed.
|
||||
*/
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_PIRSWITCH;
|
||||
}
|
||||
};
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
const char PIRsensorSwitch::_name[] PROGMEM = "PIRsensorSwitch";
|
||||
const char PIRsensorSwitch::_enabled[] PROGMEM = "PIRenabled";
|
||||
const char PIRsensorSwitch::_switchOffDelay[] PROGMEM = "PIRoffSec";
|
||||
const char PIRsensorSwitch::_onPreset[] PROGMEM = "on-preset";
|
||||
const char PIRsensorSwitch::_offPreset[] PROGMEM = "off-preset";
|
||||
const char PIRsensorSwitch::_nightTime[] PROGMEM = "nighttime-only";
|
||||
const char PIRsensorSwitch::_mqttOnly[] PROGMEM = "mqtt-only";
|
||||
const char PIRsensorSwitch::_offOnly[] PROGMEM = "off-only";
|
||||
const char PIRsensorSwitch::_notify[] PROGMEM = "notifications";
|
36
usermods/PWM_fan/readme.md
Normal file
36
usermods/PWM_fan/readme.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# PWM fan
|
||||
|
||||
v2 Usermod to to control PWM fan with RPM feedback and temperature control
|
||||
|
||||
This usermod requires Dallas Temperature usermod to obtain temperature information. If this is not available the fan will always run at 100% speed.
|
||||
If the fan does not have _tacho_ (RPM) output you can set the _tacho-pin_ to -1 to not use that feature.
|
||||
|
||||
You can also set the thershold temperature at which fan runs at lowest speed. If the actual temperature measured will be 3°C greater than threshold temperature the fan will run at 100%.
|
||||
|
||||
If the _tacho_ is supported the current speed (in RPM) will be repored in WLED Info page.
|
||||
|
||||
## Installation
|
||||
|
||||
Add the compile-time option `-D USERMOD_PWM_FAN` to your `platformio.ini` (or `platformio_override.ini`) or use `#define USERMOD_PWM_FAN` in `myconfig.h`.
|
||||
You will also need `-D USERMOD_DALLASTEMPERATURE`.
|
||||
|
||||
### Define Your Options
|
||||
|
||||
All of the parameters are configured during run-time using Usermods settings page.
|
||||
This includes:
|
||||
|
||||
* PWM output pin
|
||||
* tacho input pin
|
||||
* sampling frequency in seconds
|
||||
* threshold temperature in degees C
|
||||
|
||||
_NOTE:_ You may also need to tweak Dallas Temperature usermod sampling frequency to match PWM fan sampling frequency.
|
||||
|
||||
### PlatformIO requirements
|
||||
|
||||
No special requirements.
|
||||
|
||||
## Change Log
|
||||
|
||||
2021-10
|
||||
* First public release
|
332
usermods/PWM_fan/usermod_PWM_fan.h
Normal file
332
usermods/PWM_fan/usermod_PWM_fan.h
Normal file
@@ -0,0 +1,332 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef USERMOD_DALLASTEMPERATURE
|
||||
#error The "PWM fan" usermod requires "Dallas Temeprature" usermod to function properly.
|
||||
#endif
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
// PWM & tacho code curtesy of @KlausMu
|
||||
// https://github.com/KlausMu/esp32-fan-controller/tree/main/src
|
||||
// adapted for WLED usermod by @blazoncek
|
||||
|
||||
|
||||
// tacho counter
|
||||
static volatile unsigned long counter_rpm = 0;
|
||||
// Interrupt counting every rotation of the fan
|
||||
// https://desire.giesecke.tk/index.php/2018/01/30/change-global-variables-from-isr/
|
||||
static void IRAM_ATTR rpm_fan() {
|
||||
counter_rpm++;
|
||||
}
|
||||
|
||||
|
||||
class PWMFanUsermod : public Usermod {
|
||||
|
||||
private:
|
||||
|
||||
bool initDone = false;
|
||||
bool enabled = true;
|
||||
unsigned long msLastTachoMeasurement = 0;
|
||||
uint16_t last_rpm = 0;
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
uint8_t pwmChannel = 255;
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_DALLASTEMPERATURE
|
||||
UsermodTemperature* tempUM;
|
||||
#endif
|
||||
|
||||
// configurable parameters
|
||||
int8_t tachoPin = -1;
|
||||
int8_t pwmPin = -1;
|
||||
uint8_t tachoUpdateSec = 30;
|
||||
float targetTemperature = 25.0;
|
||||
uint8_t minPWMValuePct = 50;
|
||||
uint8_t numberOfInterrupsInOneSingleRotation = 2; // Number of interrupts ESP32 sees on tacho signal on a single fan rotation. All the fans I've seen trigger two interrups.
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
static const char _name[];
|
||||
static const char _enabled[];
|
||||
static const char _tachoPin[];
|
||||
static const char _pwmPin[];
|
||||
static const char _temperature[];
|
||||
static const char _tachoUpdateSec[];
|
||||
static const char _minPWMValuePct[];
|
||||
static const char _IRQperRotation[];
|
||||
|
||||
void initTacho(void) {
|
||||
if (tachoPin < 0 || !pinManager.allocatePin(tachoPin, false, PinOwner::UM_Unspecified)){
|
||||
tachoPin = -1;
|
||||
return;
|
||||
}
|
||||
pinMode(tachoPin, INPUT);
|
||||
digitalWrite(tachoPin, HIGH);
|
||||
attachInterrupt(digitalPinToInterrupt(tachoPin), rpm_fan, FALLING);
|
||||
DEBUG_PRINTLN(F("Tacho sucessfully initialized."));
|
||||
}
|
||||
|
||||
void deinitTacho(void) {
|
||||
if (tachoPin < 0) return;
|
||||
detachInterrupt(digitalPinToInterrupt(tachoPin));
|
||||
pinManager.deallocatePin(tachoPin, PinOwner::UM_Unspecified);
|
||||
tachoPin = -1;
|
||||
}
|
||||
|
||||
void updateTacho(void) {
|
||||
if (tachoPin < 0) return;
|
||||
|
||||
// start of tacho measurement
|
||||
// detach interrupt while calculating rpm
|
||||
detachInterrupt(digitalPinToInterrupt(tachoPin));
|
||||
// calculate rpm
|
||||
last_rpm = (counter_rpm * 60) / numberOfInterrupsInOneSingleRotation;
|
||||
last_rpm /= tachoUpdateSec;
|
||||
// reset counter
|
||||
counter_rpm = 0;
|
||||
// store milliseconds when tacho was measured the last time
|
||||
msLastTachoMeasurement = millis();
|
||||
// attach interrupt again
|
||||
attachInterrupt(digitalPinToInterrupt(tachoPin), rpm_fan, FALLING);
|
||||
}
|
||||
|
||||
// https://randomnerdtutorials.com/esp32-pwm-arduino-ide/
|
||||
void initPWMfan(void) {
|
||||
if (pwmPin < 0 || !pinManager.allocatePin(pwmPin, true, PinOwner::UM_Unspecified)) {
|
||||
pwmPin = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ESP8266
|
||||
analogWriteRange(255);
|
||||
analogWriteFreq(WLED_PWM_FREQ);
|
||||
#else
|
||||
pwmChannel = pinManager.allocateLedc(1);
|
||||
if (pwmChannel == 255) { //no more free LEDC channels
|
||||
deinitPWMfan(); return;
|
||||
}
|
||||
// configure LED PWM functionalitites
|
||||
ledcSetup(pwmChannel, 25000, 8);
|
||||
// attach the channel to the GPIO to be controlled
|
||||
ledcAttachPin(pwmPin, pwmChannel);
|
||||
#endif
|
||||
DEBUG_PRINTLN(F("Fan PWM sucessfully initialized."));
|
||||
}
|
||||
|
||||
void deinitPWMfan(void) {
|
||||
if (pwmPin < 0) return;
|
||||
|
||||
pinManager.deallocatePin(pwmPin, PinOwner::UM_Unspecified);
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
pinManager.deallocateLedc(pwmChannel, 1);
|
||||
#endif
|
||||
pwmPin = -1;
|
||||
}
|
||||
|
||||
void updateFanSpeed(uint8_t pwmValue){
|
||||
if (pwmPin < 0) return;
|
||||
|
||||
#ifdef ESP8266
|
||||
analogWrite(pwmPin, pwmValue);
|
||||
#else
|
||||
ledcWrite(pwmChannel, pwmValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
float getActualTemperature(void) {
|
||||
#ifdef USERMOD_DALLASTEMPERATURE
|
||||
if (tempUM != nullptr)
|
||||
return tempUM->getTemperatureC();
|
||||
#endif
|
||||
return -127.0f;
|
||||
}
|
||||
|
||||
void setFanPWMbasedOnTemperature(void) {
|
||||
float temp = getActualTemperature();
|
||||
float difftemp = temp - targetTemperature;
|
||||
// Default to run fan at full speed.
|
||||
int newPWMvalue = 255;
|
||||
int pwmStep = ((100 - minPWMValuePct) * newPWMvalue) / (7*100);
|
||||
int pwmMinimumValue = (minPWMValuePct * newPWMvalue) / 100;
|
||||
|
||||
if ((temp == NAN) || (temp <= 0.0)) {
|
||||
DEBUG_PRINTLN(F("WARNING: no temperature value available. Cannot do temperature control. Will set PWM fan to 255."));
|
||||
} else if (difftemp <= 0.0) {
|
||||
// Temperature is below target temperature. Run fan at minimum speed.
|
||||
newPWMvalue = pwmMinimumValue;
|
||||
} else if (difftemp <= 0.5) {
|
||||
newPWMvalue = pwmMinimumValue + pwmStep;
|
||||
} else if (difftemp <= 1.0) {
|
||||
newPWMvalue = pwmMinimumValue + 2*pwmStep;
|
||||
} else if (difftemp <= 1.5) {
|
||||
newPWMvalue = pwmMinimumValue + 3*pwmStep;
|
||||
} else if (difftemp <= 2.0) {
|
||||
newPWMvalue = pwmMinimumValue + 4*pwmStep;
|
||||
} else if (difftemp <= 2.5) {
|
||||
newPWMvalue = pwmMinimumValue + 5*pwmStep;
|
||||
} else if (difftemp <= 3.0) {
|
||||
newPWMvalue = pwmMinimumValue + 6*pwmStep;
|
||||
}
|
||||
updateFanSpeed(newPWMvalue);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// gets called once at boot. Do all initialization that doesn't depend on
|
||||
// network here
|
||||
void setup() {
|
||||
#ifdef USERMOD_DALLASTEMPERATURE
|
||||
// This Usermod requires Temperature usermod
|
||||
tempUM = (UsermodTemperature*) usermods.lookup(USERMOD_ID_TEMPERATURE);
|
||||
#endif
|
||||
initTacho();
|
||||
initPWMfan();
|
||||
updateFanSpeed((minPWMValuePct * 255) / 100); // inital fan speed
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
// gets called every time WiFi is (re-)connected. Initialize own network
|
||||
// interfaces here
|
||||
void connected() {}
|
||||
|
||||
/*
|
||||
* Da loop.
|
||||
*/
|
||||
void loop() {
|
||||
if (!enabled || strip.isUpdating()) return;
|
||||
|
||||
unsigned long now = millis();
|
||||
if ((now - msLastTachoMeasurement) < (tachoUpdateSec * 1000)) return;
|
||||
|
||||
updateTacho();
|
||||
setFanPWMbasedOnTemperature();
|
||||
}
|
||||
|
||||
/*
|
||||
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
|
||||
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
|
||||
* Below it is shown how this could be used for e.g. a light sensor
|
||||
*/
|
||||
void addToJsonInfo(JsonObject& root) {
|
||||
if (tachoPin < 0) return;
|
||||
JsonObject user = root["u"];
|
||||
if (user.isNull()) user = root.createNestedObject("u");
|
||||
JsonArray data = user.createNestedArray(FPSTR(_name));
|
||||
data.add(last_rpm);
|
||||
data.add(F("rpm"));
|
||||
}
|
||||
|
||||
/*
|
||||
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
|
||||
* Values in the state object may be modified by connected clients
|
||||
*/
|
||||
//void addToJsonState(JsonObject& root) {
|
||||
//}
|
||||
|
||||
/*
|
||||
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
|
||||
* Values in the state object may be modified by connected clients
|
||||
*/
|
||||
//void readFromJsonState(JsonObject& root) {
|
||||
// if (!initDone) return; // prevent crash on boot applyPreset()
|
||||
//}
|
||||
|
||||
/*
|
||||
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
|
||||
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
|
||||
* If you want to force saving the current state, use serializeConfig() in your loop().
|
||||
*
|
||||
* CAUTION: serializeConfig() will initiate a filesystem write operation.
|
||||
* It might cause the LEDs to stutter and will cause flash wear if called too often.
|
||||
* Use it sparingly and always in the loop, never in network callbacks!
|
||||
*
|
||||
* addToConfig() will also not yet add your setting to one of the settings pages automatically.
|
||||
* To make that work you still have to add the setting to the HTML, xml.cpp and set.cpp manually.
|
||||
*
|
||||
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
|
||||
*/
|
||||
void addToConfig(JsonObject& root) {
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
|
||||
top[FPSTR(_enabled)] = enabled;
|
||||
top[FPSTR(_pwmPin)] = pwmPin;
|
||||
top[FPSTR(_tachoPin)] = tachoPin;
|
||||
top[FPSTR(_tachoUpdateSec)] = tachoUpdateSec;
|
||||
top[FPSTR(_temperature)] = targetTemperature;
|
||||
top[FPSTR(_minPWMValuePct)] = minPWMValuePct;
|
||||
top[FPSTR(_IRQperRotation)] = numberOfInterrupsInOneSingleRotation;
|
||||
DEBUG_PRINTLN(F("Autosave config saved."));
|
||||
}
|
||||
|
||||
/*
|
||||
* readFromConfig() can be used to read back the custom settings you added with addToConfig().
|
||||
* This is called by WLED when settings are loaded (currently this only happens once immediately after boot)
|
||||
*
|
||||
* readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes),
|
||||
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
|
||||
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
|
||||
*
|
||||
* The function should return true if configuration was successfully loaded or false if there was no configuration.
|
||||
*/
|
||||
bool readFromConfig(JsonObject& root) {
|
||||
int8_t newTachoPin = tachoPin;
|
||||
int8_t newPwmPin = pwmPin;
|
||||
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
enabled = top[FPSTR(_enabled)] | enabled;
|
||||
newTachoPin = top[FPSTR(_tachoPin)] | newTachoPin;
|
||||
newPwmPin = top[FPSTR(_pwmPin)] | newPwmPin;
|
||||
tachoUpdateSec = top[FPSTR(_tachoUpdateSec)] | tachoUpdateSec;
|
||||
tachoUpdateSec = (uint8_t) max(1,(int)tachoUpdateSec); // bounds checking
|
||||
targetTemperature = top[FPSTR(_temperature)] | targetTemperature;
|
||||
minPWMValuePct = top[FPSTR(_minPWMValuePct)] | minPWMValuePct;
|
||||
minPWMValuePct = (uint8_t) min(100,max(0,(int)minPWMValuePct)); // bounds checking
|
||||
numberOfInterrupsInOneSingleRotation = top[FPSTR(_IRQperRotation)] | numberOfInterrupsInOneSingleRotation;
|
||||
numberOfInterrupsInOneSingleRotation = (uint8_t) max(1,(int)numberOfInterrupsInOneSingleRotation); // bounds checking
|
||||
|
||||
if (!initDone) {
|
||||
// first run: reading from cfg.json
|
||||
tachoPin = newTachoPin;
|
||||
pwmPin = newPwmPin;
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
// changing paramters from settings page
|
||||
if (tachoPin != newTachoPin || pwmPin != newPwmPin) {
|
||||
DEBUG_PRINTLN(F("Re-init pins."));
|
||||
// deallocate pin and release interrupts
|
||||
deinitTacho();
|
||||
deinitPWMfan();
|
||||
tachoPin = newTachoPin;
|
||||
pwmPin = newPwmPin;
|
||||
// initialise
|
||||
setup();
|
||||
}
|
||||
}
|
||||
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return !top[FPSTR(_IRQperRotation)].isNull();
|
||||
}
|
||||
|
||||
/*
|
||||
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
|
||||
* This could be used in the future for the system to determine whether your usermod is installed.
|
||||
*/
|
||||
uint16_t getId() {
|
||||
return USERMOD_ID_PWM_FAN;
|
||||
}
|
||||
};
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
const char PWMFanUsermod::_name[] PROGMEM = "PWM-fan";
|
||||
const char PWMFanUsermod::_enabled[] PROGMEM = "enabled";
|
||||
const char PWMFanUsermod::_tachoPin[] PROGMEM = "tacho-pin";
|
||||
const char PWMFanUsermod::_pwmPin[] PROGMEM = "PWM-pin";
|
||||
const char PWMFanUsermod::_temperature[] PROGMEM = "target-temp-C";
|
||||
const char PWMFanUsermod::_tachoUpdateSec[] PROGMEM = "tacho-update-s";
|
||||
const char PWMFanUsermod::_minPWMValuePct[] PROGMEM = "min-PWM-percent";
|
||||
const char PWMFanUsermod::_IRQperRotation[] PROGMEM = "IRQs-per-rotation";
|
34
usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.md
Normal file
34
usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# QuinLED Dig Uno board
|
||||
|
||||
These files allow WLED 0.9.1 to report the temp sensor on the Quinled board to MQTT. I use it to report the board temp to Home Assistant via MQTT, so it will send notifications if something happens and the board start to heat up.
|
||||
This code uses Aircookie's WLED software. It has a premade file for user modifications. I use it to publish the temperature from the dallas temperature sensor on the Quinled board. The entries for the top of the WLED00 file, initializes the required libraries, and variables for the sensor. The .ino file waits for 60 seconds, and checks to see if the MQTT server is connected (thanks Aircoookie). It then poles the sensor, and published it using the MQTT service already running, using the main topic programmed in the WLED UI.
|
||||
|
||||
Installation of file: Copy and replace file in wled00 directory
|
||||
|
||||
## Project link
|
||||
|
||||
* [QuinLED-Dig-Uno](https://quinled.info/2018/09/15/quinled-dig-uno/) - Project link
|
||||
|
||||
### Platformio requirements
|
||||
|
||||
Uncomment `DallasTemperature@~3.8.0`,`OneWire@~2.3.5 under` `[common]` section in `platformio.ini`:
|
||||
|
||||
```ini
|
||||
# platformio.ini
|
||||
...
|
||||
[platformio]
|
||||
...
|
||||
; default_envs = esp07
|
||||
default_envs = d1_mini
|
||||
...
|
||||
[common]
|
||||
...
|
||||
lib_deps_external =
|
||||
...
|
||||
#For use SSD1306 OLED display uncomment following
|
||||
U8g2@~2.27.3
|
||||
#For Dallas sensor uncomment following 2 lines
|
||||
DallasTemperature@~3.8.0
|
||||
OneWire@~2.3.5
|
||||
...
|
||||
```
|
54
usermods/QuinLED_Dig_Uno_Temp_MQTT/usermod.cpp
Normal file
54
usermods/QuinLED_Dig_Uno_Temp_MQTT/usermod.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#include <Arduino.h>
|
||||
#include "wled.h"
|
||||
//Intiating code for QuinLED Dig-Uno temp sensor
|
||||
//Uncomment Celsius if that is your prefered temperature scale
|
||||
#include <DallasTemperature.h> //Dallastemperature sensor
|
||||
#ifdef ARDUINO_ARCH_ESP32 //ESP32 boards
|
||||
OneWire oneWire(18);
|
||||
#else //ESP8266 boards
|
||||
OneWire oneWire(14);
|
||||
#endif
|
||||
DallasTemperature sensor(&oneWire);
|
||||
long temptimer = millis();
|
||||
long lastMeasure = 0;
|
||||
#define Celsius // Show temperature mesaurement in Celcius otherwise is in Fahrenheit
|
||||
void userSetup()
|
||||
{
|
||||
// Start the DS18B20 sensor
|
||||
sensor.begin();
|
||||
}
|
||||
|
||||
//gets called every time WiFi is (re-)connected. Initialize own network interfaces here
|
||||
void userConnected()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void userLoop()
|
||||
{
|
||||
temptimer = millis();
|
||||
|
||||
// Timer to publishe new temperature every 60 seconds
|
||||
if (temptimer - lastMeasure > 60000) {
|
||||
lastMeasure = temptimer;
|
||||
|
||||
//Check if MQTT Connected, otherwise it will crash the 8266
|
||||
if (mqtt != nullptr){
|
||||
sensor.requestTemperatures();
|
||||
|
||||
//Gets prefered temperature scale based on selection in definitions section
|
||||
#ifdef Celsius
|
||||
float board_temperature = sensor.getTempCByIndex(0);
|
||||
#else
|
||||
float board_temperature = sensors.getTempFByIndex(0);
|
||||
#endif
|
||||
|
||||
//Create character string populated with user defined device topic from the UI, and the read temperature. Then publish to MQTT server.
|
||||
char subuf[38];
|
||||
strcpy(subuf, mqttDeviceTopic);
|
||||
strcat(subuf, "/temperature");
|
||||
mqtt->publish(subuf, 0, true, String(board_temperature).c_str());
|
||||
return;}
|
||||
return;}
|
||||
return;
|
||||
}
|
8
usermods/RTC/readme.md
Normal file
8
usermods/RTC/readme.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# DS1307/DS3231 Real time clock
|
||||
|
||||
Gets the time from I2C RTC module on boot. This allows clocks to operate e.g. if temporarily no WiFi is available.
|
||||
The stored time is updated each time NTP is synced.
|
||||
|
||||
## Installation
|
||||
|
||||
Add the build flag `-D USERMOD_RTC` to your platformio environment.
|
59
usermods/RTC/usermod_rtc.h
Normal file
59
usermods/RTC/usermod_rtc.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/dependencies/time/DS1307RTC.h"
|
||||
#include "wled.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#define HW_PIN_SCL 22
|
||||
#define HW_PIN_SDA 21
|
||||
#else
|
||||
#define HW_PIN_SCL 5
|
||||
#define HW_PIN_SDA 4
|
||||
#endif
|
||||
|
||||
//Connect DS1307 to standard I2C pins (ESP32: GPIO 21 (SDA)/GPIO 22 (SCL))
|
||||
|
||||
class RTCUsermod : public Usermod {
|
||||
private:
|
||||
unsigned long lastTime = 0;
|
||||
bool disabled = false;
|
||||
public:
|
||||
|
||||
void setup() {
|
||||
PinManagerPinType pins[2] = { { HW_PIN_SCL, true }, { HW_PIN_SDA, true } };
|
||||
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { disabled = true; return; }
|
||||
time_t rtcTime = RTC.get();
|
||||
if (rtcTime) {
|
||||
toki.setTime(rtcTime,TOKI_NO_MS_ACCURACY,TOKI_TS_RTC);
|
||||
updateLocalTime();
|
||||
} else {
|
||||
if (!RTC.chipPresent()) disabled = true; //don't waste time if H/W error
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (strip.isUpdating()) return;
|
||||
if (!disabled && toki.isTick()) {
|
||||
time_t t = toki.second();
|
||||
if (t != RTC.get()) RTC.set(t); //set RTC to NTP/UI-provided value
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
|
||||
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
|
||||
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
|
||||
*/
|
||||
void addToConfig(JsonObject& root)
|
||||
{
|
||||
JsonObject top = root.createNestedObject("RTC");
|
||||
JsonArray pins = top.createNestedArray("pin");
|
||||
pins.add(HW_PIN_SCL);
|
||||
pins.add(HW_PIN_SDA);
|
||||
}
|
||||
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_RTC;
|
||||
}
|
||||
};
|
76
usermods/RelayBlinds/index.htm
Normal file
76
usermods/RelayBlinds/index.htm
Normal file
@@ -0,0 +1,76 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<meta charset="utf-8">
|
||||
<title>Blinds</title>
|
||||
<script>
|
||||
strA = "";
|
||||
function send()
|
||||
{
|
||||
nocache = "&nocache=" + Math.random() * 1000000;
|
||||
var request = new XMLHttpRequest();
|
||||
// send HTTP request
|
||||
request.open("GET", "win/" + strA +nocache, true);
|
||||
request.send(null);
|
||||
strA = "";
|
||||
}
|
||||
function up()
|
||||
{
|
||||
strA = "&U0=2";
|
||||
send();
|
||||
}
|
||||
function down()
|
||||
{
|
||||
strA = "&U0=1";
|
||||
send();
|
||||
}
|
||||
function OpenSettings()
|
||||
{
|
||||
window.open("/settings", "_self");
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
body {
|
||||
text-align: center;
|
||||
background: linear-gradient(45deg,#0ca,#0ac);
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
}
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
svg {
|
||||
width: 30vw;
|
||||
padding: 2vh;
|
||||
}
|
||||
.tool_box {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
</style>
|
||||
<style id="holderjs-style" type="text/css"></style></head>
|
||||
<body class=" __plain_text_READY__">
|
||||
<svg style="position: absolute; width: 0; height: 0; overflow: hidden;" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<symbol id="icon-box-add" viewBox="0 0 32 32">
|
||||
<path d="M26 2h-20l-6 6v21c0 0.552 0.448 1 1 1h30c0.552 0 1-0.448 1-1v-21l-6-6zM16 26l-10-8h6v-6h8v6h6l-10 8zM4.828 6l2-2h18.343l2 2h-22.343z"></path>
|
||||
</symbol>
|
||||
<symbol id="icon-box-remove" viewBox="0 0 32 32">
|
||||
<path d="M26 2h-20l-6 6v21c0 0.552 0.448 1 1 1h30c0.552 0 1-0.448 1-1v-21l-6-6zM20 20v6h-8v-6h-6l10-8 10 8h-6zM4.828 6l2-2h18.343l2 2h-22.343z"></path>
|
||||
</symbol>
|
||||
<symbol id="icon-cog" viewBox="0 0 32 32">
|
||||
<path d="M29.181 19.070c-1.679-2.908-0.669-6.634 2.255-8.328l-3.145-5.447c-0.898 0.527-1.943 0.829-3.058 0.829-3.361 0-6.085-2.742-6.085-6.125h-6.289c0.008 1.044-0.252 2.103-0.811 3.070-1.679 2.908-5.411 3.897-8.339 2.211l-3.144 5.447c0.905 0.515 1.689 1.268 2.246 2.234 1.676 2.903 0.672 6.623-2.241 8.319l3.145 5.447c0.895-0.522 1.935-0.82 3.044-0.82 3.35 0 6.067 2.725 6.084 6.092h6.289c-0.003-1.034 0.259-2.080 0.811-3.038 1.676-2.903 5.399-3.894 8.325-2.219l3.145-5.447c-0.899-0.515-1.678-1.266-2.232-2.226zM16 22.479c-3.578 0-6.479-2.901-6.479-6.479s2.901-6.479 6.479-6.479c3.578 0 6.479 2.901 6.479 6.479s-2.901 6.479-6.479 6.479z"></path>
|
||||
</symbol>
|
||||
</defs>
|
||||
</svg>
|
||||
<div id="tbB" class="tool_box">
|
||||
<svg id="upb" onclick="up()"><use xlink:href="#icon-box-remove"></use></svg>
|
||||
<svg id="dnb" onclick="down()"><use xlink:href="#icon-box-add"></use></svg>
|
||||
<svg id="stb" onclick="OpenSettings()"><use xlink:href="#icon-cog"></use></svg>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
1
usermods/RelayBlinds/presets.json
Normal file
1
usermods/RelayBlinds/presets.json
Normal file
@@ -0,0 +1 @@
|
||||
{"0":{},"2":{"n":"▲","win":"U0=2"},"1":{"n":"▼","win":"U0=1"}}
|
8
usermods/RelayBlinds/readme.md
Normal file
8
usermods/RelayBlinds/readme.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# RelayBlinds usermod
|
||||
|
||||
This simple usermod toggles two relay pins momentarily (default for 500ms) when `userVar0` is set.
|
||||
This can be used to e.g. "push" the buttons of a window blinds motor controller.
|
||||
|
||||
v1 usermod. Please replace usermod.cpp in the `wled00` directory with the one in this file.
|
||||
You may upload `index.htm` to `[WLED-IP]/edit` to replace the default lighting UI with a simple Up/Down button one.
|
||||
Also, a simple `presets.json` file is available, this makes the relay actions controllable via two presets to facilitate control e.g. via the default UI or Alexa.
|
83
usermods/RelayBlinds/usermod.cpp
Normal file
83
usermods/RelayBlinds/usermod.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#include "wled.h"
|
||||
|
||||
//Use userVar0 and userVar1 (API calls &U0=,&U1=, uint16_t)
|
||||
|
||||
//gets called once at boot. Do all initialization that doesn't depend on network here
|
||||
void userSetup()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//gets called every time WiFi is (re-)connected. Initialize own network interfaces here
|
||||
void userConnected()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Physical IO
|
||||
*/
|
||||
#define PIN_UP_RELAY 4
|
||||
#define PIN_DN_RELAY 5
|
||||
#define PIN_ON_TIME 500
|
||||
bool upActive = false, upActiveBefore = false, downActive = false, downActiveBefore = false;
|
||||
unsigned long upStartTime = 0, downStartTime = 0;
|
||||
|
||||
void handleRelay()
|
||||
{
|
||||
//up and down relays
|
||||
if (userVar0) {
|
||||
upActive = true;
|
||||
if (userVar0 == 1) {
|
||||
upActive = false;
|
||||
downActive = true;
|
||||
}
|
||||
userVar0 = 0;
|
||||
}
|
||||
|
||||
if (upActive)
|
||||
{
|
||||
if(!upActiveBefore)
|
||||
{
|
||||
pinMode(PIN_UP_RELAY, OUTPUT);
|
||||
digitalWrite(PIN_UP_RELAY, LOW);
|
||||
upActiveBefore = true;
|
||||
upStartTime = millis();
|
||||
DEBUG_PRINTLN("UPA");
|
||||
}
|
||||
if (millis()- upStartTime > PIN_ON_TIME)
|
||||
{
|
||||
upActive = false;
|
||||
DEBUG_PRINTLN("UPN");
|
||||
}
|
||||
} else if (upActiveBefore)
|
||||
{
|
||||
pinMode(PIN_UP_RELAY, INPUT);
|
||||
upActiveBefore = false;
|
||||
}
|
||||
|
||||
if (downActive)
|
||||
{
|
||||
if(!downActiveBefore)
|
||||
{
|
||||
pinMode(PIN_DN_RELAY, OUTPUT);
|
||||
digitalWrite(PIN_DN_RELAY, LOW);
|
||||
downActiveBefore = true;
|
||||
downStartTime = millis();
|
||||
}
|
||||
if (millis()- downStartTime > PIN_ON_TIME)
|
||||
{
|
||||
downActive = false;
|
||||
}
|
||||
} else if (downActiveBefore)
|
||||
{
|
||||
pinMode(PIN_DN_RELAY, INPUT);
|
||||
downActiveBefore = false;
|
||||
}
|
||||
}
|
||||
|
||||
//loop. You can use "if (WLED_CONNECTED)" to check for successful connection
|
||||
void userLoop()
|
||||
{
|
||||
handleRelay();
|
||||
}
|
16
usermods/SN_Photoresistor/platformio_override.ini
Normal file
16
usermods/SN_Photoresistor/platformio_override.ini
Normal file
@@ -0,0 +1,16 @@
|
||||
; Options
|
||||
; -------
|
||||
; USERMOD_SN_PHOTORESISTOR - define this to have this user mod included wled00\usermods_list.cpp
|
||||
; USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL - the number of milliseconds between measurements, defaults to 60 seconds
|
||||
; USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT - the number of milliseconds after boot to take first measurement, defaults to 20 seconds
|
||||
; USERMOD_SN_PHOTORESISTOR_REFERENCE_VOLTAGE - the voltage supplied to the sensor, defaults to 5v
|
||||
; USERMOD_SN_PHOTORESISTOR_ADC_PRECISION - the ADC precision is the number of distinguishable ADC inputs, defaults to 1024.0 (10 bits)
|
||||
; USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE - the resistor size, defaults to 10000.0 (10K hms)
|
||||
; USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE - the offset value to report on, defaults to 25
|
||||
;
|
||||
[env:usermod_sn_photoresistor_d1_mini]
|
||||
extends = env:d1_mini
|
||||
build_flags =
|
||||
${common.build_flags_esp8266}
|
||||
-D USERMOD_SN_PHOTORESISTOR
|
||||
lib_deps = ${env.lib_deps}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user