SlideShare a Scribd company logo
Arduino を PHP で制御する
@oasynnoum
PHPカンファレンス関西2014
自己紹介
● @oasynnoum
● PHP が好きです
● 物を動かせるものに惹かれます
● シリアルポートを制御するモジュール
– http://sandbox.n-3.so/Gorilla/
● USB制御するモジュール(libusbラッパー)
– http://oasynnoum.hatenablog.com/entry/2013/03/22/013251
● CD,DVDドライブを(☝ ՞ ՞)☝ ™ウイーンਊ するモジュール
– https://github.com/oasynnoum/php-eject
– http://eject.kokuda.org/
● 工作の腕はお察し下さい
まとめ
firmata プロトコルを実装した PHP ライブラリ、
PHPMakeFirmataを使えば
ArduinoとWebを連携させたアプリが
簡単にPHPで作れる!
Arduino を PHP で制御する
ご清聴ありがとうございました
m(_ _)m
終
お話したいこと
● firmataとは
これの次に述べる PHPMakeFirmata API の根底を理解して
もらう目的で firmata プロトコルを少し細かく説明
– PHPのカンファレンスで話す内容としては不適切かもしれ
ません。PHP関係なさすぎて
● PHPMakeFirmata について
– 導入方法
– API
– デモ
firmata プロトコル概要
● firmata はPCのようなホストマシンから Arduino の
ようなデバイスを制御するためのプロトコル
● firmata はシリアル通信の上のアプリケーションプ
ロトコル
firmata プロトコル概要
● ホストからデバイスにリクエストを出し、
デバイスはそれに応じ動作し、必要に応じて
レスポンスを返す
– 必ずしも「リクエスト対レスポンス」ではない
● デバイスが断続的にピンの状態を通知したり
● ホストから一方的にピンの状態を変化させたり
● 通信データの形式は MIDI メッセージ形式
● 色々なデバイス側の実装がある
– https://github.com/firmata
firmata プロトコル概要
● 最大0xF個のデジタルポートをサポート
– 一つのポートは8個のデジタルピンを持つ
– つまり最大128個のデジタルピンをサポート
● 最大0xF個のアナログ入力ピンをサポート
firmata プロトコル概要
● 用途(メリット)
– もっとお手軽プロトタイピング(スクリプティング)
● ホスト側でプログラムを書ければ言語は何でもいい
● あるデバイスが firmata を実装しているとわかれば、その
デバイス専用のプログラマ(ライター)・IDE等が無くてもいい
– ホストとデバイスが協調動作するようなアプリ開発
– 場合によってはホストから電源が供給される
● デメリット
– ホストとデバイスはケーブルで繋がる必要がある
● firmata over XBeeとかアイディアはあるらしい・・・
– ホストが無いと何も出来ない
● 単にデバイスだけを動かせばいいようなアプリの場合、
ホストの計算資源を要求するため無駄が多い
StandardFirmata 概要
● Firmata のデータ形式は大雑把に
– シンプルな固定長のコマンド
– 可変長データを内包するコマンド(SysEx)
● #define START_SYSEX 0xF0
● #define END_SYSEX 0xF7
に分けられる
● ホストからの接続時にデバイスの状態がリセットされ
る
StandardFirmata 概要
● リセット時、デバイスから特定のメッセージが送られる
● デバイス初期化実装例
https://github.com/firmata/arduino/blob/45c67d3c18351d23eb979bc2
ff32537dac180560/Firmata.cpp#L66
void FirmataClass::begin(long speed)
{
Serial.begin(speed);
FirmataSerial = &Serial;
blinkVersion();
printVersion();
printFirmwareVersion();
}
ファームウェア情報の取得
● REPORT_FIRMWARE
– #define REPORT_FIRMWARE 0x79
– pack('C3',
START_SYSEX,
REPORT_FIRMWARE,
END_SYSEX);
● ファームウェア名(StandardFirmata.ino)と、
バージョン番号がレスポンスとして返ってくる
● ただし、この情報は前述したリセット時の特定のデータに
含まれるため、このクエリを発行する場面は稀
ピンの機能を調べる
● デバイスに存在するピン一つ一つについて、
それが持つ機能を返す
● つまり、ピンの総数とピンそれぞれの機能がこのコ
マンドでわかる
● CAPABILITY_QUERY
– #define CAPABILITY_QUERY 0x6B
– pack('C3',
START_SYSEX,
CAPABILITY_QUERY,
END_SYSEX);
ピンの機能を調べる
● 定義されている Capability は次の通り
– digital input
– digital output
– analog read
– PWM
– servo
– I2C
● このコマンドはGUIアプリケーションの初期化時に有用
– ピンの数、機能に応じたコントロールを配置する
デジタル出力
● 3バイトのコマンド
● 先頭バイトは DIGITAL_MESSAGE|ポート番号
– 下位4bitはポート番号
– #define DIGITAL_MESSAGE 0x90
– $n番目のピンがあるポート番号は floor($n/8)
● ピン番号を8で割り、端数を切り捨て
● つまりピンは8毎にグループ化される
● 6番目のピンはポート0に、13ピンはポート1にある
● 2バイト目、3バイト目はポート中のピン状態を示す
デジタル出力
● 1バイト目例:0x90|1
– ポート1に対するデジタル出力
● 2バイト目例:01011011
– 最上位ビットは 0 固定
– 最下位ビットから順にポート中のピン番号 0 ~ 6 の状態を示す
● 3バイト目例:00000001
– 最上位ビットから2ビット目まで 0 固定
– 最下位ビットはポート中のピン番号 7 の状態を示す
● ポート 0 に対し偶数ピンをHIGHにするコマンド
● pack('C3', 0x90|0, 0b00101010, 0b00000001);
デジタル入力
● ポートの状態を監視し、変化があれば通知する
– 読み出さないとバッファにどんどんデータが溜まっていく
– 入力を受け取るアプリの場合、
この理由でポーリングが必要になる
● REPORT_DIGITAL
– #define REPORT_DIGITAL 0xD0
デジタル入力
● 2バイトのコマンドで監視、または監視をやめるポートを指定
● 1バイト目は REPORT_DIGITAL|ポート番号
– 下位8bitはポート番号
– 特定のピンの状態だけ教えてくれればいいよという訳にはいかない
– 5ピンはポート0に属するので、5ピンの状態が欲しい場合、そのポートの他
のピンの状態変化の通知も受け取ることになる
● 2バイト目は真偽値、0もしくは1
– 1で状態通知お願いします
– 0で状態通知、もう結構です
● pack('C2', REPORT_DIGITAL|0, 1);
– ポート0に属するピンの状態を通知
デジタル入力
● 指定したポート中のピンに状態変化があった場合
3バイトのデータを受け取る
● データの形式はデジタル出力のコマンドと同じ
アナログ入力
● デバイスがアナログピンの状態を監視し、変化があれば
通知する
● 監視要請のコマンドの形式は
デジタル入力のそれとほぼ同じ
● #define REPORT_ANALOG 0xC0
● pack('C2', REPORT_ANALOG|1, 1);
アナログピン1の状態を監視させる
● デジタル入力同様、読み出さないとバッファにデータが
蓄積されていく
アナログ入力
● 指定したアナログピンに状態変化があった場合
3バイトのデータを受け取る
● 1バイト目は監視要請のコマンドと同じ
– REPORT_ANALOG|ピン番号
● 2バイト目はピンの状態(値)の下位7bit
● 3バイト目はピンの状態(値)の上位7bit
● $pinState = (($first << 7) | $second) & 0xFF
その他主要コマンド
● PWM(アナログ出力)
– 単位時間あたりのピンがHIGHとなる時間を調整し、
デジタル(二値的)なピンにアナログ(多値的)な振舞をさ
せる
● Servo
– 特別な場合の PWM コマンド
● I2C
– 他のデバイス、マイコン等とシリアル通信を行う
PHPMakeFirmataについて
● Firmataのホスト側実装
● 先に述べたプロトコルの詳細を隠蔽
– PHPMakeFirmataDevice が対象のデバイスを表現
● コンストラクタは Capability チェックなど、まず必要となりそうなやりとりをデ
バイスと行う
– digitalWrite(), analogWrite(), ピンを監視する仕組み
などをフレームワークとして提供する
● PHP による firmata のホスト側実装は他に
https://github.com/ThomasWeinert/carica-firmataがある
– 両者の API の違いはピン監視のAPIにおいて interface を用いるの
か callable を用いるのか程度
– callable 多用するのが嫌いな人は PHPMakeFirmata を使うといい
と思う
PHPMakeFirmataについて
● WebSocket の機能(余計なお世話)
– Ratchet との連携により WebSocket サーバーの機能
も提供
– フレームワークユーザーがピン監視のポーリングと
Ratchet が提供するイベント駆動の API を組み合わせ
て整合性を取りつつ WebSocket アプリを作るのは多
分めんどくさいだろうと思ったので
– しかし、 Ratchet の API は少し調べただけなので、
恐らく今のところあまりいい実装ではない
PHPMakeFirmataについて
● ホスト側実装のキモは parser だと思う
– バッファはデジタルピン、アナログピンの状態通知など
色々なデータが混じる
● 例えば REPORT_FIRMWARE のクエリを投げた
直後に受け取るバッファ先頭のデータは必ずしも
START_SYSEX,
REPORT_FIRMWARE,
END_SYSEX
ではない
– 動かない下手くそなパーサーを何度も書いては捨てた
– 今のところパーサーは動くが、依然下手くそ
導入
● Composer で簡単に firmata プロジェクト作れる!
しかし、依存の PHPMakeSerialPort は拡張モジュール
なのでこれを先に手動でインストールする必要がある
– ちなみに、先に上げた carica-firmata もシリアル通信部分に
PHPMakeSerialPort がインストールされていれば使うように
なっています
● PHPMakeFirmata のインストールについては
http://sandbox.n-3.so/Gorilla/download/
を参照してください
● ArduinoにStandardFirmataを書き込む必要があります
http://qiita.com/oasynnoum@github/items/91aed30
9bd9de8af8d0a
を参照してください
LEDを点滅させよう(Lチカ)
● 要約:
– Device のインスタンスを作って、 digitalWrite() をコール
– http://qiita.com/oasynnoum@github/items/91aed309bd9de8af8d0a
<?php
require dirname(__FILE__) . '/vendor/autoload.php';
/* initialize the device */
$device = new PHPMakeFirmataDevice('/dev/ttyACM0');
/* for Windows */
// $device = new PHPMakeFirmataDevice('COM3');
$pin = 13;
for ($i = 0; $i < 3; ++$i) {
$device->digitalWrite($pin, PHPMakeFirmata::HIGH); // light
sleep(1);
$device->digitalWrite($pin, PHPMakeFirmata::LOW); // unlight
sleep(1);
}
デジタル入力を読む
● コードは少し長いので省きました
– http://qiita.com/oasynnoum@github/items/cd9f90cfec8c0
18a47c6
を参照してください
● $dev->setPinMode(13, PHPMakeFirmata::INPUT);
● 監視したいピンを reportDigitalPin() で指定する
– 前述のとおり、プロトコル自体はポート単位で監視・通知を行
うがフレームワークがそこを隠蔽しピン単位での監視・通知を
行う
● PinObserver を実装し、そのインスタンスを Device に
addDigitalPinObserver() で渡す
– ピン変化通知を notify() で受け取る
デジタル入力を読む
● デバイスから送られる通知を取得するため、
なるべく短い時間間隔でバッファを確認する必要がある
(ポーリング)
– このポーリングの事をデバイスループと呼んでます
– デバイスループに入るには Device の
run() メソッドをコールします
● 引数については後述
● デバイスループに入ると処理はそこでブロックされる
– run() 呼び出しの後に続くコードブロックが実行されるのはデ
バイスループが終了したあと
– このままだとフレームワークユーザーは何も出来ない
デジタル入力を読む
● LoopDelegate
– デバイスループ以降何も出来なくなることを回避するため用
意されたインターフェース
– このインスタンスを run() の引数に渡す
– LoopDelegate の tick() メソッドはそのループの間隔ごとに
コールされます
– getInterval() メソッドはループの間隔を定義します
● デバイスループが実行される最初にだけ Device 内部でコールさ
れます
● デバイスループを止めるには Device の stop() をコー
ルします
– 典型的には tick() 内から stop() とか
アナログ入力を読む
● デジタル入力とほぼ同じです
● ただし、 PinObserver のインスタンスは
addAnalogPinObserver() メソッドで Device に渡
します
● $dev->setPinMode(13, PHPMakeFirmata::ANALOG);
PWM(アナログ出力)
● $dev->setPinMode(13, PHPMakeFirmata::PWM);
● Device の analogWrite() をコールします
● 第一引数はピン、第二引数は値
● 第二引数の値のとり得る範囲は 0~上限値
● 上限はデバイス、ピンにより異なる
● Capability により定義されている
– $capability = $dev->getPin(13)->getCapability();
$pwmResolution = $capability->getResolutionPWM();
デモ1
● firmata-websocket-taste
– https://github.com/PHPMake/firmata-websocket-taste
– Taste は test のタイポ。
面白いと思ったのでそのままにしています。
● firmata.org が提供しているテストGUIアプリがUbuntuで動かな
かったため作った
● バグっぽいです
● Onsenui で作った
– http://onsenui.io/
● Onsenui は素晴らしい。
このデモがバグっぽいのは実装者のせい
● ピンモードを変えられるようにと思ってセグメントコントロールをつ
けましたが、今のところ実装していません
デモ2
● firmata-space-travel
– https://github.com/PHPMake/firmata-space-travel
● Arduino でしょぼい宇宙船制御コンソール
● phoria.js でなんちゃって太陽系
– http://www.kevs3d.co.uk/dev/phoria/
– phoria.js は悪くない。悪いのは実装者の腕
PHPMakeFirmata の課題
● firmata のメリットはそのままにデメリットを減じたい
– デメリット
● ホストとデバイスはケーブルで繋がる必要がある
● ホストが無いと何も出来ない
– 普通に Wiring 使えば?というのは無し
● 俺はぺちぱーだ!
● Wiring(Arduinoの開発言語というかフレームワーク)
● ドキュメントが無い
– 申し訳程度の Janglish で書かれた phpdoc オンリー
参考資料
● firmata.org
– http://firmata.org/wiki/Main_Page
● firmata/arduino
– https://github.com/firmata/arduino
● Arduino Uno/Leonardo で始める電子工作
– http://www.amazon.co.jp/dp/4877832963

More Related Content

Arduino を PHP で制御する