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.
#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
#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()
#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
#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)
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.