Poohl / joycontrol

Emulate Nintendo Switch Controllers over Bluetooth
GNU General Public License v3.0
299 stars 67 forks source link

Console not pairing since update to 12.0.0 #3

Closed thxomas closed 3 years ago

thxomas commented 3 years ago

I had a working setup on rpi3B with some issues, and after dist-upgrade (bad idea), I cannot connect anymore from the Grip/Order menu.

Joycontrol is indefinitely waiting for Switch to connect :

[EDIT: unrelated issue fixed.]

I'm trying to fix this before being able to troubleshoot amiibo_writing again. :)

thxomas commented 3 years ago

Update : fixed Device Class setting by doing it AFTER hid.discoverable() in server.py

However, still no pairing from the switch, here is the end of hcidump :

> HCI Event: User Confirmation Request (0x33) plen 10
    bdaddr 94:58:CB:XX:XX:XX passkey 185519
< HCI Command: User Confirmation Request Negative Reply (0x01|0x002d) plen 6
    bdaddr 94:58:CB:XX:XX:XX
> HCI Event: Command Complete (0x0e) plen 10
    User Confirmation Request Negative Reply (0x01|0x002d) ncmd 1
    status 0x00 bdaddr 94:58:CB:XX:XX:XX
> HCI Event: Simple Pairing Complete (0x36) plen 7
    status 0x05 bdaddr 94:58:CB:XX:XX:XX
    Error: Authentication Failure
> HCI Event: Disconn Complete (0x05) plen 4
    status 0x00 handle 12 reason 0x13
    Reason: Remote User Terminated Connection

The rpi seems to reject the passkey although the AuthenticationRequired=False flag. So nothing happens at python level. Investigating if this is a new default behaviour of the bluez version.

T

thxomas commented 3 years ago

OK, what's the practice on git ? Rename an issue or create a new one ? I didn't notice the update to 12.0.0... 🤔 so this issue is merely caused by the console, not linux...

thxomas commented 3 years ago

Went a step further in the pairing process, by setting a bt-agent to accept the passkey. Next step is failing after " Link Key Notification"

thxomas commented 3 years ago

If anyone interested : After gathering info from various projects, I managed to get the amiibo_writing branch work on raspberry pi 3, latest raspbian buster. Amiibo read/write is not yet functional, but initial pairing works and then using reconnect gives a stable connection to the switch (I was able to launch BOTW from Home menu, load a save, and trigger amiibo reading, but was greeted by "Connect a controller that can read amiibos...").

Please be aware it is still manual, and not ready for easy sharing. Steps to reproduce :

        # start advertising
        hid.discoverable()

        # set the device class to "Gamepad/joystick"
        await hid.set_class()

When this is done : try pairing.. sudo ./run-controller.py PRO_CONTROLLER and go to Grip Screen, connection works ! However if you send 'a' command, the menu exits, but "ERROR - Connection lost"

Now if you reconnect with -r option, the connection keeps ON, until trying NFC read/write.

Of course, these steps should be in some way integrated to the project, but at least the POC is done. Another drawback, is that the RPi is somewhat dedicated only to emulating a joycon, and trying any other BT service would break it..

Thanks to @Brikwerk from NXBT project for having found the missing information to reach this state

ref. https://github.com/Brikwerk/nxbt/issues/18

I need amiibos back ! 💪 T.

pokemon-bot commented 3 years ago

I am trying your solution on my HP laptop with intel bluetooth on ubuntu and I followed your steps but I got stuck this file is missing /etc/machine-info and where do I get test/simple-agent ? and what is the modification I need to change ?

thxomas commented 3 years ago

I am trying your solution on my HP laptop with intel bluetooth on ubuntu and I followed your steps but I got stuck this file is missing /etc/machine-info

This file is specific to the RPi I think. this is the way bluez gets the hostname for bluetooth. On Ubuntu, you may check /etc/bluetooth/main.conf for the Name setting (top of file, just before "Class")

and where do I get test/simple-agent ? and what is the modification I need to change ?

The file is an example found in the bluez source package. I attached my (dirty) modified version to this comment (rename .py or remove extension) Unlike joycontrol it runs on python2, you may need to apt install python-bluez

