Interfacing Teletypes and Paper Tape · Volume 7

Interfacing Teletypes and Paper Tape — Vol 7: Two Builds — a Pico Loop Interface and an Optical Tape Reader

A Raspberry Pi Pico interface that puts any 60 mA or 20 mA teleprinter on USB, with its schematic, parts, firmware and bring-up; a hand-pulled optical reader for 5- and 8-level tape; and the PURPLE cipher-machine replica they are meant for.

7.1 Two builds, and why

The first six volumes of this dive are the parts. This one assembles them into two things that can be built at a bench in a weekend each:

  • a Pico loop interface, which makes any neutral-loop teleprinter, five-level or eight-level, 60 mA or 20 mA, look like an ordinary USB serial port; and
  • an optical paper-tape reader, pulled by hand, which turns a tape into a file.

I have a particular use for the first. On Iconic Machines there is a twelve-volume history of PURPLE, the Japanese diplomatic cipher machine broken by the US Army’s Signal Intelligence Service in 1940, and its last volumes design a working replica (PURPLE, Vol 10). The 1941 machine sat between two electric typewriters, one to type on and one to print the result, and the replica’s design gives the printer job to a thermal printer while noting that “a vintage teleprinter is the faithful alternative”. I plan a pair of teletypes for that job: one whose keyboard feeds the replica, one whose printer prints its output. The Teletype Model 15, Vol 8, makes the case for a pair of Model 15s and works out the wiring; this interface is the hardware between them and the replica’s controller, which is itself a Raspberry Pi Pico 2 running its teleprinter lines on PIO.

None of what follows has been built yet, by me or anyone else. It has been designed, drawn, simulated and partly tested in software, and the status section at the end of each build says exactly how far. Check every row of the parts lists against the schematic before ordering, and expect to change something on the bench.

7.2 Build 1: the Pico loop interface

What it does. The computer sees a USB CDC serial port; any terminal program works, and the speed it is set to does not matter. Text from the computer is converted to Baudot (or passed through as ASCII for a Model 33) and sent on the printer loop at the machine’s own speed by a PIO state machine. Characters typed on the machine’s keyboard come back as ASCII. The interface tracks LTRS and FIGS, wraps lines, pads carriage returns, switches the motor through an external relay, and can pass bytes through raw for punching tape.

Figure 1 — The complete Pico loop interface (Rev A). Block A keys and regulates the printer loop at 60 or 20 mA from 48 V; block B senses the printer and keyboard loops through optocouplers; block C is the Pi…
Figure 1 — The complete Pico loop interface (Rev A). Block A keys and regulates the printer loop at 60 or 20 mA from 48 V; block B senses the printer and keyboard loops through optocouplers; block C is the Pico, the power input and the motor-relay jack. Drawn by draw_schematics.py; Vol 3 explains each block.

Choose a driver. Block A is Vol 2’s Design A, a 48 V constant-current sink: two transistors, a handful of resistors, 2.5 W into the case. Vol 2’s Design B, the asymmetric half-bridge, replaces block A (and the 48 V adapter) when a cool, small driver is wanted; it runs from 12 V, dissipates under a watt, and keys the magnet faster and more symmetrically, at the cost of about twenty more parts and a 150 V bus inside the box. Blocks B and C are the same for both, except that Design B’s keyboard loop runs from 12 V through 680 Ω. The firmware supports either with one command.

Figure 2 — Design B, the asymmetric half-bridge driver, which can replace block A. See Vol 2 for the arithmetic. Drawn by drawschematics.py.
Figure 2 — Design B, the asymmetric half-bridge driver, which can replace block A. See Vol 2 for the arithmetic. Drawn by draw_schematics.py.
Figure 3 — A Raspberry Pi Pico: the RP2040, its flash, and forty pins, of which the interface uses four. (Phiarc, Wikimedia Commons, CC BY-SA 4.0.)
Figure 3 — A Raspberry Pi Pico: the RP2040, its flash, and forty pins, of which the interface uses four. (Phiarc, Wikimedia Commons, CC BY-SA 4.0.)

7.2.1 Parts

The full list follows, generated from the same file as the printable PDF at the end of this volume, so the two agree. The PDF adds suppliers, alternates and tick boxes for ordering, checking and fitting.

