Pi Piper I/O編


# Pi Piper
Pi PiperはRaspberry PiでGPIOをRubyから制御できるGemです。
https://github.com/jwhitehorn/pi_piper

# 導入
導入はGemをインストールするだけといたって簡単です。

```sh
$ sudo gem install
```

# Digital Input/Output

今回はデジタル入出力について記述します。
PiPiper::Pinクラスを用いて制御します。
ここではRaspberry Pi B+をベースに記述してます。


## 出力
Ledを点灯させるなどの場合に出力を使用します。

```led.rb
require 'pi_piper'
include PiPiper

pin = Pin.new pin:23, direction: :out

loop do
  pin.on
  sleep 0.5
  pin.off
  sleep 0.5
end
```

```Pin.new pin:23, direction: :out```でオブジェクトを生成しています。
オプションの```pin```で渡している```23```はGPIOのピン番号で```GPIO 23```を意味し、```direction```はI/Oの方向で、出力の場合は```:out```を指定します。

この例では1秒周期でLedが点滅します。

```sh
$ sudo ruby led.rb
```

## 入力
スイッチやセンサーの状態を取り込む場合に入力を使用します。

```sw.rb
require 'pi_piper'
include PiPiper

pin = Pin.new pin:20, direction: :in, pull: :down

loop do
  pin.read
  puts "#{pin.last_value} => #{pin.value}" if pin.changed?
  sleep 0.5
end
```

実行結果で、状態が変わるごとに表示されます。

```sh
$ sudo ruby ./sw.rb
0 => 1
1 => 0
0 => 1

```

0.5秒ごとに状態を更新し、変化があれば表示します。


### Pinオブジェクト生成

Pinオブジェクトの生成では```pull```オプションが増えています。
実はRaspberri PiのGPIOにはプルダウン/プルアップ抵抗が内臓されていて、わざわざ外付けのプルダウン/プルアップ抵抗を付けなくてもいいのです。

指定できるのは

| 値 | 内容 |
|---|---|---|
|:down|プルダウン|
|:up|プルアップ|
|:float|なし|
|:off|:floatに同じ。デフォルト|

Pinオブジェクト生成時のオプションでこの他に```invert```があります。
```true```の場合に値が反転して取り込まれます。入力が回路的に負論理の場合に、プログラム上では正論理で扱いたい場合に便利です。
出力でも反映されると便利な気がしますが、今の所入力のみのようです。

### 値の更新

```pin.read``` を呼び出すことで入力の状態が取り込まれます。
現在の状態は```pin.value```で取得でき、```pin.last_value```はその前に```pin.read```を行った時の値が取得できます。
```pin.changed?``` で変化したかどうかが判別できます。

```pin.value``` の代わりに```pin.on?```、```pin.off?```も利用できます。ONかOFFか直接判断したい場合に利用できます。


通常はトリガーやイベントとして取り扱いたい場合が多いと思いますので、この様なポーリングでの使い方はあまりしないと思いますが、原理として押さえておきたい所です。

### トリガー

上記の入力処理では0.5秒置きに```pin.changed?```で入力の状態が変化したかどうか調べて、変化があったら表示するという、ポーリングといわれる方法を使って読み取りました。

トリガーを使うと```Pin#wait_for_change```を使用して状態が変化するまで待つことができます。
Pinオブジェクト生成時に```trigger```オプションを渡す事でトリガーを利用できます。

triggerオプションとして指定できるのは下の3つになります。

| 値 | 内容 |
|---|---|---|
|:rising|立ち上がり<br />OFFからONになった時|
|:falling |立ち下がり<br />ONからOFFになった時|
|:both|立ち上がりと立ち下がりの両方|

下の例ではPinオブジェクト生成時に```trigger```オプションで```:rising```を指定してます。
```pin.wait_for_change```を実行すると、立ち上がり(つまりスイッチを押した時)までブロックされた状態になります。
無駄な待ちや分岐が不要になります。

```trigger.rb
require 'pi_piper'
include PiPiper

pin = Pin.new pin:20, direction: :in, pull: :down, trigger: :rising

loop do
  pin.wait_for_change
  puts "rising"
end
```

スイッチを押す度にrisingが表示されます。

```sh
$ sudo ruby trigger.rb
rising
rising
rising
```

:bothを指定した場合は立ち上がりと立ち下がりのどちらか分からないので、```pin.read```と```pin.on?```を使って調べる事になります。

```trigger.rb
require 'pi_piper'
include PiPiper

pin = Pin.new pin:20, direction: :in, pull: :up, trigger: :both

loop do
  pin.wait_for_change
  pin.read
  if pin.on?
    puts "rising"
  else
    puts "falling"
  end
end
```

:bothではrisingとfallingは交互に現れるはずですが、チャタリングのように高速に状態が変わる場合はputsなどの処理をしているときに状態が変わってしまい取りこぼしてしまう事があります。
そのため、rising, risingやfalling, fallingと同じ状態が続いてしまうときがあります。

```sh
sudo ruby trigger.rb 
rising
falling
rising
falling
falling
rising
```


チャタリングによる影響は:bothに限ったことではなく他でも発生する事で、ちゃんとやろうとすると対策が必要になってきますが、ここでの説明は割愛します。
抵抗とコンデンサでハード的にフィルタリングするのが最善策です。
GPIOに時定数設定などあったらいいのにですね。


#### 並列処理

triggerを使用すると処理としてはスッキリしますがブロックされるので、同時に他の事をしたいという時に困ってしまいます。
そういう時はThreadを使って処理します。

スイッチが押されるとLEDの点滅パターンが変わる処理はThreadを使ってこんな風になります。
スイッチが押された事を伝えるのにQueueを使ってます。

```trigger2.rb
require 'pi_piper'
include PiPiper

switch_pin = Pin.new pin:20, direction: :in, pull: :up, trigger: :rising
led_pin = Pin.new pin:23, direction: :out

@queue = Queue.new

Thread.new do
  loop do
    switch_pin.wait_for_change
    @queue.push :rising
    puts "rising"
  end
end

patterns = [[0.5, 0.5], [0.2, 0.2, 0.2, 0.4]]
pattern = 0
step = 0
loop do
  if step % 2 == 0
    led_pin.on
  else
    led_pin.off
  end
  sleep patterns[pattern][step]

  # goto next step
  step = (step + 1) % patterns[pattern].size
  unless @queue.empty?
    pattern = 1 - pattern
    step = 0
    @queue.pop
  end
end
```




### イベント

後で書く