Goertzel for reliable DTMF Decoding

I've been looking all over the web for a simple Arduino library for the implementation of either Goertzel or FFT. My goal is to use something like this to decode DTMF tones reliably. The closest thing I could find was this GitHub - jacobrosenthal/Goertzel: Arduino Library implementation of the Goertzel algorithm. I attempted to use the library example, but the results were less than perfect. I put an LED between digital 13 and the ground right above it. Then I plugged the headphone out from my iphone into A0 and ground. Volume was set at full. The default target frequency is 440.

First test:
No matter the tone or shape generated the LED did not light.

Second test:
I changed the "magnitude" to 500 and the led triggered from around 350 to 670 which is hugely unacceptable

Third test:
I spent some time changing the magnitude and target frequencies with no avail. Either the LED never triggered or it would trigger in +/-200 range of the target frequency.

As a side note, I am not very mathematically inclined. Otherwise i would've just taken apart the algorithm myself and made an Arduino library.

Anyway, if anyone has any good Arduino software solutions for decoding DTMF or any suggestions on how to make this or another library work, it would be greatly appreciated. I do not want a hardware solution. I already have a perfectly working hardware solution and I am now looking to eliminate it all together. Thanks for your help!

you may try
http://arduino.cc/forum/index.php/topic,119273.0/topicseen.html

Then I plugged the headphone out from my iphone into A0 and ground.

You shouldn't, A0 probably already damaged.
http://coolarduino.wordpress.com/2012/06/22/audio-input-to-arduino/

I did, indeed, forget that you cannot simply wire up ground to analog an expect results, but I fail to see how I may have cause damage. Analog is a 0 to 5 volt input. In a rather oversimplified way, if it accepts a +5v input then connecting it to a ground or audio out shouldn't harm it. Analog is an input. So, its not going to short out with ground and a headphone jack is only going to put out a maximum ~3volts and a couple milliamps. So, unless I'm missing something (which is totally possible), no damage should occur.

Also after wiring it up correctly, with a different arduino(in case I damaged it :P), the results were exactly the same. Nothing happens unless you lower the magnitude and then the LED fires in +/-100hz ranges that are no where near the target range.

Also, I'm confused on how to use the FFT Library you linked me to. It only provides 2 examples and one requires a codec shield. The other one only outputs data in binary form. How would I go about getting a frequency as a number or getting it to recognize certain frequencies?

Again, thanks for you help.

I'm confused on how to use the FFT Library you linked me to.

For single tone detection stick with Goertzel's algorithm. It's much faster.

I put an LED between digital 13 and the ground right above it.

Not a good idea. You must have a resistor (220 ohms or higher) in series with the LED otherwise you'll probably damage pin 13 and even the whole Arduino.
But which Arduino are you using? Arduino boards have an LED on Pin 13 anyway.

Then I plugged the headphone out from my iphone into A0 and ground.

As has been pointed out, this is also not a good idea. You should connect the headphone output through a capactor (about 0.1uF) to the centre of a 1:1 voltage divider. Then the analog input pin is also connected to the centre of the divider. The headphone output should also have a resistor to ground.

Volume was set at full

You might have been overloading the input and all it was seeing was 1023, 1023, 1023, .....

Either the LED never triggered or it would trigger in +/-200 range of the target frequency.

Post your code. There's no way to know what's wrong without seeing it - all of it.

Pete

On a Duemilanove or UNO pin 13 has a resistor and led on the board, but I added a brighter LED because the one on the board is rather dim and hard to see. The pin directly above it is ground. http://www.arduino.cc/en/Tutorial/BlinkingLED

As stated in my last post I already fixed the audio connection to follow this suggested schematic http://coolarduino.wordpress.com/2012/06/22/audio-input-to-arduino/ You'd be hard pressed to damage an analog input with audio out.

Even with the volume at max an iphone/ipod wont put out more than ~3volts which would max out the reading somewhere around 613.8.

The code and the library were posted in the first post via a link. I figured that would be more appropriate because you need the library to test the code anyway.
Here is the code anyway:

/*
  Blinks a light on a 16mhz Arduino when it detects an A4 (440 hz), tone
 the tuning fork pitch and something easily generated by the Tone library
  or a google search.

  The Goertzel algorithm is long standing so see 
  http://en.wikipedia.org/wiki/Goertzel_algorithm for a full description.
  It is often used in DTMF tone detection as an alternative to the Fast 
  Fourier Transform because it is quick with low overheard because it
  is only searching for a single frequency rather than showing the 
  occurrence of all frequencies.
  
  This work is entirely based on the Kevin Banks code found at
  http://www.eetimes.com/design/embedded/4024443/The-Goertzel-Algorithm 
  so full credit to him for his generic implementation and breakdown. I've
  simply massaged it into an Arduino library. I recommend reading his article
  for a full description of whats going on behind the scenes.

  Created by Jacob Rosenthal, June 20, 2012.
  Released into the public domain.
*/
#include <Goertzel.h>

int sensorPin = A0;
int led = 13;

