/* Copyright 2019 Olga, based on work of Frank Adams Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ // This software implements old Compaq keyboard controller on Teensy 3.5 connected directly with keyboard // IMPORTANT ! DO NOT CONNECT PIN 13 AS IT DRIVES LED AND GIVES RANDOM READING ! // This routine uses the Teensyduino "Micro-Manager Method" to send Normal and Modifier // keys over USB. // Description of Teensyduino keyboard functions is at www.pjrc.com/teensy/td_keyboard.html // // Revision History // Initial Release Feb 14, 2021 // // // comment this line to have debug code included #define NODEBUG // keyboard to teensy connection definitions #define KBD_X 32 #define KBD_FN 34 #define KBD_LSH 35 #define KBD_LCT 36 #define KBD_RSH 37 #define KBD_RCT 38 #define KBD_RAL 39 #define KBD_LAL 14 #define KBD_A 15 #define KBD_B 16 #define KBD_C 17 #define KBD_D 18 #define KBD_E 19 #define KBD_F 20 #define KBD_G 21 #define KBD_H 22 #define KBD_1 30 #define KBD_2 29 #define KBD_3 28 #define KBD_4 27 #define KBD_5 26 #define KBD_6 25 #define KBD_7 24 #define KBD_8 12 #define KBD_9 11 #define KBD_10 10 #define KBD_11 8 #define KBD_CAPS 2 #define KBD_NUM 3 #define KBD_SCR 4 #define USB_LED_NUM_LOCK 0 #define USB_LED_CAPS_LOCK 1 #define USB_LED_SCROLL_LOCK 2 struct KeyDesc { const char* name; boolean isModifier; int pin1; int pin2; int keyCode; int fnKeyCode; int numKeyCode; int scrKeyCode; boolean scanned; }; struct KeySent { int rawKey; int sendKey; }; struct KeyDesc Keys[] = { { "LCTRL", true, KBD_X, KBD_LCT, MODIFIERKEY_LEFT_CTRL, 0, 0, 0 }, { "RCTRL", true, KBD_X, KBD_RCT, MODIFIERKEY_RIGHT_CTRL, 0, 0, 0 }, { "LSHIFT", true, KBD_X, KBD_LSH, MODIFIERKEY_LEFT_SHIFT, 0, 0, 0 }, { "RSHIFT", true, KBD_X, KBD_RSH, MODIFIERKEY_RIGHT_SHIFT, 0, 0, 0 }, { "LALT", true, KBD_X, KBD_LAL, MODIFIERKEY_LEFT_ALT, 0, 0, 0 }, { "RALT", true, KBD_X, KBD_RAL, MODIFIERKEY_RIGHT_ALT, 0, 0, 0 }, { "FN", true, KBD_X, KBD_FN, MODIFIERKEY_GUI, 0, 0, 0 }, { "ESC", false, KBD_A, KBD_3, KEY_ESC, 0, 0, 0 }, { "F1", false, KBD_A, KBD_2, KEY_F1, 0, 0, 0 }, { "F2", false, KBD_A, KBD_4, KEY_F2, 0, 0, 0 }, { "F3", false, KBD_A, KBD_1, KEY_F3, 0, 0, 0 }, { "F4", false, KBD_A, KBD_8, KEY_F4, 0, 0, 0 }, { "F5", false, KBD_A, KBD_11, KEY_F5, 0, 0, 0 }, { "F6", false, KBD_A, KBD_5, KEY_F6, 0, 0, 0 }, { "F7", false, KBD_A, KBD_7, KEY_F7, 0, 0, 0 }, { "F8", false, KBD_A, KBD_6, KEY_F8, 0, 0, 0 }, { "F9", false, KBD_A, KBD_10, KEY_F9, 0, 0, 0 }, { "F10", false, KBD_A, KBD_9, KEY_F10, 0, 0, 0 }, { "F11", false, KBD_G, KBD_9, KEY_F11, 0, 0, 0 }, { "F12", false, KBD_G, KBD_10, KEY_F12, 0, 0, 0 }, { "NUMLCK", false, KBD_G, KBD_6, KEY_NUM_LOCK, 0, 0, 0 }, { "PRTSCR", false, KBD_G, KBD_7, KEY_PRINTSCREEN, 0, 0, 0 }, { "~", false, KBD_B, KBD_3, KEY_TILDE, 0, 0, 0 }, { "1", false, KBD_B, KBD_2, KEY_1, 0, 0, 0 }, { "2", false, KBD_B, KBD_4, KEY_2, 0, 0, 0 }, { "3", false, KBD_B, KBD_1, KEY_3, 0, 0, 0 }, { "4", false, KBD_B, KBD_8, KEY_4, 0, 0, 0 }, { "5", false, KBD_B, KBD_11, KEY_5, 0, 0, 0 }, { "6", false, KBD_B, KBD_5, KEY_6, 0, 0, 0 }, { "7", false, KBD_B, KBD_7, KEY_7, KEY_HOME, KEYPAD_7, 0 }, { "8", false, KBD_B, KBD_6, KEY_8, KEY_UP, KEYPAD_8, 0 }, { "9", false, KBD_B, KBD_10, KEY_9, KEY_PAGE_UP, KEYPAD_9, 0 }, { "0", false, KBD_B, KBD_9, KEY_0, 0, KEYPAD_ASTERIX, 0 }, { "-(B)", false, KBD_F, KBD_9, KEY_MINUS, 0, 0, 0 }, { "=(Apo)", false, KBD_F, KBD_10, KEY_EQUAL, 0, 0, 0 }, { "BCKSPC", false, KBD_F, KBD_6, KEY_BACKSPACE, 0, 0, 0 }, { "SCRLCK", false, KBD_F, KBD_7, KEY_SCROLL_LOCK, 0, 0, 0 }, { "TAB", false, KBD_C, KBD_3, KEY_TAB, 0, 0, 0 }, { "Q", false, KBD_C, KBD_2, KEY_Q, 0, 0, 0 }, { "W", false, KBD_C, KBD_4, KEY_W, 0, 0, 0 }, { "E", false, KBD_C, KBD_1, KEY_E, 0, 0, 0 }, { "R", false, KBD_C, KBD_8, KEY_R, 0, 0, 0 }, { "T", false, KBD_C, KBD_11, KEY_T, 0, 0, 0 }, { "Y", false, KBD_C, KBD_5, KEY_Y, 0, 0, 0 }, { "U", false, KBD_C, KBD_7, KEY_U, KEY_LEFT, KEYPAD_4, 0 }, { "I", false, KBD_C, KBD_6, KEY_I, 0, KEYPAD_5, 0 }, { "O", false, KBD_C, KBD_10, KEY_O, KEY_RIGHT, KEYPAD_6, 0 }, { "P", false, KBD_C, KBD_9, KEY_P, 0, KEYPAD_MINUS, 0 }, { "[(Ue)", false, KBD_H, KBD_9, KEY_LEFT_BRACE, 0, 0, 0 }, { "](+~)", false, KBD_H, KBD_10, KEY_RIGHT_BRACE, 0, 0, 0 }, { "ENTER", false, KBD_H, KBD_8, KEY_ENTER, 0, KEYPAD_ENTER, 0 }, { "PAUSE", false, KBD_H, KBD_7, KEY_PAUSE, 0, 0, 0 }, { "CAPSLCK", false, KBD_D, KBD_2, KEY_CAPS_LOCK, 0, 0, 0 }, { "A", false, KBD_D, KBD_4, KEY_A, 0, 0, 0 }, { "S", false, KBD_D, KBD_1, KEY_S, 0, 0, 0 }, { "D", false, KBD_D, KBD_8, KEY_D, 0, 0, 0 }, { "F", false, KBD_D, KBD_11, KEY_F, 0, 0, 0 }, { "G", false, KBD_D, KBD_5, KEY_G, 0, 0, 0 }, { "H", false, KBD_D, KBD_3, KEY_H, 0, 0, 0 }, { "J", false, KBD_D, KBD_7, KEY_J, KEY_END, KEYPAD_1, 0 }, { "K", false, KBD_D, KBD_6, KEY_K, KEY_DOWN, KEYPAD_2, 0 }, { "L", false, KBD_D, KBD_10, KEY_L, KEY_PAGE_DOWN, KEYPAD_3, 0 }, { ";(Oe)", false, KBD_D, KBD_9, KEY_SEMICOLON, 0, KEYPAD_PLUS, 0 }, { "'(Ae)", false, KBD_H, KBD_5, KEY_QUOTE, 0, 0, 0 }, { "\\(#)", false, KBD_H, KBD_11, KEY_BACKSLASH, 0, 0, 0 }, { "INS", false, KBD_H, KBD_1, KEY_INSERT, 0, 0, 0 }, { "<>", false, KBD_E, KBD_4, KEY_MENU, 0, 0, 0 }, { "Z", false, KBD_E, KBD_1, KEY_Z, 0, 0, 0 }, { "X", false, KBD_E, KBD_8, KEY_X, 0, 0, 0 }, { "C", false, KBD_E, KBD_11, KEY_C, 0, 0, 0 }, { "V", false, KBD_E, KBD_5, KEY_V, 0, 0, 0 }, { "B", false, KBD_E, KBD_3, KEY_B, 0, 0, 0 }, { "N", false, KBD_E, KBD_7, KEY_N, 0, 0, 0 }, { "M", false, KBD_E, KBD_6, KEY_M, 0, 0, 0 }, { ",", false, KBD_E, KBD_10, KEY_COMMA, KEY_INSERT, KEYPAD_0, 0 }, { ".", false, KBD_E, KBD_9, KEY_PERIOD, KEY_DELETE, KEYPAD_PERIOD, 0 }, { "/", false, KBD_F, KBD_5, KEY_SLASH, 0, KEYPAD_SLASH, 0 }, { "DEL", false, KBD_F, KBD_1, KEY_DELETE, 0, 0, 0 }, { "SPACE", false, KBD_G, KBD_3, KEY_SPACE, 0, 0, 0 }, { "UP", false, KBD_F, KBD_8, KEY_UP, KEY_PAGE_UP, 0, KEY_PAGE_UP }, { "LEFT", false, KBD_G, KBD_11, KEY_LEFT, KEY_HOME, 0, KEY_HOME }, { "DOWN", false, KBD_G, KBD_8, KEY_DOWN, KEY_PAGE_DOWN, 0, KEY_PAGE_DOWN }, { "RIGHT", false, KBD_G, KBD_1, KEY_RIGHT, KEY_END, 0, KEY_END }, { "none", false, 0, 0, 0, 0 } // last key - no key }; // second keycode accessible via modifier #define SECOND_CODE MODIFIERKEY_GUI // number of teensy pins #define PIN_MAX 39 // slots hold the normal key values to be sent over USB. // WARNING - this must be set between 1 and 6 #define SLOT_NORMAL_COUNT 6 struct KeySent slots[SLOT_NORMAL_COUNT]; // modifiers table holds - surprise - the modifier keys states #define SLOT_MOD_COUNT 6 struct KeySent mods[SLOT_MOD_COUNT]; // general check for button pressed - uses raw key boolean is_pressed(int rawKey, const struct KeySent* slot, int slotCount) { for(int i = 0; i < slotCount ; i++) { if (slot[i].rawKey == rawKey) { return true; } } return false; } // general slot setup function, returns true on change boolean set_slot(int rawKey, int sendKey, boolean isPressed, struct KeySent* slot, int slotCount) { boolean currentlyPressed = is_pressed(rawKey, slot, slotCount); if (currentlyPressed == isPressed) { return false; } for(int i = 0; i < slotCount ; i++) { if ((isPressed && slot[i].rawKey == 0) || (!isPressed && slot[i].rawKey == rawKey)) { slot[i].rawKey = isPressed ? rawKey : 0; slot[i].sendKey = isPressed ? sendKey : 0; return true; } } return false; } // check if normal key pressed boolean is_key_pressed(int rawKey) { return is_pressed(rawKey, slots, SLOT_NORMAL_COUNT); } // check if mod key pressed boolean is_mod_pressed(int rawKey) { return is_pressed(rawKey, mods, SLOT_MOD_COUNT); } // Function to load the key name into the first available slot boolean load_slot(int rawKey, int sendKey) { return set_slot(rawKey, sendKey, true, slots, SLOT_NORMAL_COUNT); } // Function to clear the slot that contains the key name boolean clear_slot(int rawKey) { return set_slot(rawKey, 0, false, slots, SLOT_NORMAL_COUNT); } // Function to load the modifier key name into the appropriate mod variable boolean load_mod(int m_key) { return set_slot(m_key, m_key, true, mods, SLOT_MOD_COUNT); } // Function to load 0 into the appropriate mod variable boolean clear_mod(int m_key) { return set_slot(m_key, 0, false, mods, SLOT_MOD_COUNT); } // // Function to send the modifier keys over usb void send_mod() { int totalMod = 0; for(int i = 0; i < SLOT_MOD_COUNT ; i++) { totalMod |= mods[i].sendKey; } Keyboard.set_modifier(totalMod); Keyboard.send_now(); } // // Function to send the normal keys in the 6 slots over usb void send_normals() { for(int i = 0 ; i < SLOT_NORMAL_COUNT ; i++) { int value = slots[i].sendKey; switch(i) { case 0: Keyboard.set_key1(value); break; case 1: Keyboard.set_key2(value); break; case 2: Keyboard.set_key3(value); break; case 3: Keyboard.set_key4(value); break; case 4: Keyboard.set_key5(value); break; case 5: Keyboard.set_key6(value); break; default: // no send here break; } } Keyboard.send_now(); } // // Function to set a pin to high impedance (acts like open drain output) void go_z(int pin) { pinMode(pin, INPUT); digitalWrite(pin, HIGH); } // // Function to set a pin as an input with a pullup void go_pu(int pin) { pinMode(pin, INPUT_PULLUP); digitalWrite(pin, HIGH); } // // Function to send a pin to a logic low void go_0(int pin) { pinMode(pin, OUTPUT); digitalWrite(pin, LOW); } // // Function to send a pin to a logic high void go_1(int pin) { pinMode(pin, OUTPUT); digitalWrite(pin, HIGH); } ////// DEBUG CODE void d_start() { #ifndef NODEBUG while( !Serial ); Serial.begin(9600); #endif } void d_print(const char* format, ...) { #ifndef NODEBUG va_list(aptr); int ret; char buffer[200]; va_start(aptr, format); ret = vsprintf(buffer, format, aptr); va_end(aptr); if (ret >= 0) { Serial.println(buffer); } else { Serial.println("--- error constructing debug log ---"); } #endif } ////// END DEBUG CODE int KeyCount = 0; int readPins = 0; int readPIN[PIN_MAX]; int writePins = 0; int writePIN[PIN_MAX]; boolean is_in_array(int val, int* arr, int max) { for(int i = 0 ; i < max ; i++) { if (arr[i] == val) { return true; } } return false; } void blink() { go_0(KBD_CAPS); go_0(KBD_NUM); go_0(KBD_SCR); delay(200); go_1(KBD_CAPS); go_1(KBD_NUM); go_1(KBD_SCR); } // //----------------------------------Setup------------------------------------------- void setup() { d_start(); d_print("Setup Start: LED"); Keyboard.begin(); go_1(KBD_CAPS); go_1(KBD_NUM); go_1(KBD_SCR); d_print("Setup LED complete, setup tables start."); // setup keyboard tables for(int i = 0 ; i < SLOT_NORMAL_COUNT ; i++) { slots[i].rawKey = 0; slots[i].sendKey = 0; } for(int i = 0 ; i < SLOT_MOD_COUNT ; i++) { mods[i].rawKey = 0; mods[i].sendKey = 0; } // initialize readers and writers for(int i = 0 ; i < PIN_MAX ; i++) { readPIN[i] = -1; writePIN[i] = -1; } for(int i = 0 ; ; i++) { struct KeyDesc& keyDesc = Keys[i]; if (keyDesc.keyCode == 0) { KeyCount = i; break; } int pin1 = keyDesc.pin1; boolean pin1InRead = is_in_array(pin1, readPIN, readPins); boolean pin1InWrite = is_in_array(pin1, writePIN, writePins); int pin2 = keyDesc.pin2; boolean pin2InRead = is_in_array(pin2, readPIN, readPins); boolean pin2InWrite = is_in_array(pin2, writePIN, writePins); if (pin1InRead || pin2InWrite) { // means changed places of pins pin1 = keyDesc.pin2; pin2 = keyDesc.pin1; keyDesc.pin1 = pin1; keyDesc.pin2 = pin2; boolean pinInRead = pin1InRead; boolean pinInWrite = pin1InWrite; pin1InRead = pin2InRead; pin1InWrite = pin2InWrite; pin2InRead = pinInRead; pin2InWrite = pinInWrite; #ifndef NODEBUG char text[100]; sprintf(text, "Swapped pins for key %s, pins now are %d %d", keyDesc.name, keyDesc.pin1, keyDesc.pin2); d_print(text); #endif } if ((pin1InRead && pin1InWrite) || (pin2InRead && pin2InWrite) || (pin1InRead && pin2InRead) || (pin1InWrite && pin2InWrite)) { #ifndef NODEBUG char text[100]; sprintf(text, "Error in keyboard configuration for key %s pins %d %d", keyDesc.name, keyDesc.pin1, keyDesc.pin2); d_print(text); #endif continue; } if (!pin1InWrite) { writePIN[writePins++] = pin1; } if (!pin2InRead) { readPIN[readPins++] = pin2; } } for(int i = 0 ; i < writePins ; i++) { go_z(readPIN[i]); } for(int i = 0 ; i < writePins ; i++) { go_z(writePIN[i]); } d_print("Setup OK"); blink(); } // // //---------------------------------Main Loop--------------------------------------------- // void loop() { // read LED state boolean isCapsLock = keyboard_leds & (1<