An active buzzer is the simplest alarm you can add to a NodeMCU: one GPIO switches it on, and it makes its own tone. It is the usual choice for a gas, water-tank or door alarm.
On the ESP8266 the only real decision is which pin. Put it on D5 (GPIO14) so it stays silent while the board boots, and power it from 3V3 so both kinds of module — high-trigger and low-trigger — can be switched fully off by a 3.3 V output.
Wiring
| NodeMCU ESP8266 | Active buzzer module (high-level trigger) | Wire | Notes |
|---|---|---|---|
| 3V3 | VCC | red | 3V3 works for both high- and low-trigger modules. A high-trigger module is louder on VIN (5 V); a low-trigger one must stay on 3V3. |
| GND | GND | black | |
| D5 | I/O | yellow | D5 is GPIO14 — it stays quiet while the ESP8266 boots. |
3.3–5 V part, 3.3 V board. The I/O pin only drives the module's transistor, so a 3.3 V GPIO is enough even when the buzzer runs from 5 V. Check the label: "high-level trigger" beeps when I/O is HIGH; "low-level trigger" modules (with a PNP transistor) beep when I/O is LOW.
Board quirks
- Pick the pin carefully: D4 (GPIO2) and TX (GPIO1) toggle during boot, so a buzzer there chirps at every reset. D3 and D8 are boot-strapping pins, and a low-trigger module on D3 can hold it low and stop the board starting. D1, D2, D5, D6 and D7 are quiet.
- A “low-level trigger” module has a PNP transistor tied to VCC. If you power it from 5 V, the NodeMCU’s 3.3 V HIGH can’t switch it off — it beeps for ever. Power it from 3V3, or set
BUZZER_ACTIVE_LOWand keep it on 3V3. - The sketch writes the off level before
pinMode(OUTPUT), so the buzzer doesn’t blip when the pin is configured. - Patterns run from
millis()instead of longdelay()chains. That matters on the ESP8266: once you add Wi-Fi, a loop that blocks for seconds can trigger a soft WDT reset or drop the connection. - An active buzzer has its own oscillator —
tone()won’t change its pitch. For melodies use a passive buzzer.
Code
// Active buzzer module on a NodeMCU (ESP8266).
// VCC → 3V3, GND → GND, I/O → D5 (GPIO14).
// Plays three patterns without blocking: the loop keeps running, so Wi-Fi and the
// watchdog are never starved.
const uint8_t BUZZER_PIN = D5; // GPIO14 — quiet at boot, unlike D3/D4/D8
const bool BUZZER_ACTIVE_LOW = false; // true for "low-level trigger" modules
// Pattern: on/off times in ms, 0 ends it. Even steps sound, odd steps are gaps.
const uint16_t SHORT2[] = {100, 100, 100, 0};
const uint16_t LONG1[] = {600, 0};
const uint16_t SOS[] = {100, 100, 100, 100, 100, 300, 300, 100, 300, 100, 300, 300, 100, 100, 100, 100, 100, 0};
const uint16_t* const PATTERNS[] = {SHORT2, LONG1, SOS};
const char* const NAMES[] = {"two short beeps", "one long beep", "SOS"};
const uint16_t* pattern = nullptr;
uint8_t step = 0;
unsigned long stepStart = 0;
void buzzer(bool on) {
digitalWrite(BUZZER_PIN, (on != BUZZER_ACTIVE_LOW) ? HIGH : LOW);
}
void play(uint8_t n) {
pattern = PATTERNS[n];
step = 0;
stepStart = millis();
buzzer(true);
Serial.printf("Playing %s\n", NAMES[n]);
}
void setup() {
buzzer(false); // set the level before the pin becomes an output
pinMode(BUZZER_PIN, OUTPUT);
Serial.begin(115200);
Serial.println();
Serial.println("Active buzzer on D5 (GPIO14)");
}
void loop() {
static unsigned long lastStart = 0;
static uint8_t next = 0;
if (!pattern && millis() - lastStart >= 3000) { // a new pattern every 3 s
lastStart = millis();
play(next);
next = (next + 1) % 3;
}
if (pattern && millis() - stepStart >= pattern[step]) {
step++;
stepStart = millis();
if (pattern[step] == 0) {
buzzer(false);
pattern = nullptr;
} else {
buzzer(step % 2 == 0);
}
}
}// Active buzzer module on a NodeMCU (ESP8266).
// VCC → 3V3, GND → GND, I/O → D5 (GPIO14).
// Plays three patterns without blocking: the loop keeps running, so Wi-Fi and the
// watchdog are never starved.
const uint8_t BUZZER_PIN = D5; // GPIO14 — quiet at boot, unlike D3/D4/D8
const bool BUZZER_ACTIVE_LOW = false; // true for "low-level trigger" modules
// Pattern: on/off times in ms, 0 ends it. Even steps sound, odd steps are gaps.
const uint16_t SHORT2[] = {100, 100, 100, 0};
const uint16_t LONG1[] = {600, 0};
const uint16_t SOS[] = {100, 100, 100, 100, 100, 300, 300, 100, 300, 100, 300, 300, 100, 100, 100, 100, 100, 0};
const uint16_t* const PATTERNS[] = {SHORT2, LONG1, SOS};
const char* const NAMES[] = {"two short beeps", "one long beep", "SOS"};
const uint16_t* pattern = nullptr;
uint8_t step = 0;
unsigned long stepStart = 0;
void buzzer(bool on) {
digitalWrite(BUZZER_PIN, (on != BUZZER_ACTIVE_LOW) ? HIGH : LOW);
}
void play(uint8_t n) {
pattern = PATTERNS[n];
step = 0;
stepStart = millis();
buzzer(true);
Serial.printf("Playing %s\n", NAMES[n]);
}
void setup() {
buzzer(false); // set the level before the pin becomes an output
pinMode(BUZZER_PIN, OUTPUT);
Serial.begin(115200);
Serial.println();
Serial.println("Active buzzer on D5 (GPIO14)");
}
void loop() {
static unsigned long lastStart = 0;
static uint8_t next = 0;
if (!pattern && millis() - lastStart >= 3000) { // a new pattern every 3 s
lastStart = millis();
play(next);
next = (next + 1) % 3;
}
if (pattern && millis() - stepStart >= pattern[step]) {
step++;
stepStart = millis();
if (pattern[step] == 0) {
buzzer(false);
pattern = nullptr;
} else {
buzzer(step % 2 == 0);
}
}
}Expected serial output
Active buzzer on D5 (GPIO14)
Playing two short beeps
Playing one long beep
Playing SOSActive buzzer on D5 (GPIO14) Playing two short beeps Playing one long beep Playing SOS
Common errors
Buzzer beeps all the time and never stops
A low-level trigger module powered from 5 V. Move its VCC to 3V3 and set BUZZER_ACTIVE_LOW = true.
Buzzer chirps every time the board resets
It is on D4 or TX, which toggle during boot. Move I/O to D5.
Board won’t start with the buzzer connected
The module is on D3 or D8 and pulls a boot pin the wrong way. Use D5 — see boot mode (1,6).