Raspberry Pi 3 Model Bに Adafruit PiOLED を接続

## はじめに
[Raspberry Pi 3 で CO2濃度を測る](http://qiita.com/revsystem/items/5a362e749ef80358e801) で取得したCO2濃度の値を、他システムに通知するなどの仕組みを設けずにRaspberry Pi 3上で直接参照できるよう、OLEDを取り付けてみました。
ここでは、Raspberry Pi用 128x32 モノクロOLEDモジュールの取付けとサンプルプログラムの実行までを記載します。

## 準備する物
 - [アイ・オー・データ機器 UD-RP3BP Raspberry Pi メインボード（Bluetooth、Wi-Fi対応モデル） Raspberry Pi 3 Model B+](https://www.amazon.co.jp/dp/B07SDFCDYQ/)
 - [Raspberry Pi用 128x32 モノクロOLEDモジュール](https://www.switch-science.com/catalog/3393/)
 - [uxcell コネクタライン ジャンパーケーブル 1P-1P 40P 10cm メスオス](https://www.amazon.co.jp/gp/product/B013BK5JX8/)
	 - [Raspberry Pi 3 で CO2濃度を測る](http://qiita.com/revsystem/items/5a362e749ef80358e801) の手順でMH-Z19センサーを取り付けている場合はGPIOが重複するため、モノクロOLEDモジュールはジャンパーケーブルで接続する。

## 前提条件
 - "[RaspberryPi 3 model B に CentOS7をインストール](http://qiita.com/revsystem/items/9989a18116e554c133b6)" でCentOS7をインストールしていること。
 - rootユーザのほかに一般ユーザが存在し sudo コマンドが実行可能であること。

## 参考情報
 - http://www.mana-cat.com/entry/PiOLED
 - https://www.switch-science.com/catalog/3393/
 - http://d.hatena.ne.jp/penkoba/20131215/1387119945
 - https://stackoverflow.com/questions/6057726/how-to-access-i2c-device-driver-node
 - https://raspberrypi.stackexchange.com/questions/29477/i-loose-my-node-i2c-on-each-reboot-dev-i2c-1
 - https://wiki.archlinuxjp.org/index.php/%E3%82%AB%E3%83%BC%E3%83%8D%E3%83%AB%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB#.2Fetc.2Fmodprobe.d.2F_.E5.86.85.E3.81.AE.E3.83.95.E3.82.A1.E3.82.A4.E3.83.AB.E3.82.92.E4.BD.BF.E3.81.86
 - http://elinux.org/RPi_Low-level_peripherals#Model_A.2B.2C_B.2B_and_B2
 - https://pypi.python.org/pypi/smbus-cffi/
 - http://www.turbolinux.com/support/document/knowledge/204.html
 - https://www.hiroom2.com/2017/02/18/linux-systemd-s-start-request-repeated-too-quickly-for-xxx-service/#sec-3

## ライブラリのインストール
### RPi.GPIO ライブラリのインストール

gcc 、python-devel 、 pip をインストールした後、RPi.GPIO ライブラリをインストールします。　

```
$ sudo yum install -y gcc python-devel
$ curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py"
$ sudo python get-pip.py
$ sudo pip install RPi.GPIO 

Collecting RPi.GPIO
  Using cached RPi.GPIO-0.6.3.tar.gz
Building wheels for collected packages: RPi.GPIO
  Running setup.py bdist_wheel for RPi.GPIO ... done
  Stored in directory: /root/.cache/pip/wheels/ae/4d/3b/e924997dbf06810adf3b2e37f1d9627b2327eb9cbb285949c9
Successfully built RPi.GPIO
Installing collected packages: RPi.GPIO
Successfully installed RPi.GPIO-0.6.3
```

### python-imaging 、smbusライブラリのインストール
python-imaging、sumbusライブラリをインストールします。

```
$ sudo  yum install -y python-imaging
$ sudo yum install libffi-devel
$ sudo pip install smbus-cffi
```

## モノクロOLEDモジュールの接続
Raspberry Pi 3 をシャットダウンし、モノクロOLEDモジュールを接続して起動します。
モノクロOLEDモジュールを直接GPIOに接続する場合は、https://www.switch-science.com/catalog/3393/ の `Pi Zeroへの搭載例` にあるように挿すだけです。
MH-Z19センサーとモノクロOLEDモジュールを同時に使用する場合は、
http://elinux.org/File:Pi-GPIO-header.png と https://cdn-learn.adafruit.com/assets/assets/000/042/248/medium800/adafruit_products_schem.png?1496376981 の回路図をもとにジャンパーケーブルで接続します。

![OLED.png](https://qiita-image-store.s3.amazonaws.com/0/61809/4af13620-abbe-0d77-fe02-1f094323f418.png)
Drawn by [Cacoo](https://cacoo.com/)


### カーネルモジュールのロード
I2C通信に必要なカーネルモジュール ( i2c-dev, i2c-bcm2708 ) の情報は以下のとおり。

```
$ sudo modinfo i2c-dev
filename:       /lib/modules/4.9.35-v7.1.el7/kernel/drivers/i2c/i2c-dev.ko
license:        GPL
description:    I2C /dev entries driver
author:         Frodo Looijaard <frodol@dds.nl> and Simon G. Vogl <simon@tk.uni-linz.ac.at>
srcversion:     39CD4E8A99571AFB173792F
depends:
intree:         Y
vermagic:       4.9.35-v7.1.el7 SMP mod_unload modversions ARMv7 p2v8
```

```
$ sudo modinfo i2c-bcm2708
filename:       /lib/modules/4.9.35-v7.1.el7/kernel/drivers/i2c/busses/i2c-bcm2708.ko
alias:          platform:bcm2708_i2c
license:        GPL v2
author:         Chris Boot <bootc@bootc.net>
description:    BSC controller driver for Broadcom BCM2708
srcversion:     0C1F4F8690E98B7CEAABAE5
alias:          of:N*T*Cbrcm,bcm2708-i2cC*
alias:          of:N*T*Cbrcm,bcm2708-i2c
depends:
intree:         Y
vermagic:       4.9.35-v7.1.el7 SMP mod_unload modversions ARMv7 p2v8
parm:           baudrate:The I2C baudrate (uint)
parm:           combined:Use combined transactions (bool)
```

カーネルモジュールをロードします。
ロードすると、さきほど確認したカーネルモジュール情報の description が dmesg に出力されます。

```
$ sudo modprobe i2c-dev
$ dmesg | grep i2c
[    5.202056] i2c /dev entries driver
```

### /dev/i2c-1 の作成
必要となるスペシャルファイル( /dev/i2c-1 もしくは /dev/i2c/1 ) が存在しないため、作成します。
スペシャルファイルを作成する mknod コマンドの引数となるmajor number (ここでは89)を確認します。

```
$ cat /proc/devices | grep i2c
 89 i2c
```

mknod コマンドを実行し、/dev/i2c-1 が作成されたことを確認します。

```
$ sudo mknod /dev/i2c-1 c 89 1
$ ls /dev/i2c-1
/dev/i2c-1
```

### i2c-toolsのインストール
モノクロOLEDモジュールが接続されていることを確認するため、i2c-tools をインストールします。

```
$ sudo yum install -y i2c-tools
```
i2cdetectコマンドを実行し、モノクロOLEDモジュールのアドレス 0x3c が出力されることを確認します。

```
$ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
```

## サンプルプログラム
サンプルプログラムを git cloneします。
git がインストールされていない場合は、先にインストールします。

```
# sudp yum -y install git
$ git clone https://github.com/adafruit/Adafruit_Python_SSD1306.git
```

### サンプルプログラムのセットアップ
サンプルプログラムの実行環境をセットアップします。

```
$ cd Adafruit_Python_SSD1306
$ sudo python ./setup.py build
$ sudo python ./setup.py install
```

### サンプルプログラムの実行
以下のように、stats.py を実行するとモノクロOLEDモジュールに出力例のようなリソース情報が出力されます。

```
$ python ./examples/stats.py &
```

出力例)

```
IP: 192.168.1.110
CPU Load: 0.50
Mem: 48/923MB 5.20%
Disk: 1/14GB 9%
```
モノクロOLEDモジュールを直接GPIOに接続した場合
![DSC_1399s.jpg](https://qiita-image-store.s3.amazonaws.com/0/61809/4c5ba0d5-622d-28ec-c656-50fe69739ed3.jpeg)

MH-Z19センサーとモノクロOLEDモジュールを同時に使用する場合
![DSC_1403s.jpg](https://qiita-image-store.s3.amazonaws.com/0/61809/f959a83e-4adb-61b4-b9c7-952c8e9a4566.jpeg)


## OS起動時のカーネルモジュール自動ロード設定
OS起動時にカーネルモジュールが自動的に読み込まれるよう設定します。
/boot/config.txtの末尾に以下の2行を追記します。

```
$ sudo vi /boot/config.txt 

dtparam=i2c=on1=on
dtparam=i2c_arm=on
```

/etc/modules-load.d 以下に i2c-dev.conf を新規作成し、i2c-dev と追記します。

```
$ sudo vi /etc/modules-load.d/i2c-dev.conf

i2c-dev
```

## MH-Z19センサーの値を表示
[Raspberry Pi 3 で CO2濃度を測る](http://qiita.com/revsystem/items/5a362e749ef80358e801) で取得したCO2濃度の値をモノクロOLED モジュールに表示します。

### mh_z19.py を呼び出し
サンプルプログラムの stats.py で 4 行目に表示していたディスク使用量をCO2濃度の値にします。
./examples/stats.py を ./examples/stats-co2.py など別名にし、 実行コマンドを置き換えます。
差分は以下のとおり。
`/PATH/TO/slider/mh_z19.py` の箇所は、実際のパスに置き換えます。
` time.sleep(.1)` では実行頻度が多すぎるので、` time.sleep(60)`に変更します。

```
$ diff ./examples/stats.py ./examples/stats-co2.py
117,118c117,120
<     cmd = "df -h | awk '$NF==\"/\"{printf \"Disk: %d/%dGB %s\", $3,$2,$5}'"
<     Disk = subprocess.check_output(cmd, shell = True )
---
>     #cmd = "df -h | awk '$NF==\"/\"{printf \"Disk: %d/%dGB %s\", $3,$2,$5}'"
>     #Disk = subprocess.check_output(cmd, shell = True )
>     cmd = "python /PATH/TO/slider/mh_z19.py  |awk '{printf \"CO2: %d\", $2}'"
>     CO2 = subprocess.check_output(cmd, shell = True )
125c127,128
<     draw.text((x, top+25),    str(Disk),  font=font, fill=255)
---
>     draw.text((x, top+25),    str(CO2),  font=font, fill=255)
>     #draw.text((x, top+25),    str(Disk),  font=font, fill=255)
130c133
<     time.sleep(.1)
---
>     time.sleep(60)
```

### 実行
4行目に　　CO2: 335 と、実行結果が表示されました。

```
$ sudo python ./examples/stats-co2.py &
```

![DSC_1450.JPG](https://qiita-image-store.s3.amazonaws.com/0/61809/2626a16e-59ed-fbbd-5f90-5d2821d3e446.jpeg)


### 実行頻度を多くした場合のエラー対策

stats.py では、`time.sleep(.1)` となっていて、0.1秒毎に実行されています。
この頻度で mh_z19.py が呼ばれると、systemctl の再起動制限に該当し、以下のメッセージがコンソールに出力されます。

```
Job for serial-getty@ttyS0.service failed because start of the service was attempted too often. See "systemctl status serial-getty@ttyS0.service" and "journalctl -xe" for details.
To force a start use "systemctl reset-failed serial-getty@ttyS0.service" followed by "systemctl start serial-getty@ttyS0.service" again.
```

[10秒間に6回以上の再起動でエラー](https://www.hiroom2.com/2017/02/18/linux-systemd-s-start-request-repeated-too-quickly-for-xxx-service/) となるため、`time.sleep(60)` のように間隔をあけるか、systemdファイルに `StartLimitBurst=0` を追記します。
ファイルのパスを確認します。

```
$ systemctl show -p FragmentPath  serial-getty@ttyS0.service
FragmentPath=/usr/lib/systemd/system/serial-getty@.service
```


```
$ sudo vi FragmentPath=/usr/lib/systemd/system/serial-getty\@.service

IgnoreSIGPIPE=no
SendSIGHUP=yes
StartLimitBurst=0  <- この行を追加

[Install]
WantedBy=getty.target
```

systemdを再起動します。

```
$ sudo systemctl daemon-reload
```