Нужна помощь с постановкой на сигнализацию.

Densoider
Offline
Зарегистрирован: 02.11.2012

Уважаемые!

Пытаюсь организовать постановку на охрану. никак не получается.

Алгоритм такой -

1. уходя из помещения включаем сигнализацию (выключателем).

2. По прошествии 1 минуты система встает на охрану и высылает статусный смс.

3. для снятии с охраны шлем смс о выключении охраны. После этого у нас есть 1 минуты для -

4. захода в помещение и выключение охраны выключателем. Если не успели - система повторно встает на охрану и шлет статусный смс.

вот кусок моего кода -

int eventDetect() { // запуск процедуры обработки событий
if (DEBUG) { Serial.println("Waiting for event");} // Сообщение "отладчика"
 if (!digitalRead(ALARM_ACTIVATE_PIN)){  // Проверяем выключатель
  if(millis() > currentTimer) { // запускаем таймер
  ALARM_STATE = !ALARM_STATE; // меняем флаг состояния охраны.
  if (DEBUG) { Serial.println("Alarm ON");}
  //начинаем слушать датчики
  if(digitalRead(INTRUDER_SENSOR_PIN) && !INTRUDER_STATE)
  {
    INTRUDER_STATE = 1;
    blinkLED();
    event = 1;
    raiseEvent(); // запускаем процедуру посылки смс
  }
  if(!digitalRead(INTRUDER_SENSOR_PIN))
  {
    INTRUDER_STATE = 0;
    digitalWrite(ALARM_ON_LED, HIGH);
    digitalWrite(ALARM_OFF_LED, LOW);
  }

  if(digitalRead(DOOR_SENSOR_PIN) && !DOOR_STATE)
  {
    DOOR_STATE = 1;
    blinkLED();
    event = 2;
    raiseEvent();  // запускаем процедуру посылки смс
  }
  if(!digitalRead(DOOR_SENSOR_PIN))
  {
    DOOR_STATE = 0;
    digitalWrite(ALARM_ON_LED, HIGH);
    digitalWrite(ALARM_OFF_LED, LOW);
  }

  if(digitalRead(WINDOW_SENSOR_PIN) && !WINDOW_STATE)
  {
    WINDOW_STATE = 1;
    blinkLED();
    event = 3;
    raiseEvent();  // запускаем процедуру посылки смс
  }
  if(!digitalRead(WINDOW_SENSOR_PIN)) 
  {
    WINDOW_STATE = 0;
    digitalWrite(ALARM_ON_LED, HIGH);
    digitalWrite(ALARM_OFF_LED, LOW);
  }
  currentTimer = millis() + Alarm_on_interval; // увеличиваем таймер на интервал (1мин)
 }
}
 else { //если выключатель не на охране - просто считываем состояние датчиков для статуса.
if (DEBUG) { Serial.println("Alarm OFF");}
digitalWrite(ALARM_ON_LED, LOW);
digitalWrite(ALARM_OFF_LED, HIGH);
if (!digitalRead(ALARM_ACTIVATE_PIN)) ALARM_STATE = 1; else ALARM_STATE = 0;
if(digitalRead(DOOR_SENSOR_PIN) && !DOOR_STATE) DOOR_STATE = 1;
  if(!digitalRead(DOOR_SENSOR_PIN)) DOOR_STATE = 0;
if(digitalRead(WINDOW_SENSOR_PIN) && !WINDOW_STATE) WINDOW_STATE = 1;
  if(!digitalRead(WINDOW_SENSOR_PIN)) WINDOW_STATE = 0;
ALARM_STATE = 0;
}
}

Что нужно - именно работа с временными интервалами

Borland
Offline
Зарегистрирован: 17.05.2012

Из кода можно понять что это только код

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

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

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

Что касается алгоритма, непонятно зачем слать смс о снятии с охраны, и что будет если смс не будет а человек зайдет и выключит выключатель, я бы предложил снимать с охраны просто смс с авторизованного(ых) телефона(нов)

Ну да ладно, это дело реализации, работа с временными интервалами

что касается временных интервалов - 2 выбора

1) замечательная функция milis()

Timeout=60000;
if (открыли дверь)
tiks=millis();
//продолжаем дальше чтото  делать
....
latstiks=millis();
if((lasttiks-Timeout > tiks ) {
/// Прошел таймаут после вскрытия двери, сирену включим или поставим на сигнализацию снова и тд и тп
}

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

2) в GSM модуле есть часы,

вызывается командой AT+CCLK?

