diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 00000000..811db619 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,20 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 120 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 7 +# Issues with these labels will never be considered stale +exemptLabels: + - pinned + - keep + - enhancement + - confirmed +# Label to use when marking an issue as stale +staleLabel: stale +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + Hey! This issue has been open for quite some time without any new comments now. + It will be closed automatically in a week if no further activity occurs. + + Thank you for using WLED! +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false diff --git a/.gitignore b/.gitignore index 2116e693..44474514 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,7 @@ !.vscode/extensions.json /wled00/Release /wled00/extLibs +/platformio_override.ini +.DS_Store +.gitignore +.clang-format diff --git a/.travis.yml b/.travis.yml index 5d976261..882fe54a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,18 +18,26 @@ # it (remove "# " before each line) or use own configuration according to the # Travis CI documentation (see above). # +# * Test the Travis config here: +# < https://config.travis-ci.com/explore > +# language: python python: - - "2.7" -sudo: false + # - "2.7" + - "3.5" +os: linux cache: + bundler: true + ccache: true directories: - "~/.platformio" + - "~/.buildcache" env: - PLATFORMIO_CI_SRC=wled00 install: - pip install -U platformio - platformio update script: - - platformio ci --project-conf=./platformio.ini + # - platformio ci --project-conf=./platformio.ini + - platformio run \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 8281e64c..0f0d7401 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,7 +1,7 @@ { - // See http://go.microsoft.com/fwlink/?LinkId=827846 - // for the documentation about the extensions.json format - "recommendations": [ - "platformio.platformio-ide" - ] -} \ No newline at end of file + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ] +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..5dbfeeea --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,126 @@ +## WLED changelog + +### Development versions after 0.10.0 release + +#### Build 2005100 + +- Update to Espalexa v2.4.6 (+1.6kB free heap memory) +- Added `m5atom` PlatformIO environment + +#### Build 2005090 + +- Default to ESP8266 Arduino core v2.7.1 in PlatformIO +- Fixed Preset Slot 16 always indicating as empty (#891) +- Disabled Alexa emulation by default (causes bootloop for some users) +- Added BWLT11 and SHOJO_PCB defines to NpbWrapper +- Merged pull request #898 adding Solid Glitter effect + +### WLED version 0.10.0 + +#### Build 2005030 + +- DMX Single RGW and Single DRGB modes now support an additional white channel +- Improved palettes derived from set colors and changed their names + +### Development versions between 0.9.1 and 0.10.0 release + +#### Build 2005020 + +- Added ACST and ACST/ACDT timezones + +#### Build 2005010 + +- Added module info page to web UI +- Added realtime override functionality to web UI +- Added individial 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 + +#### Build 2004300 + +- Added realtime override option and `lor` JSON property +- Added `lm` (live mode) and `lip` (live IP) properties to info in JSON API +- Added reset commands to APIs +- Added `json/si`, returning state and info, but no FX or Palette lists +- Added rollover detection to millis(). Can track uptimes longer than 49 days +- Attempted to fix Wifi issues with Unifi brand APs + +#### Build 2004230 + +- Added brightness and power for individual segments +- Added `on` and `bri` properties to Segment object in JSON API +- Added `C3` an `SB` commands to HTTP get API +- Merged pull request #865 for 5CH_Shojo_PCB environment + +#### Build 2004220 + +- Added Candle Multi effect +- Added Palette capability to Pacifica effect + +#### Build 2004190 + +- Added TM1814 type LED defines + +#### Build 2004120 + +- Added Art-Net support +- Added OTA platform to platformio.ini + +#### Build 2004100 + +- Fixed DMX output compilation +- Added DMX start LED setting + +#### Build 2004061 + +- Fixed RBG and BGR getPixelColor (#825) +- Improved formatting + +#### Build 2004060 + +- Consolidated global variables in wled.h + +#### Build 2003300 + +- Major change of project structure from .ino to .cpp and func_declare.h + +#### Build 2003262 + +- Fixed compilation for Analog LEDs +- Fixed sync settings network port fields too small + +#### Build 2003261 + +- Fixed live preview not displaying whole light if over 255 LEDs + +#### Build 2003251 + +- Added Pacifica effect (tentative, doesn't yet support other colors) +- Added Atlantica palette +- Fixed ESP32 build of Espalexa + +#### Build 2003222 + +- Fixed Alexa Whites on non-RGBW lights (bump Espalexa to 2.4.5) + +#### Build 2003221 + +- Moved Cronixie driver from FX library to drawOverlay handler + +#### Build 2003211 + +- Added custom mapping compile define to FX_fcn.h +- Merged pull request #784 by @TravisDean: Fixed initialization bug when toggling skip first +- Added link to youtube videos by Room31 to readme + +#### Build 2003141 + +- Fixed color of main segment returned in JSON API during transition not being target color (closes #765) +- Fixed arlsLock() being called after pixels set in E1.31 (closes #772) +- Fixed HTTP API calls not having an effect if no segment selected (now applies to main segment) + +#### Build 2003121 + +- Created changelog.md - make tracking changes to code easier +- Merged pull request #766 by @pille: Fix E1.31 out-of sequence detection + diff --git a/platformio.ini b/platformio.ini index f9c3575d..dad6f950 100644 --- a/platformio.ini +++ b/platformio.ini @@ -2,245 +2,326 @@ ; Please visit documentation: https://docs.platformio.org/page/projectconf.html [platformio] -src_dir = ./wled00 +src_dir = ./wled00 data_dir = ./wled00/data -;lib_extra_dirs = ./wled00/src -lib_dir = ./wled00/src -; Please uncomment one of the 5 lines below to select your board -env_default = nodemcuv2 -; env_default = esp01 -; env_default = esp01_1m -; env_default = esp07 -; env_default = d1_mini -; env_default = esp32dev -; env_default = esp8285_4CH_MagicHome -; env_default = esp8285_4CH_H801 -; env_default = esp8285_5CH_H801 +lib_dir = ./wled00/src +build_cache_dir = ~/.buildcache +extra_configs = + platformio_override.ini + +# ------------------------------------------------------------------------------ +# ENVIRONMENTS +# +# Please uncomment one of the lines below to select your board(s) +# ------------------------------------------------------------------------------ + +# Travis CI binaries (comment this out when building for single board) +default_envs = d1_mini, esp01, esp01_1m_ota, esp32dev + +# Release binaries +; default_envs = nodemcuv2, esp01, esp01_1m_ota, esp01_1m_full, esp32dev, custom_WS2801, custom_APA102, custom_LEDPIN_16, custom_LEDPIN_4, custom32_LEDPIN_16 + +# Single binaries (uncomment your board) +; default_envs = nodemcuv2 +; default_envs = esp01 +; default_envs = esp01_1m_ota +; 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_4CH_H801 +; default_envs = esp8285_5CH_H801 +; default_envs = d1_mini_5CH_Shojo_PCB +; default_envs = wemos_shield_esp32 +; default_envs = m5atom [common] -framework = arduino -monitor_speed = 115200 -board_build.flash_mode = dout -upload_speed = 115200 -upload_speed_fast = 921600 -build_flags = - -w ; supresses all C/C++ warnings - ; -D VERSION=0.8.5 - ; -D DEBUG - #build_flags for the IRremoteESP8266 library (enabled decoders have to appear here) - -D _IR_ENABLE_DEFAULT_=false - -D DECODE_HASH=true - -D DECODE_NEC=true - -D DECODE_SONY=true - -D DECODE_PANASONIC=true - -D DECODE_JVC=true - -D DECODE_SAMSUNG=true - -D DECODE_LG=true - -D DECODE_SANYO=true - -D DECODE_SHARP=true - -D DECODE_DENON=true -# TODO replace libs in /lib with managed libs in here if possible. -# If they are not changed it's just a matter of setting the correct version and change the import statement -lib_deps_external = - #Blynk@0.5.4(changed) - #E131@1.0.0(changed) - FastLED@3.3.2 - NeoPixelBus@2.5.1 - ESPAsyncTCP@1.2.0 - ESPAsyncUDP@697c75a025 - AsyncTCP@1.0.3 - Esp Async WebServer@1.2.0 - #ArduinoJson@5.13.5 - #IRremoteESP8266@2.7.2 - #For saving 20k program space the brnach "compile_flags" will be used: - https://github.com/crankyoldgit/IRremoteESP8266.git#compile_flags - #Time@1.5 - #Timezone@1.2.1 - #For use SSD1306 0.91" OLED display uncomment following - #U8g2@~2.27.2 - #For Dallas sensor uncomment following 2 lines - #DallasTemperature@~3.8.0 - #OneWire@~2.3.5 -[common:esp8266] # ------------------------------------------------------------------------------ # PLATFORM: # !! DO NOT confuse platformio's ESP8266 development platform with Arduino core for ESP8266 -# We use Arduino Core 2.5.0 (platformIO 2.0.4) as default # # arduino core 2.3.0 = platformIO 1.5.0 # arduino core 2.4.0 = platformIO 1.6.0 # arduino core 2.4.1 = platformIO 1.7.3 # arduino core 2.4.2 = platformIO 1.8.0 # arduino core 2.5.0 = platformIO 2.0.4 -# arduino core stage = platformIO feature#stage +# arduino core 2.5.1 = platformIO 2.1.1 +# arduino core 2.5.2 = platformIO 2.2.3 +# arduino core 2.6.1 = platformIO 2.3.0 +# arduino core 2.6.2 = platformIO 2.3.1 +# arduino core 2.6.3 = platformIO 2.3.2 +# arduino core 2.7.0 = platformIO 2.5.0 # ------------------------------------------------------------------------------ arduino_core_2_3_0 = espressif8266@1.5.0 arduino_core_2_4_0 = espressif8266@1.6.0 arduino_core_2_4_1 = espressif8266@1.7.3 arduino_core_2_4_2 = espressif8266@1.8.0 arduino_core_2_5_0 = espressif8266@2.0.4 +arduino_core_2_5_1 = espressif8266@2.1.1 arduino_core_2_5_2 = espressif8266@2.2.3 arduino_core_2_6_1 = espressif8266@2.3.0 arduino_core_2_6_2 = espressif8266@2.3.1 -arduino_core_stage = https://github.com/platformio/platform-espressif8266.git#feature/stage -platform = ${common:esp8266.arduino_core_2_6_2} -build_flags = - -D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH - -Wl,-Teagle.flash.4m1m.ld ;;;; Required for core > v2.5.0 or staging version 4MB Flash 3MB SPIFFs +arduino_core_2_6_3 = espressif8266@2.3.3 +arduino_core_2_7_1 = espressif8266@2.5.1 -[common:esp8266_1M] -platform = espressif8266@1.8.0 -build_flags = - -D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH - -Wl,-Teagle.flash.1m0.ld ;;;; Compile with no SPIFFS to leave space for OTA - ; -D WLED_DISABLE_MOBILE_UI - ; -D WLED_DISABLE_OTA - ; -D WLED_DISABLE_ALEXA - -D WLED_DISABLE_BLYNK - -D WLED_DISABLE_CRONIXIE - ; -D WLED_DISABLE_HUESYNC - ; -D WLED_DISABLE_INFRARED +# Development platforms +arduino_core_develop = https://github.com/platformio/platform-espressif8266#develop +arduino_core_git = https://github.com/platformio/platform-espressif8266#feature/stage -[common:esp8266_512k] -platform = espressif8266@1.8.0 -build_flags = - -D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH - -Wl,-Teagle.flash.512k0.ld ;;;; Compile with no SPIFFS - ; -D WLED_DISABLE_MOBILE_UI - -D WLED_DISABLE_OTA - ; -D WLED_DISABLE_ALEXA - -D WLED_DISABLE_BLYNK - -D WLED_DISABLE_CRONIXIE - -D WLED_DISABLE_HUESYNC - ; -D WLED_DISABLE_INFRARED +# Platform to use for ESP8266 +platform_latest = ${common.arduino_core_2_7_1} -[common:esp32] -platform = espressif32@1.11.1 -build_flags = - -D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH - -D ARDUINO_ARCH_ESP32 +# ------------------------------------------------------------------------------ +# FLAGS: DEBUG +# +# ------------------------------------------------------------------------------ +debug_flags = -D DEBUG=1 -D WLED_DEBUG -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_TLS_MEM +#if needed (for memleaks etc) also add; -DDEBUG_ESP_OOM -include "umm_malloc/umm_malloc_cfg.h" +#-DDEBUG_ESP_CORE is not working right now + +# ------------------------------------------------------------------------------ +# FLAGS: ldscript +# ldscript_512k ( 512 KB) = 487 KB sketch, 4 KB eeprom, no spiffs, 16 KB reserved +# ldscript_1m0m (1024 KB) = 999 KB sketch, 4 KB eeprom, no spiffs, 16 KB reserved +# ldscript_2m1m (2048 KB) = 1019 KB sketch, 4 KB eeprom, 1004 KB spiffs, 16 KB reserved +# ldscript_4m1m (4096 KB) = 1019 KB sketch, 4 KB eeprom, 1002 KB spiffs, 16 KB reserved, 2048 KB empty/ota? +# ldscript_4m3m (4096 KB) = 1019 KB sketch, 4 KB eeprom, 3040 KB spiffs, 16 KB reserved +# +# Available lwIP variants (macros): +# -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH = v1.4 Higher Bandwidth (default) +# -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY = v2 Lower Memory +# -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH = v2 Higher Bandwidth +# +# BearSSL performance: +# When building with -DSECURE_CLIENT=SECURE_CLIENT_BEARSSL, please add `board_build.f_cpu = 160000000` to the environment configuration +# +# BearSSL ciphers: +# When building on core >= 2.5, you can add the build flag -DBEARSSL_SSL_BASIC in order to build BearSSL with a limited set of ciphers: +# TLS_RSA_WITH_AES_128_CBC_SHA256 / AES128-SHA256 +# TLS_RSA_WITH_AES_256_CBC_SHA256 / AES256-SHA256 +# TLS_RSA_WITH_AES_128_CBC_SHA / AES128-SHA +# TLS_RSA_WITH_AES_256_CBC_SHA / AES256-SHA +# This reduces the OTA size with ~45KB, so it's especially useful on low memory boards (512k/1m). +# ------------------------------------------------------------------------------ +build_flags = -g -w -DMQTT_MAX_PACKET_SIZE=1024 -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH + -DSECURE_CLIENT=SECURE_CLIENT_BEARSSL -DBEARSSL_SSL_BASIC + #build_flags for the IRremoteESP8266 library (enabled decoders have to appear here) + -D _IR_ENABLE_DEFAULT_=false + -D DECODE_HASH=true + -D DECODE_NEC=true + -D DECODE_SONY=true + -D DECODE_SAMSUNG=true + -D DECODE_LG=true + +build_flags_esp8266 = ${common.build_flags} -DESP8266 +build_flags_esp32 = ${common.build_flags} -DARDUINO_ARCH_ESP32 + +ldscript_512k = eagle.flash.512k.ld ;for older versions change this to eagle.flash.512k0.ld +ldscript_1m0m = eagle.flash.1m.ld ;for older versions change this to eagle.flash.1m0.ld +ldscript_2m1m = eagle.flash.2m1m.ld +ldscript_4m1m = eagle.flash.4m1m.ld +ldscript_4m3m = eagle.flash.4m3m.ld + +shared_libdeps_dir = ./wled00/src + +# ------------------------------------------------------------------------------ +# COMMON SETTINGS: +# ------------------------------------------------------------------------------ +[env] +framework = arduino +board_build.flash_mode = dout +monitor_speed = 115200 +upload_speed = 115200 +lib_extra_dirs = + ${common.shared_libdeps_dir} + +# ------------------------------------------------------------------------------ +# LIBRARIES: required dependencies +# Please note that we don't always use the latest version of a library. +# +# The following libraries have been included (and some of them changd) in the source: +# ArduinoJson@5.13.5, Blynk@0.5.4(changed), E131@1.0.0(changed), Time@1.5, Timezone@1.2.1 +# ------------------------------------------------------------------------------ +lib_compat_mode = strict +lib_deps = + FastLED@3.3.2 + NeoPixelBus@2.5.7 + ESPAsyncTCP@1.2.0 + ESPAsyncUDP@697c75a025 + AsyncTCP@1.0.3 + Esp Async WebServer@1.2.0 + IRremoteESP8266@2.7.3 + #For use SSD1306 OLED display uncomment following + #U8g2@~2.27.2 + #For Dallas sensor uncomment following 2 lines + #OneWire@~2.3.5 + #For BME280 sensor uncomment following + #BME280@~3.0.0 +lib_ignore = + AsyncTCP + +# ------------------------------------------------------------------------------ +# WLED BUILDS +# ------------------------------------------------------------------------------ -# see: http://docs.platformio.org/en/latest/platforms/espressif8266.html [env:nodemcuv2] board = nodemcuv2 -platform = ${common:esp8266.platform} -monitor_speed = ${common.monitor_speed} -upload_speed = ${common.upload_speed} -framework = ${common.framework} -build_flags = - ${common.build_flags} - ${common:esp8266.build_flags} -lib_deps = - ${common.lib_deps_external} - -[env:d1_mini] -board = d1_mini -platform = ${common:esp8266.platform} -monitor_speed = ${common.monitor_speed} -upload_speed = ${common.upload_speed} -framework = ${common.framework} -build_flags = - ${common.build_flags} - ${common:esp8266.build_flags} -lib_deps = - ${common.lib_deps_external} - -[env:esp01_1m] -board = esp01_1m -platform = ${common:esp8266_1M.platform} -monitor_speed = ${common.monitor_speed} -upload_speed = ${common.upload_speed} -framework = ${common.framework} -build_flags = - ${common.build_flags} - ${common:esp8266_1M.build_flags} - # disable IR because there is no pin for it - -D WLED_DISABLE_INFRARED -lib_deps = - ${common.lib_deps_external} +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_4m1m} +build_flags = ${common.build_flags_esp8266} [env:esp01] board = esp01 -platform = ${common:esp8266_512k.platform} -monitor_speed = ${common.monitor_speed} -upload_speed = ${common.upload_speed} -framework = ${common.framework} -build_flags = - ${common.build_flags} - ${common:esp8266_512k.build_flags} - -D WLED_DISABLE_INFRARED -lib_deps = - ${common.lib_deps_external} +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_512k} +build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA -D WLED_DISABLE_ALEXA -D WLED_DISABLE_BLYNK + -D WLED_DISABLE_CRONIXIE -D WLED_DISABLE_HUESYNC -D WLED_DISABLE_INFRARED + +[env:esp01_1m_ota] +board = esp01_1m +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_1m0m} +build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_ALEXA -D WLED_DISABLE_BLYNK -D WLED_DISABLE_CRONIXIE -D WLED_DISABLE_HUESYNC -D WLED_DISABLE_INFRARED + +[env:esp01_1m_full] +board = esp01_1m +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_1m0m} +build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA [env:esp07] board = esp07 -platform = ${common:esp8266.platform} -monitor_speed = ${common.monitor_speed} -upload_speed = ${common.upload_speed} -framework = ${common.framework} -build_flags = - ${common.build_flags} - ${common:esp8266.build_flags} -lib_deps = - ${common.lib_deps_external} +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_4m1m} +build_flags = ${common.build_flags_esp8266} + +[env:d1_mini] +board = d1_mini +platform = ${common.platform_latest} +upload_speed = 921500 +board_build.ldscript = ${common.ldscript_4m1m} +build_flags = ${common.build_flags_esp8266} + +[env:heltec_wifi_kit_8] +board = d1_mini +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_4m1m} +build_flags = ${common.build_flags_esp8266} + +[env:h803wf] +board = d1_mini +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_4m1m} +build_flags = ${common.build_flags_esp8266} -D LEDPIN=1 -D WLED_DISABLE_INFRARED -# see: http://docs.platformio.org/en/latest/platforms/espressif32.html [env:esp32dev] board = esp32dev -platform = ${common:esp32.platform} -monitor_speed = ${common.monitor_speed} -upload_speed = ${common.upload_speed_fast} -framework = ${common.framework} -build_flags = - ${common.build_flags} - ${common:esp32.build_flags} -lib_deps = - ${common.lib_deps_external} +platform = espressif32@1.11.2 +build_flags = ${common.build_flags_esp32} lib_ignore = + ESPAsyncTCP ESPAsyncUDP [env:esp8285_4CH_MagicHome] board = esp8285 -platform = ${common:esp8266_1M.platform} -monitor_speed = ${common.monitor_speed} -upload_speed = ${common.upload_speed} -framework = ${common.framework} -build_flags = - ${common.build_flags} - ${common:esp8266_1M.build_flags} - -D WLED_DISABLE_HUESYNC - -D WLED_USE_ANALOG_LEDS -lib_deps = - ${common.lib_deps_external} +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_1m0m} +build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_HUESYNC -D WLED_USE_ANALOG_LEDS [env:esp8285_4CH_H801] board = esp8285 -platform = ${common:esp8266_1M.platform} -monitor_speed = ${common.monitor_speed} -upload_speed = ${common.upload_speed} -framework = ${common.framework} -build_flags = - ${common.build_flags} - ${common:esp8266_1M.build_flags} - -D WLED_DISABLE_HUESYNC - -D WLED_USE_ANALOG_LEDS - -D WLED_USE_H801 -lib_deps = - ${common.lib_deps_external} +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_1m0m} +build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_HUESYNC -D WLED_USE_ANALOG_LEDS -D WLED_USE_H801 [env:esp8285_5CH_H801] board = esp8285 -platform = ${common:esp8266_1M.platform} -monitor_speed = ${common.monitor_speed} -upload_speed = ${common.upload_speed} -framework = ${common.framework} -build_flags = - ${common.build_flags} - ${common:esp8266_1M.build_flags} - -D WLED_DISABLE_HUESYNC - -D WLED_USE_ANALOG_LEDS - -D WLED_USE_H801 - -D WLED_ENABLE_5CH_LEDS -lib_deps = - ${common.lib_deps_external} - +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_1m0m} +build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_HUESYNC -D WLED_USE_ANALOG_LEDS -D WLED_USE_H801 -D WLED_ENABLE_5CH_LEDS + +[env:d1_mini_5CH_Shojo_PCB] +board = d1_mini +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_4m1m} +build_flags = ${common.build_flags_esp8266} -D WLED_USE_ANALOG_LEDS -D WLED_USE_SHOJO_PCB -D WLED_ENABLE_5CH_LEDS + +# ------------------------------------------------------------------------------ +# DEVELOPMENT BOARDS +# ------------------------------------------------------------------------------ + +[env:d1_mini_debug] +board = d1_mini +build_type = debug +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_4m1m} +build_flags = ${common.build_flags_esp8266} ${common.debug_flags} + +[env:d1_mini_ota] +board = d1_mini +upload_protocol = espota +# exchange for your WLED IP +upload_port = "10.10.1.27" +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_4m1m} +build_flags = ${common.build_flags_esp8266} + +# ------------------------------------------------------------------------------ +# custom board configurations +# ------------------------------------------------------------------------------ + +[env:custom_LEDPIN_4] +board = d1_mini +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_4m1m} +build_flags = ${common.build_flags_esp8266} -D LEDPIN=4 -D IRPIN=5 + +[env:custom_LEDPIN_16] +board = d1_mini +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_4m1m} +build_flags = ${common.build_flags_esp8266} -D LEDPIN=16 + +[env:custom_APA102] +board = d1_mini +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_4m1m} +build_flags = ${common.build_flags_esp8266} -D USE_APA102 + +[env:custom_WS2801] +board = d1_mini +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_4m1m} +build_flags = ${common.build_flags_esp8266} -D USE_WS2801 + +[env:custom32_LEDPIN_16] +board = esp32dev +platform = espressif32@1.11.2 +build_flags = ${common.build_flags_esp32} -D LEDPIN=16 +lib_ignore = + ESPAsyncTCP + ESPAsyncUDP + +[env:wemos_shield_esp32] +board = esp32dev +platform = espressif32@1.11.2 +upload_port = /dev/cu.SLAB_USBtoUART +monitor_port = /dev/cu.SLAB_USBtoUART +upload_speed = 460800 +build_flags = ${common.build_flags_esp32} -D LEDPIN=16 -D RLYPIN=19 -D BTNPIN=17 +lib_ignore = + ESPAsyncTCP + ESPAsyncUDP + +[env:m5atom] +board = esp32dev +build_flags = ${common.build_flags_esp32} -D LEDPIN=27 -D BTNPIN=39 +lib_ignore = + ESPAsyncTCP + ESPAsyncUDP +platform = espressif32@1.11.2 \ No newline at end of file diff --git a/platformio_override.ini.example b/platformio_override.ini.example new file mode 100644 index 00000000..ba829aa1 --- /dev/null +++ b/platformio_override.ini.example @@ -0,0 +1,36 @@ +# Example PlatformIO Project Configuration Override +# ------------------------------------------------------------------------------ +# Copy to platformio_override.ini to activate overrides +# ------------------------------------------------------------------------------ +# Please visit documentation: https://docs.platformio.org/page/projectconf.html + +[env:esp8266_1m_custom] +board = esp01_1m +platform = ${common.arduino_core_2_4_2} +board_build.ldscript = ${common.ldscript_1m0m} +build_flags = ${common.build_flags_esp8266} + -D WLED_DISABLE_OTA + -D WLED_DISABLE_ALEXA + -D WLED_DISABLE_BLYNK + -D WLED_DISABLE_CRONIXIE + -D WLED_DISABLE_HUESYNC + -D WLED_DISABLE_INFRARED +; PIN defines - uncomment and change, if needed: +; -D LEDPIN=2 +; -D BTNPIN=0 +; -D IR_PIN=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 +; to drive analog LED strips (aka 5050), uncomment the following +; PWM pins 5,12,13,15 are used with Magic Home LED Controller (default) +; -D WLED_USE_ANALOG_LEDS +; for the H801 controller (PINs 15,13,12,14 (W2 = 04)) uncomment this +; -D WLED_USE_H801 +; for the BW-LT11 controller (PINs 12,4,14,5 ) uncomment this +; -D WLED_USE_BWLT11 +; and to enable channel 5 for RGBW-CT led strips this +; -D WLED_USE_5CH_LEDS diff --git a/readme.md b/readme.md index 8a9d2ada..53671c6b 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,7 @@ ![WLED logo](https://raw.githubusercontent.com/Aircoookie/WLED/master/wled_logo.png) [![](https://img.shields.io/github/release/Aircoookie/WLED.svg?style=flat-square)](https://github.com/Aircoookie/WLED/releases) +[![](https://img.shields.io/discourse/topics?colorB=blue&label=forum&server=https%3A%2F%2Fwled.discourse.group%2F&style=flat-square)](https://wled.discourse.group) [![](https://img.shields.io/discord/473448917040758787.svg?colorB=blue&label=discord&style=flat-square)](https://discord.gg/KuqP7NE) [![](https://img.shields.io/badge/quick_start-wiki-blue.svg?style=flat-square)](https://github.com/Aircoookie/WLED/wiki) [![](https://img.shields.io/badge/app-wled-blue.svg?style=flat-square)](https://github.com/Aircoookie/WLED-App) @@ -10,8 +11,8 @@ A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control NeoPixel (WS2812B, WS2811, SK6812, APA102) LEDs! ### Features: -- WS2812FX library integrated for almost 100 special effects -- FastLED noise effects and palettes +- WS2812FX library integrated for over 100 special effects +- FastLED noise effects and 50 palettes - Modern UI with color, effect and segment controls - Segments to set different effects and colors to parts of the LEDs - Settings page - configuration over network @@ -49,6 +50,10 @@ DrZzs has made some excellent video guides: If you'd rather read, here is a very [detailed step-by-step beginner tutorial](https://tynick.com/blog/11-03-2019/getting-started-with-wled-on-esp8266/) by tynick! +Russian speakers, check out the videos by Room31: +[WLED Firmware Overview: Interface and Settings](https://youtu.be/h7lKsczEI7E) +[ESP8266 based LED controller for WS2812b strip. WLED Firmware + OpenHAB](https://youtu.be/K4ioTt3XvGc) + ### Other Licensed under the MIT license @@ -57,7 +62,8 @@ Credits [here](https://github.com/Aircoookie/WLED/wiki/Contributors-&-About)! Uses Linearicons by Perxis! Join the Discord [server](https://discord.gg/KuqP7NE) to discuss everything about WLED! -You can also send me mails to [dev.aircoookie@gmail.com](mailto:dev.aircoookie@gmail.com). +Check out the WLED [Discourse forum](https://wled.discourse.group)! +You can also send me mails to [dev.aircoookie@gmail.com](mailto:dev.aircoookie@gmail.com), but please only do so if you want to talk to me privately. If WLED really brightens up your every day, you can [![](https://img.shields.io/badge/send%20me%20a%20small%20gift-paypal-blue.svg?style=flat-square)](https://paypal.me/aircoookie) *Disclaimer:* diff --git a/usermods/Enclosure_with_OLED_temp_ESP07/assets/controller.jpg b/usermods/Enclosure_with_OLED_temp_ESP07/assets/controller.jpg new file mode 100644 index 00000000..d518ca3e Binary files /dev/null and b/usermods/Enclosure_with_OLED_temp_ESP07/assets/controller.jpg differ diff --git a/usermods/Enclosure_with_OLED_temp_ESP07/assets/pcb.png b/usermods/Enclosure_with_OLED_temp_ESP07/assets/pcb.png new file mode 100644 index 00000000..cf146918 Binary files /dev/null and b/usermods/Enclosure_with_OLED_temp_ESP07/assets/pcb.png differ diff --git a/usermods/Enclosure_with_OLED_temp_ESP07/assets/readme.md b/usermods/Enclosure_with_OLED_temp_ESP07/assets/readme.md new file mode 100644 index 00000000..4d1005d9 --- /dev/null +++ b/usermods/Enclosure_with_OLED_temp_ESP07/assets/readme.md @@ -0,0 +1,7 @@ +# Enclosure and PCB + +## IP67 rated enclosure +![Enclosure](controller.jpg) + +## PCB +![PCB](pcb.png) diff --git a/usermods/Enclosure_with_OLED_temp_ESP07/readme.md b/usermods/Enclosure_with_OLED_temp_ESP07/readme.md new file mode 100644 index 00000000..94d1c1f2 --- /dev/null +++ b/usermods/Enclosure_with_OLED_temp_ESP07/readme.md @@ -0,0 +1,68 @@ +# Almost universal controller board for outdoor applications +This usermod is using ideas from @mrVanboy and @400killer + +Installation of file: Copy and replace file in wled00 directory. + +For BME280 sensor use usermod_bme280.cpp. Copy to wled00 and rename to usermod.cpp + +## Project repository +- [Original repository](https://github.com/srg74/Controller-for-WLED-firmware) - Main controller repository +## Features +- SSD1306 128x32 and 128x64 I2C OLED display +- On screen IP address, SSID and controller status (e.g. ON or OFF, recent effect) +- Auto display shutoff for saving display lifetime +- Dallas temperature sensor +- Reporting temperature to MQTT broker + +## Hardware +![Hardware connection](assets/controller.jpg) + +## Functionality checked with +- ESP-07S +- PlatformIO +- SSD1306 128x32 I2C OLED display +- DS18B20 (temperature sensor) +- BME280 (temperature, humidity and pressure sensor) +- KY-022 (infrared receiver) +- Push button (N.O. momentary switch) + +For Dallas sensor uncomment `U8g2@~2.27.3`,`DallasTemperature@~3.8.0`,`OneWire@~2.3.5 under` `[common]` section in `platformio.ini`: +```ini +# platformio.ini +... +[platformio] +... +default_envs = esp07 +; default_envs = d1_mini +... +[common] +... +lib_deps_external = + ... + #For use SSD1306 OLED display uncomment following + U8g2@~2.27.3 + #For Dallas sensor uncomment following 2 lines + DallasTemperature@~3.8.0 + OneWire@~2.3.5 +... +``` + +For BME280 sensor uncomment `U8g2@~2.27.3`,`BME280@~3.0.0 under` `[common]` section in `platformio.ini`: +```ini +# platformio.ini +... +[platformio] +... +default_envs = esp07 +; default_envs = d1_mini +... +[common] +... +lib_deps_external = + ... + #For use SSD1306 OLED display uncomment following + U8g2@~2.27.3 + #For BME280 sensor uncomment following + BME280@~3.0.0 +... +``` diff --git a/usermods/Enclosure_with_OLED_temp_ESP07/usermod.cpp b/usermods/Enclosure_with_OLED_temp_ESP07/usermod.cpp new file mode 100644 index 00000000..9724a1b7 --- /dev/null +++ b/usermods/Enclosure_with_OLED_temp_ESP07/usermod.cpp @@ -0,0 +1,209 @@ +#include "wled.h" +#include +#include // from https://github.com/olikraus/u8g2/ +#include //Dallastemperature sensor +//The SCL and SDA pins are defined here. +//Lolin32 boards use SCL=5 SDA=4 +#define U8X8_PIN_SCL 5 +#define U8X8_PIN_SDA 4 +// Dallas sensor +OneWire oneWire(13); +DallasTemperature sensor(&oneWire); +long temptimer = millis(); +long lastMeasure = 0; +#define Celsius // Show temperature mesaurement in Celcius otherwise is in Fahrenheit + +// If display does not work or looks corrupted check the +// constructor reference: +// https://github.com/olikraus/u8g2/wiki/u8x8setupcpp +// or check the gallery: +// https://github.com/olikraus/u8g2/wiki/gallery +// --> First choise of cheap I2C OLED 128X32 0.91" +U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA +// --> Second choise of cheap I2C OLED 128X64 0.96" or 1.3" +//U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA +// gets called once at boot. Do all initialization that doesn't depend on +// network here +void userSetup() { + sensor.begin(); //Start Dallas temperature sensor + u8x8.begin(); + //u8x8.setFlipMode(1); //Uncoment if using WLED Wemos shield + u8x8.setPowerSave(0); + u8x8.setContrast(10); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255 + u8x8.setFont(u8x8_font_chroma48medium8_r); + u8x8.drawString(0, 0, "Loading..."); +} + +// gets called every time WiFi is (re-)connected. Initialize own network +// interfaces here +void userConnected() {} + +// needRedraw marks if redraw is required to prevent often redrawing. +bool needRedraw = true; + +// Next variables hold the previous known values to determine if redraw is +// required. +String knownSsid = ""; +IPAddress knownIp; +uint8_t knownBrightness = 0; +uint8_t knownMode = 0; +uint8_t knownPalette = 0; + +long lastUpdate = 0; +long lastRedraw = 0; +bool displayTurnedOff = false; +// How often we are redrawing screen +#define USER_LOOP_REFRESH_RATE_MS 5000 + +void userLoop() { + +//----> Dallas temperature sensor MQTT publishing + temptimer = millis(); +// Timer to publishe new temperature every 60 seconds + if (temptimer - lastMeasure > 60000) + { + lastMeasure = temptimer; +//Check if MQTT Connected, otherwise it will crash the 8266 + if (mqtt != nullptr) + { + sensor.requestTemperatures(); +//Gets prefered temperature scale based on selection in definitions section + #ifdef Celsius + float board_temperature = sensor.getTempCByIndex(0); + #else + float board_temperature = sensor.getTempFByIndex(0); + #endif +//Create character string populated with user defined device topic from the UI, and the read temperature. Then publish to MQTT server. + char subuf[38]; + strcpy(subuf, mqttDeviceTopic); + strcat(subuf, "/temperature"); + mqtt->publish(subuf, 0, true, String(board_temperature).c_str()); + } + } + + // Check if we time interval for redrawing passes. + if (millis() - lastUpdate < USER_LOOP_REFRESH_RATE_MS) { + return; + } + lastUpdate = millis(); + + // Turn off display after 3 minutes with no change. + if(!displayTurnedOff && millis() - lastRedraw > 3*60*1000) { + u8x8.setPowerSave(1); + displayTurnedOff = true; + } + + // Check if values which are shown on display changed from the last time. + if (((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid) { + needRedraw = true; + } else if (knownIp != (apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP())) { + needRedraw = true; + } else if (knownBrightness != bri) { + needRedraw = true; + } else if (knownMode != strip.getMode()) { + needRedraw = true; + } else if (knownPalette != strip.getSegment(0).palette) { + needRedraw = true; + } + + if (!needRedraw) { + return; + } + needRedraw = false; + + if (displayTurnedOff) + { + u8x8.setPowerSave(0); + displayTurnedOff = false; + } + lastRedraw = millis(); + + // Update last known values. + #if defined(ESP8266) + knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID(); + #else + knownSsid = WiFi.SSID(); + #endif + knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP(); + knownBrightness = bri; + knownMode = strip.getMode(); + knownPalette = strip.getSegment(0).palette; + u8x8.clear(); + u8x8.setFont(u8x8_font_chroma48medium8_r); + + // First row with Wifi name + u8x8.setCursor(1, 0); + u8x8.print(knownSsid.substring(0, u8x8.getCols() > 1 ? u8x8.getCols() - 2 : 0)); + // Print `~` char to indicate that SSID is longer, than owr dicplay + if (knownSsid.length() > u8x8.getCols()) + u8x8.print("~"); + + // Second row with IP or Psssword + u8x8.setCursor(1, 1); + // Print password in AP mode and if led is OFF. + if (apActive && bri == 0) + u8x8.print(apPass); + else + u8x8.print(knownIp); + + // Third row with mode name + u8x8.setCursor(2, 2); + uint8_t qComma = 0; + bool insideQuotes = false; + uint8_t printedChars = 0; + char singleJsonSymbol; + + // Find the mode name in JSON + for (size_t i = 0; i < strlen_P(JSON_mode_names); i++) { + singleJsonSymbol = pgm_read_byte_near(JSON_mode_names + i); + switch (singleJsonSymbol) { + case '"': + insideQuotes = !insideQuotes; + break; + case '[': + case ']': + break; + case ',': + qComma++; + default: + if (!insideQuotes || (qComma != knownMode)) + break; + u8x8.print(singleJsonSymbol); + printedChars++; + } + if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2)) + break; + } + // Fourth row with palette name + u8x8.setCursor(2, 3); + qComma = 0; + insideQuotes = false; + printedChars = 0; + // Looking for palette name in JSON. + for (size_t i = 0; i < strlen_P(JSON_palette_names); i++) { + singleJsonSymbol = pgm_read_byte_near(JSON_palette_names + i); + switch (singleJsonSymbol) { + case '"': + insideQuotes = !insideQuotes; + break; + case '[': + case ']': + break; + case ',': + qComma++; + default: + if (!insideQuotes || (qComma != knownPalette)) + break; + u8x8.print(singleJsonSymbol); + printedChars++; + } + if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2)) + break; + } + + u8x8.setFont(u8x8_font_open_iconic_embedded_1x1); + u8x8.drawGlyph(0, 0, 80); // wifi icon + u8x8.drawGlyph(0, 1, 68); // home icon + u8x8.setFont(u8x8_font_open_iconic_weather_2x2); + u8x8.drawGlyph(0, 2, 66 + (bri > 0 ? 3 : 0)); // sun/moon icon +} \ No newline at end of file diff --git a/usermods/Enclosure_with_OLED_temp_ESP07/usermod_bme280.cpp b/usermods/Enclosure_with_OLED_temp_ESP07/usermod_bme280.cpp new file mode 100644 index 00000000..c39c74d2 --- /dev/null +++ b/usermods/Enclosure_with_OLED_temp_ESP07/usermod_bme280.cpp @@ -0,0 +1,266 @@ +#include "wled.h" +#include +#include // from https://github.com/olikraus/u8g2/ +#include +#include //BME280 sensor + +void UpdateBME280Data(); + +#define Celsius // Show temperature mesaurement in Celcius otherwise is in Fahrenheit +BME280I2C bme; // Default : forced mode, standby time = 1000 ms + // Oversampling = pressure ×1, temperature ×1, humidity ×1, filter off, + +#ifdef ARDUINO_ARCH_ESP32 //ESP32 boards +uint8_t SCL_PIN = 22; +uint8_t SDA_PIN = 21; +#else //ESP8266 boards +uint8_t SCL_PIN = 5; +uint8_t SDA_PIN = 4; +// uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8 +#endif + +//The SCL and SDA pins are defined here. +//ESP8266 Wemos D1 mini board use SCL=5 SDA=4 while ESP32 Wemos32 mini board use SCL=22 SDA=21 +#define U8X8_PIN_SCL SCL_PIN +#define U8X8_PIN_SDA SDA_PIN +//#define U8X8_PIN_RESET RST_PIN // Uncoment for Heltec WiFi-Kit-8 + +// If display does not work or looks corrupted check the +// constructor reference: +// https://github.com/olikraus/u8g2/wiki/u8x8setupcpp +// or check the gallery: +// https://github.com/olikraus/u8g2/wiki/gallery +// --> First choise of cheap I2C OLED 128X32 0.91" +U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA +// --> Second choise of cheap I2C OLED 128X64 0.96" or 1.3" +//U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA +// --> Third choise of Heltec WiFi-Kit-8 OLED 128X32 0.91" +//U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_RESET, U8X8_PIN_SCL, U8X8_PIN_SDA); // Constructor for Heltec WiFi-Kit-8 +// gets called once at boot. Do all initialization that doesn't depend on network here + +// BME280 sensor timer +long tempTimer = millis(); +long lastMeasure = 0; + +float SensorPressure(NAN); +float SensorTemperature(NAN); +float SensorHumidity(NAN); + +void userSetup() { + u8x8.begin(); + u8x8.setPowerSave(0); + u8x8.setFlipMode(1); + u8x8.setContrast(10); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255 + u8x8.setFont(u8x8_font_chroma48medium8_r); + u8x8.drawString(0, 0, "Loading..."); + Wire.begin(SDA_PIN,SCL_PIN); + +while(!bme.begin()) + { + Serial.println("Could not find BME280I2C sensor!"); + delay(1000); + } +switch(bme.chipModel()) + { + case BME280::ChipModel_BME280: + Serial.println("Found BME280 sensor! Success."); + break; + case BME280::ChipModel_BMP280: + Serial.println("Found BMP280 sensor! No Humidity available."); + break; + default: + Serial.println("Found UNKNOWN sensor! Error!"); + } +} + +// gets called every time WiFi is (re-)connected. Initialize own network +// interfaces here +void userConnected() {} + +// needRedraw marks if redraw is required to prevent often redrawing. +bool needRedraw = true; + +// Next variables hold the previous known values to determine if redraw is +// required. +String knownSsid = ""; +IPAddress knownIp; +uint8_t knownBrightness = 0; +uint8_t knownMode = 0; +uint8_t knownPalette = 0; + +long lastUpdate = 0; +long lastRedraw = 0; +bool displayTurnedOff = false; +// How often we are redrawing screen +#define USER_LOOP_REFRESH_RATE_MS 5000 + +void userLoop() { + +// BME280 sensor MQTT publishing + tempTimer = millis(); +// Timer to publish new sensor data every 60 seconds + if (tempTimer - lastMeasure > 60000) + { + lastMeasure = tempTimer; + +// Check if MQTT Connected, otherwise it will crash the 8266 + if (mqtt != nullptr) + { + UpdateBME280Data(); + float board_temperature = SensorTemperature; + float board_pressure = SensorPressure; + float board_humidity = SensorHumidity; + +// Create string populated with user defined device topic from the UI, and the read temperature, humidity and pressure. Then publish to MQTT server. + String t = String(mqttDeviceTopic); + t += "/temperature"; + mqtt->publish(t.c_str(), 0, true, String(board_temperature).c_str()); + String p = String(mqttDeviceTopic); + p += "/pressure"; + mqtt->publish(p.c_str(), 0, true, String(board_pressure).c_str()); + String h = String(mqttDeviceTopic); + h += "/humidity"; + mqtt->publish(h.c_str(), 0, true, String(board_humidity).c_str()); + } + } + + // Check if we time interval for redrawing passes. + if (millis() - lastUpdate < USER_LOOP_REFRESH_RATE_MS) { + return; + } + lastUpdate = millis(); + + // Turn off display after 3 minutes with no change. + if(!displayTurnedOff && millis() - lastRedraw > 3*60*1000) { + u8x8.setPowerSave(1); + displayTurnedOff = true; + } + + // Check if values which are shown on display changed from the last time. + if (((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid) { + needRedraw = true; + } else if (knownIp != (apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP())) { + needRedraw = true; + } else if (knownBrightness != bri) { + needRedraw = true; + } else if (knownMode != strip.getMode()) { + needRedraw = true; + } else if (knownPalette != strip.getSegment(0).palette) { + needRedraw = true; + } + + if (!needRedraw) { + return; + } + needRedraw = false; + + if (displayTurnedOff) + { + u8x8.setPowerSave(0); + displayTurnedOff = false; + } + lastRedraw = millis(); + + // Update last known values. + #if defined(ESP8266) + knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID(); + #else + knownSsid = WiFi.SSID(); + #endif + knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP(); + knownBrightness = bri; + knownMode = strip.getMode(); + knownPalette = strip.getSegment(0).palette; + u8x8.clear(); + u8x8.setFont(u8x8_font_chroma48medium8_r); + + // First row with Wifi name + u8x8.setCursor(1, 0); + u8x8.print(knownSsid.substring(0, u8x8.getCols() > 1 ? u8x8.getCols() - 2 : 0)); + // Print `~` char to indicate that SSID is longer, than owr dicplay + if (knownSsid.length() > u8x8.getCols()) + u8x8.print("~"); + + // Second row with IP or Psssword + u8x8.setCursor(1, 1); + // Print password in AP mode and if led is OFF. + if (apActive && bri == 0) + u8x8.print(apPass); + else + u8x8.print(knownIp); + + // Third row with mode name + u8x8.setCursor(2, 2); + uint8_t qComma = 0; + bool insideQuotes = false; + uint8_t printedChars = 0; + char singleJsonSymbol; + + // Find the mode name in JSON + for (size_t i = 0; i < strlen_P(JSON_mode_names); i++) { + singleJsonSymbol = pgm_read_byte_near(JSON_mode_names + i); + switch (singleJsonSymbol) { + case '"': + insideQuotes = !insideQuotes; + break; + case '[': + case ']': + break; + case ',': + qComma++; + default: + if (!insideQuotes || (qComma != knownMode)) + break; + u8x8.print(singleJsonSymbol); + printedChars++; + } + if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2)) + break; + } + // Fourth row with palette name + u8x8.setCursor(2, 3); + qComma = 0; + insideQuotes = false; + printedChars = 0; + // Looking for palette name in JSON. + for (size_t i = 0; i < strlen_P(JSON_palette_names); i++) { + singleJsonSymbol = pgm_read_byte_near(JSON_palette_names + i); + switch (singleJsonSymbol) { + case '"': + insideQuotes = !insideQuotes; + break; + case '[': + case ']': + break; + case ',': + qComma++; + default: + if (!insideQuotes || (qComma != knownPalette)) + break; + u8x8.print(singleJsonSymbol); + printedChars++; + } + if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2)) + break; + } + + u8x8.setFont(u8x8_font_open_iconic_embedded_1x1); + u8x8.drawGlyph(0, 0, 80); // wifi icon + u8x8.drawGlyph(0, 1, 68); // home icon + u8x8.setFont(u8x8_font_open_iconic_weather_2x2); + u8x8.drawGlyph(0, 2, 66 + (bri > 0 ? 3 : 0)); // sun/moon icon +} + +void UpdateBME280Data() { + float temp(NAN), hum(NAN), pres(NAN); +#ifdef Celsius + BME280::TempUnit tempUnit(BME280::TempUnit_Celsius); +#else + BME280::TempUnit tempUnit(BME280::TempUnit_Fahrenheit); +#endif + BME280::PresUnit presUnit(BME280::PresUnit_Pa); + bme.read(pres, temp, hum, tempUnit, presUnit); + SensorTemperature=temp; + SensorHumidity=hum; + SensorPressure=pres; +} diff --git a/usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.txt b/usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.md similarity index 50% rename from usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.txt rename to usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.md index 61287336..60fc31f7 100644 --- a/usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.txt +++ b/usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.md @@ -1,8 +1,34 @@ -These files allow WLED 0.8.6 to report the temp sensor on the Quinled board to MQTT. I use it to report the board temp to Home Assistant via MQTT, so it will send notifications if something happens and the board start to heat up. +# QuinLED Dig Uno board +These files allow WLED 0.9.1 to report the temp sensor on the Quinled board to MQTT. I use it to report the board temp to Home Assistant via MQTT, so it will send notifications if something happens and the board start to heat up. This code uses Aircookie's WLED software. It has a premade file for user modifications. I use it to publish the temperature from the dallas temperature sensor on the Quinled board. The entries for the top of the WLED00 file, initializes the required libraries, and variables for the sensor. The .ino file waits for 60 seconds, and checks to see if the MQTT server is connected (thanks Aircoookie). It then poles the sensor, and published it using the MQTT service already running, using the main topic programmed in the WLED UI. -To install: +Installation of file: Copy and replace file in wled00 directory -Add the entries in the WLED00 file to the top of the same file from Aircoookies WLED. -Replace the WLED06_usermod.ino file in Aircoookies WLED folder. +## Project link + +* [QuinLED-Dig-Uno](https://quinled.info/2018/09/15/quinled-dig-uno/) - Project link + +### Platformio requirements + +Uncomment `DallasTemperature@~3.8.0`,`OneWire@~2.3.5 under` `[common]` section in `platformio.ini`: + +```ini +# platformio.ini +... +[platformio] +... +; default_envs = esp07 +default_envs = d1_mini +... +[common] +... +lib_deps_external = + ... + #For use SSD1306 OLED display uncomment following + U8g2@~2.27.3 + #For Dallas sensor uncomment following 2 lines + DallasTemperature@~3.8.0 + OneWire@~2.3.5 +... +``` diff --git a/usermods/QuinLED_Dig_Uno_Temp_MQTT/wled06_usermod.ino b/usermods/QuinLED_Dig_Uno_Temp_MQTT/usermod.cpp similarity index 61% rename from usermods/QuinLED_Dig_Uno_Temp_MQTT/wled06_usermod.ino rename to usermods/QuinLED_Dig_Uno_Temp_MQTT/usermod.cpp index 1309a4f8..5b4e2e5c 100644 --- a/usermods/QuinLED_Dig_Uno_Temp_MQTT/wled06_usermod.ino +++ b/usermods/QuinLED_Dig_Uno_Temp_MQTT/usermod.cpp @@ -1,8 +1,21 @@ -//starts Dallas Temp service on boot +#include +#include "wled.h" +//Intiating code for QuinLED Dig-Uno temp sensor +//Uncomment Celsius if that is your prefered temperature scale +#include //Dallastemperature sensor +#ifdef ARDUINO_ARCH_ESP32 //ESP32 boards +OneWire oneWire(18); +#else //ESP8266 boards +OneWire oneWire(14); +#endif +DallasTemperature sensor(&oneWire); +long temptimer = millis(); +long lastMeasure = 0; +#define Celsius // Show temperature mesaurement in Celcius otherwise is in Fahrenheit void userSetup() { // Start the DS18B20 sensor - sensors.begin(); + sensor.begin(); } //gets called every time WiFi is (re-)connected. Initialize own network interfaces here @@ -21,11 +34,11 @@ void userLoop() //Check if MQTT Connected, otherwise it will crash the 8266 if (mqtt != nullptr){ - sensors.requestTemperatures(); + sensor.requestTemperatures(); //Gets prefered temperature scale based on selection in definitions section #ifdef Celsius - float board_temperature = sensors.getTempCByIndex(0); + float board_temperature = sensor.getTempCByIndex(0); #else float board_temperature = sensors.getTempFByIndex(0); #endif diff --git a/usermods/QuinLED_Dig_Uno_Temp_MQTT/wled00.txt b/usermods/QuinLED_Dig_Uno_Temp_MQTT/wled00.txt deleted file mode 100644 index 661a7e24..00000000 --- a/usermods/QuinLED_Dig_Uno_Temp_MQTT/wled00.txt +++ /dev/null @@ -1,8 +0,0 @@ -//Intiating code for QuinLED Dig-Uno temp sensor -//Uncomment Celsius if that is your prefered temperature scale -#include -OneWire oneWire(14); -DallasTemperature sensors(&oneWire); -long temptimer = millis(); -long lastMeasure = 0; -//#define Celsius diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md b/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md new file mode 100644 index 00000000..eebc50da --- /dev/null +++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md @@ -0,0 +1,71 @@ +# Wemos D1 mini and Wemos32 mini shield +- Installation of file: Copy and replace file in wled00 directory +- For BME280 sensor use usermod_bme280.cpp. Copy to wled00 and rename to usermod.cpp +- Added third choice of controller Heltec WiFi-Kit-8. Totally DIY but with OLED display. +## Project repository +- [Original repository](https://github.com/srg74/WLED-wemos-shield) - WLED Wemos shield repository +- [Wemos shield project Wiki](https://github.com/srg74/WLED-wemos-shield/wiki) +- [Precompiled WLED firmware](https://github.com/srg74/WLED-wemos-shield/tree/master/resources/Firmware) +## Features +- SSD1306 128x32 or 128x64 I2C OLED display +- On screen IP address, SSID and controller status (e.g. ON or OFF, recent effect) +- Auto display shutoff for saving display lifetime +- Dallas temperature sensor +- Reporting temperature to MQTT broker +- Relay for energy saving + +## Hardware +![Shield](https://github.com/srg74/WLED-wemos-shield/blob/master/resources/Images/Assembly_8.jpg) + +## Functionality checked with + +- Wemos D1 mini original v3.1 and clones +- Wemos32 mini +- PlatformIO +- SSD1306 128x32 I2C OLED display +- DS18B20 (temperature sensor) +- BME280 (temperature, humidity and pressure sensor) +- Push button (N.O. momentary switch) + +### Platformio requirements + +For Dallas sensor uncomment `U8g2@~2.27.3`,`DallasTemperature@~3.8.0`,`OneWire@~2.3.5 under` `[common]` section in `platformio.ini`: +```ini +# platformio.ini +... +[platformio] +... +; default_envs = esp07 +default_envs = d1_mini +... +[common] +... +lib_deps_external = + ... + #For use SSD1306 OLED display uncomment following + U8g2@~2.27.3 + #For Dallas sensor uncomment following 2 lines + DallasTemperature@~3.8.0 + OneWire@~2.3.5 +... +``` + +For BME280 sensor uncomment `U8g2@~2.27.3`,`BME280@~3.0.0 under` `[common]` section in `platformio.ini`: +```ini +# platformio.ini +... +[platformio] +... +; default_envs = esp07 +default_envs = d1_mini +... +[common] +... +lib_deps_external = + ... + #For use SSD1306 OLED display uncomment following + U8g2@~2.27.3 + #For BME280 sensor uncomment following + BME280@~3.0.0 +... +``` diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod.cpp b/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod.cpp new file mode 100644 index 00000000..a93b20c9 --- /dev/null +++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod.cpp @@ -0,0 +1,246 @@ +#include "wled.h" +#include +#include // from https://github.com/olikraus/u8g2/ +#include // Dallas temperature sensor + +//Dallas sensor quick reading. Credit to - Author: Peter Scargill, August 17th, 2013 +int16_t Dallas(int x, byte start) +{ + OneWire DallasSensor(x); + byte i; + byte data[2]; + int16_t result; + do + { + DallasSensor.reset(); + DallasSensor.write(0xCC); + DallasSensor.write(0xBE); + for ( i = 0; i < 2; i++) data[i] = DallasSensor.read(); + result=(data[1]<<8)|data[0]; + result>>=4; if (data[1]&128) result|=61440; + if (data[0]&8) ++result; + DallasSensor.reset(); + DallasSensor.write(0xCC); + DallasSensor.write(0x44,1); + if (start) delay(1000); + } while (start--); + return result; +} +#ifdef ARDUINO_ARCH_ESP32 +uint8_t SCL_PIN = 22; +uint8_t SDA_PIN = 21; +uint8_t DALLAS_PIN =23; +#else +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 +#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 + +// Dallas sensor reading timer +long temptimer = millis(); +long lastMeasure = 0; +#define Celsius // Show temperature mesaurement in Celcius otherwise is in Fahrenheit + +// If display does not work or looks corrupted check the +// constructor reference: +// https://github.com/olikraus/u8g2/wiki/u8x8setupcpp +// or check the gallery: +// https://github.com/olikraus/u8g2/wiki/gallery +// --> First choise of cheap I2C OLED 128X32 0.91" +U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA +// --> Second choise of cheap I2C OLED 128X64 0.96" or 1.3" +//U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA +// --> Third choise of Heltec WiFi-Kit-8 OLED 128X32 0.91" +//U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_RESET, U8X8_PIN_SCL, U8X8_PIN_SDA); // Constructor for Heltec WiFi-Kit-8 +// gets called once at boot. Do all initialization that doesn't depend on network here +void userSetup() { +//Serial.begin(115200); + + Dallas (DALLAS_PIN,1); + u8x8.begin(); + u8x8.setPowerSave(0); + u8x8.setFlipMode(1); + u8x8.setContrast(10); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255 + u8x8.setFont(u8x8_font_chroma48medium8_r); + u8x8.drawString(0, 0, "Loading..."); +} + +// gets called every time WiFi is (re-)connected. Initialize own network +// interfaces here +void userConnected() {} + +// needRedraw marks if redraw is required to prevent often redrawing. +bool needRedraw = true; + +// Next variables hold the previous known values to determine if redraw is +// required. +String knownSsid = ""; +IPAddress knownIp; +uint8_t knownBrightness = 0; +uint8_t knownMode = 0; +uint8_t knownPalette = 0; + +long lastUpdate = 0; +long lastRedraw = 0; +bool displayTurnedOff = false; +// How often we are redrawing screen +#define USER_LOOP_REFRESH_RATE_MS 5000 + +void userLoop() { + +//----> Dallas temperature sensor MQTT publishing + temptimer = millis(); +// Timer to publishe new temperature every 60 seconds + if (temptimer - lastMeasure > 60000) + { + lastMeasure = temptimer; +//Check if MQTT Connected, otherwise it will crash the 8266 + if (mqtt != nullptr) + { +// Serial.println(Dallas(DALLAS_PIN,0)); +//Gets prefered temperature scale based on selection in definitions section + #ifdef Celsius + int16_t board_temperature = Dallas(DALLAS_PIN,0); + #else + int16_t board_temperature = (Dallas(DALLAS_PIN,0)* 1.8 + 32); + #endif +//Create character string populated with user defined device topic from the UI, and the read temperature. Then publish to MQTT server. + String t = String(mqttDeviceTopic); + t += "/temperature"; + mqtt->publish(t.c_str(), 0, true, String(board_temperature).c_str()); + } + } + + // Check if we time interval for redrawing passes. + if (millis() - lastUpdate < USER_LOOP_REFRESH_RATE_MS) { + return; + } + lastUpdate = millis(); + + // Turn off display after 3 minutes with no change. + if(!displayTurnedOff && millis() - lastRedraw > 3*60*1000) { + u8x8.setPowerSave(1); + displayTurnedOff = true; + } + + // Check if values which are shown on display changed from the last time. + if (((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid) { + needRedraw = true; + } else if (knownIp != (apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP())) { + needRedraw = true; + } else if (knownBrightness != bri) { + needRedraw = true; + } else if (knownMode != strip.getMode()) { + needRedraw = true; + } else if (knownPalette != strip.getSegment(0).palette) { + needRedraw = true; + } + + if (!needRedraw) { + return; + } + needRedraw = false; + + if (displayTurnedOff) + { + u8x8.setPowerSave(0); + displayTurnedOff = false; + } + lastRedraw = millis(); + + // Update last known values. + #ifdef ARDUINO_ARCH_ESP32 + knownSsid = WiFi.SSID(); + #else + knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID(); + #endif + knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP(); + knownBrightness = bri; + knownMode = strip.getMode(); + knownPalette = strip.getSegment(0).palette; + u8x8.clear(); + u8x8.setFont(u8x8_font_chroma48medium8_r); + + // First row with Wifi name + u8x8.setCursor(1, 0); + u8x8.print(knownSsid.substring(0, u8x8.getCols() > 1 ? u8x8.getCols() - 2 : 0)); + // Print `~` char to indicate that SSID is longer, than owr dicplay + if (knownSsid.length() > u8x8.getCols()) + u8x8.print("~"); + + // Second row with IP or Psssword + u8x8.setCursor(1, 1); + // Print password in AP mode and if led is OFF. + if (apActive && bri == 0) + u8x8.print(apPass); + else + u8x8.print(knownIp); + + // Third row with mode name + u8x8.setCursor(2, 2); + uint8_t qComma = 0; + bool insideQuotes = false; + uint8_t printedChars = 0; + char singleJsonSymbol; + + // Find the mode name in JSON + for (size_t i = 0; i < strlen_P(JSON_mode_names); i++) { + singleJsonSymbol = pgm_read_byte_near(JSON_mode_names + i); + switch (singleJsonSymbol) { + case '"': + insideQuotes = !insideQuotes; + break; + case '[': + case ']': + break; + case ',': + qComma++; + default: + if (!insideQuotes || (qComma != knownMode)) + break; + u8x8.print(singleJsonSymbol); + printedChars++; + } + if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2)) + break; + } + // Fourth row with palette name + u8x8.setCursor(2, 3); + qComma = 0; + insideQuotes = false; + printedChars = 0; + // Looking for palette name in JSON. + for (size_t i = 0; i < strlen_P(JSON_palette_names); i++) { + singleJsonSymbol = pgm_read_byte_near(JSON_palette_names + i); + switch (singleJsonSymbol) { + case '"': + insideQuotes = !insideQuotes; + break; + case '[': + case ']': + break; + case ',': + qComma++; + default: + if (!insideQuotes || (qComma != knownPalette)) + break; + u8x8.print(singleJsonSymbol); + printedChars++; + } + if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2)) + break; + } + + u8x8.setFont(u8x8_font_open_iconic_embedded_1x1); + u8x8.drawGlyph(0, 0, 80); // wifi icon + u8x8.drawGlyph(0, 1, 68); // home icon + u8x8.setFont(u8x8_font_open_iconic_weather_2x2); + u8x8.drawGlyph(0, 2, 66 + (bri > 0 ? 3 : 0)); // sun/moon icon +} diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod_bme280.cpp b/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod_bme280.cpp new file mode 100644 index 00000000..15ec58ad --- /dev/null +++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod_bme280.cpp @@ -0,0 +1,268 @@ +#include "wled.h" +#include +#include // from https://github.com/olikraus/u8g2/ +#include +#include //BME280 sensor + +void UpdateBME280Data(); + +#define Celsius // Show temperature mesaurement in Celcius otherwise is in Fahrenheit +BME280I2C bme; // Default : forced mode, standby time = 1000 ms + // Oversampling = pressure ×1, temperature ×1, humidity ×1, filter off, + +#ifdef ARDUINO_ARCH_ESP32 //ESP32 boards +uint8_t SCL_PIN = 22; +uint8_t SDA_PIN = 21; +#else //ESP8266 boards +uint8_t SCL_PIN = 5; +uint8_t SDA_PIN = 4; +// uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8 +#endif + +//The SCL and SDA pins are defined here. +//ESP8266 Wemos D1 mini board use SCL=5 SDA=4 while ESP32 Wemos32 mini board use SCL=22 SDA=21 +#define U8X8_PIN_SCL SCL_PIN +#define U8X8_PIN_SDA SDA_PIN +//#define U8X8_PIN_RESET RST_PIN // Uncoment for Heltec WiFi-Kit-8 + +// If display does not work or looks corrupted check the +// constructor reference: +// https://github.com/olikraus/u8g2/wiki/u8x8setupcpp +// or check the gallery: +// https://github.com/olikraus/u8g2/wiki/gallery +// --> First choise of cheap I2C OLED 128X32 0.91" +U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA +// --> Second choise of cheap I2C OLED 128X64 0.96" or 1.3" +//U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // Pins are Reset, SCL, SDA +// --> Third choise of Heltec WiFi-Kit-8 OLED 128X32 0.91" +//U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_RESET, U8X8_PIN_SCL, U8X8_PIN_SDA); // Constructor for Heltec WiFi-Kit-8 +// gets called once at boot. Do all initialization that doesn't depend on network here + +// BME280 sensor timer +long tempTimer = millis(); +long lastMeasure = 0; + +float SensorPressure(NAN); +float SensorTemperature(NAN); +float SensorHumidity(NAN); + +void userSetup() { + u8x8.begin(); + u8x8.setPowerSave(0); + u8x8.setFlipMode(1); + u8x8.setContrast(10); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255 + u8x8.setFont(u8x8_font_chroma48medium8_r); + u8x8.drawString(0, 0, "Loading..."); + Wire.begin(SDA_PIN,SCL_PIN); + +while(!bme.begin()) + { + Serial.println("Could not find BME280I2C sensor!"); + delay(1000); + } +switch(bme.chipModel()) + { + case BME280::ChipModel_BME280: + Serial.println("Found BME280 sensor! Success."); + break; + case BME280::ChipModel_BMP280: + Serial.println("Found BMP280 sensor! No Humidity available."); + break; + default: + Serial.println("Found UNKNOWN sensor! Error!"); + } +} + +// gets called every time WiFi is (re-)connected. Initialize own network +// interfaces here +void userConnected() {} + +// needRedraw marks if redraw is required to prevent often redrawing. +bool needRedraw = true; + +// Next variables hold the previous known values to determine if redraw is +// required. +String knownSsid = ""; +IPAddress knownIp; +uint8_t knownBrightness = 0; +uint8_t knownMode = 0; +uint8_t knownPalette = 0; + +long lastUpdate = 0; +long lastRedraw = 0; +bool displayTurnedOff = false; +// How often we are redrawing screen +#define USER_LOOP_REFRESH_RATE_MS 5000 + +void userLoop() { + +// BME280 sensor MQTT publishing + tempTimer = millis(); +// Timer to publish new sensor data every 60 seconds + if (tempTimer - lastMeasure > 60000) + { + lastMeasure = tempTimer; + +// Check if MQTT Connected, otherwise it will crash the 8266 + if (mqtt != nullptr) + { + UpdateBME280Data(); + float board_temperature = SensorTemperature; + float board_pressure = SensorPressure; + float board_humidity = SensorHumidity; + +// Create string populated with user defined device topic from the UI, and the read temperature, humidity and pressure. Then publish to MQTT server. + String t = String(mqttDeviceTopic); + t += "/temperature"; + mqtt->publish(t.c_str(), 0, true, String(board_temperature).c_str()); + String p = String(mqttDeviceTopic); + p += "/pressure"; + mqtt->publish(p.c_str(), 0, true, String(board_pressure).c_str()); + String h = String(mqttDeviceTopic); + h += "/humidity"; + mqtt->publish(h.c_str(), 0, true, String(board_humidity).c_str()); + } + } + + // Check if we time interval for redrawing passes. + if (millis() - lastUpdate < USER_LOOP_REFRESH_RATE_MS) { + return; + } + lastUpdate = millis(); + + // Turn off display after 3 minutes with no change. + if(!displayTurnedOff && millis() - lastRedraw > 3*60*1000) { + u8x8.setPowerSave(1); + displayTurnedOff = true; + } + + // Check if values which are shown on display changed from the last time. + if (((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid) { + needRedraw = true; + } else if (knownIp != (apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP())) { + needRedraw = true; + } else if (knownBrightness != bri) { + needRedraw = true; + } else if (knownMode != strip.getMode()) { + needRedraw = true; + } else if (knownPalette != strip.getSegment(0).palette) { + needRedraw = true; + } + + if (!needRedraw) { + return; + } + needRedraw = false; + + if (displayTurnedOff) + { + u8x8.setPowerSave(0); + displayTurnedOff = false; + } + lastRedraw = millis(); + + // Update last known values. + #if defined(ESP8266) + knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID(); + #else + knownSsid = WiFi.SSID(); + #endif + knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP(); + knownBrightness = bri; + knownMode = strip.getMode(); + knownPalette = strip.getSegment(0).palette; + u8x8.clear(); + u8x8.setFont(u8x8_font_chroma48medium8_r); + + // First row with Wifi name + u8x8.setCursor(1, 0); + u8x8.print(knownSsid.substring(0, u8x8.getCols() > 1 ? u8x8.getCols() - 2 : 0)); + // Print `~` char to indicate that SSID is longer, than owr dicplay + if (knownSsid.length() > u8x8.getCols()) + u8x8.print("~"); + + // Second row with IP or Psssword + u8x8.setCursor(1, 1); + // Print password in AP mode and if led is OFF. + if (apActive && bri == 0) + u8x8.print(apPass); + else + u8x8.print(knownIp); + + // Third row with mode name + u8x8.setCursor(2, 2); + uint8_t qComma = 0; + bool insideQuotes = false; + uint8_t printedChars = 0; + char singleJsonSymbol; + + // Find the mode name in JSON + for (size_t i = 0; i < strlen_P(JSON_mode_names); i++) { + singleJsonSymbol = pgm_read_byte_near(JSON_mode_names + i); + switch (singleJsonSymbol) { + case '"': + insideQuotes = !insideQuotes; + break; + case '[': + case ']': + break; + case ',': + qComma++; + default: + if (!insideQuotes || (qComma != knownMode)) + break; + u8x8.print(singleJsonSymbol); + printedChars++; + } + if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2)) + break; + } + // Fourth row with palette name + u8x8.setCursor(2, 3); + qComma = 0; + insideQuotes = false; + printedChars = 0; + // Looking for palette name in JSON. + for (size_t i = 0; i < strlen_P(JSON_palette_names); i++) { + singleJsonSymbol = pgm_read_byte_near(JSON_palette_names + i); + switch (singleJsonSymbol) { + case '"': + insideQuotes = !insideQuotes; + break; + case '[': + case ']': + break; + case ',': + qComma++; + default: + if (!insideQuotes || (qComma != knownPalette)) + break; + u8x8.print(singleJsonSymbol); + printedChars++; + } + if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2)) + break; + } + + u8x8.setFont(u8x8_font_open_iconic_embedded_1x1); + u8x8.drawGlyph(0, 0, 80); // wifi icon + u8x8.drawGlyph(0, 1, 68); // home icon + u8x8.setFont(u8x8_font_open_iconic_weather_2x2); + u8x8.drawGlyph(0, 2, 66 + (bri > 0 ? 3 : 0)); // sun/moon icon +} + +void UpdateBME280Data() { + float temp(NAN), hum(NAN), pres(NAN); +#ifdef Celsius + BME280::TempUnit tempUnit(BME280::TempUnit_Celsius); + BME280::PresUnit presUnit(BME280::PresUnit_Pa); + bme.read(pres, temp, hum, tempUnit, presUnit); +#else + BME280::TempUnit tempUnit(BME280::TempUnit_Fahrenheit); + BME280::PresUnit presUnit(BME280::PresUnit_Pa); + bme.read(pres, temp, hum, tempUnit, presUnit); +#endif + SensorTemperature=temp; + SensorHumidity=hum; + SensorPressure=pres; +} diff --git a/usermods/battery_keypad_controller/README.md b/usermods/battery_keypad_controller/README.md new file mode 100644 index 00000000..77e3c1e4 --- /dev/null +++ b/usermods/battery_keypad_controller/README.md @@ -0,0 +1,12 @@ +# Battery powered controller with keypad + +I'm using this controller for a festival totem. Runs on 3 18650 Cells, can deliver >5A current. + +Via keypad one can select 8 presets, change effect, effect speed, effect intensity and palette. Brightness can be +adjusted with a potentiometer. + +## Pictures + +![bat-key-ctrl-1](assets/bat-key-ctrl-1.jpg) +![bat-key-ctrl-2](assets/bat-key-ctrl-2.jpg) +![bat-key-ctrl-3](assets/bat-key-ctrl-3.jpg) diff --git a/usermods/battery_keypad_controller/assets/bat-key-ctrl-1.jpg b/usermods/battery_keypad_controller/assets/bat-key-ctrl-1.jpg new file mode 100644 index 00000000..f352ba3a Binary files /dev/null and b/usermods/battery_keypad_controller/assets/bat-key-ctrl-1.jpg differ diff --git a/usermods/battery_keypad_controller/assets/bat-key-ctrl-2.jpg b/usermods/battery_keypad_controller/assets/bat-key-ctrl-2.jpg new file mode 100644 index 00000000..2bf359d3 Binary files /dev/null and b/usermods/battery_keypad_controller/assets/bat-key-ctrl-2.jpg differ diff --git a/usermods/battery_keypad_controller/assets/bat-key-ctrl-3.jpg b/usermods/battery_keypad_controller/assets/bat-key-ctrl-3.jpg new file mode 100644 index 00000000..892b13ff Binary files /dev/null and b/usermods/battery_keypad_controller/assets/bat-key-ctrl-3.jpg differ diff --git a/usermods/battery_keypad_controller/wled06_usermod.ino b/usermods/battery_keypad_controller/wled06_usermod.ino new file mode 100644 index 00000000..877713b6 --- /dev/null +++ b/usermods/battery_keypad_controller/wled06_usermod.ino @@ -0,0 +1,151 @@ +/* + * WLED usermod for keypad and brightness-pot. + * 3'2020 https://github.com/hobbyquaker + */ + +#include +const byte keypad_rows = 4; +const byte keypad_cols = 4; +char keypad_keys[keypad_rows][keypad_cols] = { + {'1', '2', '3', 'A'}, + {'4', '5', '6', 'B'}, + {'7', '8', '9', 'C'}, + {'*', '0', '#', 'D'} +}; + +byte keypad_colPins[keypad_rows] = {D3, D2, D1, D0}; +byte keypad_rowPins[keypad_cols] = {D7, D6, D5, D4}; + +Keypad myKeypad = Keypad(makeKeymap(keypad_keys), keypad_rowPins, keypad_colPins, keypad_rows, keypad_cols); + +void userSetup() +{ + +} + +void userConnected() +{ + +} + +long lastTime = 0; +int delayMs = 20; //we want to do something every 2 seconds + +void userLoop() +{ + if (millis()-lastTime > delayMs) + { + + long analog = analogRead(0); + int new_bri = 1; + if (analog > 900) { + new_bri = 255; + } else if (analog > 30) { + new_bri = dim8_video(map(analog, 31, 900, 16, 255)); + } + if (bri != new_bri) { + bri = new_bri; + colorUpdated(1); + + } + + char myKey = myKeypad.getKey(); + if (myKey != NULL) { + switch (myKey) { + case '1': + applyPreset(1); + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + case '2': + applyPreset(2); + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + case '3': + applyPreset(3); + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + case '4': + applyPreset(4); + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + case '5': + applyPreset(5); + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + case '6': + applyPreset(6); + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + case 'A': + applyPreset(7); + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + case 'B': + applyPreset(8); + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + + case '7': + effectCurrent += 1; + if (effectCurrent >= MODE_COUNT) effectCurrent = 0; + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + case '*': + effectCurrent -= 1; + if (effectCurrent < 0) effectCurrent = (MODE_COUNT-1); + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + + case '8': + if (effectSpeed < 240) { + effectSpeed += 12; + } else if (effectSpeed < 255) { + effectSpeed += 1; + } + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + case '0': + if (effectSpeed > 15) { + effectSpeed -= 12; + } else if (effectSpeed > 0) { + effectSpeed -= 1; + } + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + + case '9': + if (effectIntensity < 240) { + effectIntensity += 12; + } else if (effectIntensity < 255) { + effectIntensity += 1; + } + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + case '#': + if (effectIntensity > 15) { + effectIntensity -= 12; + } else if (effectIntensity > 0) { + effectIntensity -= 1; + } + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + + case 'C': + effectPalette += 1; + if (effectPalette >= 50) effectPalette = 0; + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + case 'D': + effectPalette -= 1; + if (effectPalette <= 0) effectPalette = 50; + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); + break; + + } + + } + + lastTime = millis(); + } + +} \ No newline at end of file diff --git a/usermods/blynk_relay_control/README.md b/usermods/blynk_relay_control/README.md new file mode 100644 index 00000000..b6494b46 --- /dev/null +++ b/usermods/blynk_relay_control/README.md @@ -0,0 +1,28 @@ +# Blynk controllable relay +This usermod allows controlling a relay state from the user variables. It also allows the user variables to be set over Blynk. + +Optionally, the servo can have a reset timer to go back to it's default state after an interval. This interval is set through userVar1. + +## Instalation + +Replace the WLED06_usermod.ino file in Aircoookies WLED folder with the one here. + +## Customizations + +Update the following parameters in WLED06_usermod.ino to configure the mod's behavior: + +```cpp +//Which pin is the relay connected to +#define RELAY_PIN 5 +//Which pin state should the relay default to +#define RELAY_PIN_DEFAULT LOW +//If >0 The controller returns to RELAY_PIN_DEFAULT after this time in milliseconds +#define RELAY_PIN_TIMER_DEFAULT 3000 + +//Blynk virtual pin for controlling relay +#define BLYNK_USER_VAR0_PIN V9 +//Blynk virtual pin for controlling relay timer +#define BLYNK_USER_VAR1_PIN V10 +//Number of milliseconds between updating blynk +#define BLYNK_RELAY_UPDATE_INTERVAL 5000 +``` diff --git a/usermods/blynk_relay_control/wled06_usermod.ino b/usermods/blynk_relay_control/wled06_usermod.ino new file mode 100644 index 00000000..d4028ea5 --- /dev/null +++ b/usermods/blynk_relay_control/wled06_usermod.ino @@ -0,0 +1,96 @@ +/* + * 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 wled_eeprom.h) + * bytes 2400+ are currently ununsed, but might be used for future wled features + */ + +//Use userVar0 (API calls &U0=, uint16_t) to set relay state +#define relayPinState userVar0 +//Use userVar1 (API calls &U1=, uint16_t) to set relay timer duration +//Ignored if 0, otherwise number of milliseconds to allow relay to stay in +//non default state. +#define relayTimerInterval userVar1 + +//Which pin is the relay connected to +#define RELAY_PIN 5 +//Which pin state should the relay default to +#define RELAY_PIN_DEFAULT LOW +//If >0 The controller returns to RELAY_PIN_DEFAULT after this time in milliseconds +#define RELAY_PIN_TIMER_DEFAULT 3000 + +//Blynk virtual pin for controlling relay +#define BLYNK_USER_VAR0_PIN V9 +//Blynk virtual pin for controlling relay timer +#define BLYNK_USER_VAR1_PIN V10 +//Number of milliseconds between updating blynk +#define BLYNK_RELAY_UPDATE_INTERVAL 5000 + +//Is the timer for resetting the relay active +bool relayTimerStarted = false; +//millis() time after which relay will be reset +unsigned long relayTimeToDefault = 0; +//millis() time after which relay vars in Blynk will be sent +unsigned long relayBlynkUpdateTime = 0; + +//gets called once at boot. Do all initialization that doesn't depend on network here +void userSetup() +{ + relayPinState = RELAY_PIN_DEFAULT; + relayTimerInterval = RELAY_PIN_TIMER_DEFAULT; + pinMode(RELAY_PIN, OUTPUT); + digitalWrite(RELAY_PIN, relayPinState); +} + +//gets called every time WiFi is (re-)connected. Initialize own network interfaces here +void userConnected() +{ +} + +//loop. You can use "if (WLED_CONNECTED)" to check for successful connection +void userLoop() +{ + //Normalize relayPinState to an accepted value + if (relayPinState != HIGH && relayPinState != LOW) { + relayPinState = RELAY_PIN_DEFAULT; + } + //If relay changes and relayTimerInterval is set, start a timer to change back + if (relayTimerInterval != 0 && + relayPinState != RELAY_PIN_DEFAULT && + !relayTimerStarted ) { + relayTimerStarted = true; + relayTimeToDefault = millis() + relayTimerInterval; + } + //If manually changed back to default, cancel timer + if (relayTimerStarted && relayPinState == RELAY_PIN_DEFAULT ) { + relayTimerStarted = false; + } + //If timer completes, set relay back to default + if (relayTimerStarted && millis() > relayTimeToDefault) { + relayPinState = RELAY_PIN_DEFAULT; + relayTimerStarted = false; + } + digitalWrite(RELAY_PIN, relayPinState); + updateRelayBlynk(); +} + +//Update Blynk with state of userVars at BLYNK_RELAY_UPDATE_INTERVAL +void updateRelayBlynk() +{ + if (!WLED_CONNECTED) return; + if (relayBlynkUpdateTime > millis()) return; + Blynk.virtualWrite(BLYNK_USER_VAR0_PIN, userVar0); + Blynk.virtualWrite(BLYNK_USER_VAR1_PIN, userVar1); + relayBlynkUpdateTime = millis() + BLYNK_RELAY_UPDATE_INTERVAL; +} + +//Add Blynk callback for setting userVar0 +BLYNK_WRITE(BLYNK_USER_VAR0_PIN) +{ + userVar0 = param.asInt(); +} +//Add Blynk callback for setting userVar1 +BLYNK_WRITE(BLYNK_USER_VAR1_PIN) +{ + userVar1 = param.asInt(); +} diff --git a/usermods/project_cars_shiftlight/readme.md b/usermods/project_cars_shiftlight/readme.md new file mode 100644 index 00000000..4490a2ba --- /dev/null +++ b/usermods/project_cars_shiftlight/readme.md @@ -0,0 +1,23 @@ +### Shift Light for Project Cars + +Turn your WLED lights into a rev light and shift indicator for Project Cars. + +It is pretty straight forward to use. + +1. Make sure, your WLED device and your PC/console are on the same network and can talk to each other + +2. Go to the gameplay settings menu in PCARS and enable UDP. There are 9 numbers you can choose from. This is the refresh rate. The lower the number, the better. But you might run into problems at faster rates. + +| Number | Updates/Second | +| ------ | -------------- | +| 1 | 60 | +| 2 | 50 | +| 3 | 40 | +| 4 | 30 | +| 5 | 20 | +| 6 | 15 | +| 7 | 10 | +| 8 | 05 | +| 9 | 1 | + +3. once you enter a race, WLED should automatically shift to PCARS mode. Done. diff --git a/usermods/project_cars_shiftlight/wled06_usermod.ino b/usermods/project_cars_shiftlight/wled06_usermod.ino new file mode 100644 index 00000000..b00c2946 --- /dev/null +++ b/usermods/project_cars_shiftlight/wled06_usermod.ino @@ -0,0 +1,96 @@ +/* + * Car rev display and shift indicator for Project Cars + * + * This works via the UDP telemetry function. You'll need to enable it in the settings of the game. + * I've had good results with settings around 5 (20 fps). + * + */ +const uint8_t PCARS_dimcolor = 20; +WiFiUDP UDP; +const unsigned int PCARS_localUdpPort = 5606; // local port to listen on +char PCARS_packet[2048]; + +char PCARS_tempChar[2]; // Temporary array for u16 conversion + +u16 PCARS_RPM; +u16 PCARS_maxRPM; + +long PCARS_lastRead = millis() - 2001; +float PCARS_rpmRatio; + + +void userSetup() +{ + UDP.begin(PCARS_localUdpPort); +} + +void userConnected() +{ + // new wifi, who dis? +} + +void userLoop() +{ + PCARS_readValues(); + if (PCARS_lastRead > millis() - 2000) { + PCARS_buildcolorbars(); + } +} + +void PCARS_readValues() { + + int PCARS_packetSize = UDP.parsePacket(); + if (PCARS_packetSize) { + int len = UDP.read(PCARS_packet, PCARS_packetSize); + if (len > 0) { + PCARS_packet[len] = 0; + } + if (len == 1367) { // Telemetry packet. Ignoring everything else. + PCARS_lastRead = millis(); + + arlsLock(realtimeTimeoutMs, REALTIME_MODE_GENERIC); + // current RPM + memcpy(&PCARS_tempChar, &PCARS_packet[124], 2); + PCARS_RPM = (PCARS_tempChar[1] << 8) + PCARS_tempChar[0]; + + // max RPM + memcpy(&PCARS_tempChar, &PCARS_packet[126], 2); + PCARS_maxRPM = (PCARS_tempChar[1] << 8) + PCARS_tempChar[0]; + + if (PCARS_maxRPM) { + PCARS_rpmRatio = constrain((float)PCARS_RPM / (float)PCARS_maxRPM, 0, 1); + } else { + PCARS_rpmRatio = 0.0; + } + } + } +} +void PCARS_buildcolorbars() { + boolean activated = false; + float ledratio = 0; + + for (uint16_t i = 0; i < ledCount; i++) { + if (PCARS_rpmRatio < .95 || (millis() % 100 > 70 )) { + + ledratio = (float)i / (float)ledCount; + if (ledratio < PCARS_rpmRatio) { + activated = true; + } else { + activated = false; + } + if (ledratio > 0.66) { + setRealtimePixel(i, 0, 0, PCARS_dimcolor + ((255 - PCARS_dimcolor)*activated), 0); + } else if (ledratio > 0.33) { + setRealtimePixel(i, PCARS_dimcolor + ((255 - PCARS_dimcolor)*activated), 0, 0, 0); + } else { + setRealtimePixel(i, 0, PCARS_dimcolor + ((255 - PCARS_dimcolor)*activated), 0, 0); + } + } + else { + setRealtimePixel(i, 0, 0, 0, 0); + + } + } + colorUpdated(5); + strip.show(); +} \ No newline at end of file diff --git a/usermods/readme.md b/usermods/readme.md index 8cd2d486..34ddef0f 100644 --- a/usermods/readme.md +++ b/usermods/readme.md @@ -1,18 +1,18 @@ ### Usermods -This folder serves as a repository for usermods (custom `wled06_usermod.ino` files)! +This folder serves as a repository for usermods (custom `usermod.cpp` files)! If you have created an usermod that you believe is useful (for example to support a particular sensor, display, feature...), feel free to contribute by opening a pull request! In order for other people to be able to have fun with your usermod, please keep these points in mind: - Create a folder in this folder with a descriptive name (for example `usermod_ds18b20_temp_sensor_mqtt`) -- Include your custom `wled06_usermod.ino` file -- If your usermod requieres changes to other WLED files, please write a `readme.md` outlining the steps one has to take to use the usermod +- Include your custom `usermod.cpp` file +- If your usermod requires changes to other WLED files, please write a `readme.md` outlining the steps one has to take to use the usermod - Create a pull request! - If your feature is useful for the majority of WLED users, I will consider adding it to the base code! While I do my best to not break too much, keep in mind that as WLED is being updated, usermods might break. I am not actively maintaining any usermod in this directory, that is your responsibility as the creator of the usermod. -Thank you for your help :) \ No newline at end of file +Thank you for your help :) diff --git a/usermods/rotary_encoder_change_brightness/usermod.cpp b/usermods/rotary_encoder_change_brightness/usermod.cpp new file mode 100644 index 00000000..8fae6120 --- /dev/null +++ b/usermods/rotary_encoder_change_brightness/usermod.cpp @@ -0,0 +1,62 @@ +#include "wled.h" +/* + * 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 + */ + +//Use userVar0 and userVar1 (API calls &U0=,&U1=, uint16_t) + +/* +** Rotary Encoder Example +** Use the Sparkfun Rotary Encoder to vary brightness of LED +** +** Sample the encoder at 500Hz using the millis() function +*/ + +int fadeAmount = 5; // how many points to fade the Neopixel with each step +unsigned long currentTime; +unsigned long loopTime; +const int pinA = D6; // DT from encoder +const int pinB = D7; // CLK from encoder + +unsigned char Enc_A; +unsigned char Enc_B; +unsigned char Enc_A_prev = 0; + +//gets called once at boot. Do all initialization that doesn't depend on network here +void userSetup() { + pinMode(pinA, INPUT_PULLUP); + pinMode(pinB, INPUT_PULLUP); + currentTime = millis(); + loopTime = currentTime; +} + +//gets called every time WiFi is (re-)connected. Initialize own network interfaces here +void userConnected() { +} + +//loop. You can use "if (WLED_CONNECTED)" to check for successful connection +void userLoop() { + currentTime = millis(); // get the current elapsed time + if(currentTime >= (loopTime + 2)) // 2ms since last check of encoder = 500Hz + { + 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 + if(bri + fadeAmount <= 255) bri += fadeAmount; // increase the brightness, dont go over 255 + + } else if (Enc_B == LOW) { // B is low so counter-clockwise + if(bri - fadeAmount >= 0) bri -= fadeAmount; // decrease the brightness, dont go below 0 + } + } + Enc_A_prev = Enc_A; // Store value of A for next time + loopTime = currentTime; // Updates loopTime + + //call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification) + // 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa + colorUpdated(6); + } +} diff --git a/usermods/rotary_encoder_change_effect/wled06_usermod.ino b/usermods/rotary_encoder_change_effect/wled06_usermod.ino index dbde9560..f004ba2f 100644 --- a/usermods/rotary_encoder_change_effect/wled06_usermod.ino +++ b/usermods/rotary_encoder_change_effect/wled06_usermod.ino @@ -39,7 +39,7 @@ void userLoop() { //call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification) // 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa - colorUpdated(6); + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); lastTime = millis(); } } diff --git a/usermods/stairway_wipe_basic/wled06_usermod.ino b/usermods/stairway_wipe_basic/wled06_usermod.ino index 9fafb679..0cc85df7 100644 --- a/usermods/stairway_wipe_basic/wled06_usermod.ino +++ b/usermods/stairway_wipe_basic/wled06_usermod.ino @@ -1,7 +1,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 wled01_eeprom.h) + * EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in wled_eeprom.h) * bytes 2400+ are currently ununsed, but might be used for future wled features */ @@ -11,6 +11,9 @@ byte wipeState = 0; //0: inactive 1: wiping 2: solid unsigned long timeStaticStart = 0; uint16_t previousUserVar0 = 0; +//comment this out if you want the turn off effect to be just fading out instead of reverse wipe +#define STAIRCASE_WIPE_OFF + //gets called once at boot. Do all initialization that doesn't depend on network here void userSetup() { @@ -44,7 +47,7 @@ void userLoop() if (millis() + strip.timebase > (cycleTime - 25)) { //wipe complete effectCurrent = FX_MODE_STATIC; timeStaticStart = millis(); - colorUpdated(3); + colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION); wipeState = 2; } } else if (wipeState == 2) { //static @@ -52,14 +55,28 @@ void userLoop() { if (millis() - timeStaticStart > userVar1*1000) wipeState = 3; } - } else { //wipeState == 3, turn off slowly + } else if (wipeState == 3) { //switch to wipe off + #ifdef STAIRCASE_WIPE_OFF + effectCurrent = FX_MODE_COLOR_WIPE; + strip.timebase = 360 + (255 - effectSpeed)*75 - millis(); //make sure wipe starts fully lit + colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION); + wipeState = 4; + #else turnOff(); - userVar0 = 0; - wipeState = 0; + #endif + } else { //wiping off + if (millis() + strip.timebase > (725 + (255 - effectSpeed)*150)) turnOff(); //wipe complete } } else { - if (previousUserVar0) turnOff(); wipeState = 0; //reset for next time + if (previousUserVar0) { + #ifdef STAIRCASE_WIPE_OFF + userVar0 = previousUserVar0; + wipeState = 3; + #else + turnOff(); + #endif + } previousUserVar0 = 0; } } @@ -76,12 +93,19 @@ void startWipe() bool doReverse = (userVar0 == 2); seg.setOption(1, doReverse); - colorUpdated(3); + colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION); } void turnOff() { - transitionDelayTemp = 4000; + #ifdef STAIRCASE_WIPE_OFF + transitionDelayTemp = 0; //turn off immediately after wipe completed + #else + transitionDelayTemp = 4000; //fade out slowly + #endif bri = 0; - colorUpdated(3); + colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION); + wipeState = 0; + userVar0 = 0; + previousUserVar0 = 0; } diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 4a535a86..c49c8304 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -35,7 +35,7 @@ */ uint16_t WS2812FX::mode_static(void) { fill(SEGCOLOR(0)); - return (SEGMENT.getOption(7)) ? FRAMETIME : 500; //update faster if in transition + return (SEGMENT.getOption(SEG_OPTION_TRANSITIONAL)) ? FRAMETIME : 500; //update faster if in transition } @@ -69,7 +69,7 @@ uint16_t WS2812FX::blink(uint32_t color1, uint32_t color2, bool strobe, bool do_ uint32_t color = ((SEGENV.aux0 & 1) == 0) ? color1 : color2; if (color == color1 && do_palette) { - for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) { + for(uint16_t i = 0; i < SEGLEN; i++) { setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } } else fill(color); @@ -149,18 +149,18 @@ uint16_t WS2812FX::color_wipe(bool rev, bool useRandomColors) { if (rem > 255) rem = 255; uint32_t col1 = useRandomColors? color_wheel(SEGENV.aux1) : SEGCOLOR(1); - for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) + for (uint16_t i = 0; i < SEGLEN; i++) { - uint16_t index = (rev && back)? SEGMENT.stop -1 -i +SEGMENT.start : i; + uint16_t index = (rev && back)? SEGLEN -1 -i : i; uint32_t col0 = useRandomColors? color_wheel(SEGENV.aux0) : color_from_palette(index, true, PALETTE_SOLID_WRAP, 0); - if (i - SEGMENT.start < ledIndex) + if (i < ledIndex) { setPixelColor(index, back? col1 : col0); } else { setPixelColor(index, back? col0 : col1); - if (i - SEGMENT.start == ledIndex) setPixelColor(index, color_blend(back? col0 : col1, back? col1 : col0, rem)); + if (i == ledIndex) setPixelColor(index, color_blend(back? col0 : col1, back? col1 : col0, rem)); } } return FRAMETIME; @@ -253,7 +253,7 @@ uint16_t WS2812FX::mode_dynamic(void) { } for (uint16_t i = 0; i < SEGLEN; i++) { - setPixelColor(SEGMENT.start + i, color_wheel(SEGENV.data[i])); + setPixelColor(i, color_wheel(SEGENV.data[i])); } return FRAMETIME; } @@ -272,7 +272,7 @@ uint16_t WS2812FX::mode_breath(void) { } uint8_t lum = 30 + var; - for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) { + for(uint16_t i = 0; i < SEGLEN; i++) { setPixelColor(i, color_blend(SEGCOLOR(1), color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), lum)); } @@ -287,7 +287,7 @@ uint16_t WS2812FX::mode_fade(void) { uint16_t counter = (now * ((SEGMENT.speed >> 3) +10)); uint8_t lum = triwave16(counter) >> 8; - for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) { + for(uint16_t i = 0; i < SEGLEN; i++) { setPixelColor(i, color_blend(SEGCOLOR(1), color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), lum)); } @@ -303,7 +303,7 @@ uint16_t WS2812FX::scan(bool dual) uint32_t cycleTime = 750 + (255 - SEGMENT.speed)*150; uint32_t perc = now % cycleTime; uint16_t prog = (perc * 65535) / cycleTime; - uint16_t size = 1 + ((SEGMENT.intensity * SEGLEN) >>9); + uint16_t size = 1 + ((SEGMENT.intensity * SEGLEN) >> 9); uint16_t ledIndex = (prog * ((SEGLEN *2) - size *2)) >> 16; fill(SEGCOLOR(1)); @@ -313,14 +313,13 @@ uint16_t WS2812FX::scan(bool dual) if (dual) { for (uint16_t j = led_offset; j < led_offset + size; j++) { - uint16_t i2 = SEGMENT.stop -1 -j; + uint16_t i2 = SEGLEN -1 -j; setPixelColor(i2, color_from_palette(i2, true, PALETTE_SOLID_WRAP, (SEGCOLOR(2))? 2:0)); } } for (uint16_t j = led_offset; j < led_offset + size; j++) { - uint16_t i = SEGMENT.start + j; - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); + setPixelColor(j, color_from_palette(j, true, PALETTE_SOLID_WRAP, 0)); } return FRAMETIME; @@ -347,7 +346,7 @@ uint16_t WS2812FX::mode_dual_scan(void) { * Cycles all LEDs at once through a rainbow. */ uint16_t WS2812FX::mode_rainbow(void) { - uint16_t counter = (now * ((SEGMENT.speed >> 3) +2)) & 0xFFFF; + uint16_t counter = (now * ((SEGMENT.speed >> 2) +2)) & 0xFFFF; counter = counter >> 8; if (SEGMENT.intensity < 128){ @@ -364,13 +363,13 @@ uint16_t WS2812FX::mode_rainbow(void) { * Cycles a rainbow over the entire string of LEDs. */ uint16_t WS2812FX::mode_rainbow_cycle(void) { - uint16_t counter = (now * ((SEGMENT.speed >> 3) +2)) & 0xFFFF; + uint16_t counter = (now * ((SEGMENT.speed >> 2) +2)) & 0xFFFF; counter = counter >> 8; - for(uint16_t i=0; i < SEGLEN; i++) { + for(uint16_t i = 0; i < SEGLEN; i++) { //intensity/29 = 0 (1/16) 1 (1/8) 2 (1/4) 3 (1/2) 4 (1) 5 (2) 6 (4) 7 (8) 8 (16) uint8_t index = (i * (16 << (SEGMENT.intensity /29)) / SEGLEN) + counter; - setPixelColor(SEGMENT.start + i, color_wheel(index)); + setPixelColor(i, color_wheel(index)); } return FRAMETIME; @@ -380,7 +379,7 @@ uint16_t WS2812FX::mode_rainbow_cycle(void) { /* * theater chase function */ -uint16_t WS2812FX::theater_chase(uint32_t color1, uint32_t color2, bool dopalette) { +uint16_t WS2812FX::theater_chase(uint32_t color1, uint32_t color2, bool do_palette) { byte gap = 2 + ((255 - SEGMENT.intensity) >> 5); uint32_t cycleTime = 50 + (255 - SEGMENT.speed)*2; uint32_t it = now / cycleTime; @@ -390,16 +389,16 @@ uint16_t WS2812FX::theater_chase(uint32_t color1, uint32_t color2, bool dopalett SEGENV.step = it; } - for(uint16_t i=0; i < SEGLEN; i++) { + for(uint16_t i = 0; i < SEGLEN; i++) { if((i % gap) == SEGENV.aux0) { - if (dopalette) + if (do_palette) { - setPixelColor(SEGMENT.start + i, color_from_palette(SEGMENT.start + i, true, PALETTE_SOLID_WRAP, 0)); + setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } else { - setPixelColor(SEGMENT.start + i, color1); + setPixelColor(i, color1); } } else { - setPixelColor(SEGMENT.start + i, color2); + setPixelColor(i, color2); } } return FRAMETIME; @@ -431,7 +430,7 @@ uint16_t WS2812FX::running_base(bool saw) { uint8_t x_scale = SEGMENT.intensity >> 2; uint32_t counter = (now * SEGMENT.speed) >> 9; - for(uint16_t i=0; i < SEGLEN; i++) { + for(uint16_t i = 0; i < SEGLEN; i++) { uint8_t s = 0; uint8_t a = i*x_scale - counter; if (saw) { @@ -443,7 +442,7 @@ uint16_t WS2812FX::running_base(bool saw) { } } s = sin8(a); - setPixelColor(SEGMENT.start + i, color_blend(color_from_palette(SEGMENT.start + i, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), s)); + setPixelColor(i, color_blend(color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), s)); } return FRAMETIME; } @@ -492,8 +491,7 @@ uint16_t WS2812FX::mode_twinkle(void) { { PRNG16 = (uint16_t)(PRNG16 * 2053) + 13849; // next 'random' number uint32_t p = (uint32_t)SEGLEN * (uint32_t)PRNG16; - uint16_t mapped = p >> 16; - uint16_t j = SEGMENT.start + mapped; + uint16_t j = p >> 16; setPixelColor(j, color_from_palette(j, true, PALETTE_SOLID_WRAP, 0)); } @@ -512,7 +510,7 @@ uint16_t WS2812FX::dissolve(uint32_t color) { if (random8() <= SEGMENT.intensity) { for (uint8_t times = 0; times < 10; times++) //attempt to spawn a new pixel 5 times { - uint16_t i = SEGMENT.start + random16(SEGLEN); + uint16_t i = random16(SEGLEN); if (SEGENV.aux0) { //dissolve to primary/palette if (getPixelColor(i) == SEGCOLOR(1) || wa) { if (color == SEGCOLOR(0)) @@ -559,7 +557,7 @@ uint16_t WS2812FX::mode_dissolve_random(void) { * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ */ uint16_t WS2812FX::mode_sparkle(void) { - for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) { + for(uint16_t i = 0; i < SEGLEN; i++) { setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); } uint32_t cycleTime = 10 + (255 - SEGMENT.speed)*2; @@ -570,7 +568,7 @@ uint16_t WS2812FX::mode_sparkle(void) { SEGENV.step = it; } - setPixelColor(SEGMENT.start + SEGENV.aux0, SEGCOLOR(0)); + setPixelColor(SEGENV.aux0, SEGCOLOR(0)); return FRAMETIME; } @@ -580,13 +578,13 @@ uint16_t WS2812FX::mode_sparkle(void) { * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ */ uint16_t WS2812FX::mode_flash_sparkle(void) { - for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) { + for(uint16_t i = 0; i < SEGLEN; i++) { setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } if(random8(5) == 0) { SEGENV.aux0 = random16(SEGLEN); // aux0 stores the random led index - setPixelColor(SEGMENT.start + SEGENV.aux0, SEGCOLOR(1)); + setPixelColor(SEGENV.aux0, SEGCOLOR(1)); return 20; } return 20 + (uint16_t)(255-SEGMENT.speed); @@ -598,13 +596,13 @@ uint16_t WS2812FX::mode_flash_sparkle(void) { * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ */ uint16_t WS2812FX::mode_hyper_sparkle(void) { - for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) { + for(uint16_t i = 0; i < SEGLEN; i++) { setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } if(random8(5) < 2) { - for(uint16_t i=0; i < max(1, SEGLEN/3); i++) { - setPixelColor(SEGMENT.start + random16(SEGLEN), SEGCOLOR(1)); + for(uint16_t i = 0; i < MAX(1, SEGLEN/3); i++) { + setPixelColor(random16(SEGLEN), SEGCOLOR(1)); } return 20; } @@ -616,7 +614,7 @@ uint16_t WS2812FX::mode_hyper_sparkle(void) { * Strobe effect with different strobe count and pause, controlled by speed. */ uint16_t WS2812FX::mode_multi_strobe(void) { - for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) { + for(uint16_t i = 0; i < SEGLEN; i++) { setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); } //blink(SEGCOLOR(0), SEGCOLOR(1), true, true); @@ -625,7 +623,7 @@ uint16_t WS2812FX::mode_multi_strobe(void) { uint16_t count = 2 * ((SEGMENT.speed / 10) + 1); if(SEGENV.step < count) { if((SEGENV.step & 1) == 0) { - for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) { + for(uint16_t i = 0; i < SEGLEN; i++) { setPixelColor(i, SEGCOLOR(0)); } delay = 20; @@ -641,12 +639,8 @@ uint16_t WS2812FX::mode_multi_strobe(void) { * Android loading circle */ uint16_t WS2812FX::mode_android(void) { - if (SEGENV.call == 0) - { - SEGENV.step = SEGMENT.start; - } - for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) { + for(uint16_t i = 0; i < SEGLEN; i++) { setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); } @@ -670,19 +664,19 @@ uint16_t WS2812FX::mode_android(void) { if (SEGENV.call %3 != 1) SEGENV.aux1--; } - if (a >= SEGMENT.stop) a = SEGMENT.start; + if (a >= SEGLEN) a = 0; - if (a + SEGENV.aux1 < SEGMENT.stop) + if (a + SEGENV.aux1 < SEGLEN) { for(int i = a; i < a+SEGENV.aux1; i++) { setPixelColor(i, SEGCOLOR(0)); } } else { - for(int i = a; i < SEGMENT.stop; i++) { + for(int i = a; i < SEGLEN; i++) { setPixelColor(i, SEGCOLOR(0)); } - for(int i = SEGMENT.start; i < SEGENV.aux1 - (SEGMENT.stop -a); i++) { + for(int i = 0; i < SEGENV.aux1 - (SEGLEN -a); i++) { setPixelColor(i, SEGCOLOR(0)); } } @@ -696,18 +690,68 @@ uint16_t WS2812FX::mode_android(void) { * color1 = background color * color2 and color3 = colors of two adjacent leds */ -uint16_t WS2812FX::chase(uint32_t color1, uint32_t color2, uint32_t color3, bool dopalette) { - uint16_t counter = now * (SEGMENT.speed >> 3) + 1; +uint16_t WS2812FX::chase(uint32_t color1, uint32_t color2, uint32_t color3, bool do_palette) { + uint16_t counter = now * ((SEGMENT.speed >> 2) + 1); uint16_t a = counter * SEGLEN >> 16; + + bool chase_random = (SEGMENT.mode == FX_MODE_CHASE_RANDOM); + if (chase_random) { + if (a < SEGENV.step) //we hit the start again, choose new color for Chase random + { + SEGENV.aux1 = SEGENV.aux0; //store previous random color + SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0); + } + color1 = color_wheel(SEGENV.aux0); + } + SEGENV.step = a; + // Use intensity setting to vary chase up to 1/2 string length - uint16_t b = (a + 1 + (SEGMENT.intensity * SEGLEN >> 10)) % SEGLEN; - uint16_t c = (b + 1 + (SEGMENT.intensity * SEGLEN >> 10)) % SEGLEN; + uint8_t size = 1 + (SEGMENT.intensity * SEGLEN >> 10); - if (dopalette) color1 = color_from_palette(SEGMENT.start + a, true, PALETTE_SOLID_WRAP, 1); + uint16_t b = a + size; //"trail" of chase, filled with color1 + if (b > SEGLEN) b -= SEGLEN; + uint16_t c = b + size; + if (c > SEGLEN) c -= SEGLEN; - setPixelColor(SEGMENT.start + a, color1); - setPixelColor(SEGMENT.start + b, color2); - setPixelColor(SEGMENT.start + c, color3); + //background + if (do_palette) + { + for(uint16_t i = 0; i < SEGLEN; i++) { + setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); + } + } else fill(color1); + + //if random, fill old background between a and end + if (chase_random) + { + color1 = color_wheel(SEGENV.aux1); + for (uint16_t i = a; i < SEGLEN; i++) + setPixelColor(i, color1); + } + + //fill between points a and b with color2 + if (a < b) + { + for (uint16_t i = a; i < b; i++) + setPixelColor(i, color2); + } else { + for (uint16_t i = a; i < SEGLEN; i++) //fill until end + setPixelColor(i, color2); + for (uint16_t i = 0; i < b; i++) //fill from start until b + setPixelColor(i, color2); + } + + //fill between points b and c with color2 + if (b < c) + { + for (uint16_t i = b; i < c; i++) + setPixelColor(i, color3); + } else { + for (uint16_t i = b; i < SEGLEN; i++) //fill until end + setPixelColor(i, color3); + for (uint16_t i = 0; i < c; i++) //fill from start until c + setPixelColor(i, color3); + } return FRAMETIME; } @@ -717,7 +761,7 @@ uint16_t WS2812FX::chase(uint32_t color1, uint32_t color2, uint32_t color3, bool * Bicolor chase, more primary color. */ uint16_t WS2812FX::mode_chase_color(void) { - return chase(SEGCOLOR(1), SEGCOLOR(0), SEGCOLOR(0), true); + return chase(SEGCOLOR(1), (SEGCOLOR(2)) ? SEGCOLOR(2) : SEGCOLOR(0), SEGCOLOR(0), true); } @@ -725,10 +769,19 @@ uint16_t WS2812FX::mode_chase_color(void) { * Primary running followed by random color. */ uint16_t WS2812FX::mode_chase_random(void) { - if(SEGENV.step == 0) { - SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0); - } - return chase(color_wheel(SEGENV.aux0), SEGCOLOR(0), SEGCOLOR(0), false); + return chase(SEGCOLOR(1), (SEGCOLOR(2)) ? SEGCOLOR(2) : SEGCOLOR(0), SEGCOLOR(0), false); +} + + +/* + * Primary, secondary running on rainbow. + */ +uint16_t WS2812FX::mode_chase_rainbow(void) { + uint8_t color_sep = 256 / SEGLEN; + uint8_t color_index = SEGENV.call & 0xFF; + uint32_t color = color_wheel(((SEGENV.step * color_sep) + color_index) & 0xFF); + + return chase(color, SEGCOLOR(0), SEGCOLOR(1), false); } @@ -768,23 +821,23 @@ uint16_t WS2812FX::mode_colorful(void) { SEGENV.step = it; } - uint16_t i = SEGMENT.start; - for (i; i < SEGMENT.stop -3; i+=4) + uint16_t i = 0; + for (i; i < SEGLEN -3; i+=4) { setPixelColor(i, cols[SEGENV.aux0]); setPixelColor(i+1, cols[SEGENV.aux0+1]); setPixelColor(i+2, cols[SEGENV.aux0+2]); setPixelColor(i+3, cols[SEGENV.aux0+3]); } - if(i < SEGMENT.stop) + if(i < SEGLEN) { setPixelColor(i, cols[SEGENV.aux0]); - if(i+1 < SEGMENT.stop) + if(i+1 < SEGLEN) { setPixelColor(i+1, cols[SEGENV.aux0+1]); - if(i+2 < SEGMENT.stop) + if(i+2 < SEGLEN) { setPixelColor(i+2, cols[SEGENV.aux0+2]); } @@ -799,10 +852,10 @@ uint16_t WS2812FX::mode_colorful(void) { * Emulates a traffic light. */ uint16_t WS2812FX::mode_traffic_light(void) { - for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) + for(uint16_t i=0; i < SEGLEN; i++) setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); uint32_t mdelay = 500; - for (int i = SEGMENT.start; i < SEGMENT.stop-2 ; i+=3) + for (int i = 0; i < SEGLEN-2 ; i+=3) { switch (SEGENV.aux0) { @@ -824,18 +877,6 @@ uint16_t WS2812FX::mode_traffic_light(void) { } -/* - * Primary, secondary running on rainbow. - */ -uint16_t WS2812FX::mode_chase_rainbow(void) { - uint8_t color_sep = 256 / SEGLEN; - uint8_t color_index = SEGENV.call & 0xFF; - uint32_t color = color_wheel(((SEGENV.step * color_sep) + color_index) & 0xFF); - - return chase(color, SEGCOLOR(0), SEGCOLOR(1), 0); -} - - /* * Sec flashes running on prim. */ @@ -843,7 +884,7 @@ uint16_t WS2812FX::mode_chase_rainbow(void) { uint16_t WS2812FX::mode_chase_flash(void) { uint8_t flash_step = SEGENV.call % ((FLASH_COUNT * 2) + 1); - for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) { + for(uint16_t i = 0; i < SEGLEN; i++) { setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } @@ -852,8 +893,8 @@ uint16_t WS2812FX::mode_chase_flash(void) { if(flash_step % 2 == 0) { uint16_t n = SEGENV.step; uint16_t m = (SEGENV.step + 1) % SEGLEN; - setPixelColor(SEGMENT.start + n, SEGCOLOR(1)); - setPixelColor(SEGMENT.start + m, SEGCOLOR(1)); + setPixelColor( n, SEGCOLOR(1)); + setPixelColor( m, SEGCOLOR(1)); delay = 20; } else { delay = 30; @@ -871,8 +912,8 @@ uint16_t WS2812FX::mode_chase_flash(void) { uint16_t WS2812FX::mode_chase_flash_random(void) { uint8_t flash_step = SEGENV.call % ((FLASH_COUNT * 2) + 1); - for(uint16_t i=0; i < SEGENV.step; i++) { - setPixelColor(SEGMENT.start + i, color_wheel(SEGENV.aux0)); + for(uint16_t i = 0; i < SEGENV.step; i++) { + setPixelColor(i, color_wheel(SEGENV.aux0)); } uint16_t delay = 1 + ((10 * (uint16_t)(255 - SEGMENT.speed)) / SEGLEN); @@ -880,12 +921,12 @@ uint16_t WS2812FX::mode_chase_flash_random(void) { uint16_t n = SEGENV.step; uint16_t m = (SEGENV.step + 1) % SEGLEN; if(flash_step % 2 == 0) { - setPixelColor(SEGMENT.start + n, SEGCOLOR(0)); - setPixelColor(SEGMENT.start + m, SEGCOLOR(0)); + setPixelColor( n, SEGCOLOR(0)); + setPixelColor( m, SEGCOLOR(0)); delay = 20; } else { - setPixelColor(SEGMENT.start + n, color_wheel(SEGENV.aux0)); - setPixelColor(SEGMENT.start + m, SEGCOLOR(1)); + setPixelColor( n, color_wheel(SEGENV.aux0)); + setPixelColor( m, SEGCOLOR(1)); delay = 30; } } else { @@ -908,17 +949,17 @@ uint16_t WS2812FX::running(uint32_t color1, uint32_t color2) { uint32_t it = now / cycleTime; if (SEGMENT.speed == 0) it = 0; - for(uint16_t i=0; i < SEGLEN; i++) { + for(uint16_t i = 0; i < SEGLEN; i++) { if((i + SEGENV.aux0) % (pxw*2) < pxw) { if (color1 == SEGCOLOR(0)) { - setPixelColor(SEGMENT.stop -i -1, color_from_palette(SEGMENT.stop -i -1, true, PALETTE_SOLID_WRAP, 0)); + setPixelColor(SEGLEN -i -1, color_from_palette(SEGLEN -i -1, true, PALETTE_SOLID_WRAP, 0)); } else { - setPixelColor(SEGMENT.stop -i -1, color1); + setPixelColor(SEGLEN -i -1, color1); } } else { - setPixelColor(SEGMENT.stop -i -1, color2); + setPixelColor(SEGLEN -i -1, color2); } } @@ -971,12 +1012,12 @@ uint16_t WS2812FX::mode_running_random(void) { if (SEGENV.aux1 == it) return FRAMETIME; for(uint16_t i=SEGLEN-1; i > 0; i--) { - setPixelColor(SEGMENT.start + i, getPixelColor(SEGMENT.start + i - 1)); + setPixelColor( i, getPixelColor( i - 1)); } if(SEGENV.step == 0) { SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0); - setPixelColor(SEGMENT.start, color_wheel(SEGENV.aux0)); + setPixelColor(0, color_wheel(SEGENV.aux0)); } SEGENV.step++; @@ -1009,7 +1050,7 @@ uint16_t WS2812FX::larson_scanner(bool dual) { for (uint16_t i = SEGENV.step; i < index; i++) { uint16_t j = (SEGENV.aux0)?i:SEGLEN-1-i; - setPixelColor(SEGMENT.start + j, color_from_palette(j, true, PALETTE_SOLID_WRAP, 0)); + setPixelColor( j, color_from_palette(j, true, PALETTE_SOLID_WRAP, 0)); } if (dual) { uint32_t c; @@ -1021,7 +1062,7 @@ uint16_t WS2812FX::larson_scanner(bool dual) { for (uint16_t i = SEGENV.step; i < index; i++) { uint16_t j = (SEGENV.aux0)?SEGLEN-1-i:i; - setPixelColor(SEGMENT.start + j, c); + setPixelColor(j, c); } } @@ -1031,15 +1072,26 @@ uint16_t WS2812FX::larson_scanner(bool dual) { /* - * Firing comets from one end. + * Firing comets from one end. "Lighthouse" */ uint16_t WS2812FX::mode_comet(void) { - uint16_t counter = now * (SEGMENT.speed >>3) +1; + uint16_t counter = now * ((SEGMENT.speed >>2) +1); uint16_t index = counter * SEGLEN >> 16; + if (SEGENV.call == 0) SEGENV.aux0 = index; fade_out(SEGMENT.intensity); - setPixelColor(SEGMENT.start + index, color_from_palette(index, true, PALETTE_SOLID_WRAP, 0)); + setPixelColor( index, color_from_palette(index, true, PALETTE_SOLID_WRAP, 0)); + if (index > SEGENV.aux0) { + for (uint16_t i = SEGENV.aux0; i < index ; i++) { + setPixelColor( i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); + } + } else if (index < SEGENV.aux0 && index < 10) { + for (uint16_t i = 0; i < index ; i++) { + setPixelColor( i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); + } + } + SEGENV.aux0 = index++; return FRAMETIME; } @@ -1054,8 +1106,8 @@ uint16_t WS2812FX::mode_fireworks() { SEGENV.aux0 = UINT16_MAX; SEGENV.aux1 = UINT16_MAX; } - bool valid1 = (SEGENV.aux0 < SEGMENT.stop && SEGENV.aux0 >= SEGMENT.start); - bool valid2 = (SEGENV.aux1 < SEGMENT.stop && SEGENV.aux1 >= SEGMENT.start); + bool valid1 = (SEGENV.aux0 < SEGLEN); + bool valid2 = (SEGENV.aux1 < SEGLEN); uint32_t sv1 = 0, sv2 = 0; if (valid1) sv1 = getPixelColor(SEGENV.aux0); if (valid2) sv2 = getPixelColor(SEGENV.aux1); @@ -1063,9 +1115,9 @@ uint16_t WS2812FX::mode_fireworks() { if (valid1) setPixelColor(SEGENV.aux0 , sv1); if (valid2) setPixelColor(SEGENV.aux1, sv2); - for(uint16_t i=0; i> 1)) == 0) { - uint16_t index = SEGMENT.start + random(SEGLEN); + uint16_t index = random(SEGLEN); setPixelColor(index, color_from_palette(random8(), false, false, 0)); SEGENV.aux1 = SEGENV.aux0; SEGENV.aux0 = index; @@ -1082,17 +1134,17 @@ uint16_t WS2812FX::mode_rain() if (SEGENV.step > SPEED_FORMULA_L) { SEGENV.step = 0; //shift all leds right - uint32_t ctemp = getPixelColor(SEGMENT.stop -1); - for(uint16_t i=SEGMENT.stop -1; i>SEGMENT.start; i--) { + uint32_t ctemp = getPixelColor(SEGLEN -1); + for(uint16_t i = SEGLEN -1; i > 0; i--) { setPixelColor(i, getPixelColor(i-1)); } - setPixelColor(SEGMENT.start, ctemp); + setPixelColor(0, ctemp); SEGENV.aux0++; SEGENV.aux1++; if (SEGENV.aux0 == 0) SEGENV.aux0 = UINT16_MAX; if (SEGENV.aux1 == 0) SEGENV.aux0 = UINT16_MAX; - if (SEGENV.aux0 == SEGMENT.stop) SEGENV.aux0 = SEGMENT.start; - if (SEGENV.aux1 == SEGMENT.stop) SEGENV.aux1 = SEGMENT.start; + if (SEGENV.aux0 == SEGLEN) SEGENV.aux0 = 0; + if (SEGENV.aux1 == SEGLEN) SEGENV.aux1 = 0; } return mode_fireworks(); } @@ -1110,12 +1162,12 @@ uint16_t WS2812FX::mode_fire_flicker(void) { byte r = (SEGCOLOR(0) >> 16) & 0xFF; byte g = (SEGCOLOR(0) >> 8) & 0xFF; byte b = (SEGCOLOR(0) & 0xFF); - byte lum = (SEGMENT.palette == 0) ? max(w, max(r, max(g, b))) : 255; + byte lum = (SEGMENT.palette == 0) ? MAX(w, MAX(r, MAX(g, b))) : 255; lum /= (((256-SEGMENT.intensity)/16)+1); - for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) { + for(uint16_t i = 0; i < SEGLEN; i++) { byte flicker = random8(lum); if (SEGMENT.palette == 0) { - setPixelColor(i, max(r - flicker, 0), max(g - flicker, 0), max(b - flicker, 0), max(w - flicker, 0)); + setPixelColor(i, MAX(r - flicker, 0), MAX(g - flicker, 0), MAX(b - flicker, 0), MAX(w - flicker, 0)); } else { setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0, 255 - flicker)); } @@ -1130,29 +1182,25 @@ uint16_t WS2812FX::mode_fire_flicker(void) { * Gradient run base function */ uint16_t WS2812FX::gradient_base(bool loading) { - uint16_t counter = now * (SEGMENT.speed >> 3) + 1; - SEGENV.step = counter * SEGLEN >> 16; - if (SEGMENT.speed == 0) SEGENV.step = SEGMENT.start + (SEGLEN >> 1); - if (SEGENV.call == 0) SEGENV.step = 0; - float per,val; //0.0 = sec 1.0 = pri - float brd = SEGMENT.intensity; - if (!loading) brd = SEGMENT.intensity/2; + uint16_t counter = now * ((SEGMENT.speed >> 2) + 1); + uint16_t pp = counter * SEGLEN >> 16; + if (SEGENV.call == 0) pp = 0; + float val; //0.0 = sec 1.0 = pri + float brd = loading ? SEGMENT.intensity : SEGMENT.intensity/2; if (brd <1.0) brd = 1.0; - int pp = SEGENV.step; int p1 = pp-SEGLEN; int p2 = pp+SEGLEN; - for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) + for(uint16_t i = 0; i < SEGLEN; i++) { if (loading) { val = abs(((i>pp) ? p2:pp) -i); } else { - val = min(abs(pp-i),min(abs(p1-i),abs(p2-i))); + val = MIN(abs(pp-i),MIN(abs(p1-i),abs(p2-i))); } - per = val/brd; - if (per >1.0) per = 1.0; - setPixelColor(i, color_blend(SEGCOLOR(0), color_from_palette(i, true, PALETTE_SOLID_WRAP, 1), per*255)); + val = (brd > val) ? val/brd * 255 : 255; + setPixelColor(i, color_blend(SEGCOLOR(0), color_from_palette(i, true, PALETTE_SOLID_WRAP, 1), val)); } return FRAMETIME; @@ -1176,21 +1224,44 @@ uint16_t WS2812FX::mode_loading(void) { //American Police Light with all LEDs Red and Blue -uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2) +uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2, bool all) { - uint16_t counter = now * ((SEGMENT.speed >> 3) +1); + uint16_t counter = now * ((SEGMENT.speed >> 2) +1); uint16_t idexR = (counter * SEGLEN) >> 16; if (idexR >= SEGLEN) idexR = 0; uint16_t topindex = SEGLEN >> 1; - uint16_t idexB = idexR + topindex; - - if (idexR > topindex) idexB -= SEGLEN; + uint16_t idexB = (idexR > topindex) ? idexR - topindex : idexR + topindex; + if (SEGENV.call == 0) SEGENV.aux0 = idexR; if (idexB >= SEGLEN) idexB = 0; //otherwise overflow on odd number of LEDs - setPixelColor(SEGMENT.start + idexR, color1); - setPixelColor(SEGMENT.start + idexB, color2); - + if (all) { //different algo, ensuring immediate fill + if (idexB > idexR) { + fill(color2); + for (uint16_t i = idexR; i < idexB; i++) setPixelColor(i, color1); + } else { + fill(color1); + for (uint16_t i = idexB; i < idexR; i++) setPixelColor(i, color2); + } + } else { //regular dot-only mode + uint8_t size = 1 + SEGMENT.intensity >> 3; + if (size > SEGLEN/2) size = 1+ SEGLEN/2; + for (uint8_t i=0; i <= size; i++) { + setPixelColor(idexR+i, color1); + setPixelColor(idexB+i, color2); + } + if (SEGENV.aux0 != idexR) { + uint8_t gap = (SEGENV.aux0 < idexR)? idexR - SEGENV.aux0:SEGLEN - SEGENV.aux0 + idexR; + for (uint8_t i = 0; i <= gap ; i++) { + if ((idexR - i) < 0) idexR = SEGLEN-1 + i; + if ((idexB - i) < 0) idexB = SEGLEN-1 + i; + setPixelColor(idexR-i, color1); + setPixelColor(idexB-i, color2); + } + SEGENV.aux0 = idexR; + } + } + return FRAMETIME; } @@ -1198,7 +1269,7 @@ uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2) //American Police Light with all LEDs Red and Blue uint16_t WS2812FX::mode_police_all() { - return police_base(RED, BLUE); + return police_base(RED, BLUE, true); } @@ -1207,14 +1278,14 @@ uint16_t WS2812FX::mode_police() { fill(SEGCOLOR(1)); - return police_base(RED, BLUE); + return police_base(RED, BLUE, false); } //Police All with custom colors uint16_t WS2812FX::mode_two_areas() { - return police_base(SEGCOLOR(0), SEGCOLOR(1)); + return police_base(SEGCOLOR(0), SEGCOLOR(1), true); } @@ -1224,7 +1295,7 @@ uint16_t WS2812FX::mode_two_dots() fill(SEGCOLOR(2)); uint32_t color2 = (SEGCOLOR(1) == SEGCOLOR(2)) ? SEGCOLOR(0) : SEGCOLOR(1); - return police_base(SEGCOLOR(0), color2); + return police_base(SEGCOLOR(0), color2, false); } @@ -1237,14 +1308,14 @@ uint16_t WS2812FX::tricolor_chase(uint32_t color1, uint32_t color2) { uint8_t width = (1 + SEGMENT.intensity/32) * 3; //value of 1-8 for each colour uint8_t index = it % width; - for(uint16_t i=0; i < SEGLEN; i++, index++) { + for(uint16_t i = 0; i < SEGLEN; i++, index++) { if(index > width-1) index = 0; uint32_t color = color1; if(index > width*2/3-1) color = color_from_palette(i, true, PALETTE_SOLID_WRAP, 1); else if(index > width/3-1) color = color2; - setPixelColor(SEGMENT.stop - i -1, color); + setPixelColor(SEGLEN - i -1, color); } return FRAMETIME; @@ -1272,22 +1343,23 @@ uint16_t WS2812FX::mode_tricolor_chase(void) { */ uint16_t WS2812FX::mode_icu(void) { uint16_t dest = SEGENV.step & 0xFFFF; + uint8_t space = (SEGMENT.intensity >> 3) +2; fill(SEGCOLOR(1)); - byte pindex = map(dest, 0, SEGLEN/2, 0, 255); + byte pindex = map(dest, 0, SEGLEN-SEGLEN/space, 0, 255); uint32_t col = color_from_palette(pindex, false, false, 0); - setPixelColor(SEGMENT.start + dest, col); - setPixelColor(SEGMENT.start + dest + SEGLEN/2, col); + setPixelColor(dest, col); + setPixelColor(dest + SEGLEN/space, col); if(SEGENV.aux0 == dest) { // pause between eye movements if(random8(6) == 0) { // blink once in a while - setPixelColor(SEGMENT.start + dest, SEGCOLOR(1)); - setPixelColor(SEGMENT.start + dest + SEGLEN/2, SEGCOLOR(1)); + setPixelColor(dest, SEGCOLOR(1)); + setPixelColor(dest + SEGLEN/space, SEGCOLOR(1)); return 200; } - SEGENV.aux0 = random16(SEGLEN/2); + SEGENV.aux0 = random16(SEGLEN-SEGLEN/space); return 1000 + random16(2000); } @@ -1299,8 +1371,8 @@ uint16_t WS2812FX::mode_icu(void) { dest--; } - setPixelColor(SEGMENT.start + dest, col); - setPixelColor(SEGMENT.start + dest + SEGLEN/2, col); + setPixelColor(dest, col); + setPixelColor(dest + SEGLEN/space, col); return SPEED_FORMULA_L; } @@ -1317,26 +1389,26 @@ uint16_t WS2812FX::mode_tricolor_wipe(void) uint16_t ledIndex = (prog * SEGLEN * 3) >> 16; uint16_t ledOffset = ledIndex; - for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) + for (uint16_t i = 0; i < SEGLEN; i++) { setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 2)); } if(ledIndex < SEGLEN) { //wipe from 0 to 1 - for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) + for (uint16_t i = 0; i < SEGLEN; i++) { - setPixelColor(i, (i - SEGMENT.start > ledOffset)? SEGCOLOR(0) : SEGCOLOR(1)); + setPixelColor(i, (i > ledOffset)? SEGCOLOR(0) : SEGCOLOR(1)); } } else if (ledIndex < SEGLEN*2) { //wipe from 1 to 2 ledOffset = ledIndex - SEGLEN; - for (uint16_t i = SEGMENT.start +ledOffset +1; i < SEGMENT.stop; i++) + for (uint16_t i = ledOffset +1; i < SEGLEN; i++) { setPixelColor(i, SEGCOLOR(1)); } } else //wipe from 2 to 0 { ledOffset = ledIndex - SEGLEN*2; - for (uint16_t i = SEGMENT.start; i <= SEGMENT.start +ledOffset; i++) + for (uint16_t i = 0; i <= ledOffset; i++) { setPixelColor(i, SEGCOLOR(0)); } @@ -1375,7 +1447,7 @@ uint16_t WS2812FX::mode_tricolor_fade(void) byte stp = prog; // % 256 uint32_t color = 0; - for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) { + for(uint16_t i = 0; i < SEGLEN; i++) { if (stage == 2) { color = color_blend(color_from_palette(i, true, PALETTE_SOLID_WRAP, 2), color2, stp); } else if (stage == 1) { @@ -1396,17 +1468,18 @@ uint16_t WS2812FX::mode_tricolor_fade(void) */ uint16_t WS2812FX::mode_multi_comet(void) { - uint32_t cycleTime = 20 + (2 * (uint32_t)(255 - SEGMENT.speed)); + uint32_t cycleTime = 10 + (uint32_t)(255 - SEGMENT.speed); uint32_t it = now / cycleTime; if (SEGENV.step == it) return FRAMETIME; - + if (!SEGENV.allocateData(sizeof(uint16_t) * 8)) return mode_static(); //allocation failed + fade_out(SEGMENT.intensity); - - static uint16_t comets[] = {UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX}; + + uint16_t* comets = reinterpret_cast(SEGENV.data); for(uint8_t i=0; i < 8; i++) { if(comets[i] < SEGLEN) { - uint16_t index = SEGMENT.start + comets[i]; + uint16_t index = comets[i]; if (SEGCOLOR(2) != 0) { setPixelColor(index, i % 2 ? color_from_palette(index, true, PALETTE_SOLID_WRAP, 0) : SEGCOLOR(2)); @@ -1446,15 +1519,15 @@ uint16_t WS2812FX::mode_random_chase(void) uint32_t it = now / cycleTime; if (SEGENV.step == it) return FRAMETIME; - for(uint16_t i=SEGMENT.stop -1; i>SEGMENT.start; i--) { + for(uint16_t i = SEGLEN -1; i > 0; i--) { setPixelColor(i, getPixelColor(i-1)); } - uint32_t color = getPixelColor(SEGMENT.start); - if (SEGLEN > 1) color = getPixelColor(SEGMENT.start + 1); + uint32_t color = getPixelColor(0); + if (SEGLEN > 1) color = getPixelColor( 1); uint8_t r = random8(6) != 0 ? (color >> 16 & 0xFF) : random8(); uint8_t g = random8(6) != 0 ? (color >> 8 & 0xFF) : random8(); uint8_t b = random8(6) != 0 ? (color & 0xFF) : random8(); - setPixelColor(SEGMENT.start, r, g, b); + setPixelColor(0, r, g, b); SEGENV.step = it; return FRAMETIME; @@ -1490,7 +1563,7 @@ uint16_t WS2812FX::mode_oscillate(void) uint32_t cycleTime = 20 + (2 * (uint32_t)(255 - SEGMENT.speed)); uint32_t it = now / cycleTime; - for(uint8_t i=0; i < numOscillators; i++) { + for(uint8_t i = 0; i < numOscillators; i++) { // if the counter has increased, move the oscillator by the random step if (it != SEGENV.step) oscillators[i].pos += oscillators[i].dir * oscillators[i].speed; oscillators[i].size = SEGLEN/(3+SEGMENT.intensity/8); @@ -1511,10 +1584,10 @@ uint16_t WS2812FX::mode_oscillate(void) uint32_t color = BLACK; for(uint8_t j=0; j < numOscillators; j++) { if(i >= oscillators[j].pos - oscillators[j].size && i <= oscillators[j].pos + oscillators[j].size) { - color = (color == BLACK) ? SEGMENT.colors[j] : color_blend(color, SEGMENT.colors[j], 128); + color = (color == BLACK) ? SEGCOLOR(j) : color_blend(color, SEGCOLOR(j), 128); } } - setPixelColor(SEGMENT.start + i, color); + setPixelColor(i, color); } SEGENV.step = it; @@ -1524,8 +1597,8 @@ uint16_t WS2812FX::mode_oscillate(void) uint16_t WS2812FX::mode_lightning(void) { - uint16_t ledstart = SEGMENT.start + random16(SEGLEN); // Determine starting location of flash - uint16_t ledlen = random16(SEGMENT.stop -1 -ledstart); // Determine length of flash (not to go beyond NUM_LEDS-1) + uint16_t ledstart = random16(SEGLEN); // Determine starting location of flash + uint16_t ledlen = random16(SEGLEN -1 -ledstart); // Determine length of flash (not to go beyond NUM_LEDS-1) uint8_t bri = 255/random8(1, 3); if (SEGENV.step == 0) @@ -1584,7 +1657,7 @@ uint16_t WS2812FX::mode_pride_2015(void) uint16_t brightnesstheta16 = sPseudotime; CRGB fastled_col; - for( uint16_t i = SEGMENT.start ; i < SEGMENT.stop; i++) { + for (uint16_t i = 0 ; i < SEGLEN; i++) { hue16 += hueinc16; uint8_t hue8 = hue16 >> 8; @@ -1598,7 +1671,7 @@ uint16_t WS2812FX::mode_pride_2015(void) CRGB newcolor = CHSV( hue8, sat8, bri8); fastled_col = col_to_crgb(getPixelColor(i)); - nblend( fastled_col, newcolor, 64); + nblend(fastled_col, newcolor, 64); setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); } SEGENV.step = sPseudotime; @@ -1613,7 +1686,7 @@ uint16_t WS2812FX::mode_juggle(void){ CRGB fastled_col; byte dothue = 0; for ( byte i = 0; i < 8; i++) { - uint16_t index = SEGMENT.start + beatsin88((128 + SEGMENT.speed)*(i + 7), 0, SEGLEN -1); + uint16_t index = 0 + beatsin88((128 + SEGMENT.speed)*(i + 7), 0, SEGLEN -1); fastled_col = col_to_crgb(getPixelColor(index)); fastled_col |= (SEGMENT.palette==0)?CHSV(dothue, 220, 255):ColorFromPalette(currentPalette, dothue, 255); setPixelColor(index, fastled_col.red, fastled_col.green, fastled_col.blue); @@ -1639,7 +1712,7 @@ uint16_t WS2812FX::mode_palette() if (noWrap) colorIndex = map(colorIndex, 0, 255, 0, 240); //cut off blend at palette "end" - setPixelColor(SEGMENT.start + i, color_from_palette(colorIndex, false, true, 255)); + setPixelColor(i, color_from_palette(colorIndex, false, true, 255)); } return FRAMETIME; } @@ -1705,8 +1778,8 @@ uint16_t WS2812FX::mode_fire_2012() // Step 4. Map from heat cells to LED colors for (uint16_t j = 0; j < SEGLEN; j++) { - CRGB color = ColorFromPalette(currentPalette, min(heat[j],240), 255, LINEARBLEND); - setPixelColor(SEGMENT.start + j, color.red, color.green, color.blue); + CRGB color = ColorFromPalette(currentPalette, MIN(heat[j],240), 255, LINEARBLEND); + setPixelColor(j, color.red, color.green, color.blue); } return FRAMETIME; } @@ -1721,19 +1794,20 @@ uint16_t WS2812FX::mode_colorwaves() uint16_t sPseudotime = SEGENV.step; uint16_t sHue16 = SEGENV.aux0; - uint8_t brightdepth = beatsin88( 341, 96, 224); + uint8_t brightdepth = beatsin88(341, 96, 224); uint16_t brightnessthetainc16 = beatsin88( 203, (25 * 256), (40 * 256)); uint8_t msmultiplier = beatsin88(147, 23, 60); uint16_t hue16 = sHue16;//gHue * 256; - uint16_t hueinc16 = beatsin88(113, 300, 1500); + // uint16_t hueinc16 = beatsin88(113, 300, 1500); + uint16_t hueinc16 = beatsin88(113, 60, 300)*SEGMENT.intensity*10/255; // Use the Intensity Slider for the hues sPseudotime += duration * msmultiplier; - sHue16 += duration * beatsin88( 400, 5, 9); + sHue16 += duration * beatsin88(400, 5, 9); uint16_t brightnesstheta16 = sPseudotime; CRGB fastled_col; - for ( uint16_t i = SEGMENT.start ; i < SEGMENT.stop; i++) { + for ( uint16_t i = 0 ; i < SEGLEN; i++) { hue16 += hueinc16; uint8_t hue8 = hue16 >> 8; uint16_t h16_128 = hue16 >> 7; @@ -1768,7 +1842,7 @@ uint16_t WS2812FX::mode_bpm() CRGB fastled_col; uint32_t stp = (now / 20) & 0xFF; uint8_t beat = beatsin8(SEGMENT.speed, 64, 255); - for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) { + for (uint16_t i = 0; i < SEGLEN; i++) { fastled_col = ColorFromPalette(currentPalette, stp + (i * 2), beat - stp + (i * 10)); setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); } @@ -1780,8 +1854,8 @@ uint16_t WS2812FX::mode_fillnoise8() { if (SEGENV.call == 0) SEGENV.step = random16(12345); CRGB fastled_col; - for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) { - uint8_t index = inoise8(i * SEGLEN, SEGENV.step + i * SEGLEN) % 255; + for (uint16_t i = 0; i < SEGLEN; i++) { + uint8_t index = inoise8(i * SEGLEN, SEGENV.step + i * SEGLEN); fastled_col = ColorFromPalette(currentPalette, index, 255, LINEARBLEND); setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); } @@ -1796,7 +1870,7 @@ uint16_t WS2812FX::mode_noise16_1() CRGB fastled_col; SEGENV.step += (1 + SEGMENT.speed/16); - for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) { + for (uint16_t i = 0; i < SEGLEN; i++) { uint16_t shift_x = beatsin8(11); // the x position of the noise field swings @ 17 bpm uint16_t shift_y = SEGENV.step/42; // the y position becomes slowly incremented @@ -1824,7 +1898,7 @@ uint16_t WS2812FX::mode_noise16_2() CRGB fastled_col; SEGENV.step += (1 + (SEGMENT.speed >> 1)); - for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) { + for (uint16_t i = 0; i < SEGLEN; i++) { uint16_t shift_x = SEGENV.step >> 6; // x as a function of time uint16_t shift_y = SEGENV.step/42; @@ -1849,7 +1923,7 @@ uint16_t WS2812FX::mode_noise16_3() CRGB fastled_col; SEGENV.step += (1 + SEGMENT.speed); - for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) { + for (uint16_t i = 0; i < SEGLEN; i++) { uint16_t shift_x = 4223; // no movement along x and y uint16_t shift_y = 1234; @@ -1875,8 +1949,8 @@ uint16_t WS2812FX::mode_noise16_4() { CRGB fastled_col; uint32_t stp = (now * SEGMENT.speed) >> 7; - for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) { - int16_t index = inoise16(uint32_t(i - SEGMENT.start) << 12, stp); + for (uint16_t i = 0; i < SEGLEN; i++) { + int16_t index = inoise16(uint32_t(i) << 12, stp); fastled_col = ColorFromPalette(currentPalette, index); setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); } @@ -1892,11 +1966,11 @@ uint16_t WS2812FX::mode_colortwinkle() CRGB fastled_col, prev; fract8 fadeUpAmount = 8 + (SEGMENT.speed/4), fadeDownAmount = 5 + (SEGMENT.speed/7); - for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) { + for (uint16_t i = 0; i < SEGLEN; i++) { fastled_col = col_to_crgb(getPixelColor(i)); prev = fastled_col; - uint16_t index = (i - SEGMENT.start) >> 3; - uint8_t bitNum = (i - SEGMENT.start) & 0x07; + uint16_t index = i >> 3; + uint8_t bitNum = i & 0x07; bool fadeUp = bitRead(SEGENV.data[index], bitNum); if (fadeUp) { @@ -1925,11 +1999,11 @@ uint16_t WS2812FX::mode_colortwinkle() if (random8() <= SEGMENT.intensity) { for (uint8_t times = 0; times < 5; times++) //attempt to spawn a new pixel 5 times { - int i = SEGMENT.start + random16(SEGLEN); + int i = random16(SEGLEN); if(getPixelColor(i) == 0) { fastled_col = ColorFromPalette(currentPalette, random8(), 64, NOBLEND); - uint16_t index = (i - SEGMENT.start) >> 3; - uint8_t bitNum = (i - SEGMENT.start) & 0x07; + uint16_t index = i >> 3; + uint8_t bitNum = i & 0x07; bitWrite(SEGENV.data[index], bitNum, true); setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); break; //only spawn 1 new pixel per frame per 50 LEDs @@ -1949,7 +2023,7 @@ uint16_t WS2812FX::mode_lake() { uint8_t wave3 = beatsin8(sp +2, 0,80); CRGB fastled_col; - for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) + for (uint16_t i = 0; i < SEGLEN; i++) { int index = cos8((i*15)+ wave1)/2 + cubicwave8((i*23)+ wave2)/2; uint8_t lum = (index > wave3) ? index - wave3 : 0; @@ -1978,7 +2052,7 @@ uint16_t WS2812FX::mode_meteor() { { byte meteorTrailDecay = 128 + random8(127); trail[i] = scale8(trail[i], meteorTrailDecay); - setPixelColor(SEGMENT.start + i, color_from_palette(trail[i], false, true, 255)); + setPixelColor(i, color_from_palette(trail[i], false, true, 255)); } } @@ -1990,7 +2064,7 @@ uint16_t WS2812FX::mode_meteor() { } trail[index] = 240; - setPixelColor(SEGMENT.start + index, color_from_palette(trail[index], false, true, 255)); + setPixelColor(index, color_from_palette(trail[index], false, true, 255)); } return FRAMETIME; @@ -2016,7 +2090,7 @@ uint16_t WS2812FX::mode_meteor_smooth() { trail[i] += change; if (trail[i] > 245) trail[i] = 0; if (trail[i] > 240) trail[i] = 240; - setPixelColor(SEGMENT.start + i, color_from_palette(trail[i], false, true, 255)); + setPixelColor(i, color_from_palette(trail[i], false, true, 255)); } } @@ -2026,7 +2100,7 @@ uint16_t WS2812FX::mode_meteor_smooth() { if(in + j >= SEGLEN) { index = (in + j - SEGLEN); } - setPixelColor(SEGMENT.start + index, color_blend(getPixelColor(SEGMENT.start + index), color_from_palette(240, false, true, 255), 48)); + setPixelColor(index, color_blend(getPixelColor(index), color_from_palette(240, false, true, 255), 48)); trail[index] = 240; } @@ -2053,10 +2127,10 @@ uint16_t WS2812FX::mode_railway() if (p0 < 255) pos = p0; } if (SEGENV.aux0) pos = 255 - pos; - for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i += 2) + for (uint16_t i = 0; i < SEGLEN; i += 2) { setPixelColor(i, color_from_palette(255 - pos, false, false, 255)); - if (i < SEGMENT.stop -1) + if (i < SEGLEN -1) { setPixelColor(i + 1, color_from_palette(pos, false, false, 255)); } @@ -2077,7 +2151,7 @@ typedef struct Ripple { uint16_t pos; } ripple; -uint16_t WS2812FX::mode_ripple() +uint16_t WS2812FX::ripple_base(bool rainbow) { uint16_t maxRipples = 1 + (SEGLEN >> 2); if (maxRipples > 100) maxRipples = 100; @@ -2087,8 +2161,25 @@ uint16_t WS2812FX::mode_ripple() Ripple* ripples = reinterpret_cast(SEGENV.data); - fill(SEGCOLOR(1)); - + // ranbow background or chosen background, all very dim. + if (rainbow) { + if (SEGENV.call ==0) { + SEGENV.aux0 = random8(); + SEGENV.aux1 = random8(); + } + if (SEGENV.aux0 == SEGENV.aux1) { + SEGENV.aux1 = random8(); + } + else if (SEGENV.aux1 > SEGENV.aux0) { + SEGENV.aux0++; + } else { + SEGENV.aux0--; + } + fill(color_blend(color_wheel(SEGENV.aux0),BLACK,235)); + } else { + fill(SEGCOLOR(1)); + } + //draw wave for (uint16_t i = 0; i < maxRipples; i++) { @@ -2107,12 +2198,12 @@ uint16_t WS2812FX::mode_ripple() for (int16_t v = left; v < left +4; v++) { uint8_t mag = scale8(cubicwave8((propF>>2)+(v-left)*64), amp); - if (v < SEGMENT.stop && v >= SEGMENT.start) + if (v < SEGLEN && v >= 0) { setPixelColor(v, color_blend(getPixelColor(v), col, mag)); } int16_t w = left + propI*2 + 3 -(v-left); - if (w < SEGMENT.stop && w >= SEGMENT.start) + if (w < SEGLEN && w >= 0) { setPixelColor(w, color_blend(getPixelColor(w), col, mag)); } @@ -2124,7 +2215,7 @@ uint16_t WS2812FX::mode_ripple() if (random16(IBN + 10000) <= SEGMENT.intensity) { ripples[i].state = 1; - ripples[i].pos = SEGMENT.start + random16(SEGLEN); + ripples[i].pos = random16(SEGLEN); ripples[i].color = random8(); //color } } @@ -2132,6 +2223,15 @@ uint16_t WS2812FX::mode_ripple() return FRAMETIME; } +uint16_t WS2812FX::mode_ripple(void) { + return ripple_base(false); +} + +uint16_t WS2812FX::mode_ripple_rainbow(void) { + return ripple_base(true); +} + + // TwinkleFOX by Mark Kriegsman: https://gist.github.com/kriegsman/756ea6dcae8e30845b5a // @@ -2228,7 +2328,7 @@ uint16_t WS2812FX::twinklefox_base(bool cat) uint8_t backgroundBrightness = bg.getAverageLight(); - for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) { + for (uint16_t i = 0; i < SEGLEN; i++) { PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number uint16_t myclockoffset16= PRNG16; // use that number as clock offset @@ -2289,7 +2389,7 @@ uint16_t WS2812FX::mode_halloween_eyes() if (stateTime == 0) stateTime = 2000; if (state == 0) { //spawn eyes - SEGENV.aux0 = random16(SEGMENT.start, SEGMENT.stop - eyeLength); //start pos + SEGENV.aux0 = random16(0, SEGLEN - eyeLength); //start pos SEGENV.aux1 = random8(); //color state = 1; } @@ -2339,7 +2439,7 @@ uint16_t WS2812FX::mode_static_pattern() bool drawingLit = true; uint16_t cnt = 0; - for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) { + for (uint16_t i = 0; i < SEGLEN; i++) { setPixelColor(i, (drawingLit) ? color_from_palette(i, true, PALETTE_SOLID_WRAP, 0) : SEGCOLOR(1)); cnt++; if (cnt >= ((drawingLit) ? lit : unlit)) { @@ -2357,7 +2457,7 @@ uint16_t WS2812FX::mode_tri_static_pattern() uint8_t currSeg = 0; uint16_t currSegCount = 0; - for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) { + for (uint16_t i = 0; i < SEGLEN; i++) { if ( currSeg % 3 == 0 ) { setPixelColor(i, SEGCOLOR(0)); } else if( currSeg % 3 == 1) { @@ -2392,7 +2492,7 @@ uint16_t WS2812FX::spots_base(uint16_t threshold) { uint16_t wave = triwave16((i * 0xFFFF) / zoneLen); if (wave > threshold) { - uint16_t index = SEGMENT.start + pos + i; + uint16_t index = 0 + pos + i; uint8_t s = (wave - threshold)*255 / (0xFFFF - threshold); setPixelColor(index, color_blend(color_from_palette(index, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), 255-s)); } @@ -2473,13 +2573,13 @@ uint16_t WS2812FX::mode_bouncing_balls(void) { uint32_t color = SEGCOLOR(0); if (SEGMENT.palette) { - color = color_wheel(i*(256/max(numBalls, 8))); + color = color_wheel(i*(256/MAX(numBalls, 8))); } else if (hasCol2) { color = SEGCOLOR(i % NUM_COLORS); } uint16_t pos = round(balls[i].height * (SEGLEN - 1)); - setPixelColor(SEGMENT.start + pos, color); + setPixelColor(pos, color); } return FRAMETIME; @@ -2491,21 +2591,32 @@ uint16_t WS2812FX::mode_bouncing_balls(void) { */ uint16_t WS2812FX::sinelon_base(bool dual, bool rainbow=false) { fade_out(SEGMENT.intensity); - int pos = beatsin16(SEGMENT.speed/10,0,SEGLEN-1); - + uint16_t pos = beatsin16(SEGMENT.speed/10,0,SEGLEN-1); + if (SEGENV.call == 0) SEGENV.aux0 = pos; uint32_t color1 = color_from_palette(pos, true, false, 0); + uint32_t color2 = SEGCOLOR(2); if (rainbow) { color1 = color_wheel((pos & 0x07) * 32); } - setPixelColor(SEGMENT.start + pos, color1); - + setPixelColor(pos, color1); if (dual) { - uint32_t color2 = SEGCOLOR(2); - if (!color2) color2 = color_from_palette(pos, true, false, 0); if (rainbow) color2 = color1; //rainbow - - setPixelColor(SEGMENT.start + SEGLEN-1-pos, color2); + setPixelColor(SEGLEN-1-pos, color2); + } + if (SEGENV.aux0 != pos) { + if (SEGENV.aux0 < pos) { + for (uint16_t i = SEGENV.aux0; i < pos ; i++) { + setPixelColor(i, color1); + if (dual) setPixelColor(SEGLEN-1-i, color2); + } + } else { + for (uint16_t i = SEGENV.aux0; i > pos ; i--) { + setPixelColor(i, color1); + if (dual) setPixelColor(SEGLEN-1-i, color2); + } + } + SEGENV.aux0 = pos; } return FRAMETIME; @@ -2531,7 +2642,7 @@ uint16_t WS2812FX::mode_glitter() if (SEGMENT.intensity > random8()) { - setPixelColor(SEGMENT.start + random16(SEGLEN), ULTRAWHITE); + setPixelColor(random16(SEGLEN), ULTRAWHITE); } return FRAMETIME; @@ -2572,16 +2683,16 @@ uint16_t WS2812FX::mode_popcorn(void) { for(uint8_t i = 0; i < numPopcorn; i++) { bool isActive = popcorn[i].pos >= 0.0f; - if(isActive) { // if kernel is active, update its position + if (isActive) { // if kernel is active, update its position popcorn[i].pos += popcorn[i].vel; popcorn[i].vel += gravity; uint32_t col = color_wheel(popcorn[i].colIndex); if (!SEGMENT.palette && popcorn[i].colIndex < NUM_COLORS) col = SEGCOLOR(popcorn[i].colIndex); - uint16_t ledIndex = SEGMENT.start + popcorn[i].pos; - if(ledIndex >= SEGMENT.start && ledIndex < SEGMENT.stop) setPixelColor(ledIndex, col); + uint16_t ledIndex = popcorn[i].pos; + if (ledIndex < SEGLEN) setPixelColor(ledIndex, col); } else { // if kernel is inactive, randomly pop it - if(random8() < 2) { // POP!!! + if (random8() < 2) { // POP!!! popcorn[i].pos = 0.01f; uint16_t peakHeight = 128 + random8(128); //0-255 @@ -2608,50 +2719,92 @@ uint16_t WS2812FX::mode_popcorn(void) { //Inspired by https://github.com/avanhanegem/ArduinoCandleEffectNeoPixel //and https://cpldcpu.wordpress.com/2016/01/05/reverse-engineering-a-real-candle/ -uint16_t WS2812FX::mode_candle() +uint16_t WS2812FX::candle(bool multi) { - if (SEGENV.call == 0) { - SEGENV.aux0 = 128; SEGENV.aux1 = 132; SEGENV.step = 1; - } - bool newTarget = false; - - uint8_t s = SEGENV.aux0, target = SEGENV.aux1, fadeStep = SEGENV.step; - - if (target > s) { //fade up - s = qadd8(s, fadeStep); - if (s >= target) newTarget = true; - } else { - s = qsub8(s, fadeStep); - if (s <= target) newTarget = true; - } - SEGENV.aux0 = s; - - for (uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) { - setPixelColor(i, color_blend(color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), 255-s)); - } - - if (newTarget) + if (multi) { - uint8_t valrange = SEGMENT.intensity; - uint8_t rndval = valrange >> 1; - target = random8(rndval) + random8(rndval); - if (target < (rndval >> 1)) target = (rndval >> 1) + random8(rndval); - uint8_t offset = (255 - valrange) >> 1; - target += offset; + //allocate segment data + uint16_t dataSize = (SEGLEN -1) *3; + if (!SEGENV.allocateData(dataSize)) return candle(false); //allocation failed + } - uint8_t dif = (target > s) ? target - s : s - target; - - //how much to move closer to target per frame - fadeStep = dif >> 2; //mode called every ~25 ms, so 4 frames to have a new target every 100ms - if (fadeStep == 0) fadeStep = 1; + //max. flicker range controlled by intensity + uint8_t valrange = SEGMENT.intensity; + uint8_t rndval = valrange >> 1; + + //step (how much to move closer to target per frame) coarsely set by speed + uint8_t speedFactor = 4; + if (SEGMENT.speed > 252) { //epilepsy + speedFactor = 1; + } else if (SEGMENT.speed > 99) { //regular candle (mode called every ~25 ms, so 4 frames to have a new target every 100ms) + speedFactor = 2; + } else if (SEGMENT.speed > 49) { //slower fade + speedFactor = 3; + } //else 4 (slowest) + + uint16_t numCandles = (multi) ? SEGLEN : 1; + + for (uint16_t i = 0; i < numCandles; i++) + { + uint16_t d = 0; //data location + + uint8_t s = SEGENV.aux0, s_target = SEGENV.aux1, fadeStep = SEGENV.step; + if (i > 0) { + d = (i-1) *3; + s = SEGENV.data[d]; s_target = SEGENV.data[d+1]; fadeStep = SEGENV.data[d+2]; + } + if (fadeStep == 0) { //init vals + s = 128; s_target = 130 + random8(4); fadeStep = 1; + } + + bool newTarget = false; + if (s_target > s) { //fade up + s = qadd8(s, fadeStep); + if (s >= s_target) newTarget = true; + } else { + s = qsub8(s, fadeStep); + if (s <= s_target) newTarget = true; + } + + if (newTarget) { + s_target = random8(rndval) + random8(rndval); + if (s_target < (rndval >> 1)) s_target = (rndval >> 1) + random8(rndval); + uint8_t offset = (255 - valrange) >> 1; + s_target += offset; + + uint8_t dif = (s_target > s) ? s_target - s : s - s_target; - SEGENV.step = fadeStep; - SEGENV.aux1 = target; + fadeStep = dif >> speedFactor; + if (fadeStep == 0) fadeStep = 1; + } + + if (i > 0) { + setPixelColor(i, color_blend(SEGCOLOR(1), color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), s)); + + SEGENV.data[d] = s; SEGENV.data[d+1] = s_target; SEGENV.data[d+2] = fadeStep; + } else { + for (uint16_t j = 0; j < SEGLEN; j++) { + setPixelColor(j, color_blend(SEGCOLOR(1), color_from_palette(j, true, PALETTE_SOLID_WRAP, 0), s)); + } + + SEGENV.aux0 = s; SEGENV.aux1 = s_target; SEGENV.step = fadeStep; + } } return FRAMETIME; } +uint16_t WS2812FX::mode_candle() +{ + return candle(false); +} + + +uint16_t WS2812FX::mode_candle_multi() +{ + return candle(true); +} + /* / Fireworks in starburst effect @@ -2766,7 +2919,7 @@ uint16_t WS2812FX::mode_starburst(void) { if (start == end) end++; if (end > SEGLEN) end = SEGLEN; for (int p = start; p < end; p++) { - setPixelColor(SEGMENT.start+p, c.r, c.g, c.b); + setPixelColor(p, c.r, c.g, c.b); } } } @@ -2790,9 +2943,9 @@ uint16_t WS2812FX::mode_exploding_fireworks(void) fill(BLACK); - bool actuallyReverse = SEGMENT.getOption(1); + bool actuallyReverse = SEGMENT.getOption(SEG_OPTION_REVERSED); //have fireworks start in either direction based on intensity - SEGMENT.setOption(1, SEGENV.step); + SEGMENT.setOption(SEG_OPTION_REVERSED, SEGENV.step); Spark* sparks = reinterpret_cast(SEGENV.data); Spark* flare = sparks; //first spark is flare data @@ -2814,7 +2967,7 @@ uint16_t WS2812FX::mode_exploding_fireworks(void) // launch if (flare->vel > 12 * gravity) { // flare - setPixelColor(SEGMENT.start + int(flare->pos),flare->col,flare->col,flare->col); + setPixelColor(int(flare->pos),flare->col,flare->col,flare->col); flare->pos += flare->vel; flare->pos = constrain(flare->pos, 0, SEGLEN-1); @@ -2868,7 +3021,7 @@ uint16_t WS2812FX::mode_exploding_fireworks(void) c.g = qsub8(c.g, cooling); c.b = qsub8(c.b, cooling * 2); } - setPixelColor(SEGMENT.start + int(sparks[i].pos), c.red, c.green, c.blue); + setPixelColor(int(sparks[i].pos), c.red, c.green, c.blue); } } dying_gravity *= .99; // as sparks burn out they fall slower @@ -2883,7 +3036,7 @@ uint16_t WS2812FX::mode_exploding_fireworks(void) } } - SEGMENT.setOption(1, actuallyReverse); + SEGMENT.setOption(SEG_OPTION_REVERSED, actuallyReverse); return FRAMETIME; } @@ -2896,7 +3049,7 @@ uint16_t WS2812FX::mode_exploding_fireworks(void) uint16_t WS2812FX::mode_drip(void) { //allocate segment data - uint16_t numDrops = 2; + uint16_t numDrops = 4; uint16_t dataSize = sizeof(spark) * numDrops; if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed @@ -2904,6 +3057,8 @@ uint16_t WS2812FX::mode_drip(void) Spark* drops = reinterpret_cast(SEGENV.data); + numDrops = 1 + (SEGMENT.intensity >> 6); + float gravity = -0.001 - (SEGMENT.speed/50000.0); gravity *= SEGLEN; int sourcedrop = 12; @@ -2916,12 +3071,12 @@ uint16_t WS2812FX::mode_drip(void) drops[j].colIndex = 1; // drop state (0 init, 1 forming, 2 falling, 5 bouncing) } - setPixelColor(SEGMENT.start + SEGLEN-1,color_blend(BLACK,SEGCOLOR(0), sourcedrop));// water source + setPixelColor(SEGLEN-1,color_blend(BLACK,SEGCOLOR(0), sourcedrop));// water source if (drops[j].colIndex==1) { if (drops[j].col>255) drops[j].col=255; - setPixelColor(SEGMENT.start + int(drops[j].pos),color_blend(BLACK,SEGCOLOR(0),drops[j].col)); + setPixelColor(int(drops[j].pos),color_blend(BLACK,SEGCOLOR(0),drops[j].col)); - drops[j].col += map(SEGMENT.intensity, 0, 255, 1, 6); // swelling + drops[j].col += map(SEGMENT.speed, 0, 255, 1, 6); // swelling if (random8() < drops[j].col/10) { // random drop drops[j].colIndex=2; //fall @@ -2935,11 +3090,11 @@ uint16_t WS2812FX::mode_drip(void) drops[j].vel += gravity; for (int i=1;i<7-drops[j].colIndex;i++) { // some minor math so we don't expand bouncing droplets - setPixelColor(SEGMENT.start + int(drops[j].pos)+i,color_blend(BLACK,SEGCOLOR(0),drops[j].col/i)); //spread pixel with fade while falling + setPixelColor(int(drops[j].pos)+i,color_blend(BLACK,SEGCOLOR(0),drops[j].col/i)); //spread pixel with fade while falling } if (drops[j].colIndex > 2) { // during bounce, some water is on the floor - setPixelColor(SEGMENT.start,color_blend(SEGCOLOR(0),BLACK,drops[j].col)); + setPixelColor(0,color_blend(SEGCOLOR(0),BLACK,drops[j].col)); } } else { // we hit bottom if (drops[j].colIndex > 2) { // already hit once, so back to forming @@ -2960,3 +3115,222 @@ uint16_t WS2812FX::mode_drip(void) } return FRAMETIME; } + + +/* +/ Plasma Effect +/ adapted from https://github.com/atuline/FastLED-Demos/blob/master/plasma/plasma.ino +*/ +uint16_t WS2812FX::mode_plasma(void) { + uint8_t thisPhase = beatsin8(6,-64,64); // Setting phase change for a couple of waves. + uint8_t thatPhase = beatsin8(7,-64,64); + + for (int i = 0; i < SEGLEN; i++) { // For each of the LED's in the strand, set color & brightness based on a wave as follows: + uint8_t colorIndex = cubicwave8((i*(1+ 3*(SEGMENT.speed >> 5)))+(thisPhase) & 0xFF)/2 // factor=23 // Create a wave and add a phase change and add another wave with its own phase change. + + cos8((i*(1+ 2*(SEGMENT.speed >> 5)))+(thatPhase) & 0xFF)/2; // factor=15 // Hey, you can even change the frequencies if you wish. + uint8_t thisBright = qsub8(colorIndex, beatsin8(6,0, (255 - SEGMENT.intensity)|0x01 )); + CRGB color = ColorFromPalette(currentPalette, colorIndex, thisBright, LINEARBLEND); + setPixelColor(i, color.red, color.green, color.blue); + } + + return FRAMETIME; +} + + +/* + * Percentage display + * Intesity values from 0-100 turn on the leds. + */ +uint16_t WS2812FX::mode_percent(void) { + + uint8_t percent = MAX(0, MIN(200, SEGMENT.intensity)); + uint16_t active_leds = (percent < 100) ? SEGLEN * percent / 100.0 + : SEGLEN * (200 - percent) / 100.0; + + uint8_t size = (1 + ((SEGMENT.speed * SEGLEN) >> 11)); + if (SEGMENT.speed == 255) size = 255; + + if (percent < 100) { + for (uint16_t i = 0; i < SEGLEN; i++) { + if (i < SEGENV.step) { + setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); + } + else { + setPixelColor(i, SEGCOLOR(1)); + } + } + } else { + for (uint16_t i = 0; i < SEGLEN; i++) { + if (i < (SEGLEN - SEGENV.step)) { + setPixelColor(i, SEGCOLOR(1)); + } + else { + setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); + } + } + } + + if(active_leds > SEGENV.step) { // smooth transition to the target value + SEGENV.step += size; + if (SEGENV.step > active_leds) SEGENV.step = active_leds; + } else if (active_leds < SEGENV.step) { + if (SEGENV.step > size) SEGENV.step -= size; else SEGENV.step = 0; + if (SEGENV.step < active_leds) SEGENV.step = active_leds; + } + + return FRAMETIME; +} + +/* +/ Modulates the brightness similar to a heartbeat +*/ +uint16_t WS2812FX::mode_heartbeat(void) { + uint8_t bpm = 40 + (SEGMENT.speed >> 4); + uint32_t msPerBeat = (60000 / bpm); + uint32_t secondBeat = (msPerBeat / 3); + + uint32_t bri_lower = SEGENV.aux1; + bri_lower = bri_lower * 2042 / (2048 + SEGMENT.intensity); + SEGENV.aux1 = bri_lower; + + unsigned long beatTimer = millis() - SEGENV.step; + if((beatTimer > secondBeat) && !SEGENV.aux0) { // time for the second beat? + SEGENV.aux1 = UINT16_MAX; //full bri + SEGENV.aux0 = 1; + } + if(beatTimer > msPerBeat) { // time to reset the beat timer? + SEGENV.aux1 = UINT16_MAX; //full bri + SEGENV.aux0 = 0; + SEGENV.step = millis(); + } + + for (uint16_t i = 0; i < SEGLEN; i++) { + setPixelColor(i, color_blend(color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), 255 - (SEGENV.aux1 >> 8))); + } + + return FRAMETIME; +} + + +// "Pacifica" +// Gentle, blue-green ocean waves. +// December 2019, Mark Kriegsman and Mary Corey March. +// For Dan. +// +// +// In this animation, there are four "layers" of waves of light. +// +// Each layer moves independently, and each is scaled separately. +// +// All four wave layers are added together on top of each other, and then +// another filter is applied that adds "whitecaps" of brightness where the +// waves line up with each other more. Finally, another pass is taken +// over the led array to 'deepen' (dim) the blues and greens. +// +// The speed and scale and motion each layer varies slowly within independent +// hand-chosen ranges, which is why the code has a lot of low-speed 'beatsin8' functions +// with a lot of oddly specific numeric ranges. +// +// These three custom blue-green color palettes were inspired by the colors found in +// the waters off the southern coast of California, https://goo.gl/maps/QQgd97jjHesHZVxQ7 +// +// Modified for WLED, based on https://github.com/FastLED/FastLED/blob/master/examples/Pacifica/Pacifica.ino +// +uint16_t WS2812FX::mode_pacifica() +{ + CRGBPalette16 pacifica_palette_1 = + { 0x000507, 0x000409, 0x00030B, 0x00030D, 0x000210, 0x000212, 0x000114, 0x000117, + 0x000019, 0x00001C, 0x000026, 0x000031, 0x00003B, 0x000046, 0x14554B, 0x28AA50 }; + CRGBPalette16 pacifica_palette_2 = + { 0x000507, 0x000409, 0x00030B, 0x00030D, 0x000210, 0x000212, 0x000114, 0x000117, + 0x000019, 0x00001C, 0x000026, 0x000031, 0x00003B, 0x000046, 0x0C5F52, 0x19BE5F }; + CRGBPalette16 pacifica_palette_3 = + { 0x000208, 0x00030E, 0x000514, 0x00061A, 0x000820, 0x000927, 0x000B2D, 0x000C33, + 0x000E39, 0x001040, 0x001450, 0x001860, 0x001C70, 0x002080, 0x1040BF, 0x2060FF }; + + if (SEGMENT.palette) { + pacifica_palette_1 = currentPalette; + pacifica_palette_2 = currentPalette; + pacifica_palette_3 = currentPalette; + } + + // Increment the four "color index start" counters, one for each wave layer. + // Each is incremented at a different speed, and the speeds vary over time. + uint16_t sCIStart1 = SEGENV.aux0, sCIStart2 = SEGENV.aux1, sCIStart3 = SEGENV.step, sCIStart4 = SEGENV.step >> 16; + //static uint16_t sCIStart1, sCIStart2, sCIStart3, sCIStart4; + uint32_t deltams = 26 + (SEGMENT.speed >> 3); + + uint16_t speedfactor1 = beatsin16(3, 179, 269); + uint16_t speedfactor2 = beatsin16(4, 179, 269); + uint32_t deltams1 = (deltams * speedfactor1) / 256; + uint32_t deltams2 = (deltams * speedfactor2) / 256; + uint32_t deltams21 = (deltams1 + deltams2) / 2; + sCIStart1 += (deltams1 * beatsin88(1011,10,13)); + sCIStart2 -= (deltams21 * beatsin88(777,8,11)); + sCIStart3 -= (deltams1 * beatsin88(501,5,7)); + sCIStart4 -= (deltams2 * beatsin88(257,4,6)); + SEGENV.aux0 = sCIStart1; SEGENV.aux1 = sCIStart2; + SEGENV.step = sCIStart4; SEGENV.step = (SEGENV.step << 16) + sCIStart3; + + // Clear out the LED array to a dim background blue-green + //fill(132618); + + uint8_t basethreshold = beatsin8( 9, 55, 65); + uint8_t wave = beat8( 7 ); + + for( uint16_t i = 0; i < SEGLEN; i++) { + CRGB c = CRGB(2, 6, 10); + // Render each of four layers, with different scales and speeds, that vary over time + c += pacifica_one_layer(i, pacifica_palette_1, sCIStart1, beatsin16(3, 11 * 256, 14 * 256), beatsin8(10, 70, 130), 0-beat16(301)); + c += pacifica_one_layer(i, pacifica_palette_2, sCIStart2, beatsin16(4, 6 * 256, 9 * 256), beatsin8(17, 40, 80), beat16(401)); + c += pacifica_one_layer(i, pacifica_palette_3, sCIStart3, 6 * 256 , beatsin8(9, 10,38) , 0-beat16(503)); + c += pacifica_one_layer(i, pacifica_palette_3, sCIStart4, 5 * 256 , beatsin8(8, 10,28) , beat16(601)); + + // Add extra 'white' to areas where the four layers of light have lined up brightly + uint8_t threshold = scale8( sin8( wave), 20) + basethreshold; + wave += 7; + uint8_t l = c.getAverageLight(); + if (l > threshold) { + uint8_t overage = l - threshold; + uint8_t overage2 = qadd8(overage, overage); + c += CRGB(overage, overage2, qadd8(overage2, overage2)); + } + + //deepen the blues and greens + c.blue = scale8(c.blue, 145); + c.green = scale8(c.green, 200); + c |= CRGB( 2, 5, 7); + + setPixelColor(i, c.red, c.green, c.blue); + } + + return FRAMETIME; +} + +// Add one layer of waves into the led array +CRGB WS2812FX::pacifica_one_layer(uint16_t i, CRGBPalette16& p, uint16_t cistart, uint16_t wavescale, uint8_t bri, uint16_t ioff) +{ + uint16_t ci = cistart; + uint16_t waveangle = ioff; + uint16_t wavescale_half = (wavescale >> 1) + 20; + + waveangle += ((120 + SEGMENT.intensity) * i); //original 250 * i + uint16_t s16 = sin16(waveangle) + 32768; + uint16_t cs = scale16(s16, wavescale_half) + wavescale_half; + ci += (cs * i); + uint16_t sindex16 = sin16(ci) + 32768; + uint8_t sindex8 = scale16(sindex16, 240); + return ColorFromPalette(p, sindex8, bri, LINEARBLEND); +} + +//Solid colour background with glitter +uint16_t WS2812FX::mode_solid_glitter() +{ + fill(SEGCOLOR(0)); + + if (SEGMENT.intensity > random8()) + { + setPixelColor(random16(SEGLEN), ULTRAWHITE); + } + return FRAMETIME; +} diff --git a/wled00/FX.h b/wled00/FX.h index e3a5c3a9..728ab224 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -28,6 +28,7 @@ #define WS2812FX_h #include "NpbWrapper.h" +#include "const.h" #define FASTLED_INTERNAL //remove annoying pragma messages #include "FastLED.h" @@ -37,8 +38,8 @@ #define DEFAULT_SPEED (uint8_t)128 #define DEFAULT_COLOR (uint32_t)0xFFAA00 -#define min(a,b) ((a)<(b)?(a):(b)) -#define max(a,b) ((a)>(b)?(a):(b)) +#define MIN(a,b) ((a)<(b)?(a):(b)) +#define MAX(a,b) ((a)>(b)?(a):(b)) /* Not used in all effects yet */ #define WLED_FPS 42 @@ -55,11 +56,14 @@ #define MAX_SEGMENT_DATA 8192 #endif +#define LED_SKIP_AMOUNT 1 +#define MIN_SHOW_DELAY 15 + #define NUM_COLORS 3 /* number of colors per segment */ #define SEGMENT _segments[_segment_index] #define SEGCOLOR(x) gamma32(_segments[_segment_index].colors[x]) #define SEGENV _segment_runtimes[_segment_index] -#define SEGLEN SEGMENT.length() +#define SEGLEN _virtualSegmentLength #define SEGACT SEGMENT.stop #define SPEED_FORMULA_L 5 + (50*(255 - SEGMENT.speed))/SEGLEN #define RESET_RUNTIME memset(_segment_runtimes, 0, sizeof(_segment_runtimes)) @@ -80,18 +84,21 @@ // options // bit 7: segment is in transition mode -// bits 2-6: TBD +// bits 3-6: TBD +// bit 2: segment is on // bit 1: reverse segment // bit 0: segment is selected #define NO_OPTIONS (uint8_t)0x00 #define TRANSITIONAL (uint8_t)0x80 +#define SEGMENT_ON (uint8_t)0x04 #define REVERSE (uint8_t)0x02 #define SELECTED (uint8_t)0x01 #define IS_TRANSITIONAL ((SEGMENT.options & TRANSITIONAL) == TRANSITIONAL) -#define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE ) -#define IS_SELECTED ((SEGMENT.options & SELECTED) == SELECTED ) +#define IS_SEGMENT_ON ((SEGMENT.options & SEGMENT_ON ) == SEGMENT_ON ) +#define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE ) +#define IS_SELECTED ((SEGMENT.options & SELECTED ) == SELECTED ) -#define MODE_COUNT 97 +#define MODE_COUNT 104 #define FX_MODE_STATIC 0 #define FX_MODE_BLINK 1 @@ -190,7 +197,13 @@ #define FX_MODE_SINELON_RAINBOW 94 #define FX_MODE_POPCORN 95 #define FX_MODE_DRIP 96 - +#define FX_MODE_PLASMA 97 +#define FX_MODE_PERCENT 98 +#define FX_MODE_RIPPLE_RAINBOW 99 +#define FX_MODE_HEARTBEAT 100 +#define FX_MODE_PACIFICA 101 +#define FX_MODE_CANDLE_MULTI 102 +#define FX_MODE_SOLID_GLITTER 103 class WS2812FX { typedef uint16_t (WS2812FX::*mode_ptr)(void); @@ -207,8 +220,8 @@ class WS2812FX { uint8_t intensity; uint8_t palette; uint8_t mode; - uint8_t options; //bit pattern: msb first: transitional tbd tbd tbd tbd paused reverse selected - uint8_t group, spacing; + uint8_t options; //bit pattern: msb first: transitional needspixelstate tbd tbd (paused) on reverse selected + uint8_t grouping, spacing; uint8_t opacity; uint32_t colors[NUM_COLORS]; void setOption(uint8_t n, bool val) @@ -236,6 +249,15 @@ class WS2812FX { { return stop - start; } + uint16_t groupLength() + { + return grouping + spacing; + } + uint16_t virtualLength() + { + uint16_t groupLen = groupLength(); + return (length() + groupLen -1) / groupLen; + } } segment; // segment runtime parameters @@ -367,6 +389,13 @@ class WS2812FX { _mode[FX_MODE_SINELON_RAINBOW] = &WS2812FX::mode_sinelon_rainbow; _mode[FX_MODE_POPCORN] = &WS2812FX::mode_popcorn; _mode[FX_MODE_DRIP] = &WS2812FX::mode_drip; + _mode[FX_MODE_PLASMA] = &WS2812FX::mode_plasma; + _mode[FX_MODE_PERCENT] = &WS2812FX::mode_percent; + _mode[FX_MODE_RIPPLE_RAINBOW] = &WS2812FX::mode_ripple_rainbow; + _mode[FX_MODE_HEARTBEAT] = &WS2812FX::mode_heartbeat; + _mode[FX_MODE_PACIFICA] = &WS2812FX::mode_pacifica; + _mode[FX_MODE_CANDLE_MULTI] = &WS2812FX::mode_candle_multi; + _mode[FX_MODE_SOLID_GLITTER] = &WS2812FX::mode_solid_glitter; _brightness = DEFAULT_BRIGHTNESS; currentPalette = CRGBPalette16(CRGB::Black); @@ -379,7 +408,7 @@ class WS2812FX { } void - init(bool supportWhite, uint16_t countPixels, bool skipFirs, uint8_t disableNLeds), + init(bool supportWhite, uint16_t countPixels, bool skipFirst), service(void), blur(uint8_t), fade_out(uint8_t r), @@ -387,18 +416,16 @@ class WS2812FX { setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0), setColor(uint8_t slot, uint32_t c), setBrightness(uint8_t b), - driverModeCronixie(bool b), - setCronixieDigits(byte* d), - setCronixieBacklight(bool b), setRange(uint16_t i, uint16_t i2, uint32_t col), setShowCallback(show_callback cb), setTransitionMode(bool t), trigger(void), - setSegment(uint8_t n, uint16_t start, uint16_t stop), + setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 0, uint8_t spacing = 0), resetSegments(), setPixelColor(uint16_t n, uint32_t c), setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0), - show(void); + show(void), + setRgbwPwm(void); bool reverseMode = false, @@ -410,11 +437,11 @@ class WS2812FX { uint8_t mainSegment = 0, + rgbwMode = RGBW_MODE_DUAL, paletteFade = 0, paletteBlend = 0, colorOrder = 0, milliampsPerLed = 55, - _disableNLeds = 0, getBrightness(void), getMode(void), getSpeed(void), @@ -429,8 +456,7 @@ class WS2812FX { uint16_t ablMilliampsMax, currentMilliamps, - triwave16(uint16_t), - getUsableCount(); + triwave16(uint16_t); uint32_t timebase, @@ -538,7 +564,7 @@ class WS2812FX { mode_twinklecat(void), mode_halloween_eyes(void), mode_static_pattern(void), - mode_tri_static_pattern(void), + mode_tri_static_pattern(void), mode_spots(void), mode_spots_fade(void), mode_glitter(void), @@ -550,8 +576,14 @@ class WS2812FX { mode_sinelon_dual(void), mode_sinelon_rainbow(void), mode_popcorn(void), - mode_drip(void); - + mode_drip(void), + mode_plasma(void), + mode_percent(void), + mode_ripple_rainbow(void), + mode_heartbeat(void), + mode_pacifica(void), + mode_candle_multi(void), + mode_solid_glitter(void); private: NeoPixelWrapper *bus; @@ -562,23 +594,20 @@ class WS2812FX { CRGBPalette16 targetPalette; uint32_t now; - uint16_t _length, _lengthRaw, _usableCount; + uint16_t _length, _lengthRaw, _virtualSegmentLength; uint16_t _rand16seed; uint8_t _brightness; static uint16_t _usedSegmentData; + void load_gradient_palette(uint8_t); void handle_palette(void); void fill(uint32_t); bool - _rgbwMode, - _cronixieMode, - _cronixieBacklightEnabled, + _useRgbw = false, _skipFirstMode, _triggered; - byte _cronixieDigits[6]; - mode_ptr _mode[MODE_COUNT]; // SRAM footprint: 4 bytes per element show_callback _callback = nullptr; @@ -586,6 +615,7 @@ class WS2812FX { // mode helper functions uint16_t blink(uint32_t, uint32_t, bool strobe, bool), + candle(bool), color_wipe(bool, bool), scan(bool), theater_chase(uint32_t, uint32_t, bool), @@ -595,25 +625,35 @@ class WS2812FX { dissolve(uint32_t), chase(uint32_t, uint32_t, uint32_t, bool), gradient_base(bool), - police_base(uint32_t, uint32_t), + ripple_base(bool), + police_base(uint32_t, uint32_t, bool), running(uint32_t, uint32_t), tricolor_chase(uint32_t, uint32_t), twinklefox_base(bool), spots_base(uint16_t); CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat); + CRGB pacifica_one_layer(uint16_t i, CRGBPalette16& p, uint16_t cistart, uint16_t wavescale, uint8_t bri, uint16_t ioff); uint32_t _lastPaletteChange = 0; uint32_t _lastShow = 0; + #ifdef WLED_USE_ANALOG_LEDS + uint32_t _analogLastShow = 0; + RgbwColor _analogLastColor = 0; + uint8_t _analogLastBri = 0; + #endif + uint8_t _segment_index = 0; uint8_t _segment_index_palette_last = 99; segment _segments[MAX_NUM_SEGMENTS] = { // SRAM footprint: 24 bytes per element - // start, stop, speed, intensity, palette, mode, options, 3 unused bytes (group, spacing, opacity), color[] + // start, stop, speed, intensity, palette, mode, options, grouping, spacing, opacity (unused), color[] { 0, 7, DEFAULT_SPEED, 128, 0, DEFAULT_MODE, NO_OPTIONS, 1, 0, 255, {DEFAULT_COLOR}} }; segment_runtime _segment_runtimes[MAX_NUM_SEGMENTS]; // SRAM footprint: 28 bytes per element friend class Segment_runtime; + + uint16_t realPixelIndex(uint16_t i); }; @@ -628,17 +668,18 @@ const char JSON_mode_names[] PROGMEM = R"=====([ "Scanner Dual","Stream 2","Oscillate","Pride 2015","Juggle","Palette","Fire 2012","Colorwaves","Bpm","Fill Noise", "Noise 1","Noise 2","Noise 3","Noise 4","Colortwinkles","Lake","Meteor","Meteor Smooth","Railway","Ripple", "Twinklefox","Twinklecat","Halloween Eyes","Solid Pattern","Solid Pattern Tri","Spots","Spots Fade","Glitter","Candle","Fireworks Starburst", -"Fireworks 1D","Bouncing Balls","Sinelon","Sinelon Dual","Sinelon Rainbow","Popcorn","Drip" +"Fireworks 1D","Bouncing Balls","Sinelon","Sinelon Dual","Sinelon Rainbow","Popcorn","Drip","Plasma","Percent","Ripple Rainbow", +"Heartbeat","Pacifica","Candle Multi", "Solid Glitter" ])====="; const char JSON_palette_names[] PROGMEM = R"=====([ -"Default","Random Cycle","Primary Color","Based on Primary","Set Colors","Based on Set","Party","Cloud","Lava","Ocean", +"Default","* Random Cycle","* Color 1","* Colors 1&2","* Color Gradient","* Colors Only","Party","Cloud","Lava","Ocean", "Forest","Rainbow","Rainbow Bands","Sunset","Rivendell","Breeze","Red & Blue","Yellowout","Analogous","Splash", "Pastel","Sunset 2","Beech","Vintage","Departure","Landscape","Beach","Sherbet","Hult","Hult 64", "Drywet","Jul","Grintage","Rewhi","Tertiary","Fire","Icefire","Cyane","Light Pink","Autumn", "Magenta","Magred","Yelmag","Yelblu","Orange & Teal","Tiamat","April Night","Orangery","C9","Sakura", -"Aurora" +"Aurora","Atlantica" ])====="; #endif diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 3081c2be..3843a350 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -27,31 +27,33 @@ #include "FX.h" #include "palettes.h" -#define LED_SKIP_AMOUNT 1 -#define MIN_SHOW_DELAY 15 +//enable custom per-LED mapping. This can allow for better effects on matrices or special displays +//#define WLED_CUSTOM_LED_MAPPING -void WS2812FX::init(bool supportWhite, uint16_t countPixels, bool skipFirst, uint8_t disableNLeds) +#ifdef WLED_CUSTOM_LED_MAPPING +//this is just an example (30 LEDs). It will first set all even, then all uneven LEDs. +const uint16_t customMappingTable[] = { + 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, + 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29}; + +//another example. Switches direction every 5 LEDs. +/*const uint16_t customMappingTable[] = { + 0, 1, 2, 3, 4, 9, 8, 7, 6, 5, 10, 11, 12, 13, 14, + 19, 18, 17, 16, 15, 20, 21, 22, 23, 24, 29, 28, 27, 26, 25};*/ + +const uint16_t customMappingSize = sizeof(customMappingTable)/sizeof(uint16_t); //30 in example +#endif + +void WS2812FX::init(bool supportWhite, uint16_t countPixels, bool skipFirst) { - if (supportWhite == _rgbwMode && countPixels == _length && disableNLeds == _disableNLeds) return; + if (supportWhite == _useRgbw && countPixels == _length && _skipFirstMode == skipFirst) return; RESET_RUNTIME; - _rgbwMode = supportWhite; - _skipFirstMode = skipFirst; + _useRgbw = supportWhite; _length = countPixels; - - if (disableNLeds > 0) { - uint16_t groupCount = disableNLeds +1; - //since 1st led is lit, even partial group has a led lit, whereas int division truncates decimal. - bool hasExtraLight = _length % groupCount != 0; - _usableCount = _length/groupCount; - _usableCount += hasExtraLight ? 1 : 0; - } else { - _usableCount = _length; - } - - _disableNLeds = disableNLeds; + _skipFirstMode = skipFirst; uint8_t ty = 1; - if (supportWhite) ty =2; + if (supportWhite) ty = 2; _lengthRaw = _length; if (_skipFirstMode) { _lengthRaw += LED_SKIP_AMOUNT; @@ -60,7 +62,7 @@ void WS2812FX::init(bool supportWhite, uint16_t countPixels, bool skipFirst, uin bus->Begin((NeoPixelType)ty, _lengthRaw); _segments[0].start = 0; - _segments[0].stop = _usableCount; + _segments[0].stop = _length; setBrightness(_brightness); } @@ -70,6 +72,7 @@ void WS2812FX::service() { now = nowUp + timebase; if (nowUp - _lastShow < MIN_SHOW_DELAY) return; bool doShow = false; + for(uint8_t i=0; i < MAX_NUM_SEGMENTS; i++) { _segment_index = i; @@ -77,6 +80,8 @@ void WS2812FX::service() { { if(nowUp > SEGENV.next_time || _triggered || (doShow && SEGMENT.mode == 0)) //last is temporary { + if (SEGMENT.grouping == 0) SEGMENT.grouping = 1; //sanity check + _virtualSegmentLength = SEGMENT.virtualLength(); doShow = true; handle_palette(); uint16_t delay = (this->*_mode[SEGMENT.mode])(); @@ -85,6 +90,7 @@ void WS2812FX::service() { } } } + _virtualSegmentLength = 0; if(doShow) { yield(); show(); @@ -100,85 +106,88 @@ void WS2812FX::setPixelColor(uint16_t n, uint32_t c) { setPixelColor(n, r, g, b, w); } +uint16_t WS2812FX::realPixelIndex(uint16_t i) { + int16_t iGroup = i * SEGMENT.groupLength(); + + /* reverse just an individual segment */ + int16_t realIndex = iGroup; + if (IS_REVERSE) realIndex = SEGMENT.length() -iGroup -1; + + realIndex += SEGMENT.start; + /* Reverse the whole string */ + if (reverseMode) realIndex = _length - 1 - realIndex; + + return realIndex; +} + void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w) { - i = i * (_disableNLeds+1); - if (IS_REVERSE) i = SEGMENT.stop -1 -i + SEGMENT.start; //reverse just individual segment - byte tmpg = g; - switch (colorOrder) //0 = Grb, default - { - case 0: break; //0 = Grb, default - case 1: g = r; r = tmpg; break; //1 = Rgb, common for WS2811 - case 2: g = b; b = tmpg; break; //2 = Brg - case 3: g = b; b = r; r = tmpg; //3 = Rbg - } - if (!_cronixieMode) - { - if (reverseMode) i = _length -1 -i; - if (_skipFirstMode) - { - if (i < LED_SKIP_AMOUNT) bus->SetPixelColor(i, RgbwColor(0,0,0,0)); - i += LED_SKIP_AMOUNT; - } - if (i < _lengthRaw) bus->SetPixelColor(i, RgbwColor(r,g,b,w)); - if (_disableNLeds > 0) { - for(uint16_t offCount=0; offCount < _disableNLeds; offCount++) { - if (i < _lengthRaw) bus->SetPixelColor((i + offCount + 1), RgbwColor(0,0,0,0)); - } - } - } else { - if(i>6)return; - byte o = 10*i; - if (_cronixieBacklightEnabled && _cronixieDigits[i] <11) + //auto calculate white channel value if enabled + if (_useRgbw) { + if (rgbwMode == RGBW_MODE_AUTO_BRIGHTER || (w == 0 && (rgbwMode == RGBW_MODE_DUAL || rgbwMode == RGBW_MODE_LEGACY))) { - byte r2 = _segments[0].colors[1] >>16; - byte g2 = _segments[0].colors[1] >> 8; - byte b2 = _segments[0].colors[1]; - byte w2 = _segments[0].colors[1] >>24; - for (int j=o; j< o+19; j++) - { - bus->SetPixelColor(j, RgbwColor(r2,g2,b2,w2)); - } - } else + //white value is set to lowest RGB channel + //thank you to @Def3nder! + w = r < g ? (r < b ? r : b) : (g < b ? g : b); + } else if (rgbwMode == RGBW_MODE_AUTO_ACCURATE && w == 0) { - for (int j=o; j< o+19; j++) - { - bus->SetPixelColor(j, RgbwColor(0,0,0,0)); - } - } - if (_skipFirstMode) o += LED_SKIP_AMOUNT; - switch(_cronixieDigits[i]) - { - case 0: bus->SetPixelColor(o+5, RgbwColor(r,g,b,w)); break; - case 1: bus->SetPixelColor(o+0, RgbwColor(r,g,b,w)); break; - case 2: bus->SetPixelColor(o+6, RgbwColor(r,g,b,w)); break; - case 3: bus->SetPixelColor(o+1, RgbwColor(r,g,b,w)); break; - case 4: bus->SetPixelColor(o+7, RgbwColor(r,g,b,w)); break; - case 5: bus->SetPixelColor(o+2, RgbwColor(r,g,b,w)); break; - case 6: bus->SetPixelColor(o+8, RgbwColor(r,g,b,w)); break; - case 7: bus->SetPixelColor(o+3, RgbwColor(r,g,b,w)); break; - case 8: bus->SetPixelColor(o+9, RgbwColor(r,g,b,w)); break; - case 9: bus->SetPixelColor(o+4, RgbwColor(r,g,b,w)); break; + w = r < g ? (r < b ? r : b) : (g < b ? g : b); + r -= w; g -= w; b -= w; } } -} - -void WS2812FX::driverModeCronixie(bool b) -{ - _cronixieMode = b; - _segments[0].stop = (b) ? 6 : _length; -} - -void WS2812FX::setCronixieBacklight(bool b) -{ - _cronixieBacklightEnabled = b; -} - -void WS2812FX::setCronixieDigits(byte d[]) -{ - for (int i = 0; i<6; i++) + + RgbwColor col; + switch (colorOrder) { - _cronixieDigits[i] = d[i]; + case 0: col.G = g; col.R = r; col.B = b; break; //0 = GRB, default + case 1: col.G = r; col.R = g; col.B = b; break; //1 = RGB, common for WS2811 + case 2: col.G = b; col.R = r; col.B = g; break; //2 = BRG + case 3: col.G = r; col.R = b; col.B = g; break; //3 = RBG + case 4: col.G = b; col.R = g; col.B = r; break; //4 = BGR + default: col.G = g; col.R = b; col.B = r; break; //5 = GBR + } + col.W = w; + + uint16_t skip = _skipFirstMode ? LED_SKIP_AMOUNT : 0; + if (SEGLEN) {//from segment + + //color_blend(getpixel, col, SEGMENT.opacity); (pseudocode for future blending of segments) + if (IS_SEGMENT_ON) + { + if (SEGMENT.opacity < 255) { + col.R = scale8(col.R, SEGMENT.opacity); + col.G = scale8(col.G, SEGMENT.opacity); + col.B = scale8(col.B, SEGMENT.opacity); + col.W = scale8(col.W, SEGMENT.opacity); + } + } else { + col = BLACK; + } + + /* Set all the pixels in the group, ensuring _skipFirstMode is honored */ + bool reversed = reverseMode ^ IS_REVERSE; + uint16_t realIndex = realPixelIndex(i); + + for (uint16_t j = 0; j < SEGMENT.grouping; j++) { + int16_t indexSet = realIndex + (reversed ? -j : j); + int16_t indexSetRev = indexSet; + if (reverseMode) indexSetRev = _length - 1 - indexSet; + #ifdef WLED_CUSTOM_LED_MAPPING + if (indexSet < customMappingSize) indexSet = customMappingTable[indexSet]; + #endif + if (indexSetRev >= SEGMENT.start && indexSetRev < SEGMENT.stop) bus->SetPixelColor(indexSet + skip, col); + } + } else { //live data, etc. + if (reverseMode) i = _length - 1 - i; + #ifdef WLED_CUSTOM_LED_MAPPING + if (i < customMappingSize) i = customMappingTable[i]; + #endif + bus->SetPixelColor(i + skip, col); + } + if (skip && i == 0) { + for (uint16_t j = 0; j < skip; j++) { + bus->SetPixelColor(j, RgbwColor(0, 0, 0, 0)); + } } } @@ -201,10 +210,17 @@ void WS2812FX::show(void) { //each LED can draw up 195075 "power units" (approx. 53mA) //one PU is the power it takes to have 1 channel 1 step brighter per brightness step //so A=2,R=255,G=0,B=0 would use 510 PU per LED (1mA is about 3700 PU) - - if (ablMilliampsMax > 149 && milliampsPerLed > 0) //0 mA per LED and too low numbers turn off calculation + bool useWackyWS2815PowerModel = false; + byte actualMilliampsPerLed = milliampsPerLed; + + if(milliampsPerLed == 255) { + useWackyWS2815PowerModel = true; + actualMilliampsPerLed = 12; // from testing an actual strip + } + + if (ablMilliampsMax > 149 && actualMilliampsPerLed > 0) //0 mA per LED and too low numbers turn off calculation { - uint32_t puPerMilliamp = 195075 / milliampsPerLed; + uint32_t puPerMilliamp = 195075 / actualMilliampsPerLed; uint32_t powerBudget = (ablMilliampsMax - MA_FOR_ESP) * puPerMilliamp; //100mA for ESP power if (powerBudget > puPerMilliamp * _length) //each LED uses about 1mA in standby, exclude that from power budget { @@ -219,10 +235,20 @@ void WS2812FX::show(void) { for (uint16_t i = 0; i < _length; i++) //sum up the usage of each LED { RgbwColor c = bus->GetPixelColorRgbw(i); - powerSum += (c.R + c.G + c.B + c.W); + + if(useWackyWS2815PowerModel) + { + // ignore white component on WS2815 power calculation + powerSum += (MAX(MAX(c.R,c.G),c.B)) * 3; + } + else + { + powerSum += (c.R + c.G + c.B + c.W); + } } - if (_rgbwMode) //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less + + if (_useRgbw) //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less { powerSum *= 3; powerSum = powerSum >> 2; //same as /= 4 @@ -278,7 +304,7 @@ uint8_t WS2812FX::getModeCount() uint8_t WS2812FX::getPaletteCount() { - return 13 + gGradientPaletteCount; + return 13 + GRADIENT_PALETTE_COUNT; } //TODO transitions @@ -289,6 +315,8 @@ bool WS2812FX::setEffectConfig(uint8_t m, uint8_t s, uint8_t in, uint8_t p) { Segment& seg = _segments[getMainSegmentId()]; uint8_t modePrev = seg.mode, speedPrev = seg.speed, intensityPrev = seg.intensity, palettePrev = seg.palette; + bool applied = false; + if (applyToAllSelected) { for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) { @@ -298,9 +326,12 @@ bool WS2812FX::setEffectConfig(uint8_t m, uint8_t s, uint8_t in, uint8_t p) { _segments[i].intensity = in; _segments[i].palette = p; setMode(i, m); + applied = true; } } - } else { + } + + if (!applyToAllSelected || !applied) { seg.speed = s; seg.intensity = in; seg.palette = p; @@ -317,12 +348,17 @@ void WS2812FX::setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w void WS2812FX::setColor(uint8_t slot, uint32_t c) { if (slot >= NUM_COLORS) return; + + bool applied = false; + if (applyToAllSelected) { for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) { if (_segments[i].isSelected()) _segments[i].colors[slot] = c; } - } else { + } + + if (!applyToAllSelected || !applied) { _segments[getMainSegmentId()].colors[slot] = c; } } @@ -331,7 +367,7 @@ void WS2812FX::setBrightness(uint8_t b) { if (_brightness == b) return; _brightness = (gammaCorrectBri) ? gamma8(b) : b; _segment_index = 0; - if (SEGENV.next_time > millis() + 22) show();//apply brightness change immediately if no refresh soon + if (SEGENV.next_time > millis() + 22 && millis() - _lastShow > MIN_SHOW_DELAY) show();//apply brightness change immediately if no refresh soon } uint8_t WS2812FX::getMode(void) { @@ -374,40 +410,28 @@ uint32_t WS2812FX::getColor(void) { uint32_t WS2812FX::getPixelColor(uint16_t i) { - i = i * (_disableNLeds+1); - if (reverseMode) i = _length- 1 -i; - if (IS_REVERSE) i = SEGMENT.stop -1 -i + SEGMENT.start; //reverse just individual segment + i = realPixelIndex(i); + + #ifdef WLED_CUSTOM_LED_MAPPING + if (i < customMappingSize) i = customMappingTable[i]; + #endif + if (_skipFirstMode) i += LED_SKIP_AMOUNT; - if (_cronixieMode) - { - if(i>6)return 0; - byte o = 10*i; - switch(_cronixieDigits[i]) - { - case 0: i=o+5; break; - case 1: i=o+0; break; - case 2: i=o+6; break; - case 3: i=o+1; break; - case 4: i=o+7; break; - case 5: i=o+2; break; - case 6: i=o+8; break; - case 7: i=o+3; break; - case 8: i=o+9; break; - case 9: i=o+4; break; - default: return 0; - } - } + if (i >= _lengthRaw) return 0; - RgbwColor lColor = bus->GetPixelColorRgbw(i); - byte r = lColor.R, g = lColor.G, b = lColor.B; + + RgbwColor col = bus->GetPixelColorRgbw(i); switch (colorOrder) { - case 0: break; //0 = Grb - case 1: r = lColor.G; g = lColor.R; break; //1 = Rgb, common for WS2811 - case 2: g = lColor.B; b = lColor.G; break; //2 = Brg - case 3: r = lColor.B; g = lColor.R; b = lColor.G; //3 = Rbg + // W G R B + case 0: return ((col.W << 24) | (col.G << 8) | (col.R << 16) | (col.B)); //0 = GRB, default + case 1: return ((col.W << 24) | (col.R << 8) | (col.G << 16) | (col.B)); //1 = RGB, common for WS2811 + case 2: return ((col.W << 24) | (col.B << 8) | (col.R << 16) | (col.G)); //2 = BRG + case 3: return ((col.W << 24) | (col.B << 8) | (col.G << 16) | (col.R)); //3 = RBG + case 4: return ((col.W << 24) | (col.R << 8) | (col.B << 16) | (col.G)); //4 = BGR + case 5: return ((col.W << 24) | (col.G << 8) | (col.B << 16) | (col.R)); //5 = GBR } - return ( (lColor.W << 24) | (r << 16) | (g << 8) | (b) ); + return 0; } WS2812FX::Segment& WS2812FX::getSegment(uint8_t id) { @@ -423,31 +447,46 @@ WS2812FX::Segment* WS2812FX::getSegments(void) { return _segments; } -uint16_t WS2812FX::getUsableCount(void) { - return _usableCount; -} - uint32_t WS2812FX::getLastShow(void) { return _lastShow; } -void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2) { +void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing) { if (n >= MAX_NUM_SEGMENTS) return; Segment& seg = _segments[n]; - if (seg.start == i1 && seg.stop == i2) return; - _segment_index = n; fill(0); //turn old segment range off + //return if neither bounds nor grouping have changed + if (seg.start == i1 && seg.stop == i2 && (!grouping || (seg.grouping == grouping && seg.spacing == spacing))) return; + + if (seg.stop) setRange(seg.start, seg.stop -1, 0); //turn old segment range off if (i2 <= i1) //disable segment { - seg.stop = 0; return; + seg.stop = 0; + if (n == mainSegment) //if main segment is deleted, set first active as main segment + { + for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) + { + if (_segments[i].isActive()) { + mainSegment = i; + return; + } + } + mainSegment = 0; //should not happen (always at least one active segment) + } + return; } if (i1 < _length) seg.start = i1; seg.stop = i2; if (i2 > _length) seg.stop = _length; + if (grouping) { + seg.grouping = grouping; + seg.spacing = spacing; + } _segment_runtimes[n].reset(); } void WS2812FX::resetSegments() { + mainSegment = 0; memset(_segments, 0, sizeof(_segments)); //memset(_segment_runtimes, 0, sizeof(_segment_runtimes)); _segment_index = 0; @@ -456,10 +495,17 @@ void WS2812FX::resetSegments() { _segments[0].start = 0; _segments[0].speed = DEFAULT_SPEED; _segments[0].stop = _length; - _segments[0].setOption(0, 1); //select + _segments[0].grouping = 1; + _segments[0].setOption(SEG_OPTION_SELECTED, 1); + _segments[0].setOption(SEG_OPTION_ON, 1); + _segments[0].opacity = 255; + for (uint16_t i = 1; i < MAX_NUM_SEGMENTS; i++) { _segments[i].colors[0] = color_wheel(i*51); + _segments[i].grouping = 1; + _segments[i].setOption(SEG_OPTION_ON, 1); + _segments[i].opacity = 255; _segment_runtimes[i].reset(); } _segment_runtimes[0].reset(); @@ -484,7 +530,7 @@ void WS2812FX::setShowCallback(show_callback cb) void WS2812FX::setTransitionMode(bool t) { _segment_index = getMainSegmentId(); - SEGMENT.setOption(7,t); + SEGMENT.setOption(SEG_OPTION_TRANSITIONAL, t); if (!t) return; unsigned long waitMax = millis() + 20; //refresh after 20 ms if transition enabled if (SEGMENT.mode == FX_MODE_STATIC && SEGENV.next_time > waitMax) SEGENV.next_time = waitMax; @@ -519,7 +565,7 @@ uint32_t WS2812FX::color_blend(uint32_t color1, uint32_t color2, uint8_t blend) * Fills segment with color */ void WS2812FX::fill(uint32_t c) { - for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) { + for(uint16_t i = 0; i < SEGLEN; i++) { setPixelColor(i, c); } } @@ -537,7 +583,7 @@ void WS2812FX::fade_out(uint8_t rate) { int g2 = (color >> 8) & 0xff; int b2 = color & 0xff; - for(uint16_t i=SEGMENT.start; i < SEGMENT.stop; i++) { + for(uint16_t i = 0; i < SEGLEN; i++) { color = getPixelColor(i); int w1 = (color >> 24) & 0xff; int r1 = (color >> 16) & 0xff; @@ -567,14 +613,14 @@ void WS2812FX::blur(uint8_t blur_amount) uint8_t keep = 255 - blur_amount; uint8_t seep = blur_amount >> 1; CRGB carryover = CRGB::Black; - for(uint16_t i = SEGMENT.start; i < SEGMENT.stop; i++) + for(uint16_t i = 0; i < SEGLEN; i++) { CRGB cur = col_to_crgb(getPixelColor(i)); CRGB part = cur; part.nscale8(seep); cur.nscale8(keep); cur += carryover; - if(i > SEGMENT.start) { + if(i > 0) { uint32_t c = getPixelColor(i-1); uint8_t r = (c >> 16 & 0xFF); uint8_t g = (c >> 8 & 0xFF); @@ -621,7 +667,7 @@ uint8_t WS2812FX::get_random_wheel_index(uint8_t pos) { r = random8(); x = abs(pos - r); y = 255 - x; - d = min(x, y); + d = MIN(x, y); } return r; } @@ -643,6 +689,15 @@ CRGB WS2812FX::col_to_crgb(uint32_t color) } +void WS2812FX::load_gradient_palette(uint8_t index) +{ + byte i = constrain(index, 0, GRADIENT_PALETTE_COUNT -1); + byte tcp[72]; //support gradient palettes with up to 18 entries + memcpy_P(tcp, (byte*)pgm_read_dword(&(gGradientPalettes[i])), 72); + targetPalette.loadDynamicGradientPalette(tcp); +} + + /* * FastLED palette modes helper function. Limitation: Due to memory reasons, multiple active segments with FastLED will disable the Palette transitions */ @@ -660,13 +715,13 @@ void WS2812FX::handle_palette(void) case 0: {//default palette. Differs depending on effect switch (SEGMENT.mode) { - case FX_MODE_FIRE_2012 : targetPalette = gGradientPalettes[22]; break;//heat palette - case FX_MODE_COLORWAVES : targetPalette = gGradientPalettes[13]; break;//landscape 33 + case FX_MODE_FIRE_2012 : load_gradient_palette(22); break;//heat palette + case FX_MODE_COLORWAVES : load_gradient_palette(13); break;//landscape 33 case FX_MODE_FILLNOISE8 : targetPalette = OceanColors_p; break; - case FX_MODE_NOISE16_1 : targetPalette = gGradientPalettes[17]; break;//Drywet - case FX_MODE_NOISE16_2 : targetPalette = gGradientPalettes[30]; break;//Blue cyan yellow - case FX_MODE_NOISE16_3 : targetPalette = gGradientPalettes[22]; break;//heat palette - case FX_MODE_NOISE16_4 : targetPalette = gGradientPalettes[13]; break;//landscape 33 + case FX_MODE_NOISE16_1 : load_gradient_palette(17); break;//Drywet + case FX_MODE_NOISE16_2 : load_gradient_palette(30); break;//Blue cyan yellow + case FX_MODE_NOISE16_3 : load_gradient_palette(22); break;//heat palette + case FX_MODE_NOISE16_4 : load_gradient_palette(13); break;//landscape 33 //case FX_MODE_GLITTER : targetPalette = RainbowColors_p; break; default: targetPalette = PartyColors_p; break;//palette, bpm @@ -689,25 +744,25 @@ void WS2812FX::handle_palette(void) case 2: {//primary color only CRGB prim = col_to_crgb(SEGCOLOR(0)); targetPalette = CRGBPalette16(prim); break;} - case 3: {//based on primary - //considering performance implications - CRGB prim = col_to_crgb(SEGCOLOR(0)); - CHSV prim_hsv = rgb2hsv_approximate(prim); - targetPalette = CRGBPalette16( - CHSV(prim_hsv.h, prim_hsv.s, prim_hsv.v), //color itself - CHSV(prim_hsv.h, max(prim_hsv.s - 50,0), prim_hsv.v), //less saturated - CHSV(prim_hsv.h, prim_hsv.s, max(prim_hsv.v - 50,0)), //darker - CHSV(prim_hsv.h, prim_hsv.s, prim_hsv.v)); //color itself - break;} - case 4: {//primary + secondary + case 3: {//primary + secondary CRGB prim = col_to_crgb(SEGCOLOR(0)); CRGB sec = col_to_crgb(SEGCOLOR(1)); - targetPalette = CRGBPalette16(sec,prim); break;} - case 5: {//based on primary + secondary + targetPalette = CRGBPalette16(prim,prim,sec,sec); break;} + case 4: {//primary + secondary + tertiary CRGB prim = col_to_crgb(SEGCOLOR(0)); CRGB sec = col_to_crgb(SEGCOLOR(1)); CRGB ter = col_to_crgb(SEGCOLOR(2)); targetPalette = CRGBPalette16(ter,sec,prim); break;} + case 5: {//primary + secondary (+tert if not off), more distinct + CRGB prim = col_to_crgb(SEGCOLOR(0)); + CRGB sec = col_to_crgb(SEGCOLOR(1)); + if (SEGCOLOR(2)) { + CRGB ter = col_to_crgb(SEGCOLOR(2)); + targetPalette = CRGBPalette16(prim,prim,prim,prim,prim,sec,sec,sec,sec,sec,ter,ter,ter,ter,ter,prim); + } else { + targetPalette = CRGBPalette16(prim,prim,prim,prim,prim,prim,prim,prim,sec,sec,sec,sec,sec,sec,sec,sec); + } + break;} case 6: //Party colors targetPalette = PartyColors_p; break; case 7: //Cloud colors @@ -723,7 +778,7 @@ void WS2812FX::handle_palette(void) case 12: //Rainbow stripe colors targetPalette = RainbowStripeColors_p; break; default: //progmem palettes - targetPalette = gGradientPalettes[constrain(SEGMENT.palette -13, 0, gGradientPaletteCount -1)]; + load_gradient_palette(SEGMENT.palette -13); } if (singleSegmentMode && paletteFade) //only blend if just one segment uses FastLED mode @@ -739,7 +794,7 @@ uint32_t WS2812FX::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8 { if (SEGMENT.palette == 0 && mcol < 3) return SEGCOLOR(mcol); //WS2812FX default uint8_t paletteIndex = i; - if (mapping) paletteIndex = map(i,SEGMENT.start,SEGMENT.stop-1,0,255); + if (mapping) paletteIndex = (i*255)/(SEGLEN -1); if (!wrap) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end" CRGB fastled_col; fastled_col = ColorFromPalette( currentPalette, paletteIndex, pbri, (paletteBlend == 3)? NOBLEND:LINEARBLEND); @@ -758,10 +813,46 @@ bool WS2812FX::segmentsAreIdentical(Segment* a, Segment* b) if (a->speed != b->speed) return false; if (a->intensity != b->intensity) return false; if (a->palette != b->palette) return false; - //if (a->getOption(1) != b->getOption(1)) return false; //reverse + //if (a->getOption(SEG_OPTION_REVERSED) != b->getOption(SEG_OPTION_REVERSED)) return false; return true; } +#ifdef WLED_USE_ANALOG_LEDS +void WS2812FX::setRgbwPwm(void) { + uint32_t nowUp = millis(); // Be aware, millis() rolls over every 49 days + if (nowUp - _analogLastShow < MIN_SHOW_DELAY) return; + + _analogLastShow = nowUp; + + RgbwColor color = bus->GetPixelColorRgbw(0); + byte b = getBrightness(); + if (color == _analogLastColor && b == _analogLastBri) return; + + // check color values for Warm / Cold white mix (for RGBW) // EsplanexaDevice.cpp + #ifdef WLED_USE_5CH_LEDS + if (color.R == 255 && color.G == 255 && color.B == 255 && color.W == 255) { + bus->SetRgbwPwm(0, 0, 0, 0, color.W * b / 255); + } else if (color.R == 127 && color.G == 127 && color.B == 127 && color.W == 255) { + bus->SetRgbwPwm(0, 0, 0, color.W * b / 512, color.W * b / 255); + } else if (color.R == 0 && color.G == 0 && color.B == 0 && color.W == 255) { + bus->SetRgbwPwm(0, 0, 0, color.W * b / 255, 0); + } else if (color.R == 130 && color.G == 90 && color.B == 0 && color.W == 255) { + bus->SetRgbwPwm(0, 0, 0, color.W * b / 255, color.W * b / 512); + } else if (color.R == 255 && color.G == 153 && color.B == 0 && color.W == 255) { + bus->SetRgbwPwm(0, 0, 0, color.W * b / 255, 0); + } else { // not only white colors + bus->SetRgbwPwm(color.R * b / 255, color.G * b / 255, color.B * b / 255, color.W * b / 255); + } + #else + bus->SetRgbwPwm(color.R * b / 255, color.G * b / 255, color.B * b / 255, color.W * b / 255); + #endif + _analogLastColor = color; + _analogLastBri = b; +} +#else +void WS2812FX::setRgbwPwm() {} +#endif + //gamma 2.4 lookup table used for color correction const byte gammaT[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, diff --git a/wled00/NpbWrapper.h b/wled00/NpbWrapper.h index 77d4e1e4..4a6e3cdc 100644 --- a/wled00/NpbWrapper.h +++ b/wled00/NpbWrapper.h @@ -3,24 +3,43 @@ #define NpbWrapper_h //PIN CONFIGURATION -#define LEDPIN 2 //strip pin. Any for ESP32, gpio2 or 3 is recommended for ESP8266 (gpio2/3 are labeled D4/RX on NodeMCU and Wemos) +#ifndef LEDPIN +#define LEDPIN 2 //strip pin. Any for ESP32, gpio2 or 3 is recommended for ESP8266 (gpio2/3 are labeled D4/RX on NodeMCU and Wemos) +#endif //#define USE_APA102 // Uncomment for using APA102 LEDs. //#define USE_WS2801 // Uncomment for using WS2801 LEDs (make sure you have NeoPixelBus v2.5.6 or newer) //#define USE_LPD8806 // Uncomment for using LPD8806 +//#define USE_TM1814 // Uncomment for using TM1814 LEDs (make sure you have NeoPixelBus v2.5.7 or newer) +//#define USE_P9813 // Uncomment for using P9813 LEDs (make sure you have NeoPixelBus v2.5.8 or newer) //#define WLED_USE_ANALOG_LEDS //Uncomment for using "dumb" PWM controlled LEDs (see pins below, default R: gpio5, G: 12, B: 15, W: 13) //#define WLED_USE_H801 //H801 controller. Please uncomment #define WLED_USE_ANALOG_LEDS as well -//#define WLED_USE_5CH //5 Channel H801 for cold and warm white +//#define WLED_USE_5CH_LEDS //5 Channel H801 for cold and warm white +//#define WLED_USE_BWLT11 +//#define WLED_USE_SHOJO_PCB +#ifndef BTNPIN #define BTNPIN 0 //button pin. Needs to have pullup (gpio0 recommended) -#define IR_PIN 4 //infrared pin (-1 to disable) MagicHome: 4, H801 Wifi: 0 -#define RLYPIN 12 //pin for relay, will be set HIGH if LEDs are on (-1 to disable). Also usable for standby leds, triggers,... -#define AUXPIN -1 //debug auxiliary output pin (-1 to disable) +#endif -#define RLYMDE 1 //mode for relay, 0: LOW if LEDs are on 1: HIGH if LEDs are on +#ifndef IR_PIN +#define IR_PIN 4 //infrared pin (-1 to disable) MagicHome: 4, H801 Wifi: 0 +#endif + +#ifndef RLYPIN +#define RLYPIN 12 //pin for relay, will be set HIGH if LEDs are on (-1 to disable). Also usable for standby leds, triggers,... +#endif + +#ifndef AUXPIN +#define AUXPIN -1 //debug auxiliary output pin (-1 to disable) +#endif + +#ifndef RLYMDE +#define RLYMDE 1 //mode for relay, 0: LOW if LEDs are on 1: HIGH if LEDs are on +#endif //END CONFIGURATION -#if defined(USE_APA102) || defined(USE_WS2801) || defined(USE_LPD8806) +#if defined(USE_APA102) || defined(USE_WS2801) || defined(USE_LPD8806) || defined(USE_P9813) #define CLKPIN 0 #define DATAPIN 2 #if BTNPIN == CLKPIN || BTNPIN == DATAPIN @@ -34,17 +53,30 @@ #define RPIN 15 //R pin for analog LED strip #define GPIN 13 //G pin for analog LED strip #define BPIN 12 //B pin for analog LED strip - #define WPIN 14 //W pin for analog LED strip (W1: 14, W2: 04) + #define WPIN 14 //W pin for analog LED strip #define W2PIN 04 //W2 pin for analog LED strip #undef BTNPIN #undef IR_PIN #define IR_PIN 0 //infrared pin (-1 to disable) MagicHome: 4, H801 Wifi: 0 + #elif defined(WLED_USE_BWLT11) + //PWM pins - to use with BW-LT11 + #define RPIN 12 //R pin for analog LED strip + #define GPIN 4 //G pin for analog LED strip + #define BPIN 14 //B pin for analog LED strip + #define WPIN 5 //W pin for analog LED strip + #elif defined(WLED_USE_SHOJO_PCB) + //PWM pins - to use with Shojo PCB (https://www.bastelbunker.de/esp-rgbww-wifi-led-controller-vbs-edition/) + #define RPIN 14 //R pin for analog LED strip + #define GPIN 4 //G pin for analog LED strip + #define BPIN 5 //B pin for analog LED strip + #define WPIN 15 //W pin for analog LED strip + #define W2PIN 12 //W2 pin for analog LED strip #else //PWM pins - PINs 5,12,13,15 are used with Magic Home LED Controller - #define RPIN 5 //R pin for analog LED strip - #define GPIN 12 //G pin for analog LED strip - #define BPIN 15 //B pin for analog LED strip - #define WPIN 13 //W pin for analog LED strip (W1: 14, W2: 04) + #define RPIN 5 //R pin for analog LED strip + #define GPIN 12 //G pin for analog LED strip + #define BPIN 15 //B pin for analog LED strip + #define WPIN 13 //W pin for analog LED strip #endif #undef RLYPIN #define RLYPIN -1 //disable as pin 12 is used by analog LEDs @@ -58,6 +90,10 @@ #define PIXELMETHOD NeoWs2801Method #elif defined(USE_LPD8806) #define PIXELMETHOD Lpd8806Method + #elif defined(USE_TM1814) + #define PIXELMETHOD NeoTm1814Method + #elif defined(USE_P9813) + #define PIXELMETHOD P9813Method #else #define PIXELMETHOD NeoEsp32Rmt0Ws2812xMethod #endif @@ -69,6 +105,10 @@ #define PIXELMETHOD NeoWs2801Method #elif defined(USE_LPD8806) #define PIXELMETHOD Lpd8806Method + #elif defined(USE_TM1814) + #define PIXELMETHOD NeoTm1814Method + #elif defined(USE_P9813) + #define PIXELMETHOD P9813Method #elif LEDPIN == 2 #define PIXELMETHOD NeoEsp8266Uart1Ws2813Method //if you get an error here, try to change to NeoEsp8266UartWs2813Method or update Neopixelbus #elif LEDPIN == 3 @@ -86,10 +126,15 @@ #define PIXELFEATURE4 DotStarLbgrFeature #elif defined(USE_LPD8806) #define PIXELFEATURE3 Lpd8806GrbFeature - #define PIXELFEATURE4 Lpd8806GrbFeature #elif defined(USE_WS2801) #define PIXELFEATURE3 NeoRbgFeature #define PIXELFEATURE4 NeoRbgFeature +#elif defined(USE_TM1814) + #define PIXELFEATURE3 NeoWrgbTm1814Feature + #define PIXELFEATURE4 NeoWrgbTm1814Feature +#elif defined(USE_P9813) + #define PIXELFEATURE3 P9813BgrFeature + #define PIXELFEATURE4 NeoGrbwFeature #else #define PIXELFEATURE3 NeoGrbFeature #define PIXELFEATURE4 NeoGrbwFeature @@ -131,7 +176,7 @@ public: switch (_type) { case NeoPixelType_Grb: - #if defined(USE_APA102) || defined(USE_WS2801) || defined(USE_LPD8806) + #if defined(USE_APA102) || defined(USE_WS2801) || defined(USE_LPD8806) || defined(USE_P9813) _pGrb = new NeoPixelBrightnessBus(countPixels, CLKPIN, DATAPIN); #else _pGrb = new NeoPixelBrightnessBus(countPixels, LEDPIN); @@ -140,7 +185,7 @@ public: break; case NeoPixelType_Grbw: - #if defined(USE_APA102) || defined(USE_WS2801) || defined(USE_LPD8806) + #if defined(USE_APA102) || defined(USE_WS2801) || defined(USE_LPD8806) || defined(USE_P9813) _pGrbw = new NeoPixelBrightnessBus(countPixels, CLKPIN, DATAPIN); #else _pGrbw = new NeoPixelBrightnessBus(countPixels, LEDPIN); @@ -167,7 +212,7 @@ public: #endif } #else // ESP8266 - //init PWM pins - PINs 5,12,13,15 are used with Magic Home LED Controller + //init PWM pins pinMode(RPIN, OUTPUT); pinMode(GPIN, OUTPUT); pinMode(BPIN, OUTPUT); @@ -188,9 +233,9 @@ public: void SetRgbwPwm(uint8_t r, uint8_t g, uint8_t b, uint8_t w, uint8_t w2=0) { #ifdef ARDUINO_ARCH_ESP32 - ledcWrite(0, r); //RPIN - ledcWrite(1, g); //GPIN - ledcWrite(2, b); //BPIN + ledcWrite(0, r); + ledcWrite(1, g); + ledcWrite(2, b); switch (_type) { case NeoPixelType_Grb: break; #ifdef WLED_USE_5CH_LEDS @@ -199,7 +244,7 @@ public: case NeoPixelType_Grbw: ledcWrite(3, w); break; #endif } - #else + #else // ESP8266 analogWrite(RPIN, r); analogWrite(GPIN, g); analogWrite(BPIN, b); @@ -230,11 +275,6 @@ public: switch (_type) { case NeoPixelType_Grb: { _pGrb->SetPixelColor(indexPixel, RgbColor(color.R,color.G,color.B)); - #ifdef WLED_USE_ANALOG_LEDS - if (indexPixel != 0) return; //set analog LEDs from first pixel - byte b = _pGrb->GetBrightness(); - SetRgbwPwm(color.R * b / 255, color.G * b / 255, color.B * b / 255, 0); - #endif } break; case NeoPixelType_Grbw: { @@ -243,28 +283,6 @@ public: #else _pGrbw->SetPixelColor(indexPixel, color); #endif - #ifdef WLED_USE_ANALOG_LEDS - if (indexPixel != 0) return; //set analog LEDs from first pixel - byte b = _pGrbw->GetBrightness(); - // check color values for Warm / Cold white mix (for RGBW) // EsplanexaDevice.cpp - #ifdef WLED_USE_5CH_LEDS - if (color.R == 255 & color.G == 255 && color.B == 255 && color.W == 255) { - SetRgbwPwm(0, 0, 0, 0, color.W * b / 255); - } else if (color.R == 127 & color.G == 127 && color.B == 127 && color.W == 255) { - SetRgbwPwm(0, 0, 0, color.W * b / 512, colorW.W * b / 255); - } else if (color.R == 0 & color.G == 0 && color.B == 0 && color.W == 255) { - SetRgbwPwm(0, 0, 0, color.W * b / 255, 0); - } else if (color.R == 130 & color.G == 90 && color.B == 0 && color.W == 255) { - SetRgbwPwm(0, 0, 0, color.W * b / 255, color.W * b / 512); - } else if (color.R == 255 & color.G == 153 && color.B == 0 && color.W == 255) { - SetRgbwPwm(0, 0, 0, color.W * b / 255, 0); - } else { // not only white colors - SetRgbwPwm(color.R * b / 255, colorW.G * b / 255, colorW.B * b / 255, color.W * b / 255); - } - #else - SetRgbwPwm(color.R * b / 255, color.G * b / 255, color.B * b / 255, color.W * b / 255); - #endif - #endif } break; } @@ -290,6 +308,15 @@ public: return 0; } + uint8_t* GetPixels(void) + { + switch (_type) { + case NeoPixelType_Grb: return _pGrb->Pixels(); break; + case NeoPixelType_Grbw: return _pGrbw->Pixels(); break; + } + return 0; + } + private: NeoPixelType _type; diff --git a/wled00/__vm/.wled00.vsarduino.h b/wled00/__vm/.wled00.vsarduino.h index 722f8378..2a9ef40a 100644 --- a/wled00/__vm/.wled00.vsarduino.h +++ b/wled00/__vm/.wled00.vsarduino.h @@ -17,8 +17,10 @@ #define __ESP32_ESP32__ #define ESP_PLATFORM #define HAVE_CONFIG_H +#define GCC_NOT_5_2_0 0 +#define WITH_POSIX #define F_CPU 240000000L -#define ARDUINO 10809 +#define ARDUINO 108011 #define ARDUINO_ESP32_DEV #define ARDUINO_ARCH_ESP32 #define ESP32 @@ -92,18 +94,11 @@ typedef long pthread_cond_t; #include "arduino.h" #include -//#include "..\generic\Common.h" -//#include "..\generic\pins_arduino.h" - -//#undef F -//#define F(string_literal) ((const PROGMEM char *)(string_literal)) -//#undef PSTR -//#define PSTR(string_literal) ((const PROGMEM char *)(string_literal)) -//current vc++ does not understand this syntax so use older arduino example for intellisense -//todo:move to the new clang/gcc project types. #define interrupts() sei() #define noInterrupts() cli() +#define ESP_LOGI(tag, ...) + #include "wled00.ino" #include "wled01_eeprom.ino" #include "wled02_xml.ino" diff --git a/wled00/__vm/Compile.vmps.xml b/wled00/__vm/Compile.vmps.xml index a7819d20..6390398e 100644 --- a/wled00/__vm/Compile.vmps.xml +++ b/wled00/__vm/Compile.vmps.xml @@ -1,8 +1,8 @@ - - + + - + diff --git a/wled00/wled12_alexa.ino b/wled00/alexa.cpp similarity index 53% rename from wled00/wled12_alexa.ino rename to wled00/alexa.cpp index c3bc01ab..c9f61ff2 100644 --- a/wled00/wled12_alexa.ino +++ b/wled00/alexa.cpp @@ -1,10 +1,13 @@ +#include "wled.h" + /* - * Alexa Voice On/Off/Brightness Control. Emulates a Philips Hue bridge to Alexa. + * Alexa Voice On/Off/Brightness/Color Control. Emulates a Philips Hue bridge to Alexa. * * This was put together from these two excellent projects: * https://github.com/kakopappa/arduino-esp8266-alexa-wemo-switch * https://github.com/probonopd/ESP8266HueEmulator */ +#include "src/dependencies/espalexa/EspalexaDevice.h" #ifndef WLED_DISABLE_ALEXA void onAlexaChange(EspalexaDevice* dev); @@ -41,7 +44,7 @@ void onAlexaChange(EspalexaDevice* dev) if (bri == 0) { bri = briLast; - colorUpdated(10); + colorUpdated(NOTIFIER_CALL_MODE_ALEXA); } } else applyMacro(macroAlexaOn); } else if (m == EspalexaDeviceProperty::off) @@ -52,22 +55,38 @@ void onAlexaChange(EspalexaDevice* dev) { briLast = bri; bri = 0; - colorUpdated(10); + colorUpdated(NOTIFIER_CALL_MODE_ALEXA); } } else applyMacro(macroAlexaOff); } else if (m == EspalexaDeviceProperty::bri) { bri = espalexaDevice->getValue(); - colorUpdated(10); + colorUpdated(NOTIFIER_CALL_MODE_ALEXA); } else //color { - uint32_t color = espalexaDevice->getRGB(); - col[3] = ((color >> 24) & 0xFF); // white color from Alexa is "pure white only" - col[0] = ((color >> 16) & 0xFF); - col[1] = ((color >> 8) & 0xFF); - col[2] = (color & 0xFF); - if (useRGBW && col[3] == 0) colorRGBtoRGBW(col); // do not touch white value if EspalexaDevice.cpp did set the white channel - colorUpdated(10); + if (espalexaDevice->getColorMode() == EspalexaColorMode::ct) //shade of white + { + uint16_t ct = espalexaDevice->getCt(); + if (useRGBW) + { + switch (ct) { //these values empirically look good on RGBW + case 199: col[0]=255; col[1]=255; col[2]=255; col[3]=255; break; + case 234: col[0]=127; col[1]=127; col[2]=127; col[3]=255; break; + case 284: col[0]= 0; col[1]= 0; col[2]= 0; col[3]=255; break; + case 350: col[0]=130; col[1]= 90; col[2]= 0; col[3]=255; break; + case 383: col[0]=255; col[1]=153; col[2]= 0; col[3]=255; break; + } + } else { + colorCTtoRGB(ct, col); + } + } else { + uint32_t color = espalexaDevice->getRGB(); + + col[0] = ((color >> 16) & 0xFF); + col[1] = ((color >> 8) & 0xFF); + col[2] = ( color & 0xFF); + } + colorUpdated(NOTIFIER_CALL_MODE_ALEXA); } } diff --git a/wled00/wled16_blynk.ino b/wled00/blynk.cpp similarity index 78% rename from wled00/wled16_blynk.ino rename to wled00/blynk.cpp index c09d0c18..39b43ba8 100644 --- a/wled00/wled16_blynk.ino +++ b/wled00/blynk.cpp @@ -1,3 +1,6 @@ +#include "wled.h" +#include "src/dependencies/blynk/Blynk/BlynkHandlers.h" + /* * Remote light control with the free Blynk app */ @@ -41,45 +44,45 @@ void updateBlynk() BLYNK_WRITE(V0) { bri = param.asInt();//bri - colorUpdated(9); + colorUpdated(NOTIFIER_CALL_MODE_BLYNK); } BLYNK_WRITE(V1) { blHue = param.asInt();//hue colorHStoRGB(blHue*10,blSat,(false)? colSec:col); - colorUpdated(9); + colorUpdated(NOTIFIER_CALL_MODE_BLYNK); } BLYNK_WRITE(V2) { blSat = param.asInt();//sat colorHStoRGB(blHue*10,blSat,(false)? colSec:col); - colorUpdated(9); + colorUpdated(NOTIFIER_CALL_MODE_BLYNK); } BLYNK_WRITE(V3) { bool on = (param.asInt()>0); - if (!on != !bri) {toggleOnOff(); colorUpdated(9);} + if (!on != !bri) {toggleOnOff(); colorUpdated(NOTIFIER_CALL_MODE_BLYNK);} } BLYNK_WRITE(V4) { effectCurrent = param.asInt()-1;//fx - colorUpdated(9); + colorUpdated(NOTIFIER_CALL_MODE_BLYNK); } BLYNK_WRITE(V5) { effectSpeed = param.asInt();//sx - colorUpdated(9); + colorUpdated(NOTIFIER_CALL_MODE_BLYNK); } BLYNK_WRITE(V6) { effectIntensity = param.asInt();//ix - colorUpdated(9); + colorUpdated(NOTIFIER_CALL_MODE_BLYNK); } BLYNK_WRITE(V7) diff --git a/wled00/wled09_button.ino b/wled00/button.cpp similarity index 96% rename from wled00/wled09_button.ino rename to wled00/button.cpp index 86227dc3..e71cce96 100644 --- a/wled00/wled09_button.ino +++ b/wled00/button.cpp @@ -1,3 +1,5 @@ +#include "wled.h" + /* * Physical IO */ @@ -7,7 +9,7 @@ void shortPressAction() if (!macroButton) { toggleOnOff(); - colorUpdated(2); + colorUpdated(NOTIFIER_CALL_MODE_BUTTON); } else { applyMacro(macroButton); } @@ -44,7 +46,7 @@ void handleButton() if (dur > 6000) //long press { - initAP(true); + WLED::instance().initAP(true); } else if (!buttonLongPressed) { //short press if (macroDoublePress) diff --git a/wled00/wled14_colors.ino b/wled00/colors.cpp similarity index 93% rename from wled00/wled14_colors.ino rename to wled00/colors.cpp index 8b041462..f76499a0 100644 --- a/wled00/wled14_colors.ino +++ b/wled00/colors.cpp @@ -1,3 +1,5 @@ +#include "wled.h" + /* * Color conversion methods */ @@ -18,7 +20,7 @@ void colorFromUint32(uint32_t in, bool secondary) } //load a color without affecting the white channel -void colorFromUint24(uint32_t in, bool secondary = false) +void colorFromUint24(uint32_t in, bool secondary) { if (secondary) { colSec[0] = in >> 16 & 0xFF; @@ -32,7 +34,7 @@ void colorFromUint24(uint32_t in, bool secondary = false) } //relatively change white brightness, minumum A=5 -void relativeChangeWhite(int8_t amount, byte lowerBoundary =0) +void relativeChangeWhite(int8_t amount, byte lowerBoundary) { int16_t new_val = (int16_t) col[3] + amount; if (new_val > 0xFF) new_val = 0xFF; @@ -57,10 +59,9 @@ void colorHStoRGB(uint16_t hue, byte sat, byte* rgb) //hue, sat to rgb case 4: rgb[0]=t,rgb[1]=p,rgb[2]=255;break; case 5: rgb[0]=255,rgb[1]=p,rgb[2]=q; } - if (useRGBW) colorRGBtoRGBW(col); + if (useRGBW && strip.rgbwMode == RGBW_MODE_LEGACY) colorRGBtoRGBW(col); } -#ifndef WLED_DISABLE_HUESYNC void colorCTtoRGB(uint16_t mired, byte* rgb) //white spectrum to rgb { //this is only an approximation using WS2812B with gamma correction enabled @@ -81,9 +82,10 @@ void colorCTtoRGB(uint16_t mired, byte* rgb) //white spectrum to rgb } else { rgb[0]=237;rgb[1]=255;rgb[2]=239;//150 } - if (useRGBW) colorRGBtoRGBW(col); + if (useRGBW && strip.rgbwMode == RGBW_MODE_LEGACY) colorRGBtoRGBW(col); } +#ifndef WLED_DISABLE_HUESYNC void colorXYtoRGB(float x, float y, byte* rgb) //coordinates to rgb (https://www.developers.meethue.com/documentation/color-conversions-rgb-xy) { float z = 1.0f - x - y; @@ -149,7 +151,8 @@ void colorRGBtoXY(byte* rgb, float* xy) //rgb to coordinates (https://www.develo xy[0] = X / (X + Y + Z); xy[1] = Y / (X + Y + Z); } -#endif +#endif // WLED_DISABLE_HUESYNC + void colorFromDecOrHexString(byte* rgb, char* in) { @@ -183,7 +186,7 @@ float maxf (float v, float w) return v; } -void colorRGBtoRGBW(byte* rgb) //rgb to rgbw (http://codewelt.com/rgbw) +void colorRGBtoRGBW(byte* rgb) //rgb to rgbw (http://codewelt.com/rgbw). (RGBW_MODE_LEGACY) { float low = minf(rgb[0],minf(rgb[1],rgb[2])); float high = maxf(rgb[0],maxf(rgb[1],rgb[2])); diff --git a/wled00/const.h b/wled00/const.h new file mode 100644 index 00000000..8a4a59dc --- /dev/null +++ b/wled00/const.h @@ -0,0 +1,109 @@ +#ifndef WLED_CONST_H +#define WLED_CONST_H + +/* + * Readability defines and their associated numerical values + compile-time constants + */ + +//Defaults +#define DEFAULT_CLIENT_SSID "Your_Network" +#define DEFAULT_AP_PASS "wled1234" +#define DEFAULT_OTA_PASS "wledota" + +//Access point behavior +#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot +#define AP_BEHAVIOR_NO_CONN 1 //Open when no connection (either after boot or if connection is lost) +#define AP_BEHAVIOR_ALWAYS 2 //Always open +#define AP_BEHAVIOR_BUTTON_ONLY 3 //Only when button pressed for 6 sec + +//Notifier callMode +#define NOTIFIER_CALL_MODE_INIT 0 //no updates on init, can be used to disable updates +#define NOTIFIER_CALL_MODE_DIRECT_CHANGE 1 +#define NOTIFIER_CALL_MODE_BUTTON 2 +#define NOTIFIER_CALL_MODE_NOTIFICATION 3 +#define NOTIFIER_CALL_MODE_NIGHTLIGHT 4 +#define NOTIFIER_CALL_MODE_NO_NOTIFY 5 +#define NOTIFIER_CALL_MODE_FX_CHANGED 6 //no longer used +#define NOTIFIER_CALL_MODE_HUE 7 +#define NOTIFIER_CALL_MODE_PRESET_CYCLE 8 +#define NOTIFIER_CALL_MODE_BLYNK 9 +#define NOTIFIER_CALL_MODE_ALEXA 10 + +//RGB to RGBW conversion mode +#define RGBW_MODE_MANUAL_ONLY 0 //No automatic white channel calculation. Manual white channel slider +#define RGBW_MODE_AUTO_BRIGHTER 1 //New algorithm. Adds as much white as the darkest RGBW channel +#define RGBW_MODE_AUTO_ACCURATE 2 //New algorithm. Adds as much white as the darkest RGBW channel and subtracts this amount from each RGB channel +#define RGBW_MODE_DUAL 3 //Manual slider + auto calculation. Automatically calculates only if manual slider is set to off (0) +#define RGBW_MODE_LEGACY 4 //Old floating algorithm. Too slow for realtime and palette support + +//realtime modes +#define REALTIME_MODE_INACTIVE 0 +#define REALTIME_MODE_GENERIC 1 +#define REALTIME_MODE_UDP 2 +#define REALTIME_MODE_HYPERION 3 +#define REALTIME_MODE_E131 4 +#define REALTIME_MODE_ADALIGHT 5 +#define REALTIME_MODE_ARTNET 6 + +//realtime override modes +#define REALTIME_OVERRIDE_NONE 0 +#define REALTIME_OVERRIDE_ONCE 1 +#define REALTIME_OVERRIDE_ALWAYS 2 + +//E1.31 DMX modes +#define DMX_MODE_DISABLED 0 //not used +#define DMX_MODE_SINGLE_RGB 1 //all LEDs same RGB color (3 channels) +#define DMX_MODE_SINGLE_DRGB 2 //all LEDs same RGB color and master dimmer (4 channels) +#define DMX_MODE_EFFECT 3 //trigger standalone effects of WLED (11 channels) +#define DMX_MODE_MULTIPLE_RGB 4 //every LED is addressed with its own RGB (ledCount * 3 channels) +#define DMX_MODE_MULTIPLE_DRGB 5 //every LED is addressed with its own RGB and share a master dimmer (ledCount * 3 + 1 channels) + +//Light capability byte (unused) +#define TYPE_NONE 0 //light is not configured +#define TYPE_RESERVED 1 //unused. Might indicate a "virtual" light +#define TYPE_WS2812_RGB 2 +#define TYPE_SK6812_RGBW 3 +#define TYPE_WS2812_WWA 4 //amber + warm + cold white +#define TYPE_WS2801 5 +#define TYPE_ANALOG_1CH 6 //single channel PWM. Uses value of brightest RGBW channel +#define TYPE_ANALOG_2CH 7 //analog WW + CW +#define TYPE_ANALOG_3CH 8 //analog RGB +#define TYPE_ANALOG_4CH 9 //analog RGBW +#define TYPE_ANALOG_5CH 10 //analog RGB + WW + CW +#define TYPE_APA102 11 +#define TYPE_LPD8806 12 + +//Hue error codes +#define HUE_ERROR_INACTIVE 0 +#define HUE_ERROR_UNAUTHORIZED 1 +#define HUE_ERROR_LIGHTID 3 +#define HUE_ERROR_PUSHLINK 101 +#define HUE_ERROR_JSON_PARSING 250 +#define HUE_ERROR_TIMEOUT 251 +#define HUE_ERROR_ACTIVE 255 + +//Segment option byte bits +#define SEG_OPTION_SELECTED 0 +#define SEG_OPTION_REVERSED 1 +#define SEG_OPTION_ON 2 +#define SEG_OPTION_PAUSED 3 //unused +#define SEG_OPTION_NONUNITY 4 //Indicates that the effect does not use FRAMETIME or needs getPixelColor +#define SEG_OPTION_TRANSITIONAL 7 + +//EEPROM size +#define EEPSIZE 2560 //Maximum is 4096 + +#define NTP_PACKET_SIZE 48 + +// maximum number of LEDs - MAX_LEDS is coming from the JSON response getting too big, MAX_LEDS_DMA will become a timing issue +#define MAX_LEDS 1500 +#define MAX_LEDS_DMA 500 + +// string temp buffer (now stored in stack locally) +#define OMAX 2048 + +#define E131_MAX_UNIVERSE_COUNT 9 + +#define ABL_MILLIAMPS_DEFAULT 850; // auto lower brightness to stay close to milliampere limit + +#endif diff --git a/wled00/data/index.htm b/wled00/data/index.htm index e7ea5e75..9bf3de6b 100644 --- a/wled00/data/index.htm +++ b/wled00/data/index.htm @@ -14,16 +14,32 @@ src: url(data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAABsAAAsAAAAAGrQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABPUy8yAAABCAAAAGAAAABgD50AIWNtYXAAAAFoAAABRAAAAURfBgSRZ2FzcAAAAqwAAAAIAAAACAAAABBnbHlmAAACtAAAFWQAABVkn5Xq/GhlYWQAABgYAAAANgAAADYXA6M1aGhlYQAAGFAAAAAkAAAAJAcYA19obXR4AAAYdAAAAKgAAAConAAT3mxvY2EAABkcAAAAVgAAAFZwmGsgbWF4cAAAGXQAAAAgAAAAIAA0AF1uYW1lAAAZlAAAAUoAAAFKQULQ4XBvc3QAABrgAAAAIAAAACAAAwAAAAMEAAGQAAUAAAKZAswAAACPApkCzAAAAesAMwEJAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAQAAA5BADM/80AMwDMwDMAAAAAQAAAAAAAAAAAAAAIAAAAAAAAwAAAAMAAAAcAAEAAwAAABwAAwABAAAAHAAEASgAAABGAEAABQAGAAEAIOA34DngPOBM4I/gouCp4Lvg1ODp4RbhOeE/4XzhiuIt4j3iouKm4rPixuLk4yXjM+NM45DjlePW4/HkCeQQ//3//wAAAAAAIOA34DngPOBM4I/gouCp4Lvg1ODo4RbhN+E/4XzhiuIt4j3iouKm4rPixuLj4yXjM+NL44/jlOPW4/HkCeQQ//3//wAB/+MfzR/MH8ofux95H2cfYR9QHzgfJR75Htke1B6YHosd6R3aHXYdcx1nHVUdORz5HOwc1RyTHJAcUBw2HB8cGQADAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAf//AA8AAQAAAAAAAAAAAAIAADc5AQAAAAABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAgDV/8ADKwLAAAkAEgAAJREhERQGIyEiJgEVITUzNzMXMwEAAgAyI/6qIzICK/2qlirWKpYVAgD+ACMyMgKjVVUrKwADANX/wAMrAsAACQAOABYAACURIREUBiMhIiYTESERISUzFSE1MzczAQACADIj/qojMlUBVv6qAUCW/aqWKtYVAgD+ACMyMgHO/lUBq9VVVSsAAAABAJEAFQOAAlEABQAAJQEXASc3AYABxDz+AO88jQHEPP4A7zwAAAAAAgBV/7EDqwLAACQAQQAAATIXHgEXFhUUBw4BBwYPAScmJy4BJyY1NDc+ATc2MzIWFz4BMwM2Nz4BNzY1NCYjIgYHIy4BIyIGFRQXHgEXFh8BAsAxKys/EhMaG19DRFI+PlJEQ18bGhMSPysrMThlIyNlOLxMPz5YGBhVQDFWEVARVjFAVRgYWD4/TAQCwBISQCsqMjw5OHU/QEs4OEs/QHU4OTwyKitAEhIwKSkw/WlEOzpnLy8uQFY5LCw5VkAuLy9nOjtEBQACAID/wAOAAsAABAA2AAABESMRMxcWFx4BFxYVFAcOAQcGIyInLgEnJjU0Nz4BNzY3Fw4BFRQXHgEXFjMyNz4BNzY1NCYnAitWVs4fGRkjCgkeHmlGRVBQRUZpHh4JCiMZGR88MjwYF1E3Nj4+NjdRFxg8MwLA/lUBq10aICFKKSksUEVGaR4eHh5pRkVQLCkpSiEgGjwpeEY+NjdRFxgYF1E3Nj5GeCkAAAAAAgB0/6YDjALaAE4AWgAAARceAQ8BDgEvAQ4BDwEOASsBIiYvAS4BJwcGJi8BJjY/AS4BNTQ2NycuAT8BPgEfAT4BPwE+ATsBMhYfAR4BFzc2Fh8BFgYPAR4BFRQGBwUyNjU0JiMiBhUUFgMxVQYDBFIDDwdmDyMTDwELCKQICwEQEyIQZgcOBFIDAwVXAgECAVYGAwRSAw8HZg8jEw8BCwikCAsBEBMiEGYHDgRSAwMFVwIBAQH+zz9bWz8/W1sBGEQEDweNBwUCKQwUCGwICgoIbAgUDCkCBQeNBw8ERAoUCgoUCkQEDweNBwUCKQwUCGwICgoIbAgUDCkCBQeNBw8ERAoUCgoUCnJbPz9bWz8/WwAAAwAr/4QD1QMVAB4AMwBSAAABMhceARcWFSM0Jy4BJyYjIgcOAQcGFSM0Nz4BNzYzExUXBycHJzc1LgE1NDYzMhYVFAYHAzIXHgEXFhUjNCcuAScmIyIHDgEHBhUjNDc+ATc2MwIAPjY3URcYVhAROicnLCwnJzoREFYYF1E3Nj4rkTyAgDyRHCQ/LCw/JBwrYVZVgCQlVR4eaUZFUFBFRmkeHlUlJIBVVmECaxgXUTc2PiwnJzoREBAROicnLD42N1EXGP5zjZE8gIA8kY0NNCEsPz8sITQNAjclJIBVVmFQRUZpHh4eHmlGRVBhVlWAJCUAAAIAVf+VA6sC6wAcACYAAAEyFx4BFxYVFAcOAQcGIyInLgEnJjU0Nz4BNzYzEyc3LwEPARcHNwIAWE5OdCEiIiF0Tk5YWU1OdCEiIiF0Tk1ZtC+f0lJS0p8vtALrIiF0Tk5YWE5OdCEiIiF0Tk5YWE5OdCEi/VXOiRLCwhKKzW0AAwAr/5UD1QLrACAAKQAsAAAlBycHJzcuASczHgEXPgE3ITUhNTMVIRUjBgcOAQcGDwElEyMnIwcjEzMDMycCJSCF1T3ZKEAXVRQxHi5EFf4kASpWASp9CxAQJxgYHAEBXMBVMMswVcBVb4pFvVeF1j3WLWI0JkghM3Q9VVZWVSYkJUYhIh8Bbf4AgIACAP7WuAAAAAMAKwAAA9UCgAAbADcAQwAAATIXHgEXFhcGBw4BBwYjIicuAScmJzY3PgE3NhMyNz4BNzY1NCcuAScmIyIHDgEHBhUUFx4BFxYTMhYVFAYjIiY1NDYCAFBJSXovLxsbLy96SUlQUElJei8vGxsvL3pJSVAsJyc6ERAQETonJywsJyc6ERAQETonJyw1S0s1NUtLAoAYF1U7O0ZGOztVFxgYF1U7O0ZGOztVFxj96xAROicnLCwnJzoREBAROicnLCwnJzoREAFVSzU1S0s1NUsAAAAABAAr/5UD1QLAABsALgBGAEwAAAEiBgcnPgEzMhceARcWFw4BByc+ATU0Jy4BJyYlNwEHJw4BIyInLgEnJic+ATcnFw4BFRQXHgEXFjMyNjcnDgEjIiY1NDY3PwEyFh0BAgAVJxJcJ1YtUElIey4vHBdLMH0HCBAROicn/ik3AvQ2jyteMlBJSXovLxsZUTUTigoMEBE6JycsGS8WQgcOBzVLAgF2BzZKAhUIB1wPDxgXVTs7RjxmKX0SJxUsJyc6ERB1Nv0MN48RExgXVTs7Rj9tKRSLFi8ZLCcnOhEQDApCAQJLNQcOB2MBSzUHAAAAAgCr/2sDVQMVABkAMgAAATIXHgEXFhUUBgcnPgE1NCcuAScmIxUnNxURNRcHNSInLgEnJjU0NjcXDgEVFBceARcWAgBHPj5dGxocGT4PDxQURi4vNaurq6tHPj5dGxocGT4PDxQURi4vApUaG10+PkcyXCg/Gj0gNS8uRhQUgKuqgP2rgKuqgBobXT4+RzJcKD8aPSA1Ly5GFBQAAgEAAEADAAJAAAIABgAAJREBEzMRIwEAAWtAVVVAAgD/AAEA/gAAAAIBAABAAwACQAAEAAcAAAEzESMREwERAQBVVZUBawJA/gACAP8AAQD+AAAACABX/5cDqwLpAAMABwALABQAHAAlAC4ATQAAARcFER8BBREXJxElAw4BByc+ATcVBw4BByM+ATcDHgEXBy4BJzMTNx4BFxUuAScBFAcOAQcGBzU2Nz4BNzY1NCcuAScmJzUWFx4BFxYVAi1+/wCCfv8AgoIBANYuVSM9MHNA4hwkBVcHMScIBSQcPScxB1dEPSNVLkBzMAK5Hh1nRkZQPzY2UBcWFhdQNjY/UEZGZx0eAZ5ewAGAYl7AAYBiYv6AwAFSBSQcPScxB1eBI1UuQHMw/scuVCQ9MHNA/uE9HCQFVwcxJwFKU0lKcSUkCVcIHh1bOTpBQTo5Wx0eCFcJJCVxSklTAAAABQBV/+sDqwKVABAAHgAqADgARQAAATIWFREUBiMhIiY1ETQ2MyEBLgE1NDY3Jw4BFRQWFzcyNjU0JiMiBhUUFgU+ATU0JicHHgEVFAYHAzIWFRQGIyImNTQ2MwNVJDIyJP1WJDIyJAKq/fYlJiYlPDIyMjLxR2RkR0dkZAE4MjIyMjwlJiYltSMyMiMjMjIjApUyI/4AIzIyIwIAIzL99iZeMTFfJTwxfkJCfTJGZEdHZGRHR2RGMX5CQn0yPCZeMTFfJQEKMiMjMjIjIzIAAAMAq//rA0MCgwAMAB0ALgAANzQ2MzIWFRQGIyImNREyFx4BFxYVIzQnLgEnJiM1FTIXHgEXFhUjNCcuAScmIzWrNicmNzcmJzaJeXm0NDV5KyuTY2NwV01NcyEheRcYUjc3PkgmNzcmJzY2JwI7NTS0eXmJcGNjkysrefIhIXNNTVc+NzdSGBd5AAAAAQDVABUDKwJrAAsAAAEhESMRITUhETMRIQMr/wBW/wABAFYBAAEV/wABAFYBAP8AAAAAAAYAVf/rA4AClQALABEAHAAhACYAKwAANzUzFSM1MzUjNTM1AzUjNTMVBzUzFQczFSM1NyMTIRUhNRE1IRUhETUhFSFVgIBWKysrK1ZWgExMgE1N1gJV/asCVf2rAlX9q2sqqioWKhYBgIAqqoAqJloqJloBAFZW/apWVgEAVlYABQBV/5UDqwLrABwAOABEAFAAWAAAATIXHgEXFhUUBw4BBwYjIicuAScmNTQ3PgE3NjMRMjc+ATc2NTQnLgEnJiMiBw4BBwYVFBceARcWEyImNTQ2MzIWFRQGISImNTQ2MzIWFRQGEyImJyEOASMCAFhOTnQhIiIhdE5OWFlNTnQhIiIhdE5NWUc+Pl0bGhobXT4+R0c+Pl0bGhobXT4+3BomJhobJSX+uxslJRsaJiZ7S3UaAbQadUsC6yIhdE5OWFhOTnQhIiIhdE5OWFhOTnQhIv0AGhtdPj5HRz4+XRsaGhtdPj5HRz4+XRsaAYAlGxomJhobJSUbGiYmGhsl/upUQkJUAAAAAQEA/5UDKwLrACIAAAEyFx4BFxYVFAcOAQcGIyImJzY3PgE3NjU0Jy4BJyYnPgEzAYBYTk50ISIiIXROTlgiQB5BNzdPFhcXFk83N0EeQCIC6yIhdE5OWFhOTnQhIgoKFCgnakFBSEhBQWonKBQKCgAAAAADAB3/XQPjAyMADwArADgAAAEXBxUjBycjNSc3NTM3FzMBMjc+ATc2NTQnLgEnJiMiBw4BBwYVFBceARcWEzIWFRQGIyImNTQ2MwNVjo7IjY3Ijo7IjY3I/qs1Ly5GFBQUFEYuLzU1Ly5GFBQUFEYuLzVHZGRHR2RkRwHNjY3Ijo7IjY3Ijo79qxQURi4vNTUvLkYUFBQURi4vNTUvLkYUFAGrZEdHZGRHR2QABQCA/8ADgALAACgANABAAEwAWAAAATIXHgEXFhUUBw4BBwYrASIGFRQWFx4BFRQGIyInLgEnJjU0Nz4BNzYDMjY1NCYjIgYVFBY3MjY1NCYjIgYVFBYzMjY1NCYjIgYVFBYXMjY1NCYjIgYVFBYCAFBFRmkeHhEROScnLEwaJgkHCAklG1BFRmkeHh4eaUZFmxslJRsaJiaaGyUlGxomJvAaJiYaGyUlmxomJhobJSUCwBsbXD4/RiwnJzoRESUbDBYICRYMGyUeHmlGRVBQRUZpHh7+gCUbGyUlGxslqyUbGiYmGhslJRsaJiYaGyWrJRsbJSUbGyUAAAAAAgCA/8ADdAK0AAUADwAANwEXASM1AQcnNzYyHwEWFIAB2KD+KKAC9E6gTgwjDWQMYAHYoP4ooAG0TqBODAxkDSMAAAABASv/lQLVAusABwAAASEDMwERIxEBKwGqqqr+1oAC6/6q/gABgAHWAAAAAAkAgP9rA4ADFQADAAcAFgAbAB8AIwAnACsAMAAABTUzFRM1MxUlNDY7ARUjETMVIyImNRElMhYVIwERMxEBNTMVATUzFRM1MxUDNTMUBgKAVVZV/QAyI6urq6sjMgKrIzJV/qpWAQBV/wBVVlVVVTJAVVUCAFVVqyMyVf2qVTIjAlZVMiP9AAOq/FYBAFVVAgBVVf6qVlb+q1UjMgAAAAAEAID/lQOAAxUAAwAHACcARAAAARUhNRMRMxEBHgEVFAcOAQcGIyInLgEnJjU0Nz4BNzYzMhYXNx4BFwEyNz4BNzY1NCcuAScmIyIHDgEHBhUUFx4BFxYzAoD/AFVWAQEnLR4eaEZGUFBGRmgeHh4eaUZFUER6MjwRHg7+lz42N1EXGBgXUTc2Pj42N1EXGBgXUTc2PgMVVVX91gEA/wABGjJ6RE9GRmgeHx8eaEZGT1BGRmgeHiwoPA0eEf2qFxhRNjY+Pjc2URgXFxhRNjc+PjY2URgXAAAJACv/ggPVAykAAwAHAAsADwATABcAMwA3ADsAAAEHJzcDFSM1ARUjNQUHJzcDNxcHEzMVIwEyFx4BFxYVFAcOAQcGIyInLgEnJjU0Nz4BNzYTNTMVJTcXBwEgPE09KYACAFYBlE08TEw7TTwpgID+qzUvLkYUFBQURi4vNTUvLkYUFBQURi4vClb+bE08TAJxPE08/sJVVQGpfn6nTTxN/Xs8TD0BlFUBKhQURS8vNTUuL0UVFBQVRS8uNTUvL0UUFP0tfn6nTTxNAAACAID/vQOAAusABQAKAAAtARcJATcFCQIHAgABOkb+gP6ARQE7/oABgAGARin1Nv7VASs1iAErASv+1TYAAAAAAwBV/70DqwMVAAMACAAWAAAlJzcXJwcBNwEJAQcnBwE3BTcnBwE3JwNNPTM9Rmb+sHwBgP0MAx83odP+gEYBOpc9Wv6AibTAPSg9olABUGH+1QFV/OE2oaQBKzX0dT1GAStrtAAAAAACAFX/lQOrAusAHAAoAAABMhceARcWFRQHDgEHBiMiJy4BJyY1NDc+ATc2MxMnNycHJwcXBxc3FwIAWE5OdCEiIiF0Tk5YWE5OdCEiIiF0Tk5Y1ZmZPJmZPJmZPJmZAusiIXROTlhYTk50ISIiIXROTlhYTk50ISL9vJmZPJmZPJmZPJmZAAAAAQCRABUDgAJRAAUAACUBFwEnNwGAAcQ8/gDvPY4Bwzz+AO88AAAAAAEBAACvAwAB6wAFAAAJAQcnBycCAAEAPMTEPAHr/wA8w8M8AAEBAACVAwAB0QAFAAABFwkBNxcCxDz/AP8APMQB0Tz/AAEAPMMAAAABAFX/lQOrAusALAAAARUjFwcnIxUXBycVIzUHJzc1IwcnNyM1Myc3FzM1JzcXNTMVNxcHFTM3FwczA6uyijzHVcY8ilaKPMZVxzyKsrKKPMdVxjyKVoo8xlXHPIqyAWtWijzGVcc8irKyijzHVcY8ilaKPMZVxzyKsrKKPMdVxjyKAAAFAFX/lQOrAusAHAA4AEQAUABXAAABMhceARcWFRQHDgEHBiMiJy4BJyY1NDc+ATc2MxEyNz4BNzY1NCcuAScmIyIHDgEHBhUUFx4BFxYTIiY1NDYzMhYVFAYhIiY1NDYzMhYVFAYXMhYXIT4BAgBYTk50ISIiIXROTlhZTU50ISIiIXROTVlHPj5dGxoaG10+PkdHPj5dGxoaG10+PtwaJiYaGyUl/rsbJSUbGiYme0t1Gv5MGnUC6yIhdE5OWFhOTnQhIiIhdE5OWFhOTnQhIv0AGhtdPj5HRz4+XRsaGhtdPj5HRz4+XRsaAYAlGxomJhobJSUbGiYmGhslgFRCQlQAAAIAq/+VA1UDIwAmADkAAAEWFx4BFxYVFAcOAQcGIyInLgEnJjU0Nz4BNzY3BxQWMzI2NTQmMQMyNz4BNzY1NCYnDgEHDgEVFBYCQD8zM0kTFBobXT4+R0c+Pl0bGgkJJBoZIAFZQkJQIEwqJSY3EBAMDSBsOThAUQMjMj4/kVFRVkc+Pl0bGxsbXT4+RzYzNGAsLCYPQl5eQkSI/PIQETclJiotViosNwwLRjQ3TwAAAgBV/8ADqwLrAAkAEwAAAQcTJQUTJyUbAQMXJzcvAQ8BFwcDq+lG/vj++EbpATN4eHihK467SUm6jSoBtsr+1J+fASzKGgEb/uX+32G2exCtrBB7twAAAAEAAAABAABJZ54xXw889QALBAAAAAAA2gCv2QAAAADaAK/ZAAD/XQPjAykAAAAIAAIAAAAAAAAAAQAAAzP/NAAABAAAAAAAA+MAAQAAAAAAAAAAAAAAAAAAACoEAAAAAAAAAAAAAAAAAAAABAAA1QQAANUEAACRBAAAVQQAAIAEAAB0BAAAKwQAAFUEAAArBAAAKwQAACsEAACrBAABAAQAAQAEAABXBAAAVQQAAKsEAADVBAAAVQQAAFUEAAEABAAAHQQAAIAEAACABAABKwQAAIAEAACABAAAKwQAAIAEAABVBAAAVQQAAJEEAAEABAABAAQAAFUEAABVBAAAqwQAAFUAAAAAAAoAFAAeAEAAagB+AOIBOAHCAjwCfALGAzADqAP2BAoEIASkBQwFUgVsBawGMAZqBsAHPgdgB3YHxggyCJgIuAjsCTAJRAlWCWoJrAouCoYKsgAAAAEAAAAqAFsACQAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAOAK4AAQAAAAAAAQACAAAAAQAAAAAAAgAHADMAAQAAAAAAAwACACcAAQAAAAAABAACAEgAAQAAAAAABQALAAYAAQAAAAAABgACAC0AAQAAAAAACgAaAE4AAwABBAkAAQAEAAIAAwABBAkAAgAOADoAAwABBAkAAwAEACkAAwABBAkABAAEAEoAAwABBAkABQAWABEAAwABBAkABgAEAC8AAwABBAkACgA0AGh1aQB1AGlWZXJzaW9uIDEuMABWAGUAcgBzAGkAbwBuACAAMQAuADB1aQB1AGl1aQB1AGlSZWd1bGFyAFIAZQBnAHUAbABhAHJ1aQB1AGlGb250IGdlbmVyYXRlZCBieSBJY29Nb29uLgBGAG8AbgB0ACAAZwBlAG4AZQByAGEAdABlAGQAIABiAHkAIABJAGMAbwBNAG8AbwBuAC4AAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) format('woff'); } +:root { + --c-bg: #111; + --c-tx: #fff; + --c-2 : #222; + --c-3 : #333; + --c-4 : #444; + --c-5 : #555; + --c-6 : #666; + --c-8 : #888; + --c-b : #bbb; + --c-c : #ccc; + --c-e : #eee; + --c-d : #ddd; + --c-red:#831; +} + html { touch-action: manipulation; } body { margin: 0; - background-color: #111; + background-color: var(--c-bg); font-family: Helvetica, Verdana, sans-serif; font-size: 17px; - color: white; + color: var(--c-tx); text-align: center; -webkit-touch-callout: none; -webkit-user-select: none; @@ -43,7 +59,7 @@ body { p { margin: 10px 0 2px 0; - color: #ddd; + color: var(--c-d); } button { @@ -60,7 +76,7 @@ button { position: fixed; bottom: 72px; right: 4px; - color: #666; + color: var(--c-6); writing-mode: vertical-rl; } @@ -73,7 +89,7 @@ button { top: 0; left: 0; right: 0; - background: #222; + background: var(--c-2); z-index: 1; } @@ -86,23 +102,53 @@ button { margin: -2px 0 4px 0; } +.huge { + font-size: 42px; +} + +.infot { + table-layout: fixed; + width: 100%; +} + +.segt { + table-layout: fixed; + width: 76%; +} + +.segtd { + text-align: center; + text-transform: uppercase; + font-size: 14px; +} + +.keytd { + text-align: left; + padding-bottom: 8px; +} + +.valtd { + text-align: right; + padding-bottom: 8px; +} + .slider-icon { - transform: translateY(2px); - color: #ddd; + transform: translateY(3px); + color: var(--c-d); } .sel-icon { margin: -1px 2px 5px 0; vertical-align: middle; - color: #ddd; + color: var(--c-d); } .flr { float: right; cursor: pointer; margin: 0; - color: #fff; + color: var(--c-tx); transform: rotate(0deg); transition: transform 0.3s; } @@ -124,9 +170,9 @@ button { } .tab { - background-color: #222; - color: #ddd; - filter: drop-shadow(0px 0px 2px #000); + background-color: var(--c-2); + color: var(--c-d); + filter: drop-shadow(0px 0px 0px #111); } .bot { @@ -142,7 +188,7 @@ button { border: none; transition: 0.3s; font-size: 17px; - color: #ccc; + color: var(--c-c); } .top button { @@ -155,18 +201,18 @@ button { } .tab button:hover { - background-color: #333; - color: #eee; + background-color: var(--c-3); + color: var(--c-e); } .tab button.active { - background-color: #666; - color: #fff; + background-color: var(--c-6); + color: var(--c-tx); } .active { - background-color: #666 !important; - color: #fff; + background-color: var(--c-6) !important; + color: var(--c-tx); } .container { @@ -194,7 +240,7 @@ button { #Effects { padding-top: 0; - border-top: 11px solid #111; + border-top: 11px solid var(--c-bg); } .smooth { transition: transform calc(var(--f, 1)*.5s) ease-out } @@ -205,29 +251,30 @@ button { } .overlay { - position: fixed; - height: 100%; - width: 100%; - top: 0; - left: 0; - background-color: #333; - font-size: 24px; - display: flex; - align-items: center; + position: fixed; + height: 100%; + width: 100%; + top: 0; + left: 0; + background-color: var(--c-3); + font-size: 24px; + display: flex; + align-items: center; justify-content: center; - z-index: 11; - opacity: 0.95; - transition: 0.7s; - pointer-events: none; + z-index: 11; + opacity: 0.95; + transition: 0.7s; + pointer-events: none; } .staytop { display: block; position: -webkit-sticky; position: sticky; - background: #111; + background: var(--c-bg); top: 0px; z-index: 1; + margin-top: 1px; } #staytop1 { @@ -240,14 +287,14 @@ button { #toast { opacity: 0; - background-color: #555; + background-color: var(--c-5); max-width: 90%; - color: #fff; + color: var(--c-tx); text-align: center; - border-radius: 2px; + border-radius: 5px; padding: 16px; position: fixed; - z-index: 2; + z-index: 5; left: 50%; transform: translateX(-50%); bottom: 100px; @@ -266,6 +313,62 @@ button { animation: fadein 0.5s; } +.modal { + position:fixed; + left: 0px; + bottom: 0px; + right: 0px; + background-color: #222e; + transform: translateY(100%); + transition: transform 0.4s; + padding: 8px; + font-size: 20px; + overflow: auto; +} + +#info { + z-index: 3; +} + +#rover { + z-index: 2; +} + +#roverstar { + position: fixed; + top: 75px; + left: 1px; + display: none; + cursor: pointer; +} + +#imgw { + width: 200px; + height: 55px; + display: inline-block; +} + +#kv { + max-width: 490px; + display: inline-block; +} + +#lv { + max-width: 600px; + display: inline-block; +} + +#heart { + transition: color 0.9s; + font-size: 16px; + color: #f00; +} + +img { + max-width: 100%; + max-height: 100%; +} + @keyframes fadein { from {bottom: 0; opacity: 0;} to {bottom: 100px; opacity: 1;} @@ -276,7 +379,7 @@ button { position: absolute; top: 13px; bottom: 13px; left: 10px; right: 10px; - background: #444; + background: var(--c-4); border-radius: 17px; pointer-events: none; z-index: -1; @@ -303,7 +406,7 @@ input[type=range]::-webkit-slider-thumb { height: 16px; width: 16px; border-radius: 17px; - background: #fff; + background: var(--c-tx); cursor: pointer; -webkit-appearance: none; margin-top: 7px; @@ -318,7 +421,7 @@ input[type=range]::-moz-range-thumb { height: 16px; width: 16px; border-radius: 17px; - background: #fff; + background: var(--c-tx); transform: translateY(7px); } input[type=range]::-ms-track { @@ -338,7 +441,7 @@ input[type=range]::-ms-thumb { height: 16px; width: 16px; border-radius: 16px; - background: #fff; + background: var(--c-tx); margin-top: -12px; } @@ -352,6 +455,14 @@ input[type=range]::-ms-thumb { position: relative; } +.sws { + width: 212px; +} + +.sis { + width: 192px !important; +} + #briwrap { float: right; } @@ -359,7 +470,7 @@ input[type=range]::-ms-thumb { #picker { margin: 10px auto; width: 260px; - filter: drop-shadow(0px 0px 2px #000); + filter: drop-shadow(0px 0px 1px #000); } .btn { @@ -367,11 +478,11 @@ input[type=range]::-ms-thumb { margin: 10px; width: 230px; font-size: 19px; - background-color: #333; - color: white; + background-color: var(--c-3); + color: var(--c-tx); border: 0px solid white; - border-radius: 5px; - filter: drop-shadow(0px 0px 2px #000); + border-radius: 25px; + filter: drop-shadow(0px 0px 1px #000); transition-duration: 0.5s; -webkit-backface-visibility: hidden; -webkit-transform:translate3d(0,0,0); @@ -380,20 +491,39 @@ input[type=range]::-ms-thumb { .btn-i { padding-bottom: 4px; width: 276px; - background-color: #222; + background-color: var(--c-2); } .btn-icon { - margin: 0px 8px 5px 0; + margin: 0px 8px 4px 0; vertical-align: middle; } +.qcs-w { + margin-top: 10px; +} +.qcs { + padding: 14px; + margin: 2px; + border-radius: 14px; + display: inline-block; +} +.qcsb { + padding: 13px; + border: 1px solid var(--c-tx); +} + +.cl { + width: 42px; +} + select { -webkit-appearance: none; -moz-appearance: none; appearance: none; background: url("data:image/svg+xml;utf8,") no-repeat; background-size: 12px; - background-position: 210px 16px; + background-position: 206px 16px; + padding-left: 12px !important; background-repeat: no-repeat; outline: none; } @@ -403,13 +533,13 @@ select:-moz-focusring { text-shadow: 0 0 0 #fff; } option { - background-color: #333; - color: #fff; + background-color: var(--c-3); + color: var(--c-tx); } input[type=number] { - background: #333; - color: white; + background: var(--c-3); + color: var(--c-tx); border: 0px solid white; border-radius: 5px; padding: 8px; @@ -424,7 +554,7 @@ input[type=number] { } input[type=number]:focus { - background: #666; + background: var(--c-6); } input[type=number]::-webkit-inner-spin-button, @@ -432,56 +562,87 @@ input[type=number]::-webkit-outer-spin-button { -webkit-appearance: none; } -.botpad { - margin-bottom: 16px !important; +.segn { + margin: 3px 0 6px 0 !important; +} + +.segname { + position: absolute; + top: 10px; + left: 50%; + transform: translateX(-50%); + white-space: nowrap; + cursor: pointer; +} + +.newseg { + cursor: default; } .ic { padding: 6px 0 0 0; } -.xs { - width: 60px; +.xxs { + width: 40px; + margin: 6px; } -.xxs { - width: 42px; - margin: 8px; +.rect { + border-radius: 5px; } .psts { - background-color: #222; - color: #888; + background-color: var(--c-2); + color: var(--c-8); cursor: default; } .stored { - background-color: #333; - color: white; + background-color: var(--c-3); + color: var(--c-tx); cursor: pointer; } .saving { - background-color: #333; - color: white; + background-color: var(--c-3); + color: var(--c-tx); cursor: pointer; } .stored.saving { - background-color: #831; - color: white; + background-color: var(--c-red); + color: var(--c-tx); } .cnf { position: absolute; - top: 75px; - right: 60px; - color: #fff; + top: 66px; + right: 28px; + color: var(--c-tx); cursor: pointer; + background: var(--c-3); + padding: 43px 6px; + border-radius: 5px; +} + +.pwr { + color: var(--c-6); + transform: translate(2px, 3px); + cursor: pointer; +} + +.act { + color: var(--c-tx); +} + +.half { + padding: 6px 6px; + top: 64px; } .del { position: absolute; bottom: 8px; right: 8px; - color: #fff; + color: var(--c-tx); cursor: pointer; } @@ -495,13 +656,14 @@ input[type=number]::-webkit-outer-spin-button { } .schkl { - padding: 2px 70px 0px 35px; + padding: 2px 22px 0px 35px; margin-bottom: 0px; } .revchkl { padding: 2px 0px 0px 35px; margin-bottom: 0px; + margin-top: 8px; } .check input { @@ -518,7 +680,7 @@ input[type=number]::-webkit-outer-spin-button { left: 0; height: 25px; width: 25px; - background-color: #333; + background-color: var(--c-3); border-radius: 5px; } @@ -541,11 +703,11 @@ input[type=number]::-webkit-outer-spin-button { .check:hover input ~ .checkmark { - background-color: #444; + background-color: var(--c-4); } .check input:checked ~ .checkmark { - background-color: #666; + background-color: var(--c-6); } .checkmark:after { @@ -563,7 +725,7 @@ input[type=number]::-webkit-outer-spin-button { top: 5px; width: 5px; height: 10px; - border: solid white; + border: solid var(--c-tx); border-width: 0 3px 3px 0; -webkit-transform: rotate(45deg); -ms-transform: rotate(45deg); @@ -572,8 +734,12 @@ input[type=number]::-webkit-outer-spin-button { .h { font-size: 13px; - color: #bbb; - text-align: left; + text-align: center; + color: var(--c-b); +} + +.bp { + margin-bottom: 5px; } .seg { @@ -583,11 +749,11 @@ input[type=number]::-webkit-outer-spin-button { margin: 10px; width: 260px; font-size: 19px; - background-color: #222; - color: white; + background-color: var(--c-2); + color: var(--c-tx); border: 0px solid white; border-radius: 5px; - filter: drop-shadow(0px 0px 2px #000); + filter: drop-shadow(0px 0px 1px #000); text-align: left; } @@ -600,9 +766,15 @@ input[type=number]::-webkit-outer-spin-button { display: block; } -@media all and (max-width: 645px) { +@media all and (max-width: 550px) and (min-width: 374px) { + .infobtn { + width: 155px; + } +} + +@media all and (max-width: 685px) { .top button { - width: 20%; + width: 16.6%; padding: 8px 0 4px 0; } .hd { @@ -612,6 +784,12 @@ input[type=number]::-webkit-outer-spin-button { float: none; } } + +@media all and (max-width: 1249px) { + #buttonPcm { + display: none; + } +} @@ -625,8 +803,10 @@ input[type=number]::-webkit-outer-spin-button { - + + +

