当前位置: 首页 > 工具软件 > php-ws > 使用案例 >

WS2812灯珠(六)---移植WS2812FX库

谷出野
2023-12-01

移植WS2812FX库

该库实现了比Adafruit_NeoPixel库更丰富的显示效果,且该库的实现是建立在Adafruit_NeoPixel库的基础上实现的。这里的移植是基于STM32 Keil MDK实现的(其他平台未测试)。

移植原则

  • 移植需要用到的变量,这里全部声明为static,外部函数需要使用其变量时必须通过函数的方式访问。
  • 为所有向外提供使用的函数统一添加Adafruit_NeoPixel_的前缀,既便于识别又不会和其他文件函数产生重名冲突。
  • 当函数存在重载情形时将重载函数的不一致参数组合作为函数名的后缀。
  • 最大限度的尊重源库的命名,方便后续升级

移植过程

WS2812FX库对应的WS2812FX.cpp文件中有1600多行代码。贸然直接复制过来修改的话会有太多的工作量。我们根据具体的内容将代码分为两部分移植—基础函数和功能函数。

  1. keil MDK 编译器不支持二进制表示,因此首先要实现binary.h文件(直接从arduino库中复制出来)。
  2. 实现库中需要用到的宏函数和一些不存在类型的typedef
  3. 移植基础函数部分(暂时屏蔽掉一些不重要的辅助函数)
  4. 移植功能函数部分

未移植自定义显示效果部分的函数;屏蔽掉了显示效果名称部分的定义(节省部分flash空间)

移植结果

WS2812FX.h

#ifndef __WS2812FX_H
#define __WS2812FX_H
#define WS2812FX_VERSION 0x00000001

/*
  移植该库需要建立在移植好Adafruit_NeoPixel库的基础上来实现
  1.keil MDK编译器不支持二进制表示,而该库用到了二进制表示,需要用到binary.h文件
  2.为了减少库文件本身内容的修改,定义了一些方便移植的宏。
  3.当前版本移植匆忙未完全测试
  4.该库需要实现获取系统运行时间的函数,这里直接利用了HAL库提供的HAL_GetTick函数。
 */

#include <stdint.h>
#include <stdbool.h>
#include "stm32f4xx.h"
#include "binary.h"


/* 为方便移植定义的宏 */
typedef char __FlashStringHelper;
#define millis HAL_GetTick
#define PROGMEM 
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define constrain(amt,low,high) ((amt)<=(low)?(low):((amt)>=(high)?(high):(amt)))
typedef bool boolean;
#define FSH(x) (__FlashStringHelper*)(x)

//#include <Adafruit_NeoPixel.h>

#define DEFAULT_BRIGHTNESS (uint8_t)50
#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")
  #define SPEED_MIN (uint16_t)2
#else
  //#pragma message("Compiling for Arduino")
  #define SPEED_MIN (uint16_t)10
#endif
#define SPEED_MAX (uint16_t)65535

#define BRIGHTNESS_MIN (uint8_t)0
#define BRIGHTNESS_MAX (uint8_t)255

