Основы работы с потенциометром и Arduino
Разбираемся, как устроен потенциометр, в каких форматах он встречается и как подключить его к Arduino для управления цветом светодиодов.
Потенциометр — это простое механическое устройство, которое существует во множестве форм. Его суть — переменное сопротивление, которое меняется при физическом воздействии. В примерах этой статьи используется потенциометр с поворотной ручкой — один из самых распространённых вариантов.
Подавая напряжение через потенциометр на аналоговый вход Arduino, можно считывать текущее положение ручки как аналоговое значение. Статья покажет два практических сценария: потенциометр как вход для смешивания цветов RGB, а также плавное перекрёстное затухание между цветами.
Типичный потенциометр имеет 3 вывода: два для питания (+5V и GND) и один сигнальный, который подключается к аналоговому входу Arduino.
Чтобы узнать, как считывать данные с потенциометра и выводить их в монитор Serial, посетите Analog Read Serial example.
Необходимые компоненты
- Arduino board
- Потенциометр
- 1x красный, 1x зелёный, 1x синий светодиод (LED)
- 3x резистора 220 Ом
Схема подключения
- Плюсовой вывод потенциометра → 5V
- Минусовой вывод потенциометра → GND
- Сигнальный вывод потенциометра → A3
- Красный LED → пин 9 через резистор 220 Ом
- Зелёный LED → пин 10 через резистор 220 Ом
- Синий LED → пин 11 через резистор 220 Ом

