Arduino UNO and the Google ADK – part III

Preparing the Arduino

The Arduino firmware is a simple program receiving a toggle LED command from the connected Android device. Some parts are taken from the USB Host Shield 2.0 library.

Arduino UNO connected to LED

Connect the USB Host Shield 2.0 to the Arduino UNO (note that the picture above does not show the connected host shield, but it will affix atop the UNO). Also connect a LED between digital pin 3 and ground (GND) on the host shield.

The Android Accessory Protocol

The accessory must implement the Android Accessory Protocol:

  • Wait for and detect connected devices
  • Determine the device’s accessory mode support
  • Attempt to start the device in accessory mode if needed
  • Establish communication with the device if it supports the Android accessory protocol
Lucky for us, the Usb Host Shield library already implements the Android Accessory Protocol, and can be used to start a connected device in accessory mode. Note that the USB Host Shield library implementation differs slightly (most notably in how methods and classes are named) from the Google ADK library implementation described on the Android Accessory Protocol section.

Building the firmware

Copy the downloaded USB Host Shield library to the arduino core directory:

sudo cp <path to downloaded USB_Host_Shield_2.0 library>/*.* /usr/share/arduino/hardware/arduino/cores/arduino

Now, create a project directory with the following content:

A make file Makefile

ARDUINO_DIR            = /usr/share/arduino
TARGET                 = <the name of your project>
ARDUINO_LIBS           =
MCU                    = atmega328p
F_CPU                  = 16000000
ARDUINO_PORT           = <the port connected to the arduino>
AVRDUDE_ARD_BAUDRATE   = 115200
AVRDUDE_ARD_PROGRAMMER = arduino

include /usr/share/arduino/Arduino.mk

A main file main.cpp

#include <Usb.h>
#include <adk.h>

#define LED_PIN 3
#define LONG_BLINK 2000
#define SHORT_BLINK 500

int ledStatus = HIGH;

USB Usb;
ADK adk(&Usb,"JoeKickass",
            "AndroidApkTakeTwo",
            "ADK example application",
            "1.0",
            "http://joekickass.se",
            "0000000012345678");

void toggleLed() {
    digitalWrite(LED_PIN, ledStatus);
    ledStatus = (ledStatus == LOW) ? HIGH : LOW;
}

void blinkLed(int delayFactor) {
    toggleLed();
    delay(delayFactor);
    toggleLed();
    delay(delayFactor);
}

void errorBlink() {
    blinkLed(SHORT_BLINK);
}

void readMessage() {
    uint8_t msg[2] = { 0x00 };
    uint16_t len = sizeof(msg);
    adk.RcvData(&len, msg);
    if(len > 0) {
        if (msg[0] == 0xf) {
            toggleLed();
        }
    }
}

void setup() {
    pinMode(LED_PIN, OUTPUT);
    if (Usb.Init() == -1) {
        while(1) {
            errorBlink();
        }
    }
}

void loop() {
    Usb.Task();

    if (!adk.isReady()) {
        return;
    }

    readMessage();
}

int main(void) {
    init();
    setup();
    for (;;) {
        loop();
    }
}

This application loops forever, waiting for the device to send a command (0xf) to it. When the command is received, the accessory toggles the LED. Below are some highlighted sections:

USB Usb;
ADK adk(&Usb,"JoeKickass",
            "AndroidApkTakeTwo",
            "ADK example application",
            "1.0",
            "http://joekickass.se",
            "0000000012345678");

The ADK class contains all the functionality needed to communicate with an Android device. The initialization of Android device as well as switching it to accessory mode is performed automatically by USB subsystem. The ADK::Init() member function is called each time a new device is detected on a bus. It first tries to determine if a device is in accessory mode already by reading its VID, PID, and if yes, configures it and reports success to the system. If device fails accessory check, the standard probing and accessory switching method is performed. If successful, device resets and appears on USB bus as an “accessory mode-capable” unit.

if (!adk.isReady()) {
    return;
}

adk.isReady() returns True when the device is connected and ready to communicate with the accessory. If not, the program will loop until a device is connected.

void readMessage() {
    uint8_t msg[2] = { 0x00 };
    uint16_t len = sizeof(msg);
    adk.RcvData(&len, msg);
    if(len > 0) {
        if (msg[0] == 0xf) {
            toggleLed();
        }
    }
}

readMessage() reads data from the connected device. It only accepts one command (0xf) located in the first byte of the received data. When the correct command is received, the led is toggled.

An Android device draws a lot of power when it is not fully charged. This can interfere with the communication between the device and the accessory. One way to deal with this, at least when developing, is to connect a separately powered USB hub between the accessory and the device.  Personally, I’ve had some difficulties initiating hub instances, and thus to eliminate sources of error I have not included that in this example.

Now, build and upload the firmware:

make upload

That’s it. Enjoy your blinking!

Here are all posts in the series:

This Post Has 3 Comments

  1. florian

    cool project. Think it was a plenty of work to get this done !

  2. Sven Schippers

    Just a tip: if you are using a Sparkfun USB Host shield, you need to connect RST to D7 to make it work. It took me quite a while to figure this out :-)

    Setup used: Arduino UNO + Sparkfun USB Host shield + UST Host shield 2.0 + Android Nexus S (2.3.6)

  3. Marco

    hello, i know this thread is a couple of month old.
    Maybe somebody could give me some hints to solve my problem.
    I am not able to compile this example application with the current host shield 2.0 and arduino version (installed by apt-get). It seems that the problem is related with the compiler/linker. (installed version 4.7)
    I use the currrent version which is installed over apt-get, too. Here is a line from the compile output.
    ../hardware/arduino/cores/arduino/hidescriptorparser.cpp:3:54: error: variable ‘ReportDescParserBase::usagePageTitles0’ must be const in order to be put into read-only section by means of ‘__attribute__((progmem))’
    Any ideas?
    Thanks for this great step by step tutorial.

    Best regards from colonifornien

Leave a Reply