ESP8266 (ESP-WROOM-02) で圧電ブザーを制御して電話をかける

**本記事は稼働テストが成功していません**

## 概要
DTMF(**トーン信号**)を生成して、電話をかける。

## はじめに

私は、電子工作でどうしても試してみたいことがありました。それがこのDTMFです。

映画『名探偵コナン 戦慄の楽譜』(2008年) では、コナンともう一人の生声によって、110番に電話をかけるシーンが出てきます。「音痴で有名なコナンが特定周波数の声を出せるのか！？」と疑問に思いましたが、「まぁ、映画だしね。」と流していました。そもそも、声でそんなこと出来ないだろうし。

ところが、その後、朝日放送『探偵!ナイトスクープ』(2009年) では、コナンを検証する形で、音大生二人の生声によって時報サービスに電話をかけることに成功しました。この瞬間、鳥肌が立ちましたね。もう、何年も前の話ですが、鮮明に覚えています。

「WROOM02 で、是非、やってやろう！」というのが今回の企画です。

「このご時世、Twilio 等、専用の電話APIがあるじゃない」は、言わないで。ただ、やってみたいだけですから。

## 仕様

<dl>
<dt>DTMF</dt>
<dd>
DTMF（英: Dual-Tone Multi-Frequency）は、0から9までの数字と、*、#、A、B、C、Dの記号の計16種類の符号を、低群・高群の2つの音声周波数帯域の合成信号音で送信する方法である。別名「トーン信号」「プッシュ信号」とも呼ばれ、その信号音は人間の可聴域にあるため日本語では「ピ、ポ、パ」とも擬音語表記される。
</dd>
</dl>
https://ja.wikipedia.org/wiki/DTMF

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 を切り替える部分が面倒でした。

```cpp
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)
```

## まとめ

実験が成功しなくて、とても、残念です。
自宅の電話が、外部からのトーン信号を受け付けないのかもしれません。