ESP32: Analog Joystick is a runnable microcontroller simulation example on SensBench — open the wired circuit, inspect the code, and compile real firmware in your browser, no install needed. Last updated Aug 14, 2026.

FREE SIMULATOR EXAMPLE · BEGINNER

ESP32: Analog Joystick

This example reads a two-axis analog joystick: X on GPIO35, Y on GPIO34, and the click button on GPIO15 using the internal pull-up. The Serial Monitor prints raw ADC counts, a −100 to +100 percentage per axis and the button state, ten times a second.

Board ESP32 DevKit (Xtensa LX6, 240 MHz)
Difficulty beginner
Category sensors
Libraries None — just the Arduino core
Runs in Sensbench browser simulator — free, no account
ESP32 reading a two-axis analog joystick, X on GPIO35, Y on GPIO34 and click button on GPIO15, in the Sensbench simulator

The exact circuit you get when you open this example — already wired, ready to run.

The sketch

// ESP32 — Analog Joystick
// Wiring: HORZ → D35 | VERT → D34 | SEL → D15
//         VCC → 3V3  | GND → GND


#define JOY_HORZ 35  // input-only ADC pin
#define JOY_VERT 34  // input-only ADC pin
#define JOY_BTN  15  // GPIO with pull-up


void setup() {
  Serial.begin(115200);
  pinMode(JOY_BTN, INPUT_PULLUP);
  Serial.println("ESP32 Joystick ready");
}

void loop() {
  int x    = analogRead(JOY_HORZ); // 0–4095
  int y    = analogRead(JOY_VERT); // 0–4095
  bool btn = (digitalRead(JOY_BTN) == LOW);

  int xPct = map(x, 0, 4095, -100, 100);
  int yPct = map(y, 0, 4095, -100, 100);

  Serial.printf("X=%4d(%4d%%) Y=%4d(%4d%%) BTN=%s\n",
    x, xPct, y, yPct, btn ? "PRESSED" : "---");
  delay(100);
}

How it works

The two axes sit on GPIO35 and GPIO34 — the code comments call them what they are: input-only ADC pins. analogRead() returns 0–4095 from the ESP32’s 12-bit ADC, then map(x, 0, 4095, -100, 100) turns raw counts into a signed percentage, so centre reads near 0 and the stick’s ends read ±100.

The click button is wired to GPIO15 with pinMode(JOY_BTN, INPUT_PULLUP), which makes it active-low: digitalRead(JOY_BTN) == LOW means pressed, no external resistor needed. A delay(100) closes the loop, so the Serial Monitor gets a fresh line of X, Y and button state ten times a second — fast enough to feel live while you wiggle the stick.

Before you build it for real

GPIO34 and GPIO35 are input-only — no output driver, no internal pull-up — which is exactly why they suit a joystick’s voltage-divider wipers, but never plan to reuse them for anything you need to drive. GPIO15 is a strapping pin: it must read high at reset, so if the button is held down while the board boots you can silence the boot log and confuse yourself. And real sticks never centre at exactly 2048 — add a deadband around zero or your project will drift on its own.

Get the parts

Related examples

FAQ

Why are the axes on GPIO34/35 and not other pins?

Those two are input-only ADC1 pins: they cannot drive anything, but they read analog cleanly and — more importantly — ADC1 keeps working when WiFi is on, while ADC2 pins get claimed by the radio. For any project that might grow WiFi later, reading analog on ADC1 is the safe habit.

The values jitter around center — is that normal?

Yes. Cheap joystick pots are electrically noisy and the ESP32’s ADC wanders a few counts on its own; the simulator just makes it look clean. Average several readings and add a deadband of about ±3–5% around zero before your code acts on the value.