Джойстик KY-023 на Arduino: подключение, map() и серво
Модуль KY-023 (он же «аналоговый джойстик», он же лежит в каждом втором стартовом наборе) — это два потенциометра по 10 кОм, скрещённых под общей ручкой, плюс кнопка под большим пальцем. Ручка на пружинке: отпускаете — сама возвращается в центр, вдавливаете — щёлкает кнопка. Стоит копейки и потому живёт в прицеле, робо-руке, меню устройств и везде, где надо выбирать из пары направлений плавно, а не кнопкой.
В симуляторе это компонент палитры «Макетка»: карточка «Аналоговый джойстик» с двумя осями и кнопкой SW, ручка тянется мышкой — analogRead меняется вживую. Схема «джойстик → map() → серво» исполняется рантаймом целиком, проверено прогоном. Ниже — распиновка, скетч с сервой, центр и мёртвая зона без теории про дребезг (она тут не главное).
Распиновка: пять проводов и ни одного лишнего
У модуля пять выводов: GND, +5V, VRx, VRy и SW. Оси — средние ножки тех самых потенциометров: крайние ноги сидят на питании и земле, ручка двигает движок, analogRead видит от 0 до 1023. Центр — примерно 512, но «примерно» тут ключевое слово: у моих двух клонов механический ноль стоял на 505 и на 519, и это норма, а не брак. Кнопка SW замыкает контакт на землю при вдавливании ручки, поэтому пин настраивается INPUT_PULLUP — в покое HIGH, при нажатии LOW, инвертированная логика из урока про кнопку. Внешние резисторы не нужны: подтяжка встроена, потенциометрам хватает питания платы.
- VRx — ось влево-вправо на A0, VRy — вверх-вниз на A1: оба входа аналоговые, 0-1023.
- SW — на D2 с INPUT_PULLUP: 1 в покое, 0 при вдавленной ручке.
- Один замер оси — около 100 мкс; обе оси и кнопка опрашиваются быстрее, чем за треть миллисекунды.
- Какой край оси в какую сторону — зависит от распайки конкретного клона: сверяйте по монитору порта.
- Питание 3,3-5 В: на 3,3 В шкала ужмётся к 0-660, формула перевода не изменится, изменится опорное.
Скетч: джойстик крутит серву
#include <Servo.h> const int KNOPKA = 2; Servo povorot;int ugol = 90; void setup() { povorot.attach(9); pinMode(KNOPKA, INPUT_PULLUP); Serial.begin(9600);} void loop() { int x = analogRead(A0); // 0-1023 с ручки ugol = map(x, 0, 1023, 0, 180); // код → угол серво if (digitalRead(KNOPKA) == LOW) { // ручку вдавили ugol = 90; // кнопка возвращает в центр } povorot.write(ugol); Serial.print("x="); Serial.print(x); Serial.print(" ugol="); Serial.println(ugol); delay(100);}map() — целочисленная функция: map(x, 0, 1023, 0, 180) сжимает диапазон кода в угол, обрезкой за границами не занимается, но analogRead сам не вылезает за 1023. Прогон в симуляторе: карточка джойстика из палитры «Макетка» — ручка до упора вправо даёт x=1023 и ugol=180, до упора влево x=0 и ugol=0, кнопка SW вживую печатает 0 и возвращает серву в 90. Серво и analogRead исполняются рантаймом целиком; тряска самой сервы на живой плате — уже физика железа. Родственная связка — симулятор сервопривода и подключение SG90: про крайние положения, дёрганье и питание сервы.
Центр и мёртвая зона: как убрать дрожь
Голый map() для меню или курсора дёргается: центр каждого экземпляра смещён на 5-15 значений, дешёвые потенциометры шумят на 2-3 единицы, и условие «x больше 512» срабатывает само по себе. Лечение двухслойное: калибровка при старте (двадцать замеров в setup, среднее становится центром вместо бумажных 512) и мёртвая зона — отклонение меньше 40-50 значений от центра считается стоящей ручкой. Для серво с картой 0-180 это уже не критично: угол съезжает на пару градусов, ладно. Для меню и курсора — обязательно. Полный разбор с калибровочным кодом и автоматом направлений — в уроке про джойстик, там же почему центр надо мерить, а не зашивать константой.
| Канал | Пин | Центр | Края |
|---|---|---|---|
| Ось X (VRx) | A0 | около 512 | 0 и 1023 до упора |
| Ось Y (VRy) | A1 | около 512 | те же 0 и 1023 |
| Кнопка SW | D2, INPUT_PULLUP | 1 (HIGH) | 0 (LOW), пока вдавлена |
Грабли KY-023
- Направления осей перевёрнуты у части клонов: не паяйте заново — инвертируйте в скетче map(x, 0, 1023, 180, 0).
- SW без INPUT_PULLUP читает воздух: пин настраивается подтяжкой, а не внешним резистором на 10 кОм.
- map() целочисленный и без constrain: свой диапазон уже 0-1023, но при чужом датчике добавьте constrain.
- Серву питают от 5 В платы только на пробу: рывки сервы просаживают питание и дёргают оси джойстика — берите отдельный БП.