Interrupt makes loop stop working

I’m working on an LCD that displays time and temp/humidity readings which I have gotten to work. Now I am trying to program an interrupt through a button press to set an alarm. When I press the button, the program seems to stop working until I press the reset button on the Arduino. I have it set to print “alarm” in the serial monitor and it only prints “al” and then stops working. It is also supposed to change the LCD screen but it seems to be stopping before it gets to that point in the interrupt. I am using a Mega 2560, the code is below.

#include <RTClib.h>
#include <LiquidCrystal_I2C.h>
#include <dht.h>

dht DHT;

#define DHT11_PIN 5

RTC_Millis rtc;
LiquidCrystal_I2C lcd(0x27, 16, 2);

void alarmprogram() {

  Serial.print("alarm");
  lcd.setCursor(0, 0);
  lcd.print("                ");
  lcd.setCursor(0, 1);
  lcd.print("                ");
  lcd.setCursor(0, 0);
  lcd.print("Alarm!");




}

void setup() {
  lcd.init(); // initialize the lcd
  lcd.backlight();
  lcd.setCursor(0, 0);
  Serial.begin(9600);
  rtc.begin(DateTime(F(__DATE__), F(__TIME__)));
  attachInterrupt(0, alarmprogram, RISING);
  pinMode(2, INPUT);
}

void loop() {

  lcd.setCursor(0, 0);
  lcd.print("                ");
  lcd.setCursor(0, 1);
  lcd.print("                ");
  for (int i = 0; i <= 5; i++) {
    lcd.setCursor(0, 0);
    DateTime now = rtc.now();
    int currentmin = now.minute();
    int currentsec = now.second();
    lcd.print("Time:  ");
    lcd.print(now.hour(), DEC);
    lcd.print(':');
    if (currentmin < 10) {
      lcd.print('0');
    }
    lcd.print(now.minute(), DEC);
    lcd.print(':');
    if (currentsec < 10) {
      lcd.print('0');
    }
    lcd.print(now.second(), DEC);
    lcd.setCursor(0, 1);
    lcd.print("Date: ");
    lcd.print(now.month(), DEC);
    lcd.print("/");
    lcd.print(now.day(), DEC);
    lcd.print("/");
    lcd.print(now.year(), DEC);
    delay(1000);
  }

  lcd.setCursor(0, 0);
  lcd.print("                ");
  lcd.setCursor(0, 1);
  lcd.print("                ");
  for (int i = 0; i <= 5; i++) {
    int chk = DHT.read11(DHT11_PIN);
    lcd.setCursor(0, 0);
    lcd.print("Temp: ");
    lcd.print(DHT.temperature);
    lcd.print(" C");
    lcd.setCursor(0, 1);
    lcd.print("Humidity :");
    lcd.print(DHT.humidity);
    lcd.print("%");

    delay(1000);
  }

}

Assuming that you actually need to use an interrupt (why ?) then don’t print in the ISR.
Printing uses interrupts.
Interrupts are turned off when in an ISR.
Can you see a problem ?

If you use an ISR (why ?) then set a flag in the ISR and react to it in loop()

Ah ok, I did not know printing also used interrupts. Would you recommend a better method than an interrupt to monitor for a button press?

I already did

Sorry I should have been more specific, how do I get the code from that flag changing to execute anywhere in the loop. For example, I’ll put the new loop code below, but it only reacts to the button press at the beginning of the loop. If I press the button half way through, it won’t put the alarm screen up until the loop resets. “alarmpage” changes to 1 when the button is pressed.

