Keyboard/mouse controller for ThinkPad X41 keyboard

I would like to connect a ThinkPad X41 keyboard with TrackPoint via USB, with the help of an Arduino. Some facts:

  • I've never used an Arduino before, and neither am I an electronics guy. While I know some basics, and I've used a soldering iron in the past, I am mostly a programmer, capable of writing code close to hardware.
  • The connector has 40 pins. (I am still looking for a suitable socket.)
  • The TrackPoint (mouse) on the keyboard already generates PS/2 signals.
  • I want to be able to configure the TrackPoint using proprietary PS/2 commands directly from the Arduino, on certain key combinations. Made up example: FN-F2 = increase sensitivity

Questions, under the assumption that this is feasible:

  • Which Arduino should I chose?
  • I saw that some Arduinos, such as the Leonardo, can be used as HID via USB. However, when looking at the library functions, I see for example no method to send mouse scroll commands. Could I add methods myself, i.e. somehow bit-bang the USB connection?
  • How do I get started? My idea is to put something in between the keyboard and the laptop, and then use a multimeter to measure the signals that get emitted when a key is pressed. Only afterwards, I would connect the keyboard to the Arduino.

(There are ready-made ThinkPad keyboards for USB, but I'm not interested in those.)

Which Arduino should I chose?

Probably a Mega2560 to read the whole Keyboard. You can program the ATmega16U2 on the board to do the same as the Leonardo is doing, you just have to define a serial protocol between them. You can try a Leonardo with some additional hardware (shift-in/-out registers may work) but be prepared that this gets a bit more complex.

However, when looking at the library functions, I see for example no method to send mouse scroll commands.

Scroll commands are button clicks with button numbers greater 3.

How do I get started? My idea is to put something in between the keyboard and the laptop, and then use a multimeter to measure the signals that get emitted when a key is pressed. Only afterwards, I would connect the keyboard to the Arduino.

My guess is that a multimeter isn't fast enough. You need something like a scope or logic analyzer to get a knowledge of what's going on. It's probably some kind of matrix with row and column pins. You can check that with a multimeter. If my guess is correct two of the pins are connected if you press a key.

pylon: Probably a Mega2560 to read the whole Keyboard. You can program the ATmega16U2 on the board to do the same as the Leonardo is doing, you just have to define a serial protocol between them.

Thanks for the suggestion. Just to make sure: What is "them", the ATmega2560 and the ATmega16U2?

Is writing that serial communication straight forward? Has someone done this before?

You can try a Leonardo with some additional hardware (shift-in/-out registers may work) but be prepared that this gets a bit more complex.

Not interested.

How about a Due? According to product description: "The Native USB port is connected to the SAM3X. It allows for serial (CDC) communication over USB. This provides a serial connection to the Serial Monitor or other applications on your computer. It also enables the Due to emulate a USB mouse or keyboard to an attached computer. To use these features, see the Mouse and Keyboard library reference pages."

My guess is that a multimeter isn't fast enough.

I assume, though, that keeping a button pressed causes a current to flow, which can be measured. I don't expect there to be just some quick signal.

You need something like a scope

Don't have access to one, and it's expensive.

In the worst case, I have to connect the keyboard directly to the Arduino, right from the beginning.

Thanks for the suggestion. Just to make sure: What is "them", the ATmega2560 and the ATmega16U2?

Yes.

Is writing that serial communication straight forward? Has someone done this before?

For exactly the same purpose? To my knowledge: no. For parts of your problem (just keyboard): yes.

How about a Due?

The Due is able to do that but might be a bit expensive for the task. And you always have to keep in mind that the Due runs on 3V3 and not 5V.

I don't expect there to be just some quick signal.

As I wrote, I expect it to be wired as a matrix, so the controller will turn on one row, read all column inputs and turns that row off again to turn on the next row. This will be fast enough to get the fastest key strokes, do you think your multimeter is fast enough?

In the worst case, I have to connect the keyboard directly to the Arduino, right from the beginning.

As I already wrote: you can use your multimeter but not when the keyboard is connected to the notebook. Use it in the "beep" mode (connection check) and find out how the matrix is wired (which are the rows, which are the columns). It's a tedious job but if you connect the Arduino from the beginning chances are quite high you fry your GPIOs.

pylon: The Due is able to do that but might be a bit expensive for the task.

Well, the first goal is to make it work at all. If later other people are interested in the solution, then it may make sense to try it with a cheaper Arduino.

As I wrote, I expect it to be wired as a matrix, so the controller will turn on one row, read all column inputs and turns that row off again to turn on the next row.

Phew, that would be complicated. I was expecting that one key press corresponds to current flowing over two pins, one for column, the other one for row.

I wonder whether it would be easier to simply get a dedicated keyboard controller, such as the KeyWarrior. I could let that convert keyboard signals to PS/2, and then pipe them into the Arduino, together with the mouse PS/2 signals. Then the Arduino would take care of configuring the TrackPoint (mouse) on key press, and for converting PS/2 signals to USB.

It's a tedious job but if you connect the Arduino from the beginning chances are quite high you fry your GPIOs.

Thanks for the warning!

Phew, that would be complicated. I was expecting that one key press corresponds to current flowing over two pins, one for column, the other one for row.

That would be a problem if two (or more) keys are hit together. You cannot distinguish which are actually pushed down.

I wonder whether it would be easier to simply get a dedicated keyboard controller, such as the KeyWarrior. I could let that convert keyboard signals to PS/2, and then pipe them into the Arduino, together with the mouse PS/2 signals. Then the Arduino would take care of configuring the TrackPoint (mouse) on key press, and for converting PS/2 signals to USB.

KeyWarriors all know USB and you can buy a cheap PS/2 to USB converter for the TrackPoint. Because of the last comment in your first post I thought you want to do it with an Arduino because you're interested in doing it not because you're interested in the result.

pylon: That would be a problem if two (or more) keys are hit together. You cannot distinguish which are actually pushed down.

Right - I didn't think about that.

KeyWarriors all know USB and you can buy a cheap PS/2 to USB converter for the TrackPoint.

Note that I need to bring the keyboard and the TrackPoint signals together in a microcontroller, because - to quote from my earlier messages: "I want to be able to configure the TrackPoint using proprietary PS/2 commands directly from the Arduino, on certain key combinations. Made up example: FN-F2 = increase sensitivity"

Possibly it's easiest not even to attempt to convert to USB at all inside of the Arduino:

[TrackPoint PS/2]------------------\
                                    ====[Arduino]====[PS/2-to-USB converter]----[HID input]
[keyboard]----[KeyWarrior PS/2]----/

Only it makes me unhappy that there are so many components. Reducing them could be something for the next iteration.

I thought you want to do it with an Arduino

If it's reasonably straight forward, then I would be interested in doing all inside of an Arduino right from the beginning.

If it's reasonably straight forward, then I would be interested in doing all inside of an Arduino right from the beginning.

It's reasonable to do it with an Arduino. Even for the KeyWarriors you have to find out how the keys are wired, these chips don't know it and I doubt that the connector is standardized for that purpose. I don't see where the use of the KeyWarrior provides an advantage in this system.

pylon:
It’s reasonable to do it with an Arduino.

So how would I do it? A possibly naive idea: Cycle through the rows and columns real fast, applying a voltage to them. Each time check if a current is flowing.

Hm, to see how the ThinkPad controller is doing it, it looks like an oscilloscope would be very handy. Perhaps I can find someone to lend me one.

pylon: And you always have to keep in mind that the Due runs on 3V3 and not 5V.

...and that could be problematic when working with PS/2 (TrackPoint), which runs with 5V.

...and that could be problematic when working with PS/2 (TrackPoint), which runs with 5V.

If you always keep it in mind, it's not such a big problem. You just have to make sure you use some kind of voltage divider if a PS/2 output goes to a Due input.

A possibly naive idea: Cycle through the rows and columns real fast, applying a voltage to them.

Almost the right idea: cycle through the columns and make the row pins inputs. If you read any input for a given column, you have a key hit. But you have to derive which pins are which column or which row. That may be quite a lengthy task.

pylon: If you always keep it in mind, it's not such a big problem. You just have to make sure you use some kind of voltage divider if a PS/2 output goes to a Due input.

For output from PS/2, yes. But for input, it could be (haven't checked) that it's necessary to provide signals at 5V. Input is necessary to configure the TrackPoint, e.g. the sensitivity.

Others have experimented with connecting a PS/2 mouse to a Due.

It's a tough decision: Due (3.3V may be a pain) or Mega 2560 (HID/USB may be a pain). All other Arduinos look unattractive because of their low number of pins.

Almost the right idea: cycle through the columns and make the row pins inputs. If you read any input for a given column, you have a key hit.

Makes sense - thanks!

I guess for the keyboard interface, 5V vs. 3.3V should not be an issue.

But you have to derive which pins are which column or which row. That may be quite a lengthy task.

40 pins are scary. ;-) I hope that it's easy to see which pins are for the TrackPoint and which ones are for the keyboard, simply by looking at the ribbon cable.

pylon: You can try a Leonardo with some additional hardware (shift-in/-out registers may work) but be prepared that this gets a bit more complex.

Does it really become that more complex?

Suppose the keyboard is arranged in an 8x8 matrix, then two 8 bit shift registers should be able to do the job. To check if the key at column 5 in row 2 is active, I would set the column shift register to 00001000, and the row shift register to 01000000. Then I would check if a current is flowing.

Disadvantage: Setting the pins via a shift register may take some extra clock cycles, thus slowing down scan speed. For comparison: The KeyWarrior is clocked at 6MHz.

If my message is confusing: Note that I'm a total beginner, never having touched an Arduino so far, and also never having worked with a shift register (and a bit lazy on reading ;-).

Does it really become that more complex?

Yes, you have more components which you have to solder separately. And you have more complexity in the programming because a shift register is differently programmed than a GPIO. But it's not that much, that's why I added the "bit" in the sentence.

Then I would check if a current is flowing.

How do you do that? In a 8x8 matrix you need one shift-out register and one shift-in register. Set the row and read the column. An additional problem may be that the typical keyboard has more than 64 keys.

Disadvantage: Setting the pins via a shift register may take some extra clock cycles, thus slowing down scan speed.

That's absolutely no problem you'll be more than fast enough for a keyboard. If you can hit a key 5 times a second you're really fast, even with the shift registers you'll get a scan rate much above 5Hz.

For comparison: The KeyWarrior is clocked at 6MHz.

The typical Arduino is clocked at 16MHz, so it's much faster.

pylon:

Then I would check if a current is flowing.

How do you do that?

From the top of my head (it has been a long time that I last dealt with electronics): Probably with an input pin and a resistor, to measure if current is flowing through the wire providing voltage to the shift register.

If you can hit a key 5 times a second you're really fast, even with the shift registers you'll get a scan rate much above 5Hz.

Hitting the key five times per second would require a scan rate of at least 10 Hz (down, up, down, up, ...) - nitpicking, yes, but also for my own better understanding. Please correct me if I'm wrong.

From the top of my head (it has been a long time that I last dealt with electronics): Probably with an input pin and a resistor, to measure if current is flowing through the wire providing voltage to the shift register.

With an input pin you can measure voltage and not current. And if you want to measure the current 64 matrix cross points you need 64 input pins, quite a bit and you haven't saved any pin with the matrix.

Hitting the key five times per second would require a scan rate of at least 10 Hz (down, up, down, up, ...) - nitpicking, yes, but also for my own better understanding. Please correct me if I'm wrong.

You're right, but believe an Arduino can do a lot of things in a tenth of a second.

pylon:
With an input pin you can measure voltage and not current.

Well, in the end I need to measure resistance: When the button is pressed, then resistance goes down. Based on the 74HC595, I created the attached circuit diagram. Perhaps with some tweaks here and there, wouldn’t that work?

shift.png

That's one part, but that's not a matrix. As I already wrote: you can do that stuff with an output shift register (p.e. 74HC595) and an input shift register (p.e. 74HC597), the first for the rows, the second for the columns.

pylon: That's one part, but that's not a matrix.

I simplified, too much.

74HC597

Thanks for the suggestion! Now I didn't find any nice tutorial for it, so I guess I'll have to use the data sheets available online.

the first for the rows, the second for the columns.

But in this case I wouldn't need to measure the current [at] 64 matrix cross points, right?

I’ve been working on a very similar project myself for a few weeks, except I have an Apple PowerBook G4 (Titanium DVI).

First of all I had to get the connector off of the motherboard because it’s proprietary (I think, if not then I have no idea what I’d search for to find one to buy), I did this by cleaning most of the components off of the motherboard (which was dead anyway) then hacking the corner off of the board with a Stanley knife. Then I soldered 30 pre-crimped flexible jumper wires (i.e. multi core, not solid copper) to the connector to make it breadboard friendly. Then I tested every pin for continuity with every other pin to see what that told me; turns out two pins were shorted together but all the rest were open circuit.

Then came the question of how to actually go about mapping the matrix out. I mean, for every connector pad there would only be a few keys that would close the circuit between it and one other pad, but which one? There were 30 pins so the combinations to check were numerous to say the least. I thought I’d connect all but one pad to ground then use continuity mode connected between the one pad that wasn’t grounded and the 29 others, so I tried that but got no beeps because the keys have significant resistance of between 90 and 130 ohms (I think this was part of an anti-ghosting method involving ADCs, but I can’t be bothered to reproduce it because I won’t be pressing more than one key at a time).

So got my 5V power supply and connected an LED (with series 330R resistor) to +5V but left the cathode floating and connected the one keyboard connector I hadn’t grounded to it, the theory being that there would be some keys on that connector’s FPCB trace that would then ground through another pin to close the circuit and illuminate the LED. I started pressing every button on the keyboard and got some flashes - Bingo! In about an hour I had a complete map, except for one puzzling problem…

Two pins on the board made the breadboard LED flash whenever I pressed the modifier keys, but curiously they also made either the Num lock or Caps lock LED flash with them. Very strange. There were also five other pads that did nothing at all. I then remembered the two pads I’d found that were shorted together; I had neglected to test them because I thought they were shorted because of damage to the PCB (from the Stanley knife) or a tiny soldering error that I couldn’t see. So I plugged them into the ground too and tested one of the two pads that were causing the keyboard LEDs to flash, and guess what: The LED stayed on without me pressing anything. Odd… So I disconnected that pad and tried the ones that previously did nothing. Each one caused the test LED to flash when pressing a unique modifier key.

From this I concluded that the two shorted connections were the modifier key common and also the common cathode for the two keyboard LEDs. The two pads that were causing the LEDs to flash when I pressed a modifier key were the LED anode voltage and nothing more, and the five pads that had previously done nothing were unique pads for shift, ctrl, alt, cmd and fn.

You seem to think that you’ll be able to detect a current flowing at both ends of the matrix, this is impossible. Keypads work by putting a voltage, let’s say 5V, through one column (or row, but let’s say column for the sake of example) at a time. The microcontroller then ‘listens’ to the rows (or columns, but again, for this example) and waits for a 5V signal. When it gets a signal it identifies which column was set to high and which row returned the voltage. Say it’s column B and row 2, that would give you “2” if your matrix is the same as mine (which it almost definitely isn’t).

So my Leonardo sketch (oh yeah, I’m using a Leonardo, I’m kind of hoping I can just wire the trackpad with a PS/2 cable and use a PS/2 - USB adaptor) will have a 2D array with all of the keys stored in it. It will cycle through the rows (of which there are eight) via a 595 and then listen to the columns (of which there are 10) for a voltage, then it’ll grab the co-ordinate when it gets a voltage and then print the relevant keystroke.

I have a thread about it in this very forum actually, it might be helpful if we checked on each other from time to time… But I’m tempted to look into that KeyWarrior as I have my matrix mapped!