I’m just going to dump all my discoveries here, and hopefully they would be useful to the Nintendo Switch community.

Contact Information

If you think something is wrong, have some observations that I might have missed, stuff you want to contribute, or just general questions please feel free to post here or contact me by email:

dekunukem_gmail_com

If you want to use the information here somewhere else, feel free to do so, but do please credit me (dekuNukem).

Remarks

  • Joycon runs at 1.8 V

  • There is no silkscreen marking component and test point numbers, maybe Nintendo is trying to discourage people from doing funky things to the Joycon?

  • Also, in a bizarre move, Nintendo didn’t use the traditional “one side pulled-up other side to ground” way of reading buttons, instead they used a keypad configuration where buttons are arranged to rows and columns. They used the keypad scanner built-in inside the BCM20734 with 128KHz clock for reading the buttons. That means it would be extremely hard to spoof button presses for TAS and twitch-plays. Maybe the Pro controller is different, need to buy one though.

  • The only button that’s not part of the keypad is the joystick button, which is still activated by pulling it down to ground.

Accelerometer and gyroscope

Upon connection the microcontroller initializes a software reset of the MEMS chip, then set up the accelerometer and gyroscope as follows:

Accelerometer Gyroscope
The ODR 1.66 KHz, full – scale + 8 g ODR 208Hz, full-scale 2000dps

The accelerometer also has AA filter at 100Hz bandwidth, low-pass filter enabled, slope filter enabled with cut-off frequency at 416Hz.

The Joycon then polls LSM6DS3 every 1.35ms(740Hz) for both accelerometer and gyroscope data in all axises, totaling 12 bytes(6 axises, each axis 2 bytes).

Since the Joycon polls MEMS data every 1.35ms but only send out controller update every 15ms, there might be some internal averaging to smooth out the data, needs to go through the numbers to find out.

Joycon to Console Communication

When attached to the console, the Joycon talks to it through a physical connection instead of Bluetooth. There are 10 pins on the connector, I’m just going to arbitrarily name it like this:

Looking at the pins on both Joycon facing towards you, the left most one is Pin 1, and the right most one is Pin 10. I simply removed the rumble motor, burned a hole on the back cover, and routed all the wires out through that.

Capture of the docking of the left and right Joycon.

Joycon Connector Pinout

Logic analyzer channel Joycon Connector Pin Function Remarks
1 GND
2 GND
0 3 BT status? HIGH when connected to console via bluetooth. Joy-Con will not send serial data post-handshake unless pin is pulled LOW by console.
1 4 5V Joycon power and charging
2 5 Serial data console to Joycon Inverted level (idle at GND)
3 6 Flow control Console will only send data to Joycon when this line is LOW
7 GND
4 8 Serial data Joycon to console Standard level (idle at 1.8V)
5 9 ? Always at GND
6 10 Flow control Joycon will only send data to console when this line is HIGH
  • When first connected the baud rate is at 1000000bps(!) , after the initial handshake the speed is then switched to 3125000bps(!!) .

Pesky checksums

It turns out the last byte of each command seems to be a checksum of some sort, and without figuring it out it would be rather difficult testing what each command does because the console will not accept commands with the wrong checksum.

Luckily here are some examples of the checksum, seeing it changes drastically with the difference of a single bit it’s probably not some simple xor or modular checksum. If you can figure it out it would be really helpful.

19 01 03 07 00 91 10 00 00 00 00 3D
19 01 03 07 00 91 01 00 00 00 00 24
19 01 03 07 00 91 11 00 00 00 00 0E

19 81 03 07 00 94 10 00 00 00 00 D6
19 81 03 07 00 94 11 00 00 0F 00 33
Copy the code

Thanks to ewalt1’s effort and contribution, we seems to have a solution to the checksum problem:

The first 4 bytes are a header, with the 4th byte being the length of the remaining packet (not counting the checksum). The next 7 bytes are some type of data, with the 8th byte being the CRC of that data. The CRC used is CRC-8 with a polynomial of 0x8D and an initial value of 0x00.

There’s some example code for calculating this CRC using a lookup table in packet_parse/joycon_crc.py.

Joycon status data packet

In normal operation the console asks Joycon for an update every 15ms (66.6 FPS), the command for requesting update is:

19 01 03 08 00 92 00 01 00 00 69 2d 1f
Copy the code

Around 4ms later, Joycon respond with a 61 bytes long answer.

One sample:

19 81 03 38 
00 92 00 31 
00 00 e9 2e 
30 7f 40 00 
00 00 65 f7 
81 00 00 00 
c0 23 01 e2 
ff 3e 10 0a 
00 d6 ff d0 
ff 23 01 e1 
ff 37 10 0a 
00 d6 ff cf 
ff 29 01 dd 
ff 34 10 0a 
00 d7 ff ce 
ff 
Copy the code

Here is what I figured out:

Byte # Sample value Remarks
0 to 8 19 81 03 38 00 92 00 31 Header, fixed
16 and 17 00 02 Button status, see section below
19 f7 Joystick X value, reversed nibble?
20 81 Joystick Y value
31 and 32 4e 05 Gyroscope X value
33 and 34 cc fb Gyroscope Y value
35 and 36 eb ff Gyroscope Z value
37 and 38 00 41 Accelerometer X Value
39 and 40 1b 03 Accelerometer Y Value
41 and 42 82 f0 Accelerometer Z Value

Each accelerometer and gyroscope axis data is 2 bytes long and forms a int16_t, last byte is the higher byte.

Joystick value

Byte 19 and 20 (f7 81 between 5th and 6th line) are the Joystick values, most likely the raw 8-bit ADC data. Byte 19 is X while byte 20 is Y. Again, bizarrely, the X nibble is reversed, as in the f7 should actually be 7f (127 at neutral position). The Y value is correct though (0x81 is 129).

Rumble commands

I did a capture of the command sent from console to initiate a rumble on the Joycon. It was captured by pressing L to set off a bomb in BotW, which results in a fairly short rumble. Here is the capture.

You can see the console sends longer commands (17 bytes vs 8 bytes) during the rumble period. I’m yet to look into this, but of course you can.

Touchscreen controller

The console itself uses a FT9CJ capacitive touchscreen controller. And according to techinsights it’s a custom part by STMicroelectronics for the Nintendo Switch. After looking at the communication it appears to use I2C, which is in line with other touchscreen controller chips. Here is a capture of the I2C bus on power-up.

The 7-bit I2C address of the chip is 0x49 (0x92 write, 0x93 read), and it’s polled around every 4ms for update.

Docking station firmware dump

The docking station uses a STM32F048 microcontroller. It’s actually labeled as STM32P048 because it uses the FASTROM option where ST pre-programs the flash memory inside the factory. It has 32KB flash memory and 6KB RAM, runs at 48MHz.

It uses SWD debugging and programming interface, and interestingly the programming testpoints are on the PCB and clearly labeled. After connecting a ST-Link programmer to it reveals that the chip is not read-protected at all, so a firmware dump was easily made. I’m not going to post it in the repo, but if you want it just ask.