/* 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
#define GREEN      (uint32_t)0x00FF00
#define BLUE       (uint32_t)0x0000FF
#define WHITE      (uint32_t)0xFFFFFF
#define BLACK      (uint32_t)0x000000
#define YELLOW     (uint32_t)0xFFFF00
#define CYAN       (uint32_t)0x00FFFF
#define MAGENTA    (uint32_t)0xFF00FF
#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 DIM(c)     (uint32_t)((c >> 2) & 0x3f3f3f3f) // color at 25% intensity
#define DARK(c)    (uint32_t)((c >> 4) & 0x0f0f0f0f) // color at  6% intensity


// segment options
// bit    7: reverse animation
// bits 4-6: fade rate (0-7)
// bit    3: gamma correction
// bits 1-2: size
// bits   0: TBD
#define NO_OPTIONS   (uint8_t)B00000000
#define REVERSE      (uint8_t)B10000000
#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
#define FADE_SLOW    (uint8_t)B01000000
#define FADE_XSLOW   (uint8_t)B01010000
#define FADE_XXSLOW  (uint8_t)B01100000
#define FADE_GLACIAL (uint8_t)B01110000
#define FADE_RATE    ((_seg->options >> 4) & 7)
#define GAMMA        (uint8_t)B00001000
#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  ((_seg->options >> 1) & 3)

// segment runtime options (aux_param2)
#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 63 //(sizeof(_names)/sizeof(_names[0]))

#define FX_MODE_STATIC                   0
#define FX_MODE_BLINK                    1
#define FX_MODE_BREATH                   2
#define FX_MODE_COLOR_WIPE               3
#define FX_MODE_COLOR_WIPE_INV           4 
#define FX_MODE_COLOR_WIPE_REV           5
#define FX_MODE_COLOR_WIPE_REV_INV       6
#define FX_MODE_COLOR_WIPE_RANDOM        7
#define FX_MODE_RANDOM_COLOR             8
#define FX_MODE_SINGLE_DYNAMIC           9
#define FX_MODE_MULTI_DYNAMIC           10
#define FX_MODE_RAINBOW                 11
#define FX_MODE_RAINBOW_CYCLE           12
#define FX_MODE_SCAN                    13
#define FX_MODE_DUAL_SCAN               14
#define FX_MODE_FADE                    15
#define FX_MODE_THEATER_CHASE           16
#define FX_MODE_THEATER_CHASE_RAINBOW   17
#define FX_MODE_RUNNING_LIGHTS          18
#define FX_MODE_TWINKLE                 19
#define FX_MODE_TWINKLE_RANDOM          20
#define FX_MODE_TWINKLE_FADE            21
#define FX_MODE_TWINKLE_FADE_RANDOM     22
#define FX_MODE_SPARKLE                 23
#define FX_MODE_FLASH_SPARKLE           24
#define FX_MODE_HYPER_SPARKLE           25
#define FX_MODE_STROBE                  26
#define FX_MODE_STROBE_RAINBOW          27
#define FX_MODE_MULTI_STROBE            28
#define FX_MODE_BLINK_RAINBOW           29
#define FX_MODE_CHASE_WHITE             30
#define FX_MODE_CHASE_COLOR             31
#define FX_MODE_CHASE_RANDOM            32
#define FX_MODE_CHASE_RAINBOW           33
#define FX_MODE_CHASE_FLASH             34
#define FX_MODE_CHASE_FLASH_RANDOM      35
#define FX_MODE_CHASE_RAINBOW_WHITE     36
#define FX_MODE_CHASE_BLACKOUT          37
#define FX_MODE_CHASE_BLACKOUT_RAINBOW  38
#define FX_MODE_COLOR_SWEEP_RANDOM      39
#define FX_MODE_RUNNING_COLOR           40
#define FX_MODE_RUNNING_RED_BLUE        41
#define FX_MODE_RUNNING_RANDOM          42
#define FX_MODE_LARSON_SCANNER          43
#define FX_MODE_COMET                   44
#define FX_MODE_FIREWORKS               45
#define FX_MODE_FIREWORKS_RANDOM        46
#define FX_MODE_MERRY_CHRISTMAS         47
#define FX_MODE_FIRE_FLICKER            48
#define FX_MODE_FIRE_FLICKER_SOFT       49
#define FX_MODE_FIRE_FLICKER_INTENSE    50
#define FX_MODE_CIRCUS_COMBUSTUS        51
#define FX_MODE_HALLOWEEN               52
#define FX_MODE_BICOLOR_CHASE           53
#define FX_MODE_TRICOLOR_CHASE          54
#define FX_MODE_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


typedef uint16_t (*mode_ptr)(void);

// 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[MAX_NUM_COLORS];
} Segment;

// segment runtime parameters
typedef struct Segment_runtime { // 16 bytes
  unsigned long next_time;
  uint32_t counter_mode_step;
  uint32_t counter_mode_call;
  uint8_t aux_param;   // auxilary param (usually stores a color_wheel index)
  uint8_t aux_param2;  // auxilary param (usually stores bitwise options)
  uint16_t aux_param3; // auxilary param (usually stores a segment index)
} Segment_runtime;


void
//    timer(void),
  WS2812FX_init(uint16_t num_leds, uint8_t type),
  WS2812FX_service(void),
  WS2812FX_start(void),
  WS2812FX_stop(void),
  WS2812FX_pause(void),
  WS2812FX_resume(void),
  WS2812FX_strip_off(void),
  WS2812FX_fade_out(void),
  WS2812FX_fade_out_targetColor(uint32_t),
  WS2812FX_setMode(uint8_t m),
  WS2812FX_setMode_seg(uint8_t seg, uint8_t m),
  WS2812FX_setOptions(uint8_t seg, uint8_t o),
  WS2812FX_setCustomMode(uint16_t (*p)()),
  WS2812FX_setCustomShow(void (*p)()),
  WS2812FX_setSpeed(uint16_t s),
  WS2812FX_setSpeed_seg(uint8_t seg, uint16_t s),
  WS2812FX_increaseSpeed(uint8_t s),
  WS2812FX_decreaseSpeed(uint8_t s),
  WS2812FX_setColor_rgb(uint8_t r, uint8_t g, uint8_t b),
  WS2812FX_setColor_rgbw(uint8_t r, uint8_t g, uint8_t b, uint8_t w),
  WS2812FX_setColor(uint32_t c),
  WS2812FX_setColor_seg(uint8_t seg, uint32_t c),
  WS2812FX_setColors(uint8_t seg, uint32_t* c),
  WS2812FX_setBrightness(uint8_t b),
  WS2812FX_increaseBrightness(uint8_t s),
  WS2812FX_decreaseBrightness(uint8_t s),
  WS2812FX_setLength(uint16_t b),
  WS2812FX_increaseLength(uint16_t s),
  WS2812FX_decreaseLength(uint16_t s),
  WS2812FX_trigger(void),
  WS2812FX_setCycle(void),
  WS2812FX_setNumSegments(uint8_t n),
  WS2812FX_setSegment_colorReverse(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color,          uint16_t speed, bool reverse),
  WS2812FX_setSegment_colorOptions(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color,          uint16_t speed, uint8_t options),
  WS2812FX_setSegment_colorsReverse(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t colors[], uint16_t speed, bool reverse),
  WS2812FX_setSegment_colorsOptions(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t colors[], uint16_t speed, uint8_t options),
  WS2812FX_setIdleSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color,          uint16_t speed, uint8_t options),
  WS2812FX_setIdleSegment_colors(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t colors[], uint16_t speed, uint8_t options),
  WS2812FX_addActiveSegment(uint8_t seg),
  WS2812FX_removeActiveSegment(uint8_t seg),
  WS2812FX_swapActiveSegment(uint8_t oldSeg, uint8_t newSeg),
  WS2812FX_resetSegments(void),
  WS2812FX_resetSegmentRuntimes(void),
  WS2812FX_resetSegmentRuntime(uint8_t),
  WS2812FX_setPixelColor(uint16_t n, uint32_t c),
  WS2812FX_setPixelColor_rgb(uint16_t n, uint8_t r, uint8_t g, uint8_t b),
  WS2812FX_setPixelColor_rgbw(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w),
  WS2812FX_copyPixels(uint16_t d, uint16_t s, uint16_t c),
  WS2812FX_setPixels(uint16_t, uint8_t*),
  WS2812FX_show(void);
      
boolean
  WS2812FX_isRunning(void),
  WS2812FX_isTriggered(void),
  WS2812FX_isFrame(void),
  WS2812FX_isFrame_seg(uint8_t),
  WS2812FX_isCycle(void),
  WS2812FX_isCycle_seg(uint8_t),
  WS2812FX_isActiveSegment(uint8_t seg);

uint8_t
  WS2812FX_random8(void),
  WS2812FX_random8_lim(uint8_t),
  WS2812FX_getMode(void),
  WS2812FX_getMode_seg(uint8_t),
  WS2812FX_getModeCount(void),
//      WS2812FX_setCustomMode(const __FlashStringHelper* name, uint16_t (*p)()),
//      WS2812FX_setCustomMode(uint8_t i, const __FlashStringHelper* name, uint16_t (*p)()),
  WS2812FX_getNumSegments(void),
  WS2812FX_get_random_wheel_index(uint8_t),
  WS2812FX_getOptions(uint8_t),
  WS2812FX_getNumBytesPerPixel(void);

uint16_t
  WS2812FX_random16(void),
  WS2812FX_random16_lim(uint16_t),
  WS2812FX_getSpeed(void),
  WS2812FX_getSpeed_seg(uint8_t),
  WS2812FX_getLength(void),
  WS2812FX_getNumBytes(void);

uint32_t
  WS2812FX_color_wheel(uint8_t),
  WS2812FX_getColor(void),
  WS2812FX_getColor_seg(uint8_t),
  WS2812FX_intensitySum(void);

uint32_t* getColors(uint8_t);
uint32_t* intensitySums(void);
uint8_t*  getActiveSegments(void);

const __FlashStringHelper* getModeName(uint8_t m);

Segment* getSegment(void);

Segment* getSegment_seg(uint8_t);

Segment* getSegments(void);

Segment_runtime* getSegmentRuntime(void);

Segment_runtime* getSegmentRuntime_seg(uint8_t);

Segment_runtime* getSegmentRuntimes(void);

// mode helper functions
uint16_t
  WS2812FX_blink(uint32_t, uint32_t, bool strobe),
  WS2812FX_color_wipe(uint32_t, uint32_t, bool),
  WS2812FX_twinkle(uint32_t, uint32_t),
  WS2812FX_twinkle_fade(uint32_t),
  WS2812FX_sparkle(uint32_t, uint32_t),
  WS2812FX_chase(uint32_t, uint32_t, uint32_t),
  WS2812FX_chase_flash(uint32_t, uint32_t),
  WS2812FX_running(uint32_t, uint32_t),
  WS2812FX_fireworks(uint32_t),
  WS2812FX_fire_flicker(int),
  WS2812FX_tricolor_chase(uint32_t, uint32_t, uint32_t),
  WS2812FX_scan(uint32_t, uint32_t, bool);

uint32_t
  WS2812FX_color_blend(uint32_t, uint32_t, uint8_t);


// builtin modes
uint16_t
  WS2812FX_mode_static(void),
  WS2812FX_mode_blink(void),
  WS2812FX_mode_blink_rainbow(void),
  WS2812FX_mode_strobe(void),
  WS2812FX_mode_strobe_rainbow(void),
  WS2812FX_mode_color_wipe(void),
  WS2812FX_mode_color_wipe_inv(void),
  WS2812FX_mode_color_wipe_rev(void),
  WS2812FX_mode_color_wipe_rev_inv(void),
  WS2812FX_mode_color_wipe_random(void),
  WS2812FX_mode_color_sweep_random(void),
  WS2812FX_mode_random_color(void),
  WS2812FX_mode_single_dynamic(void),
  WS2812FX_mode_multi_dynamic(void),
  WS2812FX_mode_breath(void),
  WS2812FX_mode_fade(void),
  WS2812FX_mode_scan(void),
  WS2812FX_mode_dual_scan(void),
  WS2812FX_mode_theater_chase(void),
  WS2812FX_mode_theater_chase_rainbow(void),
  WS2812FX_mode_rainbow(void),
  WS2812FX_mode_rainbow_cycle(void),
  WS2812FX_mode_running_lights(void),
  WS2812FX_mode_twinkle(void),
  WS2812FX_mode_twinkle_random(void),
  WS2812FX_mode_twinkle_fade(void),
  WS2812FX_mode_twinkle_fade_random(void),
  WS2812FX_mode_sparkle(void),
  WS2812FX_mode_flash_sparkle(void),
  WS2812FX_mode_hyper_sparkle(void),
  WS2812FX_mode_multi_strobe(void),
  WS2812FX_mode_chase_white(void),
  WS2812FX_mode_chase_color(void),
  WS2812FX_mode_chase_random(void),
  WS2812FX_mode_chase_rainbow(void),
  WS2812FX_mode_chase_flash(void),
  WS2812FX_mode_chase_flash_random(void),
  WS2812FX_mode_chase_rainbow_white(void),
  WS2812FX_mode_chase_blackout(void),
  WS2812FX_mode_chase_blackout_rainbow(void),
  WS2812FX_mode_running_color(void),
  WS2812FX_mode_running_red_blue(void),
  WS2812FX_mode_running_random(void),
  WS2812FX_mode_larson_scanner(void),
  WS2812FX_mode_comet(void),
  WS2812FX_mode_fireworks(void),
  WS2812FX_mode_fireworks_random(void),
  WS2812FX_mode_merry_christmas(void),
  WS2812FX_mode_halloween(void),
  WS2812FX_mode_fire_flicker(void),
  WS2812FX_mode_fire_flicker_soft(void),
  WS2812FX_mode_fire_flicker_intense(void),
  WS2812FX_mode_circus_combustus(void),
  WS2812FX_mode_bicolor_chase(void),
  WS2812FX_mode_tricolor_chase(void),
  WS2812FX_mode_custom_0(void),
  WS2812FX_mode_custom_1(void),
  WS2812FX_mode_custom_2(void),
  WS2812FX_mode_custom_3(void),
  WS2812FX_mode_custom_4(void),
  WS2812FX_mode_custom_5(void),
  WS2812FX_mode_custom_6(void),
  WS2812FX_mode_custom_7(void);

#endif /* __BSP_WS2812FX_H */

WS2812FX.c

#include "WS2812FX.h"
#include <stdbool.h>
#include "bsp_delay.h"
#include "Adafruit_NeoPixel.h"
#include <stdlib.h>
#include <string.h>


// create GLOBAL names to allow WS2812FX to compile with sketches and other libs
// that store strings in PROGMEM (get rid of the "section type conflict with __c"
// errors once and for all. Amen.)
//const char name_0[] PROGMEM = "Static";
//const char name_1[] PROGMEM = "Blink";
//const char name_2[] PROGMEM = "Breath";
//const char name_3[] PROGMEM = "Color Wipe";
//const char name_4[] PROGMEM = "Color Wipe Inverse";
//const char name_5[] PROGMEM = "Color Wipe Reverse";
//const char name_6[] PROGMEM = "Color Wipe Reverse Inverse";
//const char name_7[] PROGMEM = "Color Wipe Random";
//const char name_8[] PROGMEM = "Random Color";
//const char name_9[] PROGMEM = "Single Dynamic";
//const char name_10[] PROGMEM = "Multi Dynamic";
//const char name_11[] PROGMEM = "Rainbow";
//const char name_12[] PROGMEM = "Rainbow Cycle";
//const char name_13[] PROGMEM = "Scan";
//const char name_14[] PROGMEM = "Dual Scan";
//const char name_15[] PROGMEM = "Fade";
//const char name_16[] PROGMEM = "Theater Chase";
//const char name_17[] PROGMEM = "Theater Chase Rainbow";
//const char name_18[] PROGMEM = "Running Lights";
//const char name_19[] PROGMEM = "Twinkle";
//const char name_20[] PROGMEM = "Twinkle Random";
//const char name_21[] PROGMEM = "Twinkle Fade";
//const char name_22[] PROGMEM = "Twinkle Fade Random";
//const char name_23[] PROGMEM = "Sparkle";
//const char name_24[] PROGMEM = "Flash Sparkle";
//const char name_25[] PROGMEM = "Hyper Sparkle";
//const char name_26[] PROGMEM = "Strobe";
//const char name_27[] PROGMEM = "Strobe Rainbow";
//const char name_28[] PROGMEM = "Multi Strobe";
//const char name_29[] PROGMEM = "Blink Rainbow";
//const char name_30[] PROGMEM = "Chase White";
//const char name_31[] PROGMEM = "Chase Color";
//const char name_32[] PROGMEM = "Chase Random";
//const char name_33[] PROGMEM = "Chase Rainbow";
//const char name_34[] PROGMEM = "Chase Flash";
//const char name_35[] PROGMEM = "Chase Flash Random";
//const char name_36[] PROGMEM = "Chase Rainbow White";
//const char name_37[] PROGMEM = "Chase Blackout";
//const char name_38[] PROGMEM = "Chase Blackout Rainbow";
//const char name_39[] PROGMEM = "Color Sweep Random";
//const char name_40[] PROGMEM = "Running Color";
//const char name_41[] PROGMEM = "Running Red Blue";
//const char name_42[] PROGMEM = "Running Random";
//const char name_43[] PROGMEM = "Larson Scanner";
//const char name_44[] PROGMEM = "Comet";
//const char name_45[] PROGMEM = "Fireworks";
//const char name_46[] PROGMEM = "Fireworks Random";
//const char name_47[] PROGMEM = "Merry Christmas";
//const char name_48[] PROGMEM = "Fire Flicker";
//const char name_49[] PROGMEM = "Fire Flicker (soft)";
//const char name_50[] PROGMEM = "Fire Flicker (intense)";
//const char name_51[] PROGMEM = "Circus Combustus";
//const char name_52[] PROGMEM = "Halloween";
//const char name_53[] PROGMEM = "Bicolor Chase";
//const char name_54[] PROGMEM = "Tricolor Chase";
//const char name_55[] PROGMEM = "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),
//  FSH(name_1),
//  FSH(name_2),
//  FSH(name_3),
//  FSH(name_4),
//  FSH(name_5),
//  FSH(name_6),
//  FSH(name_7),
//  FSH(name_8),
//  FSH(name_9),
//  FSH(name_10),
//  FSH(name_11),
//  FSH(name_12),
//  FSH(name_13),
//  FSH(name_14),
//  FSH(name_15),
//  FSH(name_16),
//  FSH(name_17),
//  FSH(name_18),
//  FSH(name_19),
//  FSH(name_20),
//  FSH(name_21),
//  FSH(name_22),
//  FSH(name_23),
//  FSH(name_24),
//  FSH(name_25),
//  FSH(name_26),
//  FSH(name_27),
//  FSH(name_28),
//  FSH(name_29),
//  FSH(name_30),
//  FSH(name_31),
//  FSH(name_32),
//  FSH(name_33),
//  FSH(name_34),
//  FSH(name_35),
//  FSH(name_36),
//  FSH(name_37),
//  FSH(name_38),
//  FSH(name_39),
//  FSH(name_40),
//  FSH(name_41),
//  FSH(name_42),
//  FSH(name_43),
//  FSH(name_44),
//  FSH(name_45),
//  FSH(name_46),
//  FSH(name_47),
//  FSH(name_48),
//  FSH(name_49),
//  FSH(name_50),
//  FSH(name_51),
//  FSH(name_52),
//  FSH(name_53),
//  FSH(name_54),
//  FSH(name_55),
//  FSH(name_56),
//  FSH(name_57),
//  FSH(name_58),
//  FSH(name_59),
//  FSH(name_60),
//  FSH(name_61),
//  FSH(name_62)
//};

static uint16_t _rand16seed;
static void (*customShow)(void) = NULL;
static bool _running, _triggered;
static Segment* _segments;                 // array of segments (20 bytes per element)
static Segment_runtime* _segment_runtimes; // array of segment runtimes (16 bytes per element)
static uint8_t* _active_segments;          // array of active segments (1 bytes per element)
static uint8_t _segments_len = 0;          // size of _segments array
static uint8_t _active_segments_len = 0;   // size of _segments_runtime and _active_segments arrays
static uint8_t _num_segments = 0;          // number of configured segments in the _segments array
static Segment* _seg;                      // currently active segment (20 bytes)
static Segment_runtime* _seg_rt;           // currently active segment runtime (16 bytes)
static uint16_t _seg_len;                  // num LEDs in the currently active segment

// define static array of member function pointers.
// function pointers MUST be in the same order as the corresponding name in the _name array.
static mode_ptr _modes[MODE_COUNT] = {
  &WS2812FX_mode_static,
  &WS2812FX_mode_blink,
  &WS2812FX_mode_breath,
  &WS2812FX_mode_color_wipe,
  &WS2812FX_mode_color_wipe_inv,
  &WS2812FX_mode_color_wipe_rev,
  &WS2812FX_mode_color_wipe_rev_inv,
  &WS2812FX_mode_color_wipe_random,
  &WS2812FX_mode_random_color,
  &WS2812FX_mode_single_dynamic,
  &WS2812FX_mode_multi_dynamic,
  &WS2812FX_mode_rainbow,
  &WS2812FX_mode_rainbow_cycle,
  &WS2812FX_mode_scan,
  &WS2812FX_mode_dual_scan,
  &WS2812FX_mode_fade,
  &WS2812FX_mode_theater_chase,
  &WS2812FX_mode_theater_chase_rainbow,
  &WS2812FX_mode_running_lights,
  &WS2812FX_mode_twinkle,
  &WS2812FX_mode_twinkle_random,
  &WS2812FX_mode_twinkle_fade,
  &WS2812FX_mode_twinkle_fade_random,
  &WS2812FX_mode_sparkle,
  &WS2812FX_mode_flash_sparkle,
  &WS2812FX_mode_hyper_sparkle,
  &WS2812FX_mode_strobe,
  &WS2812FX_mode_strobe_rainbow,
  &WS2812FX_mode_multi_strobe,
  &WS2812FX_mode_blink_rainbow,
  &WS2812FX_mode_chase_white,
  &WS2812FX_mode_chase_color,
  &WS2812FX_mode_chase_random,
  &WS2812FX_mode_chase_rainbow,
  &WS2812FX_mode_chase_flash,
  &WS2812FX_mode_chase_flash_random,
  &WS2812FX_mode_chase_rainbow_white,
  &WS2812FX_mode_chase_blackout,
  &WS2812FX_mode_chase_blackout_rainbow,
  &WS2812FX_mode_color_sweep_random,
  &WS2812FX_mode_running_color,
  &WS2812FX_mode_running_red_blue,
  &WS2812FX_mode_running_random,
  &WS2812FX_mode_larson_scanner,
  &WS2812FX_mode_comet,
  &WS2812FX_mode_fireworks,
  &WS2812FX_mode_fireworks_random,
  &WS2812FX_mode_merry_christmas,
  &WS2812FX_mode_fire_flicker,
  &WS2812FX_mode_fire_flicker_soft,
  &WS2812FX_mode_fire_flicker_intense,
  &WS2812FX_mode_circus_combustus,
  &WS2812FX_mode_halloween,
  &WS2812FX_mode_bicolor_chase,
  &WS2812FX_mode_tricolor_chase,
//  &WS2812FX_mode_custom_0,
//  &WS2812FX_mode_custom_1,
//  &WS2812FX_mode_custom_2,
//  &WS2812FX_mode_custom_3,
//  &WS2812FX_mode_custom_4,
//  &WS2812FX_mode_custom_5,
//  &WS2812FX_mode_custom_6,
//  &WS2812FX_mode_custom_7
};


void WS2812FX_init(uint16_t num_leds, uint8_t type) {
  Adafruit_NeoPixel_init(num_leds, type); //该函数要放在起始位置,放在后面可能会导致内存申请失败
  _running = false;
  _segments_len = MAX_NUM_SEGMENTS;
  _active_segments_len = MAX_NUM_ACTIVE_SEGMENTS;
  
  // create all the segment arrays and init to zeros
  _segments = (Segment *)malloc(_segments_len * sizeof(Segment));
  _active_segments = (uint8_t *)malloc(_active_segments_len * sizeof(uint8_t));
  _segment_runtimes = (Segment_runtime *)malloc(_active_segments_len * sizeof(uint8_t));
  
  WS2812FX_resetSegments();
  WS2812FX_setSegment_colorOptions(0, 0, num_leds - 1, DEFAULT_MODE, DEFAULT_COLOR, DEFAULT_SPEED, NO_OPTIONS);
  
  WS2812FX_resetSegmentRuntimes();
  
  Adafruit_NeoPixel_setBrightness(DEFAULT_BRIGHTNESS + 1);
}

// void WS2812FX::timer() {
//     for (int j=0; j < 1000; j++) {
//       uint16_t delay = (this->*_modes[_seg->mode])();
//     }
// }

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 < _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 = (*_modes[_seg->mode])();
          _seg_rt->next_time = now + max(delay, SPEED_MIN);
          _seg_rt->counter_mode_call++;
        }
      }
    }
    if(doShow) {
//      delay(1); // for ESP32 (see https://forums.adafruit.com/viewtopic.php?f=47&t=117327)
      WS2812FX_show();
    }
    _triggered = false;
  }
}

// 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) {
  uint8_t w = (c >> 24) & 0xFF;
  uint8_t r = (c >> 16) & 0xFF;
  uint8_t g = (c >>  8) & 0xFF;
  uint8_t b =  c        & 0xFF;
  WS2812FX_setPixelColor_rgbw(n, r, g, b, w);
}

void WS2812FX_setPixelColor_rgb(uint16_t n, uint8_t r, uint8_t g, uint8_t b) {
  WS2812FX_setPixelColor_rgbw(n, r, g, b, 0);
}

