-
Notifications
You must be signed in to change notification settings - Fork 786
/
SwitchProParser.h
385 lines (328 loc) · 12.6 KB
/
SwitchProParser.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
/* 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 : [email protected]
*/
#ifndef _switch_pro_parser_h_
#define _switch_pro_parser_h_
#include "Usb.h"
#include "controllerEnums.h"
/** 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
};
/** Buttons on the controller */
const uint8_t SWITCH_PRO_BUTTONS[] PROGMEM = {
0x11, // UP
0x12, // RIGHT
0x10, // DOWN
0x13, // LEFT
0x0D, // Capture
0x09, // PLUS
0x0B, // L3
0x0A, // R3
0x08, // MINUS
0x0C, // HOME
0, 0, // Skip
0x02, // B
0x03, // A
0x01, // X
0x00, // Y
0x16, // L
0x06, // R
0x17, // ZL
0x07, // ZR
};
// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/bluetooth_hid_notes.md#standard-input-report-format
union SwitchProButtons {
struct {
uint8_t y : 1;
uint8_t x : 1;
uint8_t b : 1;
uint8_t a : 1;
uint8_t dummy1 : 2;
uint8_t r : 1;
uint8_t zr : 1;
uint8_t minus : 1;
uint8_t plus : 1;
uint8_t r3 : 1;
uint8_t l3 : 1;
uint8_t home : 1;
uint8_t capture : 1;
uint8_t dummy2 : 2;
uint8_t dpad : 4;
uint8_t dummy3 : 2;
uint8_t l : 1;
uint8_t zl : 1;
} __attribute__((packed));
uint32_t val : 24;
} __attribute__((packed));
struct ImuData {
int16_t accX, accY, accZ;
int16_t gyroX, gyroY, gyroZ;
} __attribute__((packed));
struct SwitchProData {
struct {
uint8_t connection_info : 4;
uint8_t battery_level : 4;
} __attribute__((packed));
/* Button and joystick values */
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];
} __attribute__((packed));
struct SwitchProOutput {
bool leftRumbleOn;
bool rightRumbleOn;
uint8_t ledMask; // Higher nibble flashes the LEDs, lower nibble sets them on/off
bool ledHome;
// Used to send the reports at the same rate as the controller is sending messages
bool ledReportChanged;
bool ledHomeReportChanged;
bool enableFullReportMode;
int8_t enableImu; // -1 == Do nothing, 0 == disable IMU, 1 == enable IMU
bool sendHandshake;
bool disableTimeout;
} __attribute__((packed));
/** This class parses all the data sent by the Switch Pro controller */
class SwitchProParser {
public:
/** Constructor for the SwitchProParser class. */
SwitchProParser() : output_sequence_counter(0) {
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.
* @return Return the analog value as a signed 16-bit value.
*/
int16_t getAnalogHat(AnalogHatEnum a);
/**
* 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;
}
/**
* 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)
return (atan2f(-switchProData.imu[0].accY, -switchProData.imu[0].accZ) + PI) * RAD_TO_DEG;
else
return (atan2f(switchProData.imu[0].accX, -switchProData.imu[0].accZ) + PI) * RAD_TO_DEG;
};
/**
* 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:
return switchProData.imu[0].gyroX;
case gY:
return switchProData.imu[0].gyroY;
case gZ:
return switchProData.imu[0].gyroZ;
case aX:
return switchProData.imu[0].accX;
case aY:
return switchProData.imu[0].accY;
case aZ:
return switchProData.imu[0].accZ;
default:
return 0;
}
};
/** Turn both rumble and the LEDs off. */
void setAllOff() {
setRumbleOff();
setLedOff();
setLedHomeOff();
};
/** Set rumble off. */
void setRumbleOff() {
setRumble(false, false);
}
/** Toggle rumble. */
void setRumbleToggle() {
setRumble(!switchProOutput.leftRumbleOn, !switchProOutput.rightRumbleOn);
}
/**
* Turn on/off rumble.
* @param leftRumbleOn Turn on/off left rumble motor.
* @param rightRumbleOn Turn on/off right rumble motor.
*/
void setRumble(bool leftRumbleOn, bool rightRumbleOn) {
switchProOutput.leftRumbleOn = leftRumbleOn;
switchProOutput.rightRumbleOn = rightRumbleOn;
switchProOutput.ledReportChanged = true; // Set this, so the rumble effect gets changed immediately
}
/**
* 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
}
/**
* Set LED value without using the ::LEDEnum.
* This can also be used to flash the LEDs by setting the high 4-bits of the mask.
* @param value See: ::LEDEnum.
*/
void setLedRaw(uint8_t mask) {
switchProOutput.ledMask = mask;
switchProOutput.ledReportChanged = true;
}
/** Turn all LEDs off. */
void setLedOff() {
setLedRaw(0);
}
/**
* Turn the specific ::LEDEnum off.
* @param a The ::LEDEnum to turn off.
*/
void setLedOff(LEDEnum a) {
switchProOutput.ledMask &= ~((uint8_t)(pgm_read_byte(&SWITCH_PRO_LEDS[(uint8_t)a]) & 0x0f));
switchProOutput.ledReportChanged = true;
}
/**
* Turn the specific ::LEDEnum on.
* @param a The ::LEDEnum to turn on.
*/
void setLedOn(LEDEnum a) {
switchProOutput.ledMask |= (uint8_t)(pgm_read_byte(&SWITCH_PRO_LEDS[(uint8_t)a]) & 0x0f);
switchProOutput.ledReportChanged = true;
}
/**
* Toggle the specific ::LEDEnum.
* @param a The ::LEDEnum to toggle.
*/
void setLedToggle(LEDEnum a) {
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;
}
/** Get the incoming message count. */
uint16_t getMessageCounter() {
return message_counter;
}
/**
* 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;
}
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.
* @param data Pointer to buffer to send by the derived class.
* @param len Length of buffer.
*/
virtual void sendOutputReport(uint8_t *data, uint8_t len) = 0;
/** 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;
private:
static int8_t getButtonIndexSwitchPro(ButtonEnum b);
void sendOutputCmd();
void sendRumbleOutputReport();
SwitchProData switchProData;
SwitchProButtons oldButtonState, buttonClickState;
uint16_t message_counter = 0;
uint8_t output_sequence_counter : 4;
uint32_t rumble_on_timer = 0;
};
#endif