Merge remote-tracking branch 'plugin/Heatmap/f/monorepo' into f/monorepo-stage2

pull/389/head
Gergely Nagy 6 years ago
commit 0956c83f8f
No known key found for this signature in database
GPG Key ID: AC1E90BAC433F68F

@ -0,0 +1,89 @@
# Kaleidoscope-Heatmap
The `Heatmap` plugin provides a LED effect, that displays a heatmap on the
keyboard. The LEDs under each key will have a color according to how much use
they see. Fewer used keys will have deep blue colors, that gradually turns
lighter, then green, to yellow, and finally red for the most used keys. The
heatmap is not updated on every key press, but periodically. It's precision is
also an approximation, and not a hundred percent exact. Nevertheless, it is a
reasonable estimate.
## Using the plugin
The plugin comes with reasonable defaults pre-configured, all one needs to do is
include the header, and make sure the plugin is in use:
```c++
#include <Kaleidoscope.h>
#include <Kaleidoscope-LEDControl.h>
#include <Kaleidoscope-Heatmap.h>
static const cRGB heat_colors[] PROGMEM = {
{ 0, 0, 0}, // black
{255, 25, 25}, // blue
{ 25, 255, 25}, // green
{ 25, 25, 255} // red
};
KALEIDOSCOPE_INIT_PLUGINS(LEDControl, HeatmapEffect);
void setup() {
Kaleidoscope.setup ();
HeatmapEffect.heat_colors = heat_colors;
HeatmapEffect.heat_colors_length = 4;
}
```
This sets up the heatmap to update every second (by default). It also registers
a new LED effect, which means that if you have not set up any other effects,
then Heatmap will likely be the default. You may not want that, so setting up
at least one other LED effect, such as `LEDOff` is highly recommended.
## Plugin methods
The plugin provides a `HeatmapEffect` object, which has the following methods
and properties:
### `.activate()`
> When called, immediately activates the Heatmap effect. Mostly useful in the
> `setup()` method of the Sketch, or in macros that are meant to switch to the
> heatmap effect, no matter where we are in the list.
### `.update_delay`
> The number of milliseconds to wait between updating the heatmap. Updating the
> heatmap incurs a significant performance penalty, and should not be done too
> often. Doing it too rarely, on the other hand, make it much less useful. One
> has to strike a reasonable balance.
>
> Defaults to *1000*.
### `.heat_colors`
> A cRGB array describing the gradian of colors that will be used, from colder
> to hoter keys.
> E.g. with `heat_colors = {{100, 0, 0}, {0, 100, 0}, {0, 0, 100}}`, a key
> with a temperature of 0.8 (0=coldest, 1=hotest), will end up with a color
> `{0, 40, 60}`.
>
> Defaults to `{{0, 0, 0}, {25, 255, 25}, {25, 255, 255}, {25, 25, 255}}`
> (black, green, yellow, red)
### `.heat_colors_length`
> Length of the `heat_colors` array.
>
> Defaults to *4*
## Dependencies
* [Kaleidoscope-LEDControl](LEDControl.md)
## Further reading
Starting from the [example][plugin:example] is the recommended way of getting
started with the plugin.
[plugin:example]: ../../examples/Heatmap/Heatmap.ino

