Покачто возникла необходимость в автономном GSM-стороже.
Нашел только один проект сигналки, использующей экономичные режимы и работающей долгое время от батарейки
https://ongroup.ru/energoeffektivnaya_gsm-signalizaciya_na_osnove_arduino_sborka_proshivka_test.html (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 ); // включить прерывания
НО
Почему-то при любом дёргании за любую из двух ног ардуины, срабатывают ОБА обработчика прерываний.
Что не так ?
покажи весь код
Внутри функции обработки прерывания не работает delay(), значения возвращаемые millis() не изменяются. Возможна потеря данных передаваемых по последовательному соединению (Serial data) в момент выполнения функциии обработки прерывания. Переменные, изменяемые в функции, должным быть объявлены как volatile.
http://arduino.ru/Reference/AttachInterrupt
Убери функции и сразу в attachInterrupt изменяй переменную.
К стати - подтяжку по входам сделал-включил?
Ок. благодаря вашим советам была найдена ошибка.
при пробуждении, я выключал подтяжки и из за этого срабатывало второе прерывание.
Теперь другой вопрос.
Если во время бодрствования когда прерывания выключены, была активность модема, то после ухода в сон и пробуждению по прерыванию от датчика, срабатывает и обработчик активности модема.
Т.е похоже, что при бодрствовании устанавливается какой-то флаг, который обрабатывается только после ухода в сон и последующего пробуждения.
Возможно что использование в одном коде (Serial data) и (attachInterrupt) чревато подобными фокусами , судя по всему они используют одинаковые регистры для прерываний.
Цитата: b612 от 20 марта 2018 г., 11:10:31Т.е похоже, что при бодрствовании устанавливается какой-то флаг, который обрабатывается только после ухода в сон и последующего пробуждения.
да. когда ты разрешаешь общее прерывание, тогда прерывания и обрабатываются. в порядке приоритетов.
у INT приоритет больше чем у UART.
у INT0 приоритет больше чем у INT1.
Цитата: 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 тоже чревато разными "фокусами"
У каждого прерывания свои флаги. И они друг на друга не влияют.
Цитата: ЛУТ от 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
А внутри обработчика прерывания можно проанализировать флаги прерываний ? Они сразу сбрасываются или только при возврате из прерывания ?
Не знаю , вообще надо поискать есть ли отладчик для это среды, где пошагово и с индикацией состояния флагов и регистров, тогда только можно выяснить что происходит.
Попробуй так
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
}
может что и измениться ::)
ЦитироватьЭто с точки зрения того кто пишет на асме, а такие готовые функции как (Serial data) и (attachInterrupt) вполне могут использовать(и используют) общие регистры флаги и прочие ресурсы МК.
они могут использовать общие регистры. а флаги у каждого прерывания свои.
и он соединит TX и INT. помехи по UART могут вызывать прерывания.
поэтому я бы сделал подтяжку к плюсу, UART и INT, внешними резисторами на 10кОм.
Там ещё в коде много используется вместо (digitalWrite) непосредственные обращения к регистрам , типа (PORTD|=B00010000;)
Я не анализировал досконально код но возможно что ошибка в этом , так как (digitalWrite) корректно устанавливает биты порта(хотя и медленно), а с непосредственным обращениями к портам легко ошибиться.
Наконец-то опять руки дошли до сигналки
Победил эти прерывания.
Надо было просто перед включением прерываний, сбросить их флаги.
А так прерывания вызывались сразу при включении.
Потом контроллер сразу уходил в сон и не обрабатывал результаты прерываний
до тех пор пока не возникало ещё одно событие.
Так и получалось, что он помнил старые причины, которые возникли ещё до ухода в сон.
Ещё вопросик.
Можно ли программно настраивать чувствительность датчика ?
типа HC-SR501 (https://ru.aliexpress.com/item/5pcs-High-Quality-HC-SR501-Adjust-Infrared-PIR-Motion-Sensor-Detector-Module-For-Raspberry-pi-For/32830483190.html?src=google&albslr=222463252&source=%257Bifdyn%253Adyn%257D%257Bifpla%253Apla%257D%257Bifdbm%253ADBM&albch=shopping&acnt=494-037-6276&albcp=657872107&albag=38867178172&slnk=&trgt=61865531738&plac=&crea=ru32830483190&netw=g&device=c&mtctp=&gclid=Cj0KCQjwnqzWBRC_ARIsABSMVTN3zTONXptWeOPdxyxc0CQge18UCVaYDp2pSEcrbBr7IP63EnUov8UaAgQhEALw_wcB&gclsrc=aw.ds&aff_platform=link-c-tool&cpt=1523278523755&sk=YFAI6ub&aff_trace_key=33525fcf0ddf4f4fb37eb06605bd63f5-1523278523755-05302-YFAI6ub&terminal_id=c631919e02b647f4a07d95050e5cba6d)
Похоже без управляемых резисторов это не получится (
За то похоже можно снимать усиленный аналоговый сигнал с 16-ой ноги микрухи датчика.
Так, что при срабатывании и пробуждении ардуины можно не сразу звонить, а ещё маленько, но уже с умом понаблюдать за движениями.
схема датчика (http://radiolubitel.moy.su/blog/infrakrasnyj_datchik_dvizhenija_hc_sr501/2016-05-13-331")
Цитата: b612 от 09 апреля 2018 г., 16:02:53
Ещё вопросик.
Можно ли программно настраивать чувствительность датчика ?
Цифровой потенциометр (http://avrdevices.ru/cifrovoy-potentsiometr/)
Это если не лень усложнять код и есть свободные выводы МК.
Либо сигнал с 16-го вывода микросхемы датчика на аналоговый вход МК и дальше программно задавать порог срабатывания - там только фантазией ограничено ::) ну там ещё надо учитывать продолжительность сигнала, что-бы исключить ложные срабатывания.