ハードウェア技術者のスキルアップ日誌

某家電メーカーの技術者がスキルアップのために勉強したことを記録するブログです

ラズパイでGPIO制御をする

今までソフトウェア中心の勉強でしたが、ハードウェアのスキルの幅を
広げるためにもラズベリーパイを使って電子回路を制御し、
いろんなものを作っていきたいと思っています。
そのためにラズパイでハードウェアを制御する方法を勉強していきます。
初回はGPIO制御です。LEDの点灯、消灯を実際にやってみたいと思います。

ラズパイのピンレイアウト

私が持っているラズパイはRaspberry Pi 3B+ですが、
ピンレイアウトはこのようになっています。
いくつかのピンはGPIOだけでなく、I2C, SPI, UARTとしても使用できます。

f:id:masashi_k:20190706222932j:plain  f:id:masashi_k:20190706222941p:plain

コマンドでも$gpio readallで確認できます。

GPIOポートの使い方

ラズパイのポートをGPIOとして使用する方法はいくつかあるようですが、
ディープラーニングPythonを使い慣れていることもあり、RPi.GPIOという
ライブラリを使ってみます。

まず、ライブラリのインストールです。

$ sudo apt-get install python-rpi.gpio


 Pythonスクリプト上でRPi.GPIOを使用するためにライブラリをインポートします。

import RPi.GPIO as GPIO

 

RPi.GPIOではポートの指定方法に2種類あります。
1つはピン番号1-40(上図のNo.の列の番号)で指定する方法 GPIO.BOARD
もう一つはGPIOの番号(上図のGPIO列の番号)で指定する方法 GPIO.BCM
どちらの表記で記載するかを以下のようにGPIO.setmode()で指定します。

GPIO.setmode(GPIO.BOARD)    # ボードのピン番号で指定
GPIO.setmode(GPIO.BCM)      # GPIO番号で指定

 

GPIOポートの入出力設定はGPIO.setup()で行います。
例えば、GPIO2を出力、GPIO3を入力ポートで使用する場合は以下のようになります。

GPIO.setmode(GPIO.BCM)      # GPIO番号で指定
GPIO.setup(2, GPIO.OUT)     # GPIO2:出力ポート
GPIO.setup(3, GPIO.IN)      # GPIO3:入力ポート

 

出力設定したポートのH/L出力を指定するにはGPIO.output()を使います。
GPIO2のポートをH/L制御する場合は以下のようになります。

GPIO.output(2, GPIO.HIGH)   # 出力H
GPIO.output(2, GPIO.LOW)    # 出力L

 

入力設定したポートの値を読み出すにはGPIO.input()を使います。
この関数の戻り値がポートの値となっています。

print GPIO.input(3)   # GPIO3の値を読み出し
# 結果:1 or 0

 

GPIO入力をトリガに割り込み処理を実行

プッシュスイッチなどポートの値の変化をトリガにして処理を行いたいケースが
あったので割り込み処理の作り方を調べてみました。

callback関数を定義し、ポートの値が変化したらcallback関数を呼び出す
add_event_detect()というメソッドを使用します。

def callback(gpio_pin):
    #関数内処理

GPIO.add_event_detect("ピン名", "トリガエッジ", 
                       callback=callback, bouncetime=300)

ピン名:ピン番号かGPIO名を指定 入力ポートに設定しておく必要あり
トリガエッジ:立ち上がりエッジ GPIO.RISING, 立下りエッジ GPIO.FALLING
                         両方 GPIO.BOTH
callback:コールバック関数の指定
bouncetime:チャタリング防止のため、指定時間の間次のイベントを呼び出さない
       単位msec

サンプル

最後に上記内容を使って作成したコードを上げておきます。
「スイッチを押すとLEDが点灯し、ラズパイに接続したUSBカメラで写真を撮る」
というものです。

回路図は以下の通り

f:id:masashi_k:20190706235403p:plain

38pinがトリガでL->Hとなったときに36pinがHとなり、LEDが点灯
同時にUSBカメラのシャッターを切る制御が動作するという仕組みです。
38pinはスイッチにつなぎたかったのですが、スイッチがなかったため、
40pinの出力結果を入力するようにしました。

# coding: utf-8
import RPi.GPIO as GPIO
import time
import subprocess

SHUTTER_PIN = 21      # 40pin
SHUTTER_TRIGGER = 20  # 38pin
LED_PIN = 26          # 36pin

def main():
    # cameraの設定
    cmd = 'fswebcam capture.jpg'

    GPIO.setmode(GPIO.BCM)
    GPIO.setup(SHUTTER_PIN,GPIO.OUT)
    GPIO.setup(SHUTTER_TRIGGER ,GPIO.IN)
    GPIO.setup(LED_PIN ,GPIO.OUT)
    # callback登録(GIO.RISING:立ち上がりエッジ検出、bouncetime:300ms)
    GPIO.add_event_detect(SHUTTER_PIN, GPIO.RISING, callback=callback, bouncetime=300)

    try:
        while(True):
            time.sleep(0.1)

    # Ctrl + Cで抜ける
    except KeyboardInterrupt:
        print("break")
        GPIO.output(TRIGGER_PIN, GPIO.LOW)  # LEDを消して終了
        GPIO.cleanup()

def callback(gpio_pin):
  print(gpio_pin)
  GPIO.output(LED_PIN , GPIO.HIGH)
  ret = subprocess.check_output(cmd, shell=True) # カメラ撮影

if __name__ == "__main__":
    main()

カメラの撮影はfswebcamというパッケージを使用しています。
もしインストールされていない場合は
  $ sudo apt-get install fswebcam
でインストールできます。

40pinのトリガーはこちらのスクリプトで発生させています。

import RPi.GPIO as GPIO

SHUTTER_PIN= 21 # 40pin

def main():
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(SHUTTER_PIN,GPIO.OUT)
    GPIO.output(SHUTTER_PIN, GPIO.HIGH)

if __name__ == "__main__":
    main()

 

このスクリプトで写真撮影はできていましたが、fswebcamを使うと
トリガー発生時から約1sec後にシャッターを切るようです。
リモートからのシャッター制御には使えないですね。
トリガーが発生したら即撮影するいい方法があったら教えてください。