Close

Blackberry Curve USB keyboard

A project log for weekend novelty projects

minimal advanced planning. built from stuff i already have around the shop. typically not very useful. generally completed in one weekend.

zakqwyzakqwy 09/29/2017 at 19:070 Comments

[hooray for vertical videos... ]

I had a Blackberry Curve a number of years ago and liked the keyboard. Yay tiny buttons! Sadly, I didn't see @WooDWorkeR's  #PIMP (Personal Information Manager & Pager)  project until I'd already torn into my Model 8530; the Q10 keyboard used there would have made this project a bit less tedious. Ah well, I like tedious and this bodge-tastic project does keep half a Blackberry out of the landfill.

Step One was to tear apart the phone (kindly held together with relatively common T6s and zero glue) and hot air everything until my desk was covered in parts. I wasn't sure this was going anywhere so I didn't get any pictures, and the desoldering effort was not insignificant; nearly all of the Curve's electronics were sealed under custom-sized RF cans.

My initial hope was to easily extract a nice keyboard module that I could reverse engineer and use by itself. Sadly, this phone uses a single main board for everything. After a bit of study, I found a pair mysterious DFN16 components on my desk that looked hopeful:

The chips are both marked '2398P' which (of course) didn't turn up anything on Google, but the large number of pins and adjacent resistor networks (pullups?) made me think they could be some kind of matrix controllers. I tacked on a few bits of magnet wire, bridging a bunch of pins each time, and took a few continuity readings -- confirmed! Time to break the footprint out!

The picture above is after final assembly, hence the layers of Kapton (an attempt at strain relief) and the Teensy control board on the left. In any case, tacking 34-AWG magnet wires onto 16 0.5mm spaced pads certainly isn't impossible, but it does take a steady hand and a great deal of patience. I actually considered soldering on a pair of 8-pin FFC connectors I had left over from #TP-BMP, but soon realized that one of the connectors would get in the way of the other and I don't have any flat cable on hand. Assuming the space between the two footprints is a multiple of 0.5mm, a decent solution for anyone else attempting this could be to use a 17 or 18-pin FFC connector. You could probably even tack the support tabs onto the RF can outline or some of the other desoldered footprints for extra mechanical strength.

