Using the ESP8266 NodeMCU Falsh Button in Interrupt

Hi,

I use the arduino IDE to program my ESP12E module - and I am trying to use the flash button (GPIO 0).

It works fine with digitalRead() but not as an interrupt assigned in the setup() section:

int FlashButtonPIN = 0; pinMode(FlashButtonPIN, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(FlashButtonPIN), handleInterrupt, RISING);

This crashes the ESP right as the interrupt is attached with the following error:

load 0x4010f000, len 1384, room 16 tail 8 chksum 0x2d csum 0x2d vbc204a9b ~ld ISR not in IRAM!

any ideas how to make this work?

Yup, assign the ISR to IRAM.

void IRAM_ATTR onTimer()
{
  BaseType_t xHigherPriorityTaskWoken;
  iDoTheBME280Thing++;
  if ( iDoTheBME280Thing == 60000 )
  {
    xEventGroupSetBitsFromISR( eg, evtGroupBits, &xHigherPriorityTaskWoken );
    iDoTheBME280Thing = 0;
  }
}

I use the IRAM_ATTR to assign the ISR onTimer blah, blah, blah.

On ESP8266 and ESP32 you can use an attribute to inform the compiler put interrupthandler into IRAM which means internal RAM

this is done through inserting the word IRAM_ATTR at the right place

void IRAM_ATTR handleInterrupt()

Code that is stored in internal RAM is executed much faster than code stored in flash. Though using the IO-pin that is used for initiating the "uploadcode"-modeflashing code into the flash for other purposes like this might be difficult.

Are all other IO-pins occupied? Is there a special reason for using IO-pin zero? In the title you have written nodeMCU in the text you write ESP826612E what is the exact name of the "device" you were using? Alternatively a picture of your device would help too

variables that shall be shared between your interrupthandler and your main code need the addition "volatile"

example

volatile unsigned long MyVariable;

best regards Stefan

Just checked: I use the NodeMCU V2 WiFi Lua Amica ESP8266 ESP-12F - it has a flash button on the board that lets the ESP enter flash mode if pressed during boot. Later is can be read using pin 0.

The reason I like to use this button is so I don't need to add an extra button, cause the device I make will be really small.

My stripped down code looks like this:

#define FlashButtonPIN 0
volatile int dMode = 1;

void setup() {
  pinMode(FlashButtonPIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(FlashButtonPIN), handleInterrupt, FALLING);
}

void loop() {
  ...                   
}

void handleInterrupt() {
  Serial.println("Interrupt Detected");
  dMode++;
  if ( dMode > 4 ) { dMode = 1; }
  
}

If I change this to

void IRAM_ATTR handleInterrupt() {

the compiler will fail with " 'handleInterrupt' was not declared in this scope"

Hi Beejayf,

OK. You posted a stripped down-codeversion. Stripping-down the code is one of the hints given in the how to use this forum-thread. Though this code-version is stripped down a little bit too much. Some basic code in the function loop() should be still there.

About the compiler-error. Somehow when the IRAM_ATTR is used it is nescessary that the function that shall be called when an interrupt was invoked must be above the setup()-function. This means simply put the function handleInterrupt() above the setup()-function. If the function handleInterrupt() is above the compiler already knows there is a function handleInterrupt() if the compiler processes the codeline

  attachInterrupt(digitalPinToInterrupt(FlashButtonPIN), handleInterrupt, FALLING);

From what you have coded in the function handleInterrupt() I conclude that you need a little bit more knowledge about how coding with interrupts works. (no serial output in the ISR)

I googled for tutorials about interrupts and did a quick cross-reading which tutorial seems to be easy to understand
From this quick cross-reading I decided to choose this one

It describes interrupts for Arduinos. On ESP8266-microprocessors it works very similar.
Add the IRAM_ATTR as shown above to the arduino-code.

The ESP8266 has an onboard LED too but it is GPIO-pin number 2
So you have to change the IO-number to 2

best regards Stefan

Thanks for the very detailed reply. Your hints made my code work nicely now!

Btw: Your conclusion is absolutely correct! ;-)

Here is a modified version of your code that shows how interrupt-functions should be used:

the interrupt-function should finish as fast as possible. This means Inside the interrupt-function you do only those things that must be done inside the interrupthandler. Everything else is done outside the interrupthandler. Like serial output or other actions.

In this small testprogram it doesn't matter. But imagine the following situation: You add more and more code inside the interrupt-handler so executing the interrupt-code takes 10 seconds to finish. Other things like send/receive data on the serial interface or WiFi needs interrupts too but the microprozessor is still busy with your code. This means that the other functionlities like send/receive over Wifi will fail to wok properly.

additionally this code shows how to detect a state-change with the help of a second variable.

#define FlashButtonPIN 0
volatile int dMode = 1;

int last_dMode = 0;

void IRAM_ATTR handleInterrupt() {
  dMode++; 
}

void setup() {
  Serial.begin(115200);
  Serial.println();
  pinMode(FlashButtonPIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(FlashButtonPIN), handleInterrupt, FALLING);
}

void loop() {
  if (dMode != last_dMode) {   // != logical "not equal"
    Serial.println("Buttonpress detected");
    last_dMode = dMode;
  }
}

best regards Stefan