считывается с сериала как +CCLK: "12/08/31,13:53:48+16" и легко раздраконивается на год минуты секунды и проч
Перед тем как ей пользоваться надо сказать модему тоже AT командой чтобы он брал время у сотового оператора, они все сейчас поддерживаютэту фичу.

Иначе модем будет показывать нелепое время

я нашел эту команду, вбил ее в модем, и теперь мой склероз не дает мне ее вспомнить, хотя самому хотелось бы знать и записать ее

 

 

Borland
Offline
Зарегистрирован: 17.05.2012

Да третий выбор real time чип, но мне кажется он лишний для GSM модуля

Densoider
Offline
Зарегистрирован: 02.11.2012

Спасибо, попробую.

Решил выложить скетч упрощенный, но полный, а не кусок.

Это исходный код (без интервалов), на который буду накручивать "постановку на охрану"

int INTRUDER_SENSOR_LED = 4;
int DOOR_SENSOR_LED = 5;
int WINDOW_SENSOR_LED = 6;
int INTRUDER_SENSOR_PIN = 7;
int DOOR_SENSOR_PIN = 8;
int WINDOW_SENSOR_PIN = 9;
int event = 0;
boolean INTRUDER_STATE = 0;
boolean DOOR_STATE = 0;
boolean WINDOW_STATE = 0;
char* eventMsg[] = {"No error","Intruder!!!","Door Opened","Window Broken"};

void setup() {

Serial.begin(19200);
pinMode(INTRUDER_SENSOR_LED, OUTPUT);
pinMode(DOOR_SENSOR_LED, OUTPUT);
pinMode(WINDOW_SENSOR_LED, OUTPUT);
pinMode(INTRUDER_SENSOR_PIN, INPUT);
pinMode(DOOR_SENSOR_PIN, INPUT);
pinMode(WINDOW_SENSOR_PIN, INPUT);
}

void loop() {
    if(digitalRead(INTRUDER_SENSOR_PIN) && !INTRUDER_STATE)
  {
    INTRUDER_STATE = 1;
    digitalWrite(INTRUDER_SENSOR_LED, HIGH);
    event = 1;
    raiseEvent();
  }
  if(!digitalRead(INTRUDER_SENSOR_PIN))
  {
    INTRUDER_STATE = 0;
    digitalWrite(INTRUDER_SENSOR_LED, LOW);
  }

  if(digitalRead(DOOR_SENSOR_PIN) && !DOOR_STATE)
  {
    DOOR_STATE = 1;
    digitalWrite(DOOR_SENSOR_LED, HIGH);
    event = 2;
    raiseEvent();
  }
  if(!digitalRead(DOOR_SENSOR_PIN))
  {
    DOOR_STATE = 0;
    digitalWrite(DOOR_SENSOR_LED, LOW);
  }

  if(digitalRead(WINDOW_SENSOR_PIN) && !WINDOW_STATE)
  {
    WINDOW_STATE = 1;
    digitalWrite(WINDOW_SENSOR_LED, HIGH);
    event = 3;
    raiseEvent();
  }
  if(!digitalRead(WINDOW_SENSOR_PIN)) 
  {
    WINDOW_STATE = 0;
    digitalWrite(WINDOW_SENSOR_LED, LOW);
  }
  delay(10);
}

int raiseEvent() {
  sendEventSMS();
  event = 0;
}

void sendEventSMS() {
    Serial.println(eventMsg[event]);   
}  

 

Borland
Offline
Зарегистрирован: 17.05.2012

Прочел, 3 датчика как я понял, Интрудер(Что это кстати?), дверь и  окно , если какой либо срабатывает, включаем состояние 1 и шлем смс

На мой вкус, поскольку в loop будет еще логика, и чтобы не  мозолить глаза многочисленными проверками вынести проверку всех датчиков в одну функцию типа CheckSensors возвращаюжую 0 если все ок, и номер датчика+1 если плохо) , плюс состояние датчиков можно держать в глобальном массиве (легче проверяется, кроме того сам массив можно хранить и отсылать). Хранение необходимо если предположить такой подход к реализации постановки на охрану - Система запоминает состояние датчиков в момент постановки на охрану. любое изменение состояния в режиме охраны - вызывает алярм и смс.

тогда при постановке на охрану достаточно записать строку состояний датчиков, и сравнивать с ней  текущую строку состояния датчиков

запись самой переменной состояния режима Охраны , скажем Armed =1 ( на охране) =0 (снята с охраны) позволит при перезагрузке контроллера, уже в функции setup прочитать состояния Режима  Охраны , и строку "нормального" положения датчиков охраны, что позволит контроллеру продолжать прерванную функцию охраны

