From 2b62056f6c6f90c94b9c2562fb2a32fa54abc382 Mon Sep 17 00:00:00 2001 From: Keith Lord Date: Sat, 26 Mar 2022 13:11:17 -0400 Subject: [PATCH] Reorganized the code and tweaked some speed parameters. See change log for details. (#311) * Split lib into Arduino and ESP files. Added 4 more effects for ESP devices (breaks ws2812fx_patterns_web!) * Tweaked some timing and finalized the effect categories * removed extar ESP modes and refactored rainbow_cycle to be more efficient * updated change log * fixed documentation * updated tester.txt --- examples/tester.txt | 300 +++++ examples/tester.zsh | 96 ++ .../ws2812fx_patterns_web.ino | 304 ++--- .../ws2812fx_segments_OTA.ino | 2 +- extras/WS2812FX change log.txt | 47 +- library.json | 2 +- library.properties | 2 +- src/WS2812FX.cpp | 1101 +---------------- src/WS2812FX.h | 286 +---- src/modes.cpp | 794 ++++++++++++ src/modes_arduino.h | 313 +++++ src/modes_esp.h | 258 ++++ src/modes_funcs.cpp | 397 ++++++ 13 files changed, 2341 insertions(+), 1561 deletions(-) create mode 100644 examples/tester.txt create mode 100755 examples/tester.zsh create mode 100644 src/modes.cpp create mode 100644 src/modes_arduino.h create mode 100644 src/modes_esp.h create mode 100644 src/modes_funcs.cpp diff --git a/examples/tester.txt b/examples/tester.txt new file mode 100644 index 0000000..b3d416f --- /dev/null +++ b/examples/tester.txt @@ -0,0 +1,300 @@ + +Verifing auto_mode_cycle/auto_mode_cycle.ino, for Arduino Leonardo +Sketch uses 19822 bytes (69%) of program storage space. Maximum is 28672 bytes. +Global variables use 506 bytes (19%) of dynamic memory, leaving 2054 bytes for local variables. Maximum is 2560 bytes. +exit status 0 + +Verifing external_trigger/external_trigger.ino, for Arduino Leonardo +Sketch uses 19854 bytes (69%) of program storage space. Maximum is 28672 bytes. +Global variables use 506 bytes (19%) of dynamic memory, leaving 2054 bytes for local variables. Maximum is 2560 bytes. +exit status 0 + +Verifing serial_control/serial_control.ino, for Arduino Leonardo +Sketch uses 23784 bytes (82%) of program storage space. Maximum is 28672 bytes. +Global variables use 690 bytes (26%) of dynamic memory, leaving 1870 bytes for local variables. Maximum is 2560 bytes. +exit status 0 + +Verifing ws2812fx_audio_reactive/ws2812fx_audio_reactive.ino, for Arduino Leonardo +Sketch uses 20468 bytes (71%) of program storage space. Maximum is 28672 bytes. +Global variables use 526 bytes (20%) of dynamic memory, leaving 2034 bytes for local variables. Maximum is 2560 bytes. +exit status 0 + +Verifing ws2812fx_custom_effect/ws2812fx_custom_effect.ino, for Arduino Leonardo +Sketch uses 20248 bytes (70%) of program storage space. Maximum is 28672 bytes. +Global variables use 498 bytes (19%) of dynamic memory, leaving 2062 bytes for local variables. Maximum is 2560 bytes. +exit status 0 + +Verifing ws2812fx_custom_effect2/ws2812fx_custom_effect2.ino, for Arduino Leonardo +Sketch uses 20148 bytes (70%) of program storage space. Maximum is 28672 bytes. +Global variables use 513 bytes (20%) of dynamic memory, leaving 2047 bytes for local variables. Maximum is 2560 bytes. +exit status 0 + +Verifing ws2812fx_custom_FastLED/ws2812fx_custom_FastLED.ino, for Arduino Leonardo +Sketch uses 20054 bytes (69%) of program storage space. Maximum is 28672 bytes. +Global variables use 543 bytes (21%) of dynamic memory, leaving 2017 bytes for local variables. Maximum is 2560 bytes. +exit status 0 + +Verifing ws2812fx_limit_current/ws2812fx_limit_current.ino, for Arduino Leonardo +Sketch uses 23470 bytes (81%) of program storage space. Maximum is 28672 bytes. +Global variables use 790 bytes (30%) of dynamic memory, leaving 1770 bytes for local variables. Maximum is 2560 bytes. +exit status 0 + +Verifing ws2812fx_matrix/ws2812fx_matrix.ino, for Arduino Leonardo +Sketch uses 12432 bytes (43%) of program storage space. Maximum is 28672 bytes. +Global variables use 430 bytes (16%) of dynamic memory, leaving 2130 bytes for local variables. Maximum is 2560 bytes. +exit status 0 + +Verifing ws2812fx_msgeq7/ws2812fx_msgeq7.ino, for Arduino Leonardo +Sketch uses 20486 bytes (71%) of program storage space. Maximum is 28672 bytes. +Global variables use 522 bytes (20%) of dynamic memory, leaving 2038 bytes for local variables. Maximum is 2560 bytes. +exit status 0 + +Verifing ws2812fx_overlay/ws2812fx_overlay.ino, for Arduino Leonardo +Sketch uses 19820 bytes (69%) of program storage space. Maximum is 28672 bytes. +Global variables use 616 bytes (24%) of dynamic memory, leaving 1944 bytes for local variables. Maximum is 2560 bytes. +exit status 0 + +Verifing ws2812fx_segment_sequence/ws2812fx_segment_sequence.ino, for Arduino Leonardo +Sketch uses 20560 bytes (71%) of program storage space. Maximum is 28672 bytes. +Global variables use 528 bytes (20%) of dynamic memory, leaving 2032 bytes for local variables. Maximum is 2560 bytes. +exit status 0 + +Verifing ws2812fx_segments/ws2812fx_segments.ino, for Arduino Leonardo +Sketch uses 19838 bytes (69%) of program storage space. Maximum is 28672 bytes. +Global variables use 498 bytes (19%) of dynamic memory, leaving 2062 bytes for local variables. Maximum is 2560 bytes. +exit status 0 + +Verifing ws2812fx_spi/ws2812fx_spi.ino, for Arduino Leonardo +Sketch uses 20074 bytes (70%) of program storage space. Maximum is 28672 bytes. +Global variables use 499 bytes (19%) of dynamic memory, leaving 2061 bytes for local variables. Maximum is 2560 bytes. +exit status 0 + +Verifing ws2812fx_transitions/ws2812fx_transitions.ino, for Arduino Leonardo +Sketch uses 20280 bytes (70%) of program storage space. Maximum is 28672 bytes. +Global variables use 456 bytes (17%) of dynamic memory, leaving 2104 bytes for local variables. Maximum is 2560 bytes. +exit status 0 + +Verifing ws2812fx_virtual_strip/ws2812fx_virtual_strip.ino for Arduino Leonardo +Sketch uses 20128 bytes (70%) of program storage space. Maximum is 28672 bytes. +Global variables use 616 bytes (24%) of dynamic memory, leaving 1944 bytes for local variables. Maximum is 2560 bytes. +exit status 0 + +Verifing auto_mode_cycle/auto_mode_cycle.ino, for ESP8266 +Sketch uses 273961 bytes (26%) of program storage space. Maximum is 1044464 bytes. +Global variables use 29196 bytes (35%) of dynamic memory, leaving 52724 bytes for local variables. Maximum is 81920 bytes. +exit status 0 + +Verifing external_trigger/external_trigger.ino, for ESP8266 +Sketch uses 273977 bytes (26%) of program storage space. Maximum is 1044464 bytes. +Global variables use 29196 bytes (35%) of dynamic memory, leaving 52724 bytes for local variables. Maximum is 81920 bytes. +exit status 0 + +Verifing serial_control/serial_control.ino, for ESP8266 +Sketch uses 281337 bytes (26%) of program storage space. Maximum is 1044464 bytes. +Global variables use 29324 bytes (35%) of dynamic memory, leaving 52596 bytes for local variables. Maximum is 81920 bytes. +exit status 0 + +Verifing ws2812fx_audio_reactive/ws2812fx_audio_reactive.ino, for ESP8266 +Sketch uses 279177 bytes (26%) of program storage space. Maximum is 1044464 bytes. +Global variables use 29300 bytes (35%) of dynamic memory, leaving 52620 bytes for local variables. Maximum is 81920 bytes. +exit status 0 + +Verifing ws2812fx_custom_effect/ws2812fx_custom_effect.ino, for ESP8266 +Sketch uses 278693 bytes (26%) of program storage space. Maximum is 1044464 bytes. +Global variables use 29256 bytes (35%) of dynamic memory, leaving 52664 bytes for local variables. Maximum is 81920 bytes. +exit status 0 + +Verifing ws2812fx_custom_effect2/ws2812fx_custom_effect2.ino, for ESP8266 +Sketch uses 278785 bytes (26%) of program storage space. Maximum is 1044464 bytes. +Global variables use 29264 bytes (35%) of dynamic memory, leaving 52656 bytes for local variables. Maximum is 81920 bytes. +exit status 0 + +Verifing ws2812fx_custom_FastLED/ws2812fx_custom_FastLED.ino, for ESP8266 +Sketch uses 279005 bytes (26%) of program storage space. Maximum is 1044464 bytes. +Global variables use 29320 bytes (35%) of dynamic memory, leaving 52600 bytes for local variables. Maximum is 81920 bytes. +exit status 0 + +Verifing ws2812fx_limit_current/ws2812fx_limit_current.ino, for ESP8266 +Sketch uses 281245 bytes (26%) of program storage space. Maximum is 1044464 bytes. +Global variables use 29408 bytes (35%) of dynamic memory, leaving 52512 bytes for local variables. Maximum is 81920 bytes. +exit status 0 + +Verifing ws2812fx_matrix/ws2812fx_matrix.ino, for ESP8266 +Sketch uses 269033 bytes (25%) of program storage space. Maximum is 1044464 bytes. +Global variables use 28556 bytes (34%) of dynamic memory, leaving 53364 bytes for local variables. Maximum is 81920 bytes. +exit status 0 + +Verifing ws2812fx_msgeq7/ws2812fx_msgeq7.ino, for ESP8266 +Sketch uses 279121 bytes (26%) of program storage space. Maximum is 1044464 bytes. +Global variables use 29272 bytes (35%) of dynamic memory, leaving 52648 bytes for local variables. Maximum is 81920 bytes. +exit status 0 + +Verifing ws2812fx_overlay/ws2812fx_overlay.ino, for ESP8266 +Sketch uses 274041 bytes (26%) of program storage space. Maximum is 1044464 bytes. +Global variables use 29380 bytes (35%) of dynamic memory, leaving 52540 bytes for local variables. Maximum is 81920 bytes. +exit status 0 + +Verifing ws2812fx_segment_sequence/ws2812fx_segment_sequence.ino, for ESP8266 +Sketch uses 279045 bytes (26%) of program storage space. Maximum is 1044464 bytes. +Global variables use 29296 bytes (35%) of dynamic memory, leaving 52624 bytes for local variables. Maximum is 81920 bytes. +exit status 0 + +Verifing ws2812fx_segments/ws2812fx_segments.ino, for ESP8266 +Sketch uses 278485 bytes (26%) of program storage space. Maximum is 1044464 bytes. +Global variables use 29256 bytes (35%) of dynamic memory, leaving 52664 bytes for local variables. Maximum is 81920 bytes. +exit status 0 + +Verifing ws2812fx_spi/ws2812fx_spi.ino, for ESP8266 +Sketch uses 275117 bytes (26%) of program storage space. Maximum is 1044464 bytes. +Global variables use 29200 bytes (35%) of dynamic memory, leaving 52720 bytes for local variables. Maximum is 81920 bytes. +exit status 0 + +Verifing ws2812fx_transitions/ws2812fx_transitions.ino, for ESP8266 +Sketch uses 274301 bytes (26%) of program storage space. Maximum is 1044464 bytes. +Global variables use 29120 bytes (35%) of dynamic memory, leaving 52800 bytes for local variables. Maximum is 81920 bytes. +exit status 0 + +Verifing ws2812fx_virtual_strip/ws2812fx_virtual_strip.ino for ESP8266 +Sketch uses 274025 bytes (26%) of program storage space. Maximum is 1044464 bytes. +Global variables use 29380 bytes (35%) of dynamic memory, leaving 52540 bytes for local variables. Maximum is 81920 bytes. +exit status 0 + +Verifing auto_mode_cycle/auto_mode_cycle.ino, for ESP32 +Sketch uses 236601 bytes (18%) of program storage space. Maximum is 1310720 bytes. +Global variables use 17548 bytes (5%) of dynamic memory, leaving 310132 bytes for local variables. Maximum is 327680 bytes. +exit status 0 + +Verifing external_trigger/external_trigger.ino, for ESP32 +Sketch uses 242241 bytes (18%) of program storage space. Maximum is 1310720 bytes. +Global variables use 17740 bytes (5%) of dynamic memory, leaving 309940 bytes for local variables. Maximum is 327680 bytes. +exit status 0 + +Verifing serial_control/serial_control.ino, for ESP32 +Sketch uses 256929 bytes (19%) of program storage space. Maximum is 1310720 bytes. +Global variables use 17708 bytes (5%) of dynamic memory, leaving 309972 bytes for local variables. Maximum is 327680 bytes. +exit status 0 + +Verifing ws2812fx_audio_reactive/ws2812fx_audio_reactive.ino, for ESP32 +Sketch uses 259177 bytes (19%) of program storage space. Maximum is 1310720 bytes. +Global variables use 17876 bytes (5%) of dynamic memory, leaving 309804 bytes for local variables. Maximum is 327680 bytes. +exit status 0 + +Verifing ws2812fx_custom_effect/ws2812fx_custom_effect.ino, for ESP32 +Sketch uses 254493 bytes (19%) of program storage space. Maximum is 1310720 bytes. +Global variables use 17684 bytes (5%) of dynamic memory, leaving 309996 bytes for local variables. Maximum is 327680 bytes. +exit status 0 + +Verifing ws2812fx_custom_effect2/ws2812fx_custom_effect2.ino, for ESP32 +Sketch uses 254581 bytes (19%) of program storage space. Maximum is 1310720 bytes. +Global variables use 17692 bytes (5%) of dynamic memory, leaving 309988 bytes for local variables. Maximum is 327680 bytes. +exit status 0 + +Verifing ws2812fx_custom_FastLED/ws2812fx_custom_FastLED.ino, for ESP32 +Sketch uses 255801 bytes (19%) of program storage space. Maximum is 1310720 bytes. +Global variables use 17820 bytes (5%) of dynamic memory, leaving 309860 bytes for local variables. Maximum is 327680 bytes. +exit status 0 + +Verifing ws2812fx_limit_current/ws2812fx_limit_current.ino, for ESP32 +Sketch uses 256873 bytes (19%) of program storage space. Maximum is 1310720 bytes. +Global variables use 17708 bytes (5%) of dynamic memory, leaving 309972 bytes for local variables. Maximum is 327680 bytes. +exit status 0 + +Verifing ws2812fx_matrix/ws2812fx_matrix.ino, for ESP32 +Sketch uses 238801 bytes (18%) of program storage space. Maximum is 1310720 bytes. +Global variables use 17412 bytes (5%) of dynamic memory, leaving 310268 bytes for local variables. Maximum is 327680 bytes. +exit status 0 + +Verifing ws2812fx_msgeq7/ws2812fx_msgeq7.ino, for ESP32 +Sketch uses 259433 bytes (19%) of program storage space. Maximum is 1310720 bytes. +Global variables use 17876 bytes (5%) of dynamic memory, leaving 309804 bytes for local variables. Maximum is 327680 bytes. +exit status 0 + +Verifing ws2812fx_overlay/ws2812fx_overlay.ino, for ESP32 +Sketch uses 236701 bytes (18%) of program storage space. Maximum is 1310720 bytes. +Global variables use 17732 bytes (5%) of dynamic memory, leaving 309948 bytes for local variables. Maximum is 327680 bytes. +exit status 0 + +Verifing ws2812fx_segment_sequence/ws2812fx_segment_sequence.ino, for ESP32 +Sketch uses 255793 bytes (19%) of program storage space. Maximum is 1310720 bytes. +Global variables use 17732 bytes (5%) of dynamic memory, leaving 309948 bytes for local variables. Maximum is 327680 bytes. +exit status 0 + +Verifing ws2812fx_segments/ws2812fx_segments.ino, for ESP32 +Sketch uses 254281 bytes (19%) of program storage space. Maximum is 1310720 bytes. +Global variables use 17684 bytes (5%) of dynamic memory, leaving 309996 bytes for local variables. Maximum is 327680 bytes. +exit status 0 + +Verifing ws2812fx_spi/ws2812fx_spi.ino, for ESP32 +Sketch uses 239481 bytes (18%) of program storage space. Maximum is 1310720 bytes. +Global variables use 17612 bytes (5%) of dynamic memory, leaving 310068 bytes for local variables. Maximum is 327680 bytes. +exit status 0 + +Verifing ws2812fx_transitions/ws2812fx_transitions.ino, for ESP32 +Sketch uses 237045 bytes (18%) of program storage space. Maximum is 1310720 bytes. +Global variables use 17476 bytes (5%) of dynamic memory, leaving 310204 bytes for local variables. Maximum is 327680 bytes. +exit status 0 + +Verifing ws2812fx_virtual_strip/ws2812fx_virtual_strip.ino for ESP32 +Sketch uses 236685 bytes (18%) of program storage space. Maximum is 1310720 bytes. +Global variables use 17732 bytes (5%) of dynamic memory, leaving 309948 bytes for local variables. Maximum is 327680 bytes. +exit status 0 + +Verifing esp8266_webinterface/esp8266_webinterface.ino, for ESP8266 +Sketch uses 319997 bytes (30%) of program storage space. Maximum is 1044464 bytes. +Global variables use 30052 bytes (36%) of dynamic memory, leaving 51868 bytes for local variables. Maximum is 81920 bytes. +exit status 0 + +Verifing ws2812fx_alexa/ws2812fx_alexa.ino, for ESP8266 +Sketch uses 334177 bytes (31%) of program storage space. Maximum is 1044464 bytes. +Global variables use 31112 bytes (37%) of dynamic memory, leaving 50808 bytes for local variables. Maximum is 81920 bytes. +exit status 0 + +Verifing ws2812fx_patterns_web/ws2812fx_patterns_web.ino, for ESP8266 +Sketch uses 365761 bytes (35%) of program storage space. Maximum is 1044464 bytes. +Global variables use 34344 bytes (41%) of dynamic memory, leaving 47576 bytes for local variables. Maximum is 81920 bytes. +exit status 0 + +Verifing ws2812fx_segments_OTA/ws2812fx_segments_OTA.ino, for ESP8266 +Sketch uses 326909 bytes (31%) of program storage space. Maximum is 1044464 bytes. +Global variables use 30084 bytes (36%) of dynamic memory, leaving 51836 bytes for local variables. Maximum is 81920 bytes. +exit status 0 + +Verifing ws2812fx_segments_web/ws2812fx_segments_web.ino for ESP8266 +Sketch uses 450289 bytes (43%) of program storage space. Maximum is 1044464 bytes. +Global variables use 36460 bytes (44%) of dynamic memory, leaving 45460 bytes for local variables. Maximum is 81920 bytes. +exit status 0 + +Verifing esp8266_webinterface/esp8266_webinterface.ino, for ESP32 +Sketch uses 706837 bytes (53%) of program storage space. Maximum is 1310720 bytes. +Global variables use 45020 bytes (13%) of dynamic memory, leaving 282660 bytes for local variables. Maximum is 327680 bytes. +exit status 0 + +Verifing ws2812fx_alexa/ws2812fx_alexa.ino, for ESP32 +Sketch uses 727933 bytes (55%) of program storage space. Maximum is 1310720 bytes. +Global variables use 40072 bytes (12%) of dynamic memory, leaving 287608 bytes for local variables. Maximum is 327680 bytes. +exit status 0 + +Verifing ws2812fx_patterns_web/ws2812fx_patterns_web.ino, for ESP32 +Sketch uses 783157 bytes (59%) of program storage space. Maximum is 1310720 bytes. +Global variables use 44976 bytes (13%) of dynamic memory, leaving 282704 bytes for local variables. Maximum is 327680 bytes. +exit status 0 + +Verifing ws2812fx_segments_OTA/ws2812fx_segments_OTA.ino, for ESP32 +Sketch uses 712377 bytes (54%) of program storage space. Maximum is 1310720 bytes. +Global variables use 42796 bytes (13%) of dynamic memory, leaving 284884 bytes for local variables. Maximum is 327680 bytes. +exit status 0 + +Verifing ws2812fx_segments_web/ws2812fx_segments_web.ino for ESP32 +Sketch uses 850161 bytes (64%) of program storage space. Maximum is 1310720 bytes. +Global variables use 45536 bytes (13%) of dynamic memory, leaving 282144 bytes for local variables. Maximum is 327680 bytes. +exit status 0 + +Verifing ws2812fx_soundfx/ws2812fx_soundfx.ino for ESP8266 +Sketch uses 381137 bytes (36%) of program storage space. Maximum is 1044464 bytes. +Global variables use 34580 bytes (42%) of dynamic memory, leaving 47340 bytes for local variables. Maximum is 81920 bytes. +exit status 0 + +Verifing ws2812fx_dma/ws2812fx_dma.ino for ESP8266 +Sketch uses 280125 bytes (26%) of program storage space. Maximum is 1044464 bytes. +Global variables use 29348 bytes (35%) of dynamic memory, leaving 52572 bytes for local variables. Maximum is 81920 bytes. +exit status 0 diff --git a/examples/tester.zsh b/examples/tester.zsh new file mode 100755 index 0000000..d1a8cd7 --- /dev/null +++ b/examples/tester.zsh @@ -0,0 +1,96 @@ +#!/bin/zsh + +# simple test script that verifies all the WS2812FX example sketches. +# note: behind the scenes this script runs the Arduino IDE, so you will see +# the IDE's splash screen displayed during each individual test. +# also note: ths script takes a long time to run. + +# add the Arduino application to $PATH +export PATH=$PATH:/Applications/Arduino.app/Contents/MacOS + +# test that we can execute the Arduino command +#Arduino --version + +# board aliases +alias -g leonardo-board="arduino:avr:leonardo" +alias -g esp8266-board="esp8266:esp8266:nodemcuv2" +alias -g esp32-board="esp32:esp32:esp32doit-devkit-v1" + +# port aliases +alias -g leonardo-port="/dev/cu.usbmodem14101" +alias -g esp8266-port="/dev/cu.usbserial-0001" +alias -g esp32-port="/dev/cu.usbserial-0001" + +# example verify command: +# arduino --board leonardo-board --verify ws2812fx_segments/ws2812fx_segments.ino + +# example upload command: +#arduino --board leonardo-board --port leonardo-port --upload ws2812fx_segments/ws2812fx_segments.ino + +# create an list of basic example sketches that can be verified for all boards +basic_sketches=( + 'auto_mode_cycle/auto_mode_cycle.ino', + 'external_trigger/external_trigger.ino', + 'serial_control/serial_control.ino', + 'ws2812fx_audio_reactive/ws2812fx_audio_reactive.ino', + 'ws2812fx_custom_effect/ws2812fx_custom_effect.ino', + 'ws2812fx_custom_effect2/ws2812fx_custom_effect2.ino', + 'ws2812fx_custom_FastLED/ws2812fx_custom_FastLED.ino', + 'ws2812fx_limit_current/ws2812fx_limit_current.ino', + 'ws2812fx_matrix/ws2812fx_matrix.ino', + 'ws2812fx_msgeq7/ws2812fx_msgeq7.ino', + 'ws2812fx_overlay/ws2812fx_overlay.ino', + 'ws2812fx_segment_sequence/ws2812fx_segment_sequence.ino', + 'ws2812fx_segments/ws2812fx_segments.ino', + 'ws2812fx_spi/ws2812fx_spi.ino', + 'ws2812fx_transitions/ws2812fx_transitions.ino', + 'ws2812fx_virtual_strip/ws2812fx_virtual_strip.ino' +) + +# run verify command for all sketches for the Arduino Leonardo board +for ((i = 1; i <= $#basic_sketches; i++)) { + echo "\nVerifing" $basic_sketches[i] for Arduino Leonardo + arduino --board leonardo-board --verify $basic_sketches[i] 2>/dev/null; echo "exit status" $? +} + +# run verify command for all sketches for the ESP8266 board +for ((i = 1; i <= $#basic_sketches; i++)) { + echo "\nVerifing" $basic_sketches[i] for ESP8266 + arduino --board esp8266-board --verify $basic_sketches[i] 2>/dev/null; echo "exit status" $? +} + +# run verify command for all sketches for the ESP32 board +for ((i = 1; i <= $#basic_sketches; i++)) { + echo "\nVerifing" $basic_sketches[i] for ESP32 + arduino --board esp32-board --verify $basic_sketches[i] 2>/dev/null; echo "exit status" $? +} + +# create an list of example sketches that use WiFi to be verified only on ESP boards +wifi_sketches=( + 'esp8266_webinterface/esp8266_webinterface.ino', + 'ws2812fx_alexa/ws2812fx_alexa.ino', + 'ws2812fx_patterns_web/ws2812fx_patterns_web.ino', + 'ws2812fx_segments_OTA/ws2812fx_segments_OTA.ino', + 'ws2812fx_segments_web/ws2812fx_segments_web.ino' +) + +# run verify command for all sketches for the ESP8266 board +for ((i = 1; i <= $#wifi_sketches; i++)) { + echo "\nVerifing" $wifi_sketches[i] for ESP8266 + arduino --board esp8266-board --verify $wifi_sketches[i] 2>/dev/null; echo "exit status" $? +} + +# run verify command for all sketches for the ESP32 board +for ((i = 1; i <= $#wifi_sketches; i++)) { + echo "\nVerifing" $wifi_sketches[i] for ESP32 + arduino --board esp32-board --verify $wifi_sketches[i] 2>/dev/null; echo "exit status" $? +} + +# the ws2812fx_soundfx example sketch uses the ESP8266Audio, which doesn't seem to support +# the ESP32 very well, so test that separately +echo "\nVerifing ws2812fx_soundfx/ws2812fx_soundfx.ino for ESP8266" +arduino --board esp8266-board --verify ws2812fx_soundfx/ws2812fx_soundfx.ino 2>/dev/null; echo "exit status" $? + +# the ws2812fx_dma example sketch is written to work only on ESP8266 boards, so test that separately +echo "\nVerifing ws2812fx_dma/ws2812fx_dma.ino for ESP8266" +arduino --board esp8266-board --verify ws2812fx_dma/ws2812fx_dma.ino 2>/dev/null; echo "exit status" $? diff --git a/examples/ws2812fx_patterns_web/ws2812fx_patterns_web.ino b/examples/ws2812fx_patterns_web/ws2812fx_patterns_web.ino index 013b107..9202ba6 100644 --- a/examples/ws2812fx_patterns_web/ws2812fx_patterns_web.ino +++ b/examples/ws2812fx_patterns_web/ws2812fx_patterns_web.ino @@ -37,15 +37,18 @@ 2018-11-30 added custom aux functions and OTA update */ +#define DYNAMIC_JSON_DOCUMENT_SIZE 4096 /* used by AsyncJson. Default 1024 bytes is too small */ + #include -#include +#include +#include #include #include #include -#define VERSION "2.1.0" +#define VERSION "2.4.0" -uint8_t dataPin = D1; // default digital pin used to drive the LED strip +uint8_t dataPin = 14; // default digital pin used to drive the LED strip uint16_t numLeds = 30; // default number of LEDs on the strip #define WIFI_SSID "xxxxxxxx" // WiFi network @@ -53,8 +56,6 @@ uint16_t numLeds = 30; // default number of LEDs on the strip #define HTTP_PORT 80 #define MAX_NUM_PATTERNS 8 -// duration, brightness, numSegments, [ { first, last, speed, mode, options, colors[] } ] -#define DEFAULT_PATTERN {30, 64, 1, { {0, (uint16_t)(numLeds-1), (uint16_t)(numLeds*20), FX_MODE_STATIC, NO_OPTIONS, {RED, BLACK, BLACK}} }} typedef struct Pattern { // 208 bytes/pattern int duration; @@ -64,13 +65,19 @@ typedef struct Pattern { // 208 bytes/pattern } pattern; // setup a default patterns array -Pattern patterns[MAX_NUM_PATTERNS] = { DEFAULT_PATTERN }; +Pattern patterns[MAX_NUM_PATTERNS] = { + { 300, 32, 1, { // duration, brightness, numSegments + // first, last, speed, mode, options, colors[] + {0, (uint16_t)(numLeds - 1), 5000, FX_MODE_STATIC, NO_OPTIONS, {BLUE, BLACK, BLACK}} + } + } +}; int numPatterns = 1; int currentPattern = 0; unsigned long lastTime = 0; WS2812FX ws2812fx = WS2812FX(numLeds, dataPin, NEO_GRB + NEO_KHZ800); -ESP8266WebServer server(HTTP_PORT); +AsyncWebServer server(HTTP_PORT); void (*customAuxFunc[])(void) { // define custom auxiliary functions here [] { Serial.println("running customAuxFunc[0]"); }, @@ -83,7 +90,14 @@ void setup() { delay(500); Serial.println("\r\n"); - EEPROM.begin(2048); // for ESP8266 (comment out if using an Arduino) + // init LED strip with a default segment + ws2812fx.init(); + ws2812fx.setBrightness(64); + ws2812fx.setSegment(0, 0, numLeds - 1, FX_MODE_STATIC, GREEN, 1000); + ws2812fx.start(); + Serial.println("LED strip initialized"); + + EEPROM.begin(4096); // for ESP (comment out if using an Arduino) // init WiFi WiFi.begin(WIFI_SSID, WIFI_PASSWORD); @@ -117,29 +131,35 @@ void setup() { }); ArduinoOTA.begin(); - // init LED strip with a default segment - ws2812fx.init(); - ws2812fx.setBrightness(128); - ws2812fx.setSegment(0, 0, numLeds - 1, FX_MODE_STATIC, RED, 3000, false); +#ifdef ARDUINO_ARCH_ESP8266 // if not rebooting due to catastrophic error, restore pattern data from eeprom - struct rst_info *rstInfo = system_get_rst_info(); - //Serial.print("rstInfo->reason:"); Serial.println(rstInfo->reason); + struct rst_info *rstInfo = system_get_rst_info(); // ESP8266 + Serial.print("rstInfo->reason:"); Serial.println(rstInfo->reason); + Serial.print("ESP.getResetReason():"); Serial.println(ESP.getResetReason()); if (rstInfo->reason != REASON_EXCEPTION_RST) { // not reason 2 restoreFromEEPROM(); } +#endif - ws2812fx.start(); +#ifdef ARDUINO_ARCH_ESP32 + // if esp32 was reset by powering on or uploading a program via the serial port or + // uploading a program via OTA, restore pattern data from eeprom + esp_reset_reason_t rstInfo = esp_reset_reason(); // ESP32 + if (rstInfo == ESP_RST_POWERON || rstInfo == ESP_RST_WDT || rstInfo == ESP_RST_SW) { + restoreFromEEPROM(); + } +#endif // config and start the web server configServer(); + DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*"); // add CORS header server.begin(); } void loop() { ArduinoOTA.handle(); ws2812fx.service(); - server.handleClient(); // if it's time to change pattern, do it now unsigned long now = millis(); @@ -153,30 +173,32 @@ void loop() { WS2812FX::segment seg = patterns[currentPattern].segments[i]; ws2812fx.setSegment(i, seg.start, seg.stop, seg.mode, seg.colors, seg.speed, seg.options); } + ws2812fx.start(); lastTime = now; } } void configServer() { - server.onNotFound([]() { - server.sendHeader("Access-Control-Allow-Origin", "*"); - server.send(404, "text/plain", "Page not found"); + + server.onNotFound([](AsyncWebServerRequest * request) { + request->send(404, "text/plain", "Page not found"); }); // return the WS2812FX status // optionally set the running state or run custom auxiliary functions - server.on("/status", []() { - server.sendHeader("Access-Control-Allow-Origin", "*"); - String running = server.arg("running"); - if (running.length() > 0) { - if (running == "true") ws2812fx.start(); + server.on("/status", [](AsyncWebServerRequest * request) { + if (request->hasParam("running")) { + AsyncWebParameter* p = request->getParam("running"); + const char* running = p->value().c_str(); + if (strcmp(running, "true") == 0) ws2812fx.start(); else ws2812fx.stop(); } - String auxFunc = server.arg("auxFunc"); - if (auxFunc.length() > 0) { - int auxFuncIndex = auxFunc.toInt(); - if (auxFuncIndex >= 0 && (size_t)auxFuncIndex < sizeof(customAuxFunc) / sizeof(customAuxFunc[0])) { + if (request->hasParam("auxFunc")) { + AsyncWebParameter* p = request->getParam("auxFunc"); + int auxFuncIndex = atoi(p->value().c_str()); + size_t customAuxFuncSize = sizeof(customAuxFunc) / sizeof(customAuxFunc[0]); + if (auxFuncIndex >= 0 && (size_t)auxFuncIndex < customAuxFuncSize) { customAuxFunc[auxFuncIndex](); } } @@ -186,12 +208,13 @@ void configServer() { strcat(status, "\",\"isRunning\":"); strcat(status, ws2812fx.isRunning() ? "true" : "false"); strcat(status, "}"); - server.send(200, "application/json", status); + + request->send(200, "application/json", status); }); // send the WS2812FX mode info in JSON format - server.on("/getModes", []() { - char modes[1000] = "["; + server.on("/getModes", [](AsyncWebServerRequest * request) { + char modes[1300] = "["; for (uint8_t i = 0; i < ws2812fx.getModeCount(); i++) { strcat(modes, "\""); strcat_P(modes, (PGM_P)ws2812fx.getModeName(i)); @@ -199,24 +222,24 @@ void configServer() { } modes[strlen(modes) - 1] = ']'; - server.sendHeader("Access-Control-Allow-Origin", "*"); - server.send(200, "application/json", modes); + request->send(200, "application/json", modes); }); - server.on("/upload", HTTP_OPTIONS, []() { // CORS preflight request - server.sendHeader("Access-Control-Allow-Origin", "*"); - server.sendHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); - server.sendHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS"); - server.send(200, "text/plain", "OK"); + server.on("/upload", HTTP_OPTIONS, [](AsyncWebServerRequest * request) { // CORS preflight request + AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", "OK"); + response->addHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); + response->addHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS"); + request->send(response); }); // receive the device info in JSON format and update the pattern data - server.on("/upload", HTTP_POST, []() { - String data = server.arg("plain"); - Serial.println(data); - - bool isParseOk = json2patterns(data); + AsyncCallbackJsonWebHandler* uploadHandler = new AsyncCallbackJsonWebHandler("/upload", + [](AsyncWebServerRequest * request, JsonVariant & json) { + if (request->method() == HTTP_POST) { + JsonObject jsonObj = json.as(); + serializeJson(jsonObj, Serial); Serial.println(); // debug + bool isParseOk = json2patterns(jsonObj); if (isParseOk && numPatterns > 0) { ws2812fx.stop(); ws2812fx.clear(); @@ -229,9 +252,11 @@ void configServer() { ws2812fx.start(); } - server.sendHeader("Access-Control-Allow-Origin", "*"); - server.send(200, "application/json", "{\"status\":200, \"message\":\"OK\"}"); + AsyncWebServerResponse *response = request->beginResponse(200, "application/json", "{\"status\":200, \"message\":\"OK\"}"); + request->send(response); + } }); + server.addHandler(uploadHandler); } #define EEPROM_MAGIC_NUMBER 0x010e0d05 @@ -252,6 +277,7 @@ void restoreFromEEPROM() { EEPROM.get(sizeof(int) * 0, magicNumber); if (magicNumber == EEPROM_MAGIC_NUMBER) { Serial.println("restoring from EEPROM"); + ws2812fx.stop(); EEPROM.get(sizeof(int) * 1, pin); if (ws2812fx.getPin() != pin) { ws2812fx.setPin(pin); @@ -262,6 +288,7 @@ void restoreFromEEPROM() { } EEPROM.get(sizeof(int) * 3, numPatterns); EEPROM.get(sizeof(int) * 4, patterns); + ws2812fx.start(); } } @@ -274,166 +301,73 @@ int modeName2Index(const char* name) { return 0; } -#if ARDUINOJSON_VERSION_MAJOR == 5 -//#pragma message("Compiling for ArduinoJson v5") -bool json2patterns(String &json) { - DynamicJsonBuffer jsonBuffer(2000); - JsonObject& deviceJson = jsonBuffer.parseObject(json); - if (deviceJson.success()) { - ws2812fx.setPin(deviceJson["dataPin"]); - ws2812fx.setLength(deviceJson["numLeds"]); - - JsonArray& patternsJson = deviceJson["patterns"]; - if (patternsJson.size() > 0 ) { - numPatterns = 0; - for (int i = 0; i < patternsJson.size(); i++) { - JsonObject& patt = patternsJson[i]; -// bool isEnabled = patt["isEnabled"]; -// if (! isEnabled) continue; // disabled patterns are not stored - - JsonArray& segmentsJson = patt["segments"]; - if (segmentsJson.size() == 0 ) continue; - - patterns[numPatterns].brightness = patt["brightness"]; - patterns[numPatterns].duration = patt["duration"]; - - patterns[numPatterns].numSegments = segmentsJson.size(); - for (int j = 0; j < segmentsJson.size(); j++) { - JsonObject& seg = segmentsJson[j]; - //seg.printTo(Serial);Serial.println(); - int start = seg["start"]; - if (start < 0 || start >= ws2812fx.getLength()) start = 0; - patterns[numPatterns].segments[j].start = start; - - int stop = seg["stop"]; - if (stop < 0 || stop >= ws2812fx.getLength()) stop = ws2812fx.getLength() - 1; - patterns[numPatterns].segments[j].stop = stop; - - if (seg["mode"].is()) { // seg["mode"] can be a mode number or a mode name - patterns[numPatterns].segments[j].mode = seg["mode"]; - } else { - patterns[numPatterns].segments[j].mode = modeName2Index(seg["mode"]); - } - - int speed = seg["speed"]; - if (speed < SPEED_MIN || speed >= SPEED_MAX) speed = 1000; - patterns[numPatterns].segments[j].speed = speed; - - patterns[numPatterns].segments[j].options = 0; - bool reverse = seg["reverse"]; - if (reverse) patterns[numPatterns].segments[j].options |= REVERSE; - - bool gamma = seg["gamma"]; - if (gamma) patterns[numPatterns].segments[j].options |= GAMMA; - - int fadeRate = seg["fadeRate"]; - if (fadeRate > 0) patterns[numPatterns].segments[j].options |= (fadeRate & 0x7) << 4; - - int size = seg["size"]; - if (size > 0) patterns[numPatterns].segments[j].options |= (size & 0x3) << 1; - - JsonArray& colors = seg["colors"]; // the web interface sends three color values - // convert colors from strings ('#ffffff') to uint32_t - patterns[numPatterns].segments[j].colors[0] = strtoul(colors[0].as() + 1, 0, 16); - patterns[numPatterns].segments[j].colors[1] = strtoul(colors[1].as() + 1, 0, 16); - patterns[numPatterns].segments[j].colors[2] = strtoul(colors[2].as() + 1, 0, 16); - } - numPatterns++; - if (numPatterns >= MAX_NUM_PATTERNS) break; - } - } else { - Serial.println(F("JSON contains no pattern data")); - return false; - } - } else { - Serial.println(F("Could not parse JSON payload")); - return false; - } - return true; -} -#endif - #if ARDUINOJSON_VERSION_MAJOR == 6 //#pragma message("Compiling for ArduinoJson v6") -bool json2patterns(String &json) { - // in ArduinoJson v6 a DynamicJsonDocument does not expand as needed - // like it did in ArduinoJson v5. So rather then try to compute the - // optimum heap size, we'll just allocated a bunch of space on the - // heap and hope it's enough. - int freeHeap = ESP.getFreeHeap(); - DynamicJsonDocument doc(freeHeap - 3096); // allocate all of the available heap except 3kB - DeserializationError error = deserializeJson(doc, json); - if (!error) { - JsonObject deviceJson = doc.as(); - ws2812fx.setPin(deviceJson["dataPin"]); - ws2812fx.setLength(deviceJson["numLeds"]); - - JsonArray patternsJson = deviceJson["patterns"]; - if (patternsJson.size() > 0 ) { - numPatterns = 0; - for (size_t i = 0; i < patternsJson.size(); i++) { - JsonObject patt = patternsJson[i]; +bool json2patterns(JsonObject deviceJson) { + ws2812fx.setPin(deviceJson["dataPin"]); + ws2812fx.setLength(deviceJson["numLeds"]); + + JsonArray patternsJson = deviceJson["patterns"]; + if (patternsJson.size() > 0 ) { + numPatterns = 0; + for (size_t i = 0; i < patternsJson.size(); i++) { + JsonObject patt = patternsJson[i]; // bool isEnabled = patt["isEnabled"]; // if (! isEnabled) continue; // disabled patterns are not stored - JsonArray segmentsJson = patt["segments"]; - if (segmentsJson.size() == 0 ) continue; + JsonArray segmentsJson = patt["segments"]; + if (segmentsJson.size() == 0 ) continue; - patterns[numPatterns].brightness = patt["brightness"]; - patterns[numPatterns].duration = patt["duration"]; + patterns[numPatterns].brightness = patt["brightness"]; + patterns[numPatterns].duration = patt["duration"]; - patterns[numPatterns].numSegments = segmentsJson.size(); - for (size_t j = 0; j < segmentsJson.size(); j++) { - JsonObject seg = segmentsJson[j]; + patterns[numPatterns].numSegments = segmentsJson.size(); + for (size_t j = 0; j < segmentsJson.size(); j++) { + JsonObject seg = segmentsJson[j]; //seg.printTo(Serial);Serial.println(); - int start = seg["start"]; - if (start < 0 || start >= ws2812fx.getLength()) start = 0; - patterns[numPatterns].segments[j].start = start; + int start = seg["start"]; + if (start < 0 || start >= ws2812fx.getLength()) start = 0; + patterns[numPatterns].segments[j].start = start; - int stop = seg["stop"]; - if (stop < 0 || stop >= ws2812fx.getLength()) stop = ws2812fx.getLength() - 1; - patterns[numPatterns].segments[j].stop = stop; + int stop = seg["stop"]; + if (stop < 0 || stop >= ws2812fx.getLength()) stop = ws2812fx.getLength() - 1; + patterns[numPatterns].segments[j].stop = stop; - if (seg["mode"].is()) { // seg["mode"] can be a mode number or a mode name - patterns[numPatterns].segments[j].mode = seg["mode"]; - } else { - patterns[numPatterns].segments[j].mode = modeName2Index(seg["mode"]); - } + if (seg["mode"].is()) { // seg["mode"] can be a mode number or a mode name + patterns[numPatterns].segments[j].mode = seg["mode"]; + } else { + patterns[numPatterns].segments[j].mode = modeName2Index(seg["mode"]); + } - int speed = seg["speed"]; - if (speed < SPEED_MIN || speed >= SPEED_MAX) speed = 1000; - patterns[numPatterns].segments[j].speed = speed; + int speed = seg["speed"]; + if (speed < SPEED_MIN || speed >= SPEED_MAX) speed = 1000; + patterns[numPatterns].segments[j].speed = speed; - patterns[numPatterns].segments[j].options = 0; - bool reverse = seg["reverse"]; - if (reverse) patterns[numPatterns].segments[j].options |= REVERSE; + patterns[numPatterns].segments[j].options = 0; + bool reverse = seg["reverse"]; + if (reverse) patterns[numPatterns].segments[j].options |= REVERSE; - bool gamma = seg["gamma"]; - if (gamma) patterns[numPatterns].segments[j].options |= GAMMA; + bool gamma = seg["gamma"]; + if (gamma) patterns[numPatterns].segments[j].options |= GAMMA; - int fadeRate = seg["fadeRate"]; - if (fadeRate > 0) patterns[numPatterns].segments[j].options |= (fadeRate & 0x7) << 4; + int fadeRate = seg["fadeRate"]; + if (fadeRate > 0) patterns[numPatterns].segments[j].options |= (fadeRate & 0x7) << 4; - int size = seg["size"]; - if (size > 0) patterns[numPatterns].segments[j].options |= (size & 0x3) << 1; + int size = seg["size"]; + if (size > 0) patterns[numPatterns].segments[j].options |= (size & 0x3) << 1; - JsonArray colors = seg["colors"]; // the web interface sends three color values - // convert colors from strings ('#ffffff') to uint32_t - patterns[numPatterns].segments[j].colors[0] = strtoul(colors[0].as() + 1, 0, 16); - patterns[numPatterns].segments[j].colors[1] = strtoul(colors[1].as() + 1, 0, 16); - patterns[numPatterns].segments[j].colors[2] = strtoul(colors[2].as() + 1, 0, 16); - } - numPatterns++; - if (numPatterns >= MAX_NUM_PATTERNS) break; + JsonArray colors = seg["colors"]; // the web interface sends three color values + // convert colors from strings ('#ffffff') to uint32_t + patterns[numPatterns].segments[j].colors[0] = strtoul(colors[0].as() + 1, 0, 16); + patterns[numPatterns].segments[j].colors[1] = strtoul(colors[1].as() + 1, 0, 16); + patterns[numPatterns].segments[j].colors[2] = strtoul(colors[2].as() + 1, 0, 16); } - } else { - Serial.println(F("JSON contains no pattern data")); - return false; + numPatterns++; + if (numPatterns >= MAX_NUM_PATTERNS) break; } } else { - Serial.print(F("Could not parse JSON payload: ")); - Serial.println(error.c_str()); + Serial.println(F("JSON contains no pattern data")); return false; } return true; diff --git a/examples/ws2812fx_segments_OTA/ws2812fx_segments_OTA.ino b/examples/ws2812fx_segments_OTA/ws2812fx_segments_OTA.ino index bff40bd..2a0c71d 100644 --- a/examples/ws2812fx_segments_OTA/ws2812fx_segments_OTA.ino +++ b/examples/ws2812fx_segments_OTA/ws2812fx_segments_OTA.ino @@ -40,7 +40,7 @@ #include #include -#define LED_PIN D1 // digital pin used to drive the LED strip +#define LED_PIN 14 // digital pin used to drive the LED strip #define LED_COUNT 30 // number of LEDs on the strip #define WIFI_SSID "xxxxxxxx" // WiFi network diff --git a/extras/WS2812FX change log.txt b/extras/WS2812FX change log.txt index 5a6fbc2..395eeb7 100644 --- a/extras/WS2812FX change log.txt +++ b/extras/WS2812FX change log.txt @@ -1,8 +1,51 @@ WS2182FX Change Log +v1.4.0 changes 03/23/2022 +------------------------- + +1) The source files were getting very big, so I split the .h and .cpp + files into three .h files and three .cpp files. The files are + still pretty big, but are now a little more managable. + +2) The Arduino's memory limitations are starting to really hinder + expanding the library's feature set, so I split the header file into + seperate files for ESP (modes_esp.h) and Arduino (modes_arduino.h). + Hopefully this will make it easer to add new features/effects for + ESP devices, and not blow up the memory footprint for Arduino devices. + +3) Updated the ws2812fx_patterns_web example sketch to use the + async web server (ESPAsyncWebServer) instead of ESP8266WebServer, + so the sketch now supports ESP8266 and ESP32. + +4) removed support for the lagacy AduinoJson v5 library in the + ws2812fx_patterns_web example sketch. You must use the + ArduinoJson v6 library. + +5) Added the tester.zsh script to verify all the example sketches + for Arduino, ESP8266 and ESP32 devices. Automated + testing??? What a concept! :) + +6) Refactored the modes for ESP devices, so the mode data is stored + in a struct, and added an effect category to the struct. This + should have been done long ago, but the tight coupling of Arduino + and ESP code made it problematic. Hopefully this will spur more + innovative GUI designs that don't just show one gigantic list + of effects. + +7) POTENTIALLY BREAKING CHANGE + Changed the timing of the CHASE, TWINKLE_FADE, RUNNING, DYNAMIC + and Fireworks effects to make them more consistent with the other + effects. A speed setting of 1000 should give reasonable results + for all effects. While this isn't strictly a breaking change, + your speed settings for these effects might need to be tweeked + to get the same animation rate you had before. + +8) Refactored the rainbow_chase effect to be more efficient. + + v1.3.6 changes xx/xx/xxxx ------------------------- +------------------------- 1) Added REVERSE option for rainbow_cycle effect and fixed direction for rainbow_cycle, theater_chase_rainbow and rain effects so they @@ -15,7 +58,7 @@ v1.3.6 changes xx/xx/xxxx v1.3.5 changes 11/18/2021 ------------------------- +------------------------- 1) When specifying a binary number, changed from "Bxxxx" notation to "0bxxxx" notation. Apparently this is the preferred notation, diff --git a/library.json b/library.json index 694b672..fa11659 100644 --- a/library.json +++ b/library.json @@ -6,7 +6,7 @@ "name": "Harm Aldick", "url": "https://github.com/kitesurfer1404/WS2812FX" }, - "version": "1.3.5", + "version": "1.4.0", "frameworks": "arduino", "platforms": "*", "repository": { diff --git a/library.properties b/library.properties index 8709c1c..39918a0 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=WS2812FX -version=1.3.5 +version=1.4.0 author=Harm Aldick maintainer=Harm Aldick sentence=WS2812 FX Library for Arduino and ESP microprocessors. diff --git a/src/WS2812FX.cpp b/src/WS2812FX.cpp index 926eff1..e8a4b89 100644 --- a/src/WS2812FX.cpp +++ b/src/WS2812FX.cpp @@ -60,9 +60,9 @@ void WS2812FX::init() { } // void WS2812FX::timer() { -// for (int j=0; j < 1000; j++) { -// uint16_t delay = (this->*_modes[_seg->mode])(); -// } +// for (int j=0; j < 1000; j++) { +// uint16_t delay = (MODE_PTR(_seg->mode))(); +// } // } bool WS2812FX::service() { @@ -78,7 +78,7 @@ bool WS2812FX::service() { if(now > _seg_rt->next_time || _triggered) { SET_FRAME; doShow = true; - uint16_t delay = (this->*_modes[_seg->mode])(); + uint16_t delay = (MODE_PTR(_seg->mode))(); _seg_rt->next_time = now + max(delay, SPEED_MIN); _seg_rt->counter_mode_call++; } @@ -373,7 +373,7 @@ uint8_t* WS2812FX::getActiveSegments(void) { const __FlashStringHelper* WS2812FX::getModeName(uint8_t m) { if(m < MODE_COUNT) { - return _names[m]; + return MODE_NAME(m); } else { return F(""); } @@ -623,1095 +623,6 @@ uint32_t* WS2812FX::intensitySums() { return intensities; } - -/* ##################################################### -# -# Mode Functions -# -##################################################### */ - -/* - * No blinking. Just plain old static light. - */ -uint16_t WS2812FX::mode_static(void) { - fill(_seg->colors[0], _seg->start, _seg_len); - SET_CYCLE; - return _seg->speed; -} - - -/* - * Blink/strobe function - * Alternate between color1 and color2 - * if(strobe == true) then create a strobe effect - */ -uint16_t WS2812FX::blink(uint32_t color1, uint32_t color2, bool strobe) { - if(_seg_rt->counter_mode_call & 1) { - uint32_t color = (IS_REVERSE) ? color1 : color2; // off - fill(color, _seg->start, _seg_len); - SET_CYCLE; - return strobe ? _seg->speed - 20 : (_seg->speed / 2); - } else { - uint32_t color = (IS_REVERSE) ? color2 : color1; // on - fill(color, _seg->start, _seg_len); - return strobe ? 20 : (_seg->speed / 2); - } -} - - -/* - * Normal blinking. 50% on/off time. - */ -uint16_t WS2812FX::mode_blink(void) { - return blink(_seg->colors[0], _seg->colors[1], false); -} - - -/* - * Classic Blink effect. Cycling through the rainbow. - */ -uint16_t WS2812FX::mode_blink_rainbow(void) { - return blink(color_wheel(_seg_rt->counter_mode_call & 0xFF), _seg->colors[1], false); -} - - -/* - * Classic Strobe effect. - */ -uint16_t WS2812FX::mode_strobe(void) { - return blink(_seg->colors[0], _seg->colors[1], true); -} - - -/* - * Classic Strobe effect. Cycling through the rainbow. - */ -uint16_t WS2812FX::mode_strobe_rainbow(void) { - return blink(color_wheel(_seg_rt->counter_mode_call & 0xFF), _seg->colors[1], true); -} - - -/* - * Color wipe function - * LEDs are turned on (color1) in sequence, then turned off (color2) in sequence. - * if (bool rev == true) then LEDs are turned off in reverse order - */ -uint16_t WS2812FX::color_wipe(uint32_t color1, uint32_t color2, bool rev) { - if(_seg_rt->counter_mode_step < _seg_len) { - uint32_t led_offset = _seg_rt->counter_mode_step; - if(IS_REVERSE) { - setPixelColor(_seg->stop - led_offset, color1); - } else { - setPixelColor(_seg->start + led_offset, color1); - } - } else { - uint32_t led_offset = _seg_rt->counter_mode_step - _seg_len; - if((IS_REVERSE && !rev) || (!IS_REVERSE && rev)) { - setPixelColor(_seg->stop - led_offset, color2); - } else { - setPixelColor(_seg->start + led_offset, color2); - } - } - - _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) % (_seg_len * 2); - - if(_seg_rt->counter_mode_step == 0) SET_CYCLE; - - return (_seg->speed / (_seg_len * 2)); -} - -/* - * Lights all LEDs one after another. - */ -uint16_t WS2812FX::mode_color_wipe(void) { - return color_wipe(_seg->colors[0], _seg->colors[1], false); -} - -uint16_t WS2812FX::mode_color_wipe_inv(void) { - return color_wipe(_seg->colors[1], _seg->colors[0], false); -} - -uint16_t WS2812FX::mode_color_wipe_rev(void) { - return color_wipe(_seg->colors[0], _seg->colors[1], true); -} - -uint16_t WS2812FX::mode_color_wipe_rev_inv(void) { - return color_wipe(_seg->colors[1], _seg->colors[0], true); -} - - -/* - * Turns all LEDs after each other to a random color. - * Then starts over with another color. - */ -uint16_t WS2812FX::mode_color_wipe_random(void) { - if(_seg_rt->counter_mode_step % _seg_len == 0) { // aux_param will store our random color wheel index - _seg_rt->aux_param = get_random_wheel_index(_seg_rt->aux_param); - } - uint32_t color = color_wheel(_seg_rt->aux_param); - return color_wipe(color, color, false) * 2; -} - - -/* - * Random color introduced alternating from start and end of strip. - */ -uint16_t WS2812FX::mode_color_sweep_random(void) { - if(_seg_rt->counter_mode_step % _seg_len == 0) { // aux_param will store our random color wheel index - _seg_rt->aux_param = get_random_wheel_index(_seg_rt->aux_param); - } - uint32_t color = color_wheel(_seg_rt->aux_param); - return color_wipe(color, color, true) * 2; -} - - -/* - * Lights all LEDs in one random color up. Then switches them - * to the next random color. - */ -uint16_t WS2812FX::mode_random_color(void) { - _seg_rt->aux_param = get_random_wheel_index(_seg_rt->aux_param); // aux_param will store our random color wheel index - uint32_t color = color_wheel(_seg_rt->aux_param); - fill(color, _seg->start, _seg_len); - SET_CYCLE; - return _seg->speed; -} - - -/* - * Lights every LED in a random color. Changes one random LED after the other - * to another random color. - */ -uint16_t WS2812FX::mode_single_dynamic(void) { - if(_seg_rt->counter_mode_call == 0) { - for(uint16_t i=_seg->start; i <= _seg->stop; i++) { - setPixelColor(i, color_wheel(random8())); - } - } - - setPixelColor(_seg->start + random16(_seg_len), color_wheel(random8())); - SET_CYCLE; - return _seg->speed; -} - - -/* - * Lights every LED in a random color. Changes all LED at the same time - * to new random colors. - */ -uint16_t WS2812FX::mode_multi_dynamic(void) { - for(uint16_t i=_seg->start; i <= _seg->stop; i++) { - setPixelColor(i, color_wheel(random8())); - } - SET_CYCLE; - return _seg->speed; -} - - -/* - * Does the "standby-breathing" of well known i-Devices. Fixed Speed. - * Use mode "fade" if you like to have something similar with a different speed. - */ -uint16_t WS2812FX::mode_breath(void) { - int lum = _seg_rt->counter_mode_step; - if(lum > 255) lum = 511 - lum; // lum = 15 -> 255 -> 15 - - uint16_t delay; - if(lum == 15) delay = 970; // 970 pause before each breath - else if(lum <= 25) delay = 38; // 19 - else if(lum <= 50) delay = 36; // 18 - else if(lum <= 75) delay = 28; // 14 - else if(lum <= 100) delay = 20; // 10 - else if(lum <= 125) delay = 14; // 7 - else if(lum <= 150) delay = 11; // 5 - else delay = 10; // 4 - - uint32_t color = color_blend(_seg->colors[1], _seg->colors[0], lum); - fill(color, _seg->start, _seg_len); - - _seg_rt->counter_mode_step += 2; - if(_seg_rt->counter_mode_step > (512-15)) { - _seg_rt->counter_mode_step = 15; - SET_CYCLE; - } - return delay; -} - - -/* - * Fades the LEDs between two colors - */ -uint16_t WS2812FX::mode_fade(void) { - int lum = _seg_rt->counter_mode_step; - if(lum > 255) lum = 511 - lum; // lum = 0 -> 255 -> 0 - - uint32_t color = color_blend(_seg->colors[1], _seg->colors[0], lum); - fill(color, _seg->start, _seg_len); - - _seg_rt->counter_mode_step += 4; - if(_seg_rt->counter_mode_step > 511) { - _seg_rt->counter_mode_step = 0; - SET_CYCLE; - } - return (_seg->speed / 128); -} - - -/* - * scan function - runs a block of pixels back and forth. - */ -uint16_t WS2812FX::scan(uint32_t color1, uint32_t color2, bool dual) { - int8_t dir = _seg_rt->aux_param ? -1 : 1; - uint8_t size = 1 << SIZE_OPTION; - - fill(color2, _seg->start, _seg_len); - - for(uint8_t i = 0; i < size; i++) { - if(IS_REVERSE || dual) { - setPixelColor(_seg->stop - _seg_rt->counter_mode_step - i, color1); - } - if(!IS_REVERSE || dual) { - setPixelColor(_seg->start + _seg_rt->counter_mode_step + i, color1); - } - } - - _seg_rt->counter_mode_step += dir; - if(_seg_rt->counter_mode_step == 0) { - _seg_rt->aux_param = 0; - SET_CYCLE; - } - if(_seg_rt->counter_mode_step >= (uint16_t)(_seg_len - size)) _seg_rt->aux_param = 1; - - return (_seg->speed / (_seg_len * 2)); -} - - -/* - * Runs a block of pixels back and forth. - */ -uint16_t WS2812FX::mode_scan(void) { - return scan(_seg->colors[0], _seg->colors[1], false); -} - - -/* - * Runs two blocks of pixels back and forth in opposite directions. - */ -uint16_t WS2812FX::mode_dual_scan(void) { - return scan(_seg->colors[0], _seg->colors[1], true); -} - - -/* - * Cycles all LEDs at once through a rainbow. - */ -uint16_t WS2812FX::mode_rainbow(void) { - uint32_t color = color_wheel(_seg_rt->counter_mode_step); - fill(color, _seg->start, _seg_len); - - _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) & 0xFF; - - if(_seg_rt->counter_mode_step == 0) SET_CYCLE; - - return (_seg->speed / 256); -} - - -/* - * Cycles a rainbow over the entire string of LEDs. - */ -uint16_t WS2812FX::mode_rainbow_cycle(void) { - for(uint16_t i=0; i < _seg_len; i++) { - uint32_t color = color_wheel(((i * 256 / _seg_len) + _seg_rt->counter_mode_step) & 0xFF); - if(IS_REVERSE) { - setPixelColor(_seg->stop - i, color); - } else { - setPixelColor(_seg->start + i, color); - } - } - - _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) & 0xFF; - - if(_seg_rt->counter_mode_step == 0) SET_CYCLE; - - return (_seg->speed / 256); -} - - -/* - * Tricolor chase function - */ -uint16_t WS2812FX::tricolor_chase(uint32_t color1, uint32_t color2, uint32_t color3) { - uint8_t sizeCnt = 1 << SIZE_OPTION; - uint8_t sizeCnt2 = sizeCnt + sizeCnt; - uint8_t sizeCnt3 = sizeCnt2 + sizeCnt; - uint16_t index = _seg_rt->counter_mode_step % sizeCnt3; - for(uint16_t i=0; i < _seg_len; i++, index++) { - index = index % sizeCnt3; - - uint32_t color = color3; - if(index < sizeCnt) color = color1; - else if(index < sizeCnt2) color = color2; - - if(IS_REVERSE) { - setPixelColor(_seg->start + i, color); - } else { - setPixelColor(_seg->stop - i, color); - } - } - - _seg_rt->counter_mode_step++; - if(_seg_rt->counter_mode_step % _seg_len == 0) SET_CYCLE; - - return (_seg->speed / _seg_len); -} - - -/* - * Tricolor chase mode - */ -uint16_t WS2812FX::mode_tricolor_chase(void) { - return tricolor_chase(_seg->colors[0], _seg->colors[1], _seg->colors[2]); -} - - -/* - * Alternating white/red/black pixels running. - */ -uint16_t WS2812FX::mode_circus_combustus(void) { - return tricolor_chase(RED, WHITE, BLACK); -} - - -/* - * Theatre-style crawling lights. - * Inspired by the Adafruit examples. - */ -uint16_t WS2812FX::mode_theater_chase(void) { - return tricolor_chase(_seg->colors[0], _seg->colors[1], _seg->colors[1]); -} - - -/* - * Theatre-style crawling lights with rainbow effect. - * Inspired by the Adafruit examples. - */ -uint16_t WS2812FX::mode_theater_chase_rainbow(void) { - _seg_rt->aux_param = (_seg_rt->aux_param + 1) & 0xFF; - uint32_t color = color_wheel(_seg_rt->aux_param); - return tricolor_chase(color, _seg->colors[1], _seg->colors[1]); -} - - -/* - * Running lights effect with smooth sine transition. - */ -uint16_t WS2812FX::mode_running_lights(void) { - uint8_t size = 1 << SIZE_OPTION; - uint8_t sineIncr = max(1, (256 / _seg_len) * size); - for(uint16_t i=0; i < _seg_len; i++) { - int lum = (int)sine8(((i + _seg_rt->counter_mode_step) * sineIncr)); - uint32_t color = color_blend(_seg->colors[0], _seg->colors[1], lum); - if(IS_REVERSE) { - setPixelColor(_seg->start + i, color); - } else { - setPixelColor(_seg->stop - i, color); - } - } - _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) % 256; - if(_seg_rt->counter_mode_step == 0) SET_CYCLE; - return (_seg->speed / _seg_len); -} - - -/* - * twinkle function - */ -uint16_t WS2812FX::twinkle(uint32_t color1, uint32_t color2) { - if(_seg_rt->counter_mode_step == 0) { - fill(color2, _seg->start, _seg_len); - uint16_t min_leds = (_seg_len / 4) + 1; // make sure, at least one LED is on - _seg_rt->counter_mode_step = random(min_leds, min_leds * 2); - SET_CYCLE; - } - - setPixelColor(_seg->start + random16(_seg_len), color1); - - _seg_rt->counter_mode_step--; - return (_seg->speed / _seg_len); -} - -/* - * Blink several LEDs on, reset, repeat. - * Inspired by www.tweaking4all.com/hardware/arduino/arduino-led-strip-effects/ - */ -uint16_t WS2812FX::mode_twinkle(void) { - return twinkle(_seg->colors[0], _seg->colors[1]); -} - -/* - * Blink several LEDs in random colors on, reset, repeat. - * Inspired by www.tweaking4all.com/hardware/arduino/arduino-led-strip-effects/ - */ -uint16_t WS2812FX::mode_twinkle_random(void) { - return twinkle(color_wheel(random8()), _seg->colors[1]); -} - - -/* - * fade out functions - */ -void WS2812FX::fade_out() { - return fade_out(_seg->colors[1]); -} - -void WS2812FX::fade_out(uint32_t targetColor) { - static const uint8_t rateMapH[] = {0, 1, 1, 1, 2, 3, 4, 6}; - static const uint8_t rateMapL[] = {0, 2, 3, 8, 8, 8, 8, 8}; - - uint8_t rate = FADE_RATE; - uint8_t rateH = rateMapH[rate]; - uint8_t rateL = rateMapL[rate]; - - uint32_t color = targetColor; - int w2 = (color >> 24) & 0xff; - int r2 = (color >> 16) & 0xff; - int g2 = (color >> 8) & 0xff; - int b2 = color & 0xff; - - for(uint16_t i=_seg->start; i <= _seg->stop; i++) { - color = getPixelColor(i); // current color - if(rate == 0) { // old fade-to-black algorithm - setPixelColor(i, (color >> 1) & 0x7F7F7F7F); - } else { // new fade-to-color algorithm - int w1 = (color >> 24) & 0xff; - int r1 = (color >> 16) & 0xff; - int g1 = (color >> 8) & 0xff; - int b1 = color & 0xff; - - // calculate the color differences between the current and target colors - int wdelta = w2 - w1; - int rdelta = r2 - r1; - int gdelta = g2 - g1; - int bdelta = b2 - b1; - - // if the current and target colors are almost the same, jump right to the target - // color, otherwise calculate an intermediate color. (fixes rounding issues) - wdelta = abs(wdelta) < 3 ? wdelta : (wdelta >> rateH) + (wdelta >> rateL); - rdelta = abs(rdelta) < 3 ? rdelta : (rdelta >> rateH) + (rdelta >> rateL); - gdelta = abs(gdelta) < 3 ? gdelta : (gdelta >> rateH) + (gdelta >> rateL); - bdelta = abs(bdelta) < 3 ? bdelta : (bdelta >> rateH) + (bdelta >> rateL); - - setPixelColor(i, r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta); - } - } -} - - -/* - * color blend function - */ -uint32_t WS2812FX::color_blend(uint32_t color1, uint32_t color2, uint8_t blendAmt) { - uint32_t blendedColor; - blend((uint8_t*)&blendedColor, (uint8_t*)&color1, (uint8_t*)&color2, sizeof(uint32_t), blendAmt); - return blendedColor; -} - -uint8_t* WS2812FX::blend(uint8_t *dest, uint8_t *src1, uint8_t *src2, uint16_t cnt, uint8_t blendAmt) { - if(blendAmt == 0) { - memmove(dest, src1, cnt); - } else if(blendAmt == 255) { - memmove(dest, src2, cnt); - } else { - for(uint16_t i=0; istart + random16(_seg_len - size + 1); - fill(color, index, size); - SET_CYCLE; - } - return (_seg->speed / 8); -} - - -/* - * Blink several LEDs on, fading out. - */ -uint16_t WS2812FX::mode_twinkle_fade(void) { - return twinkle_fade(_seg->colors[0]); -} - - -/* - * Blink several LEDs in random colors on, fading out. - */ -uint16_t WS2812FX::mode_twinkle_fade_random(void) { - return twinkle_fade(color_wheel(random8())); -} - -/* - * Sparkle function - * color1 = background color - * color2 = sparkle color - */ -uint16_t WS2812FX::sparkle(uint32_t color1, uint32_t color2) { - if(_seg_rt->counter_mode_step == 0) { - fill(color1, _seg->start, _seg_len); - } - - uint8_t size = 1 << SIZE_OPTION; - fill(color1, _seg->start + _seg_rt->aux_param3, size); - - _seg_rt->aux_param3 = random16(_seg_len - size + 1); // aux_param3 stores the random led index - fill(color2, _seg->start + _seg_rt->aux_param3, size); - - SET_CYCLE; - return (_seg->speed / 32); -} - - -/* - * Blinks one LED at a time. - * Inspired by www.tweaking4all.com/hardware/arduino/arduino-led-strip-effects/ - */ -uint16_t WS2812FX::mode_sparkle(void) { - return sparkle(_seg->colors[1], _seg->colors[0]); -} - - -/* - * Lights all LEDs in the color. Flashes white pixels randomly. - * Inspired by www.tweaking4all.com/hardware/arduino/arduino-led-strip-effects/ - */ -uint16_t WS2812FX::mode_flash_sparkle(void) { - return sparkle(_seg->colors[0], WHITE); -} - - -/* - * Like flash sparkle. With more flash. - * Inspired by www.tweaking4all.com/hardware/arduino/arduino-led-strip-effects/ - */ -uint16_t WS2812FX::mode_hyper_sparkle(void) { - fill(_seg->colors[0], _seg->start, _seg_len); - - uint8_t size = 1 << SIZE_OPTION; - for(uint8_t i=0; i<8; i++) { - fill(WHITE, _seg->start + random16(_seg_len - size + 1), size); - } - - SET_CYCLE; - return (_seg->speed / 32); -} - - -/* - * Strobe effect with different strobe count and pause, controlled by speed. - */ -uint16_t WS2812FX::mode_multi_strobe(void) { - fill(_seg->colors[1], _seg->start, _seg_len); - - uint16_t delay = 200 + ((9 - (_seg->speed % 10)) * 100); - uint16_t count = 2 * ((_seg->speed / 100) + 1); - if(_seg_rt->counter_mode_step < count) { - if((_seg_rt->counter_mode_step & 1) == 0) { - fill(_seg->colors[0], _seg->start, _seg_len); - delay = 20; - } else { - delay = 50; - } - } - - _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) % (count + 1); - if(_seg_rt->counter_mode_step == 0) SET_CYCLE; - return delay; -} - - -/* - * color chase function. - * color1 = background color - * color2 and color3 = colors of two adjacent leds - */ -uint16_t WS2812FX::chase(uint32_t color1, uint32_t color2, uint32_t color3) { - uint8_t size = 1 << SIZE_OPTION; - for(uint8_t i=0; icounter_mode_step + i) % _seg_len; - uint16_t b = (a + size) % _seg_len; - uint16_t c = (b + size) % _seg_len; - if(IS_REVERSE) { - setPixelColor(_seg->stop - a, color1); - setPixelColor(_seg->stop - b, color2); - setPixelColor(_seg->stop - c, color3); - } else { - setPixelColor(_seg->start + a, color1); - setPixelColor(_seg->start + b, color2); - setPixelColor(_seg->start + c, color3); - } - } - - if(_seg_rt->counter_mode_step + (size * 3) == _seg_len) SET_CYCLE; - - _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) % _seg_len; - return (_seg->speed / _seg_len); -} - - -/* - * Bicolor chase mode - */ -uint16_t WS2812FX::mode_bicolor_chase(void) { - return chase(_seg->colors[0], _seg->colors[1], _seg->colors[2]); -} - - -/* - * White running on _color. - */ -uint16_t WS2812FX::mode_chase_color(void) { - return chase(_seg->colors[0], WHITE, WHITE); -} - - -/* - * Black running on _color. - */ -uint16_t WS2812FX::mode_chase_blackout(void) { - return chase(_seg->colors[0], BLACK, BLACK); -} - - -/* - * _color running on white. - */ -uint16_t WS2812FX::mode_chase_white(void) { - return chase(WHITE, _seg->colors[0], _seg->colors[0]); -} - - -/* - * White running followed by random color. - */ -uint16_t WS2812FX::mode_chase_random(void) { - if(_seg_rt->counter_mode_step == 0) { - _seg_rt->aux_param = get_random_wheel_index(_seg_rt->aux_param); - } - return chase(color_wheel(_seg_rt->aux_param), WHITE, WHITE); -} - - -/* - * Rainbow running on white. - */ -uint16_t WS2812FX::mode_chase_rainbow_white(void) { - uint16_t n = _seg_rt->counter_mode_step; - uint16_t m = (_seg_rt->counter_mode_step + 1) % _seg_len; - uint32_t color2 = color_wheel(((n * 256 / _seg_len) + (_seg_rt->counter_mode_call & 0xFF)) & 0xFF); - uint32_t color3 = color_wheel(((m * 256 / _seg_len) + (_seg_rt->counter_mode_call & 0xFF)) & 0xFF); - - return chase(WHITE, color2, color3); -} - - -/* - * White running on rainbow. - */ -uint16_t WS2812FX::mode_chase_rainbow(void) { - uint8_t color_sep = 256 / _seg_len; - uint8_t color_index = _seg_rt->counter_mode_call & 0xFF; - uint32_t color = color_wheel(((_seg_rt->counter_mode_step * color_sep) + color_index) & 0xFF); - - return chase(color, WHITE, WHITE); -} - - -/* - * Black running on rainbow. - */ -uint16_t WS2812FX::mode_chase_blackout_rainbow(void) { - uint8_t color_sep = 256 / _seg_len; - uint8_t color_index = _seg_rt->counter_mode_call & 0xFF; - uint32_t color = color_wheel(((_seg_rt->counter_mode_step * color_sep) + color_index) & 0xFF); - - return chase(color, BLACK, BLACK); -} - -/* - * running white flashes function. - * color1 = background color - * color2 = flash color - */ -uint16_t WS2812FX::chase_flash(uint32_t color1, uint32_t color2) { - const static uint8_t flash_count = 4; - uint8_t flash_step = _seg_rt->counter_mode_call % ((flash_count * 2) + 1); - - if(flash_step < (flash_count * 2)) { - uint32_t color = (flash_step % 2 == 0) ? color2 : color1; - uint16_t n = _seg_rt->counter_mode_step; - uint16_t m = (_seg_rt->counter_mode_step + 1) % _seg_len; - if(IS_REVERSE) { - setPixelColor(_seg->stop - n, color); - setPixelColor(_seg->stop - m, color); - } else { - setPixelColor(_seg->start + n, color); - setPixelColor(_seg->start + m, color); - } - return 30; - } else { - _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) % _seg_len; - if(_seg_rt->counter_mode_step == 0) { - // update aux_param so mode_chase_flash_random() will select the next color - _seg_rt->aux_param = get_random_wheel_index(_seg_rt->aux_param); - SET_CYCLE; - } - } - return (_seg->speed / _seg_len); -} - -/* - * White flashes running on _color. - */ -uint16_t WS2812FX::mode_chase_flash(void) { - return chase_flash(_seg->colors[0], WHITE); -} - - -/* - * White flashes running, followed by random color. - */ -uint16_t WS2812FX::mode_chase_flash_random(void) { - return chase_flash(color_wheel(_seg_rt->aux_param), WHITE); -} - - -/* - * Alternating pixels running function. - */ -uint16_t WS2812FX::running(uint32_t color1, uint32_t color2) { - uint8_t size = 2 << SIZE_OPTION; - uint32_t color = (_seg_rt->counter_mode_step & size) ? color1 : color2; - - if(IS_REVERSE) { - copyPixels(_seg->start, _seg->start + 1, _seg_len - 1); - setPixelColor(_seg->stop, color); - } else { - copyPixels(_seg->start + 1, _seg->start, _seg_len - 1); - setPixelColor(_seg->start, color); - } - - _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) % _seg_len; - if(_seg_rt->counter_mode_step == 0) SET_CYCLE; - return (_seg->speed / _seg_len); -} - - -/* - * Alternating color/white pixels running. - */ -uint16_t WS2812FX::mode_running_color(void) { - return running(_seg->colors[0], _seg->colors[1]); -} - - -/* - * Alternating red/blue pixels running. - */ -uint16_t WS2812FX::mode_running_red_blue(void) { - return running(RED, BLUE); -} - - -/* - * Alternating red/green pixels running. - */ -uint16_t WS2812FX::mode_merry_christmas(void) { - return running(RED, GREEN); -} - -/* - * Alternating orange/purple pixels running. - */ -uint16_t WS2812FX::mode_halloween(void) { - return running(PURPLE, ORANGE); -} - - -/* - * Random colored pixels running. - */ -uint16_t WS2812FX::mode_running_random(void) { - uint8_t size = 2 << SIZE_OPTION; - if((_seg_rt->counter_mode_step) % size == 0) { - _seg_rt->aux_param = get_random_wheel_index(_seg_rt->aux_param); - } - - uint32_t color = color_wheel(_seg_rt->aux_param); - - return running(color, color); -} - - -/* - * K.I.T.T. - */ -uint16_t WS2812FX::mode_larson_scanner(void) { - fade_out(); - - if(_seg_rt->counter_mode_step < _seg_len) { - if(IS_REVERSE) { - setPixelColor(_seg->stop - _seg_rt->counter_mode_step, _seg->colors[0]); - } else { - setPixelColor(_seg->start + _seg_rt->counter_mode_step, _seg->colors[0]); - } - } else { - uint16_t index = (_seg_len * 2) - _seg_rt->counter_mode_step - 2; - if(IS_REVERSE) { - setPixelColor(_seg->stop - index, _seg->colors[0]); - } else { - setPixelColor(_seg->start + index, _seg->colors[0]); - } - } - - _seg_rt->counter_mode_step++; - if(_seg_rt->counter_mode_step >= (uint16_t)((_seg_len * 2) - 2)) { - _seg_rt->counter_mode_step = 0; - SET_CYCLE; - } - - return (_seg->speed / (_seg_len * 2)); -} - - -/* - * Firing comets from one end. - */ -uint16_t WS2812FX::mode_comet(void) { - fade_out(); - - if(IS_REVERSE) { - setPixelColor(_seg->stop - _seg_rt->counter_mode_step, _seg->colors[0]); - } else { - setPixelColor(_seg->start + _seg_rt->counter_mode_step, _seg->colors[0]); - } - - _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) % _seg_len; - if(_seg_rt->counter_mode_step == 0) SET_CYCLE; - - return (_seg->speed / _seg_len); -} - - -/* - * Fireworks function. - */ -uint16_t WS2812FX::fireworks(uint32_t color) { - fade_out(); - -// for better performance, manipulate the Adafruit_NeoPixels pixels[] array directly - uint8_t *pixels = getPixels(); - uint8_t bytesPerPixel = getNumBytesPerPixel(); // 3=RGB, 4=RGBW - uint16_t startPixel = _seg->start * bytesPerPixel + bytesPerPixel; - uint16_t stopPixel = _seg->stop * bytesPerPixel; - for(uint16_t i=startPixel; i > 2) + - pixels[i] + - (pixels[i + bytesPerPixel] >> 2); - pixels[i] = tmpPixel > 255 ? 255 : tmpPixel; - } - - uint8_t size = 2 << SIZE_OPTION; - if(!_triggered) { - for(uint16_t i=0; istart + random16(_seg_len - size + 1); - fill(color, index, size); - SET_CYCLE; - } - } - } else { - for(uint16_t i=0; istart + random16(_seg_len - size + 1); - fill(color, index, size); - SET_CYCLE; - } - } - - return (_seg->speed / _seg_len); -} - -/* - * Firework sparks. - */ -uint16_t WS2812FX::mode_fireworks(void) { - uint32_t color = BLACK; - do { // randomly choose a non-BLACK color from the colors array - color = _seg->colors[random8(MAX_NUM_COLORS)]; - } while (color == BLACK); - return fireworks(color); -} - -/* - * Random colored firework sparks. - */ -uint16_t WS2812FX::mode_fireworks_random(void) { - return fireworks(color_wheel(random8())); -} - - -/* - * Fire flicker function - */ -uint16_t WS2812FX::fire_flicker(int rev_intensity) { - byte w = (_seg->colors[0] >> 24) & 0xFF; - byte r = (_seg->colors[0] >> 16) & 0xFF; - byte g = (_seg->colors[0] >> 8) & 0xFF; - byte b = (_seg->colors[0] & 0xFF); - byte lum = max(w, max(r, max(g, b))) / rev_intensity; - for(uint16_t i=_seg->start; i <= _seg->stop; i++) { - int flicker = random8(lum); - setPixelColor(i, max(r - flicker, 0), max(g - flicker, 0), max(b - flicker, 0), max(w - flicker, 0)); - } - - SET_CYCLE; - return (_seg->speed / _seg_len); -} - -/* - * Random flickering. - */ -uint16_t WS2812FX::mode_fire_flicker(void) { - return fire_flicker(3); -} - -/* -* Random flickering, less intensity. -*/ -uint16_t WS2812FX::mode_fire_flicker_soft(void) { - return fire_flicker(6); -} - -/* -* Random flickering, more intensity. -*/ -uint16_t WS2812FX::mode_fire_flicker_intense(void) { - return fire_flicker(1); -} - -// An adaptation of Mark Kriegsman's FastLED twinkeFOX effect -// https://gist.github.com/kriegsman/756ea6dcae8e30845b5a -uint16_t WS2812FX::mode_twinkleFOX(void) { - uint16_t mySeed = 0; // reset the random number generator seed - - // Get and translate the segment's size option - uint8_t size = 1 << ((_seg->options >> 1) & 0x03); // 1,2,4,8 - - // Get the segment's colors array values - uint32_t color0 = _seg->colors[0]; - uint32_t color1 = _seg->colors[1]; - uint32_t color2 = _seg->colors[2]; - uint32_t blendedColor; - - for (uint16_t i = _seg->start; i <= _seg->stop; i+=size) { - // Use Mark Kriegsman's clever idea of using pseudo-random numbers to determine - // each LED's initial and increment blend values - mySeed = (mySeed * 2053) + 13849; // a random, but deterministic, number - uint16_t initValue = (mySeed + (mySeed >> 8)) & 0xff; // the LED's initial blend index (0-255) - mySeed = (mySeed * 2053) + 13849; // another random, but deterministic, number - uint16_t incrValue = (((mySeed + (mySeed >> 8)) & 0x07) + 1) * 2; // blend index increment (2,4,6,8,10,12,14,16) - - // We're going to use a sine function to blend colors, instead of Mark's triangle - // function, simply because a sine lookup table is already built into the - // Adafruit_NeoPixel lib. Yes, I'm lazy. - // Use the counter_mode_call var as a clock "tick" counter and calc the blend index - uint8_t blendIndex = (initValue + (_seg_rt->counter_mode_call * incrValue)) & 0xff; // 0-255 - // Index into the built-in Adafruit_NeoPixel sine table to lookup the blend amount - uint8_t blendAmt = Adafruit_NeoPixel::sine8(blendIndex); // 0-255 - - // If colors[0] is BLACK, blend random colors - if(color0 == BLACK) { - blendedColor = color_blend(color_wheel(initValue), color1, blendAmt); - // If colors[2] isn't BLACK, choose to blend colors[0]/colors[1] or colors[1]/colors[2] - // (which color pair to blend is picked randomly) - } else if((color2 != BLACK) && (initValue < 128) == 0) { - blendedColor = color_blend(color2, color1, blendAmt); - // Otherwise always blend colors[0]/colors[1] - } else { - blendedColor = color_blend(color0, color1, blendAmt); - } - - // Assign the new color to the number of LEDs specified by the SIZE option - for(uint8_t j=0; jstop) { - setPixelColor(i + j, blendedColor); - } - } - } - setCycle(); - return _seg->speed / 32; -} - -// A combination of the Fireworks effect and the running effect -// to create an effect that looks like rain. -uint16_t WS2812FX::mode_rain(void) { - // randomly choose colors[0] or colors[2] - uint32_t rainColor = (random8() & 1) == 0 ? _seg->colors[0] : _seg->colors[2]; - // if colors[0] == colors[1], choose a random color - if(_seg->colors[0] == _seg->colors[1]) rainColor = color_wheel(random8()); - - // run the fireworks effect to create a "raindrop" - fireworks(rainColor); - - // shift everything two pixels - if(IS_REVERSE) { - copyPixels(_seg->start, _seg->start + 2, _seg_len - 2); - } else { - copyPixels(_seg->start + 2, _seg->start, _seg_len - 2); - } - - return (_seg->speed / 16); -} - -/* - * Custom modes - */ -uint16_t WS2812FX::mode_custom_0() { - return customModes[0](); -} -uint16_t WS2812FX::mode_custom_1() { - return customModes[1](); -} -uint16_t WS2812FX::mode_custom_2() { - return customModes[2](); -} -uint16_t WS2812FX::mode_custom_3() { - return customModes[3](); -} -uint16_t WS2812FX::mode_custom_4() { - return customModes[4](); -} -uint16_t WS2812FX::mode_custom_5() { - return customModes[5](); -} -uint16_t WS2812FX::mode_custom_6() { - return customModes[6](); -} -uint16_t WS2812FX::mode_custom_7() { - return customModes[7](); -} - /* * Custom mode helpers */ @@ -1726,7 +637,7 @@ uint8_t WS2812FX::setCustomMode(const __FlashStringHelper* name, uint16_t (*p)() uint8_t WS2812FX::setCustomMode(uint8_t index, const __FlashStringHelper* name, uint16_t (*p)()) { if((uint8_t)(FX_MODE_CUSTOM_0 + index) < MODE_COUNT) { - _names[FX_MODE_CUSTOM_0 + index] = name; // store the custom mode name + MODE_NAME(FX_MODE_CUSTOM_0 + index) = name; customModes[index] = p; // store the custom mode return (FX_MODE_CUSTOM_0 + index); diff --git a/src/WS2812FX.h b/src/WS2812FX.h index 5bb010d..137d03d 100644 --- a/src/WS2812FX.h +++ b/src/WS2812FX.h @@ -86,7 +86,6 @@ #define DIM(c) (uint32_t)((c >> 2) & 0x3f3f3f3f) // color at 25% intensity #define DARK(c) (uint32_t)((c >> 4) & 0x0f0f0f0f) // color at 6% intensity - // segment options // bit 7: reverse animation // bits 4-6: fade rate (0-7) @@ -121,212 +120,6 @@ #define CLR_CYCLE (_seg_rt->aux_param2 &= ~CYCLE) #define CLR_FRAME_CYCLE (_seg_rt->aux_param2 &= ~(FRAME | CYCLE)) -#define MODE_COUNT (sizeof(_names)/sizeof(_names[0])) - -#define FX_MODE_STATIC 0 -#define FX_MODE_BLINK 1 -#define FX_MODE_BREATH 2 -#define FX_MODE_COLOR_WIPE 3 -#define FX_MODE_COLOR_WIPE_INV 4 -#define FX_MODE_COLOR_WIPE_REV 5 -#define FX_MODE_COLOR_WIPE_REV_INV 6 -#define FX_MODE_COLOR_WIPE_RANDOM 7 -#define FX_MODE_RANDOM_COLOR 8 -#define FX_MODE_SINGLE_DYNAMIC 9 -#define FX_MODE_MULTI_DYNAMIC 10 -#define FX_MODE_RAINBOW 11 -#define FX_MODE_RAINBOW_CYCLE 12 -#define FX_MODE_SCAN 13 -#define FX_MODE_DUAL_SCAN 14 -#define FX_MODE_FADE 15 -#define FX_MODE_THEATER_CHASE 16 -#define FX_MODE_THEATER_CHASE_RAINBOW 17 -#define FX_MODE_RUNNING_LIGHTS 18 -#define FX_MODE_TWINKLE 19 -#define FX_MODE_TWINKLE_RANDOM 20 -#define FX_MODE_TWINKLE_FADE 21 -#define FX_MODE_TWINKLE_FADE_RANDOM 22 -#define FX_MODE_SPARKLE 23 -#define FX_MODE_FLASH_SPARKLE 24 -#define FX_MODE_HYPER_SPARKLE 25 -#define FX_MODE_STROBE 26 -#define FX_MODE_STROBE_RAINBOW 27 -#define FX_MODE_MULTI_STROBE 28 -#define FX_MODE_BLINK_RAINBOW 29 -#define FX_MODE_CHASE_WHITE 30 -#define FX_MODE_CHASE_COLOR 31 -#define FX_MODE_CHASE_RANDOM 32 -#define FX_MODE_CHASE_RAINBOW 33 -#define FX_MODE_CHASE_FLASH 34 -#define FX_MODE_CHASE_FLASH_RANDOM 35 -#define FX_MODE_CHASE_RAINBOW_WHITE 36 -#define FX_MODE_CHASE_BLACKOUT 37 -#define FX_MODE_CHASE_BLACKOUT_RAINBOW 38 -#define FX_MODE_COLOR_SWEEP_RANDOM 39 -#define FX_MODE_RUNNING_COLOR 40 -#define FX_MODE_RUNNING_RED_BLUE 41 -#define FX_MODE_RUNNING_RANDOM 42 -#define FX_MODE_LARSON_SCANNER 43 -#define FX_MODE_COMET 44 -#define FX_MODE_FIREWORKS 45 -#define FX_MODE_FIREWORKS_RANDOM 46 -#define FX_MODE_MERRY_CHRISTMAS 47 -#define FX_MODE_FIRE_FLICKER 48 -#define FX_MODE_FIRE_FLICKER_SOFT 49 -#define FX_MODE_FIRE_FLICKER_INTENSE 50 -#define FX_MODE_CIRCUS_COMBUSTUS 51 -#define FX_MODE_HALLOWEEN 52 -#define FX_MODE_BICOLOR_CHASE 53 -#define FX_MODE_TRICOLOR_CHASE 54 -#define FX_MODE_TWINKLEFOX 55 -#define FX_MODE_RAIN 56 -#define FX_MODE_CUSTOM 57 // keep this for backward compatiblity -#define FX_MODE_CUSTOM_0 57 // custom modes need to go at the end -#define FX_MODE_CUSTOM_1 58 -#define FX_MODE_CUSTOM_2 59 -#define FX_MODE_CUSTOM_3 60 -#define FX_MODE_CUSTOM_4 61 -#define FX_MODE_CUSTOM_5 62 -#define FX_MODE_CUSTOM_6 63 -#define FX_MODE_CUSTOM_7 64 - -// create GLOBAL names to allow WS2812FX to compile with sketches and other libs -// that store strings in PROGMEM (get rid of the "section type conflict with __c" -// errors once and for all. Amen.) -const char name_0[] PROGMEM = "Static"; -const char name_1[] PROGMEM = "Blink"; -const char name_2[] PROGMEM = "Breath"; -const char name_3[] PROGMEM = "Color Wipe"; -const char name_4[] PROGMEM = "Color Wipe Inverse"; -const char name_5[] PROGMEM = "Color Wipe Reverse"; -const char name_6[] PROGMEM = "Color Wipe Reverse Inverse"; -const char name_7[] PROGMEM = "Color Wipe Random"; -const char name_8[] PROGMEM = "Random Color"; -const char name_9[] PROGMEM = "Single Dynamic"; -const char name_10[] PROGMEM = "Multi Dynamic"; -const char name_11[] PROGMEM = "Rainbow"; -const char name_12[] PROGMEM = "Rainbow Cycle"; -const char name_13[] PROGMEM = "Scan"; -const char name_14[] PROGMEM = "Dual Scan"; -const char name_15[] PROGMEM = "Fade"; -const char name_16[] PROGMEM = "Theater Chase"; -const char name_17[] PROGMEM = "Theater Chase Rainbow"; -const char name_18[] PROGMEM = "Running Lights"; -const char name_19[] PROGMEM = "Twinkle"; -const char name_20[] PROGMEM = "Twinkle Random"; -const char name_21[] PROGMEM = "Twinkle Fade"; -const char name_22[] PROGMEM = "Twinkle Fade Random"; -const char name_23[] PROGMEM = "Sparkle"; -const char name_24[] PROGMEM = "Flash Sparkle"; -const char name_25[] PROGMEM = "Hyper Sparkle"; -const char name_26[] PROGMEM = "Strobe"; -const char name_27[] PROGMEM = "Strobe Rainbow"; -const char name_28[] PROGMEM = "Multi Strobe"; -const char name_29[] PROGMEM = "Blink Rainbow"; -const char name_30[] PROGMEM = "Chase White"; -const char name_31[] PROGMEM = "Chase Color"; -const char name_32[] PROGMEM = "Chase Random"; -const char name_33[] PROGMEM = "Chase Rainbow"; -const char name_34[] PROGMEM = "Chase Flash"; -const char name_35[] PROGMEM = "Chase Flash Random"; -const char name_36[] PROGMEM = "Chase Rainbow White"; -const char name_37[] PROGMEM = "Chase Blackout"; -const char name_38[] PROGMEM = "Chase Blackout Rainbow"; -const char name_39[] PROGMEM = "Color Sweep Random"; -const char name_40[] PROGMEM = "Running Color"; -const char name_41[] PROGMEM = "Running Red Blue"; -const char name_42[] PROGMEM = "Running Random"; -const char name_43[] PROGMEM = "Larson Scanner"; -const char name_44[] PROGMEM = "Comet"; -const char name_45[] PROGMEM = "Fireworks"; -const char name_46[] PROGMEM = "Fireworks Random"; -const char name_47[] PROGMEM = "Merry Christmas"; -const char name_48[] PROGMEM = "Fire Flicker"; -const char name_49[] PROGMEM = "Fire Flicker (soft)"; -const char name_50[] PROGMEM = "Fire Flicker (intense)"; -const char name_51[] PROGMEM = "Circus Combustus"; -const char name_52[] PROGMEM = "Halloween"; -const char name_53[] PROGMEM = "Bicolor Chase"; -const char name_54[] PROGMEM = "Tricolor Chase"; -const char name_55[] PROGMEM = "TwinkleFOX"; -const char name_56[] PROGMEM = "Rain"; -const char name_57[] PROGMEM = "Custom 0"; // custom modes need to go at the end -const char name_58[] PROGMEM = "Custom 1"; -const char name_59[] PROGMEM = "Custom 2"; -const char name_60[] PROGMEM = "Custom 3"; -const char name_61[] PROGMEM = "Custom 4"; -const char name_62[] PROGMEM = "Custom 5"; -const char name_63[] PROGMEM = "Custom 6"; -const char name_64[] PROGMEM = "Custom 7"; - -static const __FlashStringHelper* _names[] = { - FSH(name_0), - FSH(name_1), - FSH(name_2), - FSH(name_3), - FSH(name_4), - FSH(name_5), - FSH(name_6), - FSH(name_7), - FSH(name_8), - FSH(name_9), - FSH(name_10), - FSH(name_11), - FSH(name_12), - FSH(name_13), - FSH(name_14), - FSH(name_15), - FSH(name_16), - FSH(name_17), - FSH(name_18), - FSH(name_19), - FSH(name_20), - FSH(name_21), - FSH(name_22), - FSH(name_23), - FSH(name_24), - FSH(name_25), - FSH(name_26), - FSH(name_27), - FSH(name_28), - FSH(name_29), - FSH(name_30), - FSH(name_31), - FSH(name_32), - FSH(name_33), - FSH(name_34), - FSH(name_35), - FSH(name_36), - FSH(name_37), - FSH(name_38), - FSH(name_39), - FSH(name_40), - FSH(name_41), - FSH(name_42), - FSH(name_43), - FSH(name_44), - FSH(name_45), - FSH(name_46), - FSH(name_47), - FSH(name_48), - FSH(name_49), - FSH(name_50), - FSH(name_51), - FSH(name_52), - FSH(name_53), - FSH(name_54), - FSH(name_55), - FSH(name_56), - FSH(name_57), - FSH(name_58), - FSH(name_59), - FSH(name_60), - FSH(name_61), - FSH(name_62), - FSH(name_63), - FSH(name_64) -}; - class WS2812FX : public Adafruit_NeoPixel { public: @@ -576,6 +369,10 @@ class WS2812FX : public Adafruit_NeoPixel { mode_tricolor_chase(void), mode_twinkleFOX(void), mode_rain(void), + mode_block_dissolve(void), + mode_icu(void), + mode_dual_larson(void), + mode_random_wipe_bright(void), mode_custom_0(void), mode_custom_1(void), mode_custom_2(void), @@ -682,73 +479,10 @@ class WS2812FXT { bool transitionDirection = true; }; -// define static array of member function pointers. -// function pointers MUST be in the same order as the corresponding name in the _name array. -__attribute__ ((unused)) static WS2812FX::mode_ptr _modes[MODE_COUNT] = { - &WS2812FX::mode_static, - &WS2812FX::mode_blink, - &WS2812FX::mode_breath, - &WS2812FX::mode_color_wipe, - &WS2812FX::mode_color_wipe_inv, - &WS2812FX::mode_color_wipe_rev, - &WS2812FX::mode_color_wipe_rev_inv, - &WS2812FX::mode_color_wipe_random, - &WS2812FX::mode_random_color, - &WS2812FX::mode_single_dynamic, - &WS2812FX::mode_multi_dynamic, - &WS2812FX::mode_rainbow, - &WS2812FX::mode_rainbow_cycle, - &WS2812FX::mode_scan, - &WS2812FX::mode_dual_scan, - &WS2812FX::mode_fade, - &WS2812FX::mode_theater_chase, - &WS2812FX::mode_theater_chase_rainbow, - &WS2812FX::mode_running_lights, - &WS2812FX::mode_twinkle, - &WS2812FX::mode_twinkle_random, - &WS2812FX::mode_twinkle_fade, - &WS2812FX::mode_twinkle_fade_random, - &WS2812FX::mode_sparkle, - &WS2812FX::mode_flash_sparkle, - &WS2812FX::mode_hyper_sparkle, - &WS2812FX::mode_strobe, - &WS2812FX::mode_strobe_rainbow, - &WS2812FX::mode_multi_strobe, - &WS2812FX::mode_blink_rainbow, - &WS2812FX::mode_chase_white, - &WS2812FX::mode_chase_color, - &WS2812FX::mode_chase_random, - &WS2812FX::mode_chase_rainbow, - &WS2812FX::mode_chase_flash, - &WS2812FX::mode_chase_flash_random, - &WS2812FX::mode_chase_rainbow_white, - &WS2812FX::mode_chase_blackout, - &WS2812FX::mode_chase_blackout_rainbow, - &WS2812FX::mode_color_sweep_random, - &WS2812FX::mode_running_color, - &WS2812FX::mode_running_red_blue, - &WS2812FX::mode_running_random, - &WS2812FX::mode_larson_scanner, - &WS2812FX::mode_comet, - &WS2812FX::mode_fireworks, - &WS2812FX::mode_fireworks_random, - &WS2812FX::mode_merry_christmas, - &WS2812FX::mode_fire_flicker, - &WS2812FX::mode_fire_flicker_soft, - &WS2812FX::mode_fire_flicker_intense, - &WS2812FX::mode_circus_combustus, - &WS2812FX::mode_halloween, - &WS2812FX::mode_bicolor_chase, - &WS2812FX::mode_tricolor_chase, - &WS2812FX::mode_twinkleFOX, - &WS2812FX::mode_rain, - &WS2812FX::mode_custom_0, - &WS2812FX::mode_custom_1, - &WS2812FX::mode_custom_2, - &WS2812FX::mode_custom_3, - &WS2812FX::mode_custom_4, - &WS2812FX::mode_custom_5, - &WS2812FX::mode_custom_6, - &WS2812FX::mode_custom_7 -}; +#if defined(ESP8266) || defined(ESP32) +#include "modes_esp.h" +#else +#include "modes_arduino.h" +#endif + #endif diff --git a/src/modes.cpp b/src/modes.cpp new file mode 100644 index 0000000..6b92ae2 --- /dev/null +++ b/src/modes.cpp @@ -0,0 +1,794 @@ +/* + modes.cpp - WS2812FX animation modes/effects + + LICENSE + + The MIT License (MIT) + + Copyright (c) 2016 Harm Aldick + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + + CHANGELOG + + 2022-03-23 Separated from the original WS2812FX.cpp file +*/ + +#include "WS2812FX.h" + +/* ##################################################### +# +# Mode Functions +# +##################################################### */ + +/* + * No blinking. Just plain old static light. + */ +uint16_t WS2812FX::mode_static(void) { + fill(_seg->colors[0], _seg->start, _seg_len); + SET_CYCLE; + return _seg->speed; +} + +/* + * Normal blinking. 50% on/off time. + */ +uint16_t WS2812FX::mode_blink(void) { + return blink(_seg->colors[0], _seg->colors[1], false); +} + +/* + * Classic Blink effect. Cycling through the rainbow. + */ +uint16_t WS2812FX::mode_blink_rainbow(void) { + return blink(color_wheel(_seg_rt->counter_mode_call & 0xFF), _seg->colors[1], false); +} + +/* + * Classic Strobe effect. + */ +uint16_t WS2812FX::mode_strobe(void) { + return blink(_seg->colors[0], _seg->colors[1], true); +} + +/* + * Classic Strobe effect. Cycling through the rainbow. + */ +uint16_t WS2812FX::mode_strobe_rainbow(void) { + return blink(color_wheel(_seg_rt->counter_mode_call & 0xFF), _seg->colors[1], true); +} + +/* + * Lights all LEDs one after another. + */ +uint16_t WS2812FX::mode_color_wipe(void) { + return color_wipe(_seg->colors[0], _seg->colors[1], false); +} + +uint16_t WS2812FX::mode_color_wipe_inv(void) { + return color_wipe(_seg->colors[1], _seg->colors[0], false); +} + +uint16_t WS2812FX::mode_color_wipe_rev(void) { + return color_wipe(_seg->colors[0], _seg->colors[1], true); +} + +uint16_t WS2812FX::mode_color_wipe_rev_inv(void) { + return color_wipe(_seg->colors[1], _seg->colors[0], true); +} + +/* + * Turns all LEDs after each other to a random color. + * Then starts over with another color. + */ +uint16_t WS2812FX::mode_color_wipe_random(void) { + if(_seg_rt->counter_mode_step % _seg_len == 0) { // aux_param will store our random color wheel index + _seg_rt->aux_param = get_random_wheel_index(_seg_rt->aux_param); + } + uint32_t color = color_wheel(_seg_rt->aux_param); + return color_wipe(color, color, false) * 2; +} + +/* + * Random color introduced alternating from start and end of strip. + */ +uint16_t WS2812FX::mode_color_sweep_random(void) { + if(_seg_rt->counter_mode_step % _seg_len == 0) { // aux_param will store our random color wheel index + _seg_rt->aux_param = get_random_wheel_index(_seg_rt->aux_param); + } + uint32_t color = color_wheel(_seg_rt->aux_param); + return color_wipe(color, color, true) * 2; +} + +/* + * Lights all LEDs in one random color up. Then switches them + * to the next random color. + */ +uint16_t WS2812FX::mode_random_color(void) { + _seg_rt->aux_param = get_random_wheel_index(_seg_rt->aux_param); // aux_param will store our random color wheel index + uint32_t color = color_wheel(_seg_rt->aux_param); + fill(color, _seg->start, _seg_len); + SET_CYCLE; + return _seg->speed; +} + +/* + * Lights every LED in a random color. Changes one random LED after the other + * to another random color. + */ +uint16_t WS2812FX::mode_single_dynamic(void) { + if(_seg_rt->counter_mode_call == 0) { + for(uint16_t i=_seg->start; i <= _seg->stop; i++) { + setPixelColor(i, color_wheel(random8())); + } + } + + setPixelColor(_seg->start + random16(_seg_len), color_wheel(random8())); + SET_CYCLE; + return (_seg->speed / 16) ; +} + +/* + * Lights every LED in a random color. Changes all LED at the same time + * to new random colors. + */ +uint16_t WS2812FX::mode_multi_dynamic(void) { + for(uint16_t i=_seg->start; i <= _seg->stop; i++) { + setPixelColor(i, color_wheel(random8())); + } + SET_CYCLE; + return (_seg->speed / 4); +} + +/* + * Does the "standby-breathing" of well known i-Devices. Fixed Speed. + * Use mode "fade" if you like to have something similar with a different speed. + */ +uint16_t WS2812FX::mode_breath(void) { + int lum = _seg_rt->counter_mode_step; + if(lum > 255) lum = 511 - lum; // lum = 15 -> 255 -> 15 + + uint16_t delay; + if(lum == 15) delay = 970; // 970 pause before each breath + else if(lum <= 25) delay = 38; // 19 + else if(lum <= 50) delay = 36; // 18 + else if(lum <= 75) delay = 28; // 14 + else if(lum <= 100) delay = 20; // 10 + else if(lum <= 125) delay = 14; // 7 + else if(lum <= 150) delay = 11; // 5 + else delay = 10; // 4 + + uint32_t color = color_blend(_seg->colors[1], _seg->colors[0], lum); + fill(color, _seg->start, _seg_len); + + _seg_rt->counter_mode_step += 2; + if(_seg_rt->counter_mode_step > (512-15)) { + _seg_rt->counter_mode_step = 15; + SET_CYCLE; + } + return delay; +} + +/* + * Fades the LEDs between two colors + */ +uint16_t WS2812FX::mode_fade(void) { + int lum = _seg_rt->counter_mode_step; + if(lum > 255) lum = 511 - lum; // lum = 0 -> 255 -> 0 + + uint32_t color = color_blend(_seg->colors[1], _seg->colors[0], lum); + fill(color, _seg->start, _seg_len); + + _seg_rt->counter_mode_step += 4; + if(_seg_rt->counter_mode_step > 511) { + _seg_rt->counter_mode_step = 0; + SET_CYCLE; + } + return (_seg->speed / 128); +} + +/* + * Runs a block of pixels back and forth. + */ +uint16_t WS2812FX::mode_scan(void) { + return scan(_seg->colors[0], _seg->colors[1], false); +} + +/* + * Runs two blocks of pixels back and forth in opposite directions. + */ +uint16_t WS2812FX::mode_dual_scan(void) { + return scan(_seg->colors[0], _seg->colors[1], true); +} + +/* + * Cycles all LEDs at once through a rainbow. + */ +uint16_t WS2812FX::mode_rainbow(void) { + uint32_t color = color_wheel(_seg_rt->counter_mode_step); + fill(color, _seg->start, _seg_len); + + _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) & 0xFF; + + if(_seg_rt->counter_mode_step == 0) SET_CYCLE; + + return (_seg->speed / 256); +} + +/* + * Cycles a rainbow over the entire string of LEDs. + */ +uint16_t WS2812FX::mode_rainbow_cycle(void) { + uint32_t color = color_wheel(_seg_rt->counter_mode_step); + if(IS_REVERSE) { + copyPixels(_seg->start, _seg->start + 1, _seg_len - 1); + setPixelColor(_seg->stop, color); + } else { + copyPixels(_seg->start + 1, _seg->start, _seg_len - 1); + setPixelColor(_seg->start, color); + } + + _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) & 0xFF; + if(_seg_rt->counter_mode_step == 0) SET_CYCLE; + + return (_seg->speed / 256); +} + +/* + * Tricolor chase mode + */ +uint16_t WS2812FX::mode_tricolor_chase(void) { + return tricolor_chase(_seg->colors[0], _seg->colors[1], _seg->colors[2]); +} + +/* + * Alternating white/red/black pixels running. + */ +uint16_t WS2812FX::mode_circus_combustus(void) { + return tricolor_chase(RED, WHITE, BLACK); +} + +/* + * Theatre-style crawling lights. + * Inspired by the Adafruit examples. + */ +uint16_t WS2812FX::mode_theater_chase(void) { + return tricolor_chase(_seg->colors[0], _seg->colors[1], _seg->colors[1]); +} + +/* + * Theatre-style crawling lights with rainbow effect. + * Inspired by the Adafruit examples. + */ +uint16_t WS2812FX::mode_theater_chase_rainbow(void) { + _seg_rt->aux_param = (_seg_rt->aux_param + 1) & 0xFF; + uint32_t color = color_wheel(_seg_rt->aux_param); + return tricolor_chase(color, _seg->colors[1], _seg->colors[1]); +} + +/* + * Running lights effect with smooth sine transition. + */ +uint16_t WS2812FX::mode_running_lights(void) { + uint8_t size = 1 << SIZE_OPTION; + uint8_t sineIncr = max(1, (256 / _seg_len) * size); + for(uint16_t i=0; i < _seg_len; i++) { + int lum = (int)sine8(((i + _seg_rt->counter_mode_step) * sineIncr)); + uint32_t color = color_blend(_seg->colors[0], _seg->colors[1], lum); + if(IS_REVERSE) { + setPixelColor(_seg->start + i, color); + } else { + setPixelColor(_seg->stop - i, color); + } + } + _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) % 256; + if(_seg_rt->counter_mode_step == 0) SET_CYCLE; + return (_seg->speed / _seg_len); +} + +/* + * Blink several LEDs on, reset, repeat. + * Inspired by www.tweaking4all.com/hardware/arduino/arduino-led-strip-effects/ + */ +uint16_t WS2812FX::mode_twinkle(void) { + return twinkle(_seg->colors[0], _seg->colors[1]); +} + +/* + * Blink several LEDs in random colors on, reset, repeat. + * Inspired by www.tweaking4all.com/hardware/arduino/arduino-led-strip-effects/ + */ +uint16_t WS2812FX::mode_twinkle_random(void) { + return twinkle(color_wheel(random8()), _seg->colors[1]); +} + +/* + * Blink several LEDs on, fading out. + */ +uint16_t WS2812FX::mode_twinkle_fade(void) { + return twinkle_fade(_seg->colors[0]); +} + +/* + * Blink several LEDs in random colors on, fading out. + */ +uint16_t WS2812FX::mode_twinkle_fade_random(void) { + return twinkle_fade(color_wheel(random8())); +} + +/* + * Blinks one LED at a time. + * Inspired by www.tweaking4all.com/hardware/arduino/arduino-led-strip-effects/ + */ +uint16_t WS2812FX::mode_sparkle(void) { + return sparkle(_seg->colors[1], _seg->colors[0]); +} + +/* + * Lights all LEDs in the color. Flashes white pixels randomly. + * Inspired by www.tweaking4all.com/hardware/arduino/arduino-led-strip-effects/ + */ +uint16_t WS2812FX::mode_flash_sparkle(void) { + return sparkle(_seg->colors[0], WHITE); +} + +/* + * Like flash sparkle. With more flash. + * Inspired by www.tweaking4all.com/hardware/arduino/arduino-led-strip-effects/ + */ +uint16_t WS2812FX::mode_hyper_sparkle(void) { + fill(_seg->colors[0], _seg->start, _seg_len); + + uint8_t size = 1 << SIZE_OPTION; + for(uint8_t i=0; i<8; i++) { + fill(WHITE, _seg->start + random16(_seg_len - size + 1), size); + } + + SET_CYCLE; + return (_seg->speed / 32); +} + +/* + * Strobe effect with different strobe count and pause, controlled by speed. + */ +uint16_t WS2812FX::mode_multi_strobe(void) { + fill(_seg->colors[1], _seg->start, _seg_len); + + uint16_t delay = 200 + ((9 - (_seg->speed % 10)) * 100); + uint16_t count = 2 * ((_seg->speed / 100) + 1); + if(_seg_rt->counter_mode_step < count) { + if((_seg_rt->counter_mode_step & 1) == 0) { + fill(_seg->colors[0], _seg->start, _seg_len); + delay = 20; + } else { + delay = 50; + } + } + + _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) % (count + 1); + if(_seg_rt->counter_mode_step == 0) SET_CYCLE; + return delay; +} + +/* + * Bicolor chase mode + */ +uint16_t WS2812FX::mode_bicolor_chase(void) { + return chase(_seg->colors[0], _seg->colors[1], _seg->colors[2]); +} + +/* + * White running on _color. + */ +uint16_t WS2812FX::mode_chase_color(void) { + return chase(_seg->colors[0], WHITE, WHITE); +} + +/* + * Black running on _color. + */ +uint16_t WS2812FX::mode_chase_blackout(void) { + return chase(_seg->colors[0], BLACK, BLACK); +} + +/* + * _color running on white. + */ +uint16_t WS2812FX::mode_chase_white(void) { + return chase(WHITE, _seg->colors[0], _seg->colors[0]); +} + +/* + * White running followed by random color. + */ +uint16_t WS2812FX::mode_chase_random(void) { + if(_seg_rt->counter_mode_step == 0) { + _seg_rt->aux_param = get_random_wheel_index(_seg_rt->aux_param); + } + return chase(color_wheel(_seg_rt->aux_param), WHITE, WHITE); +} + +/* + * Rainbow running on white. + */ +uint16_t WS2812FX::mode_chase_rainbow_white(void) { + uint16_t n = _seg_rt->counter_mode_step; + uint16_t m = (_seg_rt->counter_mode_step + 1) % _seg_len; + uint32_t color2 = color_wheel(((n * 256 / _seg_len) + (_seg_rt->counter_mode_call & 0xFF)) & 0xFF); + uint32_t color3 = color_wheel(((m * 256 / _seg_len) + (_seg_rt->counter_mode_call & 0xFF)) & 0xFF); + + return chase(WHITE, color2, color3); +} + +/* + * White running on rainbow. + */ +uint16_t WS2812FX::mode_chase_rainbow(void) { + uint8_t color_sep = 256 / _seg_len; + uint8_t color_index = _seg_rt->counter_mode_call & 0xFF; + uint32_t color = color_wheel(((_seg_rt->counter_mode_step * color_sep) + color_index) & 0xFF); + + return chase(color, WHITE, WHITE); +} + +/* + * Black running on rainbow. + */ +uint16_t WS2812FX::mode_chase_blackout_rainbow(void) { + uint8_t color_sep = 256 / _seg_len; + uint8_t color_index = _seg_rt->counter_mode_call & 0xFF; + uint32_t color = color_wheel(((_seg_rt->counter_mode_step * color_sep) + color_index) & 0xFF); + + return chase(color, BLACK, BLACK); +} + +/* + * White flashes running on _color. + */ +uint16_t WS2812FX::mode_chase_flash(void) { + return chase_flash(_seg->colors[0], WHITE); +} + +/* + * White flashes running, followed by random color. + */ +uint16_t WS2812FX::mode_chase_flash_random(void) { + return chase_flash(color_wheel(_seg_rt->aux_param), WHITE); +} + +/* + * Alternating color/white pixels running. + */ +uint16_t WS2812FX::mode_running_color(void) { + return running(_seg->colors[0], _seg->colors[1]); +} + +/* + * Alternating red/blue pixels running. + */ +uint16_t WS2812FX::mode_running_red_blue(void) { + return running(RED, BLUE); +} + +/* + * Alternating red/green pixels running. + */ +uint16_t WS2812FX::mode_merry_christmas(void) { + return running(RED, GREEN); +} + +/* + * Alternating orange/purple pixels running. + */ +uint16_t WS2812FX::mode_halloween(void) { + return running(PURPLE, ORANGE); +} + +/* + * Random colored pixels running. + */ +uint16_t WS2812FX::mode_running_random(void) { + uint8_t size = 2 << SIZE_OPTION; + if((_seg_rt->counter_mode_step) % size == 0) { + _seg_rt->aux_param = get_random_wheel_index(_seg_rt->aux_param); + } + + uint32_t color = color_wheel(_seg_rt->aux_param); + + return running(color, color); +} + +/* + * K.I.T.T. + */ +uint16_t WS2812FX::mode_larson_scanner(void) { + fade_out(); + + if(_seg_rt->counter_mode_step < _seg_len) { + if(IS_REVERSE) { + setPixelColor(_seg->stop - _seg_rt->counter_mode_step, _seg->colors[0]); + } else { + setPixelColor(_seg->start + _seg_rt->counter_mode_step, _seg->colors[0]); + } + } else { + uint16_t index = (_seg_len * 2) - _seg_rt->counter_mode_step - 2; + if(IS_REVERSE) { + setPixelColor(_seg->stop - index, _seg->colors[0]); + } else { + setPixelColor(_seg->start + index, _seg->colors[0]); + } + } + + _seg_rt->counter_mode_step++; + if(_seg_rt->counter_mode_step >= (uint16_t)((_seg_len * 2) - 2)) { + _seg_rt->counter_mode_step = 0; + SET_CYCLE; + } + + return (_seg->speed / (_seg_len * 2)); +} + +/* + * Firing comets from one end. + */ +uint16_t WS2812FX::mode_comet(void) { + fade_out(); + + if(IS_REVERSE) { + setPixelColor(_seg->stop - _seg_rt->counter_mode_step, _seg->colors[0]); + } else { + setPixelColor(_seg->start + _seg_rt->counter_mode_step, _seg->colors[0]); + } + + _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) % _seg_len; + if(_seg_rt->counter_mode_step == 0) SET_CYCLE; + + return (_seg->speed / _seg_len); +} + +/* + * Firework sparks. + */ +uint16_t WS2812FX::mode_fireworks(void) { + uint32_t color = BLACK; + do { // randomly choose a non-BLACK color from the colors array + color = _seg->colors[random8(MAX_NUM_COLORS)]; + } while (color == BLACK); + return fireworks(color); +} + +/* + * Random colored firework sparks. + */ +uint16_t WS2812FX::mode_fireworks_random(void) { + return fireworks(color_wheel(random8())); +} + +/* + * Random flickering. + */ +uint16_t WS2812FX::mode_fire_flicker(void) { + return fire_flicker(3); +} + +/* +* Random flickering, less intensity. +*/ +uint16_t WS2812FX::mode_fire_flicker_soft(void) { + return fire_flicker(6); +} + +/* +* Random flickering, more intensity. +*/ +uint16_t WS2812FX::mode_fire_flicker_intense(void) { + return fire_flicker(1); +} + +// An adaptation of Mark Kriegsman's FastLED twinkeFOX effect +// https://gist.github.com/kriegsman/756ea6dcae8e30845b5a +uint16_t WS2812FX::mode_twinkleFOX(void) { + uint16_t mySeed = 0; // reset the random number generator seed + + // Get and translate the segment's size option + uint8_t size = 1 << ((_seg->options >> 1) & 0x03); // 1,2,4,8 + + // Get the segment's colors array values + uint32_t color0 = _seg->colors[0]; + uint32_t color1 = _seg->colors[1]; + uint32_t color2 = _seg->colors[2]; + uint32_t blendedColor; + + for (uint16_t i = _seg->start; i <= _seg->stop; i+=size) { + // Use Mark Kriegsman's clever idea of using pseudo-random numbers to determine + // each LED's initial and increment blend values + mySeed = (mySeed * 2053) + 13849; // a random, but deterministic, number + uint16_t initValue = (mySeed + (mySeed >> 8)) & 0xff; // the LED's initial blend index (0-255) + mySeed = (mySeed * 2053) + 13849; // another random, but deterministic, number + uint16_t incrValue = (((mySeed + (mySeed >> 8)) & 0x07) + 1) * 2; // blend index increment (2,4,6,8,10,12,14,16) + + // We're going to use a sine function to blend colors, instead of Mark's triangle + // function, simply because a sine lookup table is already built into the + // Adafruit_NeoPixel lib. Yes, I'm lazy. + // Use the counter_mode_call var as a clock "tick" counter and calc the blend index + uint8_t blendIndex = (initValue + (_seg_rt->counter_mode_call * incrValue)) & 0xff; // 0-255 + // Index into the built-in Adafruit_NeoPixel sine table to lookup the blend amount + uint8_t blendAmt = Adafruit_NeoPixel::sine8(blendIndex); // 0-255 + + // If colors[0] is BLACK, blend random colors + if(color0 == BLACK) { + blendedColor = color_blend(color_wheel(initValue), color1, blendAmt); + // If colors[2] isn't BLACK, choose to blend colors[0]/colors[1] or colors[1]/colors[2] + // (which color pair to blend is picked randomly) + } else if((color2 != BLACK) && (initValue < 128) == 0) { + blendedColor = color_blend(color2, color1, blendAmt); + // Otherwise always blend colors[0]/colors[1] + } else { + blendedColor = color_blend(color0, color1, blendAmt); + } + + // Assign the new color to the number of LEDs specified by the SIZE option + for(uint8_t j=0; jstop) { + setPixelColor(i + j, blendedColor); + } + } + } + SET_CYCLE; + return _seg->speed / 32; +} + +// A combination of the Fireworks effect and the running effect +// to create an effect that looks like rain. +uint16_t WS2812FX::mode_rain(void) { + // randomly choose colors[0] or colors[2] + uint32_t rainColor = (random8() & 1) == 0 ? _seg->colors[0] : _seg->colors[2]; + // if colors[0] == colors[1], choose a random color + if(_seg->colors[0] == _seg->colors[1]) rainColor = color_wheel(random8()); + + // run the fireworks effect to create a "raindrop" + fireworks(rainColor); + + // shift everything two pixels + if(IS_REVERSE) { + copyPixels(_seg->start, _seg->start + 2, _seg_len - 2); + } else { + copyPixels(_seg->start + 2, _seg->start, _seg_len - 2); + } + + return (_seg->speed / 16); +} + +// block dissolve effect +uint16_t WS2812FX::mode_block_dissolve(void) { + uint32_t color = _seg->colors[_seg_rt->aux_param]; // get the target color + + // get the decimated color after setPixelColor() has mangled it + // in accordance to the brightness setting + setPixelColor(_seg->start, color); + uint32_t desColor = getPixelColor(_seg->start); + + // find a random pixel that isn't the target color and update it + for(uint16_t i=0; i<_seg_len; i++) { + int index = _seg->start + random16(_seg_len); + if(getPixelColor(index) != desColor) { + setPixelColor(index, color); + return _seg->speed / 64; + } + } + + // if didn't find a random pixel that wasn't the target color, + // then set the entire segment to the target color + fill(color, _seg->start, _seg_len); + + // choose a new target color + _seg_rt->aux_param = (_seg_rt->aux_param + 1) % MAX_NUM_COLORS; + if(_seg_rt->aux_param == 0) SET_CYCLE; + return _seg->speed / 64; +} + +// ICU effect +uint16_t WS2812FX::mode_icu(void) { + uint16_t pos = _seg_rt->counter_mode_step; // current eye position + uint16_t dest = _seg_rt->aux_param3; // eye destination + uint16_t index = _seg->start + pos; // index of the first eye + uint16_t index2 = index + _seg_len/2; // index of the second eye + + Adafruit_NeoPixel::clear(); // erase the current eyes + + // if the eyes have not reached their destination + if(pos != dest) { + // move the eyes right or left depending on position relative to destination + int dir = dest > pos ? 1 : -1; + setPixelColor(index + dir, _seg->colors[0]); // paint two eyes + setPixelColor(index2 + dir, _seg->colors[0]); + _seg_rt->counter_mode_step += dir; // update the eye position + return (_seg->speed / _seg_len); + } else { // the eyes have reached their destination + if(random8(6) == 0) { // blink the eyes once in a while + return 200; + } else { + setPixelColor(index, _seg->colors[0]); + setPixelColor(index2, _seg->colors[0]); + _seg_rt->aux_param3 = random16(_seg_len/2); // set a new destination + SET_CYCLE; + return 1000 + random16(2000); // pause a second or two + } + } +} + +// Dual Larson effect +uint16_t WS2812FX::mode_dual_larson(void) { + fade_out(); + + _seg_rt->aux_param3 += _seg_rt->aux_param ? -1 : 1; // update the LED index + + setPixelColor(_seg->start + _seg_rt->aux_param3, _seg->colors[0]); + setPixelColor(_seg->stop - _seg_rt->aux_param3, _seg->colors[2] ? _seg->colors[2] : _seg->colors[0]); + + if(_seg_rt->aux_param3 == 0 || _seg_rt->aux_param3 >= _seg_len - 1) { + _seg_rt->aux_param = !_seg_rt->aux_param; // change direction + SET_CYCLE; + } + + return (_seg->speed / (_seg_len * 2)); +} + +// Random Wipe Bright effect (same as custom RandomChase effect) +uint16_t WS2812FX::mode_random_wipe_bright(void) { + uint32_t color = IS_REVERSE ? getPixelColor(_seg->stop): getPixelColor(_seg->start); + + // periodically change each RGB component to a random value + uint8_t mask = random8(7); + color = mask & 0x1 ? color : (color & 0x00ffff) | (random8() << 16); + color = mask & 0x2 ? color : (color & 0xff00ff) | (random8() << 8); + color = mask & 0x4 ? color : (color & 0xffff00) | (random8()); + + return running(color, color); +} + +/* + * Custom modes + */ +uint16_t WS2812FX::mode_custom_0() { + return customModes[0](); +} +uint16_t WS2812FX::mode_custom_1() { + return customModes[1](); +} +uint16_t WS2812FX::mode_custom_2() { + return customModes[2](); +} +uint16_t WS2812FX::mode_custom_3() { + return customModes[3](); +} +uint16_t WS2812FX::mode_custom_4() { + return customModes[4](); +} +uint16_t WS2812FX::mode_custom_5() { + return customModes[5](); +} +uint16_t WS2812FX::mode_custom_6() { + return customModes[6](); +} +uint16_t WS2812FX::mode_custom_7() { + return customModes[7](); +} diff --git a/src/modes_arduino.h b/src/modes_arduino.h new file mode 100644 index 0000000..9f09e72 --- /dev/null +++ b/src/modes_arduino.h @@ -0,0 +1,313 @@ +/* + modes_arduino.h - WS2812FX header file for Arduino microprocessors + + LICENSE + + The MIT License (MIT) + + Copyright (c) 2016 Harm Aldick + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + + CHANGELOG + + 2022-03-23 Separated from the original WS2812FX.h file +*/ +#ifndef mode_arduino_h +#define mode_arduino_h + +#define MODE_COUNT (sizeof(_names)/sizeof(_names[0])) +#define MODE_PTR(x) this->*_modes[x] +#define MODE_NAME(x) _names[x] + +#define FX_MODE_STATIC 0 +#define FX_MODE_BLINK 1 +#define FX_MODE_BREATH 2 +#define FX_MODE_COLOR_WIPE 3 +#define FX_MODE_COLOR_WIPE_INV 4 +#define FX_MODE_COLOR_WIPE_REV 5 +#define FX_MODE_COLOR_WIPE_REV_INV 6 +#define FX_MODE_COLOR_WIPE_RANDOM 7 +#define FX_MODE_RANDOM_COLOR 8 +#define FX_MODE_SINGLE_DYNAMIC 9 +#define FX_MODE_MULTI_DYNAMIC 10 +#define FX_MODE_RAINBOW 11 +#define FX_MODE_RAINBOW_CYCLE 12 +#define FX_MODE_SCAN 13 +#define FX_MODE_DUAL_SCAN 14 +#define FX_MODE_FADE 15 +#define FX_MODE_THEATER_CHASE 16 +#define FX_MODE_THEATER_CHASE_RAINBOW 17 +#define FX_MODE_RUNNING_LIGHTS 18 +#define FX_MODE_TWINKLE 19 +#define FX_MODE_TWINKLE_RANDOM 20 +#define FX_MODE_TWINKLE_FADE 21 +#define FX_MODE_TWINKLE_FADE_RANDOM 22 +#define FX_MODE_SPARKLE 23 +#define FX_MODE_FLASH_SPARKLE 24 +#define FX_MODE_HYPER_SPARKLE 25 +#define FX_MODE_STROBE 26 +#define FX_MODE_STROBE_RAINBOW 27 +#define FX_MODE_MULTI_STROBE 28 +#define FX_MODE_BLINK_RAINBOW 29 +#define FX_MODE_CHASE_WHITE 30 +#define FX_MODE_CHASE_COLOR 31 +#define FX_MODE_CHASE_RANDOM 32 +#define FX_MODE_CHASE_RAINBOW 33 +#define FX_MODE_CHASE_FLASH 34 +#define FX_MODE_CHASE_FLASH_RANDOM 35 +#define FX_MODE_CHASE_RAINBOW_WHITE 36 +#define FX_MODE_CHASE_BLACKOUT 37 +#define FX_MODE_CHASE_BLACKOUT_RAINBOW 38 +#define FX_MODE_COLOR_SWEEP_RANDOM 39 +#define FX_MODE_RUNNING_COLOR 40 +#define FX_MODE_RUNNING_RED_BLUE 41 +#define FX_MODE_RUNNING_RANDOM 42 +#define FX_MODE_LARSON_SCANNER 43 +#define FX_MODE_COMET 44 +#define FX_MODE_FIREWORKS 45 +#define FX_MODE_FIREWORKS_RANDOM 46 +#define FX_MODE_MERRY_CHRISTMAS 47 +#define FX_MODE_FIRE_FLICKER 48 +#define FX_MODE_FIRE_FLICKER_SOFT 49 +#define FX_MODE_FIRE_FLICKER_INTENSE 50 +#define FX_MODE_CIRCUS_COMBUSTUS 51 +#define FX_MODE_HALLOWEEN 52 +#define FX_MODE_BICOLOR_CHASE 53 +#define FX_MODE_TRICOLOR_CHASE 54 +#define FX_MODE_TWINKLEFOX 55 +#define FX_MODE_RAIN 56 +#define FX_MODE_CUSTOM 57 // keep this for backward compatiblity +#define FX_MODE_CUSTOM_0 57 // custom modes need to go at the end +#define FX_MODE_CUSTOM_1 58 +#define FX_MODE_CUSTOM_2 59 +#define FX_MODE_CUSTOM_3 60 +#define FX_MODE_CUSTOM_4 61 +#define FX_MODE_CUSTOM_5 62 +#define FX_MODE_CUSTOM_6 63 +#define FX_MODE_CUSTOM_7 64 + +// create GLOBAL names to allow WS2812FX to compile with sketches and other libs +// that store strings in PROGMEM (get rid of the "section type conflict with __c" +// errors once and for all. Amen.) +const char name_0[] PROGMEM = "Static"; +const char name_1[] PROGMEM = "Blink"; +const char name_2[] PROGMEM = "Breath"; +const char name_3[] PROGMEM = "Color Wipe"; +const char name_4[] PROGMEM = "Color Wipe Inverse"; +const char name_5[] PROGMEM = "Color Wipe Reverse"; +const char name_6[] PROGMEM = "Color Wipe Reverse Inverse"; +const char name_7[] PROGMEM = "Color Wipe Random"; +const char name_8[] PROGMEM = "Random Color"; +const char name_9[] PROGMEM = "Single Dynamic"; +const char name_10[] PROGMEM = "Multi Dynamic"; +const char name_11[] PROGMEM = "Rainbow"; +const char name_12[] PROGMEM = "Rainbow Cycle"; +const char name_13[] PROGMEM = "Scan"; +const char name_14[] PROGMEM = "Dual Scan"; +const char name_15[] PROGMEM = "Fade"; +const char name_16[] PROGMEM = "Theater Chase"; +const char name_17[] PROGMEM = "Theater Chase Rainbow"; +const char name_18[] PROGMEM = "Running Lights"; +const char name_19[] PROGMEM = "Twinkle"; +const char name_20[] PROGMEM = "Twinkle Random"; +const char name_21[] PROGMEM = "Twinkle Fade"; +const char name_22[] PROGMEM = "Twinkle Fade Random"; +const char name_23[] PROGMEM = "Sparkle"; +const char name_24[] PROGMEM = "Flash Sparkle"; +const char name_25[] PROGMEM = "Hyper Sparkle"; +const char name_26[] PROGMEM = "Strobe"; +const char name_27[] PROGMEM = "Strobe Rainbow"; +const char name_28[] PROGMEM = "Multi Strobe"; +const char name_29[] PROGMEM = "Blink Rainbow"; +const char name_30[] PROGMEM = "Chase White"; +const char name_31[] PROGMEM = "Chase Color"; +const char name_32[] PROGMEM = "Chase Random"; +const char name_33[] PROGMEM = "Chase Rainbow"; +const char name_34[] PROGMEM = "Chase Flash"; +const char name_35[] PROGMEM = "Chase Flash Random"; +const char name_36[] PROGMEM = "Chase Rainbow White"; +const char name_37[] PROGMEM = "Chase Blackout"; +const char name_38[] PROGMEM = "Chase Blackout Rainbow"; +const char name_39[] PROGMEM = "Color Sweep Random"; +const char name_40[] PROGMEM = "Running Color"; +const char name_41[] PROGMEM = "Running Red Blue"; +const char name_42[] PROGMEM = "Running Random"; +const char name_43[] PROGMEM = "Larson Scanner"; +const char name_44[] PROGMEM = "Comet"; +const char name_45[] PROGMEM = "Fireworks"; +const char name_46[] PROGMEM = "Fireworks Random"; +const char name_47[] PROGMEM = "Merry Christmas"; +const char name_48[] PROGMEM = "Fire Flicker"; +const char name_49[] PROGMEM = "Fire Flicker (soft)"; +const char name_50[] PROGMEM = "Fire Flicker (intense)"; +const char name_51[] PROGMEM = "Circus Combustus"; +const char name_52[] PROGMEM = "Halloween"; +const char name_53[] PROGMEM = "Bicolor Chase"; +const char name_54[] PROGMEM = "Tricolor Chase"; +const char name_55[] PROGMEM = "TwinkleFOX"; +const char name_56[] PROGMEM = "Rain"; +const char name_57[] PROGMEM = "Custom 0"; // custom modes need to go at the end +const char name_58[] PROGMEM = "Custom 1"; +const char name_59[] PROGMEM = "Custom 2"; +const char name_60[] PROGMEM = "Custom 3"; +const char name_61[] PROGMEM = "Custom 4"; +const char name_62[] PROGMEM = "Custom 5"; +const char name_63[] PROGMEM = "Custom 6"; +const char name_64[] PROGMEM = "Custom 7"; + +static const __FlashStringHelper* _names[] = { + FSH(name_0), + FSH(name_1), + FSH(name_2), + FSH(name_3), + FSH(name_4), + FSH(name_5), + FSH(name_6), + FSH(name_7), + FSH(name_8), + FSH(name_9), + FSH(name_10), + FSH(name_11), + FSH(name_12), + FSH(name_13), + FSH(name_14), + FSH(name_15), + FSH(name_16), + FSH(name_17), + FSH(name_18), + FSH(name_19), + FSH(name_20), + FSH(name_21), + FSH(name_22), + FSH(name_23), + FSH(name_24), + FSH(name_25), + FSH(name_26), + FSH(name_27), + FSH(name_28), + FSH(name_29), + FSH(name_30), + FSH(name_31), + FSH(name_32), + FSH(name_33), + FSH(name_34), + FSH(name_35), + FSH(name_36), + FSH(name_37), + FSH(name_38), + FSH(name_39), + FSH(name_40), + FSH(name_41), + FSH(name_42), + FSH(name_43), + FSH(name_44), + FSH(name_45), + FSH(name_46), + FSH(name_47), + FSH(name_48), + FSH(name_49), + FSH(name_50), + FSH(name_51), + FSH(name_52), + FSH(name_53), + FSH(name_54), + FSH(name_55), + FSH(name_56), + FSH(name_57), + FSH(name_58), + FSH(name_59), + FSH(name_60), + FSH(name_61), + FSH(name_62), + FSH(name_63), + FSH(name_64) +}; + +// define static array of member function pointers. +// function pointers MUST be in the same order as the corresponding name in the _name array. +__attribute__ ((unused)) static WS2812FX::mode_ptr _modes[] = { + &WS2812FX::mode_static, + &WS2812FX::mode_blink, + &WS2812FX::mode_breath, + &WS2812FX::mode_color_wipe, + &WS2812FX::mode_color_wipe_inv, + &WS2812FX::mode_color_wipe_rev, + &WS2812FX::mode_color_wipe_rev_inv, + &WS2812FX::mode_color_wipe_random, + &WS2812FX::mode_random_color, + &WS2812FX::mode_single_dynamic, + &WS2812FX::mode_multi_dynamic, + &WS2812FX::mode_rainbow, + &WS2812FX::mode_rainbow_cycle, + &WS2812FX::mode_scan, + &WS2812FX::mode_dual_scan, + &WS2812FX::mode_fade, + &WS2812FX::mode_theater_chase, + &WS2812FX::mode_theater_chase_rainbow, + &WS2812FX::mode_running_lights, + &WS2812FX::mode_twinkle, + &WS2812FX::mode_twinkle_random, + &WS2812FX::mode_twinkle_fade, + &WS2812FX::mode_twinkle_fade_random, + &WS2812FX::mode_sparkle, + &WS2812FX::mode_flash_sparkle, + &WS2812FX::mode_hyper_sparkle, + &WS2812FX::mode_strobe, + &WS2812FX::mode_strobe_rainbow, + &WS2812FX::mode_multi_strobe, + &WS2812FX::mode_blink_rainbow, + &WS2812FX::mode_chase_white, + &WS2812FX::mode_chase_color, + &WS2812FX::mode_chase_random, + &WS2812FX::mode_chase_rainbow, + &WS2812FX::mode_chase_flash, + &WS2812FX::mode_chase_flash_random, + &WS2812FX::mode_chase_rainbow_white, + &WS2812FX::mode_chase_blackout, + &WS2812FX::mode_chase_blackout_rainbow, + &WS2812FX::mode_color_sweep_random, + &WS2812FX::mode_running_color, + &WS2812FX::mode_running_red_blue, + &WS2812FX::mode_running_random, + &WS2812FX::mode_larson_scanner, + &WS2812FX::mode_comet, + &WS2812FX::mode_fireworks, + &WS2812FX::mode_fireworks_random, + &WS2812FX::mode_merry_christmas, + &WS2812FX::mode_fire_flicker, + &WS2812FX::mode_fire_flicker_soft, + &WS2812FX::mode_fire_flicker_intense, + &WS2812FX::mode_circus_combustus, + &WS2812FX::mode_halloween, + &WS2812FX::mode_bicolor_chase, + &WS2812FX::mode_tricolor_chase, + &WS2812FX::mode_twinkleFOX, + &WS2812FX::mode_rain, + &WS2812FX::mode_custom_0, + &WS2812FX::mode_custom_1, + &WS2812FX::mode_custom_2, + &WS2812FX::mode_custom_3, + &WS2812FX::mode_custom_4, + &WS2812FX::mode_custom_5, + &WS2812FX::mode_custom_6, + &WS2812FX::mode_custom_7 +}; +#endif diff --git a/src/modes_esp.h b/src/modes_esp.h new file mode 100644 index 0000000..db1fec1 --- /dev/null +++ b/src/modes_esp.h @@ -0,0 +1,258 @@ +/* + modes_esp.h - WS2812FX header file for ESP8266 and ESP32 microprocessors + + LICENSE + + The MIT License (MIT) + + Copyright (c) 2016 Harm Aldick + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + + CHANGELOG + + 2022-03-23 Separated from the original WS2812FX.h file +*/ +#ifndef mode_esp_h +#define mode_esp_h + +#define MODE_COUNT (sizeof(_modes)/sizeof(_modes[0])) +#define MODE_PTR(x) this->*_modes[x].mode_ptr +#define MODE_NAME(x) _modes[x].name + +#define FX_MODE_STATIC 0 +#define FX_MODE_BLINK 1 +#define FX_MODE_BREATH 2 +#define FX_MODE_COLOR_WIPE 3 +#define FX_MODE_COLOR_WIPE_INV 4 +#define FX_MODE_COLOR_WIPE_REV 5 +#define FX_MODE_COLOR_WIPE_REV_INV 6 +#define FX_MODE_COLOR_WIPE_RANDOM 7 +#define FX_MODE_RANDOM_COLOR 8 +#define FX_MODE_SINGLE_DYNAMIC 9 +#define FX_MODE_MULTI_DYNAMIC 10 +#define FX_MODE_RAINBOW 11 +#define FX_MODE_RAINBOW_CYCLE 12 +#define FX_MODE_SCAN 13 +#define FX_MODE_DUAL_SCAN 14 +#define FX_MODE_FADE 15 +#define FX_MODE_THEATER_CHASE 16 +#define FX_MODE_THEATER_CHASE_RAINBOW 17 +#define FX_MODE_RUNNING_LIGHTS 18 +#define FX_MODE_TWINKLE 19 +#define FX_MODE_TWINKLE_RANDOM 20 +#define FX_MODE_TWINKLE_FADE 21 +#define FX_MODE_TWINKLE_FADE_RANDOM 22 +#define FX_MODE_SPARKLE 23 +#define FX_MODE_FLASH_SPARKLE 24 +#define FX_MODE_HYPER_SPARKLE 25 +#define FX_MODE_STROBE 26 +#define FX_MODE_STROBE_RAINBOW 27 +#define FX_MODE_MULTI_STROBE 28 +#define FX_MODE_BLINK_RAINBOW 29 +#define FX_MODE_CHASE_WHITE 30 +#define FX_MODE_CHASE_COLOR 31 +#define FX_MODE_CHASE_RANDOM 32 +#define FX_MODE_CHASE_RAINBOW 33 +#define FX_MODE_CHASE_FLASH 34 +#define FX_MODE_CHASE_FLASH_RANDOM 35 +#define FX_MODE_CHASE_RAINBOW_WHITE 36 +#define FX_MODE_CHASE_BLACKOUT 37 +#define FX_MODE_CHASE_BLACKOUT_RAINBOW 38 +#define FX_MODE_COLOR_SWEEP_RANDOM 39 +#define FX_MODE_RUNNING_COLOR 40 +#define FX_MODE_RUNNING_RED_BLUE 41 +#define FX_MODE_RUNNING_RANDOM 42 +#define FX_MODE_LARSON_SCANNER 43 +#define FX_MODE_COMET 44 +#define FX_MODE_FIREWORKS 45 +#define FX_MODE_FIREWORKS_RANDOM 46 +#define FX_MODE_MERRY_CHRISTMAS 47 +#define FX_MODE_FIRE_FLICKER 48 +#define FX_MODE_FIRE_FLICKER_SOFT 49 +#define FX_MODE_FIRE_FLICKER_INTENSE 50 +#define FX_MODE_CIRCUS_COMBUSTUS 51 +#define FX_MODE_HALLOWEEN 52 +#define FX_MODE_BICOLOR_CHASE 53 +#define FX_MODE_TRICOLOR_CHASE 54 +#define FX_MODE_TWINKLEFOX 55 +#define FX_MODE_RAIN 56 +#define FX_MODE_CUSTOM 57 // keep this for backward compatiblity +#define FX_MODE_CUSTOM_0 57 // custom modes need to go at the end +#define FX_MODE_CUSTOM_1 58 +#define FX_MODE_CUSTOM_2 59 +#define FX_MODE_CUSTOM_3 60 +#define FX_MODE_CUSTOM_4 61 +#define FX_MODE_CUSTOM_5 62 +#define FX_MODE_CUSTOM_6 63 +#define FX_MODE_CUSTOM_7 64 + +typedef struct Mode { + const __FlashStringHelper* name; + const __FlashStringHelper* category; + uint16_t (WS2812FX::*mode_ptr)(void); +} mode; + +// mode categories +const char cat_simple[] PROGMEM = "Simple"; +const char cat_wipe[] PROGMEM = "Wipe"; +const char cat_sweep[] PROGMEM = "Sweep"; +const char cat_special[] PROGMEM = "Special"; +const char cat_custom[] PROGMEM = "Custom"; + +// create GLOBAL names to allow WS2812FX to compile with sketches and other libs +// that store strings in PROGMEM (get rid of the "section type conflict with __c" +// errors once and for all. Amen.) +const char name_0[] PROGMEM = "Static"; +const char name_1[] PROGMEM = "Blink"; +const char name_2[] PROGMEM = "Breath"; +const char name_3[] PROGMEM = "Color Wipe"; +const char name_4[] PROGMEM = "Color Wipe Inverse"; +const char name_5[] PROGMEM = "Color Wipe Reverse"; +const char name_6[] PROGMEM = "Color Wipe Reverse Inverse"; +const char name_7[] PROGMEM = "Color Wipe Random"; +const char name_8[] PROGMEM = "Random Color"; +const char name_9[] PROGMEM = "Single Dynamic"; +const char name_10[] PROGMEM = "Multi Dynamic"; +const char name_11[] PROGMEM = "Rainbow"; +const char name_12[] PROGMEM = "Rainbow Cycle"; +const char name_13[] PROGMEM = "Scan"; +const char name_14[] PROGMEM = "Dual Scan"; +const char name_15[] PROGMEM = "Fade"; +const char name_16[] PROGMEM = "Theater Chase"; +const char name_17[] PROGMEM = "Theater Chase Rainbow"; +const char name_18[] PROGMEM = "Running Lights"; +const char name_19[] PROGMEM = "Twinkle"; +const char name_20[] PROGMEM = "Twinkle Random"; +const char name_21[] PROGMEM = "Twinkle Fade"; +const char name_22[] PROGMEM = "Twinkle Fade Random"; +const char name_23[] PROGMEM = "Sparkle"; +const char name_24[] PROGMEM = "Flash Sparkle"; +const char name_25[] PROGMEM = "Hyper Sparkle"; +const char name_26[] PROGMEM = "Strobe"; +const char name_27[] PROGMEM = "Strobe Rainbow"; +const char name_28[] PROGMEM = "Multi Strobe"; +const char name_29[] PROGMEM = "Blink Rainbow"; +const char name_30[] PROGMEM = "Chase White"; +const char name_31[] PROGMEM = "Chase Color"; +const char name_32[] PROGMEM = "Chase Random"; +const char name_33[] PROGMEM = "Chase Rainbow"; +const char name_34[] PROGMEM = "Chase Flash"; +const char name_35[] PROGMEM = "Chase Flash Random"; +const char name_36[] PROGMEM = "Chase Rainbow White"; +const char name_37[] PROGMEM = "Chase Blackout"; +const char name_38[] PROGMEM = "Chase Blackout Rainbow"; +const char name_39[] PROGMEM = "Color Sweep Random"; +const char name_40[] PROGMEM = "Running Color"; +const char name_41[] PROGMEM = "Running Red Blue"; +const char name_42[] PROGMEM = "Running Random"; +const char name_43[] PROGMEM = "Larson Scanner"; +const char name_44[] PROGMEM = "Comet"; +const char name_45[] PROGMEM = "Fireworks"; +const char name_46[] PROGMEM = "Fireworks Random"; +const char name_47[] PROGMEM = "Merry Christmas"; +const char name_48[] PROGMEM = "Fire Flicker"; +const char name_49[] PROGMEM = "Fire Flicker (soft)"; +const char name_50[] PROGMEM = "Fire Flicker (intense)"; +const char name_51[] PROGMEM = "Circus Combustus"; +const char name_52[] PROGMEM = "Halloween"; +const char name_53[] PROGMEM = "Bicolor Chase"; +const char name_54[] PROGMEM = "Tricolor Chase"; +const char name_55[] PROGMEM = "TwinkleFOX"; +const char name_56[] PROGMEM = "Rain"; +const char name_57[] PROGMEM = "Custom 0"; // custom modes need to go at the end +const char name_58[] PROGMEM = "Custom 1"; +const char name_59[] PROGMEM = "Custom 2"; +const char name_60[] PROGMEM = "Custom 3"; +const char name_61[] PROGMEM = "Custom 4"; +const char name_62[] PROGMEM = "Custom 5"; +const char name_63[] PROGMEM = "Custom 6"; +const char name_64[] PROGMEM = "Custom 7"; + +// define static array of member function pointers. +// make sure the order of the _modes array elements matches the FX_MODE_* values +__attribute__ ((unused)) static mode _modes[] = { + { FSH(name_0), FSH(cat_simple), &WS2812FX::mode_static }, + { FSH(name_1), FSH(cat_simple), &WS2812FX::mode_blink }, + { FSH(name_2), FSH(cat_special), &WS2812FX::mode_breath }, + { FSH(name_3), FSH(cat_wipe), &WS2812FX::mode_color_wipe }, + { FSH(name_4), FSH(cat_wipe), &WS2812FX::mode_color_wipe_inv }, + { FSH(name_5), FSH(cat_sweep), &WS2812FX::mode_color_wipe_rev }, + { FSH(name_6), FSH(cat_sweep), &WS2812FX::mode_color_wipe_rev_inv }, + { FSH(name_7), FSH(cat_wipe), &WS2812FX::mode_color_wipe_random }, + { FSH(name_8), FSH(cat_simple), &WS2812FX::mode_random_color }, + { FSH(name_9), FSH(cat_simple), &WS2812FX::mode_single_dynamic }, + { FSH(name_10), FSH(cat_simple), &WS2812FX::mode_multi_dynamic }, + { FSH(name_11), FSH(cat_simple), &WS2812FX::mode_rainbow }, + { FSH(name_12), FSH(cat_wipe), &WS2812FX::mode_rainbow_cycle }, + { FSH(name_13), FSH(cat_sweep), &WS2812FX::mode_scan }, + { FSH(name_14), FSH(cat_sweep), &WS2812FX::mode_dual_scan }, + { FSH(name_15), FSH(cat_sweep), &WS2812FX::mode_fade }, + { FSH(name_16), FSH(cat_wipe), &WS2812FX::mode_theater_chase }, + { FSH(name_17), FSH(cat_wipe), &WS2812FX::mode_theater_chase_rainbow }, + { FSH(name_18), FSH(cat_wipe), &WS2812FX::mode_running_lights }, + { FSH(name_19), FSH(cat_simple), &WS2812FX::mode_twinkle }, + { FSH(name_20), FSH(cat_simple), &WS2812FX::mode_twinkle_random }, + { FSH(name_21), FSH(cat_simple), &WS2812FX::mode_twinkle_fade }, + { FSH(name_22), FSH(cat_simple), &WS2812FX::mode_twinkle_fade_random }, + { FSH(name_23), FSH(cat_simple), &WS2812FX::mode_sparkle }, + { FSH(name_24), FSH(cat_simple), &WS2812FX::mode_flash_sparkle }, + { FSH(name_25), FSH(cat_simple), &WS2812FX::mode_hyper_sparkle }, + { FSH(name_26), FSH(cat_simple), &WS2812FX::mode_strobe }, + { FSH(name_27), FSH(cat_simple), &WS2812FX::mode_strobe_rainbow }, + { FSH(name_28), FSH(cat_simple), &WS2812FX::mode_multi_strobe }, + { FSH(name_29), FSH(cat_simple), &WS2812FX::mode_blink_rainbow }, + { FSH(name_30), FSH(cat_wipe), &WS2812FX::mode_chase_white }, + { FSH(name_31), FSH(cat_wipe), &WS2812FX::mode_chase_color }, + { FSH(name_32), FSH(cat_wipe), &WS2812FX::mode_chase_random }, + { FSH(name_33), FSH(cat_wipe), &WS2812FX::mode_chase_rainbow }, + { FSH(name_34), FSH(cat_wipe), &WS2812FX::mode_chase_flash }, + { FSH(name_35), FSH(cat_wipe), &WS2812FX::mode_chase_flash_random }, + { FSH(name_36), FSH(cat_wipe), &WS2812FX::mode_chase_rainbow_white }, + { FSH(name_37), FSH(cat_wipe), &WS2812FX::mode_chase_blackout }, + { FSH(name_38), FSH(cat_wipe), &WS2812FX::mode_chase_blackout_rainbow }, + { FSH(name_39), FSH(cat_sweep), &WS2812FX::mode_color_sweep_random }, + { FSH(name_40), FSH(cat_wipe), &WS2812FX::mode_running_color }, + { FSH(name_41), FSH(cat_wipe), &WS2812FX::mode_running_red_blue }, + { FSH(name_42), FSH(cat_wipe), &WS2812FX::mode_running_random }, + { FSH(name_43), FSH(cat_sweep), &WS2812FX::mode_larson_scanner }, + { FSH(name_44), FSH(cat_wipe), &WS2812FX::mode_comet }, + { FSH(name_45), FSH(cat_simple), &WS2812FX::mode_fireworks }, + { FSH(name_46), FSH(cat_simple), &WS2812FX::mode_fireworks_random }, + { FSH(name_47), FSH(cat_wipe), &WS2812FX::mode_merry_christmas }, + { FSH(name_48), FSH(cat_simple), &WS2812FX::mode_fire_flicker }, + { FSH(name_49), FSH(cat_simple), &WS2812FX::mode_fire_flicker_soft }, + { FSH(name_50), FSH(cat_simple), &WS2812FX::mode_fire_flicker_intense }, + { FSH(name_51), FSH(cat_wipe), &WS2812FX::mode_circus_combustus }, + { FSH(name_52), FSH(cat_wipe), &WS2812FX::mode_halloween }, + { FSH(name_53), FSH(cat_wipe), &WS2812FX::mode_bicolor_chase }, + { FSH(name_54), FSH(cat_wipe), &WS2812FX::mode_tricolor_chase }, + { FSH(name_55), FSH(cat_special), &WS2812FX::mode_twinkleFOX }, + { FSH(name_56), FSH(cat_special), &WS2812FX::mode_rain }, + { FSH(name_57), FSH(cat_custom), &WS2812FX::mode_custom_0 }, + { FSH(name_58), FSH(cat_custom), &WS2812FX::mode_custom_1 }, + { FSH(name_59), FSH(cat_custom), &WS2812FX::mode_custom_2 }, + { FSH(name_60), FSH(cat_custom), &WS2812FX::mode_custom_3 }, + { FSH(name_61), FSH(cat_custom), &WS2812FX::mode_custom_4 }, + { FSH(name_62), FSH(cat_custom), &WS2812FX::mode_custom_5 }, + { FSH(name_63), FSH(cat_custom), &WS2812FX::mode_custom_6 }, + { FSH(name_64), FSH(cat_custom), &WS2812FX::mode_custom_7 } +}; +#endif diff --git a/src/modes_funcs.cpp b/src/modes_funcs.cpp new file mode 100644 index 0000000..9fe0f37 --- /dev/null +++ b/src/modes_funcs.cpp @@ -0,0 +1,397 @@ +/* + modes_funcs.cpp - WS2812FX effects helper functions + + LICENSE + + The MIT License (MIT) + + Copyright (c) 2016 Harm Aldick + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + + CHANGELOG + + 2022-03-23 Separated from the original WS2812FX.cpp file +*/ +#include "WS2812FX.h" + +/* + * Blink/strobe function + * Alternate between color1 and color2 + * if(strobe == true) then create a strobe effect + */ +uint16_t WS2812FX::blink(uint32_t color1, uint32_t color2, bool strobe) { + if(_seg_rt->counter_mode_call & 1) { + uint32_t color = (IS_REVERSE) ? color1 : color2; // off + fill(color, _seg->start, _seg_len); + SET_CYCLE; + return strobe ? _seg->speed - 20 : (_seg->speed / 2); + } else { + uint32_t color = (IS_REVERSE) ? color2 : color1; // on + fill(color, _seg->start, _seg_len); + return strobe ? 20 : (_seg->speed / 2); + } +} + +/* + * Color wipe function + * LEDs are turned on (color1) in sequence, then turned off (color2) in sequence. + * if (bool rev == true) then LEDs are turned off in reverse order + */ +uint16_t WS2812FX::color_wipe(uint32_t color1, uint32_t color2, bool rev) { + if(_seg_rt->counter_mode_step < _seg_len) { + uint32_t led_offset = _seg_rt->counter_mode_step; + if(IS_REVERSE) { + setPixelColor(_seg->stop - led_offset, color1); + } else { + setPixelColor(_seg->start + led_offset, color1); + } + } else { + uint32_t led_offset = _seg_rt->counter_mode_step - _seg_len; + if((IS_REVERSE && !rev) || (!IS_REVERSE && rev)) { + setPixelColor(_seg->stop - led_offset, color2); + } else { + setPixelColor(_seg->start + led_offset, color2); + } + } + + _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) % (_seg_len * 2); + + if(_seg_rt->counter_mode_step == 0) SET_CYCLE; + + return (_seg->speed / (_seg_len * 2)); +} + + +/* + * scan function - runs a block of pixels back and forth. + */ +uint16_t WS2812FX::scan(uint32_t color1, uint32_t color2, bool dual) { + int8_t dir = _seg_rt->aux_param ? -1 : 1; + uint8_t size = 1 << SIZE_OPTION; + + fill(color2, _seg->start, _seg_len); + + for(uint8_t i = 0; i < size; i++) { + if(IS_REVERSE || dual) { + setPixelColor(_seg->stop - _seg_rt->counter_mode_step - i, color1); + } + if(!IS_REVERSE || dual) { + setPixelColor(_seg->start + _seg_rt->counter_mode_step + i, color1); + } + } + + _seg_rt->counter_mode_step += dir; + if(_seg_rt->counter_mode_step == 0) { + _seg_rt->aux_param = 0; + SET_CYCLE; + } + if(_seg_rt->counter_mode_step >= (uint16_t)(_seg_len - size)) _seg_rt->aux_param = 1; + + return (_seg->speed / (_seg_len * 2)); +} + +/* + * Tricolor chase function + */ +uint16_t WS2812FX::tricolor_chase(uint32_t color1, uint32_t color2, uint32_t color3) { + uint8_t sizeCnt = 1 << SIZE_OPTION; + uint8_t sizeCnt2 = sizeCnt + sizeCnt; + uint8_t sizeCnt3 = sizeCnt2 + sizeCnt; + uint16_t index = _seg_rt->counter_mode_step % sizeCnt3; + for(uint16_t i=0; i < _seg_len; i++, index++) { + index = index % sizeCnt3; + + uint32_t color = color3; + if(index < sizeCnt) color = color1; + else if(index < sizeCnt2) color = color2; + + if(IS_REVERSE) { + setPixelColor(_seg->start + i, color); + } else { + setPixelColor(_seg->stop - i, color); + } + } + + _seg_rt->counter_mode_step++; + if(_seg_rt->counter_mode_step % _seg_len == 0) SET_CYCLE; + + return (_seg->speed / 16); +} + +/* + * twinkle function + */ +uint16_t WS2812FX::twinkle(uint32_t color1, uint32_t color2) { + if(_seg_rt->counter_mode_step == 0) { + fill(color2, _seg->start, _seg_len); + uint16_t min_leds = (_seg_len / 4) + 1; // make sure, at least one LED is on + _seg_rt->counter_mode_step = random(min_leds, min_leds * 2); + SET_CYCLE; + } + + setPixelColor(_seg->start + random16(_seg_len), color1); + + _seg_rt->counter_mode_step--; + return (_seg->speed / _seg_len); +} + +/* + * fade out functions + */ +void WS2812FX::fade_out() { + return fade_out(_seg->colors[1]); +} + +void WS2812FX::fade_out(uint32_t targetColor) { + static const uint8_t rateMapH[] = {0, 1, 1, 1, 2, 3, 4, 6}; + static const uint8_t rateMapL[] = {0, 2, 3, 8, 8, 8, 8, 8}; + + uint8_t rate = FADE_RATE; + uint8_t rateH = rateMapH[rate]; + uint8_t rateL = rateMapL[rate]; + + uint32_t color = targetColor; + int w2 = (color >> 24) & 0xff; + int r2 = (color >> 16) & 0xff; + int g2 = (color >> 8) & 0xff; + int b2 = color & 0xff; + + for(uint16_t i=_seg->start; i <= _seg->stop; i++) { + color = getPixelColor(i); // current color + if(rate == 0) { // old fade-to-black algorithm + setPixelColor(i, (color >> 1) & 0x7F7F7F7F); + } else { // new fade-to-color algorithm + int w1 = (color >> 24) & 0xff; + int r1 = (color >> 16) & 0xff; + int g1 = (color >> 8) & 0xff; + int b1 = color & 0xff; + + // calculate the color differences between the current and target colors + int wdelta = w2 - w1; + int rdelta = r2 - r1; + int gdelta = g2 - g1; + int bdelta = b2 - b1; + + // if the current and target colors are almost the same, jump right to the target + // color, otherwise calculate an intermediate color. (fixes rounding issues) + wdelta = abs(wdelta) < 3 ? wdelta : (wdelta >> rateH) + (wdelta >> rateL); + rdelta = abs(rdelta) < 3 ? rdelta : (rdelta >> rateH) + (rdelta >> rateL); + gdelta = abs(gdelta) < 3 ? gdelta : (gdelta >> rateH) + (gdelta >> rateL); + bdelta = abs(bdelta) < 3 ? bdelta : (bdelta >> rateH) + (bdelta >> rateL); + + setPixelColor(i, r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta); + } + } +} + +/* + * color blend function + */ +uint32_t WS2812FX::color_blend(uint32_t color1, uint32_t color2, uint8_t blendAmt) { + uint32_t blendedColor; + blend((uint8_t*)&blendedColor, (uint8_t*)&color1, (uint8_t*)&color2, sizeof(uint32_t), blendAmt); + return blendedColor; +} + +uint8_t* WS2812FX::blend(uint8_t *dest, uint8_t *src1, uint8_t *src2, uint16_t cnt, uint8_t blendAmt) { + if(blendAmt == 0) { + memmove(dest, src1, cnt); + } else if(blendAmt == 255) { + memmove(dest, src2, cnt); + } else { + for(uint16_t i=0; istart + random16(_seg_len - size + 1); + fill(color, index, size); + SET_CYCLE; + } + return (_seg->speed / 16); +} + +/* + * Sparkle function + * color1 = background color + * color2 = sparkle color + */ +uint16_t WS2812FX::sparkle(uint32_t color1, uint32_t color2) { + if(_seg_rt->counter_mode_step == 0) { + fill(color1, _seg->start, _seg_len); + } + + uint8_t size = 1 << SIZE_OPTION; + fill(color1, _seg->start + _seg_rt->aux_param3, size); + + _seg_rt->aux_param3 = random16(_seg_len - size + 1); // aux_param3 stores the random led index + fill(color2, _seg->start + _seg_rt->aux_param3, size); + + SET_CYCLE; + return (_seg->speed / 32); +} + +/* + * color chase function. + * color1 = background color + * color2 and color3 = colors of two adjacent leds + */ +uint16_t WS2812FX::chase(uint32_t color1, uint32_t color2, uint32_t color3) { + uint8_t size = 1 << SIZE_OPTION; + for(uint8_t i=0; icounter_mode_step + i) % _seg_len; + uint16_t b = (a + size) % _seg_len; + uint16_t c = (b + size) % _seg_len; + if(IS_REVERSE) { + setPixelColor(_seg->stop - a, color1); + setPixelColor(_seg->stop - b, color2); + setPixelColor(_seg->stop - c, color3); + } else { + setPixelColor(_seg->start + a, color1); + setPixelColor(_seg->start + b, color2); + setPixelColor(_seg->start + c, color3); + } + } + + if(_seg_rt->counter_mode_step + (size * 3) == _seg_len) SET_CYCLE; + + _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) % _seg_len; + return (_seg->speed / _seg_len); +} + +/* + * running white flashes function. + * color1 = background color + * color2 = flash color + */ +uint16_t WS2812FX::chase_flash(uint32_t color1, uint32_t color2) { + const static uint8_t flash_count = 4; + uint8_t flash_step = _seg_rt->counter_mode_call % ((flash_count * 2) + 1); + + if(flash_step < (flash_count * 2)) { + uint32_t color = (flash_step % 2 == 0) ? color2 : color1; + uint16_t n = _seg_rt->counter_mode_step; + uint16_t m = (_seg_rt->counter_mode_step + 1) % _seg_len; + if(IS_REVERSE) { + setPixelColor(_seg->stop - n, color); + setPixelColor(_seg->stop - m, color); + } else { + setPixelColor(_seg->start + n, color); + setPixelColor(_seg->start + m, color); + } + return 30; + } else { + _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) % _seg_len; + if(_seg_rt->counter_mode_step == 0) { + // update aux_param so mode_chase_flash_random() will select the next color + _seg_rt->aux_param = get_random_wheel_index(_seg_rt->aux_param); + SET_CYCLE; + } + } + return (_seg->speed / _seg_len); +} + +/* + * Alternating pixels running function. + */ +uint16_t WS2812FX::running(uint32_t color1, uint32_t color2) { + uint8_t size = 2 << SIZE_OPTION; + uint32_t color = (_seg_rt->counter_mode_step & size) ? color1 : color2; + + if(IS_REVERSE) { + copyPixels(_seg->start, _seg->start + 1, _seg_len - 1); + setPixelColor(_seg->stop, color); + } else { + copyPixels(_seg->start + 1, _seg->start, _seg_len - 1); + setPixelColor(_seg->start, color); + } + + _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) % _seg_len; + if(_seg_rt->counter_mode_step == 0) SET_CYCLE; + return (_seg->speed / 16); +} + +/* + * Fireworks function. + */ +uint16_t WS2812FX::fireworks(uint32_t color) { + fade_out(); + +// for better performance, manipulate the Adafruit_NeoPixels pixels[] array directly + uint8_t *pixels = getPixels(); + uint8_t bytesPerPixel = getNumBytesPerPixel(); // 3=RGB, 4=RGBW + uint16_t startPixel = _seg->start * bytesPerPixel + bytesPerPixel; + uint16_t stopPixel = _seg->stop * bytesPerPixel; + for(uint16_t i=startPixel; i > 2) + + pixels[i] + + (pixels[i + bytesPerPixel] >> 2); + pixels[i] = tmpPixel > 255 ? 255 : tmpPixel; + } + + uint8_t size = 2 << SIZE_OPTION; + if(!_triggered) { + for(uint16_t i=0; istart + random16(_seg_len - size + 1); + fill(color, index, size); + SET_CYCLE; + } + } + } else { + for(uint16_t i=0; istart + random16(_seg_len - size + 1); + fill(color, index, size); + SET_CYCLE; + } + } + + return (_seg->speed / 16); +} + +/* + * Fire flicker function + */ +uint16_t WS2812FX::fire_flicker(int rev_intensity) { + byte w = (_seg->colors[0] >> 24) & 0xFF; + byte r = (_seg->colors[0] >> 16) & 0xFF; + byte g = (_seg->colors[0] >> 8) & 0xFF; + byte b = (_seg->colors[0] & 0xFF); + byte lum = max(w, max(r, max(g, b))) / rev_intensity; + for(uint16_t i=_seg->start; i <= _seg->stop; i++) { + int flicker = random8(lum); + setPixelColor(i, max(r - flicker, 0), max(g - flicker, 0), max(b - flicker, 0), max(w - flicker, 0)); + } + + SET_CYCLE; + return (_seg->speed / _seg_len); +}