294 lines
11 KiB
C++
Executable File
294 lines
11 KiB
C++
Executable File
/*
|
||
* 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;
|
||
}
|
||
}
|