Parent directory

rgblight.c

50393 bytes
   1/* Copyright 2016-2017 Yang Liu
   2 *
   3 * This program is free software: you can redistribute it and/or modify
   4 * it under the terms of the GNU General Public License as published by
   5 * the Free Software Foundation, either version 2 of the License, or
   6 * (at your option) any later version.
   7 *
   8 * This program is distributed in the hope that it will be useful,
   9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11 * GNU General Public License for more details.
  12 *
  13 * You should have received a copy of the GNU General Public License
  14 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  15 */
  16#include <math.h>
  17#include <string.h>
  18#include <stdlib.h>
  19#include "progmem.h"
  20#include "sync_timer.h"
  21#include "rgblight.h"
  22#include "color.h"
  23#include "debug.h"
  24#include "util.h"
  25#include "led_tables.h"
  26#include <lib/lib8tion/lib8tion.h>
  27#ifdef EEPROM_ENABLE
  28#    include "eeprom.h"
  29#endif
  30
  31#ifdef RGBLIGHT_SPLIT
  32/* for split keyboard */
  33#    define RGBLIGHT_SPLIT_SET_CHANGE_MODE rgblight_status.change_flags |= RGBLIGHT_STATUS_CHANGE_MODE
  34#    define RGBLIGHT_SPLIT_SET_CHANGE_HSVS rgblight_status.change_flags |= RGBLIGHT_STATUS_CHANGE_HSVS
  35#    define RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS rgblight_status.change_flags |= (RGBLIGHT_STATUS_CHANGE_MODE | RGBLIGHT_STATUS_CHANGE_HSVS)
  36#    define RGBLIGHT_SPLIT_SET_CHANGE_LAYERS rgblight_status.change_flags |= RGBLIGHT_STATUS_CHANGE_LAYERS
  37#    define RGBLIGHT_SPLIT_SET_CHANGE_TIMER_ENABLE rgblight_status.change_flags |= RGBLIGHT_STATUS_CHANGE_TIMER
  38#    define RGBLIGHT_SPLIT_ANIMATION_TICK rgblight_status.change_flags |= RGBLIGHT_STATUS_ANIMATION_TICK
  39#else
  40#    define RGBLIGHT_SPLIT_SET_CHANGE_MODE
  41#    define RGBLIGHT_SPLIT_SET_CHANGE_HSVS
  42#    define RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS
  43#    define RGBLIGHT_SPLIT_SET_CHANGE_LAYERS
  44#    define RGBLIGHT_SPLIT_SET_CHANGE_TIMER_ENABLE
  45#    define RGBLIGHT_SPLIT_ANIMATION_TICK
  46#endif
  47
  48#define _RGBM_SINGLE_STATIC(sym) RGBLIGHT_MODE_##sym,
  49#define _RGBM_SINGLE_DYNAMIC(sym)
  50#define _RGBM_MULTI_STATIC(sym) RGBLIGHT_MODE_##sym,
  51#define _RGBM_MULTI_DYNAMIC(sym)
  52#define _RGBM_TMP_STATIC(sym, msym) RGBLIGHT_MODE_##sym,
  53#define _RGBM_TMP_DYNAMIC(sym, msym)
  54static uint8_t static_effect_table[] = {
  55#include "rgblight_modes.h"
  56};
  57
  58#define _RGBM_SINGLE_STATIC(sym) RGBLIGHT_MODE_##sym,
  59#define _RGBM_SINGLE_DYNAMIC(sym) RGBLIGHT_MODE_##sym,
  60#define _RGBM_MULTI_STATIC(sym) RGBLIGHT_MODE_##sym,
  61#define _RGBM_MULTI_DYNAMIC(sym) RGBLIGHT_MODE_##sym,
  62#define _RGBM_TMP_STATIC(sym, msym) RGBLIGHT_MODE_##msym,
  63#define _RGBM_TMP_DYNAMIC(sym, msym) RGBLIGHT_MODE_##msym,
  64static uint8_t mode_base_table[] = {
  65    0, // RGBLIGHT_MODE_zero
  66#include "rgblight_modes.h"
  67};
  68
  69#if !defined(RGBLIGHT_DEFAULT_MODE)
  70#    define RGBLIGHT_DEFAULT_MODE RGBLIGHT_MODE_STATIC_LIGHT
  71#endif
  72
  73#if !defined(RGBLIGHT_DEFAULT_HUE)
  74#    define RGBLIGHT_DEFAULT_HUE 0
  75#endif
  76
  77#if !defined(RGBLIGHT_DEFAULT_SAT)
  78#    define RGBLIGHT_DEFAULT_SAT UINT8_MAX
  79#endif
  80
  81#if !defined(RGBLIGHT_DEFAULT_VAL)
  82#    define RGBLIGHT_DEFAULT_VAL RGBLIGHT_LIMIT_VAL
  83#endif
  84
  85#if !defined(RGBLIGHT_DEFAULT_SPD)
  86#    define RGBLIGHT_DEFAULT_SPD 0
  87#endif
  88
  89#if !defined(RGBLIGHT_DEFAULT_ON)
  90#    define RGBLIGHT_DEFAULT_ON true
  91#endif
  92
  93static inline int is_static_effect(uint8_t mode) {
  94    return memchr(static_effect_table, mode, sizeof(static_effect_table)) != NULL;
  95}
  96
  97#ifdef RGBLIGHT_LED_MAP
  98const uint8_t led_map[] PROGMEM = RGBLIGHT_LED_MAP;
  99#endif
 100
 101#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT
 102__attribute__((weak)) const uint8_t RGBLED_GRADIENT_RANGES[] PROGMEM = {255, 170, 127, 85, 64};
 103#endif
 104
 105rgblight_config_t rgblight_config;
 106rgblight_status_t rgblight_status         = {.timer_enabled = false};
 107bool              is_rgblight_initialized = false;
 108
 109#ifdef RGBLIGHT_SLEEP
 110static bool is_suspended;
 111static bool pre_suspend_enabled;
 112#endif
 113
 114#ifdef RGBLIGHT_USE_TIMER
 115animation_status_t animation_status = {};
 116#endif
 117
 118#ifdef RGBLIGHT_LAYERS
 119rgblight_segment_t const *const *rgblight_layers = NULL;
 120
 121static bool deferred_set_layer_state = false;
 122#endif
 123
 124rgblight_ranges_t rgblight_ranges = {0, RGBLIGHT_LED_COUNT, 0, RGBLIGHT_LED_COUNT, RGBLIGHT_LED_COUNT};
 125
 126void rgblight_set_clipping_range(uint8_t start_pos, uint8_t num_leds) {
 127    rgblight_ranges.clipping_start_pos = start_pos;
 128    rgblight_ranges.clipping_num_leds  = num_leds;
 129}
 130
 131void rgblight_set_effect_range(uint8_t start_pos, uint8_t num_leds) {
 132    if (start_pos >= RGBLIGHT_LED_COUNT) return;
 133    if (start_pos + num_leds > RGBLIGHT_LED_COUNT) return;
 134    rgblight_ranges.effect_start_pos = start_pos;
 135    rgblight_ranges.effect_end_pos   = start_pos + num_leds;
 136    rgblight_ranges.effect_num_leds  = num_leds;
 137}
 138
 139__attribute__((weak)) rgb_t rgblight_hsv_to_rgb(hsv_t hsv) {
 140    return hsv_to_rgb(hsv);
 141}
 142
 143uint8_t rgblight_led_index(uint8_t index) {
 144#if defined(RGBLIGHT_LED_MAP)
 145    return pgm_read_byte(&led_map[index]) - rgblight_ranges.clipping_start_pos;
 146#else
 147    return index - rgblight_ranges.clipping_start_pos;
 148#endif
 149}
 150
 151void setrgb(uint8_t r, uint8_t g, uint8_t b, int index) {
 152    rgblight_driver.set_color(rgblight_led_index(index), r, g, b);
 153}
 154
 155void sethsv_raw(uint8_t hue, uint8_t sat, uint8_t val, int index) {
 156    hsv_t hsv = {hue, sat, val};
 157    rgb_t rgb = rgblight_hsv_to_rgb(hsv);
 158    setrgb(rgb.r, rgb.g, rgb.b, index);
 159}
 160
 161void sethsv(uint8_t hue, uint8_t sat, uint8_t val, int index) {
 162    sethsv_raw(hue, sat, val > RGBLIGHT_LIMIT_VAL ? RGBLIGHT_LIMIT_VAL : val, index);
 163}
 164
 165void rgblight_check_config(void) {
 166    /* Add some out of bound checks for RGB light config */
 167
 168    if (rgblight_config.mode < RGBLIGHT_MODE_STATIC_LIGHT) {
 169        rgblight_config.mode = RGBLIGHT_MODE_STATIC_LIGHT;
 170    } else if (rgblight_config.mode > RGBLIGHT_MODES) {
 171        rgblight_config.mode = RGBLIGHT_MODES;
 172    }
 173
 174    if (rgblight_config.val > RGBLIGHT_LIMIT_VAL) {
 175        rgblight_config.val = RGBLIGHT_LIMIT_VAL;
 176    }
 177}
 178
 179uint64_t eeconfig_read_rgblight(void) {
 180#ifdef EEPROM_ENABLE
 181    return (uint64_t)((eeprom_read_dword(EECONFIG_RGBLIGHT)) | ((uint64_t)eeprom_read_byte(EECONFIG_RGBLIGHT_EXTENDED) << 32));
 182#else
 183    return 0;
 184#endif
 185}
 186
 187void eeconfig_update_rgblight(uint64_t val) {
 188#ifdef EEPROM_ENABLE
 189    rgblight_check_config();
 190    eeprom_update_dword(EECONFIG_RGBLIGHT, val & 0xFFFFFFFF);
 191    eeprom_update_byte(EECONFIG_RGBLIGHT_EXTENDED, (val >> 32) & 0xFF);
 192#endif
 193}
 194
 195void eeconfig_update_rgblight_current(void) {
 196    eeconfig_update_rgblight(rgblight_config.raw);
 197}
 198
 199void eeconfig_update_rgblight_default(void) {
 200    rgblight_config.enable    = RGBLIGHT_DEFAULT_ON;
 201    rgblight_config.velocikey = 0;
 202    rgblight_config.mode      = RGBLIGHT_DEFAULT_MODE;
 203    rgblight_config.hue       = RGBLIGHT_DEFAULT_HUE;
 204    rgblight_config.sat       = RGBLIGHT_DEFAULT_SAT;
 205    rgblight_config.val       = RGBLIGHT_DEFAULT_VAL;
 206    rgblight_config.speed     = RGBLIGHT_DEFAULT_SPD;
 207    RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS;
 208    eeconfig_update_rgblight(rgblight_config.raw);
 209}
 210
 211void eeconfig_debug_rgblight(void) {
 212    dprintf("rgblight_config EEPROM:\n");
 213    dprintf("rgblight_config.enable = %d\n", rgblight_config.enable);
 214    dprintf("rgblight_config.velocikey = %d\n", rgblight_config.velocikey);
 215    dprintf("rghlight_config.mode = %d\n", rgblight_config.mode);
 216    dprintf("rgblight_config.hue = %d\n", rgblight_config.hue);
 217    dprintf("rgblight_config.sat = %d\n", rgblight_config.sat);
 218    dprintf("rgblight_config.val = %d\n", rgblight_config.val);
 219    dprintf("rgblight_config.speed = %d\n", rgblight_config.speed);
 220}
 221
 222void rgblight_init(void) {
 223    /* if already initialized, don't do it again.
 224       If you must do it again, extern this and set to false, first.
 225       This is a dirty, dirty hack until proper hooks can be added for keyboard startup. */
 226    if (is_rgblight_initialized) {
 227        return;
 228    }
 229
 230    dprintf("rgblight_init start!\n");
 231    rgblight_config.raw = eeconfig_read_rgblight();
 232    RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS;
 233    if (!rgblight_config.mode) {
 234        dprintf("rgblight_init rgblight_config.mode = 0. Write default values to EEPROM.\n");
 235        eeconfig_update_rgblight_default();
 236        rgblight_config.raw = eeconfig_read_rgblight();
 237    }
 238    rgblight_check_config();
 239
 240    eeconfig_debug_rgblight(); // display current eeprom values
 241
 242    rgblight_timer_init(); // setup the timer
 243
 244    rgblight_driver.init();
 245
 246    if (rgblight_config.enable) {
 247        rgblight_mode_noeeprom(rgblight_config.mode);
 248    }
 249
 250    is_rgblight_initialized = true;
 251}
 252
 253void rgblight_reload_from_eeprom(void) {
 254    /* Reset back to what we have in eeprom */
 255    rgblight_config.raw = eeconfig_read_rgblight();
 256    RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS;
 257    rgblight_check_config();
 258    eeconfig_debug_rgblight(); // display current eeprom values
 259    if (rgblight_config.enable) {
 260        rgblight_mode_noeeprom(rgblight_config.mode);
 261    }
 262}
 263
 264uint64_t rgblight_read_qword(void) {
 265    return rgblight_config.raw;
 266}
 267
 268void rgblight_update_qword(uint64_t qword) {
 269    RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS;
 270    rgblight_config.raw = qword;
 271    if (rgblight_config.enable)
 272        rgblight_mode_noeeprom(rgblight_config.mode);
 273    else {
 274        rgblight_timer_disable();
 275        rgblight_set();
 276    }
 277}
 278
 279void rgblight_increase(void) {
 280    uint8_t mode = 0;
 281    if (rgblight_config.mode < RGBLIGHT_MODES) {
 282        mode = rgblight_config.mode + 1;
 283    }
 284    rgblight_mode(mode);
 285}
 286void rgblight_decrease(void) {
 287    uint8_t mode = 0;
 288    // Mode will never be < 1. If it ever is, eeprom needs to be initialized.
 289    if (rgblight_config.mode > RGBLIGHT_MODE_STATIC_LIGHT) {
 290        mode = rgblight_config.mode - 1;
 291    }
 292    rgblight_mode(mode);
 293}
 294void rgblight_step_helper(bool write_to_eeprom) {
 295    uint8_t mode = 0;
 296    mode         = rgblight_config.mode + 1;
 297    if (mode > RGBLIGHT_MODES) {
 298        mode = 1;
 299    }
 300    rgblight_mode_eeprom_helper(mode, write_to_eeprom);
 301}
 302void rgblight_step_noeeprom(void) {
 303    rgblight_step_helper(false);
 304}
 305void rgblight_step(void) {
 306    rgblight_step_helper(true);
 307}
 308void rgblight_step_reverse_helper(bool write_to_eeprom) {
 309    uint8_t mode = 0;
 310    mode         = rgblight_config.mode - 1;
 311    if (mode < 1) {
 312        mode = RGBLIGHT_MODES;
 313    }
 314    rgblight_mode_eeprom_helper(mode, write_to_eeprom);
 315}
 316void rgblight_step_reverse_noeeprom(void) {
 317    rgblight_step_reverse_helper(false);
 318}
 319void rgblight_step_reverse(void) {
 320    rgblight_step_reverse_helper(true);
 321}
 322
 323uint8_t rgblight_get_mode(void) {
 324    if (!rgblight_config.enable) {
 325        return false;
 326    }
 327
 328    return rgblight_config.mode;
 329}
 330
 331void rgblight_mode_eeprom_helper(uint8_t mode, bool write_to_eeprom) {
 332    if (!rgblight_config.enable) {
 333        return;
 334    }
 335    if (mode < RGBLIGHT_MODE_STATIC_LIGHT) {
 336        rgblight_config.mode = RGBLIGHT_MODE_STATIC_LIGHT;
 337    } else if (mode > RGBLIGHT_MODES) {
 338        rgblight_config.mode = RGBLIGHT_MODES;
 339    } else {
 340        rgblight_config.mode = mode;
 341    }
 342    RGBLIGHT_SPLIT_SET_CHANGE_MODE;
 343    if (write_to_eeprom) {
 344        eeconfig_update_rgblight(rgblight_config.raw);
 345        dprintf("rgblight mode [EEPROM]: %u\n", rgblight_config.mode);
 346    } else {
 347        dprintf("rgblight mode [NOEEPROM]: %u\n", rgblight_config.mode);
 348    }
 349    if (is_static_effect(rgblight_config.mode)) {
 350        rgblight_timer_disable();
 351    } else {
 352        rgblight_timer_enable();
 353    }
 354#ifdef RGBLIGHT_USE_TIMER
 355    animation_status.restart = true;
 356#endif
 357    rgblight_sethsv_noeeprom(rgblight_config.hue, rgblight_config.sat, rgblight_config.val);
 358}
 359
 360void rgblight_mode(uint8_t mode) {
 361    rgblight_mode_eeprom_helper(mode, true);
 362}
 363
 364void rgblight_mode_noeeprom(uint8_t mode) {
 365    rgblight_mode_eeprom_helper(mode, false);
 366}
 367
 368void rgblight_toggle(void) {
 369    dprintf("rgblight toggle [EEPROM]: rgblight_config.enable = %u\n", !rgblight_config.enable);
 370    if (rgblight_config.enable) {
 371        rgblight_disable();
 372    } else {
 373        rgblight_enable();
 374    }
 375}
 376
 377void rgblight_toggle_noeeprom(void) {
 378    dprintf("rgblight toggle [NOEEPROM]: rgblight_config.enable = %u\n", !rgblight_config.enable);
 379    if (rgblight_config.enable) {
 380        rgblight_disable_noeeprom();
 381    } else {
 382        rgblight_enable_noeeprom();
 383    }
 384}
 385
 386void rgblight_enable(void) {
 387    rgblight_config.enable = 1;
 388    // No need to update EEPROM here. rgblight_mode() will do that, actually
 389    // eeconfig_update_rgblight(rgblight_config.raw);
 390    dprintf("rgblight enable [EEPROM]: rgblight_config.enable = %u\n", rgblight_config.enable);
 391    rgblight_mode(rgblight_config.mode);
 392}
 393
 394void rgblight_enable_noeeprom(void) {
 395    rgblight_config.enable = 1;
 396    dprintf("rgblight enable [NOEEPROM]: rgblight_config.enable = %u\n", rgblight_config.enable);
 397    rgblight_mode_noeeprom(rgblight_config.mode);
 398}
 399
 400void rgblight_disable(void) {
 401    rgblight_config.enable = 0;
 402    eeconfig_update_rgblight(rgblight_config.raw);
 403    dprintf("rgblight disable [EEPROM]: rgblight_config.enable = %u\n", rgblight_config.enable);
 404    rgblight_timer_disable();
 405    RGBLIGHT_SPLIT_SET_CHANGE_MODE;
 406    rgblight_set();
 407}
 408
 409void rgblight_disable_noeeprom(void) {
 410    rgblight_config.enable = 0;
 411    dprintf("rgblight disable [NOEEPROM]: rgblight_config.enable = %u\n", rgblight_config.enable);
 412    rgblight_timer_disable();
 413    RGBLIGHT_SPLIT_SET_CHANGE_MODE;
 414    rgblight_set();
 415}
 416
 417void rgblight_enabled_noeeprom(bool state) {
 418    state ? rgblight_enable_noeeprom() : rgblight_disable_noeeprom();
 419}
 420
 421bool rgblight_is_enabled(void) {
 422    return rgblight_config.enable;
 423}
 424
 425void rgblight_increase_hue_helper(bool write_to_eeprom) {
 426    uint8_t hue = rgblight_config.hue + RGBLIGHT_HUE_STEP;
 427    rgblight_sethsv_eeprom_helper(hue, rgblight_config.sat, rgblight_config.val, write_to_eeprom);
 428}
 429void rgblight_increase_hue_noeeprom(void) {
 430    rgblight_increase_hue_helper(false);
 431}
 432void rgblight_increase_hue(void) {
 433    rgblight_increase_hue_helper(true);
 434}
 435void rgblight_decrease_hue_helper(bool write_to_eeprom) {
 436    uint8_t hue = rgblight_config.hue - RGBLIGHT_HUE_STEP;
 437    rgblight_sethsv_eeprom_helper(hue, rgblight_config.sat, rgblight_config.val, write_to_eeprom);
 438}
 439void rgblight_decrease_hue_noeeprom(void) {
 440    rgblight_decrease_hue_helper(false);
 441}
 442void rgblight_decrease_hue(void) {
 443    rgblight_decrease_hue_helper(true);
 444}
 445void rgblight_increase_sat_helper(bool write_to_eeprom) {
 446    uint8_t sat = qadd8(rgblight_config.sat, RGBLIGHT_SAT_STEP);
 447    rgblight_sethsv_eeprom_helper(rgblight_config.hue, sat, rgblight_config.val, write_to_eeprom);
 448}
 449void rgblight_increase_sat_noeeprom(void) {
 450    rgblight_increase_sat_helper(false);
 451}
 452void rgblight_increase_sat(void) {
 453    rgblight_increase_sat_helper(true);
 454}
 455void rgblight_decrease_sat_helper(bool write_to_eeprom) {
 456    uint8_t sat = qsub8(rgblight_config.sat, RGBLIGHT_SAT_STEP);
 457    rgblight_sethsv_eeprom_helper(rgblight_config.hue, sat, rgblight_config.val, write_to_eeprom);
 458}
 459void rgblight_decrease_sat_noeeprom(void) {
 460    rgblight_decrease_sat_helper(false);
 461}
 462void rgblight_decrease_sat(void) {
 463    rgblight_decrease_sat_helper(true);
 464}
 465void rgblight_increase_val_helper(bool write_to_eeprom) {
 466    uint8_t val = qadd8(rgblight_config.val, RGBLIGHT_VAL_STEP);
 467    rgblight_sethsv_eeprom_helper(rgblight_config.hue, rgblight_config.sat, val, write_to_eeprom);
 468}
 469void rgblight_increase_val_noeeprom(void) {
 470    rgblight_increase_val_helper(false);
 471}
 472void rgblight_increase_val(void) {
 473    rgblight_increase_val_helper(true);
 474}
 475void rgblight_decrease_val_helper(bool write_to_eeprom) {
 476    uint8_t val = qsub8(rgblight_config.val, RGBLIGHT_VAL_STEP);
 477    rgblight_sethsv_eeprom_helper(rgblight_config.hue, rgblight_config.sat, val, write_to_eeprom);
 478}
 479void rgblight_decrease_val_noeeprom(void) {
 480    rgblight_decrease_val_helper(false);
 481}
 482void rgblight_decrease_val(void) {
 483    rgblight_decrease_val_helper(true);
 484}
 485
 486void rgblight_increase_speed_helper(bool write_to_eeprom) {
 487    if (rgblight_config.speed < 3) rgblight_config.speed++;
 488    // RGBLIGHT_SPLIT_SET_CHANGE_HSVS; // NEED?
 489    if (write_to_eeprom) {
 490        eeconfig_update_rgblight(rgblight_config.raw);
 491    }
 492}
 493void rgblight_increase_speed(void) {
 494    rgblight_increase_speed_helper(true);
 495}
 496void rgblight_increase_speed_noeeprom(void) {
 497    rgblight_increase_speed_helper(false);
 498}
 499
 500void rgblight_decrease_speed_helper(bool write_to_eeprom) {
 501    if (rgblight_config.speed > 0) rgblight_config.speed--;
 502    // RGBLIGHT_SPLIT_SET_CHANGE_HSVS; // NEED??
 503    if (write_to_eeprom) {
 504        eeconfig_update_rgblight(rgblight_config.raw);
 505    }
 506}
 507void rgblight_decrease_speed(void) {
 508    rgblight_decrease_speed_helper(true);
 509}
 510void rgblight_decrease_speed_noeeprom(void) {
 511    rgblight_decrease_speed_helper(false);
 512}
 513
 514void rgblight_sethsv_noeeprom_old(uint8_t hue, uint8_t sat, uint8_t val) {
 515    if (rgblight_config.enable) {
 516        rgb_t rgb = hsv_to_rgb((hsv_t){hue, sat, val > RGBLIGHT_LIMIT_VAL ? RGBLIGHT_LIMIT_VAL : val});
 517        rgblight_setrgb(rgb.r, rgb.g, rgb.b);
 518    }
 519}
 520
 521void rgblight_sethsv_eeprom_helper(uint8_t hue, uint8_t sat, uint8_t val, bool write_to_eeprom) {
 522    if (rgblight_config.enable) {
 523#ifdef RGBLIGHT_SPLIT
 524        if (rgblight_config.hue != hue || rgblight_config.sat != sat || rgblight_config.val != val) {
 525            RGBLIGHT_SPLIT_SET_CHANGE_HSVS;
 526        }
 527#endif
 528        rgblight_status.base_mode = mode_base_table[rgblight_config.mode];
 529        if (rgblight_config.mode == RGBLIGHT_MODE_STATIC_LIGHT) {
 530            // same static color
 531#ifdef RGBLIGHT_LAYERS_RETAIN_VAL
 532            // needed for rgblight_layers_write() to get the new val, since it reads rgblight_config.val
 533            rgblight_config.val = val;
 534#endif
 535            rgb_t rgb = hsv_to_rgb((hsv_t){hue, sat, val > RGBLIGHT_LIMIT_VAL ? RGBLIGHT_LIMIT_VAL : val});
 536            rgblight_setrgb(rgb.r, rgb.g, rgb.b);
 537        } else {
 538            // all LEDs in same color
 539            if (1 == 0) { // dummy
 540            }
 541#ifdef RGBLIGHT_EFFECT_BREATHING
 542            else if (rgblight_status.base_mode == RGBLIGHT_MODE_BREATHING) {
 543                // breathing mode, ignore the change of val, use in memory value instead
 544                val = rgblight_config.val;
 545            }
 546#endif
 547#ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD
 548            else if (rgblight_status.base_mode == RGBLIGHT_MODE_RAINBOW_MOOD) {
 549                // rainbow mood, ignore the change of hue
 550                hue = rgblight_config.hue;
 551            }
 552#endif
 553#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL
 554            else if (rgblight_status.base_mode == RGBLIGHT_MODE_RAINBOW_SWIRL) {
 555                // rainbow swirl, ignore the change of hue
 556                hue = rgblight_config.hue;
 557            }
 558#endif
 559#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT
 560            else if (rgblight_status.base_mode == RGBLIGHT_MODE_STATIC_GRADIENT) {
 561                // static gradient
 562                uint8_t delta     = rgblight_config.mode - rgblight_status.base_mode;
 563                bool    direction = (delta % 2) == 0;
 564
 565                uint8_t range = pgm_read_byte(&RGBLED_GRADIENT_RANGES[delta / 2]);
 566                for (uint8_t i = 0; i < rgblight_ranges.effect_num_leds; i++) {
 567                    uint8_t _hue = ((uint16_t)i * (uint16_t)range) / rgblight_ranges.effect_num_leds;
 568                    if (direction) {
 569                        _hue = hue + _hue;
 570                    } else {
 571                        _hue = hue - _hue;
 572                    }
 573                    dprintf("rgblight rainbow set hsv: %d,%d,%d,%u\n", i, _hue, direction, range);
 574                    sethsv(_hue, sat, val, i + rgblight_ranges.effect_start_pos);
 575                }
 576#    ifdef RGBLIGHT_LAYERS_RETAIN_VAL
 577                // needed for rgblight_layers_write() to get the new val, since it reads rgblight_config.val
 578                rgblight_config.val = val;
 579#    endif
 580                rgblight_set();
 581            }
 582#endif
 583        }
 584        rgblight_config.hue = hue;
 585        rgblight_config.sat = sat;
 586        rgblight_config.val = val;
 587        if (write_to_eeprom) {
 588            eeconfig_update_rgblight(rgblight_config.raw);
 589            dprintf("rgblight set hsv [EEPROM]: %u,%u,%u\n", rgblight_config.hue, rgblight_config.sat, rgblight_config.val);
 590        } else {
 591            dprintf("rgblight set hsv [NOEEPROM]: %u,%u,%u\n", rgblight_config.hue, rgblight_config.sat, rgblight_config.val);
 592        }
 593    }
 594}
 595
 596void rgblight_sethsv(uint8_t hue, uint8_t sat, uint8_t val) {
 597    rgblight_sethsv_eeprom_helper(hue, sat, val, true);
 598}
 599
 600void rgblight_sethsv_noeeprom(uint8_t hue, uint8_t sat, uint8_t val) {
 601    rgblight_sethsv_eeprom_helper(hue, sat, val, false);
 602}
 603
 604uint8_t rgblight_get_speed(void) {
 605    return rgblight_config.speed;
 606}
 607
 608void rgblight_set_speed_eeprom_helper(uint8_t speed, bool write_to_eeprom) {
 609    rgblight_config.speed = speed;
 610    if (write_to_eeprom) {
 611        eeconfig_update_rgblight(rgblight_config.raw);
 612        dprintf("rgblight set speed [EEPROM]: %u\n", rgblight_config.speed);
 613    } else {
 614        dprintf("rgblight set speed [NOEEPROM]: %u\n", rgblight_config.speed);
 615    }
 616}
 617
 618void rgblight_set_speed(uint8_t speed) {
 619    rgblight_set_speed_eeprom_helper(speed, true);
 620}
 621
 622void rgblight_set_speed_noeeprom(uint8_t speed) {
 623    rgblight_set_speed_eeprom_helper(speed, false);
 624}
 625
 626uint8_t rgblight_get_hue(void) {
 627    return rgblight_config.hue;
 628}
 629
 630uint8_t rgblight_get_sat(void) {
 631    return rgblight_config.sat;
 632}
 633
 634uint8_t rgblight_get_val(void) {
 635    return rgblight_config.val;
 636}
 637
 638hsv_t rgblight_get_hsv(void) {
 639    return (hsv_t){rgblight_config.hue, rgblight_config.sat, rgblight_config.val};
 640}
 641
 642void rgblight_setrgb(uint8_t r, uint8_t g, uint8_t b) {
 643    if (!rgblight_config.enable) {
 644        return;
 645    }
 646
 647    for (uint8_t i = rgblight_ranges.effect_start_pos; i < rgblight_ranges.effect_end_pos; i++) {
 648        rgblight_driver.set_color(rgblight_led_index(i), r, g, b);
 649    }
 650    rgblight_set();
 651}
 652
 653void rgblight_setrgb_at(uint8_t r, uint8_t g, uint8_t b, uint8_t index) {
 654    if (!rgblight_config.enable || index >= RGBLIGHT_LED_COUNT) {
 655        return;
 656    }
 657
 658    rgblight_driver.set_color(rgblight_led_index(index), r, g, b);
 659    rgblight_set();
 660}
 661
 662void rgblight_sethsv_at(uint8_t hue, uint8_t sat, uint8_t val, uint8_t index) {
 663    if (!rgblight_config.enable) {
 664        return;
 665    }
 666
 667    rgb_t rgb = hsv_to_rgb((hsv_t){hue, sat, val > RGBLIGHT_LIMIT_VAL ? RGBLIGHT_LIMIT_VAL : val});
 668    rgblight_setrgb_at(rgb.r, rgb.g, rgb.b, index);
 669}
 670
 671#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)
 672
 673static uint8_t get_interval_time(const uint8_t *default_interval_address, uint8_t velocikey_min, uint8_t velocikey_max) {
 674    return
 675#    ifdef VELOCIKEY_ENABLE
 676        rgblight_velocikey_enabled() ? rgblight_velocikey_match_speed(velocikey_min, velocikey_max) :
 677#    endif
 678                                     pgm_read_byte(default_interval_address);
 679}
 680
 681#endif
 682
 683void rgblight_setrgb_range(uint8_t r, uint8_t g, uint8_t b, uint8_t start, uint8_t end) {
 684    if (!rgblight_config.enable || start < 0 || start >= end || end > RGBLIGHT_LED_COUNT) {
 685        return;
 686    }
 687
 688    for (uint8_t i = start; i < end; i++) {
 689        rgblight_driver.set_color(rgblight_led_index(i), r, g, b);
 690    }
 691    rgblight_set();
 692}
 693
 694void rgblight_sethsv_range(uint8_t hue, uint8_t sat, uint8_t val, uint8_t start, uint8_t end) {
 695    if (!rgblight_config.enable) {
 696        return;
 697    }
 698
 699    rgb_t rgb = hsv_to_rgb((hsv_t){hue, sat, val > RGBLIGHT_LIMIT_VAL ? RGBLIGHT_LIMIT_VAL : val});
 700    rgblight_setrgb_range(rgb.r, rgb.g, rgb.b, start, end);
 701}
 702
 703#ifndef RGBLIGHT_SPLIT
 704void rgblight_setrgb_master(uint8_t r, uint8_t g, uint8_t b) {
 705    rgblight_setrgb_range(r, g, b, 0, (uint8_t)RGBLIGHT_LED_COUNT / 2);
 706}
 707
 708void rgblight_setrgb_slave(uint8_t r, uint8_t g, uint8_t b) {
 709    rgblight_setrgb_range(r, g, b, (uint8_t)RGBLIGHT_LED_COUNT / 2, (uint8_t)RGBLIGHT_LED_COUNT);
 710}
 711
 712void rgblight_sethsv_master(uint8_t hue, uint8_t sat, uint8_t val) {
 713    rgblight_sethsv_range(hue, sat, val, 0, (uint8_t)RGBLIGHT_LED_COUNT / 2);
 714}
 715
 716void rgblight_sethsv_slave(uint8_t hue, uint8_t sat, uint8_t val) {
 717    rgblight_sethsv_range(hue, sat, val, (uint8_t)RGBLIGHT_LED_COUNT / 2, (uint8_t)RGBLIGHT_LED_COUNT);
 718}
 719#endif // ifndef RGBLIGHT_SPLIT
 720
 721#ifdef RGBLIGHT_LAYERS
 722void rgblight_set_layer_state(uint8_t layer, bool enabled) {
 723    rgblight_layer_mask_t mask = (rgblight_layer_mask_t)1 << layer;
 724    if (enabled) {
 725        rgblight_status.enabled_layer_mask |= mask;
 726    } else {
 727        rgblight_status.enabled_layer_mask &= ~mask;
 728    }
 729    RGBLIGHT_SPLIT_SET_CHANGE_LAYERS;
 730
 731    // Calling rgblight_set() here (directly or indirectly) could
 732    // potentially cause timing issues when there are multiple
 733    // successive calls to rgblight_set_layer_state(). Instead,
 734    // set a flag and do it the next time rgblight_task() runs.
 735
 736    deferred_set_layer_state = true;
 737}
 738
 739bool rgblight_get_layer_state(uint8_t layer) {
 740    rgblight_layer_mask_t mask = (rgblight_layer_mask_t)1 << layer;
 741    return (rgblight_status.enabled_layer_mask & mask) != 0;
 742}
 743
 744// Write any enabled LED layers into the buffer
 745static void rgblight_layers_write(void) {
 746#    ifdef RGBLIGHT_LAYERS_RETAIN_VAL
 747    uint8_t current_val = rgblight_get_val();
 748#    endif
 749    uint8_t i = 0;
 750    // For each layer
 751    for (const rgblight_segment_t *const *layer_ptr = rgblight_layers; i < RGBLIGHT_MAX_LAYERS; layer_ptr++, i++) {
 752        if (!rgblight_get_layer_state(i)) {
 753            continue; // Layer is disabled
 754        }
 755        const rgblight_segment_t *segment_ptr = pgm_read_ptr(layer_ptr);
 756        if (segment_ptr == NULL) {
 757            break; // No more layers
 758        }
 759        // For each segment
 760        while (1) {
 761            rgblight_segment_t segment;
 762            memcpy_P(&segment, segment_ptr, sizeof(rgblight_segment_t));
 763            if (segment.index == RGBLIGHT_END_SEGMENT_INDEX) {
 764                break; // No more segments
 765            }
 766            // Write segment.count LEDs
 767            int limit = MIN(segment.index + segment.count, RGBLIGHT_LED_COUNT);
 768            for (int i = segment.index; i < limit; i++) {
 769#    ifdef RGBLIGHT_LAYERS_RETAIN_VAL
 770                sethsv(segment.hue, segment.sat, current_val, i);
 771#    else
 772                sethsv(segment.hue, segment.sat, segment.val, i);
 773#    endif
 774            }
 775            segment_ptr++;
 776        }
 777    }
 778}
 779
 780#    ifdef RGBLIGHT_LAYER_BLINK
 781rgblight_layer_mask_t _blinking_layer_mask = 0;
 782static uint16_t       _repeat_timer;
 783static uint8_t        _times_remaining;
 784static uint16_t       _dur;
 785
 786void rgblight_blink_layer(uint8_t layer, uint16_t duration_ms) {
 787    rgblight_blink_layer_repeat(layer, duration_ms, 1);
 788}
 789
 790void rgblight_blink_layer_repeat(uint8_t layer, uint16_t duration_ms, uint8_t times) {
 791    if (times > UINT8_MAX / 2) {
 792        times = UINT8_MAX / 2;
 793    }
 794
 795    _times_remaining = times * 2;
 796    _dur             = duration_ms;
 797
 798    rgblight_set_layer_state(layer, true);
 799    _times_remaining--;
 800    _blinking_layer_mask |= (rgblight_layer_mask_t)1 << layer;
 801    _repeat_timer = sync_timer_read() + duration_ms;
 802}
 803
 804void rgblight_unblink_layer(uint8_t layer) {
 805    rgblight_set_layer_state(layer, false);
 806    _blinking_layer_mask &= ~((rgblight_layer_mask_t)1 << layer);
 807}
 808
 809void rgblight_unblink_all_but_layer(uint8_t layer) {
 810    for (uint8_t i = 0; i < RGBLIGHT_MAX_LAYERS; i++) {
 811        if (i != layer) {
 812            if ((_blinking_layer_mask & (rgblight_layer_mask_t)1 << i) != 0) {
 813                rgblight_unblink_layer(i);
 814            }
 815        }
 816    }
 817}
 818
 819void rgblight_blink_layer_repeat_helper(void) {
 820    if (_blinking_layer_mask != 0 && timer_expired(sync_timer_read(), _repeat_timer)) {
 821        for (uint8_t layer = 0; layer < RGBLIGHT_MAX_LAYERS; layer++) {
 822            if ((_blinking_layer_mask & (rgblight_layer_mask_t)1 << layer) != 0) {
 823                if (_times_remaining % 2 == 1) {
 824                    rgblight_set_layer_state(layer, false);
 825                } else {
 826                    rgblight_set_layer_state(layer, true);
 827                }
 828            }
 829        }
 830        _times_remaining--;
 831        if (_times_remaining <= 0) {
 832            _blinking_layer_mask = 0;
 833        } else {
 834            _repeat_timer = sync_timer_read() + _dur;
 835        }
 836    }
 837}
 838#    endif
 839
 840#endif
 841
 842#ifdef RGBLIGHT_SLEEP
 843
 844void rgblight_suspend(void) {
 845    rgblight_timer_disable();
 846    if (!is_suspended) {
 847        is_suspended        = true;
 848        pre_suspend_enabled = rgblight_config.enable;
 849
 850#    ifdef RGBLIGHT_LAYER_BLINK
 851        // make sure any layer blinks don't come back after suspend
 852        rgblight_status.enabled_layer_mask &= ~_blinking_layer_mask;
 853        _blinking_layer_mask = 0;
 854#    endif
 855
 856        rgblight_disable_noeeprom();
 857    }
 858}
 859
 860void rgblight_wakeup(void) {
 861    is_suspended = false;
 862
 863    if (pre_suspend_enabled) {
 864        rgblight_enable_noeeprom();
 865    }
 866#    ifdef RGBLIGHT_LAYERS_OVERRIDE_RGB_OFF
 867    // Need this or else the LEDs won't be set
 868    else if (rgblight_status.enabled_layer_mask != 0) {
 869        rgblight_set();
 870    }
 871#    endif
 872
 873    rgblight_timer_enable();
 874}
 875
 876#endif
 877
 878void rgblight_set(void) {
 879    if (!rgblight_config.enable) {
 880        for (uint8_t i = rgblight_ranges.effect_start_pos; i < rgblight_ranges.effect_end_pos; i++) {
 881            rgblight_driver.set_color(rgblight_led_index(i), 0, 0, 0);
 882        }
 883    }
 884
 885#ifdef RGBLIGHT_LAYERS
 886    if (rgblight_layers != NULL
 887#    if !defined(RGBLIGHT_LAYERS_OVERRIDE_RGB_OFF)
 888        && rgblight_config.enable
 889#    elif defined(RGBLIGHT_SLEEP)
 890        && !is_suspended
 891#    endif
 892    ) {
 893        rgblight_layers_write();
 894    }
 895#endif
 896
 897    rgblight_driver.flush();
 898}
 899
 900#ifdef RGBLIGHT_SPLIT
 901/* for split keyboard master side */
 902uint8_t rgblight_get_change_flags(void) {
 903    return rgblight_status.change_flags;
 904}
 905
 906void rgblight_clear_change_flags(void) {
 907    rgblight_status.change_flags = 0;
 908}
 909
 910void rgblight_get_syncinfo(rgblight_syncinfo_t *syncinfo) {
 911    syncinfo->config = rgblight_config;
 912    syncinfo->status = rgblight_status;
 913}
 914
 915/* for split keyboard slave side */
 916void rgblight_update_sync(rgblight_syncinfo_t *syncinfo, bool write_to_eeprom) {
 917#    ifdef RGBLIGHT_LAYERS
 918    if (syncinfo->status.change_flags & RGBLIGHT_STATUS_CHANGE_LAYERS) {
 919        rgblight_status.enabled_layer_mask = syncinfo->status.enabled_layer_mask;
 920    }
 921#    endif
 922    if (syncinfo->status.change_flags & RGBLIGHT_STATUS_CHANGE_MODE) {
 923        if (syncinfo->config.enable) {
 924            rgblight_config.enable = 1; // == rgblight_enable_noeeprom();
 925            rgblight_mode_eeprom_helper(syncinfo->config.mode, write_to_eeprom);
 926        } else {
 927            rgblight_disable_noeeprom();
 928        }
 929    }
 930    if (syncinfo->status.change_flags & RGBLIGHT_STATUS_CHANGE_HSVS) {
 931        rgblight_sethsv_eeprom_helper(syncinfo->config.hue, syncinfo->config.sat, syncinfo->config.val, write_to_eeprom);
 932        // rgblight_config.speed = config->speed; // NEED???
 933    }
 934#    ifdef RGBLIGHT_USE_TIMER
 935    if (syncinfo->status.change_flags & RGBLIGHT_STATUS_CHANGE_TIMER) {
 936        if (syncinfo->status.timer_enabled) {
 937            rgblight_timer_enable();
 938        } else {
 939            rgblight_timer_disable();
 940        }
 941    }
 942#        ifndef RGBLIGHT_SPLIT_NO_ANIMATION_SYNC
 943    if (syncinfo->status.change_flags & RGBLIGHT_STATUS_ANIMATION_TICK) {
 944        animation_status.restart = true;
 945    }
 946#        endif /* RGBLIGHT_SPLIT_NO_ANIMATION_SYNC */
 947#    endif     /* RGBLIGHT_USE_TIMER */
 948}
 949#endif /* RGBLIGHT_SPLIT */
 950
 951#ifdef RGBLIGHT_USE_TIMER
 952
 953typedef void (*effect_func_t)(animation_status_t *anim);
 954
 955// Animation timer -- use system timer (AVR Timer0)
 956void rgblight_timer_init(void) {
 957    rgblight_status.timer_enabled = false;
 958    RGBLIGHT_SPLIT_SET_CHANGE_TIMER_ENABLE;
 959}
 960void rgblight_timer_enable(void) {
 961    if (!is_static_effect(rgblight_config.mode)) {
 962        rgblight_status.timer_enabled = true;
 963    }
 964    animation_status.last_timer = sync_timer_read();
 965    RGBLIGHT_SPLIT_SET_CHANGE_TIMER_ENABLE;
 966    dprintf("rgblight timer enabled.\n");
 967}
 968void rgblight_timer_disable(void) {
 969    rgblight_status.timer_enabled = false;
 970    RGBLIGHT_SPLIT_SET_CHANGE_TIMER_ENABLE;
 971    dprintf("rgblight timer disable.\n");
 972}
 973void rgblight_timer_toggle(void) {
 974    dprintf("rgblight timer toggle.\n");
 975    if (rgblight_status.timer_enabled) {
 976        rgblight_timer_disable();
 977    } else {
 978        rgblight_timer_enable();
 979    }
 980}
 981
 982void rgblight_show_solid_color(uint8_t r, uint8_t g, uint8_t b) {
 983    rgblight_enable();
 984    rgblight_mode(RGBLIGHT_MODE_STATIC_LIGHT);
 985    rgblight_setrgb(r, g, b);
 986}
 987
 988static void rgblight_effect_dummy(animation_status_t *anim) {
 989    // do nothing
 990    /********
 991    dprintf("rgblight_task() what happened?\n");
 992    dprintf("is_static_effect %d\n", is_static_effect(rgblight_config.mode));
 993    dprintf("mode = %d, base_mode = %d, timer_enabled %d, ",
 994            rgblight_config.mode, rgblight_status.base_mode,
 995            rgblight_status.timer_enabled);
 996    dprintf("last_timer = %d\n",anim->last_timer);
 997    **/
 998}
 999
