Compare commits
21 Commits
the_other_
...
master
Author | SHA1 | Date | |
---|---|---|---|
fff9dec208 | |||
1ca5d7b208 | |||
bcbf835bcd | |||
dff7c4745f | |||
0c703101a3 | |||
66ef9685fa | |||
21a352ed43 | |||
09075932a7 | |||
fed15da794 | |||
eb5aee66bc | |||
e6597a726b | |||
fd7491d8aa | |||
7f35b88b81 | |||
aa1ed81a6f | |||
f830cd2251 | |||
f748faa6e8 | |||
34ae6a7f70 | |||
4bfe585457 | |||
5ea835ef74 | |||
0e6bf815ca | |||
eb947bff8c |
286
Hohm_Phone.ino
@ -6,35 +6,28 @@
|
|||||||
////////// PARAMETERS //////////
|
////////// PARAMETERS //////////
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
|
|
||||||
// Arduino will wake from sleep when WAKE_PIN is pulled low. Connect any waking buttons to WAKE_PIN
|
#define LED_BAT_LOW 3
|
||||||
#define WAKE_PIN 2
|
#define LED_NO_SERVICE 2
|
||||||
|
|
||||||
#define BUT_ANS A3
|
#define BUT_ANS A3
|
||||||
#define BUT_END A4
|
#define BUT_END A4
|
||||||
|
|
||||||
// RX/TX are in reference to the Arduino, not SIM800
|
|
||||||
#define GSM_RX 3
|
|
||||||
#define GSM_TX 11
|
|
||||||
#define GSM_RST A2
|
#define GSM_RST A2
|
||||||
#define GSM_RING A5
|
#define GSM_RING A5
|
||||||
|
#define GSM_BAUDRATE 115200
|
||||||
|
|
||||||
// TIMEOUT_SLEEP is the time to stay awake from last activity until sleep (milliseconds)
|
// Low battery light threshold
|
||||||
#define TIMEOUT_SLEEP 6000
|
#define CHG_VLO 3800
|
||||||
|
|
||||||
// Charging voltage thresholds. Will turn on charging at CHG_VLO and turn off charging at CHG_VHI (millivolts)
|
// Time in miliseconds to stop listening for keypad input
|
||||||
#define CHG_VLO 3900
|
#define SLEEP_TIMEOUT 120000
|
||||||
#define CHG_VHI 4100
|
|
||||||
#define CHG_PIN A0
|
|
||||||
|
|
||||||
// Comment the following line to use HW serial for Fona and disable debugging information
|
// RSSI value below which No Service LED will light
|
||||||
#define USB_DEBUG
|
#define RSSI_THRESHOLD 1
|
||||||
|
|
||||||
// Comment the following line to disable sleeping the Arduino
|
|
||||||
//#define SLEEP
|
|
||||||
|
|
||||||
// Keypad pinout
|
// Keypad pinout
|
||||||
byte rowPins[4] = {9, 4, 5, 7};
|
byte rowPins[4] = {5, 10, 9, 7};
|
||||||
byte colPins[3] = {8, 10, 6};
|
byte colPins[3] = {6, 4, 8};
|
||||||
|
|
||||||
////////////////////////////////////
|
////////////////////////////////////
|
||||||
////////// END PARAMETERS //////////
|
////////// END PARAMETERS //////////
|
||||||
@ -53,84 +46,55 @@ char keys[4][3] = {
|
|||||||
unsigned long lastActiveTime;
|
unsigned long lastActiveTime;
|
||||||
bool dialtoneActive = false;
|
bool dialtoneActive = false;
|
||||||
bool startDialtone = false;
|
bool startDialtone = false;
|
||||||
|
bool awake = true;
|
||||||
|
|
||||||
#ifdef USB_DEBUG
|
|
||||||
#include "SoftwareSerial.h"
|
|
||||||
SoftwareSerial fonaSS = SoftwareSerial(GSM_RX, GSM_TX);
|
|
||||||
SoftwareSerial *fonaSerial = &fonaSS;
|
|
||||||
#else
|
|
||||||
HardwareSerial *fonaSerial = &Serial;
|
HardwareSerial *fonaSerial = &Serial;
|
||||||
#endif
|
|
||||||
|
|
||||||
Adafruit_FONA fona = Adafruit_FONA(GSM_RST);
|
Adafruit_FONA fona = Adafruit_FONA(GSM_RST);
|
||||||
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, 4, 3 );
|
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, 4, 3 );
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////
|
///////////////////////////////
|
||||||
////////// FUNCTIONS //////////
|
////////// FUNCTIONS //////////
|
||||||
///////////////////////////////
|
///////////////////////////////
|
||||||
|
|
||||||
void wakeFromSleep() {
|
|
||||||
sleep_disable();
|
|
||||||
detachInterrupt(WAKE_PIN);
|
|
||||||
#ifdef USB_DEBUG
|
|
||||||
Serial.println("Woke up");
|
|
||||||
#endif
|
|
||||||
startDialtone = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void goToSleep() {
|
|
||||||
#ifdef USB_DEBUG
|
|
||||||
Serial.println("Going to sleep");
|
|
||||||
#endif
|
|
||||||
sleep_enable();
|
|
||||||
attachInterrupt(WAKE_PIN, wakeFromSleep, LOW);
|
|
||||||
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
|
|
||||||
cli();
|
|
||||||
sleep_bod_disable();
|
|
||||||
sei();
|
|
||||||
sleep_cpu();
|
|
||||||
}
|
|
||||||
|
|
||||||
void inCall() {
|
void inCall() {
|
||||||
while (1) {
|
while (1) {
|
||||||
if ( !digitalRead(BUT_END) || fona.getCallStatus() < 3 ) { // End button pressed
|
if ( !digitalRead(BUT_END) || fona.getCallStatus() < 3 ) { // End button pressed
|
||||||
fona.hangUp();
|
fona.hangUp();
|
||||||
#ifdef USB_DEBUG
|
|
||||||
Serial.println("Hanging up");
|
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// numpad stuff
|
// numpad stuff
|
||||||
}
|
}
|
||||||
|
clearPhoneNumber();
|
||||||
|
startDialtone = true;
|
||||||
|
lastActiveTime = millis();
|
||||||
|
delay(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
void beginCall() {
|
||||||
|
fona.sendCheckReply( F("AT+STTONE=0"), F("OK") ); // End dialtone
|
||||||
|
dialtoneActive = false;
|
||||||
|
if ( fona.callPhone( phoneNumber ) ) {
|
||||||
|
inCall();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void resumeDialtone() {
|
||||||
|
fona.sendCheckReply( ("AT+STTONE=1,20," + String(SLEEP_TIMEOUT)).c_str(), "OK" ); // Start dialtone
|
||||||
|
dialtoneActive = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearPhoneNumber() {
|
||||||
for ( int j = 0; j < phoneNumberLength; j++) {
|
for ( int j = 0; j < phoneNumberLength; j++) {
|
||||||
phoneNumber[j] = 0;
|
phoneNumber[j] = 0;
|
||||||
}
|
}
|
||||||
phoneNumberLength = 0;
|
phoneNumberLength = 0;
|
||||||
startDialtone = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void beginCall() {
|
void goToSleep() {
|
||||||
#ifdef USB_DEBUG
|
digitalWrite( LED_NO_SERVICE, LOW );
|
||||||
Serial.println("Starting Call");
|
digitalWrite( LED_BAT_LOW, LOW );
|
||||||
#endif
|
awake = false;
|
||||||
fona.sendCheckReply( F("AT+STTONE=0"), F("OK") ); // End dialtone
|
|
||||||
dialtoneActive = false;
|
|
||||||
if ( fona.callPhone( phoneNumber ) ) {
|
|
||||||
#ifdef USB_DEBUG
|
|
||||||
Serial.println("Call Started");
|
|
||||||
#endif
|
|
||||||
inCall();
|
|
||||||
}
|
|
||||||
#ifdef USB_DEBUG
|
|
||||||
Serial.println("Call ended");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void resumeDialtone() {
|
|
||||||
fona.sendCheckReply( F("AT+STTONE=1,20,30000" ), F("OK") ); // Start dialtone
|
|
||||||
dialtoneActive = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
@ -138,112 +102,110 @@ void resumeDialtone() {
|
|||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
#ifdef USB_DEBUG
|
|
||||||
Serial.begin(9600);
|
|
||||||
Serial.println("Booted. Setting up");
|
|
||||||
#endif
|
|
||||||
pinMode( WAKE_PIN, INPUT_PULLUP );
|
|
||||||
pinMode( BUT_ANS, INPUT_PULLUP );
|
pinMode( BUT_ANS, INPUT_PULLUP );
|
||||||
pinMode( BUT_END, INPUT_PULLUP );
|
pinMode( BUT_END, INPUT_PULLUP );
|
||||||
pinMode( GSM_RST, OUTPUT );
|
pinMode( GSM_RST, OUTPUT );
|
||||||
pinMode( GSM_RING, INPUT_PULLUP );
|
pinMode( GSM_RING, INPUT_PULLUP );
|
||||||
pinMode( CHG_PIN, OUTPUT );
|
pinMode( LED_BAT_LOW, OUTPUT);
|
||||||
|
pinMode( LED_NO_SERVICE, OUTPUT);
|
||||||
|
|
||||||
|
digitalWrite( LED_BAT_LOW, HIGH );
|
||||||
|
digitalWrite( LED_NO_SERVICE, HIGH );
|
||||||
|
|
||||||
digitalWrite( GSM_RST, HIGH );
|
digitalWrite( GSM_RST, HIGH );
|
||||||
digitalWrite( CHG_PIN, HIGH );
|
fonaSerial->begin(GSM_BAUDRATE);
|
||||||
|
|
||||||
fonaSerial->begin(4800);
|
|
||||||
if (! fona.begin(*fonaSerial)) {
|
if (! fona.begin(*fonaSerial)) {
|
||||||
#ifdef USB_DEBUG
|
|
||||||
Serial.println("Fona failed to respond");
|
|
||||||
#endif
|
|
||||||
while (1); //fona didn't start
|
while (1); //fona didn't start
|
||||||
|
digitalWrite( LED_BAT_LOW, HIGH );
|
||||||
}
|
}
|
||||||
while ( !fona.setAudio(FONA_EXTAUDIO) ) {}
|
|
||||||
|
|
||||||
#ifdef USB_DEBUG
|
|
||||||
Serial.println("Setup Complete");
|
|
||||||
#endif
|
|
||||||
startDialtone = true;
|
startDialtone = true;
|
||||||
lastActiveTime = millis();
|
lastActiveTime = millis();
|
||||||
|
|
||||||
fona.sendCheckReply( F("AT+CLVL=20"), F("OK") ); // set dialtone volume
|
fona.sendCheckReply( F("AT+CLVL=100"), F("OK") ); // set volume
|
||||||
|
|
||||||
|
digitalWrite( LED_BAT_LOW, LOW );
|
||||||
|
digitalWrite( LED_NO_SERVICE, LOW );
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
if ( !digitalRead(GSM_RING) ) {
|
if ( awake ) {
|
||||||
while ( digitalRead(BUT_ANS) & !digitalRead(GSM_RING) ) delay(10); // Wait for answer button or end ring/call
|
|
||||||
if ( !digitalRead(BUT_ANS) ) {
|
// Handle Incoming Call
|
||||||
fona.pickUp();
|
if ( !digitalRead(GSM_RING) ) {
|
||||||
delay(100);
|
while ( digitalRead(BUT_ANS) & digitalRead(BUT_END) & !digitalRead(GSM_RING) ) delay(10); // Wait for answer button or end ring/call
|
||||||
inCall();
|
if ( !digitalRead(BUT_ANS) ) {
|
||||||
|
fona.pickUp();
|
||||||
|
delay(100);
|
||||||
|
inCall();
|
||||||
|
} else if( !digitalRead(BUT_END) ) {
|
||||||
|
fona.hangUp();
|
||||||
|
clearPhoneNumber();
|
||||||
|
startDialtone = true;
|
||||||
|
delay(100);
|
||||||
|
resumeDialtone();
|
||||||
|
lastActiveTime = millis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Begin dialtone if necessary
|
||||||
|
if ( startDialtone ) {
|
||||||
|
resumeDialtone();
|
||||||
|
startDialtone = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read keypad input
|
||||||
|
char key = keypad.getKey();
|
||||||
|
if ( key != NO_KEY ) {
|
||||||
|
phoneNumber[ phoneNumberLength ] = key;
|
||||||
|
phoneNumberLength = phoneNumberLength + 1;
|
||||||
|
|
||||||
|
if ( dialtoneActive ) {
|
||||||
|
fona.sendCheckReply( F("AT+STTONE=0"), F("OK") ); // End dialtone
|
||||||
|
dialtoneActive = false;
|
||||||
|
delay(100);
|
||||||
|
}
|
||||||
|
fona.playDTMF( key ); // Play DTMF tone
|
||||||
|
|
||||||
|
lastActiveTime = millis();
|
||||||
|
// Check for complete phone number (including +1 country code)
|
||||||
|
if ( ( phoneNumberLength == 10 & phoneNumber[0] != '1' ) || phoneNumberLength > 10 ) {
|
||||||
|
beginCall();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear stored phone number on press of end button
|
||||||
|
if ( !digitalRead(BUT_END) ) {
|
||||||
|
clearPhoneNumber();
|
||||||
|
resumeDialtone();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Indicator LEDs
|
||||||
|
uint16_t vbat;
|
||||||
|
fona.getBattVoltage(&vbat);
|
||||||
|
if ( vbat < CHG_VLO ) {
|
||||||
|
digitalWrite( LED_BAT_LOW, HIGH );
|
||||||
|
} else {
|
||||||
|
digitalWrite( LED_BAT_LOW, LOW );
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t rssi;
|
||||||
|
rssi = fona.getRSSI();
|
||||||
|
if ( rssi < RSSI_THRESHOLD || rssi == 99 ) {
|
||||||
|
digitalWrite( LED_NO_SERVICE, HIGH );
|
||||||
|
} else {
|
||||||
|
digitalWrite( LED_NO_SERVICE, LOW );
|
||||||
|
}
|
||||||
|
|
||||||
|
// If inactive, sleep
|
||||||
|
if ( (long)(millis() - lastActiveTime) > SLEEP_TIMEOUT ) {
|
||||||
|
goToSleep();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// sleeping
|
||||||
|
if ( !digitalRead( BUT_ANS ) || !digitalRead( BUT_END ) || !digitalRead( GSM_RING ) ) {
|
||||||
|
lastActiveTime = millis();
|
||||||
|
awake = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( startDialtone ) {
|
|
||||||
resumeDialtone();
|
|
||||||
startDialtone = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read from keypad
|
|
||||||
char key = keypad.getKey();
|
|
||||||
if ( key != NO_KEY ) {
|
|
||||||
phoneNumber[ phoneNumberLength ] = key;
|
|
||||||
phoneNumberLength = phoneNumberLength + 1;
|
|
||||||
|
|
||||||
if ( dialtoneActive ) {
|
|
||||||
fona.sendCheckReply( F("AT+STTONE=0"), F("OK") ); // End dialtone
|
|
||||||
dialtoneActive = false;
|
|
||||||
delay(50);
|
|
||||||
}
|
|
||||||
fona.playDTMF( key ); // Play DTMF tone
|
|
||||||
|
|
||||||
lastActiveTime = millis();
|
|
||||||
#ifdef USB_DEBUG
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < 15; i = i + 1) {
|
|
||||||
Serial.print(phoneNumber[i]);
|
|
||||||
Serial.print(", ");
|
|
||||||
}
|
|
||||||
Serial.println(phoneNumberLength);
|
|
||||||
#endif
|
|
||||||
// Check for complete phone number (including +1 country code)
|
|
||||||
if ( ( phoneNumberLength == 10 & phoneNumber[0] != '1' ) || phoneNumberLength > 10 ) {
|
|
||||||
beginCall();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Clear stored phone number on press of end button
|
|
||||||
if ( !digitalRead(BUT_END) ) {
|
|
||||||
for ( int j = 0; j < phoneNumberLength; j++) {
|
|
||||||
phoneNumber[j] = 0;
|
|
||||||
}
|
|
||||||
phoneNumberLength = 0;
|
|
||||||
#ifdef USB_DEBUG
|
|
||||||
Serial.println( phoneNumberLength );
|
|
||||||
#endif
|
|
||||||
resumeDialtone();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t vbat;
|
|
||||||
fona.getBattVoltage(&vbat);
|
|
||||||
if ( vbat < CHG_VLO ) {
|
|
||||||
digitalWrite( CHG_PIN, HIGH );
|
|
||||||
} else if ( vbat > CHG_VHI ) {
|
|
||||||
digitalWrite( CHG_PIN, LOW );
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SLEEP
|
|
||||||
// Autoshutdown if inactive for extended period
|
|
||||||
// Typecast to long avoids "rollover" issues
|
|
||||||
if ( (long)(millis() - lastActiveTime) > TIMEOUT_SLEEP ) {
|
|
||||||
goToSleep();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
BIN
hardware/Assem2.SLDASM
Normal file
BIN
hardware/BOM_1v1.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
hardware/Basepic.SLDDRW
Normal file
54
hardware/DTMF_Research.bib
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
@misc{ wiki:DTMF,
|
||||||
|
author = "Wikipedia",
|
||||||
|
title = "Dual-tone multi-frequency signaling",
|
||||||
|
year = "2016",
|
||||||
|
url = "https://en.wikipedia.org/wiki/Dual-tone_multi-frequency_signaling",
|
||||||
|
note = "[Online; accessed 27-August-2016]"
|
||||||
|
}
|
||||||
|
|
||||||
|
@misc{ wiki:Goertzel,
|
||||||
|
author = "Wikipedia",
|
||||||
|
title = "Goertzel algorithm",
|
||||||
|
year = "2016",
|
||||||
|
url = "https://en.wikipedia.org/wiki/Goertzel_algorithm",
|
||||||
|
note = "[Online; accessed 27-August-2016]"
|
||||||
|
}
|
||||||
|
|
||||||
|
@misc{ wiki:FIR,
|
||||||
|
author = "Wikipedia",
|
||||||
|
title = "Finite impulse response",
|
||||||
|
year = "2016",
|
||||||
|
url = "https://en.wikipedia.org/wiki/Finite_impulse_response",
|
||||||
|
note = "[Online; accessed 27-August-2016]"
|
||||||
|
}
|
||||||
|
|
||||||
|
@misc{ wiki:IIR,
|
||||||
|
author = "Wikipedia",
|
||||||
|
title = "Infinite impulse response",
|
||||||
|
year = "2016",
|
||||||
|
url = "https://en.wikipedia.org/wiki/Infinite_impulse_response",
|
||||||
|
note = "[Online; accessed 27-August-2016]"
|
||||||
|
}
|
||||||
|
|
||||||
|
@misc{ wiki:Nyquist-Shannon,
|
||||||
|
author = "Wikipedia",
|
||||||
|
title = "Nyquist–Shannon sampling theorem",
|
||||||
|
year = "2016",
|
||||||
|
url = "https://en.wikipedia.org/wiki/Nyquist%E2%80%93Shannon_sampling_theorem",
|
||||||
|
note = "[Online; accessed 27-August-2016]"
|
||||||
|
}
|
||||||
|
|
||||||
|
@misc{ wiki:Z-transform,
|
||||||
|
author = "Wikipedia",
|
||||||
|
title = "Z-transform",
|
||||||
|
year = "2016",
|
||||||
|
url = "https://en.wikipedia.org/wiki/Z-transform",
|
||||||
|
note = "[Online; accessed 27-August-2016]"
|
||||||
|
}
|
||||||
|
|
||||||
|
@article{ DTMF_genave,
|
||||||
|
author = "Genave.com",
|
||||||
|
title = "DTMF Explained",
|
||||||
|
url = "http://www.genave.com/dtmf.htm",
|
||||||
|
note = "[Online; accessed 27-August-2016]"
|
||||||
|
}
|
BIN
hardware/DTMF_Research.pdf
Normal file
BIN
hardware/DTMF_Research.synctex.gz
Normal file
66
hardware/DTMF_Research.tex
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
\documentclass[12pt]{article}
|
||||||
|
\usepackage[margin=1in]{geometry}
|
||||||
|
\usepackage{parskip}
|
||||||
|
\usepackage{cite}
|
||||||
|
\usepackage{url}
|
||||||
|
\usepackage{arydshln}
|
||||||
|
\usepackage{amsmath}
|
||||||
|
|
||||||
|
\begin{document}
|
||||||
|
\title{Dual-Tone Multi-Frequency Signaling}
|
||||||
|
\author{Brendan Haines}
|
||||||
|
\maketitle
|
||||||
|
|
||||||
|
\section{Encoding}
|
||||||
|
Dual-Tone Multi Frequency Signaling uses audible tones as opposed to digital signals due to the nature of the mediums they are broadcast over. Since DTMF is often used with radio phones as well as traditional "landline" telephones, and these technologies are designed to carry voice well, staying within the same frequency range reduces complexity and expense of specialized equipment at both the transmitting and receiving stations. Often, the speaker output of a radio can simply be plugged into a decoder with no additional specialized hardware, resulting in lower equipment costs\cite{DTMF_genave}.
|
||||||
|
|
||||||
|
\subsection{Frequencies\cite{wiki:DTMF}}
|
||||||
|
\begin{tabular}{|c c c : c | l |}
|
||||||
|
\hline
|
||||||
|
1 & 2 & 3 & A & 687 Hz \\
|
||||||
|
4 & 5 & 6 & B & 770 Hz\\
|
||||||
|
7 & 8 & 9 & C & 852 Hz\\
|
||||||
|
$\ast$ & 0 & \# & D & 941 Hz\\
|
||||||
|
\hline
|
||||||
|
1209 Hz & 1336 Hz & 1477 Hz & 1633 Hz \\
|
||||||
|
\cline{1-4}
|
||||||
|
\end{tabular}
|
||||||
|
|
||||||
|
The A, B, C, and D keys are omitted on most handsets however are often used for automation purposes for triggering of remote functions such as controling an amateur radio repeater during an active phone call\cite{wiki:DTMF}.
|
||||||
|
|
||||||
|
\subsection{Timing}
|
||||||
|
DTF uses a "mark" (amplitude $\neq$ 0) followed by a "space" (amplitude = 0). Timing can vary widely depending on the system being used. For example, when using a manual encoder such as an ordinary telephone each button press will create a mark and the time between presses will be spaces. Higher speeds can be achieved using automatic or "store and forward" DTMF encoders. Motorola uses a standard of 250ms mark and 250ms space, and other systems include 40ms/20ms or 20ms/20ms mark and space respectively\cite{DTMF_genave}.
|
||||||
|
|
||||||
|
\section{Decoding}
|
||||||
|
Originally decoded using tuned filter banks, however DSP now dominates decoding and the Goertzel algorthim is often used\cite{wiki:DTMF}.
|
||||||
|
|
||||||
|
\subsection{Goertzel Algorithm}
|
||||||
|
The Goertzel algorithm or Goertzel filter uses the Discrete Fourier Transform (DFT) to evaluate frequency content of a signal at specific frequencies very efficiently. Although the Fast Fourier Transform (FFT) is more efficient for analyzing a complete spectrum of frequency, in the case where only a few frequencies are relevant (such as the 8 DTMF frequencies), the Goertzel algorithm is more numerically efficient. It works well even on small processors in embedded applications\cite{wiki:Goertzel}
|
||||||
|
|
||||||
|
Where $x[n]$ is the $n^{th}$ sample and $\omega _{0}$ is frequency in radians per sample, an intermediate sequence $s[n]$:
|
||||||
|
$$s[-2]=s[-1]=0$$
|
||||||
|
\begin{equation} \label{eqn:stage_1_goertzel}
|
||||||
|
s[n] = x[n] + 2 cos(\omega _{0})s[n-1] - s[n-2]
|
||||||
|
\end{equation}
|
||||||
|
|
||||||
|
The second stage of the Goertzel filter applies to $s[n]$, producing output sequence $y[n]$:
|
||||||
|
\begin{equation} \label{eqn:stage_2_goertzel}
|
||||||
|
y[n] = s[n] - e^{-j\omega _{0}}s[n-1]
|
||||||
|
\end{equation}
|
||||||
|
|
||||||
|
A Z-transform converts a discrete-time signal to a complex frequency domain\cite{wiki:Z-transform}. It can be applied to equations \ref{eqn:stage_1_goertzel} and \ref{eqn:stage_2_goertzel} respectively:
|
||||||
|
\begin{align} \label{eqn:z_goertzel}
|
||||||
|
\frac{S(z)}{X(z)} &= \frac{1}{1-2cos(\omega _{0})z^{-1} + z^{-2}}\nonumber\\
|
||||||
|
&= \frac{1}{(1-e^{+j\omega _{0}} z^{-1})(1 - e^{-j\omega _{0}}z^{-1})}\\
|
||||||
|
\frac{Y(z)}{S(z)} &= 1 - e^{-j\omega _{0}} z^{-1}
|
||||||
|
\end{align}
|
||||||
|
|
||||||
|
The combined transfer function of the cascade of the two filters:
|
||||||
|
\begin{align} \label{eqn:z_combined_goertzel}
|
||||||
|
\frac{S(z)}{X(z)}\frac{Y(z)}{S(z)} = \frac{Y(z)}{X(z)} &= \frac{(1-e^{-j\omega _{0}}z^{-1})}{(1-e^{+j\omega _{0}} z^{-1})(1 - e^{-j\omega _{0}}z^{-1})}\nonumber\\
|
||||||
|
&= \frac{1}{(1-e^{+j\omega _{0}} z^{-1})}
|
||||||
|
\end{align}
|
||||||
|
|
||||||
|
\bibliography{DTMF_Research}{}
|
||||||
|
\bibliographystyle{IEEEtran}
|
||||||
|
\end{document}
|
3
hardware/DTMF_Research_bib.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
@article{ wikipedia_DTMF
|
||||||
|
title="DTMF"
|
||||||
|
}
|
BIN
hardware/Hohm Phone pcb 3D.png
Normal file
After Width: | Height: | Size: 288 KiB |
BIN
hardware/Home Base.sldprt
Normal file
BIN
hardware/P1.SLDPRT
Normal file
BIN
hardware/P1.STL
Normal file
BIN
hardware/P1Inductor.SLDPRT
Normal file
BIN
hardware/P1Keypad.SLDPRT
Normal file
BIN
hardware/P1Speaker.SLDPRT
Normal file
BIN
hardware/P2.SLDPRT
Normal file
BIN
hardware/P2.STL
Normal file
BIN
hardware/P2_casual.png
Normal file
After Width: | Height: | Size: 4.8 MiB |
BIN
hardware/P2_casual_g.png
Normal file
After Width: | Height: | Size: 3.2 MiB |
BIN
hardware/P2_closeup_buttons.png
Normal file
After Width: | Height: | Size: 4.1 MiB |
BIN
hardware/P2_left.SLDPRT
Normal file
BIN
hardware/P2_left.STL
Normal file
BIN
hardware/P2_right.SLDPRT
Normal file
BIN
hardware/P2_right.STL
Normal file
BIN
hardware/P3 - Part 2.stl
Normal file
BIN
hardware/P3 - base.SLDPRT
Normal file
BIN
hardware/P3.SLDPRT
Normal file
BIN
hardware/P7.SLDPRT
Normal file
BIN
hardware/P7_assembly.png
Normal file
After Width: | Height: | Size: 1.9 MiB |
BIN
hardware/P7_assembly_better.png
Normal file
After Width: | Height: | Size: 2.1 MiB |
BIN
hardware/P8.SLDPRT
Normal file
BIN
hardware/P8_left.SLDDRW
Normal file
BIN
hardware/P8_left.SLDPRT
Normal file
BIN
hardware/P8_left.STL
Normal file
BIN
hardware/P8_right.SLDPRT
Normal file
BIN
hardware/P8_right.STL
Normal file
BIN
hardware/P9.SLDPRT
Normal file
BIN
hardware/P9_left.STL
Normal file
BIN
hardware/P9_right.STL
Normal file
BIN
hardware/Slack for iOS Upload (1).jpg
Normal file
After Width: | Height: | Size: 2.8 MiB |
BIN
hardware/Slack for iOS Upload.jpg
Normal file
After Width: | Height: | Size: 2.6 MiB |
BIN
hardware/button cap.SLDPRT
Normal file
BIN
hardware/final_assem.SLDASM
Normal file
BIN
hardware/final_bottom.png
Normal file
After Width: | Height: | Size: 3.3 MiB |
BIN
hardware/final_docked.png
Normal file
After Width: | Height: | Size: 4.2 MiB |
BIN
hardware/final_top.png
Normal file
After Width: | Height: | Size: 3.9 MiB |