Pico teletype loop interface, Rev A

Table 1 — Pico teletype loop interface, Rev A

QtyRefPartSpecificationPart number
Logic
1—Raspberry Pi PicoRP2040, USB, 3.3 V logicSC0915
1R1Resistor 330 Ω¼ W 1 % metal film — U1 LED current ≈6 mAYageo MFR-25FBF52-330R
2R11, R15Resistor 10 kΩ¼ W 1 % — sense-output pull-ups to 3V3Yageo MFR-25FBF52-10K
1R16Resistor 220 Ω¼ W — GP5 to J3 tip (motor relay)Yageo MFR-25FBF52-220R
Isolation
3U1, U2, U3Optocouplerphototransistor, CTR 100–200 % at 10 mA, 5.3 kV, DIP-4Vishay SFH617A-3
Printer-loop keyer and current sink (block A)
1Q1N-MOSFET 400 VTO-220, linear duty ≈2.5 W at 60 mA — bolt to the caseVishay IRF740PBF
1Q2NPN transistorcurrent-sense, TO-92onsemi 2N3904BU
1R2Resistor 4.7 kΩ½ W — feeds the 12 V gate rail from +48 VYageo MFR50SFTE52-4K7
1D2Zener 12 V1 W, DO-41 — gate railonsemi 1N4742A
1R3Resistor 10 kΩ¼ W — gate pull-upYageo MFR-25FBF52-10K
1R4Resistor 1 kΩ¼ W — Q2 baseYageo MFR-25FBF52-1K
1R5Resistor 33 Ω½ W 1 % — sets 20 mA (0.65 V / 33 Ω)Yageo MFR50SFTE52-33R
1R6Resistor 16 Ω½ W 1 % — with JP1 in, 33 ∥ 16 Ω sets 60 mAYageo MFR50SFTE52-16R
1D3Rectifier diode1 A 1000 V — clamponsemi 1N4007
1D4Zener 47 V1 W, DO-41 — releases the magnet at ≈48 Vonsemi 1N4756A
Loop sense networks (block B, two identical)
2R8, R12Resistor 100 Ω½ W 1 % — sense resistorYageo MFR50SFTE52-100R
2R9, R13Resistor 47 Ω¼ W — in parallel via JP2/JP3 for 60 mAYageo MFR-25FBF52-47R
2R10, R14Resistor 68 Ω¼ W — opto LED series resistorYageo MFR-25FBF52-68R
2D5, D6Signal diodereverse protection across the opto LEDonsemi 1N4148
1R7Resistor 2.2 kΩ 3 Wmetal oxide — keyboard loop, ≈21 mA, dissipates ≈1 WYageo RSF3WSJT-73-2K2
Power
1—48 V DC adapter25 W, 0.52 A, 2.1 × 5.5 mm centre-positive plugMean Well GST25A48-P1J
1J4DC power jack2.1 mm pin, panel mountSwitchcraft 721A
1F1Fuse 0.5 A slow5 × 20 mm, time-lagLittelfuse 0218.500MXP
1D1Rectifier diodereverse-polarity protectiononsemi 1N4007
1C1Electrolytic 100 µF 100 VradialNichicon UVR2A101MPD
Connectors, hardware, motor control
2J1, J21/4-inch phone jack2-conductor (tip/sleeve), open frameSwitchcraft 11
1J33.5 mm jackmotor-relay control outputCUI Devices SJ1-3523N
3JP1–JP32-pin header + shunt0.1 inSullins PRPC040SAAN-RC + SPC02SYAN
1—TO-220 insulating kitThermalsil pad, shoulder washer, screwBoyd 4880SG
1—Enclosuredie-cast aluminium, 119 × 94 × 34 mm — also Q1’s heatsinkHammond 1590BB
1—Perfboard0.1 in, about 80 × 60 mm—
1—Enclosed mains relay for the motor3–48 V DC control input, four outletsDigital Loggers IoT Relay II
Option: Design B asymmetric half-bridge driver (replaces block A and the 48 V adapter)
1—12 V DC adapter25 W, 2.1 × 5.5 mm centre-positiveMean Well GST25A12-P1J
1—HV boost module3.7–15 V in, 100–230 V adjustable out — set to 150 VOmnixie NCH6300HV
1D1Schottky diode1 A 40 V — 12 V reverse protectiononsemi 1N5819
1F1Fuse 0.5 A slow5 × 20 mmLittelfuse 0218.500MXP
1F2Fuse 100 mA fast5 × 20 mm, busLittelfuse 0217.100MXP
1C1Electrolytic 47 µF 250 Vbus capacitor, radialNichicon UVR2E470MHD
1R1Resistor 100 kΩ 1 Wbus bleederYageo RSF100JB-73-100K
1D5TVS 1.5KE200A171 V standoff — bus clampLittelfuse 1.5KE200A
2Q1, Q2N-MOSFET 400 VTO-220 — high and low side; no heatsink neededVishay IRF740PBF
2D2, D3Ultrafast diode1000 V 1 A, 75 ns — energy-return diodesVishay UF4007-E3/54
1U6Photovoltaic MOSFET driver8.4 V open-circuit, fast turn-off, SOP-4 — high-side gateVishay VOM1271T
1U1Current-mode PWM controllernear-100 % duty, CS ≈ 1 V, PDIP-8Texas Instruments UCC28C43P
1U5OptocouplerDIP-4 — COMP shutdownVishay SFH617A-3
2Q3, Q4NPN transistorTO-92 — COMP pull-down; LED driveronsemi 2N3904BU
1R2Resistor 47 Ω½ W 1 % — sense, 20 mAYageo MFR50SFTE52-47R
1R3Resistor 24 Ω½ W 1 % — with JP1, 63 mAYageo MFR50SFTE52-24R
1R4Resistor 10 Ω¼ W — gateYageo MFR-25FBF52-10R
1R5Resistor 1 kΩ¼ W — CS filterYageo MFR-25FBF52-1K
1C2Capacitor 470 pFC0G, 50 V — CS filter—
2RT, R6Resistor 10 kΩ¼ W — oscillator, Q3 biasYageo MFR-25FBF52-10K
1CTCapacitor 2.2 nFC0G or film — sets frequency (check the datasheet curve)—
2—Capacitor 0.1 µF + 1 µFVREF and VDD bypassKEMET C320C104K5R5TA
1R7Resistor 150 Ω¼ W — LED string ≈16 mAYageo MFR-25FBF52-150R
1R8Resistor 1 kΩ¼ W — Q4 baseYageo MFR-25FBF52-1K
1—Resistor 680 Ω½ W — keyboard loop from 12 V, ≈15 mAYageo MFR50SFTE52-680R

