Phase 3: GPIO & Hardware 15 min read

Using Relays Safely with ESP32: Wiring, Code, and AC Safety

Learn to control relays with ESP32 safely — understand relay modules, flyback diodes, 3.3V logic compatibility, active-LOW vs active-HIGH, and AC mains precautions.

Updated June 19, 2026

What is a Relay and Why Use One?

A relay is an electrically operated switch. A small control current in the coil creates a magnetic field that mechanically moves a switch contact, connecting or disconnecting a much higher-power circuit. Relays let an ESP32 operating at 3.3V, 40 mA per pin safely switch 240V AC at 10A — a power ratio of 10,000:1.

Common relay applications with ESP32: smart home light switches, automatic irrigation valves, garage door openers, HVAC control, motorised blinds, and industrial solenoid control.

Anatomy of a Relay Module

Never use a bare relay with ESP32. Relay modules include the transistor driver, flyback diode, and status LED:

Module Component Purpose
Relay coil (5V) Electromechanical switch actuator
NPN transistor (driver) Amplifies weak ESP32 signal to drive coil
Flyback diode (1N4007) Suppresses back-EMF voltage spike on coil off
Optocoupler (better modules) Fully isolates ESP32 ground from relay coil circuit
Status LED Visual indicator of relay state
Screw terminals (COM, NO, NC) Connection points for the switched load

Relay Terminal Definitions

Terminal Full Name Behaviour
COM Common Always connected — attach power line here
NO Normally Open Open when relay OFF, closed when relay ON → load ON when relay ON
NC Normally Closed Closed when relay OFF, open when relay ON → load OFF when relay ON

For a light switch: wire COM to live, NO to the lamp, and neutral directly to the lamp. When ESP32 activates the relay, the circuit completes and the lamp turns on.

Wiring ESP32 to a Single Relay Module

Relay Module Pin Connect To
VCC External 5V supply (or ESP32 VIN if powered by USB)
GND GND (common with ESP32)
IN ESP32 GPIO16
COM Load power supply positive
NO Load positive terminal

Code: Active-LOW Relay (Most Common)

Most relay modules are active-LOW: LOW on IN = relay ON, HIGH on IN = relay OFF.

Arduino (C++) — Active-LOW relay
#define RELAY_PIN 16

void setup() {
  Serial.begin(115200);
  // Set HIGH FIRST to prevent relay click at startup
  digitalWrite(RELAY_PIN, HIGH);
  pinMode(RELAY_PIN, OUTPUT);
  Serial.println("Relay control ready");
}

void relayOn()  { digitalWrite(RELAY_PIN, LOW);  Serial.println("Relay ON"); }
void relayOff() { digitalWrite(RELAY_PIN, HIGH); Serial.println("Relay OFF"); }

void loop() {
  relayOn();
  delay(3000);   // Load ON for 3 seconds
  relayOff();
  delay(3000);   // Load OFF for 3 seconds
}

Code: Active-HIGH Relay

Arduino (C++) — Active-HIGH relay
#define RELAY_PIN 16

void setup() {
  digitalWrite(RELAY_PIN, LOW);   // Ensure relay off at start
  pinMode(RELAY_PIN, OUTPUT);
  Serial.begin(115200);
}

void relayOn()  { digitalWrite(RELAY_PIN, HIGH); }
void relayOff() { digitalWrite(RELAY_PIN, LOW);  }

void loop() {
  Serial.println("Relay ON for 5 seconds");
  relayOn();  delay(5000);
  Serial.println("Relay OFF for 5 seconds");
  relayOff(); delay(5000);
}

Non-Blocking Relay Timer with millis()

Arduino (C++)
#define RELAY_PIN   16
#define ON_DURATION  5000UL   // 5 seconds ON
#define OFF_DURATION 2000UL   // 2 seconds OFF

unsigned long lastChange = 0;
bool relayState = false;

void setup() {
  digitalWrite(RELAY_PIN, HIGH);  // Active-LOW: start OFF
  pinMode(RELAY_PIN, OUTPUT);
  Serial.begin(115200);
}

void loop() {
  unsigned long now = millis();
  unsigned long duration = relayState ? ON_DURATION : OFF_DURATION;

  if (now - lastChange >= duration) {
    lastChange  = now;
    relayState  = !relayState;
    digitalWrite(RELAY_PIN, relayState ? LOW : HIGH);  // Active-LOW
    Serial.printf("Relay: %sn", relayState ? "ON" : "OFF");
  }

  // Loop free for Wi-Fi, sensors, OLED, etc.
}

Button-Controlled Relay Toggle

Arduino (C++)
#define RELAY_PIN  16
#define BUTTON_PIN 17

bool relayOn    = false;
bool lastBtn    = HIGH;

