From 008df0430c43402c8aaab272275dd5184f1e3d28 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 18 Apr 2012 14:34:51 +0200 Subject: [PATCH] [sam] added AndroidAccesory class from ADK --- .../AndroidAccessory/AndroidAccessory.cpp | 336 ++++++++++++++++++ .../AndroidAccessory/AndroidAccessory.h | 90 +++++ .../sam/system/AndroidAccessory/COPYING | 17 + .../sam/system/AndroidAccessory/README | 11 + .../_01_digitalRead_adk.ino | 66 ++++ .../_02_digitalWrite_adk.ino | 65 ++++ .../_03_analogRead_adk/_03_analogRead_adk.ino | 63 ++++ .../_04_analogWrite_adk.ino | 61 ++++ .../examples/_05_test_adk/_05_test_adk.ino | 34 ++ 9 files changed, 743 insertions(+) create mode 100644 hardware/arduino/sam/system/AndroidAccessory/AndroidAccessory.cpp create mode 100644 hardware/arduino/sam/system/AndroidAccessory/AndroidAccessory.h create mode 100644 hardware/arduino/sam/system/AndroidAccessory/COPYING create mode 100755 hardware/arduino/sam/system/AndroidAccessory/README create mode 100644 hardware/arduino/sam/system/AndroidAccessory/examples/_01_digitalRead_adk/_01_digitalRead_adk.ino create mode 100644 hardware/arduino/sam/system/AndroidAccessory/examples/_02_digitalWrite_adk/_02_digitalWrite_adk.ino create mode 100644 hardware/arduino/sam/system/AndroidAccessory/examples/_03_analogRead_adk/_03_analogRead_adk.ino create mode 100644 hardware/arduino/sam/system/AndroidAccessory/examples/_04_analogWrite_adk/_04_analogWrite_adk.ino create mode 100644 hardware/arduino/sam/system/AndroidAccessory/examples/_05_test_adk/_05_test_adk.ino diff --git a/hardware/arduino/sam/system/AndroidAccessory/AndroidAccessory.cpp b/hardware/arduino/sam/system/AndroidAccessory/AndroidAccessory.cpp new file mode 100644 index 000000000..799e149b2 --- /dev/null +++ b/hardware/arduino/sam/system/AndroidAccessory/AndroidAccessory.cpp @@ -0,0 +1,336 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include + +#define USB_ACCESSORY_VENDOR_ID 0x18D1 +#define USB_ACCESSORY_PRODUCT_ID 0x2D00 + +#define USB_ACCESSORY_ADB_PRODUCT_ID 0x2D01 +#define ACCESSORY_STRING_MANUFACTURER 0 +#define ACCESSORY_STRING_MODEL 1 +#define ACCESSORY_STRING_DESCRIPTION 2 +#define ACCESSORY_STRING_VERSION 3 +#define ACCESSORY_STRING_URI 4 +#define ACCESSORY_STRING_SERIAL 5 + +#define ACCESSORY_GET_PROTOCOL 51 +#define ACCESSORY_SEND_STRING 52 +#define ACCESSORY_START 53 + + +AndroidAccessory::AndroidAccessory(const char *manufacturer, + const char *model, + const char *description, + const char *version, + const char *uri, + const char *serial) : manufacturer(manufacturer), + model(model), + description(description), + version(version), + uri(uri), + serial(serial), + connected(false) +{ + +} + +void AndroidAccessory::powerOn(void) +{ + max.powerOn(); + delay(200); +} + +int AndroidAccessory::getProtocol(byte addr) +{ + uint16_t protocol = -1; + usb.ctrlReq(addr, 0, + USB_SETUP_DEVICE_TO_HOST | + USB_SETUP_TYPE_VENDOR | + USB_SETUP_RECIPIENT_DEVICE, + ACCESSORY_GET_PROTOCOL, 0, 0, 0, 2, (char *)&protocol); + return protocol; +} + +void AndroidAccessory::sendString(byte addr, int index, const char *str) +{ + usb.ctrlReq(addr, 0, + USB_SETUP_HOST_TO_DEVICE | + USB_SETUP_TYPE_VENDOR | + USB_SETUP_RECIPIENT_DEVICE, + ACCESSORY_SEND_STRING, 0, 0, index, + strlen(str) + 1, (char *)str); +} + + +bool AndroidAccessory::switchDevice(byte addr) +{ + int protocol = getProtocol(addr); + + if (protocol == 1) { + Serial.print(F("device supports protcol 1\n")); + } else { + Serial.print(F("could not read device protocol version\n")); + return false; + } + + sendString(addr, ACCESSORY_STRING_MANUFACTURER, manufacturer); + sendString(addr, ACCESSORY_STRING_MODEL, model); + sendString(addr, ACCESSORY_STRING_DESCRIPTION, description); + sendString(addr, ACCESSORY_STRING_VERSION, version); + sendString(addr, ACCESSORY_STRING_URI, uri); + sendString(addr, ACCESSORY_STRING_SERIAL, serial); + + usb.ctrlReq(addr, 0, + USB_SETUP_HOST_TO_DEVICE | + USB_SETUP_TYPE_VENDOR | + USB_SETUP_RECIPIENT_DEVICE, + ACCESSORY_START, 0, 0, 0, 0, NULL); + + while (usb.getUsbTaskState() != USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE) { + max.Task(); + usb.Task(); + } + + return true; +} + +// Finds the first bulk IN and bulk OUT endpoints +bool AndroidAccessory::findEndpoints(byte addr, EP_RECORD *inEp, EP_RECORD *outEp) +{ + int len; + byte err; + uint8_t *p; + + err = usb.getConfDescr(addr, 0, 4, 0, (char *)descBuff); + if (err) { + Serial.print(F("Can't get config descriptor length\n")); + return false; + } + + + len = descBuff[2] | ((int)descBuff[3] << 8); + if (len > sizeof(descBuff)) { + Serial.print(F("config descriptor too large\n")); + /* might want to truncate here */ + return false; + } + + err = usb.getConfDescr(addr, 0, len, 0, (char *)descBuff); + if (err) { + Serial.print(F("Can't get config descriptor\n")); + return false; + } + + p = descBuff; + inEp->epAddr = 0; + outEp->epAddr = 0; + while (p < (descBuff + len)){ + uint8_t descLen = p[0]; + uint8_t descType = p[1]; + USB_ENDPOINT_DESCRIPTOR *epDesc; + EP_RECORD *ep; + + switch (descType) { + case USB_DESCRIPTOR_CONFIGURATION: + Serial.print(F("config desc\n")); + break; + + case USB_DESCRIPTOR_INTERFACE: + Serial.print(F("interface desc\n")); + break; + + case USB_DESCRIPTOR_ENDPOINT: + epDesc = (USB_ENDPOINT_DESCRIPTOR *)p; + if (!inEp->epAddr && (epDesc->bEndpointAddress & 0x80)) + ep = inEp; + else if (!outEp->epAddr) + ep = outEp; + else + ep = NULL; + + if (ep) { + ep->epAddr = epDesc->bEndpointAddress & 0x7f; + ep->Attr = epDesc->bmAttributes; + ep->MaxPktSize = epDesc->wMaxPacketSize; + ep->sndToggle = bmSNDTOG0; + ep->rcvToggle = bmRCVTOG0; + } + break; + + default: + Serial.print(F("unkown desc type ")); + Serial.println( descType, HEX); + break; + } + + p += descLen; + } + + if (!(inEp->epAddr && outEp->epAddr)) + Serial.println(F("can't find accessory endpoints")); + + return inEp->epAddr && outEp->epAddr; +} + +bool AndroidAccessory::configureAndroid(void) +{ + byte err; + EP_RECORD inEp, outEp; + + if (!findEndpoints(1, &inEp, &outEp)) + return false; + + memset(&epRecord, 0x0, sizeof(epRecord)); + + epRecord[inEp.epAddr] = inEp; + if (outEp.epAddr != inEp.epAddr) + epRecord[outEp.epAddr] = outEp; + + in = inEp.epAddr; + out = outEp.epAddr; + + Serial.println(inEp.epAddr, HEX); + Serial.println(outEp.epAddr, HEX); + + epRecord[0] = *(usb.getDevTableEntry(0,0)); + usb.setDevTableEntry(1, epRecord); + + err = usb.setConf( 1, 0, 1 ); + if (err) { + Serial.print(F("Can't set config to 1\n")); + return false; + } + + usb.setUsbTaskState( USB_STATE_RUNNING ); + + return true; +} + +bool AndroidAccessory::isConnected(void) +{ + USB_DEVICE_DESCRIPTOR *devDesc = (USB_DEVICE_DESCRIPTOR *) descBuff; + byte err; + + max.Task(); + usb.Task(); + + if (!connected && + usb.getUsbTaskState() >= USB_STATE_CONFIGURING && + usb.getUsbTaskState() != USB_STATE_RUNNING) { + Serial.print(F("\nDevice addressed... ")); + Serial.print(F("Requesting device descriptor.\n")); + + err = usb.getDevDescr(1, 0, 0x12, (char *) devDesc); + if (err) { + Serial.print(F("\nDevice descriptor cannot be retrieved. Trying again\n")); + return false; + } + + if (isAccessoryDevice(devDesc)) { + Serial.print(F("found android acessory device\n")); + + connected = configureAndroid(); + } else { + Serial.print(F("found possible device. swithcing to serial mode\n")); + switchDevice(1); + } + } else if (usb.getUsbTaskState() == USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE) { + if (connected) + Serial.println(F("disconnect\n")); + connected = false; + } + + return connected; +} + +bool AndroidAccessory::dataBufferIsEmpty() { + return (numBytesInDataBuff == nextByteInDataBuffOffset); +} + +void AndroidAccessory::refillDataBuffer() { + int bytesRead = 0; + + numBytesInDataBuff = nextByteInDataBuffOffset = 0; + + // TODO: Add is connected check? + + bytesRead = read(dataBuff, sizeof(dataBuff)); + + if (bytesRead >= 1) { + numBytesInDataBuff = bytesRead; + } +} + +int AndroidAccessory::read() { + + if (dataBufferIsEmpty()) { + refillDataBuffer(); + } + + return dataBufferIsEmpty() ? -1 : dataBuff[nextByteInDataBuffOffset++]; +} + +int AndroidAccessory::peek() { + + if (dataBufferIsEmpty()) { + refillDataBuffer(); + } + + return dataBufferIsEmpty() ? -1 : dataBuff[nextByteInDataBuffOffset]; +} + +int AndroidAccessory::available() { + + // Strictly speaking this doesn't meet the "This is only for bytes + // that have already arrived" definition from + // but since the + // data isn't handled by an ISR it's the only way to avoid hanging + // waiting for `available()` to return true. + if (dataBufferIsEmpty()) { + refillDataBuffer(); + } + + return numBytesInDataBuff - nextByteInDataBuffOffset; +} + +int AndroidAccessory::read(void *buff, int len, unsigned int nakLimit) +{ + return usb.newInTransfer(1, in, len, (char *)buff, nakLimit); +} + +size_t AndroidAccessory::write(uint8_t *buff, size_t len) +{ + usb.outTransfer(1, out, len, (char *)buff); + return len; +} + +size_t AndroidAccessory::write(uint8_t c) { + return write(&c, 1); +} + +void AndroidAccessory::flush() { + /* + "Waits for the transmission of outgoing [...] data to complete." + + from + + We're treating this as a no-op at the moment. + */ +} diff --git a/hardware/arduino/sam/system/AndroidAccessory/AndroidAccessory.h b/hardware/arduino/sam/system/AndroidAccessory/AndroidAccessory.h new file mode 100644 index 000000000..c635a614f --- /dev/null +++ b/hardware/arduino/sam/system/AndroidAccessory/AndroidAccessory.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __AndroidAccessory_h__ +#define __AndroidAccessory_h__ + +#include "Arduino.h" +#include "Stream.h" + +#define DATA_BUFFER_SIZE 64 + +class AndroidAccessory : public Stream { +private: + const char *manufacturer; + const char *model; + const char *description; + const char *version; + const char *uri; + const char *serial; + + MAX3421E max; + USB usb; + bool connected; + uint8_t in; + uint8_t out; + + EP_RECORD epRecord[8]; + + // TODO: Reuse `descBuff` after connection and/or stream descriptor? + uint8_t descBuff[256]; + + byte dataBuff[DATA_BUFFER_SIZE]; + unsigned int numBytesInDataBuff; + unsigned int nextByteInDataBuffOffset; + + bool isAccessoryDevice(USB_DEVICE_DESCRIPTOR *desc) + { + return desc->idVendor == 0x18d1 && + (desc->idProduct == 0x2D00 || desc->idProduct == 0x2D01); + } + + int getProtocol(byte addr); + void sendString(byte addr, int index, const char *str); + bool switchDevice(byte addr); + bool findEndpoints(byte addr, EP_RECORD *inEp, EP_RECORD *outEp); + bool configureAndroid(void); + + bool dataBufferIsEmpty(); + void refillDataBuffer(); + + // Private because it bypasses the data buffer. + int read(void *buff, int len, unsigned int nakLimit = USB_NAK_LIMIT); + +public: + AndroidAccessory(const char *manufacturer, + const char *model, + const char *description, + const char *version, + const char *uri, + const char *serial); + + void powerOn(void); + + bool isConnected(void); + virtual size_t write(uint8_t *buff, size_t len); + + virtual int available(void); + virtual int peek(void); + virtual int read(void); + + virtual void flush(); + virtual size_t write(uint8_t); + + using Print::write; // pull in write(str) and write(buf, size) from Print +}; + +#endif /* __AndroidAccessory_h__ */ diff --git a/hardware/arduino/sam/system/AndroidAccessory/COPYING b/hardware/arduino/sam/system/AndroidAccessory/COPYING new file mode 100644 index 000000000..0f33bc971 --- /dev/null +++ b/hardware/arduino/sam/system/AndroidAccessory/COPYING @@ -0,0 +1,17 @@ +--------------------------------------------------------------------------------------------------- + +Copyright (C) 2011 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +--------------------------------------------------------------------------------------------------- diff --git a/hardware/arduino/sam/system/AndroidAccessory/README b/hardware/arduino/sam/system/AndroidAccessory/README new file mode 100755 index 000000000..e171fc07d --- /dev/null +++ b/hardware/arduino/sam/system/AndroidAccessory/README @@ -0,0 +1,11 @@ +Copyright Google, Inc. 2011 + +This directory contains files & directories originally found in the +ADK_release_0512.zip archive: + +README.txt This file +AndroidAccessory.* Open Accessory Protocol and USB Host Libraries + +The original README.txt suggested you go to +http://a.android.com/demokit for more information about how to use the +ADK. diff --git a/hardware/arduino/sam/system/AndroidAccessory/examples/_01_digitalRead_adk/_01_digitalRead_adk.ino b/hardware/arduino/sam/system/AndroidAccessory/examples/_01_digitalRead_adk/_01_digitalRead_adk.ino new file mode 100644 index 000000000..ed8468ff5 --- /dev/null +++ b/hardware/arduino/sam/system/AndroidAccessory/examples/_01_digitalRead_adk/_01_digitalRead_adk.ino @@ -0,0 +1,66 @@ +/* +* ADK usb digitalRead + * + * TADA! + * + * (c) 2012 D. Cuartielles & A. Goransson + * http://arduino.cc, http://1scale1.com + * + */ + +#include +#include +#include + +// accessory descriptor. It's how Arduino identifies itself to Android +char applicationName[] = "Mega_ADK"; // the app on your phone +char accessoryName[] = "Mega_ADK"; // your Arduino board +char companyName[] = "Arduino SA"; + +// make up anything you want for these +char versionNumber[] = "1.0"; +char serialNumber[] = "1"; +char url[] = "http://labs.arduino.cc/adk/ADK_count"; // the URL of your app online + +// button variables +int buttonPin = A1; +int buttonState = 0; +char letter = 'a'; + +// counters +long timer = millis(); + + +// initialize the accessory: +AndroidAccessory usb(companyName, applicationName, +accessoryName,versionNumber,url,serialNumber); + +void setup() { + // start the connection to the device over the USB host: + usb.powerOn(); + + pinMode(buttonPin, INPUT); +} + +void loop() { + /* Read button state */ + buttonState = digitalRead(buttonPin); + + /* Print to usb */ + if(millis()-timer>100) { // sending 10 times per second + if (usb.isConnected()) { // isConnected makes sure the USB connection is ope + if (buttonState == HIGH) { + usb.write( 'a' ); + } + else{ + usb.write( ' ' ); + } + } + timer = millis(); + } +} + + + + + diff --git a/hardware/arduino/sam/system/AndroidAccessory/examples/_02_digitalWrite_adk/_02_digitalWrite_adk.ino b/hardware/arduino/sam/system/AndroidAccessory/examples/_02_digitalWrite_adk/_02_digitalWrite_adk.ino new file mode 100644 index 000000000..add3bcefb --- /dev/null +++ b/hardware/arduino/sam/system/AndroidAccessory/examples/_02_digitalWrite_adk/_02_digitalWrite_adk.ino @@ -0,0 +1,65 @@ +/* +* ADK usb digitalRead + * + * TADA! + * + * (c) 2012 D. Cuartielles & A. Goransson + * http://arduino.cc, http://1scale1.com + * + */ + +#include +#include +#include + +// accessory descriptor. It's how Arduino identifies itself to Android +char applicationName[] = "Mega_ADK"; // the app on your phone +char accessoryName[] = "Mega_ADK"; // your Arduino board +char companyName[] = "Arduino SA"; + +// make up anything you want for these +char versionNumber[] = "1.0"; +char serialNumber[] = "1"; +char url[] = "http://labs.arduino.cc/adk/ADK_count"; // the URL of your app online + +// led variables +int ledPin = 10; + +// counters +long timer = millis(); + +// initialize the accessory: +AndroidAccessory usb(companyName, applicationName, +accessoryName,versionNumber,url,serialNumber); + +void setup() { + Serial.begin( 9600 ); + // start the connection to the device over the USB host: + usb.powerOn(); + + pinMode(ledPin, OUTPUT); +} + +void loop() { + /* Print to usb */ + if(millis()-timer>100) { // sending 10 times per second + if (usb.isConnected()) { // isConnected makes sure the USB connection is ope + char val = usb.read(); + Serial.print( val ); + if( val == 'a' ) + digitalWrite( ledPin, HIGH ); + else if( val == 'b' ) + digitalWrite( ledPin, LOW ); + } + timer = millis(); + } +} + + + + + + + + + diff --git a/hardware/arduino/sam/system/AndroidAccessory/examples/_03_analogRead_adk/_03_analogRead_adk.ino b/hardware/arduino/sam/system/AndroidAccessory/examples/_03_analogRead_adk/_03_analogRead_adk.ino new file mode 100644 index 000000000..05927e8c8 --- /dev/null +++ b/hardware/arduino/sam/system/AndroidAccessory/examples/_03_analogRead_adk/_03_analogRead_adk.ino @@ -0,0 +1,63 @@ +/* +* ADK usb digitalRead + * + * TADA! + * + * (c) 2012 D. Cuartielles & A. Goransson + * http://arduino.cc, http://1scale1.com + * + */ + +#include +#include +#include + +// accessory descriptor. It's how Arduino identifies itself to Android +char applicationName[] = "Mega_ADK"; // the app on your phone +char accessoryName[] = "Mega_ADK"; // your Arduino board +char companyName[] = "Arduino SA"; + +// make up anything you want for these +char versionNumber[] = "1.0"; +char serialNumber[] = "1"; +char url[] = "http://labs.arduino.cc/adk/ADK_count"; // the URL of your app online + +// button variables +int sliderPin = A1; +int val; + +// counters +long timer = millis(); + + +// initialize the accessory: +AndroidAccessory usb(companyName, applicationName, +accessoryName,versionNumber,url,serialNumber); + +void setup() { + // start the connection to the device over the USB host: + usb.powerOn(); + + pinMode(sliderPin, INPUT); +} + +void loop() { + /* Read button state */ + val = analogRead(sliderPin); + val /= 4; + + /* Print to usb */ + if(millis()-timer>100) { // sending 10 times per second + if (usb.isConnected()) { // isConnected makes sure the USB connection is ope + usb.write(val); + } + timer = millis(); + } +} + + + + + + + diff --git a/hardware/arduino/sam/system/AndroidAccessory/examples/_04_analogWrite_adk/_04_analogWrite_adk.ino b/hardware/arduino/sam/system/AndroidAccessory/examples/_04_analogWrite_adk/_04_analogWrite_adk.ino new file mode 100644 index 000000000..268675158 --- /dev/null +++ b/hardware/arduino/sam/system/AndroidAccessory/examples/_04_analogWrite_adk/_04_analogWrite_adk.ino @@ -0,0 +1,61 @@ +/* + * ADK usb analogWrite + * + * TADA! + * + * (c) 2012 D. Cuartielles & A. Goransson + * http://arduino.cc, http://1scale1.com + * + */ + +#include +#include +#include + +// accessory descriptor. It's how Arduino identifies itself to Android +char applicationName[] = "Mega_ADK"; // the app on your phone +char accessoryName[] = "Mega_ADK"; // your Arduino board +char companyName[] = "Arduino SA"; + +// make up anything you want for these +char versionNumber[] = "1.0"; +char serialNumber[] = "1"; +char url[] = "http://labs.arduino.cc/adk/ADK_count"; // the URL of your app online + +// led variables +int ledPin = 10; + +// counters +long timer = millis(); + +// initialize the accessory: +AndroidAccessory usb(companyName, applicationName, +accessoryName,versionNumber,url,serialNumber); + +void setup() { + Serial.begin( 9600 ); + // start the connection to the device over the USB host: + usb.powerOn(); + + pinMode(ledPin, OUTPUT); +} + +void loop() { + /* Print to usb */ + if(millis()-timer>100) { // sending 10 times per second + if (usb.isConnected()) { // isConnected makes sure the USB connection is ope + int val = usb.read(); + Serial.println( val ); + analogWrite( ledPin, val ); + } + timer = millis(); + } +} + + + + + + + + diff --git a/hardware/arduino/sam/system/AndroidAccessory/examples/_05_test_adk/_05_test_adk.ino b/hardware/arduino/sam/system/AndroidAccessory/examples/_05_test_adk/_05_test_adk.ino new file mode 100644 index 000000000..7be3eb6ba --- /dev/null +++ b/hardware/arduino/sam/system/AndroidAccessory/examples/_05_test_adk/_05_test_adk.ino @@ -0,0 +1,34 @@ +#include +#include +#include + +AndroidAccessory acc("Google, Inc.", + "DemoKit", + "DemoKit Arduino Board", + "1.0", + "http://www.android.com", + "0000000012345678"); +void setup(); +void loop(); + +void setup() +{ + Serial.begin(115200); + Serial.print("\r\nStart"); + acc.powerOn(); +} + +void loop() +{ + byte msg[3]; + + if (acc.isConnected()) { + Serial.print("Accessory connected. "); + int len = acc.read(msg, sizeof(msg), 1); + Serial.print("Message length: "); + Serial.println(len, DEC); + } + + delay(100); +} +