Курс Arduino с нуля · Датчики

Урок 14. Потенциометр: плавная регулировка крутилкой

У кнопки два состояния, у крутилки — тысяча. Знакомимся с аналоговыми входами, делителем напряжения, командой analogRead и функцией map.

Урок 14 из 31⏱ примерно 50 минутОбновлено: 26 июля 2026

Что понадобится

  • Плата Uno из набора
  • Кабель USB
  • Макетная плата
  • Потенциометр 10 кОм
  • Светодиод (любого цвета)
  • Резистор 470 Ом
  • 5 проводов «папа-папа»

Все детали есть в обучающем наборе, по которому идёт курс.

Весь прошлый блок плата слушала кнопки, а у кнопки всего два состояния: нажата или нет. Мир так не устроен. Громкость, яркость, температура, скорость — всё это меняется плавно. Пора научить плату различать не два значения, а тысячу.

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

Цифровое и аналоговое

До сих пор мы работали с цифровыми сигналами: есть напряжение или нет, HIGH или LOW, единица или ноль. Такой сигнал читает digitalRead.

Но у платы есть и другие выводы — аналоговые входы. Они подписаны A0…A5 и живут на нижней гребёнке, отдельной группой. Эти выводы умеют не просто отвечать «есть или нет», а измерять напряжение и сообщать его числом. Команда для этого — analogRead.

Важное ограничение. Аналоговые выводы умеют только читать. Выдать плавно меняющееся напряжение плата не может — вместо этого она притворяется с помощью ШИМ, с которым мы познакомились в восьмом уроке. Отсюда несимметричность: analogRead берёт значения 0…1023, а analogWrite принимает 0…255.

Знакомимся с потенциометром

Потенциометр из набора — синий квадратик с валом под отвёртку и тремя ножками. На корпусе написано его полное сопротивление: 10 кОм.

Внешний видтри ножки в ряд,вал крутится на 270°Что внутрирезистивная дорожкакрайсреднийкрайползунок скользит по дорожкеОбозначениеRрезистор со стрелкой —она и есть ползунок
Внутри — дугообразная резистивная дорожка и ползунок, который по ней скользит.

Устройство простое до гениальности. Между двумя крайними выводами лежит дорожка с сопротивлением 10 кОм — она никогда не меняется. А средний вывод соединён с ползунком, который ездит по этой дорожке, когда вы крутите вал. Значит, меняется не общее сопротивление, а то, в какой точке дорожки мы подключились.

Как крутилка делает напряжение

Подадим на крайние выводы 5 вольт и землю. Дорожка окажется под напряжением целиком, и в каждой её точке будет своё значение — от нуля у земли до пяти вольт у плюса. Ползунок просто «снимает» напряжение в той точке, где стоит:

Ручка влево5 ВA0на среднем выводе 0,5 ВanalogRead даёт ≈ 102Ручка посередине5 ВA0на среднем выводе 2,5 ВanalogRead даёт ≈ 512Ручка вправо5 ВA0на среднем выводе 4,5 ВanalogRead даёт ≈ 921Сопротивление дорожки не меняется — меняется лишь то, в какой её точке мы измеряем напряжение.
Крутите ручку — и на среднем выводе плавно меняется напряжение от 0 до 5 вольт.

Такая схема называется делителем напряжения, и она встречается всюду. Запомните её: в следующем уроке ровно так же будет работать фоторезистор, только двигать «ползунок» будет свет, а не ваши пальцы.

А если перепутать крайние выводы? Ничего страшного: устройство просто начнёт работать наоборот — по часовой стрелке значение будет уменьшаться, а не расти. Поменять их местами можно в любой момент. А вот подключать 5 вольт и землю к среднему выводу не нужно.

Собираем схему

Отключите USB. Обратите внимание: провод от крутилки идёт не на цифровой вывод, а на A0 — он находится на нижней гребёнке платы, в группе с подписью ANALOG IN.

