Friday, March 22, 2013

Android 4.1.2 Jelly Bean and Arduino Mega 2560 with USB Host Shield Communication

Few  weeks ago I wrote my first Android Jelly Bean App named ColorChanger. Since then I wanted to integrate it with Arduino and Control a physical RGB LED Light from the ColorChanger Android App. I finally managed to get it done using the following Items.
  1. Arduino Mega 2560 Rev 3
  2. DFRobot USB Host Shield 
  3. Arduino IDE 0023
  4. ADK Library
  5. 1 RGB LED
  6. 1 1k Resistor
  7. Few Jumper Cables
  8. One USB to Micro USB Cable
Few things changed from the original ColorChanger App starting from the AndroidManifest.xml where I had to allow my MainActivity to listen for USB_ACCESSORY_ATTACHED intent action and when my Accessory connects open the ColorChanger App.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.shazin.colorchanger"
    android:versionCode="1"
    android:versionName="1.0" >
    
    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.shazin.colorchanger.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
                
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            
            <meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
                android:resource="@xml/accessory_filter" /> 
        </activity>
    </application>

</manifest>

The filtering criteria is placed in res/xml/accessory_filter.xml file which is as follows

<?xml version="1.0" encoding="utf-8"?>

<resources>
    <usb-accessory model="ColorChangerApp" manufacturer="Shazin Sadakath" version="1.0"/>
</resources>
Then my MainActivity class changed as follows
package com.shazin.colorchanger;

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

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Color;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.view.Menu;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import android.widget.Toast;


public class MainActivity extends Activity implements OnSeekBarChangeListener, Runnable {

    private TextView textViewColor;
    
    private SeekBar seekBarRed;
    
    private SeekBar seekBarGreen;
    
    private SeekBar seekBarBlue;
    
    private byte red, green, blue;
    
    private LinearLayout linearLayout;
    
    private UsbAccessory mAccessory;
    
    private ParcelFileDescriptor mFileDescriptor;
    
    private FileInputStream mInputStream;
    
    private FileOutputStream mOutputStream;
    
    private UsbManager mUsbManager;
    
