Thursday, October 15, 2015

Android control Arduino Due LED, using ADK (Accessory Development Kit)

I have old posts of "Android code sending command to Arduino Due to turn LED On/Off (Android side)" and "Control Arduino Due LED from Android using ADK (Arduino Due side)" to show a simple control from Android to Arduino Due using ADK (Accessory Development Kit), at 2013. At both Android and Arduino development tools updated, I re-visit the example code again.



Arduino Due Side:

In current Arduino IDE 1.6.5, both Arduino Due and ADK library are not include by default, so you have to:
- Install Arduino Due to Arduino IDE
- Install USBHost library to Arduino IDE

DurAdkLed.ino (same as before)
#include "variant.h"
#include <stdio.h>
#include <adk.h>

// Accessory descriptor. It's how Arduino identifies itself to Android.
char applicationName[] = "HelloADKLED"; // the app on your phone
char accessoryName[] = "Arduino Due"; // your Arduino board
char companyName[] = "Arduino-er";
 
// Make up anything you want for these
char versionNumber[] = "0.1";
char serialNumber[] = "1";
char url[] = "https://sites.google.com/site/arduinosite/exercise/androidadkled/AndroidADKLED_0.1.apk";
 
USBHost Usb;
ADK adk(&Usb, companyName, applicationName, accessoryName,versionNumber,url,serialNumber);
 
// Pin 13 has an LED connected on most Arduino boards.
int led = 13;
 
void setup() {
    Serial.begin(9600);
    cpu_irq_enable();
   
    pinMode(led, OUTPUT);
    //Indicate start of program
    digitalWrite(led, LOW);
    delay(2000);
    digitalWrite(led, HIGH);
    for(int i = 0; i <= 2; i++){
        digitalWrite(led, HIGH);
        delay(250);
        digitalWrite(led, LOW);
        delay(250);
    }
}
 
#define RCVSIZE 128
 
void loop() {
   
    char helloworld[] = "Hello World!\r\n";
   
    uint8_t buf[RCVSIZE];
    uint32_t nbread = 0;
   
    Usb.Task();
     
    if (adk.isReady()){
      
        adk.read(&nbread, RCVSIZE, buf);
        if (nbread > 0){
            adk.write(nbread, buf);
            
            //Convert nbread to String
            String s = "";
            for (uint32_t i = 0; i < nbread; ++i) {
              s += (char)buf[i];
            }
            
            if(s == "LEDON"){
              digitalWrite(led, HIGH);
            }else if(s == "LEDOFF"){
              digitalWrite(led, LOW);
            }
        }
         
    }
     
}


Android Side:

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.blogspot.android_er.androidadkled" >

    <uses-feature android:name="android.hardware.usb.accessory"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme" >
        <activity android:name=".MainActivity" >
            <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_ACCESSORY_ATTACHED" />
            </intent-filter>
            <meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
                android:resource="@xml/myfilter"/>
        </activity>
    </application>

</manifest>


xml/myfilter.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <usb-accessory
        manufacturer="Arduino-er"
        model="HelloADKLED"
        version="0.1"/>
</resources>

layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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:padding="16dp"
    android:orientation="vertical"
    tools:context=".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:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://arduino-er.blogspot.com/"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/textin"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <RadioGroup
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
        <RadioButton
            android:id="@+id/LedOn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="LED On" />
        <RadioButton
            android:id="@+id/LedOff"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="LED Off" />
    </RadioGroup>
</LinearLayout>


AbstractAdkActivity.java
package com.blogspot.android_er.androidadkled;

import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.support.v7.app.AppCompatActivity;

