Как сделать таймер на arduino

Урок 39. Реле времени: управление устройствами по таймеру

b3af4ae96a80e2cad8c6194d3a457117

В этом уроке мы создадим четырёхканальное реле времени. К данному устройству можно подключить до 4 приборов (лампочки, светодиодные ленты, моторы, обогреватели, вентиляторы и т.д.), каждое из которых будет включаться на заданные для него промежутки времени суток и в заданные дни недели.

В реле времени имеется 20 таймеров (их количество можно уменьшить или увеличить до 128, указав нужное число в строке 16 скетча). Один таймер включает только одно устройство (канал) на заданный промежуток времени, не влияя на работу остальных устройств (каналов). Каждому устройству (каналу) можно назначить несколько таймеров, следовательно, включать и выключать каждое из устройств можно несколько раз в сутки и на разную мощность. При отключении питания, таймеры реле не сбиваются, так как их настройки хранятся в энергонезависимой памяти Arduino. Текущее время также не сбивается, так как оно берётся из модуля часов реального времени, который снабжен батарейкой.

Реле времени можно использовать для включения освещения по времени в доме, квартире, на даче, на производстве и т.д. Можно использовать для включения по времени вентиляции, котлов, обогревателей, полива газонов, систем очистки дачных бассейнов и т.д. Еще одним преимуществом реле времени является создание эффекта присутствия, например, Вас нет дома, но свет утром и вечером включается, а днём и ночью выключается, утром включается радио или телевизор, а ночью включается ночник. Это может заставить задуматься нежелательных «гостей», что дом обитаем и делать там нечего.

Нам понадобится:

Для реализации проекта нам необходимо установить библиотеки:

Видео:

Схема подключения:

Trema модуль RTC и дисплей LCD1602 I2C подключаются к аппаратной шине I2C через Trema I2C HUB, а Trema энкодер можно подключать к любым (цифровым или аналоговым) выводам Arduino, их номера указываются в скетче (в примере использованы выводы D4, D7 и D8). Для удобства подключения используется Trema Shield.

7cd048e22a7c4a548f3a7d3132ee9e1d

Приборы подключаются к каналам 1-4:

Алгоритм работы:

Режим просмотра времени: При включении питания на индикаторе отображается текущее время, дата и день недели. Номера включённых каналов отображаются в правом верхнем углу дисплея.

Меню: Для входа в меню нужно нажать на энкодер. Далее поворачивая экодер вправо или влево можно выбрать разделы «ТАЙМЕРЫ», «ЧАСЫ», «ВЫХОД», для входа в требуемый раздел нужно опять нажать на энкодер.

Меню>часы: В данном разделе меню, поворачивая энкодер вправо или влево, можно выбрать разделы «ВРЕМЯ», «ДАТА», «ВЫХОД», для входа в требуемый раздел нужно нажать на энкодер.

Меню>часы>время: Этот раздел меню предназначен для установки текущего времени. Устанавливаемый в данный момент параметр времени (часы, минуты, секунды) должен мигать. Выбор значения осуществляется поворотом энкодера, а переход к следующему значению, нажатием на энкодер.

Меню>часы>дата: Этот раздел меню предназначен для установки текущей даты и дня недели. Устанавливаемый в данный момент параметр даты (день, месяц, год, день недели) должен мигать. Выбор значения осуществляется поворотом энкодера, а переход к следующему значению, нажатием на энкодер.

Меню>таймеры: В данном разделе меню, поворачивая энкодер вправо или влево, можно выбрать один из установленных таймеров (для их редактирования) или разделы «НОВЫЙ ТАЙМЕР», «СТЕРЕТЬ ВСЕ ТАЙМЕРЫ», «ВЫХОД», для входа в требуемый раздел нужно нажать на энкодер. Установленные таймеры отображаются в виде строки из времени их старта/сброса и номера канала «00:00-00:00-0».

Меню>таймеры>новый таймер: Выбор данного раздела приведёт к созданию нового таймера, на экране отобразится надпись «НОВЫЙ ТАЙМЕР СОЗДАН» после чего Вам будет предложено ввести время старта/сброса и указать номер канала (который будет включаться данным таймером). Данный раздел меню недоступен если установлены все таймеры.

