Tuesday, 3 September 2013

Interrupts - What, Where & How? - Working with the Simple Labs DTMF Control Board

What are Interrupts?

Interrupts are signals that interrupt the current task being performed by the microcontroller. An Interrupt might occur based on external(a sensor senses something) / internal parameters (timer,etc).    When an interrupt occurs the microcontroller pauses whatever it is doing and calls a dedicated function associated with that interrupt. This function is referred to as the ISR - Interrupt Service Routine. Once the ISR is executed the microcontroller  will go back to the task it was doing and continue the process from where it paused.

Where are they used?

Consider this, You are building a fire alarm system, now, you have 2 ways of doing this, either you check the fire sensor pin manually or you attach it to an interrupt pin and write the alarm code as part of the ISR. if you check it manually, there is a chance that you might miss the signal from the fire alarm when the actual fire happens (the chances of a fire would be very less and its kind of pointless to keep checking the fire sensor) and fail to trigger the alarm. 

Another usage example would be a vehicle speedometer. Most vehicles use some kind of a sensor to measure the number of rotations of a wheel. These sensors would send one / multiple pulses for every rotation of the wheel, based on the number of pulses and the wheel diameter, speed and distance are calculated. Now, all these pulses must be captured for the speed / distance to be calculated correctly. If you are checking this manually, what if a pulse occurs when the control is processing the display? In such cases, you can use interrupts to automatically process those pulses as and when they occur.

If you are still wondering why use interrupts at all? here's a neat explanation on why to use interrupts

" Imagine you’re sitting on your couch, enjoying a frosty brew and watching a movie after a long day.  Life is good.  There’s only one problem: you’re waiting for an incredibly important package to arrive, and you need it as soon as possible.  If you were a normal AVR program or Arduino sketch, you’d have to repeatedly stop your movie, get up, and go check the mailbox every 5 minutes to make sure you knew when the package was there.

Instead, imagine if the package was sent Fedex or UPS with delivery confirmation.  Now, the delivery man will go to your front door and ring the doorbell as soon as he arrives. That’s your interrupt trigger.  Once you get the trigger, you can pause your movie and go deal with the package.  That’s your interrupt service routine.  As soon as you’re done, you can pick up the film where you left off, with no extra time wasted. That’s the power of interrupts." - extracted from this interesting writeup
How to use them?

We shall focus on using the external hardware interrupts as other types of interrupts (timers, etc) would be better left alone when working with Arduino as some of the Arduino functions are based on these. Arduino boards have several interrupts based on the microcontroller used. The Atmega328 has 2 hardware interrupts INT0(on digital Pin 2) & INT1(on digital Pin 3).

The following Arduino Functions help us work with these interrupts
  • attachInterrupt(Interrupt, ISR, Interrupt Type) => The attachInterrupt() function is used to attach an interrupt(INTO or INT1) to a specific function(Interrupt Service Routine) that has to be called when a specific type of interrupt(LOW / CHANGE / RISING / FALLING) occurs.
  • detachInterrupt(Interrupt) => The detachInterrupt() function detaches or disables an Interrupt.

What is DTMF?

Dual-tone multi-frequency signaling (DTMF) is used for communication over telephone lines in the voice-frequency band between telephone handsets and other communications devices. A mixture of two different frequencies - 1 High Frequency & 1 Low Frequency - is generated for each button pressed on a telephone keypad. Each button press generates a unique combination of these frequencies. When you press a button on the keypad, the communication device / decoder device at the other end can identify the key pressed based on the frequencies detected.

Here's a table for easier understanding. Based on the table, when you press '1' on the keypad  a mixed signal of 1209 HZ Frequency and a 697 HZ Frequency is generated.


DTMF keypad frequencies
1209 Hz 1336 Hz 1477 Hz 1633 Hz
697 Hz 1 2 3 A
770 Hz 4 5 6 B
852 Hz 7 8 9 C
941 Hz * 0 # D

Working with the Simple Labs DTMF Decoder Board / DTMF Shield

DTMF Decoder Board

The Simple Labs DTMF Decoder board is an MT8870/C9370 based complete DTMF decoder circuit. The board has two strips of pin-out connections - a 2-pin connection for audio input and a 7-pin connection for micro-controller interface. It ships with a 3.5 mm audio jack which can be plugged into any headphone line, which on the other end could be plugged onto the DTMF decoder board.  On the other end, the 7-pin connector has the following pinouts.

  • StD -> Steered Delay [Connect this to the Digital Pin 2(INT 0) of your Induino R3 Board]
  • Q1-Q4 -> 4-bit binary data [Connect  Q1-> Digital Pin 3, Q2-> Digital Pin 4, Q3-> Digital Pin 5 and Q4-> Digital Pin 6 ]
  • +5 -> 5V supply [ Connect this to the +5V Pin on the Induino R3]
  • Gnd -> Ground [ Connect this to the GND Pin on the Induino R3]