Подсказка по схеме. Обратите внимание на полярность светодиодов: длинная ножка (анод) идёт к пину Arduino через резистор, короткая (катод) — к GND. Если светодиод не светится, скорее всего, он вставлен наоборот.
Как это работает
Потенциометр делит входное напряжение (5V) пропорционально положению ручки. Когда ручка повёрнута до упора в одну сторону — на сигнальном выводе почти 0V, в другую — почти 5V. Функция analogRead() преобразует это напряжение в целое число от 0 до 1023. Затем функция map() или простая математика позволяет перевести это значение в нужный диапазон — например, 0–255 для управления яркостью через analogWrite().
Пример: смеситель цветов
Этот пример показывает, как потенциометр используется в качестве аналогового входа для тонкого управления смешиванием цветов RGB.
/*
* Код для управления тремя светодиодами (красным, зелёным и синим) или одним RGB-светодиодом с помощью одного потенциометра.
* Программа плавно переходит от красного к зелёному, от зелёного к синему и от синего к красному.
* Clay Shirky <clay.shirky@nyu.edu>
*/
// ВХОД: потенциометр подключить к 5V и GND
int potPin = A3; // Выход потенциометра подключён к аналоговому пину 3
int potVal = 0; // Переменная для хранения значения с потенциометра
// ВЫХОД: используются цифровые пины 9–11 (PWM)
// Катоды светодиодов подключить к цифровому GND
int redPin = 9; // Красный светодиод подключён к цифровому пину 9
int grnPin = 10; // Зелёный светодиод подключён к цифровому пину 10
int bluPin = 11; // Синий светодиод подключён к цифровому пину 11
// Переменные программы
int redVal = 0; // Переменные для хранения значений, передаваемых на пины
int grnVal = 0;
int bluVal = 0;
void setup()
{
pinMode(redPin, OUTPUT); // Настройка пинов как выходов
pinMode(grnPin, OUTPUT);
pinMode(bluPin, OUTPUT);
}
// Основная программа
void loop()
{
potVal = analogRead(potPin); // Считать значение потенциометра с входного пина
if (potVal < 341) // Нижняя треть диапазона потенциометра (0–340)
{
potVal = (potVal * 3) / 4; // Нормализация до 0–255
redVal = 256 - potVal; // Красный: от максимума до нуля
grnVal = potVal; // Зелёный: от нуля до максимума
bluVal = 1; // Синий: выключен
}
else if (potVal < 682) // Средняя треть диапазона потенциометра (341–681)
{
potVal = ( (potVal-341) * 3) / 4; // Нормализация до 0–255
redVal = 1; // Красный: выключен
grnVal = 256 - potVal; // Зелёный: от максимума до нуля
bluVal = potVal; // Синий: от нуля до максимума
}
else // Верхняя треть диапазона потенциометра (682–1023)
{
potVal = ( (potVal-683) * 3) / 4; // Нормализация до 0–255
redVal = potVal; // Красный: от нуля до максимума
grnVal = 1; // Зелёный: выключен
bluVal = 256 - potVal; // Синий: от максимума до нуля
}
analogWrite(redPin, redVal); // Записать значения на пины светодиодов
analogWrite(grnPin, grnVal);
analogWrite(bluPin, bluVal);
}
Пример: плавное перекрёстное затухание цветов
/*
* Код для плавного перехода между тремя цветами: красным, зелёным и синим (RGB).
* Для создания переходов нужно сделать две вещи:
* 1. Описать цвета, которые должны отображаться.
* 2. Задать порядок, в котором они сменяют друг друга.
*
* ОПИСАНИЕ ЦВЕТА:
* Цвет — это массив из трёх значений в процентах (0–100),
* управляющих красным, зелёным и синим светодиодами.
*
* Красный — красный светодиод на максимуме, синий и зелёный выключены:
* int red = { 100, 0, 0 }
* Тусклый белый — все три светодиода на 30%:
* int dimWhite = {30, 30, 30}
* и т.д.
*
* Ниже приведены распространённые цвета, можно добавить свои.
*
* ПОРЯДОК СМЕНЫ ЦВЕТОВ:
* В основной части программы нужно указать порядок смены цветов, например:
* crossFade(red);
* crossFade(green);
* crossFade(blue);
*
* Цвета будут сменяться в указанном порядке с плавным переходом
* от одного к другому.
*
* Дополнительно можно настроить 5 параметров:
* 1. Начальный цвет по умолчанию — чёрный (первый цвет плавно появляется),
* но можно задать любой другой начальный цвет.
* 2. Внутренний цикл выполняется 1020 итераций; переменная 'wait'
* задаёт примерную длительность одного перехода. Теоретически
* 'wait' = 10 мс даёт переход ~10 секунд. На практике
* из-за других операций это занимает ~11 секунд на моей плате. YMMV.
* 3. Если 'repeat' = 0, программа работает бесконечно.
* Если задано число, цикл повторится указанное количество раз,
* затем остановится на последнем цвете. (Установите 'return' = 1
* и сделайте последний цвет чёрным, если нужно плавное угасание в конце.)
* 4. Есть необязательная переменная 'hold' — пауза в миллисекундах
* после завершения цвета, перед началом следующего перехода.
* 5. Установите флаг DEBUG = 1, чтобы выводить отладочную информацию
* в монитор порта.
*
* Внутренняя логика программы несложная, но требует аккуратности —
* подробное объяснение приведено ниже основного цикла.
*
* Апрель 2007, Clay Shirky <clay.shirky@nyu.edu>
*/
// Выход
int redPin = 9; // Красный светодиод подключён к цифровому пину 9
int grnPin = 10; // Зелёный светодиод подключён к цифровому пину 10
int bluPin = 11; // Синий светодиод подключён к цифровому пину 11
// Массивы цветов
int black[3] = { 0, 0, 0 };
int white[3] = { 100, 100, 100 };
int red[3] = { 100, 0, 0 };
int green[3] = { 0, 100, 0 };
int blue[3] = { 0, 0, 100 };
int yellow[3] = { 40, 95, 0 };
int dimWhite[3] = { 30, 30, 30 };
// и т.д.
// Задать начальный цвет
int redVal = black[0];
int grnVal = black[1];
int bluVal = black[2];
int wait = 10; // Внутренняя задержка crossFade 10 мс; увеличьте для более медленных переходов
int hold = 0; // Необязательная пауза после завершения цвета, перед следующим crossFade
int DEBUG = 1; // Счётчик DEBUG; если = 1, значения передаются через serial
int loopCount = 60; // Как часто выводить отладочную информацию?
int repeat = 3; // Сколько раз повторять цикл перед остановкой? (0 — без остановки)
int j = 0; // Счётчик повторений
// Инициализация переменных цвета
int prevR = redVal;
int prevG = grnVal;
int prevB = bluVal;
// Настройка выходов для светодиодов
void setup()
{
pinMode(redPin, OUTPUT); // Настройка пинов как выходов
pinMode(grnPin, OUTPUT);
pinMode(bluPin, OUTPUT);
if (DEBUG) { // Если нужен вывод отладочных значений...
Serial.begin(9600); // ...инициализировать serial
}
}
// Основная программа: задать порядок переходов crossFade
void loop()
{
crossFade(red);
crossFade(green);
crossFade(blue);
crossFade(yellow);
if (repeat) { // Нужно ли ограничить число повторений?
j += 1;
if (j >= repeat) { // Достигли нужного числа?
exit(j); // Если да — остановиться.
}
}
}
/* НИЖЕ — МАТЕМАТИКА. ДЛЯ БАЗОВОГО ИСПОЛЬЗОВАНИЯ МЕНЯТЬ НЕ НУЖНО.
*
* Принцип работы программы:
* Представьте переход, в котором красный LED меняется с 0 до 10,
* зелёный — с 0 до 5, а синий — с 10 до 7, за десять шагов.
* Нужно отсчитать 10 шагов и равномерно увеличивать или
* уменьшать значения цветов.
* Пусть + означает увеличение на 1, а - — уменьшение.
* Тогда 10-шаговый переход выглядит так:
*
* 1 2 3 4 5 6 7 8 9 10
* R + + + + + + + + + +
* G + + + + +
* B - - -
*
* Красный растёт с 0 до 10 за десять шагов, зелёный —
* с 0 до 5 за пять шагов, синий падает с 10 до 7 за три шага.
*
* В реальной программе проценты цвета переводятся в значения 0–255,
* а шагов всего 1020 (255*4).
*
* Чтобы определить размер шага между изменениями значения LED,
* вызывается calculateStep() — она вычисляет абсолютную разницу
* между начальным и конечным значениями, затем делит её на 1020.
*/
int calculateStep(int prevValue, int endValue) {
int step = endValue - prevValue; // Какова общая разница?
if (step) { // Если не ноль,
step = 1020/step; // делим на 1020
}
return step;
}
/* Следующая функция — calculateVal. Когда счётчик цикла i
* достигает размера шага для одного из цветов,
* она увеличивает или уменьшает значение этого цвета на 1.
* (R, G и B вычисляются независимо.)
*/
int calculateVal(int step, int val, int i) {
if ((step) && i % step == 0) { // Если шаг ненулевой и пришло время изменить значение,
if (step > 0) { // увеличить значение, если шаг положительный...
val += 1;
}
else if (step < 0) { // ...или уменьшить, если шаг отрицательный
val -= 1;
}
}
// Защита: убедиться, что val остаётся в диапазоне 0–255
if (val > 255) {
val = 255;
}
else if (val < 0) {
val = 0;
}
return val;
}
/* crossFade() переводит процентные значения цветов в диапазон 0–255,
* затем выполняет 1020 итераций, проверяя необходимость обновления
* значений и записывая их на соответствующие пины.
*/
void crossFade(int color[3]) {
// Перевести в диапазон 0–255
int R = (color[0] * 255) / 100;
int G = (color[1] * 255) / 100;
int B = (color[2] * 255) / 100;
int stepR = calculateStep(prevR, R);
int stepG = calculateStep(prevG, G);
int stepB = calculateStep(prevB, B);
for (int i = 0; i <= 1020; i++) {
redVal = calculateVal(stepR, redVal, i);
grnVal = calculateVal(stepG, grnVal, i);
bluVal = calculateVal(stepB, bluVal, i);
analogWrite(redPin, redVal); // Записать текущие значения на пины светодиодов
analogWrite(grnPin, grnVal);
analogWrite(bluPin, bluVal);
delay(wait); // Пауза 'wait' миллисекунд перед следующей итерацией
if (DEBUG) { // Если нужен вывод в serial — печатать в начале
if (i == 0 or i % loopCount == 0) { // и каждые loopCount итераций
Serial.print("Loop/RGB: #");
Serial.print(i);
Serial.print(" | ");
Serial.print(redVal);
Serial.print(" / ");
Serial.print(grnVal);
Serial.print(" / ");
Serial.println(bluVal);
}
DEBUG += 1;
}
}
// Обновить текущие значения для следующей итерации
prevR = redVal;
prevG = grnVal;
prevB = bluVal;
delay(hold); // Необязательная пауза 'wait' миллисекунд перед продолжением цикла
}
Практические замечания
- Дребезг значений. Аналоговый вход может немного «дрожать» даже при неподвижной ручке. Если это мешает, усредняйте несколько последовательных считываний или используйте небольшой
delay(). - Диапазон
analogRead. Функция возвращает значения 0–1023 (10-битный АЦП). Для управления PWM-выходом нужен диапазон 0–255, поэтому используйтеmap(value, 0, 1023, 0, 255). - Три потенциометра вместо одного. Если хочется управлять каждым цветовым каналом независимо, подключите три потенциометра к трём разным аналоговым входам.
- Выбор резисторов. Резисторы 220 Ом — разумный минимум для защиты светодиодов от тока. Если светодиоды светят слишком ярко или слишком тускло, подберите значение под конкретную модель.
Типичные ошибки
- Неверный аналоговый пин. Убедитесь, что сигнальный провод потенциометра идёт именно на A3, как указано в схеме — иначе
analogRead(A3)будет возвращать случайные значения. - Перепутаны питание и GND. Потенциометр симметричен внешне, но если поменять местами +5V и GND, диапазон значений просто инвертируется. Это не сломает устройство, но логика программы будет работать наоборот.
- Светодиод без резистора. Подключение светодиода напрямую к пину Arduino без резистора почти наверняка выведет из строя либо светодиод, либо пин платы.