Меню>таймеры>стереть все таймеры: Выбор данного раздела приведёт к удалению всех таймеров, на экране отобразится надпись «ВСЕ ТАЙМЕРЫ УДАЛЕНЫ». Данный раздел меню недоступен если нет ни одного установленного таймера.

Меню>таймеры>00:00-00:00-0: Вместо «00:00-00:00-0» будет строка из времени старта/сброса таймера и номера канала которым он управляет. Данный раздел меню предназначен для редактирования выбранного таймера, поворачивая энкодер вправо или влево, можно выбрать разделы «ВРЕМЯ И КАНАЛ», «ПОВТОРЫ», «УРОВЕНЬ СИГНАЛА», «СТЕРЕТЬ ТАЙМЕР», «ВЫХОД», для входа в требуемый раздел нужно нажать на энкодер.

Меню>таймеры>00:00-00:00-0>время и канал: Этот раздел меню предназначен для установки (редактирования) времени старта/сброса таймера и номера канала которым он управляет. Устанавливаемый в данный момент параметр (час старта, минута старта, час сброса, минута сброса, номер канала) должен мигать. Выбор значения осуществляется поворотом энкодера, а переход к следующему значению, нажатием на энкодер.

Меню>таймеры>00:00-00:00-0>повторы: Этот раздел меню предназначен для установки (редактирования) повторов таймера по дням недели, в которые он должен срабатывать. Под устанавливаемым в данный момент параметром (ПН, ВТ, СР, ЧТ, ПТ, СБ, ВС) должен мигать курсор. Поворот энкодера устанавливает или сбрасывает стрелочку под устанавливаемым параметром, если она установлена значит в этот день недели таймер будет срабатывать, иначе он срабатывать не будет. Переход к следующему дню недели осуществляется нажатием на энкодер.

Меню>таймеры>00:00-00:00-0>уровень сигнала: Этот раздел меню предназначен для установки (редактирования) уровня сигнала на выбранном канале при срабатывании таймера. Выбор уровня сигнала от 5% до 100% осуществляется поворотом энкодера с шагом 5%, а нажатие на энкодер приведёт к выходу из данного раздела.

Меню>таймеры>00:00-00:00-0>стереть таймер: Выбор данного раздела приведёт к удалению выбранного таймера, на экране отобразится надпись «ТАЙМЕР УДАЛЕН».

Примеры:

Создание таймера который по будням, между 18:00 и 20:00, будет включать 4 канал с уровнем сигнала 100%:

Теперь на экране отображается текущее время, дата и день недели, а по будням, с 18:00 до 20:00 в правом верхнем углу экрана будет отображаться цифра 4, при этом на 4 канале будет установлен уровень логической «1» (сигнал ШИМ со 100% заполнением). На остальных каналах будет уровень логического «0».

Создание таймера который между 19:00 и 21:00 каждого дня, будет включать 3 канал с уровнем сигнала 50%:

После того как Вы установите два таймера (из 1 и 2 примера): По будням с 18:00 до 19:00 в правом верхнем углу экрана будет отображаться цифра 4, при этом на 4 канале будет установлен уровень логической 1 (сигнал ШИМ со 100% заполнением). По будням с 19:00 до 20:00 в правом верхнем углу экрана будет отображаться цифра 3 и 4, при этом на 3 канале будет установлен сигнал ШИМ с 50% заполнением, а на 4 канале будет установлен уровень логической «1» (сигнал ШИМ со 100% заполнением). По будням с 20:00 до 21:00 и в выходные с 19:00 до 21:00, в правом верхнем углу экрана будет отображаться цифра 3, при этом на 3 канале будет установлен сигнал ШИМ с 50% заполнением.

Примечание:

Включение и выключение устройств осуществляется по установленным таймерам только в режиме просмотра времени. Это сделано для того, чтобы устройства «случайно» не включились во время редактирования текущей даты, времени или таймера.

Код программы:

Библиотека iarduino_Encoder_tmr использует второй аппаратный таймер, НЕ ВЫВОДИТЕ СИГНАЛЫ ШИМ НА 3 ИЛИ 11 ВЫВОД!

Источник

ОБОРУДОВАНИЕ
ТЕХНОЛОГИИ
РАЗРАБОТКИ

Блог технической поддержки моих разработок

Урок 11. Программные таймеры в Ардуино. Циклы с различными временами периода от одного таймера.

ArduinoUnoR3 9551

Научимся создавать программные таймеры в параллельных процессах. Напишем учебную программу для охранной сигнализации.

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

