Skip to content

Commit

Permalink
import
Browse files Browse the repository at this point in the history
  • Loading branch information
jolan committed Oct 24, 2020
0 parents commit 067b1a1
Show file tree
Hide file tree
Showing 22 changed files with 1,671 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
remotegamepad
674 changes: 674 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

121 changes: 121 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# remotegamepad

Remote Gamepad allows you or your friends to use any browser-supported gamepad
to mimic a Nintendo Switch Pro Controller by using a Raspberry Pi as a relay.

![remotegamepad screenshot](remotegamepad_screenshot.png)

## How It Works

* Web browsers have a [Gamepad API](https://caniuse.com/gamepad). You can check if your gamepad/controller is supported at [Gamepad Tester](https://gamepad-tester.com/).
* Inexpensive devices such as the Raspberry Pi Zero W are capable of imitating a Nintendo Switch Pro Controller when placed in OTG/USB gadget mode.
* Remote Gamepad provides some glue between the two by setting up button mapping and sending a stream of controller data to the Raspberry Pi which in turn will relay it to the Switch via USB.

If you have a capture card and a low latency way to share the video stream, you
can game with friends who don't have a Switch over the Internet. Map/redirect a
port on your router/gateway to port 8085 on your Raspberry Pi and have your
friend connect to it with their browser.

## Requirements

* Nintendo Switch
* Nintendo Switch Dock
* Raspberry Pi Zero W, Raspberry Pi 4 Model B, or other board cable of OTG mode+network
* Browser support for your controller/gamepad

## Pi Setup

Enable dwc2 USB controller driver

echo "dtoverlay=dwc2" | sudo tee -a /boot/config.txt

Enable needed kernel modules

echo "dwc2" | sudo tee -a /etc/modules.d/dwc2.conf
echo "libcomposite" | sudo tee -a /etc/modules.d/libcomposite.conf

Reboot for changes to take effect

sudo reboot

Download [add_procon_gadget.sh](https://gist.github.com/mzyy94/60ae253a45e2759451789a117c59acf9#file-add_procon_gadget-sh)
and run it with root privileges. By default the resulting device, `/dev/hidg0` can only be used by root. You should chown/chmod like so:

sudo chown root:dialout /dev/hidg0
sudo chmod 660 /dev/hidg0

If you're going to be using the setup long-term, a udev rule should be used instead.

## Launch

[Download the release](https://github.com/jolan/remotegamepad/releases), extract it,
`cd` into the directory and run `./remotegamepad`

Or get the source:

go get -u -v github.com/jolan/remotepamepad

And then build and run it:

cd ~/go/src/github.com/jolan/remotegamepad && go build && ./remotegamepad

Or cross-compile it and then copy everything to the Raspberry Pi:

env GOOS=linux GOARCH=arm GOARM=6 go build

The binary expects to find `webroot` in the same directory that is run from.

Finally, enter the URL to your Raspberry Pi (e.g. http://192.0.2.53:8085) into your browser.

## Adding A Profile For A Controller

1. Go to https://gamepad-tester.com/.
2. Copy/paste the full controller name and annotate the button layout for your controller in gamepadStateSend().
3. Add shortname to gamepadShortName().

## Limitations

* Non-Switch controllers cannot be used to wake the system from sleep.
* Starting a session requires the Switch to be out of sleep mode.
* At the moment, the supported controller list is hardcoded and tiny.
* Can only use a single gamepad per computer (seems to be a browser limitation).
* Every remote gamepad requires their own Raspberry Pi/relay device.

## Roadmap

* Add profiles for common controllers like Wii U, DualShock, Xbox 360, etc.
* Add button mapping dialog for controllers using the default profile.
* Improve binary release with systemd service, config/logging options, udev rule, etc.
* Code cleanup.
* Add mouse/keyboard support.
* Test on non-Chrome browsers to check for bugs/quirks.
* Determine if multiple controllers-on-a-single is possible.
* Add audio/video streaming support.
* Maybe support consoles other than the Switch one day.

## Acknowledgements

@mzyy94 - remotegamepad borrows much of the functionality from his work.

* https://github.com/mzyy94/nscon
* https://github.com/mzyy94/ns-remote
* https://mzyy94.com/blog/2020/03/20/nintendo-switch-pro-controller-usb-gadget/
* https://mzyy94.com/blog/2020/04/17/nintendo-switch-audio-uac-gadget/
* https://mzyy94.com/blog/2020/05/11/play-nintendo-switch-on-smartphone/
* https://mzyy94.com/blog/2020/05/12/raspberry-pi-hdmi-edid-cec/

Thanks to others for precursor/similar/inspiring work:

* @dekuNekum - https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
* @shinyquagsire23 - https://github.com/shinyquagsire23/HID-Joy-Con-Whispering
* @Xfennec - https://gist.github.com/Xfennec/e1215febb15b40c21bf029b38a31640b
* @mumumusuc - https://github.com/mumumusuc/pi-joystick
* @omakoto - https://github.com/omakoto/raspberry-switch-control
* @mart1nro - https://github.com/mart1nro/joycontrol
* @javmarina - https://github.com/javmarina/Nintendo-Switch-Remote-Control
* @ndeadly - https://github.com/ndeadly/MissionControl
* Parsec - https://www.youtube.com/watch?v=uTlwVyWobRc

## License

[GPL v3](LICENSE)
9 changes: 9 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module github.com/jolan/remotegamepad

go 1.15

require (
github.com/gorilla/mux v1.8.0
github.com/gorilla/websocket v1.4.2
github.com/mzyy94/nscon v0.1.0
)
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/mzyy94/nscon v0.1.0 h1:K6DZWWvaVtIsUxtcaLVGq6AjAHLEHPZXaTI1/81TcO0=
github.com/mzyy94/nscon v0.1.0/go.mod h1:5XyHX0uDmuhaKIDkJz+Xi4BK027IcrLswlzNjg6x7L0=
19 changes: 19 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package main

import (
"flag"

"github.com/jolan/remotegamepad/webserver"
"github.com/mzyy94/nscon"
)

func main() {
var (
device = flag.String("device", "/dev/hidg0", "simulating hid gadget path")
)
flag.Parse()

controller := nscon.NewController(*device)

webserver.StartHTTPServer(controller)
}
Binary file added remotegamepad_screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added webroot/android-chrome-192x192.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added webroot/android-chrome-512x512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added webroot/apple-touch-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions webroot/css/remotegamepad.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
body {
padding-top: 5rem;
}
.remotegamepad {
padding: 3rem 1.5rem;
text-align: center;
}
Binary file added webroot/favicon-16x16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added webroot/favicon-32x32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added webroot/favicon.ico
Binary file not shown.
141 changes: 141 additions & 0 deletions webroot/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<meta name="description" content="Remote Gamepad allows you to connect a controller to your switch remotely">
<title>Remote Gamepad · Home</title>
<link rel="apple-touch-icon" sizes="180x180" href="./apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png" type="image/png">
<link rel="icon" type="image/png" sizes="16x16" href="./favicon-16x16.png" type="image/png">
<link rel="manifest" href="./site.webmanifest">
<!-- TODO check if updated to 4.5.3 has been pushed -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootswatch/4.5.2/slate/bootstrap.min.css"
integrity="sha384-8iuq0iaMHpnH2vSyvZMSIqQuUnQA7QM+f6srIdlgBrTSEyd//AWNMyEaSF2yPzNQ" crossorigin="anonymous">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.1/css/all.css"
integrity="sha384-vp86vTRFVJgpjF9jiIGPEEqYqlDwgyBgEF109VFjmqGmIY/Y4HV4d3Gp2irVfcrp" crossorigin="anonymous">
<style>
.bd-placeholder-img {
font-size: 1.125rem;
text-anchor: middle;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}

@media (min-width: 768px) {
.bd-placeholder-img-lg {
font-size: 3.5rem;
}
}
</style>
<link href="./css/remotegamepad.css" rel="stylesheet">
</head>

<body>
<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
<a class="navbar-brand" href="#">Remote Gamepad <i class="fas fa-laptop-house"></i><i
class="fas fa-gamepad"></i></a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault"
aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>

<div class="collapse navbar-collapse" id="navbarsExampleDefault">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
</li>
</ul>
<span><a href="http://github.com/jolan/remotegamepad">v0.1.0 <i class="fab fa-github"></i></a></span>
</div>
</nav>

<main role="main" class="container">
<div id="sessions" class="remotegamepad table-responsive">
<h1 id="sessionsActiveHeader" style="display: none;">Active Sessions</h1>
<table id="sessionsActive" class="table table-bordered table-condensed table-hover table-striped"
style="display: none;">
<thead>
<tr>
<th><i class="fas fa-gamepad fa-2x"><br></i><br>Gamepad</th>
<th><i class="fab fa-raspberry-pi fa-2x"></i><br>Raspberry Pi</th>
<th><i class="fas fa-stream fa-2x"></i><br>Status</th>
<th><i class="fas fa-sort-amount-up-alt fa-2x"></i><br>Buffer</th>
<th><i class="fas fa-cog fa-2x"></i><br>Control</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<h1>Start Session</h1>
<table id="sessionsAvailable" class="table table-bordered table-condensed table-hover table-striped">
<thead>
<tr>
<th style="vertical-align: middle;"><i class=" fas fa-gamepad fa-2x"></i><br>Gamepad</th>
<th style="vertical-align: middle;"><i class="fas fa-sliders-h fa-2x"></i><br>Swap Buttons
<button type="button" class="btn btn-sm btn-dark" data-toggle="tooltip" data-placement="top"
title="Whether to swap the face buttons or not (e.g. B&lt;-&gt;A, X&lt;-&gt;Y)"><i
class="fas fa-question-circle"></i></button>
</th>
<th style="vertical-align: middle;"><i class="fab fa-raspberry-pi fa-2x"></i><br>Raspberry Pi
</th>
<th style="vertical-align: middle;"><i class="fas fa-cog fa-2x"></i><br>Control</th>
</tr>
</thead>
<tbody>
</tbody>
</table>

<div id="status"></div>
<div class="alert alert-info alert-dismissible fade show" role="alert">
<strong>Welcome! Here are some tips to get started:</strong>
<div class="text-left">
<ul>
<li>Press a button or wiggle the joysticks to trigger detection of the gamepad.</li>
<li>Close any applications in the background that may interfere with input e.g. Steam.</li>
<li>Use Chrome/Chromium, leave the tab in the foreground, and don't refresh the page.</li>
</ul>
</div>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<!-- TODO
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<strong>Known Issues:</strong>
<div class="text-left">
<ul>
<li>Multiple gamepads don't work. Only the first/lowest gamepad will work. Seems to be a
browser limitation.</li>
<li>The switch needs to powered on/not sleeping when starting a session or else pairing will
fail and the switch communication library used, nscon, will silently hang.</li>
<li>Only two models of Xbox One and the Switch Pro Controller are currently supported.</li>
</ul>
</div>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div> -->
</div>

</main><!-- /.container -->
<script src="https://code.jquery.com/jquery-3.5.1.min.js"
integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js"
integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js"
integrity="sha384-w1Q4orYjBQndcko6MimVbzY0tgp4pWB4lZ7lr30WKz0vr/aWKhXdBNmNb5D92v7s"
crossorigin="anonymous"></script>
<script src="./js/remotegamepad.config.js"></script>
<!-- fallback to default config if no custom config present -->
<script>if (typeof relayservers == 'undefined') {
document.write(unescape("%3Cscript src='./js/remotegamepad.config-default.js' type='text/javascript'%3E%3C/script%3E"));
}</script>
<script src="./js/remotegamepad.js"></script>
</body>

</html>
1 change: 1 addition & 0 deletions webroot/js/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
remotegamepad.config.js
5 changes: 5 additions & 0 deletions webroot/js/remotegamepad.config-default.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
var relayservers = {};
relayservers[0] = {
"name": "pi",
"dsn": window.location.hostname + ":" + location.port
};
Loading

0 comments on commit 067b1a1

Please sign in to comment.