Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions docs/features/led_matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ enum led_matrix_effects {
LED_MATRIX_SOLID_MULTISPLASH, // Value pulses away from multiple key hits then fades out
LED_MATRIX_WAVE_LEFT_RIGHT, // Sine wave scrolling from left to right
LED_MATRIX_WAVE_UP_DOWN, // Sine wave scrolling from up to down
LED_MATRIX_TYPING_HEATMAP, // How hot is your WPM!
LED_MATRIX_EFFECT_MAX
};
```
Expand All @@ -140,6 +141,14 @@ You can enable a single effect by defining `ENABLE_[EFFECT_NAME]` in your `confi
|`#define ENABLE_LED_MATRIX_WAVE_LEFT_RIGHT` |Enables `LED_MATRIX_WAVE_LEFT_RIGHT` |
|`#define ENABLE_LED_MATRIX_WAVE_UP_DOWN` |Enables `LED_MATRIX_WAVE_UP_DOWN` |

|Framebuffer Defines |Description |
|------------------------------------------------------|-----------------------------------------------|
|`#define ENABLE_LED_MATRIX_TYPING_HEATMAP` |Enables `LED_MATRIX_TYPING_HEATMAP` |

::: tip
These modes introduce additional logic that can increase firmware size.
:::

|Reactive Defines |Description |
|-------------------------------------------------------|----------------------------------------------|
|`#define ENABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE` |Enables `LED_MATRIX_SOLID_REACTIVE_SIMPLE` |
Expand All @@ -156,6 +165,42 @@ You can enable a single effect by defining `ENABLE_[EFFECT_NAME]` in your `confi
These modes introduce additional logic that can increase firmware size.
:::

### LED Matrix Effect Typing Heatmap {#led-matrix-effect-typing-heatmap}

This effect will scale the LED matrix brightness according to a heatmap of recently pressed keys. Whenever a key is pressed its "temperature" increases as well as that of its neighboring keys. The temperature of each key is then decreased automatically every 25 milliseconds by default.

In order to change the delay of temperature decrease define `LED_MATRIX_TYPING_HEATMAP_DECREASE_DELAY_MS`:

```c
#define LED_MATRIX_TYPING_HEATMAP_DECREASE_DELAY_MS 50
```

As heatmap uses the physical position of the leds set in the g_led_config, you may need to tweak the following options to get the best effect for your keyboard. Note the size of this grid is `224x64`.

Limit the distance the effect spreads to surrounding keys.

```c
#define LED_MATRIX_TYPING_HEATMAP_SPREAD 40
```

Limit how hot surrounding keys get from each press.

```c
#define LED_MATRIX_TYPING_HEATMAP_AREA_LIMIT 16
```

Remove the spread effect entirely.

```c
#define LED_MATRIX_TYPING_HEATMAP_SLIM
```

It's also possible to adjust the tempo of *heating up*. It's defined as the number of steps by which to increment the brightness. Decreasing this value increases the number of keystrokes needed to fully heat up the key.

```c
#define LED_MATRIX_TYPING_HEATMAP_INCREASE_STEP 32
```

## Custom LED Matrix Effects {#custom-led-matrix-effects}

By setting `LED_MATRIX_CUSTOM_USER = yes` in `rules.mk`, new effects can be defined directly from your keymap or userspace, without having to edit any QMK core files. To declare new effects, create a `led_matrix_user.inc` file in the user keymap directory or userspace folder.
Expand Down
1 change: 1 addition & 0 deletions quantum/led_matrix/animations/led_matrix_effects.inc
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@
#include "solid_splash_anim.h"
#include "wave_left_right_anim.h"
#include "wave_up_down_anim.h"
#include "typing_heatmap_anim.h"
112 changes: 112 additions & 0 deletions quantum/led_matrix/animations/typing_heatmap_anim.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#if defined(LED_MATRIX_FRAMEBUFFER_EFFECTS) && defined(ENABLE_LED_MATRIX_TYPING_HEATMAP)
LED_MATRIX_EFFECT(TYPING_HEATMAP)
# ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS
# ifndef LED_MATRIX_TYPING_HEATMAP_INCREASE_STEP
# define LED_MATRIX_TYPING_HEATMAP_INCREASE_STEP 32
# endif

# ifndef LED_MATRIX_TYPING_HEATMAP_DECREASE_DELAY_MS
# define LED_MATRIX_TYPING_HEATMAP_DECREASE_DELAY_MS 25
# endif

# ifndef LED_MATRIX_TYPING_HEATMAP_SPREAD
# define LED_MATRIX_TYPING_HEATMAP_SPREAD 40
# endif

# ifndef LED_MATRIX_TYPING_HEATMAP_AREA_LIMIT
# define LED_MATRIX_TYPING_HEATMAP_AREA_LIMIT 16
# endif
void process_led_matrix_typing_heatmap(uint8_t row, uint8_t col) {
# ifdef LED_MATRIX_TYPING_HEATMAP_SLIM
// Limit effect to pressed keys
g_led_frame_buffer[row][col] = qadd8(g_led_frame_buffer[row][col], LED_MATRIX_TYPING_HEATMAP_INCREASE_STEP);
# else
if (g_led_config.matrix_co[row][col] == NO_LED) { // skip as pressed key doesn't have an led position
return;
}
for (uint8_t i_row = 0; i_row < MATRIX_ROWS; i_row++) {
for (uint8_t i_col = 0; i_col < MATRIX_COLS; i_col++) {
if (g_led_config.matrix_co[i_row][i_col] == NO_LED) { // skip as target key doesn't have an led position
continue;
}
if (i_row == row && i_col == col) {
g_led_frame_buffer[row][col] = qadd8(g_led_frame_buffer[row][col], LED_MATRIX_TYPING_HEATMAP_INCREASE_STEP);
} else {
# define LED_DISTANCE(led_a, led_b) sqrt16(((int32_t)(led_a.x - led_b.x) * (int32_t)(led_a.x - led_b.x)) + ((int32_t)(led_a.y - led_b.y) * (int32_t)(led_a.y - led_b.y)))
uint8_t distance = LED_DISTANCE(g_led_config.point[g_led_config.matrix_co[row][col]], g_led_config.point[g_led_config.matrix_co[i_row][i_col]]);
# undef LED_DISTANCE
if (distance <= LED_MATRIX_TYPING_HEATMAP_SPREAD) {
uint8_t amount = qsub8(LED_MATRIX_TYPING_HEATMAP_SPREAD, distance);
if (amount > LED_MATRIX_TYPING_HEATMAP_AREA_LIMIT) {
amount = LED_MATRIX_TYPING_HEATMAP_AREA_LIMIT;
}
g_led_frame_buffer[i_row][i_col] = qadd8(g_led_frame_buffer[i_row][i_col], amount);
}
}
}
}
# endif
}

// A timer to track the last time we decremented all heatmap values.
static uint16_t heatmap_decrease_timer;
// Whether we should decrement the heatmap values during the next update.
static bool decrease_heatmap_values;

bool TYPING_HEATMAP(effect_params_t* params) {
LED_MATRIX_USE_LIMITS(led_min, led_max);

if (params->init) {
led_matrix_set_value_all(0);
memset(g_led_frame_buffer, 0, sizeof g_led_frame_buffer);
}

// The heatmap animation might run in several iterations depending on
// `LED_MATRIX_LED_PROCESS_LIMIT`, therefore we only want to update the
// timer when the animation starts.
if (params->iter == 0) {
decrease_heatmap_values = timer_elapsed(heatmap_decrease_timer) >= LED_MATRIX_TYPING_HEATMAP_DECREASE_DELAY_MS;

// Restart the timer if we are going to decrease the heatmap this frame.
if (decrease_heatmap_values) {
heatmap_decrease_timer = timer_read();
}
}

// Render heatmap & decrease
uint8_t count = 0;
for (uint8_t row = 0; row < MATRIX_ROWS && count < LED_MATRIX_LED_PROCESS_LIMIT; row++) {
for (uint8_t col = 0; col < MATRIX_COLS && LED_MATRIX_LED_PROCESS_LIMIT; col++) {
uint8_t val = g_led_frame_buffer[row][col];

bool processed = false;

uint8_t led[LED_HITS_TO_REMEMBER];
uint8_t led_count = led_matrix_map_row_column_to_led(row, col, led);
for (uint8_t index = 0; index < led_count; index++) {
uint8_t led_index = led[index];

if (led_index >= led_min && led_index < led_max) {
count++;

if (!HAS_ANY_FLAGS(g_led_config.flags[led_index], params->flags)) {
continue;
}

uint8_t value = scale8(led_matrix_eeconfig.val, val);
led_matrix_set_value(led_index, value);
processed = true;
}
}

if (processed && decrease_heatmap_values) {
g_led_frame_buffer[row][col] = qsub8(val, 1);
}
}
}

return led_matrix_check_finished_leds(led_max);
}

# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
#endif // defined(LED_MATRIX_FRAMEBUFFER_EFFECTS) && defined(ENABLE_LED_MATRIX_TYPING_HEATMAP)
5 changes: 5 additions & 0 deletions quantum/led_matrix/post_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@

// clang-format off

// framebuffer
#if defined(ENABLE_LED_MATRIX_TYPING_HEATMAP)
# define LED_MATRIX_FRAMEBUFFER_EFFECTS
#endif

// reactive
#if defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE) || \
defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_WIDE) || \
Expand Down