XIM Community

Das Keyboard + teensy  (Read 5437 times)

Offline tuffrabit

  • MVP
  • *
  • Posts: 7352
    • View Profile
    • tuffrabit's Twitch.tv channel
  • Gamertag: TuFFrabit
Das Keyboard + teensy
« on: 10:22 AM - 12/18/13 »
This is as much a tutorial as it is a please help me thread.

I bought a Das Keyboard Model S Ultimate from Ebay.  It was described as "broken / for parts" so it only cost me $8.  I figured I had a pretty good chance of fixing it, as long as the controller PCB wasn't dead.  Well, it turns out that's exactly the problem.  The USB cables are good (tested with ohm meter), the matrix PCB and connections are fine, and there are no visibly loose/cold solder joints anywhere.  Best I can tell is the IC that controls the matrix is just dead.  Das Keyboard won't sell me a new factory controller PCB, so I am stuck with this.

My plan is to replace the controller PCB with a Teensy board.  The Das matrix is a standard 8x18, so the Teensy 2.0++ I think has enough I/O pins to do this and run three LEDs.

Right now this is all in my head, I don't have a Teensy yet.  Probably will start this in earnest sometime after Christmas.  Any comments or suggestions are welcome.

*EDIT

All done.  Here's pics and code.

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

Main KB Code:
Code: [Select]
#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]
/*
*** 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 Edit: 02:50 PM - 01/08/14 by tuffrabit »


XIM4 Anti Aim-assist curve: http://xim4.com/community/index.php?topic=31309.msg400143#msg400143
All you ever wanted to know about headsets and chat for the 360: http://www.xim3.com/community/index.php?topic=16461.0

Offline Od1n

  • Global Moderator
  • MVP
  • *
  • Posts: 30729
    • View Profile
Re: Das Keyboard + teensy
« Reply #1 on: 10:40 AM - 12/18/13 »
someone call w11ce, the teensy expert
PS4 & X1 | C: MS | XIM4 | FW: the latest ofc! :) | Mon: BenQ XL2420Z | Pad: Zowie G-SR | KB: Ducky DK1008L Cherry Red O-Rings| A: AKG K701 Creative X7 Antlion Modmic | M: Zowie EC1-A | D: 3200 | P: 1000hz | BF4 4 HIP: 35 ADS: 20

My Fortnite Creator Code: beamimpact
Check out my Youtube Channel: XIMGameplay

Offline abc123

  • MVP
  • *
  • Posts: 2958
  • It's as easy as ABC 123
    • View Profile
    • unofficial xim api cloud for configs
  • Gamertag: Dr Donkey Kong
Re: Das Keyboard + teensy
« Reply #2 on: 12:57 PM - 12/18/13 »
The Teensy has 21 digital I/O pins and the Teensy++ has 38

meaning with a keymatrix...  you can take 38/2 = 19 and then that's the number of rows and columns you get so...

19*19 (key matrix) = 361 keys

you'll code it using the following library:
http://www.pjrc.com/teensy/td_libs_Keypad.html

Edit: just saw the 3 LEDs part...

38 (digital I/O) - 3 = 35 / 2 = 17.5 (floor) = 17
17 (rows) * 17 (columns) = 289 keys

so mathematically it looks fine to me.  If you want to re-use their PCB you'd need to know the number of columns and rows they are using but they are interchangeable...


17 rows with 17 columns
can also be
19 rows with 15 columns...

Doesn't have to be even numbers.  the number of keys is the same with multiplication was all i was showing above...But i image this will work fine.
« Last Edit: 01:01 PM - 12/18/13 by abc123 »

Offline tuffrabit

  • MVP
  • *
  • Posts: 7352
    • View Profile
    • tuffrabit's Twitch.tv channel
  • Gamertag: TuFFrabit
Re: Das Keyboard + teensy
« Reply #3 on: 01:42 PM - 12/18/13 »
Yeah, I'll reuse the original matrix PCB.  It's 18x8, the hard part is tracing the copper and figuring out the rows and columns.


XIM4 Anti Aim-assist curve: http://xim4.com/community/index.php?topic=31309.msg400143#msg400143
All you ever wanted to know about headsets and chat for the 360: http://www.xim3.com/community/index.php?topic=16461.0

Offline abc123

  • MVP
  • *
  • Posts: 2958
  • It's as easy as ABC 123
    • View Profile
    • unofficial xim api cloud for configs
  • Gamertag: Dr Donkey Kong
Re: Das Keyboard + teensy
« Reply #4 on: 01:55 PM - 12/18/13 »
Yeah, I'll reuse the original matrix PCB.  It's 18x8, the hard part is tracing the copper and figuring out the rows and columns.

Nah just wire them in then code them one way, if it doesn't work reverse your code tada!

Offline tuffrabit

  • MVP
  • *
  • Posts: 7352
    • View Profile
    • tuffrabit's Twitch.tv channel
  • Gamertag: TuFFrabit
Re: Das Keyboard + teensy
« Reply #5 on: 02:15 PM - 12/18/13 »
Um ok... the programming side just got crazy easier.

http://www.humblehacker.com/keyboard/

Has a matrix discovery mode.


XIM4 Anti Aim-assist curve: http://xim4.com/community/index.php?topic=31309.msg400143#msg400143
All you ever wanted to know about headsets and chat for the 360: http://www.xim3.com/community/index.php?topic=16461.0

Offline W11cE

  • MVP
  • *
  • Posts: 1519
    • View Profile
Re: Das Keyboard + teensy
« Reply #6 on: 02:41 PM - 12/18/13 »
Well, looks like im not needed here. Ill just continue...
One thing: Teensy 3.0 or 3.1
-33 I/O pins
-More power!
-More bits!
-My custom usb types work with it (if you want to add few joysticks to sides and use those with xim ;))

Also my teensy support is focused mainly on 3.0 and 3.1.

Offline abc123

  • MVP
  • *
  • Posts: 2958
  • It's as easy as ABC 123
    • View Profile
    • unofficial xim api cloud for configs
  • Gamertag: Dr Donkey Kong
Re: Das Keyboard + teensy
« Reply #7 on: 02:54 PM - 12/18/13 »
Um ok... the programming side just got crazy easier.

http://www.humblehacker.com/keyboard/

Has a matrix discovery mode.

Nice, glad to see you are seeing the light of cake mode coming into view.

Well, looks like im not needed here. Ill just continue...
One thing: Teensy 3.0 or 3.1
-33 I/O pins
-More power!
-More bits!
-My custom usb types work with it (if you want to add few joysticks to sides and use those with xim ;))

Also my teensy support is focused mainly on 3.0 and 3.1.

you good sir are always needed :)

Offline tuffrabit

  • MVP
  • *
  • Posts: 7352
    • View Profile
    • tuffrabit's Twitch.tv channel
  • Gamertag: TuFFrabit
Re: Das Keyboard + teensy
« Reply #8 on: 01:49 PM - 12/26/13 »
Teensy ordered and on its way.


XIM4 Anti Aim-assist curve: http://xim4.com/community/index.php?topic=31309.msg400143#msg400143
All you ever wanted to know about headsets and chat for the 360: http://www.xim3.com/community/index.php?topic=16461.0

Offline abc123

  • MVP
  • *
  • Posts: 2958
  • It's as easy as ABC 123
    • View Profile
    • unofficial xim api cloud for configs
  • Gamertag: Dr Donkey Kong
Re: Das Keyboard + teensy
« Reply #9 on: 01:59 PM - 12/26/13 »
Teensy ordered and on its way.

wooohooooo :) it's getting hot in here, so take off all your clothes

Offline tuffrabit

  • MVP
  • *
  • Posts: 7352
    • View Profile
    • tuffrabit's Twitch.tv channel
  • Gamertag: TuFFrabit
Re: Das Keyboard + teensy
« Reply #10 on: 04:18 PM - 01/01/14 »
Sigh... I can't get that HumbleHacker thingy to build.  I hate bad platform ports.  It's obvious the guy wrote it for Linux and gcc, not windows.  I'm waiting on him to respond to my troubleshooting email.

In the mean time I sat down with an ohm meter to try and trace the matrix.  About three hours in and I was able to find exactly two key pinouts... YAY ME!


XIM4 Anti Aim-assist curve: http://xim4.com/community/index.php?topic=31309.msg400143#msg400143
All you ever wanted to know about headsets and chat for the 360: http://www.xim3.com/community/index.php?topic=16461.0

Offline tuffrabit

  • MVP
  • *
  • Posts: 7352
    • View Profile
    • tuffrabit's Twitch.tv channel
  • Gamertag: TuFFrabit
Re: Das Keyboard + teensy
« Reply #11 on: 04:15 PM - 01/02/14 »
Screw the humblehacker firmware and screw the ohm meter.  I wrote my own matrix discovery firmware.  I always love being a programmer, but sometimes I REALLY love being a programmer.


XIM4 Anti Aim-assist curve: http://xim4.com/community/index.php?topic=31309.msg400143#msg400143
All you ever wanted to know about headsets and chat for the 360: http://www.xim3.com/community/index.php?topic=16461.0

Offline abc123

  • MVP
  • *
  • Posts: 2958
  • It's as easy as ABC 123
    • View Profile
    • unofficial xim api cloud for configs
  • Gamertag: Dr Donkey Kong
Re: Das Keyboard + teensy
« Reply #12 on: 12:11 PM - 01/03/14 »
Screw the humblehacker firmware and screw the ohm meter.  I wrote my own matrix discovery firmware.  I always love being a programmer, but sometimes I REALLY love being a programmer.

@#$% straight, I love our attitudes in general

Offline tuffrabit

  • MVP
  • *
  • Posts: 7352
    • View Profile
    • tuffrabit's Twitch.tv channel
  • Gamertag: TuFFrabit
Re: Das Keyboard + teensy
« Reply #13 on: 12:15 PM - 01/03/14 »
i'm writing on the das with the teensy right now.  no caps yet because i haven't programmed the modifier keys yet.  lots of progress though.

i'll have pics of the hardware side of it up soon.


XIM4 Anti Aim-assist curve: http://xim4.com/community/index.php?topic=31309.msg400143#msg400143
All you ever wanted to know about headsets and chat for the 360: http://www.xim3.com/community/index.php?topic=16461.0

Offline abc123

  • MVP
  • *
  • Posts: 2958
  • It's as easy as ABC 123
    • View Profile
    • unofficial xim api cloud for configs
  • Gamertag: Dr Donkey Kong
Re: Das Keyboard + teensy
« Reply #14 on: 12:23 PM - 01/03/14 »
i'm writing on the das with the teensy right now.  no caps yet because i haven't programmed the modifier keys yet.  lots of progress though.

i'll have pics of the hardware side of it up soon.

good @#$% dude