Параллельная работа света и звука
Учимся управлять несколькими исполнительными устройствами в одной программе. Светодиоды и пищалка работают согласованно, кнопка включает режим «Охрана», а перед включением даётся 5 секунд, чтобы выйти из комнаты.
Цель урока: научиться связывать несколько исполнительных устройств в одном коде.
Что понадобится
- 1×Arduino Uno и USB-кабель
- 1×Макетная плата
- 3×Светодиоды: красный, синий, жёлтый
- 3×Резисторы 220 Ом
- 1×Пищалка (пассивный зуммер)
- 1×Тактовая кнопка
- 11×Провода «папа–папа»
Чему научимся
- управлять звуком:
tone(),noTone() - включать свет и звук синхронно
- заменять
delay()наmillis() - подключать кнопку с
INPUT_PULLUP - описывать работу устройства режимами
Теория
10 коротких шагов с примерами. Интерактивные примеры работают прямо на странице.
Исполнительные устройства
Arduino умеет не только «чувствовать» (кнопки, датчики), но и «действовать». Детали, которые что-то делают — светят, звучат, двигаются, — называют исполнительными устройствами. Сегодня их два: светодиод и пищалка.
Оба подключаются к цифровым пинам и управляются одинаково: пин делаем выходом (OUTPUT), а потом подаём на него 5 вольт (HIGH) или 0 вольт (LOW).
pinMode(7, OUTPUT); // пин 7 — выход digitalWrite(7, HIGH); // подать 5 В — светодиод горит digitalWrite(7, LOW); // подать 0 В — светодиод погас
Arduino выполняет команды по одной, сверху вниз, но очень быстро — около 16 миллионов простых действий в секунду. Если команды для света и звука стоят рядом, для нас они срабатывают одновременно.
Светодиод: три правила подключения
- Полярность. Длинная ножка — анод (+), к ней приходит сигнал от пина. Короткая — катод (−), она идёт к земле GND. Со стороны катода на ободке корпуса есть срез.
- Резистор обязателен и стоит со стороны плюса: пин → резистор 220 Ом → длинная ножка. Без резистора потечёт слишком большой ток, и сгорит светодиод или пин Arduino.
- Короткая ножка — в минус через провод. Короткую ножку вставляем в свой столбец, а от него чёрный провод идёт в синюю шину «−». Шина соединена проводом с GND Arduino.
Почему именно 220 Ом? Посчитаем ток по закону Ома. На светодиоде «теряется» около 2 В, на резистор остаётся 5 − 2 = 3 В:
Пин Arduino спокойно выдаёт до 20 мА, светодиоду тоже нормально — светит ярко и не перегревается. Если резистор поставить со стороны минуса, ток будет тот же: в цепи из последовательно соединённых деталей он одинаковый везде. Мы ставим его со стороны плюса.
Пищалка: откуда берётся звук
Внутри пищалки — тонкая пластинка из пьезокерамики. Когда на неё подают напряжение, она чуть-чуть выгибается. Если очень быстро включать и выключать напряжение, пластинка дрожит и толкает воздух — получается звук.
Сколько раз в секунду пластинка дрожит, такая у звука частота. Её измеряют в герцах: 1000 Гц — это 1000 колебаний в секунду. Чем выше частота, тем выше и тоньше звук. Человек слышит примерно от 20 до 20 000 Гц, а пищалка громче всего звучит около 2000–4000 Гц.
| Активная пищалка | Пассивная пищалка | |
|---|---|---|
| Внутри | Свой генератор — пищит сама | Только пластинка |
| Как включить | digitalWrite(pin, HIGH) | tone(pin, частота) |
| Звук | Один тон | Любая частота: мелодии, сирена |
| Как отличить | Снизу залита чёрным, сверху часто наклейка | Снизу видна зелёная плата |
Для сирены нужна пассивная пищалка: только у неё можно менять частоту. У пищалки тоже есть полярность: вывод «+» подключаем к пину, второй — к GND.
Команды tone() и noTone()
tone(8, 1000); // пищать на пине 8 частотой 1000 Гц,
// пока не выключим
tone(8, 1000, 200); // пищать 200 мс — звук выключится сам
noTone(8); // выключить звук на пине 8- tone() не останавливает программу. Звук идёт «в фоне», а следующие команды выполняются сразу. Поэтому, чтобы дослушать звук, после
tone()часто ставятdelay(). - Звук продолжается, пока его не выключат
noTone()или не поменяют следующимtone()с другой частотой. - Одновременно может звучать только один
tone().
Пока работает tone(), на пинах 3 и 11 не работает плавная яркость analogWrite(): у них общий внутренний таймер. Сегодня мы используем только digitalWrite(), поэтому нам это не мешает.
Сирена: свет и звук по фазам
Сирена — это повторяющиеся фазы. В каждой фазе одновременно включаются свой цвет и свой звук. Секрет синхронизации простой: команды для света и для звука стоят рядом, без delay() между ними, а пауза — одна на всю фазу.
// Фаза 1: красный + высокий тон digitalWrite(RED_LED, HIGH); digitalWrite(BLUE_LED, LOW); tone(BUZZER, 1000); delay(400); // Фаза 2: синий + низкий тон digitalWrite(RED_LED, LOW); digitalWrite(BLUE_LED, HIGH); tone(BUZZER, 750); delay(400);
Бывает и сирена-«вой»: частота плавно растёт и падает. Её делают циклом for, который много раз немного меняет частоту:
for (int freq = 600; freq <= 1500; freq = freq + 10) {
tone(BUZZER, freq); // каждый шаг — чуть выше
delay(5);
}Почему delay() мешает
delay(400) — это «сон»: 400 мс Arduino ничего не делает и ничего не замечает. Пока работает только сирена, это не страшно. Но если добавить кнопку, Arduino будет проверять её только между «снами», и короткое нажатие потеряется.
Представьте охранника, который каждые полсекунды закрывает глаза на 0,4 секунды. Вора он заметит не всегда.
Попробуйте в примере справа: нажимайте кнопку в разные моменты. Сверху Arduino «спит» в delay(), снизу проверяет кнопку постоянно.
millis() — часы внутри Arduino
millis() сообщает, сколько миллисекунд прошло с момента включения Arduino. Это секундомер, который никогда не останавливается. Вместо того чтобы «спать», программа постоянно смотрит на часы и спрашивает: «Не пора ли?»
unsigned long lastSwitch = 0; // когда последний раз переключали
void loop() {
if (millis() - lastSwitch >= 400) { // прошло 400 мс?
lastSwitch = millis(); // запомнили время
// ... переключаем фазу сирены
}
// здесь же успеваем проверять кнопку
}Так loop() выполняется тысячи раз в секунду, и в каждом проходе Arduino успевает и проверить кнопку, и, если пора, переключить сирену. Получается параллельная работа.
Когда варим яйца 10 минут, мы не стоим у плиты, глядя на кастрюлю. Мы запоминаем время, делаем бутерброды и иногда поглядываем на часы.
Миллисекунд быстро становится много: через минуту — 60 000. В int помещается только до 32 767, поэтому время храним в unsigned long (до 4 миллиардов — это почти 50 дней).
Кнопка и INPUT_PULLUP
Кнопка — устройство ввода. Arduino читает её командой digitalRead(), которая возвращает HIGH или LOW.
Если пин ни к чему не подключён, он «висит в воздухе» и ловит помехи: digitalRead() выдаёт то HIGH, то LOW случайно. Чтобы этого не было, пин через резистор «подтягивают» к +5 В. У Arduino такой резистор есть внутри — его включает одна команда:
pinMode(BUTTON, INPUT_PULLUP); // вход с подтяжкой к +5 В int b = digitalRead(BUTTON); // отпущена → HIGH, нажата → LOW
Кнопка подключается одной ножкой к пину 2, другой — к GND. Нажали — пин соединился с землёй, и Arduino прочитала LOW. Да, наоборот: нажата = LOW.
Дребезг контактов и кнопка-выключатель
Металлические контакты кнопки при нажатии несколько раз «подпрыгивают», и за 1–5 мс сигнал успевает много раз смениться. Arduino очень быстрая и может посчитать одно нажатие за несколько. Это называется дребезг.
Лечится просто: заметили изменение — подождали 50 мс, пока контакты успокоятся. А чтобы кнопка работала как выключатель (нажал — вкл, нажал ещё раз — выкл), ловим момент нажатия и переворачиваем переменную знаком ! («не»):
int button = digitalRead(BUTTON);
if (button != lastButton) { // состояние изменилось
delay(50); // ждём конца дребезга
if (button == LOW) { // это было нажатие
guardOn = !guardOn; // true ↔ false
}
lastButton = button; // запоминаем
}Режимы работы (состояния)
Сложное устройство удобно описывать режимами. Наша сигнализация всегда находится ровно в одном из трёх режимов и в каждом ведёт себя по-своему. Из режима в режим её переводят события: нажатие кнопки или истечение времени.
const int MODE_OFF = 0; // выключено
const int MODE_ARMING = 1; // постановка: 5 секунд на выход
const int MODE_GUARD = 2; // охрана: сирена
int mode = MODE_OFF; // текущий режим
if (mode == MODE_ARMING) {
// мигаем жёлтым и смотрим, не прошло ли 5 секунд
}Так устроены светофоры, стиральные машины, лифты и настоящие сигнализации. В программировании такую схему называют конечным автоматом.
Практика
Четыре задания: каждое добавляет к схеме и коду что-то новое.
Задание 1. Собрать схему из пищалки и двух мигающих светодиодов (красный и синий)
Собираем основу сирены. Светодиоды мигают по очереди, как маячки полицейской машины, а пищалка при включении коротко пикает — так проверяем, что всё подключено правильно.
Задание 2. Синхронизировать звук сирены с переключением светодиодов
Схема не меняется — меняется только программа. В каждую фазу добавляем звук: красный свет — высокий тон, синий — низкий. Свет и звук включаются одной группой команд, поэтому меняются одновременно.
Есть два варианта кода: «ти-та» (два тона) и «вой» (частота плавно растёт и падает).
Задание 3. Добавить кнопку включения и выключения режима «Охрана»
Одно нажатие включает охрану (работает сирена), следующее — выключает. Чтобы кнопка срабатывала мгновенно, переписываем сирену без delay(): теперь фазы переключаются по часам millis().
Сравните с вариантом «Ошибка: delay()». В нём сирена сделана как в задании 2, и короткие нажатия теряются. Попробуйте в симуляторе!
Задание 4. При включении даётся 5 секунд задержки (мигает жёлтый светодиод)
Как у настоящей сигнализации: после нажатия кнопки у хозяина есть 5 секунд, чтобы выйти. Всё это время мигает жёлтый светодиод и тикает пищалка, затем включается сирена. Повторное нажатие в любой момент — отмена.
Сборка по шагам
Резистор ставим со стороны плюса: он лежит вдоль ряда I и соединяет столбец провода от пина со столбцом длинной ножки (+). От столбца короткой ножки (−) чёрный провод идёт в синюю шину «−» — так же, как у пищалки.
Как работает код
- В начале даём пинам имена:
RED_LED = 7и так далее. Если провод придётся переставить на другой пин, достаточно поменять одно число. - В
setup()все три пина становятся выходами. Затемtone()→delay(200)→noTone(): писк длиной 0,2 секунды. Он звучит один раз —setup()выполняется только при включении. loop()повторяется бесконечно: фаза 1 (горит красный) и фаза 2 (горит синий) по 300 мс.- Следите за подсветкой строк в коде: она показывает, какая строка выполняется прямо сейчас. Почти всё время программа стоит на строках с
delay().
Если не работает
- Светодиод не горит. Длинная ножка (+) — в одном столбце с концом резистора, короткая (−) — в столбце, откуда чёрный провод идёт в шину «−». Перевёрнутый светодиод не горит: поменяйте ножки местами.
- Не горит ничего. Провод GND от Arduino должен идти в ту же синюю шину «−», куда приходят чёрные провода от светодиодов.
- Пищалка молчит. Её ножки должны стоять в разных столбцах. «+» — к пину 8, «−» — к GND.
- Звук хриплый или один и тот же. Скорее всего, пищалка активная. Для сирены нужна пассивная.
Попробуй изменить
- Поставь
BLINK_TIME = 80— получится стробоскоп. - Добавь в каждую фазу короткий щелчок:
tone(BUZZER, 2000, 20); - Сделай «двойную вспышку»: красный мигает два раза быстро, потом синий два раза.
Код в окне можно редактировать и сразу запускать в симуляторе кнопкой «Запустить изменённый код».
Сборка
Схема та же, что в задании 1, — ничего добавлять не нужно. Загрузите новый скетч.
Как работает код
tone()не ждёт: звук начинается и продолжается сам, пока следующийtone()не поменяет частоту. Поэтому в каждой фазе свой тон звучит ровно 400 мс.- Команды света и звука стоят подряд, без паузы, а
delay()— одна на фазу. Значит, свет и звук меняются в один и тот же момент. - Посмотрите на график под симулятором: края красной и синей полос совпадают со сменой частоты пищалки.
- В варианте «Вой» цикл
forменяет частоту от 600 до 1500 Гц шагами по 10 Гц каждые 5 мс: 91 шаг ≈ 0,45 с. Пока звук растёт, горит красный, пока падает — синий.
Если не работает
- Звук не меняется. Проверьте, что в фазах стоят разные константы:
HIGH_TONEиLOW_TONE. - Свет и звук «разъехались». Между командами фазы не должно быть
delay()— только одна пауза в конце фазы. - Вой звучит ступеньками. Это нормально: частота меняется шагами по 10 Гц. Уменьшите
STEPдо 5 — станет плавнее.
Попробуй изменить
- Поменяй тоны:
HIGH_TONE = 1500,LOW_TONE = 500. - Сделай фазы по 150 мс — сирена станет тревожнее.
- В варианте «Вой» поставь
STEP_TIME = 10— вой станет медленнее.
Сборка по шагам
Кнопку ставим поперёк центральной канавки, чтобы её ножки оказались в разных половинах платы. Провод от D2 и провод на GND — в столбцы по разные стороны кнопки.
Как работает код
loop()теперь выполняется тысячи раз в секунду. В каждом проходе две быстрые проверки: кнопка и сирена.- Кнопка. Если состояние изменилось, ждём 50 мс (дребезг) и, если это нажатие, переключаем
guardOn. При выключении сразу гасим свет и звук. - Сирена. Условие
guardOn && now - lastSwitch >= PHASE_TIMEчитается так: «охрана включена и с последней смены фазы прошло 400 мс». Тогда меняем фазу и сразу включаем нужный цвет и тон. - Монитор порта показывает сообщения «Охрана ВКЛЮЧЕНА» и «Охрана выключена» — так удобно проверять программу.
- Нажмите вариант «Ошибка: delay()» и попробуйте быстро нажимать кнопку, пока звучит сирена: нажатия теряются. В подсветке кода видно, что программа почти всё время стоит на
delay().
Если не работает
- Сирена включается сама или «дёргается». Кнопка стоит не поперёк канавки или провода попали в один столбец с одной стороны кнопки.
- Кнопка не реагирует. Проверьте
pinMode(BUTTON, INPUT_PULLUP)и провод от второй ножки кнопки к GND. - Одно нажатие срабатывает дважды. Увеличьте паузу дребезга до 80–100 мс.
- В Мониторе порта ничего нет. Откройте его после загрузки и выберите скорость 9600.
Попробуй изменить
- Замени
INPUT_PULLUPнаINPUTи запусти в симуляторе: вход начнёт «шуметь», сирена будет включаться сама. - Удали строку
delay(50);и проверь на настоящей кнопке, всегда ли нажатие срабатывает один раз. - Сделай сирену «вой» на
millis(): каждые 5 мс меняй частоту на 10 Гц.
Сборка по шагам
Как работает код
- Вместо
bool guardOnтеперьint mode: режимов три, а не два. Номера режимов — константыMODE_OFF,MODE_ARMING,MODE_GUARD. - Кнопка: из режима ВЫКЛ переводит в ПОСТАНОВКУ и засекает время
armStart = now. Из любого другого режима — выключает всё. - Постановка: каждые 250 мс жёлтый светодиод меняет состояние. При вспышке звучит «тик»
tone(BUZZER, 2000, 30)— он выключится сам через 30 мс. Когдаnow - armStart >= 5000, режим меняется на ОХРАНУ. - Охрана: работает та же сирена, что в задании 3.
- Над графиком симулятора видно текущий режим и сколько секунд осталось до включения охраны.
Если не работает
- Жёлтый не мигает. Проверьте провод от D5, резистор вдоль ряда I и чёрный провод от короткой ножки в шину «−».
- Сирена включается сразу. Убедитесь, что
armStart = now;стоит внутри ветки, где режим становитсяMODE_ARMING. - Сирена не выключается. В ветке выключения должны быть
noTone(BUZZER)и гашение всех трёх светодиодов.
Попробуй изменить
- Дай на выход 10 секунд вместо 5.
- В последние 2 секунды мигай вдвое быстрее. Подсказка:
if (now - armStart > 3000). - Добавь «датчик двери» — вторую кнопку. Тогда в режиме охраны сирена молчит и включается, только когда «открыли дверь».
Итоги
Вопросы для закрепления, задания для быстрых и план урока для учителя.
1Почему свет и звук меняются «одновременно», хотя команды выполняются по очереди?
Между этими командами нет паузы, а Arduino выполняет их за миллионные доли секунды — для глаза и уха это один момент.
2Чем tone(8, 1000) отличается от tone(8, 1000, 200)?
Первая команда звучит, пока не вызовут noTone(8) или другой tone(). Вторая сама выключит звук через 200 мс.
3Почему в задании 3 нельзя оставить delay(400) в сирене?
Во время delay() Arduino не проверяет кнопку, и короткое нажатие теряется.
4Что вернёт digitalRead() для нажатой кнопки с INPUT_PULLUP?
LOW: нажатая кнопка соединяет пин с землёй. Отпущенная — HIGH благодаря подтягивающему резистору.
5Зачем нужна пауза 50 мс после изменения состояния кнопки?
Чтобы переждать дребезг контактов: иначе одно нажатие может посчитаться за несколько.
6Почему время храним в unsigned long, а не в int?
int вмещает максимум 32 767 — это всего 32 секунды в миллисекундах. unsigned long хватит почти на 50 дней.
7Сколько режимов у сигнализации в задании 4 и что переключает их?
Три: выключено, постановка, охрана. Переходы вызывают нажатие кнопки и истечение 5 секунд.
Задания для тех, кто справился быстрее
- Датчик двери. Вторая кнопка: в режиме охраны сирена молчит, а при «открытии двери» включается тревога.
- Секретное снятие. Охрана выключается только двойным нажатием.
- Вой без delay(). В задании 4 замените «ти-та» на плавный вой на
millis(). - Индикатор готовности. Зелёный светодиод горит, когда система выключена.
План урока · первые 45 минут
- 0–5Цель урока, показ готовой сирены в симуляторе
- 5–20Теория 1–5: пищалка,
tone(), синхронизация - 20–35Задание 1: сборка, проверка писка
- 35–45Задание 2: «ти-та» и «вой»
План урока · вторые 45 минут
- 0–12Теория 6–9: «delay против millis», кнопка, дребезг
- 12–27Задание 3, сравнение с вариантом «Ошибка: delay()»
- 27–32Теория 10: режимы
- 32–42Задание 4
- 42–45Вопросы для закрепления