++abcdefghij11020456121517123451Потенциометр: три ножки в соседние столбики 4, 5 и 6 (ряд b), корпус висит над макеткой.2Крайние ножки: столбик 4 — в шину «+», столбик 6 — в шину «−».3Жёлтый провод: средняя ножка (столбик 5) → вход A0 платы.4Светодиод: вывод 10 → резистор 470 Ом → длинная ножка; короткая — в шину «−».5Питание макетки: 5V платы → шина «+», GND → шина «−».
Крайние ножки потенциометра — на шины питания, средняя — на вход A0. Светодиод остаётся на выводе 10.
UnoA0D10GNDR110 кОм5 ВVD1R2 470 ОмКрайние выводы — на 5 вольт и на землю, средний — на аналоговый вход A0.
На чертеже потенциометр рисуют как резистор со стрелкой: стрелка — это и есть ползунок.

Проверьте перед включением: три ножки крутилки стоят в три соседних столбика; крайние соединены с шинами «+» и «−»; средняя идёт на A0; шины запитаны с платы. Светодиод — как в прошлых уроках, на выводе 10 через резистор 470 Ом.

Шаг 1. Смотрим, что выдаёт вход

Первым делом — не программировать, а посмотреть. Мы уже умеем: пусть плата печатает то, что видит.

// Аналоговые входы называются A0…A5.
const int POT = A0;

void setup() {
  Serial.begin(9600);
  Serial.println("Крутите ручку:");
  // Аналоговый вход настраивать не нужно — pinMode ему не требуется.
}

void loop() {
  int value = analogRead(POT);
  Serial.println(value);
  delay(100);
}

Откройте монитор порта и покрутите ручку. Числа поедут от 0 до 1023 и обратно. Это тот самый приём из двенадцатого урока: с новым датчиком всегда начинайте с печати, и только потом придумывайте, что с его показаниями делать.

А теперь переключитесь на вкладку «График» — и крутите ручку снова. Числа превратятся в живую кривую, которая слушается вашей руки. Так работать с датчиками нагляднее всего.

Почему у аналогового входа нет pinMode? Потому что он умеет только читать — настраивать нечего. Строку pinMode(A0, INPUT) писать не запрещено, но она ничего не меняет. Это одна из немногих поблажек, которые даёт плата.

От нуля до 1023: откуда это число

Плата измеряет напряжение не бесконечно точно, а ступеньками. Ступенек ровно 1024 — от 0 до 1023. Ноль означает «на выводе 0 вольт», 1023 — «на выводе 5 вольт».

Что происходит с сигналомнапряжение0 В2,5 В5 ВanalogRead — измеряет и превращает в числоanalogRead05121023map — растягивает или сжимает в нужный диапазонanalogWrite0128255
Напряжение превращается в число, а map перекладывает это число в любой нужный диапазон.

Легко посчитать цену одной ступеньки: 5 вольт разделить на 1024 — получается около 5 милливольт. Изменения мельче этого плата просто не заметит. Для наших задач точность отличная: человеческий глаз различает куда меньше оттенков яркости.

Шаг 2. Переводим в вольты

Число 512 мало о чём говорит. Переведём его в вольты — так понятнее, что происходит на самом деле:

const int POT = A0;

void setup() {
  Serial.begin(9600);
}

void loop() {
  int value = analogRead(POT);

  // 1023 ступени растянуты ровно на 5 вольт.
  // float — тип для чисел с дробной частью.
  float volts = value * 5.0 / 1023;

  Serial.print(value);
  Serial.print("   это ");
  Serial.print(volts);
  Serial.println(" В");

  delay(200);
}

Здесь новый тип переменной — float. В нём хранятся числа с дробной частью: 2.47, 0.09, 4.98. Все прежние типы (int, long) умели только целые: 5 разделить на 2 давало 2, а не 2.5.

Почему 5.0, а не просто 5? Если написать value * 5 / 1023, плата посчитает всё в целых числах и почти всегда получит ноль или единицу. Точка в записи 5.0 — команда «считай с дробями». Забыть её — классическая ошибка, из-за которой формула выдаёт бессмыслицу.

Шаг 3. Крутилка управляет яркостью

