From 2fe7e221ec9e412cc008aa5c03eaf27e35ff62c6 Mon Sep 17 00:00:00 2001 From: Joshua Diamond Date: Sat, 9 May 2020 04:56:16 -0400 Subject: [PATCH] New RGB Lighting effect: Twinkle (#8887) * Add twinkle RGB Lighting effect * 2nd twinkle algo - double-buffering * Further refinement: Per-LED twinkle * Add documentation for Twinkle RBG Lighting mode * Bias twinkle saturation closer to the set value * Fix whitespace --- docs/feature_rgblight.md | 11 +++++-- quantum/rgblight.c | 65 +++++++++++++++++++++++++++++++++++++++- quantum/rgblight.h | 20 ++++++++++++- quantum/rgblight_modes.h | 8 +++++ 4 files changed, 100 insertions(+), 4 deletions(-) diff --git a/docs/feature_rgblight.md b/docs/feature_rgblight.md index de19b74197..045d97775d 100644 --- a/docs/feature_rgblight.md +++ b/docs/feature_rgblight.md @@ -94,6 +94,7 @@ if `RGBLIGHT_EFFECT_xxxx` or `RGBLIGHT_ANIMATIONS` is defined, you also have a n |`RGBLIGHT_MODE_STATIC_GRADIENT`| 0,1,..,9 |Static gradient | |`RGBLIGHT_MODE_RGB_TEST` | *None* |RGB Test | |`RGBLIGHT_MODE_ALTERNATING` | *None* |Alternating | +|`RGBLIGHT_MODE_TWINKLE` | 0,1,2,3,4,5 |Twinkle | Check out [this video](https://youtube.com/watch?v=VKrpPAHlisY) for a demonstration. @@ -103,8 +104,8 @@ Note: For versions older than 0.6.117, The mode numbers were written directly. I Use these defines to add or remove animations from the firmware. When you are running low on flash space, it can be helpful to disable animations you are not using. -|Define |Default |Description | -|------------------------------------|-------------|-------------------------------------------------------------------------------------| +|Define |Default |Description | +|------------------------------------|-------------|-------------------------------------------------------------------------| |`RGBLIGHT_ANIMATIONS` |*Not defined*|Enable all additional animation modes. | |`RGBLIGHT_EFFECT_ALTERNATING` |*Not defined*|Enable alternating animation mode. | |`RGBLIGHT_EFFECT_BREATHING` |*Not defined*|Enable breathing animation mode. | @@ -115,6 +116,7 @@ Use these defines to add or remove animations from the firmware. When you are ru |`RGBLIGHT_EFFECT_RGB_TEST` |*Not defined*|Enable RGB test animation mode. | |`RGBLIGHT_EFFECT_SNAKE` |*Not defined*|Enable snake animation mode. | |`RGBLIGHT_EFFECT_STATIC_GRADIENT` |*Not defined*|Enable static gradient mode. | +|`RGBLIGHT_EFFECT_TWINKLE` |*Not defined*|Enable twinkle animation mode. | ### Effect and Animation Settings @@ -131,6 +133,8 @@ The following options are used to tweak the various animations: |`RGBLIGHT_EFFECT_KNIGHT_OFFSET` |`0` |The number of LEDs to start the "Knight" animation from the start of the strip by | |`RGBLIGHT_RAINBOW_SWIRL_RANGE` |`255` |Range adjustment for the rainbow swirl effect to get different swirls | |`RGBLIGHT_EFFECT_SNAKE_LENGTH` |`4` |The number of LEDs to light up for the "Snake" animation | +|`RGBLIGHT_EFFECT_TWINKLE_LIFE` |`75` |Adjusts how quickly each LED brightens and dims when twinkling (in animation steps) | +|`RGBLIGHT_EFFECT_TWINKLE_PROBABILITY`|`1/127` |Adjusts how likely each LED is to twinkle (on each animation step) | ### Example Usage to Reduce Memory Footprint 1. Remove `RGBLIGHT_ANIMATIONS` from `config.h`. @@ -168,6 +172,9 @@ const uint8_t RGBLED_SNAKE_INTERVALS[] PROGMEM = {100, 50, 20}; // How long (in milliseconds) to wait between animation steps for each of the "Knight" animations const uint8_t RGBLED_KNIGHT_INTERVALS[] PROGMEM = {127, 63, 31}; +// How long (in milliseconds) to wait between animation steps for each of the "Twinkle" animations +const uint8_t RGBLED_TWINKLE_INTERVALS[] PROGMEM = {50, 25, 10}; + // These control which hues are selected for each of the "Static gradient" modes const uint8_t RGBLED_GRADIENT_RANGES[] PROGMEM = {255, 170, 127, 85, 64}; ``` diff --git a/quantum/rgblight.c b/quantum/rgblight.c index 33326fa868..8d01bcf0e6 100644 --- a/quantum/rgblight.c +++ b/quantum/rgblight.c @@ -15,6 +15,7 @@ */ #include #include +#include #ifdef __AVR__ # include # include @@ -561,7 +562,7 @@ void rgblight_sethsv_at(uint8_t hue, uint8_t sat, uint8_t val, uint8_t index) { rgblight_setrgb_at(tmp_led.r, tmp_led.g, tmp_led.b, index); } -#if defined(RGBLIGHT_EFFECT_BREATHING) || defined(RGBLIGHT_EFFECT_RAINBOW_MOOD) || defined(RGBLIGHT_EFFECT_RAINBOW_SWIRL) || defined(RGBLIGHT_EFFECT_SNAKE) || defined(RGBLIGHT_EFFECT_KNIGHT) +#if defined(RGBLIGHT_EFFECT_BREATHING) || defined(RGBLIGHT_EFFECT_RAINBOW_MOOD) || defined(RGBLIGHT_EFFECT_RAINBOW_SWIRL) || defined(RGBLIGHT_EFFECT_SNAKE) || defined(RGBLIGHT_EFFECT_KNIGHT) || defined(RGBLIGHT_EFFECT_TWINKLE) static uint8_t get_interval_time(const uint8_t *default_interval_address, uint8_t velocikey_min, uint8_t velocikey_max) { return @@ -904,6 +905,12 @@ void rgblight_task(void) { interval_time = 500; effect_func = (effect_func_t)rgblight_effect_alternating; } +# endif +# ifdef RGBLIGHT_EFFECT_TWINKLE + else if (rgblight_status.base_mode == RGBLIGHT_MODE_TWINKLE) { + interval_time = get_interval_time(&RGBLED_TWINKLE_INTERVALS[delta % 3], 5, 50); + effect_func = (effect_func_t)rgblight_effect_twinkle; + } # endif if (animation_status.restart) { animation_status.restart = false; @@ -1189,3 +1196,59 @@ void rgblight_effect_alternating(animation_status_t *anim) { anim->pos = (anim->pos + 1) % 2; } #endif + +#ifdef RGBLIGHT_EFFECT_TWINKLE +__attribute__((weak)) const uint8_t RGBLED_TWINKLE_INTERVALS[] PROGMEM = {50, 25, 10}; + +typedef struct PACKED { + HSV hsv; + uint8_t life; + bool up; +} TwinkleState; + +static TwinkleState led_twinkle_state[RGBLED_NUM]; + +void rgblight_effect_twinkle(animation_status_t *anim) { + + bool random_color = anim->delta / 3; + bool restart = anim->pos == 0; + anim->pos = 1; + + for (uint8_t i = 0; i < rgblight_ranges.effect_num_leds; i++) { + TwinkleState *t = &(led_twinkle_state[i]); + HSV *c = &(t->hsv); + if (restart) { + // Restart + t->life = 0; + t->hsv.v = 0; + } else if (t->life) { + // This LED is already on, either brightening or dimming + t->life--; + uint8_t on = t->up ? RGBLIGHT_EFFECT_TWINKLE_LIFE - t->life : t->life; + c->v = (uint16_t) rgblight_config.val * on / RGBLIGHT_EFFECT_TWINKLE_LIFE; + if (t->life == 0 && t->up) { + t->up = false; + t->life = RGBLIGHT_EFFECT_TWINKLE_LIFE; + } + if (!random_color) { + c->h = rgblight_config.hue; + c->s = rgblight_config.sat; + } + } else if (rand() < RAND_MAX * RGBLIGHT_EFFECT_TWINKLE_PROBABILITY) { + // This LED is off, but was randomly selected to start brightening + c->h = random_color ? rand() % 0xFF : rgblight_config.hue; + c->s = random_color ? (rand() % (rgblight_config.sat / 2)) + (rgblight_config.sat / 2) : rgblight_config.sat; + c->v = 0; + t->life = RGBLIGHT_EFFECT_TWINKLE_LIFE; + t->up = true; + } else { + // This LED is off, and was NOT selected to start brightening + } + + LED_TYPE *ledp = led + i + rgblight_ranges.effect_start_pos; + sethsv(c->h, c->s, c->v, ledp); + } + + rgblight_set(); +} +#endif diff --git a/quantum/rgblight.h b/quantum/rgblight.h index e060737283..7de9c3f3de 100644 --- a/quantum/rgblight.h +++ b/quantum/rgblight.h @@ -59,6 +59,12 @@ | 34 | RGBLIGHT_MODE_STATIC_GRADIENT + 9 | | 35 | RGBLIGHT_MODE_RGB_TEST | | 36 | RGBLIGHT_MODE_ALTERNATING | +| 37 | RGBLIGHT_MODE_TWINKLE | +| 38 | RGBLIGHT_MODE_TWINKLE + 1 | +| 39 | RGBLIGHT_MODE_TWINKLE + 2 | +| 40 | RGBLIGHT_MODE_TWINKLE + 3 | +| 41 | RGBLIGHT_MODE_TWINKLE + 4 | +| 42 | RGBLIGHT_MODE_TWINKLE + 5 | |-----------------|-----------------------------------| *****/ @@ -73,6 +79,7 @@ # define RGBLIGHT_EFFECT_STATIC_GRADIENT # define RGBLIGHT_EFFECT_RGB_TEST # define RGBLIGHT_EFFECT_ALTERNATING +# define RGBLIGHT_EFFECT_TWINKLE #endif #ifdef RGBLIGHT_STATIC_PATTERNS @@ -89,7 +96,8 @@ || defined(RGBLIGHT_EFFECT_KNIGHT) \ || defined(RGBLIGHT_EFFECT_CHRISTMAS) \ || defined(RGBLIGHT_EFFECT_RGB_TEST) \ - || defined(RGBLIGHT_EFFECT_ALTERNATING) + || defined(RGBLIGHT_EFFECT_ALTERNATING) \ + || defined(RGBLIGHT_EFFECT_TWINKLE) # define RGBLIGHT_USE_TIMER #endif @@ -141,6 +149,14 @@ enum RGBLIGHT_EFFECT_MODE { # define RGBLIGHT_EFFECT_CHRISTMAS_STEP 2 # endif +# ifndef RGBLIGHT_EFFECT_TWINKLE_LIFE +# define RGBLIGHT_EFFECT_TWINKLE_LIFE 75 +# endif + +# ifndef RGBLIGHT_EFFECT_TWINKLE_PROBABILITY +# define RGBLIGHT_EFFECT_TWINKLE_PROBABILITY 1/127 +# endif + # ifndef RGBLIGHT_HUE_STEP # define RGBLIGHT_HUE_STEP 8 # endif @@ -208,6 +224,7 @@ extern const uint8_t RGBLED_RAINBOW_SWIRL_INTERVALS[3] PROGMEM; extern const uint8_t RGBLED_SNAKE_INTERVALS[3] PROGMEM; extern const uint8_t RGBLED_KNIGHT_INTERVALS[3] PROGMEM; extern const uint16_t RGBLED_RGBTEST_INTERVALS[1] PROGMEM; +extern const uint8_t RGBLED_TWINKLE_INTERVALS[3] PROGMEM; extern bool is_rgblight_initialized; // Should stay in sycn with rgb matrix config as we reuse eeprom storage for both (for now) @@ -398,6 +415,7 @@ void rgblight_effect_knight(animation_status_t *anim); void rgblight_effect_christmas(animation_status_t *anim); void rgblight_effect_rgbtest(animation_status_t *anim); void rgblight_effect_alternating(animation_status_t *anim); +void rgblight_effect_twinkle(animation_status_t *anim); # endif diff --git a/quantum/rgblight_modes.h b/quantum/rgblight_modes.h index 40c9ce4980..7abdb87bc6 100644 --- a/quantum/rgblight_modes.h +++ b/quantum/rgblight_modes.h @@ -53,6 +53,14 @@ _RGBM_SINGLE_DYNAMIC(RGB_TEST) # ifdef RGBLIGHT_EFFECT_ALTERNATING _RGBM_SINGLE_DYNAMIC(ALTERNATING) # endif +# ifdef RGBLIGHT_EFFECT_TWINKLE +_RGBM_MULTI_DYNAMIC(TWINKLE) +_RGBM_TMP_DYNAMIC(twinkle_38, TWINKLE) +_RGBM_TMP_DYNAMIC(twinkle_39, TWINKLE) +_RGBM_TMP_DYNAMIC(twinkle_40, TWINKLE) +_RGBM_TMP_DYNAMIC(twinkle_41, TWINKLE) +_RGBM_TMP_DYNAMIC(TWINKLE_end, TWINKLE) +# endif //// Add a new mode here. // #ifdef RGBLIGHT_EFFECT_ // _RGBM__( )