How to Connect a Cheap AliExpress 9-Pin NES-Style Controller to an ESP32

by Cyrill in Circuits > Arduino

39 Views, 1 Favorites, 0 Comments

How to Connect a Cheap AliExpress 9-Pin NES-Style Controller to an ESP32

Screenshot 2026-06-02 090227.png

Many cheap retro game controllers from AliExpress use a 9-pin connector, but inside the cable there may only be 5 wires.

Supplies

What You Need

  1. ESP32 development board
  2. Cheap AliExpress 9-pin retro/NES-style controller - https://de.aliexpress.com/item/32919127872.html
  3. Jumper wires
  4. Multimeter
  5. Optional but recommended: 1 kΩ resistors for the three signal lines

Info

Many cheap retro game controllers from AliExpress use a 9-pin connector, but inside the cable there may only be 5 wires. I cut open one of these controllers and found the following wire colours:

White
Blue
Red
Brown
Yellow

The controller PCB was marked:

NES Ver:2.0

Although the plug looks like a DB9-style retro connector, this is not a simple Atari-style joystick where every direction has its own wire. It is a NES-style serial controller. That means it uses only five connections:

VCC
GND
Latch / Strobe
Clock
Data

After testing with a multimeter and ESP32, this was the working pinout for my controller.

Final Wire Mapping

Wire colourFunctionESP32 connection

Red VCC 3V3

Yellow GND

White Data

GPIO 25

Blue Latch

GPIO 27

Brown Clock

GPIO 26

Important: wire colours may vary between manufacturers. This mapping worked for my AliExpress NES Ver:2.0 9-pin controller with 5 internal wires.

Open the Controller

Remove the screws from the back of the controller and inspect the PCB.

Mine had five cable wires soldered to the board:

White
Brown
Blue
Yellow
Red

The board was marked NES Ver:2.0, which was the first clue that this was a NES-style serial controller.

Identify the Power Wires

Using diode mode on the multimeter, I found that red and yellow behave like the power pair.

The result was:

Red = VCC
Yellow = GND

On my controller, connecting red to 3.3 V and yellow to GND worked correctly.

Use 3.3 V, not 5 V, when connecting to an ESP32. The ESP32 GPIO pins are not 5 V tolerant.

Connect the Controller to the ESP32

Use this wiring:

Controller wire ESP32 pin

Red 3V3

Yellow GND

White GPIO 25

Brown GPIO 26

Blue GPIO 27

Recommended safer test wiring:

White -> 1 kΩ resistor -> GPIO 25
Brown -> 1 kΩ resistor -> GPIO 26
Blue -> 1 kΩ resistor -> GPIO 27

The resistors are not strictly required once the pinout is confirmed, but they are useful protection during testing

Upload the ESP32 Test Code

This code reads the controller and prints the pressed buttons in the Serial Monitor.

Open the Serial Monitor at:

115200 baud

ESP32 Arduino Code

/*
AliExpress 9-pin NES-style controller to ESP32

Tested controller:
- PCB marked "NES Ver:2.0"
- 9-pin external connector
- 5 internal wires

Working wire mapping:
Red = 3.3 V / VCC
Yellow = GND
White = LATCH / STROBE
Blue = CLOCK
Brown = DATA

ESP32 wiring:
Red -> 3V3
Yellow -> GND

NES-style protocol:
- LATCH captures the current button state
- CLOCK shifts out one button bit at a time
- DATA is read by the ESP32
- Buttons are active LOW
*/

const int PIN_LATCH = 27; // Blue wire
const int PIN_DATA = 25; // White wire
const int PIN_CLOCK = 26; // Data wire

uint8_t readControllerRaw() {
uint8_t raw = 0;

// Make sure latch and clock start low
digitalWrite(PIN_LATCH, LOW);
digitalWrite(PIN_CLOCK, LOW);
delayMicroseconds(10);

// Latch current button states
digitalWrite(PIN_LATCH, HIGH);
delayMicroseconds(12);
digitalWrite(PIN_LATCH, LOW);
delayMicroseconds(6);

// Read 8 bits, least significant bit first
for (int i = 0; i < 8; i++) {
if (digitalRead(PIN_DATA) == HIGH) {
raw |= (1 << i);
}

digitalWrite(PIN_CLOCK, HIGH);
delayMicroseconds(6);
digitalWrite(PIN_CLOCK, LOW);
delayMicroseconds(6);
}

return raw;
}

void printBinaryMSB(uint8_t value) {
for (int i = 7; i >= 0; i--) {
Serial.print((value >> i) & 1);
}
}

