408 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			408 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* 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_
 | 
						|
 | 
						|
#include "Usb.h"
 | 
						|
#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;
 | 
						|
                uint8_t reportCounter : 6;
 | 
						|
        } __attribute__((packed));
 | 
						|
        uint32_t val : 24;
 | 
						|
} __attribute__((packed));
 | 
						|
 | 
						|
struct touchpadXY {
 | 
						|
        uint8_t dummy; // I can not figure out what this data is for, it seems to change randomly, maybe a timestamp?
 | 
						|
        struct {
 | 
						|
                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;
 | 
						|
        } __attribute__((packed)) finger[2]; // 0 = first finger, 1 = second finger
 | 
						|
} __attribute__((packed));
 | 
						|
 | 
						|
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));
 | 
						|
 | 
						|
struct PS4Data {
 | 
						|
        /* Button and joystick values */
 | 
						|
        uint8_t hatValue[4];
 | 
						|
        PS4Buttons btn;
 | 
						|
        uint8_t trigger[2];
 | 
						|
 | 
						|
        /* Gyro and accelerometer values */
 | 
						|
        uint8_t dummy[3]; // First two looks random, while the third one might be some kind of status - it increments once in a while
 | 
						|
        int16_t gyroY, gyroZ, gyroX;
 | 
						|
        int16_t accX, accZ, accY;
 | 
						|
 | 
						|
        uint8_t dummy2[5];
 | 
						|
        PS4Status status;
 | 
						|
        uint8_t dummy3[3];
 | 
						|
 | 
						|
        /* The rest is data for the touchpad */
 | 
						|
        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.
 | 
						|
                          // 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
 | 
						|
} __attribute__((packed));
 | 
						|
 | 
						|
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));
 | 
						|
 | 
						|
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.
 | 
						|
         * @param  b The ::ButtonEnum to read.
 | 
						|
         * 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.
 | 
						|
         */
 | 
						|
        uint8_t getAnalogButton(ButtonEnum b);
 | 
						|
 | 
						|
        /**
 | 
						|
         * 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);
 | 
						|
 | 
						|
        /**
 | 
						|
         * 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.
 | 
						|
         */
 | 
						|
        uint16_t getX(uint8_t finger = 0, uint8_t xyId = 0) {
 | 
						|
                return ps4Data.xy[xyId].finger[finger].x;
 | 
						|
        };
 | 
						|
 | 
						|
        /**
 | 
						|
         * 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.
 | 
						|
         */
 | 
						|
        uint16_t getY(uint8_t finger = 0, uint8_t xyId = 0) {
 | 
						|
                return ps4Data.xy[xyId].finger[finger].y;
 | 
						|
        };
 | 
						|
 | 
						|
        /**
 | 
						|
         * 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
 | 
						|
        };
 | 
						|
 | 
						|
        /**
 | 
						|
         * 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.
 | 
						|
         */
 | 
						|
        uint8_t getTouchCounter(uint8_t finger = 0, uint8_t xyId = 0) {
 | 
						|
                return ps4Data.xy[xyId].finger[finger].counter;
 | 
						|
        };
 | 
						|
 | 
						|
        /**
 | 
						|
         * 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.
 | 
						|
         */
 | 
						|
        double getAngle(AngleEnum a) {
 | 
						|
                if (a == Pitch)
 | 
						|
                        return (atan2(ps4Data.accY, ps4Data.accZ) + PI) * RAD_TO_DEG;
 | 
						|
                else
 | 
						|
                        return (atan2(ps4Data.accX, ps4Data.accZ) + PI) * RAD_TO_DEG;
 | 
						|
        };
 | 
						|
 | 
						|
        /**
 | 
						|
         * 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.
 | 
						|
         */
 | 
						|
        int16_t getSensor(SensorEnum s) {
 | 
						|
                switch(s) {
 | 
						|
                        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;
 | 
						|
                }
 | 
						|
        };
 | 
						|
 | 
						|
        /**
 | 
						|
         * 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;
 | 
						|
        };
 | 
						|
 | 
						|
        /** 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);
 | 
						|
                else
 | 
						|
                        setRumbleOn(0xFF, 0x00);
 | 
						|
        };
 | 
						|
 | 
						|
        /**
 | 
						|
         * 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;
 | 
						|
        };
 | 
						|
        /**@}*/
 | 
						|
 | 
						|
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);
 | 
						|
 | 
						|
        /** Used to reset the different buffers to their default values */
 | 
						|
        void Reset() {
 | 
						|
                uint8_t i;
 | 
						|
                for (i = 0; i < sizeof(ps4Data.hatValue); i++)
 | 
						|
                        ps4Data.hatValue[i] = 127; // Center value
 | 
						|
                ps4Data.btn.val = 0;
 | 
						|
                oldButtonState.val = 0;
 | 
						|
                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;
 | 
						|
 | 
						|
                ps4Output.bigRumble = ps4Output.smallRumble = 0;
 | 
						|
                ps4Output.r = ps4Output.g = ps4Output.b = 0;
 | 
						|
                ps4Output.flashOn = ps4Output.flashOff = 0;
 | 
						|
                ps4Output.reportChanged = false;
 | 
						|
        };
 | 
						|
 | 
						|
        /**
 | 
						|
         * 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;
 | 
						|
 | 
						|
private:
 | 
						|
        bool checkDpad(ButtonEnum b); // Used to check PS4 DPAD buttons
 | 
						|
 | 
						|
        PS4Data ps4Data;
 | 
						|
        PS4Buttons oldButtonState, buttonClickState;
 | 
						|
        PS4Output ps4Output;
 | 
						|
        uint8_t oldDpad;
 | 
						|
};
 | 
						|
#endif
 |