Showing posts with label Android code sample: usb. Show all posts
Showing posts with label Android code sample: usb. Show all posts

Wednesday, April 15, 2015

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

It's the 3rd example to implement Bi-directional communication between Android and Arduino in USB Host Mode, modified from another example "Android + Arduino Uno: display bits and ASCII char on LED Matrix".


On the Android to Arduino Uno direction, user tap on the custom view (8x8 squares) to turn ON/OFF LEDs on Arduino, or select character from the spiner.
On the Arduino Uno to Android direction, tap on the "Load from Arduino" button on Android, Android send a command to Arduino, then Arduino send back 64 bytes back to Android, represent (inverted) ON/OFF state of each dots.
The right hand side TextView show the data received on Android.


Roughly, to implement receiving on Android
- MainActivity implement Runnable
- override run() method to monitor UsbRequest
- start background thread after openDevice() to run the run() method

Connection:
Android - USB OTG Cable - USB Cable - Arduino Uno - 8x8 LED Matrix


Android side:

/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.androidtouchview.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" />

    <TextView
        android:id="@+id/textstatus"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Status"
        android:textStyle="bold" />

    <Spinner
        android:id="@+id/asciispinner"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/load"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Load from Arduino" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal" >

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:orientation="vertical" >

            <GridLayout
                android:id="@+id/mygrid"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_gravity="center"
                android:background="@android:color/background_light"
                android:columnCount="8"
                android:orientation="horizontal"
                android:rowCount="8" >
            </GridLayout>
        </LinearLayout>

        <TextView
            android:id="@+id/logtext"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:textColor="#FFFFFF"
            android:background="#A0A0A0" />
    </LinearLayout>

</LinearLayout>

MainActivity.java
package com.example.androidusbmatrix;

import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Iterator;

import com.example.androidusbmatrix.MyView.OnToggledListener;

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.hardware.usb.UsbRequest;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.GridLayout;
import android.widget.Spinner;
import android.widget.TextView;