Brightness

@@ -652,6 +832,19 @@ input[type=number]::-webkit-outer-spin-button {
+
+
+
+
+
+
+

+
+
+
+
+
R
+
@@ -734,8 +927,8 @@ input[type=number]::-webkit-outer-spin-button {
First preset:
Last preset:
- Time per preset: s
- Transition: s + Time per preset: s
+ Transition: s
@@ -748,7 +941,28 @@ input[type=number]::-webkit-outer-spin-button {
+ + +
- - - -
-
-
-

Sync setup

-

Button setup

- On/Off button enabled:
- Infrared receiver enabled:
- IR info -

WLED Sync UDP Broadcast

- UDP Port:
- Receive Brightness, Color, and Effects
- Send notifications on direct change:
- Send notifications on button press:
- Send Alexa notifications:
- Send Philips Hue change notifications:
- Send Macro notifications:
- Send notifications twice: -

Realtime

- Receive UDP realtime:

- E1.31 (sACN)
- Multicast mode:
- E1.31 start universe:
- Reboot required. Check out LedFx!

- Timeout: ms
- Force max brightness:
- Disable realtime gamma correction:
- Realtime LED offset: -

Alexa Voice Assistant

- Emulate Alexa device:
- Alexa invocation name: -

Blynk

- Blynk, MQTT and Hue sync all connect to external hosts!
- This may impact the responsiveness of the ESP8266.

- For best results, only use one of these services at a time.
- (alternatively, connect a second ESP to them and use the UDP sync)

- Device Auth token:
- Clear the token field to disable. Setup info -

MQTT

- Enable MQTT:
- Broker:
- Username:
- Password:
- Client ID:
- Device Topic:
- Group Topic:
- MQTT info -

Philips Hue

- You can find the bridge IP and the light number in the 'About' section of the hue app.
- Poll Hue light every ms:
- Then, receive On/Off, Brightness, and Color
- Hue Bridge IP:
- . - . - . -
- Press the pushlink button on the bridge, after that save this page!
- (when first connecting)
- - - Hue status: Internal ESP Error!
- -
+Sync Settings + + + +
+
+
+

Sync setup

+

Button setup

+On/Off button enabled:
+Infrared remote: +
+IR info +

WLED Broadcast

+UDP Port:
+Receive Brightness, Color, and Effects
+Send notifications on direct change:
+Send notifications on button press:
+Send Alexa notifications:
+Send Philips Hue change notifications:
+Send Macro notifications:
+Send notifications twice: +

Realtime

+Receive UDP realtime:

+Network DMX input
+Type: +
+
Port:
+Multicast:
+Start universe:
+Reboot required. Check out LedFx!
+Skip out-of-sequence packets:
+DMX start address:
+DMX mode: +
+E1.31 info
+Timeout: ms
+Force max brightness:
+Disable realtime gamma correction:
+Realtime LED offset: +

Alexa Voice Assistant

+Emulate Alexa device:
+Alexa invocation name: +

Blynk

+Blynk, MQTT and Hue sync all connect to external hosts!
+This may impact the responsiveness of the ESP8266.

+For best results, only use one of these services at a time.
+(alternatively, connect a second ESP to them and use the UDP sync)

+Device Auth token:
+Clear the token field to disable. Setup info +

MQTT

+Enable MQTT:
+Broker: +Port:
+The MQTT credentials are sent over an unsecured connection.
+Never use the MQTT password for another service!

+Username:
+Password:
+Client ID:
+Device Topic:
+Group Topic:
+Reboot required to apply changes. MQTT info +

Philips Hue

+You can find the bridge IP and the light number in the 'About' section of the hue app.
+Poll Hue light every ms:
+Then, receive On/Off, Brightness, and Color
+Hue Bridge IP:
+ . + . + . +
+Press the pushlink button on the bridge, after that save this page!
+(when first connecting)
+Hue status: Disabled in this build
+ +
\ No newline at end of file diff --git a/wled00/dmx.cpp b/wled00/dmx.cpp new file mode 100644 index 00000000..ccd3b064 --- /dev/null +++ b/wled00/dmx.cpp @@ -0,0 +1,67 @@ +#include "wled.h" + +/* + * Support for DMX via MAX485. + * Change the output pin in src/dependencies/ESPDMX.cpp if needed. + * Library from: + * https://github.com/Rickgg/ESP-Dmx + */ + +#ifdef WLED_ENABLE_DMX +#include "src/dependencies/dmx/ESPDMX.h" +DMXESPSerial dmx; + +void handleDMX() +{ + // TODO: calculate brightness manually if no shutter channel is set + + uint8_t brightness = strip.getBrightness(); + + for (int i = DMXStartLED; i < ledCount; i++) { // uses the amount of LEDs as fixture count + + uint32_t in = strip.getPixelColor(i); // get the colors for the individual fixtures as suggested by Aircoookie in issue #462 + byte w = in >> 24 & 0xFF; + byte r = in >> 16 & 0xFF; + byte g = in >> 8 & 0xFF; + byte b = in & 0xFF; + + int DMXFixtureStart = DMXStart + (DMXGap * (i - DMXStartLED)); + for (int j = 0; j < DMXChannels; j++) { + int DMXAddr = DMXFixtureStart + j; + switch (DMXFixtureMap[j]) { + case 0: // Set this channel to 0. Good way to tell strobe- and fade-functions to fuck right off. + dmx.write(DMXAddr, 0); + break; + case 1: // Red + dmx.write(DMXAddr, r); + break; + case 2: // Green + dmx.write(DMXAddr, g); + break; + case 3: // Blue + dmx.write(DMXAddr, b); + break; + case 4: // White + dmx.write(DMXAddr, w); + break; + case 5: // Shutter channel. Controls the brightness. + dmx.write(DMXAddr, brightness); + break; + case 6: // Sets this channel to 255. Like 0, but more wholesome. + dmx.write(DMXAddr, 255); + break; + } + } + } + + dmx.update(); // update the DMX bus +} + +void initDMX() { + dmx.init(512); // initialize with bus length +} + +#else +void handleDMX() {} +void initDMX() {} +#endif diff --git a/wled00/e131.cpp b/wled00/e131.cpp new file mode 100644 index 00000000..ddbfabe0 --- /dev/null +++ b/wled00/e131.cpp @@ -0,0 +1,167 @@ +#include "wled.h" + +/* + * E1.31 handler + */ + +void handleE131Packet(e131_packet_t* p, IPAddress clientIP, bool isArtnet){ + //E1.31 protocol support + + uint16_t uni = 0, dmxChannels = 0; + uint8_t* e131_data = nullptr; + uint8_t seq = 0, mde = REALTIME_MODE_E131; + + if (isArtnet) + { + uni = p->art_universe; + dmxChannels = htons(p->art_length); + e131_data = p->art_data; + seq = p->art_sequence_number; + mde = REALTIME_MODE_ARTNET; + } else { + uni = htons(p->universe); + dmxChannels = htons(p->property_value_count) -1; + e131_data = p->property_values; + seq = p->sequence_number; + } + + // only listen for universes we're handling & allocated memory + if (uni >= (e131Universe + E131_MAX_UNIVERSE_COUNT)) return; + + uint8_t previousUniverses = uni - e131Universe; + uint16_t possibleLEDsInCurrentUniverse; + + if (e131SkipOutOfSequence) + if (seq < e131LastSequenceNumber[uni-e131Universe] && seq > 20 && e131LastSequenceNumber[uni-e131Universe] < 250){ + DEBUG_PRINT("skipping E1.31 frame (last seq="); + DEBUG_PRINT(e131LastSequenceNumber[uni-e131Universe]); + DEBUG_PRINT(", current seq="); + DEBUG_PRINT(seq); + DEBUG_PRINT(", universe="); + DEBUG_PRINT(uni); + DEBUG_PRINTLN(")"); + return; + } + e131LastSequenceNumber[uni-e131Universe] = seq; + + // update status info + realtimeIP = clientIP; + byte wChannel = 0; + + switch (DMXMode) { + case DMX_MODE_DISABLED: + return; // nothing to do + break; + + case DMX_MODE_SINGLE_RGB: + if (uni != e131Universe) return; + if (dmxChannels-DMXAddress+1 < 3) return; + realtimeLock(realtimeTimeoutMs, mde); + if (realtimeOverride) return; + wChannel = (dmxChannels-DMXAddress+1 > 3) ? e131_data[DMXAddress+3] : 0; + for (uint16_t i = 0; i < ledCount; i++) + setRealtimePixel(i, e131_data[DMXAddress+0], e131_data[DMXAddress+1], e131_data[DMXAddress+2], wChannel); + break; + + case DMX_MODE_SINGLE_DRGB: + if (uni != e131Universe) return; + if (dmxChannels-DMXAddress+1 < 4) return; + realtimeLock(realtimeTimeoutMs, mde); + if (realtimeOverride) return; + wChannel = (dmxChannels-DMXAddress+1 > 4) ? e131_data[DMXAddress+4] : 0; + if (DMXOldDimmer != e131_data[DMXAddress+0]) { + DMXOldDimmer = e131_data[DMXAddress+0]; + bri = e131_data[DMXAddress+0]; + strip.setBrightness(bri); + } + for (uint16_t i = 0; i < ledCount; i++) + setRealtimePixel(i, e131_data[DMXAddress+1], e131_data[DMXAddress+2], e131_data[DMXAddress+3], wChannel); + break; + + case DMX_MODE_EFFECT: + if (uni != e131Universe) return; + if (dmxChannels-DMXAddress+1 < 11) return; + if (DMXOldDimmer != e131_data[DMXAddress+0]) { + DMXOldDimmer = e131_data[DMXAddress+0]; + bri = e131_data[DMXAddress+0]; + } + if (e131_data[DMXAddress+1] < MODE_COUNT) + effectCurrent = e131_data[DMXAddress+ 1]; + effectSpeed = e131_data[DMXAddress+ 2]; // flickers + effectIntensity = e131_data[DMXAddress+ 3]; + effectPalette = e131_data[DMXAddress+ 4]; + col[0] = e131_data[DMXAddress+ 5]; + col[1] = e131_data[DMXAddress+ 6]; + col[2] = e131_data[DMXAddress+ 7]; + colSec[0] = e131_data[DMXAddress+ 8]; + colSec[1] = e131_data[DMXAddress+ 9]; + colSec[2] = e131_data[DMXAddress+10]; + if (dmxChannels-DMXAddress+1 > 11) + { + col[3] = e131_data[DMXAddress+11]; //white + colSec[3] = e131_data[DMXAddress+12]; + } + transitionDelayTemp = 0; // act fast + colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION); // don't send UDP + return; // don't activate realtime live mode + break; + + case DMX_MODE_MULTIPLE_RGB: + realtimeLock(realtimeTimeoutMs, mde); + if (realtimeOverride) return; + if (previousUniverses == 0) { + // first universe of this fixture + possibleLEDsInCurrentUniverse = (dmxChannels - DMXAddress + 1) / 3; + for (uint16_t i = 0; i < ledCount; i++) { + if (i >= possibleLEDsInCurrentUniverse) break; // more LEDs will follow in next universe(s) + setRealtimePixel(i, e131_data[DMXAddress+i*3+0], e131_data[DMXAddress+i*3+1], e131_data[DMXAddress+i*3+2], 0); + } + } else if (previousUniverses > 0 && uni < (e131Universe + E131_MAX_UNIVERSE_COUNT)) { + // additional universe(s) of this fixture + uint16_t numberOfLEDsInPreviousUniverses = ((512 - DMXAddress + 1) / 3); // first universe + if (previousUniverses > 1) numberOfLEDsInPreviousUniverses += (512 / 3) * (previousUniverses - 1); // extended universe(s) before current + possibleLEDsInCurrentUniverse = dmxChannels / 3; + for (uint16_t i = numberOfLEDsInPreviousUniverses; i < ledCount; i++) { + uint8_t j = i - numberOfLEDsInPreviousUniverses; + if (j >= possibleLEDsInCurrentUniverse) break; // more LEDs will follow in next universe(s) + setRealtimePixel(i, e131_data[j*3+1], e131_data[j*3+2], e131_data[j*3+3], 0); + } + } + break; + + case DMX_MODE_MULTIPLE_DRGB: + realtimeLock(realtimeTimeoutMs, mde); + if (realtimeOverride) return; + if (previousUniverses == 0) { + // first universe of this fixture + if (DMXOldDimmer != e131_data[DMXAddress+0]) { + DMXOldDimmer = e131_data[DMXAddress+0]; + bri = e131_data[DMXAddress+0]; + strip.setBrightness(bri); + } + possibleLEDsInCurrentUniverse = (dmxChannels - DMXAddress) / 3; + for (uint16_t i = 0; i < ledCount; i++) { + if (i >= possibleLEDsInCurrentUniverse) break; // more LEDs will follow in next universe(s) + setRealtimePixel(i, e131_data[DMXAddress+i*3+1], e131_data[DMXAddress+i*3+2], e131_data[DMXAddress+i*3+3], 0); + } + } else if (previousUniverses > 0 && uni < (e131Universe + E131_MAX_UNIVERSE_COUNT)) { + // additional universe(s) of this fixture + uint16_t numberOfLEDsInPreviousUniverses = ((512 - DMXAddress + 1) / 3); // first universe + if (previousUniverses > 1) numberOfLEDsInPreviousUniverses += (512 / 3) * (previousUniverses - 1); // extended universe(s) before current + possibleLEDsInCurrentUniverse = dmxChannels / 3; + for (uint16_t i = numberOfLEDsInPreviousUniverses; i < ledCount; i++) { + uint8_t j = i - numberOfLEDsInPreviousUniverses; + if (j >= possibleLEDsInCurrentUniverse) break; // more LEDs will follow in next universe(s) + setRealtimePixel(i, e131_data[j*3+1], e131_data[j*3+2], e131_data[j*3+3], 0); + } + } + break; + + default: + DEBUG_PRINTLN("unknown E1.31 DMX mode"); + return; // nothing to do + break; + } + + e131NewData = true; +} diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h new file mode 100644 index 00000000..2d8401bc --- /dev/null +++ b/wled00/fcn_declare.h @@ -0,0 +1,184 @@ +#ifndef WLED_FCN_DECLARE_H +#define WLED_FCN_DECLARE_H +#include +#include "src/dependencies/espalexa/EspalexaDevice.h" +#include "src/dependencies/e131/ESPAsyncE131.h" + +/* + * All globally accessible functions are declared here + */ + +//alexa.cpp +void onAlexaChange(EspalexaDevice* dev); +void alexaInit(); +void handleAlexa(); +void onAlexaChange(EspalexaDevice* dev); + +//blynk.cpp +void initBlynk(const char* auth); +void handleBlynk(); +void updateBlynk(); + +//button.cpp +void shortPressAction(); +void handleButton(); +void handleIO(); + +//colors.cpp +void colorFromUint32(uint32_t in, bool secondary = false); +void colorFromUint24(uint32_t in, bool secondary = false); +void relativeChangeWhite(int8_t amount, byte lowerBoundary = 0); +void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb +void colorCTtoRGB(uint16_t mired, byte* rgb); //white spectrum to rgb + +void colorXYtoRGB(float x, float y, byte* rgb); // only defined if huesync disabled TODO +void colorRGBtoXY(byte* rgb, float* xy); // only defined if huesync disabled TODO + +void colorFromDecOrHexString(byte* rgb, char* in); +void colorRGBtoRGBW(byte* rgb); //rgb to rgbw (http://codewelt.com/rgbw). (RGBW_MODE_LEGACY) + +//dmx.cpp +void initDMX(); +void handleDMX(); + +//e131.cpp +void handleE131Packet(e131_packet_t* p, IPAddress clientIP, bool isArtnet); + +//file.cpp +bool handleFileRead(AsyncWebServerRequest*, String path); + +//hue.cpp +void handleHue(); +void reconnectHue(); +void onHueError(void* arg, AsyncClient* client, int8_t error); +void onHueConnect(void* arg, AsyncClient* client); +void sendHuePoll(); +void onHueData(void* arg, AsyncClient* client, void *data, size_t len); + +//ir.cpp +bool decodeIRCustom(uint32_t code); +void relativeChange(byte* property, int8_t amount, byte lowerBoundary = 0, byte higherBoundary = 0xFF); +void changeEffectSpeed(int8_t amount); +void changeEffectIntensity(int8_t amount); +void decodeIR(uint32_t code); +void decodeIR24(uint32_t code); +void decodeIR24OLD(uint32_t code); +void decodeIR24CT(uint32_t code); +void decodeIR40(uint32_t code); +void decodeIR44(uint32_t code); +void decodeIR21(uint32_t code); +void decodeIR6(uint32_t code); + +void initIR(); +void handleIR(); + +//json.cpp +#include "ESPAsyncWebServer.h" +#include "src/dependencies/json/ArduinoJson-v6.h" +#include "src/dependencies/json/AsyncJson-v6.h" +#include "FX.h" +// TODO: AsynicWebServerRequest conflict? + +void deserializeSegment(JsonObject elem, byte it); +bool deserializeState(JsonObject root); +void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id); +void serializeState(JsonObject root); +void serializeInfo(JsonObject root); +void serveJson(AsyncWebServerRequest* request); +void serveLiveLeds(AsyncWebServerRequest* request); + +//led.cpp +void setValuesFromMainSeg(); +void resetTimebase(); +void toggleOnOff(); +void setAllLeds(); +void setLedsStandard(bool justColors = false); +bool colorChanged(); +void colorUpdated(int callMode); +void updateInterfaces(uint8_t callMode); +void handleTransitions(); +void handleNightlight(); + +//mqtt.cpp +bool initMqtt(); +void publishMqtt(); + +//ntp.cpp +void handleNetworkTime(); +void sendNTPPacket(); +bool checkNTPResponse(); +void updateLocalTime(); +void getTimeString(char* out); +bool checkCountdown(); +void setCountdown(); +byte weekdayMondayFirst(); +void checkTimers(); + +//overlay.cpp +void initCronixie(); +void handleOverlays(); +void handleOverlayDraw(); +void _overlayAnalogCountdown(); +void _overlayAnalogClock(); + +byte getSameCodeLength(char code, int index, char const cronixieDisplay[]); +void setCronixie(); +void _overlayCronixie(); +void _drawOverlayCronixie(); + +//set.cpp +void _setRandomColor(bool _sec,bool fromButton=false); +bool isAsterisksOnly(const char* str, byte maxLen); +void handleSettingsSet(AsyncWebServerRequest *request, byte subPage); +bool handleSet(AsyncWebServerRequest *request, const String& req); +int getNumVal(const String* req, uint16_t pos); +bool updateVal(const String* req, const char* key, byte* val, byte minv=0, byte maxv=255); + +//udp.cpp +void notify(byte callMode, bool followUp=false); +void realtimeLock(uint32_t timeoutMs, byte md = REALTIME_MODE_GENERIC); +void handleNotifications(); +void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w); + +//usermod.cpp +void userSetup(); +void userConnected(); +void userLoop(); + +//wled_eeprom.cpp +void commit(); +void clearEEPROM(); +void writeStringToEEPROM(uint16_t pos, char* str, uint16_t len); +void readStringFromEEPROM(uint16_t pos, char* str, uint16_t len); +void saveSettingsToEEPROM(); +void loadSettingsFromEEPROM(bool first); +void savedToPresets(); +bool applyPreset(byte index, bool loadBri = true); +void savePreset(byte index, bool persist = true); +void loadMacro(byte index, char* m); +void applyMacro(byte index); +void saveMacro(byte index, String mc, bool persist = true); //only commit on single save, not in settings + +//wled_serial.cpp +void handleSerial(); + +//wled_server.cpp +bool isIp(String str); +bool captivePortal(AsyncWebServerRequest *request); +void initServer(); +void serveIndexOrWelcome(AsyncWebServerRequest *request); +void serveIndex(AsyncWebServerRequest* request); +String msgProcessor(const String& var); +void serveMessage(AsyncWebServerRequest* request, uint16_t code, String headl, String subl="", byte optionT=255); +String settingsProcessor(const String& var); +String dmxProcessor(const String& var); +void serveSettings(AsyncWebServerRequest* request); + +//xml.cpp +char* XML_response(AsyncWebServerRequest *request, char* dest = nullptr); +char* URL_response(AsyncWebServerRequest *request); +void sappend(char stype, const char* key, int val); +void sappends(char stype, const char* key, char* val); +void getSettingsJS(byte subPage, char* dest); + +#endif diff --git a/wled00/file.cpp b/wled00/file.cpp new file mode 100644 index 00000000..eb0dcb93 --- /dev/null +++ b/wled00/file.cpp @@ -0,0 +1,55 @@ +#include "wled.h" + +/* + * Utility for SPIFFS filesystem + */ + +//filesystem +#ifndef WLED_DISABLE_FILESYSTEM +#include +#ifdef ARDUINO_ARCH_ESP32 +#include "SPIFFS.h" +#endif +#include "SPIFFSEditor.h" +#endif + + +#if !defined WLED_DISABLE_FILESYSTEM && defined WLED_ENABLE_FS_SERVING +//Un-comment any file types you need +String getContentType(AsyncWebServerRequest* request, String filename){ + if(request->hasArg("download")) return "application/octet-stream"; + else if(filename.endsWith(".htm")) return "text/html"; + else if(filename.endsWith(".html")) return "text/html"; +// else if(filename.endsWith(".css")) return "text/css"; +// else if(filename.endsWith(".js")) return "application/javascript"; + else if(filename.endsWith(".json")) return "application/json"; + else if(filename.endsWith(".png")) return "image/png"; +// else if(filename.endsWith(".gif")) return "image/gif"; + else if(filename.endsWith(".jpg")) return "image/jpeg"; + else if(filename.endsWith(".ico")) return "image/x-icon"; +// else if(filename.endsWith(".xml")) return "text/xml"; +// else if(filename.endsWith(".pdf")) return "application/x-pdf"; +// else if(filename.endsWith(".zip")) return "application/x-zip"; +// else if(filename.endsWith(".gz")) return "application/x-gzip"; + return "text/plain"; +} + +bool handleFileRead(AsyncWebServerRequest* request, String path){ + DEBUG_PRINTLN("FileRead: " + path); + if(path.endsWith("/")) path += "index.htm"; + String contentType = getContentType(request, path); + String pathWithGz = path + ".gz"; + if(SPIFFS.exists(pathWithGz)){ + request->send(SPIFFS, pathWithGz, contentType); + return true; + } + if(SPIFFS.exists(path)) { + request->send(SPIFFS, path, contentType); + return true; + } + return false; +} + +#else +bool handleFileRead(AsyncWebServerRequest*, String path){return false;} +#endif diff --git a/wled00/html_other.h b/wled00/html_other.h index 4111bc6e..348efc41 100644 --- a/wled00/html_other.h +++ b/wled00/html_other.h @@ -1,5 +1,5 @@ /* - * Various pages + * Various web pages */ //USER HTML HERE (/u subpage) @@ -15,12 +15,42 @@ const char PAGE_msg[] PROGMEM = R"=====(

%MSG%)====="; +//DMX channel map +const char PAGE_dmxmap[] PROGMEM = R"=====( + +DMX Map + + +
...
)====="; + //firmware update page const char PAGE_update[] PROGMEM = R"=====( WLED Update -

WLED Software Update

Installed version: 0.9.0-b1
Download the latest binary:

)====="; +

WLED Software Update

Installed version: 0.10.0
Download the latest binary:

)====="; //new user welcome page diff --git a/wled00/html_settings.h b/wled00/html_settings.h index 32d6bf4e..a2d8c9c4 100644 --- a/wled00/html_settings.h +++ b/wled00/html_settings.h @@ -1,23 +1,23 @@ /* - * Settings html + * Settings html */ //common CSS of settings pages -const char PAGE_settingsCss[] PROGMEM = R"=====()====="; - +const char PAGE_settingsCss[] PROGMEM = R"=====()====="; //settings menu const char PAGE_settings[] PROGMEM = R"=====( -WLED Settings +WLED Settings
-
+
%DMXMENU%
+ )====="; @@ -62,7 +62,12 @@ AP opens:
-AP IP: Not active
+AP IP: Not active
+

Experimental

+Disable WiFi sleep:
+Can help with connectivity issues.
+Do not enable if WiFi is working correctly, increases power consumption.
+
@@ -75,7 +80,8 @@ const char PAGE_settings_leds[] PROGMEM = R"=====( LED Settings -"; } else if (optt == 253) { - messageBody += "

"; //button to settings + messageBody += F("

"); //button to settings } else if (optt == 254) { - messageBody += "

"; + messageBody += F("

"); } return messageBody; } @@ -284,7 +305,7 @@ String msgProcessor(const String& var) } -void serveMessage(AsyncWebServerRequest* request, uint16_t code, String headl, String subl="", byte optionT=255) +void serveMessage(AsyncWebServerRequest* request, uint16_t code, String headl, String subl, byte optionT) { messageHead = headl; messageSub = subl; @@ -301,10 +322,38 @@ String settingsProcessor(const String& var) getSettingsJS(optionType, buf); return String(buf); } + + #ifdef WLED_ENABLE_DMX + + if (var == "DMXMENU") { + return String(F("
")); + } + + #endif if (var == "SCSS") return String(FPSTR(PAGE_settingsCss)); return String(); } +String dmxProcessor(const String& var) +{ + String mapJS; + #ifdef WLED_ENABLE_DMX + if (var == "DMXVARS") { + mapJS += "\nCN=" + String(DMXChannels) + ";\n"; + mapJS += "CS=" + String(DMXStart) + ";\n"; + mapJS += "CG=" + String(DMXGap) + ";\n"; + mapJS += "LC=" + String(ledCount) + ";\n"; + mapJS += "var CH=["; + for (int i=0;i<15;i++) { + mapJS += String(DMXFixtureMap[i]) + ","; + } + mapJS += "0];"; + } + #endif + + return mapJS; +} + void serveSettings(AsyncWebServerRequest* request) { @@ -318,6 +367,9 @@ void serveSettings(AsyncWebServerRequest* request) else if (url.indexOf("sync") > 0) subPage = 4; else if (url.indexOf("time") > 0) subPage = 5; else if (url.indexOf("sec") > 0) subPage = 6; + #ifdef WLED_ENABLE_DMX // include only if DMX is enabled + else if (url.indexOf("dmx") > 0) subPage = 7; + #endif } else subPage = 255; //welcome page if (subPage == 1 && wifiLock && otaLock) @@ -339,7 +391,8 @@ void serveSettings(AsyncWebServerRequest* request) case 4: request->send_P(200, "text/html", PAGE_settings_sync, settingsProcessor); break; case 5: request->send_P(200, "text/html", PAGE_settings_time, settingsProcessor); break; case 6: request->send_P(200, "text/html", PAGE_settings_sec , settingsProcessor); break; + case 7: request->send_P(200, "text/html", PAGE_settings_dmx , settingsProcessor); break; case 255: request->send_P(200, "text/html", PAGE_welcome); break; - default: request->send_P(200, "text/html", PAGE_settings); + default: request->send_P(200, "text/html", PAGE_settings , settingsProcessor); } } diff --git a/wled00/wled02_xml.ino b/wled00/xml.cpp similarity index 69% rename from wled00/wled02_xml.ino rename to wled00/xml.cpp index 786c90f5..ab1c8e94 100644 --- a/wled00/wled02_xml.ino +++ b/wled00/xml.cpp @@ -1,15 +1,17 @@ +#include "wled.h" + /* * Sending XML status files to client */ //build XML response to HTTP /win API request -char* XML_response(AsyncWebServerRequest *request, char* dest = nullptr) +char* XML_response(AsyncWebServerRequest *request, char* dest) { char sbuf[(dest == nullptr)?1024:1]; //allocate local buffer if none passed obuf = (dest == nullptr)? sbuf:dest; olen = 0; - oappend(""); + oappend((const char*)F("")); oappendi((nightlightActive && nightlightFade) ? briT : bri); oappend(""); @@ -46,23 +48,36 @@ char* XML_response(AsyncWebServerRequest *request, char* dest = nullptr) oappend(""); oappendi(effectPalette); oappend(""); - if (useRGBW && !autoRGBtoRGBW) { + if (strip.rgbwMode) { oappendi(col[3]); } else { oappend("-1"); } oappend(""); oappendi(colSec[3]); - oappend(""); + oappend(""); + oappendi((currentPreset < 1) ? 0:currentPreset); + oappend(""); oappendi(presetCyclingEnabled); oappend(""); - if (realtimeActive) + if (realtimeMode) { String mesg = "Live "; - if (realtimeIP[0] == 0) + if (realtimeMode == REALTIME_MODE_E131 || realtimeMode == REALTIME_MODE_ARTNET) { - mesg += "E1.31 mode"; - } else { + mesg += (realtimeMode == REALTIME_MODE_E131) ? "E1.31" : "Art-Net"; + mesg += " mode "; + mesg += DMXMode; + mesg += F(" at DMX Address "); + mesg += DMXAddress; + mesg += " from "; + mesg += realtimeIP[0]; + for (int i = 1; i < 4; i++) + { + mesg += "."; + mesg += realtimeIP[i]; + } + } else if (realtimeMode == REALTIME_MODE_UDP || realtimeMode == REALTIME_MODE_HYPERION) { mesg += "UDP from "; mesg += realtimeIP[0]; for (int i = 1; i < 4; i++) @@ -70,6 +85,10 @@ char* XML_response(AsyncWebServerRequest *request, char* dest = nullptr) mesg += "."; mesg += realtimeIP[i]; } + } else if (realtimeMode == REALTIME_MODE_ADALIGHT) { + mesg += F("USB Adalight"); + } else { //generic + mesg += "data"; } oappend((char*)mesg.c_str()); } else { @@ -81,6 +100,53 @@ char* XML_response(AsyncWebServerRequest *request, char* dest = nullptr) if (request != nullptr) request->send(200, "text/xml", obuf); } +char* URL_response(AsyncWebServerRequest *request) +{ + char sbuf[256]; //allocate local buffer if none passed + char s2buf[100]; + obuf = s2buf; + olen = 0; + + char s[16]; + oappend("http://"); + IPAddress localIP = WiFi.localIP(); + sprintf(s, "%d.%d.%d.%d", localIP[0], localIP[1], localIP[2], localIP[3]); + oappend(s); + oappend("/win&A="); + oappendi(bri); + oappend("&CL=h"); + for (int i = 0; i < 3; i++) + { + sprintf(s,"%02X", col[i]); + oappend(s); + } + oappend("&C2=h"); + for (int i = 0; i < 3; i++) + { + sprintf(s,"%02X", colSec[i]); + oappend(s); + } + oappend("&FX="); + oappendi(effectCurrent); + oappend("&SX="); + oappendi(effectSpeed); + oappend("&IX="); + oappendi(effectIntensity); + oappend("&FP="); + oappendi(effectPalette); + + obuf = sbuf; + olen = 0; + + oappend((const char*)F("")); + oappend(s2buf); + oappend((const char*)F("")); + + if (request != nullptr) request->send(200, "text/html", obuf); +} + //append a numeric setting to string buffer void sappend(char stype, const char* key, int val) { @@ -144,7 +210,7 @@ void getSettingsJS(byte subPage, char* dest) obuf = dest; olen = 0; - if (subPage <1 || subPage >6) return; + if (subPage <1 || subPage >7) return; if (subPage == 1) { sappends('s',"CS",clientSSID); @@ -176,6 +242,8 @@ void getSettingsJS(byte subPage, char* dest) sappends('s',"AP",fapass); sappend('v',"AC",apChannel); + sappend('c',"WS",noWifiSleep); + if (WiFi.localIP()[0] != 0) //is connected { @@ -220,7 +288,7 @@ void getSettingsJS(byte subPage, char* dest) sappend('v',"CA",briS); sappend('c',"EW",useRGBW); sappend('i',"CO",strip.colorOrder); - sappend('c',"AW",autoRGBtoRGBW); + sappend('v',"AW",strip.rgbwMode); sappend('c',"BO",turnOnAtBoot); sappend('v',"BP",bootPreset); @@ -237,7 +305,6 @@ void getSettingsJS(byte subPage, char* dest) sappend('i',"PB",strip.paletteBlend); sappend('c',"RV",strip.reverseMode); sappend('c',"SL",skipFirstLed); - sappend('v',"DL",disableNLeds); } if (subPage == 3) @@ -260,8 +327,12 @@ void getSettingsJS(byte subPage, char* dest) sappend('c',"SM",notifyMacro); sappend('c',"S2",notifyTwice); sappend('c',"RD",receiveDirect); + sappend('v',"EP",e131Port); + sappend('c',"ES",e131SkipOutOfSequence); sappend('c',"EM",e131Multicast); sappend('v',"EU",e131Universe); + sappend('v',"DA",DMXAddress); + sappend('v',"DM",DMXMode); sappend('v',"ET",realtimeTimeoutMs); sappend('c',"FB",arlsForceMaxBri); sappend('c',"RG",arlsDisableGammaCorrection); @@ -287,9 +358,7 @@ void getSettingsJS(byte subPage, char* dest) sappends('s',"MG",mqttGroupTopic); #endif - #ifdef WLED_DISABLE_HUESYNC - sappends('m',"(\"hms\")[0]","Unsupported in build"); - #else + #ifndef WLED_DISABLE_HUESYNC sappend('v',"H0",hueIP[0]); sappend('v',"H1",hueIP[1]); sappend('v',"H2",hueIP[2]); @@ -300,7 +369,20 @@ void getSettingsJS(byte subPage, char* dest) sappend('c',"HO",hueApplyOnOff); sappend('c',"HB",hueApplyBri); sappend('c',"HC",hueApplyColor); - sappends('m',"(\"hms\")[0]",hueError); + char hueErrorString[25]; + switch (hueError) + { + case HUE_ERROR_INACTIVE : strcpy(hueErrorString,(char*)F("Inactive")); break; + case HUE_ERROR_ACTIVE : strcpy(hueErrorString,(char*)F("Active")); break; + case HUE_ERROR_UNAUTHORIZED : strcpy(hueErrorString,(char*)F("Unauthorized")); break; + case HUE_ERROR_LIGHTID : strcpy(hueErrorString,(char*)F("Invalid light ID")); break; + case HUE_ERROR_PUSHLINK : strcpy(hueErrorString,(char*)F("Link button not pressed")); break; + case HUE_ERROR_JSON_PARSING : strcpy(hueErrorString,(char*)F("JSON parsing error")); break; + case HUE_ERROR_TIMEOUT : strcpy(hueErrorString,(char*)F("Timeout")); break; + default: sprintf(hueErrorString,"Bridge Error %i",hueError); + } + + sappends('m',"(\"sip\")[0]",hueErrorString); #endif } @@ -363,12 +445,38 @@ void getSettingsJS(byte subPage, char* dest) sappend('c',"NO",otaLock); sappend('c',"OW",wifiLock); sappend('c',"AO",aOtaEnabled); - sappends('m',"(\"msg\")[0]","WLED "); + sappends('m',"(\"sip\")[0]","WLED "); olen -= 2; //delete "; oappend(versionString); oappend(" (build "); oappendi(VERSION); - oappend(") OK\";"); + oappend(")\";"); } + + #ifdef WLED_ENABLE_DMX // include only if DMX is enabled + if (subPage == 7) + { + sappend('v',"CN",DMXChannels); + sappend('v',"CG",DMXGap); + sappend('v',"CS",DMXStart); + sappend('v',"SL",DMXStartLED); + + sappend('i',"CH1",DMXFixtureMap[0]); + sappend('i',"CH2",DMXFixtureMap[1]); + sappend('i',"CH3",DMXFixtureMap[2]); + sappend('i',"CH4",DMXFixtureMap[3]); + sappend('i',"CH5",DMXFixtureMap[4]); + sappend('i',"CH6",DMXFixtureMap[5]); + sappend('i',"CH7",DMXFixtureMap[6]); + sappend('i',"CH8",DMXFixtureMap[7]); + sappend('i',"CH9",DMXFixtureMap[8]); + sappend('i',"CH10",DMXFixtureMap[9]); + sappend('i',"CH11",DMXFixtureMap[10]); + sappend('i',"CH12",DMXFixtureMap[11]); + sappend('i',"CH13",DMXFixtureMap[12]); + sappend('i',"CH14",DMXFixtureMap[13]); + sappend('i',"CH15",DMXFixtureMap[14]); + } + #endif oappend("}"); }