This code is basically a 'word-for-word' interpretation of the Japanese timing diagram in the Research page, this is what allows us to reset the keyboard, query it for data and of course set the two LEDs!
// special command for setting LEDs
void setLEDs(bool leftLED, bool rightLED) {
digitalWrite(KEYBOARDOUT, LOW);
delayMicroseconds(TIMING *9);
digitalWrite(KEYBOARDOUT, HIGH);
delayMicroseconds(TIMING *3);
digitalWrite(KEYBOARDOUT, LOW);
delayMicroseconds(TIMING);
if (leftLED)
digitalWrite(KEYBOARDOUT, HIGH);
else
digitalWrite(KEYBOARDOUT, LOW);
delayMicroseconds(TIMING);
if (rightLED)
digitalWrite(KEYBOARDOUT, HIGH);
else
digitalWrite(KEYBOARDOUT, LOW);
delayMicroseconds(TIMING);
digitalWrite(KEYBOARDOUT, LOW);
delayMicroseconds(TIMING *7);
digitalWrite(KEYBOARDOUT, HIGH);
}
void query() {
// query the keyboard for data
digitalWrite(KEYBOARDOUT, LOW);
delayMicroseconds(TIMING *5);
digitalWrite(KEYBOARDOUT, HIGH);
delayMicroseconds(TIMING );
digitalWrite(KEYBOARDOUT, LOW);
delayMicroseconds(TIMING *3);
digitalWrite(KEYBOARDOUT, HIGH);
}
void nextreset() {
// reset the keyboard
digitalWrite(KEYBOARDOUT, LOW);
delayMicroseconds(TIMING);
digitalWrite(KEYBOARDOUT, HIGH);
delayMicroseconds(TIMING*4);
digitalWrite(KEYBOARDOUT, LOW);
delayMicroseconds(TIMING);
digitalWrite(KEYBOARDOUT, HIGH);
delayMicroseconds(TIMING*6);
digitalWrite(KEYBOARDOUT, LOW);
delayMicroseconds(TIMING*10);
digitalWrite(KEYBOARDOUT, HIGH);
}
We use cli() and sei() to turn off interrupts for the best precision! Interrupts can garble data by changing the timing.
uint32_t getresponse() {
// bitbang timing, read 22 bits 50 microseconds apart
cli();
while ( readkbd() );
delayMicroseconds(TIMING/2);
uint32_t data = 0;
for (uint8_t i=0; i < 22; i++) {
if (readkbd())
data |= ((uint32_t)1 << i);
delayMicroseconds(TIMING);
}
sei();
return data;
}
void loop() {
digitalWrite(LED, LOW);
delay(20);
uint32_t resp;
query();
resp = getresponse();
// check for a 'idle' response, we'll do nothing
if (resp == 0x200600) return;
// turn on the LED when we get real resposes!
digitalWrite(LED, HIGH);
// keycode is the lower 7 bits
uint8_t keycode = resp & 0xFF;
keycode /= 2;
#ifdef DEBUG
Serial.print('['); Serial.print(resp, HEX); Serial.print("] ");
Serial.print("keycode: "); Serial.print(keycode);
#endif
if (keycode == 0) {
// modifiers! you can remap these here,
// but I suggest doing it in the OS instead
if (resp & 0x1000)
Keyboard.press(KEY_LEFT_GUI);
else
Keyboard.release(KEY_LEFT_GUI);
if (resp & 0x2000) {
Keyboard.press(KEY_LEFT_SHIFT);
} else {
Keyboard.release(KEY_LEFT_SHIFT);
}
if (resp & 0x4000) {
Keyboard.press(KEY_RIGHT_SHIFT);
} else {
Keyboard.release(KEY_RIGHT_SHIFT);
}
// turn on shift LEDs if shift is held down
if (resp & 0x6000)
setLEDs(true, true);
else
setLEDs(false, false);
if (resp & 0x8000)
Keyboard.press(KEY_LEFT_CTRL);
else
Keyboard.release(KEY_LEFT_CTRL);
if (resp & 0x10000)
Keyboard.press(KEY_RIGHT_CTRL);
else
Keyboard.release(KEY_RIGHT_CTRL);
if (resp & 0x20000)
Keyboard.press(KEY_LEFT_ALT);
else
Keyboard.release(KEY_LEFT_ALT);
if (resp & 0x40000)
Keyboard.press(KEY_RIGHT_ALT);
else
Keyboard.release(KEY_RIGHT_ALT);
return;
}
for (int i = 0; i< 100; i++) {
if (nextkbd_keydesc_us[i*3] == keycode) {
char ascii = nextkbd_keydesc_us[i*3+1];
#ifdef DEBUG
Serial.print("--> "); Serial.print(ascii);
#endif
int code;
switch (keycode) {
case 73: code = KEY_ESC; break;
case 13: code = KEY_RETURN; break;
case 42: code = KEY_RETURN; break;
case 27: code = KEY_BACKSPACE; break;
case 22: code = KEY_UP_ARROW; break;
case 15: code = KEY_DOWN_ARROW; break;
case 16: code = KEY_RIGHT_ARROW; break;
case 9: code = KEY_LEFT_ARROW; break;
// remap the 'lower volume' key to Delete (its where youd expect it)
case 2: code = KEY_DELETE; break;
default: code = ascii;
}
if ((resp & 0xF00) == 0x400) { // down press
#ifdef DEBUG
Serial.println(" v ");
#endif
Keyboard.press(code);
break;
}
if ((resp & 0xF00) == 0x500) {
Keyboard.release(code);
#ifdef DEBUG
Serial.println(" ^ ");
#endif
break;
}
}
}