blob: 69e75bca616da057eabe128fd85b4bf0a9cd29bc [file] [log] [blame]
Erik Gilling3edead52011-03-08 15:59:23 -08001#include <Max3421e.h>
2#include <Usb.h>
3#include <Wire.h>
4#include <Servo.h>
5
6#define USB_ACCESSORY_VENDOR_ID 0x18D1
7#define USB_ACCESSORY_PRODUCT_ID 0x2D00
8
9#define USB_ACCESSORY_ADB_PRODUCT_ID 0x2D01
10#define ACCESSORY_STRING_MANUFACTURER 0
11#define ACCESSORY_STRING_MODEL 1
Erik Gilling77bad742011-03-15 22:10:35 -070012#define ACCESSORY_STRING_DESCRIPTION 2
Erik Gilling3edead52011-03-08 15:59:23 -080013#define ACCESSORY_STRING_VERSION 3
Erik Gilling77bad742011-03-15 22:10:35 -070014#define ACCESSORY_STRING_URI 4
15#define ACCESSORY_STRING_SERIAL 5
Erik Gilling3edead52011-03-08 15:59:23 -080016
Erik Gilling77bad742011-03-15 22:10:35 -070017#define ACCESSORY_GET_PROTOCOL 51
Erik Gilling3edead52011-03-08 15:59:23 -080018#define ACCESSORY_SEND_STRING 52
19#define ACCESSORY_START 53
20
21
22#define LED3_RED 2
Erik Gillingea909002011-03-16 15:07:07 -070023#define LED3_GREEN 4
24#define LED3_BLUE 3
Erik Gilling3edead52011-03-08 15:59:23 -080025
26#define LED2_RED 5
Erik Gillingea909002011-03-16 15:07:07 -070027#define LED2_GREEN 7
28#define LED2_BLUE 6
Erik Gilling3edead52011-03-08 15:59:23 -080029
30#define LED1_RED 8
Erik Gillingea909002011-03-16 15:07:07 -070031#define LED1_GREEN 10
32#define LED1_BLUE 9
Erik Gilling3edead52011-03-08 15:59:23 -080033
34#define SERVO1 11
35#define SERVO2 12
36#define SERVO3 13
37
38#define TOUCH 14
39
40#define RELAY1 A0
41#define RELAY2 A1
42
43#define LIGHT_SENSOR A2
44#define TEMP_SENSOR A3
45
46#define BUTTON1 A6
47#define BUTTON2 A7
48#define BUTTON3 A8
49
50#define JOY_SWITCH A9 // pulls line down when pressed
51#define JOY_nINT A10 // active low interrupt input
52#define JOY_nRESET A11 // active low reset output
53
54
55MAX3421E Max;
56USB Usb;
57Servo servos[3];
58
59
60void setup();
61void loop();
62
63uint8_t usbBuff[256];
64
65
66void init_buttons()
67{
68 pinMode( BUTTON1, INPUT );
69 pinMode( BUTTON2, INPUT );
70 pinMode( BUTTON3, INPUT );
71
72 digitalWrite( BUTTON1, HIGH ); // enable the internal pullups
73 digitalWrite( BUTTON2, HIGH );
74 digitalWrite( BUTTON3, HIGH );
75}
76
77
78void init_relays()
79{
80 pinMode( RELAY1, OUTPUT );
81 pinMode( RELAY2, OUTPUT );
82}
83
84
85void init_leds()
86{
87 digitalWrite( LED1_RED, 1 );
88 digitalWrite( LED1_GREEN, 1 );
89 digitalWrite( LED1_BLUE, 1 );
90
91 pinMode( LED1_RED, OUTPUT );
92 pinMode( LED1_GREEN, OUTPUT );
93 pinMode( LED1_BLUE, OUTPUT );
94
95 digitalWrite( LED2_RED, 1 );
96 digitalWrite( LED2_GREEN, 1 );
97 digitalWrite( LED2_BLUE, 1 );
98
99 pinMode( LED2_RED, OUTPUT );
100 pinMode( LED2_GREEN, OUTPUT );
101 pinMode( LED2_BLUE, OUTPUT );
102
103 digitalWrite( LED3_RED, 1 );
104 digitalWrite( LED3_GREEN, 1 );
105 digitalWrite( LED3_BLUE, 1 );
106
107 pinMode( LED3_RED, OUTPUT );
108 pinMode( LED3_GREEN, OUTPUT );
109 pinMode( LED3_BLUE, OUTPUT );
110}
111
112void init_joystick( int threshold );
113
114void setup()
115{
116 Serial.begin( 115200 );
117 Serial.print("\r\nStart");
118
119 init_leds();
120 init_relays();
121 init_buttons();
122 init_joystick( 5 ); // initialize with thresholding enabled, dead zone of 5 units
123
124
125 servos[0].attach(SERVO1);
126 servos[0].write(90);
127 servos[1].attach(SERVO2);
128 servos[1].write(90);
129 servos[2].attach(SERVO3);
130 servos[2].write(90);
131
132 Max.powerOn();
133 delay( 200 );
134}
135
136bool isAndroidVendor(USB_DEVICE_DESCRIPTOR *desc)
137{
138 return desc->idVendor == 0x18d1 || desc->idVendor == 0x22B8;
139}
140
141bool isAccessoryDevice(USB_DEVICE_DESCRIPTOR *desc)
142{
143 return desc->idProduct == 0x2D00 || desc->idProduct == 0x2D01;
144}
145
Erik Gilling77bad742011-03-15 22:10:35 -0700146int getProtocol(byte addr)
147{
148 uint16_t protocol = -1;
149 Usb.ctrlReq(addr, 0, USB_SETUP_DEVICE_TO_HOST | USB_SETUP_TYPE_VENDOR | USB_SETUP_RECIPIENT_DEVICE,
150 ACCESSORY_GET_PROTOCOL, 0, 0, 0, 2, (char *)&protocol);
151 return protocol;
152}
153
Erik Gilling3edead52011-03-08 15:59:23 -0800154void sendString(byte addr, int index, char *str)
155{
156 Usb.ctrlReq(addr, 0, USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_VENDOR | USB_SETUP_RECIPIENT_DEVICE,
157 ACCESSORY_SEND_STRING, 0, 0, index, strlen(str) + 1, str);
158
159}
160
Erik Gilling77bad742011-03-15 22:10:35 -0700161bool switchDevice(byte addr)
Erik Gilling3edead52011-03-08 15:59:23 -0800162{
Erik Gilling77bad742011-03-15 22:10:35 -0700163 int protocol = getProtocol(addr);
164 if (protocol == 1)
165 Serial.print("device supports protcol 1\n");
166 else {
167 Serial.print("could not read device protocol version\n");
168 return false;
169 }
Erik Gilling3edead52011-03-08 15:59:23 -0800170 sendString(addr, ACCESSORY_STRING_MANUFACTURER, "Google, Inc.");
171 sendString(addr, ACCESSORY_STRING_MODEL, "DemoKit");
Erik Gilling77bad742011-03-15 22:10:35 -0700172 sendString(addr, ACCESSORY_STRING_DESCRIPTION, "DemoKit test board");
Erik Gilling3edead52011-03-08 15:59:23 -0800173 sendString(addr, ACCESSORY_STRING_VERSION, "1.0");
Erik Gilling77bad742011-03-15 22:10:35 -0700174 sendString(addr, ACCESSORY_STRING_URI, "http://www.android.com");
175 sendString(addr, ACCESSORY_STRING_SERIAL, "0000000012345678");
Erik Gilling3edead52011-03-08 15:59:23 -0800176
177 Usb.ctrlReq(addr, 0, USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_VENDOR | USB_SETUP_RECIPIENT_DEVICE,
178 ACCESSORY_START, 0, 0, 0, 0, NULL);
Erik Gilling77bad742011-03-15 22:10:35 -0700179 return true;
Erik Gilling3edead52011-03-08 15:59:23 -0800180}
181
182bool findEndpoints(byte addr, EP_RECORD *inEp, EP_RECORD *outEp)
183{
184 int len;
185 byte err;
186 uint8_t *p;
187
188 err = Usb.getConfDescr(addr, 0, 4, 0, (char *)usbBuff);
189 if (err) {
190 Serial.print("Can't get config descriptor length\n");
191 return false;
192 }
193
194 len = usbBuff[2] | ((int)usbBuff[3] << 8);
195 Serial.print("Config Desc Length: ");
196 Serial.println(len, DEC);
197 if (len > sizeof(usbBuff)) {
198 Serial.print("config descriptor too large\n");
199 /* might want to truncate here */
200 return false;
201 }
202
203 err = Usb.getConfDescr(addr, 0, len, 0, (char *)usbBuff);
204 if (err) {
205 Serial.print("Can't get config descriptor\n");
206 return false;
207 }
208
209 p = usbBuff;
210 inEp->epAddr = 0;
211 outEp->epAddr = 0;
212 while (p < (usbBuff + len)){
213 uint8_t descLen = p[0];
214 uint8_t descType = p[1];
215 USB_ENDPOINT_DESCRIPTOR *epDesc;
216 EP_RECORD *ep;
217
218 switch (descType) {
219 case USB_DESCRIPTOR_CONFIGURATION:
220 Serial.print("config desc\n");
221 break;
222
223 case USB_DESCRIPTOR_INTERFACE:
224 Serial.print("interface desc\n");
225 break;
226
227 case USB_DESCRIPTOR_ENDPOINT:
228 epDesc = (USB_ENDPOINT_DESCRIPTOR *)p;
229 if (!inEp->epAddr && (epDesc->bEndpointAddress & 0x80))
230 ep = inEp;
231 else if (!outEp->epAddr)
232 ep = outEp;
233 else
234 ep = NULL;
235
236 if (ep) {
237 ep->epAddr = epDesc->bEndpointAddress & 0x7f;
238 ep->Attr = epDesc->bmAttributes;
239 ep->MaxPktSize = epDesc->wMaxPacketSize;
240 ep->sndToggle = bmSNDTOG0;
241 ep->rcvToggle = bmRCVTOG0;
242 }
243 break;
244
245 default:
246 Serial.print("unkown desc type ");
247 Serial.println( descType, HEX);
248 break;
249 }
250
251 p += descLen;
252 }
253
254 return inEp->epAddr && outEp->epAddr;
255}
256
257EP_RECORD ep_record[ 8 ]; //endpoint record structure for the mouse
258
259
260void doAndroid(void)
261{
262 byte err;
263 byte idle;
264 byte b1, b2, b3, c;
265 EP_RECORD inEp, outEp;
266 byte count = 0;
267
268 if (findEndpoints(1, &inEp, &outEp)) {
269
270 ep_record[inEp.epAddr] = inEp;
271 if (outEp.epAddr != inEp.epAddr)
272 ep_record[outEp.epAddr] = outEp;
273
274 Serial.print("inEp: ");
275 Serial.println(inEp.epAddr, HEX);
276 Serial.print("outEp: ");
277 Serial.println(outEp.epAddr, HEX);
278
279 ep_record[0] = *(Usb.getDevTableEntry(0,0));
280 Usb.setDevTableEntry(1, ep_record);
281
282 err = Usb.setConf( 1, 0, 1 );
283 if (err)
284 Serial.print("Can't set config to 1\n");
285
286 Usb.setUsbTaskState( USB_STATE_RUNNING );
287
288 b1 = digitalRead(BUTTON1);
289 b2 = digitalRead(BUTTON2);
290 b3 = digitalRead(BUTTON3);
291 c = captouched();
292
293 while(1) {
294 int len = Usb.newInTransfer(1, inEp.epAddr, sizeof(usbBuff),
295 (char *)usbBuff, 1);
296 int i;
297 byte b;
298 byte msg[3];
299 msg[0] = 0x1;
300
301 if (len > 0) {
302 // XXX: assumes only one command per packet
303 Serial.print(usbBuff[0], HEX);
304 Serial.print(":");
305 Serial.print(usbBuff[1], HEX);
306 Serial.print(":");
307 Serial.println(usbBuff[2], HEX);
308 if (usbBuff[0] == 0x2) {
309 if (usbBuff[1] == 0x0)
310 analogWrite( LED1_RED, 255 - usbBuff[2]);
311 else if (usbBuff[1] == 0x1)
312 analogWrite( LED1_GREEN, 255 - usbBuff[2]);
313 else if (usbBuff[1] == 0x2)
314 analogWrite( LED1_BLUE, 255 - usbBuff[2]);
315 else if (usbBuff[1] == 0x3)
316 analogWrite( LED2_RED, 255 - usbBuff[2]);
317 else if (usbBuff[1] == 0x4)
318 analogWrite( LED2_GREEN, 255 - usbBuff[2]);
319 else if (usbBuff[1] == 0x5)
320 analogWrite( LED2_BLUE, 255 - usbBuff[2]);
321 else if (usbBuff[1] == 0x6)
322 analogWrite( LED3_RED, 255 - usbBuff[2]);
323 else if (usbBuff[1] == 0x7)
324 analogWrite( LED3_GREEN, 255 - usbBuff[2]);
325 else if (usbBuff[1] == 0x8)
326 analogWrite( LED3_BLUE, 255 - usbBuff[2]);
327 else if (usbBuff[1] == 0x10)
328 servos[0].write(map(usbBuff[2], 0, 255, 0, 180));
329 else if (usbBuff[1] == 0x11)
330 servos[1].write(map(usbBuff[2], 0, 255, 0, 180));
331 else if (usbBuff[1] == 0x12)
332 servos[2].write(map(usbBuff[2], 0, 255, 0, 180));
333 } else if (usbBuff[0] == 0x3) {
334 if (usbBuff[1] == 0x0)
335 digitalWrite( RELAY1, usbBuff[2] ? HIGH : LOW );
336 else if (usbBuff[1] == 0x1)
337 digitalWrite( RELAY2, usbBuff[2] ? HIGH : LOW );
338
339 }
340
341// for (i = 0; i < len; i++)
342// Serial.print('\n');
343 }
344
345 b = digitalRead(BUTTON1);
346 if (b != b1) {
347 msg[1] = 0;
348 msg[2] = b ? 0 : 1;
349 Usb.outTransfer(1, outEp.epAddr, 3, (char *)msg);
350 b1 = b;
351 }
352
353 b = digitalRead(BUTTON2);
354 if (b != b2) {
355 msg[1] = 1;
356 msg[2] = b ? 0 : 1;
357 Usb.outTransfer(1, outEp.epAddr, 3, (char *)msg);
358 b2 = b;
359 }
360
361 b = digitalRead(BUTTON3);
362 if (b != b3) {
363 msg[1] = 2;
364 msg[2] = b ? 0 : 1;
365 Usb.outTransfer(1, outEp.epAddr, 3, (char *)msg);
366 b3 = b;
367 }
368
369 if ((count++ % 16) == 0) {
370 uint16_t val;
371 int x, y;
372
373 val = analogRead(TEMP_SENSOR);
374 msg[0] = 0x4;
375 msg[1] = val >> 8;
376 msg[2] = val & 0xff;
377 Usb.outTransfer(1, outEp.epAddr, 3, (char *)msg);
378
379 val = analogRead(LIGHT_SENSOR);
380 msg[0] = 0x5;
381 msg[1] = val >> 8;
382 msg[2] = val & 0xff;
383 Usb.outTransfer(1, outEp.epAddr, 3, (char *)msg);
384
385 read_joystick(&x, &y);
386 msg[0] = 0x6;
387 msg[1] = constrain(x, -128, 127);
388 msg[2] = constrain(y, -128, 127);
389 Usb.outTransfer(1, outEp.epAddr, 3, (char *)msg);
390
391 char c0 = captouched();
392 if (c0 != c) {
393 msg[0] = 0x1;
394 msg[1] = 3;
395 msg[2] = c0 ? 0 : 1;
396 Usb.outTransfer(1, outEp.epAddr, 3, (char *)msg);
397 c = c0;
398 }
399 }
400
401 delay(10);
402
403 }
404
405 }
406
407}
408
409
410void loop()
411{
412 USB_DEVICE_DESCRIPTOR *devDesc = (USB_DEVICE_DESCRIPTOR *) usbBuff;
413 byte err;
414
415 Max.Task();
416 Usb.Task();
417 if( Usb.getUsbTaskState() >= USB_STATE_CONFIGURING ) {
418 Serial.print("\nDevice addressed... ");
419 Serial.print("Requesting device descriptor.");
420
421 err = Usb.getDevDescr(1, 0, 0x12, (char *) devDesc);
422 if (err) {
423 Serial.print("\nDevice descriptor cannot be retrieved. Program Halted\n");
424 while(1);
425 }
426
427 if (isAndroidVendor(devDesc)) {
428 Serial.print("found android device\n");
429
430 if (isAccessoryDevice(devDesc)) {
431 Serial.print("found android acessory device\n");
432 doAndroid();
433 } else {
434 Serial.print("found possible device. swithcing to serial mode\n");
435 switchDevice(1);
436 }
437 }
438
439 while (Usb.getUsbTaskState() != USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE) {
440 Max.Task();
441 Usb.Task();
442
443
444 }
445
446 Serial.print("detached\n");
447
448 }
449
450}
451
452// ==============================================================================
453// Austria Microsystems i2c Joystick
454
455/*
456 If a threshold is provided, the dead zone will be programmed such that interrupts will not
457 be generated unless the threshold is exceeded.
458
459 Note that if you use that mode, you will have to use passage of time with no new interrupts
460 to detect that the stick has been released and has returned to center.
461
462 If you need to explicitly track return to center, pass 0 as the threshold. "Center" will
463 still bounce around a little
464*/
465
466
467void init_joystick( int threshold )
468{
469 byte status = 0;
470
471 pinMode( JOY_SWITCH, INPUT );
472 digitalWrite( JOY_SWITCH, HIGH ); // enable the internal pullup
473
474 pinMode( JOY_nINT, INPUT );
475 digitalWrite( JOY_nINT, HIGH ); // enable the internal pullup
476
477 pinMode( JOY_nRESET, OUTPUT );
478
479 digitalWrite( JOY_nRESET, 1 );
480 delay(1);
481 digitalWrite( JOY_nRESET, 0 );
482 delay(1);
483 digitalWrite( JOY_nRESET, 1 );
484
485 Wire.begin();
486
487 do {
488 status = read_joy_reg( 0x0f ); // XXX need timeout
489 } while ((status & 0xf0) != 0xf0);
490
491 write_joy_reg( 0x2e, 0x86 ); // invert magnet polarity setting, per datasheet
492
493 calibrate_joystick( threshold ); // calibrate & set up dead zone area
494}
495
496
497int offset_X, offset_Y;
498
499void calibrate_joystick( int dz )
500{
501 char iii;
502 int x_cal = 0;
503 int y_cal = 0;
504
505 write_joy_reg( 0x0f, 0x00 ); // Low Power Mode, 20ms auto wakeup
506 // INTn output enabled
507 // INTn active after each measurement
508 // Normal (non-Reset) mode
509 delay(1);
510
511 read_joy_reg( 0x11 ); // dummy read of Y_reg to reset interrupt
512
513 for( iii = 0; iii != 16; iii++ ) { // read coords 16 times & average
514 while( !joystick_interrupt() ) // poll for interrupt
515 ;
516 x_cal += read_joy_reg( 0x10 ); // X pos
517 y_cal += read_joy_reg( 0x11 ); // Y pos
518 }
519
520 offset_X = -(x_cal>>4); // divide by 16 to get average
521 offset_Y = -(y_cal>>4);
522
523 //sprintf(msgbuf, "offsets = %d, %d\n", offset_X, offset_Y);
524 //Serial.print(msgbuf);
525
526 write_joy_reg( 0x12, dz - offset_X ); // Xp, LEFT threshold for INTn
527 write_joy_reg( 0x13, -dz - offset_X ); // Xn, RIGHT threshold for INTn
528 write_joy_reg( 0x14, dz - offset_Y ); // Yp, UP threshold for INTn
529 write_joy_reg( 0x15, -dz - offset_Y ); // Yn, DOWN threshold for INTn
530
531 if ( dz ) // dead zone threshold detect requested?
532 write_joy_reg( 0x0f, 0x04 ); // Low Power Mode, 20ms auto wakeup
533 // INTn output enabled
534 // INTn active when movement exceeds dead zone
535 // Normal (non-Reset) mode
536}
537
538
539void read_joystick( int *x, int *y )
540{
541 *x = read_joy_reg( 0x10 ) + offset_X;
542 *y = read_joy_reg( 0x11 ) + offset_Y; // reading Y clears the interrupt
543}
544
545char joystick_interrupt()
546{
547 return ( digitalRead( JOY_nINT ) == 0 );
548}
549
550
551#define JOY_I2C_ADDR 0x40
552
553char read_joy_reg( char reg_addr )
554{
555 char c;
556
557 Wire.beginTransmission( JOY_I2C_ADDR );
558 Wire.send( reg_addr );
559 Wire.endTransmission();
560
561 Wire.requestFrom( JOY_I2C_ADDR, 1 );
562
563 while(Wire.available())
564 c = Wire.receive();
565
566 return c;
567}
568
569void write_joy_reg( char reg_addr, char val )
570{
571 Wire.beginTransmission( JOY_I2C_ADDR );
572 Wire.send( reg_addr );
573 Wire.send( val );
574 Wire.endTransmission();
575}
576
577/* Capacitive touch technique from Mario Becker, Fraunhofer IGD, 2007 http://www.igd.fhg.de/igd-a4 */
578
579char captouched()
580{
581 char iii, jjj, retval;
582
583 retval = 0;
584
585 for( jjj = 0; jjj != 10; jjj++ ) {
586 delay( 10 );
587
588 pinMode( TOUCH, INPUT );
589 digitalWrite( TOUCH, HIGH );
590
591 for ( iii = 0; iii < 16; iii++ )
592 if( digitalRead( TOUCH ) )
593 break;
594
595 digitalWrite( TOUCH, LOW );
596 pinMode( TOUCH, OUTPUT );
597
598 retval += iii;
599 }
600
601 return retval;
602}