Difference between revisions of "Joy-Con"

From Nintendo Switch Brew
Jump to navigation Jump to search
(Added internals to the hardware section)
 
(60 intermediate revisions by 14 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 || Samsung K4F6E304HB-MGCH 2 GB LPDDR4 DRAM
+
| IMU || STMicroelectronics LSM6DS3H
 
|-
 
|-
| Yellow|| Macronix International MX25U4033E 4 Mb CMOS Flash
+
| PMIC || Texas Instruments BQ24072
 +
|-
 +
| Storage || Macronix International MX25U4033E 4 Mb CMOS Flash
 +
|}
 +
 
 +
== Ukyo ==
 +
{| class="wikitable"
 +
! Component || Description
 +
|-
 +
| SoC || Broadcom BCM20734
 +
|-
 +
| MCU || STMicroelectronics STM32P411
 +
|-
 +
| NFC || STMicroelectronics ST21NFCB
 +
|-
 +
| IMU || STMicroelectronics LSM6DS3H
 +
|-
 +
| PMIC || Texas Instruments BQ24072
 +
|-
 +
| Storage || Macronix International MX25U4033E 4 Mb CMOS Flash
 +
|}
 +
 
 +
= Protocol =
 +
The Joy-Con can communicate wirelessly over Bluetooth or through the rails over UART. The latter uses a Nintendo proprietary protocol called "Nwcp".
 +
 
 +
