Monday, September 1, 2014

Send data from Android to Arduino Uno, in USB Host Mode

This example show how to send String from Android to Arduino Uni via USB Serial, in USB Host mode. Actually, it is modified from the example of "Send Hello to Arduino from Android in USB Host Mode", but target to Arduino Uno, instead of Esplora.

In Arduino Uno, the String received from USB port will be displayed on the equipped 2x16 LCD Module.


Because you have to connect Arduino Uno to your Android device with USB port (with USB OTG cable), you are suggested to enable WiFi debugging for ADB.

To target to Arduino Uno, you have to "Check idVendor and idProduct of Arduino Uno". It should be:
vendor-id="9025"
product-id="0067"

Create /res/xml/device_filter.xml to specify vendor-id and product-id.
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- idVendor=2341, idProduct=0043 for Arduino Uno R3 -->
    <usb-device 
        vendor-id="9025" 
        product-id="0067" />
</resources>

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.androidusbhostarduinouno"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-feature android:name="android.hardware.usb.host" />

    <uses-sdk
        android:minSdkVersion="13"
        android:targetSdkVersion="21" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:configChanges="keyboard|orientation"
            android:label="@string/app_name" >
            <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>

layout, /res/layout/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="com.example.androidusbhostarduinouno.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" />
    
    <EditText
        android:id="@+id/textout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    <Button
        android:id="@+id/send"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Send"/>

    <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>

MainActivity.java
package com.example.androidusbhostarduinouno;

import java.util.HashMap;
import java.util.Iterator;

import android.support.v7.app.ActionBarActivity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
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.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends ActionBarActivity {

 TextView textInfo;
 TextView textSearchedEndpoint;

 TextView textDeviceName;
 TextView textStatus;

 private static final int targetVendorID = 9025;  //Arduino Uno
 private static final int targetProductID = 67; //Arduino Uno, not 0067
 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;
 
 EditText textOut;
 Button buttonSend;

 @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();
  
  textOut = (EditText)findViewById(R.id.textout);
  buttonSend = (Button)findViewById(R.id.send);
  buttonSend.setOnClickListener(buttonSendOnClickListener);
 }
 
 OnClickListener buttonSendOnClickListener =
  new OnClickListener(){

   @Override
   public void onClick(View v) {
    
    if(deviceFound != null){

     String tOut = textOut.getText().toString();
     byte[] bytesOut = tOut.getBytes(); //convert String to byte[]
     int usbResult = usbDeviceConnection.bulkTransfer(
      endpointOut, bytesOut, bytesOut.length, 0);
     
    }else{
     Toast.makeText(MainActivity.this, 
      "deviceFound == null", 
      Toast.LENGTH_LONG).show();
    }
    
    
   }};

 @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);

    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 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();
     }else{
      Toast.makeText(MainActivity.this, 
        "device == deviceFound, no call releaseUsb()\n" +
        device.toString() + "\n" +
        deviceFound.toString(), 
        Toast.LENGTH_LONG).show();
     }
    }else{
     Toast.makeText(MainActivity.this, 
      "device == null, no call releaseUsb()", Toast.LENGTH_LONG).show();
    }
    
    textInfo.setText("");
   }
  }

 };

}


download filesDownload the files.

For Arduino Uno side, refer to "Read from Arduino Serial port, and write to 2x16 LCD" of my another blog for Arduino.


More of Android + Arduino Uno

13 comments:

Maria said...

Hello. Is it possible to send data from arduino to android via USB?

Erik said...

hello Maria,

suppose it can.

But I have not success in doing so:(

Erik said...

Hello Maria,

Please read Bi-directional communication between Android and Arduino in USB Host Mode.

Unknown said...

It is showing Unfortunaely app has stopped.
I think the problem is with the Thread
as all has been done on main UI

c.c.josh said...

can you use Wifi not USB?
to connect arduino and receive data from arduino to android?

Anonymous said...

how can i control arduino matrix with android?

Erik said...

To control arduino matrix with android, visit: Android USB Host Mode control Arduino Uno + 8x8 LED Matrix

Anirudh said...

Hello and thx for this nice article.
I tried this project but ran into some problems so I would appreciate it if anyone could help me.
I used an Arduino UNO for this one and connected it to my phone with the help of an USB OTG Adapter connected to the printer-like cable u get with an Arduino. when I plug the cable into the phone, I get a device not found error in the Toast as well as in the TextView.
I Have already change the Product and Vendor ID's(changed to 2341 and 0043) in the device_filter.xml as well as in the MainActivity.java file.
I wonder what could be the issue.

Unknown said...

This code may I use transfer data
android to pc ?

Erik said...

hello Eiphyu phwe,

No.

In this example Android act as host, and Arduino as slave.

PC cannot act as slave, as I understand. So cannot.

Unknown said...

Hello Eric,
i wanna turn on a led using android and arduino with usb and otg cable. please would you help me ? or just give some references.

Unknown said...

Hey !!
what is the code for uno ??

Is just simple lcd code if yes .. then is your app send data to serial monitor ?

Erik said...

For Arduino Uno side, refer to "Read from Arduino Serial port, and write to 2x16 LCD" (http://arduino-er.blogspot.com/2014/08/read-from-arduino-serial-port-and-write.html).