итого не считая логики обработки сенсоров в основном цикле имеем пока 2 if, легко читается и модифицируется для дальнейшего накручивания логики

byte Sensors[3];

byte Armed;

loop() {

if (Armed)

if (CheckSensors())

   sendEventSMS();

}

void sendEventSMS() {

int i;

for (i=0;i<3;i++)

Serial.print(Sensor[i]);

73

 

Ну это на мой вкус.

Писать лучше конечно в eprom, быстрее и надежнее, писать сразу структурой

 

Borland
Offline
Зарегистрирован: 17.05.2012

Нашел.

Команда , заставляющая GSM модуль устанавливать локальное время сотового оператора

AT+CLTS=1 (разрешить)

AT+CLTS=0(Запретить, модуль не будет учитывать рассылку от оператора)

моя функция получающая время от модуля ICOMSAT с бибилиотекой GSMSHIELD

typedef struct {
       byte year;
       byte mounth;
       byte day;
       byte hour;
       byte minutes;
       byte seconds;
} Timestr;
 


void GetDateString (byte *a) {
byte i;
char bytestr[3];
if(AT_RESP_OK == gsm.SendATCmdWaitResp("AT+CCLK?", 5000, 500, "OK", 5)) {
/*       strncpy(MyDate,(char *)&gsm.comm_buf[10],8);
       MyDate[9]='\0';
       strncpy(MyTime,(char *)&gsm.comm_buf[19],8);
       MyTime[9]='\0';  
*/  // For String values of Date Time
       for (i=0;i<6;i++) {
         strncpy(bytestr,(char *)&gsm.comm_buf[10+i*3],2);
         bytestr[2]='\0';
         *a++=(byte)atoi(bytestr);

       }

     }
else 
     Serial.println("ErrorSendATWaitResp");
gsm.SetCommLineStatus(CLS_FREE);
}

Вызов функции 
  Timestr TimeData;
 
/*  char MyDate[10];
  char MyTime[10]; */
  GetDateString((byte*)&TimeData);

В структуре TimeData в 6ти байтах целочисленные значения лет месяцев дней часов минут и секунд 

 

Densoider
Offline
Зарегистрирован: 02.11.2012

Borland,

ОГРОМЕННОЕ Вам Спасибо!

Будет чем заняться на выходных!

Borland
Offline
Зарегистрирован: 17.05.2012

Не за што

Я попробовал перенести  в эпром все системные параметры , но понял что это несколько неблагодарная работа, поскольку память симки организована гораздо удобнее - Номер ячейки, телефон, имя. Номер ячейки обьявляется #define в инклуде в телефоне хранятся цифровые данные, в Имени - коментарии.  Все однозначно. В эпроме же приходится работать со структурами параметров системы  , и для записи , чтения одного параметра придется либо извращаться отдельными фунциями для записи полей, либо писать читать всю структуру, либо написать одну функцию которая будет вычислять адрес каждого поля в структуре ( поскольку поля все разных типов). В общем и то и другое и третье показалось некрасивым, и помятуя что ячейки эпрома портятся, портя саму дуину, а  порча ячеек симки, легко восполняется ее заменой я решил  что увеличение скорости чтения записи на 1-3 секунды того не стоят, тем более эта запись чтение производится только при смене каких либо параметров системы, то есть редко

Третьего буду на работе , если интересно скину свои системные параметры которые храню и обновляю на симке

 

Borland
Offline
Зарегистрирован: 17.05.2012

millis отличная функция для организации "таймеров" внутри loop.

Чтото нужно проверять раз в секунду, чтото раз в 10 секунд, чтото раз в час, чтото наконец раз в сутки и тд.

Единственный недостаток кмк, что ее использование сильно засоряет код самого цикла, поскольку требует обязательно двух вызовов millis, первый с первичной отсечкой времени, второй с проверкой - " а прошло ли это время?"

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


int TimeGone(byte channel,unsigned long time ) {
byte i;
static unsigned long timeouts[10];
unsigned long seconds;
  if(channel==11)  { // reset all timers couse of rebooot
     for(i=0;i<10;i++)
        timeouts[i]=0l;
   return 1;   
  }       
  if (timeouts[channel]) {        // Counter already loaded,
    seconds=(millis()/1000);             
    if((seconds-timeouts[channel])> time) {    // just check if time is gone
       timeouts[channel]=0l;
       return 1;   
    } else return 0; 
    
  } else
  timeouts[channel]=millis()/1000;
  return 0;
}

тогда есть возможность организовать 10 канальный "таймер" который вернет 1 , если время какогото канала вышло и пора чтото сделать:

например так:

  if(TimeGone(0,12l)){ // every 12 seconds
    for(i=0;i<TotalSensors;i++) {
       sensors.requestTemperaturesByAddress(Thermometer[i].Address);
       Temp=Thermometer[i].CurrentTemp=sensors.getTempC(Thermometer[i].Address);
       Serial.print("Sensor ");
       Serial.print(i,DEC);
       Serial.print(": ");
       Serial.println(Temp);
    }
  }
  if(TimeGone(1,86400l)) {  // every 24 hours
        TimeGone(11,0l);   // Reset all timers to zero
         resetFunc() ;  // reboot controller
  }

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

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

Таким образом основной цикл становится гораздо читабельней, да и кода полагаю меньше, чем при использовании просто millis.

Желающие могут переделать код в десятые, сотые секунды, для GSM - секунды имхо самое то

 

 

 

leshak
Offline
Зарегистрирован: 29.09.2011

Можете еще глянуть в http://arduino.ru/forum/programmirovanie/tsifrovoi-datchik-kholla#comment-19985

В сообщении #11 я показывал аналогичное решение через #define.

Плюс его в том, что нет нужды заморачиватся с "сколько у нас каналов". Но, например внутри классов, его уже труднее использовать (хотя, с небольшими поправками - тоже можно).

Densoider
Offline
Зарегистрирован: 02.11.2012

2 Borland, по поводу АТ запроса времени.

Добрался до нормального интернета. Жалко, что на моем Wismo Q2400 нету такой АТ команды. Походу придется на работе махнуть на SIM 900, если получится конечно. 8-) А пока мне далласовские часы идут...

Конечно мне интересно как и что ты в симке хранишь. Поделись, если можно.

Borland
Offline
Зарегистрирован: 17.05.2012

А есть вообще список AT команд для WISMO Q2400 ? я чегото не нашел . Что это вообще - ТАм по сериал общение ? может есть аналог команды.

На самом деле определение времени по способу который я описал имеет для меня один существенный при отладке

у меня часть loop организована как то так:

 

   if (Serial.available()) { // check if keyboard is pressed 
  
     key1=Serial.read();
     Serial.write(key1);
  
     switch(key1) {
       case 's': InitialSetup();
            break;

       .............
    }  
  Serial1.write(key1);
  
  } // endof if available()     
 

  if(Serial1.available())  {   // Check if something is coming from GSM
    key2=Serial1.read();
    Serial.print(key2);
    if(AnyInfoFromGSM(key2)) {
         if(strstr(UrgentMessage,"+CMTI: \"SM\",")){ // SMS Urgent Message is coming;
          ...................................
         } 
          .............
    } 
 
  }
 

часть текста выкинул не потому что это ноухау а просто чтобы видна была логика

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

все что приходит с клавиатуры  тоже анализируем и если это AT команда - отправляем в модуль, если нет то тоже чтото делаем

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

при использовании функции GetDateString эта фича рушится, поскольку она посылает свой "AT+CCLK?"  в то время когда я набираю AT

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

 

пока такие параметры , в основном слизаны с  кситала, с некоторыми модификациями

         ADDPhoneNumber(30,(char*)"1",(char*)"MyHome");      // просто стринг опознающий систему
         ADDPhoneNumber(31,(char*)"10",(char*)"Pause");      // Пауза в секундах до снятия и  постановки на охрану
         ADDPhoneNumber(32,(char*)"12234",(char*)"Password");  // Пароль системы, необходимый для аторизации смс команд
         ADDPhoneNumber(33,(char*)"0",(char*)"Report");         // необходим ли ежедневный отчет
         ADDPhoneNumber(34,(char*)"19",(char*)"ReportHour");    // в котором часу отсылать отчет
         ADDPhoneNumber(35,(char*)"1",(char*)"SMS Report");      //  отправлять ли sms отчеты о выполнении команд
         ADDPhoneNumber(36,(char*)"6",(char*)"Syren");           // включать ли сирену $ 0 - нет 6 - номер  реле к которому подключена сирена 
         ADDPhoneNumber(37,(char*)"22",(char*)"PortRele");       // начальный номер порта реле 
         ADDPhoneNumber(39,(char*)"#100#",(char*)"Balance");     // запрос баланса Sim карты для МТС
#if NUMBEROFRELE==4         
         ADDPhoneNumber(40,(char*)"0000",(char*)"RState");       // начальное состояние реле
#else
         ADDPhoneNumber(40,(char*)"00000000",(char*)"RState");   // начальное состояние реле для варианта 8 реле
#endif        
         ADDPhoneNumber(ARMED,(char*)"0",(char*)"Secured");      // под Охраной
         ADDPhoneNumber(PERIMETER,(char*)"1023",(char*)"Perimeter1");   // состояние Аналогового сигнала от датчика двери 1
         ADDPhoneNumber(PERIMETER+1,(char*)"1023",(char*)"Perimeter2");  // // состояние Аналогового сигнала от датчика двери 2

Соответсвенно этот кусок кода - инициализация начальных параметров, Пока памяти навалом, делаю в том же скетче

в setup соответствено чтение этих параметров в глобальные переменные, учитывая что модуль в цифровых полях телефона всегда пишет "+" в начале.

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

 

Ну и отдельную структуру термометров, ради чего всё задумывалось храню в эпроме,так как в симку ее тяжелее затолкать :

typedef struct {
int Valid;
DeviceAddress Address;
char Type;    // 4 types of Devices
              //  B - Simple, Inside the Body only for statistics and report
	      //  S - Simple, Ordinary only for statistics and report
	      //  R - Full Rele controled with High Low limits
              //  O - Can be Overrided with sms command R1111 from phone;

int LowLimit;
int HighLimit;
unsigned char ReleNumber;
float CurrentTemp;
} Term;

ТОже меняется Смсками, что позволяет включать отключать контроль регулирования и лимиты температуры

 

Borland
Offline
Зарегистрирован: 17.05.2012

leshak пишет:

Можете еще глянуть в http://arduino.ru/forum/programmirovanie/tsifrovoi-datchik-kholla#comment-19985

В сообщении #11 я показывал аналогичное решение через #define.

Плюс его в том, что нет нужды заморачиватся с "сколько у нас каналов". Но, например внутри классов, его уже труднее использовать (хотя, с небольшими поправками - тоже можно).

Ну да leshak , мне тоже больно смотреть  на if millis раскиданные по телу цикла

Едиственное Имхо с define  серьезная логика в скобках тоже не очень читабельна

 

 

leshak
Offline
Зарегистрирован: 29.09.2011

Borland пишет:

leshak пишет:

В сообщении #11 я показывал аналогичное решение через #define.

Ну да leshak , мне тоже больно смотреть  на if millis раскиданные по телу цикла

Если честно, то if-фы мне как раз не сильно смущают. Они читаются нормально. Гораздо больше парит "заводитьп еременные", придумывать им имена (и не перепутать).

В вашем варианте с "каналами", на самом деле они тоже нужны. По хорошему магических цифр быть не должно.  То есть канал 0,1,2,3 - это моветон. Не дай бог в циферке очепятаешься. Прийдется их или константами как-то обзывать или опять дефайнами объявлять. Да еще и следить их  количеством.

Не, я не хочу сказать что ваше решение "ужас". Как раз вполне себе имеет право на жизнь. Я просто предложил "еще вариант". Выбор между ними - вообщем-то дело вкуса.

Borland пишет:

Едиственное Имхо с define  серьезная логика в скобках тоже не очень читабельна

Есть такое. Согласен. Ну можно еще фигурными оборачивать, что-бы "более явно было видно что это код". Делать что-то вида 

EVERY(500,{
  // логика
});

Тогда это уже будет почти как "настоящие анонимные функции" :) Можно еще и пустой дефайн для слова DO сделать тогда 

EVERY(500,DO { ЛОГИКА });

Будет вообще как DSL выглядить ;)

А, по хорошему, серьезная логика не должна быть в loop() в больших объемах. Если там набралось что-то больше 5-ти строк, то почему-бы его не вынести в отдельную функцию с говорящим именем. Что-то типа

// где-то в верху

#define sec *1000
#define min *sec*60
#define h *min*60
....
loop(){
 EVERY(10 sec,printShorReport()); // каждый 10 секунд краткий отчет
 EVERY(15 min,printReport());  // каждые 15 минут обычный отчет
 EVERY (1 h, printFullReport()); // каждый час - полный отчет
}

- имхо смотрится вполне симпатично и понятно.

 

Borland
Offline
Зарегистрирован: 17.05.2012

соглашусь вполне наверное красиво

но дефайны заменять кодом не люблю, тоже вкус

ну да порты обьявить, смысл тех же "каналов" минута секунда сутки

смысл дефайна именно в этом имхо, а именно  - быстро переопределить какой либо параметр

заталкивать в дефайн макрос , ну да есть вариант и вполне смотрибельно , но кто будет анализировать обьем кода и не пора ли дефайн в функцию сунуть

внутри дефайна дефайн не всегда правильно работают

вопщим это дело вкуса и первого нарыва на "ну ево нахер этот дефайн"

хотя решение красивое

мы оба маладцы))