7.2.2 Construction

  • Two zones. Lay the perfboard out with a clear line through the middle of the three optocouplers: loop side on one side, Pico side on the other, nothing crossing but the optos. Leave the strip of board under the optos empty. Vol 3 explains why.
  • Q1 on the case. Bolt Design A’s Q1 to the wall of the Hammond 1590BB through the Boyd 4880SG insulator kit, and check with an ohmmeter that the tab is not connected to the case before power goes anywhere near it. Keep Q2 at the other end of the board: its base-emitter voltage sets the current, and heat shifts it (Vol 2).
  • R7 off the board. The 2.2 kΩ keyboard-loop resistor dissipates about a watt continuously; mount it on its leads, clear of everything.
  • Jacks. Two 1/4-inch jacks for the loops, labelled PRINTER (J1) and KEYBOARD (J2), a 3.5 mm jack for the motor relay, and the 2.1 mm power jack. Quarter-inch cables are the traditional teletype loop connector and are still made by the mile.
  • Meter jack. A pair of terminals in series with the printer loop, normally shorted by a link, so that a milliammeter can be inserted to set and check the current.

7.2.3 Firmware

The firmware is in 03-outputs/firmware/pico_loop/: main.c, the Baudot tables in baudot.c, the two PIO programs in loop.pio, and a CMakeLists.txt for the standard Raspberry Pi pico-sdk build:

export PICO_SDK_PATH=/path/to/pico-sdk
cp $PICO_SDK_PATH/external/pico_sdk_import.cmake .
mkdir build && cd build && cmake .. -DPICO_BOARD=pico      # or pico2
make                                                        # -> pico_loop.uf2

Hold BOOTSEL, plug in the Pico, and copy pico_loop.uf2 to the drive that appears.

