Das Keyboard + teensy

User avatar
tuffrabit

17 Dec 2013, 17:32

I bought a Das Keyboard Model S Ultimate from Ebay. It was advertised as not working. I figured I had a decent chance of making it work. Indeed it is broken, Windows doesn't even register a USB connection with it. Everything on the daughter board looks fine and the USB cable is good (tested with an ohm meter). Best I can figure is the matrix IC is dead. Only cost me $8, so not that much of a loss. I'm considering replacing the daughter PCB with a teensy or arduino. I figure it shouldn't be to hard, maybe the most difficult part will be tracing the matrix PCB to figure out the rows and columns. The only things I can't figure is how I'll make the num/scroll/caps LEDs light up.

Anybody attempted something like this before?

*EDIT

All done. Here's pics and code.

Imgur album of all the pics: http://imgur.com/a/zK1BN

Main KB Code:

Code: Select all

#include <usb_private.h>

#define KEY_MENU 0x76

const int numLockPin = PIN_D2;
const int capsLockPin = PIN_D1;
const int scrollLockPin = PIN_D0;-

int rowCount = 18;
int colCount = 8;
int rowPins[18] = {9, 10, 11, 12, 13, 14, 15, 16, 17, 20, 23, 24, 25, 26, 40, 41, 42, 45};
int colPins[9] = {18, 19, 21, 22, 38, 39, 43, 44};
int keys[6] = {0, 0, 0, 0, 0, 0};
int modifiers[4] = {0, 0, 0, 0};

void setup() {
  resetColumns();
  
  pinMode(numLockPin, OUTPUT);
  pinMode(capsLockPin, OUTPUT);
  pinMode(scrollLockPin, OUTPUT);
}

void loop() {
  scanMatrix();
  
  if (isNumOn()) {
    digitalWrite(numLockPin, HIGH);
  }
  else {
    digitalWrite(numLockPin, LOW);
  }
  
  if (isCapsOn()) {
    digitalWrite(capsLockPin, HIGH);
  }
  else {
    digitalWrite(capsLockPin, LOW);
  }
  
  if (isScrollOn()) {
    digitalWrite(scrollLockPin, HIGH);
  }
  else {
    digitalWrite(scrollLockPin, LOW);
  }
}

int ledkeys(void) {
  return int(keyboard_leds);
}

boolean isScrollOn(void) {
  return ((ledkeys() & 4) == 4) ? true : false;
}

boolean isCapsOn(void) {
  return ((ledkeys() & 2) == 2) ? true : false;
}

boolean isNumOn(void) {
  return ((ledkeys() & 1) == 1) ? true : false;
}

void scanMatrix() {
  for (int i = 0; i < rowCount; i++) {
    int row = rowPins[i];

    activateRow(row);

    for (int z = 0; z < colCount; z++) {
      int col = colPins[z];
      int key = getKeyFromCombo(row, col);

      if (checkColumn(col)) {
        Keyboard.press(key);
      }
      else {
        Keyboard.release(key);
      }
    }

    digitalWrite(row, HIGH);
  }
}

void resetColumns() {
  for (int i = 0; i < colCount; i++) {
    int col = colPins[i];

    pinMode(col, INPUT_PULLUP);
  }
}

void activateRow(int row) {
  pinMode(row, OUTPUT);
  digitalWrite(row, LOW);
}

boolean checkColumn(int col) {
  boolean returnValue = false;
  pinMode(col, INPUT_PULLUP);

  if (digitalRead(col) == LOW) {
    returnValue = true;
  }
  else {
    returnValue = false;
  }

  return returnValue;
}

boolean setNextKey(int key) {
  boolean isSet = false;
  
  if (keys[0] == 0) {
    keys[0] = key;
    isSet = true;
  }
  else if (keys[1] == 0) {
    keys[1] = key;
    isSet = true;
  }
  else if (keys[2] == 0) {
    keys[2] = key;
    isSet = true;
  }
  else if (keys[3] == 0) {
    keys[3] = key;
    isSet = true;
  }
  else if (keys[4] == 0) {
    keys[4] = key;
    isSet = true;
  }
  else if (keys[5] == 0) {
    keys[5] = key;
    isSet = true;
  }
  
  return isSet;
}

