cmdr-keyboard/firmware/src/cmdr_keyboard.cpp

294 lines
11 KiB
C++
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Project: CMtec CMDR Keyboard 42
* Date: 2023-09-02
* Author: Christoffer Martinsson
* Email: cm@cmtec.se
* License: Please refer to LICENSE in root directory
*/
#include "IndicatorLed.h"
#include <Arduino.h>
#include <Keypad.h>
const uint8_t USB_LED_CAPS_LOCK = 1; // Caps lock LED definition from HID spec
const uint16_t KEY_OFFSET = 0xAA00; // Offset to apply for not interfere with already defined keyboard keys
const uint16_t KEY_FN = 1 + KEY_OFFSET; // Function layer button reference
const uint16_t KEY_GUI_LATCH = 2 + KEY_OFFSET; // GUI latching (ex: Win) button reference
const uint8_t NBR_OF_BUTTONS = 42; // Number of buttons used (42 in this case)
IndicatorLed status_led = IndicatorLed(13);
uint64_t current_timestamp = 0;
uint64_t button_timestamp = 0;
uint64_t indicator_timestamp = 0;
bool key_pressed = false;
bool win_latched = false;
const byte KEYBOARD_MATRIX_ROWS = 4;
const byte KEYBOARD_MATRIX_COLS = 12;
byte keyboard_martix_row_pins[KEYBOARD_MATRIX_ROWS] = {0, 1, 2, 3};
byte keyboard_matrix_col_pins[KEYBOARD_MATRIX_COLS] = {9, 8, 7, 6, 5, 4, 19, 18, 17, 16, 15, 14};
const uint8_t MAX_SIMULTANIOUS_KEYS = LIST_MAX;
const char keyboard_matrix_id[KEYBOARD_MATRIX_ROWS][KEYBOARD_MATRIX_COLS] = {
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12},
{13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24},
{25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36},
{0, 0, 0, 37, 38, 39, 40, 41, 42, 0, 0, 0},
};
Keypad keyboard_matrix =
Keypad(makeKeymap(keyboard_matrix_id), keyboard_martix_row_pins, keyboard_matrix_col_pins, KEYBOARD_MATRIX_ROWS, KEYBOARD_MATRIX_COLS);
struct Button
{
uint16_t keycode = NO_KEY;
uint16_t fn1_keycode = NO_KEY;
uint16_t fn2_keycode = NO_KEY;
uint8_t kstate = IDLE;
uint16_t last_keycode = NO_KEY;
bool run_keycode = false;
};
/*
* "KeyId" (keyboard matrix id) corresponding with the physical design of the actual keyboard.
------------------------------------- -------------------------------------
| 1 | 2 | 3 | 4 | 5 | 6 | | 7 | 8 | 9 | 1O | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | | 31 | 32 | 33 | 34 | 35 | 36 |
------------------| 37 | 38 | 39 | | 40 | 41 | 42 |------------------
------------------- -------------------
* "Fn0 key" is the layer 0 key to use.
* "Fn1 key" is the layer 1 key to use. Don NOT add KEY_FN1 or KEY_FN2 to this layer.
* "Fn2 key" is the layer 2 key to use. Don NOT add KEY_FN1 or KEY_FN2 to this layer.
Swedish keymap (ISO) special characters (not matching the key definition):
* KEY_TILDE = §
* KEY_SEMICOLON = ö
* KEY_QUOTE = ä
* KEY_LEFT_BRACE = å
* KEY_SLASH = -
* KEY_NON_US_BS = <
* KEY_EQUAL = ´
* KEY_BACKSLASH = '
* KEY_RIGHT_BRACE = ^
* KEY_MINUS = +
* KEY_LEFT_ALT = Alt
* KEY_RIGHT_ALT = AltGr
*/
/* Keymap config ------------------------------------------------------------------------- */
// clang-format off
Button buttons[NBR_OF_BUTTONS] =
{
/* KeyId Fn0 key Fn1 key Fn2 key - Do not change! - */
/* 1 */ {KEY_TAB, KEY_ESC, KEY_F11, IDLE, NO_KEY, false},
/* 2 */ {KEY_Q, KEY_F1, KEY_F12, IDLE, NO_KEY, false},
/* 3 */ {KEY_W, KEY_F2, KEY_F13, IDLE, NO_KEY, false},
/* 4 */ {KEY_E, KEY_F3, KEY_F14, IDLE, NO_KEY, false},
/* 5 */ {KEY_R, KEY_F4, KEY_F15, IDLE, NO_KEY, false},
/* 6 */ {KEY_T, KEY_F5, KEY_F16, IDLE, NO_KEY, false},
/* 7 */ {KEY_Y, KEY_F6, KEY_TILDE, IDLE, NO_KEY, false},
/* 8 */ {KEY_U, KEY_F7, KEY_GUI_LATCH, IDLE, NO_KEY, false},
/* 9 */ {KEY_I, KEY_F8, KEY_LEFT_GUI, IDLE, NO_KEY, false},
/* 10 */ {KEY_O, KEY_F9, NO_KEY, IDLE, NO_KEY, false},
/* 11 */ {KEY_P, KEY_F10, KEY_CAPS_LOCK, IDLE, NO_KEY, false},
/* 12 */ {KEY_LEFT_BRACE, KEY_BACKSPACE, KEY_BACKSPACE, IDLE, NO_KEY, false},
/* 13 */ {KEY_LEFT_CTRL, KEY_LEFT_CTRL, KEY_LEFT_CTRL, IDLE, NO_KEY, false},
/* 14 */ {KEY_A, KEY_1, KEY_MEDIA_PLAY_PAUSE, IDLE, NO_KEY, false},
/* 15 */ {KEY_S, KEY_2, KEY_MEDIA_NEXT_TRACK, IDLE, NO_KEY, false},
/* 16 */ {KEY_D, KEY_3, KEY_F17, IDLE, NO_KEY, false},
/* 17 */ {KEY_F, KEY_4, KEY_F18, IDLE, NO_KEY, false},
/* 18 */ {KEY_G, KEY_5, KEY_F19, IDLE, NO_KEY, false},
/* 19 */ {KEY_H, KEY_6, KEY_LEFT_ARROW, IDLE, NO_KEY, false},
/* 20 */ {KEY_J, KEY_7, KEY_DOWN_ARROW, IDLE, NO_KEY, false},
/* 21 */ {KEY_K, KEY_8, KEY_UP_ARROW, IDLE, NO_KEY, false},
/* 22 */ {KEY_L, KEY_9, KEY_RIGHT_ARROW, IDLE, NO_KEY, false},
/* 23 */ {KEY_SEMICOLON, KEY_0, KEY_DELETE, IDLE, NO_KEY, false},
/* 24 */ {KEY_QUOTE, KEY_ENTER, KEY_ENTER, IDLE, NO_KEY, false},
/* 25 */ {KEY_LEFT_SHIFT, KEY_LEFT_SHIFT, KEY_LEFT_SHIFT, IDLE, NO_KEY, false},
/* 26 */ {KEY_Z, KEY_6, KEY_F20, IDLE, NO_KEY, false},
/* 27 */ {KEY_X, KEY_7, KEY_F21, IDLE, NO_KEY, false},
/* 28 */ {KEY_C, KEY_8, KEY_F22, IDLE, NO_KEY, false},
/* 29 */ {KEY_V, KEY_9, KEY_F23, IDLE, NO_KEY, false},
/* 30 */ {KEY_B, KEY_0, KEY_F24, IDLE, NO_KEY, false},
/* 31 */ {KEY_N, KEY_NON_US_BS, KEY_HOME, IDLE, NO_KEY, false},
/* 32 */ {KEY_M, KEY_EQUAL, KEY_PAGE_DOWN, IDLE, NO_KEY, false},
/* 33 */ {KEY_COMMA, KEY_BACKSLASH, KEY_PAGE_UP, IDLE, NO_KEY, false},
/* 34 */ {KEY_PERIOD, KEY_RIGHT_BRACE, KEY_END, IDLE, NO_KEY, false},
/* 35 */ {KEY_SLASH, KEY_MINUS, KEY_INSERT, IDLE, NO_KEY, false},
/* 36 */ {KEY_RIGHT_SHIFT, KEY_RIGHT_SHIFT, KEY_RIGHT_SHIFT, IDLE, NO_KEY, false},
/* 37 */ {KEY_LEFT_ALT, KEY_LEFT_ALT, KEY_LEFT_ALT, IDLE, NO_KEY, false},
/* 38 */ {KEY_FN, NO_KEY, NO_KEY, IDLE, NO_KEY, false},
/* 39 */ {KEY_SPACE, KEY_BACKSPACE, KEY_LEFT_GUI, IDLE, NO_KEY, false},
/* 40 */ {KEY_SPACE, KEY_BACKSPACE, KEY_BACKSPACE, IDLE, NO_KEY, false},
/* 41 */ {KEY_FN, NO_KEY, NO_KEY, IDLE, NO_KEY, false},
/* 42 */ {KEY_FN, KEY_RIGHT_ALT, NO_KEY, IDLE, NO_KEY, false}};
// clang-format on
/* End of keymap config ------------------------------------------------------------------ */
/**
set_key.
@param keycode code to apply action.
@param kstate PRESSED or RELEASED.
*/
void set_key(uint16_t keycode, uint8_t kstate)
{
if (keycode == NO_KEY || keycode == KEY_FN) return;
if (keycode == KEY_GUI_LATCH)
{
win_latched = true;
return;
}
if (kstate == RELEASED)
{
if (win_latched == true)
{
Keyboard.release(keycode);
Keyboard.release(KEY_LEFT_GUI);
win_latched = false;
}
else
{
Keyboard.release(keycode);
}
}
else if (kstate == PRESSED)
{
if (win_latched == true)
{
Keyboard.press(KEY_LEFT_GUI);
Keyboard.press(keycode);
}
else
{
Keyboard.press(keycode);
}
}
}
/**
* process_keypad
*
*/
void process_keys()
{
/* Scan keypad, exit if not ready */
if (keyboard_matrix.getKeys() == false) return;
int fn_mode = 0;
int corner_pressed = 0;
for (int i = 0; i < MAX_SIMULTANIOUS_KEYS; i++)
{
/* Update button table if key pressed/releaseed */
if ((keyboard_matrix.key[i].kstate == PRESSED || keyboard_matrix.key[i].kstate == RELEASED) &&
(keyboard_matrix.key[i].stateChanged == true))
{
buttons[keyboard_matrix.key[i].kchar - 1].run_keycode = true;
buttons[keyboard_matrix.key[i].kchar - 1].kstate = keyboard_matrix.key[i].kstate;
}
/* Skip if key is not pressed */
if (keyboard_matrix.key[i].kstate == IDLE || keyboard_matrix.key[i].kstate == RELEASED) continue;
/* Count number of corner keys pressed */
if (keyboard_matrix.key[i].kchar == 1) corner_pressed++; // Upper left
if (keyboard_matrix.key[i].kchar == 25) corner_pressed++; // Upper right
if (keyboard_matrix.key[i].kchar == 12) corner_pressed++; // Lower left
if (keyboard_matrix.key[i].kchar == 36) corner_pressed++; // Lower right
/* Count number of FN keys pressed */
if (buttons[keyboard_matrix.key[i].kchar - 1].keycode == KEY_FN) fn_mode++;
}
if (corner_pressed == 4)
{
Keyboard.releaseAll();
status_led.on();
status_led.update();
delay(200); // Wait for usb to settle before rebooting
_reboot_Teensyduino_();
}
for (int i = 0; i < NBR_OF_BUTTONS; i++)
{
/* Check if key is ready to be set. Else skip to next button */
if (buttons[i].run_keycode == false) continue;
buttons[i].run_keycode = false;
if (buttons[i].kstate == RELEASED)
{
set_key(buttons[i].last_keycode, RELEASED);
continue;
}
if (fn_mode == 0)
{
set_key(buttons[i].keycode, PRESSED);
buttons[i].last_keycode = buttons[i].keycode;
}
else if (fn_mode == 1)
{
set_key(buttons[i].fn1_keycode, PRESSED);
buttons[i].last_keycode = buttons[i].fn1_keycode;
}
else if (fn_mode == 2)
{
set_key(buttons[i].fn2_keycode, PRESSED);
buttons[i].last_keycode = buttons[i].fn2_keycode;
}
}
}
void setup()
{
/* Init HW */
status_led.begin();
}
void loop()
{
/* Update current time (ms) */
current_timestamp = millis();
/* Scan buttons 1ms */
if (current_timestamp >= button_timestamp)
{
process_keys();
button_timestamp = current_timestamp + 1;
}
/* Update indicator 200ms */
if (current_timestamp >= indicator_timestamp)
{
/* Set status indication */
if (keyboard_leds & (1 << USB_LED_CAPS_LOCK))
{
status_led.blink();
}
else if (win_latched)
{
status_led.on();
}
else
{
status_led.off();
}
status_led.update();
indicator_timestamp = current_timestamp + 200;
}
}