simple-agent.txt

Does the MAC Address change have an effect on the Intel chipset ? Good luck T.

thxomas commented 3 years ago

Run joycontrol, then, in another shell session, check your controller status by comparing to this :

/home/pi# bluetoothctl show

Controller 94:58:CB:96:BC:79 (public)           #MAC Address must be in Nintendo OUI range
        Name: Pro Controller                    #Name and Alias like a true controller
        Alias: Pro Controller
        Class: 0x00002508                       # Set by joycontrol
        Powered: yes
        Discoverable: yes
        Pairable: yes
        UUID: Generic Attribute Profile (00001801-0000-1000-8000-00805f9b34fb)   # Default profile (accepted by switch)
        UUID: Human Interface Device... (00001124-0000-1000-8000-00805f9b34fb)   # Gamepad profile set by joycontrol
        UUID: PnP Information           (00001200-0000-1000-8000-00805f9b34fb)   # Default profile (accepted by switch)
        UUID: Generic Access Profile    (00001800-0000-1000-8000-00805f9b34fb)   # Default profile (accepted by switch)
        Modalias: usb:v1D6Bp0246d0532
        Discovering: no

The simple-agent must be running detached, or in another session, it will auto accept pairing. Once paired, you don't need it to be running to reconnect with -r option.

T

JaredEzz commented 3 years ago

Thanks to @thxomas for providing steps to troubleshoot.

1. Change RPi BT MAC address to get in Nintendo OUI range I installed the libbluetooth-dev & bdaddr tools to help with this, follow instructions here. I'm using a raspberry pi 2 with a usb bluetooth connector, this was the info before the change:

Manufacturer:   Cambridge Silicon Radio (10)
Device address: 00:1A:7D:DA:71:13

and after the change:

Manufacturer:   Cambridge Silicon Radio (10)
Device address: 00:1A:7D:DA:71:13
New BD address: 94:58:CB:DA:71:13

Address changed - Reset device manually

2. Disable extra SDP Profiles I changed ExecStart in /lib/systemd/system/bluetooth.service from ExecStart=/usr/lib/bluetooth/bluetoothd --noplugin=input to ExecStart=/usr/lib/bluetooth/bluetoothd -C -P sap,input,avrcp

3. Force default device class I changed Class in /etc/bluetooth/main.conf (under the [General] section at the top) from #Class = 0x000100 to Class = 0x002508

4. Set default hostname in /etc/machine-info I also did not find the machine-info file on my raspberry pi, could be because of the linux version, rpi version, etc. I changed Name in /etc/bluetooth/main.conf from Name = BlueZ to Name = Pro Controller

5. Configure an Agent I copied the contents of the prived simple-agent.txt into simple-agent on my rpi, made it executable and ran it, and got some missing packages errors. I installed the recommended package python-bluez, as well as python-gobject, python-dbus because of import errors, and I got the following error:

ERROR:dbus.proxies:Introspect error on :1.5:/org/bluez: dbus.exceptions.DBusException: org.freedesktop.DBus.Error.AccessDenied: Rejected send message, 2 matched rules; type="method_call", sender=":1.39" (uid=1001 pid=1087 comm="/usr/bin/python ./simple-agent ") interface="org.freedesktop.DBus.Introspectable" member="Introspect" error name="(unset)" requested_reply="0" destination=":1.5" (uid=0 pid=280 comm="/usr/lib/bluetooth/bluetoothd --noplugin=input ")
Traceback (most recent call last):
  File "./simple-agent", line 161, in <module>
    manager.RegisterAgent(path, capability)
  File "/usr/lib/python2.7/dist-packages/dbus/proxies.py", line 70, in __call__
    return self._proxy_method(*args, **keywords)
  File "/usr/lib/python2.7/dist-packages/dbus/proxies.py", line 145, in __call__
    **keywords)
  File "/usr/lib/python2.7/dist-packages/dbus/connection.py", line 651, in call_blocking
    message, timeout)
