0

Arduino FirmataのSysex Messageとは何か

Sysexコマンドを実装する為にArduino IDE 1.0.3内のコードを色々読んだ。
日本語どころか英語でもちゃんと解説している文書が見当たらなかったので、忘れる前にメモしておく。


Firmata

Arduinoを操り人形モードにしてPCから指令だして逐次動作させるプロトコルのこと。Midiを元にしている。
http://firmata.org/wiki/V2.1ProtocolDetails

使う分には、だいたいRPCのような物だと思っていて良い。

パソコンとArduinoを連動させるプログラムを作る時、両側のプログラムを書いてシリアル通信で連動させるのは面倒なので、パソコン側プログラムの中にArduinoのプログラムを埋め込めるようにしよう!!ということ。

Arduino本体のコード読むと、Atmel AVRマイコン専用ではなく他社のマイコンでも動かせるように物凄い実装が抽象化されているカッコいいコードな事がわかる。
実際基本的な命令セットのラッパー部分を書き換えればMSP430 LaunchPadもArduino化する
Firmataも同じように、Arduino専用プロトコルではない。例えばGainerやFunnelでも使っている。

Arduinoを呼び出す側も色々な言語での実装がある。俺が作った物では
がある。


Sysex Message

PC-Firmata間で2byte以上のデータをやりとりする時に使う。
Arduino IDE付属のStandardFirmataでは、Arduino→PCへのDIGITAL_MESSAGE, ANALOG_MESSAGE, REPORT_VERSIONの3つで既に使われている。