boolean resetKey(int key) {
  boolean isReset = false;
  
  if (keys[0] == key) {
    keys[0] = 0;
    isReset = true;
  }
  else if (keys[1] == key) {
    keys[1] = 0;
    isReset = true;
  }
  else if (keys[2] == key) {
    keys[2] = 0;
    isReset = true;
  }
  else if (keys[3] == key) {
    keys[3] = 0;
    isReset = true;
  }
  else if (keys[4] == key) {
    keys[4] = 0;
    isReset = true;
  }
  else if (keys[5] == key) {
    keys[5] = 0;
    isReset = true;
  }
  
  return isReset;
}

int getKeyFromCombo(int rowPin, int colPin) {
  int key = -1;
  
  switch (rowPin) {
    case 9:
      switch (colPin) {
        case 18:
          key = -1;
        case 19:
          key = KEY_PAUSE;
          break;
        case 21:
          key = KEY_RIGHT_CTRL;
          break;
        case 22:
          key = -1;
          break;
        case 38:
          key = -1;
          break;
        case 39:
          key = KEY_LEFT_CTRL;
          break;
        case 43:
          key = KEY_F5;
          break;
        case 44:
          key = -1;
          break;
        }
      break;
    case 10:
      switch (colPin) {
        case 18:
          key = KEY_TAB;
          break;
        case 19:
          key = KEY_Q;
          break;
        case 21:
          key = KEY_Z;
          break;
        case 22:
          key = -1;
          break;
        case 38:
          key = KEY_A;
          break;
        case 39:
          key = KEY_TILDE;
          break;
        case 43:
          key = KEY_1;
          break;
        case 44:
          key = KEY_ESC;
          break;
        }
      break;
    case 11:
      switch (colPin) {
        case 18:
          key = KEY_CAPS_LOCK;
          break;
        case 19:
          key = KEY_W;
          break;
        case 21:
          key = KEY_X;
          break;
        case 22:
          key = -1;
          break;
        case 38:
          key = KEY_S;
          break;
        case 39:
          key = KEY_F1;
          break;
        case 43:
          key = KEY_2;
          break;
        case 44:
          key = -1;
          break;
        }
      break;
    case 12:
      switch (colPin) {
        case 18:
          key = KEY_F3;
          break;
        case 19:
          key = KEY_E;
          break;
        case 21:
          key = KEY_C;
          break;
        case 22:
          key = -1;
          break;
        case 38:
          key = KEY_D;
          break;
        case 39:
          key = KEY_F12;
          break;
        case 43:
          key = KEY_3;
          break;
        case 44:
          key = KEY_F4;
          break;
        }
      break;
    case 13:
      switch (colPin) {
        case 18:
          key = KEY_T;
          break;
        case 19:
          key = KEY_R;
          break;
        case 21:
          key = KEY_V;
          break;
        case 22:
          key = KEY_B;
          break;
        case 38:
          key = KEY_F;
          break;
        case 39:
          key = KEY_5;
          break;
        case 43:
          key = KEY_4;
          break;
        case 44:
          key = KEY_G;
          break;
        }
      break;
    case 14:
      switch (colPin) {
        case 18:
          key = KEY_Y;
          break;
        case 19:
          key = KEY_U;
          break;
        case 21:
          key = KEY_M;
          break;
        case 22:
          key = KEY_N;
          break;
        case 38:
          key = KEY_J;
          break;
        case 39:
          key = KEY_6;
          break;
        case 43:
          key = KEY_7;
          break;
        case 44:
          key = KEY_H;
          break;
        }
      break;
    case 15:
      switch (colPin) {
        case 18:
          key = KEY_F7;
          break;
        case 19:
          key = KEY_O;
          break;
        case 21:
          key = KEY_PERIOD;
          break;
        case 22:
          key = KEY_MENU; // Menu Key
          break;
        case 38:
          key = KEY_L;
          break;
        case 39:
          key = KEY_F8;
          break;
        case 43:
          key = KEY_9;
          break;
        case 44:
          key = -1;
          break;
        }
      break;
    case 16:
      switch (colPin) {
        case 18:
          key = -1;
          break;
        case 19:
          key = KEY_SCROLL_LOCK;
          break;
        case 21:
          key = -1;
          break;
        case 22:
          key = KEY_RIGHT_ALT;
          break;
        case 38:
          key = -1;
          break;
        case 39:
          key = -1;
          break;
        case 43:
          key = KEY_PRINTSCREEN;
          break;
        case 44:
          key = KEY_LEFT_ALT;
          break;
        }
      break;
    case 17:
      switch (colPin) {
        case 18:
          key = KEY_RIGHT_BRACE;
          break;
        case 19:
          key = KEY_I;
          break;
        case 21:
          key = KEY_COMMA;
          break;
        case 22:
          key = -1;
          break;
        case 38:
          key = KEY_K;
          break;
        case 39:
          key = KEY_EQUAL;
          break;
        case 43:
          key = KEY_8;
          break;
        case 44:
          key = KEY_F6;
          break;
        }
      break;
    case 20:
      switch (colPin) {
        case 18:
          key = KEYPAD_6;
          break;
        case 19:
          key = KEYPAD_9;
          break;
        case 21:
          key = KEYPAD_ASTERIX;
          break;
        case 22:
          key = KEYPAD_MINUS;
          break;
        case 38:
          key = KEYPAD_3;
          break;
        case 39:
          key = KEY_PAGE_UP;
          break;
        case 43:
          key = KEY_PAGE_DOWN;
          break;
        case 44:
          key = KEYPAD_PERIOD;
          break;
        }
      break;
    case 23:
      switch (colPin) {
        case 18:
          key = -1;
          break;
        case 19:
          key = -1;
          break;
        case 21:
          key = -1;
          break;
        case 22:
          key = -1;
          break;
        case 38:
          key = KEY_RIGHT_GUI;
          break;
        case 39:
          key = -1;
          break;
        case 43:
          key = -1;
          break;
        case 44:
          key = -1;
          break;
        }
      break;
    case 24:
      switch (colPin) {
        case 18:
          key = KEY_LEFT_GUI;
          break;
        case 19:
          key = -1;
          break;
        case 21:
          key = -1;
          break;
        case 22:
          key = -1;
          break;
        case 38:
          key = -1;
          break;
        case 39:
          key = -1;
          break;
        case 43:
          key = -1;
          break;
        case 44:
          key = -1;
          break;
        }
      break;
    case 25:
      switch (colPin) {
        case 18:
          key = KEY_LEFT_SHIFT;
          break;
        case 19:
          key = -1;
          break;
        case 21:
          key = -1;
          break;
        case 22:
          key = -1;
          break;
        case 38:
          key = KEY_RIGHT_SHIFT;
          break;
        case 39:
          key = -1;
          break;
        case 43:
          key = -1;
          break;
        case 44:
          key = -1;
          break;
        }
      break;
    case 26:
      switch (colPin) {
        case 18:
          key = -1;
          break;
        case 19:
          key = KEYPAD_PLUS;
          break;
        case 21:
          key = -1;
          break;
        case 22:
          key = KEY_LEFT;
          break;
        case 38:
          key = KEYPAD_ENTER;
          break;
        case 39:
          key = KEY_HOME;
          break;
        case 43:
          key = KEY_END;
          break;
        case 44:
          key = KEY_UP;
          break;
        }
      break;
    case 40:
      switch (colPin) {
        case 18:
          key = KEYPAD_4;
          break;
        case 19:
          key = KEYPAD_7;
          break;
        case 21:
          key = KEY_NUM_LOCK;
          break;
        case 22:
          key = KEY_DOWN;
          break;
        case 38:
          key = KEYPAD_1;
          break;
        case 39:
          key = KEY_DELETE;
          break;
        case 43:
          key = -1;
          break;
        case 44:
          key = KEY_SPACE;
          break;
        }
      break;
    case 41:
      switch (colPin) {
        case 18:
          key = KEY_LEFT_BRACE;
          break;
        case 19:
          key = KEY_P;
          break;
        case 21:
          key = -1;
          break;
        case 22:
          key = KEY_SLASH;
          break;
        case 38:
          key = KEY_SEMICOLON;
          break;
        case 39:
          key = KEY_MINUS;
          break;
        case 43:
          key = KEY_0;
          break;
        case 44:
          key = KEY_QUOTE;
          break;
        }
      break;
    case 42:
      switch (colPin) {
        case 18:
          key = KEYPAD_5;
          break;
        case 19:
          key = KEYPAD_8;
          break;
        case 21:
          key = KEYPAD_SLASH;
          break;
        case 22:
          key = KEY_RIGHT;
          break;
        case 38:
          key = KEYPAD_2;
          break;
        case 39:
          key = KEY_INSERT;
          break;
        case 43:
          key = -1;
          break;
        case 44:
          key = KEYPAD_0;
          break;
        }
      break;
    case 45:
      switch (colPin) {
        case 18:
          key = KEY_BACKSPACE;
          break;
        case 19:
          key = -1;
          break;
        case 21:
          key = KEY_ENTER;
          break;
        case 22:
          key = KEY_F12;
          break;
        case 38:
          key = KEY_BACKSLASH;
          break;
        case 39:
          key = KEY_F9;
          break;
        case 43:
          key = KEY_F10;
          break;
        case 44:
          key = KEY_F11;
          break;
        }
      break;
  }
  
  return key;
}
Matrix Discovery:

