Skip to content

Commit

Permalink
Add RGB fade out before suspend
Browse files Browse the repository at this point in the history
  • Loading branch information
ViToni committed Dec 13, 2021
1 parent a810078 commit ab21c4d
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 5 deletions.
3 changes: 3 additions & 0 deletions keyboards/gmmk/pro/iso/keymaps/vitoni/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
#define RGB_MATRIX_STARTUP_MODE RGB_MATRIX_SOLID_COLOR
// number of milliseconds to wait until turning off RGB automatically
#define RGB_DISABLE_TIMEOUT 300000 // 300 seconds / 5 min
// start fading out before getting disabled
// fading out is timed (depending on the rgb_matrix_config.speed) to have finished before reaching RGB_DISABLE_TIMEOUT
#define RGB_DISABLE_WITH_FADE_OUT
#define RGB_DISABLE_WHEN_USB_SUSPENDED
// fade in when we have been suspended
#define RGB_FADE_IN
Expand Down
1 change: 1 addition & 0 deletions keyboards/gmmk/pro/iso/keymaps/vitoni/readme.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ When using `RGB_DISABLE_TIMEOUT` addtional options are available:

* `RGB_FADE_IN` makes the RGB lights fade in instead of setting the value/brightness to 100% (implicitly due to HSV including the brightness) when resuming after RGB lights have been turned off.
Fade in occurs when the keyboard is initialized and when the RGB brightness has been changed (e.g. suspending, fade out, etc.).
* `RGB_DISABLE_WITH_FADE_OUT` activates fade out before the keyboard is disabled by `RGB_DISABLE_TIMEOUT`.

Parameters used to define the behavior are:
[%header]
Expand Down
49 changes: 49 additions & 0 deletions users/vitoni/rgb_matrix_effects.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ bool fade_in_ranged(const uint8_t time, const uint8_t range_min, const uint8_t r
return scaled_sin_up(time, range_min, range_max, max_delta, &(rgb_matrix_config.hsv.v));
}

bool fade_out_ranged(const uint8_t time, const uint8_t range_min, const uint8_t range_max) {
static const uint8_t max_delta = 1;
return scaled_sin_down(time, range_min, range_max, max_delta, &(rgb_matrix_config.hsv.v));
}

/**
* @brief Convenience method to eventually skip the value part when setting HSV.
* @details When setting HSV this includes the value/brightness.
Expand Down Expand Up @@ -156,3 +161,47 @@ bool fade_in(const uint8_t time) {
return fade_in_ranged(time, range_min, range_max);
}
#endif

#if defined(RGB_DISABLE_WITH_FADE_OUT)

/**
* @brief Calculates the time offset required by fade out.
* @details Using an arbitrary timer any point on the Sinus curve might be pointed to.
* The offest is calculated so that
* a) the point is at the highest point in the curve and the curve is failing
* b) the point is near the current brightness (eg. fade out might be called while on breath effect).
* @param[in] time Current time usually represented by a(usually scaled) timer
* @return Offset required so that time matches the current brightness
*/
uint8_t calc_fade_out_offset(const uint8_t time) {
static const uint8_t range_min = 0;
static const uint8_t range_max = RGB_MATRIX_MAXIMUM_BRIGHTNESS;

// start at the right point in the sin() curve
uint8_t time_offset = offset_for_time(time, PHASE_HIGH);

// find the right offset to match the current brightness
for (int i = 1; i < 127; i++) {
const uint8_t value = scaled_sin(time + time_offset + 1, range_min, range_max);
if (in_range(value, range_min, range_max) && rgb_matrix_config.hsv.v < value) {
time_offset++;
} else {
break;
}
}

return time_offset;
}

/**
* @brief Decreases value/brightness until reaching 0 based on given timer.
* @param[in] time A (usually scaled) timer
* @return Returns `true` if 0 has been reached, `false` otherwise.
*/
bool fade_out(const uint8_t time) {
static const uint8_t range_min = 0;
static const uint8_t range_max = RGB_MATRIX_MAXIMUM_BRIGHTNESS;

return fade_out_ranged(time, range_min, range_max);
}
#endif
32 changes: 31 additions & 1 deletion users/vitoni/rgb_matrix_effects.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ enum states {
REGULAR //!< when in regular use
#if defined(RGB_FADE_IN)
,FADE_IN //!< when starting initially or before going back to REGULAR
#endif
#if defined(RGB_DISABLE_WITH_FADE_OUT)
,FADE_OUT //!< before supending
#endif
,SUSPENDED //!< expecting to be suspended by RGB_DISABLE_TIMEOUT any time
};
Expand Down Expand Up @@ -58,7 +61,7 @@ uint16_t scale_2_rgb_time(const uint8_t scaled_time);
*/
void rgb_matrix_sethsv_noeeprom_user(const uint16_t hue, const uint8_t sat, const uint8_t val);

#if defined(RGB_FADE_IN)
#if defined(RGB_FADE_IN) || defined(RGB_DISABLE_WITH_FADE_OUT)
# if defined(RGB_MATRIX_MAXIMUM_BRIGHTNESS)
# if (RGB_MATRIX_MAXIMUM_BRIGHTNESS) < 1
# error "RGB_MATRIX_MAXIMUM_BRIGHTNESS must not be less than ONE"
Expand All @@ -69,7 +72,9 @@ void rgb_matrix_sethsv_noeeprom_user(const uint16_t hue, const uint8_t sat, cons
# else
# define RGB_MATRIX_MAXIMUM_BRIGHTNESS 200
# endif
#endif

#if defined(RGB_FADE_IN)
/**
* @brief Calculates the time offset required by fade in.
* @details Using an arbitrary timer any point on the sine curve might be pointed to.
Expand All @@ -88,3 +93,28 @@ uint8_t calc_fade_in_offset(const uint8_t time);
*/
bool fade_in(const uint8_t time);
#endif

#if defined(RGB_DISABLE_WITH_FADE_OUT)
# if !defined(RGB_DISABLE_TIMEOUT)
# warning "RGB_DISABLE_WITH_FADE_OUT expects RGB_DISABLE_TIMEOUT to be defined"
# endif

/**
* @brief Calculates the time offset required by fade out.
* @details Using an arbitrary timer any point on the Sinus curve might be pointed to.
* The offest is calculated so that
* a) the point is at the highest point in the curve and the curve is failing
* b) the point is near the current brightness (eg. fade out might be called while on breath effect).
* @param[in] time Current time usually represented by a(usually scaled) timer
* @return Offset required so that time matches the current brightness
*/
uint8_t calc_fade_out_offset(const uint8_t time);

/**
* @brief Decreases value/brightness until reaching 0 based on given timer.
* @param[in] time A (usually scaled) timer
* @return Returns `true` if 0 has been reached, `false` otherwise.
*/
bool fade_out(const uint8_t time);
#endif

33 changes: 33 additions & 0 deletions users/vitoni/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,36 @@ bool scaled_sin_up(const uint8_t theta, const uint8_t range_min, const uint8_t r

return delta <= max_delta;
}

/**
* @brief Decreases the given value until reaching range_min.
* The decrements occur following an downwards sinus wave (scaled from range_min to range_max).
* @param[in] theta Angle (a full circle mapped to 0-255) used as input for sinus calculation.
* @param[in] range_min Lower bound of range (inclusive).
* @param[in] range_max Upper bound of range (inclusive).
* @param[in] max_delta Maximum delta between value and range_min (due to values being integers and eventually not fully matching).
* @param[in,out] value Reference of variable to be decreased
* @return `true` if value and range_max are within a delta of 3 (chosen by fair dice rolling), `false` otherwise
* @see scaled_sin()
*/
bool scaled_sin_down(const uint8_t theta, const uint8_t range_min, const uint8_t range_max, const uint8_t max_delta, uint8_t *value) {
// ensure lower range bound
if ((*value) <= range_min) {
(*value) = range_min;
return true;
}

const uint8_t new_value = scaled_sin(theta, range_min, range_max);
if (in_range(new_value, range_min, range_max) && new_value < (*value)) {
(*value) = new_value;

return range_min == (*value);
}

const uint8_t delta = (*value) - range_min;
if (delta <= max_delta) {
(*value) = range_min;
}

return delta <= max_delta;
}
13 changes: 13 additions & 0 deletions users/vitoni/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,16 @@ uint8_t scaled_sin(const uint8_t theta, const uint8_t range_min, const uint8_t r
* @see scaled_sin()
*/
bool scaled_sin_up(const uint8_t thea, const uint8_t range_min, const uint8_t range_max, const uint8_t max_delta, uint8_t *value);

/**
* @brief Decreases the given value until reaching range_min.
* The decrements occur following an downwards sinus wave (scaled from range_min to range_max).
* @param[in] theta Angle (a full circle mapped to 0-255) used as input for sinus calculation.
* @param[in] range_min Lower bound of range (inclusive).
* @param[in] range_max Upper bound of range (inclusive).
* @param[in] max_delta Maximum delta between value and range_min (due to values being integers and eventually not fully matching).
* @param[in,out] value Reference of variable to be decreased
* @return `true` if value and range_max are within a delta of 3 (chosen by fair dice rolling), `false` otherwise
* @see scaled_sin()
*/
bool scaled_sin_down(const uint8_t theta, const uint8_t range_min, const uint8_t range_max, const uint8_t max_delta, uint8_t *value);
40 changes: 36 additions & 4 deletions users/vitoni/vitoni.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,54 @@
#include "rgb_matrix_effects.h"
#include "utils.h"

#if defined(RGB_FADE_IN)
#if defined(RGB_FADE_IN) || defined(RGB_DISABLE_WITH_FADE_OUT)
static uint8_t state;

// flag used to indicate that offset calculation is needed to adjust the timer,
// so that it matches the index used for sine calculation
static bool calc_offset;

void matrix_scan_user_rgb(void) {
#if defined(RGB_DISABLE_WITH_FADE_OUT)
const uint8_t time = rgb_time_2_scale();
#endif
static uint8_t time_offset;

const uint32_t inactivity_time = last_input_activity_elapsed();
const uint32_t inactivity_millis = last_input_activity_elapsed();

#if defined(RGB_DISABLE_WITH_FADE_OUT)
const uint32_t fade_out_duration = scale_2_rgb_time(128);
const uint32_t start_fade_out_after_millis = (RGB_DISABLE_TIMEOUT) > fade_out_duration
? (RGB_DISABLE_TIMEOUT) - fade_out_duration
: 0;

// setting brightness to black as starting point for fade in
if (start_fade_out_after_millis <= inactivity_millis) {
update_value(&state, FADE_OUT, &calc_offset);
}
#else
// having to set brightness "manually" to black as starting point for fade in
// for the time when returning from suspended state
if (RGB_DISABLE_TIMEOUT <= inactivity_time + 15) {
if (RGB_DISABLE_TIMEOUT <= inactivity_millis + 15) {
rgb_matrix_config.hsv.v = 0;
state = SUSPENDED;
}
#endif

switch(state) {
#if defined(RGB_DISABLE_WITH_FADE_OUT)
case FADE_OUT:
if (calc_offset) {
time_offset = calc_fade_out_offset(time);

// resetting flag for subsequent calls
calc_offset = false;
}
if (fade_out(time + time_offset)) {
update_value(&state, SUSPENDED, &calc_offset);
}
break;
#endif
#if defined(RGB_FADE_IN)
case FADE_IN:
{
// since we want to be active, fade in should be faster than e.g. fading out
Expand All @@ -44,11 +72,13 @@ void matrix_scan_user_rgb(void) {
}
}
break;
#endif
default:
break;
}
}

#if defined(RGB_FADE_IN)
bool process_record_user_rgb(const uint16_t keycode, const keyrecord_t *record) {
// if we are in a non regular state we might have faded out (eventually partially)
// so we restore brightness (to max as we don't keep track of manually changed brightness)
Expand All @@ -69,3 +99,5 @@ void suspend_wakeup_init_user(void) {
}
}
#endif // defined(RGB_FADE_IN)

#endif // defined(RGB_FADE_IN) || defined(RGB_DISABLE_WITH_FADE_OUT)

0 comments on commit ab21c4d

Please sign in to comment.