A Simple DTMF Encoder (Tone Generator) Sketch for Arduino.

Last Modified: May 15, 2016, at 10:24 AM
By: dndubins
Platforms: Written using Arduino IDE 1.6.8, tested on an Arduino Uno R3 (not tested with other platforms)

The regular Tone Library does not allow for simultaneous tones. This library uses two digital pins both connected to the same speaker, to produce DTMF tones for touch-tone phone dialing. If placed near the microphone end of a handset, the speaker can dial a phone number when the momentary button is pushed.


Introduction

There are many hardware strategies to produce DTMF tones, for instance the HM91710A. However, it is possible to generate dual tones without using a DTMF encoding chip. Here is a software strategy to accomplish the same task, requiring a breadboard, a small 8 Ohm audio speaker, 2 x 240 Ohm resistors, a 4.7 uF capacitor, a momentary button, and 4 jumper wires. The only purpose of the button is to trigger dialing. This approach is very simple, and worked on two different phone systems I tested. You can tweak the "fudge factor" in the DTMF function if your frequency is off.

The functions available in the sketch include:

playDTMF(digit,duration); // emulates a single digit on a touch tone keypad (duration in msec)
dialNumber(number[],len); // plays a phone number, of length "len"

Usage

Save the following sketch as DTMF.ino, in an appropriately labeled directory:

  1. /* DTMF encoder (Dual Tone Generator) for a Phone Dialer
  2.  *  Created by David Dubins, May 13th, 2016.
  3.  *  Released into the public domain.
  4.  *
  5.  * SETUP:
  6.  * - Connect Pins 12 and 13 to the + speaker terminal, each through their own 240 Ohm resistor
  7.  * - Connect a 4.7 uF capacitor between the + and - terminals of the speaker
  8.  * - Connect speaker GND to Arduino GND
  9.  * - Connect a momentary switch to Pin 8, and the other side of the switch to GND
  10.  */
  11.  
  12. const byte tone1Pin=12; // pin for tone 1
  13. const byte tone2Pin=13; // pin for tone 2
  14. byte PhoneNumber[]={8,6,7,5,3,0,9}; // for special characters: 10=*, 11=#, 12=1sec delay
  15. byte PhoneNumberLength = 7;  // adjust to length of phone number
  16. const byte buttonPin=8; // for momentary switch
  17.  
  18. // frequencies adopted from: https://en.wikipedia.org/wiki/Dual-tone_multi-frequency_signaling
  19. int DTMF[13][2]={
  20.   {941,1336}, // frequencies for touch tone 0
  21.   {697,1209}, // frequencies for touch tone 1
  22.   {697,1336}, // frequencies for touch tone 2
  23.   {697,1477}, // frequencies for touch tone 3
  24.   {770,1209}, // frequencies for touch tone 4
  25.   {770,1336}, // frequencies for touch tone 5
  26.   {770,1477}, // frequencies for touch tone 6
  27.   {852,1209}, // frequencies for touch tone 7
  28.   {852,1336}, // frequencies for touch tone 8
  29.   {852,1477}, // frequencies for touch tone 9
  30.   {941,1209}, // frequencies for touch tone *
  31.   {941,1477}, // frequencies for touch tone #
  32.   {0,0} // pause
  33. };
  34.  
  35. void setup()
  36. {  
  37.   pinMode(tone1Pin,OUTPUT); // Output for Tone 1
  38.   pinMode(tone2Pin,OUTPUT); // Output for Tone 2
  39.   pinMode(buttonPin,INPUT_PULLUP); // Button
  40. }
  41.  
  42. void loop()
  43. {
  44.   if(digitalRead(buttonPin)==LOW){  // If the button is pushed
  45.     dialNumber(PhoneNumber,PhoneNumberLength);  // Dial the number
  46.   }
  47. }
  48.  
  49. void playDTMF(byte digit, byte duration){
  50.   boolean tone1state=false;
  51.   boolean tone2state=false;
  52.   int tone1delay=(500000/DTMF[digit][0])-10; // calculate delay (in microseconds) for tone 1 (half of the period of one cycle). 10 is a fudge factor to raise the frequency due to sluggish timing.
  53.   int tone2delay=(500000/DTMF[digit][1])-10; // calculate delay (in microseconds) for tone 2 (half of the period of one cycle). 10 is a fudge factor to raise the frequency due to sluggish timing.
  54.   unsigned long tone1timer=micros();
  55.   unsigned long tone2timer=micros();
  56.   unsigned long timer=millis(); // for timing duration of a single tone
  57.   if(digit==12){
  58.     delay(1000); // one second delay if digit is 12
  59.   } else {
  60.     while(millis()-timer<duration){
  61.       if(micros()-tone1timer>tone1delay){
  62.         tone1timer=micros(); // reset the timer
  63.         tone1state=!tone1state; // toggle tone1state
  64.         digitalWrite(tone1Pin, tone1state);
  65.       }
  66.       if(micros()-tone2timer>tone2delay){
  67.         tone2timer=micros(); // reset the timer
  68.         tone2state=!tone2state; // toggle tone2state
  69.         digitalWrite(tone2Pin, tone2state);
  70.       }
  71.     }
  72.     digitalWrite(tone1Pin,LOW);
  73.     digitalWrite(tone2Pin,LOW);
  74.   }
  75. }
  76.  
  77. void dialNumber(byte number[],byte len){
  78.   for(int i=0;i<len;i++){
  79.     playDTMF(number[i], 100);  // 100 msec duration of tone
  80.     delay(100); // 100 msec pause between tones
  81.   }
  82. }
  83. //END OF FILE

Notes

The 4.7 uF capacitor smooths the signal from a square wave to more of a sine curve. I found that tone recognition on the tested phone systems worked with or without it. The 240 ohm resistors prevent a short when the tone pins are at different states.

If your phone does NOT correctly detect the tones, try adjusting the fudge factor in the playDTMF function, to "tune" the frequencies to match your landline phone. A fudge factor of 10 microseconds worked for me.

The speaker should be very close to the handset mic. I tried substituting a piezo for the speaker, and this didn't work at all.

To do

Change PhoneNumber to char array, to allow for * and # to be used directly.

Share