[Bjonnh.net]# _

There is a new article about this project Read the update

Articles / Adding an IBM trackPoint to a Microsoft ergonomic keyboard. >


categories computer hack tags keyboard ergonomic trackpoint arduino

There is an update

First the obligatory XKCD:

XKCD #245

That’s what the keyboard looked like before, with the inspector checking it:

keyboard

Disclaimer: This could have been made in a much finished way, but I decided to go only with materials I had on hand (because my “creative” criteria was to spend less than 24hrs on it).

Hardware

Salvaging the TrackPoints

First I needed one of those trackPoints. The good thing is that they are pretty much standardized on thinkpads and it is easy to find old thinkpad keyboards. A bit of unscrewing/drilling/breaking is necessary to separate the module from the keyboard, it was especially hard on the oldest keyboard.

The trackpoint modules

Soldering the TrackPoint

DeskThority has a nice Wiki page on TrackPoint Pinouts.

Connecting the trackpoint

Testing the TrackPoint

At this stage, I wanted to make sure everything was OK. So I made a small arduino program using Rampadc’s project. The streaming mode was not working though, playing with timings I started to get values that were somewhat decent, but with a lot of errors so I went back to a polling (called Remote) mode.

The trackpoint connected to an Arduino Mega

Finding some space inside the keyboard

Next I needed to find some place inside the keyboard. There is some room, and I don’t have a tiny microcontroller board for now (I’m eying those Trinket M0) so I have to run another cable outside. If you are doing that, just take a cheap hub, a trinket-like microcontroller and connect everything inside, that way you have only the original cable going out.

As I didn’t have access to a 3D printer that day, I needed to do everything with simple things (hot glue!!!). Some people are gluing sticks on the TrackPoints to have more space when needed (they use long piercings for example). But in my case I was never using that scroll wheel in the middle of the keyboard and it was the perfect spot so I decided to remove it and use that space.

The keyboard with the scroll system removed

The hardcore part, melting plastics with a gas torch

There were some ridges on the side of the scroll wheel and that made the thing not so practical. So I melted them using a gas torch (with a special end, so it is only hot air, not a flame). Super easy and fast, and much less messy than grinding/sanding it.

The torch and the keyboard

And that’s what I got after, the picture doesn’t show it that much, but it removed at least 3mm.

The keyboard melted

Reconstructing around the module

I inserted the module, and put it in position with hot glue. I then made some black epoxy using charcoal and two component epoxy. Just grind the charcoal in a fine powder or not, I decided to have a mixture of coarse and fine so it looks a bit more like the plastic of the keyboard. First mix the two components, then add the charcoal. My epoxy sets in 20 min, and in reality you just have 5 min to work it after that it is too elastic to shape it. I did not finish that part yet, because I wanted the module to be removable if anything went wrong. I’ll finish it once this first layer can be cured (usually 24hrs before sanding, if not it may be a bit gunky inside still).

The epoxy just installed

Passing cables and making adapters

I only had a cable with 4 pins to go outside of the keyboard, so I decided to use the 5V from the keyboard itself, saving one cable. It is also possible to use a little RC circuit to get rid of the RESET, but I always prefer to have a real RESET on such devices.

The inside of the keyboard

I passed cables everywhere.

Cable

And I added temporary buttons as the only ones I have are large and noisy.

Final productotype

And that’s what it looks like. Of course it is not finished, I have to wait for the epoxy to fully cure before sanding it and finishing the shape around the TrackPoint (use oil so epoxy doesn’t stick to things you don’t want, silicone oil is the best but is impossible to remove if you did a mistake).

#The final keyboard as a prototype

Software

Uinput

For the software part, I’m using Uinput a Linux kernel module. It allows for the emulation of input devices directly from user space. That’s the best way if you want your device to work in X11, Wayland, console…

