Skip to content
This repository has been archived by the owner on Sep 25, 2022. It is now read-only.

Wired Pro Controller emulation #1

Open
javmarina opened this issue Aug 9, 2020 · 13 comments
Open

Wired Pro Controller emulation #1

javmarina opened this issue Aug 9, 2020 · 13 comments

Comments

@javmarina
Copy link

I have seen that one of the items in your to-do list is "Emulate a wired Switch Pro Controller and correctly interface with the Switch console". I just wanted to contact you because I recently published a firmware for AVR microcontrollers that emulates a Pro Controller. As far as I know, Pro Controller emulation with microcontrollers hasn't been a thing until now, as most projects (some of which are very influential, such as https://github.com/shinyquagsire23/Switch-Fightstick) just emulate a Horipad S controller. Some repositories emulate a Pro Controller, but use Raspberry Pi/Python scripts and therefore don't mind about the low-level stuff, which is probably the hard part for your project.
My project was initially based in a similar firmware, but I wanted to unlock all the features that Pro Controller has compared to other USB controllers (custom colors, IMU, NFC, IR...). I started the transition more than one month ago and made some interesting discoveries about low-level USB communication and sync protocol.
I thought it could be useful to you, and I guess the ST USB library will be similar to the one I use (LUFA library). You can check my project here. Thanks in advance!

@rossmacarthur
Copy link
Owner

@javmarina thanks for sharing! I'll be sure to check it out. I took a little bit of a break on this project because I got stuck.

@rossmacarthur
Copy link
Owner

rossmacarthur commented Sep 7, 2020

@javmarina Using your code I managed to get a bit further although I still can't get the Switch to detect my device as a controller. I have the following conversation between the switch and my STM32F4-Discovery board.

(Output all in Hex)

switch:  [00, 00]
switch:  [00, 00]
switch:  [80, 05]
stm32f4: [81, 05]
switch:  [00, 00]
switch:  [80, 01]
stm32f4: [81, 01, 00, 03, 57, 30, ea, 8a, bb, 7c]
switch:  [80, 02]
stm32f4: [81, 02]
switch:  [01, 00, 00, 00, 00, 00, 00, 00, 00, 00, 03, 30, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00]
stm32f4: [21, 03, 91, 00, 00, 00, 00, 08, 80, 00, 08, 80, 0c, 80, 03]
switch:  [80, 04]
stm32f4: [30, 03, 91, 00, 00, 00, 00, 08, 80, 00, 08, 80, 0c]
switch:  [01, 01, 00, 00, 00, 00, 00, 00, 00, 00, 48, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00]
stm32f4: [21, 06, 91, 00, 00, 00, 00, 08, 80, 00, 08, 80, 0c, 80, 48]
switch:  [01, 02, 00, 00, 00, 00, 00, 00, 00, 00, 02, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00]
stm32f4: [21, 06, 91, 00, 00, 00, 00, 08, 80, 00, 08, 80, 0c, 82, 02, 03, 48, 03, 02, 57, 30, ef, 8a, bb, 7c, 03, 02]
switch:  [01, 03, 00, 00, 00, 00, 00, 00, 00, 00, 08, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00]
stm32f4: [21, 09, 91, 00, 00, 00, 00, 08, 80, 00, 08, 80, 0c, 80, 08]

After this if I start sending reports like the following it still doesn't do anything

stm32f4: [30, f2, 91, 00, 00, 00, 00, 08, 80, 00, 08, 80, 0c]

Can you see anything obvious that is going wrong?

@rossmacarthur
Copy link
Owner

I realized I wasn't sending the full buffer, when I do this it causes the switch to crash!

