Урок 17. Рабочий проект Ардуино. Охранная сигнализация.

Arduino UNO R3

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

Предыдущий урок     Список уроков     Следующий урок

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

 

Это аналог моей разработки на PIC-контроллере, только выполненный на базе платы Arduino UNO.

Охранная сигнализация

Можете прочитать статью об этом устройстве. Я повторю задачу.

Общая информация об устройстве.

Сигнализация обеспечивает:

  • Контроль состояния двух охранных шлейфов с измерением сопротивления цепей датчиков и цифровой фильтрацией сигналов.
  • Управление устройством с помощью пульта (светодиод и две кнопки):
    • включение сигнализации;
    • отключение сигнализации с помощью секретного кода;
    • установка секретного кода (код содержится в EEPROM контроллера).
  • Отображение режимов работы внешним источником света и светодиодом пульта.
  • Устройство формирует временные задержки, необходимые для закрытия дверей помещения и набора секретного кода.
  • При срабатывании сигнализации устройство включает звуковой оповещатель (сирену).

 

Структурная схема устройства.

Структурная схема

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

  • 2 стандартных охранных шлейфа с:
    • НР – нормально разомкнутыми датчиками;
    • НЗ – нормально замкнутыми датчиками;
    • Rок – оконечными резисторами.
  • Пульт управления.
  • Наружный блок индикации режима и звукового оповещения.
  • Блок питания 12 В.
  • Источник резервного питания.

Режимы и управление сигнализацией.

Пульт это коробочка с одним светодиодом и двумя кнопками.

Пульт

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

При подаче питания устройство переходит в режим СИГНАЛИЗАЦИЯ ОТКЛЮЧЕНА. Светодиод пульта не светится. В этом режиме сигнализация находится в течение рабочего дня.

Включается сигнализация (режим ОХРАНА) нажатием на две кнопки одновременно. Светодиод начинает мигать с частотой 5 раз в секунду, и через 20 секунд устройство переходит в режим ОХРАНА, т.е. контролирует состояние охранных датчиков. Время 20 секунд необходимо для того, чтобы выйти из помещения, закрыть входную дверь.

Если в течение этого времени (20 секунд) нажать любую кнопку пульта, то устройство вернется в режим СИГНАЛИЗАЦИЯ ОТКЛЮЧЕНА. Люди часто что-то вспоминают перед выходом из помещения.

Через 20 секунд после включения сигнализация перейдет в режим ОХРАНА. Светодиоды пульта и блока наружной индикации мигают раз в секунду, контролируется состояние датчиков.

При срабатывании любого датчика светодиод пульта начинает мигать с частотой 5 раз в секунду, и устройство отсчитывает время, по истечении которого прозвучит сигнал сирены. Это время (30 секунд) необходимо для того, чтобы отключить сигнализацию, набрав кнопками пульта секретный код.

На пульте всего две кнопки. Поэтому код это комбинация цифр 1 и 2. Например, код 2122221 означает, что надо последовательно нажать кнопки 2, 1, четыре раза 2 и 1. Секретный код может содержать от 1 до 8 цифр.

Если код был набран неправильно, надо нажать 2 кнопки пульта одновременно и повторить набор кода.

Правильно набранный код переводит устройство в режим СИГНАЛИЗАЦИЯ ОТКЛЮЧЕНА.

Если в течение 30 секунд после срабатывания охранного датчика правильный код не был набран, то включается сирена. Отключить ее можно набрав правильный код. Иначе сирена будет звучать в течение минуты, а затем устройство перейдет в режим СИГНАЛИЗАЦИЯ ОТКЛЮЧЕНА.

Секретный код можно задать только из режима СИГНАЛИЗАЦИЯ ОТКЛЮЧЕНА. Для этого надо удерживать обе кнопки пульта нажатыми не менее 6 секунд. Отпустить, когда загорится светодиод пульта. Это означает, что сигнализация перешла в режим задания секретного кода.

Теперь надо набрать новый секретный код (от 1 до 8 цифр). Пауза между нажатиями кнопок должна быть не более 5 секунд. Подождать пока светодиод погаснет (еще 5 секунд). Новый секретный код будет сохранен в энергонезависимой памяти контроллера.

Состояния охранной сигнализации сведены в таблицу.

Режим Состояние
светодиода
Условие перехода Переход на режим
СИГНАЛИЗАЦИЯ ОТКЛЮЧЕНА Не светится  Нажатие двух кнопок одновременно Ожидание ОХРАНЫ (20 сек).

Необходимо на то, чтобы выйти и закрыть входную дверь.

Удержание  двух кнопок нажатыми 6 сек Установка секретного кода
Ожидание охраны

Необходимо на то, чтобы выйти и закрыть входную дверь.

Часто мигает (5раз в сек) Время 20 сек                          ОХРАНА
Нажатие любой кнопки (отмена) СИГНАЛИЗАЦИЯ ОТКЛЮЧЕНА
ОХРАНА Мигает раз в секунду Срабатывание датчиков Время на отключение сигнализации секретным кодом (30 сек)

Необходимо для того, чтобы отключить сигнализацию набором кода

Время на отключение сигнализации кодом (30 сек)

Необходимо для того, чтобы отключить сигнализацию набором кода

Часто мигает
(5 раз в сек)
Набран правильный код СИГНАЛИЗАЦИЯ ОТКЛЮЧЕНА
Правильный код не набран в течение 30 сек Звукой сигнал сирены
(тревога)
Звукой сигнал сирены (тревога) Часто мигает Набран правильный код СИГНАЛИЗАЦИЯ ОТКЛЮЧЕНА
Время 60 сек СИГНАЛИЗАЦИЯ ОТКЛЮЧЕНА
Установка секретного кода Постоянно светится Набор кода СИГНАЛИЗАЦИЯ ОТКЛЮЧЕНА

Управление сигнализацией практически сводится к двум операциям:

  • Покидая помещение – нажать обе кнопки пульта одновременно и закрыть дверь.
  • Войдя в помещение – набрать секретный код.

