Compare commits
	
		
			1018 Commits
		
	
	
		
			v0.15.0-b5
			...
			nightly
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 9d706010f5 | ||
|   | 97b20438fd | ||
|   | 65913f990d | ||
|   | 56d00357d3 | ||
|   | 1864e550e6 | ||
|   | 75f6de9dc2 | ||
|   | 16cfbf7500 | ||
|   | aecac2c56c | ||
|   | 385504e6db | ||
|   | a0321170d0 | ||
|   | d70018ae9f | ||
|   | 9359f0b7fc | ||
|   | be74196a62 | ||
|   | 705f2035f4 | ||
|   | 65efcb351e | ||
| ![copilot-swe-agent[bot]](/assets/img/avatar_default.png)  | 72ad39d6a7 | ||
| ![copilot-swe-agent[bot]](/assets/img/avatar_default.png)  | 5950204d34 | ||
|   | 46df9410b3 | ||
| ![copilot-swe-agent[bot]](/assets/img/avatar_default.png)  | b7e4cd0d9a | ||
| ![copilot-swe-agent[bot]](/assets/img/avatar_default.png)  | 0becd61323 | ||
| ![copilot-swe-agent[bot]](/assets/img/avatar_default.png)  | 3f2e92c4c5 | ||
|   | 666a59ff53 | ||
|   | ce7ca3f2d2 | ||
|   | 87092ccb80 | ||
|   | a037d99469 | ||
|   | 8cc5d64819 | ||
|   | 4c948cca13 | ||
|   | 5cb8dc3978 | ||
|   | 8fc87aa17d | ||
|   | c8757d45c8 | ||
|   | 62fad4dcdf | ||
|   | da7f107273 | ||
|   | d5d7fde30f | ||
|   | 6f914d79b1 | ||
|   | dd13c2df47 | ||
|   | 8aeb9e1abe | ||
|   | cfad0b8a52 | ||
|   | a60be251d2 | ||
|   | f15c1fbca6 | ||
| ![copilot-swe-agent[bot]](/assets/img/avatar_default.png)  | 708baf1ed7 | ||
| ![copilot-swe-agent[bot]](/assets/img/avatar_default.png)  | 4155a6bc23 | ||
|   | c92f0a9d90 | ||
|   | 5fa901c37c | ||
|   | 46f3bc0ced | ||
|   | f8ce5980a1 | ||
|   | 4b5c3a396d | ||
|   | 550b4d9dea | ||
|   | f3e3f585df | ||
|   | 2082b01a3c | ||
|   | 8baa6a4616 | ||
|   | 1fb9eb771e | ||
|   | dee581f58d | ||
| ![copilot-swe-agent[bot]](/assets/img/avatar_default.png)  | 7943b00017 | ||
|   | 4de6656bc4 | ||
| ![copilot-swe-agent[bot]](/assets/img/avatar_default.png)  | cd8ddb81e1 | ||
| ![copilot-swe-agent[bot]](/assets/img/avatar_default.png)  | 890860ebf6 | ||
| ![copilot-swe-agent[bot]](/assets/img/avatar_default.png)  | 624042d97e | ||
|   | 85d4db83ed | ||
|   | 5146926723 | ||
|   | 3b5c6ca284 | ||
|   | dcc1fbc96e | ||
|   | 7865985eeb | ||
|   | 4ac7eb7eb2 | ||
|   | 7285efebca | ||
|   | af2d46c30d | ||
|   | f4d89c4196 | ||
|   | c9c442a933 | ||
|   | b8b59b2bb1 | ||
|   | c33e303323 | ||
|   | caf3c7a2f9 | ||
|   | c8d8ab020e | ||
|   | 297d5ced75 | ||
|   | 3f90366aa8 | ||
|   | e3653baf74 | ||
|   | f74d1459b9 | ||
|   | 93e011d403 | ||
|   | 374d90629d | ||
|   | bfe5cd52e7 | ||
|   | e374c7ae55 | ||
|   | 9e4675ef46 | ||
|   | b76ef231fc | ||
|   | 591dbe387c | ||
|   | e5ba97bbe2 | ||
|   | b79e81f3be | ||
|   | ab5b6f9b7d | ||
|   | 4fd1b393a8 | ||
|   | e2f5becdd0 | ||
|   | 71301ddc57 | ||
|   | c30c7e1da5 | ||
|   | 806163f1ed | ||
|   | ecc3eae247 | ||
|   | 07e303bcc1 | ||
|   | 24f2306129 | ||
|   | 79b3bc2573 | ||
|   | e7157e542a | ||
|   | a24420ae70 | ||
|   | b7bfd6fc67 | ||
|   | 8bcd4550f8 | ||
|   | 9569ec7ccf | ||
|   | 61990189de | ||
|   | c0875b36bb | ||
|   | 929a5a8d80 | ||
|   | 731f140b88 | ||
|   | d7d1e929fe | ||
|   | e2800d75f7 | ||
|   | 2c1cf87e08 | ||
|   | e6716fe834 | ||
|   | fc0d64ec78 | ||
|   | 7b0075d375 | ||
|   | e227d01436 | ||
|   | db55fec879 | ||
|   | 9099b13f69 | ||
|   | 0ba05877d7 | ||
|   | b1ed99dfe6 | ||
|   | a2e9e2b7d1 | ||
|   | 5163fbf36b | ||
|   | b187f9427d | ||
|   | 7cc5c87a52 | ||
|   | 1c4141a2b1 | ||
|   | cc81cc27b0 | ||
|   | 6a5dcb3a76 | ||
|   | 796494e925 | ||
|   | 7973fd84f1 | ||
|   | 66869f8341 | ||
|   | 10d1098403 | ||
|   | a041fd1266 | ||
|   | e2fd1559d2 | ||
|   | 9dbd5f89b6 | ||
|   | 0f00c95aba | ||
|   | bbfe90d2ca | ||
|   | 3f03cb4d2b | ||
|   | 05557ca790 | ||
|   | a53baa9b42 | ||
|   | 7eafc01ab9 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 57cc1ce5d0 | ||
|   | 368351bbdd | ||
|   | f9bfcce65d | ||
|   | e97723dcc7 | ||
|   | a6f5080a4f | ||
|   | d2d5c423b7 | ||
|   | 42bf8fb40d | ||
|   | b0dd9690e7 | ||
|   | 65a79d411d | ||
|   | 3a413a59ba | ||
|   | 16a88775c3 | ||
|   | 728b1e8ad4 | ||
|   | 7835550f1a | ||
|   | a50b4f5e3c | ||
|   | 80c40f6afd | ||
|   | f442d58b70 | ||
|   | 201d04910a | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 65f2ced6c2 | ||
|   | 2a0e78c656 | ||
|   | 05f0630b9c | ||
|   | ea231cbea8 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 24e71cc6d5 | ||
|   | 94f226aadf | ||
|   | 00d1fcc5fb | ||
|   | caeda96fbd | ||
|   | 9805ae59d5 | ||
|   | 00eb4068b0 | ||
|   | fc7d4dfcb0 | ||
|   | ab28b6d58f | ||
|   | 4abaf13598 | ||
|   | 8b65d873b3 | ||
|   | a87b562bc2 | ||
|   | 23a51e0982 | ||
|   | 4a3af814bf | ||
|   | db22936153 | ||
|   | a147f4120c | ||
|   | dcd3e07273 | ||
|   | f26733cd8c | ||
|   | f3623158d7 | ||
|   | e80a7c6b75 | ||
|   | 309c8d67f3 | ||
|   | 75c95d88e2 | ||
|   | 0a7d3a9d9b | ||
|   | aa28769e71 | ||
|   | 75cd411073 | ||
|   | 792a7aa081 | ||
|   | 7ea510e75b | ||
|   | 242df4b049 | ||
|   | 358e38e056 | ||
|   | c693f63244 | ||
|   | 817157bbc1 | ||
|   | ac61eb4b1b | ||
|   | 24ab2952ee | ||
|   | 999637f8ad | ||
|   | 25223c446f | ||
|   | 66ad27ad3a | ||
|   | d9ad4ec743 | ||
|   | d381108dc0 | ||
|   | cbe7d0678b | ||
|   | 42d9a41cf5 | ||
|   | b5a710dbe4 | ||
|   | 40653b0d6f | ||
|   | 608aff1e17 | ||
|   | 5fe766399b | ||
|   | 891115ecee | ||
|   | 0fe722e478 | ||
|   | ee3864175d | ||
|   | 849d5e6667 | ||
|   | a8dd2435ec | ||
|   | d9b086cbe9 | ||
|   | 6464c620c7 | ||
|   | 7998650e60 | ||
|   | d10714d1c1 | ||
|   | f721efca1e | ||
|   | 125a21da75 | ||
|   | c934776f45 | ||
|   | f1c88bc38d | ||
|   | 92db9e0e00 | ||
|   | 19ba257722 | ||
|   | 6c4d049c1a | ||
|   | fbb7ef7cfc | ||
|   | b77881f634 | ||
|   | 7852ff558e | ||
|   | adb9b773b0 | ||
|   | 410025f30f | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | f1d52a8ec1 | ||
|   | 7f2b6a3f10 | ||
|   | 0f321bfb38 | ||
|   | 10b925acb7 | ||
|   | ee9ac947a1 | ||
|   | 8baf0fdef7 | ||
|   | 353868414a | ||
|   | c661d8f5b7 | ||
|   | ca7d7d9369 | ||
|   | 02f14baad4 | ||
|   | 88aa8e8178 | ||
|   | e979c58c98 | ||
|   | 81b74227fa | ||
|   | 9e96bd6705 | ||
|   | 5203c3927f | ||
|   | bc099baeb1 | ||
|   | 646ec30ae5 | ||
|   | dc5eaf3ae9 | ||
|   | b941654a68 | ||
|   | 6f5482782b | ||
|   | 8a6e3a9898 | ||
|   | 354a0aef52 | ||
|   | b0b3196e52 | ||
|   | 0139c34ec2 | ||
|   | ff3680813c | ||
|   | 7db52d794b | ||
|   | fbbb369fa4 | ||
|   | f719ee5b18 | ||
|   | c3ab562258 | ||
|   | 62d3e155bd | ||
|   | fab80f4e4e | ||
|   | 1cd3a97c51 | ||
|   | ff99c7de70 | ||
|   | e2c919d270 | ||
|   | 035d4aed26 | ||
|   | d03604b14a | ||
|   | 468ef50f75 | ||
|   | 16525c2f3f | ||
|   | af1a966986 | ||
|   | 1492f1ce89 | ||
|   | 7e87891701 | ||
|   | e76e9a3e1f | ||
|   | bfe6fa1e5d | ||
|   | cf391034da | ||
|   | 36cb1cad36 | ||
|   | 22e2b6f3c5 | ||
|   | 9c8f8c645e | ||
|   | e21a09cec9 | ||
|   | a0d1a8cbc4 | ||
|   | 86393e0b1d | ||
|   | f328713710 | ||
|   | ed6efe4c9e | ||
|   | befff2f034 | ||
|   | 630315180d | ||
|   | a70bfa0c89 | ||
|   | d2b7e474d6 | ||
|   | 494b72c287 | ||
|   | 229e7b940f | ||
|   | 95dcb03f6d | ||
|   | a6d9a8220c | ||
|   | 0ba80ce61e | ||
|   | 7c23872e03 | ||
|   | 507938be35 | ||
|   | 8bee304e4d | ||
|   | be239993f4 | ||
|   | f396dac1cc | ||
|   | ebba1f78ab | ||
|   | c8c3fc2bcc | ||
|   | 14c6c05e00 | ||
|   | 3fbdd9635f | ||
|   | d7be9c9a66 | ||
|   | 928c9fbacd | ||
|   | 2b666ab9f7 | ||
|   | 4ecc531998 | ||
|   | b908384ba2 | ||
|   | b27a4a94e4 | ||
|   | 5e8073022b | ||
|   | 9553425374 | ||
|   | 2012317bc9 | ||
|   | 18a118bb7a | ||
|   | e00789f838 | ||
|   | 20563e6306 | ||
|   | 580c2d8213 | ||
|   | c9b8789ea9 | ||
|   | 4271588ecd | ||
|   | a5277ff4a0 | ||
|   | a66f5d660d | ||
|   | 80061e8d76 | ||
|   | d07a057f04 | ||
|   | fa2949af7d | ||
|   | a4680b7216 | ||
|   | 523eb910a6 | ||
|   | 2f21de79f7 | ||
|   | 092d402ff8 | ||
|   | 12e8d6c4c6 | ||
|   | 8c1486d577 | ||
|   | 57e9199baa | ||
|   | f4aac0bd8d | ||
|   | 664150811a | ||
|   | 3cb4ae16ab | ||
|   | 73fc9ea54d | ||
|   | dd80919ed6 | ||
|   | d7103cd75c | ||
|   | 29beee367c | ||
|   | d22fe9227b | ||
|   | 6ea9535463 | ||
|   | bbd1b730b1 | ||
|   | 6e138e4f79 | ||
|   | d6453bb53d | ||
|   | 9bfd34e6b2 | ||
|   | 7b13771cfe | ||
|   | b70b222f80 | ||
|   | 2ffc8dd228 | ||
|   | 08d12f9ee6 | ||
|   | 7ce96c190f | ||
|   | 409b4b1963 | ||
|   | 05cd7d753e | ||
|   | 7be460e8b1 | ||
|   | 6dfbd07e86 | ||
|   | 7b56e53c47 | ||
|   | a8cde3289a | ||
|   | f4e7202dd0 | ||
|   | a2de3d33bf | ||
|   | 46a3e3d353 | ||
|   | 756e27fb52 | ||
|   | 1d6e5a2464 | ||
|   | dc77428df4 | ||
|   | b293356cec | ||
|   | 1dc7647bb9 | ||
|   | c3f6537a4d | ||
|   | 70a3dc938a | ||
|   | 1fd703aff9 | ||
|   | aa3fb7d165 | ||
|   | 2b9ff3148c | ||
|   | 7f24269511 | ||
|   | b34d65fce0 | ||
|   | 778cecb512 | ||
|   | e7e0eb0f32 | ||
|   | a0c55c6406 | ||
|   | aba736cb96 | ||
|   | bdec873fed | ||
|   | 2cc73660bf | ||
|   | f3de45c6ad | ||
|   | 386e2c6306 | ||
|   | 2473065b98 | ||
|   | ed91c54654 | ||
|   | 4d53e0adde | ||
|   | 35f87365c9 | ||
|   | 8e7d6d5dad | ||
|   | 95a10c692c | ||
|   | 3a31d5d49e | ||
|   | 77d7082ffc | ||
|   | c43d09c8b1 | ||
|   | d92e60ee5f | ||
|   | b9ceacb43d | ||
|   | 2fe809f15a | ||
|   | 078a054dbd | ||
|   | c57be77039 | ||
|   | e6910f732f | ||
|   | d0b599781d | ||
|   | 2431f2058b | ||
|   | 8c717537c4 | ||
|   | e088f4654a | ||
|   | b363b6151c | ||
|   | 3baa4f8223 | ||
|   | 373f4cfefd | ||
|   | 64a02b705a | ||
|   | f72b5d04e8 | ||
|   | 1688546519 | ||
|   | 2eff6b7a3a | ||
|   | 58962f8470 | ||
|   | dfd7ff5b39 | ||
|   | d56ded8c18 | ||
|   | af3ebbb3c2 | ||
|   | 1db3359b84 | ||
|   | 7a40ef74c6 | ||
|   | b64cd36468 | ||
|   | b3f9983f44 | ||
|   | 070b08a9e6 | ||
|   | 851e9ece03 | ||
|   | 51db63dff7 | ||
|   | a5b972d87e | ||
|   | 4bc3408410 | ||
|   | 5d392d89ce | ||
|   | 86f97614b0 | ||
|   | 5b7bab6752 | ||
|   | 70042db2de | ||
|   | bf69d37cbe | ||
|   | ee7ec20f29 | ||
|   | 1e1ba9afa3 | ||
|   | 32a75c1ff5 | ||
|   | 61b99471a1 | ||
|   | e27fa882fa | ||
|   | 7c05914e5a | ||
|   | daa438b349 | ||
|   | 35624ab998 | ||
|   | 2d6ad41ed4 | ||
|   | 8544bdd881 | ||
|   | a778ff01f6 | ||
|   | e7c0ce794b | ||
|   | 4c50119ac2 | ||
|   | 2181618357 | ||
|   | bec7e54f7f | ||
|   | dc317220b3 | ||
|   | 981750a48a | ||
|   | 1df717084b | ||
|   | f2caf14d6a | ||
|   | 199529a031 | ||
|   | 99108f9eff | ||
|   | 74672e2130 | ||
|   | 7d48bba926 | ||
|   | 5f19608e41 | ||
|   | 8d4c9119b4 | ||
|   | b1b2eead26 | ||
|   | b3af04d3ca | ||
|   | 04c7eace09 | ||
|   | 0e7d5dd013 | ||
|   | 27d3420ad7 | ||
|   | c9672b35ce | ||
|   | 39512da74e | ||
|   | 0d44e7ec27 | ||
|   | 24082d169b | ||
|   | 3debaf0f41 | ||
|   | 2448266d7c | ||
|   | 3a426e258b | ||
|   | b062d1ee3e | ||
|   | ebc171d405 | ||
|   | 4951be6999 | ||
|   | 01a71132d5 | ||
|   | a421a90e0a | ||
|   | 1d558e8391 | ||
|   | 30a697e708 | ||
|   | 0233daeda2 | ||
|   | 48372bcd91 | ||
|   | 44a1a1ebde | ||
|   | 2b07be1d09 | ||
|   | a98685d89e | ||
|   | 1c4ba20646 | ||
|   | d3954b94d3 | ||
|   | 7daea18907 | ||
|   | 0c84235a95 | ||
|   | aab29cb0ab | ||
|   | 566c5057f9 | ||
|   | cd52d7bcf6 | ||
|   | ed3ec66d33 | ||
|   | 01c463c8e8 | ||
|   | 872465df40 | ||
|   | b6f74287d0 | ||
|   | 013684b5ca | ||
|   | 90c2955a71 | ||
|   | 7be868db12 | ||
|   | 703f84e5e1 | ||
|   | c92dbb10ac | ||
|   | 8ee2c44550 | ||
|   | 48f5099646 | ||
|   | b9aeb19834 | ||
|   | 193926c795 | ||
|   | a4c3491f0c | ||
|   | ad65856b3d | ||
|   | b2aac9f991 | ||
|   | a582786655 | ||
|   | 953e994c88 | ||
|   | 9a6e91d3e5 | ||
|   | fc4e7a2dee | ||
|   | a56014bb66 | ||
|   | ebfc438bd4 | ||
|   | 3996f02dea | ||
|   | d637524bfc | ||
|   | 8570922dcc | ||
|   | 6598265f9b | ||
|   | 8f398dfd08 | ||
|   | 68e9d701de | ||
|   | 67e8a00b6d | ||
|   | 11b48bc374 | ||
|   | fa80c62b28 | ||
|   | 7f9cc67518 | ||
|   | 84eb6fd460 | ||
|   | 2cc5a29b86 | ||
|   | b178c08271 | ||
|   | 9a3b208ac5 | ||
|   | 2989155f05 | ||
|   | 50b56c64f5 | ||
|   | be3e331afb | ||
|   | 9d8fdd0b20 | ||
|   | 033c7abe62 | ||
|   | 5525a21696 | ||
|   | aed03cd03b | ||
|   | 5a5661f136 | ||
|   | a3bcf92ea5 | ||
|   | 0ad31c90f6 | ||
|   | 789d68e80d | ||
|   | f06a1e8b49 | ||
|   | a0ca243955 | ||
|   | 9e2268bd74 | ||
|   | 702d085117 | ||
|   | 356a0d72c3 | ||
|   | 7fcc4a5283 | ||
|   | 15edfcd088 | ||
|   | 278d204d1c | ||
|   | 39b3e7e507 | ||
|   | 8487dd7cfd | ||
|   | 5d05d7936c | ||
|   | 0afd2fe720 | ||
|   | 59a79a30da | ||
|   | 5da380e1b0 | ||
|   | 2adf745d06 | ||
|   | ef2eb0764c | ||
|   | 9e37d7051c | ||
|   | 05098c3382 | ||
|   | 32607ee74c | ||
|   | 30559cd2d3 | ||
|   | 8fd905215f | ||
|   | 270d75afe2 | ||
|   | a65f97ac75 | ||
|   | 53c1856038 | ||
|   | 56b8af86d7 | ||
|   | e852df3179 | ||
|   | 7c03f716a8 | ||
|   | d4ba603cf7 | ||
|   | deb0306347 | ||
|   | 881da25e8c | ||
|   | a37b953e72 | ||
|   | bba5188594 | ||
|   | 7dc633581d | ||
|   | bd00d012e2 | ||
|   | 4f4476b79f | ||
|   | 0b8721c25e | ||
|   | 869e275e48 | ||
|   | e4714870a4 | ||
|   | 1dbd7066de | ||
|   | 022e4986ee | ||
|   | 2c9c413e79 | ||
|   | b380d5e2c7 | ||
|   | 650853c177 | ||
|   | ae698f988a | ||
|   | f920fdecfe | ||
|   | 4e4f823141 | ||
|   | b421f7ae87 | ||
|   | ca80d0489b | ||
|   | 1ed82426a1 | ||
|   | 7e9f7d4101 | ||
|   | f240a33935 | ||
|   | 5bd0a26126 | ||
|   | cc011e39ce | ||
|   | 29ee551b06 | ||
|   | 5b5e4157e3 | ||
|   | 3c19692312 | ||
|   | 24accf96a8 | ||
|   | f2626b0fc0 | ||
|   | cc9e9b109c | ||
|   | adead9b578 | ||
|   | 8527d231e1 | ||
|   | 4c19341279 | ||
|   | 075fd4da2d | ||
|   | 52bee88ad2 | ||
|   | 52b784e0e5 | ||
|   | 3521732597 | ||
|   | d3eec72e45 | ||
|   | 79bac912aa | ||
|   | 2381e323c1 | ||
|   | d64cedd3fc | ||
|   | c16d83fab0 | ||
|   | cbed841414 | ||
|   | 67022beca0 | ||
|   | 6e76a72d78 | ||
|   | a5575bc3a0 | ||
|   | 90b18158fc | ||
|   | 71b0e8e937 | ||
|   | 4d5e0ca7a3 | ||
|   | b8685f2c39 | ||
|   | c4e697d797 | ||
|   | 3502a39181 | ||
|   | 4a56c92e7b | ||
|   | 2aab617c2e | ||
|   | 471bd83eb2 | ||
|   | ec7a7f4c25 | ||
|   | bb0c0af189 | ||
|   | 709aeff9ea | ||
|   | 34f18122f5 | ||
|   | 7208282431 | ||
|   | 204e72e9eb | ||
|   | f626dfb7b7 | ||
|   | 08b263bf4e | ||
|   | d0e99923fd | ||
|   | 1750512477 | ||
|   | 5e3b4c3a11 | ||
|   | 0c431d9746 | ||
|   | 438c5d9909 | ||
|   | 7d29edf6f4 | ||
|   | 50d505b896 | ||
|   | 2e06f5b1e8 | ||
|   | 3adcbb7904 | ||
|   | 27e98147ef | ||
|   | 48958cc638 | ||
|   | ae4de2782a | ||
|   | dcf89e0dbd | ||
|   | 35d92f43c0 | ||
|   | 12db60885f | ||
|   | d637260dc3 | ||
|   | 0937064e18 | ||
|   | 54264efb20 | ||
|   | 6a1d3de75b | ||
|   | 3fc8c7d560 | ||
|   | 56e1d577fd | ||
|   | 272129f66c | ||
|   | 0441ede229 | ||
|   | f001846e00 | ||
|   | 0ac627dfbb | ||
|   | 1a82a3bf7b | ||
|   | a2d84886c0 | ||
|   | 97e8382a41 | ||
|   | 88738327fd | ||
|   | 0ad65f4748 | ||
|   | 9dad436f72 | ||
|   | ebdc38fff2 | ||
|   | 97bbe6f305 | ||
|   | 2f0dbef1e5 | ||
|   | 1711286ef0 | ||
|   | 099d3f7b41 | ||
|   | 5f77478841 | ||
|   | 07cc3aa5c0 | ||
|   | ff26f54bfd | ||
|   | 3323d2ed37 | ||
|   | 7b9b3f1ee2 | ||
|   | cae98451e3 | ||
|   | 83da7569f5 | ||
|   | 26397ee8ad | ||
|   | dcfebcb973 | ||
|   | fd3b47908b | ||
|   | 217d2aeb7f | ||
|   | e57c701837 | ||
|   | b4aa8376de | ||
|   | d4976ac47a | ||
|   | b8a96d9a77 | ||
|   | f75d582eee | ||
|   | 18e0ec9a55 | ||
|   | 68b80cdadc | ||
|   | 3d3c475d1b | ||
|   | 6668e72351 | ||
|   | b72695f01a | ||
|   | 65f98c1f30 | ||
|   | c6fd4c51cf | ||
|   | 3c11c8441f | ||
|   | 3261c5b071 | ||
|   | c8625c70dd | ||
|   | 396e9d0c39 | ||
|   | e16d3bf040 | ||
|   | 2c58a87982 | ||
|   | a705ae5278 | ||
|   | a426e93011 | ||
|   | 1c220d25ca | ||
|   | 039858dca2 | ||
|   | 4758b5efe8 | ||
|   | 36e065ab4d | ||
|   | 076497e14d | ||
|   | e8d9891d13 | ||
|   | 4902d7fb9e | ||
|   | a873ca6a3e | ||
|   | a86cb27cfe | ||
|   | d620930f10 | ||
|   | 8db8ecfef3 | ||
|   | a0f99393f5 | ||
|   | ae8c3b02d0 | ||
|   | 99427c2ef7 | ||
|   | 334f16c0b6 | ||
|   | 7a80a772c1 | ||
|   | 8b1d712e1e | ||
|   | a121f5b61b | ||
|   | 251062170e | ||
|   | 92e59af4d8 | ||
|   | fa4c23b76e | ||
|   | a0a46850f5 | ||
|   | 4cd0563a93 | ||
|   | b83f0f461c | ||
|   | 3668ede0ff | ||
|   | 945584384a | ||
|   | b1dd27b516 | ||
|   | dcba1aad10 | ||
|   | acc8b9cdbc | ||
|   | 6cbdd825eb | ||
|   | cc55f6015d | ||
|   | 95718ab6ec | ||
|   | feab27295d | ||
|   | ca176c7549 | ||
|   | f8a7a0d6e8 | ||
|   | 0a05611e1d | ||
|   | 1a8aaa3b26 | ||
|   | cd1c13b4b1 | ||
|   | d87c5035dd | ||
|   | 77967731d5 | ||
|   | 0b54034470 | ||
|   | 8ad2583785 | ||
|   | dd533a9ab4 | ||
|   | 7236589037 | ||
|   | 2c583c3071 | ||
|   | 5b989adebc | ||
|   | 855e606163 | ||
|   | 20f8d3c8a9 | ||
|   | 0c77dbb7ea | ||
|   | 2448e2ae3b | ||
|   | d53d7aa2e2 | ||
|   | 0be1df7ee8 | ||
|   | 6aef0e145c | ||
|   | 4f1965fbaa | ||
|   | 32dc54ce72 | ||
|   | 548736f432 | ||
|   | 6790f8af08 | ||
|   | 89d587e7dd | ||
|   | 5c8b2ebf7a | ||
|   | 5ac8ba9bae | ||
|   | 8f8afd98a5 | ||
|   | 49fb16e2c6 | ||
|   | 0db47a8586 | ||
|   | cec8978886 | ||
|   | 0160e3fa87 | ||
|   | 545bfa6ef9 | ||
|   | c596b5a17d | ||
|   | 84dd26c1b7 | ||
|   | a765903a41 | ||
|   | 25ab381916 | ||
|   | 4d3df5d98f | ||
|   | 6fe2024542 | ||
|   | 7f69a0bc5e | ||
|   | 5b829adedb | ||
|   | 4cd4c13b2d | ||
|   | 86be5df475 | ||
|   | 8c5e0cd4e9 | ||
|   | a9b0b8adc8 | ||
|   | ba01cb82f7 | ||
|   | 223b97b884 | ||
|   | d437027f26 | ||
|   | 9a564ee204 | ||
|   | d37ee89e84 | ||
|   | 5de86d3d91 | ||
|   | 4b6041302e | ||
|   | 536444f9d1 | ||
|   | ba5ec57e4d | ||
|   | 9fa53ccf05 | ||
|   | 5c2bac4b9d | ||
|   | ef1e24cec2 | ||
|   | 001e2ad287 | ||
|   | 0404ec9881 | ||
|   | 6ff5c88ebf | ||
|   | 1e761c31bd | ||
|   | ab7b2d729e | ||
|   | 271a07a7d6 | ||
|   | 029293a086 | ||
|   | cf1630a94a | ||
|   | 4634ace74e | ||
|   | 3733715184 | ||
|   | bf37ac53a3 | ||
|   | 50934e6840 | ||
|   | 891ea48e11 | ||
|   | 70323b9477 | ||
|   | 451cd4c74a | ||
|   | 7d0951a08a | ||
|   | d98ca9a202 | ||
|   | 3c2c5bedc5 | ||
|   | 749d34cd30 | ||
|   | 1898be2fe1 | ||
|   | af34da4160 | ||
|   | fa053b7e60 | ||
|   | 4588219e31 | ||
|   | 6e89346f00 | ||
|   | 2703c9899a | ||
|   | d05c358fd2 | ||
|   | 7c6bc5c421 | ||
|   | 798c398f23 | ||
|   | 4cc2cc4ad4 | ||
|   | dcfdca6351 | ||
|   | 832599b8c5 | ||
|   | c81ef2669e | ||
|   | 19d837c222 | ||
|   | 4f48ddfaec | ||
|   | 2e01fe0b5b | ||
|   | 7d067d8c30 | ||
|   | b3b326738c | ||
|   | 2bb2caf2d2 | ||
|   | 2a094883ad | ||
|   | 6d1126b8aa | ||
|   | 26a47537f9 | ||
|   | 01d43c69fb | ||
|   | 7db1989093 | ||
|   | 32eee3365a | ||
|   | 95b4bde918 | ||
|   | 0a97e28aab | ||
|   | 4fa8a3898a | ||
|   | caa997fff1 | ||
|   | bd68b977d5 | ||
|   | 98a6907976 | ||
|   | e9d2182390 | ||
|   | 5e29f2c1b7 | ||
|   | 44e28f96e0 | ||
|   | a0e81da8c5 | ||
|   | 85a7c3c60d | ||
|   | 01e07ca0bc | ||
|   | 1468ee5fde | ||
|   | 49f044ecde | ||
|   | 37f32ab197 | ||
|   | a60231ba59 | ||
|   | c8dafede6d | ||
|   | 210191b251 | ||
|   | 7deea9eb75 | ||
|   | 5e9a46d54d | ||
|   | be64930ebb | ||
|   | 488974dd3e | ||
|   | 5975b9125f | ||
|   | f301296f1e | ||
|   | 407477dc68 | ||
|   | eb5ad232a0 | ||
|   | 1b0ce9a123 | ||
|   | 7b855c851d | ||
|   | ca062140f3 | ||
|   | a15c391e6c | ||
|   | ba636b17a0 | ||
|   | 2a07eb84f6 | ||
|   | 949b9fb10e | ||
|   | ae1b6af0d4 | ||
|   | dd27504d30 | ||
|   | c30a08cfc5 | ||
|   | a4c49aa35e | ||
|   | ba3a61f623 | ||
|   | 402fba734a | ||
|   | 262af0678f | ||
|   | 3765d558b6 | ||
|   | 4ed8ded502 | ||
|   | ee380c5377 | ||
|   | 7fa25ca7ae | ||
|   | d3c401ed4e | ||
|   | cb8dae1ddb | ||
|   | 59deebc961 | ||
|   | 0ae73296cf | ||
|   | 8e78fb4caa | ||
|   | 336da25463 | ||
|   | ffbc8c5f70 | ||
|   | 10d8cfde85 | ||
|   | 3c7f83407b | ||
|   | fe4b668107 | ||
|   | cc87b32206 | ||
|   | 9114867578 | ||
|   | 6f221852a2 | ||
|   | c842994df5 | ||
|   | 202901b09f | ||
|   | 7c0fe1285a | ||
|   | 6dc2c680c5 | ||
|   | 9a4b56db6e | ||
|   | 8180f2c742 | ||
|   | a76a895f1d | ||
|   | b404458369 | ||
|   | c44b9f8659 | ||
|   | bef1ac2668 | ||
|   | 906f8fc2e7 | ||
|   | c600c6da63 | ||
|   | e789a18553 | ||
|   | 3f3c986932 | ||
|   | 3ccc5babc1 | ||
|   | fafb2eba69 | ||
|   | 33cf82a982 | ||
|   | bd7cd32f91 | ||
|   | 0a5400263b | ||
|   | 17d59d3337 | ||
|   | 9cb3531e2d | ||
|   | b50e6e0d90 | ||
|   | a88436c620 | ||
|   | 45cf90094a | ||
|   | ae1df20893 | ||
|   | 71148740d4 | ||
|   | 4ef583c844 | ||
|   | 16f61ea96d | ||
|   | 32f9616b6e | ||
|   | 1346eb4f76 | ||
|   | d4268ba070 | ||
|   | 696290527a | ||
|   | 686866c6f9 | ||
|   | 72455ccde1 | ||
|   | 88fb860568 | ||
|   | ceed494cf7 | ||
|   | ac8f919304 | ||
|   | 7f1ec4802d | ||
|   | d5777b7bce | ||
|   | e34f179a82 | ||
|   | 6a90b9a512 | ||
|   | ce5ee65d60 | ||
|   | 65a8dbfe41 | ||
|   | 1ff667b7ef | ||
|   | 0806c7fbdb | ||
|   | 5708d7a6b8 | ||
|   | 2264cc5d10 | ||
|   | 28cb3f9d0c | ||
|   | f16ed4697f | ||
|   | 81e412fe5b | ||
|   | f3137eb0a9 | ||
|   | 0e5bd4ed74 | ||
|   | 6a37f25c5d | ||
|   | 2afff05014 | ||
|   | e3d9417b84 | ||
|   | 6a188033c6 | ||
|   | df24fd7bf2 | ||
|   | d45b4ad134 | ||
|   | ec938f254c | ||
|   | fa82e759bd | ||
|   | daf0bcfac3 | ||
|   | 09428dcade | ||
|   | 49d51c5af8 | ||
|   | ec7f69d559 | ||
|   | a8c5bf2573 | ||
|   | f12de61d7f | ||
|   | 71fcd6482d | ||
|   | e59b38a5d2 | ||
|   | b07658b460 | ||
|   | 5b8d9d1510 | ||
|   | 6169c458bf | ||
|   | 23400d0449 | ||
|   | 992d11be10 | ||
|   | feac45fd0a | ||
|   | 934176818f | ||
|   | c5435ec1fa | ||
|   | c3f472fbcb | ||
|   | 1cee1c3562 | ||
|   | eae5a74a11 | ||
|   | 5df1a223c2 | ||
|   | 81382a4bc0 | ||
|   | 750a429bd4 | ||
|   | dbe78dff93 | ||
|   | 5869627b32 | ||
|   | 5bc5ef0ae8 | ||
|   | 2cfd2e1410 | ||
|   | cce5e8f811 | ||
|   | 058e66c7fc | ||
|   | 8ae09c3d02 | ||
|   | 83699bc5c3 | ||
|   | eb06e575c2 | ||
|   | b4315152e2 | ||
|   | c5b31e2f63 | ||
|   | ac689a9a2e | ||
|   | 43cc4ec009 | ||
|   | 9cc333b2bd | ||
|   | 55d98ff935 | ||
|   | dc90a4ed42 | ||
|   | ef80abd885 | ||
|   | 092a5a1ca3 | ||
|   | d3c31c52a2 | ||
|   | 329173e145 | ||
|   | 8e4f8fcbc8 | ||
|   | b1e58dd10d | ||
|   | 2cc0aceac7 | ||
|   | d79d5dbadd | ||
|   | ebd8a10cef | ||
|   | be7475fe66 | ||
|   | 85e2f7eb34 | ||
|   | 9bb979f2e8 | ||
|   | c942345453 | ||
|   | fcc344ba99 | ||
|   | a3b28871c6 | ||
|   | 2ca034bfa8 | ||
|   | 0d035a08d6 | ||
|   | 6655e2664e | ||
|   | c51ce2eec7 | ||
|   | b2e00eb868 | ||
|   | 820df0c596 | ||
|   | 865e3dcd0c | ||
|   | f55e2fc7b8 | ||
|   | 8818a9e82a | ||
|   | dbb47d506c | ||
|   | 6f3267aee9 | ||
|   | 0430dc5a1f | ||
|   | 011afd0910 | ||
|   | 77723b615f | ||
|   | e1598a9966 | ||
|   | 365c1987ed | ||
|   | c03422ee37 | ||
|   | 1975c9c34a | ||
|   | d88bb3c668 | ||
|   | 13ed78be96 | ||
|   | 0275bd1d45 | ||
|   | f3891c305d | ||
|   | 593970ed6d | ||
|   | bee75a4508 | ||
|   | a4ac444bda | ||
|   | 296df2660a | ||
|   | b9849da66e | ||
|   | da484b07f5 | ||
|   | 20c0916adc | ||
|   | de5632b1cf | ||
|   | ccce0f2d3b | ||
|   | f441ce9c17 | ||
|   | 8e8ffa30a6 | ||
|   | 9735d1c6f3 | ||
|   | ef017fd343 | ||
|   | b3810a16cc | ||
|   | a3a8fa1cef | ||
|   | c6805271e9 | ||
|   | f5199d2b73 | ||
|   | 0c8d9d5614 | ||
|   | 247de600af | ||
|   | 3e60d3d96e | ||
|   | b2afac8914 | ||
|   | 3ad56ea103 | ||
|   | 1f25edc737 | 
| @@ -2,12 +2,7 @@ | ||||
|  | ||||
| # [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 | ||||
| FROM mcr.microsoft.com/devcontainers/python:0-${VARIANT} | ||||
|  | ||||
| # [Optional] If your pip requirements rarely change, uncomment this section to add them to the image. | ||||
| # COPY requirements.txt /tmp/pip-tmp/ | ||||
|   | ||||
| @@ -5,10 +5,7 @@ | ||||
| 		"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/*" | ||||
| 			"VARIANT": "3" | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| @@ -27,34 +24,35 @@ | ||||
| 	// risk to running the build directly on the host. | ||||
| 	// "runArgs": ["--privileged", "-v", "/dev/bus/usb:/dev/bus/usb", "--group-add", "dialout"], | ||||
|  | ||||
| 	// 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" | ||||
| 	"customizations": { | ||||
| 		"vscode": { | ||||
| 			"settings": {  | ||||
| 				"terminal.integrated.shell.linux": "/bin/bash", | ||||
| 				"python.pythonPath": "/usr/local/bin/python", | ||||
| 				"python.linting.enabled": true, | ||||
| 				"python.linting.pylintEnabled": true, | ||||
| 				"python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8", | ||||
| 				"python.formatting.blackPath": "/usr/local/py-utils/bin/black", | ||||
| 				"python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf", | ||||
| 				"python.linting.banditPath": "/usr/local/py-utils/bin/bandit", | ||||
| 				"python.linting.flake8Path": "/usr/local/py-utils/bin/flake8", | ||||
| 				"python.linting.mypyPath": "/usr/local/py-utils/bin/mypy", | ||||
| 				"python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle", | ||||
| 				"python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle", | ||||
| 				"python.linting.pylintPath": "/usr/local/py-utils/bin/pylint" | ||||
| 			}, | ||||
| 			"extensions": [ | ||||
| 				"ms-python.python", | ||||
| 				"platformio.platformio-ide" | ||||
| 			] | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	// 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", | ||||
| 	"postCreateCommand": "bash -i -c 'nvm install && npm ci'", | ||||
|  | ||||
| 	// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. | ||||
| 	"remoteUser": "vscode" | ||||
|   | ||||
							
								
								
									
										3
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,2 +1,3 @@ | ||||
| github: [Aircoookie,blazoncek] | ||||
| github: [Aircoookie,blazoncek,DedeHai,lost-hope,willmmiles] | ||||
| custom: ['https://paypal.me/Aircoookie','https://paypal.me/blazoncek'] | ||||
| thanks_dev: u/gh/netmindz | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/ISSUE_TEMPLATE/bug.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/ISSUE_TEMPLATE/bug.yml
									
									
									
									
										vendored
									
									
								
							| @@ -80,7 +80,7 @@ body: | ||||
|     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) | ||||
|       description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/wled-dev/WLED/blob/main/CODE_OF_CONDUCT.md) | ||||
|       options: | ||||
|         - label: I agree to follow this project's Code of Conduct | ||||
|           required: true | ||||
|   | ||||
							
								
								
									
										138
									
								
								.github/copilot-instructions.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								.github/copilot-instructions.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,138 @@ | ||||
| # WLED - ESP32/ESP8266 LED Controller Firmware | ||||
|  | ||||
| WLED is a fast and feature-rich implementation of an ESP32 and ESP8266 webserver to control NeoPixel (WS2812B, WS2811, SK6812) LEDs and SPI-based chipsets. The project consists of C++ firmware for microcontrollers and a modern web interface. | ||||
|  | ||||
| Always reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here. | ||||
|  | ||||
| ## Working Effectively | ||||
|  | ||||
| ### Initial Setup | ||||
| - Install Node.js 20+ (specified in `.nvmrc`): Check your version with `node --version` | ||||
| - Install dependencies: `npm ci` (takes ~5 seconds) | ||||
| - Install PlatformIO for hardware builds: `pip install -r requirements.txt` (takes ~60 seconds) | ||||
|  | ||||
| ### Build and Test Workflow | ||||
| - **ALWAYS build web UI first**: `npm run build` -- takes 3 seconds. NEVER CANCEL. | ||||
| - **Run tests**: `npm test` -- takes 40 seconds. NEVER CANCEL. Set timeout to 2+ minutes. | ||||
| - **Development mode**: `npm run dev` -- monitors file changes and auto-rebuilds web UI | ||||
| - **Hardware firmware build**: `pio run -e [environment]` -- takes 15+ minutes. NEVER CANCEL. Set timeout to 30+ minutes. | ||||
|  | ||||
| ### Build Process Details | ||||
| The build has two main phases: | ||||
| 1. **Web UI Generation** (`npm run build`): | ||||
|    - Processes files in `wled00/data/` (HTML, CSS, JS) | ||||
|    - Minifies and compresses web content  | ||||
|    - Generates `wled00/html_*.h` files with embedded web content | ||||
|    - **CRITICAL**: Must be done before any hardware build | ||||
|  | ||||
| 2. **Hardware Compilation** (`pio run`): | ||||
|    - Compiles C++ firmware for various ESP32/ESP8266 targets | ||||
|    - Common environments: `nodemcuv2`, `esp32dev`, `esp8266_2m` | ||||
|    - List all targets: `pio run --list-targets` | ||||
|  | ||||
| ## Validation and Testing | ||||
|  | ||||
| ### Web UI Testing | ||||
| - **ALWAYS validate web UI changes manually**: | ||||
|   - Start local server: `cd wled00/data && python3 -m http.server 8080` | ||||
|   - Open `http://localhost:8080/index.htm` in browser | ||||
|   - Test basic functionality: color picker, effects, settings pages | ||||
| - **Check for JavaScript errors** in browser console | ||||
|  | ||||
| ### Code Validation | ||||
| - **No automated linting configured** - follow existing code style in files you edit | ||||
| - **Code style**: Use tabs for web files (.html/.css/.js), spaces (2 per level) for C++ files | ||||
| - **C++ formatting available**: `clang-format` is installed but not in CI | ||||
| - **Always run tests before finishing**: `npm test` | ||||
|  | ||||
| ### Manual Testing Scenarios | ||||
| After making changes to web UI, always test: | ||||
| - **Load main interface**: Verify index.htm loads without errors | ||||
| - **Navigation**: Test switching between main page and settings pages | ||||
| - **Color controls**: Verify color picker and brightness controls work | ||||
| - **Effects**: Test effect selection and parameter changes | ||||
| - **Settings**: Test form submission and validation | ||||
|  | ||||
| ## Common Tasks | ||||
|  | ||||
| ### Repository Structure | ||||
| ``` | ||||
| wled00/                 # Main firmware source (C++) | ||||
|   ├── data/            # Web interface files  | ||||
|   │   ├── index.htm    # Main UI | ||||
|   │   ├── settings*.htm # Settings pages | ||||
|   │   └── *.js/*.css   # Frontend resources | ||||
|   ├── *.cpp/*.h        # Firmware source files | ||||
|   └── html_*.h         # Generated embedded web files (DO NOT EDIT) | ||||
| tools/                 # Build tools (Node.js) | ||||
|   ├── cdata.js         # Web UI build script | ||||
|   └── cdata-test.js    # Test suite | ||||
| platformio.ini         # Hardware build configuration | ||||
| package.json           # Node.js dependencies and scripts | ||||
| .github/workflows/     # CI/CD pipelines | ||||
| ``` | ||||
|  | ||||
| ### Key Files and Their Purpose | ||||
| - `wled00/data/index.htm` - Main web interface | ||||
| - `wled00/data/settings*.htm` - Configuration pages   | ||||
| - `tools/cdata.js` - Converts web files to C++ headers | ||||
| - `wled00/wled.h` - Main firmware configuration | ||||
| - `platformio.ini` - Hardware build targets and settings | ||||
|  | ||||
| ### Development Workflow | ||||
| 1. **For web UI changes**: | ||||
|    - Edit files in `wled00/data/` | ||||
|    - Run `npm run build` to regenerate headers | ||||
|    - Test with local HTTP server | ||||
|    - Run `npm test` to validate build system | ||||
|  | ||||
| 2. **For firmware changes**: | ||||
|    - Edit files in `wled00/` (but NOT `html_*.h` files) | ||||
|    - Ensure web UI is built first (`npm run build`) | ||||
|    - Build firmware: `pio run -e [target]` | ||||
|    - Flash to device: `pio run -e [target] --target upload` | ||||
|  | ||||
| 3. **For both web and firmware**: | ||||
|    - Always build web UI first | ||||
|    - Test web interface manually | ||||
|    - Build and test firmware if making firmware changes | ||||
|  | ||||
| ## Build Timing and Timeouts | ||||
|  | ||||
| - **Web UI build**: 3 seconds - Set timeout to 30 seconds minimum | ||||
| - **Test suite**: 40 seconds - Set timeout to 2 minutes minimum   | ||||
| - **Hardware builds**: 15+ minutes - Set timeout to 30+ minutes minimum | ||||
| - **NEVER CANCEL long-running builds** - PlatformIO downloads and compilation can take significant time | ||||
|  | ||||
| ## Troubleshooting | ||||
|  | ||||
| ### Common Issues | ||||
| - **Build fails with missing html_*.h**: Run `npm run build` first | ||||
| - **Web UI looks broken**: Check browser console for JavaScript errors | ||||
| - **PlatformIO network errors**: Try again, downloads can be flaky | ||||
| - **Node.js version issues**: Ensure Node.js 20+ is installed (check `.nvmrc`) | ||||
|  | ||||
| ### When Things Go Wrong | ||||
| - **Clear generated files**: `rm -f wled00/html_*.h` then rebuild | ||||
| - **Force web UI rebuild**: `npm run build -- --force` or `npm run build -- -f` | ||||
| - **Clean PlatformIO cache**: `pio run --target clean` | ||||
| - **Reinstall dependencies**: `rm -rf node_modules && npm install` | ||||
|  | ||||
| ## Important Notes | ||||
|  | ||||
| - **DO NOT edit `wled00/html_*.h` files** - they are auto-generated | ||||
| - **Always commit both source files AND generated html_*.h files** | ||||
| - **Web UI must be built before firmware compilation** | ||||
| - **Test web interface manually after any web UI changes** | ||||
| - **Use VS Code with PlatformIO extension for best development experience** | ||||
| - **Hardware builds require appropriate ESP32/ESP8266 development board** | ||||
|  | ||||
| ## CI/CD Pipeline | ||||
| The GitHub Actions workflow: | ||||
| 1. Installs Node.js and Python dependencies | ||||
| 2. Runs `npm test` to validate build system | ||||
| 3. Builds web UI with `npm run build`  | ||||
| 4. Compiles firmware for multiple hardware targets | ||||
| 5. Uploads build artifacts | ||||
|  | ||||
| Match this workflow in your local development to ensure CI success. | ||||
							
								
								
									
										85
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,85 @@ | ||||
| name: WLED Build | ||||
|  | ||||
| # Only included into other workflows | ||||
| on: | ||||
|   workflow_call: | ||||
|    | ||||
| jobs: | ||||
|  | ||||
|   get_default_envs: | ||||
|     name: Gather Environments | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|     - uses: actions/checkout@v4 | ||||
|     - uses: actions/setup-python@v5 | ||||
|       with: | ||||
|         python-version: '3.12' | ||||
|         cache: 'pip' | ||||
|     - name: Install PlatformIO | ||||
|       run: pip install -r requirements.txt | ||||
|     - name: Get default environments | ||||
|       id: envs | ||||
|       run: | | ||||
|         echo "environments=$(pio project config --json-output | jq -cr '.[0][1][0][1]')" >> $GITHUB_OUTPUT | ||||
|     outputs: | ||||
|       environments: ${{ steps.envs.outputs.environments }} | ||||
|  | ||||
|  | ||||
|   build: | ||||
|     name: Build Environments | ||||
|     runs-on: ubuntu-latest | ||||
|     needs: get_default_envs | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         environment: ${{ fromJSON(needs.get_default_envs.outputs.environments) }} | ||||
|     steps: | ||||
|     - uses: actions/checkout@v4 | ||||
|     - name: Set up Node.js | ||||
|       uses: actions/setup-node@v4 | ||||
|       with: | ||||
|         node-version-file: '.nvmrc' | ||||
|         cache: 'npm' | ||||
|     - run: | | ||||
|         npm ci | ||||
|         VERSION=`date +%y%m%d0` | ||||
|         sed -i -r -e "s/define VERSION .+/define VERSION $VERSION/" wled00/wled.h | ||||
|     - name: Cache PlatformIO | ||||
|       uses: actions/cache@v4 | ||||
|       with: | ||||
|         path: | | ||||
|               ~/.platformio/.cache | ||||
|               ~/.buildcache | ||||
|               build_output | ||||
|         key: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-${{ hashFiles('wled00/**', 'usermods/**') }} | ||||
|         restore-keys: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}- | ||||
|     - name: Set up Python | ||||
|       uses: actions/setup-python@v5 | ||||
|       with: | ||||
|           python-version: '3.12' | ||||
|           cache: 'pip' | ||||
|     - name: Install PlatformIO | ||||
|       run: pip install -r requirements.txt | ||||
|  | ||||
|     - name: Build firmware | ||||
|       run: pio run -e ${{ matrix.environment }} | ||||
|     - uses: actions/upload-artifact@v4 | ||||
|       with: | ||||
|         name: firmware-${{ matrix.environment }} | ||||
|         path: | | ||||
|           build_output/release/*.bin | ||||
|           build_output/release/*_ESP02*.bin.gz | ||||
|  | ||||
|  | ||||
|   testCdata: | ||||
|     name: Test cdata.js | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|       - name: Use Node.js | ||||
|         uses: actions/setup-node@v4 | ||||
|         with: | ||||
|           node-version-file: '.nvmrc' | ||||
|           cache: 'npm' | ||||
|       - run: npm ci | ||||
|       - run: npm test | ||||
							
								
								
									
										49
									
								
								.github/workflows/nightly.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								.github/workflows/nightly.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
|  | ||||
| name: Deploy Nightly | ||||
| on: | ||||
|   # This can be used to automatically publish nightlies at UTC nighttime | ||||
|   schedule: | ||||
|     - cron: '0 2 * * *' # run at 2 AM UTC | ||||
|   # This can be used to allow manually triggering nightlies from the web interface | ||||
|   workflow_dispatch: | ||||
|  | ||||
| jobs: | ||||
|   wled_build: | ||||
|     uses: ./.github/workflows/build.yml | ||||
|   nightly: | ||||
|     name: Deploy nightly | ||||
|     runs-on: ubuntu-latest | ||||
|     needs: wled_build | ||||
|     steps: | ||||
|       - name: Download artifacts | ||||
|         uses: actions/download-artifact@v4 | ||||
|         with: | ||||
|           merge-multiple: true | ||||
|       - name: Show Files | ||||
|         run: ls -la | ||||
|       - name: "✏️ Generate release changelog" | ||||
|         id: changelog | ||||
|         uses: janheinrichmerker/action-github-changelog-generator@v2.3 | ||||
|         with: | ||||
|           token: ${{ secrets.GITHUB_TOKEN }}  | ||||
|           sinceTag: v0.15.0 | ||||
|           # Exclude issues that were closed without resolution from changelog | ||||
|           exclude-labels: 'stale,wontfix,duplicate,invalid' | ||||
|       - name: Update Nightly Release | ||||
|         uses: andelf/nightly-release@main | ||||
|         env: | ||||
|           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||
|         with: | ||||
|           tag_name: nightly | ||||
|           name: 'Nightly Release $$' | ||||
|           prerelease: true | ||||
|           body: ${{ steps.changelog.outputs.changelog }} | ||||
|           files: | | ||||
|             *.bin | ||||
|             *.bin.gz | ||||
|       - name: Repository Dispatch | ||||
|         uses: peter-evans/repository-dispatch@v3 | ||||
|         with: | ||||
|           repository: wled/WLED-WebInstaller | ||||
|           event-type: release-nightly | ||||
|           token: ${{ secrets.PAT_PUBLIC }} | ||||
							
								
								
									
										38
									
								
								.github/workflows/pr-merge.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								.github/workflows/pr-merge.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
|     name: Notify Discord on PR Merge | ||||
|     on: | ||||
|       workflow_dispatch: | ||||
|       pull_request_target: | ||||
|         types: [closed] | ||||
|  | ||||
|     jobs: | ||||
|       notify: | ||||
|         runs-on: ubuntu-latest | ||||
|         if: github.event.pull_request.merged == true | ||||
|         steps: | ||||
|         - name: Get User Permission | ||||
|           id: checkAccess | ||||
|           uses: actions-cool/check-user-permission@v2 | ||||
|           with: | ||||
|             require: write | ||||
|             username: ${{ github.triggering_actor }} | ||||
|           env: | ||||
|             GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||
|         - name: Check User Permission | ||||
|           if: steps.checkAccess.outputs.require-result == 'false' | ||||
|           run: | | ||||
|             echo "${{ github.triggering_actor }} does not have permissions on this repo." | ||||
|             echo "Current permission level is ${{ steps.checkAccess.outputs.user-permission }}" | ||||
|             echo "Job originally triggered by ${{ github.actor }}" | ||||
|             exit 1 | ||||
|         - name: Send Discord notification | ||||
|           env: | ||||
|             PR_NUMBER: ${{ github.event.pull_request.number }} | ||||
|             PR_TITLE: ${{ github.event.pull_request.title }} | ||||
|             PR_URL: ${{ github.event.pull_request.html_url }} | ||||
|             ACTOR: ${{ github.actor }} | ||||
|           run: | | ||||
|             jq -n \ | ||||
|               --arg content "Pull Request #${PR_NUMBER} \"${PR_TITLE}\" merged by ${ACTOR} | ||||
|             ${PR_URL}. It will be included in the next nightly builds, please test" \ | ||||
|               '{content: $content}' \ | ||||
|               | curl -H "Content-Type: application/json" -d @- ${{ secrets.DISCORD_WEBHOOK_BETA_TESTERS }} | ||||
							
								
								
									
										38
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| name: WLED Release CI | ||||
|  | ||||
| on: | ||||
|   push: | ||||
|     tags: | ||||
|       - '*' | ||||
|  | ||||
| jobs: | ||||
|    | ||||
|   wled_build: | ||||
|     uses: ./.github/workflows/build.yml | ||||
|      | ||||
|   release: | ||||
|     name: Create Release | ||||
|     runs-on: ubuntu-latest | ||||
|     needs: wled_build | ||||
|     steps: | ||||
|     - uses: actions/download-artifact@v4 | ||||
|       with: | ||||
|         merge-multiple: true | ||||
|     - name: "✏️ Generate release changelog" | ||||
|       id: changelog | ||||
|       uses: janheinrichmerker/action-github-changelog-generator@v2.3 | ||||
|       with: | ||||
|           token: ${{ secrets.GITHUB_TOKEN }}  | ||||
|           sinceTag: v0.15.0 | ||||
|           maxIssues: 500 | ||||
|           # Exclude issues that were closed without resolution from changelog | ||||
|           exclude-labels: 'stale,wontfix,duplicate,invalid'        | ||||
|     - name: Create draft release | ||||
|       uses: softprops/action-gh-release@v1 | ||||
|       with: | ||||
|         body: ${{ steps.changelog.outputs.changelog }} | ||||
|         draft: True | ||||
|         files: | | ||||
|           *.bin | ||||
|           *.bin.gz | ||||
|  | ||||
							
								
								
									
										13
									
								
								.github/workflows/test.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								.github/workflows/test.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| on: | ||||
|   workflow_dispatch: | ||||
|  | ||||
| jobs: | ||||
|   dispatch: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Repository Dispatch | ||||
|         uses: peter-evans/repository-dispatch@v3 | ||||
|         with: | ||||
|           repository: wled/WLED-WebInstaller | ||||
|           event-type: release-nightly | ||||
|           token: ${{ secrets.PAT_PUBLIC }} | ||||
							
								
								
									
										74
									
								
								.github/workflows/usermods.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								.github/workflows/usermods.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | ||||
| name: Usermod CI | ||||
|  | ||||
| on: | ||||
|   push: | ||||
|     paths: | ||||
|       - usermods/** | ||||
|       - .github/workflows/usermods.yml | ||||
|   pull_request: | ||||
|     paths: | ||||
|       - usermods/** | ||||
|      | ||||
| jobs: | ||||
|  | ||||
|   get_usermod_envs: | ||||
|     name: Gather Usermods | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|     - uses: actions/checkout@v4 | ||||
|     - uses: actions/setup-python@v5 | ||||
|       with: | ||||
|         python-version: '3.12' | ||||
|         cache: 'pip' | ||||
|     - name: Install PlatformIO | ||||
|       run: pip install -r requirements.txt | ||||
|     - name: Get default environments | ||||
|       id: envs | ||||
|       run: | | ||||
|         echo "usermods=$(find usermods/ -name library.json | xargs dirname | xargs -n 1 basename | jq -R | grep -v PWM_fan | grep -v BME68X_v2| grep -v pixels_dice_tray | jq --slurp -c)" >> $GITHUB_OUTPUT | ||||
|     outputs: | ||||
|       usermods: ${{ steps.envs.outputs.usermods }} | ||||
|  | ||||
|  | ||||
|   build: | ||||
|     name: Build Enviornments | ||||
|     runs-on: ubuntu-latest | ||||
|     needs: get_usermod_envs | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         usermod: ${{ fromJSON(needs.get_usermod_envs.outputs.usermods) }} | ||||
|         environment: [usermods_esp32, usermods_esp32c3, usermods_esp32s2, usermods_esp32s3] | ||||
|     steps: | ||||
|     - uses: actions/checkout@v4 | ||||
|     - name: Set up Node.js | ||||
|       uses: actions/setup-node@v4 | ||||
|       with: | ||||
|         node-version-file: '.nvmrc' | ||||
|         cache: 'npm' | ||||
|     - run: npm ci | ||||
|     - name: Cache PlatformIO | ||||
|       uses: actions/cache@v4 | ||||
|       with: | ||||
|         path: | | ||||
|               ~/.platformio/.cache | ||||
|               ~/.buildcache | ||||
|               build_output | ||||
|         key: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-${{ hashFiles('wled00/**', 'usermods/**') }} | ||||
|         restore-keys: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}- | ||||
|     - name: Set up Python | ||||
|       uses: actions/setup-python@v5 | ||||
|       with: | ||||
|           python-version: '3.12' | ||||
|           cache: 'pip' | ||||
|     - name: Install PlatformIO | ||||
|       run: pip install -r requirements.txt | ||||
|     - name: Add usermods environment | ||||
|       run: | | ||||
|         cp -v usermods/platformio_override.usermods.ini platformio_override.ini | ||||
|         echo >> platformio_override.ini | ||||
|         echo  "custom_usermods = ${{ matrix.usermod }}" >> platformio_override.ini | ||||
|         cat platformio_override.ini | ||||
|  | ||||
|     - name: Build firmware | ||||
|       run: pio run -e ${{ matrix.environment }}     | ||||
							
								
								
									
										97
									
								
								.github/workflows/wled-ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										97
									
								
								.github/workflows/wled-ci.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,94 +1,11 @@ | ||||
| name: WLED CI | ||||
|  | ||||
| on: [push, pull_request] | ||||
| on: | ||||
|   push: | ||||
|     branches: | ||||
|       - '*' | ||||
|   pull_request: | ||||
|  | ||||
| jobs: | ||||
|  | ||||
|   get_default_envs: | ||||
|     name: Gather Environments | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|     - uses: actions/checkout@v4 | ||||
|     - uses: actions/setup-python@v5 | ||||
|       with: | ||||
|         python-version: '3.12' | ||||
|         cache: 'pip' | ||||
|     - name: Install PlatformIO | ||||
|       run: pip install -r requirements.txt | ||||
|     - name: Get default environments | ||||
|       id: envs | ||||
|       run: | | ||||
|         echo "environments=$(pio project config --json-output | jq -cr '.[0][1][0][1]')" >> $GITHUB_OUTPUT | ||||
|     outputs: | ||||
|       environments: ${{ steps.envs.outputs.environments }} | ||||
|  | ||||
|  | ||||
|   build: | ||||
|     name: Build Enviornments | ||||
|     runs-on: ubuntu-latest | ||||
|     needs: get_default_envs | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         environment: ${{ fromJSON(needs.get_default_envs.outputs.environments) }} | ||||
|     steps: | ||||
|     - uses: actions/checkout@v4 | ||||
|     - name: Set up Node.js | ||||
|       uses: actions/setup-node@v4 | ||||
|       with: | ||||
|         cache: 'npm' | ||||
|     - run: npm ci | ||||
|     - name: Cache PlatformIO | ||||
|       uses: actions/cache@v4 | ||||
|       with: | ||||
|         path: | | ||||
|               ~/.platformio/.cache | ||||
|               ~/.buildcache | ||||
|               build_output | ||||
|         key: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-${{ hashFiles('wled00/**', 'usermods/**') }} | ||||
|         restore-keys: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}- | ||||
|     - name: Set up Python | ||||
|       uses: actions/setup-python@v5 | ||||
|       with: | ||||
|           python-version: '3.12' | ||||
|           cache: 'pip' | ||||
|     - name: Install PlatformIO | ||||
|       run: pip install -r requirements.txt | ||||
|     - name: Build firmware | ||||
|       run: pio run -e ${{ matrix.environment }} | ||||
|     - uses: actions/upload-artifact@v4 | ||||
|       with: | ||||
|         name: firmware-${{ matrix.environment }} | ||||
|         path: | | ||||
|           build_output/release/*.bin | ||||
|           build_output/release/*_ESP02*.bin.gz | ||||
|   release: | ||||
|     name: Create Release | ||||
|     runs-on: ubuntu-latest | ||||
|     needs: build | ||||
|     if: startsWith(github.ref, 'refs/tags/') | ||||
|     steps: | ||||
|     - uses: actions/download-artifact@v4 | ||||
|       with: | ||||
|         merge-multiple: true | ||||
|     - name: Create draft release | ||||
|       uses: softprops/action-gh-release@v1 | ||||
|       with: | ||||
|         draft: True | ||||
|         files: | | ||||
|           *.bin | ||||
|           *.bin.gz | ||||
|  | ||||
|  | ||||
|   testCdata: | ||||
|     name: Test cdata.js | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|       - name: Use Node.js | ||||
|         uses: actions/setup-node@v4 | ||||
|         with: | ||||
|           node-version: '20.x' | ||||
|           cache: 'npm' | ||||
|       - run: npm ci | ||||
|       - run: npm test | ||||
|   wled_build: | ||||
|     uses: ./.github/workflows/build.yml | ||||
|   | ||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -15,6 +15,7 @@ wled-update.sh | ||||
|  | ||||
| /build_output/ | ||||
| /node_modules/ | ||||
| /logs/ | ||||
|  | ||||
| /wled00/extLibs | ||||
| /wled00/LittleFS | ||||
|   | ||||
							
								
								
									
										33
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,5 +1,36 @@ | ||||
| ## WLED changelog | ||||
|  | ||||
| #### Build 2410270 | ||||
| -   WLED 0.15.0-b7 release | ||||
| -   Re-license the WLED project from MIT to EUPL (#4194 by @Aircoookie) | ||||
| -   Fix alexa devices invisible/uncontrollable (#4214 by @Svennte) | ||||
| -   Add visual expand button on hover (#4172) | ||||
| -   Usermod: Audioreactive tuning and performance enhancements (by @softhack007) | ||||
| -   `/json/live` (JSON live data/peek) only enabled when WebSockets are disabled | ||||
| -   Various bugfixes and optimisations: #4179, #4215, #4219, #4222, #4223, #4224, #4228, #4230 | ||||
|  | ||||
| #### Build 2410140 | ||||
| -   WLED 0.15.0-b6 release | ||||
| -   Added BRT timezone (#4188 by @LuisFadini) | ||||
| -   Fixed the positioning of the "Download the latest binary" button (#4184 by @maxi4329) | ||||
| -   Add WLED_AUTOSEGMENTS compile flag (#4183 by @PaoloTK) | ||||
| -   New 512kB FS parition map for 4MB devices | ||||
| -   Internal API change: Static PinManager & UsermodManager | ||||
| -   Change in Improv chip ID and version generation | ||||
| -   Various optimisations, bugfixes and enhancements (#4005, #4174 & #4175 by @Xevel, #4180, #4168, #4154, #4189 by @dosipod) | ||||
|  | ||||
| #### Build 2409170 | ||||
| -   UI: Introduce common.js in settings pages (size optimisation) | ||||
| -   Add the ability to toggle the reception of palette synchronizations (#4137 by @felddy) | ||||
| -   Usermod/FX: Temperature usermod added Temperature effect (example usermod effect by @blazoncek) | ||||
| -   Fix AsyncWebServer version pin | ||||
|  | ||||
| #### Build 2409140 | ||||
| -   Configure different kinds of busses at compile (#4107 by @PaoloTK) | ||||
|     - BREAKING: removes LEDPIN and DEFAULT_LED_TYPE compile overrides | ||||
| -   Fetch LED types from Bus classes (dynamic UI) (#4129 by @netmindz, @blazoncek, @dedehai) | ||||
| -   Temperature usermod: update OneWire to 2.3.8 (#4131 by @iammattcoleman) | ||||
|  | ||||
| #### Build 2409100 | ||||
| -   WLED 0.15.0-b5 release | ||||
| -   Audioreactive usermod included by default in all compatible builds (including ESP8266) | ||||
| @@ -142,7 +173,7 @@ | ||||
| -   v0.15.0-b2 | ||||
| -   WS2805 support (RGB + WW + CW, 600kbps) | ||||
| -   Unified PSRAM use | ||||
| -   NeoPixelBus v2.7.9 | ||||
| -   NeoPixelBus v2.7.9 (for future WS2805 support) | ||||
| -   Ubiquitous PSRAM mode for all variants of ESP32 | ||||
| -   SSD1309_64 I2C Support for FLD Usermod (#3836 by @THATDONFC) | ||||
| -   Palette cycling fix (add support for `{"seg":[{"pal":"X~Y~"}]}` or `{"seg":[{"pal":"X~Yr"}]}`) | ||||
|   | ||||
| @@ -14,7 +14,21 @@ A good description helps us to review and understand your proposed changes. For | ||||
|  | ||||
| ### Target branch for pull requests | ||||
|  | ||||
| Please make all PRs against the `0_15` branch. | ||||
| Please make all PRs against the `main` branch. | ||||
|  | ||||
| ### Updating your code | ||||
| While the PR is open - and under review by maintainers - you may be asked to modify your PR source code. | ||||
| You can simply update your own branch, and push changes in response to reviewer recommendations.  | ||||
| Github will pick up the changes so your PR stays up-to-date. | ||||
|  | ||||
| > [!CAUTION] | ||||
| > Do not use "force-push" while your PR is open! | ||||
| > It has many subtle and unexpected consequences on our github reposistory. | ||||
| > For example, we regularly lost review comments when the PR author force-pushes code changes. So, pretty please, do not force-push. | ||||
|  | ||||
|  | ||||
| You can find a collection of very useful tips and tricks here: https://github.com/wled-dev/WLED/wiki/How-to-properly-submit-a-PR | ||||
|  | ||||
|  | ||||
| ### Code style | ||||
|  | ||||
| @@ -37,6 +51,11 @@ if (a == b) { | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ```cpp | ||||
| if (a == b) doStuff(a); | ||||
| ``` | ||||
|  | ||||
| Acceptable - however the first variant is usually easier to read: | ||||
| ```cpp | ||||
| if (a == b) | ||||
| { | ||||
| @@ -44,9 +63,6 @@ if (a == b) | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ```cpp | ||||
| if (a == b) doStuff(a); | ||||
| ``` | ||||
|  | ||||
| There should always be a space between a keyword and its condition and between the condition and brace.   | ||||
| Within the condition, no space should be between the parenthesis and variables.   | ||||
| @@ -89,4 +105,4 @@ Good: | ||||
|  | ||||
| There is no hard character limit for a comment within a line, | ||||
| though as a rule of thumb consider wrapping after 120 characters. | ||||
| Inline comments are OK if they describe that line only and are not exceedingly wide. | ||||
| Inline comments are OK if they describe that line only and are not exceedingly wide. | ||||
|   | ||||
							
								
								
									
										307
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										307
									
								
								LICENSE
									
									
									
									
									
								
							| @@ -1,21 +1,294 @@ | ||||
| MIT License | ||||
| Copyright (c) 2016-present Christian Schwinne and individual WLED contributors | ||||
| Licensed under the EUPL v. 1.2 or later | ||||
|  | ||||
| Copyright (c) 2016 Christian Schwinne | ||||
|                       EUROPEAN UNION PUBLIC LICENCE v. 1.2 | ||||
|                       EUPL © the European Union 2007, 2016 | ||||
|  | ||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| of this software and associated documentation files (the "Software"), to deal | ||||
| in the Software without restriction, including without limitation the rights | ||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| copies of the Software, and to permit persons to whom the Software is | ||||
| furnished to do so, subject to the following conditions: | ||||
| This European Union Public Licence (the ‘EUPL’) applies to the Work (as | ||||
| defined below) which is provided under the terms of this Licence. Any use of | ||||
| the Work, other than as authorised under this Licence is prohibited (to the | ||||
| extent such use is covered by a right of the copyright holder of the Work). | ||||
|  | ||||
| The above copyright notice and this permission notice shall be included in all | ||||
| copies or substantial portions of the Software. | ||||
| The Work is provided under the terms of this Licence when the Licensor (as | ||||
| defined below) has placed the following notice immediately following the | ||||
| copyright notice for the Work: | ||||
|  | ||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
| SOFTWARE. | ||||
|         Licensed under the EUPL | ||||
|  | ||||
| or has expressed by any other means his willingness to license under the EUPL. | ||||
|  | ||||
| 1. Definitions | ||||
|  | ||||
| In this Licence, the following terms have the following meaning: | ||||
|  | ||||
| - ‘The Licence’: this Licence. | ||||
|  | ||||
| - ‘The Original Work’: the work or software distributed or communicated by the | ||||
|   Licensor under this Licence, available as Source Code and also as Executable | ||||
|   Code as the case may be. | ||||
|  | ||||
| - ‘Derivative Works’: the works or software that could be created by the | ||||
|   Licensee, based upon the Original Work or modifications thereof. This | ||||
|   Licence does not define the extent of modification or dependence on the | ||||
|   Original Work required in order to classify a work as a Derivative Work; | ||||
|   this extent is determined by copyright law applicable in the country | ||||
|   mentioned in Article 15. | ||||
|  | ||||
| - ‘The Work’: the Original Work or its Derivative Works. | ||||
|  | ||||
| - ‘The Source Code’: the human-readable form of the Work which is the most | ||||
|   convenient for people to study and modify. | ||||
|  | ||||
| - ‘The Executable Code’: any code which has generally been compiled and which | ||||
|   is meant to be interpreted by a computer as a program. | ||||
|  | ||||
| - ‘The Licensor’: the natural or legal person that distributes or communicates | ||||
|   the Work under the Licence. | ||||
|  | ||||
| - ‘Contributor(s)’: any natural or legal person who modifies the Work under | ||||
|   the Licence, or otherwise contributes to the creation of a Derivative Work. | ||||
|  | ||||
| - ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of | ||||
|   the Work under the terms of the Licence. | ||||
|  | ||||
| - ‘Distribution’ or ‘Communication’: any act of selling, giving, lending, | ||||
|   renting, distributing, communicating, transmitting, or otherwise making | ||||
|   available, online or offline, copies of the Work or providing access to its | ||||
|   essential functionalities at the disposal of any other natural or legal | ||||
|   person. | ||||
|  | ||||
| 2. Scope of the rights granted by the Licence | ||||
|  | ||||
| The Licensor hereby grants You a worldwide, royalty-free, non-exclusive, | ||||
| sublicensable licence to do the following, for the duration of copyright | ||||
| vested in the Original Work: | ||||
|  | ||||
| - use the Work in any circumstance and for all usage, | ||||
| - reproduce the Work, | ||||
| - modify the Work, and make Derivative Works based upon the Work, | ||||
| - communicate to the public, including the right to make available or display | ||||
|   the Work or copies thereof to the public and perform publicly, as the case | ||||
|   may be, the Work, | ||||
| - distribute the Work or copies thereof, | ||||
| - lend and rent the Work or copies thereof, | ||||
| - sublicense rights in the Work or copies thereof. | ||||
|  | ||||
| Those rights can be exercised on any media, supports and formats, whether now | ||||
| known or later invented, as far as the applicable law permits so. | ||||
|  | ||||
| In the countries where moral rights apply, the Licensor waives his right to | ||||
| exercise his moral right to the extent allowed by law in order to make | ||||
| effective the licence of the economic rights here above listed. | ||||
|  | ||||
| The Licensor grants to the Licensee royalty-free, non-exclusive usage rights | ||||
| to any patents held by the Licensor, to the extent necessary to make use of | ||||
| the rights granted on the Work under this Licence. | ||||
|  | ||||
| 3. Communication of the Source Code | ||||
|  | ||||
| The Licensor may provide the Work either in its Source Code form, or as | ||||
| Executable Code. If the Work is provided as Executable Code, the Licensor | ||||
| provides in addition a machine-readable copy of the Source Code of the Work | ||||
| along with each copy of the Work that the Licensor distributes or indicates, | ||||
| in a notice following the copyright notice attached to the Work, a repository | ||||
| where the Source Code is easily and freely accessible for as long as the | ||||
| Licensor continues to distribute or communicate the Work. | ||||
|  | ||||
| 4. Limitations on copyright | ||||
|  | ||||
| Nothing in this Licence is intended to deprive the Licensee of the benefits | ||||
| from any exception or limitation to the exclusive rights of the rights owners | ||||
| in the Work, of the exhaustion of those rights or of other applicable | ||||
| limitations thereto. | ||||
|  | ||||
| 5. Obligations of the Licensee | ||||
|  | ||||
| The grant of the rights mentioned above is subject to some restrictions and | ||||
| obligations imposed on the Licensee. Those obligations are the following: | ||||
|  | ||||
| Attribution right: The Licensee shall keep intact all copyright, patent or | ||||
| trademarks notices and all notices that refer to the Licence and to the | ||||
| disclaimer of warranties. The Licensee must include a copy of such notices and | ||||
| a copy of the Licence with every copy of the Work he/she distributes or | ||||
| communicates. The Licensee must cause any Derivative Work to carry prominent | ||||
| notices stating that the Work has been modified and the date of modification. | ||||
|  | ||||
| Copyleft clause: If the Licensee distributes or communicates copies of the | ||||
| Original Works or Derivative Works, this Distribution or Communication will be | ||||
| done under the terms of this Licence or of a later version of this Licence | ||||
| unless the Original Work is expressly distributed only under this version of | ||||
| the Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee | ||||
| (becoming Licensor) cannot offer or impose any additional terms or conditions | ||||
| on the Work or Derivative Work that alter or restrict the terms of the | ||||
| Licence. | ||||
|  | ||||
| Compatibility clause: If the Licensee Distributes or Communicates Derivative | ||||
| Works or copies thereof based upon both the Work and another work licensed | ||||
| under a Compatible Licence, this Distribution or Communication can be done | ||||
| under the terms of this Compatible Licence. For the sake of this clause, | ||||
| ‘Compatible Licence’ refers to the licences listed in the appendix attached to | ||||
| this Licence. Should the Licensee's obligations under the Compatible Licence | ||||
| conflict with his/her obligations under this Licence, the obligations of the | ||||
| Compatible Licence shall prevail. | ||||
|  | ||||
| Provision of Source Code: When distributing or communicating copies of the | ||||
| Work, the Licensee will provide a machine-readable copy of the Source Code or | ||||
| indicate a repository where this Source will be easily and freely available | ||||
| for as long as the Licensee continues to distribute or communicate the Work. | ||||
|  | ||||
| Legal Protection: This Licence does not grant permission to use the trade | ||||
| names, trademarks, service marks, or names of the Licensor, except as required | ||||
| for reasonable and customary use in describing the origin of the Work and | ||||
| reproducing the content of the copyright notice. | ||||
|  | ||||
| 6. Chain of Authorship | ||||
|  | ||||
| The original Licensor warrants that the copyright in the Original Work granted | ||||
| hereunder is owned by him/her or licensed to him/her and that he/she has the | ||||
| power and authority to grant the Licence. | ||||
|  | ||||
| Each Contributor warrants that the copyright in the modifications he/she | ||||
| brings to the Work are owned by him/her or licensed to him/her and that he/she | ||||
| has the power and authority to grant the Licence. | ||||
|  | ||||
| Each time You accept the Licence, the original Licensor and subsequent | ||||
| Contributors grant You a licence to their contributions to the Work, under the | ||||
| terms of this Licence. | ||||
|  | ||||
| 7. Disclaimer of Warranty | ||||
|  | ||||
| The Work is a work in progress, which is continuously improved by numerous | ||||
| Contributors. It is not a finished work and may therefore contain defects or | ||||
| ‘bugs’ inherent to this type of development. | ||||
|  | ||||
| For the above reason, the Work is provided under the Licence on an ‘as is’ | ||||
| basis and without warranties of any kind concerning the Work, including | ||||
| without limitation merchantability, fitness for a particular purpose, absence | ||||
| of defects or errors, accuracy, non-infringement of intellectual property | ||||
| rights other than copyright as stated in Article 6 of this Licence. | ||||
|  | ||||
| This disclaimer of warranty is an essential part of the Licence and a | ||||
| condition for the grant of any rights to the Work. | ||||
|  | ||||
| 8. Disclaimer of Liability | ||||
|  | ||||
| Except in the cases of wilful misconduct or damages directly caused to natural | ||||
| persons, the Licensor will in no event be liable for any direct or indirect, | ||||
| material or moral, damages of any kind, arising out of the Licence or of the | ||||
| use of the Work, including without limitation, damages for loss of goodwill, | ||||
| work stoppage, computer failure or malfunction, loss of data or any commercial | ||||
| damage, even if the Licensor has been advised of the possibility of such | ||||
| damage. However, the Licensor will be liable under statutory product liability | ||||
| laws as far such laws apply to the Work. | ||||
|  | ||||
| 9. Additional agreements | ||||
|  | ||||
| While distributing the Work, You may choose to conclude an additional | ||||
| agreement, defining obligations or services consistent with this Licence. | ||||
| However, if accepting obligations, You may act only on your own behalf and on | ||||
| your sole responsibility, not on behalf of the original Licensor or any other | ||||
| Contributor, and only if You agree to indemnify, defend, and hold each | ||||
| Contributor harmless for any liability incurred by, or claims asserted against | ||||
| such Contributor by the fact You have accepted any warranty or additional | ||||
| liability. | ||||
|  | ||||
| 10. Acceptance of the Licence | ||||
|  | ||||
| The provisions of this Licence can be accepted by clicking on an icon ‘I | ||||
| agree’ placed under the bottom of a window displaying the text of this Licence | ||||
| or by affirming consent in any other similar way, in accordance with the rules | ||||
| of applicable law. Clicking on that icon indicates your clear and irrevocable | ||||
| acceptance of this Licence and all of its terms and conditions. | ||||
|  | ||||
| Similarly, you irrevocably accept this Licence and all of its terms and | ||||
| conditions by exercising any rights granted to You by Article 2 of this | ||||
| Licence, such as the use of the Work, the creation by You of a Derivative Work | ||||
| or the Distribution or Communication by You of the Work or copies thereof. | ||||
|  | ||||
| 11. Information to the public | ||||
|  | ||||
| In case of any Distribution or Communication of the Work by means of | ||||
| electronic communication by You (for example, by offering to download the Work | ||||
| from a remote location) the distribution channel or media (for example, a | ||||
| website) must at least provide to the public the information requested by the | ||||
| applicable law regarding the Licensor, the Licence and the way it may be | ||||
| accessible, concluded, stored and reproduced by the Licensee. | ||||
|  | ||||
| 12. Termination of the Licence | ||||
|  | ||||
| The Licence and the rights granted hereunder will terminate automatically upon | ||||
| any breach by the Licensee of the terms of the Licence. | ||||
|  | ||||
| Such a termination will not terminate the licences of any person who has | ||||
| received the Work from the Licensee under the Licence, provided such persons | ||||
| remain in full compliance with the Licence. | ||||
|  | ||||
| 13. Miscellaneous | ||||
|  | ||||
| Without prejudice of Article 9 above, the Licence represents the complete | ||||
| agreement between the Parties as to the Work. | ||||
|  | ||||
| If any provision of the Licence is invalid or unenforceable under applicable | ||||
| law, this will not affect the validity or enforceability of the Licence as a | ||||
| whole. Such provision will be construed or reformed so as necessary to make it | ||||
| valid and enforceable. | ||||
|  | ||||
| The European Commission may publish other linguistic versions or new versions | ||||
| of this Licence or updated versions of the Appendix, so far this is required | ||||
| and reasonable, without reducing the scope of the rights granted by the | ||||
| Licence. New versions of the Licence will be published with a unique version | ||||
| number. | ||||
|  | ||||
| All linguistic versions of this Licence, approved by the European Commission, | ||||
| have identical value. Parties can take advantage of the linguistic version of | ||||
| their choice. | ||||
|  | ||||
| 14. Jurisdiction | ||||
|  | ||||
| Without prejudice to specific agreement between parties, | ||||
|  | ||||
| - any litigation resulting from the interpretation of this License, arising | ||||
|   between the European Union institutions, bodies, offices or agencies, as a | ||||
|   Licensor, and any Licensee, will be subject to the jurisdiction of the Court | ||||
|   of Justice of the European Union, as laid down in article 272 of the Treaty | ||||
|   on the Functioning of the European Union, | ||||
|  | ||||
| - any litigation arising between other parties and resulting from the | ||||
|   interpretation of this License, will be subject to the exclusive | ||||
|   jurisdiction of the competent court where the Licensor resides or conducts | ||||
|   its primary business. | ||||
|  | ||||
| 15. Applicable Law | ||||
|  | ||||
| Without prejudice to specific agreement between parties, | ||||
|  | ||||
| - this Licence shall be governed by the law of the European Union Member State | ||||
|   where the Licensor has his seat, resides or has his registered office, | ||||
|  | ||||
| - this licence shall be governed by Belgian law if the Licensor has no seat, | ||||
|   residence or registered office inside a European Union Member State. | ||||
|  | ||||
| Appendix | ||||
|  | ||||
| ‘Compatible Licences’ according to Article 5 EUPL are: | ||||
|  | ||||
| - GNU General Public License (GPL) v. 2, v. 3 | ||||
| - GNU Affero General Public License (AGPL) v. 3 | ||||
| - Open Software License (OSL) v. 2.1, v. 3.0 | ||||
| - Eclipse Public License (EPL) v. 1.0 | ||||
| - CeCILL v. 2.0, v. 2.1 | ||||
| - Mozilla Public Licence (MPL) v. 2 | ||||
| - GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3 | ||||
| - Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for | ||||
|   works other than software | ||||
| - European Union Public Licence (EUPL) v. 1.1, v. 1.2 | ||||
| - Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong | ||||
|   Reciprocity (LiLiQ-R+). | ||||
|  | ||||
| The European Commission may update this Appendix to later versions of the | ||||
| above licences without producing a new version of the EUPL, as long as they | ||||
| provide the rights granted in Article 2 of this Licence and protect the | ||||
| covered Source Code from exclusive appropriation. | ||||
|  | ||||
| All other changes or additions to this Appendix require the production of a | ||||
| new EUPL version. | ||||
							
								
								
									
										47
									
								
								boards/lolin_s3_mini.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								boards/lolin_s3_mini.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| { | ||||
|     "build": { | ||||
|       "arduino": { | ||||
|         "ldscript": "esp32s3_out.ld", | ||||
|         "memory_type": "qio_qspi" | ||||
|       }, | ||||
|       "core": "esp32", | ||||
|       "extra_flags": [ | ||||
|         "-DBOARD_HAS_PSRAM", | ||||
|         "-DARDUINO_LOLIN_S3_MINI", | ||||
|         "-DARDUINO_USB_MODE=1" | ||||
|       ], | ||||
|       "f_cpu": "240000000L", | ||||
|       "f_flash": "80000000L", | ||||
|       "flash_mode": "qio", | ||||
|       "hwids": [ | ||||
|         [ | ||||
|           "0x303A", | ||||
|           "0x8167" | ||||
|         ] | ||||
|       ], | ||||
|       "mcu": "esp32s3", | ||||
|       "variant": "lolin_s3_mini" | ||||
|     }, | ||||
|     "connectivity": [ | ||||
|       "bluetooth", | ||||
|       "wifi" | ||||
|     ], | ||||
|     "debug": { | ||||
|       "openocd_target": "esp32s3.cfg" | ||||
|     }, | ||||
|     "frameworks": [ | ||||
|       "arduino", | ||||
|       "espidf" | ||||
|     ], | ||||
|     "name": "WEMOS LOLIN S3 Mini", | ||||
|     "upload": { | ||||
|       "flash_size": "4MB", | ||||
|       "maximum_ram_size": 327680, | ||||
|       "maximum_size": 4194304, | ||||
|       "require_upload_port": true, | ||||
|       "speed": 460800 | ||||
|     }, | ||||
|     "url": "https://www.wemos.cc/en/latest/s3/index.html", | ||||
|     "vendor": "WEMOS" | ||||
| } | ||||
|    | ||||
							
								
								
									
										504
									
								
								lib/ESP8266PWM/src/core_esp8266_waveform_phase.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										504
									
								
								lib/ESP8266PWM/src/core_esp8266_waveform_phase.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,504 @@ | ||||
| /* esp8266_waveform imported from platform source code | ||||
|    Modified for WLED to work around a fault in the NMI handling, | ||||
|    which can result in the system locking up and hard WDT crashes. | ||||
|  | ||||
|    Imported from https://github.com/esp8266/Arduino/blob/7e0d20e2b9034994f573a236364e0aef17fd66de/cores/esp8266/core_esp8266_waveform_phase.cpp | ||||
| */ | ||||
|  | ||||
|  | ||||
| /* | ||||
|   esp8266_waveform - General purpose waveform generation and control, | ||||
|                      supporting outputs on all pins in parallel. | ||||
|  | ||||
|   Copyright (c) 2018 Earle F. Philhower, III.  All rights reserved. | ||||
|   Copyright (c) 2020 Dirk O. Kaar. | ||||
|  | ||||
|   The core idea is to have a programmable waveform generator with a unique | ||||
|   high and low period (defined in microseconds or CPU clock cycles).  TIMER1 is | ||||
|   set to 1-shot mode and is always loaded with the time until the next edge | ||||
|   of any live waveforms. | ||||
|  | ||||
|   Up to one waveform generator per pin supported. | ||||
|  | ||||
|   Each waveform generator is synchronized to the ESP clock cycle counter, not the | ||||
|   timer.  This allows for removing interrupt jitter and delay as the counter | ||||
|   always increments once per 80MHz clock.  Changes to a waveform are | ||||
|   contiguous and only take effect on the next waveform transition, | ||||
|   allowing for smooth transitions. | ||||
|  | ||||
|   This replaces older tone(), analogWrite(), and the Servo classes. | ||||
|  | ||||
|   Everywhere in the code where "ccy" or "ccys" is used, it means ESP.getCycleCount() | ||||
|   clock cycle time, or an interval measured in clock cycles, but not TIMER1 | ||||
|   cycles (which may be 2 CPU clock cycles @ 160MHz). | ||||
|  | ||||
|   This library is free software; you can redistribute it and/or | ||||
|   modify it under the terms of the GNU Lesser General Public | ||||
|   License as published by the Free Software Foundation; either | ||||
|   version 2.1 of the License, or (at your option) any later version. | ||||
|  | ||||
|   This library is distributed in the hope that it will be useful, | ||||
|   but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|   Lesser General Public License for more details. | ||||
|  | ||||
|   You should have received a copy of the GNU Lesser General Public | ||||
|   License along with this library; if not, write to the Free Software | ||||
|   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||||
| */ | ||||
|  | ||||
| #include "core_esp8266_waveform.h" | ||||
| #include <Arduino.h> | ||||
| #include "debug.h" | ||||
| #include "ets_sys.h" | ||||
| #include <atomic> | ||||
|  | ||||
|  | ||||
| // ----- @willmmiles begin patch ----- | ||||
| // Linker magic | ||||
| extern "C" void usePWMFixedNMI(void) {}; | ||||
|  | ||||
| // NMI crash workaround | ||||
| // Sometimes the NMI fails to return, stalling the CPU.  When this happens, | ||||
| // the next NMI gets a return address /inside the NMI handler function/. | ||||
| // We work around this by caching the last NMI return address, and restoring | ||||
| // the epc3 and eps3 registers to the previous values if the observed epc3 | ||||
| // happens to be pointing to the _NMILevelVector function. | ||||
| extern "C" void _NMILevelVector(); | ||||
| extern "C" void _UserExceptionVector_1(); // the next function after _NMILevelVector | ||||
| static inline IRAM_ATTR void nmiCrashWorkaround() { | ||||
|   static uintptr_t epc3_backup, eps3_backup; | ||||
|  | ||||
|   uintptr_t epc3, eps3; | ||||
|   __asm__ __volatile__("rsr %0,epc3; rsr %1,eps3":"=a"(epc3),"=a" (eps3)); | ||||
|   if ((epc3 < (uintptr_t) &_NMILevelVector) || (epc3 >= (uintptr_t) &_UserExceptionVector_1)) { | ||||
|     // Address is good; save backup | ||||
|     epc3_backup = epc3; | ||||
|     eps3_backup = eps3; | ||||
|   } else { | ||||
|     // Address is inside the NMI handler -- restore from backup | ||||
|     __asm__ __volatile__("wsr %0,epc3; wsr %1,eps3"::"a"(epc3_backup),"a"(eps3_backup)); | ||||
|   } | ||||
| } | ||||
| // ----- @willmmiles end patch ----- | ||||
|  | ||||
|  | ||||
| // No-op calls to override the PWM implementation | ||||
| extern "C" void _setPWMFreq_weak(uint32_t freq) { (void) freq; } | ||||
| extern "C" IRAM_ATTR bool _stopPWM_weak(int pin) { (void) pin; return false; } | ||||
| extern "C" bool _setPWM_weak(int pin, uint32_t val, uint32_t range) { (void) pin; (void) val; (void) range; return false; } | ||||
|  | ||||
|  | ||||
| // Timer is 80MHz fixed. 160MHz CPU frequency need scaling. | ||||
| constexpr bool ISCPUFREQ160MHZ = clockCyclesPerMicrosecond() == 160; | ||||
| // Maximum delay between IRQs, Timer1, <= 2^23 / 80MHz | ||||
| constexpr int32_t MAXIRQTICKSCCYS = microsecondsToClockCycles(10000); | ||||
| // Maximum servicing time for any single IRQ | ||||
| constexpr uint32_t ISRTIMEOUTCCYS = microsecondsToClockCycles(18); | ||||
| // The latency between in-ISR rearming of the timer and the earliest firing | ||||
| constexpr int32_t IRQLATENCYCCYS = microsecondsToClockCycles(2); | ||||
| // The SDK and hardware take some time to actually get to our NMI code | ||||
| constexpr int32_t DELTAIRQCCYS = ISCPUFREQ160MHZ ? | ||||
|   microsecondsToClockCycles(2) >> 1 : microsecondsToClockCycles(2); | ||||
|  | ||||
| // for INFINITE, the NMI proceeds on the waveform without expiry deadline. | ||||
| // for EXPIRES, the NMI expires the waveform automatically on the expiry ccy. | ||||
| // for UPDATEEXPIRY, the NMI recomputes the exact expiry ccy and transitions to EXPIRES. | ||||
| // for UPDATEPHASE, the NMI recomputes the target timings | ||||
| // for INIT, the NMI initializes nextPeriodCcy, and if expiryCcy != 0 includes UPDATEEXPIRY. | ||||
| enum class WaveformMode : uint8_t {INFINITE = 0, EXPIRES = 1, UPDATEEXPIRY = 2, UPDATEPHASE = 3, INIT = 4}; | ||||
|  | ||||
| // Waveform generator can create tones, PWM, and servos | ||||
| typedef struct { | ||||
|   uint32_t nextPeriodCcy; // ESP clock cycle when a period begins. | ||||
|   uint32_t endDutyCcy;    // ESP clock cycle when going from duty to off | ||||
|   int32_t dutyCcys;       // Set next off cycle at low->high to maintain phase | ||||
|   int32_t adjDutyCcys;    // Temporary correction for next period | ||||
|   int32_t periodCcys;     // Set next phase cycle at low->high to maintain phase | ||||
|   uint32_t expiryCcy;     // For time-limited waveform, the CPU clock cycle when this waveform must stop. If WaveformMode::UPDATE, temporarily holds relative ccy count | ||||
|   WaveformMode mode; | ||||
|   bool autoPwm;           // perform PWM duty to idle cycle ratio correction under high load at the expense of precise timings | ||||
| } Waveform; | ||||
|  | ||||
| namespace { | ||||
|  | ||||
|   static struct { | ||||
|     Waveform pins[17];             // State of all possible pins | ||||
|     uint32_t states = 0;           // Is the pin high or low, updated in NMI so no access outside the NMI code | ||||
|     uint32_t enabled = 0; // Is it actively running, updated in NMI so no access outside the NMI code | ||||
|  | ||||
|     // Enable lock-free by only allowing updates to waveform.states and waveform.enabled from IRQ service routine | ||||
|     int32_t toSetBits = 0;     // Message to the NMI handler to start/modify exactly one waveform | ||||
|     int32_t toDisableBits = 0; // Message to the NMI handler to disable exactly one pin from waveform generation | ||||
|  | ||||
|     // toSetBits temporaries | ||||
|     // cheaper than packing them in every Waveform, since we permit only one use at a time | ||||
|     uint32_t phaseCcy;      // positive phase offset ccy count   | ||||
|     int8_t alignPhase;      // < 0 no phase alignment, otherwise starts waveform in relative phase offset to given pin | ||||
|  | ||||
|     uint32_t(*timer1CB)() = nullptr; | ||||
|  | ||||
|     bool timer1Running = false; | ||||
|  | ||||
|     uint32_t nextEventCcy; | ||||
|   } waveform; | ||||
|  | ||||
| } | ||||
|  | ||||
| // Interrupt on/off control | ||||
| static IRAM_ATTR void timer1Interrupt(); | ||||
|  | ||||
| // Non-speed critical bits | ||||
| #pragma GCC optimize ("Os") | ||||
|  | ||||
| static void initTimer() { | ||||
|   timer1_disable(); | ||||
|   ETS_FRC_TIMER1_INTR_ATTACH(NULL, NULL); | ||||
|   ETS_FRC_TIMER1_NMI_INTR_ATTACH(timer1Interrupt); | ||||
|   timer1_enable(TIM_DIV1, TIM_EDGE, TIM_SINGLE); | ||||
|   waveform.timer1Running = true; | ||||
|   timer1_write(IRQLATENCYCCYS); // Cause an interrupt post-haste | ||||
| } | ||||
|  | ||||
| static void IRAM_ATTR deinitTimer() { | ||||
|   ETS_FRC_TIMER1_NMI_INTR_ATTACH(NULL); | ||||
|   timer1_disable(); | ||||
|   timer1_isr_init(); | ||||
|   waveform.timer1Running = false; | ||||
| } | ||||
|  | ||||
| extern "C" { | ||||
|  | ||||
| // Set a callback.  Pass in NULL to stop it | ||||
| void setTimer1Callback_weak(uint32_t (*fn)()) { | ||||
|   waveform.timer1CB = fn; | ||||
|   std::atomic_thread_fence(std::memory_order_acq_rel); | ||||
|   if (!waveform.timer1Running && fn) { | ||||
|     initTimer(); | ||||
|   } else if (waveform.timer1Running && !fn && !waveform.enabled) { | ||||
|     deinitTimer(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| // Start up a waveform on a pin, or change the current one.  Will change to the new | ||||
| // waveform smoothly on next low->high transition.  For immediate change, stopWaveform() | ||||
| // first, then it will immediately begin. | ||||
| int startWaveformClockCycles_weak(uint8_t pin, uint32_t highCcys, uint32_t lowCcys, | ||||
|   uint32_t runTimeCcys, int8_t alignPhase, uint32_t phaseOffsetCcys, bool autoPwm) { | ||||
|   uint32_t periodCcys = highCcys + lowCcys; | ||||
|   if (periodCcys < MAXIRQTICKSCCYS) { | ||||
|     if (!highCcys) { | ||||
|       periodCcys = (MAXIRQTICKSCCYS / periodCcys) * periodCcys; | ||||
|     } | ||||
|     else if (!lowCcys) { | ||||
|       highCcys = periodCcys = (MAXIRQTICKSCCYS / periodCcys) * periodCcys; | ||||
|     } | ||||
|   } | ||||
|   // sanity checks, including mixed signed/unsigned arithmetic safety | ||||
|   if ((pin > 16) || isFlashInterfacePin(pin) || (alignPhase > 16) || | ||||
|     static_cast<int32_t>(periodCcys) <= 0 || | ||||
|     static_cast<int32_t>(highCcys) < 0 || static_cast<int32_t>(lowCcys) < 0) { | ||||
|     return false; | ||||
|   } | ||||
|   Waveform& wave = waveform.pins[pin]; | ||||
|   wave.dutyCcys = highCcys; | ||||
|   wave.adjDutyCcys = 0; | ||||
|   wave.periodCcys = periodCcys; | ||||
|   wave.autoPwm = autoPwm; | ||||
|   waveform.alignPhase = (alignPhase < 0) ? -1 : alignPhase; | ||||
|   waveform.phaseCcy = phaseOffsetCcys; | ||||
|  | ||||
|   std::atomic_thread_fence(std::memory_order_acquire); | ||||
|   const uint32_t pinBit = 1UL << pin; | ||||
|   if (!(waveform.enabled & pinBit)) { | ||||
|     // wave.nextPeriodCcy and wave.endDutyCcy are initialized by the ISR | ||||
|     wave.expiryCcy = runTimeCcys; // in WaveformMode::INIT, temporarily hold relative cycle count | ||||
|     wave.mode = WaveformMode::INIT; | ||||
|     if (!wave.dutyCcys) { | ||||
|       // If initially at zero duty cycle, force GPIO off | ||||
|       if (pin == 16) { | ||||
|         GP16O = 0; | ||||
|       } | ||||
|       else { | ||||
|         GPOC = pinBit; | ||||
|       } | ||||
|     } | ||||
|     std::atomic_thread_fence(std::memory_order_release); | ||||
|     waveform.toSetBits = 1UL << pin; | ||||
|     std::atomic_thread_fence(std::memory_order_release); | ||||
|     if (!waveform.timer1Running) { | ||||
|       initTimer(); | ||||
|     } | ||||
|     else if (T1V > IRQLATENCYCCYS) { | ||||
|       // Must not interfere if Timer is due shortly | ||||
|       timer1_write(IRQLATENCYCCYS); | ||||
|     } | ||||
|   } | ||||
|   else { | ||||
|     wave.mode = WaveformMode::INFINITE; // turn off possible expiry to make update atomic from NMI | ||||
|     std::atomic_thread_fence(std::memory_order_release); | ||||
|     if (runTimeCcys) { | ||||
|       wave.expiryCcy = runTimeCcys; // in WaveformMode::UPDATEEXPIRY, temporarily hold relative cycle count | ||||
|       wave.mode = WaveformMode::UPDATEEXPIRY; | ||||
|       std::atomic_thread_fence(std::memory_order_release); | ||||
|       waveform.toSetBits = 1UL << pin; | ||||
|     } else if (alignPhase >= 0) { | ||||
|       // @willmmiles new feature | ||||
|       wave.mode = WaveformMode::UPDATEPHASE; // recalculate start | ||||
|       std::atomic_thread_fence(std::memory_order_release); | ||||
|       waveform.toSetBits = 1UL << pin; | ||||
|     } | ||||
|   } | ||||
|   std::atomic_thread_fence(std::memory_order_acq_rel); | ||||
|   while (waveform.toSetBits) { | ||||
|     esp_yield(); // Wait for waveform to update | ||||
|     std::atomic_thread_fence(std::memory_order_acquire); | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| // Stops a waveform on a pin | ||||
| IRAM_ATTR int stopWaveform_weak(uint8_t pin) { | ||||
|   // Can't possibly need to stop anything if there is no timer active | ||||
|   if (!waveform.timer1Running) { | ||||
|     return false; | ||||
|   } | ||||
|   // If user sends in a pin >16 but <32, this will always point to a 0 bit | ||||
|   // If they send >=32, then the shift will result in 0 and it will also return false | ||||
|   std::atomic_thread_fence(std::memory_order_acquire); | ||||
|   const uint32_t pinBit = 1UL << pin; | ||||
|   if (waveform.enabled & pinBit) { | ||||
|     waveform.toDisableBits = 1UL << pin; | ||||
|     std::atomic_thread_fence(std::memory_order_release); | ||||
|     // Must not interfere if Timer is due shortly | ||||
|     if (T1V > IRQLATENCYCCYS) { | ||||
|       timer1_write(IRQLATENCYCCYS); | ||||
|     } | ||||
|     while (waveform.toDisableBits) { | ||||
|       /* no-op */ // Can't delay() since stopWaveform may be called from an IRQ | ||||
|       std::atomic_thread_fence(std::memory_order_acquire); | ||||
|     } | ||||
|   } | ||||
|   if (!waveform.enabled && !waveform.timer1CB) { | ||||
|     deinitTimer(); | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| }; | ||||
|  | ||||
| // Speed critical bits | ||||
| #pragma GCC optimize ("O2") | ||||
|  | ||||
| // For dynamic CPU clock frequency switch in loop the scaling logic would have to be adapted. | ||||
| // Using constexpr makes sure that the CPU clock frequency is compile-time fixed. | ||||
| static inline IRAM_ATTR int32_t scaleCcys(const int32_t ccys, const bool isCPU2X) { | ||||
|   if (ISCPUFREQ160MHZ) { | ||||
|     return isCPU2X ? ccys : (ccys >> 1); | ||||
|   } | ||||
|   else { | ||||
|     return isCPU2X ? (ccys << 1) : ccys; | ||||
|   } | ||||
| } | ||||
|  | ||||
| static IRAM_ATTR void timer1Interrupt() { | ||||
|   const uint32_t isrStartCcy = ESP.getCycleCount(); | ||||
|   //int32_t clockDrift = isrStartCcy - waveform.nextEventCcy; | ||||
|  | ||||
|   // ----- @willmmiles begin patch ----- | ||||
|   nmiCrashWorkaround(); | ||||
|   // ----- @willmmiles end patch ----- | ||||
|  | ||||
|   const bool isCPU2X = CPU2X & 1; | ||||
|   if ((waveform.toSetBits && !(waveform.enabled & waveform.toSetBits)) || waveform.toDisableBits) { | ||||
|     // Handle enable/disable requests from main app. | ||||
|     waveform.enabled = (waveform.enabled & ~waveform.toDisableBits) | waveform.toSetBits; // Set the requested waveforms on/off | ||||
|     // Find the first GPIO being generated by checking GCC's find-first-set (returns 1 + the bit of the first 1 in an int32_t) | ||||
|     waveform.toDisableBits = 0; | ||||
|   } | ||||
|  | ||||
|   if (waveform.toSetBits) { | ||||
|     const int toSetPin = __builtin_ffs(waveform.toSetBits) - 1; | ||||
|     Waveform& wave = waveform.pins[toSetPin]; | ||||
|     switch (wave.mode) { | ||||
|     case WaveformMode::INIT: | ||||
|       waveform.states &= ~waveform.toSetBits; // Clear the state of any just started | ||||
|       if (waveform.alignPhase >= 0 && waveform.enabled & (1UL << waveform.alignPhase)) { | ||||
|         wave.nextPeriodCcy = waveform.pins[waveform.alignPhase].nextPeriodCcy + scaleCcys(waveform.phaseCcy, isCPU2X); | ||||
|       } | ||||
|       else { | ||||
|         wave.nextPeriodCcy = waveform.nextEventCcy; | ||||
|       } | ||||
|       if (!wave.expiryCcy) { | ||||
|         wave.mode = WaveformMode::INFINITE; | ||||
|         break; | ||||
|       } | ||||
|       // fall through | ||||
|     case WaveformMode::UPDATEEXPIRY: | ||||
|       // in WaveformMode::UPDATEEXPIRY, expiryCcy temporarily holds relative CPU cycle count | ||||
|       wave.expiryCcy = wave.nextPeriodCcy + scaleCcys(wave.expiryCcy, isCPU2X); | ||||
|       wave.mode = WaveformMode::EXPIRES; | ||||
|       break; | ||||
|     // @willmmiles new feature | ||||
|     case WaveformMode::UPDATEPHASE: | ||||
|       // in WaveformMode::UPDATEPHASE, we recalculate the targets | ||||
|       if ((waveform.alignPhase >= 0) && (waveform.enabled & (1UL << waveform.alignPhase))) { | ||||
|         // Compute phase shift to realign with target | ||||
|         auto const newPeriodCcy = waveform.pins[waveform.alignPhase].nextPeriodCcy + scaleCcys(waveform.phaseCcy, isCPU2X); | ||||
|         auto const period = scaleCcys(wave.periodCcys, isCPU2X); | ||||
|         auto shift = ((static_cast<int32_t> (newPeriodCcy - wave.nextPeriodCcy) + period/2) % period) - (period/2); | ||||
|         wave.nextPeriodCcy += static_cast<uint32_t>(shift); | ||||
|         if (static_cast<int32_t>(wave.endDutyCcy - wave.nextPeriodCcy) > 0) { | ||||
|           wave.endDutyCcy = wave.nextPeriodCcy; | ||||
|         } | ||||
|       } | ||||
|     default: | ||||
|       break; | ||||
|     } | ||||
|     waveform.toSetBits = 0; | ||||
|   } | ||||
|  | ||||
|   // Exit the loop if the next event, if any, is sufficiently distant. | ||||
|   const uint32_t isrTimeoutCcy = isrStartCcy + ISRTIMEOUTCCYS; | ||||
|   uint32_t busyPins = waveform.enabled; | ||||
|   waveform.nextEventCcy = isrStartCcy + MAXIRQTICKSCCYS; | ||||
|  | ||||
|   uint32_t now = ESP.getCycleCount(); | ||||
|   uint32_t isrNextEventCcy = now; | ||||
|   while (busyPins) { | ||||
|     if (static_cast<int32_t>(isrNextEventCcy - now) > IRQLATENCYCCYS) { | ||||
|       waveform.nextEventCcy = isrNextEventCcy; | ||||
|       break; | ||||
|     } | ||||
|     isrNextEventCcy = waveform.nextEventCcy; | ||||
|     uint32_t loopPins = busyPins; | ||||
|     while (loopPins) { | ||||
|       const int pin = __builtin_ffsl(loopPins) - 1; | ||||
|       const uint32_t pinBit = 1UL << pin; | ||||
|       loopPins ^= pinBit; | ||||
|  | ||||
|       Waveform& wave = waveform.pins[pin]; | ||||
|  | ||||
| /* @willmmiles - wtf?  We don't want to accumulate drift | ||||
|       if (clockDrift) { | ||||
|         wave.endDutyCcy += clockDrift; | ||||
|         wave.nextPeriodCcy += clockDrift; | ||||
|         wave.expiryCcy += clockDrift; | ||||
|       } | ||||
| */           | ||||
|  | ||||
|       uint32_t waveNextEventCcy = (waveform.states & pinBit) ? wave.endDutyCcy : wave.nextPeriodCcy; | ||||
|       if (WaveformMode::EXPIRES == wave.mode && | ||||
|         static_cast<int32_t>(waveNextEventCcy - wave.expiryCcy) >= 0 && | ||||
|         static_cast<int32_t>(now - wave.expiryCcy) >= 0) { | ||||
|         // Disable any waveforms that are done | ||||
|         waveform.enabled ^= pinBit; | ||||
|         busyPins ^= pinBit; | ||||
|       } | ||||
|       else { | ||||
|         const int32_t overshootCcys = now - waveNextEventCcy; | ||||
|         if (overshootCcys >= 0) { | ||||
|           const int32_t periodCcys = scaleCcys(wave.periodCcys, isCPU2X); | ||||
|           if (waveform.states & pinBit) { | ||||
|             // active configuration and forward are 100% duty | ||||
|             if (wave.periodCcys == wave.dutyCcys) { | ||||
|               wave.nextPeriodCcy += periodCcys; | ||||
|               wave.endDutyCcy = wave.nextPeriodCcy; | ||||
|             } | ||||
|             else { | ||||
|               if (wave.autoPwm) { | ||||
|                 wave.adjDutyCcys += overshootCcys; | ||||
|               } | ||||
|               waveform.states ^= pinBit; | ||||
|               if (16 == pin) { | ||||
|                 GP16O = 0; | ||||
|               } | ||||
|               else { | ||||
|                 GPOC = pinBit; | ||||
|               } | ||||
|             } | ||||
|             waveNextEventCcy = wave.nextPeriodCcy; | ||||
|           } | ||||
|           else { | ||||
|             wave.nextPeriodCcy += periodCcys; | ||||
|             if (!wave.dutyCcys) { | ||||
|               wave.endDutyCcy = wave.nextPeriodCcy; | ||||
|             } | ||||
|             else { | ||||
|               int32_t dutyCcys = scaleCcys(wave.dutyCcys, isCPU2X); | ||||
|               if (dutyCcys <= wave.adjDutyCcys) { | ||||
|                 dutyCcys >>= 1; | ||||
|                 wave.adjDutyCcys -= dutyCcys; | ||||
|               } | ||||
|               else if (wave.adjDutyCcys) { | ||||
|                 dutyCcys -= wave.adjDutyCcys; | ||||
|                 wave.adjDutyCcys = 0; | ||||
|               } | ||||
|               wave.endDutyCcy = now + dutyCcys; | ||||
|               if (static_cast<int32_t>(wave.endDutyCcy - wave.nextPeriodCcy) > 0) { | ||||
|                 wave.endDutyCcy = wave.nextPeriodCcy; | ||||
|               } | ||||
|               waveform.states |= pinBit; | ||||
|               if (16 == pin) { | ||||
|                 GP16O = 1; | ||||
|               } | ||||
|               else { | ||||
|                 GPOS = pinBit; | ||||
|               } | ||||
|             } | ||||
|             waveNextEventCcy = wave.endDutyCcy; | ||||
|           } | ||||
|  | ||||
|           if (WaveformMode::EXPIRES == wave.mode && static_cast<int32_t>(waveNextEventCcy - wave.expiryCcy) > 0) { | ||||
|             waveNextEventCcy = wave.expiryCcy; | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         if (static_cast<int32_t>(waveNextEventCcy - isrTimeoutCcy) >= 0) { | ||||
|           busyPins ^= pinBit; | ||||
|           if (static_cast<int32_t>(waveform.nextEventCcy - waveNextEventCcy) > 0) { | ||||
|             waveform.nextEventCcy = waveNextEventCcy; | ||||
|           } | ||||
|         } | ||||
|         else if (static_cast<int32_t>(isrNextEventCcy - waveNextEventCcy) > 0) { | ||||
|           isrNextEventCcy = waveNextEventCcy; | ||||
|         } | ||||
|       } | ||||
|       now = ESP.getCycleCount(); | ||||
|     } | ||||
|     //clockDrift = 0; | ||||
|   } | ||||
|  | ||||
|   int32_t callbackCcys = 0; | ||||
|   if (waveform.timer1CB) { | ||||
|     callbackCcys = scaleCcys(waveform.timer1CB(), isCPU2X); | ||||
|   } | ||||
|   now = ESP.getCycleCount(); | ||||
|   int32_t nextEventCcys = waveform.nextEventCcy - now; | ||||
|   // Account for unknown duration of timer1CB(). | ||||
|   if (waveform.timer1CB && nextEventCcys > callbackCcys) { | ||||
|     waveform.nextEventCcy = now + callbackCcys; | ||||
|     nextEventCcys = callbackCcys; | ||||
|   } | ||||
|  | ||||
|   // Timer is 80MHz fixed. 160MHz CPU frequency need scaling. | ||||
|   int32_t deltaIrqCcys = DELTAIRQCCYS; | ||||
|   int32_t irqLatencyCcys = IRQLATENCYCCYS; | ||||
|   if (isCPU2X) { | ||||
|     nextEventCcys >>= 1; | ||||
|     deltaIrqCcys >>= 1; | ||||
|     irqLatencyCcys >>= 1; | ||||
|   } | ||||
|  | ||||
|   // Firing timer too soon, the NMI occurs before ISR has returned. | ||||
|   if (nextEventCcys < irqLatencyCcys + deltaIrqCcys) { | ||||
|     waveform.nextEventCcy = now + IRQLATENCYCCYS + DELTAIRQCCYS; | ||||
|     nextEventCcys = irqLatencyCcys; | ||||
|   } | ||||
|   else { | ||||
|     nextEventCcys -= deltaIrqCcys; | ||||
|   } | ||||
|  | ||||
|   // Register access is fast and edge IRQ was configured before. | ||||
|   T1L = nextEventCcys; | ||||
| } | ||||
| @@ -1,717 +0,0 @@ | ||||
| /* esp8266_waveform imported from platform source code | ||||
|    Modified for WLED to work around a fault in the NMI handling, | ||||
|    which can result in the system locking up and hard WDT crashes. | ||||
|  | ||||
|    Imported from https://github.com/esp8266/Arduino/blob/7e0d20e2b9034994f573a236364e0aef17fd66de/cores/esp8266/core_esp8266_waveform_pwm.cpp | ||||
| */ | ||||
|  | ||||
| /* | ||||
|   esp8266_waveform - General purpose waveform generation and control, | ||||
|                      supporting outputs on all pins in parallel. | ||||
|  | ||||
|   Copyright (c) 2018 Earle F. Philhower, III.  All rights reserved. | ||||
|  | ||||
|   The core idea is to have a programmable waveform generator with a unique | ||||
|   high and low period (defined in microseconds or CPU clock cycles).  TIMER1 | ||||
|   is set to 1-shot mode and is always loaded with the time until the next | ||||
|   edge of any live waveforms. | ||||
|  | ||||
|   Up to one waveform generator per pin supported. | ||||
|  | ||||
|   Each waveform generator is synchronized to the ESP clock cycle counter, not | ||||
|   the timer.  This allows for removing interrupt jitter and delay as the | ||||
|   counter always increments once per 80MHz clock.  Changes to a waveform are | ||||
|   contiguous and only take effect on the next waveform transition, | ||||
|   allowing for smooth transitions. | ||||
|  | ||||
|   This replaces older tone(), analogWrite(), and the Servo classes. | ||||
|  | ||||
|   Everywhere in the code where "cycles" is used, it means ESP.getCycleCount() | ||||
|   clock cycle count, or an interval measured in CPU clock cycles, but not | ||||
|   TIMER1 cycles (which may be 2 CPU clock cycles @ 160MHz). | ||||
|  | ||||
|   This library is free software; you can redistribute it and/or | ||||
|   modify it under the terms of the GNU Lesser General Public | ||||
|   License as published by the Free Software Foundation; either | ||||
|   version 2.1 of the License, or (at your option) any later version. | ||||
|  | ||||
|   This library is distributed in the hope that it will be useful, | ||||
|   but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|   Lesser General Public License for more details. | ||||
|  | ||||
|   You should have received a copy of the GNU Lesser General Public | ||||
|   License along with this library; if not, write to the Free Software | ||||
|   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||||
| */ | ||||
|  | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #include <coredecls.h> | ||||
| #include "ets_sys.h" | ||||
| #include "core_esp8266_waveform.h" | ||||
| #include "user_interface.h" | ||||
|  | ||||
| extern "C" { | ||||
|  | ||||
| // Linker magic | ||||
| void usePWMFixedNMI() {}; | ||||
|  | ||||
| // Maximum delay between IRQs | ||||
| #define MAXIRQUS (10000) | ||||
|  | ||||
| // Waveform generator can create tones, PWM, and servos | ||||
| typedef struct { | ||||
|   uint32_t nextServiceCycle;   // ESP cycle timer when a transition required | ||||
|   uint32_t expiryCycle;        // For time-limited waveform, the cycle when this waveform must stop | ||||
|   uint32_t timeHighCycles;     // Actual running waveform period (adjusted using desiredCycles) | ||||
|   uint32_t timeLowCycles;      // | ||||
|   uint32_t desiredHighCycles;  // Ideal waveform period to drive the error signal | ||||
|   uint32_t desiredLowCycles;   // | ||||
|   uint32_t lastEdge;           // Cycle when this generator last changed | ||||
| } Waveform; | ||||
|  | ||||
| class WVFState { | ||||
| public: | ||||
|   Waveform waveform[17];        // State of all possible pins | ||||
|   uint32_t waveformState = 0;   // Is the pin high or low, updated in NMI so no access outside the NMI code | ||||
|   uint32_t waveformEnabled = 0; // Is it actively running, updated in NMI so no access outside the NMI code | ||||
|  | ||||
|   // Enable lock-free by only allowing updates to waveformState and waveformEnabled from IRQ service routine | ||||
|   uint32_t waveformToEnable = 0;  // Message to the NMI handler to start a waveform on a inactive pin | ||||
|   uint32_t waveformToDisable = 0; // Message to the NMI handler to disable a pin from waveform generation | ||||
|  | ||||
|   uint32_t waveformToChange = 0; // Mask of pin to change. One bit set in main app, cleared when effected in the NMI | ||||
|   uint32_t waveformNewHigh = 0; | ||||
|   uint32_t waveformNewLow = 0; | ||||
|  | ||||
|   uint32_t (*timer1CB)() = NULL; | ||||
|  | ||||
|   // Optimize the NMI inner loop by keeping track of the min and max GPIO that we | ||||
|   // are generating.  In the common case (1 PWM) these may be the same pin and | ||||
|   // we can avoid looking at the other pins. | ||||
|   uint16_t startPin = 0; | ||||
|   uint16_t endPin = 0; | ||||
| }; | ||||
| static WVFState wvfState; | ||||
|  | ||||
|  | ||||
| // Ensure everything is read/written to RAM | ||||
| #define MEMBARRIER() { __asm__ volatile("" ::: "memory"); } | ||||
|  | ||||
| // Non-speed critical bits | ||||
| #pragma GCC optimize ("Os") | ||||
|  | ||||
| // Interrupt on/off control | ||||
| static IRAM_ATTR void timer1Interrupt(); | ||||
| static bool timerRunning = false; | ||||
|  | ||||
| static __attribute__((noinline)) void initTimer() { | ||||
|   if (!timerRunning) { | ||||
|     timer1_disable(); | ||||
|     ETS_FRC_TIMER1_INTR_ATTACH(NULL, NULL); | ||||
|     ETS_FRC_TIMER1_NMI_INTR_ATTACH(timer1Interrupt); | ||||
|     timer1_enable(TIM_DIV1, TIM_EDGE, TIM_SINGLE); | ||||
|     timerRunning = true; | ||||
|     timer1_write(microsecondsToClockCycles(10)); | ||||
|   } | ||||
| } | ||||
|  | ||||
| static IRAM_ATTR void forceTimerInterrupt() { | ||||
|   if (T1L > microsecondsToClockCycles(10)) { | ||||
|     T1L = microsecondsToClockCycles(10); | ||||
|   } | ||||
| } | ||||
|  | ||||
| // PWM implementation using special purpose state machine | ||||
| // | ||||
| // Keep an ordered list of pins with the delta in cycles between each | ||||
| // element, with a terminal entry making up the remainder of the PWM | ||||
| // period.  With this method sum(all deltas) == PWM period clock cycles. | ||||
| // | ||||
| // At t=0 set all pins high and set the timeout for the 1st edge. | ||||
| // On interrupt, if we're at the last element reset to t=0 state | ||||
| // Otherwise, clear that pin down and set delay for next element | ||||
| // and so forth. | ||||
|  | ||||
| constexpr int maxPWMs = 8; | ||||
|  | ||||
| // PWM machine state | ||||
| typedef struct PWMState { | ||||
|   uint32_t mask; // Bitmask of active pins | ||||
|   uint32_t cnt;  // How many entries | ||||
|   uint32_t idx;  // Where the state machine is along the list | ||||
|   uint8_t  pin[maxPWMs + 1]; | ||||
|   uint32_t delta[maxPWMs + 1]; | ||||
|   uint32_t nextServiceCycle;  // Clock cycle for next step | ||||
|   struct PWMState *pwmUpdate; // Set by main code, cleared by ISR | ||||
| } PWMState; | ||||
|  | ||||
| static PWMState pwmState; | ||||
| static uint32_t _pwmFreq = 1000; | ||||
| static uint32_t _pwmPeriod = microsecondsToClockCycles(1000000UL) / _pwmFreq; | ||||
|  | ||||
|  | ||||
| // If there are no more scheduled activities, shut down Timer 1. | ||||
| // Otherwise, do nothing. | ||||
| static IRAM_ATTR void disableIdleTimer() { | ||||
|  if (timerRunning && !wvfState.waveformEnabled && !pwmState.cnt && !wvfState.timer1CB) { | ||||
|     ETS_FRC_TIMER1_NMI_INTR_ATTACH(NULL); | ||||
|     timer1_disable(); | ||||
|     timer1_isr_init(); | ||||
|     timerRunning = false; | ||||
|   } | ||||
| } | ||||
|  | ||||
| // Notify the NMI that a new PWM state is available through the mailbox. | ||||
| // Wait for mailbox to be emptied (either busy or delay() as needed) | ||||
| static IRAM_ATTR void _notifyPWM(PWMState *p, bool idle) { | ||||
|   p->pwmUpdate = nullptr; | ||||
|   pwmState.pwmUpdate = p; | ||||
|   MEMBARRIER(); | ||||
|   forceTimerInterrupt(); | ||||
|   while (pwmState.pwmUpdate) { | ||||
|     if (idle) { | ||||
|       esp_yield(); | ||||
|     } | ||||
|     MEMBARRIER(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| static void _addPWMtoList(PWMState &p, int pin, uint32_t val, uint32_t range); | ||||
|  | ||||
|  | ||||
| // Called when analogWriteFreq() changed to update the PWM total period | ||||
| //extern void _setPWMFreq_weak(uint32_t freq) __attribute__((weak));  | ||||
| void _setPWMFreq_weak(uint32_t freq) { | ||||
|   _pwmFreq = freq; | ||||
|  | ||||
|   // Convert frequency into clock cycles | ||||
|   uint32_t cc = microsecondsToClockCycles(1000000UL) / freq; | ||||
|  | ||||
|   // Simple static adjustment to bring period closer to requested due to overhead | ||||
|   // Empirically determined as a constant PWM delay and a function of the number of PWMs | ||||
| #if F_CPU == 80000000 | ||||
|   cc -= ((microsecondsToClockCycles(pwmState.cnt) * 13) >> 4) + 110; | ||||
| #else | ||||
|   cc -= ((microsecondsToClockCycles(pwmState.cnt) * 10) >> 4) + 75; | ||||
| #endif | ||||
|  | ||||
|   if (cc == _pwmPeriod) { | ||||
|     return; // No change | ||||
|   } | ||||
|  | ||||
|   _pwmPeriod = cc; | ||||
|  | ||||
|   if (pwmState.cnt) { | ||||
|     PWMState p;  // The working copy since we can't edit the one in use | ||||
|     p.mask = 0; | ||||
|     p.cnt = 0; | ||||
|     for (uint32_t i = 0; i < pwmState.cnt; i++) { | ||||
|       auto pin = pwmState.pin[i]; | ||||
|       _addPWMtoList(p, pin, wvfState.waveform[pin].desiredHighCycles, wvfState.waveform[pin].desiredLowCycles); | ||||
|     } | ||||
|     // Update and wait for mailbox to be emptied | ||||
|     initTimer(); | ||||
|     _notifyPWM(&p, true); | ||||
|     disableIdleTimer(); | ||||
|   } | ||||
| } | ||||
| /* | ||||
| static void _setPWMFreq_bound(uint32_t freq) __attribute__((weakref("_setPWMFreq_weak"))); | ||||
| void _setPWMFreq(uint32_t freq) {  | ||||
|   _setPWMFreq_bound(freq); | ||||
| } | ||||
| */ | ||||
|  | ||||
| // Helper routine to remove an entry from the state machine | ||||
| // and clean up any marked-off entries | ||||
| static void _cleanAndRemovePWM(PWMState *p, int pin) { | ||||
|   uint32_t leftover = 0; | ||||
|   uint32_t in, out; | ||||
|   for (in = 0, out = 0; in < p->cnt; in++) { | ||||
|     if ((p->pin[in] != pin) && (p->mask & (1<<p->pin[in]))) { | ||||
|         p->pin[out] = p->pin[in]; | ||||
|         p->delta[out] = p->delta[in] + leftover; | ||||
|         leftover = 0; | ||||
|         out++; | ||||
|     } else { | ||||
|         leftover += p->delta[in]; | ||||
|         p->mask &= ~(1<<p->pin[in]); | ||||
|     } | ||||
|   } | ||||
|   p->cnt = out; | ||||
|   // Final pin is never used: p->pin[out] = 0xff; | ||||
|   p->delta[out] = p->delta[in] + leftover; | ||||
| } | ||||
|  | ||||
|  | ||||
| // Disable PWM on a specific pin (i.e. when a digitalWrite or analogWrite(0%/100%)) | ||||
| //extern bool _stopPWM_weak(uint8_t pin) __attribute__((weak)); | ||||
| IRAM_ATTR bool _stopPWM_weak(uint8_t pin) { | ||||
|   if (!((1<<pin) & pwmState.mask)) { | ||||
|     return false; // Pin not actually active | ||||
|   } | ||||
|  | ||||
|   PWMState p;  // The working copy since we can't edit the one in use | ||||
|   p = pwmState; | ||||
|  | ||||
|   // In _stopPWM we just clear the mask but keep everything else | ||||
|   // untouched to save IRAM.  The main startPWM will handle cleanup. | ||||
|   p.mask &= ~(1<<pin); | ||||
|   if (!p.mask) { | ||||
|     // If all have been stopped, then turn PWM off completely | ||||
|     p.cnt = 0; | ||||
|   } | ||||
|  | ||||
|   // Update and wait for mailbox to be emptied, no delay (could be in ISR) | ||||
|   _notifyPWM(&p, false); | ||||
|   // Possibly shut down the timer completely if we're done | ||||
|   disableIdleTimer(); | ||||
|   return true; | ||||
| } | ||||
| /* | ||||
| static bool _stopPWM_bound(uint8_t pin) __attribute__((weakref("_stopPWM_weak"))); | ||||
| IRAM_ATTR bool _stopPWM(uint8_t pin) { | ||||
|   return _stopPWM_bound(pin); | ||||
| } | ||||
| */ | ||||
|  | ||||
| static void _addPWMtoList(PWMState &p, int pin, uint32_t val, uint32_t range) { | ||||
|   // Stash the val and range so we can re-evaluate the fraction | ||||
|   // should the user change PWM frequency.  This allows us to | ||||
|   // give as great a precision as possible.  We know by construction | ||||
|   // that the waveform for this pin will be inactive so we can borrow | ||||
|   // memory from that structure. | ||||
|   wvfState.waveform[pin].desiredHighCycles = val;  // Numerator == high | ||||
|   wvfState.waveform[pin].desiredLowCycles = range; // Denominator == low | ||||
|  | ||||
|   uint32_t cc = (_pwmPeriod * val) / range; | ||||
|  | ||||
|   // Clip to sane values in the case we go from OK to not-OK when adjusting frequencies | ||||
|   if (cc == 0) { | ||||
|     cc = 1; | ||||
|   } else if (cc >= _pwmPeriod) { | ||||
|     cc = _pwmPeriod - 1; | ||||
|   } | ||||
|  | ||||
|   if (p.cnt == 0) { | ||||
|     // Starting up from scratch, special case 1st element and PWM period | ||||
|     p.pin[0] = pin; | ||||
|     p.delta[0] = cc; | ||||
|    // Final pin is never used: p.pin[1] = 0xff; | ||||
|     p.delta[1] = _pwmPeriod - cc; | ||||
|   } else { | ||||
|     uint32_t ttl = 0; | ||||
|     uint32_t i; | ||||
|     // Skip along until we're at the spot to insert | ||||
|     for (i=0; (i <= p.cnt) && (ttl + p.delta[i] < cc); i++) { | ||||
|       ttl += p.delta[i]; | ||||
|     } | ||||
|     // Shift everything out by one to make space for new edge | ||||
|     for (int32_t j = p.cnt; j >= (int)i; j--) { | ||||
|       p.pin[j + 1] = p.pin[j]; | ||||
|       p.delta[j + 1] = p.delta[j]; | ||||
|     } | ||||
|     int off = cc - ttl; // The delta from the last edge to the one we're inserting | ||||
|     p.pin[i] = pin; | ||||
|     p.delta[i] = off; // Add the delta to this new pin | ||||
|     p.delta[i + 1] -= off; // And subtract it from the follower to keep sum(deltas) constant | ||||
|   } | ||||
|   p.cnt++; | ||||
|   p.mask |= 1<<pin; | ||||
| } | ||||
|  | ||||
| // Called by analogWrite(1...99%) to set the PWM duty in clock cycles | ||||
| //extern bool _setPWM_weak(int pin, uint32_t val, uint32_t range) __attribute__((weak)); | ||||
| bool _setPWM_weak(int pin, uint32_t val, uint32_t range) { | ||||
|   stopWaveform(pin); | ||||
|   PWMState p;  // Working copy | ||||
|   p = pwmState; | ||||
|   // Get rid of any entries for this pin | ||||
|   _cleanAndRemovePWM(&p, pin); | ||||
|   // And add it to the list, in order | ||||
|   if (p.cnt >= maxPWMs) { | ||||
|     return false; // No space left | ||||
|   } | ||||
|  | ||||
|   // Sanity check for all-on/off | ||||
|   uint32_t cc = (_pwmPeriod * val) / range; | ||||
|   if ((cc == 0) || (cc >= _pwmPeriod)) { | ||||
|     digitalWrite(pin, cc ? HIGH : LOW); | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   _addPWMtoList(p, pin, val, range); | ||||
|  | ||||
|   // Set mailbox and wait for ISR to copy it over | ||||
|   initTimer(); | ||||
|   _notifyPWM(&p, true); | ||||
|   disableIdleTimer(); | ||||
|  | ||||
|   // Potentially recalculate the PWM period if we've added another pin | ||||
|   _setPWMFreq(_pwmFreq); | ||||
|  | ||||
|   return true; | ||||
| } | ||||
| /* | ||||
| static bool _setPWM_bound(int pin, uint32_t val, uint32_t range) __attribute__((weakref("_setPWM_weak"))); | ||||
| bool _setPWM(int pin, uint32_t val, uint32_t range) { | ||||
|   return _setPWM_bound(pin, val, range); | ||||
| } | ||||
| */ | ||||
|  | ||||
| // Start up a waveform on a pin, or change the current one.  Will change to the new | ||||
| // waveform smoothly on next low->high transition.  For immediate change, stopWaveform() | ||||
| // first, then it will immediately begin. | ||||
| //extern int startWaveformClockCycles_weak(uint8_t pin, uint32_t timeHighCycles, uint32_t timeLowCycles, uint32_t runTimeCycles, int8_t alignPhase, uint32_t phaseOffsetUS, bool autoPwm)  __attribute__((weak)); | ||||
| int startWaveformClockCycles_weak(uint8_t pin, uint32_t timeHighCycles, uint32_t timeLowCycles, uint32_t runTimeCycles, | ||||
|                              int8_t alignPhase, uint32_t phaseOffsetUS, bool autoPwm) { | ||||
|   (void) alignPhase; | ||||
|   (void) phaseOffsetUS; | ||||
|   (void) autoPwm; | ||||
|  | ||||
|    if ((pin > 16) || isFlashInterfacePin(pin) || (timeHighCycles == 0)) { | ||||
|     return false; | ||||
|   } | ||||
|   Waveform *wave = &wvfState.waveform[pin]; | ||||
|   wave->expiryCycle = runTimeCycles ? ESP.getCycleCount() + runTimeCycles : 0; | ||||
|   if (runTimeCycles && !wave->expiryCycle) { | ||||
|     wave->expiryCycle = 1; // expiryCycle==0 means no timeout, so avoid setting it | ||||
|   } | ||||
|  | ||||
|   _stopPWM(pin); // Make sure there's no PWM live here | ||||
|  | ||||
|   uint32_t mask = 1<<pin; | ||||
|   MEMBARRIER(); | ||||
|   if (wvfState.waveformEnabled & mask) { | ||||
|     // Make sure no waveform changes are waiting to be applied | ||||
|     while (wvfState.waveformToChange) { | ||||
|       esp_yield(); // Wait for waveform to update | ||||
|       MEMBARRIER(); | ||||
|     } | ||||
|     wvfState.waveformNewHigh = timeHighCycles; | ||||
|     wvfState.waveformNewLow = timeLowCycles; | ||||
|     MEMBARRIER(); | ||||
|     wvfState.waveformToChange = mask; | ||||
|     // The waveform will be updated some time in the future on the next period for the signal | ||||
|   } else { //  if (!(wvfState.waveformEnabled & mask)) { | ||||
|     wave->timeHighCycles = timeHighCycles; | ||||
|     wave->desiredHighCycles = timeHighCycles; | ||||
|     wave->timeLowCycles = timeLowCycles; | ||||
|     wave->desiredLowCycles = timeLowCycles; | ||||
|     wave->lastEdge = 0; | ||||
|     wave->nextServiceCycle = ESP.getCycleCount() + microsecondsToClockCycles(1); | ||||
|     wvfState.waveformToEnable |= mask; | ||||
|     MEMBARRIER(); | ||||
|     initTimer(); | ||||
|     forceTimerInterrupt(); | ||||
|     while (wvfState.waveformToEnable) { | ||||
|       esp_yield(); // Wait for waveform to update | ||||
|       MEMBARRIER(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return true; | ||||
| } | ||||
| /* | ||||
| static int startWaveformClockCycles_bound(uint8_t pin, uint32_t timeHighCycles, uint32_t timeLowCycles, uint32_t runTimeCycles, int8_t alignPhase, uint32_t phaseOffsetUS, bool autoPwm) __attribute__((weakref("startWaveformClockCycles_weak"))); | ||||
| int startWaveformClockCycles(uint8_t pin, uint32_t timeHighCycles, uint32_t timeLowCycles, uint32_t runTimeCycles, int8_t alignPhase, uint32_t phaseOffsetUS, bool autoPwm) { | ||||
|   return startWaveformClockCycles_bound(pin, timeHighCycles, timeLowCycles, runTimeCycles, alignPhase, phaseOffsetUS, autoPwm); | ||||
| } | ||||
|  | ||||
|  | ||||
| // This version falls-thru to the proper startWaveformClockCycles call and is invariant across waveform generators | ||||
| int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t runTimeUS, | ||||
|                   int8_t alignPhase, uint32_t phaseOffsetUS, bool autoPwm) { | ||||
|   return startWaveformClockCycles_bound(pin, | ||||
|     microsecondsToClockCycles(timeHighUS), microsecondsToClockCycles(timeLowUS), | ||||
|     microsecondsToClockCycles(runTimeUS), alignPhase, microsecondsToClockCycles(phaseOffsetUS), autoPwm); | ||||
| } | ||||
| */ | ||||
|  | ||||
| // Set a callback.  Pass in NULL to stop it | ||||
| //extern void setTimer1Callback_weak(uint32_t (*fn)()) __attribute__((weak)); | ||||
| void setTimer1Callback_weak(uint32_t (*fn)()) { | ||||
|   wvfState.timer1CB = fn; | ||||
|   if (fn) { | ||||
|     initTimer(); | ||||
|     forceTimerInterrupt(); | ||||
|   } | ||||
|   disableIdleTimer(); | ||||
| } | ||||
| /* | ||||
| static void setTimer1Callback_bound(uint32_t (*fn)()) __attribute__((weakref("setTimer1Callback_weak"))); | ||||
| void setTimer1Callback(uint32_t (*fn)()) { | ||||
|   setTimer1Callback_bound(fn); | ||||
| } | ||||
| */ | ||||
|  | ||||
| // Stops a waveform on a pin | ||||
| //extern int stopWaveform_weak(uint8_t pin) __attribute__((weak)); | ||||
| IRAM_ATTR int stopWaveform_weak(uint8_t pin) { | ||||
|   // Can't possibly need to stop anything if there is no timer active | ||||
|   if (!timerRunning) { | ||||
|     return false; | ||||
|   } | ||||
|   // If user sends in a pin >16 but <32, this will always point to a 0 bit | ||||
|   // If they send >=32, then the shift will result in 0 and it will also return false | ||||
|   uint32_t mask = 1<<pin; | ||||
|   if (wvfState.waveformEnabled & mask) { | ||||
|     wvfState.waveformToDisable = mask; | ||||
|     // Cancel any pending updates for this waveform, too. | ||||
|     if (wvfState.waveformToChange & mask) { | ||||
|         wvfState.waveformToChange = 0; | ||||
|     } | ||||
|     forceTimerInterrupt(); | ||||
|     while (wvfState.waveformToDisable) { | ||||
|       MEMBARRIER(); // If it wasn't written yet, it has to be by now | ||||
|       /* no-op */ // Can't delay() since stopWaveform may be called from an IRQ | ||||
|     } | ||||
|   } | ||||
|   disableIdleTimer(); | ||||
|   return true; | ||||
| } | ||||
| /* | ||||
| static int stopWaveform_bound(uint8_t pin) __attribute__((weakref("stopWaveform_weak"))); | ||||
| IRAM_ATTR int stopWaveform(uint8_t pin) { | ||||
|   return stopWaveform_bound(pin); | ||||
| } | ||||
| */ | ||||
|  | ||||
| // Speed critical bits | ||||
| #pragma GCC optimize ("O2") | ||||
|  | ||||
| // Normally would not want two copies like this, but due to different | ||||
| // optimization levels the inline attribute gets lost if we try the | ||||
| // other version. | ||||
| static inline IRAM_ATTR uint32_t GetCycleCountIRQ() { | ||||
|   uint32_t ccount; | ||||
|   __asm__ __volatile__("rsr %0,ccount":"=a"(ccount)); | ||||
|   return ccount; | ||||
| } | ||||
|  | ||||
| // Find the earliest cycle as compared to right now | ||||
| static inline IRAM_ATTR uint32_t earliest(uint32_t a, uint32_t b) { | ||||
|     uint32_t now = GetCycleCountIRQ(); | ||||
|     int32_t da = a - now; | ||||
|     int32_t db = b - now; | ||||
|     return (da < db) ? a : b; | ||||
| } | ||||
|  | ||||
| // ----- @willmmiles begin patch ----- | ||||
| // NMI crash workaround | ||||
| // Sometimes the NMI fails to return, stalling the CPU.  When this happens, | ||||
| // the next NMI gets a return address /inside the NMI handler function/. | ||||
| // We work around this by caching the last NMI return address, and restoring | ||||
| // the epc3 and eps3 registers to the previous values if the observed epc3 | ||||
| // happens to be pointing to the _NMILevelVector function. | ||||
| extern void _NMILevelVector(); | ||||
| extern void _UserExceptionVector_1(); // the next function after _NMILevelVector | ||||
| static inline IRAM_ATTR void nmiCrashWorkaround() { | ||||
|   static uintptr_t epc3_backup, eps3_backup; | ||||
|  | ||||
|   uintptr_t epc3, eps3; | ||||
|   __asm__ __volatile__("rsr %0,epc3; rsr %1,eps3":"=a"(epc3),"=a" (eps3)); | ||||
|   if ((epc3 < (uintptr_t) &_NMILevelVector) || (epc3 >= (uintptr_t) &_UserExceptionVector_1)) { | ||||
|     // Address is good; save backup | ||||
|     epc3_backup = epc3; | ||||
|     eps3_backup = eps3; | ||||
|   } else { | ||||
|     // Address is inside the NMI handler -- restore from backup | ||||
|     __asm__ __volatile__("wsr %0,epc3; wsr %1,eps3"::"a"(epc3_backup),"a"(eps3_backup)); | ||||
|   } | ||||
| } | ||||
| // ----- @willmmiles end patch ----- | ||||
|  | ||||
|  | ||||
| // The SDK and hardware take some time to actually get to our NMI code, so | ||||
| // decrement the next IRQ's timer value by a bit so we can actually catch the | ||||
| // real CPU cycle counter we want for the waveforms. | ||||
|  | ||||
| // The SDK also sometimes is running at a different speed the the Arduino core | ||||
| // so the ESP cycle counter is actually running at a variable speed. | ||||
| // adjust(x) takes care of adjusting a delta clock cycle amount accordingly. | ||||
| #if F_CPU == 80000000 | ||||
|   #define DELTAIRQ (microsecondsToClockCycles(9)/4) | ||||
|   #define adjust(x) ((x) << (turbo ? 1 : 0)) | ||||
| #else | ||||
|   #define DELTAIRQ (microsecondsToClockCycles(9)/8) | ||||
|   #define adjust(x) ((x) >> 0) | ||||
| #endif | ||||
|  | ||||
| // When the time to the next edge is greater than this, RTI and set another IRQ to minimize CPU usage | ||||
| #define MINIRQTIME microsecondsToClockCycles(6) | ||||
|  | ||||
| static IRAM_ATTR void timer1Interrupt() { | ||||
|   // ----- @willmmiles begin patch ----- | ||||
|   nmiCrashWorkaround(); | ||||
|   // ----- @willmmiles end patch ----- | ||||
|  | ||||
|   // Flag if the core is at 160 MHz, for use by adjust() | ||||
|   bool turbo = (*(uint32_t*)0x3FF00014) & 1 ? true : false; | ||||
|  | ||||
|   uint32_t nextEventCycle = GetCycleCountIRQ() + microsecondsToClockCycles(MAXIRQUS); | ||||
|   uint32_t timeoutCycle = GetCycleCountIRQ() + microsecondsToClockCycles(14); | ||||
|  | ||||
|   if (wvfState.waveformToEnable || wvfState.waveformToDisable) { | ||||
|     // Handle enable/disable requests from main app | ||||
|     wvfState.waveformEnabled = (wvfState.waveformEnabled & ~wvfState.waveformToDisable) | wvfState.waveformToEnable; // Set the requested waveforms on/off | ||||
|     wvfState.waveformState &= ~wvfState.waveformToEnable;  // And clear the state of any just started | ||||
|     wvfState.waveformToEnable = 0; | ||||
|     wvfState.waveformToDisable = 0; | ||||
|     // No mem barrier.  Globals must be written to RAM on ISR exit. | ||||
|     // Find the first GPIO being generated by checking GCC's find-first-set (returns 1 + the bit of the first 1 in an int32_t) | ||||
|     wvfState.startPin = __builtin_ffs(wvfState.waveformEnabled) - 1; | ||||
|     // Find the last bit by subtracting off GCC's count-leading-zeros (no offset in this one) | ||||
|     wvfState.endPin = 32 - __builtin_clz(wvfState.waveformEnabled); | ||||
|   } else if (!pwmState.cnt && pwmState.pwmUpdate) { | ||||
|     // Start up the PWM generator by copying from the mailbox | ||||
|     pwmState.cnt = 1; | ||||
|     pwmState.idx = 1; // Ensure copy this cycle, cause it to start at t=0 | ||||
|     pwmState.nextServiceCycle = GetCycleCountIRQ(); // Do it this loop! | ||||
|     // No need for mem barrier here.  Global must be written by IRQ exit | ||||
|   } | ||||
|  | ||||
|   bool done = false; | ||||
|   if (wvfState.waveformEnabled || pwmState.cnt) { | ||||
|     do { | ||||
|       nextEventCycle = GetCycleCountIRQ() + microsecondsToClockCycles(MAXIRQUS); | ||||
|  | ||||
|       // PWM state machine implementation | ||||
|       if (pwmState.cnt) { | ||||
|         int32_t cyclesToGo; | ||||
|         do { | ||||
|             cyclesToGo = pwmState.nextServiceCycle - GetCycleCountIRQ(); | ||||
|             if (cyclesToGo < 0) { | ||||
|                 if (pwmState.idx == pwmState.cnt) { // Start of pulses, possibly copy new | ||||
|                   if (pwmState.pwmUpdate) { | ||||
|                     // Do the memory copy from temp to global and clear mailbox | ||||
|                     pwmState = *(PWMState*)pwmState.pwmUpdate; | ||||
|                   } | ||||
|                   GPOS = pwmState.mask; // Set all active pins high | ||||
|                   if (pwmState.mask & (1<<16)) { | ||||
|                     GP16O = 1; | ||||
|                   } | ||||
|                   pwmState.idx = 0; | ||||
|                 } else { | ||||
|                   do { | ||||
|                     // Drop the pin at this edge | ||||
|                     if (pwmState.mask & (1<<pwmState.pin[pwmState.idx])) { | ||||
|                       GPOC = 1<<pwmState.pin[pwmState.idx]; | ||||
|                       if (pwmState.pin[pwmState.idx] == 16) { | ||||
|                         GP16O = 0; | ||||
|                       } | ||||
|                     } | ||||
|                     pwmState.idx++; | ||||
|                     // Any other pins at this same PWM value will have delta==0, drop them too. | ||||
|                   } while (pwmState.delta[pwmState.idx] == 0); | ||||
|                 } | ||||
|                 // Preserve duty cycle over PWM period by using now+xxx instead of += delta | ||||
|                 cyclesToGo = adjust(pwmState.delta[pwmState.idx]); | ||||
|                 pwmState.nextServiceCycle = GetCycleCountIRQ() + cyclesToGo; | ||||
|             } | ||||
|             nextEventCycle = earliest(nextEventCycle, pwmState.nextServiceCycle); | ||||
|         } while (pwmState.cnt && (cyclesToGo < 100)); | ||||
|       } | ||||
|  | ||||
|       for (auto i = wvfState.startPin; i <= wvfState.endPin; i++) { | ||||
|         uint32_t mask = 1<<i; | ||||
|  | ||||
|         // If it's not on, ignore! | ||||
|         if (!(wvfState.waveformEnabled & mask)) { | ||||
|           continue; | ||||
|         } | ||||
|  | ||||
|         Waveform *wave = &wvfState.waveform[i]; | ||||
|         uint32_t now = GetCycleCountIRQ(); | ||||
|  | ||||
|         // Disable any waveforms that are done | ||||
|         if (wave->expiryCycle) { | ||||
|           int32_t expiryToGo = wave->expiryCycle - now; | ||||
|           if (expiryToGo < 0) { | ||||
|               // Done, remove! | ||||
|               if (i == 16) { | ||||
|                 GP16O = 0; | ||||
|               }  | ||||
|               GPOC = mask; | ||||
|               wvfState.waveformEnabled &= ~mask; | ||||
|               continue; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Check for toggles | ||||
|         int32_t cyclesToGo = wave->nextServiceCycle - now; | ||||
|         if (cyclesToGo < 0) { | ||||
|           uint32_t nextEdgeCycles; | ||||
|           uint32_t desired = 0; | ||||
|           uint32_t *timeToUpdate; | ||||
|           wvfState.waveformState ^= mask; | ||||
|           if (wvfState.waveformState & mask) { | ||||
|             if (i == 16) { | ||||
|               GP16O = 1; | ||||
|             } | ||||
|             GPOS = mask; | ||||
|  | ||||
|             if (wvfState.waveformToChange & mask) { | ||||
|               // Copy over next full-cycle timings | ||||
|               wave->timeHighCycles = wvfState.waveformNewHigh; | ||||
|               wave->desiredHighCycles = wvfState.waveformNewHigh; | ||||
|               wave->timeLowCycles = wvfState.waveformNewLow; | ||||
|               wave->desiredLowCycles = wvfState.waveformNewLow; | ||||
|               wave->lastEdge = 0; | ||||
|               wvfState.waveformToChange = 0; | ||||
|             } | ||||
|             if (wave->lastEdge) { | ||||
|               desired = wave->desiredLowCycles; | ||||
|               timeToUpdate = &wave->timeLowCycles; | ||||
|             } | ||||
|             nextEdgeCycles = wave->timeHighCycles; | ||||
|           } else { | ||||
|             if (i == 16) { | ||||
|               GP16O = 0; | ||||
|             } | ||||
|             GPOC = mask; | ||||
|             desired = wave->desiredHighCycles; | ||||
|             timeToUpdate = &wave->timeHighCycles; | ||||
|             nextEdgeCycles = wave->timeLowCycles; | ||||
|           } | ||||
|           if (desired) { | ||||
|             desired = adjust(desired); | ||||
|             int32_t err = desired - (now - wave->lastEdge); | ||||
|             if (abs(err) < desired) { // If we've lost > the entire phase, ignore this error signal | ||||
|                 err /= 2; | ||||
|                 *timeToUpdate += err; | ||||
|             } | ||||
|           } | ||||
|           nextEdgeCycles = adjust(nextEdgeCycles); | ||||
|           wave->nextServiceCycle = now + nextEdgeCycles; | ||||
|           wave->lastEdge = now; | ||||
|         } | ||||
|         nextEventCycle = earliest(nextEventCycle, wave->nextServiceCycle); | ||||
|       } | ||||
|  | ||||
|       // Exit the loop if we've hit the fixed runtime limit or the next event is known to be after that timeout would occur | ||||
|       uint32_t now = GetCycleCountIRQ(); | ||||
|       int32_t cycleDeltaNextEvent = nextEventCycle - now; | ||||
|       int32_t cyclesLeftTimeout = timeoutCycle - now; | ||||
|       done = (cycleDeltaNextEvent > MINIRQTIME) || (cyclesLeftTimeout < 0); | ||||
|     } while (!done); | ||||
|   } // if (wvfState.waveformEnabled) | ||||
|  | ||||
|   if (wvfState.timer1CB) { | ||||
|     nextEventCycle = earliest(nextEventCycle, GetCycleCountIRQ() + wvfState.timer1CB()); | ||||
|   } | ||||
|  | ||||
|   int32_t nextEventCycles = nextEventCycle - GetCycleCountIRQ(); | ||||
|  | ||||
|   if (nextEventCycles < MINIRQTIME) { | ||||
|     nextEventCycles = MINIRQTIME; | ||||
|   } | ||||
|   nextEventCycles -= DELTAIRQ; | ||||
|  | ||||
|   // Do it here instead of global function to save time and because we know it's edge-IRQ | ||||
|   T1L = nextEventCycles >> (turbo ? 1 : 0); | ||||
| } | ||||
|  | ||||
| }; | ||||
							
								
								
									
										1971
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1971
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										17
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								package.json
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "wled", | ||||
|   "version": "0.15.0-b5", | ||||
|   "version": "0.16.0-alpha", | ||||
|   "description": "Tools for WLED project", | ||||
|   "main": "tools/cdata.js", | ||||
|   "directories": { | ||||
| @@ -14,18 +14,21 @@ | ||||
|   }, | ||||
|   "repository": { | ||||
|     "type": "git", | ||||
|     "url": "git+https://github.com/Aircoookie/WLED.git" | ||||
|     "url": "git+https://github.com/wled-dev/WLED.git" | ||||
|   }, | ||||
|   "author": "", | ||||
|   "license": "ISC", | ||||
|   "bugs": { | ||||
|     "url": "https://github.com/Aircoookie/WLED/issues" | ||||
|     "url": "https://github.com/wled-dev/WLED/issues" | ||||
|   }, | ||||
|   "homepage": "https://github.com/Aircoookie/WLED#readme", | ||||
|   "homepage": "https://github.com/wled-dev/WLED#readme", | ||||
|   "dependencies": { | ||||
|     "clean-css": "^5.3.3", | ||||
|     "html-minifier-terser": "^7.2.0", | ||||
|     "inliner": "^1.13.1", | ||||
|     "nodemon": "^3.0.2" | ||||
|     "web-resource-inliner": "^7.0.0", | ||||
|     "nodemon": "^3.1.9" | ||||
|   }, | ||||
|   "engines": { | ||||
|     "node": ">=20.0.0" | ||||
|   } | ||||
| } | ||||
| } | ||||
| @@ -1,3 +1,21 @@ | ||||
| Import('env') | ||||
| Import("env") | ||||
| import shutil | ||||
|  | ||||
| env.Execute("npm run build") | ||||
| node_ex = shutil.which("node") | ||||
| # Check if Node.js is installed and present in PATH if it failed, abort the build | ||||
| if node_ex is None: | ||||
|     print('\x1b[0;31;43m' + 'Node.js is not installed or missing from PATH html css js will not be processed check https://kno.wled.ge/advanced/compiling-wled/' + '\x1b[0m') | ||||
|     exitCode = env.Execute("null") | ||||
|     exit(exitCode) | ||||
| else: | ||||
|     # Install the necessary node packages for the pre-build asset bundling script | ||||
|     print('\x1b[6;33;42m' + 'Installing node packages' + '\x1b[0m') | ||||
|     env.Execute("npm ci") | ||||
|  | ||||
|     # Call the bundling script | ||||
|     exitCode = env.Execute("npm run build") | ||||
|  | ||||
|     # If it failed, abort the build | ||||
|     if (exitCode): | ||||
|       print('\x1b[0;31;43m' + 'npm run build fails check https://kno.wled.ge/advanced/compiling-wled/' + '\x1b[0m') | ||||
|       exit(exitCode) | ||||
|   | ||||
							
								
								
									
										107
									
								
								pio-scripts/load_usermods.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								pio-scripts/load_usermods.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,107 @@ | ||||
| Import('env') | ||||
| from collections import deque | ||||
| from pathlib import Path   # For OS-agnostic path manipulation | ||||
| from click import secho | ||||
| from SCons.Script import Exit | ||||
| from platformio.builder.tools.piolib import LibBuilderBase | ||||
|  | ||||
| usermod_dir = Path(env["PROJECT_DIR"]).resolve() / "usermods" | ||||
|  | ||||
| # Utility functions | ||||
| def find_usermod(mod: str) -> Path: | ||||
|   """Locate this library in the usermods folder. | ||||
|      We do this to avoid needing to rename a bunch of folders; | ||||
|      this could be removed later | ||||
|   """ | ||||
|   # Check name match | ||||
|   mp = usermod_dir / mod | ||||
|   if mp.exists(): | ||||
|     return mp | ||||
|   mp = usermod_dir / f"{mod}_v2" | ||||
|   if mp.exists(): | ||||
|     return mp | ||||
|   mp = usermod_dir / f"usermod_v2_{mod}" | ||||
|   if mp.exists(): | ||||
|     return mp | ||||
|   raise RuntimeError(f"Couldn't locate module {mod} in usermods directory!") | ||||
|  | ||||
| def is_wled_module(dep: LibBuilderBase) -> bool: | ||||
|   """Returns true if the specified library is a wled module | ||||
|   """ | ||||
|   return usermod_dir in Path(dep.src_dir).parents or str(dep.name).startswith("wled-") | ||||
|  | ||||
| ## Script starts here | ||||
| # Process usermod option | ||||
| usermods = env.GetProjectOption("custom_usermods","") | ||||
|  | ||||
| # Handle "all usermods" case | ||||
| if usermods == '*': | ||||
|   usermods = [f.name for f in usermod_dir.iterdir() if f.is_dir() and f.joinpath('library.json').exists()] | ||||
| else: | ||||
|   usermods = usermods.split() | ||||
|  | ||||
| if usermods: | ||||
|   # Inject usermods in to project lib_deps | ||||
|   symlinks = [f"symlink://{find_usermod(mod).resolve()}" for mod in usermods] | ||||
|   env.GetProjectConfig().set("env:" + env['PIOENV'], 'lib_deps', env.GetProjectOption('lib_deps') + symlinks) | ||||
|  | ||||
| # Utility function for assembling usermod include paths | ||||
| def cached_add_includes(dep, dep_cache: set, includes: deque): | ||||
|   """ Add dep's include paths to includes if it's not in the cache """ | ||||
|   if dep not in dep_cache: | ||||
|     dep_cache.add(dep) | ||||
|     for include in dep.get_include_dirs(): | ||||
|       if include not in includes: | ||||
|         includes.appendleft(include) | ||||
|       if usermod_dir not in Path(dep.src_dir).parents: | ||||
|         # Recurse, but only for NON-usermods | ||||
|         for subdep in dep.depbuilders: | ||||
|           cached_add_includes(subdep, dep_cache, includes) | ||||
|  | ||||
| # Monkey-patch ConfigureProjectLibBuilder to mark up the dependencies | ||||
| # Save the old value | ||||
| old_ConfigureProjectLibBuilder = env.ConfigureProjectLibBuilder | ||||
|  | ||||
| # Our new wrapper | ||||
| def wrapped_ConfigureProjectLibBuilder(xenv): | ||||
|   # Call the wrapped function | ||||
|   result = old_ConfigureProjectLibBuilder.clone(xenv)() | ||||
|  | ||||
|   # Fix up include paths | ||||
|   # In PlatformIO >=6.1.17, this could be done prior to ConfigureProjectLibBuilder | ||||
|   wled_dir = xenv["PROJECT_SRC_DIR"] | ||||
|   # Build a list of dependency include dirs | ||||
|   # TODO: Find out if this is the order that PlatformIO/SCons puts them in?? | ||||
|   processed_deps = set() | ||||
|   extra_include_dirs = deque()  # Deque used for fast prepend | ||||
|   for dep in result.depbuilders: | ||||
|      cached_add_includes(dep, processed_deps, extra_include_dirs) | ||||
|  | ||||
|   wled_deps = [dep for dep in result.depbuilders if is_wled_module(dep)] | ||||
|  | ||||
|   broken_usermods = [] | ||||
|   for dep in wled_deps: | ||||
|     # Add the wled folder to the include path | ||||
|     dep.env.PrependUnique(CPPPATH=str(wled_dir)) | ||||
|     # Add WLED's own dependencies | ||||
|     for dir in extra_include_dirs: | ||||
|       dep.env.PrependUnique(CPPPATH=str(dir)) | ||||
|     # Enforce that libArchive is not set; we must link them directly to the executable | ||||
|     if dep.lib_archive: | ||||
|       broken_usermods.append(dep) | ||||
|  | ||||
|   if broken_usermods: | ||||
|     broken_usermods = [usermod.name for usermod in broken_usermods] | ||||
|     secho( | ||||
|       f"ERROR: libArchive=false is missing on usermod(s) {' '.join(broken_usermods)} -- modules will not compile in correctly", | ||||
|       fg="red", | ||||
|       err=True) | ||||
|     Exit(1) | ||||
|  | ||||
|   # Save the depbuilders list for later validation | ||||
|   xenv.Replace(WLED_MODULES=wled_deps) | ||||
|  | ||||
|   return result | ||||
|  | ||||
| # Apply the wrapper | ||||
| env.AddMethod(wrapped_ConfigureProjectLibBuilder, "ConfigureProjectLibBuilder") | ||||
| @@ -19,8 +19,9 @@ def _create_dirs(dirs=["map", "release", "firmware"]): | ||||
|         os.makedirs(os.path.join(OUTPUT_DIR, d), exist_ok=True) | ||||
|  | ||||
| def create_release(source): | ||||
|     release_name = _get_cpp_define_value(env, "WLED_RELEASE_NAME") | ||||
|     if release_name: | ||||
|     release_name_def = _get_cpp_define_value(env, "WLED_RELEASE_NAME") | ||||
|     if release_name_def: | ||||
|         release_name = release_name_def.replace("\\\"", "") | ||||
|         version = _get_cpp_define_value(env, "WLED_VERSION") | ||||
|         release_file = os.path.join(OUTPUT_DIR, "release", f"WLED_{version}_{release_name}.bin") | ||||
|         release_gz_file = release_file + ".gz" | ||||
|   | ||||
							
								
								
									
										80
									
								
								pio-scripts/validate_modules.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								pio-scripts/validate_modules.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,80 @@ | ||||
| import re | ||||
| from pathlib import Path   # For OS-agnostic path manipulation | ||||
| from typing import Iterable | ||||
| from click import secho | ||||
| from SCons.Script import Action, Exit | ||||
| from platformio.builder.tools.piolib import LibBuilderBase | ||||
|  | ||||
|  | ||||
| def is_wled_module(env, dep: LibBuilderBase) -> bool: | ||||
|   """Returns true if the specified library is a wled module | ||||
|   """ | ||||
|   usermod_dir = Path(env["PROJECT_DIR"]).resolve() / "usermods" | ||||
|   return usermod_dir in Path(dep.src_dir).parents or str(dep.name).startswith("wled-") | ||||
|  | ||||
|  | ||||
| def read_lines(p: Path): | ||||
|     """ Read in the contents of a file for analysis """ | ||||
|     with p.open("r", encoding="utf-8", errors="ignore") as f: | ||||
|         return f.readlines() | ||||
|  | ||||
|  | ||||
| def check_map_file_objects(map_file: list[str], dirs: Iterable[str]) -> set[str]: | ||||
|     """ Identify which dirs contributed to the final build | ||||
|  | ||||
|         Returns the (sub)set of dirs that are found in the output ELF | ||||
|     """ | ||||
|     # Pattern to match symbols in object directories | ||||
|     # Join directories into alternation | ||||
|     usermod_dir_regex = "|".join([re.escape(dir) for dir in dirs]) | ||||
|     # Matches nonzero address, any size, and any path in a matching directory | ||||
|     object_path_regex = re.compile(r"0x0*[1-9a-f][0-9a-f]*\s+0x[0-9a-f]+\s+\S+[/\\](" + usermod_dir_regex + r")[/\\]\S+\.o") | ||||
|  | ||||
|     found = set() | ||||
|     for line in map_file: | ||||
|         matches = object_path_regex.findall(line) | ||||
|         for m in matches: | ||||
|             found.add(m) | ||||
|     return found | ||||
|  | ||||
|  | ||||
| def count_usermod_objects(map_file: list[str]) -> int: | ||||
|     """ Returns the number of usermod objects in the usermod list """ | ||||
|     # Count the number of entries in the usermods table section | ||||
|     return len([x for x in map_file if ".dtors.tbl.usermods.1" in x]) | ||||
|  | ||||
|  | ||||
| def validate_map_file(source, target, env): | ||||
|     """ Validate that all modules appear in the output build """ | ||||
|     build_dir = Path(env.subst("$BUILD_DIR")) | ||||
|     map_file_path = build_dir /  env.subst("${PROGNAME}.map") | ||||
|  | ||||
|     if not map_file_path.exists(): | ||||
|         secho(f"ERROR: Map file not found: {map_file_path}", fg="red", err=True) | ||||
|         Exit(1) | ||||
|  | ||||
|     # Identify the WLED module builders, set by load_usermods.py | ||||
|     module_lib_builders = env['WLED_MODULES'] | ||||
|  | ||||
|     # Extract the values we care about | ||||
|     modules = {Path(builder.build_dir).name: builder.name for builder in module_lib_builders} | ||||
|     secho(f"INFO: {len(modules)} libraries linked as WLED optional/user modules") | ||||
|  | ||||
|     # Now parse the map file | ||||
|     map_file_contents = read_lines(map_file_path) | ||||
|     usermod_object_count = count_usermod_objects(map_file_contents) | ||||
|     secho(f"INFO: {usermod_object_count} usermod object entries") | ||||
|  | ||||
|     confirmed_modules = check_map_file_objects(map_file_contents, modules.keys()) | ||||
|     missing_modules = [modname for mdir, modname in modules.items() if mdir not in confirmed_modules] | ||||
|     if missing_modules: | ||||
|         secho( | ||||
|             f"ERROR: No object files from {missing_modules} found in linked output!", | ||||
|             fg="red", | ||||
|             err=True) | ||||
|         Exit(1) | ||||
|     return None | ||||
|  | ||||
| Import("env") | ||||
| env.Append(LINKFLAGS=[env.subst("-Wl,--Map=${BUILD_DIR}/${PROGNAME}.map")]) | ||||
| env.AddPostAction("$BUILD_DIR/${PROGNAME}.elf", Action(validate_map_file, cmdstr='Checking linked optional modules (usermods) in map file')) | ||||
							
								
								
									
										282
									
								
								platformio.ini
									
									
									
									
									
								
							
							
						
						
									
										282
									
								
								platformio.ini
									
									
									
									
									
								
							| @@ -10,7 +10,7 @@ | ||||
| # ------------------------------------------------------------------------------ | ||||
|  | ||||
| # CI/release binaries | ||||
| default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, nodemcuv2_160, esp8266_2m_160, esp01_1m_full_160, nodemcuv2_compat, esp8266_2m_compat, esp01_1m_full_compat, esp32dev, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_16MB_opi, esp32s3dev_8MB_opi, esp32s3_4M_qspi, esp32_wrover | ||||
| default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, nodemcuv2_160, esp8266_2m_160, esp01_1m_full_160, nodemcuv2_compat, esp8266_2m_compat, esp01_1m_full_compat, esp32dev, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_16MB_opi, esp32s3dev_8MB_opi, esp32s3_4M_qspi, esp32_wrover, usermods | ||||
|  | ||||
| src_dir  = ./wled00 | ||||
| data_dir = ./wled00/data | ||||
| @@ -114,7 +114,9 @@ extra_scripts = | ||||
|   post:pio-scripts/output_bins.py | ||||
|   post:pio-scripts/strip-floats.py | ||||
|   pre:pio-scripts/user_config_copy.py | ||||
|   pre:pio-scripts/load_usermods.py | ||||
|   pre:pio-scripts/build_ui.py | ||||
|   post:pio-scripts/validate_modules.py  ;; double-check the build output usermods | ||||
|   ; post:pio-scripts/obj-dump.py  ;; convenience script to create a disassembly dump of the firmware (hardcore debugging) | ||||
|  | ||||
| # ------------------------------------------------------------------------------ | ||||
| @@ -138,9 +140,10 @@ lib_compat_mode = strict | ||||
| lib_deps = | ||||
|     fastled/FastLED @ 3.6.0 | ||||
|     IRremoteESP8266 @ 2.8.2 | ||||
|     makuna/NeoPixelBus @ 2.8.0 | ||||
|     makuna/NeoPixelBus @ 2.8.3 | ||||
|     #https://github.com/makuna/NeoPixelBus.git#CoreShaderBeta | ||||
|     https://github.com/Aircoookie/ESPAsyncWebServer.git @ 2.2.1 | ||||
|     https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.4.2 | ||||
|     marvinroger/AsyncMqttClient @ 0.9.0 | ||||
|   # for I2C interface | ||||
|     ;Wire | ||||
|   # ESP-NOW library | ||||
| @@ -157,25 +160,18 @@ lib_deps = | ||||
|     ;adafruit/Adafruit BMP280 Library @ 2.1.0 | ||||
|     ;adafruit/Adafruit CCS811 Library @ 1.0.4 | ||||
|     ;adafruit/Adafruit Si7021 Library @ 1.4.0 | ||||
|   #For ADS1115 sensor uncomment following | ||||
|     ;adafruit/Adafruit BusIO @ 1.13.2 | ||||
|     ;adafruit/Adafruit ADS1X15 @ 2.4.0 | ||||
|   #For MAX1704x Lipo Monitor / Fuel Gauge uncomment following | ||||
|     ; https://github.com/adafruit/Adafruit_BusIO @ 1.14.5 | ||||
|     ; https://github.com/adafruit/Adafruit_MAX1704X @ 1.0.2 | ||||
|   #For MPU6050 IMU uncomment follwoing | ||||
|     ;electroniccats/MPU6050 @1.0.1 | ||||
|   # For -D USERMOD_ANIMARTRIX | ||||
|   # CC BY-NC 3.0 licensed effects by Stefan Petrick, include this usermod only if you accept the terms! | ||||
|     ;https://github.com/netmindz/animartrix.git#18bf17389e57c69f11bc8d04ebe1d215422c7fb7 | ||||
|   # SHT85 | ||||
|     ;robtillaart/SHT85@~0.3.3 | ||||
|   # Audioreactive usermod | ||||
|     ;kosme/arduinoFFT @ 2.0.1 | ||||
|  | ||||
| extra_scripts = ${scripts_defaults.extra_scripts} | ||||
|  | ||||
| [esp8266] | ||||
| build_unflags = ${common.build_unflags} | ||||
| build_flags = | ||||
|   -DESP8266 | ||||
|   -DFP_IN_IROM | ||||
| @@ -197,6 +193,7 @@ build_flags = | ||||
|   ; decrease code cache size and increase IRAM to fit all pixel functions | ||||
|   -D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48 ;; in case of linker errors like "section `.text1' will not fit in region `iram1_0_seg'" | ||||
|   ; -D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48_SECHEAP_SHARED ;; (experimental) adds some extra heap, but may cause slowdown | ||||
|   -D NON32XFER_HANDLER ;; ask forgiveness for PROGMEM misuse | ||||
|  | ||||
| lib_deps = | ||||
|   #https://github.com/lorol/LITTLEFS.git | ||||
| @@ -234,103 +231,110 @@ lib_deps_compat = | ||||
|   IRremoteESP8266 @ 2.8.2 | ||||
|   makuna/NeoPixelBus @ 2.7.9 | ||||
|   https://github.com/blazoncek/QuickESPNow.git#optional-debug | ||||
|   https://github.com/Aircoookie/ESPAsyncWebServer.git @ 2.2.1 | ||||
|   https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.4.0 | ||||
|  | ||||
| [esp32_all_variants] | ||||
| lib_deps = | ||||
|   esp32async/AsyncTCP @ 3.4.7 | ||||
|   bitbank2/AnimatedGIF@^1.4.7 | ||||
|   https://github.com/Aircoookie/GifDecoder#bc3af18 | ||||
| build_flags = | ||||
|   -D CONFIG_ASYNC_TCP_USE_WDT=0 | ||||
|   -D CONFIG_ASYNC_TCP_STACK_SIZE=8192 | ||||
|   -D WLED_ENABLE_GIF | ||||
|  | ||||
| [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 | ||||
|   ; -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3 | ||||
| platform = ${esp32_idf_V4.platform} | ||||
| platform_packages = | ||||
| build_unflags = ${common.build_unflags} | ||||
| build_flags = ${esp32_idf_V4.build_flags} | ||||
| lib_deps = ${esp32_idf_V4.lib_deps} | ||||
|  | ||||
| tiny_partitions = tools/WLED_ESP32_2MB_noOTA.csv | ||||
| default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv | ||||
| extended_partitions = tools/WLED_ESP32_4MB_700k_FS.csv | ||||
| big_partitions = tools/WLED_ESP32_4MB_256KB_FS.csv     ;; 1.8MB firmware, 256KB filesystem, coredump support | ||||
| large_partitions = tools/WLED_ESP32_8MB.csv | ||||
| extreme_partitions = tools/WLED_ESP32_16MB_9MB_FS.csv | ||||
| lib_deps = | ||||
|   https://github.com/lorol/LITTLEFS.git | ||||
|   https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 | ||||
|   ${env.lib_deps} | ||||
| # additional build flags for audioreactive | ||||
| AR_build_flags = -D USERMOD_AUDIOREACTIVE | ||||
| AR_lib_deps = kosme/arduinoFFT @ 2.0.1 | ||||
|  | ||||
| board_build.partitions = ${esp32.default_partitions}   ;; default partioning for 4MB Flash - can be overridden in build envs | ||||
| # additional build flags for audioreactive - must be applied globally | ||||
| AR_build_flags = ;; -fsingle-precision-constant ;; forces ArduinoFFT to use float math (2x faster) | ||||
| AR_lib_deps =  ;; for pre-usermod-library platformio_override compatibility | ||||
|  | ||||
|  | ||||
| [esp32_idf_V4] | ||||
| ;; experimental build environment for ESP32 using ESP-IDF 4.4.x / arduino-esp32 v2.0.5 | ||||
| ;; very similar to the normal ESP32 flags, but omitting Lorol LittleFS, as littlefs is included in the new framework already. | ||||
| ;; build environment for ESP32 using ESP-IDF 4.4.x / arduino-esp32 v2.0.5 | ||||
| ;; | ||||
| ;; please note that you can NOT update existing ESP32 installs with a "V4" build. Also updating by OTA will not work properly. | ||||
| ;; You need to completely erase your device (esptool erase_flash) first, then install the "V4" build from VSCode+platformio. | ||||
| platform = espressif32@ ~6.3.2 | ||||
| platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0    ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them) | ||||
|  | ||||
| ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them) | ||||
| platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.06.02/platform-espressif32.zip ;; Tasmota Arduino Core 2.0.9 with IPv6 support, based on IDF 4.4.4 | ||||
| build_unflags = ${common.build_unflags} | ||||
| build_flags = -g | ||||
|   -Wshadow=compatible-local ;; emit warning in case a local variable "shadows" another local one | ||||
|   -DARDUINO_ARCH_ESP32 -DESP32 | ||||
|   -D CONFIG_ASYNC_TCP_USE_WDT=0 | ||||
|   -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3 | ||||
|   ${esp32_all_variants.build_flags} | ||||
|   -D WLED_ENABLE_DMX_INPUT | ||||
| lib_deps = | ||||
|   https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 | ||||
|   ${esp32_all_variants.lib_deps} | ||||
|   https://github.com/someweisguy/esp_dmx.git#47db25d | ||||
|   ${env.lib_deps} | ||||
|  | ||||
| [esp32s2] | ||||
| ;; generic definitions for all ESP32-S2 boards | ||||
| platform = espressif32@ ~6.3.2 | ||||
| platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0    ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them) | ||||
| platform = ${esp32_idf_V4.platform} | ||||
| build_unflags = ${common.build_unflags} | ||||
| build_flags = -g | ||||
|   -DARDUINO_ARCH_ESP32 | ||||
|   -DARDUINO_ARCH_ESP32S2 | ||||
|   -DCONFIG_IDF_TARGET_ESP32S2=1 | ||||
|   -D CONFIG_ASYNC_TCP_USE_WDT=0 | ||||
|   -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 | ||||
|   -DCO | ||||
|   -DARDUINO_USB_MODE=0 ;; this flag is mandatory for ESP32-S2 ! | ||||
|   ;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry: | ||||
|   ;; ARDUINO_USB_CDC_ON_BOOT | ||||
|   ${esp32_idf_V4.build_flags} | ||||
| lib_deps = | ||||
|   https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 | ||||
|   ${env.lib_deps} | ||||
|   ${esp32_idf_V4.lib_deps} | ||||
| board_build.partitions = ${esp32.default_partitions}   ;; default partioning for 4MB Flash - can be overridden in build envs | ||||
|  | ||||
| [esp32c3] | ||||
| ;; generic definitions for all ESP32-C3 boards | ||||
| platform = espressif32@ ~6.3.2 | ||||
| platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0    ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them) | ||||
| platform = ${esp32_idf_V4.platform} | ||||
| build_unflags = ${common.build_unflags} | ||||
| build_flags = -g | ||||
|   -DARDUINO_ARCH_ESP32 | ||||
|   -DARDUINO_ARCH_ESP32C3 | ||||
|   -DCONFIG_IDF_TARGET_ESP32C3=1 | ||||
|   -D CONFIG_ASYNC_TCP_USE_WDT=0 | ||||
|   -DCO | ||||
|   -DARDUINO_USB_MODE=1 ;; this flag is mandatory for ESP32-C3 | ||||
|   ;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry: | ||||
|   ;; ARDUINO_USB_CDC_ON_BOOT | ||||
|   ${esp32_idf_V4.build_flags} | ||||
| lib_deps = | ||||
|   https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 | ||||
|   ${env.lib_deps} | ||||
|   ${esp32_idf_V4.lib_deps} | ||||
| board_build.partitions = ${esp32.default_partitions}   ;; default partioning for 4MB Flash - can be overridden in build envs | ||||
| board_build.flash_mode = qio | ||||
|  | ||||
| [esp32s3] | ||||
| ;; generic definitions for all ESP32-S3 boards | ||||
| platform = espressif32@ ~6.3.2 | ||||
| platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0    ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them) | ||||
| platform = ${esp32_idf_V4.platform} | ||||
| build_unflags = ${common.build_unflags} | ||||
| build_flags = -g | ||||
|   -DESP32 | ||||
|   -DARDUINO_ARCH_ESP32 | ||||
|   -DARDUINO_ARCH_ESP32S3 | ||||
|   -DCONFIG_IDF_TARGET_ESP32S3=1 | ||||
|   -D CONFIG_ASYNC_TCP_USE_WDT=0 | ||||
|   -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_DFU_ON_BOOT=0 | ||||
|   -DCO | ||||
|   ;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry: | ||||
|   ;; ARDUINO_USB_MODE, ARDUINO_USB_CDC_ON_BOOT | ||||
|   ${esp32_idf_V4.build_flags} | ||||
| lib_deps = | ||||
|   https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 | ||||
|   ${env.lib_deps} | ||||
|   ${esp32_idf_V4.lib_deps} | ||||
| board_build.partitions = ${esp32.large_partitions}   ;; default partioning for 8MB flash - can be overridden in build envs | ||||
|  | ||||
|  | ||||
| # ------------------------------------------------------------------------------ | ||||
| @@ -343,7 +347,8 @@ 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.build_flags} -D WLED_RELEASE_NAME=ESP8266 #-DWLED_DISABLE_2D | ||||
| build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP8266\" #-DWLED_DISABLE_2D | ||||
|   -D WLED_DISABLE_PARTICLESYSTEM2D | ||||
| lib_deps = ${esp8266.lib_deps} | ||||
| monitor_filters = esp8266_exception_decoder | ||||
|  | ||||
| @@ -352,14 +357,16 @@ extends = env:nodemcuv2 | ||||
| ;; using platform version and build options from WLED 0.14.0 | ||||
| platform = ${esp8266.platform_compat} | ||||
| platform_packages = ${esp8266.platform_packages_compat} | ||||
| build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=ESP8266_compat #-DWLED_DISABLE_2D | ||||
| build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP8266_compat\" #-DWLED_DISABLE_2D | ||||
|   -D WLED_DISABLE_PARTICLESYSTEM2D | ||||
| ;; lib_deps = ${esp8266.lib_deps_compat} ;; experimental - use older NeoPixelBus 2.7.9 | ||||
|  | ||||
| [env:nodemcuv2_160] | ||||
| extends = env:nodemcuv2 | ||||
| board_build.f_cpu = 160000000L | ||||
| build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP8266_160 #-DWLED_DISABLE_2D | ||||
|   -D USERMOD_AUDIOREACTIVE | ||||
| build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP8266_160\" #-DWLED_DISABLE_2D | ||||
|   -D WLED_DISABLE_PARTICLESYSTEM2D | ||||
| custom_usermods = audioreactive | ||||
|  | ||||
| [env:esp8266_2m] | ||||
| board = esp_wroom_02 | ||||
| @@ -367,7 +374,9 @@ 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.build_flags} -D WLED_RELEASE_NAME=ESP02 | ||||
| build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP02\" | ||||
|   -D WLED_DISABLE_PARTICLESYSTEM2D | ||||
|   -D WLED_DISABLE_PARTICLESYSTEM1D | ||||
| lib_deps = ${esp8266.lib_deps} | ||||
|  | ||||
| [env:esp8266_2m_compat] | ||||
| @@ -375,13 +384,17 @@ extends = env:esp8266_2m | ||||
| ;; using platform version and build options from WLED 0.14.0 | ||||
| platform = ${esp8266.platform_compat} | ||||
| platform_packages = ${esp8266.platform_packages_compat} | ||||
| build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=ESP02_compat #-DWLED_DISABLE_2D | ||||
| build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP02_compat\" #-DWLED_DISABLE_2D | ||||
|   -D WLED_DISABLE_PARTICLESYSTEM1D | ||||
|   -D WLED_DISABLE_PARTICLESYSTEM2D | ||||
|  | ||||
| [env:esp8266_2m_160] | ||||
| extends = env:esp8266_2m | ||||
| board_build.f_cpu = 160000000L | ||||
| build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP02_160 | ||||
|   -D USERMOD_AUDIOREACTIVE | ||||
| build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP02_160\" | ||||
|   -D WLED_DISABLE_PARTICLESYSTEM1D | ||||
|   -D WLED_DISABLE_PARTICLESYSTEM2D | ||||
| custom_usermods = audioreactive | ||||
|  | ||||
| [env:esp01_1m_full] | ||||
| board = esp01_1m | ||||
| @@ -389,8 +402,10 @@ 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.build_flags} -D WLED_RELEASE_NAME=ESP01 -D WLED_DISABLE_OTA | ||||
| build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP01\" -D WLED_DISABLE_OTA | ||||
|   ; -D WLED_USE_REAL_MATH ;; may fix wrong sunset/sunrise times, at the cost of 7064 bytes FLASH and 975 bytes RAM | ||||
|   -D WLED_DISABLE_PARTICLESYSTEM1D | ||||
|   -D WLED_DISABLE_PARTICLESYSTEM2D | ||||
| lib_deps = ${esp8266.lib_deps} | ||||
|  | ||||
| [env:esp01_1m_full_compat] | ||||
| @@ -398,91 +413,91 @@ extends = env:esp01_1m_full | ||||
| ;; using platform version and build options from WLED 0.14.0 | ||||
| platform = ${esp8266.platform_compat} | ||||
| platform_packages = ${esp8266.platform_packages_compat} | ||||
| build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=ESP01_compat -D WLED_DISABLE_OTA #-DWLED_DISABLE_2D | ||||
| build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP01_compat\" -D WLED_DISABLE_OTA #-DWLED_DISABLE_2D | ||||
|   -D WLED_DISABLE_PARTICLESYSTEM1D | ||||
|   -D WLED_DISABLE_PARTICLESYSTEM2D | ||||
|  | ||||
| [env:esp01_1m_full_160] | ||||
| extends = env:esp01_1m_full | ||||
| board_build.f_cpu = 160000000L | ||||
| build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP01_160 -D WLED_DISABLE_OTA | ||||
|   -D USERMOD_AUDIOREACTIVE | ||||
| build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP01_160\" -D WLED_DISABLE_OTA | ||||
|   ; -D WLED_USE_REAL_MATH ;; may fix wrong sunset/sunrise times, at the cost of 7064 bytes FLASH and 975 bytes RAM | ||||
|   -D WLED_DISABLE_PARTICLESYSTEM1D | ||||
|   -D WLED_DISABLE_PARTICLESYSTEM2D | ||||
| custom_usermods = audioreactive | ||||
|  | ||||
| [env:esp32dev] | ||||
| board = esp32dev | ||||
| platform = ${esp32.platform} | ||||
| platform_packages = ${esp32.platform_packages} | ||||
| platform = ${esp32_idf_V4.platform} | ||||
| build_unflags = ${common.build_unflags} | ||||
| build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=ESP32 #-D WLED_DISABLE_BROWNOUT_DET | ||||
|   ${esp32.AR_build_flags} | ||||
| lib_deps = ${esp32.lib_deps} | ||||
|   ${esp32.AR_lib_deps} | ||||
| custom_usermods = audioreactive | ||||
| build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_V4\" #-D WLED_DISABLE_BROWNOUT_DET | ||||
|               -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3 | ||||
| lib_deps = ${esp32_idf_V4.lib_deps} | ||||
| monitor_filters = esp32_exception_decoder | ||||
| board_build.partitions = ${esp32.default_partitions} | ||||
| board_build.flash_mode = dio | ||||
|  | ||||
| [env:esp32dev_8M] | ||||
| board = esp32dev | ||||
| platform = ${esp32_idf_V4.platform} | ||||
| platform_packages = ${esp32_idf_V4.platform_packages} | ||||
| custom_usermods = audioreactive | ||||
| build_unflags = ${common.build_unflags} | ||||
| build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=ESP32_8M #-D WLED_DISABLE_BROWNOUT_DET | ||||
|   ${esp32.AR_build_flags} | ||||
| build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_8M\" #-D WLED_DISABLE_BROWNOUT_DET | ||||
| lib_deps = ${esp32_idf_V4.lib_deps} | ||||
|   ${esp32.AR_lib_deps} | ||||
| monitor_filters = esp32_exception_decoder | ||||
| board_build.partitions = ${esp32.large_partitions} | ||||
| board_upload.flash_size = 8MB | ||||
| board_upload.maximum_size = 8388608 | ||||
| ; board_build.f_flash = 80000000L | ||||
| ; board_build.flash_mode = qio | ||||
|  | ||||
| ;[env:esp32dev_audioreactive] | ||||
| ;board = esp32dev | ||||
| ;platform = ${esp32.platform} | ||||
| ;platform_packages = ${esp32.platform_packages} | ||||
| ;build_unflags = ${common.build_unflags} | ||||
| ;build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=ESP32_audioreactive #-D WLED_DISABLE_BROWNOUT_DET | ||||
| ;  ${esp32.AR_build_flags} | ||||
| ;lib_deps = ${esp32.lib_deps} | ||||
| ;  ${esp32.AR_lib_deps} | ||||
| ;monitor_filters = esp32_exception_decoder | ||||
| ;board_build.partitions = ${esp32.default_partitions} | ||||
| ;; board_build.f_flash = 80000000L | ||||
| ;; board_build.flash_mode = dio | ||||
| [env:esp32dev_16M] | ||||
| board = esp32dev | ||||
| platform = ${esp32_idf_V4.platform} | ||||
| custom_usermods = audioreactive | ||||
| build_unflags = ${common.build_unflags} | ||||
| build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_16M\" #-D WLED_DISABLE_BROWNOUT_DET | ||||
| lib_deps = ${esp32_idf_V4.lib_deps} | ||||
| monitor_filters = esp32_exception_decoder | ||||
| board_build.partitions = ${esp32.extreme_partitions} | ||||
| board_upload.flash_size = 16MB | ||||
| board_upload.maximum_size = 16777216 | ||||
| board_build.f_flash = 80000000L | ||||
| board_build.flash_mode = dio | ||||
|  | ||||
| [env:esp32_eth] | ||||
| board = esp32-poe | ||||
| platform = ${esp32.platform} | ||||
| platform_packages = ${esp32.platform_packages} | ||||
| platform = ${esp32_idf_V4.platform} | ||||
| upload_speed = 921600 | ||||
| custom_usermods = audioreactive | ||||
| build_unflags = ${common.build_unflags} | ||||
| build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=ESP32_Ethernet -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1 | ||||
| build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"ESP32_Ethernet\" -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1 | ||||
| ;  -D WLED_DISABLE_ESPNOW ;; ESP-NOW requires wifi, may crash with ethernet only | ||||
|   ${esp32.AR_build_flags} | ||||
| lib_deps = ${esp32.lib_deps} | ||||
|   ${esp32.AR_lib_deps} | ||||
| board_build.partitions = ${esp32.default_partitions} | ||||
| board_build.flash_mode = dio | ||||
|  | ||||
| [env:esp32_wrover] | ||||
| extends = esp32_idf_V4 | ||||
| platform = ${esp32_idf_V4.platform} | ||||
| platform_packages = ${esp32_idf_V4.platform_packages} | ||||
| board = ttgo-t7-v14-mini32 | ||||
| board_build.f_flash = 80000000L | ||||
| board_build.flash_mode = qio | ||||
| board_build.partitions = ${esp32.extended_partitions} | ||||
| custom_usermods = audioreactive | ||||
| build_unflags = ${common.build_unflags} | ||||
| build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=ESP32_WROVER | ||||
| build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_WROVER\" | ||||
|   -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue ;; Older ESP32 (rev.<3) need a PSRAM fix (increases static RAM used) https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/external-ram.html | ||||
|   -D LEDPIN=25 | ||||
|   ${esp32.AR_build_flags} | ||||
|   -D DATA_PINS=25 | ||||
| lib_deps = ${esp32_idf_V4.lib_deps} | ||||
|   ${esp32.AR_lib_deps} | ||||
|  | ||||
|    | ||||
| [env:esp32c3dev] | ||||
| extends = esp32c3 | ||||
| platform = ${esp32c3.platform} | ||||
| platform_packages = ${esp32c3.platform_packages} | ||||
| framework = arduino | ||||
| board = esp32-c3-devkitm-1 | ||||
| board_build.partitions = ${esp32.default_partitions} | ||||
| build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLED_RELEASE_NAME=ESP32-C3 | ||||
| build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-C3\" | ||||
|   -D WLED_WATCHDOG_TIMEOUT=0 | ||||
|   -DLOLIN_WIFI_FIX ; seems to work much better with this | ||||
|   -DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB | ||||
| @@ -496,18 +511,18 @@ lib_deps = ${esp32c3.lib_deps} | ||||
| board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support | ||||
| board_build.arduino.memory_type = qio_opi     ;; use with PSRAM: 8MB or 16MB | ||||
| platform = ${esp32s3.platform} | ||||
| platform_packages = ${esp32s3.platform_packages} | ||||
| upload_speed = 921600 | ||||
| custom_usermods = audioreactive | ||||
| build_unflags = ${common.build_unflags} | ||||
| build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_16MB_opi | ||||
| build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_16MB_opi\" | ||||
|   -D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0 | ||||
|   ;-D ARDUINO_USB_CDC_ON_BOOT=0  ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip | ||||
|   -D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1      ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB") | ||||
|   -DBOARD_HAS_PSRAM | ||||
|   ${esp32.AR_build_flags} | ||||
| lib_deps = ${esp32s3.lib_deps} | ||||
|   ${esp32.AR_lib_deps} | ||||
| board_build.partitions = ${esp32.extreme_partitions} | ||||
| board_upload.flash_size = 16MB | ||||
| board_upload.maximum_size = 16777216 | ||||
| board_build.f_flash = 80000000L | ||||
| board_build.flash_mode = qio | ||||
| monitor_filters = esp32_exception_decoder | ||||
| @@ -517,37 +532,58 @@ monitor_filters = esp32_exception_decoder | ||||
| board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support | ||||
| board_build.arduino.memory_type = qio_opi     ;; use with PSRAM: 8MB or 16MB | ||||
| platform = ${esp32s3.platform} | ||||
| platform_packages = ${esp32s3.platform_packages} | ||||
| upload_speed = 921600 | ||||
| custom_usermods = audioreactive | ||||
| build_unflags = ${common.build_unflags} | ||||
| build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_8MB_opi | ||||
| build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_8MB_opi\" | ||||
|   -D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0 | ||||
|   ;-D ARDUINO_USB_CDC_ON_BOOT=0  ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip | ||||
|   -D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1      ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB") | ||||
|   -DBOARD_HAS_PSRAM | ||||
|   ${esp32.AR_build_flags} | ||||
| lib_deps = ${esp32s3.lib_deps} | ||||
|   ${esp32.AR_lib_deps} | ||||
| board_build.partitions = ${esp32.large_partitions} | ||||
| board_build.f_flash = 80000000L | ||||
| board_build.flash_mode = qio | ||||
| monitor_filters = esp32_exception_decoder | ||||
|  | ||||
| [env:esp32S3_wroom2] | ||||
| ;; For ESP32-S3 WROOM-2, a.k.a. ESP32-S3 DevKitC-1 v1.1 | ||||
| ;; with >= 16MB FLASH and >= 8MB PSRAM (memory_type: opi_opi) | ||||
| platform = ${esp32s3.platform} | ||||
| board = esp32s3camlcd ;; this is the only standard board with "opi_opi" | ||||
| board_build.arduino.memory_type = opi_opi | ||||
| upload_speed = 921600 | ||||
| custom_usermods = audioreactive | ||||
| build_unflags = ${common.build_unflags} | ||||
| build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_WROOM-2\" | ||||
|   -D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0 | ||||
|   -D ARDUINO_USB_CDC_ON_BOOT=0  ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip | ||||
|   ;; -D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1      ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB") | ||||
|   -DBOARD_HAS_PSRAM | ||||
|   -D LEDPIN=38 -D DATA_PINS=38 ;; buildin WS2812b LED | ||||
|   -D BTNPIN=0 -D RLYPIN=16 -D IRPIN=17 -D AUDIOPIN=-1 | ||||
|   -D WLED_DEBUG | ||||
|   -D SR_DMTYPE=1 -D I2S_SDPIN=13 -D I2S_CKPIN=14 -D I2S_WSPIN=15 -D MCLK_PIN=4  ;; I2S mic | ||||
| lib_deps = ${esp32s3.lib_deps} | ||||
|  | ||||
| board_build.partitions = ${esp32.extreme_partitions} | ||||
| board_upload.flash_size = 16MB | ||||
| board_upload.maximum_size = 16777216 | ||||
| monitor_filters = esp32_exception_decoder | ||||
|  | ||||
| [env:esp32s3_4M_qspi] | ||||
| ;; ESP32-S3, with 4MB FLASH and <= 4MB PSRAM (memory_type: qio_qspi) | ||||
| board = lolin_s3_mini ;; -S3 mini, 4MB flash 2MB PSRAM  | ||||
| platform = ${esp32s3.platform} | ||||
| platform_packages = ${esp32s3.platform_packages} | ||||
| upload_speed = 921600 | ||||
| custom_usermods = audioreactive | ||||
| build_unflags = ${common.build_unflags} | ||||
| build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_4M_qspi | ||||
| build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_4M_qspi\" | ||||
|   -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MODE=1      ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB") | ||||
|   -DBOARD_HAS_PSRAM | ||||
|   -DLOLIN_WIFI_FIX ; seems to work much better with this | ||||
|   -D WLED_WATCHDOG_TIMEOUT=0 | ||||
|   ${esp32.AR_build_flags} | ||||
| lib_deps = ${esp32s3.lib_deps} | ||||
|   ${esp32.AR_lib_deps} | ||||
| board_build.partitions = ${esp32.default_partitions} | ||||
| board_build.f_flash = 80000000L | ||||
| board_build.flash_mode = qio | ||||
| @@ -555,27 +591,37 @@ monitor_filters = esp32_exception_decoder | ||||
|  | ||||
| [env:lolin_s2_mini] | ||||
| platform = ${esp32s2.platform} | ||||
| platform_packages = ${esp32s2.platform_packages} | ||||
| board = lolin_s2_mini | ||||
| board_build.partitions = ${esp32.default_partitions} | ||||
| board_build.flash_mode = qio | ||||
| board_build.f_flash = 80000000L | ||||
| custom_usermods = audioreactive | ||||
| build_unflags = ${common.build_unflags} | ||||
| build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=ESP32-S2 | ||||
| build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S2\" | ||||
|   -DARDUINO_USB_CDC_ON_BOOT=1 | ||||
|   -DARDUINO_USB_MSC_ON_BOOT=0 | ||||
|   -DARDUINO_USB_DFU_ON_BOOT=0 | ||||
|   -DBOARD_HAS_PSRAM | ||||
|   -DLOLIN_WIFI_FIX ; seems to work much better with this | ||||
|   -D WLED_WATCHDOG_TIMEOUT=0 | ||||
|   -D CONFIG_ASYNC_TCP_USE_WDT=0 | ||||
|   -D LEDPIN=16 | ||||
|   -D DATA_PINS=16 | ||||
|   -D HW_PIN_SCL=35 | ||||
|   -D HW_PIN_SDA=33 | ||||
|   -D HW_PIN_CLOCKSPI=7 | ||||
|   -D HW_PIN_DATASPI=11 | ||||
|   -D HW_PIN_MISOSPI=9 | ||||
| ;  -D STATUSLED=15 | ||||
|   ${esp32.AR_build_flags} | ||||
| lib_deps = ${esp32s2.lib_deps} | ||||
|   ${esp32.AR_lib_deps} | ||||
|  | ||||
|  | ||||
| [env:usermods] | ||||
| board = esp32dev | ||||
| platform = ${esp32_idf_V4.platform} | ||||
| build_unflags = ${common.build_unflags} | ||||
| build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_USERMODS\" | ||||
|   -DTOUCH_CS=9 | ||||
| lib_deps = ${esp32_idf_V4.lib_deps} | ||||
| monitor_filters = esp32_exception_decoder | ||||
| board_build.flash_mode = dio | ||||
| custom_usermods = *   ; Expands to all usermods in usermods folder | ||||
| board_build.partitions = ${esp32.extreme_partitions}  ; We're gonna need a bigger boat | ||||
|   | ||||
| @@ -5,7 +5,7 @@ | ||||
| # Please visit documentation: https://docs.platformio.org/page/projectconf.html | ||||
|  | ||||
| [platformio] | ||||
| default_envs = WLED_tasmota_1M  # define as many as you need | ||||
| default_envs = WLED_generic8266_1M, esp32dev_V4_dio80  # put the name(s) of your own build environment here. You can define as many as you need | ||||
|  | ||||
| #---------- | ||||
| # SAMPLE | ||||
| @@ -28,16 +28,15 @@ lib_deps = ${esp8266.lib_deps} | ||||
| ;  robtillaart/SHT85@~0.3.3 | ||||
| ;  ;gmag11/QuickESPNow @ ~0.7.0 # will also load QuickDebug | ||||
| ;  https://github.com/blazoncek/QuickESPNow.git#optional-debug  ;; exludes debug library | ||||
| ;  ${esp32.AR_lib_deps} ;; used for USERMOD_AUDIOREACTIVE | ||||
| ;  bitbank2/PNGdec@^1.0.1 ;; used for POV display uncomment following | ||||
| ;  ${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE | ||||
|  | ||||
| build_unflags = ${common.build_unflags} | ||||
| build_flags = ${common.build_flags} ${esp8266.build_flags} | ||||
| ; | ||||
| ; *** To use the below defines/overrides, copy and paste each onto it's own line just below build_flags in the section above. | ||||
| ; *** To use the below defines/overrides, copy and paste each onto its own line just below build_flags in the section above. | ||||
| ;  | ||||
| ; Set a release name that may be used to distinguish required binary for flashing | ||||
| ;   -D WLED_RELEASE_NAME=ESP32_MULTI_USREMODS | ||||
| ;   -D WLED_RELEASE_NAME=\"ESP32_MULTI_USREMODS\" | ||||
| ; | ||||
| ; disable specific features | ||||
| ;   -D WLED_DISABLE_OTA | ||||
| @@ -59,7 +58,7 @@ build_flags = ${common.build_flags} ${esp8266.build_flags} | ||||
| ;   -D WLED_ENABLE_DMX | ||||
| ; | ||||
| ; PIN defines - uncomment and change, if needed: | ||||
| ;   -D LEDPIN=2 | ||||
| ;   -D DATA_PINS=2 | ||||
| ; or use this for multiple outputs | ||||
| ;   -D DATA_PINS=1,3 | ||||
| ;   -D BTNPIN=0 | ||||
| @@ -111,7 +110,6 @@ build_flags = ${common.build_flags} ${esp8266.build_flags} | ||||
| ; | ||||
| ; Use 4 Line Display usermod with SPI display | ||||
| ;   -D USERMOD_FOUR_LINE_DISPLAY | ||||
| ;   -D USE_ALT_DISPlAY # mandatory | ||||
| ;   -DFLD_SPI_DEFAULT | ||||
| ;   -D FLD_TYPE=SSD1306_SPI64 | ||||
| ;   -D FLD_PIN_CLOCKSPI=14 | ||||
| @@ -142,7 +140,8 @@ build_flags = ${common.build_flags} ${esp8266.build_flags} | ||||
| ;   -D PIR_SENSOR_MAX_SENSORS=2 # max allowable sensors (uses OR logic for triggering) | ||||
| ; | ||||
| ; Use Audioreactive usermod and configure I2S microphone | ||||
| ;   -D USERMOD_AUDIOREACTIVE | ||||
| ;   ${esp32.AR_build_flags} ;; default flags required to properly configure ArduinoFFT | ||||
| ;   ;; don't forget to add ArduinoFFT to your libs_deps: ${esp32.AR_lib_deps} | ||||
| ;   -D AUDIOPIN=-1 | ||||
| ;   -D DMTYPE=1     # 0-analog/disabled, 1-I2S generic, 2-ES7243, 3-SPH0645, 4-I2S+mclk, 5-I2S PDM | ||||
| ;   -D I2S_SDPIN=36 | ||||
| @@ -158,17 +157,22 @@ build_flags = ${common.build_flags} ${esp8266.build_flags} | ||||
| ;   -D USERMOD_POV_DISPLAY | ||||
| ; Use built-in or custom LED as a status indicator (assumes LED is connected to GPIO16) | ||||
| ;   -D STATUSLED=16 | ||||
| ;    | ||||
| ; | ||||
| ; set the name of the module - make sure there is a quote-backslash-quote before the name and a backslash-quote-quote after the name | ||||
| ;   -D SERVERNAME="\"WLED\"" | ||||
| ;    | ||||
| ; | ||||
| ; set the number of LEDs | ||||
| ;   -D DEFAULT_LED_COUNT=30 | ||||
| ;   -D PIXEL_COUNTS=30 | ||||
| ; or this for multiple outputs | ||||
| ;   -D PIXEL_COUNTS=30,30 | ||||
| ; | ||||
| ; set the default LED type | ||||
| ;   -D DEFAULT_LED_TYPE=22    # see const.h (TYPE_xxxx) | ||||
| ;   -D LED_TYPES=22    # see const.h (TYPE_xxxx) | ||||
| ; or this for multiple outputs | ||||
| ;   -D LED_TYPES=TYPE_SK6812_RGBW,TYPE_WS2812_RGB | ||||
| ; | ||||
| ; set default color order of your led strip | ||||
| ;   -D DEFAULT_LED_COLOR_ORDER=COL_ORDER_GRB | ||||
| ; | ||||
| ; set milliampere limit when using ESP power pin (or inadequate PSU) to power LEDs | ||||
| ;   -D ABL_MILLIAMPS_DEFAULT=850 | ||||
| @@ -177,9 +181,6 @@ build_flags = ${common.build_flags} ${esp8266.build_flags} | ||||
| ; 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 | ||||
| ;    | ||||
| ; set default color order of your led strip | ||||
| ;   -D DEFAULT_LED_COLOR_ORDER=COL_ORDER_GRB | ||||
| ; | ||||
| ; use PSRAM on classic ESP32 rev.1 (rev.3 or above has no issues) | ||||
| ;   -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue   # needed only for classic ESP32 rev.1 | ||||
| ; | ||||
| @@ -233,18 +234,17 @@ 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.build_flags} -D LEDPIN=1 -D WLED_DISABLE_INFRARED | ||||
| build_flags = ${common.build_flags} ${esp8266.build_flags} -D DATA_PINS=1 -D WLED_DISABLE_INFRARED | ||||
| lib_deps = ${esp8266.lib_deps} | ||||
|  | ||||
| [env:esp32dev_qio80] | ||||
| extends = env:esp32dev  # we want to extend the existing esp32dev environment (and define only updated options) | ||||
| board = esp32dev | ||||
| platform = ${esp32.platform} | ||||
| platform_packages = ${esp32.platform_packages} | ||||
| build_unflags = ${common.build_unflags} | ||||
| build_flags = ${common.build_flags} ${esp32.build_flags} #-D WLED_DISABLE_BROWNOUT_DET | ||||
|   ${esp32.AR_build_flags} ;; optional - includes USERMOD_AUDIOREACTIVE | ||||
| lib_deps = ${esp32.lib_deps} | ||||
|   ${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE | ||||
| monitor_filters = esp32_exception_decoder | ||||
| board_build.partitions = ${esp32.default_partitions} | ||||
| board_build.f_flash = 80000000L | ||||
| board_build.flash_mode = qio | ||||
|  | ||||
| @@ -252,26 +252,25 @@ board_build.flash_mode = qio | ||||
| ;; experimental ESP32 env using ESP-IDF V4.4.x | ||||
| ;; Warning: this build environment is not stable!! | ||||
| ;; please erase your device before installing. | ||||
| extends = esp32_idf_V4  # based on newer "esp-idf V4" platform environment | ||||
| board = esp32dev | ||||
| platform = ${esp32_idf_V4.platform} | ||||
| platform_packages = ${esp32_idf_V4.platform_packages} | ||||
| build_unflags = ${common.build_unflags} | ||||
| build_flags = ${common.build_flags}  ${esp32_idf_V4.build_flags} #-D WLED_DISABLE_BROWNOUT_DET | ||||
|   ${esp32.AR_build_flags} ;; includes USERMOD_AUDIOREACTIVE | ||||
| lib_deps = ${esp32_idf_V4.lib_deps} | ||||
|   ${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE | ||||
| monitor_filters = esp32_exception_decoder | ||||
| board_build.partitions = ${esp32_idf_V4.default_partitions} | ||||
| board_build.partitions = ${esp32.default_partitions}  ;; if you get errors about "out of program space", change this to ${esp32.extended_partitions} or even ${esp32.big_partitions} | ||||
| board_build.f_flash = 80000000L | ||||
| board_build.flash_mode = dio | ||||
|  | ||||
| [env:esp32s2_saola] | ||||
| extends = esp32s2 | ||||
| board = esp32-s2-saola-1 | ||||
| platform = ${esp32s2.platform} | ||||
| platform_packages = ${esp32s2.platform_packages} | ||||
| framework = arduino | ||||
| board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv | ||||
| board_build.flash_mode = qio | ||||
| upload_speed = 460800 | ||||
| build_unflags = ${common.build_unflags} | ||||
| build_flags = ${common.build_flags} ${esp32s2.build_flags} | ||||
|   ;-DLOLIN_WIFI_FIX ;; try this in case Wifi does not work | ||||
|   -DARDUINO_USB_CDC_ON_BOOT=1 | ||||
| @@ -280,7 +279,7 @@ lib_deps = ${esp32s2.lib_deps} | ||||
| [env:esp32s3dev_8MB_PSRAM_qspi] | ||||
| ;; ESP32-TinyS3 development board, with 8MB FLASH and PSRAM (memory_type: qio_qspi) | ||||
| extends = env:esp32s3dev_8MB_PSRAM_opi | ||||
| ;board = um_tinys3 ;    -> needs workaround from https://github.com/Aircoookie/WLED/pull/2905#issuecomment-1328049860 | ||||
| ;board = um_tinys3 ;    -> needs workaround from https://github.com/wled-dev/WLED/pull/2905#issuecomment-1328049860 | ||||
| board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support | ||||
| board_build.arduino.memory_type = qio_qspi ;; use with PSRAM: 2MB or  4MB | ||||
|  | ||||
| @@ -308,7 +307,7 @@ 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.build_flags} -D WLED_USE_SHOJO_PCB | ||||
| build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_USE_SHOJO_PCB ;; NB: WLED_USE_SHOJO_PCB is not used anywhere in the source code. Not sure why its needed. | ||||
| lib_deps = ${esp8266.lib_deps} | ||||
|  | ||||
| [env:d1_mini_debug] | ||||
| @@ -339,7 +338,7 @@ 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.build_flags} -D LEDPIN=12 -D IRPIN=-1 -D RLYPIN=2 | ||||
| build_flags = ${common.build_flags} ${esp8266.build_flags} -D DATA_PINS=12 -D IRPIN=-1 -D RLYPIN=2 | ||||
| lib_deps = ${esp8266.lib_deps} | ||||
|  | ||||
| [env:esp32c3dev_2MB] | ||||
| @@ -359,51 +358,65 @@ upload_speed = 115200 | ||||
| lib_deps = ${esp32c3.lib_deps} | ||||
| board_build.partitions = tools/WLED_ESP32_2MB_noOTA.csv | ||||
| board_build.flash_mode = dio | ||||
| board_upload.flash_size = 2MB | ||||
| board_upload.maximum_size = 2097152 | ||||
|  | ||||
| [env:wemos_shield_esp32] | ||||
| extends = esp32              ;; use default esp32 platform | ||||
| board = esp32dev | ||||
| platform = ${esp32.platform} | ||||
| platform_packages = ${esp32.platform_packages} | ||||
| upload_speed = 460800 | ||||
| build_unflags = ${common.build_unflags} | ||||
| build_flags = ${common.build_flags} ${esp32.build_flags} | ||||
|   -D LEDPIN=16 | ||||
|   -D WLED_RELEASE_NAME=\"ESP32_wemos_shield\" | ||||
|   -D DATA_PINS=16 | ||||
|   -D RLYPIN=19 | ||||
|   -D BTNPIN=17 | ||||
|   -D IRPIN=18 | ||||
|   -D UWLED_USE_MY_CONFIG | ||||
|   -UWLED_USE_MY_CONFIG | ||||
|   -D USERMOD_DALLASTEMPERATURE | ||||
|   -D USERMOD_FOUR_LINE_DISPLAY | ||||
|   -D TEMPERATURE_PIN=23 | ||||
|   -D USE_ALT_DISPlAY ; new versions of USERMOD_FOUR_LINE_DISPLAY and USERMOD_ROTARY_ENCODER_UI | ||||
|   -D USERMOD_AUDIOREACTIVE | ||||
|   ${esp32.AR_build_flags} ;; includes USERMOD_AUDIOREACTIVE | ||||
| lib_deps = ${esp32.lib_deps} | ||||
|   OneWire@~2.3.5 | ||||
|   olikraus/U8g2 @ ^2.28.8 | ||||
|   https://github.com/blazoncek/arduinoFFT.git | ||||
|   OneWire@~2.3.5          ;; needed for USERMOD_DALLASTEMPERATURE | ||||
|   olikraus/U8g2 @ ^2.28.8 ;; needed for USERMOD_FOUR_LINE_DISPLAY | ||||
|   ${esp32.AR_lib_deps}    ;; needed for USERMOD_AUDIOREACTIVE | ||||
| board_build.partitions = ${esp32.default_partitions} | ||||
|  | ||||
| [env:m5atom] | ||||
| board = esp32dev | ||||
| build_unflags = ${common.build_unflags} | ||||
| build_flags = ${common.build_flags} ${esp32.build_flags} -D LEDPIN=27 -D BTNPIN=39 | ||||
| [env:esp32_pico-D4] | ||||
| extends = esp32              ;; use default esp32 platform | ||||
| board = pico32               ;; pico32-D4 is different from the standard esp32dev | ||||
|                              ;; hardware details from https://github.com/srg74/WLED-ESP32-pico | ||||
| build_flags = ${common.build_flags} ${esp32.build_flags} | ||||
|   -D WLED_RELEASE_NAME=\"pico32-D4\" -D SERVERNAME='"WLED-pico32"' | ||||
|   -D WLED_DISABLE_ADALIGHT   ;; no serial-to-USB chip on this board - better to disable serial protocols | ||||
|   -D DATA_PINS=2,18          ;; LED pins | ||||
|   -D RLYPIN=19 -D BTNPIN=0 -D IRPIN=-1 ;; no default pin for IR | ||||
|   ${esp32.AR_build_flags}    ;; include USERMOD_AUDIOREACTIVE | ||||
|   -D UM_AUDIOREACTIVE_ENABLE ;; enable AR by default | ||||
|   ;; Audioreactive settings for on-board microphone (ICS-43432) | ||||
|   -D SR_DMTYPE=1 -D I2S_SDPIN=25 -D I2S_WSPIN=15 -D I2S_CKPIN=14 | ||||
|   -D SR_SQUELCH=5 -D SR_GAIN=30 | ||||
| lib_deps = ${esp32.lib_deps} | ||||
| platform = ${esp32.platform} | ||||
| platform_packages = ${esp32.platform_packages} | ||||
|   ${esp32.AR_lib_deps}       ;; needed for USERMOD_AUDIOREACTIVE | ||||
| board_build.partitions = ${esp32.default_partitions} | ||||
| board_build.f_flash = 80000000L | ||||
|  | ||||
| [env:m5atom] | ||||
| extends = env:esp32dev  # we want to extend the existing esp32dev environment (and define only updated options) | ||||
| build_flags = ${common.build_flags} ${esp32.build_flags} -D DATA_PINS=27 -D BTNPIN=39 | ||||
|  | ||||
| [env:sp501e] | ||||
| board = esp_wroom_02 | ||||
| platform = ${common.platform_wled_default} | ||||
| board_build.ldscript = ${common.ldscript_2m512k} | ||||
| build_flags = ${common.build_flags} ${esp8266.build_flags} -D LEDPIN=3 -D BTNPIN=1 | ||||
| build_flags = ${common.build_flags} ${esp8266.build_flags} -D DATA_PINS=3 -D BTNPIN=1 | ||||
| lib_deps = ${esp8266.lib_deps} | ||||
|  | ||||
| [env:sp511e] | ||||
| board = esp_wroom_02 | ||||
| platform = ${common.platform_wled_default} | ||||
| board_build.ldscript = ${common.ldscript_2m512k} | ||||
| build_flags = ${common.build_flags} ${esp8266.build_flags} -D LEDPIN=3 -D BTNPIN=2 -D IRPIN=5 -D WLED_MAX_BUTTONS=3 | ||||
| build_flags = ${common.build_flags} ${esp8266.build_flags} -D DATA_PINS=3 -D BTNPIN=2 -D IRPIN=5 -D WLED_MAX_BUTTONS=3 | ||||
| lib_deps = ${esp8266.lib_deps} | ||||
|  | ||||
| [env:Athom_RGBCW]        ;7w and 5w(GU10) bulbs | ||||
| @@ -413,7 +426,7 @@ platform_packages = ${common.platform_packages} | ||||
| board_build.ldscript = ${common.ldscript_2m512k} | ||||
| build_unflags = ${common.build_unflags} | ||||
| build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,13,5 | ||||
|                                             -D DEFAULT_LED_TYPE=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0 | ||||
|                                             -D LED_TYPES=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0 | ||||
| lib_deps = ${esp8266.lib_deps} | ||||
|  | ||||
| [env:Athom_15w_RGBCW]        ;15w bulb | ||||
| @@ -423,7 +436,7 @@ platform_packages = ${common.platform_packages} | ||||
| board_build.ldscript = ${common.ldscript_2m512k} | ||||
| build_unflags = ${common.build_unflags} | ||||
| build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,5,13 | ||||
|                                             -D DEFAULT_LED_TYPE=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0 -D WLED_USE_IC_CCT | ||||
|                                             -D LED_TYPES=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0 -D WLED_USE_IC_CCT | ||||
| lib_deps = ${esp8266.lib_deps} | ||||
|  | ||||
| [env:Athom_3Pin_Controller]        ;small controller with only data | ||||
| @@ -432,7 +445,7 @@ 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.build_flags} -D BTNPIN=0 -D RLYPIN=-1 -D LEDPIN=1 -D WLED_DISABLE_INFRARED | ||||
| build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=0 -D RLYPIN=-1 -D DATA_PINS=1 -D WLED_DISABLE_INFRARED | ||||
| lib_deps = ${esp8266.lib_deps} | ||||
|  | ||||
| [env:Athom_4Pin_Controller]       ; With clock and data interface | ||||
| @@ -441,7 +454,7 @@ 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.build_flags} -D BTNPIN=0 -D RLYPIN=12 -D LEDPIN=1 -D WLED_DISABLE_INFRARED | ||||
| build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=0 -D RLYPIN=12 -D DATA_PINS=1 -D WLED_DISABLE_INFRARED | ||||
| lib_deps = ${esp8266.lib_deps} | ||||
|  | ||||
| [env:Athom_5Pin_Controller]      ;Analog light strip controller | ||||
| @@ -489,17 +502,15 @@ lib_deps = ${esp8266.lib_deps} | ||||
| # EleksTube-IPS | ||||
| # ------------------------------------------------------------------------------ | ||||
| [env:elekstube_ips] | ||||
| extends = esp32              ;; use default esp32 platform | ||||
| board = esp32dev | ||||
| platform = ${esp32.platform} | ||||
| platform_packages = ${esp32.platform_packages} | ||||
| upload_speed = 921600 | ||||
| custom_usermods = ${env:esp32dev.custom_usermods} RTC EleksTube_IPS | ||||
| build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_DISABLE_BROWNOUT_DET -D WLED_DISABLE_INFRARED | ||||
|   -D USERMOD_RTC | ||||
|   -D USERMOD_ELEKSTUBE_IPS | ||||
|   -D LEDPIN=12 | ||||
|   -D DATA_PINS=12 | ||||
|   -D RLYPIN=27 | ||||
|   -D BTNPIN=34 | ||||
|   -D DEFAULT_LED_COUNT=6 | ||||
|   -D PIXEL_COUNTS=6 | ||||
|   # Display config | ||||
|   -D ST7789_DRIVER | ||||
|   -D TFT_WIDTH=135 | ||||
| @@ -513,7 +524,14 @@ build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_DISABLE_BROWNOU | ||||
|   -D SPI_FREQUENCY=40000000 | ||||
|   -D USER_SETUP_LOADED | ||||
| monitor_filters = esp32_exception_decoder | ||||
| lib_deps = | ||||
|   ${esp32.lib_deps} | ||||
|   TFT_eSPI @ ^2.3.70 | ||||
| board_build.partitions = ${esp32.default_partitions} | ||||
|  | ||||
| # ------------------------------------------------------------------------------ | ||||
| # Usermod examples | ||||
| # ------------------------------------------------------------------------------ | ||||
|  | ||||
| # 433MHz RF remote example for esp32dev | ||||
| [env:esp32dev_usermod_RF433] | ||||
| extends = env:esp32dev | ||||
| build_flags = ${env:esp32dev.build_flags} -D USERMOD_RF433 | ||||
| lib_deps = ${env:esp32dev.lib_deps} | ||||
|   sui77/rc-switch @ 2.6.4 | ||||
|   | ||||
							
								
								
									
										21
									
								
								readme.md
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								readme.md
									
									
									
									
									
								
							| @@ -1,18 +1,20 @@ | ||||
| <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://github.com/wled-dev/WLED/releases"><img src="https://img.shields.io/github/release/wled-dev/WLED.svg?style=flat-square"></a> | ||||
|   <a href="https://raw.githubusercontent.com/wled-dev/WLED/main/LICENSE"><img src="https://img.shields.io/github/license/wled-dev/wled?color=blue&style=flat-square"></a> | ||||
|   <a href="https://wled.discourse.group"><img src="https://img.shields.io/discourse/topics?colorB=blue&label=forum&server=https%3A%2F%2Fwled.discourse.group%2F&style=flat-square"></a> | ||||
|   <a href="https://discord.gg/QAh7wJHrRM"><img src="https://img.shields.io/discord/473448917040758787.svg?colorB=blue&label=discord&style=flat-square"></a> | ||||
|   <a href="https://kno.wled.ge"><img src="https://img.shields.io/badge/quick_start-wiki-blue.svg?style=flat-square"></a> | ||||
|   <a href="https://github.com/Aircoookie/WLED-App"><img src="https://img.shields.io/badge/app-wled-blue.svg?style=flat-square"></a> | ||||
|   <a href="https://gitpod.io/#https://github.com/Aircoookie/WLED"><img src="https://img.shields.io/badge/Gitpod-ready--to--code-blue?style=flat-square&logo=gitpod"></a> | ||||
|   <a href="https://gitpod.io/#https://github.com/wled-dev/WLED"><img src="https://img.shields.io/badge/Gitpod-ready--to--code-blue?style=flat-square&logo=gitpod"></a> | ||||
|  | ||||
|   </p> | ||||
|  | ||||
| # Welcome to my project WLED! ✨ | ||||
| # Welcome to WLED! ✨ | ||||
|  | ||||
| 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! | ||||
| A fast and feature-rich implementation of an ESP32 and ESP8266 webserver to control NeoPixel (WS2812B, WS2811, SK6812) LEDs or also SPI based chipsets like the WS2801 and APA102! | ||||
|  | ||||
| Originally created by [Aircoookie](https://github.com/Aircoookie) | ||||
|  | ||||
| ## ⚙️ Features | ||||
| - WS2812FX library with more than 100 special effects   | ||||
| @@ -21,7 +23,7 @@ A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control | ||||
| - Segments to set different effects and colors to user defined parts of the LED string   | ||||
| - Settings page - configuration via the network   | ||||
| - Access Point and station mode - automatic failsafe AP   | ||||
| - Up to 10 LED outputs per instance | ||||
| - [Up to 10 LED outputs](https://kno.wled.ge/features/multi-strip/#esp32) per instance | ||||
| - Support for RGBW strips   | ||||
| - Up to 250 user presets to save and load colors/effects easily, supports cycling through them.   | ||||
| - Presets can be used to automatically execute API calls   | ||||
| @@ -32,7 +34,7 @@ A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control | ||||
| - Filesystem-based config for easier backup of presets and settings   | ||||
|  | ||||
| ## 💡 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) | ||||
| - WLED app for [Android](https://play.google.com/store/apps/details?id=ca.cgagnier.wlednativeandroid) and [iOS](https://apps.apple.com/gb/app/wled-native/id6446207239) | ||||
| - JSON and HTTP request APIs   | ||||
| - MQTT    | ||||
| - E1.31, Art-Net, DDP and TPM2.net | ||||
| @@ -61,8 +63,9 @@ See [here](https://kno.wled.ge/basics/compatible-hardware)! | ||||
|  | ||||
| ## ✌️ Other | ||||
|  | ||||
| Licensed under the MIT license   | ||||
| Licensed under the EUPL v1.2 license   | ||||
| Credits [here](https://kno.wled.ge/about/contributors/)! | ||||
| CORS proxy by [Corsfix](https://corsfix.com/) | ||||
|  | ||||
| Join the Discord server to discuss everything about WLED! | ||||
|  | ||||
| @@ -80,5 +83,5 @@ If WLED really brightens up your day, you can [ { | ||||
|   for i in {0..10} | ||||
|   do   | ||||
|     echo -n " http://${HOST}/settings.js?p=$i -o ${TGT_PATH}/$i.xml" | ||||
|   done | ||||
| } | ||||
| read -a TARGETS <<< $(replicate) | ||||
|  | ||||
| mkdir -p ${TGT_PATH} | ||||
| curl ${CURL_ARGS} ${TARGETS[@]} | ||||
| @@ -17,7 +17,7 @@ | ||||
|  | ||||
| const fs = require("node:fs"); | ||||
| const path = require("path"); | ||||
| const inliner = require("inliner"); | ||||
| const inline = require("web-resource-inliner"); | ||||
| const zlib = require("node:zlib"); | ||||
| const CleanCSS = require("clean-css"); | ||||
| const minifyHtml = require("html-minifier-terser").minify; | ||||
| @@ -89,7 +89,7 @@ function adoptVersionAndRepo(html) { | ||||
|     repoUrl = repoUrl.replace(/^git\+/, ""); | ||||
|     repoUrl = repoUrl.replace(/\.git$/, ""); | ||||
|     html = html.replaceAll("https://github.com/atuline/WLED", repoUrl); | ||||
|     html = html.replaceAll("https://github.com/Aircoookie/WLED", repoUrl); | ||||
|     html = html.replaceAll("https://github.com/wled-dev/WLED", repoUrl); | ||||
|   } | ||||
|   let version = packageJson.version; | ||||
|   if (version) { | ||||
| @@ -101,6 +101,7 @@ function adoptVersionAndRepo(html) { | ||||
| async function minify(str, type = "plain") { | ||||
|   const options = { | ||||
|     collapseWhitespace: true, | ||||
|     conservativeCollapse: true, // preserve spaces in text | ||||
|     collapseBooleanAttributes: true, | ||||
|     collapseInlineTagWhitespace: true, | ||||
|     minifyCSS: true, | ||||
| @@ -116,7 +117,8 @@ async function minify(str, type = "plain") { | ||||
|   } else if (type == "css-minify") { | ||||
|     return new CleanCSS({}).minify(str).styles; | ||||
|   } else if (type == "js-minify") { | ||||
|     return await minifyHtml('<script>' + str + '</script>', options).replace(/<[\/]*script>/g, ''); | ||||
|     let js = await minifyHtml('<script>' + str + '</script>', options); | ||||
|     return js.replace(/<[\/]*script>/g, ''); | ||||
|   } else if (type == "html-minify") { | ||||
|     return await minifyHtml(str, options); | ||||
|   } | ||||
| @@ -126,21 +128,26 @@ async function minify(str, type = "plain") { | ||||
|  | ||||
| async function writeHtmlGzipped(sourceFile, resultFile, page) { | ||||
|   console.info("Reading " + sourceFile); | ||||
|   new inliner(sourceFile, async function (error, html) { | ||||
|     if (error) throw error; | ||||
|   inline.html({ | ||||
|     fileContent: fs.readFileSync(sourceFile, "utf8"), | ||||
|     relativeTo: path.dirname(sourceFile), | ||||
|     strict: true, | ||||
|   }, | ||||
|     async function (error, html) { | ||||
|       if (error) throw error; | ||||
|  | ||||
|     html = adoptVersionAndRepo(html); | ||||
|     const originalLength = html.length; | ||||
|     html = await minify(html, "html-minify"); | ||||
|     const result = zlib.gzipSync(html, { level: zlib.constants.Z_BEST_COMPRESSION }); | ||||
|     console.info("Minified and compressed " + sourceFile + " from " + originalLength + " to " + result.length + " bytes"); | ||||
|     const array = hexdump(result); | ||||
|     let src = singleHeader; | ||||
|     src += `const uint16_t PAGE_${page}_L = ${result.length};\n`; | ||||
|     src += `const uint8_t PAGE_${page}[] PROGMEM = {\n${array}\n};\n\n`; | ||||
|     console.info("Writing " + resultFile); | ||||
|     fs.writeFileSync(resultFile, src); | ||||
|   }); | ||||
|       html = adoptVersionAndRepo(html); | ||||
|       const originalLength = html.length; | ||||
|       html = await minify(html, "html-minify"); | ||||
|       const result = zlib.gzipSync(html, { level: zlib.constants.Z_BEST_COMPRESSION }); | ||||
|       console.info("Minified and compressed " + sourceFile + " from " + originalLength + " to " + result.length + " bytes"); | ||||
|       const array = hexdump(result); | ||||
|       let src = singleHeader; | ||||
|       src += `const uint16_t PAGE_${page}_L = ${result.length};\n`; | ||||
|       src += `const uint8_t PAGE_${page}[] PROGMEM = {\n${array}\n};\n\n`; | ||||
|       console.info("Writing " + resultFile); | ||||
|       fs.writeFileSync(resultFile, src); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| async function specToChunk(srcDir, s) { | ||||
| @@ -252,6 +259,12 @@ writeChunks( | ||||
|         str | ||||
|           .replace("%%", "%") | ||||
|     }, | ||||
|     { | ||||
|       file: "common.js", | ||||
|       name: "JS_common", | ||||
|       method: "gzip", | ||||
|       filter: "js-minify", | ||||
|     }, | ||||
|     { | ||||
|       file: "settings.htm", | ||||
|       name: "PAGE_settings", | ||||
|   | ||||
| @@ -27,6 +27,7 @@ read -a JSON_TINY_TARGETS <<< $(replicate "json/nodes") | ||||
| read -a JSON_SMALL_TARGETS <<< $(replicate "json/info") | ||||
| read -a JSON_LARGE_TARGETS <<< $(replicate "json/si") | ||||
| read -a JSON_LARGER_TARGETS <<< $(replicate "json/fxdata") | ||||
| read -a INDEX_TARGETS <<< $(replicate "") | ||||
|  | ||||
| # Expand target URLS to full arguments for curl | ||||
| TARGETS=(${TARGET_STR[@]}) | ||||
|   | ||||
							
								
								
									
										286
									
								
								tools/wled-tools
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										286
									
								
								tools/wled-tools
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,286 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| # WLED Tools | ||||
| # A utility for managing WLED devices in a local network | ||||
| # https://github.com/wled/WLED | ||||
|  | ||||
| # Color Definitions | ||||
| GREEN="\e[32m" | ||||
| RED="\e[31m" | ||||
| BLUE="\e[34m" | ||||
| YELLOW="\e[33m" | ||||
| RESET="\e[0m" | ||||
|  | ||||
| # Logging function | ||||
| log() { | ||||
|     local category="$1" | ||||
|     local color="$2" | ||||
|     local text="$3" | ||||
|  | ||||
|     if [ "$quiet" = true ]; then | ||||
|         return | ||||
|     fi | ||||
|  | ||||
|     if [ -t 1 ]; then  # Check if output is a terminal | ||||
|         echo -e "${color}[${category}]${RESET} ${text}" | ||||
|     else | ||||
|         echo "[${category}] ${text}" | ||||
|     fi | ||||
| } | ||||
|  | ||||
| # Generic curl handler function | ||||
| curl_handler() { | ||||
|     local command="$1" | ||||
|     local hostname="$2" | ||||
|  | ||||
|     response=$($command -w "%{http_code}" -o /dev/null) | ||||
|     curl_exit_code=$? | ||||
|  | ||||
|     if [ "$response" -ge 200 ] && [ "$response" -lt 300 ]; then | ||||
|         return 0 | ||||
|     elif [ $curl_exit_code -ne 0 ]; then | ||||
|         log "ERROR" "$RED" "Connection error during request to $hostname (curl exit code: $curl_exit_code)." | ||||
|         return 1 | ||||
|     elif [ "$response" -ge 400 ]; then | ||||
|         log "ERROR" "$RED" "Server error during request to $hostname (HTTP status code: $response)." | ||||
|         return 2 | ||||
|     else | ||||
|         log "ERROR" "$RED" "Unexpected response from $hostname (HTTP status code: $response)." | ||||
|         return 3 | ||||
|     fi | ||||
| } | ||||
|  | ||||
| # Print help message | ||||
| show_help() { | ||||
|     cat << EOF | ||||
| Usage: wled-tools.sh [OPTIONS] COMMAND [ARGS...] | ||||
|  | ||||
| Options: | ||||
|   -h, --help              Show this help message and exit. | ||||
|   -t, --target <IP/Host>  Specify a single WLED device by IP address or hostname. | ||||
|   -D, --discover          Discover multiple WLED devices using mDNS. | ||||
|   -d, --directory <Path>  Specify a directory for saving backups (default: working directory). | ||||
|   -f, --firmware <File>   Specify the firmware file for updating devices. | ||||
|   -q, --quiet             Suppress logging output (also makes discover output hostnames only). | ||||
|  | ||||
| Commands: | ||||
|   backup      Backup the current state of a WLED device or multiple discovered devices. | ||||
|   update      Update the firmware of a WLED device or multiple discovered devices. | ||||
|   discover    Discover WLED devices using mDNS and list their IP addresses and names. | ||||
|  | ||||
| Examples: | ||||
|   # Discover all WLED devices on the network | ||||
|   ./wled-tools discover | ||||
|  | ||||
|   # Backup a specific WLED device | ||||
|   ./wled-tools -t 192.168.1.100 backup | ||||
|  | ||||
|   # Backup all discovered WLED devices to a specific directory | ||||
|   ./wled-tools -D -d /path/to/backups backup | ||||
|  | ||||
|   # Update firmware on all discovered WLED devices | ||||
|   ./wled-tools -D -f /path/to/firmware.bin update | ||||
|  | ||||
| EOF | ||||
| } | ||||
|  | ||||
| # Discover devices using mDNS | ||||
| discover_devices() {   | ||||
|     if ! command -v avahi-browse &> /dev/null; then   | ||||
|         log "ERROR" "$RED" "'avahi-browse' is required but not installed, please install avahi-utils using your preferred package manager." | ||||
|         exit 1   | ||||
|     fi   | ||||
|  | ||||
|     # Map avahi responses to strings seperated by 0x1F (unit separator) | ||||
|     mapfile -t raw_devices < <(avahi-browse _wled._tcp --terminate -r -p | awk -F';' '/^=/ {print $7"\x1F"$8"\x1F"$9}')   | ||||
|  | ||||
|     local devices_array=()   | ||||
|     for device in "${raw_devices[@]}"; do   | ||||
|         IFS=$'\x1F' read -r hostname address port <<< "$device"   | ||||
|         devices_array+=("$hostname" "$address" "$port")   | ||||
|     done   | ||||
|  | ||||
|     echo "${devices_array[@]}"   | ||||
| }   | ||||
|  | ||||
| # Backup one device | ||||
| backup_one() { | ||||
|     local hostname="$1" | ||||
|     local address="$2" | ||||
|     local port="$3" | ||||
|  | ||||
|     log "INFO" "$YELLOW" "Backing up device config/presets: $hostname ($address:$port)" | ||||
|  | ||||
|     mkdir -p "$backup_dir" | ||||
|  | ||||
|     local cfg_url="http://$address:$port/cfg.json" | ||||
|     local presets_url="http://$address:$port/presets.json" | ||||
|     local cfg_dest="${backup_dir}/${hostname}.cfg.json" | ||||
|     local presets_dest="${backup_dir}/${hostname}.presets.json" | ||||
|  | ||||
|     # Write to ".tmp" files first, then move when success, to ensure we don't write partial files | ||||
|     local curl_command_cfg="curl -s "$cfg_url" -o "$cfg_dest.tmp"" | ||||
|     local curl_command_presets="curl -s "$presets_url" -o "$presets_dest.tmp"" | ||||
|  | ||||
|     if ! curl_handler "$curl_command_cfg" "$hostname"; then   | ||||
|         log "ERROR" "$RED" "Failed to backup configuration for $hostname"   | ||||
|         rm -f "$cfg_dest.tmp"   | ||||
|         return 1   | ||||
|     fi   | ||||
|      | ||||
|     if ! curl_handler "$curl_command_presets" "$hostname"; then   | ||||
|         log "ERROR" "$RED" "Failed to backup presets for $hostname"   | ||||
|         rm -f "$presets_dest.tmp"   | ||||
|         return 1   | ||||
|     fi  | ||||
|  | ||||
|     mv "$cfg_dest.tmp" "$cfg_dest" | ||||
|     mv "$presets_dest.tmp" "$presets_dest" | ||||
|     log "INFO" "$GREEN" "Successfully backed up config and presets for $hostname" | ||||
|     return 0 | ||||
| } | ||||
|  | ||||
| # Update one device | ||||
| update_one() { | ||||
|     local hostname="$1" | ||||
|     local address="$2" | ||||
|     local port="$3" | ||||
|     local firmware="$4" | ||||
|  | ||||
|     log "INFO" "$YELLOW" "Starting firmware update for device: $hostname ($address:$port)" | ||||
|  | ||||
|     local url="http://$address:$port/update" | ||||
|     local curl_command="curl -s -X POST -F "file=@$firmware" "$url"" | ||||
|  | ||||
|     if ! curl_handler "$curl_command" "$hostname"; then | ||||
|         log "ERROR" "$RED" "Failed to update firmware for $hostname" | ||||
|         return 1 | ||||
|     fi | ||||
|      | ||||
|     log "INFO" "$GREEN" "Successfully initiated firmware update for $hostname" | ||||
|     return 0 | ||||
| } | ||||
|  | ||||
| # Command-line arguments processing | ||||
| command="" | ||||
| target="" | ||||
| discover=false | ||||
| quiet=false | ||||
| backup_dir="./" | ||||
| firmware_file="" | ||||
|  | ||||
| if [ $# -eq 0 ]; then | ||||
|     show_help | ||||
|     exit 0 | ||||
| fi | ||||
|  | ||||
| while [[ $# -gt 0 ]]; do | ||||
|     case "$1" in | ||||
|         -h|--help) | ||||
|             show_help | ||||
|             exit 0 | ||||
|             ;; | ||||
|         -t|--target) | ||||
|             if [ -z "$2" ] || [[ "$2" == -* ]]; then | ||||
|                 log "ERROR" "$RED" "The --target option requires an argument." | ||||
|                 exit 1 | ||||
|             fi | ||||
|             target="$2" | ||||
|             shift 2 | ||||
|             ;; | ||||
|         -D|--discover) | ||||
|             discover=true | ||||
|             shift | ||||
|             ;; | ||||
|         -d|--directory) | ||||
|             if [ -z "$2" ] || [[ "$2" == -* ]]; then | ||||
|                 log "ERROR" "$RED" "The --directory option requires an argument." | ||||
|                 exit 1 | ||||
|             fi | ||||
|             backup_dir="$2" | ||||
|             shift 2 | ||||
|             ;; | ||||
|         -f|--firmware) | ||||
|             if [ -z "$2" ] || [[ "$2" == -* ]]; then | ||||
|                 log "ERROR" "$RED" "The --firmware option requires an argument." | ||||
|                 exit 1 | ||||
|             fi | ||||
|             firmware_file="$2" | ||||
|             shift 2 | ||||
|             ;; | ||||
|         -q|--quiet) | ||||
|             quiet=true | ||||
|             shift | ||||
|             ;; | ||||
|         backup|update|discover) | ||||
|             command="$1" | ||||
|             shift | ||||
|             ;; | ||||
|         *) | ||||
|             log "ERROR" "$RED" "Unknown argument: $1" | ||||
|             exit 1 | ||||
|             ;; | ||||
|     esac | ||||
| done | ||||
|  | ||||
| # Execute the appropriate command | ||||
| case "$command" in | ||||
|     discover) | ||||
|         read -ra devices <<< "$(discover_devices)" | ||||
|         for ((i=0; i<${#devices[@]}; i+=3)); do | ||||
|             hostname="${devices[$i]}" | ||||
|             address="${devices[$i+1]}" | ||||
|             port="${devices[$i+2]}" | ||||
|  | ||||
|             if [ "$quiet" = true ]; then | ||||
|                 echo "$hostname" | ||||
|             else | ||||
|                 log "INFO" "$BLUE" "Discovered device: Hostname=$hostname, Address=$address, Port=$port" | ||||
|             fi | ||||
|         done | ||||
|         ;; | ||||
|     backup) | ||||
|         if [ -n "$target" ]; then | ||||
|             # Assume target is both the hostname and address, with port 80 | ||||
|             backup_one "$target" "$target" "80" | ||||
|         elif [ "$discover" = true ]; then | ||||
|             read -ra devices <<< "$(discover_devices)" | ||||
|             for ((i=0; i<${#devices[@]}; i+=3)); do | ||||
|                 hostname="${devices[$i]}" | ||||
|                 address="${devices[$i+1]}" | ||||
|                 port="${devices[$i+2]}" | ||||
|                 backup_one "$hostname" "$address" "$port" | ||||
|             done | ||||
|         else | ||||
|             log "ERROR" "$RED" "No target specified. Use --target or --discover." | ||||
|             exit 1 | ||||
|         fi | ||||
|         ;; | ||||
|     update) | ||||
|         # Validate firmware before proceeding | ||||
|         if [ -z "$firmware_file" ] || [ ! -f "$firmware_file" ]; then | ||||
|             log "ERROR" "$RED" "Please provide a file in --firmware that exists" | ||||
|             exit 1 | ||||
|         fi | ||||
|          | ||||
|         if [ -n "$target" ]; then | ||||
|             # Assume target is both the hostname and address, with port 80 | ||||
|             update_one "$target" "$target" "80" "$firmware_file" | ||||
|         elif [ "$discover" = true ]; then | ||||
|             read -ra devices <<< "$(discover_devices)" | ||||
|             for ((i=0; i<${#devices[@]}; i+=3)); do | ||||
|                 hostname="${devices[$i]}" | ||||
|                 address="${devices[$i+1]}" | ||||
|                 port="${devices[$i+2]}" | ||||
|                 update_one "$hostname" "$address" "$port" "$firmware_file" | ||||
|             done | ||||
|         else | ||||
|             log "ERROR" "$RED" "No target specified. Use --target or --discover." | ||||
|             exit 1 | ||||
|         fi | ||||
|         ;; | ||||
|     *) | ||||
|         show_help | ||||
|         exit 1 | ||||
|         ;; | ||||
| esac | ||||
| @@ -1,5 +1,3 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "wled.h" | ||||
| #include <Adafruit_ADS1X15.h> | ||||
| #include <math.h> | ||||
| @@ -252,4 +250,7 @@ class ADS1115Usermod : public Usermod { | ||||
|         int16_t results = ads.getLastConversionResults(); | ||||
|         readings[activeChannel] = ads.computeVolts(results); | ||||
|     } | ||||
| }; | ||||
| }; | ||||
| 
 | ||||
| static ADS1115Usermod ads1115_v2; | ||||
| REGISTER_USERMOD(ads1115_v2); | ||||
							
								
								
									
										8
									
								
								usermods/ADS1115_v2/library.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								usermods/ADS1115_v2/library.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| { | ||||
|   "name": "ADS1115_v2", | ||||
|   "build": { "libArchive": false }, | ||||
|   "dependencies": { | ||||
|     "Adafruit BusIO": "https://github.com/adafruit/Adafruit_BusIO#1.13.2", | ||||
|     "Adafruit ADS1X15": "https://github.com/adafruit/Adafruit_ADS1X15#2.4.0" | ||||
|   } | ||||
| } | ||||
| @@ -6,5 +6,5 @@ Configuration is performed via the Usermod menu. There are no parameters to set | ||||
|  | ||||
| ## Installation  | ||||
|  | ||||
| Add the build flag `-D USERMOD_ADS1115` to your platformio environment. | ||||
| Uncomment libraries with comment `#For ADS1115 sensor uncomment following` | ||||
| Add 'ADS1115' to `custom_usermods` in your platformio environment. | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,3 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "wled.h" | ||||
| #include <AHT10.h> | ||||
| 
 | ||||
| @@ -54,12 +52,6 @@ private: | ||||
|     _lastTemperature = 0; | ||||
|   } | ||||
| 
 | ||||
|   ~UsermodAHT10() | ||||
|   { | ||||
|     delete _aht; | ||||
|     _aht = nullptr; | ||||
|   } | ||||
| 
 | ||||
| #ifndef WLED_DISABLE_MQTT | ||||
|   void mqttInitialize() | ||||
|   { | ||||
| @@ -322,6 +314,15 @@ public: | ||||
|     _initDone = true; | ||||
|     return configComplete; | ||||
|   } | ||||
| 
 | ||||
|   ~UsermodAHT10() | ||||
|   { | ||||
|     delete _aht; | ||||
|     _aht = nullptr; | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| const char UsermodAHT10::_name[] PROGMEM = "AHTxx"; | ||||
| const char UsermodAHT10::_name[] PROGMEM = "AHTxx"; | ||||
| 
 | ||||
| static UsermodAHT10 aht10_v2; | ||||
| REGISTER_USERMOD(aht10_v2); | ||||
| @@ -22,15 +22,9 @@ Dependencies, These must be added under `lib_deps` in your `platform.ini` (or `p | ||||
|  | ||||
| # Compiling | ||||
|  | ||||
| To enable, compile with `USERMOD_AHT10` defined  (e.g. in `platformio_override.ini`) | ||||
| To enable, add 'AHT10' to `custom_usermods` in your platformio encrionment  (e.g. in `platformio_override.ini`) | ||||
| ```ini | ||||
| [env:aht10_example] | ||||
| extends = env:esp32dev | ||||
| build_flags = | ||||
|   ${common.build_flags} ${esp32.build_flags} | ||||
|   -D USERMOD_AHT10 | ||||
|   ; -D USERMOD_AHT10_DEBUG ; -- add a debug status to the info modal | ||||
| lib_deps =  | ||||
|   ${esp32.lib_deps} | ||||
|   enjoyneering/AHT10@~1.1.0 | ||||
| custom_usermods = ${env:esp32dev.custom_usermods} AHT10 | ||||
| ``` | ||||
|   | ||||
							
								
								
									
										7
									
								
								usermods/AHT10_v2/library.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								usermods/AHT10_v2/library.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| { | ||||
|   "name": "AHT10_v2", | ||||
|   "build": { "libArchive": false },   | ||||
|   "dependencies": { | ||||
|     "enjoyneering/AHT10":"~1.1.0" | ||||
|   } | ||||
| } | ||||
| @@ -2,8 +2,4 @@ | ||||
| extends = env:esp32dev | ||||
| build_flags = | ||||
|   ${common.build_flags} ${esp32.build_flags} | ||||
|   -D USERMOD_AHT10 | ||||
|   ; -D USERMOD_AHT10_DEBUG ; -- add a debug status to the info modal | ||||
| lib_deps =  | ||||
|   ${esp32.lib_deps} | ||||
|   enjoyneering/AHT10@~1.1.0 | ||||
| @@ -1,4 +1,3 @@ | ||||
| #pragma once | ||||
| #include "wled.h" | ||||
| 
 | ||||
| /*
 | ||||
| @@ -102,10 +101,10 @@ private: | ||||
| 
 | ||||
|     void secondsEffectSineFade(int16_t secondLed, Toki::Time const& time) { | ||||
|         uint32_t ms = time.ms % 1000; | ||||
|         uint8_t b0 = (cos8(ms * 64 / 1000) - 128) * 2; | ||||
|         setPixelColor(secondLed, gamma32(scale32(secondColor, b0))); | ||||
|         uint8_t b1 = (sin8(ms * 64 / 1000) - 128) * 2; | ||||
|         setPixelColor(inc(secondLed, 1, secondsSegment), gamma32(scale32(secondColor, b1))); | ||||
|         uint8_t b0 = (cos8_t(ms * 64 / 1000) - 128) * 2; | ||||
|         setPixelColor(secondLed, scale32(secondColor, b0)); | ||||
|         uint8_t b1 = (sin8_t(ms * 64 / 1000) - 128) * 2; | ||||
|         setPixelColor(inc(secondLed, 1, secondsSegment), scale32(secondColor, b1)); | ||||
|     } | ||||
| 
 | ||||
|     static inline uint32_t qadd32(uint32_t c1, uint32_t c2) { | ||||
| @@ -192,7 +191,7 @@ public: | ||||
|             // for (uint16_t i = 1; i < secondsTrail + 1; ++i) {
 | ||||
|             //     uint16_t trailLed = dec(secondLed, i, secondsSegment);
 | ||||
|             //     uint8_t trailBright = 255 / (secondsTrail + 1) * (secondsTrail - i + 1);
 | ||||
|             //     setPixelColor(trailLed, gamma32(scale32(secondColor, trailBright)));
 | ||||
|             //     setPixelColor(trailLed, scale32(secondColor, trailBright));
 | ||||
|             // }
 | ||||
|         } | ||||
| 
 | ||||
| @@ -254,3 +253,7 @@ public: | ||||
|         return USERMOD_ID_ANALOG_CLOCK; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| static AnalogClockUsermod analog_clock; | ||||
| REGISTER_USERMOD(analog_clock); | ||||
							
								
								
									
										4
									
								
								usermods/Analog_Clock/library.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								usermods/Analog_Clock/library.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| { | ||||
|   "name": "Analog_Clock", | ||||
|   "build": { "libArchive": false } | ||||
| } | ||||
| @@ -7,7 +7,6 @@ | ||||
|  *  | ||||
|  * See the accompanying README.md file for more info. | ||||
|  */ | ||||
| #pragma once | ||||
| #include "wled.h" | ||||
| 
 | ||||
| class Animated_Staircase : public Usermod { | ||||
| @@ -332,7 +331,7 @@ class Animated_Staircase : public Usermod { | ||||
|       }; | ||||
|       // 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)) { | ||||
|       if (!PinManager::allocateMultiplePins(pins, 4, PinOwner::UM_AnimatedStaircase)) { | ||||
|         topPIRorTriggerPin = -1; | ||||
|         topEchoPin = -1; | ||||
|         bottomPIRorTriggerPin = -1; | ||||
| @@ -425,10 +424,10 @@ class Animated_Staircase : public Usermod { | ||||
|     } | ||||
| 
 | ||||
|     void appendConfigData() { | ||||
|       //oappend(SET_F("dd=addDropdown('staircase','selectfield');"));
 | ||||
|       //oappend(SET_F("addOption(dd,'1st value',0);"));
 | ||||
|       //oappend(SET_F("addOption(dd,'2nd value',1);"));
 | ||||
|       //oappend(SET_F("addInfo('staircase:selectfield',1,'additional info');"));  // 0 is field type, 1 is actual field
 | ||||
|       //oappend(F("dd=addDropdown('staircase','selectfield');"));
 | ||||
|       //oappend(F("addOption(dd,'1st value',0);"));
 | ||||
|       //oappend(F("addOption(dd,'2nd value',1);"));
 | ||||
|       //oappend(F("addInfo('staircase:selectfield',1,'additional info');"));  // 0 is field type, 1 is actual field
 | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| @@ -513,10 +512,10 @@ class Animated_Staircase : public Usermod { | ||||
|             (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); | ||||
|           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(); | ||||
|       } | ||||
| @@ -562,3 +561,7 @@ const char Animated_Staircase::_bottomEcho_pin[]            PROGMEM = "bottomEch | ||||
| const char Animated_Staircase::_topEchoCm[]                 PROGMEM = "top-dist-cm"; | ||||
| const char Animated_Staircase::_bottomEchoCm[]              PROGMEM = "bottom-dist-cm"; | ||||
| const char Animated_Staircase::_togglePower[]               PROGMEM = "toggle-on-off"; | ||||
| 
 | ||||
| 
 | ||||
| static Animated_Staircase animated_staircase; | ||||
| REGISTER_USERMOD(animated_staircase); | ||||
| @@ -1,4 +1,5 @@ | ||||
| # Usermod Animated Staircase | ||||
|  | ||||
| This usermod makes your staircase look cool by illuminating it with an animation. It uses | ||||
| PIR or ultrasonic sensors at the top and bottom of your stairs to: | ||||
|  | ||||
| @@ -11,14 +12,15 @@ The Animated Staircase can be controlled by the WLED API. Change settings such a | ||||
| speed, on/off time and distance 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://kno.wled.ge/advanced/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. | ||||
| Edit your environment in `platformio_override.ini` | ||||
|  | ||||
| 1. Open `platformio_override.ini` | ||||
| 2. add `Animated_Staircase` to the `custom_usermods` line for your environment | ||||
|  | ||||
| You can configure usermod using the Usermods settings page. | ||||
| Please enter GPIO pins for PIR or ultrasonic sensors (trigger and echo). | ||||
| @@ -26,10 +28,10 @@ If you use PIR sensor enter -1 for echo pin. | ||||
| Maximum distance for ultrasonic sensor can be configured as the time needed for an echo (see below). | ||||
|  | ||||
| ## Hardware installation | ||||
|  | ||||
| 1. Attach the LED strip to 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. | ||||
| 3. Connect the data-out pin at the end of each strip per step to the data-in pin on the  | ||||
|    next step, creating one large virtual LED strip. | ||||
| 3. Connect the data-out pin at the end of each strip per step to the data-in pin on 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 | ||||
| @@ -38,24 +40,23 @@ Maximum distance for ultrasonic sensor can be configured as the time needed for | ||||
| 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, configure 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. | ||||
|  | ||||
| 1. In the WLED UI, configure 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   |  | ||||
| | bottom-sensor    | Manually trigger a down to up animation via API               | false   | | ||||
| | top-sensor       | Manually trigger an up to down animation via API              | false   | | ||||
|  | ||||
|  | ||||
| @@ -75,6 +76,7 @@ The staircase settings and sensor states are inside the WLED "state" element: | ||||
| ``` | ||||
|  | ||||
| ### 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 you to play with the lights without the usermod switching them on or off. | ||||
|  | ||||
| @@ -91,6 +93,7 @@ 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, including sensor pins, delay between segment activation etc. | ||||
|  | ||||
| When an ultrasonic sensor is enabled you can enter maximum detection distance in centimeters separately for top and bottom sensors. | ||||
| @@ -100,6 +103,7 @@ distances creates delays in the WLED software, _might_ introduce timing hiccups | ||||
| a less responsive web interface. It is therefore advised to keep the detection distance as short as possible. | ||||
|  | ||||
| ### Animation triggering through the API | ||||
|  | ||||
| In addition to activation by one of the stair sensors, you can also trigger the animation manually | ||||
| via the API. To simulate triggering the bottom sensor, use: | ||||
|  | ||||
| @@ -116,15 +120,19 @@ 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 the usermod. | ||||
|  | ||||
| Have fun with this usermod.<br/> | ||||
| www.rolfje.com | ||||
| Have fun with this usermod | ||||
|  | ||||
| `www.rolfje.com` | ||||
|  | ||||
| Modifications @blazoncek | ||||
|  | ||||
| ## Change log | ||||
|  | ||||
| 2021-04 | ||||
| * Adaptation for runtime configuration. | ||||
|  | ||||
| - Adaptation for runtime configuration. | ||||
|   | ||||
							
								
								
									
										4
									
								
								usermods/Animated_Staircase/library.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								usermods/Animated_Staircase/library.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| { | ||||
|   "name": "Animated_Staircase", | ||||
|   "build": { "libArchive": false } | ||||
| } | ||||
							
								
								
									
										186
									
								
								usermods/BH1750_v2/BH1750_v2.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								usermods/BH1750_v2/BH1750_v2.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,186 @@ | ||||
| // force the compiler to show a warning to confirm that this file is included | ||||
| #warning **** Included USERMOD_BH1750 **** | ||||
|  | ||||
| #include "wled.h" | ||||
| #include "BH1750_v2.h" | ||||
|  | ||||
| #ifdef WLED_DISABLE_MQTT | ||||
| #error "This user mod requires MQTT to be enabled." | ||||
| #endif | ||||
|  | ||||
| static bool checkBoundSensor(float newValue, float prevValue, float maxDiff) | ||||
| { | ||||
|   return isnan(prevValue) || newValue <= prevValue - maxDiff || newValue >= prevValue + maxDiff || (newValue == 0.0 && prevValue > 0.0); | ||||
| } | ||||
|  | ||||
| void Usermod_BH1750::_mqttInitialize() | ||||
| { | ||||
|   mqttLuminanceTopic = String(mqttDeviceTopic) + F("/brightness"); | ||||
|  | ||||
|   if (HomeAssistantDiscovery) _createMqttSensor(F("Brightness"), mqttLuminanceTopic, F("Illuminance"), F(" lx")); | ||||
| } | ||||
|  | ||||
| // Create an MQTT Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop. | ||||
| void Usermod_BH1750::_createMqttSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement) | ||||
| { | ||||
|   String t = String(F("homeassistant/sensor/")) + mqttClientID + F("/") + name + F("/config"); | ||||
|    | ||||
|   StaticJsonDocument<600> doc; | ||||
|    | ||||
|   doc[F("name")] = String(serverDescription) + " " + name; | ||||
|   doc[F("state_topic")] = topic; | ||||
|   doc[F("unique_id")] = String(mqttClientID) + name; | ||||
|   if (unitOfMeasurement != "") | ||||
|     doc[F("unit_of_measurement")] = unitOfMeasurement; | ||||
|   if (deviceClass != "") | ||||
|     doc[F("device_class")] = deviceClass; | ||||
|   doc[F("expire_after")] = 1800; | ||||
|  | ||||
|   JsonObject device = doc.createNestedObject(F("device")); // attach the sensor to the same device | ||||
|   device[F("name")] = serverDescription; | ||||
|   device[F("identifiers")] = "wled-sensor-" + String(mqttClientID); | ||||
|   device[F("manufacturer")] = F(WLED_BRAND); | ||||
|   device[F("model")] = F(WLED_PRODUCT_NAME); | ||||
|   device[F("sw_version")] = versionString; | ||||
|  | ||||
|   String temp; | ||||
|   serializeJson(doc, temp); | ||||
|   DEBUG_PRINTLN(t); | ||||
|   DEBUG_PRINTLN(temp); | ||||
|  | ||||
|   mqtt->publish(t.c_str(), 0, true, temp.c_str()); | ||||
| } | ||||
|  | ||||
| void Usermod_BH1750::setup() | ||||
| { | ||||
|   if (i2c_scl<0 || i2c_sda<0) { enabled = false; return; } | ||||
|   sensorFound = lightMeter.begin(); | ||||
|   initDone = true; | ||||
| } | ||||
|  | ||||
| void Usermod_BH1750::loop() | ||||
| { | ||||
|   if ((!enabled) || 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) | ||||
|     { | ||||
|       if (!mqttInitialized) | ||||
|         { | ||||
|           _mqttInitialize(); | ||||
|           mqttInitialized = true; | ||||
|         } | ||||
|       mqtt->publish(mqttLuminanceTopic.c_str(), 0, true, String(lux).c_str()); | ||||
|       DEBUG_PRINTLN(F("Brightness: ") + String(lux) + F("lx")); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|       DEBUG_PRINTLN(F("Missing MQTT connection. Not publishing data")); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
|  | ||||
| void Usermod_BH1750::addToJsonInfo(JsonObject &root) | ||||
| { | ||||
|   JsonObject user = root[F("u")]; | ||||
|   if (user.isNull()) | ||||
|     user = root.createNestedObject(F("u")); | ||||
|  | ||||
|   JsonArray lux_json = user.createNestedArray(F("Luminance")); | ||||
|   if (!enabled) { | ||||
|     lux_json.add(F("disabled")); | ||||
|   } else if (!sensorFound) { | ||||
|       // if no sensor  | ||||
|       lux_json.add(F("BH1750 ")); | ||||
|       lux_json.add(F("Not Found")); | ||||
|   } else 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; | ||||
|   } else { | ||||
|     lux_json.add(lastLux); | ||||
|     lux_json.add(F(" lx")); | ||||
|   } | ||||
| } | ||||
|  | ||||
| // (called from set.cpp) stores persistent properties to cfg.json | ||||
| void Usermod_BH1750::addToConfig(JsonObject &root) | ||||
| { | ||||
|   // we add JSON object. | ||||
|   JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname | ||||
|   top[FPSTR(_enabled)] = enabled; | ||||
|   top[FPSTR(_maxReadInterval)] = maxReadingInterval; | ||||
|   top[FPSTR(_minReadInterval)] = minReadingInterval; | ||||
|   top[FPSTR(_HomeAssistantDiscovery)] = HomeAssistantDiscovery; | ||||
|   top[FPSTR(_offset)] = offset; | ||||
|  | ||||
|   DEBUG_PRINTLN(F("BH1750 config saved.")); | ||||
| } | ||||
|  | ||||
| // called before setup() to populate properties from values stored in cfg.json | ||||
| bool Usermod_BH1750::readFromConfig(JsonObject &root) | ||||
| { | ||||
|   // we look for JSON object. | ||||
|   JsonObject top = root[FPSTR(_name)]; | ||||
|   if (top.isNull()) | ||||
|   { | ||||
|     DEBUG_PRINT(FPSTR(_name)); | ||||
|     DEBUG_PRINT(F("BH1750")); | ||||
|     DEBUG_PRINTLN(F(": No config found. (Using defaults.)")); | ||||
|     return false; | ||||
|   } | ||||
|   bool configComplete = !top.isNull(); | ||||
|  | ||||
|   configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled, false); | ||||
|   configComplete &= getJsonValue(top[FPSTR(_maxReadInterval)], maxReadingInterval, 10000); //ms | ||||
|   configComplete &= getJsonValue(top[FPSTR(_minReadInterval)], minReadingInterval, 500); //ms | ||||
|   configComplete &= getJsonValue(top[FPSTR(_HomeAssistantDiscovery)], HomeAssistantDiscovery, false); | ||||
|   configComplete &= getJsonValue(top[FPSTR(_offset)], offset, 1); | ||||
|  | ||||
|   DEBUG_PRINT(FPSTR(_name)); | ||||
|   if (!initDone) { | ||||
|     DEBUG_PRINTLN(F(" config loaded.")); | ||||
|   } else { | ||||
|     DEBUG_PRINTLN(F(" config (re)loaded.")); | ||||
|   } | ||||
|  | ||||
|   return configComplete; | ||||
|    | ||||
| } | ||||
|  | ||||
|  | ||||
| // 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::_HomeAssistantDiscovery[] PROGMEM = "HomeAssistantDiscoveryLux"; | ||||
| const char Usermod_BH1750::_offset[] PROGMEM = "offset-lx"; | ||||
|  | ||||
|  | ||||
| static Usermod_BH1750 bh1750_v2; | ||||
| REGISTER_USERMOD(bh1750_v2); | ||||
							
								
								
									
										92
									
								
								usermods/BH1750_v2/BH1750_v2.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								usermods/BH1750_v2/BH1750_v2.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | ||||
|  | ||||
| #pragma once | ||||
| #include "wled.h" | ||||
| #include <BH1750.h> | ||||
|  | ||||
| #ifdef WLED_DISABLE_MQTT | ||||
| #error "This user mod requires MQTT to be enabled." | ||||
| #endif | ||||
|  | ||||
| // 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 difference 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 enabled = true; | ||||
|  | ||||
|   // 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[]; | ||||
|   static const char _HomeAssistantDiscovery[]; | ||||
|  | ||||
|   bool initDone = false; | ||||
|   bool sensorFound = false; | ||||
|  | ||||
|   // Home Assistant and MQTT   | ||||
|   String mqttLuminanceTopic; | ||||
|   bool mqttInitialized = false; | ||||
|   bool HomeAssistantDiscovery = true; // Publish Home Assistant Discovery messages | ||||
|  | ||||
|   BH1750 lightMeter; | ||||
|   float lastLux = -1000; | ||||
|  | ||||
|   // set up Home Assistant discovery entries | ||||
|   void _mqttInitialize(); | ||||
|  | ||||
|   // Create an MQTT Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop. | ||||
|   void _createMqttSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement); | ||||
|  | ||||
| public: | ||||
|   void setup(); | ||||
|   void loop(); | ||||
|   inline float getIlluminance()  { | ||||
|     return (float)lastLux; | ||||
|   } | ||||
|  | ||||
|   void addToJsonInfo(JsonObject &root); | ||||
|  | ||||
|   // (called from set.cpp) stores persistent properties to cfg.json | ||||
|   void addToConfig(JsonObject &root); | ||||
|  | ||||
|   // called before setup() to populate properties from values stored in cfg.json | ||||
|   bool readFromConfig(JsonObject &root); | ||||
|  | ||||
|   inline uint16_t getId() | ||||
|   { | ||||
|     return USERMOD_ID_BH1750; | ||||
|   } | ||||
|  | ||||
| }; | ||||
							
								
								
									
										7
									
								
								usermods/BH1750_v2/library.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								usermods/BH1750_v2/library.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| { | ||||
|   "name": "BH1750_v2", | ||||
|   "build": { "libArchive": false }, | ||||
|   "dependencies": { | ||||
|     "claws/BH1750":"^1.2.0" | ||||
|   } | ||||
| } | ||||
| @@ -4,43 +4,40 @@ This usermod will read from an ambient light sensor like the BH1750. | ||||
| The luminance is displayed in both the Info section of the web UI, as well as published to the `/luminance` MQTT topic if enabled. | ||||
|  | ||||
| ## Dependencies | ||||
|  | ||||
| - Libraries | ||||
|   - `claws/BH1750 @^1.2.0` | ||||
|   - This must be added under `lib_deps` in your `platformio.ini` (or `platformio_override.ini`). | ||||
| - Data is published over MQTT - make sure you've enabled the MQTT sync interface. | ||||
|  | ||||
| ## Compilation | ||||
|  | ||||
| To enable, compile with `USERMOD_BH1750` defined  (e.g. in `platformio_override.ini`) | ||||
| ```ini | ||||
| [env:usermod_BH1750_d1_mini] | ||||
| extends = env:d1_mini | ||||
| build_flags = | ||||
|     ${common.build_flags_esp8266} | ||||
|     -D USERMOD_BH1750 | ||||
| lib_deps =  | ||||
|     ${esp8266.lib_deps} | ||||
|     claws/BH1750 @ ^1.2.0 | ||||
| ``` | ||||
| To enable, compile with `BH1750` in `custom_usermods` (e.g. in `platformio_override.ini`) | ||||
|  | ||||
| ### Configuration Options | ||||
|  | ||||
| The following settings can be set at compile-time but are configurable on the usermod menu (except First Measurement time): | ||||
| *   `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_OFFSET_VALUE` - the offset value to report on, defaults to 1 | ||||
| *   `USERMOD_BH1750_FIRST_MEASUREMENT_AT` - the number of milliseconds after boot to take first measurement, defaults to 10000 ms | ||||
|  | ||||
| - `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_OFFSET_VALUE` - the offset value to report on, defaults to 1 | ||||
| - `USERMOD_BH1750_FIRST_MEASUREMENT_AT` - the number of milliseconds after boot to take first measurement, defaults to 10000 ms | ||||
|  | ||||
| In addition, the Usermod screen allows you to: | ||||
|  | ||||
| - enable/disable the usermod | ||||
| - Enable Home Assistant Discovery of usermod | ||||
| - Configure the SCL/SDA pins | ||||
|  | ||||
| ## API | ||||
|  | ||||
| The following method is available to interact with the usermod from other code modules: | ||||
|  | ||||
| - `getIlluminance` read the brightness from the sensor | ||||
|  | ||||
| ## Change Log | ||||
|  | ||||
| Jul 2022 | ||||
|  | ||||
| - Added Home Assistant Discovery | ||||
| - Implemented PinManager to register pins | ||||
| - Made pins configurable in usermod menu | ||||
|   | ||||
| @@ -1,252 +0,0 @@ | ||||
| // force the compiler to show a warning to confirm that this file is included | ||||
| #warning **** Included USERMOD_BH1750 **** | ||||
|  | ||||
| #ifndef WLED_ENABLE_MQTT | ||||
| #error "This user mod requires MQTT to be enabled." | ||||
| #endif | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "wled.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 difference 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 enabled = true; | ||||
|  | ||||
|   // 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[]; | ||||
|   static const char _HomeAssistantDiscovery[]; | ||||
|  | ||||
|   bool initDone = false; | ||||
|   bool sensorFound = false; | ||||
|  | ||||
|   // Home Assistant and MQTT   | ||||
|   String mqttLuminanceTopic; | ||||
|   bool mqttInitialized = false; | ||||
|   bool HomeAssistantDiscovery = true; // Publish Home Assistant Discovery messages | ||||
|  | ||||
|   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); | ||||
|   } | ||||
|    | ||||
|   // set up Home Assistant discovery entries | ||||
|   void _mqttInitialize() | ||||
|   { | ||||
|     mqttLuminanceTopic = String(mqttDeviceTopic) + F("/brightness"); | ||||
|  | ||||
|     if (HomeAssistantDiscovery) _createMqttSensor(F("Brightness"), mqttLuminanceTopic, F("Illuminance"), F(" lx")); | ||||
|   } | ||||
|  | ||||
|   // Create an MQTT Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop. | ||||
|   void _createMqttSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement) | ||||
|   { | ||||
|     String t = String(F("homeassistant/sensor/")) + mqttClientID + F("/") + name + F("/config"); | ||||
|      | ||||
|     StaticJsonDocument<600> doc; | ||||
|      | ||||
|     doc[F("name")] = String(serverDescription) + " " + name; | ||||
|     doc[F("state_topic")] = topic; | ||||
|     doc[F("unique_id")] = String(mqttClientID) + name; | ||||
|     if (unitOfMeasurement != "") | ||||
|       doc[F("unit_of_measurement")] = unitOfMeasurement; | ||||
|     if (deviceClass != "") | ||||
|       doc[F("device_class")] = deviceClass; | ||||
|     doc[F("expire_after")] = 1800; | ||||
|  | ||||
|     JsonObject device = doc.createNestedObject(F("device")); // attach the sensor to the same device | ||||
|     device[F("name")] = serverDescription; | ||||
|     device[F("identifiers")] = "wled-sensor-" + String(mqttClientID); | ||||
|     device[F("manufacturer")] = F(WLED_BRAND); | ||||
|     device[F("model")] = F(WLED_PRODUCT_NAME); | ||||
|     device[F("sw_version")] = versionString; | ||||
|  | ||||
|     String temp; | ||||
|     serializeJson(doc, temp); | ||||
|     DEBUG_PRINTLN(t); | ||||
|     DEBUG_PRINTLN(temp); | ||||
|  | ||||
|     mqtt->publish(t.c_str(), 0, true, temp.c_str()); | ||||
|   } | ||||
|  | ||||
| public: | ||||
|   void setup() | ||||
|   { | ||||
|     if (i2c_scl<0 || i2c_sda<0) { enabled = false; return; } | ||||
|     sensorFound = lightMeter.begin(); | ||||
|     initDone = true; | ||||
|   } | ||||
|  | ||||
|   void loop() | ||||
|   { | ||||
|     if ((!enabled) || 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(); | ||||
| #ifndef WLED_DISABLE_MQTT | ||||
|       if (WLED_MQTT_CONNECTED) | ||||
|       { | ||||
|         if (!mqttInitialized) | ||||
|           { | ||||
|             _mqttInitialize(); | ||||
|             mqttInitialized = true; | ||||
|           } | ||||
|         mqtt->publish(mqttLuminanceTopic.c_str(), 0, true, String(lux).c_str()); | ||||
|         DEBUG_PRINTLN(F("Brightness: ") + String(lux) + F("lx")); | ||||
|       } | ||||
|       else | ||||
|       { | ||||
|         DEBUG_PRINTLN(F("Missing MQTT connection. Not publishing data")); | ||||
|       } | ||||
| #endif | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   inline float getIlluminance() { | ||||
|     return (float)lastLux; | ||||
|   } | ||||
|  | ||||
|   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 (!enabled) { | ||||
|       lux_json.add(F("disabled")); | ||||
|     } else if (!sensorFound) { | ||||
|         // if no sensor  | ||||
|         lux_json.add(F("BH1750 ")); | ||||
|         lux_json.add(F("Not Found")); | ||||
|     } else 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; | ||||
|     } else { | ||||
|       lux_json.add(lastLux); | ||||
|       lux_json.add(F(" lx")); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // (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)] = enabled; | ||||
|     top[FPSTR(_maxReadInterval)] = maxReadingInterval; | ||||
|     top[FPSTR(_minReadInterval)] = minReadingInterval; | ||||
|     top[FPSTR(_HomeAssistantDiscovery)] = HomeAssistantDiscovery; | ||||
|     top[FPSTR(_offset)] = offset; | ||||
|  | ||||
|     DEBUG_PRINTLN(F("BH1750 config saved.")); | ||||
|   } | ||||
|  | ||||
|   // 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_PRINT(F("BH1750")); | ||||
|       DEBUG_PRINTLN(F(": No config found. (Using defaults.)")); | ||||
|       return false; | ||||
|     } | ||||
|     bool configComplete = !top.isNull(); | ||||
|  | ||||
|     configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled, false); | ||||
|     configComplete &= getJsonValue(top[FPSTR(_maxReadInterval)], maxReadingInterval, 10000); //ms | ||||
|     configComplete &= getJsonValue(top[FPSTR(_minReadInterval)], minReadingInterval, 500); //ms | ||||
|     configComplete &= getJsonValue(top[FPSTR(_HomeAssistantDiscovery)], HomeAssistantDiscovery, false); | ||||
|     configComplete &= getJsonValue(top[FPSTR(_offset)], offset, 1); | ||||
|  | ||||
|     DEBUG_PRINT(FPSTR(_name)); | ||||
|     if (!initDone) { | ||||
|       DEBUG_PRINTLN(F(" config loaded.")); | ||||
|     } else { | ||||
|       DEBUG_PRINTLN(F(" config (re)loaded.")); | ||||
|     } | ||||
|  | ||||
|     return configComplete; | ||||
|      | ||||
|   } | ||||
|  | ||||
|   uint16_t getId() | ||||
|   { | ||||
|     return USERMOD_ID_BH1750; | ||||
|   } | ||||
|  | ||||
| }; | ||||
|  | ||||
| // 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::_HomeAssistantDiscovery[] PROGMEM = "HomeAssistantDiscoveryLux"; | ||||
| const char Usermod_BH1750::_offset[] PROGMEM = "offset-lx"; | ||||
| @@ -1,17 +1,15 @@ | ||||
| // force the compiler to show a warning to confirm that this file is included
 | ||||
| #warning **** Included USERMOD_BME280 version 2.0 **** | ||||
| 
 | ||||
| #ifndef WLED_ENABLE_MQTT | ||||
| #error "This user mod requires MQTT to be enabled." | ||||
| #endif | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "wled.h" | ||||
| #include <Arduino.h> | ||||
| #include <BME280I2C.h>               // BME280 sensor
 | ||||
| #include <EnvironmentCalculations.h> // BME280 extended measurements
 | ||||
| 
 | ||||
| #ifdef WLED_DISABLE_MQTT | ||||
| #error "This user mod requires MQTT to be enabled." | ||||
| #endif | ||||
| 
 | ||||
| class UsermodBME280 : public Usermod | ||||
| { | ||||
| private: | ||||
| @@ -241,7 +239,7 @@ public: | ||||
|         // from the UI and values read from sensor, then publish to broker
 | ||||
|         if (temperature != lastTemperature || PublishAlways) | ||||
|         { | ||||
|           publishMqtt("temperature", String(temperature, TemperatureDecimals).c_str()); | ||||
|           publishMqtt("temperature", String(temperature, (unsigned) TemperatureDecimals).c_str()); | ||||
|         } | ||||
| 
 | ||||
|         lastTemperature = temperature; // Update last sensor temperature for next loop
 | ||||
| @@ -254,17 +252,17 @@ public: | ||||
| 
 | ||||
|           if (humidity != lastHumidity || PublishAlways) | ||||
|           { | ||||
|             publishMqtt("humidity", String(humidity, HumidityDecimals).c_str()); | ||||
|             publishMqtt("humidity", String(humidity, (unsigned) HumidityDecimals).c_str()); | ||||
|           } | ||||
| 
 | ||||
|           if (heatIndex != lastHeatIndex || PublishAlways) | ||||
|           { | ||||
|             publishMqtt("heat_index", String(heatIndex, TemperatureDecimals).c_str()); | ||||
|             publishMqtt("heat_index", String(heatIndex, (unsigned) TemperatureDecimals).c_str()); | ||||
|           } | ||||
| 
 | ||||
|           if (dewPoint != lastDewPoint || PublishAlways) | ||||
|           { | ||||
|             publishMqtt("dew_point", String(dewPoint, TemperatureDecimals).c_str()); | ||||
|             publishMqtt("dew_point", String(dewPoint, (unsigned) TemperatureDecimals).c_str()); | ||||
|           } | ||||
| 
 | ||||
|           lastHumidity = humidity; | ||||
| @@ -281,7 +279,7 @@ public: | ||||
| 
 | ||||
|         if (pressure != lastPressure || PublishAlways) | ||||
|         { | ||||
|           publishMqtt("pressure", String(pressure, PressureDecimals).c_str()); | ||||
|           publishMqtt("pressure", String(pressure, (unsigned) PressureDecimals).c_str()); | ||||
|         } | ||||
| 
 | ||||
|         lastPressure = pressure; | ||||
| @@ -479,3 +477,7 @@ public: | ||||
| 
 | ||||
| const char UsermodBME280::_name[]                      PROGMEM = "BME280/BMP280"; | ||||
| const char UsermodBME280::_enabled[]                   PROGMEM = "enabled"; | ||||
| 
 | ||||
| 
 | ||||
| static UsermodBME280 bme280_v2; | ||||
| REGISTER_USERMOD(bme280_v2); | ||||
| @@ -22,7 +22,6 @@ Dependencies | ||||
| - Libraries | ||||
|   - `BME280@~3.0.0` (by [finitespace](https://github.com/finitespace/BME280)) | ||||
|   - `Wire` | ||||
|   - These must be added under `lib_deps` in your `platform.ini` (or `platform_override.ini`). | ||||
| - Data is published over MQTT - make sure you've enabled the MQTT sync interface. | ||||
| - This usermod also writes to serial (GPIO1 on ESP8266). Please make sure nothing else is listening to the serial TX pin or your board will get confused by log messages! | ||||
|  | ||||
| @@ -40,17 +39,11 @@ Methods also exist to read the read/calculated values from other WLED modules th | ||||
|  | ||||
| # Compiling | ||||
|  | ||||
| To enable, compile with `USERMOD_BME280` defined  (e.g. in `platformio_override.ini`) | ||||
| To enable, add `BME280_v2` to your `custom_usermods`  (e.g. in `platformio_override.ini`) | ||||
| ```ini | ||||
| [env:usermod_bme280_d1_mini] | ||||
| extends = env:d1_mini | ||||
| build_flags = | ||||
|   ${common.build_flags_esp8266} | ||||
|   -D USERMOD_BME280 | ||||
| lib_deps =  | ||||
|   ${esp8266.lib_deps} | ||||
|   BME280@~3.0.0 | ||||
|   Wire | ||||
| custom_usermods = ${env:d1_mini.custom_usermods} BME280_v2 | ||||
| ``` | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										7
									
								
								usermods/BME280_v2/library.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								usermods/BME280_v2/library.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| { | ||||
|   "name": "BME280_v2", | ||||
|   "build": { "libArchive": false }, | ||||
|   "dependencies": { | ||||
|     "finitespace/BME280":"~3.0.0" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										1114
									
								
								usermods/BME68X_v2/BME68X_v2.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1114
									
								
								usermods/BME68X_v2/BME68X_v2.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,65 +1,70 @@ | ||||
| # Usermod BME68X | ||||
| This usermod was developed for a BME680/BME68X sensor. The BME68X is not compatible with the BME280/BMP280 chip. It has its own library. The original 'BSEC Software Library' from Bosch was used to develop the code. The measured values are displayed on the WLED info page.  | ||||
|  | ||||
| This usermod was developed for a BME680/BME68X sensor. The BME68X is not compatible with the BME280/BMP280 chip. It has its own library. The original 'BSEC Software Library' from Bosch was used to develop the code. The measured values are displayed on the WLED info page. | ||||
|  | ||||
| <p align="center"><img src="pics/pic1.png" style="width:60%;"></p> | ||||
|  | ||||
| In addition, the values are published on MQTT if this is active. The topic used for this is: 'wled/[MQTT Client ID]'. The Client ID is set in the WLED MQTT settings. | ||||
|  | ||||
| <p align="center"><img src="pics/pic2.png"></p> | ||||
|  | ||||
| If you use HomeAssistance discovery, the device tree for HomeAssistance is created.  This is published under the topic 'homeassistant/sensor/[MQTT Client ID]' via MQTT. | ||||
|  | ||||
| <p align="center"><img src="pics/pic3.png"></p> | ||||
|  | ||||
| A device with the following sensors appears in HomeAssistant. Please note that MQTT must be activated in HomeAssistant. | ||||
|  | ||||
| <p align="center"><img src="pics/pic4.png" style="width:60%;"></p> | ||||
|  | ||||
|  | ||||
| ## Features | ||||
|  | ||||
| Raw sensor types | ||||
|  | ||||
| 	Sensor		Accuracy	Scale		Range | ||||
|  	-------------------------------------------------------------------------------------------------- | ||||
| 	Temperature	+/- 1.0		°C/°F		-40 to 85 °C | ||||
| 	Humidity	+/- 3 		%		0 to 100 % | ||||
| 	Pressure	+/- 1 		hPa		300 to 1100 hPa | ||||
| 	Gas Resistance			Ohm | ||||
| Sensor		Accuracy	Scale		Range | ||||
| ----------------------------- | ||||
|  | ||||
| Temperature	+/- 1.0		°C/°F		-40 to 85 °C | ||||
| Humidity	+/- 3 		%		0 to 100 % | ||||
| Pressure	+/- 1 		hPa		300 to 1100 hPa | ||||
| Gas Resistance			Ohm | ||||
| The BSEC Library calculates the following values via the gas resistance | ||||
|  | ||||
| 	Sensor		Accuracy	Scale		Range | ||||
|  	-------------------------------------------------------------------------------------------------- | ||||
| 	IAQ 						value between 0 and 500 | ||||
| 	Static IAQ 					same as IAQ but for permanently installed devices | ||||
| 	CO2 				PPM | ||||
| 	VOC 				PPM | ||||
| 	Gas-Percentage 			% | ||||
|  | ||||
| Sensor		Accuracy	Scale		Range | ||||
| ----------------------------- | ||||
|  | ||||
| IAQ 						value between 0 and 500 | ||||
| Static IAQ 					same as IAQ but for permanently installed devices | ||||
| CO2 				PPM | ||||
| VOC 				PPM | ||||
| Gas-Percentage 			% | ||||
| In addition the usermod calculates | ||||
|  | ||||
| 	Sensor		Accuracy	Scale		Range | ||||
|  	-------------------------------------------------------------------------------------------------- | ||||
| 	Absolute humidity	 	g/m³ | ||||
| 	Dew point 			°C/°F | ||||
| Sensor		Accuracy	Scale		Range | ||||
| ----------------------------- | ||||
|  | ||||
| Absolute humidity	 	g/m³ | ||||
| Dew point 			°C/°F | ||||
|  | ||||
| ### IAQ (Indoor Air Quality) | ||||
| The IAQ is divided into the following value groups.  | ||||
|  | ||||
| The IAQ is divided into the following value groups. | ||||
|  | ||||
| <p align="center"><img src="pics/pic5.png"></p> | ||||
|  | ||||
| For more detailed information, please consult the enclosed Bosch product description (BME680.pdf). | ||||
|  | ||||
|  | ||||
| ## Calibration of the device | ||||
|  | ||||
| The gas sensor of the BME68X must be calibrated. This differs from the BME280, which does not require any calibration.  | ||||
| The gas sensor of the BME68X must be calibrated. This differs from the BME280, which does not require any calibration. | ||||
| There is a range of additional information for this, which the driver also provides. These values can be found in HomeAssistant under Diagnostics. | ||||
|  | ||||
| - **STABILIZATION_STATUS**: Gas sensor stabilization status [boolean] Indicates initial stabilization status of the gas sensor element: stabilization is ongoing (0) or stabilization is finished (1). | ||||
| - **RUN_IN_STATUS**: 	Gas sensor run-in status [boolean] Indicates power-on stabilization status of the gas sensor element: stabilization is ongoing (0) or stabilization is finished (1) | ||||
|  | ||||
| Furthermore, all GAS based values have their own accuracy value. These have the following meaning:  | ||||
| Furthermore, all GAS based values have their own accuracy value. These have the following meaning: | ||||
|  | ||||
| - **Accuracy = 0** 	means the sensor is being stabilized (this can take a while on the first run)  | ||||
| - **Accuracy = 1**	means that the previous measured values show too few differences and cannot be used for calibration. If the sensor is at accuracy 1 for too long, you must ensure that the ambient air is chaning. Opening the windows is fine. Or sometimes it is sufficient to breathe on the sensor for approx. 5 minutes.  | ||||
| - **Accuracy = 0** 	means the sensor is being stabilized (this can take a while on the first run) | ||||
| - **Accuracy = 1**	means that the previous measured values show too few differences and cannot be used for calibration. If the sensor is at accuracy 1 for too long, you must ensure that the ambient air is chaning. Opening the windows is fine. Or sometimes it is sufficient to breathe on the sensor for approx. 5 minutes. | ||||
| - **Accuracy = 2**	means the sensor is currently calibrating. | ||||
| - **Accuracy = 3**	means that the sensor has been successfully calibrated. Once accuracy 3 is reached, the calibration data is automatically written to the file system. This calibration data will be used again at the next start and will speed up the calibration. | ||||
|  | ||||
| @@ -67,28 +72,29 @@ The IAQ index is therefore only meaningful if IAQ Accuracy = 3. In addition to t | ||||
|  | ||||
| Reasonably reliable values are therefore only achieved when accuracy displays the value 3. | ||||
|  | ||||
|  | ||||
|  | ||||
| ## Settings | ||||
| The settings of the usermods are set in the usermod section of wled.  | ||||
|  | ||||
| The settings of the usermods are set in the usermod section of wled. | ||||
|  | ||||
| <p align="center"><img src="pics/pic6.png"></p> | ||||
|  | ||||
| The possible settings are | ||||
|  | ||||
| - **Enable:**			Enables / disables the usermod | ||||
| - **I2C address:**		I2C address of the sensor. You can choose between 0X77 & 0X76. The default is 0x77. | ||||
| - **Interval:**			Specifies the interval of seconds at which the usermod should be executed. The default is every second.  | ||||
| - **Pub Chages Only:**		If this item is active, the values are only published if they have changed since the last publication.  | ||||
| - **Pub Accuracy:**		The Accuracy values associated with the gas values are also published.  | ||||
| - **Pub Calib State:**		If this item is active, STABILIZATION_STATUS& RUN_IN_STATUS are also published.  | ||||
| - **Interval:**			Specifies the interval of seconds at which the usermod should be executed. The default is every second. | ||||
| - **Pub Chages Only:**		If this item is active, the values are only published if they have changed since the last publication. | ||||
| - **Pub Accuracy:**		The Accuracy values associated with the gas values are also published. | ||||
| - **Pub Calib State:**		If this item is active, STABILIZATION_STATUS& RUN_IN_STATUS are also published. | ||||
| - **Temp Scale:**		Here you can choose between °C and °F. | ||||
| - **Temp Offset:**		The temperature offset is always set in °C. It must be converted for Fahrenheit.  | ||||
| - **HA Discovery:**		If this item is active, the HomeAssistant sensor tree is created.  | ||||
| - **Temp Offset:**		The temperature offset is always set in °C. It must be converted for Fahrenheit. | ||||
| - **HA Discovery:**		If this item is active, the HomeAssistant sensor tree is created. | ||||
| - **Pause While WLED Active:**	If WLED has many LEDs to calculate, the computing power may no longer be sufficient to calculate the LEDs and read the sensor data. The LEDs then hang for a few microseconds, which can be seen. If this point is active, no sensor data is fetched as long as WLED is running. | ||||
| - **Del Calibration Hist:**	If a check mark is set here, the calibration file saved in the file system is deleted when the settings are saved.  | ||||
| - **Del Calibration Hist:**	If a check mark is set here, the calibration file saved in the file system is deleted when the settings are saved. | ||||
|  | ||||
| ### Sensors | ||||
| Applies to all sensors. The number of decimal places is set here. If the sensor is set to -1, it will no longer be published. In addition, the IAQ values can be activated here in verbal form.  | ||||
|  | ||||
| Applies to all sensors. The number of decimal places is set here. If the sensor is set to -1, it will no longer be published. In addition, the IAQ values can be activated here in verbal form. | ||||
|  | ||||
| It is recommended to use the Static IAQ for the IAQ values. This is recommended by Bosch for statically placed devices. | ||||
|  | ||||
| @@ -99,8 +105,9 @@ Data is published over MQTT - make sure you've enabled the MQTT sync interface. | ||||
| In addition to outputting via MQTT, you can read the values from the Info Screen on the dashboard page of the device's web interface. | ||||
|  | ||||
| Methods also exist to read the read/calculated values from other WLED modules through code. | ||||
|  | ||||
| - getTemperature();	The scale °C/°F is depended to the settings | ||||
| - getHumidity();	 | ||||
| - getHumidity(); | ||||
| - getPressure(); | ||||
| - getGasResistance(); | ||||
| - getAbsoluteHumidity(); | ||||
| @@ -118,32 +125,36 @@ Methods also exist to read the read/calculated values from other WLED modules th | ||||
| - getStabStatus(); | ||||
| - getRunInStatus(); | ||||
|  | ||||
| ## Compilation | ||||
|  | ||||
| ## Compiling | ||||
| To enable, compile with `BME68X` in `custom_usermods` (e.g. in `platformio_override.ini`) | ||||
|  | ||||
| To enable, compile with `USERMOD_BME68X` defined (e.g. in `platformio_override.ini`) and add the `BSEC Software Library` to the lib_deps. | ||||
| Example: | ||||
|  | ||||
| ``` | ||||
| [env:esp32-BME680] | ||||
| board = 		esp32dev | ||||
| platform = 		${esp32.platform} | ||||
| platform_packages = 	${esp32.platform_packages} | ||||
| lib_deps = 		${esp32.lib_deps} | ||||
|            		boschsensortec/BSEC Software Library @ ^1.8.1492      	; USERMOD: BME680                                           | ||||
| build_unflags = 	${common.build_unflags} | ||||
| build_flags = 		${common.build_flags_esp32}  | ||||
|               		-D USERMOD_BME68X                      			; USERMOD: BME680 | ||||
| ```[env:esp32_mySpecial] | ||||
| extends = env:esp32dev | ||||
| custom_usermods = ${env:esp32dev.custom_usermods} BME68X | ||||
| ``` | ||||
|  | ||||
| ## Revision History | ||||
|  | ||||
| ### Version 1.0.0 | ||||
|  | ||||
| - First version of the BME68X_v user module | ||||
|  | ||||
| ### Version 1.0.1 | ||||
|  | ||||
| - Rebased to WELD Version 0.15 | ||||
| - Reworked some default settings | ||||
| - A problem with the default settings has been fixed | ||||
|  | ||||
| ### Version 1.0.2 | ||||
|  | ||||
| * Rebased to WELD Version 0.16 | ||||
| * Fixed: Solved compilation problems related to some macro naming interferences. | ||||
|  | ||||
| ## Known problems | ||||
|  | ||||
| - MQTT goes online at device start. Shortly afterwards it goes offline and takes quite a while until it goes online again. The problem does not come from this user module, but from the WLED core. | ||||
| - If you save the settings often, WLED can get stuck. | ||||
| - If many LEDS are connected to WLED, reading the sensor can cause a small but noticeable hang. The "Pause While WLED Active" option was introduced as a workaround. | ||||
|   | ||||
							
								
								
									
										7
									
								
								usermods/BME68X_v2/library.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								usermods/BME68X_v2/library.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| { | ||||
|   "name": "BME68X", | ||||
|   "build": { "libArchive": false }, | ||||
|   "dependencies": { | ||||
|     "boschsensortec/BSEC Software Library":"^1.8.1492" | ||||
|   } | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,5 +1,3 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "wled.h" | ||||
| #include "battery_defaults.h" | ||||
| #include "UMBattery.h" | ||||
| @@ -200,7 +198,7 @@ class UsermodBattery : public Usermod | ||||
|         bool success = false; | ||||
|         DEBUG_PRINTLN(F("Allocating battery pin...")); | ||||
|         if (batteryPin >= 0 && digitalPinToAnalogChannel(batteryPin) >= 0)  | ||||
|           if (pinManager.allocatePin(batteryPin, false, PinOwner::UM_Battery)) { | ||||
|           if (PinManager::allocatePin(batteryPin, false, PinOwner::UM_Battery)) { | ||||
|             DEBUG_PRINTLN(F("Battery pin allocation succeeded.")); | ||||
|             success = true; | ||||
|           } | ||||
| @@ -478,29 +476,29 @@ class UsermodBattery : public Usermod | ||||
|     void appendConfigData() | ||||
|     { | ||||
|       // Total: 462 Bytes
 | ||||
|       oappend(SET_F("td=addDropdown('Battery','type');"));              // 34 Bytes
 | ||||
|       oappend(SET_F("addOption(td,'Unkown','0');"));                    // 28 Bytes
 | ||||
|       oappend(SET_F("addOption(td,'LiPo','1');"));                      // 26 Bytes
 | ||||
|       oappend(SET_F("addOption(td,'LiOn','2');"));                      // 26 Bytes
 | ||||
|       oappend(SET_F("addInfo('Battery:type',1,'<small style=\"color:orange\">requires reboot</small>');")); // 81 Bytes
 | ||||
|       oappend(SET_F("addInfo('Battery:min-voltage',1,'v');"));          // 38 Bytes
 | ||||
|       oappend(SET_F("addInfo('Battery:max-voltage',1,'v');"));          // 38 Bytes
 | ||||
|       oappend(SET_F("addInfo('Battery:interval',1,'ms');"));            // 36 Bytes
 | ||||
|       oappend(SET_F("addInfo('Battery:HA-discovery',1,'');"));          // 38 Bytes
 | ||||
|       oappend(SET_F("addInfo('Battery:auto-off:threshold',1,'%');"));   // 45 Bytes
 | ||||
|       oappend(SET_F("addInfo('Battery:indicator:threshold',1,'%');"));  // 46 Bytes
 | ||||
|       oappend(SET_F("addInfo('Battery:indicator:duration',1,'s');"));   // 45 Bytes
 | ||||
|       oappend(F("td=addDropdown('Battery','type');"));              // 34 Bytes
 | ||||
|       oappend(F("addOption(td,'Unkown','0');"));                    // 28 Bytes
 | ||||
|       oappend(F("addOption(td,'LiPo','1');"));                      // 26 Bytes
 | ||||
|       oappend(F("addOption(td,'LiOn','2');"));                      // 26 Bytes
 | ||||
|       oappend(F("addInfo('Battery:type',1,'<small style=\"color:orange\">requires reboot</small>');")); // 81 Bytes
 | ||||
|       oappend(F("addInfo('Battery:min-voltage',1,'v');"));          // 38 Bytes
 | ||||
|       oappend(F("addInfo('Battery:max-voltage',1,'v');"));          // 38 Bytes
 | ||||
|       oappend(F("addInfo('Battery:interval',1,'ms');"));            // 36 Bytes
 | ||||
|       oappend(F("addInfo('Battery:HA-discovery',1,'');"));          // 38 Bytes
 | ||||
|       oappend(F("addInfo('Battery:auto-off:threshold',1,'%');"));   // 45 Bytes
 | ||||
|       oappend(F("addInfo('Battery:indicator:threshold',1,'%');"));  // 46 Bytes
 | ||||
|       oappend(F("addInfo('Battery:indicator:duration',1,'s');"));   // 45 Bytes
 | ||||
|        | ||||
|       // this option list would exeed the oappend() buffer
 | ||||
|       // a list of all presets to select one from
 | ||||
|       // oappend(SET_F("bd=addDropdown('Battery:low-power-indicator', 'preset');"));
 | ||||
|       // the loop generates: oappend(SET_F("addOption(bd, 'preset name', preset id);"));
 | ||||
|       // oappend(F("bd=addDropdown('Battery:low-power-indicator', 'preset');"));
 | ||||
|       // the loop generates: oappend(F("addOption(bd, 'preset name', preset id);"));
 | ||||
|       // for(int8_t i=1; i < 42; i++) {
 | ||||
|       //   oappend(SET_F("addOption(bd, 'Preset#"));
 | ||||
|       //   oappend(F("addOption(bd, 'Preset#"));
 | ||||
|       //   oappendi(i);
 | ||||
|       //   oappend(SET_F("',"));
 | ||||
|       //   oappend(F("',"));
 | ||||
|       //   oappendi(i);
 | ||||
|       //   oappend(SET_F(");"));
 | ||||
|       //   oappend(F(");"));
 | ||||
|       // }
 | ||||
|     } | ||||
| 
 | ||||
| @@ -561,7 +559,7 @@ class UsermodBattery : public Usermod | ||||
|           if (newBatteryPin != batteryPin)  | ||||
|           { | ||||
|             // deallocate pin
 | ||||
|             pinManager.deallocatePin(batteryPin, PinOwner::UM_Battery); | ||||
|             PinManager::deallocatePin(batteryPin, PinOwner::UM_Battery); | ||||
|             batteryPin = newBatteryPin; | ||||
|             // initialise
 | ||||
|             setup(); | ||||
| @@ -857,3 +855,7 @@ const char UsermodBattery::_preset[]        PROGMEM = "preset"; | ||||
| const char UsermodBattery::_duration[]      PROGMEM = "duration"; | ||||
| const char UsermodBattery::_init[]          PROGMEM = "init"; | ||||
| const char UsermodBattery::_haDiscovery[]   PROGMEM = "HA-discovery"; | ||||
| 
 | ||||
| 
 | ||||
| static UsermodBattery battery; | ||||
| REGISTER_USERMOD(battery); | ||||
							
								
								
									
										4
									
								
								usermods/Battery/library.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								usermods/Battery/library.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| { | ||||
|   "name": "Battery", | ||||
|   "build": { "libArchive": false } | ||||
| } | ||||
| @@ -23,9 +23,7 @@ Enables battery level monitoring of your project. | ||||
|  | ||||
| ## 🎈 Installation | ||||
|  | ||||
| | **Option 1** | **Option 2** | | ||||
| |--------------|--------------| | ||||
| | In `wled00/my_config.h`<br>Add the line: `#define USERMOD_BATTERY`<br><br>[Example: my_config.h](assets/installation_my_config_h.png) | In `platformio_override.ini` (or `platformio.ini`)<br>Under: `build_flags =`, add the line: `-D USERMOD_BATTERY`<br><br>[Example: platformio_override.ini](assets/installation_platformio_override_ini.png) | | ||||
| In `platformio_override.ini` (or `platformio.ini`)<br>Under: `custom_usermods =`, add the line: `Battery`<br><br>[Example: platformio_override.ini](assets/installation_platformio_override_ini.png) | | ||||
|  | ||||
| <br><br> | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,3 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "wled.h" | ||||
| 
 | ||||
| class UsermodCronixie : public Usermod { | ||||
| @@ -249,7 +247,7 @@ class UsermodCronixie : public Usermod { | ||||
|          | ||||
|         if (backlight && _digitOut[i] <11) | ||||
|         { | ||||
|           uint32_t col = gamma32(strip.getSegment(0).colors[1]); | ||||
|           uint32_t col = strip.getSegment(0).colors[1]; | ||||
|           for (uint16_t j=o; j< o+10; j++) { | ||||
|             if (j != excl) strip.setPixelColor(j, col); | ||||
|           } | ||||
| @@ -299,4 +297,7 @@ class UsermodCronixie : public Usermod { | ||||
|     { | ||||
|       return USERMOD_ID_CRONIXIE; | ||||
|     } | ||||
| }; | ||||
| }; | ||||
| 
 | ||||
| static UsermodCronixie cronixie; | ||||
| REGISTER_USERMOD(cronixie); | ||||
							
								
								
									
										4
									
								
								usermods/Cronixie/library.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								usermods/Cronixie/library.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| { | ||||
|   "name": "Cronixie", | ||||
|   "build": { "libArchive": false } | ||||
| } | ||||
| @@ -4,5 +4,5 @@ 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.   | ||||
| Compile and upload after adding `Cronixie` to `custom_usermods` of your PlatformIO environment.   | ||||
| Make sure the Auto Brightness Limiter is enabled at 420mA (!) and configure 60 WS281x LEDs. | ||||
| @@ -1,7 +1,5 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "wled.h" | ||||
| #ifndef WLED_ENABLE_MQTT | ||||
| #ifdef WLED_DISABLE_MQTT | ||||
| #error "This user mod requires MQTT to be enabled." | ||||
| #endif | ||||
| 
 | ||||
| @@ -245,3 +243,7 @@ class UsermodDHT : public Usermod { | ||||
|     } | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| static UsermodDHT dht; | ||||
| REGISTER_USERMOD(dht); | ||||
							
								
								
									
										7
									
								
								usermods/DHT/library.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								usermods/DHT/library.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| { | ||||
|   "name": "DHT", | ||||
|   "build": { "libArchive": false}, | ||||
|   "dependencies": { | ||||
|     "DHT_nonblocking":"https://github.com/alwynallan/DHT_nonblocking" | ||||
|   } | ||||
| } | ||||
| @@ -1,6 +1,5 @@ | ||||
| ; 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 | ||||
| @@ -11,13 +10,11 @@ | ||||
|  | ||||
| [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:d1_mini.lib_deps} | ||||
|     https://github.com/alwynallan/DHT_nonblocking | ||||
| custom_usermods = ${env:d1_mini.custom_usermods} DHT | ||||
| build_flags = ${env:d1_mini.build_flags} -D USERMOD_DHT_CELSIUS | ||||
|  | ||||
| [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 | ||||
| custom_usermods = ${env:custom32_LEDPIN_16.custom_usermods} DHT | ||||
| build_flags = ${env:custom32_LEDPIN_16.build_flags} -D USERMOD_DHT_CELSIUS -D USERMOD_DHT_STATS | ||||
|  | ||||
|   | ||||
| @@ -15,7 +15,6 @@ Copy the example `platformio_override.ini` to the root directory.  This file sho | ||||
|  | ||||
| ### Define Your Options | ||||
|  | ||||
| * `USERMOD_DHT`                      - define this to include this user mod 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 Celsius, otherwise Fahrenheit will be reported | ||||
|   | ||||
							
								
								
									
										5
									
								
								usermods/EXAMPLE/library.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								usermods/EXAMPLE/library.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| { | ||||
|   "name": "EXAMPLE", | ||||
|   "build": { "libArchive": false }, | ||||
|   "dependencies": {} | ||||
| } | ||||
| @@ -4,7 +4,6 @@ In this usermod file you can find the documentation on how to take advantage of | ||||
| 
 | ||||
| ## Installation  | ||||
| 
 | ||||
| Copy `usermod_v2_example.h` to the wled00 directory.   | ||||
| Uncomment the corresponding lines in `usermods_list.cpp` and compile!   | ||||
| Add `EXAMPLE` to `custom_usermods` in your PlatformIO environment and compile! | ||||
| _(You shouldn't need to actually install this, it does nothing useful)_ | ||||
| 
 | ||||
| @@ -1,10 +1,8 @@ | ||||
| #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
 | ||||
|  * See: https://github.com/wled-dev/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. | ||||
| @@ -71,7 +69,7 @@ class MyExampleUsermod : public Usermod { | ||||
|     //   #endif
 | ||||
|     //  in setup()
 | ||||
|     //   #ifdef USERMOD_EXAMPLE
 | ||||
|     //   UM = (MyExampleUsermod*) usermods.lookup(USERMOD_ID_EXAMPLE);
 | ||||
|     //   UM = (MyExampleUsermod*) UsermodManager::lookup(USERMOD_ID_EXAMPLE);
 | ||||
|     //   #endif
 | ||||
|     //  somewhere in loop() or other member method
 | ||||
|     //   #ifdef USERMOD_EXAMPLE
 | ||||
| @@ -287,11 +285,11 @@ class MyExampleUsermod : public Usermod { | ||||
|      */ | ||||
|     void appendConfigData() override | ||||
|     { | ||||
|       oappend(SET_F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F(":great")); oappend(SET_F("',1,'<i>(this is a great config value)</i>');")); | ||||
|       oappend(SET_F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F(":testString")); oappend(SET_F("',1,'enter any string you want');")); | ||||
|       oappend(SET_F("dd=addDropdown('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F("','testInt');")); | ||||
|       oappend(SET_F("addOption(dd,'Nothing',0);")); | ||||
|       oappend(SET_F("addOption(dd,'Everything',42);")); | ||||
|       oappend(F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(F(":great")); oappend(F("',1,'<i>(this is a great config value)</i>');")); | ||||
|       oappend(F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(F(":testString")); oappend(F("',1,'enter any string you want');")); | ||||
|       oappend(F("dd=addDropdown('")); oappend(String(FPSTR(_name)).c_str()); oappend(F("','testInt');")); | ||||
|       oappend(F("addOption(dd,'Nothing',0);")); | ||||
|       oappend(F("addOption(dd,'Everything',42);")); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| @@ -404,3 +402,6 @@ void MyExampleUsermod::publishMqtt(const char* state, bool retain) | ||||
|   } | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| static MyExampleUsermod example_usermod; | ||||
| REGISTER_USERMOD(example_usermod); | ||||
| @@ -1,4 +1,3 @@ | ||||
| #pragma once | ||||
| #include "TFTs.h" | ||||
| #include "wled.h" | ||||
| 
 | ||||
| @@ -156,3 +155,7 @@ class ElekstubeIPSUsermod : public Usermod { | ||||
| const char ElekstubeIPSUsermod::_name[]         PROGMEM = "EleksTubeIPS"; | ||||
| const char ElekstubeIPSUsermod::_tubeSeg[]      PROGMEM = "tubeSegment"; | ||||
| const char ElekstubeIPSUsermod::_digitOffset[]  PROGMEM = "digitOffset"; | ||||
| 
 | ||||
| 
 | ||||
| static ElekstubeIPSUsermod elekstube_ips; | ||||
| REGISTER_USERMOD(elekstube_ips); | ||||
							
								
								
									
										8
									
								
								usermods/EleksTube_IPS/library.json.disabled
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								usermods/EleksTube_IPS/library.json.disabled
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| { | ||||
|   "name:": "EleksTube_IPS", | ||||
|   "build": { "libArchive": false }, | ||||
|   "dependencies": { | ||||
|     "TFT_eSPI" : "2.5.33" | ||||
|   }  | ||||
| } | ||||
| # Seems to add 300kb to the RAM requirement??? | ||||
| @@ -1,11 +1,12 @@ | ||||
| #ifndef WLED_ENABLE_MQTT | ||||
| #error "This user mod requires MQTT to be enabled." | ||||
| #endif | ||||
|  | ||||
| #include "wled.h" | ||||
| #include <Arduino.h> | ||||
| #include <U8x8lib.h> // from https://github.com/olikraus/u8g2/ | ||||
| #include <DallasTemperature.h> //Dallastemperature sensor | ||||
|  | ||||
| #ifdef WLED_DISABLE_MQTT | ||||
| #error "This user mod requires MQTT to be enabled." | ||||
| #endif | ||||
|  | ||||
| //The SCL and SDA pins are defined here.  | ||||
| //Lolin32 boards use SCL=5 SDA=4  | ||||
| #define U8X8_PIN_SCL 5 | ||||
|   | ||||
| @@ -1,13 +1,13 @@ | ||||
| #ifndef WLED_ENABLE_MQTT | ||||
| #error "This user mod requires MQTT to be enabled." | ||||
| #endif | ||||
|  | ||||
| #include "wled.h" | ||||
| #include <Arduino.h> | ||||
| #include <U8x8lib.h> // from https://github.com/olikraus/u8g2/ | ||||
| #include <Wire.h> | ||||
| #include <BME280I2C.h> //BME280 sensor | ||||
|  | ||||
| #ifdef WLED_DISABLE_MQTT | ||||
| #error "This user mod requires MQTT to be enabled." | ||||
| #endif | ||||
|  | ||||
| void UpdateBME280Data(); | ||||
|  | ||||
| #define Celsius // Show temperature measurement in Celsius otherwise is in Fahrenheit  | ||||
|   | ||||
							
								
								
									
										4
									
								
								usermods/Fix_unreachable_netservices_v2/library.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								usermods/Fix_unreachable_netservices_v2/library.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| { | ||||
|   "name": "Fix_unreachable_netservices_v2", | ||||
|   "platforms": ["espressif8266"] | ||||
| } | ||||
| @@ -30,41 +30,6 @@ The usermod supports the following state changes: | ||||
|  | ||||
| ## 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()); | ||||
|  | ||||
| } | ||||
| ``` | ||||
| 1. Add `Fix_unreachable_netservices` to `custom_usermods` in your PlatformIO environment. | ||||
|  | ||||
| Hopefully I can help someone with that - @gegu | ||||
|   | ||||
| @@ -1,12 +1,4 @@ | ||||
| #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> | ||||
| @@ -16,7 +8,7 @@ class FixUnreachableNetServices : public Usermod | ||||
|  * 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
 | ||||
|  * See: https://github.com/wled-dev/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. | ||||
| @@ -168,4 +160,11 @@ Delay <input type=\"number\" min=\"5\" max=\"300\" value=\""; | ||||
|     return USERMOD_ID_FIXNETSERVICES; | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| static FixUnreachableNetServices fix_unreachable_net_services; | ||||
| REGISTER_USERMOD(fix_unreachable_net_services); | ||||
| 
 | ||||
| #else /* !ESP8266 */ | ||||
| #warning "Usermod FixUnreachableNetServices works only with ESP8266 builds" | ||||
| #endif | ||||
| 
 | ||||
| @@ -1,5 +1,3 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "wled.h" | ||||
| #include <INA226_WE.h> | ||||
| 
 | ||||
| @@ -210,12 +208,6 @@ private: | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     ~UsermodINA226() | ||||
|     { | ||||
|         delete _ina226; | ||||
|         _ina226 = nullptr; | ||||
|     } | ||||
| 
 | ||||
| #ifndef WLED_DISABLE_MQTT | ||||
|     void mqttInitialize() | ||||
|     { | ||||
| @@ -551,6 +543,17 @@ public: | ||||
|         _initDone = true; | ||||
|         return configComplete; | ||||
|     } | ||||
| 
 | ||||
|     ~UsermodINA226() | ||||
|     { | ||||
|         delete _ina226; | ||||
|         _ina226 = nullptr; | ||||
|     } | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| const char UsermodINA226::_name[] PROGMEM = "INA226"; | ||||
| 
 | ||||
| 
 | ||||
| static UsermodINA226 ina226_v2; | ||||
| REGISTER_USERMOD(ina226_v2); | ||||
| @@ -22,13 +22,6 @@ The following settings can be configured in the Usermod Menu: | ||||
| - **MqttPublishAlways**: Publish always, regardless if there is a change. | ||||
| - **MqttHomeAssistantDiscovery**: Enable Home Assistant discovery. | ||||
|  | ||||
| ## Dependencies | ||||
|  | ||||
| These must be added under `lib_deps` in your `platform.ini` (or `platform_override.ini`). | ||||
|  | ||||
| - Libraries | ||||
|   - `wollewald/INA226_WE@~1.2.9` (by [wollewald](https://registry.platformio.org/libraries/wollewald/INA226_WE)) | ||||
|   - `Wire` | ||||
|  | ||||
| ## Understanding Samples and Conversion Times | ||||
|  | ||||
| @@ -62,16 +55,12 @@ For detailed programming information and register configurations, refer to the [ | ||||
|  | ||||
| ## Compiling | ||||
|  | ||||
| To enable, compile with `USERMOD_INA226` defined (e.g. in `platformio_override.ini`). | ||||
| To enable, compile with `INA226` in `custom_usermods` (e.g. in `platformio_override.ini`). | ||||
|  | ||||
| ```ini | ||||
| [env:ina226_example] | ||||
| extends = env:esp32dev | ||||
| build_flags = | ||||
|   ${common.build_flags} ${esp32.build_flags} | ||||
|   -D USERMOD_INA226 | ||||
| custom_usermods = ${env:esp32dev.custom_usermods} INA226 | ||||
| build_flags = ${env:esp32dev.build_flags} | ||||
|   ; -D USERMOD_INA226_DEBUG ; -- add a debug status to the info modal | ||||
| lib_deps =  | ||||
|   ${esp32.lib_deps} | ||||
|   wollewald/INA226_WE@~1.2.9 | ||||
| ``` | ||||
							
								
								
									
										7
									
								
								usermods/INA226_v2/library.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								usermods/INA226_v2/library.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| { | ||||
|   "name": "INA226_v2", | ||||
|   "build": { "libArchive": false }, | ||||
|   "dependencies": { | ||||
|     "wollewald/INA226_WE":"~1.2.9" | ||||
|   } | ||||
| } | ||||
| @@ -1,9 +1,6 @@ | ||||
| [env:ina226_example] | ||||
| extends = env:esp32dev | ||||
| custom_usermods = ${env:esp32dev.custom_usermods} INA226_v2 | ||||
| build_flags = | ||||
|   ${common.build_flags} ${esp32.build_flags} | ||||
|   -D USERMOD_INA226 | ||||
|   ${env:esp32dev.build_flags} | ||||
|   ; -D USERMOD_INA226_DEBUG ; -- add a debug status to the info modal | ||||
| lib_deps =  | ||||
|   ${esp32.lib_deps} | ||||
|   wollewald/INA226_WE@~1.2.9 | ||||
| @@ -1,5 +1,3 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "wled.h" | ||||
| 
 | ||||
| class InternalTemperatureUsermod : public Usermod | ||||
| @@ -50,7 +48,7 @@ public: | ||||
| #else                                    // ESP32 ESP32S3 and ESP32C3
 | ||||
|     temperature = roundf(temperatureRead() * 10) / 10; | ||||
| #endif | ||||
| 
 | ||||
|  if(presetToActivate != 0){ | ||||
|     // Check if temperature has exceeded the activation threshold
 | ||||
|     if (temperature >= activationThreshold) { | ||||
|       // Update the state flag if not already set
 | ||||
| @@ -58,7 +56,7 @@ public: | ||||
|         isAboveThreshold = true; | ||||
|         } | ||||
|       // Check if a 'high temperature' preset is configured and it's not already active
 | ||||
|       if (presetToActivate != 0 && currentPreset != presetToActivate) { | ||||
|       if (currentPreset != presetToActivate) { | ||||
|         // If a playlist is active, store it for reactivation later
 | ||||
|         if (currentPlaylist > 0) { | ||||
|           previousPlaylist = currentPlaylist; | ||||
| @@ -101,6 +99,7 @@ public: | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|  } | ||||
| 
 | ||||
| #ifndef WLED_DISABLE_MQTT | ||||
|     if (WLED_MQTT_CONNECTED) | ||||
| @@ -149,11 +148,11 @@ public: | ||||
|     void appendConfigData() | ||||
|     { | ||||
|     // Display 'ms' next to the 'Loop Interval' setting
 | ||||
|     oappend(SET_F("addInfo('Internal Temperature:Loop Interval', 1, 'ms');")); | ||||
|     oappend(F("addInfo('Internal Temperature:Loop Interval', 1, 'ms');")); | ||||
|     // Display '°C' next to the 'Activation Threshold' setting
 | ||||
|     oappend(SET_F("addInfo('Internal Temperature:Activation Threshold', 1, '°C');")); | ||||
|     oappend(F("addInfo('Internal Temperature:Activation Threshold', 1, '°C');")); | ||||
|     // Display '0 = Disabled' next to the 'Preset To Activate' setting
 | ||||
|     oappend(SET_F("addInfo('Internal Temperature:Preset To Activate', 1, '0 = unused');")); | ||||
|     oappend(F("addInfo('Internal Temperature:Preset To Activate', 1, '0 = unused');")); | ||||
|     } | ||||
| 
 | ||||
|   bool readFromConfig(JsonObject &root) | ||||
| @@ -192,4 +191,7 @@ void InternalTemperatureUsermod::publishMqtt(const char *state, bool retain) | ||||
|     mqtt->publish(subuf, 0, retain, state); | ||||
|   } | ||||
| #endif | ||||
| } | ||||
| } | ||||
| 
 | ||||
| static InternalTemperatureUsermod internal_temperature_v2; | ||||
| REGISTER_USERMOD(internal_temperature_v2); | ||||
							
								
								
									
										4
									
								
								usermods/Internal_Temperature_v2/library.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								usermods/Internal_Temperature_v2/library.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| { | ||||
|   "name": "Internal_Temperature_v2", | ||||
|   "build": { "libArchive": false } | ||||
| } | ||||
| @@ -23,8 +23,7 @@ | ||||
|  | ||||
|  | ||||
| ## Installation | ||||
| - Add a build flag `-D USERMOD_INTERNAL_TEMPERATURE` to your `platformio.ini` (or `platformio_override.ini`). | ||||
|  | ||||
| - Add `Internal_Temperature` to `custom_usermods` in your `platformio.ini` (or `platformio_override.ini`). | ||||
|  | ||||
| ## 📝 Change Log | ||||
|  | ||||
|   | ||||
| @@ -1,14 +1,10 @@ | ||||
| #warning **** Included USERMOD_LD2410 **** | ||||
| 
 | ||||
| #ifndef WLED_ENABLE_MQTT | ||||
| #error "This user mod requires MQTT to be enabled." | ||||
| #endif | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "wled.h" | ||||
| #include <ld2410.h> | ||||
| 
 | ||||
| #ifdef WLED_DISABLE_MQTT | ||||
| #error "This user mod requires MQTT to be enabled." | ||||
| #endif | ||||
| 
 | ||||
| class LD2410Usermod : public Usermod { | ||||
| 
 | ||||
|   private: | ||||
| @@ -235,3 +231,7 @@ void LD2410Usermod::publishMqtt(const char* topic, const char* state, bool retai | ||||
|   } | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static LD2410Usermod ld2410_v2; | ||||
| REGISTER_USERMOD(ld2410_v2); | ||||
							
								
								
									
										7
									
								
								usermods/LD2410_v2/library.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								usermods/LD2410_v2/library.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| { | ||||
|   "name": "LD2410_v2", | ||||
|   "build": { "libArchive": false }, | ||||
|   "dependencies": { | ||||
|     "ncmreynolds/ld2410":"^0.1.3" | ||||
|   } | ||||
| } | ||||
| @@ -10,21 +10,15 @@ The movement and presence state are displayed in both the Info section of the we | ||||
| ## Dependencies | ||||
| - Libraries | ||||
|   - `ncmreynolds/ld2410@^0.1.3` | ||||
|   - This must be added under `lib_deps` in your `platformio.ini` (or `platformio_override.ini`). | ||||
| - Data is published over MQTT - make sure you've enabled the MQTT sync interface. | ||||
|  | ||||
| ## Compilation | ||||
|  | ||||
| To enable, compile with `USERMOD_LD2410` defined  (e.g. in `platformio_override.ini`) | ||||
| To enable, compile with `LD2140` in `custom_usermods` (e.g. in `platformio_override.ini`) | ||||
| ```ini | ||||
| [env:usermod_USERMOD_LD2410_esp32dev] | ||||
| extends = env:esp32dev | ||||
| build_flags = | ||||
|     ${common.build_flags_esp32} | ||||
|     -D USERMOD_LD2410 | ||||
| lib_deps =  | ||||
|     ${esp32.lib_deps} | ||||
|     ncmreynolds/ld2410@^0.1.3 | ||||
| custom_usermods = ${env:esp32dev.custom_usermods} LD2140_v2 | ||||
| ``` | ||||
|  | ||||
| ### Configuration Options | ||||
|   | ||||
| @@ -1,4 +1,3 @@ | ||||
| #pragma once | ||||
| #include "wled.h" | ||||
| 
 | ||||
| #ifndef ARDUINO_ARCH_ESP32 | ||||
| @@ -30,7 +29,7 @@ class LDR_Dusk_Dawn_v2 : public Usermod { | ||||
|     void setup() { | ||||
|       // register ldrPin
 | ||||
|       if ((ldrPin >= 0) && (digitalPinToAnalogChannel(ldrPin) >= 0)) { | ||||
|         if(!pinManager.allocatePin(ldrPin, false, PinOwner::UM_LDR_DUSK_DAWN)) ldrEnabled = false; // pin already in use -> disable usermod
 | ||||
|         if(!PinManager::allocatePin(ldrPin, false, PinOwner::UM_LDR_DUSK_DAWN)) ldrEnabled = false; // pin already in use -> disable usermod
 | ||||
|         else pinMode(ldrPin, INPUT);                                                               // alloc success -> configure pin for input
 | ||||
|       } else ldrEnabled = false;                                                                   // invalid pin -> disable usermod
 | ||||
|       initDone = true; | ||||
| @@ -110,7 +109,7 @@ class LDR_Dusk_Dawn_v2 : public Usermod { | ||||
| 
 | ||||
|       if (initDone && (ldrPin != oldLdrPin)) { | ||||
|          // pin changed - un-register previous pin, register new pin
 | ||||
|         if (oldLdrPin >= 0) pinManager.deallocatePin(oldLdrPin, PinOwner::UM_LDR_DUSK_DAWN); | ||||
|         if (oldLdrPin >= 0) PinManager::deallocatePin(oldLdrPin, PinOwner::UM_LDR_DUSK_DAWN); | ||||
|         setup();             // setup new pin
 | ||||
|       } | ||||
|       return configComplete; | ||||
| @@ -139,7 +138,7 @@ class LDR_Dusk_Dawn_v2 : public Usermod { | ||||
|       //LDR_Off_Count.add(ldrOffCount);
 | ||||
| 
 | ||||
|       //bool pinValid = ((ldrPin >= 0) && (digitalPinToAnalogChannel(ldrPin) >= 0));
 | ||||
|       //if (pinManager.getPinOwner(ldrPin) != PinOwner::UM_LDR_DUSK_DAWN) pinValid = false;
 | ||||
|       //if (PinManager::getPinOwner(ldrPin) != PinOwner::UM_LDR_DUSK_DAWN) pinValid = false;
 | ||||
|       //JsonArray LDR_valid = user.createNestedArray(F("LDR pin"));
 | ||||
|       //LDR_valid.add(ldrPin);
 | ||||
|       //LDR_valid.add(pinValid ? F(" OK"): F(" invalid"));
 | ||||
| @@ -151,3 +150,7 @@ class LDR_Dusk_Dawn_v2 : public Usermod { | ||||
| }; | ||||
| 
 | ||||
| const char LDR_Dusk_Dawn_v2::_name[]    PROGMEM = "LDR_Dusk_Dawn_v2"; | ||||
| 
 | ||||
| 
 | ||||
| static LDR_Dusk_Dawn_v2 ldr_dusk_dawn_v2; | ||||
| REGISTER_USERMOD(ldr_dusk_dawn_v2); | ||||
| @@ -2,13 +2,14 @@ | ||||
| This usermod will obtain readings from a Light Dependent Resistor (LDR) and will turn on/off specific presets based on those readings. This is useful for exterior lighting situations where you want the lights to only be on when it is dark out. | ||||
|  | ||||
| # Installation | ||||
| Add "-D USERMOD_LDR_DUSK_DAWN" to your platformio.ini [common] build_flags and build. | ||||
| Add "LDR_Dusk_Dawn" to your platformio.ini environment's custom_usermods and build. | ||||
|  | ||||
| Example: | ||||
| ``` | ||||
| [common] | ||||
| build_flags = | ||||
|   -D USERMOD_LDR_DUSK_DAWN   # Enable LDR Dusk Dawn Usermod | ||||
| [env:usermod_LDR_Dusk_Dawn_esp32dev] | ||||
| extends = env:esp32dev | ||||
| custom_usermods = ${env:esp32dev.custom_usermods}  | ||||
|   LDR_Dusk_Dawn   # Enable LDR Dusk Dawn Usermod | ||||
| ``` | ||||
|  | ||||
| # Usermod Settings | ||||
|   | ||||
							
								
								
									
										4
									
								
								usermods/LDR_Dusk_Dawn_v2/library.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								usermods/LDR_Dusk_Dawn_v2/library.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| { | ||||
|   "name": "LDR_Dusk_Dawn_v2", | ||||
|   "build": { "libArchive": false } | ||||
| } | ||||
| @@ -1,8 +1,6 @@ | ||||
| // force the compiler to show a warning to confirm that this file is included
 | ||||
| #warning **** Included USERMOD_MAX17048 V2.0 **** | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "wled.h" | ||||
| #include "Adafruit_MAX1704X.h" | ||||
| 
 | ||||
| @@ -37,8 +35,8 @@ class  Usermod_MAX17048 : public Usermod { | ||||
|     unsigned long lastSend = UINT32_MAX - (USERMOD_MAX17048_MAX_MONITOR_INTERVAL - USERMOD_MAX17048_FIRST_MONITOR_AT); | ||||
| 
 | ||||
| 
 | ||||
|     uint8_t  VoltageDecimals = 3;  // Number of decimal places in published voltage values
 | ||||
|     uint8_t  PercentDecimals = 1;    // Number of decimal places in published percent values
 | ||||
|     unsigned VoltageDecimals = 3;  // Number of decimal places in published voltage values
 | ||||
|     unsigned PercentDecimals = 1;  // Number of decimal places in published percent values
 | ||||
| 
 | ||||
|     // string that are used multiple time (this will save some flash memory)
 | ||||
|     static const char _name[]; | ||||
| @@ -279,3 +277,7 @@ const char Usermod_MAX17048::_enabled[] PROGMEM = "enabled"; | ||||
| const char Usermod_MAX17048::_maxReadInterval[] PROGMEM = "max-read-interval-ms"; | ||||
| const char Usermod_MAX17048::_minReadInterval[] PROGMEM = "min-read-interval-ms"; | ||||
| const char Usermod_MAX17048::_HomeAssistantDiscovery[] PROGMEM = "HomeAssistantDiscovery"; | ||||
| 
 | ||||
| 
 | ||||
| static Usermod_MAX17048 max17048_v2; | ||||
| REGISTER_USERMOD(max17048_v2); | ||||
							
								
								
									
										7
									
								
								usermods/MAX17048_v2/library.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								usermods/MAX17048_v2/library.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| { | ||||
|   "name": "MAX17048_v2", | ||||
|   "build": { "libArchive": false}, | ||||
|   "dependencies": { | ||||
|     "Adafruit_MAX1704X":"https://github.com/adafruit/Adafruit_MAX1704X#1.0.2" | ||||
|   } | ||||
| } | ||||
| @@ -5,26 +5,16 @@ This usermod reads information from an Adafruit MAX17048  and outputs the follow | ||||
|  | ||||
|  | ||||
| ## Dependencies | ||||
| Libraries: | ||||
| - `Adafruit_BusIO@~1.14.5` (by [adafruit](https://github.com/adafruit/Adafruit_BusIO)) | ||||
| - `Adafruit_MAX1704X@~1.0.2` (by [adafruit](https://github.com/adafruit/Adafruit_MAX1704X)) | ||||
|  | ||||
| These must be added under `lib_deps` in your `platform.ini` (or `platform_override.ini`). | ||||
| Data is published over MQTT - make sure you've enabled the MQTT sync interface. | ||||
|  | ||||
| ## Compilation | ||||
|  | ||||
| Add "MAX17048_v2" to your platformio.ini environment's custom_usermods and build. | ||||
| To enable, compile with `USERMOD_MAX17048` define in the build_flags (e.g. in `platformio.ini` or `platformio_override.ini`) such as in the example below: | ||||
| ```ini | ||||
| [env:usermod_max17048_d1_mini] | ||||
| extends = env:d1_mini | ||||
| build_flags = | ||||
|   ${common.build_flags_esp8266} | ||||
|   -D USERMOD_MAX17048 | ||||
| lib_deps =  | ||||
|   ${esp8266.lib_deps} | ||||
|   https://github.com/adafruit/Adafruit_BusIO @ 1.14.5 | ||||
|   https://github.com/adafruit/Adafruit_MAX1704X @ 1.0.2 | ||||
| custom_usermods = ${env:d1_mini.custom_usermods} MAX17048_v2 | ||||
| ``` | ||||
|  | ||||
| ### Configuration Options | ||||
|   | ||||
| @@ -1,5 +1,3 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "wled.h" | ||||
| #include "MY92xx.h" | ||||
| 
 | ||||
| @@ -42,4 +40,7 @@ class MY9291Usermod : public Usermod { | ||||
|     uint16_t getId() { | ||||
|       return USERMOD_ID_MY9291; | ||||
|     } | ||||
| }; | ||||
| }; | ||||
| 
 | ||||
| static MY9291Usermod my9291; | ||||
| REGISTER_USERMOD(my9291); | ||||
							
								
								
									
										5
									
								
								usermods/MY9291/library.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								usermods/MY9291/library.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| { | ||||
|   "name": "MY9291", | ||||
|   "build": { "libArchive": false }, | ||||
|   "platforms": ["espressif8266"] | ||||
| } | ||||
| @@ -42,7 +42,7 @@ | ||||
|  *  | ||||
|  *  | ||||
|  * Usermods allow you to add own functionality to WLED more easily | ||||
|  * See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality | ||||
|  * See: https://github.com/wled-dev/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. | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user