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.


Friday, August 29, 2014

Setup Android adb for Wifi debug



To enable Android Debug Bridge (adb) debug via WiFi, connect IP address with the command:

$ adb connect <Android device IP>

or

$ adb connect <Android device IP>:<port>

example:
$ adb connect 192.168.1.111
(the default port number is 5555)

remark:
In my own case, i need to run the command adb tcpip before connect at the first time:
adb tcpip 5555

Check idVendor and idProduct of USB device, Arduino Uno

To check idVendor and idProduct of Arduino Uno, on Ubuntu, enter Linux command dmesg or lsusb after Uno inserted.


Sunday, August 24, 2014

Android Eclipse tips: switch between Automatic and manual target modes

In Eclipse and ADT, you can select launching your app automatically select suitable AVD/device, or manually select.

By default, a run configuration uses the automatic target mode in order to select an AVD/device. If your run configuration uses manual mode, then the "device chooser" is presented every time that your application is run, so that you can select which AVD/device to use.
http://developer.android.com/tools/building/building-eclipse.html#RunConfig

To set Run Configuration:
  • Right click your project -> Run As -> Run Configurations...
  • or, Click the arrow (downward) beside the Play button (green arrow) on the top menu -> Run Configurations...
In Run Configuration Dialog, select your project under Android Application on the left, select Target, then select your expect Deployment Target Selection Mode.



Migrating to Android for iOS Developers

Migrating to Android for iOS Developers gives you—as an experienced native iOS app developer—the skills to learn native Android apps development from scratch. Starting with preparing your Android integrated development environment and introducing just enough Android application framework fundamentals, you’ll understand how to create a simple but meaningful HelloAndroid project immediately.

Migrating to Android for iOS Developers

This book provides the guidelines and tutorial projects to show you how to translate your existing iOS app to the Android platform. You’ll use your mobile app knowledge to structure your Android apps in a similar way to how you would structure your iOS apps. To implement use cases with detailed screens, the most common mobile topics are discussed, including user interfaces, managing data, and networking with remote services. As you move through the book, you’ll create Android apps with rich UI components to handle common CRUD operations locally and remotely.

There are many Android goodies described in the book. Instead of relying on routine text descriptions, you’ll discover the uniqueness of Android and appreciate the many features that are unique to the platform. This book also explores more powerful mobile UX patterns that are commonly used on the iOS and Android platforms.

When you finish reading Migrating to Android for iOS Developers, you’ll be an Android developer as well as an iOS developer. And, you will be fully convinced you can do everything in Android that you can do in iOS.
What you’ll learn
• How to maximize your existing iOS mobile knowledge to learn Android programming skills
• How to use the Android integrated development environment with the Eclipse ADT plugin
• How to translate your existing iOS code to Android with the following common mobile topics:
° Common mobile screen navigation patterns
° User interface components and UI animations
° Storing data
° Networking and using remote services
° Using system apps
° Maps and location awareness
° Mobile search frameworks
° Mobile analytics

Who this book is for
This book is for iOS app developers—like you—who want to port their native iOS app to become an Android app. Also, if you are not an iOS developer, but already familiar with mobile apps, then this book can also help you understand Android development with step-by-step instructions and tutorial projects.

Table of Contents
1. Setup Development Environment
2. Android Programming BasicsChapter
3. Structure your App and Break it into Components
4. Implement Piece by Piece
5. More About Android Application Components
6. Android Application Resources
7. Common Mobile Use Cases
8. Pulling it all together - Recap with a Case Study
9. Appendix