So i made this cool class with which you can instantly connect your playstation 2 controller to your netduino, assuming you have 4 GPIO-ports available. I use this at the moment to control 3 servos of which 2 i have attached to the analog sticks and 1 to the d-pad. This is still somewhat work in progress, since i have not yet implemented the polling of trigger, d-pad and button pressures.
Anyways this is a very straightforward interface which i think you'll find easy to use.
PS2Controller.cs
using System; using Microsoft.SPOT; using Microsoft.SPOT.Hardware; using SecretLabs.NETMF.Hardware; using SecretLabs.NETMF.Hardware.Netduino; namespace Robot3DOF { class PS2Controller { // I/O wires private OutputPort attention; private OutputPort command; private OutputPort clock; private InputPort data; // Properties, first the analog sticks' positions private short leftX = 127; private short leftY = 127; private short rightX = 127; private short rightY = 127; // Triggers private bool leftTrigger1 = false; private bool leftTrigger2 = false; private bool rightTrigger1 = false; private bool rightTrigger2 = false; // D-Pad private bool left = false; private bool right = false; private bool up = false; private bool down = false; // Buttons private bool triangle = false; private bool circle = false; private bool square = false; private bool cross = false; // Other private bool select = false; private bool start = false; private bool leftStickPress = false; private bool rightStickPress = false; public bool LeftStickPress { get { return leftStickPress; } set { leftStickPress = value; } } public bool RightStickPress { get { return rightStickPress; } set { rightStickPress = value; } } public bool Select { get { return select; } set { select = value; } } public bool Start { get { return start; } set { start = value; } } public bool Triangle { get { return triangle; } set { triangle = value; } } public bool Circle { get { return circle; } set { circle = value; } } public bool Cross { get { return cross; } set { cross = value; } } public bool Square { get { return square; } set { square = value; } } public short RightX { get { return rightX; } set { rightX = value; } } public short LeftY { get { return leftY; } set { leftY = value; } } public short RightY { get { return rightY; } set { rightY = value; } } public short LeftX { get { return leftX; } set { leftX = value; } } public bool LeftTrigger1 { get { return leftTrigger1; } set { leftTrigger1 = value; } } public bool LeftTrigger2 { get { return leftTrigger2; } set { leftTrigger2 = value; } } public bool RightTrigger1 { get { return rightTrigger1; } set { rightTrigger1 = value; } } public bool RightTrigger2 { get { return rightTrigger2; } set { rightTrigger2 = value; } } public bool Left { get { return left; } set { left = value; } } public bool Right { get { return right; } set { right = value; } } public bool Down { get { return down; } set { down = value; } } public bool Up { get { return up; } set { up = value; } } public PS2Controller() { // Change these pins as necessary this.attention = new OutputPort(Pins.GPIO_PIN_D1, false); this.command = new OutputPort(Pins.GPIO_PIN_D2, false); this.clock = new OutputPort(Pins.GPIO_PIN_D3, false); this.data = new InputPort(Pins.GPIO_PIN_D4, false, Port.ResistorMode.PullUp); } // This brings the controller to "config mode" public bool toConfigMode() { this.command.Write(true); this.attention.Write(false); gameByte(0x01); gameByte(0x43); gameByte(0x00); gameByte(0x01); gameByte(0x00); this.command.Write(true); this.attention.Write(true); return true; } // This brings the controller back from config mode public bool fromConfigMode() { this.command.Write(true); this.clock.Write(true); this.attention.Write(false); gameByte(0x01); gameByte(0x43); gameByte(0x00); gameByte(0x00); gameByte(0x5A); gameByte(0x5A); gameByte(0x5A); gameByte(0x5A); gameByte(0x5A); this.command.Write(true); this.attention.Write(true); return true; } // This sets the controller's operating mode to "analog" public bool toAnalogMode() { this.command.Write(true); this.clock.Write(true); this.attention.Write(false); gameByte(0x01); gameByte(0x44); gameByte(0x00); gameByte(0x01); gameByte(0x03); gameByte(0x00); gameByte(0x00); gameByte(0x00); gameByte(0x00); this.command.Write(true); this.attention.Write(true); return true; } // This polls the controller with 3 bytes header and 6 bytes data payload. // This could be optimized for different operating modes, for example in the digital mode only 2 bytes of data // are needed. This function takes around 31ms to complete. public bool pollController() { byte[] data = new byte[6]; this.command.Write(true); this.clock.Write(true); this.attention.Write(false); gameByte(0x01); // bite 0. header. gameByte(0x42); // bite 1. header. (should possibly put test on this byte to detect unplugging of controller.) gameByte(0x00); // bite 2. header. gameByte(0x00, ref data[0]); // Buttons1 gameByte(0x00, ref data[1]); // Buttons2 gameByte(0x00, ref data[2]); // Right joy X gameByte(0x00, ref data[3]); // Right joy Y gameByte(0x00, ref data[4]); // Left joy X gameByte(0x00, ref data[5]); // left joy Y #region Set properties // My goodness, i do this for all ya n000000000000bs if ((data[0] & (1 << 0)) == 0) // Select this.Select = true; else this.Select = false; if ((data[0] & (1 << 1)) == 0) // L3 (joy push) this.LeftStickPress = true; else this.LeftStickPress = false; if ((data[0] & (1 << 2)) == 0) // R3 this.RightStickPress = true; else this.RightStickPress = false; if ((data[0] & (1 << 3)) == 0) // Start this.Start = true; else this.Start = false; if ((data[0] & (1 << 4)) == 0) // Up this.Up = true; else this.Up = false; if ((data[0] & (1 << 5)) == 0) // Right this.Right = true; else this.Right = false; if ((data[0] & (1 << 6)) == 0) // Down this.Down = true; else this.Down = false; if ((data[0] & (1 << 7)) == 0) // Left this.Left = true; else this.Left = false; if ((data[1] & (1 << 0)) == 0) // L2 this.LeftTrigger2 = true; else this.LeftTrigger2 = false; if ((data[1] & (1 << 1)) == 0) // R2 this.RightTrigger2 = true; else this.RightTrigger2 = false; if ((data[1] & (1 << 2)) == 0) // L1 this.LeftTrigger1 = true; else this.LeftTrigger1 = false; if ((data[1] & (1 << 3)) == 0) // R1 this.RightTrigger1 = true; else this.RightTrigger1 = false; if ((data[1] & (1 << 4)) == 0) // Triangle this.Triangle = true; else this.Triangle = false; if ((data[1] & (1 << 5)) == 0) // Circle this.Circle = true; else this.Circle = false; if ((data[1] & (1 << 6)) == 0) // Cross this.Cross = true; else this.Cross = false; if ((data[1] & (1 << 7)) == 0) // Square this.Square = true; else this.Square = false; #endregion this.command.Write(true); this.attention.Write(true); this.LeftX = data[4]; this.RightX = data[2]; return true; } // Send a byte, receive a byte void gameByte(byte command, ref byte dataByte) { byte i; for (i = 0; i < 8; i++) { if ((command & (1 << i)) == (1 << i)) // Adjust command wire according to the command this.command.Write(true); else this.command.Write(false); this.clock.Write(false); // CLK to low if (this.data.Read() == true) // Hacked delay dataByte |= (byte)(1 << i); this.clock.Write(true); // CLK to high } this.command.Write(true); } // Send byte only void gameByte(byte command) { byte i; byte data = 0x00; for (i = 0; i < 8; i++) { if ((command & (1 << i)) == (1 << i)) // Adjust command wire according to the command this.command.Write(true); else this.command.Write(false); this.clock.Write(false); // CLK to low if (this.data.Read() == true) // Hacked delay data |= (byte)(1 << i); this.clock.Write(true); // CLK to high } this.command.Write(true); } } }
A small application example
using System; using System.Threading; using Microsoft.SPOT; using Microsoft.SPOT.Hardware; using SecretLabs.NETMF.Hardware; using SecretLabs.NETMF.Hardware.Netduino; namespace Robot3DOF { public class Program { public static void Main() { Servo servo2 = new Servo(Pins.GPIO_PIN_D6); // Setting up some servos to control Servo servo3 = new Servo(Pins.GPIO_PIN_D5); PS2Controller controller = new PS2Controller(); servo2.Degree = 0; servo3.Degree = 0; float coeff = (180f / 255f); // Needed, since the analog stick gives an 8-bit value while (true) { controller.pollController(); servo2.Degree = (int)(coeff * (float)controller.LeftX); servo3.Degree = (int)(coeff * (float)controller.RightX); } } } }
So in a nutshell
- Connect wires
- Instantiate controller in code
- Poll controller state
- Assign properties to cool stuff
Note that the controller operates at 3.3V and should be connected accordingly.
Any ideas to make this interface better will yield an aggressive and sensuel brohug from yours truly.