自作のコマンドをArduinoに実行させるのにも使える。
例えばサーボモーターを角度0〜180まで3秒間かけて動かす処理をする時、普通にFirmata上のservoWrite(サーボ番号, 角度)関数を使うとsleepを駆使して小刻みにPCからArduinoにコマンドを送り続ける。
「n秒かけて角度aからbまで回す」というSysexコマンドを定義すれば、命令は1回で済む。
あとは赤外線リモコンのような38KHzをきっちり送るような処理にも使える。LEDのon/off指令をPCから逐次送信していては間に合わないので、sysexコマンドを自作する。(FlashとArduinoで赤外線リモコン


[START_SYSEX, コマンド名, 7bitを31個分のデータ, END_SYSEX] を1セットとして送信する。
8bit目が立っているbyteはMidiではコマンドなので、7bitになっている。

http://firmata.org/wiki/V2.1ProtocolDetails#Sysex_Message_Format

/* Generic Sysex Message
* 0 START_SYSEX (0xF0)
* 1 sysex command (0x00-0x7F)
* x between 0 and MAX_DATA_BYTES 7-bit bytes of arbitrary data
* last END_SYSEX (0xF7)
*/

MAX_DATA_BYTESは Arduino.app/Contents/Resources/Java/libraries/Firmata/Firmata.h で定義されている。

デフォルトでコマンド1つ + 7bitデータ31個の合計32個が送れる。

#define MAX_DATA_BYTES 32 // max number of data bytes in non-Sysex messages
コメントにsysex以外のメッセージって書いてあるけど、Firmata.cppのコードを追っていくとsysexのデータを格納する配列サイズはMAX_DATA_BYTESで指定されている。


Firmata.hで定義されているSysexコマンド。0x00-0x0Fはユーザが自分のアプリ内で定義していいらしい。
// extended command set using sysex (0-127/0x00-0x7F)
/* 0x00-0x0F reserved for user-defined commands */
#define SERVO_CONFIG 0x70 // set max angle, minPulse, maxPulse, freq
#define STRING_DATA 0x71 // a string message with 14-bits per char
#define SHIFT_DATA 0x75 // a bitstream to/from a shift register
#define I2C_REQUEST 0x76 // send an I2C read/write request
#define I2C_REPLY 0x77 // a reply to an I2C read request
#define I2C_CONFIG 0x78 // config I2C settings such as delay times and power pins
#define EXTENDED_ANALOG 0x6F // analog write (PWM, Servo, etc) to any pin
#define PIN_STATE_QUERY 0x6D // ask for a pin's current mode and value
#define PIN_STATE_RESPONSE 0x6E // reply with pin's current mode and value
#define CAPABILITY_QUERY 0x6B // ask for supported modes and resolution of all pins
#define CAPABILITY_RESPONSE 0x6C // reply with supported modes and resolution
#define ANALOG_MAPPING_QUERY 0x69 // ask for mapping of analog to pin numbers
#define ANALOG_MAPPING_RESPONSE 0x6A // reply with mapping info
#define REPORT_FIRMWARE 0x79 // report name and version of the firmware
#define SAMPLING_INTERVAL 0x7A // set the poll rate of the main loop
#define SYSEX_NON_REALTIME 0x7E // MIDI Reserved for non-realtime messages
#define SYSEX_REALTIME 0x7F // MIDI Reserved for realtime messages
// these are DEPRECATED to make the naming more consistent
#define FIRMATA_STRING 0x71 // same as STRING_DATA
#define SYSEX_I2C_REQUEST 0x76 // same as I2C_REQUEST
#define SYSEX_I2C_REPLY 0x77 // same as I2C_REPLY
#define SYSEX_SAMPLING_INTERVAL 0x7A // same as SAMPLING_INTERVAL


自作SysexコマンドをArduino側に登録する

登録すると、PC側からSysexコマンド発行すればArduino上で実行できる。

Arduino.app/Contents/Resources/Java/libraries/Firmata/Firmata.cpp にあるattach関数にsysex command (0x00-0x7F)を引数として、コールバックを登録する。

http://firmata.org/wiki/V2.1ProtocolDetails#Sysex_Message_Format を見ると、0x00~0x0Fはユーザが自由に使って良い様に予約されているんだけど、登録しても現在Arduino IDEに付属しているFirmata.cpp内では呼び出されるように実装されてない。

方法は2つで、

1. SysexのSTRING_DATAコマンドが来た時のイベントを登録する
2. Sysexを受信した時の関数を呼び出すように、自分でswitch文に追加する
のどちらか。


1. STRING_DATA (0x71)コマンドが来た時のイベントを登録

STRING_DATAは自作アプリで使える。

STRING_DATA (0x71)はFirmata.cpp内でコールバックとして登録できるようになっているが、Arduino IDE付属のStandardFirmata.inoからは使われていない。

Arduino IDEに入っているスケッチ例のEchoStringで使ってる。
#include <Firmata.h>
byte analogPin;

void stringCallback(char *myString)
{
Firmata.sendString(myString);
}

void sysexCallback(byte command, byte argc, byte*argv)
{
Firmata.sendSysex(command, argc, argv);
}

void setup()
{
Firmata.setFirmwareVersion(0, 1);
Firmata.attach(STRING_DATA, stringCallback);
Firmata.attach(START_SYSEX, sysexCallback);
Firmata.begin(57600);
}

void loop()
{
while(Firmata.available()) {
Firmata.processInput();
}
}


2. Sysexを受信した時のコールバック関数を登録

StandardFirmataを改造して、 void sysexCallback(byte command, byte argc, byte *argv) 関数内のswitch文に書き足すといい。
I2C関係もここで定義されているので破壊しないように注意しつつ追加する。

Arduino IDEに入っているStandardFirmataにLED点滅コマンドを追加してみた
追加した箇所は差分を見ればわかりやすいと思う。


自作したSysexコマンドを呼び出すPC側のプログラム


試しに、指定したLEDを指定回数、指定間隔(ミリ秒)で点滅させるsysexコマンド(0x01)を作る。


Ruby用Firmataライブラリで、sysexコマンドを送るコード。
require 'rubygems'
require 'arduino_firmata'

arduino = ArduinoFirmata.connect ARGV.shift
puts "firmata version #{arduino.version}"

## regist event
arduino.on :sysex do |command, data|
puts "command : #{command}"
puts "data : #{data.inspect}"
end

## send sysex command
arduino.sysex 0x01, [13, 5, 2] # pin13, blink 5 times, 200 msec interval
arduino.sysex 0x01, [11, 3, 10] # pin11, blink 3 times, 1000 msec interval

loop do
sleep 1
end


Arduino側。
EchoStringのサンプルを改造した。
受け取ったSysexコマンドとデータをそのまま返すようにしてある。
#include <Firmata.h>

void setup()
{
Firmata.setFirmwareVersion(FIRMATA_MAJOR_VERSION, FIRMATA_MINOR_VERSION);
Firmata.attach(START_SYSEX, sysexCallback);
Firmata.begin(57600);
}

void loop()
{
while(Firmata.available()) {
Firmata.processInput();
}
}

void sysexCallback(byte command, byte argc, byte*argv)
{
switch(command){
case 0x01: // LED Blink Command
if(argc < 3) break;
byte blink_pin;
byte blink_count;
int delayTime;
blink_pin = argv[0];
blink_count = argv[1];
delayTime = argv[2] * 100;

pinMode(blink_pin, OUTPUT);
byte i;
for(i = 0; i < blink_count; i++){
digitalWrite(blink_pin, true);
delay(delayTime);
digitalWrite(blink_pin, false);
delay(delayTime);
}
Firmata.sendSysex(command, argc, argv); // callback
break;
}
}


StandardFirmataに同様の機能を追加してみた。この方法なら、いつものanalogReadやdigitalWrite機能が使えるままのArduinoに、赤外線学習リモコン機能を追加したりとかできる。
追加した箇所は差分を見るとわかりやすいと思う。


Android版からもRubyと同様にコマンドを送れる。
byte[] data = {11, 3, 10};  // pin11, blink 3 times, 1000 msec interval
arduino.sysex((byte)0x01, data);


Firmata.sendSysex

Arduino側からPCにsysexコマンドを送るFirmata.sendSysex(byte command, byte argc, byte*argv)関数は、内部でsendValueAsTwo7bitBytes(int value)を呼び出している。
sysexで送れるデータは7bitまでなので、8bit以上と7bit以下を2byteに分けて送信するために使われている。
sendSysexに14bitの値を入れれるならいいんだけど、byte配列しか渡せなくて効率が良くないのでFirmata.send関数を直接使いたいがこっちはprivateになっていて使えない。

幸い、PC側はバッファサイズは無限に余裕があるので何byteでも1度に送って問題ない。

0

Androidのライブラリを作るときのメモ

次にまたAndroid用のライブラリを作るときのために、Android版Arduino Firmataを作った時に色々やった事をメモする。
最終的にCUIだけで全部ビルドしてリリースできるようになったし、rubygemなみにメンテしやすい構成ができたと思われ

ソースはここに公開してる https://github.com/shokai/ArduinoFirmata-Android


問題

EclipseでAndroidプロジェクトを作って、パッケージエクスプローラのフォルダを右クリックして「エクスポート」からjarファイルが作れる。プロジェクトの中で使っているファイルの一部分だけをライブラリとして切り出せる。


でも
  • jarに切り出すリリース作業をGUIでやるとミス起きそう
  • gitで管理しにくそう
  • jarにREADMEとかを含めるのが面倒
  • ライブラリの性質上実機で動作確認しなければならず、手元のMacで自動テストできない
  • 作ったjarを複数のサンプルアプリで動かすのが面倒
  • usb-serial-for-androidというgoogle製ライブラリを使うが、リポジトリに含めたくないのでgit submoduleで読み込みたい
などの問題があって、これは継続的にメンテするのが面倒くさそうだし、半年ぶりに更新する時に手順を全部忘れたりしそうだなあと思った。
rubygemみたいに久しぶりにメンテしても普通にリリースできる環境を作りたい。


Eclipseやめてターミナルでantとmakeとemacsで作ることにした。
Eclipseは適当に書いても足りないクラスをimportしてくれたり、アプリの画面をドラッグアンドドロップで作れるけど、ライブラリを作る時には意味ない。


開発環境

MacのターミナルとEmacsでAndroidアプリを作る環境を整える。
橋本商会 » Android開発環境をインストールしなおした
橋本商会 » emacsでAndroidのAPIも入力補完する


フォルダ構成


こんな感じ
├── History.txt
├── LICENSE.txt
├── Makefile (これ重要!)
├── README.md
├── arduino-firmata.jar (リリース版jarファイル)
├── samples (サンプルアプリを入れる場所)
│   └── FirmataSampleApp(サンプルアプリ)
│   ├── AndroidManifest.xml
│   ├── Makefile (これも重要!)
│   ├── libs
│   │   ├── arduino-firmata.jar (実は../../../arduino-firmata.jarへのシンボリックリンク)
│   │   └── usb-serial-for-android-v010.jar (これは実体)
│   └── (略)
├── src
│   ├── com
│   │   └── hoho(make initするとsubmoduleからコピーされてくるが、git ignoreされてる)
│   │   └── android
│   │   └── usbserial
│   └── org
│   └── shokai
│   └── firmata(ライブラリ本体)
│   ├── ArduinoFirmata.java
│   ├── ArduinoFirmataEventHandler.java
│   ├── ArduinoFirmataException.java
│   └── Error.class
└── usb-serial-for-android (git submodule。make initすると最新版をpullしてくる)


arduino-firmata.jarのビルド方法


Android SDKへのクラスパスを通して
% export CLASSPATH=$CLASSPATH:/usr/local/var/lib/android-sdk/platforms/android-13/android.jar
% export LC_ALL=en

ビルドする
% make init
% make

これを実現するMakefile
all: build

init:
cd usb-serial-for-android &&\
git pull origin master &&\
cd ../ &&\
cp -R usb-serial-for-android/UsbSerialLibrary/src/com src/

compile:
cd src &&\
javac org/shokai/firmata/ArduinoFirmataException.java &&\
javac org/shokai/firmata/ArduinoFirmataEventHandler.java &&\
javac org/shokai/firmata/ArduinoFirmata.java

build: compile
cd src &&\
jar cvf arduino-firmata.jar ./org &&\
jar uvf arduino-firmata.jar ../README.md &&\
jar uvf arduino-firmata.jar ../LICENSE.txt &&\
jar uvf arduino-firmata.jar ../History.txt &&\
mv arduino-firmata.jar ../

make initでsubmoduleのusb-serial-for-androidをgoogle codeのgitからpullしてきて、必要なソース(com.hoho.android.usbserial)だけsrcにコピーする。
コピーされたソースコードは.gitignoreに書いてあるのでコミットされない。

make buildでライブラリを全部コンパイルしてjarを作って、READMEやLICENSEを追加してルートディレクトリに置く。
これをgithubのmasterブランチにpushすると、github pagesで作ったArduino Firmata on Androidのページのチュートリアルのところに出るようになってる。


サンプルアプリのビルド方法

samples/FirmataSampleApp/ に移動して、
% make setup
で自分のAndroid開発環境の設定が読み込まれる


% make
% make install
これでサンプルアプリがインストールされる


Makefile
all: clean build
setup:
android update project --path `pwd` --name 'FirmataSample'
build:
ant debug
install:
adb install -r bin/FirmataSample-debug.apk
uninstall:
adb uninstall org.shokai.firmata.sample
logcat:
adb logcat | grep firmata
clean:
/bin/rm -r -f bin/classes

毎回.classファイルを削除してからビルドしている。
ant debugはソースのjavaファイルが更新された時のみ.classファイルに新しくコンパイルしなおすので、
毎回消してやらないとarduino-firmata.jarだけが更新された時に正しくビルドされない。

libsディレクトリにarduino-firmata.jarがあるが、これはルートディレクトリへのシンボリックリンク。
usb-serial-for-android.jarは http://code.google.com/p/usb-serial-for-android/からダウンロードしてきたもの。

antをmakeで使うのはなんかよくわからない気もするが、まあいいや。


更新作業

githubのissueを立てて、要求とおおまかな設計を書く。
ローカルでissue名のブランチを切って、commitメッセージの最後に#7とかissue番号を入れてcommitする。
githubにpushするとissueのページに書き込まれるので進捗が管理しやすい。
機能が完成したらmasterブランチにmergeする。



リリース作業は
History.txtにこれまでのcommitメッセージまとめを書いて、
ArduinoFirmata.javaの定数VERSIONを書き換えてからjarにビルドしなおして、
git tagをうってgithubにpushする。

だいたいこんな感じか。

1

Android版ArduinoFirmataを作った

AndroidのJavaの中で

int sensor = arduino.analogRead(1);
arduino.servoWrite(9, 155);
とか書けて便利です。

ここにチュートリアルとか、すぐ試せる無署名のapkとか置いといたのでどうぞ
ArduinoFirmata on Android

ArduinoFirmata on Android


先週ArduinoとAndroidを接続できたので、Firmataプロトコルの移植はRubyでもやった事だしさくっとAndroid版も作ってみた。



Android OS 3.2以上でUSBホスト機能がある端末とStandard FirmataをインストールしたArduinoを、USBホストケーブルで接続する。
Arduino IDE -> [File] -> [Examples] -> [Firmata] -> [StandardFirmata] でインストールできる。
(Firmataを入れたArduinoは、シリアル通信からの命令で動く操り人形モードになります)

Android用ライブラリはjarファイルにしてArduinoFirmata on Androidに置いてある。
Androidプロジェクトのlibsディレクトリに入れれば使える。


Arduino Duemillanove, UNO, Seeduino v2で動作確認した。
USBホストケーブルはこれこれが使えた。


ご意見ご感想はtwitterやgithubのissueやpull requestなどお待ちしております

0

RubyからArduinoを操作できるようにFirmataプロトコルを移植した

Javaから移植した
Arduino Firmata on Ruby

インストール

gem install arduino_firmata
Ruby 1.8.7と1.9.2で動作確認してる。serialport gemを使ってるのでJRubyでは動かない。


できること・使い方

RubyからArduinoのデジタル・アナログの入出力が使える。

例えばTwの内部APIと温度センサLM35DZと組み合わせると、9行で温度をtweetできたりして便利。
#usr/bin/env ruby
require 'rubygems'
require 'arduino_firmata'
require 'tw'

arduino = ArduinoFirmata.connect "/dev/tty.usbmodem621"
puts temp = arduino.analog_read(1)*100*5/1024 # 摂氏1度毎に10mV出力なので計算する
client = Tw::Client.new
client.auth "shokai"
client.tweet "現在の温度 #{temp}度"


あとはSinatraと組み合わせて20行ぐらいでブラウザからArduino動かしたりとかもできる



Firmataとは


http://firmata.org
Firmata(ふぁるまーた)はProcessingからArduinoを制御するために5年ぐらい前に作られたプロトコルだ。

Arduinoに Firmataファームウェア を書き込むと、USBシリアル通信経由でパソコンとやりとりしてIOを操作したり色々やってくれる。
プログラムを書く側はArduinoのコードを書いたりパソコンとの通信を意識したりする必要が無くなり、
手元のRubyやProcessingだけを書けば済むのでとても楽になる。
ただし、通信する分だけ遅くなるので超細かくモーターを制御する等には向いていない。


Processing以外にもMax/MSPやopenFrameworksなど色々なプログラミング言語からArduinoを制御する時に使われるようになったり、
また逆に、Arduino以外のボード(Funnel IOなど)をProcessingから制御するのにも使われてたりしている。

いろんな言語とボードを接続するのに便利なプロトコルなのだが、
PhidgetsがあるんだからArduinoはスタンドアロンで使うべきじゃないの?という印象だった。2週間前までは。

八王子の山奥のopenFrameworksセミナーで使ったGrove Systemが拡張パーツの多さ・入手の容易さ・自分で拡張しやすいという点でPhidgetsを食ってしまっていたので、Rubyでも使いたくなった。
つまり Ruby+Firmata+Arduino+GroveSystem 最強なのでは。全部オープンソースだし。

実装はそんなに難しくなくて昨日お風呂にはいる時間を削って作ってみたらすぐ動いたのでgemにした。
Arduino playground – Processing にあったJavaのFirmataライブラリをまるごとRubyで書きなおした。


Rubyっぽい書き方


せっかくRubyで書いてるので使いやすいAPIにしたい。

デバイス名を省略してconnectしても適当に探して接続したりとか、
ADコンバータにcallback登録できるようにしてみた。
arduino = ArduinoFirmata.connect

arduino.on_analog_changed 0 do |value|
puts "analog pin 0 changed #{value}"
arduino.analog_write 11, value
end

ブロックを使うと、arduino.analog_readをanalog_readだけに省略できたりとか
ArduinoFirmata.connect do
puts "firmata version #{version}"

30.times do
an = analog_read 0
analog_write 11, an
sleep 0.01
end
end


簡単なチュートリアルも Arduino Firmata on Ruby に書いたし、
今後も機能追加するので(未実装のon_digital_changed等)見ると良いと思います。