Code: Select all

/*
*** INSTRUCTIONS ***
You need to change the pins array and the pinCount int.
Just follow the comments for each of those fields.

Upload this sucker to your teensy, open a text editor, 
and then start pushing buttons.  You need to keep track of 
what row/column combos show up for which button you are currently pressing.
The format is (row#/column#).
*/

// Array of all physical pin numbers on your particular Teensy
int pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 
              15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 
              27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 
              39, 40, 41, 42, 43, 44, 45};

// The number of physical pins on your particular Teensy
int pinCount = 45;

void setup() {
  resetPins();
}

void loop() {
  scanMatrix();
}

void resetPins() {
  for (int i = 0; i < pinCount; i++) {
    pinMode(pins[i], INPUT_PULLUP);
  }
}

void activateRow(int row) {
  pinMode(row, OUTPUT);
  digitalWrite(row, LOW);
}

boolean checkColumn(int col) {
  boolean returnValue = false;
  pinMode(col, INPUT_PULLUP);
  
  if (digitalRead(col) == LOW) {
    returnValue = true;
  }
  else {
    returnValue = false;
  }
  
  return returnValue;
}

void scanMatrix() {
  for (int i = 0; i < pinCount; i++) {
    int row = pins[i];
    
    for (int z = 0; z < pinCount; z++) {
      int col = pins[z];
      
      resetPins();
      activateRow(row);
      
      if (row != col && checkColumn(col)) {
        printCombo(row, col);
      }
    }
  }
}

