commit
d4907ebd56
@ -0,0 +1,65 @@
|
||||
# Kaleidoscope-EEPROM-Keymap
|
||||
|
||||
While keyboards usually ship with a keymap programmed in, to be able to change that keymap, without flashing new firmware, we need a way to place the keymap into a place we can update at run-time, and which persists across reboots. Fortunately, we have a bit of `EEPROM` on the keyboard, and can use it to store either the full keymap (and saving space in the firmware then), or store additional layers there.
|
||||
|
||||
In short, this plugin allows us to change our keymaps, without having to compile and flash new firmware. It does so through the use of the [FocusSerial][plugin:focusSerial] plugin.
|
||||
|
||||
[plugin:focusSerial]: FocusSerial.md
|
||||
|
||||
## Using the plugin
|
||||
|
||||
Using the plugin is reasonably simple: after including the header, enable the plugin, and configure how many layers at most we want to store in `EEPROM`. There are other settings one can tweak, but these two steps are enough to get started with.
|
||||
|
||||
Once these are set up, we can update the keymap via [Focus][plugin:focusSerial].
|
||||
|
||||
```c++
|
||||
#include <Kaleidoscope.h>
|
||||
#include <Kaleidoscope-EEPROM-Keymap.h>
|
||||
#include <Kaleidoscope-FocusSerial.h>
|
||||
|
||||
KALEIDOSCOPE_INIT_PLUGINS(EEPROMKeymap,
|
||||
Focus);
|
||||
|
||||
void setup() {
|
||||
Kaleidoscope.setup();
|
||||
|
||||
EEPROMKeymap.setup(1);
|
||||
}
|
||||
```
|
||||
|
||||
## Plugin methods
|
||||
|
||||
The plugin provides the `EEPROMKeymap` object, which has the following method:
|
||||
|
||||
### `.setup(layers[, mode])`
|
||||
|
||||
> Reserve space in EEPROM for up to `layers` layers, and set things up to work according to the specified `mode` (see below for a list of supported modes). To be called from the `setup` method of one's sketch.
|
||||
>
|
||||
> Supported modes are:
|
||||
> - `EEPROMKeymap.Mode::EXTEND`: Extend the keymap with layers from EEPROM, treating them as extensions of the main keymap embedded in the firmware. The first layer in EEPROM will have a number one higher than the last layer in PROGMEM. In this case, the total number of layers will be the number of them in PROGMEM plus `layers`.
|
||||
> - `EEPROMKeymap.Mode::CUSTOM`: For advanced use cases where the `EXTEND` mode is not appropriate. In this case, the plugin merely reserves a slice of EEPROM for the requested amount of layers, but does no other configuration - that's entirely up to the Sketch.
|
||||
|
||||
## Focus commands
|
||||
|
||||
The plugin provides the `keymap.map` and a `keymap.roLayers` commands.
|
||||
|
||||
### `keymap.map [codes...]`
|
||||
|
||||
> Without arguments, displays the keymap currently in effect. Each key is printed as its raw, 16-bit keycode.
|
||||
>
|
||||
> With arguments, it stores as many keys as given. One does not need to set all keys, on all layers: the command will start from the first key on the first layer, and go on as long as it has input. It will not go past the total amount of layers (that is, `layer_count`).
|
||||
|
||||
### `keymap.roLayers`
|
||||
|
||||
> Returns the number of read-only layers. This only makes sense for the `EEPROMKeymap.Mode::EXTEND` mode, where it returns the number of layers in PROGMEM. In any other case, it doesn't return anything, doing so is left for another event handler that understands what the correct value would be.
|
||||
|
||||
## Dependencies
|
||||
|
||||
* [Kaleidoscope-EEPROM-Settings](EEPROM-Settings.md)
|
||||
* [Kaleidoscope-FocusSerial](FocusSerial.md)
|
||||
|
||||
## Further reading
|
||||
|
||||
Starting from the [example][plugin:example] is the recommended way of getting started with the plugin.
|
||||
|
||||
[plugin:example]: ../../examples/EEPROM-Keymap/EEPROM-Keymap.ino
|
@ -0,0 +1,53 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* Kaleidoscope-EEPROM-Keymap -- EEPROM-based keymap support.
|
||||
* Copyright (C) 2017, 2018 Keyboard.io, Inc
|
||||
*
|
||||
* 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, version 3.
|
||||
*
|
||||
* 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-EEPROM-Keymap.h>
|
||||
#include <Kaleidoscope-FocusSerial.h>
|
||||
|
||||
// *INDENT-OFF*
|
||||
KEYMAPS(
|
||||
[0] = KEYMAP_STACKED
|
||||
(Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey,
|
||||
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_skip,
|
||||
|
||||
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_skip),
|
||||
)
|
||||
// *INDENT-ON*
|
||||
|
||||
KALEIDOSCOPE_INIT_PLUGINS(EEPROMKeymap, Focus);
|
||||
|
||||
void setup() {
|
||||
Kaleidoscope.setup();
|
||||
|
||||
EEPROMKeymap.setup(1, EEPROMKeymap.Mode::EXTEND);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Kaleidoscope.loop();
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* Kaleidoscope-EEPROM-Keymap -- EEPROM-based keymap support.
|
||||
* Copyright (C) 2017 Keyboard.io, Inc
|
||||
*
|
||||
* 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, version 3.
|
||||
*
|
||||
* 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/EEPROM-Keymap.h>
|
@ -0,0 +1,155 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* Kaleidoscope-EEPROM-Keymap -- EEPROM-based keymap support.
|
||||
* Copyright (C) 2017, 2018 Keyboard.io, Inc
|
||||
*
|
||||
* 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, version 3.
|
||||
*
|
||||
* 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-EEPROM-Settings.h>
|
||||
#include <Kaleidoscope-EEPROM-Keymap.h>
|
||||
#include <Kaleidoscope-FocusSerial.h>
|
||||
|
||||
namespace kaleidoscope {
|
||||
namespace plugin {
|
||||
EEPROMKeymap::Mode EEPROMKeymap::mode_;
|
||||
uint16_t EEPROMKeymap::keymap_base_;
|
||||
uint8_t EEPROMKeymap::max_layers_;
|
||||
uint8_t EEPROMKeymap::progmem_layers_;
|
||||
|
||||
EventHandlerResult EEPROMKeymap::onSetup() {
|
||||
::EEPROMSettings.onSetup();
|
||||
progmem_layers_ = layer_count;
|
||||
return EventHandlerResult::OK;
|
||||
}
|
||||
|
||||
void EEPROMKeymap::setup(uint8_t max, Mode mode) {
|
||||
switch (mode) {
|
||||
case Mode::CUSTOM:
|
||||
break;
|
||||
case Mode::EXTEND:
|
||||
layer_count = progmem_layers_ + max;
|
||||
Layer.getKey = getKeyExtended;
|
||||
break;
|
||||
}
|
||||
mode_ = mode;
|
||||
max_layers(max);
|
||||
}
|
||||
|
||||
void EEPROMKeymap::max_layers(uint8_t max) {
|
||||
max_layers_ = max;
|
||||
keymap_base_ = ::EEPROMSettings.requestSlice(max_layers_ * ROWS * COLS * 2);
|
||||
}
|
||||
|
||||
Key EEPROMKeymap::getKey(uint8_t layer, byte row, byte col) {
|
||||
Key key;
|
||||
|
||||
if (layer >= max_layers_)
|
||||
return Key_NoKey;
|
||||
|
||||
uint16_t pos = ((layer * ROWS * COLS) + (row * COLS) + col) * 2;
|
||||
|
||||
key.flags = EEPROM.read(keymap_base_ + pos);
|
||||
key.keyCode = EEPROM.read(keymap_base_ + pos + 1);
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
Key EEPROMKeymap::getKeyExtended(uint8_t layer, byte row, byte col) {
|
||||
Key key;
|
||||
|
||||
// If the layer is within PROGMEM bounds, look it up from there
|
||||
if (layer < progmem_layers_) {
|
||||
return Layer.getKeyFromPROGMEM(layer, row, col);
|
||||
}
|
||||
|
||||
// If the layer is outside of PROGMEM, look up from EEPROM
|
||||
return getKey(layer - progmem_layers_, row, col);
|
||||
}
|
||||
|
||||
uint16_t EEPROMKeymap::keymap_base(void) {
|
||||
return keymap_base_;
|
||||
}
|
||||
|
||||
void EEPROMKeymap::updateKey(uint16_t base_pos, Key key) {
|
||||
EEPROM.update(keymap_base_ + base_pos * 2, key.flags);
|
||||
EEPROM.update(keymap_base_ + base_pos * 2 + 1, key.keyCode);
|
||||
}
|
||||
|
||||
Key EEPROMKeymap::parseKey(void) {
|
||||
Key key;
|
||||
|
||||
key.raw = Serial.parseInt();
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
void EEPROMKeymap::printKey(Key k) {
|
||||
::Focus.printNumber(k.raw);
|
||||
}
|
||||
|
||||
EventHandlerResult EEPROMKeymap::onFocusEvent(const char *command) {
|
||||
const char *cmd = PSTR("keymap.map");
|
||||
if (::Focus.handleHelp(command, PSTR("keymap.map\nkeymap.roLayers")))
|
||||
return EventHandlerResult::OK;
|
||||
|
||||
if (strncmp_P(command, PSTR("keymap."), 7) != 0)
|
||||
return EventHandlerResult::OK;
|
||||
|
||||
if (strcmp_P(command + 7, PSTR("roLayers")) == 0) {
|
||||
if (mode_ != Mode::EXTEND)
|
||||
return EventHandlerResult::OK;
|
||||
Serial.println(progmem_layers_);
|
||||
return EventHandlerResult::EVENT_CONSUMED;
|
||||
}
|
||||
|
||||
if (strcmp_P(command + 7, PSTR("map")) != 0)
|
||||
return EventHandlerResult::OK;
|
||||
|
||||
if (Serial.peek() == '\n') {
|
||||
for (uint8_t layer = 0; layer < layer_count; layer++) {
|
||||
for (uint8_t row = 0; row < ROWS; row++) {
|
||||
for (uint8_t col = 0; col < COLS; col++) {
|
||||
Key k = Layer.getKey(layer, row, col);
|
||||
|
||||
printKey(k);
|
||||
::Focus.printSpace();
|
||||
}
|
||||
}
|
||||
}
|
||||
Serial.println();
|
||||
} else {
|
||||
uint16_t i = 0;
|
||||
uint8_t layers = layer_count;
|
||||
if (layers > 0)
|
||||
layers--;
|
||||
while ((Serial.peek() != '\n') && (i < ROWS * COLS * layers)) {
|
||||
Key k = parseKey();
|
||||
|
||||
if (mode_ == Mode::EXTEND) {
|
||||
uint8_t layer = i / (ROWS * COLS);
|
||||
if (layer >= progmem_layers_)
|
||||
updateKey(i - (progmem_layers_ * ROWS * COLS), k);
|
||||
} else {
|
||||
updateKey(i, k);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return EventHandlerResult::EVENT_CONSUMED;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
kaleidoscope::plugin::EEPROMKeymap EEPROMKeymap;
|
@ -0,0 +1,60 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* Kaleidoscope-EEPROM-Keymap -- EEPROM-based keymap support.
|
||||
* Copyright (C) 2017, 2018 Keyboard.io, Inc
|
||||
*
|
||||
* 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, version 3.
|
||||
*
|
||||
* 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-EEPROM-Settings.h>
|
||||
|
||||
namespace kaleidoscope {
|
||||
namespace plugin {
|
||||
class EEPROMKeymap : public kaleidoscope::Plugin {
|
||||
public:
|
||||
enum class Mode {
|
||||
CUSTOM,
|
||||
EXTEND,
|
||||
};
|
||||
|
||||
EEPROMKeymap(void) {}
|
||||
|
||||
EventHandlerResult onSetup();
|
||||
EventHandlerResult onFocusEvent(const char *command);
|
||||
|
||||
static void setup(uint8_t max, Mode mode = Mode::EXTEND);
|
||||
|
||||
static void max_layers(uint8_t max);
|
||||
|
||||
static uint16_t keymap_base(void);
|
||||
|
||||
static Key getKey(uint8_t layer, byte row, byte col);
|
||||
static Key getKeyExtended(uint8_t layer, byte row, byte col);
|
||||
|
||||
static void updateKey(uint16_t base_pos, Key key);
|
||||
|
||||
private:
|
||||
static uint16_t keymap_base_;
|
||||
static uint8_t max_layers_;
|
||||
static uint8_t progmem_layers_;
|
||||
static Mode mode_;
|
||||
|
||||
static Key parseKey(void);
|
||||
static void printKey(Key key);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
extern kaleidoscope::plugin::EEPROMKeymap EEPROMKeymap;
|
Loading…
Reference in new issue