Pythonでデバイスを制御しよう

第2回:プッシュボタンを扱う(1) Pythonでプッシュボタンを扱うには

ラズパイを電子機器の一部として使用する際、タクトスイッチなどのプッシュボタンの動きに合わせて処理を行うのは基本中の基本です。そこで『Pythonでデバイスを制御しよう』の第2回では、ラズパイでプッシュボタンの状態に合わせて処理する方法に注目します。
ハードウェアから見ると「GPIOピン ≫ プッシュボタン ≫ グランド」と配線するだけですが、ソフトウェアでは「ボタンが押されたことを示す電気信号をどのように受け取るのか?」「ボタンが押された際の処理の起動方法は?」なども考慮します。

プッシュボタンが押されたことを示す電気信号の受け取り方については、信号の入り口となるGPIOピンの仕組みを理解する必要があります。特にプルアップ/プルダウンはスイッチ動作で必須の知識なので必ず覚えておきましょう。
また、プッシュボタンなどの電子部品を扱う際には「チャタリング」と呼ばれる誤動作の原因への対処が必要です。本記事では「チャタリングとは何か?」といった基本的な部分から、ソフトウェアで行える対策についても解説します。

プッシュボタンが押された際の処理の起動方法には「ポーリング制御」「イベント駆動」の2種類の手法がよく用いられます。ポーリング制御はハードウェアに近い感覚の制御方法、イベント駆動はプログラミングらしい手法です。本記事ではそれぞれの特徴と具体的なPythonプログラムでの実装方法を紹介します。

最後に、実践的なコーディングに欠かせないPythonの例外処理についても解説します。「例外とは何か?」「積極的に例外を活用する方法」などを学びます。

 

本記事の構成

本記事では次のように解説を進めます。

  • パート1 Pythonでプッシュボタンを扱う
  • パート2 誤動作の原因となるチャタリングを防止する
  • パート3 ポーリング制御でプッシュボタンに反応する(制御手法)
  • パート4 イベント駆動でプッシュボタンに反応する(制御手法)
  • パート5 プログラムをストップさせる例外を捕捉して処理する(例外処理)
  • パート6 「raise Exception」で積極的に例外を利用する(例外処理)

初回のパート1は「ボタンが押されたことを示す電気信号をどのように受け取るのか?」についてです。

パート2ではプッシュボタンを扱う際に誤動作の原因となるチャタリングについて説明します。

パート3、パート4は「ボタンが押された際の処理の起動方法」です。制御では「ポーリング制御」「イベント駆動」の2種類の手法がよく使われます。
パート3ではポーリング制御を紹介します。イベント駆動の詳細はパート4です。

パート5、パート6ではPythonの例外処理について説明します。
例外はプログラムを停止させてしまう厄介者のように考えられていますがパート5では適切に処理する方法を、パート6では例外を積極的に扱って効率良くコーディングする方法を紹介します。

control-device-with-python-vol2-01-01

 

パート1 Pythonでプッシュボタンを扱うには

ソフトウェアから見るとGPIOピンが外部からの信号の入り口となります。そこでGPIOピンが信号を扱う仕組みと、それをソフトウェアがどのように受け取るかについて説明します。

目次

  1. 必要な機器
  2. ハードウェアの準備
  3. ソフトウェアの準備
  4. GPIOピンの構造とプルアップ/プルダウン
    1. 4.1. プルアップ/プルダウンとは?
    2. 4.2. ラズパイをプルアップするには?
  5. RPi.GPIOを使ってボタンの入力を検出
  6. プッシュボタンの動きに合わせて処理を起動
  7. まとめ

 

必要な機器

本記事で紹介するプログラムの動作を検証したラズパイの構成は以下の通りです。

control-device-with-python-vol2-01-02

システム構成(ブロック)

 

