Робототехника · Arduino Uno
Урок 8·2 академических часа·Проект «Охранная сирена»

Параллельная работа света и звука

Учимся управлять несколькими исполнительными устройствами в одной программе. Светодиоды и пищалка работают согласованно, кнопка включает режим «Охрана», а перед включением даётся 5 секунд, чтобы выйти из комнаты.

Цель урока: научиться связывать несколько исполнительных устройств в одном коде.

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

  • 1×Arduino Uno и USB-кабель
  • 1×Макетная плата
  • 3×Светодиоды: красный, синий, жёлтый
  • 3×Резисторы 220 Ом
  • 1×Пищалка (пассивный зуммер)
  • 1×Тактовая кнопка
  • 11×Провода «папа–папа»

Чему научимся

  • управлять звуком: tone(), noTone()
  • включать свет и звук синхронно
  • заменять delay() на millis()
  • подключать кнопку с INPUT_PULLUP
  • описывать работу устройства режимами
Итоговая схема проекта после задания 4. В разделе «Практика» её можно запустить, нажимать кнопку и слушать сирену.

Теория

10 коротких шагов с примерами. Интерактивные примеры работают прямо на странице.

01

Исполнительные устройства

Arduino умеет не только «чувствовать» (кнопки, датчики), но и «действовать». Детали, которые что-то делают — светят, звучат, двигаются, — называют исполнительными устройствами. Сегодня их два: светодиод и пищалка.

Оба подключаются к цифровым пинам и управляются одинаково: пин делаем выходом (OUTPUT), а потом подаём на него 5 вольт (HIGH) или 0 вольт (LOW).

pinMode(7, OUTPUT);      // пин 7 — выход
digitalWrite(7, HIGH);   // подать 5 В — светодиод горит
digitalWrite(7, LOW);    // подать 0 В — светодиод погас
Главная идея урока

Arduino выполняет команды по одной, сверху вниз, но очень быстро — около 16 миллионов простых действий в секунду. Если команды для света и звука стоят рядом, для нас они срабатывают одновременно.

02

Светодиод: три правила подключения

  1. Полярность. Длинная ножка — анод (+), к ней приходит сигнал от пина. Короткая — катод (−), она идёт к земле GND. Со стороны катода на ободке корпуса есть срез.
  2. Резистор обязателен и стоит со стороны плюса: пин → резистор 220 Ом → длинная ножка. Без резистора потечёт слишком большой ток, и сгорит светодиод или пин Arduino.
  3. Короткая ножка — в минус через провод. Короткую ножку вставляем в свой столбец, а от него чёрный провод идёт в синюю шину «−». Шина соединена проводом с GND Arduino.

Почему именно 220 Ом? Посчитаем ток по закону Ома. На светодиоде «теряется» около 2 В, на резистор остаётся 5 − 2 = 3 В:

I = U / R = 3 В / 220 Ом ≈ 0,014 А = 14 мА

Пин Arduino спокойно выдаёт до 20 мА, светодиоду тоже нормально — светит ярко и не перегревается. Если резистор поставить со стороны минуса, ток будет тот же: в цепи из последовательно соединённых деталей он одинаковый везде. Мы ставим его со стороны плюса.

Анод (+) длинная Катод (−) короткая срез — состороны катода 220 Ом красный · красный · коричневый · золотой
У 5-полосных резисторов (часто синие) 220 Ом — это красный, красный, чёрный, чёрный, коричневый.
03

Пищалка: откуда берётся звук

Внутри пищалки — тонкая пластинка из пьезокерамики. Когда на неё подают напряжение, она чуть-чуть выгибается. Если очень быстро включать и выключать напряжение, пластинка дрожит и толкает воздух — получается звук.

Сколько раз в секунду пластинка дрожит, такая у звука частота. Её измеряют в герцах: 1000 Гц — это 1000 колебаний в секунду. Чем выше частота, тем выше и тоньше звук. Человек слышит примерно от 20 до 20 000 Гц, а пищалка громче всего звучит около 2000–4000 Гц.

Активная пищалкаПассивная пищалка
ВнутриСвой генератор — пищит самаТолько пластинка
Как включитьdigitalWrite(pin, HIGH)tone(pin, частота)
ЗвукОдин тонЛюбая частота: мелодии, сирена
Как отличитьСнизу залита чёрным, сверху часто наклейкаСнизу видна зелёная плата

Для сирены нужна пассивная пищалка: только у неё можно менять частоту. У пищалки тоже есть полярность: вывод «+» подключаем к пину, второй — к GND.

04

Команды 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(), поэтому нам это не мешает.

05

Сирена: свет и звук по фазам

Сирена — это повторяющиеся фазы. В каждой фазе одновременно включаются свой цвет и свой звук. Секрет синхронизации простой: команды для света и для звука стоят рядом, без 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);
}
Временная диаграмма настоящего скетча из задания 2: края цветных полос и смена частоты совпадают — это и есть синхронизация.
06

Почему delay() мешает

delay(400) — это «сон»: 400 мс Arduino ничего не делает и ничего не замечает. Пока работает только сирена, это не страшно. Но если добавить кнопку, Arduino будет проверять её только между «снами», и короткое нажатие потеряется.

Представьте охранника, который каждые полсекунды закрывает глаза на 0,4 секунды. Вора он заметит не всегда.

Попробуйте в примере справа: нажимайте кнопку в разные моменты. Сверху Arduino «спит» в delay(), снизу проверяет кнопку постоянно.

07

millis() — часы внутри Arduino

