Ардуино не может проснуться с помощью прерывания после отправки SMS

мой случай использования заключается в том, что я строю датчик расстояния с помощью Arduino Uno. Идея заключается в том, что когда расстояние находится между пороговым значением, с устройства отправляется SMS-сообщение.

Я использую DS3231, SIM800L и HCSR04 для своих нужд.

Я тоже хочу сэкономить энергопотребление, поэтому пытаюсь погрузить свой Arduino в глубокий сон, а затем проснуться с помощью прерывания RTC.

Приведенный ниже код отлично работает, когда расстояние НЕ находится в пороге. Это означает, что Arduino просыпается от прерывания, а затем делает то, что ему нужно. Однако если расстояние находится между порогом, то SMS отправляется и принимается, но после этого Arduino больше никогда не просыпается. Я проверил SQW pin DS3231 с помощью мультиметра, и сигнал действительно идет НИЗКО, но Arduino больше не просыпается.

Библиотеки, которые я использую , ds3231, Sim800l, NewPing

Вот код,который я использую,

#include <Arduino.h>
#include <avr/sleep.h>
#include <wire.h>
#include <ds3231.h>
#include <NewPing.h>
#include <Sim800L.h>

#define WAKEUP_PIN 2  // when low, makes 328P wake up, must be an interrupt pin (2 or 3 on ATMEGA328P)
#define LED_PIN 13  // выходной вывод для светодиода (чтобы показать, что он бодрствует)

#define TRIGGER_PIN 7
#define ECHO_PIN 6
#define MAX_DISTANCE 200 //максимальное расстояние, которое мы хотим измерить в см
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);


// DS3231 время будильника
uint8_t wake_HOUR;
uint8_t wake_MINUTE;
uint8_t wake_SECOND;

static boolean goToSleep = false; //для отслеживания того, когда нужно ложиться спать, а когда получать данные и делать что-то еще

#define SIM_MODULE_RX 10
#define SIM_MODULE_TX 11
Sim800L gsmModule(SIM_MODULE_RX, SIM_MODULE_TX);


///* Структура-это структура логических переменных, используемых в качестве полной единицы
// struct ts {
// uint8_t sec; /* секунды */
// uint8_t min; /* минуты */
// uint8_t hour; /* hours */
// uint8_t mday; /* день месяца */
// uint8_t пн; /* месяц */
// int16_t год; /* год */
// uint8_t wday; /* день недели */
// uint8_t yday; /* день в году */
// uint8_t isdst; /* переход на летнее время */
// uint8_t year_s; /* год в краткой нотации*/
//#ifdef CONFIG_UNIXTIME
// uint32_t unixtime; /* секунды с 01.01.1970 00:00:00 UTC*/
//#endif
//};
struct ts t;

// Когда WAKEUP_PIN понижается, это прерывание срабатывает ПЕРВЫМ (даже в режиме PWR_DOWN sleep)
void sleepISR()
{
    // Предотвратить спящий режим, поэтому мы не входим в него снова, кроме как намеренно, с помощью кода
    sleep_disable();

    // Отсоединить прерывание, которое вывело нас из сна
    detachInterrupt(digitalPinToInterrupt(WAKEUP_PIN));

    // Теперь мы продолжаем запускать основной loop() сразу после того, как заснули
}

// Двойное моргание просто для того, чтобы показать, что мы бежим. Обратите внимание, что мы этого не делаем
// используйте задержку для окончательной задержки здесь, это делается путем проверки
// millis вместо этого (неблокирующий)
void doBlink()
{
    static unsigned long lastMillis = 0;

    if (millis() > lastMillis + 1000)
    {
        digitalWrite(LED_PIN, HIGH);
        delay(10);
        digitalWrite(LED_PIN, LOW);
        delay(200);
        digitalWrite(LED_PIN, HIGH);
        delay(10);
        digitalWrite(LED_PIN, LOW);
        lastMillis = millis();
    }
}

// Set the next alarm
void setNextAlarm(void)
{
    // флаги определяют, какой компонент календаря должен быть проверен по текущему времени в порядке
    // чтобы вызвать тревогу - см. таблицу данных
    // A1M1 (секунды) (0 для включения, 1 для отключения)
    // A1M2 (минуты) (0 для включения, 1 для отключения)
    // A1M3 (час) (0 для включения, 1 для отключения)
    // A1M4 (день) (0 для включения, 1 для отключения)
    // DY/DT (dayofweek == 1/dayofmonth == 0)
    uint8_t flags[5] = {0, 0, 0, 1, 1};

    // получить текущее время, чтобы мы могли вычислить следующий сигнал тревоги
    DS3231_get(&t);

    wake_SECOND = t.sec;
    wake_MINUTE = t.min;
    wake_HOUR = t.hour;

    // Добавить несколько секунд к текущему времени. Если переполнение инкремент минут и т.д.
    wake_SECOND = wake_SECOND + 10;
    if (wake_SECOND > 59)
    {
        wake_MINUTE++;
        wake_SECOND = wake_SECOND - 60;

        if (wake_MINUTE > 59)
        {
            wake_HOUR++;
            wake_MINUTE -= 60;
        }
    }

    // Установить время будильника (но еще не активирован)
    DS3231_set_a1(wake_SECOND, wake_MINUTE, wake_HOUR, 0, flags);

    // Включить будильник
    DS3231_set_creg(DS3231_CONTROL_INTCN | DS3231_CONTROL_A1IE);
}

void getDistance(){
    
    // Отправить пинг, получить расстояние в см
    float distance = sonar.ping_cm();
    
    // Отправка результатов на последовательный монитор
    Serial.print("Distance = ");
    
    Serial.print(distance);
    Serial.println(" cm");
    delay(500);

    if (distance < 10.00)
    {
        //Настройка Sim800 для отправки sms
        gsmModule.begin(9600);
        bool err = gsmModule.sendSms("+91790xxxxxxx", "CLose");
        Serial.println(err);
        gsmModule.end();
        delay(3000);
    }
    

    //sleep arduino после описанной выше операции
    goToSleep = true;
}



// Standard setup( ) function
void setup()
{
    Serial.begin(9600);

    // Держите контакты высоко, пока мы не заземлим их
    pinMode(WAKEUP_PIN, INPUT_PULLUP);

    // Мигающий светодиод просто показывает, что микроконтроллер работает
    digitalWrite(LED_PIN, LOW);
    pinMode(LED_PIN, OUTPUT);

    // Clear the current alarm (ставит DS3231 INT high)
    Wire.begin();
    DS3231_init(DS3231_CONTROL_INTCN);
    DS3231_clear_a1f();


    Serial.println("Setup completed.");
}


// Цикл мигает светодиод, когда он не находится в спящем режиме
void loop()
{

    // Просто моргните дважды, чтобы показать, что мы бежим
    doBlink();

    // Теперь штифт "ложись спать", контакт НИЗКИЙ?
    if (goToSleep == true)
    {
        // Установите будильник DS3231 так, чтобы он просыпался через X секунд
        setNextAlarm();

        // Отключить АЦП (аналого-цифровой преобразователь, контакты A0 [14] - A5 [19])
        static byte prevADCSRA = ADCSRA;
        ADCSRA = 0;

        /* Установите тип спящего режима, который мы хотим. Может быть одним из (в порядке энергосбережения):
         SLEEP_MODE_IDLE (таймер 0 будет просыпаться каждую миллисекунду, чтобы держать миллис работает)
         SLEEP_MODE_ADC
         SLEEP_MODE_PWR_SAVE (ТАЙМЕР 2 продолжает работать)
         SLEEP_MODE_EXT_STANDBY
         SLEEP_MODE_STANDBY (генератор продолжает работать, делает для более быстрого пробуждения)
         SLEEP_MODE_PWR_DOWN (Глубокий сон)
         */
        set_sleep_mode(SLEEP_MODE_PWR_DOWN);
        sleep_enable();

        // Обнаружение поворота коричневого выхода (низкое напряжение)
        // Спасибо Нику Гэммону за то, как сделать это (временно) в программном обеспечении, а не
// постоянно используя командную строку avrdude.
        //
        // Примечание: Состояние микрочипа: BODS и BODSE доступны только для устройств picoPower ATmega48PA/88PA/168PA/328P
        //
        // BODS должен быть установлен в один, а BODSE должен быть установлен в ноль в течение четырех тактов. Это устанавливает
        // регистр управления MCU (MCUCR)
        MCUCR = bit(BODS) | bit(BODSE);

        // Бит BODS автоматически очищается после трех тактов, поэтому нам лучше продолжить с ним
        MCUCR = bit(BODS);

        // Убедитесь, что мы можем проснуться снова, сначала отключив interupts (временно), так что
        // wakeISR не запускается до того, как мы спим, а затем предотвращает прерывания,
        // а затем определяет ISR (Процедуру обслуживания прерываний) для запуска при пробуждении
        noInterrupts();
        attachInterrupt(digitalPinToInterrupt(WAKEUP_PIN), sleepISR, LOW);

        // Отправить сообщение, чтобы показать, что мы собираемся спать
        Serial.println("Deep Sleep");
        Serial.flush();

        // Разрешить прерывания сейчас
        interrupts();

        // И войти в спящий режим, как указано выше
        sleep_cpu();

        // --------------------------------------------------------
        // µController теперь спит до тех пор, пока его не разбудит прерывание
        // --------------------------------------------------------

        // Просыпается в этот момент, когда WAKEUP_PIN вызывается НИЗКО - сначала выполняется процедура прерывания
        Serial.println("Awake");

        // Очистите существующий сигнал тревоги, чтобы int pin снова стал высоким
        DS3231_clear_a1f();

        // Повторно включить АЦП, если он ранее был запущен
        ADCSRA = prevADCSRA;

        goToSleep = false; //сброс так, что arduino делает то, что ему нужно

    }
    else
    {
        
        //проверьте, меньше ли расстояние
        Serial.println("Checking");
        getDistance();
    }
}

, 👍1

Обсуждение

Я бы сначала удалил весь этот просто-наверняка код, такой как обработчик прерываний, повторил присоединение и отсоединение, отключение прерываний, BOD, @Juraj

@Juraj Не уверен, как это поможет, потому что в тот момент, когда я удалю "если" (расстояние < 10.00) " заблокируйте все так, как ожидалось. (Arduino просыпается каждые 10 секунд, получает расстояние и спит, цикл продолжается), @TechMky