[PYTHON] Link the mouse to the gyro of the Nintendo Switch Procon

at first

I wanted to operate Splatoon 2 of Nintendo Switch with a mouse. In the streets, converters that can move the Nintendo switch with a mouse are already on the market. However, commercial converters only replaced the movement of the mouse stick with the movement of the mouse (as far as I could see). I thought that it would not work well with that method. Therefore, I made a device that replaces the movement of the gyro of the professional controller with the movement of the mouse.

However, it became something that I could not operate well with my technical skills. I think that the method of replacing the movement of the gyro with the movement of the mouse will be practical if made well. I'm going to publish what I've done so far here.

This is made

The video cannot be opened

How to use

Articles that were very helpful

-Operate Nintendo Switch with your smartphone ~ Simulate Pro Controller with USB Gadget ~

Things to prepare

The mouse I'm using https://www.elecom.co.jp/products/M-Y8UBXBK.html I think this is probably the case. If the data format sent by the mouse is the same as that of my mouse, I think it will save you the trouble of rewriting the python code that will be posted later.

procedure

Please set up Raspberry Pi (this time I chose Raspbian as the OS normally). Turn on the wired connection in the Nintendo Switch controller settings. Turn on the gyro in the Splatoon 2 settings. Find out the Vendor ID (VID) of your mouse. The VID of my ELECOM mouse was 04F3 Connect your mouse, keyboard, and Pro controller to the Raspberry Pi's USB Type-A port. Connect HDMI port 0 on the Raspberry Pi to the display. The connected display is for displaying the screen of Raspberry Pi. The display that displays the game screen connects to the HDMI port on the Nintendo Switch dock. After confirming that the Nintendo Switch is activated, Connect the Nintendo Switch to the Type-C port (I connected it to the USB port on the Nintendo Switch Dock). Then Raspberry Pi will start. In Raspbian, add dtoverlay = dwc2 to /boot/config.txt and dwc2 and libcomposite to / etc / modules to load the dwc2 module.

Be able to become superuser with su on the command line Create a file called add_procon_gadget.sh shown below Type source add_procon_gadget.sh on the command line

add_procon_gadget.sh


#!/bin/bash

cd /sys/kernel/config/usb_gadget/
mkdir -p procon
cd procon
echo 0x057e > idVendor
echo 0x2009 > idProduct
echo 0x0200 > bcdDevice
echo 0x0200 > bcdUSB
echo 0x00 > bDeviceClass
echo 0x00 > bDeviceSubClass
echo 0x00 > bDeviceProtocol

mkdir -p strings/0x409
echo "000000000001" > strings/0x409/serialnumber
echo "Nintendo Co., Ltd." > strings/0x409/manufacturer
echo "Pro Controller" > strings/0x409/product

mkdir -p configs/c.1/strings/0x409
echo "Nintendo Switch Pro Controller" > configs/c.1/strings/0x409/configuration
echo 500 > configs/c.1/MaxPower
echo 0xa0 > configs/c.1/bmAttributes

mkdir -p functions/hid.usb0
echo 0 > functions/hid.usb0/protocol
echo 0 > functions/hid.usb0/subclass
echo 64 > functions/hid.usb0/report_length
echo 050115000904A1018530050105091901290A150025017501950A5500650081020509190B290E150025017501950481027501950281030B01000100A1000B300001000B310001000B320001000B35000100150027FFFF0000751095048102C00B39000100150025073500463B0165147504950181020509190F2912150025017501950481027508953481030600FF852109017508953F8103858109027508953F8103850109037508953F9183851009047508953F9183858009057508953F9183858209067508953F9183C0 | xxd -r -ps > functions/hid.usb0/report_desc

ln -s functions/hid.usb0 configs/c.1/

ls /sys/class/udc > UDC