Теперь соединим вход с выходом. Задача простая на словах и хитрая в деталях: у нас на входе числа 0…1023, а analogWrite принимает 0…255. Диапазоны разные.

const int POT = A0;
const int LED = 10;

void setup() {
  pinMode(LED, OUTPUT);
}

void loop() {
  int value = analogRead(POT);            // 0…1023 — что даёт крутилка
  int bright = map(value, 0, 1023, 0, 255); // 0…255 — что нужно ШИМ

  analogWrite(LED, bright);
}

Всю работу делает map. Читается она так: «возьми value, который живёт в диапазоне от 0 до 1023, и найди для него такое же место в диапазоне от 0 до 255». Середина останется серединой, край — краем.

Считать пропорцию вручную тоже можно: value / 4 даст почти то же самое (1023 ÷ 4 = 255). Но map удобнее: диапазоны видно прямо в строке, и их легко поменять — например, ограничить яркость снизу, написав map(value, 0, 1023, 60, 255), чтобы светодиод никогда не гас совсем.

map умеет переворачивать. Поменяйте местами последние два числа: map(value, 0, 1023, 255, 0) — и крутилка станет работать наоборот. Этот приём пригодится уже в следующем уроке: там нам понадобится «чем темнее, тем ярче».

Шаг 4. Крутилка управляет скоростью

Яркость — не единственное, чем можно управлять. Пусть ручка задаёт скорость мигания. И обойдёмся без delay: иначе плата будет подолгу не смотреть на крутилку и начнёт отзываться с опозданием.

const int POT = A0;
const int LED = 10;

bool ledOn = false;
unsigned long lastBlink = 0;

void setup() {
  pinMode(LED, OUTPUT);
}

void loop() {
  // Ручка задаёт паузу: от 40 мс (быстро) до 1000 мс (медленно).
  unsigned long pause = map(analogRead(POT), 0, 1023, 40, 1000);

  if (millis() - lastBlink >= pause) {
    ledOn = !ledOn;
    digitalWrite(LED, ledOn);
    lastBlink = millis();
  }
}

Обратите внимание на диапазон в map: от 40 до 1000 миллисекунд. Мы сразу переводим показания крутилки в те единицы, в которых думает программа. Это хорошая привычка — не таскать по программе «сырые» показания датчика, а превращать их в осмысленную величину как можно раньше.

Шаг 5. Последняя цифра дрожит

Присмотритесь к монитору: даже если ручку не трогать, значение слегка гуляет — 512, 511, 513, 512. Это не поломка, а обычный электрический шум, который ловит любой измеритель. Обычно он не мешает, но, если по показаниям принимать решения, лучше его сгладить:

const int POT = A0;

void setup() {
  Serial.begin(9600);
}

void loop() {
  // Складываем десять измерений и делим на десять.
  long sum = 0;
  for (int i = 0; i < 10; i++) {
    sum = sum + analogRead(POT);
    delay(2);
  }
  int average = sum / 10;

  Serial.println(average);
  delay(100);
}

Приём называется усреднением: делаем десять измерений подряд и берём среднее. Случайные отклонения гасят друг друга, и число перестаёт дёргаться. Плата тратит на это 20 миллисекунд — незаметно.

Зачем long для суммы? Десять значений по 1023 дают до 10 230, а в int помещается 32 767 — казалось бы, влезает. Но стоит взять сто измерений, и сумма перевалит за предел. С long об этом можно не думать: привыкайте выбирать тип с запасом.

Не работает? Разбираемся

  • Значение всегда 0 или всегда 1023. Скорее всего, средний вывод не подключён к A0 или провод сидит в соседнем столбике. Проверьте, что жёлтый провод идёт именно от средней ножки.
  • Значение прыгает как попало, ручка ни на что не влияет. Крайние выводы не подключены к питанию: без 5 вольт и земли дорожка ни под каким напряжением, и вход снова «висит в воздухе».
  • Ручка работает только в одном крайнем положении. Проверьте, не перепутаны ли средний и крайний выводы: средний — обязательно тот, что посередине.
  • Крутится наоборот. Не ошибка: поменяйте местами провода на крайних выводах — или два последних числа в map.
  • Светодиод не гаснет до конца. Так и есть: в крайнем положении analogRead может давать не 0, а 3–5. Добавьте порог: if (bright < 5) bright = 0;
  • Плата не прошивается. Убедитесь, что на A0 не подан плюс напрямую в обход дорожки, а также что вы не заняли выводы 0 и 1.