The PIO programs are described in Vol 4. Both run at 64 state-machine cycles per element, so changing speed is a change of clock divider and nothing else. The transmit program takes one 32-bit word per character, describing the frame in half-elements, so 5-bit Baudot with 1.5 stop units and 8-bit ASCII with two stop bits use the same four instructions. The receive program samples at the centre of each element and ignores start elements that vanish within half an element, which rejects contact bounce.

Settings are changed by typing a line that starts with two tildes:

Table 2 — Settings are changed by typing a line that starts with two tildes

CommandEffect
~~showprint the current settings and the framing-error count
~~baud 45.45line speed: 45.45, 50, 56.88, 74.2 or any value from 30 to 300
~~baudot / ~~asciifive-bit Baudot (default) or eight-bit ASCII at 110 baud for a Model 33
~~ustty / ~~ita2the figures table the machine’s type box carries (Vol 4)
~~uos on / offassume unshift-on-space (default on)
~~echo on / offprint the keyboard’s characters locally in full duplex (default on)
~~half on / offkeyboard and printer in one series loop on J1; ignore the interface’s own sending
~~width 72characters per line before an automatic CR LF
~~motor auto / on / offmotor relay: on when there is traffic, off after a minute of silence
~~raw onpass bytes straight through as codes, for punching tape; three RS (0x1E) bytes leave raw mode
~~txinv on / offoutput polarity: on for Design A (default), off for Design B

7.2.4 Bring-up

  1. Logic only. No loop power. Plug in USB, open a terminal on the port (/dev/ttyACM0 on Linux), type ~~show. The settings should print.
  2. The loop on a resistor. Connect the 48 V adapter. Put a 470 Ω 5 W resistor and a milliammeter in series on J1 instead of a machine. The meter should read 60 mA with JP1 in, 20 mA with it out, and should not change when you type nothing: the line idles marking.
  3. Signal. Type RYRYRYRY continuously. A moving-coil meter should drop to about 32 mA (Vol 1); a battery-powered oscilloscope across a 10 Ω resistor in the loop should show 22 ms elements with clean edges. On Design A, the drain of Q1 should rise to about 96 V at each space and stop there.
  4. Keyboard loop. Short J2’s tip to its sleeve through a switch and press it in a rhythm: nothing should print, but ~~show should count framing errors, which proves that U2 sees the loop. A real keyboard will produce characters.
  5. The machine. Strap the selector magnet for the loop current (Vol 1), bypass any polar relay, and wire the keyboard and printer to separate jacks. Switch the motor on, send RYRY, and type on the keyboard. Then take a range (Inside the Teletype Mechanism, Vol 1) and write the result down: it is the number that says whether the interface is good.

7.2.5 Using it

  • As a terminal. Any terminal program; or a Linux login with agetty -L 9600 ttyACM0 tty33 (Vol 4).
  • For the news. The ITTY pipeline of Vol 5 ends in /dev/ttyACM0; fldigi_bridge.py /dev/ttyACM0 prints whatever fldigi decodes off the air.
  • For tape. On a Model 33, ~~ascii and ~~raw on, then send DC2, a NUL leader, the bytes, a NUL trailer and DC4 (Vol 6). On a Baudot reperforator, ~~raw on and send the five-bit codes.
  • For the PURPLE pair. The replica’s controller can talk to the machines through two of these interfaces’ front ends, or use their PIO programs directly on its own Pico 2: the keyboard machine’s loop on a sense input, the printer machine’s loop on a keyer.

7.2.6 Status

Built: no. Tested so far: the PIO programs, assembled and run in an RP2040 PIO emulator (test_pio.py: the letter Y transmitted with correct timing; the letter R received through a deliberate glitch); the C Baudot tables against the Python ones, character for character; main.c syntax-checked against stub headers (not yet compiled against the real pico-sdk); the loop designs simulated (loop_sim.py); the termios2 sender run on a pseudo-terminal; the AFSK decoder run on the live ITTY stream.

7.3 Build 2: an optical tape reader

What it does. Pull a 5-level or 8-level tape through it by hand, and each row comes out of the USB port as a byte, or as decoded Baudot text. The feed hole is the clock, so the speed of the pull does not matter.