Разработка аппаратной части устройства.

При всей многофункциональности платы Ардуино, для создания работоспособного устройства необходимо добавить к ней электронные узлы для подключения:

  • охранных шлейфов;
  • кнопок и светодиода пульта;
  • сирены и наружной световой индикации
  • системы питания.

Рассмотрим эти узлы подробно.

Шлейф охранной сигнализации.

В охранных системах для подключения датчиков используются охранные шлейфы. Охранный шлейф это цепь из последовательно и параллельно включенных датчиков. К одному шлейфу могут быть подключены несколько датчиков, как нормально замкнутых (НЗ), так и нормально разомкнутых (НР).

Схема охранного шлейфа

Блок сигнализации контролирует сопротивление шлейфа. Если сопротивление меньше нижнего или больше верхнего порога, то устройство формирует сигнал тревоги. Нормальным считается сопротивление шлейфа , заданного оконечным резистором (часто 2 кОм). Если злоумышленник замкнет провода шлейфа или разорвет их, то сработает сигнализация.

Выберем параметры шлейфа такие же, как в прототипе на PIC-контроллере.

 Сопротивление шлейфа
 Номинальное значение  2000 Ом
 Верхний порог  5900 Ом
 Нижний порог  540 Ом

Сопротивление шлейфа в диапазоне 540…5900 Ом считаем нормальным. При выходе значения сопротивления шлейфа за указанные пределы должна срабатывать сигнализация.

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

Схема подключения шлейфа

Резистор R1 задает ток шлейфа. Делитель R2, R3 согласовывает уровень напряжения цепи с входным напряжением аналогового входа (5 В). При указанных на схеме номиналах резисторов сопротивлению шлейфа будут соответствовать следующие значения напряжения на входе платы.

 Сопротивление шлейфа  Напряжение на входах A0, A1
 5900 Ом  3,6 В
 2000 Ом  2,43 В
 540 Ом  1,04 В

Конденсатор C1 вместе с резисторами осуществляет аналоговую фильтрацию сигнала шлейфа. Для повышения помехозащищенности системы в устройстве происходит еще и цифровая фильтрация сигнала.

 

Кнопки пульта.

Кнопки пульта, как и охранные датчики, подключены к контроллеру длинными проводами. Сигналы от них также требуют аналоговой и цифровой фильтрации для обеспечения высокой помехозащищенности и устранения дребезга. Подключим кнопки к цифровым выводам платы 9 и 10.

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

При разомкнутой кнопке напряжение 12 В ограничивается на уровне 5 В внутренним защитным диодом микроконтроллера.

Наружная индикация режима и сирена.

Сирена и источник света для наружной индикации режима могут потребовать значительных токов питания. Кроме того они подключены к контроллеру длинными проводами. Поэтому для коммутации этих элементов необходимы транзисторные ключи. Вот схема одного ключа на ток до 2 А.

Схема подключения сирены

Резистор R12 ограничивает ток базы транзистора. Диоды защищают транзистор от выбросов сигнала длинной линии связи при коммутации.

Система питания.

Устройство питается от блока питания напряжением 12 В. Такое напряжение подходит для питания как платы Ардуино, так и сирены. Для подключения резервного питания от батареи можно использовать простую схему на двух диодах.

Схема питания

В качестве диода для основного питания используется диод  Шоттки (VD1). Это обеспечивает приоритет блоку питания при равенстве напряжения на его выходе с напряжением резервного питания.

 

Схема охранной сигнализации на базе платы Arduino UNO R3.

Схема охранной сигнализации Arduino UNO R3

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

Макет сигнализации

Макет сигнализации

Разработка программы.

Прежде всего, подключаем библиотеки, назначаем выводы контроллера, создаем объекты, переменные, с которыми мы будем работать:

  • две  кнопки пульта;
  • прерывание по таймеру;
  • программные блоки чтения аналоговых входов для шлейфов и усреднения их значений.

В результате у нас есть следующие переменные с которыми мы будем работать:

  • признаки состояния кнопок пульта:
    • button1.flagPress;
    • button1.flagClick;
    • button2.flagPress;
    • button2.flagClick;
  • измеренные средние значения напряжения на выходах шлейфов:
    • avarageShleif1;
    • avarageShleif2.

Для проверки посылаем состояние этих переменных через последовательный порт на компьютер.
// охранная сигнализация
#include <MsTimer2.h>
#include <Button.h>
#include <avr/wdt.h>

// назначение выводов
#define BUTTON_1_PIN 9  // кнопка 1 пульта
#define BUTTON_2_PIN 10 // кнопка 2 пульта
#define LED_PIN 11    // светодиод пульта и внешняя индикация
#define SIREN_PIN 12  // сирена подключена к выводу 9
#define SHLEIF_1_PIN A0 // шлейф 1
#define SHLEIF_2_PIN A1 // шлейф 2

Button button1(BUTTON_1_PIN, 25); // создание объекта кнопка 1
Button button2(BUTTON_2_PIN, 25); // создание объекта кнопка 2

unsigned int sumShleif1, sumShleif2; // переменные для суммирования кодов АЦП
unsigned int avarageShleif1, avarageShleif2; // сумма кодов АЦП (среднее напряжение шлейфов * 50)
int avarageCount; // счетчик усреднния кодов АЦП (напряжения шлейфов)
int serialCount;    // счетчик времени передачи отладочных данных через UART
void setup() {
  pinMode(LED_PIN, OUTPUT);      // вывод светодиода
  pinMode(SIREN_PIN, OUTPUT);    // вывод сирены
 
  MsTimer2::set(2, timerInterrupt); // период прерывания по таймеру 2 мс
  MsTimer2::start();                // разрешаем прерывание по таймеру

  Serial.begin(9600); // инициализируем порт, скорость 9600
  wdt_enable(WDTO_15MS); // разрешаем сторожевой таймер, тайм-аутом 15 мс 
}
void loop() {

  // передача отладочных данных через UART
  // каждые 500 мс
  if ( serialCount >= 250 ) {

    // состояние кнопок
    if ( button1.flagPress == true ) Serial.print("Btn1 -_- ");
    else Serial.print("Btn1 _-_ ");
    if ( button2.flagPress == true ) Serial.print("Btn2 -_- ");
    else Serial.print("Btn2 _-_ ");

    // напряжения шлейфов
    Serial.print(" Shleif1= ");
    Serial.print( (float)avarageShleif1 * 0.000097656, 2);
    Serial.print(" V");
    Serial.print(" Shleif2= ");
    Serial.print( (float)avarageShleif2 * 0.000097656, 2);
    Serial.println(" V");   
    // * 0.000097656 = / 50. * 5. / 1024.           
  }
}

