In Android Side:
MainActivity.java.
package com.example.androidusbhost;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
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.view.View;
import android.view.View.OnClickListener;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
public class MainActivity extends Activity {
ToggleButton btnLED;
SeekBar barR, barG, barB;
TextView textRx;
TextView textInfo;
TextView textSearchedEndpoint;
TextView textDeviceName;
TextView textStatus;
private static final int targetVendorID= 9025;
private static final int targetProductID = 32828;
UsbDevice deviceFound = null;
UsbInterface usbInterfaceFound = null;
UsbEndpoint endpointIn = null;
UsbEndpoint endpointOut = null;
private static final String ACTION_USB_PERMISSION =
"com.android.example.USB_PERMISSION";
PendingIntent mPermissionIntent;
UsbInterface usbInterface;
UsbDeviceConnection usbDeviceConnection;
ThreadUsbTx threadUsbTx;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnLED = (ToggleButton)findViewById(R.id.ledbutton);
btnLED.setOnClickListener(btnLEDOnClickListener);
barR = (SeekBar)findViewById(R.id.rbar);
barG = (SeekBar)findViewById(R.id.gbar);
barB = (SeekBar)findViewById(R.id.bbar);
barR.setOnSeekBarChangeListener(colorOnSeekBarChangeListener);
barG.setOnSeekBarChangeListener(colorOnSeekBarChangeListener);
barB.setOnSeekBarChangeListener(colorOnSeekBarChangeListener);
textRx = (TextView)findViewById(R.id.textrx);
textStatus = (TextView)findViewById(R.id.textstatus);
textDeviceName = (TextView)findViewById(R.id.textdevicename);
textInfo = (TextView) findViewById(R.id.info);
textSearchedEndpoint = (TextView)findViewById(R.id.searchedendpoint);
//register the broadcast receiver
mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
registerReceiver(mUsbReceiver, filter);
registerReceiver(mUsbDeviceReceiver, new IntentFilter(UsbManager.ACTION_USB_DEVICE_ATTACHED));
registerReceiver(mUsbDeviceReceiver, new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED));
connectUsb();
}
@Override
protected void onDestroy() {
releaseUsb();
unregisterReceiver(mUsbReceiver);
unregisterReceiver(mUsbDeviceReceiver);
super.onDestroy();
}
OnClickListener btnLEDOnClickListener =
new OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
int usbResult;
byte[] cmdLED;
if(((ToggleButton)v).isChecked()){
cmdLED = new byte[] {(byte)'L', 'E', 'D', 'O', 'N', '\n'};
}else{
cmdLED = new byte[] {(byte)'L', 'E', 'D', 'O', 'F', 'F', '\n'};
}
threadUsbTx.insertCmd(cmdLED);
}
};
OnSeekBarChangeListener colorOnSeekBarChangeListener =
new OnSeekBarChangeListener(){
byte[] toAscii = { '0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
byte[] cmdCol = new byte[] {(byte)'C', 'O', 'L', '#',
'0', '0', '0', '0', '0', '0', '\n'};
cmdCol[4] = toAscii[(barR.getProgress()>>4) & 0x0f];
cmdCol[5] = toAscii[barR.getProgress() & 0x0f];
cmdCol[6] = toAscii[(barG.getProgress()>>4) & 0x0f];
cmdCol[7] = toAscii[barG.getProgress() & 0x0f];
cmdCol[8] = toAscii[(barB.getProgress()>>4) & 0x0f];
cmdCol[9] = toAscii[barB.getProgress() & 0x0f];
threadUsbTx.insertCmd(cmdCol.clone());
}
};
private void connectUsb(){
btnLED.setEnabled(false);
barR.setEnabled(false);
barG.setEnabled(false);
barB.setEnabled(false);
Toast.makeText(MainActivity.this,
"connectUsb()",
Toast.LENGTH_LONG).show();
textStatus.setText("connectUsb()");
searchEndPoint();
if(usbInterfaceFound != null){
setupUsbComm();
threadUsbTx = new ThreadUsbTx(usbDeviceConnection, endpointOut);
threadUsbTx.start();
btnLED.setEnabled(true);
barR.setEnabled(true);
barG.setEnabled(true);
barB.setEnabled(true);
//Turn On LED once connected
btnLED.setChecked(true);
barR.setProgress(0x80);
barG.setProgress(0x80);
barB.setProgress(0x80);
threadUsbTx.insertCmd(
new byte[] {(byte)'L', 'E', 'D', 'O', 'N', '\n'});
threadUsbTx.insertCmd(
new byte[] {(byte)'C', 'O', 'L', '#',
'8', '0', '8', '0', '8', '0', '\n'});
}
}
private void releaseUsb(){
Toast.makeText(MainActivity.this,
"releaseUsb()",
Toast.LENGTH_LONG).show();
textStatus.setText("releaseUsb()");
if(usbDeviceConnection != null){
if(usbInterface != null){
usbDeviceConnection.releaseInterface(usbInterface);
usbInterface = null;
}
usbDeviceConnection.close();
usbDeviceConnection = null;
}
deviceFound = null;
usbInterfaceFound = null;
endpointIn = null;
endpointOut = null;
btnLED.setEnabled(false);
barR.setEnabled(false);
barG.setEnabled(false);
barB.setEnabled(false);
if(threadUsbTx!=null){
threadUsbTx.setRunning(false);
}
}
private void searchEndPoint(){
textInfo.setText("");
textSearchedEndpoint.setText("");
usbInterfaceFound = null;
endpointOut = null;
endpointIn = null;
//Search device for targetVendorID and targetProductID
if(deviceFound == null){
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
while (deviceIterator.hasNext()) {
UsbDevice device = deviceIterator.next();
if(device.getVendorId()==targetVendorID){
if(device.getProductId()==targetProductID){
deviceFound = device;
}
}
}
}
if(deviceFound==null){
textStatus.setText("device not found");
}else{
String s = deviceFound.toString() + "\n" +
"DeviceID: " + deviceFound.getDeviceId() + "\n" +
"DeviceName: " + deviceFound.getDeviceName() + "\n" +
"DeviceClass: " + deviceFound.getDeviceClass() + "\n" +
"DeviceSubClass: " + deviceFound.getDeviceSubclass() + "\n" +
"VendorID: " + deviceFound.getVendorId() + "\n" +
"ProductID: " + deviceFound.getProductId() + "\n" +
"InterfaceCount: " + deviceFound.getInterfaceCount();
textInfo.setText(s);
//Search for UsbInterface with Endpoint of USB_ENDPOINT_XFER_BULK,
//and direction USB_DIR_OUT and USB_DIR_IN
for(int i=0; i<deviceFound.getInterfaceCount(); i++){
UsbInterface usbif = deviceFound.getInterface(i);
UsbEndpoint tOut = null;
UsbEndpoint tIn = null;
int tEndpointCnt = usbif.getEndpointCount();
if(tEndpointCnt>=2){
for(int j=0; j<tEndpointCnt; j++){
if(usbif.getEndpoint(j).getType() ==
UsbConstants.USB_ENDPOINT_XFER_BULK){
if(usbif.getEndpoint(j).getDirection() ==
UsbConstants.USB_DIR_OUT){
tOut = usbif.getEndpoint(j);
}else if(usbif.getEndpoint(j).getDirection() ==
UsbConstants.USB_DIR_IN){
tIn = usbif.getEndpoint(j);
}
}
}
if(tOut!=null && tIn!=null){
//This interface have both USB_DIR_OUT
//and USB_DIR_IN of USB_ENDPOINT_XFER_BULK
usbInterfaceFound = usbif;
endpointOut = tOut;
endpointIn = tIn;
}
}
}
if(usbInterfaceFound==null){
textSearchedEndpoint.setText("No suitable interface found!");
}else{
textSearchedEndpoint.setText(
"UsbInterface found: " + usbInterfaceFound.toString() + "\n\n" +
"Endpoint OUT: " + endpointOut.toString() + "\n\n" +
"Endpoint IN: " + endpointIn.toString());
}
}
}
private boolean setupUsbComm(){
//for more info, search SET_LINE_CODING and
//SET_CONTROL_LINE_STATE in the document:
//"Universal Serial Bus Class Definitions for Communication Devices"
//at http://adf.ly/dppFt
final int RQSID_SET_LINE_CODING = 0x20;
final int RQSID_SET_CONTROL_LINE_STATE = 0x22;
boolean success = false;
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
Boolean permitToRead = manager.hasPermission(deviceFound);
if(permitToRead){
usbDeviceConnection = manager.openDevice(deviceFound);
if(usbDeviceConnection != null){
usbDeviceConnection.claimInterface(usbInterfaceFound, true);
showRawDescriptors(); //skip it if you no need show RawDescriptors
int usbResult;
/*
* D7: 0 - Host to Device
* 1 = Device to Host
* D6..5 Type
* 0 = Standard
* 1 = Class
* 2 = Vendor
* 3 = Reserved
* D4..0 Recipient
* 0 = Device
* 1 = Interface
* 2 = Endpoint
* 3 = Other
*/
//int requestType = 0x21;
int requestType = 0x42;
usbResult = usbDeviceConnection.controlTransfer(
requestType,
RQSID_SET_CONTROL_LINE_STATE, //SET_CONTROL_LINE_STATE
0, //value
0, //index
null, //buffer
0, //length
0); //timeout
//baud rate = 9600
//8 data bit
//1 stop bit
byte[] encodingSetting =
new byte[] {(byte)0x80, 0x25, 0x00, 0x00, 0x00, 0x00, 0x08 };
usbResult = usbDeviceConnection.controlTransfer(
requestType,
RQSID_SET_LINE_CODING, //SET_LINE_CODING
0, //value
0, //index
encodingSetting, //buffer
7, //length
0); //timeout
}
}else{
manager.requestPermission(deviceFound, mPermissionIntent);
Toast.makeText(MainActivity.this,
"Permission: " + permitToRead,
Toast.LENGTH_LONG).show();
textStatus.setText("Permission: " + permitToRead);
}
return success;
}
private void showRawDescriptors(){
final int STD_USB_REQUEST_GET_DESCRIPTOR = 0x06;
final int LIBUSB_DT_STRING = 0x03;
byte[] buffer = new byte[255];
int indexManufacturer = 14;
int indexProduct = 15;
String stringManufacturer = "";
String stringProduct = "";
byte[] rawDescriptors = usbDeviceConnection.getRawDescriptors();
int lengthManufacturer = usbDeviceConnection.controlTransfer(
UsbConstants.USB_DIR_IN|UsbConstants.USB_TYPE_STANDARD, //requestType
STD_USB_REQUEST_GET_DESCRIPTOR, //request ID for this transaction
(LIBUSB_DT_STRING << 8) | rawDescriptors[indexManufacturer], //value
0, //index
buffer, //buffer
0xFF, //length
0); //timeout
try {
stringManufacturer = new String(buffer, 2, lengthManufacturer-2, "UTF-16LE");
} catch (UnsupportedEncodingException e) {
textStatus.setText(e.toString());
}
int lengthProduct = usbDeviceConnection.controlTransfer(
UsbConstants.USB_DIR_IN|UsbConstants.USB_TYPE_STANDARD,
STD_USB_REQUEST_GET_DESCRIPTOR,
(LIBUSB_DT_STRING << 8) | rawDescriptors[indexProduct],
0,
buffer,
0xFF,
0);
try {
stringProduct = new String(buffer, 2, lengthProduct-2, "UTF-16LE");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
textStatus.setText("Manufacturer: " + stringManufacturer + "\n" +
"Product: " + stringProduct);
}
private final BroadcastReceiver mUsbReceiver =
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ACTION_USB_PERMISSION.equals(action)) {
textStatus.setText("ACTION_USB_PERMISSION");
synchronized (this) {
UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
if(device != null){
connectUsb();
}
}
else {
Toast.makeText(MainActivity.this,
"permission denied for device " + device,
Toast.LENGTH_LONG).show();
textStatus.setText("permission denied for device " + device);
}
}
}
}
};
private final BroadcastReceiver mUsbDeviceReceiver =
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
deviceFound = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
Toast.makeText(MainActivity.this,
"ACTION_USB_DEVICE_ATTACHED: \n" +
deviceFound.toString(),
Toast.LENGTH_LONG).show();
textStatus.setText("ACTION_USB_DEVICE_ATTACHED: \n" +
deviceFound.toString());
connectUsb();
}else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
Toast.makeText(MainActivity.this,
"ACTION_USB_DEVICE_DETACHED: \n" +
device.toString(),
Toast.LENGTH_LONG).show();
textStatus.setText("ACTION_USB_DEVICE_DETACHED: \n" +
device.toString());
if(device!=null){
if(device == deviceFound){
releaseUsb();
}
}
textInfo.setText("");
}
}
};
class ThreadUsbTx extends Thread{
boolean running;
UsbDeviceConnection txConnection;
UsbEndpoint txEndpoint;
Queue<byte[]> cmdQueue;
byte[] cmdToSent;
ThreadUsbTx(UsbDeviceConnection conn, UsbEndpoint endpoint){
txConnection = conn;
txEndpoint = endpoint;
cmdQueue = new LinkedList<byte[]>();
cmdToSent = null;
running = true;
}
public void setRunning(boolean r){
running = r;
}
public void insertCmd(byte[] cmd){
synchronized(cmdQueue){
cmdQueue.add(cmd);
}
}
@Override
public void run() {
while(running){
synchronized(cmdQueue){
if(cmdQueue.size()>0){
cmdToSent = cmdQueue.remove();
}
}
if(cmdToSent!=null){
int usbResult = usbDeviceConnection.bulkTransfer(
endpointOut,
cmdToSent,
cmdToSent.length,
0);
final String s = new String(cmdToSent);
runOnUiThread(new Runnable(){
@Override
public void run() {
Toast.makeText(MainActivity.this,
s,
Toast.LENGTH_SHORT).show();
}});
cmdToSent = null;
}
}
}
}
}
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:autoLink="web"
android:text="http://android-er.blogspot.com/"
android:textStyle="bold" />
<ToggleButton
android:id="@+id/ledbutton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textOn="LED ON"
android:textOff="LED OFF" />
<SeekBar
android:id="@+id/rbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="255"
android:progress="0"/>
<SeekBar
android:id="@+id/gbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="255"
android:progress="0"/>
<SeekBar
android:id="@+id/bbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="255"
android:progress="0"/>
<TextView
android:id="@+id/textrx"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textStyle="bold"
android:textColor="#ff0000" />
<TextView
android:id="@+id/textstatus"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textStyle="bold" />
<TextView
android:id="@+id/textdevicename"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textStyle="bold|italic" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="@+id/info"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/searchedendpoint"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textStyle="bold" />
</LinearLayout>
</ScrollView>
</LinearLayout>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.androidusbhost"
android:versionCode="1"
android:versionName="1.0" >
<uses-feature android:name="android.hardware.usb.host" />
<uses-sdk
android:minSdkVersion="13"
android:targetSdkVersion="18" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.androidusbhost.MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|orientation" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/device_filter" />
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
</intent-filter>
</activity>
</application>
</manifest>
Download the files.
In Arduino Side: re-use the code in my another blog Node.js + Arduino, running on PC.
#include <Esplora.h>
#include <TFT.h>
#include <SPI.h>
int MAX_CMD_LENGTH = 10;
char cmd[10];
int cmdIndex;
char incomingByte;
int prevSlider = 0;
void setup() {
EsploraTFT.begin();
EsploraTFT.background(0,0,0);
EsploraTFT.stroke(255,255,255); //preset stroke color
//Setup Serial Port with baud rate of 9600
Serial.begin(9600);
//indicate start
Esplora.writeRGB(255, 255, 255);
delay(250);
Esplora.writeRGB(0, 0, 0);
cmdIndex = 0;
}
void loop() {
if (incomingByte=Serial.available()>0) {
char byteIn = Serial.read();
cmd[cmdIndex] = byteIn;
if(byteIn=='\n'){
//command finished
cmd[cmdIndex] = '\0';
//Serial.println(cmd);
cmdIndex = 0;
String stringCmd = String(cmd);
if(strcmp(cmd, "LEDON") == 0){
//Serial.println("Command received: LEDON");
Esplora.writeRGB(255, 255, 255);
}else if (strcmp(cmd, "LEDOFF") == 0) {
//Serial.println("Command received: LEDOFF");
Esplora.writeRGB(0, 0, 0);
}else if(stringCmd.substring(0,4)="COL#"){
//Serial.println("Command received: COL#");
if(stringCmd.length()==10){
char * pEnd;
long int rgb = strtol(&cmd[4], &pEnd, 16);
int r = (rgb & 0xff0000) >> 16;
int g = (rgb & 0xff00) >> 8;
int b = rgb & 0xff;
//Serial.println(r);
//Serial.println(g);
//Serial.println(b);
EsploraTFT.background(b,g,r);
}
}else{
//Serial.println("Command received: unknown!");
}
}else{
if(cmdIndex++ >= MAX_CMD_LENGTH){
cmdIndex = 0;
}
}
}
//Read Slider
int slider = Esplora.readSlider();
//convert slider value from [0-1023] to [0x00-0xFF]
slider = slider>>2 & 0xff;
if(slider!=prevSlider){
prevSlider = slider;
String stringSlider = String(slider, HEX);
Serial.println("SLD#" + stringSlider +"\n");
}
}
Remark:
The device-to-host (Arduino-to-Android) communication have NOT been implemented on Android side in this example.
Step-by-step: Android USB Host Mode programming