public class MainActivity extends ActionBarActivity 
 implements OnToggledListener, Runnable{

 Button btnLoad;
 TextView textStatus, logText;
 volatile MyView[] myViews;
 GridLayout myGridLayout;
 
 private static final int targetVendorID = 9025; //Arduino Uno
 private static final int targetProductID = 67; //Arduino Uno, not 0067
 private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
 PendingIntent mPermissionIntent;
 UsbDevice deviceFound = null;
 UsbInterface usbInterfaceFound = null;
 UsbInterface usbInterface;
 UsbDeviceConnection usbDeviceConnection;
 UsbEndpoint endpointIn = null;
 UsbEndpoint endpointOut = null;
 
 Spinner spAscii;
 AsciiCode Asciis[];
 ArrayAdapter<AsciiCode> asciiArrayAdapter;
 
 final byte SYNC_WORD   = (byte) 0xff;
 final byte CMD_01_Bits    = 0x01;
 final byte CMD_02_SingleChar  = 0x02;
 final byte CMD_03_Load   = 0x03;
 
 String logString = "";

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  textStatus = (TextView) findViewById(R.id.textstatus);
  logText = (TextView) findViewById(R.id.logtext);
  myGridLayout = (GridLayout) findViewById(R.id.mygrid);

  int numOfCol = myGridLayout.getColumnCount();
  int numOfRow = myGridLayout.getRowCount();
  myViews = new MyView[numOfCol * numOfRow];
  for (int yPos = 0; yPos < numOfRow; yPos++) {
   for (int xPos = 0; xPos < numOfCol; xPos++) {
    MyView tView = new MyView(this, xPos, yPos);
    tView.setOnToggledListener(this);
    myViews[yPos * numOfCol + xPos] = tView;
    myGridLayout.addView(tView);
   }
  }

  myGridLayout.getViewTreeObserver().addOnGlobalLayoutListener(
    new OnGlobalLayoutListener() {

     @Override
     public void onGlobalLayout() {

      int pLength;
      final int MARGIN = 5;

      int pWidth = myGridLayout.getWidth();
      int pHeight = myGridLayout.getHeight();
      int numOfCol = myGridLayout.getColumnCount();
      int numOfRow = myGridLayout.getRowCount();

      // Set myGridLayout equal width and height
      if (pWidth >= pHeight) {
       pLength = pHeight;
      } else {
       pLength = pWidth;
      }
      ViewGroup.LayoutParams pParams = myGridLayout
        .getLayoutParams();
      pParams.width = pLength;
      pParams.height = pLength;
      myGridLayout.setLayoutParams(pParams);

      int w = pLength / numOfCol; // pWidth/numOfCol;
      int h = pLength / numOfRow; // pHeight/numOfRow;

      for (int yPos = 0; yPos < numOfRow; yPos++) {
       for (int xPos = 0; xPos < numOfCol; xPos++) {
        GridLayout.LayoutParams params = (GridLayout.LayoutParams) myViews[yPos
          * numOfCol + xPos].getLayoutParams();
        params.width = w - 2 * MARGIN;
        params.height = h - 2 * MARGIN;
        params.setMargins(MARGIN, MARGIN, MARGIN,
          MARGIN);
        myViews[yPos * numOfCol + xPos]
          .setLayoutParams(params);
       }
      }

      // deprecated in API level 16
      myGridLayout.getViewTreeObserver()
        .removeGlobalOnLayoutListener(this);
      // for API Level >= 16
      // myGridLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
     }
    });
  
  // 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();
  
  spAscii = (Spinner)findViewById(R.id.asciispinner);
  initAsciis();
  asciiArrayAdapter = new ArrayAdapter<AsciiCode>(this,
    android.R.layout.simple_list_item_1, 
    android.R.id.text1, 
    Asciis);
  asciiArrayAdapter.setDropDownViewResource(
    android.R.layout.simple_spinner_dropdown_item);
  spAscii.setAdapter(asciiArrayAdapter);
  spAscii.setOnItemSelectedListener(new OnItemSelectedListener(){

   @Override
   public void onItemSelected(AdapterView<?> parent, View view,
     int position, long id) {
    AsciiCode item = (AsciiCode) parent.getItemAtPosition(position);
    
    if(deviceFound != null){
     byte[] bytesSend = new byte[3];
     
     bytesSend[0] = SYNC_WORD;
     bytesSend[1] = CMD_02_SingleChar; //CMD
     bytesSend[2] = (byte) item.charAscii;
     
     usbDeviceConnection.bulkTransfer(endpointOut,
       bytesSend, bytesSend.length, 0);
    }
   }

   @Override
   public void onNothingSelected(AdapterView<?> arg0) {
    // TODO Auto-generated method stub
    
   }});
  
  btnLoad = (Button)findViewById(R.id.load);
  btnLoad.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View arg0) {
    logString = "";
    logText.setText(logString);
    LoadFromArduino();
   }});
 }
 
 private void LoadFromArduino(){
  if(deviceFound != null){
   indexRx = 0;
   
   byte[] bytesSend = new byte[2];
   
   bytesSend[0] = SYNC_WORD;
   bytesSend[1] = CMD_03_Load;
   usbDeviceConnection.bulkTransfer(endpointOut,
     bytesSend, bytesSend.length, 0);

  }
 }

 @Override
 public void OnToggled(MyView v, boolean touchOn) {
  if(deviceFound != null){
   byte[] bytesSend = setupCmd();

   usbDeviceConnection.bulkTransfer(endpointOut,
     bytesSend, bytesSend.length, 0);
  }
 }
 
 private byte[] setupCmd(){
  byte[] bytes = new byte[18];
  
  bytes[0] = SYNC_WORD;
  bytes[1] = CMD_01_Bits; //CMD
  
  int i = 2;
  int numOfRow = 8;
  int numOfCol = 8;
  int numOfHalfCol = 4;

  for (int yPos = 0; yPos < numOfRow; yPos++) {
   byte b = 0x00;
   byte mask = 0x01;

   for (int xPos = 0; xPos < numOfHalfCol; xPos++) {
    
    if(myViews[yPos * numOfCol + xPos].touchOn){
     b = (byte) (b | mask);
    }
    
    mask = (byte) (mask << 1);
   }
   bytes[i] = b;
   
   i++;
   b = 0x00;
   mask = 0x10;
   for (int xPos = numOfHalfCol; xPos < numOfCol; xPos++) {
    
    if(myViews[yPos * numOfCol + xPos].touchOn){
     b = (byte) (b | mask);
    }
    
    mask = (byte) (mask << 1);
   }
   bytes[i] = b;
   
   i++;
  }

  return bytes;
 }
 
 //usb related
 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);

    usbDeviceConnection.controlTransfer(0x21, // 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 };
    usbDeviceConnection.controlTransfer(0x21, // requestType
      RQSID_SET_LINE_CODING, // SET_LINE_CODING
      0, // value
      0, // index
      encodingSetting, // buffer
      7, // length
      0); // timeout

    //2015-04-15
                Thread thread = new Thread(this);
                thread.start();
   }

  } else {
   manager.requestPermission(deviceFound, mPermissionIntent);
   textStatus.setText("Permission: " + permitToRead);
  }

  return success;
 }
 
 private void connectUsb() {

  textStatus.setText("connectUsb()");

  searchEndPoint();

  if (usbInterfaceFound != null) {
   setupUsbComm();
  }

 }
 
 private void releaseUsb() {

  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() {

  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 {

   // 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;
     }
    }

   }

  }
 }
 
 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 {
      textStatus.setText("permission denied for 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);

    textStatus.setText("ACTION_USB_DEVICE_ATTACHED");

    connectUsb();

   } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {

    UsbDevice device = (UsbDevice) intent
      .getParcelableExtra(UsbManager.EXTRA_DEVICE);

    textStatus.setText("ACTION_USB_DEVICE_DETACHED");

    if (device != null) {
     if (device == deviceFound) {
      releaseUsb();
     }
    }
    
   }
  }

 };
 
 public void initAsciis() {
  Asciis = new AsciiCode[128];
  for (int i = 0; i < 128; i++) {
   Asciis[i] = new AsciiCode(i);
  }
 }
 
 class AsciiCode{
  int id;
  char charAscii;
  
  AsciiCode(int id){
   this.id = id;
   charAscii = (char)id;
  }
  
  public String toString(){
   return 
    String.format("%03d", id)
    + " /(hex) 0x" + String.format("%02x", id).toUpperCase()
    + " : " + charAscii;
  }

 }

 int indexRx;
 final int RX_STATE_0_IDLE = 0;
 final int RX_STATE_1_RXing = 1;
 int RxState;
 final byte PIXELON = 'A';
 final byte PIXELOFF = 'B';
 //Handle receiving from Arduino
 @Override
 public void run() {
  RxState = RX_STATE_0_IDLE;
  ByteBuffer buffer = ByteBuffer.allocate(1);
        UsbRequest request = new UsbRequest();
        request.initialize(usbDeviceConnection, endpointIn);
        while (true) {
         request.queue(buffer, 1);
         if (usbDeviceConnection.requestWait() == request) {
          final byte dataRx = buffer.get(0);
          
          
          logString += String.format("%02d", dataRx) + " ";
          runOnUiThread(new Runnable(){

           @Override
           public void run() {
            
            logText.setText(logString);
           }});

          if(indexRx < myViews.length){
           //only accept two value
           //ignore unaccept value
           //I found always have '0' received, 
           //I don't know why!
              if(dataRx == PIXELON){
               myViews[indexRx].touchOn = true;
               rqsRedrawMyView(indexRx);
               indexRx++;
              }else if(dataRx == PIXELOFF){
               myViews[indexRx].touchOn = false;
               rqsRedrawMyView(indexRx);
               indexRx++;
              }
              
          }
         }else{
          break;
         }
         
        }
 }
 
 private void rqsRedrawMyView(final int i){
  runOnUiThread(new Runnable(){

   @Override
   public void run() {
    myViews[i].invalidate();
   }});
 }

}