    private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
        private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
         
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                if (ACTION_USB_PERMISSION.equals(action)) {
                    synchronized (this) {
                        UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);

                        if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                            if(accessory != null){
                                //call method to set up accessory communication
                                openAccessory();
                            }
                        }
                        else {
                            //Log.d(TAG, "permission denied for accessory " + accessory);
                            Toast.makeText(context, "permission denied for accessory "+accessory, 2).show();
                        }
                    }
                    unregisterReceiver(mUsbReceiver);
                }    
                
            }
        };
    
        public final BroadcastReceiver mUsbDetachReciever = new BroadcastReceiver() {

            @Override
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
                    UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
                    if (accessory != null) {
                        cleanUp();                
                    }
                    unregisterReceiver(mUsbDetachReciever);
                }
                
            }
            
        };
        


        private boolean mPermissionRequestPending;        
        
        private void cleanUp() {
            try {
                if(mFileDescriptor != null)
                    mFileDescriptor.close();
                
                if(mInputStream != null) 
                    mInputStream.close();
                
                if(mOutputStream != null)
                    mOutputStream.close();
                mAccessory = null;
                mUsbManager = null;
                Toast.makeText(this, "Closed all resources", 2).show();
            } catch (IOException e) {
                Toast.makeText(this, "Error occured "+e, 2).show();
            }
        }
        
            
    @SuppressLint("NewApi")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        linearLayout = (LinearLayout) findViewById(R.id.linearLayout);
        
        textViewColor = (TextView) findViewById(R.id.textViewColor);
                
        seekBarRed = (SeekBar) findViewById(R.id.seekBarRed);
        seekBarGreen = (SeekBar) findViewById(R.id.seekBarGreen);
        seekBarBlue = (SeekBar) findViewById(R.id.seekBarBlue);
        
        seekBarBlue.setOnSeekBarChangeListener(this);
        seekBarGreen.setOnSeekBarChangeListener(this);
        seekBarRed.setOnSeekBarChangeListener(this);
        
        textViewColor.setText(String.format("(%d, %d, %d)", red, green, blue));
        linearLayout.setBackgroundColor(Color.rgb(red, green, blue));
        
        
        mAccessory = (UsbAccessory) getIntent().getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
        Toast.makeText(this, "USB Accessory = "+mAccessory, 2).show();
        
        mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
        Toast.makeText(this, "USB Manager = "+mUsbManager, 2).show();
        
        PendingIntent mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
        IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
        registerReceiver(mUsbReceiver, filter);
        Toast.makeText(this, "Intent Registered", 2).show();
        
        if(mUsbManager.hasPermission(mAccessory)) {
            Toast.makeText(this, "Accessory has permission", 2).show();
            unregisterReceiver(mUsbReceiver);
            openAccessory();
        } else {            
            Toast.makeText(this, "Accessory has no permission", 2).show();
            synchronized (mUsbReceiver) {                
                if (!mPermissionRequestPending) {
                    mUsbManager.requestPermission(mAccessory, mPermissionIntent);
                    Toast.makeText(this, "Permission Request Sent", 2).show();
                    mPermissionRequestPending = true;
                }
            }
        }
        
        IntentFilter filter2 = new IntentFilter(ACTION_USB_PERMISSION);
        filter2.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
        registerReceiver(mUsbDetachReciever, filter2);        
        
    }
    
    public void onDestroy() {
        super.onDestroy();
        cleanUp();
    }
    
    @SuppressLint("NewApi")
    private void openAccessory() {
        //Log.d(TAG, "openAccessory: " + accessory);
        mFileDescriptor = mUsbManager.openAccessory(mAccessory);
        Toast.makeText(this, "Accessory Opened", 2).show();
        if (mFileDescriptor != null) {
            FileDescriptor fd = mFileDescriptor.getFileDescriptor();
            Toast.makeText(this, "File Desc "+fd, 2).show();
            mInputStream = new FileInputStream(fd);
            mOutputStream = new FileOutputStream(fd);
            
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int value, boolean flag) {
        if(seekBar == seekBarRed) {
            red = (byte) value;
        } else if(seekBar == seekBarGreen) {
            green = (byte) value;
        } else if(seekBar == seekBarBlue) {
            blue = (byte) value;
        }
        linearLayout.setBackgroundColor(Color.rgb(red & 0xFF, green & 0xFF, blue & 0xFF));
        textViewColor.setText(String.format("(%d, %d, %d)", red & 0xFF, green & 0xFF, blue & 0xFF));
        if(mOutputStream != null) {
            try {
                byte[] data = new byte[4];
                data[0] = 0x2;
                data[1] = red;
                data[2] = green;
                data[3] = blue;
                mOutputStream.write(data);
                mOutputStream.flush();
            } catch (IOException e) {
                Toast.makeText(this, "Error "+e, 2).show();
            }
        }    
    }

    @Override
    public void onStartTrackingTouch(SeekBar arg0) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void onStopTrackingTouch(SeekBar arg0) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void run() {
    
    }

}


And the Arduino Sketch is as follows

#include <avrpins.h>
#include <max3421e.h>
#include <usbhost.h>
#include <usb_ch9.h>
#include <Usb.h>
#include <usbhub.h>
#include <avr/pgmspace.h>
#include <address.h>

#include <adk.h>

#include <printhex.h>
#include <message.h>
#include <hexdump.h>
#include <parsetools.h>
// For Arduino IDE 1.0.x Only following two is needed
#include <adk.h>
#include &ltusbhub.h>

USB Usb;
USBHub hub0(&Usb);
USBHub hub1(&Usb);
ADK adk(&Usb,"Shazin Sadakath",
            "ColorChangerApp", // This name Should match with xml/res/accessory_filter.xml name
            "Color Changer App",
            "1.0",
            "http://shazsterblog.blogspot.com",
            "0000000012345678");

#define  LED1_RED       5
#define  LED1_GREEN       4
#define  LED1_BLUE       3

void setup();
void loop();

void init_leds()
{
    digitalWrite(LED1_RED, 0);
    digitalWrite(LED1_GREEN, 0);
    digitalWrite(LED1_BLUE, 0);

    pinMode(LED1_RED, OUTPUT);
    pinMode(LED1_GREEN, OUTPUT);
    pinMode(LED1_BLUE, OUTPUT);
}

void setup()
{
    Serial.begin(115200);
    Serial.println("\r\nADK demo start");
        
        if (Usb.Init() == -1) {
          Serial.println("OSCOKIRQ failed to assert");
        while(1); //halt
        }//if (Usb.Init() == -1...


    init_leds();
}

void loop()
{
  uint8_t rcode;
  uint8_t msg[4] = { 0x00 };
   Usb.Task();
   
   if( adk.isReady() == false ) {
     analogWrite(LED1_RED, 255);
     return;
   }
   uint16_t len = sizeof(msg);
   
   rcode = adk.RcvData(&len, msg);
   if( rcode ) {
     USBTRACE2("Data rcv. :", rcode );
   } 
   if(len > 0) {
     if(msg[0] == 0x2) {
         USBTRACE("\r\nData Packet.");
         uint8_t r = (msg[1]) & 0xFF;
         uint8_t g = (msg[2]) & 0xFF;
         uint8_t b = (msg[3]) & 0xFF;
         analogWrite(LED1_RED, 255 - r);
         analogWrite(LED1_GREEN, 255 - g);
         analogWrite(LED1_BLUE, 255 - b);
     }
     

   }
   
   msg[0] = 0x1;
   
      delay( 10 );       
}

The video of the whole thing working together is available below.

 

References 

4 comments:

  1. can you post all file needed too run this? like in colorcharger (colorcharger.zip)

    Thank's

    ReplyDelete
  2. Hi Yogi,

    Please check the reference section in this post for the link of the ColorChanger Android Eclipse Project.

    Cheers.

    ReplyDelete
  3. Hello, I am trying communication in freeduino uno board and android phone(jelly bean and ICS). I am compiling your sketch but getting following error.
    In file included from sketch_oct30b.ino:1:
    C:\Users\shree\Documents\Arduino\libraries\USB_Host_Shield_20/avrpins.h:21:2: error: #error "Never include avrpins.h directly; include Usb.h instead"
    In file included from sketch_oct30b.ino:2:
    C:\Users\shree\Documents\Arduino\libraries\USB_Host_Shield_20/max3421e.h:18:2: error: #error "Never include max3421e.h directly; include Usb.h instead"
    In file included from sketch_oct30b.ino:3:
    C:\Users\shree\Documents\Arduino\libraries\USB_Host_Shield_20/usbhost.h:21:2: error: #error "Never include usbhost.h directly; include Usb.h instead"
    In file included from sketch_oct30b.ino:4:
    C:\Users\shree\Documents\Arduino\libraries\USB_Host_Shield_20/usb_ch9.h:19:2: error: #error "Never include usb_ch9.h directly; include Usb.h instead"
    In file included from sketch_oct30b.ino:8:
    C:\Users\shree\Documents\Arduino\libraries\USB_Host_Shield_20/address.h:19:2: error: #error "Never include address.h directly; include Usb.h instead"
    In file included from sketch_oct30b.ino:12:
    C:\Users\shree\Documents\Arduino\libraries\USB_Host_Shield_20/printhex.h:19:2: error: #error "Never include printhex.h directly; include Usb.h instead"
    In file included from sketch_oct30b.ino:13:
    C:\Users\shree\Documents\Arduino\libraries\USB_Host_Shield_20/message.h:18:2: error: #error "Never include message.h directly; include Usb.h instead"
    In file included from sketch_oct30b.ino:14:
    C:\Users\shree\Documents\Arduino\libraries\USB_Host_Shield_20/hexdump.h:18:2: error: #error "Never include hexdump.h directly; include Usb.h instead"
    In file included from sketch_oct30b.ino:15:
    C:\Users\shree\Documents\Arduino\libraries\USB_Host_Shield_20/parsetools.h:19:2: error: #error "Never include parsetools.h directly; include Usb.h instead"

    Thank you in advance.

    ReplyDelete