前回でRaspberry Piの初期設定がだいたい終わったので、引き続きIoTエンジニア養成読本のハンズオンの内容をベースにLチカ(LED点滅)をやってみました。
書籍の例ではPythonが使われていますが、そのままやっても面白くないのでRubyで挑戦しました。mrubyでやりたかったのですが、mrubyでGPIOを操作するためのライブラリのmruby-WiringPiとmruby-raspberryを試してみたもののビルドがうまくいかなかったので、ひとまずCRubyでやってみます。
まずは常時点灯
まずは特に制御はせずに、常時LEDが点灯するように接続してみます。下記の図のように接続するとLEDが点灯します。書籍で使われている抵抗は330Ωですが、購入した抵抗セットの中には含まれてなかったので、1kΩのものを使っています。また、回路図は Fritzing を使って作成しました。
Fritzing
http://fritzing.org/home/
赤:1番ピン
黒:6番ピン(GND)
コマンドラインからLED点灯
次にコマンドラインから直接GPIIOを操作してみます。GPIOへの入出力はOSの擬似ファイルとして提供されているようなので、それを操作します。
まず使用するGPIOのピンをexportファイルに対して設定します。今回はGPIO22のピンを使います。
黒:15番ピン(GPIO22)
赤:6番ピン(GND)
pi@raspberrypi:~ $ ls -l /sys/class/gpio/export -rwxrwx--- 1 root gpio 4096 May 1 05:36 /sys/class/gpio/export pi@raspberrypi:~ $ pi@raspberrypi:~ $ file /sys/class/gpio/export /sys/class/gpio/export: ERROR: cannot read `/sys/class/gpio/export' (Input/output error) pi@raspberrypi:~ $ pi@raspberrypi:~ $ cat /sys/class/gpio/export cat: /sys/class/gpio/export: Input/output error pi@raspberrypi:~ $ pi@raspberrypi:~ $ echo 22 > /sys/class/gpio/export pi@raspberrypi:~ $
するとそのGPIOピンを操作するためのディレクトリが作られるので、まずは入出力の向きを指定します。今回はLEDの点灯のための出力なので、outを設定します。
pi@raspberrypi:~ $ ls -l /sys/class/gpio/gpio22/direction -rwxrwx--- 1 root gpio 4096 May 1 06:16 /sys/class/gpio/gpio22/direction pi@raspberrypi:~ $ pi@raspberrypi:~ $ file /sys/class/gpio/gpio22/direction /sys/class/gpio/gpio22/direction: ASCII text pi@raspberrypi:~ $ pi@raspberrypi:~ $ cat /sys/class/gpio/gpio22/direction in pi@raspberrypi:~ $ pi@raspberrypi:~ $ echo out > /sys/class/gpio/gpio22/direction pi@raspberrypi:~ $
そしてそのピンの値を1にすると点灯状態になり、0にすると消灯状態に戻ります。
pi@raspberrypi:~ $ ls -l /sys/class/gpio/gpio22/value -rwxrwx--- 1 root gpio 4096 May 1 06:16 /sys/class/gpio/gpio22/value pi@raspberrypi:~ $ pi@raspberrypi:~ $ file /sys/class/gpio/gpio22/value /sys/class/gpio/gpio22/value: ASCII text pi@raspberrypi:~ $ pi@raspberrypi:~ $ cat /sys/class/gpio/gpio22/value 0 pi@raspberrypi:~ $ pi@raspberrypi:~ $ echo 1 > /sys/class/gpio/gpio22/value pi@raspberrypi:~ $ pi@raspberrypi:~ $ echo 0 > /sys/class/gpio/gpio22/value pi@raspberrypi:~ $
RubyスクリプトでLチカ
ではRubyスクリプトでLチカをやってみたいと思います。まずはRaspberry Pi上にRuby環境を作るために、gitとrbenvと、その他必要なライブラリをインストールします。
pi@raspberrypi:~ $ sudo apt-get install git pi@raspberrypi:~ $ sudo apt-get install rbenv pi@raspberrypi:~ $ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> .bashrc pi@raspberrypi:~ $ echo 'eval "$(rbenv init -)"' >> .bashrc pi@raspberrypi:~ $ pi@raspberrypi:~ $ tail .bashrc # sources /etc/bash.bashrc). if ! shopt -oq posix; then if [ -f /usr/share/bash-completion/bash_completion ]; then . /usr/share/bash-completion/bash_completion elif [ -f /etc/bash_completion ]; then . /etc/bash_completion fi fi export PATH="$HOME/.rbenv/bin:$PATH" eval "$(rbenv init -)" pi@raspberrypi:~ $ pi@raspberrypi:~ $ git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build Cloning into '/home/pi/.rbenv/plugins/ruby-build'... remote: Counting objects: 7534, done. remote: Compressing objects: 100% (48/48), done. remote: Total 7534 (delta 36), reused 0 (delta 0), pack-reused 7483 Receiving objects: 100% (7534/7534), 1.54 MiB | 850.00 KiB/s, done. Resolving deltas: 100% (4567/4567), done. Checking connectivity... done. pi@raspberrypi:~ $ pi@raspberrypi:~ $ sudo apt-get install -y libssl-dev libreadline-dev
そしてruby2.4.1をインストール。
pi@raspberrypi:~ $ rbenv install 2.4.1 perl: warning: Setting locale failed. perl: warning: Please check that your locale settings: LANGUAGE = (unset), LC_ALL = (unset), LC_CTYPE = "UTF-8", LANG = "en_GB.UTF-8" are supported and installed on your system. perl: warning: Falling back to a fallback locale ("en_GB.UTF-8"). Downloading ruby-2.4.1.tar.bz2... -> https://cache.ruby-lang.org/pub/ruby/2.4/ruby-2.4.1.tar.bz2 Installing ruby-2.4.1... Installed ruby-2.4.1 to /home/pi/.rbenv/versions/2.4.1 pi@raspberrypi:~ $ pi@raspberrypi:~ $ rbenv global 2.4.1 pi@raspberrypi:~ $ pi@raspberrypi:~ $ rbenv version 2.4.1 (set by /home/pi/.rbenv/version) pi@raspberrypi:~ $ pi@raspberrypi:~ $ ruby -v ruby 2.4.1p111 (2017-03-22 revision 58053) [armv7l-linux-eabihf]
gemの管理にはbundlerを使います。
pi@raspberrypi:~ $ gem install bundler Fetching: bundler-1.14.6.gem (100%) Successfully installed bundler-1.14.6 Parsing documentation for bundler-1.14.6 Installing ri documentation for bundler-1.14.6 Done installing documentation for bundler after 27 seconds 1 gem installed pi@raspberrypi:~ $ pi@raspberrypi:~ $ bundle init Writing new Gemfile to /home/pi/Gemfile pi@raspberrypi:~ $
GPIOの操作にはroot権限が必要なので、sudoでbundlerが使えるように、visudoでrbenvのパスを追加しておきます。
pi@raspberrypi:~ $ sudo visudo secure_path に /home/pi/.rbenv/shims を追加
そして今回はRubyからGPIOを操作するために、Pi Piper を使用してみます。
Gemfileに pi_piper の記述を追加してbundle installします。
pi@raspberrypi:~ $ vi Gemfile pi@raspberrypi:~ $ pi@raspberrypi:~ $ cat Gemfile # frozen_string_literal: true source "https://rubygems.org" gem 'pi_piper' pi@raspberrypi:~ $ pi@raspberrypi:~ $ bundle install --path vendor/bundle Fetching gem metadata from https://rubygems.org/........... Fetching version metadata from https://rubygems.org/. Resolving dependencies... Installing eventmachine 1.0.9 with native extensions Installing ffi 1.9.18 with native extensions Using bundler 1.14.6 Installing pi_piper 2.0.0 Bundle complete! 1 Gemfile dependency, 4 gems now installed. Bundled gems are installed into ./vendor/bundle. pi@raspberrypi:~ $
ここまででスクリプトを実行する準備はできたので、スクリプトを用意します。内容は下記の通り。
require 'bundler/setup' require 'pi_piper' pin = PiPiper::Pin.new(pin: 22, direction: :out) loop do pin.on sleep 1 pin.off sleep 1 end
GPIOナンバーと入出力の方向を指定してPinオブジェクトを生成し、onメソッドで点灯、offメソッドで消灯しています。実行にはroot権限が必要なので、sudoでbundler経由でスクリプトを実行します。
pi@raspberrypi:~ $ sudo bundle exec ruby led-flash.rb
スイッチの入力検出
では次にタクトスイッチを追加して、その入力を検出してみたいと思います。
赤:15番ピン(GPIO22)
黒:6番ピン(GND)
黄:16番ピン(GPIO23)
require 'bundler/setup' require 'pi_piper' pin = PiPiper::Pin.new(pin: 23, direction: :in, pull: :up) loop do pin.read if pin.value == 0 puts 'The switch has been pushed.' end sleep 1 end
GPIOナンバーは23で、入出力の向きはinを指定します。プルアップ・プルダウン抵抗はプルアップを指定します。readメソッドで入力値を読み取って、ボタンが押されたら(valueが0だったら)コンソールにメッセージを出力します。
スイッチ押下中だけLED点灯
次にスイッチの入力とLEDへの出力を組み合わせて、スイッチを押している間だけLEDを点灯させます。
require 'bundler/setup' require 'pi_piper' led_pin = PiPiper::Pin.new(pin: 22, direction: :out) switch_pin = PiPiper::Pin.new(pin: 23, direction: :in, pull: :up) loop do switch_pin.read if switch_pin.off? led_pin.on else led_pin.off end sleep(0.5) end
スイッチの入力値の関係がまだよくわかっていないのですが、押された時はvalueが0になり、off?メソッドがtrueになるので、その場合はLEDを点灯しています。
スイッチ押下でLEDのON/OFF切り替え
そして最後にスイッチを押すことでLEDのON/OFFを切り替えるようにしてみます。
require 'bundler/setup' require 'pi_piper' led_pin = PiPiper::Pin.new(pin: 22, direction: :out) led_pin.off switch_pin = PiPiper::Pin.new(pin: 23, direction: :in, pull: :up) loop do switch_pin.read if switch_pin.value == 0 puts "Turn %s the LED since the switch has been pushed." % (led_pin.off? ? 'ON' : 'OFF') led_pin.off? ? led_pin.on : led_pin.off led_pin.read end sleep(0.5) end
スイッチが押されたらLEDの点灯状態を反転させています。ON/OFF切り替え後にreadメソッドを実行しないと現在の状態が認識されないようだったので、実行しています。
Pi Piperではループを回して待ち受けるのではなく、watch や after メソッドを使ってイベントドリブンな形で実装することもできるようなのですが、ちょっと試した限りではうまくいかなかったので、いずれそちらの形で実装できるようにしてみたいと思います。