ラズベリーパイこと始め２ -LED制御編-

# 始めに
前回の記事[ラズベリーパイこと始め -セットアップ編-](https://qiita.com/twinoze/private/42024e120c35eaa22357)では、ラズベリーパイを購入したところからOS(Raspbian)のセットアップまでを紹介しました。
今回はラズベリーパイのIOを使用したデバイス制御について。

# いきなりの失敗
ラズベリーパイにはGPIOというIOがついています。
GPIOに関してはラズベリーパイに特化したものではなく、マイコンなどの各種電子機器に付属される一般的なIOなのだそうです。詳しくは[Wikipedia](https://ja.wikipedia.org/wiki/GPIO)その他を参照ください。
で、失敗というのは・・・ラズベリーパイのGPIOにはアナログIOがないのだそうです！
知りませんでした。GPIO一般がそうなのか、ラズベリーパイがそうなのかは知りません。すみません。
困りました。というのが、[DS18B20温度センサプローブ](http://amzn.asia/0qe6WBF)を購入していたのです。これで温度を採取して色々遊んでみよう！という私の目論見はあえなく撃沈となりました。

# LED制御
ですがへこんでばかりもいられませんので、少し考えた結果が今回のタイトルです。
LEDのランプを数本と入力用にボタン一つを用意しました。
[LED 5mm レッド](http://amzn.asia/38zD2hu)
[押しボタンスイッチ](http://amzn.asia/eGrutuu)

ボタンを押すたびに内部でカウンタを加算して、カウンタの値に従ってLEDの点灯状態を変化させる回路を作成したいと思います。

# 回路検討
### 内部結線
まずはハードウェアの構築から始めます。
ラズベリーパイ内部の[結線図](https://www.raspberrypi.org/documentation/hardware/raspberrypi/schematics/README.md)は公式で確認できます。

これによると、このように結線されているようです。
ここで、1番はラズベリーパイ本体のSDカードスロット側。
40番はUSBポート側です。
![GPIO.png](https://qiita-image-store.s3.amazonaws.com/0/248779/609cf923-5375-7006-ddc3-84efc7d40bbc.png)
手持ちのカードテスターで確認したところ、3.3V同士、５V同士、GND同士はすべて内部で結線されていました。

### 入力と出力
GPIOでは各端子の入力・出力は決められていません。
すべての端子についてプログラムから、それぞれの端子が入力であるのか、出力であるのかを指定します。
一つの端子を入力・出力として同時に指定することはできませんが、出力端子の情報をプログラム上で参照することは可能です。

### 電流の制限について
ラズベリーパイのGPIOでは、全体として50mA、各端子で16mAまでしか電流を流すことが出来ないとのこと。[ここ](https://www.raspberrypi.org/documentation/hardware/raspberrypi/power/README.md)を確認ください。下線を引いた部分が電流制限に関する説明です。
![powersupply.png](https://qiita-image-store.s3.amazonaws.com/0/248779/a8f683cd-0b60-214a-a549-1ff3ecb8be43.png)
この電流制限を考慮して、LEDの回路途中に抵抗を挿入します。
3.3Vの電源ですので、間に330Ωの抵抗を入れると、
I=V/R[A]ですので、3.3/330=0.01[A]つまり10[mA]となります。
一応、個別端子の電流制限を超えないと思います。
電気工作では、それぞれのパーツの精度のぶれを考慮し、流す電流を公称値の半分程度に抑えることが推奨なのだそうです。その点を考慮するともう少し大きめの抵抗を使用したいところですが、このテストの段階で手元にありませんでした。。。
今後、徐々に部品をそろえて行く必要があるみたいです。

今回スイッチの入力に使用したGPIO2の端子は、上記内部回路図によると内部で3.3Vの電源との間に1.8KΩの抵抗が入っています。こため今回は抵抗を追加で使用することはしませんでした。

### 回路設計
これらのことを考慮し、さらに[ブレッドボード](http://amzn.asia/gNFFigS)との接続のしやすさも考慮し、このような回路構成としました。
![ledtest_circuit.png](https://qiita-image-store.s3.amazonaws.com/0/248779/cd725a8b-f26d-c660-6c7d-953b0377a8d4.png)

![ledtest_bread.png](https://qiita-image-store.s3.amazonaws.com/0/248779/ceb2dd97-144a-b58f-afc8-5b48b3c6ea93.png)

実際の配線の様子はこちら
![CIMG5594A.jpg](https://qiita-image-store.s3.amazonaws.com/0/248779/80285b79-ecf6-eaa4-c9bc-ad134dbb3e4d.jpeg)

# プログラム作成
```python:ledtest.py
#!/usr/bin/python
# coding: utf-8
# timeモジュールと補助モジュールをimport
import time
import ledtest_sub

print ("LED test Start!")

# GPIO初期化
ledtest_sub.gpio_init()

# 変数初期化
roop = 1
val = 0
old = 0

# 繰り返し処理
while roop == 1:

	# GPIO入力
	inp = ledtest_sub.gpio_inp_val()
	if inp == 1 and inp != old:
		val = val + 1
		if val > 3:
			val = 0
		ledtest_sub.gpio_set_value(val)
		print("val:"+format(val))

 	# 待機
	time.sleep(0.05)

	# キーボード入力
	key = ledtest_sub.getkey()
	if key != 0:
		roop = 0	
	old = inp
# GPIO終了処理
ledtest_sub.gpio_exit()

print ("LED test End!")
```
```python:ledtest_sub.py

#!/usr/bin/python
# coding: utf-8
# GPIOモジュールをimport
import RPi.GPIO as GPIO

# getkey 用
import fcntl
import termios
import sys
import os

pinInp=2
pinOut0=20
pinOut1=21

# ****************************************
# GPIO初期化
def gpio_init():
	# GPIO指定をGPIO番号で行う
	GPIO.setmode(GPIO.BCM)

	# GPIOピンの入出力モードを設定
	GPIO.setup(pinInp, GPIO.IN)
	GPIO.setup(pinOut0, GPIO.OUT)
	GPIO.setup(pinOut1, GPIO.OUT)

	# GPIO 出力初期化
	GPIO.output(pinOut0, GPIO.HIGH)
	GPIO.output(pinOut1, GPIO.HIGH)

# ****************************************
# GPIO終了処理
def gpio_exit():
	# GPIOピンをリセット
	GPIO.cleanup()

# ****************************************
#GPIO指定位置の値を設定
#出力定義していないポートに対してこの関数を使用しないこと
def gpio_set_state(num, val):
	GPIO.output(num, val)

# ****************************************
# 指定GPIOの現在値を返す
def gpio_cur_val(num):
	if num<0:
		return 0
	if num>27:
		return 0
	val=GPIO.input(num)
	if val==0:
		val=1
	else:
		val=0
	return val

# 指定GPIOの現在値を返す
def gpio_inp_val():
	return gpio_cur_val(pinInp)

# ****************************************
#GPIO出力
def gpio_set_value(val):
	if val==0:
		GPIO.output(pinOut0, GPIO.HIGH)
		GPIO.output(pinOut1, GPIO.HIGH)
	elif val==1:
		GPIO.output(pinOut0, GPIO.LOW)
		GPIO.output(pinOut1, GPIO.HIGH)
	elif val==2:
		GPIO.output(pinOut0, GPIO.HIGH)
		GPIO.output(pinOut1, GPIO.LOW)
	elif val==3:
		GPIO.output(pinOut0, GPIO.LOW)
		GPIO.output(pinOut1, GPIO.LOW)

# ****************************************
# キー入力を非同期待機
def getkey():
	# 標準入力
	fno = sys.stdin.fileno()

	# stdinの端末属性
	attr_old = termios.tcgetattr(fno)

	# stdinのエコー無効、カノニカルモード無効
	attr = termios.tcgetattr(fno)
	attr[3] = attr[3] & ~termios.ECHO & ~termios.ICANON # & ~termios.ISIG
	termios.tcsetattr(fno, termios.TCSADRAIN, attr)

	# stdinをNONBLOCKに設定
	fcntl_old = fcntl.fcntl(fno, fcntl.F_GETFL)
	fcntl.fcntl(fno, fcntl.F_SETFL, fcntl_old | os.O_NONBLOCK)

	key = 0
	try:
		# キー取得
		rd = sys.stdin.read(1)
		if len(rd):
			while len(rd):
				key = (key << 8) + ord(rd)
				rd = sys.stdin.read(1)
	finally:
		# stdinを元に戻す
		fcntl.fcntl(fno, fcntl.F_SETFL, fcntl_old)
		termios.tcsetattr(fno, termios.TCSANOW, attr_old)

	return key
# ****************************************
```
ledtest.pyがメインルーチン、ledtest_sub.pyがサブルーチン集です。

# テストと修正
実際に動作を確認したところ、ボタンを押した際の動作に不安定なところがあったためledtest.pyのループ内に0.05秒の待機タイマを入れました。
プログラムの動作としては、ボタンを押すたびに内部変数 val の値を[0->1->2->3->0...]と変化させます。
そして val の値に従って二つのLEDが

| val              |       LED1        |    LED2            |
|:-----------------|------------------:|:------------------:|
| 0                |            OFF    |        OFF         |
| 1                |            ON     |        OFF         |
| 2                |            OFF    |        ON          |
| 3                |            ON     |        ON          |

と変化します。
また、キーボードの入力でプログラムが終了します。

# 感想
電子工作的な作業はあまり経験がないのですが、GPIOのピンにリードワイヤーを挿すのが固いなと感じました。
また、前回の記事で電源用のマイクロUSBポートへの接続がきつかったと書きましたが、今回のテストの際にバキッ！と言わせてしまいました。
致命傷にはならずに済んだのですが、皆様お気を付けください。

# 関連リンク
[ラズベリーパイこと始め１ -セットアップ編-](https://qiita.com/twinoze/private/42024e120c35eaa22357)
[ラズベリーパイこと始め３ -デジタル温度入力編-](https://qiita.com/twinoze/private/c960eea23c57e342ea4b)

# 追記
その後情報頂きまして、DS18B20をラズパイでダイレクトに使用する方法があるとのこと。
[Raspberry Piで1-Wireデジタル温度センサのDS18B20をPythonから使う](https://qiita.com/masato/items/cf5a27af696a27b73b86)
[Raspberry Pi で温度センサーの値を読み取るプログラムを作ってみた](http://y-naito.ddo.jp/index.php?id=1435972105)
後日試してみたいと思います。