You talk to the uinput system (you need to be root for that or have given the right, be careful, if you give the right to a user, they can type anything on the machine, so a program could well run commands for you):

    struct uinput_setup usetup; 
    
    int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
    if (fd < 0) {
        fprintf(stderr, "failed to open device %s\n", strerror(errno));
        return 0;
    }

Then you declare what your device is and what it can do:

    ioctl(fd, UI_SET_EVBIT, EV_KEY); // We have buttons
    ioctl(fd, UI_SET_KEYBIT, BTN_LEFT); 
    ioctl(fd, UI_SET_KEYBIT, BTN_MIDDLE);
    ioctl(fd, UI_SET_KEYBIT, BTN_RIGHT);

    ioctl(fd, UI_SET_EVBIT, EV_REL); // We have relative movements and two axis
    ioctl(fd, UI_SET_RELBIT, REL_X);
    ioctl(fd, UI_SET_RELBIT, REL_Y);

    memset(&usetup, 0, sizeof(usetup));
    usetup.id.bustype = BUS_USB;
    usetup.id.vendor = 0x1234; /* sample vendor */
    usetup.id.product = 0x5678; /* sample product */
    strcpy(usetup.name, "Bjo's nipple");

Sending commands, is as simple:

    emit(fd, EV_REL, REL_X, 5);
    emit(fd, EV_REL, REL_Y, 5);
    emit(fd, EV_SYN, SYN_REPORT, 0);

Arduino firmware

On the arduino, as I have to do polling, I don’t want to send values that didn’t change so I’m keeping the last message and only send the changes.

These are the messages it is sending:

m x (int) y (int) Movement relative of x and y
c btn (int) Click of btn
r btn (int) Release of btn
  TrackPoint::DataReport d = trackpoint.readData();
  if ((d.x!=0) ||  (d.y!=0)) {
    Serial.print("m ");
    Serial.print(d.x);
    Serial.print(" ");
    Serial.println(-d.y);
  }
   
  for (int btn=1;btn<4;btn++) {
    int pos = (1<<(btn-1));
    if (((d.state&pos) != 0) && (btnStatus&pos) == 0) {
      Serial.print("c ");
      Serial.println(btn);
      btnStatus = btnStatus | pos;
    }
    if (((d.state&pos) == 0) && (btnStatus&pos) != 0) {
      Serial.print("r ");
      Serial.println(btn);
      btnStatus = btnStatus & (0b1111 - pos);
    }
  }

I am also doing some kind of message handling so the computer can reset the module. These modules do their 0 when they are initialized, if you are touching it while it is initializing, then your zero is not right and you will have a drift.

    if (inputString == "WHO") {
      Serial.println("ME");
    } else if (inputString == "RESET") {
      Serial.println("OK");
      trackpoint.reset();
      trackpoint.setSensitivityFactor(0xb0);
      trackpoint.setRemoteMode();
      Serial.println("READY");
    }

And that’s the full sequende diagram we have for now:

%%{init: {'theme': 'dark', 'themeVariables': { 'darkMode': 'true'}}}%% sequenceDiagram participant Arduino participant Linux Arduino->>Linux: m x y Arduino->>Linux: c btn Arduino->>Linux: r btn Linux->>Arduino: WHO activate Arduino Arduino->>Linux: ME deactivate Arduino Linux->>Arduino: RESET activate Arduino Arduino->>Linux: OK Note left of Arduino: Resets the module Arduino->>Linux: READY deactivate Arduino

Conclusion

That was a fun quick 24h project, having this uinput module really made things much easier.

These are the things left to do:

  • Finish the polishing/gluing
  • Try to make that stream mode work (clock signals are ok), I’ll need to bring my oscilloscope.
  • Add a third button: for now we have two-button emulation, but that also mean I don’t have a nice scroll, and too bad I removed that scroll wheel ;)
  • Buy a small board and integrate everything inside the keyboard.

Oh and keyboards are gross:

XKCD #237

You can find the code at: GitHub Bjo’s nipple