項目 仕様
ラズパイ Raspberry Pi 4B
microSD 32Gバイト
OS Raspberry Pi OS 2021-01-11Raspbian GNU/Linux 10 (buster)
Python 3.9.1
Wi-Fi 固定IPアドレス(192.168.11.51)
USBメモリ 32Gバイト ext4
GPIO5 タクトスイッチ(Alps SKHHAJ
GPIO6 タクトスイッチ(Alps SKHHAJ

ラズパイの操作はSSH接続したターミナルで行います。
今回、ラズパイには記憶領域を増やすために32GバイトのUSBメモリを使用しています。解説で扱うアプリケーションは大きな容量を必要としないため、USBメモリはなくても問題ありません。
GPIO5とGPIO6にタクトスイッチを接続します。タクトスイッチはどのようなものでも使用できます。

 

ハードウェアの準備

以下が本記事で使用する回路です。

control-device-with-python-vol2-01-03

本記事で使用する回路(回路図)

 

タクトスイッチを
「GPIO5 ≫ タクトスイッチ ≫ GND」
となるように配線します。GPIO6も同様です。
GPIO6に接続したタクトスイッチはパート4で使用します。それまでは1個のタクトスイッチでも検証を進められます。
配線はブレッドボードを使用すると便利です。

control-device-with-python-vol2-01-04

本記事で使用する回路(外観)

 

ソフトウェアの準備

ラズパイでGPIOピンを扱うには、OSにデバイスドライバをインストールします。通常通りラズパイをセットアップすれば、このデバイスドライバもインストールされます。

プログラムからGPIOピンへのアクセスは、/sys/class/gpio以下に展開されるファイルへのファイルディスクリプタを通して行います。通常のファイルの読み書きと大きな違いはありませんが少々煩雑かもしれません。
そこで、Pythonにはプログラマに代わってこのファイルの読み書きを行うライブラリ「RPi.GPIO」が用意されています。本記事でもGPIOピンのアクセスにこれを使用します。
RPi.GPIOライブラリはインストールが必要です。インストールするにはターミナルで以下のコマンドを実行します。

pip install RPi.GPIO

 

GPIOピンの構造とプルアップ/プルダウン

タクトスイッチの配線とRPi.GPIOライブラリのインストールが終わればPythonのプログラムからGPIOピンを制御できます。
ここでGPIOピンの制御に最低限必要なプルアップ/プルダウンについて再確認しておきます。

プルアップ/プルダウンとは?

プルアップ/プルダウンは正確には「プルアップ抵抗/プルダウン抵抗」と言います。名前の通り電子回路中に挿入する抵抗です。
このプルアップ抵抗/プルダウン抵抗は特定の目的で電子回路中に挿入する抵抗を指す名称です。
「特定の目的とは何か?」ですが、それを説明する前に前述の『ハードウェアの準備』で掲載した図、『本記事で使用する回路(回路図)』を参照してください。
この回路は
「タクトスイッチ(プッシュボタン)を押す/離すとGPIOピンの電圧が変化し、それをプログラムで検出する」
ために使用します。
回路中の「GPIO5 ≫ タクトスイッチ ≫ GND」の部分に着目したのが以下の図です。ポイントが分かりやすくなるように記号を変えてありますが、同じ回路です。

control-device-with-python-vol2-01-05

GPIO5につながるタクトスイッチ

 

GPIO5はタクトスイッチの接点が閉じるとGND(グランド)に接続されるのでピンの電圧は0Vです。では接点が開いている時の電圧は何ボルトになるでしょう?
答えは「不定」です。
接点が開いている時はピンがどこにも接続されていない状態となります。この状態を「入力ピンが浮いている」と言い、ピンにかかる電圧が不安定であることを意味します。どこにも接続されていないのでほとんどの場合は0Vになりますが、ノイズなどの影響で3.3V以上の電圧が加わるなど不安定な状態になります。こういった状態が「不定」であり、プログラムの誤動作を引き起こします。
そこで
「GPIO5 ≫ タクトスイッチ ≫ GND」
と接続する場合、GPIO5はさらに抵抗を通して電源に接続する必要があります。

control-device-with-python-vol2-01-06

GPIOは抵抗を通して電源に接続

 

これによって、タクトスイッチが押されていない状態ではGPIO5には電源電圧(3.3V)が確実にかかるようになります。一方、タクトスイッチを押すとGPIO5はGNDと等電位になるので0Vです。
このような目的で使用する抵抗を「プルアップ抵抗」と呼びます。

「プルダウン抵抗」は「プルアップ抵抗」と反対です。
タクトスイッチを
「電源 ≫ タクトスイッチ ≫ GPIO5」
と接続した場合、接点が閉じると電源に接続されるのでピンの電圧は3.3Vとなりますが、接点が開いている時は不定です。

control-device-with-python-vol2-01-07

タクトスイッチを電源に接続

 

そこで接点が開いている時はGPIO5が確実にグランドに接続されるようにします。そのために抵抗を通してGPIO5をグランドに接続します。
この抵抗が「プルダウン抵抗」です。

control-device-with-python-vol2-01-08

GPIOは抵抗を通してグランドに接続

 

ラズパイをプルアップするには?

本記事で使用する回路はタクトスイッチを
「GPIO5 ≫ タクトスイッチ ≫ GND」
と接続するので、正しく動作させるためにGPIO5は抵抗を通して電源に接続する(プルアップ抵抗)必要があります。
ところが前述の『本記事で使用する回路(回路図)』にはそれがありません。
これは
「ラズパイにはプルアップ抵抗/プルダウン抵抗が内蔵されている」
ため、回路には抵抗を使用する必要がないからです。

control-device-with-python-vol2-01-09

ラズパイはプルアップ/プルダウンを内蔵

 

このラズパイに内蔵するプルアップ抵抗/プルダウン抵抗に関してはソフトウェアで使用する/しないが決められます。「プルアップ抵抗を使用しない」と設定した場合、プルアップ抵抗の外付けが必須になります。

 

RPi.GPIOを使ってボタンの入力を検出

ハードウェア・ソフトウェアの準備ができたらRPi.GPIOライブラリを使ってGPIOピンに接続したタクトスイッチの状態をPythonで読み取ります。
手順は

  1. RPi.GPIOライブラリをインポート
  2. ピン番号の指定方法を設定
  3. 対象のピンの動作を指定
  4. 対象のピンを読み取る
  5. (終了時)ピンの設定をリセット

です。

以下にターミナル上からPythonを対話モードで起動し、直接コマンドを入力した様子を示します。

>>> import RPi.GPIO as GPIO
>>>
>>> GPIO.setmode(GPIO.BCM)
>>>
>>> GPIO.setup(5, GPIO.IN, pull_up_down=GPIO.GPIO.PUD_UP)
>>>
>>> GPIO.input(5)
0
>>>
>>> GPIO.cleanup()
>>>
>>> exit()

「import RPi.GPIO as GPIO」が手順「1.」のライブラリをインポートです。
これ以降、「GPIO.関数()」と記述してGPIOピンを操作します。

手順「2.」の「ピン番号の指定方法」は関数「GPIO.setmode()」の引数で指定します。引数にはGPIO.BOARD、GPIO.BCMのいずれかが指定できます。

setmodeの引数 説明
GPIO.BOARD 1〜40までのGPIOコネクタのピン番号を指定
GPIO.BCM GPIOnのnを指定

GPIO.BOARDはプログラム内でピン番号を指定する際に、ラズパイ上のGPIOコネクタの物理的なピン番号を指定する方法です。GPIOコネクタにはピンが40本存在し、例えばGPIO5を指定する場合は29番となります。
一方、GPIO.BCMはGPIOピンの名称に使われる番号を指定します。例えばGPIO5に対する操作は5番、GPIO6に対する操作は6番です。
本記事で紹介するプログラムは全てGPIO.BCMでピン番号を指定します。

手順「3.」ではGPIO.setup()関数で操作の対象となるピン(GPIO5)の動作を指定します。引数は

  1. ピン番号
  2. 入力(出力として使用する場合はGPIO.OUT)
  3. ラズパイに内蔵するプルアップを使用(プルダウンはGPIO.PUD_DOWN)

です。

動作の指定まで終わったら、好きなタイミングにGPIO.input()関数でGPIOピンの状態を読み取れます。関数の引数は読み取りたいGPIOピンの番号です。今回の例ではこの関数は0(LOW)を返しました。これはタクトスイッチを押した状態です。
Pythonの対話モードを終了する(exit()関数)の前にはGPIO.cleanup()関数を必ず呼び出してください。GPIO.cleanup()関数の呼び出しを忘れると、次回使用時にエラーが発生する場合があります。
不必要なトラブルを避けるため、GPIO.cleanup()に限らず、プログラムの実行中に行った設定は、終了時にリセットする癖をつけましょう。

パート2からは、上記の操作をPythonのプログラム内に組み込み、プッシュボタンの動きに合わせて処理を起動します。処理の起動に使用する手法は「ポーリング制御」と「イベント駆動」です。

 

プッシュボタンの動きに合わせて処理を起動

プッシュボタンが押されたことを示す電気信号の受け取り方について理解した後は「どのように必要な処理を起動するか?」について考えます。
制御でよく用いられる処理の起動方法は「ポーリング制御」と「イベント駆動」の2種類です。

ポーリング制御とは、無限ループの中で常にシステムの状況を監視し、その状況に合わせた処理を行う制御です。
シンプルで応用しやすい仕組みなのでハードウェアの制御を行うマイコンなどのプログラムに多く使われます。

control-device-with-python-vol2-01-10

ポーリング制御とは

 

一方のイベント駆動は、「ボタンが押された」のようにシステムの状態が変化した時、登録した関数を呼び出して処理を行う制御です。
OSが乗っているシステムでは、ほとんどこの手法でプログラミングされます。

control-device-with-python-vol2-01-11

イベント駆動とは

 

ポーリング制御とイベント駆動については、それぞれパート3、パート4で詳細に解説します。

 

まとめ

タクトスイッチを始めとするプッシュボタンは、操作する人間の意図をプログラムが把握できる手軽で便利な入力ツールです。
ハードウェアから見ると「GPIOピン ≫ プッシュボタン ≫ グランド」と配線するだけですが、適切に制御するには多くの知識が必要です。
パート1では「ボタンが押されたことを示す電気信号をどのように受け取るのか?」について解説し、RPi.GPIOライブラリを使用してPythonの対話モードでGPIOピンに接続したプッシュボタンの状態を読み取りました。
パート2からは実際にPythonのプログラムでGPIOピンの状態を取得して必要な処理を起動します。また誤動作の原因となるチャタリングや例外処理などについても随時説明していきます。

 

今回の連載の流れ

プッシュボタンを扱う(1) Pythonでプッシュボタンを扱うには(今回)
プッシュボタンを扱う(2) 誤動作の原因となるチャタリングを防止する
プッシュボタンを扱う(3) ポーリング制御でプッシュボタンに反応する
プッシュボタンを扱う(4) イベント駆動でプッシュボタンに反応する
プッシュボタンを扱う(5) プログラムをストップさせる例外を捕捉して処理する
プッシュボタンを扱う(6) 「raise Exception」で積極的に例外を利用する

エレクトロニクスやメカトロニクスを愛するみなさんに、深く愛されるサイトを目指してDevice Plusを運営中。

https://deviceplus.jp


Arduino互換機(M5Stick-C)とセンサを使った衝突回避機能付きリモコンカーをつくろう!