Обе сигнализации из этой пары уроков смотрели в одну точку: дверная на герконе не видела, что шкаф качнули со спины, а датчик наклона молчал, когда створку просто открыли без толчка. Настоящая охрана начинается, когда датчиков несколько и они делят один зуммер: толчок без открытия — может, сквозняк, открытие без толчка — хозяйка за банкой варенья. Решать, что из этого тревога, будет автомат состояний — и заодно появятся режимы, которых не было у одиночных датчиков: постановка, задержка на выход, сброс.
План такой: автомат из четырёх состояний на switch, постановка кнопкой на D4 с задержкой 5 секунд на выход, датчики на D2 (наклон) и D3 (геркон), тревога — сирена tone от 400 до 1200 Гц с миганием светодиода, сброс той же кнопкой. Сирену с пилообразной частотой мы уже собирали в уроке про звуковую сигнализацию — здесь она становится одним из выходов автомата, а не всей программой.
Автомат из четырёх состояний
Каждый режим — число в переменной, а развилка — switch, как в уроке про switch и режимы. Режим 0 — снято: светодиод погашен, датчиков никто не слушает, кнопка ставит систему на охрану. Режим 1 — задержка на выход: светодиод мигает часто, как обратный отсчёт, через 5000 мс автомат сам переходит на охрану. Режим 2 — на охране: светодиод вспыхивает раз в 800 мс, скетч слушает оба датчика. Режим 3 — тревога: сирена-пила плюс мерцание, кнопка сбрасывает в снято. Каркас automata — тот же, что у светофора из урока про конечный автомат, только входов два.
Кнопка на D4 опрашивается по фронту с гейтом 25 мс — тем же приёмом, что счётчик встрясок из урока про KY-002 выше. Нажатие работает по-разному в зависимости от режима: в снятом — ставит на охрану с отсчётом, в задержке и на охране — возвращает в снятое (передумали), в тревоге — сброс. Одной кнопки хватает на всё управление: для самоделки это честный компромисс, а у кодового замка из урока про клавиатуру 4x4 на ту же роль идёт набор ключа.
Датчики опрашиваются только в режиме 2 — и это не лень, а смысл автомата. Пока идёт отсчёт 5 секунд, вы сами хлопаете дверью и сдвигаете шкаф: если бы датчики слушались и здесь, система завыла бы на вашей же постановке. А в снятом режиме ей вообще нечего делать с толчками — сигналка, которая орёт при выключенном охранном режиме, годится только в кино.
Пять секунд — компромисс из практики: дверь гаража успеешь закрыть, калитку — тоже, а терпение нажимающего ещё не кончится. Меньше трёх — успеешь только рывком, больше десяти — забудешь, что система уже ставится. Отсчёт держит не delay, а сравнение millis() - tMode >= 5000 внутри case 1, поэтому кнопка «передумать» работает в любой момент отсчёта — и это стоит держать в голове, показывая систему семье: случайное нажатие снимает постановку молча, только по строке в журнале.
| Режим | Светодиод | Звук | Как выйти |
|---|---|---|---|
| 0 — снято | погашен | тишина | кнопка — постановка с отсчётом |
| 1 — задержка 5 с | мигает быстро, около 4 раз в секунду | тишина | сам через 5000 мс — на охрану; кнопка — передумать |
| 2 — на охране | вспышка раз в 800 мс | тишина | толчок или открытие двери — тревога; кнопка — снять |
| 3 — тревога | мерцает вместе с шагами сирены | пила 400-1200 Гц | кнопка — сброс в «снято» |
const int TILT = 2; // HIGH = толчок или наклонconst int REED = 3; // HIGH = дверь закрытаconst int BUZZER = 8;const int BTN = 4; // постановка и сбросconst int LED = 12; int mode = 0; // 0 снято, 1 задержка, 2 охрана, 3 тревогаunsigned long tMode = 0;unsigned long tStep = 0;unsigned long tLed = 0;unsigned long tBtn = 0;unsigned long tLog = 0;int freq = 400;int prevBtn = HIGH;bool ledOn = false; void setup() { pinMode(TILT, INPUT); pinMode(REED, INPUT); pinMode(BTN, INPUT_PULLUP); pinMode(BUZZER, OUTPUT); pinMode(LED, OUTPUT); Serial.begin(9600); Serial.println("Ohrana: knopka D4 - postanovka / sbros");} void loop() { int btn = digitalRead(BTN); if (btn == LOW && prevBtn == HIGH && millis() - tBtn > 25) { tBtn = millis(); if (mode == 0) { mode = 1; tMode = millis(); Serial.println("postanovka: 5 s na vyhod"); } else { mode = 0; noTone(BUZZER); digitalWrite(LED, LOW); ledOn = false; Serial.println("snyato s ohrany"); } } prevBtn = btn; if (millis() - tLog >= 2000) { // пульс в журнале tLog = millis(); Serial.print("rezhim="); Serial.println(mode); } switch (mode) { case 0: digitalWrite(LED, LOW); break; case 1: // задержка на выход if (millis() - tLed >= 120) { tLed = millis(); ledOn = !ledOn; digitalWrite(LED, ledOn ? HIGH : LOW); } if (millis() - tMode >= 5000) { mode = 2; tMode = millis(); Serial.println("na ohrane"); } break; case 2: // на охране: слушаем датчики if (millis() - tLed >= 800) { tLed = millis(); ledOn = !ledOn; digitalWrite(LED, ledOn ? HIGH : LOW); } if (digitalRead(TILT) == HIGH) { mode = 3; tStep = millis(); freq = 400; Serial.println("trevoga: tolchok"); } else if (digitalRead(REED) == LOW) { mode = 3; tStep = millis(); freq = 400; Serial.println("trevoga: dver"); } break; case 3: // тревога: сирена и мерцание if (millis() - tStep >= 40) { // шаг пилы 40 Гц за 40 мс tStep = millis(); freq = freq + 40; if (freq > 1200) freq = 400; tone(BUZZER, freq); } if (millis() - tLed >= 150) { tLed = millis(); ledOn = !ledOn; digitalWrite(LED, ledOn ? HIGH : LOW); } break; } delay(10);}Постановка разобрана выше, теперь тревога. Сирена — пила: частота растёт по 40 Гц каждые 40 мс от 400 до 1200, полный подъём 800 мс, и ухо читает нарастание как знакомый вой. Светодиод в тревоге мерцает со своим таймером в 150 мс — аварийная лампа, которую видно даже из угла. Важно, что ни одна ветка не блокирует loop(): шаг сирены длится 40 мс, за это же время успевает опроситься кнопка сброса — та самая грабля блокирующего for из урока про сирену здесь исключена конструкцией.
Сброс работает только кнопкой: закрыли дверь при воющей сирене — ничего не изменится, тревога не снимается «автоматически». Это осознанная строгость: тревога — событие, о котором человек должен узнать и подтвердить, иначе вор закроет дверь за вас, и система сама себя утихомирит. По той же логике после сброса система встаёт в «снято», а не обратно на охрану: повторная постановка — ваше решение, ещё одно нажатие.
Расширение системы видно прямо по коду: третий датчик — это ещё одна строка else if в режиме 2. Газовый MQ-2 из урока про сигнализацию на реле встанет туда же с порогом вместо фронта, а реле из урока про реле и 220 В добавит настоящую 12-вольтовую сирену или прожектор: в режиме 3 — digitalWrite(RELE, HIGH) одной строкой. Дальше — хоть журнал в EEPROM, хоть модуль SMS: система растёт по строке на дело, и каждое живёт по своему таймеру.
И про границы применимости без иллюзий: плата с герконом и шариком — уведомление, а не защита. Она сообщит о событии тому, кто смотрит в Serial или слышит зуммер; для SMS нужен GSM-модуль, для истории — EEPROM или SD-карта. Зато по электрике система аскетична: оба датчика пассивны, ток покоя складывается из подтяжек — доли миллиампера, и сигнализация живёт от повербанка неделю. Мой сценарий со шкафом в мастерской — именно такой: ценность не в обороне, а в том, что вой и журнал появляются раньше, чем я успею забыть, где что лежит.
Почему без прерываний
Оба датчика сидят на D2 и D3 — единственных пинах Uno с внешними прерываниями, и соблазн повесить attachInterrupt велик. Я сознательно остался на опросе, и вот почему. Прерывание окупается, когда loop() занят длинными делами и цифровые фронты теряются; здесь loop() оборачивается за десять миллисекунд, звон шарика 5-10 мс ловится заведомо, а геркон держит уровень и вовсе сколько угодно. Зато прерывания добавляют волатильные переменные, запрет delay в обработчике и отдельную отладку — всё это перечислено в уроке про attachInterrupt. Для автомата с пятью таймерами опрос проще, а простота в сигнализации ценнее хитрости: меньше веток — меньше шансов, что тревога потеряется.
Проверка в симуляторе
Макетка урока подключит пять компонентов сразу: датчик наклона на D2, геркон на D3, зуммер на D8, кнопку на D4 и светодиод на D12 — те же номера, что в скетче: симулятор раздаёт пины в порядке подключения компонентов, и порядок в comps подобран под скетч. В симе датчики — тумблеры: «наклонён» и «магнит рядом». На живой плате события короче — шарик щёлкает на доли секунды, но фронт успевает сработать: опрос каждые 10 мс против звонка 5-10 мс. Геркон честнее в обе стороны: пока тумблер в «магнит рядом», система считает дверь закрытой, и тревога по ней не поднимется.
Прогоните полный сценарий: постановка кнопкой, пять секунд отсчёта, охрана, тревога по геркону, сброс, снова постановка — и тревога уже по наклону. Строки rezhim= делают каждую смену режима видимой, а плоттер Serial может строить ступени прямо по переменной mode: снято нулём, охрана двойкой, тревога тройкой. Если сирена молчит при тревоге — проверьте, что тумблер не вернулся в «магнит рядом» раньше, чем вы заметили вой: в симе события фиксированные, на живой плате — мгновенные.
- Автомат из четырёх состояний на switch: снято, задержка на выход, на охране, тревога — режим это число в переменной
- Постановка кнопкой D4 с отсчётом 5000 мс: датчики слушаются только на охране, свои движения при постановке тревогу не поднимают
- Тревога — пила tone 400-1200 Гц по 40 Гц каждые 40 мс плюс LED-мерцание 150 мс: всё на millis(), кнопка сброса опрашивается во время воя
- Тревогу снимает только кнопка: закрытая дверь сама вой не глушит — событие обязан подтвердить человек
- Расширение — по строке на датчик в режиме 2: MQ-2 с порогом, реле с настоящей сиреной 12 В
- 1. Почему датчики опрашиваются только в режиме 2, но не в режиме 1?
- 2. Что сделает кнопка D4, нажатая во время режима 1 (идёт отсчёт)?
- 3. Сирена растёт по 40 Гц каждые 40 мс от 400 до 1200. Через сколько цикл вернётся к 400 Гц?
- 4. В тревоге дверь закрыли — геркон снова HIGH. Что произойдёт?
- 5. Зачем кнопке D4 гейт millis() - tBtn > 25, если нажатие уже ловится по фронту?
Частые вопросы по теме урока
- Почему датчики опрашиваются только в режиме 2, но не в режиме 1?
- Задержка на выход существует, чтобы вы вышли и закрыли дверь без воя. Слушание датчиков включается ровно в момент перехода на охрану. во время отсчёта вы сами ходите рядом: свои толчки поднимали бы ложную тревогу
- Что сделает кнопка D4, нажатая во время режима 1 (идёт отсчёт)?
- Нажатие в любом режиме, кроме «снято», переводит систему в снято: noTone, погашенный светодиод, строка в журнале. Постановка — только из режима 0. вернёт систему в «снято» — передумали
- Сирена растёт по 40 Гц каждые 40 мс от 400 до 1200. Через сколько цикл вернётся к 400 Гц?
- От 400 до 1200 — 800 Гц подъёма, по 40 Гц за шаг: двадцать шагов по 40 мс, полный цикл 800 мс. Именно поэтому вой слышится нарастающим. 800 мс: двадцать шагов по 40 мс
- В тревоге дверь закрыли — геркон снова HIGH. Что произойдёт?
- Сброс — только кнопкой. Иначе вор закрыл бы дверь за вас, и система утихомирилась сама. После сброса система встаёт в «снято», не обратно на охрану. ничего: тревогу снимает только кнопка
- Зачем кнопке D4 гейт millis() - tBtn > 25, если нажатие уже ловится по фронту?
- Дребезг тактовой кнопки растягивается на 10-20 мс: одно нажатие выглядит серией фронтов. Окно 25 мс глушит серию, но не мешает следующему честному нажатию. дребезг кнопки даёт несколько фронтов за 10-20 мс — гейт оставляет из них один