libgpiodの使い方


ずっと昔からWiringPiを使ってRaspberryPiのGPIOを制御していました。非常に簡単で便利なライブラリでしたか、2019/08/06のニュースで非推奨になることがアナウンスされました。
代わりの方法を探したところ/sys/class/gpioにあるファイルを操作することで制御することができるようですがこちらもlinux 4.8から非推奨になっていたようです。
じゃあどうすればいいんだ〜となりましたがきちんと公式ページlibgpiodを使うように書いてありました。

しかし公式ページに載っていたリンク先では簡単な説明とソースコードだけでは初心者にとってはなかなか分かりにくいと思うのでここに簡単にまとめたいと思います。
ちなみにI2CやSPIはここでは扱いません(libgpiodは多分非対応)のでご留意ください。
(以下の内容は手元に転がっていたRaspberryPi2でテストしましたが他のモデルでも大差はないと思います。)

TL;DR

libgpiodのライブラリと開発環境、ドキュメントをインストールすれば楽に開発できます。ただドキュメントも最初読んだだけでは分かりにくいと思うので詳しくは次の項目以降に記載します。ここではライブラリなどのインストールとドキュメントの場所を記載します。

# ライブラリなどをインストール
sudo apt install libgpiod2 libgpiod-dev libgpiod-doc

# ドキュメントは下記のディレクトリにhtml形式で保存されています。
cd /usr/share/doc/libgpiod-dev/html

# ブラウザで下記のファイルを開くとドキュメントが確認できます。
# /usr/share/doc/libgpiod-dev/html/index.html

# gpiodをインストールするとlibgpiodを使用したツールもインストールできます
# ツールのソースコードはlibgpiodのソースコードについているので参考になります。
# ただこのツールでGPIOの出力を設定することはできないようです。(後述)
sudo apt install gpiod

サンプルコードはとりあえずコードを書いてみるを参照してください。

必要なパッケージをインストール

まずは必要なパッケージをインストールしていきます。

sudo apt install libgpiod2 libgpiod-dev libgpiod-doc

libgpiod2は共有ライブラリ、
libgpiod-devは開発に使用するファイル(静的ライブラリとヘッダーファイル)
libgpiod-docはライブラリのドキュメントです。

ちなみに

sudo apt install gpiod

gpiodをインストールするとlibgpiodを使用したツールもインストールできます。
ツールのソースコードはlibgpiodのソースコードについているので参考になります。
ただこのツールでRaspberryPiのGPIOの出力を設定することはできないようです。(後述)

とりあえずコードを書いてみる

とりあえず何か簡単なコードを動かしてみたいという人は下記のコードを実行してみてください。
まずは下記のファイルを作成します。
GPIOはここを参考に好きなピンを選んでください。(サンプルでは20番を入力、21番を出力として使用しています。)
ピン番号は物理的なピンの配置ではないので注意してください。

test.c
#include<stdio.h>
#include<unistd.h>
#include<gpiod.h>

int main() {
  const char *appname = "gpiotest";

  struct gpiod_chip *gchip;
  struct gpiod_line *glinein, *glineout;
  int value;

  // GPIOデバイスを開く
  if ((gchip=gpiod_chip_open_lookup("")) == NULL) {
    perror("gpiod_chip_open_lookup");
    return 1;
  }

  // GPIO入力
  // GPIOのピンのハンドラを取得する
  // ここでは20番ピンを使用
  if ((glinein=gpiod_chip_get_line(gchip, 20)) == NULL) {
    perror("gpiod_chip_get_line");
    return 1;
  }

  // GPIOを入力モードに設定する
  if (gpiod_line_request_input(glinein, appname) != 0) {
    perror("gpiod_line_request_input");
  }

  // GPIOの値を取得する
  if ((value=gpiod_line_get_value(glinein)) == -1) {
    perror("gpiod_line_get_value");
  }
  printf("GPIO20=%d\n", value);

  // GPIO出力
  // GPIOのピンのハンドラを取得する
  // ここでは21番ピンを使用
  if ((glineout=gpiod_chip_get_line(gchip, 21)) == NULL) {
    perror("gpiod_chip_get_line");
    return 1;
  }

  // GPIOを出力モードに設定する
  if (gpiod_line_request_output(glineout, appname, 0) != 0) {
    perror("gpiod_line_request_output");
  }

  // GPIOの値を1に設定する
  if ((value=gpiod_line_set_value(glineout, 1)) == -1) {
    perror("gpiod_line_set_value");
  }

  // 出力を維持するためちょっと待つ。
  sleep(5);

  // GPIOの値を0に設定する
  if ((value=gpiod_line_set_value(glineout, 0)) == -1) {
    perror("gpiod_line_set_value");
  }

  // 出力を維持するためちょっと待つ。
  sleep(5);

  // GPIOデバイスを閉じる
  gpiod_chip_close(gchip);
  return 0;
}

ポイントとしては
1. gpioデバイスを開く(gpiod_chip_open_lookup)
2. gpioのピンのハンドラーを開く(gpiod_chip_get_line)
3. 必要に応じて入出力を設定する(gpiod_line_request_input/output)
4. ピンの値を取得、設定する(gpiod_line_get/set_value)
の4点だと思います。

ドキュメントを見てみる

簡単な入出力でライブラリを使うだけなら上記のコードでもわかると思いますが複数のピンを同時に制御したり入力の変化を検知したいという人はドキュメントも少し見てみましょう。

libgpiod-docがインストールされていれば/usr/share/doc/libgpiod-dev/htmlにhtml形式でドキュメントが入っています。
その中にあるindex.htmlをブラウザなどで開いてください。
(chromium-browser /usr/share/doc/libgpiod-dev/html/index.htmlで開けると思います)

メインページが出てくると思いますが上の方にある「Modules」をクリックしてください。
リストが表示されますが、この中から重要そうなものを抜粋して紹介したいと思います。
(ちなみにここで説明するlibgpiodはバージョンが1.2のものです。)

High-level API
GPIOの操作を簡単に行うことができます。(ただし出力については後述を参照してください)

GPIO chip operations
GPIOを制御しているデバイスを操作することができます。GPIOのピンの一覧や各ピンのハンドラーも取得できます。

GPIO line operations
GPIOの個々のピンの制御を行います。サブメニューになっています。

Operating on multiple lines
複数のGPIOピンを制御するために必要な構造体などを説明しています。

Line info
GPIOの個々のピンの情報を取得します。入出力の方向や使用中かが分かります。

Line requests
GPIOの個々のピンの属性等を設定します。入出力の方向や入力変化の検知を設定できます。

Reading & setting line values
GPIOの状態を取得、設定します。一番重要な項目です。

Line events handling
GPIOの入力変化の検知について設定します。

とりあえず上記を押さえて読んでいけば読みやすくなると思います。

RaspberryPiでのGPIO出力について

libgpiodを試していた時に出力がうまくできませんでした。
調べたところRaspberryPiではgpioデバイスをクローズしてしまうと初期値に戻ってしまい設定が残らないようです。
設定後にすぐに終了してしまうgpiosetツールや設定後すぐにデバイスを閉じてしまうHigh Level APIでは思った動きにならない可能性があります。
GPIOの出力を設定する際はHigh Level APIを使わず上記のコードのように自分で設定するようにした方がいいと思います。

あとがき

GPIOの制御を簡単にしようと思ったら色々ハマってしまい、結果的にドキュメントを隅から隅まで(場合によってはソースコードも辿って)読むことができ理解が深まりました。
この記事によって変なところに引っかからずに最短経路で理解できる人が増えることを願っています。
思ったよりも長くなってしまったので今回はここまでにします。
もし間違いなどがあればご指摘いただければ幸いです。