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