You can grab the full Arduino sketch and the header files here at github. Click DOWNLOADs and unzip the directory, then rename it USB_NeXT_Keyboard and place in your Arduino Sketch folder. Upload to a Leonardo or Micro.

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!
Copy Code
// 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);
}
This is the bitbang-receiver code. It looks from a low pin transition, then delay()'s half a timing pulse, and reads 22 pulses out, stuffing one after the other into a 32 bit variable.

We use cli() and sei() to turn off interrupts for the best precision! Interrupts can garble data by changing the timing.
Copy Code
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;
}
We spend most of the loop query()ing the keyboard for new data. If there's no new information, we'll get the 0x200600 "idle" response, which we can ignore. If we don't get that response, we can print out the keycode (what key was pressed) in the debugging serial console
Copy Code
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
There's a special exception for keys like SHIFT and ALT - these don't send a keycode, you have to check the higher bits to see what key was pressed - you can have multiple ones pressed at once! These are turned into USB keyboard presses of the matching 'modifier' keys. See http://arduino.cc/en/Reference/KeyboardModifiers for more!

Copy Code
  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;
  }
This is where all the hard work really happens. We convert the keycode into an ascii code using the NetBSD keycode table - for some codes, we have to perform a special action because the ascii code isn't the same as the code we have to send down the USB bus. For example Escape, Return, Backspace, and the Arrows. The Delete key doesn't actually exist on the NeXT so we mapped the 'lower volume' button to it since they're in the same location. Then we can look at the 3rd byte to see if its a key down or key up command and send that press or release!
Copy Code
  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;
      }
    }
  }
Last updated on 2014-04-24 at 03.41.15 PM Published on 2012-12-07 at 09.26.35 PM