switch:  [00, 00]
switch:  [00, 00]
switch:  [80, 05]
switch:  [00, 00]
switch:  [80, 01]
stm32f4: [81, 01, 00, 03, 57, 30, ea, 8a, bb, 7c, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00]
switch:  [80, 02]
stm32f4: [81, 02, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00]
switch:  [01, 00, 00, 00, 00, 00, 00, 00, 00, 00, 03, 30, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00]
stm32f4: [21, 03, 91, 00, 00, 00, 00, 08, 80, 00, 08, 80, 0c, 80, 03, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00]
switch:  [80, 04]
stm32f4: [30, 03, 91, 00, 00, 00, 00, 08, 80, 00, 08, 80, 0c, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00]
switch:  [01, 08, 00, 00, 00, 00, 00, 00, 00, 00, 02, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00]
stm32f4: [21, 03, 91, 00, 00, 00, 00, 08, 80, 00, 08, 80, 0c, 82, 02, 03, 48, 03, 02, 57, 30, ef, 8a, bb, 7c, 03, 02, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00]
switch:  [01, 09, 00, 00, 00, 00, 00, 00, 00, 00, 08, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00]
stm32f4: [21, 06, 91, 00, 00, 00, 00, 08, 80, 00, 08, 80, 0c, 80, 08, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00]
switch:  [01, 0a, 00, 00, 00, 00, 00, 00, 00, 00, 10, 00, 60, 00, 00, 10, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00]
stm32f4: [21, 06, 91, 00, 00, 00, 00, 08, 80, 00, 08, 80, 0c, 80, 10, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00]

Gives

image

@javmarina
Copy link
Author

That should definitely not happen. I wonder what triggered that crash. You might want to use Wireshark in order to analyze the USB traffic between your computer and the controller. If you have Chrome installed, it will try to communicate with the Pro Controller and all the messages will appear in Wireshark. The way I fixed my code was comparing the USB traffic with a real Pro Controller and the traffic with my Arduino board. You can also check https://mzyy94.com/blog/2020/03/20/nintendo-switch-pro-controller-usb-gadget/ and https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering.
I didn't see any change in the code recently, so I guess all you're doing is still on your local repository.
Finally, have you used the correct USB descriptors?

@javmarina
Copy link
Author

javmarina commented Sep 10, 2020

One thing that might confuse the Switch is the fact that the counter is not incremented after every packet. In my project I increment the counter by 3 after every reply, but seems like you do that every two packets. I read a long ago that the counter should increment very quickly and could cause issues.
Note that the counter is the second byte only in packets that start with 0x30 (input report) or 0x21 (UART reply), and is not used in other packets.

@rossmacarthur
Copy link
Owner

@javmarina
Copy link
Author

javmarina commented Sep 10, 2020

Another thing that I noticed is that, when you receive [80, 04] from Switch, you reply with an invalid buffer. In your dump:

switch: [80, 04]
stm32f4: [30, 03, 91, 00, 00, 00, 00, 08, 80, 00, 08, 80, 0c, 00, 00, 00, ... 00]

First byte: input report code (fine)
Second byte: counter (note what I said in the last comment)
Next 11 bytes: input report:

  • First byte (0x91): connection and battery info (Pro Controller, USB connected, battery full and charging)
  • Then 3 bytes for pressed buttons (all 0x00, no buttons pressed)
  • Then 6 bytes for left and right joysticks (which are centered)
  • One byte for vibrator input report (I still don't understand what it's used for, but that value should work).

After the input report, you should have

Never mind, my bad. I leave that for reference.

@rossmacarthur
Copy link
Owner

The one thing I wasn't sure about was at what point do I start sending regular reports? Or should I only send them if the Switch requests them?

@rossmacarthur
Copy link
Owner

I'm going to fix the counter issue, and try again.

@rossmacarthur
Copy link
Owner

rossmacarthur commented Sep 10, 2020

I didn't implement the SPI commands which I'm guessing is going to be necessary to get this working 😅.

@javmarina
Copy link
Author

You are right, didn't notice that.
SPI commands are mandatory, at least the ones I implemented. I suppose the Switch crashed because it couldn't get factory parameters and calibration from the controller. I copied the values from mzyy94 and you can check them here: https://github.com/javmarina/Nintendo-Switch-Remote-Control/blob/master/firmware/EmulatedSPI.c

@javmarina
Copy link
Author

The one thing I wasn't sure about was at what point do I start sending regular reports? Or should I only send them if the Switch requests them?

To be fair, it's not clear when you have to send regular reports. The Switch won't usually send a request packet, but instead will generate a USB interrupt. In my project, I send a regular report if I didn't receive any packet from Switch since the last interrupt. Seems to work fine. Otherwise, if you receive a request from the console, you have to send the reply to that request in the next interrupt and the regular report will hopefully make it to the next interrupt.

See void send_IN_report(void) in https://github.com/javmarina/Nintendo-Switch-Remote-Control/blob/master/firmware/Response.c for more details (note the usage of the nextPacketReady boolean).

@javmarina
Copy link
Author

Hi Ross! How is everything going?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants