Convert Sony PS2 Controller to Arduino RC Controller
by taifur in Circuits > Remote Control
6104 Views, 111 Favorites, 0 Comments
Convert Sony PS2 Controller to Arduino RC Controller
Robots, cars, robotcars, and all sorts of prank devices require some degree of remote control. Most of the time, it's tempting to implement this yourself using XBee or some other wireless technology. Sometimes it's not a bad idea, but more often than not it's an over-powered and somewhat frustrating way to go.
Radio Control units, or RC Controllers, have been used by model airplanes, helicopters, rovers and boating enthusiasts for years. But these controllers are not limited to hobby vehicle control. They are equally suitable for use in controlling any electronic project that requires a full-featured wireless remote control.
A typical transmitter will have a few control surfaces, like wheels or joysticks, as well as some switches or dials.
Today, I’ll show you how to make a very common and inexpensive Arduino RC Controller from an old Sony Play Station 2 controller. In this project, we will use low-power 2.4GHz nRF24L01 transceiver modules to make wireless communication between the robot car and the RC remote.
Supplies
Old/New Sony Play Station 2 Controller:
The DualShock 2 Controller is a sixth-generation gamepad and a successor of the DualShock Controller. It was released into the market in 2000 as a video game controller for Sony PlayStation 2. The DualShock 2 Controller is also forward compatible with original models of PlayStation and backward compatible with PlayStation 3 through the third-party peripheral connecting the controller and the console via a USB port.
Noticeable features of the DualShock 2 Controller include stiffer analog sticks, a blue DualShock logo at the top of the controller, and pressure-sensitive analog values. It also cannot work with games that require Sixaxis functionality.
The DualShock 2 Controller has a height of 3.74” (95 mm), width of 6.18” (157 mm), depth of 2.16” (54.9 mm), and approximate weight of 7.4 oz (.21 kg).
nRF Nano (buy one from aliexpress)
This board combines the NRF24L01+ wireless transceiver with the familiar form factor and programming paradigm of the Arduino Nano. For someone who needs wireless capabilities and prefers to keep solder and wiring work to a minimum, this board is a good option.
or Arduino Nano + nRF24L01
16340 Li-ion rechargeable battery (buy from aliexpress.com)
The 16340 battery (or RCR123A) is a cylindrical li-ion cell classified by its 34mm x 16mm dimensions, and is the rechargeable version of a CR123A battery. They usually have a 3.6V or 3.7V voltage, button-top terminal, and most use a LiCoO2 (lithium carbon-oxide) chemistry. While some come with built-in USB charging ports, all 16340 batteries are rechargeable in li-ion smart chargers. 16340 cells are often used in flashlights or other small compact electronics
Power Bank Charger Circuit (buy from aliexpress.com)
This power bank circuit uses two integrated modules. The first module is a lithium-ion battery charger and the second is a DC-DC boost converter module.
Fiber-glass Perf Board (buy from aliexpress)
Opening the PS2 Controller
Remove the six 7.8 mm anchoring screws from the back casing of the controller using a Phillips screwdriver. Pull apart the outer casing by hand, or by using a plastic opening tool, from the bottom of the controller. Remove two vibration motors from inside the controller. We don't need them.
If you want to use the dc vibration motors, you will need to add a motor driver (example: L293D) as you cannot directly connect it to the pins of the Arduino as it requires a lot of currents which may harm the Arduino.
Cut the data cable of the PS2 Controller keeping 10 cm with the circuit.
Preparing the PCB
For bringing the wireless functionality in the remote we will use Arduino Nano and nRF24L01 wireless transceiver. I will place the Nano board and the nRF24l01 module in a perf board and connect the data and power lines of the controller circuit.
I used an anti-cutter to cut the perf board according to my measurements. The size of the PCB piece I prepared is 10CM X 2CM.
PCB will house the Arduino Nano, nRF24L01 wireless transceiver, and the Li-ion charger and boost converter circuit. I will use female headers to connect the Arduino with the PCB.
Making the Connections
The connection between Controller and Arduino Nano
Connect the controller pins to Arduino according to the image attached. This connection is changeable but you need to change it in the Arduino program also.
The connection between nRF24L01 and Arduino Nano
The connection is exactly the same on both the transmitter and receiver end.
Connect the GND pin to -ve and VCC pin to 3.3v pin of Arduino. The signals generated by these modules are very sensitive to power supply noises. So, adding a decoupling capacitor (anything from 10uF to 100uF) across the power supply line is always a very good idea.
Then connect the CSN pin to D9, CE to D10, MOSI to D11, MISO to D12, and SCK to D13 pin of the Arduino.
Since the nRF24L01+ module requires a lot of data transfer, it will give the best performance when connected to the hardware SPI pins on the microcontroller. Note that each Arduino board has different SPI pins that must be connected accordingly. Have a look at the table onscreen for a quick understanding.
PCB Connections
For connecting the Arduino with the controller board I directly used the wires cut from the cable.
The power consumption of this module is just around 12mA during transmission, which is even lower than a single LED. The operating voltage of the module is from 1.9 to 3.6V, but the good thing is that the other pins tolerate 5V logic, so we can easily connect it to an Arduino without using any logic level converters.
Three of these pins are for the SPI communication and they need to be connected to the SPI pins of the Arduino, but note that each Arduino board has different SPI pins. The pins CSN and CE can be connected to any digital pin of the Arduino board and they are used for setting the module in standby or active mode, as well as for switching between transmitting or command mode. The last pin is an interrupt pin which doesn’t have to be used.
Battery Charger & Boost Converter
The converted RC remote will be powered by li-ion battery. Li-ion batteries required a sophisticated charging circuit that protects the battery from overcharge and deep discharge. The output voltage of a li-ion cell is 3.7V which is not enough for the PS2 internal circuit. So, I converted that voltage to 5V using a boost converter circuit. The good news is that the li-ion charger circuit and boost converter circuit are available in a single PCB. That is called power bank circuit, generally used in power banks.
So, I used a power bank circuit to charge the battery and as well as to step up the voltage to 5V. I cut out the USB output port from the PCB as it is not required in our case.
Attaching All the Parts
All the parts like PCB board for connecting Nano, NRF24L01, and charger circuits are ready. Now we need to connect all the components together. The Nano and the NRF module will be connected to the female header we soldered in the PCB board. The power bank circuit will be placed using hot glue. Before that, we need to complete the necessary soldering of the power bank circuit. The power bank circuit has 2 (two) through solder pads for connecting the battery. As we will place batteries inside the controller and the power bank circuit will be outside of the controller I soldered 2 long wires to the battery connector pad of the power bank circuit. The output of the power bank circuit is connected to the 5V input of the circuit through an SPDT switch. The ground is directly connected to the ground of the system.
After making the connections and gluing the power bank circuit with the PCB I placed the final terminal of the circuit PCB on top of the controller PCB. Then I used hot glue to fix them together. I also use some hot glue to organize the connecting wires and make them protected. Finally, I placed the 16340 li-ion battery inside the controller and placed and fixed in place with hot glue. Two li-ion batteries can be used in parallel to get more battery backup or you can use one.
Final Assembling
All the circuit placement is completed. It is the right time to re-assemble the controller. We have already the top part with the glued PCB. Now we need to place the bottom part in the right place and use the screws to make it fix with the top part.
After final assembling your controller is ready to remotely control your robot car but before that, you have a final task. That is the programming. You need to upload the Arduino program to read the joysticks and button and send it to your RC car through the nRF24L01 module. In the next step, I will add the Arduino sketch for you.
Uploading the Arduino Sketch
The following sketch can be uploaded to your nano to transmit the value to the RC car. Your receiver also needs to upload a code that is synchronized with this code.
Arduino PS2X library was used in this Arduino program. The zip file with the library and the code are attached. You can separately download the library from here:
#include <PS2X_lib.h> #include <RF24_config.h> #include <RF24.h> #include <nRF24L01.h> #include <SPI.h> /*-------------------Preprocessors-----------------------*/ //RF24 #define PIN_07_RF_CE 9 #define PIN_08_RF_CSN 10 #define RF_PAYLOAD_SIZE_10 10 //PS2X #define PIN_02_PS2X_ATT 5 #define PIN_03_PS2X_COM 6 #define PIN_04_PS2X_DTA 7 #define PIN_05_PS2X_CLK 8 /*--------Variables/Class Declarations-------------------*/ //NRF24 RF24 radio(PIN_07_RF_CE, PIN_08_RF_CSN); const uint64_t pipe = 0xE8E8F0F0E1LL; // Define the transmit pipe, NOTE: the "LL" at the end of the constant is "LongLong" type unsigned int rx_status[2] = { 0, 0}; //PS2X PS2X ps2x; int ps2x_ErrorCheck = 0; char vibrate = 0; //analogs typedef struct analogs { unsigned int leftX; unsigned int leftY; unsigned int rghtX; unsigned int rghtY; }; //digitals typedef union digitals{ struct { unsigned int l1 : 1; unsigned int l2 : 1; unsigned int r1 : 1; unsigned int r2 : 1; unsigned int up : 1; unsigned int dn : 1; unsigned int lf : 1; unsigned int rt : 1; unsigned int tr : 1; unsigned int cr : 1; unsigned int sq : 1; unsigned int cl : 1; unsigned int sl : 1; unsigned int st : 1; }bits; unsigned int onoff; } ; struct keys { analogs analogKeys; digitals digitalKeys; }; keys keyValues; void setup() { // enable serial monitor for debugging Serial.begin(115200); //RF24 radio.begin(); radio.setAutoAck(true); radio.enableAckPayload(); radio.enableDynamicPayloads(); //radio.setPayloadSize(sizeof(keyValues)+1); radio.setPALevel(RF24_PA_LOW); //try to set to RF24_PA_LOW if there are some issues, RF24_PA_MAX is the default radio.stopListening(); radio.openWritingPipe(pipe); radio.setRetries(5, 5); //PS2X setup ps2x_ErrorCheck = ps2x.config_gamepad(PIN_05_PS2X_CLK, PIN_03_PS2X_COM, PIN_02_PS2X_ATT, PIN_04_PS2X_DTA, true, true); //setup pins and settings: GamePad(clock, command, attention, data, Pressures?, Rumble?) check for error switch(ps2x_ErrorCheck){ case 0: Serial.println("Found Controller, configured successful"); Serial.println("Try out all the buttons, X will vibrate the controller, faster as you press harder;"); Serial.println("holding L1 or R1 will print out the analog stick values."); Serial.println("Go to www.billporter.info for updates and to report bugs."); break; case 1: Serial.println("No controller found, check wiring, see readme.txt to enable debug. visit www.billporter.info for troubleshooting tips"); break; case 2: Serial.println("Controller found but not accepting commands. see readme.txt to enable debug. Visit www.billporter.info for troubleshooting tips"); break; case 3: default: Serial.println("Controller refusing to enter Pressures mode, may not support it. "); break; } memset(&keyValues, 0, sizeof(keyValues)); // intialize struct to 0 } void loop() { // PS2X read gamepad values: gameController_Reading(); //RF24 if (radio.write(&keyValues, sizeof(keyValues)) ) { Serial.println("...tx success"); if (radio.isAckPayloadAvailable()) { radio.read(rx_status, sizeof(rx_status)); Serial.print("received ack payload is : "); Serial.println(rx_status[0]); } else { Serial.println("status has become false so stop here...."); } } } //Read the controllers inputs void gameController_Reading() { ps2x.read_gamepad(false, false); //read controller but set rumble motor to off //analog readings keyValues.analogKeys.leftX = ps2x.Analog(PSS_LX); // left analog stick keyValues.analogKeys.leftY = ps2x.Analog(PSS_LY); keyValues.analogKeys.rghtX = ps2x.Analog(PSS_RX); // right analog stick keyValues.analogKeys.rghtY = ps2x.Analog(PSS_RY); // digital readings if (ps2x.NewButtonState()) { // use masking to toggle the value if (ps2x.ButtonPressed(PSB_L1)) keyValues.digitalKeys.bits.l1 = toggleButton(0x0001); if (ps2x.ButtonPressed(PSB_L2)) keyValues.digitalKeys.bits.l2 = toggleButton(0x0002); if (ps2x.ButtonPressed(PSB_R1)) keyValues.digitalKeys.bits.r1 = toggleButton(0x0008); if (ps2x.ButtonPressed(PSB_R2)) keyValues.digitalKeys.bits.r2 = toggleButton(0x0010); if (ps2x.ButtonPressed(PSB_PAD_UP)) keyValues.digitalKeys.bits.up = toggleButton(0x0040); if (ps2x.ButtonPressed(PSB_PAD_DOWN)) keyValues.digitalKeys.bits.dn = toggleButton(0x0080); if (ps2x.ButtonPressed(PSB_PAD_LEFT)) keyValues.digitalKeys.bits.lf = toggleButton(0x0100); if (ps2x.ButtonPressed(PSB_PAD_RIGHT)) keyValues.digitalKeys.bits.rt = toggleButton(0x0200); if (ps2x.ButtonPressed(PSB_TRIANGLE)) keyValues.digitalKeys.bits.tr = toggleButton(0x0400); if (ps2x.ButtonPressed(PSB_CROSS)) keyValues.digitalKeys.bits.cr = toggleButton(0x0800); if (ps2x.ButtonReleased(PSB_CROSS)) keyValues.digitalKeys.bits.cr = toggleButton(0x0800); if (ps2x.ButtonPressed(PSB_SQUARE)) keyValues.digitalKeys.bits.sq = toggleButton(0x1000); if (ps2x.ButtonPressed(PSB_CIRCLE)) keyValues.digitalKeys.bits.cl = toggleButton(0x2000); if (ps2x.ButtonPressed(PSB_SELECT)) keyValues.digitalKeys.bits.sl = toggleButton(0x4000); if (ps2x.ButtonPressed(PSB_START)) keyValues.digitalKeys.bits.st = toggleButton(0x8000); } } int toggleButton(unsigned int mask) { if ((keyValues.digitalKeys.onoff & mask) == 0) { return 1; } else { return 0; } }