import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public abstract  class AbstractAdkActivity extends AppCompatActivity {

    private static int RQS_USB_PERMISSION = 0;
    private static final String ACTION_USB_PERMISSION = "arduino-er.usb_permission";
    private PendingIntent PendingIntent_UsbPermission;

    private UsbManager myUsbManager;
    private UsbAccessory myUsbAccessory;
    private ParcelFileDescriptor myAdkParcelFileDescriptor;
    private FileInputStream myAdkInputStream;
    private FileOutputStream myAdkOutputStream;
    boolean firstRqsPermission;

    //do something in onCreate()
    protected abstract void doOnCreate(Bundle savedInstanceState);
    //do something after adk read
    protected abstract void doAdkRead(String stringIn);

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

        myUsbManager = (UsbManager)getSystemService(Context.USB_SERVICE);
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
        registerReceiver(myUsbReceiver, intentFilter);

        //Ask USB Permission from user
        Intent intent_UsbPermission = new Intent(ACTION_USB_PERMISSION);
        PendingIntent_UsbPermission = PendingIntent.getBroadcast(
                this,      //context
                RQS_USB_PERMISSION,  //request code
                intent_UsbPermission, //intent
                0);      //flags
        IntentFilter intentFilter_UsbPermission = new IntentFilter(ACTION_USB_PERMISSION);
        registerReceiver(myUsbPermissionReceiver, intentFilter_UsbPermission);

        firstRqsPermission = true;
        doOnCreate(savedInstanceState);
    }

    @Override
    protected void onResume() {
        super.onResume();

        if(myAdkInputStream == null || myAdkOutputStream == null){

            UsbAccessory[] usbAccessoryList = myUsbManager.getAccessoryList();
            UsbAccessory usbAccessory = null;
            if(usbAccessoryList != null){
                usbAccessory = usbAccessoryList[0];

                if(usbAccessory != null){
                    if(myUsbManager.hasPermission(usbAccessory)){
                        //already have permission
                        OpenUsbAccessory(usbAccessory);
                    }else{

                        if(firstRqsPermission){

                            firstRqsPermission = false;

                            synchronized(myUsbReceiver){
                                myUsbManager.requestPermission(usbAccessory,
                                        PendingIntent_UsbPermission);
                            }
                        }

                    }
                }
            }
        }
    }

    //Write String to Adk
    void WriteAdk(String text){

        byte[] buffer = text.getBytes();

        if(myAdkOutputStream != null){

            try {
                myAdkOutputStream.write(buffer);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        closeUsbAccessory();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(myUsbReceiver);
        unregisterReceiver(myUsbPermissionReceiver);
    }

    Runnable runnableReadAdk = new Runnable(){

        @Override
        public void run() {
            int numberOfByteRead = 0;
            byte[] buffer = new byte[255];

            while(numberOfByteRead >= 0){

                try {
                    numberOfByteRead = myAdkInputStream.read(buffer, 0, buffer.length);
                    final StringBuilder stringBuilder = new StringBuilder();
                    for(int i=0; i<numberOfByteRead; i++){
                        stringBuilder.append((char)buffer[i]);
                    }

                    runOnUiThread(new Runnable(){

                        @Override
                        public void run() {
                            doAdkRead(stringBuilder.toString());
                        }});


                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                    break;
                }
            }
        }

    };

    private BroadcastReceiver myUsbReceiver = new BroadcastReceiver(){

        @Override
        public void onReceive(Context context, Intent intent) {

            String action = intent.getAction();
            if(action.equals(UsbManager.ACTION_USB_ACCESSORY_DETACHED)){

                UsbAccessory usbAccessory =
                        (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);

                if(usbAccessory!=null && usbAccessory.equals(myUsbAccessory)){
                    closeUsbAccessory();
                }
            }
        }
    };

    private BroadcastReceiver myUsbPermissionReceiver = new BroadcastReceiver(){

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if(action.equals(ACTION_USB_PERMISSION)){

                synchronized(this){

                    UsbAccessory usbAccessory =
                            (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);

                    if(intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)){
                        OpenUsbAccessory(usbAccessory);
                    }else{
                        finish();
                    }
                }
            }
        }

    };

    private void OpenUsbAccessory(UsbAccessory acc){
        myAdkParcelFileDescriptor = myUsbManager.openAccessory(acc);
        if(myAdkParcelFileDescriptor != null){

            myUsbAccessory = acc;
            FileDescriptor fileDescriptor = myAdkParcelFileDescriptor.getFileDescriptor();
            myAdkInputStream = new FileInputStream(fileDescriptor);
            myAdkOutputStream = new FileOutputStream(fileDescriptor);

            Thread thread = new Thread(runnableReadAdk);
            thread.start();
        }
    }

    private void closeUsbAccessory(){

        if(myAdkParcelFileDescriptor != null){
            try {
                myAdkParcelFileDescriptor.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        myAdkParcelFileDescriptor = null;
        myUsbAccessory = null;
    }
}


MainActivity.java
package com.blogspot.android_er.androidadkled;

import android.os.Bundle;
import android.widget.CompoundButton;
import android.widget.RadioButton;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AbstractAdkActivity  {

    TextView textIn;
    RadioButton ledOn, ledOff;

    @Override
    protected void doOnCreate(Bundle savedInstanceState) {
        setContentView(R.layout.activity_main);
        textIn = (TextView)findViewById(R.id.textin);
        ledOn = (RadioButton)findViewById(R.id.LedOn);
        ledOff = (RadioButton)findViewById(R.id.LedOff);

        ledOn.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener(){

            @Override
            public void onCheckedChanged(CompoundButton buttonView,
                                         boolean isChecked) {
                if(isChecked){
                    WriteAdk("LEDON");
                    Toast.makeText(getApplicationContext(),
                            "LEDON", Toast.LENGTH_LONG).show();
                }
            }});

        ledOff.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener(){

            @Override
            public void onCheckedChanged(CompoundButton buttonView,
                                         boolean isChecked) {
                if(isChecked){
                    WriteAdk("LEDOFF");
                    Toast.makeText(getApplicationContext(),
                            "LEDOFF", Toast.LENGTH_SHORT).show();
                }
            }});

    }

    @Override
    protected void doAdkRead(String stringIn) {
        textIn.setText(stringIn);
    }
}



5 comments:

  1. Following exactly your instruction but on Arduino Due compiling get this error:
    arm-none-eabi-gcc: error: C:\Users\WKHO~1\AppData\Local\Temp\build5105731938559175413.tmp/core/syscalls_sam3.c.o: No such file or directory
    Error compiling.

    ReplyDelete
    Replies
    1. Can I know which version of Arduino IDE you use?

      Delete
    2. I had the same problem with the SAM Boards version 1.6.5 and 1.6.6. When I went back to 1.6.4 everything worked fine.

      Delete
  2. This is fantastic! It's so rare to find such a complete example that works perfectly on the first time. I really appreciate this. It surely has saved me a bunch of work. Thanks, dude!

    ReplyDelete
  3. I can get my Nexus 5 to write to the Arduino and toggle the LED, but my read function fails -- java.io.Exception: read failed: EIO (I/O error). It seems strange that one works and the other doesn't. Any ideas? I'm running the same code you've posted.

    ReplyDelete