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