void loop() {
  if (alarmpage == 1) {
      Serial.print("alarm");
  lcd.setCursor(0, 0);
  lcd.print("                ");
  lcd.setCursor(0, 1);
  lcd.print("                ");
  lcd.setCursor(0, 0);
  lcd.print("Alarm!");
  delay(3000);
  alarmpage = 0;
    }
   
  lcd.setCursor(0, 0);
  lcd.print("                ");
  lcd.setCursor(0, 1);
  lcd.print("                ");
  for (int i = 0; i <= 5; i++) {
    lcd.setCursor(0, 0);
    DateTime now = rtc.now();
    int currentmin = now.minute();
    int currentsec = now.second();
    lcd.print("Time:  ");
    lcd.print(now.hour(), DEC);
    lcd.print(':');
    if (currentmin < 10) {
      lcd.print('0');
    }
    lcd.print(now.minute(), DEC);
    lcd.print(':');
    if (currentsec < 10) {
      lcd.print('0');
    }
    lcd.print(now.second(), DEC);
    lcd.setCursor(0, 1);
    lcd.print("Date: ");
    lcd.print(now.month(), DEC);
    lcd.print("/");
    lcd.print(now.day(), DEC);
    lcd.print("/");
    lcd.print(now.year(), DEC);
    delay(1000);
  }

  lcd.setCursor(0, 0);
  lcd.print("                ");
  lcd.setCursor(0, 1);
  lcd.print("                ");
  for (int i = 0; i <= 5; i++) {
    int chk = DHT.read11(DHT11_PIN);
    lcd.setCursor(0, 0);
    lcd.print("Temp: ");
    lcd.print(DHT.temperature);
    lcd.print(" C");
    lcd.setCursor(0, 1);
    lcd.print("Humidity :");
    lcd.print(DHT.humidity);
    lcd.print("%");

    delay(1000);
  }

}

The first thing you do is to restructure the program to use millis() for timing instead of delay() so that loop() can keep repeating freely

See https://forum.arduino.cc/index.php?topic=503368.0 and https://forum.arduino.cc/index.php?topic=223286.0

1 Like

Generally, using an interrupt to read a button pressed by a human is unnecessary. If you get rid of the delays, loop will likely be fast enough for your purposes.

I’m gonna be honest I still don’t understand this millis stuff even after reading the links. I took everything out of the code, I know this code doesn’t work I’m just trying to understand using millis. I’m trying to use timemillis to update the time display once every second, and switch over to a temperature display after 5 seconds using tempmillis, then back to time. I don’t know how to delay the screen switching without using delay like I did originally.

unsigned long startmillis;
unsigned long timemillis;
unsigned long tempmillis;
const unsigned long period = 1000;
const unsigned long longerperiod = 5000;

void setup() {
  startmillis = millis();
}

void loop() {
  timemillis = millis();
  tempmillis = millis();
  if (timemillis - startmillis >= period) {
    for (int i = 0; i <= 5; i++) {
        lcd.print(time);
        timemillis = currentmillis;
    }
  }
  if (tempmillis - startmillis >= longerperiod) {
      lcd.print(temp);
      tempmillis = currentmillis;
    }


}

Wildbil,
I have the delays in there for readability otherwise it would switch back and forth between the temperature and time screens too fast to read.

Sure, but you can use millis instead. In your example though, if the period you’re waiting for is over, you will need to set startmillis to millis(). Get it working for the first period before you try it with longerPeriod.

Using millis() is fundamentally simple

Save the value of millis() when a period starts. Check each time through loop() whether the required period has expired. If so, then do what you want. If not keep going round loop() until it does

An example

unsigned long currentTime;
unsigned long updateStartTime;
unsigned long updatePeriod = 1000;
boolean type = true;
byte count;

void setup()
{
  Serial.begin(115200);
  while (!Serial);
}

void loop()
{
  currentTime = millis();
  if (currentTime - updateStartTime >= updatePeriod)
  {
    Serial.print("displaying ");
    if (type == true)
    {
      Serial.println("temperature");
    }
    else
    {
      Serial.println("time");
    }
    updateStartTime = currentTime;
    count++;
  }
  if (count == 5)
  {
    type = !type; //invert the type
    count = 0;
  }
}
1 Like

After some struggling I’ve gotten it to work, thanks for the suggestion of using millis instead of delay.

I am glad that you got it working

It would be helpful for anyone reading this topic to see your solution

Here is the code that works for a passive clock and temperature monitoring. Note that I am still trying to implement setting an alarm through buttons which is broken, but that part is marked with comments and can be commented out without effecting the clock/temp display.

#include <RTClib.h>
#include <LiquidCrystal_I2C.h>
#include <dht.h>

dht DHT;
volatile int alarmpage = 0;
const int unsigned long period = 1000;
const int unsigned long tempperiod = 5000;
unsigned long temptime = 0;
unsigned long clocktime = 0;
int initial = 0;
byte count = 0;
boolean type = true;
int alarmminute = 0;
int alarmhour = 0;
int exitbutton = 0;

