cmdr-keyboard/firmware/src/cmdr_keyboard.cpp

324 lines
13 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 end of this file for license information
*/
#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" 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)
{
button_timestamp = current_timestamp + 1;
process_keys();
}
/* Update indicator 200ms */
if (current_timestamp >= indicator_timestamp)
{
indicator_timestamp = current_timestamp + 200;
/* 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();
}
}
/* @License
* =======================================================================================================
* -------------------------------------------------------------------------------------------------------
* ---####################-----###########-------###########-----############--############-############--
* --######################---#############-----#############---- -- - ---
* --###### ##---##### ###-----### #####---------##-------#######------#-------------
* -- -------------- --- ----- --- ----- ---------##-------#------------#-------------
* --#####--------------------#####------####-####------#####---------##-------###########--############--
* -- -------------------- ------ ------ --------- ------- -- --
* --#####--------------------#####--------#####--------#####---------------------------------------------
* -- -------------------- -------- -------- ---------------------------------------------
* --######--------------##---#####---------------------#####---------------------------------------------
* --##################### ---#####---------------------#####---------------------------------------------
* ---################### ----#####---------------------#####---------------------------------------------
* --- ----- --------------------- ---------------------------------------------
* -------------------------------------------------------------------------------------------------------
* =======================================================================================================
*
* Copyright 2022 Christoffer Martinsson <cm@cmtec.se>
*
* CMtec CMDR Keyboard 42 can be redistributed and/or modified under the terms of the GNU General
* Public License (Version 2), as published by the Free Software Foundation.
* A copy of the license can be found online at www.gnu.o urg/licenses.
*
* CMtec CMDR Keyboard 42 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.
*
* Keyboard/Mouse based on standard teensy "Keypad" library for button scanning, standard teensy
* "usb_keyboard" library for HID keyboard/mouse usb data communication.
*/