DTMF Shield

The Simple Labs DTMF Shield is also based on the MT8870/C9370 IC and has the complete Decoder Circuitry. The shield has a 2 pin connector for connecting audio input. The outputs from the decoder are directly connected to the IO's (no wiring required) as follows
StD -> Digital Pin 2
Q1 -> Digital Pin 3
Q2 -> Digital Pin 4
Q3 -> Digital Pin 5
Q4 -> Digital Pin 6

Beyond the above the DTMF Decoder Board / Shield are functionally the same.  The audio jack can be plugged into the headphone jack of your mobile(or any device that can output DTMF) that can give a DTMF output. When a key is pressed (i.e. when a DTMF tone is given as input), the StD pin gives a short pulse indicating that the output has been updated. The corresponding number is given out as a 4-bit number through 4 parallel lines, Q1-Q4.

The output of the MT8870/C9370 is latched, meaning which the 4-bit output stays until the next input. That is, if you press the number 9, a short pulse would be presented on the StD pin and Q1- Q4 would have the states 1,0,0,1. The states of Q1-Q4 would not change until another key is pressed. The StD pulse is pretty short and so its better connected to an Interrupt Pin.


One thing to remember would be while the phones have a keypad not all of them are configured to give a DTMF output from the keypad to the audio jack. In case your phone doesn't support DTMF output on the Audio Jack, You can download this flash file to generate DTMF tones from your Laptop / Computer. Just plug the audio input connector into the headphone jack of your Laptop / Computer -> DTMF FLASH FILE

Lets do a simple program to see the output from our DTMF board / shield onto the Serial Monitor.

/*  Induino R3 User Guide - Program 14.0 - Interfacing the DTMF decoder Board / Shield */  

int val=0;// A variable to store the value calculated based on the binary inputs q1 to q4

void setup()
{
  pinMode(2,INPUT); // Steered delay Pin (INT 0)
  pinMode(3,INPUT); // q1
  pinMode(4,INPUT); // q2
  pinMode(5,INPUT); // q3
  pinMode(6,INPUT); // q4
  attachInterrupt(0,dtmf,RISING); // Enable the Interrupt Pin, associate it with the ISR dtmf and configure it to trigger on a RISING interrupt
  Serial.begin(9600); // Enable Serial Communication
}

void loop()
{
  if(val!=0) // Check if we have received a new value, when a new value is received we print the value and reset it to 0
  {
   Serial.println(val); // print the new value to the serial monitor
   val = 0; // reset the value to 0
  }
}

void dtmf() // the ISR - this function is called everytime a RISING interrupt occurs on PIN 2 (INT 0)
{
  for(int i=0;i<4;i++) // Once we receive an Interrupt, we need to read each of q1 to q4, with q1 being the LSB and q4 the MSB
  {
    if(digitalRead(i+3)==1)// check if the pin is 1 or 0, if 1 then we need to process for binary to decimal conversion
    {
      val = val + (1<<i); // Binary to decimal conversion
    }
  } 
}


Now Lets try to build a Secure Device Control System - Controlling a Device using a DTMF Password.


Connect a lamp to your relay board and connect the controlling wire for that to the 13th pin to have more fun. The code will work without the relay board as well.



Here's the code
/*  Induino R3 User Guide - Program 14.1 - DTMF Based Secure Control
The User presses a 4 digit password. If the Password matches to a set password (set in the program) then the user is logged in.
An LED on the 11th pin indicates the Log in state. Once Logged in the User can switch On / Off a device connected to Pin 13 pressing '1' for On 
and '0' for Off. When Logged in Pressing '#' logs the user out. The state of the 13th pin will not be affected when the user logs out

The 4 digit password has to be entered within 5 seconds of entering the the first digit if not, the count is reset to the first
digit and the user has to start again.

The user can reset the count mid-way through entering a password by pressing '#' key

Reset is indicated by the led on the 12th pin.
*/

int val=0;// A variable to store the value calculated based on the binary inputs q1 to q4

const int set_pass[]={1,11,10,2}; // The DTMF Value for 4 Digits of Password [Remember in DTMF Values 0 => 10, * =>11 & #=> 12]

int recvd_pass[4]; // a variable to store the password received

boolean login_flag = 0; // a flag variable to store the log in status of the user