#define DHT11_PIN 5

RTC_Millis rtc;
LiquidCrystal_I2C lcd(0x27, 16, 2);

void alarmprogram() {
  alarmpage = 1;

}

void setup() {
  lcd.init(); // initialize the lcd
  lcd.backlight();
  lcd.setCursor(0, 0);
  Serial.begin(9600);
  rtc.begin(DateTime(F(__DATE__), F(__TIME__)));
  attachInterrupt(0, alarmprogram, RISING);
  pinMode(2, INPUT);
  pinMode(3, INPUT);
  pinMode(8, INPUT);
}

void loop() {
  unsigned long currenttime = millis();

  //print initial time
  if (initial == 0) {
    lcd.setCursor(0, 0);
    lcd.print("                ");
    lcd.setCursor(0, 1);
    lcd.print("                ");
    lcd.setCursor(0, 0);
    DateTime now = rtc.now();
    int currentmin = now.minute();
    int currentsec = now.second();
    lcd.print("Time:  ");
    lcd.print(now.hour(), DEC);
    lcd.print(':');
    if (currentmin < 10) {
      lcd.print('0');
    }
    lcd.print(now.minute(), DEC);
    lcd.print(':');
    if (currentsec < 10) {
      lcd.print('0');
    }
    lcd.print(now.second(), DEC);
    lcd.setCursor(0, 1);
    lcd.print("Date: ");
    lcd.print(now.month(), DEC);
    lcd.print("/");
    lcd.print(now.day(), DEC);
    lcd.print("/");
    lcd.print(now.year(), DEC);
    initial = 1;
  }

  if (currenttime - clocktime >= period && alarmpage == 0) {
    //print updated time after 1 second

    if (type == true) {

      lcd.setCursor(0, 0);
      DateTime now = rtc.now();
      int currentmin = now.minute();
      int currentsec = now.second();
      lcd.print("Time:  ");
      lcd.print(now.hour(), DEC);
      lcd.print(':');
      if (currentmin < 10) {
        lcd.print('0');
      }
      lcd.print(now.minute(), DEC);
      lcd.print(':');
      if (currentsec < 10) {
        lcd.print('0');
      }
      lcd.print(now.second(), DEC);
      lcd.setCursor(0, 1);
      lcd.print("Date: ");
      lcd.print(now.month(), DEC);
      lcd.print("/");
      lcd.print(now.day(), DEC);
      lcd.print("/");
      lcd.print(now.year(), DEC);

      initial = 1;

    }

    else if (type == false) {
      int chk = DHT.read11(DHT11_PIN);
      lcd.setCursor(0, 0);
      lcd.print("Temp: ");
      lcd.print(DHT.temperature);
      lcd.print(" C");
      lcd.setCursor(0, 1);
      lcd.print("Humidity :");
      lcd.print(DHT.humidity);
      lcd.print("%");

    }
    clocktime = currenttime;
    count++;

  }

  if (count == 5) {
    type = !type;
    count = 0;
    lcd.setCursor(0, 0);
    lcd.print("                ");
    lcd.setCursor(0, 1);
    lcd.print("                ");
  }

//BROKEN BELOW HERE

  if (alarmpage == 1) {
    lcd.setCursor(0, 0);
    lcd.print("                ");
    lcd.setCursor(0, 1);
    lcd.print("                ");
    lcd.setCursor(0, 0);
    lcd.print("Alarm Time: ");
    lcd.setCursor(0, 1);
    lcd.print("00:00");
    if (digitalRead(2) == HIGH) {
      alarmhour++;
      lcd.setCursor(0,1);
      lcd.print(alarmhour);
      lcd.print(":");
      lcd.print(alarmminute);
      }
        if (digitalRead(3) == HIGH) {
      alarmminute++;
      lcd.setCursor(0,1);
      lcd.print(alarmhour);
      lcd.print(":");
      lcd.print(alarmminute);
      }
      if (digitalRead(8) == HIGH) {
        exitbutton = 1;
        }
    
    if (exitbutton == 1) {
      alarmpage = 0;
      }  
    
  }


}

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.