Preview build — page text is a draft awaiting human review. Code badges come from real compiles. The flasher is real but not yet hardware-tested; the Playground is still a preview.

Joystick module with Arduino Uno — read X, Y and the button

Wire a KY-023 style joystick to an Arduino Uno R3 (VRx A0, VRy A1, SW D2), calibrate the centre, add a deadzone and read direction and button.

Arduino C++Verified — core avr 1.8.8, 3 Nov 2026Compile-only

Community draft — report a mistake

The thumb joystick module is two potentiometers (X and Y) plus a push switch under the stick. It is the usual controller for pan-tilt servos, robot cars and simple games on an Uno.

Both axes are analog, so they go to A0 and A1. The Uno’s 10-bit ADC reads them from 0 to 1023, with about 512 at rest. The button closes to GND when you press the stick down. It goes to D2 with the Uno’s internal pull-up turned on, so no resistor is needed.

The sketch calibrates the centre at start-up, ignores small wobble, and prints each axis as −100 % to +100 % with a direction word, ready to map onto servo angles or motor speeds.

Wiring

Arduino Uno R3Only the pins used are shown · 5 V logicDual-axis joystick module (KY-023)5VGNDA0A1D2GND+5VVRxVRySW
Every connection
Arduino Uno R3Dual-axis joystick module (KY-023)WireNotes
GNDGNDblack
5V+5VredThe pots sit across this supply, so 5 V gives the full 0–1023 range on the Uno.
A0VRxyellow
A1VRygreen
D2SWbluePush switch to GND. Most modules have no pull-up, so the sketch turns on the Uno’s internal one.

3.3–5 V part, 5 V board. VRx and VRy swing between 0 V and the module’s supply. On ESP32 and Pico, power the joystick from 3.3 V (the pin labelled +5V is just the supply input) so the analog outputs never exceed 3.3 V.

Board quirks

  • The centre is rarely exactly 512 — 490–530 is typical. The sketch reads it at start-up, so leave the stick alone while the Uno resets.
  • Each half of the travel is scaled separately, and a small deadzone stops the reading flickering around zero.
  • Which way is “up” depends on how you hold the module. If a direction is reversed, swap the sign of x or y rather than rewiring.
  • Readings are ratiometric: the pots and the Uno’s ADC both use the same 5 V, so a USB supply that is really 4.8 V doesn’t shift the result.

Code

joystick-uno.inoArduino C++Open in PlaygroundOpen in PlaygroundFlash
// Analog joystick module (KY-023 style) on an Arduino Uno R3.
// GND → GND, +5V → 5V, VRx → A0, VRy → A1, SW → D2 (internal pull-up; pressed = LOW).
const uint8_t X_PIN = A0, Y_PIN = A1, SW_PIN = 2;
const int DEADZONE = 40;  // ignore small wobble around the centre

int centreX = 512, centreY = 512;

int axis(int raw, int centre) {
  int v = raw - centre;
  if (abs(v) < DEADZONE) return 0;
  // Scale each half separately: the centre is rarely exactly 512.
  return v > 0 ? map(v, DEADZONE, 1023 - centre, 0, 100) : map(v, -centre, -DEADZONE, -100, 0);
}

void setup() {
  pinMode(SW_PIN, INPUT_PULLUP);
  Serial.begin(9600);
  centreX = analogRead(X_PIN);  // don't touch the stick during reset
  centreY = analogRead(Y_PIN);
  Serial.print(F("Joystick centre X "));
  Serial.print(centreX);
  Serial.print(F(" Y "));
  Serial.println(centreY);
}

void loop() {
  int x = constrain(axis(analogRead(X_PIN), centreX), -100, 100);
  int y = constrain(axis(analogRead(Y_PIN), centreY), -100, 100);
  bool pressed = digitalRead(SW_PIN) == LOW;
  Serial.print(F("X "));
  Serial.print(x);
  Serial.print(F("%  Y "));
  Serial.print(y);
  Serial.print(F("%  "));
  if (pressed) Serial.print(F("[button] "));
  if (y > 50) Serial.print(F("UP"));
  else if (y < -50) Serial.print(F("DOWN"));
  if (x > 50) Serial.print(F(" RIGHT"));
  else if (x < -50) Serial.print(F(" LEFT"));
  Serial.println();
  delay(200);
}

Expected serial output

Serial Monitor · 9600Output
Joystick centre X 508 Y 517
X 0%  Y 0%
X 0%  Y 100%  UP
X -100%  Y 0%   LEFT
X 0%  Y 0%  [button]

Common errors

The button reads pressed all the time, or flickers

INPUT_PULLUP is missing, or SW is on a pin other than D2. Without the pull-up the pin floats.

X and Y are swapped

Module silkscreens differ. Swap A0 and A1 in the sketch.

Values never reach 0 or 1023

Normal for cheap sticks — they often stop around 5 and 1018. The scaling still reaches ±100 % thanks to constrain().

↑↓ to move · Enter to open