Wednesday, September 24, 2014

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

It's another example to implement Bi-directional communication between Android and Arduino in USB Host Mode. A button on Andndroid is used to turn ON/OFF the on-board LED of Arduino Uno, and EditText to send string to Arduino Uno. it will be displayed on connected LCD, and sent back to Android.


Notice:
- Sync word of '0xFF' is used to mark the beginning of data, to sync the communication. Extra in-correct '0x00' received in Android side (I don't know why), so have to ignore it. Make sure the data will not be '0xFF' and '0x00'.
- Command 0x01, 0x02, and 0x03 are used to define LED ON, LED OFF, and string of text.
- Data will lost if sent too fast, so I break the message and insert dummy delay between bytes. (inside method sendArduinoText())
- A USB OTG cable is need to connect to Android side, and a normal USB is used to connect USB OTG cable and Arduino Uno.


Android side:

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
<?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" />

    <ToggleButton 
        android:id="@+id/arduinoled"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textOn="ON"
        android:textOff="OFF" />
    
    <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/textin"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</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.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.ToggleButton;
import android.widget.CompoundButton.OnCheckedChangeListener;

public class MainActivity extends ActionBarActivity implements Runnable{
 
 private static final String TAG = "AndroidUsbHostArduino";

 private static final byte IGNORE_00 = (byte) 0x00;
 private static final byte SYNC_WORD = (byte) 0xFF;
 
 private static final int CMD_LED_OFF = 2;
 private static final int CMD_LED_ON = 1;
 private static final int CMD_TEXT = 3;
 private static final int MAX_TEXT_LENGTH = 16;

 ToggleButton buttonLed;
 EditText textOut;
 Button buttonSend;
 TextView textIn;
 
 String stringToRx;
 
 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);

  buttonLed = (ToggleButton)findViewById(R.id.arduinoled);
  buttonLed.setOnCheckedChangeListener(new OnCheckedChangeListener(){

   @Override
   public void onCheckedChanged(CompoundButton buttonView,
     boolean isChecked) {
    if(isChecked){
     sendArduinoCommand(CMD_LED_ON);
    }else{
     sendArduinoCommand(CMD_LED_OFF);
    }
   }});
  
  textOut = (EditText)findViewById(R.id.textout);
  textIn = (TextView)findViewById(R.id.textin);
  buttonSend = (Button)findViewById(R.id.send);
  buttonSend.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    final String textToSend = textOut.getText().toString();
    if(textToSend!=""){
     stringToRx = "";
     textIn.setText("");
     Thread threadsendArduinoText = 
      new Thread(new Runnable(){

       @Override
       public void run() {
        sendArduinoText(textToSend);
       }});
     threadsendArduinoText.start();
    }
    
   }});
  
  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)) {
             
             connection.controlTransfer(0x21, 34, 0, 0, null, 0, 0);
             connection.controlTransfer(0x21, 32, 0, 0, 
               new byte[] { (byte) 0x80, 0x25, 0x00, 
                0x00, 0x00, 0x00, 0x08 }, 
               7, 0);
             
                usbDeviceConnection = connection;
                Thread thread = new Thread(this);
                thread.start();

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

            if (usbDeviceConnection != null) {
                byte[] message = new byte[2];
                message[0] = SYNC_WORD;
                message[1] = (byte)control;

                usbDeviceConnection.bulkTransfer(endpointOut,
                  message, message.length, 0);
                
                Log.d(TAG, "sendArduinoCommand: " + String.valueOf(control));
            }
        }
    }
 
 private void sendArduinoText(String s) {
        synchronized (this) {

            if (usbDeviceConnection != null) {
             
             Log.d(TAG, "sendArduinoText: " + s);
             
             int length = s.length();
             if(length>MAX_TEXT_LENGTH){
              length = MAX_TEXT_LENGTH;
             }
                byte[] message = new byte[length + 3];
                message[0] = SYNC_WORD;
                message[1] = (byte)CMD_TEXT;
                message[2] = (byte)length;
                s.getBytes(0, length, message, 3);
                
                /*
                usbDeviceConnection.bulkTransfer(endpointOut,
                  message, message.length, 0);
                */
                
                byte[] b = new byte[1];
                for(int i=0; i< length+3; i++){
                 b[0] = message[i];
                 Log.d(TAG, "sendArduinoTextb[0]: " + b[0]);
                 usbDeviceConnection.bulkTransfer(endpointOut,
                      b, 1, 0);
                 try {
      Thread.sleep(100);
     } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }
                }
            }
        }
    }

 @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 dataRx = buffer.get(0);
                Log.d(TAG, "dataRx: " + dataRx);
                if(dataRx!=IGNORE_00){
                 
                 stringToRx += (char)dataRx;
                 runOnUiThread(new Runnable(){

      @Override
      public void run() {
       textIn.setText(stringToRx);
      }});
                }
            } else {
                break;
            }
        }
  
 }

}