Type ʻexiton the command line to return to a normal user. Typesudo dmesg | grep -A7 057e` on the command line.

pi@raspberrypi:~ $ sudo dmesg | grep -A7 057e
[ 7201.091044] usb 1-1.3: New USB device found, idVendor=057e, idProduct=2009, bcdDevice= 2.00
[ 7201.091060] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 7201.091074] usb 1-1.3: Product: Pro Controller
[ 7201.091087] usb 1-1.3: Manufacturer: Nintendo Co., Ltd.
[ 7201.091099] usb 1-1.3: SerialNumber: 000000000001
[ 7201.112454] input: Nintendo Co., Ltd. Pro Controller as /devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.3/1-1.3:1.0/0003:057E:2009.0002/input/input1
[ 7201.114447] hid-generic 0003:057E:2009.0002: input,hidraw0: USB HID v1.11 Joystick [Nintendo Co., Ltd. Pro Controller] on usb-0000:01:00.0-1.3/input0

There is a place where it says hidraw0. This is like a file that represents a Pro controller, and the numbers below hidraw may vary depending on your environment, so check what number hidraw is.

Next, the mouse will find out what number hidraw is, so on the command line sudo dmesg | grep -A7 Replace the" mouse VID "part of the mouse VID with the mouse VID and hit it. The VID of my ELECOM mouse was 04F3. Check what number your mouse is hidraw.

Create a program that interrupts mouse operation data between the communication between the Nintendo Switch and the Pro controller. Create the Python code file shown below. (Sorry for the very dirty code).

mouse_gyro.py


#!/usr/bin/env python3

import os
import threading
import time
import random

# Re-connect USB Gadget device
os.system('echo > /sys/kernel/config/usb_gadget/procon/UDC')
os.system('ls /sys/class/udc > /sys/kernel/config/usb_gadget/procon/UDC')

time.sleep(0.5)

gadget = os.open('/dev/hidg0', os.O_RDWR | os.O_NONBLOCK)
procon = os.open('/dev/hidraw3', os.O_RDWR | os.O_NONBLOCK)
mouse  = os.open('/dev/hidraw2', os.O_RDWR | os.O_NONBLOCK)
mouse_int = bytes([0,0,0,0])
def mouse_input():
    global mouse_int
    while True:
        try:
            mouse_int = os.read(mouse, 128)
            #print('<<<', output_data.hex())
            #print(output_mouse.hex())
            #os.write(gadget, output_mouse)
        except BlockingIOError:
            pass
        except:
            os._exit(1)



def procon_input():
    while True:
        try:
            input_data = os.read(gadget, 128)
            #print('>>>', input_data.hex())
            os.write(procon, input_data)
        except BlockingIOError:
            pass
        except:
            os._exit(1)

def convert(ou_dt_i, mo_in_i, weight, reflect):
    mo_in_i = int.from_bytes(mo_in_i, byteorder='little', signed=True)
    ou_dt_i = int.from_bytes(ou_dt_i, byteorder='little', signed=True)
    if reflect == True:
        mo_in_i = mo_in_i * -1
        ou_dt_i = ou_dt_i * -1
    merged_gy = ou_dt_i + mo_in_i * weight
    if merged_gy > 32767:
        merged_gy = 32767
    elif merged_gy < -32768:
        merged_gy = -32768
    else:
        pass
    merged_gy = merged_gy.to_bytes(2, byteorder='little', signed=True)

    return merged_gy

def replace_mouse(output_data, mouse_int):
    #a = output_data[0:13]

    #mouse no click wo migi no button ni henkan
    ri_btn = 0
    if mouse_int[0] == 1:#hidari click
        ri_btn = 0x80#ZR button
    elif mouse_int[0] == 2:#migi click
        ri_btn = 0x40#R button
    elif mouse_int[0] == 4:#chuu click
        ri_btn = 0x08#A button
    ri_btn = (output_data[3] + ri_btn).to_bytes(1, byteorder='little')
    a = output_data[0:3] + ri_btn + output_data[4:13]

    #kasokudo sensor ni tekitou ni atai wo ire naito setsuzoku ga kireru
    if mouse_int[1] != 0:
        b = 127
    else:
        b=0
    if mouse_int[2] != 0:
        b = 127
    else:
        b = 0
    d = bytes([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0])
    ac0 = bytes([255]) if output_data[14] + b > 255 else bytes([output_data[14] + b])
    ac1 = bytes([255]) if output_data[16] + b > 255 else bytes([output_data[16] + b])
    ac2 = bytes([255]) if output_data[18] + b > 255 else bytes([output_data[18] + b])
    ac0_1 = bytes([255]) if output_data[26] + b > 255 else bytes([output_data[26] + b])
    ac1_1 = bytes([255]) if output_data[28] + b > 255 else bytes([output_data[28] + b])
    ac2_1 = bytes([255]) if output_data[30] + b > 255 else bytes([output_data[30] + b])
    ac0_2 = bytes([255]) if output_data[38] + b > 255 else bytes([output_data[38] + b])
    ac1_2 = bytes([255]) if output_data[40] + b > 255 else bytes([output_data[40] + b])
    ac2_2 = bytes([255]) if output_data[42] + b > 255 else bytes([output_data[42] + b])

    #mouse no ugoki wo gyro no ugoki ni henkan
    gy0_0 = convert(output_data[19:21], mouse_int[1:2], 250, False)#
    gy1_0 = convert(output_data[21:23], mouse_int[2:3], 250, False)#
    gy2_0 = convert(output_data[23:25], mouse_int[2:3], 0, False)#

    gy0_1 = convert(output_data[31:33], mouse_int[1:2], 250, False)#
    gy1_1 = convert(output_data[33:35], mouse_int[2:3], 250, False)#
    gy2_1 = convert(output_data[35:37], mouse_int[2:3], 0, False)#

    gy0_2 = convert(output_data[43:45], mouse_int[1:2], 250, False)#
    gy1_2 = convert(output_data[45:47], mouse_int[2:3], 250, False)#
    gy2_2 = convert(output_data[47:49], mouse_int[2:3], 0, False)#

    e = a+output_data[13:14]+ac0+output_data[15:16]+ac1+output_data[17:18]+ac2 \
        +gy0_0+gy1_0+gy2_0 \
        +output_data[25:26]+ac0_1+output_data[27:28]+ac1_1+output_data[29:30]+ac2_1 \
        +gy0_1+gy1_1+gy2_1 \
        +output_data[37:38]+ac0_2+output_data[39:40]+ac1_2+output_data[41:42]+ac2_2 \
        +gy0_2+gy1_2+gy2_2 \
        +d

    print(int.from_bytes(gy1_0, byteorder='little'))
    #print(mouse_int[1])
    return e

def procon_output():
    global mouse_int
    while True:
        try:
            output_data = os.read(procon, 128)
            #output_mouse = os.read(mouse, 128)
            #print('<<<', output_data.hex())
            #print(output_data)
            e = replace_mouse(output_data, mouse_int)
            #print(e.hex())
            os.write(gadget, e)#output_data
            mouse_int = bytes([0,0,0,0])
        except BlockingIOError:
            pass
        except Exception as g:
            print(type(g))
            print(g)
            os._exit(1)

threading.Thread(target=procon_input).start()
threading.Thread(target=procon_output).start()
threading.Thread(target=mouse_input).start()

Rewrite the number after hidraw assigned to the variables procon and mouse to the number you confirmed earlier. Next, I rewrite the python code to match the python code with the signal sent by the mouse. The mouse I'm using https://www.elecom.co.jp/products/M-Y8UBXBK.html I think this is probably the case, but with it you may not need to rewrite your code. To find out what data your mouse sends, uncomment #print (output_mouse.hex ()) in the mouse_input () function. Instead, it's easier to see if you comment out the print () part elsewhere. To run this python code, type sudo python3 mouse_gyro.py on the command line.

Hopefully, you can move the gyro awkwardly with the mouse, like the video above.

It often happens that the Pro controller is disconnected in the middle. Try following the instructions on the switch screen, or press and hold the small round button on the side of the controller to re-register the controller. At that time, the next number of hidraw may change. In that case, rewrite the assignment part of the procon variable of the python code by typing the command to check the hidraw number written above again.

At the end

If you watched the above video or tried the operation, you will understand. Even if I move the mouse, the gyro does not move properly. I would like to ask for your help. The Python code is available on GitHub, and there is no special license, so feel free to modify it. I would be very grateful if you could cooperate.

Thank you for reading.

https://github.com/Bokuchin/SwitchProconGyroMouse

Article link that I was allowed to refer to

-Operate Nintendo Switch with your smartphone ~ Simulate Pro Controller with USB Gadget ~

Recommended Posts

Link the mouse to the gyro of the Nintendo Switch Procon
Switch the setting value of setting.py according to the development environment
Supplement to the explanation of vscode
The story of trying to reconnect the client
Script to change the description of fasta
10 methods to improve the accuracy of BERT
How to check the version of Django
The story of adding MeCab to ubuntu 16.04
How to switch mouse operations on CentOS
The story of pep8 changing to pycodestyle
Paste a link to the data point of the graph created by jupyterlab & matplotlib
Mouse over Matplotlib to display the corresponding image
How to find the area of the Voronoi diagram
Combinatorial optimization to find the hand of "Millijan"
The inaccuracy of Tensorflow was due to log (0)
Set the range of active strips to the preview range
I tried to correct the keystone of the image
Change the decimal point of logging from, to.
To get the path of the currently running python.exe
I want to customize the appearance of zabbix
From the introduction of pyethapp to the execution of contract
Try to simulate the movement of the solar system
[Java] How to switch between multiple versions of Java
The story of moving from Pipenv to Poetry
I tried to predict the price of ETF
I tried to vectorize the lyrics of Hinatazaka46!