USB Host Shield 2.0
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
PS3USB.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
2 
3  This software may be distributed and modified under the terms of the GNU
4  General Public License version 2 (GPL2) as published by the Free Software
5  Foundation and appearing in the file GPL2.TXT included in the packaging of
6  this file. Please note that GPL2 Section 2[b] requires that all works based
7  on this software must also be made publicly available under the terms of
8  the GPL2 ("Copyleft").
9 
10  Contact information
11  -------------------
12 
13  Kristian Lauszus, TKJ Electronics
14  Web : http://www.tkjelectronics.com
15  e-mail : kristianl@tkjelectronics.com
16  */
17 
18 #include "PS3USB.h"
19 #define DEBUG // Uncomment to print data for debugging
20 //#define EXTRADEBUG // Uncomment to get even more debugging data
21 //#define PRINTREPORT // Uncomment to print the report send by the PS3 Controllers
22 
23 const uint8_t PS3_REPORT_BUFFER[] PROGMEM = {
24  0x00, 0x00, 0x00, 0x00, 0x00,
25  0x00, 0x00, 0x00, 0x00, 0x00,
26  0xff, 0x27, 0x10, 0x00, 0x32,
27  0xff, 0x27, 0x10, 0x00, 0x32,
28  0xff, 0x27, 0x10, 0x00, 0x32,
29  0xff, 0x27, 0x10, 0x00, 0x32,
30  0x00, 0x00, 0x00, 0x00, 0x00,
31  0x00, 0x00, 0x00, 0x00, 0x00,
32  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
33 };
34 const uint8_t MOVE_REPORT_BUFFER[] PROGMEM = {
35  0x02, 0x00, // Always 0x02, 0x00,
36  0x00, 0x00, 0x00, // r, g, b,
37  0x00, // Always 0x00,
38  0x00 // Rumble
39 };
40 
41 PS3USB::PS3USB(USB *p, uint8_t btadr5, uint8_t btadr4, uint8_t btadr3, uint8_t btadr2, uint8_t btadr1, uint8_t btadr0):
42 pUsb(p), // pointer to USB class instance - mandatory
43 bAddress(0), // device address - mandatory
44 bPollEnable(false) // don't start polling before dongle is connected
45 {
46  for(uint8_t i=0; i<PS3_MAX_ENDPOINTS; i++) {
47  epInfo[i].epAddr = 0;
48  epInfo[i].maxPktSize = (i) ? 0 : 8;
49  epInfo[i].epAttribs = 0;
51  }
52 
53  if (pUsb) // register in USB subsystem
54  pUsb->RegisterDeviceClass(this); //set devConfig[] entry
55 
56  my_bdaddr[5] = btadr5; // Change to your dongle's Bluetooth address instead
57  my_bdaddr[4] = btadr4;
58  my_bdaddr[3] = btadr3;
59  my_bdaddr[2] = btadr2;
60  my_bdaddr[1] = btadr1;
61  my_bdaddr[0] = btadr0;
62 }
63 
64 uint8_t PS3USB::Init(uint8_t parent, uint8_t port, bool lowspeed) {
65  uint8_t buf[sizeof(USB_DEVICE_DESCRIPTOR)];
66  uint8_t rcode;
67  UsbDevice *p = NULL;
68  EpInfo *oldep_ptr = NULL;
69  uint16_t PID;
70  uint16_t VID;
71 
72  // get memory address of USB device address pool
73  AddressPool &addrPool = pUsb->GetAddressPool();
74 #ifdef EXTRADEBUG
75  Notify(PSTR("\r\nPS3USB Init"));
76 #endif
77  // check if address has already been assigned to an instance
78  if (bAddress) {
79 #ifdef DEBUG
80  Notify(PSTR("\r\nAddress in use"));
81 #endif
83  }
84 
85  // Get pointer to pseudo device with address 0 assigned
86  p = addrPool.GetUsbDevicePtr(0);
87 
88  if (!p) {
89 #ifdef DEBUG
90  Notify(PSTR("\r\nAddress not found"));
91 #endif
93  }
94 
95  if (!p->epinfo) {
96 #ifdef DEBUG
97  Notify(PSTR("\r\nepinfo is null"));
98 #endif
100  }
101 
102  // Save old pointer to EP_RECORD of address 0
103  oldep_ptr = p->epinfo;
104 
105  // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
106  p->epinfo = epInfo;
107 
108  p->lowspeed = lowspeed;
109 
110  // Get device descriptor
111  rcode = pUsb->getDevDescr(0, 0, sizeof(USB_DEVICE_DESCRIPTOR), (uint8_t*)buf);// Get device descriptor - addr, ep, nbytes, data
112  // Restore p->epinfo
113  p->epinfo = oldep_ptr;
114 
115  if(rcode)
116  goto FailGetDevDescr;
117 
118  VID = ((USB_DEVICE_DESCRIPTOR*)buf)->idVendor;
119  PID = ((USB_DEVICE_DESCRIPTOR*)buf)->idProduct;
120 
121  if(VID != PS3_VID || (PID != PS3_PID && PID != PS3NAVIGATION_PID && PID != PS3MOVE_PID))
122  goto FailUnknownDevice;
123 
124  // Allocate new address according to device class
125  bAddress = addrPool.AllocAddress(parent, false, port);
126 
127  if (!bAddress)
129 
130  // Extract Max Packet Size from device descriptor
131  epInfo[0].maxPktSize = (uint8_t)((USB_DEVICE_DESCRIPTOR*)buf)->bMaxPacketSize0;
132 
133  // Assign new address to the device
134  rcode = pUsb->setAddr( 0, 0, bAddress );
135  if (rcode) {
136  p->lowspeed = false;
137  addrPool.FreeAddress(bAddress);
138  bAddress = 0;
139 #ifdef DEBUG
140  Notify(PSTR("\r\nsetAddr: "));
141 #endif
142  PrintHex<uint8_t>(rcode);
143  return rcode;
144  }
145 #ifdef EXTRADEBUG
146  Notify(PSTR("\r\nAddr: "));
147  PrintHex<uint8_t>(bAddress);
148 #endif
149  p->lowspeed = false;
150 
151  //get pointer to assigned address record
152  p = addrPool.GetUsbDevicePtr(bAddress);
153  if (!p)
155 
156  p->lowspeed = lowspeed;
157 
158  // Assign epInfo to epinfo pointer - only EP0 is known
159  rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
160  if (rcode)
161  goto FailSetDevTblEntry;
162 
163 
164  /* The application will work in reduced host mode, so we can save program and data
165  memory space. After verifying the PID and VID we will use known values for the
166  configuration values for device, interface, endpoints and HID for the PS3 Controllers */
167 
168  /* Initialize data structures for endpoints of device */
169  epInfo[ PS3_OUTPUT_PIPE ].epAddr = 0x02; // PS3 output endpoint
171  epInfo[ PS3_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
175  epInfo[ PS3_INPUT_PIPE ].epAddr = 0x01; // PS3 report endpoint
177  epInfo[ PS3_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
181 
182  rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);
183  if( rcode )
184  goto FailSetDevTblEntry;
185 
186  delay(200);//Give time for address change
187 
188  rcode = pUsb->setConf(bAddress, epInfo[ PS3_CONTROL_PIPE ].epAddr, 1);
189  if( rcode )
190  goto FailSetConf;
191 
192  if(PID == PS3_PID || PID == PS3NAVIGATION_PID) {
193  if(PID == PS3_PID) {
194 #ifdef DEBUG
195  Notify(PSTR("\r\nDualshock 3 Controller Connected"));
196 #endif
197  PS3Connected = true;
198  } else { // must be a navigation controller
199 #ifdef DEBUG
200  Notify(PSTR("\r\nNavigation Controller Connected"));
201 #endif
202  PS3NavigationConnected = true;
203  }
204  /* Set internal bluetooth address and request for data */
205  setBdaddr(my_bdaddr);
206  enable_sixaxis();
207  setLedOn(LED1);
208 
209  // Needed for PS3 Dualshock and Navigation commands to work
210  for (uint8_t i = 0; i < PS3_REPORT_BUFFER_SIZE; i++)
211  writeBuf[i] = pgm_read_byte(&PS3_REPORT_BUFFER[i]);
212 
213  for (uint8_t i = 6; i < 10; i++)
214  readBuf[i] = 0x7F; // Set the analog joystick values to center position
215  }
216  else { // must be a Motion controller
217 #ifdef DEBUG
218  Notify(PSTR("\r\nMotion Controller Connected"));
219 #endif
220  PS3MoveConnected = true;
221  setMoveBdaddr(my_bdaddr); // Set internal bluetooth address
222  moveSetBulb(Red);
223 
224  // Needed for Move commands to work
225  for (uint8_t i = 0; i < MOVE_REPORT_BUFFER_SIZE; i++)
226  writeBuf[i] = pgm_read_byte(&MOVE_REPORT_BUFFER[i]);
227  }
228 
229  bPollEnable = true;
230  Notify(PSTR("\r\n"));
231  timer = millis();
232  return 0; // successful configuration
233 
234  /* diagnostic messages */
235 FailGetDevDescr:
236 #ifdef DEBUG
237  Notify(PSTR("\r\ngetDevDescr:"));
238 #endif
239  goto Fail;
240 FailSetDevTblEntry:
241 #ifdef DEBUG
242  Notify(PSTR("\r\nsetDevTblEn:"));
243 #endif
244  goto Fail;
245 FailSetConf:
246 #ifdef DEBUG
247  Notify(PSTR("\r\nsetConf:"));
248 #endif
249  goto Fail;
250 FailUnknownDevice:
251 #ifdef DEBUG
252  Notify(PSTR("\r\nUnknown Device Connected - VID: "));
253  PrintHex<uint16_t>(VID);
254  Notify(PSTR(" PID: "));
255  PrintHex<uint16_t>(PID);
256 #endif
258  goto Fail;
259 Fail:
260 #ifdef DEBUG
261  Notify(PSTR("\r\nPS3 Init Failed, error code: "));
262  Serial.print(rcode,HEX);
263 #endif
264  Release();
265  return rcode;
266 }
267 
268 /* Performs a cleanup after failed Init() attempt */
269 uint8_t PS3USB::Release() {
270  PS3Connected = false;
271  PS3MoveConnected = false;
272  PS3NavigationConnected = false;
274  bAddress = 0;
275  bPollEnable = false;
276  return 0;
277 }
278 uint8_t PS3USB::Poll() {
279  if (!bPollEnable)
280  return 0;
281 
283  uint16_t BUFFER_SIZE = EP_MAXPKTSIZE;
284  pUsb->inTransfer(bAddress, epInfo[ PS3_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf); // input on endpoint 1
285  if(millis() - timer > 100) { // Loop 100ms before processing data
286  readReport();
287 #ifdef PRINTREPORT
288  printReport(); // Uncomment "#define PRINTREPORT" to print the report send by the PS3 Controllers
289 #endif
290  }
291  }
292  else if(PS3MoveConnected) { // One can only set the color of the bulb, set the rumble, set and get the bluetooth address and calibrate the magnetometer via USB
293  if (millis() - timer > 4000) // Send at least every 4th second
294  {
295  Move_Command(writeBuf, MOVE_REPORT_BUFFER_SIZE); // The Bulb and rumble values, has to be written again and again, for it to stay turned on
296  timer = millis();
297  }
298  }
299  return 0;
300 }
301 
302 void PS3USB::readReport() {
303  if (readBuf == NULL)
304  return;
305 
306  ButtonState = (uint32_t)(readBuf[2] | ((uint16_t)readBuf[3] << 8) | ((uint32_t)readBuf[4] << 16));
307 
308  //Notify(PSTR("\r\nButtonState");
309  //PrintHex<uint32_t>(ButtonState);
310 
311  if(ButtonState != OldButtonState) {
312  ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable
313  OldButtonState = ButtonState;
314  }
315 }
316 
317 void PS3USB::printReport() { //Uncomment "#define PRINTREPORT" to print the report send by the PS3 Controllers
318 #ifdef PRINTREPORT
319  if (readBuf == NULL)
320  return;
321  for(uint8_t i = 0; i < PS3_REPORT_BUFFER_SIZE;i++) {
322  PrintHex<uint8_t>(readBuf[i]);
323  Serial.print(" ");
324  }
325  Serial.println();
326 #endif
327 }
328 
330  return (ButtonState & pgm_read_dword(&BUTTONS[(uint8_t)b]));
331 }
333  uint32_t button = pgm_read_dword(&BUTTONS[(uint8_t)b]);
334  bool click = (ButtonClickState & button);
335  ButtonClickState &= ~button; // clear "click" event
336  return click;
337 }
339  if (readBuf == NULL)
340  return 0;
341  return (uint8_t)(readBuf[(pgm_read_byte(&ANALOGBUTTONS[(uint8_t)a]))-9]);
342 }
344  if (readBuf == NULL)
345  return 0;
346  return (uint8_t)(readBuf[((uint8_t)a+6)]);
347 }
349  if (readBuf == NULL)
350  return 0;
351  return ((readBuf[((uint16_t)a)-9] << 8) | readBuf[((uint16_t)a + 1)-9]);
352 }
354  if(PS3Connected) {
355  double accXval;
356  double accYval;
357  double accZval;
358 
359  // Data for the Kionix KXPC4 used in the DualShock 3
360  const double zeroG = 511.5; // 1.65/3.3*1023 (1,65V)
361  accXval = -((double)getSensor(aX)-zeroG);
362  accYval = -((double)getSensor(aY)-zeroG);
363  accZval = -((double)getSensor(aZ)-zeroG);
364 
365  // Convert to 360 degrees resolution
366  // atan2 outputs the value of -π to π (radians)
367  // We are then converting it to 0 to 2π and then to degrees
368  if (a == Pitch) {
369  double angle = (atan2(accYval,accZval)+PI)*RAD_TO_DEG;
370  return angle;
371  } else {
372  double angle = (atan2(accXval,accZval)+PI)*RAD_TO_DEG;
373  return angle;
374  }
375  } else
376  return 0;
377 }
379  if (readBuf == NULL)
380  return false;
381  if (readBuf[((uint16_t)c >> 8)-9] == ((uint8_t)c & 0xff))
382  return true;
383  return false;
384 }
387  char statusOutput[100];
388 
389  strcpy(statusOutput,"ConnectionStatus: ");
390 
391  if (getStatus(Plugged)) strcat(statusOutput,"Plugged");
392  else if (getStatus(Unplugged)) strcat(statusOutput,"Unplugged");
393  else strcat(statusOutput,"Error");
394 
395 
396  strcat(statusOutput," - PowerRating: ");
397 
398  if (getStatus(Charging)) strcat(statusOutput,"Charging");
399  else if (getStatus(NotCharging)) strcat(statusOutput,"Not Charging");
400  else if (getStatus(Shutdown)) strcat(statusOutput,"Shutdown");
401  else if (getStatus(Dying)) strcat(statusOutput,"Dying");
402  else if (getStatus(Low)) strcat(statusOutput,"Low");
403  else if (getStatus(High)) strcat(statusOutput,"High");
404  else if (getStatus(Full)) strcat(statusOutput,"Full");
405  else strcat(statusOutput,"Error");
406 
407  strcat(statusOutput," - WirelessStatus: ");
408 
409  if (getStatus(CableRumble)) strcat(statusOutput,"Cable - Rumble is on");
410  else if (getStatus(Cable)) strcat(statusOutput,"Cable - Rumble is off");
411  else if (getStatus(BluetoothRumble)) strcat(statusOutput,"Bluetooth - Rumble is on");
412  else if (getStatus(Bluetooth)) strcat(statusOutput,"Bluetooth - Rumble is off");
413  else strcat(statusOutput,"Error");
414 
415  return statusOutput;
416  }
417 }
418 
419 /* Playstation Sixaxis Dualshock and Navigation Controller commands */
420 void PS3USB::PS3_Command(uint8_t* data, uint16_t nbytes) {
421  //bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0x01), Report Type (Output 0x02), interface (0x00), datalength, datalength, data)
422  pUsb->ctrlReq(bAddress,epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x01, 0x02, 0x00, nbytes, nbytes, data, NULL);
423 }
425  for (uint8_t i = 0; i < PS3_REPORT_BUFFER_SIZE; i++)
426  writeBuf[i] = pgm_read_byte(&PS3_REPORT_BUFFER[i]); // Reset buffer
427 
428  PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE);
429 }
431  writeBuf[1] = 0x00;
432  writeBuf[2] = 0x00;//low mode off
433  writeBuf[3] = 0x00;
434  writeBuf[4] = 0x00;//high mode off
435 
436  PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE);
437 }
439  /* Still not totally sure how it works, maybe something like this instead?
440  * 3 - duration_right
441  * 4 - power_right
442  * 5 - duration_left
443  * 6 - power_left
444  */
445  if ((mode & 0x30) > 0) {
446  writeBuf[1] = 0xfe;
447  writeBuf[3] = 0xfe;
448  if (mode == RumbleHigh) {
449  writeBuf[2] = 0;//low mode off
450  writeBuf[4] = 0xff;//high mode on
451  }
452  else {
453  writeBuf[2] = 0xff;//low mode on
454  writeBuf[4] = 0;//high mode off
455  }
456  PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE);
457  }
458 }
460  writeBuf[9] &= ~((uint8_t)((pgm_read_byte(&LEDS[(uint8_t)a]) & 0x0f) << 1));
461  PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE);
462 }
464  writeBuf[9] |= (uint8_t)((pgm_read_byte(&LEDS[(uint8_t)a]) & 0x0f) << 1);
465  PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE);
466 }
468  writeBuf[9] ^= (uint8_t)((pgm_read_byte(&LEDS[(uint8_t)a]) & 0x0f) << 1);
469  PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE);
470 }
471 void PS3USB::setBdaddr(uint8_t* BDADDR) {
472  /* Set the internal bluetooth address */
473  uint8_t buf[8];
474  buf[0] = 0x01;
475  buf[1] = 0x00;
476  for (uint8_t i = 0; i < 6; i++)
477  buf[i+2] = BDADDR[5 - i];//Copy into buffer, has to be written reversed
478 
479  //bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0xF5), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data)
480  pUsb->ctrlReq(bAddress,epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0xF5, 0x03, 0x00, 8, 8, buf, NULL);
481 #ifdef DEBUG
482  Notify(PSTR("\r\nBluetooth Address was set to: "));
483  for(int8_t i = 5; i > 0; i--) {
484  PrintHex<uint8_t>(my_bdaddr[i]);
485  Serial.print(":");
486  }
487  PrintHex<uint8_t>(my_bdaddr[0]);
488 #endif
489  return;
490 }
491 void PS3USB::enable_sixaxis() { //Command used to enable the Dualshock 3 and Navigation controller to send data via USB
492  uint8_t cmd_buf[4];
493  cmd_buf[0] = 0x42;// Special PS3 Controller enable commands
494  cmd_buf[1] = 0x0c;
495  cmd_buf[2] = 0x00;
496  cmd_buf[3] = 0x00;
497 
498  //bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0xF4), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data)
499  pUsb->ctrlReq(bAddress,epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0xF4, 0x03, 0x00, 4, 4, cmd_buf, NULL);
500 }
501 
502 /* Playstation Move Controller commands */
503 void PS3USB::Move_Command(uint8_t* data, uint16_t nbytes) {
504  pUsb->outTransfer(bAddress, epInfo[ PS3_OUTPUT_PIPE ].epAddr, nbytes, data);
505 }
506 
507 void PS3USB::moveSetBulb(uint8_t r, uint8_t g, uint8_t b) { //Use this to set the Color using RGB values
508  // set the Bulb's values into the write buffer
509  writeBuf[2] = r;
510  writeBuf[3] = g;
511  writeBuf[4] = b;
512 
513  Move_Command(writeBuf, MOVE_REPORT_BUFFER_SIZE);
514 }
515 void PS3USB::moveSetBulb(Colors color) { //Use this to set the Color using the predefined colors in "enums.h"
516  moveSetBulb((uint8_t)(color >> 16),(uint8_t)(color >> 8),(uint8_t)(color));
517 }
518 void PS3USB::moveSetRumble(uint8_t rumble) {
519 #ifdef DEBUG
520  if(rumble < 64 && rumble != 0) // The rumble value has to at least 64, or approximately 25% (64/255*100)
521  Notify(PSTR("\r\nThe rumble value has to at least 64, or approximately 25%"));
522 #endif
523  //set the rumble value into the write buffer
524  writeBuf[6] = rumble;
525 
526  Move_Command(writeBuf, MOVE_REPORT_BUFFER_SIZE);
527 }
528 void PS3USB::setMoveBdaddr(uint8_t* BDADDR) {
529  /* Set the internal bluetooth address */
530  uint8_t buf[11];
531  buf[0] = 0x05;
532  buf[7] = 0x10;
533  buf[8] = 0x01;
534  buf[9] = 0x02;
535  buf[10] = 0x12;
536 
537  for (uint8_t i = 0; i < 6; i++)
538  buf[i + 1] = BDADDR[i];
539 
540  //bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0x05), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data)
541  pUsb->ctrlReq(bAddress,epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x05, 0x03, 0x00,11,11, buf, NULL);
542 #ifdef DEBUG
543  Notify(PSTR("\r\nBluetooth Address was set to: "));
544  for(int8_t i = 5; i > 0; i--) {
545  PrintHex<uint8_t>(my_bdaddr[i]);
546  Serial.print(":");
547  }
548  PrintHex<uint8_t>(my_bdaddr[0]);
549 #endif
550  return;
551 }