millis() сообщает, сколько миллисекунд прошло с момента включения Arduino. Это секундомер, который никогда не останавливается. Вместо того чтобы «спать», программа постоянно смотрит на часы и спрашивает: «Не пора ли?»

unsigned long lastSwitch = 0;   // когда последний раз переключали

void loop() {
  if (millis() - lastSwitch >= 400) {   // прошло 400 мс?
    lastSwitch = millis();              // запомнили время
    // ... переключаем фазу сирены
  }
  // здесь же успеваем проверять кнопку
}

Так loop() выполняется тысячи раз в секунду, и в каждом проходе Arduino успевает и проверить кнопку, и, если пора, переключить сирену. Получается параллельная работа.

Аналогия

Когда варим яйца 10 минут, мы не стоим у плиты, глядя на кастрюлю. Мы запоминаем время, делаем бутерброды и иногда поглядываем на часы.

Тип unsigned long

Миллисекунд быстро становится много: через минуту — 60 000. В int помещается только до 32 767, поэтому время храним в unsigned long (до 4 миллиардов — это почти 50 дней).

08

Кнопка и 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.

09

Дребезг контактов и кнопка-выключатель

Металлические контакты кнопки при нажатии несколько раз «подпрыгивают», и за 1–5 мс сигнал успевает много раз смениться. Arduino очень быстрая и может посчитать одно нажатие за несколько. Это называется дребезг.

Лечится просто: заметили изменение — подождали 50 мс, пока контакты успокоятся. А чтобы кнопка работала как выключатель (нажал — вкл, нажал ещё раз — выкл), ловим момент нажатия и переворачиваем переменную знаком ! («не»):

int button = digitalRead(BUTTON);
if (button != lastButton) {    // состояние изменилось
  delay(50);                   // ждём конца дребезга
  if (button == LOW) {         // это было нажатие
    guardOn = !guardOn;        // true ↔ false
  }
  lastButton = button;         // запоминаем
}
HIGHLOW дребезг 1–5 мс ждём 50 мс читаем — точно LOW нажали
Так выглядит сигнал настоящей кнопки на осциллографе: вместо одного перехода — несколько.
10

Режимы работы (состояния)

Сложное устройство удобно описывать режимами. Наша сигнализация всегда находится ровно в одном из трёх режимов и в каждом ведёт себя по-своему. Из режима в режим её переводят события: нажатие кнопки или истечение времени.

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 секунд
}

Так устроены светофоры, стиральные машины, лифты и настоящие сигнализации. В программировании такую схему называют конечным автоматом.

ВЫКЛЮЧЕНО ПОСТАНОВКА 5 с · мигает жёлтый ОХРАНА сирена кнопка прошло 5 с кнопка кнопка (отмена)
Стрелки — переходы между режимами. В задании 4 вы увидите текущий режим прямо в симуляторе.

Практика

Четыре задания: каждое добавляет к схеме и коду что-то новое.

Задание 1. Собрать схему из пищалки и двух мигающих светодиодов (красный и синий)

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

Детали
красный светодиодсиний светодиод2 × резистор 220 Омпищалка7 проводов
Новое в коде
tone()noTone()const intdelay()

Сборка по шагам

Резистор ставим со стороны плюса: он лежит вдоль ряда I и соединяет столбец провода от пина со столбцом длинной ножки (+). От столбца короткой ножки (−) чёрный провод идёт в синюю шину «−» — так же, как у пищалки.

Как работает код

  1. В начале даём пинам имена: RED_LED = 7 и так далее. Если провод придётся переставить на другой пин, достаточно поменять одно число.
  2. В setup() все три пина становятся выходами. Затем tone() → delay(200) → noTone(): писк длиной 0,2 секунды. Он звучит один раз — setup() выполняется только при включении.
  3. loop() повторяется бесконечно: фаза 1 (горит красный) и фаза 2 (горит синий) по 300 мс.
  4. Следите за подсветкой строк в коде: она показывает, какая строка выполняется прямо сейчас. Почти всё время программа стоит на строках с delay().

Если не работает

  • Светодиод не горит. Длинная ножка (+) — в одном столбце с концом резистора, короткая (−) — в столбце, откуда чёрный провод идёт в шину «−». Перевёрнутый светодиод не горит: поменяйте ножки местами.
  • Не горит ничего. Провод GND от Arduino должен идти в ту же синюю шину «−», куда приходят чёрные провода от светодиодов.
  • Пищалка молчит. Её ножки должны стоять в разных столбцах. «+» — к пину 8, «−» — к GND.
  • Звук хриплый или один и тот же. Скорее всего, пищалка активная. Для сирены нужна пассивная.

Попробуй изменить

  • Поставь BLINK_TIME = 80 — получится стробоскоп.
  • Добавь в каждую фазу короткий щелчок: tone(BUZZER, 2000, 20);
  • Сделай «двойную вспышку»: красный мигает два раза быстро, потом синий два раза.

Код в окне можно редактировать и сразу запускать в симуляторе кнопкой «Запустить изменённый код».

Итоги

Вопросы для закрепления, задания для быстрых и план урока для учителя.

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 минут

  1. 0–5Цель урока, показ готовой сирены в симуляторе
  2. 5–20Теория 1–5: пищалка, tone(), синхронизация
  3. 20–35Задание 1: сборка, проверка писка
  4. 35–45Задание 2: «ти-та» и «вой»

План урока · вторые 45 минут

  1. 0–12Теория 6–9: «delay против millis», кнопка, дребезг
  2. 12–27Задание 3, сравнение с вариантом «Ошибка: delay()»
  3. 27–32Теория 10: режимы
  4. 32–42Задание 4
  5. 42–45Вопросы для закрепления