Модуль джойстика: читаем оси X-Y и кнопку с Arduino
Как работает аналоговый модуль джойстика (KY-023)? Пины VRx, VRy и SW, подключение к Arduino, мёртвая зона и калибровка, определение направления, управление сервоприводом, код для ESP32 и Pico.
Модуль джойстика — это вариант стиков с игровых геймпадов, удобный для Arduino. Когда двигаешь ручку влево-вправо и вперёд-назад, он сообщает её положение по двум осям, а если нажать на ручку, срабатывает кнопка внутри. Это самый практичный способ ввода для пультов машинок-роботов, поворотных платформ для камер (pan-tilt), игр на светодиодной матрице и меню на дисплее. Самая распространённая модель известна также как KY-023.

Как работает модуль джойстика?
Под ручкой стоят два потенциометра по 10 kΩ, расположенные под прямым углом друг к другу: один чувствует движение по оси X, другой — по оси Y. Каждый потенциометр работает как делитель напряжения; когда ручка в центре, его движок даёт около 2,5 V, а analogRead() показывает примерно 512. Если отвести ручку до упора в одну сторону, значение приближается к 0, в другую — к 1023. Если нажать на ручку, маленькая кнопка соединяет пин SW с GND.
| Положение ручки | VRx (X) | VRy (Y) |
|---|---|---|
| Центр (отпущена) | ~512 | ~512 |
| До упора влево | ~0 | ~512 |
| До упора вправо | ~1023 | ~512 |
| До упора вверх | ~512 | ~0 |
| До упора вниз | ~512 | ~1023 |
Какое направление даёт 0, а какое 1023, зависит от того, как держать модуль. Если направления не совпадают с таблицей, когда пины смотрят на тебя, не переживай: ось легко перевернуть в коде, вычтя значение из 1023.
Распиновка и подключение джойстика
| Пин | Назначение | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|---|
| GND | Земля | GND | GND | GND |
| +5V | Питание | 5V | 3V3 | 3V3 |
| VRx | Ось X (аналоговый) | A0 | GPIO 34 | GP26 |
| VRy | Ось Y (аналоговый) | A1 | GPIO 35 | GP27 |
| SW | Кнопка (при нажатии замыкается на GND) | D2 | GPIO 25 | GP15 |
Чтение джойстика с Arduino
Код ниже читает обе оси и кнопку и выводит их в монитор порта. Кнопка при нажатии замыкается на GND, а на большинстве модулей нет подтягивающего резистора, поэтому пин SW нужно настроить как INPUT_PULLUP (внутренняя подтяжка, pull-up); тогда он читает HIGH, когда кнопка отпущена, и LOW, когда нажата.
1const int X_PIN = A0;
2const int Y_PIN = A1;
3const int SW_PIN = 2;
4
5void setup() {
6 pinMode(SW_PIN, INPUT_PULLUP); // basınca LOW / LOW when pressed
7 Serial.begin(9600);
8}
9
10void loop() {
11 int x = analogRead(X_PIN); // 0–1023, ortada ~512 / ~512 at center
12 int y = analogRead(Y_PIN);
13 bool pressed = digitalRead(SW_PIN) == LOW;
14
15 Serial.print("X: "); Serial.print(x);
16 Serial.print(" Y: "); Serial.print(y);
17 Serial.print(" Buton / button: ");
18 Serial.println(pressed ? "basılı / pressed" : "-");
19 delay(100);
20}Мёртвая зона и калибровка
В жизни отпущенная ручка показывает не ровно 512, а что-то вроде 495 или 525, и значение дрожит на несколько единиц. Если это не исправить, машинка-робот ползёт вперёд или сервопривод дёргается, хотя к джойстику никто не прикасается. Решение в два шага: при запуске измерить и сохранить центральное значение (калибровка) и игнорировать небольшие отклонения около центра (мёртвая зона). Функция ниже превращает каждую ось в чистое значение от -100 до +100, где центр ровно 0:
1int centerX, centerY;
2const int DEAD_ZONE = 40;
3
4int readAxis(int pin, int center) {
5 int v = analogRead(pin) - center;
6 if (abs(v) < DEAD_ZONE) return 0; // ölü bölge / dead zone
7 if (v > 0) return map(v, DEAD_ZONE, 1023 - center, 0, 100); // sağ ya da aşağı / right or down
8 return map(v, -center, -DEAD_ZONE, -100, 0); // sol ya da yukarı / left or up
9}
10
11void setup() {
12 Serial.begin(9600);
13 centerX = analogRead(A0); // açılışta joystick'e dokunma / don't touch it at startup
14 centerY = analogRead(A1);
15}
16
17void loop() {
18 Serial.print(readAxis(A0, centerX));
19 Serial.print(", ");
20 Serial.println(readAxis(A1, centerY));
21 delay(50);
22}Определение направлений: вверх, вниз, влево, вправо
В играх и меню часто нужно не точное положение, а только направление. Сравнивая значения с двумя порогами, можно превратить джойстик в четырёхпозиционную крестовину. Для навигации по меню нужно, чтобы каждое отклонение считалось одним шагом, поэтому в коде есть короткая задержка, или новые шаги игнорируются, пока ручка не вернётся в центр.
1void loop() {
2 int x = analogRead(A0);
3 int y = analogRead(A1);
4 if (x < 300) Serial.println("Sol / left");
5 else if (x > 700) Serial.println("Sağ / right");
6 else if (y < 300) Serial.println("Yukarı / up");
7 else if (y > 700) Serial.println("Aşağı / down");
8 delay(150);
9}Управление сервоприводом джойстиком
Если отобразить одну ось джойстика на 0–180 градусов, сервоприводом можно управлять вручную; с двумя осями и двумя сервоприводами получится поворотная платформа pan-tilt, которая поворачивает камеру влево-вправо и вверх-вниз. Про подключение сервопривода и питание читай в статье Что такое сервопривод?
1#include <Servo.h>
2
3Servo panServo;
4
5void setup() {
6 panServo.attach(9);
7}
8
9void loop() {
10 int x = analogRead(A0);
11 int angle = map(x, 0, 1023, 0, 180); // joystick → açı / joystick → angle
12 panServo.write(angle);
13 delay(15);
14}Джойстик с ESP32 и Raspberry Pi Pico
АЦП у ESP32 12-битный и даёт значения от 0 до 4095, но из-за его нелинейности центр часто получается около 1800–1950, а не 2048. Поэтому способ калибровки, описанный выше, на ESP32 ещё важнее. Если будешь использовать Wi-Fi, подключай оси к пинам ADC1 (GPIO 32–39). На Pico аналоговые входы — GP26–GP28, а read_u16() возвращает значения от 0 до 65535:
1from machine import ADC, Pin
2import time
3
4x_axis = ADC(26) # GP26 → VRx
5y_axis = ADC(27) # GP27 → VRy
6button = Pin(15, Pin.IN, Pin.PULL_UP) # SW, basınca 0 / 0 when pressed
7
8while True:
9 x = x_axis.read_u16() # 0–65535, ortada ~32768 / ~32768 at center
10 y = y_axis.read_u16()
11 print(x, y, "BASILI / PRESSED" if button.value() == 0 else "")
12 time.sleep(0.1)Идеи проектов с джойстиком
- Пульт для машинки-робота: подключи джойстик к плате-передатчику и отправляй команды машинке через nRF24L01 или Bluetooth.
- Камера pan-tilt: направляй камеру в любую сторону двумя сервоприводами и одним джойстиком.
- Змейка на светодиодной матрице: управляй змейкой на матрице 8x8 джойстиком.
- Меню на дисплее: перемещайся по пунктам вверх и вниз и выбирай кнопкой; про дисплей читай в статье I2C LCD-дисплей.
- Выбор цвета: управляй RGB-светодиодом, задавая цвет осью X, а яркость — осью Y.
- Компьютерная мышь: преврати джойстик в настоящую USB-мышь с Arduino Leonardo или Pro Micro.
Частые ошибки и как их исправить
- Кнопка кажется нажатой, хотя её не трогают: пин SW не настроен как INPUT_PULLUP; пин «висит» в воздухе и читает случайные значения.
- В покое значение не 512 и дрожит: это нормально; откалибруй центр при запуске и добавь мёртвую зону.
- Оси перевёрнуты или перепутаны: дело в ориентации модуля; вычти значение из 1023 или поменяй местами провода VRx и VRy.
- На ESP32 центр не 2048: АЦП ESP32 нелинейный; измерь центр при запуске и считай от него.
- Сервопривод дрожит или Arduino перезагружается: сервопривод перегружает 5 V Arduino; питай его от отдельного источника 5 V и соедини земли.
Джойстик — это, по сути, два потенциометра и кнопка, поэтому статьи Что такое потенциометр? и Как подключить кнопку к Arduino закрепят то, что ты здесь узнаешь. В приложении BlueGrays есть проекты с управлением светодиодами и сервоприводами от джойстика и игра «Змейка» на светодиодной матрице — со схемами подключения и кодом.
Частые вопросы
Для чего нужны пины модуля джойстика?
GND и +5V — пины питания. VRx и VRy выдают положение по осям X и Y в виде аналогового напряжения и подключаются к аналоговым пинам. SW — выход кнопки, который при нажатии на ручку замыкается на GND; он подключается к цифровому пину, настроенному как INPUT_PULLUP.
Почему джойстик в центре показывает не ровно 512?
Потенциометры и механика не идеальны; в покое значение обычно где-то между 490 и 530 и дрожит на несколько единиц. Проблему решают измерение и сохранение центра при запуске и мёртвая зона ±30–50.
Почему пин SW случайно переключается между LOW и HIGH?
На большинстве модулей джойстика нет подтягивающего резистора для кнопки, поэтому пин «висит», как будто ни к чему не подключён. Включи внутреннюю подтяжку Arduino командой pinMode(SW_PIN, INPUT_PULLUP), и ты будешь читать HIGH, когда кнопка отпущена, и LOW, когда нажата.
Сколькими сервоприводами можно управлять одним джойстиком?
У джойстика две оси, поэтому можно независимо управлять двумя сервоприводами — идеально для камеры pan-tilt. Для большего числа сервоприводов добавляют второй джойстик; 6 аналоговых пинов Arduino Uno хватает на три джойстика.
Можно ли использовать джойстик как беспроводной пульт?
Да. Подключи джойстик к одной Arduino и отправляй его значения на другую плату через радиомодуль nRF24L01, Bluetooth-модуль HC-05 или ESP-NOW между двумя ESP32. Так устроено большинство проектов машинок-роботов и радиоуправления.
Работает ли модуль джойстика с ESP32 и Raspberry Pi Pico?
Да, но питать его нужно от пина 3V3. На ESP32 значения идут от 0 до 4095, на Pico — от 0 до 65535. На ESP32 используй пины ADC1 (GPIO 32–39), которые работают при включённом Wi-Fi, и калибруй центр при запуске.
Что собрать после прочтения
Все проекты
Управление светодиодами джойстиком
Наклони рычажок джойстика до упора в любую сторону, и загорится светодиод этого направления: вправо, влево, вверх и вниз — четыре разных цвета. Отпусти рычажок, он вернётся в центр, и все светодиоды п

