Using an old rotary phone to do stuff

Hi

I have been playing around with this sketch from instructables.

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);
}

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);
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.
count++;
needToPrint = 1; // we'll need to print this number (once the dial has finished rotating)
}
}
}
lastState = reading;
}

I hooked up my parents old rotary phone and I'm sending numbers to my computer. It works. Great.
Then I tried to figure out how I could make it do other stuff. But my lack of coding knowledge is sitting in the corner, laughing at me.
I've been testing with an led connected to pin 13. Trying to light it when 9 is dialled.
Is it even possible with a change or addition to the above code and how would I go about it?

The number of pulses sent by the dial end up in the count variable and it is printed by this part of the code

// 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);

So you could say

if ((count % 10) == 9)
{
  //code here to turn on the LED
}
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 == 9)  //<<<<<<<<<<<<<<<<<<<<<<<<<<<<
      {
       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;
}

Thanks a lot. Works like a charm.
I was moving in the right direction, I was just using the entire serial.print line in the if. I should read more.

Hey guys!

So if I was using this code and wanted the Arduino to send 5v to pin3 when a certain number is dialed on on the phone (1234567 for instance), would it look something like this?

if ((count % 10) == 1234567)
{
digitalWrite(3, HIGH)
}

or is it more complicated to have it sense multiple digit dials?

I would replace

Serial.print(count % 10, DEC);

with

digitDialled(count % 10);

And then commence to program up the digitDialled() function

void digitDialled(byte digit) {
}

Now, say you want to check if 1234567 is dialled. Heaps of ways to do it, of course, but one way is to keep an array that holds all of the digits that have been dialled.

const int DIGIT_BUFFER_SZ = 10; // keep track go the last 10 digits
byte digitBuffer[DIGIT_BUFFER_SZ];

And keep another of your target sting of digits that you are searching for:

const byte digitSearch[] = {1,2,3,4,5,6,7};
const int DIGIT_SEARCH_SZ = sizeof(digitSearch)/sizeof(*digitSearch);

These variables are defined outside your different functions.

Notice that the size of the search string is not the same as the size of the buffer. I have done this on purpose to demonstrate that they don't need to be the same. But the buffer does need to be at least as big as the search sequence. Notice also I ask the compiler to calculate DIGIT_SEARCH_SZ for me.

So. Each time digit dialled gets invoked, it first needs to put the digit into digitBuffer, then it needs to compare the contents of the digit buffer with the target string.

To put the digits in the buffer, best way to do this is to treat it as a ring buffer. This means you put the digits into consecutive positions in the array as they come in, and when you run up to the end of the array then just start from zero again.

So, you need to keep track of where you currently are in the buffer:

int nextDigitLocation = 0;

And when you get a digit, the first thing you need to do is put it in the buffer:

  digitBuffer[nextDigitLocation] = digit;
  nextDigitLocation ++;
  if(nextDigitLocation >= DIGIT_BUFFER_SZ) {
    nextDigitLocation = 0;
  }

Ok! now you need to compare the contents of the buffer with the target. There are a couple of things to note.

First, after the push into the buffer, 'nextDigitLocation" is the location just after the last digit that was dialled. But DIGIT_SEARCH_SZ is also the index of the location just after the last digit of the search sequence (because indexes start at zero). So they correspond neatly.

Second, we have this looping issue - the digits dialled may cross over the end of our buffer. To deal with this, we use the C modulus '%' operator.

boolean match = true;
for(int i = 0; i< DIGIT_SEARCH_SZ;i++) {
  if(digitSearch[i] != digitBuffer[(
        nextDigitLocation 
        - DIGIT_SEARCH_SZ 
        + i 
        + DIGIT_BUFFER_SZ
      )% DIGIT_BUFFER_SZ]) {
    match = false;
    break;
  }
}

if(match) {
  // flash the LED, or whatever.
}

Now you may be asking - why do I add DIGIT_BUFFER_SZ to the sum? It's to force the value to be positive. Modulus on negative numbers can return a negative result, which won't do at all.

Anyway. These are the bits you need. Put 'em, together and you should have your phone thing.

Oh - couple of things. First, you should really clear out the buffer in the setup() function, or cow could potentially (unlikely, but possible) get a spurious match.

Second - how will this behave if your target match is "123123", or "0000"? Think about it :slight_smile: