ESP32 IoT Smart Switch

ESP32 IoT Smart Switch - 4CH WiFi Relay Controller

ESP32 IoT Smart Switch - 4CH WiFi Relay Controller

Build a professional 4-channel IoT smart switch that lets you control electrical appliances from your phone via WiFi, while also providing manual control through onboard tactile switches. Perfect for home automation projects!


How the System Works

Imagine controlling four electrical devices from your phone… and if you don't want to use your phone, you can also control them manually using these switches.

This 4-channel ESP32 IoT smart switch lets you control appliances wirelessly or manually anytime. The custom web interface allows you to turn all lights on/off simultaneously or control each channel individually. When using the onboard manual buttons, the status updates instantly on the web interface.

The system uses a Hi-Link module to convert household AC voltage to 5V DC, a ULN2003 IC to drive the relays, and an ESP32 to host the web server and handle WiFi communication.


Key Features

4 Channels
Individual Control
WiFi + Manual
Dual Control Options
Real-time Sync
Instant Status Updates
AC Powered
220V to 5V Conversion

Components Required

๐Ÿ”ง Main Components

  • ESP32 DevKit V1
  • ULN2003A Darlington IC
  • 4x SRD-05VDC-SL-C 5V Relays
  • HLK-PM01 AC-DC Converter
  • 14D431K MOV (Varistor)

⚡ Protection & Filtering

  • 250V Slow-Blow Fuse
  • 1000ยตF Electrolytic Capacitor
  • 0.1ยตF Ceramic Capacitor
  • 330ฮฉ Resistors

๐Ÿ’ก Indicators & Controls

  • 5mm Red LED Indicator
  • 12mm Tactile Push Buttons (4x)
  • 2-Pin Screw Terminals (5x)
  • Female Header Pins

Complete Materials List with Buy Links:

  • ESP32 DevKit V1 - Buy Here
  • ULN2003A Darlington Transistor Array IC - Buy Here
  • SRD-05VDC-SL-C 5V Relay (4x) - Buy Here
  • HLK-PM01 220V AC to 5V DC Power Module - Buy Here
  • 14D431K Metal Oxide Varistor (MOV) - Buy Here
  • 250V Slow-Blow Fuse - Buy Here
  • 1000ยตF Electrolytic Capacitor - Buy Here
  • 0.1ยตF Ceramic Capacitor - Buy Here
  • 330ฮฉ Resistor - Buy Here
  • 5mm LED Indicator - Buy Here
  • 12mm Tactile Push Button Switch (4x) - Buy Here
  • 2-Pin Screw Terminal Block Connector (5x) - Buy Here

For Testing:


Schematic Diagram

Click on the image to view in full size or use the download link below:


PCB Gerber Files

Download the PCB Gerber files for manufacturing your custom circuit board:

Order PCBs using JLCPCB's professional service:

Order PCBs from JLCPCB

๐ŸŽ“ Design Your Own PCBs with Altium

For designing professional PCBs like this one, I use Altium — a platform that makes electronics design easier, faster, and more connected. It lets your team collaborate in real time by sharing designs, managing versions, and staying in sync from anywhere.

For students, they have Altium Student Lab. Just sign up with your university email for free access to step-by-step PCB courses and industry-recognized certifications that prepare you for real-world roles.

๐Ÿ‘‰ Start building your future with Altium

Assembly Steps

1 Component Placement

Start with the Hi-Link module, then add the fuse, resistors, capacitors, and MOV. Apply masking tape to prevent components from falling while soldering.

2 Solder IC and Relays

Solder the ULN2003 IC, followed by the four 5V relays. Ensure no solder bridges cause short circuits.

3 Add Indicators and Buttons

Solder the four blue LEDs with their 330ฮฉ resistors, then add the four tactile switches for manual control.

4 Screw Terminals

Install the five 2-pin screw terminals - one for AC input and four for appliance connections.

5 Mount ESP32

Attach the ESP32 module to the female header pins. The board is now ready!


Pin Connections

Component ESP32 Pin Notes
Relay CH1 GPIO16 Via ULN2003
Relay CH2 GPIO17 Via ULN2003
Relay CH3 GPIO18 Via ULN2003
Relay CH4 GPIO19 Via ULN2003
Button CH1 GPIO23 INPUT_PULLUP
Button CH2 GPIO22 INPUT_PULLUP
Button CH3 GPIO33 INPUT_PULLUP
Button CH4 GPIO32 INPUT_PULLUP

System Demo Video


Arduino Code

ESP32 IoT Smart Switch Code
#include <WiFi.h>
#include <WebServer.h>

const char* ssid = "Your_WiFi_Name";
const char* password = "Your_Password";

WebServer server(80);

// Relay pins (connected to ULN2003 inputs)
const int relayPins[4] = {16, 17, 18, 19};  // D16, D17, D18, D19

// =====================================================
// MANUAL BUTTON MAPPING - CH3 AND CH4 SWAPPED ONLY
// =====================================================
// CH1 reads from GPIO23
// CH2 reads from GPIO22
// CH3 reads from GPIO32 (was GPIO33)
// CH4 reads from GPIO33 (was GPIO32)
// =====================================================
const int buttonPins[4] = {23, 22, 33, 32};  

// Relay states
bool relayStates[4] = {false, false, false, false};

// Button state tracking
int lastButtonState[4] = {HIGH, HIGH, HIGH, HIGH};
int buttonState[4] = {HIGH, HIGH, HIGH, HIGH};
unsigned long lastDebounceTime[4] = {0, 0, 0, 0};
const unsigned long debounceDelay = 100;

// ==================== RELAY CONTROL ====================
void setRelay(int channel, bool state) {
  if (channel >= 0 && channel < 4) {
    relayStates[channel] = state;
    digitalWrite(relayPins[channel], state ? HIGH : LOW);
    Serial.printf("Channel %d: %s\n", channel + 1, state ? "ON" : "OFF");
  }
}

void toggleRelay(int channel) {
  if (channel >= 0 && channel < 4) {
    setRelay(channel, !relayStates[channel]);
  }
}

// ==================== PCB HARDWARE SETUP ====================
void initPCB() {
  Serial.println("\n=== 4CH IoT Switch Starting ===");
  
  // Configure relay pins as outputs
  for (int i = 0; i < 4; i++) {
    pinMode(relayPins[i], OUTPUT);
    digitalWrite(relayPins[i], LOW);
  }

  // Configure button pins
  for (int i = 0; i < 4; i++) {
    pinMode(buttonPins[i], INPUT_PULLUP);
    lastButtonState[i] = digitalRead(buttonPins[i]);
  }
  
  // Show button mapping
  Serial.println("Button Mapping (CH3 & CH4 Swapped):");
  Serial.println("CH1 <- GPIO23");
  Serial.println("CH2 <- GPIO22");
  Serial.println("CH3 <- GPIO32");
  Serial.println("CH4 <- GPIO33");
}

// ==================== WEB PAGE ====================
String webpage() {
  String html = R"rawliteral(
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=yes">
<title>4CH IoT Switch</title>
<style>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  background: #0a0c10;
  font-family: 'Segoe UI', Roboto, system-ui, sans-serif;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
}

.container {
  max-width: 900px;
  width: 100%;
  background: #14181f;
  border-radius: 32px;
  padding: 28px;
  box-shadow: 0 20px 40px rgba(0,0,0,0.6);
  border: 1px solid #2a3038;
}

.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 32px;
  flex-wrap: wrap;
  gap: 15px;
}

h1 {
  color: #fff;
  font-size: 24px;
  font-weight: 600;
}

.wifi-badge {
  display: flex;
  align-items: center;
  gap: 8px;
  background: #1e242c;
  padding: 10px 18px;
  border-radius: 40px;
  border: 1px solid #333b45;
}

.wifi-led {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #f44336;
  box-shadow: 0 0 10px #f44336;
  transition: all 0.3s;
}

.wifi-led.online {
  background: #4caf50;
  box-shadow: 0 0 15px #4caf50;
}

.wifi-text {
  color: #e0e0e0;
  font-size: 14px;
  font-weight: 500;
}

/* Grid Layout */
.channels-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 20px;
  margin: 30px 0;
}

.channel-card {
  background: #1e242c;
  border-radius: 24px;
  padding: 24px;
  border: 1px solid #2f3742;
  transition: all 0.3s;
  text-align: center;
}

.channel-card.active {
  border-color: #4caf50;
  box-shadow: 0 0 30px rgba(76, 175, 80, 0.2);
}

.channel-name {
  color: #e0e0e0;
  font-size: 20px;
  font-weight: 600;
  margin-bottom: 20px;
}

/* LED Indicator */
.web-led {
  width: 60px;
  height: 60px;
  border-radius: 50%;
  margin: 0 auto 20px;
  background: #f44336;
  box-shadow: 0 0 20px #f44336;
  transition: all 0.3s;
  display: flex;
  align-items: center;
  justify-content: center;
}

.web-led.on {
  background: #4caf50;
  box-shadow: 0 0 40px #4caf50;
}

.web-led span {
  color: white;
  font-size: 24px;
  font-weight: bold;
}

/* Button Style with SVG - SYMBOL BIGGER AND CENTERED */
.power-btn {
  width: 100%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: radial-gradient(circle at 30% 30%, #3a3f48, #151a22 90%);
  border: 8px solid #4a525d;
  box-shadow: 
    inset 0 8px 12px rgba(255,255,255,0.1),
    inset 0 -8px 15px rgba(0,0,0,0.7),
    0 10px 25px rgba(0,0,0,0.5);
  cursor: pointer;
  transition: all 0.15s;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 15px 0;
}

.power-btn:active {
  transform: translateY(5px) scale(0.96);
}

/* SVG Power Icon - LARGER AND PERFECTLY CENTERED */
.power-svg {
  width: 100px;
  height: 100px;
  display: block;
  margin: 0 auto;
}

.power-svg path,
.power-svg line {
  stroke: #8a929f;
  stroke-width: 12;
  stroke-linecap: round;
  fill: none;
  transition: all 0.3s;
}

.power-btn.active .power-svg path,
.power-btn.active .power-svg line {
  stroke: #4caf50;
  filter: drop-shadow(0 0 10px #4caf50);
}

/* Control Panel */
.control-panel {
  display: flex;
  gap: 16px;
  justify-content: center;
  margin-top: 30px;
  flex-wrap: wrap;
}

.master-btn {
  padding: 14px 32px;
  border: none;
  border-radius: 40px;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.2s;
  display: flex;
  align-items: center;
  gap: 8px;
}

.master-btn.all-on {
  background: #2e7d32;
  color: white;
}

.master-btn.all-off {
  background: #c62828;
  color: white;
}

.master-btn:active {
  transform: scale(0.96);
}

@media (max-width: 600px) {
  .container { padding: 20px; }
  .channels-grid { grid-template-columns: 1fr; }
  .power-svg { width: 80px; height: 80px; }
}
</style>
</head>
<body>

<div class="container">
  <div class="header">
    <h1>⚡ 4CH IoT Switch</h1>
    <div class="wifi-badge">
      <span class="wifi-led" id="wifiLed"></span>
      <span class="wifi-text" id="wifiText">Connecting...</span>
    </div>
  </div>

  <!-- Channels Grid -->
  <div class="channels-grid" id="channelsGrid"></div>

  <!-- Master Control -->
  <div class="control-panel">
    <button class="master-btn all-on" onclick="allOn()">
      <span>⚡</span> ALL ON
    </button>
    <button class="master-btn all-off" onclick="allOff()">
      <span>⭘</span> ALL OFF
    </button>
  </div>
</div>

<script>
let channels = [
  { id: 0, name: "CH1", state: false },
  { id: 1, name: "CH2", state: false },
  { id: 2, name: "CH3", state: false },
  { id: 3, name: "CH4", state: false }
];

function renderChannels() {
  const grid = document.getElementById('channelsGrid');
  grid.innerHTML = '';
  
  channels.forEach(ch => {
    const card = document.createElement('div');
    card.className = `channel-card ${ch.state ? 'active' : ''}`;
    card.id = `ch-${ch.id}`;
    
    card.innerHTML = `
      <div class="channel-name">${ch.name}</div>
      
      <div class="web-led ${ch.state ? 'on' : ''}" id="led-${ch.id}">
        <span>${ch.state ? 'ON' : 'OFF'}</span>
      </div>
      
      <div class="power-btn ${ch.state ? 'active' : ''}" onclick="toggleChannel(${ch.id})">
        <svg class="power-svg" viewBox="0 0 100 100">
          <!-- Vertical line -->
          <line x1="50" y1="10" x2="50" y2="45"/>
          <!-- Circle arc -->
          <path d="M 28 30 A 30 30 0 1 0 72 30"/>
        </svg>
      </div>
    `;
    
    grid.appendChild(card);
  });
}

function toggleChannel(id) {
  fetch(`/toggle?id=${id}`)
    .then(() => console.log(`Toggled CH${id+1}`))
    .catch(err => console.log('Error:', err));
}

function allOn() {
  fetch('/all/on')
    .then(() => console.log('All ON'))
    .catch(err => console.log('Error:', err));
}

function allOff() {
  fetch('/all/off')
    .then(() => console.log('All OFF'))
    .catch(err => console.log('Error:', err));
}

function updateStatus() {
  fetch('/status')
    .then(res => res.json())
    .then(data => {
      document.getElementById('wifiLed').className = 'wifi-led online';
      document.getElementById('wifiText').textContent = 'Connected';
      
      channels.forEach((ch, i) => {
        ch.state = data[i];
        
        const card = document.getElementById(`ch-${i}`);
        const led = document.getElementById(`led-${i}`);
        const btn = card?.querySelector('.power-btn');
        
        if (card) {
          if (ch.state) {
            card.classList.add('active');
            if (led) {
              led.className = 'web-led on';
              led.innerHTML = '<span>ON</span>';
            }
            btn?.classList.add('active');
          } else {
            card.classList.remove('active');
            if (led) {
              led.className = 'web-led';
              led.innerHTML = '<span>OFF</span>';
            }
            btn?.classList.remove('active');
          }
        }
      });
    })
    .catch(() => {
      document.getElementById('wifiLed').className = 'wifi-led';
      document.getElementById('wifiText').textContent = 'Offline';
    });
}

renderChannels();
updateStatus();
setInterval(updateStatus, 1000);
</script>
</body>
</html>
)rawliteral";

  return html;
}

