ArduinoだけでDTMFデコーダーを構築 – 外付けIC不要
必要な部品と材料
- Arduino UNO × 1
- LED(汎用) × 10
- ブレッドボード(汎用) × 1
- ジャンパーワイヤー(汎用) × 20本
使用するアプリとオンラインサービス
- Arduino IDE
プロジェクトの概要
学期のプロジェクト課題でDTMF制御のラジコンカーを見つけました。これはCM8870というDTMFデコーダーICを使用し、デコードされたバイナリデータをArduinoに渡して前後左右の制御を行うものでした。しかし、この外付けICの組み込みが難しく、ライブラリやコードでICを置き換えられる方法を探していました。そこで、ゲルツェルアルゴリズム(Goertzelアルゴリズム)をベースにしたGoertzel.hライブラリを見つけました。これは特定の周波数が信号に含まれているかどうかを、単位時間あたりのパルス数と振幅寄与から判定するものです。
このライブラリの基本サンプルコードをベースに、DTMFトーンに含まれる主要な周波数を検出し、電話の相手側で押された番号をデコードするコードを作成しました。
回路図
デジタルピン2〜12に10個のLEDを接続し、0〜9の数字に対応するトーンが検出されるとそれぞれ点灯します。ピン13にはパルス検出用LEDを接続し、信号検出時に短く点滅します。10以上の特殊文字(*、#、A〜D)はシリアルモニターに出力されます。オーディオジャックまたはAUXケーブルからの入力は、1μFのコンデンサを直列に挟んでアナログピンA0に接続し、低周波とDCオフセットを除去します。
ソースコード
/* DTMFデコーダーの基本実装
* アナログピンA0から16文字のDTMFコードを検出し、DTMFマトリクスの上段・下段トーンをチェックして
* 対応する数字をデジタル出力(0-9)およびシリアル出力(A-D、*、#)で表示します。
* Kevin Banks氏のコード(https://www.embedded.com/design/embedded/4024443/The-Goertzel-Algorithm)をベースにしています。
* ゲルツェルアルゴリズムの詳細は https://en.wikipedia.org/wiki/Goertzel_algorithm を参照。
* Mian Mohammad Shoaib氏によって改変・公開。
* 問い合わせ: MMSHOAIB8452@GMAIL.COM
*/
#include <Goertzel.h>
int sensorPin = A0;
const int N = 100; // サンプル数(感度調整可能、大きすぎると処理が遅くなる)
const float threshold = 2000; // 検出閾値(誤検知/検知漏れに応じて調整)
const float sampling_freq = 8900; // サンプリング周波数(Arduino Uno 16MHzで最大8900Hz)
float x_frequencies[4]; // 高周波群(列)
float y_frequencies[4]; // 低周波群(行)
void setup() {
pinMode(13, OUTPUT); // 検出表示用LED
for (int i = 2; i <= 12; i++) pinMode(i, OUTPUT); // 数字表示用LED
Serial.begin(9600);
// DTMFマトリクスの周波数設定
x_frequencies[0] = 1209; x_frequencies[1] = 1336;
x_frequencies[2] = 1477; x_frequencies[3] = 1633;
y_frequencies[0] = 697; y_frequencies[1] = 770;
y_frequencies[2] = 852; y_frequencies[3] = 941;
}
// 指定周波数のトーンを検出
bool detect_tone(float freq) {
Goertzel goertzel = Goertzel(freq, N, sampling_freq);
goertzel.sample(sensorPin);
float magnitude = goertzel.detect();
if (magnitude > threshold) {
digitalWrite(13, HIGH);
delay(250);
digitalWrite(13, LOW);
Serial.println(freq);
return true;
}
return false;
}
// 行・列から文字を特定して出力
void print_number(int row, int column) {
int number = 0;
if (row == 0) {
if (column == 0) number = 1;
else if (column == 1) number = 2;
else if (column == 2) number = 3;
else if (column == 3) number = 10; // A
} else if (row == 1) {
if (column == 0) number = 4;
else if (column == 1) number = 5;
else if (column == 2) number = 6;
else if (column == 3) number = 11; // B
} else if (row == 2) {
if (column == 0) number = 7;
else if (column == 1) number = 8;
else if (column == 2) number = 9;
else if (column == 3) number = 12; // C
} else if (row == 3) {
if (column == 0) number = 14; // *
else if (column == 1) number = 0;
else if (column == 2) number = 15; // #
else if (column == 3) number = 13; // D
}
if (number < 10) {
digitalWrite(number + 2, HIGH);
Serial.print(number);
} else if (number == 10) Serial.print('A');
else if (number == 11) Serial.print('B');
else if (number == 12) Serial.print('C');
else if (number == 13) Serial.print('D');
else if (number == 14) Serial.print('*');
else if (number == 15) Serial.print('#');
Serial.println();
delay(800);
for (int i = 2; i <= 12; i++) digitalWrite(i, LOW);
}
void loop() {
int column = 0, row = 0, i = 0;
// 高周波群(列)を検出
while (1) {
if (detect_tone(x_frequencies[i])) { column = i; break; }
i++; if (i == 4) i = 0;
}
// 低周波群(行)を検出
i = 0;
while (1) {
if (detect_tone(y_frequencies[i])) { row = i; break; }
i++; if (i == 4) i = 0;
}
print_number(row, column);
}
ライブラリのインストール
Goertzelライブラリが必要です。以下からダウンロードしてArduino IDEにインストールしてください。
https://github.com/jacobrosenthal/Goertzel
製造プロセス
- Arduinoで作るモニター用Ambilightシステム:没入感アップの自作ガイド
- Arduinoで超音波レベーション(浮遊)装置を自作:ステップバイステップガイド
- SERENA:Arduino Mega 2560とTFT LCDタッチスクリーン搭載カスタムアラームシステム
- Arduinoとスマホで作るスマート電圧計 – 簡単DIYプロジェクト
- ArduinoとMAX30100で作るリアルタイムIoT心拍数モニター
- レーザーモジュールでモールス信号通信 – Arduino二台で送受信
- Arduino UNO WiFi Webサーバー:ブラウザからLEDをON/OFF制御
- スマート駐車場カウンター:Arduino、Processing、PHPでリアルタイム車両管理
- Arduinoで自動化するChrome恐竜ゲーム – LDRとPythonで障害物を検知してジャンプ
- Python 3からArduino UNOを制御 – シリアル通信でLED点滅デモ
- Arduino NanoとRDA5807Mで作るコンパクトFMラジオ