DTMF detection in software

Hello!
I bought an Arduino (Uno Rev3) yesterday and decided to try it out with some nice DTMF detection (alright, I admit that I started off with blinking a stupid LED), and being as omnipotent as I am (I, as in I am one with Google) I knew straight away that the Goertzel algorithm was the way to go. After looking it up on Wikipedia, and seeing the lovely pseudo-code implementation, I started coding away. The result:

byte detect() {
  int i, j;

  unsigned int x[800];

  const float coeff[9] = {
    1.764203748,  // 697 Hz
    1.713499850,  // 770 Hz
    1.651160423,  // 852 Hz
    1.577286044,  // 941 Hz
    1.318773510,  // 1209 Hz
    1.179308597,  // 1336 Hz
    1.013493069,  // 1477 Hz
    .8184907739,  // 1633 Hz
    .6656390890   // 1750 Hz
  };

  float s[9] = {0};
  float s_prev1[9] = {0};
  float s_prev2[9] = {0};
  float power[9];
  boolean detected[9] = {false};
  
  for (i = 0; i < 800; i++) {
    x[i] = analogRead(audio_in);
  }

  for (i = 0; i < 800; i++) {
    for (j = 0; j < 9; j++) {
      s[j] = x[i] + coeff[j] * s_prev1[j] - s_prev2[j];
      s_prev2[j] = s_prev1[j];
      s_prev1[j] = s[j];
    }
  }
  
  for (i = 0; i < 9; i++) {
    power[i] = s_prev1[i] * s_prev1[i] + s_prev2[i] * s_prev2[i] - coeff[i] * s_prev1[i] * s_prev2[i];
    if (power[i] > threshold) {
      detected[i] = true;
    }
  }
  
  if (detected[8]) {
    return ('T');
  } else if (detected[0] && detected[4]) {
    return ('1');
  } else if (detected[0] && detected[5]) {
    return ('2');
  } else if (detected[0] && detected[6]) {
    return ('3');
  } else if (detected[0] && detected[7]) {
    return ('A');
  } else if (detected[1] && detected[4]) {
    return ('4');
  } else if (detected[1] && detected[5]) {
    return ('5');
  } else if (detected[1] && detected[6]) {
    return ('6');
  } else if (detected[1] && detected[7]) {
    return ('B');
  } else if (detected[2] && detected[4]) {
    return ('7');
  } else if (detected[2] && detected[5]) {
    return ('8');
  } else if (detected[2] && detected[6]) {
    return ('9');
  } else if (detected[2] && detected[7]) {
    return ('C');
  } else if (detected[3] && detected[4]) {
    return ('*');
  } else if (detected[3] && detected[5]) {
    return ('0');
  } else if (detected[3] && detected[6]) {
    return ('#');
  } else if (detected[3] && detected[7]) {
    return ('D');
  } else {
    return 0;
  }
}

As you probably see I have a constant named "audio_in" which simply is the analog pin that I sample from, and then I also have a constant named "threshold", which is what it says. The value of threshold is quite important and should be tried out in each application (its value depends on the input volume), 2e7 was a good value when using the headphone jack on my MacBook as input. And you probably notice that I also do detection for 1750 Hz (common for opening amateur radio repeaters here in Europe), which you easily can remove.

It takes about 100 ms to detect a tone, and with a correct threshold value it (judging from my quick testing) never gives any false alarms, it detects if each individual frequency is between +- 20 Hz from its supposed value, or something like that (I have only yet tried it with -10 Hz). As a quick warning, this is from the very early phase of my project (it is straight from the top of my head, almost entirely without revision), and some things it does is purely stupid. For example, the threshold value is entirely unnecessary, as one could just look at relative power levels. And then there is that nasty looking if-thingy at the end.

I hope someone finds this useful, and as this is a work-in-progress I welcome all input.

If you were "one with Google" you should have already seen these two threads and saved yourself some time and effort.
http://arduino.cc/forum/index.php/topic,121030.0.html

http://arduino.cc/forum/index.php/topic,121540.0.html

Pete

Indeed I've seen several implementations for various platforms, including Arduino, but it's always fun to do (and understand) something oneself. And it's not like it took very much time, or effort, to implement it. There is more to life than results. :slight_smile:

Fors:
Indeed I've seen several implementations for various platforms, including Arduino, but it's always fun to do (and understand) something oneself. And it's not like it took very much time, or effort, to implement it. There is more to life than results. :slight_smile:

Yes, the journey is part of the reward.