1000void rgblight_timer_task(void) {
1001    if (rgblight_status.timer_enabled) {
1002        effect_func_t effect_func   = rgblight_effect_dummy;
1003        uint16_t      interval_time = 2000; // dummy interval
1004        uint8_t       delta         = rgblight_config.mode - rgblight_status.base_mode;
1005        animation_status.delta      = delta;
1006
1007        // static light mode, do nothing here
1008        if (1 == 0) { // dummy
1009        }
1010#    ifdef RGBLIGHT_EFFECT_BREATHING
1011        else if (rgblight_status.base_mode == RGBLIGHT_MODE_BREATHING) {
1012            // breathing mode
1013            interval_time = get_interval_time(&RGBLED_BREATHING_INTERVALS[delta], 1, 100);
1014            effect_func   = rgblight_effect_breathing;
1015        }
1016#    endif
1017#    ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD
1018        else if (rgblight_status.base_mode == RGBLIGHT_MODE_RAINBOW_MOOD) {
1019            // rainbow mood mode
1020            interval_time = get_interval_time(&RGBLED_RAINBOW_MOOD_INTERVALS[delta], 5, 100);
1021            effect_func   = rgblight_effect_rainbow_mood;
1022        }
1023#    endif
1024#    ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL
1025        else if (rgblight_status.base_mode == RGBLIGHT_MODE_RAINBOW_SWIRL) {
1026            // rainbow swirl mode
1027            interval_time = get_interval_time(&RGBLED_RAINBOW_SWIRL_INTERVALS[delta / 2], 1, 100);
1028            effect_func   = rgblight_effect_rainbow_swirl;
1029        }
1030#    endif
1031#    ifdef RGBLIGHT_EFFECT_SNAKE
1032        else if (rgblight_status.base_mode == RGBLIGHT_MODE_SNAKE) {
1033            // snake mode
1034            interval_time = get_interval_time(&RGBLED_SNAKE_INTERVALS[delta / 2], 1, 200);
1035            effect_func   = rgblight_effect_snake;
1036        }
1037#    endif
1038#    ifdef RGBLIGHT_EFFECT_KNIGHT
1039        else if (rgblight_status.base_mode == RGBLIGHT_MODE_KNIGHT) {
1040            // knight mode
1041            interval_time = get_interval_time(&RGBLED_KNIGHT_INTERVALS[delta], 5, 100);
1042            effect_func   = rgblight_effect_knight;
1043        }
1044#    endif
1045#    ifdef RGBLIGHT_EFFECT_CHRISTMAS
1046        else if (rgblight_status.base_mode == RGBLIGHT_MODE_CHRISTMAS) {
1047            // christmas mode
1048            interval_time = RGBLIGHT_EFFECT_CHRISTMAS_INTERVAL;
1049            effect_func   = (effect_func_t)rgblight_effect_christmas;
1050        }
1051#    endif
1052#    ifdef RGBLIGHT_EFFECT_RGB_TEST
1053        else if (rgblight_status.base_mode == RGBLIGHT_MODE_RGB_TEST) {
1054            // RGB test mode
1055            interval_time = pgm_read_word(&RGBLED_RGBTEST_INTERVALS[0]);
1056            effect_func   = (effect_func_t)rgblight_effect_rgbtest;
1057        }
1058#    endif
1059#    ifdef RGBLIGHT_EFFECT_ALTERNATING
1060        else if (rgblight_status.base_mode == RGBLIGHT_MODE_ALTERNATING) {
1061            interval_time = 500;
1062            effect_func   = (effect_func_t)rgblight_effect_alternating;
1063        }
1064#    endif
1065#    ifdef RGBLIGHT_EFFECT_TWINKLE
1066        else if (rgblight_status.base_mode == RGBLIGHT_MODE_TWINKLE) {
1067            interval_time = get_interval_time(&RGBLED_TWINKLE_INTERVALS[delta % 3], 5, 30);
1068            effect_func   = (effect_func_t)rgblight_effect_twinkle;
1069        }
1070#    endif
1071        if (animation_status.restart) {
1072            animation_status.restart    = false;
1073            animation_status.last_timer = sync_timer_read();
1074            animation_status.pos16      = 0; // restart signal to local each effect
1075        }
1076        uint16_t now = sync_timer_read();
1077        if (timer_expired(now, animation_status.last_timer)) {
1078#    if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC)
1079            static uint16_t report_last_timer = 0;
1080            static bool     tick_flag         = false;
1081            uint16_t        oldpos16;
1082            if (tick_flag) {
1083                tick_flag = false;
1084                if (timer_expired(now, report_last_timer)) {
1085                    report_last_timer += 30000;
1086                    dprintf("rgblight animation tick report to slave\n");
1087                    RGBLIGHT_SPLIT_ANIMATION_TICK;
1088                }
1089            }
1090            oldpos16 = animation_status.pos16;
1091#    endif
1092            animation_status.last_timer += interval_time;
1093            effect_func(&animation_status);
1094#    if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC)
1095            if (animation_status.pos16 == 0 && oldpos16 != 0) {
1096                tick_flag = true;
1097            }
1098#    endif
1099        }
1100    }
1101
1102#    ifdef RGBLIGHT_LAYERS
1103#        ifdef RGBLIGHT_LAYER_BLINK
1104    rgblight_blink_layer_repeat_helper();
1105#        endif
1106
1107    if (deferred_set_layer_state) {
1108        deferred_set_layer_state = false;
1109
1110        // Static modes don't have a ticker running to update the LEDs
1111        if (rgblight_status.timer_enabled == false) {
1112            rgblight_mode_noeeprom(rgblight_config.mode);
1113        }
1114
1115#        ifdef RGBLIGHT_LAYERS_OVERRIDE_RGB_OFF
1116        // If not enabled, then nothing else will actually set the LEDs...
1117        if (!rgblight_config.enable) {
1118            rgblight_set();
1119        }
1120#        endif
1121    }
1122#    endif
1123}
1124
1125#endif /* RGBLIGHT_USE_TIMER */
1126
1127#if defined(RGBLIGHT_EFFECT_BREATHING) || defined(RGBLIGHT_EFFECT_TWINKLE)
1128
1129#    ifndef RGBLIGHT_EFFECT_BREATHE_CENTER
1130#        ifndef RGBLIGHT_BREATHE_TABLE_SIZE
1131#            define RGBLIGHT_BREATHE_TABLE_SIZE 256 // 256 or 128 or 64
1132#        endif
1133#        include <rgblight_breathe_table.h>
1134#    endif
1135
1136static uint8_t breathe_calc(uint8_t pos) {
1137    // http://sean.voisen.org/blog/2011/10/breathing-led-with-arduino/
1138#    ifdef RGBLIGHT_EFFECT_BREATHE_TABLE
1139    return pgm_read_byte(&rgblight_effect_breathe_table[pos / table_scale]);
1140#    else
1141    return (exp(sin((pos / 255.0) * M_PI)) - RGBLIGHT_EFFECT_BREATHE_CENTER / M_E) * (RGBLIGHT_EFFECT_BREATHE_MAX / (M_E - 1 / M_E));
1142#    endif
1143}
1144
1145#endif
1146
1147// Effects
1148#ifdef RGBLIGHT_EFFECT_BREATHING
1149
1150__attribute__((weak)) const uint8_t RGBLED_BREATHING_INTERVALS[] PROGMEM = {30, 20, 10, 5};
1151
1152void rgblight_effect_breathing(animation_status_t *anim) {
1153    uint8_t val = breathe_calc(anim->pos);
1154    rgblight_sethsv_noeeprom_old(rgblight_config.hue, rgblight_config.sat, val);
1155    anim->pos = (anim->pos + 1);
1156}
1157#endif
1158
1159#ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD
1160__attribute__((weak)) const uint8_t RGBLED_RAINBOW_MOOD_INTERVALS[] PROGMEM = {120, 60, 30};
1161
1162void rgblight_effect_rainbow_mood(animation_status_t *anim) {
1163    rgblight_sethsv_noeeprom_old(anim->current_hue, rgblight_config.sat, rgblight_config.val);
1164    anim->current_hue++;
1165}
1166#endif
1167
1168#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL
1169#    ifndef RGBLIGHT_RAINBOW_SWIRL_RANGE
1170#        define RGBLIGHT_RAINBOW_SWIRL_RANGE 255
1171#    endif
1172
1173__attribute__((weak)) const uint8_t RGBLED_RAINBOW_SWIRL_INTERVALS[] PROGMEM = {100, 50, 20};
1174
1175void rgblight_effect_rainbow_swirl(animation_status_t *anim) {
1176    uint8_t hue;
1177    uint8_t i;
1178
1179    for (i = 0; i < rgblight_ranges.effect_num_leds; i++) {
1180        hue = (RGBLIGHT_RAINBOW_SWIRL_RANGE / rgblight_ranges.effect_num_leds * i + anim->current_hue);
1181        sethsv(hue, rgblight_config.sat, rgblight_config.val, i + rgblight_ranges.effect_start_pos);
1182    }
1183    rgblight_set();
1184
1185    if (anim->delta % 2) {
1186        anim->current_hue++;
1187    } else {
1188        anim->current_hue--;
1189    }
1190}
1191#endif
1192
1193#ifdef RGBLIGHT_EFFECT_SNAKE
1194__attribute__((weak)) const uint8_t RGBLED_SNAKE_INTERVALS[] PROGMEM = {100, 50, 20};
1195
1196void rgblight_effect_snake(animation_status_t *anim) {
1197    static uint8_t pos = 0;
1198    uint8_t        i, j;
1199    int8_t         k;
1200    int8_t         increment = 1;
1201
1202    if (anim->delta % 2) {
1203        increment = -1;
1204    }
1205
1206#    if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC)
1207    if (anim->pos == 0) { // restart signal
1208        if (increment == 1) {
1209            pos = rgblight_ranges.effect_num_leds - 1;
1210        } else {
1211            pos = 0;
1212        }
1213        anim->pos = 1;
1214    }
1215#    endif
1216
1217    for (i = 0; i < rgblight_ranges.effect_num_leds; i++) {
1218        rgblight_driver.set_color(rgblight_led_index(i + rgblight_ranges.effect_start_pos), 0, 0, 0);
1219
1220        for (j = 0; j < RGBLIGHT_EFFECT_SNAKE_LENGTH; j++) {
1221            k = pos + j * increment;
1222            if (k > RGBLIGHT_LED_COUNT) {
1223                k = k % (RGBLIGHT_LED_COUNT);
1224            }
1225            if (k < 0) {
1226                k = k + rgblight_ranges.effect_num_leds;
1227            }
1228            if (i == k) {
1229                sethsv(rgblight_config.hue, rgblight_config.sat, (uint8_t)(rgblight_config.val * (RGBLIGHT_EFFECT_SNAKE_LENGTH - j) / RGBLIGHT_EFFECT_SNAKE_LENGTH), i + rgblight_ranges.effect_start_pos);
1230            }
1231        }
1232    }
1233    rgblight_set();
1234    if (increment == 1) {
1235        if (pos - RGBLIGHT_EFFECT_SNAKE_INCREMENT < 0) {
1236            pos = rgblight_ranges.effect_num_leds - 1;
1237#    if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC)
1238            anim->pos = 0;
1239#    endif
1240        } else {
1241            pos -= RGBLIGHT_EFFECT_SNAKE_INCREMENT;
1242#    if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC)
1243            anim->pos = 1;
1244#    endif
1245        }
1246    } else {
1247        pos = (pos + RGBLIGHT_EFFECT_SNAKE_INCREMENT) % rgblight_ranges.effect_num_leds;
1248#    if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC)
1249        anim->pos = pos;
1250#    endif
1251    }
1252}
1253#endif
1254
1255#ifdef RGBLIGHT_EFFECT_KNIGHT
1256__attribute__((weak)) const uint8_t RGBLED_KNIGHT_INTERVALS[] PROGMEM = {127, 63, 31};
1257
1258void rgblight_effect_knight(animation_status_t *anim) {
1259    static int8_t low_bound  = 0;
1260    static int8_t high_bound = RGBLIGHT_EFFECT_KNIGHT_LENGTH - 1;
1261    static int8_t increment  = RGBLIGHT_EFFECT_KNIGHT_INCREMENT;
1262    uint8_t       i, cur;
1263
1264#    if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC)
1265    if (anim->pos == 0) { // restart signal
1266        anim->pos  = 1;
1267        low_bound  = 0;
1268        high_bound = RGBLIGHT_EFFECT_KNIGHT_LENGTH - 1;
1269        increment  = 1;
1270    }
1271#    endif
1272    // Set all the LEDs to 0
1273    for (i = rgblight_ranges.effect_start_pos; i < rgblight_ranges.effect_end_pos; i++) {
1274        rgblight_driver.set_color(rgblight_led_index(i), 0, 0, 0);
1275    }
1276    // Determine which LEDs should be lit up
1277    for (i = 0; i < RGBLIGHT_EFFECT_KNIGHT_LED_NUM; i++) {
1278        cur = (i + RGBLIGHT_EFFECT_KNIGHT_OFFSET) % rgblight_ranges.effect_num_leds + rgblight_ranges.effect_start_pos;
1279
1280        if (i >= low_bound && i <= high_bound) {
1281            sethsv(rgblight_config.hue, rgblight_config.sat, rgblight_config.val, cur);
1282        } else {
1283            rgblight_driver.set_color(rgblight_led_index(cur), 0, 0, 0);
1284        }
1285    }
1286    rgblight_set();
1287
1288    // Move from low_bound to high_bound changing the direction we increment each
1289    // time a boundary is hit.
1290    low_bound += increment;
1291    high_bound += increment;
1292
1293    if (high_bound <= 0 || low_bound >= RGBLIGHT_EFFECT_KNIGHT_LED_NUM - 1) {
1294        increment = -increment;
1295#    if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC)
1296        if (increment == 1) {
1297            anim->pos = 0;
1298        }
1299#    endif
1300    }
1301}
1302#endif
1303
1304#ifdef RGBLIGHT_EFFECT_CHRISTMAS
1305#    define CUBED(x) ((x) * (x) * (x))
1306
1307/**
1308 * Christmas lights effect, with a smooth animation between red & green.
1309 */
1310void rgblight_effect_christmas(animation_status_t *anim) {
1311    static int8_t increment = 1;
1312    const uint8_t max_pos   = 32;
1313    const uint8_t hue_green = 85;
1314
1315    uint32_t xa;
1316    uint8_t  hue, val;
1317    uint8_t  i;
1318
1319    // The effect works by animating anim->pos from 0 to 32 and back to 0.
1320    // The pos is used in a cubic bezier formula to ease-in-out between red and green, leaving the interpolated colors visible as short as possible.
1321    xa  = CUBED((uint32_t)anim->pos);
1322    hue = ((uint32_t)hue_green) * xa / (xa + CUBED((uint32_t)(max_pos - anim->pos)));
1323    // Additionally, these interpolated colors get shown with a slightly darker value, to make them less prominent than the main colors.
1324    val = 255 - (3 * (hue < hue_green / 2 ? hue : hue_green - hue) / 2);
1325
1326    for (i = 0; i < rgblight_ranges.effect_num_leds; i++) {
1327        uint8_t local_hue = (i / RGBLIGHT_EFFECT_CHRISTMAS_STEP) % 2 ? hue : hue_green - hue;
1328        sethsv(local_hue, rgblight_config.sat, val, i + rgblight_ranges.effect_start_pos);
1329    }
1330    rgblight_set();
1331
1332    if (anim->pos == 0) {
1333        increment = 1;
1334    } else if (anim->pos == max_pos) {
1335        increment = -1;
1336    }
1337    anim->pos += increment;
1338}
1339#endif
1340
1341#ifdef RGBLIGHT_EFFECT_RGB_TEST
1342__attribute__((weak)) const uint16_t RGBLED_RGBTEST_INTERVALS[] PROGMEM = {1024};
1343
1344void rgblight_effect_rgbtest(animation_status_t *anim) {
1345    uint8_t val = rgblight_get_val();
1346
1347    uint8_t r = anim->pos & 1 ? val : 0;
1348    uint8_t g = anim->pos & 2 ? val : 0;
1349    uint8_t b = anim->pos & 4 ? val : 0;
1350    rgblight_setrgb(r, g, b);
1351    anim->pos = (anim->pos + 1) % 8;
1352}
1353#endif
1354
1355#ifdef RGBLIGHT_EFFECT_ALTERNATING
1356void rgblight_effect_alternating(animation_status_t *anim) {
1357    for (int i = 0; i < rgblight_ranges.effect_num_leds; i++) {
1358        if (i < rgblight_ranges.effect_num_leds / 2 && anim->pos) {
1359            sethsv(rgblight_config.hue, rgblight_config.sat, rgblight_config.val, i + rgblight_ranges.effect_start_pos);
1360        } else if (i >= rgblight_ranges.effect_num_leds / 2 && !anim->pos) {
1361            sethsv(rgblight_config.hue, rgblight_config.sat, rgblight_config.val, i + rgblight_ranges.effect_start_pos);
1362        } else {
1363            sethsv(rgblight_config.hue, rgblight_config.sat, 0, i + rgblight_ranges.effect_start_pos);
1364        }
1365    }
1366    rgblight_set();
1367    anim->pos = (anim->pos + 1) % 2;
1368}
1369#endif
1370
1371#ifdef RGBLIGHT_EFFECT_TWINKLE
1372__attribute__((weak)) const uint8_t RGBLED_TWINKLE_INTERVALS[] PROGMEM = {30, 15, 5};
1373
1374typedef struct PACKED {
1375    hsv_t   hsv;
1376    uint8_t life;
1377    uint8_t max_life;
1378} TwinkleState;
1379
1380static TwinkleState led_twinkle_state[RGBLIGHT_LED_COUNT];
1381
1382void rgblight_effect_twinkle(animation_status_t *anim) {
1383    const bool random_color = anim->delta / 3;
1384    const bool restart      = anim->pos == 0;
1385    anim->pos               = 1;
1386
1387    const uint8_t bottom = breathe_calc(0);
1388    const uint8_t top    = breathe_calc(127);
1389
1390    uint8_t frac(uint8_t n, uint8_t d) {
1391        return (uint16_t)255 * n / d;
1392    }
1393    uint8_t scale(uint16_t v, uint8_t scale) {
1394        return (v * scale) >> 8;
1395    }
1396
1397    const uint8_t trigger = scale((uint16_t)0xFF * RGBLIGHT_EFFECT_TWINKLE_PROBABILITY, 127 + rgblight_config.val / 2);
1398
1399    for (uint8_t i = 0; i < rgblight_ranges.effect_num_leds; i++) {
1400        TwinkleState *t = &(led_twinkle_state[i]);
1401        hsv_t *       c = &(t->hsv);
1402
1403        if (!random_color) {
1404            c->h = rgblight_config.hue;
1405            c->s = rgblight_config.sat;
1406        }
1407
1408        if (restart) {
1409            // Restart
1410            t->life = 0;
1411            c->v    = 0;
1412        } else if (t->life) {
1413            // This LED is already on, either brightening or dimming
1414            t->life--;
1415            uint8_t unscaled = frac(breathe_calc(frac(t->life, t->max_life)) - bottom, top - bottom);
1416            c->v             = scale(rgblight_config.val, unscaled);
1417        } else if ((rand() % 0xFF) < trigger) {
1418            // This LED is off, but was randomly selected to start brightening
1419            if (random_color) {
1420                c->h = rand() % 0xFF;
1421                c->s = (rand() % (rgblight_config.sat / 2)) + (rgblight_config.sat / 2);
1422            }
1423            c->v        = 0;
1424            t->max_life = MAX(20, MIN(RGBLIGHT_EFFECT_TWINKLE_LIFE, rgblight_config.val));
1425            t->life     = t->max_life;
1426        } else {
1427            // This LED is off, and was NOT selected to start brightening
1428        }
1429
1430        sethsv(c->h, c->s, c->v, i + rgblight_ranges.effect_start_pos);
1431    }
1432
1433    rgblight_set();
1434}
1435#endif
1436
1437void preprocess_rgblight(void) {
1438#ifdef VELOCIKEY_ENABLE
1439    if (rgblight_velocikey_enabled()) {
1440        rgblight_velocikey_accelerate();
1441    }
1442#endif
1443}
1444
1445void rgblight_task(void) {
1446#ifdef RGBLIGHT_USE_TIMER
1447    rgblight_timer_task();
1448#endif
1449
1450#ifdef VELOCIKEY_ENABLE
1451    if (rgblight_velocikey_enabled()) {
1452        rgblight_velocikey_decelerate();
1453    }
1454#endif
1455}
1456
1457#ifdef VELOCIKEY_ENABLE
1458#    define TYPING_SPEED_MAX_VALUE 200
1459
1460static uint8_t typing_speed = 0;
1461
1462bool rgblight_velocikey_enabled(void) {
1463    return rgblight_config.velocikey;
1464}
1465
1466void rgblight_velocikey_toggle(void) {
1467    dprintf("rgblight velocikey toggle [EEPROM]: rgblight_config.velocikey = %u\n", !rgblight_config.velocikey);
1468    rgblight_config.velocikey = !rgblight_config.velocikey;
1469    eeconfig_update_rgblight_current();
1470}
1471
1472void rgblight_velocikey_accelerate(void) {
1473    if (typing_speed < TYPING_SPEED_MAX_VALUE) typing_speed += (TYPING_SPEED_MAX_VALUE / 100);
1474}
1475
1476void rgblight_velocikey_decelerate(void) {
1477    static uint16_t decay_timer = 0;
1478
1479    if (timer_elapsed(decay_timer) > 500 || decay_timer == 0) {
1480        if (typing_speed > 0) typing_speed -= 1;
1481        // Decay a little faster at half of max speed
1482        if (typing_speed > TYPING_SPEED_MAX_VALUE / 2) typing_speed -= 1;
1483        // Decay even faster at 3/4 of max speed
1484        if (typing_speed > TYPING_SPEED_MAX_VALUE / 4 * 3) typing_speed -= 2;
1485        decay_timer = timer_read();
1486    }
1487}
1488
1489uint8_t rgblight_velocikey_match_speed(uint8_t minValue, uint8_t maxValue) {
1490    return MAX(minValue, maxValue - (maxValue - minValue) * ((float)typing_speed / TYPING_SPEED_MAX_VALUE));
1491}
1492
1493#endif