Fixed dual controller support
This commit is contained in:
parent
21f7f42cc9
commit
a4bce53d1e
13 changed files with 469 additions and 238 deletions
197
docs/Analog Stick Input.md
Normal file
197
docs/Analog Stick Input.md
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
# Analog Stick Input Information
|
||||
|
||||
**Disclaimer:** A chunk info within this document is sourced from the Switch reverse engineering
|
||||
effort at [DekuNukem's Repository](https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering).
|
||||
|
||||
The below sections contain info on the formulation and derivation of data
|
||||
pertaining to the Nintendo Switch's controllers. The section on the analog
|
||||
sticks contains info on encoding/decoding stick X/Y data, deadzones,
|
||||
maximum range, etc.
|
||||
|
||||
If you want to tweak or check out the full stick decode/encode script,
|
||||
please visit the *scripts/sticks.py* script.
|
||||
|
||||
## Analog Stick Information
|
||||
|
||||
Information on a controller's analog sticks is stored in three primary
|
||||
locations (user calibration excluded):
|
||||
|
||||
| Obtained From | Byte # | Data Type | Info |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Standard Input Report | 6-11 | 2 uint16 | Contains X/Y Data of Analog Sticks<sup>1</sup>
|
||||
| SPI Flash Read (Offset 0x6080) | 13-30 | 12 uint16 LE | Dead Zone, Range ratio |
|
||||
| SPI Flash Read (Offset 0x603D) | 7-24 | 12 uint16 LE | X/Y Min/Max and Centers |
|
||||
|
||||
<sup>1</sup> This data is relative, meaning that stick calibration data
|
||||
*must* be used to encode/decode X and Y positions.
|
||||
|
||||
## Decoding a Stick's Position
|
||||
|
||||
**Note:** The following configuration values are used within Nxbt.
|
||||
|
||||
First, we use the data obtained from the 0x603D SPI flash read to
|
||||
derive the right/left stick calibration parameters.
|
||||
|
||||
Sample data output by Nxbt:
|
||||
```
|
||||
Payload: 0xA1 0x21 0x2B 0x90 0x00 0x00 0x00 0x74 0x58 0x75 0x4B 0x68 0x7C 0x90
|
||||
0 1 2 3 4 5 6 7 8 9 10 11 12 13
|
||||
Subcommand: 0x90 0x10 0x3D 0x60 0x00 0x00 0x19 0xBA 0xF5 0x62 0x6F 0xC8 0x77 0xED
|
||||
14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
||||
0x95 0x5B 0x16 0xD8 0x7D 0xF2 0xB5 0x5F 0x86 0x65 0x5E 0xFF 0x82 0x82
|
||||
28 29 30 31 32 33 34 35
|
||||
0x82 0x0F 0x0F 0x0F 0x00 0x00 0x00 0x00
|
||||
```
|
||||
|
||||
Which gives us:
|
||||
|
||||
```
|
||||
Left Stick: 0xBA 0xF5 0x62 0x6F 0xC8 0x77 0xED 0x95 0x5B
|
||||
Right Stick: 0x16 0xD8 0x7D 0xF2 0xB5 0x5F 0x86 0x65 0x5E
|
||||
```
|
||||
|
||||
Using the following equations, we can decode these values into meaningful ones.
|
||||
Each stick's data is treated as an array of byte values for the equations.
|
||||
|
||||
```python
|
||||
# The nine stick bytes are labelled stick_cal[0] - stick_cal[8] here
|
||||
data = [0] * 6
|
||||
data[0] = (stick_cal[1] << 8) & 0xF00 | stick_cal[0];
|
||||
data[1] = (stick_cal[2] << 4) | (stick_cal[1] >> 4);
|
||||
data[2] = (stick_cal[4] << 8) & 0xF00 | stick_cal[3];
|
||||
data[3] = (stick_cal[5] << 4) | (stick_cal[4] >> 4);
|
||||
data[4] = (stick_cal[7] << 8) & 0xF00 | stick_cal[6];
|
||||
data[5] = (stick_cal[8] << 4) | (stick_cal[7] >> 4);
|
||||
|
||||
# Using the above data to create right stick data
|
||||
right_center_x = data[0];
|
||||
right_center_y = data[1];
|
||||
right_x_min = rstick_center_x - data[2];
|
||||
right_x_max = rstick_center_x + data[4];
|
||||
right_y_min = rstick_center_y - data[3];
|
||||
right_y_max = rstick_center_y + data[5];
|
||||
|
||||
# or left stick data
|
||||
left_center_x = data[2]
|
||||
left_center_y = data[3]
|
||||
left_x_min = left_center_x - data[0]
|
||||
left_x_max = left_center_x + data[4]
|
||||
left_y_min = left_center_y - data[1]
|
||||
left_y_max = left_center_y + data[5]
|
||||
```
|
||||
|
||||
Resulting in the following values for the sticks:
|
||||
|
||||
```
|
||||
Right Stick
|
||||
~~~~~~~~~~~
|
||||
Center X = 2070
|
||||
Center Y = 2013
|
||||
X Min = 548
|
||||
X Max = 3484
|
||||
Y Min = 482
|
||||
Y Max = 3523
|
||||
|
||||
Left Stick
|
||||
~~~~~~~~~~
|
||||
Center X = 2159
|
||||
Center Y = 1916
|
||||
X Min = 693
|
||||
X Max = 3676
|
||||
Y Min = 333
|
||||
Y Max = 3381
|
||||
```
|
||||
|
||||
Please note that the left stick calibration data is decoded slightly
|
||||
different than the right stick calibration data.
|
||||
|
||||
With the above calibration data, we can now decode a controller's
|
||||
reported stick position:
|
||||
|
||||
```python
|
||||
# Sample Stick Data Conversion:
|
||||
stick_data = [0xB3, 0x32, 0x6C]
|
||||
stick_horizontal = stick_data[0] | ((stick_data[1] & 0xF) << 8)
|
||||
stick_vertical = (stick_data[1] >> 4) | (stick_data[2] << 4)
|
||||
|
||||
print("Example Left Stick Data to Ratio Conversion:")
|
||||
print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
|
||||
print("Raw X/Y Uint16 Values:", stick_horizontal, stick_vertical)
|
||||
ratio_x = abs((stick_horizontal - left_center_x)) / (left_x_min - left_center_x)
|
||||
ratio_y = (stick_vertical - left_center_y) / (left_y_min - left_center_y)
|
||||
print("Relative X/Y Values", ratio_x, ratio_y)
|
||||
```
|
||||
|
||||
Which results in the ratios:
|
||||
|
||||
```
|
||||
Example Left Stick Data to Ratio Conversion:
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Raw X/Y Uint16 Values: 691 1731
|
||||
Relative X/Y Values -1.0013642564802183 0.11686670878079596
|
||||
```
|
||||
|
||||
We can see from the above data that the stick is being pushed left horizontally with
|
||||
very little vertical component.
|
||||
|
||||
## Converting Ratio-based Stick Position to a Calibrated Position
|
||||
|
||||
Given the stick calibration settings from the previous section,
|
||||
we can convert a given set of X/Y stick ratios to a calibrated set
|
||||
of values. This worked example will use the ratios defined before
|
||||
(-1.00136 X and 0.116866 Y).
|
||||
|
||||
First, we need to convert our given ratios to the numeric range
|
||||
defined by the calibration settings. Since we're using left stick ratios
|
||||
for our example, our X values range from 693 - 3676 and our Y values range
|
||||
from 333 - 3381. The following section of code demonstrates the math
|
||||
behind this conversion.
|
||||
|
||||
```python
|
||||
print("Example Left Stick Ratio to Data Conversion:")
|
||||
print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
|
||||
if ratio_x < 0:
|
||||
data_x_converted = (abs(ratio_x) * (left_x_min - left_center_x) + left_center_x)
|
||||
else:
|
||||
data_x_converted = (abs(ratio_x) * (left_x_max - left_center_x) + left_center_x)
|
||||
data_x_converted = int(round(data_x_converted))
|
||||
|
||||
if ratio_y < 0:
|
||||
data_y_converted = (abs(ratio_y) * (left_y_min - left_center_y) + left_center_y)
|
||||
else:
|
||||
data_y_converted = (abs(ratio_y) * (left_y_max - left_center_y) + left_center_y)
|
||||
data_y_converted = int(round(data_y_converted))
|
||||
|
||||
print("X/Y Converted Values:", data_x_converted, data_y_converted)
|
||||
```
|
||||
|
||||
Which results in:
|
||||
|
||||
```
|
||||
Example Left Stick Ratio to Data Conversion:
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
X/Y Converted Values: 691 1731
|
||||
```
|
||||
|
||||
Since the stick's X/Y position is broken up into 3 bytes in the standard input
|
||||
report, we need to split these uint16 values into 3 uint8 values. The following
|
||||
code accomplishes this split:
|
||||
|
||||
```python
|
||||
# Converting the two X/Y uint16 values to 3 uint8 Little Endian values
|
||||
converted_values = [
|
||||
# Get the last two hex digits
|
||||
hex(data_x_converted & 0xFF),
|
||||
# Combine the last digit of the Y uint16 and the first digit
|
||||
# of the X uint16
|
||||
hex(((data_y_converted & 0xF) << 4) + (data_x_converted >> 8)),
|
||||
# Get the first two digits of the Y uint16
|
||||
hex(data_y_converted >> 4)]
|
||||
print("Uint8 Converted Values:", converted_values)
|
||||
```
|
||||
|
||||
Which results bytes ready to be sent to the Switch:
|
||||
|
||||
```
|
||||
Uint8 Converted Values: ['0xb3', '0x32', '0x6c']
|
||||
```
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
# Analog Stick and Button Input Information
|
||||
|
||||
**Disclaimer:** The info within this document is sourced from the Switch reverse engineering
|
||||
effort at [DekuNukem's Repository](https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering).
|
||||
|
||||
The below sections contain info on the formulation and derivation of data
|
||||
pertaining to the Nintendo Switch's controllers. The section on the analog
|
||||
sticks contains info on encoding/decoding stick X/Y data, deadzones,
|
||||
maximum range, etc. The button info section contains info on how each
|
||||
button's state is communicated.
|
||||
|
||||
## Analog Stick Information
|
||||
|
||||
Information on a controller's analog sticks is stored in three primary
|
||||
locations (user calibration excluded):
|
||||
|
||||
| Obtained From | Byte # | Data Type | Info |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Standard Input Report | 6-11 | 2 uint16 | Contains X/Y Data of Analog Sticks<sup>1</sup>
|
||||
| SPI Flash Read (Offset 0x6080) | 13-30 | 12 uint16 LE | Dead Zone, Range ratio |
|
||||
| SPI Flash Read (Offset 0x603D) | 7-24 | 12 uint16 LE | X/Y Min/Max and Centers |
|
||||
|
||||
<sup>1</sup> This data is relative, meaning that stick calibration data
|
||||
*must* be used to encode/decode X and Y positions.
|
||||
|
||||
## Decoding a Stick's Position
|
||||
|
||||
**Note:** The following configuration values are used within Nxbt.
|
||||
|
||||
First, we use the data obtained from the 0x603D SPI flash read to
|
||||
derive the right/left stick calibration parameters.
|
||||
|
||||
Sample data output by Nxbt:
|
||||
```
|
||||
Payload: 0xA1 0x21 0x2B 0x90 0x00 0x00 0x00 0x74 0x58 0x75 0x4B 0x68 0x7C 0x90
|
||||
0 1 2 3 4 5 6 7 8 9 10 11 12 13
|
||||
Subcommand: 0x90 0x10 0x3D 0x60 0x00 0x00 0x19 0xBA 0xF5 0x62 0x6F 0xC8 0x77 0xED
|
||||
14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
||||
0x95 0x5B 0x16 0xD8 0x7D 0xF2 0xB5 0x5F 0x86 0x65 0x5E 0xFF 0x82 0x82
|
||||
28 29 30 31 32 33 34 35
|
||||
0x82 0x0F 0x0F 0x0F 0x00 0x00 0x00 0x00
|
||||
```
|
||||
|
||||
Which gives us:
|
||||
|
||||
```
|
||||
Left Stick: 0xBA 0xF5 0x62 0x6F 0xC8 0x77 0xED 0x95 0x5B
|
||||
Right Stick: 0x16 0xD8 0x7D 0xF2 0xB5 0x5F 0x86 0x65 0x5E
|
||||
```
|
||||
|
||||
Using the following equations, we can decode these values into meaningful ones.
|
||||
Each stick's data is treated as an array of byte values for the equations.
|
||||
|
||||
```
|
||||
# The nine stick bytes are labelled stick_cal[0] - stick_cal[8] here
|
||||
uint16_t data[6]
|
||||
data[0] = (stick_cal[1] << 8) & 0xF00 | stick_cal[0];
|
||||
data[1] = (stick_cal[2] << 4) | (stick_cal[1] >> 4);
|
||||
data[2] = (stick_cal[4] << 8) & 0xF00 | stick_cal[3];
|
||||
data[3] = (stick_cal[5] << 4) | (stick_cal[4] >> 4);
|
||||
data[4] = (stick_cal[7] << 8) & 0xF00 | stick_cal[6];
|
||||
data[5] = (stick_cal[8] << 4) | (stick_cal[7] >> 4);
|
||||
|
||||
# These values used as such in, for example, a right stick
|
||||
uint16_t rstick_center_x = data[0];
|
||||
uint16_t rstick_center_y = data[1];
|
||||
uint16_t rstick_x_min = rstick_center_x - data[2];
|
||||
uint16_t rstick_x_max = rstick_center_x + data[4];
|
||||
uint16_t rstick_y_min = rstick_center_y - data[3];
|
||||
uint16_t rstick_y_max = rstick_center_y + data[5];
|
||||
```
|
||||
|
||||
Resulting in the following values for the sticks:
|
||||
|
||||
```
|
||||
Left Stick
|
||||
~~~~~~~~~~
|
||||
Center X =
|
||||
Center Y =
|
||||
X Min =
|
||||
X Max =
|
||||
Y Min =
|
||||
Y Max =
|
||||
|
||||
Right Stick
|
||||
~~~~~~~~~~~
|
||||
Center X =
|
||||
Center Y =
|
||||
X Min =
|
||||
X Max =
|
||||
Y Min =
|
||||
Y Max =
|
||||
```
|
||||
|
|
@ -21,3 +21,22 @@ inquiry input report packet.
|
|||
|
||||
Eg: You could get away with emulating a Joy-Con (L) while having the
|
||||
Bluetooth alias set to "Pro Controller".
|
||||
|
||||
## Pro Controller Grip Colours
|
||||
|
||||
At the time of writing, grip colours are being read by the Switch, however,
|
||||
they aren't being used to display the controller graphic. Eg: If the left
|
||||
and right grip colours are set to white and the controller body is set to
|
||||
black, the grip colours will be black. This is likely because Nintendo hasn't
|
||||
produced any official Pro Controllers that feature a unique grip and body
|
||||
colour.
|
||||
|
||||
Currently, grip colours are hardcoded for the official, black Pro Controller.
|
||||
The black pro controller reports all white (or blank) grip colours, however,
|
||||
the Switch displays a slightly lighter grey when the icon is displayed. Any
|
||||
emulated controller can produce this grip colour if the body colour is set
|
||||
to #323232, the button colour set to #FFFFFF and the grip colours are set to
|
||||
#FFFFFF.
|
||||
|
||||
In the future, Nintendo may produce more Pro Controller colours, however,
|
||||
at this point in time, setting the grip colour is not possible.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue