ESP32 Actuator Control: Relay Interface Design

ESP32 Relay Interface Design Guide

1. Basic Principles of Relays

Functions and Types of Relays

A relay is an electromagnetic switch that controls a high-voltage circuit (110V/220V) through a low-voltage control signal (3.3V/5V). The common types used in the ESP32 system are:

Type Control Signal Load Capacity Isolation Method Features
Mechanical Relay Low Current Up to 250V 10A Electromagnetic Isolation Has mechanical action sound, lifespan of about 100,000 cycles
Solid State Relay 3-32VDC Up to 480V 5A Optical Isolation No mechanical noise, long lifespan
Power Module 3.3V Logic 60V 30A Semiconductor Isolation Can be PWM controlled, supports high-frequency switching

Working Principle of Relays

ESP32 GPIO → Opto-isolation → Driver Circuit → Electromagnetic Coil → Contact Switch → High Voltage Load
                                  (Mechanical)
                                  or
                                  TRIAC/Transistor → High Voltage Load
                                  (Solid State)

2. Hardware Interface Design

Typical Wiring Scheme (Mechanical Relay)

ESP32        Relay Module      Load Device
-------------------------------
GND    →      GND
GPIO13 →      IN
3.3V   →      VCC
                      NO →  Load Live Wire
                      COM → AC Power Live Wire
                      NC →  Not Used (Normally Closed)
Load Neutral Wire → AC Power Neutral Wire

Key Design for Anti-Interference

  1. Opto-IsolationChoose a relay module with opto-isolation (e.g., JQC-3FF-S-Z)
  2. Reverse ProtectionConnect a 1N4007 diode in parallel with the relay coil
  3. Buffer CircuitConnect an RC snubber circuit (100Ω + 0.1μF) in parallel with the relay output
  4. Independent Power SupplyPower the relay separately (do not share with ESP32)

3. Basic Control Code

Simple Switch Control

const int RELAY_PIN = 13; // Control Pin

void setup() {
  pinMode(RELAY_PIN, OUTPUT);
  digitalWrite(RELAY_PIN, LOW); // Initial off state
  Serial.begin(115200);
}

void loop() {
  // Turn on the relay
  digitalWrite(RELAY_PIN, HIGH);
  Serial.println("Relay is ON");
delay(5000); // Maintain for 5 seconds

  // Turn off the relay
  digitalWrite(RELAY_PIN, LOW);
  Serial.println("Relay is OFF");
delay(5000); // Off for 5 seconds
}

Improvement: Adding Protection Mechanism

unsigned long lastSwitchTime = 0;
const int MIN_SWITCH_INTERVAL = 3000; // Minimum switch interval (milliseconds)

void controlRelay(bool state) {
  if (millis() - lastSwitchTime < MIN_SWITCH_INTERVAL) {
    Serial.println("Switching too frequently!");
    return;
  }

  digitalWrite(RELAY_PIN, state ? HIGH : LOW);
  lastSwitchTime = millis();

  // State confirmation
  int actualState = digitalRead(RELAY_PIN);
  if (actualState != state) {
    Serial.println("Relay state abnormal!");
    // Execute safety protection
    emergencyShutdown();
  }
}

4. Advanced Application Design

Multi-Channel Relay Control

// Relay Pin Mapping
const int RELAY_PINS[] = {13, 12, 14, 27};
const int RELAY_COUNT = sizeof(RELAY_PINS) / sizeof(int);

// Relay interlock safety mechanism
void activateRelay(int index, bool state) {
  if (index < 0 || index >= RELAY_COUNT) return;

  // Check conflicting relay
  if (state && index == 0) {
    deactivateRelay(1); // Turn off relay 1 when relay 0 is on
  }

  digitalWrite(RELAY_PINS[index], state ? HIGH : LOW);
}

// Emergency shutdown for all relays
void emergencyShutdown() {
  for (int i = 0; i < RELAY_COUNT; i++) {
    digitalWrite(RELAY_PINS[i], LOW);
  }
  Serial.println("All relays have been emergency shut down");
}

Timed Control Function

#include <TimeLib.h> // Time Library

struct Schedule {
  int relayIndex;
  int hourOn;
  int minOn;
  int hourOff;
  int minOff;
};