void printCombo(int rowPin, int colPin) {
  Keyboard.print("(");
  Keyboard.print(rowPin);
  Keyboard.print(",");
  Keyboard.print(colPin);
  Keyboard.print(")");
}
Last edited by tuffrabit on 08 Jan 2014, 21:54, edited 1 time in total.

Findecanor

17 Dec 2013, 18:30

People have certainly rebuilt existing keyboards before, with a Teensy in it, but most of them have been older keyboards with lots of space in the case for the extra cables needed for connecting the Teensy to the matrix.

If I were you, I would try to use a rainbow ribbon cable - a little bit more work planning how to route it, but the result will be so much more clean and tidy than with individual cables.
I would also replace the LEDs and their resistors completely with ones that I know the values for.

User avatar
Soarer

17 Dec 2013, 18:34

Can you post some pics of the daughter-board etc? Does it unplug?

User avatar
tuffrabit

17 Dec 2013, 18:36

I'll try, but my phone camera is a potato.

User avatar
Soarer

17 Dec 2013, 18:58

Image

User avatar
tuffrabit

17 Dec 2013, 19:00

Got my co-worker to use his iphone. No potato.

Image

User avatar
Soarer

17 Dec 2013, 19:32

Cool, plenty of room for a Teensy or whatever, and a standard connector to connect to as a bonus!