download filesDownload the files.


Arduino side:

UsbSlave.ino
#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
int pinLED = 13;

const int ST_0 = 0;      //waiting Sync word
const int ST_1_CMD = 1;  //Waiting CMD
const int ST_2_LENGTH= 2;//Receiving Length for CMD_03_TEXT
const int ST_3_DATA= 3;  //Receiving Data for CMD_03_TEXT
const byte IGNORE_00 = 0x00;
const byte SYNC_WORD = 0xFF;
const byte CMD_01_LEDON = 0x01;
const byte CMD_02_LEDOFF= 0x02;
const byte CMD_03_TEXT  = 0x03;
int cmdState;
int dataIndex;

const int MAX_LENGTH = 16;
byte data[MAX_LENGTH];
int dataLength;

void setup() {
  Serial.begin(9600);
  pinMode(pinLED, OUTPUT);
  digitalWrite(pinLED, LOW);
  
  lcd.begin(16, 2);
  lcd.print("USB Host Mode:");
}

void loop() {
  
  if(Serial.available()){
    //lcd.setCursor(0, 1);
    //lcd.print("                ");
    //delay(100);
    //lcd.setCursor(0, 1);
    while(Serial.available() > 0){
      int byteIn = Serial.read();
      //Serial.write(byteIn);
      //lcd.write(byteIn);
      cmdHandle(byteIn);
    }
  }
}

void cmdHandle(int incomingByte){
  
  //prevent from lost-sync
  if(incomingByte == SYNC_WORD){
    cmdState = ST_1_CMD;
    return;
  }
  if(incomingByte == IGNORE_00){
    return;
  }
  
  switch(cmdState){
    case ST_1_CMD:{
          switch(incomingByte){
            case CMD_01_LEDON:
                digitalWrite(pinLED, HIGH);
                break;
            case CMD_02_LEDOFF:
                digitalWrite(pinLED, LOW);
                break;
            case CMD_03_TEXT:
                for(int i=0; i < MAX_LENGTH; i++){
                  data[i] = 0;
                }
                lcd.setCursor(0, 1);
                lcd.print("                ");
                lcd.setCursor(0, 1);
            
                cmdState = ST_2_LENGTH;
                dataIndex = 0;
                break;
            default:
                cmdState = ST_0;
          }
        }
        break;
    case ST_2_LENGTH:{
        dataLength = incomingByte;
        if(dataLength > MAX_LENGTH){
          dataLength = MAX_LENGTH;
        }
        cmdState = ST_3_DATA;
        }
        break;
    case ST_3_DATA:{
          data[dataIndex] = incomingByte;
          dataIndex++;
          
          Serial.write(incomingByte);
          lcd.write(incomingByte);
          
          if(dataIndex==dataLength){
            cmdState = ST_0;
          }
        }
        break;
  }
  
}

Download HERE.

Connection of LCD in Arduino Uno:



- Bi-directional communication between Android and Arduino in USB Host Mode, example 1
- Bi-directional communication between Android and Arduino in USB Host Mode, example 3 - work with 8x8 LED Matrix

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

7 comments:

melegio said...

Hi,
I notice that if the USB is already connected when you lanch the program, it doesn't see it. In other words it detect the USB only when "onResume()" is trigged. How can I modify "onCReate()" to make it finds the USB already attached?
thank you

sunny teki said...

Hello,
Nice tutorial Android-er.I want to know how to set baud rate of 115200 in this code can you please tell me how to do this

Andr.oid Eric said...

hello sunny teki,

Actually I don't know the setting of usbDeviceConnection.controlTransfer()! As i mention in the post Send Hello to Arduino from Android in USB Host Mode. You can reference to the page ANDROID USB HOST + ARDUINO: HOW TO COMMUNICATE WITHOUT ROOTING YOUR ANDROID TABLET OR PHONE.

Manh pham van said...

Hi Android-er

I run example. But arduino don't display. I see signal tranfer anderoi to arduino. but led 13 not toggle when i press on/off

Anonymous said...

Very helpful posts.
Is there a way to send command to arduino when the activity starts without using a button?Where must I put the commands?inside onCreate?
thank you

Ritesh kotiyal said...

Can someone provide me the source file link, the give one is not working

Pavan Kumar said...

Dear Android-er,

it was a nice tutorial.i need to send the data or word at a time not letter by letter and also receive the same at a time.can u please suggest the logic.

Thank you.