Joy-Con 2 / Pro 2 / NSO GC2
Status: hardware-validated (Joy-Con 2 L; the other variants share the protocol).
The second-generation controllers - Joy-Con 2 L/R, Pro Controller 2, and the NSO GameCube 2 - use Bluetooth Low Energy, not Bluetooth Classic HID. They are the only Nintendo controllers here with a magnetometer, enabling 9-DOF fusion.
Hardware
Section titled “Hardware”| Component | Part | Range / scale |
|---|---|---|
| IMU (6-axis) | TDK InvenSense ICM-42670-P | gyro ≈ ±1994 dps (0.001062 rad/s per raw), accel ±8 g (4096 = 1 g) |
| Magnetometer | Asahi Kasei AK09919 | 3-axis, ±4900 µT, 16-bit (~0.15 µT resolution) |
| Battery charger | TI bq25619 | |
| Audio amp | MAX98388/89 |
The right Joy-Con also carries an NFC controller, which is irrelevant to tracking.
Pairing
Section titled “Pairing”The “special handshake” that blocks the Windows pairing dialog is a UX quirk, not cryptography. Connect to the unpaired, advertising controller directly - do not pair it through the OS Bluetooth settings. Rapid reconnects lock the radio for several minutes, so the driver honors a backoff.
Advertisement filter
Section titled “Advertisement filter”- Nintendo manufacturer ID
0x0553. - Manufacturer-data prefix
01 00 03 7E(first 4 bytes). - Discovery must not gate on advert byte 4 (an unverified per-variant guess
that leaves controllers undiscoverable). Any
0553+01 00 03 7Eadvert is treated as a Switch 2 device; Left/Right is resolved from the USB PID scanned in the advert.
GATT topology
Section titled “GATT topology”Service ab7de9be-89fe-49ad-828f-118f09df7fd0 (handle 0x0008):
| Role | UUID | Handle |
|---|---|---|
Common input report (0x05) |
ab7de9be-89fe-49ad-828f-118f09df7fd2 |
0x000A |
Joy-Con L input (0x07) |
cc1bbbb5-7354-4d32-a716-a81cb241a32a |
0x000E |
Joy-Con R input (0x08) |
d5a9e01e-2ffc-4cca-b20c-8b67142bf442 |
0x000E |
Pro Controller 2 input (0x09) |
7492866c-ec3e-4619-8258-32755ffcc0f8 |
0x000E |
NSO GC2 input (0x0A) |
8261cba1-9435-420c-84d6-f0c75a2c8e4d |
0x000E |
| Write commands | 649d4ac9-8eb7-4e6c-af44-1ea54fe5f005 |
- |
| Output / rumble | 289326cb-a471-485d-a8f4-240c14f18241 |
0x0012 |
| Command response (notify) | c765a961-d9d8-4d36-a20a-5315b111836a |
- |
USB PIDs
Section titled “USB PIDs”| PID | Device |
|---|---|
2066 |
Joy-Con 2 Right |
2067 |
Joy-Con 2 Left |
2068 |
Joy-Con 2 Pair (grip mode) |
2069 |
Switch 2 Pro Controller |
2073 |
Switch 2 GameCube Controller |
Input report 0x05 (62-byte common format)
Section titled “Input report 0x05 (62-byte common format)”| Offset | Size | Field |
|---|---|---|
0x00 |
4 | Counter (uint32 LE, +1 per packet - use for drop detection) |
0x04 |
4 | Buttons bitfield |
0x0A |
3 | Left stick (12-bit packed) |
0x0D |
3 | Right stick |
0x10 |
8 | Mouse data (Joy-Con only) |
0x19 |
6 | Magnetometer X/Y/Z (int16 LE, feature bit 7) |
0x1F |
2 | Battery voltage mV (uint16 LE) |
0x21 |
1 | Charging state (0x34 charging, 0x20 full) |
0x22 |
2 | Battery current (feature bit 5) |
0x2A |
18 | Motion block (feature bit 2) |
0x3C |
1 | Left trigger (NSO GC2 only) |
0x3D |
1 | Right trigger (NSO GC2 only) |
Motion block (0x2A)
Section titled “Motion block (0x2A)”| Sub-offset | Abs | Size | Field |
|---|---|---|---|
| +0 | 0x2A |
4 | Motion timestamp |
| +4 | 0x2E |
2 | Temperature (25 + raw/127 °C) |
| +6 | 0x30 |
2 | Accel X (int16 LE, 4096 = 1 g) |
| +8 | 0x32 |
2 | Accel Y |
| +10 | 0x34 |
2 | Accel Z |
| +12 | 0x36 |
2 | Gyro X (int16 LE) |
| +14 | 0x38 |
2 | Gyro Y |
| +16 | 0x3A |
2 | Gyro Z |
Effective IMU rate is ~62 Hz (16 ms packets) on the common report.
Connection interval
Section titled “Connection interval”These controllers never issue a Connection Parameter Update Request - the host must request a short interval. A default host interval of ~60 ms caps reporting at ~16.7 Hz; requesting a throughput-optimized interval (~5 ms) restores the full rate. On Windows 11 the driver requests it right after connect; on Linux the interval is negotiated by the system, tuned via the Bluetooth config. The path degrades gracefully - it streams at whatever interval the OS grants rather than failing.
Commands
Section titled “Commands”Written to the command characteristic (WriteWithoutResponse, 8-byte minimum
pad). Header: CMD | 0x91 | 0x01 | SUBCMD | 0x00 | LEN | 0x00 | 0x00 | DATA…
| CMD | Purpose |
|---|---|
0x02 |
Flash read/write (factory / user calibration) |
0x03 |
Init (input-report select) |
0x06 |
Shutdown / reboot |
0x09 |
Player LED |
0x0A |
Vibration / sound preset |
0x0B |
Battery query |
0x0C |
Feature select (enable IMU / mag) |
0x15 |
DO NOT USE - pairing-persistence write; the wrong call can brick pairing |
Feature bitmask (CMD 0x0C, data byte 0)
Section titled “Feature bitmask (CMD 0x0C, data byte 0)”| Bit | Mask | Feature |
|---|---|---|
| 0 | 0x01 |
Button state |
| 1 | 0x02 |
Analog sticks |
| 2 | 0x04 |
IMU (accel + gyro) |
| 4 | 0x10 |
Mouse (Joy-Con only) |
| 5 | 0x20 |
Rumble + battery current |
| 7 | 0x80 |
Magnetometer (9-DOF) |
Canonical enable sequence:
0C 91 01 02 00 04 00 00 <mask> 00 00 00- set feature mask- wait 500 ms
0C 91 01 04 00 04 00 00 <mask> 00 00 00- activate features
Masks: 0x04 IMU, 0x80 mag, 0xFF all.
Memory map (CMD 0x02 flash read)
Section titled “Memory map (CMD 0x02 flash read)”| Address | Content |
|---|---|
0x13000-0x14FFF |
Factory data block |
0x13002 |
Serial number |
0x13012 |
USB product ID (authoritative variant id) |
0x13040 + 4/8/12 |
3 × float32 gyro bias |
0x13080 / 0x130C0 |
left / right stick calibration |
0x13100 + 12/16/20 |
3 × float32 accel bias |
0x1FA000 |
BLE pairing info (host + link key, up to 2 hosts) |
0x1FD000 |
shipment flag |
The chip applies factory IMU calibration internally, so there is no host-consumable cal to read for tracking - fusion’s rest-bias estimator covers it.
Axis convention
Section titled “Axis convention”Body frame (x, z, -y), gravity +Z face-up. Standalone (non-grip) variants add:
| Variant | Remap |
|---|---|
| Joy-Con 2 L | (x, y, z) → ( z, y, -x) (+90° about Y) |
| Joy-Con 2 R | (x, y, z) → (-z, y, x) (−90° about Y) |
The magnetometer shares the IMU’s PCB, so it uses the same base remap.
Reset gestures
Section titled “Reset gestures”| Gesture | Bit | Action |
|---|---|---|
| Home (short) | byte 0x05, bit 0x10 |
Reset Yaw |
| Home (≥1 s hold) | byte 0x05, bit 0x10 (timed) |
Reset Full |
| Capture | byte 0x05, bit 0x20 |
Reset Yaw |
- A separate high-rate motion block (reports
0x07-0x0A, 40 bytes) is encrypted and unusable without a pairing-derived key, so the plaintext common report0x05is used instead. - Motion-based auto-calibration re-estimates gyro bias when the controller is held still (never still-based subtraction, which would break fusion).
- The peripheral disappears from the BLE scan after pairing; restart the adapter if it isn’t seen within 30 s.