// ==================== WEB HANDLERS ====================
void handleRoot() {
  server.send(200, "text/html", webpage());
}

void handleToggle() {
  if (server.hasArg("id")) {
    int id = server.arg("id").toInt();
    if (id >= 0 && id < 4) {
      toggleRelay(id);
      server.send(200, "text/plain", "OK");
      return;
    }
  }
  server.send(400, "text/plain", "Invalid");
}

void handleStatus() {
  String json = "[";
  for (int i = 0; i < 4; i++) {
    if (i > 0) json += ",";
    json += relayStates[i] ? "true" : "false";
  }
  json += "]";
  server.send(200, "application/json", json);
}

void handleAllOn() {
  for (int i = 0; i < 4; i++) {
    setRelay(i, true);
  }
  server.send(200, "text/plain", "OK");
}

void handleAllOff() {
  for (int i = 0; i < 4; i++) {
    setRelay(i, false);
  }
  server.send(200, "text/plain", "OK");
}

// ==================== SETUP ====================
void setup() {
  Serial.begin(115200);
  
  // Initialize PCB hardware
  initPCB();

  // Connect to WiFi
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi");
  
  int attempts = 0;
  while (WiFi.status() != WL_CONNECTED && attempts < 40) {
    delay(500);
    Serial.print(".");
    attempts++;
  }
  
  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("\n✅ WiFi Connected!");
    Serial.print("๐Ÿ“ก IP: ");
    Serial.println(WiFi.localIP());
  }

  // Setup routes
  server.on("/", handleRoot);
  server.on("/toggle", handleToggle);
  server.on("/status", handleStatus);
  server.on("/all/on", handleAllOn);
  server.on("/all/off", handleAllOff);

  server.begin();
}

// ==================== LOOP ====================
void loop() {
  server.handleClient();

  // Read buttons with debounce
  for (int i = 0; i < 4; i++) {
    int reading = digitalRead(buttonPins[i]);
    
    if (reading != lastButtonState[i]) {
      lastDebounceTime[i] = millis();
    }
    
    if ((millis() - lastDebounceTime[i]) > debounceDelay) {
      if (reading != buttonState[i]) {
        buttonState[i] = reading;
        
        if (buttonState[i] == LOW) {
          toggleRelay(i);
        }
      }
    }
    
    lastButtonState[i] = reading;
  }
  
  delay(10);
}

Code Explanation:

Key Features:

  • Built-in web server with responsive UI for mobile control
  • Dual control - WiFi and manual buttons with real-time sync
  • Debounced button reading for reliable manual operation
  • Master ON/OFF controls for all channels
  • Custom SVG power buttons with visual feedback
  • Real-time status updates every second

Setup Instructions:

  • Change ssid and password to your WiFi credentials
  • Install ESP32 board support in Arduino IDE
  • Select ESP32 Dev Module and correct COM port
  • Upload code and check Serial Monitor for IP address
  • Access the web interface from any device on same network

Technical Specifications

  • Microcontroller: ESP32 DevKit V1 (Dual-core 240MHz)
  • Relay Type: 4x SRD-05VDC-SL-C (10A 250VAC / 10A 30VDC)
  • Power Supply: 220V AC to 5V DC via HLK-PM01
  • Driver IC: ULN2003A Darlington Array
  • Manual Control: 4x 12mm tactile switches
  • WiFi: 2.4GHz 802.11 b/g/n
  • Web Interface: Responsive design with SVG buttons
  • Protection: Fuse, MOV, filtering capacitors

Applications

๐Ÿ  Home Automation
Control lights, fans, appliances
๐Ÿญ Industrial
Remote equipment control
๐ŸŽ“ Education
Learn IoT and PCB design
๐Ÿ”Œ Retrofit
Add smart control to existing switches

๐Ÿ”ง Safety Warning: This project involves working with high voltage AC (220V). Always disconnect power while assembling and testing. Ensure proper insulation and use appropriate safety equipment. Double-check all connections before applying power.

Comments

Popular posts from this blog

Solar Tracking System

Arduino Code

Arduino Code Car Parking System