今回はシリアル通信します
前回は、なんとなくAndroidのUSBホスト(OTG)機能をつかって、接続されているデバイスの情報を引っこ抜いてみました。今回は、もう少し踏み込んで、接続されたArduinoとシリアル通信してみます。
ArduinoとAndroid のUSBホスト機能を利用した通信にはオープンなライブラリが多数あります。
今回は、このようなライブラリを利用せずに、1からAndroidの公開APIだけで実装していきます。
まずはAndroid側
Manifestですが、前回同様以下の1文のみ変更します。
<uses-feature android:name="android.hardware.usb.host"/>
いわゆるIntentFilterの追加はしません。必ずしも必要というわけではなく、USB機器が接続されたら自動でActivityを立ち上げる必要がある時だけです。
Activityではこんな実装をしました。 ポイントは3つ。
- 接続されたUSBデバイスと通信する際はPermissionが必要。Android MのRuntimePermissionとはことなり、
UsbManager#requestPermission()
を使用する。デバイスごとにPermissionが必要で、デバイスを切断するとPermissionはクリアされ、再度接続した際はもう一度Permissionを取得する必要がある。 UsbManager#requestPermission()
の結果は、引数のPendingIntentで記載された呼び出し先で取得できるIntentに詰まっている。この引数をnullにすると、Permission取得後にAndroidのネイティブアプリが落ちる(笑)ので、なにかしらPendingIntentを詰める必要がある。- シリアル通信を開始する前に、
UsbDeviceConnection#controlTransfer()
でコントロール転送を行いシリアル通信を開始できるように準備をする必要がある。ここで転送する値はUSBデバイス(つまりUSB-シリアル変換を行うチップ)によってことなる。今回はArduino決め打ちでUsbDevice#getVendorId()
でベンダーIDを見て、Arduino(=9025)であれば接続を行うようにしている。
取り敢えず、テストということで、全部Activityクラスに書いています。本当はちゃんとクラス分けして下さい。また、Buttonが連打された時の排他処理は入っていません(笑)
package com.android.usbhost; import android.app.PendingIntent; import android.content.Intent; import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbEndpoint; import android.hardware.usb.UsbInterface; import android.hardware.usb.UsbManager; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.TextView; import java.util.HashMap; public class MainActivity extends AppCompatActivity { private TextView mTextView; private Button mButton; private UsbManager mUsbManager; private UsbDevice mUsbDevice; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void onResume() { super.onResume(); mTextView = (TextView)findViewById(R.id.textview); mButton = (Button)findViewById(R.id.button); mUsbManager = (UsbManager)getSystemService(USB_SERVICE); updateList(); mButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mUsbDevice == null) { return; } if (!mUsbManager.hasPermission(mUsbDevice)) { mUsbManager.requestPermission(mUsbDevice, PendingIntent.getBroadcast(MainActivity.this, 0, new Intent("なにか"), 0)); return; } connectDevice(); } }); } public void onPause() { super.onPause(); mUsbDevice = null; mButton.setOnClickListener(null); } private void updateList() { HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList(); if (deviceList == null || deviceList.isEmpty()) { mTextView.setText("no device found"); } else { String string = ""; for (String name : deviceList.keySet()) { string += name; if (deviceList.get(name).getVendorId() == 9025) { string += " (Arduino)\n"; mUsbDevice = deviceList.get(name); } else { string += "\n"; } } mTextView.setText(string); } } private void connectDevice() { new Thread(new Runnable() { @Override public void run() { UsbDeviceConnection connection = mUsbManager.openDevice(mUsbDevice); if (!connection.claimInterface(mUsbDevice.getInterface(1), true)) { connection.close(); return; } connection.controlTransfer(0x21, 34, 0, 0, null, 0, 0); connection.controlTransfer(0x21, 32, 0, 0, new byte[] { (byte)0x80, 0x25, 0x00, 0x00, 0x00, 0x00, 0x08 }, 7, 0); UsbEndpoint epIN = null; UsbEndpoint epOUT = null; UsbInterface usbIf = mUsbDevice.getInterface(1); for (int i = 0; i < usbIf.getEndpointCount(); i++) { if (usbIf.getEndpoint(i).getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) { if (usbIf.getEndpoint(i).getDirection() == UsbConstants.USB_DIR_IN) epIN = usbIf.getEndpoint(i); else epOUT = usbIf.getEndpoint(i); } } connection.bulkTransfer(epOUT, "1".getBytes(), 1, 0); connection.close(); } }).start(); } }
Arduino側の実装
こっちは本当に単純。Serialで何かしらのInputがあったら、LEDをチカチカさせます。
void setup() { pinMode(2, OUTPUT); Serial.begin(9600); } void loop() { if (Serial.read() == -1) { digitalWrite(2, LOW); delay(1000); } else { digitalWrite(2, HIGH); delay(250); digitalWrite(2, LOW); delay(250); digitalWrite(2, HIGH); delay(250); digitalWrite(2, LOW); delay(250); } digitalWrite(2, LOW); }
いざ動作させん
Arduino互換機のFreaduinoを使用。LEDは抵抗挟まず直挿し・・・本当はダメです(笑)
OTGアダプターを介して、Android端末とArduinoを接続させます。
ボタンを押すと、チカチカします。成功です!