Go Down

Topic: DTMF decoder library (Read 104937 times) previous topic - next topic

el_supremo

That code compiles with no errors or warnings with Arduino 1.6.13 on Win 7 Pro.

I don't understand how your code managed to produce an error in function main(). I've never had that happen.

The error "unable to find a register to spill" has occurred before with this code but it was in the DTMF sketch itself, not main(), and it was solved by splitting a complicated calculation into two statements.

Pete
Don't send me technical questions via Private Message.

SHTIRLITZ

Ok, it seems was my mistake...

First time I download sketch from post #1, than i have a errors during compilation.
Afterwards, i got a files from link in post #6, compilation was passed OK!

S1sm1x

With DTMF, the code is 2 of 8 consisting of one tone from the "row" frequencies and one tone from the "column" frequencies. The code uses the Goertzel algorithm to detect each of the 8 frequencies. The DTMF frequencies are arranged like this on a touchtone phone:
Code: [Select]

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

So hitting the number '8' will send tones 852 and 1366 together. When receiving these tones, the code looks for them in the order they are listed in the dtmf_tones array. The code then maps them into two hex digits, one for the rows and one for the columns.
Code: [Select]

int dtmf_tones[8] = {
// Row tones (low order hex digit)
 697,     //1
 770,     //2
 852,     //4
 941,     //8
// Column tones (high order hex digit)
1209,    //1
1336,    //2
1477,    //4
1633     //8
};

If tones 852 and 1366 are received together, they will be converted into hex 24. This is then looked up in the dtmf_map array and the index where it is found (7) is then used to map into the dtmf_char array which translates it to the character '8'.

In your case, there are only six tones which aren't in a convenient row/column arrangement but it shouldn't be too hard to modify the code to read it provided that the individual tones are long enough for the Goertzel algorithm to reliably detect them.

Pete
Hi, before the most, I would like to congratulate for the great job.

Before posting this comment, I read a lot aboud Goertzel algorithm and I think that I can understand its fundamentals. Anyway, I have a basic questions which hasn't been fully approached by now.

I'm interested on recognizing more than one frequency from a single channel (ADC / analogic Arduino pin) and, if possible, even tracking more than 1 analogic pin connected to microphones.
Is there any clear implementation for that you may have? Can I use something from the Pete's above quoted article ?

By that time, I'm using successfully this Goertzel Algorithm implementation: https://github.com/jacobrosenthal/Goertzel .
I use an Arduino Mega instead of Uno preventing any RAM problems :)

Hoping for answers.
Have a nice day !

s1sm1x



el_supremo

Quote
I'm interested on recognizing more than one frequency from a single channel
That is what the DTMF code does. But it only looks for two out of eight possible frequencies. When you want to detect more frequencies, there's a point where you might as well do an FFT instead of using Goertzel.
What are you trying to detect?

Quote
even tracking more than 1 analogic pin
That might be possible but I think you'll quickly run out of cpu time on a Mega.

Pete
Don't send me technical questions via Private Message.

S1sm1x

That is what the DTMF code does. But it only looks for two out of eight possible frequencies. When you want to detect more frequencies, there's a point where you might as well do an FFT instead of using Goertzel.
What are you trying to detect?
Yes, I know that's what dtmf code does, but isn't it restricted to that freqs? For instance, how can I change the code to detect 2 freqs that aren't necessarily a tone ( like 800 Hz and 950Hz or any other pair conveniently separated in the spectrum)?
Going for FFT on Arduino Mega seems feasible? I haven't even tried it because I felt Mega was too weak for that.

That might be possible but I think you'll quickly run out of cpu time on a Mega.
I wonder if it was possible to apply simultaneously the Goertzel Algorithm to more than 1 analog pin. After analysing the real time calculus, it seemed kind a fast as long as the number of samples is kept below 200, am I wrong?

Thank you for the time :D

s1sm1x

el_supremo

In DTMF.cpp there is an array of 8 integers called dtmf_tones. These are the eight DTMF tones that the library decodes. When the library is instantiated, it converts these tones into a coefficient that is used by the Goertzel algorithm. The code which does this is in the for loop which immediately follows this comment:
Code: [Select]
  // Calculate the coefficient for each DTMF tone

Quote
as long as the number of samples is kept below 200
You don't have a choice about the number of samples you get. You have to sample at a rate that is at least twice as high as the highest frequency you wish to detect. If you don't use all of those samples, you are just adding noise to your signal which will make the detection much more difficult.

