Preview build — page text is a draft awaiting human review. Code badges come from real compiles. The flasher is real but not yet hardware-tested; the Playground is still a preview.

Push button with NodeMCU ESP8266 — pull-up, interrupt and FLASH button

Wire a push button to a NodeMCU ESP8266 on D5 with the internal pull-up, count presses with an IRAM_ATTR interrupt, and reuse the on-board FLASH button.

Arduino C++Verified — core esp8266 3.1.2, 3 Nov 2026Compile-only

Community draft — report a mistake

A push button is the simplest input, but the NodeMCU has a few pins where it behaves differently. On D5 with the internal pull-up, wired to GND, it just works — and with an interrupt the sketch catches every press even while Wi-Fi is busy.

The sketch also shows a NodeMCU trick: the FLASH button on the board is GPIO0 (D3), and once your sketch is running it is a free extra input.

Wiring

NodeMCU ESP8266Only the pins used are shown · 3.3 V logicPush button (6 mm tactile switch)GNDD512
Every connection
NodeMCU ESP8266Push button (6 mm tactile switch)WireNotes
D51yellowGPIO14 with INPUT_PULLUP: reads HIGH, LOW while pressed.
GND2blackUse the diagonal legs of a 4-pin tactile switch so the wires sit on opposite sides.

No supply — passive switch part, 3.3 V board. Wire the button between a GPIO and GND and use the internal pull-up. Never connect a button between a GPIO and 5 V on a 3.3 V board.

Board quirks

  • Wire the button to GND and use INPUT_PULLUP. Never wire a button to 5 V/VIN — the ESP8266 pins are 3.3 V only.
  • D0 (GPIO16) has no pull-up (only INPUT_PULLDOWN_16) and no interrupts. A button on D0 needs an external 10 kΩ pull-up and polling.
  • D3 is GPIO0 and already has the board’s FLASH button on it. After boot it is a normal input — the sketch reads it, so you get a second button for free. Don’t hold it during reset, or the board enters download mode.
  • D8 (GPIO15) is pulled down on the board, so a button there must go to 3V3, not GND, and must not be held at power-up.
  • Interrupt handlers on the ESP8266 need IRAM_ATTR. Without it the board crashes with “ISR not in IRAM!” as soon as the interrupt is attached.

Code

button-nodemcu.inoArduino C++Open in PlaygroundOpen in PlaygroundFlash
// Push button on a NodeMCU (ESP8266): one leg → D5 (GPIO14), other leg → GND, internal pull-up.
// Also reads the board's own FLASH button (GPIO0 = D3), which is free to use once the sketch runs.
// Each press of the D5 button toggles the on-board LED (D4 / GPIO2, LOW = on).
const uint8_t BUTTON_PIN = D5;
const uint8_t FLASH_PIN = D3;  // on-board FLASH button, pulled up on the board
volatile uint32_t presses = 0;
volatile uint32_t lastEdgeUs = 0;

// Interrupt handlers on the ESP8266 must live in IRAM, or the board crashes when flash is busy.
void IRAM_ATTR onPress() {
  uint32_t now = micros();
  if (now - lastEdgeUs > 200000) presses++;  // 200 ms debounce
  lastEdgeUs = now;
}

void setup() {
  Serial.begin(115200);
  Serial.println();
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  pinMode(FLASH_PIN, INPUT_PULLUP);
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);  // off
  attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), onPress, FALLING);
  Serial.println("Press the button on D5, or the FLASH button");
}

void loop() {
  static uint32_t seen = 0;
  static bool flashWasDown = false;
  noInterrupts();
  uint32_t n = presses;
  interrupts();
  if (n != seen) {
    seen = n;
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
    Serial.printf("D5 button pressed (%u)\n", n);
  }
  bool flashDown = digitalRead(FLASH_PIN) == LOW;
  if (flashDown && !flashWasDown) Serial.println("FLASH button pressed");
  flashWasDown = flashDown;
  delay(10);
}

Expected serial output

Serial Monitor · 115200Output
Press the button on D5, or the FLASH button
D5 button pressed (1)
D5 button pressed (2)
FLASH button pressed

Common errors

“ISR not in IRAM!” and a reboot loop

The interrupt function is missing IRAM_ATTR. Full fix.

One press counts several times

Contact bounce. The sketch ignores edges within 200 ms; raise that for very noisy switches.

Board goes into download mode when the button is held at power-up

The button is on D3 (GPIO0) or D4 (GPIO2) — both must be HIGH at reset. Use D5 for buttons you might hold.

↑↓ to move · Enter to open