Getting Arduino to detect dial and hook seperately on vintage rotary phone

Hi! I'm trying to help with the haunted house/maze at school and we thought it would be awesome to have an old phone which plays files from a flashdrive if people get the numbers right. We got it hooked up to the arduino and it is detecting the numbers we dial, only one issue...

It doesn't seem to be able to separate the hook from the dial. So if you start dialing the serial will read out "receiver on, receiver off" over and over. But if you remove the receiver option from the code, the very same wire will print out the proper dialed numbers. Of course the issue is they need to be able to hang up the phone to reset in case they get it wrong. SO the two have to work at the same time. Any ideas? I'd imagine the phone must have a way to tell the difference between the pulses of the switch-hook and the dial -- or how did it ever work?

That's the way those old phones work. The dial is clicking it on and off the hook over and over really fast. Once for a 1, twice for a 2, three times for a three ... ten times for a 0. You can actually dial any landline phone without the dial or keypad by simply tapping the hook over and over in the right pattern. If you listen on the line while dialing an old pulse (not a tone) phone then you can hear the clicks as it pulses the hook.

If you want it to work a different way then you'll have to rewire the phone so that the hook and the dial are operating separate switches.

Delta_G:
That's the way those old phones work. The dial is clicking it on and off the hook over and over really fast. Once for a 1, twice for a 2, three times for a three ... ten times for a 0. You can actually dial any landline phone without the dial or keypad by simply tapping the hook over and over in the right pattern. If you listen on the line while dialing an old pulse (not a tone) phone then you can hear the clicks as it pulses the hook.

If you want it to work a different way then you'll have to rewire the phone so that the hook and the dial are operating separate switches.

I remember some guys in College trying to dial numbers that way. Never got it to work because we would loose concentration part way through the number.

Paul

Delta_G:
That's the way those old phones work. The dial is clicking it on and off the hook over and over really fast. Once for a 1, twice for a 2, three times for a three ... ten times for a 0. You can actually dial any landline phone without the dial or keypad by simply tapping the hook over and over in the right pattern. If you listen on the line while dialing an old pulse (not a tone) phone then you can hear the clicks as it pulses the hook.

If you want it to work a different way then you'll have to rewire the phone so that the hook and the dial are operating separate switches.

Aha, I was afraid you might say something like that. What about a workaround where the code resets after five numbers are entered and a sound plays? Would that be a way to sort of cheat it?

Paul_KD7HB:
I remember some guys in College trying to dial numbers that way. Never got it to work because we would loose concentration part way through the number.

Paul

I blew someone's mind one night when we were trapped in a building in a power outage and the phones there wouldn't work without power. I noticed that they still had dial tones, it was just the touch-tone base itself that didn't work. So I dialed out with the hook and called someone to come get us. They called me Macgyver after that.

Despereaux:
Aha, I was afraid you might say something like that. What about a workaround where the code resets after five numbers are entered and a sound plays? Would that be a way to sort of cheat it?

That depends on a LOT of things you haven't told us yet. Personally, I would rewire the phone so I could take two separate outputs.

Delta_G:
That depends on a LOT of things you haven't told us yet. Personally, I would rewire the phone so I could take two separate outputs.

Oh, I thought you were tossing that out there as a sort of last resort. Do you think it would actually be doable?

Despereaux:
Oh, I thought you were tossing that out there as a sort of last resort. Do you think it would actually be doable?

Absolutely. Open up the phone and see if you can ferret out how it works. It can't be that hard. If I had an old rotary phone here I'd look. But there have to be two switches, one in the dial and one in the hook. Just get the signal from the two separately. Three wires. One for each switch and a common ground.

Delta_G:
That depends on a LOT of things you haven't told us yet. Personally, I would rewire the phone so I could take two separate outputs.

Totally ridiculous! :roll_eyes:

What we have not seen so far, is the code. And the answer is in the code.

