GPIO実験ボード (Sunhayato AS-E405) を使ったラズパイGPIO学習

初投稿。
先月はじめてラズパイを購入してみました。
元々ネットワーク勉強用として使っていたのですが、GPIOを用いた電子工作に興味をもって学習中の初心者です。GPIO学習に適したラズパイ用GPIO拡張ボードを見つけたので購入、簡単ですが使い方をまとめました。

# 使ったもの
- RaspberryPi4 / Model B / 4G RAM
- GPIO実験ボード ([Sunhayato AS-E405](#実験ボードの仕様))
- ジャンパワイヤ (両端オス)

# GPIOとは？
以下に学習の際に参考にしたサイトを共有します。

https://deviceplus.jp/raspberrypi/raspberrypi-gpio/

簡単に説明すると...
汎用入出力(I/O)を意味するGeneral-purpose input/outputの略。
I/OピンのHigh・Lowをプログラムなどから制御することで外部デバイスを操作したり、入力信号を受け取ったりすることが可能。(LED点灯、モーター制御、センサー機器のデータ取得 など)

詳しくは上記サイトが非常に参考になると思います。


# 実験ボードの仕様
↓今回使用した実験ボードはこちら
[Sunhayato AS-E405(公式ページ)](https://shop.sunhayato.co.jp/products/as-e405)

![AS-E405.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3051291/2f36a7f5-0146-ca12-d9a3-383374d4600b.png)

- GPIOコネクタ（メス型）
- LED x4 （出力デバイス）
- タクトスイッチ x3 （入力デバイス）
- スライドスイッチ x1　(入力デバイス)
- 温度センサTMP102（I2C接続）
- A/DコンバーターADS1015（I2C接続、12bit分解能、4Ch）

GPIOコネクタの傍に対応するGPIOピンの名前がラベリングされているため配線の確認が楽になります。また、LED/スイッチ/温度センサ/ADコンバータ がボード上に搭載されているためブレッドボードやその他の回路部品を用意せずとも簡単なGPIO操作を体験できます。

# 実験
ラズパイに実験ボードを取り付けます。このとき念のため電源切ってから作業することをお勧めします。
1. 裏面のコネクタが無い側に付属の金属スペーサを取り付ける
2. GPIOピンに合わせてラズパイに重ねるようにして接続する
(横から見てラズパイのGPIOピンが見えなくなるくらいまで深く差し込む）

|![DSC_0213.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3051291/06ab761a-c44a-98b7-2aa3-33b56ecc6376.jpeg)| ![DSC_0218.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3051291/e5a7694c-6224-8774-6126-6148500f3e48.jpeg)|
|:-:|:-:|
|1. 金属スペーサの装着|2. ラズパイへの取り付け|

ラズパイの初期セットアップ作業については省略。
OSは「Raspberry Pi OS（32-bit）」を使用しています。

作業用ディレクトリはhomeディレクトリ下に作成したGPIOフォルダとしました。
```md:Raspberrypi terminal
mkdir ~/GPIO
cd ~/GPIO
``` 
プログラム言語はpython。GPIO用ライブラリとしてRPi.GPIOを使用しました。
Raspberr Pi OSであればデフォルトで使用できます。必要であればインストール
```md:Raspberrypi terminal
pip install RPi.GPIO
```

実験
[1. LEDを使ってみる](#ledを使ってみる)
[2. スイッチを使ってみる](#スイッチを使ってみる)
[ I2C通信の有効化](#i2c通信の有効化)
[3. 温度センサを使ってみる](#温度センサを使ってみる)
[4. A/Dコンバータを使ってみる](#adコンバータを使ってみる)

## LEDを使ってみる
実験ボード上で以下のような回路となっているようです。1kΩの抵抗を挟んでGNDに接続されているのでGPIO出力や電源の3.3Vをコネクタに繋げばLEDが光るはず。
通常LEDを使用する際にはLEDに大きすぎる電流が流れないように抵抗を配置しないとLEDの破損につながるのですが、実験ボードでは内部で抵抗が既に接続されているため自分で抵抗を用意する必要はないです。
![LED.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3051291/32b1af46-d197-9aaa-77cd-380153098b71.png)


<h4>配線</h4>

GPIO26, GPIO19, GPIO13, GPIO6を使用。以下の図のように配線しました。
![led配線.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3051291/2d7f2267-a704-d468-937e-631d30af7213.png)

<h4>pythonプログラム</h4>

分かり易いようシンプルにベタ書きしてます。
```LED.py
import RPi.GPIO as GPIO
import time

LedPin1 = 26
LedPin2 = 19
LedPin3 = 13
LedPin4 = 6

# GPIOピンのバインド
def setup():
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(LedPin1, GPIO.OUT,initial=GPIO.LOW)
    GPIO.setup(LedPin2, GPIO.OUT,initial=GPIO.LOW)
    GPIO.setup(LedPin3, GPIO.OUT,initial=GPIO.LOW)
    GPIO.setup(LedPin4, GPIO.OUT,initial=GPIO.LOW)

# GPIOピンの開放
def destroy():
    GPIO.output(LedPin1,GPIO.LOW)
    GPIO.output(LedPin2,GPIO.LOW)
    GPIO.output(LedPin3,GPIO.LOW)
    GPIO.output(LedPin4,GPIO.LOW)
    GPIO.cleanup()

# main
setup()
try:
    for i in range(5):
        GPIO.output(LedPin1,GPIO.HIGH)
        time.sleep(0.5)
        GPIO.output(LedPin1,GPIO.LOW)
        GPIO.output(LedPin2,GPIO.HIGH)
        time.sleep(0.5)
        GPIO.output(LedPin2,GPIO.LOW)
        GPIO.output(LedPin3,GPIO.HIGH)
        time.sleep(0.5)
        GPIO.output(LedPin3,GPIO.LOW)
        GPIO.output(LedPin4,GPIO.HIGH)
        time.sleep(0.5)
        GPIO.output(LedPin4,GPIO.LOW)
    destroy()
except :
    destroy() 
```
実行して上手く動けばLEDが順番に点灯していくのが確認できるはずです。

※念のためtry-exceptで例外処理を記述し、GPIOリソースの開放を行ってます。
(Ctrl-cなどプログラムを外部から強制終了したときにGPIOリソースが確保されたままになるのを防ぐため)

## スイッチを使ってみる
ボードで以下のような回路となっているようです。スイッチがOFFの時はGNDに繋がり0V、スイッチがONの時は3.3Vがコネクタに出力される仕様。
![switch.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3051291/42260e89-4145-8df9-8220-5609b95dcea0.png)

<h4>配線</h4>

LED1とSW1、GPIO26とGPIO22を使用。以下の図のように配線しました。
![switch配線.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3051291/15739c88-832d-f533-7bc3-58c3cccf4142.png)

<h4>pythonプログラム</h4>

whileで無限ループさせています。止めるときは＜Ctrl-C＞で。
```switch.py
import RPi.GPIO as GPIO
import time

LedPin = 26
SwPin = 22

SwStatus = 0
LedStatus = 0

def setup():
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(LedPin,GPIO.OUT,initial=GPIO.LOW)
    GPIO.setup(SwPin,GPIO.IN,pull_up_down=GPIO.PUD_DOWN)
    LedStatus = GPIO.input(SwPin)
def destroy():
    GPIO.setup(LedPin,GPIO.OUT,initial=GPIO.LOW)
    GPIO.cleanup()

# main
setup()
try:
    while True:
        # スイッチ読み取り 
        sw = GPIO.input(SwPin)
        # スイッチが押されるたびにLEDを (on <-> off)
        if SwStatus==0 and SwStatus!=sw:
            LedStatus = not LedStatus
        # LEDの制御
        if LedStatus == 1:
            GPIO.output(LedPin,GPIO.HIGH)
        else: 
            GPIO.output(LedPin,GPIO.LOW)
        # スイッチの状態更新
        SwStatus = sw
        
        time.sleep(0.1)
    destroy()  
except:
    destroy()
```
実行して上手く動けばsw1を押すたびにLEDのonとoffが切り替わるはずです。

処理としてはif文でスイッチ信号がLOWからHIGHに切り替わるタイミングを判定してLEDの状態を入れ替えています。

# I2C通信の有効化

I2C通信とは他デバイスとデータをやり取りするための通信手法のことです。
温度センサとADコンバータを使うにはラズパイの設定でI2C通信を有効にする必要があります。

ラズパイで以下のコマンドを実行。
```md:Raspberrypi terminal
sudo raspi-config
```
以下のような画面が表示されたら、
`3 Interface Options`を選択
![raspi-config.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3051291/b37662c5-4c95-ef7e-4b10-86f49dd3cfb9.png)

続いて`I5 I2C`を選択。
![raspi-config2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3051291/47a135f7-84d1-0583-0d9c-48e26e2b6ac6.png)


Would you like the ARM I2C interface to be enabled?
と聞かれるので`Yes`を選択します。

I2C有効化の設定を行ったら**一度再起動**してください。
```md:Raspberrypi terminal
sudo reboot
```

正しく設定が完了していれば以下のコマンドでI2Cデバイスのアドレスを確認できます。
```md:Raspberrypi terminal
sudo i2cdetect -y 1

     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- 48 49 -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
```
0x48が温度センサ、0x49がA/Dコンバータです

通常GPIOでI2C通信を利用するにはGPIO3(SCL)、GPIO2(SDA)、3V3、GNDへの配線が必要ですが、実験ボード上ではすでに内部で配線されているため簡単に利用できます。

基本的にプログラム側で使用したいデバイスのアドレスを指定してデータの読み出し&書き込みを行うことになります。

## 温度センサを使ってみる
↓温度センサの仕様は以下で確認できます。
[TI社 TMP102 製品ページ](https://www.ti.com/product/ja-jp/TMP102)

ボード上で以下のような回路となっているようです。配線はボード上で完結しているため、追加の配線は必要ありません。
![温度センサ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3051291/e6a2349a-333a-03ef-932e-76fda208c285.png)

TMP102を利用するためのpythonライブラリのインストール
```md:Raspberrypi terminal
sudo pip install smbus2
```

<h4>pythonプログラム</h4>

```tmp102.py
import time
from smbus2 import SMBus

# バス番号の指定(基本的に1)
busNum = 1
smbus2 = SMBus(busNum)

# I2Cのアドレスの指定(0x48)
addr = 0x48

for i in range(10):
    
    # TMP102の温度情報は12bit
    # 12bitのデータを1byteごとに2回取り出して結合する
    data  = []
    data = smbus2.read_i2c_block_data(addr,0,2)

    # data[0] : aaaa bbbb (8bit)
    # data[1] : cccc xxxx (8bit)
    tmp  = 0                # tmp = 0000 0000 0000 0000
    tmp  = data[0] << 4     # tmp = 0000 aaaa bbbb 0000
    tmp += data[1] >> 4     # tmp = 0000 aaaa bbbb cccc

    # 温度の表示 分解能12bit 0.0625度きざみ
    print(tmp * 0.0625)

    time.sleep(1)
```
センサで取得した温度が表示されるはずです。ラズパイ排熱の影響か、外気温よりも高い値が出力される様子?

以下の記事を参考にしました。丁寧に解説されていて非常に分かり易かったです。

http://tech-and-investment.com/raspberrypi2-3-i2c-tmp102/

## A/Dコンバータを使ってみる
↓A/Dコンバータの仕様は以下で確認できます。
[TI社 ADS1015 製品ページ](https://www.ti.com/product/ja-jp/ADS1015)

ADコンバータは入力されたアナログ電圧値をデジタル変換するデバイスです。
実験ボードのADS入力は4チャンネル存在し、プログラム側でどのチャンネルから入力値を受け取るか制御します。

ボード上で以下のような回路となっているようです。
コネクタに3V3とGNDが存在しますが、ADS用のVDD, GNDは内部で接続されているため、こちらのコネクタに電源とGNDを配線する必要はありません。(下図CN3の5, 6番のこと)
入力はAIN0~AIN3の4チャンネル。
![ADS1015.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3051291/498b2190-fcda-4688-be42-5baf09cebca9.png)

<h4>配線</h4>
可変抵抗が手元にあれば使用したいところ。今回はボード上のタクトスイッチSW1をADS入力チャンネルAIN0に直接配線してみました。

![ADS配線.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3051291/87e711fd-fb3d-aff4-f42b-f53343d1108f.png)

ADS1015を利用するためのpythonライブラリのインストール
```md:Raspberrypi terminal
sudo pip install adafruit-ads1x15
```
<h4>pythonコード</h4>

チャンネルAIN0に入力された電圧の値を出力します。
上記の配線であればSW1を押している間だけ3.3Vを出力し、それ以外では0Vを出力するはず。
```ads1015.py
import time
import Adafruit_ADS1x15

# ADS1015の設定をします
ads = Adafruit_ADS1x15.ADS1015(address=0x49, busnum=1)

# ゲインの設定
# 指定によって、取得できる電圧の範囲が変わります

#  - 2/3 = ±6.144V
#  -   1 = ±4.096V
#  -   2 = ±2.048V
#  -   4 = ±1.024V
#  -   8 = ±0.512V
#  -  16 = ±0.256V
GAIN = 1

# 「ゲイン1」の範囲は±4.096なので、4.096*2 = 8.192、
# ADSの分解能は12bit=4096、
# 範囲を分解能で割ると、ADSの値「1」につき電圧は0.002[V]
# UNIT=単位
UNIT = (4.096 * 2) / 4096

# ADSのアナログ入力チャンネルを指定(AIN0~AIN3)
ads1015_pin  = 0

for i in range(40):
    data = 0
    volt = 0
    # ADS1015からデータを取得
    data = ads.read_adc( ads1015_pin, gain=GAIN)
    # 取得データから電圧を計算
    volt = data * UNIT
    print("電圧：" + "{:.3f}".format(volt))

    time.sleep(0.25)

print("done.")
```
以下の記事を参考にしました。温度センサ同様に細かく解説されていて非常に分かり易かったです。

http://tech-and-investment.com/raspberrypi2-5-ads1015/

# おわりに
わたしのようなGPIO初心者にとっては学習用にとても便利な実験ボードだと感じました。今後ブレッドボードやその他の電子部品を用意して色々と試していくことでGPIOへの理解を深めていきたいです。

-蛇足-
初投稿でしたが手を動かした作業よりも作業内容をまとめたり記事の構成考えたりする時間の方がとても長かったです...
世の中の技術ブログや覚え書きもこういった手間がかかっているのだということを改めて実感しました。どんな内容であれ記事を書いてくれる人が居ることに心から感謝です！
***
参考
https://deviceplus.jp/raspberrypi/raspberrypi-gpio/
https://www.raspberrypirulo.net/entry/raspberrypi-i2c
http://tech-and-investment.com/raspberrypi2-5-ads1015/
http://tech-and-investment.com/raspberrypi2-3-i2c-tmp102/

Sunhayato AS-E405 取説
https://docs.rs-online.com/b8e0/0900766b81726ee0.pdf