Figure 4 — The optical reader: three strings of three IR LEDs from the Pico's 5 V, nine phototransistors as emitter followers, an MCP3008 for data tracks 1–8 and the Pico's ADC0 for the feed hole, and a calib…
Figure 4 — The optical reader: three strings of three IR LEDs from the Pico's 5 V, nine phototransistors as emitter followers, an MCP3008 for data tracks 1–8 and the Pico's ADC0 for the feed hole, and a calibration button. Drawn by draw_schematics.py.

7.3.1 Parts

Optical paper-tape reader, Rev A

Table 3 — Optical paper-tape reader, Rev A

QtyRefPartSpecificationPart number
Electronics
1—Raspberry Pi PicoMicroPython firmware main.pySC0915
1U18-channel 10-bit ADCSPI, DIP-16, runs at 3.3 VMicrochip MCP3008-I/P
10D1–D9 (+1 spare)IR LED 940 nm3 mm (T-1), narrow beamVishay TSAL4400
10Q1–Q9 (+1 spare)Phototransistor3 mm (T-1), daylight filterVishay TEFT4300
3R1–R3Resistor 47 Ω¼ W — LED strings ≈30 mAYageo MFR-25FBF52-47R
9R (×9)Resistor 22 kΩ¼ W — phototransistor loads; try 47 kΩ for dense tapeYageo MFR-25FBF52-22K
1C1Capacitor 0.1 µFceramic, MCP3008 decouplingKEMET C320C104K5R5TA
1SW1Pushbuttonmomentary, CALOmron B3F-1000
Mechanics
1—Tape guide and aperture platestwo plates with a 0.1 in row of 1.5 mm holes, 1 in and 11/16 in channels—
1—Perfboard0.1 in—

7.3.2 Mechanics

The electronics are the easy part; the mechanics decide whether it reads. Two plates sandwich the tape: an illuminator plate with the nine LEDs, and a detector plate with the nine phototransistors behind an aperture, a row of nine holes no larger than about 1.5 mm, on a 2.54 mm pitch, with the feed-hole aperture in the fourth position counting from the reference edge for 8-level tape. The aperture is what stops each sensor seeing its neighbour’s hole; Müller’s e-basteln design makes its apertures from thin PCB, and that is a good model. Spacers set a channel wide enough for 1-inch tape, with a removable guide strip for 11/16-inch tape that puts its feed track over the same sensor; its five levels then fall on sensors 2 to 6, and the firmware shifts them down when LEVELS = 5. The tape should slide flat, with no sprocket and no tension.

7.3.3 Firmware and use

03-outputs/firmware/tape_reader/main.py is MicroPython. Copy it to the Pico as main.py. Set LEVELS (5 or 8) and MODE (raw, hex or baudot) at the top.

Calibrate once for each kind of tape: hold the CAL button while pulling about 30 cm of punched tape through, then release. The firmware records the darkest and brightest reading on every channel and puts each threshold midway; they are saved in thresholds.json and survive power-off. Read by pulling the tape steadily. While the feed hole is in view, the firmware keeps the peak reading of each data channel; when the feed hole has passed, it compares the peaks with the thresholds and emits one row. On Linux:

stty -F /dev/ttyACM0 raw -echo
cat /dev/ttyACM0 > tape.bin            # MODE = "raw": one byte per row
python3 tape.py svg tape.bin --levels 8 tape.svg      # look at what was read

For a fragile tape, scan it instead and use tape.py decode (Vol 6).

7.3.4 Status

Built: no. The Baudot tables in main.py were checked against baudot.py; the rest is untested. The aperture plates are the part most likely to need a second attempt.

7.4 What the two builds share

Both builds are small, and both are deliberately plain. Nothing in either needs a part that is hard to buy, and nothing in the interface needs a voltage that can hurt anyone outside the box. Both are built around the same idea that runs through this whole dive: a teletype is a serial device with a strict but simple protocol, and a few dollars of modern parts can speak that protocol exactly, without the 120-volt supply and the hot resistors that made the job dangerous for fifty years.

When the PURPLE pair arrives, the interface will be the first thing to connect and the ITTY news the first thing to print. I expect to come back and correct this volume once both have happened.

7.5 Printable Bill of Materials

The parts lists above as printable PDFs, with ordering, in-hand and fitted tick boxes, part numbers, alternates and suppliers:


7.5.1 Sources (Vol 7)