void WS2812FX_setPixelColor_rgbw(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
  if(IS_GAMMA) {
    Adafruit_NeoPixel_setPixelColor_rgbw(n, Adafruit_NeoPixel_gamma8(r), Adafruit_NeoPixel_gamma8(g), Adafruit_NeoPixel_gamma8(b), Adafruit_NeoPixel_gamma8(w));
  } else {
    Adafruit_NeoPixel_setPixelColor_rgbw(n, r, g, b, w);
  }
}

void WS2812FX_copyPixels(uint16_t dest, uint16_t src, uint16_t count) {
  uint8_t *pixels = Adafruit_NeoPixel_getPixels();
  uint8_t bytesPerPixel = WS2812FX_getNumBytesPerPixel(); // 3=RGB, 4=RGBW

  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) {
  customShow == NULL ? Adafruit_NeoPixel_show() : customShow();
}

void WS2812FX_start() {
  WS2812FX_resetSegmentRuntimes();
  _running = true;
}

void WS2812FX_stop() {
  _running = false;
  WS2812FX_strip_off();
}

void WS2812FX_pause() {
  _running = false;
}

void WS2812FX_resume() {
  _running = true;
}

void WS2812FX_trigger() {
  _triggered = true;
}

void WS2812FX_setMode(uint8_t m) {
  WS2812FX_setMode_seg(0, m);
}

void WS2812FX_setMode_seg(uint8_t seg, uint8_t m) {
  WS2812FX_resetSegmentRuntime(seg);
  _segments[seg].mode = constrain(m, 0, MODE_COUNT - 1);
}

void WS2812FX_setOptions(uint8_t seg, uint8_t o) {
  _segments[seg].options = o;
}

void WS2812FX_setSpeed(uint16_t s) {
  WS2812FX_setSpeed_seg(0, s);
}

void WS2812FX_setSpeed_seg(uint8_t seg, uint16_t s) {
  _segments[seg].speed = constrain(s, SPEED_MIN, SPEED_MAX);
}

void WS2812FX_increaseSpeed(uint8_t s) {
  uint16_t newSpeed = constrain(_seg->speed + s, SPEED_MIN, SPEED_MAX);
  WS2812FX_setSpeed(newSpeed);
}

void WS2812FX_decreaseSpeed(uint8_t s) {
  uint16_t newSpeed = constrain(_seg->speed - s, SPEED_MIN, SPEED_MAX);
  WS2812FX_setSpeed(newSpeed);
}

void WS2812FX_setColor_rgb(uint8_t r, uint8_t g, uint8_t b) {
  WS2812FX_setColor(((uint32_t)r << 16) | ((uint32_t)g << 8) | b);
}

void WS2812FX_setColor_rgbw(uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
  WS2812FX_setColor((((uint32_t)w << 24)| ((uint32_t)r << 16) | ((uint32_t)g << 8)| ((uint32_t)b)));
}

void WS2812FX_setColor(uint32_t c) {
  WS2812FX_setColor_seg(0, c);
}

void WS2812FX_setColor_seg(uint8_t seg, uint32_t c) {
  _segments[seg].colors[0] = c;
}

void WS2812FX_setColors(uint8_t seg, uint32_t* c) {
  for(uint8_t i=0; i<MAX_NUM_COLORS; i++) {
    _segments[seg].colors[i] = c[i];
  }
}

void WS2812FX_setBrightness(uint8_t b) {
  b = constrain(b, BRIGHTNESS_MIN, BRIGHTNESS_MAX);
  Adafruit_NeoPixel_setBrightness(b);
  WS2812FX_show();
}

void WS2812FX_increaseBrightness(uint8_t s) {
  s = constrain(Adafruit_NeoPixel_getBrightness() + s, BRIGHTNESS_MIN, BRIGHTNESS_MAX);
  WS2812FX_setBrightness(s);
}

void WS2812FX_decreaseBrightness(uint8_t s) {
  s = constrain(Adafruit_NeoPixel_getBrightness() - s, BRIGHTNESS_MIN, BRIGHTNESS_MAX);
  WS2812FX_setBrightness(s);
}

//void WS2812FX::setLength(uint16_t b) {
//  resetSegmentRuntimes();
//  if (b < 1) b = 1;

//  // Decrease numLEDs to maximum available memory
//  do {
//      Adafruit_NeoPixel::updateLength(b);
//      b--;
//  } while(!Adafruit_NeoPixel::numLEDs && b > 1);

//  _segments[0].start = 0;
//  _segments[0].stop = Adafruit_NeoPixel::numLEDs - 1;
//}

//void WS2812FX_increaseLength(uint16_t s) {
//  uint16_t seglen = _segments[0].stop - _segments[0].start + 1;
//  setLength(seglen + s);
//}

//void WS2812FX_decreaseLength(uint16_t s) {
//  uint16_t seglen = _segments[0].stop - _segments[0].start + 1;
//  fill(BLACK, _segments[0].start, seglen);
//  show();

//  if (s < seglen) setLength(seglen - s);
//}

boolean WS2812FX_isRunning() {
  return _running;
}

boolean WS2812FX_isTriggered() {
  return _triggered;
}

boolean WS2812FX_isFrame() {
  return WS2812FX_isFrame_seg(0);
}

boolean WS2812FX_isFrame_seg(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 WS2812FX_isCycle_seg(0);
}

boolean WS2812FX_isCycle_seg(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) {
  return WS2812FX_getMode_seg(0);
}

uint8_t WS2812FX_getMode_seg(uint8_t seg) {
  return _segments[seg].mode;
}

uint16_t WS2812FX_getSpeed(void) {
  return WS2812FX_getSpeed_seg(0);
}

uint16_t WS2812FX_getSpeed_seg(uint8_t seg) {
  return _segments[seg].speed;
}

uint8_t WS2812FX_getOptions(uint8_t seg) {
  return _segments[seg].options;
}

uint16_t WS2812FX_getLength(void) {
  return Adafruit_NeoPixel_numPixels();
}

uint16_t WS2812FX_getNumBytes(void) {
  return Adafruit_NeoPixel_getNumBytes();
}

uint8_t WS2812FX_getNumBytesPerPixel(void) {
  return Adafruit_NeoPixel_getNumBytesPerPixel();
}

uint8_t WS2812FX_getModeCount(void) {
  return MODE_COUNT;
}

uint8_t WS2812FX_getNumSegments(void) {
  return _num_segments;
}

void WS2812FX_setNumSegments(uint8_t n) {
  _num_segments = n;
}

uint32_t WS2812FX_getColor(void) {
  return WS2812FX_getColor_seg(0);
}

uint32_t WS2812FX_getColor_seg(uint8_t seg) {
  return _segments[seg].colors[0];
}

uint32_t* WS2812FX_getColors(uint8_t seg) {
  return _segments[seg].colors;
}

Segment* WS2812FX_getSegment(void) {
  return _seg;
}

Segment* WS2812FX_getSegment_seg(uint8_t seg) {
  return &_segments[seg];
}

Segment* WS2812FX_getSegments(void) {
  return _segments;
}

Segment_runtime* WS2812FX_getSegmentRuntime(void) {
  return _seg_rt;
}

Segment_runtime* WS2812FX_getSegmentRuntime_seg(uint8_t 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];
}

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];
//  } else {
//    return "";//F("");
//  }
//}

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};
  WS2812FX_setIdleSegment_colors(n, start, stop, mode, colors, speed, options);
}

void WS2812FX_setIdleSegment_colors(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t colors[], uint16_t speed, uint8_t options) {
  WS2812FX_setSegment_colorsOptions(n, start, stop, mode, colors, speed, options);
  if(n < _active_segments_len) WS2812FX_removeActiveSegment(n);;
}

void WS2812FX_setSegment_colorReverse(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};
  WS2812FX_setSegment_colorsOptions(n, start, stop, mode, colors, speed, (uint8_t)(reverse ? REVERSE : NO_OPTIONS));
}

void WS2812FX_setSegment_colorsReverse(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t colors[], uint16_t speed, bool reverse) {
  WS2812FX_setSegment_colorsOptions(n, start, stop, mode, colors, speed, (uint8_t)(reverse ? REVERSE : NO_OPTIONS));
}

void WS2812FX_setSegment_colorOptions(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};
  WS2812FX_setSegment_colorsOptions(n, start, stop, mode, colors, speed, options);
}

void WS2812FX_setSegment_colorsOptions(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t colors[], uint16_t speed, uint8_t options) {
  if(n < _segments_len) {
    if(n + 1 > _num_segments) _num_segments = n + 1;
    _segments[n].start = start;
    _segments[n].stop = stop;
    _segments[n].mode = mode;
    _segments[n].speed = speed;
    _segments[n].options = options;

    WS2812FX_setColors(n, (uint32_t*)colors);

    if(n < _active_segments_len) WS2812FX_addActiveSegment(n);
  }
}

void WS2812FX_addActiveSegment(uint8_t seg) {
  uint8_t* ptr = (uint8_t*)memchr(_active_segments, seg, _active_segments_len);
  if(ptr != NULL) return; // segment already active
  for(uint8_t i=0; i<_active_segments_len; i++) {
    if(_active_segments[i] == INACTIVE_SEGMENT) {
      _active_segments[i] = seg;
      WS2812FX_resetSegmentRuntime(seg);
      break;
    }
  }
}

void WS2812FX_removeActiveSegment(uint8_t seg) {
  for(uint8_t i=0; i<_active_segments_len; i++) {
    if(_active_segments[i] == seg) {
      _active_segments[i] = INACTIVE_SEGMENT;
    }
  }
}

