KY-023 Dual Axis Joystick Module
The KY-023 is a dual-axis joystick module that provides analog outputs for X and Y positions, along with a digital output for a built-in push-button. It's ideal for applications requiring directional input, such as remote controls, gaming interfaces, and robotic navigation.

On this page
KY-023 pinout
The KY-023 is a 5-pin dual-axis joystick module with integrated push button:
| Pin | Type | Description | Notes |
|---|---|---|---|
| GND | Power | Ground connection | |
| +5V | Power | Power supply | 3.3V or 5V (check compatibility) |
| VRx | Communication | X-axis analog output | Voltage varies with X position |
| VRy | Communication | Y-axis analog output | Voltage varies with Y position |
| SW | Control | Switch/button signal | Active low when pressed |
Interface: Dual analog output + digital button
Axes: X and Y axis potentiometers (0-3.3V or 0-5V range)
Button: Built-in push-button (active low when pressed)
Power: 3.3V or 5V operation
Center Position: ~1.65V (3.3V) or ~2.5V (5V) when centered
Applications: Game controllers, robotic navigation, camera control, RC vehicles
Wiring the KY-023 to ESP32
To interface the KY-023 with an ESP32 for joystick input:
| KY-023 pin | ESP32 pin | Purpose |
|---|---|---|
| GND | GND | Ground |
| +5V | 3.3V | Power supply (use 3.3V for ESP32) |
| VRx | GPIO36 | X-axis analog input (ADC pin) |
| VRy | GPIO39 | Y-axis analog input (ADC pin) |
| SW | GPIO34 | Button input with internal pull-up · optional |
ADC Pins: Use GPIO32-39 for analog inputs on ESP32
Voltage: Use 3.3V to match ESP32 ADC range (0-3.3V)
Centering: Read center position at startup for calibration
Pull-up: Enable internal pull-up resistor on SW pin
Mapping: Map ADC values (0-4095) to desired ranges
KY-023 code examples
KY-023 Arduino example
Copy// Declaration and initialization of the input pins
int JoyStick_X = 36; // X-axis signal (GPIO36 / ADC1_CH0, matches the wiring above)
int JoyStick_Y = 39; // Y-axis signal (GPIO39 / ADC1_CH3)
int Button = 34; // Button (GPIO34 - input only, needs an external pull-up to 3.3V)
void setup() {
pinMode(Button, INPUT); // GPIO34 has no internal pull-up; add an external ~10k resistor
Serial.begin(115200);
}
void loop() {
float x, y;
int buttonState;
// Read current values and convert to voltage (ESP32: 12-bit ADC, 3.3V)
x = analogRead(JoyStick_X) * (3.3 / 4095.0);
y = analogRead(JoyStick_Y) * (3.3 / 4095.0);
buttonState = digitalRead(Button);
// Output the values
Serial.print("X-axis:"); Serial.print(x, 4); Serial.print("V, ");
Serial.print("Y-axis:"); Serial.print(y, 4); Serial.print("V, ");
Serial.print("Button:");
if (buttonState == HIGH) {
Serial.println("not pressed");
} else {
Serial.println("pressed");
}
delay(200);
}This Arduino code reads the analog values from the X and Y axes of the KY-023 joystick and the digital state of the push-button. It then prints the corresponding voltage levels and button state to the serial monitor.
KY-023 ESP-IDF example
Copy#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/adc.h"
#include "driver/gpio.h"
#define JOYSTICK_X ADC1_CHANNEL_0 // GPIO36
#define JOYSTICK_Y ADC1_CHANNEL_3 // GPIO39
#define BUTTON GPIO_NUM_34
void app_main(void) {
adc1_config_width(ADC_WIDTH_BIT_12);
adc1_config_channel_atten(JOYSTICK_X, ADC_ATTEN_DB_11);
adc1_config_channel_atten(JOYSTICK_Y, ADC_ATTEN_DB_11);
gpio_set_direction(BUTTON, GPIO_MODE_INPUT);
gpio_pulldown_en(BUTTON);
printf("KY-023 Joystick Test\n");
while (1) {
int x_value = adc1_get_raw(JOYSTICK_X);
int y_value = adc1_get_raw(JOYSTICK_Y);
int button_state = gpio_get_level(BUTTON);
printf("X: %d, Y: %d, Button: %s\n", x_value, y_value, button_state ? "Not Pressed" : "Pressed");
vTaskDelay(pdMS_TO_TICKS(200));
}
}This ESP-IDF code reads analog values from the KY-023 joystick's X and Y axes using ADC1 on GPIO36 and GPIO39. It also monitors the push-button state on GPIO34 and prints the readings to the console every 200ms.
KY-023 ESPHome example
Copysensor:
- platform: adc
pin: GPIO36
name: "KY-023 Joystick X-Axis"
attenuation: 12db
update_interval: 200ms
- platform: adc
pin: GPIO39
name: "KY-023 Joystick Y-Axis"
attenuation: 12db
update_interval: 200ms
binary_sensor:
- platform: gpio
pin:
number: GPIO34 # input-only, no internal pull-up - add an external ~10k resistor to 3.3V
inverted: true
name: "KY-023 Joystick Button"The two axes are adc sensors on GPIO36/GPIO39 with 12 dB attenuation (full 0-3.3V range). The switch is read on GPIO34 - an input-only pin with no internal pull-up, so add an external ~10k resistor to 3.3V; the pin reads inverted (LOW when pressed).
KY-023 PlatformIO example
Copy[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200#include <Arduino.h>
// Declaration and initialization of the input pins
int JoyStick_X = 36; // X-axis signal (GPIO36 / ADC1_CH0, matches the wiring above)
int JoyStick_Y = 39; // Y-axis signal (GPIO39 / ADC1_CH3)
int Button = 34; // Button (GPIO34 - input only, needs an external pull-up to 3.3V)
void setup() {
pinMode(Button, INPUT); // GPIO34 has no internal pull-up; add an external ~10k resistor
Serial.begin(115200);
}
void loop() {
float x, y;
int buttonState;
// Read current values and convert to voltage (ESP32: 12-bit ADC, 3.3V)
x = analogRead(JoyStick_X) * (3.3 / 4095.0);
y = analogRead(JoyStick_Y) * (3.3 / 4095.0);
buttonState = digitalRead(Button);
// Output the values
Serial.print("X-axis:"); Serial.print(x, 4); Serial.print("V, ");
Serial.print("Y-axis:"); Serial.print(y, 4); Serial.print("V, ");
Serial.print("Button:");
if (buttonState == HIGH) {
Serial.println("not pressed");
} else {
Serial.println("pressed");
}
delay(200);
}This PlatformIO code reads the analog values of the KY-023 joystick's X and Y axes and detects button presses. It prints the readings to the serial monitor every 200ms.
KY-023 MicroPython example
Copyimport machine
import time
JOYSTICK_X = machine.ADC(machine.Pin(36))
JOYSTICK_Y = machine.ADC(machine.Pin(39))
BUTTON = machine.Pin(34, machine.Pin.IN, machine.Pin.PULL_UP)
JOYSTICK_X.atten(machine.ADC.ATTN_11DB)
JOYSTICK_Y.atten(machine.ADC.ATTN_11DB)
while True:
x_value = JOYSTICK_X.read()
y_value = JOYSTICK_Y.read()
button_state = BUTTON.value()
print("X:", x_value, "Y:", y_value, "Button:", "Not Pressed" if button_state else "Pressed")
time.sleep(0.2)This MicroPython script reads the analog values of the KY-023 joystick's X and Y axes using ADC on GPIO36 and GPIO39. It also monitors button presses on GPIO34 and prints the results every 200ms.
KY-023 specifications
About the KY-023
The KY-023 is two independent potentiometers, one per axis, mechanically ganged to a joystick handle, plus a separate tactile push-button under the stick - electrically it’s no different from wiring up the same KY-004 button alongside two ordinary pots. Each axis is a plain voltage divider, so both VRx and VRy need to land on ESP32 ADC1 pins (GPIO32-39); the button is a simple digital input, though note GPIO34 has no internal pull-up since it’s an input-only pin, so it needs an external pull-up resistor if that’s the one chosen for it.
Don’t trust the “centered = exactly half of VCC” assumption from the datasheet too literally - these are cheap ganged potentiometers, and unit-to-unit tolerance plus the ESP32 ADC’s own nonlinearity mean the resting voltage on either axis rarely lands on a clean number. Read and store the center position at startup and compute movement relative to that baseline rather than a hardcoded midpoint; it’s a more reliable way to get consistent joystick behavior across different boards.
KY-023 troubleshooting
Joystick Not Responding
›
Issue: No change in analog readings when moving the joystick.
Solutions:
- Verify all connections are secure and correctly placed.
- Ensure the module is receiving the appropriate voltage (3.3V or 5V).
- Check that the microcontroller's analog input pins are correctly configured.
- Test the joystick with a multimeter to confirm the potentiometers are functioning.
Button Not Registering Presses
›
Issue: The push-button does not trigger any response.
Solutions:
- Ensure the SW pin is connected to a digital input configured with a pull-up resistor.
- Check for continuity in the button circuit using a multimeter.
- Confirm that the microcontroller's digital input pin is correctly configured in the code.
Where to buy the KY-023

Resources
Similar sensors





