Help with setting up Arduino Mega and GPO746 rotary telephone and SD card module

Hi,

I am currently attempting to wire up a GPO746 rotary dial phone to an Arduino Mega for part of a part I am organising.

I am very new to this, so any help would be greatly appreciated.

The idea is to dial a correct number sequence (62442) to play an audio file from an SD card module. Any other five digit number dialled will play an alternative audio file from the SD card.

I have followed the instructions on this video; Call Me! Rotary Telephone + Arduino Escape Room Puzzle - YouTube which uses a vintage GPO746, mine is a new one.

When I use this code;

#define DEBUG

#include <SPI.h> 
#include <SD.h> 
// buffSize declaration in pcmComfig.h was increased to 254 to reduce crackle
#include <TMRpcm.h>

const byte phonePin = 6; // Red wire from phone
const byte hookPin = 8; // Green wire from phone
const byte chipSelectPin = 53;
const unsigned long debounceDelay = 5; //ms
const unsigned long maxPulseInterval = 500; // ms
const int numDigitsInPhoneNumber = 5;

//GLOBALS

TMRpcm tmrpcm;
//the char representation of the number dialled to (1+ to allow for string-terminating character \0)
char number[numDigitsInPhoneNumber + 1];
//the digit currently being dialled
int currentDigit;
//how many pulses have been detected for the current digit
int pulseCount;
//states in which the telephone can be
typedef enum { ON_HOOK, OFF_HOOK, DIALLING, CONNECTED } stateType;
//assume that the handest starts "on hook"
stateType state = ON_HOOK;
//keep track of last pin
int previousPinReading = HIGH;
//the time at which the pin last changed value
unsigned long timePinChanged;
//and the current time
unsigned long now = millis ();

void setup() {
  // both pins will initially be set as inputs
  pinMode(phonePin, INPUT_PULLUP);
  pinMode(hookPin, INPUT_PULLUP);

 Serial.begin (9600);
 Serial.println (F("Serial connection started"));

 if (!SD.begin(chipSelectPin)) { //sees if card is present and intialise
  Serial.println("SD card initialisation failed!");
  return; //don't do anything more if not
 }

 tmrpcm.setVolume(4);
 tmrpcm.quality(1);

 Serial.println("Setup Complete");
 
}

void loop() {
  // put your main code here, to run repeatedly:

int hookValue = digitalRead(hookPin);

if(hookValue == 0 && state == ON_HOOK) {

  #ifdef DEBUG
    Serial.println("Receiver Lifted");
  #endif

  state = OFF_HOOK;
}

else if(hookValue == 1 && state != ON_HOOK) {

  #ifdef DEBUG
    Serial.println("Receiver Replaced");
  #endif

  state = ON_HOOK;

  pulseCount = 0;
  currentDigit = 0;

  tmrpcm.stopPlayback();

  pinMode(phonePin, INPUT_PULLUP);
  
}

if(state == OFF_HOOK || state == DIALLING) {

  now = millis();

  int pinReading = digitalRead (phonePin);

  if (pinReading != previousPinReading) {

  state = DIALLING;

  if (now - timePinChanged < debounceDelay) {

    return;
   
  }

  if(pinReading == HIGH) {
    pulseCount++;
  }

  timePinChanged = now;
  previousPinReading = pinReading;

}

if (((now - timePinChanged) >= maxPulseInterval) && pulseCount > 0) {

   if (currentDigit < numDigitsInPhoneNumber) {

    if (pulseCount == 10) { pulseCount = 0; }

    #ifdef DEBUG
      Serial.println (F("Digit dialled: "));
      Serial.println (pulseCount);
    #endif

    number[currentDigit] = pulseCount | '0';

    currentDigit++;

    number[currentDigit] = 0;
}

if (currentDigit == numDigitsInPhoneNumber) {

  #ifdef DEBUG
  Serial.println (F("Number dialled: "));
  Serial.println (number);
  #endif

if(strcmp(number, "62442") == 0) {
  #ifdef DEBUG
    Serial.println (F("Playing Sound"));
  #endif

 pinMode(phonePin, OUTPUT);

 tmrpcm.speakerPin = 6;

 tmrpcm.play("clue");

 while(!digitalRead(hookPin)){ delay(1000); }
}

else {
  pinMode(phonePin, OUTPUT);
  tmrpcm.speakerPin = 6;
  tmrpcm.play ("fail");
  delay(2500);
}
  
state = CONNECTED;
}
pulseCount=0;
}
}
}