void printButtons(uint8_t raw) {
// NES-style controllers are usually active LOW:
// raw bit = 1 means not pressed
// raw bit = 0 means pressed
uint8_t pressed = ~raw;

bool anyPressed = false;

if (pressed & (1 << 0)) {
Serial.print("A ");
anyPressed = true;
}

if (pressed & (1 << 1)) {
Serial.print("B ");
anyPressed = true;
}

if (pressed & (1 << 2)) {
Serial.print("SELECT ");
anyPressed = true;
}

if (pressed & (1 << 3)) {
Serial.print("START ");
anyPressed = true;
}

if (pressed & (1 << 4)) {
Serial.print("UP ");
anyPressed = true;
}

if (pressed & (1 << 5)) {
Serial.print("DOWN ");
anyPressed = true;
}

if (pressed & (1 << 6)) {
Serial.print("LEFT ");
anyPressed = true;
}

if (pressed & (1 << 7)) {
Serial.print("RIGHT ");
anyPressed = true;
}

if (!anyPressed) {
Serial.print("none");
}
}

void setup() {
Serial.begin(115200);
delay(1000);

pinMode(PIN_LATCH, OUTPUT);
pinMode(PIN_CLOCK, OUTPUT);
pinMode(PIN_DATA, INPUT_PULLUP);

digitalWrite(PIN_LATCH, LOW);
digitalWrite(PIN_CLOCK, LOW);

Serial.println();
Serial.println("AliExpress NES-style controller test");
Serial.println("Wire mapping:");
Serial.println("Red = 3.3 V");
Serial.println("Yellow = GND");
Serial.println("White = LATCH");
Serial.println("Blue = CLOCK");
Serial.println("Brown = DATA");
Serial.println();
}

void loop() {
uint8_t raw = readControllerRaw();

Serial.print("Raw binary: ");
printBinaryMSB(raw);

Serial.print(" | Raw hex: 0x");
if (raw < 16) {
Serial.print("0");
}
Serial.print(raw, HEX);

Serial.print(" | Pressed: ");
printButtons(raw);

Serial.println();

delay(100);
}

Expected Serial Output

With no button pressed, the raw value should normally be:

Raw binary: 11111111 | Raw hex: 0xFF | Pressed: none

When pressing a button, one bit should change from 1 to 0.

Typical NES button order:

ButtonRaw hex value

A 0xFE

B 0xFD

Select 0xFB

Start 0xF7

Up 0xEF

Down 0xDF

Left 0xBF

Right 0x7F

Some clone controllers may use a different button order, so test every button individually.

How the Protocol Works

The controller does not send all buttons on separate wires. Instead, the ESP32 talks to it using three signal lines:

Latch
Clock
Data

The sequence is:

  1. ESP32 sends a short pulse on Latch.
  2. The controller captures the current state of all buttons.
  3. ESP32 sends 8 pulses on Clock.
  4. On each clock pulse, the controller sends one button state on Data.
  5. The ESP32 reads the 8 bits and converts them into button names.

The buttons are normally active LOW:

1 = not pressed
0 = pressed

Troubleshooting

Nothing changes when pressing buttons

Check:

Red -> 3V3
Yellow -> GND
White -> GPIO 25
Blue -> GPIO 27
Brown -> GPIO 26

Also make sure the Serial Monitor is set to:

115200 baud

Random values appear

Possible causes:

  1. DATA wire is not connected properly
  2. GND is missing
  3. Wrong signal mapping
  4. No pull-up on DATA

The code uses:

pinMode(PIN_DATA, INPUT_PULLUP);

so the data line should not float.

One button is always shown as pressed

Check whether that button is physically stuck or whether the carbon pad is shorted. Also inspect the PCB for solder bridges or damaged traces.

Controller does not work at 3.3 V

My controller worked at 3.3 V. Some clones may expect 5 V. If you use 5 V, do not connect the DATA line directly to the ESP32. Use a level shifter or at least a voltage divider on the DATA line.

For ESP32, the safest first test is always:

VCC = 3.3 V

Final Pinout Summary

For my AliExpress 9-pin NES-style controller with 5 internal wires:

Red = VCC / 3.3 V
Yellow = GND
White = DATA
Blue = LATCH
Brown = CLOCK

ESP32 pins used in the example:

White -> GPIO 25
Brown -> GPIO 26
Blue -> GPIO 27
Red -> 3V3
Yellow -> GND

This saves others from having to repeat the full multimeter and permutation-testing procedure.