11

AndroidのUSBホスト機能でArduinoとシリアル通信する

超便利なライブラリ作ったのでこっち使うといいです → 橋本商会 » Android版ArduinoFirmataを作った

——-以下本文

なんか意外と簡単にできたし、まったくやってる人の情報が無いので書いておく。


Nexus7+Arduino(Seeduino)+サーボモータ を動かした


AndroidにArduino等のシリアルデバイスを直つなぎできる。
ADKやUSBホストシールドやIOIOと違って、むずかしい回路やプログラムはいらないし、電源もAndroid側から供給もできる。
マイコン側は単純にシリアル通信するだけ。Android側もUSBシリアルポートを探して接続するだけ。


Nexus7にはUSBポートとは別に充電ポートがあるのでそっちで充電させつつ、デバイス制御もできそう

必要な物は3つ

1. USBホスト機能に対応したAndroid端末
2. USBホストケーブル
3. Arduino等のUSBシリアルデバイス


USBホスト機能に対応したAndroid端末

Android 3.1(タブレット用)や4以上の端末はUSBホスト機能がある。
2012年夏ごろからの端末が該当していて、USBホストシールドやADKが必要なくなった。

Galaxy NexusやGalaxy S3以降でAndroid4以上の端末はokのはず。
Googleが各ベンダになるべくUSBホスト機能を持たせるように働きかけているってsonyの人が言ってた。
最近Nexus SにもAndroid4が配信されたけど、チップセットにUSBホストの機能が無いので無理。


USBホストケーブル

ようするに「AndroidにUSBキーボードやマウスを接続できる」と書いてあるケーブルのこと。
これをUSBポートに接続すると、AndroidはUSBホストモードになる。

USBケーブルは普通4ピンだ。VCC、GND、D+、D-ピンの4つ。
USB mini/microから、5ピンになった。IDピンが増えた。
USBホストケーブルを作るには、IDピンとGNDピンをショートさせるだけでいい。


これを買った。ぴったりで良い。


なお自作も可能。S2 » USBホストケーブルとUSBケーブルの違い にくわしく書いてある。
市販のUSB-マイクロUSB変換コネクタを分解して、IDピンを短絡させてる。


昔のArduinoと今後のArduino

Arduino UNO以降は、AVRマイコン内蔵のUSBシリアル機能を使うようになっている。
Arduino Duemillanove以前は、FTDI社のFT232というUSBシリアル変換ドライバICを使っている。
それぞれ違うデバイスドライバが必要だけど、両方対応できるようにAndroidアプリを実装可能。

このGoogle製ライブラリを使えばいい。勝手にデバイス判別してくれる。
ボーレートの設定とかも扱いやすい関数が揃ってて便利。
usb-serial-for-android – Android USB serial and Arduino device host support library. – Google Project Hosting

これを使わなくてもandroid.hardware.usb.UsbDeviceクラスを使えば自分で実装できるが、ソース見てみればわかるけどげんなりする処理が多いのでusb-serial-for-android.jarを使うのが良いと思います。

AndroidでArduinoとシリアル通信する


usb-serial-for-androidのDownloadsメニューからusb-serial-for-android-v010.jarをダウンロードして、Androidアプリのlibsディレクトリに入れる


AndroidManifest.xml のinternt-filterの中に追加
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />


さらにintent-filterと同列にmeta-dataを作る
<meta-data
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/device_filter" />


device_filter.xml を取得する。FTDIチップやArduinoのUSBベンダIDが書いてある。
wget http://usb-serial-for-android.googlecode.com/git/UsbSerialExamples/res/xml/device_filter.xml
mkdir res/xml
mv device_filter.xml res/xml/


接続する。
アクティビティ内で
import java.io.*;
import android.hardware.usb.*;
import com.hoho.android.usbserial.driver.*;

public void onCreate(Bundle savedInstanceState) {
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
UsbSerialDriver usb = UsbSerialProber.acquire(manager);
if (usb != null) {
try{
usb.open();
usb.setBaudRate(9600);
start_read_thread(); // シリアル通信を読むスレッドを起動
}
catch(IOException e){
e.printStackTrace();
}
}
}


Arduino → Android
シリアル通信で受信したデータを読む。ArduinoでSerial.println(“文字列”)したデータがAndroid側で読める。
public void start_read_thread(){
new Thread(new Runnable(){
public void run(){
try{
while(true){
byte buf[] = new byte[256];
int num = usb.read(buf, buf.length);
if(num > 0) Log.v("arduino", new String(buf, 0, num)); // Arduinoから受信した値をlogcat出力
Thread.sleep(10);
}
}
catch(IOException e){
e.printStackTrace();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}


adb logcat | grep arduino
して見てみましょう。受信できているはず。


Android → Arduino
AndroidからArduinoに文字列「o」や「x」送信する。
while(true){
usb.write("o".getBytes("UTF-8"),1);
Thread.sleep(1000);
usb.write("x".getBytes("UTF-8"),1);
Thread.sleep(1000);
}


接続を閉じる。
try{
usb.close();
}
catch(IOException e){
e.printStackTrace();
}


Arduinoにはこんなのを書いておくと、シリアル通信でoとxが来たらLEDを点灯消灯させたりサーボモータを回したりできる。
char led_pin = 13;
char serial_recv;

void setup(){
pinMode(led_pin, OUTPUT);
Serial.begin(9600);
}

void loop(){
Serial.println(analogRead(0)); // アナログセンサの値をAndroidに送る
while(Serial.available() > 0){
serial_recv = Serial.read(); // シリアル通信を受信
switch(serial_recv){
case 'o':
digitalWrite(led_pin, true); // 基板上のLEDを点灯
analogWrite(11, 200); // サーボ回す
break;
case 'x':
digitalWrite(led_pin, false); // 消灯
analogWrite(11, 20); // サーボ回す
break;
}
Serial.println(serial_recv);
}
delay(10);
}


WiFiでAndroidアプリ開発

ArduinoをAndroidに接続しているとUSBケーブルがさせない。MacからWiFi経由でAndroidにプログラムを書くといい。
Nexus7が同じ無線LAN内の192.168.1.121にあるとする。
設定→タブレット情報→端末の状態 にIPアドレスが書いてある。
AndroidをMacにUSB接続してから
adb tcpip 5555
adb connect 192.168.1.121:5555
adb logcat
USBケーブルを抜く。

あとはもう、adb installもEclipseからのDebug Runも、logcatもWiFiでできるようになってる。


元に戻すには、USBケーブルを接続して
adb usb
これで戻る。


その他の作例

こういうのを作ってる人がいて、参考になった

Android USB Host + Arduino: How to communicate without rooting your Android Tablet or Phone | Using Android in Industrial Automation
android.hardware.usb.UsbDeviceを使ってArduino UNOとシリアル通信している

Android Host – Arduino間シリアル通信の実現 – アトリエのどか
FTDIドライバを使ってArduino Duemillanove以前とシリアル通信してる

0

HTML+JavaScriptをArduinoに直結できるシリアルポートサーバーを作った

SerialPort Serverを使うと、HTMLとJSをArduinoに直結できる。JavaScriptを少し書くだけでArduinoに「カーテン開けろ」とか「部屋の明るさよこせ」とか命令を送れるわけだ。
Web系の技術とハードウェアの技術を同時に使うには、間に「つなぎ」が必要なので、必要な機能を全部入れたサーバーを作ってみた感じです。
(github pagesでプロジェクトページ作ってみたんだけど楽でいいですね)


こういうこと。


シリアルポートサーバーはrubygemsでインストールできる。

gem install serialport-server
which serialport-server
serialport-server --help
serialport-server /dev/tty.デバイス名
–helpでヘルプが出る。デバイス名を引数にして起動できる。
Macならsudo gem installで一発でインストールできるはず。

起動するとHTTPサーバー(http://localhost:8783)、WebSocketサーバー(ws://localhost:8784)、TCP Socketサーバー(localhost:8785)の3つが同時に起動する。

HTTPのサーバーはajaxのクロスドメイン制限を超えてデータのやりとりができるようにしてある。シリアルポートサーバーを動かして、HTMLとJavaScriptを書けばArduinoとJavaScriptが直接通信できるというわけだ。
(response headerにAccess-Control-Allow-Originを付ければ良いとyuisekiのはてブを見ていて知ったのでやってみた。)


■デモ
SerialPort Serverのページにも英語で書いたが、簡単なサンプルプロジェクトを日本語でも解説しておく。


1. Arduinoにプログラムを書き込む

2. ArduinoのDigital13番ピンにLEDを、Analog0番ピンにCdSと10kΩの抵抗を接続する
SerialPort Server sample
SerialPort Server sample

3. SerialPort Serverを起動する

4. Ajax版サンプルを開いて試してみる

5. WebSocket版サンプルを開いて試してみる


WebブラウザからLEDを点灯消灯したり、CdSの明るさの値がリアルタイムにスライダーに反映されているはずだ。
実際にAjaxサンプルのJavaScriptのコードを見てみると簡単さがわかると思う。17行でLEDとCdSを制御できている。



もちろんWebサーバーなので、複数のWebブラウザがSerialPort Serverから同時にデータを読み出そうとしても大丈夫。
増井研で1年以上、遠隔操縦ロボットの制御に使っていたプログラムを元に使っているのでプロセスを起動してたぶん3ヶ月ぐらい放置していても動き続けるぐらいに安定していると思う。

0

Rubyでシリアルポートを使う(最新版)

■先にまとめ

gem install serialport
でインストールして、
require 'rubygems'
gem 'serialport','>=1.0.4'
require 'serialport'
必ずバージョン1.0.4以上をロードするように指定して使いましょう。


基本的なserialportの読み書きはこうやる。
実運用ではgetsは改行がくるまでブロックされるのでloopで回して待てばいい。
sp = SerialPort.new('/dev/tty.usb-serialdevice', 9600, 8, 1, 0) # 9600bps, 8bit, stopbit 1, parity none

line = sp.gets # read
sp.puts "echo:#{line}" # write



■詳しいこと
rubyでserialportを使う場合、/dev/ttyのデバイスファイルを直接読むか、それともruby-serialportというのを使うかの2通りの方法がある。
serialportのインストール方法は以前書いた。


で、この頃(2010年初頭)まではserialportのバージョン0.6を使うのが良かった。0.7が最新だったけど関数のインタフェースが変わったのにドキュメントが追いついてなくて使い方がわからなかった為。

でもこのserialport 0.6はrubygemsからインストールができなくて、適当にzipで拾ってきてインストールするしかなかった。


最近はserialport 1.0.4がrubygemsからインストールできて、MacとUbuntuで普通に使えるようになった。Windowsでもちゃんと動くらしい。
インタフェースも0.6と同じものになっている。

ただし、古いserialport 0.6がインストールされている環境では注意が必要で、なぜかgemをバージョン指定してrequireしないと0.6の方が読み込まれる。
しかも0.6のアンインストール方法がよくわからないので、
ぜひ皆様プログラムはこのように
require 'rubygems'
gem 'serialport','>=1.0.4'
require 'serialport'
書いてくれるといちいち修正して使う手間が省けるのでよろしくお願いします。


**********
こないだかず助に行ったときに、2ヶ月ぐらいめんどくさくてこのblog書いてなくてネタたまりすぎてヤバイという話をしたら、@ykfに毎日テーマを送って、書かなかったら怒られて消化していこうという事になったのでしばらく毎日書くことになった。毎日書けば1週間ぐらいで全部書ききれる。

0

serial-http-gateway作った

シリアルポートをhttpで使えるツールを作った。
ブラウザでhttp://localhost:8783を開くとデータが読める。POSTでデータを送ると書き込める。

webブラウザでロボットを操作する部分がある、OB降臨システムというのを作っているのでその部品として作った。

githubに全部置いた。



■インストールと起動
git clone git://github.com/shokai/serial-http-gateway.git
cd serial-http-gateway
gem install serialport eventmachine eventmachine_httpserver json ArgsParser


Arduino等をMacに接続すると/dev/tty.usb〜〜という名前になる。引数に渡して起動する。
./serial-http-gateway --help
./serial-http-gateway /dev/tty.usbserial-A7006Rqn
port 8783で起動する。引数-portで変更できる。



gemが全て入っていれば実行ファイル単体で動くので、適当なパスが通っている場所にコピーして置くと便利。
sudo cp serial-http-gateway /usr/local/sbin/



■使う
HTTP POSTでシリアルポートに書き込める。
% curl -d 'testtest' 'http://localhost:8783'


HTTP-GETでシリアルポートからのデータが読める。
最近100件の受信データが保存してあって配列で返ってくる。timeに時間が入っている。
curl 'http://localhost:8783'


時間はミリ秒でunixtimeなので、1000で割れば普通のunixtimeになる
[{"data":287,"time":1296767483756},{"data":288,"time":1296767483253},{"data":291,"time":1296767482751},{"data":293,"time":1296767482246},{"data":292,"time":1296767481743},{"data":293,"time":1296767481238},{"data":294,"time":1296767480736},{"data":299,"time":1296767480233},{"data":303,"time":1296767479729},{"data":305,"time":1296767479226},{"data":307,"time":1296767478721},{"data":312,"time":1296767478219},{"data":321,"time":1296767477714},{"data":332,"time":1296767477211},{"data":344,"time":1296767476709},{"data":359,"time":1296767476204},{"data":"\u0000390","time":1296767475701}]


eventmachine_httpserver便利だなー

0

serial-socket-gateway

よくある シリアル通信 <=> TCP Socket する物を作った。
しばらく使ってて特に問題なかったので、使い方とか書いておく。
Mac OSX LeopardとUbuntu 9.04,と10.04とWindows XPで動作した。

普通のsocketなので大抵のプログラム言語から使えるし、少なくとも10個ぐらいのプログラムを下にぶらさげてマイコンと通信させられる。
最近はコレでsocket化したシリアルデバイスにJRubyで作ったGUIをつないで色々やってる。


■ソースコード
githubにある
http://github.com/shokai/serial-socket-gateway


■インストール

git clone git://github.com/shokai/serial-socket-gateway.git

中のserial-socket-gatewayがそれ。Rubyで書かれている。
単体のファイルで動くので、そのまま /usr/local/bin/ とか適当なパスの通っているディレクトリに cp すればいいと思います


■必要なgemのインストール
ruby-serialportとeventmachineを使っているのでそれぞれインストールする。

gem install eventmachine ArgsParser


ruby-serialportは0.7が動かなかったので、俺は0.6を使っている。MacやUbuntuはソースからビルドしてインストーrうする
wget http://rubyforge.org/frs/download.php/72/ruby-serialport-0.6.tar.gz
tar -zxvf ruby-serialport-0.6.tar.gz
cd ruby-serialport-0.6
ruby extconf.rb
make
sudo make install


Windowsは、http://rubyforge.org/tracker/download.php/61/321/9924/1800/ruby-serialport-0.6.0-mswin32-gem.zipからwindows用のバイナリ(gem)を持ってきて、解凍してインストール。
gem install serialport-0.6.0-mswin32.gem


■起動
Arduino等をMacに接続すると /dev/ の下に、tty.usbなんとかというデバイスができているはず。
serial-socket-gateway /dev/tty.usbserial-A7006Rqn
デバイス名を引数に渡して起動。


接続してみる。telnetでok
telnet localhost 8782
デフォルトで8782番portで起動している。port番号はソースの上の方をいじれば変えられる。


■通信の仕様
改行(¥n)区切りで、1行毎に通信している。改行が来るまで送受信しない。


■サンプルプログラム Arduinoから送信
こんなかんじでADコンバータで電圧測って、Serial.println()を使って末尾に改行を付けて9600bpsパリティなしストップビット1で送信。
int ad_pin = 0;

void setup(){
Serial.begin(9600);
}

void loop(){
int ad = analogRead(ad_pin);
Serial.println(ad);
}


■サンプルプログラム Rubyで受信
間にserial-socket-gatewayを挟んでシリアルデバイスと通信する。

require 'socket'
s = TCPSocket.open("192.168.1.100", 8782)

loop do
res = s.gets
if res.to_s.size > 0
puts res
end
end
serial-socket-gatewayはclientとの接続が切れていないか確認する為に、たまに空文字列を送ってくる。
受信した文字列の長さを見て無視してください

送信は
s.puts "abc abc"
rubyのputsは末尾に改行(¥n)が付く。



■サンプルプログラム Rubyで送受信を同時に
標準入力をserial-socket-gatewayにそのまま送る。こういうのeventmachineでやると楽だね
#!/usr/bin/env ruby
require 'rubygems'
require 'socket'
require 'eventmachine'

HOST = 'localhost'
PORT = 8782

begin
s = TCPSocket.open(HOST, PORT)
rescue => e
STDERR.puts e
exit 1
end

EventMachine::run do
EventMachine::defer do
loop do
res = s.gets
exit unless res
if res.to_s.size > 0
puts res
end
end
end

EventMachine::defer do
loop do
s.puts gets
end
end
end


eventmachineが便利!!