Compare commits

..

769 Commits

Author SHA1 Message Date
Blaz Kristan
30435e6406 2D compile fix 2024-04-11 20:11:29 +02:00
Blaz Kristan
bd1c06a7a7 Changelog update 2024-04-10 20:06:46 +02:00
Blaž Kristan
ff3cce0ed2 Merge pull request #3763 from Aircoookie/new-KITT
Effect: modified KITT (Scanner)
2024-04-10 19:40:37 +02:00
Blaz Kristan
38539aac74 Merge branch '0_15' into new-KITT 2024-04-09 16:36:18 +02:00
Blaz Kristan
58e8346209 Fix for #3889 2024-04-09 08:25:07 +02:00
Blaz Kristan
ba9ce4adf2 PIO env. PSRAM fix for S3 & S3 with 4M flash
- audioreactive always included for S3 & S2
2024-04-08 16:32:21 +02:00
Blaž Kristan
bd60fe5a13 Merge pull request #3798 from DedeHai/ESP32_S3_TouchFix
Esp32 s3 touch fix
2024-04-07 22:24:32 +02:00
Blaž Kristan
b72f3baab7 Update set.cpp
Compile fix
2024-04-07 22:21:41 +02:00
Blaž Kristan
d1d54ce9c8 Update cfg.cpp
Indentation fix
2024-04-07 22:15:58 +02:00
Blaž Kristan
954f26308b Update button.cpp
Indentation fix
2024-04-07 22:12:01 +02:00
Blaž Kristan
1cafa0b33f Merge pull request #3880 from DedeHai/harmonic-random-palette-generator
Added fix for #3870
2024-04-07 22:03:59 +02:00
Damian Schneider
18c17168e1 replaced &0xFFFF with explicit casts 2024-04-07 16:07:37 +02:00
Damian Schneider
d3a97f1062 removed detachinterrupt from pin manager, added it to set.cpp instead 2024-04-07 13:49:17 +02:00
Blaž Kristan
d18f078b1e Merge pull request #3887 from gaaat98/0_15
fix for #3884
2024-04-06 16:47:18 +02:00
gaaat98
9ffcde878a fix for #3884 2024-04-06 15:40:32 +02:00
Blaz Kristan
aa970d6ca5 Extend JSON API info object
- add "clock" - CPU clock in MHz
- add "flash" - flash size in MB
Fix for #3879
2024-04-05 19:26:09 +02:00
Damian Schneider
503c68320a Bugfix: millis rollover (fix for #3870)
millis()/1000 rollover after 18h was not handled, truncating to 16bits after division fixes it.
2024-04-05 07:23:28 +02:00
Blaz Kristan
a106342439 PICO & WROVER runtime support
return of GPIO16
2024-04-04 23:09:59 +02:00
Blaž Kristan
3b0e6ec65c Merge pull request #3667 from ccruz09/main
usermod support for Adafruit MAX17048 module
2024-04-04 20:25:14 +02:00
Blaž Kristan
8691ddc081 Merge pull request #3875 from w00000dy/cdata
Update cdata.js to rebuild if package.json changes
2024-04-04 20:21:27 +02:00
Blaz Kristan
24c5935661 Fix for missing firmware build
updated changelog (missing credit)
2024-04-03 18:31:17 +02:00
Woody
78b37b592e backup & restore package.json in cdata tests 2024-04-02 20:59:31 +02:00
Woody
4db88cf86b update cdata.js to rebuild if package.json changes 2024-04-02 20:38:56 +02:00
Woody
a7e17eabff add cdata test for package.json & fix a small bug 2024-04-02 20:12:14 +02:00
Blaž Kristan
93d9ce18b2 Merge pull request #3873 from willmmiles/aws-v2.2.1
Update to AsyncWebServer v2.2.1
2024-04-02 07:13:00 +02:00
Will Miles
6f7ac93d84 Update to AsyncWebServer v2.2.1
Fix use-after-free issue and slightly improve code size.  Note that the
version is changed to a hard pin, so future updates can be validated
before getting picked up by new clones, and old version builds
are reproducible.
2024-04-01 11:25:31 -04:00
Carlos Cruz
fa5d60ca26 Merge branch '0_15' into main 2024-04-01 11:02:02 -04:00
Blaz Kristan
20ed81cd86 Prefer I2S1 over RMT on ESP32
- remove Audioreactive limitation
2024-03-30 11:02:28 +01:00
Blaž Kristan
6f38874096 Merge pull request #3862 from willmmiles/fix-esp8266-platform-selection
Fix ESP8266 framework version selection
2024-03-29 21:01:21 +01:00
Blaž Kristan
b7db5be4df Merge pull request #3859 from w00000dy/serg
Only create bin.gz files for esp8266
2024-03-29 21:00:43 +01:00
Blaz Kristan
157dbffc59 Changelog update 2024-03-29 18:56:43 +01:00
Blaz Kristan
f21ab3588d Individual color channel control for JSON API
- fixes #3860
- debug verbose
- PSRAM detection
2024-03-29 16:43:37 +01:00
Will Miles
2900bda8f9 Select ESP8266 framework version via platform
By explicitly listing an unversioned framework dependency in
'platform_packages', we were overriding the selection via the 'platform'
specification, allowing PlatformIO to select any random version.
Remove this line to allow 'platform' to add the framework dependency
with the expected version.
2024-03-29 09:33:12 -04:00
Blaz Kristan
5f37c19d42 PSRAM fix & CCT IC
- prevent PSRAM use on ESP32 rev.1 without compile fix
- add runtime selection for CCT IC (Athom 15W bulb)
2024-03-28 16:03:06 +01:00
Woody
6f6356e617 fix print output for bin.gz file 2024-03-27 21:34:51 +01:00
Woody
5d152baac0 Do not create release_gz folder 2024-03-27 21:19:03 +01:00
Woody
d7739f9764 Revert "Change path for ESP02-binary"
This reverts commit e3271b8082.
2024-03-26 17:41:28 +01:00
Woody
d6e73fde50 only create bin.gz files for esp8266 2024-03-26 17:34:01 +01:00
Blaz Kristan
fd149b3f46 fileDoc removal (optimisation) 2024-03-26 17:18:52 +01:00
Blaž Kristan
d1d45e7166 Merge pull request #3850 from w00000dy/GitHub-Actions
Use npm ci instead of npm install & Remove zlib dependency from package.json
2024-03-25 08:46:40 +01:00
Blaz Kristan
4b19759dd6 Rotary encoder palette count bugfix 2024-03-24 20:34:22 +01:00
Blaz Kristan
47f44680a3 Multi-update
- 0.15.0-b2
- WS2805 support (NeoPixelBus 2.7.9)
- generic PSRAM support (ESP32 rev.1 still needs compile fix)
- BREAKING: default LEDPIN 2 (due to PSRAM)
- PinManager optimisations
2024-03-24 17:37:11 +01:00
Woody
0a344ada97 Add bin.gz binaries for 160mhz builds 2024-03-24 15:23:33 +01:00
Woody
3b98d01f71 Merge branch '0_15' into serg 2024-03-24 14:42:06 +01:00
Woody
23d8000239 Remove accidental space in cdata.js 2024-03-24 14:34:17 +01:00
Woody
c32ee40ca0 add node: prefix to Core modules
The node: prefix makes it clear that you’re importing a built-in module, not a user-defined or third-party module. This can help avoid confusion.
2024-03-24 14:19:49 +01:00
Woody
213f45494f Merge branch '0_15' into GitHub-Actions 2024-03-24 13:38:36 +01:00
Frank
85a51e6c42 Update requirements.txt to solve CI build errors
use latest platformIO package, to avoid build errors due to missing 'scons'

> Tool Manager: Installing platformio/tool-scons @ ~4.40400.0 
> Error: Could not find the package with 'platformio/tool-scons @ ~4.40400.0' requirements for your system 'linux_x86_64' 

> Error: Process completed with exit code 1.
2024-03-24 12:27:32 +01:00
Blaz Kristan
ecfdc6f0a8 Bugfixes:
- #3843
- #3844
- network scan on new install
- misc optimization
2024-03-22 20:49:13 +01:00
Woody
1c1c3fd832 Use npm ci instead of npm install 2024-03-21 12:03:35 +01:00
Woody
7bafe995e5 Remove zlib dependency from package.json 2024-03-21 12:00:18 +01:00
Blaž Kristan
3c23672347 Merge pull request #3836 from THATDONFC/0_15
Add SSD1309_64 I2C Support for FLD Usermod
2024-03-21 05:48:42 +01:00
thatdonfc
33fe68d7eb Revert breaking change 2024-03-20 13:48:15 -07:00
Blaz Kristan
b031fa1531 Palette cycling fix.
Updated getPaletteCount() to return count of all available palettes, including custom ones.
2024-03-20 20:12:21 +01:00
Blaž Kristan
dab4dc3f41 Merge pull request #3810 from deece/fw1906_0_15
Fw1906 Support (based on 0-15)
2024-03-20 19:57:10 +01:00
Blaž Kristan
78096803ea Merge branch '0_15' into fw1906_0_15 2024-03-20 19:51:32 +01:00
Blaž Kristan
f1987b9544 Merge pull request #3828 from willmmiles/0_15-asyncwebserver-2.2.0-features
ESPAsyncWebServer 2.2.0 + features
2024-03-20 19:50:06 +01:00
Will Miles
8b6bf08a23 serveLiveLeds: Use variable buffer size
Allocate the serialization buffer size at the required length, rather
than always allocating the maximum size.
2024-03-19 23:50:32 -04:00
Will Miles
2640203c88 wled_server: Remove local content type variables
Use the CONTENT_TYPEs exported by AsyncWebServer directly.
2024-03-19 23:46:55 -04:00
thatdonfc
2dafa9644f Update Readme for Rotary Encoder Usermod
Add example platformio_override.sample.ini
2024-03-19 14:48:51 -07:00
thatdonfc
92ebeddcb0 Update Readme for Four Line Display Usermod 2024-03-19 14:48:04 -07:00
thatdonfc
ecee073e08 Fix palette names when palette ID > 58 and not custom 2024-03-19 14:04:24 -07:00
thatdonfc
6d1b9ffad2 Add SSD1309_64 I2C Support to FDL Usermod 2024-03-19 14:04:09 -07:00
Woody
7ee4b54154 optimize output_bins.py 2024-03-19 20:06:09 +01:00
Woody
e3271b8082 Change path for ESP02-binary 2024-03-19 18:15:18 +01:00
Woody
c6ff45f959 Merge branch '0_15' into serg 2024-03-19 17:49:56 +01:00
Blaz Kristan
5d3f22e06a Changelog update and build bump. 2024-03-19 15:18:17 +01:00
Blaž Kristan
c77f6c5f7b Merge pull request #3711 from muebau/tetris-ai
Tetris AI usermod
2024-03-19 14:44:51 +01:00
Blaž Kristan
6d03c3a54c Merge pull request #3834 from willmmiles/ddp-no-push
Support DDP sources that don't PUSH
2024-03-19 07:30:58 +01:00
Blaž Kristan
92dc63e5c9 Merge pull request #3833 from willmmiles/fix-avgloopmillis
Fix avgLoopMillis
2024-03-19 06:20:29 +01:00
Will Miles
505d319e01 Fix avgLoopMillis 2024-03-18 19:50:54 -04:00
Will Miles
62f845a94e DDP: Support sources that don't push
If the source never sends the push flag, WLED buffers the update but
never publishes it to the LEDs.  This causes the confusing case where
the peek display shows one thing but the LEDs themselves something else.

Add a static flag that tracks if we've seen a push from the source;
until we do, apply every update as soon as it's received, per the DDP
specification.
2024-03-18 19:49:26 -04:00
Blaz Kristan
7a9eff7f35 Fix. 2024-03-18 20:23:30 +01:00
Blaz Kristan
b849ea8eaa Fix for #3827 2024-03-18 20:01:32 +01:00
muebau
f1635fa302 Merge branch '0_15' into tetris-ai 2024-03-18 13:21:35 +01:00
Blaž Kristan
6110b72b87 Merge pull request #3831 from willmmiles/missing-ir-disable
getSettingsJS: Fix missing DISABLE_INFRARED guard
2024-03-18 08:00:44 +01:00
Will Miles
6de617ecd5 getSettingsJS: Fix missing DISABLE_INFRARED guard 2024-03-17 16:28:09 -04:00
Blaz Kristan
7c8df97968 Merge branch 'main' into 0_15 2024-03-17 12:21:35 +01:00
Blaz Kristan
acf6736afd WLED 0.14.2 release 2024-03-17 11:52:09 +01:00
Will Miles
5f2480c3d9 serveLiveLeds: Use dynamic buffer
There were three problems here:
- AsyncWebServer is going to copy to a heap buffer anyways, so we might
   as well just pass it one it can use
- The buffer size estimate was wrong -- we need 9 bytes per pixel
   ("RRGGBB",), so the buffer could overflow, and it was not
   considering the extra 2D requirements
- On ESP8266, the stack allocation was overflowing the stack, causing
  corruption and crashes.
2024-03-16 12:16:36 -04:00
Will Miles
0593a078c6 handleFileRead: Leverage AWS code
No need to filter or look up content type, just pitch it over the wall.
Also fixes .gz'd content processing.
2024-03-16 12:12:48 -04:00
Will Miles
323c70dcdf Update for new AsyncWebSocketBuffer
Eliminate the extra indirection and allocate shared buffers directly.
2024-03-16 12:12:48 -04:00
Will Miles
a1b0f84444 Pass PROGMEM type to server.on()
Rather than relying on the exception handler, indicate the
__FlashStringHelper type so the correct String constructor is
used.
2024-03-16 12:12:48 -04:00
Will Miles
df6c271830 Use web server ContentType symbols
These were mostly PROGMEM already, but every little bit helps.
2024-03-16 12:12:48 -04:00
Will Miles
12bf04826a Update ESPAsyncWebServer to v2.2.0 2024-03-16 12:12:48 -04:00
Blaz Kristan
52a1b0453c MQTT cleanup & reduction 2024-03-16 15:26:52 +01:00
Blaz Kristan
0dcb56eab5 Modify Bus & BusManager to accommodate digital CCT
- additional fix in hasWhite() & setCCT() & hasCCT()
2024-03-16 12:36:05 +01:00
Frank
a42f78b08b Update CONTRIBUTING.md
Trying to explain why any PR should have a`description`
2024-03-14 20:27:03 +01:00
Blaž Kristan
505768db04 Some fixes & implement recommendation from @Aircoookie
https://github.com/Aircoookie/WLED/pull/3298#issuecomment-1651910418
2024-03-13 20:44:34 +11:00
Robert
c74db95c14 Add FW1906 support 2024-03-13 20:44:33 +11:00
Robert
af3f27feae Move CTT calculation from BusPwm to Bus class.
This enable it to other bus types.
2024-03-13 20:26:58 +11:00
Blaz Kristan
7b366d49d2 Effect: 2D Drift gets original twin option 2024-03-12 21:23:09 +01:00
Blaž Kristan
88b30e7e28 Merge pull request #3816 from BaptisteHudyma/3813-fix-the-drift-animation-0_15
fix the 2d drift animation
2024-03-12 20:59:19 +01:00
BaptisteHudyma
05c0febd04 fix the 2d drift animation 2024-03-12 12:01:25 +01:00
Blaz Kristan
9d70ec56f2 Distinguish 160MHz binaries 2024-03-11 23:22:14 +01:00
Blaz Kristan
7f8ec59939 Changelog update 2024-03-11 23:08:50 +01:00
Blaz Kristan
0398ec40b7 Merge branch 'main' into v0142-beta2 2024-03-11 23:00:17 +01:00
Blaz Kristan
f55465f8b8 Fix AsyncWebServer library 2024-03-11 18:02:03 +01:00
Blaz Kristan
a4384bd340 Simpler boot preset save
- fixes #3806
2024-03-11 17:59:13 +01:00
Blaz Kristan
a2368a75f7 Fix AsynWebServer lib
Add 160MHz builds for ESP8266
2024-03-11 17:41:17 +01:00
Christian Schwinne
68e5e74882 Fix expanded preset low width
Fix spaces missing in HTML somehow
Update copyright year
2024-03-10 21:52:54 +01:00
Damian Schneider
0637c1c9d4 bugfixes
-added minimum threshold, had some crashes when setting threshold to zero before
-moved interrupt detach to GPIO deallocation where it belongs
-added check for touchbutton before detaching interrupt
-moved thochThreshold readout up so it gets updated before passing it to the system call
2024-03-10 19:44:27 +01:00
Damian Schneider
0453a5fb3d added interrupt detach to cfg.cpp 2024-03-10 16:54:25 +01:00
Christian Schwinne
6322ab9f07 . 2024-03-09 22:00:18 +01:00
Christian Schwinne
21d0f10dd7 Fix AR compilation 2024-03-09 21:59:45 +01:00
Christian Schwinne
51b9e81c3d Fix missing spaces in info page 2024-03-09 21:48:48 +01:00
Christian Schwinne
7723011594 Update CONTRIBUTING.md 2024-03-09 21:32:10 +01:00
Christian Schwinne
91c11a18e0 Update CONTRIBUTING.md 2024-03-09 21:31:53 +01:00
Christian Schwinne
215ba35fe1 Make ASCII art in build script more like WLED logo 2024-03-09 20:56:14 +01:00
Christian Schwinne
7e17011ebc Revert "Merge branch 'gif' into 0_15"
This reverts commit cb5eb9edc7, reversing
changes made to da5c12f466.
2024-03-09 20:00:41 +01:00
Carlos Cruz
ab13db73e7 Added files to usermod directory and changes to platform.ini to support Adafruit MAX17048 module. (#2)
Co-authored-by: Azots <78281612+Azots@users.noreply.github.com>
2024-03-09 13:06:30 -05:00
Christian Schwinne
cb5eb9edc7 Merge branch 'gif' into 0_15 2024-03-09 18:23:59 +01:00
Damian Schneider
509675fe66 added touch interrupt activation to set.cpp for S2/S3 2024-03-09 16:03:01 +01:00
Blaz Kristan
da5c12f466 Segment UI bugfix. 2024-03-09 12:37:35 +01:00
Blaz Kristan
f2ad10bbce Limit max frequency to 64k 2024-03-08 18:11:40 +01:00
Blaz Kristan
99a7bece2e Build bump & changlog update 2024-03-07 21:43:41 +01:00
Blaz Kristan
b305fd8865 Fix for ArduinoFFT 2024-03-07 20:52:18 +01:00
Blaz Kristan
c0beb621e2 Better low brightness level PWM handling
Additional string optimisation
2024-03-07 20:21:56 +01:00
Blaz Kristan
da6d64e581 Multiple fixes
- compile time button configuration #3792
- remove IR config if not compiled
- additional string optimisations
2024-03-05 16:27:28 +01:00
Blaž Kristan
a6d58ee360 Merge pull request #3790 from WoodyLetsCode/fix-preset-sorting
Fix preset sorting
2024-03-05 11:38:26 +01:00
Woody
2a480ab7db Revert "fix preset sorting reordering bug"
This reverts commit f25fadafd8.
2024-03-05 11:30:11 +01:00
Damian Schneider
5c09ee29db removed notes 2024-03-04 14:42:50 +01:00
Damian Schneider
15526bd6e8 some tuning for touch buttons on S2/S3
now better fits the default threshold value of 32
2024-03-04 14:38:51 +01:00
Damian Schneider
c8f48168b4 fixed touch buttons for ESP32 S2 and S3
touch is implemented differently on S2 and S3, these changes make touch buttons work on S2 and S3
2024-03-04 14:38:37 +01:00
Blaž Kristan
d937d473f9 Merge pull request #3737 from WoodyLetsCode/GitHub-Actions
Update setup-node and cache usermods in wled-ci.yml
2024-03-04 07:00:49 +01:00
Woody
66f14c6343 restore default preset sorting if no search term is entered 2024-03-03 15:58:30 +01:00
Woody
f25fadafd8 fix preset sorting reordering bug 2024-03-03 15:16:10 +01:00
Will Miles
d9b0df94e2 LockedJsonResponse: Release early if possible
Release the json buffer lock as soon as we've finished serializing.
This should slightly reduce the number of lock collisions as the
response class isn't destructed until after the last packet is ack'd.
2024-03-03 10:58:40 +01:00
Woody
200d11ca99 Revert "Fix preset sorting"
This reverts commit 00038453e1.
2024-03-02 21:56:25 +01:00
Blaž Kristan
f8c48ef60a Merge pull request #3760 from willmmiles/json-response-early-unlock
LockedJsonResponse: Release early if possible
2024-03-02 14:29:34 +01:00
Blaz Kristan
1fc0680c71 160MHz clock & alternate AWS 2024-03-02 14:12:57 +01:00
Blaž Kristan
ec4afb2cbc Merge pull request #3616 from demophoon/dmx-segment-options-expanded
Add additional segment options when controlling over e1.31
2024-03-01 16:28:19 +01:00
Blaz Kristan
a2a632c415 Variable PWM duty cycle resolution 2024-03-01 14:36:17 +01:00
Blaz Kristan
962e64106c A few more PROGMEM optimisations 2024-03-01 14:36:07 +01:00
Blaž Kristan
c40b6088ee Merge pull request #3783 from shammy642/virtual-artnet-rgbw
Add virtual Art-Net RGBW option
2024-02-29 14:57:42 +01:00
shammy642
0d1a254ca8 Changed values to 89 2024-02-29 12:47:45 +00:00
shammy642
fdbcb61a12 Rebased to 0.15 2024-02-29 08:27:11 +00:00
Blaz Kristan
6435cb1466 Fix for missing string in OTA 2024-02-28 20:25:59 +01:00
Blaz Kristan
4739cfab9a JS ABL optimisation 2024-02-28 18:14:42 +01:00
Blaz Kristan
00a73e9cc8 ABL display bugfix 2024-02-27 21:21:53 +01:00
Blaz Kristan
0ff5dec004 Reposition upload warning message.
Fixes #3778
2024-02-27 15:59:41 +01:00
Blaz Kristan
41129cf379 Sqeeze every byte 2024-02-25 17:08:01 +01:00
Will Miles
c8c394b4e9 TZ_TABLE: Add missing const 2024-02-20 18:33:18 +01:00
Will Miles
59886a1528 Move timezone table to PROGMEM 2024-02-20 18:33:09 +01:00
Frank
a28d2c869f Merge pull request #3766 from willmmiles/tztable-progmem
Move timezone table to PROGMEM, saving ~200 bytes of RAM on 8266.
2024-02-20 17:09:47 +01:00
Will Miles
6f1fff44fa TZ_TABLE: Add missing const 2024-02-19 09:48:34 -05:00
Will Miles
108978d1a5 Move timezone table to PROGMEM 2024-02-18 21:23:53 -05:00
Blaz Kristan
a4733b4d91 Move strings into flash 2024-02-18 14:25:07 +01:00
Blaz Kristan
66f4671ec0 Move strings into flash 2024-02-18 14:09:38 +01:00
Blaz Kristan
c932621b5d string fix 2024-02-17 15:00:47 +01:00
Frank
21173dc907 Fixing a potential array bounds violation in ESPDMX
DMXESPSerial::write and DMXESPSerial::read could access DMXDataStore[512] -   one more than the allocated number of elements (i.e. 0...511].
2024-02-17 13:05:04 +01:00
Blaz Kristan
f2c30ba3f7 Effect: modified KITT (Scanner)
- add delay
- add dual checkmark
- rename Fade rate to Trail (inverse)
- makes Scanner Dual obsolete
2024-02-17 12:58:04 +01:00
Blaz Kristan
989bdfb0d5 Reduce string RAM usage for ESP8266 debug builds 2024-02-17 11:33:42 +01:00
Blaz Kristan
5761dce957 JSON buffer lock error messages
Reduce wait time for lock to 100ms
2024-02-17 11:30:29 +01:00
Blaz Kristan
95e2e574b8 ESP-NOW packet modification
- include up to 5 segments in 1st packet
- header contains total number of packets (instead of segments)

web server code reorganise
2024-02-15 20:40:55 +01:00
Will Miles
c789d80ce5 LockedJsonResponse: Release early if possible
Release the json buffer lock as soon as we've finished serializing.
This should slightly reduce the number of lock collisions as the
response class isn't destructed until after the last packet is ack'd.
2024-02-14 19:37:34 -05:00
Frank
8422ffcd19 Fixing a potential array bounds violation in ESPDMX
DMXESPSerial::write and DMXESPSerial::read could access DMXDataStore[512] -   one more than the allocated number of elements (i.e. 0...511].
2024-02-15 00:53:00 +01:00
lost-hope
2d30535b69 Fixing stairway usermod and adding buildflags 2024-02-14 09:56:57 +01:00
Blaž Kristan
0a815179a2 Merge pull request #3758 from lost-hope/main
Fixing stairway usermod and adding buildflags
2024-02-14 09:45:52 +01:00
Blaz Kristan
aa24e5d284 Preset caching on chips with PSRAM
- credit @akaricchi
2024-02-13 11:12:04 +01:00
lost-hope
23aad24027 Fixing stairway usermod and adding buildflags
Fixing Stairway:
missing setup function

Adding builflags:
USERMOD_STAIRCASE_WIPE to install the usermod
2024-02-12 22:41:13 +01:00
Blaz Kristan
7ae5459fe7 0.14.2-b1 2024-02-12 13:29:08 +01:00
Blaz Kristan
0a8a88421e Possible fix for #3589 & partial fix for #3605 2024-02-12 13:20:47 +01:00
Blaz Kristan
4f42a176ef Prevent JSON buffer clear after failed lock attempt
(alternative to #3743)
2024-02-11 20:12:54 +01:00
Blaz Kristan
4457067045 Multiple analog button fix for #3549 2024-02-10 11:47:40 +01:00
Blaz Kristan
e7dc2048ad Multiple analog button fix for #3549 2024-02-10 11:45:33 +01:00
Blaz Kristan
af6094bb06 Bump 2024-02-09 22:58:11 +01:00
Blaž Kristan
0ab139cb24 Merge pull request #3560 from roelbroersma/usermod_v2_HttpPullLightControl_0.0.5
Usermod:  httpPullLightControl v0.0.5 (supersede v0.0.4)
2024-02-09 22:42:40 +01:00
Blaz Kristan
500a7301f2 Fix merge error 2024-02-09 22:37:52 +01:00
Blaz Kristan
c92cef1780 Merge branch '0_15' into usermod_v2_HttpPullLightControl_0.0.5 2024-02-09 22:34:38 +01:00
Blaz Kristan
49ceac1ac7 Add "RGB2Go Tetra" Ethernet 2024-02-09 22:21:14 +01:00
Blaz Kristan
276a93605d Multiupdate
- allow DMX for S2 & C3 (ESPDMX, needs testing)
- (debug) string cleanup & optimisation
- WLED_BRAND for AP SSID
2024-02-09 22:15:29 +01:00
Blaz Kristan
0150c3fe23 Undo flash string in DMX 2024-02-08 23:35:00 +01:00
Blaz Kristan
34674501c3 Remove throttling from UI 2024-02-08 19:33:08 +01:00
Blaz Kristan
f6206d4c30 Web server string optimisation
(saves 140B of RAM on ESP8266)
ETag bugfix
2024-02-08 19:32:23 +01:00
Blaz Kristan
6dcd9596a2 Prevent JSON buffer clear after failed lock attempt
(alternative to #3743)
2024-02-07 18:58:56 +01:00
Blaz Kristan
e165838e54 PIR readme update. 2024-02-07 18:03:58 +01:00
Blaž Kristan
eae1707e97 Merge pull request #3750 from Moustachauve/product-data-0_15
Allow product information to be specified at build time
2024-02-07 13:20:35 +01:00
Christophe Gagnier
ed2950f73b Allow product information to be specified at build time 2024-02-07 02:09:03 -05:00
Blaz Kristan
7eae8f68d8 Merge branch 'main' into 0_15 2024-02-06 14:47:20 +01:00
Blaž Kristan
0ab2d18b52 Merge pull request #3729 from DedeHai/harmonic-random-palette-generator
added function to generate random palette based on harmonic color theory
2024-02-06 13:16:05 +01:00
Blaz Kristan
8ab621bc91 Another unsigned 2024-02-06 11:43:57 +01:00
Blaz Kristan
41e51bbeb5 Configuration item for harmonic palettes
Comment cleanup
2024-02-06 11:06:23 +01:00
Blaz Kristan
66d9e8c038 Merge branch '0_15' into harmonic-random-palette-generator 2024-02-06 10:53:44 +01:00
Blaz Kristan
f5e47b2b74 Fix compiler warning 2024-02-06 10:17:53 +01:00
Blaz Kristan
678a823e8b PULLDOWN fix 2024-02-05 22:21:12 +01:00
Blaz Kristan
246746a82e Multi PIR usermod 2024-02-05 22:13:32 +01:00
Blaz Kristan
00038453e1 Fix preset sorting 2024-02-03 19:24:08 +01:00
Blaz Kristan
9ce485eade Ignore missing remote.json 2024-02-03 14:40:45 +01:00
Blaz Kristan
8a6ff5a42b UI fixes and refinements 2024-02-03 14:40:28 +01:00
Woody
1eaac0072d Add a separate folder for .gz release files 2024-02-03 00:44:38 +01:00
Woody
35716a7e4c Cache usermods in wled-ci.yml 2024-02-01 20:44:46 +01:00
Damian Schneider
e0f89beebb slight change to color parameters
added more white, changed function return value of fully random palette function
2024-02-01 20:06:48 +01:00
Blaz Kristan
b8bf2a707c Remove obsolete usermods
- mode sort
- 4LD (non ALT)
- Rotary encoder (non ALT)
2024-02-01 18:53:55 +01:00
Woody
7d616be8f4 Update setup-node in wled-ci.yml 2024-02-01 18:41:21 +01:00
Blaz Kristan
0a7e9f9f8f Build bump and ghanglog update 2024-02-01 18:36:10 +01:00
Blaz Kristan
226e188903 Merge branch '0_15' of https://github.com/aircoookie/WLED into 0_15 2024-02-01 18:33:23 +01:00
Blaž Kristan
590d454119 Merge pull request #3718 from WoodyLetsCode/GitHub-Actions
Update GitHub Actions workflow
2024-02-01 18:26:13 +01:00
Blaž Kristan
06d5bd799f Merge pull request #3732 from wled-install/patch-1
UM Audioreactive: add two compiler options
2024-02-01 18:24:39 +01:00
Woody
7fe6541b7c Merge branch '0_15' into GitHub-Actions 2024-02-01 18:05:39 +01:00
Blaž Kristan
0f30fa5fb9 Merge pull request #3702 from Aircoookie/trim-platformio_ini
Trim platformio.ini & add info to sample
2024-02-01 17:17:46 +01:00
Blaz Kristan
2c362315ad Rename sample 2024-02-01 17:09:55 +01:00
Blaž Kristan
7dd79edc52 Merge pull request #3734 from WoodyLetsCode/tooltip
Improvements for tooltips
2024-02-01 17:07:52 +01:00
Blaž Kristan
3d6f25a9d3 Merge pull request #3732 from wled-install/patch-1
UM Audioreactive: add two compiler options
2024-02-01 17:07:00 +01:00
Blaž Kristan
b3c21feba3 Merge pull request #3699 from WoodyLetsCode/cdata
Update dependencies for cdata.js and make some improvements
2024-02-01 17:05:19 +01:00
Blaž Kristan
fe54fadbf8 Merge pull request #3709 from Aircoookie/multiwifi
Implement multiple WiFi
2024-02-01 17:04:18 +01:00
Blaž Kristan
caa0722d49 Merge pull request #3715 from WoodyLetsCode/nodelist
Allow nodes buttons to be opened in new target or copy ip address
2024-02-01 17:00:21 +01:00
Blaž Kristan
fee32622f1 Merge pull request #3489 from WoodyLetsCode/clock-overlay
Show clock overlay only if all LEDs are solid black
2024-02-01 16:54:26 +01:00
Blaz Kristan
d1910e4274 Merge branch '0_15' into multiwifi 2024-02-01 16:49:55 +01:00
Blaž Kristan
45eb11d7f4 Merge pull request #3721 from Aircoookie/temporary-AP
Temporary AP
2024-02-01 16:44:41 +01:00
Blaž Kristan
874179fa75 Merge pull request #3625 from m-wachter/ESP32-POE-WROVER
Add support for Olimex ESP32-POE-WROVER.
2024-02-01 16:41:40 +01:00
Blaž Kristan
8b16cb4bae Merge pull request #3580 from itstefanjanos/feature/add-APA106-led
Add APA106 support
2024-02-01 16:41:02 +01:00
Woody
777ed55c21 Add tooltip for toggle freeze & label 2024-01-31 22:36:09 +01:00
Woody
fcafd8b05e Improved tooltips 2024-01-31 22:35:10 +01:00
Blaz Kristan
5f1cadfea4 Merge branch '0_15' into feature/add-APA106-led 2024-01-31 17:21:55 +01:00
wled-install
917a981096 Update audio_reactive.h 2024-01-31 06:34:15 +01:00
wled-install
89f8b6060f Update readme.md 2024-01-31 06:33:48 +01:00
wled-install
345346315a Update readme.md 2024-01-30 22:59:29 +01:00
wled-install
f1c27e1798 Update readme.md 2024-01-30 22:56:09 +01:00
wled-install
4316024dc7 Update audio_reactive.h 2024-01-30 22:51:18 +01:00
Damian Schneider
ca05aa84ff changed randomness and added optimizations
-added fully random palette function ('the old way', currently just used for initialization)
-changed randomness values to make it a little less random
-added 10% chance for pastel color palette
-now using swap() from std library for shuffeling
-changed function name
-moved update check from loadPalette() to handleRandomPalette() saving CPU cycles
2024-01-30 22:28:40 +01:00
Blaz Kristan
d10d7dc298 upload speed and clarifications 2024-01-30 19:44:10 +01:00
Blaz Kristan
063af1dbc7 core description fix 2024-01-30 19:26:37 +01:00
Damian Schneider
ef6fe43251 random palette transitions with set transition time 2024-01-29 22:14:26 +01:00
Damian Schneider
2659055c31 fixed typos 2024-01-29 20:47:09 +01:00
Damian Schneider
f5ed757780 added some minor fixes
-fastled flags
-gamma correction
-pass by reference instead of pointer
2024-01-29 20:47:09 +01:00
Damian Schneider
e114b842ba fixed initialisation or random palette 2024-01-29 20:47:08 +01:00
Damian Schneider
12e2116acb bugfix, need to initialize the random palette to something useful 2024-01-29 20:47:08 +01:00
Damian Schneider
bccc97d25f fixed include, bugfix in random palette handling 2024-01-29 20:47:08 +01:00
Damian Schneider
a9bcc75733 added function to generate random palette based on harmonic color theory 2024-01-29 20:47:08 +01:00
Blaz Kristan
7b87475af8 Fix random palette transitions. 2024-01-29 19:06:41 +01:00
Michael Wachter
2ac218886a Add support for Olimex ESP32-POE-WROVER.
There is a version of the Olimex ESP32-POE board with an ESP32-WROVER
module which has a the ethernet clock connected to a different IO than
the version with an ESP32-WROOM module.

This commit adds a new runtime selectable variant for the WROVER version.
2024-01-28 17:54:19 +01:00
Blaz Kristan
97503897f0 New effect: 2D Rotozoomer 2024-01-28 15:09:40 +01:00
lost-hope
7e06e32cb6 Usermod Klipper Percentage: fix error with slashes on non windows devices 2024-01-27 23:26:30 +01:00
Blaz Kristan
15b813c6af Build bump & changelog update 2024-01-27 21:22:25 +01:00
Blaž Kristan
a71c9107bd Merge pull request #3683 from TripleWhy/rotatingPalletteEffect
Rotating 2D Palette effect
2024-01-27 20:04:36 +01:00
Blaž Kristan
4c2110189b Merge pull request #3682 from Aircoookie/supersync
Use strip.now instead of millis() in effects
2024-01-27 14:06:26 +01:00
Blaz Kristan
df750c2a71 Fix for single wifi 2024-01-27 08:39:54 +01:00
Blaz Kristan
3eb412b750 Add rescan & selection on disconnect 2024-01-26 18:38:56 +01:00
Blaz Kristan
5c7b7e4182 Constant & override 2024-01-26 15:31:09 +01:00
Frank
2b0fad87d2 Merge pull request #3722 from lost-hope/main
Usermod Klipper Percentage: Fix for include paths (windows backslash vs. linux slash)
2024-01-26 11:41:02 +01:00
Blaz Kristan
8817d41275 Wrong return value 2024-01-25 19:57:04 +01:00
Blaž Kristan
04ef3f9496 Merge pull request #3723 from Derek4aty1/dnaSpiralFix
DNA Spiral Effect Speed Fix
2024-01-25 19:52:14 +01:00
Blaz Kristan
fbe26e13ae Remove erroneous wifi selection 2024-01-25 19:46:47 +01:00
Blaz Kristan
4408dffa87 Better invalid password handling
Changed function return value
2024-01-25 19:42:23 +01:00
Derek4aty1
546192865c DNA Spiral Effect Speed Fix 2024-01-25 11:21:16 -06:00
lost-hope
7afa550ba4 Usermod Klipper Percentage: fix error with slashes on non windows devices 2024-01-25 17:55:41 +01:00
Woody
21a9372320 fix clock overlay display 2024-01-25 12:14:44 +01:00
Woody
f4a2eec984 Use a reference instead of segment object copy 2024-01-25 11:33:40 +01:00
Woody
94b9455c9c closes Aircoookie/WLED#984
Implements option to show clock overlay only if all LEDs are solid black
2024-01-25 11:33:36 +01:00
Woody
c41c71c8c1 add bin.gz firmware for ESP02 to artifacs (#4) 2024-01-24 21:22:54 +01:00
Blaz Kristan
dde647c570 Required fix 2024-01-24 20:53:17 +01:00
Blaz Kristan
445a89ba6e Try hard to release memory 2024-01-24 20:49:22 +01:00
Blaz Kristan
07495f6621 Merge branch '0_15' into temporary-AP 2024-01-24 20:22:50 +01:00
Blaz Kristan
5952edc550 Some fixes 2024-01-24 19:52:41 +01:00
Woody
b66a038a2f Change path separator 2024-01-24 17:15:06 +01:00
Woody
df1c8a64e5 Update GitHub Actions workflow
This also adds a WLED_RELEASE_NAME for esp32s3dev_8MB_PSRAM_opi
2024-01-24 16:11:51 +01:00
Blaz Kristan
3e2aebcd10 Remove regression
Init wifi for scan
Always save WiFi name
2024-01-24 15:43:59 +01:00
TripleWhy
0d279cb4dd Add some comments 2024-01-24 15:38:50 +01:00
Woody
3942a76d15 Fix #3708 2024-01-23 21:00:44 +01:00
Blaz Kristan
1bebf3d3d6 Optimise wifi scan
- prioritize strongest signal
- prune removed networks
- fill present networks
2024-01-23 20:44:43 +01:00
Blaz Kristan
d5f54c240a Debug pin fix 2024-01-22 21:01:57 +01:00
Blaz Kristan
8868ecd11a Cleanup 2 2024-01-22 20:48:03 +01:00
Blaz Kristan
8a31c57bfa Fix getActiveSegmentsNum
Limit rotation to +-90 deg (swapping sin/cos & limit angle)
Shift palette shift
2024-01-22 19:38:13 +01:00
muebau
7e2cce4a8d Tetris AI usermod 2024-01-21 17:28:10 +01:00
Blaz Kristan
bfb217c203 Implement multiple WiFi
- similar to #3705
- solves #2845, #2974, #852, #1228
2024-01-21 00:30:15 +01:00
Blaz Kristan
593e55af95 Cleanup. 2024-01-20 23:29:45 +01:00
Blaz Kristan
29af62f956 Fix for 1D & remove segment ID 2024-01-20 14:55:22 +01:00
Blaž Kristan
2b022e1871 Update platformio.ini
Added WROVER model and removed flash options for ESP32-S2
2024-01-19 14:18:54 +01:00
Woody
783a4d3996 old line break style 2024-01-18 21:23:29 +01:00
Woody
ddfe925f8c rename test 2024-01-18 21:23:29 +01:00
Woody
1735bcab21 fix wled banner color in cdata.js 2024-01-18 21:23:29 +01:00
Woody
ff6cacf766 npm update 2024-01-18 21:23:28 +01:00
Woody
2a72eae77f cdata.js: Better console print messages 2024-01-18 21:23:28 +01:00
Woody
39fb7fdfe3 Add test for -f and --force argument 2024-01-18 21:23:28 +01:00
Woody
e0facf35c8 refactored tests 2024-01-18 21:23:28 +01:00
Woody
73d48c8ac4 remove html-minify-ui option 2024-01-18 21:23:28 +01:00
Woody
5080059143 Rebuild if a cdata.js has been modified 2024-01-18 21:23:28 +01:00
Woody
90d5b77d74 more minifying 2024-01-18 21:23:28 +01:00
Woody
e0f987328b add new test 2024-01-18 21:23:28 +01:00
Woody
04c3a925cc Fix test on Github Actions
This needed more time on Github
2024-01-18 21:23:27 +01:00
Woody
219b29991b Update wled-ci.yml 2024-01-18 21:23:27 +01:00
Woody
5120045459 refactor cdata.js 2024-01-18 21:23:27 +01:00
Woody
3bca4a73f4 More tests for cdata 2024-01-18 21:23:27 +01:00
Woody
b4857ab036 Automatically test cdata.js 2024-01-18 21:23:27 +01:00
Woody
f3ae041691 Add first unit tests for cdata.js 2024-01-18 21:23:27 +01:00
Woody
a84627a465 Update dependencies for cdata.js 2024-01-18 21:23:27 +01:00
Blaz Kristan
78aeca6399 Trim platformio.ini & add info to sample 2024-01-18 18:28:01 +01:00
Blaz Kristan
5dd8f0a0b7 typo 2024-01-17 23:39:33 +01:00
Christian Schwinne
9378fc7276 Merge remote-tracking branch 'origin/0_15' into gif 2024-01-16 21:53:08 +01:00
Blaz Kristan
3a0d8069b6 Bus type clarifications
- expansion of digital types
- updated macros
- mem calc function
2024-01-16 21:02:08 +01:00
Blaz Kristan
bab42014a4 Fix for #3693 2024-01-16 19:35:18 +01:00
Blaz Kristan
74fed5695f Fix for #3693 2024-01-16 19:34:23 +01:00
Blaz Kristan
3be368c333 A few more overrides 2024-01-14 21:04:18 +01:00
Blaz Kristan
a4a8e2614e Release WLED 0.14.1 2024-01-14 18:33:27 +01:00
Blaz Kristan
58eb151ba1 Use strip.now instead of millis() in effects
AKA "supersync" from WLED MM fork
2024-01-14 17:51:48 +01:00
Blaz Kristan
21d21ea7f0 Orange flash fix (#3196) for transitions 2024-01-14 17:40:04 +01:00
Blaz Kristan
45dc5e236d Override identifier 2024-01-14 17:38:29 +01:00
dependabot[bot]
0597eeed6b Bump urllib3 from 1.26.15 to 1.26.18 (#3455)
Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.26.15 to 1.26.18.
- [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.15...1.26.18)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-14 15:58:51 +01:00
Christian Schwinne
0a66fe196a Merge remote-tracking branch 'origin/0_15' into pr/3580 2024-01-14 15:08:13 +01:00
Woody
3f21b4aa7d Add own background image upload (#3596)
* Add own bg image upload

* Fix undefined issue in settings UI
2024-01-14 14:16:32 +01:00
TripleWhy
b97186c5bf Rotating palette effect 2024-01-13 18:49:17 +01:00
Blaz Kristan
874b24fb32 WLED time overrides 2024-01-13 13:06:00 +01:00
Blaz Kristan
97889b5e55 Fix for audio palettes 2024-01-12 20:45:04 +01:00
Woody
4b134194cf Better sorting and naming of static palettes 2024-01-12 19:44:25 +01:00
Blaž Kristan
24b60a25d5 Merge pull request #3674 from WoodyLetsCode/cpal
Better sorting and naming of static palettes in the Custom Palette Editor
2024-01-12 18:57:01 +01:00
Blaž Kristan
3e069376a7 Merge pull request #3666 from WoodyLetsCode/pio
Ignore autogenerated files with binary array for the Web UI and fix caching bug
2024-01-12 16:52:33 +01:00
Woody
5bdb2c9e86 Fix bug where some changes were not recognized 2024-01-12 00:02:53 +01:00
Woody
71b0c5f805 Update wled-ci.yml 2024-01-12 00:02:53 +01:00
Woody
a1f62e7a1f Add result file check to isCached function 2024-01-12 00:02:53 +01:00
Woody
186c40801a Delete html_*.h files 2024-01-12 00:02:52 +01:00
Woody
bf867e84d6 Update .gitignore 2024-01-12 00:02:44 +01:00
Woody
27035a8e1d Better sorting and naming of static palettes 2024-01-11 23:13:26 +01:00
Blaž Kristan
db3770c3e5 Merge pull request #3673 from netmindz/animartrix
Add ANIMartRIX usermod
2024-01-11 22:06:31 +01:00
Will Tatam
46337ca554 Add ANIMartRIX usermod 2024-01-11 20:20:53 +00:00
Blaž Kristan
4f0f288000 Merge pull request #3621 from zanhecht/patch-2
Use canvas instead of CSS gradient for liveview
2024-01-11 20:41:35 +01:00
Blaz Kristan
8060bae1b8 Width fix 2024-01-11 20:37:51 +01:00
Blaz Kristan
ea32c97b82 Fix for #3672 2024-01-11 20:24:59 +01:00
Blaz Kristan
99efbd30f1 ColoOrderMap W channel swap
& cleanup
2024-01-11 20:22:42 +01:00
Frank
a9eb7cb4fd udp realtime - improve robustness against short packets (fix for #3672)
when only two bytes were received in a packet, the "for" condition `packetsize -3` would underflow, leading to an infinite loop.
2024-01-11 18:07:23 +01:00
Blaz Kristan
a11cab0f88 Fix for #3665 2024-01-10 19:41:51 +01:00
Blaz Kristan
447324b59f Fix for #3665 2024-01-10 19:40:59 +01:00
Blaz Kristan
e580e815f0 Fix .gitignore 2024-01-10 18:52:46 +01:00
Blaž Kristan
5fc00840f2 Merge pull request #3554 from ezcGman/ledmaps-realtime
En-/disable LED maps when receiving realtime data
2024-01-10 18:25:41 +01:00
ezcGman
ff59dcd66e Merge branch '0_15' of https://github.com/Aircoookie/WLED into ledmaps-realtime 2024-01-10 05:11:44 +01:00
zanhecht
0a1d82de2a Remove legacy CSS gradient code from liveview.htm
Reduce file size by removing legacy CSS gradient code and moving drawing operations to a separate function
2024-01-09 19:27:45 -05:00
Blaz Kristan
26837645ac Add PWM frequency selection in settings 2024-01-09 21:54:21 +01:00
Janos Stefan
c318699d6a Merge branch 'feature/add-APA106-led' of https://github.com/itstefanjanos/WLED into feature/add-APA106-led 2024-01-09 21:32:40 +01:00
Janos Stefan
e5caab635a Remove compiled html headers. 2024-01-09 21:31:25 +01:00
Janos Stefan
d5f96295ff Add APA106 support 2024-01-09 21:30:41 +01:00
Blaz Kristan
4a6c78aaf3 npm 2024-01-09 19:11:45 +01:00
Blaž Kristan
c4d214f5c0 Merge pull request #3598 from WoodyLetsCode/pio
Automatically build UI before compiling
2024-01-09 19:05:04 +01:00
Blaž Kristan
1ed7e6cb1c Merge pull request #3611 from Aircoookie/suspend
Suspend strip during operations
2024-01-09 18:33:44 +01:00
Blaz Kristan
6b7f80f24a Merge branch '0_15' into suspend 2024-01-09 18:30:27 +01:00
Blaz Kristan
220217561a Possible fix for #3589 & partial fix for #3605 2024-01-09 18:20:20 +01:00
Frank
75e857a510 npm run build 2024-01-09 11:57:34 +01:00
Frank
052bdd8eb0 fix for #3655
make WS2814 explicit in LED driver drop-down menu
2024-01-09 11:54:46 +01:00
Britt Gresham
9e54de8a8a Set 2 values between each segment option
Due to the midi interface being difficult/impossible to increment on
values of 1 because it has 7-bits of granularity, this commit moves all
the bitfields for configuring segment options left by one which
guarantees that every option has 2 values next to each other.

This allows midi controllers to more easily select an individual segment
option for 2D arrays.
2024-01-08 11:16:55 -08:00
Britt Gresham
fd2809e367 Replace bitshifts with masks and division 2024-01-08 11:16:55 -08:00
Britt Gresham
18874c2069 Remove unneeded floor call on segment option channel 2024-01-08 11:16:55 -08:00
Britt Gresham
115eb0ddef Add additional segment options when controlling over e1.31
Before this commit it was only possible to control mirror and reverse on
a 1d segment. All of the other options for 2d effects could not be set
and thus they would be kept disabled.

This commit replaces the Effect Option dmx channel with a bitfield which
allows for each segment option to be individually toggled depending on
which bit is set in the field. Backwards compatibility has been
maintained with existing 1d segment options.
2024-01-08 11:16:55 -08:00
Blaž Kristan
9940258725 Merge pull request #3654 from willmmiles/mpu6050-upgrade
Upgrade the MPU6050 usermod
2024-01-08 19:32:36 +01:00
Blaž Kristan
74e14a4fc3 Merge pull request #3656 from WoodyLetsCode/documentation
Make loadBg() more readable
2024-01-08 19:28:18 +01:00
Blaž Kristan
90ea386c62 Merge pull request #3658 from WoodyLetsCode/colorPicker
Fix that color picker is not centered on screens with width between 1024px and 1249px
2024-01-08 19:24:53 +01:00
Woody
a5b132dfd7 Fix that color picker is not centered
Color Picker was not centerd on screens with width between 1024px and 1249px
2024-01-08 14:20:02 +01:00
Woody
157debf8e4 Make loadBg() more readable 2024-01-07 23:51:38 +01:00
Woody
74a5528f17 Small refactor of isCached function in cdata.js 2024-01-07 23:14:24 +01:00
Woody
21de9e938a Rebuild if command line argument is set 2024-01-07 22:06:20 +01:00
Will Miles
9c4d3cd6e2 platformio.ini: Add MPU6050 library dep 2024-01-07 15:50:15 -05:00
Will Miles
0616d184e0 Add GyroSurge usermod
This usermod applies the IMU data to create a "surge" effect when
the device is moved.
2024-01-07 15:44:11 -05:00
Will Miles
6f8b3a6242 usermod_list: Add IMU usermod 2024-01-07 15:44:11 -05:00
Will Miles
9a006dc84a usermod_mpu6050: Add options and outputs
Upgrade the MPU6050 usermod to provide configuration settings for
interrupt pin selection and calibration, a polled mode, and data
output for consumption by other usermods.
2024-01-07 15:44:11 -05:00
Will Miles
d2dbaf52a1 usermod_mpu6050: Fix debug prints
The MPU6050 library happens to choose the same defines as WLED, which
collide and result in nothing printing.   Un- and re-define the macros
to work around this.
2024-01-07 13:48:41 -05:00
Woody
0a9145cd3c add caching for cdata.js 2024-01-07 01:06:52 +01:00
Woody
e778b02c0c Automatically build UI before compiling 2024-01-07 01:06:52 +01:00
Blaz Kristan
8fb5f0ef3c Changelog update v0.14.1-b3 2024-01-06 20:34:31 +01:00
Blaz Kristan
43f5e4360d Changelog update
Remove obsolete semaphore
2024-01-06 20:28:05 +01:00
Blaž Kristan
cdc8640218 Merge pull request #3648 from willmmiles/json-response-locking
Expand JSON buffer lock scope to entire web reply
2024-01-06 17:16:33 +01:00
Blaz Kristan
21f8d7a967 Bump 2024-01-06 17:04:56 +01:00
Blaž Kristan
801d92bbba Merge pull request #3648 from willmmiles/json-response-locking
Expand JSON buffer lock scope to entire web reply
2024-01-06 17:01:34 +01:00
Will Miles
77116172e4 serveJson: Fix possible memory leak
Ensure we delete the response if it's not locked
2024-01-06 10:24:05 -05:00
Will Miles
9d3c0f4ff0 serveJson: Ensure semaphore scope lasts until reply is done
Extend the JSON response class to hold the global JSON buffer lock
until the transaction is completed.

Fixes #3641
2024-01-06 10:09:07 -05:00
Will Miles
6382d2b730 Add C++-style guard class for JSON Buffer lock
Add JSONBufferGuard, an RAII lock guard class for JSONBufferLock
analogous to std::lock_guard.  This permits binding the guard to a scope,
such as an object life cycle or function body, guaranteeing that the
lock will be released when the scope exits.
2024-01-06 10:05:18 -05:00
Blaž Kristan
6ad4493a91 Merge pull request #3639 from WoodyLetsCode/caching
Improve ETag Caching
2024-01-04 19:16:56 +01:00
Blaz Kristan
f070dc5527 Cosmetic & needsUpdate() 2024-01-04 17:40:23 +01:00
Blaz Kristan
fc6e7c81d3 Merge branch '0_15' into suspend 2024-01-04 17:33:07 +01:00
Woody
59a725c52c Use uint16_t eTagSuffix instead of String 2024-01-03 23:39:45 +01:00
Frank
51b3d7cb4a Merge pull request #3569 from raymondhardy/ESP32-S3-WROOM-1-N16R8
new buildenv for esp32s3dev_16MB_PSRAM_opi dev board (LilyGo T7-S3)
2024-01-03 22:20:01 +01:00
Frank
3baff4e675 Merge pull request #3572 from drasch/fix/esp32c3-2mb-flash-size
fix(esp32c3-2mb): correct flash size for c3 board with only 2MB
2024-01-03 22:18:24 +01:00
Blaž Kristan
06911e7ad0 Merge pull request #3642 from peterpociask/patch-1
Update README.md
2024-01-03 21:16:38 +01:00
PeterP
4e0f3e35db Update README.md
Pointed link to current directions for compiling WLED
2024-01-03 15:08:02 -05:00
Woody
fd6ce57003 Improve ETag Caching
This also adds support for ETag caching for the settings pages
Also fixed some issues with the previous caching not being RFC 7232 compliant.
2024-01-03 18:53:54 +01:00
Blaz Kristan
4f55be4f19 Bugfix #3632 2024-01-01 23:21:22 +01:00
Blaz Kristan
bd20c83919 Bugfix #3632 2024-01-01 23:14:45 +01:00
Blaz Kristan
48f8a45031 Last minute adjustments & fixes 2023-12-31 18:05:21 +01:00
Istvan Meszaros
680cec5abb Make palette editor mobile friendly. 2023-12-30 19:49:08 +01:00
Blaž Kristan
e8b3c66c21 Merge pull request #3617 from imeszaros/cpal-mobile
Make palette editor more mobile friendly
2023-12-30 18:43:49 +01:00
Blaz Kristan
ecdc3be52e Changlog update 2023-12-30 11:56:21 +01:00
Blaž Kristan
301bdf2186 Merge pull request #3610 from Aircoookie/psram-4-json
Allow PSRAM (SPI RAM) to be used for JSON buffer
2023-12-30 11:00:53 +01:00
Blaz Kristan
1c1b67e000 Full per-port ABL implementation and bugfix.
Update of BusManager class (static)
2023-12-29 23:07:29 +01:00
Blaz Kristan
662870baf4 Changlog update, b2 release 2023-12-29 10:35:44 +01:00
Blaz Kristan
bc85520f5e Merge branch '0_15' into suspend 2023-12-28 23:38:42 +01:00
Blaz Kristan
d3be7a3edf Merge branch '0_15' into psram-4-json 2023-12-28 23:38:27 +01:00
Blaz Kristan
6cd0da821a Reduce heap fragmentation when switching ledmaps 2023-12-28 23:32:47 +01:00
Blaz Kristan
1333c41811 Slight tweak to reduce footprint 2023-12-28 20:05:02 +01:00
zanhecht
55eceb4080 Fix Firefox loading order, more resilient fallback to gradient method 2023-12-28 13:06:13 -05:00
Blaz Kristan
e95629f827 Remote preset cancles playlist 2023-12-27 20:14:51 +01:00
Blaz Kristan
6332ee6edb Effect buffer optimisations
setMode() fix for selecting gap
UI error messages
2023-12-27 19:36:25 +01:00
zanhecht
901d56f898 Use canvas instead of CSS gradient for liveview
Allows for "pixel perfect" liveview instead of diffused view, to better match what's shown at https://kno.wled.ge/features/effects/. This will make it easier to see what the LEDs are doing, although it may not be as accurate a representation for installations with diffused LEDs. Includes fallback to CSS gradient method for browsers that don't support canvas.
2023-12-27 13:20:22 -05:00
Istvan Meszaros
010c3494fd Make palette editor mobile friendly. 2023-12-26 11:09:20 +01:00
Blaz Kristan
5ebc345e95 Possible bugfix for #3609 #3616 2023-12-25 17:47:39 +01:00
Blaz Kristan
b3a768a04b Remove check for strip 2023-12-25 12:29:12 +01:00
Blaz Kristan
f414af4d82 Fix incorrect compare 2023-12-25 12:27:27 +01:00
Frank
b743ca8eb1 Merge pull request #3615 from srg74/patch-2
Update readme.md
2023-12-24 20:51:43 +01:00
srg74
a3a5bffce4 Update readme.md
Fixed typo
2023-12-24 12:10:51 -05:00
Blaz Kristan
7971f3cbd8 Fix for saving config. 2023-12-23 22:56:07 +01:00
Blaz Kristan
d1b80aa4d4 Beta2 2023-12-23 21:13:01 +01:00
Blaz Kristan
08d9f7d967 Fix for #2922 2023-12-23 21:05:01 +01:00
Blaz Kristan
5f62b4a89d Fix for #2922 2023-12-23 20:58:55 +01:00
Blaž Kristan
776502e6db Merge pull request #3606 from WoodyLetsCode/welcome
Fix bug that welcome page was not shown
2023-12-23 20:27:20 +01:00
Frank
39e0a1fb9e Merge pull request #3612 from Aircoookie/sunset-fix
workaround for invalid sunrise/sunset times (#3601)
2023-12-23 20:26:02 +01:00
Broersma
5ed02d2f0d Merge branch 'upstream/0_14_2' into usermod_v2_HttpPullLightControl_0.0.5 2023-12-23 13:47:42 +01:00
Frank
72e864b013 sunrise/sunset: fix for ambiguous error value
sunset = 0 is a valid result, as the function result is in UTC and not local time .
Example: local time is UTC-8, local sunrise = 08:00am => getSunriseUTC()  = 0.

So we cannot use "0" for "invalid". Using UINT16_MAX resolves the problem, and even allows to simplify calculateSunriseAndSunset() a bit.
2023-12-23 13:13:10 +01:00
Frank
5ab012163e workaround for #3601
if case of invalid or impossible sunset/sunrise results, retry with the previous day. max 3 days back, to prevent infinite loops and far-away results.
2023-12-22 23:28:58 +01:00
Blaz Kristan
1900686bc4 Bump 2023-12-22 15:49:51 +01:00
Blaz Kristan
809a294f9d Fix Pixel Magic button. 2023-12-22 15:43:31 +01:00
Blaz Kristan
cf3f6ede72 Suspend strip during operations 2023-12-22 15:39:07 +01:00
ezcGman
b76a90304d Merge branch '0_15' of https://github.com/Aircoookie/WLED into ledmaps-realtime 2023-12-22 13:19:44 +01:00
Woody
7af5b24b03 Fix bug that welcome page was not shown 2023-12-21 22:17:43 +01:00
Blaz Kristan
1f81fb9284 Implement JSON buffer in PSRAM to free up DRAM. 2023-12-21 21:30:17 +01:00
Blaz Kristan
4e105492ca Retry on failed JSON load.
Throttle for ESP8266
This is a band-aid for ESP8266 JSON corruption and not a proper fix.
2023-12-21 21:02:53 +01:00
Blaž Kristan
abf7dd5fa8 Merge pull request #3587 from WoodyLetsCode/search-scroll-top
Scroll to first search result on search
2023-12-21 20:49:44 +01:00
Blaž Kristan
6c877b6d44 Merge pull request #3597 from WoodyLetsCode/inline
Inline iro.js and rangetouch.js
2023-12-21 20:43:29 +01:00
Xaver
86d2998dc0 Add reference to old loading of iro.js and rangetouch.js 2023-12-20 11:30:28 +01:00
Woody
201daf8ff3 Remove /iro.js and /rangetouch.js 2023-12-19 22:40:24 +01:00
Woody
1140f5f6cb Update version to 2312190 2023-12-19 21:18:33 +01:00
Woody
0a91d60677 add onload event to body 2023-12-19 21:12:07 +01:00
Woody
5cd8f5681e Remove unused feedback function
This was probably forgotten to remove in commit d6c0642a02
2023-12-19 20:41:56 +01:00
Xaver
b2babd9502 inline iro.js and rangetouch.js 2023-12-19 16:52:15 +01:00
Blaz Kristan
10faaaf531 Build bump & changelog 2023-12-18 21:52:22 +01:00
Blaž Kristan
7642c5a50d Merge pull request #3529 from Aircoookie/0_14_1
0.14.1-beta
2023-12-18 21:20:12 +01:00
Blaz Kristan
42dba3170d Fix for #3593 2023-12-17 22:16:57 +01:00
Blaz Kristan
5c90a742ba Fix for #3593 2023-12-17 22:15:28 +01:00
Blaz Kristan
9e57ef074d Simplify PIR usermod
- remove periodic updates
- immediately trigger MQTT
- call mode chang to CALL_MODE_BUTTON
2023-12-17 12:56:30 +01:00
Blaz Kristan
9e9b1c3c98 Fix for incorrect move of MQTT publish 2023-12-17 12:14:44 +01:00
Blaz Kristan
13d5deddec Fix buffer overwrite in PIR MQTT 2023-12-17 11:34:44 +01:00
Blaž Kristan
8522760c70 Merge pull request #3591 from WoodyLetsCode/caching
Fix ETag caching
2023-12-16 13:31:07 +01:00
Blaž Kristan
5bc2282ac5 Merge branch '0_15' into caching 2023-12-16 13:27:50 +01:00
Blaz Kristan
2944b2a8f6 Merge branch '0_15' into temporary-AP 2023-12-16 13:07:40 +01:00
Blaž Kristan
264480b9bc Merge pull request #3586 from WoodyLetsCode/aircoookie-link
Add link to Aircoookie in Info
2023-12-16 13:04:54 +01:00
Blaž Kristan
7d52cc46dc Merge pull request #3590 from WoodyLetsCode/style
Send empty page if skin.css does not exist
2023-12-16 13:03:39 +01:00
Blaž Kristan
37c9fd278c Merge pull request #3511 from WoodyLetsCode/simple-mode
New implementation of the simplified UI
2023-12-16 13:02:18 +01:00
Blaz Kristan
3d6fe0a565 Reduce memory fragmentation 2023-12-16 12:38:15 +01:00
Frank
da488f76d2 LDR_Dusk_Dawn: use pinManager, check ldrPin before use (quick-fix for #3490) 2023-12-15 21:58:05 +01:00
Frank
6d28de77d1 npm run build
refreshing all UI files
2023-12-14 20:47:58 +01:00
Frank
a8bd46c521 bugfix - unusable pins on pico32 boards (#3573)
According to the technical manual, GPIO 16 + 17 are used for onboard flash, so cannot be used by WLED.

example buildenv:

[env:esp32_pico]
extends = env:esp32dev_qio80
board = pico32
2023-12-14 20:36:05 +01:00
Frank
1ec3a74d4c Manila Time is the same as CST (solves #3517)
PHST abbreviation added for clarity.
2023-12-14 19:44:36 +01:00
Blaz Kristan
076449a33d Strings in flash 2023-12-14 18:34:00 +01:00
Blaz Kristan
1b5cc2abf1 Button handling
- debug messages
- verify touch GPIO
2023-12-14 18:32:54 +01:00
Blaz Kristan
b176225910 Add Domoticz MQTT API
- to PIR sensor
- to Dallas temperature
2023-12-14 18:30:24 +01:00
Frank
082d1eea48 oappend robustness improvement
obuf is reset to nullptr in some cases
2023-12-14 15:58:45 +01:00
Woody
e99dfbae0d Fix ETag caching 2023-12-14 14:41:08 +01:00
Frank
d6735bed4f comment clean-up (cosmetic) 2023-12-14 13:53:56 +01:00
Woody
b024d93fa0 Send empty page if skin.css does not exist
This has the advantage that we do not need to send the 404 error page every time
2023-12-14 13:21:09 +01:00
Frank
dbe8554724 code spell checking - part3 (usermods)
if you can spell Fahrenheit, you can't spell Celsius. And vice versa :-)
2023-12-14 03:52:06 +01:00
Frank
9f79e64678 code spell checking - part2 (dependencies) 2023-12-14 03:50:22 +01:00
Frank
f513cae66e code spell checking - part1 (core)
I've found a code spellchecker, so this is what can be corrected easily. Changes are only affecting comments, readme and a few user-visible strings. So no functional impact expected.
2023-12-14 03:49:54 +01:00
Woody
37b807210e Scroll to first search result on search 2023-12-13 21:42:24 +01:00
Xaver
15e26ba929 Add link to Aircoookie in Info 2023-12-13 11:34:25 +01:00
Woody
db1795981f Fix dialog backdrop blur on Safari 2023-12-13 00:38:13 +01:00
Woody
2a40baf509 Update backdrop-filter blur value in index.css 2023-12-13 00:24:56 +01:00
Woody
2dbf72e452 Better implementation of dialog 2023-12-12 23:57:20 +01:00
Blaz Kristan
67ae716c60 JSON HTTP error handling 2023-12-12 15:45:57 +01:00
Janos Stefan
8dfb130d19 Remove compiled html headers. 2023-12-11 12:23:45 +01:00
Janos Stefan
2a42fea32a Add APA106 support 2023-12-11 09:26:20 +01:00
Blaz Kristan
f69edefddf Segment syncing debug info 2023-12-10 10:12:58 +01:00
Blaz Kristan
6f3b5fc559 Add JSON API for AP toggle 2023-12-10 09:59:36 +01:00
Blaz Kristan
7275a34e1f Partial fix for #3578 2023-12-09 22:39:26 +01:00
Blaz Kristan
687e2ecaaa Merge branch '0_15' into simple-mode 2023-12-09 19:41:36 +01:00
Blaz Kristan
64312aedae npm and minor tweak 2023-12-09 19:29:32 +01:00
Blaz Kristan
391876e557 Autowhite cleanup
Fix for USC8904 hasWhite()
2023-12-09 19:03:33 +01:00
Blaz Kristan
cb95a7d418 Autowhite cleanup
Fix for UCS8904 hasWhite().
2023-12-09 18:58:45 +01:00
Woody
830f806dee Fix segments not be displayed in simplified ui 2023-12-08 12:17:28 +01:00
Woody
21c2316f6b Minor optimizations 2023-12-08 11:41:00 +01:00
Woody
930a1ae450 Fix dialog scrollbar on Firefox 2023-12-07 17:09:56 +01:00
Woody
aec331b5a6 Add selected palette and effect names to dropdown 2023-12-07 14:58:33 +01:00
Woody
1db6c281b1 Hide buttons for pixel art and custom palettes 2023-12-07 13:11:34 +01:00
ezcGman
5b70ca81a7 Dropped RLM again from HTTP API 2023-12-06 21:42:17 +01:00
ezcGman
d00df785a5 Merge branch '0_15' of https://github.com/Aircoookie/WLED into ledmaps-realtime 2023-12-06 21:41:34 +01:00
Woody
d0c6e7a285 Add check if UI was already simplified 2023-12-06 19:20:58 +01:00
Woody
058995a0b6 fix selected effect position 2023-12-06 18:57:34 +01:00
Woody
fd3be887b4 Add dropdown for effects 2023-12-06 17:48:14 +01:00
skinnyquiver
84802d9065 [fix] Uses extends as per softhack007 comment 2023-12-05 10:36:31 -06:00
David Rasch
90d696d826 fix(esp32c3-2mb): correct flash size for c3 board with only 2MB 2023-12-05 10:54:32 -05:00
skinnyquiver
aeb9e2ad9f [fix] Removes additional default envs line 2023-12-04 19:24:35 -06:00
skinnyquiver
26ab1bfd4f [Feat] Adds new esp32s3dev_16MB_PSRAM_opi dev board to work with LilyGo T7_s3 ESP32-S3-WROOM-1-N16R8 2023-12-04 19:17:15 -06:00
Woody
23d44cb28b Update top position in .dialog CSS
for better mobile appearance
2023-12-04 20:42:33 +01:00
Broersma
de4b352495 Fix AsyncClient hangs 2023-12-02 16:23:21 +01:00
Blaz Kristan
d635117194 Bugfix ledtype 2023-12-02 12:15:57 +01:00
Blaz Kristan
c0412d368e SEGLEN bugfix if default map1D2D not defined
- comments
2023-12-02 00:48:49 +01:00
Blaz Kristan
7ec30f0796 FX:
- Matrix bugfix
- Lissajous metadata fix
2023-12-02 00:47:16 +01:00
Blaz Kristan
9815a55221 Bugfix #3561 2023-12-02 00:45:35 +01:00
Blaz Kristan
7c108e5128 Bugfix for SEGENV if defult map1D2D not defined 2023-12-02 00:44:30 +01:00
Blaz Kristan
4873031c37 Bugfix #3561 2023-12-02 00:39:43 +01:00
Blaz Kristan
15d23870c1 Tootip bugfix 2023-12-01 16:29:05 +01:00
Woody
2abbf7e762 Hide palette dialog if not available 2023-12-01 15:31:28 +01:00
Woody
1578aa7cb6 Finalize rebase 2023-12-01 14:09:02 +01:00
Woody
f2230751e7 Update padding of palette dialog 2023-12-01 11:57:38 +01:00
Woody
8fc0dda26d Reset scrolling when opening pallet dialog 2023-12-01 11:57:38 +01:00
Woody
0ab71badfa Clear palette search when dialog is opened 2023-12-01 11:57:38 +01:00
Woody
0f24d924f9 Update background color of palette dialog 2023-12-01 11:57:38 +01:00
Woody
518a0de95f minor tweak 2023-12-01 11:57:38 +01:00
Woody
fa5648c0c3 Remove hideD class to use hide class instead 2023-12-01 11:57:38 +01:00
Christophe Gagnier
bd620a764d Fix width issue with simplified UI due to desktop mode leftovers 2023-12-01 11:53:46 +01:00
Christophe Gagnier
cff42b5d27 Improve palette list resizing a bit 2023-12-01 11:53:46 +01:00
Christophe Gagnier
25eef2a40e Show palette selection in a dialog 2023-12-01 11:51:50 +01:00
Woody
db276d5f1f moved segcont simplification to simplifyUI() 2023-12-01 11:51:50 +01:00
Blaz Kristan
ce0d906c35 Dev mode fix. 2023-12-01 11:51:49 +01:00
Blaz Kristan
520826abc0 Minor tewaks. 2023-12-01 11:51:49 +01:00
Woody
5e1d6014d5 Fix color picker 2023-12-01 11:51:49 +01:00
Woody
32af17317c minor improvements 2023-12-01 11:51:49 +01:00
Woody
3e7cc8a0a0 add comment for simplifyUI() 2023-12-01 11:51:49 +01:00
Woody
f717ce52fa Display effect mode text on small screens 2023-12-01 11:50:07 +01:00
Woody
d880f3c628 Add palette list dropdown 2023-12-01 11:50:07 +01:00
Woody
0a97717b35 Add simplified segments 2023-12-01 11:50:07 +01:00
Woody
029403ea10 Only Effect Search should stay on top 2023-12-01 11:50:07 +01:00
Woody
e4ec65622f Enable simplifiedUI by default 2023-12-01 11:50:07 +01:00
Woody
3ee3b97255 Remove old simplifiedUI code and update cdata.js 2023-12-01 11:09:42 +01:00
Woody
a3dd6ce891 simplifiedUI moved to index.js 2023-12-01 11:09:11 +01:00
Woody
0c97d7701e Remove no longer needed code for simplifiedUI 2023-12-01 10:55:14 +01:00
Woody
b852b6f55c Add simplifiedUI to info object 2023-12-01 10:55:14 +01:00
Woody
d3b7b857a4 Fix palette list shadow of selected option
Fix padding and margin in pallist element

Fix palette list shadow of selected option
2023-12-01 10:55:13 +01:00
Woody
63ad3f9290 Show brightness slider 2023-12-01 10:55:13 +01:00
Woody
40cc30e054 Remove unnecessary comment in simple.js 2023-12-01 10:55:13 +01:00
Woody
23374e98ae Added preset quick load before palette list 2023-12-01 10:55:13 +01:00
Woody
40f544fffc Reimplemented simplified UI 2023-12-01 10:52:45 +01:00
ezcGman
4fc69c9a2c Merge branch '0_15' of https://github.com/Aircoookie/WLED into ledmaps-realtime 2023-11-29 20:14:51 +01:00
ezcGman
2e5a485804 Realtime LED Maps: Feedback applied 2023-11-29 20:12:31 +01:00
Blaz Kristan
2d10b5be90 from #3555 2023-11-29 13:46:35 +01:00
Blaž Kristan
b0d0bb7953 Merge pull request #3555 from srg74/patch-1
Update readme.md
2023-11-29 13:42:43 +01:00
Blaž Kristan
8c69b85280 Merge pull request #3540 from WoodyLetsCode/search
Improve search and filter functionality
2023-11-29 13:41:38 +01:00
Blaz Kristan
9d6d1e4ff8 npm 2023-11-29 13:37:37 +01:00
Blaz Kristan
e71c72b4a2 Remove re-seeding FastLED random8/random16 in FX 2023-11-29 13:33:10 +01:00
Blaž Kristan
bf4b29b3e3 Merge pull request #3552 from TripleWhy/seedRandom16
Seed FastLED's RNG
2023-11-29 13:30:06 +01:00
Blaz Kristan
8e0809ceba Code size reduction 2023-11-29 11:56:47 +01:00
Broersma
230af48899 Fixing parsing only VALID JSON, in the past, when invalid JSON, the WLED runtime would crash 2023-11-28 12:04:41 +01:00
ezcGman
9dc459192e Merge branch '0_15' of https://github.com/Aircoookie/WLED into ledmaps-realtime 2023-11-28 11:49:53 +01:00
Blaz Kristan
8de2301c6b Usermod transition change 2023-11-28 10:46:04 +01:00
Blaz Kristan
f66b343edd Possible fix for #3541 2023-11-28 10:42:18 +01:00
srg74
ff82d36e94 Update readme.md
Fix typo
2023-11-27 22:47:07 -05:00
Blaz Kristan
c3e937f5e0 Fix for #3514 2023-11-27 20:58:21 +01:00
TripleWhy
25c5e823d9 Seed FastLED's random functions during startup
This allows effects or color palettes that use FastLED's RNG to be
different each time the device is powered on.
2023-11-27 18:44:09 +01:00
TripleWhy
528b8b1271 Use FastLED's random functions instead of arduino's for all effects 2023-11-27 18:18:47 +01:00
Blaz Kristan
b88344a6c0 Second attempt at #3541 2023-11-27 14:24:07 +01:00
Blaz Kristan
9e135cc81d Hide transitions if not enabled. 2023-11-27 13:39:52 +01:00
Blaz Kristan
72ef288357 Fix for #3514 2023-11-27 13:34:35 +01:00
Blaz Kristan
426ac29ca5 Possible fix for #3541 2023-11-27 13:27:52 +01:00
Woody
ceb330e3e4 Do not hide filter menu if filter is active 2023-11-26 18:15:00 +01:00
Woody
7ea8f741f2 Make code more readable 2023-11-26 17:35:29 +01:00
Woody
443c9fba03 Fix search input focus issue 2023-11-26 17:07:42 +01:00
Blaž Kristan
0227f79bd7 Merge pull request #3547 from micw/feature/wiz-smart-button-0.15
Support for wiz smart button
2023-11-25 19:17:36 +01:00
Michael Wyraz
2812eaa13d Support for wiz smart button 2023-11-25 16:42:39 +01:00
Woody
cb293fcdb3 Fix filter hiding on chrome 2023-11-24 13:48:12 +01:00
Woody
706428fe98 Fix filterFx function calls 2023-11-24 13:21:03 +01:00
Woody
698f5f951e Fix search function behavior when filter is active 2023-11-24 13:04:29 +01:00
Woody
155c12f657 Improve search and filter functionality 2023-11-24 11:24:34 +01:00
ezcGman
a36c731d15 En-/disable LED maps when receiving realtime data 2023-11-23 17:13:13 +01:00
Blaz Kristan
6a793536dd Add button type: Touch (switch) #3537 2023-11-22 15:35:11 +01:00
Blaz Kristan
27532a4237 Add new AP type to prevent user frustration. 2023-11-22 15:30:04 +01:00
Blaz Kristan
467f69f50e Switch off AP after 5min
- when no clients are connected in "No connection after boot" mode
2023-11-21 17:23:51 +01:00
Frank
32e724e744 Merge pull request #3536 from Aircoookie/ntp_errorchecking
NTP validation, and rejecting malformed responses (related to #3515)
2023-11-20 21:13:35 +01:00
Frank
5fb891c495 indentation fix2 2023-11-20 19:24:04 +01:00
Frank
d56cefdc7a indentation fix 2023-11-20 19:20:18 +01:00
Frank
80c67d37c0 bufix: ntp query when NTP gets enabled from UI
when NTP got enabled via UI, WLED would wait up to 12 hours before issuing the first NTP request.
2023-11-20 15:13:39 +01:00
Frank
1e29d9463e validate NTP responses (fixes #3515)
* purge old (not yet processes) NTP responses
* validate server responses before updating WLED time
* purge receive buffer when package is rejected (avoids mem leak on esp32)
2023-11-20 12:24:34 +01:00
Blaz Kristan
75f6a53269 Fix (#3522) 2023-11-18 18:50:19 +01:00
Blaz Kristan
b5a8f3156c Bugfix (#3522) 2023-11-18 18:49:29 +01:00
Blaž Kristan
de245c08ce Merge pull request #3483 from ajotanc/pxm_update
Update Pixel Magic Tool
2023-11-18 18:34:55 +01:00
Blaž Kristan
76a7f25083 Merge pull request #3522 from martinez20m/fix_ir_repeat
Fix repeatable action for ir json
2023-11-18 18:29:46 +01:00
Blaz Kristan
d9a96fbb29 Bugfix (#3533) 2023-11-18 18:24:22 +01:00
Blaz Kristan
16617660c3 Bugfix (#3533) 2023-11-18 18:22:47 +01:00
Blaž Kristan
a83d9a075f Merge pull request #3508 from Moustachauve/fix-settings-cache
Fix settings caching on some browsers
2023-11-17 08:48:21 +01:00
Christophe Gagnier
eca3fb1e19 Revert "Implement front-end strategy to prevent caching"
This reverts commit 51dfa9a247.
2023-11-16 21:21:09 -05:00
Blaz Kristan
41bc799c3f 0.14.1 changelog 2023-11-16 20:37:03 +01:00
Blaz Kristan
44726c7411 Changelog 0.14.1-b1 2023-11-16 19:34:53 +01:00
Blaz Kristan
8f7f9ec367 Feature: Per-port ABL 2023-11-15 19:37:07 +01:00
Blaz Kristan
21de8f2284 Effect: 2D Matrix gap fix 2023-11-15 18:05:44 +01:00
Blaz Kristan
15797a89e7 Bugfix (#3526) 2023-11-15 18:04:54 +01:00
Blaz Kristan
c340a1d47f Bugfix (#3526) 2023-11-15 18:04:14 +01:00
Broersma
71cec39bc4 update to 0.0.4 includes SHA1 hash instead of old 10digit hash 2023-11-15 17:24:05 +01:00
Broersma
882542318c Update to v0.0.3, allowing all WLED JSON giving much more possibilities 2023-11-13 22:05:15 +01:00
Marcin Skiba
7238ef4f55 Fix repeatable action for ir json 2023-11-13 12:28:05 +01:00
Blaz Kristan
1049d65621 Changelog update 2023-11-12 14:21:50 +01:00
Blaž Kristan
552ae836e9 Merge pull request #3501 from TripleWhy/spookierEyes
A spookier version of Halloween Eyes
2023-11-12 14:01:56 +01:00
Blaz Kristan
1852c0a88c Merge branch '0_15' into spookierEyes 2023-11-12 12:49:02 +01:00
Blaz Kristan
d0d399bdfc Revert to comparing _data for double buffering
- warning: test for potential FPS drop (regression)
2023-11-12 12:45:28 +01:00
Blaz Kristan
3975fe0f02 Unload playlist on Wiz remote preset button press 2023-11-12 12:41:14 +01:00
Blaž Kristan
0739cfc240 Merge pull request #3498 from chrisburrows/multi-relay-settings
Allow more compile time settings for multi-relay
2023-11-10 19:54:41 +01:00
Chris Burrows
f31bf4f7c9 Updated readme 2023-11-10 17:36:48 +00:00
Blaz Kristan
f413671915 Focus fix. 2023-11-09 17:14:40 +01:00
Christophe Gagnier
e67a210e95 Implement back-end strategy to prevent caching
By setting the response header "Cache-Control" to "no-store" and setting "Expires" to 0, we make sure the browsers and place calling this never store it in cache.
2023-11-08 23:46:31 -05:00
Christophe Gagnier
51dfa9a247 Implement front-end strategy to prevent caching
By adding a random string to the path of the js file every time, we make sure to never hit the browser cache.
2023-11-08 23:45:48 -05:00
Blaz Kristan
7c121b8ba6 Dissolve fix (#3502) 2023-11-08 18:42:43 +01:00
Blaz Kristan
ae92a5c25e Dissolve fix (#3502) 2023-11-08 18:41:46 +01:00
Blaz Kristan
fabd3b8421 Better reverse proxy support. 2023-11-08 18:40:52 +01:00
Chris Burrows
94d754e63a Allow more compile time settings 2023-11-07 16:06:03 +00:00
Blaz Kristan
f869542a47 Changelog update. 2023-11-06 20:39:58 +01:00
Blaz Kristan
2b616b688d Implement global JSON API boolean toggle. 2023-11-06 20:08:45 +01:00
TripleWhy
0925ea6f9c Add some comments 2023-11-04 16:12:22 +01:00
TripleWhy
87b5565679 Minor simplification 2023-11-04 16:12:10 +01:00
TripleWhy
adcecf969a Simplify casting a bit, where possible 2023-11-04 15:54:21 +01:00
TripleWhy
f3831665d4 Fix type name 2023-11-04 15:48:22 +01:00
Blaz Kristan
9db8d44654 Sort presets by ID 2023-11-04 09:51:25 +01:00
Blaz Kristan
e1075a3bbf Sort presets by ID 2023-11-04 09:39:08 +01:00
TripleWhy
3b6f499fc5 Reduce EyeData size by a few bytes 2023-11-02 18:31:58 +01:00
TripleWhy
2c1fbe103b Spookier version of Halloween Eyes 2023-11-02 18:12:18 +01:00
Blaž Kristan
1543862f3b Merge pull request #3490 from JeffWDH/main
Add LDR sensor dusk/dawn preset control Usermod
2023-11-02 16:47:00 +01:00
JeffWDH
5b7b34a9f0 Add LDR sensor dusk/dawn preset control Usermod 2023-11-02 11:20:08 -04:00
Blaž Kristan
fb6271eecf Merge pull request #3496 from WoodyLetsCode/power-supply
Fix wrong power supply info
2023-11-01 10:35:57 +01:00
Blaz Kristan
031526b619 Correction 2023-11-01 10:29:52 +01:00
Blaz Kristan
e42ca2dbf2 Fix #3496 2023-11-01 10:28:57 +01:00
Blaz Kristan
4f5816f42e Merge branch '0_14_1' into power-supply 2023-11-01 10:26:42 +01:00
Blaz Kristan
555d0d3378 Npm & version comment 2023-11-01 10:23:57 +01:00
Blaž Kristan
1495b756d3 Merge pull request #3481 from WoodyLetsCode/random-bg-img
Improved random bg image and added random bg image options
2023-10-31 10:27:47 +01:00
Blaz Kristan
7f78d00b97 Tweaks 2023-10-31 10:21:00 +01:00
Blaz Kristan
bfd58b3cdf Merge branch '0_15' into random-bg-img 2023-10-31 09:24:08 +01:00
Blaz Kristan
e260b9473a Bump build 2023-10-31 09:20:54 +01:00
Blaž Kristan
ccc56d446f Merge pull request #3484 from WoodyLetsCode/settings-page
Show 2D Configuration button by default
2023-10-31 09:17:50 +01:00
Blaz Kristan
31a4e38bc0 Shave a few bytes. 2023-10-30 13:43:50 +01:00
Woody
284366e6bb fixed wrong power supply info 2023-10-29 19:04:54 +01:00
Blaz Kristan
9c6dda9bd2 Segment copy bugfix
Audio palette modification
2023-10-29 11:27:17 +01:00
Blaz Kristan
e5f6f8dfa7 Twinklefox & Twinklecat fix 2023-10-28 21:04:19 +02:00
Blaz Kristan
0232117de5 Twinklefox fix
Tooltip tuning
2023-10-28 21:02:49 +02:00
Blaz Kristan
96700fe3d1 Fix for udp sync (#3487) 2023-10-27 17:49:48 +02:00
Blaz Kristan
367a4e17a2 Fix udp sync (fix for #3487) 2023-10-27 17:48:19 +02:00
Blaz Kristan
eba0d7c1e0 Remove loggong. 2023-10-27 16:53:08 +02:00
Blaz Kristan
341a4a0d60 Fix sticky top. 2023-10-27 16:35:03 +02:00
Blaz Kristan
d116424241 Filter dropdown. 2023-10-26 22:09:46 +02:00
Blaz Kristan
db881ee011 Small optimisations.
2D liveView (non-WS) core
soundSim fix
2023-10-25 20:27:01 +02:00
Woody
34bed44a9b Hide 2D Configuration if 2D is not compiled in 2023-10-25 18:33:31 +02:00
Xaver
d431aa4b59 Show 2D Config button by default 2023-10-25 08:33:07 +02:00
Alerson Jorge
2512bebc62 Corrections
- CSS adjustments
- Removal of some html
- Correction of CORS error when using the file locally, it was unable to generate the images that are saved in WLED
- Among others that I don't remember now
2023-10-24 18:56:12 -03:00
Alerson Jorge
309dbd7585 Fixies
- Correction in animation generation that was actually breaking
- Button to return to WLED if pxmagic.htm is accessed via url (http://), if accessed via external (file://) the button will not show
- Adjustments to CSS and some features
2023-10-24 13:28:33 -03:00
Alerson Jorge
a1f28caa8e Update Pixel Magic Tool
Reducing the size of HTML/CSS/Javascript
2023-10-24 12:52:06 -03:00
Woody
4204393337 Merge branch '0_15' into random-bg-img 2023-10-23 22:21:01 +02:00
Blaz Kristan
789e34702b Pixel Magic logo 2023-10-23 21:06:53 +02:00
Blaz Kristan
47bc1bf88d Merge branch '0_14_1' into 0_15 2023-10-23 20:18:55 +02:00
Blaz Kristan
9867227ccd Increase IRAM at the expense of cache for ESP8266 2023-10-23 19:30:44 +02:00
Blaz Kristan
26302d478e Shave a few bytes. 2023-10-23 19:26:02 +02:00
Blaž Kristan
0decf94c9c Merge pull request #3480 from knarfd/0_14_1
Add #HH and #MM time options for ScrollingText effect
2023-10-23 19:20:17 +02:00
knarfd
0ae78efd43 Update FX.cpp 2023-10-23 18:28:47 +02:00
Woody
b5af6b0bf9 added options for random bg image 2023-10-23 17:30:15 +02:00
Blaž Kristan
3eabefd3d6 Merge pull request #3466 from WoodyLetsCode/fix-effect-jumping
Fix jumping of selected effect on refresh
2023-10-22 19:12:49 +02:00
Woody
7ff6a6e0e7 updated html_ui.h via npm run build 2023-10-22 18:26:52 +02:00
Woody
94dc611024 Merge pull request #1 from WoodyLetsCode/0_14_1
update to 0.14.1
2023-10-22 18:20:24 +02:00
Woody
0b28107432 Merge branch 'fix-effect-jumping' into 0_14_1 2023-10-22 18:19:15 +02:00
Blaz Kristan
fe717dad7f Possible fix for:
- #3382
- #3312
2023-10-22 17:54:00 +02:00
Blaz Kristan
fb3816e2e5 Possible fix for:
- #3382
- #3312
2023-10-22 17:53:16 +02:00
Blaz Kristan
535bf4c848 npm 2023-10-22 12:37:27 +02:00
Blaž Kristan
1fc60b0682 Merge pull request #3460 from mountainash/fix/broken-asset-links
Fixed broken Alphacoders hosted assest asset links
2023-10-22 11:41:15 +02:00
Mountain/\Ash
302f1ee8e3 removed: past Easter event 2023-10-22 11:02:26 +02:00
Mountain/\Ash
42aec56b8e refactor: removed trailing whitespace 2023-10-22 11:02:26 +02:00
Mountain/\Ash
199c00c59b fix: image URLs moved on alphacoders 2023-10-22 11:02:26 +02:00
Blaž Kristan
107bb14555 Merge pull request #3296 from ajotanc/pxmagic
Updating pxmagic and WLED UI
2023-10-21 20:22:06 +02:00
Blaz Kristan
5820792013 Revert changes. 2023-10-21 20:12:22 +02:00
Blaz Kristan
0e20248494 Fix gitignore. 2023-10-21 20:01:12 +02:00
Blaz Kristan
c4f4b247bc Merge branch 'main' into 0_15 2023-10-21 19:52:49 +02:00
Blaz Kristan
0d66bc49c2 Merge branch 'main' into 0_14_1 2023-10-21 19:52:29 +02:00
Blaž Kristan
bdc78a826f Merge pull request #3464 from ajotanc/new_feature
UI tooltips (interface improvements)
2023-10-21 19:27:01 +02:00
Blaz Kristan
6570062e61 Slight tweaks. 2023-10-21 19:18:45 +02:00
Woody
820facb833 Removed unnecessary text 2023-10-21 18:13:01 +02:00
Alerson Jorge
a933fcf7e2 Optimisations, and fix variable multiplier in tooltip() 2023-10-20 19:13:45 -03:00
Alerson Jorge
2d3039c6a2 Fix 2023-10-20 18:22:13 -03:00
Alerson Jorge
aca01044f4 Add brand
Since PXM will not open internally but on another page from the button, I decided to add the logo again if it's not a problem of course.. Just so I don't run out of information
2023-10-20 18:20:28 -03:00
Blaz Kristan
dffd1b1d69 Optimisations.
- tooltip everywhere
- smaller footprint
- fix brightness div
2023-10-20 21:32:39 +02:00
Alerson Jorge
14fc64f2f1 Fix
Checkbox fixes added
2023-10-20 12:20:49 -03:00
Woody
04791d06d0 hide URL field when Random BG image is checked 2023-10-20 15:58:25 +02:00
Alerson Jorge
a485451328 Fix tooltip
Messages showing correctly according to their respective effect.
2023-10-20 09:27:00 -03:00
Woody
c7d399c122 fix effect jumping on refresh
fix effect jumping on refresh

fix jumping of selected effect on refresh
2023-10-20 12:08:11 +02:00
Alerson Jorge
df155bed89 Merge branch 'new_feature' of https://github.com/ajotanc/WLED into new_feature 2023-10-19 16:39:17 -03:00
Alerson Jorge
3ee3630d22 Correction
Correction of the sliders in the `setEffectParameters()` function to eliminate the need to update the old tooltip span.
2023-10-19 16:39:00 -03:00
Alerson Jorge
6b2911b8c8 Merge branch '0_15' into new_feature 2023-10-19 15:20:32 -03:00
Blaz Kristan
d474200d7f Power button override (solves #3431)
Simple CSS optimisation.
2023-10-19 18:54:54 +02:00
Alerson Jorge
cc6f6277f6 Squashed commit of the following:
commit 1dab26bcbc
Author: Christian Schwinne <dev.aircoookie@gmail.com>
Date:   Thu Oct 19 13:29:46 2023 +0200

    Update Discord invite links to point to guidelines channel
2023-10-19 13:49:52 -03:00
Alerson Jorge
0521e988bc UI interface improvements 2023-10-19 12:47:37 -03:00
Alerson Jorge
832ff39eb6 Revert "UI interface improvements"
This reverts commit 134798e82f.
2023-10-19 12:45:41 -03:00
Alerson Jorge
134798e82f UI interface improvements
Improvements in tooltip creation
2023-10-19 12:41:08 -03:00
Alerson Jorge
b6f43966ea Some functionality improvements 2023-10-19 09:30:28 -03:00
Christian Schwinne
1dab26bcbc Update Discord invite links to point to guidelines channel 2023-10-19 13:29:46 +02:00
Blaz Kristan
afa7be6fdc CSS optimisation 2023-10-17 20:55:10 +02:00
Blaž Kristan
36290c20aa Merge pull request #3445 from Moustachauve/ota-lock-post-fix
Update response code when access is denied
2023-10-17 06:29:43 +02:00
Alerson Jorge
c33c7eb68e Correction related to enabling and disabling PXM in the User Interface 2023-10-16 19:27:11 -03:00
Alerson Jorge
5da33afecd Corrections made, added in a very accessible place
I added the possibility of using the tooltip on buttons (.btn) with the span inside the button;
Adjusted the tooltip css to center 100% in the middle
Some cleaning and correction of sele
2023-10-16 19:09:43 -03:00
Blaz Kristan
a49b49aaa9 Request throttling
UI/UX improvements
2023-10-16 18:34:51 +02:00
Christophe Gagnier
225fd0d05b Update response code when access is denied
Having the wrong pin would result in a server error (500). The more appropriate error code for that would be 401.
This also changes the page that asks for users to login from 200 to 401.
2023-10-16 03:19:14 -04:00
Alerson Jorge
c43b4f9cf0 Merge branch 'main' into pxmagic 2023-10-16 01:22:41 -03:00
Alerson Jorge
3581f4c87e Modifications 2023-10-16 01:18:00 -03:00
Alerson Jorge
7a4e0cc850 Revert "Modifications"
This reverts commit ba49da75de.
2023-10-16 01:15:21 -03:00
Alerson Jorge
ba49da75de Modifications 2023-10-16 01:10:40 -03:00
Blaz Kristan
f329a5950e Speed optimisations. 2023-10-15 13:06:40 +02:00
Blaž Kristan
d95158003c Merge pull request #3368 from tetele/usermod-ssdr-leading-zero
Usermod SSDR - allow configurable leading zero
2023-10-14 20:50:53 +02:00
Blaz Kristan
1be6223a31 Pixelmagic optimisation
Filter tooltips
2023-10-14 20:44:11 +02:00
Blaz Kristan
76178b423e Fix pixel magic 2023-10-14 18:10:37 +02:00
Blaz Kristan
195e537499 Merge branch '0_14_1' into 0_15 2023-10-14 17:39:20 +02:00
Blaž Kristan
418abc2b0a Merge pull request #3414 from DanCaveman/max-fan-pct-to-pwm-fan-usermod
added the max fan pct to the PWM-Fan usermod.
2023-10-14 17:31:38 +02:00
Blaz Kristan
277f0346f2 0.14.1 alpha 1 2023-10-13 20:42:19 +02:00
Blaz Kristan
6148cbb122 Merge branch 'main' into 0_14_1 2023-10-13 20:40:42 +02:00
Frank
3847bfc41a npm run build
plus fixing a small typo in VERSION
2023-10-13 13:55:13 +02:00
Frank
783424dd26 version bump 0.14.0 (release) 2023-10-13 13:45:25 +02:00
Blaz Kristan
4c1861dd27 Merge branch 'main' into 0_15 2023-10-12 23:08:32 +02:00
Blaz Kristan
7f6486c77d Custom palette gamma
Mode blending option
Optimisations
2023-10-12 23:06:39 +02:00
Blaz Kristan
1b2a50f9a3 Minor optimisation 2023-10-12 16:15:16 +02:00
Frank
5dadf92a62 also change second discord link in readme
seems this was missing in the previous PR.
2023-10-11 21:09:56 +02:00
Frank
76821addd7 Merge pull request #3437 from chrishultin/fix_discord_invite
Updating "Discord" invite to point to #general
2023-10-11 21:07:22 +02:00
Christopher
a769c55c72 Updating "Discord" invite to point to #general 2023-10-11 09:58:58 -06:00
Blaz Kristan
347ec91bfc notify() bugfix 2023-10-08 22:44:30 +02:00
Frank
4245767357 fix for partly uninitialized sound sync packets (audioreactive)
audioSyncPacket contains four "invisible" padding bytes added by the compiler. These need to be initialized to zero, as future versions of the protocol will make use of these fields.
2023-10-08 19:54:14 +02:00
Frank
74d196ad50 npm run build
chores
2023-10-06 16:37:12 +02:00
Frank
3c4649748d Merge pull request #3420 from mountainash/fix/duplicate-viewport-declaration
Remove duplicate `viewport` declaration, put lang=en into the right place
2023-10-06 16:27:25 +02:00
Frank
22985900a8 Merge pull request #3425 from mountainash/fix/git-ignore
.gitignore Reordered & grouped
2023-10-06 16:26:04 +02:00
Daniel Kaufman
86300a8e3c code review suggestions to make the code more efficient and cleaner 2023-10-05 09:49:00 -05:00
Mountain/\Ash
32ab2ae201 Allow /wled00/html_*.h files 2023-10-05 11:55:48 +02:00
Mountain/\Ash
23e4a2e28e Reordered & grouped 2023-10-05 10:26:51 +02:00
Mountain/\Ash
40515e62ac Ignoring HTML build output 2023-10-05 10:21:24 +02:00
Daniel Kaufman
b56490650c fix for maxPWMValuePct being ignored 2023-10-05 00:37:51 -05:00
Mountain/\Ash
b5751795b5 Fix: lang moved to the correct HTML element 2023-10-04 21:42:15 +02:00
Mountain/\Ash
43d6151506 Fix: removing duplicate viewport declaration
- removed unneeded self-closing slash
2023-10-04 21:37:10 +02:00
Daniel Kaufman
8ccfb606c0 added the max fan pct to the PWM-Fan usermod. This allows high speed (loud fans) like delta fans to cap at a maximum percentage to keep noise down. 2023-10-03 12:42:51 -05:00
Blaz Kristan
3b74cd5676 Merge branch 'main' into 0_15 2023-10-02 21:20:20 +02:00
Frank
26e766ee19 Merge pull request #3406 from Aircoookie/sunset_accuracy_fix
fix for #3400 - wrong sunset time 00:00
2023-10-02 19:58:06 +02:00
Blaz Kristan
6f96dc8f23 Gamma correction for custom palettes (#3399).
Bugfix for audio palettes.
2023-10-01 21:15:14 +02:00
Frank
1c3fdb73fb optimization: only use "float" math functions
- saves 5KB flash and some RAM
-allow to build with -D WLED_USE_UNREAL_MATH, to restore old behaviour and save another 6KB flash
2023-10-01 19:04:30 +02:00
Blaz Kristan
332be7edd6 Build bump. 2023-10-01 13:38:10 +02:00
Blaz Kristan
76e0e935f0 Merge branch 'bugfix' into 0_15 2023-10-01 13:27:18 +02:00
Blaz Kristan
7e6eb65950 Fix for #3403 2023-10-01 13:26:31 +02:00
Blaz Kristan
b0a56a431b Fix for #3405 2023-10-01 13:04:05 +02:00
Frank
166316e0c5 fix for #3400
replace low_accuracy math functions (sint_t, cos_t, atan_t, ...) with standard libm functions that have higher accuracy.
2023-09-30 23:34:02 +02:00
Alerson Jorge
4194e66d81 Corrections
It was missing to upload this file in the commit.

- PXMagic is enabled by default in the Preset tab, and can be disabled in Settings, UI session
2023-09-28 12:41:52 -03:00
Alerson Jorge
c9b9d86892 Corrections
Topic 1, 2
I made the change, took it off the website and put it in the Preset section under the +Preset, +Playlist buttons, it was in a good location.

Topic 3, 4
It reloads because it needs the updated information so that it can create the preset correctly with all the information generated from the selected image.

Topic 5
The change has already been made, it just hasn't gone up yet, it's default upload

Topic 6, 7
Fixed
2023-09-28 09:41:48 -03:00
Alerson Jorge
f2d00e6e42 Merge remote-tracking branch 'upstream/main' into pxmagic 2023-09-28 09:10:47 -03:00
Alerson Jorge
acfc166a11 Corrections 2023-09-28 09:09:42 -03:00
Blaz Kristan
3255382132 Add JSON override for ESP-NOW remote. 2023-09-27 20:39:26 +02:00
Blaz Kristan
25831bfb60 Codename "Ray of light" 2023-09-26 20:34:52 +02:00
Blaz Kristan
d011ca0626 Merge branch 'main' into 0_15 2023-09-24 16:59:27 +02:00
Blaz Kristan
5eadbe7ecd FX update
- Meteor: trail & 0.13 behaviour
- Meteor Smooth: train & 0.13 behaviour
- Scrolling Text: rotation
2023-09-24 16:48:59 +02:00
Frank
ea7e0c6204 npm run build
regenerate UI files from latest sources.
2023-09-23 21:22:14 +02:00
Frank
2dcb126e6d version bump 0.14.0-b6
beta release preparation.
2023-09-23 21:08:11 +02:00
Frank
438525e8ec AR: use bandpass filter for analog input
Many bad quality analog mics are not centered properly at 1.6V, but stuck at 0V or stuck at 3.3V in silence. The bandpass filter removes DC offsets and improve signal quality.
2023-09-23 16:48:45 +02:00
Frank
eb66a403d9 Merge pull request #3377 from christianpatterson/audioreactive-initialize-i2ssource-bugfix
Fix ES8388Source & ES7243 initialization.
2023-09-19 16:32:10 +02:00
Frank
26ac612474 fix wrong signature of SPH0654::initialize()
* debug messages added to different initializers
* SPH0654::initialize() was having a wrong signature: uint8 instead of int8.

C++ can be a real bastard ;-)
2023-09-19 15:27:41 +02:00
Blaz Kristan
2e28df3e8a Merge 'main' to 0_15 2023-09-18 21:28:43 +02:00
Frank
43613e3b10 Matrix effect speedup
Typically, more than 50% of pixels are black. 
This optimization avoids to fade and rewrite already black pixels.
2023-09-18 15:56:50 +02:00
Frank
555dd2e726 matrix: fix for a corner case (e.g. gapmaps)
workaround for a corner case; if the reference pixels falls into a "gap" then gPC returns BLACK. Solutions is to reject BLACK.
2023-09-18 15:34:53 +02:00
Frank
3260f46543 bugfix for #3375
* improves robustness of the Matrix effect, by dynamically adjusting the "reference color" used to identify "falling code" head pixels.
* a bit faster, as I've removed the need to scan all pixels a second time for "black screen" detection.

Its still not perfect, and the main loop could be simplified a lot by leveraging on the fact that all changes actually happen in the top row, and "falling" is actually just moving everything down by one pixel.
2023-09-18 14:57:15 +02:00
Christian Patterson
f9de23402a Remove obsolete I2C parameters from AudioSource::initialize and all overriding methods. 2023-09-17 19:10:18 -05:00
Christian Patterson
9e155cf94a Fix ES8388Source & ES7243 initialization.
Update ES8388Source::initialize and ES7243::initialize method signatures to match I2SSource::initialize so that when initialize is called on a AudioSource pointer the child class's method is used.
2023-09-17 14:12:20 -05:00
Blaz Kristan
dcace43ce2 Merge branch 'main' into 0_15 2023-09-17 20:39:13 +02:00
Frank
527e3d6cd0 Merge pull request #3373 from Aircoookie/ripple-tweak
2D Ripple and Meteor effect tweak
2023-09-17 18:48:43 +02:00
Blaz Kristan
befce5b887 Typo fix and preset reload after restore 2023-09-16 20:29:32 +02:00
Blaz Kristan
9929c96650 Restore corrupt presets from backup 2023-09-16 14:41:47 +02:00
Blaz Kristan
c7d45c28cf Meteor effect change
- remedy for #3374
2023-09-16 12:30:57 +02:00
Blaz Kristan
87cc3fd714 2D Ripple effect tweak
for #3370
2023-09-16 00:21:10 +02:00
Frank
282d58a6fe audioreactive: stack size tuning
This gives ~3KB extra free heap on -S2.
2023-09-14 18:26:57 +02:00
Blaz Kristan
b4aec91d67 Bugfix 2023-09-12 23:44:18 +02:00
Blaz Kristan
d2a149ee23 Version 2023-09-12 17:07:31 +02:00
Blaz Kristan
8a72f94b3d Merge branch 'main' into 0_15 2023-09-12 17:02:40 +02:00
Blaz Kristan
fc4ed1c50b Fix for #3369 2023-09-12 16:48:32 +02:00
Blaz Kristan
ae1d0a18f3 Separate mode blending option 2023-09-12 06:17:06 +02:00
tetele
9709ca331e Backwards compatible default value 2023-09-11 16:25:36 +03:00
tetele
b4a9641c31 Add option for leading zero 2023-09-11 15:59:04 +03:00
Blaz Kristan
cc68e6b6e6 Squashed commit of the following:
Remove sync receive
Disallow 2D effects on non-2D segments
Optimisations
Sync clarification
AR palettes
Return of 2 audio simulations
Bugfix in sync #3344
    - remove excessive segments
    - ignore inactive segments if not syncing bounds
    - send UDP/WS on segment change
    - pop_back() when removing last segment
Add pairing support for ESP-NOW sync
Reduce string RAM footprint
UDP parse optimisation
Make WizMote work with sync.
ESP-NOW wireless sync POC.
    - caveat: devices have to be on the same channel
    - clashes with WizMote handling ATM
2023-09-10 18:52:14 +02:00
cschwinne
6be5360bdd Remove temp workaround 2023-08-01 00:48:18 +02:00
cschwinne
2b9007958b Merge branch 'main' into gif 2023-07-31 18:26:35 +02:00
Aircoookie
2f9c126d34 GIF Error codes 2023-07-31 18:13:03 +02:00
Alerson Jorge
37dc17ae15 Correction 2023-07-20 13:50:32 -03:00
Alerson Jorge
cd6b3d7dee Merge remote-tracking branch 'upstream/main' into pxmagic 2023-07-20 13:32:35 -03:00
Alerson Jorge
aec0bc5029 Updating pxmagic and WLED UI 2023-07-20 13:20:13 -03:00
Alerson Jorge
e010e67717 Revert "Updating pxmagic and inserting it in the WLED UI with option to enable and disable."
This reverts commit 44197d91c6.
2023-07-20 12:30:02 -03:00
Alerson Jorge
44197d91c6 Updating pxmagic and inserting it in the WLED UI with option to enable and disable. 2023-07-20 12:25:16 -03:00
cschwinne
ed69692f08 Dynamic 2023-07-01 15:36:41 +02:00
cschwinne
7119999df8 Gamma and speed control 2023-07-01 13:20:10 +02:00
cschwinne
c16462a0ce Experimental GIF support 2023-06-30 23:59:29 +02:00
Christian Schwinne
94aeb19245 Enable pxmagic by default 2023-06-22 11:18:58 +02:00
Alerson Jorge
0d287283d4 Corrections and performance improvements 2023-06-16 20:21:59 -03:00
Aircoookie
ea964124d6 Remove IE compatibility tag
(saves a few bytes and IE10 is over 10 years old and unsupported)
Correct HTML language attribute
(Chrome would show a popup asking to translate from Portugese)
2023-06-16 12:02:09 +02:00
Alerson Jorge
c361c34b16 Corrections and performance improvements 2023-06-15 22:10:54 -03:00
Alerson Jorge
68860ae866 Adding Pixel Magic Tool to WLED 2023-06-14 19:38:11 -03:00
Alerson Jorge
61637f12c7 Revert "Adding Pixel Magic Tool to WLED"
This reverts commit b4f08fa8d5.
2023-06-14 19:34:12 -03:00
Alerson Jorge
b4f08fa8d5 Adding Pixel Magic Tool to WLED 2023-06-13 19:42:16 -03:00
180 changed files with 13558 additions and 19143 deletions

View File

@@ -1,4 +1,4 @@
name: PlatformIO CI
name: WLED CI
on: [push, pull_request]
@@ -8,17 +8,11 @@ jobs:
name: Gather Environments
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Cache pip
uses: actions/cache@v3
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- uses: actions/setup-python@v4
with:
python-version: '3.9'
python-version: '3.12'
cache: 'pip'
- name: Install PlatformIO
run: pip install -r requirements.txt
- name: Get default environments
@@ -38,54 +32,63 @@ jobs:
matrix:
environment: ${{ fromJSON(needs.get_default_envs.outputs.environments) }}
steps:
- uses: actions/checkout@v3
- name: Cache pip
uses: actions/cache@v3
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
cache: 'npm'
- run: npm ci
- name: Cache PlatformIO
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ~/.platformio
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
path: |
~/.platformio/.cache
~/.buildcache
build_output
key: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-${{ hashFiles('wled00/**', 'usermods/**') }}
restore-keys: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-
- name: Set up Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: '3.9'
python-version: '3.12'
cache: 'pip'
- name: Install PlatformIO
run: pip install -r requirements.txt
- name: Build firmware
env:
WLED_RELEASE: True
run: pio run -e ${{ matrix.environment }}
- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v4
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
build_output/release/*.bin
build_output/release/*_ESP02*.bin.gz
release:
name: Create Release
runs-on: ubuntu-latest
needs: [get_default_envs, build]
needs: build
if: startsWith(github.ref, 'refs/tags/')
steps:
- uses: actions/download-artifact@v2
- uses: actions/download-artifact@v4
with:
name: firmware-release
merge-multiple: true
- name: Create draft release
uses: softprops/action-gh-release@v1
with:
draft: True
files: |
*.bin
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
*.bin.gz
testCdata:
name: Test cdata.js
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'npm'
- run: npm ci
- run: npm test

33
.gitignore vendored
View File

@@ -1,21 +1,24 @@
.pio
.cache
.clang-format
.direnv
.DS_Store
.idea
.pio
.pioenvs
.piolibdeps
.vscode
/wled00/Release
/wled00/extLibs
/platformio_override.ini
/wled00/my_config.h
/build_output
.DS_Store
.gitignore
.clang-format
node_modules
.idea
.direnv
wled-update.sh
esp01-update.sh
/wled00/LittleFS
platformio_override.ini
replace_fs.py
wled00/wled00.ino.cpp
wled-update.sh
/build_output/
/node_modules/
/wled00/extLibs
/wled00/LittleFS
/wled00/my_config.h
/wled00/Release
/wled00/wled00.ino.cpp
/wled00/html_*.h

6
.vscode/tasks.json vendored
View File

@@ -9,8 +9,8 @@
],
"dependsOrder": "sequence",
"problemMatcher": [
"$platformio",
],
"$platformio"
]
},
{
"type": "PlatformIO",
@@ -18,7 +18,7 @@
"task": "Build",
"group": {
"kind": "build",
"isDefault": true,
"isDefault": true
},
"problemMatcher": [
"$platformio"

View File

@@ -1,12 +1,255 @@
## WLED changelog
#### Build 2404100
- Internals: #3859, #3862, #3873, #3875
- Prefer I2S1 over RMT on ESP32
- usermod for Adafruit MAX17048 (#3667 by @ccruz09)
- Runtime detection of ESP32 PICO, general PSRAM support
- Extend JSON API "info" object
- add "clock" - CPU clock in MHz
- add "flash" - flash size in MB
- Fix for #3879
- Analog PWM fix for ESP8266 (#3887 by @gaaat98)
- Fix for #3870 (#3880 by @DedeHai)
- ESP32 S3/S2 touch fix (#3798 by @DedeHai)
- PIO env. PSRAM fix for S3 & S3 with 4M flash
- audioreactive always included for S3 & S2
- Fix for #3889
- BREAKING: Effect: modified KITT (Scanner) (#3763)
#### Build 2403280
- Individual color channel control for JSON API (fixes #3860)
- "col":[int|string|object|array, int|string|object|array, int|string|object|array]
int = Kelvin temperature or 0 for black
string = hex representation of [WW]RRGGBB
object = individual channel control {"r":0,"g":127,"b":255,"w":255}, each being optional (valid to send {})
array = direct channel values [r,g,b,w] (w element being optional)
- runtime selection for CCT IC (Athom 15W bulb)
- #3850 (by @w00000dy)
- Rotary encoder palette count bugfix
- bugfixes and optimisations
#### Build 2403240
- v0.15.0-b2
- WS2805 support (RGB + WW + CW, 600kbps)
- Unified PSRAM use
- NeoPixelBus v2.7.9
- Ubiquitous PSRAM mode for all variants of ESP32
- SSD1309_64 I2C Support for FLD Usermod (#3836 by @THATDONFC)
- Palette cycling fix (add support for `{"seg":[{"pal":"X~Y~"}]}` or `{"seg":[{"pal":"X~Yr"}]}`)
- FW1906 Support (#3810 by @deece and @Robert-github-com)
- ESPAsyncWebServer 2.2.0 (#3828 by @willmmiles)
- Bugfixes: #3843, #3844
#### Build 2403190
- limit max PWM frequency (fix incorrect PWM resolution)
- Segment UI bugfix
- Updated AsyncWebServer (by @wlillmmiles)
- Simpler boot preset (fix for #3806)
- Effect: Fix for 2D Drift animation (#3816 by @BaptisteHudyma)
- Effect: Add twin option to 2D Drift
- MQTT cleanup
- DDP: Support sources that don't push (#3833 by @willmmiles)
- Usermod: Tetris AI usermod (#3711 by @muebau)
#### Build 2403171
- merge 0.14.2 changes into 0.15
#### Build 2403070
- Add additional segment options when controlling over e1.31 (#3616 by @demophoon)
- LockedJsonResponse: Release early if possible (#3760 by @willmmiles)
- Update setup-node and cache usermods in wled-ci.yml (#3737 by @WoodyLetsCode)
- Fix preset sorting (#3790 by @WoodyLetsCode)
- compile time button configuration #3792
- remove IR config if not compiled
- additional string optimisations
- Better low brightness level PWM handling (fixes #2767, #2868)
#### Build 2402290
- Multiple analog button fix for #3549
- Preset caching on chips with PSRAM (credit @akaricchi)
- Fixing stairway usermod and adding buildflags (by @lost-hope)
- ESP-NOW packet modification
- JSON buffer lock error messages / Reduce wait time for lock to 100ms
- Reduce string RAM usage for ESP8266
- Fixing a potential array bounds violation in ESPDMX
- Move timezone table to PROGMEM (#3766 by @willmmiles)
- Reposition upload warning message. (fixes #3778)
- ABL display fix & optimisation
- Add virtual Art-Net RGBW option (#3783 by @shammy642)
#### Build 2402090
- Added new Ethernet controller RGB2Go Tetra (duplicate of ESP3DEUXQuattro)
- Usermod: httpPullLightControl (#3560 by @roelbroersma)
- DMX: S2 & C3 support via modified ESPDMX
- Bugfix: prevent cleaning of JSON buffer after a failed lock attempt (BufferGuard)
- Product/Brand override (API & AP SSID) (#3750 by @moustachauve)
#### Build 2402060
- WLED version 0.15.0-b1
- Harmonic Random Cycle palette (#3729 by @dedehai)
- Multi PIR sensor usermod (added support for attaching multiple PIR sensors)
- Removed obsolete (and nonfunctional) usermods
#### Build 2309120 till build 2402010
- WLED version 0.15.0-a0
- Multi-WiFi support. Add up to 3 (or more via cusom compile) WiFis to connect to (with help from @JPZV)
- Temporary AP. Use your WLED in public with temporary AP.
- Github CI build system enhancements (#3718 by @WoodyLetsCode)
- Accessibility: Node list ( #3715 by @WoodyLetsCode)
- Analog clock overlay enhancement (#3489 by @WoodyLetsCode)
- ESP32-POE-WROVER from Olimex ethernet support (#3625 by @m-wachter)
- APA106 support (#3580 by @itstefanjanos)
- BREAKING: Effect: updated Palette effect to support 2D (#3683 by @TripleWhy)
- "SuperSync" from WLED MM (by @MoonModules)
- Effect: DNA Spiral Effect Speed Fix (#3723 by @Derek4aty1)
- Fix for #3693
- Orange flash fix (#3196) for transitions
- Add own background image upload (#3596 by @WoodyLetsCode)
- WLED time overrides (`WLED_NTP_ENABLED`, `WLED_TIMEZONE`, `WLED_UTC_OFFSET`, `WLED_LAT` and `WLED_LON`)
- Better sorting and naming of static palettes (by @WoodyLetsCode)
- ANIMartRIX usermod and effects (#3673 by @netmindz)
- Use canvas instead of CSS gradient for liveview (#3621 by @zanhecht)
- Fix for #3672
- ColoOrderMap W channel swap (color order overrides now have W swap)
- En-/disable LED maps when receiving realtime data (#3554 by @ezcGman)
- Added PWM frequency selection to UI (Settings)
- Automatically build UI before compiling (#3598, #3666 by @WoodyLetsCode)
- Internal: Added *suspend* API to `strip` (`WS2812FX class`)
- Possible fix for #3589 & partial fix for #3605
- MPU6050 upgrade (#3654 by @willmmiles)
- UI internals (#3656 by @WoodyLetsCode)
- ColorPicker fix (#3658 by @WoodyLetsCode)
- Global JSON buffer guarding (#3648 by @willmmiles, resolves #3641, #3312, #3367, #3637, #3646, #3447)
- Effect: Fireworks 1D (fix for matrix trailing strip)
- BREAKING: Reduced number of segments (12) on ESP8266 due to less available RAM
- Increased available effect data buffer (increases more if board has PSRAM)
- Custom palette editor mobile UI enhancement (by @imeszaros)
- Per port Auto Brightness Limiter (ABL)
- Use PSRAM for JSON buffer (double size, larger ledmaps, up to 2k)
- Reduced heap fragmentation by allocating ledmap array only once and not deallocating effect buffer
- HTTP retries on failed UI load
- UI Search: scroll to top (#3587 by @WoodyLetsCode)
- Return to inline iro.js and rangetouch.js (#3597 by @WoodyLetsCode)
- Better caching (#3591 by @WoodyLetsCode)
- Do not send 404 for missing `skin.css` (#3590 by @WoodyLetsCode)
- Simplified UI rework (#3511 by @WoodyLetsCode)
- Domoticz device ID for PIR and Temperature usermods
- Bugfix for UCS8904 `hasWhite()`
- Better search in UI (#3540 by @WoodyLetsCode)
- Seeding FastLED PRNG (#3552 by @TripleWhy)
- WIZ Smart Button support (#3547 by @micw)
- New button type (button switch, fix for #3537)
- Pixel Magic Tool update (#3483 by @ajotanc)
- Effect: 2D Matrix fix for gaps
- Bugfix #3526, #3533, #3561
- Spookier Halloween Eyes (#3501)
- Compile time options for Multi Relay usermod (#3498)
- Effect: Fix for Dissolve (#3502)
- Better reverse proxy support (nested paths)
- Implement global JSON API boolean toggle (i.e. instead of "var":true or "var":false -> "var":"t").
- Sort presets by ID
- Fix for #3641, #3312, #3367, #3637, #3646, #3447, #3632, #3496, #2922, #3593, #3514, #3522, #3578 (partial), #3606 (@WoodyLetsCode)
- Improved random bg image and added random bg image options (@WoodyLetsCode, #3481)
- Audio palettes (Audioreactive usermod, credit @netmindz)
- Better UI tooltips (@ajotnac, #3464)
- Better effect filters (filter dropdown)
- UDP sync fix (for #3487)
- Power button override (solves #3431)
- Additional HTTP request throttling (ESP8266)
- Additional UI/UX improvements
- Segment class optimisations (internal)
- ESP-NOW sync
- ESP-NOW Wiz remote JSON overrides (similar to IR JSON) & bugfixes
- Gamma correction for custom palettes (#3399).
- Restore presets from browser local storage
- Optional effect blending
- Restructured UDP Sync (internal)
- Remove sync receive
- Sync clarification
- Disallow 2D effects on non-2D segments
- Return of 2 audio simulations
- Bugfix in sync #3344 (internal)
- remove excessive segments
- ignore inactive segments if not syncing bounds
- send UDP/WS on segment change
- pop_back() when removing last segment
#### Build 2403170
- WLED 0.14.2 release
#### Build 2403110
- Beta WLED 0.14.2-b2
- New AsyncWebServer (improved performance and reduced memory use)
- New builds for ESP8266 with 160MHz CPU clock
- Fixing stairway usermod and adding buildflags (#3758 by @lost-hope)
- Fixing a potential array bounds violation in ESPDMX
- Reduced RAM usage (moved strings and TZ data (by @willmmiles) to PROGMEM)
- LockedJsonResponse: Release early if possible (by @willmmiles)
#### Build 2402120
- Beta WLED 0.14.2-b1
- Possible fix for #3589 & partial fix for #3605
- Prevent JSON buffer clear after failed lock attempt
- Multiple analog button fix for #3549
- UM Audioreactive: add two compiler options (#3732 by @wled-install)
- Fix for #3693
#### Build 2401141
- Official release of WLED 0.14.1
- Fix for #3566, #3665, #3672
- Sorting of palettes in custom palette editor (#3674 by @WoodyLetsCode)
#### Build 2401060
- Version bump: 0.14.1-b3
- Global JSON buffer guarding (#3648 by @willmmiles, resolves #3641, #3312, #3367, #3637, #3646, #3447)
- Fix for #3632
- Custom palette editor mobile UI enhancement (#3617 by @imeszaros)
- changelog update
#### Build 2312290
- Fix for #3622, #3613, #3609
- Various tweaks and fixes
- changelog update
#### Build 2312230
- Version bump: 0.14.1-b2
- Fix for Pixel Magic button
- Fix for #2922 (option to force WiFi PHY mode to G on ESP8266)
- Fix for #3601, #3400 (incorrect sunrise/sunset, #3612 by @softhack007)
#### Build 2312180
- Bugfixes (#3593, #3490, #3573, #3517, #3561, #3555, #3541, #3536, #3515, #3522, #3533, #3508)
- Various other internal cleanups and optimisations
#### Build 2311160
- Version bump: 0.14.1-b1
- Bugfixes (#3526, #3502, #3496, #3484, #3487, #3445, #3466, #3296, #3382, #3312)
- New feature: Sort presets by ID
- New usermod: LDR sensor (#3490 by @JeffWDH)
- Effect: Twinklefox & Tinklecat metadata fix
- Effect: separate #HH and #MM for Scrolling Text (#3480)
- SSDR usermod enhancements (#3368)
- PWM fan usermod enhancements (#3414)
#### Build 2310010, build 2310130
- Release of WLED version 0.14.0 "Hoshi"
- Bugfixes for #3400, #3403, #3405
- minor HTML optimizations
- audioreactive: bugfix for UDP sound sync (partly initialized packets)
#### Build 2309240
- Release of WLED beta version 0.14.0-b6 "Hoshi"
- Effect bugfixes and improvements (Meteor, Meteor Smooth, Scrolling Text)
- audioreactive: bugfixes for ES8388 and ES7243 init; minor improvements for analog inputs
#### Build 2309100
- Release of WLED beta version 0.14.0-b5 "Hoshi"
- New standard esp32 build with audioreactive
- Effect blending bugfixes, and minor optimizations
#### Build 2309050
- Effect blending (#3311) (finally efect transitions!)
- Effect blending (#3311) (finally effect transitions!)
*WARNING*: May not work well with ESP8266, with plenty of segments or usermods (low RAM condition)!!!
- Added receive and send sync groups to JSON API (#3317) (you can change sync groups using preset)
- Internal temperature usermod (#3246)
@@ -35,7 +278,7 @@
- Preset cycle bugfix (#3262)
- Rotary encoder ALT fix for large LED count (#3276)
- effect updates (2D Plasmaball), `blur()` speedup
- On/Off toggle from nodes view (may show unknow device type on older versions) (#3291)
- On/Off toggle from nodes view (may show unknown device type on older versions) (#3291)
- various fixes and improvements (ABL, crashes when changing presets with different segments)
#### Build 2306270
@@ -48,7 +291,7 @@
#### Build 2306210
- 0.14.0-b3 release
- respect global I2C in all usermods (no local initilaisation of I2C bus)
- respect global I2C in all usermods (no local initialization of I2C bus)
- Multi relay usermod compile-time enabled option (-D MULTI_RELAY_ENABLED=true|false)
#### Build 2306180
@@ -74,7 +317,7 @@
#### Build 2306020
- Support for segment sets (PR #3171)
- Reduce sound simulation modes to 2 to facilitiate segment sets
- Reduce sound simulation modes to 2 to facilitate segment sets
- Trigger button immediately on press if all configured presets are the same (PR #3226)
- Changes for allowing Alexa to change light color to White when auto-calculating from RGB (PR #3211)
@@ -411,7 +654,7 @@
- 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
- Fixed transition manually updated in preset overridden by field value
#### Build 2108050
@@ -940,7 +1183,7 @@
#### Build 2011040
- Inversed Rain direction (fixes #1147)
- Inverted Rain direction (fixes #1147)
#### Build 2011010
@@ -1151,7 +1394,7 @@
- Added module info page to web UI
- Added realtime override functionality to web UI
- Added individial segment power and brightness to web UI
- Added individual segment power and brightness to web UI
- Added feature to one-click select single segment only by tapping segment name
- Removed palette jumping to default if color is changed

View File

@@ -2,6 +2,20 @@
Here are a few suggestions to make it easier for you to contribute!
### Describe your PR
Please add a description of your proposed code changes. It does not need to be an exhaustive essay, however a PR with no description or just a few words might not get accepted, simply because very basic information is missing.
A good description helps us to review and understand your proposed changes. For example, you could say a few words about
* what you try to achieve (new feature, fixing a bug, refactoring, security enhancements, etc.)
* how your code works (short technical summary - focus on important aspects that might not be obvious when reading the code)
* testing you performed, known limitations, open ends you possibly could not solve.
* any areas where you like to get help from an experienced maintainer (yes WLED has become big 😉)
### Target branch for pull requests
Please make all PRs against the `0_15` branch.
### Code style
When in doubt, it is easiest to replicate the code style you find in the files you want to edit :)
@@ -73,6 +87,6 @@ Good:
<!-- 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.
There is no hard character limit for a comment within a line,
though as a rule of thumb consider wrapping after 120 characters.
Inline comments are OK if they describe that line only and are not exceedingly wide.

2290
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "wled",
"version": "0.14.0-b5",
"version": "0.15.0-b2",
"description": "Tools for WLED project",
"main": "tools/cdata.js",
"directories": {
@@ -9,6 +9,7 @@
},
"scripts": {
"build": "node tools/cdata.js",
"test": "node --test",
"dev": "nodemon -e js,html,htm,css,png,jpg,gif,ico,js -w tools/ -w wled00/data/ -x node tools/cdata.js"
},
"repository": {
@@ -22,10 +23,9 @@
},
"homepage": "https://github.com/Aircoookie/WLED#readme",
"dependencies": {
"clean-css": "^4.2.3",
"html-minifier-terser": "^5.1.1",
"clean-css": "^5.3.3",
"html-minifier-terser": "^7.2.0",
"inliner": "^1.13.1",
"nodemon": "^2.0.20",
"zlib": "^1.0.5"
"nodemon": "^3.0.2"
}
}

3
pio-scripts/build_ui.py Normal file
View File

@@ -0,0 +1,3 @@
Import('env')
env.Execute("npm run build")

View File

@@ -4,6 +4,7 @@ import shutil
import gzip
OUTPUT_DIR = "build_output{}".format(os.path.sep)
#OUTPUT_DIR = os.path.join("build_output")
def _get_cpp_define_value(env, define):
define_list = [item[-1] for item in env["CPPDEFINES"] if item[0] == define]
@@ -13,14 +14,24 @@ def _get_cpp_define_value(env, define):
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)
def _create_dirs(dirs=["map", "release", "firmware"]):
for d in dirs:
if not os.path.isdir("{}{}".format(OUTPUT_DIR, d)):
os.mkdir("{}{}".format(OUTPUT_DIR, d))
os.makedirs(os.path.join(OUTPUT_DIR, d), exist_ok=True)
def create_release(source):
release_name = _get_cpp_define_value(env, "WLED_RELEASE_NAME")
if release_name:
version = _get_cpp_define_value(env, "WLED_VERSION")
release_file = os.path.join(OUTPUT_DIR, "release", f"WLED_{version}_{release_name}.bin")
release_gz_file = release_file + ".gz"
print(f"Copying {source} to {release_file}")
shutil.copy(source, release_file)
bin_gzip(release_file, release_gz_file)
else:
variant = env["PIOENV"]
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
print(f"Copying {source} to {bin_file}")
shutil.copy(source, bin_file)
def bin_rename_copy(source, target, env):
_create_dirs()
@@ -28,42 +39,21 @@ def bin_rename_copy(source, target, env):
# 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)
create_release(str(target[0]))
# 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:
def bin_gzip(source, target):
# only create gzip for esp8266
if not env["PIOPLATFORM"] == "espressif8266":
return
print(f"Creating gzip file {target} from {source}")
with open(source,"rb") as fp:
with gzip.open(target, "wb", compresslevel = 9) as f:
shutil.copyfileobj(fp, f)
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_rename_copy, bin_gzip])
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", bin_rename_copy)

View File

@@ -9,39 +9,8 @@
# (use `platformio_override.ini` when building for your own board; see `platformio_override.ini.sample` for an example)
# ------------------------------------------------------------------------------
# CI binaries
; default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth # ESP32 variant builds are temporarily excluded from CI due to toolchain issues on the GitHub Actions Linux environment
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, esp32dev_audioreactive, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB, esp32s3dev_8MB_PSRAM_opi
# Release binaries
; default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB
# 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, nodemcuv2, esp32_eth, anavi_miracle_controller, esp07, esp01_1m_full, m5atom, h803wf, d1_mini_ota, heltec_wifi_kit_8, esp8285_H801, d1_mini_debug, wemos_shield_esp32, elekstube_ips
# Single binaries (uncomment your board)
; default_envs = elekstube_ips
; default_envs = nodemcuv2
; default_envs = esp8266_2m
; default_envs = esp01_1m_full
; default_envs = esp07
; default_envs = d1_mini
; default_envs = heltec_wifi_kit_8
; default_envs = h803wf
; default_envs = d1_mini_debug
; default_envs = d1_mini_ota
; default_envs = esp32dev
; default_envs = esp8285_4CH_MagicHome
; default_envs = esp8285_H801
; default_envs = d1_mini_5CH_Shojo_PCB
; default_envs = wemos_shield_esp32
; default_envs = m5atom
; default_envs = esp32_eth
; default_envs = esp32dev_qio80
; default_envs = esp32_eth_ota1mapp
; default_envs = esp32s2_saola
; default_envs = esp32c3dev
; default_envs = lolin_s2_mini
# CI/release binaries
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, nodemcuv2_160, esp8266_2m_160, esp01_1m_full_160, esp32dev, esp32_eth, esp32dev_audioreactive, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB, esp32s3dev_8MB_PSRAM_opi, esp32s3_4M_PSRAM_qspi, esp32_wrover
src_dir = ./wled00
data_dir = ./wled00/data
@@ -60,9 +29,9 @@ 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_2_0 = espressif8266@3.2.0
arduino_core_4_1_0 = espressif8266@4.1.0
arduino_core_3_1_2 = espressif8266@4.2.0
arduino_core_3_0_2 = espressif8266@3.2.0
arduino_core_3_1_0 = espressif8266@4.1.0
arduino_core_3_1_2 = espressif8266@4.2.1
# Development platforms
arduino_core_develop = https://github.com/platformio/platform-espressif8266#develop
@@ -72,14 +41,13 @@ arduino_core_git = https://github.com/platformio/platform-espressif8266#feature/
platform_wled_default = ${common.arduino_core_3_1_2}
# We use 2.7.4.7 for all, includes PWM flicker fix and Wstring optimization
#platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7
platform_packages = platformio/framework-arduinoespressif8266
platformio/toolchain-xtensa @ ~2.100300.220621 #2.40802.200502
platform_packages = platformio/toolchain-xtensa @ ~2.100300.220621 #2.40802.200502
platformio/tool-esptool #@ ~1.413.0
platformio/tool-esptoolpy #@ ~1.30000.0
## previous platform for 8266, in case of problems with the new one
## you'll need makuna/NeoPixelBus@ 2.6.9 for arduino_core_3_2_0, which does not support Ucs890x
;; platform_wled_default = ${common.arduino_core_3_2_0}
## you'll need makuna/NeoPixelBus@ 2.6.9 for arduino_core_3_0_2, which does not support Ucs890x
;; platform_wled_default = ${common.arduino_core_3_0_2}
;; platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7
;; platformio/toolchain-xtensa @ ~2.40802.200502
;; platformio/tool-esptool @ ~1.413.0
@@ -132,11 +100,7 @@ build_flags =
-D DECODE_SONY=true
-D DECODE_SAMSUNG=true
-D DECODE_LG=true
;-Dregister= # remove warnings in C++17 due to use of deprecated register keyword by the FastLED library ;; warning: this breaks framework code on ESP32-C3 and ESP32-S2
-DWLED_USE_MY_CONFIG
; -D USERMOD_SENSORSTOMQTT
#For ADS1115 sensor uncomment following
; -D USERMOD_ADS1115
build_unflags =
@@ -155,6 +119,7 @@ extra_scripts =
post:pio-scripts/output_bins.py
post:pio-scripts/strip-floats.py
pre:pio-scripts/user_config_copy.py
pre:pio-scripts/build_ui.py
# ------------------------------------------------------------------------------
# COMMON SETTINGS:
@@ -163,10 +128,8 @@ extra_scripts =
framework = arduino
board_build.flash_mode = dout
monitor_speed = 115200
# slow upload speed (comment this out with a ';' when building for development use)
# slow upload speed but most compatible (use platformio_override.ini to use faster speed)
upload_speed = 115200
# fast upload speed (remove ';' when building for development use)
; upload_speed = 921600
# ------------------------------------------------------------------------------
# LIBRARIES: required dependencies
@@ -179,22 +142,39 @@ lib_compat_mode = strict
lib_deps =
fastled/FastLED @ 3.6.0
IRremoteESP8266 @ 2.8.2
makuna/NeoPixelBus @ 2.7.5
https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.7
makuna/NeoPixelBus @ 2.7.9
https://github.com/Aircoookie/ESPAsyncWebServer.git @ 2.2.1
# for I2C interface
;Wire
# ESP-NOW library
;gmag11/QuickESPNow @ ~0.7.0
https://github.com/blazoncek/QuickESPNow.git#optional-debug
#For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line
#TFT_eSPI
#For compatible OLED display uncomment following
#U8g2 #@ ~2.33.15
#olikraus/U8g2 #@ ~2.33.15
#For Dallas sensor uncomment following
#OneWire @ ~2.3.7
#paulstoffregen/OneWire @ ~2.3.8
#For BME280 sensor uncomment following
#BME280 @ ~3.0.0
; adafruit/Adafruit BMP280 Library @ 2.1.0
; adafruit/Adafruit CCS811 Library @ 1.0.4
; adafruit/Adafruit Si7021 Library @ 1.4.0
;adafruit/Adafruit BMP280 Library @ 2.1.0
;adafruit/Adafruit CCS811 Library @ 1.0.4
;adafruit/Adafruit Si7021 Library @ 1.4.0
#For ADS1115 sensor uncomment following
; adafruit/Adafruit BusIO @ 1.13.2
; adafruit/Adafruit ADS1X15 @ 2.4.0
;adafruit/Adafruit BusIO @ 1.13.2
;adafruit/Adafruit ADS1X15 @ 2.4.0
#For MAX1704x Lipo Monitor / Fuel Gauge uncomment following
; https://github.com/adafruit/Adafruit_BusIO @ 1.14.5
; https://github.com/adafruit/Adafruit_MAX1704X @ 1.0.2
#For MPU6050 IMU uncomment follwoing
;electroniccats/MPU6050 @1.0.1
# For -D USERMOD_ANIMARTRIX
# CC BY-NC 3.0 licensed effects by Stefan Petrick, include this usermod only if you accept the terms!
;https://github.com/netmindz/animartrix.git#18bf17389e57c69f11bc8d04ebe1d215422c7fb7
# SHT85
;robtillaart/SHT85@~0.3.3
# Audioreactive usermod
;kosme/arduinoFFT @ 2.0.0
extra_scripts = ${scripts_defaults.extra_scripts}
@@ -217,7 +197,8 @@ build_flags =
; restrict to minimal mime-types
-DMIMETYPE_MINIMAL
; other special-purpose framework flags (see https://docs.platformio.org/en/latest/platforms/espressif8266.html)
; -D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48 ;; in case of linker errors like "section `.text1' will not fit in region `iram1_0_seg'"
; decrease code cache size and increase IRAM to fit all pixel functions
-D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48 ;; in case of linker errors like "section `.text1' will not fit in region `iram1_0_seg'"
; -D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48_SECHEAP_SHARED ;; (experimental) adds some extra heap, but may cause slowdown
lib_deps =
@@ -243,8 +224,8 @@ lib_deps =
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
${env.lib_deps}
# additional build flags for audioreactive
AR_build_flags = -D USERMOD_AUDIOREACTIVE -D UM_AUDIOREACTIVE_USE_NEW_FFT
AR_lib_deps = https://github.com/kosme/arduinoFFT#develop @ ^1.9.2
AR_build_flags = -D USERMOD_AUDIOREACTIVE
AR_lib_deps = kosme/arduinoFFT @ 2.0.0
[esp32_idf_V4]
;; experimental build environment for ESP32 using ESP-IDF 4.4.x / arduino-esp32 v2.0.5
@@ -269,6 +250,7 @@ lib_deps =
;; generic definitions for all ESP32-S2 boards
platform = espressif32@5.3.0
platform_packages =
default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
build_flags = -g
-DARDUINO_ARCH_ESP32
-DARDUINO_ARCH_ESP32S2
@@ -279,7 +261,6 @@ build_flags = -g
-DARDUINO_USB_MODE=0 ;; this flag is mandatory for ESP32-S2 !
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
;; ARDUINO_USB_CDC_ON_BOOT
lib_deps =
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
${env.lib_deps}
@@ -297,7 +278,6 @@ build_flags = -g
-DARDUINO_USB_MODE=1 ;; this flag is mandatory for ESP32-C3
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
;; ARDUINO_USB_CDC_ON_BOOT
lib_deps =
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
${env.lib_deps}
@@ -336,6 +316,11 @@ build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 #-DWLED
lib_deps = ${esp8266.lib_deps}
monitor_filters = esp8266_exception_decoder
[env:nodemcuv2_160]
extends = env:nodemcuv2
board_build.f_cpu = 160000000L
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266_160 #-DWLED_DISABLE_2D
[env:esp8266_2m]
board = esp_wroom_02
platform = ${common.platform_wled_default}
@@ -345,6 +330,11 @@ build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP02
lib_deps = ${esp8266.lib_deps}
[env:esp8266_2m_160]
extends = env:esp8266_2m
board_build.f_cpu = 160000000L
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP02_160
[env:esp01_1m_full]
board = esp01_1m
platform = ${common.platform_wled_default}
@@ -352,45 +342,14 @@ 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 WLED_USE_UNREAL_MATH ;; may cause wrong sunset/sunrise times, but saves 7064 bytes FLASH and 975 bytes RAM
lib_deps = ${esp8266.lib_deps}
[env:esp07]
board = esp07
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
lib_deps = ${esp8266.lib_deps}
[env:d1_mini]
board = d1_mini
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
upload_speed = 921600
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
lib_deps = ${esp8266.lib_deps}
monitor_filters = esp8266_exception_decoder
[env:heltec_wifi_kit_8]
board = d1_mini
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
lib_deps = ${esp8266.lib_deps}
[env:h803wf]
board = d1_mini
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D LEDPIN=1 -D WLED_DISABLE_INFRARED
lib_deps = ${esp8266.lib_deps}
[env:esp01_1m_full_160]
extends = env:esp01_1m_full
board_build.f_cpu = 160000000L
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP01_160 -D WLED_DISABLE_OTA
; -D WLED_USE_UNREAL_MATH ;; may cause wrong sunset/sunrise times, but saves 7064 bytes FLASH and 975 bytes RAM
[env:esp32dev]
board = esp32dev
@@ -416,33 +375,6 @@ board_build.partitions = ${esp32.default_partitions}
; board_build.f_flash = 80000000L
; board_build.flash_mode = dio
[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_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:esp32dev_V4_dio80]
;; experimental ESP32 env using ESP-IDF V4.4.x
;; Warning: this build environment is not stable!!
;; please erase your device before installing.
board = esp32dev
platform = ${esp32_idf_V4.platform}
platform_packages = ${esp32_idf_V4.platform_packages}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=ESP32_V4_qio80 #-D WLED_DISABLE_BROWNOUT_DET
lib_deps = ${esp32_idf_V4.lib_deps}
monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32_idf_V4.default_partitions}
board_build.f_flash = 80000000L
board_build.flash_mode = dio
[env:esp32_eth]
board = esp32-poe
platform = ${esp32.platform}
@@ -454,19 +386,17 @@ build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_Ethernet -D
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
[env:esp32_wrover]
platform = ${esp32.platform}
board = ttgo-t7-v14-mini32
board_build.f_flash = 80000000L
board_build.flash_mode = qio
upload_speed = 460800
board_build.partitions = tools/WLED_ESP32-wrover_4MB.csv
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s2.build_flags} #-D WLED_RELEASE_NAME=S2_saola
;-DLOLIN_WIFI_FIX ;; try this in case Wifi does not work
-DARDUINO_USB_CDC_ON_BOOT=1
lib_deps = ${esp32s2.lib_deps}
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_WROVER
-DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue ;; Older ESP32 (rev.<3) need a PSRAM fix (increases static RAM used) https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/external-ram.html
-D LEDPIN=25
lib_deps = ${esp32.lib_deps}
[env:esp32c3dev]
extends = esp32c3
@@ -495,8 +425,9 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
;-D ARDUINO_USB_CDC_ON_BOOT=1 ;; -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
;-D WLED_DEBUG
${esp32.AR_build_flags}
lib_deps = ${esp32s3.lib_deps}
${esp32.AR_lib_deps}
board_build.partitions = tools/WLED_ESP32_8MB.csv
board_build.f_flash = 80000000L
board_build.flash_mode = qio
@@ -511,295 +442,62 @@ platform = ${esp32s3.platform}
platform_packages = ${esp32s3.platform_packages}
upload_speed = 921600
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s3.build_flags}
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_8MB_PSRAM_opi
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
-D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
; -D WLED_RELEASE_NAME=ESP32-S3_PSRAM
-D WLED_USE_PSRAM -DBOARD_HAS_PSRAM ; tells WLED that PSRAM shall be used
-DBOARD_HAS_PSRAM
${esp32.AR_build_flags}
lib_deps = ${esp32s3.lib_deps}
${esp32.AR_lib_deps}
board_build.partitions = tools/WLED_ESP32_8MB.csv
board_build.f_flash = 80000000L
board_build.flash_mode = qio
monitor_filters = esp32_exception_decoder
[env:esp32s3dev_8MB_PSRAM_qspi]
;; ESP32-TinyS3 development board, with 8MB FLASH and PSRAM (memory_type: qio_qspi)
extends = env:esp32s3dev_8MB_PSRAM_opi
;board = um_tinys3 ; -> needs workaround from https://github.com/Aircoookie/WLED/pull/2905#issuecomment-1328049860
[env:esp32s3_4M_PSRAM_qspi]
;; ESP32-S3, with 4MB FLASH and <= 4MB PSRAM (memory_type: qio_qspi)
board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support
board_build.arduino.memory_type = qio_qspi ;; use with PSRAM: 2MB or 4MB
[env:esp8285_4CH_MagicHome]
board = esp8285
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k}
board_build.arduino.memory_type = qio_qspi ;; use with PSRAM: 2MB or 4MB
platform = ${esp32s3.platform}
platform_packages = ${esp32s3.platform_packages}
upload_speed = 921600
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA
lib_deps = ${esp8266.lib_deps}
[env:esp8285_H801]
board = esp8285
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA
lib_deps = ${esp8266.lib_deps}
[env:d1_mini_5CH_Shojo_PCB]
board = d1_mini
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_USE_SHOJO_PCB
lib_deps = ${esp8266.lib_deps}
# ------------------------------------------------------------------------------
# DEVELOPMENT BOARDS
# ------------------------------------------------------------------------------
[env:d1_mini_debug]
board = d1_mini
build_type = debug
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} ${common.debug_flags}
lib_deps = ${esp8266.lib_deps}
[env:d1_mini_ota]
board = d1_mini
upload_protocol = espota
# exchange for your WLED IP
upload_port = "10.10.1.27"
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
lib_deps = ${esp8266.lib_deps}
[env:anavi_miracle_controller]
board = d1_mini
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D LEDPIN=12 -D IRPIN=-1 -D RLYPIN=2
lib_deps = ${esp8266.lib_deps}
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_4M_PSRAM_qspi
-DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
-DBOARD_HAS_PSRAM
-D WLED_WATCHDOG_TIMEOUT=0
${esp32.AR_build_flags}
lib_deps = ${esp32s3.lib_deps}
${esp32.AR_lib_deps}
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
board_build.f_flash = 80000000L
board_build.flash_mode = qio
monitor_filters = esp32_exception_decoder
[env:lolin_s2_mini]
platform = ${esp32s2.platform}
platform_packages = ${esp32s2.platform_packages}
board = lolin_s2_mini
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
build_unflags = ${common.build_unflags} #-DARDUINO_USB_CDC_ON_BOOT=1
;board_build.flash_mode = qio
;board_build.f_flash = 80000000L
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=ESP32-S2
-DBOARD_HAS_PSRAM
-DARDUINO_USB_CDC_ON_BOOT=1 # try disabling and enabling unflag above in case of board-specific issues, will disable Serial
-DARDUINO_USB_CDC_ON_BOOT=1
-DARDUINO_USB_MSC_ON_BOOT=0
-DARDUINO_USB_DFU_ON_BOOT=0
-DBOARD_HAS_PSRAM
-DLOLIN_WIFI_FIX ; seems to work much better with this
-D WLED_USE_PSRAM
-D WLED_WATCHDOG_TIMEOUT=0
-D CONFIG_ASYNC_TCP_USE_WDT=0
-D LEDPIN=16
-D BTNPIN=18
-D RLYPIN=9
-D IRPIN=7
-D HW_PIN_SCL=35
-D HW_PIN_SDA=33
-D HW_PIN_CLOCKSPI=7
-D HW_PIN_DATASPI=11
-D HW_PIN_MISOSPI=9
; -D STATUSLED=15
${esp32.AR_build_flags}
lib_deps = ${esp32s2.lib_deps}
# ------------------------------------------------------------------------------
# custom board configurations
# ------------------------------------------------------------------------------
[env:esp32c3dev_2MB]
;; for ESP32-C3 boards with 2MB flash (instead of 4MB).
;; this board need a specific partition file. OTA not possible.
extends = esp32c3
platform = ${esp32c3.platform}
platform_packages = ${esp32c3.platform_packages}
board = esp32-c3-devkitm-1
build_flags = ${common.build_flags} ${esp32c3.build_flags} #-D WLED_RELEASE_NAME=ESP32-C3
-D WLED_WATCHDOG_TIMEOUT=0
-D WLED_DISABLE_OTA
; -DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB
-DARDUINO_USB_CDC_ON_BOOT=0 ;; for serial-to-USB chip
build_unflags = ${common.build_unflags}
upload_speed = 115200
lib_deps = ${esp32c3.lib_deps}
board_build.partitions = tools/WLED_ESP32_2MB_noOTA.csv
board_build.flash_mode = dio
[env:wemos_shield_esp32]
board = esp32dev
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
upload_speed = 460800
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32}
-D LEDPIN=16
-D RLYPIN=19
-D BTNPIN=17
-D IRPIN=18
-D UWLED_USE_MY_CONFIG
-D USERMOD_DALLASTEMPERATURE
-D USERMOD_FOUR_LINE_DISPLAY
-D TEMPERATURE_PIN=23
-D USE_ALT_DISPlAY ; new versions of USERMOD_FOUR_LINE_DISPLAY and USERMOD_ROTARY_ENCODER_UI
-D USERMOD_AUDIOREACTIVE
lib_deps = ${esp32.lib_deps}
OneWire@~2.3.5
olikraus/U8g2 @ ^2.28.8
https://github.com/blazoncek/arduinoFFT.git
board_build.partitions = ${esp32.default_partitions}
[env:m5atom]
board = esp32dev
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D LEDPIN=27 -D BTNPIN=39
lib_deps = ${esp32.lib_deps}
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
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:Athom_RGBCW] ;7w and 5w(GU10) bulbs
board = esp8285
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=ESP8266 -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,13,5
-D DEFAULT_LED_TYPE=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0
lib_deps = ${esp8266.lib_deps}
[env:Athom_15w_RGBCW] ;15w bulb
board = esp8285
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=ESP8266 -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,5,13
-D DEFAULT_LED_TYPE=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0 -D WLED_USE_IC_CCT
lib_deps = ${esp8266.lib_deps}
[env:Athom_3Pin_Controller] ;small controller with only data
board = esp8285
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=ESP8266 -D BTNPIN=0 -D RLYPIN=-1 -D LEDPIN=1 -D WLED_DISABLE_INFRARED
lib_deps = ${esp8266.lib_deps}
[env:Athom_4Pin_Controller] ; With clock and data interface
board = esp8285
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=ESP8266 -D BTNPIN=0 -D RLYPIN=12 -D LEDPIN=1 -D WLED_DISABLE_INFRARED
lib_deps = ${esp8266.lib_deps}
[env:Athom_5Pin_Controller] ;Analog light strip controller
board = esp8285
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=ESP8266 -D BTNPIN=0 -D RLYPIN=-1 DATA_PINS=4,12,14,13 -D WLED_DISABLE_INFRARED
lib_deps = ${esp8266.lib_deps}
[env:MY9291]
board = esp01_1m
platform = ${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}
# ------------------------------------------------------------------------------
# codm pixel controller board configurations
# codm-controller-0_6 can also be used for the TYWE3S controller
# ------------------------------------------------------------------------------
[env:codm-controller-0_6]
board = esp_wroom_02
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
lib_deps = ${esp8266.lib_deps}
[env:codm-controller-0_6-rev2]
board = esp_wroom_02
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
lib_deps = ${esp8266.lib_deps}
# ------------------------------------------------------------------------------
# EleksTube-IPS
# ------------------------------------------------------------------------------
[env:elekstube_ips]
board = esp32dev
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
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 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}
${esp32.AR_lib_deps}

View File

@@ -1,65 +0,0 @@
# Example PlatformIO Project Configuration Override
# ------------------------------------------------------------------------------
# Copy to platformio_override.ini to activate overrides
# ------------------------------------------------------------------------------
# Please visit documentation: https://docs.platformio.org/page/projectconf.html
[platformio]
default_envs = WLED_tasmota_1M
[env:WLED_tasmota_1M]
board = esp01_1m
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k}
lib_deps = ${esp8266.lib_deps}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
; *********************************************************************
; *** Use custom settings from file my_config.h
-DWLED_USE_MY_CONFIG
; *********************************************************************
;
;
; *** To use the below defines/overrides, copy and paste each onto it's own line just below build_flags in the section above.
;
; disable specific features
; -D WLED_DISABLE_OTA
; -D WLED_DISABLE_ALEXA
; -D WLED_DISABLE_HUESYNC
; -D WLED_DISABLE_INFRARED
; -D WLED_DISABLE_WEBSOCKETS
; PIN defines - uncomment and change, if needed:
; -D LEDPIN=2
; -D BTNPIN=0
; -D TOUCHPIN=T0
; -D IRPIN=4
; -D RLYPIN=12
; -D RLYMDE=1
; digital LED strip types - uncomment only one ! - this will disable WS281x / SK681x support
; -D USE_APA102
; -D USE_WS2801
; -D USE_LPD8806
; PIN defines for 2 wire LEDs
-D CLKPIN=0
-D DATAPIN=2
; to drive analog LED strips (aka 5050) hardware configuration is no longer necessary
; configure the settings in the UI as follows (hard):
; for the Magic Home LED Controller use PWM pins 5,12,13,15
; for the H801 controller use PINs 15,13,12,14 (W2 = 04)
; for the BW-LT11 controller use PINs 12,4,14,5
;
; set the name of the module - make sure there is a quote-backslash-quote before the name and a backslash-quote-quote after the name
; -D SERVERNAME="\"WLED\""
;
; set the number of LEDs
; -D DEFAULT_LED_COUNT=30
;
; set milliampere limit when using ESP pin to power leds
; -D ABL_MILLIAMPS_DEFAULT=850
;
; enable IR by setting remote type
; -D IRTYPE=0 ;0 Remote disabled | 1 24-key RGB | 2 24-key with CT | 3 40-key blue | 4 40-key RGB | 5 21-key RGB | 6 6-key black | 7 9-key red | 8 JSON remote
;
; set default color order of your led strip
; -D DEFAULT_LED_COLOR_ORDER=COL_ORDER_GRB

View File

@@ -0,0 +1,494 @@
# Example PlatformIO Project Configuration Override
# ------------------------------------------------------------------------------
# Copy to platformio_override.ini to activate overrides
# ------------------------------------------------------------------------------
# Please visit documentation: https://docs.platformio.org/page/projectconf.html
[platformio]
default_envs = WLED_tasmota_1M # define as many as you need
#----------
# SAMPLE
#----------
[env:WLED_tasmota_1M]
extends = env:esp01_1m_full # when you want to extend the existing environment (define only updated options)
; board = esp01_1m # uncomment when ou need different board
; platform = ${common.platform_wled_default} # uncomment and change when you want particular platform
; platform_packages = ${common.platform_packages}
; board_build.ldscript = ${common.ldscript_1m128k}
; upload_speed = 921600 # fast upload speed (remove ';' if your board supports fast upload speed)
# Sample libraries used for various usermods. Uncomment when using particular usermod.
lib_deps = ${esp8266.lib_deps}
; olikraus/U8g2 # @~2.33.15
; paulstoffregen/OneWire@~2.3.8
; adafruit/Adafruit Unified Sensor@^1.1.4
; adafruit/DHT sensor library@^1.4.1
; adafruit/Adafruit BME280 Library@^2.2.2
; Wire
; robtillaart/SHT85@~0.3.3
; gmag11/QuickESPNow ;@ 0.6.2
; https://github.com/blazoncek/QuickESPNow.git#optional-debug ;; exludes debug library
; https://github.com/kosme/arduinoFFT#develop @ 1.9.2+sha.419d7b0 ;; used for USERMOD_AUDIOREACTIVE - using "known working" hash
; build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
;
; *** 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_HUESYNC
; -D WLED_DISABLE_LOXONE
; -D WLED_DISABLE_INFRARED
; -D WLED_DISABLE_WEBSOCKETS
; -D WLED_DISABLE_MQTT
; -D WLED_DISABLE_ADALIGHT
; -D WLED_DISABLE_2D
; -D WLED_DISABLE_PXMAGIC
; -D WLED_DISABLE_ESPNOW
; -D WLED_DISABLE_BROWNOUT_DET
;
; PIN defines - uncomment and change, if needed:
; -D LEDPIN=2
; or use this for multiple outputs
; -D DATA_PINS=1,3
; -D BTNPIN=0
; -D IRPIN=4
; -D RLYPIN=12
; -D RLYMDE=1
; -D LED_BUILTIN=2 # GPIO of built-in LED
;
; Limit max buses
; -D WLED_MAX_BUSSES=2
;
; Configure default WiFi
; -D CLIENT_SSID='"MyNetwork"'
; -D CLIENT_PASS='"Netw0rkPassw0rd"'
;
; Configure and use Ethernet
; -D WLED_USE_ETHERNET
; -D WLED_ETH_DEFAULT=5
; do not use pins 5, (16,) 17, 18, 19, 21, 22, 23, 25, 26, 27 for anything but ethernet
; -D PHY_ADDR=0 -D ETH_PHY_POWER=5 -D ETH_PHY_MDC=23 -D ETH_PHY_MDIO=18
; -D ETH_CLK_MODE=ETH_CLOCK_GPIO17_OUT
;
; NTP time configuration
; -D WLED_NTP_ENABLED=true
; -D WLED_TIMEZONE=2
; -D WLED_LAT=48.86
; -D WLED_LON=2.33
;
; Use Watchdog timer with 10s guard
; -D WLED_WATCHDOG_TIMEOUT=10
;
; Create debug build (with remote debug)
; -D WLED_DEBUG
; -D WLED_DEBUG_HOST='"192.168.0.100"'
; -D WLED_DEBUG_PORT=7868
;
; Use Autosave usermod and set it to do save after 90s
; -D USERMOD_AUTO_SAVE
; -D AUTOSAVE_AFTER_SEC=90
;
; Use 4 Line Display usermod with SPI display
; -D USERMOD_FOUR_LINE_DISPLAY
; -D USE_ALT_DISPlAY # mandatory
; -DFLD_SPI_DEFAULT
; -D FLD_TYPE=SSD1306_SPI64
; -D FLD_PIN_CLOCKSPI=14
; -D FLD_PIN_DATASPI=13
; -D FLD_PIN_DC=26
; -D FLD_PIN_CS=15
; -D FLD_PIN_RESET=27
;
; Use Rotary encoder usermod (in conjunction with 4LD)
; -D USERMOD_ROTARY_ENCODER_UI
; -D ENCODER_DT_PIN=5
; -D ENCODER_CLK_PIN=18
; -D ENCODER_SW_PIN=19
;
; Use Dallas DS18B20 temperature sensor usermod and configure it to use GPIO13
; -D USERMOD_DALLASTEMPERATURE
; -D TEMPERATURE_PIN=13
;
; Use Multi Relay usermod and configure it to use 6 relays and appropriate GPIO
; -D USERMOD_MULTI_RELAY
; -D MULTI_RELAY_MAX_RELAYS=6
; -D MULTI_RELAY_PINS=12,23,22,21,24,25
;
; Use PIR sensor usermod and configure it to use GPIO4 and timer of 60s
; -D USERMOD_PIRSWITCH
; -D PIR_SENSOR_PIN=4
; -D PIR_SENSOR_OFF_SEC=60
;
; Use Audioreactive usermod and configure I2S microphone
; -D USERMOD_AUDIOREACTIVE
; -D UM_AUDIOREACTIVE_USE_NEW_FFT
; -D AUDIOPIN=-1
; -D DMTYPE=1 # 0-analog/disabled, 1-I2S generic, 2-ES7243, 3-SPH0645, 4-I2S+mclk, 5-I2S PDM
; -D I2S_SDPIN=36
; -D I2S_WSPIN=23
; -D I2S_CKPIN=19
;
; Use PWM fan usermod
; -D USERMOD_PWM_FAN
; -D TACHO_PIN=33
; -D PWM_PIN=32
;
; Use built-in or custom LED as a status indicator (assumes LED is connected to GPIO16)
; -D STATUSLED=16
;
; set the name of the module - make sure there is a quote-backslash-quote before the name and a backslash-quote-quote after the name
; -D SERVERNAME="\"WLED\""
;
; set the number of LEDs
; -D DEFAULT_LED_COUNT=30
; or this for multiple outputs
; -D PIXEL_COUNTS=30,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
;
; use PSRAM on classic ESP32 rev.1 (rev.3 or above has no issues)
; -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue
;
; configure I2C and SPI interface (for various hardware)
; -D I2CSDAPIN=33 # initialise interface
; -D I2CSCLPIN=35 # initialise interface
; -D HW_PIN_SCL=35
; -D HW_PIN_SDA=33
; -D HW_PIN_CLOCKSPI=7
; -D HW_PIN_DATASPI=11
; -D HW_PIN_MISOSPI=9
# ------------------------------------------------------------------------------
# PRE-CONFIGURED DEVELOPMENT BOARDS AND CONTROLLERS
# ------------------------------------------------------------------------------
[env:esp07]
board = esp07
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
lib_deps = ${esp8266.lib_deps}
[env:d1_mini]
board = d1_mini
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
upload_speed = 921600
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
lib_deps = ${esp8266.lib_deps}
monitor_filters = esp8266_exception_decoder
[env:heltec_wifi_kit_8]
board = d1_mini
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
lib_deps = ${esp8266.lib_deps}
[env:h803wf]
board = d1_mini
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D LEDPIN=1 -D WLED_DISABLE_INFRARED
lib_deps = ${esp8266.lib_deps}
[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_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:esp32dev_V4_dio80]
;; experimental ESP32 env using ESP-IDF V4.4.x
;; Warning: this build environment is not stable!!
;; please erase your device before installing.
board = esp32dev
platform = ${esp32_idf_V4.platform}
platform_packages = ${esp32_idf_V4.platform_packages}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=ESP32_V4_qio80 #-D WLED_DISABLE_BROWNOUT_DET
lib_deps = ${esp32_idf_V4.lib_deps}
monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32_idf_V4.default_partitions}
board_build.f_flash = 80000000L
board_build.flash_mode = dio
[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}
build_flags = ${common.build_flags} ${esp32s2.build_flags} #-D WLED_RELEASE_NAME=S2_saola
;-DLOLIN_WIFI_FIX ;; try this in case Wifi does not work
-DARDUINO_USB_CDC_ON_BOOT=1
lib_deps = ${esp32s2.lib_deps}
[env:esp32s3dev_8MB_PSRAM_qspi]
;; ESP32-TinyS3 development board, with 8MB FLASH and PSRAM (memory_type: qio_qspi)
extends = env:esp32s3dev_8MB_PSRAM_opi
;board = um_tinys3 ; -> needs workaround from https://github.com/Aircoookie/WLED/pull/2905#issuecomment-1328049860
board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support
board_build.arduino.memory_type = qio_qspi ;; use with PSRAM: 2MB or 4MB
[env:esp8285_4CH_MagicHome]
board = esp8285
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA
lib_deps = ${esp8266.lib_deps}
[env:esp8285_H801]
board = esp8285
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA
lib_deps = ${esp8266.lib_deps}
[env:d1_mini_5CH_Shojo_PCB]
board = d1_mini
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_USE_SHOJO_PCB
lib_deps = ${esp8266.lib_deps}
[env:d1_mini_debug]
board = d1_mini
build_type = debug
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} ${common.debug_flags}
lib_deps = ${esp8266.lib_deps}
[env:d1_mini_ota]
board = d1_mini
upload_protocol = espota
# exchange for your WLED IP
upload_port = "10.10.1.27"
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
lib_deps = ${esp8266.lib_deps}
[env:anavi_miracle_controller]
board = d1_mini
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D LEDPIN=12 -D IRPIN=-1 -D RLYPIN=2
lib_deps = ${esp8266.lib_deps}
[env:esp32c3dev_2MB]
;; for ESP32-C3 boards with 2MB flash (instead of 4MB).
;; this board need a specific partition file. OTA not possible.
extends = esp32c3
platform = ${esp32c3.platform}
platform_packages = ${esp32c3.platform_packages}
board = esp32-c3-devkitm-1
build_flags = ${common.build_flags} ${esp32c3.build_flags} #-D WLED_RELEASE_NAME=ESP32-C3
-D WLED_WATCHDOG_TIMEOUT=0
-D WLED_DISABLE_OTA
; -DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB
-DARDUINO_USB_CDC_ON_BOOT=0 ;; for serial-to-USB chip
build_unflags = ${common.build_unflags}
upload_speed = 115200
lib_deps = ${esp32c3.lib_deps}
board_build.partitions = tools/WLED_ESP32_2MB_noOTA.csv
board_build.flash_mode = dio
[env:wemos_shield_esp32]
board = esp32dev
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
upload_speed = 460800
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32}
-D LEDPIN=16
-D RLYPIN=19
-D BTNPIN=17
-D IRPIN=18
-D UWLED_USE_MY_CONFIG
-D USERMOD_DALLASTEMPERATURE
-D USERMOD_FOUR_LINE_DISPLAY
-D TEMPERATURE_PIN=23
-D USE_ALT_DISPlAY ; new versions of USERMOD_FOUR_LINE_DISPLAY and USERMOD_ROTARY_ENCODER_UI
-D USERMOD_AUDIOREACTIVE
lib_deps = ${esp32.lib_deps}
OneWire@~2.3.5
olikraus/U8g2 @ ^2.28.8
https://github.com/blazoncek/arduinoFFT.git
board_build.partitions = ${esp32.default_partitions}
[env:m5atom]
board = esp32dev
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D LEDPIN=27 -D BTNPIN=39
lib_deps = ${esp32.lib_deps}
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
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:Athom_RGBCW] ;7w and 5w(GU10) bulbs
board = esp8285
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=ESP8266 -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,13,5
-D DEFAULT_LED_TYPE=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0
lib_deps = ${esp8266.lib_deps}
[env:Athom_15w_RGBCW] ;15w bulb
board = esp8285
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=ESP8266 -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,5,13
-D DEFAULT_LED_TYPE=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0 -D WLED_USE_IC_CCT
lib_deps = ${esp8266.lib_deps}
[env:Athom_3Pin_Controller] ;small controller with only data
board = esp8285
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=ESP8266 -D BTNPIN=0 -D RLYPIN=-1 -D LEDPIN=1 -D WLED_DISABLE_INFRARED
lib_deps = ${esp8266.lib_deps}
[env:Athom_4Pin_Controller] ; With clock and data interface
board = esp8285
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=ESP8266 -D BTNPIN=0 -D RLYPIN=12 -D LEDPIN=1 -D WLED_DISABLE_INFRARED
lib_deps = ${esp8266.lib_deps}
[env:Athom_5Pin_Controller] ;Analog light strip controller
board = esp8285
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=ESP8266 -D BTNPIN=0 -D RLYPIN=-1 DATA_PINS=4,12,14,13 -D WLED_DISABLE_INFRARED
lib_deps = ${esp8266.lib_deps}
[env:MY9291]
board = esp01_1m
platform = ${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}
# ------------------------------------------------------------------------------
# codm pixel controller board configurations
# codm-controller-0_6 can also be used for the TYWE3S controller
# ------------------------------------------------------------------------------
[env:codm-controller-0_6]
board = esp_wroom_02
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
lib_deps = ${esp8266.lib_deps}
[env:codm-controller-0_6-rev2]
board = esp_wroom_02
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}
lib_deps = ${esp8266.lib_deps}
# ------------------------------------------------------------------------------
# EleksTube-IPS
# ------------------------------------------------------------------------------
[env:elekstube_ips]
board = esp32dev
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
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 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

@@ -3,7 +3,7 @@
<a href="https://github.com/Aircoookie/WLED/releases"><img src="https://img.shields.io/github/release/Aircoookie/WLED.svg?style=flat-square"></a>
<a href="https://raw.githubusercontent.com/Aircoookie/WLED/master/LICENSE"><img src="https://img.shields.io/github/license/Aircoookie/wled?color=blue&style=flat-square"></a>
<a href="https://wled.discourse.group"><img src="https://img.shields.io/discourse/topics?colorB=blue&label=forum&server=https%3A%2F%2Fwled.discourse.group%2F&style=flat-square"></a>
<a href="https://discord.gg/KuqP7NE"><img src="https://img.shields.io/discord/473448917040758787.svg?colorB=blue&label=discord&style=flat-square"></a>
<a href="https://discord.gg/QAh7wJHrRM"><img src="https://img.shields.io/discord/473448917040758787.svg?colorB=blue&label=discord&style=flat-square"></a>
<a href="https://kno.wled.ge"><img src="https://img.shields.io/badge/quick_start-wiki-blue.svg?style=flat-square"></a>
<a href="https://github.com/Aircoookie/WLED-App"><img src="https://img.shields.io/badge/app-wled-blue.svg?style=flat-square"></a>
<a href="https://gitpod.io/#https://github.com/Aircoookie/WLED"><img src="https://img.shields.io/badge/Gitpod-ready--to--code-blue?style=flat-square&logo=gitpod"></a>
@@ -26,7 +26,7 @@ A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control
- Up to 250 user presets to save and load colors/effects easily, supports cycling through them.
- Presets can be used to automatically execute API calls
- Nightlight function (gradually dims down)
- Full OTA software updatability (HTTP + ArduinoOTA), password protectable
- Full OTA software updateability (HTTP + ArduinoOTA), password protectable
- Configurable analog clock (Cronixie, 7-segment and EleksTube IPS clock support via usermods)
- Configurable Auto Brightness limit for safe operation
- Filesystem-based config for easier backup of presets and settings
@@ -66,7 +66,7 @@ Credits [here](https://kno.wled.ge/about/contributors/)!
Join the Discord server to discuss everything about WLED!
<a href="https://discord.gg/KuqP7NE"><img src="https://discordapp.com/api/guilds/473448917040758787/widget.png?style=banner2" width="25%"></a>
<a href="https://discord.gg/QAh7wJHrRM"><img src="https://discordapp.com/api/guilds/473448917040758787/widget.png?style=banner2" width="25%"></a>
Check out the WLED [Discourse forum](https://wled.discourse.group)!

View File

@@ -1,6 +1,6 @@
#
# This file is autogenerated by pip-compile with python 3.8
# To update, run:
# This file is autogenerated by pip-compile with Python 3.12
# by the following command:
#
# pip-compile
#
@@ -21,7 +21,9 @@ click==8.1.3
# platformio
# uvicorn
colorama==0.4.6
# via platformio
# via
# click
# platformio
h11==0.14.0
# via
# uvicorn
@@ -34,7 +36,7 @@ marshmallow==3.19.0
# via platformio
packaging==23.1
# via marshmallow
platformio==6.1.6
platformio==6.1.14
# via -r requirements.in
pyelftools==0.29
# via platformio
@@ -50,7 +52,7 @@ starlette==0.23.1
# via platformio
tabulate==0.9.0
# via platformio
urllib3==1.26.15
urllib3==1.26.18
# via requests
uvicorn==0.20.0
# via platformio

View File

@@ -1,6 +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,
app0, app, ota_0, 0x10000, 0x1A0000,
app1, app, ota_1, 0x1B0000,0x1A0000,
spiffs, data, spiffs, 0x350000,0xB0000,
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 0x1A0000
5 app1 app ota_1 0x190000 0x1B0000 0x180000 0x1A0000
6 spiffs data spiffs 0x310000 0x350000 0xF0000 0xB0000

212
tools/cdata-test.js Normal file
View File

@@ -0,0 +1,212 @@
'use strict';
const assert = require('node:assert');
const { describe, it, before, after } = require('node:test');
const fs = require('fs');
const path = require('path');
const child_process = require('child_process');
const util = require('util');
const execPromise = util.promisify(child_process.exec);
process.env.NODE_ENV = 'test'; // Set the environment to testing
const cdata = require('./cdata.js');
describe('Function', () => {
const testFolderPath = path.join(__dirname, 'testFolder');
const oldFilePath = path.join(testFolderPath, 'oldFile.txt');
const newFilePath = path.join(testFolderPath, 'newFile.txt');
// Create a temporary file before the test
before(() => {
// Create test folder
if (!fs.existsSync(testFolderPath)) {
fs.mkdirSync(testFolderPath);
}
// Create an old file
fs.writeFileSync(oldFilePath, 'This is an old file.');
// Modify the 'mtime' to simulate an old file
const oldTime = new Date();
oldTime.setFullYear(oldTime.getFullYear() - 1);
fs.utimesSync(oldFilePath, oldTime, oldTime);
// Create a new file
fs.writeFileSync(newFilePath, 'This is a new file.');
});
// delete the temporary files after the test
after(() => {
fs.rmSync(testFolderPath, { recursive: true });
});
describe('isFileNewerThan', async () => {
it('should return true if the file is newer than the provided time', async () => {
const pastTime = Date.now() - 10000; // 10 seconds ago
assert.strictEqual(cdata.isFileNewerThan(newFilePath, pastTime), true);
});
it('should return false if the file is older than the provided time', async () => {
assert.strictEqual(cdata.isFileNewerThan(oldFilePath, Date.now()), false);
});
it('should throw an exception if the file does not exist', async () => {
assert.throws(() => {
cdata.isFileNewerThan('nonexistent.txt', Date.now());
});
});
});
describe('isAnyFileInFolderNewerThan', async () => {
it('should return true if a file in the folder is newer than the given time', async () => {
const time = fs.statSync(path.join(testFolderPath, 'oldFile.txt')).mtime;
assert.strictEqual(cdata.isAnyFileInFolderNewerThan(testFolderPath, time), true);
});
it('should return false if no files in the folder are newer than the given time', async () => {
assert.strictEqual(cdata.isAnyFileInFolderNewerThan(testFolderPath, new Date()), false);
});
it('should throw an exception if the folder does not exist', async () => {
assert.throws(() => {
cdata.isAnyFileInFolderNewerThan('nonexistent', new Date());
});
});
});
});
describe('Script', () => {
const folderPath = 'wled00';
const dataPath = path.join(folderPath, 'data');
before(() => {
process.env.NODE_ENV = 'production';
// Backup files
fs.cpSync("wled00/data", "wled00Backup", { recursive: true });
fs.cpSync("tools/cdata.js", "cdata.bak.js");
fs.cpSync("package.json", "package.bak.json");
});
after(() => {
// Restore backup
fs.rmSync("wled00/data", { recursive: true });
fs.renameSync("wled00Backup", "wled00/data");
fs.rmSync("tools/cdata.js");
fs.renameSync("cdata.bak.js", "tools/cdata.js");
fs.rmSync("package.json");
fs.renameSync("package.bak.json", "package.json");
});
// delete all html_*.h files
async function deleteBuiltFiles() {
const files = await fs.promises.readdir(folderPath);
await Promise.all(files.map(file => {
if (file.startsWith('html_') && path.extname(file) === '.h') {
return fs.promises.unlink(path.join(folderPath, file));
}
}));
}
// check if html_*.h files were created
async function checkIfBuiltFilesExist() {
const files = await fs.promises.readdir(folderPath);
const htmlFiles = files.filter(file => file.startsWith('html_') && path.extname(file) === '.h');
assert(htmlFiles.length > 0, 'html_*.h files were not created');
}
async function runAndCheckIfBuiltFilesExist() {
await execPromise('node tools/cdata.js');
await checkIfBuiltFilesExist();
}
async function checkIfFileWasNewlyCreated(file) {
const modifiedTime = fs.statSync(file).mtimeMs;
assert(Date.now() - modifiedTime < 500, file + ' was not modified');
}
async function testFileModification(sourceFilePath, resultFile) {
// run cdata.js to ensure html_*.h files are created
await execPromise('node tools/cdata.js');
// modify file
fs.appendFileSync(sourceFilePath, ' ');
// delay for 1 second to ensure the modified time is different
await new Promise(resolve => setTimeout(resolve, 1000));
// run script cdata.js again and wait for it to finish
await execPromise('node tools/cdata.js');
await checkIfFileWasNewlyCreated(path.join(folderPath, resultFile));
}
describe('should build if', () => {
it('html_*.h files are missing', async () => {
await deleteBuiltFiles();
await runAndCheckIfBuiltFilesExist();
});
it('only one html_*.h file is missing', async () => {
// run script cdata.js and wait for it to finish
await execPromise('node tools/cdata.js');
// delete a random html_*.h file
let files = await fs.promises.readdir(folderPath);
let htmlFiles = files.filter(file => file.startsWith('html_') && path.extname(file) === '.h');
const randomFile = htmlFiles[Math.floor(Math.random() * htmlFiles.length)];
await fs.promises.unlink(path.join(folderPath, randomFile));
await runAndCheckIfBuiltFilesExist();
});
it('script was executed with -f or --force', async () => {
await execPromise('node tools/cdata.js');
await new Promise(resolve => setTimeout(resolve, 1000));
await execPromise('node tools/cdata.js --force');
await checkIfFileWasNewlyCreated(path.join(folderPath, 'html_ui.h'));
await new Promise(resolve => setTimeout(resolve, 1000));
await execPromise('node tools/cdata.js -f');
await checkIfFileWasNewlyCreated(path.join(folderPath, 'html_ui.h'));
});
it('a file changes', async () => {
await testFileModification(path.join(dataPath, 'index.htm'), 'html_ui.h');
});
it('a inlined file changes', async () => {
await testFileModification(path.join(dataPath, 'index.js'), 'html_ui.h');
});
it('a settings file changes', async () => {
await testFileModification(path.join(dataPath, 'settings_leds.htm'), 'html_ui.h');
});
it('the favicon changes', async () => {
await testFileModification(path.join(dataPath, 'favicon.ico'), 'html_ui.h');
});
it('cdata.js changes', async () => {
await testFileModification('tools/cdata.js', 'html_ui.h');
});
it('package.json changes', async () => {
await testFileModification('package.json', 'html_ui.h');
});
});
describe('should not build if', () => {
it('the files are already built', async () => {
await deleteBuiltFiles();
// run script cdata.js and wait for it to finish
let startTime = Date.now();
await execPromise('node tools/cdata.js');
const firstRunTime = Date.now() - startTime;
// run script cdata.js and wait for it to finish
startTime = Date.now();
await execPromise('node tools/cdata.js');
const secondRunTime = Date.now() - startTime;
// check if second run was faster than the first (must be at least 2x faster)
assert(secondRunTime < firstRunTime / 2, 'html_*.h files were rebuilt');
});
});
});

View File

@@ -2,7 +2,7 @@
* Writes compressed C arrays of data files (web interface)
* How to use it?
*
* 1) Install Node 11+ and npm
* 1) Install Node 20+ and npm
* 2) npm install
* 3) npm run build
*
@@ -15,26 +15,58 @@
* It uses NodeJS packages to inline, minify and GZIP files. See writeHtmlGzipped and writeChunks invocations at the bottom of the page.
*/
const fs = require("fs");
const fs = require("node:fs");
const path = require("path");
const inliner = require("inliner");
const zlib = require("zlib");
const zlib = require("node:zlib");
const CleanCSS = require("clean-css");
const MinifyHTML = require("html-minifier-terser").minify;
const minifyHtml = require("html-minifier-terser").minify;
const packageJson = require("../package.json");
/**
*
// Export functions for testing
module.exports = { isFileNewerThan, isAnyFileInFolderNewerThan };
const output = ["wled00/html_ui.h", "wled00/html_pixart.h", "wled00/html_cpal.h", "wled00/html_pxmagic.h", "wled00/html_settings.h", "wled00/html_other.h"]
// \x1b[34m is blue, \x1b[36m is cyan, \x1b[0m is reset
const wledBanner = `
\t\x1b[34m ## ## ## ###### ######
\t\x1b[34m## ## ## ## ## ## ##
\t\x1b[34m## ## ## ## ###### ## ##
\t\x1b[34m## ## ## ## ## ## ##
\t\x1b[34m ## ## ###### ###### ######
\t\t\x1b[36m build script for web UI
\x1b[0m`;
const singleHeader = `/*
* Binary array for the Web UI.
* gzip is used for smaller size and improved speeds.
*
* Please see https://kno.wled.ge/advanced/custom-features/#changing-web-ui
* to find out how to easily modify the web UI source!
*/
function hexdump(buffer,isHex=false) {
`;
const multiHeader = `/*
* More web UI HTML source arrays.
* This file is auto generated, please don't make any changes manually.
*
* Instead, see https://kno.wled.ge/advanced/custom-features/#changing-web-ui
* to find out how to easily modify the web UI source!
*/
`;
function hexdump(buffer, isHex = false) {
let lines = [];
for (let i = 0; i < buffer.length; i +=(isHex?32:16)) {
for (let i = 0; i < buffer.length; i += (isHex ? 32 : 16)) {
var block;
let hexArray = [];
if (isHex) {
block = buffer.slice(i, i + 32)
for (let j = 0; j < block.length; j +=2 ) {
hexArray.push("0x" + block.slice(j,j+2))
for (let j = 0; j < block.length; j += 2) {
hexArray.push("0x" + block.slice(j, j + 2))
}
} else {
block = buffer.slice(i, i + 16); // cut buffer into blocks of 16
@@ -51,204 +83,163 @@ function hexdump(buffer,isHex=false) {
return lines.join(",\n");
}
function strReplace(str, search, replacement) {
return str.split(search).join(replacement);
}
function adoptVersionAndRepo(html) {
let repoUrl = packageJson.repository ? packageJson.repository.url : undefined;
if (repoUrl) {
repoUrl = repoUrl.replace(/^git\+/, "");
repoUrl = repoUrl.replace(/\.git$/, "");
// Replace we
html = strReplace(html, "https://github.com/atuline/WLED", repoUrl);
html = strReplace(html, "https://github.com/Aircoookie/WLED", repoUrl);
html = html.replaceAll("https://github.com/atuline/WLED", repoUrl);
html = html.replaceAll("https://github.com/Aircoookie/WLED", repoUrl);
}
let version = packageJson.version;
if (version) {
html = strReplace(html, "##VERSION##", version);
html = html.replaceAll("##VERSION##", version);
}
return html;
}
function filter(str, type) {
str = adoptVersionAndRepo(str);
if (type === undefined) {
async function minify(str, type = "plain") {
const options = {
collapseWhitespace: true,
collapseBooleanAttributes: true,
collapseInlineTagWhitespace: true,
minifyCSS: true,
minifyJS: true,
removeAttributeQuotes: true,
removeComments: true,
sortAttributes: true,
sortClassName: true,
};
if (type == "plain") {
return str;
} else if (type == "css-minify") {
return new CleanCSS({}).minify(str).styles;
} else if (type == "js-minify") {
return MinifyHTML('<script>' + str + '</script>', {
collapseWhitespace: true,
minifyJS: true,
continueOnParseError: false,
removeComments: true,
}).replace(/<[\/]*script>/g,'');
return await minifyHtml('<script>' + str + '</script>', options).replace(/<[\/]*script>/g, '');
} else if (type == "html-minify") {
return MinifyHTML(str, {
collapseWhitespace: true,
maxLineLength: 80,
minifyCSS: true,
minifyJS: true,
continueOnParseError: false,
removeComments: true,
});
} else if (type == "html-minify-ui") {
return MinifyHTML(str, {
collapseWhitespace: true,
conservativeCollapse: true,
maxLineLength: 80,
minifyCSS: true,
minifyJS: true,
continueOnParseError: false,
removeComments: true,
});
} else {
console.warn("Unknown filter: " + type);
return str;
return await minifyHtml(str, options);
}
throw new Error("Unknown filter: " + type);
}
function writeHtmlGzipped(sourceFile, resultFile, page) {
async function writeHtmlGzipped(sourceFile, resultFile, page) {
console.info("Reading " + sourceFile);
new inliner(sourceFile, function (error, html) {
console.info("Inlined " + html.length + " characters");
html = filter(html, "html-minify-ui");
console.info("Minified to " + html.length + " characters");
if (error) {
console.warn(error);
throw error;
}
new inliner(sourceFile, async function (error, html) {
if (error) throw error;
html = adoptVersionAndRepo(html);
zlib.gzip(html, { level: zlib.constants.Z_BEST_COMPRESSION }, function (error, result) {
if (error) {
console.warn(error);
throw error;
}
console.info("Compressed " + result.length + " bytes");
const array = hexdump(result);
const src = `/*
* Binary array for the Web UI.
* gzip is used for smaller size and improved speeds.
*
* Please see https://kno.wled.ge/advanced/custom-features/#changing-web-ui
* to find out how to easily modify the web UI source!
*/
// Autogenerated from ${sourceFile}, do not edit!!
const uint16_t PAGE_${page}_L = ${result.length};
const uint8_t PAGE_${page}[] PROGMEM = {
${array}
};
`;
console.info("Writing " + resultFile);
fs.writeFileSync(resultFile, src);
});
const originalLength = html.length;
html = await minify(html, "html-minify");
const result = zlib.gzipSync(html, { level: zlib.constants.Z_BEST_COMPRESSION });
console.info("Minified and compressed " + sourceFile + " from " + originalLength + " to " + result.length + " bytes");
const array = hexdump(result);
let src = singleHeader;
src += `const uint16_t PAGE_${page}_L = ${result.length};\n`;
src += `const uint8_t PAGE_${page}[] PROGMEM = {\n${array}\n};\n\n`;
console.info("Writing " + resultFile);
fs.writeFileSync(resultFile, src);
});
}
function specToChunk(srcDir, s) {
if (s.method == "plaintext") {
const buf = fs.readFileSync(srcDir + "/" + s.file);
const str = buf.toString("utf-8");
const chunk = `
// Autogenerated from ${srcDir}/${s.file}, do not edit!!
const char ${s.name}[] PROGMEM = R"${s.prepend || ""}${filter(str, s.filter)}${
s.append || ""
}";
async function specToChunk(srcDir, s) {
const buf = fs.readFileSync(srcDir + "/" + s.file);
let chunk = `\n// Autogenerated from ${srcDir}/${s.file}, do not edit!!\n`
`;
return s.mangle ? s.mangle(chunk) : chunk;
} else if (s.method == "gzip") {
const buf = fs.readFileSync(srcDir + "/" + s.file);
var str = buf.toString('utf-8');
if (s.mangle) str = s.mangle(str);
const zip = zlib.gzipSync(filter(str, s.filter), { level: zlib.constants.Z_BEST_COMPRESSION });
const result = hexdump(zip.toString('hex'), true);
const chunk = `
// Autogenerated from ${srcDir}/${s.file}, do not edit!!
const uint16_t ${s.name}_length = ${zip.length};
const uint8_t ${s.name}[] PROGMEM = {
${result}
};
`;
return chunk;
} else if (s.method == "binary") {
const buf = fs.readFileSync(srcDir + "/" + s.file);
const result = hexdump(buf);
const chunk = `
// Autogenerated from ${srcDir}/${s.file}, do not edit!!
const uint16_t ${s.name}_length = ${result.length};
const uint8_t ${s.name}[] PROGMEM = {
${result}
};
`;
return chunk;
} else {
console.warn("Unknown method: " + s.method);
return undefined;
}
}
function writeChunks(srcDir, specs, resultFile) {
let src = `/*
* More web UI HTML source arrays.
* This file is auto generated, please don't make any changes manually.
* Instead, see https://kno.wled.ge/advanced/custom-features/#changing-web-ui
* to find out how to easily modify the web UI source!
*/
`;
specs.forEach((s) => {
try {
console.info("Reading " + srcDir + "/" + s.file + " as " + s.name);
src += specToChunk(srcDir, s);
} catch (e) {
console.warn(
"Failed " + s.name + " from " + srcDir + "/" + s.file,
e.message.length > 60 ? e.message.substring(0, 60) : e.message
);
if (s.method == "plaintext" || s.method == "gzip") {
let str = buf.toString("utf-8");
str = adoptVersionAndRepo(str);
const originalLength = str.length;
if (s.method == "gzip") {
if (s.mangle) str = s.mangle(str);
const zip = zlib.gzipSync(await minify(str, s.filter), { level: zlib.constants.Z_BEST_COMPRESSION });
console.info("Minified and compressed " + s.file + " from " + originalLength + " to " + zip.length + " bytes");
const result = hexdump(zip);
chunk += `const uint16_t ${s.name}_length = ${zip.length};\n`;
chunk += `const uint8_t ${s.name}[] PROGMEM = {\n${result}\n};\n\n`;
return chunk;
} else {
const minified = await minify(str, s.filter);
console.info("Minified " + s.file + " from " + originalLength + " to " + minified.length + " bytes");
chunk += `const char ${s.name}[] PROGMEM = R"${s.prepend || ""}${minified}${s.append || ""}";\n\n`;
return s.mangle ? s.mangle(chunk) : chunk;
}
});
} else if (s.method == "binary") {
const result = hexdump(buf);
chunk += `const uint16_t ${s.name}_length = ${buf.length};\n`;
chunk += `const uint8_t ${s.name}[] PROGMEM = {\n${result}\n};\n\n`;
return chunk;
}
throw new Error("Unknown method: " + s.method);
}
async function writeChunks(srcDir, specs, resultFile) {
let src = multiHeader;
for (const s of specs) {
console.info("Reading " + srcDir + "/" + s.file + " as " + s.name);
src += await specToChunk(srcDir, s);
}
console.info("Writing " + src.length + " characters into " + resultFile);
fs.writeFileSync(resultFile, src);
}
// Check if a file is newer than a given time
function isFileNewerThan(filePath, time) {
const stats = fs.statSync(filePath);
return stats.mtimeMs > time;
}
// Check if any file in a folder (or its subfolders) is newer than a given time
function isAnyFileInFolderNewerThan(folderPath, time) {
const files = fs.readdirSync(folderPath, { withFileTypes: true });
for (const file of files) {
const filePath = path.join(folderPath, file.name);
if (isFileNewerThan(filePath, time)) {
return true;
}
if (file.isDirectory() && isAnyFileInFolderNewerThan(filePath, time)) {
return true;
}
}
return false;
}
// Check if the web UI is already built
function isAlreadyBuilt(webUIPath, packageJsonPath = "package.json") {
let lastBuildTime = Infinity;
for (const file of output) {
try {
lastBuildTime = Math.min(lastBuildTime, fs.statSync(file).mtimeMs);
} catch (e) {
if (e.code !== 'ENOENT') throw e;
console.info("File " + file + " does not exist. Rebuilding...");
return false;
}
}
return !isAnyFileInFolderNewerThan(webUIPath, lastBuildTime) && !isFileNewerThan(packageJsonPath, lastBuildTime) && !isFileNewerThan(__filename, lastBuildTime);
}
// Don't run this script if we're in a test environment
if (process.env.NODE_ENV === 'test') {
return;
}
console.info(wledBanner);
if (isAlreadyBuilt("wled00/data") && process.argv[2] !== '--force' && process.argv[2] !== '-f') {
console.info("Web UI is already built");
return;
}
writeHtmlGzipped("wled00/data/index.htm", "wled00/html_ui.h", 'index');
writeHtmlGzipped("wled00/data/simple.htm", "wled00/html_simple.h", 'simple');
writeHtmlGzipped("wled00/data/pixart/pixart.htm", "wled00/html_pixart.h", 'pixart');
writeHtmlGzipped("wled00/data/cpal/cpal.htm", "wled00/html_cpal.h", 'cpal');
writeHtmlGzipped("wled00/data/pxmagic/pxmagic.htm", "wled00/html_pxmagic.h", 'pxmagic');
/*
writeChunks(
"wled00/data",
[
{
file: "simple.css",
name: "PAGE_simpleCss",
method: "gzip",
filter: "css-minify",
},
{
file: "simple.js",
name: "PAGE_simpleJs",
method: "gzip",
filter: "js-minify",
},
{
file: "simple.htm",
name: "PAGE_simple",
method: "gzip",
filter: "html-minify-ui",
}
],
"wled00/html_simplex.h"
);
*/
writeChunks(
"wled00/data",
[
@@ -259,7 +250,7 @@ writeChunks(
filter: "css-minify",
mangle: (str) =>
str
.replace("%%","%")
.replace("%%", "%")
},
{
file: "settings.htm",
@@ -406,16 +397,6 @@ const char PAGE_dmxmap[] PROGMEM = R"=====()=====";
file: "favicon.ico",
name: "favicon",
method: "binary",
},
{
file: "iro.js",
name: "iroJs",
method: "gzip"
},
{
file: "rangetouch.js",
name: "rangetouchJs",
method: "gzip"
}
],
"wled00/html_other.h"

View File

@@ -133,7 +133,7 @@ class Animated_Staircase : public Usermod {
* 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.
* The speed of sound is 343 meters per second at 20 degrees Celsius.
* Since the sound has to travel back and forth, the detection
* distance for the sensor in cm is (0.0343 * maxTimeUs) / 2.
*
@@ -259,7 +259,7 @@ class Animated_Staircase : public Usermod {
}
}
// send sesnor values to JSON API
// send sensor values to JSON API
void writeSensorsToJson(JsonObject& staircase) {
staircase[F("top-sensor")] = topSensorRead;
staircase[F("bottom-sensor")] = bottomSensorRead;
@@ -297,8 +297,8 @@ class Animated_Staircase : public Usermod {
offIndex = maxSegmentId = strip.getLastActiveSegmentId() + 1;
// shorten the strip transition time to be equal or shorter than segment delay
transitionDelayTemp = transitionDelay = segment_delay_ms;
strip.setTransition(segment_delay_ms/100);
transitionDelay = segment_delay_ms;
strip.setTransition(segment_delay_ms);
strip.trigger();
} else {
if (togglePower && !on && offMode) toggleOnOff(); // toggle power on if off
@@ -309,7 +309,7 @@ class Animated_Staircase : public Usermod {
seg.setOption(SEG_OPTION_ON, true);
}
strip.trigger(); // force strip update
stateChanged = true; // inform external dvices/UI of change
stateChanged = true; // inform external devices/UI of change
colorUpdated(CALL_MODE_DIRECT_CHANGE);
DEBUG_PRINTLN(F("Animated Staircase disabled."));
}
@@ -492,7 +492,7 @@ class Animated_Staircase : public Usermod {
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)
topMaxDist = min(150,max(30,(int)topMaxDist)); // max distance ~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)

View File

@@ -11,7 +11,7 @@ The Animated Staircase can be controlled by the WLED API. Change settings such a
speed, on/off time and distance by sending an HTTP request, see below.
## WLED integration
To include this usermod in your WLED setup, you have to be able to [compile WLED from source](https://github.com/Aircoookie/WLED/wiki/Compiling-WLED).
To include this usermod in your WLED setup, you have to be able to [compile WLED from source](https://kno.wled.ge/advanced/compiling-wled/).
Before compiling, you have to make the following modifications:
@@ -38,7 +38,7 @@ Maximum distance for ultrasonic sensor can be configured as the time needed for
You _may_ need to use 10k pull-down resistors on the selected PIR pins, depending on the sensor.
## WLED configuration
1. In the WLED UI, confgure a segment for each step. The lowest step of the stairs is the
1. In the WLED UI, configure a segment for each step. The lowest step of the stairs is the
lowest segment id.
2. Save your segments into a preset.
3. Ideally, add the preset in the config > LED setup menu to the "apply
@@ -91,7 +91,7 @@ To enable the usermod again, use `"enabled":true`.
Alternatively you can use _Usermod_ Settings page where you can change other parameters as well.
### Changing animation parameters and detection range of the ultrasonic HC-SR04 sensor
Using _Usermod_ Settings page you can define different usermod parameters, includng sensor pins, delay between segment activation etc.
Using _Usermod_ Settings page you can define different usermod parameters, including sensor pins, delay between segment activation etc.
When an ultrasonic sensor is enabled you can enter maximum detection distance in centimeters separately for top and bottom sensors.

View File

@@ -9,7 +9,7 @@ The luminance is displayed in both the Info section of the web UI, as well as pu
- This must be added under `lib_deps` in your `platformio.ini` (or `platformio_override.ini`).
- Data is published over MQTT - make sure you've enabled the MQTT sync interface.
## Compiliation
## Compilation
To enable, compile with `USERMOD_BH1750` defined (e.g. in `platformio_override.ini`)
```ini

View File

@@ -25,7 +25,7 @@
#define USERMOD_BH1750_FIRST_MEASUREMENT_AT 10000
#endif
// only report if differance grater than offset value
// only report if difference grater than offset value
#ifndef USERMOD_BH1750_OFFSET_VALUE
#define USERMOD_BH1750_OFFSET_VALUE 1
#endif
@@ -86,7 +86,7 @@ private:
StaticJsonDocument<600> doc;
doc[F("name")] = String(serverDescription) + F(" ") + name;
doc[F("name")] = String(serverDescription) + " " + name;
doc[F("state_topic")] = topic;
doc[F("unique_id")] = String(mqttClientID) + name;
if (unitOfMeasurement != "")
@@ -98,8 +98,8 @@ private:
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("manufacturer")] = F(WLED_BRAND);
device[F("model")] = F(WLED_PRODUCT_NAME);
device[F("sw_version")] = versionString;
String temp;

View File

@@ -31,7 +31,7 @@ private:
// set the default pins based on the architecture, these get overridden by Usermod menu settings
#ifdef ESP8266
//uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8
//uint8_t RST_PIN = 16; // Un-comment for Heltec WiFi-Kit-8
#endif
bool initDone = false;
@@ -78,7 +78,7 @@ private:
static const char _name[];
static const char _enabled[];
// Read the BME280/BMP280 Sensor (which one runs depends on whether Celsius or Farenheit being set in Usermod Menu)
// Read the BME280/BMP280 Sensor (which one runs depends on whether Celsius or Fahrenheit being set in Usermod Menu)
void UpdateBME280Data(int SensorType)
{
float _temperature, _humidity, _pressure;
@@ -160,8 +160,8 @@ private:
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("manufacturer")] = F(WLED_BRAND);
device[F("model")] = F(WLED_PRODUCT_NAME);
device[F("sw_version")] = versionString;
String temp;

View File

@@ -19,7 +19,7 @@ If you have an ESP32 board, connect the positive side of the battery to ADC1 (GP
- 💯 Displays current battery voltage
- 🚥 Displays battery level
- 🚫 Auto-off with configurable Threshold
- 🚨 Low power indicator with many configuration posibilities
- 🚨 Low power indicator with many configuration possibilities
## 🎈 Installation
@@ -41,7 +41,7 @@ define `USERMOD_BATTERY` in `wled00/my_config.h`
| `USERMOD_BATTERY_MEASUREMENT_INTERVAL` | ms | battery check interval. defaults to 30 seconds |
| `USERMOD_BATTERY_MIN_VOLTAGE` | v | minimum battery voltage. default is 2.6 (18650 battery standard) |
| `USERMOD_BATTERY_MAX_VOLTAGE` | v | maximum battery voltage. default is 4.2 (18650 battery standard) |
| `USERMOD_BATTERY_TOTAL_CAPACITY` | mAh | the capacity of all cells in parralel sumed up |
| `USERMOD_BATTERY_TOTAL_CAPACITY` | mAh | the capacity of all cells in parallel summed up |
| `USERMOD_BATTERY_CALIBRATION` | | offset / calibration number, fine tune the measured voltage by the microcontroller |
| Auto-Off | --- | --- |
| `USERMOD_BATTERY_AUTO_OFF_ENABLED` | true/false | enables auto-off |

View File

@@ -114,7 +114,7 @@ class UsermodCronixie : public Usermod {
//W Week of Month | WW Week of Year
//D Day of Week | DD Day Of Month | DDD Day Of Year
DEBUG_PRINT("cset ");
DEBUG_PRINT(F("cset "));
DEBUG_PRINTLN(cronixieDisplay);
for (int i = 0; i < 6; i++)
@@ -160,7 +160,7 @@ class UsermodCronixie : public Usermod {
//case 'v': break; //user var1
}
}
DEBUG_PRINT("result ");
DEBUG_PRINT(F("result "));
for (int i = 0; i < 5; i++)
{
DEBUG_PRINT((int)dP[i]);

View File

@@ -49,7 +49,7 @@
#endif
// how many seconds after boot to take first measurement, 90 seconds
// 90 gives enough time to OTA update firmware if this crashses
// 90 gives enough time to OTA update firmware if this crashes
#ifndef USERMOD_DHT_FIRST_MEASUREMENT_AT
#define USERMOD_DHT_FIRST_MEASUREMENT_AT 90000
#endif

View File

@@ -46,7 +46,7 @@ class MyExampleUsermod : public Usermod {
static const char _enabled[];
// any private methods should go here (non-inline methosd should be defined out of class)
// any private methods should go here (non-inline method should be defined out of class)
void publishMqtt(const char* state, bool retain = false); // example for publishing MQTT message
@@ -87,7 +87,7 @@ class MyExampleUsermod : public Usermod {
* readFromConfig() is called prior to setup()
* You can use it to initialize variables, sensors or similar.
*/
void setup() {
void setup() override {
// do your set-up here
//Serial.println("Hello from my usermod!");
initDone = true;
@@ -98,7 +98,7 @@ class MyExampleUsermod : public Usermod {
* connected() is called every time the WiFi is (re)connected
* Use it to initialize network interfaces
*/
void connected() {
void connected() override {
//Serial.println("Connected to WiFi!");
}
@@ -113,7 +113,7 @@ class MyExampleUsermod : public Usermod {
* 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() {
void loop() override {
// if usermod is disabled or called during strip updating just exit
// NOTE: on very long strips strip.isUpdating() may always return true so update accordingly
if (!enabled || strip.isUpdating()) return;
@@ -131,7 +131,7 @@ class MyExampleUsermod : public Usermod {
* 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)
void addToJsonInfo(JsonObject& root) override
{
// if "u" object does not exist yet wee need to create it
JsonObject user = root["u"];
@@ -156,7 +156,7 @@ class MyExampleUsermod : public Usermod {
* 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)
void addToJsonState(JsonObject& root) override
{
if (!initDone || !enabled) return; // prevent crash on boot applyPreset()
@@ -171,7 +171,7 @@ class MyExampleUsermod : public Usermod {
* 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)
void readFromJsonState(JsonObject& root) override
{
if (!initDone) return; // prevent crash on boot applyPreset()
@@ -220,7 +220,7 @@ class MyExampleUsermod : public Usermod {
*
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
*/
void addToConfig(JsonObject& root)
void addToConfig(JsonObject& root) override
{
JsonObject top = root.createNestedObject(FPSTR(_name));
top[FPSTR(_enabled)] = enabled;
@@ -253,7 +253,7 @@ class MyExampleUsermod : public Usermod {
*
* This function is guaranteed to be called on boot, but could also be called every time settings are updated
*/
bool readFromConfig(JsonObject& root)
bool readFromConfig(JsonObject& root) override
{
// 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)
@@ -285,7 +285,7 @@ class MyExampleUsermod : public Usermod {
* it may add additional metadata for certain entry fields (adding drop down is possible)
* be careful not to add too much as oappend() buffer is limited to 3k
*/
void appendConfigData()
void appendConfigData() override
{
oappend(SET_F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F(":great")); oappend(SET_F("',1,'<i>(this is a great config value)</i>');"));
oappend(SET_F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F(":testString")); oappend(SET_F("',1,'enter any string you want');"));
@@ -300,7 +300,7 @@ class MyExampleUsermod : public Usermod {
* 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()
void handleOverlayDraw() override
{
//strip.setPixelColor(0, RGBW32(0,0,0,0)) // set the first pixel to black
}
@@ -311,7 +311,7 @@ class MyExampleUsermod : public Usermod {
* will prevent button working in a default way.
* Replicating button.cpp
*/
bool handleButton(uint8_t b) {
bool handleButton(uint8_t b) override {
yield();
// ignore certain button types as they may have other consequences
if (!enabled
@@ -334,7 +334,7 @@ class MyExampleUsermod : public Usermod {
* handling of MQTT message
* topic only contains stripped topic (part after /wled/MAC)
*/
bool onMqttMessage(char* topic, char* payload) {
bool onMqttMessage(char* topic, char* payload) override {
// check if we received a command
//if (strlen(topic) == 8 && strncmp_P(topic, PSTR("/command"), 8) == 0) {
// String action = payload;
@@ -355,7 +355,7 @@ class MyExampleUsermod : public Usermod {
/**
* onMqttConnect() is called when MQTT connection is established
*/
void onMqttConnect(bool sessionPresent) {
void onMqttConnect(bool sessionPresent) override {
// do any MQTT related initialisation here
//publishMqtt("I am alive!");
}
@@ -366,7 +366,7 @@ class MyExampleUsermod : public Usermod {
* onStateChanged() is used to detect WLED state change
* @mode parameter is CALL_MODE_... parameter used for notifications
*/
void onStateChange(uint8_t mode) {
void onStateChange(uint8_t mode) override {
// do something if WLED state changed (color, brightness, effect, preset, etc)
}
@@ -375,7 +375,7 @@ class MyExampleUsermod : public Usermod {
* 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()
uint16_t getId() override
{
return USERMOD_ID_EXAMPLE;
}

View File

@@ -15,23 +15,23 @@ OneWire oneWire(13);
DallasTemperature sensor(&oneWire);
long temptimer = millis();
long lastMeasure = 0;
#define Celsius // Show temperature mesaurement in Celcius otherwise is in Fahrenheit
#define Celsius // Show temperature measurement in Celsius otherwise is in Fahrenheit
// If display does not work or looks corrupted check the
// constructor reference:
// https://github.com/olikraus/u8g2/wiki/u8x8setupcpp
// or check the gallery:
// https://github.com/olikraus/u8g2/wiki/gallery
// --> First choise of cheap I2C OLED 128X32 0.91"
// --> First choice of cheap I2C OLED 128X32 0.91"
U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA
// --> Second choise of cheap I2C OLED 128X64 0.96" or 1.3"
// --> Second choice of cheap I2C OLED 128X64 0.96" or 1.3"
//U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA
// gets called once at boot. Do all initialization that doesn't depend on
// network here
void userSetup() {
sensor.begin(); //Start Dallas temperature sensor
u8x8.begin();
//u8x8.setFlipMode(1); //Uncoment if using WLED Wemos shield
//u8x8.setFlipMode(1); //Un-comment if using WLED Wemos shield
u8x8.setPowerSave(0);
u8x8.setContrast(10); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255
u8x8.setFont(u8x8_font_chroma48medium8_r);
@@ -71,7 +71,7 @@ void userLoop() {
if (mqtt != nullptr)
{
sensor.requestTemperatures();
//Gets prefered temperature scale based on selection in definitions section
//Gets preferred temperature scale based on selection in definitions section
#ifdef Celsius
float board_temperature = sensor.getTempCByIndex(0);
#else
@@ -138,11 +138,11 @@ void userLoop() {
// First row with Wifi name
u8x8.setCursor(1, 0);
u8x8.print(knownSsid.substring(0, u8x8.getCols() > 1 ? u8x8.getCols() - 2 : 0));
// Print `~` char to indicate that SSID is longer, than owr dicplay
// Print `~` char to indicate that SSID is longer than our display
if (knownSsid.length() > u8x8.getCols())
u8x8.print("~");
// Second row with IP or Psssword
// Second row with IP or Password
u8x8.setCursor(1, 1);
// Print password in AP mode and if led is OFF.
if (apActive && bri == 0)

View File

@@ -10,7 +10,7 @@
void UpdateBME280Data();
#define Celsius // Show temperature mesaurement in Celcius otherwise is in Fahrenheit
#define Celsius // Show temperature measurement in Celsius otherwise is in Fahrenheit
BME280I2C bme; // Default : forced mode, standby time = 1000 ms
// Oversampling = pressure ×1, temperature ×1, humidity ×1, filter off,
@@ -20,14 +20,14 @@ 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
// uint8_t RST_PIN = 16; // Un-comment for Heltec WiFi-Kit-8
#endif
//The SCL and SDA pins are defined here.
//ESP8266 Wemos D1 mini board use SCL=5 SDA=4 while ESP32 Wemos32 mini board use SCL=22 SDA=21
#define U8X8_PIN_SCL SCL_PIN
#define U8X8_PIN_SDA SDA_PIN
//#define U8X8_PIN_RESET RST_PIN // Uncoment for Heltec WiFi-Kit-8
//#define U8X8_PIN_RESET RST_PIN // Un-comment for Heltec WiFi-Kit-8
// If display does not work or looks corrupted check the
// constructor reference:
@@ -36,9 +36,9 @@ uint8_t SDA_PIN = 4;
// https://github.com/olikraus/u8g2/wiki/gallery
// --> First choise of cheap I2C OLED 128X32 0.91"
U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA
// --> Second choise of cheap I2C OLED 128X64 0.96" or 1.3"
// --> Second choice of cheap I2C OLED 128X64 0.96" or 1.3"
//U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA
// --> Third choise of Heltec WiFi-Kit-8 OLED 128X32 0.91"
// --> Third choice of Heltec WiFi-Kit-8 OLED 128X32 0.91"
//U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_RESET, U8X8_PIN_SCL, U8X8_PIN_SDA); // Constructor for Heltec WiFi-Kit-8
// gets called once at boot. Do all initialization that doesn't depend on network here
@@ -181,11 +181,11 @@ void userLoop() {
// First row with Wifi name
u8x8.setCursor(1, 0);
u8x8.print(knownSsid.substring(0, u8x8.getCols() > 1 ? u8x8.getCols() - 2 : 0));
// Print `~` char to indicate that SSID is longer, than owr dicplay
// Print `~` char to indicate that SSID is longer than our display
if (knownSsid.length() > u8x8.getCols())
u8x8.print("~");
// Second row with IP or Psssword
// Second row with IP or Password
u8x8.setCursor(1, 1);
// Print password in AP mode and if led is OFF.
if (apActive && bri == 0)

View File

@@ -2,7 +2,7 @@
**Attention: This usermod compiles only for ESP8266**
This usermod-v2 modification performs a ping request to a local IP address every 60 seconds. This ensures WLED net services remain accessible in some problematic WLAN environments.
This usermod-v2 modification performs a ping request to a local IP address every 60 seconds. This ensures WLED net services remain accessible in some problematic WiFi environments.
The modification works with static or DHCP IP address configuration.
@@ -24,7 +24,7 @@ The usermod supports the following state changes:
| JSON key | Value range | Description |
|-------------|------------------|---------------------------------|
| PingDelayMs | 5000 to 18000000 | Deactivdate/activate the sensor |
| PingDelayMs | 5000 to 18000000 | Deactivate/activate the sensor |
Changes also persist after a reboot.

View File

@@ -15,7 +15,7 @@ private:
static const char _enabled[];
static const char _loopInterval[];
// any private methods should go here (non-inline methosd should be defined out of class)
// any private methods should go here (non-inline method should be defined out of class)
void publishMqtt(const char *state, bool retain = false); // example for publishing MQTT message
public:

View File

@@ -0,0 +1,26 @@
# LDR_Dusk_Dawn_v2
This usermod will obtain readings from a Light Dependent Resistor (LDR) and will turn on/off specific presets based on those readings. This is useful for exterior lighting situations where you want the lights to only be on when it is dark out.
# Installation
Add "-D USERMOD_LDR_DUSK_DAWN" to your platformio.ini [common] build_flags and build.
Example:
```
[common]
build_flags =
-D USERMOD_LDR_DUSK_DAWN # Enable LDR Dusk Dawn Usermod
```
# Usermod Settings
Setting | Description | Default
--- | --- | ---
Enabled | Enable/Disable the LDR functionality. | Disabled
LDR Pin | The analog capable pin your LDR is connected to. | 34
Threshold Minutes | The number of minutes of consistent readings above/below the on/off threshold before the LED state will change. | 5
Threshold | The analog read value threshold from the LDR. Readings lower than this number will count towards changing the LED state to off. You can see the current LDR reading by going into the info section when LDR functionality is enabled. | 1000
On Preset | The WLED preset to be used for the LED on state. | 1
Off Preset | The WLED preset to be used for the LED off state. | 2
## Author
[@jeffwdh](https://github.com/jeffwdh)
jeffwdh@tarball.ca

View File

@@ -0,0 +1,153 @@
#pragma once
#include "wled.h"
#ifndef ARDUINO_ARCH_ESP32
// 8266 does not support analogRead on user selectable pins
#error only ESP32 is supported by usermod LDR_DUSK_DAWN
#endif
class LDR_Dusk_Dawn_v2 : public Usermod {
private:
// Defaults
bool ldrEnabled = false;
int ldrPin = 34; //A2 on Adafruit Huzzah32
int ldrThresholdMinutes = 5; // How many minutes of readings above/below threshold until it switches LED state
int ldrThreshold = 1000; // Readings higher than this number will turn off LED.
int ldrOnPreset = 1; // Default "On" Preset
int ldrOffPreset = 2; // Default "Off" Preset
// Variables
bool initDone = false;
bool ldrEnabledPreviously = false; // Was LDR enabled for the previous check? First check is always no.
int ldrOffCount; // Number of readings above the threshold
int ldrOnCount; // Number of readings below the threshold
int ldrReading = 0; // Last LDR reading
int ldrLEDState; // Current LED on/off state
unsigned long lastMillis = 0;
static const char _name[];
public:
void setup() {
// register ldrPin
if ((ldrPin >= 0) && (digitalPinToAnalogChannel(ldrPin) >= 0)) {
if(!pinManager.allocatePin(ldrPin, false, PinOwner::UM_LDR_DUSK_DAWN)) ldrEnabled = false; // pin already in use -> disable usermod
else pinMode(ldrPin, INPUT); // alloc success -> configure pin for input
} else ldrEnabled = false; // invalid pin -> disable usermod
initDone = true;
}
void loop() {
// Only update every 10 seconds
if (millis() - lastMillis > 10000) {
if ( (ldrEnabled == true)
&& (ldrPin >= 0) && (digitalPinToAnalogChannel(ldrPin) >= 0) ) { // make sure that pin is valid for analogread()
// Default state is off
if (ldrEnabledPreviously == false) {
applyPreset(ldrOffPreset);
ldrEnabledPreviously = true;
ldrLEDState = 0;
}
// Get LDR reading and increment counter by number of seconds since last read
ldrReading = analogRead(ldrPin);
if (ldrReading <= ldrThreshold) {
ldrOnCount = ldrOnCount + 10;
ldrOffCount = 0;
} else {
ldrOffCount = ldrOffCount + 10;
ldrOnCount = 0;
}
if (ldrOnCount >= (ldrThresholdMinutes * 60)) {
ldrOnCount = 0;
// If LEDs were previously off, turn on
if (ldrLEDState == 0) {
applyPreset(ldrOnPreset);
ldrLEDState = 1;
}
}
if (ldrOffCount >= (ldrThresholdMinutes * 60)) {
ldrOffCount = 0;
// If LEDs were previously on, turn off
if (ldrLEDState == 1) {
applyPreset(ldrOffPreset);
ldrLEDState = 0;
}
}
} else {
// LDR is disabled, reset variables to default
ldrReading = 0;
ldrOnCount = 0;
ldrOffCount = 0;
ldrLEDState = 0;
ldrEnabledPreviously = false;
}
lastMillis = millis();
}
}
void addToConfig(JsonObject& root) {
JsonObject top = root.createNestedObject(FPSTR(_name));
top["Enabled"] = ldrEnabled;
top["LDR Pin"] = ldrPin;
top["Threshold Minutes"] = ldrThresholdMinutes;
top["Threshold"] = ldrThreshold;
top["On Preset"] = ldrOnPreset;
top["Off Preset"] = ldrOffPreset;
}
bool readFromConfig(JsonObject& root) {
int8_t oldLdrPin = ldrPin;
JsonObject top = root[FPSTR(_name)];
bool configComplete = !top.isNull();
configComplete &= getJsonValue(top["Enabled"], ldrEnabled);
configComplete &= getJsonValue(top["LDR Pin"], ldrPin);
configComplete &= getJsonValue(top["Threshold Minutes"], ldrThresholdMinutes);
configComplete &= getJsonValue(top["Threshold"], ldrThreshold);
configComplete &= getJsonValue(top["On Preset"], ldrOnPreset);
configComplete &= getJsonValue(top["Off Preset"], ldrOffPreset);
if (initDone && (ldrPin != oldLdrPin)) {
// pin changed - un-register previous pin, register new pin
if (oldLdrPin >= 0) pinManager.deallocatePin(oldLdrPin, PinOwner::UM_LDR_DUSK_DAWN);
setup(); // setup new pin
}
return configComplete;
}
void addToJsonInfo(JsonObject& root) {
// If "u" object does not exist yet we need to create it
JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u");
JsonArray LDR_Enabled = user.createNestedArray("LDR dusk/dawn enabled");
LDR_Enabled.add(ldrEnabled);
if (!ldrEnabled) return; // do not add more if usermod is disabled
JsonArray LDR_Reading = user.createNestedArray("LDR reading");
LDR_Reading.add(ldrReading);
JsonArray LDR_State = user.createNestedArray("LDR turned LEDs on");
LDR_State.add(bool(ldrLEDState));
// Optional debug information:
//JsonArray LDR_On_Count = user.createNestedArray("LDR on count");
//LDR_On_Count.add(ldrOnCount);
//JsonArray LDR_Off_Count = user.createNestedArray("LDR off count");
//LDR_Off_Count.add(ldrOffCount);
//bool pinValid = ((ldrPin >= 0) && (digitalPinToAnalogChannel(ldrPin) >= 0));
//if (pinManager.getPinOwner(ldrPin) != PinOwner::UM_LDR_DUSK_DAWN) pinValid = false;
//JsonArray LDR_valid = user.createNestedArray(F("LDR pin"));
//LDR_valid.add(ldrPin);
//LDR_valid.add(pinValid ? F(" OK"): F(" invalid"));
}
uint16_t getId() {
return USERMOD_ID_LDR_DUSK_DAWN;
}
};
const char LDR_Dusk_Dawn_v2::_name[] PROGMEM = "LDR_Dusk_Dawn_v2";

View File

@@ -0,0 +1,64 @@
# Adafruit MAX17048 Usermod (LiPo & LiIon Battery Monitor & Fuel Gauge)
This usermod reads information from an Adafruit MAX17048 and outputs the following:
- Battery Voltage
- Battery Level Percentage
## Dependencies
Libraries:
- `Adafruit_BusIO@~1.14.5` (by [adafruit](https://github.com/adafruit/Adafruit_BusIO))
- `Adafruit_MAX1704X@~1.0.2` (by [adafruit](https://github.com/adafruit/Adafruit_MAX1704X))
These must be added under `lib_deps` in your `platform.ini` (or `platform_override.ini`).
Data is published over MQTT - make sure you've enabled the MQTT sync interface.
## Compilation
To enable, compile with `USERMOD_MAX17048` define in the build_flags (e.g. in `platformio.ini` or `platformio_override.ini`) such as in the example below:
```ini
[env:usermod_max17048_d1_mini]
extends = env:d1_mini
build_flags =
${common.build_flags_esp8266}
-D USERMOD_MAX17048
lib_deps =
${esp8266.lib_deps}
https://github.com/adafruit/Adafruit_BusIO @ 1.14.5
https://github.com/adafruit/Adafruit_MAX1704X @ 1.0.2
```
### Configuration Options
The following settings can be set at compile-time but are configurable on the usermod menu (except First Monitor time):
- USERMOD_MAX17048_MIN_MONITOR_INTERVAL (the min number of milliseconds between checks, defaults to 10,000 ms)
- USERMOD_MAX17048_MAX_MONITOR_INTERVAL (the max number of milliseconds between checks, defaults to 10,000 ms)
- USERMOD_MAX17048_FIRST_MONITOR_AT
Additionally, the Usermod Menu allows you to:
- Enable or Disable the usermod
- Enable or Disable Home Assistant Discovery (turn on/off to sent MQTT Discovery entries for Home Assistant)
- Configure SCL/SDA GPIO Pins
## API
The following method is available to interact with the usermod from other code modules:
- `getBatteryVoltageV` read the last battery voltage (in Volt) obtained from the sensor
- `getBatteryPercent` reads the last battery percentage obtained from the sensor
## MQTT
MQTT topics are as follows (`<deviceTopic>` is set in MQTT section of Sync Setup menu):
Measurement type | MQTT topic
--- | ---
Battery Voltage | `<deviceTopic>/batteryVoltage`
Battery Percent | `<deviceTopic>/batteryPercent`
## Authors
Carlos Cruz [@ccruz09](https://github.com/ccruz09)
## Revision History
Jan 2024
- Added Home Assistant Discovery
- Implemented PinManager to register pins
- Added API call for other modules to read battery voltage and percentage
- Added info-screen outputs
- Updated `readme.md`

View File

@@ -0,0 +1,281 @@
// force the compiler to show a warning to confirm that this file is included
#warning **** Included USERMOD_MAX17048 V2.0 ****
#pragma once
#include "wled.h"
#include "Adafruit_MAX1704X.h"
// the max interval to check battery level, 10 seconds
#ifndef USERMOD_MAX17048_MAX_MONITOR_INTERVAL
#define USERMOD_MAX17048_MAX_MONITOR_INTERVAL 10000
#endif
// the min interval to check battery level, 500 ms
#ifndef USERMOD_MAX17048_MIN_MONITOR_INTERVAL
#define USERMOD_MAX17048_MIN_MONITOR_INTERVAL 500
#endif
// how many seconds after boot to perform the first check, 10 seconds
#ifndef USERMOD_MAX17048_FIRST_MONITOR_AT
#define USERMOD_MAX17048_FIRST_MONITOR_AT 10000
#endif
/*
* Usermod to display Battery Life using Adafruit's MAX17048 LiPoly/ LiIon Fuel Gauge and Battery Monitor.
*/
class Usermod_MAX17048 : public Usermod {
private:
bool enabled = true;
unsigned long maxReadingInterval = USERMOD_MAX17048_MAX_MONITOR_INTERVAL;
unsigned long minReadingInterval = USERMOD_MAX17048_MIN_MONITOR_INTERVAL;
unsigned long lastCheck = UINT32_MAX - (USERMOD_MAX17048_MAX_MONITOR_INTERVAL - USERMOD_MAX17048_FIRST_MONITOR_AT);
unsigned long lastSend = UINT32_MAX - (USERMOD_MAX17048_MAX_MONITOR_INTERVAL - USERMOD_MAX17048_FIRST_MONITOR_AT);
uint8_t VoltageDecimals = 3; // Number of decimal places in published voltage values
uint8_t PercentDecimals = 1; // Number of decimal places in published percent values
// string that are used multiple time (this will save some flash memory)
static const char _name[];
static const char _enabled[];
static const char _maxReadInterval[];
static const char _minReadInterval[];
static const char _HomeAssistantDiscovery[];
bool monitorFound = false;
bool firstReadComplete = false;
bool initDone = false;
Adafruit_MAX17048 maxLipo;
float lastBattVoltage = -10;
float lastBattPercent = -1;
// MQTT and Home Assistant Variables
bool HomeAssistantDiscovery = false; // Publish Home Assistant Device Information
bool mqttInitialized = false;
void _mqttInitialize()
{
char mqttBatteryVoltageTopic[128];
char mqttBatteryPercentTopic[128];
snprintf_P(mqttBatteryVoltageTopic, 127, PSTR("%s/batteryVoltage"), mqttDeviceTopic);
snprintf_P(mqttBatteryPercentTopic, 127, PSTR("%s/batteryPercent"), mqttDeviceTopic);
if (HomeAssistantDiscovery) {
_createMqttSensor(F("BatteryVoltage"), mqttBatteryVoltageTopic, "voltage", F("V"));
_createMqttSensor(F("BatteryPercent"), mqttBatteryPercentTopic, "battery", F("%"));
}
}
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());
}
void publishMqtt(const char *topic, const char* state) {
#ifndef WLED_DISABLE_MQTT
//Check if MQTT Connected, otherwise it will crash the 8266
if (WLED_MQTT_CONNECTED){
char subuf[128];
snprintf_P(subuf, 127, PSTR("%s/%s"), mqttDeviceTopic, topic);
mqtt->publish(subuf, 0, false, state);
}
#endif
}
public:
inline void enable(bool enable) { enabled = enable; }
inline bool isEnabled() { return enabled; }
void setup() {
// do your set-up here
if (i2c_scl<0 || i2c_sda<0) { enabled = false; return; }
monitorFound = maxLipo.begin();
initDone = true;
}
void loop() {
// if usermod is disabled or called during strip updating just exit
// NOTE: on very long strips strip.isUpdating() may always return true so update accordingly
if (!enabled || strip.isUpdating()) return;
unsigned long now = millis();
if (now - lastCheck < minReadingInterval) { return; }
bool shouldUpdate = now - lastSend > maxReadingInterval;
float battVoltage = maxLipo.cellVoltage();
float battPercent = maxLipo.cellPercent();
lastCheck = millis();
firstReadComplete = true;
if (shouldUpdate)
{
lastBattVoltage = roundf(battVoltage * powf(10, VoltageDecimals)) / powf(10, VoltageDecimals);
lastBattPercent = roundf(battPercent * powf(10, PercentDecimals)) / powf(10, PercentDecimals);
lastSend = millis();
publishMqtt("batteryVoltage", String(lastBattVoltage, VoltageDecimals).c_str());
publishMqtt("batteryPercent", String(lastBattPercent, PercentDecimals).c_str());
DEBUG_PRINTLN(F("Battery Voltage: ") + String(lastBattVoltage, VoltageDecimals) + F("V"));
DEBUG_PRINTLN(F("Battery Percent: ") + String(lastBattPercent, PercentDecimals) + F("%"));
}
}
void onMqttConnect(bool sessionPresent)
{
if (WLED_MQTT_CONNECTED && !mqttInitialized)
{
_mqttInitialize();
mqttInitialized = true;
}
}
inline float getBatteryVoltageV() {
return (float) lastBattVoltage;
}
inline float getBatteryPercent() {
return (float) lastBattPercent;
}
void addToJsonInfo(JsonObject& root)
{
// if "u" object does not exist yet wee need to create it
JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u");
JsonArray battery_json = user.createNestedArray(F("Battery Monitor"));
if (!enabled) {
battery_json.add(F("Disabled"));
}
else if(!monitorFound) {
battery_json.add(F("MAX17048 Not Found"));
}
else if (!firstReadComplete) {
// if we haven't read the sensor yet, let the user know
// that we are still waiting for the first measurement
battery_json.add((USERMOD_MAX17048_FIRST_MONITOR_AT - millis()) / 1000);
battery_json.add(F(" sec until read"));
} else {
battery_json.add(F("Enabled"));
JsonArray voltage_json = user.createNestedArray(F("Battery Voltage"));
voltage_json.add(lastBattVoltage);
voltage_json.add(F("V"));
JsonArray percent_json = user.createNestedArray(F("Battery Percent"));
percent_json.add(lastBattPercent);
percent_json.add(F("%"));
}
}
void addToJsonState(JsonObject& root)
{
JsonObject usermod = root[FPSTR(_name)];
if (usermod.isNull())
{
usermod = root.createNestedObject(FPSTR(_name));
}
usermod[FPSTR(_enabled)] = enabled;
}
void readFromJsonState(JsonObject& root)
{
JsonObject usermod = root[FPSTR(_name)];
if (!usermod.isNull())
{
if (usermod[FPSTR(_enabled)].is<bool>())
{
enabled = usermod[FPSTR(_enabled)].as<bool>();
}
}
}
void addToConfig(JsonObject& root)
{
JsonObject top = root.createNestedObject(FPSTR(_name));
top[FPSTR(_enabled)] = enabled;
top[FPSTR(_maxReadInterval)] = maxReadingInterval;
top[FPSTR(_minReadInterval)] = minReadingInterval;
top[FPSTR(_HomeAssistantDiscovery)] = HomeAssistantDiscovery;
DEBUG_PRINT(F(_name));
DEBUG_PRINTLN(F(" config saved."));
}
bool readFromConfig(JsonObject& root)
{
JsonObject top = root[FPSTR(_name)];
if (top.isNull()) {
DEBUG_PRINT(F(_name));
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false;
}
bool configComplete = !top.isNull();
configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled);
configComplete &= getJsonValue(top[FPSTR(_maxReadInterval)], maxReadingInterval, USERMOD_MAX17048_MAX_MONITOR_INTERVAL);
configComplete &= getJsonValue(top[FPSTR(_minReadInterval)], minReadingInterval, USERMOD_MAX17048_MIN_MONITOR_INTERVAL);
configComplete &= getJsonValue(top[FPSTR(_HomeAssistantDiscovery)], HomeAssistantDiscovery, false);
DEBUG_PRINT(FPSTR(_name));
if (!initDone) {
// first run: reading from cfg.json
DEBUG_PRINTLN(F(" config loaded."));
} else {
DEBUG_PRINTLN(F(" config (re)loaded."));
// changing parameters from settings page
}
return configComplete;
}
uint16_t getId()
{
return USERMOD_ID_MAX17048;
}
};
// add more strings here to reduce flash memory usage
const char Usermod_MAX17048::_name[] PROGMEM = "Adafruit MAX17048 Battery Monitor";
const char Usermod_MAX17048::_enabled[] PROGMEM = "enabled";
const char Usermod_MAX17048::_maxReadInterval[] PROGMEM = "max-read-interval-ms";
const char Usermod_MAX17048::_minReadInterval[] PROGMEM = "min-read-interval-ms";
const char Usermod_MAX17048::_HomeAssistantDiscovery[] PROGMEM = "HomeAssistantDiscovery";

View File

@@ -23,7 +23,7 @@ You can also use usermod's off timer instead of sensor's. In such case rotate th
## Usermod installation
**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. You can also change the default off time by adding `-D PIR_SENSOR_OFF_SEC=30`.
**NOTE:** Usermod has been included in master branch of WLED so it can be compiled in directly just by defining `-D USERMOD_PIRSWITCH` and optionally `-D PIR_SENSOR_PIN=16` to override default pin. You can also change the default off time by adding `-D PIR_SENSOR_OFF_SEC=30`.
## API to enable/disable the PIR sensor from outside. For example from another usermod:
@@ -31,7 +31,7 @@ To query or change the PIR sensor state the methods `bool PIRsensorEnabled()` an
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 send just the MQTT message but not change WLED state using settings page as well as responding to motion only at night
(assuming NTP and lattitude/longitude are set to determine sunrise/sunset times).
(assuming NTP and latitude/longitude are set to determine sunrise/sunset times).
### There are two options to get access to the usermod instance:
@@ -75,6 +75,9 @@ Usermod can be configured via the Usermods settings page.
* `mqtt-only` - send only MQTT messages, do not interact with WLED
* `off-only` - only trigger presets or turn WLED on/off if WLED is not already on (displaying effect)
* `notifications` - enable or disable sending notifications to other WLED instances using Sync button
* `HA-discovery` - enable automatic discovery in Home Assistant
* `override` - override PIR input when WLED state is changed using UI
* `domoticz-idx` - Domoticz virtual switch ID (used with MQTT `domoticz/in`)
Have fun - @gegu & @blazoncek
@@ -85,9 +88,16 @@ Have fun - @gegu & @blazoncek
2021-11
* Added information about dynamic configuration options
* Added option to temporary enable/disble usermod from WLED UI (Info dialog)
* Added option to temporary enable/disable usermod from WLED UI (Info dialog)
2022-11
* Added compile time option for off timer.
* Added Home Assistant autodiscovery MQTT broadcast.
* Updated info on compiling.
2023-??
* Override option
* Domoticz virtual switch ID (used with MQTT `domoticz/in`)
2024-02
* Added compile time option to expand number of PIR sensors (they are logically ORed) `-D PIR_SENSOR_MAX_SENSORS=3`

View File

@@ -15,6 +15,9 @@
#define PIR_SENSOR_OFF_SEC 600
#endif
#ifndef PIR_SENSOR_MAX_SENSORS
#define PIR_SENSOR_MAX_SENSORS 1
#endif
/*
* This usermod handles PIR sensor states.
@@ -50,14 +53,13 @@ private:
volatile unsigned long offTimerStart = 0; // off timer start time
volatile bool PIRtriggered = false; // did PIR trigger?
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
unsigned long lastLoop = 0;
bool initDone = false; // status of initialization
unsigned long lastLoop = 0;
bool sensorPinState[PIR_SENSOR_MAX_SENSORS] = {LOW}; // current PIR sensor pin state
// configurable parameters
bool enabled = true; // PIR sensor enabled
int8_t PIRsensorPin = PIR_SENSOR_PIN; // PIR sensor pin
int8_t PIRsensorPin[PIR_SENSOR_MAX_SENSORS] = {PIR_SENSOR_PIN}; // PIR sensor pin
uint32_t m_switchOffDelay = PIR_SENSOR_OFF_SEC*1000; // 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
@@ -70,6 +72,7 @@ private:
// Home Assistant
bool HomeAssistantDiscovery = false; // is HA discovery turned on
int16_t idx = -1; // Domoticz virtual switch idx
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
@@ -81,8 +84,8 @@ private:
static const char _mqttOnly[];
static const char _offOnly[];
static const char _haDiscovery[];
static const char _notify[];
static const char _override[];
static const char _domoticzIDX[];
/**
* check if it is daytime
@@ -94,14 +97,14 @@ private:
* switch strip on/off
*/
void switchStrip(bool switchOn);
void publishMqtt(const char* state);
void publishMqtt(bool switchOn);
// Create an MQTT Binary Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop.
void publishHomeAssistantAutodiscovery();
/**
* Read and update PIR sensor state.
* Initilize/reset switch off timer
* Initialize/reset switch off timer
*/
bool updatePIRsensorState();
@@ -117,7 +120,7 @@ public:
* 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();
void setup() override;
/**
* connected() is called every time the WiFi is (re)connected
@@ -128,24 +131,24 @@ public:
/**
* onMqttConnect() is called when MQTT connection is established
*/
void onMqttConnect(bool sessionPresent);
void onMqttConnect(bool sessionPresent) override;
/**
* loop() is called continuously. Here you can check for events, read sensors, etc.
*/
void loop();
void loop() override;
/**
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
*
* Add PIR sensor state and switch off timer duration to jsoninfo
*/
void addToJsonInfo(JsonObject &root);
void addToJsonInfo(JsonObject &root) override;
/**
* onStateChanged() is used to detect WLED state change
*/
void onStateChange(uint8_t mode);
void onStateChange(uint8_t mode) override;
/**
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
@@ -157,17 +160,17 @@ public:
* 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);
void readFromJsonState(JsonObject &root) override;
/**
* provide the changeable values
*/
void addToConfig(JsonObject &root);
void addToConfig(JsonObject &root) override;
/**
* provide UI information and allow extending UI options
*/
void appendConfigData();
void appendConfigData() override;
/**
* restore the changeable values
@@ -175,13 +178,13 @@ public:
*
* The function should return true if configuration was successfully loaded or false if there was no configuration.
*/
bool readFromConfig(JsonObject &root);
bool readFromConfig(JsonObject &root) override;
/**
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
* This could be used in the future for the system to determine whether your usermod is installed.
*/
uint16_t getId() { return USERMOD_ID_PIRSWITCH; }
uint16_t getId() override { return USERMOD_ID_PIRSWITCH; }
};
// strings to reduce flash memory usage (used more than twice)
@@ -194,8 +197,8 @@ const char PIRsensorSwitch::_nightTime[] PROGMEM = "nighttime-only";
const char PIRsensorSwitch::_mqttOnly[] PROGMEM = "mqtt-only";
const char PIRsensorSwitch::_offOnly[] PROGMEM = "off-only";
const char PIRsensorSwitch::_haDiscovery[] PROGMEM = "HA-discovery";
const char PIRsensorSwitch::_notify[] PROGMEM = "notifications";
const char PIRsensorSwitch::_override[] PROGMEM = "override";
const char PIRsensorSwitch::_domoticzIDX[] PROGMEM = "domoticz-idx";
bool PIRsensorSwitch::isDayTime() {
updateLocalTime();
@@ -235,24 +238,24 @@ void PIRsensorSwitch::switchStrip(bool switchOn)
prevPlaylist = 0;
prevPreset = 255;
}
applyPreset(m_onPreset, NotifyUpdateMode);
applyPreset(m_onPreset, CALL_MODE_BUTTON_PRESET);
return;
}
// preset not assigned
if (bri == 0) {
bri = briLast;
stateUpdated(NotifyUpdateMode);
stateUpdated(CALL_MODE_BUTTON);
}
} else {
if (m_offPreset) {
applyPreset(m_offPreset, NotifyUpdateMode);
applyPreset(m_offPreset, CALL_MODE_BUTTON_PRESET);
return;
} else if (prevPlaylist) {
if (currentPreset==m_onPreset || currentPlaylist==m_onPreset) applyPreset(prevPlaylist, NotifyUpdateMode);
if (currentPreset==m_onPreset || currentPlaylist==m_onPreset) applyPreset(prevPlaylist, CALL_MODE_BUTTON_PRESET);
prevPlaylist = 0;
return;
} else if (prevPreset) {
if (prevPreset<255) { if (currentPreset==m_onPreset || currentPlaylist==m_onPreset) applyPreset(prevPreset, NotifyUpdateMode); }
if (prevPreset<255) { if (currentPreset==m_onPreset || currentPlaylist==m_onPreset) applyPreset(prevPreset, CALL_MODE_BUTTON_PRESET); }
else { if (currentPreset==m_onPreset || currentPlaylist==m_onPreset) applyTemporaryPreset(); }
prevPreset = 0;
return;
@@ -261,19 +264,29 @@ void PIRsensorSwitch::switchStrip(bool switchOn)
if (bri != 0) {
briLast = bri;
bri = 0;
stateUpdated(NotifyUpdateMode);
stateUpdated(CALL_MODE_BUTTON);
}
}
}
void PIRsensorSwitch::publishMqtt(const char* state)
void PIRsensorSwitch::publishMqtt(bool switchOn)
{
#ifndef WLED_DISABLE_MQTT
//Check if MQTT Connected, otherwise it will crash the 8266
if (WLED_MQTT_CONNECTED) {
char buf[64];
char buf[128];
sprintf_P(buf, PSTR("%s/motion"), mqttDeviceTopic); //max length: 33 + 7 = 40
mqtt->publish(buf, 0, false, state);
mqtt->publish(buf, 0, false, switchOn?"on":"off");
// Domoticz formatted message
if (idx > 0) {
StaticJsonDocument <128> msg;
msg[F("idx")] = idx;
msg[F("RSSI")] = WiFi.RSSI();
msg[F("command")] = F("switchlight");
msg[F("switchcmd")] = switchOn ? F("On") : F("Off");
serializeJson(msg, buf, 128);
mqtt->publish("domoticz/in", 0, false, buf);
}
}
#endif
}
@@ -299,8 +312,8 @@ void PIRsensorSwitch::publishHomeAssistantAutodiscovery()
JsonObject device = doc.createNestedObject(F("device")); // attach the sensor to the same device
device[F("name")] = serverDescription;
device[F("ids")] = String(F("wled-sensor-")) + mqttClientID;
device[F("mf")] = "WLED";
device[F("mdl")] = F("FOSS");
device[F("mf")] = F(WLED_BRAND);
device[F("mdl")] = F(WLED_PRODUCT_NAME);
device[F("sw")] = versionString;
sprintf_P(buf, PSTR("homeassistant/binary_sensor/%s/config"), uid);
@@ -315,34 +328,36 @@ void PIRsensorSwitch::publishHomeAssistantAutodiscovery()
bool PIRsensorSwitch::updatePIRsensorState()
{
bool pinState = digitalRead(PIRsensorPin);
if (pinState != sensorPinState) {
sensorPinState = pinState; // change previous state
bool stateChanged = false;
bool allOff = true;
for (int i = 0; i < PIR_SENSOR_MAX_SENSORS; i++) {
if (PIRsensorPin[i] < 0) continue;
if (sensorPinState == HIGH) {
offTimerStart = 0;
if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(true);
else if (NotifyUpdateMode != CALL_MODE_NO_NOTIFY) updateInterfaces(CALL_MODE_WS_SEND);
publishMqtt("on");
} else {
// start switch off timer
offTimerStart = millis();
if (NotifyUpdateMode != CALL_MODE_NO_NOTIFY) updateInterfaces(CALL_MODE_WS_SEND);
bool pinState = digitalRead(PIRsensorPin[i]);
if (pinState != sensorPinState[i]) {
sensorPinState[i] = pinState; // change previous state
stateChanged = true;
if (sensorPinState[i] == HIGH) {
offTimerStart = 0;
allOff = false;
if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(true);
}
}
return true;
}
return false;
if (stateChanged) {
publishMqtt(!allOff);
// start switch off timer
if (allOff) offTimerStart = millis();
}
return stateChanged;
}
bool PIRsensorSwitch::handleOffTimer()
{
if (offTimerStart > 0 && millis() - offTimerStart > m_switchOffDelay) {
offTimerStart = 0;
if (enabled == true) {
if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()) || PIRtriggered)) switchStrip(false);
else if (NotifyUpdateMode != CALL_MODE_NO_NOTIFY) updateInterfaces(CALL_MODE_WS_SEND);
publishMqtt("off");
}
if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()) || PIRtriggered)) switchStrip(false);
return true;
}
return false;
@@ -352,18 +367,21 @@ bool PIRsensorSwitch::handleOffTimer()
void PIRsensorSwitch::setup()
{
if (enabled) {
for (int i = 0; i < PIR_SENSOR_MAX_SENSORS; i++) {
sensorPinState[i] = LOW;
if (PIRsensorPin[i] < 0) continue;
// 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);
if (pinManager.allocatePin(PIRsensorPin[i], false, PinOwner::UM_PIR)) {
// PIR Sensor mode INPUT_PULLDOWN
#ifdef ESP8266
pinMode(PIRsensorPin[i], PIRsensorPin[i]==16 ? INPUT_PULLDOWN_16 : INPUT_PULLUP); // ESP8266 has INPUT_PULLDOWN on GPIO16 only
#else
pinMode(PIRsensorPin[i], INPUT_PULLDOWN);
#endif
sensorPinState[i] = digitalRead(PIRsensorPin[i]);
} else {
if (PIRsensorPin >= 0) {
DEBUG_PRINTLN(F("PIRSensorSwitch pin allocation failed."));
}
PIRsensorPin = -1; // allocation failed
enabled = false;
DEBUG_PRINT(F("PIRSensorSwitch pin ")); DEBUG_PRINTLN(i); DEBUG_PRINTLN(F(" allocation failed."));
PIRsensorPin[i] = -1; // allocation failed
}
}
initDone = true;
@@ -378,8 +396,8 @@ void PIRsensorSwitch::onMqttConnect(bool sessionPresent)
void PIRsensorSwitch::loop()
{
// only check sensors 4x/s
if (!enabled || millis() - lastLoop < 250 || strip.isUpdating()) return;
// only check sensors 5x/s
if (!enabled || millis() - lastLoop < 200) return;
lastLoop = millis();
if (!updatePIRsensorState()) {
@@ -392,37 +410,35 @@ void PIRsensorSwitch::addToJsonInfo(JsonObject &root)
JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u");
bool state = LOW;
for (int i = 0; i < PIR_SENSOR_MAX_SENSORS; i++)
if (PIRsensorPin[i] >= 0) state |= sensorPinState[i];
JsonArray infoArr = user.createNestedArray(FPSTR(_name));
String uiDomString;
if (enabled) {
if (offTimerStart > 0)
{
if (offTimerStart > 0) {
uiDomString = "";
unsigned int offSeconds = (m_switchOffDelay - (millis() - offTimerStart)) / 1000;
if (offSeconds >= 3600)
{
if (offSeconds >= 3600) {
uiDomString += (offSeconds / 3600);
uiDomString += F("h ");
offSeconds %= 3600;
}
if (offSeconds >= 60)
{
if (offSeconds >= 60) {
uiDomString += (offSeconds / 60);
offSeconds %= 60;
}
else if (uiDomString.length() > 0)
{
} else if (uiDomString.length() > 0) {
uiDomString += 0;
}
if (uiDomString.length() > 0)
{
if (uiDomString.length() > 0) {
uiDomString += F("min ");
}
uiDomString += (offSeconds);
infoArr.add(uiDomString + F("s"));
} else {
infoArr.add(sensorPinState ? F("sensor on") : F("inactive"));
infoArr.add(state ? F("sensor on") : F("inactive"));
}
} else {
infoArr.add(F("disabled"));
@@ -442,9 +458,11 @@ void PIRsensorSwitch::addToJsonInfo(JsonObject &root)
uiDomString += F("</button>");
infoArr.add(uiDomString);
JsonObject sensor = root[F("sensor")];
if (sensor.isNull()) sensor = root.createNestedObject(F("sensor"));
sensor[F("motion")] = sensorPinState || offTimerStart>0 ? true : false;
if (enabled) {
JsonObject sensor = root[F("sensor")];
if (sensor.isNull()) sensor = root.createNestedObject(F("sensor"));
sensor[F("motion")] = state || offTimerStart>0 ? true : false;
}
}
void PIRsensorSwitch::onStateChange(uint8_t mode) {
@@ -474,7 +492,8 @@ void PIRsensorSwitch::addToConfig(JsonObject &root)
JsonObject top = root.createNestedObject(FPSTR(_name));
top[FPSTR(_enabled)] = enabled;
top[FPSTR(_switchOffDelay)] = m_switchOffDelay / 1000;
top["pin"] = PIRsensorPin;
JsonArray pinArray = top.createNestedArray("pin");
for (int i = 0; i < PIR_SENSOR_MAX_SENSORS; i++) pinArray.add(PIRsensorPin[i]);
top[FPSTR(_onPreset)] = m_onPreset;
top[FPSTR(_offPreset)] = m_offPreset;
top[FPSTR(_nightTime)] = m_nightTimeOnly;
@@ -482,21 +501,28 @@ void PIRsensorSwitch::addToConfig(JsonObject &root)
top[FPSTR(_offOnly)] = m_offOnly;
top[FPSTR(_override)] = m_override;
top[FPSTR(_haDiscovery)] = HomeAssistantDiscovery;
top[FPSTR(_notify)] = (NotifyUpdateMode != CALL_MODE_NO_NOTIFY);
top[FPSTR(_domoticzIDX)] = idx;
DEBUG_PRINTLN(F("PIR config saved."));
}
void PIRsensorSwitch::appendConfigData()
{
oappend(SET_F("addInfo('PIRsensorSwitch:HA-discovery',1,'HA=Home Assistant');")); // 0 is field type, 1 is actual field
oappend(SET_F("addInfo('PIRsensorSwitch:notifications',1,'Periodic WS updates');")); // 0 is field type, 1 is actual field
oappend(SET_F("addInfo('PIRsensorSwitch:override',1,'Cancel timer on change');")); // 0 is field type, 1 is actual field
for (int i = 0; i < PIR_SENSOR_MAX_SENSORS; i++) {
char str[128];
sprintf_P(str, PSTR("addInfo('PIRsensorSwitch:pin[]',%d,'','#%d');"), i, i);
oappend(str);
}
}
bool PIRsensorSwitch::readFromConfig(JsonObject &root)
{
bool oldEnabled = enabled;
int8_t oldPin = PIRsensorPin;
int8_t oldPin[PIR_SENSOR_MAX_SENSORS];
for (int i = 0; i < PIR_SENSOR_MAX_SENSORS; i++) {
oldPin[i] = PIRsensorPin[i];
PIRsensorPin[i] = -1;
}
DEBUG_PRINT(FPSTR(_name));
JsonObject top = root[FPSTR(_name)];
@@ -505,7 +531,13 @@ bool PIRsensorSwitch::readFromConfig(JsonObject &root)
return false;
}
PIRsensorPin = top["pin"] | PIRsensorPin;
JsonArray pins = top["pin"];
if (!pins.isNull()) {
for (size_t i = 0; i < PIR_SENSOR_MAX_SENSORS; i++)
if (i < pins.size()) PIRsensorPin[i] = pins[i] | PIRsensorPin[i];
} else {
PIRsensorPin[0] = top["pin"] | oldPin[0];
}
enabled = top[FPSTR(_enabled)] | enabled;
@@ -521,33 +553,17 @@ bool PIRsensorSwitch::readFromConfig(JsonObject &root)
m_offOnly = top[FPSTR(_offOnly)] | m_offOnly;
m_override = top[FPSTR(_override)] | m_override;
HomeAssistantDiscovery = top[FPSTR(_haDiscovery)] | HomeAssistantDiscovery;
NotifyUpdateMode = top[FPSTR(_notify)] ? CALL_MODE_DIRECT_CHANGE : CALL_MODE_NO_NOTIFY;
idx = top[FPSTR(_domoticzIDX)] | idx;
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);
}
}
for (int i = 0; i < PIR_SENSOR_MAX_SENSORS; i++)
if (oldPin[i] >= 0) pinManager.deallocatePin(oldPin[i], PinOwner::UM_PIR);
setup();
DEBUG_PRINTLN(F(" config (re)loaded."));
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[FPSTR(_override)].isNull();
return !(pins.isNull() || pins.size() != PIR_SENSOR_MAX_SENSORS);
}

View File

@@ -5,7 +5,7 @@ v2 Usermod to to control PWM fan with RPM feedback and temperature control
This usermod requires the Dallas Temperature usermod to obtain temperature information. If it's not available, the fan will run at 100% speed.
If the fan does not have _tachometer_ (RPM) output you can set the _tachometer-pin_ to -1 to disable that feature.
You can also set the thershold temperature at which fan runs at lowest speed. If the measured temperature is 3°C greater than the threshold temperature, the fan will run at 100%.
You can also set the threshold temperature at which fan runs at lowest speed. If the measured temperature is 3°C greater than the threshold temperature, the fan will run at 100%.
If the _tachometer_ is supported, the current speed (in RPM) will be displayed on the WLED Info page.
@@ -22,7 +22,7 @@ This includes:
* PWM output pin (can be configured at compile time `-D PWM_PIN=xx`)
* tachometer input pin (can be configured at compile time `-D TACHO_PIN=xx`)
* sampling frequency in seconds
* threshold temperature in degees C
* threshold temperature in degrees Celsius
_NOTE:_ You may also need to tweak Dallas Temperature usermod sampling frequency to match PWM fan sampling frequency.

View File

@@ -52,9 +52,15 @@ class PWMFanUsermod : public Usermod {
uint8_t tachoUpdateSec = 30;
float targetTemperature = 35.0;
uint8_t minPWMValuePct = 0;
uint8_t maxPWMValuePct = 100;
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.
uint8_t pwmValuePct = 0;
// constant values
static const uint8_t _pwmMaxValue = 255;
static const uint8_t _pwmMaxStepCount = 7;
float _pwmTempStepSize = 0.5f;
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
static const char _enabled[];
@@ -63,6 +69,7 @@ class PWMFanUsermod : public Usermod {
static const char _temperature[];
static const char _tachoUpdateSec[];
static const char _minPWMValuePct[];
static const char _maxPWMValuePct[];
static const char _IRQperRotation[];
static const char _speed[];
static const char _lock[];
@@ -156,38 +163,32 @@ class PWMFanUsermod : public Usermod {
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;
// dividing minPercent and maxPercent into equal pwmvalue sizes
int pwmStepSize = ((maxPWMValuePct - minPWMValuePct) * _pwmMaxValue) / (_pwmMaxStepCount*100);
int pwmStep = calculatePwmStep(temp - targetTemperature);
// minimum based on full speed - not entered MaxPercent
int pwmMinimumValue = (minPWMValuePct * _pwmMaxValue) / 100;
updateFanSpeed(pwmMinimumValue + pwmStep*pwmStepSize);
}
if ((temp == NAN) || (temp <= -100.0)) {
uint8_t calculatePwmStep(float diffTemp){
if ((diffTemp == NAN) || (diffTemp <= -100.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;
return _pwmMaxStepCount;
}
updateFanSpeed(newPWMvalue);
if(diffTemp <=0){
return 0;
}
int calculatedStep = (diffTemp / _pwmTempStepSize)+1;
// anything greater than max stepcount gets max
return (uint8_t)min((int)_pwmMaxStepCount,calculatedStep);
}
public:
// gets called once at boot. Do all initialization that doesn't depend on
// network here
void setup() {
void setup() override {
#ifdef USERMOD_DALLASTEMPERATURE
// This Usermod requires Temperature usermod
tempUM = (UsermodTemperature*) usermods.lookup(USERMOD_ID_TEMPERATURE);
@@ -202,12 +203,12 @@ class PWMFanUsermod : public Usermod {
// gets called every time WiFi is (re-)connected. Initialize own network
// interfaces here
void connected() {}
void connected() override {}
/*
* Da loop.
*/
void loop() {
void loop() override {
if (!enabled || strip.isUpdating()) return;
unsigned long now = millis();
@@ -222,7 +223,7 @@ class PWMFanUsermod : public Usermod {
* 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) {
void addToJsonInfo(JsonObject& root) override {
JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u");
@@ -271,7 +272,7 @@ class PWMFanUsermod : public Usermod {
* 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) {
void readFromJsonState(JsonObject& root) override {
if (!initDone) return; // prevent crash on boot applyPreset()
JsonObject usermod = root[FPSTR(_name)];
if (!usermod.isNull()) {
@@ -304,7 +305,7 @@ class PWMFanUsermod : public Usermod {
*
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
*/
void addToConfig(JsonObject& root) {
void addToConfig(JsonObject& root) override {
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
top[FPSTR(_enabled)] = enabled;
top[FPSTR(_pwmPin)] = pwmPin;
@@ -312,6 +313,7 @@ class PWMFanUsermod : public Usermod {
top[FPSTR(_tachoUpdateSec)] = tachoUpdateSec;
top[FPSTR(_temperature)] = targetTemperature;
top[FPSTR(_minPWMValuePct)] = minPWMValuePct;
top[FPSTR(_maxPWMValuePct)] = maxPWMValuePct;
top[FPSTR(_IRQperRotation)] = numberOfInterrupsInOneSingleRotation;
DEBUG_PRINTLN(F("Autosave config saved."));
}
@@ -326,7 +328,7 @@ class PWMFanUsermod : public Usermod {
*
* The function should return true if configuration was successfully loaded or false if there was no configuration.
*/
bool readFromConfig(JsonObject& root) {
bool readFromConfig(JsonObject& root) override {
int8_t newTachoPin = tachoPin;
int8_t newPwmPin = pwmPin;
@@ -345,6 +347,8 @@ class PWMFanUsermod : public Usermod {
targetTemperature = top[FPSTR(_temperature)] | targetTemperature;
minPWMValuePct = top[FPSTR(_minPWMValuePct)] | minPWMValuePct;
minPWMValuePct = (uint8_t) min(100,max(0,(int)minPWMValuePct)); // bounds checking
maxPWMValuePct = top[FPSTR(_maxPWMValuePct)] | maxPWMValuePct;
maxPWMValuePct = (uint8_t) min(100,max((int)minPWMValuePct,(int)maxPWMValuePct)); // bounds checking
numberOfInterrupsInOneSingleRotation = top[FPSTR(_IRQperRotation)] | numberOfInterrupsInOneSingleRotation;
numberOfInterrupsInOneSingleRotation = (uint8_t) max(1,(int)numberOfInterrupsInOneSingleRotation); // bounds checking
@@ -376,7 +380,7 @@ class PWMFanUsermod : public Usermod {
* 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() {
uint16_t getId() override {
return USERMOD_ID_PWM_FAN;
}
};
@@ -389,6 +393,7 @@ 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::_maxPWMValuePct[] PROGMEM = "max-PWM-percent";
const char PWMFanUsermod::_IRQperRotation[] PROGMEM = "IRQs-per-rotation";
const char PWMFanUsermod::_speed[] PROGMEM = "speed";
const char PWMFanUsermod::_lock[] PROGMEM = "lock";

View File

@@ -43,12 +43,12 @@ void handleRelay()
digitalWrite(PIN_UP_RELAY, LOW);
upActiveBefore = true;
upStartTime = millis();
DEBUG_PRINTLN("UPA");
DEBUG_PRINTLN(F("UPA"));
}
if (millis()- upStartTime > PIN_ON_TIME)
{
upActive = false;
DEBUG_PRINTLN("UPN");
DEBUG_PRINTLN(F("UPN"));
}
} else if (upActiveBefore)
{

View File

@@ -30,7 +30,7 @@
#define USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE 10000.0f
#endif
// only report if differance grater than offset value
// only report if difference grater than offset value
#ifndef USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE
#define USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE 5
#endif
@@ -119,7 +119,7 @@ public:
}
else
{
DEBUG_PRINTLN("Missing MQTT connection. Not publishing data");
DEBUG_PRINTLN(F("Missing MQTT connection. Not publishing data"));
}
}
#endif

View File

@@ -132,7 +132,7 @@ class St7789DisplayUsermod : public Usermod {
* 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()
void setup() override
{
PinManagerPinType spiPins[] = { { spi_mosi, true }, { spi_miso, false}, { spi_sclk, true } };
if (!pinManager.allocateMultiplePins(spiPins, 3, PinOwner::HW_SPI)) { enabled = false; return; }
@@ -162,7 +162,7 @@ class St7789DisplayUsermod : public Usermod {
* connected() is called every time the WiFi is (re)connected
* Use it to initialize network interfaces
*/
void connected() {
void connected() override {
//Serial.println("Connected to WiFi!");
}
@@ -176,7 +176,7 @@ class St7789DisplayUsermod : public Usermod {
* 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() {
void loop() override {
char buff[LINE_BUFFER_SIZE];
// Check if we time interval for redrawing passes.
@@ -307,7 +307,7 @@ class St7789DisplayUsermod : public Usermod {
// Print estimated milliamp usage (must specify the LED type in LED prefs for this to be a reasonable estimate).
tft.print("Current: ");
tft.setTextColor(TFT_ORANGE);
tft.print(strip.currentMilliamps);
tft.print(BusManager::currentMilliamps());
tft.print("mA");
}
@@ -316,7 +316,7 @@ class St7789DisplayUsermod : public Usermod {
* 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)
void addToJsonInfo(JsonObject& root) override
{
JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u");
@@ -330,7 +330,7 @@ class St7789DisplayUsermod : public Usermod {
* 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)
void addToJsonState(JsonObject& root) override
{
//root["user0"] = userVar0;
}
@@ -340,7 +340,7 @@ class St7789DisplayUsermod : public Usermod {
* 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)
void readFromJsonState(JsonObject& root) override
{
//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!"));
@@ -361,7 +361,7 @@ class St7789DisplayUsermod : public Usermod {
*
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
*/
void addToConfig(JsonObject& root)
void addToConfig(JsonObject& root) override
{
JsonObject top = root.createNestedObject("ST7789");
JsonArray pins = top.createNestedArray("pin");
@@ -373,7 +373,7 @@ class St7789DisplayUsermod : public Usermod {
}
void appendConfigData() {
void appendConfigData() override {
oappend(SET_F("addInfo('ST7789:pin[]',0,'','SPI CS');"));
oappend(SET_F("addInfo('ST7789:pin[]',1,'','SPI DC');"));
oappend(SET_F("addInfo('ST7789:pin[]',2,'','SPI RST');"));
@@ -388,7 +388,7 @@ class St7789DisplayUsermod : public Usermod {
* 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 :)
*/
bool readFromConfig(JsonObject& root)
bool readFromConfig(JsonObject& root) override
{
//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)
@@ -400,7 +400,7 @@ class St7789DisplayUsermod : public Usermod {
* 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()
uint16_t getId() override
{
return USERMOD_ID_ST7789_DISPLAY;
}

View File

@@ -93,8 +93,8 @@ class Si7021_MQTT_HA : public Usermod
JsonObject device = doc.createNestedObject("device"); // attach the sensor to the same device
device["name"] = String(serverDescription);
device["model"] = "WLED";
device["manufacturer"] = "Aircoookie";
device["model"] = F(WLED_PRODUCT_NAME);
device["manufacturer"] = F(WLED_BRAND);
device["identifiers"] = String("wled-") + String(serverDescription);
device["sw_version"] = VERSION;

View File

@@ -3,7 +3,7 @@
* This 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)
* bytes 2400+ are currently ununsed, but might be used for future wled features
* bytes 2400+ are currently unused, but might be used for future wled features
*/
/*
@@ -144,7 +144,7 @@ void userLoop() {
// First row with Wifi name
tft.setCursor(1, 1);
tft.print(knownSsid.substring(0, tftcharwidth > 1 ? tftcharwidth - 1 : 0));
// Print `~` char to indicate that SSID is longer, than our dicplay
// Print `~` char to indicate that SSID is longer than our display
if (knownSsid.length() > tftcharwidth)
tft.print("~");

View File

@@ -18,7 +18,7 @@ Copy the example `platformio_override.ini` to the root directory. This file sho
* `USERMOD_DALLASTEMPERATURE` - enables this user mod wled00/usermods_list.cpp
* `USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL` - number of milliseconds between measurements, defaults to 60000 ms (60s)
All parameters can be configured at runtime via the Usermods settings page, including pin, temperature in degrees Celsius or Farenheit and measurement interval.
All parameters can be configured at runtime via the Usermods settings page, including pin, temperature in degrees Celsius or Fahrenheit and measurement interval.
## Project link

View File

@@ -48,6 +48,7 @@ class UsermodTemperature : public Usermod {
bool enabled = true;
bool HApublished = false;
int16_t idx = -1; // Domoticz virtual sensor idx
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
@@ -55,7 +56,11 @@ class UsermodTemperature : public Usermod {
static const char _readInterval[];
static const char _parasite[];
static const char _parasitePin[];
static const char _domoticzIDX[];
static const char _sensor[];
static const char _temperature[];
static const char _Temperature[];
//Dallas sensor quick (& dirty) reading. Credit to - Author: Peter Scargill, August 17th, 2013
float readDallas();
void requestTemperatures();
@@ -74,26 +79,26 @@ class UsermodTemperature : public Usermod {
inline float getTemperatureF() { return temperature * 1.8f + 32.0f; }
float getTemperature();
const char *getTemperatureUnit();
uint16_t getId() { return USERMOD_ID_TEMPERATURE; }
uint16_t getId() override { return USERMOD_ID_TEMPERATURE; }
void setup();
void loop();
//void connected();
void setup() override;
void loop() override;
//void connected() override;
#ifndef WLED_DISABLE_MQTT
void onMqttConnect(bool sessionPresent);
void onMqttConnect(bool sessionPresent) override;
#endif
//void onUpdateBegin(bool init);
//void onUpdateBegin(bool init) override;
//bool handleButton(uint8_t b);
//void handleOverlayDraw();
//bool handleButton(uint8_t b) override;
//void handleOverlayDraw() override;
void addToJsonInfo(JsonObject& root);
//void addToJsonState(JsonObject &root);
//void readFromJsonState(JsonObject &root);
void addToConfig(JsonObject &root);
bool readFromConfig(JsonObject &root);
void addToJsonInfo(JsonObject& root) override;
//void addToJsonState(JsonObject &root) override;
//void readFromJsonState(JsonObject &root) override;
void addToConfig(JsonObject &root) override;
bool readFromConfig(JsonObject &root) override;
void appendConfigData();
void appendConfigData() override;
};
//Dallas sensor quick (& dirty) reading. Credit to - Author: Peter Scargill, August 17th, 2013
@@ -108,9 +113,9 @@ float UsermodTemperature::readDallas() {
#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]);
for (byte i=0; i < 9; i++) DEBUG_PRINTF_P(PSTR("0x%02X "), data[i]);
DEBUG_PRINT(F(" => "));
DEBUG_PRINTF("0x%02X\n", OneWire::crc8(data,8));
DEBUG_PRINTF_P(PSTR("0x%02X\n"), OneWire::crc8(data,8));
}
#endif
switch(sensorFound) {
@@ -147,7 +152,7 @@ void UsermodTemperature::readTemperature() {
temperature = readDallas();
lastMeasurement = millis();
waitingForConversion = false;
//DEBUG_PRINTF("Read temperature %2.1f.\n", temperature); // does not work properly on 8266
//DEBUG_PRINTF_P(PSTR("Read temperature %2.1f.\n"), temperature); // does not work properly on 8266
DEBUG_PRINT(F("Read temperature "));
DEBUG_PRINTLN(temperature);
}
@@ -169,7 +174,7 @@ bool UsermodTemperature::findSensor() {
case 0x42: // DS28EA00
DEBUG_PRINTLN(F("Sensor found."));
sensorFound = deviceAddress[0];
DEBUG_PRINTF("0x%02X\n", sensorFound);
DEBUG_PRINTF_P(PSTR("0x%02X\n"), sensorFound);
return true;
}
}
@@ -189,9 +194,9 @@ void UsermodTemperature::publishHomeAssistantAutodiscovery() {
sprintf_P(buf, PSTR("%s Temperature"), serverDescription);
json[F("name")] = buf;
strcpy(buf, mqttDeviceTopic);
strcat_P(buf, PSTR("/temperature"));
strcat_P(buf, _Temperature);
json[F("state_topic")] = buf;
json[F("device_class")] = F("temperature");
json[F("device_class")] = FPSTR(_temperature);
json[F("unique_id")] = escapedMac.c_str();
json[F("unit_of_measurement")] = F("°C");
payload_size = serializeJson(json, json_str);
@@ -264,16 +269,25 @@ void UsermodTemperature::loop() {
#ifndef WLED_DISABLE_MQTT
if (WLED_MQTT_CONNECTED) {
char subuf[64];
char subuf[128];
strcpy(subuf, mqttDeviceTopic);
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"));
strcat_P(subuf, _Temperature);
mqtt->publish(subuf, 0, false, String(getTemperatureC()).c_str());
strcat_P(subuf, PSTR("_f"));
mqtt->publish(subuf, 0, false, String(getTemperatureF()).c_str());
if (idx > 0) {
StaticJsonDocument <128> msg;
msg[F("idx")] = idx;
msg[F("RSSI")] = WiFi.RSSI();
msg[F("nvalue")] = 0;
msg[F("svalue")] = String(getTemperatureC());
serializeJson(msg, subuf, 127);
mqtt->publish("domoticz/in", 0, false, subuf);
}
} else {
// publish something else to indicate status?
}
@@ -324,9 +338,9 @@ void UsermodTemperature::addToJsonInfo(JsonObject& root) {
temp.add(getTemperature());
temp.add(getTemperatureUnit());
JsonObject sensor = root[F("sensor")];
if (sensor.isNull()) sensor = root.createNestedObject(F("sensor"));
temp = sensor.createNestedArray(F("temperature"));
JsonObject sensor = root[FPSTR(_sensor)];
if (sensor.isNull()) sensor = root.createNestedObject(FPSTR(_sensor));
temp = sensor.createNestedArray(FPSTR(_temperature));
temp.add(getTemperature());
temp.add(getTemperatureUnit());
}
@@ -356,10 +370,11 @@ void UsermodTemperature::addToConfig(JsonObject &root) {
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
top[FPSTR(_enabled)] = enabled;
top["pin"] = temperaturePin; // usermodparam
top["degC"] = degC; // usermodparam
top[F("degC")] = degC; // usermodparam
top[FPSTR(_readInterval)] = readingInterval / 1000;
top[FPSTR(_parasite)] = parasite;
top[FPSTR(_parasitePin)] = parasitePin;
top[FPSTR(_domoticzIDX)] = idx;
DEBUG_PRINTLN(F("Temperature config saved."));
}
@@ -381,11 +396,12 @@ bool UsermodTemperature::readFromConfig(JsonObject &root) {
enabled = top[FPSTR(_enabled)] | enabled;
newTemperaturePin = top["pin"] | newTemperaturePin;
degC = top["degC"] | degC;
degC = top[F("degC")] | degC;
readingInterval = top[FPSTR(_readInterval)] | readingInterval/1000;
readingInterval = min(120,max(10,(int)readingInterval)) * 1000; // convert to ms
parasite = top[FPSTR(_parasite)] | parasite;
parasitePin = top[FPSTR(_parasitePin)] | parasitePin;
idx = top[FPSTR(_domoticzIDX)] | idx;
if (!initDone) {
// first run: reading from cfg.json
@@ -406,7 +422,7 @@ bool UsermodTemperature::readFromConfig(JsonObject &root) {
}
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[FPSTR(_parasitePin)].isNull();
return !top[FPSTR(_domoticzIDX)].isNull();
}
void UsermodTemperature::appendConfigData() {
@@ -430,3 +446,7 @@ const char UsermodTemperature::_enabled[] PROGMEM = "enabled";
const char UsermodTemperature::_readInterval[] PROGMEM = "read-interval-s";
const char UsermodTemperature::_parasite[] PROGMEM = "parasite-pwr";
const char UsermodTemperature::_parasitePin[] PROGMEM = "parasite-pwr-pin";
const char UsermodTemperature::_domoticzIDX[] PROGMEM = "domoticz-idx";
const char UsermodTemperature::_sensor[] PROGMEM = "sensor";
const char UsermodTemperature::_temperature[] PROGMEM = "temperature";
const char UsermodTemperature::_Temperature[] PROGMEM = "/temperature";

View File

@@ -0,0 +1,117 @@
/******************************************************************************
* @file : gridbw.h
* @brief : contains the tetris grid as binary so black and white version
******************************************************************************
* @attention
*
* Copyright (c) muebau 2023
* All rights reserved.</center></h2>
*
******************************************************************************
*/
#ifndef __GRIDBW_H__
#define __GRIDBW_H__
#include <iterator>
#include <vector>
#include "pieces.h"
using namespace std;
class GridBW
{
private:
public:
uint8_t width;
uint8_t height;
std::vector<uint32_t> pixels;
GridBW(uint8_t width, uint8_t height):
width(width),
height(height),
pixels(height)
{
if (width > 32)
{
throw std::invalid_argument("maximal width is 32");
}
}
void placePiece(Piece* piece, uint8_t x, uint8_t y)
{
for (uint8_t row = 4 - piece->getRotation().height; row < 4; row++)
{
pixels[y + (row - (4 - piece->getRotation().height))] |= piece->getGridRow(x, row, width);
}
}
void erasePiece(Piece* piece, uint8_t x, uint8_t y)
{
for (uint8_t row = 4 - piece->getRotation().height; row < 4; row++)
{
pixels[y + (row - (4 - piece->getRotation().height))] &= ~piece->getGridRow(x, row, width);
}
}
bool noCollision(Piece* piece, uint8_t x, uint8_t y)
{
//if it touches a wall it is a collision
if (x > (this->width - piece->getRotation().width) || y > this->height - piece->getRotation().height)
{
return false;
}
for (uint8_t row = 4 - piece->getRotation().height; row < 4; row++)
{
if (piece->getGridRow(x, row, width) & pixels[y + (row - (4 - piece->getRotation().height))])
{
return false;
}
}
return true;
}
void findLandingPosition(Piece* piece)
{
// move down until the piece bumps into some occupied pixels or the 'wall'
while (noCollision(piece, piece->x, piece->landingY))
{
piece->landingY++;
}
//at this point the positon is 'in the wall' or 'over some occupied pixel'
//so the previous position was the last correct one (clamped to 0 as minimum).
piece->landingY = piece->landingY > 0 ? piece->landingY - 1 : 0;
}
void cleanupFullLines()
{
uint8_t offset = 0;
//from "height - 1" to "0", so from bottom row to top
for (uint8_t row = height; row-- > 0; )
{
//full line?
if (isLineFull(row))
{
offset++;
pixels[row] = 0x0;
continue;
}
if (offset > 0)
{
pixels[row + offset] = pixels[row];
pixels[row] = 0x0;
}
}
}
bool isLineFull(uint8_t y)
{
return pixels[y] == (uint32_t)((1 << width) - 1);
}
};
#endif /* __GRIDBW_H__ */

View File

@@ -0,0 +1,132 @@
/******************************************************************************
* @file : gridcolor.h
* @brief : contains the tetris grid as 8bit indexed color version
******************************************************************************
* @attention
*
* Copyright (c) muebau 2023
* All rights reserved.</center></h2>
*
******************************************************************************
*/
#ifndef __GRIDCOLOR_H__
#define __GRIDCOLOR_H__
#include <stdint.h>
#include <stdbool.h>
#include <vector>
#include "gridbw.h"
#include "gridcolor.h"
using namespace std;
class GridColor
{
private:
public:
uint8_t width;
uint8_t height;
GridBW gridBW;
std::vector<uint8_t> pixels;
GridColor(uint8_t width, uint8_t height):
width(width),
height(height),
gridBW(width, height),
pixels(width* height)
{}
void clear()
{
for (uint8_t y = 0; y < height; y++)
{
gridBW.pixels[y] = 0x0;
for (int8_t x = 0; x < width; x++)
{
*getPixel(x, y) = 0;
}
}
}
void placePiece(Piece* piece, uint8_t x, uint8_t y)
{
for (uint8_t pieceY = 0; pieceY < piece->getRotation().height; pieceY++)
{
for (uint8_t pieceX = 0; pieceX < piece->getRotation().width; pieceX++)
{
if (piece->getPixel(pieceX, pieceY))
{
*getPixel(x + pieceX, y + pieceY) = piece->pieceData->colorIndex;
}
}
}
}
void erasePiece(Piece* piece, uint8_t x, uint8_t y)
{
for (uint8_t pieceY = 0; pieceY < piece->getRotation().height; pieceY++)
{
for (uint8_t pieceX = 0; pieceX < piece->getRotation().width; pieceX++)
{
if (piece->getPixel(pieceX, pieceY))
{
*getPixel(x + pieceX, y + pieceY) = 0;
}
}
}
}
void cleanupFullLines()
{
uint8_t offset = 0;
//from "height - 1" to "0", so from bottom row to top
for (uint8_t y = height; y-- > 0; )
{
if (gridBW.isLineFull(y))
{
offset++;
for (uint8_t x = 0; x < width; x++)
{
pixels[y * width + x] = 0;
}
continue;
}
if (offset > 0)
{
if (gridBW.pixels[y])
{
for (uint8_t x = 0; x < width; x++)
{
pixels[(y + offset) * width + x] = pixels[y * width + x];
pixels[y * width + x] = 0;
}
}
}
}
gridBW.cleanupFullLines();
}
uint8_t* getPixel(uint8_t x, uint8_t y)
{
return &pixels[y * width + x];
}
void sync()
{
for (uint8_t y = 0; y < height; y++)
{
gridBW.pixels[y] = 0x0;
for (int8_t x = 0; x < width; x++)
{
gridBW.pixels[y] <<= 1;
if (*getPixel(x, y) != 0)
{
gridBW.pixels[y] |= 0x1;
}
}
}
}
};
#endif /* __GRIDCOLOR_H__ */

View File

@@ -0,0 +1,184 @@
/******************************************************************************
* @file : pieces.h
* @brief : contains the tetris pieces with their colors indecies
******************************************************************************
* @attention
*
* Copyright (c) muebau 2022
* All rights reserved.</center></h2>
*
******************************************************************************
*/
#ifndef __PIECES_H__
#define __PIECES_H__
#include <stdint.h>
#include <stdbool.h>
#include <bitset>
#include <cstddef>
#include <cassert>
#include <iostream>
#define numPieces 7
struct PieceRotation
{
uint8_t width;
uint8_t height;
uint16_t rows;
};
struct PieceData
{
uint8_t rotCount;
uint8_t colorIndex;
PieceRotation rotations[4];
};
PieceData piecesData[numPieces] = {
// I
{
2,
1,
{
{ 1, 4, 0b0001000100010001},
{ 4, 1, 0b0000000000001111}
}
},
// O
{
1,
2,
{
{ 2, 2, 0b0000000000110011}
}
},
// Z
{
2,
3,
{
{ 3, 2, 0b0000000001100011},
{ 2, 3, 0b0000000100110010}
}
},
// S
{
2,
4,
{
{ 3, 2, 0b0000000000110110},
{ 2, 3, 0b0000001000110001}
}
},
// L
{
4,
5,
{
{ 2, 3, 0b0000001000100011},
{ 3, 2, 0b0000000001110100},
{ 2, 3, 0b0000001100010001},
{ 3, 2, 0b0000000000010111}
}
},
// J
{
4,
6,
{
{ 2, 3, 0b0000000100010011},
{ 3, 2, 0b0000000001000111},
{ 2, 3, 0b0000001100100010},
{ 3, 2, 0b0000000001110001}
}
},
// T
{
4,
7,
{
{ 3, 2, 0b0000000001110010},
{ 2, 3, 0b0000000100110001},
{ 3, 2, 0b0000000000100111},
{ 2, 3, 0b0000001000110010}
}
},
};
class Piece
{
private:
public:
uint8_t x;
uint8_t y;
PieceData* pieceData;
uint8_t rotation;
uint8_t landingY;
Piece(uint8_t pieceIndex = 0):
x(0),
y(0),
rotation(0),
landingY(0)
{
this->pieceData = &piecesData[pieceIndex];
}
void reset()
{
this->rotation = 0;
this->x = 0;
this->y = 0;
this->landingY = 0;
}
uint32_t getGridRow(uint8_t x, uint8_t y, uint8_t width)
{
if (x < width)
{
//shift the row with the "top-left" position to the "x" position
auto shiftx = (width - 1) - x;
auto topleftx = (getRotation().width - 1);
auto finalShift = shiftx - topleftx;
auto row = getRow(y);
auto finalResult = row << finalShift;
return finalResult;
}
return 0xffffffff;
}
uint8_t getRow(uint8_t y)
{
if (y < 4)
{
return (getRotation().rows >> (12 - (4 * y))) & 0xf;
}
return 0xf;
}
bool getPixel(uint8_t x, uint8_t y)
{
if(x > getRotation().width - 1 || y > getRotation().height - 1 )
{
return false;
}
if (x < 4 && y < 4)
{
return (getRow((4 - getRotation().height) + y) >> (3 - ((4 - getRotation().width) + x))) & 0x1;
}
return false;
}
PieceRotation getRotation()
{
return this->pieceData->rotations[rotation];
}
};
#endif /* __PIECES_H__ */

View File

@@ -0,0 +1,64 @@
/******************************************************************************
* @file : rating.h
* @brief : contains the tetris rating of a grid
******************************************************************************
* @attention
*
* Copyright (c) muebau 2022
* All rights reserved.</center></h2>
*
******************************************************************************
*/
#ifndef __RATING_H__
#define __RATING_H__
#include <stdint.h>
#include <float.h>
#include <stdbool.h>
#include <math.h>
#include <vector>
#include "rating.h"
using namespace std;
class Rating
{
private:
public:
uint8_t minHeight;
uint8_t maxHeight;
uint16_t holes;
uint8_t fullLines;
uint16_t bumpiness;
uint16_t aggregatedHeight;
double score;
uint8_t width;
std::vector<uint8_t> lineHights;
Rating(uint8_t width):
width(width),
lineHights(width)
{
reset();
}
void reset()
{
this->minHeight = 0;
this->maxHeight = 0;
for (uint8_t line = 0; line < this->width; line++)
{
this->lineHights[line] = 0;
}
this->holes = 0;
this->fullLines = 0;
this->bumpiness = 0;
this->aggregatedHeight = 0;
this->score = -DBL_MAX;
}
};
#endif /* __RATING_H__ */

View File

@@ -0,0 +1,33 @@
# Tetris AI effect usermod
This usermod brings you a effect brings a self playing Tetris game. The mod needs version 0.14 or above as it is based on matrix support. The effect was tested on an ESP32 with a WS2812B 16x16 matrix.
Version 1.0
## Installation
Just activate the usermod with `-D USERMOD_TETRISAI` and the effect will become available under the name 'Tetris AI'.
## Usage
It is best to set the background color to black, the border color to light grey and the game over color (foreground) to dark grey.
### Sliders and boxes
#### Sliders
* speed: speed the game plays
* look ahead: how many pieces is the AI allowed to know the next pieces (0 - 2)
* intelligence: how good the AI will play
* Rotate color: make the colors shift (rotate) every few cicles
* Mistakes free: how many good moves between mistakes (if activated)
#### Checkboxes
* show next: if true a space of 5 pixels from the right is used to show the next pieces. The whole segment is used for the grid otherwise.
* show border: if true an additional column of 1 pixel is used to draw a border between the grid and the next pieces
* mistakes: if true the worst instead of the best move is choosen every few moves (read above)
## Best results
If the speed is set to be a little bit faster than a good human could play with maximal intelligence and very few mistakes it makes people furious/happy at a party.

View File

@@ -0,0 +1,302 @@
/******************************************************************************
* @file : ai.h
* @brief : contains the heuristic
******************************************************************************
* @attention
*
* Copyright (c) muebau 2023
* All rights reserved.</center></h2>
*
******************************************************************************
*/
#ifndef __AI_H__
#define __AI_H__
#include "gridbw.h"
#include "rating.h"
using namespace std;
class TetrisAI
{
private:
public:
double aHeight;
double fullLines;
double holes;
double bumpiness;
bool findWorstMove = false;
uint8_t countOnes(uint32_t vector)
{
uint8_t count = 0;
while (vector)
{
vector &= (vector - 1);
count++;
}
return count;
}
void updateRating(GridBW grid, Rating* rating)
{
rating->minHeight = 0;
rating->maxHeight = 0;
rating->holes = 0;
rating->fullLines = 0;
rating->bumpiness = 0;
rating->aggregatedHeight = 0;
fill(rating->lineHights.begin(), rating->lineHights.end(), 0);
uint32_t columnvector = 0x0;
uint32_t lastcolumnvector = 0x0;
for (uint8_t row = 0; row < grid.height; row++)
{
columnvector |= grid.pixels[row];
//first (highest) column makes it
if (rating->maxHeight == 0 && columnvector)
{
rating->maxHeight = grid.height - row;
}
//if column vector is full we found the minimal height (or it stays zero)
if (rating->minHeight == 0 && (columnvector == (uint32_t)((1 << grid.width) - 1)))
{
rating->minHeight = grid.height - row;
}
//line full if all ones in mask :-)
if (grid.isLineFull(row))
{
rating->fullLines++;
}
//holes are basically a XOR with the "full" columns
rating->holes += countOnes(columnvector ^ grid.pixels[row]);
//calculate the difference (XOR) between the current column vector and the last one
uint32_t columnDelta = columnvector ^ lastcolumnvector;
//process every new column
uint8_t index = 0;
while (columnDelta)
{
//if this is a new column
if (columnDelta & 0x1)
{
//update hight of this column
rating->lineHights[(grid.width - 1) - index] = grid.height - row;
// update aggregatedHeight
rating->aggregatedHeight += grid.height - row;
}
index++;
columnDelta >>= 1;
}
lastcolumnvector = columnvector;
}
//compare every two columns to get the difference and add them up
for (uint8_t column = 1; column < grid.width; column++)
{
rating->bumpiness += abs(rating->lineHights[column - 1] - rating->lineHights[column]);
}
rating->score = (aHeight * (rating->aggregatedHeight)) + (fullLines * (rating->fullLines)) + (holes * (rating->holes)) + (bumpiness * (rating->bumpiness));
}
TetrisAI(): TetrisAI(-0.510066, 0.760666, -0.35663, -0.184483)
{}
TetrisAI(double aHeight, double fullLines, double holes, double bumpiness):
aHeight(aHeight),
fullLines(fullLines),
holes(holes),
bumpiness(bumpiness)
{}
void findBestMove(GridBW grid, Piece *piece)
{
vector<Piece> pieces = {*piece};
findBestMove(grid, &pieces);
*piece = pieces[0];
}
void findBestMove(GridBW grid, std::vector<Piece> *pieces)
{
findBestMove(grid, pieces->begin(), pieces->end());
}
void findBestMove(GridBW grid, std::vector<Piece>::iterator start, std::vector<Piece>::iterator end)
{
Rating bestRating(grid.width);
findBestMove(grid, start, end, &bestRating);
}
void findBestMove(GridBW grid, std::vector<Piece>::iterator start, std::vector<Piece>::iterator end, Rating* bestRating)
{
grid.cleanupFullLines();
Rating curRating(grid.width);
Rating deeperRating(grid.width);
Piece piece = *start;
// for every rotation of the piece
for (piece.rotation = 0; piece.rotation < piece.pieceData->rotCount; piece.rotation++)
{
// put piece to top left corner
piece.x = 0;
piece.y = 0;
//test for every column
for (piece.x = 0; piece.x <= grid.width - piece.getRotation().width; piece.x++)
{
//todo optimise by the use of the previous grids height
piece.landingY = 0;
//will set landingY to final position
grid.findLandingPosition(&piece);
// draw piece
grid.placePiece(&piece, piece.x, piece.landingY);
if(start == end - 1)
{
//at the deepest level
updateRating(grid, &curRating);
}
else
{
//go deeper to take another piece into account
findBestMove(grid, start + 1, end, &deeperRating);
curRating = deeperRating;
}
// eraese piece
grid.erasePiece(&piece, piece.x, piece.landingY);
if(findWorstMove)
{
//init rating for worst
if(bestRating->score == -DBL_MAX)
{
bestRating->score = DBL_MAX;
}
// update if we found a worse one
if (bestRating->score > curRating.score)
{
*bestRating = curRating;
(*start) = piece;
}
}
else
{
// update if we found a better one
if (bestRating->score < curRating.score)
{
*bestRating = curRating;
(*start) = piece;
}
}
}
}
}
bool findBestMoveNonBlocking(GridBW grid, std::vector<Piece>::iterator start, std::vector<Piece>::iterator end, Rating* bestRating)
{
//vector with pieces
//for every piece
//for every
switch (expression)
{
case INIT:
break;
default:
break;
}
}
bool findBestMoveNonBlocking(GridBW grid, std::vector<Piece>::iterator start, std::vector<Piece>::iterator end, Rating* bestRating)
{
//INIT
grid.cleanupFullLines();
Rating curRating(grid.width);
Rating deeperRating(grid.width);
Piece piece = *start;
// for every rotation of the piece
piece.rotation = 0;
//HANDLE
while (piece.rotation < piece.pieceData->rotCount)
{
// put piece to top left corner
piece.x = 0;
piece.y = 0;
//test for every column
piece.x = 0;
while (piece.x <= grid.width - piece.getRotation().width)
{
//todo optimise by the use of the previous grids height
piece.landingY = 0;
//will set landingY to final position
grid.findLandingPosition(&piece);
// draw piece
grid.placePiece(&piece, piece.x, piece.landingY);
if(start == end - 1)
{
//at the deepest level
updateRating(grid, &curRating);
}
else
{
//go deeper to take another piece into account
findBestMove(grid, start + 1, end, &deeperRating);
curRating = deeperRating;
}
// eraese piece
grid.erasePiece(&piece, piece.x, piece.landingY);
if(findWorstMove)
{
//init rating for worst
if(bestRating->score == -DBL_MAX)
{
bestRating->score = DBL_MAX;
}
// update if we found a worse one
if (bestRating->score > curRating.score)
{
*bestRating = curRating;
(*start) = piece;
}
}
else
{
// update if we found a better one
if (bestRating->score < curRating.score)
{
*bestRating = curRating;
(*start) = piece;
}
}
piece.x++;
}
piece.rotation++;
}
//EXIT
return true;
}
};
#endif /* __AI_H__ */

View File

@@ -0,0 +1,150 @@
/******************************************************************************
* @file : tetrisaigame.h
* @brief : main tetris functions
******************************************************************************
* @attention
*
* Copyright (c) muebau 2022
* All rights reserved.</center></h2>
*
******************************************************************************
*/
#ifndef __TETRISAIGAME_H__
#define __TETRISAIGAME_H__
#include <stdint.h>
#include <stdbool.h>
#include <vector>
#include "pieces.h"
#include "gridcolor.h"
#include "tetrisbag.h"
#include "tetrisai.h"
using namespace std;
class TetrisAIGame
{
private:
bool animateFallOfPiece(Piece* piece, bool skip)
{
if (skip || piece->y >= piece->landingY)
{
piece->y = piece->landingY;
grid.gridBW.placePiece(piece, piece->x, piece->landingY);
grid.placePiece(piece, piece->x, piece->y);
return false;
}
else
{
// eraese last drawing
grid.erasePiece(piece, piece->x, piece->y);
//move piece down
piece->y++;
// draw piece
grid.placePiece(piece, piece->x, piece->y);
return true;
}
}
public:
uint8_t width;
uint8_t height;
uint8_t nLookAhead;
TetrisBag bag;
GridColor grid;
TetrisAI ai;
Piece curPiece;
PieceData* piecesData;
enum States { INIT, TEST_GAME_OVER, GET_NEXT_PIECE, FIND_BEST_MOVE, ANIMATE_MOVE, ANIMATE_GAME_OVER } state = INIT;
TetrisAIGame(uint8_t width, uint8_t height, uint8_t nLookAhead, PieceData* piecesData, uint8_t nPieces):
width(width),
height(height),
nLookAhead(nLookAhead),
bag(nPieces, 1, nLookAhead),
grid(width, height + 4),
ai(),
piecesData(piecesData)
{
}
void nextPiece()
{
grid.cleanupFullLines();
bag.queuePiece();
}
void findBestMove()
{
ai.findBestMove(grid.gridBW, &bag.piecesQueue);
}
bool animateFall(bool skip)
{
return animateFallOfPiece(&(bag.piecesQueue[0]), skip);
}
bool isGameOver()
{
//if there is something in the 4 lines of the hidden area the game is over
return grid.gridBW.pixels[0] || grid.gridBW.pixels[1] || grid.gridBW.pixels[2] || grid.gridBW.pixels[3];
}
void poll()
{
switch (state)
{
case INIT:
reset();
state = TEST_GAME_OVER;
break;
case TEST_GAME_OVER:
if (isGameOver())
{
state = ANIMATE_GAME_OVER;
}
else
{
state = GET_NEXT_PIECE;
}
break;
case GET_NEXT_PIECE:
nextPiece();
state = FIND_BEST_MOVE;
break;
case FIND_BEST_MOVE:
findBestMove();
state = ANIMATE_MOVE;
break;
case ANIMATE_MOVE:
if (!animateFall(false))
{
state = TEST_GAME_OVER;
}
break;
case ANIMATE_GAME_OVER:
static auto curPixel = grid.pixels.size();
grid.pixels[curPixel] = 254;
if (curPixel == 0)
{
state = INIT;
curPixel = grid.pixels.size();
}
curPixel--;
break;
}
}
void reset()
{
grid.clear();
bag.init();
}
};
#endif /* __TETRISAIGAME_H__ */

View File

@@ -0,0 +1,100 @@
/******************************************************************************
* @file : tetrisbag.h
* @brief : the tetris implementation of a random piece generator
******************************************************************************
* @attention
*
* Copyright (c) muebau 2022
* All rights reserved.</center></h2>
*
******************************************************************************
*/
#ifndef __TETRISBAG_H__
#define __TETRISBAG_H__
#include <stdint.h>
#include <vector>
#include <algorithm>
#include "tetrisbag.h"
class TetrisBag
{
private:
public:
uint8_t nPieces;
uint8_t nBagLength;
uint8_t bagIdx;
std::vector<uint8_t> bag;
std::vector<Piece> piecesQueue;
TetrisBag(uint8_t nPieces, uint8_t nBagLength, uint8_t queueLength):
nPieces(nPieces),
nBagLength(nBagLength),
bag(nPieces * nBagLength),
piecesQueue(queueLength)
{
init();
}
void init()
{
//will shuffle the bag at first use
bagIdx = nPieces - 1;
for (uint8_t bagIndex = 0; bagIndex < nPieces * nBagLength; bagIndex++)
{
bag[bagIndex] = bagIndex % nPieces;
}
//will init the queue
for (uint8_t index = 0; index < piecesQueue.size(); index++)
{
queuePiece();
}
}
void shuffleBag()
{
uint8_t temp;
uint8_t swapIdx;
for (int index = nPieces - 1; index > 0; index--)
{
//get candidate to swap
swapIdx = rand() % index;
//swap it!
temp = bag[swapIdx];
bag[swapIdx] = bag[index];
bag[index] = temp;
}
}
Piece getNextPiece()
{
bagIdx++;
if (bagIdx >= nPieces)
{
shuffleBag();
bagIdx = 0;
}
return Piece(bag[bagIdx]);
}
void queuePiece()
{
//move vector to left
std::rotate(piecesQueue.begin(), piecesQueue.begin() + 1, piecesQueue.end());
piecesQueue[piecesQueue.size() - 1] = getNextPiece();
}
void queuePiece(uint8_t idx)
{
//move vector to left
std::rotate(piecesQueue.begin(), piecesQueue.begin() + 1, piecesQueue.end());
piecesQueue[piecesQueue.size() - 1] = Piece(idx % nPieces);
}
};
#endif /* __TETRISBAG_H__ */

View File

@@ -0,0 +1,222 @@
#pragma once
#include "wled.h"
#include "FX.h"
#include "fcn_declare.h"
#include "tetrisaigame.h"
// By: muebau
typedef struct TetrisAI_data
{
unsigned long lastTime = 0;
TetrisAIGame tetris;
uint8_t intelligence;
uint8_t rotate;
bool showNext;
bool showBorder;
uint8_t colorOffset;
uint8_t colorInc;
uint8_t mistaceCountdown;
} tetrisai_data;
void drawGrid(TetrisAIGame* tetris, TetrisAI_data* tetrisai_data)
{
SEGMENT.fill(SEGCOLOR(1));
//GRID
for (auto index_y = 4; index_y < tetris->grid.height; index_y++)
{
for (auto index_x = 0; index_x < tetris->grid.width; index_x++)
{
CRGB color;
if (*tetris->grid.getPixel(index_x, index_y) == 0)
{
//BG color
color = SEGCOLOR(1);
}
//game over animation
else if(*tetris->grid.getPixel(index_x, index_y) == 254)
{
//use fg
color = SEGCOLOR(0);
}
else
{
//spread the color over the whole palette
uint8_t colorIndex = *tetris->grid.getPixel(index_x, index_y) * 32;
colorIndex += tetrisai_data->colorOffset;
color = ColorFromPalette(SEGPALETTE, colorIndex, 255, NOBLEND);
}
SEGMENT.setPixelColorXY(index_x, index_y - 4, color);
}
}
tetrisai_data->colorOffset += tetrisai_data->colorInc;
//NEXT PIECE AREA
if (tetrisai_data->showNext)
{
//BORDER
if (tetrisai_data->showBorder)
{
//draw a line 6 pixels from right with the border color
for (auto index_y = 0; index_y < SEGMENT.virtualHeight(); index_y++)
{
SEGMENT.setPixelColorXY(SEGMENT.virtualWidth() - 6, index_y, SEGCOLOR(2));
}
}
//NEXT PIECE
int piecesOffsetX = SEGMENT.virtualWidth() - 4;
int piecesOffsetY = 1;
for (uint8_t nextPieceIdx = 1; nextPieceIdx < tetris->nLookAhead; nextPieceIdx++)
{
uint8_t pieceNbrOffsetY = (nextPieceIdx - 1) * 5;
Piece piece(tetris->bag.piecesQueue[nextPieceIdx]);
for (uint8_t pieceY = 0; pieceY < piece.getRotation().height; pieceY++)
{
for (uint8_t pieceX = 0; pieceX < piece.getRotation().width; pieceX++)
{
if (piece.getPixel(pieceX, pieceY))
{
uint8_t colIdx = ((piece.pieceData->colorIndex * 32) + tetrisai_data->colorOffset);
SEGMENT.setPixelColorXY(piecesOffsetX + pieceX, piecesOffsetY + pieceNbrOffsetY + pieceY, ColorFromPalette(SEGPALETTE, colIdx, 255, NOBLEND));
}
}
}
}
}
}
////////////////////////////
// 2D Tetris AI //
////////////////////////////
uint16_t mode_2DTetrisAI()
{
if (!strip.isMatrix || !SEGENV.allocateData(sizeof(tetrisai_data)))
{
// not a 2D set-up
SEGMENT.fill(SEGCOLOR(0));
return 350;
}
TetrisAI_data* tetrisai_data = reinterpret_cast<TetrisAI_data*>(SEGENV.data);
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
//range 0 - 1024ms => 1024/255 ~ 4
uint16_t msDelayMove = 1024 - (4 * SEGMENT.speed);
int16_t msDelayGameOver = msDelayMove / 4;
//range 0 - 2 (not including current)
uint8_t nLookAhead = SEGMENT.intensity ? (SEGMENT.intensity >> 7) + 2 : 1;
//range 0 - 16
tetrisai_data->colorInc = SEGMENT.custom2 >> 4;
if (!tetrisai_data->tetris || (tetrisai_data->tetris.nLookAhead != nLookAhead
|| tetrisai_data->showNext != SEGMENT.check1
|| tetrisai_data->showBorder != SEGMENT.check2
)
)
{
tetrisai_data->showNext = SEGMENT.check1;
tetrisai_data->showBorder = SEGMENT.check2;
//not more than 32 as this is the limit of this implementation
uint8_t gridWidth = cols < 32 ? cols : 32;
uint8_t gridHeight = rows;
// do we need space for the 'next' section?
if (tetrisai_data->showNext)
{
// make space for the piece and one pixel of space
gridWidth = gridWidth - 5;
// do we need space for a border?
if (tetrisai_data->showBorder)
{
gridWidth = gridWidth - 1;
}
}
tetrisai_data->tetris = TetrisAIGame(gridWidth, gridHeight, nLookAhead, piecesData, numPieces);
SEGMENT.fill(SEGCOLOR(1));
}
if (tetrisai_data->intelligence != SEGMENT.custom1)
{
tetrisai_data->intelligence = SEGMENT.custom1;
double dui = 0.2 - (0.2 * (tetrisai_data->intelligence / 255.0));
tetrisai_data->tetris.ai.aHeight = -0.510066 + dui;
tetrisai_data->tetris.ai.fullLines = 0.760666 - dui;
tetrisai_data->tetris.ai.holes = -0.35663 + dui;
tetrisai_data->tetris.ai.bumpiness = -0.184483 + dui;
}
if (tetrisai_data->tetris.state == TetrisAIGame::ANIMATE_MOVE)
{
if (millis() - tetrisai_data->lastTime > msDelayMove)
{
drawGrid(&tetrisai_data->tetris, tetrisai_data);
tetrisai_data->lastTime = millis();
tetrisai_data->tetris.poll();
}
}
else if (tetrisai_data->tetris.state == TetrisAIGame::ANIMATE_GAME_OVER)
{
if (millis() - tetrisai_data->lastTime > msDelayGameOver)
{
drawGrid(&tetrisai_data->tetris, tetrisai_data);
tetrisai_data->lastTime = millis();
tetrisai_data->tetris.poll();
}
}
else if (tetrisai_data->tetris.state == TetrisAIGame::FIND_BEST_MOVE)
{
if (SEGMENT.check3)
{
if(tetrisai_data->mistaceCountdown == 0)
{
tetrisai_data->tetris.ai.findWorstMove = true;
tetrisai_data->tetris.poll();
tetrisai_data->tetris.ai.findWorstMove = false;
tetrisai_data->mistaceCountdown = SEGMENT.custom3;
}
tetrisai_data->mistaceCountdown--;
}
tetrisai_data->tetris.poll();
}
else
{
tetrisai_data->tetris.poll();
}
return FRAMETIME;
} // mode_2DTetrisAI()
static const char _data_FX_MODE_2DTETRISAI[] PROGMEM = "Tetris AI@!,Look ahead,Intelligence,Rotate color,Mistake free,Show next,Border,Mistakes;Game Over,!,Border;!;2;sx=127,ix=64,c1=255,c2=0,c3=31,o1=1,o2=1,o3=0,pal=11";
class TetrisAIUsermod : public Usermod
{
private:
public:
void setup()
{
strip.addEffect(255, &mode_2DTetrisAI, _data_FX_MODE_2DTETRISAI);
}
void loop()
{
}
uint16_t getId()
{
return USERMOD_ID_TETRISAI;
}
};

View File

@@ -34,30 +34,30 @@ uint8_t DALLAS_PIN =23;
uint8_t SCL_PIN = 5;
uint8_t SDA_PIN = 4;
uint8_t DALLAS_PIN =13;
// uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8
// uint8_t RST_PIN = 16; // Un-comment for Heltec WiFi-Kit-8
#endif
//The SCL and SDA pins are defined here.
//ESP8266 Wemos D1 mini board use SCL=5 SDA=4 while ESP32 Wemos32 mini board use SCL=22 SDA=21
#define U8X8_PIN_SCL SCL_PIN
#define U8X8_PIN_SDA SDA_PIN
//#define U8X8_PIN_RESET RST_PIN // Uncoment for Heltec WiFi-Kit-8
//#define U8X8_PIN_RESET RST_PIN // Un-comment for Heltec WiFi-Kit-8
// Dallas sensor reading timer
long temptimer = millis();
long lastMeasure = 0;
#define Celsius // Show temperature mesaurement in Celcius otherwise is in Fahrenheit
#define Celsius // Show temperature measurement in Celsius otherwise is in Fahrenheit
// If display does not work or looks corrupted check the
// constructor reference:
// https://github.com/olikraus/u8g2/wiki/u8x8setupcpp
// or check the gallery:
// https://github.com/olikraus/u8g2/wiki/gallery
// --> First choise of cheap I2C OLED 128X32 0.91"
// --> First choice of cheap I2C OLED 128X32 0.91"
U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA
// --> Second choise of cheap I2C OLED 128X64 0.96" or 1.3"
// --> Second choice of cheap I2C OLED 128X64 0.96" or 1.3"
//U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA
// --> Third choise of Heltec WiFi-Kit-8 OLED 128X32 0.91"
// --> Third choice of Heltec WiFi-Kit-8 OLED 128X32 0.91"
//U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_RESET, U8X8_PIN_SCL, U8X8_PIN_SDA); // Constructor for Heltec WiFi-Kit-8
// gets called once at boot. Do all initialization that doesn't depend on network here
void userSetup() {
@@ -97,7 +97,7 @@ void userLoop() {
//----> Dallas temperature sensor MQTT publishing
temptimer = millis();
// Timer to publishe new temperature every 60 seconds
// Timer to publish new temperature every 60 seconds
if (temptimer - lastMeasure > 60000)
{
lastMeasure = temptimer;
@@ -106,7 +106,7 @@ void userLoop() {
if (mqtt != nullptr)
{
// Serial.println(Dallas(DALLAS_PIN,0));
//Gets prefered temperature scale based on selection in definitions section
//Gets preferred temperature scale based on selection in definitions section
#ifdef Celsius
int16_t board_temperature = Dallas(DALLAS_PIN,0);
#else
@@ -173,11 +173,11 @@ void userLoop() {
// First row with Wifi name
u8x8.setCursor(1, 0);
u8x8.print(knownSsid.substring(0, u8x8.getCols() > 1 ? u8x8.getCols() - 2 : 0));
// Print `~` char to indicate that SSID is longer, than owr dicplay
// Print `~` char to indicate that SSID is longer than our display
if (knownSsid.length() > u8x8.getCols())
u8x8.print("~");
// Second row with IP or Psssword
// Second row with IP or Password
u8x8.setCursor(1, 1);
// Print password in AP mode and if led is OFF.
if (apActive && bri == 0)

View File

@@ -6,7 +6,7 @@
void UpdateBME280Data();
#define Celsius // Show temperature mesaurement in Celcius otherwise is in Fahrenheit
#define Celsius // Show temperature measurement in Celsius otherwise is in Fahrenheit
BME280I2C bme; // Default : forced mode, standby time = 1000 ms
// Oversampling = pressure ×1, temperature ×1, humidity ×1, filter off,
@@ -16,25 +16,25 @@ 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
// uint8_t RST_PIN = 16; // Un-comment for Heltec WiFi-Kit-8
#endif
//The SCL and SDA pins are defined here.
//ESP8266 Wemos D1 mini board use SCL=5 SDA=4 while ESP32 Wemos32 mini board use SCL=22 SDA=21
#define U8X8_PIN_SCL SCL_PIN
#define U8X8_PIN_SDA SDA_PIN
//#define U8X8_PIN_RESET RST_PIN // Uncoment for Heltec WiFi-Kit-8
//#define U8X8_PIN_RESET RST_PIN // Un-comment for Heltec WiFi-Kit-8
// If display does not work or looks corrupted check the
// constructor reference:
// https://github.com/olikraus/u8g2/wiki/u8x8setupcpp
// or check the gallery:
// https://github.com/olikraus/u8g2/wiki/gallery
// --> First choise of cheap I2C OLED 128X32 0.91"
// --> First choice of cheap I2C OLED 128X32 0.91"
U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA
// --> Second choise of cheap I2C OLED 128X64 0.96" or 1.3"
// --> Second choice of cheap I2C OLED 128X64 0.96" or 1.3"
//U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA
// --> Third choise of Heltec WiFi-Kit-8 OLED 128X32 0.91"
// --> Third choice of Heltec WiFi-Kit-8 OLED 128X32 0.91"
//U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_RESET, U8X8_PIN_SCL, U8X8_PIN_SDA); // Constructor for Heltec WiFi-Kit-8
// gets called once at boot. Do all initialization that doesn't depend on network here
@@ -179,11 +179,11 @@ void userLoop() {
// First row with Wifi name
u8x8.setCursor(1, 0);
u8x8.print(knownSsid.substring(0, u8x8.getCols() > 1 ? u8x8.getCols() - 2 : 0));
// Print `~` char to indicate that SSID is longer, than owr dicplay
// Print `~` char to indicate that SSID is longer, than our display
if (knownSsid.length() > u8x8.getCols())
u8x8.print("~");
// Second row with IP or Psssword
// Second row with IP or Password
u8x8.setCursor(1, 1);
// Print password in AP mode and if led is OFF.
if (apActive && bri == 0)

View File

@@ -4,6 +4,10 @@
#include <driver/i2s.h>
#include <driver/adc.h>
#ifdef WLED_ENABLE_DMX
#error This audio reactive usermod is not compatible with DMX Out.
#endif
#ifndef ARDUINO_ARCH_ESP32
#error This audio reactive usermod does not support the ESP8266.
#endif
@@ -51,6 +55,8 @@
#define PLOT_PRINTF(x...)
#endif
#define MAX_PALETTES 3
// use audio source class (ESP32 specific)
#include "audio_source.h"
constexpr i2s_port_t I2S_PORT = I2S_NUM_0; // I2S port to use (do not change !)
@@ -73,11 +79,15 @@ static uint8_t audioSyncEnabled = 0; // bit field: bit 0 - send, bit 1
static bool udpSyncConnected = false; // UDP connection status -> true if connected to multicast group
// user settable parameters for limitSoundDynamics()
static bool limiterOn = true; // bool: enable / disable dynamics limiter
#ifdef UM_AUDIOREACTIVE_DYNAMICS_LIMITER_OFF
static bool limiterOn = false; // bool: enable / disable dynamics limiter
#else
static bool limiterOn = true;
#endif
static uint16_t attackTime = 80; // int: attack time in milliseconds. Default 0.08sec
static uint16_t decayTime = 1400; // int: decay time in milliseconds. Default 1.40sec
// user settable options for FFTResult scaling
static uint8_t FFTScalingMode = 3; // 0 none; 1 optimized logarithmic; 2 optimized linear; 3 optimized sqare root
static uint8_t FFTScalingMode = 3; // 0 none; 1 optimized logarithmic; 2 optimized linear; 3 optimized square root
//
// AGC presets
@@ -112,9 +122,9 @@ static float sampleAgc = 0.0f; // Smoothed AGC sample
static bool samplePeak = false; // Boolean flag for peak - used in effects. Responding routine may reset this flag. Auto-reset after strip.getMinShowDelay()
static uint8_t maxVol = 31; // Reasonable value for constant volume for 'peak detector', as it won't always trigger (deprecated)
static uint8_t binNum = 8; // Used to select the bin for FFT based beat detection (deprecated)
static bool udpSamplePeak = false; // Boolean flag for peak. Set at the same tiem as samplePeak, but reset by transmitAudioData
static bool udpSamplePeak = false; // Boolean flag for peak. Set at the same time as samplePeak, but reset by transmitAudioData
static unsigned long timeOfPeak = 0; // time of last sample peak detection.
static void detectSamplePeak(void); // peak detection function (needs scaled FFT reasults in vReal[])
static void detectSamplePeak(void); // peak detection function (needs scaled FFT results in vReal[])
static void autoResetPeak(void); // peak auto-reset function
@@ -173,31 +183,20 @@ constexpr uint16_t samplesFFT_2 = 256; // meaningfull part of FFT resul
// These are the input and output vectors. Input vectors receive computed results from FFT.
static float vReal[samplesFFT] = {0.0f}; // FFT sample inputs / freq output - these are our raw result bins
static float vImag[samplesFFT] = {0.0f}; // imaginary parts
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
static float windowWeighingFactors[samplesFFT] = {0.0f};
#endif
// Create FFT object
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
// lib_deps += https://github.com/kosme/arduinoFFT#develop @ 1.9.2
// these options actually cause slow-downs on all esp32 processors, don't use them.
// #define FFT_SPEED_OVER_PRECISION // enables use of reciprocals (1/x etc) - not faster on ESP32
// #define FFT_SQRT_APPROXIMATION // enables "quake3" style inverse sqrt - slower on ESP32
// Below options are forcing ArduinoFFT to use sqrtf() instead of sqrt()
#define sqrt(x) sqrtf(x) // little hack that reduces FFT time by 10-50% on ESP32
#define sqrt_internal sqrtf // see https://github.com/kosme/arduinoFFT/pull/83
#else
// around 40% slower on -S2
// lib_deps += https://github.com/blazoncek/arduinoFFT.git
#endif
// lib_deps += https://github.com/kosme/arduinoFFT#develop @ 1.9.2
// these options actually cause slow-downs on all esp32 processors, don't use them.
// #define FFT_SPEED_OVER_PRECISION // enables use of reciprocals (1/x etc) - not faster on ESP32
// #define FFT_SQRT_APPROXIMATION // enables "quake3" style inverse sqrt - slower on ESP32
// Below options are forcing ArduinoFFT to use sqrtf() instead of sqrt()
#define sqrt(x) sqrtf(x) // little hack that reduces FFT time by 10-50% on ESP32
#define sqrt_internal sqrtf // see https://github.com/kosme/arduinoFFT/pull/83
#include <arduinoFFT.h>
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
static ArduinoFFT<float> FFT = ArduinoFFT<float>( vReal, vImag, samplesFFT, SAMPLE_RATE, windowWeighingFactors);
#else
static arduinoFFT FFT = arduinoFFT(vReal, vImag, samplesFFT, SAMPLE_RATE);
#endif
// Helper functions
@@ -206,7 +205,7 @@ static float mapf(float x, float in_min, float in_max, float out_min, float out_
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
// compute average of several FFT resut bins
// compute average of several FFT result bins
static float fftAddAvg(int from, int to) {
float result = 0.0f;
for (int i = from; i <= to; i++) {
@@ -278,28 +277,13 @@ void FFTcode(void * parameter)
#endif
// run FFT (takes 3-5ms on ESP32, ~12ms on ESP32-S2)
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
FFT.dcRemoval(); // remove DC offset
FFT.windowing( FFTWindow::Flat_top, FFTDirection::Forward); // Weigh data using "Flat Top" function - better amplitude accuracy
//FFT.windowing(FFTWindow::Blackman_Harris, FFTDirection::Forward); // Weigh data using "Blackman- Harris" window - sharp peaks due to excellent sideband rejection
FFT.compute( FFTDirection::Forward ); // Compute FFT
FFT.complexToMagnitude(); // Compute magnitudes
#else
FFT.DCRemoval(); // let FFT lib remove DC component, so we don't need to care about this in getSamples()
//FFT.Windowing( FFT_WIN_TYP_HAMMING, FFT_FORWARD ); // Weigh data - standard Hamming window
//FFT.Windowing( FFT_WIN_TYP_BLACKMAN, FFT_FORWARD ); // Blackman window - better side freq rejection
//FFT.Windowing( FFT_WIN_TYP_BLACKMAN_HARRIS, FFT_FORWARD );// Blackman-Harris - excellent sideband rejection
FFT.Windowing( FFT_WIN_TYP_FLT_TOP, FFT_FORWARD ); // Flat Top Window - better amplitude accuracy
FFT.Compute( FFT_FORWARD ); // Compute FFT
FFT.ComplexToMagnitude(); // Compute magnitudes
#endif
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
FFT.majorPeak(FFT_MajorPeak, FFT_Magnitude); // let the effects know which freq was most dominant
#else
FFT.MajorPeak(&FFT_MajorPeak, &FFT_Magnitude); // let the effects know which freq was most dominant
#endif
FFT.majorPeak(&FFT_MajorPeak, &FFT_Magnitude); // let the effects know which freq was most dominant
FFT_MajorPeak = constrain(FFT_MajorPeak, 1.0f, 11025.0f); // restrict value to range expected by effects
#if defined(WLED_DEBUG) || defined(SR_DEBUG)
@@ -324,7 +308,7 @@ void FFTcode(void * parameter)
*
* Andrew's updated mapping of 256 bins down to the 16 result bins with Sample Freq = 10240, samplesFFT = 512 and some overlap.
* Based on testing, the lowest/Start frequency is 60 Hz (with bin 3) and a highest/End frequency of 5120 Hz in bin 255.
* Now, Take the 60Hz and multiply by 1.320367784 to get the next frequency and so on until the end. Then detetermine the bins.
* Now, Take the 60Hz and multiply by 1.320367784 to get the next frequency and so on until the end. Then determine the bins.
* End frequency = Start frequency * multiplier ^ 16
* Multiplier = (End frequency/ Start frequency) ^ 1/16
* Multiplier = 1.320367784
@@ -383,7 +367,7 @@ void FFTcode(void * parameter)
}
}
// post-processing of frequency channels (pink noise adjustment, AGC, smooting, scaling)
// post-processing of frequency channels (pink noise adjustment, AGC, smoothing, scaling)
postProcessFFTResults((fabsf(sampleAvg) > 0.25f)? true : false , NUM_GEQ_CHANNELS);
#if defined(WLED_DEBUG) || defined(SR_DEBUG)
@@ -430,7 +414,7 @@ static void runMicFilter(uint16_t numSamples, float *sampleBuffer) // p
// FIR lowpass, to remove high frequency noise
float highFilteredSample;
if (i < (numSamples-1)) highFilteredSample = beta1*sampleBuffer[i] + beta2*last_vals[0] + beta2*sampleBuffer[i+1]; // smooth out spikes
else highFilteredSample = beta1*sampleBuffer[i] + beta2*last_vals[0] + beta2*last_vals[1]; // spcial handling for last sample in array
else highFilteredSample = beta1*sampleBuffer[i] + beta2*last_vals[0] + beta2*last_vals[1]; // special handling for last sample in array
last_vals[1] = last_vals[0];
last_vals[0] = sampleBuffer[i];
sampleBuffer[i] = highFilteredSample;
@@ -612,8 +596,15 @@ class AudioReactive : public Usermod {
};
// set your config variables to their boot default value (this can also be done in readFromConfig() or a constructor if you prefer)
#ifdef UM_AUDIOREACTIVE_ENABLE
bool enabled = true;
#else
bool enabled = false;
#endif
bool initDone = false;
bool addPalettes = false;
int8_t palettes = 0;
// variables for UDP sound sync
WiFiUDP fftUdp; // UDP object for sound sync (from WiFi UDP, not Async UDP!)
@@ -627,7 +618,7 @@ class AudioReactive : public Usermod {
// variables used by getSample() and agcAvg()
int16_t micIn = 0; // Current sample starts with negative values and large values, which is why it's 16 bit signed
double sampleMax = 0.0; // Max sample over a few seconds. Needed for AGC controler.
double sampleMax = 0.0; // Max sample over a few seconds. Needed for AGC controller.
double micLev = 0.0; // Used to convert returned value to have '0' as minimum. A leveller
float expAdjF = 0.0f; // Used for exponential filter.
float sampleReal = 0.0f; // "sampleRaw" as float, to provide bits that are lost otherwise (before amplification by sampleGain or inputLevel). Needed for AGC.
@@ -649,13 +640,21 @@ class AudioReactive : public Usermod {
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
static const char _enabled[];
static const char _config[];
static const char _dynamics[];
static const char _frequency[];
static const char _inputLvl[];
static const char _analogmic[];
static const char _digitalmic[];
static const char _addPalettes[];
static const char UDP_SYNC_HEADER[];
static const char UDP_SYNC_HEADER_v1[];
// private methods
void removeAudioPalettes(void);
void createAudioPalettes(void);
CRGB getCRGBForBand(int x, int pal);
void fillAudioPalettes(void);
////////////////////
// Debug support //
@@ -745,13 +744,13 @@ class AudioReactive : public Usermod {
* 2. we use two setpoints, one at ~60%, and one at ~80% of the maximum signal
* 3. the amplification depends on signal level:
* a) normal zone - very slow adjustment
* b) emergency zome (<10% or >90%) - very fast adjustment
* b) emergency zone (<10% or >90%) - very fast adjustment
*/
void agcAvg(unsigned long the_time)
{
const int AGC_preset = (soundAgc > 0)? (soundAgc-1): 0; // make sure the _compiler_ knows this value will not change while we are inside the function
float lastMultAgc = multAgc; // last muliplier used
float lastMultAgc = multAgc; // last multiplier used
float multAgcTemp = multAgc; // new multiplier
float tmpAgc = sampleReal * multAgc; // what-if amplified signal
@@ -791,13 +790,13 @@ class AudioReactive : public Usermod {
if (((multAgcTemp > 0.085f) && (multAgcTemp < 6.5f)) //integrator anti-windup by clamping
&& (multAgc*sampleMax < agcZoneStop[AGC_preset])) //integrator ceiling (>140% of max)
control_integrated += control_error * 0.002 * 0.25; // 2ms = intgration time; 0.25 for damping
control_integrated += control_error * 0.002 * 0.25; // 2ms = integration time; 0.25 for damping
else
control_integrated *= 0.9; // spin down that beasty integrator
// apply PI Control
tmpAgc = sampleReal * lastMultAgc; // check "zone" of the signal using previous gain
if ((tmpAgc > agcZoneHigh[AGC_preset]) || (tmpAgc < soundSquelch + agcZoneLow[AGC_preset])) { // upper/lower emergy zone
if ((tmpAgc > agcZoneHigh[AGC_preset]) || (tmpAgc < soundSquelch + agcZoneLow[AGC_preset])) { // upper/lower energy zone
multAgcTemp = lastMultAgc + agcFollowFast[AGC_preset] * agcControlKp[AGC_preset] * control_error;
multAgcTemp += agcFollowFast[AGC_preset] * agcControlKi[AGC_preset] * control_integrated;
} else { // "normal zone"
@@ -805,7 +804,7 @@ class AudioReactive : public Usermod {
multAgcTemp += agcFollowSlow[AGC_preset] * agcControlKi[AGC_preset] * control_integrated;
}
// limit amplification again - PI controler sometimes "overshoots"
// limit amplification again - PI controller sometimes "overshoots"
//multAgcTemp = constrain(multAgcTemp, 0.015625f, 32.0f); // 1/64 < multAgcTemp < 32
if (multAgcTemp > 32.0f) multAgcTemp = 32.0f;
if (multAgcTemp < 1.0f/64.0f) multAgcTemp = 1.0f/64.0f;
@@ -835,7 +834,7 @@ class AudioReactive : public Usermod {
void getSample()
{
float sampleAdj; // Gain adjusted sample value
float tmpSample; // An interim sample variable used for calculatioins.
float tmpSample; // An interim sample variable used for calculations.
const float weighting = 0.2f; // Exponential filter weighting. Will be adjustable in a future release.
const int AGC_preset = (soundAgc > 0)? (soundAgc-1): 0; // make sure the _compiler_ knows this value will not change while we are inside the function
@@ -960,6 +959,8 @@ class AudioReactive : public Usermod {
//DEBUGSR_PRINTLN("Transmitting UDP Mic Packet");
audioSyncPacket transmitData;
memset(reinterpret_cast<void *>(&transmitData), 0, sizeof(transmitData)); // make sure that the packet - including "invisible" padding bytes added by the compiler - is fully initialized
strncpy_P(transmitData.header, PSTR(UDP_SYNC_HEADER), 6);
// transmit samples that were not modified by limitSampleDynamics()
transmitData.sampleRaw = (soundAgc) ? rawSampleAgc: sampleRaw;
@@ -1083,7 +1084,7 @@ class AudioReactive : public Usermod {
* You can use it to initialize variables, sensors or similar.
* It is called *AFTER* readFromConfig()
*/
void setup()
void setup() override
{
disableSoundProcessing = true; // just to be sure
if (!initDone) {
@@ -1175,6 +1176,7 @@ class AudioReactive : public Usermod {
DEBUGSR_PRINTLN(F("AR: Analog Microphone (left channel only)."));
audioSource = new I2SAdcSource(SAMPLE_RATE, BLOCK_SIZE);
delay(100);
useBandPassFilter = true; // PDM bandpass filter seems to help for bad quality analog
if (audioSource) audioSource->initialize(audioPin);
break;
#endif
@@ -1196,6 +1198,7 @@ class AudioReactive : public Usermod {
}
if (enabled) connectUDPSoundSync();
if (enabled && addPalettes) createAudioPalettes();
initDone = true;
}
@@ -1204,7 +1207,7 @@ class AudioReactive : public Usermod {
* connected() is called every time the WiFi is (re)connected
* Use it to initialize network interfaces
*/
void connected()
void connected() override
{
if (udpSyncConnected) { // clean-up: if open, close old UDP sync connection
udpSyncConnected = false;
@@ -1231,7 +1234,7 @@ class AudioReactive : public Usermod {
* 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()
void loop() override
{
static unsigned long lastUMRun = millis();
@@ -1253,16 +1256,16 @@ class AudioReactive : public Usermod {
{
#ifdef WLED_DEBUG
if ((disableSoundProcessing == false) && (audioSyncEnabled == 0)) { // we just switched to "disabled"
DEBUG_PRINTLN("[AR userLoop] realtime mode active - audio processing suspended.");
DEBUG_PRINTF( " RealtimeMode = %d; RealtimeOverride = %d\n", int(realtimeMode), int(realtimeOverride));
DEBUG_PRINTLN(F("[AR userLoop] realtime mode active - audio processing suspended."));
DEBUG_PRINTF_P(PSTR(" RealtimeMode = %d; RealtimeOverride = %d\n"), int(realtimeMode), int(realtimeOverride));
}
#endif
disableSoundProcessing = true;
} else {
#ifdef WLED_DEBUG
if ((disableSoundProcessing == true) && (audioSyncEnabled == 0) && audioSource->isInitialized()) { // we just switched to "enabled"
DEBUG_PRINTLN("[AR userLoop] realtime mode ended - audio processing resumed.");
DEBUG_PRINTF( " RealtimeMode = %d; RealtimeOverride = %d\n", int(realtimeMode), int(realtimeOverride));
DEBUG_PRINTLN(F("[AR userLoop] realtime mode ended - audio processing resumed."));
DEBUG_PRINTF_P(PSTR(" RealtimeMode = %d; RealtimeOverride = %d\n"), int(realtimeMode), int(realtimeOverride));
}
#endif
if ((disableSoundProcessing == true) && (audioSyncEnabled == 0)) lastUMRun = millis(); // just left "realtime mode" - update timekeeping
@@ -1286,7 +1289,7 @@ class AudioReactive : public Usermod {
// complain when audio userloop has been delayed for long time. Currently we need userloop running between 500 and 1500 times per second.
// softhack007 disabled temporarily - avoid serial console spam with MANY leds and low FPS
//if ((userloopDelay > 65) && !disableSoundProcessing && (audioSyncEnabled == 0)) {
//DEBUG_PRINTF("[AR userLoop] hickup detected -> was inactive for last %d millis!\n", userloopDelay);
// DEBUG_PRINTF_P(PSTR("[AR userLoop] hiccup detected -> was inactive for last %d millis!\n"), userloopDelay);
//}
#endif
@@ -1358,10 +1361,11 @@ class AudioReactive : public Usermod {
lastTime = millis();
}
fillAudioPalettes();
}
bool getUMData(um_data_t **data)
bool getUMData(um_data_t **data) override
{
if (!data || !enabled) return false; // no pointer provided by caller or not enabled -> exit
*data = um_data;
@@ -1369,7 +1373,7 @@ class AudioReactive : public Usermod {
}
void onUpdateBegin(bool init)
void onUpdateBegin(bool init) override
{
#ifdef WLED_DEBUG
fftTime = sampleTime = 0;
@@ -1408,7 +1412,7 @@ class AudioReactive : public Usermod {
xTaskCreateUniversal( // xTaskCreateUniversal also works on -S2 and -C3 with single core
FFTcode, // Function to implement the task
"FFT", // Name of the task
5000, // Stack size in words
3592, // Stack size in words // 3592 leaves 800-1024 bytes of task stack free
NULL, // Task input parameter
FFTTASK_PRIORITY, // Priority of the task
&FFT_Task // Task handle
@@ -1424,7 +1428,7 @@ class AudioReactive : public Usermod {
* handleButton() can be used to override default button behaviour. Returning true
* will prevent button working in a default way.
*/
bool handleButton(uint8_t b) {
bool handleButton(uint8_t b) override {
yield();
// crude way of determining if audio input is analog
// better would be for AudioSource to implement getType()
@@ -1447,7 +1451,7 @@ class AudioReactive : public Usermod {
* 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)
void addToJsonInfo(JsonObject& root) override
{
char myStringBuffer[16]; // buffer for snprintf()
JsonObject user = root["u"];
@@ -1502,7 +1506,7 @@ class AudioReactive : public Usermod {
} else {
// Analog or I2S digital input
if (audioSource && (audioSource->isInitialized())) {
// audio source sucessfully configured
// audio source successfully configured
if (audioSource->getType() == AudioSource::Type_I2SAdc) {
infoArr.add(F("ADC analog"));
} else {
@@ -1586,7 +1590,7 @@ class AudioReactive : public Usermod {
* 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)
void addToJsonState(JsonObject& root) override
{
if (!initDone) return; // prevent crash on boot applyPreset()
JsonObject usermod = root[FPSTR(_name)];
@@ -1601,7 +1605,7 @@ class AudioReactive : public Usermod {
* 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)
void readFromJsonState(JsonObject& root) override
{
if (!initDone) return; // prevent crash on boot applyPreset()
bool prevEnabled = enabled;
@@ -1610,13 +1614,28 @@ class AudioReactive : public Usermod {
if (usermod[FPSTR(_enabled)].is<bool>()) {
enabled = usermod[FPSTR(_enabled)].as<bool>();
if (prevEnabled != enabled) onUpdateBegin(!enabled);
if (addPalettes) {
// add/remove custom/audioreactive palettes
if (prevEnabled && !enabled) removeAudioPalettes();
if (!prevEnabled && enabled) createAudioPalettes();
}
}
if (usermod[FPSTR(_inputLvl)].is<int>()) {
inputLevel = min(255,max(0,usermod[FPSTR(_inputLvl)].as<int>()));
}
}
if (root.containsKey(F("rmcpal")) && root[F("rmcpal")].as<bool>()) {
// handle removal of custom palettes from JSON call so we don't break things
removeAudioPalettes();
}
}
void onStateChange(uint8_t callMode) override {
if (initDone && enabled && addPalettes && palettes==0 && strip.customPalettes.size()<10) {
// if palettes were removed during JSON call re-add them
createAudioPalettes();
}
}
/*
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
@@ -1653,10 +1672,11 @@ class AudioReactive : public Usermod {
*
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
*/
void addToConfig(JsonObject& root)
void addToConfig(JsonObject& root) override
{
JsonObject top = root.createNestedObject(FPSTR(_name));
top[FPSTR(_enabled)] = enabled;
top[FPSTR(_addPalettes)] = addPalettes;
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
JsonObject amic = top.createNestedObject(FPSTR(_analogmic));
@@ -1664,29 +1684,29 @@ class AudioReactive : public Usermod {
#endif
JsonObject dmic = top.createNestedObject(FPSTR(_digitalmic));
dmic[F("type")] = dmType;
dmic["type"] = dmType;
JsonArray pinArray = dmic.createNestedArray("pin");
pinArray.add(i2ssdPin);
pinArray.add(i2swsPin);
pinArray.add(i2sckPin);
pinArray.add(mclkPin);
JsonObject cfg = top.createNestedObject("config");
JsonObject cfg = top.createNestedObject(FPSTR(_config));
cfg[F("squelch")] = soundSquelch;
cfg[F("gain")] = sampleGain;
cfg[F("AGC")] = soundAgc;
JsonObject dynLim = top.createNestedObject("dynamics");
JsonObject dynLim = top.createNestedObject(FPSTR(_dynamics));
dynLim[F("limiter")] = limiterOn;
dynLim[F("rise")] = attackTime;
dynLim[F("fall")] = decayTime;
JsonObject freqScale = top.createNestedObject("frequency");
JsonObject freqScale = top.createNestedObject(FPSTR(_frequency));
freqScale[F("scale")] = FFTScalingMode;
JsonObject sync = top.createNestedObject("sync");
sync[F("port")] = audioSyncPort;
sync[F("mode")] = audioSyncEnabled;
sync["port"] = audioSyncPort;
sync["mode"] = audioSyncEnabled;
}
@@ -1705,12 +1725,15 @@ class AudioReactive : public Usermod {
*
* This function is guaranteed to be called on boot, but could also be called every time settings are updated
*/
bool readFromConfig(JsonObject& root)
bool readFromConfig(JsonObject& root) override
{
JsonObject top = root[FPSTR(_name)];
bool configComplete = !top.isNull();
bool oldEnabled = enabled;
bool oldAddPalettes = addPalettes;
configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled);
configComplete &= getJsonValue(top[FPSTR(_addPalettes)], addPalettes);
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
configComplete &= getJsonValue(top[FPSTR(_analogmic)]["pin"], audioPin);
@@ -1731,24 +1754,29 @@ class AudioReactive : public Usermod {
configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][2], i2sckPin);
configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][3], mclkPin);
configComplete &= getJsonValue(top["config"][F("squelch")], soundSquelch);
configComplete &= getJsonValue(top["config"][F("gain")], sampleGain);
configComplete &= getJsonValue(top["config"][F("AGC")], soundAgc);
configComplete &= getJsonValue(top[FPSTR(_config)][F("squelch")], soundSquelch);
configComplete &= getJsonValue(top[FPSTR(_config)][F("gain")], sampleGain);
configComplete &= getJsonValue(top[FPSTR(_config)][F("AGC")], soundAgc);
configComplete &= getJsonValue(top["dynamics"][F("limiter")], limiterOn);
configComplete &= getJsonValue(top["dynamics"][F("rise")], attackTime);
configComplete &= getJsonValue(top["dynamics"][F("fall")], decayTime);
configComplete &= getJsonValue(top[FPSTR(_dynamics)][F("limiter")], limiterOn);
configComplete &= getJsonValue(top[FPSTR(_dynamics)][F("rise")], attackTime);
configComplete &= getJsonValue(top[FPSTR(_dynamics)][F("fall")], decayTime);
configComplete &= getJsonValue(top["frequency"][F("scale")], FFTScalingMode);
configComplete &= getJsonValue(top[FPSTR(_frequency)][F("scale")], FFTScalingMode);
configComplete &= getJsonValue(top["sync"][F("port")], audioSyncPort);
configComplete &= getJsonValue(top["sync"][F("mode")], audioSyncEnabled);
configComplete &= getJsonValue(top["sync"]["port"], audioSyncPort);
configComplete &= getJsonValue(top["sync"]["mode"], audioSyncEnabled);
if (initDone) {
// add/remove custom/audioreactive palettes
if ((oldAddPalettes && !addPalettes) || (oldAddPalettes && !enabled)) removeAudioPalettes();
if ((addPalettes && !oldAddPalettes && enabled) || (addPalettes && !oldEnabled && enabled)) createAudioPalettes();
} // else setup() will create palettes
return configComplete;
}
void appendConfigData()
void appendConfigData() override
{
oappend(SET_F("dd=addDropdown('AudioReactive','digitalmic:type');"));
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
@@ -1803,7 +1831,7 @@ class AudioReactive : public Usermod {
* 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()
//void handleOverlayDraw() override
//{
//strip.setPixelColor(0, RGBW32(0,0,0,0)) // set the first pixel to black
//}
@@ -1813,19 +1841,109 @@ class AudioReactive : public Usermod {
* 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()
uint16_t getId() override
{
return USERMOD_ID_AUDIOREACTIVE;
}
};
void AudioReactive::removeAudioPalettes(void) {
DEBUG_PRINTLN(F("Removing audio palettes."));
while (palettes>0) {
strip.customPalettes.pop_back();
DEBUG_PRINTLN(palettes);
palettes--;
}
DEBUG_PRINT(F("Total # of palettes: ")); DEBUG_PRINTLN(strip.customPalettes.size());
}
void AudioReactive::createAudioPalettes(void) {
DEBUG_PRINT(F("Total # of palettes: ")); DEBUG_PRINTLN(strip.customPalettes.size());
if (palettes) return;
DEBUG_PRINTLN(F("Adding audio palettes."));
for (int i=0; i<MAX_PALETTES; i++)
if (strip.customPalettes.size() < 10) {
strip.customPalettes.push_back(CRGBPalette16(CRGB(BLACK)));
palettes++;
DEBUG_PRINTLN(palettes);
} else break;
}
// credit @netmindz ar palette, adapted for usermod @blazoncek
CRGB AudioReactive::getCRGBForBand(int x, int pal) {
CRGB value;
CHSV hsv;
int b;
switch (pal) {
case 2:
b = map(x, 0, 255, 0, NUM_GEQ_CHANNELS/2); // convert palette position to lower half of freq band
hsv = CHSV(fftResult[b], 255, x);
hsv2rgb_rainbow(hsv, value); // convert to R,G,B
break;
case 1:
b = map(x, 1, 255, 0, 10); // convert palette position to lower half of freq band
hsv = CHSV(fftResult[b], 255, map(fftResult[b], 0, 255, 30, 255)); // pick hue
hsv2rgb_rainbow(hsv, value); // convert to R,G,B
break;
default:
if (x == 1) {
value = CRGB(fftResult[10]/2, fftResult[4]/2, fftResult[0]/2);
} else if(x == 255) {
value = CRGB(fftResult[10]/2, fftResult[0]/2, fftResult[4]/2);
} else {
value = CRGB(fftResult[0]/2, fftResult[4]/2, fftResult[10]/2);
}
break;
}
return value;
}
void AudioReactive::fillAudioPalettes() {
if (!palettes) return;
size_t lastCustPalette = strip.customPalettes.size();
if (lastCustPalette >= palettes) lastCustPalette -= palettes;
for (size_t pal=0; pal<palettes; pal++) {
uint8_t tcp[16]; // Needs to be 4 times however many colors are being used.
// 3 colors = 12, 4 colors = 16, etc.
tcp[0] = 0; // anchor of first color - must be zero
tcp[1] = 0;
tcp[2] = 0;
tcp[3] = 0;
CRGB rgb = getCRGBForBand(1, pal);
tcp[4] = 1; // anchor of first color
tcp[5] = rgb.r;
tcp[6] = rgb.g;
tcp[7] = rgb.b;
rgb = getCRGBForBand(128, pal);
tcp[8] = 128;
tcp[9] = rgb.r;
tcp[10] = rgb.g;
tcp[11] = rgb.b;
rgb = getCRGBForBand(255, pal);
tcp[12] = 255; // anchor of last color - must be 255
tcp[13] = rgb.r;
tcp[14] = rgb.g;
tcp[15] = rgb.b;
strip.customPalettes[lastCustPalette+pal].loadDynamicGradientPalette(tcp);
}
}
// strings to reduce flash memory usage (used more than twice)
const char AudioReactive::_name[] PROGMEM = "AudioReactive";
const char AudioReactive::_enabled[] PROGMEM = "enabled";
const char AudioReactive::_config[] PROGMEM = "config";
const char AudioReactive::_dynamics[] PROGMEM = "dynamics";
const char AudioReactive::_frequency[] PROGMEM = "frequency";
const char AudioReactive::_inputLvl[] PROGMEM = "inputLevel";
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
const char AudioReactive::_analogmic[] PROGMEM = "analogmic";
#endif
const char AudioReactive::_digitalmic[] PROGMEM = "digitalmic";
const char AudioReactive::_addPalettes[] PROGMEM = "add-palettes";
const char AudioReactive::UDP_SYNC_HEADER[] PROGMEM = "00002"; // new sync header version, as format no longer compatible with previous structure
const char AudioReactive::UDP_SYNC_HEADER_v1[] PROGMEM = "00001"; // old sync header version - need to add backwards-compatibility feature

View File

@@ -44,7 +44,7 @@
// benefit: analog mic inputs will be sampled contiously -> better response times and less "glitches"
// WARNING: this option WILL lock-up your device in case that any other analogRead() operation is performed;
// for example if you want to read "analog buttons"
//#define I2S_GRAB_ADC1_COMPLETELY // (experimental) continously sample analog ADC microphone. WARNING will cause analogRead() lock-up
//#define I2S_GRAB_ADC1_COMPLETELY // (experimental) continuously sample analog ADC microphone. WARNING will cause analogRead() lock-up
// data type requested from the I2S driver - currently we always use 32bit
//#define I2S_USE_16BIT_SAMPLES // (experimental) define this to request 16bit - more efficient but possibly less compatible
@@ -122,7 +122,7 @@ class AudioSource {
This function needs to take care of anything that needs to be done
before samples can be obtained from the microphone.
*/
virtual void initialize(int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) = 0;
virtual void initialize(int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) = 0;
/* Deinitialize
Release all resources and deactivate any functionality that is used
@@ -191,7 +191,8 @@ class I2SSource : public AudioSource {
};
}
virtual void initialize(int8_t i2swsPin = I2S_PIN_NO_CHANGE, int8_t i2ssdPin = I2S_PIN_NO_CHANGE, int8_t i2sckPin = I2S_PIN_NO_CHANGE, int8_t mclkPin = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) {
virtual void initialize(int8_t i2swsPin = I2S_PIN_NO_CHANGE, int8_t i2ssdPin = I2S_PIN_NO_CHANGE, int8_t i2sckPin = I2S_PIN_NO_CHANGE, int8_t mclkPin = I2S_PIN_NO_CHANGE) {
DEBUGSR_PRINTLN(F("I2SSource:: initialize()."));
if (i2swsPin != I2S_PIN_NO_CHANGE && i2ssdPin != I2S_PIN_NO_CHANGE) {
if (!pinManager.allocatePin(i2swsPin, true, PinOwner::UM_Audioreactive) ||
!pinManager.allocatePin(i2ssdPin, false, PinOwner::UM_Audioreactive)) { // #206
@@ -377,7 +378,7 @@ class I2SSource : public AudioSource {
};
/* ES7243 Microphone
This is an I2S microphone that requires ininitialization over
This is an I2S microphone that requires initialization over
I2C before I2S data can be received
*/
class ES7243 : public I2SSource {
@@ -412,6 +413,7 @@ public:
};
void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) {
DEBUGSR_PRINTLN(F("ES7243:: initialize();"));
if ((i2sckPin < 0) || (mclkPin < 0)) {
DEBUGSR_PRINTF("\nAR: invalid I2S pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin);
return;
@@ -427,8 +429,8 @@ public:
}
};
/* ES8388 Sound Modude
This is an I2S sound processing unit that requires ininitialization over
/* ES8388 Sound Module
This is an I2S sound processing unit that requires initialization over
I2C before I2S data can be received.
*/
class ES8388Source : public I2SSource {
@@ -473,7 +475,7 @@ class ES8388Source : public I2SSource {
// The mics *and* line-in are BOTH connected to LIN2/RIN2 on the AudioKit
// so there's no way to completely eliminate the mics. It's also hella noisy.
// Line-in works OK on the AudioKit, generally speaking, as the mics really need
// amplification to be noticable in a quiet room. If you're in a very loud room,
// amplification to be noticeable in a quiet room. If you're in a very loud room,
// the mics on the AudioKit WILL pick up sound even in line-in mode.
// TL;DR: Don't use the AudioKit for anything, use the LyraT.
//
@@ -527,7 +529,7 @@ class ES8388Source : public I2SSource {
};
void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) {
DEBUGSR_PRINTLN(F("ES8388Source:: initialize();"));
if ((i2sckPin < 0) || (mclkPin < 0)) {
DEBUGSR_PRINTF("\nAR: invalid I2S pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin);
return;
@@ -584,7 +586,8 @@ class I2SAdcSource : public I2SSource {
/* identify Audiosource type - I2S-ADC*/
AudioSourceType getType(void) {return(Type_I2SAdc);}
void initialize(int8_t audioPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) {
void initialize(int8_t audioPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) {
DEBUGSR_PRINTLN(F("I2SAdcSource:: initialize()."));
_myADCchannel = 0x0F;
if(!pinManager.allocatePin(audioPin, false, PinOwner::UM_Audioreactive)) {
DEBUGSR_PRINTF("failed to allocate GPIO for audio analog input: %d\n", audioPin);
@@ -755,7 +758,8 @@ class SPH0654 : public I2SSource {
I2SSource(sampleRate, blockSize, sampleScale)
{}
void initialize(uint8_t i2swsPin, uint8_t i2ssdPin, uint8_t i2sckPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) {
void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t = I2S_PIN_NO_CHANGE) {
DEBUGSR_PRINTLN(F("SPH0654:: initialize();"));
I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin);
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
// these registers are only existing in "classic" ESP32

View File

@@ -1,6 +1,6 @@
# Audioreactive usermod
Enabless controlling LEDs via audio input. Audio source can be a microphone or analog-in (AUX) using an appropriate adapter.
Enables controlling LEDs via audio input. Audio source can be a microphone or analog-in (AUX) using an appropriate adapter.
Supported microphones range from analog (MAX4466, MAX9814, ...) to digital (INMP441, ICS-43434, ...).
Does audio processing and provides data structure that specially written effects can use.
@@ -19,7 +19,7 @@ This usermod is an evolution of [SR-WLED](https://github.com/atuline/WLED), and
## Supported MCUs
This audioreactive usermod works best on "classic ESP32" (dual core), and on ESP32-S3 which also has dual core and hardware floating point support.
It will compile succesfully for ESP32-S2 and ESP32-C3, however might not work well, as other WLED functions will become slow. Audio processing requires a lot of computing power, which can be problematic on smaller MCUs like -S2 and -C3.
It will compile successfully for ESP32-S2 and ESP32-C3, however might not work well, as other WLED functions will become slow. Audio processing requires a lot of computing power, which can be problematic on smaller MCUs like -S2 and -C3.
Analog audio is only possible on "classic" ESP32, but not on other MCUs like ESP32-S3.
@@ -35,7 +35,7 @@ Customised _arduinoFFT_ library for use with this usermod can be found at https:
### using latest (develop) _arduinoFFT_ library
Alternatively, you can use the latest arduinoFFT development version.
ArduinoFFT `develop` library is slightly more accurate, and slighly faster than our customised library, however also needs additional 2kB RAM.
ArduinoFFT `develop` library is slightly more accurate, and slightly faster than our customised library, however also needs additional 2kB RAM.
* `build_flags` = `-D USERMOD_AUDIOREACTIVE` `-D UM_AUDIOREACTIVE_USE_NEW_FFT`
* `lib_deps`= `https://github.com/kosme/arduinoFFT#develop @ 1.9.2`
@@ -55,6 +55,11 @@ If you want to define default GPIOs during compile time, use the following (defa
- `-D ES7243_SDAPIN` : GPIO for I2C SDA pin on ES7243 microphone (-1)
- `-D ES7243_SCLPIN` : GPIO for I2C SCL pin on ES7243 microphone (-1)
Other options:
- `-D UM_AUDIOREACTIVE_ENABLE` : makes usermod default enabled (not the same as include into build option!)
- `-D UM_AUDIOREACTIVE_DYNAMICS_LIMITER_OFF` : disables rise/fall limiter default
**NOTE** I2S is used for analog audio sampling. Hence, the analog *buttons* (i.e. potentiometers) are disabled when running this usermod with an analog microphone.
### Advanced Compile-Time Options
@@ -63,7 +68,7 @@ You can use the following additional flags in your `build_flags`
* `-D SR_GAIN=x` : Default "gain" setting (60)
* `-D I2S_USE_RIGHT_CHANNEL`: Use RIGHT instead of LEFT channel (not recommended unless you strictly need this).
* `-D I2S_USE_16BIT_SAMPLES`: Use 16bit instead of 32bit for internal sample buffers. Reduces sampling quality, but frees some RAM ressources (not recommended unless you absolutely need this).
* `-D I2S_GRAB_ADC1_COMPLETELY`: Experimental: continously sample analog ADC microphone. Only effective on ESP32. WARNING this _will_ cause conflicts(lock-up) with any analogRead() call.
* `-D I2S_GRAB_ADC1_COMPLETELY`: Experimental: continuously sample analog ADC microphone. Only effective on ESP32. WARNING this _will_ cause conflicts(lock-up) with any analogRead() call.
* `-D MIC_LOGGER` : (debugging) Logs samples from the microphone to serial USB. Use with serial plotter (Arduino IDE)
* `-D SR_DEBUG` : (debugging) Additional error diagnostics and debug info on serial USB.

View File

@@ -174,9 +174,9 @@ class BobLightUsermod : public Usermod {
#if WLED_DEBUG
DEBUG_PRINTLN(F("Fill light data: "));
DEBUG_PRINTF(" lights %d\n", numLights);
DEBUG_PRINTF_P(PSTR(" lights %d\n"), numLights);
for (int i=0; i<numLights; i++) {
DEBUG_PRINTF(" light %s scan %2.1f %2.1f %2.1f %2.1f\n", lights[i].lightname, lights[i].vscan[0], lights[i].vscan[1], lights[i].hscan[0], lights[i].hscan[1]);
DEBUG_PRINTF_P(PSTR(" light %s scan %2.1f %2.1f %2.1f %2.1f\n"), lights[i].lightname, lights[i].vscan[0], lights[i].vscan[1], lights[i].hscan[0], lights[i].hscan[1]);
}
#endif
}
@@ -187,11 +187,11 @@ class BobLightUsermod : public Usermod {
public:
void setup() {
void setup() override {
uint16_t totalLights = bottom + left + top + right;
if ( totalLights > strip.getLengthTotal() ) {
DEBUG_PRINTLN(F("BobLight: Too many lights."));
DEBUG_PRINTF("%d+%d+%d+%d>%d\n", bottom, left, top, right, strip.getLengthTotal());
DEBUG_PRINTF_P(PSTR("%d+%d+%d+%d>%d\n"), bottom, left, top, right, strip.getLengthTotal());
totalLights = strip.getLengthTotal();
top = bottom = (uint16_t) roundf((float)totalLights * 16.0f / 50.0f);
left = right = (uint16_t) roundf((float)totalLights * 9.0f / 50.0f);
@@ -202,14 +202,14 @@ class BobLightUsermod : public Usermod {
initDone = true;
}
void connected() {
void connected() override {
// we can only start server when WiFi is connected
if (!bob) bob = new WiFiServer(bobPort, 1);
bob->begin();
bob->setNoDelay(true);
}
void loop() {
void loop() override {
if (!enabled || strip.isUpdating()) return;
if (millis() - lastTime > 10) {
lastTime = millis();
@@ -225,7 +225,7 @@ class BobLightUsermod : public Usermod {
* 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) {
bool onMqttMessage(char* topic, char* payload) override {
//if (strlen(topic) == 6 && strncmp_P(topic, PSTR("/subtopic"), 6) == 0) {
// String action = payload;
// if (action == "on") {
@@ -242,7 +242,7 @@ class BobLightUsermod : public Usermod {
/**
* subscribe to MQTT topic for controlling usermod
*/
void onMqttConnect(bool sessionPresent) {
void onMqttConnect(bool sessionPresent) override {
//char subuf[64];
//if (mqttDeviceTopic[0] != 0) {
// strcpy(subuf, mqttDeviceTopic);
@@ -252,7 +252,7 @@ class BobLightUsermod : public Usermod {
}
#endif
void addToJsonInfo(JsonObject& root)
void addToJsonInfo(JsonObject& root) override
{
JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u");
@@ -273,7 +273,7 @@ class BobLightUsermod : public Usermod {
* 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)
void addToJsonState(JsonObject& root) override
{
}
@@ -281,7 +281,7 @@ class BobLightUsermod : public Usermod {
* 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) {
void readFromJsonState(JsonObject& root) override {
if (!initDone) return; // prevent crash on boot applyPreset()
bool en = enabled;
JsonObject um = root[FPSTR(_name)];
@@ -304,7 +304,7 @@ class BobLightUsermod : public Usermod {
}
}
void appendConfigData() {
void appendConfigData() override {
//oappend(SET_F("dd=addDropdown('usermod','selectfield');"));
//oappend(SET_F("addOption(dd,'1st value',0);"));
//oappend(SET_F("addOption(dd,'2nd value',1);"));
@@ -315,10 +315,10 @@ class BobLightUsermod : public Usermod {
oappend(SET_F("addInfo('BobLight:pct',1,'Depth of scan [%]');")); // 0 is field type, 1 is actual field
}
void addToConfig(JsonObject& root) {
void addToConfig(JsonObject& root) override {
JsonObject umData = root.createNestedObject(FPSTR(_name));
umData[FPSTR(_enabled)] = enabled;
umData[F("port")] = bobPort;
umData[ "port" ] = bobPort;
umData[F("top")] = top;
umData[F("bottom")] = bottom;
umData[F("left")] = left;
@@ -326,7 +326,7 @@ class BobLightUsermod : public Usermod {
umData[F("pct")] = pct;
}
bool readFromConfig(JsonObject& root) {
bool readFromConfig(JsonObject& root) override {
JsonObject umData = root[FPSTR(_name)];
bool configComplete = !umData.isNull();
@@ -334,7 +334,7 @@ class BobLightUsermod : public Usermod {
configComplete &= getJsonValue(umData[FPSTR(_enabled)], en);
enable(en);
configComplete &= getJsonValue(umData[F("port")], bobPort);
configComplete &= getJsonValue(umData[ "port" ], bobPort);
configComplete &= getJsonValue(umData[F("bottom")], bottom, 16);
configComplete &= getJsonValue(umData[F("top")], top, 16);
configComplete &= getJsonValue(umData[F("left")], left, 9);
@@ -355,11 +355,11 @@ class BobLightUsermod : public Usermod {
* 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() {
void handleOverlayDraw() override {
//strip.setPixelColor(0, RGBW32(0,0,0,0)) // set the first pixel to black
}
uint16_t getId() { return USERMOD_ID_BOBLIGHT; }
uint16_t getId() override { return USERMOD_ID_BOBLIGHT; }
};
@@ -392,7 +392,7 @@ void BobLightUsermod::pollBob() {
//get data from the client
while (bobClient.available()) {
String input = bobClient.readStringUntil('\n');
// DEBUG_PRINT("Client: "); DEBUG_PRINTLN(input); // may be to stressful on Serial
// DEBUG_PRINT(F("Client: ")); DEBUG_PRINTLN(input); // may be to stressful on Serial
if (input.startsWith(F("hello"))) {
DEBUG_PRINTLN(F("hello"));
bobClient.print(F("hello\n"));
@@ -445,7 +445,7 @@ void BobLightUsermod::pollBob() {
//strip.setPixelColor(light_id, RGBW32(red, green, blue, 0));
setRealtimePixel(light_id, red, green, blue, 0);
} // currently no support for interpolation or speed, we just ignore this
} else if (input.startsWith(F("sync"))) {
} else if (input.startsWith("sync")) {
BobSync();
} else {
// Client sent gibberish

View File

@@ -20,14 +20,11 @@ react to the globes orientation. See the blog post on building it <https://www.r
I2Cdev and MPU6050 must be installed.
To install them, add I2Cdevlib-MPU6050@fbde122cc5 to lib_deps in the platformio.ini file.
You also need to change lib_compat_mode from strict to soft in platformio.ini (This ignores that I2Cdevlib-MPU6050 doesn't list platform compatibility)
To install them, add electroniccats/MPU6050@1.0.1 to lib_deps in the platformio.ini file.
For example:
```
lib_compat_mode = soft
lib_deps =
FastLED@3.3.2
NeoPixelBus@2.5.7
@@ -36,7 +33,7 @@ lib_deps =
AsyncTCP@1.0.3
Esp Async WebServer@1.2.0
IRremoteESP8266@2.7.3
jrowberg/I2Cdevlib-MPU6050@^1.0.0
electroniccats/MPU6050@1.0.1
```
## Wiring

View File

@@ -0,0 +1,219 @@
#pragma once
/* This usermod uses gyro data to provide a "surge" effect on movement
Requires lib_deps = bolderflight/Bolder Flight Systems Eigen@^3.0.0
*/
#include "wled.h"
// Eigen include block
#ifdef A0
namespace { constexpr size_t A0_temp {A0}; }
#undef A0
static constexpr size_t A0 {A0_temp};
#endif
#ifdef A1
namespace { constexpr size_t A1_temp {A1}; }
#undef A1
static constexpr size_t A1 {A1_temp};
#endif
#ifdef B0
namespace { constexpr size_t B0_temp {B0}; }
#undef B0
static constexpr size_t B0 {B0_temp};
#endif
#ifdef B1
namespace { constexpr size_t B1_temp {B1}; }
#undef B1
static constexpr size_t B1 {B1_temp};
#endif
#ifdef D0
namespace { constexpr size_t D0_temp {D0}; }
#undef D0
static constexpr size_t D0 {D0_temp};
#endif
#ifdef D1
namespace { constexpr size_t D1_temp {D1}; }
#undef D1
static constexpr size_t D1 {D1_temp};
#endif
#ifdef D2
namespace { constexpr size_t D2_temp {D2}; }
#undef D2
static constexpr size_t D2 {D2_temp};
#endif
#ifdef D3
namespace { constexpr size_t D3_temp {D3}; }
#undef D3
static constexpr size_t D3 {D3_temp};
#endif
#include "eigen.h"
#include <Eigen/Geometry>
constexpr auto ESTIMATED_G = 9.801; // m/s^2
constexpr auto ESTIMATED_G_COUNTS = 8350.;
constexpr auto ESTIMATED_ANGULAR_RATE = (M_PI * 2000) / (INT16_MAX * 180); // radians per second
// Horribly lame digital filter code
// Currently implements a static IIR filter.
template<typename T, unsigned C>
class xir_filter {
typedef Eigen::Array<T, C, 1> array_t;
const array_t a_coeff, b_coeff;
const T gain;
array_t x, y;
public:
xir_filter(T gain_, array_t a, array_t b) : a_coeff(std::move(a)), b_coeff(std::move(b)), gain(gain_), x(array_t::Zero()), y(array_t::Zero()) {};
T operator()(T input) {
x.head(C-1) = x.tail(C-1); // shift by one
x(C-1) = input / gain;
y.head(C-1) = y.tail(C-1); // shift by one
y(C-1) = (x * b_coeff).sum();
y(C-1) -= (y.head(C-1) * a_coeff.head(C-1)).sum();
return y(C-1);
}
T last() { return y(C-1); };
};
class GyroSurge : public Usermod {
private:
static const char _name[];
bool enabled = true;
// Params
uint8_t max = 0;
float sensitivity = 0;
// State
uint32_t last_sample;
// 100hz input
// butterworth low pass filter at 20hz
xir_filter<float, 3> filter = { 1., { -0.36952738, 0.19581571, 1.}, {0.20657208, 0.41314417, 0.20657208} };
// { 1., { 0., 0., 1.}, { 0., 0., 1. } }; // no filter
public:
/*
* setup() is called once at boot. WiFi is not yet connected at this point.
*/
void setup() {};
/*
* 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(FPSTR(_name));
//save these vars persistently whenever settings are saved
top["max"] = max;
top["sensitivity"] = sensitivity;
}
/*
* readFromConfig() can be used to read back the custom settings you added with addToConfig().
* This is called by WLED when settings are loaded (currently this only happens immediately after boot, or after saving on the Usermod Settings page)
*
* readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes),
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
*
* Return true in case the config values returned from Usermod Settings were complete, or false if you'd like WLED to save your defaults to disk (so any missing values are editable in Usermod Settings)
*
* getJsonValue() returns false if the value is missing, or copies the value into the variable provided and returns true if the value is present
* The configComplete variable is true only if the "exampleUsermod" object and all values are present. If any values are missing, WLED will know to call addToConfig() to save them
*
* This function is guaranteed to be called on boot, but could also be called every time settings are updated
*/
bool readFromConfig(JsonObject& root)
{
// default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor
// setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed)
JsonObject top = root[FPSTR(_name)];
bool configComplete = !top.isNull();
configComplete &= getJsonValue(top["max"], max, 0);
configComplete &= getJsonValue(top["sensitivity"], sensitivity, 10);
return configComplete;
}
void loop() {
// get IMU data
um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_IMU)) {
// Apply max
strip.getSegment(0).fadeToBlackBy(max);
return;
}
uint32_t sample_count = *(uint32_t*)(um_data->u_data[8]);
if (sample_count != last_sample) {
last_sample = sample_count;
// Calculate based on new data
// We use the raw gyro data (angular rate)
auto gyros = (int16_t*)um_data->u_data[4]; // 16384 == 2000 deg/s
// Compute the overall rotation rate
// For my application (a plasma sword) we ignore X axis rotations (eg. around the long axis)
auto gyro_q = Eigen::AngleAxis<float> {
//Eigen::AngleAxis<float>(ESTIMATED_ANGULAR_RATE * gyros[0], Eigen::Vector3f::UnitX()) *
Eigen::AngleAxis<float>(ESTIMATED_ANGULAR_RATE * gyros[1], Eigen::Vector3f::UnitY()) *
Eigen::AngleAxis<float>(ESTIMATED_ANGULAR_RATE * gyros[2], Eigen::Vector3f::UnitZ()) };
// Filter the results
filter(std::min(sensitivity * gyro_q.angle(), 1.0f)); // radians per second
/*
Serial.printf("[%lu] Gy: %d, %d, %d -- ", millis(), (int)gyros[0], (int)gyros[1], (int)gyros[2]);
Serial.print(gyro_q.angle());
Serial.print(", ");
Serial.print(sensitivity * gyro_q.angle());
Serial.print(" --> ");
Serial.println(filter.last());
*/
}
}; // noop
/*
* 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()
{
// TODO: some kind of timing analysis for filtering ...
// Calculate brightness boost
auto r_float = std::max(std::min(filter.last(), 1.0f), 0.f);
auto result = (uint8_t) (r_float * max);
//Serial.printf("[%lu] %d -- ", millis(), result);
//Serial.println(r_float);
// TODO - multiple segment handling??
strip.getSegment(0).fadeToBlackBy(max - result);
}
};
const char GyroSurge::_name[] PROGMEM = "GyroSurge";

View File

@@ -20,11 +20,11 @@
XCL not connected
AD0 not connected
INT D8 (GPIO15) Interrupt pin
Using usermod:
1. Copy the usermod into the sketch folder (same folder as wled00.ino)
2. Register the usermod by adding #include "usermod_filename.h" in the top and registerUsermod(new MyUsermodClass()) in the bottom of usermods_list.cpp
3. I2Cdev and MPU6050 must be installed as libraries, or else the .cpp/.h file
3. I2Cdev and MPU6050 must be installed as libraries, or else the .cpp/.h file
for both classes must be in the include path of your project. To install the
libraries add I2Cdevlib-MPU6050@fbde122cc5 to lib_deps in the platformio.ini file.
4. You also need to change lib_compat_mode from strict to soft in platformio.ini (This ignores that I2Cdevlib-MPU6050 doesn't list platform compatibility)
@@ -33,6 +33,9 @@
#include "I2Cdev.h"
#undef DEBUG_PRINT
#undef DEBUG_PRINTLN
#undef DEBUG_PRINTF
#include "MPU6050_6Axis_MotionApps20.h"
//#include "MPU6050.h" // not necessary if using MotionApps include file
@@ -42,6 +45,23 @@
#include "Wire.h"
#endif
// Restore debug macros
// MPU6050 unfortunately uses the same macro names as WLED :(
#undef DEBUG_PRINT
#undef DEBUG_PRINTLN
#undef DEBUG_PRINTF
#ifdef WLED_DEBUG
#define DEBUG_PRINT(x) DEBUGOUT.print(x)
#define DEBUG_PRINTLN(x) DEBUGOUT.println(x)
#define DEBUG_PRINTF(x...) DEBUGOUT.printf(x)
#else
#define DEBUG_PRINT(x)
#define DEBUG_PRINTLN(x)
#define DEBUG_PRINTF(x...)
#endif
// ================================================================
// === INTERRUPT DETECTION ROUTINE ===
// ================================================================
@@ -52,21 +72,31 @@ void IRAM_ATTR dmpDataReady() {
}
class MPU6050Driver : public Usermod {
private:
MPU6050 mpu;
bool enabled = true;
// configuration state
// default values are set in readFromConfig
// By making this a struct, we enable easy backup and comparison in the readFromConfig class
struct config_t {
bool enabled;
int8_t interruptPin;
int16_t gyro_offset[3];
int16_t accel_offset[3];
};
config_t config;
// MPU control/status vars
bool irqBound = false; // set true if we have bound the IRQ pin
bool dmpReady = false; // set true if DMP init was successful
uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU
uint8_t devStatus; // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize; // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount; // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer
//NOTE: some of these can be removed to save memory, processing time
// if the measurement isn't needed
// TODO: some of these can be removed to save memory, processing time if the measurement isn't needed
Quaternion qat; // [w, x, y, z] quaternion container
float euler[3]; // [psi, theta, phi] Euler angle container
float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container
@@ -75,17 +105,67 @@ class MPU6050Driver : public Usermod {
VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements
VectorInt16 aaWorld; // [x, y, z] world-frame accel sensor measurements
VectorFloat gravity; // [x, y, z] gravity vector
uint32 sample_count;
static const int INTERRUPT_PIN = 15; // use pin 15 on ESP8266
// Usermod output
um_data_t um_data;
// config element names as progmem strs
static const char _name[];
static const char _enabled[];
static const char _interrupt_pin[];
static const char _x_acc_bias[];
static const char _y_acc_bias[];
static const char _z_acc_bias[];
static const char _x_gyro_bias[];
static const char _y_gyro_bias[];
static const char _z_gyro_bias[];
public:
//Functions called by WLED
inline bool initDone() { return um_data.u_size != 0; }; // recycle this instead of storing an extra variable
//Functions called by WLED
/*
* setup() is called once at boot. WiFi is not yet connected at this point.
*/
void setup() {
if (i2c_scl<0 || i2c_sda<0) { enabled = false; return; }
dmpReady = false; // Start clean
// one time init
if (!initDone()) {
um_data.u_size = 9;
um_data.u_type = new um_types_t[um_data.u_size];
um_data.u_data = new void*[um_data.u_size];
um_data.u_data[0] = &qat;
um_data.u_type[0] = UMT_FLOAT_ARR;
um_data.u_data[1] = &euler;
um_data.u_type[1] = UMT_FLOAT_ARR;
um_data.u_data[2] = &ypr;
um_data.u_type[2] = UMT_FLOAT_ARR;
um_data.u_data[3] = &aa;
um_data.u_type[3] = UMT_INT16_ARR;
um_data.u_data[4] = &gy;
um_data.u_type[4] = UMT_INT16_ARR;
um_data.u_data[5] = &aaReal;
um_data.u_type[5] = UMT_INT16_ARR;
um_data.u_data[6] = &aaWorld;
um_data.u_type[6] = UMT_INT16_ARR;
um_data.u_data[7] = &gravity;
um_data.u_type[7] = UMT_FLOAT_ARR;
um_data.u_data[8] = &sample_count;
um_data.u_type[8] = UMT_UINT32;
}
if (!config.enabled) return;
if (i2c_scl<0 || i2c_sda<0) { DEBUG_PRINTLN(F("MPU6050: I2C is no good.")); return; }
// Check the interrupt pin
if (config.interruptPin >= 0) {
irqBound = pinManager.allocatePin(config.interruptPin, false, PinOwner::UM_IMU);
if (!irqBound) { DEBUG_PRINTLN(F("MPU6050: IRQ pin already in use.")); return; }
pinMode(config.interruptPin, INPUT);
};
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.setClock(400000U); // 400kHz I2C clock. Comment this line if having compilation difficulties
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
@@ -95,7 +175,6 @@ class MPU6050Driver : public Usermod {
// initialize device
DEBUG_PRINTLN(F("Initializing I2C devices..."));
mpu.initialize();
pinMode(INTERRUPT_PIN, INPUT);
// verify connection
DEBUG_PRINTLN(F("Testing device connections..."));
@@ -105,11 +184,16 @@ class MPU6050Driver : public Usermod {
DEBUG_PRINTLN(F("Initializing DMP..."));
devStatus = mpu.dmpInitialize();
// supply your own gyro offsets here, scaled for min sensitivity
mpu.setXGyroOffset(220);
mpu.setYGyroOffset(76);
mpu.setZGyroOffset(-85);
mpu.setZAccelOffset(1788); // 1688 factory default for my test chip
// set offsets (from config)
mpu.setXGyroOffset(config.gyro_offset[0]);
mpu.setYGyroOffset(config.gyro_offset[1]);
mpu.setZGyroOffset(config.gyro_offset[2]);
mpu.setXAccelOffset(config.accel_offset[0]);
mpu.setYAccelOffset(config.accel_offset[1]);
mpu.setZAccelOffset(config.accel_offset[2]);
// set sample rate
mpu.setRate(16); // ~100Hz
// make sure it worked (returns 0 if so)
if (devStatus == 0) {
@@ -117,17 +201,19 @@ class MPU6050Driver : public Usermod {
DEBUG_PRINTLN(F("Enabling DMP..."));
mpu.setDMPEnabled(true);
// enable Arduino interrupt detection
DEBUG_PRINTLN(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus();
// set our DMP Ready flag so the main loop() function knows it's okay to use it
DEBUG_PRINTLN(F("DMP ready! Waiting for first interrupt..."));
dmpReady = true;
mpuInterrupt = true;
if (irqBound) {
// enable Arduino interrupt detection
DEBUG_PRINTLN(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
attachInterrupt(digitalPinToInterrupt(config.interruptPin), dmpDataReady, RISING);
}
// get expected DMP packet size for later comparison
packetSize = mpu.dmpGetFIFOPacketSize();
// set our DMP Ready flag so the main loop() function knows it's okay to use it
DEBUG_PRINTLN(F("DMP ready!"));
dmpReady = true;
} else {
// ERROR!
// 1 = initial memory load failed
@@ -137,6 +223,9 @@ class MPU6050Driver : public Usermod {
DEBUG_PRINT(devStatus);
DEBUG_PRINTLN(")");
}
fifoCount = 0;
sample_count = 0;
}
/*
@@ -144,7 +233,7 @@ class MPU6050Driver : public Usermod {
* Use it to initialize network interfaces
*/
void connected() {
//DEBUG_PRINTLN("Connected to WiFi!");
//DEBUG_PRINTLN(F("Connected to WiFi!"));
}
@@ -153,28 +242,31 @@ class MPU6050Driver : public Usermod {
*/
void loop() {
// if programming failed, don't try to do anything
if (!enabled || !dmpReady || strip.isUpdating()) return;
if (!config.enabled || !dmpReady || strip.isUpdating()) return;
// wait for MPU interrupt or extra packet(s) available
// mpuInterrupt is fixed on if interrupt pin is disabled
if (!mpuInterrupt && fifoCount < packetSize) return;
// reset interrupt flag and get INT_STATUS byte
mpuInterrupt = false;
mpuIntStatus = mpu.getIntStatus();
// get current FIFO count
auto mpuIntStatus = mpu.getIntStatus();
// Update current FIFO count
fifoCount = mpu.getFIFOCount();
// check for overflow (this should never happen unless our code is too inefficient)
if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
// reset so we can continue cleanly
mpu.resetFIFO();
DEBUG_PRINTLN(F("FIFO overflow!"));
DEBUG_PRINTLN(F("MPU6050: FIFO overflow!"));
// otherwise, check for DMP data ready interrupt (this should happen frequently)
} else if (mpuIntStatus & 0x02) {
// wait for correct available data length, should be a VERY short wait
while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
// otherwise, check for data ready
} else if (fifoCount >= packetSize) {
// clear local interrupt pending status, if not polling
mpuInterrupt = !irqBound;
// DEBUG_PRINT(F("MPU6050: Processing packet: "));
// DEBUG_PRINT(fifoCount);
// DEBUG_PRINTLN(F(" bytes in FIFO"));
// read a packet from FIFO
mpu.getFIFOBytes(fifoBuffer, packetSize);
@@ -183,7 +275,6 @@ class MPU6050Driver : public Usermod {
// (this lets us immediately read more without waiting for an interrupt)
fifoCount -= packetSize;
//NOTE: some of these can be removed to save memory, processing time
// if the measurement isn't needed
mpu.dmpGetQuaternion(&qat, fifoBuffer);
@@ -194,87 +285,141 @@ class MPU6050Driver : public Usermod {
mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &qat);
mpu.dmpGetYawPitchRoll(ypr, &qat, &gravity);
++sample_count;
}
}
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");
JsonObject imu_meas = user.createNestedObject("IMU");
JsonArray quat_json = imu_meas.createNestedArray("Quat");
// Unfortunately the web UI doesn't know how to print sub-objects: you just see '[object Object]'
// For now, we just put everything in the root userdata object.
//auto imu_meas = user.createNestedObject("IMU");
auto& imu_meas = user;
// If an element is an array, the UI expects two elements in the form [value, unit]
// Since our /value/ is an array, wrap it, eg. [[a, b, c]]
JsonArray quat_json = imu_meas.createNestedArray("Quat").createNestedArray();
quat_json.add(qat.w);
quat_json.add(qat.x);
quat_json.add(qat.y);
quat_json.add(qat.z);
JsonArray euler_json = imu_meas.createNestedArray("Euler");
JsonArray euler_json = imu_meas.createNestedArray("Euler").createNestedArray();
euler_json.add(euler[0]);
euler_json.add(euler[1]);
euler_json.add(euler[2]);
JsonArray accel_json = imu_meas.createNestedArray("Accel");
JsonArray accel_json = imu_meas.createNestedArray("Accel").createNestedArray();
accel_json.add(aa.x);
accel_json.add(aa.y);
accel_json.add(aa.z);
JsonArray gyro_json = imu_meas.createNestedArray("Gyro");
JsonArray gyro_json = imu_meas.createNestedArray("Gyro").createNestedArray();
gyro_json.add(gy.x);
gyro_json.add(gy.y);
gyro_json.add(gy.z);
JsonArray world_json = imu_meas.createNestedArray("WorldAccel");
JsonArray world_json = imu_meas.createNestedArray("WorldAccel").createNestedArray();
world_json.add(aaWorld.x);
world_json.add(aaWorld.y);
world_json.add(aaWorld.z);
JsonArray real_json = imu_meas.createNestedArray("RealAccel");
JsonArray real_json = imu_meas.createNestedArray("RealAccel").createNestedArray();
real_json.add(aaReal.x);
real_json.add(aaReal.y);
real_json.add(aaReal.z);
JsonArray grav_json = imu_meas.createNestedArray("Gravity");
JsonArray grav_json = imu_meas.createNestedArray("Gravity").createNestedArray();
grav_json.add(gravity.x);
grav_json.add(gravity.y);
grav_json.add(gravity.z);
JsonArray orient_json = imu_meas.createNestedArray("Orientation");
JsonArray orient_json = imu_meas.createNestedArray("Orientation").createNestedArray();
orient_json.add(ypr[0]);
orient_json.add(ypr[1]);
orient_json.add(ypr[2]);
}
/*
* 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)
//{
//if (root["bri"] == 255) DEBUG_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)
* 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("MPU6050_IMU");
// JsonArray pins = top.createNestedArray("pin");
// pins.add(HW_PIN_SCL);
// pins.add(HW_PIN_SDA);
// }
void addToConfig(JsonObject& root)
{
JsonObject top = root.createNestedObject(FPSTR(_name));
//save these vars persistently whenever settings are saved
top[FPSTR(_enabled)] = config.enabled;
top[FPSTR(_interrupt_pin)] = config.interruptPin;
top[FPSTR(_x_acc_bias)] = config.accel_offset[0];
top[FPSTR(_y_acc_bias)] = config.accel_offset[1];
top[FPSTR(_z_acc_bias)] = config.accel_offset[2];
top[FPSTR(_x_gyro_bias)] = config.gyro_offset[0];
top[FPSTR(_y_gyro_bias)] = config.gyro_offset[1];
top[FPSTR(_z_gyro_bias)] = config.gyro_offset[2];
}
/*
* readFromConfig() can be used to read back the custom settings you added with addToConfig().
* This is called by WLED when settings are loaded (currently this only happens immediately after boot, or after saving on the Usermod Settings page)
*
* readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes),
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
*
* Return true in case the config values returned from Usermod Settings were complete, or false if you'd like WLED to save your defaults to disk (so any missing values are editable in Usermod Settings)
*
* getJsonValue() returns false if the value is missing, or copies the value into the variable provided and returns true if the value is present
* The configComplete variable is true only if the "exampleUsermod" object and all values are present. If any values are missing, WLED will know to call addToConfig() to save them
*
* This function is guaranteed to be called on boot, but could also be called every time settings are updated
*/
bool readFromConfig(JsonObject& root)
{
// default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor
// setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed)
auto old_cfg = config;
JsonObject top = root[FPSTR(_name)];
bool configComplete = top.isNull();
// Ensure default configuration is loaded
configComplete &= getJsonValue(top[FPSTR(_enabled)], config.enabled, true);
configComplete &= getJsonValue(top[FPSTR(_interrupt_pin)], config.interruptPin, -1);
configComplete &= getJsonValue(top[FPSTR(_x_acc_bias)], config.accel_offset[0], 0);
configComplete &= getJsonValue(top[FPSTR(_y_acc_bias)], config.accel_offset[1], 0);
configComplete &= getJsonValue(top[FPSTR(_z_acc_bias)], config.accel_offset[2], 0);
configComplete &= getJsonValue(top[FPSTR(_x_gyro_bias)], config.gyro_offset[0], 0);
configComplete &= getJsonValue(top[FPSTR(_y_gyro_bias)], config.gyro_offset[1], 0);
configComplete &= getJsonValue(top[FPSTR(_z_gyro_bias)], config.gyro_offset[2], 0);
DEBUG_PRINT(FPSTR(_name));
if (top.isNull()) {
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
} else if (!initDone()) {
DEBUG_PRINTLN(F(": config loaded."));
} else if (memcmp(&config, &old_cfg, sizeof(config)) == 0) {
DEBUG_PRINTLN(F(": config unchanged."));
} else {
DEBUG_PRINTLN(F(": config updated."));
// Previously loaded and config changed
if (irqBound && ((old_cfg.interruptPin != config.interruptPin) || !config.enabled)) {
detachInterrupt(old_cfg.interruptPin);
pinManager.deallocatePin(old_cfg.interruptPin, PinOwner::UM_IMU);
irqBound = false;
}
// Just re-init
setup();
}
return configComplete;
}
bool getUMData(um_data_t **data)
{
if (!data || !config.enabled || !dmpReady) return false; // no pointer provided by caller or not enabled -> exit
*data = &um_data;
return true;
}
/*
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
@@ -285,3 +430,14 @@ class MPU6050Driver : public Usermod {
}
};
const char MPU6050Driver::_name[] PROGMEM = "MPU6050_IMU";
const char MPU6050Driver::_enabled[] PROGMEM = "enabled";
const char MPU6050Driver::_interrupt_pin[] PROGMEM = "interrupt_pin";
const char MPU6050Driver::_x_acc_bias[] PROGMEM = "x_acc_bias";
const char MPU6050Driver::_y_acc_bias[] PROGMEM = "y_acc_bias";
const char MPU6050Driver::_z_acc_bias[] PROGMEM = "z_acc_bias";
const char MPU6050Driver::_x_gyro_bias[] PROGMEM = "x_gyro_bias";
const char MPU6050Driver::_y_gyro_bias[] PROGMEM = "y_gyro_bias";
const char MPU6050Driver::_z_gyro_bias[] PROGMEM = "z_gyro_bias";

View File

@@ -50,5 +50,5 @@ This usermod listens on `[mqttDeviceTopic]/switch/0/set` (where 0 is replaced wi
Feedback about the current state is provided at `[mqttDeviceTopic]/switch/0/state`.
### Home Assistant auto-discovery
Auto-discovery information is automatically published and you shoudn't have to do anything to register the switches in Home Assistant.
Auto-discovery information is automatically published and you shouldn't have to do anything to register the switches in Home Assistant.

View File

@@ -2,7 +2,7 @@
This usermod-v2 modification allows the connection of multiple relays, each with individual delay and on/off mode.
Usermod supports PCF8574 I2C port expander to reduce GPIO use.
PCF8574 supports 8 outputs and each output corresponds to a relay in WLED (relay 0 = port 0, etc). I you are using more than 8 relays with multiple PCF8574 make sure their addresses are set conscutively (e.g. 0x20 and 0x21). You can set address of first expander in settings.
PCF8574 supports 8 outputs and each output corresponds to a relay in WLED (relay 0 = port 0, etc). I you are using more than 8 relays with multiple PCF8574 make sure their addresses are set in sequence (e.g. 0x20 and 0x21). You can set address of first expander in settings.
(**NOTE:** Will require Wire library and global I2C pins defined.)
## HTTP API
@@ -47,6 +47,24 @@ or
You can override the default maximum number of relays (which is 4) by defining MULTI_RELAY_MAX_RELAYS.
Some settings can be defined (defaults) at compile time by setting the following defines:
```cpp
// enable or disable HA discovery for externally controlled relays
#define MULTI_RELAY_HA_DISCOVERY true
```
The following definitions should be a list of values (maximum number of entries is MULTI_RELAY_MAX_RELAYS) that will be applied to the relays in order:
(e.g. assuming MULTI_RELAY_MAX_RELAYS=2)
```cpp
#define MULTI_RELAY_PINS 12,18
#define MULTI_RELAY_DELAYS 0,0
#define MULTI_RELAY_EXTERNALS false,true
#define MULTI_RELAY_INVERTS false,false
```
These can be set via your `platformio_override.ini` file or as `#define` in your `my_config.h` (remember to set `WLED_USE_MY_CONFIG` in your `platformio_override.ini`)
Example **usermods_list.cpp**:
```cpp
@@ -107,4 +125,7 @@ Have fun - @blazoncek
* Added button support.
2023-05
* Added support for PCF8574 I2C port expander (multiple)
* Added support for PCF8574 I2C port expander (multiple)
2023-11
* @chrisburrows Added support for compile time defaults for setting DELAY, EXTERNAL, INVERTS and HA discovery

View File

@@ -2,6 +2,8 @@
#include "wled.h"
#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
#ifndef MULTI_RELAY_MAX_RELAYS
#define MULTI_RELAY_MAX_RELAYS 4
#else
@@ -19,6 +21,22 @@
#define MULTI_RELAY_ENABLED true
#endif
#ifndef MULTI_RELAY_HA_DISCOVERY
#define MULTI_RELAY_HA_DISCOVERY false
#endif
#ifndef MULTI_RELAY_DELAYS
#define MULTI_RELAY_DELAYS 0
#endif
#ifndef MULTI_RELAY_EXTERNALS
#define MULTI_RELAY_EXTERNALS false
#endif
#ifndef MULTI_RELAY_INVERTS
#define MULTI_RELAY_INVERTS false
#endif
#define WLED_DEBOUNCE_THRESHOLD 50 //only consider button input of at least 50ms as valid (debouncing)
#define ON true
@@ -86,6 +104,9 @@ class MultiRelay : public Usermod {
static const char _HAautodiscovery[];
static const char _pcf8574[];
static const char _pcfAddress[];
static const char _switch[];
static const char _toggle[];
static const char _Command[];
void handleOffTimer();
void InitHtmlAPIHandle();
@@ -125,7 +146,7 @@ class MultiRelay : public Usermod {
* 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.
*/
inline uint16_t getId() { return USERMOD_ID_MULTI_RELAY; }
inline uint16_t getId() override { return USERMOD_ID_MULTI_RELAY; }
/**
* switch relay on/off
@@ -143,22 +164,22 @@ class MultiRelay : public Usermod {
* 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();
void setup() override;
/**
* connected() is called every time the WiFi is (re)connected
* Use it to initialize network interfaces
*/
inline void connected() { InitHtmlAPIHandle(); }
inline void connected() override { InitHtmlAPIHandle(); }
/**
* loop() is called continuously. Here you can check for events, read sensors, etc.
*/
void loop();
void loop() override;
#ifndef WLED_DISABLE_MQTT
bool onMqttMessage(char* topic, char* payload);
void onMqttConnect(bool sessionPresent);
bool onMqttMessage(char* topic, char* payload) override;
void onMqttConnect(bool sessionPresent) override;
#endif
/**
@@ -166,31 +187,31 @@ class MultiRelay : public Usermod {
* will prevent button working in a default way.
* Replicating button.cpp
*/
bool handleButton(uint8_t b);
bool handleButton(uint8_t b) override;
/**
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
*/
void addToJsonInfo(JsonObject &root);
void addToJsonInfo(JsonObject &root) override;
/**
* 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);
void addToJsonState(JsonObject &root) override;
/**
* 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);
void readFromJsonState(JsonObject &root) override;
/**
* provide the changeable values
*/
void addToConfig(JsonObject &root);
void addToConfig(JsonObject &root) override;
void appendConfigData();
void appendConfigData() override;
/**
* restore the changeable values
@@ -198,11 +219,11 @@ class MultiRelay : public Usermod {
*
* The function should return true if configuration was successfully loaded or false if there was no configuration.
*/
bool readFromConfig(JsonObject &root);
bool readFromConfig(JsonObject &root) override;
};
// class implementetion
// class implementation
void MultiRelay::publishMqtt(int relay) {
#ifndef WLED_DISABLE_MQTT
@@ -243,8 +264,8 @@ void MultiRelay::handleOffTimer() {
void MultiRelay::InitHtmlAPIHandle() { // https://github.com/me-no-dev/ESPAsyncWebServer
DEBUG_PRINTLN(F("Relays: Initialize HTML API"));
server.on("/relays", HTTP_GET, [this](AsyncWebServerRequest *request) {
DEBUG_PRINTLN("Relays: HTML API");
server.on(SET_F("/relays"), HTTP_GET, [this](AsyncWebServerRequest *request) {
DEBUG_PRINTLN(F("Relays: HTML API"));
String janswer;
String error = "";
//int params = request->params();
@@ -253,9 +274,9 @@ void MultiRelay::InitHtmlAPIHandle() { // https://github.com/me-no-dev/ESPAsync
if (getActiveRelayCount()) {
// Commands
if(request->hasParam("switch")) {
if (request->hasParam(FPSTR(_switch))) {
/**** Switch ****/
AsyncWebParameter* p = request->getParam("switch");
AsyncWebParameter* p = request->getParam(FPSTR(_switch));
// Get Values
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
int value = getValue(p->value(), ',', i);
@@ -266,9 +287,9 @@ void MultiRelay::InitHtmlAPIHandle() { // https://github.com/me-no-dev/ESPAsync
if (_relay[i].external) switchRelay(i, (bool)value);
}
}
} else if(request->hasParam("toggle")) {
} else if (request->hasParam(FPSTR(_toggle))) {
/**** Toggle ****/
AsyncWebParameter* p = request->getParam("toggle");
AsyncWebParameter* p = request->getParam(FPSTR(_toggle));
// Get Values
for (int i=0;i<MULTI_RELAY_MAX_RELAYS;i++) {
int value = getValue(p->value(), ',', i);
@@ -296,7 +317,7 @@ void MultiRelay::InitHtmlAPIHandle() { // https://github.com/me-no-dev/ESPAsync
janswer += error;
janswer += F("\",");
janswer += F("\"SW Version\":\"");
janswer += String(GEOGABVERSION);
janswer += String(F(GEOGABVERSION));
janswer += F("\"}");
request->send(200, "application/json", janswer);
});
@@ -343,18 +364,22 @@ MultiRelay::MultiRelay()
, initDone(false)
, usePcf8574(USE_PCF8574)
, addrPcf8574(PCF8574_ADDRESS)
, HAautodiscovery(false)
, HAautodiscovery(MULTI_RELAY_HA_DISCOVERY)
, periodicBroadcastSec(60)
, lastBroadcast(0)
{
const int8_t defPins[] = {MULTI_RELAY_PINS};
const int8_t relayDelays[] = {MULTI_RELAY_DELAYS};
const bool relayExternals[] = {MULTI_RELAY_EXTERNALS};
const bool relayInverts[] = {MULTI_RELAY_INVERTS};
for (size_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
_relay[i].pin = i<sizeof(defPins) ? defPins[i] : -1;
_relay[i].delay = 0;
_relay[i].invert = false;
_relay[i].pin = i < COUNT_OF(defPins) ? defPins[i] : -1;
_relay[i].delay = i < COUNT_OF(relayDelays) ? relayDelays[i] : 0;
_relay[i].invert = i < COUNT_OF(relayInverts) ? relayInverts[i] : false;
_relay[i].active = false;
_relay[i].state = false;
_relay[i].external = false;
_relay[i].external = i < COUNT_OF(relayExternals) ? relayExternals[i] : false;
_relay[i].button = -1;
}
}
@@ -366,7 +391,7 @@ void MultiRelay::switchRelay(uint8_t relay, bool mode) {
if (relay>=MULTI_RELAY_MAX_RELAYS || _relay[relay].pin<0) return;
_relay[relay].state = mode;
if (usePcf8574 && _relay[relay].pin >= 100) {
// we need to send all ouputs at the same time
// we need to send all outputs at the same time
uint8_t state = 0;
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
if (_relay[i].pin < 100) continue;
@@ -398,7 +423,7 @@ uint8_t MultiRelay::getActiveRelayCount() {
* topic should look like: /relay/X/command; where X is relay number, 0 based
*/
bool MultiRelay::onMqttMessage(char* topic, char* payload) {
if (strlen(topic) > 8 && strncmp_P(topic, PSTR("/relay/"), 7) == 0 && strncmp_P(topic+8, PSTR("/command"), 8) == 0) {
if (strlen(topic) > 8 && strncmp_P(topic, PSTR("/relay/"), 7) == 0 && strncmp_P(topic+8, _Command, 8) == 0) {
uint8_t relay = strtoul(topic+7, NULL, 10);
if (relay<MULTI_RELAY_MAX_RELAYS) {
String action = payload;
@@ -408,7 +433,7 @@ bool MultiRelay::onMqttMessage(char* topic, char* payload) {
} else if (action == "off") {
if (_relay[relay].external) switchRelay(relay, false);
return true;
} else if (action == "toggle") {
} else if (action == FPSTR(_toggle)) {
if (_relay[relay].external) toggleRelay(relay);
return true;
}
@@ -448,7 +473,7 @@ void MultiRelay::publishHomeAssistantAutodiscovery() {
sprintf_P(buf, PSTR("%s/relay/%d"), mqttDeviceTopic, i); //max length: 33 + 7 + 3 = 43
json["~"] = buf;
strcat_P(buf, PSTR("/command"));
strcat_P(buf, _Command);
mqtt->subscribe(buf, 0);
json[F("stat_t")] = "~";
@@ -512,10 +537,10 @@ void MultiRelay::setup() {
* loop() is called continuously. Here you can check for events, read sensors, etc.
*/
void MultiRelay::loop() {
yield();
if (!enabled || strip.isUpdating()) return;
static unsigned long lastUpdate = 0;
yield();
if (!enabled || (strip.isUpdating() && millis() - lastUpdate < 100)) return;
if (millis() - lastUpdate < 100) return; // update only 10 times/s
lastUpdate = millis();
@@ -653,8 +678,8 @@ void MultiRelay::addToJsonInfo(JsonObject &root) {
uiDomString += F(",on:");
uiDomString += _relay[i].state ? "false" : "true";
uiDomString += F("}});\">");
uiDomString += F("<i class=\"icons");
uiDomString += _relay[i].state ? F(" on") : F(" off");
uiDomString += F("<i class=\"icons ");
uiDomString += _relay[i].state ? "on" : "off";
uiDomString += F("\">&#xe08f;</i></button>");
infoArr.add(uiDomString);
}
@@ -677,11 +702,11 @@ void MultiRelay::addToJsonState(JsonObject &root) {
if (_relay[i].pin < 0) continue;
JsonObject relay = rel_arr.createNestedObject();
relay[FPSTR(_relay_str)] = i;
relay[F("state")] = _relay[i].state;
relay["state"] = _relay[i].state;
}
#else
multiRelay[FPSTR(_relay_str)] = 0;
multiRelay[F("state")] = _relay[0].state;
multiRelay["state"] = _relay[0].state;
#endif
}
@@ -781,13 +806,6 @@ bool MultiRelay::readFromConfig(JsonObject &root) {
_relay[i].external = top[parName][FPSTR(_external)] | _relay[i].external;
_relay[i].delay = top[parName][FPSTR(_delay_str)] | _relay[i].delay;
_relay[i].button = top[parName][FPSTR(_button)] | _relay[i].button;
// begin backwards compatibility (beta) remove when 0.13 is released
parName += '-';
_relay[i].pin = top[parName+"pin"] | _relay[i].pin;
_relay[i].invert = top[parName+FPSTR(_activeHigh)] | _relay[i].invert;
_relay[i].external = top[parName+FPSTR(_external)] | _relay[i].external;
_relay[i].delay = top[parName+FPSTR(_delay_str)] | _relay[i].delay;
// end compatibility
_relay[i].delay = min(600,max(0,abs((int)_relay[i].delay))); // bounds checking max 10min
}
@@ -821,3 +839,6 @@ const char MultiRelay::_broadcast[] PROGMEM = "broadcast-sec";
const char MultiRelay::_HAautodiscovery[] PROGMEM = "HA-autodiscovery";
const char MultiRelay::_pcf8574[] PROGMEM = "use-PCF8574";
const char MultiRelay::_pcfAddress[] PROGMEM = "PCF8574-address";
const char MultiRelay::_switch[] PROGMEM = "switch";
const char MultiRelay::_toggle[] PROGMEM = "toggle";
const char MultiRelay::_Command[] PROGMEM = "/command";

View File

@@ -84,11 +84,11 @@ class QuinLEDAnPentaUsermod : public Usermod
void getCurrentUsedLedPins()
{
for (int8_t lp = 0; lp <= 4; lp++) currentLedPins[lp] = 0;
byte numBusses = busses.getNumBusses();
byte numBusses = BusManager::getNumBusses();
byte numUsedPins = 0;
for (int8_t b = 0; b < numBusses; b++) {
Bus* curBus = busses.getBus(b);
Bus* curBus = BusManager::getBus(b);
if (curBus != nullptr) {
uint8_t pins[5] = {0, 0, 0, 0, 0};
currentBussesNumPins[b] = curBus->getPins(pins);
@@ -104,11 +104,11 @@ class QuinLEDAnPentaUsermod : public Usermod
void getCurrentLedcValues()
{
byte numBusses = busses.getNumBusses();
byte numBusses = BusManager::getNumBusses();
byte numLedc = 0;
for (int8_t b = 0; b < numBusses; b++) {
Bus* curBus = busses.getBus(b);
Bus* curBus = BusManager::getBus(b);
if (curBus != nullptr) {
uint32_t curPixColor = curBus->getPixelColor(0);
uint8_t _data[5] = {255, 255, 255, 255, 255};

View File

@@ -2,7 +2,7 @@
The (un)official usermod to get the best out of the QuinLED-An-Penta (https://quinled.info/quinled-an-penta/), e.g. using the OLED and the SHT30 temperature/humidity sensor.
## Requirements
* "u8gs" by olikraus, v2.28 or higher: https://github.com/olikraus/u8g2
* "u8g2" by olikraus, v2.28 or higher: https://github.com/olikraus/u8g2
* "SHT85" by Rob Tillaart, v0.2 or higher: https://github.com/RobTillaart/SHT85
## Usermod installation

View File

@@ -20,7 +20,7 @@
| `pinSourceSelect` | GPIO that is connected to SD's `SS`(source select) / `CS`(chip select) | 16 |
| `pinSourceClock` | GPIO that is connected to SD's `SCLK` (source clock) / `CLK`(clock) | 14 |
| `pinPoci` | GPIO that is connected to SD's `POCI`<sup></sup> (Peripheral-Out-Ctrl-In) / `MISO` (deprecated) | 36 |
| `pinPico` | GPIO that is connected to SD's `PICO`<sup></sup> (Peripheral-In-Ctrl-Out) / `MOSI` (deprecated) | 14 |
| `pinPico` | GPIO that is connected to SD's `PICO`<sup></sup> (Peripheral-In-Ctrl-Out) / `MOSI` (deprecated) | 15 |
| `sdEnable` | Enable to read data from the SD-card | true |
<sup></sup><sub>Following new naming convention of [OSHWA](https://www.oshwa.org/a-resolution-to-redefine-spi-signal-names/)</sub>
@@ -31,4 +31,4 @@
- checks if the specified file is available on the SD card
```cpp
bool file_onSD(const char *filepath) {...}
```
```

View File

@@ -85,8 +85,8 @@ private:
JsonObject device = doc.createNestedObject("device"); // attach the sensor to the same device
device["identifiers"] = String("wled-sensor-") + mqttClientID;
device["manufacturer"] = "Aircoookie";
device["model"] = "WLED";
device["manufacturer"] = F(WLED_BRAND);
device["model"] = F(WLED_PRODUCT_NAME);
device["sw_version"] = VERSION;
device["name"] = mqttClientID;

View File

@@ -17,7 +17,7 @@ The number of individual LEDs per segment. 7 segments per digit.
#### perPeriod -- ssLEDPerPeriod
The number of individual LEDs per period. A ':' (colon) has two periods.
#### startIdx -- ssStartLED
Index of the LED the display starts at. Enabless a seven segment display to be in the middle of a string.
Index of the LED the display starts at. Enables a seven segment display to be in the middle of a string.
#### timeEnable -- ssTimeEnabled
When true, when displayMask is configured for a time output and no message is set, the time will be displayed.
#### scrollSpd -- ssScrollSpeed

View File

@@ -409,7 +409,7 @@ public:
if (mqttGroupTopic[0] != 0)
{
//subcribe for sevenseg messages on the group topic
//subscribe for sevenseg messages on the group topic
sprintf_P(subBuffer, PSTR("%s/%S/+/set"), mqttGroupTopic, _str_sevenSeg);
mqtt->subscribe(subBuffer, 2);
}
@@ -417,7 +417,7 @@ public:
bool onMqttMessage(char *topic, char *payload)
{
//If topic beings iwth sevenSeg cut it off, otherwise not our message.
//If topic beings with sevenSeg cut it off, otherwise not our message.
size_t topicPrefixLen = strlen_P(PSTR("/sevenSeg/"));
if (strncmp_P(topic, PSTR("/sevenSeg/"), topicPrefixLen) == 0)
topic += topicPrefixLen;

View File

@@ -24,6 +24,9 @@ Enables the inverted mode in which the background should be enabled and the digi
### Colon-blinking
Enables the blinking colon(s) if they are defined
### Leading-Zero
Shows the leading zero of the hour if it exists (i.e. shows `07` instead of `7`)
### enable-auto-brightness
Enables the auto brightness feature. Can be used only when the usermod SN_Photoresistor is installed.

View File

@@ -17,6 +17,7 @@ private:
bool umSSDRDisplayTime = false;
bool umSSDRInverted = false;
bool umSSDRColonblink = true;
bool umSSDRLeadingZero = false;
bool umSSDREnableLDR = false;
String umSSDRHours = "";
String umSSDRMinutes = "";
@@ -79,6 +80,7 @@ private:
static const char _str_timeEnabled[];
static const char _str_inverted[];
static const char _str_colonblink[];
static const char _str_leadingZero[];
static const char _str_displayMask[];
static const char _str_hours[];
static const char _str_minutes[];
@@ -105,15 +107,15 @@ private:
switch (umSSDRDisplayMask[index]) {
case 'h':
timeVar = hourFormat12(localTime);
_showElements(&umSSDRHours, timeVar, 0, 1);
_showElements(&umSSDRHours, timeVar, 0, !umSSDRLeadingZero);
break;
case 'H':
timeVar = hour(localTime);
_showElements(&umSSDRHours, timeVar, 0, 1);
_showElements(&umSSDRHours, timeVar, 0, !umSSDRLeadingZero);
break;
case 'k':
timeVar = hour(localTime) + 1;
_showElements(&umSSDRHours, timeVar, 0, 0);
_showElements(&umSSDRHours, timeVar, 0, !umSSDRLeadingZero);
break;
case 'm':
timeVar = minute(localTime);
@@ -309,6 +311,9 @@ private:
if (_cmpIntSetting_P(topic, payload, _str_colonblink, &umSSDRColonblink)) {
return true;
}
if (_cmpIntSetting_P(topic, payload, _str_leadingZero, &umSSDRLeadingZero)) {
return true;
}
if (strcmp_P(topic, _str_displayMask) == 0) {
umSSDRDisplayMask = String(payload);
_publishMQTTstr_P(_str_displayMask, umSSDRDisplayMask);
@@ -323,6 +328,7 @@ private:
_publishMQTTint_P(_str_ldrEnabled, umSSDREnableLDR);
_publishMQTTint_P(_str_inverted, umSSDRInverted);
_publishMQTTint_P(_str_colonblink, umSSDRColonblink);
_publishMQTTint_P(_str_leadingZero, umSSDRLeadingZero);
_publishMQTTstr_P(_str_hours, umSSDRHours);
_publishMQTTstr_P(_str_minutes, umSSDRMinutes);
@@ -347,6 +353,7 @@ private:
ssdrObj[FPSTR(_str_ldrEnabled)] = umSSDREnableLDR;
ssdrObj[FPSTR(_str_inverted)] = umSSDRInverted;
ssdrObj[FPSTR(_str_colonblink)] = umSSDRColonblink;
ssdrObj[FPSTR(_str_leadingZero)] = umSSDRLeadingZero;
ssdrObj[FPSTR(_str_displayMask)] = umSSDRDisplayMask;
ssdrObj[FPSTR(_str_hours)] = umSSDRHours;
ssdrObj[FPSTR(_str_minutes)] = umSSDRMinutes;
@@ -425,6 +432,8 @@ public:
invert.add(umSSDRInverted);
JsonArray blink = user.createNestedArray("Blinking colon");
blink.add(umSSDRColonblink);
JsonArray zero = user.createNestedArray("Show the hour leading zero");
zero.add(umSSDRLeadingZero);
JsonArray ldrEnable = user.createNestedArray("Auto Brightness enabled");
ldrEnable.add(umSSDREnableLDR);
@@ -454,6 +463,7 @@ public:
umSSDREnableLDR = ssdrObj[FPSTR(_str_ldrEnabled)] | umSSDREnableLDR;
umSSDRInverted = ssdrObj[FPSTR(_str_inverted)] | umSSDRInverted;
umSSDRColonblink = ssdrObj[FPSTR(_str_colonblink)] | umSSDRColonblink;
umSSDRLeadingZero = ssdrObj[FPSTR(_str_leadingZero)] | umSSDRLeadingZero;
umSSDRDisplayMask = ssdrObj[FPSTR(_str_displayMask)] | umSSDRDisplayMask;
}
}
@@ -470,14 +480,14 @@ public:
if (mqttGroupTopic[0] != 0)
{
//subcribe for sevenseg messages on the group topic
//subscribe for sevenseg messages on the group topic
sprintf_P(subBuffer, PSTR("%s/%S/+/set"), mqttGroupTopic, _str_name);
mqtt->subscribe(subBuffer, 2);
}
}
bool onMqttMessage(char *topic, char *payload) {
//If topic beings iwth sevenSeg cut it off, otherwise not our message.
//If topic begins with sevenSeg cut it off, otherwise not our message.
size_t topicPrefixLen = strlen_P(PSTR("/wledSS/"));
if (strncmp_P(topic, PSTR("/wledSS/"), topicPrefixLen) == 0) {
topic += topicPrefixLen;
@@ -516,6 +526,7 @@ public:
umSSDREnableLDR = (top[FPSTR(_str_ldrEnabled)] | umSSDREnableLDR);
umSSDRInverted = (top[FPSTR(_str_inverted)] | umSSDRInverted);
umSSDRColonblink = (top[FPSTR(_str_colonblink)] | umSSDRColonblink);
umSSDRLeadingZero = (top[FPSTR(_str_leadingZero)] | umSSDRLeadingZero);
umSSDRDisplayMask = top[FPSTR(_str_displayMask)] | umSSDRDisplayMask;
umSSDRHours = top[FPSTR(_str_hours)] | umSSDRHours;
@@ -546,6 +557,7 @@ const char UsermodSSDR::_str_name[] PROGMEM = "UsermodSSDR";
const char UsermodSSDR::_str_timeEnabled[] PROGMEM = "enabled";
const char UsermodSSDR::_str_inverted[] PROGMEM = "inverted";
const char UsermodSSDR::_str_colonblink[] PROGMEM = "Colon-blinking";
const char UsermodSSDR::_str_leadingZero[] PROGMEM = "Leading-Zero";
const char UsermodSSDR::_str_displayMask[] PROGMEM = "Display-Mask";
const char UsermodSSDR::_str_hours[] PROGMEM = "LED-Numbers-Hours";
const char UsermodSSDR::_str_minutes[] PROGMEM = "LED-Numbers-Minutes";

View File

@@ -290,7 +290,7 @@ void ShtUsermod::loop()
/**
* Whenever MQTT is connected, publish HA autodiscovery topics.
*
* Is only donce once.
* Is only done once.
*
* @see Usermod::onMqttConnect()
* @see UsermodManager::onMqttConnect()

View File

@@ -1,5 +1,13 @@
### Stairway lighting
# Stairway lighting
## Install
Add the buildflag `-D USERMOD_STAIRCASE_WIPE` to your enviroment to activate it.
### Configuration
`-D STAIRCASE_WIPE_OFF`
<br>Have the LEDs wipe off instead of fading out
## Description
Quick usermod to accomplish something similar to [this video](https://www.youtube.com/watch?v=NHkju5ncC4A).
This usermod enables you to add a lightstrip alongside or on the steps of a staircase.

View File

@@ -19,10 +19,12 @@ class StairwayWipeUsermod : public Usermod {
unsigned long timeStaticStart = 0;
uint16_t previousUserVar0 = 0;
//moved to buildflag
//comment this out if you want the turn off effect to be just fading out instead of reverse wipe
#define STAIRCASE_WIPE_OFF
//#define STAIRCASE_WIPE_OFF
public:
void setup() {
}
void loop() {
//userVar0 (U0 in HTTP API):
//has to be set to 1 if movement is detected on the PIR that is the same side of the staircase as the ESP8266
@@ -84,14 +86,15 @@ class StairwayWipeUsermod : public Usermod {
uint16_t getId()
{
return USERMOD_ID_EXAMPLE;
return USERMOD_ID_STAIRWAY_WIPE;
}
void startWipe()
{
bri = briLast; //turn on
transitionDelayTemp = 0; //no transition
jsonTransitionOnce = true;
strip.setTransition(0); //no transition
effectCurrent = FX_MODE_COLOR_WIPE;
resetTimebase(); //make sure wipe starts from beginning
@@ -105,10 +108,11 @@ class StairwayWipeUsermod : public Usermod {
void turnOff()
{
jsonTransitionOnce = true;
#ifdef STAIRCASE_WIPE_OFF
transitionDelayTemp = 0; //turn off immediately after wipe completed
strip.setTransition(0); //turn off immediately after wipe completed
#else
transitionDelayTemp = 4000; //fade out slowly
strip.setTransition(4000); //fade out slowly
#endif
bri = 0;
stateUpdated(CALL_MODE_NOTIFICATION);

View File

@@ -23,7 +23,7 @@ private:
unsigned char Enc_B;
unsigned char Enc_A_prev = 0;
// private class memebers configurable by Usermod Settings (defaults set inside readFromConfig())
// private class members configurable by Usermod Settings (defaults set inside readFromConfig())
int8_t pins[3]; // pins[0] = DT from encoder, pins[1] = CLK from encoder, pins[2] = CLK from encoder (optional)
int fadeAmount; // how many points to fade the Neopixel with each step
@@ -162,7 +162,7 @@ public:
* - configComplete is used to return false if any value is missing, not just if the main object is missing
* - The defaults are loaded every time readFromConfig() is run, not just once after boot
*
* This ensures that missing values are added to the config, with their default values, in the rare but plauible cases of:
* This ensures that missing values are added to the config, with their default values, in the rare but plausible cases of:
* - a single value being missing at boot, e.g. if the Usermod was upgraded and a new setting was added
* - a single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed)
*

View File

@@ -0,0 +1,110 @@
# usermod_v2_HttpPullLightControl
The `usermod_v2_HttpPullLightControl` is a custom user module for WLED that enables remote control over the lighting state and color through HTTP requests. It periodically polls a specified URL to obtain a JSON response containing instructions for controlling individual lights.
## Features
* Configure the URL endpoint (only support HTTP for now, no HTTPS) and polling interval via the WLED user interface.
* All options from the JSON API are supported (since v0.0.3). See: https://kno.wled.ge/interfaces/json-api/
* The ability to control the brightness of all lights and the state (on/off) and color of individual lights remotely.
* Start or stop an effect and when you run the same effect when its's already running, it won't restart.
* The ability to control all these settings per segment.
* Remotely turn on/off relays, change segments or presets.
* Unique ID generation based on the device's MAC address and a configurable salt value, appended to the request URL for identification.
## Configuration
* Enable the `usermod_v2_HttpPullLightControl` via the WLED user interface.
* Specify the URL endpoint and polling interval.
## JSON Format and examples
* The module sends a GET request to the configured URL, appending a unique identifier as a query parameter: `https://www.example.com/mycustompage.php?id=xxxxxxxx` where xxxxxxx is a 40 character long SHA1 hash of the MAC address combined with a given salt.
* Response Format (since v0.0.3) it is eactly the same as the WLED JSON API, see: https://kno.wled.ge/interfaces/json-api/
After getting the URL (it can be a static file like static.json or a mylogic.php which gives a dynamic response), the response is read and parsed to WLED.
* An example of a response to set the individual lights: 0 to RED, 12 to Green and 14 to BLUE. Remember that is will SET lights, you might want to set all the others to black.
`{
"seg":
{
"i": [
0, "FF0000",
12, "00FF00",
14, "0000FF"
]
}
}`
* Another example setting the first 10 LEDs to RED, LED 40 to a PURPLE (using RGB values) and all LEDs in between OFF (black color)
`{
"seg":
{
"i": [
0,10, "FF0000",
10,40, "00FF00",
40, [0,100,100]
]
}
}`
* Or first set all lights to black (off), then the LED5 to color RED:
`{
"seg":
{
"i": [
0,40, "000000",
5, "FF0000"
]
}
}`
* Or use the following example to start an effect, but first we UNFREEZE (frz=false) the segment because it was frozen by individual light control in the previous examples (28=Chase effect, Speed=180m Intensity=128). The three color slots are the slots you see under the color wheel and used by the effect. RED, Black, White in this case.
`{
"seg":
{
"frz": false,
"fx": 28,
"sx": 200,
"ix": 128,
"col": [
"FF0000",
"000000",
"FFFFFF"
]
}
}`
## Installation
1. Add `usermod_v2_HttpPullLightControl` to your WLED project following the instructions provided in the WLED documentation.
2. Compile by setting the build_flag: -D USERMOD_HTTP_PULL_LIGHT_CONTROL and upload to your ESP32/ESP8266!
3. There are several compile options which you can put in your platformio.ini or platformio_override.ini:
- -DUSERMOD_HTTP_PULL_LIGHT_CONTROL ;To Enable the usermod
- -DHTTP_PULL_LIGHT_CONTROL_URL="\"http://mydomain.com/json-response.php\"" ; The URL which will be requested all the time to set the lights/effects
- -DHTTP_PULL_LIGHT_CONTROL_SALT="\"my_very-S3cret_C0de\"" ; A secret SALT which will help by making the ID more safe
- -DHTTP_PULL_LIGHT_CONTROL_INTERVAL=30 ; The interval at which the URL is requested in seconds
- -DHTTP_PULL_LIGHT_CONTROL_HIDE_SALT ; Do you want to Hide the SALT in the User Interface? If yes, Set this flag. Note that the salt can now only be set via the above -DHTTP_PULL_LIGHT_CONTROL_SALT= setting
- -DWLED_AP_SSID="\"Christmas Card\"" ; These flags are not just for my Usermod but you probably want to set them
- -DWLED_AP_PASS="\"christmas\""
- -DWLED_OTA_PASS="\"otapw-secret\""
- -DMDNS_NAME="\"christmascard\""
- -DSERVERNAME="\"CHRISTMASCARD\""
- -D ABL_MILLIAMPS_DEFAULT=450
- -D DEFAULT_LED_COUNT=60 ; For a LED Ring of 60 LEDs
- -D BTNPIN=41 ; The M5Stack Atom S3 Lite has a button on GPIO41
- -D LEDPIN=2 ; The M5Stack Atom S3 Lite has a Grove connector on the front, we use this GPIO2
- -D STATUSLED=35 ; The M5Stack Atom S3 Lite has a Multi-Color LED on GPIO35, although I didnt managed to control it
- -D IRPIN=4 ; The M5Stack Atom S3 Lite has a IR LED on GPIO4
- -D DEBUG=1 ; Set these DEBUG flags ONLY if you want to debug and read out Serial (using Visual Studio Code - Serial Monitor)
- -DDEBUG_LEVEL=5
- -DWLED_DEBUG
## Use Case: Interactive Christmas Cards
Imagine distributing interactive Christmas cards embedded with a tiny ESP32 and a string of 20 LEDs to 20 friends. When a friend powers on their card, it connects to their Wi-Fi network and starts polling your server via the `usermod_v2_HttpPullLightControl`. (Tip: Let them scan a QR code to connect to the WLED WiFi, from there they configure their own WiFi).
Your server keeps track of how many cards are active at any given time. If all 20 cards are active, your server instructs each card to light up all of its LEDs. However, if only 4 cards are active, your server instructs each card to light up only 4 LEDs. This creates a real-time interactive experience, symbolizing the collective spirit of the holiday season. Each lit LED represents a friend who's thinking about the others, and the visual feedback creates a sense of connection among the group, despite the physical distance.
This setup demonstrates a unique way to blend traditional holiday sentiments with modern technology, offering an engaging and memorable experience.

View File

@@ -0,0 +1,319 @@
#include "usermod_v2_HttpPullLightControl.h"
// add more strings here to reduce flash memory usage
const char HttpPullLightControl::_name[] PROGMEM = "HttpPullLightControl";
const char HttpPullLightControl::_enabled[] PROGMEM = "Enable";
void HttpPullLightControl::setup() {
//Serial.begin(115200);
// Print version number
DEBUG_PRINT(F("HttpPullLightControl version: "));
DEBUG_PRINTLN(HTTP_PULL_LIGHT_CONTROL_VERSION);
// Start a nice chase so we know its booting and searching for its first http pull.
DEBUG_PRINTLN(F("Starting a nice chase so we now it is booting."));
Segment& seg = strip.getMainSegment();
seg.setMode(28); // Set to chase
seg.speed = 200;
seg.intensity = 255;
seg.setPalette(128);
seg.setColor(0, 5263440);
seg.setColor(1, 0);
seg.setColor(2, 4605510);
// Go on with generating a unique ID and splitting the URL into parts
uniqueId = generateUniqueId(); // Cache the unique ID
DEBUG_PRINT(F("UniqueId calculated: "));
DEBUG_PRINTLN(uniqueId);
parseUrl();
DEBUG_PRINTLN(F("HttpPullLightControl successfully setup"));
}
// This is the main loop function, from here we check the URL and handle the response.
// Effects or individual lights are set as a result from this.
void HttpPullLightControl::loop() {
if (!enabled || offMode) return; // Do nothing when not enabled or powered off
if (millis() - lastCheck >= checkInterval * 1000) {
DEBUG_PRINTLN(F("Calling checkUrl function"));
checkUrl();
lastCheck = millis();
}
}
// Generate a unique ID based on the MAC address and a SALT
String HttpPullLightControl::generateUniqueId() {
uint8_t mac[6];
WiFi.macAddress(mac);
char macStr[18];
sprintf(macStr, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
// Set the MAC Address to a string and make it UPPERcase
String macString = String(macStr);
macString.toUpperCase();
DEBUG_PRINT(F("WiFi MAC address is: "));
DEBUG_PRINTLN(macString);
DEBUG_PRINT(F("Salt is: "));
DEBUG_PRINTLN(salt);
String input = macString + salt;
#ifdef ESP8266
// For ESP8266 we use the Hash.h library which is built into the ESP8266 Core
return sha1(input);
#endif
#ifdef ESP32
// For ESP32 we use the mbedtls library which is built into the ESP32 core
int status = 0;
unsigned char shaResult[20]; // SHA1 produces a hash of 20 bytes (which is 40 HEX characters)
mbedtls_sha1_context ctx;
mbedtls_sha1_init(&ctx);
status = mbedtls_sha1_starts_ret(&ctx);
if (status != 0) {
DEBUG_PRINTLN(F("Error starting SHA1 checksum calculation"));
}
status = mbedtls_sha1_update_ret(&ctx, reinterpret_cast<const unsigned char*>(input.c_str()), input.length());
if (status != 0) {
DEBUG_PRINTLN(F("Error feeding update buffer into ongoing SHA1 checksum calculation"));
}
status = mbedtls_sha1_finish_ret(&ctx, shaResult);
if (status != 0) {
DEBUG_PRINTLN(F("Error finishing SHA1 checksum calculation"));
}
mbedtls_sha1_free(&ctx);
// Convert the Hash to a hexadecimal string
char buf[41];
for (int i = 0; i < 20; i++) {
sprintf(&buf[i*2], "%02x", shaResult[i]);
}
return String(buf);
#endif
}
// This function is called when the user updates the Sald and so we need to re-calculate the unique ID
void HttpPullLightControl::updateSalt(String newSalt) {
DEBUG_PRINTLN(F("Salt updated"));
this->salt = newSalt;
uniqueId = generateUniqueId();
DEBUG_PRINT(F("New UniqueId is: "));
DEBUG_PRINTLN(uniqueId);
}
// The function is used to separate the URL in a host part and a path part
void HttpPullLightControl::parseUrl() {
int firstSlash = url.indexOf('/', 7); // Skip http(s)://
host = url.substring(7, firstSlash);
path = url.substring(firstSlash);
}
// This function is called by WLED when the USERMOD config is read
bool HttpPullLightControl::readFromConfig(JsonObject& root) {
// Attempt to retrieve the nested object for this usermod
JsonObject top = root[FPSTR(_name)];
bool configComplete = !top.isNull(); // check if the object exists
// Retrieve the values using the getJsonValue function for better error handling
configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled, enabled); // default value=enabled
configComplete &= getJsonValue(top["checkInterval"], checkInterval, checkInterval); // default value=60
#ifndef HTTP_PULL_LIGHT_CONTROL_HIDE_URL
configComplete &= getJsonValue(top["url"], url, url); // default value="http://example.com"
#endif
#ifndef HTTP_PULL_LIGHT_CONTROL_HIDE_SALT
configComplete &= getJsonValue(top["salt"], salt, salt); // default value=your_salt_here
#endif
return configComplete;
}
// This function is called by WLED when the USERMOD config is saved in the frontend
void HttpPullLightControl::addToConfig(JsonObject& root) {
// Create a nested object for this usermod
JsonObject top = root.createNestedObject(FPSTR(_name));
// Write the configuration parameters to the nested object
top[FPSTR(_enabled)] = enabled;
if (enabled==false)
// To make it a bit more user-friendly, we unfreeze the main segment after disabling the module. Because individual light control (like for a christmas card) might have been done.
strip.getMainSegment().freeze=false;
top["checkInterval"] = checkInterval;
#ifndef HTTP_PULL_LIGHT_CONTROL_HIDE_URL
top["url"] = url;
#endif
#ifndef HTTP_PULL_LIGHT_CONTROL_HIDE_SALT
top["salt"] = salt;
updateSalt(salt); // Update the UniqueID
#endif
parseUrl(); // Re-parse the URL, maybe path and host is changed
}
// Do the http request here. Note that we can not do https requests with the AsyncTCP library
// We do everything Asynchronous, so all callbacks are defined here
void HttpPullLightControl::checkUrl() {
// Extra Inactivity check to see if AsyncCLient hangs
if (client != nullptr && ( millis() - lastActivityTime > inactivityTimeout ) ) {
DEBUG_PRINTLN(F("Inactivity detected, deleting client."));
delete client;
client = nullptr;
}
if (client != nullptr && client->connected()) {
DEBUG_PRINTLN(F("We are still connected, do nothing"));
// Do nothing, Client is still connected
return;
}
if (client != nullptr) {
// Delete previous client instance if exists, just to prevent any memory leaks
DEBUG_PRINTLN(F("Delete previous instances"));
delete client;
client = nullptr;
}
DEBUG_PRINTLN(F("Creating new AsyncClient instance."));
client = new AsyncClient();
if(client) {
client->onData([](void *arg, AsyncClient *c, void *data, size_t len) {
DEBUG_PRINTLN(F("Data received."));
// Cast arg back to the usermod class instance
HttpPullLightControl *instance = (HttpPullLightControl *)arg;
instance->lastActivityTime = millis(); // Update lastactivity time when data is received
// Convertert to Safe-String
char *strData = new char[len + 1];
strncpy(strData, (char*)data, len);
strData[len] = '\0';
String responseData = String(strData);
//String responseData = String((char *)data);
// Make sure its zero-terminated String
//responseData[len] = '\0';
delete[] strData; // Do not forget to remove this one
instance->handleResponse(responseData);
}, this);
client->onDisconnect([](void *arg, AsyncClient *c) {
DEBUG_PRINTLN(F("Disconnected."));
//Set the class-own client pointer to nullptr if its the current client
HttpPullLightControl *instance = static_cast<HttpPullLightControl*>(arg);
if (instance->client == c) {
delete instance->client; // Delete the client instance
instance->client = nullptr;
}
}, this);
client->onTimeout([](void *arg, AsyncClient *c, uint32_t time) {
DEBUG_PRINTLN(F("Timeout"));
//Set the class-own client pointer to nullptr if its the current client
HttpPullLightControl *instance = static_cast<HttpPullLightControl*>(arg);
if (instance->client == c) {
delete instance->client; // Delete the client instance
instance->client = nullptr;
}
}, this);
client->onError([](void *arg, AsyncClient *c, int8_t error) {
DEBUG_PRINTLN("Connection error occurred!");
DEBUG_PRINT("Error code: ");
DEBUG_PRINTLN(error);
//Set the class-own client pointer to nullptr if its the current client
HttpPullLightControl *instance = static_cast<HttpPullLightControl*>(arg);
if (instance->client == c) {
delete instance->client;
instance->client = nullptr;
}
// Do not remove client here, it is maintained by AsyncClient
}, this);
client->onConnect([](void *arg, AsyncClient *c) {
// Cast arg back to the usermod class instance
HttpPullLightControl *instance = (HttpPullLightControl *)arg;
instance->onClientConnect(c); // Call a method on the instance when the client connects
}, this);
client->setAckTimeout(ackTimeout); // Just some safety measures because we do not want any memory fillup
client->setRxTimeout(rxTimeout);
DEBUG_PRINT(F("Connecting to: "));
DEBUG_PRINT(host);
DEBUG_PRINT(F(" via port "));
DEBUG_PRINTLN((url.startsWith("https")) ? 443 : 80);
// Update lastActivityTime just before sending the request
lastActivityTime = millis();
//Try to connect
if (!client->connect(host.c_str(), (url.startsWith("https")) ? 443 : 80)) {
DEBUG_PRINTLN(F("Failed to initiate connection."));
// Connection failed, so cleanup
delete client;
client = nullptr;
} else {
// Connection successfull, wait for callbacks to go on.
DEBUG_PRINTLN(F("Connection initiated, awaiting response..."));
}
} else {
DEBUG_PRINTLN(F("Failed to create AsyncClient instance."));
}
}
// This function is called from the checkUrl function when the connection is establised
// We request the data here
void HttpPullLightControl::onClientConnect(AsyncClient *c) {
DEBUG_PRINT(F("Client connected: "));
DEBUG_PRINTLN(c->connected() ? F("Yes") : F("No"));
if (c->connected()) {
String request = "GET " + path + (path.indexOf('?') > 0 ? "&id=" : "?id=") + uniqueId + " HTTP/1.1\r\n"
"Host: " + host + "\r\n"
"Connection: close\r\n"
"Accept: application/json\r\n"
"Accept-Encoding: identity\r\n" // No compression
"User-Agent: ESP32 HTTP Client\r\n\r\n"; // Optional: User-Agent and end with a double rnrn !
DEBUG_PRINT(request.c_str());
auto bytesSent = c->write(request.c_str());
if (bytesSent == 0) {
// Connection could not be made
DEBUG_PRINT(F("Failed to send HTTP request."));
} else {
DEBUG_PRINT(F("Request sent successfully, bytes sent: "));
DEBUG_PRINTLN(bytesSent );
}
}
}
// This function is called when we receive data after connecting and doing our request
// It parses the JSON data to WLED
void HttpPullLightControl::handleResponse(String& responseStr) {
DEBUG_PRINTLN(F("Received response for handleResponse."));
// Get a Bufferlock, we can not use doc
if (!requestJSONBufferLock(myLockId)) {
DEBUG_PRINT(F("ERROR: Can not request JSON Buffer Lock, number: "));
DEBUG_PRINTLN(myLockId);
releaseJSONBufferLock(); // Just release in any case, maybe there was already a buffer lock
return;
}
// Search for two linebreaks between headers and content
int bodyPos = responseStr.indexOf("\r\n\r\n");
if (bodyPos > 0) {
String jsonStr = responseStr.substring(bodyPos + 4); // +4 Skip the two CRLFs
jsonStr.trim();
DEBUG_PRINTLN("Response: ");
DEBUG_PRINTLN(jsonStr);
// Check for valid JSON, otherwise we brick the program runtime
if (jsonStr[0] == '{' || jsonStr[0] == '[') {
// Attempt to deserialize the JSON response
DeserializationError error = deserializeJson(doc, jsonStr);
if (error == DeserializationError::Ok) {
// Get JSON object from th doc
JsonObject obj = doc.as<JsonObject>();
// Parse the object throuhg deserializeState (use CALL_MODE_NO_NOTIFY or OR CALL_MODE_DIRECT_CHANGE)
deserializeState(obj, CALL_MODE_NO_NOTIFY);
} else {
// If there is an error in deserialization, exit the function
DEBUG_PRINT(F("DeserializationError: "));
DEBUG_PRINTLN(error.c_str());
}
} else {
DEBUG_PRINTLN(F("Invalid JSON response"));
}
} else {
DEBUG_PRINTLN(F("No body found in the response"));
}
// Release the BufferLock again
releaseJSONBufferLock();
}

View File

@@ -0,0 +1,104 @@
#pragma once
/*
* Usermod: HttpPullLightControl
* Versie: 0.0.4
* Repository: https://github.com/roelbroersma/WLED-usermodv2_HttpPullLightControl
* Author: Roel Broersma
* Website: https://www.roelbroersma.nl
* Github author: github.com/roelbroersma
* Description: This usermod for WLED will request a given URL to know which effects
* or individual lights it should turn on/off. So you can remote control a WLED
* installation without having access to it (if no port forward, vpn or public IP is available).
* Use Case: Create a WLED 'Ring of Thought' christmas card. Sent a LED ring with 60 LEDs to 60 friends.
* When they turn it on and put it at their WiFi, it will contact your server. Now you can reply with a given
* number of lights that should turn on. Each light is a friend who did contact your server in the past 5 minutes.
* So on each of your friends LED rings, the number of lights will be the number of friends who have it turned on.
* Features: It sends a unique ID (has of MAC and salt) to the URL, so you can define each client without a need to map their IP address.
* Tested: Tested on WLED v0.14 with ESP32-S3 (M5Stack Atom S3 Lite), but should also workd for other ESPs and ESP8266.
*/
#include "wled.h"
// Use the following for SHA1 computation of our HASH, unfortunatelly PlatformIO doesnt recognize Hash.h while its already in the Core.
// We use Hash.h for ESP8266 (in the core) and mbedtls/sha256.h for ESP32 (in the core).
#ifdef ESP8266
#include <Hash.h>
#endif
#ifdef ESP32
#include "mbedtls/sha1.h"
#endif
#define HTTP_PULL_LIGHT_CONTROL_VERSION "0.0.4"
class HttpPullLightControl : public Usermod {
private:
static const char _name[];
static const char _enabled[];
static const char _salt[];
static const char _url[];
bool enabled = true;
#ifdef HTTP_PULL_LIGHT_CONTROL_INTERVAL
uint16_t checkInterval = HTTP_PULL_LIGHT_CONTROL_INTERVAL;
#else
uint16_t checkInterval = 60; // Default interval of 1 minute
#endif
#ifdef HTTP_PULL_LIGHT_CONTROL_URL
String url = HTTP_PULL_LIGHT_CONTROL_URL;
#else
String url = "http://example.org/example.php"; // Default-URL (http only!), can also be url with IP address in it. HttpS urls are not supported (yet) because of AsyncTCP library
#endif
#ifdef HTTP_PULL_LIGHT_CONTROL_SALT
String salt = HTTP_PULL_LIGHT_CONTROL_SALT;
#else
String salt = "1just_a_very-secret_salt2"; // Salt for generating a unique ID when requesting the URL (in this way you can give different answers based on the WLED device who does the request)
#endif
// NOTE THAT THERE IS ALSO A #ifdef HTTP_PULL_LIGHT_CONTROL_HIDE_URL and a HTTP_PULL_LIGHT_CONTROL_HIDE_SALT IF YOU DO NOT WANT TO SHOW THE OPTIONS IN THE USERMOD SETTINGS
// Define constants
static const uint8_t myLockId = USERMOD_ID_HTTP_PULL_LIGHT_CONTROL ; // Used for the requestJSONBufferLock(id) function
static const int16_t ackTimeout = 9000; // ACK timeout in milliseconds when doing the URL request
static const uint16_t rxTimeout = 9000; // RX timeout in milliseconds when doing the URL request
static const unsigned long FNV_offset_basis = 2166136261;
static const unsigned long FNV_prime = 16777619;
static const unsigned long inactivityTimeout = 30000; // When the AsyncClient is inactive (hanging) for this many milliseconds, we kill it
unsigned long lastCheck = 0; // Timestamp of last check
unsigned long lastActivityTime = 0; // Time of last activity of AsyncClient
String host; // Host extracted from the URL
String path; // Path extracted from the URL
String uniqueId; // Cached unique ID
AsyncClient *client = nullptr; // Used very often, beware of closing and freeing
String generateUniqueId();
void parseUrl();
void updateSalt(String newSalt); // Update the salt value and recalculate the unique ID
void checkUrl(); // Check the specified URL for light control instructions
void handleResponse(String& response);
void onClientConnect(AsyncClient *c);
public:
void setup();
void loop();
bool readFromConfig(JsonObject& root);
void addToConfig(JsonObject& root);
uint16_t getId() { return USERMOD_ID_HTTP_PULL_LIGHT_CONTROL; }
inline void enable(bool enable) { enabled = enable; } // Enable or Disable the usermod
inline bool isEnabled() { return enabled; } // Get usermod enabled or disabled state
virtual ~HttpPullLightControl() {
// Remove the cached client if needed
if (client) {
client->onDisconnect(nullptr);
client->onError(nullptr);
client->onTimeout(nullptr);
client->onData(nullptr);
client->onConnect(nullptr);
// Now it is safe to delete the client.
delete client; // This is safe even if client is nullptr.
client = nullptr;
}
}
};

View File

@@ -0,0 +1,14 @@
# ANIMartRIX
Addes the effects from ANIMartRIX to WLED
CC BY-NC 3.0 licensed effects by Stefan Petrick, include this usermod only if you accept the terms!
## Installation
Please uncomment the two references to ANIMartRIX in your platform.ini
lib_dep to a version of https://github.com/netmindz/animartrix.git
and the build_flags -D USERMOD_ANIMARTRIX

View File

@@ -0,0 +1,456 @@
#pragma once
#include "wled.h"
#include <ANIMartRIX.h>
#warning WLED usermod: CC BY-NC 3.0 licensed effects by Stefan Petrick, include this usermod only if you accept the terms!
//========================================================================================================================
static const char _data_FX_mode_Module_Experiment10[] PROGMEM = "Z💡Module_Experiment10@Speed;;1;2";
static const char _data_FX_mode_Module_Experiment9[] PROGMEM = "Z💡Module_Experiment9@Speed;;1;2";
static const char _data_FX_mode_Module_Experiment8[] PROGMEM = "Z💡Module_Experiment8@Speed;;1;2";
static const char _data_FX_mode_Module_Experiment7[] PROGMEM = "Z💡Module_Experiment7@Speed;;1;2";
static const char _data_FX_mode_Module_Experiment6[] PROGMEM = "Z💡Module_Experiment6@Speed;;1;2";
static const char _data_FX_mode_Module_Experiment5[] PROGMEM = "Z💡Module_Experiment5@Speed;;1;2";
static const char _data_FX_mode_Module_Experiment4[] PROGMEM = "Z💡Module_Experiment4@Speed;;1;2";
static const char _data_FX_mode_Zoom2[] PROGMEM = "Z💡Zoom2@Speed;;1;2";
static const char _data_FX_mode_Module_Experiment3[] PROGMEM = "Z💡Module_Experiment3@Speed;;1;2";
static const char _data_FX_mode_Module_Experiment2[] PROGMEM = "Z💡Module_Experiment2@Speed;;1;2";
static const char _data_FX_mode_Module_Experiment1[] PROGMEM = "Z💡Module_Experiment1@Speed;;1;2";
static const char _data_FX_mode_Parametric_Water[] PROGMEM = "Z💡Parametric_Water@Speed;;1;2";
static const char _data_FX_mode_Water[] PROGMEM = "Z💡Water@Speed;;1;2";
static const char _data_FX_mode_Complex_Kaleido_6[] PROGMEM = "Z💡Complex_Kaleido_6@Speed;;1;2";
static const char _data_FX_mode_Complex_Kaleido_5[] PROGMEM = "Z💡Complex_Kaleido_5@Speed;;1;2";
static const char _data_FX_mode_Complex_Kaleido_4[] PROGMEM = "Z💡Complex_Kaleido_4@Speed;;1;2";
static const char _data_FX_mode_Complex_Kaleido_3[] PROGMEM = "Z💡Complex_Kaleido_3@Speed;;1;2";
static const char _data_FX_mode_Complex_Kaleido_2[] PROGMEM = "Z💡Complex_Kaleido_2@Speed;;1;2";
static const char _data_FX_mode_Complex_Kaleido[] PROGMEM = "Z💡Complex_Kaleido@Speed;;1;2";
static const char _data_FX_mode_SM10[] PROGMEM = "Z💡SM10@Speed;;1;2";
static const char _data_FX_mode_SM9[] PROGMEM = "Z💡SM9@Speed;;1;2";
static const char _data_FX_mode_SM8[] PROGMEM = "Z💡SM8@Speed;;1;2";
static const char _data_FX_mode_SM7[] PROGMEM = "Z💡SM7@Speed;;1;2";
static const char _data_FX_mode_SM6[] PROGMEM = "Z💡SM6@Speed;;1;2";
static const char _data_FX_mode_SM5[] PROGMEM = "Z💡SM5@Speed;;1;2";
static const char _data_FX_mode_SM4[] PROGMEM = "Z💡SM4@Speed;;1;2";
static const char _data_FX_mode_SM3[] PROGMEM = "Z💡SM3@Speed;;1;2";
static const char _data_FX_mode_SM2[] PROGMEM = "Z💡SM2@Speed;;1;2";
static const char _data_FX_mode_SM1[] PROGMEM = "Z💡SM1@Speed;;1;2";
static const char _data_FX_mode_Big_Caleido[] PROGMEM = "Z💡Big_Caleido@Speed;;1;2";
static const char _data_FX_mode_RGB_Blobs5[] PROGMEM = "Z💡RGB_Blobs5@Speed;;1;2";
static const char _data_FX_mode_RGB_Blobs4[] PROGMEM = "Z💡RGB_Blobs4@Speed;;1;2";
static const char _data_FX_mode_RGB_Blobs3[] PROGMEM = "Z💡RGB_Blobs3@Speed;;1;2";
static const char _data_FX_mode_RGB_Blobs2[] PROGMEM = "Z💡RGB_Blobs2@Speed;;1;2";
static const char _data_FX_mode_RGB_Blobs[] PROGMEM = "Z💡RGB_Blobs@Speed;;1;2";
static const char _data_FX_mode_Polar_Waves[] PROGMEM = "Z💡Polar_Waves@Speed;;1;2";
static const char _data_FX_mode_Slow_Fade[] PROGMEM = "Z💡Slow_Fade@Speed;;1;2";
static const char _data_FX_mode_Zoom[] PROGMEM = "Z💡Zoom@Speed;;1;2";
static const char _data_FX_mode_Hot_Blob[] PROGMEM = "Z💡Hot_Blob@Speed;;1;2";
static const char _data_FX_mode_Spiralus2[] PROGMEM = "Z💡Spiralus2@Speed;;1;2";
static const char _data_FX_mode_Spiralus[] PROGMEM = "Z💡Spiralus@Speed;;1;2";
static const char _data_FX_mode_Yves[] PROGMEM = "Z💡Yves@Speed;;1;2";
static const char _data_FX_mode_Scaledemo1[] PROGMEM = "Z💡Scaledemo1@Speed;;1;2";
static const char _data_FX_mode_Lava1[] PROGMEM = "Z💡Lava1@Speed;;1;2";
static const char _data_FX_mode_Caleido3[] PROGMEM = "Z💡Caleido3@Speed;;1;2";
static const char _data_FX_mode_Caleido2[] PROGMEM = "Z💡Caleido2@Speed;;1;2";
static const char _data_FX_mode_Caleido1[] PROGMEM = "Z💡Caleido1@Speed;;1;2";
static const char _data_FX_mode_Distance_Experiment[] PROGMEM = "Z💡Distance_Experiment@Speed;;1;2";
static const char _data_FX_mode_Center_Field[] PROGMEM = "Z💡Center_Field@Speed;;1;2";
static const char _data_FX_mode_Waves[] PROGMEM = "Z💡Waves@Speed;;1;2";
static const char _data_FX_mode_Chasing_Spirals[] PROGMEM = "Z💡Chasing_Spirals@Speed;;1;2";
static const char _data_FX_mode_Rotating_Blob[] PROGMEM = "Z💡Rotating_Blob@Speed;;1;2";
class ANIMartRIXMod:public ANIMartRIX {
public:
void initEffect() {
if (SEGENV.call == 0) {
init(SEGMENT.virtualWidth(), SEGMENT.virtualHeight(), false);
}
float speedFactor = 1.0;
if (SEGMENT.speed < 128) {
speedFactor = (float) map(SEGMENT.speed, 0, 127, 1, 10) / 10.0f;
}
else{
speedFactor = map(SEGMENT.speed, 128, 255, 10, 100) / 10;
}
setSpeedFactor(speedFactor);
}
void setPixelColor(int x, int y, rgb pixel) {
SEGMENT.setPixelColorXY(x, y, CRGB(pixel.red, pixel.green, pixel.blue));
}
void setPixelColor(int index, rgb pixel) {
SEGMENT.setPixelColor(index, CRGB(pixel.red, pixel.green, pixel.blue));
}
// Add any extra custom effects not part of the ANIMartRIX libary here
};
ANIMartRIXMod anim;
uint16_t mode_Module_Experiment10() {
anim.initEffect();
anim.Module_Experiment10();
return FRAMETIME;
}
uint16_t mode_Module_Experiment9() {
anim.initEffect();
anim.Module_Experiment9();
return FRAMETIME;
}
uint16_t mode_Module_Experiment8() {
anim.initEffect();
anim.Module_Experiment8();
return FRAMETIME;
}
uint16_t mode_Module_Experiment7() {
anim.initEffect();
anim.Module_Experiment7();
return FRAMETIME;
}
uint16_t mode_Module_Experiment6() {
anim.initEffect();
anim.Module_Experiment6();
return FRAMETIME;
}
uint16_t mode_Module_Experiment5() {
anim.initEffect();
anim.Module_Experiment5();
return FRAMETIME;
}
uint16_t mode_Module_Experiment4() {
anim.initEffect();
anim.Module_Experiment4();
return FRAMETIME;
}
uint16_t mode_Zoom2() {
anim.initEffect();
anim.Zoom2();
return FRAMETIME;
}
uint16_t mode_Module_Experiment3() {
anim.initEffect();
anim.Module_Experiment3();
return FRAMETIME;
}
uint16_t mode_Module_Experiment2() {
anim.initEffect();
anim.Module_Experiment2();
return FRAMETIME;
}
uint16_t mode_Module_Experiment1() {
anim.initEffect();
anim.Module_Experiment1();
return FRAMETIME;
}
uint16_t mode_Parametric_Water() {
anim.initEffect();
anim.Parametric_Water();
return FRAMETIME;
}
uint16_t mode_Water() {
anim.initEffect();
anim.Water();
return FRAMETIME;
}
uint16_t mode_Complex_Kaleido_6() {
anim.initEffect();
anim.Complex_Kaleido_6();
return FRAMETIME;
}
uint16_t mode_Complex_Kaleido_5() {
anim.initEffect();
anim.Complex_Kaleido_5();
return FRAMETIME;
}
uint16_t mode_Complex_Kaleido_4() {
anim.initEffect();
anim.Complex_Kaleido_4();
return FRAMETIME;
}
uint16_t mode_Complex_Kaleido_3() {
anim.initEffect();
anim.Complex_Kaleido_3();
return FRAMETIME;
}
uint16_t mode_Complex_Kaleido_2() {
anim.initEffect();
anim.Complex_Kaleido_2();
return FRAMETIME;
}
uint16_t mode_Complex_Kaleido() {
anim.initEffect();
anim.Complex_Kaleido();
return FRAMETIME;
}
uint16_t mode_SM10() {
anim.initEffect();
anim.SM10();
return FRAMETIME;
}
uint16_t mode_SM9() {
anim.initEffect();
anim.SM9();
return FRAMETIME;
}
uint16_t mode_SM8() {
anim.initEffect();
anim.SM8();
return FRAMETIME;
}
// uint16_t mode_SM7() {
// anim.initEffect();
// anim.SM7();
//
// return FRAMETIME;
// }
uint16_t mode_SM6() {
anim.initEffect();
anim.SM6();
return FRAMETIME;
}
uint16_t mode_SM5() {
anim.initEffect();
anim.SM5();
return FRAMETIME;
}
uint16_t mode_SM4() {
anim.initEffect();
anim.SM4();
return FRAMETIME;
}
uint16_t mode_SM3() {
anim.initEffect();
anim.SM3();
return FRAMETIME;
}
uint16_t mode_SM2() {
anim.initEffect();
anim.SM2();
return FRAMETIME;
}
uint16_t mode_SM1() {
anim.initEffect();
anim.SM1();
return FRAMETIME;
}
uint16_t mode_Big_Caleido() {
anim.initEffect();
anim.Big_Caleido();
return FRAMETIME;
}
uint16_t mode_RGB_Blobs5() {
anim.initEffect();
anim.RGB_Blobs5();
return FRAMETIME;
}
uint16_t mode_RGB_Blobs4() {
anim.initEffect();
anim.RGB_Blobs4();
return FRAMETIME;
}
uint16_t mode_RGB_Blobs3() {
anim.initEffect();
anim.RGB_Blobs3();
return FRAMETIME;
}
uint16_t mode_RGB_Blobs2() {
anim.initEffect();
anim.RGB_Blobs2();
return FRAMETIME;
}
uint16_t mode_RGB_Blobs() {
anim.initEffect();
anim.RGB_Blobs();
return FRAMETIME;
}
uint16_t mode_Polar_Waves() {
anim.initEffect();
anim.Polar_Waves();
return FRAMETIME;
}
uint16_t mode_Slow_Fade() {
anim.initEffect();
anim.Slow_Fade();
return FRAMETIME;
}
uint16_t mode_Zoom() {
anim.initEffect();
anim.Zoom();
return FRAMETIME;
}
uint16_t mode_Hot_Blob() {
anim.initEffect();
anim.Hot_Blob();
return FRAMETIME;
}
uint16_t mode_Spiralus2() {
anim.initEffect();
anim.Spiralus2();
return FRAMETIME;
}
uint16_t mode_Spiralus() {
anim.initEffect();
anim.Spiralus();
return FRAMETIME;
}
uint16_t mode_Yves() {
anim.initEffect();
anim.Yves();
return FRAMETIME;
}
uint16_t mode_Scaledemo1() {
anim.initEffect();
anim.Scaledemo1();
return FRAMETIME;
}
uint16_t mode_Lava1() {
anim.initEffect();
anim.Lava1();
return FRAMETIME;
}
uint16_t mode_Caleido3() {
anim.initEffect();
anim.Caleido3();
return FRAMETIME;
}
uint16_t mode_Caleido2() {
anim.initEffect();
anim.Caleido2();
return FRAMETIME;
}
uint16_t mode_Caleido1() {
anim.initEffect();
anim.Caleido1();
return FRAMETIME;
}
uint16_t mode_Distance_Experiment() {
anim.initEffect();
anim.Distance_Experiment();
return FRAMETIME;
}
uint16_t mode_Center_Field() {
anim.initEffect();
anim.Center_Field();
return FRAMETIME;
}
uint16_t mode_Waves() {
anim.initEffect();
anim.Waves();
return FRAMETIME;
}
uint16_t mode_Chasing_Spirals() {
anim.initEffect();
anim.Chasing_Spirals();
return FRAMETIME;
}
uint16_t mode_Rotating_Blob() {
anim.initEffect();
anim.Rotating_Blob();
return FRAMETIME;
}
class AnimartrixUsermod : public Usermod {
protected:
bool enabled = false; //WLEDMM
const char *_name; //WLEDMM
bool initDone = false; //WLEDMM
unsigned long lastTime = 0; //WLEDMM
public:
AnimartrixUsermod(const char *name, bool enabled) {
this->_name = name;
this->enabled = enabled;
} //WLEDMM
void setup() {
strip.addEffect(255, &mode_Module_Experiment10, _data_FX_mode_Module_Experiment10);
strip.addEffect(255, &mode_Module_Experiment9, _data_FX_mode_Module_Experiment9);
strip.addEffect(255, &mode_Module_Experiment8, _data_FX_mode_Module_Experiment8);
strip.addEffect(255, &mode_Module_Experiment7, _data_FX_mode_Module_Experiment7);
strip.addEffect(255, &mode_Module_Experiment6, _data_FX_mode_Module_Experiment6);
strip.addEffect(255, &mode_Module_Experiment5, _data_FX_mode_Module_Experiment5);
strip.addEffect(255, &mode_Module_Experiment4, _data_FX_mode_Module_Experiment4);
strip.addEffect(255, &mode_Zoom2, _data_FX_mode_Zoom2);
strip.addEffect(255, &mode_Module_Experiment3, _data_FX_mode_Module_Experiment3);
strip.addEffect(255, &mode_Module_Experiment2, _data_FX_mode_Module_Experiment2);
strip.addEffect(255, &mode_Module_Experiment1, _data_FX_mode_Module_Experiment1);
strip.addEffect(255, &mode_Parametric_Water, _data_FX_mode_Parametric_Water);
strip.addEffect(255, &mode_Water, _data_FX_mode_Water);
strip.addEffect(255, &mode_Complex_Kaleido_6, _data_FX_mode_Complex_Kaleido_6);
strip.addEffect(255, &mode_Complex_Kaleido_5, _data_FX_mode_Complex_Kaleido_5);
strip.addEffect(255, &mode_Complex_Kaleido_4, _data_FX_mode_Complex_Kaleido_4);
strip.addEffect(255, &mode_Complex_Kaleido_3, _data_FX_mode_Complex_Kaleido_3);
strip.addEffect(255, &mode_Complex_Kaleido_2, _data_FX_mode_Complex_Kaleido_2);
strip.addEffect(255, &mode_Complex_Kaleido, _data_FX_mode_Complex_Kaleido);
strip.addEffect(255, &mode_SM10, _data_FX_mode_SM10);
strip.addEffect(255, &mode_SM9, _data_FX_mode_SM9);
strip.addEffect(255, &mode_SM8, _data_FX_mode_SM8);
// strip.addEffect(255, &mode_SM7, _data_FX_mode_SM7);
strip.addEffect(255, &mode_SM6, _data_FX_mode_SM6);
strip.addEffect(255, &mode_SM5, _data_FX_mode_SM5);
strip.addEffect(255, &mode_SM4, _data_FX_mode_SM4);
strip.addEffect(255, &mode_SM3, _data_FX_mode_SM3);
strip.addEffect(255, &mode_SM2, _data_FX_mode_SM2);
strip.addEffect(255, &mode_SM1, _data_FX_mode_SM1);
strip.addEffect(255, &mode_Big_Caleido, _data_FX_mode_Big_Caleido);
strip.addEffect(255, &mode_RGB_Blobs5, _data_FX_mode_RGB_Blobs5);
strip.addEffect(255, &mode_RGB_Blobs4, _data_FX_mode_RGB_Blobs4);
strip.addEffect(255, &mode_RGB_Blobs3, _data_FX_mode_RGB_Blobs3);
strip.addEffect(255, &mode_RGB_Blobs2, _data_FX_mode_RGB_Blobs2);
strip.addEffect(255, &mode_RGB_Blobs, _data_FX_mode_RGB_Blobs);
strip.addEffect(255, &mode_Polar_Waves, _data_FX_mode_Polar_Waves);
strip.addEffect(255, &mode_Slow_Fade, _data_FX_mode_Slow_Fade);
strip.addEffect(255, &mode_Zoom, _data_FX_mode_Zoom);
strip.addEffect(255, &mode_Hot_Blob, _data_FX_mode_Hot_Blob);
strip.addEffect(255, &mode_Spiralus2, _data_FX_mode_Spiralus2);
strip.addEffect(255, &mode_Spiralus, _data_FX_mode_Spiralus);
strip.addEffect(255, &mode_Yves, _data_FX_mode_Yves);
strip.addEffect(255, &mode_Scaledemo1, _data_FX_mode_Scaledemo1);
strip.addEffect(255, &mode_Lava1, _data_FX_mode_Lava1);
strip.addEffect(255, &mode_Caleido3, _data_FX_mode_Caleido3);
strip.addEffect(255, &mode_Caleido2, _data_FX_mode_Caleido2);
strip.addEffect(255, &mode_Caleido1, _data_FX_mode_Caleido1);
strip.addEffect(255, &mode_Distance_Experiment, _data_FX_mode_Distance_Experiment);
strip.addEffect(255, &mode_Center_Field, _data_FX_mode_Center_Field);
strip.addEffect(255, &mode_Waves, _data_FX_mode_Waves);
strip.addEffect(255, &mode_Chasing_Spirals, _data_FX_mode_Chasing_Spirals);
strip.addEffect(255, &mode_Rotating_Blob, _data_FX_mode_Rotating_Blob);
initDone = true;
}
void loop() {
if (!enabled || strip.isUpdating()) return;
// do your magic here
if (millis() - lastTime > 1000) {
//USER_PRINTLN("I'm alive!");
lastTime = millis();
}
}
void addToJsonInfo(JsonObject& root)
{
char myStringBuffer[16]; // buffer for snprintf()
JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u");
JsonArray infoArr = user.createNestedArray(FPSTR(_name));
String uiDomString = F("Animartrix requires the Creative Commons Attribution License CC BY-NC 3.0");
infoArr.add(uiDomString);
}
uint16_t getId()
{
return USERMOD_ID_ANIMARTRIX;
}
};

View File

@@ -101,7 +101,7 @@ class AutoSaveUsermod : public Usermod {
// network here
void setup() {
#ifdef USERMOD_FOUR_LINE_DISPLAY
// This Usermod has enhanced funcionality if
// This Usermod has enhanced functionality if
// FourLineDisplayUsermod is available.
display = (FourLineDisplayUsermod*) usermods.lookup(USERMOD_ID_FOUR_LINE_DISP);
#endif
@@ -148,7 +148,7 @@ class AutoSaveUsermod : public Usermod {
if (autoSaveAfter && now > autoSaveAfter) {
autoSaveAfter = 0;
// Time to auto save. You may have some flickry?
// Time to auto save. You may have some flickery?
saveSettings();
displayOverlay();
}

View File

@@ -1,63 +0,0 @@
# I2C 4 Line Display Usermod
First, thanks to the authors of the ssd11306_i2c_oled_u8g2 mod.
Provides a four line display using either
128x32 or 128x64 OLED displays.
It can operate independently, but starts to provide
a relatively complete on-device UI when paired with the
Rotary Encoder UI usermod. I strongly encourage you to use
them together.
[See the pair of usermods in action](https://www.youtube.com/watch?v=tITQY80rIOA)
## Installation
Copy and update the example `platformio_override.ini.sample`
from the Rotary Encoder UI usermode folder to the root directory of your particular build.
This file should be placed in the same directory as `platformio.ini`.
### Define Your Options
* `USERMOD_FOUR_LINE_DISPLAY` - define this to have this mod included wled00\usermods_list.cpp - also tells Rotary Encoder usermod, if installed, the display is available
* `FLD_PIN_SCL` - The display SCL pin, defaults to 5
* `FLD_PIN_SDA` - The display SDA pin, defaults to 4
All of the parameters can be configured via the Usermods settings page, inluding GPIO pins.
### PlatformIO requirements
This usermod requires the `U8g2` and `Wire` libraries. See the
`platformio_override.ini.sample` found in the Rotary Encoder
UI usermod folder for how to include these using `platformio_override.ini`.
## Configuration
* `enabled` - enable/disable usermod
* `pin` - GPIO pins used for display; I2C displays use Clk & Data; SPI displays can use SCK, MOSI, CS, DC & RST
* `type` - display type in numeric format
* 1 = I2C SSD1306 128x32
* 2 = I2C SH1106 128x32
* 3 = I2C SSD1306 128x64 (4 double-height lines)
* 4 = I2C SSD1305 128x32
* 5 = I2C SSD1305 128x64 (4 double-height lines)
* 6 = SPI SSD1306 128x32
* 7 = SPI SSD1306 128x64 (4 double-height lines)
* `contrast` - set display contrast (higher contrast may reduce display lifetime)
* `refreshRateSec` - display refresh time in seconds
* `screenTimeOutSec` - screen saver time-out in seconds
* `flip` - flip/rotate display 180°
* `sleepMode` - enable/disable screen saver
* `clockMode` - enable/disable clock display in screen saver mode
* `i2c-freq-kHz` - I2C clock frequency in kHz (may help reduce dropped frames, range: 400-3400)
## Change Log
2021-02
* First public release
2021-04
* Adaptation for runtime configuration.
2021-11
* Added configuration option description.

View File

@@ -1,742 +0,0 @@
#pragma once
#include "wled.h"
#include <U8x8lib.h> // from https://github.com/olikraus/u8g2/
//
// Insired by the v1 usermod: ssd1306_i2c_oled_u8g2
//
// v2 usermod for using 128x32 or 128x64 i2c
// OLED displays to provide a four line display
// for WLED.
//
// Dependencies
// * This usermod REQURES the ModeSortUsermod
// * This Usermod works best, by far, when coupled
// with RotaryEncoderUIUsermod.
//
// Make sure to enable NTP and set your time zone in WLED Config | Time.
//
// REQUIREMENT: You must add the following requirements to
// REQUIREMENT: "lib_deps" within platformio.ini / platformio_override.ini
// REQUIREMENT: * U8g2 (the version already in platformio.ini is fine)
// REQUIREMENT: * Wire
//
//The SCL and SDA pins are defined here.
#ifndef FLD_PIN_SCL
#define FLD_PIN_SCL i2c_scl
#endif
#ifndef FLD_PIN_SDA
#define FLD_PIN_SDA i2c_sda
#endif
#ifndef FLD_PIN_CLOCKSPI
#define FLD_PIN_CLOCKSPI spi_sclk
#endif
#ifndef FLD_PIN_DATASPI
#define FLD_PIN_DATASPI spi_mosi
#endif
#ifndef FLD_PIN_CS
#define FLD_PIN_CS spi_cs
#endif
#ifdef ARDUINO_ARCH_ESP32
#ifndef FLD_PIN_DC
#define FLD_PIN_DC 19
#endif
#ifndef FLD_PIN_RESET
#define FLD_PIN_RESET 26
#endif
#else
#ifndef FLD_PIN_DC
#define FLD_PIN_DC 12
#endif
#ifndef FLD_PIN_RESET
#define FLD_PIN_RESET 16
#endif
#endif
#ifndef FLD_TYPE
#ifndef FLD_SPI_DEFAULT
#define FLD_TYPE SSD1306
#else
#define FLD_TYPE SSD1306_SPI
#endif
#endif
// When to time out to the clock or blank the screen
// if SLEEP_MODE_ENABLED.
#define SCREEN_TIMEOUT_MS 60*1000 // 1 min
#define TIME_INDENT 0
#define DATE_INDENT 2
// Minimum time between redrawing screen in ms
#define USER_LOOP_REFRESH_RATE_MS 1000
// Extra char (+1) for null
#define LINE_BUFFER_SIZE 16+1
typedef enum {
FLD_LINE_BRIGHTNESS = 0,
FLD_LINE_EFFECT_SPEED,
FLD_LINE_EFFECT_INTENSITY,
FLD_LINE_MODE,
FLD_LINE_PALETTE,
FLD_LINE_TIME
} Line4Type;
typedef enum {
NONE = 0,
SSD1306, // U8X8_SSD1306_128X32_UNIVISION_HW_I2C
SH1106, // U8X8_SH1106_128X64_WINSTAR_HW_I2C
SSD1306_64, // U8X8_SSD1306_128X64_NONAME_HW_I2C
SSD1305, // U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C
SSD1305_64, // U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C
SSD1306_SPI, // U8X8_SSD1306_128X32_NONAME_HW_SPI
SSD1306_SPI64 // U8X8_SSD1306_128X64_NONAME_HW_SPI
} DisplayType;
class FourLineDisplayUsermod : public Usermod {
private:
bool initDone = false;
unsigned long lastTime = 0;
// HW interface & configuration
U8X8 *u8x8 = nullptr; // pointer to U8X8 display object
#ifndef FLD_SPI_DEFAULT
int8_t ioPin[5] = {FLD_PIN_SCL, FLD_PIN_SDA, -1, -1, -1}; // I2C pins: SCL, SDA
uint32_t ioFrequency = 400000; // in Hz (minimum is 100000, baseline is 400000 and maximum should be 3400000)
#else
int8_t ioPin[5] = {FLD_PIN_CLOCKSPI, FLD_PIN_DATASPI, FLD_PIN_CS, FLD_PIN_DC, FLD_PIN_RESET}; // SPI pins: CLK, MOSI, CS, DC, RST
uint32_t ioFrequency = 1000000; // in Hz (minimum is 500kHz, baseline is 1MHz and maximum should be 20MHz)
#endif
DisplayType type = FLD_TYPE; // display type
bool flip = false; // flip display 180°
uint8_t contrast = 10; // screen contrast
uint8_t lineHeight = 1; // 1 row or 2 rows
uint32_t refreshRate = USER_LOOP_REFRESH_RATE_MS; // in ms
uint32_t screenTimeout = SCREEN_TIMEOUT_MS; // in ms
bool sleepMode = true; // allow screen sleep?
bool clockMode = false; // display clock
bool enabled = true;
// Next variables hold the previous known values to determine if redraw is
// required.
String knownSsid = "";
IPAddress knownIp;
uint8_t knownBrightness = 0;
uint8_t knownEffectSpeed = 0;
uint8_t knownEffectIntensity = 0;
uint8_t knownMode = 0;
uint8_t knownPalette = 0;
uint8_t knownMinute = 99;
uint8_t knownHour = 99;
bool displayTurnedOff = false;
unsigned long lastUpdate = 0;
unsigned long lastRedraw = 0;
unsigned long overlayUntil = 0;
Line4Type lineType = FLD_LINE_BRIGHTNESS;
// Set to 2 or 3 to mark lines 2 or 3. Other values ignored.
byte markLineNum = 0;
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
static const char _enabled[];
static const char _contrast[];
static const char _refreshRate[];
static const char _screenTimeOut[];
static const char _flip[];
static const char _sleepMode[];
static const char _clockMode[];
static const char _busClkFrequency[];
// If display does not work or looks corrupted check the
// constructor reference:
// https://github.com/olikraus/u8g2/wiki/u8x8setupcpp
// or check the gallery:
// https://github.com/olikraus/u8g2/wiki/gallery
public:
// gets called once at boot. Do all initialization that doesn't depend on
// network here
void setup() {
if (type == NONE || !enabled) return;
bool isHW;
PinOwner po = PinOwner::UM_FourLineDisplay;
if (type == SSD1306_SPI || type == SSD1306_SPI64) {
isHW = (ioPin[0]==spi_sclk && ioPin[1]==spi_mosi);
if (isHW) po = PinOwner::HW_SPI; // allow multiple allocations of HW I2C bus pins
PinManagerPinType pins[5] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true }, { ioPin[3], true }, { ioPin[4], true }};
if (!pinManager.allocateMultiplePins(pins, 5, po)) { type=NONE; return; }
} else {
isHW = (ioPin[0]==i2c_scl && ioPin[1]==i2c_sda);
if (isHW) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true } };
if (!pinManager.allocateMultiplePins(pins, 2, po)) { type=NONE; return; }
}
DEBUG_PRINTLN(F("Allocating display."));
switch (type) {
case SSD1306:
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
else u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
lineHeight = 1;
break;
case SH1106:
if (!isHW) u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
else u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
lineHeight = 2;
break;
case SSD1306_64:
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
else u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
lineHeight = 2;
break;
case SSD1305:
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
else u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
lineHeight = 1;
break;
case SSD1305_64:
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
else u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
lineHeight = 2;
break;
case SSD1306_SPI:
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]);
else u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset
lineHeight = 1;
break;
case SSD1306_SPI64:
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]);
else u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset
lineHeight = 2;
break;
default:
u8x8 = nullptr;
}
if (nullptr == u8x8) {
DEBUG_PRINTLN(F("Display init failed."));
pinManager.deallocateMultiplePins((const uint8_t*)ioPin, (type == SSD1306_SPI || type == SSD1306_SPI64) ? 5 : 2, po);
type = NONE;
return;
}
initDone = true;
DEBUG_PRINTLN(F("Starting display."));
/*if (!(type == SSD1306_SPI || type == SSD1306_SPI64))*/ u8x8->setBusClock(ioFrequency); // can be used for SPI too
u8x8->begin();
setFlipMode(flip);
setContrast(contrast); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255
setPowerSave(0);
drawString(0, 0, "Loading...");
}
// gets called every time WiFi is (re-)connected. Initialize own network
// interfaces here
void connected() {}
/**
* Da loop.
*/
void loop() {
if (!enabled || millis() - lastUpdate < (clockMode?1000:refreshRate) || strip.isUpdating()) return;
lastUpdate = millis();
redraw(false);
}
/**
* Wrappers for screen drawing
*/
void setFlipMode(uint8_t mode) {
if (type == NONE || !enabled) return;
u8x8->setFlipMode(mode);
}
void setContrast(uint8_t contrast) {
if (type == NONE || !enabled) return;
u8x8->setContrast(contrast);
}
void drawString(uint8_t col, uint8_t row, const char *string, bool ignoreLH=false) {
if (type == NONE || !enabled) return;
u8x8->setFont(u8x8_font_chroma48medium8_r);
if (!ignoreLH && lineHeight==2) u8x8->draw1x2String(col, row, string);
else u8x8->drawString(col, row, string);
}
void draw2x2String(uint8_t col, uint8_t row, const char *string) {
if (type == NONE || !enabled) return;
u8x8->setFont(u8x8_font_chroma48medium8_r);
u8x8->draw2x2String(col, row, string);
}
void drawGlyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font, bool ignoreLH=false) {
if (type == NONE || !enabled) return;
u8x8->setFont(font);
if (!ignoreLH && lineHeight==2) u8x8->draw1x2Glyph(col, row, glyph);
else u8x8->drawGlyph(col, row, glyph);
}
uint8_t getCols() {
if (type==NONE || !enabled) return 0;
return u8x8->getCols();
}
void clear() {
if (type == NONE || !enabled) return;
u8x8->clear();
}
void setPowerSave(uint8_t save) {
if (type == NONE || !enabled) return;
u8x8->setPowerSave(save);
}
void center(String &line, uint8_t width) {
int len = line.length();
if (len<width) for (byte i=(width-len)/2; i>0; i--) line = ' ' + line;
for (byte i=line.length(); i<width; i++) line += ' ';
}
/**
* Redraw the screen (but only if things have changed
* or if forceRedraw).
*/
void redraw(bool forceRedraw) {
static bool showName = false;
unsigned long now = millis();
if (type == NONE || !enabled) return;
if (overlayUntil > 0) {
if (now >= overlayUntil) {
// Time to display the overlay has elapsed.
overlayUntil = 0;
forceRedraw = true;
} else {
// We are still displaying the overlay
// Don't redraw.
return;
}
}
// Check if values which are shown on display changed from the last time.
if (forceRedraw ||
(((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid) ||
(knownIp != (apActive ? IPAddress(4, 3, 2, 1) : Network.localIP())) ||
(knownBrightness != bri) ||
(knownEffectSpeed != effectSpeed) ||
(knownEffectIntensity != effectIntensity) ||
(knownMode != strip.getMainSegment().mode) ||
(knownPalette != strip.getMainSegment().palette)) {
knownHour = 99; // force time update
lastRedraw = now; // update lastRedraw marker
} else if (sleepMode && !displayTurnedOff && ((now - lastRedraw)/1000)%5 == 0) {
// change line every 5s
showName = !showName;
switch (lineType) {
case FLD_LINE_BRIGHTNESS:
lineType = FLD_LINE_EFFECT_SPEED;
break;
case FLD_LINE_MODE:
lineType = FLD_LINE_BRIGHTNESS;
break;
case FLD_LINE_PALETTE:
lineType = clockMode ? FLD_LINE_MODE : FLD_LINE_BRIGHTNESS;
break;
case FLD_LINE_EFFECT_SPEED:
lineType = FLD_LINE_EFFECT_INTENSITY;
break;
case FLD_LINE_EFFECT_INTENSITY:
lineType = FLD_LINE_PALETTE;
break;
default:
lineType = FLD_LINE_MODE;
break;
}
knownHour = 99; // force time update
// do not update lastRedraw marker if just switching row contenet
} else {
// Nothing to change.
// Turn off display after 3 minutes with no change.
if(sleepMode && !displayTurnedOff && (millis() - lastRedraw > screenTimeout)) {
// We will still check if there is a change in redraw()
// and turn it back on if it changed.
sleepOrClock(true);
} else if (displayTurnedOff && clockMode) {
showTime();
}
return;
}
// Turn the display back on
if (displayTurnedOff) sleepOrClock(false);
// Update last known values.
knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID();
knownIp = apActive ? IPAddress(4, 3, 2, 1) : Network.localIP();
knownBrightness = bri;
knownMode = strip.getMainSegment().mode;
knownPalette = strip.getMainSegment().palette;
knownEffectSpeed = effectSpeed;
knownEffectIntensity = effectIntensity;
// Do the actual drawing
String line;
// First row with Wifi name
drawGlyph(0, 0, 80, u8x8_font_open_iconic_embedded_1x1); // home icon
line = knownSsid.substring(0, getCols() > 1 ? getCols() - 2 : 0);
center(line, getCols()-2);
drawString(1, 0, line.c_str());
// Print `~` char to indicate that SSID is longer, than our display
if (knownSsid.length() > (int)getCols()-1) {
drawString(getCols() - 1, 0, "~");
}
// Second row with IP or Psssword
drawGlyph(0, lineHeight, 68, u8x8_font_open_iconic_embedded_1x1); // wifi icon
// Print password in AP mode and if led is OFF.
if (apActive && bri == 0) {
drawString(1, lineHeight, apPass);
} else {
// alternate IP address and server name
line = knownIp.toString();
if (showName && strcmp(serverDescription, "WLED") != 0) {
line = serverDescription;
}
center(line, getCols()-1);
drawString(1, lineHeight, line.c_str());
}
// draw third and fourth row
drawLine(2, clockMode ? lineType : FLD_LINE_MODE);
drawLine(3, clockMode ? FLD_LINE_TIME : lineType);
drawGlyph(0, 2*lineHeight, 66 + (bri > 0 ? 3 : 0), u8x8_font_open_iconic_weather_2x2); // sun/moon icon
//if (markLineNum>1) drawGlyph(2, markLineNum*lineHeight, 66, u8x8_font_open_iconic_arrow_1x1); // arrow icon
}
void drawLine(uint8_t line, Line4Type lineType) {
char lineBuffer[LINE_BUFFER_SIZE];
uint8_t printedChars;
switch(lineType) {
case FLD_LINE_BRIGHTNESS:
sprintf_P(lineBuffer, PSTR("Brightness %3d"), bri);
drawString(2, line*lineHeight, lineBuffer);
break;
case FLD_LINE_EFFECT_SPEED:
sprintf_P(lineBuffer, PSTR("FX Speed %3d"), effectSpeed);
drawString(2, line*lineHeight, lineBuffer);
break;
case FLD_LINE_EFFECT_INTENSITY:
sprintf_P(lineBuffer, PSTR("FX Intens. %3d"), effectIntensity);
drawString(2, line*lineHeight, lineBuffer);
break;
case FLD_LINE_MODE:
printedChars = extractModeName(knownMode, JSON_mode_names, lineBuffer, LINE_BUFFER_SIZE-1);
for (;printedChars < getCols()-2 && printedChars < LINE_BUFFER_SIZE-3; printedChars++) lineBuffer[printedChars]=' ';
lineBuffer[printedChars] = 0;
drawString(2, line*lineHeight, lineBuffer);
break;
case FLD_LINE_PALETTE:
printedChars = extractModeName(knownPalette, JSON_palette_names, lineBuffer, LINE_BUFFER_SIZE-1);
for (;printedChars < getCols()-2 && printedChars < LINE_BUFFER_SIZE-3; printedChars++) lineBuffer[printedChars]=' ';
lineBuffer[printedChars] = 0;
drawString(2, line*lineHeight, lineBuffer);
break;
case FLD_LINE_TIME:
default:
showTime(false);
break;
}
}
/**
* If there screen is off or in clock is displayed,
* this will return true. This allows us to throw away
* the first input from the rotary encoder but
* to wake up the screen.
*/
bool wakeDisplay() {
if (type == NONE || !enabled) return false;
knownHour = 99;
if (displayTurnedOff) {
// Turn the display back on
sleepOrClock(false);
redraw(true);
return true;
}
return false;
}
/**
* Allows you to show up to two lines as overlay for a
* period of time.
* Clears the screen and prints on the middle two lines.
*/
void overlay(const char* line1, const char *line2, long showHowLong) {
if (type == NONE || !enabled) return;
if (displayTurnedOff) {
// Turn the display back on (includes clear())
sleepOrClock(false);
} else {
clear();
}
// Print the overlay
if (line1) {
String buf = line1;
center(buf, getCols());
drawString(0, 1*lineHeight, buf.c_str());
}
if (line2) {
String buf = line2;
center(buf, getCols());
drawString(0, 2*lineHeight, buf.c_str());
}
overlayUntil = millis() + showHowLong;
}
void setLineType(byte lT) {
lineType = (Line4Type) lT;
}
/**
* Line 3 or 4 (last two lines) can be marked with an
* arrow in the first column. Pass 2 or 3 to this to
* specify which line to mark with an arrow.
* Any other values are ignored.
*/
void setMarkLine(byte newMarkLineNum) {
if (newMarkLineNum == 2 || newMarkLineNum == 3) {
markLineNum = newMarkLineNum;
}
else {
markLineNum = 0;
}
}
/**
* Enable sleep (turn the display off) or clock mode.
*/
void sleepOrClock(bool enabled) {
clear();
if (enabled) {
if (clockMode) showTime();
else setPowerSave(1);
displayTurnedOff = true;
} else {
setPowerSave(0);
displayTurnedOff = false;
}
}
/**
* Display the current date and time in large characters
* on the middle rows. Based 24 or 12 hour depending on
* the useAMPM configuration.
*/
void showTime(bool fullScreen = true) {
if (type == NONE || !enabled) return;
char lineBuffer[LINE_BUFFER_SIZE];
updateLocalTime();
byte minuteCurrent = minute(localTime);
byte hourCurrent = hour(localTime);
byte secondCurrent = second(localTime);
if (knownMinute == minuteCurrent && knownHour == hourCurrent) {
// Time hasn't changed.
if (!fullScreen) return;
}
knownMinute = minuteCurrent;
knownHour = hourCurrent;
byte currentMonth = month(localTime);
sprintf_P(lineBuffer, PSTR("%s %2d "), monthShortStr(currentMonth), day(localTime));
if (fullScreen)
draw2x2String(DATE_INDENT, lineHeight==1 ? 0 : lineHeight, lineBuffer); // adjust for 8 line displays
else
drawString(2, lineHeight*3, lineBuffer);
byte showHour = hourCurrent;
boolean isAM = false;
if (useAMPM) {
if (showHour == 0) {
showHour = 12;
isAM = true;
}
else if (showHour > 12) {
showHour -= 12;
isAM = false;
}
else {
isAM = true;
}
}
sprintf_P(lineBuffer, (secondCurrent%2 || !fullScreen) ? PSTR("%2d:%02d") : PSTR("%2d %02d"), (useAMPM ? showHour : hourCurrent), minuteCurrent);
// For time, we always use LINE_HEIGHT of 2 since
// we are printing it big.
if (fullScreen) {
draw2x2String(TIME_INDENT+2, lineHeight*2, lineBuffer);
sprintf_P(lineBuffer, PSTR("%02d"), secondCurrent);
if (useAMPM) drawString(12+(fullScreen?0:2), lineHeight*2, (isAM ? "AM" : "PM"), true);
else drawString(12, lineHeight*2+1, lineBuffer, true); // even with double sized rows print seconds in 1 line
} else {
drawString(9+(useAMPM?0:2), lineHeight*3, lineBuffer);
if (useAMPM) drawString(12+(fullScreen?0:2), lineHeight*3, (isAM ? "AM" : "PM"), true);
}
}
/*
* 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) {
//JsonObject user = root["u"];
//if (user.isNull()) user = root.createNestedObject("u");
//JsonArray data = user.createNestedArray(F("4LineDisplay"));
//data.add(F("Loaded."));
//}
/*
* 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));
top[FPSTR(_enabled)] = enabled;
JsonArray io_pin = top.createNestedArray("pin");
for (byte i=0; i<5; i++) io_pin.add(ioPin[i]);
top["help4Pins"] = F("Clk,Data,CS,DC,RST"); // help for Settings page
top["type"] = type;
top["help4Type"] = F("1=SSD1306,2=SH1106,3=SSD1306_128x64,4=SSD1305,5=SSD1305_128x64,6=SSD1306_SPI,7=SSD1306_SPI_128x64"); // help for Settings page
top[FPSTR(_flip)] = (bool) flip;
top[FPSTR(_contrast)] = contrast;
top[FPSTR(_refreshRate)] = refreshRate/1000;
top[FPSTR(_screenTimeOut)] = screenTimeout/1000;
top[FPSTR(_sleepMode)] = (bool) sleepMode;
top[FPSTR(_clockMode)] = (bool) clockMode;
top[FPSTR(_busClkFrequency)] = ioFrequency/1000;
DEBUG_PRINTLN(F("4 Line Display 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 :)
*/
bool readFromConfig(JsonObject& root) {
bool needsRedraw = false;
DisplayType newType = type;
int8_t newPin[5]; for (byte i=0; i<5; i++) newPin[i] = ioPin[i];
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;
newType = top["type"] | newType;
for (byte i=0; i<5; i++) newPin[i] = top["pin"][i] | ioPin[i];
flip = top[FPSTR(_flip)] | flip;
contrast = top[FPSTR(_contrast)] | contrast;
refreshRate = (top[FPSTR(_refreshRate)] | refreshRate/1000) * 1000;
screenTimeout = (top[FPSTR(_screenTimeOut)] | screenTimeout/1000) * 1000;
sleepMode = top[FPSTR(_sleepMode)] | sleepMode;
clockMode = top[FPSTR(_clockMode)] | clockMode;
if (newType == SSD1306_SPI || newType == SSD1306_SPI64)
ioFrequency = min(20000, max(500, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency
else
ioFrequency = min(3400, max(100, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency
DEBUG_PRINT(FPSTR(_name));
if (!initDone) {
// first run: reading from cfg.json
for (byte i=0; i<5; i++) ioPin[i] = newPin[i];
type = newType;
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<5; i++) if (ioPin[i] != newPin[i]) { pinsChanged = true; break; }
if (pinsChanged || type!=newType) {
if (type != NONE) delete u8x8;
PinOwner po = PinOwner::UM_FourLineDisplay;
bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64);
if (isSPI) {
if (ioPin[0]==spi_sclk && ioPin[1]==spi_mosi) po = PinOwner::HW_SPI; // allow multiple allocations of HW SPI bus pins
pinManager.deallocateMultiplePins((const uint8_t *)ioPin, 5, po);
} else {
if (ioPin[0]==i2c_scl && ioPin[1]==i2c_sda) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
pinManager.deallocateMultiplePins((const uint8_t *)ioPin, 2, po);
}
for (byte i=0; i<5; i++) ioPin[i] = newPin[i];
if (ioPin[0]<0 || ioPin[1]<0) { // data & clock must be > -1
type = NONE;
return true;
} else type = newType;
setup();
needsRedraw |= true;
}
if (!(type == SSD1306_SPI || type == SSD1306_SPI64)) u8x8->setBusClock(ioFrequency); // can be used for SPI too
setContrast(contrast);
setFlipMode(flip);
if (needsRedraw && !wakeDisplay()) redraw(true);
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[FPSTR(_enabled)].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_FOUR_LINE_DISP;
}
};
// strings to reduce flash memory usage (used more than twice)
const char FourLineDisplayUsermod::_name[] PROGMEM = "4LineDisplay";
const char FourLineDisplayUsermod::_enabled[] PROGMEM = "enabled";
const char FourLineDisplayUsermod::_contrast[] PROGMEM = "contrast";
const char FourLineDisplayUsermod::_refreshRate[] PROGMEM = "refreshRateSec";
const char FourLineDisplayUsermod::_screenTimeOut[] PROGMEM = "screenTimeOutSec";
const char FourLineDisplayUsermod::_flip[] PROGMEM = "flip";
const char FourLineDisplayUsermod::_sleepMode[] PROGMEM = "sleepMode";
const char FourLineDisplayUsermod::_clockMode[] PROGMEM = "clockMode";
const char FourLineDisplayUsermod::_busClkFrequency[] PROGMEM = "i2c-freq-kHz";

View File

@@ -1,4 +1,4 @@
# I2C 4 Line Display Usermod ALT
# I2C/SPI 4 Line Display Usermod ALT
Thank you to the authors of the original version of these usermods. It would not have been possible without them!
"usermod_v2_four_line_display"
@@ -8,21 +8,20 @@ The core of these usermods are a copy of the originals. The main changes are to
The display usermod UI has been completely changed.
The changes made to the RotaryEncoder usermod were made to support the new UI in the display usermod.
Without the display it, functions identical to the original.
The changes made to the RotaryEncoder usermod were made to support the new UI in the display usermod.
Without the display, it functions identical to the original.
The original "usermod_v2_auto_save" will not work with the display just yet.
Press the encoder to cycle through the options:
*Brightness
*Speed
*Intensity
*Palette
*Effect
*Main Color (only if display is used)
*Saturation (only if display is used)
* Brightness
* Speed
* Intensity
* Palette
* Effect
* Main Color (only if display is used)
* Saturation (only if display is used)
Press and hold the encoder to display Network Info
if AP is active, it will display AP, SSID and password
Press and hold the encoder to display Network Info. If AP is active, it will display AP, SSID and password
Also shows if the timer is enabled
@@ -30,11 +29,47 @@ Also shows if the timer is enabled
## Installation
Please refer to the original `usermod_v2_rotary_encoder_ui` readme for the main instructions
Then to activate this alternative usermod add `#define USE_ALT_DISPlAY` to the `usermods_list.cpp` file,
Please refer to the original `usermod_v2_rotary_encoder_ui` readme for the main instructions.
Copy the example `platformio_override.sample.ini` from the usermod_v2_rotary_encoder_ui_ALT folder to the root directory of your particular build and rename it to `platformio_override.ini`.
This file should be placed in the same directory as `platformio.ini`.
Then, to activate this alternative usermod, add `#define USE_ALT_DISPlAY` (NOTE: CASE SENSITIVE) to the `usermods_list.cpp` file,
or add `-D USE_ALT_DISPlAY` to the original `platformio_override.ini.sample` file
## Configuration
These options are configurable in Config > Usermods
### Usermod Setup
* Global I2C GPIOs (HW) - Set the SDA and SCL pins
### 4LineDisplay
* `enabled` - enable/disable usermod
* `type` - display type in numeric format
* 1 = I2C SSD1306 128x32
* 2 = I2C SH1106 128x32
* 3 = I2C SSD1306 128x64 (4 double-height lines)
* 4 = I2C SSD1305 128x32
* 5 = I2C SSD1305 128x64 (4 double-height lines)
* 6 = SPI SSD1306 128x32
* 7 = SPI SSD1306 128x64 (4 double-height lines)
* 8 = SPI SSD1309 128x64 (4 double-height lines)
* 9 = I2C SSD1309 128x64 (4 double-height lines)
* `pin` - GPIO pins used for display; SPI displays can use SCK, MOSI, CS, DC & RST
* `flip` - flip/rotate display 180°
* `contrast` - set display contrast (higher contrast may reduce display lifetime)
* `screenTimeOutSec` - screen saver time-out in seconds
* `sleepMode` - enable/disable screen saver
* `clockMode` - enable/disable clock display in screen saver mode
* `showSeconds` - Show seconds on the clock display
* `i2c-freq-kHz` - I2C clock frequency in kHz (may help reduce dropped frames, range: 400-3400)
### PlatformIO requirements
Note: the Four Line Display usermod requires the libraries `U8g2` and `Wire`.

View File

@@ -6,7 +6,7 @@
#include "4LD_wled_fonts.c"
#ifndef FLD_ESP32_NO_THREADS
#define FLD_ESP32_USE_THREADS // comment out to use 0.13.x behviour without parallel update task - slower, but more robust. May delay other tasks like LEDs or audioreactive!!
#define FLD_ESP32_USE_THREADS // comment out to use 0.13.x behaviour without parallel update task - slower, but more robust. May delay other tasks like LEDs or audioreactive!!
#endif
//
@@ -17,7 +17,7 @@
// for WLED.
//
// Dependencies
// * This Usermod works best, by far, when coupled
// * This Usermod works best, by far, when coupled
// with RotaryEncoderUI ALT Usermod.
//
// Make sure to enable NTP and set your time zone in WLED Config | Time.
@@ -89,7 +89,8 @@ typedef enum {
SSD1305_64, // U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C
SSD1306_SPI, // U8X8_SSD1306_128X32_NONAME_HW_SPI
SSD1306_SPI64, // U8X8_SSD1306_128X64_NONAME_HW_SPI
SSD1309_SPI64 // U8X8_SSD1309_128X64_NONAME0_4W_HW_SPI
SSD1309_SPI64, // U8X8_SSD1309_128X64_NONAME0_4W_HW_SPI
SSD1309_64 // U8X8_SSD1309_128X64_NONAME0_HW_I2C
} DisplayType;
@@ -211,16 +212,16 @@ class FourLineDisplayUsermod : public Usermod {
// gets called once at boot. Do all initialization that doesn't depend on
// network here
void setup();
void setup() override;
// gets called every time WiFi is (re-)connected. Initialize own network
// interfaces here
void connected();
void connected() override;
/**
* Da loop.
*/
void loop();
void loop() override;
//function to update lastredraw
inline void updateRedrawTime() { lastRedraw = millis(); }
@@ -235,7 +236,7 @@ class FourLineDisplayUsermod : public Usermod {
void updateSpeed();
void updateIntensity();
void drawStatusIcons();
/**
* marks the position of the arrow showing
* the current setting being changed
@@ -243,11 +244,11 @@ class FourLineDisplayUsermod : public Usermod {
*/
void setMarkLine(byte newMarkLineNum, byte newMarkColNum);
//Draw the arrow for the current setting beiong changed
//Draw the arrow for the current setting being changed
void drawArrow();
//Display the current effect or palette (desiredEntry)
// on the appropriate line (row).
//Display the current effect or palette (desiredEntry)
// on the appropriate line (row).
void showCurrentEffectOrPalette(int inputEffPal, const char *qstring, uint8_t row);
/**
@@ -287,60 +288,60 @@ class FourLineDisplayUsermod : public Usermod {
*/
bool handleButton(uint8_t b);
void onUpdateBegin(bool init);
void onUpdateBegin(bool init) override;
/*
* 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);
//void addToJsonInfo(JsonObject& root) override;
/*
* 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);
//void addToJsonState(JsonObject& root) override;
/*
* 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);
//void readFromJsonState(JsonObject& root) override;
void appendConfigData();
void appendConfigData() override;
/*
* 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);
void addToConfig(JsonObject& root) override;
/*
* 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 :)
*/
bool readFromConfig(JsonObject& root);
bool readFromConfig(JsonObject& root) override;
/*
* 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() {
uint16_t getId() override {
return USERMOD_ID_FOUR_LINE_DISP;
}
};
@@ -494,7 +495,7 @@ void FourLineDisplayUsermod::showTime() {
}
if (knownHour != hourCurrent) {
// only update date when hour changes
sprintf_P(lineBuffer, PSTR("%s %2d "), monthShortStr(month(localTime)), day(localTime));
sprintf_P(lineBuffer, PSTR("%s %2d "), monthShortStr(month(localTime)), day(localTime));
draw2x2String(2, lineHeight==1 ? 0 : lineHeight, lineBuffer); // adjust for 8 line displays, draw month and day
}
sprintf_P(lineBuffer,PSTR("%2d:%02d"), (useAMPM ? AmPmHour : hourCurrent), minuteCurrent);
@@ -556,6 +557,7 @@ void FourLineDisplayUsermod::setup() {
case SSD1306_64: u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_HW_I2C(); break;
case SSD1305: u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C(); break;
case SSD1305_64: u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C(); break;
case SSD1309_64: u8x8 = (U8X8 *) new U8X8_SSD1309_128X64_NONAME0_HW_I2C(); break;
// U8X8 uses global SPI variable that is attached to VSPI bus on ESP32
case SSD1306_SPI: u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_HW_SPI(ioPin[0], ioPin[1], ioPin[2]); break; // Pins are cs, dc, reset
case SSD1306_SPI64: u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_HW_SPI(ioPin[0], ioPin[1], ioPin[2]); break; // Pins are cs, dc, reset
@@ -581,7 +583,7 @@ void FourLineDisplayUsermod::setup() {
// gets called every time WiFi is (re-)connected. Initialize own network
// interfaces here
void FourLineDisplayUsermod::connected() {
knownSsid = WiFi.SSID(); //apActive ? apSSID : WiFi.SSID(); //apActive ? WiFi.softAPSSID() :
knownSsid = WiFi.SSID(); //apActive ? apSSID : WiFi.SSID(); //apActive ? WiFi.softAPSSID() :
knownIp = Network.localIP(); //apActive ? IPAddress(4, 3, 2, 1) : Network.localIP();
networkOverlay(PSTR("NETWORK INFO"),7000);
}
@@ -637,7 +639,7 @@ void FourLineDisplayUsermod::redraw(bool forceRedraw) {
powerON = !powerON;
drawStatusIcons();
return;
} else if (knownnightlight != nightlightActive) { //trigger moon icon
} else if (knownnightlight != nightlightActive) { //trigger moon icon
knownnightlight = nightlightActive;
drawStatusIcons();
if (knownnightlight) {
@@ -652,7 +654,7 @@ void FourLineDisplayUsermod::redraw(bool forceRedraw) {
return;
} else if (knownMode != effectCurrent || knownPalette != effectPalette) {
if (displayTurnedOff) needRedraw = true;
else {
else {
if (knownPalette != effectPalette) { showCurrentEffectOrPalette(effectPalette, JSON_palette_names, 2); knownPalette = effectPalette; }
if (knownMode != effectCurrent) { showCurrentEffectOrPalette(effectCurrent, JSON_mode_names, 3); knownMode = effectCurrent; }
lastRedraw = now;
@@ -703,7 +705,7 @@ void FourLineDisplayUsermod::redraw(bool forceRedraw) {
drawArrow();
drawStatusIcons();
// Second row
// Second row
updateBrightness();
updateSpeed();
updateIntensity();
@@ -793,7 +795,7 @@ void FourLineDisplayUsermod::setMarkLine(byte newMarkLineNum, byte newMarkColNum
markColNum = newMarkColNum;
}
//Draw the arrow for the current setting beiong changed
//Draw the arrow for the current setting being changed
void FourLineDisplayUsermod::drawArrow() {
#if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS)
unsigned long now = millis();
@@ -805,8 +807,8 @@ void FourLineDisplayUsermod::drawArrow() {
lockRedraw = false;
}
//Display the current effect or palette (desiredEntry)
// on the appropriate line (row).
//Display the current effect or palette (desiredEntry)
// on the appropriate line (row).
void FourLineDisplayUsermod::showCurrentEffectOrPalette(int inputEffPal, const char *qstring, uint8_t row) {
#if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS)
unsigned long now = millis();
@@ -857,7 +859,7 @@ void FourLineDisplayUsermod::showCurrentEffectOrPalette(int inputEffPal, const c
while (smallChars1 < (MAX_MODE_LINE_SPACE-1)) smallBuffer1[smallChars1++]=' ';
smallBuffer1[smallChars1] = 0;
drawString(1, row*lineHeight, smallBuffer1, true);
while (smallChars2 < (MAX_MODE_LINE_SPACE-1)) smallBuffer2[smallChars2++]=' ';
while (smallChars2 < (MAX_MODE_LINE_SPACE-1)) smallBuffer2[smallChars2++]=' ';
smallBuffer2[smallChars2] = 0;
drawString(1, row*lineHeight+1, smallBuffer2, true);
}
@@ -1066,7 +1068,7 @@ void FourLineDisplayUsermod::networkOverlay(const char* line1, long showHowLong)
bool FourLineDisplayUsermod::handleButton(uint8_t b) {
yield();
if (!enabled
|| b // butto 0 only
|| b // button 0 only
|| buttonType[b] == BTN_TYPE_SWITCH
|| buttonType[b] == BTN_TYPE_NONE
|| buttonType[b] == BTN_TYPE_RESERVED
@@ -1150,7 +1152,7 @@ void FourLineDisplayUsermod::onUpdateBegin(bool init) {
xTaskCreatePinnedToCore(
[](void * par) { // Function to implement the task
// see https://www.freertos.org/vtaskdelayuntil.html
const TickType_t xFrequency = REFRESH_RATE_MS * portTICK_PERIOD_MS / 2;
const TickType_t xFrequency = REFRESH_RATE_MS * portTICK_PERIOD_MS / 2;
TickType_t xLastWakeTime = xTaskGetTickCount();
for(;;) {
delay(1); // DO NOT DELETE THIS LINE! It is needed to give the IDLE(0) task enough time and to keep the watchdog happy.
@@ -1205,6 +1207,7 @@ void FourLineDisplayUsermod::appendConfigData() {
oappend(SET_F("addOption(dd,'SSD1306 128x64',3);"));
oappend(SET_F("addOption(dd,'SSD1305',4);"));
oappend(SET_F("addOption(dd,'SSD1305 128x64',5);"));
oappend(SET_F("addOption(dd,'SSD1309 128x64',9);"));
oappend(SET_F("addOption(dd,'SSD1306 SPI',6);"));
oappend(SET_F("addOption(dd,'SSD1306 SPI 128x64',7);"));
oappend(SET_F("addOption(dd,'SSD1309 SPI 128x64',8);"));
@@ -1218,14 +1221,14 @@ void FourLineDisplayUsermod::appendConfigData() {
* 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 FourLineDisplayUsermod::addToConfig(JsonObject& root) {
@@ -1252,7 +1255,7 @@ void FourLineDisplayUsermod::addToConfig(JsonObject& root) {
/*
* 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 :)
@@ -1346,6 +1349,10 @@ bool FourLineDisplayUsermod::readFromConfig(JsonObject& root) {
u8x8_Setup(u8x8->getU8x8(), u8x8_d_ssd1305_128x64_adafruit, u8x8_cad_ssd13xx_fast_i2c, u8x8_byte_arduino_hw_i2c, u8x8_gpio_and_delay_arduino);
u8x8_SetPin_HW_I2C(u8x8->getU8x8(), U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE);
break;
case SSD1309_64:
u8x8_Setup(u8x8->getU8x8(), u8x8_d_ssd1309_128x64_noname0, u8x8_cad_ssd13xx_fast_i2c, u8x8_byte_arduino_hw_i2c, u8x8_gpio_and_delay_arduino);
u8x8_SetPin_HW_I2C(u8x8->getU8x8(), U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE);
break;
case SSD1306_SPI:
u8x8_Setup(u8x8->getU8x8(), u8x8_d_ssd1306_128x32_univision, u8x8_cad_001, u8x8_byte_arduino_hw_spi, u8x8_gpio_and_delay_arduino);
u8x8_SetPin_4Wire_HW_SPI(u8x8->getU8x8(), ioPin[0], ioPin[1], ioPin[2]); // Pins are cs, dc, reset

View File

@@ -10,7 +10,7 @@ curl --location --request GET 'http://[]/printer/objects/query?virtual_sdcard=pr
## Usage
Compile the source with the buildflag `-D USERMOD_KLIPPER_PERCENTAGE` added.
You can also use the WLBD bot in the Discord by simply extending an exsisting build enviroment:
You can also use the WLBD bot in the Discord by simply extending an existing build environment:
```
[env:esp32klipper]
extends = env:esp32dev
@@ -23,7 +23,7 @@ build_flags = ${common.build_flags_esp32} -D USERMOD_KLIPPER_PERCENTAGE
Checkbox to enable or disable the overlay
### Klipper IP:
IP adress of your Klipper instance you want to poll. ESP has to be restarted after change
IP address of your Klipper instance you want to poll. ESP has to be restarted after change
### Direction :
0 = normal

View File

@@ -6,7 +6,7 @@ class klipper_percentage : public Usermod
{
private:
unsigned long lastTime = 0;
String ip = "192.168.25.207";
String ip = F("0.0.0.0");
WiFiClient wifiClient;
char errorMessage[100] = "";
int printPercent = 0;
@@ -30,7 +30,7 @@ private:
{
// Send HTTP request
client.println(F("GET /printer/objects/query?virtual_sdcard=progress HTTP/1.0"));
client.println("Host: " + ip);
client.print(F("Host: ")); client.println(ip);
client.println(F("Connection: close"));
if (client.println() == 0)
{
@@ -41,7 +41,7 @@ private:
// Check HTTP status
char status[32] = {0};
client.readBytesUntil('\r', status, sizeof(status));
if (strcmp(status, "HTTP/1.1 200 OK") != 0)
if (strcmp_P(status, PSTR("HTTP/1.1 200 OK")) != 0)
{
strcat(errorMessage, PSTR("Unexpected response: "));
strcat(errorMessage, status);
@@ -79,18 +79,18 @@ public:
httpGet(wifiClient, errorMessage);
if (strcmp(errorMessage, "") == 0)
{
PSRAMDynamicJsonDocument klipperDoc(4096); // in practive about 2673
PSRAMDynamicJsonDocument klipperDoc(4096); // in practice about 2673
DeserializationError error = deserializeJson(klipperDoc, wifiClient);
if (error)
{
strcat(errorMessage, PSTR("deserializeJson() failed: "));
strcat(errorMessage, error.c_str());
}
printPercent = (int)(klipperDoc["result"]["status"]["virtual_sdcard"]["progress"].as<float>() * 100);
printPercent = (int)(klipperDoc[F("result")][F("status")][F("virtual_sdcard")][F("progress")].as<float>() * 100);
DEBUG_PRINT("Percent: ");
DEBUG_PRINTLN((int)(klipperDoc["result"]["status"]["virtual_sdcard"]["progress"].as<float>() * 100));
DEBUG_PRINT("LEDs: ");
DEBUG_PRINT(F("Percent: "));
DEBUG_PRINTLN((int)(klipperDoc[F("result")][F("status")][F("virtual_sdcard")][F("progress")].as<float>() * 100));
DEBUG_PRINT(F("LEDs: "));
DEBUG_PRINTLN(direction == 2 ? (strip.getLengthTotal() / 2) * printPercent / 100 : strip.getLengthTotal() * printPercent / 100);
}
else
@@ -106,10 +106,10 @@ public:
void addToConfig(JsonObject &root)
{
JsonObject top = root.createNestedObject("Klipper Printing Percentage");
top["Enabled"] = enabled;
top["Klipper IP"] = ip;
top["Direction"] = direction;
JsonObject top = root.createNestedObject(F("Klipper Printing Percentage"));
top[F("Enabled")] = enabled;
top[F("Klipper IP")] = ip;
top[F("Direction")] = direction;
}
bool readFromConfig(JsonObject &root)
@@ -117,12 +117,12 @@ public:
// 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["Klipper Printing Percentage"];
JsonObject top = root[F("Klipper Printing Percentage")];
bool configComplete = !top.isNull();
configComplete &= getJsonValue(top["Klipper IP"], ip);
configComplete &= getJsonValue(top["Enabled"], enabled);
configComplete &= getJsonValue(top["Direction"], direction);
configComplete &= getJsonValue(top[F("Klipper IP")], ip);
configComplete &= getJsonValue(top[F("Enabled")], enabled);
configComplete &= getJsonValue(top[F("Direction")], direction);
return configComplete;
}

View File

@@ -1,33 +0,0 @@
# Mode Sort
v2 usermod that provides data about modes and
palettes to other usermods. Notably it provides:
* A direct method for a mode or palette name
* Ability to retrieve mode and palette names in
alphabetical order
```char **getModesQStrings()```
Provides a char* array (pointers) to the names of the
palettes contained in JSON_mode_names, in the same order as
JSON_mode_names. These strings end in double quote (")
(or \0 if there is a problem).
```byte *getModesAlphaIndexes()```
A byte array designating the indexes of names of the
modes in alphabetical order. "Solid" will always remain
at the top of the list.
```char **getPalettesQStrings()```
Provides a char* array (pointers) to the names of the
palettes contained in JSON_palette_names, in the same order as
JSON_palette_names. These strings end in double quote (")
(or \0 if there is a problem).
```byte *getPalettesAlphaIndexes()```
A byte array designating the indexes of names of the
palettes in alphabetical order. "Default" and those
starting with "(" will always remain at the top of the list.

View File

@@ -1,244 +0,0 @@
#pragma once
#include "wled.h"
//
// v2 usermod that provides data about modes and
// palettes to other usermods. Notably it provides:
// * A direct method for a mode or palette name
// * Ability to retrieve mode and palette names in
// alphabetical order
//
// char **getModesQStrings()
// Provides an array of char* (pointers) to the names of the
// palettes within JSON_mode_names, in the same order as
// JSON_mode_names. These strings end in double quote (")
// (or \0 if there is a problem).
//
// byte *getModesAlphaIndexes()
// An array of byte designating the indexes of names of the
// modes in alphabetical order. "Solid" will always remain
// at the front of the list.
//
// char **getPalettesQStrings()
// Provides an array of char* (pointers) to the names of the
// palettes within JSON_palette_names, in the same order as
// JSON_palette_names. These strings end in double quote (")
// (or \0 if there is a problem).
//
// byte *getPalettesAlphaIndexes()
// An array of byte designating the indexes of names of the
// palettes in alphabetical order. "Default" and those
// starting with "(" will always remain at the front of the list.
//
// Number of modes at the start of the list to not sort
#define MODE_SORT_SKIP_COUNT 1
// Which list is being sorted
char **listBeingSorted = nullptr;
/**
* Modes and palettes are stored as strings that
* end in a quote character. Compare two of them.
* We are comparing directly within either
* JSON_mode_names or JSON_palette_names.
*/
int re_qstringCmp(const void *ap, const void *bp) {
char *a = listBeingSorted[*((byte *)ap)];
char *b = listBeingSorted[*((byte *)bp)];
int i = 0;
do {
char aVal = pgm_read_byte_near(a + i);
if (aVal >= 97 && aVal <= 122) {
// Lowercase
aVal -= 32;
}
char bVal = pgm_read_byte_near(b + i);
if (bVal >= 97 && bVal <= 122) {
// Lowercase
bVal -= 32;
}
// Relly we shouldn't ever get to '\0'
if (aVal == '"' || bVal == '"' || aVal == '\0' || bVal == '\0') {
// We're done. one is a substring of the other
// or something happenend and the quote didn't stop us.
if (aVal == bVal) {
// Same value, probably shouldn't happen
// with this dataset
return 0;
}
else if (aVal == '"' || aVal == '\0') {
return -1;
}
else {
return 1;
}
}
if (aVal == bVal) {
// Same characters. Move to the next.
i++;
continue;
}
// We're done
if (aVal < bVal) {
return -1;
}
else {
return 1;
}
} while (true);
// We shouldn't get here.
return 0;
}
class ModeSortUsermod : public Usermod {
private:
// Pointers the start of the mode names within JSON_mode_names
char **modes_qstrings = nullptr;
// Array of mode indexes in alphabetical order.
byte *modes_alpha_indexes = nullptr;
// Pointers the start of the palette names within JSON_palette_names
char **palettes_qstrings = nullptr;
// Array of palette indexes in alphabetical order.
byte *palettes_alpha_indexes = nullptr;
public:
/**
* 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() {
// Sort the modes and palettes on startup
// as they are guarantted to change.
sortModesAndPalettes();
}
char **getModesQStrings() {
return modes_qstrings;
}
byte *getModesAlphaIndexes() {
return modes_alpha_indexes;
}
char **getPalettesQStrings() {
return palettes_qstrings;
}
byte *getPalettesAlphaIndexes() {
return palettes_alpha_indexes;
}
/**
* This Usermod doesn't have anything for loop.
*/
void loop() {}
/**
* Sort the modes and palettes to the index arrays
* modes_alpha_indexes and palettes_alpha_indexes.
*/
void sortModesAndPalettes() {
modes_qstrings = re_findModeStrings(JSON_mode_names, strip.getModeCount());
modes_alpha_indexes = re_initIndexArray(strip.getModeCount());
re_sortModes(modes_qstrings, modes_alpha_indexes, strip.getModeCount(), MODE_SORT_SKIP_COUNT);
palettes_qstrings = re_findModeStrings(JSON_palette_names, strip.getPaletteCount());
palettes_alpha_indexes = re_initIndexArray(strip.getPaletteCount());
int skipPaletteCount = 1;
while (true) {
// How many palette names start with '*' and should not be sorted?
// (Also skipping the first one, 'Default').
if (pgm_read_byte_near(palettes_qstrings[skipPaletteCount]) == '*') {
skipPaletteCount++;
}
else {
break;
}
}
re_sortModes(palettes_qstrings, palettes_alpha_indexes, strip.getPaletteCount(), skipPaletteCount);
}
byte *re_initIndexArray(int numModes) {
byte *indexes = (byte *)malloc(sizeof(byte) * numModes);
for (byte i = 0; i < numModes; i++) {
indexes[i] = i;
}
return indexes;
}
/**
* Return an array of mode or palette names from the JSON string.
* They don't end in '\0', they end in '"'.
*/
char **re_findModeStrings(const char json[], int numModes) {
char **modeStrings = (char **)malloc(sizeof(char *) * numModes);
uint8_t modeIndex = 0;
bool insideQuotes = false;
// advance past the mark for markLineNum that may exist.
char singleJsonSymbol;
// Find the mode name in JSON
bool complete = false;
for (size_t i = 0; i < strlen_P(json); i++) {
singleJsonSymbol = pgm_read_byte_near(json + i);
if (singleJsonSymbol == '\0') break;
switch (singleJsonSymbol) {
case '"':
insideQuotes = !insideQuotes;
if (insideQuotes) {
// We have a new mode or palette
modeStrings[modeIndex] = (char *)(json + i + 1);
}
break;
case '[':
break;
case ']':
if (!insideQuotes) complete = true;
break;
case ',':
if (!insideQuotes) modeIndex++;
default:
if (!insideQuotes) break;
}
if (complete) break;
}
return modeStrings;
}
/**
* Sort either the modes or the palettes using quicksort.
*/
void re_sortModes(char **modeNames, byte *indexes, int count, int numSkip) {
listBeingSorted = modeNames;
qsort(indexes + numSkip, count - numSkip, sizeof(byte), re_qstringCmp);
listBeingSorted = nullptr;
}
/*
* 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) {}
/*
* 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_MODE_SORT;
}
};

View File

@@ -7,4 +7,4 @@ Contains a modification to use WLED in combination with the Ping Pong Ball LED C
To install this Usermod, you instruct PlatformIO to compile the Project with the USERMOD_PING_PONG_CLOCK flag.
WLED then automatically provides you with various settings on the Usermod Page.
Note: Depending on the size of your clock, you may have to update the led indices for the indivdual numbers and the base indices.
Note: Depending on the size of your clock, you may have to update the led indices for the individual numbers and the base indices.

View File

@@ -18,15 +18,15 @@ private:
// ---- Variables for correct LED numbering below, edit only if your clock is built different ----
int baseH = 43; // Adress for the one place of the hours
int baseHH = 7; // Adress for the tens place of the hours
int baseM = 133; // Adress for the one place of the minutes
int baseMM = 97; // Adress for the tens place of the minutes
int colon1 = 79; // Adress for the first colon led
int colon2 = 80; // Adress for the second colon led
int baseH = 43; // Address for the one place of the hours
int baseHH = 7; // Address for the tens place of the hours
int baseM = 133; // Address for the one place of the minutes
int baseMM = 97; // Address for the tens place of the minutes
int colon1 = 79; // Address for the first colon led
int colon2 = 80; // Address for the second colon led
// Matrix for the illumination of the numbers
// Note: These only define the increments of the base adress. e.g. to define the second Minute you have to add the baseMM to every led position
// Note: These only define the increments of the base address. e.g. to define the second Minute you have to add the baseMM to every led position
const int numbers[10][10] =
{
{ 0, 1, 4, 6, 13, 15, 18, 19, -1, -1 }, // 0: null

View File

@@ -1,48 +0,0 @@
[platformio]
default_envs = d1_mini
; default_envs = esp32dev
[env:esp32dev]
board = esp32dev
platform = espressif32@3.2
build_unflags = ${common.build_unflags}
build_flags =
${common.build_flags_esp32}
-D USERMOD_MODE_SORT
-D USERMOD_FOUR_LINE_DISPLAY -D FLD_PIN_SCL=22 -D FLD_PIN_SDA=21
-D USERMOD_ROTARY_ENCODER_UI -D ENCODER_DT_PIN=18 -D ENCODER_CLK_PIN=5 -D ENCODER_SW_PIN=19
-D USERMOD_AUTO_SAVE -D AUTOSAVE_PRESET_NUM=1
-D LEDPIN=16 -D BTNPIN=13
upload_speed = 460800
lib_ignore =
ESPAsyncTCP
ESPAsyncUDP
[env:d1_mini]
board = d1_mini
platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
upload_speed = 460800
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags =
${common.build_flags_esp8266}
-D USERMOD_MODE_SORT
-D USERMOD_FOUR_LINE_DISPLAY -D FLD_PIN_SCL=5 -D FLD_PIN_SDA=4
-D USERMOD_ROTARY_ENCODER_UI -D ENCODER_DT_PIN=12 -D ENCODER_CLK_PIN=14 -D ENCODER_SW_PIN=13
-D USERMOD_AUTO_SAVE -D AUTOSAVE_PRESET_NUM=1
-D LEDPIN=3 -D BTNPIN=0
monitor_filters = esp8266_exception_decoder
[env]
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.0
U8g2@~2.27.2
Wire

View File

@@ -1,39 +0,0 @@
# Rotary Encoder UI Usermod
First, thanks to the authors of other Rotary Encoder usermods.
This usermod starts to provide a relatively complete on-device
UI when paired with the Four Line Display usermod. I strongly
encourage you to try them together.
[See the pair of usermods in action](https://www.youtube.com/watch?v=tITQY80rIOA)
## Installation
Copy and update the example `platformio_override.ini.sample` to the root directory of your particular build.
This file should be placed in the same directory as `platformio.ini`.
### Define Your Options
* `USERMOD_ROTARY_ENCODER_UI` - define this to have this user mod included wled00\usermods_list.cpp
* `USERMOD_ROTARY_ENCODER_GPIO` - define the GPIO function (INPUT, INPUT_PULLUP, etc...)
* `USERMOD_FOUR_LINE_DISPLAY` - define this to have this the Four Line Display mod included wled00\usermods_list.cpp
also tells this usermod that the display is available
(see the Four Line Display usermod `readme.md` for more details)
* `ENCODER_DT_PIN` &nbsp;&nbsp;- defaults to 12
* `ENCODER_CLK_PIN` - defaults to 14
* `ENCODER_SW_PIN` &nbsp;&nbsp;- defaults to 13
* `USERMOD_ROTARY_ENCODER_GPIO` - GPIO functionality:
`INPUT_PULLUP` to use internal pull-up
`INPUT` to use pull-up on the PCB
### PlatformIO requirements
No special requirements.
Note: the Four Line Display usermod requires the libraries `U8g2` and `Wire`.
## Change Log
2021-02
* First public release

View File

@@ -1,496 +0,0 @@
#pragma once
#include "wled.h"
//
// Inspired by the v1 usermods
// * rotary_encoder_change_brightness
// * rotary_encoder_change_effect
//
// v2 usermod that provides a rotary encoder-based UI.
//
// This usermod allows you to control:
//
// * Brightness
// * Selected Effect
// * Effect Speed
// * Effect Intensity
// * Palette
//
// Change between modes by pressing a button.
//
// Dependencies
// * This usermod REQURES the ModeSortUsermod
// * This Usermod works best coupled with
// FourLineDisplayUsermod.
//
#ifndef ENCODER_DT_PIN
#define ENCODER_DT_PIN 12
#endif
#ifndef ENCODER_CLK_PIN
#define ENCODER_CLK_PIN 14
#endif
#ifndef ENCODER_SW_PIN
#define ENCODER_SW_PIN 13
#endif
#ifndef USERMOD_FOUR_LINE_DISPLAY
// These constants won't be defined if we aren't using FourLineDisplay.
#define FLD_LINE_BRIGHTNESS 0
#define FLD_LINE_MODE 0
#define FLD_LINE_EFFECT_SPEED 0
#define FLD_LINE_EFFECT_INTENSITY 0
#define FLD_LINE_PALETTE 0
#endif
// The last UI state
#define LAST_UI_STATE 4
class RotaryEncoderUIUsermod : public Usermod {
private:
int fadeAmount = 10; // Amount to change every step (brightness)
unsigned long currentTime;
unsigned long loopTime;
int8_t pinA = ENCODER_DT_PIN; // DT from encoder
int8_t pinB = ENCODER_CLK_PIN; // CLK from encoder
int8_t pinC = ENCODER_SW_PIN; // SW from encoder
unsigned char select_state = 0; // 0: brightness, 1: effect, 2: effect speed
unsigned char button_state = HIGH;
unsigned char prev_button_state = HIGH;
#ifdef USERMOD_FOUR_LINE_DISPLAY
FourLineDisplayUsermod *display;
#else
void* display = nullptr;
#endif
byte *modes_alpha_indexes = nullptr;
byte *palettes_alpha_indexes = nullptr;
unsigned char Enc_A;
unsigned char Enc_B;
unsigned char Enc_A_prev = 0;
bool currentEffectAndPaletteInitialized = false;
uint8_t effectCurrentIndex = 0;
uint8_t effectPaletteIndex = 0;
bool initDone = false;
bool enabled = true;
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
static const char _enabled[];
static const char _DT_pin[];
static const char _CLK_pin[];
static const char _SW_pin[];
public:
/*
* 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()
{
DEBUG_PRINTLN(F("Usermod Rotary Encoder init."));
PinManagerPinType pins[3] = { { pinA, false }, { pinB, false }, { pinC, false } };
if (!pinManager.allocateMultiplePins(pins, 3, PinOwner::UM_RotaryEncoderUI)) {
// BUG: configuring this usermod with conflicting pins
// will cause it to de-allocate pins it does not own
// (at second config)
// This is the exact type of bug solved by pinManager
// tracking the owner tags....
pinA = pinB = pinC = -1;
enabled = false;
return;
}
#ifndef USERMOD_ROTARY_ENCODER_GPIO
#define USERMOD_ROTARY_ENCODER_GPIO INPUT_PULLUP
#endif
pinMode(pinA, USERMOD_ROTARY_ENCODER_GPIO);
pinMode(pinB, USERMOD_ROTARY_ENCODER_GPIO);
pinMode(pinC, USERMOD_ROTARY_ENCODER_GPIO);
currentTime = millis();
loopTime = currentTime;
ModeSortUsermod *modeSortUsermod = (ModeSortUsermod*) usermods.lookup(USERMOD_ID_MODE_SORT);
modes_alpha_indexes = modeSortUsermod->getModesAlphaIndexes();
palettes_alpha_indexes = modeSortUsermod->getPalettesAlphaIndexes();
#ifdef USERMOD_FOUR_LINE_DISPLAY
// This Usermod uses FourLineDisplayUsermod for the best experience.
// But it's optional. But you want it.
display = (FourLineDisplayUsermod*) usermods.lookup(USERMOD_ID_FOUR_LINE_DISP);
if (display != nullptr) {
display->setLineType(FLD_LINE_BRIGHTNESS);
display->setMarkLine(3);
}
#endif
initDone = true;
}
/*
* connected() is called every time the WiFi is (re)connected
* Use it to initialize network interfaces
*/
void connected()
{
//Serial.println("Connected to WiFi!");
}
/*
* loop() is called continuously. Here you can check for events, read sensors, etc.
*
* Tips:
* 1. You can use "if (WLED_CONNECTED)" to check for a successful network connection.
* Additionally, "if (WLED_MQTT_CONNECTED)" is available to check for a connection to an MQTT broker.
*
* 2. Try to avoid using the delay() function. NEVER use delays longer than 10 milliseconds.
* Instead, use a timer check as shown here.
*/
void loop()
{
if (!enabled) return;
currentTime = millis(); // get the current elapsed time
// Initialize effectCurrentIndex and effectPaletteIndex to
// current state. We do it here as (at least) effectCurrent
// is not yet initialized when setup is called.
if (!currentEffectAndPaletteInitialized) {
findCurrentEffectAndPalette();
}
if (currentTime >= (loopTime + 2)) // 2ms since last check of encoder = 500Hz
{
button_state = digitalRead(pinC);
if (prev_button_state != button_state)
{
if (button_state == LOW)
{
prev_button_state = button_state;
char newState = select_state + 1;
if (newState > LAST_UI_STATE) newState = 0;
bool changedState = true;
if (display != nullptr) {
switch(newState) {
case 0:
changedState = changeState("Brightness", FLD_LINE_BRIGHTNESS, 3);
break;
case 1:
changedState = changeState("Select FX", FLD_LINE_MODE, 2);
break;
case 2:
changedState = changeState("FX Speed", FLD_LINE_EFFECT_SPEED, 3);
break;
case 3:
changedState = changeState("FX Intensity", FLD_LINE_EFFECT_INTENSITY, 3);
break;
case 4:
changedState = changeState("Palette", FLD_LINE_PALETTE, 3);
break;
}
}
if (changedState) {
select_state = newState;
}
}
else
{
prev_button_state = button_state;
}
}
int Enc_A = digitalRead(pinA); // Read encoder pins
int Enc_B = digitalRead(pinB);
if ((!Enc_A) && (Enc_A_prev))
{ // A has gone from high to low
if (Enc_B == HIGH)
{ // B is high so clockwise
switch(select_state) {
case 0:
changeBrightness(true);
break;
case 1:
changeEffect(true);
break;
case 2:
changeEffectSpeed(true);
break;
case 3:
changeEffectIntensity(true);
break;
case 4:
changePalette(true);
break;
}
}
else if (Enc_B == LOW)
{ // B is low so counter-clockwise
switch(select_state) {
case 0:
changeBrightness(false);
break;
case 1:
changeEffect(false);
break;
case 2:
changeEffectSpeed(false);
break;
case 3:
changeEffectIntensity(false);
break;
case 4:
changePalette(false);
break;
}
}
}
Enc_A_prev = Enc_A; // Store value of A for next time
loopTime = currentTime; // Updates loopTime
}
}
void findCurrentEffectAndPalette() {
currentEffectAndPaletteInitialized = true;
for (uint8_t i = 0; i < strip.getModeCount(); i++) {
//byte value = modes_alpha_indexes[i];
if (modes_alpha_indexes[i] == effectCurrent) {
effectCurrentIndex = i;
break;
}
}
for (uint8_t i = 0; i < strip.getPaletteCount(); i++) {
//byte value = palettes_alpha_indexes[i];
if (palettes_alpha_indexes[i] == strip.getSegment(0).palette) {
effectPaletteIndex = i;
break;
}
}
}
boolean changeState(const char *stateName, byte lineThreeMode, byte markedLine) {
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display != nullptr) {
if (display->wakeDisplay()) {
// Throw away wake up input
return false;
}
display->overlay("Mode change", stateName, 1500);
display->setLineType(lineThreeMode);
display->setMarkLine(markedLine);
}
#endif
return true;
}
void lampUdated() {
colorUpdated(CALL_MODE_BUTTON);
updateInterfaces(CALL_MODE_BUTTON);
}
void changeBrightness(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
// Throw away wake up input
return;
}
#endif
if (increase) {
bri = (bri + fadeAmount <= 255) ? (bri + fadeAmount) : 255;
}
else {
bri = (bri - fadeAmount >= 0) ? (bri - fadeAmount) : 0;
}
lampUdated();
}
void changeEffect(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
// Throw away wake up input
return;
}
#endif
if (increase) {
effectCurrentIndex = (effectCurrentIndex + 1 >= strip.getModeCount()) ? 0 : (effectCurrentIndex + 1);
}
else {
effectCurrentIndex = (effectCurrentIndex - 1 < 0) ? (strip.getModeCount() - 1) : (effectCurrentIndex - 1);
}
effectCurrent = modes_alpha_indexes[effectCurrentIndex];
lampUdated();
}
void changeEffectSpeed(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
// Throw away wake up input
return;
}
#endif
if (increase) {
effectSpeed = (effectSpeed + fadeAmount <= 255) ? (effectSpeed + fadeAmount) : 255;
}
else {
effectSpeed = (effectSpeed - fadeAmount >= 0) ? (effectSpeed - fadeAmount) : 0;
}
lampUdated();
}
void changeEffectIntensity(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
// Throw away wake up input
return;
}
#endif
if (increase) {
effectIntensity = (effectIntensity + fadeAmount <= 255) ? (effectIntensity + fadeAmount) : 255;
}
else {
effectIntensity = (effectIntensity - fadeAmount >= 0) ? (effectIntensity - fadeAmount) : 0;
}
lampUdated();
}
void changePalette(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
// Throw away wake up input
return;
}
#endif
if (increase) {
effectPaletteIndex = (effectPaletteIndex + 1 >= strip.getPaletteCount()) ? 0 : (effectPaletteIndex + 1);
}
else {
effectPaletteIndex = (effectPaletteIndex - 1 < 0) ? (strip.getPaletteCount() - 1) : (effectPaletteIndex - 1);
}
effectPalette = palettes_alpha_indexes[effectPaletteIndex];
lampUdated();
}
/*
* 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() (called from set.cpp) stores persistent properties to cfg.json
*/
void addToConfig(JsonObject &root) {
// we add JSON object: {"Rotary-Encoder":{"DT-pin":12,"CLK-pin":14,"SW-pin":13}}
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
top[FPSTR(_enabled)] = enabled;
top[FPSTR(_DT_pin)] = pinA;
top[FPSTR(_CLK_pin)] = pinB;
top[FPSTR(_SW_pin)] = pinC;
DEBUG_PRINTLN(F("Rotary Encoder 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: {"Rotary-Encoder":{"DT-pin":12,"CLK-pin":14,"SW-pin":13}}
JsonObject top = root[FPSTR(_name)];
if (top.isNull()) {
DEBUG_PRINT(FPSTR(_name));
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false;
}
int8_t newDTpin = top[FPSTR(_DT_pin)] | pinA;
int8_t newCLKpin = top[FPSTR(_CLK_pin)] | pinB;
int8_t newSWpin = top[FPSTR(_SW_pin)] | pinC;
enabled = top[FPSTR(_enabled)] | enabled;
DEBUG_PRINT(FPSTR(_name));
if (!initDone) {
// first run: reading from cfg.json
pinA = newDTpin;
pinB = newCLKpin;
pinC = newSWpin;
DEBUG_PRINTLN(F(" config loaded."));
} else {
DEBUG_PRINTLN(F(" config (re)loaded."));
// changing parameters from settings page
if (pinA!=newDTpin || pinB!=newCLKpin || pinC!=newSWpin) {
pinManager.deallocatePin(pinA, PinOwner::UM_RotaryEncoderUI);
pinManager.deallocatePin(pinB, PinOwner::UM_RotaryEncoderUI);
pinManager.deallocatePin(pinC, PinOwner::UM_RotaryEncoderUI);
pinA = newDTpin;
pinB = newCLKpin;
pinC = newSWpin;
if (pinA<0 || pinB<0 || pinC<0) {
enabled = false;
return true;
}
setup();
}
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[FPSTR(_enabled)].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_ROTARY_ENC_UI;
}
};
// strings to reduce flash memory usage (used more than twice)
const char RotaryEncoderUIUsermod::_name[] PROGMEM = "Rotary-Encoder";
const char RotaryEncoderUIUsermod::_enabled[] PROGMEM = "enabled";
const char RotaryEncoderUIUsermod::_DT_pin[] PROGMEM = "DT-pin";
const char RotaryEncoderUIUsermod::_CLK_pin[] PROGMEM = "CLK-pin";
const char RotaryEncoderUIUsermod::_SW_pin[] PROGMEM = "SW-pin";

View File

@@ -0,0 +1,17 @@
[platformio]
default_envs = esp32dev
[env:esp32dev]
board = esp32dev
platform = ${esp32.platform}
build_unflags = ${common.build_unflags}
build_flags =
${common.build_flags_esp32}
-D USERMOD_FOUR_LINE_DISPLAY -D USE_ALT_DISPlAY
-D USERMOD_ROTARY_ENCODER_UI -D ENCODER_DT_PIN=18 -D ENCODER_CLK_PIN=5 -D ENCODER_SW_PIN=19
upload_speed = 460800
lib_deps =
${esp32.lib_deps}
U8g2@~2.34.4
Wire

View File

@@ -8,18 +8,18 @@ The core of these usermods are a copy of the originals. The main changes are to
The display usermod UI has been completely changed.
The changes made to the RotaryEncoder usermod were made to support the new UI in the display usermod.
The changes made to the RotaryEncoder usermod were made to support the new UI in the display usermod.
Without the display, it functions identical to the original.
The original "usermod_v2_auto_save" will not work with the display just yet.
Press the encoder to cycle through the options:
*Brightness
*Speed
*Intensity
*Palette
*Effect
*Main Color (only if display is used)
*Saturation (only if display is used)
* Brightness
* Speed
* Intensity
* Palette
* Effect
* Main Color (only if display is used)
* Saturation (only if display is used)
Press and hold the encoder to display Network Info
if AP is active, it will display the AP, SSID and Password
@@ -30,10 +30,23 @@ Also shows if the timer is enabled.
## Installation
Please refer to the original `usermod_v2_rotary_encoder_ui` readme for the main instructions.<br/>
To activate this alternative usermod, add `#define USE_ALT_DISPlAY` to the `usermods_list.cpp` file,
or add `-D USE_ALT_DISPlAY` to the original `platformio_override.ini.sample` file.
Copy the example `platformio_override.sample.ini` to the root directory of your particular build and rename it to `platformio_override.ini`.
To activate this alternative usermod, add `#define USE_ALT_DISPlAY` (NOTE: CASE SENSITIVE) to the `usermods_list.cpp` file, or add `-D USE_ALT_DISPlAY` to your `platformio_override.ini` file
### Define Your Options
* `USERMOD_ROTARY_ENCODER_UI` - define this to have this user mod included wled00\usermods_list.cpp
* `USERMOD_FOUR_LINE_DISPLAY` - define this to have this the Four Line Display mod included wled00\usermods_list.cpp
also tells this usermod that the display is available
(see the Four Line Display usermod `readme.md` for more details)
* `USE_ALT_DISPlAY` - Mandatory to use Four Line Display
* `ENCODER_DT_PIN` - defaults to 18
* `ENCODER_CLK_PIN` - defaults to 5
* `ENCODER_SW_PIN` - defaults to 19
* `USERMOD_ROTARY_ENCODER_GPIO` - GPIO functionality:
`INPUT_PULLUP` to use internal pull-up
`INPUT` to use pull-up on the PCB
### PlatformIO requirements

View File

@@ -4,7 +4,7 @@
//
// Inspired by the original v2 usermods
// * usermod_v2_rotaty_encoder_ui
// * usermod_v2_rotary_encoder_ui
//
// v2 usermod that provides a rotary encoder-based UI.
//
@@ -99,7 +99,7 @@ static int re_qstringCmp(const void *ap, const void *bp) {
// Lowercase
bVal -= 32;
}
// Relly we shouldn't ever get to '\0'
// Really we shouldn't ever get to '\0'
if (aVal == '"' || bVal == '"' || aVal == '\0' || bVal == '\0') {
// We're done. one is a substring of the other
// or something happenend and the quote didn't stop us.
@@ -285,7 +285,7 @@ class RotaryEncoderUIUsermod : public Usermod {
* 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_ROTARY_ENC_UI; }
uint16_t getId() override { return USERMOD_ID_ROTARY_ENC_UI; }
/**
* Enable/Disable the usermod
*/
@@ -300,7 +300,7 @@ class RotaryEncoderUIUsermod : public Usermod {
* 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();
void setup() override;
/**
* connected() is called every time the WiFi is (re)connected
@@ -311,11 +311,11 @@ class RotaryEncoderUIUsermod : public Usermod {
/**
* loop() is called continuously. Here you can check for events, read sensors, etc.
*/
void loop();
void loop() override;
#ifndef WLED_DISABLE_MQTT
//bool onMqttMessage(char* topic, char* payload);
//void onMqttConnect(bool sessionPresent);
//bool onMqttMessage(char* topic, char* payload) override;
//void onMqttConnect(bool sessionPresent) override;
#endif
/**
@@ -323,31 +323,31 @@ class RotaryEncoderUIUsermod : public Usermod {
* will prevent button working in a default way.
* Replicating button.cpp
*/
//bool handleButton(uint8_t b);
//bool handleButton(uint8_t b) override;
/**
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
*/
//void addToJsonInfo(JsonObject &root);
//void addToJsonInfo(JsonObject &root) override;
/**
* 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);
//void addToJsonState(JsonObject &root) override;
/**
* 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);
//void readFromJsonState(JsonObject &root) override;
/**
* provide the changeable values
*/
void addToConfig(JsonObject &root);
void addToConfig(JsonObject &root) override;
void appendConfigData();
void appendConfigData() override;
/**
* restore the changeable values
@@ -355,7 +355,7 @@ class RotaryEncoderUIUsermod : public Usermod {
*
* The function should return true if configuration was successfully loaded or false if there was no configuration.
*/
bool readFromConfig(JsonObject &root);
bool readFromConfig(JsonObject &root) override;
// custom methods
void displayNetworkInfo();
@@ -392,26 +392,26 @@ byte RotaryEncoderUIUsermod::readPin(uint8_t pin) {
* modes_alpha_indexes and palettes_alpha_indexes.
*/
void RotaryEncoderUIUsermod::sortModesAndPalettes() {
DEBUG_PRINTLN(F("Sorting modes and palettes."));
DEBUG_PRINT(F("Sorting modes: ")); DEBUG_PRINTLN(strip.getModeCount());
//modes_qstrings = re_findModeStrings(JSON_mode_names, strip.getModeCount());
modes_qstrings = strip.getModeDataSrc();
modes_alpha_indexes = re_initIndexArray(strip.getModeCount());
re_sortModes(modes_qstrings, modes_alpha_indexes, strip.getModeCount(), MODE_SORT_SKIP_COUNT);
palettes_qstrings = re_findModeStrings(JSON_palette_names, strip.getPaletteCount()+strip.customPalettes.size());
palettes_alpha_indexes = re_initIndexArray(strip.getPaletteCount()+strip.customPalettes.size());
DEBUG_PRINT(F("Sorting palettes: ")); DEBUG_PRINT(strip.getPaletteCount()); DEBUG_PRINT('/'); DEBUG_PRINTLN(strip.customPalettes.size());
palettes_qstrings = re_findModeStrings(JSON_palette_names, strip.getPaletteCount());
palettes_alpha_indexes = re_initIndexArray(strip.getPaletteCount());
if (strip.customPalettes.size()) {
for (int i=0; i<strip.customPalettes.size(); i++) {
palettes_alpha_indexes[strip.getPaletteCount()+i] = 255-i;
palettes_qstrings[strip.getPaletteCount()+i] = PSTR("~Custom~");
palettes_alpha_indexes[strip.getPaletteCount()-strip.customPalettes.size()+i] = 255-i;
palettes_qstrings[strip.getPaletteCount()-strip.customPalettes.size()+i] = PSTR("~Custom~");
}
}
// How many palette names start with '*' and should not be sorted?
// (Also skipping the first one, 'Default').
int skipPaletteCount = 1;
while (pgm_read_byte_near(palettes_qstrings[skipPaletteCount++]) == '*') ;
re_sortModes(palettes_qstrings, palettes_alpha_indexes, strip.getPaletteCount(), skipPaletteCount);
while (pgm_read_byte_near(palettes_qstrings[skipPaletteCount]) == '*') skipPaletteCount++;
re_sortModes(palettes_qstrings, palettes_alpha_indexes, strip.getPaletteCount()-strip.customPalettes.size(), skipPaletteCount);
}
byte *RotaryEncoderUIUsermod::re_initIndexArray(int numModes) {
@@ -596,7 +596,7 @@ void RotaryEncoderUIUsermod::loop()
bool changedState = false;
char lineBuffer[64];
do {
// finde new state
// find new state
switch (newState) {
case 0: strcpy_P(lineBuffer, PSTR("Brightness")); changedState = true; break;
case 1: if (!extractModeSlider(effectCurrent, 0, lineBuffer, 63)) newState++; else changedState = true; break; // speed

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