yaymukund’s weblog

Hacking a GPO 746 rotary phone

Rotary dial telephones are a fantastic way to start learning about electronics. They’re built to be disassembled and serviced (because they were originally rented!). There are online communities of engineers and enthusiasts who are happy to to share what they know. Most of all, they’re beautiful objects that inspire curiosity and nostalgia.

Here’s what I’ll be making:

It’s a GPO (General Post Office) 746 rotary dial telephone that plays a random song each time you pick up the handset.

Overview

The logical core of the GPO 746 is the D92732 circuit board. It connects all of the different components— the microphone, rotary dial, ringer bells, switch hook, and handset speaker.

For this project, I only cared about the last two:

  1. The switch hook signals when the handset has been picked up.
  2. The handset receiver plays music.

Instead of the circuit board, I used the Teensy to drive all the logic. Instead of powering it by a cord to the wall, I used a 3xAA battery pack to provide 4.5V, as the Teensy docs suggest.

Getting inside the phone

I loosened the screw at the back of the telephone with a flathead screwdriver. It was held in place with a spring and nut. This is explained in more detail here.

I took a photo of the circuit…

…and then disconnected all the wires!

Brute-forcing the switch hook terminals

I found the D92732 circuit diagrams, but eventually opted for a brute-force approach.

From reading online, I learned that there are a pair of terminals that connect to the switch hook. I used a multimeter to check connectivity between every pair of terminals.

That’s 19 choose 2 = 171 combinations.

In my phone, it happened to be terminals T3 and T6. They connected when the switch hook was released, and disconnected when the switch hook was pressed. If you’re following along, they may be different terminals if you’re on another version of the GPO 746. The meaning may also be reversed (i.e. disconnected = released, connected = pressed).

The handset receiver

The handset cord has four wires leaving it: two for the receiver and two for the microphone. To identify the wires for the receiver, I just unscrewed the receiver-end of the handset and noted the colors.

Wiring the terminals

Then I made the following four connections to the terminals:

  1. The red and green handset receiver wires connect at T11 and T13 respectively.
  2. The brown and black switch hook wires connect at T3 and T6 respectively.

I chose T11 and T13 for the handset receiver wires because they happened to be “free.” That is, they didn’t seem to be connected to anything else.

Wiring the audio breakout board

Red goes to the sleeve and green goes to the tip. You may be able to switch these around— I don’t think they are polarised— but I did not test it.

Then, I soldered the Teensy Audio Shield to the Teensy using pins headers and connected the audio breakout board to the Teensy Audio Shield using a short 3.5mm audio cable.

Wiring the Teensy

  1. Red and white connect the battery pack to Teensy’s ground and Vin (3.6 to 5.5 volts) pins respectively.
  2. Brown and black connect the switch hook terminals to Teensy’s Input 0 and ground respectively.

The Teensy Pins reference is excellent. The ground pins are interchangeable, but be sure to use the Vin (3.6 to 5.5 volts) and not the 3.3V input because the battery pack will exceed 3.3V.

Battery pack

Finally, I connected the red and black wires from the Teensy to a 3xAA (4.5V) battery pack and toggle switch.

The complete circuit

Software considerations

I’ve appended the code for reference.

The PJRC website has instructions on flashing the Teensy 4. I disconnected the Teensy from the battery pack. Then, I used a micro-B USB cable to connect it to my computer and used teensy-loader-cli to transfer a compiled image.

I wrapped everything in a Nix flake so you can see exactly what is needed.

Costs

I ended up spending about £300 for everything, but it could definitely be a lot cheaper:

  £54 TS101 soldering iron
+ £52 Omnifixo "third hand" tool
+ £42 Ratcheted crimper
+ £11 Wirestrippers
+ £6 Wirecutters

= £165 for tools

  £65 GPO 746
+ £22 Teensy
+ £14 Audio adapter
+ £11 microSD card
+ £10 22 AWG Wire
+ £7 Heat shrink Tubing
+ £6 3xAA Battery packs
+ £5 Audio breakout board
+ £5 Toggle switches
+ £1 Header pins

= £311 total

If you’re trying to save money, another option is to visit a local hackerspace. They may provide tools and components for free or very cheap.

Final thoughts

After starting this project, I also felt emboldened to fix some things around the house: a damaged power cord on my hand blender, an electric toothbrush, the battery on my headphone amp.

As for the telephone, I’d like to get the rotary dial, ringer, and microphone working. The ringer may need a 9V battery. It seems like a hassle to require two kinds of batteries, so that’s an open question.

Overall, I’m very pleased with how it turned out.

Inspiration & resources

Appendix

Tools

Materials

Optional

  • 3xAA Battery pack: To power it without a USB cable.
  • Toggle switch for the battery: Why drain the battery when it’s not in use?
  • Pin headers and jumper cable: These let me easily switch the inputs/outputs. of the Teensy without soldering - useful for reuse and experimentation.
  • 3.5mm jack breakout board: Lets you avoid cutting an audio cable.
  • Fork terminals: Connects wire to terminals. Must match wire and terminal sizes.

Code

For the latest version of this code, please check the repository.

#include <Arduino.h>
#include <Audio.h>
#include <AudioStream.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <Bounce2.h>

#define DEBOUNCE_INTERVAL_MS 40
#define SERIAL_WAIT_TIMEOUT_MS 5000
#define SERIAL_BAUD_RATE 9600

// Pins
#define SDCARD_CS_PIN 10
#define SDCARD_MOSI_PIN 11
#define SDCARD_SCK_PIN 14
#define HOOK_PIN 0

#define DEBUG false

#define debug_printf(...) \
  do { if (DEBUG) Serial.printf(__VA_ARGS__); } while (0)

// Handset audio output.
AudioOutputI2S audio_out;
// Play files from the SD card.
AudioPlaySdWav play_wav;
// 
// There are two connections for L and R speakers.
AudioConnection wav_out_0(play_wav, 0, audio_out, 0);
AudioConnection wav_out_1(play_wav, 0, audio_out, 1);

// The chip on the Teensy Audio Shield that lets you control the microphone
// input and headphone output.
AudioControlSGTL5000 sgtl5000;

// A debounced "button" for the hook switch.
Bounce2::Button hook_switch_button = Bounce2::Button();

// Upload these to the SD card.
const char* FILENAMES[] = { "foo.wav", "bar.wav", "baz.wav" };

void setup_debugging() {
  if (!DEBUG) {
    return;
  }

  Serial.begin(SERIAL_BAUD_RATE);

  while (!Serial && millis() < SERIAL_WAIT_TIMEOUT_MS) {
    // Wait for the serial port to connect.
  }

  debug_printf("AUDIO_BLOCK_SAMPLES is %d.\n", AUDIO_BLOCK_SAMPLES);

  if (CrashReport) {
    /* print info (hope Serial Monitor windows is open) */
    Serial.print(CrashReport);
  }
}

extern "C" void setup(void) {
  setup_debugging();

  // Initialise random.
  uint32_t seed = micros();
  debug_printf("random seed is %d.\n", seed);
  randomSeed(seed);

  // 120 blocks of audio memory from which you construct audio_block_t.
  AudioMemory(120);

  // Enable headphone/mic.
  sgtl5000.enable();
  sgtl5000.volume(0.5);

  // Setup SD card.
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);

  hook_switch_button.attach(HOOK_PIN, INPUT_PULLUP);
  hook_switch_button.interval(DEBOUNCE_INTERVAL_MS);
  hook_switch_button.setPressedState(HIGH);


  if (!(SD.begin(SDCARD_CS_PIN))) {
    while (1) {
      debug_printf("Unable to access the SD card.\n");
      delay(1000);
    }
  }

  debug_printf("HIGH is %d\n", HIGH);
  debug_printf("LOW is %d\n", LOW);
}

extern "C" int main(void) {
  setup();

  while (1) {
    hook_switch_button.update();

    if (hook_switch_button.changed()) {
      debug_printf("Hook switch is now %d.\n", hook_switch_button.read());
    }

    if (hook_switch_button.isPressed() && play_wav.isPlaying()) {
      play_wav.stop();
    }

    if (hook_switch_button.fell() && play_wav.isStopped()) {
      int index = random(std::size(FILENAMES));
      debug_printf("Playing track #%d: %s.\n", index, FILENAMES[index]);
      play_wav.play(FILENAMES[index]);
    }
  }
}