void WS2812FX_swapActiveSegment(uint8_t oldSeg, uint8_t newSeg) {
  uint8_t* ptr = (uint8_t*)memchr(_active_segments, newSeg, _active_segments_len);
  if(ptr != NULL) return; // if newSeg is already active, don't swap
  for(uint8_t i=0; i<_active_segments_len; i++) {
    if(_active_segments[i] == oldSeg) {
      _active_segments[i] = newSeg;

      // reset all runtime parameters EXCEPT next_time,
      // allowing the current animation frame to complete
      Segment_runtime seg_rt = _segment_runtimes[i];
      seg_rt.counter_mode_step = 0;
      seg_rt.counter_mode_call = 0;
      seg_rt.aux_param = 0;
      seg_rt.aux_param2 = 0;
      seg_rt.aux_param3 = 0;
      break;
    }
  }
}

boolean WS2812FX_isActiveSegment(uint8_t seg) {
  uint8_t* ptr = (uint8_t*)memchr(_active_segments, seg, _active_segments_len);
  if(ptr != NULL) return true;
  return false;
}

void WS2812FX_resetSegments() {
  WS2812FX_resetSegmentRuntimes();
  memset(_segments, 0, _segments_len * sizeof(Segment));
  memset(_active_segments, INACTIVE_SEGMENT, _active_segments_len);
  _num_segments = 0;
}

void WS2812FX_resetSegmentRuntimes() {
  memset(_segment_runtimes, 0, _active_segments_len * sizeof(Segment_runtime));
}

void WS2812FX_resetSegmentRuntime(uint8_t seg) {
  uint8_t* ptr = (uint8_t*)memchr(_active_segments, seg, _active_segments_len);
  if(ptr == NULL) return; // segment not active
  memset(&_segment_runtimes[ptr - _active_segments], 0, sizeof(Segment_runtime));
}

/*
 * Turns everything off. Doh.
 */
void WS2812FX_strip_off() {
  Adafruit_NeoPixel_clear();
  WS2812FX_show();
}

/*
 * 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.
 */
uint32_t WS2812FX_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.
 */
uint8_t WS2812FX_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 = WS2812FX_random8();
    x = abs(pos - r);
    y = 255 - x;
    d = min(x, y);
  }

  return r;
}

// fast 8-bit random number generator shamelessly borrowed from FastLED
uint8_t WS2812FX_random8() {
    _rand16seed = (_rand16seed * 2053) + 13849;
    return (uint8_t)((_rand16seed + (_rand16seed >> 8)) & 0xFF);
}

// note random8(lim) generates numbers in the range 0 to (lim -1)
uint8_t WS2812FX_random8_lim(uint8_t lim) {
    uint8_t r = WS2812FX_random8();
    r = ((uint16_t)r * lim) >> 8;
    return r;
}

uint16_t WS2812FX_random16() {
    return (uint16_t)WS2812FX_random8() * 256 + WS2812FX_random8();
}

// note random16(lim) generates numbers in the range 0 to (lim - 1)
uint16_t WS2812FX_random16_lim(uint16_t lim) {
    uint16_t r = WS2812FX_random16();
    r = ((uint32_t)r * lim) >> 16;
    return r;
}

// Return the sum of all LED intensities (can be used for
// rudimentary power calculations)
//uint32_t WS2812FX_intensitySum() {
//  uint8_t *pixels = getPixels();
//  uint32_t sum = 0;
//  for(uint16_t i=0; i <numBytes; i++) {
//    sum+= pixels[i];
//  }
//  return sum;
//}

// Return the sum of each color's intensity. Note, the order of
// intensities in the returned array depends on the type of WS2812
// LEDs you have. NEO_GRB LEDs will return an array with entries
// in a different order then NEO_RGB LEDs.
//uint32_t* WS2812FX_intensitySums() {
//  static uint32_t intensities[] = { 0, 0, 0, 0 };
//  memset(intensities, 0, sizeof(intensities));

//  uint8_t *pixels = getPixels();
//  uint8_t bytesPerPixel = getNumBytesPerPixel(); // 3=RGB, 4=RGBW
//  for(uint16_t i=0; i <numBytes; i += bytesPerPixel) {
//    intensities[0] += pixels[i];
//    intensities[1] += pixels[i + 1];
//    intensities[2] += pixels[i + 2];
//    if(bytesPerPixel == 4) intensities[3] += pixels[i + 3]; // for RGBW LEDs
//  }
//  return intensities;
//}


/* #####################################################
#
#  Mode Functions
#
##################################################### */

/*
 * No blinking. Just plain old static light.
 */
uint16_t WS2812FX_mode_static(void) {
  Adafruit_NeoPixel_fill(_seg->colors[0], _seg->start, _seg_len);
  SET_CYCLE;
  return _seg->speed;
}


/*
 * Blink/strobe function
 * Alternate between color1 and color2
 * if(strobe == true) then create a strobe effect
 */
uint16_t WS2812FX_blink(uint32_t color1, uint32_t color2, bool strobe) {
  if(_seg_rt->counter_mode_call & 1) {
    uint32_t color = (IS_REVERSE) ? color1 : color2; // off
    Adafruit_NeoPixel_fill(color, _seg->start, _seg_len);
    SET_CYCLE;
    return strobe ? _seg->speed - 20 : (_seg->speed / 2);
  } else {
    uint32_t color = (IS_REVERSE) ? color2 : color1; // on
    Adafruit_NeoPixel_fill(color, _seg->start, _seg_len);
    return strobe ? 20 : (_seg->speed / 2);
  }
}


/*
 * Normal blinking. 50% on/off time.
 */
uint16_t WS2812FX_mode_blink(void) {
  return WS2812FX_blink(_seg->colors[0], _seg->colors[1], false);
}


/*
 * Classic Blink effect. Cycling through the rainbow.
 */
uint16_t WS2812FX_mode_blink_rainbow(void) {
  return WS2812FX_blink(WS2812FX_color_wheel(_seg_rt->counter_mode_call & 0xFF), _seg->colors[1], false);
}


/*
 * Classic Strobe effect.
 */
uint16_t WS2812FX_mode_strobe(void) {
  return WS2812FX_blink(_seg->colors[0], _seg->colors[1], true);
}


/*
 * Classic Strobe effect. Cycling through the rainbow.
 */
uint16_t WS2812FX_mode_strobe_rainbow(void) {
  return WS2812FX_blink(WS2812FX_color_wheel(_seg_rt->counter_mode_call & 0xFF), _seg->colors[1], true);
}


/*
 * Color wipe function
 * LEDs are turned on (color1) in sequence, then turned off (color2) in sequence.
 * if (bool rev == true) then LEDs are turned off in reverse order
 */
uint16_t WS2812FX_color_wipe(uint32_t color1, uint32_t color2, bool rev) {
  if(_seg_rt->counter_mode_step < _seg_len) {
    uint32_t led_offset = _seg_rt->counter_mode_step;
    if(IS_REVERSE) {
      WS2812FX_setPixelColor(_seg->stop - led_offset, color1);
    } else {
      WS2812FX_setPixelColor(_seg->start + led_offset, color1);
    }
  } else {
    uint32_t led_offset = _seg_rt->counter_mode_step - _seg_len;
    if((IS_REVERSE && !rev) || (!IS_REVERSE && rev)) {
      WS2812FX_setPixelColor(_seg->stop - led_offset, color2);
    } else {
      WS2812FX_setPixelColor(_seg->start + led_offset, color2);
    }
  }

  _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) % (_seg_len * 2);

  if(_seg_rt->counter_mode_step == 0) SET_CYCLE;

  return (_seg->speed / (_seg_len * 2));
}

/*
 * Lights all LEDs one after another.
 */
uint16_t WS2812FX_mode_color_wipe(void) {
  return WS2812FX_color_wipe(_seg->colors[0], _seg->colors[1], false);
}

uint16_t WS2812FX_mode_color_wipe_inv(void) {
  return WS2812FX_color_wipe(_seg->colors[1], _seg->colors[0], false);
}

uint16_t WS2812FX_mode_color_wipe_rev(void) {
  return WS2812FX_color_wipe(_seg->colors[0], _seg->colors[1], true);
}

uint16_t WS2812FX_mode_color_wipe_rev_inv(void) {
  return WS2812FX_color_wipe(_seg->colors[1], _seg->colors[0], true);
}


/*
 * Turns all LEDs after each other to a random color.
 * Then starts over with another color.
 */
uint16_t WS2812FX_mode_color_wipe_random(void) {
  if(_seg_rt->counter_mode_step % _seg_len == 0) { // aux_param will store our random color wheel index
    _seg_rt->aux_param = WS2812FX_get_random_wheel_index(_seg_rt->aux_param);
  }
  uint32_t color = WS2812FX_color_wheel(_seg_rt->aux_param);
  return WS2812FX_color_wipe(color, color, false) * 2;
}


/*
 * Random color introduced alternating from start and end of strip.
 */
uint16_t WS2812FX_mode_color_sweep_random(void) {
  if(_seg_rt->counter_mode_step % _seg_len == 0) { // aux_param will store our random color wheel index
    _seg_rt->aux_param = WS2812FX_get_random_wheel_index(_seg_rt->aux_param);
  }
  uint32_t color = WS2812FX_color_wheel(_seg_rt->aux_param);
  return WS2812FX_color_wipe(color, color, true) * 2;
}


/*
 * Lights all LEDs in one random color up. Then switches them
 * to the next random color.
 */
uint16_t WS2812FX_mode_random_color(void) {
  _seg_rt->aux_param = WS2812FX_get_random_wheel_index(_seg_rt->aux_param); // aux_param will store our random color wheel index
  uint32_t color = WS2812FX_color_wheel(_seg_rt->aux_param);
  Adafruit_NeoPixel_fill(color, _seg->start, _seg_len);
  SET_CYCLE;
  return _seg->speed;
}


/*
 * Lights every LED in a random color. Changes one random LED after the other
 * to another random color.
 */
