黒電話をスマホの子機にしよう!という話の、第5回。
過去記事は、以下にあります。
黒電話をスマホの子機(Bluetoothヘッドセット)に!① - エンジニアらしき人のひとりごと
黒電話をスマホの子機(Bluetoothヘッドセット)に!② - エンジニアらしき人のひとりごと
黒電話をスマホの子機(Bluetoothヘッドセット)に!③ - エンジニアらしき人のひとりごと
黒電話をスマホの子機(Bluetoothヘッドセット)に!④ - エンジニアらしき人のひとりごと
で、今回は
そろそろプログラムを書いて、何番がダイヤルされたかを検出できるようにしよう。
使う道具を決める
プログラムからGPIOを操作する方法がいくつかあるので、まずはどれを使うか選定する。
- /sys/class/gpio/ へのファイル操作(④で使用した方法)
- WiringPiライブラリ
- RPi.GPIOライブラリ
- pigpioライブラリ
http://abyz.co.uk/rpi/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 でプログラムを書いてみた。
簡単なコード
import pigpio
import time
from enum import Enum
HIGH = 1
LOW = 0
PORT_DIALING = 22
PORT_DIAL_PULSE = 17
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):
gpio = pigpio.pi()
self.gpio = gpio
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
何番がダイヤルされたかと、受話器の上げ下げが分かるようになった!