libgpiodを使ったPythonによるGPIO入力イベント検出方法

Linuxの新しいGPIOインタフェエースlibgpiodを使ってPythonからGPIOの入出力を行う方法は以下の記事にまとめた。

GPIO入力を上記Pythonインタフェースを使って常時監視をする方法も無くはないのだが、CPUの演算リソースを消費してしまう。幸いlibgpiodにはGPIOの入力変化を通知する仕組みが備わっており、マイコンへ取り付けたプッシュスイッチのOn/Off検出なども割と簡単に出来ることが分かった。

今回はPythonコードによるGPIO入力イベント検出を試したので、備忘録的にまとめておく。

スポンサーリンク

コマンドラインツールを使ったGPIO監視

libgpiod付属コマンドのgpiomonを使えば非常に簡単に出来る。

gpiomon GPIOチップ名 ライン番号

例えば、ライン番号131のGPIO入力を監視する場合は、以下のような感じでイベント検出が発生するとメッセージが出力される。

$ gpiomon gpiochip0 131
event:  RISING EDGE offset: 131 timestamp: [1683338716.580825984]
event: FALLING EDGE offset: 131 timestamp: [1683338849.856297053]
event: FALLING EDGE offset: 131 timestamp: [1683338850.523325007]
event:  RISING EDGE offset: 131 timestamp: [1683338850.523435091]
event:  RISING EDGE offset: 131 timestamp: [1683338850.523561384]

PythonによるGPIO入力イベント検出のプログラミング情報源

gpiomonのPython版はlibgpiodのサンプルプログラムとして同梱されており、コードの最新版は下記リンクの場所に置かれている。

examples « python « bindings - libgpiod/libgpiod.git - C library and tools for interacting with the linux GPIO character device

ただgpiodは開発中のためバージョンによって結構差分がある。(2023年9月現在の最新版は2.02で、実際上記フォルダから既にgpiomon.pyが無くなっている…)

Pythonコードの入手先とgpiodバージョンの調べ方

システムにインストールされているlibgpiodと同じバージョンのサンプルコードを、適宜ダウンロードして使うと良い。

libgpiod/libgpiod.git - C library and tools for interacting with the linux GPIO character device

手元のシステムにインストールされているlibgpiodのバージョンを調べるには、Python対話モードで以下のコードを実行すれば簡単に分かる。

>>> import gpiod
>>> gpiod.version_string()
'1.6.3'

v1.6.3がインストールされていることが分かったので、libgpiod-1.6.3/bindings/python/examples/gpiomon.pyを動かしてみたところ、上記コマンドライン版と全く同じ動作をしてくれた。

以下、v1.6.3のサンプルプログラムを参考にコードを作ったことを断っておく。

立ち下がりエッジ検出するPythonコード

gpiod.Lineオブジェクトを初期化する。

import gpiod
chip = gpiod.Chip('gpiochip0', gpiod.Chip.OPEN_BY_NAME)
line = chip.get_line(131)

立ち下がりエッジ検出するためには、以下のようにrequestメソッドを呼び出す。

line.request(consumer='foo', type=gpiod.LINE_REQ_EV_FALLING_EDGE)

立ち下がりエッジのイベント待ちはevent_readメソッド呼び出しで出来る。

>>> line.event_read()
'FALLING EDGE (1683349130.228234324) source('gpiochip0:131 /unnamed/')'

複数GPIOの立ち下がりを同時監視する

get_linesを使って複数GPIOをまとめて初期化すると複数GPIOのイベントを同時監視することが出来る。サンプルコードのgpiomon.pyでまさに同じ事をやっており、参考にしつつライン番号131と133の2つのGPIOを監視するコードを作ってみたのが以下。

import gpiod

LINE_NO1 = 131
LINE_NO2 = 133

chip = gpiod.Chip('gpiochip0', gpiod.Chip.OPEN_BY_NAME)
lines = chip.get_lines([LINE_NO1, LINE_NO2])
lines.request(consumer='foo', type=gpiod.LINE_REQ_EV_FALLING_EDGE)

while True:
    ev_lines = lines.event_wait(sec=100)
    if ev_lines:
        for line in ev_lines:
            event = line.event_read()
            lineno = event.source.offset()
            if lineno == LINE_NO1:
                print('LINE_NO1のイベントを検出')
            elif lineno == LINE_NO2:
                print('LINE_NO2のイベントを検出')

ポーリングオブジェクトでGPIOイベント待ちする

ポーリングオブジェクトは以前に当ブログでも取り上げた。

ポーリングオブジェクトを使うことにより、ストリームなどの入力待ちを効率的に監視することが出来る。

GPIOイベント待ちのファイルディスクリプタを取得

gpio.Lineオブジェクトのevent_get_fdメソッドを使ってファイルディスクリプタを取得することにより、os.readメソッドでGPIOのイベント待ちが出来るようになる。

fd = line.event_get_fd()

上記のように取得したファイルディスクリプタを引数として、以下のコードのようにos.read関数を実行すると、プログラムがイベント待ちの状態となり、GPIOの立ち下がり発生で読み出しが完了する。

>>> import os
>>> os.read(fd, 16)
b'+\xeaXvP~\\\x17\x02\x00\x00\x00\x00\x00\x00\x00'

read関数の読み出し結果として何かが返ってきている。恐らくイベント発生時のタイムスタンプ等の情報だろう。

ポーリングオブジェクトを使ったGPIOイベント待ちコード例

以下のコードはGPIO立ち下がりエッジ検出待ちにポーリングオブジェクトを利用した物。ポーリングオブジェクトではregisterメソッドでイベント待ちをしたいIOを複数登録することが出来、例えばストリーム読み出しとGPIOイベント検出をミックスして監視したいような場合は有効だろう。

import gpiod
import os
import select

chip = gpiod.Chip('gpiochip0', gpiod.Chip.OPEN_BY_NAME)
line = chip.get_line(131)
line.request(consumer='foo', type=gpiod.LINE_REQ_EV_FALLING_EDGE)
fd = line.event_get_fd()

poll = select.poll()
poll.register(fd, select.POLLIN | select.POLLRDHUP)

while True:
    # イベント発生まで待つ
    rdy = poll.poll();

    if not rdy:
        break

    for fno, ev in rdy:
        if fno == fd and ev == select.POLLIN:
            ret = os.read(fd, 100)
            print('立ち下がりエッジ検出!!')

まとめ

libgpiodのGPIOインタフェースを使った、PythonプログラムによるGPIO入力変化イベント検出方法についてまとめた。ポーリングオブジェクトを使う事も出来、単にGPIOイベント待ちをするだけでなく、他のIOイベント待ちと組み合わせる事も出来そうだ。

ただlibgpiodは開発中であり、今回まとめた方法はv1.6.3用である事に注意。コーディングの作法はバージョン依存の模様。

コメント