Monday, March 31, 2014

Android send command to Arduino in USB Host mode

This example show how to send command from Android to Arduino Esplora board, in USB Host Mode, to control the LED and Screen of Arduino Esplora. When user toggle the LED by clicking on the button, or change the screen color by slideing the bars, the commands will be add in a command queue, and then send to Arduino in background.


In Android Side:

MainActivity.java.
package com.example.androidusbhost;

import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;

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.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;
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 {
 
 ToggleButton btnLED;
 SeekBar barR, barG, barB;
 TextView textRx;

 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;
 
 ThreadUsbTx threadUsbTx;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  btnLED = (ToggleButton)findViewById(R.id.ledbutton);
  btnLED.setOnClickListener(btnLEDOnClickListener);
  
  barR = (SeekBar)findViewById(R.id.rbar);
  barG = (SeekBar)findViewById(R.id.gbar);
  barB = (SeekBar)findViewById(R.id.bbar);
  barR.setOnSeekBarChangeListener(colorOnSeekBarChangeListener);
  barG.setOnSeekBarChangeListener(colorOnSeekBarChangeListener);
  barB.setOnSeekBarChangeListener(colorOnSeekBarChangeListener);
  
  textRx = (TextView)findViewById(R.id.textrx);
  
  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();
 }
 
 OnClickListener btnLEDOnClickListener =
  new OnClickListener(){

   @Override
   public void onClick(View v) {
    // TODO Auto-generated method stub
    
    int usbResult;
    byte[] cmdLED;
    if(((ToggleButton)v).isChecked()){
     cmdLED = new byte[] {(byte)'L', 'E', 'D', 'O', 'N', '\n'};
    }else{
     cmdLED = new byte[] {(byte)'L', 'E', 'D', 'O', 'F', 'F', '\n'};
    }
    threadUsbTx.insertCmd(cmdLED);
   }
 
 };
 
 OnSeekBarChangeListener colorOnSeekBarChangeListener =
  new OnSeekBarChangeListener(){
  
  byte[] toAscii = { '0', '1', '2', '3', '4', '5', '6', 
    '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

   @Override
   public void onProgressChanged(SeekBar seekBar, int progress,
     boolean fromUser) {}

   @Override
   public void onStartTrackingTouch(SeekBar seekBar) {}

   @Override
   public void onStopTrackingTouch(SeekBar seekBar) {
    
    byte[] cmdCol = new byte[] {(byte)'C', 'O', 'L', '#', 
      '0', '0', '0', '0', '0', '0', '\n'};

    cmdCol[4] = toAscii[(barR.getProgress()>>4) & 0x0f];
    cmdCol[5] = toAscii[barR.getProgress() & 0x0f];
    cmdCol[6] = toAscii[(barG.getProgress()>>4) & 0x0f];
    cmdCol[7] = toAscii[barG.getProgress() & 0x0f];
    cmdCol[8] = toAscii[(barB.getProgress()>>4) & 0x0f];
    cmdCol[9] = toAscii[barB.getProgress() & 0x0f];
    threadUsbTx.insertCmd(cmdCol.clone());
   }
  
 };
 
 private void connectUsb(){
  
  btnLED.setEnabled(false);
  barR.setEnabled(false);
  barG.setEnabled(false);
  barB.setEnabled(false);
  
  Toast.makeText(MainActivity.this, 
    "connectUsb()", 
    Toast.LENGTH_LONG).show();
  textStatus.setText("connectUsb()");

  searchEndPoint();

  if(usbInterfaceFound != null){
   setupUsbComm();
   
   threadUsbTx = new ThreadUsbTx(usbDeviceConnection, endpointOut);
   threadUsbTx.start();
   
   btnLED.setEnabled(true);
   barR.setEnabled(true);
   barG.setEnabled(true);
   barB.setEnabled(true);
   //Turn On LED once connected 
   btnLED.setChecked(true);
   barR.setProgress(0x80);
   barG.setProgress(0x80);
   barB.setProgress(0x80);
   
   threadUsbTx.insertCmd(
    new byte[] {(byte)'L', 'E', 'D', 'O', 'N', '\n'});
   threadUsbTx.insertCmd(
    new byte[] {(byte)'C', 'O', 'L', '#', 
     '8', '0', '8', '0', '8', '0', '\n'});
   
  }

 }

 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;
  
  btnLED.setEnabled(false);
  barR.setEnabled(false);
  barG.setEnabled(false);
  barB.setEnabled(false);
  
  if(threadUsbTx!=null){
   threadUsbTx.setRunning(false);
  }
 }
 
 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){
   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;
    
    /*
     *  D7: 0 - Host to Device
     *   1 = Device to Host
     *  D6..5 Type
     *   0 = Standard
     *   1 = Class
     *   2 = Vendor
     *   3 = Reserved
     *  D4..0 Recipient
     *   0 = Device
     *   1 = Interface
     *   2 = Endpoint
     *   3 = Other
     */
    
    //int requestType = 0x21;
    int requestType = 0x42;

    usbResult = usbDeviceConnection.controlTransfer(
      requestType,
      RQSID_SET_CONTROL_LINE_STATE, //SET_CONTROL_LINE_STATE 
      0,     //value
      0,     //index
      null,    //buffer
      0,     //length
      0);    //timeout
    
    //baud rate = 9600
    //8 data bit
    //1 stop bit
    byte[] encodingSetting = 
      new byte[] {(byte)0x80, 0x25, 0x00, 0x00, 0x00, 0x00, 0x08 };
    usbResult = usbDeviceConnection.controlTransfer(
      requestType,
      RQSID_SET_LINE_CODING,   //SET_LINE_CODING
      0,      //value
      0,      //index
      encodingSetting,  //buffer
      7,      //length
      0);     //timeout

   }

  }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) {
         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)) {
      
      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("");
    }
   }
 
 };
 
 class ThreadUsbTx extends Thread{
  boolean running;
  
  UsbDeviceConnection txConnection;
  UsbEndpoint txEndpoint;
  Queue<byte[]> cmdQueue;
  byte[] cmdToSent;
  
  ThreadUsbTx(UsbDeviceConnection conn, UsbEndpoint endpoint){
   txConnection = conn;
   txEndpoint = endpoint;
   cmdQueue = new LinkedList<byte[]>();
   cmdToSent = null;
   running = true;
  }
  
  public void setRunning(boolean r){
   running = r;
  }
  
  public void insertCmd(byte[] cmd){
   synchronized(cmdQueue){
    cmdQueue.add(cmd);
   }
  }

  @Override
  public void run() {
   
   while(running){

    synchronized(cmdQueue){
     if(cmdQueue.size()>0){
      cmdToSent = cmdQueue.remove();
     }
    }
    
    if(cmdToSent!=null){
     int usbResult = usbDeviceConnection.bulkTransfer(
       endpointOut, 
       cmdToSent, 
       cmdToSent.length, 
       0);
     
     final String s = new String(cmdToSent);
     
     runOnUiThread(new Runnable(){

      @Override
      public void run() {
       Toast.makeText(MainActivity.this, 
         s, 
         Toast.LENGTH_SHORT).show();
      }});

     cmdToSent = null;
    }
   }
  }
 }
}

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" />
    
    <ToggleButton
        android:id="@+id/ledbutton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textOn="LED ON"
        android:textOff="LED OFF" />
    <SeekBar
        android:id="@+id/rbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="255"
        android:progress="0"/>
    <SeekBar
        android:id="@+id/gbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="255"
        android:progress="0"/>
    <SeekBar
        android:id="@+id/bbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="255"
        android:progress="0"/>
    <TextView
        android:id="@+id/textrx"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textStyle="bold"
        android:textColor="#ff0000" />
    
    <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 filesDownload the files.

