blob: a5e759a1e216458860d52ddd307074ef03b9dda7 [file] [log] [blame]
#include <Max3421e.h>
#include <Usb.h>
#include <Wire.h>
#include <Servo.h>
#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_TYPE 2
#define ACCESSORY_STRING_VERSION 3
#define ACCESSORY_SEND_STRING 52
#define ACCESSORY_START 53
#define LED3_RED 2
#define LED3_GREEN 3
#define LED3_BLUE 4
#define LED2_RED 5
#define LED2_GREEN 6
#define LED2_BLUE 7
#define LED1_RED 8
#define LED1_GREEN 9
#define LED1_BLUE 10
#define SERVO1 11
#define SERVO2 12
#define SERVO3 13
#define TOUCH 14
#define RELAY1 A0
#define RELAY2 A1
#define LIGHT_SENSOR A2
#define TEMP_SENSOR A3
#define BUTTON1 A6
#define BUTTON2 A7
#define BUTTON3 A8
#define JOY_SWITCH A9 // pulls line down when pressed
#define JOY_nINT A10 // active low interrupt input
#define JOY_nRESET A11 // active low reset output
MAX3421E Max;
USB Usb;
Servo servos[3];
void setup();
void loop();
uint8_t usbBuff[256];
void init_buttons()
{
pinMode( BUTTON1, INPUT );
pinMode( BUTTON2, INPUT );
pinMode( BUTTON3, INPUT );
digitalWrite( BUTTON1, HIGH ); // enable the internal pullups
digitalWrite( BUTTON2, HIGH );
digitalWrite( BUTTON3, HIGH );
}
void init_relays()
{
pinMode( RELAY1, OUTPUT );
pinMode( RELAY2, OUTPUT );
}
void init_leds()
{
digitalWrite( LED1_RED, 1 );
digitalWrite( LED1_GREEN, 1 );
digitalWrite( LED1_BLUE, 1 );
pinMode( LED1_RED, OUTPUT );
pinMode( LED1_GREEN, OUTPUT );
pinMode( LED1_BLUE, OUTPUT );
digitalWrite( LED2_RED, 1 );
digitalWrite( LED2_GREEN, 1 );
digitalWrite( LED2_BLUE, 1 );
pinMode( LED2_RED, OUTPUT );
pinMode( LED2_GREEN, OUTPUT );
pinMode( LED2_BLUE, OUTPUT );
digitalWrite( LED3_RED, 1 );
digitalWrite( LED3_GREEN, 1 );
digitalWrite( LED3_BLUE, 1 );
pinMode( LED3_RED, OUTPUT );
pinMode( LED3_GREEN, OUTPUT );
pinMode( LED3_BLUE, OUTPUT );
}
void init_joystick( int threshold );
void setup()
{
Serial.begin( 115200 );
Serial.print("\r\nStart");
init_leds();
init_relays();
init_buttons();
init_joystick( 5 ); // initialize with thresholding enabled, dead zone of 5 units
servos[0].attach(SERVO1);
servos[0].write(90);
servos[1].attach(SERVO2);
servos[1].write(90);
servos[2].attach(SERVO3);
servos[2].write(90);
Max.powerOn();
delay( 200 );
}
bool isAndroidVendor(USB_DEVICE_DESCRIPTOR *desc)
{
return desc->idVendor == 0x18d1 || desc->idVendor == 0x22B8;
}
bool isAccessoryDevice(USB_DEVICE_DESCRIPTOR *desc)
{
return desc->idProduct == 0x2D00 || desc->idProduct == 0x2D01;
}
void sendString(byte addr, int index, 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, str);
}
void switchDevice(byte addr)
{
sendString(addr, ACCESSORY_STRING_MANUFACTURER, "Google, Inc.");
sendString(addr, ACCESSORY_STRING_MODEL, "DemoKit");
sendString(addr, ACCESSORY_STRING_TYPE, "Sample Program");
sendString(addr, ACCESSORY_STRING_VERSION, "1.0");
Usb.ctrlReq(addr, 0, USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_VENDOR | USB_SETUP_RECIPIENT_DEVICE,
ACCESSORY_START, 0, 0, 0, 0, NULL);
}
bool findEndpoints(byte addr, EP_RECORD *inEp, EP_RECORD *outEp)
{
int len;
byte err;
uint8_t *p;
err = Usb.getConfDescr(addr, 0, 4, 0, (char *)usbBuff);
if (err) {
Serial.print("Can't get config descriptor length\n");
return false;
}
len = usbBuff[2] | ((int)usbBuff[3] << 8);
Serial.print("Config Desc Length: ");
Serial.println(len, DEC);
if (len > sizeof(usbBuff)) {
Serial.print("config descriptor too large\n");
/* might want to truncate here */
return false;
}
err = Usb.getConfDescr(addr, 0, len, 0, (char *)usbBuff);
if (err) {
Serial.print("Can't get config descriptor\n");
return false;
}
p = usbBuff;
inEp->epAddr = 0;
outEp->epAddr = 0;
while (p < (usbBuff + 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("config desc\n");
break;
case USB_DESCRIPTOR_INTERFACE:
Serial.print("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("unkown desc type ");
Serial.println( descType, HEX);
break;
}
p += descLen;
}
return inEp->epAddr && outEp->epAddr;
}
EP_RECORD ep_record[ 8 ]; //endpoint record structure for the mouse
void doAndroid(void)
{
byte err;
byte idle;
byte b1, b2, b3, c;
EP_RECORD inEp, outEp;
byte count = 0;
if (findEndpoints(1, &inEp, &outEp)) {
ep_record[inEp.epAddr] = inEp;
if (outEp.epAddr != inEp.epAddr)
ep_record[outEp.epAddr] = outEp;
Serial.print("inEp: ");
Serial.println(inEp.epAddr, HEX);
Serial.print("outEp: ");
Serial.println(outEp.epAddr, HEX);
ep_record[0] = *(Usb.getDevTableEntry(0,0));
Usb.setDevTableEntry(1, ep_record);
err = Usb.setConf( 1, 0, 1 );
if (err)
Serial.print("Can't set config to 1\n");
Usb.setUsbTaskState( USB_STATE_RUNNING );
b1 = digitalRead(BUTTON1);
b2 = digitalRead(BUTTON2);
b3 = digitalRead(BUTTON3);
c = captouched();
while(1) {
int len = Usb.newInTransfer(1, inEp.epAddr, sizeof(usbBuff),
(char *)usbBuff);
/* int i; */
/* byte b; */
/* byte msg[3]; */
/* msg[0] = 0x1; */
if (len > 0) {
// XXX: assumes only one command per packet
if (usbBuff[0] == 0x2) {
if (usbBuff[1] == 0x0)
analogWrite( LED1_RED, 255 - usbBuff[2]);
else if (usbBuff[1] == 0x1)
analogWrite( LED1_GREEN, 255 - usbBuff[2]);
else if (usbBuff[1] == 0x2)
analogWrite( LED1_BLUE, 255 - usbBuff[2]);
else if (usbBuff[1] == 0x3)
analogWrite( LED2_RED, 255 - usbBuff[2]);
else if (usbBuff[1] == 0x4)
analogWrite( LED2_GREEN, 255 - usbBuff[2]);
else if (usbBuff[1] == 0x5)
analogWrite( LED2_BLUE, 255 - usbBuff[2]);
else if (usbBuff[1] == 0x6)
analogWrite( LED3_RED, 255 - usbBuff[2]);
else if (usbBuff[1] == 0x7)
analogWrite( LED3_GREEN, 255 - usbBuff[2]);
else if (usbBuff[1] == 0x8)
analogWrite( LED3_BLUE, 255 - usbBuff[2]);
else if (usbBuff[1] == 0x10)
servos[0].write(map(usbBuff[2], 0, 255, 0, 180));
else if (usbBuff[1] == 0x11)
servos[1].write(map(usbBuff[2], 0, 255, 0, 180));
else if (usbBuff[1] == 0x12)
servos[2].write(map(usbBuff[2], 0, 255, 0, 180));
} else if (usbBuff[0] == 0x3) {
if (usbBuff[1] == 0x0)
digitalWrite( RELAY1, usbBuff[2] ? HIGH : LOW );
else if (usbBuff[1] == 0x1)
digitalWrite( RELAY2, usbBuff[2] ? HIGH : LOW );
}
// for (i = 0; i < len; i++)
// Serial.print('\n');
}
}
}
}
void loop()
{
USB_DEVICE_DESCRIPTOR *devDesc = (USB_DEVICE_DESCRIPTOR *) usbBuff;
byte err;
Max.Task();
Usb.Task();
if( Usb.getUsbTaskState() >= USB_STATE_CONFIGURING ) {
Serial.print("\nDevice addressed... ");
Serial.print("Requesting device descriptor.");
err = Usb.getDevDescr(1, 0, 0x12, (char *) devDesc);
if (err) {
Serial.print("\nDevice descriptor cannot be retrieved. Program Halted\n");
while(1);
}
if (isAndroidVendor(devDesc)) {
Serial.print("found android device\n");
if (isAccessoryDevice(devDesc)) {
Serial.print("found android acessory device\n");
doAndroid();
} else {
Serial.print("found possible device. swithcing to serial mode\n");
switchDevice(1);
}
}
while (Usb.getUsbTaskState() != USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE) {
Max.Task();
Usb.Task();
}
Serial.print("detached\n");
}
}
// ==============================================================================
// Austria Microsystems i2c Joystick
/*
If a threshold is provided, the dead zone will be programmed such that interrupts will not
be generated unless the threshold is exceeded.
Note that if you use that mode, you will have to use passage of time with no new interrupts
to detect that the stick has been released and has returned to center.
If you need to explicitly track return to center, pass 0 as the threshold. "Center" will
still bounce around a little
*/
void init_joystick( int threshold )
{
byte status = 0;
pinMode( JOY_SWITCH, INPUT );
digitalWrite( JOY_SWITCH, HIGH ); // enable the internal pullup
pinMode( JOY_nINT, INPUT );
digitalWrite( JOY_nINT, HIGH ); // enable the internal pullup
pinMode( JOY_nRESET, OUTPUT );
digitalWrite( JOY_nRESET, 1 );
delay(1);
digitalWrite( JOY_nRESET, 0 );
delay(1);
digitalWrite( JOY_nRESET, 1 );
Wire.begin();
do {
status = read_joy_reg( 0x0f ); // XXX need timeout
} while ((status & 0xf0) != 0xf0);
write_joy_reg( 0x2e, 0x86 ); // invert magnet polarity setting, per datasheet
calibrate_joystick( threshold ); // calibrate & set up dead zone area
}
int offset_X, offset_Y;
void calibrate_joystick( int dz )
{
char iii;
int x_cal = 0;
int y_cal = 0;
write_joy_reg( 0x0f, 0x00 ); // Low Power Mode, 20ms auto wakeup
// INTn output enabled
// INTn active after each measurement
// Normal (non-Reset) mode
delay(1);
read_joy_reg( 0x11 ); // dummy read of Y_reg to reset interrupt
for( iii = 0; iii != 16; iii++ ) { // read coords 16 times & average
while( !joystick_interrupt() ) // poll for interrupt
;
x_cal += read_joy_reg( 0x10 ); // X pos
y_cal += read_joy_reg( 0x11 ); // Y pos
}
offset_X = -(x_cal>>4); // divide by 16 to get average
offset_Y = -(y_cal>>4);
//sprintf(msgbuf, "offsets = %d, %d\n", offset_X, offset_Y);
//Serial.print(msgbuf);
write_joy_reg( 0x12, dz - offset_X ); // Xp, LEFT threshold for INTn
write_joy_reg( 0x13, -dz - offset_X ); // Xn, RIGHT threshold for INTn
write_joy_reg( 0x14, dz - offset_Y ); // Yp, UP threshold for INTn
write_joy_reg( 0x15, -dz - offset_Y ); // Yn, DOWN threshold for INTn
if ( dz ) // dead zone threshold detect requested?
write_joy_reg( 0x0f, 0x04 ); // Low Power Mode, 20ms auto wakeup
// INTn output enabled
// INTn active when movement exceeds dead zone
// Normal (non-Reset) mode
}
void read_joystick( int *x, int *y )
{
*x = read_joy_reg( 0x10 ) + offset_X;
*y = read_joy_reg( 0x11 ) + offset_Y; // reading Y clears the interrupt
}
char joystick_interrupt()
{
return ( digitalRead( JOY_nINT ) == 0 );
}
#define JOY_I2C_ADDR 0x40
char read_joy_reg( char reg_addr )
{
char c;
Wire.beginTransmission( JOY_I2C_ADDR );
Wire.send( reg_addr );
Wire.endTransmission();
Wire.requestFrom( JOY_I2C_ADDR, 1 );
while(Wire.available())
c = Wire.receive();
return c;
}
void write_joy_reg( char reg_addr, char val )
{
Wire.beginTransmission( JOY_I2C_ADDR );
Wire.send( reg_addr );
Wire.send( val );
Wire.endTransmission();
}
/* Capacitive touch technique from Mario Becker, Fraunhofer IGD, 2007 http://www.igd.fhg.de/igd-a4 */
char captouched()
{
char iii, jjj, retval;
retval = 0;
for( jjj = 0; jjj != 10; jjj++ ) {
delay( 10 );
pinMode( TOUCH, INPUT );
digitalWrite( TOUCH, HIGH );
for ( iii = 0; iii < 16; iii++ )
if( digitalRead( TOUCH ) )
break;
digitalWrite( TOUCH, LOW );
pinMode( TOUCH, OUTPUT );
retval += iii;
}
return retval;
}