Raspberry Pi & Ubuntu 18.04 でGPIOを使う

#１．はじめに

ROS2を活用するには、Ubuntu 18.04[^1]を使用するのが一番手っ取り早いです。
（標準サポートがUbuntu・MacOS・Windows、パッケージは64bit対応だけ・・・）
丁度、Ubuntu公式からも、RaspberryPi向けUbubtu Linuxのイメージも提供されはじめたので、下手にROS2をRaspbianでコンパイルするよりも手軽かと思い、Raspberry Piの環境をUbuntuに完全移行してみました・・・が、やはりユーザが少なく情報も少ないので、特にGPIO周りの整備でハマりました。

[^1]: ネイティブで対応している[Ubuntu MATE](https://ubuntu-mate.org/raspberry-pi/)を使っても良いです（というのを、後で教えて頂きました^^;）

ここでは、Raspberry Pi + Ubuntu 18.04 環境で、ROS2からGPIOを操作する際の考察と、最終的に、唯一まともに使うことが出来たWiringPiについて、コマンドライン版と、Python3版のインストールまでを概説します。

#２．GPIOライブラリの対応状況

Raspberry PiのGPIOを操作するには、いろいろなライブラリがありますが、Ubuntu + RaspberryPi環境で試したところいろいろ制約が見つかりました。

（評価環境）
Ubuntu 18.04.3 LTS (Bionic Beaver) arm64 版
http://cdimage.ubuntu.com/ubuntu/releases/18.04.3/release/

|ライブラリ|使える？|問題（ハマったところ）|
|:--|:--|:--|
|[RPi.GPIO](https://pypi.org/project/RPi.GPIO/)|×|pip等でインストール可能。puthonのスクリプトでimport時にエラー「This module can only be run on a Raspberry Pi!」が発生して使えない|
|[pigpio](http://abyz.me.uk/rpi/pigpio/index.html)|△|ソースからビルド可能。GPIO出力と、GPIO入力”エッジ”検出（立ち上がり・立ち下がりだけ）はOK。GPIO入力状態は検知不可（Readすると常に値が"0"）|
|[WiringPi](https://github.com/WiringPi)|×|ソースからビルド可能。"/proc/cpuinfo" ファイルの「Hardware」の項目が無くなったため、チップセットの情報が取得出来ず実行時にエラー「Oops: Unable to determine board revision from /proc/cpuinfo」が発生して使えない|
|WiringPi + [eoan](https://packages.ubuntu.com/eoan/wiringpi)パッチ|○|Ubuntu 19.10以降ではWiringPiパッケージが存在。上記の問題他を解決するパッチが提供されている|

##結論

19年9月時点では、

 - Ubuntuのバージョン縛りがない場合
    - Ubuntu 19.10 以降を使うと、WiringPiをパッケージでインストール可能。（動作確認済）
 - （ROS2を使うなど）どうしてもUbuntu 18.04LTSを使いたい場合
    - GPIO出力専用、あるいは、GPIO入力の”エッジ”だけ検出できれば良い場合は、pigpioを使う。
    - WiringPi + eoanパッチをあてて、自力でコンパイルする。


## （おまけ）JETSONでGPIOを使う場合

JETSONはUbuntu 18が動きます。
GPIOの操作に関しては、nvidiaが公式に [Jetson.GPIO](https://github.com/NVIDIA/jetson-gpio) というライブラリを提供していますので、そちらを使います。

 - [インストール例](https://qiita.com/myasu/items/33f1724d977f7b1667ad)

#３．インストール

Ubuntu 18.04で、WiringPiを使うためのビルド手順を解説します。

##(1) コマンドライン版のWiringPi

###準備

```sh:コマンドライン
#作業ディレクトリを作成
$ mkdir ~/gitwork
$ cd ~/gitwork
#ソースのダウンロード
~/gitwork$ wget http://archive.ubuntu.com/ubuntu/pool/universe/w/wiringpi/wiringpi_2.50.orig.tar.gz
~/gitwork$ wget http://archive.ubuntu.com/ubuntu/pool/universe/w/wiringpi/wiringpi_2.50-0ubuntu1.debian.tar.xz
#展開
~/gitwork$ tar xzfv wiringpi_2.50.orig.tar.gz
~/gitwork$ tar Jxfv wiringpi_2.50-0ubuntu1.debian.tar.xz
```

###パッチを当てる

```sh:コマンドライン
~/gitwork$ cd wiringpi-2.50/
#パッチのファイルを「wiringpi-2.50」ディレクトリにコピー
~/gitwork/wiringpi-2.50$ cp ../wiringpi_2.50-0ubuntu1.debian/patches/* ./
#パッチを当てる順番を確認
~/gitwork/wiringpi-2.50$ cat series 
arm64-revision.patch
fix-dev-deps.patch
no-suid.patch

#パッチを当てる・その１
~/gitwork/wiringpi-2.50$ patch -u < arm64-revision.patch
can t find file to patch at input line 9
Perhaps you should have used the -p or --strip option?
The text leading up to this was:
--------------------------
|Author: Dave Jones <dave.jones@canonical.com>
|Description: Prefer /proc/device-tree for revision information
| All later kernels provide Pi revision information via /proc/device-tree. Use
| this in preference to /proc/cpuinfo, but retain fallback to the latter to
| continue support for older kernels.
|
|--- a/wiringPi/wiringPi.c
|+++ b/wiringPi/wiringPi.c
--------------------------
File to patch: wiringPi/wiringPi.c          #### ← wiringPi/wiringPi.c をペースト
patching file wiringPi/wiringPi.c

#パッチを当てる・その２
~/gitwork/wiringpi-2.50$ patch -u < fix-dev-deps.patch 
can t find file to patch at input line 12
Perhaps you should have used the -p or --strip option?
The text leading up to this was:
--------------------------
|Author: Dave Jones <dave.jones@canonical.com>
|Description: Fix various dependencies
| The libwiringPiDev library is missing a link dependency on libwiringPi. It
| also needs to include the path to the libwiringPi headers. The gpio utility
| has extraneous lib dependencies (librt, libm, and libcrypt). Finally, for
| libwiringPi itself, the libraries to link with must be placed at the end of
| the command line or the linker (with the now default -as-needed option) won　t
| declare the exported nameds in the libraries as needed.
|
|--- a/gpio/Makefile
|+++ b/gpio/Makefile
--------------------------
File to patch: gpio/Makefile          #### ← gpio/Makefile をペースト
patching file gpio/Makefile
can　t find file to patch at input line 23
Perhaps you should have used the -p or --strip option?
The text leading up to this was:
--------------------------
|--- a/wiringPi/Makefile
|+++ b/wiringPi/Makefile
--------------------------
File to patch: wiringPi/Makefile          #### ← wiringPi/Makefile をペースト
patching file wiringPi/Makefile
can t find file to patch at input line 35
Perhaps you should have used the -p or --strip option?
The text leading up to this was:
--------------------------
|--- a/devLib/Makefile
|+++ b/devLib/Makefile
--------------------------
File to patch: devLib/Makefile          #### ← devLib/Makefile をペースト
patching file devLib/Makefile
ubuntu@raspi3u:~/gitwork/wiringpi-2.50$ patch -u < 
COPYING.LESSER        arm64-revision.patch  examples/             pins/                 wiringPiD/
INSTALL               build                 fix-dev-deps.patch    series                
People                debian/               gpio/                 update                
README.TXT            debian-template/      newVersion            version.h             
VERSION               devLib/               no-suid.patch         wiringPi/   

#パッチを当てる・その３          
ubuntu@raspi3u:~/gitwork/wiringpi-2.50$ patch -u < no-suid.patch 
can t find file to patch at input line 10
Perhaps you should have used the -p or --strip option?
The text leading up to this was:
--------------------------
|Author: Dave Jones <dave.jones@canonical.com>
|Description: Removed check for root on execution of gpio.
| The wiringPi library already has the ability to use the unprivileged
| /dev/gpiomem device if it can't open /dev/mem so there's no need to require
| root access for the gpio command, and hence no need to set the gpio command
| to have suid root privileges.
|
|--- a/gpio/gpio.c
|+++ b/gpio/gpio.c
--------------------------
File to patch: gpio/gpio.c          #### ← gpio/gpio.c をペースト
patching file gpio/gpio.c
~/gitwork/wiringpi-2.50$ 
```

###ビルド

所々でワーニングが出ますが、特には問題無さそうです。
いつものようにporgを使って、簡易的なパッケージ管理をします。

```sh:コマンドライン
#ビルド・インストール
~/gitwork/wiringpi-2.50$ sudo -lD ./build 
#インストール確認
~/gitwork/wiringpi-2.50$ porg -a
wiringpi-2.50
~/gitwork/wiringpi-2.50$ 
```


###動作確認

見慣れた画面が出てきました。

```sh:コマンドライン
~/gitwork/wiringpi-2.50$ gpio readall
 +-----+-----+---------+------+---+---Pi 3B+-+---+------+---------+-----+-----+
 | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |
 +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
 |     |     |    3.3v |      |   |  1 || 2  |   |      | 5v      |     |     |
 |   2 |   8 |   SDA.1 | ALT0 | 1 |  3 || 4  |   |      | 5v      |     |     |
 |   3 |   9 |   SCL.1 | ALT0 | 1 |  5 || 6  |   |      | 0v      |     |     |
 |   4 |   7 | GPIO. 7 |   IN | 1 |  7 || 8  | 1 | ALT5 | TxD     | 15  | 14  |
 |     |     |      0v |      |   |  9 || 10 | 1 | ALT5 | RxD     | 16  | 15  |
 |  17 |   0 | GPIO. 0 |   IN | 0 | 11 || 12 | 0 | IN   | GPIO. 1 | 1   | 18  |
 |  27 |   2 | GPIO. 2 |   IN | 0 | 13 || 14 |   |      | 0v      |     |     |
 |  22 |   3 | GPIO. 3 |   IN | 0 | 15 || 16 | 0 | IN   | GPIO. 4 | 4   | 23  |
 |     |     |    3.3v |      |   | 17 || 18 | 0 | IN   | GPIO. 5 | 5   | 24  |
 |  10 |  12 |    MOSI | ALT0 | 0 | 19 || 20 |   |      | 0v      |     |     |
 |   9 |  13 |    MISO | ALT0 | 0 | 21 || 22 | 0 | IN   | GPIO. 6 | 6   | 25  |
 |  11 |  14 |    SCLK | ALT0 | 0 | 23 || 24 | 1 | OUT  | CE0     | 10  | 8   |
 |     |     |      0v |      |   | 25 || 26 | 1 | OUT  | CE1     | 11  | 7   |
 |   0 |  30 |   SDA.0 |   IN | 1 | 27 || 28 | 1 | IN   | SCL.0   | 31  | 1   |
 |   5 |  21 | GPIO.21 |   IN | 1 | 29 || 30 |   |      | 0v      |     |     |
 |   6 |  22 | GPIO.22 |   IN | 0 | 31 || 32 | 0 | IN   | GPIO.26 | 26  | 12  |
 |  13 |  23 | GPIO.23 |   IN | 0 | 33 || 34 |   |      | 0v      |     |     |
 |  19 |  24 | GPIO.24 |   IN | 0 | 35 || 36 | 0 | IN   | GPIO.27 | 27  | 16  |
 |  26 |  25 | GPIO.25 |   IN | 0 | 37 || 38 | 0 | IN   | GPIO.28 | 28  | 20  |
 |     |     |      0v |      |   | 39 || 40 | 0 | IN   | GPIO.29 | 29  | 21  |
 +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
 | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |
 +-----+-----+---------+------+---+---Pi 3B+-+---+------+---------+-----+-----+
~/gitwork/wiringpi-2.50$ 
```


##(2) Python3版のWiringPi

pip3でもインストールが可能ですが、一緒にWiringPiもコンパイルされるみたいで、先ほどのパッチがそのままでは当てることが出来ません。
そのため、githubからソースを入手して、WiringPiにパッチを当ててコンパイル・インストールする方法を使います。

###準備

```sh:コマンドライン
#ビルドに必要なパッケージのインストール
$ sudo apt install python3-dev python3-setuptools swig
#作業ディレクトリを作成
$ mkdir ~/gitwork
$ cd ~/gitwork
#ソースのダウンロード
~/gitwork$ git clone --recursive https://github.com/WiringPi/WiringPi-Python.git
~/gitwork$ wget http://archive.ubuntu.com/ubuntu/pool/universe/w/wiringpi/wiringpi_2.50-0ubuntu1.debian.tar.xz
#パッチの展開
~/gitwork$ tar Jxfv wiringpi_2.50-0ubuntu1.debian.tar.xz
```

###パッチを当てる

```sh:コマンドライン
~/gitwork$ cd WiringPi-Python/WiringPi/
#パッチのファイルを「WiringPi」ディレクトリにコピー
~/gitwork/WiringPi-Python/WiringPi$ cp ../../wiringpi_2.50-0ubuntu1.debian/patches/* ./
#パッチを当てる
~/gitwork/WiringPi-Python/WiringPi$ patch -u < arm64-revision.patch
can t find file to patch at input line 9
Perhaps you should have used the -p or --strip option?
The text leading up to this was:
--------------------------
|Author: Dave Jones <dave.jones@canonical.com>
|Description: Prefer /proc/device-tree for revision information
| All later kernels provide Pi revision information via /proc/device-tree. Use
| this in preference to /proc/cpuinfo, but retain fallback to the latter to
| continue support for older kernels.
|
|--- a/wiringPi/wiringPi.c
|+++ b/wiringPi/wiringPi.c
--------------------------
File to patch: wiringPi/wiringPi.c      　　 #### ← wiringPi/wiringPi.c をペースト
patching file wiringPi/wiringPi.c
Hunk #2 succeeded at 724 (offset -4 lines).
Hunk #3 succeeded at 742 (offset -4 lines).
Hunk #4 succeeded at 976 (offset -4 lines).
~/gitwork/WiringPi-Python/WiringPi$ patch -u < no-suid.patch 
can t find file to patch at input line 10
Perhaps you should have used the -p or --strip option?
The text leading up to this was:
--------------------------
|Author: Dave Jones <dave.jones@canonical.com>
|Description: Removed check for root on execution of gpio.
| The wiringPi library already has the ability to use the unprivileged
| /dev/gpiomem device if it can't open /dev/mem so there's no need to require
| root access for the gpio command, and hence no need to set the gpio command
| to have suid root privileges.
|
|--- a/gpio/gpio.c
|+++ b/gpio/gpio.c
--------------------------
File to patch: gpio/gpio.c      　　 #### ← gpio/gpio.c をペースト
patching file gpio/gpio.c

```

###ビルド

所々でワーニングが出ますが、特には問題無さそうです。

```sh:コマンドライン
~/gitwork/WiringPi-Python/WiringPi$ cd ..
~/gitwork/WiringPi-Python$ sudo python3 setup.py install
running install
running bdist_egg
running egg_info
creating wiringpi.egg-info
writing wiringpi.egg-info/PKG-INFO
（省略）
creating build/lib.linux-aarch64-3.6
aarch64-linux-gnu-gcc -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-Bsymbolic-functions -Wl,-z,relro -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 build/temp.linux-aarch64-3.6/WiringPi/devLib/piFace.o build/temp.linux-aarch64-3.6/WiringPi/devLib/lcd128x64.o build/temp.linux-aarch64-3.6/WiringPi/devLib/scrollPhat.o build/temp.linux-aarch64-3.6/WiringPi/devLib/ds1302.o build/temp.linux-aarch64-3.6/WiringPi/devLib/lcd.o build/temp.linux-aarch64-3.6/WiringPi/devLib/gertboard.o build/temp.linux-aarch64-3.6/WiringPi/devLib/maxdetect.o build/temp.linux-aarch64-3.6/WiringPi/devLib/piGlow.o build/temp.linux-aarch64-3.6/WiringPi/devLib/piNes.o build/temp.linux-aarch64-3.6/WiringPi/wiringPi/mcp4802.o build/temp.linux-aarch64-3.6/WiringPi/wiringPi/mcp23017.o build/temp.linux-aarch64-3.6/WiringPi/wiringPi/pcf8591.o build/temp.linux-aarch64-3.6/WiringPi/wiringPi/mcp23016.o build/temp.linux-aarch64-3.6/WiringPi/wiringPi/wiringPiI2C.o build/temp.linux-aarch64-3.6/WiringPi/wiringPi/bmp180.o build/temp.linux-aarch64-3.6/WiringPi/wiringPi/drcNet.o build/temp.linux-aarch64-3.6/WiringPi/wiringPi/mcp23s17.o build/temp.linux-aarch64-3.6/WiringPi/wiringPi/mcp3422.o build/temp.linux-aarch64-3.6/WiringPi/wiringPi/drcSerial.o build/temp.linux-aarch64-3.6/WiringPi/wiringPi/softTone.o build/temp.linux-aarch64-3.6/WiringPi/wiringPi/ads1115.o build/temp.linux-aarch64-3.6/WiringPi/wiringPi/ds18b20.o build/temp.linu
~/gitwork/WiringPi-Python$ 
```

###動作確認

importしてみて、エラーが出なければ、ひとまずOK。

```sh:コマンドライン
~/gitwork$ python3
Python 3.6.8 (default, Aug 20 2019, 17:12:48) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import wiringpi
>>> 
:~/gitwork$ 
```

#４．参考資料
 - https://wiki.ubuntu.com/ARM/RaspberryPi
 - https://packages.ubuntu.com/eoan/wiringpi
 - https://forum.qt.io/topic/101185/oops-unable-to-determine-board-revision-from-proc-cpuinfo

##ひとりごと
[RaspberryPi対応のパッケージ](https://packages.ubuntu.com/search?keywords=Raspberry+Pi&searchon=all&suite=all&section=all)は、bionicくらいから、徐々に増えているようです。
eoanが出る頃に、概ね出揃う感じ？ですね。
ROS2ユーザ的には次のLTSである、Ubuntu 20.04が真打ちになりそうな気がします。