// XtKeyboard source: https://forum.arduino.cc/index.php?topic=18396.0 // XT protocol: https://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html #include int clockPin = 2; int dataPin = 3; volatile byte data = B0; int test = 0; volatile int counter = 0; int numbits = 10; int speakerPin = 5; int keysPressed = 0; unsigned long repeatTimeout = 0; unsigned long repeatTimeoutValue = 1000; const int keyReleaseBit = 0x80; char m[255]; bool pressed[255]; // MODS >>> // [1] send debug scancode information to serial port bool modConsoleLog = true; const int modConsoleLogKey = 59; // F1 // [2] play speaker sounds of keys bool modKeySounds = false; const int modKeySoundsKey = 60; // F2 // [3] replace CapsLock with Super key (aka GUI or Windows key) const bool modSuperCapslock = true; // [4] stuck key guard: release all the pressed keys if no data comes from the keyboard in a while bool modStuckKeyGuard = false; // [13] mod switcher: enable changing of active mods! const bool modModSwitcher = true; const int modSwitcherKey = 55; // PRTSC * // <<< MODS // https://www.arduino.cc/en/Reference/KeyboardModifiers void setupKeyMapping() { m[1]=KEY_ESC; m[2]='1'; m[3]='2'; m[4]='3'; m[5]='4'; m[6]='5'; m[7]='6'; m[8]='7'; m[9]='8'; m[10]='9'; m[11]='0'; m[12]='-'; m[13]='='; m[14]=KEY_BACKSPACE; m[15]=KEY_TAB; m[16]='q'; m[17]='w'; m[18]='e'; m[19]='r'; m[20]='t'; m[21]='y'; m[22]='u'; m[23]='i'; m[24]='o'; m[25]='p'; m[26]='['; m[27]=']'; m[28]=KEY_RETURN; m[29]=KEY_LEFT_CTRL; m[30]='a'; m[31]='s'; m[32]='d'; m[33]='f'; m[34]='g'; m[35]='h'; m[36]='j'; m[37]='k'; m[38]='l'; m[39]=';'; m[40]='\''; m[41]='`'; m[42]=KEY_LEFT_SHIFT; m[43]='\\'; m[44]='z'; m[45]='x'; m[46]='c'; m[47]='v'; m[48]='b'; m[49]='n'; m[50]='m'; m[51]=','; m[52]='.'; m[53]='/'; m[54]=KEY_RIGHT_SHIFT; m[55]='\335'; // num-* m[56]=KEY_LEFT_ALT; m[57]=' '; m[58]=(modSuperCapslock ? KEY_LEFT_GUI : KEY_CAPS_LOCK); // EVIL m[59]=KEY_F1; m[60]=KEY_F2; m[61]=KEY_F3; m[62]=KEY_F4; m[63]=KEY_F5; m[64]=KEY_F6; m[65]=KEY_F7; m[66]=KEY_F8; m[67]=KEY_F9; m[68]=KEY_F10; // https://forum.arduino.cc/index.php?topic=266688.msg1880644#msg1880644 // http://www.quadibloc.com/comp/scan.htm const byte KEY_NUM_LOCK = 219; m[69]=KEY_NUM_LOCK; const byte KEY_SCROLL_LOCK = 207; m[70]=KEY_SCROLL_LOCK; m[71]='\347'; // num-7 m[72]='\350'; // num-8 m[73]='\351'; // num-9 m[74]='\336'; // num-- m[75]='\344'; // num-4 m[76]='\345'; // num-5 m[77]='\346'; // num-6 m[78]='\337'; // num-+ m[79]='\341'; // num-1 m[80]='\342'; // num-2 m[81]='\343'; // num-3 m[82]='\352'; // num-0 m[83]='\353'; // num-. //... m[101]=KEY_F11; m[102]=KEY_F12; m[107]=KEY_INSERT; m[108]=KEY_DELETE; m[109]=KEY_UP_ARROW; m[110]=KEY_DOWN_ARROW; m[111]=KEY_LEFT_ARROW; m[112]=KEY_RIGHT_ARROW; //m[]=KEY_; } char translateKeyToChar(int key) { if (sizeof(m) <= key) { return 0; } return m[key]; } void printChar(char keyChar) { Serial.print("'"); Serial.print(keyChar); Serial.print("' ("); Serial.print(int(keyChar)); Serial.print("), Pressed keys: ["); Serial.print(keysPressed); Serial.println("]"); } void processKbdByte(byte data) { bool isRelease = data & keyReleaseBit; int key = data - (isRelease ? keyReleaseBit : 0); if (modConsoleLog) { Serial.print("Scancode: <"); Serial.print(int(data)); Serial.print("> "); Serial.print("Key: <"); Serial.print(int(key)); Serial.print("> "); } char keyChar = translateKeyToChar(key); if (isRelease) { Keyboard.release(keyChar); if (pressed[key]) { pressed[key] = false; keysPressed--; } if (modConsoleLog) { Serial.print("Release: "); printChar(keyChar); } } else { Keyboard.press(keyChar); if (!pressed[key]) { pressed[key] = true; keysPressed++; } if (modConsoleLog) { Serial.print("Press: "); printChar(keyChar); } } } void clearPressedKeys() { Keyboard.releaseAll(); keysPressed = 0; for (int i = 0; i < sizeof(pressed); i++) { pressed[i] = false; } Serial.println("Pressed keys list cleared."); } void clockInterrupt() { int clockValue = digitalRead(clockPin); if (clockValue == LOW && test == 0 && counter < numbits) { test = 1; data = data >> 1; if (digitalRead(dataPin) == HIGH) { bitSet(data, 7); } counter++; } if (clockValue == HIGH && test == 1) { test = 0; } } void setup() { setupKeyMapping(); pinMode(dataPin, INPUT); pinMode(clockPin, INPUT_PULLUP); Serial.begin(9600); Keyboard.begin(); clearPressedKeys(); attachInterrupt(digitalPinToInterrupt(clockPin), clockInterrupt, CHANGE); } void loop() { if(counter >= numbits) { processKbdByte(data); if (modKeySounds) tone(speakerPin, 60 + int(data), 25); data = B0; counter = 0; repeatTimeout = millis() + repeatTimeoutValue; } if (modStuckKeyGuard && keysPressed > 0) { unsigned long timeNow = millis(); if (repeatTimeout < timeNow) { if (modConsoleLog) Serial.println("Stuck key detected!"); clearPressedKeys(); } } if (modModSwitcher && pressed[modSwitcherKey]) { int fired = 0; if (pressed[modConsoleLogKey]) { modConsoleLog = !modConsoleLog; fired = 1; } if (pressed[modKeySoundsKey]) { modKeySounds = !modKeySounds; fired = 2; } if (fired > 0) { tone(speakerPin, 400 + fired * 20, 300); Serial.print("Metakey fired: "); Serial.println(fired); clearPressedKeys(); delay(300); } } }