uint16_t WS2812FX_mode_single_dynamic(void) {
  if(_seg_rt->counter_mode_call == 0) {
    for(uint16_t i=_seg->start; i <= _seg->stop; i++) {
      WS2812FX_setPixelColor(i, WS2812FX_color_wheel(WS2812FX_random8()));
    }
  }

  WS2812FX_setPixelColor(_seg->start + WS2812FX_random16_lim(_seg_len), WS2812FX_color_wheel(WS2812FX_random8()));
  SET_CYCLE;
  return _seg->speed;
}


/*
 * Lights every LED in a random color. Changes all LED at the same time
 * to new random colors.
 */
uint16_t WS2812FX_mode_multi_dynamic(void) {
  for(uint16_t i=_seg->start; i <= _seg->stop; i++) {
    WS2812FX_setPixelColor(i, WS2812FX_color_wheel(WS2812FX_random8()));
  }
  SET_CYCLE;
  return _seg->speed;
}


/*
 * Does the "standby-breathing" of well known i-Devices. Fixed Speed.
 * Use mode "fade" if you like to have something similar with a different speed.
 */
uint16_t WS2812FX_mode_breath(void) {
  int lum = _seg_rt->counter_mode_step;
  if(lum > 255) lum = 511 - lum; // lum = 15 -> 255 -> 15

  uint16_t delay;
  if(lum == 15) delay = 970; // 970 pause before each breath
  else if(lum <=  25) delay = 38; // 19
  else if(lum <=  50) delay = 36; // 18
  else if(lum <=  75) delay = 28; // 14
  else if(lum <= 100) delay = 20; // 10
  else if(lum <= 125) delay = 14; // 7
  else if(lum <= 150) delay = 11; // 5
  else delay = 10; // 4

  uint32_t color =  WS2812FX_color_blend(_seg->colors[1], _seg->colors[0], lum);
  Adafruit_NeoPixel_fill(color, _seg->start, _seg_len);

  _seg_rt->counter_mode_step += 2;
  if(_seg_rt->counter_mode_step > (512-15)) {
    _seg_rt->counter_mode_step = 15;
    SET_CYCLE;
  }
  return delay;
}


/*
 * Fades the LEDs between two colors
 */
uint16_t WS2812FX_mode_fade(void) {
  int lum = _seg_rt->counter_mode_step;
  if(lum > 255) lum = 511 - lum; // lum = 0 -> 255 -> 0

  uint32_t color = WS2812FX_color_blend(_seg->colors[1], _seg->colors[0], lum);
  Adafruit_NeoPixel_fill(color, _seg->start, _seg_len);

  _seg_rt->counter_mode_step += 4;
  if(_seg_rt->counter_mode_step > 511) {
    _seg_rt->counter_mode_step = 0;
    SET_CYCLE;
  }
  return (_seg->speed / 128);
}


/*
 * scan function - runs a block of pixels back and forth.
 */
uint16_t WS2812FX_scan(uint32_t color1, uint32_t color2, bool dual) {
  int8_t dir = _seg_rt->aux_param ? -1 : 1;
  uint8_t size = 1 << SIZE_OPTION;

  Adafruit_NeoPixel_fill(color2, _seg->start, _seg_len);

  for(uint8_t i = 0; i < size; i++) {
    if(IS_REVERSE || dual) {
      WS2812FX_setPixelColor(_seg->stop - _seg_rt->counter_mode_step - i, color1);
    }
    if(!IS_REVERSE || dual) {
      WS2812FX_setPixelColor(_seg->start + _seg_rt->counter_mode_step + i, color1);
    }
  }

  _seg_rt->counter_mode_step += dir;
  if(_seg_rt->counter_mode_step == 0) {
    _seg_rt->aux_param = 0;
    SET_CYCLE;
  }
  if(_seg_rt->counter_mode_step >= (uint16_t)(_seg_len - size)) _seg_rt->aux_param = 1;

  return (_seg->speed / (_seg_len * 2));
}


/*
 * Runs a block of pixels back and forth.
 */
uint16_t WS2812FX_mode_scan(void) {
  return WS2812FX_scan(_seg->colors[0], _seg->colors[1], false);
}


/*
 * Runs two blocks of pixels back and forth in opposite directions.
 */
uint16_t WS2812FX_mode_dual_scan(void) {
  return WS2812FX_scan(_seg->colors[0], _seg->colors[1], true);
}


/*
 * Cycles all LEDs at once through a rainbow.
 */
uint16_t WS2812FX_mode_rainbow(void) {
  uint32_t color = WS2812FX_color_wheel(_seg_rt->counter_mode_step);
  Adafruit_NeoPixel_fill(color, _seg->start, _seg_len);

  _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) & 0xFF;

  if(_seg_rt->counter_mode_step == 0)  SET_CYCLE;

  return (_seg->speed / 256);
}


/*
 * Cycles a rainbow over the entire string of LEDs.
 */
uint16_t WS2812FX_mode_rainbow_cycle(void) {
  for(uint16_t i=0; i < _seg_len; i++) {
	  uint32_t color = WS2812FX_color_wheel(((i * 256 / _seg_len) + _seg_rt->counter_mode_step) & 0xFF);
    WS2812FX_setPixelColor(_seg->start + i, color);
  }

  _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) & 0xFF;

  if(_seg_rt->counter_mode_step == 0) SET_CYCLE;

  return (_seg->speed / 256);
}


/*
 * Tricolor chase function
 */
uint16_t WS2812FX_tricolor_chase(uint32_t color1, uint32_t color2, uint32_t color3) {
  uint8_t sizeCnt = 1 << SIZE_OPTION;
  uint8_t sizeCnt2 = sizeCnt + sizeCnt;
  uint8_t sizeCnt3 = sizeCnt2 + sizeCnt;
  uint16_t index = _seg_rt->counter_mode_step % sizeCnt3;
  for(uint16_t i=0; i < _seg_len; i++, index++) {
    index = index % sizeCnt3;

    uint32_t color = color3;
    if(index < sizeCnt) color = color1;
    else if(index < sizeCnt2) color = color2;

    if(IS_REVERSE) {
      WS2812FX_setPixelColor(_seg->start + i, color);
    } else {
      WS2812FX_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 WS2812FX_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 WS2812FX_tricolor_chase(RED, WHITE, BLACK);
}


/*
 * Theatre-style crawling lights.
 * Inspired by the Adafruit examples.
 */
uint16_t WS2812FX_mode_theater_chase(void) {
  return WS2812FX_tricolor_chase(_seg->colors[0], _seg->colors[1], _seg->colors[1]);
}


/*
 * Theatre-style crawling lights with rainbow effect.
 * Inspired by the Adafruit examples.
 */
uint16_t WS2812FX_mode_theater_chase_rainbow(void) {
  _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) & 0xFF;
  uint32_t color = WS2812FX_color_wheel(_seg_rt->counter_mode_step);
  return WS2812FX_tricolor_chase(color, _seg->colors[1], _seg->colors[1]);
}


/*
 * Running lights effect with smooth sine transition.
 */
uint16_t WS2812FX_mode_running_lights(void) {
  uint8_t size = 1 << SIZE_OPTION;
  uint8_t sineIncr = max(1, (256 / _seg_len) * size);
  for(uint16_t i=0; i < _seg_len; i++) {
    int lum = (int)Adafruit_NeoPixel_sine8(((i + _seg_rt->counter_mode_step) * sineIncr));
    uint32_t color = WS2812FX_color_blend(_seg->colors[0], _seg->colors[1], lum);
    if(IS_REVERSE) {
      WS2812FX_setPixelColor(_seg->start + i, color);
    } else {
      WS2812FX_setPixelColor(_seg->stop - i,  color);
    }
  }
  _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) % 256;
  if(_seg_rt->counter_mode_step == 0) SET_CYCLE;
  return (_seg->speed / _seg_len);
}


/*
 * twinkle function
 */
uint16_t WS2812FX_twinkle(uint32_t color1, uint32_t color2) {
  if(_seg_rt->counter_mode_step == 0) {
    Adafruit_NeoPixel_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 = WS2812FX_random16_lim(min_leds * 2);//random(min_leds, min_leds * 2);
    SET_CYCLE;
  }

  WS2812FX_setPixelColor(_seg->start + WS2812FX_random16_lim(_seg_len), color1);

  _seg_rt->counter_mode_step--;
  return (_seg->speed / _seg_len);
}

/*
 * Blink several LEDs on, reset, repeat.
 * Inspired by www.tweaking4all.com/hardware/arduino/arduino-led-strip-effects/
 */
uint16_t WS2812FX_mode_twinkle(void) {
  return WS2812FX_twinkle(_seg->colors[0], _seg->colors[1]);
}

/*
 * Blink several LEDs in random colors on, reset, repeat.
 * Inspired by www.tweaking4all.com/hardware/arduino/arduino-led-strip-effects/
 */
uint16_t WS2812FX_mode_twinkle_random(void) {
  return WS2812FX_twinkle(WS2812FX_color_wheel(WS2812FX_random8()), _seg->colors[1]);
}


/*
 * fade out functions
 */
void WS2812FX_fade_out() {
  /*return*/ WS2812FX_fade_out_targetColor(_seg->colors[1]);
}

