Compare commits

...

576 Commits

Author SHA1 Message Date
cschwinne
102a28aef4 Release of WLED v0.13.3 2022-08-23 01:26:18 +02:00
cschwinne
cade1800f4 Update python dependencies 2022-08-21 12:50:52 +02:00
Blaz Kristan
8176f1141e Merge branch 'Sousanator-master'
Added LPD6803 chip support.
2022-08-16 20:35:57 +02:00
Blaz Kristan
515827c745 Merge branch 'master' of https://github.com/Sousanator/WLED into Sousanator-master 2022-08-16 20:35:17 +02:00
cschwinne
420f858d9b Release of WLED v0.13.2 2022-08-15 02:08:48 +02:00
Blaž Kristan
902c11d074 Merge pull request #2657 from poelzi/watchdog
Enable ESP watchdog by default
2022-07-22 09:26:46 +02:00
Blaž Kristan
38330b735c Merge pull request #2723 from albarlow/BME280-Enhancements
BME280 Usermod Enhancements
2022-07-21 18:42:19 +02:00
Blaz Kristan
bda3c4ab7a Minor optimisations. 2022-07-21 18:38:07 +02:00
Blaz Kristan
d8d01ac353 Merge branch 'BME280-Enhancements' of https://github.com/albarlow/WLED into BME280-Enhancements 2022-07-21 18:37:17 +02:00
dependabot[bot]
51d935f419 Bump terser from 4.8.0 to 4.8.1 (#2726)
Bumps [terser](https://github.com/terser/terser) from 4.8.0 to 4.8.1.
- [Release notes](https://github.com/terser/terser/releases)
- [Changelog](https://github.com/terser/terser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/terser/terser/commits)

---
updated-dependencies:
- dependency-name: terser
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-21 11:24:54 +02:00
albarlow
c96f83b076 Registered Usermod and Pins
Implemented PinManager and rerun setup of usermod after updating pins/

Registered in const.h and pin_manager.h   I tried to follow the existing formatting/numbering in these files.

Wrapped any strings I could in F()
2022-07-21 09:48:37 +01:00
albarlow
7308f5993c Added changelog 2022-07-20 22:53:45 +01:00
albarlow
22ac12dc36 Remove compile-time variables and revamp readme.md 2022-07-20 13:06:27 +01:00
albarlow
866296fefd Update usermod bme280 rounding
Apply rounding per usermod settings
2022-07-19 22:15:26 +01:00
albarlow
9d574397bc usermod bme280
Added public variables to the BME280 usermod based on those in the Temperature usermod.  Only complication is that this usermod utilises different function calls depending on whether user defines celsius or not.  I have handled this for the temperature, but the Dew Point and Heat Index are relative to the temperature.

I've also addressed some areas where I'd previously assumed Celsius for reporting purposes as my test case is using Farenheit.
2022-07-19 21:47:56 +01:00
albarlow
bee48dae7e Update readme 2022-07-19 14:52:39 +01:00
albarlow
e12f7b67e5 Usermod BME280 Enhancements
I added a Usermod interface for key settings.  I used a PinArray for the SDA/SCL pins, but you can't name these individually.

I have also made a display to show the temperature/humidity values in the web interface's Info screen.

I had to change the definition of those items in order to allow these new functions to work.  I have not noticed any negative side effects to this change.

At the moment, I've not figured out how to make Celsius/Farenheit toggleable due to the way the #define setup works.

Finally, I have added a routine to publish MQTT Discovery Topics for Home Assistant (toggleable in the Usermod screen).

I've been testing this on the only suitable device I have for a few months and haven't noticed any problems.
2022-07-19 14:52:10 +01:00
Blaz Kristan
a8908238d5 Prevent race condition when saving bus config.
(loop() is executed on different core than handleSet())
2022-07-02 14:28:09 +02:00
Blaž Kristan
fd4c0e795a Merge pull request #2693 from softhack007/analogread_smoothing_fix_2587
noise filter for potentiometer reading - fix for issue #2587
2022-06-22 13:20:16 +02:00
Frank
c79eb43347 disabled second check for strip.isUpdating()
commented out the second `strip.isUpdating()` check, because it should not be neccesary; Strip.service() is called after handleIO()/handleButton().
2022-06-22 12:36:47 +02:00
Blaž Kristan
860e74bffa Comment & float constant. 2022-06-22 09:58:21 +02:00
Frank
ed374684a6 Update button.cpp
overlooked one
2022-06-20 22:00:23 +02:00
Frank
169a46c38c button.cpp: marked literal constant as "float! 2022-06-20 21:56:16 +02:00
Frank
1dbea434a3 fix for issue #2587 2022-06-20 16:04:43 +02:00
dependabot[bot]
0dd12cf0a6 Bump bottle from 0.12.19 to 0.12.20 (#2683)
Bumps [bottle](https://github.com/bottlepy/bottle) from 0.12.19 to 0.12.20.
- [Release notes](https://github.com/bottlepy/bottle/releases)
- [Changelog](https://github.com/bottlepy/bottle/blob/master/docs/changelog.rst)
- [Commits](https://github.com/bottlepy/bottle/compare/0.12.19...0.12.20)

---
updated-dependencies:
- dependency-name: bottle
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-16 10:31:14 +02:00
Blaž Kristan
19c8b4fe2d Merge pull request #2665 from Stoom/segment_bri_fix
🐛 fix(json): allow for using `~-16` or `~16` when setting a segments brightness though the JSON api
2022-05-29 11:02:59 +02:00
Daniel Poelzleithner
26fa38d052 Watchdog: disable watchdog while OTA is running 2022-05-23 22:30:13 +00:00
Jamie Stoom
db8e1dec3e 🐛 fix(json): allow for using ~-16 or ~16 when setting a segments brightness though the JSON api 2022-05-09 18:06:45 -07:00
Daniel Poelzleithner
213e3e998a Enable ESP watchdog by default
Use the ESP watchdog to detect hanging ESP and reset the firmware.
Can be disable by defining WLED_WATCHDOG_TIMOUT 0
Default timeout is 3 seconds on ESP32 and 8 seconds on ESP2688
2022-05-05 11:33:00 +00:00
Luke Plassman
bef9c68f81 Working DMX Libraries (#2652)
* added SparkFunDMX library dependencies

* changed variable names to avoid conflicts with SparkFunDMX library

* board version checks

* minor changes to DMX

* fix brightness when no shutter DMX channel is set

* fix compile issue on newer ESP32 variants
2022-05-05 02:28:09 +02:00
ChuckMash
099d2fd03d WiZ Lights usermod - Adding more options and features (#2638)
* Update wizlights.h

adds new features and options for wizlights usermod

* Update wizlights.h

Change how IPs are numbered.
Non-programmers incorrectly start counting at 1

* Update wizlights.h

updated default cold white enhanced white setting to a lower value.

* Update wizlights.h

added logic for connection check before UDP sending.
Seems more important for ESP32

* Update readme.md
2022-05-03 12:18:21 +02:00
Blaz Kristan
23d39e5366 Compile time options for Multi Relay & PWM Fan 2022-04-29 09:56:48 +02:00
Thomas
1a513c7bbf wled.cpp: Wrap Serial calls in #ifdef WLED_ENABLE_ADALIGHT. (#2630)
handleImprovPacket() unconditionally gobbles serial data which a problem
if we want another feature to consume it. This patch uses the same guard
as the existing call in `handleSerial()`.

Co-authored-by: Thomas Fubar <FUB4R@users.noreply.github.com>
2022-04-17 00:08:27 +02:00
ChuckMash
d1f76042e1 bugfix for outgoing serial TPM2 message length (#2628)
bugfix for outgoing serial TPM2 message length
2022-04-12 10:20:08 +02:00
Christian Bartsch
9cd8acab43 Usermod: Add support for Si7021 temperature and humidity sensors (#2617)
* added first version (work in progress)

* added some sensors to publish

* typo

* added dependency

* mqtt si7021_* names + don't  retain

* timer to 60 s

* some changes to HA auto discovery

* added config entries (no function yet)

* renaming

* made configs work

* added getId()

* refactoring + wrong mqtt topics fixed

* retain HA auto discovery

* do not spam serial console on each sensor update

* added readme

* add update interval info

Co-authored-by: Christian Schwinne <dev.aircoookie@gmail.com>
2022-04-03 22:30:37 +02:00
4lloyd
8b79a9708b Next universe overflow and Art-Net DMX start address (#2607)
* Improve calculation LEDs in next universe

* Fix Art-Net DMX start address
2022-04-03 17:52:36 +02:00
cschwinne
e362b3b6aa Fixed sunset time off by an hour on DST change day (fixes #2603) 2022-04-01 02:07:50 +02:00
Christian Schwinne
d2ced93e58 Merge pull request #2601 from Aircoookie/minseg-udp
UDP on main segment only.
2022-04-01 01:24:13 +02:00
cschwinne
958cd35e21 Live mainseg improvements
Make override work in mainseg mode
Move unfreeze on turn on from UI to JSON parser
Fix mainseg not unfreezing on timeout
2022-04-01 00:59:19 +02:00
Spectre
46eae410c3 add My9291 light bulb driver (#2599)
Co-authored-by: Christian Schwinne <dev.aircoookie@gmail.com>
2022-03-31 20:49:00 +02:00
Stefan Riese
73a9e1c316 V2 usermod for wordclock with 11x10 LEDs and 4 minute dots (#2608)
* - implement V2 Usermod to handle wordclocks with 11x10 pixels and 4 additional dots for the minutes

* - fix wording issue for "six"
-

* - add some more comments

* - fix issue with "zwölf"
2022-03-31 20:31:37 +02:00
cschwinne
03862d4b6c Add icon font 2022-03-28 23:23:38 +02:00
Blaz Kristan
ae90aa4ccc Power off. 2022-03-28 23:07:37 +02:00
Blaz Kristan
b583def913 Using freeze instead of power for segment.
Fix for missing udp.cpp
2022-03-28 20:44:49 +02:00
dependabot[bot]
dd85da053f Bump minimist from 1.2.5 to 1.2.6 (#2602)
Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6.
- [Release notes](https://github.com/substack/minimist/releases)
- [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6)

---
updated-dependencies:
- dependency-name: minimist
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-28 09:44:17 +02:00
Blaz Kristan
6079effae3 UDP on main segment only. 2022-03-25 17:20:41 +01:00
Woody
8d2fe315db Fix cross-origin error in File Mode (#2585)
* fixed cross-origin error

* removed unnecessary code

* try/catch for parent WS

Co-authored-by: cschwinne <dev.aircoookie@gmail.com>
2022-03-24 12:17:10 +01:00
Eduardo Ibanez
22c3ac5be3 Add usermode to control Wiz lights (#2595)
* Add usermode to control Wiz lights (#1)

* Fix inclusion in usermods list

Co-authored-by: Christian Schwinne <dev.aircoookie@gmail.com>
2022-03-23 16:20:18 +01:00
cschwinne
a517f0df1d Add ESP32-ETHERNET-KIT-VE type 2022-03-20 22:12:18 +01:00
cschwinne
9c9854b6bf Fixed sunrise/set calculation 2022-03-19 19:27:32 +01:00
cschwinne
d280e16723 Fixed /json/cfg unable to set busses (fixes #2589) 2022-03-19 14:21:14 +01:00
cschwinne
b93a9cb8bc FIxed liveview 2022-03-18 14:24:10 +01:00
cschwinne
8601052179 Fixed liveview buffer over-write (fixes #2586 )
(with odd LED counts > 256)
2022-03-18 14:08:37 +01:00
cschwinne
eaa20ff4bf Add handleOverlayDraw() to example v2 usermod 2022-03-16 19:32:11 +01:00
Blaž Kristan
e4c6e4bc48 Merge pull request #2584 from tonyn0/master
DDP in AP mode.
2022-03-16 17:31:09 +01:00
Blaž Kristan
c52597205e Merge branch 'master' into master 2022-03-16 17:29:12 +01:00
tonyn0
c73033c0b4 udp.cpp update
added ap check for ddp in L657
2022-03-16 11:00:29 -05:00
cschwinne
522e752582 Add ability to skip up to 255 LEDs (#2342) 2022-03-16 01:45:07 +01:00
SpeakingOfBrad
854ed8cfa9 ABL milliamps no longer hardcoded to 850 at runtime (#2581) 2022-03-16 00:35:49 +01:00
SpeakingOfBrad
4642205768 Optionally set led strip color order at runtime. (#2582) 2022-03-16 00:17:49 +01:00
André Klitzing
40dbfbe092 Update IRremoteESP8266 to 2.8.2 (#2579)
This fixes some C3 issues
2022-03-16 00:16:49 +01:00
cschwinne
6c315e5a9c 0.13.1
Fix persistent preset bug
2022-03-15 03:45:20 +01:00
cschwinne
ef0f91d8d0 Release of WLED v0.13.0 2022-03-15 02:28:26 +01:00
cschwinne
9552784e72 Remove persistent argument from savePreset()
(fixes temp preset not applicable by APIs)
Default to 5Mhz hardware SPI driving (#2558)
2022-03-14 20:53:00 +01:00
SpeakingOfBrad
f068327307 Add option to set module name at runtime, and add extra examples in platformio_overrides.ini (#2578)
* Added option to set the name of the module at runtime.

* added example how to set number of LEDs at runtime

* Example to enable/set IR remote type at runtime

* Clarification on how to use platformio_overrides

* Example for setting abl milliamp limit at runtime

* Corrected example set LED count
2022-03-14 18:23:53 +01:00
cschwinne
1bc698ae78 Button 0 long press factory reset
JS simplification
2022-03-14 12:26:45 +01:00
cschwinne
1b2134d7a8 Add old blinds usermod 2022-03-11 09:20:01 +01:00
cschwinne
f922268af7 Remove unneeded brightness set in live Serial 2022-03-11 08:41:01 +01:00
cschwinne
4865ddb377 Fix realtime mode disabled by brightness change
Fix realtime mode not working immediately at turn on
Fix individual segment control not working immediately at turn on
2022-03-10 20:40:48 +01:00
cschwinne
a556732e4f Add ability to set presets from DMX
(effect mode with single channel)
2022-03-10 12:13:12 +01:00
cschwinne
0ea31cb088 Fix indefinite realtime terminated by new packet (fixes #2356 )
Add custom Aircoookie fork of ESP32 core (reduces bin size by >100kB)
2022-03-10 11:20:39 +01:00
cschwinne
b626c7620e Disabled auto white mode in segments with no RGB bus 2022-03-08 02:16:33 +01:00
cschwinne
5d90d8930e Fix non-0 terminated hostname str 2022-03-07 20:37:48 +01:00
PLCHome
b01309c3bf Mixed content exception in web browser in websocket communication on peek behind an https backproxy. (#2571)
"ws://" must be the change to the "wss://" for encryption
2022-03-07 18:26:53 +01:00
cschwinne
961d5591bd Fixed Popcorn mode not lighting first LED on pop 2022-03-07 00:53:20 +01:00
cschwinne
eca3f12fed Fixed analog overlay not settable 2022-03-07 00:11:43 +01:00
cschwinne
a2c8796e04 Replaced native Cronixie support with usermod 2022-03-06 23:47:36 +01:00
cschwinne
ad301fd087 Elekstube usermod enhancements
Coloring of grayscale images
Dimming control from configurable segment
2022-03-06 22:24:24 +01:00
cschwinne
02b08939cd No color order select on PWM busses (fixes #2569) 2022-03-06 11:48:17 +01:00
cschwinne
9b0d583f1b EleksTube usermod 1, 4, 8 BPP BMP support 2022-03-05 21:48:11 +01:00
RedNax67
4a0a07f158 Added digit dimming and support for .clk format (see https://github.c… (#2555)
* Added digit dimming and support for .clk format (see https://github.com/aly-fly/EleksTubeHAX)

* Small fixes and improvements, dimming optional

Co-authored-by: cschwinne <dev.aircoookie@gmail.com>
2022-03-05 03:10:32 +01:00
Christian Schwinne
9c864c9759 UI: show color controls based on segment light capabilities (#2567)
* Dynamic hiding of unused color controls in UI

(e.g. a PWM white bus with no auto white mode will not display the color wheel or palette list)

* Store segment capabilities

Don't use palettes if no RGB supported
Make it safe to update busses using `/json/cfg`
2022-03-05 01:05:26 +01:00
cschwinne
85b1c309d1 Relative value wrapping and operator fix (fixes #2566 ) 2022-03-04 14:42:35 +01:00
cschwinne
6fe43b7b5c Separate color memory from slot selectors
Slot selector dynamic text color black/white
Selected slot selector JS simplification
2022-03-03 20:54:54 +01:00
Blaz Kristan
25427ee60d Fix:
- disbled timed preset expanding
- incorrect calendar icon on Mac/Firefox
2022-03-03 10:57:07 +01:00
cschwinne
be90bf0188 Minor CSS simplifications 2022-03-01 18:22:54 +01:00
cschwinne
adcdaba199 Indentation: Consistent use of Tab
Added style guide
Updated changelog
2022-03-01 12:14:41 +01:00
cschwinne
17907589cc Indentation: Consistent use of Tab for CSS 2022-03-01 11:20:10 +01:00
cschwinne
f333df181f Small CSS alignment adjustments
mainseg is not highlighted by default, but can be enabled by CSS only
Simplify some CSS (new segment box, segment brightness slider)
Started labeling CSS classes
2022-03-01 11:13:56 +01:00
Blaz Kristan
4ce557a829 Multiple fixes:
- ability so select mainseg (UI)
- changed preset sort (UI)
- IR40 white +/- fix
- IR repeatable actions fix
- minor UI CSS change
- removed unnecessary color functions
2022-02-27 22:19:05 +01:00
Christian Schwinne
fc845dc936 Add locate button for easy lat/lon auto-fill (#2559) 2022-02-26 01:37:30 +01:00
Blaž Kristan
7beae93441 IR rewrite. (#2561)
* IR rewrite.
- added CCT (WW/CW) support
- support for applying change to main segment or all selected segments

* Remove extra setValuesFromFirstSelectedSeg()

Co-authored-by: cschwinne <dev.aircoookie@gmail.com>
2022-02-26 01:37:09 +01:00
cschwinne
4d4a20e05e 0.13.0-b7
Mitigate Pixel 6 UI issue
2022-02-24 01:16:24 +01:00
cschwinne
c03d4f115f Fixed presets not triggering interface update 2022-02-23 19:42:34 +01:00
cschwinne
ed90b638a9 Main seg replaced by first selected in internal interfaces
Version bump to 0.13.0-b7
Various small improvements
2022-02-23 19:20:07 +01:00
cschwinne
94a0199955 Readme cleanup 2022-02-21 22:26:35 +01:00
cschwinne
44739c5198 Merge effectChanged and colorChanged to stateChanged 2022-02-21 22:12:13 +01:00
cschwinne
5f871bc01f HTTP API: Set segments directly in set.cpp 2022-02-21 20:48:11 +01:00
cschwinne
1f5971f15a Another HTTP API segment improvement 2022-02-21 18:31:19 +01:00
cschwinne
694466a196 Apply segment by Enter key on input field 2022-02-21 17:58:18 +01:00
cschwinne
03311d3776 Do not set main seg before apply
Setting mainseg before applyValuesToSelectedSegs() causes the updated value to not be set to other selected segments
2022-02-21 16:57:18 +01:00
cschwinne
ae0eba866a Improve Stream and fix HTTP segment application 2022-02-21 16:19:11 +01:00
cschwinne
906737bedb Changelog update 2022-02-20 02:15:34 +01:00
Christian Schwinne
7138e891be Add per-segment light capability info (#2552)
* Add per-segment light capability info

* Fix duplication

* Change to more lightweight seglc array

* Added segment capabilities.

* Differs and capabilities refactoring

* Add back selection differs option

Co-authored-by: Blaz Kristan <blaz@kristan-sp.si>
2022-02-20 00:20:22 +01:00
Christian Schwinne
53abe36b83 Merge pull request #2547 from Aircoookie/sync-segbounds2
Sync segment bounds
2022-02-19 23:00:31 +01:00
cschwinne
efbb7a034c Slight websocket reconnection tweaks 2022-02-19 22:47:17 +01:00
Blaz Kristan
05bc81bf4e Add default preset name if none specified. 2022-02-19 11:42:59 +01:00
Blaz Kristan
f8bc0bd2b5 Removed unnecessary if. 2022-02-18 19:23:55 +01:00
Blaz Kristan
cf94cb1092 Allow saving preset from IR
Removed double clolorUpdated() call
2022-02-18 18:35:51 +01:00
Blaz Kristan
02d92e32c7 Parsing IR JSON cmd fix. 2022-02-18 17:01:34 +01:00
Blaž Kristan
7f92607b85 Added WS reconnect on error toast. 2022-02-17 12:51:37 +01:00
Blaž Kristan
3be4b69b44 WS reconnect logic & WS memory leak protection 2022-02-17 12:45:50 +01:00
Tom D'Roza
bb9afcb304 Added: CSS hover effect on buttons within modal dialogs, e.g. Info, Nodes (#2545)
* Added: CSS hover effect on buttons within modal dialogs, e.g. Info,
Nodes

* Update index.css

Co-authored-by: Tom D'Roza <Tom.D'Roza>
Co-authored-by: Christian Schwinne <cschwinne@gmail.com>
2022-02-17 00:38:47 +01:00
cschwinne
e9a05890a5 Return to core 2.7.4 for higher stability 2022-02-17 00:17:00 +01:00
cschwinne
613809c2af Do not turn rpt segments off 2022-02-16 23:54:14 +01:00
cschwinne
7b969bb8c2 Various state changed logic simplifications
Changed main segment, must be selected
2022-02-16 21:12:33 +01:00
cschwinne
7aef551292 Segment bounds sync option 2022-02-16 14:55:35 +01:00
cschwinne
447b811fa0 Remove build flag leading to wdt reset on some boards 2022-02-14 18:49:13 +01:00
dependabot[bot]
435040814d Bump ajv from 6.12.2 to 6.12.6 (#2543)
Bumps [ajv](https://github.com/ajv-validator/ajv) from 6.12.2 to 6.12.6.
- [Release notes](https://github.com/ajv-validator/ajv/releases)
- [Commits](https://github.com/ajv-validator/ajv/compare/v6.12.2...v6.12.6)

---
updated-dependencies:
- dependency-name: ajv
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-14 15:35:13 +01:00
Blaž Kristan
9987416a4a Allow float array values in usermod config. 2022-02-14 12:19:33 +01:00
Blaž Kristan
31e33e0a8b Fix for creating segments if config set to length. 2022-02-14 08:15:35 +01:00
cschwinne
b211d8b085 Fix SSDR usermod if SN_Photoresistor mod is not included 2022-02-10 19:48:13 +01:00
Blaž Kristan
83416ee2e0 Merge pull request #2530 from Proto-molecule/patch-api
bugs, json remote repeat, cmd &R=
2022-02-10 14:06:34 +01:00
Blaz Kristan
fa981a389f Add transitions to other segments. 2022-02-10 13:59:59 +01:00
Blaz Kristan
55817f31f9 Merge branch 'master' into patch-api 2022-02-10 13:48:48 +01:00
Blaž Kristan
6d2ef4e0bf Merge pull request #2539 from dylan09/multirelay-discovery
Fixed buffer overflow in HA autodiscovery. #2538
2022-02-10 00:33:16 +01:00
ulrich
4d714cf9a4 Fixed buffer overflow in HA autodiscovery. #2538 2022-02-09 23:08:42 +01:00
cschwinne
930ded6767 Fix touch pin 2022-02-09 19:59:17 +01:00
Blaz Kristan
4cdb18907f Fix for Four Line Display usermod. 2022-02-09 19:27:52 +01:00
Henry Gabryjelski
38bc618ee5 Float and better 3rd party library compatibility (#2534)
* define as float (not double)

* Avoid #define of 1 or 2 char symbols

Having this file define 'A' and 'C' pollutes
the global namespace, and causes conflicts
with other libraries that also pollute the
global namespace with short #defines.
It's easier to fix this header.

* unused variable warning
2022-02-09 09:46:54 +01:00
Christian Schwinne
00b0193a43 Fix re-init segment data leak (fixes #2535 ) (#2536) 2022-02-09 08:43:35 +01:00
bole5
f9bce54104 Change Default Relay Pin to -1 (#2531) 2022-02-08 00:15:24 +01:00
bole5
7ee14724fc Improve Pin Manager Debugging (#2532) 2022-02-08 00:03:20 +01:00
Blaz Kristan
4eb0dbb5a4 repeat actions cleanup & fix 2022-02-07 11:13:12 +01:00
Proto-molecule
8c5b3fe23e bugs, json remote repeat, cmd &R= 2022-02-06 19:00:03 -08:00
cschwinne
97f8eea302 Refactored isRgbw to hasWhiteChannel() 2022-02-04 13:28:00 +01:00
cschwinne
04d5932252 Un-F()-ed some strings
(that were either occuring at least 4 times, or were F()-ed in some places and not in others)
2022-02-04 10:10:37 +01:00
cschwinne
b33c5798ee Changelog update 2022-02-03 23:37:30 +01:00
Blaz Kristan
6180c2f948 Fix for overallocated LiveView buffer. 2022-02-03 20:21:09 +01:00
Christian Schwinne
795c515999 Merge pull request #2517 from ChuckMash/serial+
Extend Serial functions. Baudrate changeable during runtime and fast LED data retrieval
2022-02-01 20:07:30 +01:00
cschwinne
00dbdc2267 Baud rate setting 2022-02-01 20:02:46 +01:00
cschwinne
32286888e5 PinManager, cleanup and tmp2 out 2022-02-01 18:21:30 +01:00
Christian Schwinne
565d8d8f04 Binary Websockets for Peek (#2516)
* Binary Websockets for Peek

* No IRAM_ATTR here

* Use builtin LittleFS for all ESP32 builds

* Attempt LittleFS compilation fix

* Use tasmota zip for all ESP32 builds

* Revert to Arduino Core 1 for the time being
2022-02-01 12:02:04 +01:00
Blaž Kristan
0a5a0bef48 Enhanced usermods. (#2522) 2022-02-01 09:33:57 +01:00
Blaz Kristan
6e0e5c102e Added extractModeName() utility function. 2022-01-31 20:43:35 +01:00
Blaz Kristan
be8a9ae73b setPixelSegment() optimization 2022-01-31 17:56:21 +01:00
Sousanator
afaa001738 Merge branch 'master' into master 2022-01-30 10:51:51 -05:00
ChuckMash
22fbb0e35b Update wled_serial.cpp
Adds serial functionality

Can now change baud rate during runtime to be faster
Retrieve LED strip data as JSON blob
Retrieve LED strip data as BYTES (fast!)
2022-01-28 23:35:40 -08:00
Blaž Kristan
e17203ca1b Fix for expand timed presets. 2022-01-28 13:51:52 +01:00
Blaz Kristan
3170fa2208 Playlist bugfix. 2022-01-27 21:00:43 +01:00
Blaz Kristan
07216db864 Merge branch 'master' of https://github.com/aircoookie/WLED 2022-01-27 19:26:56 +01:00
Blaz Kristan
fec870f264 Fix for default action not triggering colorUpdated 2022-01-27 19:26:53 +01:00
cschwinne
2c5eba335f Added white channel to Peek (closes #1716) 2022-01-26 13:26:57 +01:00
Mike Ryan
fb19f1ecbc Allow overriding of color order by LED pixel range. (#2463)
* Overridable color order

- Use `ColorOrderMap` to hold optional color order overrides for ranges
  of LEDs.
- Serialization of config to/from filesystem is complete.
- Back-end configuration is complete.
- TODO: front-end changes to the LED settings page.

* Add Color order override settings

- Adds color order override section to settings page.

* PR Feedback

- Limit max number of color order overrides to 5 on ESP8266
- Only append color overrides if they were provided in the POST of LED
  settings.
2022-01-26 00:42:04 +01:00
Blaž Kristan
e879fe5843 Remove obsolete usermods. (#2510) 2022-01-25 16:42:35 +01:00
Blaž Kristan
0ca7699fe5 Merge pull request #2427 from Aircoookie/sync-seg
Sync segment options.
2022-01-25 12:54:24 +01:00
Blaž Kristan
7f6adfa331 Converted indentation tabs to spaces. 2022-01-25 12:47:14 +01:00
cschwinne
5f0b102671 Send segment ID, start, stop, and cct 2022-01-24 18:31:05 +01:00
cschwinne
d28eb6ae21 Repeat other seg than 0 bugfix 2022-01-24 16:44:47 +01:00
André Klitzing
eca980dfca Add initial support for ESP32-C3 (#2454)
* WIP Add support for ESP32-C3

* Add esp32c3 to default_envs

* Use new platform from tasmota

* Switch back to 2.8.1 as it seems by fixed
2022-01-24 11:34:02 +01:00
Jason2866
742c792ec7 Use latest Arduino ESP32 (#2502)
build with IDF44-rc1 and Arduino from 21.01.2022. Toolchains updated to 8.4.0 2021r2-patch2
The platform can be used for ESP32, ESP32-S2 and ESP32-C3
2022-01-24 11:28:49 +01:00
Blaž Kristan
9b062f33c5 Merge pull request #2450 from frankalicious/patch-1
fix name of image for ST7789 usermod
2022-01-22 23:07:15 +01:00
Blaž Kristan
ea15c2245e Merge pull request #2497 from herm/usermod_multirelay
Add Home Assisant MQTT autodiscovery for usermod multi_relay.
2022-01-22 22:57:24 +01:00
Blaz Kristan
26ae6d3691 Added config option for HA autodiscovery. 2022-01-22 20:49:43 +01:00
cschwinne
f97bc9dba8 Fix DMX menu settings item 2022-01-21 20:35:30 +01:00
Blaz Kristan
fe6b1c13c4 Periodic broadcasts. 2022-01-21 16:08:02 +01:00
Blaz Kristan
5608425a12 Added comments.
Fix for incorrect boot state.
2022-01-21 15:55:25 +01:00
cschwinne
f784b01d20 Update year 2022-01-21 01:48:50 +01:00
Hermann Kraus
2648eba5bf Deprecate usermod mqtt_switch. (#2499) 2022-01-20 23:31:28 +01:00
Blaz Kristan
255347ab77 Minor clenup. 2022-01-20 17:38:18 +01:00
Hermann Kraus
52c36ef6a4 Add Home Assisant MQTT autodiscovery for usermod multi_relay. 2022-01-20 00:30:17 +01:00
Blaz Kristan
e54819e7e5 Merge branch 'master' into sync-seg 2022-01-15 14:08:08 +01:00
Blaz Kristan
7eb029dcb6 Complete segment syncing.
Reduced complexity of colorUpdated regarding effect/color change.
Minor optimizations.
2022-01-15 14:04:16 +01:00
André Klitzing
f8c80283e4 Use arduino-esp32 2.0.2 for ESP32-S2 (#2452)
* Use arduino-esp32 2.0.2 for ESP32-S2

LittleFS is merged into it.

* Fix filesystem error for ESP32-S2

Use platform of tasmota until upstream released it and
use board_build.flash_mode = qio

* Fix empty disk usage

* Add esp32s2_saola to default_envs

* Remove lorol LITTLEFS for esp32dev, too

* Revert "Remove lorol LITTLEFS for esp32dev, too"

This reverts commit 3586d5eef7.
2022-01-14 17:19:33 +01:00
Blaž Kristan
04f5bdb843 Fix for NTP sync on millis() rollover.
AP mode delay on boot (#2242).
2022-01-10 13:53:11 +01:00
Blaz Kristan
aba4dc7c50 Bugfix for analog button read limitation. 2022-01-09 15:13:58 +01:00
cschwinne
7fb46cf982 WIP: Segment loop size byte 2022-01-09 14:16:07 +01:00
Blaz Kristan
ae8281f835 error in udpIn 2022-01-07 21:16:14 +01:00
Blaž Kristan
fa35293618 Full segment syncing. 2022-01-07 19:12:06 +01:00
Blaž Kristan
20ccca0aec Swapped grouping and spacing in UDP packet. 2022-01-07 17:53:07 +01:00
Blaž Kristan
10e216da6b Fix for missing middle segment. 2022-01-07 17:48:46 +01:00
Blaž Kristan
6491353a57 Missing rptSeg() 2022-01-07 17:31:28 +01:00
IgorMarkovic
9f44f989e5 Fix staircase mode segment 1 animation (#2469) 2022-01-05 16:09:25 +01:00
cschwinne
33f72e40da Replace icon unavailable on some mobile browsers 2022-01-02 18:06:42 +01:00
Blaž Kristan
18868a5bd6 Date controlled timed presets. (#2447)
* Date controlled timed presets.

* C/P fix for sunset.

* Fixed % escape character

* Date range support

* Date logic fix

Co-authored-by: cschwinne <dev.aircoookie@gmail.com>
2021-12-31 14:09:48 +01:00
Christian Schwinne
754682577c Configurable framerate (#2444)
Updated arduino core versions
Better performance on esp32 core 3.x due to IRAM_ATTR
Fixed analog busses init to full white/on
2021-12-30 01:48:27 +01:00
Sousanator
71520f6709 Update const.h
Added option for LDP6803
2021-12-27 18:06:58 -05:00
Sousanator
3f5a09229d Update settings_leds.htm
Added option for LPD6803
2021-12-27 18:04:14 -05:00
Sousanator
5609771993 Update html_settings.h 2021-12-27 18:01:42 -05:00
Sousanator
79b01cdc3c Update bus_wrapper.h
Added support for LPD6803
2021-12-27 17:54:37 -05:00
frankalicious
aef0243b73 fix name of image for ST7789 usermod 2021-12-26 11:06:20 +01:00
akshay rajput
736053e24e Patch to remove preceding zero of hour digit for 7segment usermod (#2445)
* Update usermod_seven_segment_reloaded.h

* Update usermod_seven_segment_reloaded.h
2021-12-26 02:29:56 +01:00
Blaz Kristan
2c14181051 LAT/LON helper for Samsung devices. 2021-12-23 20:32:45 +01:00
Blaz Kristan
296fe4b62e Unload playlist on timed presets. 2021-12-21 22:46:06 +01:00
Blaz Kristan
118f02fd11 Unload playlist on timed presets. 2021-12-21 18:47:21 +01:00
Blaz Kristan
990d0f6e3e Fix for skipping reset of segments when changing spacing. 2021-12-21 18:26:51 +01:00
Blaz Kristan
84624666ce Incorrect delete fix. 2021-12-21 18:16:25 +01:00
Blaz Kristan
8bd716c056 Prevent undefind FX behaviour on millis() rollover 2021-12-20 16:43:47 +01:00
Andy Hofmann
cd95abb2a1 Usermod quinled-an-penta: Updated IOs for v1r1 release (#2429)
* UM QuinLED-An-Penta: First version

* UM QuinLED-An-Penta: Made OLED seconds a setting; small improvements

* UM QuinLED-An-Penta: Fixed unique ID

* Merge branch 'master' of https://github.com/Aircoookie/WLED

* UM QuinLED-An-Penta: Fixed config loading

* UM QuinLED-An-Penta: Replaced ledcRead() with calculating the percentage

* UM QuinLED-An-Penta: Fixed temp sensor readings

* UM QuinLED-An-Penta: Removing OLED bus clk setting

* UM QuinLED-An-Penta: ETH support, lots of OLED improvements

* UM quinled-an-penta: v1r1 adjustments
2021-12-20 01:41:37 +01:00
Blaz Kristan
1270f2d577 Sync segment options.
Freeze effect.
Repeat last segment until end.
2021-12-17 20:33:48 +01:00
Blaž Kristan
c27117e99e Merge pull request #2415 from Aircoookie/i2c-sharing
Pin manager support for sharing multipin buses.
2021-12-16 19:57:08 +01:00
Blaž Kristan
28556790d6 Removed loadInfo() in animated staircase. 2021-12-14 10:35:50 +01:00
Blaž Kristan
41c9bb63a0 Pin manager support for sharing multipin buses. 2021-12-14 09:38:38 +01:00
Roman Reitschmied
7d5e2466f0 add ability get LDR value from other usermods (#2408) 2021-12-12 00:31:54 +01:00
Roman Reitschmied
d3f35955d6 New flexible usermod for seven segment displays (#2409)
* add first version

* added max/min brightness to autoldr functionality

* added more information to the readme
2021-12-12 00:31:21 +01:00
cschwinne
fb338c0728 Button preset call mode
(stops main segment values from being applied to all presets)
2021-12-11 23:44:21 +01:00
cschwinne
2ce8f1ee5d Skip first and apply preset fixes 2021-12-11 19:30:11 +01:00
Blaz Kristan
3f0258e215 Playlist corrupting JSON buffer bugfix. 2021-12-10 20:45:37 +01:00
Christian Schwinne
e72a8d999f Merge pull request #2393 from guardmedia/blends-effect
Improved speed and reduced memory usage for Blends effect
2021-12-10 18:42:46 +01:00
Blaž Kristan
fed16fd14e Fix for disconnects on ESP8266 with static JSON buffer 2021-12-10 17:30:57 +01:00
Blaž Kristan
5dbc45ecb9 Fix for incorrectly placed release JSON buffer. 2021-12-10 17:06:04 +01:00
Blaz Kristan
094bdb29b6 Fix for >10 buttons. 2021-12-08 22:41:16 +01:00
cschwinne
9e6866c160 0.13.0-b6 2021-12-08 01:22:48 +01:00
cschwinne
7101ad81c4 No auto white for RGB DDP bus 2021-12-07 13:37:28 +01:00
cschwinne
8643263227 Add two new Fairy FX modes 2021-12-07 11:03:41 +01:00
Tyler Walters
eab132ed32 Improved speed and reduced memory usage for Blends effect
color_blend() function only generates 256 colors from the palette before repeating. Improved the function to use either 256 max or the segment length if shorter.
2021-12-06 22:17:35 -05:00
Christian Schwinne
66bad2b6f8 Single json buffer (#2336)
* Single/static JSON buffer for all requests.

* Missing json.cpp changes.

* Async fix.

* Added conditional compile (WLED_USE_DYNAMIC_JSON).

* Advanced locking with time-out.

* Missing releaseJSONBufferLock() on error response.

* Fix for config saving.

* Fixes and optimisations.
Dadded debugging information.

* Fix for ledmaps.

* No unsolicited serial sending if GPIO1 allocated

* Stray semicolons

* Fix JSON ledmap

Co-authored-by: Blaz Kristan <blaz@kristan-sp.si>
2021-12-04 01:05:01 +01:00
cschwinne
46ec504743 Various fixes
Fixed ESP32 crash on Colortwinkles brightness change
Fixed setting picker to black resetting hue and saturation
Fixed auto white mode not saved to config
2021-12-03 20:36:37 +01:00
cschwinne
cadda12371 Fixed spacing LEDs not blanked if offset is changed 2021-12-02 00:52:36 +01:00
cschwinne
a643b56555 Fixed no color updated on full JSON state request 2021-12-01 23:03:30 +01:00
cschwinne
f7404085de Unload playlist on PL= 2021-12-01 00:20:33 +01:00
cschwinne
33036e7599 Fix sliders on page load if black is set 2021-12-01 00:16:43 +01:00
Christian Schwinne
f6e5b67f0d Merge pull request #2285 from Aircoookie/CCT-support
CCT (color white balance support)
2021-11-30 23:41:13 +01:00
Jeff Cooper
9547ac353d Save and load the e131 Proxy Universe setting in the DMX menu (#2365) 2021-11-30 23:10:23 +01:00
cschwinne
48339b19d4 Status LED support in Bus manager 2021-11-30 22:52:17 +01:00
cschwinne
11c7ffad4e Alexa and UDP sync CCT support 2021-11-28 04:01:58 +01:00
cschwinne
1973424e05 Remove JSON ledmap
(to be implemented in #2336 )
2021-11-28 01:27:51 +01:00
Christian Schwinne
16d97d3c63 Merge branch 'master' into CCT-support 2021-11-28 01:21:57 +01:00
cschwinne
3e6728fedb Only do auto white calc for busses with white channel
Revert auto white to global setting
Rounded /settings buttons by blazoncek
Removed obsolete script from /settings
2021-11-28 01:21:17 +01:00
cschwinne
3e9aea072d CCT blending setting 2021-11-27 16:49:42 +01:00
Blaz Kristan
9f3e66fff0 Cleaning up indentations (sorry but my OCD is strong). 2021-11-27 11:50:18 +01:00
cschwinne
624993fc89 CCT transitions 2021-11-27 01:33:48 +01:00
cschwinne
ba8a00764a cctFromRgb option 2021-11-26 20:18:38 +01:00
cschwinne
3dec4a6651 UI slider and CCT adjustments 2021-11-26 02:06:05 +01:00
cschwinne
02fb2550d0 Shorter link color 2021-11-24 12:38:54 +01:00
cschwinne
37bd525638 Improve link contrast 2021-11-24 11:04:50 +01:00
cschwinne
ea0f37f5b9 CCT bus manager logic simplification
CCT from RGB if none set (-1)
2021-11-24 11:02:25 +01:00
Blaz Kristan
97b3c3db7b Incrementing & random effects, palettes via JSON. 2021-11-23 20:05:51 +01:00
cschwinne
b97b6dc144 Remove F macro for "ps" 2021-11-23 13:17:33 +01:00
cschwinne
c8d5218c65 Updated outdated wiki links in readme 2021-11-22 22:23:51 +01:00
cschwinne
80a657965e Fixed preset cycle not working from preset called by UI 2021-11-22 21:41:04 +01:00
cschwinne
b3324d22f5 allowCCT performance improvement 2021-11-21 23:46:44 +01:00
cschwinne
31b7cdff9b Change effect names to be more consistent 2021-11-19 12:34:14 +01:00
Christian Schwinne
0465298507 Merge branch 'master' into CCT-support 2021-11-18 16:50:24 +01:00
cschwinne
d31e4c7815 Added getPinOwner
Only disable builtin LED if bus to avoid breaking other things on the pin
2021-11-17 11:13:07 +01:00
cschwinne
4af1f62aab Revert testing PIO changes 2021-11-17 01:19:04 +01:00
cschwinne
bc403440ba 0.13.0-b5 2021-11-17 01:18:19 +01:00
Christian Schwinne
38d8dfe5ab Improv support (#2334)
* Working Improv device identification

* Improv functional

* Cast fix

* Minor fix for two back-to-back Improv packets

* Improv checksum update and logic simplification

* Improved improv failed connection behavior
2021-11-16 23:20:26 +01:00
Blaz Kristan
eb92c0bbf5 Fix for ACCURATE auto-white mode. 2021-11-11 17:34:38 +01:00
Jeff Cooper
6df64d0d31 Fix a bug which prevents DMX channel mappings from loading correctly. (#1525) (#2328) 2021-11-10 00:29:04 +01:00
cschwinne
83753a5f81 Fixed no gamma correction for JSON individual LED control 2021-11-09 17:50:29 +01:00
Blaz Kristan
3161f5fa47 Fix for network 'pin' conflicts. 2021-11-09 17:46:05 +01:00
Christian Schwinne
5784092c1b Fix settings JS buffer too small (#2323) 2021-11-09 09:56:02 +01:00
Blaž Kristan
d6ad089c60 Merge pull request #2324 from yoeywire/DMXdefine
Corrected WLED_USE_DMX to WLED_ENABLE_DMX
2021-11-09 07:57:41 +01:00
yoeywire
446b4b084c Changed WLED_USE_DMX to WLED_ENABLE_DMX 2021-11-08 17:05:47 +01:00
Blaz Kristan
d590e01a58 Merge branch 'master' into CCT-support 2021-11-05 21:27:52 +01:00
Shaun Eccles-Smith
adeb9bccb1 Use New Issue Forms for Bug Reports (#2312)
* Convert to Github Issue Forms

* Remove pre-filled title

* Change bug report from textarea to input
2021-11-04 12:23:53 +01:00
cschwinne
b44ffffed8 Fixed DDP override 2021-11-03 16:14:01 +01:00
Christian Schwinne
2bdaf53ecf Merge pull request #2311 from underritoSR/master
Add MX-CST/CDT Time Zone
2021-11-03 09:14:22 +01:00
Rod Minor
46e7db6d94 Add MX-CST/CDT Time Zone
Add the Mexico City CST/CDT timezone.
The start and finish date for DLS differs with the US-CST/CDT rules.
2021-11-02 21:26:11 -06:00
Christian Schwinne
7e1920dc4b Remove ledCount (#2300)
Bus initialization on reading from eeprom
2021-10-31 11:57:41 +01:00
Blaž Kristan
a93f05c047 Multirelay button support. (#2284)
* Multirelay button support.
Added button hook for usermods.

* Added MultiRelay relay states to JSON state object

* Move button timings to constants

No delay waiting for double press on button 0 if no macro set

Co-authored-by: cschwinne <dev.aircoookie@gmail.com>
2021-10-31 11:57:03 +01:00
Christian Schwinne
00238247cd JSON in/decrementing (#2258)
* Initial JSON in/decrementing

* Segment brightness in/decrement

* Update json-increment (#2290)

* Add Basic Overlay support to Usermods.

* Add seven segment overlay usermod

* Add seven_seg debug build

* Add scrolling message to seven seg um

* Fixed red color on IP address

* bh1750

* Add msg scroll. Add MQTT and Config support

* Add readme

* Restore platformio.inii

* Edit comments

* Add strip off refresh option in LED settings. (#2259)

* Add strip off refresh option in LED settings.
New strip initialization logic.
Minor code clen-up.

* Dev code removal.

* Missing ethernet include

* Renamed mainseg to selseg

* Fix for preset cycling bounds.

* "Preset 0" bugfix.

* Auto segments only if segments were not modified

Co-authored-by: cschwinne <dev.aircoookie@gmail.com>

* Exclude virtual busses from current calculation (#2262)

* Refactor string usage

* 0.13.0-b4

* Fix MQTT Null publish

* Additional Flash string concat

* Add AKST/AKDT

* UM RGB-Rotary-Encoder: Properly used PinOwner

* Cycling bugfix.

Co-authored-by: Gregory Schmidt <gregory.b.schmidt@hotmail.com>
Co-authored-by: Blaž Kristan <blaz@kristan-sp.si>
Co-authored-by: Caleb Mah <calebmah@gmail.com>
Co-authored-by: ezcGman <ich@andy-hofmann.com>

* Working JSON preset cycle

* Fix some Codacy style issues

Co-authored-by: Gregory Schmidt <gregory.b.schmidt@hotmail.com>
Co-authored-by: Blaž Kristan <blaz@kristan-sp.si>
Co-authored-by: Caleb Mah <calebmah@gmail.com>
Co-authored-by: ezcGman <ich@andy-hofmann.com>
2021-10-30 14:42:17 +02:00
Andy Hofmann
b33e28835d New Usermod: QuinLED-An-Penta (#2296)
* UM QuinLED-An-Penta: First version

* UM QuinLED-An-Penta: Made OLED seconds a setting; small improvements

* UM QuinLED-An-Penta: Fixed unique ID

* Merge branch 'master' of https://github.com/Aircoookie/WLED

* UM QuinLED-An-Penta: Fixed config loading

* UM QuinLED-An-Penta: Replaced ledcRead() with calculating the percentage

* UM QuinLED-An-Penta: Fixed temp sensor readings

* UM QuinLED-An-Penta: Removing OLED bus clk setting

* UM QuinLED-An-Penta: ETH support, lots of OLED improvements
2021-10-30 11:38:09 +02:00
Blaž Kristan
f55f803531 Updated aut-white calculation. 2021-10-27 14:02:48 +02:00
Blaz Kristan
8ca298b299 Removed legacy auto-white calculation.
Introduced color mangling macros.
Minor optimizations/fixes.
2021-10-26 19:17:42 +02:00
Blaz Kristan
090e29effd Moved auto white calculation to bus manager.
Other minor fixes.
2021-10-25 20:15:42 +02:00
Blaz Kristan
0acca2e313 Cycling bugfix. 2021-10-22 23:24:41 +02:00
Blaz Kristan
0d77027f60 Bugfix for white value. 2021-10-22 07:21:47 +02:00
Blaz Kristan
39b7b3ad53 CCT (color white balance support) 2021-10-20 20:29:13 +02:00
Blaž Kristan
00f1b483eb Merge pull request #2279 from ezcGman/um-rgb-rotary-encoder
UM RGB Rotary Encoder: Now properly using the PinOwner class and unique ID
2021-10-17 14:16:18 +02:00
ezcGman
c3d48acb4c UM RGB-Rotary-Encoder: Properly used PinOwner 2021-10-17 01:24:23 +02:00
Blaž Kristan
392bda7d8c Merge pull request #2261 from AK5nowman/overlayum
Basic Usermod Overlay support & Seven Segment UM
2021-10-13 06:14:32 +02:00
Blaž Kristan
10cfcdab8c Merge pull request #2264 from AK5nowman/Alaskatime
Add AKST/AKDT Time Zone
2021-10-11 07:37:52 +02:00
Gregory Schmidt
3f71d3b250 Add AKST/AKDT 2021-10-10 19:09:48 -08:00
Gregory Schmidt
1b50fbab22 Additional Flash string concat 2021-10-10 17:24:36 -08:00
Gregory Schmidt
303fc65a6a Merge branch 'master' of https://github.com/Aircoookie/WLED into overlayum 2021-10-10 17:06:12 -08:00
Gregory Schmidt
445b6ee13f Fix MQTT Null publish 2021-10-10 17:05:55 -08:00
cschwinne
8afaac1e30 0.13.0-b4 2021-10-11 02:42:58 +02:00
Gregory Schmidt
0327f9428e Merge branch 'master' of https://github.com/Aircoookie/WLED into overlayum 2021-10-10 16:27:47 -08:00
Gregory Schmidt
a5de66bbd5 Merge branch 'overlayum' of https://github.com/AK5nowman/WLED into overlayum 2021-10-10 16:26:42 -08:00
Gregory Schmidt
d47157eec3 Refactor string usage 2021-10-10 16:26:14 -08:00
Christian Schwinne
f4b47ed399 Exclude virtual busses from current calculation (#2262) 2021-10-11 02:19:53 +02:00
Blaž Kristan
8b2145bd88 Add strip off refresh option in LED settings. (#2259)
* Add strip off refresh option in LED settings.
New strip initialization logic.
Minor code clen-up.

* Dev code removal.

* Missing ethernet include

* Renamed mainseg to selseg

* Fix for preset cycling bounds.

* "Preset 0" bugfix.

* Auto segments only if segments were not modified

Co-authored-by: cschwinne <dev.aircoookie@gmail.com>
2021-10-11 02:19:33 +02:00
Christian Schwinne
de454e8b78 Edit comments 2021-10-11 01:29:13 +02:00
Gregory Schmidt
6cd770b4c7 Restore platformio.inii 2021-10-09 11:29:41 -08:00
Gregory Schmidt
355525c248 Add readme 2021-10-09 11:14:52 -08:00
Gregory Schmidt
47d4e7381f Merge 'master' of Aircoookie/WLED into overlayum 2021-10-08 00:00:09 -08:00
Gregory Schmidt
5dac6690d7 Add msg scroll. Add MQTT and Config support 2021-10-07 23:56:57 -08:00
Christian Schwinne
b89f7180db Merge pull request #2257 from calebmah/bh1750-usermod
Add BH1750 usermod
2021-10-07 23:52:48 +02:00
Caleb Mah
2ebb837a15 bh1750 2021-10-08 02:11:39 +08:00
Blaž Kristan
849aa64678 Fixed red color on IP address 2021-10-07 14:11:47 +02:00
cschwinne
cbb12e1b7c Updated vid and changelog 2021-10-06 20:46:49 +02:00
Christian Schwinne
cc87ba4962 Merge pull request #2245 from Aircoookie/network-bus
Network bus/virtual WLED instances
2021-10-06 20:05:32 +02:00
Blaz Kristan
fb2e556726 Allow playlist as end preset in playlist.
Playlist chaining.
2021-10-06 19:01:56 +02:00
Blaz Kristan
3f0eb0a046 Code optimization, updated URL links. 2021-10-06 16:29:04 +02:00
cschwinne
7d6d9eddc4 Change virtual bus type range 2021-10-06 14:30:41 +02:00
Blaž Kristan
cf87da0ef3 Minor UI fixes. 2021-10-06 08:37:27 +02:00
Blaž Kristan
0775acedc0 Merge pull request #2250 from Proto-molecule/master
Fix Glyphs for 4 line display
2021-10-04 21:15:19 +02:00
Blaz Kristan
8f1cee2e61 Fixed mem calculation. 2021-10-04 19:44:46 +02:00
Blaz Kristan
caa9cc32d7 Removed double buffer.
Moved bri scaling into UDP function.
Prevent double DDP port allocation.
2021-10-04 19:41:20 +02:00
Blaž Kristan
b750f827c5 Merge pull request #2168 from itCarl/usermod_battery_status
Updated Usermod Battery Status Basic
2021-10-04 14:56:33 +02:00
Blaž Kristan
5d147163e5 Merge pull request #2251 from blazoncek/pwm-fan-usermod
Pwm fan usermod enhancement
2021-10-04 13:12:22 +02:00
Blaž Kristan
75fe1a19eb Merge master 2021-10-04 13:01:06 +02:00
Blaž Kristan
5c9405fffc Added configurable PWM fan parameters:
- min PWM value (%)
- IRQs per rotation
2021-10-04 11:48:03 +02:00
Proto-molecule
6457314794 Glyph fix for 4 line 2021-10-03 21:23:52 -07:00
Proto-molecule
84f4e3eedc Merge branch 'Aircoookie:master' into master 2021-10-03 18:13:01 -07:00
Blaž Kristan
b003ed3f03 PWM fan with temperature control usermod (#2246)
* PWM fan with temperature control usermod

* Fix for incorrect RPM reported.
2021-10-03 23:34:21 +02:00
cschwinne
330da137db Fixed virtual getPixelColor() returning scaled values 2021-10-03 22:01:50 +02:00
cschwinne
9e5d45d0de Optional custom start indices 2021-10-03 20:48:08 +02:00
Blaz Kristan
b5c15d97fa Fix for incorrect RPM reported. 2021-10-03 14:01:05 +02:00
cschwinne
6ddcba8917 Change currentPreset to byte
JSON API still returns -1 for no preset
2021-10-03 13:53:37 +02:00
cschwinne
91598cbbbf Remove Total LEDs field 2021-10-03 12:23:24 +02:00
Blaz Kristan
772c80aa85 PWM fan with temperature control usermod 2021-10-03 10:33:17 +02:00
Proto-molecule
a28345d858 Merge branch 'Aircoookie:master' into master 2021-10-02 12:33:02 -07:00
Proto-molecule
05b532b9eb Add new Usermod (#2244) 2021-10-02 20:19:12 +02:00
cschwinne
0b0d18f182 Fix preset variable 2021-10-02 20:10:52 +02:00
Blaz Kristan
c1b0877956 Bus implementation.
Added separate DDP listener.
LED settings overhaul.
Minor fixes:
- reduced LED memory
- boot brightness fix
- reduced debug frequency
- added usermod time spent debug
- mDNS glitch fix
2021-10-02 15:07:02 +02:00
Blaz Kristan
46b66c76ef Merge pbolduc/WLED/feature/upd-ddp-send into network-bus 2021-10-02 10:48:48 +02:00
Gregory Schmidt
d00b4335b5 Add scrolling message to seven seg um 2021-10-01 21:34:20 -08:00
Proto-molecule
7a129e6de1 Add new Usermod 2021-10-01 20:46:58 -07:00
cschwinne
17c20276a9 Make sbuff local
This should save 4 bytes per ESPAsyncE131 instance
2021-10-01 20:26:23 +02:00
Blaž Kristan
dc9dedf220 Fixed pin reservations. (#2214)
* Fixed pin reservations.
Added ethernet pin reservations.
Minor tweaks in usermods.

* Optional ADA compile (not default, free GPIO3 use)

* Move ethernet definitions

Remove pin 3 used check

Co-authored-by: cschwinne <dev.aircoookie@gmail.com>
2021-09-30 16:30:44 +02:00
Gregory Schmidt
3ac772badc Add seven_seg debug build 2021-09-29 20:59:02 -08:00
Gregory Schmidt
22fc58d93b Add seven segment overlay usermod 2021-09-29 20:01:26 -08:00
Gregory Schmidt
ccd3152b24 Add Basic Overlay support to Usermods. 2021-09-29 19:23:32 -08:00
Blaž Kristan
7d929dcde6 Merge pull request #2236 from scottrbailey/ir_bri_fix
Add colorUpdated call
2021-09-29 17:44:20 +02:00
Scott Bailey
3a874bc8c7 Add colorUpdated call 2021-09-28 09:56:00 -07:00
cschwinne
8453cd82e9 Fixed DMXmap 2021-09-27 22:51:40 +02:00
cschwinne
f62e56b7ec Merge branch 'master' of https://github.com/Aircoookie/WLED 2021-09-27 14:20:41 +02:00
cschwinne
2ac90bbb96 Fixed sunrise/set UTC offset 2021-09-27 14:20:21 +02:00
Blaž Kristan
f85f2d5d22 Merge pull request #2203 from protoplasma-mx/master
Updated links in files generated by cdata.js
2021-09-26 20:20:23 +02:00
Blaz Kristan
a94269ceb9 Novosibirsk time-zone. 2021-09-26 17:21:32 +02:00
Blaž Kristan
476ac263fb Merge pull request #2227 from blazoncek/master
Add "on":true to playlist JSON
2021-09-24 16:07:01 +02:00
Blaž Kristan
51a4f61a8f Add "on":true to playlist JSON 2021-09-24 12:20:20 +02:00
Blaz Kristan
267f5159a3 Wled math bugfix. 2021-09-23 20:38:50 +02:00
cschwinne
96422de031 0.13.0-b3 2021-09-21 23:37:35 +02:00
Blaž Kristan
f2043dc181 Merge pull request #2219 from blazoncek/analog-invert
Fix for missing inverted analog.
2021-09-21 12:19:15 +02:00
Blaž Kristan
e416ec9279 Removed dev types. 2021-09-21 12:05:28 +02:00
Blaž Kristan
5eb4ffb1cc Fix for missing inverted analog. 2021-09-21 11:59:23 +02:00
Christian Schwinne
8fae964ee8 Allocate segment data based on currently active segments (#2217) 2021-09-20 21:22:50 +02:00
cschwinne
baf49b88f4 Semi-working segment on/off transition 2021-09-20 21:22:04 +02:00
Phil Bolduc
3577da05ac Avoid redundant localIP calls, each call takes 0.700 us on ESP32 240Mhz (#2206)
* Avoid redundant localIP calls, each call takes 0.700 us on ESP32 240Mhz

* Fall through to check Wifi local ip if not connected to ETH

* Changed local var from ipAddress to localIP to better reflect content
2021-09-19 19:51:54 +02:00
Christian Schwinne
b8e8028eb9 Merge pull request #2184 from Aircoookie/fx-mods
FX optimisations and segment names
2021-09-19 15:16:23 +02:00
Gabriel
a899666e68 Merge branch 'master' of https://github.com/protoplasma-mx/WLED 2021-09-18 09:04:54 -05:00
Gabriel
10a52f8cf9 Updated cdata.js so generated files point to knoWLEDge 2021-09-18 09:02:37 -05:00
Gabriel
72d04a0120 Updated reference to docs on readme to knoWLEDge 2021-09-18 00:23:51 -05:00
Gabriel
6dbed30008 Updated Web UI dev docs reference 2021-09-18 00:03:59 -05:00
cschwinne
c5eac298e6 Do not delete name if segment bounds are unchanged 2021-09-18 01:20:17 +02:00
cschwinne
bc18eda336 Segment name UI changes
Text field accessible by edit icon
Replaced magnifier with iconfont
Use woff2 font format
Fix scaled checkboxes visible in UI settings header
2021-09-18 00:31:39 +02:00
Blaž Kristan
3cefb14297 Merge pull request #2195 from scottrbailey/sp511
add env for sp511
2021-09-16 08:53:47 +02:00
Scott Bailey
4bc401278e add env for sp511 2021-09-15 18:44:09 -07:00
cschwinne
d7e3765efe Fix segment creation 2021-09-14 23:35:04 +02:00
Christian Schwinne
3d51d1e345 Merge pull request #2175 from henrygab/revert_pr1902
Revert changes from PR1902
2021-09-14 00:29:59 +02:00
cschwinne
bd23942893 Fixed IR JSON cmd string (closes #2187 ) 2021-09-12 01:37:41 +02:00
cschwinne
c8610b8ad2 Small improvements to segment names 2021-09-12 01:15:51 +02:00
Blaz Kristan
d0440122b9 Bugfix for AutoSave & 4LD. 2021-09-12 01:08:19 +02:00
Christian Schwinne
8d4636bbab Merge pull request #2170 from scottrbailey/error-12-fix
Fix error 12 issues
2021-09-11 14:32:06 +02:00
cschwinne
f59c6e7a7c Replace wiki link in readme to knoWLEDge 2021-09-11 11:18:39 +02:00
Christian Schwinne
c24ab1b21d Auto create segments setting (#2183) 2021-09-11 01:17:42 +02:00
cschwinne
6a01658355 Use pbolduc fork of AsyncTCP
(fixing flicker with upstream AsyncTCP v1.1.1)
2021-09-09 17:50:59 +02:00
cschwinne
f1e2439e66 Slight IR JSON simplefication
Check for missing file
No duplicate cmd object
2021-09-09 12:05:02 +02:00
Blaz Kristan
4d89ed701d FX optimisations.
Added segment names.
2021-09-08 23:10:54 +02:00
Blaz Kristan
e80594d61d Bugfix (sclPin, sdaPin).
Removed unnecessary static_cast.
2021-09-07 17:03:46 +02:00
cschwinne
83c6f72eb0 Fix segment runtime not reset on FX change via HTTP API 2021-09-05 01:28:00 +02:00
cschwinne
e26299b998 Revert some small syntactical changes 2021-09-05 00:39:47 +02:00
Scott Bailey
a839809eb8 change random mode choice on presetFallback 2021-09-03 00:14:07 -07:00
Scott Bailey
88ceba59cf Fix error 12 issues 2021-09-02 22:56:49 -07:00
itCarl
f368bbec32 added MQTT support, Battery voltage to Info, circuit diagram to readme, minor fixes 2021-09-02 15:41:19 +02:00
Henry Gabryjelski
021c4ba68a Revert changes from PR1902 2021-08-29 11:49:06 -07:00
Christian Schwinne
54f4658dae Added JSON API over serial support (#2156)
* Added JSON API over serial support

* Disable Serial API if pin 3 is used

Disable serial response if pin 1 is used
2021-08-26 11:04:27 +02:00
Blaž Kristan
dbc67e077d Merge pull request #2134 from scottrbailey/sr_palettes
Add new palettes from SR branch
2021-08-26 06:52:56 +02:00
Scott Bailey
e968917dbc rename palette arrays 2021-08-25 10:16:30 -07:00
Scott Bailey
d8240bb683 Changing some palette names 2021-08-25 09:17:03 -07:00
Blaž Kristan
b481c13829 Sync groups (#2150)
* Added UDP sync groups.

* Shortened string.

* Changed sync default to group 1 only.

* Make packets with version < 9 group 1

* Send sync group options as bytes, parse in JS

Co-authored-by: cschwinne <dev.aircoookie@gmail.com>
2021-08-25 16:39:12 +02:00
Blaž Kristan
77c0ba990d Bugfix for calling FX=~ from within playlist preset. 2021-08-24 06:10:59 +02:00
Henry Gabryjelski
1d4487b6cd Ethernet configuration fix, improve PinManager (#2123)
* Improved pin manager, ethernet config

* Ethernet is configured prior even to LED pins
* Pin Manager allocation / deallocation functions
   now take an "ownership" tag parameter, helping
   avoid accidentally free'ing pins that were allocated
   by other code
* Pin Manager now has ability to allocate multiple
  pins at once; Simplifies error handling

* Fix operator precedence error

Bitwise AND has lower precedence than the
relational "greater than" operator.

* PinManager update for some user modules

* don't build everything...

* Final step to reduce RAM overhead

* update comment

* remove macros

* Remove leftover allocated

* Init ethernet after settings saved

Co-authored-by: Christian Schwinne <dev.aircoookie@gmail.com>
2021-08-23 14:14:48 +02:00
Blaž Kristan
ff8145b745 Merge pull request #2101 from blazoncek/fix-mqtt-pir
Fix for missing off-only MQTT messages.
2021-08-21 11:10:41 +02:00
Blaz Kristan
530e8b39e5 Added SSD1306 SPI display option to 4 Line Display 2021-08-20 23:58:09 +02:00
Blaz Kristan
50aeee288b Merge branch 'master' of https://github.com/aircoookie/WLED 2021-08-20 23:22:53 +02:00
Blaz Kristan
72e001b0d5 Bash and Wnindows CMD scripts for updating multiple WLEDs. 2021-08-20 23:20:04 +02:00
Maximilian Mewes
f04c9d101e Added usermod "battery status basic" (#2127)
* added usermod battery_status_basic

* test.. something is wrong

* Squashed commit of the following:

commit 0f845527c53f838e2c68d50ec3e9d6c68c4cee46
Author: itCarl <mewes.maximilian@gmx.de>
Date:   Tue Aug 10 18:35:15 2021 +0200

    updated readme and added image showing info modal

commit 055579fcf71796519d00566452030f31798121d0
Author: itCarl <mewes.maximilian@gmx.de>
Date:   Mon Aug 9 20:53:07 2021 +0200

    small map function fix

commit 811614cf9e73f4731acb234d0d210a7b19565e9a
Author: itCarl <mewes.maximilian@gmx.de>
Date:   Mon Aug 9 19:35:21 2021 +0200

    updated ui

commit cadf2e23b7
Author: itCarl <mewes.maximilian@gmx.de>
Date:   Mon Aug 9 16:07:32 2021 +0200

    added usermod battery_status_basic

* updated readme, changed USERMOD_BATTERY_MIN_VOLTAGE default to 2.6 volt

* fixed readme image file naming

* added usermod settings for runtime changes

* fixed copy and paste mistake

* undo ui changes

* reworked addToJsonInfo() to make it compatible with the standard Info page.

* removed images from readme

* added ESP32 support

* updated readme
2021-08-20 20:42:46 +02:00
Ahmed Shehata
2ecc53ba56 UDP Signal color correction (#1902)
* added ui changes for saturation in sync

* added setters/getters for hsv settings

* added color correction logic

* faster algorithm for color conversion

* added save/load config to fs

* adjusted value scale

* move color functions to colors.cpp

* remove unchecked file

* Various small changes

Moved settings location in sync settings
Changed wording from hyperion to live
Moved code into setRealtimePixel(), reducing duplication and enabling the functionality for DMX streams

Co-authored-by: Christian Schwinne <dev.aircoookie@gmail.com>
2021-08-19 18:24:41 +02:00
Christian Schwinne
3eb1fe0eb2 Merge pull request #2144 from coliss86/patch-1
Fix formatting of the first title
2021-08-19 08:44:43 +02:00
coliss86
aec998acc1 Fix formatting of the first title 2021-08-18 22:21:31 +02:00
cschwinne
91e758f66f Fixed JSON IR remote not working with codes greater than 0xFFFFFF (fixes #2135) 2021-08-18 02:10:40 +02:00
cschwinne
441416b241 Fixed edge case with transition 0 2021-08-18 01:59:01 +02:00
Christian Schwinne
e541d8697e Merge pull request #2140 from Aircoookie/ws-ping
Added application level pong websockets reply (#2139)
2021-08-17 17:56:29 +02:00
cschwinne
4b817208aa Added application level pong websockets reply (#2139) 2021-08-17 12:47:01 +02:00
Scott Bailey
7fea0c3244 Add new palettes from SR branch 2021-08-12 12:58:51 -07:00
cschwinne
bd13336256 Fixed undesirable boot color transition 2021-08-06 02:08:36 +02:00
Christian Schwinne
815940913b Merge pull request #2113 from tschundler/master
Fix ordering in platformio_override.ini.sample
2021-08-03 22:17:43 +02:00
Ted Schundler
f7191c0381 Fix ordering platformio_override.ini.sample
The flag examples must be after the build_flags line to be usable.
2021-08-01 20:23:05 -07:00
Blaz Kristan
07d11c845c Fix for missing off-only MQTT messages. 2021-07-28 22:50:29 +02:00
Blaž Kristan
2e9bd477d9 Upload files & skinning (#2084)
* Skinning WLED & uploading files.
Backup & restore configuration & presets.
External holidays.json

* Option for segment count instead of stop.

* Small fixes and improvements

* Further improvements

* Enable custom CSS by default

Co-authored-by: Christian Schwinne <dev.aircoookie@gmail.com>
2021-07-26 00:10:36 +02:00
Christian Schwinne
b058fb8db4 Merge pull request #2093 from blazoncek/PIR-sensor-update
Added PIR  option to trigger only if WLED is off.
2021-07-23 19:35:57 +02:00
Blaz Kristan
9f0f6181a1 Added PIR option to trigger only if WLED is off. 2021-07-23 18:43:51 +02:00
Christian Schwinne
f702e1a80d Merge pull request #2091 from blazoncek/white-slider-fix
White slider fix.
2021-07-22 23:55:12 +02:00
Blaz Kristan
e1527fcbb9 White slider fix. 2021-07-22 15:36:33 +02:00
Blaž Kristan
9ba7e5d567 Fix for not honouring enabled state for PIR usermod. (#2090) 2021-07-22 14:41:11 +02:00
Blaž Kristan
02b6d53544 Rotary Encoder Compilation fix. (#2085)
* Compilation fix.

* Make rotary encoder usermod runtime configurable.
2021-07-20 13:41:30 +02:00
cschwinne
123bd0bb92 v0.13.0-b2
Reduced unneeded websockets pushes
2021-07-11 02:38:31 +02:00
cschwinne
6a8ed1192f Accept hex color strings in individual LED API
Version bump to 0.13.0-b2 "Toki"
Fixed transition property not applying unless power/bri/color changed next
Moved transition field below segments (temporarily)
2021-07-10 16:14:17 +02:00
Andy Hofmann
0862859f93 New usermod: Support for RGB Rotary Encoder Board (#2068)
* Added RGB Rotary Encoder usermod v1

* RGB rotary encoder UM: Readme; Added example video

* RGB rotary encoder UM: Readme; Added example video

* RGB rotary encoder UM: Fixed getJsonValue usage

* RGB rotary encoder UM: Removed spaces in JSON keys

* RGB rotary encoder UM: Cleanup readFromConfig

* RGB rotary encoder UM: Cleaned up type usages

* RGB rotary encoder UM: Fixed crash on re-enable
2021-07-09 20:25:35 +02:00
Henry Gabryjelski
3ad336a1eb Bug 2064, 2063 - PinManager usage (#2066)
* Fix 2063 - Do not free pins unless allocated

* Fix 2064: Allocate pins used for Ethernet

* Fix obvious compilation errors.

* Fix multiple bugs...

* pinsAllocated[2] set twice due to copy/paste bug.
* wrong pin allocated for ETH_CLOCK_GPIO17_OUT due to copy/paste bug

* Stylistic change per PR review

* Stylistic change per PR review

* attempt to allocate pin for "default" button

* remove extra local variable

* check return value from ETH.begin()

Co-authored-by: Christian Schwinne <dev.aircoookie@gmail.com>
2021-07-09 20:06:48 +02:00
cschwinne
a17f83cedd Renamed NOTIFIER_CALL_MODE_ to CALL_MODE_ 2021-07-09 18:54:28 +02:00
cschwinne
2c6850f6e4 Fixed presets using wrong call mode (e.g. causing buttons to send UDP under direct change type)
Increased hue buffer
2021-07-09 18:42:52 +02:00
cschwinne
5da47636cf Busses extend total configured LEDs if required (closes #2056 )
Fixed extra button pins defaulting to 0 on first boot
2021-07-09 16:25:23 +02:00
cschwinne
e04b965659 Peek uses the main websocket connection 2021-07-08 02:01:17 +02:00
Aircoookie
17d2fb80f2 More robust initial resource loading in UI 2021-07-07 23:45:53 +02:00
Blaž Kristan
14b7ec2a80 Added support for ESP32 S2. (#2067)
Updated Wemos Shield parameters.
2021-07-07 01:56:07 +02:00
srg74
f27b31b581 Quick fix fro paltformio.ini (#2060)
* Added support for H803FW controller

* Create usermod_bme280.cpp

* Create usermod_bme280.cpp

* Added BME280 sensor

* Update readme.md

* Update usermod_bme280.cpp

* Update platformio.ini

* Update for lightweight sensor

* Added travis build badge

* Update readme.md

* Update readme.md

* Update .gitignore

* Changed ldscript for a file system

* Update NpbWrapper.h

* Update .gitignore

* Delete wled-ci.yml

* Added usermod for ST7789 display

Functionality tested with ESP32. Works with main WLED and @blazoncek fork.

* fixes

* Update .gitignore

* Firmware updates!!!

Updated official @Aircoookie firmware to v0.12.0-b4 build 2103290 and developer @blazoncek firmware to latest v0.12.0-b3 build 2103282.

* Updated platformio.ini for Universal Wemos Shield board

* Fixed errors in env:wemos_shield_esp32

- lib_deps must be for esp32;
- Commented global lib_deps for Dallas sensor.
2021-07-06 09:51:20 +02:00
Louis Beaudoin
8c44147a45 Usermod Settings polishing/documentation (#2061)
* Testing new wrapper functions to read Usermod config

* Usermod Settings polishing
- remove getBoolFromJsonKey() (no longer needed), fix getValueFromJsonKey(element, destination, defaultvalue)
- Update Usermod Settings html "number" field to use step="any", and make wider to make maximum values fully visible
  - step="any" allows viewing/submitting full 7/8-digit float values, and the arrow buttons step by 1 now, instead of .00001 (which wasn't good for integers or floats)
  - html wasn't generated/compressed yet

* Update usermod_v2_example.h with more complete example and documentation for Usermod Settings
- readFromConfig() has three options for how to load values from the config JSON, we need to pick one

* Update/rename usermode_rotary_brightness_color, to be used as an example of more robust parsing Usermod Settings values

* Update Usermod example, rename getValueFromJsonKey() to getJsonValue()
- chose single readFromConfig() pattern
- demonstrating 3-argument getJsonValue()
- remove leftover printf in getJsonValue()

Co-authored-by: Louis Beaudoin <louis@embedded-creations.com>
2021-07-05 23:14:57 +02:00
cschwinne
ec05215a5e Update ArduinoJSON to 6.18.1 2021-07-04 18:52:05 +02:00
cschwinne
5903e8256f Fixed preset immediately deselecting when set via HTTP API PL= 2021-07-04 13:29:59 +02:00
cschwinne
c879351063 JSON IR improvements
Restored support for "PL=~" mistakenly removed in 2106300
2021-07-04 13:23:45 +02:00
cschwinne
1bb7e36a65 More compact playlist entries 2021-07-04 00:55:32 +02:00
Christian Schwinne
793a01f7ca Bus wrapper simplification (#2054)
* Use RMTN method

* Simplify BusWrapper

* Update PlatformIO configuration

* Fix non-included dependencies
2021-07-03 13:52:23 +02:00
cschwinne
40c8fdbf64 Added WebSockets support to UI 2021-07-02 01:46:42 +02:00
cschwinne
dc01c907f1 Send websockets on every state change 2021-07-02 00:24:14 +02:00
cschwinne
801df94446 Update libraries 2021-07-01 21:20:52 +02:00
cschwinne
0197d89976 Added MQTT button option 2021-07-01 20:51:52 +02:00
Christian Schwinne
e16a67242e Merge pull request #2011 from blazoncek/multi-button-update
Added MQTT support for buttons and simplified switch.
2021-07-01 14:57:12 +02:00
cschwinne
4c678a5010 Fixed a minor visual issue with slider trail not reaching thumb on low values 2021-07-01 14:56:18 +02:00
srg74
3754088a44 Updated [env] for Universal Wemos Shield board (#2049)
* Added support for H803FW controller

* Create usermod_bme280.cpp

* Create usermod_bme280.cpp

* Added BME280 sensor

* Update readme.md

* Update usermod_bme280.cpp

* Update platformio.ini

* Update for lightweight sensor

* Added travis build badge

* Update readme.md

* Update readme.md

* Update .gitignore

* Changed ldscript for a file system

* Update NpbWrapper.h

* Update .gitignore

* Delete wled-ci.yml

* Added usermod for ST7789 display

Functionality tested with ESP32. Works with main WLED and @blazoncek fork.

* fixes

* Update .gitignore

* Firmware updates!!!

Updated official @Aircoookie firmware to v0.12.0-b4 build 2103290 and developer @blazoncek firmware to latest v0.12.0-b3 build 2103282.

* Updated platformio.ini for Universal Wemos Shield board
2021-07-01 12:05:02 +02:00
Christian Schwinne
c4f084a991 Merge JSON ircodes (#2048)
* add decodeIRJson and JSON remote option

* handle JSON API commands also

* removed code that forced IR codes in a certain range to be decoded by decodeIR24. Generate default ir.json files for currently supported remotes.

* comment out printing API commands in IR handling

* refactor decodeIRJson to change how ir.json is loaded add support for calling several c functions

* Handle setting palette when effect is still on default solid and will not display it

* remove colorUpdated notifier that was pasted in accidentally

* Update to handle both 24-key and 24-key old remotes (#1969)

* Update readme.md

* Update ir.cpp

Handle both 24-key and 24-key old in decodeIR switch statement

* Re-add JSON remote option

Co-authored-by: Scott Bailey <scottrbailey@gmail.com>
Co-authored-by: Artacus <40248830+scottrbailey@users.noreply.github.com>
2021-07-01 00:17:07 +02:00
cschwinne
4c73df4ba6 Shorten input size class names 2021-07-01 00:01:27 +02:00
cschwinne
4aa53aa5a5 Adjust input field widths 2021-06-30 21:53:22 +02:00
cschwinne
7483d3b229 Fixed settings page broken by using "%" in input fields (fixes #1516 ) 2021-06-30 18:21:56 +02:00
cschwinne
8b6cc708e7 Fixed a problem with disabled buttons reverting to pin 0 causing conflict 2021-06-30 12:33:51 +02:00
cschwinne
200960899e Removed preset cycle 2021-06-30 01:48:38 +02:00
cschwinne
599a456c81 Version bump to 0.13.0-b0 "Toki"
-   Added playlist editor UI
-   Reordered segment UI and added offset field
-   Raised maximum MQTT password length to 64 (closes #1373)
2021-06-30 01:23:35 +02:00
Christian Schwinne
4b46502d22 Playlist UI (#2046)
* Test 1

* State 2

* Playlist UI progress

* Playlist saving

* Playlist saving

* Playlist object array

* Added Offset to segment options

* Positioning

* Playlist UI complete
2021-06-30 01:01:15 +02:00
cschwinne
7233c55428 Segment Offset 2021-06-30 00:45:36 +02:00
Christian Schwinne
a58c5cce78 Merge pull request #1967 from poelzi/max_segments
Allow to override the max segments setting when compiling
2021-06-29 23:43:20 +02:00
Christian Schwinne
0b23bf65b3 Merge pull request #2045 from blazoncek/better-num-handling
Better number handling in Usermod settings.
2021-06-29 23:39:33 +02:00
Blaz Kristan
bc0a3f8a47 Better number handling in Usermod settings. 2021-06-29 18:36:55 +02:00
Christian Schwinne
9b2a0102be Merge pull request #2043 from blazoncek/v2-usermod-settings
Usermod settings v2
2021-06-27 22:22:32 +02:00
Aircoookie
04b4ef6d85 Regenerate html_settings.h 2021-06-27 22:19:03 +02:00
Blaz Kristan
9e8aadb750 Usermod settings v2
- added POST field parsing
- simpler handling in readFromConfig()
2021-06-27 15:32:33 +02:00
cschwinne
0ae0f40628 Fixed four line display usermod compilation
Thanks @Serg74!
2021-06-25 23:42:53 +02:00
cschwinne
af9aa7d201 Do not compile in example usermod 2021-06-25 12:15:23 +02:00
cschwinne
4cd3a614de Fixed preset only disabling on second effect/color change (fixes #2038 ) 2021-06-25 10:12:21 +02:00
cschwinne
1e5420e6a7 readFromConfig() update 2021-06-25 01:26:15 +02:00
cschwinne
660de0b4e5 Auto-create segments based on configured busses 2021-06-24 02:29:14 +02:00
cschwinne
b73aaecd22 Fix 4MB 1MB FS partition table 2021-06-21 13:22:14 +02:00
cschwinne
c831d62bc3 Added 2 Ethernet boards and split Ethernet configs into separate file 2021-06-20 00:14:09 +02:00
cschwinne
1539e703e9 Use sprintf_P for color array generation 2021-06-19 11:13:05 +02:00
Christian Schwinne
f43bf03768 Merge pull request #2031 from DickSwart/dickdev
made sn_photorisistor configarable
2021-06-18 19:30:43 +02:00
cschwinne
495f7f190f Fixed DOS on Chrome tab restore causing reboot 2021-06-18 13:50:22 +02:00
Dick Swart
16216b9eb9 made sn_photorisistor configarable 2021-06-18 13:07:41 +12:00
cschwinne
dfdb22f584 Optimized JSON buffer usage (pre-serialized color arrays) 2021-06-17 19:46:18 +02:00
Christian Schwinne
0b264176bc Merge pull request #2024 from Aircoookie/dependabot/npm_and_yarn/normalize-url-4.5.1
Bump normalize-url from 4.5.0 to 4.5.1
2021-06-15 23:20:46 +02:00
Christian Schwinne
bde70a27f0 Merge pull request #2025 from Aircoookie/dependabot/npm_and_yarn/glob-parent-5.1.2
Bump glob-parent from 5.1.1 to 5.1.2
2021-06-15 23:19:57 +02:00
cschwinne
7d2f5f0799 Fixed div by 0 when using palette on single LED segment 2021-06-15 20:12:20 +02:00
cschwinne
7610ab7a8d Update logo 2021-06-14 01:58:12 +02:00
dependabot[bot]
51db653b1a Bump glob-parent from 5.1.1 to 5.1.2
Bumps [glob-parent](https://github.com/gulpjs/glob-parent) from 5.1.1 to 5.1.2.
- [Release notes](https://github.com/gulpjs/glob-parent/releases)
- [Changelog](https://github.com/gulpjs/glob-parent/blob/main/CHANGELOG.md)
- [Commits](https://github.com/gulpjs/glob-parent/compare/v5.1.1...v5.1.2)

---
updated-dependencies:
- dependency-name: glob-parent
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-11 20:27:21 +00:00
dependabot[bot]
dc4e4395a9 Bump normalize-url from 4.5.0 to 4.5.1
Bumps [normalize-url](https://github.com/sindresorhus/normalize-url) from 4.5.0 to 4.5.1.
- [Release notes](https://github.com/sindresorhus/normalize-url/releases)
- [Commits](https://github.com/sindresorhus/normalize-url/commits)

---
updated-dependencies:
- dependency-name: normalize-url
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-11 12:49:20 +00:00
cschwinne
623694ab73 Playlist additions 2021-06-10 02:52:20 +02:00
cschwinne
374457df70 A few small fixes 2021-06-09 12:57:16 +02:00
Christian Schwinne
7885dddef2 Create FUNDING.yml 2021-06-09 12:43:29 +02:00
Christian Schwinne
73d6cc1e54 Merge pull request #2020 from blazoncek/endless-playlist-fix
Endless playlist fix.
2021-06-07 22:29:17 +02:00
Blaz Kristan
8fdf84068d Optimisations. 2021-06-07 21:05:31 +02:00
Blaž Kristan
131625bb53 Endless playlist fix. 2021-06-07 14:16:29 +02:00
Christian Schwinne
29c9e5cb17 Merge pull request #2015 from RedNax67/master
Added support for raw 16bpp RGB565 images, and added elekstube env to examples in platformio.ini
2021-06-04 13:11:23 +02:00
Xander X
52b60fd6a6 Added elekstube env to examples in platformio.ini
Added support for raw 16bpp RGB565 images. Upload /0.bin etc.
See https://github.com/neptune2/EleksTube-IPS-Retro-Nixie-Digits for
how to get the original digits.
2021-06-04 10:39:31 +02:00
Blaž Kristan
d6337f7500 Added MQTT support for buttons and simplified switch.
Added PIR sensor option.
2021-06-03 12:18:11 +02:00
Christian Schwinne
625e04d208 Merge pull request #2007 from Aircoookie/dependabot/pip/urllib3-1.26.5
Bump urllib3 from 1.26.4 to 1.26.5
2021-06-02 14:25:13 +02:00
Christian Schwinne
6da657d3e2 Merge pull request #2008 from DickSwart/dickDev
added SN_Photoresistor usermod
2021-06-02 14:24:01 +02:00
Dick Swart
344c9e9238 added SN_Photoresistor usermod 2021-06-02 12:01:06 +12:00
Christian Schwinne
89b2b066ef Merge pull request #2004 from HyperAtlas/master
CRONIXIE check in json.cpp
2021-06-02 00:25:04 +02:00
dependabot[bot]
894e084c7f Bump urllib3 from 1.26.4 to 1.26.5
Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.26.4 to 1.26.5.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/1.26.4...1.26.5)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-01 22:24:59 +00:00
Aircoookie
2ba064b2a5 Merge branch 'master' of https://github.com/Aircoookie/WLED 2021-06-02 00:23:12 +02:00
Aircoookie
dfe065ef82 Fixed Pushbutton inverted not selectable 2021-06-02 00:22:57 +02:00
Christian Schwinne
7bd4b78470 Merge pull request #2006 from juanesf/master
Add diyHue repo to readme.md
2021-06-02 00:17:38 +02:00
juanesf
d7991a247d Add diyHue repo to readme.md
Wled has been supported by diyHue for some time, now it also supports Hue Sync (Entertainment) under UDP in the flask branch which will be the new master branch. 

https://github.com/diyhue/diyHue/pull/628
2021-06-01 12:31:52 -04:00
SpikeyHelmet
2178fd6ee9 CRONIXIE check in json.cpp 2021-06-01 06:37:35 +05:30
Christian Schwinne
7019ddb165 Merge pull request #1997 from Aircoookie/toki
More precise NTP timekeeping
2021-05-30 13:27:35 +02:00
Christian Schwinne
9f13763637 Merge branch 'master' into toki 2021-05-30 13:22:42 +02:00
Christian Schwinne
beeba27f46 Merge pull request #1977 from blazoncek/multi-button
Multi-button support.
2021-05-30 13:16:52 +02:00
cschwinne
315d4f225a Added analog button hue control 2021-05-30 02:03:32 +02:00
cschwinne
85489458d8 Small improvements
Settings cosmetic changes (spaces, smaller pin inputs, moved analog legend to wiki)
Uncommented STATUSPIN code (still defined out by default)
Removed adalight pin 3 lock
Added define for PSRAM
2021-05-30 01:31:04 +02:00
cschwinne
bfc7f56c4d Accurate UDP sync for NTP enabled instances 2021-05-30 00:08:24 +02:00
Blaz Kristan
7685f9b73d Ignore analogue pin -1 on ESP8266 2021-05-28 17:45:14 +02:00
Blaž Kristan
664fad96fa Fixed error-prone use of shifting. 2021-05-28 14:14:50 +02:00
Christian Schwinne
669a610e36 Merge branch 'master' into multi-button 2021-05-28 10:03:27 +02:00
Blaž Kristan
7e0d9cb48c Added:
- analogue FX speed
- analogue FX intensity
- analogue palette selection
- UI legend
2021-05-28 08:47:15 +02:00
cschwinne
7cbc9d21b5 Fixed difference calculation 2021-05-27 23:29:11 +02:00
cschwinne
55b26751ae Cache CORS preflight request 2021-05-27 11:09:57 +02:00
cschwinne
c2892d7887 Add UDP sync of system time 2021-05-27 02:02:02 +02:00
cschwinne
6c8bf090fe Small optimizations 2021-05-27 00:09:52 +02:00
Blaz Kristan
13bc378069 Fixes for analog. 2021-05-25 23:59:43 +02:00
cschwinne
8431d0bd5c Replace Time with Toki 2021-05-25 09:59:19 +02:00
cschwinne
852f758be3 Subsecond accuracy NTP 2021-05-24 19:05:34 +02:00
cschwinne
b455f432d5 Toki 1st experiment 2021-05-24 14:34:03 +02:00
stevelup
306cea60a1 Update usermod_v2_rotary_encoder_ui.h (#1988)
Typo DISLAY -vs- DISPLAY
2021-05-23 21:22:15 +02:00
stevelup
ba2e07c4b9 Update readme.md (#1987)
Typo DISLAY -vs- DISPLAY
2021-05-23 21:22:01 +02:00
cschwinne
9b796531b2 Fixed Sunrise calculation (atan_t approx. used outside of value range) 2021-05-23 18:49:23 +02:00
cschwinne
08d7a1c123 Set Nixie contents via JSON API
Elekstube mod support a subset of the cronixie display patterns
2021-05-21 22:23:12 +02:00
Christian Schwinne
1f70a735c7 Merge pull request #1980 from stevelup/patch-1
Update platformio_override.ini.sample
2021-05-21 20:38:00 +02:00
stevelup
6713fcfeb1 Update platformio_override.ini.sample
Typo on lines 12 and 31 (DISLAY -vs- DISPLAY)
2021-05-21 19:01:42 +01:00
Christian Schwinne
c3107d213a Merge pull request #1979 from Aircoookie/elekstube_usermod
Elekstube IPS and RTC usermods
2021-05-21 15:34:14 +02:00
cschwinne
adf5c8c278 Center smaller images 2021-05-21 15:19:18 +02:00
Blaž Kristan
5f86a8a15b Merge branch 'master' into multi-button 2021-05-21 13:59:33 +02:00
Blaž Kristan
042c756be8 Added analog button support.
Inverted switch fix.
2021-05-21 13:33:22 +02:00
cschwinne
2d586406da First working Elekstube IPS and RTC mod 2021-05-21 10:10:29 +02:00
cschwinne
371c4e0051 Fixed WS281x output on ESP32
Fixed potential out-of-bounds write in MQTT
Fixed IR pin not changeable if IR disabled
Fixed XML API <wv> containing -1 on Manual only RGBW mode (see #888, #1783)
2021-05-20 21:41:39 +02:00
Blaz Kristan
69099fcdd7 Multi button support. 2021-05-20 19:54:07 +02:00
Daniel Poelzleithner
57e50d0c33 Allow to override the max segments setting when compiling 2021-05-20 11:46:12 +02:00
Blaž Kristan
1617658bfe Per strip skip first LED implementation (#1871)
* Per strip "skip first LED".
Moved skip first led into bus manager.

* Update cfg.cpp

* Do not display Skip 1st on analog types

Rename skipFirstLed() to clarify what it does
Remove RGBW override

Co-authored-by: Aircoookie <dev.aircoookie@gmail.com>
Co-authored-by: Aircoookie <cschwinne@gmail.com>
2021-05-17 16:23:46 +02:00
Christian Schwinne
4bcfff780a Merge pull request #1965 from blazoncek/usermod-update
Updated usermod readme and usability enhancements.
2021-05-17 13:05:11 +02:00
Blaž Kristan
12f9ad8f7f Updated usermod readme and usability enhancements. 2021-05-17 12:52:27 +02:00
cschwinne
6f843fcb27 Added experimental {"on":"t"} (resolves #1952 ) 2021-05-17 12:29:30 +02:00
cschwinne
e0f17e1778 Always copy MQTT payload 2021-05-17 10:38:07 +02:00
cschwinne
bfb27c49a2 Fixed possibility of non-0-terminated MQTT payloads 2021-05-13 01:04:33 +02:00
cschwinne
cb7b7f1dca Add notice if no usermod installed 2021-05-11 23:21:57 +02:00
cschwinne
5ca8bc3f2a Fixed various Codacy code style and logic issues 2021-05-11 14:54:03 +02:00
Aircoookie
1ccc8eec0a Merge pull request #1922 from esev/tm1814
TM1814 requires refreshes to remain powered off and a slightly faster refresh speed
2021-05-11 12:10:52 +02:00
Aircoookie
9c5afda83a Merge branch 'master' into tm1814 2021-05-11 11:59:35 +02:00
cschwinne
d94d3d4bc5 Added experimental /json/cfg endpoint for changing settings from JSON 2021-05-11 01:11:16 +02:00
Aircoookie
119826cb9b Merge pull request #1951 from blazoncek/user-configurable-usermods
User configurable usermods.
2021-05-09 23:10:37 +02:00
cschwinne
6ab95ed4ef Remove Usermod inheritance from UsermodManager 2021-05-09 22:54:04 +02:00
cschwinne
4f1eb64ac6 Added changes to set.cpp and wled_server.cpp 2021-05-08 18:04:44 +02:00
Blaz Kristan
3f8dc76f84 Added missing config info. 2021-05-08 12:13:39 +02:00
cschwinne
f60579fd21 Remove outdated Visual Studio files (please make PR to re-add if still required) 2021-05-07 16:51:41 +02:00
Blaz Kristan
136a00a301 User configurable usermods.
- Utilizing addToConfig() & readFromConfig()
- UM configuration Settings page
- MQTT support for usermods
- A few sample usermods including rewritten Temparature and new Multi-Relay
2021-05-07 12:41:39 +02:00
cschwinne
fa075f6800 Revert TV reversion 2021-05-07 11:42:12 +02:00
cschwinne
277f395595 Merge branch 'master' of https://github.com/Aircoookie/WLED 2021-05-07 11:32:13 +02:00
cschwinne
e2061464a5 Fixed not turning on after pressing "Off" on IR remote twice (fixes #1950)
Fixed OTA update file selection from Android app (TODO: file type verification in JS, since android can't deal with accept='.bin' attribute)
2021-05-07 11:32:08 +02:00
Aircoookie
fcf5cd4655 Merge pull request #1946 from jaredgisin/br-remove-unused-configs
remove unused flags
2021-05-05 10:04:45 +02:00
Jared Gisin
3816f0b68b remove unused flags 2021-05-04 18:45:05 -07:00
Aircoookie
1a2543ddde Added FPS test utility 2021-04-30 00:00:46 +02:00
Eric Severance
7c9db7edeb Add comment about the TYWE3S controller (#1924)
The `codm-controller-0.6` env also works for the [`TYWE3S` controller](https://github.com/Aircoookie/WLED/issues/1519#issuecomment-826126116). This PR just adds a note so that folks who search for `TYWE3S` will find the proper env to use.
2021-04-27 09:05:40 +02:00
Eric Severance
8b759bc5d9 TM1814 requires refreshes to remain powered off and a slightly faster refresh speed. 2021-04-25 09:08:02 -07:00
acshef
9a0aac4745 Add IP Address to JsonInfo (#1912)
* Add IP Address to JsonInfo

The value is added to the JSON only if the device is connected to the network, and uses the JSON key `"sip"` to match [wled00/xml.cpp](wled00/xml.cpp#L249). The overarching goal of this is to expose the IP Address to the Home Assistant WLED Integration, so that Home Assistant can provide a link to the WLED device (either directly through the Integration/Device page 🤞 or *ad hoc* in Lovelace).

* IP in JSON info

Empty string if not connected

Co-authored-by: Aircoookie <cschwinne@gmail.com>
2021-04-25 01:37:16 +02:00
cschwinne
ced0cc1bac 0.12.1-b1 2021-04-22 01:12:31 +02:00
cschwinne
3c49f22266 Merge branch 'master' of https://github.com/Aircoookie/WLED 2021-04-22 00:47:58 +02:00
cschwinne
13ae99edec Reduce TV simulator flash usage 2021-04-22 00:47:50 +02:00
Andy Shinn
0f82730a78 Release engineering improvements (#1844)
* version set from package.json and release bin names

* support direnv virtualenv

* versioned PlatformIO environment

* matrix support for parralel CI

* gather artifacts

* release on tagging

* minor scripts formatting
2021-04-22 00:41:36 +02:00
cschwinne
ff083daf31 Merge branch 'master' of https://github.com/Aircoookie/WLED 2021-04-21 17:22:00 +02:00
cschwinne
7f6a554e1b Small adjustments 2021-04-21 17:21:55 +02:00
Eric Severance
eb99271120 Use 22.5 for the TM1814 max current (#1905)
* Configure TM1814 max current

* Use 22.5 mA as this seems to be a common value for the LEDs
2021-04-20 21:36:52 +02:00
Eric Severance
13e5c695c3 Initialize PixelSettings for TM1814 strips (#1847)
* Better handling for TM1814 strips

* Call SetPixelSettings after Begin as described on the NeoPixelBus wiki

* Use NeoTm1814Settings::MaxCurrent constant
2021-04-18 19:14:12 +02:00
Def3nder
12de47c923 Fix TV-Simmulator Effect flash usage (#1621)
* Add alternative TV-Sim version without tv_colors.h

...and safe 18k flash this way

* ...remove the define
2021-04-16 10:22:22 +02:00
cschwinne
afde7940d8 NUM_STRIPS no longer required with compile-time strip defaults 2021-04-16 01:01:24 +02:00
cschwinne
01dd41bdbf Added ability to add multiple busses as compile time defaults using the esp32_multistrip usermod define syntax 2021-04-15 10:55:22 +02:00
cschwinne
f3b84f1365 Switch trigonometric implementation, saves 460b memory 2021-04-14 16:49:47 +02:00
cschwinne
5751d5c1b0 Fixed Button, IR, Relay pin not assigned by default (resolves #1891)
Fixed instance discovery not working if MQTT not compiled in
2021-04-14 01:16:32 +02:00
Aircoookie
3d2336aac1 Merge pull request #1890 from blazoncek/holiday-list
Easier holiday/special events handling for background images.
2021-04-14 00:56:41 +02:00
cschwinne
afe5f19464 Update holiday BG durations
Fix semicolons
2021-04-14 00:44:07 +02:00
Aircoookie
4091a3c238 Merge pull request #1889 from blazoncek/sunrise-sunset-012
Sunrise and sunset calculation and sunrise/sunset triggered presets.
2021-04-13 23:41:03 +02:00
Blaz Kristan
f411e07fb4 Easier holiday/special events handling for background images. 2021-04-12 23:00:22 +02:00
Blaz Kristan
9bfe27dd5e Sunrise and sunset calculation and sunrise/sunset triggered presets. 2021-04-12 21:53:22 +02:00
Aircoookie
c4201d9a2a Merge pull request #1886 from betaphi/BME280_Patch
Usermod BME280_v2 refinement
2021-04-12 11:07:22 +02:00
Bastian Rössler
58e9817a6d Add PublishAlways to BME280_v2 Usermod 2021-04-12 10:31:59 +02:00
cschwinne
48d5584491 Add switch support 2021-04-12 00:45:33 +02:00
Aircoookie
5786f1d057 Merge pull request #1863 from fishbone-git/running_dual
Running dual
2021-04-11 00:59:56 +02:00
cschwinne
87c6f3c757 Add gap for Running Dual 2021-04-11 00:50:14 +02:00
cschwinne
0e99c948d6 Merge branch 'master' of https://github.com/Aircoookie/WLED 2021-04-10 12:22:12 +02:00
cschwinne
0f5e0f640b Updated codm environments
Added custom 8MB ESP32 partition csv
2021-04-10 12:22:09 +02:00
Aircoookie
3d2c6388de Merge pull request #1875 from zoide/patch-1
updated bottom/top sensor
2021-04-10 12:19:49 +02:00
Udo Waechter
ad8e614ae8 updated bottom/top sensor
bottomsensor and topsensor were incorrect: bottom-sensor / top-sensor according to API
2021-04-09 17:50:26 +02:00
Aircoookie
48c0360877 Merge pull request #1850 from eg321/VL53L0X-gestures
Support of simple gestures for VL53L0X laser sensor
2021-04-08 15:31:37 +02:00
Aircoookie
d230be3e1c Merge pull request #1851 from kolcun/patch-1
Increase buffer size
2021-04-07 01:15:42 +02:00
Aircoookie
daa77d40a3 Merge pull request #1862 from fishbone-git/drip
Drip: use constrain to keep in segment limits
2021-04-07 00:59:38 +02:00
Aircoookie
6ae743684f Merge pull request #1864 from blazoncek/ledmap-multisegment-fix
Ledmap multisegment fix.
2021-04-06 12:03:45 +02:00
cschwinne
789c00dde1 Change indexSet to signed 32 bit 2021-04-06 11:42:21 +02:00
Blaž Kristan
90da471084 Fix for mirrored segments. 2021-04-06 07:48:12 +02:00
Blaž Kristan
2d55056015 Merge branch 'master' into ledmap-multisegment-fix 2021-04-05 21:19:19 +02:00
Blaz Kristan
ffab9bb893 Ledmap multisegment fix. 2021-04-05 21:14:36 +02:00
fishbone-git
1192d04391 sync and merge master 2021-04-05 20:49:31 +02:00
fishbone-git
f18dced2f3 use constrain to keep in segment limits 2021-04-05 16:08:35 +02:00
cschwinne
ecdc0a3800 Fixed ESP32 crash on Drip effect with reversed segment (#1854) 2021-04-03 17:18:29 +02:00
Mike Kolcun
a69dcfc49d Increase buffer size
Avoid buffer overflows with longer mqtt topics
mqtDeviceTopic in wled.h is defined with a size of 33, so this should be set to 45 to accommodate the additional 12 characters - /temperature
2021-04-02 14:04:27 -04:00
Egor Chernodarov
601005f837 Support of simple gestures for VL53L0X laser sensor 2021-04-02 23:04:10 +07:00
fishbone-git
cb0452964e bidirectional running lights effect 2020-02-18 21:28:46 +01:00
248 changed files with 31622 additions and 13734 deletions

1
.envrc Normal file
View File

@@ -0,0 +1 @@
layout python-venv python3

2
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,2 @@
github: [Aircoookie]
custom: ['https://paypal.me/Aircoookie']

View File

@@ -1,27 +0,0 @@
---
name: Bug
about: Noticed an issue with your lights?
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is. Please quickly search existing issues first!
**To Reproduce**
Steps to reproduce the behavior, if consistently possible
**Expected behavior**
A clear and concise description of what you expected to happen.
**WLED version**
- Board: [e.g. Wemos D1, ESP32 dev]
- Version [e.g. 0.10.0, dev200603]
- Format [e.g. Binary, self-compiled]
**Additional context**
Anything else you'd like to say about the problem?
Thank you for your help!

83
.github/ISSUE_TEMPLATE/bug.yml vendored Normal file
View File

@@ -0,0 +1,83 @@
name: Bug Report
description: File a bug report
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
Please quickly search existing issues first before submitting a bug.
- type: textarea
id: what-happened
attributes:
label: What happened?
description: A clear and concise description of what the bug is.
placeholder: Tell us what the problem is.
validations:
required: true
- type: textarea
id: how-to-reproduce
attributes:
label: To Reproduce Bug
description: Steps to reproduce the behavior, if consistently possible.
placeholder: Tell us how to make the bug appear.
validations:
required: true
- type: textarea
id: expected-behavior
attributes:
label: Expected Behavior
description: A clear and concise description of what you expected to happen.
placeholder: Tell us what you expected to happen.
validations:
required: true
- type: dropdown
id: install_format
attributes:
label: Install Method
description: How did you install WLED?
options:
- Binary from WLED.me
- Self-Compiled
validations:
required: true
- type: input
id: version
attributes:
label: What version of WLED?
description: You can find this in by going to Config -> Security & Updates -> Scroll to Bottom. Copy and paste the entire line after "Server message"
placeholder: "e.g. WLED 0.13.1 (build 2203150)"
validations:
required: true
- type: dropdown
id: Board
attributes:
label: Which microcontroller/board are you seeing the problem on?
multiple: true
options:
- ESP8266
- ESP32
- Other
validations:
required: true
- type: textarea
id: logs
attributes:
label: Relevant log/trace output
description: Please copy and paste any relevant log output if you have it. This will be automatically formatted into code, so no need for backticks.
render: shell
- type: textarea
attributes:
label: Anything else?
description: |
Links? References? Anything that will give us more context about the issue you are encountering!
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
validations:
required: false
- type: checkboxes
id: terms
attributes:
label: Code of Conduct
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/Aircoookie/WLED/blob/master/CODE_OF_CONDUCT.md)
options:
- label: I agree to follow this project's Code of Conduct
required: true

View File

@@ -3,10 +3,37 @@ name: PlatformIO CI
on: [push, pull_request]
jobs:
build:
get_default_envs:
name: Gather Environments
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Cache pip
uses: actions/cache@v2
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- uses: actions/setup-python@v2
- name: Install PlatformIO
run: pip install -r requirements.txt
- name: Get default environments
id: envs
run: |
echo "::set-output name=environments::$(pio project config --json-output | jq -cr '.[0][1][0][1]')"
outputs:
environments: ${{ steps.envs.outputs.environments }}
build:
name: Build Enviornments
runs-on: ubuntu-latest
needs: get_default_envs
strategy:
matrix:
environment: ${{ fromJSON(needs.get_default_envs.outputs.environments) }}
steps:
- uses: actions/checkout@v2
- name: Cache pip
@@ -24,8 +51,36 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v2
- name: Install PlatformIO
run: |
python -m pip install --upgrade pip
pip install --upgrade platformio
- name: Run PlatformIO
run: pio run
run: pip install -r requirements.txt
- name: Build firmware
env:
WLED_RELEASE: True
run: pio run -e ${{ matrix.environment }}
- uses: actions/upload-artifact@v2
with:
name: firmware-${{ matrix.environment }}
path: |
build_output/firmware/*.bin
build_output/firmware/*.gz
- uses: actions/upload-artifact@v2
if: startsWith(github.ref, 'refs/tags/')
with:
name: firmware-release
path: build_output/release/*.bin
release:
name: Create Release
runs-on: ubuntu-latest
needs: [get_default_envs, build]
if: startsWith(github.ref, 'refs/tags/')
steps:
- uses: actions/download-artifact@v2
with:
name: firmware-release
- name: Create draft release
uses: softprops/action-gh-release@v1
with:
draft: True
files: |
*.bin
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

1
.gitignore vendored
View File

@@ -14,3 +14,4 @@
.clang-format
node_modules
.idea
.direnv

View File

@@ -1,7 +1,10 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
]
}
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

View File

@@ -1,5 +1,454 @@
## WLED changelog
### WLED release 0.13.3
- Version bump to v0.13.3 "Toki"
- Disable ESP watchdog by default (fixes flickering and boot issues on a fresh install)
- Added support for LPD6803
### WLED release 0.13.2
#### Build 2208140
- Version bump to v0.13.2 "Toki"
- Added option to receive live data on the main segment only (PR #2601)
- Enable ESP watchdog by default (PR #2657)
- Fixed race condition when saving bus config
- Better potentiometer filtering (PR #2693)
- More suitable DMX libraries (PR #2652)
- Fixed outgoing serial TPM2 message length (PR #2628)
- Fixed next universe overflow and Art-Net DMX start address (PR #2607)
- Fixed relative segment brightness (PR #2665)
### Builds between releases 0.13.1 and 0.13.2
#### Build 2203191
- Fixed sunrise/set calculation (once again)
#### Build 2203190
- Fixed `/json/cfg` unable to set busses (#2589)
- Fixed Peek with odd LED counts > 255 (#2586)
#### Build 2203160
- Version bump to v0.13.2-a0 "Toki"
- Add ability to skip up to 255 LEDs
- Dependency version bumps
### WLED release 0.13.1
#### Build 2203150
- Version bump to v0.13.1 "Toki"
- Fix persistent preset bug, preventing save of new presets
### WLED release 0.13.0
#### Build 2203142
- Release of WLED v0.13.0 "Toki"
- Reduce APA102 hardware SPI frequency to 5Mhz
- Remove `persistent` parameter in `savePreset()`
### Builds between releases 0.12.0 and 0.13.0
#### Build 2203140
- Added factory reset by pressing button 0 for >10 seconds
- Added ability to set presets from DMX Effect mode
- Simplified label hiding JS in user interface
- Fixed JSON `{"live":true}` indefinite realtime mode
#### Build 2203080
- Disabled auto white mode in segments with no RGB bus
- Fixed hostname string not 0-terminated
- Fixed Popcorn mode not lighting first LED on pop
#### Build 2203060
- Dynamic hiding of unused color controls in UI (PR #2567)
- Removed native Cronixie support and added Cronixie usermod
- Fixed disabled timed preset expanding calendar
- Fixed Color Order setting shown for analog busses
- Fixed incorrect operator (#2566)
#### Build 2203011
- IR rewrite (PR #2561), supports CCT
- Added locate button to Time settings
- CSS fixes and adjustments
- Consistent Tab indentation in index JS and CSS
- Added initial contribution style guideline
#### Build 2202222
- Version bump to 0.13.0-b7 "Toki"
- Fixed HTTP API commands not applying to all selected segments in some conditions
- Blynk support is not compiled in by default on ESP32 builds
#### Build 2202210
- Fixed HTTP API commands not applying to all selected segments if called from JSON
- Improved Stream effects, no longer rely on LED state and won't fade out at low brightness
#### Build 2202200
- Added `info.leds.seglc` per-segment light capability info (PR #2552)
- Fixed `info.leds.rgbw` behavior
- Segment bounds sync (PR #2547)
- WebSockets auto reconnection and error handling
- Disable relay pin by default (PR #2531)
- Various fixes (ESP32 touch pin 33, floats, PR #2530, #2534, #2538)
- Deprecated `info.leds.cct`, `info.leds.wv` and `info.leds.rgbw`
- Deprecated `/url` endpoint
#### Build 2202030
- Switched to binary format for WebSockets peek (PR #2516)
- Playlist bugfix
- Added `extractModeName()` utility function
- Added serial out (PR #2517)
- Added configurable baud rate
#### Build 2201260
- Initial ESP32-C3 and ESP32-S2 support (PRs #2452, #2454, #2502)
- Full segment sync (PR #2427)
- Allow overriding of color order by ranges (PR #2463)
- Added white channel to Peek
#### Build 2112080
- Version bump to 0.13.0-b6 "Toki"
- Added "ESP02" (ESP8266 with 2M of flash) to PIO/release binaries
#### Build 2112070
- Added new effect "Fairy", replacing "Police All"
- Added new effect "Fairytwinkle", replacing "Two Areas"
- Static single JSON buffer (performance and stability improvement) (PR #2336)
#### Build 2112030
- Fixed ESP32 crash on Colortwinkles brightness change
- Fixed setting picker to black resetting hue and saturation
- Fixed auto white mode not saved to config
#### Build 2111300
- Added CCT and white balance correction support (PR #2285)
- Unified UI slider style
- Added LED settings config template upload
#### Build 2111220
- Fixed preset cycle not working from preset called by UI
- Reintroduced permanent min. and max. cycle bounds
#### Build 2111190
- Changed default ESP32 LED pin from 16 to 2
- Renamed "Running 2" to "Chase 2"
- Renamed "Tri Chase" to "Chase 3"
#### Build 2111170
- Version bump to 0.13.0-b5 "Toki"
- Improv Serial support (PR #2334)
- Button improvements (PR #2284)
- Added two time zones (PR #2264, 2311)
- JSON in/decrementing support for brightness and presets
- Fixed no gamma correction for JSON individual LED control
- Preset cycle bugfix
- Removed ledCount
- LED settings buffer bugfix
- Network pin conflict bugfix
- Changed default ESP32 partition layout to 4M, 1M FS
#### Build 2110110
- Version bump to 0.13.0-b4 "Toki"
- Added option for bus refresh if off (PR #2259)
- New auto segment logic
- Fixed current calculations for virtual or non-linear configs (PR #2262)
#### Build 2110060
- Added virtual network DDP busses (PR #2245)
- Allow playlist as end preset in playlist
- Improved bus start field UX
- Pin reservations improvements (PR #2214)
#### Build 2109220
- Version bump to 0.13.0-b3 "Toki"
- Added segment names (PR #2184)
- Improved Police and other effects (PR #2184)
- Reverted PR #1902 (Live color correction - will be implemented as usermod) (PR #2175)
- Added transitions for segment on/off
- Improved number of sparks/stars in Fireworks effect with low number of segments
- Fixed segment name edit pencil disappearing with request
- Fixed color transition active even if the segment is off
- Disallowed file upload with OTA lock active
- Fixed analog invert option missing (PR #2219)
#### Build 2109100
- Added an auto create segments per bus setting
- Added 15 new palettes from SR branch (PR #2134)
- Fixed segment runtime not reset on FX change via HTTP API
- Changed AsyncTCP dependency to pbolduc fork v1.2.0
#### Build 2108250
- Added Sync groups (PR #2150)
- Added JSON API over Serial support
- Live color correction (PR #1902)
#### Build 2108180
- Fixed JSON IR remote not working with codes greater than 0xFFFFFF (fixes #2135)
- Fixed transition 0 edge case
#### Build 2108170
- Added application level pong websockets reply (#2139)
- Use AsyncTCP 1.0.3 as it mitigates the flickering issue from 0.13.0-b2
- Fixed transition manually updated in preset overriden by field value
#### Build 2108050
- Fixed undesirable color transition from Orange to boot preset color on first boot
- Removed misleading Delete button on new playlist with one entry
- Updated NeoPixelBus to 2.6.7 and AsyncTCP to 1.1.1
#### Build 2107230
- Added skinning (extra custom CSS) (PR #2084)
- Added presets/config backup/restore (PR #2084)
- Added option for using length instead of Stop LED in UI (PR #2048)
- Added custom `holidays.json` holiday list (PR #2048)
#### Build 2107100
- Version bump to 0.13.0-b2 "Toki"
- Accept hex color strings in individual LED API
- Fixed transition property not applying unless power/bri/color changed next
- Moved transition field below segments (temporarily)
- Reduced unneeded websockets pushes
#### Build 2107091
- Fixed presets using wrong call mode (e.g. causing buttons to send UDP under direct change type)
- Increased hue buffer
- Renamed `NOTIFIER_CALL_MODE_` to `CALL_MODE_`
#### Build 2107090
- Busses extend total configured LEDs if required
- Fixed extra button pins defaulting to 0 on first boot
#### Build 2107080
- Made Peek use the main websocket connection instead of opening a second one
- Temperature usermod fix (from @blazoncek's dev branch)
#### Build 2107070
- More robust initial resource loading in UI
- Added `getJsonValue()` for usermod config parsing (PR #2061)
- Fixed preset saving over websocket
- Alpha ESP32 S2 support (filesystem does not work) (PR #2067)
#### Build 2107042
- Updated ArduinoJson to 6.18.1
- Improved Twinkleup effect
- Fixed preset immediately deselecting when set via HTTP API `PL=`
#### Build 2107041
- Restored support for "PL=~" mistakenly removed in 2106300
- JSON IR improvements
#### Build 2107040
- Playlist entries are now more compact
- Added the possibility to enter negative numbers for segment offset
#### Build 2107021
- Added WebSockets support to UI
#### Build 2107020
- Send websockets on every state change
- Improved Aurora effect
#### Build 2107011
- Added MQTT button feedback option (PR #2011)
#### Build 2107010
- Added JSON IR codes (PR #1941)
- Adjusted the width of WiFi and LED settings input fields
- Fixed a minor visual issue with slider trail not reaching thumb on low values
#### Build 2106302
- Fixed settings page broken by using "%" in input fields
#### Build 2106301
- Fixed a problem with disabled buttons reverting to pin 0 causing conflict
#### Build 2106300
- Version bump to 0.13.0-b0 "Toki"
- BREAKING: Removed preset cycle (use playlists)
- BREAKING: Removed `nl.fade`, `leds.pin` and `ccnf` from JSON API
- Added playlist editor UI
- Reordered segment UI and added offset field
- Raised maximum MQTT password length to 64 (closes #1373)
#### Build 2106290
- Added Offset to segments, allows shifting the LED considered first within a segment
- Added `of` property to seg object in JSON API to set offset
- Usermod settings improvements (PR #2043, PR #2045)
#### Build 2106250
- Fixed preset only disabling on second effect/color change
#### Build 2106241
- BREAKING: Added ability for usermods to force a config save if config incomplete. `readFromConfig()` needs to return a `bool` to indicate if the config is complete
- Updated usermods implementing `readFromConfig()`
- Auto-create segments based on configured busses
#### Build 2106200
- Added 2 Ethernet boards and split Ethernet configs into separate file
#### Build 2106180
- Fixed DOS on Chrome tab restore causing reboot
#### Build 2106170
- Optimized JSON buffer usage (pre-serialized color arrays)
#### Build 2106140
- Updated main logo
- Reduced flash usage by 0.8kB by using 8-bit instead of 32-bit PNGs for welcome and 404 pages
- Added a check to stop Alexa reporting an error if state set by macro differs from the expected state
#### Build 2106100
- Added support for multiple buttons with various types (PR #1977)
- Fixed infinite playlists (PR #2020)
- Added `r` to playlist object, allows for shuffle regardless of the `repeat` value
- Improved accuracy of NTP time sync
- Added possibility for WLED UDP sync to sync system time
- Improved UDP sync accuracy, if both sender and receiver are NTP synced
- Fixed a cache issue with restored tabs
- Cache CORS request
- Disable WiFi sleep by default on ESP32
#### Build 2105230
- No longer retain MQTT `/v` topic to alleviate storage loads on MQTT broker
- Fixed Sunrise calculation (atan_t approx. used outside of value range)
#### Build 2105200
- Fixed WS281x output on ESP32
- Fixed potential out-of-bounds write in MQTT
- Fixed IR pin not changeable if IR disabled
- Fixed XML API <wv> containing -1 on Manual only RGBW mode (see #888, #1783)
#### Build 2105171
- Always copy MQTT payloads to prevent non-0-terminated strings
- Updated ArduinoJson to 6.18.0
- Added experimental support for `{"on":"t"}` to toggle on/off state via JSON
#### Build 2105120
- Fixed possibility of non-0-terminated MQTT payloads
- Fixed two warnings regarding integer comparison
#### Build 2105112
- Usermod settings page no usermods message
- Lowered min speed for Drip effect
#### Build 2105111
- Fixed various Codacy code style and logic issues
#### Build 2105110
- Added Usermod settings page and configurable usermods (PR #1951)
- Added experimental `/json/cfg` endpoint for changing settings from JSON (see #1944, not part of official API)
#### Build 2105070
- Fixed not turning on after pressing "Off" on IR remote twice (#1950)
- Fixed OTA update file selection from Android app (TODO: file type verification in JS, since android can't deal with accept='.bin' attribute)
#### Build 2104220
- Version bump to 0.12.1-b1 "Hikari"
- Release and build script improvements (PR #1844)
#### Build 2104211
- Replace default TV simulator effect with the version that saves 18k of flash and appears visually identical
#### Build 2104210
- Added `tb` to JSON state, allowing setting the timebase (set tb=0 to start e.g. wipe effect from the beginning). Receive only.
- Slightly raised Solid mode refresh rate to work with LEDs (TM1814) that require refresh rates of at least 2fps
- Added sunrise and sunset calculation to the backup JSON time source
#### Build 2104151
- `NUM_STRIPS` no longer required with compile-time strip defaults
- Further optimizations in wled_math.h
#### Build 2104150
- Added ability to add multiple busses as compile time defaults using the esp32_multistrip usermod define syntax
#### Build 2104141
- Reduced memory usage by 540b by switching to a different trigonometric approximation
#### Build 2104140
- Added dynamic location-based Sunrise/Sunset macros (PR #1889)
- Improved seasonal background handling (PR #1890)
- Fixed instance discovery not working if MQTT not compiled in
- Fixed Button, IR, Relay pin not assigned by default (resolves #1891)
#### Build 2104120
- Added switch support (button macro is switch closing action, long press macro switch opening)
- Replaced Circus effect with new Running Dual effect (Circus is Tricolor Chase with Red/White/Black)
- Fixed ledmap with multiple segments (PR #1864)
#### Build 2104030
- Fixed ESP32 crash on Drip effect with reversed segment (#1854)
- Added flag `WLED_DISABLE_BROWNOUT_DET` to disable ESP32 brownout detector (off by default)
### WLED release 0.12.0
#### Build 2104020
@@ -91,6 +540,7 @@
- Added support for WESP32 ethernet board (PR #1764)
- Added Caching for main UI (PR #1704)
- Added Tetrix mode (PR #1729)
- Removed Merry Christmas mode (use "Chase 2" - called Running 2 before 0.13.0)
- Added memory check on Bus creation
#### Build 2102050

78
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,78 @@
## Thank you for making WLED better!
Here are a few suggestions to make it easier for you to contribute!
### Code style
When in doubt, it is easiest to replicate the code style you find in the files you want to edit :)
Below are the guidelines we use in the WLED repository.
#### Indentation
We use tabs for Indentation in Web files (.html/.css/.js) and spaces (2 per indentation level) for all other files.
You are all set if you have enabled `Editor: Detect Indentation` in VS Code.
#### Blocks
Whether the opening bracket of e.g. an `if` block is in the same line as the condition or in a separate line is up to your discretion. If there is only one statement, leaving out block braches is acceptable.
Good:
```cpp
if (a == b) {
doStuff(a);
}
```
```cpp
if (a == b)
{
doStuff(a);
}
```
```cpp
if (a == b) doStuff(a);
```
There should always be a space between a keyword and its condition and between the condition and brace.
Within the condition, no space should be between the paranthesis and variables.
Spaces between variables and operators are up to the authors discretion.
There should be no space between function names and their argument parenthesis.
Good:
```cpp
if (a == b) {
doStuff(a);
}
```
Not good:
```cpp
if( a==b ){
doStuff ( a);
}
```
#### Comments
Comments should have a space between the delimiting characters (e.g. `//`) and the comment text.
Note: This is a recent change, the majority of the codebase still has comments without spaces.
Good:
```
// This is a comment.
/* This is a CSS inline comment */
/*
* This is a comment
* wrapping over multiple lines,
* used in WLED for file headers and function explanations
*/
<!-- This is an HTML comment -->
```
There is no set character limit for a comment within a line,
though as a rule of thumb you should wrap your comment if it exceeds the width of your editor window.
Inline comments are OK if they describe that line only and are not exceedingly wide.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 602 B

View File

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

32
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "wled",
"version": "0.11.1",
"version": "0.13.3",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -28,9 +28,9 @@
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
},
"ajv": {
"version": "6.12.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz",
"integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==",
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -761,9 +761,9 @@
}
},
"glob-parent": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
"integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"requires": {
"is-glob": "^4.0.1"
}
@@ -1339,9 +1339,9 @@
}
},
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
},
"mkdirp": {
"version": "0.5.5",
@@ -1572,9 +1572,9 @@
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
},
"normalize-url": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz",
"integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ=="
"version": "4.5.1",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz",
"integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA=="
},
"nth-check": {
"version": "1.0.2",
@@ -2067,9 +2067,9 @@
"integrity": "sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw=="
},
"terser": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",
"integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==",
"version": "4.8.1",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz",
"integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==",
"requires": {
"commander": "^2.20.0",
"source-map": "~0.6.1",

View File

@@ -1,6 +1,6 @@
{
"name": "wled",
"version": "0.12.0",
"version": "0.13.3",
"description": "Tools for WLED project",
"main": "tools/cdata.js",
"directories": {

View File

@@ -1,23 +0,0 @@
Import('env')
import os
import shutil
import gzip
OUTPUT_DIR = "build_output{}".format(os.path.sep)
def bin_gzip(source, target, env):
variant = str(target[0]).split(os.path.sep)[2]
# create string with location and file names based on variant
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
gzip_file = "{}firmware{}{}.bin.gz".format(OUTPUT_DIR, os.path.sep, variant)
# check if new target files exist and remove if necessary
if os.path.isfile(gzip_file): os.remove(gzip_file)
# write gzip firmware file
with open(bin_file,"rb") as fp:
with gzip.open(gzip_file, "wb", compresslevel = 9) as f:
shutil.copyfileobj(fp, f)
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_gzip])

View File

@@ -1,34 +0,0 @@
Import('env')
import os
import shutil
OUTPUT_DIR = "build_output{}".format(os.path.sep)
def bin_rename_copy(source, target, env):
variant = str(target[0]).split(os.path.sep)[2]
# check if output directories exist and create if necessary
if not os.path.isdir(OUTPUT_DIR):
os.mkdir(OUTPUT_DIR)
for d in ['firmware', 'map']:
if not os.path.isdir("{}{}".format(OUTPUT_DIR, d)):
os.mkdir("{}{}".format(OUTPUT_DIR, d))
# create string with location and file names based on variant
map_file = "{}map{}{}.map".format(OUTPUT_DIR, os.path.sep, variant)
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
# check if new target files exist and remove if necessary
for f in [map_file, bin_file]:
if os.path.isfile(f):
os.remove(f)
# copy firmware.bin to firmware/<variant>.bin
shutil.copy(str(target[0]), bin_file)
# copy firmware.map to map/<variant>.map
if os.path.isfile("firmware.map"):
shutil.move("firmware.map", map_file)
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_rename_copy])

View File

@@ -0,0 +1,69 @@
Import('env')
import os
import shutil
import gzip
OUTPUT_DIR = "build_output{}".format(os.path.sep)
def _get_cpp_define_value(env, define):
define_list = [item[-1] for item in env["CPPDEFINES"] if item[0] == define]
if define_list:
return define_list[0]
return None
def _create_dirs(dirs=["firmware", "map"]):
# check if output directories exist and create if necessary
if not os.path.isdir(OUTPUT_DIR):
os.mkdir(OUTPUT_DIR)
for d in dirs:
if not os.path.isdir("{}{}".format(OUTPUT_DIR, d)):
os.mkdir("{}{}".format(OUTPUT_DIR, d))
def bin_rename_copy(source, target, env):
_create_dirs()
variant = env["PIOENV"]
# create string with location and file names based on variant
map_file = "{}map{}{}.map".format(OUTPUT_DIR, os.path.sep, variant)
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
release_name = _get_cpp_define_value(env, "WLED_RELEASE_NAME")
if release_name:
_create_dirs(["release"])
version = _get_cpp_define_value(env, "WLED_VERSION")
release_file = "{}release{}WLED_{}_{}.bin".format(OUTPUT_DIR, os.path.sep, version, release_name)
shutil.copy(str(target[0]), release_file)
# check if new target files exist and remove if necessary
for f in [map_file, bin_file]:
if os.path.isfile(f):
os.remove(f)
# copy firmware.bin to firmware/<variant>.bin
shutil.copy(str(target[0]), bin_file)
# copy firmware.map to map/<variant>.map
if os.path.isfile("firmware.map"):
shutil.move("firmware.map", map_file)
def bin_gzip(source, target, env):
_create_dirs()
variant = env["PIOENV"]
# create string with location and file names based on variant
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
gzip_file = "{}firmware{}{}.bin.gz".format(OUTPUT_DIR, os.path.sep, variant)
# check if new target files exist and remove if necessary
if os.path.isfile(gzip_file): os.remove(gzip_file)
# write gzip firmware file
with open(bin_file,"rb") as fp:
with gzip.open(gzip_file, "wb", compresslevel = 9) as f:
shutil.copyfileobj(fp, f)
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_rename_copy, bin_gzip])

View File

@@ -0,0 +1,8 @@
Import('env')
import json
PACKAGE_FILE = "package.json"
with open(PACKAGE_FILE, "r") as package:
version = json.load(package)["version"]
env.Append(BUILD_FLAGS=[f"-DWLED_VERSION={version}"])

View File

@@ -8,14 +8,19 @@
# Please uncomment one of the lines below to select your board(s)
# ------------------------------------------------------------------------------
# Travis CI binaries (comment this out with a ';' when building for your own board)
;default_envs = travis_esp8266, travis_esp32
# Travis CI binaries (use `platformio_override.ini` when building for your own board; see `platformio_override.ini.sample` for an example)
; default_envs = travis_esp8266, travis_esp32
# Release binaries
default_envs = nodemcuv2, esp01_1m_full, esp32dev, esp32_eth
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, esp32s2_saola, esp32c3
# Build everything
; default_envs = esp32dev, esp8285_4CH_MagicHome, codm-controller-0.6-rev2, codm-controller-0.6, esp32s2_saola, d1_mini_5CH_Shojo_PCB, d1_mini, sp501e, travis_esp8266, travis_esp32, nodemcuv2, esp32_eth, anavi_miracle_controller, esp07, esp01_1m_full, m5atom, h803wf, d1_mini_ota, heltec_wifi_kit_8, esp8285_H801, d1_mini_debug, wemos_shield_esp32, elekstube_ips
# Single binaries (uncomment your board)
; default_envs = elekstube_ips
; default_envs = nodemcuv2
; default_envs = esp8266_2m
; default_envs = esp01_1m_full
; default_envs = esp07
; default_envs = d1_mini
@@ -25,12 +30,14 @@ default_envs = nodemcuv2, esp01_1m_full, esp32dev, esp32_eth
; default_envs = d1_mini_ota
; default_envs = esp32dev
; default_envs = esp8285_4CH_MagicHome
; default_envs = esp8285_4CH_H801
; default_envs = esp8285_5CH_H801
; default_envs = esp8285_H801
; default_envs = d1_mini_5CH_Shojo_PCB
; default_envs = wemos_shield_esp32
; default_envs = m5atom
; default_envs = esp32_eth
; default_envs = esp32dev_qio80
; default_envs = esp32_eth_ota1mapp
; default_envs = esp32s2_saola
src_dir = ./wled00
data_dir = ./wled00/data
@@ -48,6 +55,8 @@ extra_configs =
# ------------------------------------------------------------------------------
arduino_core_2_6_3 = espressif8266@2.3.3
arduino_core_2_7_4 = espressif8266@2.6.2
arduino_core_3_0_0 = espressif8266@3.0.0
arduino_core_3_0_2 = espressif8266@3.2.0
# Development platforms
arduino_core_develop = https://github.com/platformio/platform-espressif8266#develop
@@ -71,11 +80,8 @@ debug_flags = -D DEBUG=1 -D WLED_DEBUG -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT
# ------------------------------------------------------------------------------
# FLAGS: ldscript (available ldscripts at https://github.com/esp8266/Arduino/tree/master/tools/sdk/ld)
# ldscript_512k ( 512 KB) = 487 KB sketch, 4 KB eeprom, no spiffs, 16 KB reserved
# ldscript_1m0m (1024 KB) = 999 KB sketch, 4 KB eeprom, no spiffs, 16 KB reserved
# ldscript_2m1m (2048 KB) = 1019 KB sketch, 4 KB eeprom, 1004 KB spiffs, 16 KB reserved
# ldscript_4m1m (4096 KB) = 1019 KB sketch, 4 KB eeprom, 1002 KB spiffs, 16 KB reserved, 2048 KB empty/ota?
# ldscript_4m3m (4096 KB) = 1019 KB sketch, 4 KB eeprom, 3040 KB spiffs, 16 KB reserved
#
# Available lwIP variants (macros):
# -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH = v1.4 Higher Bandwidth (default)
@@ -114,9 +120,6 @@ build_unflags =
# enables all features for travis CI
build_flags_all_features =
-D WLED_USE_ANALOG_LED
-D WLED_USE_H801
-D WLED_ENABLE_5CH_LEDS
-D WLED_ENABLE_ADALIGHT
-D WLED_ENABLE_DMX
-D WLED_ENABLE_MQTT
@@ -130,31 +133,12 @@ ldscript_2m512k = eagle.flash.2m512.ld
ldscript_2m1m = eagle.flash.2m1m.ld
ldscript_4m1m = eagle.flash.4m1m.ld
[esp8266]
build_flags =
-DESP8266
-DFP_IN_IROM
; NONOSDK22x_190703 = 2.2.2-dev(38a443e)
-DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703
; lwIP 2 - Higher Bandwidth no Features
; -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
; lwIP 1.4 - Higher Bandwidth (Aircoookie has)
-DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH
; VTABLES in Flash
-DVTABLES_IN_FLASH
; restrict to minimal mime-types
-DMIMETYPE_MINIMAL
[esp32]
build_flags = -g
-DARDUINO_ARCH_ESP32
-DCONFIG_LITTLEFS_FOR_IDF_3_2
[scripts_defaults]
extra_scripts = pio-scripts/name-firmware.py
pio-scripts/gzip-firmware.py
pio-scripts/strip-floats.py
pio-scripts/user_config_copy.py
extra_scripts =
pre:pio-scripts/set_version.py
post:pio-scripts/output_bins.py
post:pio-scripts/strip-floats.py
pre:pio-scripts/user_config_copy.py
# ------------------------------------------------------------------------------
# COMMON SETTINGS:
@@ -177,14 +161,9 @@ upload_speed = 115200
# ------------------------------------------------------------------------------
lib_compat_mode = strict
lib_deps =
fastled/FastLED @ 3.3.2
NeoPixelBus @ 2.6.0
ESPAsyncTCP @ 1.2.0
ESPAsyncUDP
AsyncTCP @ 1.0.3
IRremoteESP8266 @ 2.7.3
https://github.com/lorol/LITTLEFS.git
https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.2
fastled/FastLED @ 3.5.0
IRremoteESP8266 @ 2.8.2
https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.4
#For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line
#TFT_eSPI
#For use SSD1306 OLED display uncomment following
@@ -198,10 +177,79 @@ lib_deps =
; adafruit/Adafruit CCS811 Library @ 1.0.4
; adafruit/Adafruit Si7021 Library @ 1.4.0
lib_ignore =
AsyncTCP
extra_scripts = ${scripts_defaults.extra_scripts}
extra_scripts = ${scripts_defaults.extra_scripts}
[esp8266]
build_flags =
-DESP8266
-DFP_IN_IROM
;-Wno-deprecated-declarations
-Wno-register
-Wno-misleading-indentation
; NONOSDK22x_190703 = 2.2.2-dev(38a443e)
-DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703
; lwIP 2 - Higher Bandwidth no Features
; -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
; lwIP 1.4 - Higher Bandwidth (Aircoookie has)
-DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH
; VTABLES in Flash
-DVTABLES_IN_FLASH
; restrict to minimal mime-types
-DMIMETYPE_MINIMAL
lib_deps =
${env.lib_deps}
#https://github.com/lorol/LITTLEFS.git
ESPAsyncTCP @ 1.2.2
ESPAsyncUDP
makuna/NeoPixelBus @ 2.6.9
[esp32]
#platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.3/platform-espressif32-2.0.2.3.zip
platform = espressif32@3.5.0
platform_packages = framework-arduinoespressif32 @ https://github.com/Aircoookie/arduino-esp32.git#1.0.6.4
build_flags = -g
-DARDUINO_ARCH_ESP32
#-DCONFIG_LITTLEFS_FOR_IDF_3_2
-D CONFIG_ASYNC_TCP_USE_WDT=0
#use LITTLEFS library by lorol in ESP32 core 1.x.x instead of built-in in 2.x.x
-D LOROL_LITTLEFS
default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
lib_deps =
${env.lib_deps}
https://github.com/lorol/LITTLEFS.git
makuna/NeoPixelBus @ 2.6.9
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
[esp32s2]
build_flags = -g
-DARDUINO_ARCH_ESP32
-DARDUINO_ARCH_ESP32S2
-DCONFIG_IDF_TARGET_ESP32S2
-D CONFIG_ASYNC_TCP_USE_WDT=0
-DCO
lib_deps =
${env.lib_deps}
makuna/NeoPixelBus @ 2.6.9
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
[esp32c3]
build_flags = -g
-DARDUINO_ARCH_ESP32
-DARDUINO_ARCH_ESP32C3
-DCONFIG_IDF_TARGET_ESP32C3
-D CONFIG_ASYNC_TCP_USE_WDT=0
-DCO
lib_deps =
${env.lib_deps}
makuna/NeoPixelBus @ 2.6.9
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
# ------------------------------------------------------------------------------
# WLED BUILDS
@@ -213,7 +261,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 = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266
lib_deps = ${esp8266.lib_deps}
[env:esp8266_2m]
board = esp_wroom_02
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP02
lib_deps = ${esp8266.lib_deps}
[env:esp01_1m_full]
board = esp01_1m
@@ -221,7 +279,8 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP01 -D WLED_DISABLE_OTA
lib_deps = ${esp8266.lib_deps}
[env:esp07]
board = esp07
@@ -230,6 +289,7 @@ platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
lib_deps = ${esp8266.lib_deps}
[env:d1_mini]
board = d1_mini
@@ -239,6 +299,7 @@ upload_speed = 921600
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
lib_deps = ${esp8266.lib_deps}
monitor_filters = esp8266_exception_decoder
[env:heltec_wifi_kit_8]
@@ -248,6 +309,7 @@ platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
lib_deps = ${esp8266.lib_deps}
[env:h803wf]
board = d1_mini
@@ -256,25 +318,60 @@ platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D LEDPIN=1 -D WLED_DISABLE_INFRARED
lib_deps = ${esp8266.lib_deps}
[env:esp32dev]
board = esp32dev
platform = espressif32@3.2
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32}
lib_ignore =
ESPAsyncTCP
ESPAsyncUDP
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32 #-D WLED_DISABLE_BLYNK #-D WLED_DISABLE_BROWNOUT_DET
lib_deps = ${esp32.lib_deps}
monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32.default_partitions}
[env:esp32dev_qio80]
board = esp32dev
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_qio80 #-D WLED_DISABLE_BLYNK #-D WLED_DISABLE_BROWNOUT_DET
lib_deps = ${esp32.lib_deps}
monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32.default_partitions}
board_build.f_flash = 80000000L
board_build.flash_mode = qio
[env:esp32_eth]
board = esp32-poe
platform = espressif32@3.2
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
upload_speed = 921600
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1
lib_ignore =
ESPAsyncTCP
ESPAsyncUDP
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_Ethernet -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1 -D WLED_DISABLE_BLYNK
lib_deps = ${esp32.lib_deps}
board_build.partitions = ${esp32.default_partitions}
[env:esp32s2_saola]
board = esp32-s2-saola-1
platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.2/platform-tasmota-espressif32-2.0.2.zip
platform_packages =
framework = arduino
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
board_build.flash_mode = qio
upload_speed = 460800
build_unflags = ${common.build_unflags}
lib_deps = ${esp32s2.lib_deps}
[env:esp32c3]
board = esp32-c3-devkitm-1
platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.2/platform-tasmota-espressif32-2.0.2.zip
platform_packages =
framework = arduino
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
upload_speed = 460800
build_unflags = ${common.build_unflags}
lib_deps = ${esp32c3.lib_deps}
[env:esp8285_4CH_MagicHome]
board = esp8285
@@ -282,23 +379,17 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA -D WLED_USE_ANALOG_LEDS
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA
lib_deps = ${esp8266.lib_deps}
[env:esp8285_4CH_H801]
[env:esp8285_H801]
board = esp8285
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA -D WLED_USE_ANALOG_LEDS -D WLED_USE_H801
[env:esp8285_5CH_H801]
board = esp8285
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA -D WLED_USE_ANALOG_LEDS -D WLED_USE_H801 -D WLED_ENABLE_5CH_LEDS
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA
lib_deps = ${esp8266.lib_deps}
[env:d1_mini_5CH_Shojo_PCB]
board = d1_mini
@@ -306,7 +397,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} -D WLED_USE_ANALOG_LEDS -D WLED_USE_SHOJO_PCB -D WLED_ENABLE_5CH_LEDS
build_flags = ${common.build_flags_esp8266} -D WLED_USE_SHOJO_PCB
lib_deps = ${esp8266.lib_deps}
# ------------------------------------------------------------------------------
# DEVELOPMENT BOARDS
@@ -320,6 +412,7 @@ platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} ${common.debug_flags}
lib_deps = ${esp8266.lib_deps}
[env:d1_mini_ota]
board = d1_mini
@@ -331,6 +424,7 @@ platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
lib_deps = ${esp8266.lib_deps}
[env:anavi_miracle_controller]
board = d1_mini
@@ -339,105 +433,75 @@ platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D LEDPIN=12 -D IRPIN=-1 -D RLYPIN=2
lib_deps = ${esp8266.lib_deps}
# ------------------------------------------------------------------------------
# custom board configurations
# ------------------------------------------------------------------------------
[env:custom_LEDPIN_4]
board = d1_mini
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D LEDPIN=4 -D IRPIN=5
[env:custom_LEDPIN_16]
board = d1_mini
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D LEDPIN=16
[env:custom_LEDPIN_3]
board = d1_mini
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D LEDPIN=3
[env:custom_APA102]
board = d1_mini
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D USE_APA102
[env:custom_WS2801]
board = d1_mini
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D USE_WS2801
[env:custom32_LEDPIN_16]
board = esp32dev
platform = espressif32@3.2
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D LEDPIN=16 -D RLYPIN=19
lib_ignore =
ESPAsyncTCP
ESPAsyncUDP
[env:custom32_APA102]
board = esp32dev
platform = espressif32@3.2
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D USE_APA102
lib_ignore =
ESPAsyncTCP
ESPAsyncUDP
[env:custom32_TOUCHPIN_T0]
board = esp32dev
platform = espressif32@3.2
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D TOUCHPIN=T0
lib_ignore =
ESPAsyncTCP
ESPAsyncUDP
[env:wemos_shield_esp32]
board = esp32dev
platform = espressif32@3.2
upload_port = /dev/cu.SLAB_USBtoUART
monitor_port = /dev/cu.SLAB_USBtoUART
upload_speed = 460800
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D LEDPIN=16 -D RLYPIN=19 -D BTNPIN=17
lib_ignore =
ESPAsyncTCP
ESPAsyncUDP
build_flags = ${common.build_flags_esp32}
-D LEDPIN=16
-D RLYPIN=19
-D BTNPIN=17
-D IRPIN=18
-D UWLED_USE_MY_CONFIG
-D USERMOD_DALLASTEMPERATURE
-D USERMOD_FOUR_LINE_DISPLAY
-D TEMPERATURE_PIN=23
lib_deps = ${esp32.lib_deps}
OneWire@~2.3.5
olikraus/U8g2 @ ^2.28.8
board_build.partitions = ${esp32.default_partitions}
[env:m5atom]
board = esp32dev
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D LEDPIN=27 -D BTNPIN=39
lib_ignore =
ESPAsyncTCP
ESPAsyncUDP
lib_deps = ${esp32.lib_deps}
platform = espressif32@3.2
board_build.partitions = ${esp32.default_partitions}
[env:sp501e]
board = esp_wroom_02
platform = ${common.platform_wled_default}
board_build.ldscript = ${common.ldscript_2m512k}
build_flags = ${common.build_flags_esp8266} -D LEDPIN=3 -D BTNPIN=1
lib_deps = ${esp8266.lib_deps}
[env:sp511e]
board = esp_wroom_02
platform = ${common.platform_wled_default}
board_build.ldscript = ${common.ldscript_2m512k}
build_flags = ${common.build_flags_esp8266} -D LEDPIN=3 -D BTNPIN=2 -D IRPIN=5 -D WLED_MAX_BUTTONS=3
lib_deps = ${esp8266.lib_deps}
[env:athom7w]
board = esp_wroom_02
platform = ${common.platform_wled_default}
board_build.ldscript = ${common.ldscript_2m512k}
build_flags = ${common.build_flags_esp8266} -D WLED_MAX_CCT_BLEND=0 -D BTNPIN=-1 -D IRPIN=-1 -D WLED_DISABLE_INFRARED
lib_deps = ${esp8266.lib_deps}
[env:athom15w]
board = esp_wroom_02
platform = ${common.platform_wled_default}
board_build.ldscript = ${common.ldscript_2m512k}
build_flags = ${common.build_flags_esp8266} -D WLED_USE_IC_CCT -D BTNPIN=-1 -D IRPIN=-1 -D WLED_DISABLE_INFRARED
lib_deps = ${esp8266.lib_deps}
[env:MY9291]
board = esp01_1m
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP01 -D WLED_DISABLE_OTA -D USERMOD_MY9291
lib_deps = ${esp8266.lib_deps}
# ------------------------------------------------------------------------------
# travis test board configurations
@@ -457,52 +521,56 @@ build_flags = ${common.build_flags_esp32} ${common.debug_flags} ${common.build_f
# ------------------------------------------------------------------------------
# codm pixel controller board configurations
# codm-controller-0.6 can also be used for the TYWE3S controller
# ------------------------------------------------------------------------------
[env:codm-controller-0.4]
board = esp_wroom_02
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D LEDPIN=3
[env:codm-controller-0.4-WS2801]
board = esp_wroom_02
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D USE_WS2801 -D CLKPIN=13 -D DATAPIN=3
[env:codm-controller-0.4-APA102]
board = esp_wroom_02
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D USE_APA102 -D CLKPIN=13 -D DATAPIN=3
[env:codm-controller-0.5]
[env:codm-controller-0.6]
board = esp_wroom_02
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
lib_deps = ${esp8266.lib_deps}
[env:codm-controller-0.5-WS2801]
[env:codm-controller-0.6-rev2]
board = esp_wroom_02
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D USE_WS2801 #-D CLKPIN=0 -D DATAPIN=2
build_flags = ${common.build_flags_esp8266}
lib_deps = ${esp8266.lib_deps}
[env:codm-controller-0.5-APA102]
board = esp_wroom_02
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D USE_APA102 #-D CLKPIN=0 -D DATAPIN=2
# ------------------------------------------------------------------------------
# EleksTube-IPS
# ------------------------------------------------------------------------------
[env:elekstube_ips]
board = esp32dev
platform = espressif32@3.2
upload_speed = 921600
build_flags = ${common.build_flags_esp32} -D WLED_DISABLE_BROWNOUT_DET -D WLED_DISABLE_INFRARED
-D USERMOD_RTC
-D USERMOD_ELEKSTUBE_IPS
-D LEDPIN=12
-D RLYPIN=27
-D BTNPIN=34
-D WLED_DISABLE_BLYNK
-D DEFAULT_LED_COUNT=6
# Display config
-D ST7789_DRIVER
-D TFT_WIDTH=135
-D TFT_HEIGHT=240
-D CGRAM_OFFSET
-D TFT_SDA_READ
-D TFT_MOSI=23
-D TFT_SCLK=18
-D TFT_DC=25
-D TFT_RST=26
-D SPI_FREQUENCY=40000000
-D USER_SETUP_LOADED
monitor_filters = esp32_exception_decoder
lib_deps =
${esp32.lib_deps}
TFT_eSPI @ ^2.3.70
board_build.partitions = ${esp32.default_partitions}

View File

@@ -12,16 +12,21 @@ board = esp01_1m
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k}
lib_deps = ${esp8266.lib_deps}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
; *********************************************************************
; *** Use custom settings from file my_config.h
-DWLED_USE_MY_CONFIG
; *********************************************************************
;
;
; *** To use the below defines/overrides, copy and paste each onto it's own line just below build_flags in the section above.
;
; disable specific features
; -D WLED_DISABLE_OTA
; -D WLED_DISABLE_ALEXA
; -D WLED_DISABLE_BLYNK
; -D WLED_DISABLE_CRONIXIE
; -D WLED_DISABLE_HUESYNC
; -D WLED_DISABLE_INFRARED
; -D WLED_DISABLE_WEBSOCKETS
@@ -39,12 +44,23 @@ build_flags = ${common.build_flags_esp8266}
; PIN defines for 2 wire LEDs
-D CLKPIN=0
-D DATAPIN=2
; to drive analog LED strips (aka 5050), uncomment the following
; PWM pins 5,12,13,15 are used with Magic Home LED Controller (default)
-D WLED_USE_ANALOG_LEDS
; for the H801 controller (PINs 15,13,12,14 (W2 = 04)) uncomment this
; -D WLED_USE_H801
; for the BW-LT11 controller (PINs 12,4,14,5 ) uncomment this
; -D WLED_USE_BWLT11
; and to enable channel 5 for RGBW-CT led strips this
; -D WLED_USE_5CH_LEDS
; to drive analog LED strips (aka 5050) hardware configuration is no longer necessary
; configure the settings in the UI as follows (hard):
; for the Magic Home LED Controller use PWM pins 5,12,13,15
; for the H801 controller use PINs 15,13,12,14 (W2 = 04)
; for the BW-LT11 controller use PINs 12,4,14,5
;
; set the name of the module - make sure there is a quote-backslash-quote before the name and a backslash-quote-quote after the name
; -D SERVERNAME="\"WLED\""
;
; set the number of LEDs
; -D DEFAULT_LED_COUNT=30
;
; set milliampere limit when using ESP pin to power leds
; -D ABL_MILLIAMPS_DEFAULT=850
;
; enable IR by setting remote type
; -D IRTYPE=0 ;0 Remote disabled | 1 24-key RGB | 2 24-key with CT | 3 40-key blue | 4 40-key RGB | 5 21-key RGB | 6 6-key black | 7 9-key red | 8 JSON remote
;
; set default color order of your led strip
; -D DEFAULT_LED_COLOR_ORDER=COL_ORDER_GRB

View File

@@ -4,12 +4,12 @@
<a href="https://raw.githubusercontent.com/Aircoookie/WLED/master/LICENSE"><img src="https://img.shields.io/github/license/Aircoookie/wled?color=blue&style=flat-square"></a>
<a href="https://wled.discourse.group"><img src="https://img.shields.io/discourse/topics?colorB=blue&label=forum&server=https%3A%2F%2Fwled.discourse.group%2F&style=flat-square"></a>
<a href="https://discord.gg/KuqP7NE"><img src="https://img.shields.io/discord/473448917040758787.svg?colorB=blue&label=discord&style=flat-square"></a>
<a href="https://github.com/Aircoookie/WLED/wiki"><img src="https://img.shields.io/badge/quick_start-wiki-blue.svg?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>
</p>
# Welcome to my project 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!
@@ -27,7 +27,7 @@ A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control
- Presets can be used to automatically execute API calls
- Nightlight function (gradually dims down)
- Full OTA software updatability (HTTP + ArduinoOTA), password protectable
- Configurable analog clock + support for the Cronixie kit by Diamex
- Configurable analog clock (Cronixie, 7-segment and EleksTube IPS clock support via usermods)
- Configurable Auto Brightness limit for safer operation
- Filesystem-based config for easier backup of presets and settings
@@ -37,6 +37,7 @@ A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control
- MQTT
- Blynk IoT
- E1.31, Art-Net, DDP and TPM2.net
- [diyHue](https://github.com/diyhue/diyHue) (Wled is supported by diyHue, including Hue Sync Entertainment under udp. Thanks to [Gregory Mallios](https://github.com/gmallios))
- [Hyperion](https://github.com/hyperion-project/hyperion.ng)
- UDP realtime
- Alexa voice control (including dimming and color)
@@ -48,42 +49,21 @@ A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control
## 📲 Quick start guide and documentation
See the [wiki](https://github.com/Aircoookie/WLED/wiki)!
See the [documentation on our official site](https://kno.wled.ge)!
[On this page](https://github.com/Aircoookie/WLED/wiki/Learning-the-ropes) you can find excellent tutorials made by the community and helpful tools to help you get your new lamp up and running!
[On this page](https://kno.wled.ge/basics/tutorials/) you can find excellent tutorials made by the community and helpful tools to help you get your new lamp up and running!
## 🖼️ Images
## 🖼️ User interface
<img src="/images/macbook-pro-space-gray-on-the-wooden-table.jpg" width="50%"><img src="/images/walking-with-iphone-x.jpg" width="50%">
## 💾 Compatible LED Strips
Type | Voltage | Comments
|---|---|---|
WS2812B | 5v |
WS2813 | 5v |
SK6812 | 5v | RGBW
APA102 | 5v | C/D
WS2801 | 5v | C/D
LPD8806 | 5v | C/D
TM1814 | 12v | RGBW
WS2811 | 12v | 3-LED segments
WS2815 | 12v |
GS8208 | 12v |
Analog/non-addressable | any | Requires additional circuitry
## 🧊 Compatible PC RGB Fans and ARGB accessories
Brand | Model | Comments
|---|---|---|
Corsair | HD120 Fan | Uses WS2812B, data-in only
PCCOOLER | Moonlight 5-pack Fans | Uses WS2812B, includes Data-out connector to keep each fan uniquely addressable if wired in series like traditional LED strips
Any | 5v 3-pin ARGB for PC | Any PC RGB device that supports the 5v 3-pin ARGB motherboard header should work fine with WLED. All the major motherboard vendors support the Corsair HD120 and PCCOOLER fans listed, so we can safely assume any device that supports motherboard ARGB 5V 3-Pin standard will work with WLED.
## 💾 Compatible hardware
See [here](https://kno.wled.ge/basics/compatible-hardware)!
## ✌️ Other
Licensed under the MIT license
Credits [here](https://github.com/Aircoookie/WLED/wiki/Contributors-&-About)!
Uses Linearicons by Perxis!
Credits [here](https://kno.wled.ge/about/contributors/)!
Join the Discord server to discuss everything about WLED!

1
requirements.in Normal file
View File

@@ -0,0 +1 @@
platformio

70
requirements.txt Normal file
View File

@@ -0,0 +1,70 @@
#
# This file is autogenerated by pip-compile with python 3.8
# To update, run:
#
# pip-compile
#
aiofiles==0.8.0
# via platformio
ajsonrpc==1.2.0
# via platformio
anyio==3.6.1
# via starlette
async-timeout==4.0.2
# via zeroconf
bottle==0.12.23
# via platformio
certifi==2022.6.15
# via requests
charset-normalizer==2.1.1
# via requests
click==8.1.3
# via
# platformio
# uvicorn
colorama==0.4.5
# via
# click
# platformio
h11==0.13.0
# via
# uvicorn
# wsproto
idna==3.3
# via
# anyio
# requests
ifaddr==0.2.0
# via zeroconf
marshmallow==3.17.0
# via platformio
packaging==21.3
# via marshmallow
platformio==6.1.4
# via -r requirements.in
pyelftools==0.29
# via platformio
pyparsing==3.0.9
# via packaging
pyserial==3.5
# via platformio
requests==2.28.1
# via platformio
semantic-version==2.10.0
# via platformio
sniffio==1.2.0
# via anyio
starlette==0.20.4
# via platformio
tabulate==0.8.10
# via platformio
typing-extensions==4.3.0
# via starlette
urllib3==1.26.11
# via requests
uvicorn==0.18.2
# via platformio
wsproto==1.1.0
# via platformio
zeroconf==0.39.0
# via platformio

View File

@@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x200000,
app1, app, ota_1, 0x210000,0x200000,
spiffs, data, spiffs, 0x410000,0xBE0000,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x5000
3 otadata data ota 0xe000 0x2000
4 app0 app ota_0 0x10000 0x200000
5 app1 app ota_1 0x210000 0x200000
6 spiffs data spiffs 0x410000 0xBE0000

View File

@@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x180000,
app1, app, ota_1, 0x190000,0x180000,
spiffs, data, spiffs, 0x310000,0xF0000,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x5000
3 otadata data ota 0xe000 0x2000
4 app0 app ota_0 0x10000 0x180000
5 app1 app ota_1 0x190000 0x180000
6 spiffs data spiffs 0x310000 0xF0000

6
tools/WLED_ESP32_8MB.csv Normal file
View File

@@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x200000,
app1, app, ota_1, 0x210000,0x200000,
spiffs, data, spiffs, 0x410000,0x3F0000,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x5000
3 otadata data ota 0xe000 0x2000
4 app0 app ota_0 0x10000 0x200000
5 app1 app ota_1 0x210000 0x200000
6 spiffs data spiffs 0x410000 0x3F0000

View File

@@ -90,7 +90,7 @@ function writeHtmlGzipped(sourceFile, resultFile) {
* Binary array for the Web UI.
* gzip is used for smaller size and improved speeds.
*
* Please see https://github.com/Aircoookie/WLED/wiki/Add-own-functionality#web-ui
* Please see https://kno.wled.ge/advanced/custom-features/#changing-web-ui
* to find out how to easily modify the web UI source!
*/
@@ -175,7 +175,7 @@ function writeChunks(srcDir, specs, resultFile) {
let src = `/*
* More web UI HTML source arrays.
* This file is auto generated, please don't make any changes manually.
* Instead, see https://github.com/Aircoookie/WLED/wiki/Add-own-functionality#web-ui
* Instead, see https://kno.wled.ge/advanced/custom-features/#changing-web-ui
* to find out how to easily modify the web UI source!
*/
`;
@@ -217,7 +217,7 @@ writeChunks(
mangle: (str) =>
str
.replace("%", "%%")
.replace(/User Interface\<\/button\>\<\/form\>/gms, "User Interface\<\/button\>\<\/form\>%DMXMENU%"),
.replace(/Usermods\<\/button\>\<\/form\>/gms, "Usermods\<\/button\>\<\/form\>%DMXMENU%"),
},
{
file: "settings_wifi.htm",
@@ -333,6 +333,22 @@ const char PAGE_settings_dmx[] PROGMEM = R"=====()=====";
"function GetV() {var d=document;\n"
),
},
{
file: "settings_um.htm",
name: "PAGE_settings_um",
prepend: "=====(",
append: ")=====",
method: "plaintext",
filter: "html-minify",
mangle: (str) =>
str
.replace(/\<link rel="stylesheet".*\>/gms, "")
.replace(/\<style\>.*\<\/style\>/gms, "%CSS%%SCSS%")
.replace(
/function GetV().*\<\/script\>/gms,
"function GetV() {var d=document;\n"
),
}
],
"wled00/html_settings.h"
);

232
tools/fps_test.htm Normal file
View File

@@ -0,0 +1,232 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>WLED frame rate test tool</title>
<style>
body {
background-color: #222;
color: #fff;
font-family: Helvetica, Verdana, sans-serif;
}
input {
background-color: #333;
color: #fff;
}
#ip {
width: 100px;
}
#secs {
width: 36px;
}
#csva {
position: absolute;
top: -100px; /*gtfo*/
}
button {
background-color: #333;
color: #fff;
}
table, th, td {
border: 1px solid #aaa;
border-collapse: collapse;
text-align: center;
}
.red {
color: #d20;
}
</style>
<script>
var gotfx = false, running = false;
var pos = 0, prev = 0, min = 999, max = 0, fpslist = [], names = [], names_checked = [];
var to;
function S() {
document.getElementById('ip').value = localStorage.getItem('locIpFps');
if (document.getElementById('ip').value) req(false);
}
function loadC() {
hide(false);
var list = localStorage.getItem('fpsFxSelection');
if (!list) return;
list = JSON.parse(list);
var chks = document.querySelectorAll('.fxcheck');
for (let i = 0; i < chks.length; i++) {
if (i < list.length) chks[i].checked = list[i];
}
}
function saveC() {
var list = [];
var chks = document.querySelectorAll('.fxcheck');
for (let i = 0; i < chks.length; i++) {
list.push(chks[i].checked);
}
localStorage.setItem('fpsFxSelection', JSON.stringify(list));
}
function setC(c) {
hide(false);
var chks = document.querySelectorAll('.fxcheck');
for (let i = 0; i < chks.length; i++) {
chks[i].checked = (c == 255);
}
if (c == 1 && chks.length > 100) {
chks[1].checked = true; //Blink
chks[15].checked = true; //Running
chks[16].checked = true; //Saw
chks[37].checked = true; //Running 2
chks[44].checked = true; //Tetrix
chks[63].checked = true; //Pride 2015
chks[74].checked = true; //Colortwinkles
chks[101].checked = true;//Pacifica
}
}
function hide(h) {
var trs = document.querySelectorAll('.trs');
var chks = document.querySelectorAll('.fxcheck');
for (let i = 0; i < trs.length; i++) {
trs[i].style.display = (h && !chks[i].checked) ? "none":"table-row";
}
}
function run(init) {
if (init) {
running = !running;
document.getElementById('runbtn').innerText = running ? 'Stop':'Run';
if (running) {pos = 0; prev = -1; min = 999; max = 0; fpslist = []; names_checked = []; hide(true);}
clearTimeout(to);
if (!running) {req({seg:{fx:0},v:true,stop:true}); return;}
}
if (!gotfx) {req(false); return;}
var chks = document.querySelectorAll('.fxcheck');
var fpsb = document.querySelectorAll('.fps');
if (prev >= 0) {pos++};
if (pos >= chks.length) {run(true); return;} //end
while (!chks[pos].checked) {
fpsb[pos].innerText = "-";
pos++;
if (pos >= chks.length) {run(true); return;} //end
}
names_checked.push(names[pos]);
var extra = {};
try {
extra = JSON.parse(document.getElementById('ej').value);
} catch (e) {
}
var cmd = {seg:{fx:pos},v:true};
Object.assign(cmd, extra);
req(cmd);
}
function req(command) {
var ip = document.getElementById('ip').value;
if (!ip) {alert("Please enter WLED IP"); return;}
if (ip != localStorage.getItem('locIpFps')) localStorage.setItem('locIpFps', document.getElementById('ip').value);
var url = command ? `http://${ip}/json/si` : `http://${ip}/json/effects`;
var type = command ? 'post':'get';
var req = undefined;
if (command)
{
req = JSON.stringify(command);
}
fetch
(url, {
method: type,
headers: {
"Content-type": "application/json; charset=UTF-8"
},
body: req
})
.then(res => {
if (!res.ok) {
alert('Data malfunction');
}
return res.json();
})
.then(json => {
if (!json) {
alert('Empty response'); return;
}
if (!command) {
names = json;
var tblc = '';
for (let i = 0; i < json.length; i++) {
tblc += `<tr class="trs"><td><input type="checkbox" class="fxcheck" /></td><td>${i}</td><td>${json[i]}</td><td class="fps"></td></tr>`
}
var tbl = `<table>
<tr>
<th>Test?</th><th>ID</th><th>Effect Name</th><th>FPS</th>
</tr>
${tblc}
</table>`;
document.getElementById('tablecon').innerHTML = tbl;
setC(1);
loadC();
gotfx = true;
document.getElementById('runbtn').innerText = "Run";
} else {
if (!json.info) return;
document.getElementById('leds').innerText = json.info.leds.count;
document.getElementById('seg').innerText = json.state.seg[0].len;
document.getElementById('bri').innerText = json.state.bri;
if (prev >= 0) {
var lastfps = parseInt(json.info.leds.fps); //previous FX
if (lastfps < min) min = lastfps;
if (lastfps > max) max = lastfps;
fpslist.push(lastfps);
var sum = 0;
for (let i = 0; i < fpslist.length; i++) {
sum += fpslist[i];
}
sum /= fpslist.length;
document.getElementById('fps_min').innerText = min;
document.getElementById('fps_max').innerText = max;
document.getElementById('fps_avg').innerText = Math.round(sum*10)/10;
var fpsb = document.querySelectorAll('.fps');
fpsb[prev].innerHTML = lastfps;
}
prev = pos;
var delay = parseInt(document.getElementById('secs').value)*1000;
delay = Math.min(Math.max(delay, 2000), 15000)
if (!command.stop) to = setTimeout(run,delay);
}
})
.catch(function (error) {
alert('Comms malfunction');
console.log(error);
});
}
function csv(n) {
var txt = "";
for (let i = 0; i < fpslist.length; i++) {
if (!n) txt += names_checked[i] + ',';
txt += fpslist[i]; txt += "\n";
}
document.getElementById('csva').value = txt;
var copyText = document.getElementById('csva');
copyText.select();
copyText.setSelectionRange(0, 999999);
document.execCommand("copy");
}
</script>
</head>
<body onload="S()">
<h2>Starship monitoring dashboard</h2>
(or rather just a WLED frame rate tester lol)<br><br>
IP: <input id="ip" /><br>
Time per effect: <input type=number id=secs value=5 max=15 min=2 />s<br>
Effects to test:
<button type="button" onclick="setC(255)">All</button>
<button type="button" onclick="setC(1)">Selection 1</button>
<button type="button" onclick="setC(0)">None</button>
<button type="button" onclick="loadC()">Get LS</button>
<button type="button" class="red" onclick="saveC()">Save to LS</button><br>
Extra JSON: <input id="ej" /><br>
<button type="button" onclick="run(true)" id="runbtn">Fetch FX list</button><br>
LEDs: <span id="leds">-</span>, Seg: <span id="seg">-</span>, Bri: <span id="bri">-</span><br>
FPS min: <span id="fps_min">-</span>, max: <span id="fps_max">-</span>, avg: <span id="fps_avg">-</span><br><br>
<div id="tablecon">
</div><br>
<button type="button" onclick="csv(false)">Copy csv to clipboard</button>
<button type="button" onclick="csv(true)">Copy csv (FPS only)</button>
<textarea id=csva></textarea>
</body>
</html>

16
tools/multi-update.cmd Normal file
View File

@@ -0,0 +1,16 @@
@echo off
SETLOCAL
SET FWPATH=c:\path\to\your\WLED\build_output\firmware
GOTO ESPS
:UPDATEONE
IF NOT EXIST %FWPATH%\%2 GOTO SKIP
ping -w 1000 -n 1 %1 | find "TTL=" || GOTO SKIP
ECHO Updating %1
curl -s -F "update=@%FWPATH%/%2" %1/update >nul
:SKIP
GOTO:EOF
:ESPS
call :UPDATEONE 192.168.x.x firmware.bin
call :UPDATEONE ....

19
tools/multi-update.sh Normal file
View File

@@ -0,0 +1,19 @@
#!/bin/bash
FWPATH=/path/to/your/WLED/build_output/firmware
update_one() {
if [ -f $FWPATH/$2 ]; then
ping -c 1 $1 >/dev/null
PINGRESULT=$?
if [ $PINGRESULT -eq 0 ]; then
echo Updating $1
curl -s -F "update=@${FWPATH}/$2" $1/update >/dev/null
return 0
fi
return 1
fi
}
update_one 192.168.x.x firmware.bin
update_one 192.168.x.x firmware.bin
# ...

View File

@@ -9,419 +9,526 @@
*/
#pragma once
#include "wled.h"
#include "Animated_Staircase_config.h"
#define USERMOD_ID_ANIMATED_STAIRCASE 1011
/* Initial configuration (available in API and stored in flash) */
bool enabled = true; // Enable this usermod
unsigned long segment_delay_ms = 150; // Time between switching each segment
unsigned long on_time_ms = 5 * 1000; // The time for the light to stay on
#ifndef TOP_PIR_PIN
unsigned int topMaxTimeUs = 1749; // default echo timout, top
#endif
#ifndef BOTTOM_PIR_PIN
unsigned int bottomMaxTimeUs = 1749; // default echo timout, bottom
#endif
// Time between checking of the sensors
const int scanDelay = 50;
class Animated_Staircase : public Usermod {
private:
// Lights on or off.
// Flipping this will start a transition.
bool on = false;
private:
// Swipe direction for current transition
#define SWIPE_UP true
#define SWIPE_DOWN false
bool swipe = SWIPE_UP;
/* configuration (available in API and stored in flash) */
bool enabled = false; // Enable this usermod
unsigned long segment_delay_ms = 150; // Time between switching each segment
unsigned long on_time_ms = 30000; // The time for the light to stay on
int8_t topPIRorTriggerPin = -1; // disabled
int8_t bottomPIRorTriggerPin = -1; // disabled
int8_t topEchoPin = -1; // disabled
int8_t bottomEchoPin = -1; // disabled
bool useUSSensorTop = false; // using PIR or UltraSound sensor?
bool useUSSensorBottom = false; // using PIR or UltraSound sensor?
unsigned int topMaxDist = 50; // default maximum measured distance in cm, top
unsigned int bottomMaxDist = 50; // default maximum measured distance in cm, bottom
// Indicates which Sensor was seen last (to determine
// the direction when swiping off)
#define LOWER false
#define UPPER true
bool lastSensor = LOWER;
/* runtime variables */
bool initDone = false;
// Time of the last transition action
unsigned long lastTime = 0;
// Time between checking of the sensors
const unsigned int scanDelay = 100;
// Time of the last sensor check
unsigned long lastScanTime = 0;
// Lights on or off.
// Flipping this will start a transition.
bool on = false;
// Last time the lights were switched on or off
unsigned long lastSwitchTime = 0;
// Swipe direction for current transition
#define SWIPE_UP true
#define SWIPE_DOWN false
bool swipe = SWIPE_UP;
// segment id between onIndex and offIndex are on.
// controll the swipe by setting/moving these indices around.
// onIndex must be less than or equal to offIndex
byte onIndex = 0;
byte offIndex = 0;
// Indicates which Sensor was seen last (to determine
// the direction when swiping off)
#define LOWER false
#define UPPER true
bool lastSensor = LOWER;
// The maximum number of configured segments.
// Dynamically updated based on user configuration.
byte maxSegmentId = 1;
byte mainSegmentId = 0;
// Time of the last transition action
unsigned long lastTime = 0;
bool saveState = false;
// Time of the last sensor check
unsigned long lastScanTime = 0;
// These values are used by the API to read the
// last sensor state, or trigger a sensor
// through the API
bool topSensorRead = false;
bool topSensorWrite = false;
bool bottomSensorRead = false;
bool bottomSensorWrite = false;
// Last time the lights were switched on or off
unsigned long lastSwitchTime = 0;
void updateSegments() {
mainSegmentId = strip.getMainSegmentId();
WS2812FX::Segment mainsegment = strip.getSegment(mainSegmentId);
WS2812FX::Segment* segments = strip.getSegments();
for (int i = 0; i < MAX_NUM_SEGMENTS; i++, segments++) {
if (!segments->isActive()) {
maxSegmentId = i - 1;
break;
}
// segment id between onIndex and offIndex are on.
// controll the swipe by setting/moving these indices around.
// onIndex must be less than or equal to offIndex
byte onIndex = 0;
byte offIndex = 0;
if (i >= onIndex && i < offIndex) {
segments->setOption(SEG_OPTION_ON, 1, 1);
// The maximum number of configured segments.
// Dynamically updated based on user configuration.
byte maxSegmentId = 1;
byte mainSegmentId = 0;
// We may need to copy mode and colors from segment 0 to make sure
// changes are propagated even when the config is changed during a wipe
// segments->mode = mainsegment.mode;
// segments->colors[0] = mainsegment.colors[0];
} else {
segments->setOption(SEG_OPTION_ON, 0, 1);
}
// Always mark segments as "transitional", we are animating the staircase
segments->setOption(SEG_OPTION_TRANSITIONAL, 1, 1);
}
colorUpdated(NOTIFIER_CALL_MODE_DIRECT_CHANGE);
}
// These values are used by the API to read the
// last sensor state, or trigger a sensor
// through the API
bool topSensorRead = false;
bool topSensorWrite = false;
bool bottomSensorRead = false;
bool bottomSensorWrite = false;
bool topSensorState = false;
bool bottomSensorState = false;
/*
* Detects if an object is within ultrasound range.
* signalPin: The pin where the pulse is sent
* echoPin: The pin where the echo is received
* maxTimeUs: Detection timeout in microseconds. If an echo is
* received within this time, an object is detected
* and the function will return true.
*
* The speed of sound is 343 meters per second at 20 degress Celcius.
* Since the sound has to travel back and forth, the detection
* distance for the sensor in cm is (0.0343 * maxTimeUs) / 2.
*
* For practical reasons, here are some useful distances:
*
* Distance = maxtime
* 5 cm = 292 uS
* 10 cm = 583 uS
* 20 cm = 1166 uS
* 30 cm = 1749 uS
* 50 cm = 2915 uS
* 100 cm = 5831 uS
*/
bool ultrasoundRead(uint8_t signalPin,
uint8_t echoPin,
unsigned int maxTimeUs) {
digitalWrite(signalPin, HIGH);
delayMicroseconds(10);
digitalWrite(signalPin, LOW);
return pulseIn(echoPin, HIGH, maxTimeUs) > 0;
}
void checkSensors() {
if ((millis() - lastScanTime) > scanDelay) {
lastScanTime = millis();
#ifdef BOTTOM_PIR_PIN
bottomSensorRead = bottomSensorWrite || (digitalRead(BOTTOM_PIR_PIN) == HIGH);
#else
bottomSensorRead = bottomSensorWrite || ultrasoundRead(BOTTOM_TRIGGER_PIN, BOTTOM_ECHO_PIN, bottomMaxTimeUs);
#endif
#ifdef TOP_PIR_PIN
topSensorRead = topSensorWrite || (digitalRead(TOP_PIR_PIN) == HIGH);
#else
topSensorRead = topSensorWrite || ultrasoundRead(TOP_TRIGGER_PIN, TOP_ECHO_PIN, topMaxTimeUs);
#endif
// Values read, reset the flags for next API call
topSensorWrite = false;
bottomSensorWrite = false;
if (topSensorRead != bottomSensorRead) {
lastSwitchTime = millis();
if (on) {
lastSensor = topSensorRead;
} else {
// If the bottom sensor triggered, we need to swipe up, ON
swipe = bottomSensorRead;
if (swipe) {
Serial.println("ON -> Swipe up.");
} else {
Serial.println("ON -> Swipe down.");
}
if (onIndex == offIndex) {
// Position the indices for a correct on-swipe
if (swipe == SWIPE_UP) {
onIndex = mainSegmentId;
} else {
onIndex = maxSegmentId+1;
}
offIndex = onIndex;
}
on = true;
}
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
static const char _enabled[];
static const char _segmentDelay[];
static const char _onTime[];
static const char _useTopUltrasoundSensor[];
static const char _topPIRorTrigger_pin[];
static const char _topEcho_pin[];
static const char _useBottomUltrasoundSensor[];
static const char _bottomPIRorTrigger_pin[];
static const char _bottomEcho_pin[];
static const char _topEchoCm[];
static const char _bottomEchoCm[];
void publishMqtt(bool bottom, const char* state)
{
//Check if MQTT Connected, otherwise it will crash the 8266
if (WLED_MQTT_CONNECTED){
char subuf[64];
sprintf_P(subuf, PSTR("%s/motion/%d"), mqttDeviceTopic, (int)bottom);
mqtt->publish(subuf, 0, false, state);
}
}
}
void autoPowerOff() {
if (on && ((millis() - lastSwitchTime) > on_time_ms)) {
// Swipe OFF in the direction of the last sensor detection
swipe = lastSensor;
on = false;
if (swipe) {
Serial.println("OFF -> Swipe up.");
} else {
Serial.println("OFF -> Swipe down.");
}
}
}
void updateSwipe() {
if ((millis() - lastTime) > segment_delay_ms) {
lastTime = millis();
byte oldOnIndex = onIndex;
byte oldOffIndex = offIndex;
if (on) {
// Turn on all segments
onIndex = MAX(mainSegmentId, onIndex - 1);
offIndex = MIN(maxSegmentId + 1, offIndex + 1);
} else {
if (swipe == SWIPE_UP) {
onIndex = MIN(offIndex, onIndex + 1);
} else {
offIndex = MAX(onIndex, offIndex - 1);
}
}
updateSegments();
}
}
void writeSettingsToJson(JsonObject& root) {
JsonObject staircase = root["staircase"];
if (staircase.isNull()) {
staircase = root.createNestedObject("staircase");
}
staircase["enabled"] = enabled;
staircase["segment-delay-ms"] = segment_delay_ms;
staircase["on-time-s"] = on_time_ms / 1000;
#ifdef TOP_TRIGGER_PIN
staircase["top-echo-us"] = topMaxTimeUs;
#endif
#ifdef BOTTOM_TRIGGER_PIN
staircase["bottom-echo-us"] = bottomMaxTimeUs;
#endif
}
void writeSensorsToJson(JsonObject& root) {
JsonObject staircase = root["staircase"];
if (staircase.isNull()) {
staircase = root.createNestedObject("staircase");
}
staircase["top-sensor"] = topSensorRead;
staircase["bottom-sensor"] = bottomSensorRead;
}
bool readSettingsFromJson(JsonObject& root) {
JsonObject staircase = root["staircase"];
bool changed = false;
bool shouldEnable = staircase["enabled"] | enabled;
if (shouldEnable != enabled) {
enable(shouldEnable);
changed = true;
}
unsigned long c_segment_delay_ms = staircase["segment-delay-ms"] | segment_delay_ms;
if (c_segment_delay_ms != segment_delay_ms) {
segment_delay_ms = c_segment_delay_ms;
changed = true;
}
unsigned long c_on_time_ms = (staircase["on-time-s"] | (on_time_ms / 1000)) * 1000;
if (c_on_time_ms != on_time_ms) {
on_time_ms = c_on_time_ms;
changed = true;
}
#ifdef TOP_TRIGGER_PIN
unsigned int c_topMaxTimeUs = staircase["top-echo-us"] | topMaxTimeUs;
if (c_topMaxTimeUs != topMaxTimeUs) {
topMaxTimeUs = c_topMaxTimeUs;
changed = true;
}
#endif
#ifdef BOTTOM_TRIGGER_PIN
unsigned int c_bottomMaxTimeUs = staircase["bottom-echo-us"] | bottomMaxTimeUs;
if (c_bottomMaxTimeUs != bottomMaxTimeUs) {
bottomMaxTimeUs = c_bottomMaxTimeUs;
changed = true;
}
#endif
return changed;
}
void readSensorsFromJson(JsonObject& root) {
JsonObject staircase = root["staircase"];
bottomSensorWrite = bottomSensorRead || (staircase["bottom-sensor"].as<bool>());
topSensorWrite = topSensorRead || (staircase["top-sensor"].as<bool>());
}
void enable(bool enable) {
if (enable) {
Serial.println("Animated Staircase enabled.");
Serial.print("Delay between steps: ");
Serial.print(segment_delay_ms, DEC);
Serial.print(" milliseconds.\nStairs switch off after: ");
Serial.print(on_time_ms / 1000, DEC);
Serial.println(" seconds.");
#ifdef BOTTOM_PIR_PIN
pinMode(BOTTOM_PIR_PIN, INPUT);
#else
pinMode(BOTTOM_TRIGGER_PIN, OUTPUT);
pinMode(BOTTOM_ECHO_PIN, INPUT);
#endif
#ifdef TOP_PIR_PIN
pinMode(TOP_PIR_PIN, INPUT);
#else
pinMode(TOP_TRIGGER_PIN, OUTPUT);
pinMode(TOP_ECHO_PIN, INPUT);
#endif
} else {
// Restore segment options
WS2812FX::Segment mainsegment = strip.getSegment(mainSegmentId);
void updateSegments() {
mainSegmentId = strip.getMainSegmentId();
WS2812FX::Segment* segments = strip.getSegments();
for (int i = 0; i < MAX_NUM_SEGMENTS; i++, segments++) {
if (!segments->isActive()) {
maxSegmentId = i - 1;
break;
}
segments->setOption(SEG_OPTION_ON, 1, 1);
if (i >= onIndex && i < offIndex) {
segments->setOption(SEG_OPTION_ON, 1, i);
// We may need to copy mode and colors from segment 0 to make sure
// changes are propagated even when the config is changed during a wipe
// segments->mode = mainsegment.mode;
// segments->colors[0] = mainsegment.colors[0];
} else {
segments->setOption(SEG_OPTION_ON, 0, i);
}
// Always mark segments as "transitional", we are animating the staircase
segments->setOption(SEG_OPTION_TRANSITIONAL, 1, i);
}
colorUpdated(NOTIFIER_CALL_MODE_DIRECT_CHANGE);
Serial.println("Animated Staircase disabled.");
}
enabled = enable;
}
public:
void setup() { enable(enabled); }
void loop() {
// Write changed settings from to flash (see readFromJsonState())
if (saveState) {
serializeConfig();
saveState = false;
colorUpdated(CALL_MODE_DIRECT_CHANGE);
}
if (!enabled) {
return;
/*
* Detects if an object is within ultrasound range.
* signalPin: The pin where the pulse is sent
* echoPin: The pin where the echo is received
* maxTimeUs: Detection timeout in microseconds. If an echo is
* received within this time, an object is detected
* and the function will return true.
*
* The speed of sound is 343 meters per second at 20 degress Celcius.
* Since the sound has to travel back and forth, the detection
* distance for the sensor in cm is (0.0343 * maxTimeUs) / 2.
*
* For practical reasons, here are some useful distances:
*
* Distance = maxtime
* 5 cm = 292 uS
* 10 cm = 583 uS
* 20 cm = 1166 uS
* 30 cm = 1749 uS
* 50 cm = 2915 uS
* 100 cm = 5831 uS
*/
bool ultrasoundRead(int8_t signalPin, int8_t echoPin, unsigned int maxTimeUs) {
if (signalPin<0 || echoPin<0) return false;
digitalWrite(signalPin, LOW);
delayMicroseconds(2);
digitalWrite(signalPin, HIGH);
delayMicroseconds(10);
digitalWrite(signalPin, LOW);
return pulseIn(echoPin, HIGH, maxTimeUs) > 0;
}
checkSensors();
autoPowerOff();
updateSwipe();
bool checkSensors() {
bool sensorChanged = false;
}
if ((millis() - lastScanTime) > scanDelay) {
lastScanTime = millis();
uint16_t getId() { return USERMOD_ID_ANIMATED_STAIRCASE; }
bottomSensorRead = bottomSensorWrite ||
(!useUSSensorBottom ?
(bottomPIRorTriggerPin<0 ? false : digitalRead(bottomPIRorTriggerPin)) :
ultrasoundRead(bottomPIRorTriggerPin, bottomEchoPin, bottomMaxDist*59) // cm to us
);
topSensorRead = topSensorWrite ||
(!useUSSensorTop ?
(topPIRorTriggerPin<0 ? false : digitalRead(topPIRorTriggerPin)) :
ultrasoundRead(topPIRorTriggerPin, topEchoPin, topMaxDist*59) // cm to us
);
/*
* Shows configuration settings to the json API. This object looks like:
*
* "staircase" : {
* "enabled" : true
* "segment-delay-ms" : 150,
* "on-time-s" : 5
* }
*
*/
void addToJsonState(JsonObject& root) {
writeSettingsToJson(root);
writeSensorsToJson(root);
Serial.println("Staircase config exposed in API.");
}
if (bottomSensorRead != bottomSensorState) {
bottomSensorState = bottomSensorRead; // change previous state
sensorChanged = true;
publishMqtt(true, bottomSensorState ? "on" : "off");
DEBUG_PRINTLN(F("Bottom sensor changed."));
}
/*
* Reads configuration settings from the json API.
* See void addToJsonState(JsonObject& root)
*/
void readFromJsonState(JsonObject& root) {
// The call to serializeConfig() must be done in the main loop,
// so we set a flag to signal the main loop to save state.
saveState = readSettingsFromJson(root);
readSensorsFromJson(root);
Serial.println("Staircase config read from API.");
}
if (topSensorRead != topSensorState) {
topSensorState = topSensorRead; // change previous state
sensorChanged = true;
publishMqtt(false, topSensorState ? "on" : "off");
DEBUG_PRINTLN(F("Top sensor changed."));
}
/*
* Writes the configuration to internal flash memory.
*/
void addToConfig(JsonObject& root) {
writeSettingsToJson(root);
Serial.println("Staircase config saved.");
}
// Values read, reset the flags for next API call
topSensorWrite = false;
bottomSensorWrite = false;
/*
* Reads the configuration to internal flash memory before setup() is called.
*/
void readFromConfig(JsonObject& root) {
readSettingsFromJson(root);
Serial.println("Staircase config loaded.");
}
if (topSensorRead != bottomSensorRead) {
lastSwitchTime = millis();
/*
* Shows the delay between steps and power-off time in the "info"
* tab of the web-UI.
*/
void addToJsonInfo(JsonObject& root) {
JsonObject staircase = root["u"];
if (staircase.isNull()) {
staircase = root.createNestedObject("u");
if (on) {
lastSensor = topSensorRead;
} else {
// If the bottom sensor triggered, we need to swipe up, ON
swipe = bottomSensorRead;
DEBUG_PRINT(F("ON -> Swipe "));
DEBUG_PRINTLN(swipe ? F("up.") : F("down."));
if (onIndex == offIndex) {
// Position the indices for a correct on-swipe
if (swipe == SWIPE_UP) {
onIndex = mainSegmentId;
} else {
onIndex = maxSegmentId+1;
}
offIndex = onIndex;
}
on = true;
}
}
}
return sensorChanged;
}
if (enabled) {
JsonArray usermodEnabled =
staircase.createNestedArray("Staircase enabled"); // name
usermodEnabled.add("yes"); // value
void autoPowerOff() {
if (on && ((millis() - lastSwitchTime) > on_time_ms)) {
// if sensors are still on, do nothing
if (bottomSensorState || topSensorState) return;
JsonArray segmentDelay =
staircase.createNestedArray("Delay between stairs"); // name
segmentDelay.add(segment_delay_ms); // value
segmentDelay.add(" milliseconds"); // unit
// Swipe OFF in the direction of the last sensor detection
swipe = lastSensor;
on = false;
JsonArray onTime =
staircase.createNestedArray("Power-off stairs after"); // name
onTime.add(on_time_ms / 1000); // value
onTime.add(" seconds"); // unit
} else {
JsonArray usermodEnabled =
staircase.createNestedArray("Staircase enabled"); // name
usermodEnabled.add("no"); // value
DEBUG_PRINT(F("OFF -> Swipe "));
DEBUG_PRINTLN(swipe ? F("up.") : F("down."));
}
}
}
};
void updateSwipe() {
if ((millis() - lastTime) > segment_delay_ms) {
lastTime = millis();
if (on) {
// Turn on all segments
onIndex = MAX(mainSegmentId, onIndex - 1);
offIndex = MIN(maxSegmentId + 1, offIndex + 1);
} else {
if (swipe == SWIPE_UP) {
onIndex = MIN(offIndex, onIndex + 1);
} else {
offIndex = MAX(onIndex, offIndex - 1);
}
}
updateSegments();
}
}
// send sesnor values to JSON API
void writeSensorsToJson(JsonObject& staircase) {
staircase[F("top-sensor")] = topSensorRead;
staircase[F("bottom-sensor")] = bottomSensorRead;
}
// allow overrides from JSON API
void readSensorsFromJson(JsonObject& staircase) {
bottomSensorWrite = bottomSensorState || (staircase[F("bottom-sensor")].as<bool>());
topSensorWrite = topSensorState || (staircase[F("top-sensor")].as<bool>());
}
void enable(bool enable) {
if (enable) {
DEBUG_PRINTLN(F("Animated Staircase enabled."));
DEBUG_PRINT(F("Delay between steps: "));
DEBUG_PRINT(segment_delay_ms);
DEBUG_PRINT(F(" milliseconds.\nStairs switch off after: "));
DEBUG_PRINT(on_time_ms / 1000);
DEBUG_PRINTLN(F(" seconds."));
if (!useUSSensorBottom)
pinMode(bottomPIRorTriggerPin, INPUT_PULLUP);
else {
pinMode(bottomPIRorTriggerPin, OUTPUT);
pinMode(bottomEchoPin, INPUT);
}
if (!useUSSensorTop)
pinMode(topPIRorTriggerPin, INPUT_PULLUP);
else {
pinMode(topPIRorTriggerPin, OUTPUT);
pinMode(topEchoPin, INPUT);
}
} else {
// Restore segment options
WS2812FX::Segment* segments = strip.getSegments();
for (int i = 0; i < MAX_NUM_SEGMENTS; i++, segments++) {
if (!segments->isActive()) {
maxSegmentId = i - 1;
break;
}
segments->setOption(SEG_OPTION_ON, 1, i);
}
colorUpdated(CALL_MODE_DIRECT_CHANGE);
DEBUG_PRINTLN(F("Animated Staircase disabled."));
}
enabled = enable;
}
public:
void setup() {
// standardize invalid pin numbers to -1
if (topPIRorTriggerPin < 0) topPIRorTriggerPin = -1;
if (topEchoPin < 0) topEchoPin = -1;
if (bottomPIRorTriggerPin < 0) bottomPIRorTriggerPin = -1;
if (bottomEchoPin < 0) bottomEchoPin = -1;
// allocate pins
PinManagerPinType pins[4] = {
{ topPIRorTriggerPin, useUSSensorTop },
{ topEchoPin, false },
{ bottomPIRorTriggerPin, useUSSensorBottom },
{ bottomEchoPin, false },
};
// NOTE: this *WILL* return TRUE if all the pins are set to -1.
// this is *BY DESIGN*.
if (!pinManager.allocateMultiplePins(pins, 4, PinOwner::UM_AnimatedStaircase)) {
topPIRorTriggerPin = -1;
topEchoPin = -1;
bottomPIRorTriggerPin = -1;
bottomEchoPin = -1;
enabled = false;
}
enable(enabled);
initDone = true;
}
void loop() {
if (!enabled || strip.isUpdating()) return;
checkSensors();
autoPowerOff();
updateSwipe();
}
uint16_t getId() { return USERMOD_ID_ANIMATED_STAIRCASE; }
/**
* handling of MQTT message
* topic only contains stripped topic (part after /wled/MAC)
* topic should look like: /swipe with amessage of [up|down]
*/
bool onMqttMessage(char* topic, char* payload) {
if (strlen(topic) == 6 && strncmp_P(topic, PSTR("/swipe"), 6) == 0) {
String action = payload;
if (action == "up") {
bottomSensorWrite = true;
return true;
} else if (action == "down") {
topSensorWrite = true;
return true;
} else if (action == "on") {
enable(true);
return true;
} else if (action == "off") {
enable(false);
return true;
}
}
return false;
}
/**
* subscribe to MQTT topic for controlling usermod
*/
void onMqttConnect(bool sessionPresent) {
//(re)subscribe to required topics
char subuf[64];
if (mqttDeviceTopic[0] != 0) {
strcpy(subuf, mqttDeviceTopic);
strcat_P(subuf, PSTR("/swipe"));
mqtt->subscribe(subuf, 0);
}
}
void addToJsonState(JsonObject& root) {
JsonObject staircase = root[FPSTR(_name)];
if (staircase.isNull()) {
staircase = root.createNestedObject(FPSTR(_name));
}
writeSensorsToJson(staircase);
DEBUG_PRINTLN(F("Staircase sensor state exposed in API."));
}
/*
* Reads configuration settings from the json API.
* See void addToJsonState(JsonObject& root)
*/
void readFromJsonState(JsonObject& root) {
if (!initDone) return; // prevent crash on boot applyPreset()
JsonObject staircase = root[FPSTR(_name)];
if (!staircase.isNull()) {
if (staircase[FPSTR(_enabled)].is<bool>()) {
enabled = staircase[FPSTR(_enabled)].as<bool>();
} else {
String str = staircase[FPSTR(_enabled)]; // checkbox -> off or on
enabled = (bool)(str!="off"); // off is guaranteed to be present
}
readSensorsFromJson(staircase);
DEBUG_PRINTLN(F("Staircase sensor state read from API."));
}
}
/*
* Writes the configuration to internal flash memory.
*/
void addToConfig(JsonObject& root) {
JsonObject staircase = root[FPSTR(_name)];
if (staircase.isNull()) {
staircase = root.createNestedObject(FPSTR(_name));
}
staircase[FPSTR(_enabled)] = enabled;
staircase[FPSTR(_segmentDelay)] = segment_delay_ms;
staircase[FPSTR(_onTime)] = on_time_ms / 1000;
staircase[FPSTR(_useTopUltrasoundSensor)] = useUSSensorTop;
staircase[FPSTR(_topPIRorTrigger_pin)] = topPIRorTriggerPin;
staircase[FPSTR(_topEcho_pin)] = useUSSensorTop ? topEchoPin : -1;
staircase[FPSTR(_useBottomUltrasoundSensor)] = useUSSensorBottom;
staircase[FPSTR(_bottomPIRorTrigger_pin)] = bottomPIRorTriggerPin;
staircase[FPSTR(_bottomEcho_pin)] = useUSSensorBottom ? bottomEchoPin : -1;
staircase[FPSTR(_topEchoCm)] = topMaxDist;
staircase[FPSTR(_bottomEchoCm)] = bottomMaxDist;
DEBUG_PRINTLN(F("Staircase config saved."));
}
/*
* Reads the configuration to internal flash memory before setup() is called.
*
* The function should return true if configuration was successfully loaded or false if there was no configuration.
*/
bool readFromConfig(JsonObject& root) {
bool oldUseUSSensorTop = useUSSensorTop;
bool oldUseUSSensorBottom = useUSSensorBottom;
int8_t oldTopAPin = topPIRorTriggerPin;
int8_t oldTopBPin = topEchoPin;
int8_t oldBottomAPin = bottomPIRorTriggerPin;
int8_t oldBottomBPin = bottomEchoPin;
JsonObject top = root[FPSTR(_name)];
if (top.isNull()) {
DEBUG_PRINT(FPSTR(_name));
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false;
}
enabled = top[FPSTR(_enabled)] | enabled;
segment_delay_ms = top[FPSTR(_segmentDelay)] | segment_delay_ms;
segment_delay_ms = (unsigned long) min((unsigned long)10000,max((unsigned long)10,(unsigned long)segment_delay_ms)); // max delay 10s
on_time_ms = top[FPSTR(_onTime)] | on_time_ms/1000;
on_time_ms = min(900,max(10,(int)on_time_ms)) * 1000; // min 10s, max 15min
useUSSensorTop = top[FPSTR(_useTopUltrasoundSensor)] | useUSSensorTop;
topPIRorTriggerPin = top[FPSTR(_topPIRorTrigger_pin)] | topPIRorTriggerPin;
topEchoPin = top[FPSTR(_topEcho_pin)] | topEchoPin;
useUSSensorBottom = top[FPSTR(_useBottomUltrasoundSensor)] | useUSSensorBottom;
bottomPIRorTriggerPin = top[FPSTR(_bottomPIRorTrigger_pin)] | bottomPIRorTriggerPin;
bottomEchoPin = top[FPSTR(_bottomEcho_pin)] | bottomEchoPin;
topMaxDist = top[FPSTR(_topEchoCm)] | topMaxDist;
topMaxDist = min(150,max(30,(int)topMaxDist)); // max distnace ~1.5m (a lag of 9ms may be expected)
bottomMaxDist = top[FPSTR(_bottomEchoCm)] | bottomMaxDist;
bottomMaxDist = min(150,max(30,(int)bottomMaxDist)); // max distance ~1.5m (a lag of 9ms may be expected)
DEBUG_PRINT(FPSTR(_name));
if (!initDone) {
// first run: reading from cfg.json
DEBUG_PRINTLN(F(" config loaded."));
} else {
// changing parameters from settings page
DEBUG_PRINTLN(F(" config (re)loaded."));
bool changed = false;
if ((oldUseUSSensorTop != useUSSensorTop) ||
(oldUseUSSensorBottom != useUSSensorBottom) ||
(oldTopAPin != topPIRorTriggerPin) ||
(oldTopBPin != topEchoPin) ||
(oldBottomAPin != bottomPIRorTriggerPin) ||
(oldBottomBPin != bottomEchoPin)) {
changed = true;
pinManager.deallocatePin(oldTopAPin, PinOwner::UM_AnimatedStaircase);
pinManager.deallocatePin(oldTopBPin, PinOwner::UM_AnimatedStaircase);
pinManager.deallocatePin(oldBottomAPin, PinOwner::UM_AnimatedStaircase);
pinManager.deallocatePin(oldBottomBPin, PinOwner::UM_AnimatedStaircase);
}
if (changed) setup();
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return true;
}
/*
* Shows the delay between steps and power-off time in the "info"
* tab of the web-UI.
*/
void addToJsonInfo(JsonObject& root) {
JsonObject staircase = root["u"];
if (staircase.isNull()) {
staircase = root.createNestedObject("u");
}
JsonArray usermodEnabled = staircase.createNestedArray(F("Staircase")); // name
String btn = F("<button class=\"btn infobtn\" onclick=\"requestJson({staircase:{enabled:");
if (enabled) {
btn += F("false}});\">");
btn += F("enabled");
} else {
btn += F("true}});\">");
btn += F("disabled");
}
btn += F("</button>");
usermodEnabled.add(btn); // value
}
};
// strings to reduce flash memory usage (used more than twice)
const char Animated_Staircase::_name[] PROGMEM = "staircase";
const char Animated_Staircase::_enabled[] PROGMEM = "enabled";
const char Animated_Staircase::_segmentDelay[] PROGMEM = "segment-delay-ms";
const char Animated_Staircase::_onTime[] PROGMEM = "on-time-s";
const char Animated_Staircase::_useTopUltrasoundSensor[] PROGMEM = "useTopUltrasoundSensor";
const char Animated_Staircase::_topPIRorTrigger_pin[] PROGMEM = "topPIRorTrigger_pin";
const char Animated_Staircase::_topEcho_pin[] PROGMEM = "topEcho_pin";
const char Animated_Staircase::_useBottomUltrasoundSensor[] PROGMEM = "useBottomUltrasoundSensor";
const char Animated_Staircase::_bottomPIRorTrigger_pin[] PROGMEM = "bottomPIRorTrigger_pin";
const char Animated_Staircase::_bottomEcho_pin[] PROGMEM = "bottomEcho_pin";
const char Animated_Staircase::_topEchoCm[] PROGMEM = "top-dist-cm";
const char Animated_Staircase::_bottomEchoCm[] PROGMEM = "bottom-dist-cm";

View File

@@ -1,21 +0,0 @@
/*
* Animated_Staircase compiletime confguration.
*
* Please see README.md on how to change this file.
*/
// Please change the pin numbering below to match your board.
#define TOP_PIR_PIN D5
#define BOTTOM_PIR_PIN D6
// Or uncumment and a pir and use an ultrasound HC-SR04 sensor,
// see README.md for details
#ifndef TOP_PIR_PIN
#define TOP_TRIGGER_PIN D2
#define TOP_ECHO_PIN D3
#endif
#ifndef BOTTOM_PIR_PIN
#define BOTTOM_TRIGGER_PIN D4
#define BOTTOM_ECHO_PIN D5
#endif

View File

@@ -20,44 +20,10 @@ Edit `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 `Animated_Staircase_config.h`:
1. Open `usermods/Animated_Staircase/Animated_Staircase_config.h`
2. To use PIR sensors, change these lines to match your setup:
Using D7 and D6 pin notation as used on several boards:
```cpp
#define TOP_PIR_PIN D7
#define BOTTOM_PIR_PIN D6
```
Or using GPIO numbering for pins 25 and 26:
```cpp
#define TOP_PIR_PIN 26
#define BOTTOM_PIR_PIN 25
```
To use Ultrasonic HC-SR04 sensors instead of (one of the) PIR sensors,
uncomment one of the PIR sensor lines and adjust the pin numbers for the
connected Ultrasonic sensor. In the example below we use an Ultrasonic
sensor at the bottom of the stairs:
```cpp
#define TOP_PIR_PIN 32
//#define BOTTOM_PIR_PIN D6 /* This PIR sensor is disabled */
#ifndef TOP_PIR_PIN
#define TOP_SIGNAL_PIN D2
#define TOP_ECHO_PIN D3
#endif
#ifndef BOTTOM_PIR_PIN /* If the bottom PIR is disabled, */
#define BOTTOM_SIGNAL_PIN 25 /* This Ultrasonic sensor is used */
#define BOTTOM_ECHO_PIN 26
#endif
```
After these modifications, compile and upload your WLED binary to your board
and check the WLED info page to see if this usermod is enabled.
You can configure usermod using Usermods settings page.
Please enter GPIO pins for PIR sensors or ultrasonic sensor (trigger and echo).
If you use PIR sensor enter -1 for echo pin.
Maximum distance for ultrasonic sensor can be configured as a time needed for echo (see below).
## Hardware installation
1. Stick the LED strip under each step of the stairs.
@@ -90,11 +56,8 @@ or remove them and put everything on one line.
| Setting | Description | Default |
|------------------|---------------------------------------------------------------|---------|
| enabled | Enable or disable the usermod | true |
| segment-delay-ms | Delay (milliseconds) between switching on/off each step | 150 |
| on-time-s | Time (seconds) the stairs stay lit after last detection | 5 |
| bottom-echo-us | Detection range of ultrasonic sensor | 1749 |
| bottomsensor | Manually trigger a down to up animation via API | false |
| topsensor | Manually trigger an up to down 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 |
To read the current settings, open a browser to `http://xxx.xxx.xxx.xxx/json/state` (use your WLED
@@ -106,10 +69,8 @@ The staircase settings and sensor states are inside the WLED status element:
"state": {
"staircase": {
"enabled": true,
"segment-delay-ms": 150,
"on-time-s": 5,
"bottomsensor": false,
"topsensor": false
"bottom-sensor": false,
"tops-ensor": false
},
}
```
@@ -128,58 +89,16 @@ curl -X POST -H "Content-Type: application/json" \
To enable the usermod again, use `"enabled":true`.
### Changing animation parameters
To change the delay between the steps to (for example) 100 milliseconds and the on-time to
10 seconds:
Alternatively you can use _Usermod_ Settings page where you can change other parameters as well.
```bash
curl -X POST -H "Content-Type: application/json" \
-d '{"staircase":{"segment-delay-ms":100,"on-time-s":10}}' \
xxx.xxx.xxx.xxx/json/state
```
### Changing animation parameters and detection range of the ultrasonic HC-SR04 sensor
Using _Usermod_ Settings page you can define different usermod parameters, includng sensor pins, delay between segment activation and so on.
### Changing detection range of the ultrasonic HC-SR04 sensor
When an ultrasonic sensor is enabled in `Animated_Staircase_config.h`, you'll see a
`bottom-echo-us` setting appear in the json api:
```json
{
"state": {
"staircase": {
"enabled": true,
"segment-delay-ms": 150,
"on-time-s": 5,
"bottom-echo-us": 1749
},
}
```
If the HC-SR04 sensor detects an echo within 1749 microseconds (corresponding to ~30 cm
detection range from the sensor), it will trigger switching on the staircase. This setting
can be changed through the API with an HTTP POST:
```bash
curl -X POST -H "Content-Type: application/json" \
-d '{"staircase":{"bottom-echo-us":1166}}' \
xxx.xxx.xxx.xxx/json/state
```
Calculating the detection range can be performed as follows: The speed of sound is 343m/s at 20
degrees Centigrade. Since the sound has to travel back and forth, the detection range for the
sensor in cm is (0.0343 * maxTimeUs) / 2. To get you started, please find delays and distances below:
| Distance | Detection time |
|---------:|----------------:|
| 5 cm | 292 uS |
| 10 cm | 583 uS |
| 20 cm | 1166 uS |
| 30 cm | 1749 uS |
| 50 cm | 2915 uS |
| 100 cm | 5831 uS |
When an ultrasonic sensor is enabled you can enter maximum detection distance in centimeters separately for top and bottom sensors.
**Please note:** that using an HC-SR04 sensor, particularly when detecting echos at longer
distances creates delays in the WLED software, and _might_ introduce timing hickups in your animations or
a less responsive web interface. It is therefore advised to keep the detection time as short as possible.
a less responsive web interface. It is therefore advised to keep the detection distance as short as possible.
### Animation triggering through the API
Instead of stairs activation by one of the sensors, you can also trigger the animation through
@@ -187,7 +106,7 @@ the API. To simulate triggering the bottom sensor, use:
```bash
curl -X POST -H "Content-Type: application/json" \
-d '{"staircase":{"bottomsensor":true}}' \
-d '{"staircase":{"bottom-sensor":true}}' \
xxx.xxx.xxx.xxx/json/state
```
@@ -195,9 +114,18 @@ Likewise, to trigger the top sensor, use:
```bash
curl -X POST -H "Content-Type: application/json" \
-d '{"staircase":{"topsensor":true}}' \
-d '{"staircase":{"top-sensor":true}}' \
xxx.xxx.xxx.xxx/json/state
```
**MQTT**
You can publish a message with either `up` or `down` on topic `/swipe` to trigger animation.
You can also use `on` or `off` for enabling or disabling usermod.
Have fun with this usermod.<br/>
www.rolfje.com
Modifications @blazoncek
## Change log
2021-04
* Adaptation for runtime configuration.

View File

@@ -24,7 +24,7 @@ void RGBNET_readValues() {
int channel = UDP.read();
//channel data is not used we only supports one channel
int len = UDP.read(RGBNET_packet, ledCount*3);
int len = UDP.read(RGBNET_packet, strip.getLengthTotal()*3);
if(len==0){
return;
}
@@ -50,7 +50,7 @@ void handleConfig(AsyncWebServerRequest *request)
\"channels\": [\
{\
\"channel\": 1,\
\"leds\": " + ledCount + "\
\"leds\": " + strip.getLengthTotal() + "\
},\
{\
\"channel\": 2,\

View File

@@ -0,0 +1,16 @@
; Options
; -------
; USERMOD_BH1750 - define this to have this user mod included wled00\usermods_list.cpp
; USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL - the max number of milliseconds between measurements, defaults to 10000ms
; USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL - the min number of milliseconds between measurements, defaults to 500ms
; USERMOD_BH1750_FIRST_MEASUREMENT_AT - the number of milliseconds after boot to take first measurement, defaults to 10 seconds
; USERMOD_BH1750_OFFSET_VALUE - the offset value to report on, defaults to 1
;
[env:usermod_BH1750_d1_mini]
extends = env:d1_mini
build_flags =
${common.build_flags_esp8266}
-D USERMOD_BH1750
lib_deps =
${env.lib_deps}
claws/BH1750 @ ^1.2.0

View File

@@ -0,0 +1,24 @@
# BH1750 usermod
This usermod will read from an ambient light sensor like the BH1750 sensor.
The luminance is displayed both in the Info section of the web UI as well as published to the `/luminance` MQTT topic if enabled.
## Installation
Copy the example `platformio_override.ini` to the root directory. This file should be placed in the same directory as `platformio.ini`.
### Define Your Options
* `USERMOD_BH1750` - define this to have this user mod included wled00\usermods_list.cpp
* `USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL` - the max number of milliseconds between measurements, defaults to 10000ms
* `USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL` - the min number of milliseconds between measurements, defaults to 500ms
* `USERMOD_BH1750_FIRST_MEASUREMENT_AT` - the number of milliseconds after boot to take first measurement, defaults to 10 seconds
* `USERMOD_BH1750_OFFSET_VALUE` - the offset value to report on, defaults to 1
All parameters can be configured at runtime using Usermods settings page.
### PlatformIO requirements
If you are using `platformio_override.ini`, you should be able to refresh the task list and see your custom task, for example `env:usermod_BH1750_d1_mini`.
## Change Log

View File

@@ -0,0 +1,177 @@
#pragma once
#include "wled.h"
#include <Wire.h>
#include <BH1750.h>
// the max frequency to check photoresistor, 10 seconds
#ifndef USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL
#define USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL 10000
#endif
// the min frequency to check photoresistor, 500 ms
#ifndef USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL
#define USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL 500
#endif
// how many seconds after boot to take first measurement, 10 seconds
#ifndef USERMOD_BH1750_FIRST_MEASUREMENT_AT
#define USERMOD_BH1750_FIRST_MEASUREMENT_AT 10000
#endif
// only report if differance grater than offset value
#ifndef USERMOD_BH1750_OFFSET_VALUE
#define USERMOD_BH1750_OFFSET_VALUE 1
#endif
class Usermod_BH1750 : public Usermod
{
private:
int8_t offset = USERMOD_BH1750_OFFSET_VALUE;
unsigned long maxReadingInterval = USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL;
unsigned long minReadingInterval = USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL;
unsigned long lastMeasurement = UINT32_MAX - (USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL - USERMOD_BH1750_FIRST_MEASUREMENT_AT);
unsigned long lastSend = UINT32_MAX - (USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL - USERMOD_BH1750_FIRST_MEASUREMENT_AT);
// flag to indicate we have finished the first readLightLevel call
// allows this library to report to the user how long until the first
// measurement
bool getLuminanceComplete = false;
// flag set at startup
bool disabled = false;
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
static const char _enabled[];
static const char _maxReadInterval[];
static const char _minReadInterval[];
static const char _offset[];
BH1750 lightMeter;
float lastLux = -1000;
bool checkBoundSensor(float newValue, float prevValue, float maxDiff)
{
return isnan(prevValue) || newValue <= prevValue - maxDiff || newValue >= prevValue + maxDiff || (newValue == 0.0 && prevValue > 0.0);
}
public:
void setup()
{
Wire.begin();
lightMeter.begin();
}
void loop()
{
if (disabled || strip.isUpdating())
return;
unsigned long now = millis();
// check to see if we are due for taking a measurement
// lastMeasurement will not be updated until the conversion
// is complete the the reading is finished
if (now - lastMeasurement < minReadingInterval)
{
return;
}
bool shouldUpdate = now - lastSend > maxReadingInterval;
float lux = lightMeter.readLightLevel();
lastMeasurement = millis();
getLuminanceComplete = true;
if (shouldUpdate || checkBoundSensor(lux, lastLux, offset))
{
lastLux = lux;
lastSend = millis();
if (WLED_MQTT_CONNECTED)
{
char subuf[45];
strcpy(subuf, mqttDeviceTopic);
strcat_P(subuf, PSTR("/luminance"));
mqtt->publish(subuf, 0, true, String(lux).c_str());
}
else
{
DEBUG_PRINTLN("Missing MQTT connection. Not publishing data");
}
}
}
void addToJsonInfo(JsonObject &root)
{
JsonObject user = root[F("u")];
if (user.isNull())
user = root.createNestedObject(F("u"));
JsonArray lux_json = user.createNestedArray(F("Luminance"));
if (!getLuminanceComplete)
{
// if we haven't read the sensor yet, let the user know
// that we are still waiting for the first measurement
lux_json.add((USERMOD_BH1750_FIRST_MEASUREMENT_AT - millis()) / 1000);
lux_json.add(F(" sec until read"));
return;
}
lux_json.add(lastLux);
lux_json.add(F(" lx"));
}
uint16_t getId()
{
return USERMOD_ID_BH1750;
}
/**
* addToConfig() (called from set.cpp) stores persistent properties to cfg.json
*/
void addToConfig(JsonObject &root)
{
// we add JSON object.
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
top[FPSTR(_enabled)] = !disabled;
top[FPSTR(_maxReadInterval)] = maxReadingInterval;
top[FPSTR(_minReadInterval)] = minReadingInterval;
top[FPSTR(_offset)] = offset;
DEBUG_PRINTLN(F("Photoresistor config saved."));
}
/**
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
*/
bool readFromConfig(JsonObject &root)
{
// we look for JSON object.
JsonObject top = root[FPSTR(_name)];
if (top.isNull())
{
DEBUG_PRINT(FPSTR(_name));
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false;
}
disabled = !(top[FPSTR(_enabled)] | !disabled);
maxReadingInterval = (top[FPSTR(_maxReadInterval)] | maxReadingInterval); // ms
minReadingInterval = (top[FPSTR(_minReadInterval)] | minReadingInterval); // ms
offset = top[FPSTR(_offset)] | offset;
DEBUG_PRINT(FPSTR(_name));
DEBUG_PRINTLN(F(" config (re)loaded."));
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return true;
}
};
// strings to reduce flash memory usage (used more than twice)
const char Usermod_BH1750::_name[] PROGMEM = "BH1750";
const char Usermod_BH1750::_enabled[] PROGMEM = "enabled";
const char Usermod_BH1750::_maxReadInterval[] PROGMEM = "max-read-interval-ms";
const char Usermod_BH1750::_minReadInterval[] PROGMEM = "min-read-interval-ms";
const char Usermod_BH1750::_offset[] PROGMEM = "offset-lx";

View File

@@ -0,0 +1,14 @@
#include "wled.h"
/*
* Register your v2 usermods here!
*/
#ifdef USERMOD_BH1750
#include "../usermods/BH1750_v2/usermod_BH1750.h"
#endif
void registerUsermods()
{
#ifdef USERMOD_BH1750
usermods.add(new Usermod_BH1750());
#endif
}

View File

@@ -1,40 +1,90 @@
Hello! I have written a v2 usermod for the BME280/BMP280 sensor based on the [existing v1 usermod](https://github.com/Aircoookie/WLED/blob/master/usermods/Wemos_D1_mini%2BWemos32_mini_shield/usermod_bme280.cpp). It is not just a refactor, there are many changes which I made to fit my use case, and I hope they will fit the use cases of others as well! Most notably, this usermod is *just* for the BME280 and does not control a display like in the v1 usermod designed for the WeMos shield.
# Usermod BME280
This Usermod is designed to read a `BME280` or `BMP280` sensor and output the following:
- Temperature
- Humidity (`BME280` only)
- Pressure
- Heat Index (`BME280` only)
- Dew Point (`BME280` only)
- Requires libraries `BME280@~3.0.0` (by [finitespace](https://github.com/finitespace/BME280)) and `Wire`. Please add these under `lib_deps` in your `platform.ini` (or `platform_override.ini`).
- Data is published over MQTT so make sure you've enabled the MQTT sync interface.
Configuration is all completed via the Usermod menu. There are no settings to set in code! The following settings can be configured in the Usermod Menu:
- Temperature Decimals (number of decimal places to output)
- Humidity Decimals
- Pressure Decimals
- Temperature Interval (how many seconds between reads of temperature and humidity)
- Pressure Interval
- Publish Always (turn off to only publish changes, on to publish whether or not value changed)
- Use Celsius (turn off to use Farenheit)
- Home Assistant Discovery (turn on to sent MQTT Discovery entries for Home Assistant)
- SCL/SDA GPIO Pins
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 listening on the serial TX pin of your board will get confused by log messages!
To enable, compile with `USERMOD_BME280` defined (i.e. `platformio_override.ini`)
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.
- `getTemperatureC()`
- `getTemperatureF()`
- `getHumidity()`
- `getPressure()`
- `getDewPointC()`
- `getDewPointF()`
- `getHeatIndexC()`
- `getHeatIndexF()`
# Complilation
To enable, compile with `USERMOD_BME280` defined (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
```
or define `USERMOD_BME280` in `my_config.h`
```c++
#define USERMOD_BME280
lib_deps =
${esp8266.lib_deps}
BME280@~3.0.0
Wire
```
Changes include:
- Adjustable measure intervals
- Temperature and pressure have separate intervals due to pressure not frequently changing at any constant altitude
- Adjustment of number of decimal places in published sensor values
- Separate adjustment for temperature, humidity and pressure values
- Values are rounded to the specified number of decimal places
- Pressure measured in units of hPa instead of Pa
- Calculation of heat index (apparent temperature) and dew point
- These, along with humidity measurements, are disabled if the sensor is a BMP280
- 16x oversampling of sensor during measurement
- Values are only published if they are different from the previous value
- Values are published on startup (continually until the MQTT broker acknowledges a successful publication)
Adjustments are made through preprocessor definitions at the start of the class definition.
MQTT topics are as follows:
# MQTT
MQTT topics are as follows (`<deviceTopic>` is set in MQTT section of Sync Setup menu):
Measurement type | MQTT topic
--- | ---
Temperature | `<deviceTopic>/temperature`
Humidity | `<deviceTopic>/humidity`
Pressure | `<deviceTopic>/pressure`
Heat index | `<deviceTopic>/heat_index`
Dew point | `<deviceTopic>/dew_point`
Dew point | `<deviceTopic>/dew_point`
If you are using Home Assistant, and `Home Assistant Discovery` is turned on, Home Assistant should automatically detect a new device, provided you have the MQTT integration installed. The device is seperate from the main WLED device and will contain sensors for Pressure, Humidity, Temperature, Dew Point and Heat Index.
# Revision History
Jul 2022
- Added Home Assistant Discovery
- Added API interface to output data
- Removed compile-time variables
- Added usermod menu interface
- Added value outputs to info screen
- Updated `readme.md`
- Registered usermod
- Implemented PinManager for usermod
- Implemented reallocation of pins without reboot
Apr 2021
- Added `Publish Always` option
Dec 2020
- Ported to V2 Usermod format
- Customisable `measure intervals`
- Customisable number of `decimal places` in published sensor values
- Pressure measured in units of hPa instead of Pa
- Calculation of heat index (apparent temperature) and dew point
- `16x oversampling` of sensor during measurement
- Values only published if they are different from the previous value

View File

@@ -1,3 +1,6 @@
// force the compiler to show a warning to confirm that this file is included
#warning **** Included USERMOD_BME280 version 2.0 ****
#pragma once
#include "wled.h"
@@ -9,39 +12,30 @@
class UsermodBME280 : public Usermod
{
private:
// User-defined configuration
#define Celsius // Show temperature mesaurement in Celcius. Comment out for Fahrenheit
#define TemperatureDecimals 1 // Number of decimal places in published temperaure values
#define HumidityDecimals 0 // Number of decimal places in published humidity values
#define PressureDecimals 2 // Number of decimal places in published pressure values
#define TemperatureInterval 5 // Interval to measure temperature (and humidity, dew point if available) in seconds
#define PressureInterval 300 // Interval to measure pressure in seconds
// NOTE: Do not implement any compile-time variables, anything the user needs to configure
// should be configurable from the Usermod menu using the methods below
// key settings set via usermod menu
unsigned long TemperatureDecimals = 0; // Number of decimal places in published temperaure values
unsigned long HumidityDecimals = 0; // Number of decimal places in published humidity values
unsigned long PressureDecimals = 0; // Number of decimal places in published pressure values
unsigned long TemperatureInterval = 5; // Interval to measure temperature (and humidity, dew point if available) in seconds
unsigned long PressureInterval = 300; // Interval to measure pressure in seconds
bool PublishAlways = false; // Publish values even when they have not changed
bool UseCelsius = true; // Use Celsius for Reporting
bool HomeAssistantDiscovery = false; // Publish Home Assistant Device Information
// Sanity checks
#if !defined(TemperatureDecimals) || TemperatureDecimals < 0
#define TemperatureDecimals 0
#endif
#if !defined(HumidityDecimals) || HumidityDecimals < 0
#define HumidityDecimals 0
#endif
#if !defined(PressureDecimals) || PressureDecimals < 0
#define PressureDecimals 0
#endif
#if !defined(TemperatureInterval) || TemperatureInterval < 0
#define TemperatureInterval 1
#endif
#if !defined(PressureInterval) || PressureInterval < 0
#define PressureInterval TemperatureInterval
#endif
#ifdef ARDUINO_ARCH_ESP32 // ESP32 boards
uint8_t SCL_PIN = 22;
uint8_t SDA_PIN = 21;
#else // ESP8266 boards
uint8_t SCL_PIN = 5;
uint8_t SDA_PIN = 4;
//uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8
#endif
// set the default pins based on the architecture, these get overridden by Usermod menu settings
#ifdef ARDUINO_ARCH_ESP32 // ESP32 boards
#define HW_PIN_SCL 22
#define HW_PIN_SDA 21
#else // ESP8266 boards
#define HW_PIN_SCL 5
#define HW_PIN_SDA 4
//uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8
#endif
int8_t ioPin[2] = {HW_PIN_SCL, HW_PIN_SDA}; // I2C pins: SCL, SDA...defaults to Arch hardware pins but overridden at setup()
bool initDone = false;
// BME280 sensor settings
BME280I2C::Settings settings{
@@ -58,7 +52,7 @@ private:
BME280I2C bme{settings};
uint8_t SensorType;
uint8_t sensorType;
// Measurement timers
long timer;
@@ -66,11 +60,12 @@ private:
long lastPressureMeasure = 0;
// Current sensor values
float SensorTemperature;
float SensorHumidity;
float SensorHeatIndex;
float SensorDewPoint;
float SensorPressure;
float sensorTemperature;
float sensorHumidity;
float sensorHeatIndex;
float sensorDewPoint;
float sensorPressure;
String tempScale;
// Track previous sensor values
float lastTemperature;
float lastHumidity;
@@ -78,68 +73,148 @@ private:
float lastDewPoint;
float lastPressure;
// MQTT topic strings for publishing Home Assistant discovery topics
bool mqttInitialized = false;
String mqttTemperatureTopic = "";
String mqttHumidityTopic = "";
String mqttPressureTopic = "";
String mqttHeatIndexTopic = "";
String mqttDewPointTopic = "";
// Store packet IDs of MQTT publications
uint16_t mqttTemperaturePub = 0;
uint16_t mqttPressurePub = 0;
// Read the BME280/BMP280 Sensor (which one runs depends on whether Celsius or Farenheit being set in Usermod Menu)
void UpdateBME280Data(int SensorType)
{
float _temperature, _humidity, _pressure;
#ifdef Celsius
if (UseCelsius) {
BME280::TempUnit tempUnit(BME280::TempUnit_Celsius);
EnvironmentCalculations::TempUnit envTempUnit(EnvironmentCalculations::TempUnit_Celsius);
#else
BME280::PresUnit presUnit(BME280::PresUnit_hPa);
bme.read(_pressure, _temperature, _humidity, tempUnit, presUnit);
sensorTemperature = _temperature;
sensorHumidity = _humidity;
sensorPressure = _pressure;
tempScale = "°C";
if (sensorType == 1)
{
sensorHeatIndex = EnvironmentCalculations::HeatIndex(_temperature, _humidity, envTempUnit);
sensorDewPoint = EnvironmentCalculations::DewPoint(_temperature, _humidity, envTempUnit);
}
} else {
BME280::TempUnit tempUnit(BME280::TempUnit_Fahrenheit);
EnvironmentCalculations::TempUnit envTempUnit(EnvironmentCalculations::TempUnit_Fahrenheit);
#endif
BME280::PresUnit presUnit(BME280::PresUnit_hPa);
BME280::PresUnit presUnit(BME280::PresUnit_hPa);
bme.read(_pressure, _temperature, _humidity, tempUnit, presUnit);
bme.read(_pressure, _temperature, _humidity, tempUnit, presUnit);
SensorTemperature = _temperature;
SensorHumidity = _humidity;
SensorPressure = _pressure;
if (SensorType == 1)
{
SensorHeatIndex = EnvironmentCalculations::HeatIndex(_temperature, _humidity, envTempUnit);
SensorDewPoint = EnvironmentCalculations::DewPoint(_temperature, _humidity, envTempUnit);
sensorTemperature = _temperature;
sensorHumidity = _humidity;
sensorPressure = _pressure;
tempScale = "°F";
if (sensorType == 1)
{
sensorHeatIndex = EnvironmentCalculations::HeatIndex(_temperature, _humidity, envTempUnit);
sensorDewPoint = EnvironmentCalculations::DewPoint(_temperature, _humidity, envTempUnit);
}
}
}
// Procedure to define all MQTT discovery Topics
void _mqttInitialize()
{
mqttTemperatureTopic = String(mqttDeviceTopic) + F("/temperature");
mqttPressureTopic = String(mqttDeviceTopic) + F("/pressure");
mqttHumidityTopic = String(mqttDeviceTopic) + F("/humidity");
mqttHeatIndexTopic = String(mqttDeviceTopic) + F("/heat_index");
mqttDewPointTopic = String(mqttDeviceTopic) + F("/dew_point");
if (HomeAssistantDiscovery) {
_createMqttSensor(F("Temperature"), mqttTemperatureTopic, F("temperature"), tempScale);
_createMqttSensor(F("Pressure"), mqttPressureTopic, F("pressure"), F("hPa"));
_createMqttSensor(F("Humidity"), mqttHumidityTopic, F("humidity"), F("%"));
_createMqttSensor(F("HeatIndex"), mqttHeatIndexTopic, F("temperature"), tempScale);
_createMqttSensor(F("DewPoint"), mqttDewPointTopic, F("temperature"), tempScale);
}
}
// 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");
device[F("model")] = F("FOSS");
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()
{
Wire.begin(SDA_PIN, SCL_PIN);
bool HW_Pins_Used = (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA); // note whether architecture-based hardware SCL/SDA pins used
PinOwner po = PinOwner::UM_BME280; // defaults to being pinowner for SCL/SDA pins
PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true } }; // allocate pins
if (HW_Pins_Used) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
if (!pinManager.allocateMultiplePins(pins, 2, po)) { sensorType=0; return; }
Wire.begin(ioPin[1], ioPin[0]);
if (!bme.begin())
{
SensorType = 0;
Serial.println("Could not find BME280I2C sensor!");
sensorType = 0;
DEBUG_PRINTLN(F("Could not find BME280I2C sensor!"));
}
else
{
switch (bme.chipModel())
{
case BME280::ChipModel_BME280:
SensorType = 1;
Serial.println("Found BME280 sensor! Success.");
sensorType = 1;
DEBUG_PRINTLN(F("Found BME280 sensor! Success."));
break;
case BME280::ChipModel_BMP280:
SensorType = 2;
Serial.println("Found BMP280 sensor! No Humidity available.");
sensorType = 2;
DEBUG_PRINTLN(F("Found BMP280 sensor! No Humidity available."));
break;
default:
SensorType = 0;
Serial.println("Found UNKNOWN sensor! Error!");
sensorType = 0;
DEBUG_PRINTLN(F("Found UNKNOWN sensor! Error!"));
}
}
initDone=true;
}
void loop()
{
// BME280 sensor MQTT publishing
// Check if sensor present and MQTT Connected, otherwise it will crash the MCU
if (SensorType != 0 && mqtt != nullptr)
if (sensorType != 0 && WLED_MQTT_CONNECTED)
{
// Timer to fetch new temperature, humidity and pressure data at intervals
timer = millis();
@@ -148,48 +223,54 @@ public:
{
lastTemperatureMeasure = timer;
UpdateBME280Data(SensorType);
UpdateBME280Data(sensorType);
float Temperature = roundf(SensorTemperature * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals);
float Humidity, HeatIndex, DewPoint;
float temperature = roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
float humidity, heatIndex, dewPoint;
if (WLED_MQTT_CONNECTED && !mqttInitialized)
{
_mqttInitialize();
mqttInitialized = true;
}
// If temperature has changed since last measure, create string populated with device topic
// from the UI and values read from sensor, then publish to broker
if (Temperature != lastTemperature)
if (temperature != lastTemperature || PublishAlways)
{
String topic = String(mqttDeviceTopic) + "/temperature";
mqttTemperaturePub = mqtt->publish(topic.c_str(), 0, false, String(Temperature, TemperatureDecimals).c_str());
mqttTemperaturePub = mqtt->publish(topic.c_str(), 0, false, String(temperature, TemperatureDecimals).c_str());
}
lastTemperature = Temperature; // Update last sensor temperature for next loop
lastTemperature = temperature; // Update last sensor temperature for next loop
if (SensorType == 1) // Only if sensor is a BME280
if (sensorType == 1) // Only if sensor is a BME280
{
Humidity = roundf(SensorHumidity * pow(10, HumidityDecimals)) / pow(10, HumidityDecimals);
HeatIndex = roundf(SensorHeatIndex * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals);
DewPoint = roundf(SensorDewPoint * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals);
humidity = roundf(sensorHumidity * powf(10, HumidityDecimals)) / powf(10, HumidityDecimals);
heatIndex = roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
dewPoint = roundf(sensorDewPoint * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
if (Humidity != lastHumidity)
if (humidity != lastHumidity || PublishAlways)
{
String topic = String(mqttDeviceTopic) + "/humidity";
mqtt->publish(topic.c_str(), 0, false, String(Humidity, HumidityDecimals).c_str());
String topic = String(mqttDeviceTopic) + F("/humidity");
mqtt->publish(topic.c_str(), 0, false, String(humidity, HumidityDecimals).c_str());
}
if (HeatIndex != lastHeatIndex)
if (heatIndex != lastHeatIndex || PublishAlways)
{
String topic = String(mqttDeviceTopic) + "/heat_index";
mqtt->publish(topic.c_str(), 0, false, String(HeatIndex, TemperatureDecimals).c_str());
String topic = String(mqttDeviceTopic) + F("/heat_index");
mqtt->publish(topic.c_str(), 0, false, String(heatIndex, TemperatureDecimals).c_str());
}
if (DewPoint != lastDewPoint)
if (dewPoint != lastDewPoint || PublishAlways)
{
String topic = String(mqttDeviceTopic) + "/dew_point";
mqtt->publish(topic.c_str(), 0, false, String(DewPoint, TemperatureDecimals).c_str());
String topic = String(mqttDeviceTopic) + F("/dew_point");
mqtt->publish(topic.c_str(), 0, false, String(dewPoint, TemperatureDecimals).c_str());
}
lastHumidity = Humidity;
lastHeatIndex = HeatIndex;
lastDewPoint = DewPoint;
lastHumidity = humidity;
lastHeatIndex = heatIndex;
lastDewPoint = dewPoint;
}
}
@@ -197,16 +278,185 @@ public:
{
lastPressureMeasure = timer;
float Pressure = roundf(SensorPressure * pow(10, PressureDecimals)) / pow(10, PressureDecimals);
float pressure = roundf(sensorPressure * powf(10, PressureDecimals)) / powf(10, PressureDecimals);
if (Pressure != lastPressure)
if (pressure != lastPressure || PublishAlways)
{
String topic = String(mqttDeviceTopic) + "/pressure";
mqttPressurePub = mqtt->publish(topic.c_str(), 0, true, String(Pressure, PressureDecimals).c_str());
String topic = String(mqttDeviceTopic) + F("/pressure");
mqttPressurePub = mqtt->publish(topic.c_str(), 0, true, String(pressure, PressureDecimals).c_str());
}
lastPressure = Pressure;
lastPressure = pressure;
}
}
}
/*
* API calls te enable data exchange between WLED modules
*/
inline float getTemperatureC() {
if (UseCelsius) {
return (float)roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
} else {
return (float)roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals) * 1.8f + 32;
}
}
inline float getTemperatureF() {
if (UseCelsius) {
return ((float)roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals) -32) * 0.56f;
} else {
return (float)roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
}
}
inline float getHumidity() {
return (float)roundf(sensorHumidity * powf(10, HumidityDecimals));
}
inline float getPressure() {
return (float)roundf(sensorPressure * powf(10, PressureDecimals));
}
inline float getDewPointC() {
if (UseCelsius) {
return (float)roundf(sensorDewPoint * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
} else {
return (float)roundf(sensorDewPoint * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals) * 1.8f + 32;
}
}
inline float getDewPointF() {
if (UseCelsius) {
return ((float)roundf(sensorDewPoint * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals) -32) * 0.56f;
} else {
return (float)roundf(sensorDewPoint * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
}
}
inline float getHeatIndexC() {
if (UseCelsius) {
return (float)roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
} else {
return (float)roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals) * 1.8f + 32;
}
}inline float getHeatIndexF() {
if (UseCelsius) {
return ((float)roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals) -32) * 0.56f;
} else {
return (float)roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
}
}
// Publish Sensor Information to Info Page
void addToJsonInfo(JsonObject &root)
{
JsonObject user = root[F("u")];
if (user.isNull()) user = root.createNestedObject(F("u"));
if (sensorType==0) //No Sensor
{
// if we sensor not detected, let the user know
JsonArray temperature_json = user.createNestedArray(F("BME/BMP280 Sensor"));
temperature_json.add(F("Not Found"));
}
else if (sensorType==2) //BMP280
{
JsonArray temperature_json = user.createNestedArray(F("Temperature"));
JsonArray pressure_json = user.createNestedArray(F("Pressure"));
temperature_json.add(roundf(sensorTemperature * powf(10, TemperatureDecimals)));
temperature_json.add(tempScale);
pressure_json.add(roundf(sensorPressure * powf(10, PressureDecimals)));
pressure_json.add(F("hPa"));
}
else if (sensorType==1) //BME280
{
JsonArray temperature_json = user.createNestedArray(F("Temperature"));
JsonArray humidity_json = user.createNestedArray(F("Humidity"));
JsonArray pressure_json = user.createNestedArray(F("Pressure"));
JsonArray heatindex_json = user.createNestedArray(F("Heat Index"));
JsonArray dewpoint_json = user.createNestedArray(F("Dew Point"));
temperature_json.add(roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals));
temperature_json.add(tempScale);
humidity_json.add(roundf(sensorHumidity * powf(10, HumidityDecimals)));
humidity_json.add(F("%"));
pressure_json.add(roundf(sensorPressure * powf(10, PressureDecimals)));
pressure_json.add(F("hPa"));
heatindex_json.add(roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals));
heatindex_json.add(tempScale);
dewpoint_json.add(roundf(sensorDewPoint * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals));
dewpoint_json.add(tempScale);
}
return;
}
// Save Usermod Config Settings
void addToConfig(JsonObject& root)
{
JsonObject top = root.createNestedObject(F("BME280/BMP280"));
top[F("TemperatureDecimals")] = TemperatureDecimals;
top[F("HumidityDecimals")] = HumidityDecimals;
top[F("PressureDecimals")] = PressureDecimals;
top[F("TemperatureInterval")] = TemperatureInterval;
top[F("PressureInterval")] = PressureInterval;
top[F("PublishAlways")] = PublishAlways;
top[F("UseCelsius")] = UseCelsius;
top[F("HomeAssistantDiscovery")] = HomeAssistantDiscovery;
JsonArray io_pin = top.createNestedArray(F("pin"));
for (byte i=0; i<2; i++) io_pin.add(ioPin[i]);
top[F("help4Pins")] = F("SCL,SDA"); // help for Settings page
DEBUG_PRINTLN(F("BME280 config saved."));
}
// Read Usermod Config Settings
bool readFromConfig(JsonObject& root)
{
// default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor
// setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed)
int8_t newPin[2]; for (byte i=0; i<2; i++) newPin[i] = ioPin[i]; // prepare to note changed pins
JsonObject top = root[F("BME280/BMP280")];
if (top.isNull()) {
DEBUG_PRINT(F("BME280/BMP280"));
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false;
}
bool configComplete = !top.isNull();
// A 3-argument getJsonValue() assigns the 3rd argument as a default value if the Json value is missing
configComplete &= getJsonValue(top[F("TemperatureDecimals")], TemperatureDecimals, 1);
configComplete &= getJsonValue(top[F("HumidityDecimals")], HumidityDecimals, 0);
configComplete &= getJsonValue(top[F("PressureDecimals")], PressureDecimals, 0);
configComplete &= getJsonValue(top[F("TemperatureInterval")], TemperatureInterval, 30);
configComplete &= getJsonValue(top[F("PressureInterval")], PressureInterval, 30);
configComplete &= getJsonValue(top[F("PublishAlways")], PublishAlways, false);
configComplete &= getJsonValue(top[F("UseCelsius")], UseCelsius, true);
configComplete &= getJsonValue(top[F("HomeAssistantDiscovery")], HomeAssistantDiscovery, false);
for (byte i=0; i<2; i++) configComplete &= getJsonValue(top[F("pin")][i], newPin[i], ioPin[i]);
DEBUG_PRINT(FPSTR(F("BME280/BMP280")));
if (!initDone) {
// first run: reading from cfg.json
for (byte i=0; i<2; i++) ioPin[i] = newPin[i];
DEBUG_PRINTLN(F(" config loaded."));
} else {
DEBUG_PRINTLN(F(" config (re)loaded."));
// changing parameters from settings page
bool pinsChanged = false;
for (byte i=0; i<2; i++) if (ioPin[i] != newPin[i]) { pinsChanged = true; break; } // check if any pins changed
if (pinsChanged) { //if pins changed, deallocate old pins and allocate new ones
PinOwner po = PinOwner::UM_BME280;
if (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
pinManager.deallocateMultiplePins((const uint8_t *)ioPin, 2, po); // deallocate pins
for (byte i=0; i<2; i++) ioPin[i] = newPin[i];
setup();
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[F("pin")].isNull();
}
return configComplete;
}
uint16_t getId() {
return USERMOD_ID_BME280;
}
};

View File

@@ -0,0 +1,8 @@
# Cronixie clock usermod
This usermod supports driving the Cronixie M and L clock kits by Diamex.
## Installation
Compile and upload after adding `-D USERMOD_CRONIXIE` to `build_flags` of your PlatformIO environment.
Make sure the Auto Brightness Limiter is enabled at 420mA (!) and configure 60 WS281x LEDs.

View File

@@ -0,0 +1,301 @@
#pragma once
#include "wled.h"
class UsermodCronixie : public Usermod {
private:
unsigned long lastTime = 0;
char cronixieDisplay[7] = "HHMMSS";
byte _digitOut[6] = {10,10,10,10,10,10};
byte dP[6] = {255, 255, 255, 255, 255, 255};
// set your config variables to their boot default value (this can also be done in readFromConfig() or a constructor if you prefer)
bool backlight = true;
public:
void initCronixie()
{
if (dP[0] == 255) // if dP[0] is 255, cronixie is not yet init'ed
{
setCronixie();
strip.getSegment(0).grouping = 10; // 10 LEDs per digit
}
}
void setup() {
}
void loop() {
if (!toki.isTick()) return;
initCronixie();
_overlayCronixie();
strip.trigger();
}
byte getSameCodeLength(char code, int index, char const cronixieDisplay[])
{
byte counter = 0;
for (int i = index+1; i < 6; i++)
{
if (cronixieDisplay[i] == code)
{
counter++;
} else {
return counter;
}
}
return counter;
}
void setCronixie()
{
/*
* digit purpose index
* 0-9 | 0-9 (incl. random)
* 10 | blank
* 11 | blank, bg off
* 12 | test upw.
* 13 | test dnw.
* 14 | binary AM/PM
* 15 | BB upper +50 for no trailing 0
* 16 | BBB
* 17 | BBBB
* 18 | BBBBB
* 19 | BBBBBB
* 20 | H
* 21 | HH
* 22 | HHH
* 23 | HHHH
* 24 | M
* 25 | MM
* 26 | MMM
* 27 | MMMM
* 28 | MMMMM
* 29 | MMMMMM
* 30 | S
* 31 | SS
* 32 | SSS
* 33 | SSSS
* 34 | SSSSS
* 35 | SSSSSS
* 36 | Y
* 37 | YY
* 38 | YYYY
* 39 | I
* 40 | II
* 41 | W
* 42 | WW
* 43 | D
* 44 | DD
* 45 | DDD
* 46 | V
* 47 | VV
* 48 | VVV
* 49 | VVVV
* 50 | VVVVV
* 51 | VVVVVV
* 52 | v
* 53 | vv
* 54 | vvv
* 55 | vvvv
* 56 | vvvvv
* 57 | vvvvvv
*/
//H HourLower | HH - Hour 24. | AH - Hour 12. | HHH Hour of Month | HHHH Hour of Year
//M MinuteUpper | MM Minute of Hour | MMM Minute of 12h | MMMM Minute of Day | MMMMM Minute of Month | MMMMMM Minute of Year
//S SecondUpper | SS Second of Minute | SSS Second of 10 Minute | SSSS Second of Hour | SSSSS Second of Day | SSSSSS Second of Week
//B AM/PM | BB 0-6/6-12/12-18/18-24 | BBB 0-3... | BBBB 0-1.5... | BBBBB 0-1 | BBBBBB 0-0.5
//Y YearLower | YY - Year LU | YYYY - Std.
//I MonthLower | II - Month of Year
//W Week of Month | WW Week of Year
//D Day of Week | DD Day Of Month | DDD Day Of Year
DEBUG_PRINT("cset ");
DEBUG_PRINTLN(cronixieDisplay);
for (int i = 0; i < 6; i++)
{
dP[i] = 10;
switch (cronixieDisplay[i])
{
case '_': dP[i] = 10; break;
case '-': dP[i] = 11; break;
case 'r': dP[i] = random(1,7); break; //random btw. 1-6
case 'R': dP[i] = random(0,10); break; //random btw. 0-9
//case 't': break; //Test upw.
//case 'T': break; //Test dnw.
case 'b': dP[i] = 14 + getSameCodeLength('b',i,cronixieDisplay); i = i+dP[i]-14; break;
case 'B': dP[i] = 14 + getSameCodeLength('B',i,cronixieDisplay); i = i+dP[i]-14; break;
case 'h': dP[i] = 70 + getSameCodeLength('h',i,cronixieDisplay); i = i+dP[i]-70; break;
case 'H': dP[i] = 20 + getSameCodeLength('H',i,cronixieDisplay); i = i+dP[i]-20; break;
case 'A': dP[i] = 108; i++; break;
case 'a': dP[i] = 58; i++; break;
case 'm': dP[i] = 74 + getSameCodeLength('m',i,cronixieDisplay); i = i+dP[i]-74; break;
case 'M': dP[i] = 24 + getSameCodeLength('M',i,cronixieDisplay); i = i+dP[i]-24; break;
case 's': dP[i] = 80 + getSameCodeLength('s',i,cronixieDisplay); i = i+dP[i]-80; break; //refresh more often bc. of secs
case 'S': dP[i] = 30 + getSameCodeLength('S',i,cronixieDisplay); i = i+dP[i]-30; break;
case 'Y': dP[i] = 36 + getSameCodeLength('Y',i,cronixieDisplay); i = i+dP[i]-36; break;
case 'y': dP[i] = 86 + getSameCodeLength('y',i,cronixieDisplay); i = i+dP[i]-86; break;
case 'I': dP[i] = 39 + getSameCodeLength('I',i,cronixieDisplay); i = i+dP[i]-39; break; //Month. Don't ask me why month and minute both start with M.
case 'i': dP[i] = 89 + getSameCodeLength('i',i,cronixieDisplay); i = i+dP[i]-89; break;
//case 'W': break;
//case 'w': break;
case 'D': dP[i] = 43 + getSameCodeLength('D',i,cronixieDisplay); i = i+dP[i]-43; break;
case 'd': dP[i] = 93 + getSameCodeLength('d',i,cronixieDisplay); i = i+dP[i]-93; break;
case '0': dP[i] = 0; break;
case '1': dP[i] = 1; break;
case '2': dP[i] = 2; break;
case '3': dP[i] = 3; break;
case '4': dP[i] = 4; break;
case '5': dP[i] = 5; break;
case '6': dP[i] = 6; break;
case '7': dP[i] = 7; break;
case '8': dP[i] = 8; break;
case '9': dP[i] = 9; break;
//case 'V': break; //user var0
//case 'v': break; //user var1
}
}
DEBUG_PRINT("result ");
for (int i = 0; i < 5; i++)
{
DEBUG_PRINT((int)dP[i]);
DEBUG_PRINT(" ");
}
DEBUG_PRINTLN((int)dP[5]);
_overlayCronixie(); // refresh
}
void _overlayCronixie()
{
byte h = hour(localTime);
byte h0 = h;
byte m = minute(localTime);
byte s = second(localTime);
byte d = day(localTime);
byte mi = month(localTime);
int y = year(localTime);
//this has to be changed in time for 22nd century
y -= 2000; if (y<0) y += 30; //makes countdown work
if (useAMPM && !countdownMode)
{
if (h>12) h-=12;
else if (h==0) h+=12;
}
for (int i = 0; i < 6; i++)
{
if (dP[i] < 12) _digitOut[i] = dP[i];
else {
if (dP[i] < 65)
{
switch(dP[i])
{
case 21: _digitOut[i] = h/10; _digitOut[i+1] = h- _digitOut[i]*10; i++; break; //HH
case 25: _digitOut[i] = m/10; _digitOut[i+1] = m- _digitOut[i]*10; i++; break; //MM
case 31: _digitOut[i] = s/10; _digitOut[i+1] = s- _digitOut[i]*10; i++; break; //SS
case 20: _digitOut[i] = h- (h/10)*10; break; //H
case 24: _digitOut[i] = m/10; break; //M
case 30: _digitOut[i] = s/10; break; //S
case 43: _digitOut[i] = weekday(localTime); _digitOut[i]--; if (_digitOut[i]<1) _digitOut[i]= 7; break; //D
case 44: _digitOut[i] = d/10; _digitOut[i+1] = d- _digitOut[i]*10; i++; break; //DD
case 40: _digitOut[i] = mi/10; _digitOut[i+1] = mi- _digitOut[i]*10; i++; break; //II
case 37: _digitOut[i] = y/10; _digitOut[i+1] = y- _digitOut[i]*10; i++; break; //YY
case 39: _digitOut[i] = 2; _digitOut[i+1] = 0; _digitOut[i+2] = y/10; _digitOut[i+3] = y- _digitOut[i+2]*10; i+=3; break; //YYYY
//case 16: _digitOut[i+2] = ((h0/3)&1)?1:0; i++; //BBB (BBBB NI)
//case 15: _digitOut[i+1] = (h0>17 || (h0>5 && h0<12))?1:0; i++; //BB
case 14: _digitOut[i] = (h0>11)?1:0; break; //B
}
} else
{
switch(dP[i])
{
case 71: _digitOut[i] = h/10; _digitOut[i+1] = h- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //hh
case 75: _digitOut[i] = m/10; _digitOut[i+1] = m- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //mm
case 81: _digitOut[i] = s/10; _digitOut[i+1] = s- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //ss
//case 66: _digitOut[i+2] = ((h0/3)&1)?1:10; i++; //bbb (bbbb NI)
//case 65: _digitOut[i+1] = (h0>17 || (h0>5 && h0<12))?1:10; i++; //bb
case 64: _digitOut[i] = (h0>11)?1:10; break; //b
case 93: _digitOut[i] = weekday(localTime); _digitOut[i]--; if (_digitOut[i]<1) _digitOut[i]= 7; break; //d
case 94: _digitOut[i] = d/10; _digitOut[i+1] = d- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //dd
case 90: _digitOut[i] = mi/10; _digitOut[i+1] = mi- _digitOut[i]*10; if(_digitOut[i] == 0) _digitOut[i]=10; i++; break; //ii
case 87: _digitOut[i] = y/10; _digitOut[i+1] = y- _digitOut[i]*10; i++; break; //yy
case 89: _digitOut[i] = 2; _digitOut[i+1] = 0; _digitOut[i+2] = y/10; _digitOut[i+3] = y- _digitOut[i+2]*10; i+=3; break; //yyyy
}
}
}
}
}
void handleOverlayDraw()
{
byte offsets[] = {5, 0, 6, 1, 7, 2, 8, 3, 9, 4};
for (uint16_t i = 0; i < 6; i++)
{
byte o = 10*i;
byte excl = 10;
if(_digitOut[i] < 10) excl = offsets[_digitOut[i]];
excl += o;
if (backlight && _digitOut[i] <11)
{
uint32_t col = strip.gamma32(strip.getSegment(0).colors[1]);
for (uint16_t j=o; j< o+10; j++) {
if (j != excl) strip.setPixelColor(j, col);
}
} else
{
for (uint16_t j=o; j< o+10; j++) {
if (j != excl) strip.setPixelColor(j, 0);
}
}
}
}
void addToJsonState(JsonObject& root)
{
root["nx"] = cronixieDisplay;
}
void readFromJsonState(JsonObject& root)
{
if (root["nx"].is<const char*>()) {
strncpy(cronixieDisplay, root["nx"], 6);
}
}
void addToConfig(JsonObject& root)
{
JsonObject top = root.createNestedObject(F("Cronixie"));
top["backlight"] = backlight;
}
bool readFromConfig(JsonObject& root)
{
// default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor
// setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed)
JsonObject top = root[F("Cronixie")];
bool configComplete = !top.isNull();
configComplete &= getJsonValue(top["backlight"], backlight);
return configComplete;
}
uint16_t getId()
{
return USERMOD_ID_CRONIXIE;
}
};

View File

@@ -1,19 +0,0 @@
# ESP32 Touch Brightness Control
Toggle On/Off with a long press (800ms)
Switch through 5 brightness levels (defined in usermod_touchbrightness.h, values 0-255) with a short (100ms) touch
## Installation
Copy 'usermod_touchbrightness.h' to the wled00 directory.
in 'usermod_list.cpp' add this:
> #include "usermod_touchbrightness.h"
above "void registerUsermods()"
and
> usermods.add(new TouchBrightnessControl());
inside the "registerUsermods()" function

View File

@@ -1,89 +0,0 @@
//
// usermod_touchbrightness.h
// github.com/aircoookie/WLED
//
// Created by Justin Kühner on 14.09.2020.
// Copyright © 2020 NeariX. All rights reserved.
// https://github.com/NeariX67/
// Discord: @NeariX#4799
#pragma once
#include "wled.h"
#define threshold 40 //Increase value if touches falsely accur. Decrease value if actual touches are not recognized
#define touchPin T0 //T0 = D4 / GPIO4
//Define the 5 brightness levels
//Long press to turn off / on
#define brightness1 51
#define brightness2 102
#define brightness3 153
#define brightness4 204
#define brightness5 255
#ifdef ESP32
class TouchBrightnessControl : public Usermod {
private:
unsigned long lastTime = 0; //Interval
unsigned long lastTouch = 0; //Timestamp of last Touch
unsigned long lastRelease = 0; //Timestamp of last Touch release
boolean released = true; //current Touch state (touched/released)
uint16_t touchReading = 0; //sensor reading, maybe use uint8_t???
uint16_t touchDuration = 0; //duration of last touch
public:
void setup() {
lastTouch = millis();
lastRelease = millis();
lastTime = millis();
}
void loop() {
if (millis() - lastTime >= 50) { //Check every 50ms if a touch occurs
lastTime = millis();
touchReading = touchRead(touchPin); //Read touch sensor on pin T0 (GPIO4 / D4)
if(touchReading < threshold && released) { //Touch started
released = false;
lastTouch = millis();
}
else if(touchReading >= threshold && !released) { //Touch released
released = true;
lastRelease = millis();
touchDuration = lastRelease - lastTouch; //Calculate duration
}
//Serial.println(touchDuration);
if(touchDuration >= 800 && released) { //Toggle power if button press is longer than 800ms
touchDuration = 0; //Reset touch duration to avoid multiple actions on same touch
toggleOnOff();
colorUpdated(2); //Refresh values
}
else if(touchDuration >= 100 && released) { //Switch to next brightness if touch is between 100 and 800ms
touchDuration = 0; //Reset touch duration to avoid multiple actions on same touch
if(bri < brightness1) {
bri = brightness1;
} else if(bri >= brightness1 && bri < brightness2) {
bri = brightness2;
} else if(bri >= brightness2 && bri < brightness3) {
bri = brightness3;
} else if(bri >= brightness3 && bri < brightness4) {
bri = brightness4;
} else if(bri >= brightness4 && bri < brightness5) {
bri = brightness5;
} else if(bri >= brightness5) {
bri = brightness1;
}
colorUpdated(2); //Refresh values
}
}
}
};
#endif

View File

@@ -25,6 +25,18 @@ class MyExampleUsermod : public Usermod {
private:
//Private class members. You can declare variables and functions only accessible to your usermod here
unsigned long lastTime = 0;
// set your config variables to their boot default value (this can also be done in readFromConfig() or a constructor if you prefer)
bool testBool = false;
unsigned long testULong = 42424242;
float testFloat = 42.42;
String testString = "Forty-Two";
// These config variables have defaults set inside readFromConfig()
int testInt;
long testLong;
int8_t testPins[2];
public:
//Functions called by WLED
@@ -114,30 +126,96 @@ class MyExampleUsermod : public Usermod {
* It might cause the LEDs to stutter and will cause flash wear if called too often.
* Use it sparingly and always in the loop, never in network callbacks!
*
* addToConfig() will also not yet add your setting to one of the settings pages automatically.
* To make that work you still have to add the setting to the HTML, xml.cpp and set.cpp manually.
* addToConfig() will make your settings editable through the Usermod Settings page automatically.
*
* Usermod Settings Overview:
* - Numeric values are treated as floats in the browser.
* - If the numeric value entered into the browser contains a decimal point, it will be parsed as a C float
* before being returned to the Usermod. The float data type has only 6-7 decimal digits of precision, and
* doubles are not supported, numbers will be rounded to the nearest float value when being parsed.
* The range accepted by the input field is +/- 1.175494351e-38 to +/- 3.402823466e+38.
* - If the numeric value entered into the browser doesn't contain a decimal point, it will be parsed as a
* C int32_t (range: -2147483648 to 2147483647) before being returned to the usermod.
* Overflows or underflows are truncated to the max/min value for an int32_t, and again truncated to the type
* used in the Usermod when reading the value from ArduinoJson.
* - Pin values can be treated differently from an integer value by using the key name "pin"
* - "pin" can contain a single or array of integer values
* - On the Usermod Settings page there is simple checking for pin conflicts and warnings for special pins
* - Red color indicates a conflict. Yellow color indicates a pin with a warning (e.g. an input-only pin)
* - Tip: use int8_t to store the pin value in the Usermod, so a -1 value (pin not set) can be used
*
* See usermod_v2_auto_save.h for an example that saves Flash space by reusing ArduinoJson key name strings
*
* If you need a dedicated settings page with custom layout for your Usermod, that takes a lot more work.
* You will have to add the setting to the HTML, xml.cpp and set.cpp manually.
* See the WLED Soundreactive fork (code and wiki) for reference. https://github.com/atuline/WLED
*
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
*/
void addToConfig(JsonObject& root)
{
JsonObject top = root.createNestedObject("exampleUsermod");
top["great"] = userVar0; //save this var persistently whenever settings are saved
top["great"] = userVar0; //save these vars persistently whenever settings are saved
top["testBool"] = testBool;
top["testInt"] = testInt;
top["testLong"] = testLong;
top["testULong"] = testULong;
top["testFloat"] = testFloat;
top["testString"] = testString;
JsonArray pinArray = top.createNestedArray("pin");
pinArray.add(testPins[0]);
pinArray.add(testPins[1]);
}
/*
* readFromConfig() can be used to read back the custom settings you added with addToConfig().
* This is called by WLED when settings are loaded (currently this only happens once immediately after boot)
* This is called by WLED when settings are loaded (currently this only happens immediately after boot, or after saving on the Usermod Settings page)
*
* readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes),
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
*
* Return true in case the config values returned from Usermod Settings were complete, or false if you'd like WLED to save your defaults to disk (so any missing values are editable in Usermod Settings)
*
* getJsonValue() returns false if the value is missing, or copies the value into the variable provided and returns true if the value is present
* The configComplete variable is true only if the "exampleUsermod" object and all values are present. If any values are missing, WLED will know to call addToConfig() to save them
*
* This function is guaranteed to be called on boot, but could also be called every time settings are updated
*/
void readFromConfig(JsonObject& root)
bool readFromConfig(JsonObject& root)
{
JsonObject top = root["top"];
userVar0 = top["great"] | 42; //The value right of the pipe "|" is the default value in case your setting was not present in cfg.json (e.g. first boot)
// default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor
// setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed)
JsonObject top = root["exampleUsermod"];
bool configComplete = !top.isNull();
configComplete &= getJsonValue(top["great"], userVar0);
configComplete &= getJsonValue(top["testBool"], testBool);
configComplete &= getJsonValue(top["testULong"], testULong);
configComplete &= getJsonValue(top["testFloat"], testFloat);
configComplete &= getJsonValue(top["testString"], testString);
// A 3-argument getJsonValue() assigns the 3rd argument as a default value if the Json value is missing
configComplete &= getJsonValue(top["testInt"], testInt, 42);
configComplete &= getJsonValue(top["testLong"], testLong, -42424242);
configComplete &= getJsonValue(top["pin"][0], testPins[0], -1);
configComplete &= getJsonValue(top["pin"][1], testPins[1], -1);
return configComplete;
}
/*
* handleOverlayDraw() is called just before every show() (LED strip update frame) after effects have set the colors.
* Use this to blank out some LEDs or set them to a different color regardless of the set effect mode.
* Commonly used for custom clocks (Cronixie, 7 segment)
*/
void handleOverlayDraw()
{
//strip.setPixelColor(0, RGBW32(0,0,0,0)) // set the first pixel to black
}

View File

@@ -0,0 +1,70 @@
#ifndef CHIP_SELECT_H
#define CHIP_SELECT_H
#include "Hardware.h"
/*
* `digit`s are as defined in Hardware.h, 0 == seconds ones, 5 == hours tens.
*/
class ChipSelect {
private:
uint8_t digits_map;
const uint8_t all_on = 0x3F;
const uint8_t all_off = 0x00;
public:
ChipSelect() : digits_map(all_off) {}
void update() {
// Documented in README.md. Q7 and Q6 are unused. Q5 is Seconds Ones, Q0 is Hours Tens.
// Q7 is the first bit written, Q0 is the last. So we push two dummy bits, then start with
// Seconds Ones and end with Hours Tens.
// CS is Active Low, but digits_map is 1 for enable, 0 for disable. So we bit-wise NOT first.
uint8_t to_shift = (~digits_map) << 2;
digitalWrite(CSSR_LATCH_PIN, LOW);
shiftOut(CSSR_DATA_PIN, CSSR_CLOCK_PIN, LSBFIRST, to_shift);
digitalWrite(CSSR_LATCH_PIN, HIGH);
}
void begin()
{
pinMode(CSSR_LATCH_PIN, OUTPUT);
pinMode(CSSR_DATA_PIN, OUTPUT);
pinMode(CSSR_CLOCK_PIN, OUTPUT);
digitalWrite(CSSR_DATA_PIN, LOW);
digitalWrite(CSSR_CLOCK_PIN, LOW);
digitalWrite(CSSR_LATCH_PIN, LOW);
update();
}
// These speak the indexes defined in Hardware.h.
// So 0 is disabled, 1 is enabled (even though CS is active low, this gets mapped.)
// So bit 0 (LSB), is index 0, is SECONDS_ONES
// Translation to what the 74HC595 uses is done in update()
void setDigitMap(uint8_t map, bool update_=true) { digits_map = map; if (update_) update(); }
uint8_t getDigitMap() { return digits_map; }
// Helper functions
// Sets just the one digit by digit number
void setDigit(uint8_t digit, bool update_=true) { setDigitMap(0x01 << digit, update_); }
void setAll(bool update_=true) { setDigitMap(all_on, update_); }
void clear(bool update_=true) { setDigitMap(all_off, update_); }
void setSecondsOnes() { setDigit(SECONDS_ONES); }
void setSecondsTens() { setDigit(SECONDS_TENS); }
void setMinutesOnes() { setDigit(MINUTES_ONES); }
void setMinutesTens() { setDigit(MINUTES_TENS); }
void setHoursOnes() { setDigit(HOURS_ONES); }
void setHoursTens() { setDigit(HOURS_TENS); }
bool isSecondsOnes() { return ((digits_map & SECONDS_ONES_MAP) > 0); }
bool isSecondsTens() { return ((digits_map & SECONDS_TENS_MAP) > 0); }
bool isMinutesOnes() { return ((digits_map & MINUTES_ONES_MAP) > 0); }
bool isMinutesTens() { return ((digits_map & MINUTES_TENS_MAP) > 0); }
bool isHoursOnes() { return ((digits_map & HOURS_ONES_MAP) > 0); }
bool isHoursTens() { return ((digits_map & HOURS_TENS_MAP) > 0); }
};
#endif // CHIP_SELECT_H

View File

@@ -0,0 +1,52 @@
/*
* Define the hardware for the EleksTube IPS clock. Mostly pin definitions
*/
#ifndef ELEKSTUBEHAX_HARDWARE_H
#define ELEKSTUBEHAX_HARDWARE_H
#include <stdint.h>
#include <Arduino.h> // for HIGH and LOW
// Common indexing scheme, used to identify the digit
#define SECONDS_ONES (0)
#define SECONDS_TENS (1)
#define MINUTES_ONES (2)
#define MINUTES_TENS (3)
#define HOURS_ONES (4)
#define HOURS_TENS (5)
#define NUM_DIGITS (6)
#define SECONDS_ONES_MAP (0x01 << SECONDS_ONES)
#define SECONDS_TENS_MAP (0x01 << SECONDS_TENS)
#define MINUTES_ONES_MAP (0x01 << MINUTES_ONES)
#define MINUTES_TENS_MAP (0x01 << MINUTES_TENS)
#define HOURS_ONES_MAP (0x01 << HOURS_ONES)
#define HOURS_TENS_MAP (0x01 << HOURS_TENS)
// WS2812 (or compatible) LEDs on the back of the display modules.
#define BACKLIGHTS_PIN (12)
// Buttons, active low, externally pulled up (with actual resistors!)
#define BUTTON_LEFT_PIN (33)
#define BUTTON_MODE_PIN (32)
#define BUTTON_RIGHT_PIN (35)
#define BUTTON_POWER_PIN (34)
// I2C to DS3231 RTC.
#define RTC_SCL_PIN (22)
#define RTC_SDA_PIN (21)
// Chip Select shift register, to select the display
#define CSSR_DATA_PIN (14)
#define CSSR_CLOCK_PIN (16)
#define CSSR_LATCH_PIN (17)
// SPI to displays
// DEFINED IN User_Setup.h
// Look for: TFT_MOSI, TFT_SCLK, TFT_CS, TFT_DC, and TFT_RST
// Power for all TFT displays are grounded through a MOSFET so they can all be turned off.
// Active HIGH.
#define TFT_ENABLE_PIN (27)
#endif // ELEKSTUBEHAX_HARDWARE_H

View File

@@ -0,0 +1,379 @@
#ifndef TFTS_H
#define TFTS_H
#include "wled.h"
#include <FS.h>
#include <TFT_eSPI.h>
#include "Hardware.h"
#include "ChipSelect.h"
class TFTs : public TFT_eSPI {
private:
uint8_t digits[NUM_DIGITS];
// These read 16- and 32-bit types from the SD card file.
// BMP data is stored little-endian, Arduino is little-endian too.
// May need to reverse subscript order if porting elsewhere.
uint16_t read16(fs::File &f) {
uint16_t result;
((uint8_t *)&result)[0] = f.read(); // LSB
((uint8_t *)&result)[1] = f.read(); // MSB
return result;
}
uint32_t read32(fs::File &f) {
uint32_t result;
((uint8_t *)&result)[0] = f.read(); // LSB
((uint8_t *)&result)[1] = f.read();
((uint8_t *)&result)[2] = f.read();
((uint8_t *)&result)[3] = f.read(); // MSB
return result;
}
uint16_t output_buffer[TFT_HEIGHT][TFT_WIDTH];
int16_t w = 135, h = 240, x = 0, y = 0, bufferedDigit = 255;
uint16_t digitR, digitG, digitB, dimming = 255;
uint32_t digitColor = 0;
void drawBuffer() {
bool oldSwapBytes = getSwapBytes();
setSwapBytes(true);
pushImage(x, y, w, h, (uint16_t *)output_buffer);
setSwapBytes(oldSwapBytes);
}
// These BMP functions are stolen directly from the TFT_SPIFFS_BMP example in the TFT_eSPI library.
// Unfortunately, they aren't part of the library itself, so I had to copy them.
// I've modified drawBmp to buffer the whole image at once instead of doing it line-by-line.
//// BEGIN STOLEN CODE
// Draw directly from file stored in RGB565 format. Fastest
bool drawBin(const char *filename) {
fs::File bmpFS;
// Open requested file on SD card
bmpFS = WLED_FS.open(filename, "r");
size_t sz = bmpFS.size();
if (sz > 64800) {
bmpFS.close();
return false;
}
uint16_t r, g, b, dimming = 255;
int16_t row, col;
//draw img that is shorter than 240pix into the center
w = 135;
h = sz / (w * 2);
x = 0;
y = (height() - h) /2;
uint8_t lineBuffer[w * 2];
if (!realtimeMode || realtimeOverride) strip.service();
// 0,0 coordinates are top left
for (row = 0; row < h; row++) {
bmpFS.read(lineBuffer, sizeof(lineBuffer));
uint8_t PixM, PixL;
// Colors are already in 16-bit R5, G6, B5 format
for (col = 0; col < w; col++)
{
if (dimming == 255 && !digitColor) { // not needed, copy directly
output_buffer[row][col] = (lineBuffer[col*2+1] << 8) | (lineBuffer[col*2]);
} else {
// 16 BPP pixel format: R5, G6, B5 ; bin: RRRR RGGG GGGB BBBB
PixM = lineBuffer[col*2+1];
PixL = lineBuffer[col*2];
// align to 8-bit value (MSB left aligned)
r = (PixM) & 0xF8;
g = ((PixM << 5) | (PixL >> 3)) & 0xFC;
b = (PixL << 3) & 0xF8;
r *= dimming; g *= dimming; b *= dimming;
r = r >> 8; g = g >> 8; b = b >> 8;
if (digitColor) { // grayscale pixel coloring
uint8_t l = (r > g) ? ((r > b) ? r:b) : ((g > b) ? g:b);
r = g = b = l;
r *= digitR; g *= digitG; b *= digitB;
r = r >> 8; g = g >> 8; b = b >> 8;
}
output_buffer[row][col] = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
}
}
}
drawBuffer();
bmpFS.close();
return true;
}
bool drawBmp(const char *filename) {
fs::File bmpFS;
// Open requested file on SD card
bmpFS = WLED_FS.open(filename, "r");
uint32_t seekOffset, headerSize, paletteSize = 0;
int16_t row;
uint16_t r, g, b, dimming = 255, bitDepth;
uint16_t magic = read16(bmpFS);
if (magic != ('B' | ('M' << 8))) { // File not found or not a BMP
Serial.println(F("BMP not found!"));
bmpFS.close();
return false;
}
read32(bmpFS); // filesize in bytes
read32(bmpFS); // reserved
seekOffset = read32(bmpFS); // start of bitmap
headerSize = read32(bmpFS); // header size
w = read32(bmpFS); // width
h = read32(bmpFS); // height
read16(bmpFS); // color planes (must be 1)
bitDepth = read16(bmpFS);
if (read32(bmpFS) != 0 || (bitDepth != 24 && bitDepth != 1 && bitDepth != 4 && bitDepth != 8)) {
Serial.println(F("BMP format not recognized."));
bmpFS.close();
return false;
}
uint32_t palette[256];
if (bitDepth <= 8) // 1,4,8 bit bitmap: read color palette
{
read32(bmpFS); read32(bmpFS); read32(bmpFS); // size, w resolution, h resolution
paletteSize = read32(bmpFS);
if (paletteSize == 0) paletteSize = bitDepth * bitDepth; //if 0, size is 2^bitDepth
bmpFS.seek(14 + headerSize); // start of color palette
for (uint16_t i = 0; i < paletteSize; i++) {
palette[i] = read32(bmpFS);
}
}
// draw img that is shorter than 240pix into the center
x = (width() - w) /2;
y = (height() - h) /2;
bmpFS.seek(seekOffset);
uint32_t lineSize = ((bitDepth * w +31) >> 5) * 4;
uint8_t lineBuffer[lineSize];
uint8_t serviceStrip = (!realtimeMode || realtimeOverride) ? 7 : 0;
// row is decremented as the BMP image is drawn bottom up
for (row = h-1; row >= 0; row--) {
if ((row & 0b00000111) == serviceStrip) strip.service(); //still refresh backlight to mitigate stutter every few rows
bmpFS.read(lineBuffer, sizeof(lineBuffer));
uint8_t* bptr = lineBuffer;
// Convert 24 to 16 bit colors while copying to output buffer.
for (uint16_t col = 0; col < w; col++)
{
if (bitDepth == 24) {
b = *bptr++;
g = *bptr++;
r = *bptr++;
} else {
uint32_t c = 0;
if (bitDepth == 8) {
c = palette[*bptr++];
}
else if (bitDepth == 4) {
c = palette[(*bptr >> ((col & 0x01)?0:4)) & 0x0F];
if (col & 0x01) bptr++;
}
else { // bitDepth == 1
c = palette[(*bptr >> (7 - (col & 0x07))) & 0x01];
if ((col & 0x07) == 0x07) bptr++;
}
b = c; g = c >> 8; r = c >> 16;
}
if (dimming != 255) { // only dimm when needed
r *= dimming; g *= dimming; b *= dimming;
r = r >> 8; g = g >> 8; b = b >> 8;
}
if (digitColor) { // grayscale pixel coloring
uint8_t l = (r > g) ? ((r > b) ? r:b) : ((g > b) ? g:b);
r = g = b = l;
r *= digitR; g *= digitG; b *= digitB;
r = r >> 8; g = g >> 8; b = b >> 8;
}
output_buffer[row][col] = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xFF) >> 3);
}
}
drawBuffer();
bmpFS.close();
return true;
}
bool drawClk(const char *filename) {
fs::File bmpFS;
// Open requested file on SD card
bmpFS = WLED_FS.open(filename, "r");
if (!bmpFS)
{
Serial.print("File not found: ");
Serial.println(filename);
return false;
}
uint16_t r, g, b, dimming = 255, magic;
int16_t row, col;
magic = read16(bmpFS);
if (magic != 0x4B43) { // look for "CK" header
Serial.print(F("File not a CLK. Magic: "));
Serial.println(magic);
bmpFS.close();
return false;
}
w = read16(bmpFS);
h = read16(bmpFS);
x = (width() - w) / 2;
y = (height() - h) / 2;
uint8_t lineBuffer[w * 2];
if (!realtimeMode || realtimeOverride) strip.service();
// 0,0 coordinates are top left
for (row = 0; row < h; row++) {
bmpFS.read(lineBuffer, sizeof(lineBuffer));
uint8_t PixM, PixL;
// Colors are already in 16-bit R5, G6, B5 format
for (col = 0; col < w; col++)
{
if (dimming == 255 && !digitColor) { // not needed, copy directly
output_buffer[row][col+x] = (lineBuffer[col*2+1] << 8) | (lineBuffer[col*2]);
} else {
// 16 BPP pixel format: R5, G6, B5 ; bin: RRRR RGGG GGGB BBBB
PixM = lineBuffer[col*2+1];
PixL = lineBuffer[col*2];
// align to 8-bit value (MSB left aligned)
r = (PixM) & 0xF8;
g = ((PixM << 5) | (PixL >> 3)) & 0xFC;
b = (PixL << 3) & 0xF8;
r *= dimming; g *= dimming; b *= dimming;
r = r >> 8; g = g >> 8; b = b >> 8;
if (digitColor) { // grayscale pixel coloring
uint8_t l = (r > g) ? ((r > b) ? r:b) : ((g > b) ? g:b);
r = g = b = l;
r *= digitR; g *= digitG; b *= digitB;
r = r >> 8; g = g >> 8; b = b >> 8;
}
output_buffer[row][col+x] = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
}
}
}
drawBuffer();
bmpFS.close();
return true;
}
public:
TFTs() : TFT_eSPI(), chip_select()
{ for (uint8_t digit=0; digit < NUM_DIGITS; digit++) digits[digit] = 0; }
// no == Do not send to TFT. yes == Send to TFT if changed. force == Send to TFT.
enum show_t { no, yes, force };
// A digit of 0xFF means blank the screen.
const static uint8_t blanked = 255;
uint8_t tubeSegment = 1;
uint8_t digitOffset = 0;
void begin() {
pinMode(TFT_ENABLE_PIN, OUTPUT);
digitalWrite(TFT_ENABLE_PIN, HIGH); //enable displays on boot
// Start with all displays selected.
chip_select.begin();
chip_select.setAll();
// Initialize the super class.
init();
}
void showDigit(uint8_t digit) {
chip_select.setDigit(digit);
uint8_t digitToDraw = digits[digit];
if (digitToDraw < 10) digitToDraw += digitOffset;
if (digitToDraw == blanked) {
fillScreen(TFT_BLACK); return;
}
// if last digit was the same, skip loading from FS to buffer
if (!digitColor && digitToDraw == bufferedDigit) drawBuffer();
digitR = R(digitColor); digitG = G(digitColor); digitB = B(digitColor);
// Filenames are no bigger than "254.bmp\0"
char file_name[10];
// Fastest, raw RGB565
sprintf(file_name, "/%d.bin", digitToDraw);
if (WLED_FS.exists(file_name)) {
if (drawBin(file_name)) bufferedDigit = digitToDraw;
return;
}
// Fast, raw RGB565, see https://github.com/aly-fly/EleksTubeHAX on how to create this clk format
sprintf(file_name, "/%d.clk", digitToDraw);
if (WLED_FS.exists(file_name)) {
if (drawClk(file_name)) bufferedDigit = digitToDraw;
return;
}
// Slow, regular RGB888 or 1,4,8 bit palette BMP
sprintf(file_name, "/%d.bmp", digitToDraw);
if (drawBmp(file_name)) bufferedDigit = digitToDraw;
return;
}
void setDigit(uint8_t digit, uint8_t value, show_t show=yes) {
uint8_t old_value = digits[digit];
digits[digit] = value;
// Color in grayscale bitmaps if Segment 1 exists
// TODO If secondary and tertiary are black, color all in primary,
// else color first three from Seg 1 color slots and last three from Seg 2 color slots
WS2812FX::Segment& seg1 = strip.getSegment(tubeSegment);
if (seg1.isActive()) {
digitColor = strip.getPixelColor(seg1.start + digit);
dimming = seg1.opacity;
} else {
digitColor = 0;
dimming = 255;
}
if (show != no && (old_value != value || show == force)) {
showDigit(digit);
}
}
uint8_t getDigit(uint8_t digit) {return digits[digit];}
void showAllDigits() {for (uint8_t digit=0; digit < NUM_DIGITS; digit++) showDigit(digit);}
// Making chip_select public so we don't have to proxy all methods, and the caller can just use it directly.
ChipSelect chip_select;
};
#endif // TFTS_H

View File

@@ -0,0 +1,47 @@
/*
* This is intended to over-ride `User_Setup.h` that comes with the TFT_eSPI library.
* I hate having to modify the library code.
*/
// ST7789 135 x 240 display with no chip select line
#define ST7789_DRIVER // Configure all registers
#define TFT_WIDTH 135
#define TFT_HEIGHT 240
#define CGRAM_OFFSET // Library will add offsets required
//#define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue
//#define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red
//#define TFT_INVERSION_ON
//#define TFT_INVERSION_OFF
// EleksTube IPS
#define TFT_SDA_READ // Read and write on the MOSI/SDA pin, no separate MISO pin
#define TFT_MOSI 23
#define TFT_SCLK 18
//#define TFT_CS -1 // Not connected
#define TFT_DC 25 // Data Command, aka Register Select or RS
#define TFT_RST 26 // Connect reset to ensure display initialises
#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
//#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
//#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
//#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
//#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:.
//#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT
//#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts
//#define SMOOTH_FONT
//#define SPI_FREQUENCY 27000000
#define SPI_FREQUENCY 40000000
/*
* To make the Library not over-write all this:
*/
#define USER_SETUP_LOADED

View File

@@ -0,0 +1,45 @@
# EleksTube IPS Clock usermod
This usermod allows WLED to run on the EleksTube IPS clock.
It enables running all WLED effects on the background SK6812 lighting, while displaying digit bitmaps on the 6 IPS screens.
Code is largely based on https://github.com/SmittyHalibut/EleksTubeHAX by Mark Smith!
Supported:
- Display with custom bitmaps (.bmp) or raw RGB565 images (.bin) from filesystem
- Background lighting
- All 4 hardware buttons
- RTC (with RTC usermod)
- Standard WLED time features (NTP, DST, timezones)
Not supported:
- On-device setup with buttons (WiFi setup only)
Your images must be 1-135 pixels wide and 1-240 pixels high.
For BMP, 1, 4, 8, and 24 bits per pixel formats are supported.
## Installation
Compile and upload to clock using the `elekstube_ips` PlatformIO environment
Once uploaded (the clock can be flashed like any ESP32 module), go to `[WLED-IP]/edit` and upload the 0-9.bin files from [here](https://github.com/Aircoookie/NixieThemes/tree/master/themes/RealisticNixie/bin).
You can find more clockfaces in the [NixieThemes](https://github.com/Aircoookie/NixieThemes/) repo.
Use LED pin 12, relay pin 27 and button pin 34.
## Use of RGB565 images
Binary 16-bit per pixel RGB565 format `.bin` and `.clk` images are now supported. This has the benefit of only using 2/3rds of the file size a 24 BPP `.bmp` has.
The drawback is that this format cannot be handled by common image programs and that an extra conversion step is needed.
You can use https://lvgl.io/tools/imageconverter to convert your .bmp to a .bin file (settings `True color` and `Binary RGB565`).
Thank you to @RedNax67 for adding .bin and .clk support.
For most clockface designs, using 4 or 8 BPP BMP formats will save even more file size:
| Bits per pixel | File size in kB (for 135x240 img) | % of 24 BPP BMP | Max unique colors
| --- | --- | --- | --- |
24 | 98 | 100% | 16M (66K)
16 (.clk) | 64.8 | 66% | 66K
8 | 33.7 | 34% | 256
4 | 16.4 | 17% | 16
1 | 4.9 | 5% | 2
Comparison 1 vs. 4 vs. 8 vs. 24 BPP. With this clockface on the actual clock, 4 bit looks good, and 8 bit is almost indistinguishable from 24 bit.
![comparison](https://user-images.githubusercontent.com/21045690/156899667-5b55ed9f-6e03-4066-b2aa-1260e9570369.png)

View File

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

View File

@@ -100,9 +100,9 @@ void userLoop() {
needRedraw = true;
} else if (knownBrightness != bri) {
needRedraw = true;
} else if (knownMode != strip.getMode()) {
} else if (knownMode != strip.getMainSegment().mode) {
needRedraw = true;
} else if (knownPalette != strip.getSegment(0).palette) {
} else if (knownPalette != strip.getMainSegment().palette) {
needRedraw = true;
}
@@ -126,8 +126,8 @@ void userLoop() {
#endif
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
knownBrightness = bri;
knownMode = strip.getMode();
knownPalette = strip.getSegment(0).palette;
knownMode = strip.getMainSegment().mode;
knownPalette = strip.getMainSegment().palette;
u8x8.clear();
u8x8.setFont(u8x8_font_chroma48medium8_r);

View File

@@ -143,9 +143,9 @@ void userLoop() {
needRedraw = true;
} else if (knownBrightness != bri) {
needRedraw = true;
} else if (knownMode != strip.getMode()) {
} else if (knownMode != strip.getMainSegment().mode) {
needRedraw = true;
} else if (knownPalette != strip.getSegment(0).palette) {
} else if (knownPalette != strip.getMainSegment().palette) {
needRedraw = true;
}
@@ -169,8 +169,8 @@ void userLoop() {
#endif
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
knownBrightness = bri;
knownMode = strip.getMode();
knownPalette = strip.getSegment(0).palette;
knownMode = strip.getMainSegment().mode;
knownPalette = strip.getMainSegment().palette;
u8x8.clear();
u8x8.setFont(u8x8_font_chroma48medium8_r);

View File

@@ -149,11 +149,14 @@ Delay <input type=\"number\" min=\"5\" max=\"300\" value=\"";
/**
* restore the changeable values
*/
void readFromConfig(JsonObject &root)
bool readFromConfig(JsonObject &root)
{
JsonObject top = root["FixUnreachableNetServices"];
if (top.isNull()) return false;
m_pingDelayMs = top["PingDelayMs"] | m_pingDelayMs;
m_pingDelayMs = max(5000UL, min(18000000UL, m_pingDelayMs));
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return true;
}
/**

View File

@@ -0,0 +1,119 @@
{
"desc": "21-key",
"0xFFA25D": {
"label": "On",
"pos": "1x1",
"cmd": "T=1"
},
"0xFF629D": {
"label": "Off",
"pos": "1x2",
"cmd": "T=0"
},
"0xFFE21D": {
"label": "Flash",
"pos": "1x3",
"cmnt": "Cycle Effects",
"cmd": "CY=0&FX=~"
},
"0xFF22DD": {
"label": "Strobe",
"pos": "2x1",
"cmnt": "Sinelon Dual",
"cmd": "CY=0&FX=93"
},
"0xFF02FD": {
"label": "Fade",
"pos": "2x2",
"cmnt": "Rain",
"cmd": "CY=0&FX=43"
},
"0xFFC23D": {
"label": "Smooth",
"pos": "2x3",
"cmnt": "Aurora",
"cmd": "CY=0&FX=38"
},
"0xFFE01F": {
"label": "Bright +",
"pos": "3x1",
"cmd": "A=~16"
},
"0xFFA857": {
"label": "Bright -",
"pos": "3x2",
"cmd": "A=~-16"
},
"0xFF906F": {
"label": "White",
"pos": "3x3",
"cmd": "FP=5&CL=hFFFFFF&C2=hFFFFFF&C3=hA8A8A8"
},
"0xFF6897": {
"label": "Red",
"pos": "4x1",
"cmnt": "Lava",
"cmd": "FP=8"
},
"0xFF9867": {
"label": "Green",
"pos": "4x2",
"cmnt": "Forest",
"cmd": "FP=10"
},
"0xFFB04F": {
"label": "Blue",
"pos": "4x3",
"cmnt": "Breeze",
"cmd": "FP=15"
},
"0xFF30CF": {
"label": "Tomato",
"pos": "5x1",
"cmd": "FP=5&CL=hFF6347&C2=hFFBF47&C3=hA85859"
},
"0xFF18E7": {
"label": "LightGreen",
"pos": "5x2",
"cmnt": "Rivendale",
"cmd": "FP=14"
},
"0xFF7A85": {
"label": "SkyBlue",
"pos": "5x3",
"cmnt": "Ocean",
"cmd": "FP=9"
},
"0xFF10EF": {
"label": "Orange",
"pos": "6x1",
"cmnt": "Orangery",
"cmd": "FP=47"
},
"0xFF38C7": {
"label": "Aqua",
"pos": "6x2",
"cmd": "FP=5&CL=hFFFF&C2=h7FFF&C3=h39A895"
},
"0xFF5AA5": {
"label": "Purple",
"pos": "6x3",
"cmd": "FP=5&CL=h663399&C2=h993399&C3=h473864"
},
"0xFF42BD": {
"label": "Yellow",
"pos": "7x1",
"cmd": "FP=5&CL=hFFFF00&C2=hFFC800&C3=hFDFFDE"
},
"0xFF4AB5": {
"label": "Cyan",
"pos": "7x2",
"cmnt": "Beech",
"cmd": "FP=22"
},
"0xFF52AD": {
"label": "Pink",
"pos": "7x3",
"cmd": "FP=5&CL=hFFC0CB&C2=hFFD4C0&C3=hA88C96"
}
}

View File

@@ -0,0 +1,147 @@
{
"desc": "24-key",
"0xF700FF": {
"label": "+",
"pos": "1x1",
"cmnt": "Speed +",
"cmd": "SX=~16"
},
"0xF7807F": {
"label": "-",
"pos": "1x2",
"cmnt": "Speed -",
"cmd": "SX=~-16"
},
"0xF740BF": {
"label": "On/Off",
"pos": "1x3",
"cmnt": "Toggle On/Off",
"cmd": "T=2"
},
"0xF7C03F": {
"label": "W",
"pos": "1x4",
"cmnt": "Cycle color palette",
"cmd": "FP=~"
},
"0xF720DF": {
"label": "R",
"pos": "2x1",
"cmnt": "Lava",
"cmd": "FP=8"
},
"0xF7A05F": {
"label": "G",
"pos": "2x2",
"cmnt": "Forest",
"cmd": "FP=10"
},
"0xF7609F": {
"label": "B",
"pos": "2x3",
"cmnt": "Breeze",
"cmd": "FP=15"
},
"0xF7E01F": {
"label": "Bright -",
"pos": "2x4",
"cmnt": "Bright -",
"cmd": "A=~-16"
},
"0xF710EF": {
"label": "Timer1H",
"pos": "3x1",
"cmnt": "Timer 60 min",
"cmd": "NL=60&NT=0"
},
"0xF7906F": {
"label": "Timer4H",
"pos": "3x2",
"cmnt": "Timer 30 min",
"cmd": "NL=30&NT=0"
},
"0xF750AF": {
"label": "Timer8H",
"pos": "3x3",
"cmnt": "Timer 15 min",
"cmd": "NL=15&NT=0"
},
"0xF7D02F": {
"label": "Bright128",
"pos": "3x4",
"cmnt": "Bright 128",
"cmd": "A=128"
},
"0xF730CF": {
"label": "Music1",
"pos": "4x1",
"cmnt": "Cycle FX +",
"cmd": "FX=~"
},
"0xF7B04F": {
"label": "Music2",
"pos": "4x2",
"cmnt": "Cycle FX -",
"cmd": "FX=~-1"
},
"0xF7708F": {
"label": "Music3",
"pos": "4x3",
"cmnt": "Reset FX and FP",
"cmd": "FX=1&PF=6"
},
"0xF7F00F": {
"label": "Bright +",
"pos": "4x4",
"cmnt": "Bright +",
"cmd": "A=~16"
},
"0xF708F7": {
"label": "Mode1",
"pos": "5x1",
"cmnt": "Preset 1",
"cmd": "PL=1"
},
"0xF78877": {
"label": "Mode2",
"pos": "5x2",
"cmnt": "Preset 2",
"cmd": "PL=2"
},
"0xF748B7": {
"label": "Mode3",
"pos": "5x3",
"cmnt": "Preset 3",
"cmd": "PL=3"
},
"0xF7C837": {
"label": "Up",
"pos": "5x4",
"cmnt": "Intensity +",
"cmd": "IX=~16"
},
"0xF728D7": {
"label": "Mode4",
"pos": "6x1",
"cmnt": "Preset 4",
"cmd": "PL=4"
},
"0xF7A857": {
"label": "Mode5",
"pos": "6x2",
"cmnt": "Preset 5",
"cmd": "PL=5"
},
"0xF76897": {
"label": "Cycle",
"pos": "6x3",
"cmnt": "Toggle preset cycle",
"cmd": "CY=1&PT=60000"
},
"0xF7E817": {
"label": "Down",
"pos": "6x4",
"cmnt": "Intensity -",
"cmd": "IX=~-16"
}
}

View File

@@ -0,0 +1,185 @@
{
"desc": "32-key",
"0xFF08F7": {
"label": "On",
"pos": "1x1",
"cmd": "T=1"
},
"0xFFC03F": {
"label": "Off",
"pos": "1x2",
"cmd": "T=0"
},
"0xFF807F": {
"label": "Auto",
"pos": "1x3",
"cmnt": "Toggle preset cycle",
"cmd": "CY=2"
},
"0xFF609F": {
"label": "Mode",
"pos": "1x4",
"cmnt": "Cycle effects",
"cmd": "FX=~&CY=0"
},
"0xFF906F": {
"label": "4H",
"pos": "2x1",
"cmnt": "Timer 60min",
"cmd": "NL=60&NT=0"
},
"0xFFB847": {
"label": "6H",
"pos": "2x2",
"cmnt": "Timer 90min",
"cmd": "NL=90&NT=0"
},
"0xFFF807": {
"label": "8H",
"pos": "2x3",
"cmnt": "Timer 120min",
"cmd": "NL=120&NT=0"
},
"0xFFB04F": {
"label": "Timer Off",
"pos": "2x4",
"cmd": "NL=0"
},
"0xFF9867": {
"label": "Red",
"pos": "3x1",
"cmnt": "Lava",
"cmd": "FP=8"
},
"0xFFD827": {
"label": "Green",
"pos": "3x2",
"cmnt": "Forest",
"cmd": "FP=10"
},
"0xFF8877": {
"label": "Blue",
"pos": "3x3",
"cmnt": "Breeze",
"cmd": "FP=15"
},
"0xFFA857": {
"label": "White",
"pos": "3x4",
"cmd": "FP=5&CL=hFFFFFF&C2=hFFE4CD&C3=hE4E4FF"
},
"0xFFE817": {
"label": "OrangeRed",
"pos": "4x1",
"cmnt": "Sakura",
"cmd": "FP=49"
},
"0xFF48B7": {
"label": "SeaGreen",
"pos": "4x2",
"cmnt": "Rivendale",
"cmd": "FP=14"
},
"0xFF6897": {
"label": "RoyalBlue",
"pos": "4x3",
"cmnt": "Ocean",
"cmd": "FP=9"
},
"0xFFB24D": {
"label": "DarkBlue",
"pos": "4x4",
"cmnt": "Breeze",
"cmd": "FP=15"
},
"0xFF02FD": {
"label": "Orange",
"pos": "5x1",
"cmnt": "Orangery",
"cmd": "FP=47"
},
"0xFF32CD": {
"label": "YellowGreen",
"pos": "5x2",
"cmnt": "Aurora",
"cmd": "FP=37"
},
"0xFF20DF": {
"label": "SkyBlue",
"pos": "5x3",
"cmnt": "Beech",
"cmd": "FP=22"
},
"0xFF00FF": {
"label": "Orchid",
"pos": "5x4",
"cmd": "FP=5&CL=hDA70D6&C2=hDA70A0&C3=h89618F"
},
"0xFF50AF": {
"label": "Yellow",
"pos": "6x1",
"cmd": "FP=5&CL=hFFFF00&C2=hFFC800&C3=hFDFFDE"
},
"0xFF7887": {
"label": "DarkGreen",
"pos": "6x2",
"cmnt": "Orange and Teal",
"cmd": "FP=44"
},
"0xFF708F": {
"label": "RebeccaPurple",
"pos": "6x3",
"cmd": "FP=5&CL=h800080&C2=h800040&C3=h4B1C54"
},
"0xFF58A7": {
"label": "Plum",
"pos": "6x4",
"cmd": "FP=5&CL=hDDA0DD&C2=hDDA0BE&C3=h8D7791"
},
"0xFF38C7": {
"label": "Strobe",
"pos": "7x1",
"cmnt": "Dancing Shadows",
"cmd": "FX=112&CY=0"
},
"0xFF28D7": {
"label": "In Waves",
"pos": "7x2",
"cmnt": "Noise 1",
"cmd": "FX=70&CY=0"
},
"0xFFF00F": {
"label": "Speed +",
"pos": "7x3",
"cmd": "SX=~16"
},
"0xFF30CF": {
"label": "Speed -",
"pos": "7x4",
"cmd": "SX=~-16"
},
"0xFF40BF": {
"label": "Jump",
"pos": "8x1",
"cmnt": "Colortwinkles",
"cmd": "FX=74&CY=0"
},
"0xFF12ED": {
"label": "Fade",
"pos": "8x2",
"cmnt": "Sunrise",
"cmd": "FX=104&CY=0"
},
"0xFF2AD5": {
"label": "Flash",
"pos": "8x3",
"cmnt": "Railway",
"cmd": "FX=78&CY=0"
},
"0xFFA05F": {
"label": "Chase Flash",
"pos": "8x4",
"cmnt": "Washing Machine",
"cmd": "FX=113&CY=0"
}
}

View File

@@ -0,0 +1,233 @@
{
"desc": "40-key-black",
"0xFF3AC5": {
"label": "Bright +",
"pos": "1x1",
"cmd": "A=~16"
},
"0xFFBA45": {
"label": "Bright -",
"pos": "1x2",
"cmd": "A=~-16"
},
"0xFF827D": {
"label": "Off",
"pos": "1x3",
"cmd": "T=0"
},
"0xFF02FD": {
"label": "On",
"pos": "1x4",
"cmd": "T=1"
},
"0xFF1AE5": {
"label": "Red",
"pos": "2x1",
"cmnt": "Lava",
"cmd": "FP=8"
},
"0xFF9A65": {
"label": "Green",
"pos": "2x2",
"cmnt": "Forest",
"cmd": "FP=10"
},
"0xFFA25D": {
"label": "Blue",
"pos": "2x3",
"cmnt": "Breeze",
"cmd": "FP=15"
},
"0xFF22DD": {
"label": "White",
"pos": "2x4",
"cmd": "FP=5&CL=hFFFFFF&C2=hFFFFFF&C3=hA8A8A8"
},
"0xFF2AD5": {
"label": "Tomato",
"pos": "3x1",
"cmnt": "Yelmag",
"cmd": "FP=5&CL=hFF6347&C2=hFFBF47&C3=hA85859"
},
"0xFFAA55": {
"label": "LightGreen",
"pos": "3x2",
"cmnt": "Rivendale",
"cmd": "FP=14"
},
"0xFF926D": {
"label": "SkyBlue",
"pos": "3x3",
"cmnt": "Ocean",
"cmd": "FP=9"
},
"0xFF12ED": {
"label": "WarmWhite",
"pos": "3x4",
"cmnt": "Warm White",
"cmd": "FP=5&CL=hFFE4CD&C2=hFFFCCD&C3=hA89892"
},
"0xFF0AF5": {
"label": "OrangeRed",
"pos": "4x1",
"cmnt": "Sakura",
"cmd": "FP=49"
},
"0xFF8A75": {
"label": "Cyan",
"pos": "4x2",
"cmnt": "Beech",
"cmd": "FP=22"
},
"0xFFB24D": {
"label": "RebeccaPurple",
"pos": "4x3",
"cmd": "FP=5&CL=h663399&C2=h993399&C3=h473864"
},
"0xFF32CD": {
"label": "CoolWhite",
"pos": "4x4",
"cmnt": "Cool White",
"cmd": "FP=5&CL=hE4E4FF&C2=hF1E4FF&C3=h9C9EA8"
},
"0xFF38C7": {
"label": "Orange",
"pos": "5x1",
"cmnt": "Orangery",
"cmd": "FP=47"
},
"0xFFB847": {
"label": "Turquoise",
"pos": "5x2",
"cmd": "FP=5&CL=h40E0D0&C2=h40A0E0&C3=h4E9381"
},
"0xFF7887": {
"label": "Purple",
"pos": "5x3",
"cmd": "FP=5&CL=h800080&C2=h800040&C3=h4B1C54"
},
"0xFFF807": {
"label": "MedGray",
"pos": "5x4",
"cmnt": "Cycle palette +",
"cmd": "FP=~"
},
"0xFF18E7": {
"label": "Yellow",
"pos": "6x1",
"cmd": "FP=5&CL=hFFFF00&C2=h7FFF00&C3=hA89539"
},
"0xFF9867": {
"label": "DarkCyan",
"pos": "6x2",
"cmd": "FP=5&CL=h8B8B&C2=h458B&C3=h1F5B51"
},
"0xFF58A7": {
"label": "Plum",
"pos": "6x3",
"cmnt": "Magenta",
"cmd": "FP=40"
},
"0xFFD827": {
"label": "DarkGray",
"pos": "6x4",
"cmnt": "Cycle palette -",
"cmd": "FP=~-"
},
"0xFF28D7": {
"label": "Jump3",
"pos": "7x1",
"cmnt": "Colortwinkles",
"cmd": "CY=0&FX=74"
},
"0xFFA857": {
"label": "Fade3",
"pos": "7x2",
"cmnt": "Rain",
"cmd": "CY=0&FX=43"
},
"0xFF6897": {
"label": "Flash",
"pos": "7x3",
"cmnt": "Cycle Effects",
"cmd": "CY=0&FX=~"
},
"0xFFE817": {
"label": "Quick",
"pos": "7x4",
"cmnt": "Fx speed +16",
"cmd": "SX=~16"
},
"0xFF08F7": {
"label": "Jump7",
"pos": "8x1",
"cmnt": "Sinelon Dual",
"cmd": "CY=0&FX=93"
},
"0xFF8877": {
"label": "Fade7",
"pos": "8x2",
"cmnt": "Lighthouse",
"cmd": "CY=0&FX=41"
},
"0xFF48B7": {
"label": "Auto",
"pos": "8x3",
"cmnt": "Toggle preset cycle",
"cmd": "CY=2"
},
"0xFFC837": {
"label": "Slow",
"pos": "8x4",
"cmnt": "FX speed -16",
"cmd": "SX=~-16"
},
"0xFF30CF": {
"label": "Custom1",
"pos": "9x1",
"cmnt": "Noise 1",
"cmd": "CY=0&FX=70"
},
"0xFFB04F": {
"label": "Custom2",
"pos": "9x2",
"cmnt": "Dancing Shadows",
"cmd": "CY=0&FX=112"
},
"0xFF708F": {
"label": "Music +",
"pos": "9x3",
"cmnt": "FX Intensity +16",
"cmd": "IX=~16"
},
"0xFFF00F": {
"label": "Timer60",
"pos": "9x4",
"cmnt": "Timer 60 min",
"cmd": "NL=60&NT=0"
},
"0xFF10EF": {
"label": "Custom3",
"pos": "10x1",
"cmnt": "Twinklefox",
"cmd": "CY=0&FX=80"
},
"0xFF906F": {
"label": "Custom4",
"pos": "10x2",
"cmnt": "Twinklecat",
"cmd": "CY=0&FX=81"
},
"0xFF50AF": {
"label": "Music -",
"pos": "10x3",
"cmnt": "FX Intesity -16",
"cmd": "IX=~-16"
},
"0xFFD02F": {
"label": "Timer120",
"pos": "10x4",
"cmnt": "Timer 120 min",
"cmd": "NL=120&NT=0"
}
}

View File

@@ -0,0 +1,217 @@
{
"desc": "40-key-blue",
"0xFF3AC5": {
"label": "Bright +",
"pos": "1x1",
"cmd": "A=~16"
},
"0xFFBA45": {
"label": "Bright -",
"pos": "1x2",
"cmd": "A=~-16"
},
"0xFF827D": {
"label": "Off",
"pos": "1x3",
"cmd": "T=0"
},
"0xFF02FD": {
"label": "On",
"pos": "1x4",
"cmd": "T=1"
},
"0xFF1AE5": {
"label": "Red",
"pos": "2x1",
"cmnt": "Lava",
"cmd": "FP=8"
},
"0xFF9A65": {
"label": "Green",
"pos": "2x2",
"cmnt": "Forest",
"cmd": "FP=10"
},
"0xFFA25D": {
"label": "Blue",
"pos": "2x3",
"cmnt": "Breeze",
"cmd": "FP=15"
},
"0xFF22DD": {
"label": "White",
"pos": "2x4",
"cmd": "FP=5&CL=hFFFFFF&C2=hFFFFFF&C3=hA8A8A8"
},
"0xFF2AD5": {
"label": "Tomato",
"pos": "3x1",
"cmnt": "Yelmag",
"cmd": "FP=5&CL=hFF6347&C2=hFFBF47&C3=hA85859"
},
"0xFFAA55": {
"label": "LightGreen",
"pos": "3x2",
"cmnt": "Rivendale",
"cmd": "FP=14"
},
"0xFF926D": {
"label": "SkyBlue",
"pos": "3x3",
"cmnt": "Ocean",
"cmd": "FP=9"
},
"0xFF12ED": {
"label": "WarmWhite",
"pos": "3x4",
"cmnt": "Warm White",
"cmd": "FP=5&CL=hFFE4CD&C2=hFFFCCD&C3=hA89892"
},
"0xFF0AF5": {
"label": "OrangeRed",
"pos": "4x1",
"cmnt": "Sakura",
"cmd": "FP=49"
},
"0xFF8A75": {
"label": "Cyan",
"pos": "4x2",
"cmnt": "Beech",
"cmd": "FP=22"
},
"0xFFB24D": {
"label": "RebeccaPurple",
"pos": "4x3",
"cmd": "FP=5&CL=h663399&C2=h993399&C3=h473864"
},
"0xFF32CD": {
"label": "CoolWhite",
"pos": "4x4",
"cmnt": "Cool White",
"cmd": "FP=5&CL=hE4E4FF&C2=hF1E4FF&C3=h9C9EA8"
},
"0xFF38C7": {
"label": "Orange",
"pos": "5x1",
"cmnt": "Orangery",
"cmd": "FP=47"
},
"0xFFB847": {
"label": "Turquoise",
"pos": "5x2",
"cmd": "FP=5&CL=h40E0D0&C2=h40A0E0&C3=h4E9381"
},
"0xFF7887": {
"label": "Purple",
"pos": "5x3",
"cmd": "FP=5&CL=h800080&C2=h800040&C3=h4B1C54"
},
"0xFFF807": {
"label": "MedGray",
"pos": "5x4",
"cmnt": "Cycle palette +",
"cmd": "FP=~"
},
"0xFF18E7": {
"label": "Yellow",
"pos": "6x1",
"cmd": "FP=5&CL=hFFFF00&C2=h7FFF00&C3=hA89539"
},
"0xFF9867": {
"label": "DarkCyan",
"pos": "6x2",
"cmd": "FP=5&CL=h8B8B&C2=h458B&C3=h1F5B51"
},
"0xFF58A7": {
"label": "Plum",
"pos": "6x3",
"cmnt": "Magenta",
"cmd": "FP=40"
},
"0xFFD827": {
"label": "DarkGray",
"pos": "6x4",
"cmnt": "Cycle palette -",
"cmd": "FP=~-"
},
"0xFF28D7": {
"label": "W +",
"pos": "7x1"
},
"0xFFA857": {
"label": "W -",
"pos": "7x2"
},
"0xFF6897": {
"label": "W On",
"pos": "7x3"
},
"0xFFE817": {
"label": "W Off",
"pos": "7x4"
},
"0xFF08F7": {
"label": "W25",
"pos": "8x1"
},
"0xFF8877": {
"label": "W50",
"pos": "8x2"
},
"0xFF48B7": {
"label": "W75",
"pos": "8x3"
},
"0xFFC837": {
"label": "W100",
"pos": "8x4"
},
"0xFF30CF": {
"label": "Jump3",
"pos": "9x1",
"cmnt": "Colortwinkles",
"cmd": "CY=0&FX=74"
},
"0xFFB04F": {
"label": "Fade3",
"pos": "9x2",
"cmnt": "Rain",
"cmd": "CY=0&FX=43"
},
"0xFF708F": {
"label": "Jump7",
"pos": "9x3",
"cmnt": "Sinelon Dual",
"cmd": "CY=0&FX=93"
},
"0xFFF00F": {
"label": "Quick",
"pos": "9x4",
"cmnt": "Fx speed +16",
"cmd": "SX=~16"
},
"0xFF10EF": {
"label": "Fade",
"pos": "10x1",
"cmnt": "Lighthouse",
"cmd": "CY=0&FX=41"
},
"0xFF906F": {
"label": "Flash",
"pos": "10x2",
"cmnt": "Cycle Effects",
"cmd": "CY=0&FX=~"
},
"0xFF50AF": {
"label": "Auto",
"pos": "10x3",
"cmnt": "Toggle preset cycle",
"cmd": "CY=2"
},
"0xFFD02F": {
"label": "Slow",
"pos": "10x4",
"cmnt": "Sinelon Dual",
"cmd": "CY=0&FX=93"
}
}

View File

@@ -0,0 +1,241 @@
{
"desc": "44-key",
"0xFF3AC5": {
"label": "Bright +",
"pos": "1x1",
"cmd": "A=~16"
},
"0xFFBA45": {
"label": "Bright -",
"pos": "1x2",
"cmd": "A=~-16"
},
"0xFF827D": {
"label": "Off",
"pos": "1x3",
"cmd": "T=0"
},
"0xFF02FD": {
"label": "On",
"pos": "1x4",
"cmd": "T=1"
},
"0xFF1AE5": {
"label": "Red",
"pos": "2x1",
"cmnt": "Lava",
"cmd": "FP=8"
},
"0xFF9A65": {
"label": "Green",
"pos": "2x2",
"cmnt": "Forest",
"cmd": "FP=10"
},
"0xFFA25D": {
"label": "Blue",
"pos": "2x3",
"cmnt": "Breeze",
"cmd": "FP=15"
},
"0xFF22DD": {
"label": "White",
"pos": "2x4",
"cmd": "FP=5&CL=hFFFFFF&C2=hFFFFFF&C3=hA8A8A8"
},
"0xFF2AD5": {
"label": "Tomato",
"pos": "3x1",
"cmd": "FP=5&CL=hFF6347&C2=hFFBF47&C3=hA85859"
},
"0xFFAA55": {
"label": "LightGreen",
"pos": "3x2",
"cmnt": "Rivendale",
"cmd": "FP=14"
},
"0xFF926D": {
"label": "DeepBlue",
"pos": "3x3",
"cmnt": "Ocean",
"cmd": "FP=9"
},
"0xFF12ED": {
"label": "Warmwhite2",
"pos": "3x4",
"cmnt": "Warm White",
"cmd": "FP=5&CL=hFFE4CD&C2=hFFFCCD&C3=hA89892"
},
"0xFF0AF5": {
"label": "Orange",
"pos": "4x1",
"cmnt": "Sakura",
"cmd": "FP=49"
},
"0xFF8A75": {
"label": "Turquoise",
"pos": "4x2",
"cmnt": "Beech",
"cmd": "FP=22"
},
"0xFFB24D": {
"label": "Purple",
"pos": "4x3",
"cmd": "FP=5&CL=h663399&C2=h993399&C3=h473864"
},
"0xFF32CD": {
"label": "WarmWhite",
"pos": "4x4",
"cmd": "FP=5&CL=hE4E4FF&C2=hF1E4FF&C3=h9C9EA8"
},
"0xFF38C7": {
"label": "Yellowish",
"pos": "5x1",
"cmnt": "Orangery",
"cmd": "FP=47"
},
"0xFFB847": {
"label": "Cyan",
"pos": "5x2",
"cmnt": "Beech",
"cmd": "FP=22"
},
"0xFF7887": {
"label": "Magenta",
"pos": "5x3",
"cmd": "FP=5&CL=hFF00FF&C2=hFF007F&C3=h9539A8"
},
"0xFFF807": {
"label": "ColdWhite",
"pos": "5x4",
"cmd": "FP=5&CL=hE4E4FF&C2=hF1E4FF&C3=h9C9EA8"
},
"0xFF18E7": {
"label": "Yellow",
"pos": "6x1",
"cmd": "FP=5&CL=hFFFF00&C2=hFFC800&C3=hFDFFDE"
},
"0xFF9867": {
"label": "Aqua",
"pos": "6x2",
"cmd": "FP=5&CL=hFFFF&C2=h7FFF&C3=h39A895"
},
"0xFF58A7": {
"label": "Pink",
"pos": "6x3",
"cmd": "FP=5&CL=hFFC0CB&C2=hFFD4C0&C3=hA88C96"
},
"0xFFD827": {
"label": "ColdWhite2",
"pos": "6x4",
"cmd": "FP=5&CL=hE4E4FF&C2=hF1E4FF&C3=h9C9EA8"
},
"0xFF28D7": {
"label": "Red +",
"pos": "7x1",
"cmd": "FP=5&R=~16"
},
"0xFFA857": {
"label": "Green +",
"pos": "7x2",
"cmd": "FP=5&G=~16"
},
"0xFF6897": {
"label": "Blue +",
"pos": "7x3",
"cmd": "FP=5&B=~16"
},
"0xFFE817": {
"label": "Quick",
"pos": "7x4",
"cmnt": "Fx speed +16",
"cmd": "SX=~16"
},
"0xFF08F7": {
"label": "Red -",
"pos": "8x1",
"cmd": "FP=5&R=~-16"
},
"0xFF8877": {
"label": "Green -",
"pos": "8x2",
"cmd": "FP=5&G=~-16"
},
"0xFF48B7": {
"label": "Blue -",
"pos": "8x3",
"cmd": "FP=5&B=~-16"
},
"0xFFC837": {
"label": "Slow",
"pos": "8x4",
"cmnt": "FX speed -16",
"cmd": "SX=~-16"
},
"0xFF30CF": {
"label": "Diy1",
"pos": "9x1",
"cmd": "CY=0&PL=1"
},
"0xFFB04F": {
"label": "Diy2",
"pos": "9x2",
"cmd": "CY=0&PL=2"
},
"0xFF708F": {
"label": "Diy3",
"pos": "9x3",
"cmd": "CY=0&PL=3"
},
"0xFFF00F": {
"label": "Auto",
"pos": "9x4",
"cmnt": "Toggle preset cycle",
"cmd": "CY=2"
},
"0xFF10EF": {
"label": "Diy4",
"pos": "10x1",
"cmd": "CY=0&PL=4"
},
"0xFF906F": {
"label": "Diy5",
"pos": "10x2",
"cmd": "CY=0&PL=5"
},
"0xFF50AF": {
"label": "Diy6",
"pos": "10x3",
"cmd": "CY=0&PL=6"
},
"0xFFD02F": {
"label": "Flash",
"pos": "10x4",
"cmnt": "Cycle Effects",
"cmd": "CY=0&FX=~"
},
"0xFF20DF": {
"label": "Jump3",
"pos": "11x1",
"cmnt": "Colortwinkles",
"cmd": "CY=0&FX=74"
},
"0xFFA05F": {
"label": "Jump7",
"pos": "11x2",
"cmnt": "Sinelon Dual",
"cmd": "CY=0&FX=93"
},
"0xFF609F": {
"label": "Fade3",
"pos": "11x3",
"cmnt": "Rain",
"cmd": "CY=0&FX=43"
},
"0xFFE01F": {
"label": "Fade7",
"pos": "11x4",
"cmnt": "Lighthouse",
"cmd": "CY=0&FX=41"
}
}

View File

@@ -0,0 +1,38 @@
{
"desc": "6-key",
"0xFF0FF0": {
"label": "Power",
"pos": "1x1",
"cmd": "T=2"
},
"0xFF8F70": {
"label": "Channel +",
"pos": "2x1",
"cmnt": "Cycle palette up",
"cmd": "FP=~"
},
"0xFF4FB0": {
"label": "Channel -",
"pos": "3x1",
"cmnt": "Cycle palette down",
"cmd": "FP=~-"
},
"0xFFCF30": {
"label": "Volume +",
"pos": "4x1",
"cmnt": "Brighten",
"cmd": "A=~16"
},
"0xFF2FD0": {
"label": "Volume -",
"pos": "5x1",
"cmnt": "Dim",
"cmd": "A=~-16"
},
"0xFFAF50": {
"label": "Mute",
"pos": "6x1",
"cmnt": "Cycle effects",
"cmd": "CY=0&FX=~"
}
}

View File

@@ -0,0 +1,47 @@
{
"desc": "9-key",
"0xFF629D": {
"label": "Power",
"cmd": "T=2"
},
"0xFF22DD": {
"label": "A",
"cmnt": "Preset 1",
"cmd": "PL=1"
},
"0xFF02FD": {
"label": "B",
"cmnt": "Preset 2",
"cmd": "PL=2"
},
"0xFFC23D": {
"label": "C",
"cmnt": "Preset 3",
"cmd": "PL=3"
},
"0xFF30CF": {
"label": "Left",
"cmnt": "Speed -",
"cmd": "SI=~-16"
},
"0xFF7A85": {
"label": "Right",
"cmnt": "Speed +",
"cmd": "SI=~16"
},
"0xFF9867": {
"label": "Up",
"cmnt": "Bright +",
"cmd": "A=~16"
},
"0xFF38C7": {
"label": "Down",
"cmnt": "Bright -",
"cmd": "A=~-16"
},
"0xFF18E7": {
"label": "Select",
"cmnt": "Cycle effects",
"cmd": "CY=0&FX=~"
}
}

Binary file not shown.

View File

@@ -0,0 +1,108 @@
import colorsys
import json
import openpyxl
named_colors = {'AliceBlue': '0xF0F8FF', 'AntiqueWhite': '0xFAEBD7', 'Aqua': '0x00FFFF',
'Aquamarine': '0x7FFFD4', 'Azure': '0xF0FFFF', 'Beige': '0xF5F5DC', 'Bisque': '0xFFE4C4',
'Black': '0x000000', 'BlanchedAlmond': '0xFFEBCD', 'Blue': '0x0000FF',
'BlueViolet': '0x8A2BE2', 'Brown': '0xA52A2A', 'BurlyWood': '0xDEB887',
'CadetBlue': '0x5F9EA0', 'Chartreuse': '0x7FFF00', 'Chocolate': '0xD2691E',
'Coral': '0xFF7F50', 'CornflowerBlue': '0x6495ED', 'Cornsilk': '0xFFF8DC',
'Crimson': '0xDC143C', 'Cyan': '0x00FFFF', 'DarkBlue': '0x00008B', 'DarkCyan': '0x008B8B',
'DarkGoldenRod': '0xB8860B', 'DarkGray': '0xA9A9A9', 'DarkGrey': '0xA9A9A9',
'DarkGreen': '0x006400', 'DarkKhaki': '0xBDB76B', 'DarkMagenta': '0x8B008B',
'DarkOliveGreen': '0x556B2F', 'DarkOrange': '0xFF8C00', 'DarkOrchid': '0x9932CC',
'DarkRed': '0x8B0000', 'DarkSalmon': '0xE9967A', 'DarkSeaGreen': '0x8FBC8F',
'DarkSlateBlue': '0x483D8B', 'DarkSlateGray': '0x2F4F4F', 'DarkSlateGrey': '0x2F4F4F',
'DarkTurquoise': '0x00CED1', 'DarkViolet': '0x9400D3', 'DeepPink': '0xFF1493',
'DeepSkyBlue': '0x00BFFF', 'DimGray': '0x696969', 'DimGrey': '0x696969',
'DodgerBlue': '0x1E90FF', 'FireBrick': '0xB22222', 'FloralWhite': '0xFFFAF0',
'ForestGreen': '0x228B22', 'Fuchsia': '0xFF00FF', 'Gainsboro': '0xDCDCDC',
'GhostWhite': '0xF8F8FF', 'Gold': '0xFFD700', 'GoldenRod': '0xDAA520', 'Gray': '0x808080',
'Grey': '0x808080', 'Green': '0x008000', 'GreenYellow': '0xADFF2F', 'HoneyDew': '0xF0FFF0',
'HotPink': '0xFF69B4', 'IndianRed': '0xCD5C5C', 'Indigo': '0x4B0082', 'Ivory': '0xFFFFF0',
'Khaki': '0xF0E68C', 'Lavender': '0xE6E6FA', 'LavenderBlush': '0xFFF0F5',
'LawnGreen': '0x7CFC00', 'LemonChiffon': '0xFFFACD', 'LightBlue': '0xADD8E6',
'LightCoral': '0xF08080', 'LightCyan': '0xE0FFFF', 'LightGoldenRodYellow': '0xFAFAD2',
'LightGray': '0xD3D3D3', 'LightGrey': '0xD3D3D3', 'LightGreen': '0x90EE90',
'LightPink': '0xFFB6C1', 'LightSalmon': '0xFFA07A', 'LightSeaGreen': '0x20B2AA',
'LightSkyBlue': '0x87CEFA', 'LightSlateGray': '0x778899', 'LightSlateGrey': '0x778899',
'LightSteelBlue': '0xB0C4DE', 'LightYellow': '0xFFFFE0', 'Lime': '0x00FF00',
'LimeGreen': '0x32CD32', 'Linen': '0xFAF0E6', 'Magenta': '0xFF00FF', 'Maroon': '0x800000',
'MediumAquaMarine': '0x66CDAA', 'MediumBlue': '0x0000CD', 'MediumOrchid': '0xBA55D3',
'MediumPurple': '0x9370DB', 'MediumSeaGreen': '0x3CB371', 'MediumSlateBlue': '0x7B68EE',
'MediumSpringGreen': '0x00FA9A', 'MediumTurquoise': '0x48D1CC', 'MediumVioletRed': '0xC71585',
'MidnightBlue': '0x191970', 'MintCream': '0xF5FFFA', 'MistyRose': '0xFFE4E1',
'Moccasin': '0xFFE4B5', 'NavajoWhite': '0xFFDEAD', 'Navy': '0x000080', 'OldLace': '0xFDF5E6',
'Olive': '0x808000', 'OliveDrab': '0x6B8E23', 'Orange': '0xFFA500', 'OrangeRed': '0xFF4500',
'Orchid': '0xDA70D6', 'PaleGoldenRod': '0xEEE8AA', 'PaleGreen': '0x98FB98',
'PaleTurquoise': '0xAFEEEE', 'PaleVioletRed': '0xDB7093', 'PapayaWhip': '0xFFEFD5',
'PeachPuff': '0xFFDAB9', 'Peru': '0xCD853F', 'Pink': '0xFFC0CB', 'Plum': '0xDDA0DD',
'PowderBlue': '0xB0E0E6', 'Purple': '0x800080', 'RebeccaPurple': '0x663399', 'Red': '0xFF0000',
'RosyBrown': '0xBC8F8F', 'RoyalBlue': '0x4169E1', 'SaddleBrown': '0x8B4513', 'Salmon': '0xFA8072',
'SandyBrown': '0xF4A460', 'SeaGreen': '0x2E8B57', 'SeaShell': '0xFFF5EE', 'Sienna': '0xA0522D',
'Silver': '0xC0C0C0', 'SkyBlue': '0x87CEEB', 'SlateBlue': '0x6A5ACD', 'SlateGray': '0x708090',
'SlateGrey': '0x708090', 'Snow': '0xFFFAFA', 'SpringGreen': '0x00FF7F', 'SteelBlue': '0x4682B4',
'Tan': '0xD2B48C', 'Teal': '0x008080', 'Thistle': '0xD8BFD8', 'Tomato': '0xFF6347',
'Turquoise': '0x40E0D0', 'Violet': '0xEE82EE', 'Wheat': '0xF5DEB3', 'White': '0xFFFFFF',
'WhiteSmoke': '0xF5F5F5', 'Yellow': '0xFFFF00', 'YellowGreen': '0x9ACD32'}
def shift_color(col, shift=30, sat=1.0, val=1.0):
r = (col & (255 << 16)) >> 16
g = (col & (255 << 8)) >> 8
b = col & 255
hsv = colorsys.rgb_to_hsv(r, g, b)
h = (((hsv[0] * 360) + shift) % 360) / 360
rgb = colorsys.hsv_to_rgb(h, hsv[1] * sat, hsv[2] * val)
return (int(rgb[0]) << 16) + (int(rgb[1]) << 8) + int(rgb[2])
def parse_sheet(ws):
print(f'Parsing worksheet {ws.title}')
ir = {"desc": ws.title}
rows = ws.rows
keys = [col.value.lower() for col in next(rows)]
for row in rows:
rec = dict(zip(keys, [col.value for col in row]))
if rec.get('code') is None:
continue
cd = {"label": rec.get('label')}
if rec.get('row'):
cd['pos'] = f'{rec["row"]}x{rec["col"]}'
if rec.get('comment'):
cd['cmnt'] = rec.get('comment')
if rec.get('rpt'):
cd['rpt'] = bool(rec['rpt'])
if rec.get('cmd'):
cd['cmd'] = rec['cmd']
elif all((rec.get('primary'), rec.get('secondary'), rec.get('tertiary'))):
c1 = int(rec.get('primary'), 16)
c2 = int(rec.get('secondary'), 16)
c3 = int(rec.get('tertiary'), 16)
cd['cmd'] = f'FP=5&CL=h{c1:X}&C2=h{c2:X}&C3=h{c3:X}'
elif all((rec.get('primary'), rec.get('secondary'))):
c1 = int(rec.get('primary'), 16)
c2 = int(rec.get('secondary'), 16)
c3 = shift_color(c1, -1, sat=0.66, val=0.66)
cd['cmd'] = f'FP=5&CL=h{c1:X}&C2=h{c2:X}&C3=h{c3:X}'
elif rec.get('primary'):
c1 = int(rec.get('primary'), 16)
c2 = shift_color(c1, 30)
c3 = shift_color(c1, -10, sat=0.66, val=0.66)
cd['cmd'] = f'FP=5&CL=h{c1:X}&C2=h{c2:X}&C3=h{c3:X}'
elif rec.get('label') in named_colors:
c1 = int(named_colors[rec.get('label')], 16)
c2 = shift_color(c1, 30)
c3 = shift_color(c1, -10, sat=0.66, val=0.66)
cd['cmd'] = f'FP=5&CL=h{c1:X}&C2=h{c2:X}&C3=h{c3:X}'
else:
print(f'Did not find a command or color for {rec["label"]}. Hint use named CSS colors as labels')
ir[rec['code']] = cd
with open(f'{ws.title}_ir.json', 'w') as fp:
json.dump(ir, fp, indent=2)
if __name__ == '__main__':
wb = openpyxl.load_workbook('IR_Remote_Codes.xlsx')
for ws in wb.worksheets:
parse_sheet(ws)

View File

@@ -0,0 +1,33 @@
# JSON IR remote
## Purpose
The JSON IR remote allows users to customize IR remote behavior without writing custom code and compiling.
It also enables using any remote that is compatible with your IR receiver. Using the JSON IR remote, you can
map buttons from any remote to any HTTP request API or JSON API command.
## Usage
* Upload the IR config file, named _ir.json_ to your board using the [ip address]/edit url. Pick from one of the included files or create your own.
* On the config > LED settings page, set the correct IR pin.
* On the config > Sync Interfaces page, select "JSON Remote" as the Infrared remote.
## Modification
* See if there is a json file with the same number of buttons as your remote. Many remotes will have the same internals and emit the same codes but have different labels.
* In the ir.json file, each key will be the hex encoded IR code.
* The "cmd" property will be the HTTP Request API or JSON API to execute when that button is pressed.
* A limited number of c functions are supported (!incBrightness, !decBrightness, !presetFallback)
* When using !presetFallback, include properties PL (preset to load), FX (effect to fall back to) and FP (palette to fall back to)
* If the command is _repeatable_ and does not contain the "~" character, add a "rpt": true property.
* Other properties are ignored, but having a label property may help when editing.
Sample:
{
"0xFF629D": {"cmd": "T=2", "rpt": true, "label": "Toggle on/off"}, // HTTP command
"0xFF9867": {"cmd": "A=~16", "label": "Inc brightness"}, // HTTP command with incrementing
"0xFF38C7": {"cmd": {"bri": 10}, "label": "Dim to 10"}, // JSON command
"0xFF22DD": {"cmd": "!presetFallback", "PL": 1, "FX": 16, "FP": 6,
"label": "Preset 1 or fallback to Saw - Party"}, // c function
}

321
usermods/MY9291/MY92xx.h Normal file
View File

@@ -0,0 +1,321 @@
/*
MY92XX LED Driver for Arduino
Based on the C driver by MaiKe Labs
Copyright (c) 2016 - 2026 MaiKe Labs
Copyright (C) 2017 - 2018 Xose Pérez for the Arduino compatible library
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _my92xx_h
#define _my92xx_h
#include <Arduino.h>
#ifdef DEBUG_MY92XX
#if ARDUINO_ARCH_ESP8266
#define DEBUG_MSG_MY92XX(...) DEBUG_MY92XX.printf( __VA_ARGS__ )
#elif ARDUINO_ARCH_AVR
#define DEBUG_MSG_MY92XX(...) { char buffer[80]; snprintf(buffer, sizeof(buffer), __VA_ARGS__ ); DEBUG_MY92XX.print(buffer); }
#endif
#else
#define DEBUG_MSG_MY92XX(...)
#endif
typedef enum my92xx_model_t {
MY92XX_MODEL_MY9291 = 0X00,
MY92XX_MODEL_MY9231 = 0X01,
} my92xx_model_t;
typedef enum my92xx_cmd_one_shot_t {
MY92XX_CMD_ONE_SHOT_DISABLE = 0X00,
MY92XX_CMD_ONE_SHOT_ENFORCE = 0X01,
} my92xx_cmd_one_shot_t;
typedef enum my92xx_cmd_reaction_t {
MY92XX_CMD_REACTION_FAST = 0X00,
MY92XX_CMD_REACTION_SLOW = 0X01,
} my92xx_cmd_reaction_t;
typedef enum my92xx_cmd_bit_width_t {
MY92XX_CMD_BIT_WIDTH_16 = 0X00,
MY92XX_CMD_BIT_WIDTH_14 = 0X01,
MY92XX_CMD_BIT_WIDTH_12 = 0X02,
MY92XX_CMD_BIT_WIDTH_8 = 0X03,
} my92xx_cmd_bit_width_t;
typedef enum my92xx_cmd_frequency_t {
MY92XX_CMD_FREQUENCY_DIVIDE_1 = 0X00,
MY92XX_CMD_FREQUENCY_DIVIDE_4 = 0X01,
MY92XX_CMD_FREQUENCY_DIVIDE_16 = 0X02,
MY92XX_CMD_FREQUENCY_DIVIDE_64 = 0X03,
} my92xx_cmd_frequency_t;
typedef enum my92xx_cmd_scatter_t {
MY92XX_CMD_SCATTER_APDM = 0X00,
MY92XX_CMD_SCATTER_PWM = 0X01,
} my92xx_cmd_scatter_t;
typedef struct {
my92xx_cmd_scatter_t scatter : 1;
my92xx_cmd_frequency_t frequency : 2;
my92xx_cmd_bit_width_t bit_width : 2;
my92xx_cmd_reaction_t reaction : 1;
my92xx_cmd_one_shot_t one_shot : 1;
unsigned char resv : 1;
} __attribute__((aligned(1), packed)) my92xx_cmd_t;
#define MY92XX_COMMAND_DEFAULT { \
.scatter = MY92XX_CMD_SCATTER_APDM, \
.frequency = MY92XX_CMD_FREQUENCY_DIVIDE_1, \
.bit_width = MY92XX_CMD_BIT_WIDTH_8, \
.reaction = MY92XX_CMD_REACTION_FAST, \
.one_shot = MY92XX_CMD_ONE_SHOT_DISABLE, \
.resv = 0 \
}
class my92xx {
public:
my92xx(my92xx_model_t model, unsigned char chips, unsigned char di, unsigned char dcki, my92xx_cmd_t command);
unsigned char getChannels();
void setChannel(unsigned char channel, unsigned int value);
unsigned int getChannel(unsigned char channel);
void setState(bool state);
bool getState();
void update();
private:
void _di_pulse(unsigned int times);
void _dcki_pulse(unsigned int times);
void _set_cmd(my92xx_cmd_t command);
void _send();
void _write(unsigned int data, unsigned char bit_length);
my92xx_cmd_t _command;
my92xx_model_t _model = MY92XX_MODEL_MY9291;
unsigned char _chips = 1;
unsigned char _channels;
uint16_t* _value;
bool _state = false;
unsigned char _pin_di;
unsigned char _pin_dcki;
};
#if ARDUINO_ARCH_ESP8266
extern "C" {
void os_delay_us(unsigned int);
}
#elif ARDUINO_ARCH_AVR
#define os_delay_us delayMicroseconds
#endif
void my92xx::_di_pulse(unsigned int times) {
for (unsigned int i = 0; i < times; i++) {
digitalWrite(_pin_di, HIGH);
digitalWrite(_pin_di, LOW);
}
}
void my92xx::_dcki_pulse(unsigned int times) {
for (unsigned int i = 0; i < times; i++) {
digitalWrite(_pin_dcki, HIGH);
digitalWrite(_pin_dcki, LOW);
}
}
void my92xx::_write(unsigned int data, unsigned char bit_length) {
unsigned int mask = (0x01 << (bit_length - 1));
for (unsigned int i = 0; i < bit_length / 2; i++) {
digitalWrite(_pin_dcki, LOW);
digitalWrite(_pin_di, (data & mask) ? HIGH : LOW);
digitalWrite(_pin_dcki, HIGH);
data = data << 1;
digitalWrite(_pin_di, (data & mask) ? HIGH : LOW);
digitalWrite(_pin_dcki, LOW);
digitalWrite(_pin_di, LOW);
data = data << 1;
}
}
void my92xx::_set_cmd(my92xx_cmd_t command) {
// ets_intr_lock();
// TStop > 12us.
os_delay_us(12);
// Send 12 DI pulse, after 6 pulse's falling edge store duty data, and 12
// pulse's rising edge convert to command mode.
_di_pulse(12);
// Delay >12us, begin send CMD data
os_delay_us(12);
// Send CMD data
unsigned char command_data = *(unsigned char*)(&command);
for (unsigned char i = 0; i < _chips; i++) {
_write(command_data, 8);
}
// TStart > 12us. Delay 12 us.
os_delay_us(12);
// Send 16 DI pulseat 14 pulse's falling edge store CMD data, and
// at 16 pulse's falling edge convert to duty mode.
_di_pulse(16);
// TStop > 12us.
os_delay_us(12);
// ets_intr_unlock();
}
void my92xx::_send() {
#ifdef DEBUG_MY92XX
DEBUG_MSG_MY92XX("[MY92XX] Refresh: %s (", _state ? "ON" : "OFF");
for (unsigned char channel = 0; channel < _channels; channel++) {
DEBUG_MSG_MY92XX(" %d", _value[channel]);
}
DEBUG_MSG_MY92XX(" )\n");
#endif
unsigned char bit_length = 8;
switch (_command.bit_width) {
case MY92XX_CMD_BIT_WIDTH_16:
bit_length = 16;
break;
case MY92XX_CMD_BIT_WIDTH_14:
bit_length = 14;
break;
case MY92XX_CMD_BIT_WIDTH_12:
bit_length = 12;
break;
case MY92XX_CMD_BIT_WIDTH_8:
bit_length = 8;
break;
default:
bit_length = 8;
break;
}
// ets_intr_lock();
// TStop > 12us.
os_delay_us(12);
// Send color data
for (unsigned char channel = 0; channel < _channels; channel++) {
_write(_state ? _value[channel] : 0, bit_length);
}
// TStart > 12us. Ready for send DI pulse.
os_delay_us(12);
// Send 8 DI pulse. After 8 pulse falling edge, store old data.
_di_pulse(8);
// TStop > 12us.
os_delay_us(12);
// ets_intr_unlock();
}
// -----------------------------------------------------------------------------
unsigned char my92xx::getChannels() {
return _channels;
}
void my92xx::setChannel(unsigned char channel, unsigned int value) {
if (channel < _channels) {
_value[channel] = value;
}
}
unsigned int my92xx::getChannel(unsigned char channel) {
if (channel < _channels) {
return _value[channel];
}
return 0;
}
bool my92xx::getState() {
return _state;
}
void my92xx::setState(bool state) {
_state = state;
}
void my92xx::update() {
_send();
}
// -----------------------------------------------------------------------------
my92xx::my92xx(my92xx_model_t model, unsigned char chips, unsigned char di, unsigned char dcki, my92xx_cmd_t command) : _command(command) {
_model = model;
_chips = chips;
_pin_di = di;
_pin_dcki = dcki;
// Init channels
if (_model == MY92XX_MODEL_MY9291) {
_channels = 4 * _chips;
}
else if (_model == MY92XX_MODEL_MY9231) {
_channels = 3 * _chips;
}
_value = new uint16_t[_channels];
for (unsigned char i = 0; i < _channels; i++) {
_value[i] = 0;
}
// Init GPIO
pinMode(_pin_di, OUTPUT);
pinMode(_pin_dcki, OUTPUT);
digitalWrite(_pin_di, LOW);
digitalWrite(_pin_dcki, LOW);
// Clear all duty register
_dcki_pulse(32 * _chips);
// Send init command
_set_cmd(command);
DEBUG_MSG_MY92XX("[MY92XX] Initialized\n");
}
#endif

View File

@@ -0,0 +1,45 @@
#pragma once
#include "wled.h"
#include "MY92xx.h"
#define MY92XX_MODEL MY92XX_MODEL_MY9291
#define MY92XX_CHIPS 1
#define MY92XX_DI_PIN 13
#define MY92XX_DCKI_PIN 15
#define MY92XX_RED 0
#define MY92XX_GREEN 1
#define MY92XX_BLUE 2
#define MY92XX_WHITE 3
class MY9291Usermod : public Usermod {
private:
my92xx _my92xx = my92xx(MY92XX_MODEL, MY92XX_CHIPS, MY92XX_DI_PIN, MY92XX_DCKI_PIN, MY92XX_COMMAND_DEFAULT);
public:
void setup() {
_my92xx.setState(true);
}
void connected() {
}
void loop() {
uint32_t c = strip.getPixelColor(0);
int w = ((c >> 24) & 0xff) * bri / 255.0;
int r = ((c >> 16) & 0xff) * bri / 255.0;
int g = ((c >> 8) & 0xff) * bri / 255.0;
int b = (c & 0xff) * bri / 255.0;
_my92xx.setChannel(MY92XX_RED, r);
_my92xx.setChannel(MY92XX_GREEN, g);
_my92xx.setChannel(MY92XX_BLUE, b);
_my92xx.setChannel(MY92XX_WHITE, w);
_my92xx.update();
}
uint16_t getId() {
return USERMOD_ID_MY9291;
}
};

View File

@@ -1,9 +0,0 @@
# PIR sensor with MQTT
This simple usermod allows attaching a PIR sensor like the AM312 and publish the readings over MQTT. A message is sent when motion is detected as well as when motion has stopped.
This usermod has only been tested with the AM312 sensor though should work for any other PIR sensor. Note that this does not control the LED strip directly, it only publishes MQTT readings for use with other integrations like Home Assistant.
## Installation
Copy and replace the file `usermod.cpp` in wled00 directory.

View File

@@ -1,55 +0,0 @@
#include "wled.h"
/*
* This v1 usermod file allows you to add own functionality to WLED more easily
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
* EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in const.h)
* If you just need 8 bytes, use 2551-2559 (you do not need to increase EEPSIZE)
*
* Consider the v2 usermod API if you need a more advanced feature set!
*/
//Use userVar0 and userVar1 (API calls &U0=,&U1=, uint16_t)
// PIR sensor pin
const int MOTION_PIN = 16;
// MQTT topic for sensor values
const char MQTT_TOPIC[] = "/motion";
int prevState = LOW;
//gets called once at boot. Do all initialization that doesn't depend on network here
void userSetup()
{
pinMode(MOTION_PIN, INPUT);
}
//gets called every time WiFi is (re-)connected. Initialize own network interfaces here
void userConnected()
{
}
void publishMqtt(String state)
{
//Check if MQTT Connected, otherwise it will crash the 8266
if (mqtt != nullptr){
char subuf[38];
strcpy(subuf, mqttDeviceTopic);
strcat(subuf, MQTT_TOPIC);
mqtt->publish(subuf, 0, true, state.c_str());
}
}
//loop. You can use "if (WLED_CONNECTED)" to check for successful connection
void userLoop()
{
if (digitalRead(MOTION_PIN) == HIGH && prevState == LOW) { // Motion detected
publishMqtt("ON");
prevState = HIGH;
}
if (digitalRead(MOTION_PIN) == LOW && prevState == HIGH) { // Motion stopped
publishMqtt("OFF");
prevState = LOW;
}
}

View File

@@ -61,8 +61,8 @@ class PIRsensorSwitch : public Usermod {
private:
// PIR sensor pin
const uint8_t PIRsensorPin = 13; // D7 on D1 mini
// notification mode for colorUpdated()
const byte NotifyUpdateMode = NOTIFIER_CALL_MODE_NO_NOTIFY; // NOTIFIER_CALL_MODE_DIRECT_CHANGE
// notification mode for stateUpdated()
const byte NotifyUpdateMode = CALL_MODE_NO_NOTIFY; // CALL_MODE_DIRECT_CHANGE
// 1 min delay before switch off after the sensor state goes LOW
uint32_t m_switchOffDelay = 60000;
// off timer start time
@@ -127,7 +127,7 @@ class PIRsensorSwitch : public Usermod {
if (bri != briHighlight) {
bri = briHighlight; // set current highlight brightness to last set highlight brightness
}
colorUpdated(NotifyUpdateMode);
stateUpdated(NotifyUpdateMode);
highlightActive = true; // flag highlight is on
}
else { // **pir timer has elapsed**
@@ -157,7 +157,7 @@ class PIRsensorSwitch : public Usermod {
}
applyMacro(macroLongPress); // apply standby lighting without brightness
}
colorUpdated(NotifyUpdateMode);
stateUpdated(NotifyUpdateMode);
highlightActive = false; // flag highlight is off
}
}

View File

@@ -9,31 +9,17 @@ The LED strip is switched [using a relay](https://github.com/Aircoookie/WLED/wik
## Webinterface
The info page in the web interface shows the items below
- the state of the sensor. By clicking on the state the sensor can be deactivated/activated. Changes persist after a reboot.
**I recommend to deactivate the sensor before an OTA update and activate it again afterwards**.
- the remaining time of the off timer.
## JSON API
The usermod supports the following state changes:
| JSON key | Value range | Description |
|------------|-------------|---------------------------------|
| PIRenabled | bool | Deactivdate/activate the sensor |
| PIRoffSec | 60 to 43200 | Off timer seconds |
Changes also persist after a reboot.
The info page in the web interface shows the remaining time of the off timer. Usermod can also be temporarily disbled/enabled from the info page by clicking PIR button.
## Sensor connection
My setup uses an HC-SR501 sensor, a HC-SR505 should also work.
My setup uses an HC-SR501 or HC-SR602 sensor, a HC-SR505 should also work.
The usermod uses GPIO13 (D1 mini pin D7) for the sensor signal.
The usermod uses GPIO13 (D1 mini pin D7) by default for the sensor signal but can be changed in the Usermod settings page.
[This example page](http://www.esp8266learning.com/wemos-mini-pir-sensor-example.php) describes how to connect the sensor.
Use the potentiometers on the sensor to set the time-delay to the minimum and the sensitivity to about half, or slightly above.
You can also use usermod's off timer instead of sensor's. In such case rotate the potentiometer to its shortest time possible (or use SR602 which lacks such potentiometer).
## Usermod installation
@@ -74,11 +60,14 @@ void registerUsermods()
}
```
**NOTE:** Usermod has been included in master branch of WLED so it can be compiled in directly just by defining `-D USERMOD_PIRSWITCH` and optionaly `-D PIR_SENSOR_PIN=16` to override default pin.
## API to enable/disable the PIR sensor from outside. For example from another usermod.
The class provides the static method `PIRsensorSwitch* PIRsensorSwitch::GetInstance()` to get a pointer to the usermod object.
To query or change the PIR sensor state the methods `bool PIRsensorEnabled()` and `void EnablePIRsensor(bool enable)` are available.
To query or change the PIR sensor state the methods `bool PIRsensorEnabled()` and `void EnablePIRsensor(bool enable)` are available.
When the PIR sensor state changes an MQTT message is broadcasted with topic `wled/deviceMAC/motion` and message `on` or `off`.
Usermod can also be configured to just send MQTT message and not change WLED state using settings page as well as responding to motion only during nighttime (assuming NTP and lattitude/longitude are set to determine sunrise/sunset times).
### There are two options to get access to the usermod instance:
@@ -98,12 +87,38 @@ class MyUsermod : public Usermod {
//...
void togglePIRSensor() {
if (PIRsensorSwitch::GetInstance() != nullptr) {
PIRsensorSwitch::GetInstance()->EnablePIRsensor(!PIRsensorSwitch::GetInstance()->PIRsensorEnabled());
#ifdef USERMOD_PIR_SENSOR_SWITCH
PIRsensorSwitch *PIRsensor = (PIRsensorSwitch::*) usermods.lookup(USERMOD_ID_PIRSWITCH);
if (PIRsensor != nullptr) {
PIRsensor->EnablePIRsensor(!PIRsensor->PIRsensorEnabled());
}
#endif
}
//...
};
```
Have fun - @gegu
### Configuration options
Usermod can be configured in Usermods settings page.
* `PIRenabled` - enable/disable usermod
* `pin` - dynamically change GPIO pin where PIR sensor is attached to ESP
* `PIRoffSec` - number of seconds after PIR sensor deactivates when usermod triggers Off preset (or turns WLED off)
* `on-preset` - preset triggered when PIR activates (if this is 0 it will just turn WLED on)
* `off-preset` - preset triggered when PIR deactivates (if this is 0 it will just turn WLED off)
* `nighttime-only` - enable triggering only between sunset and sunrise (you will need to set up _NTP_, _Lat_ & _Lon_ in Time & Macro settings)
* `mqtt-only` - only send MQTT messages, do not interact with WLED
* `off-only` - only trigger presets or turn WLED on/off in WLED is not already on (displaying effect)
* `notifications` - enable or disable sending notifications to other WLED instances using Sync button
Have fun - @gegu & @blazoncek
## Change log
2021-04
* Adaptation for runtime configuration.
2021-11
* Added information about dynamic configuration options
* Added option to temporary enable/disble usermod from WLED UI (Info dialog)

View File

@@ -2,6 +2,15 @@
#include "wled.h"
#ifndef PIR_SENSOR_PIN
// compatible with QuinLED-Dig-Uno
#ifdef ARDUINO_ARCH_ESP32
#define PIR_SENSOR_PIN 23 // Q4
#else //ESP8266 boards
#define PIR_SENSOR_PIN 13 // Q4 (D7 on D1 mini)
#endif
#endif
/*
* This usermod handles PIR sensor states.
* The strip will be switched on and the off timer will be resetted when the sensor goes HIGH.
@@ -30,80 +39,139 @@ public:
/**
* constructor
*/
PIRsensorSwitch()
{
// set static instance pointer
PIRsensorSwitchInstance(this);
}
PIRsensorSwitch() {}
/**
* desctructor
*/
~PIRsensorSwitch()
{
PIRsensorSwitchInstance(nullptr, true);
;
}
/**
* return the instance pointer of the class
*/
static PIRsensorSwitch *GetInstance() { return PIRsensorSwitchInstance(); }
~PIRsensorSwitch() {}
/**
* Enable/Disable the PIR sensor
*/
void EnablePIRsensor(bool enable) { m_PIRenabled = enable; }
void EnablePIRsensor(bool en) { enabled = en; }
/**
* Get PIR sensor enabled/disabled state
*/
bool PIRsensorEnabled() { return m_PIRenabled; }
bool PIRsensorEnabled() { return enabled; }
private:
// PIR sensor pin
const uint8_t PIRsensorPin = 13; // D7 on D1 mini
// notification mode for colorUpdated()
const byte NotifyUpdateMode = NOTIFIER_CALL_MODE_NO_NOTIFY; // NOTIFIER_CALL_MODE_DIRECT_CHANGE
// delay before switch off after the sensor state goes LOW
uint32_t m_switchOffDelay = 600000;
// off timer start time
uint32_t m_offTimerStart = 0;
// current PIR sensor pin state
byte m_PIRsensorPinState = LOW;
// PIR sensor enabled - ISR attached
bool m_PIRenabled = true;
// state if serializeConfig() should be called
bool m_updateConfig = false;
byte prevPreset = 0;
byte prevPlaylist = 0;
bool savedState = false;
uint32_t offTimerStart = 0; // off timer start time
byte NotifyUpdateMode = CALL_MODE_NO_NOTIFY; // notification mode for stateUpdated(): CALL_MODE_NO_NOTIFY or CALL_MODE_DIRECT_CHANGE
byte sensorPinState = LOW; // current PIR sensor pin state
bool initDone = false; // status of initialization
bool PIRtriggered = false;
unsigned long lastLoop = 0;
// configurable parameters
bool enabled = true; // PIR sensor enabled
int8_t PIRsensorPin = PIR_SENSOR_PIN; // PIR sensor pin
uint32_t m_switchOffDelay = 600000; // delay before switch off after the sensor state goes LOW (10min)
uint8_t m_onPreset = 0; // on preset
uint8_t m_offPreset = 0; // off preset
bool m_nightTimeOnly = false; // flag to indicate that PIR sensor should activate WLED during nighttime only
bool m_mqttOnly = false; // flag to send MQTT message only (assuming it is enabled)
// flag to enable triggering only if WLED is initially off (LEDs are not on, preventing running effect being overwritten by PIR)
bool m_offOnly = false;
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
static const char _switchOffDelay[];
static const char _enabled[];
static const char _onPreset[];
static const char _offPreset[];
static const char _nightTime[];
static const char _mqttOnly[];
static const char _offOnly[];
static const char _notify[];
/**
* return or change if new PIR sensor state is available
* check if it is daytime
* if sunrise/sunset is not defined (no NTP or lat/lon) default to nighttime
*/
static volatile bool newPIRsensorState(bool changeState = false, bool newState = false);
bool isDayTime() {
updateLocalTime();
uint8_t hr = hour(localTime);
uint8_t mi = minute(localTime);
/**
* PIR sensor state has changed
*/
static void IRAM_ATTR ISR_PIRstateChange();
/**
* Set/get instance pointer
*/
static PIRsensorSwitch *PIRsensorSwitchInstance(PIRsensorSwitch *pInstance = nullptr, bool bRemoveInstance = false);
if (sunrise && sunset) {
if (hour(sunrise)<hr && hour(sunset)>hr) {
return true;
} else {
if (hour(sunrise)==hr && minute(sunrise)<mi) {
return true;
}
if (hour(sunset)==hr && minute(sunset)>mi) {
return true;
}
}
}
return false;
}
/**
* switch strip on/off
*/
void switchStrip(bool switchOn)
{
if (switchOn && bri == 0)
{
bri = briLast;
colorUpdated(NotifyUpdateMode);
if (m_offOnly && bri && (switchOn || (!PIRtriggered && !switchOn))) return;
PIRtriggered = switchOn;
if (switchOn) {
if (m_onPreset) {
if (currentPlaylist>0) prevPlaylist = currentPlaylist;
else if (currentPreset>0) prevPreset = currentPreset;
else {
saveTemporaryPreset();
savedState = true;
prevPlaylist = 0;
prevPreset = 0;
}
applyPreset(m_onPreset, NotifyUpdateMode);
return;
}
// preset not assigned
if (bri == 0) {
bri = briLast;
stateUpdated(NotifyUpdateMode);
}
} else {
if (m_offPreset) {
applyPreset(m_offPreset, NotifyUpdateMode);
return;
} else if (prevPlaylist) {
applyPreset(prevPlaylist, NotifyUpdateMode);
prevPlaylist = 0;
return;
} else if (prevPreset) {
applyPreset(prevPreset, NotifyUpdateMode);
prevPreset = 0;
return;
} else if (savedState) {
applyTemporaryPreset();
savedState = false;
return;
}
// preset not assigned
if (bri != 0) {
briLast = bri;
bri = 0;
stateUpdated(NotifyUpdateMode);
}
}
else if (!switchOn && bri != 0)
{
briLast = bri;
bri = 0;
colorUpdated(NotifyUpdateMode);
}
void publishMqtt(const char* state)
{
//Check if MQTT Connected, otherwise it will crash the 8266
if (WLED_MQTT_CONNECTED){
char subuf[64];
strcpy(subuf, mqttDeviceTopic);
strcat_P(subuf, PSTR("/motion"));
mqtt->publish(subuf, 0, false, state);
}
}
@@ -113,21 +181,18 @@ private:
*/
bool updatePIRsensorState()
{
if (newPIRsensorState())
{
m_PIRsensorPinState = digitalRead(PIRsensorPin);
bool pinState = digitalRead(PIRsensorPin);
if (pinState != sensorPinState) {
sensorPinState = pinState; // change previous state
if (m_PIRsensorPinState == HIGH)
{
m_offTimerStart = 0;
switchStrip(true);
}
else if (bri != 0)
{
if (sensorPinState == HIGH) {
offTimerStart = 0;
if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(true);
publishMqtt("on");
} else /*if (bri != 0)*/ {
// start switch off timer
m_offTimerStart = millis();
offTimerStart = millis();
}
newPIRsensorState(true, false);
return true;
}
return false;
@@ -138,13 +203,14 @@ private:
*/
bool handleOffTimer()
{
if (m_offTimerStart > 0 && millis() - m_offTimerStart > m_switchOffDelay)
if (offTimerStart > 0 && millis() - offTimerStart > m_switchOffDelay)
{
if (m_PIRenabled == true)
if (enabled == true)
{
switchStrip(false);
if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(false);
publishMqtt("off");
}
m_offTimerStart = 0;
offTimerStart = 0;
return true;
}
return false;
@@ -159,13 +225,21 @@ public:
*/
void setup()
{
// PIR Sensor mode INPUT_PULLUP
pinMode(PIRsensorPin, INPUT_PULLUP);
if (m_PIRenabled)
{
// assign interrupt function and set CHANGE mode
attachInterrupt(digitalPinToInterrupt(PIRsensorPin), ISR_PIRstateChange, CHANGE);
if (enabled) {
// pin retrieved from cfg.json (readFromConfig()) prior to running setup()
if (PIRsensorPin >= 0 && pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) {
// PIR Sensor mode INPUT_PULLUP
pinMode(PIRsensorPin, INPUT_PULLUP);
sensorPinState = digitalRead(PIRsensorPin);
} else {
if (PIRsensorPin >= 0) {
DEBUG_PRINTLN(F("PIRSensorSwitch pin allocation failed."));
}
PIRsensorPin = -1; // allocation failed
enabled = false;
}
}
initDone = true;
}
/**
@@ -181,14 +255,12 @@ public:
*/
void loop()
{
if (!updatePIRsensorState())
{
// only check sensors 4x/s
if (!enabled || millis() - lastLoop < 250 || strip.isUpdating()) return;
lastLoop = millis();
if (!updatePIRsensorState()) {
handleOffTimer();
if (m_updateConfig)
{
serializeConfig();
m_updateConfig = false;
}
}
}
@@ -199,132 +271,163 @@ public:
*/
void addToJsonInfo(JsonObject &root)
{
//this code adds "u":{"&#x23F2; PIR sensor state":uiDomString} to the info object
// the value contains a button to toggle the sensor enabled/disabled
JsonObject user = root["u"];
if (user.isNull())
user = root.createNestedObject("u");
if (user.isNull()) user = root.createNestedObject("u");
JsonArray infoArr = user.createNestedArray("&#x23F2; PIR sensor state"); //name
String uiDomString = "<button class=\"btn infobtn\" onclick=\"requestJson({PIRenabled:";
String sensorStateInfo;
// PIR sensor state
if (m_PIRenabled)
{
uiDomString += "false";
sensorStateInfo = (m_PIRsensorPinState != LOW ? "active" : "inactive"); //value
String uiDomString = F("<button class=\"btn\" onclick=\"requestJson({");
uiDomString += FPSTR(_name);
uiDomString += F(":{");
uiDomString += FPSTR(_enabled);
if (enabled) {
uiDomString += F(":false}});\">");
uiDomString += F("PIR <i class=\"icons\">&#xe325;</i>");
} else {
uiDomString += F(":true}});\">");
uiDomString += F("PIR <i class=\"icons\">&#xe08f;</i>");
}
else
{
uiDomString += "true";
sensorStateInfo = "Disabled !";
}
uiDomString += "});return false;\">";
uiDomString += sensorStateInfo;
uiDomString += "</button>";
infoArr.add(uiDomString); //value
uiDomString += F("</button>");
JsonArray infoArr = user.createNestedArray(uiDomString); // timer value
//this code adds "u":{"&#x23F2; switch off timer":uiDomString} to the info object
uiDomString = "&#x23F2; switch off timer<span style=\"display:block;padding-left:25px;\">\
after <input type=\"number\" min=\"1\" max=\"720\" value=\"";
uiDomString += (m_switchOffDelay / 60000);
uiDomString += "\" onchange=\"requestJson({PIRoffSec:parseInt(this.value)*60});\">min</span>";
infoArr = user.createNestedArray(uiDomString); //name
// off timer
if (m_offTimerStart > 0)
{
uiDomString = "";
unsigned int offSeconds = (m_switchOffDelay - (millis() - m_offTimerStart)) / 1000;
if (offSeconds >= 3600)
if (enabled) {
if (offTimerStart > 0)
{
uiDomString += (offSeconds / 3600);
uiDomString += " hours ";
offSeconds %= 3600;
uiDomString = "";
unsigned int offSeconds = (m_switchOffDelay - (millis() - offTimerStart)) / 1000;
if (offSeconds >= 3600)
{
uiDomString += (offSeconds / 3600);
uiDomString += F("h ");
offSeconds %= 3600;
}
if (offSeconds >= 60)
{
uiDomString += (offSeconds / 60);
offSeconds %= 60;
}
else if (uiDomString.length() > 0)
{
uiDomString += 0;
}
if (uiDomString.length() > 0)
{
uiDomString += F("min ");
}
uiDomString += (offSeconds);
infoArr.add(uiDomString + F("s"));
} else {
infoArr.add(sensorPinState ? F("sensor on") : F("inactive"));
}
if (offSeconds >= 60)
{
uiDomString += (offSeconds / 60);
offSeconds %= 60;
}
else if (uiDomString.length() > 0)
{
uiDomString += 0;
}
if (uiDomString.length() > 0)
{
uiDomString += " min ";
}
uiDomString += (offSeconds);
infoArr.add(uiDomString + " sec");
}
else
{
infoArr.add("inactive");
} else {
infoArr.add(F("disabled"));
}
}
/**
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
* Add "PIRenabled" to json state. This can be used to disable/enable the sensor.
* Add "PIRoffSec" to json state. This can be used to adjust <m_switchOffDelay> milliseconds.
*/
/*
void addToJsonState(JsonObject &root)
{
root["PIRenabled"] = m_PIRenabled;
root["PIRoffSec"] = (m_switchOffDelay / 1000);
}
*/
/**
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
* Read "PIRenabled" from json state and switch enable/disable the PIR sensor.
* Read "PIRoffSec" from json state and adjust <m_switchOffDelay> milliseconds.
*/
void readFromJsonState(JsonObject &root)
{
if (root["PIRoffSec"] != nullptr)
{
m_switchOffDelay = (1000 * max(60UL, min(43200UL, root["PIRoffSec"].as<unsigned long>())));
m_updateConfig = true;
}
if (root["PIRenabled"] != nullptr)
{
if (root["PIRenabled"] && !m_PIRenabled)
{
attachInterrupt(digitalPinToInterrupt(PIRsensorPin), ISR_PIRstateChange, CHANGE);
newPIRsensorState(true, true);
if (!initDone) return; // prevent crash on boot applyPreset()
JsonObject usermod = root[FPSTR(_name)];
if (!usermod.isNull()) {
if (usermod[FPSTR(_enabled)].is<bool>()) {
enabled = usermod[FPSTR(_enabled)].as<bool>();
}
else if (m_PIRenabled)
{
detachInterrupt(PIRsensorPin);
}
m_PIRenabled = root["PIRenabled"];
m_updateConfig = true;
}
}
/**
* provide the changeable values
*/
void addToConfig(JsonObject &root)
{
JsonObject top = root.createNestedObject("PIRsensorSwitch");
top["PIRenabled"] = m_PIRenabled;
top["PIRoffSec"] = m_switchOffDelay;
JsonObject top = root.createNestedObject(FPSTR(_name));
top[FPSTR(_enabled)] = enabled;
top[FPSTR(_switchOffDelay)] = m_switchOffDelay / 1000;
top["pin"] = PIRsensorPin;
top[FPSTR(_onPreset)] = m_onPreset;
top[FPSTR(_offPreset)] = m_offPreset;
top[FPSTR(_nightTime)] = m_nightTimeOnly;
top[FPSTR(_mqttOnly)] = m_mqttOnly;
top[FPSTR(_offOnly)] = m_offOnly;
top[FPSTR(_notify)] = (NotifyUpdateMode != CALL_MODE_NO_NOTIFY);
DEBUG_PRINTLN(F("PIR config saved."));
}
/**
* restore the changeable values
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
*
* The function should return true if configuration was successfully loaded or false if there was no configuration.
*/
void readFromConfig(JsonObject &root)
bool readFromConfig(JsonObject &root)
{
JsonObject top = root["PIRsensorSwitch"];
m_PIRenabled = (top["PIRenabled"] != nullptr ? top["PIRenabled"] : true);
m_switchOffDelay = top["PIRoffSec"] | m_switchOffDelay;
bool oldEnabled = enabled;
int8_t oldPin = PIRsensorPin;
DEBUG_PRINT(FPSTR(_name));
JsonObject top = root[FPSTR(_name)];
if (top.isNull()) {
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false;
}
PIRsensorPin = top["pin"] | PIRsensorPin;
enabled = top[FPSTR(_enabled)] | enabled;
m_switchOffDelay = (top[FPSTR(_switchOffDelay)] | m_switchOffDelay/1000) * 1000;
m_onPreset = top[FPSTR(_onPreset)] | m_onPreset;
m_onPreset = max(0,min(250,(int)m_onPreset));
m_offPreset = top[FPSTR(_offPreset)] | m_offPreset;
m_offPreset = max(0,min(250,(int)m_offPreset));
m_nightTimeOnly = top[FPSTR(_nightTime)] | m_nightTimeOnly;
m_mqttOnly = top[FPSTR(_mqttOnly)] | m_mqttOnly;
m_offOnly = top[FPSTR(_offOnly)] | m_offOnly;
NotifyUpdateMode = top[FPSTR(_notify)] ? CALL_MODE_DIRECT_CHANGE : CALL_MODE_NO_NOTIFY;
if (!initDone) {
// reading config prior to setup()
DEBUG_PRINTLN(F(" config loaded."));
} else {
if (oldPin != PIRsensorPin || oldEnabled != enabled) {
// check if pin is OK
if (oldPin != PIRsensorPin && oldPin >= 0) {
// if we are changing pin in settings page
// deallocate old pin
pinManager.deallocatePin(oldPin, PinOwner::UM_PIR);
if (pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) {
pinMode(PIRsensorPin, INPUT_PULLUP);
} else {
// allocation failed
PIRsensorPin = -1;
enabled = false;
}
}
if (enabled) {
sensorPinState = digitalRead(PIRsensorPin);
}
}
DEBUG_PRINTLN(F(" config (re)loaded."));
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[FPSTR(_notify)].isNull();
}
/**
@@ -337,30 +440,13 @@ after <input type=\"number\" min=\"1\" max=\"720\" value=\"";
}
};
//////////////////////////////////////////////////////
// PIRsensorSwitch static method implementations
volatile bool PIRsensorSwitch::newPIRsensorState(bool changeState, bool newState)
{
static volatile bool s_PIRsensorState = false;
if (changeState)
{
s_PIRsensorState = newState;
}
return s_PIRsensorState;
}
void IRAM_ATTR PIRsensorSwitch::ISR_PIRstateChange()
{
newPIRsensorState(true, true);
}
PIRsensorSwitch *PIRsensorSwitch::PIRsensorSwitchInstance(PIRsensorSwitch *pInstance, bool bRemoveInstance)
{
static PIRsensorSwitch *s_pPIRsensorSwitch = nullptr;
if (pInstance != nullptr || bRemoveInstance)
{
s_pPIRsensorSwitch = pInstance;
}
return s_pPIRsensorSwitch;
}
// strings to reduce flash memory usage (used more than twice)
const char PIRsensorSwitch::_name[] PROGMEM = "PIRsensorSwitch";
const char PIRsensorSwitch::_enabled[] PROGMEM = "PIRenabled";
const char PIRsensorSwitch::_switchOffDelay[] PROGMEM = "PIRoffSec";
const char PIRsensorSwitch::_onPreset[] PROGMEM = "on-preset";
const char PIRsensorSwitch::_offPreset[] PROGMEM = "off-preset";
const char PIRsensorSwitch::_nightTime[] PROGMEM = "nighttime-only";
const char PIRsensorSwitch::_mqttOnly[] PROGMEM = "mqtt-only";
const char PIRsensorSwitch::_offOnly[] PROGMEM = "off-only";
const char PIRsensorSwitch::_notify[] PROGMEM = "notifications";

View File

@@ -0,0 +1,36 @@
# PWM fan
v2 Usermod to to control PWM fan with RPM feedback and temperature control
This usermod requires Dallas Temperature usermod to obtain temperature information. If this is not available the fan will always run at 100% speed.
If the fan does not have _tacho_ (RPM) output you can set the _tacho-pin_ to -1 to not use that feature.
You can also set the thershold temperature at which fan runs at lowest speed. If the actual temperature measured will be 3°C greater than threshold temperature the fan will run at 100%.
If the _tacho_ is supported the current speed (in RPM) will be repored in WLED Info page.
## Installation
Add the compile-time option `-D USERMOD_PWM_FAN` to your `platformio.ini` (or `platformio_override.ini`) or use `#define USERMOD_PWM_FAN` in `myconfig.h`.
You will also need `-D USERMOD_DALLASTEMPERATURE`.
### Define Your Options
All of the parameters are configured during run-time using Usermods settings page.
This includes:
* PWM output pin (can be configured at compile time `-D PWM_PIN=xx`)
* tacho input pin (can be configured at compile time `-D TACHO_PIN=xx`)
* sampling frequency in seconds
* threshold temperature in degees C
_NOTE:_ You may also need to tweak Dallas Temperature usermod sampling frequency to match PWM fan sampling frequency.
### PlatformIO requirements
No special requirements.
## Change Log
2021-10
* First public release

View File

@@ -0,0 +1,339 @@
#pragma once
#ifndef USERMOD_DALLASTEMPERATURE
#error The "PWM fan" usermod requires "Dallas Temeprature" usermod to function properly.
#endif
#include "wled.h"
// PWM & tacho code curtesy of @KlausMu
// https://github.com/KlausMu/esp32-fan-controller/tree/main/src
// adapted for WLED usermod by @blazoncek
#ifndef TACHO_PIN
#define TACHO_PIN -1
#endif
#ifndef PWM_PIN
#define PWM_PIN -1
#endif
// tacho counter
static volatile unsigned long counter_rpm = 0;
// Interrupt counting every rotation of the fan
// https://desire.giesecke.tk/index.php/2018/01/30/change-global-variables-from-isr/
static void IRAM_ATTR rpm_fan() {
counter_rpm++;
}
class PWMFanUsermod : public Usermod {
private:
bool initDone = false;
bool enabled = true;
unsigned long msLastTachoMeasurement = 0;
uint16_t last_rpm = 0;
#ifdef ARDUINO_ARCH_ESP32
uint8_t pwmChannel = 255;
#endif
#ifdef USERMOD_DALLASTEMPERATURE
UsermodTemperature* tempUM;
#endif
// configurable parameters
int8_t tachoPin = TACHO_PIN;
int8_t pwmPin = PWM_PIN;
uint8_t tachoUpdateSec = 30;
float targetTemperature = 25.0;
uint8_t minPWMValuePct = 50;
uint8_t numberOfInterrupsInOneSingleRotation = 2; // Number of interrupts ESP32 sees on tacho signal on a single fan rotation. All the fans I've seen trigger two interrups.
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
static const char _enabled[];
static const char _tachoPin[];
static const char _pwmPin[];
static const char _temperature[];
static const char _tachoUpdateSec[];
static const char _minPWMValuePct[];
static const char _IRQperRotation[];
void initTacho(void) {
if (tachoPin < 0 || !pinManager.allocatePin(tachoPin, false, PinOwner::UM_Unspecified)){
tachoPin = -1;
return;
}
pinMode(tachoPin, INPUT);
digitalWrite(tachoPin, HIGH);
attachInterrupt(digitalPinToInterrupt(tachoPin), rpm_fan, FALLING);
DEBUG_PRINTLN(F("Tacho sucessfully initialized."));
}
void deinitTacho(void) {
if (tachoPin < 0) return;
detachInterrupt(digitalPinToInterrupt(tachoPin));
pinManager.deallocatePin(tachoPin, PinOwner::UM_Unspecified);
tachoPin = -1;
}
void updateTacho(void) {
if (tachoPin < 0) return;
// start of tacho measurement
// detach interrupt while calculating rpm
detachInterrupt(digitalPinToInterrupt(tachoPin));
// calculate rpm
last_rpm = (counter_rpm * 60) / numberOfInterrupsInOneSingleRotation;
last_rpm /= tachoUpdateSec;
// reset counter
counter_rpm = 0;
// store milliseconds when tacho was measured the last time
msLastTachoMeasurement = millis();
// attach interrupt again
attachInterrupt(digitalPinToInterrupt(tachoPin), rpm_fan, FALLING);
}
// https://randomnerdtutorials.com/esp32-pwm-arduino-ide/
void initPWMfan(void) {
if (pwmPin < 0 || !pinManager.allocatePin(pwmPin, true, PinOwner::UM_Unspecified)) {
pwmPin = -1;
return;
}
#ifdef ESP8266
analogWriteRange(255);
analogWriteFreq(WLED_PWM_FREQ);
#else
pwmChannel = pinManager.allocateLedc(1);
if (pwmChannel == 255) { //no more free LEDC channels
deinitPWMfan(); return;
}
// configure LED PWM functionalitites
ledcSetup(pwmChannel, 25000, 8);
// attach the channel to the GPIO to be controlled
ledcAttachPin(pwmPin, pwmChannel);
#endif
DEBUG_PRINTLN(F("Fan PWM sucessfully initialized."));
}
void deinitPWMfan(void) {
if (pwmPin < 0) return;
pinManager.deallocatePin(pwmPin, PinOwner::UM_Unspecified);
#ifdef ARDUINO_ARCH_ESP32
pinManager.deallocateLedc(pwmChannel, 1);
#endif
pwmPin = -1;
}
void updateFanSpeed(uint8_t pwmValue){
if (pwmPin < 0) return;
#ifdef ESP8266
analogWrite(pwmPin, pwmValue);
#else
ledcWrite(pwmChannel, pwmValue);
#endif
}
float getActualTemperature(void) {
#ifdef USERMOD_DALLASTEMPERATURE
if (tempUM != nullptr)
return tempUM->getTemperatureC();
#endif
return -127.0f;
}
void setFanPWMbasedOnTemperature(void) {
float temp = getActualTemperature();
float difftemp = temp - targetTemperature;
// Default to run fan at full speed.
int newPWMvalue = 255;
int pwmStep = ((100 - minPWMValuePct) * newPWMvalue) / (7*100);
int pwmMinimumValue = (minPWMValuePct * newPWMvalue) / 100;
if ((temp == NAN) || (temp <= 0.0)) {
DEBUG_PRINTLN(F("WARNING: no temperature value available. Cannot do temperature control. Will set PWM fan to 255."));
} else if (difftemp <= 0.0) {
// Temperature is below target temperature. Run fan at minimum speed.
newPWMvalue = pwmMinimumValue;
} else if (difftemp <= 0.5) {
newPWMvalue = pwmMinimumValue + pwmStep;
} else if (difftemp <= 1.0) {
newPWMvalue = pwmMinimumValue + 2*pwmStep;
} else if (difftemp <= 1.5) {
newPWMvalue = pwmMinimumValue + 3*pwmStep;
} else if (difftemp <= 2.0) {
newPWMvalue = pwmMinimumValue + 4*pwmStep;
} else if (difftemp <= 2.5) {
newPWMvalue = pwmMinimumValue + 5*pwmStep;
} else if (difftemp <= 3.0) {
newPWMvalue = pwmMinimumValue + 6*pwmStep;
}
updateFanSpeed(newPWMvalue);
}
public:
// gets called once at boot. Do all initialization that doesn't depend on
// network here
void setup() {
#ifdef USERMOD_DALLASTEMPERATURE
// This Usermod requires Temperature usermod
tempUM = (UsermodTemperature*) usermods.lookup(USERMOD_ID_TEMPERATURE);
#endif
initTacho();
initPWMfan();
updateFanSpeed((minPWMValuePct * 255) / 100); // inital fan speed
initDone = true;
}
// gets called every time WiFi is (re-)connected. Initialize own network
// interfaces here
void connected() {}
/*
* Da loop.
*/
void loop() {
if (!enabled || strip.isUpdating()) return;
unsigned long now = millis();
if ((now - msLastTachoMeasurement) < (tachoUpdateSec * 1000)) return;
updateTacho();
setFanPWMbasedOnTemperature();
}
/*
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
* Below it is shown how this could be used for e.g. a light sensor
*/
void addToJsonInfo(JsonObject& root) {
if (tachoPin < 0) return;
JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u");
JsonArray data = user.createNestedArray(FPSTR(_name));
data.add(last_rpm);
data.add(F("rpm"));
}
/*
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
//void addToJsonState(JsonObject& root) {
//}
/*
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
//void readFromJsonState(JsonObject& root) {
// if (!initDone) return; // prevent crash on boot applyPreset()
//}
/*
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
* If you want to force saving the current state, use serializeConfig() in your loop().
*
* CAUTION: serializeConfig() will initiate a filesystem write operation.
* It might cause the LEDs to stutter and will cause flash wear if called too often.
* Use it sparingly and always in the loop, never in network callbacks!
*
* addToConfig() will also not yet add your setting to one of the settings pages automatically.
* To make that work you still have to add the setting to the HTML, xml.cpp and set.cpp manually.
*
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
*/
void addToConfig(JsonObject& root) {
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
top[FPSTR(_enabled)] = enabled;
top[FPSTR(_pwmPin)] = pwmPin;
top[FPSTR(_tachoPin)] = tachoPin;
top[FPSTR(_tachoUpdateSec)] = tachoUpdateSec;
top[FPSTR(_temperature)] = targetTemperature;
top[FPSTR(_minPWMValuePct)] = minPWMValuePct;
top[FPSTR(_IRQperRotation)] = numberOfInterrupsInOneSingleRotation;
DEBUG_PRINTLN(F("Autosave config saved."));
}
/*
* readFromConfig() can be used to read back the custom settings you added with addToConfig().
* This is called by WLED when settings are loaded (currently this only happens once immediately after boot)
*
* readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes),
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
*
* The function should return true if configuration was successfully loaded or false if there was no configuration.
*/
bool readFromConfig(JsonObject& root) {
int8_t newTachoPin = tachoPin;
int8_t newPwmPin = pwmPin;
JsonObject top = root[FPSTR(_name)];
DEBUG_PRINT(FPSTR(_name));
if (top.isNull()) {
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false;
}
enabled = top[FPSTR(_enabled)] | enabled;
newTachoPin = top[FPSTR(_tachoPin)] | newTachoPin;
newPwmPin = top[FPSTR(_pwmPin)] | newPwmPin;
tachoUpdateSec = top[FPSTR(_tachoUpdateSec)] | tachoUpdateSec;
tachoUpdateSec = (uint8_t) max(1,(int)tachoUpdateSec); // bounds checking
targetTemperature = top[FPSTR(_temperature)] | targetTemperature;
minPWMValuePct = top[FPSTR(_minPWMValuePct)] | minPWMValuePct;
minPWMValuePct = (uint8_t) min(100,max(0,(int)minPWMValuePct)); // bounds checking
numberOfInterrupsInOneSingleRotation = top[FPSTR(_IRQperRotation)] | numberOfInterrupsInOneSingleRotation;
numberOfInterrupsInOneSingleRotation = (uint8_t) max(1,(int)numberOfInterrupsInOneSingleRotation); // bounds checking
if (!initDone) {
// first run: reading from cfg.json
tachoPin = newTachoPin;
pwmPin = newPwmPin;
DEBUG_PRINTLN(F(" config loaded."));
} else {
DEBUG_PRINTLN(F(" config (re)loaded."));
// changing paramters from settings page
if (tachoPin != newTachoPin || pwmPin != newPwmPin) {
DEBUG_PRINTLN(F("Re-init pins."));
// deallocate pin and release interrupts
deinitTacho();
deinitPWMfan();
tachoPin = newTachoPin;
pwmPin = newPwmPin;
// initialise
setup();
}
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[FPSTR(_IRQperRotation)].isNull();
}
/*
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
* This could be used in the future for the system to determine whether your usermod is installed.
*/
uint16_t getId() {
return USERMOD_ID_PWM_FAN;
}
};
// strings to reduce flash memory usage (used more than twice)
const char PWMFanUsermod::_name[] PROGMEM = "PWM-fan";
const char PWMFanUsermod::_enabled[] PROGMEM = "enabled";
const char PWMFanUsermod::_tachoPin[] PROGMEM = "tacho-pin";
const char PWMFanUsermod::_pwmPin[] PROGMEM = "PWM-pin";
const char PWMFanUsermod::_temperature[] PROGMEM = "target-temp-C";
const char PWMFanUsermod::_tachoUpdateSec[] PROGMEM = "tacho-update-s";
const char PWMFanUsermod::_minPWMValuePct[] PROGMEM = "min-PWM-percent";
const char PWMFanUsermod::_IRQperRotation[] PROGMEM = "IRQs-per-rotation";

8
usermods/RTC/readme.md Normal file
View File

@@ -0,0 +1,8 @@
# DS1307/DS3231 Real time clock
Gets the time from I2C RTC module on boot. This allows clocks to operate e.g. if temporarily no WiFi is available.
The stored time is updated each time NTP is synced.
## Installation
Add the build flag `-D USERMOD_RTC` to your platformio environment.

View File

@@ -0,0 +1,59 @@
#pragma once
#include "src/dependencies/time/DS1307RTC.h"
#include "wled.h"
#ifdef ARDUINO_ARCH_ESP32
#define HW_PIN_SCL 22
#define HW_PIN_SDA 21
#else
#define HW_PIN_SCL 5
#define HW_PIN_SDA 4
#endif
//Connect DS1307 to standard I2C pins (ESP32: GPIO 21 (SDA)/GPIO 22 (SCL))
class RTCUsermod : public Usermod {
private:
unsigned long lastTime = 0;
bool disabled = false;
public:
void setup() {
PinManagerPinType pins[2] = { { HW_PIN_SCL, true }, { HW_PIN_SDA, true } };
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { disabled = true; return; }
time_t rtcTime = RTC.get();
if (rtcTime) {
toki.setTime(rtcTime,TOKI_NO_MS_ACCURACY,TOKI_TS_RTC);
updateLocalTime();
} else {
if (!RTC.chipPresent()) disabled = true; //don't waste time if H/W error
}
}
void loop() {
if (strip.isUpdating()) return;
if (!disabled && toki.isTick()) {
time_t t = toki.second();
if (t != RTC.get()) RTC.set(t); //set RTC to NTP/UI-provided value
}
}
/*
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
*/
void addToConfig(JsonObject& root)
{
JsonObject top = root.createNestedObject("RTC");
JsonArray pins = top.createNestedArray("pin");
pins.add(HW_PIN_SCL);
pins.add(HW_PIN_SDA);
}
uint16_t getId()
{
return USERMOD_ID_RTC;
}
};

View File

@@ -0,0 +1,76 @@
<!DOCTYPE html>
<html>
<head><meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
<meta charset="utf-8">
<title>Blinds</title>
<script>
strA = "";
function send()
{
nocache = "&nocache=" + Math.random() * 1000000;
var request = new XMLHttpRequest();
// send HTTP request
request.open("GET", "win/" + strA +nocache, true);
request.send(null);
strA = "";
}
function up()
{
strA = "&U0=2";
send();
}
function down()
{
strA = "&U0=1";
send();
}
function OpenSettings()
{
window.open("/settings", "_self");
}
</script>
<style>
body {
text-align: center;
background: linear-gradient(45deg,#0ca,#0ac);
height: 100%;
margin: 0;
background-repeat: no-repeat;
background-attachment: fixed;
}
html {
height: 100%;
}
svg {
width: 30vw;
padding: 2vh;
}
.tool_box {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>
<style id="holderjs-style" type="text/css"></style></head>
<body class=" __plain_text_READY__">
<svg style="position: absolute; width: 0; height: 0; overflow: hidden;" version="1.1" xmlns="http://www.w3.org/2000/svg">
<defs>
<symbol id="icon-box-add" viewBox="0 0 32 32">
<path d="M26 2h-20l-6 6v21c0 0.552 0.448 1 1 1h30c0.552 0 1-0.448 1-1v-21l-6-6zM16 26l-10-8h6v-6h8v6h6l-10 8zM4.828 6l2-2h18.343l2 2h-22.343z"></path>
</symbol>
<symbol id="icon-box-remove" viewBox="0 0 32 32">
<path d="M26 2h-20l-6 6v21c0 0.552 0.448 1 1 1h30c0.552 0 1-0.448 1-1v-21l-6-6zM20 20v6h-8v-6h-6l10-8 10 8h-6zM4.828 6l2-2h18.343l2 2h-22.343z"></path>
</symbol>
<symbol id="icon-cog" viewBox="0 0 32 32">
<path d="M29.181 19.070c-1.679-2.908-0.669-6.634 2.255-8.328l-3.145-5.447c-0.898 0.527-1.943 0.829-3.058 0.829-3.361 0-6.085-2.742-6.085-6.125h-6.289c0.008 1.044-0.252 2.103-0.811 3.070-1.679 2.908-5.411 3.897-8.339 2.211l-3.144 5.447c0.905 0.515 1.689 1.268 2.246 2.234 1.676 2.903 0.672 6.623-2.241 8.319l3.145 5.447c0.895-0.522 1.935-0.82 3.044-0.82 3.35 0 6.067 2.725 6.084 6.092h6.289c-0.003-1.034 0.259-2.080 0.811-3.038 1.676-2.903 5.399-3.894 8.325-2.219l3.145-5.447c-0.899-0.515-1.678-1.266-2.232-2.226zM16 22.479c-3.578 0-6.479-2.901-6.479-6.479s2.901-6.479 6.479-6.479c3.578 0 6.479 2.901 6.479 6.479s-2.901 6.479-6.479 6.479z"></path>
</symbol>
</defs>
</svg>
<div id="tbB" class="tool_box">
<svg id="upb" onclick="up()"><use xlink:href="#icon-box-remove"></use></svg>
<svg id="dnb" onclick="down()"><use xlink:href="#icon-box-add"></use></svg>
<svg id="stb" onclick="OpenSettings()"><use xlink:href="#icon-cog"></use></svg>
</div>
</body>
</html>

View File

@@ -0,0 +1 @@
{"0":{},"2":{"n":"▲","win":"U0=2"},"1":{"n":"▼","win":"U0=1"}}

View File

@@ -0,0 +1,8 @@
# RelayBlinds usermod
This simple usermod toggles two relay pins momentarily (default for 500ms) when `userVar0` is set.
This can be used to e.g. "push" the buttons of a window blinds motor controller.
v1 usermod. Please replace usermod.cpp in the `wled00` directory with the one in this file.
You may upload `index.htm` to `[WLED-IP]/edit` to replace the default lighting UI with a simple Up/Down button one.
Also, a simple `presets.json` file is available, this makes the relay actions controllable via two presets to facilitate control e.g. via the default UI or Alexa.

View File

@@ -0,0 +1,83 @@
#include "wled.h"
//Use userVar0 and userVar1 (API calls &U0=,&U1=, uint16_t)
//gets called once at boot. Do all initialization that doesn't depend on network here
void userSetup()
{
}
//gets called every time WiFi is (re-)connected. Initialize own network interfaces here
void userConnected()
{
}
/*
* Physical IO
*/
#define PIN_UP_RELAY 4
#define PIN_DN_RELAY 5
#define PIN_ON_TIME 500
bool upActive = false, upActiveBefore = false, downActive = false, downActiveBefore = false;
unsigned long upStartTime = 0, downStartTime = 0;
void handleRelay()
{
//up and down relays
if (userVar0) {
upActive = true;
if (userVar0 == 1) {
upActive = false;
downActive = true;
}
userVar0 = 0;
}
if (upActive)
{
if(!upActiveBefore)
{
pinMode(PIN_UP_RELAY, OUTPUT);
digitalWrite(PIN_UP_RELAY, LOW);
upActiveBefore = true;
upStartTime = millis();
DEBUG_PRINTLN("UPA");
}
if (millis()- upStartTime > PIN_ON_TIME)
{
upActive = false;
DEBUG_PRINTLN("UPN");
}
} else if (upActiveBefore)
{
pinMode(PIN_UP_RELAY, INPUT);
upActiveBefore = false;
}
if (downActive)
{
if(!downActiveBefore)
{
pinMode(PIN_DN_RELAY, OUTPUT);
digitalWrite(PIN_DN_RELAY, LOW);
downActiveBefore = true;
downStartTime = millis();
}
if (millis()- downStartTime > PIN_ON_TIME)
{
downActive = false;
}
} else if (downActiveBefore)
{
pinMode(PIN_DN_RELAY, INPUT);
downActiveBefore = false;
}
}
//loop. You can use "if (WLED_CONNECTED)" to check for successful connection
void userLoop()
{
handleRelay();
}

View File

@@ -0,0 +1,16 @@
; Options
; -------
; USERMOD_SN_PHOTORESISTOR - define this to have this user mod included wled00\usermods_list.cpp
; USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL - the number of milliseconds between measurements, defaults to 60 seconds
; USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT - the number of milliseconds after boot to take first measurement, defaults to 20 seconds
; USERMOD_SN_PHOTORESISTOR_REFERENCE_VOLTAGE - the voltage supplied to the sensor, defaults to 5v
; USERMOD_SN_PHOTORESISTOR_ADC_PRECISION - the ADC precision is the number of distinguishable ADC inputs, defaults to 1024.0 (10 bits)
; USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE - the resistor size, defaults to 10000.0 (10K hms)
; USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE - the offset value to report on, defaults to 25
;
[env:usermod_sn_photoresistor_d1_mini]
extends = env:d1_mini
build_flags =
${common.build_flags_esp8266}
-D USERMOD_SN_PHOTORESISTOR
lib_deps = ${env.lib_deps}

View File

@@ -0,0 +1,30 @@
# SN_Photoresistor usermod
This usermod will read from an attached photoresistor sensor like the KY-018 sensor.
The luminance is displayed both in the Info section of the web UI as well as published to the `/luminance` MQTT topic if enabled.
## Installation
Copy the example `platformio_override.ini` to the root directory. This file should be placed in the same directory as `platformio.ini`.
### Define Your Options
* `USERMOD_SN_PHOTORESISTOR` - define this to have this user mod included wled00\usermods_list.cpp
* `USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL` - the number of milliseconds between measurements, defaults to 60 seconds
* `USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT` - the number of milliseconds after boot to take first measurement, defaults to 20 seconds
* `USERMOD_SN_PHOTORESISTOR_REFERENCE_VOLTAGE` - the voltage supplied to the sensor, defaults to 5v
* `USERMOD_SN_PHOTORESISTOR_ADC_PRECISION` - the ADC precision is the number of distinguishable ADC inputs, defaults to 1024.0 (10 bits)
* `USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE` - the resistor size, defaults to 10000.0 (10K hms)
* `USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE` - the offset value to report on, defaults to 25
All parameters can be configured at runtime using Usermods settings page.
## Project link
* [QuinLED-Dig-Uno](https://quinled.info/2018/09/15/quinled-dig-uno/) - Project link
### PlatformIO requirements
If you are using `platformio_override.ini`, you should be able to refresh the task list and see your custom task, for example `env:usermod_sn_photoresistor_d1_mini`.
## Change Log

View File

@@ -0,0 +1,208 @@
#pragma once
#include "wled.h"
//Pin defaults for QuinLed Dig-Uno (A0)
#define PHOTORESISTOR_PIN A0
// the frequency to check photoresistor, 10 seconds
#ifndef USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL
#define USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL 10000
#endif
// how many seconds after boot to take first measurement, 10 seconds
#ifndef USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT
#define USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT 10000
#endif
// supplied voltage
#ifndef USERMOD_SN_PHOTORESISTOR_REFERENCE_VOLTAGE
#define USERMOD_SN_PHOTORESISTOR_REFERENCE_VOLTAGE 5
#endif
// 10 bits
#ifndef USERMOD_SN_PHOTORESISTOR_ADC_PRECISION
#define USERMOD_SN_PHOTORESISTOR_ADC_PRECISION 1024.0f
#endif
// resistor size 10K hms
#ifndef USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE
#define USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE 10000.0f
#endif
// only report if differance grater than offset value
#ifndef USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE
#define USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE 5
#endif
class Usermod_SN_Photoresistor : public Usermod
{
private:
float referenceVoltage = USERMOD_SN_PHOTORESISTOR_REFERENCE_VOLTAGE;
float resistorValue = USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE;
float adcPrecision = USERMOD_SN_PHOTORESISTOR_ADC_PRECISION;
int8_t offset = USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE;
unsigned long readingInterval = USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL;
// set last reading as "40 sec before boot", so first reading is taken after 20 sec
unsigned long lastMeasurement = UINT32_MAX - (USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL - USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT);
// flag to indicate we have finished the first getTemperature call
// allows this library to report to the user how long until the first
// measurement
bool getLuminanceComplete = false;
uint16_t lastLDRValue = -1000;
// flag set at startup
bool disabled = false;
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
static const char _enabled[];
static const char _readInterval[];
static const char _referenceVoltage[];
static const char _resistorValue[];
static const char _adcPrecision[];
static const char _offset[];
bool checkBoundSensor(float newValue, float prevValue, float maxDiff)
{
return isnan(prevValue) || newValue <= prevValue - maxDiff || newValue >= prevValue + maxDiff;
}
uint16_t getLuminance()
{
// http://forum.arduino.cc/index.php?topic=37555.0
// https://forum.arduino.cc/index.php?topic=185158.0
float volts = analogRead(PHOTORESISTOR_PIN) * (referenceVoltage / adcPrecision);
float amps = volts / resistorValue;
float lux = amps * 1000000 * 2.0;
lastMeasurement = millis();
getLuminanceComplete = true;
return uint16_t(lux);
}
public:
void setup()
{
// set pinmode
pinMode(PHOTORESISTOR_PIN, INPUT);
}
void loop()
{
if (disabled || strip.isUpdating())
return;
unsigned long now = millis();
// check to see if we are due for taking a measurement
// lastMeasurement will not be updated until the conversion
// is complete the the reading is finished
if (now - lastMeasurement < readingInterval)
{
return;
}
uint16_t currentLDRValue = getLuminance();
if (checkBoundSensor(currentLDRValue, lastLDRValue, offset))
{
lastLDRValue = currentLDRValue;
if (WLED_MQTT_CONNECTED)
{
char subuf[45];
strcpy(subuf, mqttDeviceTopic);
strcat_P(subuf, PSTR("/luminance"));
mqtt->publish(subuf, 0, true, String(lastLDRValue).c_str());
}
else
{
DEBUG_PRINTLN("Missing MQTT connection. Not publishing data");
}
}
}
uint16_t getLastLDRValue()
{
return lastLDRValue;
}
void addToJsonInfo(JsonObject &root)
{
JsonObject user = root[F("u")];
if (user.isNull())
user = root.createNestedObject(F("u"));
JsonArray lux = user.createNestedArray(F("Luminance"));
if (!getLuminanceComplete)
{
// if we haven't read the sensor yet, let the user know
// that we are still waiting for the first measurement
lux.add((USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT - millis()) / 1000);
lux.add(F(" sec until read"));
return;
}
lux.add(lastLDRValue);
lux.add(F(" lux"));
}
uint16_t getId()
{
return USERMOD_ID_SN_PHOTORESISTOR;
}
/**
* addToConfig() (called from set.cpp) stores persistent properties to cfg.json
*/
void addToConfig(JsonObject &root)
{
// we add JSON object.
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
top[FPSTR(_enabled)] = !disabled;
top[FPSTR(_readInterval)] = readingInterval / 1000;
top[FPSTR(_referenceVoltage)] = referenceVoltage;
top[FPSTR(_resistorValue)] = resistorValue;
top[FPSTR(_adcPrecision)] = adcPrecision;
top[FPSTR(_offset)] = offset;
DEBUG_PRINTLN(F("Photoresistor config saved."));
}
/**
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
*/
bool readFromConfig(JsonObject &root)
{
// we look for JSON object.
JsonObject top = root[FPSTR(_name)];
if (top.isNull()) {
DEBUG_PRINT(FPSTR(_name));
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false;
}
disabled = !(top[FPSTR(_enabled)] | !disabled);
readingInterval = (top[FPSTR(_readInterval)] | readingInterval/1000) * 1000; // convert to ms
referenceVoltage = top[FPSTR(_referenceVoltage)] | referenceVoltage;
resistorValue = top[FPSTR(_resistorValue)] | resistorValue;
adcPrecision = top[FPSTR(_adcPrecision)] | adcPrecision;
offset = top[FPSTR(_offset)] | offset;
DEBUG_PRINT(FPSTR(_name));
DEBUG_PRINTLN(F(" config (re)loaded."));
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return true;
}
};
// strings to reduce flash memory usage (used more than twice)
const char Usermod_SN_Photoresistor::_name[] PROGMEM = "Photoresistor";
const char Usermod_SN_Photoresistor::_enabled[] PROGMEM = "enabled";
const char Usermod_SN_Photoresistor::_readInterval[] PROGMEM = "read-interval-s";
const char Usermod_SN_Photoresistor::_referenceVoltage[] PROGMEM = "supplied-voltage";
const char Usermod_SN_Photoresistor::_resistorValue[] PROGMEM = "resistor-value";
const char Usermod_SN_Photoresistor::_adcPrecision[] PROGMEM = "adc-precision";
const char Usermod_SN_Photoresistor::_offset[] PROGMEM = "offset";

View File

@@ -0,0 +1,14 @@
#include "wled.h"
/*
* Register your v2 usermods here!
*/
#ifdef USERMOD_SN_PHOTORESISTOR
#include "../usermods/SN_Photoresistor/usermod_sn_photoresistor.h"
#endif
void registerUsermods()
{
#ifdef USERMOD_SN_PHOTORESISTOR
usermods.add(new Usermod_SN_Photoresistor());
#endif
}

View File

@@ -0,0 +1,72 @@
# ST7789 TFT IPS Color display 240x240pxwith ESP32 boards
This usermod allow to use 240x240 display to display following:
* Network SSID;
* IP address;
* Brightness;
* Chosen effect;
* Chosen palette;
* Estimated current in mA;
## Hardware
***
![Hardware](images/ST7789_Guide.jpg)
## Library used
[Bodmer/TFT_eSPI](https://github.com/Bodmer/TFT_eSPI)
## Setup
***
### Platformio.ini changes
In the `platformio.ini` file, uncomment the `TFT_eSPI` line within the [common] section, under `lib_deps`:
```ini
# platformio.ini
...
[common]
...
lib_deps =
...
#For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line
#TFT_eSPI
...
```
Also, while in the `platformio.ini` file, you must change the environment setup to build for just the esp32dev platform as follows:
Add lines to section:
```ini
default_envs = esp32dev
build_flags = ${common.build_flags_esp32}
-D USERMOD_ST7789_DISPLAY
```
Save the `platformio.ini` file. Once this is saved, the required library files should be automatically downloaded for modifications in a later step.
### TFT_eSPI Library Adjustments
We need to modify a file in the `TFT_eSPI` library. If you followed the directions to modify and save the `platformio.ini` file above, the `User_Setup_Select.h` file can be found in the `/.pio/libdeps/esp32dev/TFT_eSPI` folder.
Modify the `User_Setup_Select.h` file as follows:
* Comment out the following line (which is the 'default' setup file):
```ini
//#include <User_Setup.h> // Default setup is root library folder
```
* Add following line:
```ini
#include <User_Setups/Setup_ST7789_Display.h> // Setup file for ESP32 ST7789V SPI bus TFT
```
* Copy file `"Setup_ST7789_Display.h"` from usermod folder to `/.pio/libdeps/esp32dev/TFT_eSPI/User_Setups`

View File

@@ -0,0 +1,350 @@
// Credits to @mrVanboy, @gwaland and my dearest friend @westward
// Also for @spiff72 for usermod TTGO-T-Display
// 210217
#pragma once
#include "wled.h"
#include <TFT_eSPI.h>
#include <SPI.h>
#define USERMOD_ST7789_DISPLAY 97
#ifndef TFT_DISPOFF
#define TFT_DISPOFF 0x28
#endif
#ifndef TFT_SLPIN
#define TFT_SLPIN 0x10
#endif
#define TFT_MOSI 21
#define TFT_SCLK 22
#define TFT_DC 18
#define TFT_RST 5
#define TFT_BL 26 // Display backlight control pin
TFT_eSPI tft = TFT_eSPI(240, 240); // Invoke custom library
// How often we are redrawing screen
#define USER_LOOP_REFRESH_RATE_MS 1000
//class name. Use something descriptive and leave the ": public Usermod" part :)
class St7789DisplayUsermod : public Usermod {
private:
//Private class members. You can declare variables and functions only accessible to your usermod here
unsigned long lastTime = 0;
bool displayTurnedOff = false;
long lastRedraw = 0;
// needRedraw marks if redraw is required to prevent often redrawing.
bool needRedraw = true;
// Next variables hold the previous known values to determine if redraw is required.
String knownSsid = "";
IPAddress knownIp;
uint8_t knownBrightness = 0;
uint8_t knownMode = 0;
uint8_t knownPalette = 0;
uint8_t tftcharwidth = 19; // Number of chars that fit on screen with text size set to 2
long lastUpdate = 0;
public:
//Functions called by WLED
/*
* setup() is called once at boot. WiFi is not yet connected at this point.
* You can use it to initialize variables, sensors or similar.
*/
void setup()
{
tft.init();
tft.setRotation(0); //Rotation here is set up for the text to be readable with the port on the left. Use 1 to flip.
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_RED);
tft.setCursor(60, 100);
tft.setTextDatum(MC_DATUM);
tft.setTextSize(2);
tft.print("Loading...");
if (TFT_BL > 0)
{ // TFT_BL has been set in the TFT_eSPI library
pinMode(TFT_BL, OUTPUT); // Set backlight pin to output mode
digitalWrite(TFT_BL, HIGH); // Turn backlight on.
}
}
/*
* connected() is called every time the WiFi is (re)connected
* Use it to initialize network interfaces
*/
void connected() {
//Serial.println("Connected to WiFi!");
}
/*
* loop() is called continuously. Here you can check for events, read sensors, etc.
*
* Tips:
* 1. You can use "if (WLED_CONNECTED)" to check for a successful network connection.
* Additionally, "if (WLED_MQTT_CONNECTED)" is available to check for a connection to an MQTT broker.
*
* 2. Try to avoid using the delay() function. NEVER use delays longer than 10 milliseconds.
* Instead, use a timer check as shown here.
*/
void loop() {
// Check if we time interval for redrawing passes.
if (millis() - lastUpdate < USER_LOOP_REFRESH_RATE_MS)
{
return;
}
lastUpdate = millis();
// Turn off display after 5 minutes with no change.
if(!displayTurnedOff && millis() - lastRedraw > 5*60*1000)
{
digitalWrite(TFT_BL, LOW); // Turn backlight off.
displayTurnedOff = true;
}
// Check if values which are shown on display changed from the last time.
if (((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid)
{
needRedraw = true;
}
else if (knownIp != (apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP()))
{
needRedraw = true;
}
else if (knownBrightness != bri)
{
needRedraw = true;
}
else if (knownMode != strip.getMainSegment().mode)
{
needRedraw = true;
}
else if (knownPalette != strip.getMainSegment().palette)
{
needRedraw = true;
}
if (!needRedraw)
{
return;
}
needRedraw = false;
if (displayTurnedOff)
{
digitalWrite(TFT_BL, TFT_BACKLIGHT_ON); // Turn backlight on.
displayTurnedOff = false;
}
lastRedraw = millis();
// Update last known values.
#if defined(ESP8266)
knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID();
#else
knownSsid = WiFi.SSID();
#endif
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
knownBrightness = bri;
knownMode = strip.getMainSegment().mode;
knownPalette = strip.getMainSegment().palette;
tft.fillScreen(TFT_BLACK);
tft.setTextSize(2);
// First row with Wifi name
tft.setTextColor(TFT_SILVER);
tft.setCursor(3, 40);
tft.print(knownSsid.substring(0, tftcharwidth > 1 ? tftcharwidth - 1 : 0));
// Print `~` char to indicate that SSID is longer, than our dicplay
if (knownSsid.length() > tftcharwidth)
tft.print("~");
// Second row with AP IP and Password or IP
tft.setTextColor(TFT_GREEN);
tft.setTextSize(2);
tft.setCursor(3, 64);
// Print AP IP and password in AP mode or knownIP if AP not active.
if (apActive)
{
tft.setTextColor(TFT_YELLOW);
tft.print("AP IP: ");
tft.print(knownIp);
tft.setCursor(3,86);
tft.setTextColor(TFT_YELLOW);
tft.print("AP Pass:");
tft.print(apPass);
}
else
{
tft.setTextColor(TFT_GREEN);
tft.print("IP: ");
tft.print(knownIp);
tft.setCursor(3,86);
//tft.print("Signal Strength: ");
//tft.print(i.wifi.signal);
tft.setTextColor(TFT_WHITE);
tft.print("Bri: ");
tft.print(((float(bri)/255)*100),0);
tft.print("%");
}
// Third row with mode name
tft.setCursor(3, 108);
uint8_t qComma = 0;
bool insideQuotes = false;
uint8_t printedChars = 0;
char singleJsonSymbol;
// Find the mode name in JSON
for (size_t i = 0; i < strlen_P(JSON_mode_names); i++)
{
singleJsonSymbol = pgm_read_byte_near(JSON_mode_names + i);
switch (singleJsonSymbol)
{
case '"':
insideQuotes = !insideQuotes;
break;
case '[':
case ']':
break;
case ',':
qComma++;
default:
if (!insideQuotes || (qComma != knownMode))
break;
tft.setTextColor(TFT_MAGENTA);
tft.print(singleJsonSymbol);
printedChars++;
}
if ((qComma > knownMode) || (printedChars > tftcharwidth - 1))
break;
}
// Fourth row with palette name
tft.setTextColor(TFT_YELLOW);
tft.setCursor(3, 130);
qComma = 0;
insideQuotes = false;
printedChars = 0;
// Looking for palette name in JSON.
for (size_t i = 0; i < strlen_P(JSON_palette_names); i++)
{
singleJsonSymbol = pgm_read_byte_near(JSON_palette_names + i);
switch (singleJsonSymbol)
{
case '"':
insideQuotes = !insideQuotes;
break;
case '[':
case ']':
break;
case ',':
qComma++;
default:
if (!insideQuotes || (qComma != knownPalette))
break;
tft.print(singleJsonSymbol);
printedChars++;
}
// The following is modified from the code from the u8g2/u8g8 based code (knownPalette was knownMode)
if ((qComma > knownPalette) || (printedChars > tftcharwidth - 1))
break;
}
// Fifth row with estimated mA usage
tft.setTextColor(TFT_SILVER);
tft.setCursor(3, 152);
// Print estimated milliamp usage (must specify the LED type in LED prefs for this to be a reasonable estimate).
tft.print("Current: ");
tft.print(strip.currentMilliamps);
tft.print("mA");
}
/*
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
* Below it is shown how this could be used for e.g. a light sensor
*/
/*
void addToJsonInfo(JsonObject& root)
{
int reading = 20;
//this code adds "u":{"Light":[20," lux"]} to the info object
JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u");
JsonArray lightArr = user.createNestedArray("Light"); //name
lightArr.add(reading); //value
lightArr.add(" lux"); //unit
}
*/
/*
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
void addToJsonState(JsonObject& root)
{
//root["user0"] = userVar0;
}
/*
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
void readFromJsonState(JsonObject& root)
{
userVar0 = root["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value
//if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!"));
}
/*
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
* If you want to force saving the current state, use serializeConfig() in your loop().
*
* CAUTION: serializeConfig() will initiate a filesystem write operation.
* It might cause the LEDs to stutter and will cause flash wear if called too often.
* Use it sparingly and always in the loop, never in network callbacks!
*
* addToConfig() will also not yet add your setting to one of the settings pages automatically.
* To make that work you still have to add the setting to the HTML, xml.cpp and set.cpp manually.
*
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
*/
void addToConfig(JsonObject& root)
{
JsonObject top = root.createNestedObject("exampleUsermod");
top["great"] = userVar0; //save this var persistently whenever settings are saved
}
/*
* readFromConfig() can be used to read back the custom settings you added with addToConfig().
* This is called by WLED when settings are loaded (currently this only happens once immediately after boot)
*
* readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes),
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
*/
void readFromConfig(JsonObject& root)
{
JsonObject top = root["top"];
userVar0 = top["great"] | 42; //The value right of the pipe "|" is the default value in case your setting was not present in cfg.json (e.g. first boot)
}
/*
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
* This could be used in the future for the system to determine whether your usermod is installed.
*/
uint16_t getId()
{
return USERMOD_ST7789_DISPLAY;
}
//More methods can be added in the future, this example will then be extended.
//Your usermod will remain compatible as it does not need to implement all methods from the Usermod base class!
};

View File

@@ -0,0 +1,39 @@
// Setup for the ESP32 board with 1.5" 240x240 display
// See SetupX_Template.h for all options available
#define ST7789_DRIVER
#define TFT_SDA_READ // Display has a bidirectionsl SDA pin
#define TFT_WIDTH 240
#define TFT_HEIGHT 240
#define CGRAM_OFFSET // Library will add offsets required
//#define TFT_MISO -1
#define TFT_MOSI 21
#define TFT_SCLK 22
//#define TFT_CS 5
#define TFT_DC 18
#define TFT_RST 5
#define TFT_BL 26 // Display backlight control pin
#define TFT_BACKLIGHT_ON HIGH // HIGH or LOW are options
#define LOAD_GLCD
#define LOAD_FONT2
#define LOAD_FONT4
#define LOAD_FONT6
#define LOAD_FONT7
#define LOAD_FONT8
#define LOAD_GFXFF
//#define SMOOTH_FONT
//#define SPI_FREQUENCY 27000000
#define SPI_FREQUENCY 40000000 // Maximum for ILI9341
#define SPI_READ_FREQUENCY 6000000 // 6 MHz is the maximum SPI read speed for the ST7789V

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

View File

@@ -0,0 +1,69 @@
# Si7021 to MQTT (with Home Assistant Auto Discovery) usermod
This usermod implements support for [Si7021 I²C temperature and humidity sensors](https://www.silabs.com/documents/public/data-sheets/Si7021-A20.pdf).
The sensor data will *not* be shown on the WLED UI (so far) but published via MQTT to WLED's "build in" MQTT device topic.
```
temperature: $mqttDeviceTopic/si7021_temperature
humidity: $mqttDeviceTopic/si7021_humidity
```
Additionally the following sensors can be published:
```
heat_index: $mqttDeviceTopic/si7021_heat_index
dew_point: $mqttDeviceTopic/si7021_dew_point
absolute_humidity: $mqttDeviceTopic/si7021_absolute_humidity
```
Sensor data will be updated/send every 60 seconds.
This usermod also supports Home Assistant Auto Discovery.
## Settings via Usermod Setup
- `enabled`: Enables this usermod
- `Send Dew Point, Abs. Humidity and Heat Index`: Enables additional sensors
- `Home Assistant MQTT Auto-Discovery`: Enables Home Assistant Auto Discovery
# Installation
## Hardware
Attach the Si7021 sensor to the I²C interface.
Default PINs ESP32:
```
SCL_PIN = 22;
SDA_PIN = 21;
```
Default PINs ESP8266:
```
SCL_PIN = 5;
SDA_PIN = 4;
```
## Software
Add to `build_flags` in platformio.ini:
```
-D USERMOD_SI7021_MQTT_HA
```
Add to `lib_deps` in platformio.ini:
```
adafruit/Adafruit Si7021 Library @ 1.4.0
BME280@~3.0.0
```
# Credits
- Aircoookie for making WLED
- Other usermod creators for example code (`sensors_to_mqtt` and `multi_relay` especially)
- You, for reading this

View File

@@ -0,0 +1,236 @@
#pragma once
// this is remixed from usermod_v2_SensorsToMqtt.h (sensors_to_mqtt usermod)
// and usermod_multi_relay.h (multi_relay usermod)
#include "wled.h"
#include <Adafruit_Si7021.h>
#include <EnvironmentCalculations.h> // EnvironmentCalculations::HeatIndex(), ::DewPoint(), ::AbsoluteHumidity()
Adafruit_Si7021 si7021;
#ifdef ARDUINO_ARCH_ESP32 //ESP32 boards
uint8_t SCL_PIN = 22;
uint8_t SDA_PIN = 21;
#else //ESP8266 boards
uint8_t SCL_PIN = 5;
uint8_t SDA_PIN = 4;
#endif
class Si7021_MQTT_HA : public Usermod
{
private:
bool sensorInitialized = false;
bool mqttInitialized = false;
float sensorTemperature = 0;
float sensorHumidity = 0;
float sensorHeatIndex = 0;
float sensorDewPoint = 0;
float sensorAbsoluteHumidity= 0;
String mqttTemperatureTopic = "";
String mqttHumidityTopic = "";
String mqttHeatIndexTopic = "";
String mqttDewPointTopic = "";
String mqttAbsoluteHumidityTopic = "";
unsigned long nextMeasure = 0;
bool enabled = false;
bool haAutoDiscovery = true;
bool sendAdditionalSensors = true;
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
static const char _enabled[];
static const char _sendAdditionalSensors[];
static const char _haAutoDiscovery[];
void _initializeSensor()
{
sensorInitialized = si7021.begin();
Serial.printf("Si7021_MQTT_HA: sensorInitialized = %d\n", sensorInitialized);
}
void _initializeMqtt()
{
mqttTemperatureTopic = String(mqttDeviceTopic) + "/si7021_temperature";
mqttHumidityTopic = String(mqttDeviceTopic) + "/si7021_humidity";
mqttHeatIndexTopic = String(mqttDeviceTopic) + "/si7021_heat_index";
mqttDewPointTopic = String(mqttDeviceTopic) + "/si7021_dew_point";
mqttAbsoluteHumidityTopic = String(mqttDeviceTopic) + "/si7021_absolute_humidity";
// Update and publish sensor data
_updateSensorData();
_publishSensorData();
if (haAutoDiscovery) {
_publishHAMqttSensor("temperature", "Temperature", mqttTemperatureTopic, "temperature", "°C");
_publishHAMqttSensor("humidity", "Humidity", mqttHumidityTopic, "humidity", "%");
if (sendAdditionalSensors) {
_publishHAMqttSensor("heat_index", "Heat Index", mqttHeatIndexTopic, "temperature", "°C");
_publishHAMqttSensor("dew_point", "Dew Point", mqttDewPointTopic, "", "°C");
_publishHAMqttSensor("absolute_humidity", "Absolute Humidity", mqttAbsoluteHumidityTopic, "", "g/m³");
}
}
mqttInitialized = true;
}
void _publishHAMqttSensor(
const String &name,
const String &friendly_name,
const String &state_topic,
const String &deviceClass,
const String &unitOfMeasurement)
{
if (WLED_MQTT_CONNECTED) {
String topic = String("homeassistant/sensor/") + mqttClientID + "/" + name + "/config";
StaticJsonDocument<300> doc;
doc["name"] = String(serverDescription) + " " + friendly_name;
doc["state_topic"] = state_topic;
doc["unique_id"] = String(mqttClientID) + name;
if (unitOfMeasurement != "")
doc["unit_of_measurement"] = unitOfMeasurement;
if (deviceClass != "")
doc["device_class"] = deviceClass;
doc["expire_after"] = 1800;
JsonObject device = doc.createNestedObject("device"); // attach the sensor to the same device
device["name"] = String(serverDescription);
device["model"] = "WLED";
device["manufacturer"] = "Aircoookie";
device["identifiers"] = String("wled-") + String(serverDescription);
device["sw_version"] = VERSION;
String payload;
serializeJson(doc, payload);
mqtt->publish(topic.c_str(), 0, true, payload.c_str());
}
}
void _updateSensorData()
{
sensorTemperature = si7021.readTemperature();
sensorHumidity = si7021.readHumidity();
// Serial.print("Si7021_MQTT_HA: Temperature: ");
// Serial.print(sensorTemperature, 2);
// Serial.print("\tHumidity: ");
// Serial.print(sensorHumidity, 2);
if (sendAdditionalSensors) {
EnvironmentCalculations::TempUnit envTempUnit(EnvironmentCalculations::TempUnit_Celsius);
sensorHeatIndex = EnvironmentCalculations::HeatIndex(sensorTemperature, sensorHumidity, envTempUnit);
sensorDewPoint = EnvironmentCalculations::DewPoint(sensorTemperature, sensorHumidity, envTempUnit);
sensorAbsoluteHumidity = EnvironmentCalculations::AbsoluteHumidity(sensorTemperature, sensorHumidity, envTempUnit);
// Serial.print("\tHeat Index: ");
// Serial.print(sensorHeatIndex, 2);
// Serial.print("\tDew Point: ");
// Serial.print(sensorDewPoint, 2);
// Serial.print("\tAbsolute Humidity: ");
// Serial.println(sensorAbsoluteHumidity, 2);
}
// else
// Serial.println("");
}
void _publishSensorData()
{
if (WLED_MQTT_CONNECTED) {
mqtt->publish(mqttTemperatureTopic.c_str(), 0, false, String(sensorTemperature).c_str());
mqtt->publish(mqttHumidityTopic.c_str(), 0, false, String(sensorHumidity).c_str());
if (sendAdditionalSensors) {
mqtt->publish(mqttHeatIndexTopic.c_str(), 0, false, String(sensorHeatIndex).c_str());
mqtt->publish(mqttDewPointTopic.c_str(), 0, false, String(sensorDewPoint).c_str());
mqtt->publish(mqttAbsoluteHumidityTopic.c_str(), 0, false, String(sensorAbsoluteHumidity).c_str());
}
}
}
public:
void addToConfig(JsonObject& root)
{
JsonObject top = root.createNestedObject(FPSTR(_name));
top[FPSTR(_enabled)] = enabled;
top[FPSTR(_sendAdditionalSensors)] = sendAdditionalSensors;
top[FPSTR(_haAutoDiscovery)] = haAutoDiscovery;
}
bool readFromConfig(JsonObject& root)
{
JsonObject top = root[FPSTR(_name)];
bool configComplete = !top.isNull();
configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled);
configComplete &= getJsonValue(top[FPSTR(_sendAdditionalSensors)], sendAdditionalSensors);
configComplete &= getJsonValue(top[FPSTR(_haAutoDiscovery)], haAutoDiscovery);
return configComplete;
}
void onMqttConnect(bool sessionPresent) {
if (mqttDeviceTopic[0] != 0)
_initializeMqtt();
}
void setup()
{
if (enabled) {
Serial.println("Si7021_MQTT_HA: Starting!");
Wire.begin(SDA_PIN, SCL_PIN);
Serial.println("Si7021_MQTT_HA: Initializing sensors.. ");
_initializeSensor();
}
}
// gets called every time WiFi is (re-)connected.
void connected()
{
nextMeasure = millis() + 5000; // Schedule next measure in 5 seconds
}
void loop()
{
yield();
if (!enabled || strip.isUpdating()) return; // !sensorFound ||
unsigned long tempTimer = millis();
if (tempTimer > nextMeasure) {
nextMeasure = tempTimer + 60000; // Schedule next measure in 60 seconds
if (!sensorInitialized) {
Serial.println("Si7021_MQTT_HA: Error! Sensors not initialized in loop()!");
_initializeSensor();
return; // lets try again next loop
}
if (WLED_MQTT_CONNECTED) {
if (!mqttInitialized)
_initializeMqtt();
// Update and publish sensor data
_updateSensorData();
_publishSensorData();
}
else {
Serial.println("Si7021_MQTT_HA: Missing MQTT connection. Not publishing data");
mqttInitialized = false;
}
}
}
uint16_t getId()
{
return USERMOD_ID_SI7021_MQTT_HA;
}
};
// strings to reduce flash memory usage (used more than twice)
const char Si7021_MQTT_HA::_name[] PROGMEM = "Si7021 MQTT (Home Assistant)";
const char Si7021_MQTT_HA::_enabled[] PROGMEM = "enabled";
const char Si7021_MQTT_HA::_sendAdditionalSensors[] PROGMEM = "Send Dew Point, Abs. Humidity and Heat Index";
const char Si7021_MQTT_HA::_haAutoDiscovery[] PROGMEM = "Home Assistant MQTT Auto-Discovery";

View File

@@ -110,9 +110,9 @@ void userLoop() {
needRedraw = true;
} else if (knownBrightness != bri) {
needRedraw = true;
} else if (knownMode != strip.getMode()) {
} else if (knownMode != strip.getMainSegment().mode) {
needRedraw = true;
} else if (knownPalette != strip.getSegment(0).palette) {
} else if (knownPalette != strip.getMainSegment().palette) {
needRedraw = true;
}
@@ -136,8 +136,8 @@ void userLoop() {
#endif
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
knownBrightness = bri;
knownMode = strip.getMode();
knownPalette = strip.getSegment(0).palette;
knownMode = strip.getMainSegment().mode;
knownPalette = strip.getMainSegment().palette;
tft.fillScreen(TFT_BLACK);
tft.setTextSize(2);

View File

@@ -3,7 +3,7 @@
Based on the excellent `QuinLED_Dig_Uno_Temp_MQTT` by srg74 and 400killer!
This usermod will read from an attached DS18B20 temperature sensor (as available on the QuinLED Dig-Uno)
The temperature is displayed both in the Info section of the web UI as well as published to the `/temperature` MQTT topic if enabled.
This usermod will be expanded with support for different sensor types in the future.
This usermod may be expanded with support for different sensor types in the future.
If temperature sensor is not detected during boot, this usermod will be disabled.
@@ -14,20 +14,21 @@ Copy the example `platformio_override.ini` to the root directory. This file sho
### Define Your Options
* `USERMOD_DALLASTEMPERATURE` - define this to have this user mod included wled00\usermods_list.cpp
* `USERMOD_DALLASTEMPERATURE_CELSIUS` - define this to report temperatures in degrees celsious, otherwise fahrenheit will be reported
* `USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL` - the number of milliseconds between measurements, defaults to 60 seconds
* `USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT` - the number of milliseconds after boot to take first measurement, defaults to 20 seconds
All parameters can be configured at runtime using Usermods settings page, including pin, selection to display temerature in degrees Celsius or Farenheit mand measurement interval.
## Project link
* [QuinLED-Dig-Uno](https://quinled.info/2018/09/15/quinled-dig-uno/) - Project link
* [Srg74-WLED-Wemos-shield](https://github.com/srg74/WLED-wemos-shield) - another great DIY WLED board
### PlatformIO requirements
If you are using `platformio_override.ini`, you should be able to refresh the task list and see your custom task, for example `env:d1_mini_usermod_dallas_temperature_C`.
If you are not using `platformio_override.ini`, you might have to uncomment `DallasTemperature@~3.8.0`,`OneWire@~2.3.5 under` `[common]` section in `platformio.ini`:
If you are not using `platformio_override.ini`, you might have to uncomment `OneWire@~2.3.5 under` `[common]` section in `platformio.ini`:
```ini
# platformio.ini
@@ -41,10 +42,7 @@ default_envs = d1_mini
...
lib_deps =
...
#For use SSD1306 OLED display uncomment following
U8g2@~2.27.3
#For Dallas sensor uncomment following 2 lines
DallasTemperature@~3.8.0
#For Dallas sensor uncomment following line
OneWire@~2.3.5
...
```
@@ -56,3 +54,5 @@ lib_deps =
* Do not report low temperatures that indicate an error to mqtt
* Disable plugin if temperature sensor not detected
* Report the number of seconds until the first read in the info screen instead of sensor error
2021-04
* Adaptation for runtime configuration.

View File

@@ -1,16 +1,15 @@
#pragma once
#include "wled.h"
#include "OneWire.h"
#include <DallasTemperature.h> //DS18B20
//Pin defaults for QuinLed Dig-Uno
//Pin defaults for QuinLed Dig-Uno if not overriden
#ifndef TEMPERATURE_PIN
#ifdef ARDUINO_ARCH_ESP32
#define TEMPERATURE_PIN 18
#else //ESP8266 boards
#define TEMPERATURE_PIN 14
#endif
#ifdef ARDUINO_ARCH_ESP32
#define TEMPERATURE_PIN 18
#else //ESP8266 boards
#define TEMPERATURE_PIN 14
#endif
#endif
// the frequency to check temperature, 1 minute
@@ -18,116 +17,188 @@
#define USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL 60000
#endif
// how many seconds after boot to take first measurement, 20 seconds
#ifndef USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT
#define USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT 20000
#endif
OneWire oneWire(TEMPERATURE_PIN);
DallasTemperature sensor(&oneWire);
class UsermodTemperature : public Usermod {
private:
// The device's unique 64-bit serial code stored in on-board ROM.
// Reading directly from the sensor device address is faster than
// reading from index. When reading by index, DallasTemperature
// must first look up the device address at the specified index.
DeviceAddress sensorDeviceAddress;
bool initDone = false;
OneWire *oneWire;
// GPIO pin used for sensor (with a default compile-time fallback)
int8_t temperaturePin = TEMPERATURE_PIN;
// measurement unit (true==°C, false==°F)
bool degC = true;
// using parasite power on the sensor
bool parasite = false;
// how often do we read from sensor?
unsigned long readingInterval = USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL;
// set last reading as "40 sec before boot", so first reading is taken after 20 sec
unsigned long lastMeasurement = UINT32_MAX - (USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL - USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT);
unsigned long lastMeasurement = UINT32_MAX - USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL;
// last time requestTemperatures was called
// used to determine when we can read the sensors temperature
// we have to wait at least 93.75 ms after requestTemperatures() is called
unsigned long lastTemperaturesRequest;
float temperature = -100; // default to -100, DS18B20 only goes down to -50C
float temperature;
// indicates requestTemperatures has been called but the sensor measurement is not complete
bool waitingForConversion = false;
// flag to indicate we have finished the first getTemperature call
// allows this library to report to the user how long until the first
// measurement
bool getTemperatureComplete = false;
// flag set at startup if DS18B20 sensor not found, avoids trying to keep getting
// temperature if flashed to a board without a sensor attached
bool disabled = false;
byte sensorFound;
void requestTemperatures() {
// there is requestTemperaturesByAddress however it
// appears to do more work,
// TODO: measure exection time difference
sensor.requestTemperatures();
lastTemperaturesRequest = millis();
waitingForConversion = true;
bool enabled = true;
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
static const char _enabled[];
static const char _readInterval[];
static const char _parasite[];
//Dallas sensor quick (& dirty) reading. Credit to - Author: Peter Scargill, August 17th, 2013
float readDallas() {
byte data[9];
int16_t result; // raw data from sensor
float retVal = -127.0f;
if (oneWire->reset()) { // if reset() fails there are no OneWire devices
oneWire->skip(); // skip ROM
oneWire->write(0xBE); // read (temperature) from EEPROM
oneWire->read_bytes(data, 9); // first 2 bytes contain temperature
#ifdef WLED_DEBUG
if (OneWire::crc8(data,8) != data[8]) {
DEBUG_PRINTLN(F("CRC error reading temperature."));
for (byte i=0; i < 9; i++) DEBUG_PRINTF("0x%02X ", data[i]);
DEBUG_PRINT(F(" => "));
DEBUG_PRINTF("0x%02X\n", OneWire::crc8(data,8));
}
#endif
switch(sensorFound) {
case 0x10: // DS18S20 has 9-bit precision
result = (data[1] << 8) | data[0];
retVal = float(result) * 0.5f;
break;
case 0x22: // DS18B20
case 0x28: // DS1822
case 0x3B: // DS1825
case 0x42: // DS28EA00
result = (data[1]<<4) | (data[0]>>4); // we only need whole part, we will add fraction when returning
if (data[1] & 0x80) result |= 0xF000; // fix negative value
retVal = float(result) + ((data[0] & 0x08) ? 0.5f : 0.0f);
break;
}
}
for (byte i=1; i<9; i++) data[0] &= data[i];
return data[0]==0xFF ? -127.0f : retVal;
}
void getTemperature() {
if (strip.isUpdating()) return;
#ifdef USERMOD_DALLASTEMPERATURE_CELSIUS
temperature = sensor.getTempC(sensorDeviceAddress);
#else
temperature = sensor.getTempF(sensorDeviceAddress);
#endif
void requestTemperatures() {
DEBUG_PRINTLN(F("Requesting temperature."));
oneWire->reset();
oneWire->skip(); // skip ROM
oneWire->write(0x44,parasite); // request new temperature reading (TODO: parasite would need special handling)
lastTemperaturesRequest = millis();
waitingForConversion = true;
}
void readTemperature() {
temperature = readDallas();
lastMeasurement = millis();
waitingForConversion = false;
getTemperatureComplete = true;
//DEBUG_PRINTF("Read temperature %2.1f.\n", temperature); // does not work properly on 8266
DEBUG_PRINT(F("Read temperature "));
DEBUG_PRINTLN(temperature);
}
bool findSensor() {
DEBUG_PRINTLN(F("Searching for sensor..."));
uint8_t deviceAddress[8] = {0,0,0,0,0,0,0,0};
// find out if we have DS18xxx sensor attached
oneWire->reset_search();
delay(10);
while (oneWire->search(deviceAddress)) {
DEBUG_PRINTLN(F("Found something..."));
if (oneWire->crc8(deviceAddress, 7) == deviceAddress[7]) {
switch (deviceAddress[0]) {
case 0x10: // DS18S20
case 0x22: // DS18B20
case 0x28: // DS1822
case 0x3B: // DS1825
case 0x42: // DS28EA00
DEBUG_PRINTLN(F("Sensor found."));
sensorFound = deviceAddress[0];
DEBUG_PRINTF("0x%02X\n", sensorFound);
return true;
}
}
}
DEBUG_PRINTLN(F("Sensor NOT found."));
return false;
}
public:
void setup() {
sensor.begin();
// get the unique 64-bit serial code stored in on-board ROM
// if getAddress returns false, the sensor was not found
disabled = !sensor.getAddress(sensorDeviceAddress, 0);
if (!disabled) {
DEBUG_PRINTLN(F("Dallas Temperature found"));
// set the resolution for this specific device
sensor.setResolution(sensorDeviceAddress, 9, true);
// do not block waiting for reading
sensor.setWaitForConversion(false);
// allocate pin & prevent other use
if (!pinManager.allocatePin(TEMPERATURE_PIN,false))
disabled = true;
} else {
DEBUG_PRINTLN(F("Dallas Temperature not found"));
int retries = 10;
sensorFound = 0;
temperature = -127.0f; // default to -127, DS18B20 only goes down to -50C
if (enabled) {
// config says we are enabled
DEBUG_PRINTLN(F("Allocating temperature pin..."));
// pin retrieved from cfg.json (readFromConfig()) prior to running setup()
if (temperaturePin >= 0 && pinManager.allocatePin(temperaturePin, true, PinOwner::UM_Temperature)) {
oneWire = new OneWire(temperaturePin);
if (oneWire->reset()) {
while (!findSensor() && retries--) {
delay(25); // try to find sensor
}
}
} else {
if (temperaturePin >= 0) {
DEBUG_PRINTLN(F("Temperature pin allocation failed."));
}
temperaturePin = -1; // allocation failed
}
}
lastMeasurement = millis() - readingInterval + 10000;
initDone = true;
}
void loop() {
if (disabled || strip.isUpdating()) return;
if (!enabled || !sensorFound || strip.isUpdating()) return;
static uint8_t errorCount = 0;
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 < USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL) return;
if (now - lastMeasurement < readingInterval) return;
// we are due for a measurement, if we are not already waiting
// we are due for a measurement, if we are not already waiting
// for a conversion to complete, then make a new request for temps
if (!waitingForConversion)
{
if (!waitingForConversion) {
requestTemperatures();
return;
}
// we were waiting for a conversion to complete, have we waited log enough?
if (now - lastTemperaturesRequest >= 94 /* 93.75ms per the datasheet */)
{
getTemperature();
if (now - lastTemperaturesRequest >= 750 /* 93.75ms per the datasheet but can be up to 750ms */) {
readTemperature();
if (getTemperatureC() < -100.0f) {
if (++errorCount > 10) sensorFound = 0;
lastMeasurement = now - readingInterval + 300; // force new measurement in 300ms
return;
}
errorCount = 0;
if (WLED_MQTT_CONNECTED) {
char subuf[38];
char subuf[64];
strcpy(subuf, mqttDeviceTopic);
if (-100 <= temperature) {
if (temperature > -100.0f) {
// dont publish super low temperature as the graph will get messed up
// the DallasTemperature library returns -127C or -196.6F when problem
// reading the sensor
strcat_P(subuf, PSTR("/temperature"));
mqtt->publish(subuf, 0, true, String(temperature).c_str());
mqtt->publish(subuf, 0, false, String(getTemperatureC()).c_str());
strcat_P(subuf, PSTR("_f"));
mqtt->publish(subuf, 0, false, String(getTemperatureF()).c_str());
} else {
// publish something else to indicate status?
}
@@ -135,35 +206,115 @@ class UsermodTemperature : public Usermod {
}
}
/*
* API calls te enable data exchange between WLED modules
*/
inline float getTemperatureC() {
return (float)temperature;
}
inline float getTemperatureF() {
return (float)temperature * 1.8f + 32;
}
/*
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
* Below it is shown how this could be used for e.g. a light sensor
*/
void addToJsonInfo(JsonObject& root) {
// dont add temperature to info if we are disabled
if (disabled) return;
if (!enabled) return;
JsonObject user = root[F("u")];
if (user.isNull()) user = root.createNestedObject(F("u"));
JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u");
JsonArray temp = user.createNestedArray(F("Temperature"));
JsonArray temp = user.createNestedArray(FPSTR(_name));
//temp.add(F("Loaded."));
if (!getTemperatureComplete) {
// if we haven't read the sensor yet, let the user know
// that we are still waiting for the first measurement
temp.add((USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT - millis()) / 1000);
temp.add(F(" sec until read"));
return;
}
if (temperature <= -100) {
if (temperature <= -100.0f) {
temp.add(0);
temp.add(F(" Sensor Error!"));
return;
}
temp.add(temperature);
#ifdef USERMOD_DALLASTEMPERATURE_CELSIUS
temp.add(F("°C"));
#else
temp.add(F("°F"));
#endif
temp.add(degC ? getTemperatureC() : getTemperatureF());
if (degC) temp.add(F("°C"));
else temp.add(F("°F"));
}
/**
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
//void addToJsonState(JsonObject &root)
//{
//}
/**
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
* Read "<usermodname>_<usermodparam>" from json state and and change settings (i.e. GPIO pin) used.
*/
//void readFromJsonState(JsonObject &root) {
// if (!initDone) return; // prevent crash on boot applyPreset()
//}
/**
* addToConfig() (called from set.cpp) stores persistent properties to cfg.json
*/
void addToConfig(JsonObject &root) {
// we add JSON object: {"Temperature": {"pin": 0, "degC": true}}
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
top[FPSTR(_enabled)] = enabled;
top["pin"] = temperaturePin; // usermodparam
top["degC"] = degC; // usermodparam
top[FPSTR(_readInterval)] = readingInterval / 1000;
top[FPSTR(_parasite)] = parasite;
DEBUG_PRINTLN(F("Temperature config saved."));
}
/**
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
*
* The function should return true if configuration was successfully loaded or false if there was no configuration.
*/
bool readFromConfig(JsonObject &root) {
// we look for JSON object: {"Temperature": {"pin": 0, "degC": true}}
int8_t newTemperaturePin = temperaturePin;
DEBUG_PRINT(FPSTR(_name));
JsonObject top = root[FPSTR(_name)];
if (top.isNull()) {
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false;
}
enabled = top[FPSTR(_enabled)] | enabled;
newTemperaturePin = top["pin"] | newTemperaturePin;
degC = top["degC"] | degC;
readingInterval = top[FPSTR(_readInterval)] | readingInterval/1000;
readingInterval = min(120,max(10,(int)readingInterval)) * 1000; // convert to ms
parasite = top[FPSTR(_parasite)] | parasite;
if (!initDone) {
// first run: reading from cfg.json
temperaturePin = newTemperaturePin;
DEBUG_PRINTLN(F(" config loaded."));
} else {
DEBUG_PRINTLN(F(" config (re)loaded."));
// changing paramters from settings page
if (newTemperaturePin != temperaturePin) {
DEBUG_PRINTLN(F("Re-init temperature."));
// deallocate pin and release memory
delete oneWire;
pinManager.deallocatePin(temperaturePin, PinOwner::UM_Temperature);
temperaturePin = newTemperaturePin;
// initialise
setup();
}
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[FPSTR(_parasite)].isNull();
}
uint16_t getId()
@@ -171,3 +322,9 @@ class UsermodTemperature : public Usermod {
return USERMOD_ID_TEMPERATURE;
}
};
// strings to reduce flash memory usage (used more than twice)
const char UsermodTemperature::_name[] PROGMEM = "Temperature";
const char UsermodTemperature::_enabled[] PROGMEM = "enabled";
const char UsermodTemperature::_readInterval[] PROGMEM = "read-interval-s";
const char UsermodTemperature::_parasite[] PROGMEM = "parasite-pwr";

View File

@@ -85,7 +85,7 @@ public:
if (m_pD2D && (999000000L != ntpLastSyncTime))
{
// to prevent needing to import all the timezone stuff from other modules, work completely in UTC
time_t timeUTC = now();
time_t timeUTC = toki.second();
tmElements_t tmNow;
breakTime(timeUTC, tmNow);
int nCurMinute = tmNow.Minute;

View File

@@ -0,0 +1,35 @@
# Description
That usermod implements support of simple hand gestures with VL53L0X sensor: on/off and brightness correction.
It can be useful for kitchen strips to avoid any touches.
- on/off - just swipe a hand below your sensor ("shortPressAction" is called and can be customized through WLED macros)
- brightness correction - keep your hand below sensor for 1 second to switch to "brightness" mode.
Configure brightness by changing distance to the sensor (see parameters below for customization).
"macroLongPress" is also called here.
## Installation
1. Attach VL53L0X sensor to i2c pins according to default pins for your board.
2. Add `-D USERMOD_VL53L0X_GESTURES` to your build flags at platformio.ini (plaformio_override.ini) for needed environment.
In my case, for example: `build_flags = ${common.build_flags_esp8266} -D RLYPIN=12 -D USERMOD_VL53L0X_GESTURES`
3. Add "pololu/VL53L0X" dependency below to `lib_deps` like this:
```ini
lib_deps = ${env.lib_deps}
pololu/VL53L0X @ ^1.3.0
```
My entire `platformio_override.ini` for example (for nodemcu board):
```ini
[platformio]
default_envs = nodemcu
[env:nodemcu]
board = nodemcu
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D RLYPIN=12 -D USERMOD_VL53L0X_GESTURES
lib_deps = ${env.lib_deps}
pololu/VL53L0X @ ^1.3.0
```

View File

@@ -0,0 +1,146 @@
/*
* That usermod implements support of simple hand gestures with VL53L0X sensor: on/off and brightness correction.
* It can be useful for kitchen strips to avoid any touches.
* - on/off - just swipe a hand below your sensor ("shortPressAction" is called and can be customized through WLED macros)
* - brightness correction - keep your hand below sensor for 1 second to switch to "brightness" mode.
* Configure brightness by changing distance to the sensor (see parameters below for customization).
* "macroLongPress" is also called here.
*
* Enabling this mod usermod:
* 1. Attach VL53L0X sensor to i2c pins according to default pins for your board.
* 2. Add "-D USERMOD_VL53L0X_GESTURES" to your build flags at platformio.ini (plaformio_override.ini) for needed environment.
* In my case, for example: build_flags = ${common.build_flags_esp8266} -D RLYPIN=12 -D USERMOD_VL53L0X_GESTURES
* 3. Add "pololu/VL53L0X" dependency to lib_deps like this:
* lib_deps = ${env.lib_deps}
* pololu/VL53L0X @ ^1.3.0
*/
#pragma once
#include "wled.h"
#include <Wire.h>
#include <VL53L0X.h>
#ifdef ARDUINO_ARCH_ESP32
#define HW_PIN_SCL 22
#define HW_PIN_SDA 21
#else
#define HW_PIN_SCL 5
#define HW_PIN_SDA 4
#endif
#ifndef VL53L0X_MAX_RANGE_MM
#define VL53L0X_MAX_RANGE_MM 230 // max height in millimiters to react for motions
#endif
#ifndef VL53L0X_MIN_RANGE_OFFSET
#define VL53L0X_MIN_RANGE_OFFSET 60 // minimal range in millimiters that sensor can detect. Used in long motions to correct brightnes calculation.
#endif
#ifndef VL53L0X_DELAY_MS
#define VL53L0X_DELAY_MS 100 // how often to get data from sensor
#endif
#ifndef VL53L0X_LONG_MOTION_DELAY_MS
#define VL53L0X_LONG_MOTION_DELAY_MS 1000 // how often to get data from sensor
#endif
class UsermodVL53L0XGestures : public Usermod {
private:
//Private class members. You can declare variables and functions only accessible to your usermod here
unsigned long lastTime = 0;
VL53L0X sensor;
bool enabled = true;
bool wasMotionBefore = false;
bool isLongMotion = false;
unsigned long motionStartTime = 0;
public:
void setup() {
PinManagerPinType pins[2] = { { HW_PIN_SCL, true }, { HW_PIN_SDA, true } };
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { enabled = false; return; }
Wire.begin();
sensor.setTimeout(150);
if (!sensor.init())
{
DEBUG_PRINTLN(F("Failed to detect and initialize VL53L0X sensor!"));
} else {
sensor.setMeasurementTimingBudget(20000); // set high speed mode
}
}
void loop() {
if (!enabled || strip.isUpdating()) return;
if (millis() - lastTime > VL53L0X_DELAY_MS)
{
lastTime = millis();
int range = sensor.readRangeSingleMillimeters();
DEBUG_PRINTF(F("range: %d, brightness: %d"), range, bri);
if (range < VL53L0X_MAX_RANGE_MM)
{
if (!wasMotionBefore)
{
motionStartTime = millis();
DEBUG_PRINTF(F("motionStartTime: %d"), motionStartTime);
}
wasMotionBefore = true;
if (millis() - motionStartTime > VL53L0X_LONG_MOTION_DELAY_MS) //long motion
{
DEBUG_PRINTF(F("long motion: %d"), motionStartTime);
if (!isLongMotion)
{
if (macroLongPress)
{
applyMacro(macroLongPress);
}
isLongMotion = true;
}
// set brightness according to range
bri = (VL53L0X_MAX_RANGE_MM - max(range, VL53L0X_MIN_RANGE_OFFSET)) * 255 / (VL53L0X_MAX_RANGE_MM - VL53L0X_MIN_RANGE_OFFSET);
DEBUG_PRINTF(F("new brightness: %d"), bri);
stateUpdated(1);
}
} else if (wasMotionBefore) { //released
long dur = millis() - motionStartTime;
if (!isLongMotion)
{ //short press
DEBUG_PRINTF(F("shortPressAction..."));
shortPressAction();
}
wasMotionBefore = false;
isLongMotion = false;
}
}
}
/*
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
*/
void addToConfig(JsonObject& root)
{
JsonObject top = root.createNestedObject("VL53L0x");
JsonArray pins = top.createNestedArray("pin");
pins.add(HW_PIN_SCL);
pins.add(HW_PIN_SDA);
}
/*
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
* This could be used in the future for the system to determine whether your usermod is installed.
*/
uint16_t getId()
{
return USERMOD_ID_VL53L0X;
}
};

View File

@@ -137,9 +137,9 @@ void userLoop() {
needRedraw = true;
} else if (knownBrightness != bri) {
needRedraw = true;
} else if (knownMode != strip.getMode()) {
} else if (knownMode != strip.getMainSegment().mode) {
needRedraw = true;
} else if (knownPalette != strip.getSegment(0).palette) {
} else if (knownPalette != strip.getMainSegment().palette) {
needRedraw = true;
}
@@ -163,8 +163,8 @@ void userLoop() {
#endif
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
knownBrightness = bri;
knownMode = strip.getMode();
knownPalette = strip.getSegment(0).palette;
knownMode = strip.getMainSegment().mode;
knownPalette = strip.getMainSegment().palette;
u8x8.clear();
u8x8.setFont(u8x8_font_chroma48medium8_r);

View File

@@ -143,9 +143,9 @@ void userLoop() {
needRedraw = true;
} else if (knownBrightness != bri) {
needRedraw = true;
} else if (knownMode != strip.getMode()) {
} else if (knownMode != strip.getMainSegment().mode) {
needRedraw = true;
} else if (knownPalette != strip.getSegment(0).palette) {
} else if (knownPalette != strip.getMainSegment().palette) {
needRedraw = true;
}
@@ -169,8 +169,8 @@ void userLoop() {
#endif
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
knownBrightness = bri;
knownMode = strip.getMode();
knownPalette = strip.getSegment(0).palette;
knownMode = strip.getMainSegment().mode;
knownPalette = strip.getMainSegment().palette;
u8x8.clear();
u8x8.setFont(u8x8_font_chroma48medium8_r);

View File

@@ -54,46 +54,38 @@ void userLoop()
switch (myKey) {
case '1':
applyPreset(1);
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
break;
case '2':
applyPreset(2);
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
break;
case '3':
applyPreset(3);
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
break;
case '4':
applyPreset(4);
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
break;
case '5':
applyPreset(5);
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
break;
case '6':
applyPreset(6);
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
break;
case 'A':
applyPreset(7);
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
break;
case 'B':
applyPreset(8);
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
break;
case '7':
effectCurrent += 1;
if (effectCurrent >= MODE_COUNT) effectCurrent = 0;
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
colorUpdated(CALL_MODE_FX_CHANGED);
break;
case '*':
effectCurrent -= 1;
if (effectCurrent < 0) effectCurrent = (MODE_COUNT-1);
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
colorUpdated(CALL_MODE_FX_CHANGED);
break;
case '8':
@@ -102,7 +94,7 @@ void userLoop()
} else if (effectSpeed < 255) {
effectSpeed += 1;
}
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
colorUpdated(CALL_MODE_FX_CHANGED);
break;
case '0':
if (effectSpeed > 15) {
@@ -110,7 +102,7 @@ void userLoop()
} else if (effectSpeed > 0) {
effectSpeed -= 1;
}
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
colorUpdated(CALL_MODE_FX_CHANGED);
break;
case '9':
@@ -119,7 +111,7 @@ void userLoop()
} else if (effectIntensity < 255) {
effectIntensity += 1;
}
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
colorUpdated(CALL_MODE_FX_CHANGED);
break;
case '#':
if (effectIntensity > 15) {
@@ -127,18 +119,18 @@ void userLoop()
} else if (effectIntensity > 0) {
effectIntensity -= 1;
}
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
colorUpdated(CALL_MODE_FX_CHANGED);
break;
case 'C':
effectPalette += 1;
if (effectPalette >= 50) effectPalette = 0;
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
colorUpdated(CALL_MODE_FX_CHANGED);
break;
case 'D':
effectPalette -= 1;
if (effectPalette <= 0) effectPalette = 50;
colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED);
colorUpdated(CALL_MODE_FX_CHANGED);
break;
}

Some files were not shown because too many files have changed in this diff Show More