FAQ
TL;DR: If your NodeMCU V1 started hard‑resetting after IDE 1.8.12, a 2‑line ICACHE_RAM_ATTR fix stops the "ISR not in IRAM!" panic—"Interrupt handler functions should have an attribute added to place them in IRAM." [Elektroda, szelus, post #18600043]
Why it matters: This FAQ helps ESP8266/NodeMCU users fix attachInterrupt resets and handle safe counter updates with WiFi enabled.
Quick Facts
- ESP8266 ISRs must reside in IRAM; add ICACHE_RAM_ATTR to your ISR declarations/definitions. [Elektroda, szelus, post #18600043]
- Confirmed fix: adding two ICACHE_RAM_ATTR lines made the project work again on NodeMCU 1.0 (ESP‑12E). [Elektroda, globalinfo, post #18600112]
- Toolchain edge case: std::atomic++ can fail with “undefined reference to __atomic_fetch_add_4”. [Elektroda, globalinfo, post #18602395]
- Workaround: inside ISR, replace counter++ with counter = counter + 1; it compiles on ESP8266 HAL. [Elektroda, khoam, post #18603608]
- Typical pulse input uses INPUT_PULLUP and FALLING with attachInterrupt for energy meter pulses. [Elektroda, globalinfo, post #18599487]
Why is my NodeMCU V1 resetting right after attachInterrupt with WiFi on ESP8266?
Because the ISR is not in IRAM. After WiFi connects, enabling the interrupt causes “ISR not in IRAM!” and a panic reset before setup() finishes. The user verified this by printing checkpoints and seeing the crash right after attachInterrupt. [Elektroda, globalinfo, post #18599978]
How do I fix “ISR not in IRAM!” on ESP8266?
Place your ISR in IRAM using ICACHE_RAM_ATTR on both the prototype and the definition. Then attach the interrupt after pinMode. How‑To: 1) Add void ICACHE_RAM_ATTR isr();
2) Define void ICACHE_RAM_ATTR isr(){...}
3) Call attachInterrupt(pin, isr, mode);
The author reported that adding two lines solved it. [Elektroda, globalinfo, post #18600112]
Do ESP8266 ISRs really need to live in IRAM?
Yes. As one expert put it, “Interrupt handler functions should have an attribute added to place them in IRAM.” Without this, the core aborts with the panic you see. This requirement applies when using attachInterrupt on ESP8266. [Elektroda, szelus, post #18600043]
Where exactly does the crash happen in setup()?
It occurs immediately after calling attachInterrupt on the first input. The user inserted 10‑second delays between Serial markers (“spr3” to “spr4”) and saw the reset right there with “ISR not in IRAM!” in the log. [Elektroda, globalinfo, post #18599978]
Should counters updated in ISRs be atomic on ESP8266?
Yes. Declare shared counters as std::atomic<unsigned long>
and include <atomic>
. Atomic access prevents torn reads and maintains memory ordering between the ISR, the main loop, and WiFi background tasks. “Atomic type objects allow basic operations to be performed in an indivisible way.” [Elektroda, khoam, post #18600813]
I hit “undefined reference to __atomic_fetch_add_4” when building. What does that indicate?
It is a link‑time failure observed when using atomic increments on the ESP8266 toolchain. The error appears when the code uses atomic fetch‑add semantics, which the provided libraries did not resolve in that build. The posted log shows the exact message and halted link step. [Elektroda, globalinfo, post #18602395]
What’s the workaround for the atomic increment issue on ESP8266?
Inside the ISR, replace counter++
with counter = counter + 1;
and keep ICACHE_RAM_ATTR on the ISR. This avoids the problematic increment form and compiles on the ESP8266 HAL. “It should compile and work.” [Elektroda, khoam, post #18603608]
Is testing with empty ISRs useful to isolate the fault?
Yes. Temporarily use empty handlers for the attached interrupts to confirm whether the fault lies in the ISR or elsewhere. If resets stop, the issue is inside the handler or its attributes, not in attachInterrupt itself. [Elektroda, Anonymous, post #18599559]
Do WiFi background tasks affect interrupt safety on ESP8266?
Yes. ESP8266 runs WiFi tasks concurrently. Shared variables touched by ISRs and other threads should be atomic, or guarded appropriately. This avoids race conditions when both the main loop and ISR update or read counters while WiFi is active. [Elektroda, khoam, post #18600813]
Which interrupt mode and pin configuration should I use for pulse inputs?
Use INPUT_PULLUP on the pin and trigger on FALLING edge for clean pulse counting from sensors or energy meters. This pairing matches typical open‑collector or dry‑contact pulse outputs and was used in the working code. [Elektroda, globalinfo, post #18599487]
How can I quickly locate where setup() fails?
Use a simple trace technique. 1) Insert Serial.println markers around suspect lines. 2) Add 10,000 ms delays to make logs readable. 3) Enable one feature at a time, like attachInterrupt, to see when it crashes. [Elektroda, globalinfo, post #18599978]
Will the ICACHE_RAM_ATTR fix apply to NodeMCU 1.0 (ESP-12E) boards?
Yes. The original reporter used a NodeMCU 1.0 (ESP‑12E). Adding ICACHE_RAM_ATTR on the ISRs resolved the resets immediately after the Arduino IDE update. They confirmed, “I added 2 lines and everything works.” [Elektroda, globalinfo, post #18600112]
Do I need to include the atomic header when using std::atomic on Arduino?
Yes. Always #include <atomic>
before declaring std::atomic<unsigned long>
variables. Without the header, types and operations may not compile or behave correctly across translation units in your sketch. This was explicitly noted alongside the atomic guidance. [Elektroda, khoam, post #18600813]