Управление сервоприводом джойстиком
Управляй двумя моторами джойстиком, как будто поворачиваешь камеру наблюдения. Наклони рычажок влево или вправо, и нижний сервопривод медленно поворачивается, наклони вверх или вниз — верхний сервопри

Пиксельная змейка на матрице 8x8
Собери классическую игру «Змейка» своими руками и сыграй в неё. На цветной светодиодной матрице 8x8 ты джойстиком управляешь зелёной змейкой и ешь красные яблоки; с каждым яблоком змейка растёт, немно

Змейка на матрице 16x16
Классическая игра «Змейка», на этот раз на огромном светодиодном дисплее из 256 огоньков. Ты джойстиком управляешь зелёной змейкой и собираешь красные яблоки; с каждым яблоком змейка растёт, счёт увел

Сервопривод на ESP32 (управление потенциометром)
В этом проекте сервопривод SG90 управляется потенциометром. Угол сервопривода задаётся сопоставлением аналогового значения потенциометра с диапазоном 0–180 градусов. Библиотека ESP32Servo формирует сигналы сервопривода на подходящих выходных пинах GPIO у ESP32; этот проект использует GPIO 18. Это базовый способ управления для роборук, подвесов камер и механизмов автоматических дверей.

Игра в пинг-понг — SSD1306
Мяч, две ракетки и ты: на маленьком OLED-экране ты играешь в настольный теннис — одну из самых первых видеоигр. Две кнопки двигают твою ракетку справа вверх и вниз, а левой ракеткой управляет компьюте

OLED T-Rex Run: игра с динозавром на Arduino
Игра с динозавром, которая появляется, когда пропадает интернет, теперь у тебя на столе: динозавр бежит по маленькому OLED-экрану, навстречу едут кактусы, а прыгаешь ты одной кнопкой. Счёт растёт в ре

Игра-бегалка на LCD с Arduino
Бесконечная игра-бегалка с горизонтальной прокруткой на символьном LCD-экране 16x2. Герой бежит сам, а игрок перепрыгивает препятствия нажатием кнопки. Случайные препятствия появляются в верхней и нижней строке. Пассивный зуммер играет вступительную мелодию, звук прыжка, праздничный звук на 50 очков и мелодию конца игры. При запуске показывается заставка «BlueGrays - Arduino Projects». Счёт обновляется сразу в правом верхнем углу экрана.
Другие статьи
Приложение BlueGrays
Собери эту схему шаг за шагом в телефоне
Схема подключения, код и список деталей на одном экране. 200+ проектов на Arduino, ESP32 и Pico готовы к сборке.
Наведи камеру телефона
Наведи камеру на код — откроется магазин приложений на телефоне.
или открыть магазин

