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のサンプルプログラムとして同梱されており、コードの最新版は下記リンクの場所に置かれている。
ただgpiodは開発中のためバージョンによって結構差分がある。(2023年9月現在の最新版は2.02で、実際上記フォルダから既にgpiomon.pyが無くなっている…)
Pythonコードの入手先とgpiodバージョンの調べ方
システムにインストールされているlibgpiodと同じバージョンのサンプルコードを、適宜ダウンロードして使うと良い。
手元のシステムにインストールされている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用である事に注意。コーディングの作法はバージョン依存の模様。
コメント