You can do FFT on Mega but the size of the FFT (number of bins) will be restricted by how much processing time it takes. If the tones you wish to detect are widely spaced, you might be able to use, for example, an FFT with 64 bins or even only 32 bins. But if you want to detect tones that are, say, 10 or 20Hz apart it will require more bins which in turn requires a lot more cpu time and the Mega won't be able to handle it.

Pete
Don't send me technical questions via Private Message.

S1sm1x

In DTMF.cpp there is an array of 8 integers called dtmf_tones. These are the eight DTMF tones that the library decodes. When the library is instantiated, it converts these tones into a coefficient that is used by the Goertzel algorithm. The code which does this is in the for loop which immediately follows this comment:
Code: [Select]
  // Calculate the coefficient for each DTMF tone

Thank you! :D I tested and It's true!

You don't have a choice about the number of samples you get. You have to sample at a rate that is at least twice as high as the highest frequency you wish to detect. If you don't use all of those samples, you are just adding noise to your signal which will make the detection much more difficult.

You can do FFT on Mega but the size of the FFT (number of bins) will be restricted by how much processing time it takes. If the tones you wish to detect are widely spaced, you might be able to use, for example, an FFT with 64 bins or even only 32 bins. But if you want to detect tones that are, say, 10 or 20Hz apart it will require more bins which in turn requires a lot more cpu time and the Mega won't be able to handle it.


Pete
I know how to work with Nyquist Frequency, but to recognize some frequencies separated by 50 / 100 Hz (that I will specify on the d_mags array), how much bin size you think it is sufficient to plug more than 1 microphone (maybe 2,3,4 )?  I know that a larger bin size can slow the process , but don't have yours practical experience :)


Thank you for your time!

Pedro

el_supremo

Quote
Yes, I know that's what dtmf code does,
Quote
I know how to work with Nyquist Frequency,
OK. I don't know what you don't know.

Code: [Select]
how much bin size you think it is sufficient
If you know "what dtmf code does" and you know "how to work with Nyquist Frequency" then you should be able to answer this yourself.

And, BTW, I have never used multiple microphones. Good luck with that.

Pete
Don't send me technical questions via Private Message.

pjrc

Acquiring multiple signal streams at high enough (and low jitter) sample rates *and* performing real-time FFT or even Goertzel analysis on all them seems like quite a tall task for an 8 bit AVR like Arduino Mega.

This sort of thing is pretty achievable on more powerful 32 bit boards where DMA can efficiently acquire the samples.  But to do it on AVR would be quite a feat!

fatpat

Hello

I'd like to thanks "el_supremo" for his work on the DTMF decoder. I'm using it for activating a led strip through a MOSFET remotely using a HAM radio using DTMF code. It's very simple but I had to modify a bit the DTMF library to be able to detect signal without DTMF code. In my case I wanted to activate the led strip at signal detection (without DTMF code) and then I added the possibility to shutdown the strip or change its brightness. But I must activate the led strip when a signal is received (just push the PTT button of the emitter as it is used as a security element and I prefer having the led to be up even if not necessary insted of having a shutdown led when it must be turned on).

The goal was to:
- have a great range
- reusing everything possible that I have at home
- cheap and simple
- must be turned of quickly and efficiently
- after 5 minutes, it shutdowns


Anyway, I have uploaded the code github: https://github.com/fatpat/arduino-remote-switch-dtmf


Thanks you for your work !!!

Adrianotiger

I am trying to implement it on the ESP32 but I am not able to see any DTMF result.

I have 2 problems:
1 - even if it is powered with 3.3V, the signal is between 1.2V and 1.8V. Can I amplify the signal, just with an amplify variable in this line: Q0 = coeff * Q1 - Q2 + (float) (sample - adc_centre) * amplify; ??

2 - New processors have much more than 16MHz, how can I calculate the sampleFrequency? For example, I can execute the readAnalog(pin) up to 24.000 times each second. Can I write 240000 or is there a formula (I read somewhere sample rate / 2 = frequency)? Or can I still write 8900 as samplefrequency?

jremington

#71
Aug 10, 2019, 04:08 am Last Edit: Aug 10, 2019, 04:09 am by jremington
1. That won't help. External amplifiers will help.

2. Read the documentation on the readAnalog() function to determine the possible sampling rates and examine your code to determine the actual sampling rate.