Schedule schedules[] = {
  {0, 7, 0, 9, 0},  // Relay 0: ON 7:00 - OFF 9:00
  {1, 18, 30, 22, 0} // Relay 1: ON 18:30 - OFF 22:00
};

void checkSchedules() {
  time_t now = now(); // Get current time
  int currentHour = hour(now);
  int currentMin = minute(now);

  for (int i = 0; i < sizeof(schedules) / sizeof(Schedule); i++) {
    bool shouldBeOn = (currentHour > schedules[i].hourOn || 
                      (currentHour == schedules[i].hourOn && currentMin >= schedules[i].minOn)) &&
                      (currentHour < schedules[i].hourOff || 
                      (currentHour == schedules[i].hourOff && currentMin < schedules[i].minOff));

    digitalWrite(RELAY_PINS[schedules[i].relayIndex], shouldBeOn ? HIGH : LOW);
  }
}

// Call in loop
void loop() {
  checkSchedules();
  delay(60000); // Check every minute
}

5. Network Control

Web Server Control

#include <WiFi.h>
#include <WebServer.h>

WebServer server(80);

void handleRoot() {
  String html = "<html><body><h1>Relay Control</h1>";
  for (int i = 0; i < RELAY_COUNT; i++) {
    html += "<p>Relay " + String(i) + ": ";
    html += "<a href=\"/relay/" + String(i) + "/on\"><button>ON</button></a> ";
    html += "<a href=\"/relay/" + String(i) + "/off\"><button>OFF</button></a></p>";
  }
  html += "</body></html>";
  server.send(200, "text/html", html);
}

void handleRelayControl() {
  String relayId = server.uri().substring(7, 8);
  String action = server.uri().substring(9);
  int relayIndex = relayId.toInt();

  if (action == "on") {
    activateRelay(relayIndex, true);
    server.send(200, "text/plain", "Relay " + relayId + " is ON");
  } else if (action == "off") {
    activateRelay(relayIndex, false);
    server.send(200, "text/plain", "Relay " + relayId + " is OFF");
  }
}

void setupServer() {
  server.on("/", handleRoot);
  server.on("/relay/*", handleRelayControl);
  server.begin();
}

MQTT Remote Control

#include <PubSubClient.h>

WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

void mqttCallback(char* topic, byte* payload, unsigned int length) {
  String topicStr = String(topic);
  String payloadStr = String((char*)payload, length);

  if (topicStr.endsWith("/relay/command")) {
    int separator = payloadStr.indexOf(',');
    if (separator != -1) {
      int relayIndex = payloadStr.substring(0, separator).toInt();
      bool state = payloadStr.substring(separator + 1) == "on";
      activateRelay(relayIndex, state);
    }
  }
}

void connectMQTT() {
  mqttClient.setServer("mqtt.server.com", 1883);
  mqttClient.setCallback(mqttCallback);

  while (!mqttClient.connect("ESP32RelayController")) {
    delay(1000);
  }

  mqttClient.subscribe("home/relay/command");
  mqttClient.publish("home/relay/status", "Online");
}

6. Safety Protection Mechanisms

Hardware Protection Measures

  1. Overvoltage ProtectionConnect a TVS diode (15V) in parallel at the input
  2. Overcurrent ProtectionConnect a resettable fuse in series at the relay output
  3. Heat Dissipation DesignLeave at least 20mm spacing between each relay
  4. Dust ProtectionCover high voltage parts with insulating plates

Software Protection Mechanisms

// Temperature Monitoring Protection
#include <OneWire.h>
#include <DallasTemperature.h>

#define TEMP_SENSOR_PIN 25
OneWire oneWire(TEMP_SENSOR_PIN);
DallasTemperature sensors(&oneWire);

void checkTemperature() {
  sensors.requestTemperatures();
  float temp = sensors.getTempCByIndex(0);

  if (temp > 65.0) { // Temperature threshold
    emergencyShutdown();
    Serial.println("Overheat protection triggered");
  }
}

// Communication watchdog
unsigned long lastCommTime = 0;
const int COMM_TIMEOUT = 600000; // 10 minutes

void checkCommunication() {
  if (millis() - lastCommTime > COMM_TIMEOUT) {
    Serial.println("Communication timeout, entering safe mode");
    activateSafeMode();
  }
}

void updateCommWatchdog() {
  lastCommTime = millis();
}

7. Application Scenario Examples

Smart Home Control Center

