2022年01月20日 更新

Raspberry Piでスイッチ入力

どうも、クラゲです。
ラズパイにタクトスイッチを付けて基本的なプログラミングを行います。主に、GPIOを常時チェックして処理する方法とGPIOに変化があったときのみ処理する方法の2通りあります。

この動画はLチカとの組合せで、スイッチを押したら5回だけ点滅しています。こちらのコードも最後に紹介します。

目次

[TOC]

ラズパイ

今回はRaspberry Pi 4 B を使用しました。別のラズパイでも問題ありません。

電子部品

ラズパイ以外に必要な部品は以下3点です。

色や大きさなどは好みで構いません。

回路結線

以下のように接続してください。
タクトスイッチは縦置きか横置きかで向きがありますが、図のように挿さる向きであればOKです。

ラズパイ側は上から3番目のピンと6番目のピンです。上側がGroundで下側がGPIO18という端子です。

プログラム1(入力値を常時チェックして処理)

import time

#GPIOの初期設定
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)

#GPIO18を入力端子設定
GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_UP)

while True:
    #スイッチ状態取得
    sw_status = GPIO.input(18)

    #画面出力
    if sw_status == 0:
        print('スイッチON!')
    else:
        print('')

    #少し待つ
    time.sleep(0.3)        

実行結果は以下のようになります。
動画ではディスプレイの代わりにスマホにVNCを導入していますが、皆さんはPCで問題ありません。

スイッチのオン/オフに関わらずPythonの表示は0.3秒毎に流れます。スイッチオンのときにスイッチON!と表示されています。短押し、長押しによって表示数も異なります。

解説

GPIO入力のポイントだけ説明します。

GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_UP)

今回接続しているGPIO18GPIO.IN、つまり入力として設定しています。
pull_up_down=GPIO.PUD_UPという引数で「プルアップ」という設定を行っています。 今回の回路ではスイッチが押されていないときのGPIOの入力値は1で、スイッチが押されたときの値は0を期待しています。
実際のところ、スイッチが押されていない状態のとき、GPIO端子は不定な状態となり、何かのノイズで1がたまに入力されてしまう場合があります。そこで入力端子に置いては「プルアップ」もしくは「プルダウン」という処理が必要です。
今回のようにスイッチを押すことでGPIO端子がGroundと導通する回路の場合は「プルアップ」を設定します。


sw_status = GPIO.input(18)

変数sw_statusGPIO18の値を入力しています。
スイッチが押されていないときのGPIOの入力値は1で、スイッチが押されたときの値は0です。


time.sleep(0.3) 

表示後にちょっとだけ一時停止しないと表示が速く流れ過ぎてしまうため、0.3秒だけスリープさせています。

プログラム2(入力値に変化があった場合のみ処理)

import time

#GPIOの初期設定
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)

#GPIO18を入力端子設定
GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_UP)

while True:
    #スイッチ押下待ち
    GPIO.wait_for_edge(18, GPIO.FALLING)

    #画面出力
    print('スイッチON!')

    #チャタリング対策
    time.sleep(0.3)

実行結果は以下のようになります。

先程と異なり、スイッチを押したときのみ表示されます。短押しも長押しも関係なく処理されています。

解説

先程からの変化点のみ解説します。

GPIO.wait_for_edge(18, GPIO.FALLING)

この記述で、GPIO18の値に変化が起きるまでここで待つことになります。
変化にも種類があります。主に立ち上がりエッジGPIO.RISINGなのか立ち下がりエッジGPIO.FALLINGなのかを指定します。
これはスイッチを押したときか、スイッチ押しを離したときのどちらかを検知したいかで変わってきます。今回はスイッチを押したときを検知させています。
スイッチを押していないときが1で、スイッチを押している間が0ですので、1 → 0の変化、つまりGPIO.FALLINGを指定するということになります。


time.sleep(0.3) 

先程と違って今回はずっと待つのでスリープは不要です。しかし、このスリープを外して実験してみると分かりますが、1回押しているだけなのに数回押されたかのような結果になる場合があります。このような現象を「チャタリング」と呼んでいます。タクトスイッチによる機械的な弾性振動が原因です。
いくつか対策がありますが、最も簡単なのはプログラミングでスリープを入れることです。今回は0.3秒を設定してます。スリープ時間を長くすればする程、チャタリングをより確実に防げる一方、連続押しの応答がどんどん悪くなるという事態になりますので注意が必要です。

プログラム3(LEDを使った応用例)

冒頭の動画のコードです。
Raspberry Pi で Lチカのときと同じようにLEDと抵抗も必要になります。

import time

#GPIOの初期設定
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)

#GPIO17を出力端子設定
GPIO.setup(17, GPIO.OUT)

#GPIO18を入力端子設定
GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_UP)

while True:
    #消灯
    GPIO.output(17, 0)

    #スイッチ押下待ち
    GPIO.wait_for_edge(18, GPIO.FALLING)

    counter = 1
    while True:
        #点灯
        GPIO.output(17, 1)

        #0.2秒スリープ
        time.sleep(0.2)

        #消灯
        GPIO.output(17, 0)

        #0.2秒スリープ(チャタリング対策も兼ねる)
        time.sleep(0.2)

        if counter >= 5:
            break

        counter = counter + 1

今回のプログラムでは点滅してる間はスイッチ入力は受け付けていません。何かのタスクを行っている最中もスイッチ入力を行いたい場合は「割り込み」という処理が必要になりますので調べてみて下さい。

以上、Raspberry Piでスイッチ入力でした!