dbus.exceptions.DBusException: org.freedesktop.DBus.Error.AccessDenied: Rejected send message, 2 matched rules; type="method_call", sender=":1.39" (uid=1001 pid=1087 comm="/usr/bin/python ./simple-agent ") interface="org.bluez.AgentManager1" member="RegisterAgent" error name="(unset)" requested_reply="0" destination=":1.5" (uid=0 pid=280 comm="/usr/lib/bluetooth/bluetoothd --noplugin=input ")

Moving on to try the rest without the agent but wanted to post in case there's a simple fix for this error

thxomas commented 3 years ago

Try sudo or running the agent as root

JaredEzz commented 3 years ago

6. Modify server.py by swapping hid.discoverable() and set_class() did this ^^ self-explanatory

FINAL ran sudo ./run-controller.py PRO_CONTROLLER and it ran as expected. Paired, connected, then disconnected after a command or two. Then ran the command with the -r flag and everything works as expected! Turns out I didn't need the agent. Thank you so much @thxomas

thxomas commented 3 years ago

For more insights if you want, find and install bluez-utils, to get tools liike btlon or btmgmt. They give good clues to the cause of problems (Switch not connecting at all, Authentication errors, etc...)

JaredEzz commented 3 years ago

I spoke too soon. It disconnected after a minute or so

thxomas commented 3 years ago

Good and bad news ! Looks like we will have to deal with the command rate spottef by NXBT 😭

Maybe @Poohl will look into it ?

JaredEzz commented 3 years ago

ran bluetoothctl show and it looks like the Alias is properly set but the name and class did not set correctly image

JaredEzz commented 3 years ago

I manually added a machine-info file in /etc and added the PRETTY_HOSTNAME to it. That fixed the Name issue. Connection is still lost after a few commands however. Looking for a solution to the Class problem.

These are the relevant lines in my server.py

        # start advertising
        hid.discoverable()

        # setting bluetooth adapter name and class to the device we wish to emulate
        await hid.set_name(protocol.controller.device_name())
        await hid.set_class()

and again, in my /etc/bluetooth/main.conf Class = 0x002508

@thxomas any thoughts?

JaredEzz commented 3 years ago

sudo hciconfig hciX class 0x002508 changes the class, but the connection still drops after a minute or two

image

Poohl commented 3 years ago

Wait, wait. Sorry for beeing late didn't get notified of the name change.

So do I get this right: @thxomas got it working again just by messing with the Bluetooth pairing process and not modifying the input frequency (or to much inside joycontrol in general)?

Since NXBT's solution is to limit the speed to 15Hz and 1hz before pairing, that can be done aswell but I highly doubt that this is how it should be done. I'll check old captures to check for something we might be doing wrong, @Brickwerk speculated here that it might be to do with the timing byte or some timing in general.

I updated my switch while I was away too, damm it.

JaredEzz commented 3 years ago

@thxomas figured out how to pair it again. I can pair and connect again with -r but the connection only lives as long as I stay in the switch menus. Entering a game causes the connection to fail

JaredEzz commented 3 years ago

I noticed that the list of UUIDs is different when connecting the first time and reconnecting. The Human Interface Device UUID isn't present when I bluetoothctl show after reconnecting

JaredEzz commented 3 years ago

Any ideas on how I can apply the sdp_record_hid.xml manually? Not having any luck googling. I'm trying to do it with sdptool

thxomas commented 3 years ago

@thxomas figured out how to pair it again. I can pair and connect again with -r but the connection only lives as long as I stay in the switch menus. Entering a game causes the connection to fail

Entering Zelda:BOTW does not disconnect, I'm so lucky :)

I noticed that the list of UUIDs is different when connecting the first time and reconnecting. The Human Interface Device UUID isn't present when I bluetoothctl show after reconnecting Any ideas on how I can apply the sdp_record_hid.xml manually? Not having any luck googling. I'm trying to do it with sdptool.

I think no need to. SDP is intended for discovery only, once paired, the Switch will remember the UUIDs and PSM to connect to.

JaredEzz commented 3 years ago

