--- /dev/null
+/*
+ WS2812FX.cpp - Library for WS2812 LED effects.
+ Harm Aldick - 2016
+ www.aldick.org
+ FEATURES
+ * A lot of blinken modes and counting
+ * WS2812FX can be used as drop-in replacement for Adafruit Neopixel Library
+ NOTES
+ * Uses the Adafruit Neopixel library. Get it here:
+ https://github.com/adafruit/Adafruit_NeoPixel
+ LICENSE
+ The MIT License (MIT)
+ Copyright (c) 2016 Harm Aldick
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ CHANGELOG
+ 2016-05-28 Initial beta release
+ 2016-06-03 Code cleanup, minor improvements, new modes
+ 2016-06-04 2 new fx, fixed setColor (now also resets _mode_color)
+ 2017-02-02 added external trigger functionality (e.g. for sound-to-light)
+ 2017-02-02 removed "blackout" on mode, speed or color-change
+*/
+
+#include "core/systick.h"
+#include "extdrv/ws2812.h"
+#include "extdrv/ws2812fx.h"
+
+static uint32_t random(uint32_t max)
+{
+ static uint32_t seed = 0;
+ /* Numerical Recipes */
+ seed = seed * 1664525 + 1013904223;
+ return seed % max;
+}
+
+static uint32_t abs(int32_t val)
+{
+ return val < 0 ? -val : val;
+}
+
+static uint32_t min(uint32_t a, uint32_t b)
+{
+ return a < b ? a : b;
+}
+
+static uint32_t max(uint32_t a, uint32_t b)
+{
+ return a > b ? a : b;
+}
+
+static unsigned long millis(void)
+{
+ return systick_get_tick_count();
+}
+
+static uint32_t constrain(int32_t val, int32_t min, int32_t max)
+{
+ return val < min ? min : val > max ? max : val;
+}
+
+
+/* #####################################################
+#
+# Color and Blinken Functions
+#
+##################################################### */
+
+/*
+ * Turns everything off. Doh.
+ */
+static void strip_off(ws2812fx_t *fx)
+{
+ ws2812_clear();
+ ws2812_send_frame(fx->_led_count);
+}
+
+#define WHITE 0xffffff
+#define RED 0xff0000
+#define GREEN 0x00ff00
+#define BLUE 0x0000ff
+#define BLACK 0x000000
+
+static void set_pixel_rgb_color(uint32_t i, uint8_t r, uint8_t g, uint8_t b)
+{
+ ws2812_set_pixel(i, r, g, b);
+}
+
+static void set_pixel_color(uint32_t i, uint32_t color)
+{
+ ws2812_set_pixel(i, color >> 16, (color >> 8) & 0xff, color & 0xff);
+}
+
+static uint32_t get_pixel_color(uint32_t i)
+{
+ uint8_t r, g, b;
+ ws2812_get_pixel(i, &r, &g, &b);
+ return (r << 16) | (g << 8) | b;
+}
+
+/*
+ * Put a value 0 to 255 in to get a color value.
+ * The colours are a transition r -> g -> b -> back to r
+ * Inspired by the Adafruit examples.
+ */
+static uint32_t color_wheel(uint8_t pos)
+{
+ pos = 255 - pos;
+ if(pos < 85) {
+ return ((uint32_t)(255 - pos * 3) << 16) | ((uint32_t)(0) << 8) | (pos * 3);
+ } else if(pos < 170) {
+ pos -= 85;
+ return ((uint32_t)(0) << 16) | ((uint32_t)(pos * 3) << 8) | (255 - pos * 3);
+ } else {
+ pos -= 170;
+ return ((uint32_t)(pos * 3) << 16) | ((uint32_t)(255 - pos * 3) << 8) | (0);
+ }
+}
+
+
+/*
+ * Returns a new, random wheel index with a minimum distance of 42 from pos.
+ */
+static uint8_t get_random_wheel_index(uint8_t pos)
+{
+ uint8_t r = 0;
+ uint8_t x = 0;
+ uint8_t y = 0;
+ uint8_t d = 0;
+
+ while(d < 42) {
+ r = random(256);
+ x = abs(pos - r);
+ y = 255 - x;
+ d = min(x, y);
+ }
+
+ return r;
+}
+
+
+/*
+ * No blinking. Just plain old static light.
+ */
+static void mode_static(ws2812fx_t *fx)
+{
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ set_pixel_color(i, fx->_color);
+ }
+ ws2812_send_frame(fx->_led_count);
+
+ fx->_mode_delay = 50;
+}
+
+
+/*
+ * Normal blinking. 50% on/off time.
+ */
+static void mode_blink(ws2812fx_t *fx)
+{
+ if(fx->_counter_mode_call % 2 == 1) {
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ set_pixel_color(i, fx->_color);
+ }
+ ws2812_send_frame(fx->_led_count);
+ } else {
+ strip_off(fx);
+ }
+
+ fx->_mode_delay = 100 + ((1986 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / FX_SPEED_MAX);
+}
+
+
+/*
+ * Lights all LEDs after each other up. Then turns them in
+ * that order off. Repeat.
+ */
+static void mode_color_wipe(ws2812fx_t *fx)
+{
+ if(fx->_counter_mode_step < fx->_led_count) {
+ set_pixel_color(fx->_counter_mode_step, fx->_color);
+ } else {
+ set_pixel_color(fx->_counter_mode_step - fx->_led_count, 0);
+ }
+ ws2812_send_frame(fx->_led_count);
+
+ fx->_counter_mode_step = (fx->_counter_mode_step + 1) % (fx->_led_count * 2);
+
+ fx->_mode_delay = 5 + ((50 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / fx->_led_count);
+}
+
+
+/*
+ * Turns all LEDs after each other to a random color.
+ * Then starts over with another color.
+ */
+static void mode_color_wipe_random(ws2812fx_t *fx)
+{
+ if(fx->_counter_mode_step == 0) {
+ fx->_mode_color = get_random_wheel_index(fx->_mode_color);
+ }
+
+ set_pixel_color(fx->_counter_mode_step, color_wheel(fx->_mode_color));
+ ws2812_send_frame(fx->_led_count);
+
+ fx->_counter_mode_step = (fx->_counter_mode_step + 1) % fx->_led_count;
+
+ fx->_mode_delay = 5 + ((50 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / fx->_led_count);
+}
+
+
+/*
+ * Lights all LEDs in one random color up. Then switches them
+ * to the next random color.
+ */
+static void mode_random_color(ws2812fx_t *fx)
+{
+ fx->_mode_color = get_random_wheel_index(fx->_mode_color);
+
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ set_pixel_color(i, color_wheel(fx->_mode_color));
+ }
+
+ ws2812_send_frame(fx->_led_count);
+ fx->_mode_delay = 100 + ((5000 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / FX_SPEED_MAX);
+}
+
+
+/*
+ * Lights every LED in a random color. Changes one random LED after the other
+ * to another random color.
+ */
+static void mode_single_dynamic(ws2812fx_t *fx)
+{
+ if(fx->_counter_mode_call == 0) {
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ set_pixel_color(i, color_wheel(random(256)));
+ }
+ }
+
+ set_pixel_color(random(fx->_led_count), color_wheel(random(256)));
+ ws2812_send_frame(fx->_led_count);
+ fx->_mode_delay = 10 + ((5000 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / FX_SPEED_MAX);
+}
+
+
+/*
+ * Lights every LED in a random color. Changes all LED at the same time
+ * to new random colors.
+ */
+static void mode_multi_dynamic(ws2812fx_t *fx)
+{
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ set_pixel_color(i, color_wheel(random(256)));
+ }
+ ws2812_send_frame(fx->_led_count);
+ fx->_mode_delay = 100 + ((5000 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / FX_SPEED_MAX);
+}
+
+
+/*
+ * Does the "standby-breathing" of well known i-Devices. Fixed Speed.
+ * Use mode "fade" if you like to have something similar with a different speed.
+ */
+static void mode_breath(ws2812fx_t *fx)
+{
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // step
+ uint16_t breath_delay_steps[] = { 7, 9, 13, 15, 16, 17, 18, 930, 19, 18, 15, 13, 9, 7, 4, 5, 10 }; // magic numbers for breathing LED
+ uint8_t breath_brightness_steps[] = { 150, 125, 100, 75, 50, 25, 16, 15, 16, 25, 50, 75, 100, 125, 150, 220, 255 }; // even more magic numbers!
+
+ if(fx->_counter_mode_call == 0) {
+ fx->_mode_color = breath_brightness_steps[0] + 1;
+ }
+
+ uint32_t breath_brightness = fx->_mode_color; // we use fx->_mode_color to store the brightness
+
+ if(fx->_counter_mode_step < 8) {
+ breath_brightness--;
+ } else {
+ breath_brightness++;
+ }
+
+ // update index of current delay when target brightness is reached, start over after the last step
+ if(breath_brightness == breath_brightness_steps[fx->_counter_mode_step]) {
+ fx->_counter_mode_step = (fx->_counter_mode_step + 1) % (sizeof(breath_brightness_steps)/sizeof(uint8_t));
+ }
+
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ set_pixel_color(i, fx->_color); // set all LEDs to selected color
+ }
+ int b = (breath_brightness * (fx->_brightness + 1)) >> 8; // keep brightness below brightness set by user
+ ws2812_set_brightness(b);
+ ws2812_send_frame(fx->_led_count);
+
+ fx->_mode_color = breath_brightness; // we use fx->_mode_color to store the brightness
+ fx->_mode_delay = breath_delay_steps[fx->_counter_mode_step];
+}
+
+
+/*
+ * Fades the LEDs on and (almost) off again.
+ */
+static void mode_fade(ws2812fx_t *fx)
+{
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ set_pixel_color(i, fx->_color);
+ }
+
+ int b = fx->_counter_mode_step - 127;
+ b = 255 - (abs(b) * 2);
+ b = (b * (min(20, fx->_brightness) + 1)) >> 8;
+ ws2812_set_brightness(b);
+ ws2812_send_frame(fx->_led_count);
+
+ fx->_counter_mode_step = (fx->_counter_mode_step + 1) % 255;
+ fx->_mode_delay = 5 + ((15 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / FX_SPEED_MAX);
+}
+
+
+/*
+ * Runs a single pixel back and forth.
+ */
+static void mode_scan(ws2812fx_t *fx)
+{
+ if(fx->_counter_mode_step > (fx->_led_count*2) - 2) {
+ fx->_counter_mode_step = 0;
+ }
+ fx->_counter_mode_step++;
+
+ int i = fx->_counter_mode_step - (fx->_led_count - 1);
+ i = abs(i);
+
+ ws2812_clear();
+ set_pixel_color(abs(i), fx->_color);
+ ws2812_send_frame(fx->_led_count);
+
+ fx->_mode_delay = 10 + ((30 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / fx->_led_count);
+}
+
+
+/*
+ * Runs two pixel back and forth in opposite directions.
+ */
+static void mode_dual_scan(ws2812fx_t *fx)
+{
+ if(fx->_counter_mode_step > (fx->_led_count*2) - 2) {
+ fx->_counter_mode_step = 0;
+ }
+ fx->_counter_mode_step++;
+
+ int i = fx->_counter_mode_step - (fx->_led_count - 1);
+ i = abs(i);
+
+ ws2812_clear();
+ set_pixel_color(i, fx->_color);
+ set_pixel_color(fx->_led_count - (i+1), fx->_color);
+ ws2812_send_frame(fx->_led_count);
+
+ fx->_mode_delay = 10 + ((30 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / fx->_led_count);
+}
+
+
+/*
+ * Cycles all LEDs at once through a rainbow.
+ */
+static void mode_rainbow(ws2812fx_t *fx)
+{
+ uint32_t color = color_wheel(fx->_counter_mode_step);
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ set_pixel_color(i, color);
+ }
+ ws2812_send_frame(fx->_led_count);
+
+ fx->_counter_mode_step = (fx->_counter_mode_step + 1) % 256;
+
+ fx->_mode_delay = 1 + ((50 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / FX_SPEED_MAX);
+}
+
+
+/*
+ * Cycles a rainbow over the entire string of LEDs.
+ */
+static void mode_rainbow_cycle(ws2812fx_t *fx)
+{
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ set_pixel_color(i, color_wheel(((i * 256 / fx->_led_count) + fx->_counter_mode_step) % 256));
+ }
+ ws2812_send_frame(fx->_led_count);
+
+ fx->_counter_mode_step = (fx->_counter_mode_step + 1) % 256;
+
+ fx->_mode_delay = 1 + ((50 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / FX_SPEED_MAX);
+}
+
+
+/*
+ * Theatre-style crawling lights.
+ * Inspired by the Adafruit examples.
+ */
+static void mode_theater_chase(ws2812fx_t *fx)
+{
+ uint8_t j = fx->_counter_mode_call % 6;
+ if(j % 2 == 0) {
+ for(uint16_t i=0; i < fx->_led_count; i=i+3) {
+ set_pixel_color(i+(j/2), fx->_color);
+ }
+ ws2812_send_frame(fx->_led_count);
+ fx->_mode_delay = 50 + ((500 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / FX_SPEED_MAX);
+ } else {
+ for(uint16_t i=0; i < fx->_led_count; i=i+3) {
+ set_pixel_color(i+(j/2), 0);
+ }
+ fx->_mode_delay = 1;
+ }
+}
+
+
+/*
+ * Theatre-style crawling lights with rainbow effect.
+ * Inspired by the Adafruit examples.
+ */
+static void mode_theater_chase_rainbow(ws2812fx_t *fx)
+{
+ uint8_t j = fx->_counter_mode_call % 6;
+ if(j % 2 == 0) {
+ for(uint16_t i=0; i < fx->_led_count; i=i+3) {
+ set_pixel_color(i+(j/2), color_wheel((i+fx->_counter_mode_step) % 256));
+ }
+ ws2812_send_frame(fx->_led_count);
+ fx->_mode_delay = 50 + ((500 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / FX_SPEED_MAX);
+ } else {
+ for(uint16_t i=0; i < fx->_led_count; i=i+3) {
+ set_pixel_color(i+(j/2), 0);
+ }
+ fx->_mode_delay = 1;
+ }
+ fx->_counter_mode_step = (fx->_counter_mode_step + 1) % 256;
+}
+
+
+#if 0
+/*
+ * Running lights effect with smooth sine transition.
+ */
+static void mode_running_lights(ws2812fx_t *fx)
+{
+ uint8_t r = ((fx->_color >> 16) & 0xFF);
+ uint8_t g = ((fx->_color >> 8) & 0xFF);
+ uint8_t b = (fx->_color & 0xFF);
+
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ int s = (sin(i+fx->_counter_mode_call) * 127) + 128;
+ set_pixel_rgb_color(i, (((uint32_t)(r * s)) / 255), (((uint32_t)(g * s)) / 255), (((uint32_t)(b * s)) / 255));
+ }
+
+ ws2812_send_frame(fx->_led_count);
+
+ fx->_mode_delay = 35 + ((350 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / FX_SPEED_MAX);
+}
+#endif
+
+
+/*
+ * Blink several LEDs on, reset, repeat.
+ * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
+ */
+static void mode_twinkle(ws2812fx_t *fx)
+{
+ if(fx->_counter_mode_step == 0) {
+ strip_off(fx);
+ uint16_t min_leds = max(1, fx->_led_count/5); // make sure, at least one LED is on
+ uint16_t max_leds = max(1, fx->_led_count/2); // make sure, at least one LED is on
+ fx->_counter_mode_step = min_leds + random(max_leds - min_leds);
+ }
+
+ set_pixel_color(random(fx->_led_count), fx->_mode_color);
+ ws2812_send_frame(fx->_led_count);
+
+ fx->_counter_mode_step--;
+ fx->_mode_delay = 50 + ((1986 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / FX_SPEED_MAX);
+}
+
+
+/*
+ * Blink several LEDs in random colors on, reset, repeat.
+ * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
+ */
+static void mode_twinkle_random(ws2812fx_t *fx)
+{
+ fx->_mode_color = color_wheel(random(256));
+ mode_twinkle(fx);
+}
+
+
+/*
+ * Blink several LEDs on, fading out.
+ */
+static void mode_twinkle_fade(ws2812fx_t *fx)
+{
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ uint32_t px_rgb = get_pixel_color(i);
+
+ uint8_t px_r = (px_rgb & 0x00FF0000) >> 16;
+ uint8_t px_g = (px_rgb & 0x0000FF00) >> 8;
+ uint8_t px_b = (px_rgb & 0x000000FF) >> 0;
+
+ // fade out (divide by 2)
+ px_r = px_r >> 1;
+ px_g = px_g >> 1;
+ px_b = px_b >> 1;
+
+ set_pixel_rgb_color(i, px_r, px_g, px_b);
+ }
+
+ if(random(3) == 0) {
+ set_pixel_color(random(fx->_led_count), fx->_mode_color);
+ }
+
+ ws2812_send_frame(fx->_led_count);
+
+ fx->_mode_delay = 100 + ((100 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / FX_SPEED_MAX);
+}
+
+
+/*
+ * Blink several LEDs in random colors on, fading out.
+ */
+static void mode_twinkle_fade_random(ws2812fx_t *fx)
+{
+ fx->_mode_color = color_wheel(random(256));
+ mode_twinkle_fade(fx);
+}
+
+
+/*
+ * Blinks one LED at a time.
+ * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
+ */
+static void mode_sparkle(ws2812fx_t *fx)
+{
+ ws2812_clear();
+ set_pixel_color(random(fx->_led_count),fx->_color);
+ ws2812_send_frame(fx->_led_count);
+ fx->_mode_delay = 10 + ((200 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / FX_SPEED_MAX);
+}
+
+
+/*
+ * Lights all LEDs in the fx->_color. Flashes single white pixels randomly.
+ * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
+ */
+static void mode_flash_sparkle(ws2812fx_t *fx)
+{
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ set_pixel_color(i, fx->_color);
+ }
+
+ if(random(10) == 7) {
+ set_pixel_color(random(fx->_led_count), WHITE);
+ fx->_mode_delay = 20;
+ } else {
+ fx->_mode_delay = 20 + ((200 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / FX_SPEED_MAX);
+ }
+
+ ws2812_send_frame(fx->_led_count);
+}
+
+
+/*
+ * Like flash sparkle. With more flash.
+ * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
+ */
+static void mode_hyper_sparkle(ws2812fx_t *fx)
+{
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ set_pixel_color(i, fx->_color);
+ }
+
+ if(random(10) < 4) {
+ for(uint16_t i=0; i < max(1, fx->_led_count/3); i++) {
+ set_pixel_color(random(fx->_led_count), WHITE);
+ }
+ fx->_mode_delay = 20;
+ } else {
+ fx->_mode_delay = 15 + ((120 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / FX_SPEED_MAX);
+ }
+
+ ws2812_send_frame(fx->_led_count);
+}
+
+
+/*
+ * Classic Strobe effect.
+ */
+static void mode_strobe(ws2812fx_t *fx)
+{
+ if(fx->_counter_mode_call % 2 == 0) {
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ set_pixel_color(i, fx->_color);
+ }
+ fx->_mode_delay = 20;
+ } else {
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ set_pixel_color(i, 0);
+ }
+ fx->_mode_delay = 50 + ((1986 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / FX_SPEED_MAX);
+ }
+ ws2812_send_frame(fx->_led_count);
+}
+
+
+/*
+ * Strobe effect with different strobe count and pause, controled by fx->_speed.
+ */
+static void mode_multi_strobe(ws2812fx_t *fx)
+{
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ set_pixel_color(i, 0);
+ }
+
+ if(fx->_counter_mode_step < (2 * ((fx->_speed / 10) + 1))) {
+ if(fx->_counter_mode_step % 2 == 0) {
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ set_pixel_color(i, fx->_color);
+ }
+ fx->_mode_delay = 20;
+ } else {
+ fx->_mode_delay = 50;
+ }
+
+ } else {
+ fx->_mode_delay = 100 + ((9 - (fx->_speed % 10)) * 125);
+ }
+
+ ws2812_send_frame(fx->_led_count);
+ fx->_counter_mode_step = (fx->_counter_mode_step + 1) % ((2 * ((fx->_speed / 10) + 1)) + 1);
+}
+
+
+/*
+ * Classic Strobe effect. Cycling through the rainbow.
+ */
+static void mode_strobe_rainbow(ws2812fx_t *fx)
+{
+ if(fx->_counter_mode_call % 2 == 0) {
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ set_pixel_color(i, color_wheel(fx->_counter_mode_call % 256));
+ }
+ fx->_mode_delay = 20;
+ } else {
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ set_pixel_color(i, 0);
+ }
+ fx->_mode_delay = 50 + ((1986 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / FX_SPEED_MAX);
+ }
+ ws2812_send_frame(fx->_led_count);
+}
+
+
+/*
+ * Classic Blink effect. Cycling through the rainbow.
+ */
+static void mode_blink_rainbow(ws2812fx_t *fx)
+{
+ if(fx->_counter_mode_call % 2 == 1) {
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ set_pixel_color(i, color_wheel(fx->_counter_mode_call % 256));
+ }
+ ws2812_send_frame(fx->_led_count);
+ } else {
+ strip_off(fx);
+ }
+
+ fx->_mode_delay = 100 + ((1986 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / FX_SPEED_MAX);
+}
+
+
+/*
+ * fx->_color running on white.
+ */
+static void mode_chase_white(ws2812fx_t *fx)
+{
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ set_pixel_color(i, WHITE);
+ }
+
+ uint16_t n = fx->_counter_mode_step;
+ uint16_t m = (fx->_counter_mode_step + 1) % fx->_led_count;
+ set_pixel_color(n, fx->_color);
+ set_pixel_color(m, fx->_color);
+ ws2812_send_frame(fx->_led_count);
+
+ fx->_counter_mode_step = (fx->_counter_mode_step + 1) % fx->_led_count;
+ fx->_mode_delay = 10 + ((30 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / fx->_led_count);
+}
+
+
+/*
+ * White running on fx->_color.
+ */
+static void mode_chase_color(ws2812fx_t *fx)
+{
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ set_pixel_color(i, fx->_color);
+ }
+
+ uint16_t n = fx->_counter_mode_step;
+ uint16_t m = (fx->_counter_mode_step + 1) % fx->_led_count;
+ set_pixel_color(n, WHITE);
+ set_pixel_color(m, WHITE);
+ ws2812_send_frame(fx->_led_count);
+
+ fx->_counter_mode_step = (fx->_counter_mode_step + 1) % fx->_led_count;
+ fx->_mode_delay = 10 + ((30 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / fx->_led_count);
+}
+
+
+/*
+ * White running followed by random color.
+ */
+static void mode_chase_random(ws2812fx_t *fx)
+{
+ if(fx->_counter_mode_step == 0) {
+ set_pixel_color(fx->_led_count-1, color_wheel(fx->_mode_color));
+ fx->_mode_color = get_random_wheel_index(fx->_mode_color);
+ }
+
+ for(uint16_t i=0; i < fx->_counter_mode_step; i++) {
+ set_pixel_color(i, color_wheel(fx->_mode_color));
+ }
+
+ uint16_t n = fx->_counter_mode_step;
+ uint16_t m = (fx->_counter_mode_step + 1) % fx->_led_count;
+ set_pixel_color(n, WHITE);
+ set_pixel_color(m, WHITE);
+
+ ws2812_send_frame(fx->_led_count);
+
+ fx->_counter_mode_step = (fx->_counter_mode_step + 1) % fx->_led_count;
+ fx->_mode_delay = 10 + ((30 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / fx->_led_count);
+}
+
+
+/*
+ * White running on rainbow.
+ */
+static void mode_chase_rainbow(ws2812fx_t *fx)
+{
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ set_pixel_color(i, color_wheel(((i * 256 / fx->_led_count) + (fx->_counter_mode_call % 256)) % 256));
+ }
+
+ uint16_t n = fx->_counter_mode_step;
+ uint16_t m = (fx->_counter_mode_step + 1) % fx->_led_count;
+ set_pixel_color(n, WHITE);
+ set_pixel_color(m, WHITE);
+ ws2812_send_frame(fx->_led_count);
+
+ fx->_counter_mode_step = (fx->_counter_mode_step + 1) % fx->_led_count;
+ fx->_mode_delay = 10 + ((30 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / fx->_led_count);
+}
+
+
+/*
+ * White flashes running on fx->_color.
+ */
+static void mode_chase_flash(ws2812fx_t *fx)
+{
+ const static uint8_t flash_count = 4;
+ uint8_t flash_step = fx->_counter_mode_call % ((flash_count * 2) + 1);
+
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ set_pixel_color(i, fx->_color);
+ }
+
+ if(flash_step < (flash_count * 2)) {
+ if(flash_step % 2 == 0) {
+ uint16_t n = fx->_counter_mode_step;
+ uint16_t m = (fx->_counter_mode_step + 1) % fx->_led_count;
+ set_pixel_color(n, WHITE);
+ set_pixel_color(m, WHITE);
+ fx->_mode_delay = 20;
+ } else {
+ fx->_mode_delay = 30;
+ }
+ } else {
+ fx->_counter_mode_step = (fx->_counter_mode_step + 1) % fx->_led_count;
+ fx->_mode_delay = 10 + ((30 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / fx->_led_count);
+ }
+
+ ws2812_send_frame(fx->_led_count);
+}
+
+
+/*
+ * White flashes running, followed by random color.
+ */
+static void mode_chase_flash_random(ws2812fx_t *fx)
+{
+ const static uint8_t flash_count = 4;
+ uint8_t flash_step = fx->_counter_mode_call % ((flash_count * 2) + 1);
+
+ for(uint16_t i=0; i < fx->_counter_mode_step; i++) {
+ set_pixel_color(i, color_wheel(fx->_mode_color));
+ }
+
+ if(flash_step < (flash_count * 2)) {
+ uint16_t n = fx->_counter_mode_step;
+ uint16_t m = (fx->_counter_mode_step + 1) % fx->_led_count;
+ if(flash_step % 2 == 0) {
+ set_pixel_color(n, WHITE);
+ set_pixel_color(m, WHITE);
+ fx->_mode_delay = 20;
+ } else {
+ set_pixel_color(n, color_wheel(fx->_mode_color));
+ set_pixel_color(m, BLACK);
+ fx->_mode_delay = 30;
+ }
+ } else {
+ fx->_counter_mode_step = (fx->_counter_mode_step + 1) % fx->_led_count;
+ fx->_mode_delay = 1 + ((10 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / fx->_led_count);
+
+ if(fx->_counter_mode_step == 0) {
+ fx->_mode_color = get_random_wheel_index(fx->_mode_color);
+ }
+ }
+
+ ws2812_send_frame(fx->_led_count);
+}
+
+
+/*
+ * Rainbow running on white.
+ */
+static void mode_chase_rainbow_white(ws2812fx_t *fx)
+{
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ set_pixel_color(i, WHITE);
+ }
+
+ uint16_t n = fx->_counter_mode_step;
+ uint16_t m = (fx->_counter_mode_step + 1) % fx->_led_count;
+ set_pixel_color(n, color_wheel(((n * 256 / fx->_led_count) + (fx->_counter_mode_call % 256)) % 256));
+ set_pixel_color(m, color_wheel(((m * 256 / fx->_led_count) + (fx->_counter_mode_call % 256)) % 256));
+ ws2812_send_frame(fx->_led_count);
+
+ fx->_counter_mode_step = (fx->_counter_mode_step + 1) % fx->_led_count;
+ fx->_mode_delay = 10 + ((30 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / fx->_led_count);
+}
+
+
+/*
+ * Black running on fx->_color.
+ */
+static void mode_chase_blackout(ws2812fx_t *fx)
+{
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ set_pixel_color(i, fx->_color);
+ }
+
+ uint16_t n = fx->_counter_mode_step;
+ uint16_t m = (fx->_counter_mode_step + 1) % fx->_led_count;
+ set_pixel_color(n, BLACK);
+ set_pixel_color(m, BLACK);
+ ws2812_send_frame(fx->_led_count);
+
+ fx->_counter_mode_step = (fx->_counter_mode_step + 1) % fx->_led_count;
+ fx->_mode_delay = 10 + ((30 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / fx->_led_count);
+}
+
+
+/*
+ * Black running on rainbow.
+ */
+static void mode_chase_blackout_rainbow(ws2812fx_t *fx)
+{
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ set_pixel_color(i, color_wheel(((i * 256 / fx->_led_count) + (fx->_counter_mode_call % 256)) % 256));
+ }
+
+ uint16_t n = fx->_counter_mode_step;
+ uint16_t m = (fx->_counter_mode_step + 1) % fx->_led_count;
+ set_pixel_color(n, BLACK);
+ set_pixel_color(m, BLACK);
+ ws2812_send_frame(fx->_led_count);
+
+ fx->_counter_mode_step = (fx->_counter_mode_step + 1) % fx->_led_count;
+ fx->_mode_delay = 10 + ((30 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / fx->_led_count);
+}
+
+
+/*
+ * Random color intruduced alternating from start and end of strip.
+ */
+static void mode_color_sweep_random(ws2812fx_t *fx)
+{
+ if(fx->_counter_mode_step == 0 || fx->_counter_mode_step == fx->_led_count) {
+ fx->_mode_color = get_random_wheel_index(fx->_mode_color);
+ }
+
+ if(fx->_counter_mode_step < fx->_led_count) {
+ set_pixel_color(fx->_counter_mode_step, color_wheel(fx->_mode_color));
+ } else {
+ set_pixel_color((fx->_led_count * 2) - fx->_counter_mode_step - 1, color_wheel(fx->_mode_color));
+ }
+ ws2812_send_frame(fx->_led_count);
+
+ fx->_counter_mode_step = (fx->_counter_mode_step + 1) % (fx->_led_count * 2);
+ fx->_mode_delay = 5 + ((50 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / fx->_led_count);
+}
+
+
+/*
+ * Alternating color/white pixels running.
+ */
+static void mode_running_color(ws2812fx_t *fx)
+{
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ if((i + fx->_counter_mode_step) % 4 < 2) {
+ set_pixel_color(i, fx->_mode_color);
+ } else {
+ set_pixel_color(i, WHITE);
+ }
+ }
+ ws2812_send_frame(fx->_led_count);
+
+ fx->_counter_mode_step = (fx->_counter_mode_step + 1) % 4;
+ fx->_mode_delay = 10 + ((30 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / fx->_led_count);
+}
+
+
+/*
+ * Alternating red/blue pixels running.
+ */
+static void mode_running_red_blue(ws2812fx_t *fx)
+{
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ if((i + fx->_counter_mode_step) % 4 < 2) {
+ set_pixel_color(i, RED);
+ } else {
+ set_pixel_color(i, BLUE);
+ }
+ }
+ ws2812_send_frame(fx->_led_count);
+
+ fx->_counter_mode_step = (fx->_counter_mode_step + 1) % 4;
+ fx->_mode_delay = 100 + ((100 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / fx->_led_count);
+}
+
+
+/*
+ * Random colored pixels running.
+ */
+static void mode_running_random(ws2812fx_t *fx)
+{
+ for(uint16_t i=fx->_led_count-1; i > 0; i--) {
+ set_pixel_color(i, get_pixel_color(i-1));
+ }
+
+ if(fx->_counter_mode_step == 0) {
+ fx->_mode_color = get_random_wheel_index(fx->_mode_color);
+ set_pixel_color(0, color_wheel(fx->_mode_color));
+ }
+
+ ws2812_send_frame(fx->_led_count);
+
+ fx->_counter_mode_step = (fx->_counter_mode_step + 1) % 2;
+
+ fx->_mode_delay = 50 + ((50 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / fx->_led_count);
+}
+
+
+/*
+ * K.I.T.T.
+ */
+static void mode_larson_scanner(ws2812fx_t *fx)
+{
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ uint32_t px_rgb = get_pixel_color(i);
+
+ uint8_t px_r = (px_rgb & 0x00FF0000) >> 16;
+ uint8_t px_g = (px_rgb & 0x0000FF00) >> 8;
+ uint8_t px_b = (px_rgb & 0x000000FF) >> 0;
+
+ // fade out (divide by 2)
+ px_r = px_r >> 1;
+ px_g = px_g >> 1;
+ px_b = px_b >> 1;
+
+ set_pixel_rgb_color(i, px_r, px_g, px_b);
+ }
+
+ uint16_t pos = 0;
+
+ if(fx->_counter_mode_step < fx->_led_count) {
+ pos = fx->_counter_mode_step;
+ } else {
+ pos = (fx->_led_count * 2) - fx->_counter_mode_step - 2;
+ }
+
+ set_pixel_color(pos, fx->_color);
+ ws2812_send_frame(fx->_led_count);
+
+ fx->_counter_mode_step = (fx->_counter_mode_step + 1) % ((fx->_led_count * 2) - 2);
+ fx->_mode_delay = 10 + ((10 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / fx->_led_count);
+}
+
+
+/*
+ * Fireing comets from one end.
+ */
+static void mode_comet(ws2812fx_t *fx)
+{
+
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ uint32_t px_rgb = get_pixel_color(i);
+
+ uint8_t px_r = (px_rgb & 0x00FF0000) >> 16;
+ uint8_t px_g = (px_rgb & 0x0000FF00) >> 8;
+ uint8_t px_b = (px_rgb & 0x000000FF) >> 0;
+
+ // fade out (divide by 2)
+ px_r = px_r >> 1;
+ px_g = px_g >> 1;
+ px_b = px_b >> 1;
+
+ set_pixel_rgb_color(i, px_r, px_g, px_b);
+ }
+
+ set_pixel_color(fx->_counter_mode_step, fx->_color);
+ ws2812_send_frame(fx->_led_count);
+
+ fx->_counter_mode_step = (fx->_counter_mode_step + 1) % fx->_led_count;
+ fx->_mode_delay = 10 + ((10 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / fx->_led_count);
+}
+
+
+/*
+ * Firework sparks.
+ */
+static void mode_fireworks(ws2812fx_t *fx)
+{
+ uint32_t px_rgb = 0;
+ uint8_t px_r = 0;
+ uint8_t px_g = 0;
+ uint8_t px_b = 0;
+
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ px_rgb = get_pixel_color(i);
+
+ px_r = (px_rgb & 0x00FF0000) >> 16;
+ px_g = (px_rgb & 0x0000FF00) >> 8;
+ px_b = (px_rgb & 0x000000FF) >> 0;
+
+ // fade out (divide by 2)
+ px_r = px_r >> 1;
+ px_g = px_g >> 1;
+ px_b = px_b >> 1;
+
+ set_pixel_rgb_color(i, px_r, px_g, px_b);
+ }
+
+ // first LED has only one neighbour
+ px_r = (((get_pixel_color(1) & 0x00FF0000) >> 16) >> 1) + ((get_pixel_color(0) & 0x00FF0000) >> 16);
+ px_g = (((get_pixel_color(1) & 0x0000FF00) >> 8) >> 1) + ((get_pixel_color(0) & 0x0000FF00) >> 8);
+ px_b = (((get_pixel_color(1) & 0x000000FF) >> 0) >> 1) + ((get_pixel_color(0) & 0x000000FF) >> 0);
+ set_pixel_rgb_color(0, px_r, px_g, px_b);
+
+ // set brightness(i) = ((brightness(i-1)/2 + brightness(i+1)) / 2) + brightness(i)
+ for(uint16_t i=1; i < fx->_led_count-1; i++) {
+ px_r = ((
+ (((get_pixel_color(i-1) & 0x00FF0000) >> 16) >> 1) +
+ (((get_pixel_color(i+1) & 0x00FF0000) >> 16) >> 0) ) >> 1) +
+ (((get_pixel_color(i ) & 0x00FF0000) >> 16) >> 0);
+
+ px_g = ((
+ (((get_pixel_color(i-1) & 0x0000FF00) >> 8) >> 1) +
+ (((get_pixel_color(i+1) & 0x0000FF00) >> 8) >> 0) ) >> 1) +
+ (((get_pixel_color(i ) & 0x0000FF00) >> 8) >> 0);
+
+ px_b = ((
+ (((get_pixel_color(i-1) & 0x000000FF) >> 0) >> 1) +
+ (((get_pixel_color(i+1) & 0x000000FF) >> 0) >> 0) ) >> 1) +
+ (((get_pixel_color(i ) & 0x000000FF) >> 0) >> 0);
+
+ set_pixel_rgb_color(i, px_r, px_g, px_b);
+ }
+
+ // last LED has only one neighbour
+ px_r = (((get_pixel_color(fx->_led_count-2) & 0x00FF0000) >> 16) >> 2) + ((get_pixel_color(fx->_led_count-1) & 0x00FF0000) >> 16);
+ px_g = (((get_pixel_color(fx->_led_count-2) & 0x0000FF00) >> 8) >> 2) + ((get_pixel_color(fx->_led_count-1) & 0x0000FF00) >> 8);
+ px_b = (((get_pixel_color(fx->_led_count-2) & 0x000000FF) >> 0) >> 2) + ((get_pixel_color(fx->_led_count-1) & 0x000000FF) >> 0);
+ set_pixel_rgb_color(fx->_led_count-1, px_r, px_g, px_b);
+
+ if(!fx->_triggered) {
+ for(uint16_t i=0; i<max(1,fx->_led_count/20); i++) {
+ if(random(10) == 0) {
+ set_pixel_color(random(fx->_led_count), fx->_mode_color);
+ }
+ }
+ } else {
+ for(uint16_t i=0; i<max(1,fx->_led_count/10); i++) {
+ set_pixel_color(random(fx->_led_count), fx->_mode_color);
+ }
+ }
+
+ ws2812_send_frame(fx->_led_count);
+
+ fx->_mode_delay = 20 + ((20 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / fx->_led_count);
+}
+
+
+/*
+ * Random colored firework sparks.
+ */
+static void mode_fireworks_random(ws2812fx_t *fx)
+{
+ fx->_mode_color = color_wheel(random(256));
+ mode_fireworks(fx);
+}
+
+
+/*
+ * Alternating red/green pixels running.
+ */
+static void mode_merry_christmas(ws2812fx_t *fx)
+{
+ for(uint16_t i=0; i < fx->_led_count; i++) {
+ if((i + fx->_counter_mode_step) % 4 < 2) {
+ set_pixel_color(i, RED);
+ } else {
+ set_pixel_color(i, GREEN);
+ }
+ }
+ ws2812_send_frame(fx->_led_count);
+
+ fx->_counter_mode_step = (fx->_counter_mode_step + 1) % 4;
+ fx->_mode_delay = 100 + ((100 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / fx->_led_count);
+}
+
+
+static void mode_fire_flicker_int(ws2812fx_t *fx, int rev_intensity)
+{
+ uint8_t p_r = (fx->_color & 0x00FF0000) >> 16;
+ uint8_t p_g = (fx->_color & 0x0000FF00) >> 8;
+ uint8_t p_b = (fx->_color & 0x000000FF) >> 0;
+ uint8_t flicker_val = max(p_r,max(p_g, p_b))/rev_intensity;
+ for(uint16_t i=0; i < fx->_led_count; i++)
+ {
+ int flicker = random(flicker_val);
+ int r1 = p_r-flicker;
+ int g1 = p_g-flicker;
+ int b1 = p_b-flicker;
+ if(g1<0) g1=0;
+ if(r1<0) r1=0;
+ if(b1<0) b1=0;
+ set_pixel_rgb_color(i,r1,g1, b1);
+ }
+ ws2812_send_frame(fx->_led_count);
+ fx->_mode_delay = 10 + ((500 * (uint32_t)(FX_SPEED_MAX - fx->_speed)) / FX_SPEED_MAX);
+}
+
+/*
+ * Random flickering.
+ */
+static void mode_fire_flicker(ws2812fx_t *fx)
+{
+ mode_fire_flicker_int(fx, 3);
+}
+
+/*
+ * Random flickering, less intesity.
+ */
+static void mode_fire_flicker_soft(ws2812fx_t *fx)
+{
+ mode_fire_flicker_int(fx, 6);
+}
+
+
+typedef void (*mode_ptr)(ws2812fx_t *fx);
+
+static struct {
+ mode_ptr _fn;
+ char *_name;
+} const modes[] = {
+ { mode_static, "Static" },
+ { mode_blink, "Blink" },
+ { mode_breath, "Breath" },
+ { mode_color_wipe, "Color Wipe" },
+ { mode_color_wipe_random, "Color Wipe Random" },
+ { mode_random_color, "Random Color" },
+ { mode_single_dynamic, "Single Dynamic" },
+ { mode_multi_dynamic, "Multi Dynamic" },
+ { mode_rainbow, "Rainbow" },
+ { mode_rainbow_cycle, "Rainbow Cycle" },
+ { mode_theater_chase, "Theater Chase" },
+ { mode_theater_chase_rainbow, "Theater Chase Rainbow" },
+ { mode_scan, "Scan" },
+ { mode_dual_scan, "Dual Scan" },
+ { mode_fade, "Fade" },
+#if 0
+ { mode_running_lights, "Running Lights" },
+#endif
+ { mode_twinkle, "Twinkle" },
+ { mode_twinkle_random, "Twinkle Random" },
+ { mode_twinkle_fade, "Twinkle Fade" },
+ { mode_twinkle_fade_random, "Twinkle Fade Random" },
+ { mode_sparkle, "Sparkle" },
+ { mode_flash_sparkle, "Flash Sparkle" },
+ { mode_hyper_sparkle, "Hyper Sparkle" },
+ { mode_strobe, "Strobe" },
+ { mode_strobe_rainbow, "Strobe Rainbow" },
+ { mode_multi_strobe, "Multi Strobe" },
+ { mode_blink_rainbow, "Blink Rainbow" },
+ { mode_chase_white, "Chase White" },
+ { mode_chase_color, "Chase Color" },
+ { mode_chase_random, "Chase Random" },
+ { mode_chase_rainbow, "Chase Rainbow" },
+ { mode_chase_flash, "Chase Flash" },
+ { mode_chase_flash_random, "Chase Flash Random" },
+ { mode_chase_rainbow_white, "Chase Rainbow White" },
+ { mode_chase_blackout, "Chase Blackout" },
+ { mode_chase_blackout_rainbow, "Chase Blackout Rainbow" },
+ { mode_color_sweep_random, "Color Sweep Random" },
+ { mode_running_color, "Running Color" },
+ { mode_running_red_blue, "Running Red Blue" },
+ { mode_running_random, "Running Random" },
+ { mode_larson_scanner, "Larson Scanner" },
+ { mode_comet, "Comet" },
+ { mode_fireworks, "Fireworks" },
+ { mode_fireworks_random, "Fireworks Random" },
+ { mode_merry_christmas, "Merry Christmas" },
+ { mode_fire_flicker, "Fire Flicker" },
+ { mode_fire_flicker_soft, "Fire Flicker (soft)" },
+};
+
+#define CALL_MODE(fx, n) (modes[n]._fn)(fx);
+
+void WS2812FX(ws2812fx_t *fx, uint16_t n, const struct pio *pin)
+{
+ ws2812_config(pin);
+
+ fx->_speed = FX_DEFAULT_SPEED;
+ fx->_brightness = FX_DEFAULT_BRIGHTNESS;
+ fx->_running = 0;
+ fx->_led_count = n;
+ fx->_mode_last_call_time = 0;
+ fx->_mode_delay = 0;
+ fx->_color = FX_DEFAULT_COLOR;
+ fx->_mode_color = FX_DEFAULT_COLOR;
+ fx->_counter_mode_call = 0;
+ fx->_counter_mode_step = 0;
+}
+
+void FX_init(ws2812fx_t *fx)
+{
+ FX_setBrightness(fx, fx->_brightness);
+ ws2812_send_frame(fx->_led_count);
+}
+
+void FX_service(ws2812fx_t *fx)
+{
+ if(fx->_running || fx->_triggered) {
+ unsigned long now = millis();
+
+ if(now - fx->_mode_last_call_time > fx->_mode_delay || fx->_triggered) {
+ CALL_MODE(fx, fx->_mode_index);
+ fx->_counter_mode_call++;
+ fx->_mode_last_call_time = now;
+ fx->_triggered = 0;
+ }
+ }
+}
+
+void FX_start(ws2812fx_t *fx)
+{
+ fx->_counter_mode_call = 0;
+ fx->_counter_mode_step = 0;
+ fx->_mode_last_call_time = 0;
+ fx->_running = 1;
+}
+
+void FX_stop(ws2812fx_t *fx)
+{
+ fx->_running = 0;
+ strip_off(fx);
+}
+
+void FX_trigger(ws2812fx_t *fx)
+{
+ fx->_triggered = 1;
+}
+
+void FX_setMode(ws2812fx_t *fx, uint8_t m)
+{
+ fx->_counter_mode_call = 0;
+ fx->_counter_mode_step = 0;
+ fx->_mode_last_call_time = 0;
+ fx->_mode_index = constrain(m, 0, FX_MODE_COUNT-1);
+ fx->_mode_color = fx->_color;
+ ws2812_set_brightness(fx->_brightness);
+ //strip_off(fx);
+}
+
+void FX_setSpeed(ws2812fx_t *fx, uint8_t s)
+{
+ fx->_counter_mode_call = 0;
+ fx->_counter_mode_step = 0;
+ fx->_mode_last_call_time = 0;
+ fx->_speed = constrain(s, FX_SPEED_MIN, FX_SPEED_MAX);
+ //strip_off(fx);
+}
+
+void FX_increaseSpeed(ws2812fx_t *fx, uint8_t s)
+{
+ s = constrain(fx->_speed + s, FX_SPEED_MIN, FX_SPEED_MAX);
+ FX_setSpeed(fx, s);
+}
+
+void FX_decreaseSpeed(ws2812fx_t *fx, uint8_t s)
+{
+ s = constrain(fx->_speed - s, FX_SPEED_MIN, FX_SPEED_MAX);
+ FX_setSpeed(fx, s);
+}
+
+void FX_setRGBColor(ws2812fx_t *fx, uint8_t r, uint8_t g, uint8_t b)
+{
+ FX_setColor(fx, ((uint32_t)r << 16) | ((uint32_t)g << 8) | b);
+}
+
+void FX_setColor(ws2812fx_t *fx, uint32_t c)
+{
+ fx->_color = c;
+ fx->_counter_mode_call = 0;
+ fx->_counter_mode_step = 0;
+ fx->_mode_last_call_time = 0;
+ fx->_mode_color = fx->_color;
+ ws2812_set_brightness(fx->_brightness);
+ //strip_off(fx);
+}
+
+void FX_setBrightness(ws2812fx_t *fx, uint8_t b)
+{
+ fx->_brightness = constrain(b, FX_BRIGHTNESS_MIN, FX_BRIGHTNESS_MAX);
+ ws2812_set_brightness(fx->_brightness);
+ ws2812_send_frame(fx->_led_count);
+}
+
+void FX_increaseBrightness(ws2812fx_t *fx, uint8_t s)
+{
+ s = constrain(fx->_brightness + s, FX_BRIGHTNESS_MIN, FX_BRIGHTNESS_MAX);
+ FX_setBrightness(fx, s);
+}
+
+void FX_decreaseBrightness(ws2812fx_t *fx, uint8_t s)
+{
+ s = constrain(fx->_brightness - s, FX_BRIGHTNESS_MIN, FX_BRIGHTNESS_MAX);
+ FX_setBrightness(fx, s);
+}
+
+int FX_isRunning(ws2812fx_t *fx)
+{
+ return fx->_running;
+}
+
+uint8_t FX_getMode(ws2812fx_t *fx)
+{
+ return fx->_mode_index;
+}
+
+uint8_t FX_getSpeed(ws2812fx_t *fx)
+{
+ return fx->_speed;
+}
+
+uint8_t FX_getBrightness(ws2812fx_t *fx)
+{
+ return fx->_brightness;
+}
+
+uint8_t FX_getModeCount(ws2812fx_t *fx)
+{
+ return FX_MODE_COUNT;
+}
+
+uint32_t FX_getColor(ws2812fx_t *fx)
+{
+ return fx->_color;
+}
+
+const char* FX_getModeName(uint8_t m)
+{
+ if (m < FX_MODE_COUNT) {
+ return modes[m]._name;
+ } else {
+ return "";
+ }
+}
--- /dev/null
+/*
+ WS2812SFX.cpp - Library for WS2812 LED effects.
+ Harm Aldick - 2016
+ www.aldick.org
+ FEATURES
+ * A lot of blinken modes and counting
+ * WS2812FX can be used as drop-in replacement for Adafruit Neopixel Library
+ NOTES
+ * Uses the Adafruit Neopixel library. Get it here:
+ https://github.com/adafruit/Adafruit_NeoPixel
+ LICENSE
+ The MIT License (MIT)
+ Copyright (c) 2016 Harm Aldick
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ CHANGELOG
+ 2016-05-28 Initial beta release
+ 2016-06-03 Code cleanup, minor improvements, new modes
+ 2016-06-04 2 new sfx, fixed setColor (now also resets _mode_color)
+ 2017-02-02 added external trigger functionality (e.g. for sound-to-light)
+ 2017-02-02 removed "blackout" on mode, speed or color-change
+*/
+
+#include "core/systick.h"
+#include "extdrv/ws2812.h"
+#include "extdrv/ws2812sfx.h"
+
+
+static uint32_t random(uint32_t max)
+{
+ static uint32_t seed = 0;
+ /* Numerical Recipes */
+ seed = seed * 1664525 + 1013904223;
+ return seed % max;
+}
+
+static uint32_t abs(int32_t val)
+{
+ return val < 0 ? -val : val;
+}
+
+static uint32_t min(uint32_t a, uint32_t b)
+{
+ return a < b ? a : b;
+}
+
+static unsigned long millis(void)
+{
+ return systick_get_tick_count();
+}
+
+static uint32_t constrain(int32_t val, int32_t min, int32_t max)
+{
+ return val < min ? min : val > max ? max : val;
+}
+
+
+/* #####################################################
+#
+# Color and Blinken Functions
+#
+##################################################### */
+
+
+static void set_color(uint32_t color)
+{
+ ws2812_set_pixel(0, color >> 16, (color >> 8) & 0xff, color & 0xff);
+ ws2812_send_frame(1);
+}
+
+
+/*
+ * Put a value 0 to 255 in to get a color value.
+ * The colours are a transition r -> g -> b -> back to r
+ * Inspired by the Adafruit examples.
+ */
+static uint32_t color_wheel(uint8_t pos)
+{
+ pos = 255 - pos;
+ if(pos < 85) {
+ return ((uint32_t)(255 - pos * 3) << 16) | ((uint32_t)(0) << 8) | (pos * 3);
+ } else if(pos < 170) {
+ pos -= 85;
+ return ((uint32_t)(0) << 16) | ((uint32_t)(pos * 3) << 8) | (255 - pos * 3);
+ } else {
+ pos -= 170;
+ return ((uint32_t)(pos * 3) << 16) | ((uint32_t)(255 - pos * 3) << 8) | (0);
+ }
+}
+
+
+/*
+ * Returns a new, random wheel index with a minimum distance of 42 from pos.
+ */
+static uint8_t get_random_wheel_index(uint8_t pos)
+{
+ uint8_t r = 0;
+ uint8_t x = 0;
+ uint8_t y = 0;
+ uint8_t d = 0;
+
+ while(d < 42) {
+ r = random(256);
+ x = abs(pos - r);
+ y = 255 - x;
+ d = min(x, y);
+ }
+
+ return r;
+}
+
+
+/*
+ * No blinking. Just plain old static light.
+ */
+static void mode_static(ws2812sfx_t *sfx)
+{
+ set_color(sfx->_color);
+
+ sfx->_mode_delay = 50;
+}
+
+
+/*
+ * Normal blinking. 50% on/off time.
+ */
+static void mode_blink(ws2812sfx_t *sfx)
+{
+ if(sfx->_counter_mode_call % 2 == 1) {
+ set_color(sfx->_color);
+ } else {
+ set_color(0);
+ }
+
+ sfx->_mode_delay = 100 + ((1986 * (uint32_t)(SFX_SPEED_MAX - sfx->_speed)) / SFX_SPEED_MAX);
+}
+
+
+/*
+ * Lights all LEDs in one random color up. Then switches them
+ * to the next random color.
+ */
+static void mode_random_color(ws2812sfx_t *sfx)
+{
+ sfx->_mode_color = get_random_wheel_index(sfx->_mode_color);
+
+ set_color(color_wheel(sfx->_mode_color));
+
+ sfx->_mode_delay = 100 + ((5000 * (uint32_t)(SFX_SPEED_MAX - sfx->_speed)) / SFX_SPEED_MAX);
+}
+
+
+/*
+ * Lights every LED in a random color. Changes all LED at the same time
+ * to new random colors.
+ */
+static void mode_multi_dynamic(ws2812sfx_t *sfx)
+{
+ set_color(color_wheel(random(256)));
+
+ sfx->_mode_delay = 100 + ((5000 * (uint32_t)(SFX_SPEED_MAX - sfx->_speed)) / SFX_SPEED_MAX);
+}
+
+
+/*
+ * Does the "standby-breathing" of well known i-Devices. Fixed Speed.
+ * Use mode "fade" if you like to have something similar with a different speed.
+ */
+static void mode_breath(ws2812sfx_t *sfx)
+{
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // step
+ uint16_t breath_delay_steps[] = { 7, 9, 13, 15, 16, 17, 18, 930, 19, 18, 15, 13, 9, 7, 4, 5, 10 }; // magic numbers for breathing LED
+ uint8_t breath_brightness_steps[] = { 150, 125, 100, 75, 50, 25, 16, 15, 16, 25, 50, 75, 100, 125, 150, 220, 255 }; // even more magic numbers!
+
+ if(sfx->_counter_mode_call == 0) {
+ sfx->_mode_color = breath_brightness_steps[0] + 1;
+ }
+
+ uint32_t breath_brightness = sfx->_mode_color; // we use sfx->_mode_color to store the brightness
+
+ if(sfx->_counter_mode_step < 8) {
+ breath_brightness--;
+ } else {
+ breath_brightness++;
+ }
+
+ // update index of current delay when target brightness is reached, start over after the last step
+ if(breath_brightness == breath_brightness_steps[sfx->_counter_mode_step]) {
+ sfx->_counter_mode_step = (sfx->_counter_mode_step + 1) % (sizeof(breath_brightness_steps)/sizeof(uint8_t));
+ }
+
+ int b = (breath_brightness * (sfx->_brightness + 1)) >> 8; // keep brightness below brightness set by user
+ ws2812_set_brightness(b);
+
+ set_color(sfx->_color); // set all LEDs to selected color
+
+ sfx->_mode_color = breath_brightness; // we use sfx->_mode_color to store the brightness
+ sfx->_mode_delay = breath_delay_steps[sfx->_counter_mode_step];
+}
+
+
+/*
+ * Fades the LEDs on and (almost) off again.
+ */
+static void mode_fade(ws2812sfx_t *sfx)
+{
+ int b = sfx->_counter_mode_step - 127;
+ b = 255 - (abs(b) * 2);
+ b = (b * (min(20, sfx->_brightness) + 1)) >> 8;
+ ws2812_set_brightness(b);
+ set_color(sfx->_color);
+
+ sfx->_counter_mode_step = (sfx->_counter_mode_step + 1) % 255;
+ sfx->_mode_delay = 5 + ((15 * (uint32_t)(SFX_SPEED_MAX - sfx->_speed)) / SFX_SPEED_MAX);
+}
+
+
+/*
+ * Cycles all LEDs at once through a rainbow.
+ */
+static void mode_rainbow(ws2812sfx_t *sfx)
+{
+ uint32_t color = color_wheel(sfx->_counter_mode_step);
+ set_color(color);
+
+ sfx->_counter_mode_step = (sfx->_counter_mode_step + 1) % 256;
+
+ sfx->_mode_delay = 1 + ((50 * (uint32_t)(SFX_SPEED_MAX - sfx->_speed)) / SFX_SPEED_MAX);
+}
+
+
+/*
+ * Classic Strobe effect.
+ */
+static void mode_strobe(ws2812sfx_t *sfx)
+{
+ if(sfx->_counter_mode_call % 2 == 0) {
+ set_color(sfx->_color);
+ sfx->_mode_delay = 20;
+ } else {
+ set_color(0);
+ sfx->_mode_delay = 50 + ((1986 * (uint32_t)(SFX_SPEED_MAX - sfx->_speed)) / SFX_SPEED_MAX);
+ }
+}
+
+
+/*
+ * Strobe effect with different strobe count and pause, controled by sfx->_speed.
+ */
+static void mode_multi_strobe(ws2812sfx_t *sfx)
+{
+ if(sfx->_counter_mode_step < (2 * ((sfx->_speed / 10) + 1))) {
+ if(sfx->_counter_mode_step % 2 == 0) {
+ set_color(sfx->_color);
+ sfx->_mode_delay = 20;
+ } else {
+ set_color(0);
+ sfx->_mode_delay = 50;
+ }
+
+ } else {
+ set_color(0);
+ sfx->_mode_delay = 100 + ((9 - (sfx->_speed % 10)) * 125);
+ }
+
+ sfx->_counter_mode_step = (sfx->_counter_mode_step + 1) % ((2 * ((sfx->_speed / 10) + 1)) + 1);
+}
+
+
+/*
+ * Classic Strobe effect. Cycling through the rainbow.
+ */
+static void mode_strobe_rainbow(ws2812sfx_t *sfx)
+{
+ if(sfx->_counter_mode_call % 2 == 0) {
+ set_color(color_wheel(sfx->_counter_mode_call % 256));
+ sfx->_mode_delay = 20;
+ } else {
+ set_color(0);
+ sfx->_mode_delay = 50 + ((1986 * (uint32_t)(SFX_SPEED_MAX - sfx->_speed)) / SFX_SPEED_MAX);
+ }
+}
+
+
+/*
+ * Classic Blink effect. Cycling through the rainbow.
+ */
+static void mode_blink_rainbow(ws2812sfx_t *sfx)
+{
+ if(sfx->_counter_mode_call % 2 == 1) {
+ set_color(color_wheel(sfx->_counter_mode_call % 256));
+ } else {
+ set_color(0);
+ }
+
+ sfx->_mode_delay = 100 + ((1986 * (uint32_t)(SFX_SPEED_MAX - sfx->_speed)) / SFX_SPEED_MAX);
+}
+
+
+typedef void (*mode_ptr)(ws2812sfx_t *sfx);
+
+static struct {
+ mode_ptr _fn;
+ char *_name;
+} const modes[] = {
+ { mode_static, "Static" },
+ { mode_blink, "Blink" },
+ { mode_breath, "Breath" },
+ { mode_random_color, "Random Color" },
+ { mode_multi_dynamic, "Multi Dynamic" },
+ { mode_rainbow, "Rainbow" },
+ { mode_fade, "Fade" },
+ { mode_strobe, "Strobe" },
+ { mode_strobe_rainbow, "Strobe Rainbow" },
+ { mode_multi_strobe, "Multi Strobe" },
+ { mode_blink_rainbow, "Blink Rainbow" },
+};
+
+#define CALL_MODE(sfx, n) (modes[n]._fn)(sfx);
+
+void WS2812SFX(ws2812sfx_t *sfx, const struct pio *pin)
+{
+ ws2812_config(pin);
+
+ sfx->_speed = SFX_DEFAULT_SPEED;
+ sfx->_brightness = SFX_DEFAULT_BRIGHTNESS;
+ sfx->_running = 0;
+ sfx->_mode_last_call_time = 0;
+ sfx->_mode_delay = 0;
+ sfx->_color = SFX_DEFAULT_COLOR;
+ sfx->_mode_color = SFX_DEFAULT_COLOR;
+ sfx->_counter_mode_call = 0;
+ sfx->_counter_mode_step = 0;
+}
+
+void SFX_init(ws2812sfx_t *sfx)
+{
+ SFX_setBrightness(sfx, sfx->_brightness);
+ ws2812_send_frame(1);
+}
+
+void SFX_service(ws2812sfx_t *sfx)
+{
+ if(sfx->_running) {
+ unsigned long now = millis();
+
+ if(now - sfx->_mode_last_call_time > sfx->_mode_delay) {
+ CALL_MODE(sfx, sfx->_mode_index);
+ sfx->_counter_mode_call++;
+ sfx->_mode_last_call_time = now;
+ }
+ }
+}
+
+void SFX_start(ws2812sfx_t *sfx)
+{
+ sfx->_counter_mode_call = 0;
+ sfx->_counter_mode_step = 0;
+ sfx->_mode_last_call_time = 0;
+ sfx->_running = 1;
+}
+
+void SFX_stop(ws2812sfx_t *sfx)
+{
+ sfx->_running = 0;
+ set_color(0);
+}
+
+void SFX_setMode(ws2812sfx_t *sfx, uint8_t m)
+{
+ sfx->_counter_mode_call = 0;
+ sfx->_counter_mode_step = 0;
+ sfx->_mode_last_call_time = 0;
+ sfx->_mode_index = constrain(m, 0, SFX_MODE_COUNT-1);
+ sfx->_mode_color = sfx->_color;
+ ws2812_set_brightness(sfx->_brightness);
+ //set_color(0);
+}
+
+void SFX_setSpeed(ws2812sfx_t *sfx, uint8_t s)
+{
+ sfx->_counter_mode_call = 0;
+ sfx->_counter_mode_step = 0;
+ sfx->_mode_last_call_time = 0;
+ sfx->_speed = constrain(s, SFX_SPEED_MIN, SFX_SPEED_MAX);
+ //set_color(0);
+}
+
+void SFX_increaseSpeed(ws2812sfx_t *sfx, uint8_t s)
+{
+ s = constrain(sfx->_speed + s, SFX_SPEED_MIN, SFX_SPEED_MAX);
+ SFX_setSpeed(sfx, s);
+}
+
+void SFX_decreaseSpeed(ws2812sfx_t *sfx, uint8_t s)
+{
+ s = constrain(sfx->_speed - s, SFX_SPEED_MIN, SFX_SPEED_MAX);
+ SFX_setSpeed(sfx, s);
+}
+
+void SFX_setRGBColor(ws2812sfx_t *sfx, uint8_t r, uint8_t g, uint8_t b)
+{
+ SFX_setColor(sfx, ((uint32_t)r << 16) | ((uint32_t)g << 8) | b);
+}
+
+void SFX_setColor(ws2812sfx_t *sfx, uint32_t c)
+{
+ sfx->_color = c;
+ sfx->_counter_mode_call = 0;
+ sfx->_counter_mode_step = 0;
+ sfx->_mode_last_call_time = 0;
+ sfx->_mode_color = sfx->_color;
+ ws2812_set_brightness(sfx->_brightness);
+ //set_color(0);
+}
+
+void SFX_setBrightness(ws2812sfx_t *sfx, uint8_t b)
+{
+ sfx->_brightness = constrain(b, SFX_BRIGHTNESS_MIN, SFX_BRIGHTNESS_MAX);
+ ws2812_set_brightness(sfx->_brightness);
+ ws2812_send_frame(1);
+}
+
+void SFX_increaseBrightness(ws2812sfx_t *sfx, uint8_t s)
+{
+ s = constrain(sfx->_brightness + s, SFX_BRIGHTNESS_MIN, SFX_BRIGHTNESS_MAX);
+ SFX_setBrightness(sfx, s);
+}
+
+void SFX_decreaseBrightness(ws2812sfx_t *sfx, uint8_t s)
+{
+ s = constrain(sfx->_brightness - s, SFX_BRIGHTNESS_MIN, SFX_BRIGHTNESS_MAX);
+ SFX_setBrightness(sfx, s);
+}
+
+
+const char* SFX_getModeName(enum ModeSFX m)
+{
+ if (m < SFX_MODE_COUNT) {
+ return modes[m]._name;
+ } else {
+ return "";
+ }
+}