Sunday, September 21, 2014

Bi-directional communication between Android and Arduino in USB Host Mode, example 1

This example implement bi-directional communication in USB Host mode, between Android and Arduino Uno. It's a button on Android, used to turn on/off the on-board LED (pin 13) on Arduino Uno. And a potentiometer on Arduino side, used to control the SeekBar on Android.


This example reference MissileLauncher example in Android SDK, you can locate it in the folder /sdk/samples/android-14/USB/MissileLauncher.

For Android-to-Arduino, command of CMD_LED_ON(1) and CMD_LED_OFF(2) are used to turn ON/OFF the LED on Arduino board. For Arduino-to-Android, '0' will be sent between data (I don't know why), so I force the value sent in the range 1-63.

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

    <uses-feature android:name="android.hardware.usb.host" />
    <uses-sdk
        android:minSdkVersion="12"
        android:targetSdkVersion="19" />

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

</manifest>

/res/xml/device_filter.xml, for Arduino Uno.
<?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>

/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.androidusbhostarduino.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" />
        
    <SeekBar
        android:id="@+id/seekbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="63"
        android:progress="0"/>
    
    <ToggleButton 
        android:id="@+id/arduinoled"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textOn="ON"
        android:textOff="OFF" />

</LinearLayout>

MainActivity.java
package com.example.androidusbhostarduino;

import java.nio.ByteBuffer;

import android.support.v7.app.ActionBarActivity;
import android.content.Context;
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.hardware.usb.UsbRequest;
import android.os.Bundle;
import android.widget.CompoundButton;
import android.widget.SeekBar;
import android.widget.ToggleButton;
import android.widget.CompoundButton.OnCheckedChangeListener;

public class MainActivity extends ActionBarActivity implements Runnable{

 private static final int CMD_LED_OFF = 2;
 private static final int CMD_LED_ON = 1;

 SeekBar bar;
 ToggleButton buttonLed;
 
 private UsbManager usbManager;
    private UsbDevice deviceFound;
    private UsbDeviceConnection usbDeviceConnection;
    private UsbInterface usbInterfaceFound = null;
 private UsbEndpoint endpointOut = null;
 private UsbEndpoint endpointIn = null;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  bar = (SeekBar)findViewById(R.id.seekbar);
  buttonLed = (ToggleButton)findViewById(R.id.arduinoled);
  buttonLed.setOnCheckedChangeListener(new OnCheckedChangeListener(){

   @Override
   public void onCheckedChanged(CompoundButton buttonView,
     boolean isChecked) {
    if(isChecked){
     sendCommand(CMD_LED_ON);
    }else{
     sendCommand(CMD_LED_OFF);
    }
   }});
  
  usbManager = (UsbManager)getSystemService(Context.USB_SERVICE);
 }

 @Override
    public void onResume() {
        super.onResume();

        Intent intent = getIntent();
        String action = intent.getAction();

        UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
        if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
            setDevice(device);
        } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
            if (deviceFound != null && deviceFound.equals(device)) {
                setDevice(null);
            }
        }
    }
 
 private void setDevice(UsbDevice device) {
        usbInterfaceFound = null;
     endpointOut = null;
     endpointIn = null;

        for (int i = 0; i < device.getInterfaceCount(); i++) {         
   UsbInterface usbif = device.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) {
            return;
        }

        deviceFound = device;
        
        if (device != null) {
            UsbDeviceConnection connection = 
             usbManager.openDevice(device);
            if (connection != null && 
             connection.claimInterface(usbInterfaceFound, true)) {
                usbDeviceConnection = connection;
                Thread thread = new Thread(this);
                thread.start();

            } else {
                usbDeviceConnection = null;
            }
         }
    }
 
 private void sendCommand(int control) {
        synchronized (this) {

            if (usbDeviceConnection != null) {
                byte[] message = new byte[1];
                message[0] = (byte)control;
                usbDeviceConnection.bulkTransfer(endpointOut,
                  message, message.length, 0);
            }
        }
    }

 @Override
 public void run() {
  ByteBuffer buffer = ByteBuffer.allocate(1);
        UsbRequest request = new UsbRequest();
        request.initialize(usbDeviceConnection, endpointIn);
        while (true) {
            request.queue(buffer, 1);
            if (usbDeviceConnection.requestWait() == request) {
                byte rxCmd = buffer.get(0);
                if(rxCmd!=0){
                 bar.setProgress((int)rxCmd);
                }

                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                }
            } else {
                break;
            }
        }
  
 }

}

