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 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
Hi,
ReplyDeleteI notice that if the USB is already connected when you lanch the program, it doesn't see it. In other words it detect the USB only when "onResume()" is trigged. How can I modify "onCReate()" to make it finds the USB already attached?
thank you
Hello,
ReplyDeleteNice tutorial Android-er.I want to know how to set baud rate of 115200 in this code can you please tell me how to do this
hello sunny teki,
ReplyDeleteActually I don't know the setting of usbDeviceConnection.controlTransfer()! As i mention in the post Send Hello to Arduino from Android in USB Host Mode. You can reference to the page ANDROID USB HOST + ARDUINO: HOW TO COMMUNICATE WITHOUT ROOTING YOUR ANDROID TABLET OR PHONE.
Hi Android-er
ReplyDeleteI run example. But arduino don't display. I see signal tranfer anderoi to arduino. but led 13 not toggle when i press on/off
Very helpful posts.
ReplyDeleteIs there a way to send command to arduino when the activity starts without using a button?Where must I put the commands?inside onCreate?
thank you
Can someone provide me the source file link, the give one is not working
ReplyDeleteDear Android-er,
ReplyDeleteit was a nice tutorial.i need to send the data or word at a time not letter by letter and also receive the same at a time.can u please suggest the logic.
Thank you.