// обработчик прерывания, 2 мс
void  timerInterrupt() {
  wdt_reset();  // сброс сторожевого таймера 

  button1.filterAvarage();  // вызов метода фильтрации сигнала кнопки 1 
  button2.filterAvarage();  // вызов метода фильтрации сигнала кнопки 2

  // чтение АЦП и усреднение значений напряжений шлейфов
  // в результате avarageShleif = среднее напряжение шлейфа * 50  
  avarageCount++;  // +1 счетчик усреднения
  sumShleif1 += analogRead(SHLEIF_1_PIN);  // суммирование кодов АЦП
  sumShleif2 += analogRead(SHLEIF_2_PIN);  // суммирование кодов АЦП

  // проверка количества выборок усреднения (50)
  if ( avarageCount >= 50 ) {
    avarageCount= 0;
    avarageShleif1 = sumShleif1; // перегрузка среднего значения
    avarageShleif2 = sumShleif2; // перегрузка среднего значения
    sumShleif1 = 0;
    sumShleif2 = 0;
    }

  serialCount++;  // счетчик времени передачи отладочных данных через UART 
}

Загружаем программу в плату. Запускаем монитор порта. Проверяем:

  • Нажатие на каждую кнопку.
  • Подключаем вместо каждого шлейфа переменный резистор (например, с сопротивлением 10 кОм), а к входу платы вольтметр и проверяем, что измерение напряжений на выходах шлейфов происходит правильно.

Окно монитора порта

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

Создаем переменную режим – mode, которая определяет текущий режим работы программы. Выбираем режимы:

mode Режим Описание
0 СИГНАЛИЗАЦИЯ ОТКЛЮЧЕНА
1 УСТАНОВКА НА СИГНАЛИЗАЦИЮ Отсчет времени (20 сек) на то, чтобы выйти из помещения и закрыть двери
2 ОХРАНА Контроль состояния датчиков
3 БЛОКИРОВКА Время на то, чтобы отключить сигнализацию секретным кодом (30 сек)
4 ТРЕВОГА Время звучания сирены (60 сек)
5 УСТАНОВКА КОДА Установка нового секретного кода

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

  if ( mode == 0 )  {
    // СИГНАЛИЗАЦИЯ ОТКЛЮЧЕНА   
  }

  else if ( mode == 1 )  {
    // УСТАНОВКА НА СИГНАЛИЗАЦИЮ   
  }

  else if ( mode == 2 )  {
    // ОХРАНА   
  }

  else if ( mode == 3 )  {
    // БЛОКИРОВКА   
  }

  else if ( mode == 4 )  {
    // ТРЕВОГА   
  }

  else if ( mode == 5 )  {
    // УСТАНОВКА КОДА   
  }
  else  mode= 0;

Для перехода на другой режим достаточно изменить значение переменной mode.

Добавляем в отладочные данные режим.

// режим     
Serial.print("Mode= ");  Serial.print(mode);

Теперь заполняем блоки – режимы.

Все программные блоки я подробно поясняю в комментариях программы. При заполнении каждого блока можно загружать программу в плату и проверять. Лучше отлаживать каждый блок отдельно, не дожидаясь пока накопятся ошибки. Например, можно добавить модуль ” переход на режим установки на сигнализацию”, загрузить в плату и проверить, что по нажатию двух кнопок одновременно, программа переходит на режим 1. А на нажатие одной кнопки не реагирует. Для этого мы и создавали блок передачи отладочных данных на компьютер.

Затем добавляем блок “переход на установку кода (долгое удержание двух кнопок)” и опять проверяем, что программа переходит на режим 5 при удержании обеих кнопок нажатыми в течение 6 секунд. И так постепенно отлаживаем всю программу.

Вот полный скетч программы.
// охранная сигнализация
#include <MsTimer2.h>
#include <Button.h>
#include <avr/wdt.h>
#include <EEPROM.h>

// назначение выводов
#define BUTTON_1_PIN 9  // кнопка 1 пульта
#define BUTTON_2_PIN 10 // кнопка 2 пульта
#define LED_PIN 11    // светодиод пульта и внешняя индикация
#define SIREN_PIN 12  // сирена подключена к выводу 9
#define SHLEIF_1_PIN A0 // шлейф 1
#define SHLEIF_2_PIN A1 // шлейф 2

// параметры
#define SET_COD_TIME 3000 // время удержания кнопок для перехода на режим установки кода (* 2 мс = 6 сек)
#define FAST_TIME_FLASH_LED 50 // время частого мигания светодиода (* 2 мс, 5 раз в сек)
#define SLOW_TIME_FLASH_LED 250 // время редкого мигания светодиода (* 2 мс, 1 раз в сек)
#define TIME_SET_GUARD 10000  // время установки на сигнализацию (* 2 мс, 20 сек)
#define TIME_BLOCK 15000  // время на ввод секретного кода (* 2 мс, 30 сек)
#define TIME_ALARM 30000  // время на звучание сирены (* 2 мс, 60 сек)
#define TIME_SET_COD 2500  // время паузы между вводом цифр кода (* 2 мс, 5 сек)
#define MAX_U 36864 // верхний предел напряжение шлейфов (3,6 В, код = 3,6 * 1024 / 5 * 50 = 36864 )
#define MIN_U 10650 // нижний предел напряжение шлейфов (1,04 В, код = 1,04 * 1024 / 5 * 50 = 10650 )

