Моя первая сигнализация на MQ-2 орала за ночь одиннадцать раз. Девять — зря: ни дыма, ни газа, просто значение балансировало на 397-402 и каждые несколько минут пересекало порог то одной, то другой стороной. Реле щёлкало, зуммер пищал, я вставал. К утру у меня была сигнализация, которой не верил даже я сам, и два часа разбирательств, из которых вырос этот урок. Первым делом я заглушил зуммер и стал смотреть в Serial — и увидел, что значение честно ходило вокруг порога, как лодка у буйка.
Проблема была не в датчике и не в пороге — порог как раз стоял разумный. Проблема была в сравнении «больше четырёхсот» и в ожидании стабильности, которой у аналогового сигнала нет. Вокруг порога значение ходит на десяток-другой единиц в обе стороны — это нормальный шум АЦП и сенсора, и любое одиночное сравнение превращает этот шум в серию ложных тревог. Шум — не дефект: у сенсора свой дрейф, у АЦП своя единица младшего разряда, у кухни свои сквозняки.
Разберём, как собрать сигнализацию, которая не будит владельца попусту: компаратор с гистерезисом, зуммер и мигание по millis, реле вентиляции и кнопка сброса звука — всё в одном скетче, который потом переносится в гараж или щиток без переделки логики. Все элементы уже встречались по отдельности — здесь они впервые собираются в один контур, где у каждого события есть начало и конец.
Порог — это if, сигнализация — это состояние
Минимальный компаратор умещается в одну строку: if (analogRead(A0) > 400) — тревога. Числа привязаны к покою: если на чистом воздухе сенсор живёт на 100-200, то порог 400 — двукратный запас от фона, и почти всё, что выше, — реальное событие, а не флуктуация. Заводской DOUT с подстроечником делает ровно то же самое железом, только порог задаётся винтом и нигде не виден в числах. Для макетки это значит: порог, подобранный винтом, никто и никогда не покажет вам числом — он живёт только в железе.
Одиночное сравнение дребезжит. Значение около порога ходит на 10-20 единиц в обе стороны, и каждая ходка через границу — новый щелчок реле и новый писк. Лечение — гистерезис: вход в тревогу на 400, выход на 300. Зазор в сотню единиц шире естественного шума, поэтому на границе система держит предыдущее состояние: было тревожно — тревога продолжается, было тихо — тишина продолжается. Проверка в симуляторе занимает полминуты: пошуршите слайдером у границы и посмотрите на alm= — состояние должно быть инертным, а не дёргаться за каждым щелчком.
Так устроены все пороговые системы: термостат с гистерезисом не щёлкает реле на границе температуры по той же причине, а урок про реле и гистерезис разбирает приём отдельно. Формула всегда одна: два порога вместо одного, состояние — переменная, вход по верхнему, выход по нижнему.
| Уровень mq | Состояние | Что делает скетч |
|---|---|---|
| 100-200 | покой | всё выключено, печатаем mq= для контроля |
| 300-400 | зона гистерезиса | состояние не меняется — держим прошлое решение |
| выше 400 | тревога | зуммер пищит, светодиод мигает, реле вентиляции замкнуто |
| ниже 300 после тревоги | выход | звук и мигание гаснут, реле размыкается |
// Сигнализация на MQ-2: гистерезис 400/300, зуммер, светодиод, реле, кнопка const int PIN_GAS = A0;const int PIN_RELE = 7; // реле вентиляции или клапанаconst int PIN_LED = 12; // красный светодиодconst int PIN_ZUM = 8; // зуммерconst int PIN_KNOPKA = 2; // кнопка сброса звука const int POROG_VHOD = 400; // тревогаconst int POROG_VYHOD = 300; // снятие тревоги int alarm = 0; // состояние: 0 — тихо, 1 — тревогаint mute = 0; // звук заглушен кнопкойint svetit = 0; // фаза миганияunsigned long tFaza = 0; // отметка времени для фаз void setup() { Serial.begin(9600); pinMode(PIN_RELE, OUTPUT); pinMode(PIN_LED, OUTPUT); pinMode(PIN_ZUM, OUTPUT); pinMode(PIN_KNOPKA, INPUT_PULLUP); digitalWrite(PIN_RELE, LOW); Serial.println("Porog 400, sniatie 300");} void loop() { int mq = analogRead(PIN_GAS); if (alarm == 0 && mq > POROG_VHOD) { // вход в тревогу — только верхний порог alarm = 1; mute = 0; // новая тревога звучит заново digitalWrite(PIN_RELE, HIGH); // вентиляция пошла } if (alarm == 1 && mq < POROG_VYHOD) { // выход — только нижний порог alarm = 0; digitalWrite(PIN_RELE, LOW); digitalWrite(PIN_LED, LOW); noTone(PIN_ZUM); } if (digitalRead(PIN_KNOPKA) == LOW) { // кнопка глушит звук mute = 1; } if (alarm == 1 && millis() - tFaza >= 250) { // фаза 250 мс по millis tFaza = millis(); svetit = 1 - svetit; digitalWrite(PIN_LED, svetit); if (svetit == 1 && mute == 0) { tone(PIN_ZUM, 2000); } else { noTone(PIN_ZUM); } } Serial.print("mq="); Serial.print(mq); Serial.print(" alm="); Serial.println(alarm); delay(50);}Мигание и писк построены на millis: каждые 250 мс скетч переключает фазу — светодиод то горит, то гаснет, зуммер то 2000 Гц, то тишина. Внутри фазы loop не блокируется: скетч успевает читать датчик и кнопку десятки раз за одно «мигание». Задвинь внутрь тревоги delay(250) — и на четверть секунды скетч ослепнет ровно тогда, когда важнее всего увидеть спад газа; урок про millis вместо delay разбирает этот приём по косточкам. Фаза 250 мс даёт четыре переключения в секунду — глаз читает это как тревожное мигание, зуммер — как знакомый по фильмам «пик-пик».
Кнопка на D2 — сброс звука, и это осознанный компромисс. Нажали — зуммер замолчал, но светодиод продолжает мигать, а реле продолжает гонять вентиляцию, пока газ не упадёт ниже 300. Тишина не равна «всё хорошо»: она равна «владелец предупреждён и устал слушать». Для простого mute дребезг контактов не страшен, но если кнопке дать больше работы — ей нужен антидребезг из урока про debounce. Удерживать кнопку долго здесь не нужно: повторное нажатие ничего не меняет, пока газ не спал.
Реле на D7 — вентиляция или электромагнитный клапан на трубе. Катушка ест десятки миллиампер, поэтому только через модуль реле с транзистором и оптроном, ни в коем случае не напрямую в пин. И проверьте логику своего модуля: многие срабатывают от LOW, тогда HIGH и LOW в скетче меняются местами — тест без нагрузки покажет это за минуту. Автопроветривание по такому же порогу уже собрано в уроке про вытяжку: там вентиляция ловит влажность, у нас дым — логика одна. И выбирайте блок питания с запасом вдвое: катушка реле и пусковой ток вентилятора — разные нагрузки, но сходятся в одном проводе.
Макетка в симуляторе
Сенсор вешают не куда удобно, а куда ползёт газ. Пропан и бутан тяжелее воздуха: они стекают к полу, и датчик в гараже с газовым баллоном ставят в полуметре от земли. Метан бытового газа легче воздуха — он поднимается к потолку, дым тоже. Одна и та же сигнализация с одинаковым скетчем меняет высоту установки в зависимости от того, какой газ она ловит: я однажды неделю не мог понять, почему потолочный датчик молчал на пропан из горелки, а пол, пересаженный туда же, — срабатывал.
Второй практический момент — вентиляция. Сенсор в потоке от форточки показывает пол комнаты вместо кухни: сквозняк разбавляет газ, и порог видит чистый воздух там, где у плиты уже душно. Ставьте модуль в полуметре-метре от окна и вентиляционной решётки, и пусть зона контроля совпадает с зоной, где газ может скопиться — для пропана это низ, для метана и дыма верх.
Порог 400/300 — не универсальная константа, а привязка к моему экземпляру с покоем около 140. У соседней платы с того же лотка покой 210 — и её порог 400 по отношению к фону уже не двукратный, а полуторакратный. Чужие числа переносятся только как отправная точка: свой порог считается от своего покоя и проверяется спиртовой ваткой — если фон у вас другой, делайте то же самое с вашими числами, а не с моими. Как снять свой фон и почему пороги из чужих скетчей подводят — предмет следующего урока про плоттер и калибровку MQ-2.
Отдельная история — тревога как событие с уровнями: многоступенчатая реакция с плавными переходами от «внимание» к «паника» разобрана в уроке про зуммер и пороги. Для газа мне хватает двух состояний, но если сигнализация живёт в жилой комнате, двухтоновая мелодия вместо писка — приятная опция: писк 2000 Гц слышен через две стены, а через закрытую дверь лучше звонят 2500-3000 Гц.
После тревоги хочется истории: когда началась, сколько длилась, сколько раз срабатывала за ночь. Минимум — метка времени в Serial: секунды с начала работы уже достаточно для разбора «что это было в три ночи». Максимум — журнал в кольцевом буфере и пороги, которые правятся без перепрошивки: как хранить настройки в энергонезависимой памяти — урок про пороги в EEPROM, а заодно он же подскажет, как хранить время последней тревоги.
- 1. Почему простое сравнение if (mq > 400) без гистерезиса даёт дребезг тревоги?
- 2. Тревога включается на 400. На каком значении скетч с гистерезисом её снимет?
- 3. Почему мигание светодиода в тревоге сделано по millis, а не через delay(250)?
- 4. Что делает кнопка сброса при всё ещё высоком уровне газа?
- 5. Почему катушку реле не подключают напрямую к пину?
- Два порога вместо одного: вход в тревогу на 400, выход на 300 — зазор шире шума, дребезга нет.
- Состояние — переменная alarm: вход по верхнему порогу, выход по нижнему, зона между ними держит прошлое решение.
- Мигание и писк — по millis с фазой 250 мс; delay() внутри тревоги слепит скетч.
- Кнопка сброса глушит только звук; реле и индикация живут до реального спада ниже 300.
- Реле — только через модуль с транзистором; у многих модулей срабатывание от LOW — проверьте логику входа.
Частые вопросы по теме урока
- Почему простое сравнение if (mq > 400) без гистерезиса даёт дребезг тревоги?
- Аналоговый сигнал шумит на 10-20 единиц вокруг среднего. Одиночный порог превращает каждую ходку через границу в новое событие — нужны два порога и состояние. значение вокруг порога ходит на десятки единиц, и каждое пересечение — новое срабатывание
- Тревога включается на 400. На каком значении скетч с гистерезисом её снимет?
- Выход из тревоги всегда по нижнему порогу: 400 на вход, 300 на выход. Зазор шире шума — на границе состояние не меняется. на 300 — ниже порога входа
- Почему мигание светодиода в тревоге сделано по millis, а не через delay(250)?
- delay() замораживает весь цикл: во время паузы не читаются ни датчик, ни кнопка. Мигание по millis оставляет loop живым. delay() блокирует loop — на четверть секунды скетч слепнет и не видит спад газа и кнопку
- Что делает кнопка сброса при всё ещё высоком уровне газа?
- Сброс — это только тишина. Индикация и вентиляция продолжаются, пока сенсор не увидит спад ниже 300: кнопка глушит звук, а не решение. глушит зуммер, но мигание и реле живут до спада ниже порога снятия
- Почему катушку реле не подключают напрямую к пину?
- Пин Uno отдаёт до 40 мА, катушке реле нужно 70-100 мА — прямой запуск убивает порт. Правильный путь — модуль реле: в нём уже есть транзистор, диод и оптрон. катушка ест десятки миллиампер — нужен модуль с транзистором и оптроном