After hacking on the arduino site of the code, now we can go to the android part
For android side, it is a easier in a way. There is nothing to bypass, just make sure to have the right version of android OS. You need 2.3.4 above to be able to use the Accessory API. Though using the API have quite a lot of stuff to setup. And I am a bit confused with the documentation(which is why I share my notes here)

Android Manifest
The manifest is in: https://github.com/sweemeng/ADK-Test-Project/blob/master/AndroidManifest.xml
|
<intent-filter> <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" /> </intent-filter> <meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" android:resource="@xml/accessory_filter"/> </activity> <uses-library android:name="com.android.future.usb.accessory"></uses-library> |
It is pretty standard across all android app, except “com.android.future.usb.accessory”. Because I am still on Android 2.3.7.
Metadata
Then add a accessory.xml entry, under res/xml. in github https://github.com/sweemeng/ADK-Test-Project/blob/master/res/xml/accessory_filter.xml
|
<?xml version="1.0" encoding="UTF-8"?> <resources> <usb-accessory model="ADKTestProject" manufacturer="sweemeng" version="1.0"/> </resources> |
Remember from the last post on arduino, the definition goes here.
Now to the code
The source code can be found at github: https://github.com/sweemeng/ADK-Test-Project/blob/master/src/adkTestProject/main/ADKTestProjectActivity.java
Library Used
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
// usbmanager expose ADK as a File Descriptor import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import android.app.Activity; // hold ADK Intent, because it is not always connected import android.app.PendingIntent; // snipped for brevity // this is precisely what usbmanager expose import android.os.ParcelFileDescriptor; // snipped for brevity // what we needed to read from ADK, // use future because I only have a android 2.3.7 OS with me import com.android.future.usb.UsbManager; import com.android.future.usb.UsbAccessory; |
Essentially openaccessory api open the device as a ParcelFileDescriptor object, which is why you need to import java FileInputStream and FileOutputStream, so you can read and write data to the arduino board
BroadcastReceiver is used because an Android Accessory can broadcast intent to android to launch an android app. IntentFilter and PendingIntent are used to setup the permission necessary for an App is used. More on that later
Variable(For reference)
Now defining variable
|
// UsbManager to check if ADK is connected private UsbManager mUsbManager; // To read permission from ADK private PendingIntent mPermissionIntent; private boolean mPermissionRequestPending; // This is the permission private static final String ACTION_USB_PERMISSION = "com.google.android.ADKTestProject.action.USB_PERMISSION"; // snipped for brevity // This is where we read and write from ADK FileInputStream mFileInputStream; FileOutputStream mFileOutputStream; ParcelFileDescriptor mFileDescriptor; // Accesory!!! UsbAccessory mUsbAccessory; |
Just so you can reference what object is the variable name is.
BroadcastReceiver
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
|
// Receive intent from ADK so that the app will start private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver(){ @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); // permission ok? if(ACTION_USB_PERMISSION.equals(action)){ synchronized (this) { // get accessory definition UsbAccessory accessory = UsbManager.getAccessory(intent); // any extra permission? if (intent.getBooleanExtra( UsbManager.EXTRA_PERMISSION_GRANTED, false)){ // start accessory!!!! openAccessory(accessory); } else { // oops Log.d(TAG,"Permission Denied For Accessory" + accessory); } mPermissionRequestPending = false; } } // it is not connected else if(UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) { // get accessory anyway UsbAccessory accessory = UsbManager.getAccessory(intent); // accessory is still not close cleanly if (accessory != null && accessory.equals(mUsbAccessory)){ closeAccessory(); } } } }; |
Remember an ADK device can send intent to an android app. So that when you plug your device, an app that is use to run with it can be launch. Essentially what this does is, when an intent is receive from the ADK, setup the app to read from ADK, via OpenAccessory method, or if it is detached, clean up all the connection via Close necessary.
line 8: Get the permission and action from the Arduino Board
line 11: Get accessory connection
line 13 – 14 : Get extra permission, from the Arduino Board
line 16: Open Accessory function to setup all the code and interface to the Arduino Board
line 27: from action UsbManager will check if there the arduino board id detached, then do cleanup
line 31-32: If cleanup is still exist, then call cleanup function
Initialize Accessory Interface
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
private void openAccessory(UsbAccessory accessory){ // the interface is a file file descriptor mFileDescriptor = mUsbManager.openAccessory(accessory); if (mFileDescriptor != null) { mUsbAccessory = accessory; // get the file descriptor FileDescriptor fd = mFileDescriptor.getFileDescriptor(); // set one to read mFileInputStream = new FileInputStream(fd); // set one to write mFileOutputStream = new FileOutputStream(fd); Thread thread = new Thread(null,this,"ADKTestProject"); thread.start(); Log.d(TAG,"Accessory Opened"); } else { Log.d(TAG,"Accessory Open Fail"); } } |
This is how you setup to read from ADK. Accessory will be defined from an intent from the ADK, mUsbManager will open the accessory, and return ParcelFileDescriptor, which will be pass to FileInputStream and FileOutputStream.
FileInputStream is use to read from the ADK, for this project I use FileOutputStream to write to the ADK Board, which start the thread to have them run in background.
Close Connection to Device
|
private void closeAccessory(){ try{ if (mFileDescriptor != null){ mFileDescriptor.close(); } } catch (IOException e) { } finally { mFileDescriptor = null; mUsbAccessory = null; } } |
Close is easy just close the file descriptor, and set variable to null
Send Commend to Device
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
// send command!!!! public void sendCommand(int red, int green, int blue){ // we will only send 3 byte byte[] buffer = new byte[3]; // make sure the value cannot be more than 255, // the LED can only receive that if(red > 255){ red = 255; } if(green > 255){ green = 255; } if(blue > 255){ blue = 255; } // assign buffer[0] = (byte)red; buffer[1] = (byte)green; buffer[2] = (byte)blue; // make sure interface is there if (mFileOutputStream != null && buffer[1] != -1){ try{ // write it mFileOutputStream.write(buffer); } catch (IOException e){ Log.e(TAG,"Write Faile",e); } } } |
Remember I say on the last post that, you just send an array of byte or read an array of bytes , it is the same here.
line 4: define the holder for data to be send to the Arduino
line 7-15: just to setup the value send to be at most 255, the LED only can access such value. It is also nice to make sure value send from app to arduino, because once go to hardware, thing will be fried(shit just turn real!!!)
line 17-19: just to assign value, remember we send bytes to Arduino, so remember to convert accordingly
line 24: send the byte array to arduino
Retain Devices
|
@Override public Object onRetainNonConfigurationInstance(){ if (mUsbAccessory != null){ return mUsbAccessory; } else{ return super.onRetainNonConfigurationInstance(); } } |
Retain ADK accessory to be reuse by new activity
Initialize the app to use Accessory
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // get the USB Manager involved mUsbManager = UsbManager.getInstance(this); // setup permission mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION),0); // get the permission IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION); // register receiver registerReceiver(mUsbReceiver,filter); // device exist? Open and send value if(getLastNonConfigurationInstance() != null){ mUsbAccessory = (UsbAccessory) getLastNonConfigurationInstance(); openAccessory(mUsbAccessory); sendCommand(redValue,blueValue,greenValue); } // snipped for brevity, ui definition down there |
This register the Permission needed and the BroadcastReceiver, also setup the retained Accessory object
To resume an activity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
@Override public void onResume(){ super.onResume(); Intent intent = getIntent(); // opps interface close, it is cool, do nothing if (mFileInputStream != null && mFileOutputStream != null){ return ; } // get a list of accessory UsbAccessory[] accessories = mUsbManager.getAccessoryList(); // if exist get the first one or give null UsbAccessory accessory = (accessories == null ? null : accessories[0]); // accessory exist if (accessory != null){ // check permission if (mUsbManager.hasPermission(accessory)){ // open and send value openAccessory(accessory); sendCommand(redValue,blueValue,greenValue); } else{ synchronized(this){ // request permission if (!mPermissionRequestPending){ mUsbManager.requestPermission(accessory, mPermissionIntent); mPermissionRequestPending = true; } } } } else{ Log.d(TAG,"mAccessory is Null"); } } |
line 7-8: so the connection already closed, it is cool, don’t do anything
line 11-13: get all connected device, if there is nothing return null, else, return the first one(there is only one anyway)
line 17-21: permission checked out, you can open(OpenAccessory) and send value to the arduino(sendCommand)
line 25-29: request permission, again
Using the Using sendCommand
|
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { // snipped out for brevity, set value to be send above // get value, and send it sendCommand(redValue,blueValue,greenValue); } |
So just get value from UI change, then send the value to send command. It is a seekbar, that value is progress, and we already save color value anyway.
We’re done
I hope this is useful, though I think I might confuse people more, so just leave a comment if there is any question