after buying a typewriter for addressing envelopes in 2007 and watching it languish in storage for the years afterwards, I decided to turn it into something more useful—a teleprinter. keypresses are sent over serial, and it prints data received.

the typewriter

this project uses a Brother SX-4000 electronic typewriter. it uses a daisy wheel for its font and a solenoid hammer against either a black inked ribbon or correction tape. it has a dictionary it uses to alert you to misspellings. it also hosts more advanced features such as text alignment (left, center, right), bold, underline, a demonstration, line buffering (using the built-in 16-character display for feedback), and automatic line wrapping.

it also has some interesting limitations: while it has keys for ¢, §, °, ±, ², ³, ¶, ¼, and ½, it is missing keys for {, }, \, |, `, ~, and ^. the layout is strange, too. SHIFT+, and SHIFT+. print comma and period (so they will work while the SHIFT LOCK is active—it is very naïve), while CODE + w and CODE + u print < and >.

the keyboard

the typewriter's keyboard is a matrix of switches on eight scan lines and eight signal lines (a maximum of 64 possible keys, though only 58 are wired). as a schematic it looks something like this.

all the lines are pulled high by default by 10kΩ resistors to Vcc and the scan lines are pulled low one at a time by the typewriter's microcontroller (separated by 1kΩ resistors). for each lowered scan line, the typewriter reads the state of the signal lines, and any signal lines that are pulled down correspond to keys on that scan line.

it gets a bit more complicated. on this typewriter, pressing and holding a, s, and d also causes z to appear to be pressed, because the keys create a path to it, causing it to appear lowered. the typewriter has some fairly advanced logic to cope with this, which I mostly emulate in this project.

the circuit

because teletypes don't print what you type into them, the AVR had to be connected directly to scan lines of the keyboard matrix, so it could forcibly pull them up regardless of the scan line and key state, blinding the typewriter to any keys being pressed. similarly, the scan lines had to be instrumented away from the keyboard, as driving the signal lines would blind not just the typewriter, but also the AVR from changes in the scan line state.

fortunately, the typewriter's board had solder pads all over the place—very convenient for attaching some surface mount resistors and Kynar wire.

beyond the signal and scan lines, power is brought to the AVR from the typewriter's circuit board. very simple.

reading the keys

each poll of a scan line done by the typewriter's controller lasts 2ms ±80µs, which is actually a very long time to a microcontroller. the AVR uses a debouncer (based on a fast and clever 2-bit stacked counter provided by Ben Holt) to make sure that the signals have settled before the AVR polls the state of the keyboard. afterwards, the signal lines are driven high before the typewriter attempts to read the state of the keys near the middle of the interval. the entire process takes between 28 - 45µs, depending on load.

the scan and signal line (and the current meta key state) for a key press is used as an index into an enormous lookup table of keys, which is sent over serial when the key is lowered and raised.

this part of the project happened very quickly; building the table of keys was time-consuming, but not difficult. an evening's worth of hacking got this portion of the project functionally complete.

writing the keys

a character sent over serial to the AVR is looked up in one of three tables (ascii, special characters, control key combinations) and translated into a meta key state, scan number, and signal number. if the code wasn't found in the lookup tables, '?' is used instead.

this code took all of a day to write too, but I ran into some very strange problems. the initial Lorem Ipsum test had some pretty inexplicable failures:

the errors were mostly sporadic—the only way to reproduce on command was to try to type a newline from the computer1. debugging output showed that the meta, signal, and scan information being looked up from the ascii table was completely wrong; that sequence of hex didn't exist in any of the lookup tables.

a lot of code cleanup and some bug fixing happened in the few days afterward, but I couldn't crack the corruption problem. as usual, half an hour with Beth at the disassembler revealed the cause2: the tables were being stored in RAM, completely overwhelming the ATmega168's 1KB memory. using PROGMEM and the associated accessor macro functions defined in avr/pgmspace.h confined the lookup tables to the EEPROM and fixed the problem.

the best part about this was that I had an ATmega328 sitting next to me this entire time. it has twice the RAM, so it would have worked without any of the corruption issues I ran into on the smaller chip. it seems like the basic Arduino environment is nice for short, simple projects, but authors of larger projects need to be more aware of the hardware than the Arduino's training-wheeled environment provides.

anyway…

the code for this project is available at git://github.com/numist/project-typewriter.git and should be pretty easy to port to other typewriters with only changes to the tables in keycodes.h and following the scan/signal line to pin mapping in the appropriate board-specific files included from signals.h.


1 the errors also went away entirely when the read code was commented out
2 it also revealed that avr-gcc's code generation is very poorly optimized