#define LIGHT_RELAY 0
#define AC_RELAY 1
#define HEATER_RELAY 2

void controlHomeAppliances(String device, bool state) {
  if (device == "light") {
    activateRelay(LIGHT_RELAY, state);
  } else if (device == "ac") {
    activateRelay(AC_RELAY, state);
  } else if (device == "heater") {
    if (state) {
      // Check safety before turning on heater
      if (!isWindowClosed()) {
        Serial.println("Window is open, heater disabled");
        return;
      }
      activateRelay(HEATER_RELAY, true);
    } else {
      activateRelay(HEATER_RELAY, false);
    }
  }
}

Industrial Automation Control

#define MOTOR_START_RELAY 0
#define MOTOR_DIRECTION_RELAY 1
#define EMERGENCY_STOP_RELAY 3

void controlMotor(bool start, bool direction) {
  // Interlock logic: must select direction before starting
  if (start && !digitalRead(MOTOR_DIRECTION_RELAY)) {
    Serial.println("Direction not selected, cannot start");
    return;
  }

  // Sequential operation
  activateRelay(MOTOR_DIRECTION_RELAY, direction);
delay(100); // Ensure direction relay fully actuates
  activateRelay(MOTOR_START_RELAY, start);

  // Safety interlock: prevent direction change during operation
  if (start) {
    deactivateRelay(MOTOR_DIRECTION_RELAY); // Disable direction switching
  }
}

// Emergency stop button handling
void handleEmergencyStop() {
  activateRelay(EMERGENCY_STOP_RELAY, true); // Trigger safety relay
  activateRelay(MOTOR_START_RELAY, false);    // Stop operation
  Serial.println("Emergency stop triggered");
}

8. Optimization and Debugging

Power Consumption Optimization

void enterLowPowerMode() {
  // 1. Disable relay power supply
  digitalWrite(RELAY_POWER_PIN, LOW);

  // 2. ESP32 enters deep sleep
  esp_sleep_enable_timer_wakeup(3600 * 1000000); // 1 hour
  esp_deep_sleep_start();
}

// Wake up only when needed
void activateSystem() {
  if (detectedActivity()) {
    digitalWrite(RELAY_POWER_PIN, HIGH);
    delay(100); // Relay stabilization time
    // ...normal operation
  }
}

Status Monitoring

void reportRelayStatus() {
  String status = "";
  for (int i = 0; i < RELAY_COUNT; i++) {
    status += "Relay " + String(i) + ": " + 
              (digitalRead(RELAY_PINS[i]) ? "ON" : "OFF");
    if (i < RELAY_COUNT - 1) status += " | ";
  }

  // Send to cloud
  if (WiFi.status() == WL_CONNECTED) {
    mqttClient.publish("home/relay/status", status.c_str());
  }

  Serial.println(status);
}

Fault Diagnosis

void diagnoseRelay(int index) {
  Serial.println("===== Relay Diagnosis =====");
  Serial.print("Status: ");
  Serial.println(digitalRead(RELAY_PINS[index]) ? "ON" : "OFF");

  // Load detection (simple)
  float voltage = analogRead(LOAD_SENSE_PINS[index]) * (3.3 / 4095.0);
  if (voltage < 0.2) {
    Serial.println("No load detected");
  } else if (voltage > 2.5) {
    Serial.println("Abnormal load current!");
  } else {
    Serial.print("Load current: ");
    Serial.println(map(voltage, 0, 3.3, 0, 10)); // 10A full scale
  }

  Serial.println("=====================");
}

9. Design Considerations

  1. Isolation LevelThere should be at least 2000V electrical isolation between the control circuit and the controlled load
  2. Heat ManagementLeave heat dissipation slots in PCB design under the relay, avoid copper covering
  3. Creepage DistanceHigh voltage terminal spacing should be at least 3.2mm (for 220VAC applications)
  4. Emergency MeasuresRetain a physical cutoff switch (forced disconnection device)
  5. Labeling StandardsAll high voltage terminals must be clearly marked with voltage levels and hazard signs

With proper design, the ESP32 relay control system can be applied in various fields such as smart homes, industrial automation, and agricultural irrigation. Always adhere to high voltage safety standards and incorporate multiple safety mechanisms during the design phase.

ESP32 IoT CompassThree Days to Master MicrocontrollersArduino Development Board

Leave a Comment