MyView.java, custom view for each square
package com.example.androidusbmatrix;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class MyView extends View {
 
 public interface OnToggledListener {
  void OnToggled(MyView v, boolean touchOn);
 }

 boolean touchOn;
 boolean mDownTouch = false;
 private OnToggledListener toggledListener;
 int idX = 0; //default
 int idY = 0; //default

 public MyView(Context context, int x, int y) {
  super(context);
  idX = x;
  idY = y;
  init();
 }
 
 public MyView(Context context) {
  super(context);
  init();
 }

 public MyView(Context context, AttributeSet attrs) {
  super(context, attrs);
  init();
 }

 public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  init();
 }

 private void init() {
  touchOn = false;
 }

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
    MeasureSpec.getSize(heightMeasureSpec));
 }

 @Override
 protected void onDraw(Canvas canvas) {
  if (touchOn) {
   canvas.drawColor(Color.RED);
  } else {
   canvas.drawColor(Color.GRAY);
  }
 }

 @Override
 public boolean onTouchEvent(MotionEvent event) {
  super.onTouchEvent(event);

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
             
             touchOn = !touchOn;
       invalidate();
       
       if(toggledListener != null){
        toggledListener.OnToggled(this, touchOn);
       }
             
                mDownTouch = true;
                return true;

            case MotionEvent.ACTION_UP:
                if (mDownTouch) {
                    mDownTouch = false;
                    performClick();
                    return true;
                }
        }
        return false;
 }

 @Override
 public boolean performClick() {
        super.performClick();
        return true;
 }
 
 public void setOnToggledListener(OnToggledListener listener){
  toggledListener = listener;
 }
 
 public int getIdX(){
  return idX;
 }
 
 public int getIdY(){
  return idY;
 }

}

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.androidusbmatrix"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-feature android:name="android.hardware.usb.host" />
    <uses-sdk
        android:minSdkVersion="14"
        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: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>

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


download filesDownload the files.

Arduino side:

UnoUsbMatrix.ino
/*
 *  http://android-er.blogspot.com
 *  http://arduino-er.blogspot.com/
 */

