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

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

Andr.oid Eric 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?

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

Andr.oid Eric 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 ?

Andr.oid Eric said...

Sorry, I haven't tried STM32F4DISCOVERY.

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

Andr.oid Eric 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.

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

Rajkumar Darbar 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.