In Android Side:
MainActivity.java
package com.example.androidusbhost;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Iterator;
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.widget.TextView;
import android.widget.Toast;
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 {
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;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
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();
}
private void connectUsb(){
Toast.makeText(MainActivity.this,
"connectUsb()",
Toast.LENGTH_LONG).show();
textStatus.setText("connectUsb()");
searchEndPoint();
if(usbInterfaceFound != null){
setupUsbComm();
}
}
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;
}
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){
Toast.makeText(MainActivity.this,
"device not found",
Toast.LENGTH_LONG).show();
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;
usbResult = usbDeviceConnection.controlTransfer(
0x21, //requestType
RQSID_SET_CONTROL_LINE_STATE, //SET_CONTROL_LINE_STATE
0, //value
0, //index
null, //buffer
0, //length
0); //timeout
Toast.makeText(MainActivity.this,
"controlTransfer(SET_CONTROL_LINE_STATE): " + usbResult,
Toast.LENGTH_LONG).show();
//baud rate = 9600
//8 data bit
//1 stop bit
byte[] encodingSetting =
new byte[] {(byte)0x80, 0x25, 0x00, 0x00, 0x00, 0x00, 0x08 };
usbResult = usbDeviceConnection.controlTransfer(
0x21, //requestType
RQSID_SET_LINE_CODING, //SET_LINE_CODING
0, //value
0, //index
encodingSetting, //buffer
7, //length
0); //timeout
Toast.makeText(MainActivity.this,
"controlTransfer(RQSID_SET_LINE_CODING): " + usbResult,
Toast.LENGTH_LONG).show();
byte[] bytesHello =
new byte[] {(byte)'H', 'e', 'l', 'l', 'o', ' ',
'f', 'r', 'o', 'm', ' ',
'A', 'n', 'd', 'r', 'o', 'i', 'd'};
usbResult = usbDeviceConnection.bulkTransfer(
endpointOut,
bytesHello,
bytesHello.length,
0);
Toast.makeText(MainActivity.this,
"bulkTransfer: " + usbResult,
Toast.LENGTH_LONG).show();
}
}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) {
Toast.makeText(MainActivity.this, e.toString(), Toast.LENGTH_LONG).show();
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)) {
Toast.makeText(MainActivity.this,
"ACTION_USB_PERMISSION",
Toast.LENGTH_LONG).show();
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("");
}
}
};
}
The setting of usbDeviceConnection.controlTransfer() inside setupUsbComm() is copied from the post: Android USB Host + Arduino: How to communicate without rooting your Android Tablet or Phone
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" />
<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:
testSerial.ino
#include <Esplora.h>
#include <TFT.h>
#include <SPI.h>
int prevSw1 = HIGH;
int incomingByte = 0;
String charsIn = "";
char printout[20]; //max char to print: 20
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);
}
void loop() {
int sw1 = Esplora.readButton(SWITCH_1);
if(sw1 != prevSw1){
if(sw1 == LOW){
Serial.println("Hello from Arduino Esplora");
}
prevSw1 = sw1;
}
while (Serial.available()) {
char charRead = Serial.read();
charsIn.concat(charRead);
}
if(charsIn != ""){
Serial.println("How are you, " + charsIn);
charsIn.toCharArray(printout, 21);
EsploraTFT.background(0,0,0);
EsploraTFT.text(printout, 0, 10);
charsIn = "";
}
}
It's same as the code in my another blog: Serial communication between Arduino Esplora and PC
Step-by-step: Android USB Host Mode programming
After a few hours, I finally got this working with Arduino Uno R3 and Samsung S3 Mini (without rooting). It wasn't exactly easy though.
ReplyDeleteFirstly, ProductID needed to be changed to 67 for Arduino Uno R3.
Secondly, it was needed to switch Samsung S3 Mini into the USB Host mode. This can be done by connecting the OTG cable to an USB power source and restarting the phone. Most easily, you can use a 2x USB cable (also called Y cable) commonly used for portable hard drives to interconnect OTG cable with a laptop. After restarting the phone, you can disconnect the 2x USB cable and connect Arduino. The phone will stay in the USB Host mode until the next restart (which also means that USB charging won't work so make sure to start with a fully charged phone).
Thirdly, this app was always crashing on my Samsung S3 Mini. I have fixed this by commenting out the function calls to showRawDescriptors() function which was crashing it.
Cheers for this great app and gut luck with Android and Arduino development!
Hello Michael Cifka,
ReplyDeleteThx for your comment :)
Here goes a video of my version working: http://www.youtube.com/watch?v=RLtCVy_M4lw
ReplyDeletegood day, I can implement this code with arduino mega ADK, which changes you should make, thanks and sorry for the English.
ReplyDeleteWhat is the value returned by the "usbDeviceConnection.releaseInterface(usbInterface);" method? I always get false.
ReplyDeleteSir please help me to do the reverse. Print arduino data on android
ReplyDeletehello sreenath tk,
ReplyDeleteplease read more examples in Send data from Android to Arduino Uno, in USB Host Mode.
he works on Arduino méga 256
ReplyDeleteIs there any way to send "Hello" from Android phone to another Android phone?
ReplyDeleteHello Eric,
ReplyDeleteThis video looks a lot like something i am looking for! Do you know by any chance if the USB host also lets you control a stepper motor (using an h-bridge for an example).
Our goal is to connect two dc motors over usb cable to an android phone and control the motors with an application.
looking forward to an answer to help me further on the way (or that makes me decide something else).
I am an android app developer and new to embedded systems.
ReplyDeleteWhile doing the control transfer, I receive -1 (ie failure) in the Toast.
Can you please tell me why is it so?(I am using FTDI board for communication between mobile app and FTDI board)
While doing control transfer for RQSID_SET_CONTROL_LINE_STATE, Why buffer is sent as null.
although in case of RQSID_SET_LINE_CODING, buffer is set to be a byte array of 255.
Please answer my queries as it's urgent for me to close this ASAP.
Not all the USB device need to do control transfer to set the configure. For my case, my USB device has preset configure, I skipped the control transfer & do bulk transfer directly.
DeleteBlogger Anchal said...
ReplyDeleteI am an android app developer and new to embedded systems.
While doing the control transfer, I receive -1 (ie failure) in the Toast.
Can you please tell me why is it so?(I am using FTDI board for communication between mobile app and FTDI board)
While doing control transfer for RQSID_SET_CONTROL_LINE_STATE, Why buffer is sent as null.
although in case of RQSID_SET_LINE_CODING, buffer is set to be a byte array of 255.
Please answer my queries as it's urgent for me to close this ASAP.
October 12, 2017 at 1:10 AM
------------------------------------------------------------------------------------
Hi, are you fix the problem?, i have the same problem :( and i don't know do :(
i wait you can help me
:)