Задания для тренировки

  • РазминкаСделайте так, чтобы светодиод разгорался при повороте ручки против часовой стрелки. Правьте только программу, провода не трогайте.
  • ПосложнееПусть плата печатает не число, а слово: «темно», «средне», «ярко» — в зависимости от положения ручки. И печатает только тогда, когда слово изменилось, а не двадцать раз в секунду.
  • Со звёздочкой ⭐Соберите шкалу уровня: три светодиода из прошлого урока загораются один за другим по мере поворота ручки — как индикатор громкости. Разбор ниже.
Показать разбор «шкала уровня»

Хитрость в том, чтобы не писать три отдельных условия. Переведём показания крутилки в количество горящих светодиодов — число от 0 до 3, — а дальше пусть цикл сам решит, кому гореть.

const int POT = A0;
const int LEDS[3] = {11, 10, 9};

void setup() {
  for (int i = 0; i < 3; i++) {
    pinMode(LEDS[i], OUTPUT);
  }
}

void loop() {
  int value = analogRead(POT);

  // Сколько светодиодов должно гореть: от 0 до 3.
  int level = map(value, 0, 1023, 0, 3);

  for (int i = 0; i < 3; i++) {
    // Горят все, чей номер меньше уровня.
    digitalWrite(LEDS[i], i < level);
  }
}

Ключевая строка — digitalWrite(LEDS[i], i < level). Сравнение i < level даёт «да» для всех светодиодов, чей номер меньше уровня, и «нет» для остальных. При level = 2 загорятся номера 0 и 1, а номер 2 погаснет.

Попробуйте увеличить число светодиодов до пяти — в программе поменяются только массив и верхняя граница map. Именно ради этого мы и городили массивы в прошлом уроке.

Что мы узнали

  • Аналоговые входы A0…A5 измеряют напряжение и выдают число от 0 до 1023. Команда — analogRead, pinMode ей не нужен.
  • Потенциометр — это делитель напряжения: дорожка под напряжением, ползунок снимает значение в своей точке.
  • map(значение, откуда1, откуда2, куда1, куда2) перекладывает число из одного диапазона в другой и умеет переворачивать шкалу.
  • float хранит дробные числа; в формулах пишите 5.0, иначе плата посчитает в целых.
  • Показания датчика всегда немного шумят. Простое лекарство — усреднить несколько измерений.

Мы научились читать плавный сигнал — и это ключ ко всему блоку. Ведь если вместо ползунка поставить деталь, сопротивление которой меняется само, получится настоящий датчик. В следующем уроке такой деталью станет фоторезистор, и наш светильник начнёт включаться в темноте без единого прикосновения.

Частые вопросы

Чем аналоговый вход отличается от цифрового?

Цифровой вывод отвечает только «есть напряжение или нет». Аналоговый вход измеряет напряжение и выдаёт число от 0 до 1023, где 0 соответствует нулю вольт, а 1023 — пяти. На Arduino Uno это выводы A0–A5.

Почему analogRead выдаёт 0–1023, а не 0–1000?

Плата измеряет напряжение ступеньками, и ступенек ровно 1024 — от 0 до 1023. Такое число получается потому, что результат укладывается в десять двоичных разрядов. Одна ступенька — примерно 5 милливольт.

Нужен ли pinMode для аналогового входа?

Нет. Аналоговый вход умеет только читать, настраивать нечего. Строку pinMode для него писать не запрещено, но она ничего не изменит.

Что делает функция map?

Перекладывает число из одного диапазона в другой, сохраняя пропорцию. Например, map(value, 0, 1023, 0, 255) превращает показания аналогового входа в значение яркости для analogWrite. Если поменять местами последние два числа, шкала перевернётся.

← Все уроки курса