Как создавать программные таймеры для таких циклов. В принципе, также как и аппаратные.

Вот пример программы с тремя программными таймерами на 10, 200 и 1000 мс.

#define CYCLE_1_TIME 5 // время цикла 1 ( * 2 = 10 мс)
#define CYCLE_2_TIME 100 // время цикла 2 ( * 2 = 200 мс)
#define CYCLE_3_TIME 500 // время цикла 3 ( * 2 = 1 сек)

byte timerCount1; // счетчик таймера 1
byte timerCount2; // счетчик таймера 2
boolean flagTimer2; // признак программного таймера 2
unsigned int timerCount3; // счетчик таймера 3

// программный таймер 1, период 10 мс
// счетчик таймера контролируется в асинхронном цикле
if ( timerCount1 >= CYCLE_1_TIME ) <
timerCount1= 0;
// код программы вызывается каждые 10 мс
>

// программный таймер 2, период 200 мс
// счетчик таймера контролируется в обработчике прерывания
if ( flagTimer2 == true ) <
flagTimer2= false;
// код программы вызывается каждые 200 мс
>

// обработчик прерывания 2 мс
void timerInterupt() <

timerCount1++; // + 1 к счетчику таймера 1

timerCount2++; // + 1 к счетчику таймера 2
if ( timerCount2 >= CYCLE_2_TIME ) <
timerCount2= 0; // сброс счетчика
flagTimer2= true; // установка флага таймера 2
>

// программный таймер 3, период 1000 мс
// счетчик таймера контролируется в обработчике прерывания
// выполняется также в цикле прерывания
timerCount3++; // + 1 к счетчику таймера 3
if ( timerCount3 >= CYCLE_3_TIME ) <
timerCount3= 0; // сброс счетчика
// код программы вызывается каждые 1000 мс
>
>

Для всех трех таймеров созданы свои счетчики. Ко всем счетчикам в обработчике прерывания каждые 2 мс прибавляется 1.

Счетчик таймера 1 проверяется в асинхронном цикле loop(). Когда его код достигает значения равного константе CYCLE_1_TIME, он сбрасывается и выполняется код программы для этого цикла.

Счетчик второго таймера проверяется в обработчике прерывания. При достижении кода сброса он сбрасывается и вырабатывается признак flagTimer2. Этот признак анализируется в асинхронном цикле и, при его активном состоянии выполняется код для цикла 2 с периодом 200 мс.

Разница у этих способов заключается в том, что при контроле счетчика в асинхронном цикле его состояние должно проверятся не реже времени периода прерывания. Если, к примеру, в асинхронном цикле программа задержится на 10 мс, то за это время счетчик timerCount1 насчитает лишние 5 единиц и будет сброшен при большем значении. Таким образом, время цикла нарушится. Во втором случае счетчик таймера 2 будет сброшен в обработчике прерывания вовремя. При задержке в асинхронном цикле задержится реакция на программный таймер 2, но период самого цикла не нарушится.

Счетчик третьего таймера контролируется в обработчике прерывания и там же выполняется код программы для этого таймера.

Могут быть разные варианты реализации циклов с разными периодами. Бывают еще программные таймеры синхронизированные друг от друга или от общего таймера. Какой способ применять надо решать в конкретной задаче.

Программа для упрощенной охранной сигнализации.

Давайте напишем реальную программу для охранной сигнализации. Это упрощенный вариант моей разработки на PIC контроллере. В будущем мы доведем эту сигнализацию до полного аналога этой разработки на Ардуино, а затем усложним, добавив GSM управление и оповещение, увеличим число датчиков, исполнительных устройств.

К плате Ардуино подключены следующие компоненты сигнализации:

Схема подключения этих элементов к плате Ардуино.

Lesson11 Scheme

У меня макет устройства выглядит так.

Lesson11 Pict

Алгоритм работы устройства.

Не знаю, насколько такая сигнализация может быть использована на практике, но для учебной программы вполне функциональная задача.

Последовательность разработки программы.

В программе много комментариев, все должно быть понятно. Я больше внимания уделю логической последовательности разработки программы.

// упрощенная охранная сигнализация

#define DOOR_SENS_PIN 12 // датчик двери подключен к выводу 12
#define SECRET_BUTTON_PIN 11 // скрытая кнопка подключена к выводу 11
#define LED_PIN 10 // светодиод подключен к выводу 10
#define SIREN_PIN 9 // сирена подключена к выводу 9

