MBC PS2 / PS4 Controller receiver × Arduino

Read the PS2 / PS4 Controller receiver with Arduino in three steps

An Arduino program goes in this order. First wire it up and paste the helper. Then, in setup(), start talking and wait until the receiver is ready. Last, in loop(), read the buttons and sticks with mbcRead(). The PS2 receiver (Controller 2.4G (PS2)) and the PS4 receiver (Controller PS4/PS5) use exactly the same program. Follow the steps on the left, and the program on the right grows with you. Click the buttons on the gamepad simulator and drag the sticks to see what the Serial Monitor shows.

1

Wire it up and paste the helper

Arduino talks to the sensor over I2C (4 wires). You do not need to install a library. Write 2 lines at the very top and paste a "helper" at the very bottom. Then you can read values with mbcRead() and send commands with mbcWrite().

Which board do you use? (Only the SDA / SCL pins change. The program is the same.)

  1. Unplug the USB cable before wiring. Use the Universal I2C edition (the SPIKE and EV3 editions have different firmware and cannot connect to Arduino).
  2. Sensor VCC → 5V, GND → GND
  3. Sensor SDA → A4, SCL → A5
  4. The receiver is marked TX, RX: on the Universal I2C edition RX is SDA and TX is SCL. The receiver has a built-in level shifter, so 5 V and 3.3 V boards can both connect directly.
  5. PS2: switch the gamepad on and it connects. Press the ANALOG button in the middle of the gamepad so its light is on (analog mode); only then do the sticks give numbers. PS4 / PS5: pair it the first time. Within 30 seconds after the receiver powers on (or after you press its Reset button), hold SHARE + PS on a PS4 gamepad or Create + PS on a PS5 gamepad until the light bar blinks fast. After that, pressing PS connects it automatically.
  6. Open the Arduino IDE: File → New, and delete what is already there.
  7. Write lines 1 and 2 at the very top. Paste the "helper" at the very bottom (marked ① in the program on the right; "Copy program" copies it too).
2

Start talking and wait until the sensor is ready

setup() runs only once, when the board starts. It opens the serial port, starts I2C and waits until the sensor says "I am ready". ADDR is the sensor's "house number" (its I2C address). You write it every time you read a value.

The sensor's I2C address:

If the sensor is not found:

3

Use a function

To read a value, write "mbcRead(ADDR, id)". To send a command, write "mbcWrite(ADDR, command)". Click a function, and the program and the Serial Monitor change to its example.

Gamepad simulator: click the buttons, drag the sticks, and watch the Serial Monitor on the right

The simulator is only a sketch. Click a button once to hold it, click it again to let go. Drag the dot on a stick to move it (it stays where you let go; press "Release all" to centre it). Sticks go from −100 to +100, 0 = centre, and right and up are positive. The PS4 gamepad battery is fixed at 80 in this demo. Only the PS4 receiver has the PS button; L3 and R3 (pushing a stick down) cannot be pressed here. Gamepad not connected: the receiver is still there, 0x06 = 0 and every button and stick reads 0. Unplug the receiver: every value the Arduino reads is -32768.

An example for every function

26 functions (values, commands, and the shared ids every MBC sensor has). Each one has a short example. The ones marked "PS4 only" only work on the PS4 receiver; the PS2 receiver ignores them or always gives 0. Press "Try it" to jump to step 3. The program and the Serial Monitor on the right change to that example.

Full examples

Press "Copy" to get the whole program (the helper is already at the bottom). Paste it into the Arduino IDE and upload.

Tank-style remote-control car (L298N motor driver)

        

!The left stick (up / down) drives the left wheel and the right stick drives the right wheel. If the gamepad disconnects or the receiver cannot be read, the car stops at once. The first time, lift the car so the wheels are off the ground: push both sticks up. If a wheel turns the wrong way, set its REVERSE to true. Too fast: make MAX_SPEED smaller. Connect the L298N GND to the Arduino GND. Works with both the PS2 and the PS4 receiver.

Which button did I press?

        

!Press or release any button and the Serial Monitor tells you which one. You can press several at once. The 16 buttons are read with mbcReadMany(), because pressing only ■ gives exactly -32768, and with mbcRead() you could not tell that apart from "no answer".

Rumble and light bar (PS4 / PS5 gamepads only)

        

!Hold ✕ and the gamepad rumbles. Push the left stick to change the light bar colour (left / right -> red, up / down -> green). Each E5 rumbles for 200 ms, so the program sends it again every loop while you hold the button. E6 needs exactly 3 numbers: red, green, blue. With a PS2 receiver the program still runs, but the gamepad does not rumble and has no light bar.

Good to know

  1. "Receiver unplugged" and "gamepad off" are not the same. When the receiver cannot be read, mbcRead() gives -32768 (MBC_NONE) and mbcReadMany() gives false. When the receiver is plugged in but the gamepad is off (flat battery, not paired), 0x06 connected = 0 and every button and stick reads 0. A remote-control car should stop in both cases (the tank car below does this). Tick "Gamepad not connected" and "Unplug the receiver" on the right to compare.
  2. Pressing only ■ makes 0x01 (16 buttons) exactly -32768. ■ is the top bit (bit 15). When it is the only bit that is 1, the number is the same as MBC_NONE ("no answer"). To tell them apart, read it with mbcReadMany() (it gives true or false), or read 0x03 shape buttons instead (■ = 8).
  3. Both receivers use the same ids, and both are at address 0x42. The only differences: 0x07 battery and 0x0A system keys are always 0 on PS2; 0x0B gamepad type 3 and 4 only appear on the PS4 receiver; E5 rumble and E6 light bar only work with PS4 / PS5 gamepads, and the PS2 receiver ignores them (the program does not fail). Only one of them can be on one I2C bus. Tell them apart with the high byte of 0xF0 (1 = PS2, 2 = PS4).
  4. Sticks go from −100 to +100, 0 = centre. Standing behind the robot and looking forward: left and down are negative, right and up are positive. A small wobble near the centre stays at 0 (the dead zone), so the robot does not creep when you let go. Read the neighbouring ids 0x10–0x13 with one mbcReadMany() call, so they all come from the same moment.
  5. Do not delete delay(2) in the helper. The PS4 / PS5 receiver is built on an ESP32 and needs 2 ms after you write the id before its data is ready. Both receivers share one address, so the program cannot tell them apart and always waits. The receiver takes a little over 1 second to start, so step 2 waits up to 5 seconds. Set the Serial Monitor to 115200 baud, or you will see strange symbols.