AndroidとArduinoUnoの通信仕方

どうも、2年のチョコです。ロ技研の初ブログだ。わくわく。

今回はAndroidの話をしたい。Arduinoの制御は主にパソコンで行われ、ロボットの場合では可動性で無線通信が使われる。しかし、無線モジュールはもちろん、通信環境を建てるのは面倒で、パソコンの持ち歩きも不便である。そこで、Android(スマホ)にArduinoを制御させることにした。本文では、ユーザーが簡単にArduinoを制御できるシステムを導入し、Android APKとして出力する手順を紹介する。

なお、Androidの公式USB通信ガイドはこちら

—————-以下本文ーーーーーーーーー(私は分割線)ーーーーーーーーーーーー

Androidホスト機能を用いて、ArduinoUNOをUSB通信で制御し、USB以外の通信システムをなくす。簡潔な環境を建てるうえ、スマホアプリとしてArduinoのシーリアルIOを簡単化させる。また、スマホをロボットの上に乗せる場合、スマホをArduino代わりに内臓センサーのデータを処理をし、ロボット製作に必要なモジュールを省略する。

Androidでは、USBが接続した状態を感知する方法が二つある。Intentでアプリに通知するか、USBManagerによる手動認識ができる。どちらを使うかは勝手であるが、ここではUSBManagerを使う。

UsbManager manager = (UsbManager) mainActivity.getSystemService(Context.USB_SERVICE);
HashMap<String, UsbDevice> map = manager.getDeviceList();
Iterator iterator = map.values().iterator();
if (iterator.hasNext()) {
//なにか接続されている
}

ここでは、USBManager.getDeviceListでいま接続しているデバイスを出す関数を用いた。このコードをボタンであるいはループで実行すれば、接続したときに感知することができる。

次に、接続したデバイスの種類を調べる必要がある。これは、USBメモリなどではなくArduinoが接続しただけでなく、機種がArduino UNOであることを確認することが重要である。(機種によって通信システムが異なる場合がある)。すべてのUSBデバイスには、ベンダーIDと製品IDが指定されている。このIDの確認でArduinoUNOであるかを調べることができる。
*Arduino UNOのベンダーIDは0x2341で、製品IDは0x0001と0x0043である。
*USBDeviceクラスでは、ベンダーIDと製品IDはそれぞれ(int) getVendorId()と(int) getProductId()で得ることができる。

int vid = device.getVendorId();
int pid = device.getProductId();
if ((pid == 67 || pid == 1) && vid == 9025) {
//UNO見っけ
}

USBと通信する前に、ユーザーに許可を得る必要がある。これは、以下のようなポップアップを出すことでできる。

プログラムにはPendingIntentクラスで上のポップアップを出すことができる。

PendingIntent intent = PendingIntent.getBroadcast(mainActivity, 0, new Intent("com.○○.○○○.USB_PERMISSION"), 0);
manager.requestPermission(device, intent);

ユーザーが許可したかどうかは、USBManagerの(boolean) hasPermission()関数で調べることができる。これをOnResumeに入れれば、ポップアップを閉じた(アプリがが再開した)瞬間に感知することができる。

if (manager.hasPermission(device)) {
//許可した。コネクトするぞい
}

この関数を用いれば、アプリに接続の状態をユーザーに見せることもできるね。

データの書き読み

connection = manager.openDevice(device);
if ((connection != null) && (device.getInterfaceCount() > 1)) {
UsbInterface controlInterface = device.getInterface(0);
if(connection.claimInterface(controlInterface, true)) {
UsbInterface dataInterface = device.getInterface(1);
if(connection.claimInterface(dataInterface, true)) {
int endpointCount = dataInterface.getEndpointCount();
if (endpointCount > 1) {
for (int a = 0; a < endpointCount; a++) {
if (dataInterface.getEndpoint(aa).getDirection() == UsbConstants.USB_DIR_IN)
readEnd = dataInterface.getEndpoint(aa); //データ読み取りに使う
else
writeEnd = dataInterface.getEndpoint(aa);//データ書き込みに使う
}
}
}
}

次はbaudrateを設定する。Baudrateはデータの通信速度を決めるが、一般は9600の値を取る。Arduino側でも設定する必要がある。

byte[] msg = new byte[]{(byte)(baudrate & 255), (byte)(baudrate >> 8 & 255), (byte)(baudrate >> 16 & 255), (byte)(baudrate >> 24 & 255), (byte)0, (byte)0, (byte)8};
connection.controlTransfer(33, 32, 0, 0, msg, msg.length, 5000);

*詳しい説明はこちら

これで接続が完成した。次はデータのやり取りだが、ここではconnection.bulkTransferを使う。まずArduinoからSerial.write()で出力されたデータを読み取る方法:

new Thread(new Runnable() {
@Override
public void run() {
final byte[] buffer = new byte[size];
final int a = connection.bulkTransfer(readEnd, buffer, size, timeout);
if (size >= 0) {
new Handler(context.getMainLooper()).post(new Runnable() {
@Override
public void run() {
OnRead(buffer, size);
}
});
}
}
}).start();

BulkTransferの読み取りはスレッドをブロックするため、別スレッドに入れることを推奨する。上のコードでは、周期的に実行し、データが入ってきた時だけメインスレッドのOnRead関数を呼ぶ。

そして、書き込む方法。これは、AndroidでSerial.Read()でもらえるデータである。

synchronized (this) {
int bytesWritten = connection.bulkTransfer(writeEnd, buffer, buffer.length, timeout);
log("wrote " + c + " bytes");
return bytesWritten;
}

経験上ではあまりラグは生じなかったので、ここではスレッドに入れない。

最後に、データを解析する必要はあるが、これはデヴァイスによってエンコーディングが異なる。ただし、Arduino Unoの場合はCNC基準のUSB通信を採用されているため、bulkTransferからもらったでーたを直接シリアルデータになる。たとえば、ArduinoがSerial.print(“hello”)でやった場合、Android側に以下のコードが成り立つ。

public void OnRead (byte[] buffer, int length) {
char[] chars = new char[length];
for (int x = 0; x < length; x++) {
chars[x] = (char)buffer[x];
}
log(new String(chars));
}

出力:hello

以上。スマホをロボットに乗せ、USBで接続すれば、スマホのセンサーデータを用いた独立ロボットを作ることができる。また、Arduinoに複数のセンサーを載せ、スマホにそのデータを解析するアプリを作ることができる。ただし、スマホとArduinoの処理速度(ループ速度)が異なるかつ、低層関数でACKなどが手動で扱われてないため、個人的にはできれば短いデータ量を送っている。

では。

広告

AndroidとArduinoUnoの通信仕方」への2件のフィードバック

  1. ピンバック: Unityでマイクロマウスをシミュレートしたい | 慶應義塾大学ロボット技術研究会

  2. ピンバック: Unityでマイクロマウスをシミュレートしたい | 慶應義塾大学ロボット技術研究会

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中