For a full-size keyboard with lock LEDs you'll need more pins than a Teensy 2.0 or Arduino has, but a Teensy++ has plenty.

What might work nicely is to use a right-angled pin header (like that one, but a single row) for the lower row, to mount the Teensy++ and make half the connections, and then a straight pin header + wires to do the rest.

User avatar
tuffrabit

17 Dec 2013, 20:43

Why wouldn't a Teensy 3 have enough pins? The matrix is only 8x18... so 26 pins should be all I need.

User avatar
Soarer

17 Dec 2013, 21:00

Plus 3 pins for the LEDs, if you want them!

Most firmwares are written for the AVR-based Teensies (2.0, ++ 2.0), currently, but I'm sure that the ARM-based 3.0 will be used more in the future.

With an 8x18 matrix, I'm sure there's a lot of unused space... probably some strobes (columns) can be combined, to reduce the number of pins needed. See this Filco Minila mod.

User avatar
tuffrabit

18 Dec 2013, 16:44

Been reading. Brain full of Teensy and breadboard. Can't wait for Christmas to be over so I can start buying myself stuff again.

User avatar
tuffrabit

08 Jan 2014, 20:57

I'm typing this from my Das Teensy keyboard (as I'm calling it now). I have only one problem... I can't get the firmware to read the lock/modifier states from the Serial bus. From what google tells me, it should be possible. I just can't figure it out. Serial.available() always returns 0.

User avatar
Soarer

08 Jan 2014, 21:17

Serial? :?

User avatar
tuffrabit

08 Jan 2014, 21:25

Nevermind I figured it out. I hate it when stuff isn't documented. You have to include usb_private.h and then read a variable named 'keyboard_leds'. It would be great if that was included in the offical documentation. I spent two days trawling through the intergoogletubes to figure that out.

Pics and code coming soon.

User avatar
tuffrabit

08 Jan 2014, 21:55

OP updated with code and pics.

qualidafial

14 Apr 2015, 22:01

Thanks for posting this!

I got a Das Keyboard several months ago, and about a month after I got it (just days outside the Amazon return window) got a shock as I was bringing my hands down to rest on the keys. One of the pins on the daughterboard microcontroller got zapped, and that column of keys starting acting haywire.

I got a a Teensy++ 2.0 and followed your instructions, and have my mechanical keyboard working again!

I tightened up the code, and replaced the key code lookup function with a table to speed things up. Note that my wiring was different so the pin numbers will be different than yours.

Code: Select all

#include <usb_private.h>

#define KEY_MENU 0x76

const int capsLockPin = 26;
const int numLockPin = 25;
const int scrollLockPin = 24;

const int rowCount = 18;
const int colCount = 8;
const int rowPins[18] = {3, 4, 5, 7, 8, 9, 11, 12, 16, 17, 18, 19, 20, 21, 39, 42, 44, 45};
const int colPins[9] = {10, 13, 14, 15, 38, 40, 41, 43};