void WS2812FX_fade_out_targetColor(uint32_t targetColor) {
  static const uint8_t rateMapH[] = {0, 1, 1, 1, 2, 3, 4, 6};
  static const uint8_t rateMapL[] = {0, 2, 3, 8, 8, 8, 8, 8};

  uint8_t rate  = FADE_RATE;
  uint8_t rateH = rateMapH[rate];
  uint8_t rateL = rateMapL[rate];

  uint32_t color = targetColor;
  int w2 = (color >> 24) & 0xff;
  int r2 = (color >> 16) & 0xff;
  int g2 = (color >>  8) & 0xff;
  int b2 =  color        & 0xff;

  for(uint16_t i=_seg->start; i <= _seg->stop; i++) {
    color = Adafruit_NeoPixel_getPixelColor(i); // current color
    if(rate == 0) { // old fade-to-black algorithm
      WS2812FX_setPixelColor(i, (color >> 1) & 0x7F7F7F7F);
    } else { // new fade-to-color algorithm
      int w1 = (color >> 24) & 0xff;
      int r1 = (color >> 16) & 0xff;
      int g1 = (color >>  8) & 0xff;
      int b1 =  color        & 0xff;

      // calculate the color differences between the current and target colors
      int wdelta = w2 - w1;
      int rdelta = r2 - r1;
      int gdelta = g2 - g1;
      int bdelta = b2 - b1;

      // if the current and target colors are almost the same, jump right to the target
      // color, otherwise calculate an intermediate color. (fixes rounding issues)
      wdelta = abs(wdelta) < 3 ? wdelta : (wdelta >> rateH) + (wdelta >> rateL);
      rdelta = abs(rdelta) < 3 ? rdelta : (rdelta >> rateH) + (rdelta >> rateL);
      gdelta = abs(gdelta) < 3 ? gdelta : (gdelta >> rateH) + (gdelta >> rateL);
      bdelta = abs(bdelta) < 3 ? bdelta : (bdelta >> rateH) + (bdelta >> rateL);

      WS2812FX_setPixelColor_rgbw(i, r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta);
    }
  }
}


/*
 * color blend function
 */
uint32_t WS2812FX_color_blend(uint32_t color1, uint32_t color2, uint8_t blend) {
  if(blend == 0)   return color1;
  if(blend == 255) return color2;

  uint8_t w1 = (color1 >> 24) & 0xff;
  uint8_t r1 = (color1 >> 16) & 0xff;
  uint8_t g1 = (color1 >>  8) & 0xff;
  uint8_t b1 =  color1        & 0xff;

  uint8_t w2 = (color2 >> 24) & 0xff;
  uint8_t r2 = (color2 >> 16) & 0xff;
  uint8_t g2 = (color2 >>  8) & 0xff;
  uint8_t b2 =  color2        & 0xff;

  uint32_t w3 = ((w2 * blend) + (w1 * (255U - blend))) / 256U;
  uint32_t r3 = ((r2 * blend) + (r1 * (255U - blend))) / 256U;
  uint32_t g3 = ((g2 * blend) + (g1 * (255U - blend))) / 256U;
  uint32_t b3 = ((b2 * blend) + (b1 * (255U - blend))) / 256U;

  return ((w3 << 24) | (r3 << 16) | (g3 << 8) | (b3));
}


/*
 * twinkle_fade function
 */
uint16_t WS2812FX_twinkle_fade(uint32_t color) {
  WS2812FX_fade_out();

  if(WS2812FX_random8_lim(3) == 0) {
    uint8_t size = 1 << SIZE_OPTION;
    uint16_t index = _seg->start + WS2812FX_random16_lim(_seg_len - size);
    Adafruit_NeoPixel_fill(color, index, size);
    SET_CYCLE;
  }
  return (_seg->speed / 8);
}


/*
 * Blink several LEDs on, fading out.
 */
uint16_t WS2812FX_mode_twinkle_fade(void) {
  return WS2812FX_twinkle_fade(_seg->colors[0]);
}


/*
 * Blink several LEDs in random colors on, fading out.
 */
uint16_t WS2812FX_mode_twinkle_fade_random(void) {
  return WS2812FX_twinkle_fade(WS2812FX_color_wheel(WS2812FX_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) {
    Adafruit_NeoPixel_fill(color1, _seg->start, _seg_len);
  }

  uint8_t size = 1 << SIZE_OPTION;
  Adafruit_NeoPixel_fill(color1, _seg->start + _seg_rt->aux_param3, size);

  _seg_rt->aux_param3 = WS2812FX_random16_lim(_seg_len - size); // aux_param3 stores the random led index
  Adafruit_NeoPixel_fill(color2, _seg->start + _seg_rt->aux_param3, size);

  SET_CYCLE;
  return (_seg->speed / 32);
}


/*
 * Blinks one LED at a time.
 * Inspired by www.tweaking4all.com/hardware/arduino/arduino-led-strip-effects/
 */
uint16_t WS2812FX_mode_sparkle(void) {
  return WS2812FX_sparkle(_seg->colors[1], _seg->colors[0]);
}


/*
 * Lights all LEDs in the color. Flashes white pixels randomly.
 * Inspired by www.tweaking4all.com/hardware/arduino/arduino-led-strip-effects/
 */
uint16_t WS2812FX_mode_flash_sparkle(void) {
  return WS2812FX_sparkle(_seg->colors[0], WHITE);
}


/*
 * Like flash sparkle. With more flash.
 * Inspired by www.tweaking4all.com/hardware/arduino/arduino-led-strip-effects/
 */
uint16_t WS2812FX_mode_hyper_sparkle(void) {
  Adafruit_NeoPixel_fill(_seg->colors[0], _seg->start, _seg_len);

  uint8_t size = 1 << SIZE_OPTION;
  for(uint8_t i=0; i<8; i++) {
    Adafruit_NeoPixel_fill(WHITE, _seg->start + WS2812FX_random16_lim(_seg_len - size), size);
  }

  SET_CYCLE;
  return (_seg->speed / 32);
}


/*
 * Strobe effect with different strobe count and pause, controlled by speed.
 */
uint16_t WS2812FX_mode_multi_strobe(void) {
  Adafruit_NeoPixel_fill(_seg->colors[1], _seg->start, _seg_len);

  uint16_t delay = 200 + ((9 - (_seg->speed % 10)) * 100);
  uint16_t count = 2 * ((_seg->speed / 100) + 1);
  if(_seg_rt->counter_mode_step < count) {
    if((_seg_rt->counter_mode_step & 1) == 0) {
      Adafruit_NeoPixel_fill(_seg->colors[0], _seg->start, _seg_len);
      delay = 20;
    } else {
      delay = 50;
    }
  }

  _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) % (count + 1);
  if(_seg_rt->counter_mode_step == 0) SET_CYCLE;
  return delay;
}


/*
 * color chase function.
 * color1 = background color
 * color2 and color3 = colors of two adjacent leds
 */
uint16_t WS2812FX_chase(uint32_t color1, uint32_t color2, uint32_t color3) {
  uint8_t size = 1 << SIZE_OPTION;
  for(uint8_t i=0; i<size; i++) {
    uint16_t a = (_seg_rt->counter_mode_step + i) % _seg_len;
    uint16_t b = (a + size) % _seg_len;
    uint16_t c = (b + size) % _seg_len;
    if(IS_REVERSE) {
      WS2812FX_setPixelColor(_seg->stop - a, color1);
      WS2812FX_setPixelColor(_seg->stop - b, color2);
      WS2812FX_setPixelColor(_seg->stop - c, color3);
    } else {
      WS2812FX_setPixelColor(_seg->start + a, color1);
      WS2812FX_setPixelColor(_seg->start + b, color2);
      WS2812FX_setPixelColor(_seg->start + c, color3);
    }
  }

  if(_seg_rt->counter_mode_step + (size * 3) == _seg_len) SET_CYCLE;

  _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) % _seg_len;
  return (_seg->speed / _seg_len);
}


/*
 * Bicolor chase mode
 */
uint16_t WS2812FX_mode_bicolor_chase(void) {
  return WS2812FX_chase(_seg->colors[0], _seg->colors[1], _seg->colors[2]);
}


/*
 * White running on _color.
 */
uint16_t WS2812FX_mode_chase_color(void) {
  return WS2812FX_chase(_seg->colors[0], WHITE, WHITE);
}


/*
 * Black running on _color.
 */
uint16_t WS2812FX_mode_chase_blackout(void) {
  return WS2812FX_chase(_seg->colors[0], BLACK, BLACK);
}


/*
 * _color running on white.
 */
uint16_t WS2812FX_mode_chase_white(void) {
  return WS2812FX_chase(WHITE, _seg->colors[0], _seg->colors[0]);
}


/*
 * White running followed by random color.
 */
uint16_t WS2812FX_mode_chase_random(void) {
  if(_seg_rt->counter_mode_step == 0) {
    _seg_rt->aux_param = WS2812FX_get_random_wheel_index(_seg_rt->aux_param);
  }
  return WS2812FX_chase(WS2812FX_color_wheel(_seg_rt->aux_param), WHITE, WHITE);
}


/*
 * Rainbow running on white.
 */
uint16_t WS2812FX_mode_chase_rainbow_white(void) {
  uint16_t n = _seg_rt->counter_mode_step;
  uint16_t m = (_seg_rt->counter_mode_step + 1) % _seg_len;
  uint32_t color2 = WS2812FX_color_wheel(((n * 256 / _seg_len) + (_seg_rt->counter_mode_call & 0xFF)) & 0xFF);
  uint32_t color3 = WS2812FX_color_wheel(((m * 256 / _seg_len) + (_seg_rt->counter_mode_call & 0xFF)) & 0xFF);

  return WS2812FX_chase(WHITE, color2, color3);
}


/*
 * White running on rainbow.
 */
uint16_t WS2812FX_mode_chase_rainbow(void) {
  uint8_t color_sep = 256 / _seg_len;
  uint8_t color_index = _seg_rt->counter_mode_call & 0xFF;
  uint32_t color = WS2812FX_color_wheel(((_seg_rt->counter_mode_step * color_sep) + color_index) & 0xFF);

  return WS2812FX_chase(color, WHITE, WHITE);
}


/*
 * Black running on rainbow.
 */
uint16_t WS2812FX_mode_chase_blackout_rainbow(void) {
  uint8_t color_sep = 256 / _seg_len;
  uint8_t color_index = _seg_rt->counter_mode_call & 0xFF;
  uint32_t color = WS2812FX_color_wheel(((_seg_rt->counter_mode_step * color_sep) + color_index) & 0xFF);

  return WS2812FX_chase(color, BLACK, BLACK);
}

