GSM сторож

Автор b612, 19 марта 2018 г., 22:24:10

« назад - далее »

b612

Покачто возникла необходимость в автономном GSM-стороже.
Нашел только один проект сигналки, использующей экономичные режимы и работающей долгое время от батарейки
https://ongroup.ru/energoeffektivnaya_gsm-signalizaciya_na_osnove_arduino_sborka_proshivka_test.html
У неё есть большой недостаток - нет обратной связи, т.е. модем не может разбудить ардуину.
Я не долго думая подключил INT1 к TX-у модема, задействовал первое (1-е) прерывание.
...
void wakeUp0(){//обработчик прерывания 
  waked0=true;
  }                         
void wakeUp1(){//обработчик прерывания
  waked1=true; 
  }                         
....
....
   attachInterrupt(0, wakeUp0, CHANGE  );// FALLING );      // включить прерывания
   attachInterrupt(1, wakeUp1, CHANGE  );      // включить прерывания


НО
Почему-то при любом дёргании за любую из двух ног ардуины, срабатывают ОБА обработчика прерываний.
Что не так ?


Grey

покажи весь код
Двое смотрят в лужу.
один видит лужу, а второй отражающиеся в ней звёзды.

ЛУТ

#2
Внутри функции обработки прерывания не работает delay(), значения возвращаемые millis() не изменяются. Возможна потеря данных передаваемых по последовательному соединению (Serial data) в момент выполнения функциии обработки прерывания. Переменные, изменяемые в функции, должным быть объявлены как volatile.

http://arduino.ru/Reference/AttachInterrupt

Убери функции и сразу в  attachInterrupt изменяй переменную.

К стати - подтяжку по входам сделал-включил?
Если вы увидели в этом мире красоту значит вы поняли смысл всего.

b612

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

ЛУТ

Возможно что использование в одном коде (Serial data) и (attachInterrupt) чревато подобными фокусами , судя по всему они используют одинаковые регистры для прерываний.
Если вы увидели в этом мире красоту значит вы поняли смысл всего.

Grey

Цитата: b612 от 20 марта 2018 г., 11:10:31Т.е похоже, что при бодрствовании устанавливается какой-то флаг, который обрабатывается только после ухода в сон и последующего пробуждения.
да. когда ты разрешаешь общее прерывание, тогда прерывания и обрабатываются. в порядке приоритетов.
у INT приоритет больше чем у UART.
у INT0 приоритет больше чем у INT1.
Двое смотрят в лужу.
один видит лужу, а второй отражающиеся в ней звёзды.

b612

#6
Цитата: Grey от 20 марта 2018 г., 22:40:42
да. когда ты разрешаешь общее прерывание, тогда прерывания и обрабатываются. в порядке приоритетов.
у INT приоритет больше чем у UART.
По идее если стоИт флаг прерывания, то при разрешении прерывания, оно должно срабатывать сразу. Но оно срабатывает не сразу, а только "с пинка" когда срабатывает соседнее.

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

#include <avr/sleep.h>
#include <SoftwareSerial.h>              // библиотека программного UART
SoftwareSerial gsm(7, 6);                // RX(7), TX(6)

volatile bool waked0=false;
volatile bool waked1=false;
volatile bool tosleep=false;
volatile bool doDial=false;
volatile int8_t incomingByte = 0;
volatile int8_t rejim = 0;
#define _Rejim0 0  // исходный режим
#define _ATcommand 1  // режим прямого ввода AT-команд
#define _SetPinTo 2
#define _SetLEDTo 3

/*ISR (INT0_vect)
  {waked0=true;}
ISR (INT1_vect)
  {waked1=true;}*/
 
void wakeUp0(){//обработчик прерывания 
  waked0=true;
  }                         
void wakeUp1(){//обработчик прерывания
  waked1=true; 
  }                         

///////////////////////////////////////////
void toRejim0(){
  rejim = _Rejim0; 
  Serial.println("");
  Serial.print(">"); 
}

void gsmOFF(){                           //
  Serial.println("gsm OFF");
  PORTD|=B00010000;                      // ВЫКЛЮЧЕНИЕ МОДУЛЯ (поднимаем DTR)
  _delay_ms(10);                         //
//  gsm.println("AT+CPWROFF");             // ПЕЧАТАЕМ КОМАНДУ OFF
  gsm.println("AT+CSCLK=2");             // усыпляем модем
  while(1){                              //
    if (gsm.find("OK"))
      {
        Serial.println("AT+CSCLK=2 ok");
        break;          //выходим из цикла
      }
  } 
  PORTB &=~ B00100000;                      // выключить LED 13
}                                        //
//=========================================
void gsmON(){                            //
  //PORTD|=B01000000;                       // 6-му порту (TX) назначить 1
  PORTD &= ~(B00010000);                  // ЗАПУСК МОДУЛЯ (опускаем DTR)
  _delay_ms(110);                         //
  PORTB |= B00100000;                     // включить LED 13 !!!
  Serial.println(F("gsm ON"));
//  while(!gsm.find("+PBREADY"));         // ждём прочтения тел. книги
//  Serial.println("PBREADY");
  gsm.println(F("AT+CSCLK=0"));             // устанавливаем управление питанием через без DTR
  while(1){                              //
    if (gsm.find("OK"))
      {
        Serial.println(F("AT+CSCLK=0 ok"));
        break;          //выходим из цикла
      }
  } 

  while(1){                              //
    gsm.println(F("AT+CREG?"));             // проверяем в сети ли модуль
    if (gsm.find("0,1"))
      { Serial.println(F("on line? Ok."));
        break;          // если сеть есть, выходим из цикла
      }
    _delay_ms(400);                      // проверка раз в 0,4 сек
    Serial.println(F("off line"));
  }                                      //
}                                        //
///////////////////////////////////////////
                                         //
void sleepNow(){                         // функция засыпания
   Serial.println("sleep"); 
   _delay_ms(100);                       //
   Serial.end();
   ADCSRA = 0x00;                        // отключить подсистему АЦП (экономия 140 мкА)
   //UCR = 0x00;                         // отключить UART
   //PORTD &=~ B01000000;                // в вывод TX поставить 0
   PORTD = 0;                            // в вывод TX поставить 0
   DDRD |= B00010000;                    // 4-й вывод - выход на DTR модема), 6-й софтварный TX   
   //digitalWrite(2, HIGH);//включим подтяжку
   //digitalWrite(3, HIGH);//включим подтяжку
   PORTB &=~ B00100000;                      // выключить LED 13
   set_sleep_mode(SLEEP_MODE_PWR_DOWN);  // режим сна PWR_DOWN
   attachInterrupt(0, wakeUp0, CHANGE  );// FALLING ); // включить прерывание 0
   attachInterrupt(1, wakeUp1, CHANGE  );      // включить прерывание 1
   sleep_enable();                       // включение возможности сна
   sleep_mode();                         // сон

   //проснулись
   sleep_disable();                      //
   detachInterrupt(0);                   // отключить прерывания
   detachInterrupt(1);                   // отключить прерывания   
   //digitalWrite(2, LOW);//вЫключим подтяжку
   //digitalWrite(3, LOW);//вЫключим подтяжку
   PORTB |= B00100000;                       // включить LED 13 !!!
   Serial.begin(9600);
   gsm.begin(9600);                        // скорость работы UART   
   Serial.println("Wake");
}

void setup(){
gsm.begin(9600);                        // скорость работы UART
Serial.begin(9600);
DDRD |= B01010000;                      // 4-й выводы на выход (DTR модема) 6-й софтварный TX
PORTD &= ~(B00010000);                  // ЗАПУСК МОДУЛЯ (опускаем DTR)
PORTB |= B00100000;                     // включить LED 13 !!!
//pinMode(2, INPUT_PULLUP);
//pinMode(3, INPUT_PULLUP);
digitalWrite(2, HIGH);//включим подтяжку
digitalWrite(3, HIGH);//включим подтяжку
DDRB |= B00100010;                      // вывод 13 на выход (светик) и на звук
//pinMode(9, OUTPUT);                      //звук
//gsmON();                                // запуск модуля для теста
//gsmOFF();                               // выключаем модуль
}

void loop(){
  if (waked0){                   // если сработало прерывание
      waked0=false;
      Serial.println("INT0");
      //gsmON();//будим модем
      for (uint8_t i=0;i<10;i++)
        {
        digitalWrite(9, HIGH);
        delay(50);
        digitalWrite(9, LOW);
        delay(50);
        }
    }
  else if (waked1)
    {                   // если сработало прерывание
      waked1=false;
      Serial.println("INT1");
      //пискнем
      digitalWrite(9, HIGH);
      delay(100);
      digitalWrite(9, LOW);
    }
  else if (doDial){     
      doDial=false;
      Serial.println("Dial");
      //gsmON();
      gsm.println("ATD+79514558448;");   // отзваниваемся, в ответ приходит OK и CONNECT
      _delay_ms(100);
      if (gsm.find("OK"))
          while(1){                      // ожидание сброса вызова
          gsm.println("AT+CPAS");        // при каждой итерации опрашиваем модуль
          if (gsm.find("0")) break;      // если 0, то выходим из цикла while
          _delay_ms(100);                // проверка раз в 0,1 сек
          }   
      for (char i=0; i<14; i++){
          PORTB|=B00100000;                 // LED 13 ON
          _delay_ms(200);
          PORTB&=~(B00100000);                // LED 13 OFF
          _delay_ms(200);
        }
      //gsmOFF();                          // выключить модуль
      _delay_ms(10);
      //while(1);                          // блокируем программу*/
  }
  else if (tosleep){
      tosleep=false;
      //gsmOFF(); //усыпляем модем
      sleepNow();                        // укладываем контроллер спать
    }
  else {
  if (gsm.available())           // Ожидаем прихода данных (ответа) от модема...
    {Serial.write(gsm.read());}    // ...и выводим их в Serial   
  if (Serial.available())           // Ожидаем команды по Serial...
    {incomingByte=Serial.read();
    switch (rejim)
      {
      case _Rejim0:
        {switch (incomingByte)
          {case 's':
            {tosleep=true;
            toRejim0();
            break;
            }
           case 'f': //"f"
            {Serial.println(F("gsm to sleep"));
            gsmOFF();
            break;
            }
           case 'g': //"g"
            {Serial.println(F("gsm to wake"));
            gsmON();
            break;
            }
           case 'a':
            {rejim=_ATcommand;
            Serial.print(F("AT>"));
            break;
            }
           case 'd':
            {rejim=_SetPinTo;
            Serial.print(F("set pin 4 to>"));
            break;
            }
           case 'l': //"L"
            {rejim=_SetLEDTo;
            Serial.print(F("set LED 13 to>"));
            break;
            }
           case 'R': //"R"
            {doDial=true;
            break;
            }
           case 'q': //"q"
            {
            Serial.print(F("portD2 = "));
            Serial.println(digitalRead(2));
            break;
            }
           case 'w': //"w"
            {
            Serial.print(F("portD3 = "));
            Serial.println(digitalRead(3));
            break;
            }
           case 'h': //"h"
            {
            Serial.println(F("a - at commands"));
            Serial.println(F("s - arduino to sleep"));
            Serial.println(F("R - do dial"));
            Serial.println(F("f - gsm to sleep"));
            Serial.println(F("g - gsm to wake"));
            Serial.println(F("f - gsm to sleep"));
            Serial.println(F("l(L) - set LED 13 to .."));
            Serial.println(F("q - read POTTd2"));
            Serial.println(F("w - read POTTd3"));
            break;
            }
           default:     
            {break;}         
          }
         break;
         }
      case _ATcommand:
        {
        gsm.write(incomingByte);    // ...и отправляем полученную команду модему         
        //Serial.write(incomingByte);
        if (incomingByte == 0x0D)
          {
            toRejim0();
          }
        break;
        }
      case _SetPinTo:
        {
        if (incomingByte == '1')
          {
            digitalWrite(4, HIGH);
             toRejim0();
          }
        else if (incomingByte == '0')
          {
            digitalWrite(4, LOW);
            toRejim0();
          }
        else
          {
            toRejim0();           
          }
        break;
        }
      case _SetLEDTo:
        {
        if (incomingByte == '1')
          {
            digitalWrite(13, HIGH);
             toRejim0();
          }
        else if (incomingByte == '0')
          {
            digitalWrite(13, LOW);
            toRejim0();
          }
        else
          {
            toRejim0();           
          }
        break;
        }
      default:     
        {break;}   
      }
    }//switch (rejim)
  }//if (Serial.available())
}

ЛУТ

Цитата: b612 от 20 марта 2018 г., 11:10:31

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

Ну всё же сходиться с предупреждениями.
Какой то из  UART устанавливает флаг-регистр ид... и не сбрасывает его при отключении, а после этот флаг-регистр читает  attachInterrupt и думает что это сигнал для срабатывания.

Это плата за лёгкость написания кода, писал бы на асме следил бы за флагами и регистрами.

Попробуй переделать код и схему так что бы использовалось только одно прерывание (один вход)
Если вы увидели в этом мире красоту значит вы поняли смысл всего.

ЛУТ

Просто использование сразу двух attachInterrupt тоже чревато разными "фокусами"
Если вы увидели в этом мире красоту значит вы поняли смысл всего.

Grey

У каждого прерывания свои флаги. И они друг на друга не влияют.
Двое смотрят в лужу.
один видит лужу, а второй отражающиеся в ней звёзды.

b612

Цитата: ЛУТ от 21 марта 2018 г., 13:46:32
Какой то из  UART устанавливает флаг-регистр ид... и не сбрасывает его при отключении, а после этот флаг-регистр читает  attachInterrupt и думает что это сигнал для срабатывания.
но ведь флаги прерываний срабатывают сразу, а не потом, когда сработало какое-то там ещё соседнее прерывание.
На одно прерывание вешать не хочется, потому, что потом придётся долго выяснять, кто дёрнул.  При этом охранный датчик может дребезжать, а  модем придётся допрашивать с пристрастием, что там у него могло произойти, то ли звонок то ли смс то ли питание начало заканчиваться.
Хотя... всё равно придётся это всё писать.
А внутри обработчика прерывания можно проанализировать флаги прерываний ? Они сразу сбрасываются или только при возврате из прерывания ?

ЛУТ

Цитата: Grey от 22 марта 2018 г., 08:15:42
У каждого прерывания свои флаги. И они друг на друга не влияют.
Это с точки зрения того кто пишет на асме, а такие готовые  функции как  (Serial data) и (attachInterrupt) вполне могут  использовать(и используют) общие регистры  флаги и прочие ресурсы МК.
attachInterrupt в этом плане вообще проблемная.

Внутри функции обработки прерываниязначения не работает delay(), возвращаемые millis() не изменяются. Возможна потеря данных передаваемых по последовательному соединению (Serial data) в момент выполнения функциии обработки прерывания. Переменные, изменяемые в функции, должным быть объявлены как volatile.

Цитата: b612 от 22 марта 2018 г., 08:38:24
А внутри обработчика прерывания можно проанализировать флаги прерываний ? Они сразу сбрасываются или только при возврате из прерывания ?
Не знаю , вообще надо поискать есть ли отладчик для это среды, где пошагово и с индикацией состояния флагов и регистров, тогда только можно выяснить что происходит.
Если вы увидели в этом мире красоту значит вы поняли смысл всего.

ЛУТ

#12
Попробуй так
attachInterrupt(0,waked0=true, CHANGE  );
...
if (waked0){                   // если сработало прерывание
      Serial.println("INT0");
      //gsmON();//будим модем
      for (uint8_t i=0;i<10;i++)
        {
        digitalWrite(9, HIGH);
        delay(50);
        digitalWrite(9, LOW);
        delay(50);
        }
   waked0=false;
waked1=false;//на всякий случай в качестве паранойи  ::) ;D
}


может что и измениться  ::)
Если вы увидели в этом мире красоту значит вы поняли смысл всего.

Grey

ЦитироватьЭто с точки зрения того кто пишет на асме, а такие готовые  функции как  (Serial data) и (attachInterrupt) вполне могут  использовать(и используют) общие регистры  флаги и прочие ресурсы МК.
они могут использовать общие регистры. а флаги у каждого прерывания свои.
и он соединит TX и INT. помехи по UART могут вызывать прерывания.
поэтому я бы сделал подтяжку к плюсу, UART и INT, внешними резисторами на 10кОм.
Двое смотрят в лужу.
один видит лужу, а второй отражающиеся в ней звёзды.

ЛУТ

Там ещё в коде много используется вместо (digitalWrite) непосредственные обращения к регистрам , типа (PORTD|=B00010000;)
Я не анализировал досконально код но возможно что ошибка в этом , так как  (digitalWrite) корректно устанавливает биты порта(хотя и медленно), а с непосредственным обращениями к портам легко ошибиться.
Если вы увидели в этом мире красоту значит вы поняли смысл всего.