LoginSignup
9
12

More than 5 years have passed since last update.

Pi Piper I/O編

Last updated at Posted at 2016-01-10

Pi Piper

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

導入

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

$ 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が点滅します。

$ 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

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

$ 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 立ち上がり
OFFからONになった時
:falling 立ち下がり
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が表示されます。

$ sudo ruby trigger.rb
rising
rising
rising

:bothを指定した場合は立ち上がりと立ち下がりのどちらか分からないので、pin.readpin.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と同じ状態が続いてしまうときがあります。

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

イベント

後で書く

9
12
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
12