日記とか、工作記録とか

自分に書けることを何でも書いてゆきます。作った物、買ったもの、コンピュータ系の話題が多くなるかもしれません。

Raspberry pi 2 + スイッチ = 温度を音声読み上げ

Raspberry pi 2と接続したブレッドボードに置いた温度センサー(MP9700)で、継続的に温度が観測できるところまで来ました。継続的な監視+グラフ作成もそれなりに面白いですが、少しすると次の何かをやりたくなるわけです。今回は、スイッチを押したら現在の気温を音声読み上げする機能をつけてみたくなりました。

すでに喋る機能は用意していますので、これらを組み合わせることにします。
過去の機能を組み合わせますので、あらましを挙げておきます。

Raspberry pi 2にスイッチからの入力を検知させたい

簡単に言うとGPIOとスイッチをつなぐわけですが、それだけでは駄目な事情がいろいろあり、他所様のサイトを参考にして配線をしました。こちらのページの下にある、プルダウン抵抗を使った配線にしました。GPIO 23(16番ピン)に接続しています。

ごく簡単ですが、スイッチの配線の写真をつけておきます。濃い青の線がGPIO 23につながっています。ブレッドボード上の青いラインのところがGround、赤いラインのところが3.3Vにつながっています。
f:id:WindVoice:20150311212756j:plain

ピン配列がわからなくなったら、いつものサイトを確認します。
Raspberry Pi GPIO Pinout - Pi 1, B+, Pi 2

pythonスクリプトでCallbackを使う

さて、スイッチが押されたことに、pythonスクリプト側ではどうやって気がついたら良いのか、という疑問が沸くわけですが、まず思いつくのは定期的にスイッチの状態を確かめに行く方法です。この場合は永久ループを作成して、1秒に1回など、スイッチの状態を確認することになります。

プログラムが直感的にわかりやすい(読みやすい)のが良いところですが、スイッチを短く押されるとタイミングにより検知できませんし、将来他の機能を追加したくなったとき、永久ループで処理が回っているために他のことを組み込みづらくなったりしそうです。

ここはコールバック関数を設定してイベント(スイッチが押された)を検知することにします。コールバック関数というのは、スイッチが押される前に、何か特定の条件(ハードウェア割り込みといいます)が発生したら、特定の処理(pythonで書かれた関数)を自動的に呼び出すよう、OSに依頼しておくことを言います。プログラムとしては、OSに依頼する部分(ここではGPIO.add_event_detect()とadd_event_callback()宣言)と、実際に呼ばれる関数(switch_on)を作ります。

pythonコードの抜粋は以下の通りです。実際には前回のセンサー読み取りプログラムの中に組み込んでいます。

# Switchが接続されているGPIOポートの番号
switch = 23

def switch_on(gpio_pin):
  # スイッチのチャタリングノイズを排除する
  global s_on
  dt = time.time()
  if dt < s_on + 1: return
  s_on = time.time()
  # 現在の気温を音声で報告する
  TEMPFILE = "/home/pi/openjtalk/tmp/temparature.txt"
  global ave_temp
  talk = open(TEMPFILE, "w")
  talk.write("はい、ただいまの気温は、%6.2f度です。" % ave_temp)
  talk.close()
  subprocess.Popen(["/home/pi/openjtalk/speak.py", TEMPFILE])

# スイッチを検知するためのハンドラを追加
GPIO.setup(switch, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.add_event_detect(switch, GPIO.FALLING)
GPIO.add_event_callback(switch, switch_on)

※この下に通常のセンサー読み取り処理ルーチンが続き、ave_tempにそのときの気温が保存されている

これを作ってみたとき、微妙に問題になったのが、スイッチのチャタリングノイズでした。
チャタリングノイズというのは、人間が一回だけスイッチを押したつもりでも、機械は何度かスイッチが押されたように誤検知してしまう現象のことです。スイッチを押した瞬間に電気的に不安定になることが原因らしいです。

少し試行錯誤した結果、一度スイッチが押されたら(=switch_on()が呼ばれたら)、続く1秒間は何もしないでswitch_on()から脱出するように、プログラム側で回避しています。回路側でチャタリングノイズを解決する方法もあるらしいのですが、そちらはよく調べていません。

間接的に呼び出されているspeak.pyスクリプトは、Open JTalkで効率よく喋らせようのところで作ったプログラムです。

さてさて、できあがったハードウェア+pythonスクリプトを使って実際に喋らせた動画がこちら。


Raspberry pi 2がただいまの気温を報告します - YouTube
まずまずのできばえとなっています。声が震えているように聞こえるのはなんでかなぁ…… 今後気になったら原因を調べてみたいと思います。

Raspberry Pi電子工作エントリーキット(Standard)

Raspberry Pi電子工作エントリーキット(Standard)