It is unbelievably simple. The dialling rate is ten impulses per second. (I have a meter here which if it had batteries in it, tests a dial and indicates with resonant reeds, also indicating the mark-space ratio with a common d'Arsonval meter.) So you simply time breaks in the current - to do which you have apparently already implemented a circuit.

If the break lasts for more than 200 ms, it is an "on hook" indication. If it does not, you count it and if there is longer than 500 ms between breaks, that digit dialled is complete.

Delta_G:
Absolutely. Open up the phone and see if you can ferret out how it works. It can't be that hard. If I had an old rotary phone here I'd look. But there have to be two switches, one in the dial and one in the hook. Just get the signal from the two separately. Three wires. One for each switch and a common ground.

Okay, so I looked inside and the hook switch, where the receiver is placed, has four wires in it. When you press down the metal moves between contact with a given two those. The fours wires then lead in a bunch of different directions. So are you saying I need to connect those four wires to a single wire? Then for the dialer, there are five wires that come out of it, blue, green, red, black and white. I'm guessing I need to splice some of those into a singe wire leading to the arduino? Let me know if any of this makes sense. I could take a picture maybe?

Paul__B:
Totally ridiculous! :roll_eyes:

What we have not seen so far, is the code. And the answer is in the code.

It is unbelievably simple. The dialling rate is ten impulses per second. (I have a meter here which if it had batteries in it, tests a dial and indicates with resonant reeds, also indicating the mark-space ratio with a common d'Arsonval meter.) So you simply time breaks in the current - to do which you have apparently already implemented a circuit.

If the break lasts for more than 200 ms, it is an "on hook" indication. If it does not, you count it and if there is longer than 500 ms between breaks, that digit dialled is complete.

This all sounds very clever if it could work! Here is the code, let me know how if it seems possible.

// DEFINES
#define DEBUG

// INCLUDES
#include <SPI.h> // Generic SPI interface, which is used to access the card reader
#include <SD.h> // Provides functionality for reading/writing to SD cards
// Provides audio playback functionality
// Download from https://github.com/TMRh20/TMRpcm
// Note that the buffSize declaration in pcmConfig.h was increased to 254 in order to reduce crackle.
#include <TMRpcm.h>

// CONSTANTS
// Configuration
// Also plug in white wire from telephone to Arduino GND
const byte phonePin = 9; // Red wire from telephone
const byte hookPin = 8; // Green wire from telephone
const byte lockPin = 2; // Connected to relay
const byte chipSelectPin = 4;
const unsigned long debounceDelay = 5;   // ms
// The max separation (in ms) between pulses of a digit being dialled
const unsigned long maxPulseInterval = 250;  // ms
const int numDigitsInPhoneNumber = 5;

// GLOBALS
// Declare a global TMRpcm object for controlling audio playback
TMRpcm tmrpcm;
// The char representation of the number dialled (+1 to allow for string-terminating character \0)
char number[numDigitsInPhoneNumber + 1];
// The digit number 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 handset starts "on hook" (i.e. placed on the base unit)
stateType state = ON_HOOK;
// In order to record "pulses" on the line, we keep track of the last pin reading...
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 (with internal pullup resistors), although
  // may be reassigned as outputs later
  pinMode(phonePin, INPUT_PULLUP);
  pinMode(hookPin, INPUT_PULLUP);

  // The lock pin will receive a LOW signal to release
  digitalWrite(lockPin, HIGH);
  pinMode(lockPin, OUTPUT);

  // Start the serial connection
  Serial.begin (9600);
  Serial.println (F("Serial connection started"));

  // Open the connection to the SD card
  if (!SD.begin(chipSelectPin)) {  // see if the card is present and can be initialized:
    Serial.println("SD card initialization failed!");
    return;   // don't do anything more if not
  }
  
  // Volume range from 0 to 7
  tmrpcm.setVolume(4);
  // Enable 2x oversampling
  tmrpcm.quality(1);

  Serial.println("Setup Complete");
}
  
void loop () {

  // Is the receiver lifted off the handset?
  int hookValue = digitalRead(hookPin);

  // If the receiver is lifted, but wasn't previously
  if(hookValue == 0 && state == ON_HOOK) {

    // Print some debug info
    #ifdef DEBUG
      Serial.println("Receiver Lifted");
    #endif

    // Update the state
    state = OFF_HOOK;
  }
  
  // If the receiver is on the hook, but wasn't previously
  else if(hookValue == 1 && state != ON_HOOK){

    // Print some debug info
    #ifdef DEBUG
      Serial.println("Receiver Replaced");
    #endif

    // Update the puzzle state
    state = ON_HOOK;

    // Clear any information about the number we were dialling
    pulseCount = 0;
    currentDigit = 0;

    // Stop any audio
    tmrpcm.stopPlayback();

    // Put the pin back into input state
    pinMode(phonePin, INPUT_PULLUP);
  }

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

    // Record the current timestamp
    now = millis();

    // Test the value of the phone pin
    int pinReading = digitalRead (phonePin);

    // If the value has changed
    if (pinReading != previousPinReading) {

      // The user is dialling a number
      state = DIALLING;

      // "Debouncing" method to ignore jittery fluctations in readings
      // If the time elapsed since the last time this pin was changed is only a small amount of time
      if (now - timePinChanged < debounceDelay) {
        // Don't do anything 
        return;
      }

      // A HIGH signal means that a dialling pulse has been detected 
      if(pinReading == HIGH){
        pulseCount++;
      }

      // Update the stored time and reading values for further comparison
      timePinChanged = now;
      previousPinReading = pinReading;
    }

    // We've recorded a sequence of pulses, and the time since the last pulse was detected
    // is longer than the maxPulseInterval
    if (((now - timePinChanged) >= maxPulseInterval) && pulseCount > 0) {

      // If we haven't yet dialled a complete number
      if (currentDigit < numDigitsInPhoneNumber) {

        // The digit '0' is represented by 10 pulses
        if (pulseCount == 10) { pulseCount = 0; }

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

        // Append the most recent digit dialled onto the end of the number array
        number[currentDigit] = pulseCount | '0';

        // Increment the counter
        currentDigit++;

        // Initialise the next value
        number[currentDigit] = 0;
      }

      // If we've dialled the correct number of digits
      if (currentDigit == numDigitsInPhoneNumber) {

        // Print some debug information
        #ifdef DEBUG
          Serial.print (F("Number dialled: "));
          Serial.println (number);
        #endif

        // This number plays a recorded message 
        if(strcmp(number, "21800") == 0){
          #ifdef DEBUG
            Serial.println (F("Playing sound"));
          #endif
          // Now, we set the pin as OUTPUT for the audio signal
          pinMode(phonePin, OUTPUT);
          // Set the TMRPCM library to use the pin for output
          tmrpcm.speakerPin = 9; // Must be 5, 6, 11 or 46 on MEGA, 9 on UNO, Nano, etc
          // Play the appropriate sound file
          tmrpcm.play("shoe.wav");
          // Wait until the receiver is replaced on the handset
          while(!digitalRead(hookPin)){ delay(1000); }
        }
       
        // If an incorrect number was dialled
        else {
          // Set the pin as OUTPUT
          pinMode(phonePin, OUTPUT);
          // Set the TMRPCM library to use the pin for output
          tmrpcm.speakerPin = 9; // Must be 5, 6, 11 or 46 on MEGA, 9 on UNO, Nano, etc
          // Play the appropriate sound file
          tmrpcm.play("BOMB.WAV");
          // Now wait for the audio to play
          delay(2500);
        }

        // Set the puzzle state to complete
        state = CONNECTED;
      }

    // This digit has been processed, so reset the pulse counter for the next digit         
    pulseCount = 0;
    }
  }
}