// адреса EEPROM
#define COD_ADR  2    // адрес секретного кода
#define NUMBER_ADR  4 // адрес числа битов секретного кода

Button button1(BUTTON_1_PIN, 25); // создание объекта кнопка 1
Button button2(BUTTON_2_PIN, 25); // создание объекта кнопка 2

unsigned int sumShleif1, sumShleif2; // переменные для суммирования кодов АЦП
unsigned int avarageShleif1, avarageShleif2; // сумма кодов АЦП (среднее напряжение шлейфов * 50)
unsigned int avarageCount; // счетчик усреднния кодов АЦП (напряжения шлейфов)
unsigned int serialCount;    // счетчик времени передачи отладочных данных через UART
byte mode;  // режим
boolean flagTwoButtons; // признак были нажаты две кнопки
unsigned int commonTimer; // таймер для разных целей
unsigned int ledFlashCount; // счетчик мигания светодиода
byte  secretCod;  // переменная для секретного кода
byte  bitNum;  // переменная для номера бита секретного кода

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

  Serial.begin(9600); // инициализируем порт, скорость 9600
  wdt_enable(WDTO_15MS); // разрешаем сторожевой таймер, тайм-аутом 15 мс 

  flagTwoButtons= false;
}
void loop() {

  if ( mode == 0 )  {
    //---------------------------------- СИГНАЛИЗАЦИЯ ОТКЛЮЧЕНА --------------------------  
   
    digitalWrite(LED_PIN, LOW); // светодиод не горит
    digitalWrite(SIREN_PIN, LOW); // сирена не звучит

    // переход на режим установки на сигнализацию (коротокое удержание двух кнопок)
    // при нажатии двух кнопок одновремено вырабатывается признак flagTwoButtons
    // переход на режим 1 происходит, если были нажаты обе кнопки, а затем они обе отжаты
    if ( (button1.flagPress == true) && (button2.flagPress == true) ) flagTwoButtons= true;
    if ( (flagTwoButtons == true) && (button1.flagPress == false) && (button2.flagPress == false)  ) {
      // переход на установку сигнализации
      commonTimer= 0;
      button1.flagClick= false;
      button2.flagClick= false;
      mode = 1;  
    }

    // переход на установку кода (долгое удержание двух кнопок) 
    // если не нажата хотя бы одна кнопка, то обнуляем commonTimer
    // как только commonTimer насчитывает 6 сек, то переходим на режим 5 
    if ( (button1.flagPress == false) || (button2.flagPress == false) ) commonTimer= 0;
    if ( commonTimer > SET_COD_TIME ) {
      // переход на режим 5
      commonTimer= 0;
      button1.flagClick= false;
      button2.flagClick= false;
      secretCod= 0;
      bitNum= 0;     
      mode= 5;     
    }  
  }

  else if ( mode == 1 )  {
    //---------------------------------- УСТАНОВКА НА СИГНАЛИЗАЦИЮ ----------------------- 

    digitalWrite(SIREN_PIN, LOW); // сирена не звучит

    // светодиод мигает 5 раз в сек
    if ( ledFlashCount > FAST_TIME_FLASH_LED ) {
      ledFlashCount= 0;
      digitalWrite(LED_PIN, ! digitalRead(LED_PIN));  // инверсия светодиода
    }

    // проверка времени перехода на режим ОХРАНА
    if ( commonTimer >= TIME_SET_GUARD ) {
      // переход на режим 2 (ОХРАНА)
      commonTimer= 0;
      button1.flagClick= false;
      button2.flagClick= false;
      secretCod= 0;
      bitNum= 0;
      mode= 2;                       
    }

    // отказ при нажатии на любую кнопку
    if ( (button1.flagClick == true) || (button2.flagClick == true) ) {
      // переход на режим 0 (СИГНАЛИЗАЦИЯ ОТКЛЮЧЕНА)
      commonTimer= 0;
      button1.flagClick= false;
      button2.flagClick= false;
      flagTwoButtons= false;
      mode= 0;             
    }                               
  }

  else if ( mode == 2 )  {
    //---------------------------------- ОХРАНА -----------------------------------------   

    digitalWrite(SIREN_PIN, LOW); // сирена не звучит

    // светодиод мигает 1 раз в сек
    if ( ledFlashCount > SLOW_TIME_FLASH_LED ) {
      ledFlashCount= 0;
      digitalWrite(LED_PIN, ! digitalRead(LED_PIN));  // инверсия светодиода
    }

    // проверка состояния шлейфов (датчиков)
    if ( (avarageShleif1 > MAX_U) || (avarageShleif1 < MIN_U) ||
         (avarageShleif2 > MAX_U) || (avarageShleif2 < MIN_U)) {
          // переход на режим 3 (БЛОКИРОВКА)
          commonTimer= 0;
          button1.flagClick= false;
          button2.flagClick= false;
          secretCod= 0;
          bitNum= 0;
          mode= 3;                       
         }

    // проверка секретного кода (оформлена функцией)
    secretCodCheck();
  }

  else if ( mode == 3 )  {
    //---------------------------------- БЛОКИРОВКА -------------------------------------

    digitalWrite(SIREN_PIN, LOW); // сирена не звучит

    // светодиод мигает 5 раз в сек
    if ( ledFlashCount > FAST_TIME_FLASH_LED ) {
      ledFlashCount= 0;
      digitalWrite(LED_PIN, ! digitalRead(LED_PIN));  // инверсия светодиода
    }

    // проверка времени на ввод секретного кода
    if ( commonTimer >= TIME_BLOCK ) {
      // переход на режим 4 (ТРЕВОГА)
      commonTimer= 0;
      button1.flagClick= false;
      button2.flagClick= false;
      secretCod= 0;
      bitNum= 0;
      mode= 4;                       
    }

    // проверка секретного кода (оформлена функцией)
    secretCodCheck();   
  }

  else if ( mode == 4 )  {
    //---------------------------------- ТРЕВОГА ---------------------------------------

    digitalWrite(SIREN_PIN, HIGH); // звучит сирена

    // светодиод мигает 5 раз в сек
    if ( ledFlashCount > FAST_TIME_FLASH_LED ) {
      ledFlashCount= 0;
      digitalWrite(LED_PIN, ! digitalRead(LED_PIN));  // инверсия светодиода
    }

    // проверка времени звучания сирены
    if ( commonTimer >= TIME_ALARM ) {
      // переход на режим 0 (СИГНАЛИЗАЦИЯ ОТКЛЮЧЕНА)
      commonTimer= 0;
      button1.flagClick= false;
      button2.flagClick= false;
      flagTwoButtons= false;
      mode= 0;   
    }                   

    // проверка секретного кода (оформлена функцией)
    secretCodCheck();       
  }

  else if ( mode == 5 )  {
    //---------------------------------- УСТАНОВКА КОДА -------------------------------

    digitalWrite(SIREN_PIN, LOW); // сирена не звучит
    digitalWrite(LED_PIN, HIGH);  // светодиод светится

    // ввод секретного кода
    if ( (button1.flagClick == true) || (button2.flagClick == true) ) {
      // кнопку нажали
      commonTimer= 0; // сброс счетчика времени
      secretCod= secretCod << 1; // сдвиг secretCod на 1 бит
      // если нажали кнопку 1 установка 0 в младший бит secretCod
      if ( button1.flagClick == true ) {
        button1.flagClick= false;
        secretCod &= 0xfe;  // установка 0 в младший бит
      }
      // если нажали кнопку 2 установка 1 в младший бит secretCod
      if ( button2.flagClick == true ) {
        button2.flagClick= false;
        secretCod |= 1;  // установка 1 в младший бит
      }
      bitNum++; // еще один бит ввели     
    }

    // проверка времени паузы между вводом цифр
    if ( commonTimer >= TIME_SET_COD ) {
      // запись кода и числа цифр кода в EEPROM
      EEPROM.write(COD_ADR, secretCod); 
      EEPROM.write(NUMBER_ADR, bitNum);       
      // переход на режим 0 (СИГНАЛИЗАЦИЯ ОТКЛЮЧЕНА)
      commonTimer= 0;
      button1.flagClick= false;
      button2.flagClick= false;
      flagTwoButtons= false;
      mode= 0;   
    }                       
  }
  else  mode= 0;

  // передача отладочных данных через UART
  // каждые 500 мс
  if ( serialCount >= 250 ) {

    // режим
    Serial.print("Mode= "); Serial.print(mode);
   
    // состояние кнопок
    if ( button1.flagPress == true ) Serial.print(" Btn1 -_- ");
    else Serial.print(" Btn1 _-_ ");
    if ( button2.flagPress == true ) Serial.print("Btn2 -_- ");
    else Serial.print("Btn2 _-_ ");

    // напряжения шлейфов
    Serial.print(" Shleif1= ");
    Serial.print( (float)avarageShleif1 * 0.000097656, 2);
    Serial.print(" V");
    Serial.print(" Shleif2= ");
    Serial.print( (float)avarageShleif2 * 0.000097656, 2);
    Serial.println(" V");   
    // * 0.000097656 = / 50. * 5. / 1024.           
  }
}

// ---------------------------------------------------обработчик прерывания, 2 мс
void  timerInterrupt() {
  wdt_reset();  // сброс сторожевого таймера 

  button1.filterAvarage();  // вызов метода фильтрации сигнала кнопки 1 
  button2.filterAvarage();  // вызов метода фильтрации сигнала кнопки 2

  // чтение АЦП и усреднение значений напряжений шлейфов
  // в результате avarageShleif = среднее напряжение шлейфа * 50  
  avarageCount++;  // +1 счетчик усреднения
  sumShleif1 += analogRead(SHLEIF_1_PIN);  // суммирование кодов АЦП
  sumShleif2 += analogRead(SHLEIF_2_PIN);  // суммирование кодов АЦП

  // проверка количества выборок усреднения (50)
  if ( avarageCount >= 50 ) {
    avarageCount= 0;
    avarageShleif1 = sumShleif1; // перегрузка среднего значения
    avarageShleif2 = sumShleif2; // перегрузка среднего значения
    sumShleif1 = 0;
    sumShleif2 = 0;
    }

  serialCount++;  // счетчик времени передачи отладочных данных через UART 
  commonTimer++;  // таймер
  ledFlashCount++; // счетчик мигания светодиода
}

//----------------------------- проверка секретного кода
// при правильном коде устанавливает mode = 0 (СИГНАЛИЗАЦИЯ ОТКЛЮЧЕНА)
void secretCodCheck() {
   
    // ввод секретного кода
    if ( (button1.flagClick == true) || (button2.flagClick == true) ) {
      // кнопку нажали
      secretCod= secretCod << 1; // сдвиг secretCod на 1 бит
      // если нажали кнопку 1 установка 0 в младший бит secretCod
      if ( button1.flagClick == true ) {
        button1.flagClick= false;
        secretCod &= 0xfe;  // установка 0 в младший бит
      }
      // если нажали кнопку 2 установка 1 в младший бит secretCod
      if ( button2.flagClick == true ) {
        button2.flagClick= false;
        secretCod |= 1;  // установка 1 в младший бит
      }
      bitNum++; // еще один бит ввели     
    }

    // проверка секретного кода
      // проверка числа цифр   
      if ( bitNum == EEPROM.read(NUMBER_ADR) ) {
        // ввели все цифры кода
        // проверка секретного кода
        if ( secretCod == EEPROM.read(COD_ADR) ) {
          // код совпал, переход на режим 0 (СИГНАЛИЗАЦИЯ ОТКЛЮЧЕНА)
          commonTimer= 0;
          button1.flagClick= false;
          button2.flagClick= false;
          flagTwoButtons= false;
          mode= 0;             
        }   
        // код набрали неправильный
        else { secretCod= 0; bitNum= 0; }   
      }

    // сброс кода по нажатию на две кнопки
    if ( (button1.flagPress == true) && (button2.flagPress == true) ) {
        button1.flagClick= false;
        button2.flagClick= false;
        secretCod= 0;
        bitNum= 0;     
    }   
}

Загрузить скетч программы и библиотеки можно по этим ссылкам: sketch_17_3.ino , Button.zip, MsTimer2.zip. Как установить библиотеку, рассказано в уроке 9. Библиотеку avr/wdt искать в интернете и устанавливать не надо. Это стандартная библиотека. Она находится в программном обеспечении для Ардуино.

Загружаем программу в плату. Проверяем. У меня работает.

Какой вариант устройства лучше: на базе платы Ардуино или на PIC-контроллере PIC12F629?

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

 

Алгоритмы работы устройств совершенно одинаковые. Сигнализация на PIC-контроллере почти год охраняет здание нашей фирмы, и к алгоритму работы претензий нет. Все сотрудники без проблем научились управлять устройством.

Предыдущий урок     Список уроков     Следующий урок

23 комментария на «Урок 17. Рабочий проект Ардуино. Охранная сигнализация.»

  1. А чем обусловлен выбор конденсаторов номиналом 1 мкф в цепях кнопок и шлейфов? 0.1 мкф или даже 0.01 мкф разве недостаточно будет?

    • Шлейфы могут быть длинные, а цифровая фильтрация работает в диапазоне только 0…5 В. Я бы все таки оставил 1 мкФ.

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

    • Я хотел реализовать такой вариант, но отказался. Можно забыть секретный код, и тогда его никогда не установить.

  3. Уважаемый Эдуард!
    Сделал попытку подключить к этому проекту GSM модуль для управления на постановка \ снятие. Похоже мешает таймер прерываний. Подскажите в в чем может быть моя ошибка. Скетч находится по ссылке: https://yadi.sk/d/zpF4ZLoUyhbtU

    • Евгений, здравствуйте! Мне трудно найти ошибку по скетчу. Я могу Вам посоветовать вести протокол всего, что у вас происходит с GSM модулем. Перед вызовом каждой функции посылайте в последовательный порт сообщение, а затем сообщение о результате выполнения функции. Увидите в каком месте ошибка.

  4. Эдуард, большой респект за проделанную работу. Повторил систему сигнализации по предложенной схеме и всё прошло штатно.
    Для идущих следом следом сообщаю: после проверки монтажа и уровней на всех входах, начинать надо с введения кода. И не ленитесь читать весь текст от Эдуарда.

  5. Помогите скетч не работает.
    Пишет ошибку:
    Arduino: 1.8.0 (Windows 7), Плата:»Arduino/Genuino Uno»

    sketch_dec28a:31: error: no matching function for call to ‘Button::Button(int, int)’

    Button button1(BUTTON_1_PIN, 25); // создание объекта кнопка 1

    ^

    C:\Users\27C6~1\AppData\Local\Temp\arduino_modified_sketch_57337\sketch_dec28a.ino:31:32: note: candidates are:

    In file included from C:\Users\27C6~1\AppData\Local\Temp\arduino_modified_sketch_57337\sketch_dec28a.ino:4:0:

    C:\Users\Руслан\Documents\Arduino\libraries\Button/Button.h:14:3: note: Button::Button(uint8_t)

    Button(uint8_t pin);

    ^

    C:\Users\Руслан\Documents\Arduino\libraries\Button/Button.h:14:3: note: candidate expects 1 argument, 2 provided

    C:\Users\Руслан\Documents\Arduino\libraries\Button/Button.h:11:7: note: constexpr Button::Button(const Button&)

    class Button

    ^

    C:\Users\Руслан\Documents\Arduino\libraries\Button/Button.h:11:7: note: candidate expects 1 argument, 2 provided

    C:\Users\Руслан\Documents\Arduino\libraries\Button/Button.h:11:7: note: constexpr Button::Button(Button&&)

    C:\Users\Руслан\Documents\Arduino\libraries\Button/Button.h:11:7: note: candidate expects 1 argument, 2 provided

    sketch_dec28a:32: error: no matching function for call to ‘Button::Button(int, int)’

    Button button2(BUTTON_2_PIN, 25); // создание объекта кнопка 2

    ^

    C:\Users\27C6~1\AppData\Local\Temp\arduino_modified_sketch_57337\sketch_dec28a.ino:32:32: note: candidates are:

    In file included from C:\Users\27C6~1\AppData\Local\Temp\arduino_modified_sketch_57337\sketch_dec28a.ino:4:0:

    C:\Users\Руслан\Documents\Arduino\libraries\Button/Button.h:14:3: note: Button::Button(uint8_t)

    Button(uint8_t pin);

    ^

    C:\Users\Руслан\Documents\Arduino\libraries\Button/Button.h:14:3: note: candidate expects 1 argument, 2 provided

    C:\Users\Руслан\Documents\Arduino\libraries\Button/Button.h:11:7: note: constexpr Button::Button(const Button&)

    class Button

    ^

    C:\Users\Руслан\Documents\Arduino\libraries\Button/Button.h:11:7: note: candidate expects 1 argument, 2 provided

    C:\Users\Руслан\Documents\Arduino\libraries\Button/Button.h:11:7: note: constexpr Button::Button(Button&&)

    C:\Users\Руслан\Documents\Arduino\libraries\Button/Button.h:11:7: note: candidate expects 1 argument, 2 provided

    C:\Users\27C6~1\AppData\Local\Temp\arduino_modified_sketch_57337\sketch_dec28a.ino: In function ‘void loop()’:

    sketch_dec28a:70: error: ‘class Button’ has no member named ‘flagPress’

    if ( (button1.flagPress == true) && (button2.flagPress == true) ) flagTwoButtons= true;

    ^

    sketch_dec28a:70: error: ‘class Button’ has no member named ‘flagPress’

    if ( (button1.flagPress == true) && (button2.flagPress == true) ) flagTwoButtons= true;

    ^

    sketch_dec28a:71: error: ‘class Button’ has no member named ‘flagPress’

    if ( (flagTwoButtons == true) && (button1.flagPress == false) && (button2.flagPress == false) ) {

    ^

    sketch_dec28a:71: error: ‘class Button’ has no member named ‘flagPress’

    if ( (flagTwoButtons == true) && (button1.flagPress == false) && (button2.flagPress == false) ) {

    ^

    sketch_dec28a:74: error: ‘class Button’ has no member named ‘flagClick’

    button1.flagClick= false;

    ^

    sketch_dec28a:75: error: ‘class Button’ has no member named ‘flagClick’

    button2.flagClick= false;

    ^

    sketch_dec28a:82: error: ‘class Button’ has no member named ‘flagPress’

    if ( (button1.flagPress == false) || (button2.flagPress == false) ) commonTimer= 0;

    ^

    sketch_dec28a:82: error: ‘class Button’ has no member named ‘flagPress’

    if ( (button1.flagPress == false) || (button2.flagPress == false) ) commonTimer= 0;

    ^

    sketch_dec28a:86: error: ‘class Button’ has no member named ‘flagClick’

    button1.flagClick= false;

    ^

    sketch_dec28a:87: error: ‘class Button’ has no member named ‘flagClick’

    button2.flagClick= false;

    ^

    sketch_dec28a:109: error: ‘class Button’ has no member named ‘flagClick’

    button1.flagClick= false;

    ^

    sketch_dec28a:110: error: ‘class Button’ has no member named ‘flagClick’

    button2.flagClick= false;

    ^

    sketch_dec28a:117: error: ‘class Button’ has no member named ‘flagClick’

    if ( (button1.flagClick == true) || (button2.flagClick == true) ) {

    ^

    sketch_dec28a:117: error: ‘class Button’ has no member named ‘flagClick’

    if ( (button1.flagClick == true) || (button2.flagClick == true) ) {

    ^

    sketch_dec28a:120: error: ‘class Button’ has no member named ‘flagClick’

    button1.flagClick= false;

    ^

    sketch_dec28a:121: error: ‘class Button’ has no member named ‘flagClick’

    button2.flagClick= false;

    ^

    sketch_dec28a:143: error: ‘class Button’ has no member named ‘flagClick’

    button1.flagClick= false;

    ^

    sketch_dec28a:144: error: ‘class Button’ has no member named ‘flagClick’

    button2.flagClick= false;

    ^

    sketch_dec28a:169: error: ‘class Button’ has no member named ‘flagClick’

    button1.flagClick= false;

    ^

    sketch_dec28a:170: error: ‘class Button’ has no member named ‘flagClick’

    button2.flagClick= false;

    ^

    sketch_dec28a:195: error: ‘class Button’ has no member named ‘flagClick’

    button1.flagClick= false;

    ^

    sketch_dec28a:196: error: ‘class Button’ has no member named ‘flagClick’

    button2.flagClick= false;

    ^

    sketch_dec28a:212: error: ‘class Button’ has no member named ‘flagClick’

    if ( (button1.flagClick == true) || (button2.flagClick == true) ) {

    ^

    sketch_dec28a:212: error: ‘class Button’ has no member named ‘flagClick’

    if ( (button1.flagClick == true) || (button2.flagClick == true) ) {

    ^

    sketch_dec28a:217: error: ‘class Button’ has no member named ‘flagClick’

    if ( button1.flagClick == true ) {

    ^

    sketch_dec28a:218: error: ‘class Button’ has no member named ‘flagClick’

    button1.flagClick= false;

    ^

    sketch_dec28a:222: error: ‘class Button’ has no member named ‘flagClick’

    if ( button2.flagClick == true ) {

    ^

    sketch_dec28a:223: error: ‘class Button’ has no member named ‘flagClick’

    button2.flagClick= false;

    ^

    sketch_dec28a:236: error: ‘class Button’ has no member named ‘flagClick’

    button1.flagClick= false;

    ^

    sketch_dec28a:237: error: ‘class Button’ has no member named ‘flagClick’

    button2.flagClick= false;

    ^

    sketch_dec28a:253: error: ‘class Button’ has no member named ‘flagPress’

    if ( button1.flagPress == true ) Serial.print(» Btn1 -_- «);

    ^

    sketch_dec28a:255: error: ‘class Button’ has no member named ‘flagPress’

    if ( button2.flagPress == true ) Serial.print(«Btn2 -_- «);

    ^

    C:\Users\27C6~1\AppData\Local\Temp\arduino_modified_sketch_57337\sketch_dec28a.ino: In function ‘void timerInterrupt()’:

    sketch_dec28a:273: error: ‘class Button’ has no member named ‘filterAvarage’

    button1.filterAvarage(); // вызов метода фильтрации сигнала кнопки 1

    ^

    sketch_dec28a:274: error: ‘class Button’ has no member named ‘filterAvarage’

    button2.filterAvarage(); // вызов метода фильтрации сигнала кнопки 2

    ^

    C:\Users\27C6~1\AppData\Local\Temp\arduino_modified_sketch_57337\sketch_dec28a.ino: In function ‘void secretCodCheck()’:

    sketch_dec28a:302: error: ‘class Button’ has no member named ‘flagClick’

    if ( (button1.flagClick == true) || (button2.flagClick == true) ) {

    ^

    sketch_dec28a:302: error: ‘class Button’ has no member named ‘flagClick’

    if ( (button1.flagClick == true) || (button2.flagClick == true) ) {

    ^

    sketch_dec28a:306: error: ‘class Button’ has no member named ‘flagClick’

    if ( button1.flagClick == true ) {

    ^

    sketch_dec28a:307: error: ‘class Button’ has no member named ‘flagClick’

    button1.flagClick= false;

    ^

    sketch_dec28a:311: error: ‘class Button’ has no member named ‘flagClick’

    if ( button2.flagClick == true ) {

    ^

    sketch_dec28a:312: error: ‘class Button’ has no member named ‘flagClick’

    button2.flagClick= false;

    ^

    sketch_dec28a:326: error: ‘class Button’ has no member named ‘flagClick’

    button1.flagClick= false;

    ^

    sketch_dec28a:327: error: ‘class Button’ has no member named ‘flagClick’

    button2.flagClick= false;

    ^

    sketch_dec28a:336: error: ‘class Button’ has no member named ‘flagPress’

    if ( (button1.flagPress == true) && (button2.flagPress == true) ) {

    ^

    sketch_dec28a:336: error: ‘class Button’ has no member named ‘flagPress’

    if ( (button1.flagPress == true) && (button2.flagPress == true) ) {

    ^

    sketch_dec28a:337: error: ‘class Button’ has no member named ‘flagClick’

    button1.flagClick= false;

    ^

    sketch_dec28a:338: error: ‘class Button’ has no member named ‘flagClick’

    button2.flagClick= false;

    ^

    Несколько библиотек найдено для «Button.h»
    Используется: C:\Users\Руслан\Documents\Arduino\libraries\Button
    Не используется: D:\ПРОГРАММЫ\Ардуино\arduino-1.8.0-windows\arduino-1.8.0\libraries\Button
    Несколько библиотек найдено для «MsTimer2.h»
    Используется: C:\Users\Руслан\Documents\Arduino\libraries\MsTimer2
    Не используется: D:\ПРОГРАММЫ\Ардуино\arduino-1.8.0-windows\arduino-1.8.0\libraries\MsTimer2
    exit status 1
    no matching function for call to ‘Button::Button(int, int)’

    Этот отчёт будет иметь больше информации с
    включенной опцией Файл -> Настройки ->
    «Показать подробный вывод во время компиляции»

  6. Эдуард, спасибо большое за интересный и полезный материал.

    Объясните пожалуйста, почему в разделе «установка кода» нулевой бит вы присваиваете оператором &= и ноль записываете в шестнадцатеричном формате (0xfe), а единицу присваиваете оператором |= уже в десятичном формате (1).

    • Разницы особой нет. Просто 1 в любом коде задается одинаково. А 0 в младшем разряде легко представляется в двоичном 11111110 и шестнадцатеричном fe форматах. Делайте как вам удобно.

  7. Эдуард, а Вы не думали добавить в проект GSM модуль для отправки сигнала о тревоге? Я хотел попробовать взять из одного проекта готового и скрестить их, но что-то не получаеться.(

  8. Здравствуйте Эдуард.

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

  9. Здравствуйте, Эдуард. Поясните, а какие датчики являются нормально разомкнутыми (я так понимаю, что герконы на дверях нормально замкнуты)? И еще, обязательно ли в шлейфе наличие и нормально-замкнутых и нормально-разомкнутых датчиков?

    • Здравствуйте!
      Посмотрите внимательно на схему шлейфа. В нормальном состоянии (когда сигнализация не должна сработать) полная цепь шлейфа должна иметь сопротивление равное оконечному резистору. На моей схеме 2 кОм. Если разомкнется любой НЗ датчик, то сопротивление шлейфа будет бесконечное. Если замкнется любой НР датчик, то сопротивление шлейфа будет близко к 0.
      Можно использовать любые датчики НЗ и НР в любых сочетаниях. На схеме состояния датчиков показаны для режима целостности шлейфа (сигнализация не должна срабатывать).

  10. Здравствуйте, Эдуард. Поясните пожалуйста по каким критериям ( формулам) Вы выбирали резисторы (R7, R8) , конденсатор ( С4) от дребезга кнопки 1 и их номиналы ?
    Спасибо за ответ
    P/S: можно пожалуйста подробно я просто только начинающий 😉

    • Здравствуйте!
      Я уже точно не помню, но логика была примерно такой.
      Резистор R7 чем меньше, тем выше помехозащищенность линии. Но с уменьшением его сопротивления увеличивается ток через кнопку, увеличивается мощность рассеивания на резисторе R7 и ток потребления схемы.
      Есть еще серьезное ограничение снизу сопротивления R7.
      Ток от источника 12 В через R7 и R8 втекает во вход микроконтроллера. Дальше он через защитный диод поступает на источник питания микроконтроллера 5 В. Этот ток не должен сжечь защитный диод (примерно 25 мА) и не должен поднять напряжение 5 В. Т.е. он должен быть гарантированно меньше тока потребления платы от источника 5 В.
      Из этих соображения я выбрал сопротивления резисторов. В моей схеме:
      — ток при замыкании кнопки = 12 / 4.3 = 3 мА;
      — при разомкнутой кнопке ток = (12 — 5) / (4.3 + 1) = 1,3 мА.
      С учетом того, что сигналы в цепи медленные, конденсатор я выбрал максимальной емкости типа 0805 из тех, что у меня были под рукой.
      Постоянная времени 1 кОм * 1 мкФ = 1 мс. Можно выбрать и больше, не будут нажимать кнопку с частотой 1000 раз в секунду.
      Я не претендую на то, что сопротивления резисторов выбраны оптимально.
      Самат, если у вас какие-то достаточно объемные вопросы требующие рисунков, расчетов, то лучше открывайте тему на форуме сайта.

      • Забыл добавить, что резистор R8 ограничивает ток на входе микроконтроллера, который может появиться из-за выбросов и наводок в длинной линии связи с кнопкой.

  11. Здравствуйте , хотел попробовать на практике но к сожалению скетч по ссылки не грузится ( исправте пожалуйста.

    • Здравствуйте!
      У всех грузится. Какую ошибку выдает?
      Может библиотеки не подключили. Скетч должен быть в папке, названной латинскими символами.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *