2021-05-02 18:17:07 +02:00
|
|
|
/* Copyright (C) 2021 Kristian Sloth Lauszus. 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 Sloth Lauszus
|
|
|
|
Web : https://lauszus.com
|
|
|
|
e-mail : lauszus@gmail.com
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef _switch_pro_parser_h_
|
|
|
|
#define _switch_pro_parser_h_
|
|
|
|
|
|
|
|
#include "Usb.h"
|
|
|
|
#include "controllerEnums.h"
|
|
|
|
|
2021-05-02 21:47:17 +02:00
|
|
|
/** Used to set the LEDs on the controller */
|
|
|
|
const uint8_t SWITCH_PRO_LEDS[] PROGMEM = {
|
|
|
|
0x00, // OFF
|
|
|
|
0x01, // LED1
|
|
|
|
0x02, // LED2
|
|
|
|
0x04, // LED3
|
|
|
|
0x08, // LED4
|
|
|
|
|
|
|
|
0x09, // LED5
|
|
|
|
0x0A, // LED6
|
|
|
|
0x0C, // LED7
|
|
|
|
0x0D, // LED8
|
|
|
|
0x0E, // LED9
|
|
|
|
0x0F, // LED10
|
|
|
|
};
|
|
|
|
|
2021-05-02 18:17:07 +02:00
|
|
|
/** Buttons on the controller */
|
|
|
|
const uint8_t SWITCH_PRO_BUTTONS[] PROGMEM = {
|
2021-05-03 22:34:46 +02:00
|
|
|
0x11, // UP
|
|
|
|
0x12, // RIGHT
|
|
|
|
0x10, // DOWN
|
2021-05-02 18:17:07 +02:00
|
|
|
0x13, // LEFT
|
|
|
|
|
|
|
|
0x0D, // Capture
|
|
|
|
0x09, // PLUS
|
2021-05-03 22:34:46 +02:00
|
|
|
0x0B, // L3
|
|
|
|
0x0A, // R3
|
2021-05-02 18:17:07 +02:00
|
|
|
|
|
|
|
0x08, // MINUS
|
|
|
|
0x0C, // HOME
|
|
|
|
0, 0, // Skip
|
|
|
|
|
2021-05-03 22:34:46 +02:00
|
|
|
0x02, // B
|
|
|
|
0x03, // A
|
|
|
|
0x01, // X
|
|
|
|
0x00, // Y
|
2021-05-02 18:17:07 +02:00
|
|
|
|
2021-05-03 22:34:46 +02:00
|
|
|
0x16, // L
|
|
|
|
0x06, // R
|
|
|
|
0x17, // ZL
|
2021-05-02 18:17:07 +02:00
|
|
|
0x07, // ZR
|
|
|
|
};
|
|
|
|
|
2021-05-03 22:34:46 +02:00
|
|
|
// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/bluetooth_hid_notes.md#standard-input-report-format
|
2021-05-02 18:17:07 +02:00
|
|
|
union SwitchProButtons {
|
|
|
|
struct {
|
|
|
|
uint8_t y : 1;
|
|
|
|
uint8_t x : 1;
|
2021-05-03 22:34:46 +02:00
|
|
|
uint8_t b : 1;
|
|
|
|
uint8_t a : 1;
|
2021-05-02 18:17:07 +02:00
|
|
|
|
2021-05-03 22:34:46 +02:00
|
|
|
uint8_t dummy1 : 2;
|
2021-05-02 18:17:07 +02:00
|
|
|
uint8_t r : 1;
|
|
|
|
uint8_t zr : 1;
|
|
|
|
|
|
|
|
uint8_t minus : 1;
|
|
|
|
uint8_t plus : 1;
|
|
|
|
uint8_t r3 : 1;
|
2021-05-03 22:34:46 +02:00
|
|
|
uint8_t l3 : 1;
|
2021-05-02 18:17:07 +02:00
|
|
|
|
|
|
|
uint8_t home : 1;
|
|
|
|
uint8_t capture : 1;
|
2021-05-03 22:34:46 +02:00
|
|
|
uint8_t dummy2 : 2;
|
2021-05-02 18:17:07 +02:00
|
|
|
|
|
|
|
uint8_t dpad : 4;
|
2021-05-03 22:34:46 +02:00
|
|
|
|
|
|
|
uint8_t dummy3 : 2;
|
|
|
|
uint8_t l : 1;
|
|
|
|
uint8_t zl : 1;
|
2021-05-02 18:17:07 +02:00
|
|
|
} __attribute__((packed));
|
|
|
|
uint32_t val : 24;
|
|
|
|
} __attribute__((packed));
|
|
|
|
|
2021-05-03 22:34:46 +02:00
|
|
|
struct ImuData {
|
|
|
|
int16_t accX, accY, accZ;
|
|
|
|
int16_t gyroX, gyroY, gyroZ;
|
|
|
|
} __attribute__((packed));
|
|
|
|
|
2021-05-02 18:17:07 +02:00
|
|
|
struct SwitchProData {
|
2021-05-09 21:07:14 +02:00
|
|
|
struct {
|
|
|
|
uint8_t connection_info : 4;
|
|
|
|
uint8_t battery_level : 4;
|
|
|
|
} __attribute__((packed));
|
2021-05-03 22:34:46 +02:00
|
|
|
|
2021-05-02 18:17:07 +02:00
|
|
|
/* Button and joystick values */
|
2021-05-03 22:34:46 +02:00
|
|
|
SwitchProButtons btn; // Bytes 3-5
|
|
|
|
|
|
|
|
// Bytes 6-11
|
|
|
|
uint16_t leftHatX : 12;
|
|
|
|
uint16_t leftHatY : 12;
|
|
|
|
uint16_t rightHatX : 12;
|
|
|
|
uint16_t rightHatY : 12;
|
|
|
|
|
|
|
|
uint8_t vibratorInput; // What is this used for?
|
|
|
|
|
|
|
|
// Bytes 13-48
|
|
|
|
// Three samples of the IMU is sent in one message
|
|
|
|
// See: https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/imu_sensor_notes.md
|
|
|
|
ImuData imu[3];
|
2021-05-02 18:17:07 +02:00
|
|
|
} __attribute__((packed));
|
|
|
|
|
|
|
|
struct SwitchProOutput {
|
2021-05-02 21:47:17 +02:00
|
|
|
bool leftRumbleOn;
|
|
|
|
bool rightRumbleOn;
|
2021-05-03 22:34:46 +02:00
|
|
|
uint8_t ledMask; // Higher nibble flashes the LEDs, lower nibble sets them on/off
|
2021-05-02 21:47:17 +02:00
|
|
|
bool ledHome;
|
|
|
|
|
2021-05-03 22:34:46 +02:00
|
|
|
// Used to send the reports at the same rate as the controller is sending messages
|
2021-05-02 21:47:17 +02:00
|
|
|
bool ledReportChanged;
|
|
|
|
bool ledHomeReportChanged;
|
2021-05-03 22:34:46 +02:00
|
|
|
bool enableFullReportMode;
|
|
|
|
int8_t enableImu; // -1 == Do nothing, 0 == disable IMU, 1 == enable IMU
|
|
|
|
bool sendHandshake;
|
|
|
|
bool disableTimeout;
|
2021-05-02 18:17:07 +02:00
|
|
|
} __attribute__((packed));
|
|
|
|
|
|
|
|
/** This class parses all the data sent by the Switch Pro controller */
|
|
|
|
class SwitchProParser {
|
|
|
|
public:
|
|
|
|
/** Constructor for the SwitchProParser class. */
|
2021-05-02 21:47:17 +02:00
|
|
|
SwitchProParser() : output_sequence_counter(0) {
|
2021-05-02 18:17:07 +02:00
|
|
|
Reset();
|
|
|
|
};
|
|
|
|
|
|
|
|
/** @name Switch Pro 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 Switch Pro Controller functions */
|
|
|
|
/**
|
|
|
|
* Used to read the analog joystick.
|
|
|
|
* @param a ::LeftHatX, ::LeftHatY, ::RightHatX, and ::RightHatY.
|
2021-05-02 21:47:17 +02:00
|
|
|
* @return Return the analog value as a signed 16-bit value.
|
2021-05-02 18:17:07 +02:00
|
|
|
*/
|
|
|
|
int16_t getAnalogHat(AnalogHatEnum a);
|
|
|
|
|
2021-05-03 22:34:46 +02:00
|
|
|
/**
|
|
|
|
* Used to enable/disable the IMU. By default it is disabled.
|
|
|
|
* @param enable Enable/disable the IMU.
|
|
|
|
*/
|
|
|
|
void enableImu(bool enable) {
|
|
|
|
// TODO: Should we just always enable it?
|
|
|
|
switchProOutput.enableImu = enable ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
2021-05-02 18:17:07 +02: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.
|
|
|
|
*/
|
|
|
|
float getAngle(AngleEnum a) {
|
|
|
|
if (a == Pitch)
|
2021-05-03 22:34:46 +02:00
|
|
|
return (atan2f(-switchProData.imu[0].accY, -switchProData.imu[0].accZ) + PI) * RAD_TO_DEG;
|
2021-05-02 18:17:07 +02:00
|
|
|
else
|
2021-05-03 22:34:46 +02:00
|
|
|
return (atan2f(switchProData.imu[0].accX, -switchProData.imu[0].accZ) + PI) * RAD_TO_DEG;
|
2021-05-02 18:17:07 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Used to get the raw values from the 3-axis gyroscope and 3-axis accelerometer inside the PS5 controller.
|
|
|
|
* @param s The sensor to read.
|
|
|
|
* @return Returns the raw sensor reading.
|
|
|
|
*/
|
|
|
|
int16_t getSensor(SensorEnum s) {
|
|
|
|
switch(s) {
|
|
|
|
case gX:
|
2021-05-03 22:34:46 +02:00
|
|
|
return switchProData.imu[0].gyroX;
|
2021-05-02 18:17:07 +02:00
|
|
|
case gY:
|
2021-05-03 22:34:46 +02:00
|
|
|
return switchProData.imu[0].gyroY;
|
2021-05-02 18:17:07 +02:00
|
|
|
case gZ:
|
2021-05-03 22:34:46 +02:00
|
|
|
return switchProData.imu[0].gyroZ;
|
2021-05-02 18:17:07 +02:00
|
|
|
case aX:
|
2021-05-03 22:34:46 +02:00
|
|
|
return switchProData.imu[0].accX;
|
2021-05-02 18:17:07 +02:00
|
|
|
case aY:
|
2021-05-03 22:34:46 +02:00
|
|
|
return switchProData.imu[0].accY;
|
2021-05-02 18:17:07 +02:00
|
|
|
case aZ:
|
2021-05-03 22:34:46 +02:00
|
|
|
return switchProData.imu[0].accZ;
|
2021-05-02 18:17:07 +02:00
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Turn both rumble and the LEDs off. */
|
|
|
|
void setAllOff() {
|
|
|
|
setRumbleOff();
|
|
|
|
setLedOff();
|
2021-05-02 21:47:17 +02:00
|
|
|
setLedHomeOff();
|
2021-05-02 18:17:07 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/** Set rumble off. */
|
|
|
|
void setRumbleOff() {
|
2021-05-02 22:34:49 +02:00
|
|
|
setRumble(false, false);
|
2021-05-02 18:17:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Toggle rumble. */
|
|
|
|
void setRumbleToggle() {
|
2021-05-02 22:34:49 +02:00
|
|
|
setRumble(!switchProOutput.leftRumbleOn, !switchProOutput.rightRumbleOn);
|
2021-05-02 18:17:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-05-02 22:34:49 +02:00
|
|
|
* Turn on/off rumble.
|
|
|
|
* @param leftRumbleOn Turn on/off left rumble motor.
|
|
|
|
* @param rightRumbleOn Turn on/off right rumble motor.
|
2021-05-02 18:17:07 +02:00
|
|
|
*/
|
2021-05-02 22:34:49 +02:00
|
|
|
void setRumble(bool leftRumbleOn, bool rightRumbleOn) {
|
2021-05-02 21:47:17 +02:00
|
|
|
switchProOutput.leftRumbleOn = leftRumbleOn;
|
|
|
|
switchProOutput.rightRumbleOn = rightRumbleOn;
|
|
|
|
switchProOutput.ledReportChanged = true; // Set this, so the rumble effect gets changed immediately
|
2021-05-02 18:17:07 +02:00
|
|
|
}
|
|
|
|
|
2021-05-02 22:34:49 +02:00
|
|
|
/**
|
|
|
|
* Turn on/off the left rumble.
|
|
|
|
* @param on Turn on/off left rumble motor.
|
|
|
|
*/
|
|
|
|
void setRumbleLeft(bool on) {
|
|
|
|
switchProOutput.leftRumbleOn = on;
|
|
|
|
switchProOutput.ledReportChanged = true; // Set this, so the rumble effect gets changed immediately
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Turn on/off the right rumble.
|
|
|
|
* @param on Turn on/off right rumble motor.
|
|
|
|
*/
|
|
|
|
void setRumbleRight(bool on) {
|
|
|
|
switchProOutput.rightRumbleOn = on;
|
|
|
|
switchProOutput.ledReportChanged = true; // Set this, so the rumble effect gets changed immediately
|
|
|
|
}
|
|
|
|
|
2021-05-02 18:17:07 +02:00
|
|
|
/**
|
|
|
|
* Set LED value without using the ::LEDEnum.
|
2021-05-02 21:47:17 +02:00
|
|
|
* This can also be used to flash the LEDs by setting the high 4-bits of the mask.
|
2021-05-02 18:17:07 +02:00
|
|
|
* @param value See: ::LEDEnum.
|
|
|
|
*/
|
2021-05-02 21:47:17 +02:00
|
|
|
void setLedRaw(uint8_t mask) {
|
|
|
|
switchProOutput.ledMask = mask;
|
|
|
|
switchProOutput.ledReportChanged = true;
|
2021-05-02 18:17:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Turn all LEDs off. */
|
|
|
|
void setLedOff() {
|
|
|
|
setLedRaw(0);
|
|
|
|
}
|
2021-05-02 21:47:17 +02:00
|
|
|
|
2021-05-02 18:17:07 +02:00
|
|
|
/**
|
|
|
|
* Turn the specific ::LEDEnum off.
|
|
|
|
* @param a The ::LEDEnum to turn off.
|
|
|
|
*/
|
|
|
|
void setLedOff(LEDEnum a) {
|
2021-05-02 21:47:17 +02:00
|
|
|
switchProOutput.ledMask &= ~((uint8_t)(pgm_read_byte(&SWITCH_PRO_LEDS[(uint8_t)a]) & 0x0f));
|
|
|
|
switchProOutput.ledReportChanged = true;
|
2021-05-02 18:17:07 +02:00
|
|
|
}
|
2021-05-02 21:47:17 +02:00
|
|
|
|
2021-05-02 18:17:07 +02:00
|
|
|
/**
|
|
|
|
* Turn the specific ::LEDEnum on.
|
|
|
|
* @param a The ::LEDEnum to turn on.
|
|
|
|
*/
|
|
|
|
void setLedOn(LEDEnum a) {
|
2021-05-02 21:47:17 +02:00
|
|
|
switchProOutput.ledMask |= (uint8_t)(pgm_read_byte(&SWITCH_PRO_LEDS[(uint8_t)a]) & 0x0f);
|
|
|
|
switchProOutput.ledReportChanged = true;
|
2021-05-02 18:17:07 +02:00
|
|
|
}
|
2021-05-02 21:47:17 +02:00
|
|
|
|
2021-05-02 18:17:07 +02:00
|
|
|
/**
|
|
|
|
* Toggle the specific ::LEDEnum.
|
|
|
|
* @param a The ::LEDEnum to toggle.
|
|
|
|
*/
|
|
|
|
void setLedToggle(LEDEnum a) {
|
2021-05-02 21:47:17 +02:00
|
|
|
switchProOutput.ledMask ^= (uint8_t)(pgm_read_byte(&SWITCH_PRO_LEDS[(uint8_t)a]) & 0x0f);
|
|
|
|
switchProOutput.ledReportChanged = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Turn home LED off. */
|
|
|
|
void setLedHomeOff() {
|
|
|
|
switchProOutput.ledHome = false;
|
|
|
|
switchProOutput.ledHomeReportChanged = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Turn home LED on. */
|
|
|
|
void setLedHomeOn() {
|
|
|
|
switchProOutput.ledHome = true;
|
|
|
|
switchProOutput.ledHomeReportChanged = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Toggle home LED. */
|
|
|
|
void setLedHomeToggle() {
|
|
|
|
switchProOutput.ledHome = !switchProOutput.ledHome;
|
|
|
|
switchProOutput.ledHomeReportChanged = true;
|
2021-05-02 18:17:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Get the incoming message count. */
|
2021-05-09 21:07:14 +02:00
|
|
|
uint16_t getMessageCounter() {
|
2021-05-02 18:17:07 +02:00
|
|
|
return message_counter;
|
|
|
|
}
|
|
|
|
|
2021-05-09 21:07:14 +02:00
|
|
|
/**
|
|
|
|
* Return the battery level of the Switch Pro Controller.
|
|
|
|
* @return The battery level as a bit mask according to ::SwitchProBatteryLevel:
|
|
|
|
* 4=full, 3=medium, 2=low, 1=critical, 0=empty.
|
|
|
|
*/
|
|
|
|
uint8_t getBatteryLevel() {
|
|
|
|
return switchProData.battery_level >> 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns whenever the controller is plugged in and charging.
|
|
|
|
* @return Returns True if the controller is charging.
|
|
|
|
*/
|
|
|
|
bool isCharging() {
|
|
|
|
return switchProData.battery_level & 0x01;
|
|
|
|
}
|
|
|
|
|
2021-05-02 18:17:07 +02:00
|
|
|
protected:
|
|
|
|
/**
|
|
|
|
* Used to parse data sent from the Switch Pro 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();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Send the output to the Switch Pro controller. This is implemented in SwitchProBT.h and SwitchProUSB.h.
|
2021-05-02 21:47:17 +02:00
|
|
|
* @param data Pointer to buffer to send by the derived class.
|
|
|
|
* @param len Length of buffer.
|
2021-05-02 18:17:07 +02:00
|
|
|
*/
|
2021-05-02 21:47:17 +02:00
|
|
|
virtual void sendOutputReport(uint8_t *data, uint8_t len) = 0;
|
2021-05-02 18:17:07 +02:00
|
|
|
|
2021-05-03 22:34:46 +02:00
|
|
|
/** Used to send a handshake command via USB before disabling the timeout. */
|
|
|
|
virtual void sendHandshake() {}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Needed to disable USB timeout for the controller,
|
|
|
|
* so it sends out data without the host having to send data continuously.
|
|
|
|
*/
|
|
|
|
virtual void disableTimeout() {}
|
|
|
|
|
|
|
|
/** Allow derived classes to access the output variables. */
|
|
|
|
SwitchProOutput switchProOutput;
|
|
|
|
|
2021-05-02 18:17:07 +02:00
|
|
|
private:
|
|
|
|
static int8_t getButtonIndexSwitchPro(ButtonEnum b);
|
|
|
|
|
2021-05-03 22:34:46 +02:00
|
|
|
void sendOutputCmd();
|
2021-05-02 21:47:17 +02:00
|
|
|
void sendRumbleOutputReport();
|
|
|
|
|
2021-05-02 18:17:07 +02:00
|
|
|
SwitchProData switchProData;
|
|
|
|
SwitchProButtons oldButtonState, buttonClickState;
|
|
|
|
uint16_t message_counter = 0;
|
2021-05-02 21:47:17 +02:00
|
|
|
uint8_t output_sequence_counter : 4;
|
|
|
|
uint32_t rumble_on_timer = 0;
|
2021-05-02 18:17:07 +02:00
|
|
|
};
|
|
|
|
#endif
|