Despereaux:
This all sounds very clever if it could work! Here is the code, let me know how if it seems possible.

Sorry, it is a bit late at night to work fully through your code, I suspect I would structure it differently.

It is not a question of "if it could work"; what I described is how it does work because that is precisely what a telephone exchange is. It appears you have a means of reading the line current of the phone - which is powered by some supply.

OK, I did it anyway! The general structure of the code is then:

State on-hook: Wait for off-hook. If so, clear impulses, clear numbers[], wait-for-number.
State wait-for-number: Wait for break. If so, time-break. Count out; if exceeds 5 seconds, go to reject.
State time-break: Count out the break; if exceeds 500 ms, go on-hook, else increment impulses and enter dialling.
State dialling: Count out the off-hook, if does not exceed 500 ms, go to time-break, else add impulses to numbers[] array, enter verify.
State verify: Check numbers[] array; if correct go to play; if a wrong number go to reject; otherwise wait-for-number.
State reject: Play rejection message while checking for break; if break, delay 2 seconds and go on-hook.
State play: Play success message while checking for break; if break, delay 2 seconds and go on-hook.

I have not added dial tone; that simply involves splitting wait-for-number and time-break into two.

Note that this is a state machine, all the "wait" steps will be a loop escape based on millis().

This might be helpful:
https://forum.arduino.cc/index.php?topic=372681.msg2571999#msg2571999

Certainly might - it looks very similar to what I outlined. :grinning: