本記事は稼働テストが成功していません
概要
DTMF(トーン信号)を生成して、電話をかける。
はじめに
私は、電子工作でどうしても試してみたいことがありました。それがこのDTMFです。
映画『名探偵コナン 戦慄の楽譜』(2008年) では、コナンともう一人の生声によって、110番に電話をかけるシーンが出てきます。「音痴で有名なコナンが特定周波数の声を出せるのか!?」と疑問に思いましたが、「まぁ、映画だしね。」と流していました。そもそも、声でそんなこと出来ないだろうし。
ところが、その後、朝日放送『探偵!ナイトスクープ』(2009年) では、コナンを検証する形で、音大生二人の生声によって時報サービスに電話をかけることに成功しました。この瞬間、鳥肌が立ちましたね。もう、何年も前の話ですが、鮮明に覚えています。
「WROOM02 で、是非、やってやろう!」というのが今回の企画です。
「このご時世、Twilio 等、専用の電話APIがあるじゃない」は、言わないで。ただ、やってみたいだけですから。
仕様
- DTMF
- DTMF(英: Dual-Tone Multi-Frequency)は、0から9までの数字と、*、#、A、B、C、Dの記号の計16種類の符号を、低群・高群の2つの音声周波数帯域の合成信号音で送信する方法である。別名「トーン信号」「プッシュ信号」とも呼ばれ、その信号音は人間の可聴域にあるため日本語では「ピ、ポ、パ」とも擬音語表記される。
DTMFマトリックス
1209 | 1336 | 1477 | 1633 | |
---|---|---|---|---|
697 | 1 | 2 | 3 | A |
770 | 4 | 5 | 6 | B |
852 | 7 | 8 | 9 | C |
941 | * | 0 | # | D |
回路図
スケッチで指定する、IO5 / IO4 番ピンに、圧電ブザーをつなげて下さい。
(ひとつにまとめても大丈夫だと思うのですが、なにぶん、実験が成功していないのでわかりません)
const int OUT_HIGH = 5;
const int OUT_LOW = 4;
スケッチ
シングルスレッドで、周期的な二つの信号の HIGH / LOW を切り替える部分が面倒でした。
const int freqL[4] = { 697, 770, 852, 941};
const int freqH[4] = {1209, 1336, 1477, 1633};
int freq[] = {
freqL[3], freqH[1], // 0
freqL[0], freqH[0], // 1
freqL[0], freqH[1], // 2
freqL[0], freqH[2], // 3
freqL[1], freqH[0], // 4
freqL[1], freqH[1], // 5
freqL[1], freqH[2], // 6
freqL[2], freqH[0], // 7
freqL[2], freqH[1], // 8
freqL[2], freqH[2], // 9
freqL[3], freqH[0], // *
freqL[3], freqH[2], // #
1, 2 // LED テスト用 (1Hz/2Hz)
};
const int OUT_HIGH = 5;
const int OUT_LOW = 4;
struct Status {
bool highlow;
long delay;
};
const int Pin[2] = {OUT_LOW, OUT_HIGH};
Status x[2];
int tel[40] = {0};
void setup() {
Serial.begin(115200);
Serial.println();
pinMode(Pin[0], OUTPUT);
pinMode(Pin[1], OUTPUT);
int N = sizeof(freq) / sizeof(freq[0]);
for (int i = 0; i < N; i++) {
freq[i] = 1000000 / freq[i] / 2; // micro sec
}
Serial.println("Input Telephone Number :");
}
void loop() {
//const int LP = 2; // 100 msec x 2回
const int LP = 40; // 100 msec x 40回 (LED)
static int lp = 0;
static int c = 0;
static int telsize = 0;
for (int i = 0; 0 < Serial.available(); ) {
char ch = Serial.read();
if (ch == '\n') {
c = 0;
break;
} else if ('0' <= ch && ch <= '9') {
tel[i++] = ch - '0';
telsize = i;
} else if ('X' == ch) {
// LEDテスト用
tel[i++] = 12;
telsize = i;
}
}
int telnum = tel[c];
int* numfreq = freq + telnum * 2;
// 初期化
if (lp == 0) {
for (int i = 0; i < 2; i++) {
digitalWrite(Pin[i], LOW);
x[i].highlow = false;
x[i].delay = 0;
}
Serial.print(c);
Serial.print(" ");
Serial.println(telnum);
}
if (telsize <= c) {
delay(400);
return;
}
Serial.print("period : ");
Serial.print(numfreq[0]);
Serial.print(" ");
Serial.print(numfreq[1]);
Serial.println(" usec");
if (LP < lp++) {
lp = 0;
c++;
}
// ループ1回は100msecにおさめる
long start = millis();
while ((millis() - start) < 100) {
// 次に反転するのは何秒後かを求める
long min = 10000;
for (int i = 0; i < 2; i++) {
long d = numfreq[i] - x[i].delay;
if (d < min) {
min = d;
}
}
// 求めた秒数待つ
delayMicroseconds(min);
// 半周期に達していれば反転する
for (int i = 0; i < 2; i++) {
x[i].delay += min;
if (x[i].delay == numfreq[i]) {
digitalWrite(Pin[i], x[i].highlow ? HIGH : LOW);
x[i].delay = 0;
x[i].highlow = !x[i].highlow;
}
}
}
}
稼働テスト
シリアルモニタから、電話番号を入力して下さい。1数字あたり 200msec の間、音が出ます。
電話の発話口に圧電ブザーを近づければ、電話がかかるはずです。(と思っている。)
1Hz / 2Hz で LED点灯 を試す場合は、スケッチの以下の部分を入れ替えて、シリアルモニタから「XXX」を入力して下さい。
const int LP = 2; // 100 msec x 2回
//const int LP = 40; // 100 msec x 40回 (LED)
まとめ
実験が成功しなくて、とても、残念です。
自宅の電話が、外部からのトーン信号を受け付けないのかもしれません。