const int keys[rowCount][colCount] = {
  {KEY_F6,        KEY_K,           KEY_I,           KEY_COMMA,      KEY_8,           KEY_EQUAL,     KEY_RIGHT_BRACE, -1           },
  {-1,            KEY_L,           KEY_O,           KEY_PERIOD,     KEY_9,           KEY_F8,        KEY_F7,          KEY_MENU     },
  {KEY_G,         KEY_F,           KEY_R,           KEY_V,          KEY_4,           KEY_5,         KEY_T,           KEY_B        },
  {-1,            KEY_S,           KEY_W,           KEY_X,          KEY_2,           KEY_F1,        KEY_CAPS_LOCK,   -1           },
  {-1,            -1,              KEY_PAUSE,       KEY_RIGHT_CTRL, KEY_F5,          KEY_LEFT_CTRL, -1,              -1           },
  {KEY_F11,       KEY_BACKSLASH,   -1,              KEY_ENTER,      KEY_F10,         KEY_F9,        KEY_BACKSPACE,   -1           },
  {KEYPAD_0,      KEYPAD_2,        KEYPAD_8,        KEYPAD_SLASH,   -1,              KEY_INSERT,    KEYPAD_5,        KEY_RIGHT    },
  {KEY_SPACE,     KEYPAD_1,        KEYPAD_7,        KEY_NUM_LOCK,   -1,              KEY_DELETE,    KEYPAD_4,        KEY_DOWN     },
  {-1,            KEY_RIGHT_GUI,   -1,              -1,             -1,              -1,            -1,              -1           },
  {-1,            KEY_RIGHT_SHIFT, -1,              -1,             -1,              -1,            KEY_LEFT_SHIFT,  -1           },
  {KEY_ESC,       KEY_A,           KEY_Q,           KEY_Z,          KEY_1,           KEY_TILDE,     KEY_TAB,         -1           },
  {KEY_F4,        KEY_D,           KEY_E,           KEY_C,          KEY_3,           KEY_F2,        KEY_F3,          -1           },
  {KEY_H,         KEY_J,           KEY_U,           KEY_M,          KEY_7,           KEY_6,         KEY_Y,           KEY_N        },
  {KEY_LEFT_ALT,  -1,              KEY_SCROLL_LOCK, -1,             KEY_PRINTSCREEN, -1,            -1,              KEY_RIGHT_ALT},
  {KEY_QUOTE,     KEY_SEMICOLON,   KEY_P,           -1,             KEY_0,           KEY_MINUS,     KEY_LEFT_BRACE,  KEY_SLASH    },
  {KEYPAD_PERIOD, KEYPAD_3,        KEYPAD_9,        KEYPAD_ASTERIX, KEY_PAGE_DOWN,   KEY_PAGE_UP,   KEYPAD_6,        KEYPAD_MINUS },
  {-1,            -1,              -1,              -1,             -1,              -1,            KEY_LEFT_GUI,    -1,          },
  {KEY_UP,        KEYPAD_ENTER,    KEYPAD_PLUS,     -1,             KEY_END,         KEY_HOME,      -1,              KEY_LEFT     }  
};

void setup() {
  Keyboard.begin();

  for (int ri = 0; ri < rowCount; ri++) {
    int row = rowPins[ri];
    pinMode(row, OUTPUT);
    digitalWrite(row, HIGH);
  }
   for (int i = 0; i < colCount; i++) {
    int col = colPins[i];
    pinMode(col, INPUT_PULLUP);
  }

  pinMode(numLockPin, OUTPUT);
  pinMode(capsLockPin, OUTPUT);
  pinMode(scrollLockPin, OUTPUT);
}

void loop() {
  scanMatrix();
  digitalWrite(numLockPin, isNumOn());
  digitalWrite(capsLockPin, isCapsOn());
  digitalWrite(scrollLockPin, isScrollOn());
}

void scanMatrix() {
  for (int i = 0; i < rowCount; i++) {
    int row = rowPins[i];

    digitalWrite(row, LOW);

    for (int j = 0; j < colCount; j++) {
      int col = colPins[j];
      int key = keys[i][j];

      if (digitalRead(col) == LOW) {
        Keyboard.press(key);
      }
      else {
        Keyboard.release(key);
      }
    }

    digitalWrite(row, HIGH);
  }
}

boolean isScrollOn(void) {
  return (keyboard_leds & 4) == 4;
}

boolean isCapsOn(void) {
  return (keyboard_leds & 2) == 2;
}

boolean isNumOn(void) {
  return (keyboard_leds & 1) == 1;
}

User avatar
tuffrabit

14 Apr 2015, 22:47

Glad it worked for you buddy. One thing I've been meaning to go back and do is implement some software debouncing. On occation I'll get double input for one key press, it's not a ton or I would've done it sooner. I'll post the changes here if/when I do it.

qualidafial

15 Apr 2015, 17:26

I was surprised not to get any double keypresses myself. I had assumed that the Arduino Keyboard class must have some debounce built into it.

Have you had issues with the keyboard needing to be unplugged / replugged when the computer it is attached to reboots?

User avatar
tuffrabit

15 Apr 2015, 17:44

The double key press happens only occasionally. It's not a huge problem.

And no, I've not had the unplug/replug issue on reboot.

Post Reply

Return to “Workshop”