The last step, at least for now, was to remove the IR touch 'mouse' and replace it with a SHIFT/ALT (as in, alternate button function) indicator. I think reverse engineering the sensor could have been possible, but it used one of those fussy double-row flat flex connectors that really beg for a custom PCB (and more time than I'm willing to spend on a hacky bodgy project like this). So I installed a pair of LEDs, using a bit of right angle header and the Teensy's uUSB connector for support:

... and glued a bit of diffusing material, a paper label, and a black light shield into the other side of the case:

The effect isn't ideal; it's a bit tough to read SHIFT and ALT due to the diffusing material and small font, but at least the demarcation between indicators is sharp:

Re-assembly was simple enough (no glue! T6 screws! thank you RIM!) after trimming away a bit of excess plastic so the Teensy would fit. I may add a backing plate as the wiring is somewhat exposed, but for now it's probably time to move on to more productive pursuits:

Firmware is simple enough, thanks to the excellent USB HID support native to the #Teensy 3.0 & 3.1 & 3.2 & 3.5 & 3.6 platform. I used a few arrays and implemented a version of @Elliot Williams's pattern-based debounce scheme. Adding things like rollover and other buttons (Ctrl, Tab, etc) would be simple enough and may happen someday. The eight rows (r0 - r7) and eight columns (c0 - c7) refer to the pads from left to right; all keys were figured out using a serial terminal and a beer on the couch. Code (no more text beyond this block, so feel free to move on now if you wish...):

const int r0 = 11;
const int r1 = 10;
const int r2 = 9;
const int r3 = 8;
const int r4 = 14;
const int r5 = 15;
const int r6 = 16;
const int r7 = 17;
const int c0 = 18;
const int c1 = 19;
const int c2 = 20;
const int c3 = 21;
const int c4 = 7;
const int c5 = 6;
const int c6 = 5;
const int c7 = 4;
const int SHIFT = 0;
const int ALT = 3;
void setup() {
  pinMode(r0, OUTPUT);
  pinMode(r1, OUTPUT);
  pinMode(r2, OUTPUT);
  pinMode(r3, OUTPUT);
  pinMode(r4, OUTPUT);
  pinMode(r5, OUTPUT);
  pinMode(r6, OUTPUT);
  pinMode(r7, OUTPUT);
  digitalWrite(r0, LOW);
  digitalWrite(r1, LOW);
  digitalWrite(r2, LOW);
  digitalWrite(r3, LOW);
  digitalWrite(r4, LOW);
  digitalWrite(r5, LOW);
  digitalWrite(r6, LOW);
  digitalWrite(r7, LOW);
  pinMode(c0, INPUT_PULLDOWN);
  pinMode(c1, INPUT_PULLDOWN);
  pinMode(c2, INPUT_PULLDOWN);
  pinMode(c3, INPUT_PULLDOWN);
  pinMode(c4, INPUT_PULLDOWN);
  pinMode(c5, INPUT_PULLDOWN);
  pinMode(c6, INPUT_PULLDOWN);
  pinMode(c7, INPUT_PULLDOWN);
  pinMode(SHIFT, OUTPUT);
  pinMode(ALT, OUTPUT);
  digitalWrite(SHIFT, LOW);
  digitalWrite(ALT, LOW);
  button_array_init();
  Serial.begin(38400);
  delay(10);
  Serial.println("start");
}
uint8_t button_array[38]; // raw button status with 8 bits of history for debounce
uint8_t output_array[38]; // output toggle: 1 = print this character
//  a  b  c  d  e  f  g  h  i  j  k  l  m  n  o  p  q  r  s  t  u  v  w  x  y  z
//  0  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
//  LL  LR  RL  RR  alt  space  0  rcaps  lcaps  del  $  enter
//  26  27  28  29  30   31     32 33     34     35   36 37
void loop() {   
 get_buttons();  
 check_for_press();
 shift_array();
 output_chars();
 delay(1);
}
uint8_t shift_active = 0; //1 when shift is on
uint8_t alt_active = 0; //1 when alt is on
void output_chars() {
   if (output_array[0] == 1) { //a
    if (alt_active) {
      Keyboard.print("*");
    }
    else if (shift_active) {
      Keyboard.print("A");
    }
    else {
      Keyboard.print("a");
    }
    output_array[0] = 0;
  }
  if (output_array[1] == 1) { //b
    if (alt_active) {
      Keyboard.print("!");
    }
    else if (shift_active) {
      Keyboard.print("B");
    }
    else {
      Keyboard.print("b");
    }
    output_array[1] = 0;
  }
  if (output_array[2] == 1) { //c
    if (alt_active) {
      Keyboard.print("9");
    }
    else if (shift_active) {
      Keyboard.print("C");
    }
    else {
      Keyboard.print("c");
    }
    output_array[2] = 0;
  }
  if (output_array[3] == 1) { //d
    if (alt_active) {
      Keyboard.print("5");
    }
    else if (shift_active) {
      Keyboard.print("D");
    }
    else {
      Keyboard.print("d");
    }
    output_array[3] = 0;
  }
  if (output_array[4] == 1) { //e
    if (alt_active) {
      Keyboard.print("2");
    }
    else if (shift_active) {
      Keyboard.print("E");
    }
    else {
      Keyboard.print("e");
    }
    output_array[4] = 0;
  }
  if (output_array[5] == 1) { //f
    if (alt_active) {
      Keyboard.print("6");
    }
    else if (shift_active) {
      Keyboard.print("F");
    }
    else {
      Keyboard.print("f");
    }
    output_array[5] = 0;
  }
  if (output_array[6] == 1) { //g
    if (alt_active) {
      Keyboard.print("/");
    }
    else if (shift_active) {
      Keyboard.print("G");
    }
    else {
      Keyboard.print("g");
    }
    output_array[6] = 0;
  }
  if (output_array[7] == 1) { //h
    if (alt_active) {
      Keyboard.print(":");
    }
    else if (shift_active) {
      Keyboard.print("H");
    }
    else {
      Keyboard.print("h");
    }
    output_array[7] = 0;
  }
  if (output_array[8] == 1) { //i
    if (alt_active) {
      Keyboard.print("-");
    }
    else if (shift_active) {
      Keyboard.print("I");
    }
    else {
      Keyboard.print("i");
    }
    output_array[8] = 0;
  }
  if (output_array[9] == 1) { //j
    if (alt_active) {
      Keyboard.print(";");
    }
    else if (shift_active) {
      Keyboard.print("J");
    }
    else {
      Keyboard.print("j");
    }
    output_array[9] = 0;
  }
  if (output_array[10] == 1) { //k
    if (alt_active) {
      Keyboard.print("'");
    }
    else if (shift_active) {
      Keyboard.print("K");
    }
    else {
      Keyboard.print("k");
    }
    output_array[10] = 0;
  }
  if (output_array[11] == 1) { //l
    if (alt_active) {
      Keyboard.print("\"");
    }
    else if (shift_active) {
      Keyboard.print("L");
    }
    else {
      Keyboard.print("l");
    }
    output_array[11] = 0;
  }
  if (output_array[12] == 1) { //m
    if (alt_active) {
      Keyboard.print(".");
    }
    else if (shift_active) {
      Keyboard.print("M");
    }
    else {
      Keyboard.print("m");
    }
    output_array[12] = 0;
  }
  if (output_array[13] == 1) { //n
    if (alt_active) {
      Keyboard.print(",");
    }
    else if (shift_active) {
      Keyboard.print("N");
    }
    else {
      Keyboard.print("n");
    }
    output_array[13] = 0;
  }
  if (output_array[14] == 1) { //o
    if (alt_active) {
      Keyboard.print("+");
    }
    else if (shift_active) {
      Keyboard.print("O");
    }
    else {
      Keyboard.print("o");
    }
    output_array[14] = 0;
  }
  if (output_array[15] == 1) { //p
    if (alt_active) {
      Keyboard.print("@");
    }
    else if (shift_active) {
      Keyboard.print("P");
    }
    else {
      Keyboard.print("p");
    }
    output_array[15] = 0;
  }
  if (output_array[16] == 1) { //q
    if (alt_active) {
      Keyboard.print("#");
    }
    else if (shift_active) {
      Keyboard.print("Q");
    }
    else {
      Keyboard.print("q");
    }
    output_array[16] = 0;
  }
  if (output_array[17] == 1) { //r
    if (alt_active) {
      Keyboard.print("3");
    }
    else if (shift_active) {
      Keyboard.print("R");
    }
    else {
      Keyboard.print("r");
    }
    output_array[17] = 0;
  }
  if (output_array[18] == 1) { //s
    if (alt_active) {
      Keyboard.print("4");
    }
    else if (shift_active) {
      Keyboard.print("S");
    }
    else {
      Keyboard.print("s");
    }
    output_array[18] = 0;
  }
  if (output_array[19] == 1) { //t
    if (alt_active) {
      Keyboard.print("(");
    }
    else if (shift_active) {
      Keyboard.print("T");
    }
    else {
      Keyboard.print("t");
    }
    output_array[19] = 0;
  }
  if (output_array[20] == 1) { //u
    if (alt_active) {
      Keyboard.print("_");
    }
    else if (shift_active) {
      Keyboard.print("U");
    }
    else {
      Keyboard.print("u");
    }
    output_array[20] = 0;
  }
  if (output_array[21] == 1) { //v
    if (alt_active) {
      Keyboard.print("?");
    }
    else if (shift_active) {
      Keyboard.print("V");
    }
    else {
      Keyboard.print("v");
    }
    output_array[21] = 0;
  }
  if (output_array[22] == 1) { //w
    if (alt_active) {
      Keyboard.print("1");
    }
    else if (shift_active) {
      Keyboard.print("W");
    }
    else {
      Keyboard.print("w");
    }
    output_array[22] = 0;
  }
  if (output_array[23] == 1) { //x
    if (alt_active) {
      Keyboard.print("8");
    }
    else if (shift_active) {
      Keyboard.print("X");
    }
    else {
      Keyboard.print("x");
    }
    output_array[23] = 0;
  }
  if (output_array[24] == 1) { //y
    if (alt_active) {
      Keyboard.print(")");
    }
    else if (shift_active) {
      Keyboard.print("Y");
    }
    else {
      Keyboard.print("y");
    }
    output_array[24] = 0;
  }
  if (output_array[25] == 1) { //z
    if (alt_active) {
      Keyboard.print("7");
    }
    else if (shift_active) {
      Keyboard.print("Z");
    }
    else {
      Keyboard.print("z");
    }
    output_array[25] = 0;
  }
  if (output_array[26] == 1) { //LL
    Keyboard.press(KEY_LEFT);
    Keyboard.release(KEY_LEFT);
    output_array[26] = 0;
  }
  if (output_array[27] == 1) { //LR
    Keyboard.press(KEY_UP);
    Keyboard.release(KEY_UP);
    output_array[27] = 0;
  }
  
  if (output_array[28] == 1) { //RL
    Keyboard.press(KEY_DOWN);
    Keyboard.release(KEY_DOWN);
    output_array[28] = 0;
  }
  if (output_array[29] == 1) { //RR
    Keyboard.press(KEY_RIGHT);
    Keyboard.release(KEY_RIGHT);
    output_array[29] = 0;
  }
  if (output_array[30] == 1) { //alt
    if (alt_active) {
      alt_active = 0;
      digitalWrite(ALT, LOW);
    }
    else {
      alt_active = 1;
      digitalWrite(ALT, HIGH);
    }
    output_array[30] = 0;
  }
  if (output_array[31] == 1) { //space
    Keyboard.print(" ");
    output_array[31] = 0;
  }
  if (output_array[32] == 1) { //0
    Keyboard.print("0");
    output_array[32] = 0;
  }
    
  if (output_array[33] == 1) { //rcaps
    if (shift_active) {
      shift_active = 0;
      digitalWrite(SHIFT, LOW);
    }
    else {
      shift_active = 1;
      digitalWrite(SHIFT, HIGH);
    }
    output_array[33] = 0;
  }
  if (output_array[34] == 1) { //lcaps
    if (shift_active) {
      shift_active = 0;
      digitalWrite(SHIFT, LOW);
    }
    else {
      shift_active = 1;
      digitalWrite(SHIFT, HIGH);
    }
    output_array[34] = 0;
  }
  if (output_array[35] == 1) { //del (does not work, shift + enter is now backspace)
    if (shift_active) {
      Keyboard.press(KEY_DELETE);
      Keyboard.release(KEY_DELETE);
    }
    else {
      Keyboard.press(KEY_BACKSPACE);
      Keyboard.release(KEY_BACKSPACE);
    }
    output_array[35] = 0;
  }
  if (output_array[36] == 1) { //space
    Keyboard.print("$");
    output_array[36] = 0;
  }
  if (output_array[37] == 1) { //enter
    if (shift_active) {
      Keyboard.press(KEY_BACKSPACE);
      Keyboard.release(KEY_BACKSPACE);
    }
    else {
      Keyboard.press(KEY_ENTER);
      Keyboard.release(KEY_ENTER);
    }
    output_array[37] = 0;
  }  
 
}
void check_for_press() {
  // elliot williams' pattern-based debounce scheme
  uint8_t i;
  for (i=0;i<38;i++) {
    if ((button_array[i] & 0b11000111) == 0b00000111) { 
      output_array[i] = 1;
      button_array[i] = 0b11111111;
    }
  }
}
void button_array_init() {
  uint8_t i;
  for (i=0;i<38;i++) {
    button_array[i] = 0;
  }
}
void get_buttons() {
  digitalWrite(r2, HIGH);
  if (digitalRead(c2)) {
    button_array[28] |= 1; //RL  
  }
  if (digitalRead(c3)) {
    button_array[27] |= 1; //LR
  }
  if (digitalRead(c4)) {
    button_array[29] |= 1; //RR
  }
  if (digitalRead(c5)) {
    button_array[26] |= 1; //LL
  }
  if (digitalRead(c6)) {
    button_array[0] |= 1; //a
  }
  digitalWrite(r2, LOW);
  digitalWrite(r3, HIGH);
  if (digitalRead(c2)) {
    button_array[30] |= 1; //alt
  }
  if (digitalRead(c4)) {
    button_array[31] |= 1; //space
  }
  if (digitalRead(c5)) {
    button_array[32] |= 1; //0
  }
  if (digitalRead(c6)) {
    button_array[16] |= 1; //q
  }
  if (digitalRead(c7)) {
    button_array[22] |= 1; //w
  }
  digitalWrite(r3, LOW);
  digitalWrite(r4, HIGH);
  if (digitalRead(c1)) {
    button_array[33] |= 1; //rcaps
  }
  if (digitalRead(c2)) {
    button_array[21] |= 1; //v
  }
  if (digitalRead(c3)) {
    button_array[19] |= 1; //t
  }
  if (digitalRead(c4)) {
    button_array[2] |= 1; //c
  }
  if (digitalRead(c5)) {
    button_array[5] |= 1; //f
  }
  if (digitalRead(c6)) {
    button_array[17] |= 1; //r
  }
  if (digitalRead(c7)) {
    button_array[6] |= 1; //g
  }
  digitalWrite(r4, LOW);
  digitalWrite(r5, HIGH);
  if (digitalRead(c1)) {
    button_array[15] |= 1; //p
  }
  if (digitalRead(c2)) {
    button_array[23] |= 1; //x
  }
  if (digitalRead(c3)) {
    button_array[3] |= 1; //d
  }
  if (digitalRead(c4)) {
    button_array[25] |= 1; //z
  }
  if (digitalRead(c5)) {
    button_array[34] |= 1; //lcaps
  }
  if (digitalRead(c6)) {
    button_array[4] |= 1; //e
  }
  if (digitalRead(c7)) {
    button_array[18] |= 1; //s
  }
  digitalWrite(r5, LOW);
  digitalWrite(r6, HIGH);
  if (digitalRead(c1)) {
    button_array[35] |= 1; //del
  }
  if (digitalRead(c2)) {
    button_array[36] |= 1; //$
  }
  if (digitalRead(c3)) {
    button_array[8] |= 1; //i
  }
  if (digitalRead(c4)) {
    button_array[12] |= 1; //m
  }
  if (digitalRead(c5)) {
    button_array[10] |= 1; //k
  }
  if (digitalRead(c6)) {
    button_array[14] |= 1; //o
  }
  if (digitalRead(c7)) {
    button_array[11] |= 1; //l
  }
  digitalWrite(r6, LOW);
  digitalWrite(r7, HIGH);
  if (digitalRead(c1)) {
    button_array[37] |= 1; //enter
  }
  if (digitalRead(c2)) {
    button_array[1] |= 1; //b
  }
  if (digitalRead(c3)) {
    button_array[24] |= 1; //y
  }
  if (digitalRead(c4)) {
    button_array[13] |= 1; //n
  }
  if (digitalRead(c5)) {
    button_array[9] |= 1; //j
  }
  if (digitalRead(c6)) {
    button_array[20] |= 1; //u
  }
  if (digitalRead(c7)) {
    button_array[7] |= 1; //h
  }
  digitalWrite(r7, LOW);
}
void shift_array() {
  uint8_t i;
  for (i=0;i<38;i++) {
    button_array[i] = button_array[i] << 1;
  }
}
 

Discussions