diff --git a/Arduino_connections.png b/Arduino_connections.png new file mode 100644 index 0000000..099e120 Binary files /dev/null and b/Arduino_connections.png differ diff --git a/ControllerClient.cs b/ControllerClient.cs new file mode 100644 index 0000000..60ee8b8 --- /dev/null +++ b/ControllerClient.cs @@ -0,0 +1,461 @@ +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Net; +using System.Net.Sockets; +using SharpDX.DirectInput; + +namespace ControllerTestConsole +{ + class Program + { + public static int Dpad = 0; + + static string ButtonTranslate(char button) + { + switch(button) + { + case '1': + return "g"; + case '2': + return "h"; + case '3': + return "i"; + case '4': + return "j"; + case '7': + return "e"; + case '8': + return "f"; + default: + return "NADA"; + } + } + + static void ButtonState(SharpDX.DirectInput.JoystickUpdate state, char number, Stream TCPstream) + { + ASCIIEncoding asen = new ASCIIEncoding(); + byte[] byteArray = null; //asen.GetBytes(message); + + string Button = ButtonTranslate(number); + + if (state.Value == 128) + { + Console.WriteLine("Button " + number + " pressed!"); + if (Button != "NADA") + { + byteArray = asen.GetBytes(Button + "1"); + TCPstream.Write(byteArray, 0, byteArray.Length); + } + } + else if (state.Value == 0) + { + Console.WriteLine("Button " + number + " un-pressed!"); + if (Button != "NADA") + { + byteArray = asen.GetBytes(Button + "0"); + TCPstream.Write(byteArray, 0, byteArray.Length); + } + } + } + + static void DpadState(SharpDX.DirectInput.JoystickUpdate state, Stream TCPstream) + { + ASCIIEncoding asen = new ASCIIEncoding(); + byte[] byteArray = null; //asen.GetBytes(message); + + switch (state.Value) + { + case -1: //ALL DPAD INPUTS OFF + switch (Dpad) + { + case 1: + Console.WriteLine("DPAD UP un-pressed"); + byteArray = asen.GetBytes("a0"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + case 2: + Console.WriteLine("DPAD UP un-pressed"); + byteArray = asen.GetBytes("a0"); + TCPstream.Write(byteArray, 0, byteArray.Length); + + Console.WriteLine("DPAD RIGHT un-pressed"); + byteArray = asen.GetBytes("d0"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + case 3: + Console.WriteLine("DPAD RIGHT un-pressed"); + byteArray = asen.GetBytes("d0"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + case 4: + Console.WriteLine("DPAD RIGHT un-pressed"); + byteArray = asen.GetBytes("d0"); + TCPstream.Write(byteArray, 0, byteArray.Length); + + Console.WriteLine("DPAD DOWN un-pressed"); + byteArray = asen.GetBytes("b0"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + case 5: + Console.WriteLine("DPAD DOWN un-pressed"); + byteArray = asen.GetBytes("b0"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + case 6: + Console.WriteLine("DPAD DOWN un-pressed"); + byteArray = asen.GetBytes("b0"); + TCPstream.Write(byteArray, 0, byteArray.Length); + + Console.WriteLine("DPAD LEFT un-pressed"); + byteArray = asen.GetBytes("c0"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + case 7: + Console.WriteLine("DPAD LEFT un-pressed"); + byteArray = asen.GetBytes("c0"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + case 8: + Console.WriteLine("DPAD LEFT un-pressed"); + byteArray = asen.GetBytes("c0"); + TCPstream.Write(byteArray, 0, byteArray.Length); + + Console.WriteLine("DPAD UP un-pressed"); + byteArray = asen.GetBytes("a0"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + } + Dpad = 0; + break; + case 0: //UP + switch (Dpad) + { + case 0: + Console.WriteLine("DPAD UP pressed"); + byteArray = asen.GetBytes("a1"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + case 2: + Console.WriteLine("DPAD RIGHT un-pressed"); + byteArray = asen.GetBytes("d0"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + case 8: + Console.WriteLine("DPAD LEFT un-pressed"); + byteArray = asen.GetBytes("c0"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + } + Dpad = 1; + break; + + case 4500: //UP RIGHT + switch (Dpad) + { + case 0: + Console.WriteLine("DPAD UP pressed"); + byteArray = asen.GetBytes("a1"); + TCPstream.Write(byteArray, 0, byteArray.Length); + + Console.WriteLine("DPAD RIGHT pressed"); + byteArray = asen.GetBytes("d1"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + case 1: + Console.WriteLine("DPAD RIGHT pressed"); + byteArray = asen.GetBytes("d1"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + case 3: + Console.WriteLine("DPAD UP pressed"); + byteArray = asen.GetBytes("a1"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + } + Dpad = 2; + break; + + case 9000: //RIGHT + switch (Dpad) + { + case 0: + Console.WriteLine("DPAD RIGHT pressed"); + byteArray = asen.GetBytes("d1"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + case 2: + Console.WriteLine("DPAD UP un-pressed"); + byteArray = asen.GetBytes("a0"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + case 4: + Console.WriteLine("DPAD DOWN un-pressed"); + byteArray = asen.GetBytes("b0"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + } + Dpad = 3; + break; + + case 13500: //DOWN RIGHT + switch (Dpad) + { + case 0: + Console.WriteLine("DPAD DOWN pressed"); + byteArray = asen.GetBytes("b1"); + TCPstream.Write(byteArray, 0, byteArray.Length); + + Console.WriteLine("DPAD RIGHT pressed"); + byteArray = asen.GetBytes("d1"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + case 3: + Console.WriteLine("DPAD DOWN pressed"); + byteArray = asen.GetBytes("b1"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + case 5: + Console.WriteLine("DPAD RIGHT pressed"); + byteArray = asen.GetBytes("d1"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + } + Dpad = 4; + break; + + case 18000: //DOWN + switch (Dpad) + { + case 0: + Console.WriteLine("DPAD DOWN pressed"); + byteArray = asen.GetBytes("b1"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + case 4: + Console.WriteLine("DPAD RIGHT un-pressed"); + byteArray = asen.GetBytes("d0"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + case 6: + Console.WriteLine("DPAD LEFT un-pressed"); + byteArray = asen.GetBytes("c0"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + } + Dpad = 5; + break; + + case 22500: //DOWN LEFT + switch (Dpad) + { + case 0: + Console.WriteLine("DPAD DOWN pressed"); + byteArray = asen.GetBytes("b1"); + TCPstream.Write(byteArray, 0, byteArray.Length); + + Console.WriteLine("DPAD LEFT pressed"); + byteArray = asen.GetBytes("c1"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + case 5: + Console.WriteLine("DPAD LEFT pressed"); + byteArray = asen.GetBytes("c1"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + case 7: + Console.WriteLine("DPAD DOWN pressed"); + byteArray = asen.GetBytes("b1"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + } + Dpad = 6; + break; + + case 27000: //LEFT + switch (Dpad) + { + case 0: + Console.WriteLine("DPAD LEFT pressed"); + byteArray = asen.GetBytes("c1"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + case 6: + Console.WriteLine("DPAD DOWN un-pressed"); + byteArray = asen.GetBytes("b0"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + case 8: + Console.WriteLine("DPAD UP un-pressed"); + byteArray = asen.GetBytes("a0"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + } + Dpad = 7; + break; + + case 31500: //UP LEFT + switch (Dpad) + { + case 0: + Console.WriteLine("DPAD UP pressed"); + byteArray = asen.GetBytes("a1"); + TCPstream.Write(byteArray, 0, byteArray.Length); + + Console.WriteLine("DPAD LEFT pressed"); + byteArray = asen.GetBytes("c1"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + case 7: + Console.WriteLine("DPAD UP pressed"); + byteArray = asen.GetBytes("a1"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + case 1: + Console.WriteLine("DPAD LEFT pressed"); + byteArray = asen.GetBytes("c1"); + TCPstream.Write(byteArray, 0, byteArray.Length); + break; + } + Dpad = 8; + break; + } + } + + static void Main(string[] args) + { + TcpClient tcpclnt = new TcpClient(); + Console.WriteLine("Enter the IP address or hostname of the PC you wish to connect to:"); + string IPAddr = Console.ReadLine(); + + Console.WriteLine("Enter the port:"); + string portString = Console.ReadLine(); + int port = Int32.Parse(portString); + + Console.WriteLine("Connecting....."); + try + { + tcpclnt.Connect(IPAddr, port); + } + catch + { + Console.WriteLine("Connection failed. Hit enter to exit"); + Console.ReadLine(); + System.Environment.Exit(1); + } + + //send test message on TCP connection + //string message = "Hello thar TCP!"; + Stream TCPstream = tcpclnt.GetStream(); + //ASCIIEncoding asen = new ASCIIEncoding(); + //byte[] byteArray = asen.GetBytes(message); + + //TCPstream.Write(byteArray, 0, byteArray.Length); + /*byte[] TempByteArray = new byte[100]; + int k = TCPstream.Read(TempByteArray, 0, 100); + + for(int i = 0; i < k; i++) + { + Console.Write(Convert.ToChar(TempByteArray[i])); + }*/ + + + // Initialize DirectInput + var directInput = new DirectInput(); + + // Find a Joystick Guid + var joystickGuid = Guid.Empty; + + foreach (var deviceInstance in directInput.GetDevices(DeviceType.Gamepad, DeviceEnumerationFlags.AllDevices)) + { + joystickGuid = deviceInstance.InstanceGuid; + } + + // If Gamepad not found, look for a Joystick + if (joystickGuid == Guid.Empty) + { + foreach (var deviceInstance in directInput.GetDevices(DeviceType.Joystick, DeviceEnumerationFlags.AllDevices)) + { + joystickGuid = deviceInstance.InstanceGuid; + } + } + + // If Joystick not found, throws an error + if (joystickGuid == Guid.Empty) + { + Console.WriteLine("No joystick/Gamepad found."); + Console.ReadKey(); + Environment.Exit(1); + } + + // Instantiate the joystick + var joystick = new Joystick(directInput, joystickGuid); + + Console.WriteLine("Found Joystick/Gamepad with GUID: {0}", joystickGuid); + + // Query all suported ForceFeedback effects + var allEffects = joystick.GetEffects(); + foreach (var effectInfo in allEffects) + { + Console.WriteLine("Effect available {0}", effectInfo.Name); + } + + // Set BufferSize in order to use buffered data. + joystick.Properties.BufferSize = 128; + + // Acquire the joystick + joystick.Acquire(); + + // Poll events from joystick + while (true) + { + joystick.Poll(); + var datas = joystick.GetBufferedData(); + foreach (var state in datas) + { + //Console.WriteLine(state); + if (state.Offset == JoystickOffset.PointOfViewControllers0) + { + DpadState(state,TCPstream); + } + else if (state.Offset == JoystickOffset.Buttons0) + { + ButtonState(state,'1',TCPstream); + } + else if (state.Offset == JoystickOffset.Buttons1) + { + ButtonState(state, '2',TCPstream); + } + else if (state.Offset == JoystickOffset.Buttons2) + { + ButtonState(state, '3', TCPstream); + } + else if (state.Offset == JoystickOffset.Buttons3) + { + ButtonState(state, '4', TCPstream); + } + else if (state.Offset == JoystickOffset.Buttons4) + { + ButtonState(state, '5', TCPstream); + } + else if (state.Offset == JoystickOffset.Buttons5) + { + ButtonState(state, '6', TCPstream); + } + else if (state.Offset == JoystickOffset.Buttons6) + { + ButtonState(state, '7', TCPstream); + } + else if (state.Offset == JoystickOffset.Buttons7) + { + ButtonState(state, '8', TCPstream); + } + } + } + + } + } +} diff --git a/ControllerInterface.ino b/ControllerInterface.ino new file mode 100644 index 0000000..164d253 --- /dev/null +++ b/ControllerInterface.ino @@ -0,0 +1,195 @@ +// Buffer to store incoming commands from serial port +String inData = ""; + +int a = 2; // up +int b = 3; // down +int c = 4; // left +int d = 5; // right +int e = 6; // select/coin +int f = 7; // start +int g = 8; // button 1 +int h = 9; // button 2 +int i = 10; // button 3 +int j = 11; // button 4 +int k = 12; // button 5 +int l = 14; // button 6 + +void setup() +{ + Serial.begin(9600); + //Serial.begin(38400); + Serial.println("Serial connection started, waiting for inputs..."); + + pinMode(l, INPUT); +} + +void loop() +{ + while(Serial.available() > 0) // maybe replace with == \n on actual hardware + { + char received = Serial.read(); + inData += received; + + // Process message when new line character is recieved + if (received == '0' || received == '1') + { + Serial.print("Arduino Received: "); + Serial.print(inData); + Serial.println(); + + switch(inData[0]) + { + case 'a': + //do stuff + if(inData[1] == '1') // Button was pressed + { + pinMode(a, OUTPUT); + digitalWrite(a, LOW); + } + else if(inData[1] == '0') // Button no longer pressed + { + pinMode(a, INPUT); + } + break; + case 'b': + //do stuff + if(inData[1] == '1') // Button was pressed + { + pinMode(b, OUTPUT); + digitalWrite(b, LOW); + } + else if(inData[1] == '0') // Button no longer pressed + { + pinMode(b, INPUT); + } + break; + case 'c': + //do stuff + if(inData[1] == '1') // Button was pressed + { + pinMode(c, OUTPUT); + digitalWrite(c, LOW); + } + else if(inData[1] == '0') // Button no longer pressed + { + pinMode(c, INPUT); + } + break; + case 'd': + //do stuff + if(inData[1] == '1') // Button was pressed + { + pinMode(d, OUTPUT); + digitalWrite(d, LOW); + } + else if(inData[1] == '0') // Button no longer pressed + { + pinMode(d, INPUT); + } + break; + case 'e': + //do stuff + if(inData[1] == '1') // Button was pressed + { + pinMode(e, OUTPUT); + digitalWrite(e, LOW); + } + else if(inData[1] == '0') // Button no longer pressed + { + pinMode(e, INPUT); + } + break; + case 'f': + //do stuff + if(inData[1] == '1') // Button was pressed + { + pinMode(f, OUTPUT); + digitalWrite(f, LOW); + } + else if(inData[1] == '0') // Button no longer pressed + { + pinMode(f, INPUT); + } + break; + case 'g': + //do stuff + if(inData[1] == '1') // Button was pressed + { + pinMode(g, OUTPUT); + digitalWrite(g, LOW); + } + else if(inData[1] == '0') // Button no longer pressed + { + pinMode(g, INPUT); + } + break; + case 'h': + //do stuff + if(inData[1] == '1') // Button was pressed + { + pinMode(h, OUTPUT); + digitalWrite(h, LOW); + } + else if(inData[1] == '0') // Button no longer pressed + { + pinMode(h, INPUT); + } + break; + case 'i': + //do stuff + if(inData[1] == '1') // Button was pressed + { + pinMode(i, OUTPUT); + digitalWrite(i, LOW); + } + else if(inData[1] == '0') // Button no longer pressed + { + pinMode(i, INPUT); + } + break; + case 'j': + //do stuff + if(inData[1] == '1') // Button was pressed + { + pinMode(j, OUTPUT); + digitalWrite(j, LOW); + } + else if(inData[1] == '0') // Button no longer pressed + { + pinMode(j, INPUT); + } + break; + case 'k': + //do stuff + if(inData[1] == '1') // Button was pressed + { + pinMode(k, OUTPUT); + digitalWrite(k, LOW); + } + else if(inData[1] == '0') // Button no longer pressed + { + pinMode(k, INPUT); + } + break; + case 'l': + //do stuff + if(inData[1] == '1') // Button was pressed + { + pinMode(l, OUTPUT); + digitalWrite(l, LOW); + } + else if(inData[1] == '0') // Button no longer pressed + { + pinMode(l, INPUT); + } + break; + //default: + //don't do stuff? + } + + // Do stuff here for button presses + + inData = ""; // Clear recieved buffer + } + } +} diff --git a/ControllerServer.cs b/ControllerServer.cs new file mode 100644 index 0000000..2cad764 --- /dev/null +++ b/ControllerServer.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Net; +using System.Net.Sockets; +using System.IO.Ports; + +namespace ControllerServer +{ + class Program + { + public static IPAddress GetLocalIPAddress() + { + var host = Dns.GetHostEntry(Dns.GetHostName()); + foreach (var ip in host.AddressList) + { + if (ip.AddressFamily == AddressFamily.InterNetwork) + { + return ip; + } + } + throw new Exception("No network adapters with an IPv4 address in the system!"); + } + + static void Main(string[] args) + { + string ClientMessage = ""; + int port = 8000; + IPAddress IPaddr = GetLocalIPAddress(); + + Console.WriteLine("Enter COM port to use (example, COM3):"); + string COMport = Console.ReadLine(); + + TcpListener TCPserver = new TcpListener(IPaddr,port); + TCPserver.Start(); + + Console.WriteLine("The server is running at port " + port + "..."); + Console.WriteLine("The local End point is: " + TCPserver.LocalEndpoint); + Console.WriteLine("Waiting for a connection....."); + + + Socket TCPsocket = TCPserver.AcceptSocket(); + Console.WriteLine("Connection accepted from " + TCPsocket.RemoteEndPoint); + + SerialPort SerialConnection = new SerialPort(COMport, 9600, Parity.None, 8, StopBits.One); + SerialConnection.Open(); + + while (true) + { + byte[] byteArray = new byte[2]; + int k = TCPsocket.Receive(byteArray); + Console.Write("Recieved: "); + + ClientMessage = ""; + for (int i = 0; i < k; i++) + { + //Console.Write(Convert.ToChar(byteArray[i])); + ClientMessage += Convert.ToChar(byteArray[i]); + } + + Console.WriteLine(ClientMessage); + SerialConnection.Write(ClientMessage); + SerialConnection.DiscardInBuffer(); //ends up locking the serial connection after a bit without this. + //SerialConnection.DiscardOutBuffer(); + } + + /*catch (Exception ex) + { + Console.WriteLine("Error: " + ex); + } + + Console.WriteLine("The End. Press Enter to exit..."); + Console.ReadLine();*/ + } + } +} diff --git a/Instructions.txt b/Instructions.txt new file mode 100644 index 0000000..81a00de --- /dev/null +++ b/Instructions.txt @@ -0,0 +1,17 @@ +This is an initial proof-of-concept version that works and is playable, but don't expect miracles on competative games or ones requiring quick reflexes due to added latency of going over the internet. Support will be very limited for this version. During my testing I was using Discord to stream. Twitch added far too much additional latency to be playable. + + +1.) Load ControllerInterface.ino on your Arduion Uno (might work on other models, but is untested) + +2.) Make your own adapter/padhack/mess of wires that connects the Arduino to DB15 style controller connection (Neo Geo, superguns, custom padhacks) as shown in Arduino_connections.png + +3.) Use a PC with a capture card to stream the game so your friend(s) can see it. + +4.) Connect Arduino to the PC and the console's 2nd player controller port (will probably support more players in the future). + +5.) Run the ControllerServer.exe, type in the name of the COM port the Arduino is running on, and forward port 8000 for that PC on your router. + +5.) Have your friend run ControllerClient.exe, and they will need to input your IP address. + +Once they connect their button presses should register in your game. +Note: the ControllerClient is currently hardcoded for a Xbox 360 controller. Other controllers may work, but are untested. There is currently no option to re-map buttons. \ No newline at end of file