Счётная шторка из прошлого урока честно складывала плюс один в монитор, и этого хватало ровно до вечера, когда я захотел, чтобы она не считала, а охраняла. Считать и охранять — разные жанры: счётчику всё равно, что случилось дальше, а охране нужно залаять и не отступать, пока человек не подтвердит, что услышал. Так у шторки появились два режима, фильтр на сто миллисекунд против мух и пыли, кнопка сброса — и она перестала быть счётчиком.
По железу почти ничего нового: лазер на D12, фоторезистор на A0, светодиод-дублёр на D11. Новое — зуммер на D8 и кнопка на D2, и весь урок про автомат, который их ведёт. Каркас мы уже видели у светофора из урока про конечный автомат, а четырёхсостоятельную охрану с постановкой и задержкой на выход — в кластере про наклон и геркон: там тревогу давали контакты, здесь луч, и постановку с отсчётом я не повторяю — сосредоточимся на оптике.
Два режима и сто миллисекунд на входе
Режим охраны: луч горит, скетч молчит и следит. Режим тревоги: зуммер воет двумя тонами, светодиод мигает — и продолжается это до кнопки. Переход охрана → тревога не мгновенный: луч должен стоять перекрытым дольше 100 мс. Прикрыли на полсекунды — тревога. Дрогнуло на двадцать — таймер обнулился, тишина.
Зачем фильтр. В луч, идущий через комнату, попадает всё, что в ней летает: пылинка ловит долю миллисекунды, капля от дождя — единицы, дрожь кронштейна от хлопнувшей двери — десятки. Все эти всплески короче ста миллисекунд, и фильтр их молча съедает; немного помогает и собственная инерция фоторезистора — десятки миллисекунд на реакцию, — но полагаться только на неё нельзя, ставим и программную границу. Человек в дверном проёме держит луч перекрытым двести-пятьсот мс минимум — проходит. Честная граница применимости: муха, севшая отдохнуть прямо на приёмник, обманет любой тайминг — против неё работает трубка из прошлого урока, а не константа в скетче.
| Режим | Что происходит | Как из него выйти |
|---|---|---|
| охрана | луч горит, тишина; перекрытие дольше 100 мс | сам — переходом в тревогу |
| тревога | сирена 1500 и 2200 Гц попеременно, LED мигает | кнопка D2 — сброс в охрану |
| сброс | мгновенная тишина, светодиод гаснет | мы уже в охране |
const int LASER = 12;const int LDR = A0;const int BUZZER = 8;const int BTN = 2;const int LED = 11;const int POROG_ZAKR = 300;const int POROG_SVOB = 400;unsigned long FILTR = 100; // перекрытие дольше 100 мс = тревога int rezim = 0; // 0 — охрана, 1 — тревогаint luch = 0; // 0 — свободен, 1 — перекрытunsigned long tPerekryt = 0;unsigned long tSir = 0;unsigned long tLed = 0;int sirFreq = 1500;int ledSost = LOW;int prevBtn = HIGH; void setup() { pinMode(LASER, OUTPUT); digitalWrite(LASER, HIGH); pinMode(BUZZER, OUTPUT); pinMode(LED, OUTPUT); pinMode(BTN, INPUT_PULLUP); Serial.begin(9600); Serial.println("Perimetr: ohrana");} void loop() { // кнопка сброса — по фронту, опрашивается каждый проход int btn = digitalRead(BTN); if (btn == LOW && prevBtn == HIGH && rezim == 1) { rezim = 0; noTone(BUZZER); digitalWrite(LED, LOW); ledSost = LOW; Serial.println("sbros: ohrana"); } prevBtn = btn; // луч с гистерезисом, как у счётчика int uroven = analogRead(LDR); if (uroven < POROG_ZAKR) { luch = 1; } else if (uroven > POROG_SVOB) { luch = 0; } if (rezim == 0 && luch == 1) { if (tPerekryt == 0) { tPerekryt = millis(); } if (millis() - tPerekryt > FILTR) { rezim = 1; tSir = millis(); tLed = millis(); Serial.println("trevoga!"); } } if (rezim == 0 && luch == 0) { tPerekryt = 0; // луч свободен — таймер в ноль } if (rezim == 1) { if (millis() - tSir >= 180) { // два тона сирены попеременно tSir = millis(); if (sirFreq == 1500) { sirFreq = 2200; } else { sirFreq = 1500; } tone(BUZZER, sirFreq); } if (millis() - tLed >= 120) { // светодиод мигает в такт tLed = millis(); if (ledSost == LOW) { ledSost = HIGH; digitalWrite(LED, HIGH); } else { ledSost = LOW; digitalWrite(LED, LOW); } } } Serial.print("ldr="); Serial.print(uroven); Serial.print(" rezim="); Serial.println(rezim); delay(10);}Сирена, кнопка и залипание
Сирена из двух тонов, а не из пилы: каждые 180 мс частота прыгает между 1500 и 2200 Гц, и ухо слышит характерный двуголосый вой. Пилу от 400 до 1200 Гц мы собирали в звуковом шкафу — она выразительнее, но двухтональный переключатель проще: один millis-гейт и одна переменная частоты. Рвать частоту важно ещё и потому, что ровный тон ухо через минуту перестаёт слышать — изменение держит внимание лучше громкости. Всё это живёт без единого delay, по правилам урока про millis вместо delay: loop() оборачивается за десятки миллисекунд.
Кнопка сброса опрашивается каждый проход по фронту HIGH → LOW, как учит урок про дребезг: нажатие живёт 100-150 мс, цикл — 10 мс, промахнуться невозможно. Сброс делает три вещи: глушит зуммер noTone(), гасит светодиод и возвращает охрану. Заметьте: сброс срабатывает только в тревоге — в охране нажатия бездействуют, и это осознанное решение, лишняя кнопка в дежурке только путает.
Второе осознанное решение — тревога залипает. Луч может открыться через секунду после старта сирены, но охрана без кнопки не вернётся: защита обязана переживать момент прохода, иначе она ловит только сам момент. Единственный выход — кнопка, и на живой плате я вешаю её внутрь помещения: сброс должен быть доступен своим и недоступен тому, кто пришёл с улицы.
Прогоните в симуляторе все три пути автомата подряд: короткий всплеск — тишина, долгое перекрытие — вой, кнопка — сброс. Затем попробуйте обмануть систему: держите слайдер в мёртвой зоне 300-400 — сколько бы ни висело, тревога не стартует, потому что состояния «перекрыт» нет. На живой плате то же самое делает приоткрытая дверь в солнечный день: луч прикрыт наполовину, LDR видит полсотни единиц сверху порога, и охрана честно молчит, потому что формально нарушений нет.
На живой плате: прицеливание и жёсткость
Первая ошибка сборки — крепить лазер и приёмник по отдельности: каждое крепление гуляет само по себе, и точка уползает с приёмника за день. Я клею оба модуля на одну рейку двусторонним скотчем и ставлю рейку в проём целиком: геометрия луча теперь живёт в дереве, а не в трёх отдельных кусках пластилина.
Прицеливание по монитору: перекройте луч пальцем и смотрите на строки ldr= — двигайте модуль по миллиметру, пока свободный луч не даст устойчивые показания выше 400. Точка должна попадать в торец трубки приёмника, а не в линзу рядом. Признание в себе: моя первая шторка раз в неделю слепла от того, что кот спиной сдвигал пакетик с фоторезистором, — с тех пор приёмник живёт в жёсткой коробке, а не в прозрачном пакете.
День и ночь дают разные полы шкалы: солнце поднимает показания свободного луча, темнота опускает. Мой зазор 300-400 съедает квартирный перепад, но не уличный; если диапазон шире зазора, автоматизируйте калибровку — пусть скетч сам запоминает минимум и максимум за минуту и ставит пороги посередине. И последнее: у кода охранной системы на наклоне и герконе постановка и задержка на выход уже собраны — если периметр станет настоящим, переносится один в один.
- Автомат из двух режимов — охрана и тревога; вся логика переходов живёт в одном проходе loop() без delay
- Фильтр 100 мс: тревога только если луч перекрыт дольше ста миллисекунд — пыль, капли и дрожь крепления короче
- Сирена — переключение 1500 и 2200 Гц каждые 180 мс через millis; LED мигает своим гейтом в 120 мс
- Кнопка D2 сбрасывает тревогу по фронту и только в тревоге; сама тревога от луча не снимается — залипает намеренно
- После Reset автомат теряет состояние: для настоящего периметра флаг тревоги держат в EEPROM
- 1. Зачем на входе тревоги фильтр 100 мс?
- 2. Луч снова свободен через секунду после старта тревоги. Что будет без кнопки?
- 3. Как меняется частота сирены в этом скетче?
- 4. Кнопка D2 нажата в режиме охраны. Что произойдёт?
- 5. Питание пропало на минуту и вернулось. В каком режиме стартует скетч?
Частые вопросы по теме урока
- Зачем на входе тревоги фильтр 100 мс?
- Всплески короче 100 мс съедаются таймером; человек в проёме держит луч 200-500 мс и проходит. Против севшей на приёмник мухи фильтр бессилен — помогает трубка. короткие всплески — пыль, капли и дрожь крепления — не должны лаять как вторжение
- Луч снова свободен через секунду после старта тревоги. Что будет без кнопки?
- Тревога залипает намеренно: защита должна переживать момент прохода. Автоматический выход был бы дырой — прошёл, подождал и свободен. сирена продолжит: тревога снимается только кнопкой
- Как меняется частота сирены в этом скетче?
- Один таймер и одна переменная частоты: два тона по очереди. Изменение частоты держит внимание лучше ровного тона, к которому ухо привыкает. millis-гейт каждые 180 мс переключает 1500 и 2200 Гц
- Кнопка D2 нажата в режиме охраны. Что произойдёт?
- Сброс в дежурке бездействует — так задумано. Он глушит сирену, гасит светодиод и возвращает охрану, только когда охрана нарушена. ничего: сброс работает только в тревоге
- Питание пропало на минуту и вернулось. В каком режиме стартует скетч?
- ОЗУ теряется без питания, и автомат честно начинает с охраны. Если периметр настоящий, флаг тревоги держат в EEPROM и читают в setup(). в охране: состояние автомата жило в ОЗУ и умерло