/* @file KeyboardDriver.ino || @version 1.0 || @author Valdis Vitolins || @description Custom keyboard driver using Teensy 3.1 controller || Used code snippets: Mark's Stanley's MultiKey.ino for Matrix controller and || PJRC's Teensy USB Keyboard howto on https://www.pjrc.com/teensy/td_keyboard.html || To compile project set: || 1. Tools — Card — Teensy 3.2/3.1 || 2. Tools — USB type — Keyboard + Mouse + Joystick (or Serial + Keyboard + Mouse + Joystick to debug with Serial.println) || 3. Tools — Keyboard Layout: US English || 3. Then select Tools — Port and proper Teensy port */ #include const byte ROWS = 7; // number of rows const byte COLS = 19; //number of columns const int DebounceTime = 20; // note, that debounce time for Cherry MX switches is ~ 5ms byte rowPins[ROWS] = {2, 3, 4, 5, 6, 7, 8}; //connect to the row pinouts of the keypad // note that IO 28 is used instead of IO 17!!! byte colPins[COLS] = {33, 32, 31, 30, 29, 23, 22, 21, 20, 19, 18, 28, 16, 15, 14, 12, 11, 10, 9}; //connect to the column pinouts of the keypad // Values for keys // in form: key code from .../hardware/teensy/avr/cores/teensy3/keylayouts.h const char _a = 4; const char _b = 5; const char _c = 6; const char _d = 7; const char _e = 8; const char _f = 9; const char _g = 10; const char _h = 11; const char _i = 12; const char _j = 13; const char _k = 14; const char _l = 15; const char _m = 16; const char _n = 17; const char _o = 18; const char _p = 19; const char _q = 20; const char _r = 21; const char _s = 22; const char _t = 23; const char _u = 24; const char _v = 25; const char _w = 26; const char _x = 27; const char _y = 28; const char _z = 29; const char _1 = 30; const char _2 = 31; const char _3 = 32; const char _4 = 33; const char _5 = 34; const char _6 = 35; const char _7 = 36; const char _8 = 37; const char _9 = 38; const char _0 = 39; const char Ent = 40; const char Esc = 41; const char Back = 42; const char Tab = 43; const char Space = 44; const char Minus = 45; const char Equal = 46; const char Lbrace = 47; const char Rbrace = 48; const char Bslash = 49; const char Num = 50; const char Scolon = 51; const char Quote = 52; const char Tilde = 53; const char Comma = 54; const char Period = 55; const char Slash = 56; const char Caps = 57; const char F1 = 58; const char F2 = 59; const char F3 = 60; const char F4 = 61; const char F5 = 62; const char F6 = 63; const char F7 = 64; const char F8 = 65; const char F9 = 66; const char F10 = 67; const char F11 = 68; const char F12 = 69; const char Prnt = 70; const char Scrol = 71; const char Pause = 72; const char Ins = 73; const char Home = 74; const char PgUp = 75; const char Del = 76; const char End = 77; const char PgDown = 78; const char Right = 79; const char Left = 80; const char Down = 81; const char Up = 82; const char Numl = 83; const char KPdiv = 84; const char KPmul = 85; const char KPsub = 86; const char KPadd = 87; const char KPent = 88; const char KP1 = 89; const char KP2 = 90; const char KP3 = 91; const char KP4 = 92; const char KP5 = 93; const char KP6 = 94; const char KP7 = 95; const char KP8 = 96; const char KP9 = 97; const char KP0 = 98; const char KPdot = 99; const char K102 = 100; const char Menu = 101; // + 110 shift const char Lctrl = 110; const char Lshift = 111; const char Lalt = 112; const char Lwin = 113; // + 114 shift const char Rctrl = 114; const char Rshift = 115; const char Ralt = 116; const char Rwin = 117; const char S0 = 240; const char S1 = 241; const char S2 = 242; const char S3 = 243; const char S4 = 244; const char S5 = 245; const char S6 = 246; const char S7 = 247; // Character symbols for each cell of an array char keys[ROWS][COLS] = { { F1, F2, F3, F4, F5, F6, Esc, Pause, Scrol, KP7, KP8, KP9, Numl, F7 , F8 , F9 , F10, F11, F12 }, {Equal, _1, _2, _3, _4, _5, Prnt, Tab , KPdiv, KP4, KP5, KP6, KPsub, _6, _7, _8, _9, _0, Minus }, { Tab, _q, _w, _e, _r, _t, Menu, S4 , KPmul, KP1, KP2, KP3, KPadd, _y, _u, _i, _o, _p, Bslash}, { Caps, _a, _s, _d, _f, _g, S0, S1 , S2 , KP0, KPdot, KPent, Back, _h, _j, _k, _l, Scolon, Quote }, {Lshift, _z, _x, _c, _v, _b, Lctrl, Lalt, S6 , 0 , Rwin, Ralt, Rctrl, _n, _m,Comma,Period, Slash, Rshift}, { 0 , K102,Tilde, Left, Right, 0 , Space, S7 , Home , 0 , PgUp, Del, Ent , 0 , Up, Down,Lbrace,Rbrace, 0 }, { 0 , 0 , 0 , 0 , 0 , 0 , 0 , Ins , End , 0 , PgDown, Back, 0 , 0 , 0 , 0 , 0 , 0 , 0 } }; // Which outputs are connected to LEDs int leds [] = {13, 24, 25, 26, 27}; // Variables to hold CapsLock, NumLock and ScrollLock settings boolean capsLock = false, numLock = false, scrollLock = false; // Triggers to toggle ..Lock keys only on first press and second release of button boolean switchCaps = true, switchNum = true, switchScroll = true; //initialize an instance of class NewKeypad Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS); // Key code int ccode; // Performance testing unsigned long loopCount = 0; unsigned long startTime = millis(); void setup() { Serial.begin(9600); kpd.setDebounceTime(DebounceTime); testLEDs(); // Flash all LEDs on startup //String msg = "Debounce time "; //msg.concat(DebounceTime); //Serial.println(msg); } // Flash all LEDs in wave pattern void testLEDs() { delay(100); for (int i = 4; i >= 0; i--) { // In descending order because LEDs are arranged starting from larger port no pinMode(leds[i], OUTPUT); digitalWrite(leds[i], HIGH); delay(50); } delay(100); for (int i = 4; i >= 0; i--) { digitalWrite(leds[i], LOW); delay(50); } } // As Keypad matrix can hold only char, but code for advanced Keyboard keys are int, // here is mapping between keymap char matrix and keyboard int codes // Look at .../hardware/teensy/avr/cores/teensy3/keylayouts.h for key codes int keycode(char kchar) { // Serial.println(kchar, DEC); // a (for querty layout) should produce 0x61 // enter should produce 0xF028 if(kchar >= 4 && kchar <= 101) return kchar + 0xF000; // Control keys 0xE001, ..02, ..04 and ..08 // left ctrl should produce 0xE001 // rigth ctrl should produce 0xE010 if (kchar >= 110 && kchar <= 117) return (1 << (kchar - 110)) + 0xE000; return 0; // see also: http://www.usb.org/developers/hidpage/Hut1_12v2.pdf // note that these codes differs from "evtest /dev/input/eventX" command // which again differs from codes shown with "xev" command } // Toggle ...Lock diodes when ...Lock key is pressed void toggleLocks(int ccode, int state) { switch (ccode) { case KEY_ESC: // Should be KEY_CAPS_LOCK, but for me CapsLock is swapped with Esc key switchCapsLock(state); break; case KEY_NUM_LOCK: switchNumLock(state); break; case KEY_SCROLL_LOCK: switchScrollLock(state); break; } } // Toggle CapsLock light void switchCapsLock(int state) { if (state == PRESSED) { if (switchCaps && !capsLock) { capsLock = true; digitalWrite(24, HIGH); //Serial.println("Caps on"); } switchCaps = !switchCaps; } if (state == RELEASED) { // released if (switchCaps && capsLock) { capsLock = false; digitalWrite(24, LOW); //Serial.println("Caps off"); } } } // Toggle NumLock light void switchNumLock(int state) { if (state == PRESSED) { if (switchNum && !numLock) { numLock = true; digitalWrite(25, HIGH); //Serial.println("NumLock on"); } switchNum = !switchNum; } if (state == RELEASED) { // released if (switchNum && numLock) { numLock = false; digitalWrite(25, LOW); //Serial.println("NumLock off"); } } } // Toggle ScrollLock light void switchScrollLock(int state) { if (state == PRESSED) { if (switchScroll && !scrollLock) { scrollLock = true; digitalWrite(26, HIGH); //Serial.println("ScrollLock on"); } switchScroll = !switchScroll; } if (state == RELEASED) { // released if (switchScroll && scrollLock) { scrollLock = false; digitalWrite(26, LOW); //Serial.println("ScrollLock off"); } } } void processKey(unsigned char ckey, int cstate) { String status = ""; ccode = keycode(ckey); switch (cstate) { // Report active key state : 0:IDLE, 1:PRESSED, 2:HOLD, or 3:RELEASED case PRESSED: //digitalWrite(13, HIGH); status = " PRESSED."; if (ckey == S0) { // "+y to copy in VIM // use QWERTY keyboard under Modern layout (e.g. KEY_M for y) Keyboard.press(MODIFIERKEY_LEFT_SHIFT); Keyboard.press(KEY_H); Keyboard.release(KEY_H); Keyboard.press(KEY_EQUAL); Keyboard.release(KEY_EQUAL); Keyboard.release(MODIFIERKEY_LEFT_SHIFT); Keyboard.press(KEY_M); Keyboard.release(KEY_M); } else if (ckey == S1) { // "+gP to paste in VIM normal mode Keyboard.press(MODIFIERKEY_LEFT_SHIFT); Keyboard.press(KEY_H); Keyboard.release(KEY_H); Keyboard.press(KEY_EQUAL); Keyboard.release(KEY_EQUAL); Keyboard.release(MODIFIERKEY_LEFT_SHIFT); Keyboard.press(KEY_P); Keyboard.release(KEY_P); Keyboard.press(MODIFIERKEY_LEFT_SHIFT); Keyboard.press(KEY_R); Keyboard.release(KEY_R); Keyboard.release(MODIFIERKEY_LEFT_SHIFT); } else if (ckey == S2) { // Ctrl+r + to paste in VIM insert mode Keyboard.press(MODIFIERKEY_LEFT_CTRL); Keyboard.press(KEY_SEMICOLON); Keyboard.release(KEY_SEMICOLON); Keyboard.release(MODIFIERKEY_LEFT_CTRL); Keyboard.press(MODIFIERKEY_LEFT_SHIFT); Keyboard.press(KEY_EQUAL); Keyboard.release(KEY_EQUAL); Keyboard.release(MODIFIERKEY_LEFT_SHIFT); } else if (ckey == S4) { Keyboard.press(MODIFIERKEY_RIGHT_ALT); } else if (ckey == S6) { // Ctrl+Alt Keyboard.press(MODIFIERKEY_LEFT_CTRL); Keyboard.press(MODIFIERKEY_LEFT_ALT); } else if (ckey == S7) { // Shift+Insert Keyboard.press(MODIFIERKEY_LEFT_SHIFT); Keyboard.press(KEY_INSERT); Keyboard.release(KEY_INSERT); Keyboard.release(MODIFIERKEY_LEFT_SHIFT); } else { toggleLocks(ccode, PRESSED); Keyboard.press(ccode); } break; case HOLD: status = " HOLD."; break; case RELEASED: status = " RELEASED."; //digitalWrite(13, LOW); if (ckey == S0 || ckey == S1 || ckey == S2 || ckey == S7) { // are "released" in PRESSED state above } else if (ckey == S6) { Keyboard.release(MODIFIERKEY_LEFT_CTRL); Keyboard.release(MODIFIERKEY_LEFT_ALT); } else { toggleLocks(ccode, RELEASED); Keyboard.release(ccode); } break; case IDLE: status = " IDLE."; //debug(ckey, ccode, status); } } void loop() { // Fill kpd.key[ ] array with up-to 10 active keys. char ckey; // key from key table int cstate; // state of the key if (kpd.getKeys()) { for (int i = 0; i < 4; i++) { // Scan the whole key list. if ( kpd.key[i].stateChanged ) { // Only find keys that have changed state. ckey = kpd.key[i].kchar; cstate = kpd.key[i].kstate; //debug(ckey, cstate, ""); processKey(ckey, cstate); } } } //countCycles(); } // Print out on virtual serial port for debugging purposes void debug(unsigned char key, int code, String status) { Serial.print("key:"); Serial.print(key); Serial.print(" code:"); Serial.print(code,HEX); Serial.print(" "); Serial.println(status); } void countCycles() { loopCount++; if ((millis() - startTime) > 1000 ) { Serial.println(loopCount); startTime = millis(); loopCount = 0; } }