In Arduino Side: re-use the code in my another blog Node.js + Arduino, running on PC.
#include <Esplora.h>
#include <TFT.h>
#include <SPI.h>

int MAX_CMD_LENGTH = 10;
char cmd[10];
int cmdIndex;
char incomingByte;

int prevSlider = 0;

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);
    
    cmdIndex = 0;
    
}
 
void loop() {
    
    if (incomingByte=Serial.available()>0) {
      
      char byteIn = Serial.read();
      cmd[cmdIndex] = byteIn;
      
      if(byteIn=='\n'){
        //command finished
        cmd[cmdIndex] = '\0';
        //Serial.println(cmd);
        cmdIndex = 0;
        
        String stringCmd = String(cmd);
        
        if(strcmp(cmd, "LEDON")  == 0){
          //Serial.println("Command received: LEDON");
          Esplora.writeRGB(255, 255, 255);
        }else if (strcmp(cmd, "LEDOFF")  == 0) {
          //Serial.println("Command received: LEDOFF");
          Esplora.writeRGB(0, 0, 0);
        }else if(stringCmd.substring(0,4)="COL#"){
          //Serial.println("Command received: COL#");
          if(stringCmd.length()==10){
            char * pEnd;
            long int rgb = strtol(&cmd[4], &pEnd, 16);
            int r = (rgb & 0xff0000) >> 16;
            int g = (rgb & 0xff00) >> 8;
            int b = rgb & 0xff;
            //Serial.println(r);
            //Serial.println(g);
            //Serial.println(b);
            EsploraTFT.background(b,g,r);
          }
        }else{
          //Serial.println("Command received: unknown!");
        }
        
      }else{
        if(cmdIndex++ >= MAX_CMD_LENGTH){
          cmdIndex = 0;
        }
      }
    }
    
    //Read Slider
    int slider = Esplora.readSlider();
    //convert slider value from [0-1023] to [0x00-0xFF]
    slider = slider>>2 & 0xff;
    
    if(slider!=prevSlider){
      prevSlider = slider;
      
      String stringSlider = String(slider, HEX);
      Serial.println("SLD#" + stringSlider +"\n");
    }
    
}

Remark:
The device-to-host (Arduino-to-Android) communication have NOT been implemented on Android side in this example.


Step-by-step: Android USB Host Mode programming

2 comments:

  1. hi,
    thanks for this post :)

    Can i do this by using Arduino UNO or Leonardo ??

    ReplyDelete