== NwcpPacket ==
 +
{| class="wikitable" border="1"
 +
|-
 +
! Offset
 +
! Size
 +
! Description
 +
|-
 +
| 0x0
 +
| 0x5
 +
| [[#HciHeader|HciHeader]]
 +
|-
 +
| 0x5
 +
| 0x7
 +
| [[#NwcpHeader|NwcpHeader]]
 +
|-
 +
| 0xC
 +
| Variable
 +
| Data
 +
|}
 +
 
 +
=== HciHeader ===
 +
This is a 5-byte packet header used by Broadcom's HCI Control Protocol.
 +
 
 +
{| class="wikitable" border="1"
 +
|-
 +
! Offset
 +
! Size
 +
! Description
 +
|-
 +
| 0x0
 +
| 0x1
 +
| PacketType (always 0x19)
 +
|-
 +
| 0x1
 +
| 0x1
 +
| [[#CommandCode|CommandCode]]
 +
|-
 +
| 0x2
 +
| 0x1
 +
| GroupCode (always 0x03)
 +
|-
 +
| 0x3
 +
| 0x2
 +
| PacketLength
 +
|}
 +
 
 +
==== CommandCode ====
 +
{| class="wikitable" border="1"
 +
! Value
 +
! Description
 +
|-
 +
| 0x1 || SendToDevice
 +
|-
 +
| 0x81 || ReceiveFromDevice
 +
|}
 +
 
 +
=== NwcpHeader ===
 +
{| class="wikitable" border="1"
 +
|-
 +
! Offset
 +
! Size
 +
! Description
 +
|-
 +
| 0x0
 +
| 0x1
 +
| [[#CommandType|CommandType]]
 +
|-
 +
| 0x1
 +
| 0x1
 +
| [[#DeviceCommand|DeviceCommand]]
 +
|-
 +
| 0x2
 +
| 0x2
 +
| DataSize
 +
|-
 +
| 0x4
 +
| 0x1
 +
| CommandResult
 +
|-
 +
| 0x5
 +
| 0x1
 +
| DataCrc (CRC8 over Data for DataSize bytes)
 +
|-
 +
| 0x6
 +
| 0x1
 +
| HeaderCrc (CRC8 over the previous 6 bytes)
 +
|}
 +
 
 +
==== CommandType ====
 +
{| class="wikitable" border="1"
 +
! Value
 +
! Description
 +
|-
 +
| 0x43 || ReportOut
 +
|-
 +
| 0x53 || ReportIn
 +
|-
 +
| 0x91 || DeviceCommandIn
 +
|-
 +
| 0x92 || Hid
 +
|-
 +
| 0x93 ||
 +
|-
 +
| 0x94 || DeviceCommandOut
 +
|-
 +
| 0x95 ||
 +
|-
 +
| 0x9A || Fifty
 +
|-
 +
| 0xA3 || ReportComplete
 +
|-
 +
| 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 || OtaWriteReport
 +
|-
 +
| 0x74 || OtaEraseReport
 +
|-
 +
| 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>
 +
|}
  
==Firmware==
+
==== AmFm7BitOneCodes ====
''See also: [[Joy-Con Firmware]]''
+
Converted from [[#VibrationAmFmPackFormatOne28bit|VibrationAmFmPackFormatOne28bit]].
  
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" border="1"
{| class="wikitable"
+
|-
! Controller || 1.0.0
+
! 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>
 +
|}
 +
 
 +
== DeviceCommand ==
 +
{| class="wikitable" border="1"
 +
! Value
 +
! Description
 +
|-
 +
| 0x01 || Inquiry
 +
|-
 +
| 0x02 || WriteChargerSettings
 +
|-
 +
| 0x03 || ReadChargerSettings
 +
|-
 +
| 0x04 || AttachmentEnable
 +
|-
 +
| 0x05 || IsAttachmentEnabled
 +
|-
 +
| 0x06 || GetBatteryVoltage
 +
|-
 +
| 0x07 || ReadWakeUpReason
 +
|-
 +
| 0x10 || CreateHidConnection
 +
|-
 +
| 0x11 || DisconnectHid
 +
|-
 +
| 0x12 || SetHidInterval
 +
|-
 +
| 0x13 || GetHidInterval
 +
|-
 +
| 0x18 || Pairing
 +
|-
 +
| 0x20 || UpdateUartBaudRate
 +
|-
 +
| 0x40 || Test
 +
|}
 +
 
 +
== HidCommand ==
 +
{| class="wikitable" border="1"
 +
! Value
 +
! Description
 +
|-
 +
| 0x01 || PairingOut
 +
|-
 +
| 0x02 || GetDeviceInfo
 +
|-
 +
| 0x03 || SetDataFormat
 +
|-
 +
| 0x04 || LRButtonDetection
 +
|-
 +
| 0x05 || Page
 +
|-
 +
| 0x06 || Reset
 +
|-
 +
| 0x07 || ClearPairingInfo
 +
|-
 +
| 0x08 || Shipment
 +
|-
 +
| 0x10 || SerialFlashRead
 +
|-
 +
| 0x11 || SerialFlashWrite
 +
|-
 +
| 0x12 || SerialFlashSectorErase
 +
|-
 +
| 0x20 || McuReset
 +
|-
 +
| 0x21 || McuWrite
 +
|-
 +
| 0x22 || McuResume
 +
|-
 +
| 0x24 || McuPollingEnable
 +
|-
 +
| 0x25 || McuPollingDisable
 +
|-
 +
| 0x28 || AttachmentWrite
 +
|-
 +
| 0x29 || AttachmentRead
 +
|-
 +
| 0x2A || AttachmentEnable
 +
|-
 +
| 0x2B || GetAttachmentInfo
 +
|-
 +
| 0x30 || SetIndicatorLed
 +
|-
 +
| 0x31 || GetIndicatorLed
 +
|-
 +
| 0x38 || SetNotificationLed
 +
|-
 +
| 0x40 || SensorSleep
 +
|-
 +
| 0x41 || SensorConfig
 +
|-
 +
| 0x42 || SensorWrite
 +
|-
 +
| 0x43 || SensorRead
 +
|-
 +
| 0x48 || MotorEnable
 +
|-
 +
| 0x50 || GetBatteryVoltage
 +
|-
 +
| 0x51 || WriteChargeSetting
 +
|-
 +
| 0x52 || ReadChargeSetting
 +
|-
 +
| 0x58 || ExtDevWrite
 +
|-
 +
| 0x59 || GetExtDevInfo
 +
|-
 +
| 0x5A || ExtDevPollingEnable
 +
|-
 +
| 0x5B || ExtDevPollingDisable
 +
|-
 +
| 0x5C || ExtDevInFormatConfig
 +
|-
 +
| 0x5D ||
 +
|-
 +
| 0x60 || InternalAttachmentWrite
 +
|-
 +
| 0x61 ||
 +
|-
 +
| 0x62 || InternalAttachmentEnable
 +
|-
 +
| 0x63 || InternalAttachmentPollingEnable
 +
|-
 +
| 0x64 || InternalAttachmentPollingDisable
 +
|-
 +
| 0x7A || AddButtonPlayData
 +
|-
 +
| 0x7B || StartStopButtonPlayData
 +
|-
 +
| 0x80 || Ack
 +
|-
 +
| 0x81 || PairingIn
 +
|-
 +
| 0x82 || DeviceInfo
 +
|-
 +
| 0x83 || LRButtonElapsedTime
 +
|-
 +
| 0x90 || SerialFlashData
 +
|-
 +
| 0xA0 || McuData
 +
|-
 +
| 0xA8 || AttachmentData
 +
|-
 +
| 0xA9 || AttachmentInfo
 +
|-
 +
| 0xAA || InternalAttachmentRead
 +
|-
 +
| 0xB0 || IndicatorLed
 +
|-
 +
| 0xC0 || SensorData
 +
|-
 +
| 0xD0 || BatteryVoltage
 +
|-
 +
| 0xD8 || ExtDevRead
 +
|-
 +
| 0xD9 || ExtDevInfo
 +
|}
 +
 
 +
== ExtGripCommand ==
 +
{| 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
 
|-
 
|-
| Joy-Con Left (Blue) || [[:File:pad.L.bin|flash dump]]
+
| 0xFF4 || 0x8 || Signature
 
|-
 
|-
| Joy-Con Right (Red) || [[:File:pad.R.bin|flash dump]]
+
| 0xFFC || 0x4 || Ds2Offset
 
|}
 
|}
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.
 
  
== Left & Right Separate Joy-con ==
+
== VolatileSection ==
 +
{| class="wikitable" border="1"
 +
|-
 +
! Offset
 +
! Size
 +
! Description
 +
|-
 +
| 0x0 || 0x26 ||
 +
|-
 +
| 0x26 || 0xFDA || Reserved
 +
|}
  
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.
+
== ShipmentInfo ==
 +
{| class="wikitable" border="1"
 +
|-
 +
! Offset
 +
! Size
 +
! Description
 +
|-
 +
| 0x0 || 0x1 || Shipment
 +
|-
 +
| 0x1 || 0xFFF || Reserved
 +
|}
  
HID Input Report Descriptor (Hexadecimal):
+
== FactoryConfiguration ==
<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 || 0x10 || IdentificationCode
00000060: 75049501 81010501 09300931 09330934  u.•......0.1.3.4
+
|-
00000070: 16000027 FFFF0000 75109504 81020601  ...'ÿÿ..u.•.....
+
| 0x10 || 0x2 || Reserved
00000080: FF850109 01750895 30910285 10091075  ÿ…...u.•0‘.…...u
+
|-
00000090: 08953091 02851109 11750895 30910285  .•0‘.…...u.•0‘.…
+
| 0x12 || 0x1 || [[#DeviceType|DeviceType]]
000000A0: 12091275 08953091 02C0              ...u.•0‘.À
+
|-
</pre>
+
| 0x13 || 0x1 || BoardRevision
 +
|-
 +
| 0x14 || 0x7 || Reserved
 +
|-
 +
| 0x1B || 0x1 || FormatVersion
 +
|-
 +
| 0x1C || 0x4 || Reserved
 +
|-
 +
| 0x20 || 0x18 || [[#Cal1|Cal1]]
 +
|-
 +
| 0x38 || 0x5 || Reserved
 +
|-
 +
| 0x3D || 0x12 || [[#Cal2|Cal2]]
 +
|-
 +
| 0x4F || 0x1 || Reserved
 +
|-
 +
| 0x50 || 0xD || [[#Design|Design]]
 +
|-
 +
| 0x5D || 0x23 || Reserved
 +
|-
 +
| 0x80 || 0x18 || [[#Model1|Model1]]
 +
|-
 +
| 0x98 || 0x12 || [[#Model2|Model2]]
 +
|-
 +
| 0xAA || 0x1 || Reserved
 +
|-
 +
| 0xAB || 0x1 || AccelerometerAxisAssignment
 +
|-
 +
| 0xAC || 0x1 || GyroscopeAxisAssignment
 +
|-
 +
| 0xAD || 0x1 || AnalogStickMainAxisAssignment
 +
|-
 +
| 0xAE || 0x1 || AnalogStickSubAxisAssignment
 +
|-
 +
| 0xAF || 0x151 || Reserved
 +
|-
 +
| 0x200 || 0x2 || BatteryVoltage
 +
|-
 +
| 0x202 || 0xB7E || Reserved
 +
|-
 +
| 0xD80 || 0x2 || TarragonVid
 +
|-
 +
| 0xD82 || 0x2 || TarragonPid
 +
|-
 +
| 0xD84 || 0x7C || Reserved
 +
|-
 +
| 0xE00 || 0x100 || InspectionLog
 +
|-
 +
| 0xF00 || 0x100 || Reserved
 +
|}
  
{| class="wikitable"
+
=== DeviceType ===
! Parsed Bytes || Description
+
{| class="wikitable" border="1"
 +
|-
 +
! Value
 +
! Description
 +
|-
 +
| 0x01 || JoyConLeft
 +
|-
 +
| 0x02 || JoyConRight
 +
|-
 +
| 0x03 || SwitchProController
 +
|-
 +
| 0x04 || MiyabiLeft
 +
|-
 +
| 0x05 || MiyabiRight
 +
|-
 +
| 0x06 || Tarragon
 +
|-
 +
| 0x07 || LarkH1
 +
|-
 +
| 0x08 || LarkH2
 +
|-
 +
| 0x09 || LarkNL
 +
|-
 +
| 0x0A || LarkNR
 +
|-
 +
| 0x0B || Lucia
 +
|-
 +
| 0x0C || [12.0.0+] Lagon
 +
|-
 +
| 0x0D || [13.0.0+] Lager
 +
|-
 +
| 0x0E || [14.0.0+] Tarragon2
 +
|-
 +
| 0x21 || FiftyL
 
|-
 
|-
|0x05, 0x01,        || Usage Page (Generic Desktop Ctrls)
+
| 0x22 || FiftyR
 +
|}
 +
 
 +
=== Cal1 ===
 +
{| class="wikitable" border="1"
 
|-
 
|-
|0x09, 0x05,        || Usage (Game Pad)
+
! Offset
 +
! Size
 +
! Description
 
|-
 
|-
|0xA1, 0x01,        || Collection (Application)
+
| 0x0 || 0x18 || [[#SixAxisSensorCalibrationValue|SixAxisSensorCalibrationValue]]
 +
|}
 +
 
 +
==== SixAxisSensorCalibrationValue ====
 +
{| class="wikitable" border="1"
 
|-
 
|-
|0x06, 0x01, 0xFF,  ||  Usage Page (Vendor Defined 0xFF01)
+
! Offset
 +
! Size
 +
! Description
 
|-
 
|-
|0x85, 0x21,        ||   Report ID (33)
+
| 0x0 || 0x2 || Accelerometer0OffsetX
 
|-
 
|-
|0x09, 0x21,        ||   Usage (0x21)
+
| 0x2 || 0x2 || Accelerometer0OffsetY
 
|-
 
|-
|0x75, 0x08,        ||   Report Size (8)
+
| 0x4 || 0x2 || Accelerometer0OffsetZ
 
|-
 
|-
|0x95, 0x30,        ||   Report Count (48)
+
| 0x6 || 0x2 || Accelerometer1gScaleX
 
|-
 
|-
|0x81, 0x02,        ||   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+
| 0x8 || 0x2 || Accelerometer1gScaleY
 
|-
 
|-
|0x85, 0x30,        ||   Report ID (48)
+
| 0xA || 0x2 || Accelerometer1gScaleZ
 
|-
 
|-
|0x09, 0x30,        ||   Usage (0x30)
+
| 0xC || 0x2 || Gyroscope0OffsetX
 
|-
 
|-
|0x75, 0x08,        ||   Report Size (8)
+
| 0xE || 0x2 || Gyroscope0OffsetY
 
|-
 
|-
|0x95, 0x30,        ||   Report Count (48)
+
| 0x10 || 0x2 || Gyroscope0OffsetZ
 
|-
 
|-
|0x81, 0x02,        ||   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+
| 0x12 || 0x2 || Gyroscope78rpmScaleX
 
|-
 
|-
|0x85, 0x31,        ||   Report ID (49)
+
| 0x14 || 0x2 || Gyroscope78rpmScaleY
 
|-
 
|-
|0x09, 0x31,        ||   Usage (0x31)
+
| 0x16 || 0x2 || Gyroscope78rpmScaleZ
 +
|}
 +
 
 +
=== Cal2 ===
 +
{| class="wikitable" border="1"
 
|-
 
|-
|0x75, 0x08,        ||  Report Size (8)
+
! Offset
 +
! Size
 +
! Description
 
|-
 
|-
|0x96, 0x69, 0x01,  ||   Report Count (361)
+
| 0x0 || 0x9 || [[#AnalogStickCalibrationValue|AnalogStickMainCalibrationValue]]
 
|-
 
|-
|0x81, 0x02,        ||   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+
| 0x9 || 0x9 || [[#AnalogStickCalibrationValue|AnalogStickSubCalibrationValue]]
 +
|}
 +
 
 +
==== AnalogStickCalibrationValue ====
 +
{| class="wikitable" border="1"
 
|-
 
|-
|0x85, 0x32,        ||  Report ID (50)
+
! Offset
 +
! Size
 +
! Description
 
|-
 
|-
|0x09, 0x32,        ||   Usage (0x32)
+
| 0x0 || 0x1 || AnalogStickCalXPositive
 
|-
 
|-
|0x75, 0x08,        ||   Report Size (8)
+
| 0x1 || 0x2 || AnalogStickCalYPositive
 
|-
 
|-
|0x96, 0x69, 0x01,  ||   Report Count (361)
+
| 0x3 || 0x1 || AnalogStickCalX0
 
|-
 
|-
|0x81, 0x02,        ||   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+
| 0x4 || 0x2 || AnalogStickCalY0
 
|-
 
|-
|0x85, 0x33,        ||   Report ID (51)
+
| 0x6 || 0x1 || AnalogStickCalXNegative
 
|-
 
|-
|0x09, 0x33,        ||   Usage (0x33)
+
| 0x7 || 0x2 || AnalogStickCalYNegative
 +
|}
 +
 
 +
=== Design ===
 +
{| class="wikitable" border="1"
 
|-
 
|-
|0x75, 0x08,        ||  Report Size (8)
+
! Offset
 +
! Size
 +
! Description
 
|-
 
|-
|0x96, 0x69, 0x01,  ||   Report Count (361)
+
| 0x0 || 0xC || [[#ControllerColor|ControllerColor]]
 
|-
 
|-
|0x81, 0x02,        ||   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+
| 0xC || 0x1 || [[#DesignVariation|DesignVariation]]
 +
|}
 +
 
 +
==== ControllerColor ====
 +
{| class="wikitable" border="1"
 
|-
 
|-
|0x85, 0x3F,        ||  Report ID (63)
+
! Offset
 +
! Size
 +
! Description
 
|-
 
|-
|0x05, 0x09,        ||   Usage Page (Button)
+
| 0x0 || 0x1 || MainColorR
 
|-
 
|-
|0x19, 0x01,        ||   Usage Minimum (0x01)
+
| 0x1 || 0x1 || MainColorG
 
|-
 
|-
|0x29, 0x10,        ||   Usage Maximum (0x10)
+
| 0x2 || 0x1 || MainColorB
 
|-
 
|-
|0x15, 0x00,        ||   Logical Minimum (0)
+
| 0x3 || 0x1 || SubColorR
 
|-
 
|-
|0x25, 0x01,        ||   Logical Maximum (1)
+
| 0x4 || 0x1 || SubColorG
 
|-
 
|-
|0x75, 0x01,        ||   Report Size (1)
+
| 0x5 || 0x1 || SubColorB
 
|-
 
|-
|0x95, 0x10,        ||   Report Count (16)
+
| 0x6 || 0x1 || 3rdColorR
 
|-
 
|-
|0x81, 0x02,        ||   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+
| 0x7 || 0x1 || 3rdColorG
 
|-
 
|-
|0x05, 0x01,        ||   Usage Page (Generic Desktop Ctrls)
+
| 0x8 || 0x1 || 3rdColorB
 
|-
 
|-
|0x09, 0x39,        ||   Usage (Hat switch)
+
| 0x9 || 0x1 || 4thColorR
 
|-
 
|-
|0x15, 0x00,        ||   Logical Minimum (0)
+
| 0xA || 0x1 || 4thColorG
 
|-
 
|-
|0x25, 0x07,        ||   Logical Maximum (7)
+
| 0xB || 0x1 || 4thColorB
 +
|}
 +
 
 +
==== DesignVariation ====
 +
{| class="wikitable" border="1"
 
|-
 
|-
|0x75, 0x04,        ||  Report Size (4)
+
! Value
 +
! Name
 
|-
 
|-
|0x95, 0x01,        ||   Report Count (1)
+
| 0x0 || LuciaJ, LagerJ
 
|-
 
|-
|0x81, 0x42,        ||   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
+
| 0x1 || LuciaE, LagerE
 
|-
 
|-
|0x05, 0x09,       ||   Usage Page (Button)
+
| 0x2 || LuciaU, LagerU
 +
|}
 +
 
 +
=== Model1 ===
 +
{| class="wikitable" border="1"
 
|-
 
|-
|0x75, 0x04,        ||  Report Size (4)
+
! Offset
 +
! Size
 +
! Description
 
|-
 
|-
|0x95, 0x01,        ||   Report Count (1)
+
| 0x0 || 0x6 || [[#SixAxisSensorModelValue|SixAxisSensorModelValue]]
 
|-
 
|-
|0x81, 0x01,        ||   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
+
| 0x6 || 0x12 || [[#AnalogStickModelValue|AnalogStickMainModelValue]]
 +
|}
 +
 
 +
==== SixAxisSensorModelValue ====
 +
{| class="wikitable" border="1"
 
|-
 
|-
|0x05, 0x01,        ||  Usage Page (Generic Desktop Ctrls)
+
! Offset
 +
! Size
 +
! Description
 
|-
 
|-
|0x09, 0x30,        ||   Usage (X)
+
| 0x0 || 0x2 || SixAxisHorizontalOffsetX
 
|-
 
|-
|0x09, 0x31,        ||   Usage (Y)
+
| 0x2 || 0x2 || SixAxisHorizontalOffsetY
 
|-
 
|-
|0x09, 0x33,        ||   Usage (Rx)
+
| 0x4 || 0x2 || SixAxisHorizontalOffsetZ
 +
|}
 +
 
 +
==== AnalogStickModelValue ====
 +
{| class="wikitable" border="1"
 
|-
 
|-
|0x09, 0x34,        ||  Usage (Ry)
+
! Offset
 +
! Size
 +
! Description
 
|-
 
|-
|0x16, 0x00, 0x00,  ||   Logical Minimum (0)
+
| 0x0 || 0x1 || AnalogStickModelNoise
 
|-
 
|-
|0x27, 0xFF, 0xFF, 0x00, 0x00,  ||   Logical Maximum (65534)
+
| 0x1 || 0x2 || AnalogStickModelTypicalStroke
 
|-
 
|-
|0x75, 0x10,        ||   Report Size (16)
+
| 0x3 || 0x1 || AnalogStickModelCenterDeadZoneSize
 
|-
 
|-
|0x95, 0x04,        ||   Report Count (4)
+
| 0x4 || 0x2 || AnalogStickModelCircuitDeadZoneScale
 
|-
 
|-
|0x81, 0x02,        ||   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+
| 0x6 || 0x1 || AnalogStickModelMinimumStrokeXPositive
 
|-
 
|-
|0x06, 0x01, 0xFF,  ||   Usage Page (Vendor Defined 0xFF01)
+
| 0x7 || 0x2 || AnalogStickModelMinimumStrokeYPositive
 
|-
 
|-
|0x85, 0x01,        ||   Report ID (1)
+
| 0x9 || 0x1 || AnalogStickModelMinimumStrokeXNegative
 
|-
 
|-
|0x09, 0x01,        ||   Usage (0x01)
+
| 0xA || 0x2 || AnalogStickModelMinimumStrokeYNegative
 
|-
 
|-
|0x75, 0x08,        ||   Report Size (8)
+
| 0xC || 0x1 || AnalogStickModelCenterRangeXPositive
 
|-
 
|-
|0x95, 0x30,        ||   Report Count (48)
+
| 0xD || 0x2 || AnalogStickModelCenterRangeYPositive
 
|-
 
|-
|0x91, 0x02,        ||   Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+
| 0xF || 0x1 || AnalogStickModelCenterRangeXNegative
 
|-
 
|-
|0x85, 0x10,        ||   Report ID (16)
+
| 0x10 || 0x2 || AnalogStickModelCenterRangeYNegative
 +
|}
 +
 
 +
=== Model2 ===
 +
{| class="wikitable" border="1"
 
|-
 
|-
|0x09, 0x10,        ||  Usage (0x10)
+
! Offset
 +
! Size
 +
! Description
 
|-
 
|-
|0x75, 0x08,        ||   Report Size (8)
+
| 0x0 || 0x12 || [[#AnalogStickModelValue|AnalogStickSubModelValue]]
 +
|}
 +
 
 +
== UserCalibration ==
 +
{| 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 || 0x10 || Reserved
 
|-
 
|-
|0x85, 0x11,        ||   Report ID (17)
+
| 0x10 || 0x16 || [[#UserCal1|UserCal1]]
 
|-
 
|-
|0x09, 0x11,        ||   Usage (0x11)
+
| 0x26 || 0x1A || [[#UserCal2|UserCal2]]
 
|-
 
|-
|0x75, 0x08,        ||   Report Size (8)
+
| 0x40 || 0xFC0 || Reserved
 +
|}
 +
 
 +
=== UserCal1 ===
 +
{| 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 || 0x2 || AnalogStickMainUserMagicNumber
 
|-
 
|-
|0x85, 0x12,        ||   Report ID (18)
+
| 0x2 || 0x9 || [[#AnalogStickCalibrationValue|AnalogStickMainUserCalibrationValue]]
 
|-
 
|-
|0x09, 0x12,        ||   Usage (0x12)
+
| 0xB || 0x2 || AnalogStickSubUserMagicNumber
 
|-
 
|-
|0x75, 0x08,        ||   Report Size (8)
+
| 0xD || 0x9 || [[#AnalogStickCalibrationValue|AnalogStickSubUserCalibrationValue]]
 +
|}
 +
 
 +
=== UserCal2 ===
 +
{| 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 || 0x2 || SixAxisUserCalibrationMagicNumber
 
|-
 
|-
|0xC0,              || End Collection
+
| 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"
! Color Name || Body HEX || Button HEX
+
! Developer Kit Joy-Con || Release || Body HEX || Button HEX
 +
|-
 +
| Black / ブラック || 2015.10.16 || #313131 || #0F0F0F
 +
|-
 +
! Standard Retail Joy-Con Color || Release || Body HEX || Button HEX
 +
|-
 +
| Gray / グレー || 2017.03.03 || #828282 || #0F0F0F
 +
|-
 +
| Neon Red / ネオンレッド || 2017.03.03 || #FF3C28 || #1E0A0A
 +
|-
 +
| Neon Blue / ネオンブルー || 2017.03.03 || #0AB9E6 || #001E1E
 +
|-
 +
| Neon Yellow / ネオンイエロー || 2017.06.16 || #E6FF00 || #142800
 +
|-
 +
| Neon Green / ネオングリーン || 2017.07.21 || #1EDC00 || #002800
 +
|-
 +
| Neon Pink / ネオンピンク || 2017.07.21 || #FF3278 || #28001E
 +
|-
 +
| Red / レッド || 2017.10.27 || #E10F00 || #280A0A
 +
|-
 +
| Blue / ブルー || 2019.10.04 || #4655F5 || #00000A
 +
|-
 +
| Neon Purple / ネオンパープル || 2019.10.04 || #B400E6 || #140014
 +
|-
 +
| Neon Orange / ネオンオレンジ || 2019.10.04 || #FAA005 || #0F0A00
 +
|-
 +
| 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
 +
|-
 +
| Nintendo Switch Gray Joy-Con / Nintendo Switch グレー || 2017.03.03 || #828282 || #0F0F0F
 +
|-
 +
| Nintendo Switch Neon-Blue Left Joy-Con / Nintendo Switch ネオンブルー・ネオンレッド || 2017.03.03 || #0AB9E6 || #001E1E
 +
|-
 +
| Nintendo Switch Neon-Red Right Joy-Con / Nintendo Switch ネオンブルー・ネオンレッド || 2017.03.03 || #FF3C28 || #1E0A0A
 +
|-
 +
| Splatoon 2 Edition Neon-Green Left Joy-Con / スプラトゥーン2 || 2017.07.21 || #1EDC00 || #002800
 +
|-
 +
| Splatoon 2 Edition Neon-Pink Right Joy-Con / スプラトゥーン2 || 2017.07.21 || #FF3278 || #28001E
 +
|-
 +
| Super Mario Odyssey Edition Red Joy-Con / スーパーマリオ オデッセイ || 2017.10.27 || #E10F00 || #280A0A
 +
|-
 +
| Super Smash Bros. Ultimate Edition Gray Joy-Con / 大乱闘スマッシュブラザーズ || 2018.11.02 || #828282 || #0F0F0F
 
|-
 
|-
| Gray || #828282 || #0F0F0F
+
| Pokémon: Let's Go! Edition Eevee-Brown Left Joy-Con / ポケットモンスター Let's Go! イーブイ || 2018.11.16 || #C88C32 || #281900
 
|-
 
|-
| Neon Red || #FF3C28 || #1E0A0A
+
| Pokémon: Let's Go! Edition Pikachu-Yellow Right Joy-Con / ポケットモンスター Let's Go! ピカチュウ || 2018.11.16 || #FFDC00 || #322800
 
|-
 
|-
| Neon Blue || #0AB9E6 || #001E1E
+
| *Nintendo Labo Creators Contest Edition "Cardboard"-Colored Joy-Con || 2018. (...?) || #D7AA73 || #1E1914
 
|-
 
|-
| Neon Yellow || #E6FF00 || #142800
+
| **Dragon Quest XI S Lotto Edition Royal-Blue Joy-Con / ドラゴン クエスト XI S (ロト版) || 2019.09.27 || #1473FA || #00000F
 
|-
 
|-
| Neon Green || #1EDC00 || #002800
+
| **Disney Tsum Tsum Festival Edition Neon-Purple Left Joy-Con / ディズニー ツムツム フェスティバル || 2019.10.10 || #B400E6 || #140014
 
|-
 
|-
| Neon Pink || #FF3278 || #28001E
+
| **Disney Tsum Tsum Festival Edition Neon-Pink Right Joy-Con / ディズニー ツムツム フェスティバル || 2019.10.10 || #FF3278 || #28001E
 
|-
 
|-
| Mario Red || #E10F00 || #280A0A
+
| Animal Crossing: New Horizons Edition Pastel-Green Left Joy-Con / あつまれ どうぶつの森 || 2020.03.13 || #82FF96 || #0A1E0A
 
|-
 
|-
| Black Pro Controller || #323232 or #323231 || ?
+
| Animal Crossing: New Horizons Edition Pastel-Blue Right Joy-Con / あつまれ どうぶつの森 || 2020.03.13 || #96F5F5 || #0A1E28
 +
|-
 +
| Fortnite Wildcat Edition Yellow Left Joy-Con / フォートナイト ワイルドキャット(山猫) || 2020.10.30 || #FFCC00 || #1A1100
 +
|-
 +
| Fortnite Wildcat Edition Dark-Blue Right Joy-Con / フォートナイト ワイルドキャット(山猫) || 2020.10.30 || #0084FF || #000F1E
 +
|-
 +
| Mario Red × Blue Edition Red Joy-Con / マリオ レッド × ブルー || 2021.02.12 || #F04614 || #1E1914
 +
|-
 +
| Monster Hunter Rise Edition Gray Joy-Con / モンスターハンターライズ || 2021.03.26 || #818282 || #0E0F0F
 +
|-
 +
| Fortnite Fleet Force Edition Dark-Blue Left Joy-Con / フォートナイト フリート フォース(力の艦隊) || 2021.06.04 || #0084FF || #000F1E
 +
|-
 +
| Fortnite Fleet Force Edition Yellow Right Joy-Con / フォートナイト フリート フォース(力の艦隊) || 2021.06.04 || #FFCC00 || #1A1100
 +
|-
 +
| Legend of Zelda: Skyward Sword Edition Dark-Blue Left Joy-Con / ゼルダの伝説 スカイウォードソード || 2021.07.16 || #2D50F0 || #1E0F46
 +
|-
 +
| Legend of Zelda: Skyward Sword Edition Dark-Purple Right Joy-Con / ゼルダの伝説 スカイウォードソード || 2021.07.16 || #500FC8 || #00051E
 +
|-
 +
| Nintendo Switch OLED Edition White Joy-Con / Nintendo Switch (有機ELモデル) ホワイト || 2021.10.08 || #E6E6E6 || #323232
 +
|-
 +
| Splatoon 3 OLED Edition Blue-Gradient Left Joy-Con / (有機ELモデル) スプラトゥーン3 || 2022.08.26 || #6455F5 || #28282D
 +
|-
 +
| Splatoon 3 OLED Edition Neon-Yellow-Gradient Right Joy-Con / (有機ELモデル) スプラトゥーン3 || 2022.08.26 || #C3FA05 || #1E1E28
 +
|-
 +
| Pokémon: Scarlet × Violet OLED Edition Scarlet Left Joy-Con / (有機ELモデル) ポケットモンスター スカーレット・バイオレット || 2022.11.04 || #F07341 || #322D1E
 +
|-
 +
| Pokémon: Scarlet × Violet OLED Edition Violet Right Joy-Con / (有機ELモデル) ポケットモンスター スカーレット・バイオレット || 2022.11.04 || #9650AA || #32322D
 +
|-
 +
| Legend of Zelda: Tears of the Kingdom Edition Gold Joy-Con / (有機ELモデル) ゼルダの伝説 ティアーズ オブ ザ キングダム || 2023.04.28 || #D2BE69 || #32322D
 +
|-
 +
| // *(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]
 

Latest revision as of 21:06, 29 September 2024

Joy-Con is the name for the Switch's primary game controllers. The Joy-Con are internally called Sakyo (Left) and Ukyo (Right).

Hardware

Sakyo

Component Description
SoC Broadcom BCM20734
IMU STMicroelectronics LSM6DS3H
PMIC Texas Instruments BQ24072
Storage Macronix International MX25U4033E 4 Mb CMOS Flash

Ukyo

Component Description
SoC Broadcom BCM20734
MCU STMicroelectronics STM32P411
NFC STMicroelectronics ST21NFCB
IMU STMicroelectronics LSM6DS3H
PMIC Texas Instruments BQ24072
Storage Macronix International MX25U4033E 4 Mb CMOS Flash

Protocol

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

NwcpPacket

Offset Size Description
0x0 0x5 HciHeader
0x5 0x7 NwcpHeader
0xC Variable Data

HciHeader

This is a 5-byte packet header used by Broadcom's HCI Control Protocol.

Offset Size Description
0x0 0x1 PacketType (always 0x19)
0x1 0x1 CommandCode
0x2 0x1 GroupCode (always 0x03)
0x3 0x2 PacketLength

CommandCode

Value Description
0x1 SendToDevice
0x81 ReceiveFromDevice

NwcpHeader

Offset Size Description
0x0 0x1 CommandType
0x1 0x1 DeviceCommand
0x2 0x2 DataSize
0x4 0x1 CommandResult
0x5 0x1 DataCrc (CRC8 over Data for DataSize bytes)
0x6 0x1 HeaderCrc (CRC8 over the previous 6 bytes)

CommandType

Value Description
0x43 ReportOut
0x53 ReportIn
0x91 DeviceCommandIn
0x92 Hid
0x93
0x94 DeviceCommandOut
0x95
0x9A Fifty
0xA3 ReportComplete
0xA5 Handshake

HidCommandInputReport

This is a 0x31-byte struct.

Offset Size Description
0x0 0x1 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 CommandId
0xF 0x22 CommandReply

HidMcuUpdateInputReport

This is a 0x30-byte struct.

Offset Size Description
0x0 0x1 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.

Offset Size Description
0x0 0x1 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.

Offset Size Description
0x0 0x1 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.

Offset Size Description
0x0 0x1 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.

Offset Size Description
0x0 0x1 ReportId
0x1 0x2 ButtonStatus
0x3 0x1 StickHatData
0x4 0x4 LeftAnalogStickData
0x8 0x4 RightAnalogStickData

HidCommandOutputReport

This is a 0x31-byte struct.

Offset Size Description
0x0 0x1 ReportId
0x1 0x1 PacketNumber
0x2 0x8 MotorData
0xA 0x1 CommandId
0xB 0x26 CommandData

HidMcuUpdateOutputReport

This is a 0x13E-byte struct.

Offset Size Description
0x0 0x1 ReportId
0x1 0x1 PacketNumber
0x2 0x8 MotorData
0xA 0x134 McuUpdateData

HidBasicOutputReport

This is a 0xA-byte struct.

Offset Size Description
0x0 0x1 ReportId
0x1 0x1 PacketNumber
0x2 0x8 MotorData

HidMcuOutputReport

This is a 0x30-byte struct.

Offset Size Description
0x0 0x1 ReportId
0x1 0x1 PacketNumber
0x2 0x8 MotorData
0xA 0x26 McuData

HidAttachmentOutputReport

This is a 0x30-byte struct.

Offset Size Description
0x0 0x1 ReportId
0x1 0x1 PacketNumber
0x2 0x8 MotorData
0xA 0x26 AttachmentData

HidReportId

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 OtaWriteReport
0x74 OtaEraseReport
0x75 OtaLaunchReport
0x80 ExtGripOutputReport
0x81 ExtGripInputReport
0x82

HidMotorData

Offset Size Description
0x0 0x4 JoyLeftVibrationAmFmPackTag
0x4 0x8 JoyRightVibrationAmFmPackTag

VibrationAmFmPackTag

This is "nn::xcd::VibrationAmFmPackTag". This is a BitPack object.

VibrationAmFmPackFormatZero

Bits Description
0-29 Reserved
30-31 PackFormat (must be 0)

VibrationAmFmPackFormatOne

Bits Description
0-19 Reserved
20-24 ChannelCodesHigh
25-29 ChannelCodesLow
30-31 PackFormat (must be 1)

VibrationAmFmPackFormatOne28bit

Bits Description
0-1 Reserved
2-8 ChannelFrequencyHigh
9-15 ChannelAmplitudeHigh
16-22 ChannelFrequencyLow
23-29 ChannelAmplitudeLow
30-31 PackFormat (must be 1)

VibrationAmFmPackFormatTwo

Bits Description
0-9 Reserved
10-14 VibrationCodesHigh
15-19 VibrationCodesLow
20-24 ChannelCodesHigh
25-29 ChannelCodesLow
30-31 PackFormat (must be 2)

VibrationAmFmPackFormatTwo14bitLow

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

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

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

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 from output reports. Depending on the packing format, these tags are then converted to an array of AmFmCodes as follows.

AmFm5BitCodes

Converted from VibrationAmFmPackFormatOne, VibrationAmFmPackFormatTwo or VibrationAmFmPackFormatThree.

Offset Size Description
0x0 0x4 (ChannelCodesLow & 0x1F)
0x4 0x4 (ChannelCodesHigh & 0x1F)
0x8 0x4 (VibrationCodesLow0 & 0x1F)
0xC 0x4 (VibrationCodesHigh0 & 0x1F)
0x10 0x4 (VibrationCodesLow1 & 0x1F)
0x14 0x4 (VibrationCodesHigh1 & 0x1F)

AmFm7BitOneCodes

Converted from VibrationAmFmPackFormatOne28bit.

Offset Size Description
0x0 0x4 (((ChannelFrequencyLow & 0x7F) << 8) | (ChannelAmplitudeLow & 0x7F) | 0x8080)
0x4 0x4 (((ChannelFrequencyHigh & 0x7F) << 8) | (ChannelAmplitudeHigh & 0x7F) | 0x8080)

AmFm7BitTwoCodes

Converted from VibrationAmFmPackFormatTwo14bitLow or VibrationAmFmPackFormatTwo14bitHigh.

Offset Size Description
0x0 0x4 IsChannelHigh ? (VibrationCodesLow0 & 0x1F) : (((ChannelFrequency & 0x7F) << 8) | (ChannelAmplitude & 0x7F) | 0x8080)
0x4 0x4 IsChannelHigh ? (((ChannelFrequency & 0x7F) << 8) | (ChannelAmplitude & 0x7F) | 0x8080) : (VibrationCodesHigh0 & 0x1F)
0x8 0x4 (VibrationCodesLow1 & 0x1F)
0xC 0x4 (VibrationCodesHigh1 & 0x1F)
0x10 0x4 Always 0x18
0x14 0x4 Always 0x18

AmFm7BitThreeCodes

Converted from VibrationAmFmPackFormatThree7bit.

Offset Size Description
0x0 0x4 IsChannelHigh ? 0x18 : (IsFm ? (((ChannelAmFm & 0x7F) << 8) | 0x8000) : ((ChannelAmFm & 0x7F) | 0x80))
0x4 0x4 IsChannelHigh ? (IsFm ? (((ChannelAmFm & 0x7F) << 8) | 0x8000) : ((ChannelAmFm & 0x7F) | 0x80)) : 0x18
0x8 0x4 (VibrationCodesLow0 & 0x1F)
0xC 0x4 (VibrationCodesHigh0 & 0x1F)
0x10 0x4 (VibrationCodesLow1 & 0x1F)
0x14 0x4 (VibrationCodesHigh1 & 0x1F)

DeviceCommand

Value Description
0x01 Inquiry
0x02 WriteChargerSettings
0x03 ReadChargerSettings
0x04 AttachmentEnable
0x05 IsAttachmentEnabled
0x06 GetBatteryVoltage
0x07 ReadWakeUpReason
0x10 CreateHidConnection
0x11 DisconnectHid
0x12 SetHidInterval
0x13 GetHidInterval
0x18 Pairing
0x20 UpdateUartBaudRate
0x40 Test

HidCommand

Value Description
0x01 PairingOut
0x02 GetDeviceInfo
0x03 SetDataFormat
0x04 LRButtonDetection
0x05 Page
0x06 Reset
0x07 ClearPairingInfo
0x08 Shipment
0x10 SerialFlashRead
0x11 SerialFlashWrite
0x12 SerialFlashSectorErase
0x20 McuReset
0x21 McuWrite
0x22 McuResume
0x24 McuPollingEnable
0x25 McuPollingDisable
0x28 AttachmentWrite
0x29 AttachmentRead
0x2A AttachmentEnable
0x2B GetAttachmentInfo
0x30 SetIndicatorLed
0x31 GetIndicatorLed
0x38 SetNotificationLed
0x40 SensorSleep
0x41 SensorConfig
0x42 SensorWrite
0x43 SensorRead
0x48 MotorEnable
0x50 GetBatteryVoltage
0x51 WriteChargeSetting
0x52 ReadChargeSetting
0x58 ExtDevWrite
0x59 GetExtDevInfo
0x5A ExtDevPollingEnable
0x5B ExtDevPollingDisable
0x5C ExtDevInFormatConfig
0x5D
0x60 InternalAttachmentWrite
0x61
0x62 InternalAttachmentEnable
0x63 InternalAttachmentPollingEnable
0x64 InternalAttachmentPollingDisable
0x7A AddButtonPlayData
0x7B StartStopButtonPlayData
0x80 Ack
0x81 PairingIn
0x82 DeviceInfo
0x83 LRButtonElapsedTime
0x90 SerialFlashData
0xA0 McuData
0xA8 AttachmentData
0xA9 AttachmentInfo
0xAA InternalAttachmentRead
0xB0 IndicatorLed
0xC0 SensorData
0xD0 BatteryVoltage
0xD8 ExtDevRead
0xD9 ExtDevInfo

ExtGripCommand

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:

Offset Size Description
0x0 0x1000 StaticSection
0x1000 0x1000 FailsafeSection
0x2000 0x1000 VolatileSection
0x3000 0x1000 VolatileSectionBackup1
0x4000 0x1000 VolatileSectionBackup2
0x5000 0x1000 ShipmentInfo
0x6000 0x1000 FactoryConfiguration
0x7000 0x1000 Reserved
0x8000 0x1000 UserCalibration
0x9000 0x7000 Reserved
0x10000 0x18000 DynamicSection1
0x28000 0x18000 DynamicSection2
0x40000 0x40000 Reserved

FailsafeSection

Offset Size Description
0x0 0xFF4 Reserved
0xFF4 0x8 Signature
0xFFC 0x4 Ds2Offset

VolatileSection

Offset Size Description
0x0 0x26
0x26 0xFDA Reserved

ShipmentInfo

Offset Size Description
0x0 0x1 Shipment
0x1 0xFFF Reserved

FactoryConfiguration

Offset Size Description
0x0 0x10 IdentificationCode
0x10 0x2 Reserved
0x12 0x1 DeviceType
0x13 0x1 BoardRevision
0x14 0x7 Reserved
0x1B 0x1 FormatVersion
0x1C 0x4 Reserved
0x20 0x18 Cal1
0x38 0x5 Reserved
0x3D 0x12 Cal2
0x4F 0x1 Reserved
0x50 0xD Design
0x5D 0x23 Reserved
0x80 0x18 Model1
0x98 0x12 Model2
0xAA 0x1 Reserved
0xAB 0x1 AccelerometerAxisAssignment
0xAC 0x1 GyroscopeAxisAssignment
0xAD 0x1 AnalogStickMainAxisAssignment
0xAE 0x1 AnalogStickSubAxisAssignment
0xAF 0x151 Reserved
0x200 0x2 BatteryVoltage
0x202 0xB7E Reserved
0xD80 0x2 TarragonVid
0xD82 0x2 TarragonPid
0xD84 0x7C Reserved
0xE00 0x100 InspectionLog
0xF00 0x100 Reserved

DeviceType

Value Description
0x01 JoyConLeft
0x02 JoyConRight
0x03 SwitchProController
0x04 MiyabiLeft
0x05 MiyabiRight
0x06 Tarragon
0x07 LarkH1
0x08 LarkH2
0x09 LarkNL
0x0A LarkNR
0x0B Lucia
0x0C [12.0.0+] Lagon
0x0D [13.0.0+] Lager
0x0E [14.0.0+] Tarragon2
0x21 FiftyL
0x22 FiftyR

Cal1

Offset Size Description
0x0 0x18 SixAxisSensorCalibrationValue

SixAxisSensorCalibrationValue

Offset Size Description
0x0 0x2 Accelerometer0OffsetX
0x2 0x2 Accelerometer0OffsetY
0x4 0x2 Accelerometer0OffsetZ
0x6 0x2 Accelerometer1gScaleX
0x8 0x2 Accelerometer1gScaleY
0xA 0x2 Accelerometer1gScaleZ
0xC 0x2 Gyroscope0OffsetX
0xE 0x2 Gyroscope0OffsetY
0x10 0x2 Gyroscope0OffsetZ
0x12 0x2 Gyroscope78rpmScaleX
0x14 0x2 Gyroscope78rpmScaleY
0x16 0x2 Gyroscope78rpmScaleZ

Cal2

Offset Size Description
0x0 0x9 AnalogStickMainCalibrationValue
0x9 0x9 AnalogStickSubCalibrationValue

AnalogStickCalibrationValue

Offset Size Description
0x0 0x1 AnalogStickCalXPositive
0x1 0x2 AnalogStickCalYPositive
0x3 0x1 AnalogStickCalX0
0x4 0x2 AnalogStickCalY0
0x6 0x1 AnalogStickCalXNegative
0x7 0x2 AnalogStickCalYNegative

Design

Offset Size Description
0x0 0xC ControllerColor
0xC 0x1 DesignVariation

ControllerColor

Offset Size Description
0x0 0x1 MainColorR
0x1 0x1 MainColorG
0x2 0x1 MainColorB
0x3 0x1 SubColorR
0x4 0x1 SubColorG
0x5 0x1 SubColorB
0x6 0x1 3rdColorR
0x7 0x1 3rdColorG
0x8 0x1 3rdColorB
0x9 0x1 4thColorR
0xA 0x1 4thColorG
0xB 0x1 4thColorB

DesignVariation

Value Name
0x0 LuciaJ, LagerJ
0x1 LuciaE, LagerE
0x2 LuciaU, LagerU

Model1

Offset Size Description
0x0 0x6 SixAxisSensorModelValue
0x6 0x12 AnalogStickMainModelValue

SixAxisSensorModelValue

Offset Size Description
0x0 0x2 SixAxisHorizontalOffsetX
0x2 0x2 SixAxisHorizontalOffsetY
0x4 0x2 SixAxisHorizontalOffsetZ

AnalogStickModelValue

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

Offset Size Description
0x0 0x12 AnalogStickSubModelValue

UserCalibration

Offset Size Description
0x0 0x10 Reserved
0x10 0x16 UserCal1
0x26 0x1A UserCal2
0x40 0xFC0 Reserved

UserCal1

Offset Size Description
0x0 0x2 AnalogStickMainUserMagicNumber
0x2 0x9 AnalogStickMainUserCalibrationValue
0xB 0x2 AnalogStickSubUserMagicNumber
0xD 0x9 AnalogStickSubUserCalibrationValue

UserCal2

Offset Size Description
0x0 0x2 SixAxisUserCalibrationMagicNumber
0x2 0x18 SixAxisUserCalibrationValue

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.

Developer Kit Joy-Con Release Body HEX Button HEX
Black / ブラック 2015.10.16 #313131 #0F0F0F
Standard Retail Joy-Con Color Release Body HEX Button HEX
Gray / グレー 2017.03.03 #828282 #0F0F0F
Neon Red / ネオンレッド 2017.03.03 #FF3C28 #1E0A0A
Neon Blue / ネオンブルー 2017.03.03 #0AB9E6 #001E1E
Neon Yellow / ネオンイエロー 2017.06.16 #E6FF00 #142800
Neon Green / ネオングリーン 2017.07.21 #1EDC00 #002800
Neon Pink / ネオンピンク 2017.07.21 #FF3278 #28001E
Red / レッド 2017.10.27 #E10F00 #280A0A
Blue / ブルー 2019.10.04 #4655F5 #00000A
Neon Purple / ネオンパープル 2019.10.04 #B400E6 #140014
Neon Orange / ネオンオレンジ 2019.10.04 #FAA005 #0F0A00
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
Nintendo Switch Gray Joy-Con / Nintendo Switch グレー 2017.03.03 #828282 #0F0F0F
Nintendo Switch Neon-Blue Left Joy-Con / Nintendo Switch ネオンブルー・ネオンレッド 2017.03.03 #0AB9E6 #001E1E
Nintendo Switch Neon-Red Right Joy-Con / Nintendo Switch ネオンブルー・ネオンレッド 2017.03.03 #FF3C28 #1E0A0A
Splatoon 2 Edition Neon-Green Left Joy-Con / スプラトゥーン2 2017.07.21 #1EDC00 #002800
Splatoon 2 Edition Neon-Pink Right Joy-Con / スプラトゥーン2 2017.07.21 #FF3278 #28001E
Super Mario Odyssey Edition Red Joy-Con / スーパーマリオ オデッセイ 2017.10.27 #E10F00 #280A0A
Super Smash Bros. Ultimate Edition Gray Joy-Con / 大乱闘スマッシュブラザーズ 2018.11.02 #828282 #0F0F0F
Pokémon: Let's Go! Edition Eevee-Brown Left Joy-Con / ポケットモンスター Let's Go! イーブイ 2018.11.16 #C88C32 #281900
Pokémon: Let's Go! Edition Pikachu-Yellow Right Joy-Con / ポケットモンスター Let's Go! ピカチュウ 2018.11.16 #FFDC00 #322800
*Nintendo Labo Creators Contest Edition "Cardboard"-Colored Joy-Con 2018. (...?) #D7AA73 #1E1914
**Dragon Quest XI S Lotto Edition Royal-Blue Joy-Con / ドラゴン クエスト XI S (ロト版) 2019.09.27 #1473FA #00000F
**Disney Tsum Tsum Festival Edition Neon-Purple Left Joy-Con / ディズニー ツムツム フェスティバル 2019.10.10 #B400E6 #140014
**Disney Tsum Tsum Festival Edition Neon-Pink Right Joy-Con / ディズニー ツムツム フェスティバル 2019.10.10 #FF3278 #28001E
Animal Crossing: New Horizons Edition Pastel-Green Left Joy-Con / あつまれ どうぶつの森 2020.03.13 #82FF96 #0A1E0A
Animal Crossing: New Horizons Edition Pastel-Blue Right Joy-Con / あつまれ どうぶつの森 2020.03.13 #96F5F5 #0A1E28
Fortnite Wildcat Edition Yellow Left Joy-Con / フォートナイト ワイルドキャット(山猫) 2020.10.30 #FFCC00 #1A1100
Fortnite Wildcat Edition Dark-Blue Right Joy-Con / フォートナイト ワイルドキャット(山猫) 2020.10.30 #0084FF #000F1E
Mario Red × Blue Edition Red Joy-Con / マリオ レッド × ブルー 2021.02.12 #F04614 #1E1914
Monster Hunter Rise Edition Gray Joy-Con / モンスターハンターライズ 2021.03.26 #818282 #0E0F0F
Fortnite Fleet Force Edition Dark-Blue Left Joy-Con / フォートナイト フリート フォース(力の艦隊) 2021.06.04 #0084FF #000F1E
Fortnite Fleet Force Edition Yellow Right Joy-Con / フォートナイト フリート フォース(力の艦隊) 2021.06.04 #FFCC00 #1A1100
Legend of Zelda: Skyward Sword Edition Dark-Blue Left Joy-Con / ゼルダの伝説 スカイウォードソード 2021.07.16 #2D50F0 #1E0F46
Legend of Zelda: Skyward Sword Edition Dark-Purple Right Joy-Con / ゼルダの伝説 スカイウォードソード 2021.07.16 #500FC8 #00051E
Nintendo Switch OLED Edition White Joy-Con / Nintendo Switch (有機ELモデル) ホワイト 2021.10.08 #E6E6E6 #323232
Splatoon 3 OLED Edition Blue-Gradient Left Joy-Con / (有機ELモデル) スプラトゥーン3 2022.08.26 #6455F5 #28282D
Splatoon 3 OLED Edition Neon-Yellow-Gradient Right Joy-Con / (有機ELモデル) スプラトゥーン3 2022.08.26 #C3FA05 #1E1E28
Pokémon: Scarlet × Violet OLED Edition Scarlet Left Joy-Con / (有機ELモデル) ポケットモンスター スカーレット・バイオレット 2022.11.04 #F07341 #322D1E
Pokémon: Scarlet × Violet OLED Edition Violet Right Joy-Con / (有機ELモデル) ポケットモンスター スカーレット・バイオレット 2022.11.04 #9650AA #32322D
Legend of Zelda: Tears of the Kingdom Edition Gold Joy-Con / (有機ELモデル) ゼルダの伝説 ティアーズ オブ ザ キングダム 2023.04.28 #D2BE69 #32322D
// *(Prize Awarded for Labo Contest - No Retail) // **(Available Japan Only)