From fc571c4b40e3cb42ddce7a68cbafcb65d2ea7ea4 Mon Sep 17 00:00:00 2001 From: Keith Lord Date: Fri, 29 May 2020 17:00:40 -0400 Subject: [PATCH] Substantial refactoring of base library code. (#228) * Added cycle flag to effects * added cycle flag to custom effects * initial code to separate segments and segment_runtimes * added functions to manage active segments * significant refactor of segment management * fixed active_segment pointer usage * fixed swap segment to allow completing the last frame * increased number of custom effects from 4 to 8 * refactor ws2812fx_webinterface example sketch * added setPixels() function. Co-authored-by: Keith Lord --- README.md | 2 +- .../esp8266_webinterface.ino | 13 +- examples/esp8266_webinterface/index.html.cpp | 181 ++-- examples/esp8266_webinterface/main.js.cpp | 187 ++-- .../ws2812fx_segment_sequence.ino | 105 ++ .../ws2812fx_segments_web.ino | 4 +- extras/WS2812FX change log.txt | 43 +- keywords.txt | 7 + library.json | 4 +- library.properties | 2 +- src/WS2812FX.cpp | 967 +++++++++--------- src/WS2812FX.h | 179 ++-- src/custom/BlockDissolve.h | 3 +- src/custom/DualLarson.h | 1 + src/custom/Fillerup.h | 1 + src/custom/Heartbeat.h | 1 + src/custom/ICU.h | 79 ++ src/custom/Matrix.h | 68 +- src/custom/MultiComet.h | 1 + src/custom/Oscillate.h | 4 +- src/custom/Popcorn.h | 1 + src/custom/RainbowFireworks.h | 1 + src/custom/RainbowLarson.h | 7 +- src/custom/RandomChase.h | 4 + src/custom/TriFade.h | 5 +- src/custom/TwinkleFox.h | 3 +- src/custom/VUMeter.h | 1 + 27 files changed, 1113 insertions(+), 761 deletions(-) create mode 100644 examples/ws2812fx_segment_sequence/ws2812fx_segment_sequence.ino create mode 100644 src/custom/ICU.h diff --git a/README.md b/README.md index fb84836..2235cfa 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Download, Install and Example You can **search for WS2812FX in the Arduino IDE Library Manager** or install the latest (or development) version manually: -* Install the famous [Adafruit NeoPixel library](https://github.com/adafruit/Adafruit_NeoPixel) (v1.1.4 or newer) +* Install the famous [Adafruit NeoPixel library](https://github.com/adafruit/Adafruit_NeoPixel) (v1.1.7 or newer) * Download this repository. * Extract to your Arduino libraries directory. * Open Arduino IDE. diff --git a/examples/esp8266_webinterface/esp8266_webinterface.ino b/examples/esp8266_webinterface/esp8266_webinterface.ino index 2fbb502..faad406 100644 --- a/examples/esp8266_webinterface/esp8266_webinterface.ino +++ b/examples/esp8266_webinterface/esp8266_webinterface.ino @@ -67,7 +67,7 @@ extern const char main_js[]; #define HTTP_PORT 80 #define DEFAULT_COLOR 0xFF5900 -#define DEFAULT_BRIGHTNESS 255 +#define DEFAULT_BRIGHTNESS 128 #define DEFAULT_SPEED 1000 #define DEFAULT_MODE FX_MODE_STATIC @@ -82,9 +82,8 @@ ESP8266WebServer server(HTTP_PORT); void setup(){ Serial.begin(115200); - Serial.println(); - Serial.println(); - Serial.println("Starting..."); + delay(500); + Serial.println("\n\nStarting..."); modes.reserve(5000); modes_setup(); @@ -192,9 +191,7 @@ void modes_setup() { uint8_t num_modes = sizeof(myModes) > 0 ? sizeof(myModes) : ws2812fx.getModeCount(); for(uint8_t i=0; i < num_modes; i++) { uint8_t m = sizeof(myModes) > 0 ? myModes[i] : i; - modes += "
  • "; + modes += "
  • "; modes += ws2812fx.getModeName(m); modes += "
  • "; } @@ -223,7 +220,7 @@ void srv_handle_modes() { void srv_handle_set() { for (uint8_t i=0; i < server.args(); i++){ if(server.argName(i) == "c") { - uint32_t tmp = (uint32_t) strtol(server.arg(i).c_str(), NULL, 16); + uint32_t tmp = (uint32_t) strtol(server.arg(i).c_str(), NULL, 10); if(tmp >= 0x000000 && tmp <= 0xFFFFFF) { ws2812fx.setColor(tmp); } diff --git a/examples/esp8266_webinterface/index.html.cpp b/examples/esp8266_webinterface/index.html.cpp index 1106f97..56a39cf 100644 --- a/examples/esp8266_webinterface/index.html.cpp +++ b/examples/esp8266_webinterface/index.html.cpp @@ -1,97 +1,114 @@ #include char index_html[] PROGMEM = R"=====( - - + + - - - WS2812FX Ctrl - + + WS2812FX Control

    WS2812FX Control

    - -
    -
      - - - - - - +
      + +
      +

      +
      + +
      + + +
        +
      • Speed:
      • +
      • +
      • +
      • +
      + + +
      +
      + +
      +
        +
      )====="; - diff --git a/examples/esp8266_webinterface/main.js.cpp b/examples/esp8266_webinterface/main.js.cpp index d0f99bf..3c7f54c 100644 --- a/examples/esp8266_webinterface/main.js.cpp +++ b/examples/esp8266_webinterface/main.js.cpp @@ -1,121 +1,110 @@ #include + +/* +The tiny Javascript/canvas based color picker is based on the clever work of the folks +at Sparkbox. https://seesparkbox.com/foundry/how_i_built_a_canvas_color_picker +*/ + char main_js[] PROGMEM = R"=====( -window.addEventListener('load', setup); -window.addEventListener('resize', drawColorbar); - -function handle_M_B_S(e) { - e.preventDefault(); - var name = e.target.className; - var val = e.target.id; - if(e.target.className.indexOf('m') > -1) { - elems = document.querySelectorAll('#mode li a'); - [].forEach.call(elems, function(el) { - el.classList.remove('active'); - name = e.target.className; - }); - e.target.classList.add('active'); - } - submitVal(name, val); -} -function submitVal(name, val) { +var activeButton = null; +var colorCanvas = null; + +window.addEventListener('DOMContentLoaded', (event) => { + // init the canvas color picker + colorCanvas = document.getElementById('color-canvas'); + var colorctx = colorCanvas.getContext('2d'); + + // Create color gradient + var gradient = colorctx.createLinearGradient(0, 0, colorCanvas.width - 1, 0); + gradient.addColorStop(0, "rgb(255, 0, 0)"); + gradient.addColorStop(0.16, "rgb(255, 0, 255)"); + gradient.addColorStop(0.33, "rgb(0, 0, 255)"); + gradient.addColorStop(0.49, "rgb(0, 255, 255)"); + gradient.addColorStop(0.66, "rgb(0, 255, 0)"); + gradient.addColorStop(0.82, "rgb(255, 255, 0)"); + gradient.addColorStop(1, "rgb(255, 0, 0)"); + + // Apply gradient to canvas + colorctx.fillStyle = gradient; + colorctx.fillRect(0, 0, colorCanvas.width - 1, colorCanvas.height - 1); + + // Create semi transparent gradient (white -> transparent -> black) + gradient = colorctx.createLinearGradient(0, 0, 0, colorCanvas.height - 1); + gradient.addColorStop(0, "rgba(255, 255, 255, 1)"); + gradient.addColorStop(0.48, "rgba(255, 255, 255, 0)"); + gradient.addColorStop(0.52, "rgba(0, 0, 0, 0)"); + gradient.addColorStop(1, "rgba(0, 0, 0, 1)"); + + // Apply gradient to canvas + colorctx.fillStyle = gradient; + colorctx.fillRect(0, 0, colorCanvas.width - 1, colorCanvas.height - 1); + + // setup the canvas click listener + colorCanvas.addEventListener('click', (event) => { + var imageData = colorCanvas.getContext('2d').getImageData(event.offsetX, event.offsetY, 1, 1); + + var selectedColor = 'rgb(' + imageData.data[0] + ',' + imageData.data[1] + ',' + imageData.data[2] + ')'; + //console.log('click: ' + event.offsetX + ', ' + event.offsetY + ', ' + selectedColor); + document.getElementById('color-value').value = selectedColor; + + selectedColor = imageData.data[0] * 65536 + imageData.data[1] * 256 + imageData.data[2]; + submitVal('c', selectedColor); + }); + + // get list of modes from ESP var xhttp = new XMLHttpRequest(); - xhttp.open('GET', 'set?' + name + '=' + val, true); + xhttp.onreadystatechange = function() { + if (xhttp.readyState == 4 && xhttp.status == 200) { + document.getElementById('modes').innerHTML = xhttp.responseText; + modes = document.querySelectorAll('ul#modes li a'); + modes.forEach(initMode); + } + }; + xhttp.open('GET', 'modes', true); xhttp.send(); -} +}); -function compToHex(c) { - hex = c.toString(16); - return hex.length == 1 ? '0' + hex : hex; +function initMode(mode, index) { + mode.addEventListener('click', (event) => onMode(event, index)); } -function getMousePos(can, evt) { - r = can.getBoundingClientRect(); - return { - x: evt.clientX - r.left, - y: evt.clientY - r.top - }; +function onColor(event, color) { + event.preventDefault(); + var match = color.match(/rgb\(([0-9]*),([0-9]*),([0-9]*)\)/); + if(match) { + var colorValue = Number(match[1]) * 65536 + Number(match[2]) * 256 + Number(match[3]); + //console.log('onColor:' + match[1] + "," + match[2] + "," + match[3] + "," + colorValue); + submitVal('c', colorValue); + } } -function Touch(e) { - e.preventDefault(); - pos = { - x: Math.round(e.targetTouches[0].pageX), - y: Math.round(e.targetTouches[0].pageY) - }; - rgb = ctx.getImageData(pos.x, pos.y, 1, 1).data; - drawColorbar(rgb); - submitVal('c', compToHex(rgb[0]) + compToHex(rgb[1]) + compToHex(rgb[2])); +function onMode(event, mode) { + event.preventDefault(); + if(activeButton) activeButton.classList.remove('active') + activeButton = event.target; + activeButton.classList.add('active'); + submitVal('m', mode); } -function Click(e) { - pos = getMousePos(can, e); - rgb = ctx.getImageData(pos.x, pos.y, 1, 1).data; - drawColorbar(rgb); - submitVal('c', compToHex(rgb[0]) + compToHex(rgb[1]) + compToHex(rgb[2])); +function onBrightness(event, dir) { + event.preventDefault(); + submitVal('b', dir); } -// Thanks to the backup at http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c -function rgbToHsl(r, g, b){ - r = r / 255; - g = g / 255; - b = b / 255; - var max = Math.max(r, g, b); - var min = Math.min(r, g, b); - var h, s, l = (max + min) / 2; - if(max == min) { - h = s = 0; - } else { - var d = max - min; - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - switch(max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h = h / 6; - } - return [h, s, l]; +function onSpeed(event, dir) { + event.preventDefault(); + submitVal('s', dir); } -function drawColorbar(rgb = [0, 0, 0]) { - can = document.getElementById('colorbar'); - ctx = can.getContext('2d'); - can.width = document.body.clientWidth * 0.25; - var h = can.height / 360; - - var hsl = rgbToHsl(rgb[0], rgb[1], rgb[2]); - - for(var i=0; i<=360; i++) { - ctx.fillStyle = 'hsl('+i+', 100%, 50%)'; - ctx.fillRect(0, i * h, can.width/2, h); - ctx.fillStyle = 'hsl(' + hsl[0] * 360 + ', 100%, ' + i * (100/360) + '%)'; - ctx.fillRect(can.width/2, i * h, can.width/2, h); - } +function onAuto(event, dir) { + event.preventDefault(); + submitVal('a', dir); } -function setup(){ +function submitVal(name, val) { var xhttp = new XMLHttpRequest(); - xhttp.onreadystatechange = function() { - if (xhttp.readyState == 4 && xhttp.status == 200) { - document.getElementById('mode').innerHTML = xhttp.responseText; - elems = document.querySelectorAll('ul li a'); // adds listener also to existing s and b buttons - [].forEach.call(elems, function(el) { - el.addEventListener('touchstart', handle_M_B_S, false); - el.addEventListener('click', handle_M_B_S, false); - }); - } - }; - xhttp.open('GET', 'modes', true); + xhttp.open('GET', 'set?' + name + '=' + val, true); xhttp.send(); - - var can = document.getElementById('colorbar'); - var ctx = can.getContext('2d'); - - drawColorbar(); - - can.addEventListener('touchstart', Touch, false); - can.addEventListener('click', Click, false); } )====="; - diff --git a/examples/ws2812fx_segment_sequence/ws2812fx_segment_sequence.ino b/examples/ws2812fx_segment_sequence/ws2812fx_segment_sequence.ino new file mode 100644 index 0000000..c5aacd8 --- /dev/null +++ b/examples/ws2812fx_segment_sequence/ws2812fx_segment_sequence.ino @@ -0,0 +1,105 @@ +/* + WS2812FX segment sequence demo. Creates a list of segments and dynamically + changes which segments are active. + + FEATURES + * example of creating idle segments + + + LICENSE + + The MIT License (MIT) + + Copyright (c) 2020 Keith Lord + + 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 + 2020-05-16 initial version + +*/ + +#include + +#define LED_PIN 10 // digital pin used to drive the LED strip +#define LED_COUNT 144 // number of LEDs on the strip + +#define NUM_SEGMENTS 4 // maximum total number of segments that can be created +#define NUM_ACTIVE_SEGMENTS 2 // maximum number of segments that can be actively running + +// create helper macros that define the start and end LEDs for each of our two segments +#define LOWER_SEG_RANGE 0, LED_COUNT/2 - 1 +#define UPPER_SEG_RANGE LED_COUNT/2, LED_COUNT - 1 + +// example of the new WS2812FX constructor that adds parameters for the number of +// segments allowed and the number of active segments allowed. +WS2812FX ws2812fx = WS2812FX(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800, NUM_SEGMENTS, NUM_ACTIVE_SEGMENTS); + +void setup() { + Serial.begin(115200); + + ws2812fx.init(); + ws2812fx.setBrightness(32); + + // create two active segments + ws2812fx.setSegment(0, LOWER_SEG_RANGE, FX_MODE_BLINK, YELLOW, 2000, NO_OPTIONS); + ws2812fx.setSegment(1, UPPER_SEG_RANGE, FX_MODE_BLINK, COLORS(RED, GREEN), 2000, NO_OPTIONS); + + // create additional "idle" segments that will be activated later + ws2812fx.setIdleSegment(2, LOWER_SEG_RANGE, FX_MODE_BLINK, CYAN, 2000, NO_OPTIONS); + ws2812fx.setIdleSegment(3, UPPER_SEG_RANGE, FX_MODE_BLINK, COLORS(PINK, BLUE), 2000, NO_OPTIONS); + + ws2812fx.start(); +} + +void loop() { + static unsigned long timer = millis(); + static unsigned int blink_counter = 0; + + ws2812fx.service(); + + /* the lower segment is updated based on a timer. + the lower segment will change every 10 seconds + */ + if(millis() > timer + 10000) { // every 10 seconds... + if(ws2812fx.isActiveSegment(0)) { // if seg[0] is active, switch to seg[2] + ws2812fx.swapActiveSegment(0, 2); + } else { // else, switch to seg[0] + ws2812fx.swapActiveSegment(2, 0); + } + timer = millis(); + } + + /* the upper segment is updated based on counting animation cycles. One "cycle" for the Blink + * effect is one on/off sequence. We'll change the upper segment every 5 blinks. + */ + // increment the blink counter every time seg[1] or seg[3] complete an animation cycle + if(ws2812fx.isCycle(1) || ws2812fx.isCycle(3)) { + blink_counter++; + } + if(blink_counter >= 5) { // every 5 blinks... + if(ws2812fx.isActiveSegment(1)) { // if seg[1] active, switch to seg[3] + ws2812fx.swapActiveSegment(1, 3); + } else { // else, switch to seg[1] + ws2812fx.swapActiveSegment(3, 1); + } + blink_counter = 0; + } +} diff --git a/examples/ws2812fx_segments_web/ws2812fx_segments_web.ino b/examples/ws2812fx_segments_web/ws2812fx_segments_web.ino index 0f8e058..774508d 100644 --- a/examples/ws2812fx_segments_web/ws2812fx_segments_web.ino +++ b/examples/ws2812fx_segments_web/ws2812fx_segments_web.ino @@ -198,7 +198,7 @@ void setup() { JsonObject seg = segments[i]; JsonArray colors = seg["colors"]; // the web interface sends three color values - uint32_t _colors[NUM_COLORS] = {colors[0], colors[1], colors[2]}; + uint32_t _colors[] = {colors[0], colors[1], colors[2]}; uint8_t _options = seg["options"]; ws2812fx.setSegment(i, seg["start"], seg["stop"], seg["mode"], _colors, seg["speed"], _options); } @@ -230,7 +230,7 @@ void setup() { JsonObject seg = segments[i]; JsonArray colors = seg["colors"]; // the web interface sends three color values - uint32_t _colors[NUM_COLORS] = {colors[0], colors[1], colors[2]}; + uint32_t _colors[] = {colors[0], colors[1], colors[2]}; uint8_t _options = seg["options"]; ws2812fx.setSegment(i, seg["start"], seg["stop"], seg["mode"], _colors, seg["speed"], _options); } diff --git a/extras/WS2812FX change log.txt b/extras/WS2812FX change log.txt index cd3a2bf..c517568 100644 --- a/extras/WS2812FX change log.txt +++ b/extras/WS2812FX change log.txt @@ -1,6 +1,47 @@ WS2182FX Change Log +v1.3.0 changes 5/11/2020 +------------------------ + +1) This library now makes use of the new Adafruit fill() function, + so requires Adafruit_NeoPixel library v1.1.7 or newer. + +2) Added the cycle flag to the effects that did not have it implemented. + +3) Refactored many effects, because the flash memory footprint was + getting too big to fit in my Arduino Leonardo. Some of the effects + don't look exactly the same, but they're pretty close. + +4) Changed the ICU effect from a built-in effect to a custom effect, + since it was quite large and removing it from the built-in effects + made more room for custom effects. It always seemed like a specialty + effect to me anyway. + +5) Refactored the Matrix custom effect so it can be configured from + outside of the Matrix.h file. + +6) Refactored the ws2812fx_webinterface example sketch to take advantage + of flex box layout and a more modern color picker. + +7) Added API for managing idle/active segments. + a) segment arrays are now configured at runtime instead of compile + time, allowing more flexibility in allocating SRAM for + that data (see the new WS2812FX constructor). + + b) The number of segments and the number of segment_runtimes + are independent now, allowing for more fine grained SRAM + allocation and the creation of "idle" segments that are + activated at a later time. See the new + ws2812fx_segment_sequence example sketch. + +8) Increased the maximum number of custom effects from 4 to 8. + +9) Substantial refactoring of the segment code to use global variables + instead of macros, which makes sketches significantly smaller. + + + v1.2.4 changes 5/8/2020 ------------------------ @@ -118,7 +159,7 @@ v1.1.7 changes 1/18/2019 setSegment(uint8_t, uint16_t, uint16_t, uint8_t, uint32_t, uint16_t, uint8_t) 4) Fixed a bug in the Larson Scanner effect that caused it to - crash if SEGMENT_LENGTH==1. Fixes issue #145: + crash if the length of the segment is 1. Fixes issue #145: https://github.com/kitesurfer1404/WS2812FX/issues/145 5) Updated the Rain custom effect to make use of all three colors. diff --git a/keywords.txt b/keywords.txt index 86d2c17..41db7e8 100644 --- a/keywords.txt +++ b/keywords.txt @@ -9,8 +9,11 @@ MAGENTA LITERAL1 PURPLE LITERAL1 ORANGE LITERAL1 PINK LITERAL1 +GRAY LITERAL1 ULTRAWHITE LITERAL1 +DIM LITERAL1 DARK LITERAL1 +COLORS LITERAL1 NO_OPTIONS LITERAL1 REVERSE LITERAL1 @@ -55,6 +58,10 @@ setColors KEYWORD2 setBrightness KEYWORD2 setLength KEYWORD2 setSegment KEYWORD2 +setIdleSegment KEYWORD2 +addActiveSegment KEYWORD2 +removeActiveSegment KEYWORD2 +swapActiveSegment KEYWORD2 resetSegments KEYWORD2 resetSegmentRuntimes KEYWORD2 resetSegmentRuntime KEYWORD2 diff --git a/library.json b/library.json index 476b098..b4e97b3 100644 --- a/library.json +++ b/library.json @@ -6,7 +6,7 @@ "name": "Harm Aldick", "url": "https://github.com/kitesurfer1404/WS2812FX" }, - "version": "1.2.4", + "version": "1.3.0", "downloadUrl": "https://github.com/kitesurfer1404/WS2812FX/archive/master.zip", "export": { "include": "WS2812FX-master" @@ -20,7 +20,7 @@ "dependencies": [ { "name": "Adafruit NeoPixel", - "version": ">=1.1.4" + "version": ">=1.1.7" } ], "examples": [ diff --git a/library.properties b/library.properties index 0c63cc1..c59667a 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=WS2812FX -version=1.2.4 +version=1.3.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 73baeff..3833fe8 100644 --- a/src/WS2812FX.cpp +++ b/src/WS2812FX.cpp @@ -61,7 +61,7 @@ void WS2812FX::init() { // void WS2812FX::timer() { // for (int j=0; j < 1000; j++) { -// uint16_t delay = (this->*_mode[SEGMENT.mode])(); +// uint16_t delay = (this->*_mode[_seg->mode])(); // } // } @@ -69,15 +69,19 @@ void WS2812FX::service() { if(_running || _triggered) { unsigned long now = millis(); // Be aware, millis() rolls over every 49 days bool doShow = false; - for(uint8_t i=0; i < _num_segments; i++) { - _segment_index = i; - CLR_FRAME; - if(now > SEGMENT_RUNTIME.next_time || _triggered) { - SET_FRAME; - doShow = true; - uint16_t delay = (this->*_mode[SEGMENT.mode])(); - SEGMENT_RUNTIME.next_time = now + max(delay, SPEED_MIN); - SEGMENT_RUNTIME.counter_mode_call++; + for(uint8_t i=0; i < _active_segments_len; i++) { + if(_active_segments[i] != INACTIVE_SEGMENT) { + _seg = &_segments[_active_segments[i]]; + _seg_len = (uint16_t)(_seg->stop - _seg->start + 1); + _seg_rt = &_segment_runtimes[i]; + CLR_FRAME_CYCLE; + if(now > _seg_rt->next_time || _triggered) { + SET_FRAME; + doShow = true; + uint16_t delay = (this->*_mode[_seg->mode])(); + _seg_rt->next_time = now + max(delay, SPEED_MIN); + _seg_rt->counter_mode_call++; + } } } if(doShow) { @@ -91,23 +95,15 @@ void WS2812FX::service() { // overload setPixelColor() functions so we can use gamma correction // (see https://learn.adafruit.com/led-tricks-gamma-correction/the-issue) void WS2812FX::setPixelColor(uint16_t n, uint32_t c) { - if(IS_GAMMA) { - uint8_t w = (c >> 24) & 0xFF; - uint8_t r = (c >> 16) & 0xFF; - uint8_t g = (c >> 8) & 0xFF; - uint8_t b = c & 0xFF; - Adafruit_NeoPixel::setPixelColor(n, gamma8(r), gamma8(g), gamma8(b), gamma8(w)); - } else { - Adafruit_NeoPixel::setPixelColor(n, c); - } + uint8_t w = (c >> 24) & 0xFF; + uint8_t r = (c >> 16) & 0xFF; + uint8_t g = (c >> 8) & 0xFF; + uint8_t b = c & 0xFF; + setPixelColor(n, r, g, b, w); } void WS2812FX::setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b) { - if(IS_GAMMA) { - Adafruit_NeoPixel::setPixelColor(n, gamma8(r), gamma8(g), gamma8(b)); - } else { - Adafruit_NeoPixel::setPixelColor(n, r, g, b); - } + setPixelColor(n, r, g, b, 0); } void WS2812FX::setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w) { @@ -125,13 +121,17 @@ void WS2812FX::copyPixels(uint16_t dest, uint16_t src, uint16_t count) { memmove(pixels + (dest * bytesPerPixel), pixels + (src * bytesPerPixel), count * bytesPerPixel); } +// change the underlying Adafruit_NeoPixel pixels pointer (use with care) +void WS2812FX::setPixels(uint16_t num_leds, uint8_t* ptr) { + free(Adafruit_NeoPixel::pixels); // free existing data (if any) + Adafruit_NeoPixel::pixels = ptr; + Adafruit_NeoPixel::numLEDs = num_leds; + Adafruit_NeoPixel::numBytes = num_leds * ((wOffset == rOffset) ? 3 : 4); +} + // overload show() functions so we can use custom show() void WS2812FX::show(void) { - if(customShow == NULL) { - Adafruit_NeoPixel::show(); - } else { - customShow(); - } + customShow == NULL ? Adafruit_NeoPixel::show() : customShow(); } void WS2812FX::start() { @@ -174,17 +174,16 @@ void WS2812FX::setSpeed(uint16_t s) { } void WS2812FX::setSpeed(uint8_t seg, uint16_t s) { -// resetSegmentRuntime(seg); _segments[seg].speed = constrain(s, SPEED_MIN, SPEED_MAX); } void WS2812FX::increaseSpeed(uint8_t s) { - uint16_t newSpeed = constrain(SEGMENT.speed + s, SPEED_MIN, SPEED_MAX); + uint16_t newSpeed = constrain(_seg->speed + s, SPEED_MIN, SPEED_MAX); setSpeed(newSpeed); } void WS2812FX::decreaseSpeed(uint8_t s) { - uint16_t newSpeed = constrain(SEGMENT.speed - s, SPEED_MIN, SPEED_MAX); + uint16_t newSpeed = constrain(_seg->speed - s, SPEED_MIN, SPEED_MAX); setSpeed(newSpeed); } @@ -201,13 +200,11 @@ void WS2812FX::setColor(uint32_t c) { } void WS2812FX::setColor(uint8_t seg, uint32_t c) { -// resetSegmentRuntime(seg); _segments[seg].colors[0] = c; } void WS2812FX::setColors(uint8_t seg, uint32_t* c) { -// resetSegmentRuntime(seg); - for(uint8_t i=0; i _segments[0].stop - _segments[0].start + 1) s = 1; - s = _segments[0].stop - _segments[0].start + 1 - s; - - for(uint16_t i=_segments[0].start + s; i <= (_segments[0].stop - _segments[0].start + 1); i++) { - setPixelColor(i, 0); - } + uint16_t seglen = _segments[0].stop - _segments[0].start + 1; + fill(BLACK, _segments[0].start, seglen); show(); - setLength(s); + if (s < seglen) setLength(seglen - s); } boolean WS2812FX::isRunning() { @@ -271,16 +264,24 @@ boolean WS2812FX::isFrame() { return isFrame(0); } -boolean WS2812FX::isFrame(uint8_t segIndex) { - return (_segment_runtimes[segIndex].aux_param2 & FRAME); +boolean WS2812FX::isFrame(uint8_t seg) { + uint8_t* ptr = (uint8_t*)memchr(_active_segments, seg, _active_segments_len); + if(ptr == NULL) return false; // segment not active + return (_segment_runtimes[ptr - _active_segments].aux_param2 & FRAME); } boolean WS2812FX::isCycle() { return isCycle(0); } -boolean WS2812FX::isCycle(uint8_t segIndex) { - return (_segment_runtimes[segIndex].aux_param2 & CYCLE); +boolean WS2812FX::isCycle(uint8_t seg) { + uint8_t* ptr = (uint8_t*)memchr(_active_segments, seg, _active_segments_len); + if(ptr == NULL) return false; // segment not active + return (_segment_runtimes[ptr - _active_segments].aux_param2 & CYCLE); +} + +void WS2812FX::setCycle() { + SET_CYCLE; } uint8_t WS2812FX::getMode(void) { @@ -299,7 +300,6 @@ uint16_t WS2812FX::getSpeed(uint8_t seg) { return _segments[seg].speed; } - uint8_t WS2812FX::getOptions(uint8_t seg) { return _segments[seg].options; } @@ -341,7 +341,7 @@ uint32_t* WS2812FX::getColors(uint8_t seg) { } WS2812FX::Segment* WS2812FX::getSegment(void) { - return &_segments[_segment_index]; + return _seg; } WS2812FX::Segment* WS2812FX::getSegment(uint8_t seg) { @@ -353,17 +353,23 @@ WS2812FX::Segment* WS2812FX::getSegments(void) { } WS2812FX::Segment_runtime* WS2812FX::getSegmentRuntime(void) { - return &_segment_runtimes[_segment_index]; + return _seg_rt; } WS2812FX::Segment_runtime* WS2812FX::getSegmentRuntime(uint8_t seg) { - return &_segment_runtimes[seg]; + uint8_t* ptr = (uint8_t*)memchr(_active_segments, seg, _active_segments_len); + if(ptr == NULL) return NULL; // segment not active + return &_segment_runtimes[ptr - _active_segments]; } WS2812FX::Segment_runtime* WS2812FX::getSegmentRuntimes(void) { return _segment_runtimes; } +uint8_t* WS2812FX::getActiveSegments(void) { + return _active_segments; +} + const __FlashStringHelper* WS2812FX::getModeName(uint8_t m) { if(m < MODE_COUNT) { return _names[m]; @@ -372,22 +378,32 @@ const __FlashStringHelper* WS2812FX::getModeName(uint8_t m) { } } -void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint16_t speed, bool reverse) { +void WS2812FX::setIdleSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint16_t speed, uint8_t options) { uint32_t colors[] = {color, 0, 0}; - setSegment(n, start, stop, mode, colors, speed, reverse); + setIdleSegment(n, start, stop, mode, colors, speed, options); } -void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint16_t speed, uint8_t options) { - uint32_t colors[] = {color, 0, 0}; +void WS2812FX::setIdleSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint16_t speed, uint8_t options) { setSegment(n, start, stop, mode, colors, speed, options); + if(n < _active_segments_len) removeActiveSegment(n);; +} + +void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint16_t speed, bool reverse) { + uint32_t colors[] = {color, 0, 0}; + setSegment(n, start, stop, mode, colors, speed, (uint8_t)(reverse ? REVERSE : NO_OPTIONS)); } void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint16_t speed, bool reverse) { setSegment(n, start, stop, mode, colors, speed, (uint8_t)(reverse ? REVERSE : NO_OPTIONS)); } +void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint16_t speed, uint8_t options) { + uint32_t colors[] = {color, 0, 0}; + setSegment(n, start, stop, mode, colors, speed, options); +} + void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint16_t speed, uint8_t options) { - if(n < (sizeof(_segments) / sizeof(_segments[0]))) { + if(n < _segments_len) { if(n + 1 > _num_segments) _num_segments = n + 1; _segments[n].start = start; _segments[n].stop = stop; @@ -395,34 +411,75 @@ void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode _segments[n].speed = speed; _segments[n].options = options; - for(uint8_t i=0; i g -> b -> back to r @@ -450,7 +506,6 @@ uint32_t WS2812FX::color_wheel(uint8_t pos) { } } - /* * Returns a new, random wheel index with a minimum distance of 42 from pos. */ @@ -479,7 +534,7 @@ uint8_t WS2812FX::random8() { // note random8(lim) generates numbers in the range 0 to (lim -1) uint8_t WS2812FX::random8(uint8_t lim) { uint8_t r = random8(); - r = (r * lim) >> 8; + r = ((uint16_t)r * lim) >> 8; return r; } @@ -524,14 +579,20 @@ uint32_t* WS2812FX::intensitySums() { return intensities; } + +/* ##################################################### +# +# Mode Functions +# +##################################################### */ + /* * No blinking. Just plain old static light. */ uint16_t WS2812FX::mode_static(void) { - for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { - setPixelColor(i, SEGMENT.colors[0]); - } - return SEGMENT.speed; + fill(_seg->colors[0], _seg->start, _seg_len); + SET_CYCLE; + return _seg->speed; } @@ -541,16 +602,15 @@ uint16_t WS2812FX::mode_static(void) { * if(strobe == true) then create a strobe effect */ uint16_t WS2812FX::blink(uint32_t color1, uint32_t color2, bool strobe) { - uint32_t color = ((SEGMENT_RUNTIME.counter_mode_call & 1) == 0) ? color1 : color2; - if(IS_REVERSE) color = (color == color1) ? color2 : color1; - for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { - setPixelColor(i, color); - } - - if((SEGMENT_RUNTIME.counter_mode_call & 1) == 0) { - return strobe ? 20 : (SEGMENT.speed / 2); + 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 { - return strobe ? SEGMENT.speed - 20 : (SEGMENT.speed / 2); + uint32_t color = (IS_REVERSE) ? color2 : color1; // on + fill(color, _seg->start, _seg_len); + return strobe ? 20 : (_seg->speed / 2); } } @@ -559,7 +619,7 @@ uint16_t WS2812FX::blink(uint32_t color1, uint32_t color2, bool strobe) { * Normal blinking. 50% on/off time. */ uint16_t WS2812FX::mode_blink(void) { - return blink(SEGMENT.colors[0], SEGMENT.colors[1], false); + return blink(_seg->colors[0], _seg->colors[1], false); } @@ -567,7 +627,7 @@ uint16_t WS2812FX::mode_blink(void) { * Classic Blink effect. Cycling through the rainbow. */ uint16_t WS2812FX::mode_blink_rainbow(void) { - return blink(color_wheel(SEGMENT_RUNTIME.counter_mode_call & 0xFF), SEGMENT.colors[1], false); + return blink(color_wheel(_seg_rt->counter_mode_call & 0xFF), _seg->colors[1], false); } @@ -575,7 +635,7 @@ uint16_t WS2812FX::mode_blink_rainbow(void) { * Classic Strobe effect. */ uint16_t WS2812FX::mode_strobe(void) { - return blink(SEGMENT.colors[0], SEGMENT.colors[1], true); + return blink(_seg->colors[0], _seg->colors[1], true); } @@ -583,7 +643,7 @@ uint16_t WS2812FX::mode_strobe(void) { * Classic Strobe effect. Cycling through the rainbow. */ uint16_t WS2812FX::mode_strobe_rainbow(void) { - return blink(color_wheel(SEGMENT_RUNTIME.counter_mode_call & 0xFF), SEGMENT.colors[1], true); + return blink(color_wheel(_seg_rt->counter_mode_call & 0xFF), _seg->colors[1], true); } @@ -593,46 +653,46 @@ uint16_t WS2812FX::mode_strobe_rainbow(void) { * 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(SEGMENT_RUNTIME.counter_mode_step < SEGMENT_LENGTH) { - uint32_t led_offset = SEGMENT_RUNTIME.counter_mode_step; + if(_seg_rt->counter_mode_step < _seg_len) { + uint32_t led_offset = _seg_rt->counter_mode_step; if(IS_REVERSE) { - setPixelColor(SEGMENT.stop - led_offset, color1); + setPixelColor(_seg->stop - led_offset, color1); } else { - setPixelColor(SEGMENT.start + led_offset, color1); + setPixelColor(_seg->start + led_offset, color1); } } else { - uint32_t led_offset = SEGMENT_RUNTIME.counter_mode_step - SEGMENT_LENGTH; + uint32_t led_offset = _seg_rt->counter_mode_step - _seg_len; if((IS_REVERSE && !rev) || (!IS_REVERSE && rev)) { - setPixelColor(SEGMENT.stop - led_offset, color2); + setPixelColor(_seg->stop - led_offset, color2); } else { - setPixelColor(SEGMENT.start + led_offset, color2); + setPixelColor(_seg->start + led_offset, color2); } } - if(SEGMENT_RUNTIME.counter_mode_step % SEGMENT_LENGTH == 0) SET_CYCLE; - else CLR_CYCLE; + _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) % (_seg_len * 2); - SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) % (SEGMENT_LENGTH * 2); - return (SEGMENT.speed / (SEGMENT_LENGTH * 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(SEGMENT.colors[0], SEGMENT.colors[1], false); + return color_wipe(_seg->colors[0], _seg->colors[1], false); } uint16_t WS2812FX::mode_color_wipe_inv(void) { - return color_wipe(SEGMENT.colors[1], SEGMENT.colors[0], false); + return color_wipe(_seg->colors[1], _seg->colors[0], false); } uint16_t WS2812FX::mode_color_wipe_rev(void) { - return color_wipe(SEGMENT.colors[0], SEGMENT.colors[1], true); + return color_wipe(_seg->colors[0], _seg->colors[1], true); } uint16_t WS2812FX::mode_color_wipe_rev_inv(void) { - return color_wipe(SEGMENT.colors[1], SEGMENT.colors[0], true); + return color_wipe(_seg->colors[1], _seg->colors[0], true); } @@ -641,10 +701,10 @@ uint16_t WS2812FX::mode_color_wipe_rev_inv(void) { * Then starts over with another color. */ uint16_t WS2812FX::mode_color_wipe_random(void) { - if(SEGMENT_RUNTIME.counter_mode_step % SEGMENT_LENGTH == 0) { // aux_param will store our random color wheel index - SEGMENT_RUNTIME.aux_param = get_random_wheel_index(SEGMENT_RUNTIME.aux_param); + 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(SEGMENT_RUNTIME.aux_param); + uint32_t color = color_wheel(_seg_rt->aux_param); return color_wipe(color, color, false) * 2; } @@ -653,10 +713,10 @@ uint16_t WS2812FX::mode_color_wipe_random(void) { * Random color introduced alternating from start and end of strip. */ uint16_t WS2812FX::mode_color_sweep_random(void) { - if(SEGMENT_RUNTIME.counter_mode_step % SEGMENT_LENGTH == 0) { // aux_param will store our random color wheel index - SEGMENT_RUNTIME.aux_param = get_random_wheel_index(SEGMENT_RUNTIME.aux_param); + 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(SEGMENT_RUNTIME.aux_param); + uint32_t color = color_wheel(_seg_rt->aux_param); return color_wipe(color, color, true) * 2; } @@ -666,13 +726,11 @@ uint16_t WS2812FX::mode_color_sweep_random(void) { * to the next random color. */ uint16_t WS2812FX::mode_random_color(void) { - SEGMENT_RUNTIME.aux_param = get_random_wheel_index(SEGMENT_RUNTIME.aux_param); // aux_param will store our random color wheel index - uint32_t color = color_wheel(SEGMENT_RUNTIME.aux_param); - - for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { - setPixelColor(i, color); - } - return SEGMENT.speed; + _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; } @@ -681,14 +739,15 @@ uint16_t WS2812FX::mode_random_color(void) { * to another random color. */ uint16_t WS2812FX::mode_single_dynamic(void) { - if(SEGMENT_RUNTIME.counter_mode_call == 0) { - for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + if(_seg_rt->counter_mode_call == 0) { + for(uint16_t i=_seg->start; i <= _seg->stop; i++) { setPixelColor(i, color_wheel(random8())); } } - setPixelColor(SEGMENT.start + random16(SEGMENT_LENGTH), color_wheel(random8())); - return SEGMENT.speed; + setPixelColor(_seg->start + random16(_seg_len), color_wheel(random8())); + SET_CYCLE; + return _seg->speed; } @@ -697,10 +756,11 @@ uint16_t WS2812FX::mode_single_dynamic(void) { * to new random colors. */ uint16_t WS2812FX::mode_multi_dynamic(void) { - for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + for(uint16_t i=_seg->start; i <= _seg->stop; i++) { setPixelColor(i, color_wheel(random8())); } - return SEGMENT.speed; + SET_CYCLE; + return _seg->speed; } @@ -709,7 +769,7 @@ uint16_t WS2812FX::mode_multi_dynamic(void) { * Use mode "fade" if you like to have something similar with a different speed. */ uint16_t WS2812FX::mode_breath(void) { - int lum = SEGMENT_RUNTIME.counter_mode_step; + int lum = _seg_rt->counter_mode_step; if(lum > 255) lum = 511 - lum; // lum = 15 -> 255 -> 15 uint16_t delay; @@ -722,17 +782,14 @@ uint16_t WS2812FX::mode_breath(void) { else if(lum <= 150) delay = 11; // 5 else delay = 10; // 4 - uint32_t color = SEGMENT.colors[0]; - uint8_t w = (color >> 24 & 0xFF) * lum / 256; - uint8_t r = (color >> 16 & 0xFF) * lum / 256; - uint8_t g = (color >> 8 & 0xFF) * lum / 256; - uint8_t b = (color & 0xFF) * lum / 256; - for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { - setPixelColor(i, r, g, b, w); - } + uint32_t color = color_blend(_seg->colors[1], _seg->colors[0], lum); + fill(color, _seg->start, _seg_len); - SEGMENT_RUNTIME.counter_mode_step += 2; - if(SEGMENT_RUNTIME.counter_mode_step > (512-15)) SEGMENT_RUNTIME.counter_mode_step = 15; + _seg_rt->counter_mode_step += 2; + if(_seg_rt->counter_mode_step > (512-15)) { + _seg_rt->counter_mode_step = 15; + SET_CYCLE; + } return delay; } @@ -741,17 +798,18 @@ uint16_t WS2812FX::mode_breath(void) { * Fades the LEDs between two colors */ uint16_t WS2812FX::mode_fade(void) { - int lum = SEGMENT_RUNTIME.counter_mode_step; + int lum = _seg_rt->counter_mode_step; if(lum > 255) lum = 511 - lum; // lum = 0 -> 255 -> 0 - uint32_t color = color_blend(SEGMENT.colors[0], SEGMENT.colors[1], lum); - for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { - setPixelColor(i, color); - } + uint32_t color = color_blend(_seg->colors[1], _seg->colors[0], lum); + fill(color, _seg->start, _seg_len); - SEGMENT_RUNTIME.counter_mode_step += 4; - if(SEGMENT_RUNTIME.counter_mode_step > 511) SEGMENT_RUNTIME.counter_mode_step = 0; - return (SEGMENT.speed / 128); + _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); } @@ -759,27 +817,28 @@ uint16_t WS2812FX::mode_fade(void) { * 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 = SEGMENT_RUNTIME.aux_param ? -1 : 1; + int8_t dir = _seg_rt->aux_param ? -1 : 1; uint8_t size = 1 << SIZE_OPTION; - for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { - setPixelColor(i, color2); - } + fill(color2, _seg->start, _seg_len); for(uint8_t i = 0; i < size; i++) { if(IS_REVERSE || dual) { - setPixelColor(SEGMENT.stop - SEGMENT_RUNTIME.counter_mode_step - i, color1); + setPixelColor(_seg->stop - _seg_rt->counter_mode_step - i, color1); } if(!IS_REVERSE || dual) { - setPixelColor(SEGMENT.start + SEGMENT_RUNTIME.counter_mode_step + i, color1); + setPixelColor(_seg->start + _seg_rt->counter_mode_step + i, color1); } } - SEGMENT_RUNTIME.counter_mode_step += dir; - if(SEGMENT_RUNTIME.counter_mode_step == 0) SEGMENT_RUNTIME.aux_param = 0; - if(SEGMENT_RUNTIME.counter_mode_step >= (uint16_t)(SEGMENT_LENGTH - size)) SEGMENT_RUNTIME.aux_param = 1; + _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 (SEGMENT.speed / (SEGMENT_LENGTH * 2)); + return (_seg->speed / (_seg_len * 2)); } @@ -787,7 +846,7 @@ uint16_t WS2812FX::scan(uint32_t color1, uint32_t color2, bool dual) { * Runs a block of pixels back and forth. */ uint16_t WS2812FX::mode_scan(void) { - return scan(SEGMENT.colors[0], SEGMENT.colors[1], false); + return scan(_seg->colors[0], _seg->colors[1], false); } @@ -795,7 +854,7 @@ uint16_t WS2812FX::mode_scan(void) { * Runs two blocks of pixels back and forth in opposite directions. */ uint16_t WS2812FX::mode_dual_scan(void) { - return scan(SEGMENT.colors[0], SEGMENT.colors[1], true); + return scan(_seg->colors[0], _seg->colors[1], true); } @@ -803,13 +862,14 @@ uint16_t WS2812FX::mode_dual_scan(void) { * Cycles all LEDs at once through a rainbow. */ uint16_t WS2812FX::mode_rainbow(void) { - uint32_t color = color_wheel(SEGMENT_RUNTIME.counter_mode_step); - for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { - setPixelColor(i, color); - } + uint32_t color = color_wheel(_seg_rt->counter_mode_step); + fill(color, _seg->start, _seg_len); - SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) & 0xFF; - return (SEGMENT.speed / 256); + _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); } @@ -817,13 +877,61 @@ uint16_t WS2812FX::mode_rainbow(void) { * Cycles a rainbow over the entire string of LEDs. */ uint16_t WS2812FX::mode_rainbow_cycle(void) { - for(uint16_t i=0; i < SEGMENT_LENGTH; i++) { - uint32_t color = color_wheel(((i * 256 / SEGMENT_LENGTH) + SEGMENT_RUNTIME.counter_mode_step) & 0xFF); - setPixelColor(SEGMENT.start + i, color); + for(uint16_t i=0; i < _seg_len; i++) { + uint32_t color = color_wheel(((i * 256 / _seg_len) + _seg_rt->counter_mode_step) & 0xFF); + setPixelColor(_seg->start + i, color); } - SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) & 0xFF; - return (SEGMENT.speed / 256); + _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); } @@ -832,7 +940,7 @@ uint16_t WS2812FX::mode_rainbow_cycle(void) { * Inspired by the Adafruit examples. */ uint16_t WS2812FX::mode_theater_chase(void) { - return tricolor_chase(SEGMENT.colors[0], SEGMENT.colors[1], SEGMENT.colors[1]); + return tricolor_chase(_seg->colors[0], _seg->colors[1], _seg->colors[1]); } @@ -841,9 +949,9 @@ uint16_t WS2812FX::mode_theater_chase(void) { * Inspired by the Adafruit examples. */ uint16_t WS2812FX::mode_theater_chase_rainbow(void) { - SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) & 0xFF; - uint32_t color = color_wheel(SEGMENT_RUNTIME.counter_mode_step); - return tricolor_chase(color, SEGMENT.colors[1], SEGMENT.colors[1]); + _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) & 0xFF; + uint32_t color = color_wheel(_seg_rt->counter_mode_step); + return tricolor_chase(color, _seg->colors[1], _seg->colors[1]); } @@ -852,18 +960,19 @@ uint16_t WS2812FX::mode_theater_chase_rainbow(void) { */ uint16_t WS2812FX::mode_running_lights(void) { uint8_t size = 1 << SIZE_OPTION; - uint8_t sineIncr = max(1, (256 / SEGMENT_LENGTH) * size); - for(uint16_t i=0; i < SEGMENT_LENGTH; i++) { - int lum = (int)sine8(((i + SEGMENT_RUNTIME.counter_mode_step) * sineIncr)); - uint32_t color = color_blend(SEGMENT.colors[0], SEGMENT.colors[1], lum); + 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(SEGMENT.start + i, color); + setPixelColor(_seg->start + i, color); } else { - setPixelColor(SEGMENT.stop - i, color); + setPixelColor(_seg->stop - i, color); } } - SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) % 256; - return (SEGMENT.speed / SEGMENT_LENGTH); + _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); } @@ -871,19 +980,17 @@ uint16_t WS2812FX::mode_running_lights(void) { * twinkle function */ uint16_t WS2812FX::twinkle(uint32_t color1, uint32_t color2) { - if(SEGMENT_RUNTIME.counter_mode_step == 0) { - for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { - setPixelColor(i, color2); - } - uint16_t min_leds = max(1, SEGMENT_LENGTH / 5); // make sure, at least one LED is on - uint16_t max_leds = max(1, SEGMENT_LENGTH / 2); // make sure, at least one LED is on - SEGMENT_RUNTIME.counter_mode_step = random(min_leds, max_leds); + 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(SEGMENT.start + random16(SEGMENT_LENGTH), color1); + setPixelColor(_seg->start + random16(_seg_len), color1); - SEGMENT_RUNTIME.counter_mode_step--; - return (SEGMENT.speed / SEGMENT_LENGTH); + _seg_rt->counter_mode_step--; + return (_seg->speed / _seg_len); } /* @@ -891,7 +998,7 @@ uint16_t WS2812FX::twinkle(uint32_t color1, uint32_t color2) { * Inspired by www.tweaking4all.com/hardware/arduino/arduino-led-strip-effects/ */ uint16_t WS2812FX::mode_twinkle(void) { - return twinkle(SEGMENT.colors[0], SEGMENT.colors[1]); + return twinkle(_seg->colors[0], _seg->colors[1]); } /* @@ -899,7 +1006,7 @@ uint16_t WS2812FX::mode_twinkle(void) { * Inspired by www.tweaking4all.com/hardware/arduino/arduino-led-strip-effects/ */ uint16_t WS2812FX::mode_twinkle_random(void) { - return twinkle(color_wheel(random8()), SEGMENT.colors[1]); + return twinkle(color_wheel(random8()), _seg->colors[1]); } @@ -907,7 +1014,7 @@ uint16_t WS2812FX::mode_twinkle_random(void) { * fade out functions */ void WS2812FX::fade_out() { - return fade_out(SEGMENT.colors[1]); + return fade_out(_seg->colors[1]); } void WS2812FX::fade_out(uint32_t targetColor) { @@ -924,7 +1031,7 @@ void WS2812FX::fade_out(uint32_t targetColor) { int g2 = (color >> 8) & 0xff; int b2 = color & 0xff; - for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + 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); @@ -987,12 +1094,11 @@ uint16_t WS2812FX::twinkle_fade(uint32_t color) { if(random8(3) == 0) { uint8_t size = 1 << SIZE_OPTION; - uint16_t index = SEGMENT.start + random16(SEGMENT_LENGTH - size); - for(uint8_t i=0; istart + random16(_seg_len - size); + fill(color, index, size); + SET_CYCLE; } - return (SEGMENT.speed / 8); + return (_seg->speed / 8); } @@ -1000,7 +1106,7 @@ uint16_t WS2812FX::twinkle_fade(uint32_t color) { * Blink several LEDs on, fading out. */ uint16_t WS2812FX::mode_twinkle_fade(void) { - return twinkle_fade(SEGMENT.colors[0]); + return twinkle_fade(_seg->colors[0]); } @@ -1011,21 +1117,33 @@ 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); // 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) { - uint8_t size = 1 << SIZE_OPTION; - for(uint8_t i=0; icolors[1], _seg->colors[0]); } @@ -1034,20 +1152,7 @@ uint16_t WS2812FX::mode_sparkle(void) { * Inspired by www.tweaking4all.com/hardware/arduino/arduino-led-strip-effects/ */ uint16_t WS2812FX::mode_flash_sparkle(void) { - for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { - setPixelColor(i, SEGMENT.colors[0]); - } - - if(random8(5) == 0) { - uint8_t size = 1 << SIZE_OPTION; - uint16_t index = SEGMENT.start + random16(SEGMENT_LENGTH - size); - for(uint8_t j=0; jcolors[0], WHITE); } @@ -1056,17 +1161,15 @@ uint16_t WS2812FX::mode_flash_sparkle(void) { * Inspired by www.tweaking4all.com/hardware/arduino/arduino-led-strip-effects/ */ uint16_t WS2812FX::mode_hyper_sparkle(void) { - for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { - setPixelColor(i, SEGMENT.colors[0]); - } + fill(_seg->colors[0], _seg->start, _seg_len); - if(random8(5) < 2) { - for(uint16_t i=0; i < max(1, SEGMENT_LENGTH/3); i++) { - setPixelColor(SEGMENT.start + random16(SEGMENT_LENGTH), WHITE); - } - return 20; + uint8_t size = 1 << SIZE_OPTION; + for(uint8_t i=0; i<8; i++) { + fill(WHITE, _seg->start + random16(_seg_len - size), size); } - return SEGMENT.speed; + + SET_CYCLE; + return (_seg->speed / 32); } @@ -1074,23 +1177,21 @@ uint16_t WS2812FX::mode_hyper_sparkle(void) { * Strobe effect with different strobe count and pause, controlled by speed. */ uint16_t WS2812FX::mode_multi_strobe(void) { - for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { - setPixelColor(i, BLACK); - } + fill(_seg->colors[1], _seg->start, _seg_len); - uint16_t delay = 200 + ((9 - (SEGMENT.speed % 10)) * 100); - uint16_t count = 2 * ((SEGMENT.speed / 100) + 1); - if(SEGMENT_RUNTIME.counter_mode_step < count) { - if((SEGMENT_RUNTIME.counter_mode_step & 1) == 0) { - for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { - setPixelColor(i, SEGMENT.colors[0]); - } + 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; } } - SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) % (count + 1); + + _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) % (count + 1); + if(_seg_rt->counter_mode_step == 0) SET_CYCLE; return delay; } @@ -1100,29 +1201,27 @@ uint16_t WS2812FX::mode_multi_strobe(void) { * color1 = background color * color2 and color3 = colors of two adjacent leds */ - uint16_t WS2812FX::chase(uint32_t color1, uint32_t color2, uint32_t color3) { 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(SEGMENT.stop - a, color1); - setPixelColor(SEGMENT.stop - b, color2); - setPixelColor(SEGMENT.stop - c, color3); + setPixelColor(_seg->stop - a, color1); + setPixelColor(_seg->stop - b, color2); + setPixelColor(_seg->stop - c, color3); } else { - setPixelColor(SEGMENT.start + a, color1); - setPixelColor(SEGMENT.start + b, color2); - setPixelColor(SEGMENT.start + c, color3); + setPixelColor(_seg->start + a, color1); + setPixelColor(_seg->start + b, color2); + setPixelColor(_seg->start + c, color3); } } - if(SEGMENT_RUNTIME.counter_mode_step + (size * 3) == SEGMENT_LENGTH) SET_CYCLE; - else CLR_CYCLE; + if(_seg_rt->counter_mode_step + (size * 3) == _seg_len) SET_CYCLE; - SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) % SEGMENT_LENGTH; - return (SEGMENT.speed / SEGMENT_LENGTH); + _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) % _seg_len; + return (_seg->speed / _seg_len); } @@ -1130,7 +1229,7 @@ uint16_t WS2812FX::chase(uint32_t color1, uint32_t color2, uint32_t color3) { * Bicolor chase mode */ uint16_t WS2812FX::mode_bicolor_chase(void) { - return chase(SEGMENT.colors[0], SEGMENT.colors[1], SEGMENT.colors[2]); + return chase(_seg->colors[0], _seg->colors[1], _seg->colors[2]); } @@ -1138,7 +1237,7 @@ uint16_t WS2812FX::mode_bicolor_chase(void) { * White running on _color. */ uint16_t WS2812FX::mode_chase_color(void) { - return chase(SEGMENT.colors[0], WHITE, WHITE); + return chase(_seg->colors[0], WHITE, WHITE); } @@ -1146,7 +1245,7 @@ uint16_t WS2812FX::mode_chase_color(void) { * Black running on _color. */ uint16_t WS2812FX::mode_chase_blackout(void) { - return chase(SEGMENT.colors[0], BLACK, BLACK); + return chase(_seg->colors[0], BLACK, BLACK); } @@ -1154,7 +1253,7 @@ uint16_t WS2812FX::mode_chase_blackout(void) { * _color running on white. */ uint16_t WS2812FX::mode_chase_white(void) { - return chase(WHITE, SEGMENT.colors[0], SEGMENT.colors[0]); + return chase(WHITE, _seg->colors[0], _seg->colors[0]); } @@ -1162,10 +1261,10 @@ uint16_t WS2812FX::mode_chase_white(void) { * White running followed by random color. */ uint16_t WS2812FX::mode_chase_random(void) { - if(SEGMENT_RUNTIME.counter_mode_step == 0) { - SEGMENT_RUNTIME.aux_param = get_random_wheel_index(SEGMENT_RUNTIME.aux_param); + if(_seg_rt->counter_mode_step == 0) { + _seg_rt->aux_param = get_random_wheel_index(_seg_rt->aux_param); } - return chase(color_wheel(SEGMENT_RUNTIME.aux_param), WHITE, WHITE); + return chase(color_wheel(_seg_rt->aux_param), WHITE, WHITE); } @@ -1173,10 +1272,10 @@ uint16_t WS2812FX::mode_chase_random(void) { * Rainbow running on white. */ uint16_t WS2812FX::mode_chase_rainbow_white(void) { - uint16_t n = SEGMENT_RUNTIME.counter_mode_step; - uint16_t m = (SEGMENT_RUNTIME.counter_mode_step + 1) % SEGMENT_LENGTH; - uint32_t color2 = color_wheel(((n * 256 / SEGMENT_LENGTH) + (SEGMENT_RUNTIME.counter_mode_call & 0xFF)) & 0xFF); - uint32_t color3 = color_wheel(((m * 256 / SEGMENT_LENGTH) + (SEGMENT_RUNTIME.counter_mode_call & 0xFF)) & 0xFF); + 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); } @@ -1186,9 +1285,9 @@ uint16_t WS2812FX::mode_chase_rainbow_white(void) { * White running on rainbow. */ uint16_t WS2812FX::mode_chase_rainbow(void) { - uint8_t color_sep = 256 / SEGMENT_LENGTH; - uint8_t color_index = SEGMENT_RUNTIME.counter_mode_call & 0xFF; - uint32_t color = color_wheel(((SEGMENT_RUNTIME.counter_mode_step * color_sep) + color_index) & 0xFF); + 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); } @@ -1198,45 +1297,50 @@ uint16_t WS2812FX::mode_chase_rainbow(void) { * Black running on rainbow. */ uint16_t WS2812FX::mode_chase_blackout_rainbow(void) { - uint8_t color_sep = 256 / SEGMENT_LENGTH; - uint8_t color_index = SEGMENT_RUNTIME.counter_mode_call & 0xFF; - uint32_t color = color_wheel(((SEGMENT_RUNTIME.counter_mode_step * color_sep) + color_index) & 0xFF); + 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. + * running white flashes function. + * color1 = background color + * color2 = flash color */ -uint16_t WS2812FX::mode_chase_flash(void) { +uint16_t WS2812FX::chase_flash(uint32_t color1, uint32_t color2) { const static uint8_t flash_count = 4; - uint8_t flash_step = SEGMENT_RUNTIME.counter_mode_call % ((flash_count * 2) + 1); - - for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { - setPixelColor(i, SEGMENT.colors[0]); - } + uint8_t flash_step = _seg_rt->counter_mode_call % ((flash_count * 2) + 1); - uint16_t delay = (SEGMENT.speed / SEGMENT_LENGTH); if(flash_step < (flash_count * 2)) { - if(flash_step % 2 == 0) { - uint16_t n = SEGMENT_RUNTIME.counter_mode_step; - uint16_t m = (SEGMENT_RUNTIME.counter_mode_step + 1) % SEGMENT_LENGTH; - if(IS_REVERSE) { - setPixelColor(SEGMENT.stop - n, WHITE); - setPixelColor(SEGMENT.stop - m, WHITE); - } else { - setPixelColor(SEGMENT.start + n, WHITE); - setPixelColor(SEGMENT.start + m, WHITE); - } - delay = 20; + 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 { - delay = 30; + setPixelColor(_seg->start + n, color); + setPixelColor(_seg->start + m, color); } + return 30; } else { - SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) % SEGMENT_LENGTH; + _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 delay; + return (_seg->speed / _seg_len); +} + +/* + * White flashes running on _color. + */ +uint16_t WS2812FX::mode_chase_flash(void) { + return chase_flash(_seg->colors[0], WHITE); } @@ -1244,34 +1348,7 @@ uint16_t WS2812FX::mode_chase_flash(void) { * White flashes running, followed by random color. */ uint16_t WS2812FX::mode_chase_flash_random(void) { - const static uint8_t flash_count = 4; - uint8_t flash_step = SEGMENT_RUNTIME.counter_mode_call % ((flash_count * 2) + 1); - - for(uint16_t i=0; i < SEGMENT_RUNTIME.counter_mode_step; i++) { - setPixelColor(SEGMENT.start + i, color_wheel(SEGMENT_RUNTIME.aux_param)); - } - - uint16_t delay = (SEGMENT.speed / SEGMENT_LENGTH); - if(flash_step < (flash_count * 2)) { - uint16_t n = SEGMENT_RUNTIME.counter_mode_step; - uint16_t m = (SEGMENT_RUNTIME.counter_mode_step + 1) % SEGMENT_LENGTH; - if(flash_step % 2 == 0) { - setPixelColor(SEGMENT.start + n, WHITE); - setPixelColor(SEGMENT.start + m, WHITE); - delay = 20; - } else { - setPixelColor(SEGMENT.start + n, color_wheel(SEGMENT_RUNTIME.aux_param)); - setPixelColor(SEGMENT.start + m, BLACK); - delay = 30; - } - } else { - SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) % SEGMENT_LENGTH; - - if(SEGMENT_RUNTIME.counter_mode_step == 0) { - SEGMENT_RUNTIME.aux_param = get_random_wheel_index(SEGMENT_RUNTIME.aux_param); - } - } - return delay; + return chase_flash(color_wheel(_seg_rt->aux_param), WHITE); } @@ -1279,32 +1356,28 @@ uint16_t WS2812FX::mode_chase_flash_random(void) { * Alternating pixels running function. */ uint16_t WS2812FX::running(uint32_t color1, uint32_t color2) { - uint8_t size = 4 << SIZE_OPTION; - for(uint16_t i=0; i < SEGMENT_LENGTH; i++) { - if((i + SEGMENT_RUNTIME.counter_mode_step) % size < (size / 2)) { - if(IS_REVERSE) { - setPixelColor(SEGMENT.start + i, color1); - } else { - setPixelColor(SEGMENT.stop - i, color1); - } - } else { - if(IS_REVERSE) { - setPixelColor(SEGMENT.start + i, color2); - } else { - setPixelColor(SEGMENT.stop - i, 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); } - SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) % size; - return (SEGMENT.speed / SEGMENT_LENGTH); + _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(SEGMENT.colors[0], WHITE); + return running(_seg->colors[0], WHITE); } @@ -1335,23 +1408,14 @@ uint16_t WS2812FX::mode_halloween(void) { * Random colored pixels running. */ uint16_t WS2812FX::mode_running_random(void) { - if(IS_REVERSE) { - copyPixels(SEGMENT.start, SEGMENT.start + 1, SEGMENT_LENGTH - 1); - } else { - copyPixels(SEGMENT.start + 1, SEGMENT.start, SEGMENT_LENGTH - 1); + 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); } - if(SEGMENT_RUNTIME.counter_mode_step == 0) { - SEGMENT_RUNTIME.aux_param = get_random_wheel_index(SEGMENT_RUNTIME.aux_param); - if(IS_REVERSE) { - setPixelColor(SEGMENT.stop, color_wheel(SEGMENT_RUNTIME.aux_param)); - } else { - setPixelColor(SEGMENT.start, color_wheel(SEGMENT_RUNTIME.aux_param)); - } - } + uint32_t color = color_wheel(_seg_rt->aux_param); - SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) % (2 << SIZE_OPTION); - return (SEGMENT.speed / SEGMENT_LENGTH); + return running(color, color); } @@ -1361,30 +1425,28 @@ uint16_t WS2812FX::mode_running_random(void) { uint16_t WS2812FX::mode_larson_scanner(void) { fade_out(); - if(SEGMENT_RUNTIME.counter_mode_step < SEGMENT_LENGTH) { + if(_seg_rt->counter_mode_step < _seg_len) { if(IS_REVERSE) { - setPixelColor(SEGMENT.stop - SEGMENT_RUNTIME.counter_mode_step, SEGMENT.colors[0]); + setPixelColor(_seg->stop - _seg_rt->counter_mode_step, _seg->colors[0]); } else { - setPixelColor(SEGMENT.start + SEGMENT_RUNTIME.counter_mode_step, SEGMENT.colors[0]); + setPixelColor(_seg->start + _seg_rt->counter_mode_step, _seg->colors[0]); } } else { - uint16_t index = (SEGMENT_LENGTH * 2) - SEGMENT_RUNTIME.counter_mode_step - 2; + uint16_t index = (_seg_len * 2) - _seg_rt->counter_mode_step - 2; if(IS_REVERSE) { - setPixelColor(SEGMENT.stop - index, SEGMENT.colors[0]); + setPixelColor(_seg->stop - index, _seg->colors[0]); } else { - setPixelColor(SEGMENT.start + index, SEGMENT.colors[0]); + setPixelColor(_seg->start + index, _seg->colors[0]); } } - if(SEGMENT_RUNTIME.counter_mode_step % SEGMENT_LENGTH == 0) SET_CYCLE; - else CLR_CYCLE; - - SEGMENT_RUNTIME.counter_mode_step++; - if(SEGMENT_RUNTIME.counter_mode_step >= (uint16_t)((SEGMENT_LENGTH * 2) - 2)) { - SEGMENT_RUNTIME.counter_mode_step = 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 (SEGMENT.speed / (SEGMENT_LENGTH * 2)); + return (_seg->speed / (_seg_len * 2)); } @@ -1395,13 +1457,15 @@ uint16_t WS2812FX::mode_comet(void) { fade_out(); if(IS_REVERSE) { - setPixelColor(SEGMENT.stop - SEGMENT_RUNTIME.counter_mode_step, SEGMENT.colors[0]); + setPixelColor(_seg->stop - _seg_rt->counter_mode_step, _seg->colors[0]); } else { - setPixelColor(SEGMENT.start + SEGMENT_RUNTIME.counter_mode_step, SEGMENT.colors[0]); + setPixelColor(_seg->start + _seg_rt->counter_mode_step, _seg->colors[0]); } - SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) % SEGMENT_LENGTH; - return (SEGMENT.speed / SEGMENT_LENGTH); + _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); } @@ -1414,8 +1478,8 @@ uint16_t WS2812FX::fireworks(uint32_t color) { // 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 = SEGMENT.start * bytesPerPixel + bytesPerPixel; - uint16_t stopPixel = SEGMENT.stop * bytesPerPixel ; + uint16_t startPixel = _seg->start * bytesPerPixel + bytesPerPixel; + uint16_t stopPixel = _seg->stop * bytesPerPixel ; for(uint16_t i=startPixel; i > 2) + pixels[i] + @@ -1425,23 +1489,22 @@ uint16_t WS2812FX::fireworks(uint32_t color) { uint8_t size = 2 << SIZE_OPTION; if(!_triggered) { - for(uint16_t i=0; istart + random16(_seg_len - size); + fill(color, index, size); + SET_CYCLE; } } } else { - for(uint16_t i=0; istart + random16(_seg_len - size); + fill(color, index, size); + SET_CYCLE; } } - return (SEGMENT.speed / SEGMENT_LENGTH); + + return (_seg->speed / _seg_len); } /* @@ -1450,7 +1513,7 @@ uint16_t WS2812FX::fireworks(uint32_t color) { uint16_t WS2812FX::mode_fireworks(void) { uint32_t color = BLACK; do { // randomly choose a non-BLACK color from the colors array - color = SEGMENT.colors[random8(NUM_COLORS)]; + color = _seg->colors[random8(MAX_NUM_COLORS)]; } while (color == BLACK); return fireworks(color); } @@ -1467,16 +1530,18 @@ uint16_t WS2812FX::mode_fireworks_random(void) { * Fire flicker function */ uint16_t WS2812FX::fire_flicker(int rev_intensity) { - byte w = (SEGMENT.colors[0] >> 24) & 0xFF; - byte r = (SEGMENT.colors[0] >> 16) & 0xFF; - byte g = (SEGMENT.colors[0] >> 8) & 0xFF; - byte b = (SEGMENT.colors[0] & 0xFF); + 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=SEGMENT.start; i <= SEGMENT.stop; i++) { + 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)); } - return (SEGMENT.speed / SEGMENT_LENGTH); + + SET_CYCLE; + return (_seg->speed / _seg_len); } /* @@ -1497,84 +1562,46 @@ uint16_t WS2812FX::mode_fire_flicker_soft(void) { * Random flickering, more intensity. */ uint16_t WS2812FX::mode_fire_flicker_intense(void) { - return fire_flicker(1.7); + return fire_flicker(1); } -/* - * Tricolor chase function - */ -uint16_t WS2812FX::tricolor_chase(uint32_t color1, uint32_t color2, uint32_t color3) { - uint8_t sizeCnt = 1 << SIZE_OPTION; - uint16_t index = SEGMENT_RUNTIME.counter_mode_call % (sizeCnt * 3); - for(uint16_t i=0; i < SEGMENT_LENGTH; i++, index++) { - index = index % (sizeCnt * 3); - - uint32_t color = color3; - if(index < sizeCnt) color = color1; - else if(index < (sizeCnt * 2)) color = color2; - - if(IS_REVERSE) { - setPixelColor(SEGMENT.start + i, color); - } else { - setPixelColor(SEGMENT.stop - i, color); - } - } - - return (SEGMENT.speed / SEGMENT_LENGTH); -} - - -/* - * Tricolor chase mode - */ -uint16_t WS2812FX::mode_tricolor_chase(void) { - return tricolor_chase(SEGMENT.colors[0], SEGMENT.colors[1], SEGMENT.colors[2]); -} - - -/* - * Alternating white/red/black pixels running. - */ -uint16_t WS2812FX::mode_circus_combustus(void) { - return tricolor_chase(RED, WHITE, BLACK); -} - /* * ICU mode */ -uint16_t WS2812FX::mode_icu(void) { - uint16_t dest = SEGMENT_RUNTIME.counter_mode_step & 0xFFFF; +// uint16_t WS2812FX::mode_icu(void) { +// uint16_t dest = _seg_rt->counter_mode_step & 0xFFFF; - setPixelColor(SEGMENT.start + dest, SEGMENT.colors[0]); - setPixelColor(SEGMENT.start + dest + SEGMENT_LENGTH/2, SEGMENT.colors[0]); - - if(SEGMENT_RUNTIME.aux_param3 == dest) { // pause between eye movements - if(random8(6) == 0) { // blink once in a while - setPixelColor(SEGMENT.start + dest, BLACK); - setPixelColor(SEGMENT.start + dest + SEGMENT_LENGTH/2, BLACK); - return 200; - } - SEGMENT_RUNTIME.aux_param3 = random16(SEGMENT_LENGTH/2); - return 1000 + random16(2000); - } - - setPixelColor(SEGMENT.start + dest, BLACK); - setPixelColor(SEGMENT.start + dest + SEGMENT_LENGTH/2, BLACK); - - if(SEGMENT_RUNTIME.aux_param3 > SEGMENT_RUNTIME.counter_mode_step) { - SEGMENT_RUNTIME.counter_mode_step++; - dest++; - } else if (SEGMENT_RUNTIME.aux_param3 < SEGMENT_RUNTIME.counter_mode_step) { - SEGMENT_RUNTIME.counter_mode_step--; - dest--; - } - - setPixelColor(SEGMENT.start + dest, SEGMENT.colors[0]); - setPixelColor(SEGMENT.start + dest + SEGMENT_LENGTH/2, SEGMENT.colors[0]); - - return (SEGMENT.speed / SEGMENT_LENGTH); -} +// setPixelColor(_seg->start + dest, _seg->colors[0]); +// setPixelColor(_seg->start + dest + _seg_len/2, _seg->colors[0]); + +// if(_seg_rt->aux_param3 == dest) { // pause between eye movements +// if(random8(6) == 0) { // blink once in a while +// setPixelColor(_seg->start + dest, BLACK); +// setPixelColor(_seg->start + dest + _seg_len/2, BLACK); +// return 200; +// } +// _seg_rt->aux_param3 = random16(_seg_len/2); +// SET_CYCLE; +// return 1000 + random16(2000); +// } + +// setPixelColor(_seg->start + dest, BLACK); +// setPixelColor(_seg->start + dest + _seg_len/2, BLACK); + +// if(_seg_rt->aux_param3 > _seg_rt->counter_mode_step) { +// _seg_rt->counter_mode_step++; +// dest++; +// } else if (_seg_rt->aux_param3 < _seg_rt->counter_mode_step) { +// _seg_rt->counter_mode_step--; +// dest--; +// } + +// setPixelColor(_seg->start + dest, _seg->colors[0]); +// setPixelColor(_seg->start + dest + _seg_len/2, _seg->colors[0]); + +// return (_seg->speed / _seg_len); +// } /* * Custom modes @@ -1591,6 +1618,18 @@ uint16_t WS2812FX::mode_custom_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 diff --git a/src/WS2812FX.h b/src/WS2812FX.h index c3b8ea0..d028a2c 100644 --- a/src/WS2812FX.h +++ b/src/WS2812FX.h @@ -45,6 +45,8 @@ #define DEFAULT_MODE (uint8_t)0 #define DEFAULT_SPEED (uint16_t)1000 #define DEFAULT_COLOR (uint32_t)0xFF0000 +#define DEFAULT_COLORS { RED, GREEN, BLUE } +#define COLORS(...) (const uint32_t[]){__VA_ARGS__} #if defined(ESP8266) || defined(ESP32) //#pragma message("Compiling for ESP") @@ -58,14 +60,13 @@ #define BRIGHTNESS_MIN (uint8_t)0 #define BRIGHTNESS_MAX (uint8_t)255 -/* each segment uses 36 bytes of SRAM memory, so if you're application fails because of - insufficient memory, decreasing MAX_NUM_SEGMENTS may help */ -#define MAX_NUM_SEGMENTS 10 -#define NUM_COLORS 3 /* number of colors per segment */ -#define MAX_CUSTOM_MODES 4 -#define SEGMENT _segments[_segment_index] -#define SEGMENT_RUNTIME _segment_runtimes[_segment_index] -#define SEGMENT_LENGTH (uint16_t)(SEGMENT.stop - SEGMENT.start + 1) +/* each segment uses 36 bytes of SRAM memory, so if you're compile fails + because of insufficient flash memory, decreasing MAX_NUM_SEGMENTS may help */ +#define MAX_NUM_SEGMENTS 10 +#define MAX_NUM_ACTIVE_SEGMENTS 10 +#define INACTIVE_SEGMENT 255 /* max uint_8 */ +#define MAX_NUM_COLORS 3 /* number of colors per segment */ +#define MAX_CUSTOM_MODES 8 // some common colors #define RED (uint32_t)0xFF0000 @@ -79,8 +80,11 @@ #define PURPLE (uint32_t)0x400080 #define ORANGE (uint32_t)0xFF3000 #define PINK (uint32_t)0xFF1493 +#define GRAY (uint32_t)0x101010 #define ULTRAWHITE (uint32_t)0xFFFFFFFF -#define DARK(c) (uint32_t)((c >> 4) & 0x0f0f0f0f) +#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 @@ -90,7 +94,7 @@ // bits 0: TBD #define NO_OPTIONS (uint8_t)B00000000 #define REVERSE (uint8_t)B10000000 -#define IS_REVERSE ((SEGMENT.options & REVERSE) == REVERSE) +#define IS_REVERSE ((_seg->options & REVERSE) == REVERSE) #define FADE_XFAST (uint8_t)B00010000 #define FADE_FAST (uint8_t)B00100000 #define FADE_MEDIUM (uint8_t)B00110000 @@ -98,22 +102,23 @@ #define FADE_XSLOW (uint8_t)B01010000 #define FADE_XXSLOW (uint8_t)B01100000 #define FADE_GLACIAL (uint8_t)B01110000 -#define FADE_RATE ((SEGMENT.options >> 4) & 7) +#define FADE_RATE ((_seg->options >> 4) & 7) #define GAMMA (uint8_t)B00001000 -#define IS_GAMMA ((SEGMENT.options & GAMMA) == GAMMA) +#define IS_GAMMA ((_seg->options & GAMMA) == GAMMA) #define SIZE_SMALL (uint8_t)B00000000 #define SIZE_MEDIUM (uint8_t)B00000010 #define SIZE_LARGE (uint8_t)B00000100 #define SIZE_XLARGE (uint8_t)B00000110 -#define SIZE_OPTION ((SEGMENT.options >> 1) & 3) +#define SIZE_OPTION ((_seg->options >> 1) & 3) // segment runtime options (aux_param2) -#define FRAME (uint8_t)B10000000 -#define SET_FRAME (SEGMENT_RUNTIME.aux_param2 |= FRAME) -#define CLR_FRAME (SEGMENT_RUNTIME.aux_param2 &= ~FRAME) -#define CYCLE (uint8_t)B01000000 -#define SET_CYCLE (SEGMENT_RUNTIME.aux_param2 |= CYCLE) -#define CLR_CYCLE (SEGMENT_RUNTIME.aux_param2 &= ~CYCLE) +#define FRAME (uint8_t)B10000000 +#define SET_FRAME (_seg_rt->aux_param2 |= FRAME) +#define CLR_FRAME (_seg_rt->aux_param2 &= ~FRAME) +#define CYCLE (uint8_t)B01000000 +#define SET_CYCLE (_seg_rt->aux_param2 |= CYCLE) +#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])) @@ -172,15 +177,19 @@ #define FX_MODE_HALLOWEEN 52 #define FX_MODE_BICOLOR_CHASE 53 #define FX_MODE_TRICOLOR_CHASE 54 -#define FX_MODE_ICU 55 -#define FX_MODE_CUSTOM 56 // keep this for backward compatiblity -#define FX_MODE_CUSTOM_0 56 // custom modes need to go at the end -#define FX_MODE_CUSTOM_1 57 -#define FX_MODE_CUSTOM_2 58 -#define FX_MODE_CUSTOM_3 59 - -// 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.) +#define FX_MODE_CUSTOM 55 // keep this for backward compatiblity +#define FX_MODE_CUSTOM_0 55 // custom modes need to go at the end +#define FX_MODE_CUSTOM_1 56 +#define FX_MODE_CUSTOM_2 57 +#define FX_MODE_CUSTOM_3 58 +#define FX_MODE_CUSTOM_4 59 +#define FX_MODE_CUSTOM_5 60 +#define FX_MODE_CUSTOM_6 61 +#define FX_MODE_CUSTOM_7 62 + +// 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"; @@ -236,11 +245,14 @@ 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 = "ICU"; -const char name_56[] PROGMEM = "Custom 0"; // custom modes need to go at the end -const char name_57[] PROGMEM = "Custom 1"; -const char name_58[] PROGMEM = "Custom 2"; -const char name_59[] PROGMEM = "Custom 3"; +const char name_55[] PROGMEM = "Custom 0"; // custom modes need to go at the end +const char name_56[] PROGMEM = "Custom 1"; +const char name_57[] PROGMEM = "Custom 2"; +const char name_58[] PROGMEM = "Custom 3"; +const char name_59[] PROGMEM = "Custom 4"; +const char name_60[] PROGMEM = "Custom 5"; +const char name_61[] PROGMEM = "Custom 6"; +const char name_62[] PROGMEM = "Custom 7"; static const __FlashStringHelper* _names[] = { FSH(name_0), @@ -302,25 +314,28 @@ static const __FlashStringHelper* _names[] = { FSH(name_56), FSH(name_57), FSH(name_58), - FSH(name_59) + FSH(name_59), + FSH(name_60), + FSH(name_61), + FSH(name_62) }; class WS2812FX : public Adafruit_NeoPixel { - typedef uint16_t (WS2812FX::*mode_ptr)(void); - - // segment parameters +typedef uint16_t (WS2812FX::*mode_ptr)(void); + public: + // segment parameters typedef struct Segment { // 20 bytes uint16_t start; uint16_t stop; uint16_t speed; uint8_t mode; uint8_t options; - uint32_t colors[NUM_COLORS]; + uint32_t colors[MAX_NUM_COLORS]; } segment; - // segment runtime parameters + // segment runtime parameters typedef struct Segment_runtime { // 16 bytes unsigned long next_time; uint32_t counter_mode_step; @@ -330,7 +345,10 @@ class WS2812FX : public Adafruit_NeoPixel { uint16_t aux_param3; // auxilary param (usually stores a segment index) } segment_runtime; - WS2812FX(uint16_t n, uint8_t p, neoPixelType t) : Adafruit_NeoPixel(n, p, t) { + WS2812FX(uint16_t num_leds, uint8_t pin, neoPixelType type, + uint8_t max_num_segments=MAX_NUM_SEGMENTS, + uint8_t max_num_active_segments=MAX_NUM_ACTIVE_SEGMENTS) + : Adafruit_NeoPixel(num_leds, pin, type) { _mode[FX_MODE_STATIC] = &WS2812FX::mode_static; _mode[FX_MODE_BLINK] = &WS2812FX::mode_blink; _mode[FX_MODE_COLOR_WIPE] = &WS2812FX::mode_color_wipe; @@ -384,31 +402,30 @@ class WS2812FX : public Adafruit_NeoPixel { _mode[FX_MODE_HALLOWEEN] = &WS2812FX::mode_halloween; _mode[FX_MODE_BICOLOR_CHASE] = &WS2812FX::mode_bicolor_chase; _mode[FX_MODE_TRICOLOR_CHASE] = &WS2812FX::mode_tricolor_chase; -// if flash memory is constrained (I'm looking at you Arduino Nano), replace modes -// that use a lot of flash with mode_static (reduces flash footprint by about 2100 bytes) -#ifdef REDUCED_MODES - _mode[FX_MODE_BREATH] = &WS2812FX::mode_static; - _mode[FX_MODE_RUNNING_LIGHTS] = &WS2812FX::mode_static; - _mode[FX_MODE_ICU] = &WS2812FX::mode_static; -#else _mode[FX_MODE_BREATH] = &WS2812FX::mode_breath; _mode[FX_MODE_RUNNING_LIGHTS] = &WS2812FX::mode_running_lights; - _mode[FX_MODE_ICU] = &WS2812FX::mode_icu; -#endif _mode[FX_MODE_CUSTOM_0] = &WS2812FX::mode_custom_0; _mode[FX_MODE_CUSTOM_1] = &WS2812FX::mode_custom_1; _mode[FX_MODE_CUSTOM_2] = &WS2812FX::mode_custom_2; _mode[FX_MODE_CUSTOM_3] = &WS2812FX::mode_custom_3; + _mode[FX_MODE_CUSTOM_4] = &WS2812FX::mode_custom_4; + _mode[FX_MODE_CUSTOM_5] = &WS2812FX::mode_custom_5; + _mode[FX_MODE_CUSTOM_6] = &WS2812FX::mode_custom_6; + _mode[FX_MODE_CUSTOM_7] = &WS2812FX::mode_custom_7; brightness = DEFAULT_BRIGHTNESS + 1; // Adafruit_NeoPixel internally offsets brightness by 1 _running = false; - _num_segments = 1; - _segments[0].mode = DEFAULT_MODE; - _segments[0].colors[0] = DEFAULT_COLOR; - _segments[0].start = 0; - _segments[0].stop = n - 1; - _segments[0].speed = DEFAULT_SPEED; - resetSegmentRuntimes(); + + _segments_len = max_num_segments; + _active_segments_len = max_num_active_segments; + + // create all the segment arrays and init to zeros + _segments = new segment[_segments_len](); + _active_segments = new uint8_t[_active_segments_len](); + _segment_runtimes = new segment_runtime[_active_segments_len](); + + resetSegments(); + setSegment(0, 0, num_leds - 1, DEFAULT_MODE, DEFAULT_COLOR, DEFAULT_SPEED, NO_OPTIONS); } void @@ -443,18 +460,25 @@ class WS2812FX : public Adafruit_NeoPixel { increaseLength(uint16_t s), decreaseLength(uint16_t s), trigger(void), + setCycle(void), setNumSegments(uint8_t n), setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint16_t speed, bool reverse), setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint16_t speed, uint8_t options), setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint16_t speed, bool reverse), setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint16_t speed, uint8_t options), - resetSegments(), - resetSegmentRuntimes(), + setIdleSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint16_t speed, uint8_t options), + setIdleSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint16_t speed, uint8_t options), + addActiveSegment(uint8_t seg), + removeActiveSegment(uint8_t seg), + swapActiveSegment(uint8_t oldSeg, uint8_t newSeg), + resetSegments(void), + resetSegmentRuntimes(void), resetSegmentRuntime(uint8_t), setPixelColor(uint16_t n, uint32_t c), setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b), setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w), copyPixels(uint16_t d, uint16_t s, uint16_t c), + setPixels(uint16_t, uint8_t*), show(void); boolean @@ -463,7 +487,8 @@ class WS2812FX : public Adafruit_NeoPixel { isFrame(void), isFrame(uint8_t), isCycle(void), - isCycle(uint8_t); + isCycle(uint8_t), + isActiveSegment(uint8_t seg); uint8_t random8(void), @@ -494,6 +519,7 @@ class WS2812FX : public Adafruit_NeoPixel { uint32_t* getColors(uint8_t); uint32_t* intensitySums(void); + uint8_t* getActiveSegments(void); const __FlashStringHelper* getModeName(uint8_t m); @@ -515,12 +541,15 @@ class WS2812FX : public Adafruit_NeoPixel { color_wipe(uint32_t, uint32_t, bool), twinkle(uint32_t, uint32_t), twinkle_fade(uint32_t), + sparkle(uint32_t, uint32_t), chase(uint32_t, uint32_t, uint32_t), + chase_flash(uint32_t, uint32_t), running(uint32_t, uint32_t), fireworks(uint32_t), fire_flicker(int), tricolor_chase(uint32_t, uint32_t, uint32_t), scan(uint32_t, uint32_t, bool); + uint32_t color_blend(uint32_t, uint32_t, uint8_t); @@ -581,15 +610,22 @@ class WS2812FX : public Adafruit_NeoPixel { mode_circus_combustus(void), mode_bicolor_chase(void), mode_tricolor_chase(void), - mode_icu(void), mode_custom_0(void), mode_custom_1(void), mode_custom_2(void), - mode_custom_3(void); + mode_custom_3(void), + mode_custom_4(void), + mode_custom_5(void), + mode_custom_6(void), + mode_custom_7(void); private: uint16_t _rand16seed; uint16_t (*customModes[MAX_CUSTOM_MODES])(void) { + []{ return (uint16_t)1000; }, + []{ return (uint16_t)1000; }, + []{ return (uint16_t)1000; }, + []{ return (uint16_t)1000; }, []{ return (uint16_t)1000; }, []{ return (uint16_t)1000; }, []{ return (uint16_t)1000; }, @@ -601,15 +637,20 @@ class WS2812FX : public Adafruit_NeoPixel { _running, _triggered; - mode_ptr _mode[MODE_COUNT]; // SRAM footprint: 4 bytes per element + mode_ptr _mode[MODE_COUNT]; // array of mode function pointers (4 bytes per element) - uint8_t _segment_index = 0; - uint8_t _num_segments = 1; - segment _segments[MAX_NUM_SEGMENTS] = { // SRAM footprint: 20 bytes per element - // start, stop, speed, mode, options, color[] - { 0, 7, DEFAULT_SPEED, FX_MODE_STATIC, NO_OPTIONS, {DEFAULT_COLOR}} - }; - segment_runtime _segment_runtimes[MAX_NUM_SEGMENTS]; // SRAM footprint: 16 bytes per element + segment* _segments; // array of segments (20 bytes per element) + segment_runtime* _segment_runtimes; // array of segment runtimes (16 bytes per element) + uint8_t* _active_segments; // array of active segments (1 bytes per element) + + uint8_t _segments_len = 0; // size of _segments array + uint8_t _active_segments_len = 0; // size of _segments_runtime and _active_segments arrays + uint8_t _num_segments = 0; // number of configured segments in the _segments array + + segment* _seg; // currently active segment (20 bytes) + segment_runtime* _seg_rt; // currently active segment runtime (16 bytes) + + uint16_t _seg_len; // num LEDs in the currently active segment }; #endif diff --git a/src/custom/BlockDissolve.h b/src/custom/BlockDissolve.h index 3efc9d0..e77724c 100644 --- a/src/custom/BlockDissolve.h +++ b/src/custom/BlockDissolve.h @@ -61,7 +61,8 @@ uint16_t blockDissolve(void) { ws2812fx.setPixelColor(i, color); } - segrt->aux_param = (segrt->aux_param + 1) % NUM_COLORS; + segrt->aux_param = (segrt->aux_param + 1) % MAX_NUM_COLORS; + if(segrt->aux_param == 0) ws2812fx.setCycle(); return seg->speed; } diff --git a/src/custom/DualLarson.h b/src/custom/DualLarson.h index dcceff6..5ef0942 100644 --- a/src/custom/DualLarson.h +++ b/src/custom/DualLarson.h @@ -59,6 +59,7 @@ uint16_t dualLarson(void) { if(segrt->aux_param3 >= (seg->stop - seg->start) || segrt->aux_param3 <= 0) { segrt->aux_param = !segrt->aux_param; if(seg->options & REVERSE) offset = (offset + 1) % seglen; + if(!segrt->aux_param) ws2812fx.setCycle(); } return (seg->speed / (seglen * 2)); diff --git a/src/custom/Fillerup.h b/src/custom/Fillerup.h index 78613f7..2e48fc8 100644 --- a/src/custom/Fillerup.h +++ b/src/custom/Fillerup.h @@ -84,6 +84,7 @@ uint16_t fillerup(void) { if(segrt->aux_param3 >= seglen) { segrt->aux_param = !segrt->aux_param; segrt->aux_param3 = 0; + ws2812fx.setCycle(); } return (seg->speed / seglen); diff --git a/src/custom/Heartbeat.h b/src/custom/Heartbeat.h index 3caa79e..1f2f6c3 100644 --- a/src/custom/Heartbeat.h +++ b/src/custom/Heartbeat.h @@ -72,6 +72,7 @@ uint16_t heartbeat(void) { beatIt(seg, size); // create the first beat secondBeatActive = false; lastBeat = millis(); + ws2812fx.setCycle(); } return(seg->speed / 32); diff --git a/src/custom/ICU.h b/src/custom/ICU.h new file mode 100644 index 0000000..d0c606b --- /dev/null +++ b/src/custom/ICU.h @@ -0,0 +1,79 @@ +/* + Custom effect that mimics two eyes looking about + + Keith Lord - 2018 + + LICENSE + + The MIT License (MIT) + + Copyright (c) 2018 Keith Lord + + 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, sub-license, 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 + 2018-07-26 initial version +*/ + +#ifndef ICU_h +#define ICU_h + +#include + +extern WS2812FX ws2812fx; + +uint16_t icu(void) { + WS2812FX::Segment* seg = ws2812fx.getSegment(); // get the current segment + WS2812FX::Segment_runtime* segrt = ws2812fx.getSegmentRuntime(); + int seglen = seg->stop - seg->start + 1; + + uint16_t dest = segrt->counter_mode_step & 0xFFFF; + + ws2812fx.setPixelColor(seg->start + dest, seg->colors[0]); + ws2812fx.setPixelColor(seg->start + dest + seglen/2, seg->colors[0]); + + if(segrt->aux_param3 == dest) { // pause between eye movements + if(ws2812fx.random8(6) == 0) { // blink once in a while + ws2812fx.setPixelColor(seg->start + dest, BLACK); + ws2812fx.setPixelColor(seg->start + dest + seglen/2, BLACK); + return 200; + } + segrt->aux_param3 = ws2812fx.random16(seglen/2); + ws2812fx.setCycle(); + return 1000 + ws2812fx.random16(2000); + } + + ws2812fx.setPixelColor(seg->start + dest, BLACK); + ws2812fx.setPixelColor(seg->start + dest + seglen/2, BLACK); + + if(segrt->aux_param3 > segrt->counter_mode_step) { + segrt->counter_mode_step++; + dest++; + } else if (segrt->aux_param3 < segrt->counter_mode_step) { + segrt->counter_mode_step--; + dest--; + } + + ws2812fx.setPixelColor(seg->start + dest, seg->colors[0]); + ws2812fx.setPixelColor(seg->start + dest + seglen/2, seg->colors[0]); + + return (seg->speed / seglen); +} + +#endif diff --git a/src/custom/Matrix.h b/src/custom/Matrix.h index b8063fb..70fece3 100644 --- a/src/custom/Matrix.h +++ b/src/custom/Matrix.h @@ -1,6 +1,26 @@ /* Custom effect that animates a 2D matrix of LEDs + In your sketch create an array of led color data: + uint32_t matrix_leds[][3][8] = { + { // page 0 + {RED, RED, RED, RED, RED, RED, RED, RED}, // row 0 + {WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE}, // row 1 + {BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE} // row 2 + }, + { // page 1 + {YELLOW, YELLOW, YELLOW, YELLOW, YELLOW, YELLOW, YELLOW, YELLOW}, // row 0 + {PINK, PINK, PINK, PINK, PINK, PINK, PINK, PINK}, // row 1 + {GREEN, GREEN, GREEN, GREEN, GREEN, GREEN, GREEN, GREEN} // row 2 + } + }; + + Then tell the matrix effect about your array: + configMatrix(NUM_PAGES, NUM_ROWS, NUM_COLS, (uint32_t*)matrix_leds); + + Then setup your matrix effect with setCustomMode() like you would any other + custom effect. + Keith Lord - 2020 LICENSE @@ -38,45 +58,41 @@ extern WS2812FX ws2812fx; -uint32_t matrix_leds[][3][8] = { // setup for a 3x8 LED matrix - { // page 0 - {RED, RED, RED, RED, RED, RED, RED, RED}, // row 0 - {WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE}, // row 1 - {BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE} // row 2 - }, - { // page 1 - {YELLOW, YELLOW, YELLOW, YELLOW, YELLOW, YELLOW, YELLOW, YELLOW}, // row 0 - {PINK, PINK, PINK, PINK, PINK, PINK, PINK, PINK}, // row 1 - {GREEN, GREEN, GREEN, GREEN, GREEN, GREEN, GREEN, GREEN} // row 2 - } +struct Matrix { + int8_t numPages; + int8_t numRows; + int8_t numCols; + int32_t* colors; }; +struct Matrix _matrix; // global variable, so this custom effect shouldn't be used in more then one segment + +void configMatrix(uint8_t numPages, uint8_t numRows, uint8_t numCols, uint32_t* colors) { + _matrix.numPages = numPages; + _matrix.numRows = numRows; + _matrix.numCols = numCols; + _matrix.colors = colors; +} uint16_t matrix(void) { WS2812FX::Segment* seg = ws2812fx.getSegment(); WS2812FX::Segment_runtime* segrt = ws2812fx.getSegmentRuntime(); int seglen = seg->stop - seg->start + 1; - const int numPages = sizeof(matrix_leds) / sizeof(matrix_leds[0]); - const int numRows = sizeof(matrix_leds[0]) / sizeof(matrix_leds[0][0]); - const int numCols = sizeof(matrix_leds[0][0]) / sizeof(matrix_leds[0][0][0]); -// Serial.print("numPages:"); Serial.println(numPages); -// Serial.print("numRows:"); Serial.println(numRows); -// Serial.print("numCols:"); Serial.println(numCols); - - uint16_t segIndex = 0; - uint8_t pageIndex = segrt->aux_param; // aux_param will store the page index - for(int i=0; istart; + uint8_t pageIndex = segrt->aux_param * _matrix.numRows * _matrix.numCols; // aux_param will store the page index + for(int rowIndex=0; rowIndex < _matrix.numRows; rowIndex++) { + uint16_t matrixIndex = pageIndex + (rowIndex * _matrix.numCols); + for(int colIndex=0; colIndex < _matrix.numCols; colIndex++) { + if(segIndex <= seg->stop) { + ws2812fx.setPixelColor(segIndex, _matrix.colors[matrixIndex + colIndex]); segIndex++; } } } // increment to the next page - segrt->aux_param < (numPages - 1) ? segrt->aux_param++ : segrt->aux_param = 0; - + segrt->aux_param < (_matrix.numPages - 1) ? segrt->aux_param++ : segrt->aux_param = 0; + if(segrt->aux_param == 0) ws2812fx.setCycle(); return seg->speed; } diff --git a/src/custom/MultiComet.h b/src/custom/MultiComet.h index 8f0b850..f43a777 100644 --- a/src/custom/MultiComet.h +++ b/src/custom/MultiComet.h @@ -60,6 +60,7 @@ uint16_t multiComet(void) { } else { if(!random(seglen)) { comets[i] = 0; + ws2812fx.setCycle(); } } } diff --git a/src/custom/Oscillate.h b/src/custom/Oscillate.h index d44c69a..da0f3a5 100644 --- a/src/custom/Oscillate.h +++ b/src/custom/Oscillate.h @@ -49,7 +49,7 @@ uint16_t oscillate(void) { WS2812FX::Segment* seg = ws2812fx.getSegment(); // get the current segment int seglen = seg->stop - seg->start + 1; - static oscillator oscillators[NUM_COLORS] = { + static oscillator oscillators[] = { {seglen/4, seglen/8, 1, 1}, {seglen/4*2, seglen/8, -1, 1}, {seglen/4*3, seglen/8, 1, 2} @@ -61,11 +61,13 @@ uint16_t oscillate(void) { oscillators[i].pos = 0; oscillators[i].dir = 1; oscillators[i].speed = random(1, 3); + ws2812fx.setCycle(); } if((oscillators[i].dir == 1) && (oscillators[i].pos >= (seglen - 1))) { oscillators[i].pos = seglen - 1; oscillators[i].dir = -1; oscillators[i].speed = random(1, 3); + ws2812fx.setCycle(); } } diff --git a/src/custom/Popcorn.h b/src/custom/Popcorn.h index 53d2942..94bfcda 100644 --- a/src/custom/Popcorn.h +++ b/src/custom/Popcorn.h @@ -84,6 +84,7 @@ uint16_t popcorn(void) { popcorn[i].color = popcornColor; ledIndex = isReverse ? seg->stop : seg->start; ws2812fx.setPixelColor(ledIndex, popcorn[i].color); + ws2812fx.setCycle(); } } } diff --git a/src/custom/RainbowFireworks.h b/src/custom/RainbowFireworks.h index 8c167bd..da79513 100644 --- a/src/custom/RainbowFireworks.h +++ b/src/custom/RainbowFireworks.h @@ -80,6 +80,7 @@ uint16_t rainbowFireworks(void) { if(ws2812fx.random8(10) == 0) { uint16_t index = seg->start + 6 + ws2812fx.random16(seglen - 12); ws2812fx.setPixelColor(index, RED); + ws2812fx.setCycle(); } } diff --git a/src/custom/RainbowLarson.h b/src/custom/RainbowLarson.h index 2512361..74777b0 100644 --- a/src/custom/RainbowLarson.h +++ b/src/custom/RainbowLarson.h @@ -42,6 +42,8 @@ #include +#define DIR_BIT (uint8_t)B00000001 // segrt->aux_param2 direction bit + extern WS2812FX ws2812fx; uint16_t rainbowLarson(void) { @@ -49,7 +51,7 @@ uint16_t rainbowLarson(void) { WS2812FX::Segment_runtime* segrt = ws2812fx.getSegmentRuntime(); int seglen = seg->stop - seg->start + 1; - int8_t dir = segrt->aux_param2 ? -1 : 1; + int8_t dir = ((segrt->aux_param2 & DIR_BIT) == DIR_BIT) ? -1 : 1; // forward? segrt->aux_param3 += dir; int16_t index = segrt->aux_param3 % seglen; @@ -63,7 +65,8 @@ uint16_t rainbowLarson(void) { } if(segrt->aux_param3 >= (seg->stop - seg->start) || segrt->aux_param3 <= 0) { - segrt->aux_param2 = !segrt->aux_param2; + segrt->aux_param2 ^= DIR_BIT; // change direction + ws2812fx.setCycle(); } return (seg->speed / (seglen * 2)); diff --git a/src/custom/RandomChase.h b/src/custom/RandomChase.h index b0d7df6..d96e6eb 100644 --- a/src/custom/RandomChase.h +++ b/src/custom/RandomChase.h @@ -40,13 +40,17 @@ extern WS2812FX ws2812fx; uint16_t randomChase(void) { WS2812FX::Segment* seg = ws2812fx.getSegment(); + WS2812FX::Segment_runtime* segrt = ws2812fx.getSegmentRuntime(); int seglen = seg->stop - seg->start + 1; + ws2812fx.copyPixels(seg->start + 1, seg->start, seglen - 1); uint32_t color = ws2812fx.getPixelColor(seg->start + 1); int r = random(6) != 0 ? (color >> 16 & 0xFF) : random(256); int g = random(6) != 0 ? (color >> 8 & 0xFF) : random(256); int b = random(6) != 0 ? (color & 0xFF) : random(256); ws2812fx.setPixelColor(seg->start, r, g, b); + + if((segrt->counter_mode_call % seglen) == 0) ws2812fx.setCycle(); return seg->speed; } diff --git a/src/custom/TriFade.h b/src/custom/TriFade.h index 501e945..2e1488f 100644 --- a/src/custom/TriFade.h +++ b/src/custom/TriFade.h @@ -82,7 +82,10 @@ uint16_t triFade(void) { } segrt->aux_param3 += 4; - if(segrt->aux_param3 >= 1536) segrt->aux_param3 = 0; + if(segrt->aux_param3 >= 1536) { + segrt->aux_param3 = 0; + ws2812fx.setCycle(); + } return (seg->speed / 128); } diff --git a/src/custom/TwinkleFox.h b/src/custom/TwinkleFox.h index 17ccd20..52564d7 100644 --- a/src/custom/TwinkleFox.h +++ b/src/custom/TwinkleFox.h @@ -69,7 +69,7 @@ uint16_t twinkleFox(void) { // 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, bland random colors + // If colors[0] is BLACK, blend random colors if(color0 == BLACK) { blendedColor = ws2812fx.color_blend(ws2812fx.color_wheel(initValue), color1, blendAmt); // If colors[2] isn't BLACK, choose to blend colors[0]/colors[1] or colors[1]/colors[2] @@ -88,6 +88,7 @@ uint16_t twinkleFox(void) { } } } + ws2812fx.setCycle(); return seg->speed / 32; } diff --git a/src/custom/VUMeter.h b/src/custom/VUMeter.h index 77ca20c..d245a54 100644 --- a/src/custom/VUMeter.h +++ b/src/custom/VUMeter.h @@ -73,6 +73,7 @@ uint16_t vuMeter(void) { } } } + ws2812fx.setCycle(); return seg->speed; }