@ -0,0 +1,55 @@
/* -*- mode: c++ -*-
* Kaleidoscope-Heatmap -- Heatmap LED effect for Kaleidoscope.
* Copyright (C) 2016, 2017, 2018 Gergely Nagy
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Kaleidoscope.h>
#include <Kaleidoscope-LEDControl.h>
#include <Kaleidoscope-Heatmap.h>
// *INDENT-OFF*
const Key keymaps[][ROWS][COLS] PROGMEM = {
[0] = KEYMAP_STACKED
(Key_LEDEffectNext, Key_1, Key_2, Key_3, Key_4, Key_5, Key_LEDEffectNext,
Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab,
Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G,
Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape,
Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift,
Key_NoKey,
Key_skip, Key_6, Key_7, Key_8, Key_9, Key_0, Key_skip,
Key_Enter, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_Equals,
Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote,
Key_skip, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus,
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
Key_NoKey),
};
// *INDENT-ON*
KALEIDOSCOPE_INIT_PLUGINS(LEDControl,
HeatmapEffect);
void setup() {
Kaleidoscope.setup();
HeatmapEffect.activate();
}
void loop() {
Kaleidoscope.loop();
}

@ -0,0 +1,21 @@
/* -*- mode: c++ -*-
* Kaleidoscope-Heatmap -- Heatmap LED effect for Kaleidoscope.
* Copyright (C) 2016, 2017 Gergely Nagy
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <kaleidoscope/plugin/Heatmap.h>

@ -0,0 +1,205 @@
/* -*- mode: c++ -*-
* Kaleidoscope-Heatmap -- Heatmap LED effect for Kaleidoscope.
* Copyright (C) 2016, 2017 Gergely Nagy
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Kaleidoscope.h>
#include <Kaleidoscope-Heatmap.h>
namespace kaleidoscope {
namespace plugin {
// store the number of times each key has been strock
uint16_t Heatmap::heatmap_[ROWS][COLS];
// max of heatmap_ (we divide by it so we start at 1)
uint16_t Heatmap::highest_ = 1;
// next heatmap computation time
uint32_t Heatmap::next_heatmap_comp_time_ = 0;
// in the cRGB struct the order is blue, green, red (It should be called cBGR…)
// default heat_colors black green yellow red
static const cRGB heat_colors_default_[] PROGMEM = {{0, 0, 0}, {25, 255, 25}, {25, 255, 255}, {25, 25, 255}};
// colors from cold to hot
const cRGB *Heatmap::heat_colors = heat_colors_default_;
uint8_t Heatmap::heat_colors_length = 4;
// number of millisecond to wait between each heatmap computation
uint16_t Heatmap::update_delay = 1000;
cRGB Heatmap::computeColor(float v) {
// compute the color corresponding to a value between 0 and 1
/*
* for exemple, if:
* v=0.8
* heat_colors_lenth=4 (hcl)
* the red components of heat_colors are: 0, 25, 25, 255 (rhc)
* the red component returned by computeColor will be: 117
*
* 255 | /
* | /
* | /
* 117 | - - - - - - -/
* | /
* 25 | ______/ |
* | __/
* | _/ |
* |/_________________
* 0 1 2 3
* 2.4
* idx1 | idx2
* <>
* fb
*
* in this exemple, I call red heat_colors: rhc
* idx1 = floor(v×(hcl-1)) = floor(0.8×3) = floor(2.4) = 2
* idx2 = idx1 + 1 = 3
* fb = v×(hcl-1)-idx1 = 0.8×3 - 2 = 0.4
* red = (rhc[idx2]-rhc[idx1])×fb + rhc[idx1] = (255-25)×(2.4-2) + 25 = 117
*/
float fb = 0;
uint8_t idx1, idx2;
if (v <= 0) {
// if v = 0, don't bother computing fb and use heat_colors[0]
idx1 = idx2 = 0;
} else if (v >= 1) {
// if v = 1,
// don't bother computing fb and use heat_colors[heat_colors_length-1]
idx1 = idx2 = heat_colors_length - 1;
} else {
float val = v * (heat_colors_length - 1);
// static_cast from float to int just drop the decimal part of the number
// static_cast<int>(5.9) → 5
idx1 = static_cast<int>(val);
idx2 = idx1 + 1;
fb = val - static_cast<float>(idx1);
}
uint8_t r = static_cast<uint8_t>((pgm_read_byte(&(heat_colors[idx2].r)) - pgm_read_byte(&(heat_colors[idx1].r))) * fb + pgm_read_byte(&(heat_colors[idx1].r)));
uint8_t g = static_cast<uint8_t>((pgm_read_byte(&(heat_colors[idx2].g)) - pgm_read_byte(&(heat_colors[idx1].g))) * fb + pgm_read_byte(&(heat_colors[idx1].g)));
uint8_t b = static_cast<uint8_t>((pgm_read_byte(&(heat_colors[idx2].b)) - pgm_read_byte(&(heat_colors[idx1].b))) * fb + pgm_read_byte(&(heat_colors[idx1].b)));
return {b, g, r};
}
void Heatmap::shiftStats(void) {
// this method is called when:
// 1. a value in heatmap_ reach INT8_MAX
// 2. highest_ reach heat_colors_length*512 (see Heatmap::loopHook)
// we divide every heatmap element by 2
for (uint8_t r = 0; r < ROWS; r++) {
for (uint8_t c = 0; c < COLS; c++) {
heatmap_[r][c] = heatmap_[r][c] >> 1;
}
}
// and also divide highest_ accordingly
highest_ = highest_ >> 1;
}
void Heatmap::resetMap(void) {
// this method can be used as a way to work around an existing bug with a single key
// getting special attention or if the user just wants a button to reset the map
for (uint8_t r = 0; r < ROWS; r++) {
for (uint8_t c = 0; c < COLS; c++) {
heatmap_[r][c] = 0;
}
}
highest_ = 1;
}
EventHandlerResult Heatmap::onKeyswitchEvent(Key &mapped_key, byte row, byte col, uint8_t key_state) {
// this methode is called frequently by Kaleidoscope
// even if the module isn't activated
// if it is a synthetic key, skip it
if (key_state & INJECTED)
return EventHandlerResult::OK;
// if the key is not toggled on, skip it
if (!keyToggledOn(key_state))
return EventHandlerResult::OK;
// increment the heatmap_ value related to the key
heatmap_[row][col]++;
// check highest_
if (highest_ < heatmap_[row][col]) {
highest_ = heatmap_[row][col];
// if highest_ (and so heatmap_ value related to the key)
// is close to overflow: call shiftStats
// NOTE: this is barely impossible since shiftStats should be
// called much sooner by Heatmap::loopHook
if (highest_ == INT16_MAX)
shiftStats();
}
return EventHandlerResult::OK;
}
EventHandlerResult Heatmap::beforeEachCycle() {
// this methode is called frequently by Kaleidoscope
// even if the module isn't activated
// call shiftStats (divide every heatmap value by 2)
// if highest_ reach heat_colors_length*512.
// So after the shift, highest_ will be heat_colors_length*256. We
// didn't lose any precision in our heatmap since between each color we have a
// maximum precision of 256 ; said differently, there is 256 state (at max)
// between heat_colors[x] and heat_colors[x+1].
if (highest_ > (static_cast<uint16_t>(heat_colors_length) << 9))
shiftStats();
return EventHandlerResult::OK;
}
void Heatmap::update(void) {
// this methode is called frequently by the LEDControl::loopHook
// do nothing if we didn't reach next_heatmap_comp_time_ yet
if (next_heatmap_comp_time_ && (millis() < next_heatmap_comp_time_))
return;
// do the heatmap computing
// (we reach next_heatmap_comp_time_ or next_heatmap_comp_time_ was never scheduled)
// schedule the next heatmap computing
next_heatmap_comp_time_ = millis() + update_delay;
// for each key
for (uint8_t r = 0; r < ROWS; r++) {
for (uint8_t c = 0; c < COLS; c++) {
// how much the key was pressed compared to the others (between 0 and 1)
// (total_keys_ can't be equal to 0)
float v = static_cast<float>(heatmap_[r][c]) / highest_;
// we could have used an interger instead of a float, but then we would
// have had to change some multiplication in division.
// / on uint is slower than * on float, so I stay with the float
// https://forum.arduino.cc/index.php?topic=92684.msg2733723#msg2733723
// set the LED color accordingly
::LEDControl.setCrgbAt(r, c, computeColor(v));
}
}
}
}
}
kaleidoscope::plugin::Heatmap HeatmapEffect;

@ -0,0 +1,52 @@
/* -*- mode: c++ -*-
* Kaleidoscope-Heatmap -- Heatmap LED effect for Kaleidoscope.
* Copyright (C) 2016, 2017, 2018 Gergely Nagy
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <Kaleidoscope.h>
#include <Kaleidoscope-LEDControl.h>
namespace kaleidoscope {
namespace plugin {
class Heatmap : public LEDMode {
public:
Heatmap(void) {}
static uint16_t update_delay;
static const cRGB *heat_colors;
static uint8_t heat_colors_length;
void resetMap(void);
EventHandlerResult onKeyswitchEvent(Key &mapped_key, byte row, byte col, uint8_t key_state);
EventHandlerResult beforeEachCycle();
protected:
void update(void) final;
private:
static uint16_t heatmap_[ROWS][COLS];
static uint16_t highest_;
static uint32_t next_heatmap_comp_time_;
static void shiftStats(void);
static cRGB computeColor(float v);
};
}
}
extern kaleidoscope::plugin::Heatmap HeatmapEffect;
Loading…
Cancel
Save