Светофор на Arduino: проект для новичка в симуляторе
Первый проект после мигающего светодиода упирается в одну стену — delay(). Скетч светофора на delay() работает ровно до тех пор, пока вы не захотите кнопку пешехода, зуммер или счётчик секунд: пауза останавливает процессор целиком, и всё, что случилось внутри неё, теряется. Правильный светофор строится на millis() с первого дня, и симулятор — место, где этот приём видно глазами: фазы идут по кругу, loop() при этом прокручивается тысячи раз в секунду.
Ниже — схема на трёх светодиодах с пинами и токами, таблица фаз, скетч без единого delay() и сценарий проверки в браузере. Три светодиода уже лежат в палитре «Макетка» нашего симулятора — соберите тройку и смотрите, как фазы идут, ещё до того, как диоды достали из пакета.
Схема: три светодиода и три резистора
- Красный — пин 12, жёлтый — пин 11, зелёный — пин 10: анод через резистор 220 Ом к пину, катод к GND.
- Резистор обязателен: без него пин отдаёт ток до предела ATmega328P (40 мА — потолок по даташиту, выше 20 мА уже не рекомендуется), а диоду для полной яркости нужно 10-15 мА.
- Ток по закону Ома: (5 В − 2 В) / 220 Ом ≈ 13,6 мА у красного и жёлтого; зелёный с падением 2,2 В берёт ≈ 12,7 мА. Ни пин, ни диод не перегружены.
- Земли всех трёх диодов собираются на одну шину GND макетки — иначе фазы будут вести себя странно.
| Фаза | Что горит | Длительность | Правило перехода |
|---|---|---|---|
| KRAS | красный, пин 12 | 5000 мс | в ZHOLT по таймеру |
| ZHOLT | жёлтый, пин 11 | 2000 мс | в ZELEN; жёлтый между красным и зелёным всегда |
| ZELEN | зелёный, пин 10 | 5000 мс | в KRAS, мимо жёлтого — нельзя |
Почему millis(), а не delay()
delay(5000) в цикле светофора выглядит невинно, пока проект не обрастает хотениями: кнопка пешехода, писк на красном, счётчик на дисплее. Всё это требует, чтобы loop() крутился без пауз, а delay() замораживает его целиком. millis() возвращает беззнаковое число миллисекунд с включения платы (переполняется через 49,7 дня — разности от этого не ломаются), и логика фазы звучит так: «прошло ли 5000 мс с момента переключения». Проверка выполняется на каждом обороте loop(). Подробный разбор — в лендинге millis() против delay() и уроке про millis вместо delay.
Скетч: автомат на трёх светодиодах без delay()
Стенд в симуляторе: три светодиода из палитры «Макетка» на пины 12, 11 и 10. Здесь диоды зажигает сам скетч — ваша задача смотреть на фазы и сверять с секундомером.
const byte PIN_FAZ[3] = {12, 11, 10}; // красный, жёлтый, зелёныйconst unsigned long DUR_MS[3] = {5000, 2000, 5000}; byte faza = 0; // 0-красный, 1-жёлтый, 2-зелёныйunsigned long tFaza = 0; // момент включения текущей фазы void setup() { for (byte i = 0; i < 3; i++) pinMode(PIN_FAZ[i], OUTPUT); digitalWrite(PIN_FAZ[0], HIGH); // старт с красного tFaza = millis();} void loop() { if (millis() - tFaza < DUR_MS[faza]) return; // фаза не досидела digitalWrite(PIN_FAZ[faza], LOW); // погасить текущую faza = (faza + 1) % 3; // красный -> жёлтый -> зелёный digitalWrite(PIN_FAZ[faza], HIGH); // зажечь следующую tFaza = millis(); // таймер перезапущен ровно раз}Ключевая строка — tFaza = millis() рядом со сменой фазы: таймер перезапускается ровно в момент переключения, и длительности получаются ровно из таблицы. Если пара «сменил состояние — перезапустил таймер» разъезжается по коду, фазы поплывут: это самая частая светофорная ошибка, и она ловится банальным секундомером.
Три грабли, которые я ловлю у новичков
- Забытый pinMode OUTPUT: диод не светится вовсе или тлеет вполнакала — пин остался в входном режиме с подтяжкой и не умеет отдавать ток.
- Таймер перезапускается не в момент смены фазы, а где-то ещё: фазы «плывут» на секунды, и таблица перестаёт сходиться с секундомером.
- Жёлтый пропущен или горит вместе с красным: электрика в порядке, а логика уже брак — жёлтый между красным и зелёным обязан быть всегда.
Усложнение: кнопка пешехода и зуммер
Когда тройка диодов ходит по кругу как часы, добавляйте кнопку на пин 2 с INPUT_PULLUP и правило «запрос укорачивает зелёный»: флаг ставится по digitalRead, проверяется в зелёной фазе. Дальше — звук: пассивный зуммер KY-006 пикнет через tone() в момент смены на красный; светодиодам здесь ШИМ не нужен, так что конфликтов с таймерами не будет. Полная версия с кнопкой, монитором порта и проверками уже разобрана в старом лендинге про эмулятор светофора и уроке про конечный автомат — соберите сначала простую тройку в симуляторе, потом усложняйте.