OK, well Pokemon Sword/Shield disconnects :( probably the frequency issue. Just for fun, I connected to Zelda:BOTW and it works fine. I'll test some other games rn too.

thxomas commented 3 years ago

So do I get this right: @thxomas got it working again just by messing with the Bluetooth pairing process and not modifying the input frequency (or to much inside joycontrol in general)?

Yes sir ! The new pairing has additional security (PIN&Encryption) and the console does not tolerate much deviation from a true controller (Name, SDP profiles, ...). But I focused on BotW and apparently got lucky. I did not tinker with any core joycontrol logic as I don't get it yet. One paired, data exchange and controller init work well.

Since NXBT's solution is to limit the speed to 15Hz and 1hz before pairing, that can be done aswell but I highly doubt that this is how it should be done. I'll check old captures to check for something we might be doing wrong, @Brickwerk speculated here that it might be to do with the timing byte or some timing in general.

I'll try to help you in the timing byte analysis when I understand it ;)

Poohl commented 3 years ago

@thxomas First I'm trying to get anything back to work. I followed @JaredEzz steps but my bluetooth adapter isn't complying. I have a raspi 4, but apparently these also have issues with that on top of the incredibly spotty wifi/bluetooth chip on there. Will take some time.

What I found about the timing byte: The switch just increments it by one each message, the joycon (right joycon) increments it by 1 every 0.005 seconds, but increments it a lot more on input mode changes or some MCU commands. Since the joycons run at 60hz, that means incrementing it by 3 every report seems sensible.

pokemon-bot commented 3 years ago

I tried

sudo ./bdaddr -i hci0 -r 94:58:CB:01:02:03
Manufacturer:   Intel Corp. (2)
Device address: 10:4A:7D:01:02:03
Unsupported manufacturer

and

sudo hcitool cmd 0x3f 0x001 0x94 0x58 0xCB 0x01 0x02 0x03
< HCI Command: ogf 0x3f, ocf 0x0001, plen 6
  94 58 CB 01 02 03 
> HCI Event: 0x0f plen 4
  01 01 01 FC 

both did not work in changing the mac address of my intel bluetooth. I think I may need to get a usb bluetooth now

Poohl commented 3 years ago

@pokemon-bot Same problem here. If you find one that works please tell me which one (brand, model/type) because if my raspi won't cooperate either I gotta buy one :(

For the record:

pokemon-bot commented 3 years ago

I am starting to think that bluetooth might be harder than emulating wired controller via arduino. I did a quick search and found this. Anyone tried this ? https://github.com/HackerLoop/Arduino-JoyCon-Library-for-Nintendo-Switch

Poohl commented 3 years ago

nope, haven't tried. Does it work by plugging into the joycon-rails?

All I've tried is USB, but then found this and abandoned any attempts to get the PRO-Controller to talk via USB.

pokemon-bot commented 3 years ago

So the Arduino is emulating a wired controller and is connected via USB to the switch. You then connect your PC to the arduino (I dont know how ? maybe USB too ?) and send commands so the arduino will send the buttons to the switch PC --> arduino (emulating gamepad) --> switch

thxomas commented 3 years ago

Back to start :( I rebooted the pi, did my steps again, checked everything was OK, tried to pair -> NOK. The pairing stops again after sending 'Link Key Notification' Been struggling for 1 hour on bringing it back to work... 😠

Poohl commented 3 years ago

@thxomas Have you tried deleting /var/lib/bluetooth? I've had issues with linux just assuming some key and the switch trying to negotiate one. Or maybe delete/unregister the controller on the switch.

JaredEzz commented 3 years ago

@pokemon-bot yeah the arduino works, you can pm me on discord about it jaredezz#1338 but let's keep this thread to bluetooth only. I'd like to figure it out

Poohl commented 3 years ago

@JaredEzz What BT dongle are you using? the only thing with a Cambridge Silicon Radio chip insinde I just found still available is something by ZEXMTE.

JaredEzz commented 3 years ago

Yep, I'm using the ZEXMTE one. Here's a link: https://amzn.to/3suFokx

Poohl commented 3 years ago

@JaredEzz @pokemon-bot I figured out how to change the MAC on a raspi 4B, it should also work on a 3B+ as they use same chip+diver combo. See my question or TLDR:

bdaddr -i hci0 01:23:45:67:89:ab
hciconfig hci0 reset
systemctl restart bluetooth.service
JaredEzz commented 3 years ago

@Poohl Great! Good luck on the rest of it. Let me know how it goes? Really want to be able to run macros again

Poohl commented 3 years ago

New branch with patches is live. see V12_fixes

Includes a script to adjust the BTADDR (for RPI4B & RPI3B+), the fix for the BT-Class and some Doc on how to get it up and running with the reconnect workaround. Pairing still just disconnects afterwards, am working on it.

Big thanks to @thxomas and @JaredEzz for figuring most of this out.

Poohl commented 3 years ago

Ok, very annoying find:

The switch will happily connect with only the alias beeing set, but once you are paired it will refuse to re-pair with the same PREATTY_NAME ever again. My fix is to just rotate the pretty name by going through the alphabet (PRETTY_NAME='Pro Controllera')

JaredEzz commented 3 years ago

I never had a problem re-pairing as long as I removed the switch from my paired devices on the RPi first

JaredEzz commented 3 years ago

I hope you figure out how to get it to stay connected! Let me know, and what games you're testing it on

Poohl commented 3 years ago

@JaredEzz thanks, it was the paired issue. I always just assumed BlueZ was glitchy again and purged it.

FYI: I'm currently only testing in the menus (want to get the pairing to not die after leaving).

Games I test NFC on are BOTW, Splatoon2 and the System Settings but currently not working on that.

If anyone wants to help with the NFC mechanim: take a look at pipe-stub where I try to get a simple Finite-state machine to make a real joycon write to a real nfc tag, because we still have no Idea what happens after the write is done. The Joycon reports some kind of processing for a while and then???

Poohl commented 3 years ago

Hi, I found a very subtle difference between the simulated and the real switch's regular input talk.

When comparing the real deal:

0000   a1 30 11 8e 40 00 00 00 00 00 c9 58 74 0c 0b f9
0010   25 f5 b4 f6 0e 97 00 e4 d6 3f fc f8 1f f5 c2 f6
0020   35 ff 27 07 e0 fd 00 f9 12 f5 d1 f6 05 10 fa 77
0030   1d 24

to the simulated one:

0000   a1 30 4e 8e 00 00 00 00 08 80 00 08 80 80

One can notice that 2/3 of the packet are missing.... am I blind? How does this even work at all? I'm very certain that this is the real deal, both were captured using wireshark and the driver-headers confirm this to be the actual length on wire.

Is the Switches implementation that bad that it doesn't notice all the 6axis data to be missing?

I'm sorry if this is a very obvious/stupid question, I honestly never looked at the raw data in regular packets before (always used nxbt.type != 0x30).

dentedghost commented 3 years ago

I'm able to connect with the above changes to "mart1nro/joycontrol"

After it sending a command 'b" it disconnects I tried the -r and get: ` $ sudo python3 run_controller_cli.py -r 01:23:45:67:89:AB PRO_CONTROLLER

Traceback (most recent call last): File "run_controller_cli.py", line 329, in _main(args) File "/usr/lib/python3.7/asyncio/base_events.py", line 584, in run_until_complete return future.result() File "run_controller_cli.py", line 287, in _main device_id=args.device_id) File "/home/pi/Development/joycontrol/joycontrol/server.py", line 115, in create_hid_server client_ctl.connect((reconnect_bt_addr, ctl_psm)) OSError: [Errno 113] No route to host

`

Which address to use after -r for reconnecting? I tried sudo python3 run_controller_cli.py -r 01:23:45:67:89:AB PRO_CONTROLLER

` $ bluetoothctl show

Controller 01:23:45:67:89:AB (public) Name: Pro Controller Alias: Pro Controller Class: 0x001c0508 Powered: yes Discoverable: no Pairable: no UUID: Headset AG (00001112-0000-1000-8000-00805f9b34fb) UUID: Generic Attribute Profile (00001801-0000-1000-8000-00805f9b34fb) UUID: OBEX File Transfer (00001106-0000-1000-8000-00805f9b34fb) UUID: Generic Access Profile (00001800-0000-1000-8000-00805f9b34fb) UUID: OBEX Object Push (00001105-0000-1000-8000-00805f9b34fb) UUID: PnP Information (00001200-0000-1000-8000-00805f9b34fb) UUID: IrMC Sync (00001104-0000-1000-8000-00805f9b34fb) UUID: Audio Source (0000110a-0000-1000-8000-00805f9b34fb) UUID: Audio Sink (0000110b-0000-1000-8000-00805f9b34fb) UUID: Vendor specific (00005005-0000-1000-8000-0002ee000001) UUID: Message Notification Se.. (00001133-0000-1000-8000-00805f9b34fb) UUID: Phonebook Access Server (0000112f-0000-1000-8000-00805f9b34fb) UUID: Message Access Server (00001132-0000-1000-8000-00805f9b34fb) UUID: Headset (00001108-0000-1000-8000-00805f9b34fb) Modalias: usb:v1D6Bp0246d0532 Discovering: no

`

Poohl commented 3 years ago

@dentedghost you need your switch's mac. since you paired successfully (did you though? 01:23:45:67:89:ab is not a accepted MAC, it has to start with 94:58:CB:...)

Easiest way to view it after pairing is bluetoothctl paired-devices it will show up as Nintendo Switch

dentedghost commented 3 years ago

@Poohl Thanks Paul, that did it.

I was able to get my scripts working again. Even it it is hacky.

I needed to reconnect multiple times. I got asphalt 9 working with my Bot Designer https://github.com/dentedghost/joycontrol_bot_designer which leverages https://github.com/choss/joycontrol_rest_api and https://github.com/mart1nro/joycontrol.

@thxomas and @JaredEzz Amazing job until we have a complete solution.

I'm running the python3 simple-agent.py

My bot been running for an hour now. Hopefully it's easy to restart again.

JaredEzz commented 3 years ago

Hmm.. my bot disconnects after like 3 commands @dentedghost. What's the difference between pokemon and other games like Asphalt and Botw?

dentedghost commented 3 years ago

@JaredEzz

I just restarted my raspberry pi and got it to work again.

Note I needed to kill any remaining python scripts I had running ps -aux | grep python this is probably a glitch on my system.

Steps:

python3 simple-agent.py

This is my output with multiple RequestAuth simple-agent.py:157: PyGIDeprecationWarning: GObject.MainLoop is deprecated; use GLib.MainLoop instead mainloop = GObject.MainLoop() Agent registered RequestAuthorization (/org/bluez/hci0/dev_70_48_F7_D4_8D_0D) authorized

Next connect controller screen, which like the rest of us throws an error.

Reconnect

Note i'm using joycontrol_rest_api example: curl -X 'POST' \ 'http://0.0.0.0:8000/controller/connect' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{ "controller_type": "PRO_CONTROLLER", "reconnect_address": "70:48:F7:D4:8D:0D" }'

Then try to move to game. Note again. I sometimes need to reconnect again.

In game, I need to reconnect 3 times before I could use my script (joycontrol_bot_designer).

Can you try reconnecting multiple times to see if it just needs to stick?

JaredEzz commented 3 years ago

@dentedghost tried again, reconnecting multiple times in game. Each time I get this error.

[17:35:50] joycontrol.transport write::155 ERROR - [Errno 104] Connection reset by peer
[17:35:50] joycontrol.protocol connection_lost::117 ERROR - Connection lost.
[17:35:50] joycontrol.protocol input_report_mode_full::209 ERROR - [Errno 104] Connection reset by peer

This is the response I get when the switch goes into sleep mode, FWIW. Also I don't think just getting it to connect in-game will work for me since part of my script requires closing and reopening the game. Thanks for the help though!

thxomas commented 3 years ago

Guys, I'm in hell... I cannot have the Switch to pair anymore since yesterday :( I use the v12_fixes branch now, everything should be fine (MAC Addr., Name, SDP, simple-agent, Device Class, unpaired, /var/lib/bluetooth wiped, ...) but it is not... I'm back at the start of this thread : "User Auth accepted", then "Link Key Exchange" then stall. I can see these events in btmon, but no connection request ever reaches joycontrol socket.. Are you still having success on your side ?

Poohl commented 3 years ago

@thxomas I just checked and I can get to python code just fine on the v12_fixes branch (not much further because dirty broken tree :| ).

Have you tried a different MAC? maybe the switch assumes something for yours by now?