今までは Raspberry Pi と他のセンサー類を組み合わせてセンサー値を読み取り、データとして使える形にはしていましたが、ハードウェアのアウトプットとしては LED を光らせるぐらいでした。今回はモーターを動かすことに挑戦してみたいと思います。
電池ボックスとモーターの配線準備
今回は下記のモーターと電池ボックスを使用します。
いずれもリード線はついていますが、そのままではブレッドボードにさすには不便なので、下記サイトを参考にリード線を加工しました。
ブレッドボードで電子回路の実験をするために乾電池ホルダーのリード線をジャンパワイヤのように端子化するmatsumotoyoshio.wordpress.com
加工に使った材料は下記の二つです。
最終的に下記のようにリード線の先端にピンがついた形になりました。
ブレッドボードで配線する
今回モーターを回すためにさらに必要なものとして、下記のモータードライバ(TA7291P)を使用しました。
これらを動作確認用の LED と組み合わせて、下記の図のように配線します。ちなみに TA7291P の Fritzing 用の画像は下記サイトのものを使用させていただきました。
モータードライバの各PINは下記のように接続しています。
- GND
- モーターの片方の端子
- 未接続
- Vref. Raspberry Pi の 18番PIN
- IN1. Raspberry pi の 27番PIN
- IN2. Raspberry Pi の 4番PIN
- Raspberry Pi の 5V 電源出力
- 電池ボックスの+側
- 未接続
- モーターのもう一方の端子
TA7291P の4番PINはモーターへの出力調整用なので、電圧を調整できるように Raspberry Pi の PWM対応ピンに接続しています。
モーターを回すサンプルコード
Ruby で GPIO を操作するために pi_piper を使っていますが、 PWM を使う場合は pi_piper のPwmクラスを使用します。
http://www.rubydoc.info/github/jwhitehorn/pi_piper/PiPiper/Pwm
まずはサンプルコードの全体を掲載します。
require 'bundler/setup' require 'pi_piper' PIN_VREF = 18 PIN_IN_1 = 27 PIN_IN_2 = 4 PIN_LED = 17 class MotorSample def initialize puts 'Initializing MotorSample...' @pin_led = PiPiper::Pin.new(pin: PIN_LED, direction: :out) @pin_in_1 = PiPiper::Pin.new(pin: PIN_IN_1, direction: :out) @pin_in_2 = PiPiper::Pin.new(pin: PIN_IN_2, direction: :out) @pin_vref = PiPiper::Pwm.new(pin: PIN_VREF) @pin_vref.value = 0 end def stop_motor puts 'Stopping.' @pin_led.off @pin_in_1.off @pin_in_2.off sleep 1 end def toggle_led(i) i % 2 == 0 ? @pin_led.on : @pin_led.off end def speeding_up puts 'Speeding Up...' 0.step(1.0, 0.1) do |v| toggle_led((v * 10).to_i) @pin_vref.value = v sleep 2 end end def slowing_down puts 'Slowing Down...' 1.0.step(0, -0.1) do |v| toggle_led((v * 10).to_i) @pin_vref.value = v sleep 2 end end def execute puts 'Rolling Forward.' @pin_in_1.on @pin_in_2.off speeding_up slowing_down stop_motor puts 'Rolling Backward.' @pin_in_1.off @pin_in_2.on speeding_up slowing_down end end if $0 == __FILE__ motor_sample = MotorSample.new motor_sample.execute end
上記のサンプルコードを動かすと、モーターが順方向へ回転し始めて徐々に速度を上げ、最高速度に到達すると徐々に速度を下げていきます。そして今度は逆方向へ同様のことを行って終了します。
ON/OFF の切り替えで良いピンは PiPiper::Pin を new していますが、 PWM を使いたいピンについては PiPiper::Pwm を new しています。
def initialize puts 'Initializing MotorSample...' @pin_led = PiPiper::Pin.new(pin: PIN_LED, direction: :out) @pin_in_1 = PiPiper::Pin.new(pin: PIN_IN_1, direction: :out) @pin_in_2 = PiPiper::Pin.new(pin: PIN_IN_2, direction: :out) @pin_vref = PiPiper::Pwm.new(pin: PIN_VREF) @pin_vref.value = 0 end
TA7291P ではモーターの ON/OFF/回転方向 の切り替えは IN1 と IN2 の ON/OFF の組み合わせて決定します。
- IN1: OFF IN2: OFF => 停止
- IN1: ON IN2: OFF => 順方向へ回転
- IN1: OFF IN2: ON => 逆方向へ回転
例えば順方向へ回転させるには下記のように指定しています。
@pin_in_1.on @pin_in_2.off
PWM を使用しているピンについてはその値を 0 〜 1.0 で指定することで出力を調整できます。今回は例えば下記のように徐々に値を増加させ、モーターの回転速度を上げていったりしています。
def speeding_up puts 'Speeding Up...' 0.step(1.0, 0.1) do |v| toggle_led((v * 10).to_i) @pin_vref.value = v sleep 2 end end
Beacon の電波強度に応じてモーターの回転速度を調節する
指定した速度で動かすだけでは面白くないので、今度は BLE と組み合わせ、 Beacon の電波強度(RSSI)に応じてモーターの回転速度を変化させてみたいと思います。
まずはモーターの動作をまとめた Motor クラスを作成します。内容としては単純に各ピンの初期化と、 ON/OFF、速度調節の値を設定する機能をまとめたものです。
require 'bundler/setup' require 'pi_piper' class Motor def initialize(pin_vref, pin_in_1, pin_in_2) @pin_in_1 = PiPiper::Pin.new(pin: pin_in_1, direction: :out) @pin_in_2 = PiPiper::Pin.new(pin: pin_in_2, direction: :out) @pin_vref = PiPiper::Pwm.new(pin: pin_vref) @pin_vref.value = 0 end def stop @pin_in_1.off @pin_in_2.off end def forward @pin_in_1.on @pin_in_2.off end def backward @pin_in_1.off @pin_in_2.on end def adjust(value) @pin_vref.value = value end end
BLE で Beacon の Advertisement を受け取って Motor を操作するコードは下記のようにしました。今回は Beacon として以前にも使ったことのある、 Texas Instruments の SensorTag を使用しています。
require 'bundler/setup' require 'dbus' require './motor.rb' class MotorTest SERVICE_NAME = 'org.bluez' SERVICE_PATH = '/org/bluez' ADAPTER = 'hci0' DEVICE_IF = 'org.bluez.Device1' DBUS_PROPERTIES_IF = 'org.freedesktop.DBus.Properties' PROPERTIES_CHANGED_SIGNAL = 'PropertiesChanged' DEVICE_NAME = 'CC2650' PIN_VREF = 18 PIN_IN_1 = 27 PIN_IN_2 = 4 PIN_LED = 17 def initialize @bus = DBus::system_bus @bluez = @bus.service(SERVICE_NAME) @adapter = @bluez.object("#{SERVICE_PATH}/#{ADAPTER}") @adapter.introspect @motor = Motor.new(PIN_VREF, PIN_IN_1, PIN_IN_2) @motor.forward @led = PiPiper::Pin.new(pin: PIN_LED, direction: :out) end def execute @adapter.SetDiscoveryFilter({'Transport' => 'le'}) @adapter.StartDiscovery @led.on target_device = nil while(target_device.nil?) @adapter.introspect @adapter.subnodes.each do |node| device = @bluez.object("#{SERVICE_PATH}/#{ADAPTER}/#{node}") device.introspect next unless device.respond_to?(:GetAll) properties = device.GetAll(DEVICE_IF).first name = properties['Name'] rssi = properties['RSSI'] next if name.nil? next unless name.downcase.include?(DEVICE_NAME.downcase) target_device = device break end sleep(0.1) end target_device.default_iface = DBUS_PROPERTIES_IF target_device.on_signal(PROPERTIES_CHANGED_SIGNAL) do |_, v| rssi = v['RSSI'] vref_value = [[(100 + rssi).to_f / 100, 1.0].min, 0].max puts "RSSI: #{rssi} VREF: #{vref_value}" @motor.forward @motor.adjust(vref_value) end main = DBus::Main.new main << @bus main.run end end if $0 == __FILE__ motor_test = MotorTest.new motor_test.execute end
BLE の操作については DBus と BlueZ を使い、 initialize で Bluetooth アダプタの初期化と Motor クラスの初期化を行っています。
def initialize @bus = DBus::system_bus @bluez = @bus.service(SERVICE_NAME) @adapter = @bluez.object("#{SERVICE_PATH}/#{ADAPTER}") @adapter.introspect @motor = Motor.new(PIN_VREF, PIN_IN_1, PIN_IN_2) @motor.forward @led = PiPiper::Pin.new(pin: PIN_LED, direction: :out) end
execute メソッドの中でBLEデバイスのスキャンを行っていますが、検索対象を絞るために SetDiscoveryFilter で BLE デバイスに限定しています。
@adapter.SetDiscoveryFilter({'Transport' => 'le'}) @adapter.StartDiscovery
RSSI が変わると PropertiesChanged シグナルが送られてくるので、シグナルを受け取った場合にモーターの速度を変更するようにしています。 PWM ピンに設定できる値は 0 〜 1.0 なので、その範囲を出ないようにしています。今回は RSSI を厳密に取り扱うことが目的ではないので、上限と下限を超える場合は超える分をカットしている形です。
target_device.default_iface = DBUS_PROPERTIES_IF target_device.on_signal(PROPERTIES_CHANGED_SIGNAL) do |_, v| rssi = v['RSSI'] vref_value = [[(100 + rssi).to_f / 100, 1.0].min, 0].max puts "RSSI: #{rssi} VREF: #{vref_value}" @motor.forward @motor.adjust(vref_value) end
このコードを動かすと、 SensorTag を Raspberry Pi に近づけるとモーターの回転速度が上がり、遠ざけると回転速度が下がります。SensorTag を動かしてからシグナルが送られるまでにタイムラグがあるのですが、とりあえずイメージした感じにはなっているので、今後調べられそうであればシグナル発行のタイミングも調べてみたいと思います。
まとめ
今回初めて外部電源(電池ボックス)を使ってモーターを動かすことに挑戦してみました。実は途中で一度、横に置いておいた電池ボックスのリード線がいつの間にかショートして、電池ボックスを一つ焦がしてダメにしてしまい、やっぱり電気は怖いなぁと改めて思ったのですが、モーターなどを動かすことができると色々とやれる幅も広がって楽しいですね。モータードライバなどは物によって使い方が違うので、データシートなどから読み解くのは少し大変ですが、色々触って試してみたいと思います。