/* * 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 #include 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) { 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 * * 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. */