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.

KY-023 Dual Axis Joystick Module image
KY-023 · Analog
Analog
Interface
5pins
Connections
3.3V - 5 V
Supply
$2
Typical price
On this page

KY-023 pinout

5 pins · Analog

The KY-023 is a 5-pin dual-axis joystick module with integrated push button:

View:
KY-023 Dual Axis Joystick Module pinout
PinTypeDescriptionNotes
GNDPowerGround connection
+5VPowerPower supply3.3V or 5V (check compatibility)
VRxCommunicationX-axis analog outputVoltage varies with X position
VRyCommunicationY-axis analog outputVoltage varies with Y position
SWControlSwitch/button signalActive 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

5 connections · 1 optional

To interface the KY-023 with an ESP32 for joystick input:

KY-023 Dual Axis Joystick Module wiring with ESP32
KY-023 pinESP32 pinPurpose
GNDGNDGround
+5V3.3VPower supply (use 3.3V for ESP32)
VRxGPIO36X-axis analog input (ADC pin)
VRyGPIO39Y-axis analog input (ADC pin)
SWGPIO34Button 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

5 platforms
Platform:

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

Copy
sensor:
  - 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
src/main.cppCopy
#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

Copy
import 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

From the datasheet
Operating Voltage
3.3V - 5V
Output Voltage Range
0V to VCC
Dimensions
34 x 39 x 26 mm
Weight
12 g

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

2 common issues

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

KY-023 Dual Axis Joystick Module
KY-023 Dual Axis Joystick Module
$2per unit, typical
Amazon and AliExpress links are affiliate links - buying through them supports the site at no extra cost to you.

Resources