void setup() {
  Serial.begin(115200);
  digitalWrite(RELAY_PIN, HIGH);   // Start OFF (active-LOW)
  pinMode(RELAY_PIN,  OUTPUT);
  pinMode(BUTTON_PIN, INPUT_PULLUP);
}

void loop() {
  bool btn = digitalRead(BUTTON_PIN);
  if (lastBtn == HIGH && btn == LOW) {   // Falling edge — button pressed
    relayOn = !relayOn;
    digitalWrite(RELAY_PIN, relayOn ? LOW : HIGH);
    Serial.printf("Relay toggled: %sn", relayOn ? "ON" : "OFF");
    delay(50);   // Debounce
  }
  lastBtn = btn;
}

AC Mains Safety Rules

Switching mains voltage (110V or 240V AC) is inherently dangerous. Follow all of these rules:

  • Use an optocoupler-isolated relay module — isolation separates the ESP32 low-voltage circuit from AC mains
  • Always work with AC disconnected — wire the relay board before connecting to the mains socket
  • Use appropriately rated relay — relay rated for 10A/250VAC for home appliances
  • Enclose AC wiring completely — no exposed terminals, use junction boxes
  • Never exceed relay current rating — a 10A relay cannot safely switch a 15A appliance
  • Add a fuse on the AC line as a safety backup
  • Follow local electrical codes — in many regions, permanent AC wiring requires a licensed electrician

Multiple Relay Control (8-Channel Module)

Arduino (C++) — 8-channel relay
const int RELAYS[] = {16, 17, 18, 19, 21, 22, 23, 25};
const int N = 8;

void setup() {
  Serial.begin(115200);
  for (int i = 0; i < N; i++) {
    digitalWrite(RELAYS[i], HIGH);   // All OFF at start (active-LOW)
    pinMode(RELAYS[i], OUTPUT);
  }
}

void allOn()  { for (int i=0;i

Summary

Always use a relay module — never connect a bare relay coil to ESP32 GPIO. Initialise GPIO HIGH before calling pinMode() to prevent startup clicks on active-LOW modules. Use millis() for non-blocking timing. For AC applications, use optocoupler-isolated modules and follow strict electrical safety practices.

Frequently Asked Questions

No. A bare relay coil draws 60–100 mA which exceeds the GPIO 40 mA maximum and will damage the ESP32. Always use a relay module with a built-in transistor driver, or add your own NPN transistor (2N2222 / BC547) between the GPIO and relay coil.
A relay coil is an inductor. When you switch it off, the collapsing magnetic field generates a voltage spike (back-EMF) that can be 10× the supply voltage. A flyback diode (1N4007) placed in reverse across the coil clamps this spike, protecting the transistor and ESP32. Quality relay modules include this diode.
Active-LOW modules trigger the relay when the control pin is driven LOW (0V). Active-HIGH modules trigger on HIGH (3.3V or 5V). Most cheap relay modules are active-LOW. This means writing LOW to the GPIO activates the relay — which can be confusing. Check your module datasheet.
Most relay modules need 5V to power the coil but accept a 3.3V control signal on the IN pin. However, some modules use BJT transistors that need at least 3.3V on the base — which is the full output of ESP32. Test with a multimeter. If the relay does not trigger at 3.3V, add an external 5V-tolerant driver stage.
NO (Normally Open): load is disconnected when relay is off, connected when relay is ON. NC (Normally Closed): load is connected when relay is off, disconnected when relay is ON. NO is used for most switching applications. NC is used for fail-safe circuits where the load must be ON if the control loses power.
Use an optocoupler-isolated relay module (inputs fully isolated from AC side), work only with the relay terminal block disconnected from AC, never touch the AC wiring while powered, use enclosures that prevent accidental contact, follow local electrical codes, and consider hiring a licensed electrician for permanent AC installations.
Many relay modules are active-LOW: they energize when the control pin is LOW (which happens by default at GPIO boot). To prevent startup click, initialise the GPIO HIGH before setup(): use gpio_config() with pull-up in esp32 or call digitalWrite(RELAY_PIN, HIGH) as the very first line of setup() before pinMode().
Yes. Each relay module needs one GPIO pin. An 8-relay module board uses 8 GPIOs and a common power supply. Connect all IN pins to separate GPIOs, share the GND connection, and power the module from an external 5V source (not the ESP32 3.3V regulator, which cannot supply enough current for multiple coils).
Electromechanical relays typically switch in 5–15 ms (coil energise time) and release in 3–10 ms. For faster switching (microseconds), use a solid-state relay (SSR) or a MOSFET. Relay modules are suitable for on/off control of AC loads, not for audio or fast PWM switching.
For motor or capacitive loads that draw high inrush current at startup, add 200–500 ms between activating multiple relays. This prevents simultaneous inrush from overloading your power supply. Use millis() timing rather than delay() to avoid blocking your main loop.

Projects to Build

Put this knowledge to work — try one of these hands-on projects.