黒電話のダイヤル検出

# 概要
黒電話のダイヤルがパルス計測で番号の判別ができることを知り、すぐに中古の黒電話を買いました。
黒電話のダイヤル検出の仕組みと開発したアプリについて説明します。

# ダイヤル検出の仕組み
## 開発環境
- 黒電話
- M5 Atom S3
- アプリ
  - Android Studio
  - Flutter 3.19.6

※GPIOを搭載したデバイス(M5Stackシリーズ、ラズパイなど)何でも良い。

## システム構成図
![ダイヤルを回せ_簡易構成図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/543183/0b3d45b8-04fd-1365-15e4-662b48588b3b.png)

## 黒電話の信号線の加工
- 電話線(2線)の被覆を剥がして、GPIOの信号へ入力できるようにジャンパー線にハンダします。

## 受話器状態とダイヤル検出
![ダイヤルを回せ_ダイヤル検出.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/543183/c2644ab4-b7f9-dcf6-4d5c-3b55fa666d5b.png)

- M5 Atom S3は電話の2線からHigh/Lowを監視します。(ActiveLow)
- 受話器を上げると信号がLowになります。
- ダイヤルを回します。
    - 番号に応じて出力されるパルス(約20msec)を計測します。
	- 1は1パルス、2は2パルス... 9はパルス
	- 0は例外で10パルス
- 受話器を置くとHighになります。
  - パルス計測と区別するため、50msec以上Highが継続した場合、受話器を置いたと判断します。

## 配線
M5 Atom S3では以下のように配線します。
赤い線 <-> GND
白い線 <-> G7

## ソースコード
受話器の上げた・置いたとダイヤルの検出を抜粋しています。  
M5 Atom S3で動作確認していますが、他のデバイスではヘッダーなどの変更が必要かもしれません。

```c++
#include <M5Unified.h>

// for measure
int inPin1 = 7;   // 未使用
int inPin2 = 8;   // INPUT

int countState = 0;    // パルスカウント状態
int totalValue = 0;    // パルス合計=数字

unsigned long timeStart = 0;
unsigned long timeEnd = 0;
unsigned long timeHigh = 0;
unsigned long timeLow = 0;
bool loopFlag = true;

void setup() {
  auto cfg = M5.config();
  M5.begin(cfg);
  Serial.begin(115200);
  pinMode(inPin1, INPUT_PULLUP);
  pinMode(inPin2, INPUT_PULLUP);
}

void loop() {
  // countState
  // 0: High待ち
  // 1: Low待ち
  if (digitalRead(inPin2) == LOW) {
    // 受話器を上げた
    countState = 0;
    totalValue = 0;
    timeHigh = 0;
    timeLow = millis();
    loopFlag = true;
    while(loopFlag) {
      int state = digitalRead(inPin2);
      switch (state) {
      case HIGH:
        switch (countState) {
        case 0:
          // パルス開始 or 受話器を置くとHighになるので検出開始
          countState = 1;
          timeHigh = millis();
          break;
        case 1:
          // 一定時間Highならば受話器を置いたと判断
          if ((millis() - timeHigh) > 100) {
            countState = 0;
            loopFlag = false;
          }
          break;
        }
        break;
      case LOW:
        switch (countState) {
        case 0:
          // Lowが続いたら数字確定
          if (timeHigh > 0 && (millis() - timeLow) > 100) {
            Serial.println(totalValue);
            timeLow = millis();

            // init
            totalValue = 0;
            timeHigh = 0;
          }
          break;
        case 1:
          timeLow = millis();
          // Highが一定時間継続していたら数字のパルスと判断
          if ((millis() - timeHigh) > 20) {
            totalValue++;
            if (totalValue == 10) {
              totalValue= 0;
            }
          }
          countState = 0;
          break;
        }
        break;
      }
    }
  }
}
```

# 開発したアプリ
Androidスマホへ検知したダイヤル番号を送信して、ダイヤルの正解率により相手の応答が変わります。
※実際に電話はかけません。

## システム構成図
![ダイヤルを回せ_システム構成図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/543183/cc6febd4-2de4-37d7-abf2-2d7f04248022.png)

## システム概要
- M5 Atom S3をWifiアクセスポイントとしてAndroidと接続します。
- M5 Atom S3とAndroidはUDPで通信します。
- Androidへ受話器を上げたことを送信します。
- ダイヤルを回します。
- Androidへ計測したダイヤル番号を送信します。
	- 受話器が奥までパルス計測とAndroidへの送信を繰り返します。
- 受話器を置くとHighになります。
  - Androidへ受話器を上げたことを送信します。

※ ダイヤルの検出は、受話器状態とダイヤル検出を参照してください。

## アプリ詳細
![ダイヤルを回せアプリ説明.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/543183/99e33aa4-40d8-26d3-b89c-268b095a1011.png)

1. 受話器を上げます。
1. 5桁の番号が表示されます。
1. ダイヤルを回します。(赤：回すダイヤル)
1. 回したダイヤルを判定します。(緑：OK、灰：NG)
1. 5桁の番号を回すまで3と4を繰り返します。
1. 5桁の番号を全て回したら完了です。
1. 電話のベルが鳴ります。
1. 相手の電話に出た音声が流れます。(全てOK、NGありで音声が変わります)

<iframe width="560" height="315" src="https://www.youtube.com/embed/aTDrjZ5E4ss?si=w4XH4zEapkCtAZfK" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

# 開発素材
ベル音  
[OtoLogic](https://otologic.jp/)

OK/NG音  
[効果音ラボ](https://soundeffect-lab.info/)

# 参考サイト
黒電話のダイヤル仕組み  
[【Arduino】エモっ！黒電話のダイヤルで数字入力！ダイヤルの仕組みが分かれば可能性が広がるよ！](https://www.youtube.com/watch?v=XJCixEV5cGs)