Ночник в детской должен гаснуть плавно, а не выключаться со щелчком — иначе ребёнок вскакивает. Я купил потенциометр, собрал диммер за вечер и с гордостью крутил ручку. Свет вёл себя как выключатель: половина хода — тишина, потом раз — полный накал.
Я грешил на ручку и даже вернул её в магазин за новой. Вторая показала то же самое. Виноват был не потенциометр, а пин: я подавал яркость на 13, у которого нет ШИМ. analogWrite на таком пине сворачивается в «вкл или выкл» — и половина хода действительно ничего не делала.
Один значок тильды на корпусе платы решал вопрос. В этом уроке разберём аналоговые входы: как плата видит напряжение между 0 и 5 вольтами, что такое 0–1023, зачем нужна функция map и почему один конец шкалы не равен другому. Заодно прочитаем фоторезистор и наведём порядок в шумных показаниях.
Цифра против аналога: что читает АЦП
Цифровой вход различает два состояния: есть 5 вольт или нет. Ручка потенциометра даёт всё промежуточное: 2,6 В, 1,3 В, 0,4 В. За перевод непрерывного напряжения в число отвечает АЦП — аналого-цифровой преобразователь. Функция analogRead(A0) возвращает int: сколько из 1023 ступеней набрало напряжение на входе.
У Uno АЦП 10-битный: 2 в степени 10 — 1024 ступени, от 0 (0 В) до 1023 (5 В). Число с условиями: на ESP32 АЦП 12-битный — 0–4095, на платформе Due — тоже 12 бит, а аналоговых входов у Mega шестнадцать против шести на Uno. Скетч с analogRead переносится на другие платы без изменений кода, но диапазоны придётся пересчитывать — к этому вернёмся в разговоре про map.
Скорость чтения — около 100 микросекунд на измерение, порядка десяти тысяч в секунду. Для ручки, фоторезистора или термистора запас огромный; для звука этого уже мало, но там применяются совсем другие приёмы, и это тема отдельного разговора.
Входы A0–A5 не требуют pinMode: analogRead сам настраивает всё, что нужно. Строку pinMode(A0, INPUT) компилятор простит, но пользы от неё нет — я её просто не пишу.
void setup() { Serial.begin(9600);} void loop() { int raw = analogRead(A0); // 0..1023 Serial.println(raw); delay(200);}Потенциометр: ручка, которая делит напряжение
Внутри потенциометра — дорожка из резистивного материала и ползунок, который ездит по ней, когда вы крутите вал. Три ноги: крайние идут к 5V и GND, средняя — к A0. Ползунок снимает напряжение с середины дорожки: на одном краю хода 0 В, на другом 5 В — и analogRead видит всю шкалу от 0 до 1023.
Если перепутать крайние ноги местами, ничего не сгорит: шкала просто перевернётся, и 0 окажется там, где был 1023. Схема прощает эту ошибку, поэтому потенциометр — идеальный первый аналоговый датчик: в пределах 5 вольт его нельзя убить неправильным подключением.
Число с условием: у обычных потенциометров полный ход — около трёх четвертей оборота; у многооборотных — десять полных оборотов, они нужны для точных настроек. Для диммера хватает первого варианта: никто не захочет крутить ручку десять оборотов, чтобы выключить ночник.
map(): из 0–1023 в 0–255 и в любые другие шкалы
analogWrite ждёт значение 0–255 — это 8-битный ШИМ, а analogRead даёт 0–1023. Функция map(raw, 0, 1023, 0, 255) переводит пропорционально: 0 станет 0, 1023 — 255, 512 — примерно 127. Смысл не в этих двух шкалах, а в приёме: map умеет переводить любые шкалы — освещённость в громкость, поворот ручки в угол серводвигателя, температуру в цвет индикатора.
У map две особенности, которые надо знать заранее. Первая: она работает с целыми числами, дробная часть отбрасывается — map(512, 0, 1023, 0, 255) даст 127, а не 127.5. Вторая: map ничего не ограничивает — если raw вылезет за 1023 из-за шума или нештатной схемы, результат уедет дальше 255. Лечится обёрткой в constrain: bright = constrain(map(raw, 0, 1023, 0, 255), 0, 255);
Мнение с оговоркой: map предполагает, что шкала линейная. Фоторезистор линейным не будет — его сопротивление падает не по прямой, и map даст приблизительность. Для игрушек и умного дома этого хватает; я тяну калибровочную таблицу только тогда, когда прибор должен именно измерять, а не просто реагировать.
Фоторезистор: ползунок, который крутит солнце
LDR, он же фоторезистор, меняет сопротивление от освещённости: под лампой — единицы килоом, в темноте — до мегаома. Само по себе сопротивление плата не видит, поэтому собирают делитель напряжения: 5V — фоторезистор — точка к A0 — резистор 10 кОм — GND. Свет меняет сопротивление верхней части делителя — напряжение в точке A0 плывёт — analogRead это замечает.
Мои значения при ярком свете — 50–150, ночью — 950 и выше. У вас цифры будут другими: они зависят от номинала второго резистора и от того, куда смотрит датчик. Порог «темноты» не берите из статьи — выведите свои значения в Serial при свете и в темноте и возьмите середину между ними.
Если фоторезистор и резистор поменять местами, шкала перевернётся: в темноте будет 0. Оба варианта рабочие, и выбирать стоит тот, при котором логика читается естественнее. Мне нравится «темно = большое число»: тогда условие light < порога звучит как «стало темно» — и код читается как фраза.
Мусор в показаниях: шум, усреднение и границы
Неподвижная ручка дрожит: последние биты значения пляшут на 1–3. Это шум — нормальное свойство АЦП, проводов и питания, а не поломка. В Serial он выглядит как бегущая последняя цифра; на яркость светодиода при 8-битном ШИМ почти не влияет, но для порогов вида light < 150 шум способен щёлкать ночником туда-сюда у самой границы.
Классическое лечение — усреднение: прочитать десять раз, сложить, поделить. Дрожание сглаживается, задержка — считанные миллисекунды. Сумму держите в long: десять чтений по 1023 дают 10 230 — в int влезает, но запас кончается на 32 чтениях: 32 × 1023 = 32 736, а на 33-м сумма уезжает за 32767 и становится отрицательной. Привычка суммировать в long снимает этот потолок целиком.
Мнение с оговоркой: усреднение по десяти — моя золотая середина для ручек, фоторезисторов и термисторов. Но для быстрых событий — микрофон, вибрация, удар — усреднение съедает сам сигнал: там нужно одиночное чтение или поиск пика. Правило простое: усредняйте только то, что меняется медленнее, чем вы читаете.
Эксперименты для закрепления
- Переверните диммер:
map(raw, 0, 1023, 255, 0). Ручка будет крутиться «не в ту сторону» — полезный способ менять логику без перепайки схемы. - Сделайте «мёртвую зону»: изменяйте яркость только когда raw больше 100, чтобы при выключенной ручке светодиод не тлел от шума.
- Добавьте индикатор темноты на пин 13: если light меньше вашего порога —
digitalWrite(13, HIGH). Получится ночник с дежурным огоньком. - Выведите в одной строке raw и усреднённое значение, покрутите ручку и посмотрите, как среднее запаздывает, но дрожит меньше. Решите, где вам важнее скорость, а где стабильность.
- Найдите собственный порог «темноты»: запишите значения ползунка при «свете» и «темноте» и возьмите середину. Сравните с моими 150 — совпали ли?
- analogRead(A0–A5) читает напряжение 0–5 В как число 0–1023: АЦП Uno 10-битный, на ESP32 — 0–4095
- pinMode для аналоговых входов не нужен — analogRead настраивает всё сам
- Потенциометр: крайние ноги к 5V и GND, средняя к A0; перепутали края — шкала перевернётся
- map переводит шкалы и не ограничивает значения; constrain удерживает результат в границах
- analogWrite ждёт 0–255 и работает только на пинах с ~: 3, 5, 6, 9, 10, 11 на Uno
- Шум лечится усреднением: десять чтений — золотая середина для медленных датчиков
- 1. Что вернёт analogRead(A0) при напряжении 5 В на входе Uno?
- 2. Сколько ступеней имеет 10-битный АЦП?
- 3. Что вернёт map(512, 0, 1023, 0, 255)?
- 4. На каких пинах Uno работает analogWrite (ШИМ)?
- 5. Почему мой диммер на пине 13 работал как выключатель?
- 6. Что случится, если суммировать 40 чтений по 1023 в переменной int?
На этом основы закончены: четыре урока — и вы умеете хранить данные, принимать решения, повторять действия и читать аналоговый мир. Ночник из начала этого урока соберите сами: потенциометр на A0, светодиод на пин 9, map и constrain — восемь строк в loop(). Добавьте кнопку и условие из второго урока — получится умный ночник: первый проект, который не стыдно подарить.
Следующий раздел — «Serial и отладка»: научимся не просто печатать значения, а отправлять плате команды и разбираться, почему код ведёт себя не так, как задуман. До встречи на виртуальной макетке.
Частые вопросы по теме урока
- Что вернёт analogRead(A0) при напряжении 5 В на входе Uno?
- АЦП Uno 10-битный: 0 В — это 0, 5 В — максимум шкалы 1023. Ступеней 1024, но нумеруются они с нуля. 1023
- Сколько ступеней имеет 10-битный АЦП?
- 2 в степени 10 = 1024. Диапазон 0–255 — это шкала 8-битного ШИМ у analogWrite, не путайте их. 1024 значения от 0 до 1023
- Что вернёт map(512, 0, 1023, 0, 255)?
- map работает с целыми и отбрасывает дробную часть: 512 × 255 / 1023 = 127.5 → 127. 127
- На каких пинах Uno работает analogWrite (ШИМ)?
- ШИМ-пины помечены значком ~. На остальных analogWrite молча работает как digitalWrite — вкл/выкл без плавности. На пинах 3, 5, 6, 9, 10, 11
- Почему мой диммер на пине 13 работал как выключатель?
- analogWrite на пине без ШИМ сводится к вкл/выкл примерно с половины шкалы. Тильда на корпусе платы показывает ШИМ-пины. На пине 13 нет ШИМ
- Что случится, если суммировать 40 чтений по 1023 в переменной int?
- 40 × 1023 = 40 920, а потолок int — 32 767. Поэтому сумму держат в long — или усредняют в несколько проходов. Переполнение: сумма уйдёт в минус