Once I run the program and lift and replace the receiver only once, the serial monitor displays, the following, it indicates three distinct lifts and replace despite only doing once, and also does not recognise the dial being used at all.

Serial connection started
Setup Complete
Receiver Lifted
Receiver Replaced
Receiver Lifted
Receiver Replaced
Receiver Lifted
Receiver Replaced

If I modify the code in this manner:

#define DEBUG

#include <SPI.h> 
#include <SD.h> 
#include <TMRpcm.h>

const byte phonePin = 6; // Red wire from phone
const byte hookPin = 8; // Green wire from phone
const byte chipSelectPin = 53;
const unsigned long debounceDelay = 10; //ms
const unsigned long maxPulseInterval = 250; // ms
const int numDigitsInPhoneNumber = 5;

TMRpcm tmrpcm;
char number[numDigitsInPhoneNumber + 1];
int currentDigit = 0;
int pulseCount = 0;

typedef enum { ON_HOOK, OFF_HOOK, DIALLING, CONNECTED } 
stateType;
stateType state = ON_HOOK;

int previousPinReading = LOW;
unsigned long timePinChanged = 0;

unsigned long now = millis ();

void setup() {
  
 Serial.begin (9600);
 Serial.println (F("Serial connection started"));

  pinMode(hookPin, INPUT_PULLUP);
  pinMode(phonePin, INPUT_PULLUP);
  

 if (!SD.begin(chipSelectPin)) { //sees if card is present and intialise
  Serial.println("SD card initialisation failed!");
  return; //don't do anything more if not
 }

 tmrpcm.setVolume(4);
 tmrpcm.quality(1);

 Serial.println("Setup Complete");
 
}

void loop() {

 int pinReadingOn = digitalRead(phonePin);
 int hookValue = digitalRead(hookPin);

 if(hookValue == 0 && state == ON_HOOK) {

  #ifdef DEBUG
    Serial.println("Receiver Lifted");
  #endif

  state = OFF_HOOK;
  delay(100);

}

else if(hookValue == 1 && state != ON_HOOK && pinReadingOn != HIGH) {

  #ifdef DEBUG
    Serial.println("Receiver Replaced");
  #endif

  state = ON_HOOK;
  delay(100);

  pulseCount = 0;
  currentDigit = 0;

  tmrpcm.stopPlayback();

  pinMode(phonePin, INPUT_PULLUP);
  
}

if(state == OFF_HOOK || state == DIALLING) {

  now = millis();

  int pinReading = digitalRead (phonePin);

  if (pinReading != previousPinReading) {

  state = DIALLING;

  if (now - timePinChanged < debounceDelay) {

    return;
   
  }

  if(pinReading == HIGH) {
    pulseCount++;
  }

  timePinChanged = now;
  previousPinReading = pinReading;

}

if (((now - timePinChanged) >= maxPulseInterval) && pulseCount > 0) {

   if (currentDigit < numDigitsInPhoneNumber) {

    if (pulseCount == 10) { pulseCount = 0; }

    #ifdef DEBUG
      Serial.println (F("Digit dialled: "));
      Serial.println (pulseCount);
    #endif

    number[currentDigit] = pulseCount | '0';

    currentDigit++;

    number[currentDigit] = 0;

    pulseCount = 0;
}

if (currentDigit == numDigitsInPhoneNumber) {

  #ifdef DEBUG
  Serial.println (F("Number dialled: "));
  Serial.println (number);
  #endif

if(strcmp(number, "62442") == 0) {
  #ifdef DEBUG
    Serial.println (F("Playing Sound"));
  #endif

 pinMode(phonePin, OUTPUT);

 tmrpcm.speakerPin = 6;

 tmrpcm.play("clue");

 while(!digitalRead(hookPin)){ delay(1000); }
}

else {
  pinMode(phonePin, OUTPUT);
  tmrpcm.speakerPin = 6;
  tmrpcm.play ("fail");
  delay(2500);
}
  
state = CONNECTED;
}
pulseCount=0;
}
}
}

