Interrupt freezes Arduino and stops code from working

Hi! I got an arduino mega with a rotary encoderstrong text and an LCD I2C module. I attatched an interrupt to the button pin of the Rotary Encoder (which yes, is on a compatible pin, PIN 18 of the MEGA), and that interrupt wakes up the lcd whenever it is pressed. After 5 seconds, the LCD Backlight is shut down again and so on.

The problem is: It doesn't work. Whenever I press the encoder, the code literally freezes. I tried opening and closing the backlight at a distance of 1 second in the main loop and it DID work, but for some reason, if I do it as a function call it crashes... Here is the relevant part of the code:

A_Global_Variables.ino

#include <LiquidCrystal_I2C.h>
// Rotary Encoder
#define outputA 19
#define outputB 45
#define encoderBtn 18
LiquidCrystal_I2C lcd(0x27, 16, 2);  // I2C address 0x27, 16 column and 2 rows

unsigned long tsms=0;  // time since menu started
unsigned long ct;    //current time

void lcd_power_save_on();
void lcd_power_save_off();

B_Setup.ino

Serial.begin(9600);
pinMode(encoderBtn, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(encoderBtn), lcd_power_save_off, CHANGE);
lcd.init();
  lcd.backlight();
  delay(500);

C_Menus.ino

int open = 1;
void lcd_power_save_on(){
  open = 0;
  Serial.print("Closing...");
  delay(50);
  lcd.noBacklight();
}
void lcd_power_save_off(){
  tsms=millis();
  open = 1;
  Serial.print("Opening...");
  delay(100);
  lcd.backlight();
}

D_Main_Loop.ino

void loop() {
  delay(1000);
  ct = millis();
  Serial.println(" ");
  Serial.println(tsms);
  Serial.print(ct);
  Serial.println(" ");
  /*if (tsms % 2 > 1) {
    lcd_display_date();
  }*/
  if (tsms + 5000 < ct && open==1) {
    lcd_power_save_on();
  }
  //delay(1000);
  //lcd_power_save_off();
}

And the LCD DOES shut down after 5 seconds with the lcd_power_save_off() function, but it fails to open when the button is pressed. When I try to print, it also doesn't print the full word:
image

The schematic is down below in my comment

Provided that exactly the same hardware is used in both cases, and no swapping or wiring changes were made...

Wrong! It's a god help for us to understand the project. The word sallad doesn't make things clear.
You do as You want and I take on other questions.


I couldn't find the good encoder in Fritzing, but it's GND, VCC, SW, DT, CLK from left to right. I also updated the code above with the pins. Is everything clear now?

I'll also add this other version:

Forgot to click the reply button. I posted the schematic above

This is a spaghetti schematic (uncooked).
Hand drawn would be better.
Shouldn't you look for falling edge in your attachinterrupt? Now you will have 2 calls, 1 when you press the button and 1 when you release it

2 Likes

Did you confirm that the supply rail pins on the proto board are not split in the middle? Some have that, and then the power and ground connections do not extend all the way between the two ends.

In your if you should subtract two unsigned integers. You need to rearrange the equation. Now it will have problems during rollover. But that is not the problem of today, so we need to look further.

Also, why are you using an interrupt to capture a button press? That is asking for trouble.

You use delay in your interrupt. Delay uses interrupts itself. I am not sure, but I think you are not supposed to do so.
Same for Serial.print()

Do not do ANY serial input or output in an interrupt. Better, do not use interrupts at all.

Rewrite your code to remove the interrupt capability, and if that doesn't fix the problem, come back.

If you do, please post a photo of a hand drawn schematic, with all the pins and parts clearly labeled.

1 Like

I fail to see how sw1 in picture 2 relates to the picture 1.

Hello nitrosop

This is my standard advice to get started for a complex project:

Keep it simple and stupid firstly.
Run some tutorials for the hardware selected.
If you are happy with the results of the tutorials you can merge these to your project.
Have a nice day and enjoy coding in C++.

Wikipedia has a page about the KISS rule: https://en.wikipedia.org/wiki/KISS_principle.

It is not possible to use the I2C bus inside a interrupt, because the Wire library is interrupt driven. Using a delay() will also crash the sketch and Serial can only be used for testing. Using Serial functions inside a interrupt is made possible since a year or so, but it is still a bad idea.

If you want to do something from a interrupt, then you can send a message to the loop(), so that the code in the loop() can do all kind of things and use libraries. That "message" is just a boolean variable with true and false.

// For: https://forum.arduino.cc/t/interrupt-freezes-arduino-and-stops-code-from-working/1081767
//
// Changes:
//   - Using the KISS rule.
//   - Changed to hd4470 library.
//   - It is not possible to use I2C (interrupt driven).
//     or delay() in a interrupt. Serial.print() should be avoided.
//   - Interrupt at falling edge to toggle the background.
//
// Conclusion: 
//   This does not feel right.
//   The button can be checked in the loop().
//   A interrupt makes debouncing harder.
//   The Encoder library can deal with the encoder:
//     https://www.pjrc.com/teensy/td_libs_Encoder.html
//


#include <Wire.h>
#include <hd44780.h>
#include <hd44780ioClass/hd44780_I2Cexp.h>

hd44780_I2Cexp lcd;

// LCD geometry
const int LCD_COLS = 16;
const int LCD_ROWS = 2;

const int encoderCLK = 19;
const int encoderDT  = 45;
const int encoderBtn = 18;

bool backlight = true;       // the current status of the backlight
volatile bool trigger = false; // to send a trigger from interrupt routine

void setup()
{
  Serial.begin(115200);
  pinMode(encoderBtn, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(encoderBtn), lcd_power_toggle, FALLING);

  Wire.begin();             // not really needed, lcd.begin will do this

  int status = lcd.begin(LCD_COLS, LCD_ROWS); // also turn on backlight
  if(status) // non zero status means it was unsuccesful
  {
    Serial.println("Display was not found");
  }

  lcd.setCursor(0, 0);      // column, row
  lcd.print( "To Freeze Or");
  lcd.setCursor(0, 1);      // column, row
  lcd.print( "Not To Freeze");
}

void loop()
{
  if(trigger)
  {
    trigger = false;       // reset the trigger
    backlight = !backlight; // toggle between true and false
    if( backlight)
    {
      lcd.backlight();    // turn backlight on.
    }
    else
    {
      lcd.noBacklight();  // turn backlight off.
    }
  }
  delay(20);              // slow down the sketch, avoid button bounce trouble.
}

void lcd_power_toggle()
{
  trigger = true;
}

You can try the sketch in Wokwi simulation:

To start the Wokwi simulation, click the start button in the upper-middle of the screen. After it has started, click the encoder button to toggle the backlight.

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