Upload
keng-ichi-ahagon
View
3.158
Download
0
Embed Size (px)
DESCRIPTION
Firmata はシリアル通信を介してPC等のホストマシンから Arduino やその他マイコンボード等のデバイスを制御するためのプロトコルです。 \PHPMake\Firmata は Firmata の PHP ライブラリです。
Citation preview
Arduino を PHP で制御する
@oasynnoumPHPカンファレンス関西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 ライブラリ、PHPMake\Firmataを使えば
ArduinoとWebを連携させたアプリが
簡単にPHPで作れる!
Arduino を PHP で制御する
ご清聴ありがとうございました
m(_ _)m
終
お話したいこと
● firmataとはこれの次に述べる PHPMake\Firmata API の根底を理解してもらう目的で firmata プロトコルを少し細かく説明
– PHPのカンファレンスで話す内容としては不適切かもしれません。PHP関係なさすぎて
● PHPMake\Firmata について– 導入方法
– 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/45c67d3c18351d23eb979bc2ff32537dac180560/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– 他のデバイス、マイコン等とシリアル通信を行う
PHPMake\Firmataについて
● Firmataのホスト側実装● 先に述べたプロトコルの詳細を隠蔽
– PHPMake\Firmata\Device が対象のデバイスを表現● コンストラクタは Capability チェックなど、まず必要となりそうなやりとりをデ
バイスと行う
– digitalWrite(), analogWrite(), ピンを監視する仕組みなどをフレームワークとして提供する
● PHP による firmata のホスト側実装は他にhttps://github.com/ThomasWeinert/carica-firmataがある– 両者の API の違いはピン監視のAPIにおいて interface を用いるの
か callable を用いるのか程度
– callable 多用するのが嫌いな人は PHPMake\Firmata を使うといいと思う
PHPMake\Firmataについて
● WebSocket の機能(余計なお世話)– Ratchet との連携により WebSocket サーバーの機能
も提供
– フレームワークユーザーがピン監視のポーリングと Ratchet が提供するイベント駆動の API を組み合わせて整合性を取りつつ WebSocket アプリを作るのは多分めんどくさいだろうと思ったので
– しかし、 Ratchet の API は少し調べただけなので、恐らく今のところあまりいい実装ではない
PHPMake\Firmataについて
● ホスト側実装のキモは parser だと思う– バッファはデジタルピン、アナログピンの状態通知など
色々なデータが混じる● 例えば REPORT_FIRMWARE のクエリを投げた直後に受け取るバッファ先頭のデータは必ずしも START_SYSEX, REPORT_FIRMWARE, END_SYSEXではない
– 動かない下手くそなパーサーを何度も書いては捨てた
– 今のところパーサーは動くが、依然下手くそ
導入
● Composer で簡単に firmata プロジェクト作れる!しかし、依存の PHPMake\SerialPort は拡張モジュールなのでこれを先に手動でインストールする必要がある– ちなみに、先に上げた carica-firmata もシリアル通信部分に
PHPMake\SerialPort がインストールされていれば使うようになっています
● PHPMake\Firmata のインストールについてはhttp://sandbox.n-3.so/Gorilla/download/を参照してください
● ArduinoにStandardFirmataを書き込む必要がありますhttp://qiita.com/oasynnoum@github/items/91aed309bd9de8af8d0aを参照してください
LEDを点滅させよう(Lチカ)
● 要約:– Device のインスタンスを作って、 digitalWrite() をコール
– http://qiita.com/oasynnoum@github/items/91aed309bd9de8af8d0a
<?phprequire dirname(__FILE__) . '/vendor/autoload.php';
/* initialize the device */$device = new PHPMake\Firmata\Device('/dev/ttyACM0');/* for Windows */// $device = new PHPMake\Firmata\Device('COM3');
$pin = 13;
for ($i = 0; $i < 3; ++$i) { $device->digitalWrite($pin, PHPMake\Firmata::HIGH); // light sleep(1); $device->digitalWrite($pin, PHPMake\Firmata::LOW); // unlight sleep(1);}
デジタル入力を読む
● コードは少し長いので省きました– http://qiita.com/oasynnoum@github/items/cd9f90cfec8c0
18a47c6を参照してください
● $dev->setPinMode(13, PHPMake\Firmata::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, PHPMake\Firmata::ANALOG);
PWM(アナログ出力)
● $dev->setPinMode(13, PHPMake\Firmata::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 は悪くない。悪いのは実装者の腕
PHPMake\Firmata の課題
● 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