DocWriter: the typewriter that sends its keystrokes in real time to a Google Doc

by James Somers, September 17, 2017

For years I’ve wanted a writing machine that would combine the best parts of a typewriter and a word processor. After months of tinkering, my friend Ben Gross and I just finished building one. We call it the DocWriter. It’s a typewriter that sends its keystrokes in real time to a Google Doc.

The beauty of a typewriter is that it propels you through a piece of writing. You can't tinker with phrases, so you get used to laying down paragraphs. Your mind, relieved from the micromechanics of language, applies itself to structure, to the building of sections and scenes and arguments. When you're done you end up with something whole, even if it's imperfect: a draft that reads from start to finish and that you can hold in your hands.

A word processor, by contrast, turns revision into a kind of play. This is true not just for the fine wordwork that comes right before publication, but for the big stuff, too, like when you want to move sections around, or see what a story looks like without a side character. Doing this kind of thing on a typewriter would be a nightmare -- to say nothing of the simple fact that your words will have to be digitized at some point and it's just not practical to scan them or type them up off a sheet of paper.

The idea behind the DocWriter is to be a bridge between these tools so that each serves its purpose: the typewriter, to create the building blocks of a piece of writing, and the word processor, to make the most of them.

How we built it

The DocWriter is actually pretty simple: we took a Brother SX-4000 electronic typewriter and spied on its keyboard switch matrix by soldering a few wires onto the main circuit board; we ran those to a Raspberry Pi 3, which runs a C program that reverse engineers the signals; we pipe this data over ssh to a computer program running in the cloud; that program maps the signals to keystrokes and runs a headless web browser that types the keys into a new Google Doc.

From the user’s perspective, you’re just using a typewriter. The Raspberry Pi is hidden inside it (the blue box in the image above), and it draws power from the typewriter itself, so there’s no extra cord. When you turn on the typewriter, it boots the Pi, which connects itself to your WiFi network, and runs the program that listens for keystrokes and pipes them to the cloud. You know that the DocWriter is ready once you get an email from Google Docs saying that the machine has shared a new document with you.

We're indebted to numist, who turned the same model typewriter into a teletype using software much more sophisticated than ours. That work made our project seem doable, and gave us many clues about the kinds of problems we'd encounter along the way.

There were, indeed, many problems: we had a surprisingly hard time getting the case off the typewriter when we first bought it; by unlatching the keyboard, we inadvertently triggered a condition where the motor would endlessly grind up against the case, and nearly convinced ourselves we'd broken the machine; the early versions of our controller code erroneously piped data to the typewriter, causing all kinds of weird behavior; we built the whole setup three times, first on an Arduino and then on a Raspberry Pi Zero, before settling on the Pi 3; we had a bad connection on a wire that caused some keys to fail; we wrote elaborate code to compensate for noise on the lines, before realizing that we could use pull-up resistors to more or less eliminate it entirely; we spent nearly a full day just installing a headless web browser that worked; and we had to rewrite our main control code about a dozen times.

In the end, though, the setup is elegant: along with wires for power and ground, we had to solder just 16 connections onto the pins controlling the keyboard switch matrix on the typewriter's circuit board. The rest is software, most of which does exactly what you'd expect. The hardest code to write was the controller to read the raw signals from the typewriter. But we got it down to something with nearly perfect behavior that's also pretty minimal (especially when you ignore special cases for the Shift key):

#include <wiringPi.h>
#include <stdio.h>
 
int main(void) {
  wiringPiSetup();
  setbuf(stdout, NULL);

  int scanPins[] = {5, 22, 10, 11, 26, 27, 28, 29};
  int signalPins[] = {13, 12, 3, 2, 0, 7, 24, 23};
  
  int i = 0;
  int j = 0;
  for (i=0; i<8; i++) {
    pinMode(scanPins[i], INPUT);
    pinMode(signalPins[i], INPUT);
    pullUpDnControl(scanPins[i], PUD_UP);
    pullUpDnControl(signalPins[i], PUD_UP);
  }
  
  int keyDown[8][8] = {
    {0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0}
  };

  int lastI;
  int lastJ;
  int sameKeyCount = 0;
  for (;;) {
    for (i=0; i<8; i++) {
      for (j=0; j<8; j++) {
        if (digitalRead(scanPins[i]) == LOW && digitalRead(signalPins[j]) == LOW) {
          (i == lastI && j == lastJ) ? sameKeyCount++ : sameKeyCount = 0;

          if (sameKeyCount > 50 && keyDown[i][j] <= 0) {
            printf("%d,%d\n", i, j);
            keyDown[i][j] = 50;
          }
          lastI = i;
          lastJ = j;
        }
        
        if (digitalRead(scanPins[i]) == LOW && digitalRead(signalPins[j]) == HIGH) {
          keyDown[i][j] = (keyDown[i][j] - 1);
        }
      }
    }
  }
}

A Ruby program in the cloud takes the output of this program (raw indexes like "6,0" for spacebar, or "5,3" for "j") and maps them to strings, which it sends to a Google Doc using the watir gem for driving headless web browsers.

Can I get one?

For now this is just a one-off project, and we sadly won't have the bandwidth to make others—except maybe for an exorbitant and unfair price (like $10,000).