/*
 * running white flashes function.
 * color1 = background color
 * color2 = flash color
 */
uint16_t WS2812FX_chase_flash(uint32_t color1, uint32_t color2) {
  const static uint8_t flash_count = 4;
  uint8_t flash_step = _seg_rt->counter_mode_call % ((flash_count * 2) + 1);

  if(flash_step < (flash_count * 2)) {
    uint32_t color = (flash_step % 2 == 0) ? color2 : color1;
    uint16_t n = _seg_rt->counter_mode_step;
    uint16_t m = (_seg_rt->counter_mode_step + 1) % _seg_len;
    if(IS_REVERSE) {
      WS2812FX_setPixelColor(_seg->stop - n, color);
      WS2812FX_setPixelColor(_seg->stop - m, color);
    } else {
      WS2812FX_setPixelColor(_seg->start + n, color);
      WS2812FX_setPixelColor(_seg->start + m, color);
    }
    return 30;
  } else {
    _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) % _seg_len;
    if(_seg_rt->counter_mode_step == 0) {
      // update aux_param so mode_chase_flash_random() will select the next color
      _seg_rt->aux_param = WS2812FX_get_random_wheel_index(_seg_rt->aux_param);
      SET_CYCLE;
    }
  }
  return (_seg->speed / _seg_len);
}

/*
 * White flashes running on _color.
 */
uint16_t WS2812FX_mode_chase_flash(void) {
  return WS2812FX_chase_flash(_seg->colors[0], WHITE);
}


/*
 * White flashes running, followed by random color.
 */
uint16_t WS2812FX_mode_chase_flash_random(void) {
  return WS2812FX_chase_flash(WS2812FX_color_wheel(_seg_rt->aux_param), WHITE);
}


/*
 * Alternating pixels running function.
 */
uint16_t WS2812FX_running(uint32_t color1, uint32_t color2) {
  uint8_t size = 2 << SIZE_OPTION;
  uint32_t color = (_seg_rt->counter_mode_step & size) ? color1 : color2;

  if(IS_REVERSE) {
    WS2812FX_copyPixels(_seg->start, _seg->start + 1, _seg_len - 1);
    WS2812FX_setPixelColor(_seg->stop, color);
  } else {
    WS2812FX_copyPixels(_seg->start + 1, _seg->start, _seg_len - 1);
    WS2812FX_setPixelColor(_seg->start, color);
  }

  _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) % _seg_len;
  if(_seg_rt->counter_mode_step == 0) SET_CYCLE;
  return (_seg->speed / _seg_len);
}


/*
 * Alternating color/white pixels running.
 */
uint16_t WS2812FX_mode_running_color(void) {
  return WS2812FX_running(_seg->colors[0], _seg->colors[1]);
}


/*
 * Alternating red/blue pixels running.
 */
uint16_t WS2812FX_mode_running_red_blue(void) {
  return WS2812FX_running(RED, BLUE);
}


/*
 * Alternating red/green pixels running.
 */
uint16_t WS2812FX_mode_merry_christmas(void) {
  return WS2812FX_running(RED, GREEN);
}

/*
 * Alternating orange/purple pixels running.
 */
uint16_t WS2812FX_mode_halloween(void) {
  return WS2812FX_running(PURPLE, ORANGE);
}


/*
 * Random colored pixels running.
 */
uint16_t WS2812FX_mode_running_random(void) {
  uint8_t size = 2 << SIZE_OPTION;
  if((_seg_rt->counter_mode_step) % size == 0) {
    _seg_rt->aux_param = WS2812FX_get_random_wheel_index(_seg_rt->aux_param);
  }

  uint32_t color = WS2812FX_color_wheel(_seg_rt->aux_param);

  return WS2812FX_running(color, color);
}


/*
 * K.I.T.T.
 */
uint16_t WS2812FX_mode_larson_scanner(void) {
  WS2812FX_fade_out();

  if(_seg_rt->counter_mode_step < _seg_len) {
    if(IS_REVERSE) {
      WS2812FX_setPixelColor(_seg->stop - _seg_rt->counter_mode_step, _seg->colors[0]);
    } else {
      WS2812FX_setPixelColor(_seg->start + _seg_rt->counter_mode_step, _seg->colors[0]);
    }
  } else {
    uint16_t index = (_seg_len * 2) - _seg_rt->counter_mode_step - 2;
    if(IS_REVERSE) {
      WS2812FX_setPixelColor(_seg->stop - index, _seg->colors[0]);
    } else {
      WS2812FX_setPixelColor(_seg->start + index, _seg->colors[0]);
    }
  }

  _seg_rt->counter_mode_step++;
  if(_seg_rt->counter_mode_step >= (uint16_t)((_seg_len * 2) - 2)) {
    _seg_rt->counter_mode_step = 0;
    SET_CYCLE;
  }

  return (_seg->speed / (_seg_len * 2));
}


/*
 * Firing comets from one end.
 */
uint16_t WS2812FX_mode_comet(void) {
  WS2812FX_fade_out();

  if(IS_REVERSE) {
    WS2812FX_setPixelColor(_seg->stop - _seg_rt->counter_mode_step, _seg->colors[0]);
  } else {
    WS2812FX_setPixelColor(_seg->start + _seg_rt->counter_mode_step, _seg->colors[0]);
  }

  _seg_rt->counter_mode_step = (_seg_rt->counter_mode_step + 1) % _seg_len;
  if(_seg_rt->counter_mode_step == 0) SET_CYCLE;

  return (_seg->speed / _seg_len);
}


/*
 * Fireworks function.
 */
uint16_t WS2812FX_fireworks(uint32_t color) {
  WS2812FX_fade_out();

// for better performance, manipulate the Adafruit_NeoPixels pixels[] array directly
  uint8_t *pixels = Adafruit_NeoPixel_getPixels();
  uint8_t bytesPerPixel = Adafruit_NeoPixel_getNumBytesPerPixel(); // 3=RGB, 4=RGBW
  uint16_t startPixel = _seg->start * bytesPerPixel + bytesPerPixel;
  uint16_t stopPixel = _seg->stop * bytesPerPixel ;
  for(uint16_t i=startPixel; i <stopPixel; i++) {
    uint16_t tmpPixel = (pixels[i - bytesPerPixel] >> 2) +
      pixels[i] +
      (pixels[i + bytesPerPixel] >> 2);
    pixels[i] =  tmpPixel > 255 ? 255 : tmpPixel;
  }

  uint8_t size = 2 << SIZE_OPTION;
  if(!_triggered) {
    for(uint16_t i=0; i<max(1, _seg_len/20); i++) {
      if(WS2812FX_random8_lim(10) == 0) {
        uint16_t index = _seg->start + WS2812FX_random16_lim(_seg_len - size);
        Adafruit_NeoPixel_fill(color, index, size);
        SET_CYCLE;
      }
    }
  } else {
    for(uint16_t i=0; i<max(1, _seg_len/10); i++) {
      uint16_t index = _seg->start + WS2812FX_random16_lim(_seg_len - size);
      Adafruit_NeoPixel_fill(color, index, size);
      SET_CYCLE;
    }
  }

  return (_seg->speed / _seg_len);
}

/*
 * Firework sparks.
 */
uint16_t WS2812FX_mode_fireworks(void) {
  uint32_t color = BLACK;
  do { // randomly choose a non-BLACK color from the colors array
    color = _seg->colors[WS2812FX_random8_lim(MAX_NUM_COLORS)];
  } while (color == BLACK);
  return WS2812FX_fireworks(color);
}

/*
 * Random colored firework sparks.
 */
uint16_t WS2812FX_mode_fireworks_random(void) {
  return WS2812FX_fireworks(WS2812FX_color_wheel(WS2812FX_random8()));
}


/*
 * Fire flicker function
 */
uint16_t WS2812FX_fire_flicker(int rev_intensity) {
  uint8_t w = (_seg->colors[0] >> 24) & 0xFF;
  uint8_t r = (_seg->colors[0] >> 16) & 0xFF;
  uint8_t g = (_seg->colors[0] >>  8) & 0xFF;
  uint8_t b = (_seg->colors[0]        & 0xFF);
  uint8_t lum = max(w, max(r, max(g, b))) / rev_intensity;
  for(uint16_t i=_seg->start; i <= _seg->stop; i++) {
    int flicker = WS2812FX_random8_lim(lum);
    WS2812FX_setPixelColor_rgbw(i, max(r - flicker, 0), max(g - flicker, 0), max(b - flicker, 0), max(w - flicker, 0));
  }

  SET_CYCLE;
  return (_seg->speed / _seg_len);
}

/*
 * Random flickering.
 */
uint16_t WS2812FX_mode_fire_flicker(void) {
  return WS2812FX_fire_flicker(3);
}

/*
* Random flickering, less intensity.
*/
uint16_t WS2812FX_mode_fire_flicker_soft(void) {
  return WS2812FX_fire_flicker(6);
}

/*
* Random flickering, more intensity.
*/
uint16_t WS2812FX_mode_fire_flicker_intense(void) {
  return WS2812FX_fire_flicker(1);
}

注意

  1. 移植该库需要建立在移植好Adafruit_NeoPixel库的基础上进行,该库会用到很多Adafruit_NeoPixel库中的接口。

  2. 移植该库需要提供获取系统运行时间的函数。此处利用的是STM32HAL库提供的HAL_GetTick()函数。

    #define millis HAL_GetTick
  3. 为减小rom内存的占用,这里屏蔽掉了各个模式名称的字符串存储。

  4. 对于twinkle()函数需要用到的random(min,max)函数尚未实现,暂且使用WS2812_random16_lim()函数替代。

 类似资料: