Выполнение кода в функции по прерыванию

У меня есть модуль часов ds3231 и плата esp32. В интернете полно примеров когда начальное время устанавливается при компилировании скетча и загрузки в начальных данных в часы. Только ни где не нашел примеров синхронизации.
Пришла идея раз в месяц/год синхронизировать модуль с сетевым временем. Для этого запустил таймер, установил время срабатывания прерывания, повесил на него функцию.
Вот тут и засада.
Если в лупе висят строки получения времени и запись в модуль часов,(часть которая закоментирована) то все работает. Если эти же операции переношу в функцию , то происходит крах и перезапуск программы.
Может опытные расскажут в чем проблема?


//------библиотеки и переменные модуля часов ds3231------------------------------------------
#include "RTClib.h" //библиотека модуля часов ds3231
RTC_DS3231 rtc;
//-------------------------------------------------------------------------------------------

//-------------библиотеки и переменные для подключения по wi-fi и Синхронизация часов -------
#include <WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
// Введите свои сетевые учетные данные в следующих переменных
const char* ssid     = "**********"; // заменить на имя сети
const char* password = "**********";// заменить на пароль сети
/* 
 *  по умолчанию подключается к серверу "pool.ntp.org",
 *  Задать другой сервер вы можете при инициализации клиента, передав его вторым параметром.
 *  NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000);
 */
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);
String formattedDate;
//-------------------------секция переменных для таймера  синхронизации часов-----------------
hw_timer_t *My_timer = NULL; // Переменная-указатель с именем My_timer для настройки
//-------------------------секция переменных модуля наличия напряжения------------------------
const int motionSensor = 34;
volatile boolean flagpower  = true;  // флаг наличия сетевого напряжения
volatile uint32_t debounce=0; // максимальное число 4 294 967 295
//----------конец  секции переменных и библиотек------------------------------------------------

void IRAM_ATTR detectsMovement() { 
  if (millis() - debounce >= 1000 && digitalRead(motionSensor)) 
    {
      Serial.println("Падение сетевого напряжения!!! это дребезг");
      flagpower = true; 
    } 
  else if (digitalRead(motionSensor))
    {
      flagpower = false;
      Serial.println("Падение сетевого напряжения!!! Окончательно");
    }
  else 
    {
      flagpower = true;
      Serial.println("Напряжение в норме!!!");     
    }
  debounce = millis();
}

//--------------подпрограмма обработки прерывания таймера для синхронизации времни по wi-fi--------------
void IRAM_ATTR sinhrotime() {
  while(!timeClient.update()) {// обновляем дату из сети, если не обновилась, то повторяем
    timeClient.forceUpdate();
  }
  formattedDate = timeClient.getFormattedDate();// строка в виде 2018-04-30T16:00:13Z
  rtc.adjust(formattedDate.c_str()); // преобразуем string в char для передачи в функцию adjust
}

//-------------------секция setup---------------------------------------------------------------------
void setup() {
  // инициализируем порт
  Serial.begin(115200);
  //---------секция часов-----------------------------------------------------------------------
    if (! rtc.begin()) {
      Serial.println("Не найден модуль часов RTC 3231");
      Serial.flush();
      while (1) delay(10);
      }
  WiFi.begin(ssid, password);               // подключанмся к сети
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  timeClient.begin(); 	// запускаем получение времени из интернет
  timeClient.setTimeOffset(10800); // сдвиг +3 часа для москвы  
//---------------------------------------------------------------------------------------------------
 /*Для инициализации таймера есть функция timerbegin ()
    - Номер таймера, который мы хотим использовать (от 0...3),
    - Значение предварительного делителя
    - Флаг, указывающий, должен ли счетчик считать вверх true/false */
  My_timer = timerBegin(0, 40, true); 
  timerAttachInterrupt(My_timer, &sinhrotime, true);// прерывание будут запускать функцию  sinhtime
  timerAlarmWrite(My_timer, 60000000, true); // делитель устанавливающий на сутки.clock.setDateTime(t2); сейчас 30 секунд
  timerAlarmEnable(My_timer);// включаем таймер  
//---------------------------------------------------------------------------------------------------
  pinMode(motionSensor, INPUT);
  attachInterrupt(digitalPinToInterrupt(motionSensor), detectsMovement, FALLING);     
}

void loop() {
  DateTime now = rtc.now();
  Serial.print(now.year(), DEC);
  Serial.print('/');
  Serial.print(now.month(), DEC);
  Serial.print('/');
  Serial.print(now.day(), DEC);
  Serial.print(' ');
  Serial.print(now.hour(), DEC);
  Serial.print(':');
  Serial.print(now.minute(), DEC);
  Serial.print(':');
  Serial.println(now.second(), DEC);

 // while(!timeClient.update()) {
 //   timeClient.forceUpdate();// условие не должно быть больше 5 раз переделать!!!! иначе при отсутствии сети зависнет
 // }
//	formattedDate = timeClient.getFormattedDate();// строка в виде 2018-04-30T16:00:13Z
//	Serial.println(formattedDate);
//	rtc.adjust(formattedDate.c_str()); // преобразуем string в char для передачи в функцию adjust
//	Serial.println(formattedDate.c_str());
// 
  delay(1000);
}

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

Какой например смысл обновлять время в прерывании?

Вы всерьез хотите сделать запрос в И-нет и получить ответ внутри аппаратного прерывания?
Не нужно этого делать.

Я подумал, что аппаратное прерывание просто запускает функцию, а не останавливает работу процессора.
То есть смысл такой должен быть
1 в функции прерывания вешаем флаг, что таймер сработал
2 В цикле лупа постоянно проверяем этот флаг
3 Если флаг поднят, то выполняем функцию обновления времени.
Так? Как то некрасиво получается. С таким же успехом можно в цикле и время считать просто. и часы(модуль) не нужны тогда.

вот и выполняйте функцию в лупе, зачем вы ее в прерывание засунули?

у вас жуткая каша в голове

Вообще идея простая, плата спит, таймер считает, пришло время запустил плату она синхронизировалась, раз в год.(или в зависимости от точности модуля) например 1302 вообще “бегунок”, его чаще нужно, а 3231 и в год хватит.

Нормальная идея,
Таймер считает, пришло время, запустил плату, выставил флаг.
Все остальное должно происходить в лупе, в том числе запрос времени через сеть

Понял . Буду править. Спасибо.

Так ото ж :frowning:

А чего тут некрасивого?
Для данной задачи часы, конечно, нужны (а иначе откуда в цикле время читать), а вот прерывание по таймеру - не нужно абсолютно.

Если есть часы, не проще ли нужный промежуток времени по часам и определять?

1 лайк

Часы мне нужны, в оконечном проекте, только как энергонезависимый источник времени.

Если есть часы, не проще ли нужный промежуток времени по часам и определять?

Не проще, длительный промежуток, чем определять, будильником? Это усложнение программы.

Практически в любом МК есть свои часы. В случае Ардуино их показания снимаются при помощи millis() или micros().

а сейчас у вас аж трое часов - сначала таймер МК, потом внешние часы, а еще синхронизация по сети. Это, конечно, “упрощение программы” :slight_smile:

Пара строк - это усложнение программы? Зачем будильник? Задать точку (дата + время) и сохранить ее. В ходе выполнения программы просто проверять - если точка достигнута, запускать коррекцию времени и сохранять новую точку коррекции

При отключении питания точка пропадает, или ее надо куда-то прописывать. Решил, что часовой модуль проще.
Однако сейчас подумал, что таймер тоже собьется при отключении питания. Надо бы синхронизацию замутить при включении питания.

А EEPROM в ардуине на что?

если таймер при отключении сбивается - вам тем более не нужны прерывания, что вы в них считать собрались, если таймер не работает?

надо изучить этот момент. Спасибо.