When I run the program and lift and replace the receiver the following is displayed, it only recognises the one lift and replace but gets them the wrong way round, and displays digit 1 despite not dialling a number. Dialling a number on the dial is still not recognised.

Serial connection started
Setup Complete
Receiver Replaced
Receiver Lifted
Digit dialled:
1

I have attached an image of the setup, I have run jumper wires directly to the circuit board to eliminate any issues with the phone cable.

Any help would be amazing, thank you so much in advance.

Dan

Hello Dan,

I am currently attempting to wire up a GPO746 rotary dial phone

I don't know what this is:


How to post an image
But it most certainly is NOT a GPO746 dial telephone.

This is a GPO746 dial telephone:

As for your code, I don't know, but I hope someone else will be able to be more helpful.

Please note that dial pulses are 66ms break, 33ms make, so any timings that are longer or even close to 33ms will not capture dial pulses reliably.

Possibly you mixed up the phone wires?

I used such a dial for input to my first computer in the 60's, after speeding up the slow pulse generation :slight_smile:

I'm not familiar with US phones, but I'd suggest to build a simple battery-LED tester or write a test sketch that copies the switch states to LEDs for further diagnosis.

Hello Dan,

Perhaps I should explain a little more; I work in telecoms in the UK, have done since 1977, and before that as a boy always found telephones fascinating as long back as I can remember. The photo I showed is of the original GPO type 746 telephone, and is the only thing I recognise as being a 746 telephone. I am vaguely aware that someone bought the rights to the name GPO, or maybe they didn't need to buy any rights, maybe the name 'GPO' was not protected. Anyway, whoever now has the rights to the name GPO is, apparently, producing telephones that are styled similarly to proper 746 telephones and calling them GPO746 telephones. Nothing will persuade me that these poor quality imitations of actual 746 telephones are GPO 746 telephones, they are horrible fakes.

Good luck with your project.

PerryBebbington:
Perhaps I should explain a little more; I work in telecoms in the UK, have done since 1977, and before that as a boy always found telephones fascinating as long back as I can remember.

That would clearly explain your fussiness. :grinning:

I used to spend an hour or so many afternoons after Uni, reading the PMG manuals at the Tech college opposite Central railway.

Sigh! :sunglasses:

I supplied a sketch to someone once doing a similar project to read the number dialed on a vintage rotary phone. Maybe it helps. It is in post #3 here:

However, that phone looks like it has electronics in it which may have to powered before it does anything but be careful connecting an Arduino to anything that is powered by an external source. A dial pulse and an off hook/on hook transition can look very similar. It used to be possible to “dial” a number by rapidly pressing the buttons exposed when the receiver was lifted.

6v6gt:
A dial pulse and an off hook/on hook transition can look very similar. It used to be possible to “dial” a number by rapidly pressing the buttons exposed when the receiver was lifted.

The same here in Germany :slight_smile:

I have a similar project where I have a U.S. dial phone that can dial out, receive calls, ring, and other than no microphone, it works just like a real phone. The project is best described as an Escape Room prop.

The dialer and most logic runs on a Wemos D1 Mini, and it communicates with an Uno that has an Adafruit Wav Shield for the audio.

I looked at the initial commit and I’ve been working on this for ten months. Whew! And it’s still a work in progress.

I don’t know if you can download it from the git because the repository is private, but try it.
https://github.com/SteveRMann/Telephone

6v6gt:
However, that phone looks like it has electronics in it which may have to powered before it does anything but be careful connecting an Arduino to anything that is powered by an external source. It used to be possible to “dial” a number by rapidly pressing the buttons exposed when the receiver was lifted.

Dan, 6v6gt has a good point, how are you powering the phone? Telephone line power is -50V fed through a pair of constant current sources, which means the phone itself sits at roughly -25V with respect to ground. If you connect circuitry to the phone that is grounded you will have problems.

A dial pulse and an off hook/on hook transition can look very similar.

The difference is in the timing. Dial pulses are 66.67ms interruptions to the current with 33.33ms make periods between each pulse. There is an inter-digit pause between pulses of something like 300ms, but I can't remember the exact figure.

SteveMann:
I don't know if you can download it from the git because the repository is private, but try it.
https://github.com/SteveRMann/Telephone

404 error.
Steve, as I am someone who knows a tiny bit about telephones, do you need any help?

There are stacks of these replicas about, for example:

I don't know the significance of this statement:
"Compatible with modern telephone banking switchboard etc"

But I wonder if it converts the dial pulses into tone dialing (DTMF)
EDIT: I've just noticed a * and # on the dial, which would seem to confirm it is DTMF.

A genuine vintage phone, for example: https://www.ebay.co.uk/itm/Vintage-Bakelite-GPO-328-On-Off-Bell-Telephone/174318702576 should (apart from the bell) work with 5 volts, in your case to detect dial pulses and hook state. That is, connecting one side to an Arduino pin with the inbuilt pull up resistor enabled should work.

s-l225.jpg

s-l225.jpg

6v6gt:
A genuine vintage phone, for example: https://www.ebay.co.uk/itm/Vintage-Bakelite-GPO-328-On-Off-Bell-Telephone/174318702576 should (apart from the bell) work with 5 volts, in your case to detect dial pulses and hook state. That is, connecting one side to an Arduino pin with the inbuilt pull up resistor enabled should work.

Will it really?

As I recall, the 2 µF bell capacitor in conjunction with the 1 k resistance bell serves as the snubber for the dial contacts, so with the current provided by INPUT_PULLUP, I doubt that the dialling impulses would actually be detected (time constant 92 milliseconds)! :roll_eyes:

Thanks all for the input,

The one I am using is indeed a retro GPO746, and the newer versions are not pulse dialling, they are tone dialling, so I have ordered a vintage one off eBay, to see if I can get this to work instead

Those nasty imitation 746 are DTMF not pulse dial like the genuine 706. I think that Perry will be able to tell us if the genuine 746 (push button 706) would do pulse dial as well? With the age of them I assume that they must do.

As an aside, I am still using 706s for my desk 'phones because they are just so much better than modern junk. I use a DTMF keypad on the mouthpiece to deal with menus etc. Maintainence is confined to a sharp rap on the desk when it gets crackly.

As I recall, the 2 µF bell capacitor in conjunction with the 1 k resistance bell serves as the snubber for the dial contacts, so with the current provided by INPUT_PULLUP, I doubt that the dialling impulses would actually be detected (time constant 92 milliseconds)!

Hi Paul,
On every phone I've seen the bell is disconnected from the line when off hook. If the bell is connected to the line while dialling, for example because it is mis-wired, then it tinkles to the dial pulses. Dialling is 10 pulses per second, which is close enough the 17Hz or 25Hz ringing for the bell to respond to it and tinkle. Bells for 2 or more phones were wired in series, on reason being that any phone off hook could disconnect all the bells and stop them from tinkling. In the Telephone type 332 schematic you have posted that does not seem to be the case, but that is seriously ancient and not how a 746 is wired. A 92ms time constant across the line or dial contacts would interfere with dialling. There are 'off normal' contacts in the dial that short things out during dialling, partly to provide a clean loop and partly to prevent loud clicks (painfully loud clicks) in the ear piece.

I would think a genuine 746 would provide a useful input to an Adruno at 5V with a pull up resistor and nothing else, if they only purpose was to detect on and off hook and dial pulses. If you want to get into feeding audio in then you need something more complicated, here is the circuit I used for a CMOS analogue switch based telephone exchange:

AJLElectronics:
Those nasty imitation 746 are DTMF not pulse dial like the genuine 706. I think that Perry will be able to tell us if the genuine 746 (push button 706) would do pulse dial as well? With the age of them I assume that they must do.

706 and 746 are both mechanical dial telephones, not push button. 756 and 8756 were push button pulse dial. 782 were DTMF.

As an aside, I am still using 706s for my desk 'phones because they are just so much better than modern junk. I use a DTMF keypad on the mouthpiece to deal with menus etc. Maintenance is confined to a sharp rap on the desk when it gets crackly.

In that case it has an original carbon granule microphone, you need to replace it with an electret microphone, try Britphone to see if they have any in stock.

Thanks for the corrections Perry.

PerryBebbington:
On every phone I've seen the bell is disconnected from the line when off hook.

Yes, that one is a bit antique. :roll_eyes:

I do agree. I couldn't find a circuit for a 312 or 332 so that is what I came across. I cannot imagine where in my dungeon I would find one.

Here is an 802 (variant) diagram - clearly genuine! The bell does get disconnected off hook but the capacitor is re-purposed as part of the bridge and becomes the snubber for the dial contacts which would defeat the built-in pullup of the Arduino which is the point I was making. :grinning: Clearly a stiffer pullup would be required.


And here is something which sits on the shelf in my office.

thingy.jpg
Expand!

I’m reasonably sure that the tests I made with that dial reader sketch, which simply used the power via the pull-up resistor, were made with the phone pictured here.

Of course it is not a GPO pattern phone. It is a Swiss “wet room” type. But I guess it works on a similar principle. If I recall correctly, there is a wiring diagram inside. I’ll post it when I get a chance.

What, incidentally, is the value of the resistor(s) which appear to be put in series with the 2uF capacitor when the phone is off hook?

Well, I guess the OP will be away for some time, awaiting the arrival of a genuine vintage phone, so we can continue this discussion here.

OK. I have verified the results that I published and that an Arduino Uno pin with builtin pull-up resistor appears to work fine for these tests. No further power source for the telephone is required for dial pulse detection or hook state.

The sketch is this:

/*
 * Detect telephone Dial Pulses
 * Handle contact bounce
 *
 * Connect phone to pin 2 and ground to demonstrate
 *
 * Author: 6V6GT
 */




uint8_t inPin = 2 ;
uint8_t pulseCount = 0 ;
bool readClean = LOW ;
bool readCleanLast = LOW ;
uint32_t readChangeAtMs = 0 ;

uint32_t pulseTimerMs = 0 ;
enum class DialState { pendingDigit, inDigit, gotDigit } ;
DialState dialState ;



void setup() {
  Serial.begin(115200) ;
  Serial.println( F( "telPulseReader V0_01" ) ) ;
  pinMode( inPin, INPUT_PULLUP ) ;
  dialState = DialState::pendingDigit ;

  // readChangeAtMs = millis() ; // !!
  delay (1000 ) ;
  Serial.print( F( "initial read=" ) ) ;
  Serial.println( digitalRead( inPin ) ) ;
}

void loop() {
  

  if ( dialState == DialState::inDigit ) {
    if ( millis() - pulseTimerMs > 200 ) {
      // we have a digit
      Serial.println( pulseCount ) ;
      pulseCount = 0 ;
      dialState = DialState::pendingDigit ;
    }
  }

  // filter read for stable changes ( > X mS )
  static bool lastRead = LOW ;
  bool currentRead = digitalRead( inPin ) ;
  if ( lastRead != currentRead ) {
    readChangeAtMs = millis() ;
    lastRead = currentRead ;
  }
  if ( readClean != currentRead ) {
    // change to readClean accepted only if the new value is mature
    if ( millis() - readChangeAtMs > 25 ) readClean = currentRead ;
  }
  if ( readCleanLast != readClean ) {
    if ( readClean == HIGH ) {
      // change to Space
      pulseTimerMs = millis() ;
      if ( dialState == DialState::pendingDigit ) {
        dialState = DialState::inDigit ;
        pulseCount = 1 ;
      }
      else if ( dialState == DialState::inDigit ) {
        pulseCount++ ;
      }
      else {
        // error
      }
    }
    readCleanLast = readClean ;
  }

}

The results are :

telPulseReader V0_01
initial read=0
6
5
9
10
6
10
10
1
1

The Oscilloscope trace using my cheap USB oscilloscope is this:

The phone circuit diagram is this:

And the open phone :

PerryBebbington:
404 error.
Steve, as I am someone who knows a tiny bit about telephones, do you need any help?

Thanks for the offer. I cloned the git and made it public:
https://github.com/SteveRMann/Telephone-ota

I basically gutted the phone and put into it a Wemos D1 mini, an Arduino Uno and an Adafruit Wav shield.

This is a prototype. I made a PCB for the ESP and ring generator section, so all the terminal strips will go away in the final version.