Quote
Or can I still write 8900 as samplefrequency?
No. The sampling rate is absolutely critical and you must know what it is, or adjust it correctly, in order for the algorithm to work at all.

Adrianotiger

#72
Aug 22, 2019, 03:36 pm Last Edit: Aug 22, 2019, 03:37 pm by Adrianotiger
Thank you very much  el_supremo for this library.
It helped me alot with my project. I uploaded it to GitHub too:

https://github.com/Adrianotiger/phoneDTMF

My scope was to make it working on the ESP32 to detect the DTMF landline phone, so I made some changes:
- this library will detect the frequency automatically. On some microcontroller the analogRead can be executed 50.000 times each second, but internally it is still sampled with 6000Hz. So this library will check if 6000Hz can be reached.
- ADC center is detected at beginning, when you init the library, so you don't need to set a center anymore.
- Magnitude is also set automatically (hope my algorithm will work everywhere) if you want

It still use floats, but the intent was to write a library for processors like on the Arduino Due, ESP and Teensy boards, not 8-bit processors.



Tombays

How i can connect thelephone? I mean not smartphone, i mean phone that stays at home. I connected in parallel a telephone, a transformer and a 12 volt power supply. When I connect the headphones to the secondary coil of the transformer - everything is audible ... But when I connect telephone to arduino through the circuit drawn in the image, it does not determine anything. analog output - 0 when nothing and 15-30 when I dial the number.

6v6gt

#74
Sep 13, 2019, 01:52 pm Last Edit: Sep 13, 2019, 05:29 pm by 6v6gt Reason: Added files
Here is yet another software DTMF decoder based on the https://github.com/jacobrosenthal/Goertzel library. The main differences are that this version does not control the sample frequency by calling analogRead() in a loop but uses a timer to drive the ADC at any user-selected frequency. The sample buffer is filled by the ISR of the ADC. Also, the bit resolution is 8 bits instead of the normal 10 to optimise the calculation speed.
It appears quite reliable even at quite high rates (50mS tone burst, 50mS space) although I need it only for detecting tones generated by a phone keypad which is far less demanding.
I also made a few other minor changes to the library because the original did not support multiple instances due to its use of global instead of instance variables. The solution described here uses 8 Goertzel objects, one for each detection frequency. However, there is only a single sample buffer.
In addition, in the test sketch, I have added a validation parameter to control the number of required identical consecutive repetitions of the detection of a tone before it is considered valid.

In principle, it works like this:
8 Goertzel objects are created, one for each detection frequency.
The shared sample buffer is initiated and a pointer is passed to the class.
The coefficients are then calculated for each Goertzel object.

During operation, the sample buffer is filled with the specified number of samples.
The 4 "row" Goertzel objects test the sample and the "winner" frequency is determined, that is the one which yields the highest magnitude.
The same applies to the 4 "column" Goertzel objects. At the end, the received DTMF character is determined.
The operation is repeated.
When the exact required number of identical consecutive detections is reached, the result is delivered together with a quality statistic.


There are a number of parameters to play with:
The number of samples: 64 is good with 9600sps. 128 is good with 19200sps.
The sample frequency (sps): see above
The ADC prescaler:  ( /8 or /16 seems OK)
The number of consecutive matches required to validate a result: This however can be slow. For large numbers of samples and high DTMF rates, this parameter must be low, say 1 or even 0.
The magnitude threshold (this is now non-critical because the highest magnitude row and column matches are selected, but it must be low enough to ensure that no valid matches are lost). 500 is good for 8 bit ADC resolution. 2000 is good if you revert to 10 bits

On the hardware side, attempt to match the 2 resistors between Vcc and Ground to ensure that the the midpoint voltage is Vcc/2.
For testing, I use an old telephone, some online resources like http://dialabc.com/sound/generate/ and https://www.audiocheck.net/audiocheck_dtmf.php and the el_supremo test files contained in the zip file in the OP of this thread).
Do NOT use the supplied circuit to connect directly to a telephone network where voltages up 90VAC may be encountered.

To use, simply copy the 2 library parts (.h and .cpp) into the folder containing the .ino file.


Notes:
1. This solution uses ATmega328P (Arduino Uno etc.) hardware registers and may have to be adapted for other boards.
2. Since the ADC is connected to a timer, the simultaneous use of analogRead() may cause problems.
3. Timer1 is used by this application.

Future:
1. attempt to drop floats to optimize calculations
2. Allow the timer driving the ADC to run only during the capture of the sample to free these resources for other purposes.



Go Up