You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

PS4Parser.h 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. /* Copyright (C) 2014 Kristian Lauszus, TKJ Electronics. All rights reserved.
  2. This software may be distributed and modified under the terms of the GNU
  3. General Public License version 2 (GPL2) as published by the Free Software
  4. Foundation and appearing in the file GPL2.TXT included in the packaging of
  5. this file. Please note that GPL2 Section 2[b] requires that all works based
  6. on this software must also be made publicly available under the terms of
  7. the GPL2 ("Copyleft").
  8. Contact information
  9. -------------------
  10. Kristian Lauszus, TKJ Electronics
  11. Web : http://www.tkjelectronics.com
  12. e-mail : [email protected]
  13. */
  14. #ifndef _ps4parser_h_
  15. #define _ps4parser_h_
  16. #include "Usb.h"
  17. #include "controllerEnums.h"
  18. /** Buttons on the controller */
  19. const uint8_t PS4_BUTTONS[] PROGMEM = {
  20. UP, // UP
  21. RIGHT, // RIGHT
  22. DOWN, // DOWN
  23. LEFT, // LEFT
  24. 0x0C, // SHARE
  25. 0x0D, // OPTIONS
  26. 0x0E, // L3
  27. 0x0F, // R3
  28. 0x0A, // L2
  29. 0x0B, // R2
  30. 0x08, // L1
  31. 0x09, // R1
  32. 0x07, // TRIANGLE
  33. 0x06, // CIRCLE
  34. 0x05, // CROSS
  35. 0x04, // SQUARE
  36. 0x10, // PS
  37. 0x11, // TOUCHPAD
  38. };
  39. union PS4Buttons {
  40. struct {
  41. uint8_t dpad : 4;
  42. uint8_t square : 1;
  43. uint8_t cross : 1;
  44. uint8_t circle : 1;
  45. uint8_t triangle : 1;
  46. uint8_t l1 : 1;
  47. uint8_t r1 : 1;
  48. uint8_t l2 : 1;
  49. uint8_t r2 : 1;
  50. uint8_t share : 1;
  51. uint8_t options : 1;
  52. uint8_t l3 : 1;
  53. uint8_t r3 : 1;
  54. uint8_t ps : 1;
  55. uint8_t touchpad : 1;
  56. uint8_t reportCounter : 6;
  57. } __attribute__((packed));
  58. uint32_t val : 24;
  59. } __attribute__((packed));
  60. struct touchpadXY {
  61. uint8_t dummy; // I can not figure out what this data is for, it seems to change randomly, maybe a timestamp?
  62. struct {
  63. uint8_t counter : 7; // Increments every time a finger is touching the touchpad
  64. uint8_t touching : 1; // The top bit is cleared if the finger is touching the touchpad
  65. uint16_t x : 12;
  66. uint16_t y : 12;
  67. } __attribute__((packed)) finger[2]; // 0 = first finger, 1 = second finger
  68. } __attribute__((packed));
  69. struct PS4Status {
  70. uint8_t battery : 4;
  71. uint8_t usb : 1;
  72. uint8_t audio : 1;
  73. uint8_t mic : 1;
  74. uint8_t unknown : 1; // Extension port?
  75. } __attribute__((packed));
  76. struct PS4Data {
  77. /* Button and joystick values */
  78. uint8_t hatValue[4];
  79. PS4Buttons btn;
  80. uint8_t trigger[2];
  81. /* Gyro and accelerometer values */
  82. uint8_t dummy[3]; // First two looks random, while the third one might be some kind of status - it increments once in a while
  83. int16_t gyroY, gyroZ, gyroX;
  84. int16_t accX, accZ, accY;
  85. uint8_t dummy2[5];
  86. PS4Status status;
  87. uint8_t dummy3[3];
  88. /* The rest is data for the touchpad */
  89. touchpadXY xy[3]; // It looks like it sends out three coordinates each time, this might be because the microcontroller inside the PS4 controller is much faster than the Bluetooth connection.
  90. // The last data is read from the last position in the array while the oldest measurement is from the first position.
  91. // The first position will also keep it's value after the finger is released, while the other two will set them to zero.
  92. // Note that if you read fast enough from the device, then only the first one will contain any data.
  93. // The last three bytes are always: 0x00, 0x80, 0x00
  94. } __attribute__((packed));
  95. struct PS4Output {
  96. uint8_t bigRumble, smallRumble; // Rumble
  97. uint8_t r, g, b; // RGB
  98. uint8_t flashOn, flashOff; // Time to flash bright/dark (255 = 2.5 seconds)
  99. bool reportChanged; // The data is send when data is received from the controller
  100. } __attribute__((packed));
  101. enum DPADEnum {
  102. DPAD_UP = 0x0,
  103. DPAD_UP_RIGHT = 0x1,
  104. DPAD_RIGHT = 0x2,
  105. DPAD_RIGHT_DOWN = 0x3,
  106. DPAD_DOWN = 0x4,
  107. DPAD_DOWN_LEFT = 0x5,
  108. DPAD_LEFT = 0x6,
  109. DPAD_LEFT_UP = 0x7,
  110. DPAD_OFF = 0x8,
  111. };
  112. /** This class parses all the data sent by the PS4 controller */
  113. class PS4Parser {
  114. public:
  115. /** Constructor for the PS4Parser class. */
  116. PS4Parser() {
  117. Reset();
  118. };
  119. /** @name PS4 Controller functions */
  120. /**
  121. * getButtonPress(ButtonEnum b) will return true as long as the button is held down.
  122. *
  123. * While getButtonClick(ButtonEnum b) will only return it once.
  124. *
  125. * So you instance if you need to increase a variable once you would use getButtonClick(ButtonEnum b),
  126. * but if you need to drive a robot forward you would use getButtonPress(ButtonEnum b).
  127. * @param b ::ButtonEnum to read.
  128. * @return getButtonPress(ButtonEnum b) will return a true as long as a button is held down, while getButtonClick(ButtonEnum b) will return true once for each button press.
  129. */
  130. bool getButtonPress(ButtonEnum b);
  131. bool getButtonClick(ButtonEnum b);
  132. /**@}*/
  133. /** @name PS4 Controller functions */
  134. /**
  135. * Used to get the analog value from button presses.
  136. * @param b The ::ButtonEnum to read.
  137. * The supported buttons are:
  138. * ::UP, ::RIGHT, ::DOWN, ::LEFT, ::L1, ::L2, ::R1, ::R2,
  139. * ::TRIANGLE, ::CIRCLE, ::CROSS, ::SQUARE, and ::T.
  140. * @return Analog value in the range of 0-255.
  141. */
  142. uint8_t getAnalogButton(ButtonEnum b);
  143. /**
  144. * Used to read the analog joystick.
  145. * @param a ::LeftHatX, ::LeftHatY, ::RightHatX, and ::RightHatY.
  146. * @return Return the analog value in the range of 0-255.
  147. */
  148. uint8_t getAnalogHat(AnalogHatEnum a);
  149. /**
  150. * Get the x-coordinate of the touchpad. Position 0 is in the top left.
  151. * @param finger 0 = first finger, 1 = second finger. If omitted, then 0 will be used.
  152. * @param xyId The controller sends out three packets with the same structure.
  153. * The third one will contain the last measure, but if you read from the controller then there is only be data in the first one.
  154. * For that reason it will be set to 0 if the argument is omitted.
  155. * @return Returns the x-coordinate of the finger.
  156. */
  157. uint16_t getX(uint8_t finger = 0, uint8_t xyId = 0) {
  158. return ps4Data.xy[xyId].finger[finger].x;
  159. };
  160. /**
  161. * Get the y-coordinate of the touchpad. Position 0 is in the top left.
  162. * @param finger 0 = first finger, 1 = second finger. If omitted, then 0 will be used.
  163. * @param xyId The controller sends out three packets with the same structure.
  164. * The third one will contain the last measure, but if you read from the controller then there is only be data in the first one.
  165. * For that reason it will be set to 0 if the argument is omitted.
  166. * @return Returns the y-coordinate of the finger.
  167. */
  168. uint16_t getY(uint8_t finger = 0, uint8_t xyId = 0) {
  169. return ps4Data.xy[xyId].finger[finger].y;
  170. };
  171. /**
  172. * Returns whenever the user is toucing the touchpad.
  173. * @param finger 0 = first finger, 1 = second finger. If omitted, then 0 will be used.
  174. * @param xyId The controller sends out three packets with the same structure.
  175. * The third one will contain the last measure, but if you read from the controller then there is only be data in the first one.
  176. * For that reason it will be set to 0 if the argument is omitted.
  177. * @return Returns true if the specific finger is touching the touchpad.
  178. */
  179. bool isTouching(uint8_t finger = 0, uint8_t xyId = 0) {
  180. return !(ps4Data.xy[xyId].finger[finger].touching); // The bit is cleared when a finger is touching the touchpad
  181. };
  182. /**
  183. * This counter increments every time a finger touches the touchpad.
  184. * @param finger 0 = first finger, 1 = second finger. If omitted, then 0 will be used.
  185. * @param xyId The controller sends out three packets with the same structure.
  186. * The third one will contain the last measure, but if you read from the controller then there is only be data in the first one.
  187. * For that reason it will be set to 0 if the argument is omitted.
  188. * @return Return the value of the counter, note that it is only a 7-bit value.
  189. */
  190. uint8_t getTouchCounter(uint8_t finger = 0, uint8_t xyId = 0) {
  191. return ps4Data.xy[xyId].finger[finger].counter;
  192. };
  193. /**
  194. * Get the angle of the controller calculated using the accelerometer.
  195. * @param a Either ::Pitch or ::Roll.
  196. * @return Return the angle in the range of 0-360.
  197. */
  198. double getAngle(AngleEnum a) {
  199. if (a == Pitch)
  200. return (atan2(ps4Data.accY, ps4Data.accZ) + PI) * RAD_TO_DEG;
  201. else
  202. return (atan2(ps4Data.accX, ps4Data.accZ) + PI) * RAD_TO_DEG;
  203. };
  204. /**
  205. * Used to get the raw values from the 3-axis gyroscope and 3-axis accelerometer inside the PS4 controller.
  206. * @param s The sensor to read.
  207. * @return Returns the raw sensor reading.
  208. */
  209. int16_t getSensor(SensorEnum s) {
  210. switch(s) {
  211. case gX:
  212. return ps4Data.gyroX;
  213. case gY:
  214. return ps4Data.gyroY;
  215. case gZ:
  216. return ps4Data.gyroZ;
  217. case aX:
  218. return ps4Data.accX;
  219. case aY:
  220. return ps4Data.accY;
  221. case aZ:
  222. return ps4Data.accZ;
  223. default:
  224. return 0;
  225. }
  226. };
  227. /**
  228. * Return the battery level of the PS4 controller.
  229. * @return The battery level in the range 0-15.
  230. */
  231. uint8_t getBatteryLevel() {
  232. return ps4Data.status.battery;
  233. };
  234. /**
  235. * Use this to check if an USB cable is connected to the PS4 controller.
  236. * @return Returns true if an USB cable is connected.
  237. */
  238. bool getUsbStatus() {
  239. return ps4Data.status.usb;
  240. };
  241. /**
  242. * Use this to check if an audio jack cable is connected to the PS4 controller.
  243. * @return Returns true if an audio jack cable is connected.
  244. */
  245. bool getAudioStatus() {
  246. return ps4Data.status.audio;
  247. };
  248. /**
  249. * Use this to check if a microphone is connected to the PS4 controller.
  250. * @return Returns true if a microphone is connected.
  251. */
  252. bool getMicStatus() {
  253. return ps4Data.status.mic;
  254. };
  255. /** Turn both rumble and the LEDs off. */
  256. void setAllOff() {
  257. setRumbleOff();
  258. setLedOff();
  259. };
  260. /** Set rumble off. */
  261. void setRumbleOff() {
  262. setRumbleOn(0, 0);
  263. };
  264. /**
  265. * Turn on rumble.
  266. * @param mode Either ::RumbleHigh or ::RumbleLow.
  267. */
  268. void setRumbleOn(RumbleEnum mode) {
  269. if (mode == RumbleLow)
  270. setRumbleOn(0x00, 0xFF);
  271. else
  272. setRumbleOn(0xFF, 0x00);
  273. };
  274. /**
  275. * Turn on rumble.
  276. * @param bigRumble Value for big motor.
  277. * @param smallRumble Value for small motor.
  278. */
  279. void setRumbleOn(uint8_t bigRumble, uint8_t smallRumble) {
  280. ps4Output.bigRumble = bigRumble;
  281. ps4Output.smallRumble = smallRumble;
  282. ps4Output.reportChanged = true;
  283. };
  284. /** Turn all LEDs off. */
  285. void setLedOff() {
  286. setLed(0, 0, 0);
  287. };
  288. /**
  289. * Use this to set the color using RGB values.
  290. * @param r,g,b RGB value.
  291. */
  292. void setLed(uint8_t r, uint8_t g, uint8_t b) {
  293. ps4Output.r = r;
  294. ps4Output.g = g;
  295. ps4Output.b = b;
  296. ps4Output.reportChanged = true;
  297. };
  298. /**
  299. * Use this to set the color using the predefined colors in ::ColorsEnum.
  300. * @param color The desired color.
  301. */
  302. void setLed(ColorsEnum color) {
  303. setLed((uint8_t)(color >> 16), (uint8_t)(color >> 8), (uint8_t)(color));
  304. };
  305. /**
  306. * Set the LEDs flash time.
  307. * @param flashOn Time to flash bright (255 = 2.5 seconds).
  308. * @param flashOff Time to flash dark (255 = 2.5 seconds).
  309. */
  310. void setLedFlash(uint8_t flashOn, uint8_t flashOff) {
  311. ps4Output.flashOn = flashOn;
  312. ps4Output.flashOff = flashOff;
  313. ps4Output.reportChanged = true;
  314. };
  315. /**@}*/
  316. protected:
  317. /**
  318. * Used to parse data sent from the PS4 controller.
  319. * @param len Length of the data.
  320. * @param buf Pointer to the data buffer.
  321. */
  322. void Parse(uint8_t len, uint8_t *buf);
  323. /** Used to reset the different buffers to their default values */
  324. void Reset() {
  325. uint8_t i;
  326. for (i = 0; i < sizeof(ps4Data.hatValue); i++)
  327. ps4Data.hatValue[i] = 127; // Center value
  328. ps4Data.btn.val = 0;
  329. oldButtonState.val = 0;
  330. for (i = 0; i < sizeof(ps4Data.trigger); i++)
  331. ps4Data.trigger[i] = 0;
  332. for (i = 0; i < sizeof(ps4Data.xy)/sizeof(ps4Data.xy[0]); i++) {
  333. for (uint8_t j = 0; j < sizeof(ps4Data.xy[0].finger)/sizeof(ps4Data.xy[0].finger[0]); j++)
  334. ps4Data.xy[i].finger[j].touching = 1; // The bit is cleared if the finger is touching the touchpad
  335. }
  336. ps4Data.btn.dpad = DPAD_OFF;
  337. oldButtonState.dpad = DPAD_OFF;
  338. buttonClickState.dpad = 0;
  339. oldDpad = 0;
  340. ps4Output.bigRumble = ps4Output.smallRumble = 0;
  341. ps4Output.r = ps4Output.g = ps4Output.b = 0;
  342. ps4Output.flashOn = ps4Output.flashOff = 0;
  343. ps4Output.reportChanged = false;
  344. };
  345. /**
  346. * Send the output to the PS4 controller. This is implemented in PS4BT.h and PS4USB.h.
  347. * @param output Pointer to PS4Output buffer;
  348. */
  349. virtual void sendOutputReport(PS4Output *output) = 0;
  350. private:
  351. bool checkDpad(ButtonEnum b); // Used to check PS4 DPAD buttons
  352. PS4Data ps4Data;
  353. PS4Buttons oldButtonState, buttonClickState;
  354. PS4Output ps4Output;
  355. uint8_t oldDpad;
  356. };
  357. #endif