2014-01-18 22:36:01 +01:00
/* Copyright (C) 2014 Kristian Lauszus, TKJ Electronics. All rights reserved.
This software may be distributed and modified under the terms of the GNU
General Public License version 2 ( GPL2 ) as published by the Free Software
Foundation and appearing in the file GPL2 . TXT included in the packaging of
this file . Please note that GPL2 Section 2 [ b ] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ( " Copyleft " ) .
Contact information
- - - - - - - - - - - - - - - - - - -
Kristian Lauszus , TKJ Electronics
Web : http : //www.tkjelectronics.com
e - mail : kristianl @ tkjelectronics . com
*/
# ifndef _ps4parser_h_
# define _ps4parser_h_
2014-01-19 16:58:29 +01:00
# include "Usb.h"
2014-01-18 22:36:01 +01:00
# include "controllerEnums.h"
/** Buttons on the controller */
const uint8_t PS4_BUTTONS [ ] PROGMEM = {
UP , // UP
RIGHT , // RIGHT
DOWN , // DOWN
LEFT , // LEFT
0x0C , // SHARE
0x0D , // OPTIONS
0x0E , // L3
0x0F , // R3
0x0A , // L2
0x0B , // R2
0x08 , // L1
0x09 , // R1
0x07 , // TRIANGLE
0x06 , // CIRCLE
0x05 , // CROSS
0x04 , // SQUARE
0x10 , // PS
0x11 , // TOUCHPAD
} ;
union PS4Buttons {
struct {
uint8_t dpad : 4 ;
uint8_t square : 1 ;
uint8_t cross : 1 ;
uint8_t circle : 1 ;
uint8_t triangle : 1 ;
uint8_t l1 : 1 ;
uint8_t r1 : 1 ;
uint8_t l2 : 1 ;
uint8_t r2 : 1 ;
uint8_t share : 1 ;
uint8_t options : 1 ;
uint8_t l3 : 1 ;
uint8_t r3 : 1 ;
uint8_t ps : 1 ;
uint8_t touchpad : 1 ;
2014-01-22 06:31:27 +01:00
uint8_t reportCounter : 6 ;
2014-02-16 01:24:38 +01:00
} __attribute__ ( ( packed ) ) ;
2014-02-17 01:25:29 +01:00
uint32_t val : 24 ;
} __attribute__ ( ( packed ) ) ;
2014-01-18 22:36:01 +01:00
struct touchpadXY {
uint8_t dummy ; // I can not figure out what this data is for, it seems to change randomly, maybe a timestamp?
struct {
2014-02-03 23:06:27 +01:00
uint8_t counter : 7 ; // Increments every time a finger is touching the touchpad
uint8_t touching : 1 ; // The top bit is cleared if the finger is touching the touchpad
uint16_t x : 12 ;
uint16_t y : 12 ;
2014-02-16 01:24:38 +01:00
} __attribute__ ( ( packed ) ) finger [ 2 ] ; // 0 = first finger, 1 = second finger
} __attribute__ ( ( packed ) ) ;
2014-01-18 22:36:01 +01:00
2014-04-23 23:40:47 +02:00
struct PS4Status {
uint8_t battery : 4 ;
uint8_t usb : 1 ;
uint8_t audio : 1 ;
uint8_t mic : 1 ;
uint8_t unknown : 1 ; // Extension port?
} __attribute__ ( ( packed ) ) ;
2014-01-18 22:36:01 +01:00
struct PS4Data {
/* Button and joystick values */
uint8_t hatValue [ 4 ] ;
PS4Buttons btn ;
uint8_t trigger [ 2 ] ;
/* Gyro and accelerometer values */
2014-01-19 16:58:29 +01:00
uint8_t dummy [ 3 ] ; // First two looks random, while the third one might be some kind of status - it increments once in a while
2014-01-18 22:36:01 +01:00
int16_t gyroY , gyroZ , gyroX ;
int16_t accX , accZ , accY ;
2014-04-23 23:40:47 +02:00
uint8_t dummy2 [ 5 ] ;
PS4Status status ;
uint8_t dummy3 [ 3 ] ;
2014-01-18 22:36:01 +01:00
/* The rest is data for the touchpad */
2014-01-22 06:31:27 +01:00
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.
2014-01-18 22:36:01 +01:00
// The last data is read from the last position in the array while the oldest measurement is from the first position.
// The first position will also keep it's value after the finger is released, while the other two will set them to zero.
// Note that if you read fast enough from the device, then only the first one will contain any data.
// The last three bytes are always: 0x00, 0x80, 0x00
2014-02-16 01:24:38 +01:00
} __attribute__ ( ( packed ) ) ;
2014-01-18 22:36:01 +01:00
2014-02-17 00:21:21 +01:00
struct PS4Output {
uint8_t bigRumble , smallRumble ; // Rumble
uint8_t r , g , b ; // RGB
uint8_t flashOn , flashOff ; // Time to flash bright/dark (255 = 2.5 seconds)
bool reportChanged ; // The data is send when data is received from the controller
} __attribute__ ( ( packed ) ) ;
2014-01-18 22:36:01 +01:00
enum DPADEnum {
DPAD_UP = 0x0 ,
DPAD_UP_RIGHT = 0x1 ,
DPAD_RIGHT = 0x2 ,
DPAD_RIGHT_DOWN = 0x3 ,
DPAD_DOWN = 0x4 ,
DPAD_DOWN_LEFT = 0x5 ,
DPAD_LEFT = 0x6 ,
DPAD_LEFT_UP = 0x7 ,
DPAD_OFF = 0x8 ,
} ;
/** This class parses all the data sent by the PS4 controller */
class PS4Parser {
public :
/** Constructor for the PS4Parser class. */
PS4Parser ( ) {
Reset ( ) ;
} ;
/** @name PS4 Controller functions */
/**
* getButtonPress ( ButtonEnum b ) will return true as long as the button is held down .
*
* While getButtonClick ( ButtonEnum b ) will only return it once .
*
* So you instance if you need to increase a variable once you would use getButtonClick ( ButtonEnum b ) ,
* but if you need to drive a robot forward you would use getButtonPress ( ButtonEnum b ) .
* @ param b : : ButtonEnum to read .
* @ 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 .
*/
bool getButtonPress ( ButtonEnum b ) ;
bool getButtonClick ( ButtonEnum b ) ;
/**@}*/
/** @name PS4 Controller functions */
/**
* Used to get the analog value from button presses .
2014-01-19 17:14:09 +01:00
* @ param b The : : ButtonEnum to read .
2014-01-18 22:36:01 +01:00
* The supported buttons are :
* : : UP , : : RIGHT , : : DOWN , : : LEFT , : : L1 , : : L2 , : : R1 , : : R2 ,
* : : TRIANGLE , : : CIRCLE , : : CROSS , : : SQUARE , and : : T .
* @ return Analog value in the range of 0 - 255.
*/
2014-01-19 16:58:29 +01:00
uint8_t getAnalogButton ( ButtonEnum b ) ;
2014-01-18 22:36:01 +01:00
/**
* Used to read the analog joystick .
* @ param a : : LeftHatX , : : LeftHatY , : : RightHatX , and : : RightHatY .
* @ return Return the analog value in the range of 0 - 255.
*/
uint8_t getAnalogHat ( AnalogHatEnum a ) ;
2014-01-19 16:58:29 +01:00
/**
* Get the x - coordinate of the touchpad . Position 0 is in the top left .
* @ param finger 0 = first finger , 1 = second finger . If omitted , then 0 will be used .
* @ param xyId The controller sends out three packets with the same structure .
* 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 .
* For that reason it will be set to 0 if the argument is omitted .
* @ return Returns the x - coordinate of the finger .
*/
2014-01-18 22:36:01 +01:00
uint16_t getX ( uint8_t finger = 0 , uint8_t xyId = 0 ) {
return ps4Data . xy [ xyId ] . finger [ finger ] . x ;
} ;
2014-01-19 16:58:29 +01:00
/**
* Get the y - coordinate of the touchpad . Position 0 is in the top left .
* @ param finger 0 = first finger , 1 = second finger . If omitted , then 0 will be used .
* @ param xyId The controller sends out three packets with the same structure .
* 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 .
* For that reason it will be set to 0 if the argument is omitted .
* @ return Returns the y - coordinate of the finger .
*/
2014-01-18 22:36:01 +01:00
uint16_t getY ( uint8_t finger = 0 , uint8_t xyId = 0 ) {
return ps4Data . xy [ xyId ] . finger [ finger ] . y ;
} ;
2014-01-19 16:58:29 +01:00
/**
* Returns whenever the user is toucing the touchpad .
* @ param finger 0 = first finger , 1 = second finger . If omitted , then 0 will be used .
* @ param xyId The controller sends out three packets with the same structure .
* 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 .
* For that reason it will be set to 0 if the argument is omitted .
* @ return Returns true if the specific finger is touching the touchpad .
*/
bool isTouching ( uint8_t finger = 0 , uint8_t xyId = 0 ) {
return ! ( ps4Data . xy [ xyId ] . finger [ finger ] . touching ) ; // The bit is cleared when a finger is touching the touchpad
2014-01-18 22:36:01 +01:00
} ;
2014-01-19 16:58:29 +01:00
/**
* This counter increments every time a finger touches the touchpad .
* @ param finger 0 = first finger , 1 = second finger . If omitted , then 0 will be used .
* @ param xyId The controller sends out three packets with the same structure .
* 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 .
* For that reason it will be set to 0 if the argument is omitted .
* @ return Return the value of the counter , note that it is only a 7 - bit value .
*/
2014-01-18 22:36:01 +01:00
uint8_t getTouchCounter ( uint8_t finger = 0 , uint8_t xyId = 0 ) {
return ps4Data . xy [ xyId ] . finger [ finger ] . counter ;
} ;
2014-01-19 16:58:29 +01:00
/**
* Get the angle of the controller calculated using the accelerometer .
* @ param a Either : : Pitch or : : Roll .
* @ return Return the angle in the range of 0 - 360.
*/
2014-01-18 22:36:01 +01:00
double getAngle ( AngleEnum a ) {
2014-02-17 00:50:02 +01:00
if ( a = = Pitch )
2014-01-18 22:36:01 +01:00
return ( atan2 ( ps4Data . accY , ps4Data . accZ ) + PI ) * RAD_TO_DEG ;
else
return ( atan2 ( ps4Data . accX , ps4Data . accZ ) + PI ) * RAD_TO_DEG ;
} ;
2014-01-19 16:58:29 +01:00
/**
* Used to get the raw values from the 3 - axis gyroscope and 3 - axis accelerometer inside the PS4 controller .
* @ param s The sensor to read .
* @ return Returns the raw sensor reading .
*/
2014-01-18 22:50:05 +01:00
int16_t getSensor ( SensorEnum s ) {
switch ( s ) {
2014-01-18 22:36:01 +01:00
case gX :
return ps4Data . gyroX ;
case gY :
return ps4Data . gyroY ;
case gZ :
return ps4Data . gyroZ ;
case aX :
return ps4Data . accX ;
case aY :
return ps4Data . accY ;
case aZ :
return ps4Data . accZ ;
default :
return 0 ;
}
} ;
2014-02-17 00:21:21 +01:00
2014-04-23 23:40:47 +02:00
/**
* Return the battery level of the PS4 controller .
* @ return The battery level in the range 0 - 15.
*/
uint8_t getBatteryLevel ( ) {
return ps4Data . status . battery ;
} ;
/**
* Use this to check if an USB cable is connected to the PS4 controller .
* @ return Returns true if an USB cable is connected .
*/
bool getUsbStatus ( ) {
return ps4Data . status . usb ;
} ;
/**
* Use this to check if an audio jack cable is connected to the PS4 controller .
* @ return Returns true if an audio jack cable is connected .
*/
bool getAudioStatus ( ) {
return ps4Data . status . audio ;
} ;
/**
* Use this to check if a microphone is connected to the PS4 controller .
* @ return Returns true if a microphone is connected .
*/
bool getMicStatus ( ) {
return ps4Data . status . mic ;
} ;
2014-02-17 00:21:21 +01:00
/** Turn both rumble and the LEDs off. */
void setAllOff ( ) {
setRumbleOff ( ) ;
setLedOff ( ) ;
} ;
/** Set rumble off. */
void setRumbleOff ( ) {
setRumbleOn ( 0 , 0 ) ;
} ;
/**
* Turn on rumble .
* @ param mode Either : : RumbleHigh or : : RumbleLow .
*/
void setRumbleOn ( RumbleEnum mode ) {
if ( mode = = RumbleLow )
setRumbleOn ( 0x00 , 0xFF ) ;
2014-02-17 17:39:15 +01:00
else
setRumbleOn ( 0xFF , 0x00 ) ;
2014-02-17 00:21:21 +01:00
} ;
/**
* Turn on rumble .
* @ param bigRumble Value for big motor .
* @ param smallRumble Value for small motor .
*/
void setRumbleOn ( uint8_t bigRumble , uint8_t smallRumble ) {
ps4Output . bigRumble = bigRumble ;
ps4Output . smallRumble = smallRumble ;
ps4Output . reportChanged = true ;
} ;
/** Turn all LEDs off. */
void setLedOff ( ) {
setLed ( 0 , 0 , 0 ) ;
} ;
/**
* Use this to set the color using RGB values .
* @ param r , g , b RGB value .
*/
void setLed ( uint8_t r , uint8_t g , uint8_t b ) {
ps4Output . r = r ;
ps4Output . g = g ;
ps4Output . b = b ;
ps4Output . reportChanged = true ;
} ;
/**
* Use this to set the color using the predefined colors in : : ColorsEnum .
* @ param color The desired color .
*/
void setLed ( ColorsEnum color ) {
setLed ( ( uint8_t ) ( color > > 16 ) , ( uint8_t ) ( color > > 8 ) , ( uint8_t ) ( color ) ) ;
} ;
/**
* Set the LEDs flash time .
* @ param flashOn Time to flash bright ( 255 = 2.5 seconds ) .
* @ param flashOff Time to flash dark ( 255 = 2.5 seconds ) .
*/
void setLedFlash ( uint8_t flashOn , uint8_t flashOff ) {
ps4Output . flashOn = flashOn ;
ps4Output . flashOff = flashOff ;
ps4Output . reportChanged = true ;
} ;
2014-01-18 22:36:01 +01:00
/**@}*/
2014-01-19 16:58:29 +01:00
protected :
/**
* Used to parse data sent from the PS4 controller .
* @ param len Length of the data .
* @ param buf Pointer to the data buffer .
*/
void Parse ( uint8_t len , uint8_t * buf ) ;
2014-01-18 22:36:01 +01:00
/** Used to reset the different buffers to their default values */
void Reset ( ) {
uint8_t i ;
for ( i = 0 ; i < sizeof ( ps4Data . hatValue ) ; i + + )
2014-02-17 01:25:29 +01:00
ps4Data . hatValue [ i ] = 127 ; // Center value
ps4Data . btn . val = 0 ;
oldButtonState . val = 0 ;
2014-01-18 22:36:01 +01:00
for ( i = 0 ; i < sizeof ( ps4Data . trigger ) ; i + + )
ps4Data . trigger [ i ] = 0 ;
for ( i = 0 ; i < sizeof ( ps4Data . xy ) / sizeof ( ps4Data . xy [ 0 ] ) ; i + + ) {
for ( uint8_t j = 0 ; j < sizeof ( ps4Data . xy [ 0 ] . finger ) / sizeof ( ps4Data . xy [ 0 ] . finger [ 0 ] ) ; j + + )
ps4Data . xy [ i ] . finger [ j ] . touching = 1 ; // The bit is cleared if the finger is touching the touchpad
}
ps4Data . btn . dpad = DPAD_OFF ;
oldButtonState . dpad = DPAD_OFF ;
buttonClickState . dpad = 0 ;
oldDpad = 0 ;
2014-02-17 00:21:21 +01:00
ps4Output . bigRumble = ps4Output . smallRumble = 0 ;
ps4Output . r = ps4Output . g = ps4Output . b = 0 ;
ps4Output . flashOn = ps4Output . flashOff = 0 ;
ps4Output . reportChanged = false ;
2014-01-18 22:36:01 +01:00
} ;
2014-02-17 00:50:02 +01:00
/**
* Send the output to the PS4 controller . This is implemented in PS4BT . h and PS4USB . h .
* @ param output Pointer to PS4Output buffer ;
*/
virtual void sendOutputReport ( PS4Output * output ) = 0 ;
2014-02-17 00:21:21 +01:00
2014-01-18 22:36:01 +01:00
private :
bool checkDpad ( ButtonEnum b ) ; // Used to check PS4 DPAD buttons
PS4Data ps4Data ;
PS4Buttons oldButtonState , buttonClickState ;
2014-02-17 00:21:21 +01:00
PS4Output ps4Output ;
2014-01-18 22:36:01 +01:00
uint8_t oldDpad ;
} ;
2015-03-12 22:27:21 +01:00
# endif