Button doorSens(DOOR_SENS_PIN, 50); // создание объекта датчик двери, типа кнопка
Button secretButton(SECRET_BUTTON_PIN, 25); // создание объекта скрытая кнопка, типа кнопка

void setup() <
pinMode(LED_PIN, OUTPUT); // определяем вывод светодиода как выход
pinMode(SIREN_PIN, OUTPUT); // определяем вывод сирены как выход
MsTimer2::set(2, timerInterupt); // задаем период прерывания по таймеру 2 мс
MsTimer2::start(); // разрешаем прерывание по таймеру
>

// обработчик прерывания 2 мс
void timerInterupt() <

doorSens.filterAvarage(); // вызов метода фильтрации сигнала для датчика двери
secretButton.filterAvarage(); // вызов метода фильтрации сигнала для скрытой кнопки

Теперь у нас в программе есть все компоненты системы. На этом этапе можно проверить аппаратную часть устройства. Для этого надо временно сделать в цикле loop() простую логическую связь между кнопками, светодиодом и сиреной. И убедится, что на открытие двери или нажатие кнопки реагирует светодиод. Мы так делали в предыдущих уроках.

Если не проверяем аппаратную часть, то, по крайней мере, компилируем программу для проверки на формальные ошибки.

Сирена у нас это пьезоэлектрический излучатель. Для того, чтобы он издавал звук тревоги надо сформировать на нем сигнал переменной формы. Сделаем программный блок для формирования этого сигнала. Будем просто инвертировать состояние соответствующего вывода в самом быстром цикле – обработчике прерывания. Для включения и выключения сирены создадим признак.

boolean sirenOn; // признак включения сирены

В обработчике прерывания напишем:

Для проверки можно вставить в loop()

sirenOn= secretButton.flagPress; // проверка сирены

При нажатии скрытой кнопки сирена (пьезоизлучатель) будет издавать тревожный сигнал. Не забудьте удалить после проверки.

Структура программы.

Задумываемся о структуре и режимах программы. Самая простая и логичная структура следующая. В асинхронном цикле выделены блоки – режимы. В каждом блоке программа работает по циклу, а на другие блоки (режимы) переходит при определенных условиях с помощью оператора goto. Логически можно выделить следующие режимы работы устройства:

Давайте создадим такие программные блоки, каждый с бесконечным циклом и меткой вначале для перехода.

Считается, что применение оператора goto надо ограничивать. Но в случаях, когда этот оператор упрощает понимание логики программы, облегчает ее читаемость, применение goto признано оправданным. У нас как раз тот случай. Ведь по логике может быть переход в любой блок из любого. Операторами break, continue и т.п. такие переходы не обеспечить.

Заполняем кодом каждый блок-режим.

Для блока ОТКЛЮЧЕНА:

digitalWrite(LED_PIN, LOW); // светодиод не горит
sirenOn= false; // сирена не звучит

// если нажали кнопку, переход на режим ОХРАНА
if ( secretButton.flagClick == true ) <
secretButton.flagClick= false;
goto guard_on;
>
>

Можно попробовать загрузить в плату. Светодиод не светится, сирена не звучит.

#define TIME_LED_PERIOD 500 // время периода мигания светодиода (* 2 мс)
#define TIME_LED_ON 100 // время включенного светодиода

unsigned int ledTimeCount; // счетчик времени для светодиода

sirenOn= false; // сирена не звучит
alarmTimeCount= 0; // сброс счетчика времени тревоги

// светодиод мигает раз в секунду
if ( ledTimeCount >= TIME_LED_PERIOD ) ledTimeCount= 0;
if ( ledTimeCount
else digitalWrite(LED_PIN, LOW);

// если нажали кнопку, переход на режим ОТКЛЮЧЕНА
if ( secretButton.flagClick == true ) <
secretButton.flagClick= false;
goto guard_off;
>

// если сработал датчик двери, переход на режим ТРЕВОГА
if ( doorSens.flagPress == true ) goto alarm;

>

ledTimeCount++; // счетчик времени мигания светодиода
>

Загружаем в плату Ардуино. Проверяем, что нажатие кнопки переводит устройство в режим ОХРАНА (мигает светодиод). Следующее нажатие кнопки возвращает в режим ОТКЛЮЧЕНА (светодиод погашен).

Остается заполнить блок режима ТРЕВОГА:

Все. Программа готова. Итоговый скетч:

// упрощенная охранная сигнализация

#define DOOR_SENS_PIN 12 // датчик двери подключен к выводу 12
#define SECRET_BUTTON_PIN 11 // скрытая кнопка подключена к выводу 11
#define LED_PIN 10 // светодиод подключен к выводу 10
#define SIREN_PIN 9 // сирена подключена к выводу 9

#define TIME_LED_PERIOD 500 // время периода мигания светодиода (* 2 мс)
#define TIME_LED_ON 100 // время включенного светодиода
#define TIME_LED_ALARM 62 // время периода мигания светодиода при ТРЕВОГЕ (* 2 мс)
#define TIME_ALARM 15000 // время в режиме ТРЕВОГА (* 2 мс)

Button doorSens(DOOR_SENS_PIN, 50); // создание объекта датчик двери, типа кнопка
Button secretButton(SECRET_BUTTON_PIN, 25); // создание объекта скрытая кнопка, типа кнопка

boolean sirenOn; // признак включения сирены
unsigned int ledTimeCount; // счетчик времени для светодиода
unsigned int alarmTimeCount; // счетчик времени тревоги

void setup() <
pinMode(LED_PIN, OUTPUT); // определяем вывод светодиода как выход
pinMode(SIREN_PIN, OUTPUT); // определяем вывод сирены как выход
MsTimer2::set(2, timerInterupt); // задаем период прерывания по таймеру 2 мс
MsTimer2::start(); // разрешаем прерывание по таймеру
>

digitalWrite(LED_PIN, LOW); // светодиод не горит
sirenOn= false; // сирена не звучит

// если нажали кнопку, переход на режим ОХРАНА
if ( secretButton.flagClick == true ) <
secretButton.flagClick= false;
goto guard_on;
>
>

sirenOn= false; // сирена не звучит
alarmTimeCount= 0; // сброс счетчика времени тревоги

// светодиод мигает раз в секунду
if ( ledTimeCount >= TIME_LED_PERIOD ) ledTimeCount= 0;
if ( ledTimeCount
else digitalWrite(LED_PIN, LOW);

// если нажали кнопку, переход на режим ОТКЛЮЧЕНА
if ( secretButton.flagClick == true ) <
secretButton.flagClick= false;
goto guard_off;
>

// если сработал датчик двери, переход на режим ТРЕВОГА
if ( doorSens.flagPress == true ) goto alarm;

>

sirenOn= true; // звучит сирена

// если нажали кнопку, переход на режим ОТКЛЮЧЕНА
if ( secretButton.flagClick == true ) <
secretButton.flagClick= false;
goto guard_off;
>

// проверка времени тревоги ( 30 сек )
if ( alarmTimeCount >= TIME_ALARM ) goto guard_off;

>
>

doorSens.filterAvarage(); // вызов метода фильтрации сигнала для датчика двери
secretButton.filterAvarage(); // вызов метода фильтрации сигнала для скрытой кнопки

ledTimeCount++; // счетчик времени мигания светодиода
alarmTimeCount++; // счетчик времени тревоги

>

Зарегистрируйтесь и оплатите. Всего 40 руб. в месяц за доступ ко всем ресурсам сайта!

Загружаем в плату, проверяем. Открывание двери имитируем нажатием первой кнопки. Все работает.

Чтобы устройство могло работать в реальных условиях, желательно доработать электрическую схему, как это сделано по этой ссылке. Но лучше подождите, через несколько уроков напишем программу, полностью выполняющую функции прототипа. А позже добавим GSM оповещение и телемеханику.

Следующий урок будет посвящен передаче данных по последовательному интерфейсу UART и отладке программ на Ардуино.

Coin

Автор публикации

Эдуард

62 комментария на « Урок 11. Программные таймеры в Ардуино. Циклы с различными временами периода от одного таймера. »

Эдуард, приветствую,
Я сделал «учебную» сигнализацию по материалам вашего урока. Но т.к. я стараюсь не просто повторять код методом вставки, а писать самостоятельно, как запомнил задачу, а потом сверяться с вашей реализацией и проправлять свою, то окончательному результату предшестувует отладка. И здесь во время этой отладки я налетел на неприятный глюк. Я начал с того, что попытался, используя ранее написанный класс «кнопка» сделать два блока-режима «online» и «offline», где, условно, если девайс в онлайне, то светодиод горит, а если в оффлайне — не горит. Далее решил усложнять по мере отладки.
Ранее написанная программа включения-выключения светодиода с классом-библиотекой работала прекрасно, а вот после разбиения на функциональные модули с «вечными» циклами в каждом, конструкция работать прекратила. Блоки выглядели так:
//————begin code-fragment———
offline:
while(true) <
if (button1.flagClick == HIGH) <
ledState=HIGH;
digitalWrite(LED_PIN,ledState);
button1.flagClick=LOW;
goto online;
>
>

online:
while(true) <
if (button1.flagClick == HIGH) <
ledState=LOW;
digitalWrite(LED_PIN,ledState);
button1.flagClick=LOW;
goto offline;
>
>
//—————end code-fragment————
Обработчик прерывания по таймеру исправно отрабатывал, флаг нажатия выставлялся, но почему-то не отлавливался в основной программе.
Я грешил на код основной программы, затем на класс, также хоть и по вашей указке, но написанный самостоятельно. После этого скачал вашу библиотеку, попробовал с ней — то же самое. После этого скачал и запустил ваш код сигнализации, который заработал и с моим и с вашим классом. В итоге выяснилось, что если вставить перед проверкой флага нажатия хотя бы один оператор, неважно какой — я пробовал delay, Serial.print или прописывать дополнительно состояние светодиода, то и мой код тоже начинает работать. Т.е. вот версия, которая работает:
//—————begin code-fragment———————-

offline:
while(true) <
delay(1); //если вместо этого поставить строку ниже, а эту убрать, то тоже будет работать
//digitalWrite(LED_PIN,LOW);
if (button1.flagClick == HIGH) <
ledState=HIGH;
digitalWrite(LED_PIN,ledState);
button1.flagClick=LOW;
goto online;
>
>

online:
while(true) <
delay(1);
//digitalWrite(LED_PIN,HIGH);
if (button1.flagClick == HIGH) <
ledState=LOW;
digitalWrite(LED_PIN,ledState);
button1.flagClick=LOW;
goto offline;
>
>

В коде сигнализации до проверки условия нажатия тоже присутствуют какие-то действия, поэтому там такой проблемы, как была у меня изначально — нет.
Я попробовал воспроизвести эту ситуацию на двух версиях плат — Micro и Mega2560. Поведение было одинаковым. Вы случайно ни на что подобное не наступали? Как можно отловить причину такого поведения?

Прошу прощения за много букв, если сочтете лишним, не публикуйте этот коментарий, а ответьте лично в почту.

Денис, здравствуйте!
Я столкнулся с подобным эффектом, который объяснить не смог.
В прерывании по таймеру у меня взводился флаг, а в бесконечном цикле while() он анализировался. Программа никак на него не реагировала. Я убрал все классы, оставил самые примитивные операции, но результат остался прежним.
Далее я выяснил, что программа правильно реагировала на признак из обработчика прерывания только при выполнении цикла loop() до конца. Очевидно, что при каждом цикле loop происходит какая-то перегрузка переменных или что-то подобное. Интересно, что программ правильно работала с бесконечным циклом while() если в нем присутствовала любая системная функция, например digitalWrite(), delay(), да любая. Даже на разрешение прерывания interrupts(), проще и короче функции нет. Скорее всего, при вызове системных функций тоже происходит перегрузка переменных.
Советую поступить так. В принципе, использовать циклы wile() для ожидания не хорошо. Я применил их в одном из начальных уроков, чтобы было проще объяснять алгоритм программы. В реальных программах я так никогда не делаю. Правильнее структуру программы строить по принципу из урока 17.
if ( mode == 0 ) <
// СИГНАЛИЗАЦИЯ ОТКЛЮЧЕНА
>
else if ( mode == 1 ) <
// УСТАНОВКА НА СИГНАЛИЗАЦИЮ
>
else if ( mode == 2 ) <
// ОХРАНА
>
else if ( mode == 3 ) <
// БЛОКИРОВКА
>
else mode= 0;
В этом случае цикл loop() не задерживается, всегда выполняется. В нем также могут находиться общие для любого режима действия.
Рад, что Вы затронули эту тему. Если не согласны напишите.

Источник