float target_freq=440.0; //must be an integer of 9000/N and be less than
                         //sampling_frequency/2 (thanks to Nyquist)
float n=20.0;
float sampling_freq=9000.0;

Goertzel goertzel = Goertzel(target_freq,n,sampling_freq);

void setup(){
  pinMode(led, OUTPUT);     
  Serial.begin(9600); 
}

void loop()
{
  goertzel.sample(sensorPin); //Will take n samples
  
  float magnitude = goertzel.detect();  //check them for target_freq
  
  if(magnitude>1000) //if you're getting false hits or no hits adjust this
    digitalWrite(led, HIGH); //if found, enable led
  else
    digitalWrite(led, LOW); //if not found, or lost, disable led
    
  Serial.println(magnitude);
}

I don't see how that library can possible work. The initialization of the sampling rate, sample size and target tone looks fine. But Goertzel.sample just reads N samples as fast as it can without any attempt to do the sampling at the rate you've specified. It would need to have the correct delay between each sample for this to work.

Pete

Any suggestions on how to modify it to work? This is obviously not my code and the math is beyond me. So, if you have any suggestions or alternatives, let me know.

Thank you

On a Duemilanove or UNO pin 13 has a resistor and led on the board, but I added a brighter LED because the one on the board is rather dim and hard to see. The pin directly above it is ground. http://www.arduino.cc/en/Tutorial/BlinkingLED

That is bad and outdated information on that linked page. The present arduino boards do not have a resistor wired in series between the chip pin and the shield pin 13, and so if you simply wire a led like in that picture there is no current control that limits current to less then maximum safe value for the chip's output pin.

If you wire an led directly between the shield pin 13 and the shield ground pin, the led will have no series resistor to limit it's current flow and puts the 328p chip and led at great risk. The external led requires, as in you must add, a series resistor to prevent damage.

Lefty

I tried your code with that library. After fixing a few problems with the library, with code to recognize a 440 Hz tone, it was responding much stronger to tones up around 550z and I couldn't figure out why so I gave up.

Pete

Yup that's my problem too. Well, if anyone else has any ideas or even another library, let me know. Thanks for giving it a try pete !

Alright so I made a few changes and made a little progress. It provides predictable results and is accurate to some degree. I found a couple errors in his code and then started making equation adjustments along the way. Since I'm rather poor at math I used this document http://www.ti.com/lit/an/spra066/spra066.pdf to help a tad and just tweaked things to as close of a reading as possible.

Here is the .ino

/*
  Blinks a light on a 16mhz Arduino when it detects an A4 (440 hz), tone
 the tuning fork pitch and something easily generated by the Tone library
  or a google search.

  The Goertzel algorithm is long standing so see 
  http://en.wikipedia.org/wiki/Goertzel_algorithm for a full description.
  It is often used in DTMF tone detection as an alternative to the Fast 
  Fourier Transform because it is quick with low overheard because it
  is only searching for a single frequency rather than showing the 
  occurrence of all frequencies.
  
  This work is entirely based on the Kevin Banks code found at
  http://www.eetimes.com/design/embedded/4024443/The-Goertzel-Algorithm 
  so full credit to him for his generic implementation and breakdown. I've
  simply massaged it into an Arduino library. I recommend reading his article
  for a full description of whats going on behind the scenes.

  Created by Jacob Rosenthal, June 20, 2012.
  Released into the public domain.
*/


/*
Crudely Revised By: SexualMoose
Frequencies generated by iPhone/iPod at full volume
Frequencies are found in a 80Hz range proportional to the target.
When looking for 697 Frequency recognition starts ~70Hz below target and stop about ~10Hz above
this range shifts down proportional to the increase in the target frequency
i.e. when looking for 1633 its starts ~1480Hz and ends ~1560Hz 
*/
#include <Goertzel.h>

int sensorPin = 0;
int led = 13;

float target_freq=1633.0; //must be an integer of 9000/N and be less than
                         //sampling_frequency/2 (thanks to Nyquist)
float n=115.0;
float sampling_freq=9000;

Goertzel goertzel = Goertzel(target_freq,n,sampling_freq);

void setup(){
  pinMode(led, OUTPUT);     
  Serial.begin(9600); 
}

void loop()
{
  delay(10);
  sensorPin = analogRead(0);
  Serial.println(sensorPin);
  goertzel.sample(sensorPin); //Will take n samples
  
  float magnitude = goertzel.detect();  //check them for target_freq
  
  if(magnitude>1000) //if you're getting false hits or no hits adjust this
    digitalWrite(led, HIGH); //if found, enable led
  else
    digitalWrite(led, LOW); //if not found, or lost, disable led
    
  Serial.println(magnitude);
}

Here is the Goertzel.cpp