int count = 0; // a variable to count the number of digits entered

long int first_press_time = 0; // a variable to store the time from the first button press

void setup()
{
  pinMode(2,INPUT); // Steered delay Pin (INT 0)
  pinMode(3,INPUT); // q1
  pinMode(4,INPUT); // q2
  pinMode(5,INPUT); // q3
  pinMode(6,INPUT); // q4
  attachInterrupt(0,dtmf,RISING); // Enable the Interrupt Pin, associate it with the ISR dtmf and configure it to trigger on a RISING interrupt
  Serial.begin(9600); // Enable Serial Communication for debugging

  pinMode(11,OUTPUT); // LED for login indication
  pinMode(12,OUTPUT); // LED for Reset indication
  pinMode(13,OUTPUT); // LED for controlling
}

void loop()
{
  // If there is a new value received print it to serial monitor for debugging
  if(val>0)
  {
    Serial.println(val);
  }
  
  // Check for Reset Conditions - Either time from first press is more than 5 seconds or the user has pressed the '#' key in the logged out mode
  
  if((count>0 && (millis()-first_press_time)>5000)||(login_flag==0 && val==12))
  {
    count = 0; // reset count
    Serial.println("RESET"); // print reset message
    val = 0; // reset val to '0' as so that we can distinguish when a new value is received.
    digitalWrite(12,HIGH); // led indication for reset
    delay(1000);
    digitalWrite(12,LOW);
  }
  
  //Get four digit input to check for login, ensure that the user is not already logged in and that 4 digits have not been input already.
  if(login_flag==0 && count< 4 && val>0)
  {
    recvd_pass[count] = val; // store the value into an array for comparing once all the 4 digits are received
    val = 0; // reset val to '0' as so that we can distinguish when a new value is received.
    if(count==0) // if this is the first key press then store the time for calculating time out
    {
      first_press_time = millis();
    }
    count++; // increment count 
    Serial.print("Count : ");Serial.println(count); // print count value for debug
    if(count==4) // if count is 4, then we need to match the password received to the passwors stored
    {
      if(recvd_pass[0]==set_pass[0] && recvd_pass[1]==set_pass[1] && recvd_pass[2]==set_pass[2] && recvd_pass[3]==set_pass[3]) // check if passwords match
      {
        login_flag = 1; // if matched set the login flag
        digitalWrite(11,HIGH); // and turn on the indication led
      }
      else
      {
        digitalWrite(12,HIGH); // if passwords dont match, show the reset indication
        delay(1000);
        digitalWrite(12,LOW);
      }
      count = 0;// on reaching 4, count has to be reset to 0
    }
  }



// logged in activities
  if(login_flag==1) // if the user is logged in then the keys pressed are treated differently
  {
    switch(val) // check which key is pressed
    {
      case 1: digitalWrite(13,HIGH); break; // turn on the 13th pin led when '1' is pressed
      case 10: digitalWrite(13,LOW); break;// turn off the 13th pin led when '0' is pressed
      case 12: digitalWrite(11,LOW); login_flag=0; break; // Log out the user, set the login flag to false and turn off the login status led
    }
    val = 0; // reset val to '0'
  }

  }

  void dtmf() // the ISR - this function is called everytime a RISING interrupt occurs on PIN 2 (INT 0)
  {
    
    for(int i=0;i<4;i++) // Once we receive an Interrupt, we need to read each of q1 to q4, with q1 being the LSB and q4 the MSB
    {
      if(digitalRead(i+3)==1)// check if the pin is 1 or 0, if 1 then we need to process for binary to decimal conversion
      {
        val = val + (1<<i); // Binary to decimal conversion
      }
    } 
  }

 #### IF YOU ARE USING THE DTMF SHIELD, YOU CAN USE EITHER THE DTMF OR THE 7-SEGMENT DISPLAYS AT ANY GIVE TIME AS THEY USE THE SAME PINS ####

Thats It For This Part! Enjoy... and feel free to drop us an email with questions you might have -> info@simplelabs.co.in

 Visit www.simplelabs.co.in for more interesting products


3 comments:

  1. Looks like a great project! I'm hoping to try this out with the Arduino Uno. Is it possible to get the project to work with that specific Arduino board (the Uno), and just the Simple Labs DTMF Decoder Board, (not the entire shield)?

    I'm trying to make a decoder that is as simple and as cheap as possible. THANKS!!!

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete
  3. Hello sir,
    This code is not working with my DTMF control board. I am not getting the output at serial monitor. Can You suggest anything about where I could be going wrong???

    ReplyDelete