byte font[128][8] = {
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0000 (nul)
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0001
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0002
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0003
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0004
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0005
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0006
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0007
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0008
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0009
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000A
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000B
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000C
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000D
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000E
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000F
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0010
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0011
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0012
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0013
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0014
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0015
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0016
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0017
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0018
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0019
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001A
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001B
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001C
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001D
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001E
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001F
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0020 (space)
    { 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00},   // U+0021 (!)
    { 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0022 (")
    { 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00},   // U+0023 (#)
    { 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00},   // U+0024 ($)
    { 0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00},   // U+0025 (%)
    { 0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00},   // U+0026 (&)
    { 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0027 (')
    { 0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00},   // U+0028 (()
    { 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00},   // U+0029 ())
    { 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00},   // U+002A (*)
    { 0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00},   // U+002B (+)
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06},   // U+002C (,)
    { 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00},   // U+002D (-)
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00},   // U+002E (.)
    { 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00},   // U+002F (/)
    { 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00},   // U+0030 (0)
    { 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00},   // U+0031 (1)
    { 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00},   // U+0032 (2)
    { 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00},   // U+0033 (3)
    { 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00},   // U+0034 (4)
    { 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00},   // U+0035 (5)
    { 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00},   // U+0036 (6)
    { 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00},   // U+0037 (7)
    { 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00},   // U+0038 (8)
    { 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00},   // U+0039 (9)
    { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00},   // U+003A (:)
    { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06},   // U+003B (//)
    { 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00},   // U+003C (<)
    { 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00},   // U+003D (=)
    { 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00},   // U+003E (>)
    { 0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00},   // U+003F (?)
    { 0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00},   // U+0040 (@)
    { 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00},   // U+0041 (A)
    { 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00},   // U+0042 (B)
    { 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00},   // U+0043 (C)
    { 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00},   // U+0044 (D)
    { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00},   // U+0045 (E)
    { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00},   // U+0046 (F)
    { 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00},   // U+0047 (G)
    { 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00},   // U+0048 (H)
    { 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00},   // U+0049 (I)
    { 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00},   // U+004A (J)
    { 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00},   // U+004B (K)
    { 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00},   // U+004C (L)
    { 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00},   // U+004D (M)
    { 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00},   // U+004E (N)
    { 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00},   // U+004F (O)
    { 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00},   // U+0050 (P)
    { 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00},   // U+0051 (Q)
    { 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00},   // U+0052 (R)
    { 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00},   // U+0053 (S)
    { 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00},   // U+0054 (T)
    { 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00},   // U+0055 (U)
    { 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00},   // U+0056 (V)
    { 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00},   // U+0057 (W)
    { 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00},   // U+0058 (X)
    { 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00},   // U+0059 (Y)
    { 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00},   // U+005A (Z)
    { 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00},   // U+005B ([)
    { 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00},   // U+005C (\)
    { 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00},   // U+005D (])
    { 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00},   // U+005E (^)
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF},   // U+005F (_)
    { 0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0060 (`)
    { 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00},   // U+0061 (a)
    { 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00},   // U+0062 (b)
    { 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00},   // U+0063 (c)
    { 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00},   // U+0064 (d)
    { 0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00},   // U+0065 (e)
    { 0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00},   // U+0066 (f)
    { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F},   // U+0067 (g)
    { 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00},   // U+0068 (h)
    { 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00},   // U+0069 (i)
    { 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E},   // U+006A (j)
    { 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00},   // U+006B (k)
    { 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00},   // U+006C (l)
    { 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00},   // U+006D (m)
    { 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00},   // U+006E (n)
    { 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00},   // U+006F (o)
    { 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F},   // U+0070 (p)
    { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78},   // U+0071 (q)
    { 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00},   // U+0072 (r)
    { 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00},   // U+0073 (s)
    { 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00},   // U+0074 (t)
    { 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00},   // U+0075 (u)
    { 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00},   // U+0076 (v)
    { 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00},   // U+0077 (w)
    { 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00},   // U+0078 (x)
    { 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F},   // U+0079 (y)
    { 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00},   // U+007A (z)
    { 0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00},   // U+007B ({)
    { 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00},   // U+007C (|)
    { 0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00},   // U+007D (})
    { 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+007E (~)
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}    // U+007F
};

// 2-dimensional array of row pin numbers:
const int row[8] = {
  2, 7, 19, 5, 13, 18, 12, 16
};

// 2-dimensional array of column pin numbers:
const int col[8] = {
  6, 11, 10, 3, 17, 4, 8, 9
};

// 2-dimensional array of pixels:
int pixels[8][8];

const int ST_0 = 0;       //waiting Sync word
const int ST_1_CMD = 1;   //Waiting CMD
const int ST_2_DATA= 2;  //Receiving Data for CMD_01_Bits
const int ST_3_CHAR= 3;  //Receiving Single Char for CMD_02_SingleChar
const int DATA_LENGTH = 16;  //length of data = 16
const byte SYNC_WORD = 0xFF;
const byte CMD_01_Bits = 0x01;        //Send as 8x8 bit map
const byte CMD_02_SingleChar = 0x02;  //single char
const byte CMD_03_Load = 0x03;        //Load from Arduino to Android
int cmdState;
int dataIndex;

byte data[DATA_LENGTH];

//represent pixel ON/OFF send back to Android
const byte PIXELON = 'A';
const byte PIXELOFF = 'B';

void setup() {
  
  Serial.begin(9600);

  // initialize the I/O pins as outputs
  // iterate over the pins:
  for (int thisPin = 0; thisPin < 8; thisPin++) {
    // initialize the output pins:
    pinMode(col[thisPin], OUTPUT);
    pinMode(row[thisPin], OUTPUT);
    // take the col pins (i.e. the cathodes) high to ensure that
    // the LEDS are off:
    digitalWrite(col[thisPin], HIGH);
  }
  
  for(int y=0; y<8; y++){
    for(int x=0; x<8; x++){
      pixels[x][y] = HIGH;
    }
  }
  
  cmdState = ST_0;
}

void loop() {
  refreshScreen();
  
  if(Serial.available()){
    cmdHandle(Serial.read());
  }

}

void cmdHandle(int incomingByte){
  
  //prevent from lost-sync
  if(incomingByte == SYNC_WORD){
    cmdState = ST_1_CMD;
    return;
  }
  
  switch(cmdState){
    case ST_0:
        if(incomingByte == SYNC_WORD){
          cmdState = ST_1_CMD;
        }
        break;
    case ST_1_CMD:{
          switch(incomingByte){
            case CMD_01_Bits:
                cmdState = ST_2_DATA;
                dataIndex = 0;
                break;
            case CMD_02_SingleChar:
                cmdState = ST_3_CHAR;
                break;
            case CMD_03_Load:
                //The program will block here
                //also no LED scan
                SendToAndroid();  
                cmdState = ST_0;
                break;
            default:
                cmdState = ST_0;
          }
        }
        break;
    case ST_2_DATA:{
          data[dataIndex] = incomingByte;
          dataIndex++;
        
          int iData = 0;
          if(dataIndex==DATA_LENGTH){
            cmdState = ST_0;
            for(int i=0; i<8; i++){
              byte mask = 0x01;
            
              for(int j=0; j<4; j++){
                  if (data[iData] & mask){
                    pixels[i][j] = LOW;
                  }else{
                    pixels[i][j] = HIGH;
                  }
                  mask = mask << 1;
              }
            
              iData++;
              for(int j=4; j<8; j++){
                  if (data[iData] & mask){
                    pixels[i][j] = LOW;
                  }else{
                    pixels[i][j] = HIGH;
                  }
                  mask = mask << 1;
              }
            
              iData++;
            }
          }
        }
        break;
      
      case ST_3_CHAR:
        setupChar((char)incomingByte);
        cmdState = ST_0;
        break;
        
  }
  
}

void SendToAndroid(){
  for(int i=0; i<8; i++){
    for(int j=0; j<8; j++){
      //send back inverted ON/OFF state
      if(pixels[i][j] == LOW){
        Serial.write(PIXELOFF);
      }else{
        Serial.write(PIXELON);
      }
      delay(10);
    }
  }
}

void setupChar(char c){

  for (int x = 0; x < 8; x++) {
    byte bitMask = 0x01;
    byte f = font[c][x];
    for (int y = 0; y < 8; y++) {
      if (f & bitMask){
        pixels[x][y] = LOW;
      }else{
        pixels[x][y] = HIGH;
      }
      bitMask = bitMask << 1;
    }
  }

}

void refreshScreen() {
  // iterate over the rows (anodes):
  for (int thisRow = 0; thisRow < 8; thisRow++) {
    // take the row pin (anode) high:
    digitalWrite(row[thisRow], HIGH);
    // iterate over the cols (cathodes):
    for (int thisCol = 0; thisCol < 8; thisCol++) {
      // get the state of the current pixel;
      int thisPixel = pixels[thisRow][thisCol];
      // when the row is HIGH and the col is LOW,
      // the LED where they meet turns on:
      digitalWrite(col[thisCol], thisPixel);
      // turn the pixel off:
      if (thisPixel == LOW) {
        digitalWrite(col[thisCol], HIGH);
      }
    }
    // take the row pin low to turn off the whole row:
    digitalWrite(row[thisRow], LOW);
  }
}

Download Arduino ino code HERE.

Connection between Arduino Uno and 8x8 LED Matrix:


Check on the right hand side TextView, the received data log: There are some unexpected '00' received. I don't know the reason! Please anybody can advise.


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

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

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

Monday, September 8, 2014

Android + Arduino Uno: display bits and ASCII char on LED Matrix

Add extra command of Displaying single ASCII char to previous example of "Android USB Host Mode control Arduino Uno + 8x8 LED Matrix".




Android Side:

Modify /res/layout/activity_main.xml to add Spinner for ASCII char selection.
<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.androidtouchview.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" />
    
    <TextView
        android:id="@+id/textstatus"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textStyle="bold"
        android:text="Status" />
    
    <Spinner
        android:id="@+id/asciispinner"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <GridLayout
        android:id="@+id/mygrid"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal"
        android:columnCount="8"
        android:rowCount="8"
        android:layout_gravity="center"
        android:background="@android:color/background_light" >
    </GridLayout>

</LinearLayout>

MainActivity.java
package com.example.androidusbmatrix;

import java.util.HashMap;
import java.util.Iterator;

import com.example.androidusbmatrix.MyView.OnToggledListener;

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.ViewGroup;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.GridLayout;
import android.widget.Spinner;
import android.widget.TextView;

public class MainActivity extends ActionBarActivity 
 implements OnToggledListener{

 TextView textStatus;
 MyView[] myViews;
 GridLayout myGridLayout;
 
 private static final int targetVendorID = 9025; //Arduino Uno
 private static final int targetProductID = 67; //Arduino Uno, not 0067
 private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
 PendingIntent mPermissionIntent;
 UsbDevice deviceFound = null;
 UsbInterface usbInterfaceFound = null;
 UsbInterface usbInterface;
 UsbDeviceConnection usbDeviceConnection;
 UsbEndpoint endpointIn = null;
 UsbEndpoint endpointOut = null;
 
 Spinner spAscii;
 AsciiCode Asciis[];
 ArrayAdapter<AsciiCode> asciiArrayAdapter;
 
 final byte SYNC_WORD   = (byte) 0xff;
 final byte CMD_01_Bits    = 0x01;
 final byte CMD_02_SingleChar  = 0x02;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  textStatus = (TextView) findViewById(R.id.textstatus);
  myGridLayout = (GridLayout) findViewById(R.id.mygrid);

  int numOfCol = myGridLayout.getColumnCount();
  int numOfRow = myGridLayout.getRowCount();
  myViews = new MyView[numOfCol * numOfRow];
  for (int yPos = 0; yPos < numOfRow; yPos++) {
   for (int xPos = 0; xPos < numOfCol; xPos++) {
    MyView tView = new MyView(this, xPos, yPos);
    tView.setOnToggledListener(this);
    myViews[yPos * numOfCol + xPos] = tView;
    myGridLayout.addView(tView);
   }
  }

  myGridLayout.getViewTreeObserver().addOnGlobalLayoutListener(
    new OnGlobalLayoutListener() {

     @Override
     public void onGlobalLayout() {

      int pLength;
      final int MARGIN = 5;

      int pWidth = myGridLayout.getWidth();
      int pHeight = myGridLayout.getHeight();
      int numOfCol = myGridLayout.getColumnCount();
      int numOfRow = myGridLayout.getRowCount();

      // Set myGridLayout equal width and height
      if (pWidth >= pHeight) {
       pLength = pHeight;
      } else {
       pLength = pWidth;
      }
      ViewGroup.LayoutParams pParams = myGridLayout
        .getLayoutParams();
      pParams.width = pLength;
      pParams.height = pLength;
      myGridLayout.setLayoutParams(pParams);

      int w = pLength / numOfCol; // pWidth/numOfCol;
      int h = pLength / numOfRow; // pHeight/numOfRow;

      for (int yPos = 0; yPos < numOfRow; yPos++) {
       for (int xPos = 0; xPos < numOfCol; xPos++) {
        GridLayout.LayoutParams params = (GridLayout.LayoutParams) myViews[yPos
          * numOfCol + xPos].getLayoutParams();
        params.width = w - 2 * MARGIN;
        params.height = h - 2 * MARGIN;
        params.setMargins(MARGIN, MARGIN, MARGIN,
          MARGIN);
        myViews[yPos * numOfCol + xPos]
          .setLayoutParams(params);
       }
      }

      // deprecated in API level 16
      myGridLayout.getViewTreeObserver()
        .removeGlobalOnLayoutListener(this);
      // for API Level >= 16
      // myGridLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
     }
    });
  
  // 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();
  
  spAscii = (Spinner)findViewById(R.id.asciispinner);
  initAsciis();
  asciiArrayAdapter = new ArrayAdapter<AsciiCode>(this,
    android.R.layout.simple_list_item_1, 
    android.R.id.text1, 
    Asciis);
  asciiArrayAdapter.setDropDownViewResource(
    android.R.layout.simple_spinner_dropdown_item);
  spAscii.setAdapter(asciiArrayAdapter);
  spAscii.setOnItemSelectedListener(new OnItemSelectedListener(){

   @Override
   public void onItemSelected(AdapterView<?> parent, View view,
     int position, long id) {
    AsciiCode item = (AsciiCode) parent.getItemAtPosition(position);
    
    if(deviceFound != null){
     byte[] bytesSend = new byte[3];
     
     bytesSend[0] = SYNC_WORD;
     bytesSend[1] = CMD_02_SingleChar; //CMD
     bytesSend[2] = (byte) item.charAscii;
     
     usbDeviceConnection.bulkTransfer(endpointOut,
       bytesSend, bytesSend.length, 0);
    }
   }

   @Override
   public void onNothingSelected(AdapterView<?> arg0) {
    // TODO Auto-generated method stub
    
   }});
  
 }

 @Override
 public void OnToggled(MyView v, boolean touchOn) {
  if(deviceFound != null){
   byte[] bytesSend = setupCmd();

   usbDeviceConnection.bulkTransfer(endpointOut,
     bytesSend, bytesSend.length, 0);
  }
 }
 
 private byte[] setupCmd(){
  byte[] bytes = new byte[18];
  
  bytes[0] = SYNC_WORD;
  bytes[1] = CMD_01_Bits; //CMD
  
  int i = 2;
  int numOfRow = 8;
  int numOfCol = 8;
  int numOfHalfCol = 4;

  for (int yPos = 0; yPos < numOfRow; yPos++) {
   byte b = 0x00;
   byte mask = 0x01;

   for (int xPos = 0; xPos < numOfHalfCol; xPos++) {
    
    if(myViews[yPos * numOfCol + xPos].touchOn){
     b = (byte) (b | mask);
    }
    
    mask = (byte) (mask << 1);
   }
   bytes[i] = b;
   
   i++;
   b = 0x00;
   mask = 0x10;
   for (int xPos = numOfHalfCol; xPos < numOfCol; xPos++) {
    
    if(myViews[yPos * numOfCol + xPos].touchOn){
     b = (byte) (b | mask);
    }
    
    mask = (byte) (mask << 1);
   }
   bytes[i] = b;
   
   i++;
  }

  return bytes;
 }
 
 //usb related
 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);

    usbDeviceConnection.controlTransfer(0x21, // 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 };
    usbDeviceConnection.controlTransfer(0x21, // requestType
      RQSID_SET_LINE_CODING, // SET_LINE_CODING
      0, // value
      0, // index
      encodingSetting, // buffer
      7, // length
      0); // timeout
   }

  } else {
   manager.requestPermission(deviceFound, mPermissionIntent);
   textStatus.setText("Permission: " + permitToRead);
  }

  return success;
 }
 
 private void connectUsb() {

  textStatus.setText("connectUsb()");

  searchEndPoint();

  if (usbInterfaceFound != null) {
   setupUsbComm();
  }

 }
 
 private void releaseUsb() {

  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() {

  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 {

   // 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;
     }
    }

   }

  }
 }
 
 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 {
      textStatus.setText("permission denied for 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);

    textStatus.setText("ACTION_USB_DEVICE_ATTACHED");

    connectUsb();

   } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {

    UsbDevice device = (UsbDevice) intent
      .getParcelableExtra(UsbManager.EXTRA_DEVICE);

    textStatus.setText("ACTION_USB_DEVICE_DETACHED");

    if (device != null) {
     if (device == deviceFound) {
      releaseUsb();
     }
    }
    
   }
  }

 };
 
 public void initAsciis() {
  Asciis = new AsciiCode[128];
  for (int i = 0; i < 128; i++) {
   Asciis[i] = new AsciiCode(i);
  }
 }
 
 class AsciiCode{
  int id;
  char charAscii;
  
  AsciiCode(int id){
   this.id = id;
   charAscii = (char)id;
  }
  
  public String toString(){
   return 
    String.format("%03d", id)
    + " /(hex) 0x" + String.format("%02x", id).toUpperCase()
    + " : " + charAscii;
  }

 }

}

Other files, AndroidManifest.xml, device_filter.xml and MyView.java, refer to last post.

download filesDownload the files.


Arduino side:

UnoUsbMatrix.ino
/*
 *  Modify from Row-Column Scanning an 8x8 LED matrix tutorial
 *  http://android-er.blogspot.com
 *  http://arduino-er.blogspot.com/
 */

byte font[128][8] = {
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0000 (nul)
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0001
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0002
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0003
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0004
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0005
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0006
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0007
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0008
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0009
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000A
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000B
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000C
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000D
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000E
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000F
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0010
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0011
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0012
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0013
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0014
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0015
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0016
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0017
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0018
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0019
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001A
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001B
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001C
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001D
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001E
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001F
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0020 (space)
    { 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00},   // U+0021 (!)
    { 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0022 (")
    { 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00},   // U+0023 (#)
    { 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00},   // U+0024 ($)
    { 0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00},   // U+0025 (%)
    { 0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00},   // U+0026 (&)
    { 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0027 (')
    { 0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00},   // U+0028 (()
    { 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00},   // U+0029 ())
    { 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00},   // U+002A (*)
    { 0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00},   // U+002B (+)
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06},   // U+002C (,)
    { 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00},   // U+002D (-)
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00},   // U+002E (.)
    { 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00},   // U+002F (/)
    { 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00},   // U+0030 (0)
    { 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00},   // U+0031 (1)
    { 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00},   // U+0032 (2)
    { 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00},   // U+0033 (3)
    { 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00},   // U+0034 (4)
    { 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00},   // U+0035 (5)
    { 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00},   // U+0036 (6)
    { 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00},   // U+0037 (7)
    { 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00},   // U+0038 (8)
    { 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00},   // U+0039 (9)
    { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00},   // U+003A (:)
    { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06},   // U+003B (//)
    { 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00},   // U+003C (<)
    { 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00},   // U+003D (=)
    { 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00},   // U+003E (>)
    { 0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00},   // U+003F (?)
    { 0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00},   // U+0040 (@)
    { 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00},   // U+0041 (A)
    { 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00},   // U+0042 (B)
    { 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00},   // U+0043 (C)
    { 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00},   // U+0044 (D)
    { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00},   // U+0045 (E)
    { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00},   // U+0046 (F)
    { 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00},   // U+0047 (G)
    { 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00},   // U+0048 (H)
    { 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00},   // U+0049 (I)
    { 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00},   // U+004A (J)
    { 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00},   // U+004B (K)
    { 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00},   // U+004C (L)
    { 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00},   // U+004D (M)
    { 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00},   // U+004E (N)
    { 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00},   // U+004F (O)
    { 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00},   // U+0050 (P)
    { 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00},   // U+0051 (Q)
    { 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00},   // U+0052 (R)
    { 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00},   // U+0053 (S)
    { 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00},   // U+0054 (T)
    { 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00},   // U+0055 (U)
    { 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00},   // U+0056 (V)
    { 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00},   // U+0057 (W)
    { 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00},   // U+0058 (X)
    { 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00},   // U+0059 (Y)
    { 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00},   // U+005A (Z)
    { 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00},   // U+005B ([)
    { 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00},   // U+005C (\)
    { 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00},   // U+005D (])
    { 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00},   // U+005E (^)
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF},   // U+005F (_)
    { 0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0060 (`)
    { 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00},   // U+0061 (a)
    { 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00},   // U+0062 (b)
    { 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00},   // U+0063 (c)
    { 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00},   // U+0064 (d)
    { 0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00},   // U+0065 (e)
    { 0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00},   // U+0066 (f)
    { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F},   // U+0067 (g)
    { 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00},   // U+0068 (h)
    { 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00},   // U+0069 (i)
    { 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E},   // U+006A (j)
    { 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00},   // U+006B (k)
    { 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00},   // U+006C (l)
    { 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00},   // U+006D (m)
    { 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00},   // U+006E (n)
    { 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00},   // U+006F (o)
    { 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F},   // U+0070 (p)
    { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78},   // U+0071 (q)
    { 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00},   // U+0072 (r)
    { 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00},   // U+0073 (s)
    { 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00},   // U+0074 (t)
    { 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00},   // U+0075 (u)
    { 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00},   // U+0076 (v)
    { 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00},   // U+0077 (w)
    { 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00},   // U+0078 (x)
    { 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F},   // U+0079 (y)
    { 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00},   // U+007A (z)
    { 0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00},   // U+007B ({)
    { 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00},   // U+007C (|)
    { 0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00},   // U+007D (})
    { 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+007E (~)
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}    // U+007F
};

// 2-dimensional array of row pin numbers:
const int row[8] = {
  2, 7, 19, 5, 13, 18, 12, 16
};

// 2-dimensional array of column pin numbers:
const int col[8] = {
  6, 11, 10, 3, 17, 4, 8, 9
};

// 2-dimensional array of pixels:
int pixels[8][8];

const int ST_0 = 0;       //waiting Sync word
const int ST_1_CMD = 1;   //Waiting CMD
const int ST_2_DATA= 2;  //Receiving Data for CMD_01_Bits
const int ST_3_CHAR= 3;  //Receiving Single Char for CMD_02_SingleChar
const int DATA_LENGTH = 16;  //length of data = 16
const byte SYNC_WORD = 0xFF;
const byte CMD_01_Bits = 0x01;        //Send as 8x8 bit map
const byte CMD_02_SingleChar = 0x02;  //single char
int cmdState;
int dataIndex;

byte data[DATA_LENGTH];

void setup() {
  
  Serial.begin(9600);

  // initialize the I/O pins as outputs
  // iterate over the pins:
  for (int thisPin = 0; thisPin < 8; thisPin++) {
    // initialize the output pins:
    pinMode(col[thisPin], OUTPUT);
    pinMode(row[thisPin], OUTPUT);
    // take the col pins (i.e. the cathodes) high to ensure that
    // the LEDS are off:
    digitalWrite(col[thisPin], HIGH);
  }
  
  for(int y=0; y<8; y++){
    for(int x=0; x<8; x++){
      pixels[x][y] = HIGH;
    }
  }
  
  cmdState = ST_0;
}

void loop() {
  refreshScreen();
  
  if(Serial.available()){
    cmdHandle(Serial.read());
  }

}

void cmdHandle(int incomingByte){
  
  //prevent from lost-sync
  if(incomingByte == SYNC_WORD){
    cmdState = ST_1_CMD;
    return;
  }
  
  switch(cmdState){
    case ST_0:
        if(incomingByte == SYNC_WORD){
          cmdState = ST_1_CMD;
        }
        break;
    case ST_1_CMD:{
          switch(incomingByte){
            case CMD_01_Bits:
                cmdState = ST_2_DATA;
                dataIndex = 0;
                break;
            case CMD_02_SingleChar:
                cmdState = ST_3_CHAR;
                break;
            default:
                cmdState = ST_0;
          }
        }
        break;
    case ST_2_DATA:{
          data[dataIndex] = incomingByte;
          dataIndex++;
        
          int iData = 0;
          if(dataIndex==DATA_LENGTH){
            cmdState = ST_0;
            for(int i=0; i<8; i++){
              byte mask = 0x01;
            
              for(int j=0; j<4; j++){
                  if (data[iData] & mask){
                    pixels[i][j] = LOW;
                  }else{
                    pixels[i][j] = HIGH;
                  }
                  mask = mask << 1;
              }
            
              iData++;
              for(int j=4; j<8; j++){
                  if (data[iData] & mask){
                    pixels[i][j] = LOW;
                  }else{
                    pixels[i][j] = HIGH;
                  }
                  mask = mask << 1;
              }
            
              iData++;
            }
          }
        }
        break;
      
      case ST_3_CHAR:
        
        
        setupChar((char)incomingByte);
        cmdState = ST_0;
        break;
        
  }
  
}

void setupChar(char c){

  for (int x = 0; x < 8; x++) {
    byte bitMask = 0x01;
    byte f = font[c][x];
    for (int y = 0; y < 8; y++) {
      if (f & bitMask){
        pixels[x][y] = LOW;
      }else{
        pixels[x][y] = HIGH;
      }
      bitMask = bitMask << 1;
    }
  }

}

void refreshScreen() {
  // iterate over the rows (anodes):
  for (int thisRow = 0; thisRow < 8; thisRow++) {
    // take the row pin (anode) high:
    digitalWrite(row[thisRow], HIGH);
    // iterate over the cols (cathodes):
    for (int thisCol = 0; thisCol < 8; thisCol++) {
      // get the state of the current pixel;
      int thisPixel = pixels[thisRow][thisCol];
      // when the row is HIGH and the col is LOW,
      // the LED where they meet turns on:
      digitalWrite(col[thisCol], thisPixel);
      // turn the pixel off:
      if (thisPixel == LOW) {
        digitalWrite(col[thisCol], HIGH);
      }
    }
    // take the row pin low to turn off the whole row:
    digitalWrite(row[thisRow], LOW);
  }
}


download filesDownload UnoUsbMatrix.ino for Arduino Uno.

Next:
Android and Arduino in USB Host Mode, example 3 - work with 8x8 LED Matrix

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.


More of Android + Arduino Uno