/*
  The Goertzel algorithm is long standing so see
  http://en.wikipedia.org/wiki/Goertzel_algorithm for a full description.
  It is often used in DTMF tone detection as an alternative to the Fast
  Fourier Transform because it is quick with low overheard because it
  is only searching for a single frequency rather than showing the
  occurrence of all frequencies.

  This work is entirely based on the Kevin Banks code found at
  http://www.eetimes.com/design/embedded/4024443/The-Goertzel-Algorithm
  so full credit to him for his generic implementation and breakdown. I've
  simply massaged it into an Arduino library. I recommend reading his article
  for a full description of whats going on behind the scenes.

  Created by Jacob Rosenthal, June 20, 2012.
  Released into the public domain.
*/
// include core Wiring API
#include "Arduino.h"

// include this library's description file
#include "Goertzel.h"

float SAMPLING_RATE;
float TARGET;
float N;
float coeff;
float Q1;
float Q2;
float sine;
float cosine;

byte testData[160];

Goertzel::Goertzel(float TARGET_FREQUENCY, float BLOCK)
{
	#if F_CPU == 16000000L
		Goertzel(TARGET_FREQUENCY, BLOCK, 9000.0);
	#else
		Goertzel(TARGET_FREQUENCY, BLOCK, 9000.0);
	#endif
}

Goertzel::Goertzel(float TARGET_FREQUENCY,float BLOCK,float SAMPLING_FREQ)
{

  SAMPLING_RATE=SAMPLING_FREQ;	//on 16mhz, ~8928.57142857143, on 8mhz ~44444
  TARGET=TARGET_FREQUENCY; //must be integer of SAMPLING_RATE/N
  N=BLOCK;	//Block size
  int	k;
  float	omega;

  k = (int) (N * (TARGET_FREQUENCY / SAMPLING_RATE) + 0.93);
  omega = (2.0 * PI * k) / N;
  sine = sin(omega);
  cosine = cos(omega);
  coeff = 2.0 * cosine;

  ResetGoertzel();
}


/* Call this routine before every "block" (size=N) of samples. */
void Goertzel::ResetGoertzel(void)
{
  Q2 = 0;
  Q1 = 0;
}


/* Call this routine for every sample. */
void Goertzel::ProcessSample(byte sample)
{
  float Q0;
  Q0 = coeff * Q1 - Q2 + (float) sample;
  Q2 = Q1;
  Q1 = Q0;
}


/* Basic Goertzel */
/* Call this routine after every block to get the complex result. */
void Goertzel::GetRealImag(float *realPart, float *imagPart)
{
  *realPart = (Q1 - Q2 * cosine);
  *imagPart = (Q2 * sine);
}


/* Sample some test data. */
void Goertzel::sample(int sensorPin)
{
  for (int index = 0; index < N; index++)
  {
    testData[index] = (byte) analogRead(0);
  }
}


float Goertzel::detect()
{

  int	index;

  float	magnitudeSquared;
  float	magnitude;
  float	real;
  float	imag;

  /* Process the samples. */
  for (index = 0; index < N; index++)
  {
    ProcessSample(testData[index]);
  }

  /* Do the "standard Goertzel" processing. */
  GetRealImag(&real, &imag);

  magnitudeSquared = real*real + imag*imag;
  magnitude = (sqrt(magnitudeSquared)/2);

  ResetGoertzel();
  return magnitude;
}

And here is a link to test out the modified library:
https://www.dropbox.com/sh/9lmbik1v6k1tgt3/Wl2xlBTypU

Here's what I'd like to do:
I need help turning the equations in this document into arduino code http://www.ti.com/lit/an/spra066/spra066.pdf. If anyone could help me out with that I'd super appreciate it. It doesn't have to be much, just a frame work so I know where to put the variables. Thanks for your help!

TMS320 is a special DSP processor, you can hardly apply that equations to atmega, though..

While yes, the formula was modified for that chip, the formula should still be sound if you change the sample rate to 9000 and "N" to 115

Literally all I need is the formula(s) in order and the inputs labeled so I know where to put them. I am able to modify parts but understanding the whole thing is beyond me. I do know that the basic modified equations still apply if you change N to 115 and the sample rate to 9000

BTW - where in your code you do sampling of the signal at the required sampling frequency?

I'm not 100% sure what you are asking, I believe the sampling code is in the goertzel.cpp and it happens at the analogRead(0);

I think I have figured out what's happening in that library. As I mentioned, the sampling loop just goes as fast as possible. Your code sets the sampling rate to 9000Hz but I've measured that loop on my Duemilanove with a frequency counter and the loop is more like 8300Hz.
Change your sampling frequency to 8300 and give it a try. On my Arduino it shows a definite peak at 440Hz now.

Pete

Are you using the modified code or the original?

Can you post your code for recording the frequency?

Also I don't believe you can just change the sampling rate without changing "N".

A Mitel MT8870 will give you 4 bit binary outputs and a data ready signal. The device contains the filters for the high and kw frequencies and decode @ < 100mV. I included thee data sheet and I've seen them on Ebay for $3 - $4.00. Much easier than devoting such a large piece of the code space for decoding... another note is that there are DTMF generators that use the same crystal... If you choose another time base only you could easily decode them. This was a common method for semi secure radio based remote control. I used it when building my first amateur radio repeater for shut down and phone patch control.

Doc

MT8870.pdf (220 KB)