Joy-Con: Difference between revisions

m Colors: ...just removed the KATAKANA suffix of the word "edition" to align with other Japanese titles (...as "edition" was also omitted in Japanese from all other previous titles)
These were swapped
 
(20 intermediate revisions by 3 users not shown)
Line 1: Line 1:
Joy-Con is the name for the Switch's primary game controllers. The controllers communicate to the console through bluetooth and can be disconnected from the main unit.
Joy-Con is the name for the Switch's primary game controllers. The Joy-Con are internally called Sakyo (Left) and Ukyo (Right).


= Hardware =
= Hardware =
The controllers themselves are simple to disassemble and identify.
== Sakyo ==
[[File:JoyconFront.jpg|400px|thumb|The front of the Joy-Con internal[https://www.ifixit.com/Teardown/Nintendo+Switch+Teardown/78263]]]
[[File:JoyconRear.jpg|400px|thumb|The rear of the Joy-Con internal[https://www.ifixit.com/Teardown/Nintendo+Switch+Teardown/78263]]]
{| class="wikitable"
{| class="wikitable"
! Color || Item
! Component || Description
|-
|-
| Red || Broadcom BCM20734 Bluetooth 4.1/2.4 GHz Transceiver
| SoC || Broadcom BCM20734
|-
|-
| Orange || STMicroelectronics NFCBEA 812006 33 (Likely NFC reader IC)
| IMU || STMicroelectronics LSM6DS3H
|-
|-
| Yellow|| Macronix International MX25U4033E 4 Mb CMOS Flash
| PMIC || Texas Instruments BQ24072
|-
| Storage || Macronix International MX25U4033E 4 Mb CMOS Flash
|}
|}


= Firmware =
== Ukyo ==
''See also: [[Joy-Con Firmware]]''
 
The firmware of the Joy-Cons seems to be generic across all instances. Details such as the handedness of the controller and the color is just indicated in a configuration part of the flash. Judging by quick glance at the firmware, it's probably identical to that used on the Pro Controller as well.
{| class="wikitable"
{| class="wikitable"
! Controller || 1.0.0
! Component || Description
|-
| SoC || Broadcom BCM20734
|-
|-
| Joy-Con Left (Blue) || [[:File:pad.L.bin|flash dump]]
| MCU || STMicroelectronics STM32P411
|-
|-
| Joy-Con Right (Red) || [[:File:pad.R.bin|flash dump]]
| NFC || STMicroelectronics ST21NFCB
|-
| IMU || STMicroelectronics LSM6DS3H
|-
| PMIC || Texas Instruments BQ24072
|-
| Storage || Macronix International MX25U4033E 4 Mb CMOS Flash
|}
|}
The flash is in a patch-ram format. Tools for dealing with it are available [https://github.com/shuffle2/nxpad].
A dump of the ROM region (taken while firmware was running) can be found [[:File:bcm20734_rom.bin|here]]. Note this is the same SoC used on the other controller models, as well.


= Protocol =
= Protocol =
The Joy-Con can communicate wirelessly over Bluetooth or through the rails over UART. This uses a Nintendo proprietary protocol called "Nwcp".  
The Joy-Con can communicate wirelessly over Bluetooth or through the rails over UART. The latter uses a Nintendo proprietary protocol called "Nwcp".  


== NwcpPacket ==
== NwcpPacket ==
Line 126: Line 128:
! Description
! Description
|-
|-
| 0x91 || DeviceCommandToDevice
| 0x43 || ReportOut
|-
| 0x53 || ReportIn
|-
| 0x91 || DeviceCommandIn
|-
|-
| 0x92 || Hid
| 0x92 || Hid
|-
|-
| 0x94 || DeviceCommandFromDevice
| 0x93 ||
|-
| 0x94 || DeviceCommandOut
|-
| 0x95 ||
|-
|-
| 0x9A ||  
| 0x9A || Fifty
|-
|-
| 0xA3 ||  
| 0xA3 || ReportComplete
|-
|-
| 0xA5 || Handshake
| 0xA5 || Handshake
|}
== HidCommandInputReport ==
This is a 0x31-byte struct.
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x1
| [[#HidReportId|ReportId]]
|-
| 0x1
| 0x1
| LatencyTimer
|-
| 0x2
| 0x1
| PowerInfo
|-
| 0x3
| 0x1
| RightPadButtonStatus
|-
| 0x4
| 0x1
| SharedButtonStatus
|-
| 0x5
| 0x1
| LeftPadButtonStatus
|-
| 0x6
| 0x3
| LeftAnalogStickData
|-
| 0x9
| 0x3
| RightAnalogStickData
|-
| 0xC
| 0x1
| MotorStatus
|-
| 0xD
| 0x1
| CommandAck
|-
| 0xE
| 0x1
| [[#HidCommand|CommandId]]
|-
| 0xF
| 0x22
| CommandReply
|}
== HidMcuUpdateInputReport ==
This is a 0x30-byte struct.
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x1
| [[#HidReportId|ReportId]]
|-
| 0x1
| 0x1
| LatencyTimer
|-
| 0x2
| 0x1
| PowerInfo
|-
| 0x3
| 0x1
| RightPadButtonStatus
|-
| 0x4
| 0x1
| SharedButtonStatus
|-
| 0x5
| 0x1
| LeftPadButtonStatus
|-
| 0x6
| 0x3
| LeftAnalogStickData
|-
| 0x9
| 0x3
| RightAnalogStickData
|-
| 0xC
| 0x1
| MotorStatus
|-
| 0xD
| 0x23
| McuUpdateData
|}
== HidBasicInputReport ==
This is a 0x31-byte struct.
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x1
| [[#HidReportId|ReportId]]
|-
| 0x1
| 0x1
| LatencyTimer
|-
| 0x2
| 0x1
| PowerInfo
|-
| 0x3
| 0x1
| RightPadButtonStatus
|-
| 0x4
| 0x1
| SharedButtonStatus
|-
| 0x5
| 0x1
| LeftPadButtonStatus
|-
| 0x6
| 0x3
| LeftAnalogStickData
|-
| 0x9
| 0x3
| RightAnalogStickData
|-
| 0xC
| 0x1
| MotorStatus
|-
| 0xD
| 0x24
| BasicData
|}
== HidMcuInputReport ==
This is a 0x16A-byte struct.
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x1
| [[#HidReportId|ReportId]]
|-
| 0x1
| 0x1
| LatencyTimer
|-
| 0x2
| 0x1
| PowerInfo
|-
| 0x3
| 0x1
| RightPadButtonStatus
|-
| 0x4
| 0x1
| SharedButtonStatus
|-
| 0x5
| 0x1
| LeftPadButtonStatus
|-
| 0x6
| 0x3
| LeftAnalogStickData
|-
| 0x9
| 0x3
| RightAnalogStickData
|-
| 0xC
| 0x1
| MotorStatus
|-
| 0xD
| 0x6
| SensorAccelSample0
|-
| 0x13
| 0x6
| SensorGyroSample0
|-
| 0x19
| 0x6
| SensorAccelSample1
|-
| 0x1F
| 0x6
| SensorGyroSample1
|-
| 0x25
| 0x6
| SensorAccelSample2
|-
| 0x2B
| 0x6
| SensorGyroSample2
|-
| 0x31
| 0x139
| McuData
|}
== HidAttachmentInputReport ==
This is a 0x16A-byte struct.
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x1
| [[#HidReportId|ReportId]]
|-
| 0x1
| 0x1
| LatencyTimer
|-
| 0x2
| 0x1
| PowerInfo
|-
| 0x3
| 0x1
| RightPadButtonStatus
|-
| 0x4
| 0x1
| SharedButtonStatus
|-
| 0x5
| 0x1
| LeftPadButtonStatus
|-
| 0x6
| 0x3
| LeftAnalogStickData
|-
| 0x9
| 0x3
| RightAnalogStickData
|-
| 0xC
| 0x1
| MotorStatus
|-
| 0xD
| 0x6
| SensorAccelSample0
|-
| 0x13
| 0x6
| SensorGyroSample0
|-
| 0x19
| 0x6
| SensorAccelSample1
|-
| 0x1F
| 0x6
| SensorGyroSample1
|-
| 0x25
| 0x6
| SensorAccelSample2
|-
| 0x2B
| 0x6
| SensorGyroSample2
|-
| 0x31
| 0x139
| AttachmentData
|}
== HidGenericInputReport ==
This is a 0xC-byte struct.
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x1
| [[#HidReportId|ReportId]]
|-
| 0x1
| 0x2
| ButtonStatus
|-
| 0x3
| 0x1
| StickHatData
|-
| 0x4
| 0x4
| LeftAnalogStickData
|-
| 0x8
| 0x4
| RightAnalogStickData
|}
== HidCommandOutputReport ==
This is a 0x31-byte struct.
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x1
| [[#HidReportId|ReportId]]
|-
| 0x1
| 0x1
| PacketNumber
|-
| 0x2
| 0x8
| [[#HidMotorData|MotorData]]
|-
| 0xA
| 0x1
| [[#HidCommand|CommandId]]
|-
| 0xB
| 0x26
| CommandData
|}
== HidMcuUpdateOutputReport ==
This is a 0x13E-byte struct.
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x1
| [[#HidReportId|ReportId]]
|-
| 0x1
| 0x1
| PacketNumber
|-
| 0x2
| 0x8
| [[#HidMotorData|MotorData]]
|-
| 0xA
| 0x134
| McuUpdateData
|}
== HidBasicOutputReport ==
This is a 0xA-byte struct.
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x1
| [[#HidReportId|ReportId]]
|-
| 0x1
| 0x1
| PacketNumber
|-
| 0x2
| 0x8
| [[#HidMotorData|MotorData]]
|}
== HidMcuOutputReport ==
This is a 0x30-byte struct.
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x1
| [[#HidReportId|ReportId]]
|-
| 0x1
| 0x1
| PacketNumber
|-
| 0x2
| 0x8
| [[#HidMotorData|MotorData]]
|-
| 0xA
| 0x26
| McuData
|}
== HidAttachmentOutputReport ==
This is a 0x30-byte struct.
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x1
| [[#HidReportId|ReportId]]
|-
| 0x1
| 0x1
| PacketNumber
|-
| 0x2
| 0x8
| [[#HidMotorData|MotorData]]
|-
| 0xA
| 0x26
| AttachmentData
|}
== HidReportId ==
{| class="wikitable" border="1"
! Value
! Description
|-
| 0x01 || CommandOutputReport
|-
| 0x03 || McuUpdateOutputReport
|-
| 0x10 || BasicOutputReport
|-
| 0x11 || McuOutputReport
|-
| 0x12 || AttachmentOutputReport
|-
| 0x21 || CommandInputReport
|-
| 0x23 || McuUpdateInputReport
|-
| 0x30 || BasicInputReport
|-
| 0x31 || McuInputReport
|-
| 0x32 || AttachmentInputReport
|-
| 0x33 ||
|-
| 0x3F || GenericInputReport
|-
| 0x70 || OtaEnableFwuReport
|-
| 0x71 || OtaSetupReadReport
|-
| 0x72 || OtaReadReport
|-
| 0x73 || OtaEraseReport
|-
| 0x74 || OtaWriteReport
|-
| 0x75 || OtaLaunchReport
|-
| 0x80 || ExtGripOutputReport
|-
| 0x81 || ExtGripInputReport
|-
| 0x82 ||
|}
== HidMotorData ==
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0 || 0x4 || [[#VibrationAmFmPackTag|JoyLeftVibrationAmFmPackTag]]
|-
| 0x4 || 0x8 || [[#VibrationAmFmPackTag|JoyRightVibrationAmFmPackTag]]
|}
== VibrationAmFmPackTag ==
This is "nn::xcd::VibrationAmFmPackTag". This is a BitPack object.
=== VibrationAmFmPackFormatZero ===
{| class="wikitable" border="1"
! Bits
! Description
|-
| 0-29 || Reserved
|-
| 30-31 || PackFormat (must be 0)
|}
=== VibrationAmFmPackFormatOne ===
{| class="wikitable" border="1"
! Bits
! Description
|-
| 0-19 || Reserved
|-
| 20-24 || ChannelCodesHigh
|-
| 25-29 || ChannelCodesLow
|-
| 30-31 || PackFormat (must be 1)
|}
=== VibrationAmFmPackFormatOne28bit ===
{| class="wikitable" border="1"
! Bits
! Description
|-
| 0-1 || Reserved
|-
| 2-8 || ChannelFrequencyHigh
|-
| 9-15 || ChannelAmplitudeHigh
|-
| 16-22 || ChannelFrequencyLow
|-
| 23-29 || ChannelAmplitudeLow
|-
| 30-31 || PackFormat (must be 1)
|}
=== VibrationAmFmPackFormatTwo ===
{| class="wikitable" border="1"
! Bits
! Description
|-
| 0-9 || Reserved
|-
| 10-14 || VibrationCodesHigh
|-
| 15-19 || VibrationCodesLow
|-
| 20-24 || ChannelCodesHigh
|-
| 25-29 || ChannelCodesLow
|-
| 30-31 || PackFormat (must be 2)
|}
=== VibrationAmFmPackFormatTwo14bitLow ===
{| class="wikitable" border="1"
! Bits
! Description
|-
| 0 || IsChannelHigh (must be 0)
|-
| 1-7 || ChannelFrequency
|-
| 8-12 || VibrationCodesHigh1
|-
| 13-17 || VibrationCodesLow1
|-
| 18-22 || VibrationCodesHigh0
|-
| 23-29 || ChannelAmplitude
|-
| 30-31 || PackFormat (must be 2)
|}
=== VibrationAmFmPackFormatTwo14bitHigh ===
{| class="wikitable" border="1"
! Bits
! Description
|-
| 0 || IsChannelHigh (must be 1)
|-
| 1-7 || ChannelFrequency
|-
| 8-12 || VibrationCodesHigh1
|-
| 13-17 || VibrationCodesLow1
|-
| 18-22 || VibrationCodesLow0
|-
| 23-29 || ChannelAmplitude
|-
| 30-31 || PackFormat (must be 2)
|}
=== VibrationAmFmPackFormatThree ===
{| class="wikitable" border="1"
! Bits
! Description
|-
| 0-4 || VibrationCodesHigh1
|-
| 5-9 || VibrationCodesLow1
|-
| 10-14 || VibrationCodesHigh0
|-
| 15-19 || VibrationCodesLow0
|-
| 20-24 || ChannelCodesHigh
|-
| 25-29 || ChannelCodesLow
|-
| 30-31 || PackFormat (must be 3)
|}
=== VibrationAmFmPackFormatThree7bit ===
{| class="wikitable" border="1"
! Bits
! Description
|-
| 0 || IsChannelHigh
|-
| 1 || IsThree7bit (must be 1)
|-
| 2 || IsFm
|-
| 3-7 || VibrationCodesHigh1
|-
| 8-12 || VibrationCodesLow1
|-
| 13-17 || VibrationCodesHigh0
|-
| 18-22 || VibrationCodesLow0
|-
| 23-29 || ChannelAmFm
|-
| 30-31 || PackFormat (must be 1, instead of 3)
|}
== AmFmCodes ==
These are 32-bit values used by the Joy-Con firmware to represent combinations of amplitude/frequency pairs for vibration.
=== Unpacking ===
The Joy-Con firmware receives vibration data packed as [[#VibrationAmFmPackTag|VibrationAmFmPackTag]] from output reports. Depending on the packing format, these tags are then converted to an array of AmFmCodes as follows.
==== AmFm5BitCodes ====
Converted from [[#VibrationAmFmPackFormatOne|VibrationAmFmPackFormatOne]], [[#VibrationAmFmPackFormatTwo|VibrationAmFmPackFormatTwo]] or [[#VibrationAmFmPackFormatThree|VibrationAmFmPackFormatThree]].
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0 || 0x4 || <nowiki>(ChannelCodesLow & 0x1F)</nowiki>
|-
| 0x4 || 0x4 || <nowiki>(ChannelCodesHigh & 0x1F)</nowiki>
|-
| 0x8 || 0x4 || <nowiki>(VibrationCodesLow0 & 0x1F)</nowiki>
|-
| 0xC || 0x4 || <nowiki>(VibrationCodesHigh0 & 0x1F)</nowiki>
|-
| 0x10 || 0x4 || <nowiki>(VibrationCodesLow1 & 0x1F)</nowiki>
|-
| 0x14 || 0x4 || <nowiki>(VibrationCodesHigh1 & 0x1F)</nowiki>
|}
==== AmFm7BitOneCodes ====
Converted from [[#VibrationAmFmPackFormatOne28bit|VibrationAmFmPackFormatOne28bit]].
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0 || 0x4 || <nowiki>(((ChannelFrequencyLow & 0x7F) << 8) | (ChannelAmplitudeLow & 0x7F) | 0x8080)</nowiki>
|-
| 0x4 || 0x4 || <nowiki>(((ChannelFrequencyHigh & 0x7F) << 8) | (ChannelAmplitudeHigh & 0x7F) | 0x8080)</nowiki>
|}
==== AmFm7BitTwoCodes ====
Converted from [[#VibrationAmFmPackFormatTwo14bitLow|VibrationAmFmPackFormatTwo14bitLow]] or [[#VibrationAmFmPackFormatTwo14bitHigh|VibrationAmFmPackFormatTwo14bitHigh]].
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0 || 0x4 || <nowiki>IsChannelHigh ? (VibrationCodesLow0 & 0x1F) : (((ChannelFrequency & 0x7F) << 8) | (ChannelAmplitude & 0x7F) | 0x8080)</nowiki>
|-
| 0x4 || 0x4 || <nowiki>IsChannelHigh ? (((ChannelFrequency & 0x7F) << 8) | (ChannelAmplitude & 0x7F) | 0x8080) : (VibrationCodesHigh0 & 0x1F)</nowiki>
|-
| 0x8 || 0x4 || <nowiki>(VibrationCodesLow1 & 0x1F)</nowiki>
|-
| 0xC || 0x4 || <nowiki>(VibrationCodesHigh1 & 0x1F)</nowiki>
|-
| 0x10 || 0x4 || <nowiki>Always 0x18</nowiki>
|-
| 0x14 || 0x4 || <nowiki>Always 0x18</nowiki>
|}
==== AmFm7BitThreeCodes ====
Converted from [[#VibrationAmFmPackFormatThree7bit|VibrationAmFmPackFormatThree7bit]].
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0 || 0x4 || <nowiki>IsChannelHigh ? 0x18 : (IsFm ? (((ChannelAmFm & 0x7F) << 8) | 0x8000) : ((ChannelAmFm & 0x7F) | 0x80))</nowiki>
|-
| 0x4 || 0x4 || <nowiki>IsChannelHigh ? (IsFm ? (((ChannelAmFm & 0x7F) << 8) | 0x8000) : ((ChannelAmFm & 0x7F) | 0x80)) : 0x18</nowiki>
|-
| 0x8 || 0x4 || <nowiki>(VibrationCodesLow0 & 0x1F)</nowiki>
|-
| 0xC || 0x4 || <nowiki>(VibrationCodesHigh0 & 0x1F)</nowiki>
|-
| 0x10 || 0x4 || <nowiki>(VibrationCodesLow1 & 0x1F)</nowiki>
|-
| 0x14 || 0x4 || <nowiki>(VibrationCodesHigh1 & 0x1F)</nowiki>
|}
|}


Line 295: Line 1,064:
|}
|}


== Left & Right Separate Joy-con ==
== ExtGripCommand ==
When paired directly to a computer over bluetooth, the joy-con both provide identical HID input report descriptor. This does not appear to include motion controls, IR data, or NFC. They both behave as detached single-player controllers.
{| class="wikitable" border="1"
! Value
! Description
|-
| 0x01 || Inquiry
|-
| 0x02 || CreateHidConnection
|-
| 0x03 || DeleteHidConnection
|-
| 0x04 || StartHidData
|-
| 0x05 || StopHidData
|-
| 0x06 ||
|-
| 0x07 || GetFirmwareVersion
|-
| 0x08 ||
|-
| 0x91 || LoopbackTest
|-
| 0x92 ||
|-
| 0xA0 || SetUartClockTrim
|}
 
= Firmware =
The firmware is stored inside the flash in Broadcom's PatchRAM format as follows:
 
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0 || 0x1000 || StaticSection
|-
| 0x1000 || 0x1000 || [[#FailsafeSection|FailsafeSection]]
|-
| 0x2000 || 0x1000 || [[#VolatileSection|VolatileSection]]
|-
| 0x3000 || 0x1000 || VolatileSectionBackup1
|-
| 0x4000 || 0x1000 || VolatileSectionBackup2
|-
| 0x5000 || 0x1000 || [[#ShipmentInfo|ShipmentInfo]]
|-
| 0x6000 || 0x1000 || [[#FactoryConfiguration|FactoryConfiguration]]
|-
| 0x7000 || 0x1000 || Reserved
|-
| 0x8000 || 0x1000 || [[#UserCalibration|UserCalibration]]
|-
| 0x9000 || 0x7000 || Reserved
|-
| 0x10000 || 0x18000 || DynamicSection1
|-
| 0x28000 || 0x18000 || DynamicSection2
|-
| 0x40000 || 0x40000 || Reserved
|}
 
== FailsafeSection ==
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0 || 0xFF4 || Reserved
|-
| 0xFF4 || 0x8 || Signature
|-
| 0xFFC || 0x4 || Ds2Offset
|}
 
== VolatileSection ==
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0 || 0x26 ||
|-
| 0x26 || 0xFDA || Reserved
|}


HID Input Report Descriptor (Hexadecimal):
== ShipmentInfo ==
<pre>
{| class="wikitable" border="1"
00000000: 05010905 A1010601 FF852109 21750895  ....¡...ÿ…!.!u.•
|-
00000010: 30810285 30093075 08953081 02853109  0..…0.0u.•0..…1.
! Offset
00000020: 31750896 69018102 85320932 75089669  1u.–i...…2.2u.–i
! Size
00000030: 01810285 33093375 08966901 8102853F  ...…3.3u.–i...…?
! Description
00000040: 05091901 29101500 25017501 95108102  ....)...%.u.•...
|-
00000050: 05010939 15002507 75049501 81420509  ...9..%.u.•..B..
| 0x0 || 0x1 || Shipment
00000060: 75049501 81010501 09300931 09330934  u.•......0.1.3.4
|-
00000070: 16000027 FFFF0000 75109504 81020601  ...'ÿÿ..u.•.....
| 0x1 || 0xFFF || Reserved
00000080: FF850109 01750895 30910285 10091075  ÿ…...u.•0‘.…...u
|}
00000090: 08953091 02851109 11750895 30910285  .•0‘.…...u.•0‘.…
000000A0: 12091275 08953091 02C0              ...u.•0‘.À
</pre>


{| class="wikitable"
== FactoryConfiguration ==
! Parsed Bytes || Description
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0 || 0x10 || IdentificationCode
|-
| 0x10 || 0x2 || Reserved
|-
| 0x12 || 0x1 || [[#DeviceType|DeviceType]]
|-
| 0x13 || 0x1 || BoardRevision
|-
| 0x14 || 0x7 || Reserved
|-
|-
|0x05, 0x01,        || Usage Page (Generic Desktop Ctrls)
| 0x1B || 0x1 || FormatVersion
|-
|-
|0x09, 0x05,        || Usage (Game Pad)
| 0x1C || 0x4 || Reserved
|-
|-
|0xA1, 0x01,        || Collection (Application)
| 0x20 || 0x18 || [[#Cal1|Cal1]]
|-
|-
|0x06, 0x01, 0xFF,  ||   Usage Page (Vendor Defined 0xFF01)
| 0x38 || 0x5 || Reserved
|-
|-
|0x85, 0x21,        ||   Report ID (33)
| 0x3D || 0x12 || [[#Cal2|Cal2]]
|-
|-
|0x09, 0x21,        ||   Usage (0x21)
| 0x4F || 0x1 || Reserved
|-
|-
|0x75, 0x08,        ||   Report Size (8)
| 0x50 || 0xD || [[#Design|Design]]
|-
|-
|0x95, 0x30,        ||   Report Count (48)
| 0x5D || 0x23 || Reserved
|-
|-
|0x81, 0x02,        ||   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
| 0x80 || 0x18 || [[#Model1|Model1]]
|-
|-
|0x85, 0x30,        ||   Report ID (48)
| 0x98 || 0x12 || [[#Model2|Model2]]
|-
|-
|0x09, 0x30,        ||   Usage (0x30)
| 0xAA || 0x1 || Reserved
|-
|-
|0x75, 0x08,        ||   Report Size (8)
| 0xAB || 0x1 || AccelerometerAxisAssignment
|-
|-
|0x95, 0x30,        ||   Report Count (48)
| 0xAC || 0x1 || GyroscopeAxisAssignment
|-
|-
|0x81, 0x02,        ||   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
| 0xAD || 0x1 || AnalogStickMainAxisAssignment
|-
|-
|0x85, 0x31,        ||   Report ID (49)
| 0xAE || 0x1 || AnalogStickSubAxisAssignment
|-
|-
|0x09, 0x31,        ||   Usage (0x31)
| 0xAF || 0x151 || Reserved
|-
|-
|0x75, 0x08,        ||   Report Size (8)
| 0x200 || 0x2 || BatteryVoltage
|-
|-
|0x96, 0x69, 0x01,  ||   Report Count (361)
| 0x202 || 0xB7E || Reserved
|-
|-
|0x81, 0x02,        ||   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
| 0xD80 || 0x2 || TarragonVid
|-
|-
|0x85, 0x32,        ||   Report ID (50)
| 0xD82 || 0x2 || TarragonPid
|-
|-
|0x09, 0x32,        ||   Usage (0x32)
| 0xD84 || 0x7C || Reserved
|-
|-
|0x75, 0x08,        ||   Report Size (8)
| 0xE00 || 0x100 || InspectionLog
|-
|-
|0x96, 0x69, 0x01,  ||   Report Count (361)
| 0xF00 || 0x100 || Reserved
|}
 
=== DeviceType ===
{| class="wikitable" border="1"
|-
|-
|0x81, 0x02,        ||  Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
! Value
! Description
|-
|-
|0x85, 0x33,        ||   Report ID (51)
| 0x01 || JoyConLeft
|-
|-
|0x09, 0x33,        ||   Usage (0x33)
| 0x02 || JoyConRight
|-
|-
|0x75, 0x08,        ||   Report Size (8)
| 0x03 || SwitchProController
|-
|-
|0x96, 0x69, 0x01,  ||   Report Count (361)
| 0x04 || MiyabiLeft
|-
|-
|0x81, 0x02,        ||   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
| 0x05 || MiyabiRight
|-
|-
|0x85, 0x3F,        ||   Report ID (63)
| 0x06 || Tarragon
|-
|-
|0x05, 0x09,        ||   Usage Page (Button)
| 0x07 || LarkH1
|-
|-
|0x19, 0x01,        ||   Usage Minimum (0x01)
| 0x08 || LarkH2
|-
|-
|0x29, 0x10,        ||   Usage Maximum (0x10)
| 0x09 || LarkNL
|-
|-
|0x15, 0x00,        ||   Logical Minimum (0)
| 0x0A || LarkNR
|-
|-
|0x25, 0x01,        ||   Logical Maximum (1)
| 0x0B || Lucia
|-
|-
|0x75, 0x01,        ||   Report Size (1)
| 0x0C || [12.0.0+] Lagon
|-
|-
|0x95, 0x10,        ||   Report Count (16)
| 0x0D || [13.0.0+] Lager
|-
|-
|0x81, 0x02,        ||   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
| 0x0E || [14.0.0+] Tarragon2
|-
|-
|0x05, 0x01,        ||   Usage Page (Generic Desktop Ctrls)
| 0x21 || FiftyL
|-
|-
|0x09, 0x39,        ||   Usage (Hat switch)
| 0x22 || FiftyR
|}
 
=== Cal1 ===
{| class="wikitable" border="1"
|-
|-
|0x15, 0x00,        ||  Logical Minimum (0)
! Offset
! Size
! Description
|-
|-
|0x25, 0x07,        ||   Logical Maximum (7)
| 0x0 || 0x18 || [[#SixAxisSensorCalibrationValue|SixAxisSensorCalibrationValue]]
|}
 
==== SixAxisSensorCalibrationValue ====
{| class="wikitable" border="1"
|-
|-
|0x75, 0x04,        ||  Report Size (4)
! Offset
! Size
! Description
|-
|-
|0x95, 0x01,        ||   Report Count (1)
| 0x0 || 0x2 || Accelerometer0OffsetX
|-
|-
|0x81, 0x42,        ||   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
| 0x2 || 0x2 || Accelerometer0OffsetY
|-
|-
|0x05, 0x09,        ||   Usage Page (Button)
| 0x4 || 0x2 || Accelerometer0OffsetZ
|-
|-
|0x75, 0x04,        ||   Report Size (4)
| 0x6 || 0x2 || Accelerometer1gScaleX
|-
|-
|0x95, 0x01,        ||   Report Count (1)
| 0x8 || 0x2 || Accelerometer1gScaleY
|-
|-
|0x81, 0x01,        ||   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
| 0xA || 0x2 || Accelerometer1gScaleZ
|-
|-
|0x05, 0x01,        ||   Usage Page (Generic Desktop Ctrls)
| 0xC || 0x2 || Gyroscope0OffsetX
|-
|-
|0x09, 0x30,        ||   Usage (X)
| 0xE || 0x2 || Gyroscope0OffsetY
|-
|-
|0x09, 0x31,        ||   Usage (Y)
| 0x10 || 0x2 || Gyroscope0OffsetZ
|-
|-
|0x09, 0x33,        ||   Usage (Rx)
| 0x12 || 0x2 || Gyroscope78rpmScaleX
|-
|-
|0x09, 0x34,        ||   Usage (Ry)
| 0x14 || 0x2 || Gyroscope78rpmScaleY
|-
|-
|0x16, 0x00, 0x00,  ||   Logical Minimum (0)
| 0x16 || 0x2 || Gyroscope78rpmScaleZ
|}
 
=== Cal2 ===
{| class="wikitable" border="1"
|-
|-
|0x27, 0xFF, 0xFF, 0x00, 0x00,  ||  Logical Maximum (65534)
! Offset
! Size
! Description
|-
|-
|0x75, 0x10,        ||   Report Size (16)
| 0x0 || 0x9 || [[#AnalogStickCalibrationValue|AnalogStickMainCalibrationValue]]
|-
|-
|0x95, 0x04,        ||   Report Count (4)
| 0x9 || 0x9 || [[#AnalogStickCalibrationValue|AnalogStickSubCalibrationValue]]
|}
 
==== AnalogStickCalibrationValue ====
{| class="wikitable" border="1"
|-
|-
|0x81, 0x02,        ||  Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
! Offset
! Size
! Description
|-
|-
|0x06, 0x01, 0xFF,  ||   Usage Page (Vendor Defined 0xFF01)
| 0x0 || 0x1 || AnalogStickCalXPositive
|-
|-
|0x85, 0x01,        ||   Report ID (1)
| 0x1 || 0x2 || AnalogStickCalYPositive
|-
|-
|0x09, 0x01,        ||   Usage (0x01)
| 0x3 || 0x1 || AnalogStickCalX0
|-
|-
|0x75, 0x08,        ||   Report Size (8)
| 0x4 || 0x2 || AnalogStickCalY0
|-
|-
|0x95, 0x30,        ||   Report Count (48)
| 0x6 || 0x1 || AnalogStickCalXNegative
|-
|-
|0x91, 0x02,        ||   Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
| 0x7 || 0x2 || AnalogStickCalYNegative
|}
 
=== Design ===
{| class="wikitable" border="1"
|-
|-
|0x85, 0x10,        ||  Report ID (16)
! Offset
! Size
! Description
|-
|-
|0x09, 0x10,        ||   Usage (0x10)
| 0x0 || 0xC || [[#ControllerColor|ControllerColor]]
|-
|-
|0x75, 0x08,        ||   Report Size (8)
| 0xC || 0x1 || [[#DesignVariation|DesignVariation]]
|}
 
==== ControllerColor ====
{| class="wikitable" border="1"
|-
|-
|0x95, 0x30,        ||  Report Count (48)
! Offset
! Size
! Description
|-
|-
|0x91, 0x02,        ||   Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
| 0x0 || 0x1 || MainColorR
|-
|-
|0x85, 0x11,        ||   Report ID (17)
| 0x1 || 0x1 || MainColorG
|-
|-
|0x09, 0x11,        ||   Usage (0x11)
| 0x2 || 0x1 || MainColorB
|-
|-
|0x75, 0x08,        ||   Report Size (8)
| 0x3 || 0x1 || SubColorR
|-
|-
|0x95, 0x30,        ||   Report Count (48)
| 0x4 || 0x1 || SubColorG
|-
|-
|0x91, 0x02,        ||   Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
| 0x5 || 0x1 || SubColorB
|-
|-
|0x85, 0x12,        ||   Report ID (18)
| 0x6 || 0x1 || 3rdColorR
|-
|-
|0x09, 0x12,        ||   Usage (0x12)
| 0x7 || 0x1 || 3rdColorG
|-
|-
|0x75, 0x08,        ||   Report Size (8)
| 0x8 || 0x1 || 3rdColorB
|-
|-
|0x95, 0x30,        ||   Report Count (48)
| 0x9 || 0x1 || 4thColorR
|-
|-
|0x91, 0x02,        ||   Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
| 0xA || 0x1 || 4thColorG
|-
|-
|0xC0,             || End Collection
| 0xB || 0x1 || 4thColorB
|}
 
==== DesignVariation ====
{| class="wikitable" border="1"
|-
! Value
! Name
|-
| 0x0 || LuciaJ, LagerJ
|-
| 0x1 || LuciaE, LagerE
|-
| 0x2 || LuciaU, LagerU
|}
 
=== Model1 ===
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0 || 0x6 || [[#SixAxisSensorModelValue|SixAxisSensorModelValue]]
|-
| 0x6 || 0x12 || [[#AnalogStickModelValue|AnalogStickMainModelValue]]
|}
 
==== SixAxisSensorModelValue ====
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0 || 0x2 || SixAxisHorizontalOffsetX
|-
| 0x2 || 0x2 || SixAxisHorizontalOffsetY
|-
| 0x4 || 0x2 || SixAxisHorizontalOffsetZ
|}
 
==== AnalogStickModelValue ====
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0 || 0x1 || AnalogStickModelNoise
|-
| 0x1 || 0x2 || AnalogStickModelTypicalStroke
|-
| 0x3 || 0x1 || AnalogStickModelCenterDeadZoneSize
|-
| 0x4 || 0x2 || AnalogStickModelCircuitDeadZoneScale
|-
| 0x6 || 0x1 || AnalogStickModelMinimumStrokeXPositive
|-
| 0x7 || 0x2 || AnalogStickModelMinimumStrokeYPositive
|-
| 0x9 || 0x1 || AnalogStickModelMinimumStrokeXNegative
|-
| 0xA || 0x2 || AnalogStickModelMinimumStrokeYNegative
|-
| 0xC || 0x1 || AnalogStickModelCenterRangeXPositive
|-
| 0xD || 0x2 || AnalogStickModelCenterRangeYPositive
|-
| 0xF || 0x1 || AnalogStickModelCenterRangeXNegative
|-
| 0x10 || 0x2 || AnalogStickModelCenterRangeYNegative
|}
 
=== Model2 ===
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0 || 0x12 || [[#AnalogStickModelValue|AnalogStickSubModelValue]]
|}
 
== UserCalibration ==
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0 || 0x10 || Reserved
|-
| 0x10 || 0x16 || [[#UserCal1|UserCal1]]
|-
| 0x26 || 0x1A || [[#UserCal2|UserCal2]]
|-
| 0x40 || 0xFC0 || Reserved
|}
 
=== UserCal1 ===
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0 || 0x2 || AnalogStickMainUserMagicNumber
|-
| 0x2 || 0x9 || [[#AnalogStickCalibrationValue|AnalogStickMainUserCalibrationValue]]
|-
| 0xB || 0x2 || AnalogStickSubUserMagicNumber
|-
| 0xD || 0x9 || [[#AnalogStickCalibrationValue|AnalogStickSubUserCalibrationValue]]
|}
 
=== UserCal2 ===
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0 || 0x2 || SixAxisUserCalibrationMagicNumber
|-
| 0x2 || 0x18 || [[#SixAxisSensorCalibrationValue|SixAxisUserCalibrationValue]]
|}
|}


== Colors ==
== Colors ==
HEX codes for the colors shown in the "Controllers" menu of the Switch UI can be found in a Joy-con SPI dump starting at offset 0x6050. Body color is first followed by button color and each is 3 bytes long. These values are able to be re-written with any HEX color value to make the Joy-cons show up as different colors in the UI. The following is a list of official HEX colors recovered from SPI dumps.
HEX codes for the colors shown in the "Controllers" menu of the Switch UI can be found in a Joy-con SPI dump starting at offset 0x6050. Body color is first followed by button color and each is 3 bytes long. These values are able to be re-written with any HEX color value to make the Joy-cons show up as different colors in the UI. The following is a list of official HEX colors recovered from SPI dumps.
{| class="wikitable"
{| class="wikitable"
! Developer Kit Joy-Con || Release || Body HEX || Button HEX
! Developer Kit Joy-Con || Release || Body HEX || Button HEX
Line 509: Line 1,543:
|-
|-
| White / ホワイト || 2021.10.08 || #E6E6E6 || #323232
| White / ホワイト || 2021.10.08 || #E6E6E6 || #323232
|-
| Pastel Pink / パステルピンク || 2023.06.30 || #FFAFAF || #372D2D
|-
| Pastel Yellow / パステルイエロー || 2023.06.30 || #F5FF82 || #32332D
|-
| Pastel Purple / パステルパープル || 2023.06.30 || #F0CBEB || #373037
|-
| Pastel Green / パステルグリーン || 2023.06.30 || #BCFFC8 || #2D322D
|-
|-
! Special Edition Joy-Con Color || Release || Body HEX || Button HEX
! Special Edition Joy-Con Color || Release || Body HEX || Button HEX
Line 572: Line 1,614:
| // *(Prize Awarded for Labo Contest - No Retail) // **(Available Japan Only)
| // *(Prize Awarded for Labo Contest - No Retail) // **(Available Japan Only)
|}
|}
= Additional Links =
[https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering Reverse Engineering of the Joy-Con by Github user dekuNukem]