Weekend project: Cerberus + PS controller + SNES emulator = Fun-time

Found an old PS One controller and thought this could get useful… Started to look up some pin outs for the controller and found one guy who have hacked the protocol… It’s fairly easy serial stuff…

More info here: Sony Playstation (PSX) joystick controller port pinout diagram @ pinoutguide.com

After a little coding, I had the controller talking to the Cerb… Now since the Cerb can’t emulate a keyboard, we had to find another way to talk to the media center PC… An unused UsbSerial module could do the trick… So it is. A nice little windows app connects to the Cerb on com and listens to events triggered on the Cerb from the old playstation controller, then simulate windows hook keyboard events. A nice SNES simulator is has the keyboard presses mapped as input for controller events.

Well… I’m out… Have to play some Super Mario ;D

3 Likes

Cerberus code:

using System;
using System.Collections;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Controls;
using Microsoft.SPOT.Presentation.Media;
using Microsoft.SPOT.Presentation.Shapes;
using Microsoft.SPOT.Touch;

using Gadgeteer.Networking;
using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using Gadgeteer.Modules.GHIElectronics;
using Gadgeteer.Interfaces;

namespace PSOneControllerTranslator
{
    public partial class Program
    {
        // This method is run when the mainboard is powered up or reset.   
        void ProgramStarted()
        {
            /*******************************************************************************************
            Modules added in the Program.gadgeteer designer view are used by typing 
            their name followed by a period, e.g.  button.  or  camera.
            
            Many modules generate useful events. Type +=<tab><tab> to add a handler to an event, e.g.:
                button.ButtonPressed +=<tab><tab>
            
            If you want to do something periodically, use a GT.Timer and handle its Tick event, e.g.:
                GT.Timer timer = new GT.Timer(1000); // every second (1000ms)
                timer.Tick +=<tab><tab>
                timer.Start();
            *******************************************************************************************/
            // inputClock.Interrupt += inputClock_Interrupt;

            // Use Debug.Print to show messages in Visual Studio's "Output" window during debugging.
            Debug.Print("Program Started");

            new Thread(new ThreadStart(TestTimer)).Start();
        }

        // private int delay = 5;

        private GT.Socket.Pin pinData    = GT.Socket.Pin.Nine;  // 9;
        private GT.Socket.Pin pinCommand = GT.Socket.Pin.Eight; // 8;
        private GT.Socket.Pin pinAtt     = GT.Socket.Pin.Seven; // 7;
        private GT.Socket.Pin pinClock   = GT.Socket.Pin.Six;   // 6;
        // private GT.Socket.Pin pinAck     = GT.Socket.Pin.Five;  // 5;

        private DigitalInput  digitalPinData;
        private DigitalOutput digitalPinCommand;
        private DigitalOutput digitalPinAtt;
        private DigitalOutput digitalPinClock;
        // private DigitalIO     digitalPinAck;

        private void SetupPins()
        {
            usbSerial.Configure(115200, Serial.SerialParity.None, Serial.SerialStopBits.One, 8);
            usbSerial.SerialLine.Open();

            // char_Display.Clear();

            var socket = GT.Socket.GetSocket(2, false, null, null);

            digitalPinData = new DigitalInput(socket, pinData, GlitchFilterMode.Off, ResistorMode.PullUp, null);
            digitalPinCommand = new DigitalOutput(socket, pinCommand, false, null);
            digitalPinAtt = new DigitalOutput(socket, pinAtt, true, null);
            digitalPinClock = new DigitalOutput(socket, pinClock, true, null);
        }

        void Set(ref byte aByte, int pos, bool value)
        {
            if (value)
            {
                //left-shift 1, then bitwise OR
                aByte = (byte)(aByte | (1 << pos));
            }
            else
            {
                //left-shift 1, then take complement, then bitwise AND
                aByte = (byte)(aByte & ~(1 << pos));
            }
        }

        private void Read()
        {
            this.PulseDebugLED();

            digitalPinAtt.Write(false);

            Shift(0x01);
            Shift(0x42);
            Shift(0xff);

            var data1 = 255 - Shift(0xff);
            var data2 = 255 - Shift(0xff);

            digitalPinAtt.Write(true);

            // var _dataOut = (data2 << 8 | data1);

            usbSerial.SerialLine.Write(new byte[] { (byte)data1, (byte)data2 });
            usbSerial.SerialLine.Flush();
        }

        private void TestTimer()
        {
            SetupPins();

            while (true)
            {
                Read();
            }
        }

        bool IsBitSet(byte b, int pos)
        {
            return (b & (1 << pos)) != 0;
        }

        private byte Shift(byte _dataOut)
        {
            bool _temp   = false;
            byte _dataIn = 0x00;

            for (var _i = 0; _i <= 7; _i++)
            {
                bool outBit = IsBitSet(_dataOut, _i);
                digitalPinCommand.Write(outBit);
                digitalPinClock.Write(false);

                Thread.Sleep(1);

                _temp = digitalPinData.Read();
                Set(ref _dataIn, _i, _temp);

                digitalPinClock.Write(true);
                Thread.Sleep(1);
            }

            return _dataIn;
        }

    }
}

Windows client

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using WindowsInput;

namespace PSOneControllerWinClient
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        SerialPort port = new SerialPort("COM3", 115200, Parity.None, 8, StopBits.One);

        private void Form1_Load(object sender, EventArgs e)
        {
            for (int i = 0; i != 16; i++)
            {
                commands[i] = false;
            }
  
            port.DataReceived += port_DataReceived;

            // Open the port for communications
            port.Open();
  
        }

        bool[] commands = new bool[16];

        void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            byte data1 = (byte)port.ReadByte();
            byte data2 = (byte)port.ReadByte();

            bool[] curr = new bool[16];

            for (var i = 0; i != 8; i++)
            {
                curr[i] = IsBitSet(data1, i);
            }

            for (var i = 0; i != 8; i++)
            {
                curr[8 + i] = IsBitSet(data2, i);
            }

            for (int i = 0; i != 16; i++)
            {
                if (commands[i] != curr[i])
                {
                    if (curr[i])
                        InputSimulator.SimulateKeyDown((VirtualKeyCode)(65 + i));
                    else
                        InputSimulator.SimulateKeyUp((VirtualKeyCode)(65 + i));
                }
            }

            commands = curr;
        }

        bool IsBitSet(byte b, int pos)
        {
            return (b & (1 << pos)) != 0;
        }
    }
}

Windows Keyboard HOOK lib: http://inputsimulator.codeplex.com/

1 Like

A total waste of time …
at least for me: I really suck at super Mario :’(

Nice TV. ;D

@ Architect - 100" - getting old and refuse to get glasses :smiley:

1 Like

@ Reinhard Ostermeier - it works with any emulator/game ;D keeps the wifey busy, and it also gave Gadgeteer 10 wife points that I can cash in when I want to do other projects :wink:

1 Like

Good for you, my Wife refuses to play with the Wii I originally bought for the two of use.
Then I thought I get here with Wii Fit and Balance board, but no chance :frowning:

I’m still drooling over that TV :slight_smile:

Looks more like a projector and screen but still way cool! :slight_smile:

@ Dave McLaughlin - it is.

@ danibjor -

Let me know when you no longer like that TV :smiley: