Please Help!!! Using a Rotary Phone for Password Input

Hello all. I am fairly new to Arduino. I have completed the starter kit, but still struggle with understanding some of the coding for the more in-depth projects. To say I am ignorant in coding would be an understatement, as my background is more business (finance/accounting) associated. So I coming here, asking everyone for help.

I am working on a project which would, in essence, turn an old rotary phone dial into a keypad for entering a multi-digit password. I have scoured the web trying to find any help I can get and have found a good bit of info up to a point. But, my timeframe window is beginning to narrow, now. What I have been able to do so far is: 1) have the serial monitor be able to read what numbers I am dialing, and 2) have an LED light turn on when I dial a single number (like dialing “4”) and turn off when I dial any other number. The code for the latter is below.

What I am asking (more begging) for help with is how to adapt this to have the LED turn on when I dial a specific sequence of number, like a passcode, (i.e. “1, 2, 3, 4”). Any other numbers dialed would results in nothing happening at all. Also, I need the serial.print to only begin when the phone is taken off the receiver. And, if you made a mistake in dialing the passcode, you would reset it by putting the phone back on the receiver (much like what the phone was built to do).

Thank you so much in advance for your help. If I can get this to work, I would be greatly appreciative.

Here’s what I have found so far:

int needToPrint = 0;
int count;
int in = 2;
int lastState = LOW;
int trueState = LOW;
long lastStateChangeTime = 0;
int cleared = 0;

// constants

int dialHasFinishedRotatingAfterMs = 100;
int debounceDelay = 10;

void setup()
{
Serial.begin(9600);
pinMode(in, INPUT);
pinMode(13,OUTPUT);
}

void loop()
{
int reading = digitalRead(in);

if ((millis() - lastStateChangeTime) > dialHasFinishedRotatingAfterMs) {
// the dial isn't being dialed, or has just finished being dialed.
if (needToPrint) {
// if it's only just finished being dialed, we need to send the number down the serial
// line and reset the count. We mod the count by 10 because '0' will send 10 pulses.
Serial.print(count % 10, DEC);
if (count == 4)  //<<<<<<<<<<<<<<<<<<<<<<<<<<<<
{
 digitalWrite (13,HIGH); //<<<<<<<<<<<<<<<<<<<<
}
needToPrint = 0;
count = 0;
cleared = 0;
}
} 

if (reading != lastState) {
lastStateChangeTime = millis();
}
if ((millis() - lastStateChangeTime) > debounceDelay) {
// debounce - this happens once it's stablized
if (reading != trueState) {
// this means that the switch has either just gone from closed->open or vice versa.
trueState = reading;
if (trueState == HIGH) {
// increment the count of pulses if it's gone high.
digitalWrite(13,LOW);
count++; 
needToPrint = 1; // we'll need to print this number (once the dial has finished rotating)
} 
}
}
lastState = reading;
}

But, my timeframe window is beginning to narrow, now.

Why? This is a fun, but impractical project.

There are lots of Arduino projects that successfully use a keypad for PIN password number entry, and Google can help you find them.

akooti:
Thank you so much in advance for your help.

There is a rather old Arduino library available to read rotary phone dials, along with some technical explanations about the "ready switch" and the "pulse switch" in such old rotary dialing units:

Thirst thing I'd make would be: Make this library and its example sketch work!

Thank you for you help. The ultimate goal is the rotary phone will be use to unlock a door, via solenoid. So the phone, itself, will not be connect to a phone line; it will only be connected to a power source.

As for why my timeframe is becoming limited, it's because my deadline is coming up. As I said before, I have gone through the Arduino starter kit, and when I first embarked on this road, I thought I would be able to learn it fairly well in a reasonable amount of time. And, as you have probably deduced, I was COMPLETELY wrong. If you were to ask me how to do the debits/credits and financial statements of a business, I wouldn't have a problem with that at all. But, this completely stumps me (and somewhat frustrates me) because I just have trouble understand it, even the help that's being giving. So, please bear with me.

I still don't understand why there should be a "deadline" for something as impractical as using a rotary phone to unlock a door.

It this for a class?

As you are beginning to discover, for various reasons this is not a simple project and advice from the forum is not going to provide a quick solution. We are happy to help, but not on a deadline!

jremington:
We are happy to help, but not on a deadline!

I apologize for my miscommunication. When I said deadline, I'm not talking like tomorrow. I am asking now because I figured it would take a little while to figure this out. My deadline is in about a month or so.

And you are absolutely correct. I have found this is nowhere near a simple project. I have done more research on this single problem than I think I have in my whole teaching career for anything.

jurs' advice (last line of reply #2) is excellent. Thirst have a beer, though.

There is plenty of info at the link jurs provided. Does your dial work the same as the one demonstrated there? I thought it might be more complicated than it looks like.

Jimmy60:
There is plenty of info at the link jurs provided. Does your dial work the same as the one demonstrated there? I thought it might be more complicated than it looks like.

Yes, as far as I can tell, my dial works the same. The "pulsePin" start as closed and once the dial is engaged the "readyPin" becomes closed and the "pulsePin" goes off and on depending on the number dialed. Also, apparently there is a difference in the pulse to number ratio, depend on the country. I have the version that's a 1-to-1 ratio (i.e. dialing 4 produces 4 pulses, dialing 6 produces 6 pulses). But, dialing zero produces 10 pulses.

But, dialing zero produces 10 pulses.

It would not be very useful if dialing zero produced zero pulses!

The rotary dial , to disgusting from "Touch Tone " dial which was invented later on, works by interrupting the DC circuit - the POTS - plain old telephone service line.
The irruptions are generated during reverse travel of the dial - moving the dial clockwise direction ( forward) does not produce these interruption.

In US the rate of interrupts is 10 PPS (pulses per second ) and each digit has 60 / 40 make / break ratio.
The pulse is 60 ms on and 40 ms off - total 100 ms per digit.

These numbers may not mean much for your design , but in real word it was / is important to maintain these timing values in precisely specified tolerances for the receiving equipment to function properly. For example 60 / 10 pulse ratio would be rejected as noise etc.

You could consider using interrupts for both timing and pulse detection in you project, but you do not have to

I came up with this late last night. It's just two switches. I tested it with some jumpers for switches and it should work. You do need to clearly define how a user should go about entering a combination. Does it reset with a wrong number? How will the user know it's ready for input? Describe how it should work.

So here's the version .00 code :wink:

It uses internal resistors so the dial's switches can be placed between the pin and ground.

const long BAUD_RATE = 115200;
const unsigned long DEBOUNCE_MILLIS = 20UL;
const byte READY_PIN = 2;
const byte PULSE_PIN = 3;

struct mySwitch {
  int state;
  boolean debounceFlag;
  unsigned long debounceTimer;
};

mySwitch Ready;
mySwitch Pulse;

void setup()
{
  Serial.begin(BAUD_RATE);
  pinMode(READY_PIN, INPUT_PULLUP);
  Ready.state = digitalRead(READY_PIN);
  Ready.debounceFlag = false;
  Ready.debounceTimer = 0;
  pinMode(PULSE_PIN, INPUT_PULLUP);
  Pulse.state = digitalRead(PULSE_PIN);
  Pulse.debounceFlag = false;
  Pulse.debounceTimer = 0;
}

void loop()
{
  static boolean dialing = false;
  static byte pulseCount = 0;

  unsigned long currentMillis = millis();

  int readySignal = digitalRead(READY_PIN);
  if (Ready.state != readySignal)
  {
    Ready.state = readySignal;
    Ready.debounceFlag = true;
    Ready.debounceTimer = currentMillis;
  }

  int pulseSignal = digitalRead(PULSE_PIN);
  if (Pulse.state != pulseSignal)
  {
    Pulse.state = pulseSignal;
    Pulse.debounceFlag = true;
    Pulse.debounceTimer = currentMillis;
  }

  if (Ready.debounceFlag && currentMillis - Ready.debounceTimer >= DEBOUNCE_MILLIS)
  {
    Ready.debounceFlag = false;
    if (Ready.state == LOW && !dialing)
    {
      dialing = true;
    }
    else if (Ready.state == HIGH && dialing)
    {
      dialing = false;
      if (pulseCount)
      {
        Serial.println(pulseCount % 10);
        pulseCount = 0;
      }
    }
  }

  if (Pulse.debounceFlag && currentMillis - Pulse.debounceTimer >= DEBOUNCE_MILLIS)
  {
    Pulse.debounceFlag = false;
    if (Ready.state == LOW && Pulse.state == HIGH)
    {
      pulseCount++;
    }
  }
}

Jimmy60:
I came up with this late last night. It's just two switches. I tested it with some jumpers for switches and it should work. You do need to clearly define how a user should go about entering a combination. Does it reset with a wrong number? How will the user know it's ready for input? Describe how it should work.

So here's the version .00 code :wink:

It uses internal resistors so the dial's switches can be placed between the pin and ground.

const long BAUD_RATE = 115200;

const unsigned long DEBOUNCE_MILLIS = 20UL;
const byte READY_PIN = 2;
const byte PULSE_PIN = 3;

struct mySwitch {
  int state;
  boolean debounceFlag;
  unsigned long debounceTimer;
};