download filesDownload the files.

UsbSlave.ino, sketch on Arduino Uno
int prvValue;

void setup() {
  Serial.begin(9600);
  pinMode(13, OUTPUT);
  
  prvValue = 0;
}

void loop() {
  if(Serial.available()){
    byte cmd = Serial.read();
    if(cmd == 0x02){
      digitalWrite(13, LOW);
    }else if(cmd == 0x01){
      digitalWrite(13, HIGH);
    }

  }
  
  int sensorValue = analogRead(A0) >> 4;
  byte dataToSent;
  if(prvValue != sensorValue){
    prvValue = sensorValue;
    
    if (prvValue==0x00){
      dataToSent = (byte)0x01;
    }else{
      dataToSent = (byte)prvValue;
    }
    
    Serial.write(dataToSent);
    delay(100);
  }

}

Download HERE.

A potentiometer is need:


Bi-directional communication between Android and Arduino in USB Host Mode, example 2

Related:
- More example of communication between Android to Arduino Uno, in USB Host Mode

15 comments:

J. shuin said...

hello, nice code
althoug i have some problem when i generate new project and copy in each file the code you write here i have a problem whit value R
It shows R cannot be resolved to a variable. on these 3 lines:

setContentView(R.layout.activity_main);

bar = (SeekBar)findViewById(R.id.seekbar);
buttonLed = (ToggleButton)findViewById(R.id.arduinoled);

do you know what is the problem?
Thank you.

Erik said...

There are many reason to cause R cannot be resolved,

Try to search here

viplov said...

hi can you show a sample code with text i mean sending a string bi-directionally. coz i am facing issues over that.

Aarvi said...

Hello,
Nice tutorial. I need to save and display the values obtained from arduino on my android device and instruct the arduino accordingly. Would that be possible?

Unknown said...

Hello,
Nice tutorial andr.oid Eric.can u tell me how to send and receive a string.I am having a problem with that please help me

Erik said...

More example of communication between Android to Arduino Uno, in USB Host Mode

Anonymous said...

hello !
Thank you for great tutorial
I havae a question for arduino it is working but when ı try stm32f4 d,scovery it is not working vendor id and product id is correct ı dont know what is problem you have any opinion ?

Erik said...

Sorry, I haven't tried STM32F4DISCOVERY.

Vinklit said...

Hello!
Thanks for that great job!
I have a problem, i think its because of Vendor id or Product id.
When i use Vid 9025 and Pid 0067 my android recognize usb conncetion but the application crashes when i turn on/off the toggle button.
When i use Vid 2341 and Pid 0043 my android don't recognize usb connection so i turn on the application she don't crashes. (but i can't have communication...)
I have an arduino Uno R3 and android 4.4.2 (i have change sdk min and target 17->23)
I m beginner... some question on your code:
-the methode onResume is never use right?
-if u can comment methode setDevice that would be very nice.
Could u help me pls?
Thanks in advance

Erik said...

hello Sylvain Reinauer,

Any error message?

try to keep using target sdk 17, it is a old example, may be some new permission requirement added from 17 to 23.

May be I have to re-test it again, later.

Vinklit said...

Thanks for your quickly answer.
I have try with target sdk 17, dosen't work.
I have the same arduino than the tutorial video.
If i use IDs of Arduino Uno R3 (2341 and 0043) there is nothing when i plug usb.
If i use IDs (9025 and 0067) when i plug usb application turn on but if i turn on toggle button the app crashes.
In both i can't see light up rx or tx led.
Which VendorId and PorductId do you use in the video?
How do you fix the 9600 baud rate in your app?
I don't have any error msg in the android studio...
Thanks in advance!
Sylvain

Unknown said...

Hi Sylvain Reinauer,

I am also facing the exactly same problem. Did you able sort it out? If yes, then please give me the suggestion. Thanks in advance.

Unknown said...

I have tried the above code . But when I am putting a Toast after the line
UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);

The application is getting crashed . what I am doing is i have used a USB TO TTL , in which one end i have connected to my pc and the other to my android phone using OTG. The Rest of the code i have kept the same . I just want to receive the data in my android phone so instead of progess bar i have kept a toast. So I am sending the data using Qcomm in my laptop to my android device .

Please Help me its very important. please

Unknown said...

great tutorial!!! i bought an arduino board from Arduino Uno R3 and i want to learn and use about it. please help me its basic use and tutorial sites about Arduino Uno R3

Anonymous said...

Thanks