黒電話をスマホの子機にしよう!という話の、第5回。
過去記事は、以下にあります。
黒電話をスマホの子機(Bluetoothヘッドセット)に!① - エンジニアらしき人のひとりごと
黒電話をスマホの子機(Bluetoothヘッドセット)に!② - エンジニアらしき人のひとりごと
黒電話をスマホの子機(Bluetoothヘッドセット)に!③ - エンジニアらしき人のひとりごと
黒電話をスマホの子機(Bluetoothヘッドセット)に!④ - エンジニアらしき人のひとりごと
前回までのあらすじ
ラズパイ(Raspberry Pi)を使って、黒電話をスマホの子機(Bluetoothヘッドセット)にしようと画策中。
スマホ⇔ラズパイ間をBluetoothで接続し、ラズパイからスマホの発着信操作ができた(②と③)。
黒電話のダイヤルは、ON/OFFを繰り返すスイッチのようなもので、なんとかラズパイに繋いでON/OFFが分かるようになった。
で、今回は
そろそろプログラムを書いて、何番がダイヤルされたかを検出できるようにしよう。
使う道具を決める
プログラムからGPIOを操作する方法がいくつかあるので、まずはどれを使うか選定する。
- /sys/class/gpio/ へのファイル操作(④で使用した方法)
- WiringPiライブラリ
- RPi.GPIOライブラリ
- pigpioライブラリ
いろいろあるが、今回ではpigpioを使うことにした。pigpio本体はLinuxのデーモンとして動いていて、高速&高精度のGPIO操作が可能。そして、Pythonのアプリから操作するためのインタフェースとしてのライブラリが提供されている。
これを選んだ最大の理由は、特徴に以下の項目があったから。
>callbacks on GPIO 0-31 level change (time accurate to a few μs)
これの何が良いかというと、
何番がダイヤルされたかをカウントするために必要な、パルスの立ち上がり(0→1になる)タイミングを教えてくれる、ということ。
もしこの機能がなかったら、立ち上がりの検出処理を自分で実装する必要が出てくるので、ちょっと面倒だし、なんだかスマートな方法じゃない。
どういう処理かというと、定期的にGPIOポートの値をチェックして「0, 0, 0, 0, 0, 0, 0, 0, 1, あ!いま1になった!」というのを見つける、いわゆるポーリングの処理。
これをライブラリ側でやってくれるというのは、すごくスマート。プログラムが簡単になる。
しかも、数マイクロ秒の誤差で、ライブラリからアプリに通知してくれる。
おぉすごい!
ということで、Python + pigpio でプログラムを書いてみた。
簡単なコード
#!/usr/bin/python3 import pigpio import time from enum import Enum HIGH = 1 LOW = 0 # D3ケーブルの接続ポート PORT_DIALING = 22 # D1ケーブルの接続ポート PORT_DIAL_PULSE = 17 # H1ケーブルの接続ポート PORT_HOOK = 24 class KurodenGpio: Status = Enum("Status", "IDLE WAIT_PULSE PULSE_HIGH PULSE_LOW") gpio = None status = Status.IDLE # パルスの立ち上がりをカウントする dial_count = 0 def __init__(self): # pigpioを生成 gpio = pigpio.pi() self.gpio = gpio # 各ポートをINPUTに gpio.set_mode(PORT_DIAL_PULSE, pigpio.INPUT) gpio.set_mode(PORT_DIALING, pigpio.INPUT) gpio.set_mode(PORT_HOOK, pigpio.INPUT) # ノイズ対策 gpio.set_glitch_filter(PORT_DIAL_PULSE, 1000) gpio.set_glitch_filter(PORT_DIALING, 1000) gpio.set_glitch_filter(PORT_HOOK, 1000) # パルスの立ち上がり/立ち下りを契機に、関数を呼び出してもらう gpio.callback(PORT_DIAL_PULSE, pigpio.EITHER_EDGE, self.callback_gpio_d1) gpio.callback(PORT_DIALING, pigpio.EITHER_EDGE, self.callback_gpio_d3) gpio.callback(PORT_HOOK, pigpio.EITHER_EDGE, self.callback_gpio_hook) def __del__(self): self.gpio.stop() def callback_gpio_hook(self, gpio, level, tick): if level == 0: print("受話器取った") else: print("受話器置いた") def callback_gpio_d3(self, gpio, level, tick): if level == 0: print("ダイヤル開始") if self.status == self.Status.IDLE: self.status = self.Status.WAIT_PULSE self.dial_count = 0 else: print("ダイヤル終了") if self.status == self.Status.PULSE_LOW or self.status == self.Status.WAIT_PULSE: self.status = self.Status.IDLE if self.dial_count != 0: if self.dial_count == 10: self.dial_count = 0 print("** Dial:", self.dial_count) def callback_gpio_d1(self, gpio, level, tick): if level == 1 and (self.status == self.Status.WAIT_PULSE or self.status == self.Status.PULSE_LOW): self.status = self.Status.PULSE_HIGH elif level == 0 and self.status == self.Status.PULSE_HIGH: self.status = self.Status.PULSE_LOW self.dial_count += 1 ##### if __name__ == '__main__': print("** START **") gpio = KurodenGpio() while True: time.sleep(1)
状態管理はいらんかな?とも思ったが、つけてみた。
このコードをgpiotest.pyとして保存し、以下のように実行している。
$ python3 gpiotest.py
何番がダイヤルされたかと、受話器の上げ下げが分かるようになった!