mySwitch Ready;
mySwitch Pulse;

void setup()
{
  Serial.begin(BAUD_RATE);
  pinMode(READY_PIN, INPUT_PULLUP);
  Ready.state = digitalRead(READY_PIN);
  Ready.debounceFlag = false;
  Ready.debounceTimer = 0;
  pinMode(PULSE_PIN, INPUT_PULLUP);
  Pulse.state = digitalRead(PULSE_PIN);
  Pulse.debounceFlag = false;
  Pulse.debounceTimer = 0;
}

void loop()
{
  static boolean dialing = false;
  static byte pulseCount = 0;

unsigned long currentMillis = millis();

int readySignal = digitalRead(READY_PIN);
  if (Ready.state != readySignal)
  {
    Ready.state = readySignal;
    Ready.debounceFlag = true;
    Ready.debounceTimer = currentMillis;
  }

int pulseSignal = digitalRead(PULSE_PIN);
  if (Pulse.state != pulseSignal)
  {
    Pulse.state = pulseSignal;
    Pulse.debounceFlag = true;
    Pulse.debounceTimer = currentMillis;
  }

if (Ready.debounceFlag && currentMillis - Ready.debounceTimer >= DEBOUNCE_MILLIS)
  {
    Ready.debounceFlag = false;
    if (Ready.state == LOW && !dialing)
    {
      dialing = true;
    }
    else if (Ready.state == HIGH && dialing)
    {
      dialing = false;
      if (pulseCount)
      {
        Serial.println(pulseCount % 10);
        pulseCount = 0;
      }
    }
  }

if (Pulse.debounceFlag && currentMillis - Pulse.debounceTimer >= DEBOUNCE_MILLIS)
  {
    Pulse.debounceFlag = false;
    if (Ready.state == LOW && Pulse.state == HIGH)
    {
      pulseCount++;
    }
  }
}

I am also "time limited" hence cannot fully analyze the code.

One point I would like to repeat - the telephone rotary pulse has well defined timing, hence the program should use such timing and not blindly apply common debounce code.
That is why using interrupts would be beneficial - detecting both edges of both make or brake pulses.
Even without using interrupts the program should be build using the known timing values.
Just a suggestion, you are of course free to code in form you prefer.

Vaclav:
One point I would like to repeat - the telephone rotary pulse has well defined timing, hence the program should use such timing and not blindly apply common debounce code.

No matter how well the timing is defined, the contacts are physical and do bounce. So I'm not sure what you're getting at here...

aarg:
No matter how well the timing is defined, the contacts are physical and do bounce. So I’m not sure what you’re getting at here…

The pulses length are defined.
The program should be checking from pulse start to pulse end and it should be within allotted time, sort off ignore the pulse until the expected length is reached.

if(pulse ) // positive edge detected}
{
while (current pulse time < expected pulse length ); // wait for expected edge ,ignore pulse debouce if you will
while( pulse); // wait for trailing edge

now check it the pulse (length) was valid

}

Also usable for detecting next digit dialed - inter-digital time is also defined.

Jimmy60:
I came up with this late last night. It’s just two switches. I tested it with some jumpers for switches and it should work. You do need to clearly define how a user should go about entering a combination. Does it reset with a wrong number? How will the user know it’s ready for input? Describe how it should work.

So here’s the version .00 code :wink:

It uses internal resistors so the dial’s switches can be placed between the pin and ground.

const long BAUD_RATE = 115200;

const unsigned long DEBOUNCE_MILLIS = 20UL;
const byte READY_PIN = 2;
const byte PULSE_PIN = 3;

struct mySwitch {
 int state;
 boolean debounceFlag;
 unsigned long debounceTimer;
};

mySwitch Ready;
mySwitch Pulse;

void setup()
{
 Serial.begin(BAUD_RATE);
 pinMode(READY_PIN, INPUT_PULLUP);
 Ready.state = digitalRead(READY_PIN);
 Ready.debounceFlag = false;
 Ready.debounceTimer = 0;
 pinMode(PULSE_PIN, INPUT_PULLUP);
 Pulse.state = digitalRead(PULSE_PIN);
 Pulse.debounceFlag = false;
 Pulse.debounceTimer = 0;
}

void loop()
{
 static boolean dialing = false;
 static byte pulseCount = 0;

unsigned long currentMillis = millis();

int readySignal = digitalRead(READY_PIN);
 if (Ready.state != readySignal)
 {
   Ready.state = readySignal;
   Ready.debounceFlag = true;
   Ready.debounceTimer = currentMillis;
 }

int pulseSignal = digitalRead(PULSE_PIN);
 if (Pulse.state != pulseSignal)
 {
   Pulse.state = pulseSignal;
   Pulse.debounceFlag = true;
   Pulse.debounceTimer = currentMillis;
 }

if (Ready.debounceFlag && currentMillis - Ready.debounceTimer >= DEBOUNCE_MILLIS)
 {
   Ready.debounceFlag = false;
   if (Ready.state == LOW && !dialing)
   {
     dialing = true;
   }
   else if (Ready.state == HIGH && dialing)
   {
     dialing = false;
     if (pulseCount)
     {
       Serial.println(pulseCount % 10);
       pulseCount = 0;
     }
   }
 }

if (Pulse.debounceFlag && currentMillis - Pulse.debounceTimer >= DEBOUNCE_MILLIS)
 {
   Pulse.debounceFlag = false;
   if (Ready.state == LOW && Pulse.state == HIGH)
   {
     pulseCount++;
   }
 }
}

@Jimmy60, Thank you so much for your time and help. I wish I could tell you if it worked for me, but I had just begun to understand the original coding and schematics. I think I understand your code as having the two readyPin wires connected to pin 2 and the pulsePin wires connected to pin 3. My assumption is (because of the use of the internal resistors, 5V goes into the “first” readyPin wire, and then the “other” readyPin wire is connected to pin 2 (I apologize for the lack of terminology). Then, in a series setup, the power continues into the first pulsePin, and the “other” pulsePin is connected to pin 3. And, finally they are both connected to the GND pin. Is my assumption even remotely correct. ~I swear I am not this ignorant in other areas.~

As for the answers to your questions, in regards to how the combination will be entered, my initial plan (hope) was the phone would be lifted on the receiver, then the “password” would be entered, and the (if correct) the lock would disengage. And, in order to reset, the phone would be placed back on the receiver and then lifted again. The user would know it’s ready for input at the moment the phone is lifted off the receiver.

Now, the more I do research, the more I realize this may be unattainable, as that would add another input to code for (the HIGH/LOW of the receiver). If that can’t be done, I’m not sure what back-up plan would be feasible. I thought about checking every “x” digits, and then after a rest period of “y seconds” it automatically resets. Thoughts?

Again, thank you so much for your help.

// line and reset the count. We mod the count by 10 because ‘0’ will send 10 pulses.

So if you dial a 0, you want to see the LED flash zero times?

     if (count == 4)  //<<<<<<<<<<<<<<<<<<<<<<<<<<<<
      {
        digitalWrite (13, HIGH); //<<<<<<<<<<<<<<<<<<<<
      }

Well:

if (needToPrint)  
  {
  for (int i = 0; i < count; i++)
    {
    digitalWrite (13, HIGH);
    delay (100);
    digitalWrite (13, LOW);
    delay (100);
    }
  }

Why are you testing count to be equal to 4, BTW? Isn’t “count” the number you just dialed?

It looks like you need two counts. One is the digit you are dialing (ie. 1 to 10) and the other is the number of times you dialed (ie. 1 to 4).

I thought about checking every “x” digits, and then after a rest period of “y seconds” it automatically resets.

Well, yes. After all, there is a short pause between digit pulses and a much longer pause between each dialling action (as you move your finger around the dial). You could detect that someone put the receiver down, since no power would be flowing.

As I understand it, dialing interrupts the flow of current through the phone briefly. So, an interruption of longer than a few milliseconds means the phone has been hung up.

akooti:
Now, the more I do research, the more I realize this may be unattainable, as that would add another input to code for (the HIGH/LOW of the receiver). If that can't be done, I'm not sure what back-up plan would be feasible. I thought about checking every "x" digits, and then after a rest period of "y seconds" it automatically resets. Thoughts?

It's attainable. You have to realize that it used to be done with simple relays. :slight_smile: Then how could it be impossible for a computer? :slight_smile:

Draw yourself a timing diagram, and the light might begin to dawn. It is best understood and maybe best implemented as a state machine. I see three states - on hook, off hook, and reading a pulse.

I assume the "count" refers to the number of pulses (4 pulses equals "4"). The problem, as you mentioned, seems to be having the system know when to stop counting for one number in the sequence and when to start count for the next number, as so on.

I saw in another board the idea of having the passcode in an array of bytes. Then, read the input for the first digit from the dial. If it matches the first digit in the array, then it continues to read the input of the second digit from the next dial. Again, if it matches the second digit in the array, then it continues on to the next dial, and so on and so forth. But, at any point the current number dialed does not match to the respective one in the array, the it resets itself. Is this something that you all feel could be a viable option? It seems like it would take away the need to have to worry about setting a reset based on a specific time.